Unpacking Software Livestream

Join our monthly Unpacking Software livestream to hear about the latest news, chat and opinion on packaging, software deployment and lifecycle management!

Learn More

Chocolatey Product Spotlight

Join the Chocolatey Team on our regular monthly stream where we put a spotlight on the most recent Chocolatey product releases. You'll have a chance to have your questions answered in a live Ask Me Anything format.

Learn More

Chocolatey Coding Livestream

Join us for the Chocolatey Coding Livestream, where members of our team dive into the heart of open source development by coding live on various Chocolatey projects. Tune in to witness real-time coding, ask questions, and gain insights into the world of package management. Don't miss this opportunity to engage with our team and contribute to the future of Chocolatey!

Learn More

Calling All Chocolatiers! Whipping Up Windows Automation with Chocolatey Central Management

Webinar from
Wednesday, 17 January 2024

We are delighted to announce the release of Chocolatey Central Management v0.12.0, featuring seamless Deployment Plan creation, time-saving duplications, insightful Group Details, an upgraded Dashboard, bug fixes, user interface polishing, and refined documentation. As an added bonus we'll have members of our Solutions Engineering team on-hand to dive into some interesting ways you can leverage the new features available!

Watch On-Demand
Chocolatey Community Coffee Break

Join the Chocolatey Team as we discuss all things Community, what we do, how you can get involved and answer your Chocolatey questions.

Watch The Replays
Chocolatey and Intune Overview

Webinar Replay from
Wednesday, 30 March 2022

At Chocolatey Software we strive for simple, and teaching others. Let us teach you just how simple it could be to keep your 3rd party applications updated across your devices, all with Intune!

Watch On-Demand
Chocolatey For Business. In Azure. In One Click.

Livestream from
Thursday, 9 June 2022

Join James and Josh to show you how you can get the Chocolatey For Business recommended infrastructure and workflow, created, in Azure, in around 20 minutes.

Watch On-Demand
The Future of Chocolatey CLI

Livestream from
Thursday, 04 August 2022

Join Paul and Gary to hear more about the plans for the Chocolatey CLI in the not so distant future. We'll talk about some cool new features, long term asks from Customers and Community and how you can get involved!

Watch On-Demand
Hacktoberfest Tuesdays 2022

Livestreams from
October 2022

For Hacktoberfest, Chocolatey ran a livestream every Tuesday! Re-watch Cory, James, Gary, and Rain as they share knowledge on how to contribute to open-source projects such as Chocolatey CLI.

Watch On-Demand

Downloads:

172,846

Downloads of v 0.9.380:

184

Last Update:

24 Jul 2018

Package Maintainer(s):

Software Author(s):

  • Chrissy LeMaire

Tags:

admin powershell module template dba sqlserver sql tools database

dbatools (PowerShell Module)

This is not the latest version of dbatools (PowerShell Module) available.

  • 1
  • 2
  • 3

0.9.380 | Updated: 24 Jul 2018

Downloads:

172,846

Downloads of v 0.9.380:

184

Maintainer(s):

Software Author(s):

  • Chrissy LeMaire

dbatools (PowerShell Module) 0.9.380

This is not the latest version of dbatools (PowerShell Module) available.

  • 1
  • 2
  • 3

Some Checks Have Failed or Are Not Yet Complete

Not All Tests Have Passed


Validation Testing Passed


Verification Testing Passed

Details

Scan Testing Resulted in Flagged:

This package was submitted (and approved) prior to automated virus scanning integration into the package moderation processs.

We recommend clicking the "Details" link to make your own decision on installing this package.

Details
Learn More

Deployment Method: Individual Install, Upgrade, & Uninstall

To install dbatools (PowerShell Module), run the following command from the command line or from PowerShell:

>

To upgrade dbatools (PowerShell Module), run the following command from the command line or from PowerShell:

>

To uninstall dbatools (PowerShell Module), run the following command from the command line or from PowerShell:

>

Deployment Method:

NOTE

This applies to both open source and commercial editions of Chocolatey.

1. Enter Your Internal Repository Url

(this should look similar to https://community.chocolatey.org/api/v2/)


2. Setup Your Environment

1. Ensure you are set for organizational deployment

Please see the organizational deployment guide

2. Get the package into your environment

  • Open Source or Commercial:
    • Proxy Repository - Create a proxy nuget repository on Nexus, Artifactory Pro, or a proxy Chocolatey repository on ProGet. Point your upstream to https://community.chocolatey.org/api/v2/. Packages cache on first access automatically. Make sure your choco clients are using your proxy repository as a source and NOT the default community repository. See source command for more information.
    • You can also just download the package and push it to a repository Download

3. Copy Your Script

choco upgrade dbatools -y --source="'INTERNAL REPO URL'" --version="'0.9.380'" [other options]

See options you can pass to upgrade.

See best practices for scripting.

Add this to a PowerShell script or use a Batch script with tools and in places where you are calling directly to Chocolatey. If you are integrating, keep in mind enhanced exit codes.

If you do use a PowerShell script, use the following to ensure bad exit codes are shown as failures:


choco upgrade dbatools -y --source="'INTERNAL REPO URL'" --version="'0.9.380'" 
$exitCode = $LASTEXITCODE

Write-Verbose "Exit code was $exitCode"
$validExitCodes = @(0, 1605, 1614, 1641, 3010)
if ($validExitCodes -contains $exitCode) {
  Exit 0
}

Exit $exitCode

- name: Install dbatools
  win_chocolatey:
    name: dbatools
    version: '0.9.380'
    source: INTERNAL REPO URL
    state: present

See docs at https://docs.ansible.com/ansible/latest/modules/win_chocolatey_module.html.


chocolatey_package 'dbatools' do
  action    :install
  source   'INTERNAL REPO URL'
  version  '0.9.380'
end

See docs at https://docs.chef.io/resource_chocolatey_package.html.


cChocoPackageInstaller dbatools
{
    Name     = "dbatools"
    Version  = "0.9.380"
    Source   = "INTERNAL REPO URL"
}

Requires cChoco DSC Resource. See docs at https://github.com/chocolatey/cChoco.


package { 'dbatools':
  ensure   => '0.9.380',
  provider => 'chocolatey',
  source   => 'INTERNAL REPO URL',
}

Requires Puppet Chocolatey Provider module. See docs at https://forge.puppet.com/puppetlabs/chocolatey.


4. If applicable - Chocolatey configuration/installation

See infrastructure management matrix for Chocolatey configuration elements and examples.

Package Approved

This package was approved as a trusted package on 24 Jul 2018.

Description

dbatools logo dbatools is sort of like a command-line SQL Server Management Studio. The project initially started out as Start-SqlMigration.ps1, but has now grown into a collection of over 300 commands that help automate SQL Server tasks and encourage best practices.

NOTE: This module requires a minimum of PowerShell v3.

NOTE: This is an automatically updated package. If you find it is out of date by more than a week, please contact the maintainer(s) and let them know the package is no longer updating correctly.


tools\.skipAutoUninstaller
 
tools\chocolateyBeforeModify.ps1
$ErrorActionPreference = 'Stop'

$moduleName = 'dbatools'      # this could be different from package name

$module = Get-Module -Name $moduleName
if ($module) {
    Write-Verbose "Module '$moduleName' is imported into the session. Removing it."
    Remove-Module -Name $moduleName -Force -ErrorAction SilentlyContinue

    if ($lib = [appdomain]::CurrentDomain.GetAssemblies() | Where-Object FullName -like "dbatools, *") {
        Write-Verbose "Found locked DLL files for module '$moduleName'."
        $moduleDir = Split-Path $module.Path -Parent
        if ($lib.Location -like "$moduleDir\*") {
            Write-Warning @"
We have detected dbatools to be already imported from '$moduleDir' and the dll files have been locked and cannot be updated.
Please close all consoles that have dbatools imported (Remove-Module dbatools is NOT enough).
"@
            throw 
        }
    }
}
tools\chocolateyInstall.ps1
$ErrorActionPreference = 'Stop'

$toolsDir   = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)"
$moduleName = 'dbatools'  # this may be different from the package name and different case

if ($PSVersionTable.PSVersion.Major -lt 3) {
    throw "$moduleName) module requires a minimum of PowerShell v3."
}

# module may already be installed outside of Chocolatey
Remove-Module -Name $moduleName -Force -ErrorAction SilentlyContinue

$sourcePath = Join-Path -Path $toolsDir -ChildPath "$modulename\*"
$destPath   = Join-Path -Path $env:ProgramFiles -ChildPath "WindowsPowerShell\Modules\$moduleName"

if ($PSVersionTable.PSVersion.Major -ge 5)
{
    $manifestFile = Join-Path -Path $toolsDir -ChildPath "$moduleName\$moduleName.psd1"
    $manifest     = Test-ModuleManifest -Path $manifestFile -WarningAction Ignore -ErrorAction Stop
    $destPath     = Join-Path -Path $destPath -ChildPath $manifest.Version.ToString()
}

Write-Verbose "Creating destination directory '$destPath' for module."
New-Item -Path $destPath -ItemType Directory -Force -ErrorAction SilentlyContinue | Out-Null

Write-Verbose "Moving '$moduleName' files from '$sourcePath' to '$destPath'."
Move-Item -Path $sourcePath -Destination $destPath -Force

if ($PSVersionTable.PSVersion.Major -lt 4)
{
    $modulePaths = [Environment]::GetEnvironmentVariable('PSModulePath', 'Machine') -split ';'
    if ($modulePaths -notcontains $destPath)
    {
        Write-Verbose "Adding '$destPath' to PSModulePath."
        $newModulePath = @($destPath, $modulePaths) -join ';'

        [Environment]::SetEnvironmentVariable('PSModulePath', $newModulePath, 'Machine')
        $env:PSModulePath = $newModulePath
    }
}
tools\chocolateyUninstall.ps1
$ErrorActionPreference = 'Stop'

$moduleName = 'dbatools'
$sourcePath = Join-Path -Path $env:ProgramFiles -ChildPath "WindowsPowerShell\Modules\$moduleName"

Write-Verbose "Removing all version of '$moduleName' from '$sourcePath'."
Remove-Item -Path $sourcePath -Recurse -Force -ErrorAction SilentlyContinue

if ($PSVersionTable.PSVersion.Major -lt 4) {
    $modulePaths = [Environment]::GetEnvironmentVariable('PSModulePath', 'Machine') -split ';'

    Write-Verbose "Removing '$sourcePath' from PSModulePath."
    $newModulePath = $modulePaths | Where-Object { $_ -ne $sourcePath }

    [Environment]::SetEnvironmentVariable('PSModulePath', $newModulePath, 'Machine')
    $env:PSModulePath = $newModulePath
}
tools\dbatools\allcommands.ps1
### DO NOT EDIT THIS FILE DIRECTLY ###
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Add-DbaComputerCertificate {
    <#
        .SYNOPSIS
            Adds a computer certificate - useful for older systems.

        .DESCRIPTION
            Adds a computer certificate from a local or remote computer.

        .PARAMETER ComputerName
            The target SQL Server. Defaults to localhost.

        .PARAMETER Credential
            Allows you to login to $ComputerName using alternative credentials.

        .PARAMETER Password
            The password for the certificate, if it is password protected.

        .PARAMETER Certificate
            The target certificate object.

        .PARAMETER Path
            The local path to the target certificate object.

        .PARAMETER Store
            Certificate store. Default is LocalMachine.

        .PARAMETER Folder
            Certificate folder. Default is My (Personal).

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .NOTES
            Tags: Certificate

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            Add-DbaComputerCertificate -ComputerName Server1 -Path C:\temp\cert.cer

            Adds the local C:\temp\cert.cer to the remote server Server1 in LocalMachine\My (Personal).

        .EXAMPLE
            Add-DbaComputerCertificate -Path C:\temp\cert.cer

            Adds the local C:\temp\cert.cer to the local computer's LocalMachine\My (Personal) certificate store.
    #>
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "High")]
    param (
        [Alias("ServerInstance", "SqlServer", "SqlInstance")]
        [DbaInstance[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [securestring]$Password,
        [parameter(ValueFromPipeline)]
        [System.Security.Cryptography.X509Certificates.X509Certificate2[]]$Certificate,
        [string]$Path,
        [string]$Store = "LocalMachine",
        [string]$Folder = "My",
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {

        if ($Path) {
            if (!(Test-Path -Path $Path)) {
                Stop-Function -Message "Path ($Path) does not exist." -Category InvalidArgument
                return
            }

            try {
                # This may be too much, but ¯\_(ツ)_/¯
                $bytes = [System.IO.File]::ReadAllBytes($Path)
                $Certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
                $Certificate.Import($bytes, $Password, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::DefaultKeySet)
            }
            catch {
                Stop-Function -Message "Can't import certificate." -ErrorRecord $_
                return
            }
        }

        #region Remoting Script
        $scriptBlock = {

            param (
                $CertificateData,

                [securestring]$Password,

                $Store,

                $Folder
            )

            $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
            $cert.Import($CertificateData, $Password, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::DefaultKeySet)
            Write-Message -Level Verbose -Message "Importing cert to $Folder\$Store"
            $tempStore = New-Object System.Security.Cryptography.X509Certificates.X509Store($Folder, $Store)
            $tempStore.Open('ReadWrite')
            $tempStore.Add($cert)
            $tempStore.Close()

            Write-Message -Level Verbose -Message "Searching Cert:\$Store\$Folder"
            Get-ChildItem "Cert:\$Store\$Folder" -Recurse | Where-Object { $_.Thumbprint -eq $cert.Thumbprint }
        }
        #endregion Remoting Script
    }
    process {
        if (Test-FunctionInterrupt) { return }

        if (-not $Certificate) {
            Stop-Function -Message "You must specify either Certificate or Path" -Category InvalidArgument
            return
        }

        foreach ($cert in $Certificate) {

            try {
                $certData = $cert.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::PFX, $Password)
            }
            catch {
                Stop-Function -Message "Can't export certificate" -ErrorRecord $_ -Continue
            }

            foreach ($computer in $ComputerName) {

                if ($PScmdlet.ShouldProcess("local", "Connecting to $computer to import cert")) {
                    try {
                        Invoke-Command2 -ComputerName $computer -Credential $Credential -ArgumentList $certdata, $Password, $Store, $Folder -ScriptBlock $scriptblock -ErrorAction Stop |
                            Select-DefaultView -Property FriendlyName, DnsNameList, Thumbprint, NotBefore, NotAfter, Subject, Issuer
                    }
                    catch {
                        Stop-Function -Message "Failure" -ErrorRecord $_ -Target $computer -Continue
                    }
                }
            }
        }
    }
}
function Add-DbaPfDataCollectorCounter {
    <#
        .SYNOPSIS
            Adds a Performance Data Collector Counter.

        .DESCRIPTION
            Adds a Performance Data Collector Counter.

        .PARAMETER ComputerName
            The target computer. Defaults to localhost.

        .PARAMETER Credential
            Allows you to login to $ComputerName using alternative credentials. To use:

            $cred = Get-Credential, then pass $cred object to the -Credential parameter.
    
        .PARAMETER CollectorSet
            The Collector Set name.

        .PARAMETER Collector
            The Collector name.
    
        .PARAMETER Counter
            The Counter name. This must be in the form of '\Processor(_Total)\% Processor Time'.
    
        .PARAMETER InputObject
            Accepts the object output by Get-DbaPfDataCollector via the pipeline.
    
        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
                   
        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
    
        .NOTES
            Tags: PerfMon
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
    
        .LINK
            https://dbatools.io/Add-DbaPfDataCollectorCounter

        .EXAMPLE
            Add-DbaPfDataCollectorCounter -ComputerName sql2017 -CollectorSet 'System Correlation' -Collector DataCollector01  -Counter '\LogicalDisk(*)\Avg. Disk Queue Length'
    
            Adds the '\LogicalDisk(*)\Avg. Disk Queue Length' counter within the DataCollector01 collector within the System Correlation collector set on sql2017.
    
        .EXAMPLE
            Get-DbaPfDataCollector | Out-GridView -PassThru | Add-DbaPfDataCollectorCounter -Counter '\LogicalDisk(*)\Avg. Disk Queue Length' -Confirm
    
            Allows you to select which Data Collector you'd like to add the counter '\LogicalDisk(*)\Avg. Disk Queue Length' on localhost and prompts for confirmation.
    #>
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Low")]
    param (
        [DbaInstance[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias("DataCollectorSet")]
        [string[]]$CollectorSet,
        [Alias("DataCollector")]
        [string[]]$Collector,
        [Alias("Name")]
        [parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [object[]]$Counter,
        [parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [switch]$EnableException
    )
    begin {
        $setscript = {
            $setname = $args[0]; $Addxml = $args[1]
            $set = New-Object -ComObject Pla.DataCollectorSet
            $set.SetXml($Addxml)
            $set.Commit($setname, $null, 0x0003) #add or modify.
            $set.Query($setname, $Null)
        }
    }
    process {
        if ($InputObject.Credential -and (Test-Bound -ParameterName Credential -Not)) {
            $Credential = $InputObject.Credential
        }
        
        if (($InputObject | Get-Member -MemberType NoteProperty -ErrorAction SilentlyContinue).Count -le 3 -and $InputObject.ComputerName -and $InputObject.Name) {
            # it's coming from Get-DbaPfAvailableCounter
            $ComputerName = $InputObject.ComputerName
            $Counter = $InputObject.Name
            $InputObject = $null
        }
        
        if (-not $InputObject -or ($InputObject -and (Test-Bound -ParameterName ComputerName))) {
            foreach ($computer in $ComputerName) {
                $InputObject += Get-DbaPfDataCollector -ComputerName $computer -Credential $Credential -CollectorSet $CollectorSet -Collector $Collector
            }
        }
        
        if ($InputObject) {
            if (-not $InputObject.DataCollectorObject) {
                Stop-Function -Message "InputObject is not of the right type. Please use Get-DbaPfDataCollector or Get-DbaPfAvailableCounter."
                return
            }
        }
        
        foreach ($object in $InputObject) {
            $computer = $InputObject.ComputerName
            $null = Test-ElevationRequirement -ComputerName $computer -Continue
            $setname = $InputObject.DataCollectorSet
            $collectorname = $InputObject.Name
            $xml = [xml]($InputObject.DataCollectorSetXml)
            
            foreach ($countername in $counter) {
                $node = $xml.SelectSingleNode("//Name[.='$collectorname']")
                $newitem = $xml.CreateElement('Counter')
                $null = $newitem.PsBase.InnerText = $countername
                $null = $node.ParentNode.AppendChild($newitem)
                $newitem = $xml.CreateElement('CounterDisplayName')
                $null = $newitem.PsBase.InnerText = $countername
                $null = $node.ParentNode.AppendChild($newitem)
            }
            $plainxml = $xml.OuterXml
            
            if ($Pscmdlet.ShouldProcess("$computer", "Adding $counters to $collectorname with the $setname collection set")) {
                try {
                    $results = Invoke-Command2 -ComputerName $computer -Credential $Credential -ScriptBlock $setscript -ArgumentList $setname, $plainxml -ErrorAction Stop
                    Write-Message -Level Verbose -Message " $results"
                    Get-DbaPfDataCollectorCounter -ComputerName $computer -Credential $Credential -CollectorSet $setname -Collector $collectorname -Counter $counter
                }
                catch {
                    Stop-Function -Message "Failure importing $Countername to $computer." -ErrorRecord $_ -Target $computer -Continue
                }
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Add-DbaRegisteredServer {
    <#
        .SYNOPSIS
            Adds registered servers to SQL Server Central Management Server (CMS)

        .DESCRIPTION
            Adds registered servers to SQL Server Central Management Server (CMS). If you need more flexiblity, look into Import-DbaRegisteredServer which
            accepts multiple kinds of input and allows you to add reg servers from different CMSes.

        .PARAMETER SqlInstance
            The target SQL Server instance

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER ServerName
            Server Name is the actual SQL instance name (labeled Server Name)

        .PARAMETER Name
            Name is basically the nickname in SSMS CMS interface (labeled Registered Server Name)

        .PARAMETER Description
            Adds a description for the registered server

        .PARAMETER Group
            Adds the registered server to a specific group.

        .PARAMETER InputObject
            Allows the piping of a registered server group

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.

            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.

            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Chrissy LeMaire (@cl)
            Tags: RegisteredServer, CMS

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Add-DbaRegisteredServer

        .EXAMPLE
           Add-DbaRegisteredServer -SqlInstance sql2008 -ServerName sql01

           Creates a registered server on sql2008's CMS which points to the SQL Server, sql01. When scrolling in CMS, the name "sql01" will be visible.

        .EXAMPLE
           Add-DbaRegisteredServer -SqlInstance sql2008 -ServerName sql01 -Name "The 2008 Clustered Instance" -Description "HR's Dedicated SharePoint instance"

           Creates a registered server on sql2008's CMS which points to the SQL Server, sql01. When scrolling in CMS, "The 2008 Clustered Instance" will be visible.
           Clearly this is hard to explain ;)

        .EXAMPLE
           Add-DbaRegisteredServer -SqlInstance sql2008 -ServerName sql01 -Group hr\Seattle

           Creates a registered server on sql2008's CMS which points to the SQL Server, sql01. When scrolling in CMS, the name "sql01" will be visible within the Seattle group which is in the hr group.

        .EXAMPLE
           Get-DbaRegisteredServerGroup -SqlInstance sql2008 -Group hr\Seattle | Add-DbaRegisteredServer -ServerName sql01111

           Creates a registered server on sql2008's CMS which points to the SQL Server, sql01. When scrolling in CMS, the name "sql01" will be visible within the Seattle group which is in the hr group.
    #>
    [CmdletBinding(SupportsShouldProcess)]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory)]
        [string]$ServerName,
        [string]$Name = $ServerName,
        [string]$Description,
        [object]$Group,
        [parameter(ValueFromPipeline)]
        [Microsoft.SqlServer.Management.RegisteredServers.ServerGroup[]]$InputObject,
        [switch]$EnableException
    )
    process {
        if (-not $InputObject -and -not $SqlInstance) {
            Stop-Function -Message "You must either pipe in a registered server group or specify a sqlinstance"
            return
        }
        
        # double check in case a null name was bound
        if (-not $Name) {
            $Name = $ServerName
        }
        
        foreach ($instance in $SqlInstance) {
            if (($Group)) {
                if ($Group -is [Microsoft.SqlServer.Management.RegisteredServers.ServerGroup]) {
                    $InputObject += Get-DbaRegisteredServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Group $Group.Name
                }
                else {
                    $InputObject += Get-DbaRegisteredServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Group $Group
                }
            }
            else {
                $InputObject += Get-DbaRegisteredServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Id 1
            }

            if (-not $InputObject) {
                Stop-Function -Message "No matching groups found on $instance" -Continue
            }
        }

        foreach ($reggroup in $InputObject) {
            $parentserver = Get-RegServerParent -InputObject $reggroup

            if ($null -eq $parentserver) {
                Stop-Function -Message "Something went wrong and it's hard to explain, sorry. This basically shouldn't happen." -Continue
            }

            $server = $parentserver.ServerConnection.SqlConnectionObject

            if ($Pscmdlet.ShouldProcess($parentserver.SqlInstance, "Adding $ServerName")) {
                try {
                    $newserver = New-Object Microsoft.SqlServer.Management.RegisteredServers.RegisteredServer($reggroup, $Name)
                    $newserver.ServerName = $ServerName
                    $newserver.Description = $Description
                    $newserver.Create()

                    Get-DbaRegisteredServer -SqlInstance $server -Name $Name -ServerName $ServerName
                }
                catch {
                    Stop-Function -Message "Failed to add $ServerName on $($parentserver.SqlInstance)" -ErrorRecord $_ -Continue
                }
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Add-DbaRegisteredServerGroup {
    <#
        .SYNOPSIS
            Adds registered server groups to SQL Server Central Management Server (CMS)

        .DESCRIPTION
            Adds registered server groups to SQL Server Central Management Server (CMS). If you need more flexiblity, look into Import-DbaRegisteredServer which
            accepts multiple kinds of input and allows you to add reg servers and groups from different CMSes.

        .PARAMETER SqlInstance
            The target SQL Server instance

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Name
            The name of the registered server group

        .PARAMETER Description
            The description for the registered server group

        .PARAMETER Group
            The SQL Server Central Management Server group. If no groups are specified, the new group will be created at the root.

        .PARAMETER InputObject
            Allows results from Get-DbaRegisteredServerGroup to be piped in

        .PARAMETER IncludeRegisteredServers
            Create the registered servers within the group, too

        .PARAMETER InputObject
            Allows results from Get-DbaRegisteredServerGroup to be piped in

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.

            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.

            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Chrissy LeMaire (@cl)
            Tags: RegisteredServer, CMS

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Add-DbaRegisteredServerGroup

        .EXAMPLE
            Add-DbaRegisteredServerGroup -SqlInstance sql2012 -Name HR

            Creates a registered server group called HR, in the root of sql2012's CMS

        .EXAMPLE
            Add-DbaRegisteredServerGroup -SqlInstance sql2012, sql2014 -Name subfolder -Group HR

            Creates a registered server group on sql2012 and sql2014 called subfolder within the HR group

    .EXAMPLE
            Get-DbaRegisteredServerGroup -SqlInstance sql2012, sql2014 -Group HR | Add-DbaRegisteredServerGroup -Name subfolder

            Creates a registered server group on sql2012 and sql2014 called subfolder within the HR group of each server
    #>
    [CmdletBinding(SupportsShouldProcess)]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory)]
        [string]$Name,
        [string]$Description,
        [string]$Group,
        [parameter(ValueFromPipeline)]
        [Microsoft.SqlServer.Management.RegisteredServers.ServerGroup[]]$InputObject,
        [switch]$EnableException
    )
    process {
        if (-not $InputObject -and -not $SqlInstance) {
            Stop-Function -Message "You must either pipe in a registered server group or specify a sqlinstance"
            return
        }
        foreach ($instance in $SqlInstance) {
            if ((Test-Bound -ParameterName Group)) {
                $InputObject += Get-DbaRegisteredServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Group $Group
            }
            else {
                $InputObject += Get-DbaRegisteredServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Id 1
            }
        }

        foreach ($reggroup in $InputObject) {
            $parentserver = Get-RegServerParent -InputObject $reggroup
            $server = $parentserver.ServerConnection.ServerInstance.SqlConnectionObject

            if ($null -eq $parentserver) {
                Stop-Function -Message "Something went wrong and it's hard to explain, sorry. This basically shouldn't happen." -Continue
            }

            if ($Pscmdlet.ShouldProcess($parentserver.SqlInstance, "Adding $Name")) {
                try {
                    $newgroup = New-Object Microsoft.SqlServer.Management.RegisteredServers.ServerGroup($reggroup, $Name)
                    $newgroup.Description = $Description
                    $newgroup.Create()
                    
                    Get-DbaRegisteredServerGroup -SqlInstance $parentserver.ServerConnection.SqlConnectionObject -Group (Get-RegServerGroupReverseParse -object $newgroup)
                    $parentserver.ServerConnection.Disconnect()
                }
                catch {
                    Stop-Function -Message "Failed to add $reggroup on $server" -ErrorRecord $_ -Continue
                }
            }
        }
    }
}
function Backup-DbaDatabase {
    <#
            .SYNOPSIS
                Backup one or more SQL Sever databases from a single SQL Server SqlInstance.

            .DESCRIPTION
                Performs a backup of a specified type of 1 or more databases on a single SQL Server Instance. These backups may be Full, Differential or Transaction log backups.

            .PARAMETER SqlInstance
                The SQL Server instance hosting the databases to be backed up.

            .PARAMETER SqlCredential
                Credentials to connect to the SQL Server instance if the calling user doesn't have permission.

            .PARAMETER Database
                The database(s) to process. This list is auto-populated from the server. If unspecified, all databases will be processed.

            .PARAMETER ExcludeDatabase
                The database(s) to exclude. This list is auto-populated from the server.

            .PARAMETER BackupFileName
                The name of the file to backup to. This is only accepted for single database backups.
                If no name is specified then the backup files will be named DatabaseName_yyyyMMddHHmm (i.e. "Database1_201714022131") with the appropriate extension.

                If the same name is used repeatedly, SQL Server will add backups to the same file at an incrementing position.

                SQL Server needs permissions to write to the specified location. Path names are based on the SQL Server (C:\ is the C drive on the SQL Server, not the machine running the script).

            .PARAMETER BackupDirectory
                Path in which to place the backup files. If not specified, the backups will be placed in the default backup location for SqlInstance.
                If multiple paths are specified, the backups will be striped across these locations. This will overwrite the FileCount option.

                If the path does not exist, Sql Server will attempt to create it. Folders are created by the Sql Instance, and checks will be made for write permissions.

                File Names with be suffixed with x-of-y to enable identifying striped sets, where y is the number of files in the set and x ranges from 1 to y.

            .PARAMETER CopyOnly
                If this switch is enabled, CopyOnly backups will be taken. By default function performs a normal backup, these backups interfere with the restore chain of the database. CopyOnly backups will not interfere with the restore chain of the database.

                For more details please refer to this MSDN article - https://msdn.microsoft.com/en-us/library/ms191495.aspx

            .PARAMETER Type
                The type of SQL Server backup to perform. Accepted values are "Full", "Log", "Differential", "Diff", "Database"

            .PARAMETER FileCount
                This is the number of striped copies of the backups you wish to create.    This value is overwritten if you specify multiple Backup Directories.

            .PARAMETER CreateFolder
                If this switch is enabled, each database will be backed up into a separate folder on each of the paths specified by BackupDirectory.

            .PARAMETER CompressBackup
                If this switch is enabled, the function will try to perform a compressed backup if supported by the version and edition of SQL Server. Otherwise, this function will use the server's default setting for compression.

            .PARAMETER MaxTransferSize
                Sets the size of the unit of transfer. Values must be a multiple of 64kb.

            .PARAMETER Blocksize
                Specifies the block size to use. Must be one of 0.5KB, 1KB, 2KB, 4KB, 8KB, 16KB, 32KB or 64KB. This can be specified in bytes.
                Refer to https://msdn.microsoft.com/en-us/library/ms178615.aspx for more detail

            .PARAMETER BufferCount
                Number of I/O buffers to use to perform the operation.
                Refer to https://msdn.microsoft.com/en-us/library/ms178615.aspx for more detail

            .PARAMETER Checksum
                If this switch is enabled, the backup checksum will be calculated.

            .PARAMETER Verify
                If this switch is enabled, the backup will be verified by running a RESTORE VERIFYONLY against the SqlInstance

            .PARAMETER WithFormat
                 Formats the media as the first step of the backup operation. NOTE: This will set Initialize and SkipTapeHeader to $true.

            .PARAMETER Initialize
                 Initializes the media as part of the backup operation.

            .PARAMETER SkipTapeHeader
                 Initializes the media as part of the backup operation.

            .PARAMETER InputObject
                Internal parameter

            .PARAMETER AzureBaseUrl
                The URL to the basecontainer of an Azure storage account to write backups to.

                If specified, the only other parameters than can be used are "NoCopyOnly", "Type", "CompressBackup", "Checksum", "Verify", "AzureCredential", "CreateFolder".

            .PARAMETER AzureCredential
                The name of the credential on the SQL instance that can write to the AzureBaseUrl.

            .PARAMETER NoRecovery
                This is passed in to perform a tail log backup if needed

            .PARAMETER BuildPath
                By default this command won't attempt to create missing paths, this switch will change the behavious so that it wll

            .PARAMETER IgnoreFileChecks
                This switch stops the function from checking for the validity of paths. This can be useful if SQL Server only has read access to the backup area.
                Note, that as we can't check the path you may well end up with errors.

            .PARAMETER OutputScriptOnly
                Switch causes only the T-SQL script for the backup to be generated. Will not create any paths if they do not exist

            .PARAMETER EnableException
                By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
                This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
                Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

            .PARAMETER WhatIf
                If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

            .PARAMETER Confirm
                If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

            .NOTES
                Tags: DisasterRecovery, Backup, Restore
                Author: Stuart Moore (@napalmgram), stuart-moore.com

                Website: https://dbatools.io
                Copyright: (C) Chrissy LeMaire, [email protected]
                License: MIT https://opensource.org/licenses/MIT

            .EXAMPLE
                Backup-DbaDatabase -SqlInstance Server1 -Database HR, Finance

                This will perform a full database backup on the databases HR and Finance on SQL Server Instance Server1 to Server1's default backup directory.

            .EXAMPLE
                Backup-DbaDatabase -SqlInstance sql2016 -BackupDirectory C:\temp -Database AdventureWorks2014 -Type Full

                Backs up AdventureWorks2014 to sql2016's C:\temp folder.

            .EXAMPLE
                Backup-DbaDatabase -SqlInstance sql2016 -AzureBaseUrl https://dbatoolsaz.blob.core.windows.net/azbackups/ -AzureCredential dbatoolscred -Type Full -CreateFolder

                Performs a full backup of all databases on the sql2016 instance to their own containers under the https://dbatoolsaz.blob.core.windows.net/azbackups/ container on Azure blog storage using the sql credential "dbatoolscred" registered on the sql2016 instance.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "")] #For AzureCredential
    param (
        [parameter(ParameterSetName = "Pipe", Mandatory = $true)]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [string[]]$BackupDirectory,
        [string]$BackupFileName,
        [switch]$CopyOnly,
        [ValidateSet('Full', 'Log', 'Differential', 'Diff', 'Database')]
        [string]$Type = 'Database',
        [parameter(ParameterSetName = "NoPipe", Mandatory = $true, ValueFromPipeline = $true)]
        [object[]]$InputObject,
        [switch]$CreateFolder,
        [int]$FileCount = 0,
        [switch]$CompressBackup,
        [switch]$Checksum,
        [switch]$Verify,
        [int]$MaxTransferSize,
        [int]$BlockSize,
        [int]$BufferCount,
        [string]$AzureBaseUrl,
        [string]$AzureCredential,
        [switch]$NoRecovery,
        [switch]$BuildPath,
        [switch]$WithFormat,
        [switch]$Initialize,
        [switch]$SkipTapeHeader,
        [switch]$IgnoreFileChecks,
        [switch]$OutputScriptOnly,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {

        if ($SqlInstance.length -ne 0) {
            Write-Message -Level Verbose -Message "Connecting to $SqlInstance"
            try {
                $Server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential -AzureUnsupported
            }
            catch {
                Stop-Function -Message "Cannot connect to $SqlInstance" -ErrorRecord $_
                return
            }

            if ($Database) {
                $InputObject = $server.Databases | Where-Object Name -in $Database
            }
            else {
                $InputObject = $server.Databases | Where-Object Name -ne 'tempdb'
            }

            if ($ExcludeDatabase) {
                $InputObject = $InputObject | Where-Object Name -notin $ExcludeDatabase
            }

            if ($BackupDirectory.Count -gt 1) {
                Write-Message -Level Verbose -Message "Multiple Backup Directories, striping"
                $Filecount = $BackupDirectory.Count
            }

            if ($InputObject.Count -gt 1 -and $BackupFileName -ne '') {
                Stop-Function -Message "1 BackupFile specified, but more than 1 database."
                return
            }

            if (($MaxTransferSize % 64kb) -ne 0 -or $MaxTransferSize -gt 4mb) {
                Stop-Function -Message "MaxTransferSize value must be a multiple of 64kb and no greater than 4MB"
                return
            }
            if ($BlockSize) {
                if ($BlockSize -notin (0.5kb, 1kb, 2kb, 4kb, 8kb, 16kb, 32kb, 64kb)) {
                    Stop-Function -Message "Block size must be one of 0.5kb,1kb,2kb,4kb,8kb,16kb,32kb,64kb"
                    return
                }
            }
            if ('' -ne $AzureBaseUrl) {
                if ($null -eq $AzureCredential) {
                    Stop-Function -Message "You must provide the credential name for the Azure Storage Account"
                    return
                }
                $AzureBaseUrl = $AzureBaseUrl.Trim("/")
                $FileCount = 1
                $BackupDirectory = $AzureBaseUrl
            }

            if ($OutputScriptOnly) {
                $IgnoreFileChecks = $true
            }
        }
    }

    process {
        if (!$SqlInstance -and !$InputObject) {
            Stop-Function -Message "You must specify a server and database or pipe some databases"
            return
        }

        Write-Message -Level Verbose -Message "$($InputObject.Count) database to backup"

        foreach ($Database in $InputObject) {
            $ProgressId = Get-Random
            $failures = @()
            $dbname = $Database.Name

            if ($dbname -eq "tempdb") {
                Stop-Function -Message "Backing up tempdb not supported" -Continue
            }

            if ('Normal' -notin ($Database.Status -split ',')) {
                Stop-Function -Message "Database status not Normal. $dbname skipped." -Continue
            }

            if ($Database.DatabaseSnapshotBaseName) {
                Stop-Function -Message "Backing up snapshots not supported. $dbname skipped." -Continue
            }

            if ($null -eq $server) { $server = $Database.Parent }

            Write-Message -Level Verbose -Message "Backup database $database"

            if ($null -eq $Database.RecoveryModel) {
                $Database.RecoveryModel = $server.Databases[$Database.Name].RecoveryModel
                Write-Message -Level Verbose -Message "$dbname is in $($Database.RecoveryModel) recovery model"
            }

            # Fixes one-off cases of StackOverflowException crashes, see issue 1481
            $dbRecovery = $Database.RecoveryModel.ToString()
            if ($dbRecovery -eq 'Simple' -and $Type -eq 'Log') {
                $failreason = "$database is in simple recovery mode, cannot take log backup"
                $failures += $failreason
                Write-Message -Level Warning -Message "$failreason"
            }

            $lastfull = $database.Refresh().LastBackupDate.Year

            if ($Type -notin @("Database", "Full") -and $lastfull -eq 1) {
                $failreason = "$database does not have an existing full backup, cannot take log or differentialbackup"
                $failures += $failreason
                Write-Message -Level Warning -Message "$failreason"
            }

            if ($CopyOnly -ne $true) {
                $CopyOnly = $false
            }

            $server.ConnectionContext.StatementTimeout = 0
            $backup = New-Object Microsoft.SqlServer.Management.Smo.Backup
            $backup.Database = $Database.Name
            $Suffix = "bak"

            if ($CompressBackup) {
                if ($server.Edition -like 'Express*' -or ($server.VersionMajor -eq 10 -and $server.VersionMinor -eq 0 -and $server.Edition -notlike '*enterprise*') -or $server.VersionMajor -lt 10) {
                    Write-Message -Level Warning -Message "Compression is not supported with this version/edition of Sql Server"
                }
                else {
                    Write-Message -Level Verbose -Message "Compression enabled"
                    $backup.CompressionOption = 1
                }
            }

            if ($Checksum) {
                $backup.Checksum = $true
            }

            if ($Type -in 'Diff', 'Differential') {
                Write-Message -Level VeryVerbose -Message "Creating differential backup"
                $SMOBackuptype = "Database"
                $backup.Incremental = $true
                $outputType = 'Differential'
            }
            $Backup.NoRecovery = $false
            if ($Type -eq "Log") {
                Write-Message -Level VeryVerbose -Message "Creating log backup"
                $Suffix = "trn"
                $OutputType = 'Log'
                $SMOBackupType = 'Log'
                $Backup.NoRecovery = $NoRecovery
            }

            if ($Type -in 'Full', 'Database') {
                Write-Message -Level VeryVerbose -Message "Creating full backup"
                $SMOBackupType = "Database"
                $OutputType = 'Full'
            }

            $backup.CopyOnly = $copyonly
            $backup.Action = $SMOBackupType
            if ('' -ne $AzureBaseUrl) {
                $backup.CredentialName = $AzureCredential
            }

            Write-Message -Level Verbose -Message "Building file name"

            $BackupFinalName = ''
            $FinalBackupPath = @()
            if ('NUL' -eq $BackupFileName) {
                $FinalBackupPath += 'NUL:'
                $IgnoreFileChecks = $true
            }
            elseif ('' -ne $BackupFileName) {
                $File = New-Object System.IO.FileInfo($BackupFileName)
                $BackupFinalName = $file.Name
                $suffix = $file.extension -Replace '^\.',''
                if ( '' -ne (Split-Path $BackupFileName)) {
                    Write-Message -Level Verbose -Message "Fully qualified path passed in"
                    $FinalBackupPath += [IO.Path]::GetFullPath($file.DirectoryName)
                }
            }
            else {
                $timestamp = (Get-Date -Format yyyyMMddHHmm)
                Write-Message -Level VeryVerbose -Message "Setting filename"
                $BackupFinalName = "$($dbname)_$timestamp.$suffix"
            }

            Write-Message -Level Verbose -Message "Building backup path"
            if ($FinalBackupPath.Count -eq 0) {
                $FinalBackupPath += $BackupDirectory
            }

            if ($BackupDirectory.Count -eq 1 -and $Filecount -gt 1) {
                for ($i = 0; $i -lt ($Filecount - 1); $i++) {
                    $FinalBackupPath += $FinalBackupPath[0]
                }
            }

            if ($AzureBaseUrl -or $AzureCredential) {
                $slash = "/"
            }
            else {
                $slash = "\"
            }
            if ($FinalBackupPath.Count -gt 1) {
                $File = New-Object System.IO.FileInfo($BackupFinalName)
                for ($i = 0; $i -lt $FinalBackupPath.Count; $i++) {
                    $FinalBackupPath[$i] = $FinalBackupPath[$i] + $slash + $($File.BaseName) + "-$($i+1)-of-$FileCount.$suffix"
                }
            }
            elseif ($FinalBackupPath[0] -ne 'NUL:') {
                $FinalBackupPath[0] = $FinalBackupPath[0] + $slash + $BackupFinalName
            }

            if ($CreateFolder -and $FinalBackupPath[0] -ne 'NUL:') {
                for ($i = 0; $i -lt $FinalBackupPath.Count; $i++) {
                    $parent = [IO.Path]::GetDirectoryName($FinalBackupPath[$i])
                    $leaf = [IO.Path]::GetFileName($FinalBackupPath[$i])
                    $FinalBackupPath[$i] = [IO.Path]::Combine($parent, $dbname, $leaf)
                }
            }

            if (-not $IgnoreFileChecks -and -not $AzureBaseUrl) {
                $parentPaths = ($FinalBackupPath | ForEach-Object { Split-Path $_ } | Select-Object -Unique)
                foreach ($parentPath in $parentPaths) {
                    if (-not (Test-DbaSqlPath -SqlInstance $server -Path $parentPath)) {
                        if (($BuildPath -eq $true) -or ($CreateFolder -eq $True)) {
                            $null = New-DbaSqlDirectory -SqlInstance $server -Path $parentPath
                        }
                        else {
                            $failreason += "SQL Server cannot check if $parentPath exists. You can try disabiling this check with -IgnoreFileChecks"
                            $failures += $failreason
                            Write-Message -Level Warning -Message "$failreason"
                        }
                    }
                }
            }


            if ('' -eq $AzureBaseUrl -and $BackupDirectory) {
                $FinalBackupPath = $FinalBackupPath | ForEach-Object { [IO.Path]::GetFullPath($_) }
            }


            $script = $null
            $backupComplete = $false

            if (!$failures) {
                $Filecount = $FinalBackupPath.Count

                foreach ($backupfile in $FinalBackupPath) {
                    $device = New-Object Microsoft.SqlServer.Management.Smo.BackupDeviceItem
                    if ('' -ne $AzureBaseUrl) {
                        $device.DeviceType = "URL"
                    }
                    else {
                        $device.DeviceType = "File"
                    }
                    
                    if ($WithFormat) {
                        Write-Message -Message "WithFormat specified. Ensuring Initialize and SkipTapeHeader are set to true." -Level Verbose
                        $Initialize = $true
                        $SkipTapeHeader = $true
                    }
                    
                    $backup.FormatMedia = $WithFormat
                    $backup.Initialize = $Initialize
                    $backup.SkipTapeHeader = $SkipTapeHeader
                    $device.Name = $backupfile
                    $backup.Devices.Add($device)
                }
                $humanBackupFile = $FinalBackupPath -Join ','
                Write-Message -Level Verbose -Message "Devices added"
                $percent = [Microsoft.SqlServer.Management.Smo.PercentCompleteEventHandler] {
                    Write-Progress -id $ProgressId -activity "Backing up database $dbname to $humanBackupFile" -percentcomplete $_.Percent -status ([System.String]::Format("Progress: {0} %", $_.Percent))
                }
                $backup.add_PercentComplete($percent)
                $backup.PercentCompleteNotification = 1
                $backup.add_Complete($complete)

                if ($MaxTransferSize) {
                    $backup.MaxTransferSize = $MaxTransferSize
                }
                if ($BufferCount) {
                    $backup.BufferCount = $BufferCount
                }
                if ($BlockSize) {
                    $backup.Blocksize = $BlockSize
                }

                Write-Progress -id $ProgressId -activity "Backing up database $dbname to $humanBackupFile" -percentcomplete 0 -status ([System.String]::Format("Progress: {0} %", 0))

                try {
                    if ($Pscmdlet.ShouldProcess($server.Name, "Backing up $dbname to $humanBackupFile")) {
                        if ($OutputScriptOnly -ne $True) {
                            $Filelist = @()
                            $FileList += $server.Databases[$dbname].FileGroups.Files | Select-Object @{ Name = "FileType"; Expression = { "D" } }, @{ Name = "Type"; Expression = { "D" } }, @{ Name = "LogicalName"; Expression = { $_.Name } }, @{ Name = "PhysicalName"; Expression = { $_.FileName } }
                            $FileList += $server.Databases[$dbname].LogFiles | Select-Object @{ Name = "FileType"; Expression = { "L" } }, @{ Name = "Type"; Expression = { "L" } }, @{ Name = "LogicalName"; Expression = { $_.Name } }, @{ Name = "PhysicalName"; Expression = { $_.FileName } }

                            $backup.SqlBackup($server)
                            $script = $backup.Script($server)
                            Write-Progress -id $ProgressId -activity "Backing up database $dbname to $backupfile" -status "Complete" -Completed
                            $BackupComplete = $true
                            if ($server.VersionMajor -eq '8') {
                                $HeaderInfo = Get-BackupAncientHistory -SqlInstance $server -Database $dbname
                            }
                            else {
                                $HeaderInfo = Get-DbaBackupHistory -SqlInstance $server -Database $dbname -Last -IncludeCopyOnly | Sort-Object -Property End -Descending | Select-Object -First 1
                            }
                            $Verified = $false
                            if ($Verify) {
                                $verifiedresult = [PSCustomObject]@{
                                    SqlInstance          = $server.name
                                    DatabaseName         = $dbname
                                    BackupComplete       = $BackupComplete
                                    BackupFilesCount     = $FinalBackupPath.Count
                                    BackupFile           = (Split-Path $FinalBackupPath -Leaf)
                                    BackupFolder         = (Split-Path $FinalBackupPath | Sort-Object -Unique)
                                    BackupPath           = ($FinalBackupPath | Sort-Object -Unique)
                                    Script               = $script
                                    Notes                = $failures -join (',')
                                    FullName             = ($FinalBackupPath | Sort-Object -Unique)
                                    FileList             = $FileList
                                    SoftwareVersionMajor = $server.VersionMajor
                                    Type                 = $outputType
                                    FirstLsn             = $HeaderInfo.FirstLsn
                                    DatabaseBackupLsn    = $HeaderInfo.DatabaseBackupLsn
                                    CheckPointLsn        = $HeaderInfo.CheckPointLsn
                                    LastLsn              = $HeaderInfo.LastLsn
                                    BackupSetId          = $HeaderInfo.BackupSetId
                                    LastRecoveryForkGUID = $HeaderInfo.LastRecoveryForkGUID
                                } | Restore-DbaDatabase -SqlInstance $server -DatabaseName DbaVerifyOnly -VerifyOnly -TrustDbBackupHistory -DestinationFilePrefix DbaVerifyOnly
                                if ($verifiedResult[0] -eq "Verify successful") {
                                    $failures += $verifiedResult[0]
                                    $Verified = $true
                                }
                                else {
                                    $failures += $verifiedResult[0]
                                    $Verified = $false
                                }
                            }
                            $HeaderInfo | Add-Member -Type NoteProperty -Name BackupComplete -Value $BackupComplete
                            $HeaderInfo | Add-Member -Type NoteProperty -Name BackupFile -Value (Split-Path $FinalBackupPath -Leaf)
                            $HeaderInfo | Add-Member -Type NoteProperty -Name BackupFilesCount -Value $FinalBackupPath.Count
                            if ($FinalBackupPath[0] -eq 'NUL:') {
                                $pathresult = "NUL:"
                            }
                            else {
                                $pathresult = (Split-Path $FinalBackupPath | Sort-Object -Unique)
                            }
                            $HeaderInfo | Add-Member -Type NoteProperty -Name BackupFolder -Value $pathresult
                            $HeaderInfo | Add-Member -Type NoteProperty -Name BackupPath -Value ($FinalBackupPath | Sort-Object -Unique)
                            $HeaderInfo | Add-Member -Type NoteProperty -Name DatabaseName -Value $dbname
                            $HeaderInfo | Add-Member -Type NoteProperty -Name Notes -Value ($failures -join (','))
                            $HeaderInfo | Add-Member -Type NoteProperty -Name Script -Value $script
                            $HeaderInfo | Add-Member -Type NoteProperty -Name Verified -Value $Verified
                        }
                        else {
                            $backup.Script($server)
                        }
                    }
                }
                catch {
                    if ($NoRecovery -and ($_.Exception.InnerException.InnerException.InnerException -like '*cannot be opened. It is in the middle of a restore.')) {
                        Write-Message -Message "Exception thrown by db going into restoring mode due to recovery" -Leve Verbose
                    }
                    else {
                        Write-Progress -id $ProgressId -activity "Backup" -status "Failed" -completed
                        Stop-Function -message "Backup Failed:  $($_.Exception.Message)" -EnableException $EnableException -ErrorRecord $_ -Continue
                        $BackupComplete = $false
                    }
                }
            }
            $OutputExclude = 'FullName', 'FileList', 'SoftwareVersionMajor'
            if ($failures.Count -eq 0) {
                $OutputExclude += ('Notes', 'FirstLsn', 'DatabaseBackupLsn', 'CheckpointLsn', 'LastLsn', 'BackupSetId', 'LastRecoveryForkGuid')
            }
            $headerinfo | Select-DefaultView -ExcludeProperty $OutputExclude
            $BackupFileName = $null
        }
    }
}
function Backup-DbaDatabaseMasterKey {
    <#
.SYNOPSIS
Backs up specified database master key.

.DESCRIPTION
Backs up specified database master key.

.PARAMETER SqlInstance
The target SQL Server instance.

.PARAMETER SqlCredential
Allows you to login to SQL Server using alternative credentials.

.PARAMETER Database
Backup master key from specific database(s).

.PARAMETER ExcludeDatabase
The database(s) to exclude - this list is auto-populated from the server.

.PARAMETER Path
The directory to export the key. If no path is specified, the default backup directory for the instance will be used.

.PARAMETER Password
The password to encrypt the exported key. This must be a SecureString.

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Certificate, Database

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
Backup-DbaDatabaseMasterKey -SqlInstance server1\sql2016

Prompts for export password, then logs into server1\sql2016 with Windows credentials then backs up all database keys to the default backup directory.

ComputerName : SERVER1
InstanceName : SQL2016
SqlInstance  : SERVER1\SQL2016
Database     : master
Filename     : E:\MSSQL13.SQL2016\MSSQL\Backup\server1$sql2016-master-20170614162311.key
Status       : Success

.EXAMPLE
Backup-DbaDatabaseMasterKey -SqlInstance Server1 -Database db1 -Path \\nas\sqlbackups\keys

Logs into sql2016 with Windows credentials then backs up db1's keys to the \\nas\sqlbackups\keys directory.

#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Security.SecureString]$Password,
        [string]$Path,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {

            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $databases = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                $databases = $databases | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }

            if (Test-Bound -ParameterName Path -Not) {
                $Path = $server.BackupDirectory
            }

            if (!$Path) {
                Stop-Function -Message "Path discovery failed. Please explicitly specify -Path" -Target $server -Continue
            }

            if (!(Test-DbaSqlPath -SqlInstance $server -Path $Path)) {
                Stop-Function -Message "$instance cannot access $Path" -Target $server -InnerErrorRecord $_ -Continue
            }

            foreach ($db in $databases) {

                if (!$db.IsAccessible) {
                    Write-Message -Level Warning -Message "Database $db is not accessible. Skipping."
                    continue
                }

                $masterkey = $db.MasterKey

                if (!$masterkey) {
                    Write-Message -Message "No master key exists in the $db database on $instance" -Target $db -Level Verbose
                    continue
                }

                # If you pass a password param, then you will not be prompted for each database, but it wouldn't be a good idea to build in insecurity
                if (Test-Bound -ParameterName Password -Not) {
                    $password = Read-Host -AsSecureString -Prompt "You must enter Service Key password for $instance"
                    $password2 = Read-Host -AsSecureString -Prompt "Type the password again"

                    if (([System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($password))) -ne ([System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($password2)))) {
                        Stop-Function -Message "Passwords do not match" -Continue
                    }
                }

                $time = (Get-Date -Format yyyMMddHHmmss)
                $dbname = $db.name
                $Path = $Path.TrimEnd("\")
                $fileinstance = $instance.ToString().Replace('\', '$')
                $filename = "$Path\$fileinstance-$dbname-$time.key"

                try {
                    $masterkey.export($filename, [System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($password)))
                    $status = "Success"
                }
                catch {
                    $status = "Failure"
                    Write-Message -Level Warning -Message "Backup failure: $($_.Exception.InnerException)"
                }

                Add-Member -Force -InputObject $masterkey -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                Add-Member -Force -InputObject $masterkey -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                Add-Member -Force -InputObject $masterkey -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                Add-Member -Force -InputObject $masterkey -MemberType NoteProperty -Name Database -value $dbname
                Add-Member -Force -InputObject $masterkey -MemberType NoteProperty -Name Filename -value $filename
                Add-Member -Force -InputObject $masterkey -MemberType NoteProperty -Name Status -value $status

                Select-DefaultView -InputObject $masterkey -Property ComputerName, InstanceName, SqlInstance, Database, 'Filename as Path', Status
            }
        }
    }
}
function Backup-DbaDbCertificate {
    <#
        .SYNOPSIS
            Exports database certificates from SQL Server using SMO.

        .DESCRIPTION
            Exports database certificates from SQL Server using SMO and outputs the .cer and .pvk files.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Certificate
            Exports certificate that matches the name(s).

        .PARAMETER Database
            Exports the encryptor for specific database(s).

        .PARAMETER ExcludeDatabase
            Database(s) to skip when exporting encryptors.

        .PARAMETER EncryptionPassword
            A string value that specifies the system path to encrypt the private key.

        .PARAMETER DecryptionPassword
            A string value that specifies the system path to decrypt the private key.

        .PARAMETER Path
            The path to output the files to. The path is relative to the SQL Server itself. If no path is specified, the default data directory will be used.

        .PARAMETER Suffix
            The suffix of the filename of the exported certificate.

        .PARAMETER InputObject
            Certificate object

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .NOTES
            Author: Jess Pomfret (@jpomfret)
            Tags: Migration, Certificate

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            Backup-DbaDbCertificate -SqlInstance Server1
            Exports all the certificates on the specified SQL Server to the default data path for the instance.

        .EXAMPLE
            $cred = Get-Credential sqladmin
            Backup-DbaDbCertificate -SqlInstance Server1 -SqlCredential $cred

            Connects using sqladmin credential and exports all the certificates on the specified SQL Server to the default data path for the instance.

        .EXAMPLE
            Backup-DbaDbCertificate -SqlInstance Server1 -Certificate Certificate1
            Exports only the certificate named Certificate1 on the specified SQL Server to the default data path for the instance.

        .EXAMPLE
            Backup-DbaDbCertificate -SqlInstance Server1 -Database AdventureWorks
            Exports only the certificates for AdventureWorks on the specified SQL Server to the default data path for the instance.

        .EXAMPLE
            Backup-DbaDbCertificate -SqlInstance Server1 -ExcludeDatabase AdventureWorks
            Exports all certificates except those for AdventureWorks on the specified SQL Server to the default data path for the instance.

        .EXAMPLE
            Backup-DbaDbCertificate -SqlInstance Server1 -Path \\Server1\Certificates -EncryptionPassword (ConvertTo-SecureString -force -AsPlainText GoodPass1234!!)
            Exports all the certificates and private keys on the specified SQL Server.

        .EXAMPLE
            $EncryptionPassword = ConvertTo-SecureString -AsPlainText "GoodPass1234!!" -force
            $DecryptionPassword = ConvertTo-SecureString -AsPlainText "Password4567!!" -force
            Backup-DbaDbCertificate -SqlInstance Server1 -EncryptionPassword $EncryptionPassword -DecryptionPassword $DecryptionPassword
            Exports all the certificates on the specified SQL Server using the supplied DecryptionPassword, since an EncryptionPassword is specified private keys are also exported.

        .EXAMPLE
            Backup-DbaDbCertificate -SqlInstance Server1 -Path \\Server1\Certificates
            Exports all certificates on the specified SQL Server to the specified path.

        .EXAMPLE
            Backup-DbaDbCertificate -SqlInstance Server1 -Suffix DbaTools
            Exports all certificates on the specified SQL Server to the specified path, appends DbaTools to the end of the filenames.

        .EXAMPLE
            Get-DbaDbCertificate -SqlInstance sql2016 | Backup-DbaDbCertificate
            Exports all certificates found on sql2016 to the default data directory.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory, ParameterSetName = "instance")]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(ParameterSetName = "instance")]
        [object[]]$Certificate,
        [parameter(ParameterSetName = "instance")]
        [object[]]$Database,
        [parameter(ParameterSetName = "instance")]
        [object[]]$ExcludeDatabase,
        [parameter(Mandatory = $false)]
        [Security.SecureString]$EncryptionPassword,
        [parameter(Mandatory = $false)]
        [Security.SecureString]$DecryptionPassword,
        [System.IO.FileInfo]$Path,
        [string]$Suffix = "$(Get-Date -format 'yyyyMMddHHmmssms')",
        [parameter(ValueFromPipeline, ParameterSetName = "collection")]
        [Microsoft.SqlServer.Management.Smo.Certificate[]]$InputObject,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {

        if ($EncryptionPassword.Length -eq 0 -and $DecryptionPassword.Length -gt 0) {
            Stop-Function -Message "If you specify an decryption password, you must also specify an encryption password" -Target $DecryptionPassword
        }

        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Backup-DbaDatabaseCertificate

        function export-cert ($cert) {
            $certName = $cert.Name
            $db = $cert.Parent
            $server = $db.Parent
            $instance = $server.Name
            $actualPath = $Path

            if ($null -eq $actualPath) {
                $actualPath = Get-SqlDefaultPaths -SqlInstance $server -filetype Data
            }

            $fullCertName = "$actualPath\$certName$Suffix"
            $exportPathKey = "$fullCertName.pvk"

            if (!(Test-DbaSqlPath -SqlInstance $server -Path $actualPath)) {
                Stop-Function -Message "$SqlInstance cannot access $actualPath" -Target $actualPath
            }

            if ($Pscmdlet.ShouldProcess($instance, "Exporting certificate $certName from $db on $instance to $actualPath")) {
                Write-Message -Level Verbose -Message "Exporting Certificate: $certName to $fullCertName"
                try {

                    $exportPathCert = "$fullCertName.cer"

                    # because the password shouldn't go to memory...
                    if ($EncryptionPassword.Length -gt 0 -and $DecryptionPassword.Length -gt 0) {

                        Write-Message -Level Verbose -Message "Both passwords passed in. Will export both cer and pvk."

                        $cert.export(
                            $exportPathCert,
                            $exportPathKey,
                            [System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($EncryptionPassword)),
                            [System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($DecryptionPassword))
                        )
                    }
                    elseif ($EncryptionPassword.Length -gt 0 -and $DecryptionPassword.Length -eq 0) {
                        Write-Message -Level Verbose -Message "Only encryption password passed in. Will export both cer and pvk."

                        $cert.export(
                            $exportPathCert,
                            $exportPathKey,
                            [System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($EncryptionPassword))
                        )
                    }
                    else {
                        Write-Message -Level Verbose -Message "No passwords passed in. Will export just cer."
                        $exportPathKey = "Password required to export key"
                        $cert.export($exportPathCert)
                    }

                    [pscustomobject]@{
                        ComputerName   = $server.ComputerName
                        InstanceName   = $server.ServiceName
                        SqlInstance    = $server.DomainInstanceName
                        Database       = $db.Name
                        Certificate    = $certName
                        Path           = $exportPathCert
                        Key            = $exportPathKey
                        ExportPath     = $exportPathCert
                        ExportKey      = $exportPathKey
                        exportPathCert = $exportPathCert
                        exportPathKey  = $exportPathKey
                        Status         = "Success"
                    } | Select-DefaultView -ExcludeProperty exportPathCert, exportPathKey, ExportPath, ExportKey
                }
                catch {

                    if ($_.Exception.InnerException) {
                        $exception = $_.Exception.InnerException.ToString() -Split "System.Data.SqlClient.SqlException: "
                        $exception = ($exception[1] -Split "at Microsoft.SqlServer.Management.Common.ConnectionManager")[0]
                    }
                    else {
                        $exception = $_.Exception
                    }
                    [pscustomobject]@{
                        ComputerName   = $server.ComputerName
                        InstanceName   = $server.ServiceName
                        SqlInstance    = $server.DomainInstanceName
                        Database       = $db.Name
                        Certificate    = $certName
                        Path           = $exportPathCert
                        Key            = $exportPathKey
                        ExportPath     = $exportPathCert
                        ExportKey      = $exportPathKey
                        exportPathCert = $exportPathCert
                        exportPathKey  = $exportPathKey
                        Status         = "Failure: $exception"
                    } | Select-DefaultView -ExcludeProperty exportPathCert, exportPathKey, ExportPath, ExportKey
                    Stop-Function -Message "$certName from $db on $instance cannot be exported." -Continue -Target $cert -ErrorRecord $_
                }
            }
        }
    }

    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                return
            }
            $databases = Get-DbaDatabase -SqlInstance $server | Where-Object IsAccessible

            if ($Database) {
                $databases = $databases | Where-Object Name -in $Database
            }
            if ($ExcludeDatabase) {
                $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }
            foreach ($db in $databases.Name) {
                $DBInputObject = Get-DbaDbCertificate -SqlInstance $server -Database $db
                if ($Certificate) {
                    $InputObject += $DBInputObject | Where-Object Name -In $Certificate
                }
                else {
                    $InputObject += $DBInputObject | Where-Object Name -NotLike "##*"
                }
                if (!$InputObject) {
                    Write-Message -Level Output -Message "No certificates found to export in $db."
                    continue
                }
            }

        }

        foreach ($cert in $InputObject) {
            if ($cert.Name.StartsWith("##")) {
                Write-Message -Level Output -Message "Skipping system cert $cert"
            }
            else {
                export-cert $cert
            }
        }
    }
}
function Clear-DbaPlanCache {
    <#
        .SYNOPSIS
            Removes adhoc and prepared plan caches is single use plans are over defined threshold.

        .DESCRIPTION
            Checks ahoc and prepared plan cache for each database, if over 100 MBs removes from the cache.

            This command automates that process.

            References: https://www.sqlskills.com/blogs/kimberly/plan-cache-adhoc-workloads-and-clearing-the-single-use-plan-cache-bloat/

        .PARAMETER SqlInstance
            The target SQL Server instance.

        .PARAMETER SqlCredential
           Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Threshold
            Memory used threshold.

        .PARAMETER InputObject
            Enables results to be piped in from Get-DbaPlanCache.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Memory
            Author: Tracy Boggiano, databasesuperhero.com

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: GNU GPL v3 https://opensource.org/licenses/GPL-3.0

        .LINK
            https://dbatools.io/Clear-DbaPlanCache

        .EXAMPLE
            Clear-DbaPlanCache -SqlInstance sql2017 -Threshold 200

            Logs into the SQL Server instance "sql2017" and removes plan caches if over 200 MB.

        .EXAMPLE
            Clear-DbaPlanCache -SqlInstance sql2017 -SqlCredential (Get-Credential sqladmin)

            Logs into the SQL instance using the SQL Login 'sqladmin' and then Windows instance as 'ad\sqldba'
            and removes if Threshold over 100 MB.
    #>
    [CmdletBinding(SupportsShouldProcess)]
    Param (
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [int]$Threshold = 100,
        [parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            $InputObject += Get-DbaPlanCache -SqlInstance $instance -SqlCredential $SqlCredential
        }

        foreach ($result in $InputObject) {
            if ($result.MB -ge $Threshold) {
                if ($Pscmdlet.ShouldProcess($($result.SqlInstance), "Cleared SQL Plans plan cache")) {
                    $server.Query("DBCC FREESYSTEMCACHE('SQL Plans')")
                    [pscustomobject]@{
                        ComputerName = $result.ComputerName
                        InstanceName = $result.InstanceName
                        SqlInstance  = $result.SqlInstance
                        Size         = $result.Size
                        Status       = "Plan cache cleared"
                    }
                }
            }
            else {
                if ($Pscmdlet.ShouldProcess($($result.SqlInstance), "Results $($result.Size) below threshold")) {
                    [pscustomobject]@{
                        ComputerName = $result.ComputerName
                        InstanceName = $result.InstanceName
                        SqlInstance  = $result.SqlInstance
                        Size         = $result.Size
                        Status       = "Plan cache size below threshold ($Threshold) "
                    }
                    Write-Message -Level Verbose -Message "Plan cache size below threshold ($Threshold) "
                }
            }
        }
    }
}
function Clear-DbaSqlConnectionPool {
    <#
    .SYNOPSIS
        Resets (or empties) the connection pool.

    .DESCRIPTION
        This command resets (or empties) the connection pool.

        If there are connections in use at the time of the call, they are marked appropriately and will be discarded (instead of being returned to the pool) when Close() is called on them.

        Ref: https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.clearallpools(v=vs.110).aspx

    .PARAMETER ComputerName
        Target computer(s). If no computer name is specified, the local computer is targeted.

    .PARAMETER Credential
        Alternate credential object to use for accessing the target computer(s).

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Tags: Connection

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Clear-DbaSqlConnectionPool

    .EXAMPLE
        Clear-DbaSqlConnectionPool

        Clears all local connection pools.

    .EXAMPLE
        Clear-DbaSqlConnectionPool -ComputerName workstation27

        Clears all connection pools on workstation27.
#>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Alias("cn", "host", "Server")]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [switch][Alias('Silent')]
        $EnableException
    )
    
    process {
        # TODO: https://jamessdixon.wordpress.com/2013/01/22/ado-net-and-connection-pooling
        
        foreach ($computer in $ComputerName) {
            try {
                if (-not $computer.IsLocalhost) {
                    Write-Message -Level Verbose -Message "Clearing all pools on remote computer $computer"
                    if (Test-Bound 'Credential') {
                        Invoke-Command2 -ComputerName $computer -Credential $Credential -ScriptBlock { [System.Data.SqlClient.SqlConnection]::ClearAllPools() }
                    }
                    else {
                        Invoke-Command2 -ComputerName $computer -ScriptBlock { [System.Data.SqlClient.SqlConnection]::ClearAllPools() }
                    }
                }
                else {
                    Write-Message -Level Verbose -Message "Clearing all local pools"
                    if (Test-Bound 'Credential') {
                        Invoke-Command2 -Credential $Credential -ScriptBlock { [System.Data.SqlClient.SqlConnection]::ClearAllPools() }
                    }
                    else {
                        Invoke-Command2 -ScriptBlock { [System.Data.SqlClient.SqlConnection]::ClearAllPools() }
                    }
                }
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_ -Target $computer -Continue
            }
        }
    }
}
function Clear-DbaWaitStatistics {
    <#
    .SYNOPSIS
        Clears wait statistics

    .DESCRIPTION
        Reset the aggregated statistics - basically just executes DBCC SQLPERF (N'sys.dm_os_wait_stats', CLEAR)

    .PARAMETER SqlInstance
        Allows you to specify a comma separated list of servers to query.

    .PARAMETER SqlCredential
        Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

    .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Tags: WaitStatistic
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Clear-DbaWaitStatistics

    .EXAMPLE
        Clear-DbaWaitStatistics -SqlInstance sql2008, sqlserver2012
        After confirmation, clears wait stats on servers sql2008 and sqlserver2012

    .EXAMPLE
        Clear-DbaWaitStatistics -SqlInstance sql2008, sqlserver2012 -Confirm:$false
        Clears wait stats on servers sql2008 and sqlserver2012, without prompting
    #>
    [CmdletBinding(ConfirmImpact = 'High', SupportsShouldProcess)]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($Pscmdlet.ShouldProcess($instance, "Performing CLEAR of sys.dm_os_wait_stats")) {
                try {
                    $server.Query("DBCC SQLPERF (N'sys.dm_os_wait_stats', CLEAR);")
                    $status = "Success"
                }
                catch {
                    $status = $_.Exception
                }

                [PSCustomObject]@{
                    ComputerName = $server.ComputerName
                    InstanceName = $server.ServiceName
                    SqlInstance  = $server.DomainInstanceName
                    Status       = $status
                }
            }
        }
    }
}
function Connect-DbaInstance {
    <#
    .SYNOPSIS
        Creates a robust SMO SQL Server object.

    .DESCRIPTION
        This command is robust because it initializes properties that do not cause enumeration by default. It also supports both Windows and SQL Server authentication methods, and detects which to use based upon the provided credentials.

        By default, this command also sets the connection's ApplicationName property  to "dbatools PowerShell module - dbatools.io - custom connection". If you're doing anything that requires profiling, you can look for this client name.

        Alternatively, you can pass in whichever client name you'd like using the -ClientName parameter. There are a ton of other parameters for you to explore as well.

        See https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.connectionstring.aspx
        and https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnectionstringbuilder.aspx,
        and https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.aspx

        To execute SQL commands, you can use $server.ConnectionContext.ExecuteReader($sql) or $server.Databases['master'].ExecuteNonQuery($sql)

    .PARAMETER SqlInstance
        SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

    .PARAMETER Credential
        Credential object used to connect to the SQL Server Instance as a different user. This can be a Windows or SQL Server account. Windows users are determined by the existence of a backslash, so if you are intending to use an alternative Windows connection instead of a SQL login, ensure it contains a backslash.

    .PARAMETER Database
        The database(s) to process. This list is auto-populated from the server.

    .PARAMETER AccessToken
        Gets or sets the access token for the connection.

    .PARAMETER AppendConnectionString
        Appends to the current connection string. Note that you cannot pass authentication information using this method. Use -SqlInstance and optionally -SqlCredential to set authentication information.

    .PARAMETER ApplicationIntent
        Declares the application workload type when connecting to a server.

        Valid values are "ReadOnly" and "ReadWrite".

    .PARAMETER BatchSeparator
        A string to separate groups of SQL statements being executed. By default, this is "GO".

    .PARAMETER ClientName
        By default, this command sets the client's ApplicationName property to "dbatools PowerShell module - dbatools.io - custom connection" if you're doing anything that requires profiling, you can look for this client name. Using -ClientName allows you to set your own custom client application name.

    .PARAMETER ConnectTimeout
        The length of time (in seconds) to wait for a connection to the server before terminating the attempt and generating an error.

        Valid values are integers between 0 and 2147483647.

        When opening a connection to a Azure SQL Database, set the connection timeout to 30 seconds.

    .PARAMETER EncryptConnection
        If this switch is enabled, SQL Server uses SSL encryption for all data sent between the client and server if the server has a certificate installed.

        For more information, see Connection String Syntax. https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/connection-string-syntax

        Beginning in .NET Framework 4.5, when TrustServerCertificate is false and Encrypt is true, the server name (or IP address) in a SQL Server SSL certificate must exactly match the server name (or IP address) specified in the connection string. Otherwise, the connection attempt will fail. For information about support for certificates whose subject starts with a wildcard character (*), see Accepted wildcards used by server certificates for server authentication. https://support.microsoft.com/en-us/help/258858/accepted-wildcards-used-by-server-certificates-for-server-authenticati

    .PARAMETER FailoverPartner
        The name of the failover partner server where database mirroring is configured.

        If the value of this key is "" (an empty string), then Initial Catalog must be present in the connection string, and its value must not be "".

        The server name can be 128 characters or less.

        If you specify a failover partner but the failover partner server is not configured for database mirroring and the primary server (specified with the Server keyword) is not available, then the connection will fail.

        If you specify a failover partner and the primary server is not configured for database mirroring, the connection to the primary server (specified with the Server keyword) will succeed if the primary server is available.


    .PARAMETER IsActiveDirectoryUniversalAuth
        If this switch is enabled, the connection will be configured to use Azure Active Directory authentication.

    .PARAMETER LockTimeout
        Sets the time in seconds required for the connection to time out when the current transaction is locked.

    .PARAMETER MaxPoolSize
        Sets the maximum number of connections allowed in the connection pool for this specific connection string.

    .PARAMETER MinPoolSize
        Sets the minimum number of connections allowed in the connection pool for this specific connection string.

    .PARAMETER MultipleActiveResultSets
        If this switch is enabled, an application can maintain multiple active result sets (MARS).

        If this switch is not enabled, an application must process or cancel all result sets from one batch before it can execute any other batch on that connection.

    .PARAMETER MultiSubnetFailover
        If this switch is enabled, and your application is connecting to an AlwaysOn availability group (AG) on different subnets, detection of and connection to the currently active server will be faster. For more information about SqlClient support for Always On Availability Groups, see https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql/sqlclient-support-for-high-availability-disaster-recovery

    .PARAMETER NetworkProtocol
        Explicitly sets the network protocol used to connect to the server.

        Valid values are "TcpIp","NamedPipes","Multiprotocol","AppleTalk","BanyanVines","Via","SharedMemory" and "NWLinkIpxSpx"

    .PARAMETER NonPooledConnection
        If this switch is enabled, a non-pooled connection will be requested.

    .PARAMETER PacketSize
        Sets the size in bytes of the network packets used to communicate with an instance of SQL Server. Must match at server.

    .PARAMETER PooledConnectionLifetime
        When a connection is returned to the pool, its creation time is compared with the current time and the connection is destroyed if that time span (in seconds) exceeds the value specified by Connection Lifetime. This is useful in clustered configurations to force load balancing between a running server and a server just brought online.

        A value of zero (0) causes pooled connections to have the maximum connection timeout.

    .PARAMETER SqlExecutionModes
        The SqlExecutionModes enumeration contains values that are used to specify whether the commands sent to the referenced connection to the server are executed immediately or saved in a buffer.

        Valid values include "CaptureSql", "ExecuteAndCaptureSql" and "ExecuteSql".

    .PARAMETER StatementTimeout
        Sets the number of seconds a statement is given to run before failing with a timeout error.

    .PARAMETER TrustServerCertificate
        When this switch is enabled, the channel will be encrypted while bypassing walking the certificate chain to validate trust.

    .PARAMETER WorkstationId
        Sets the name of the workstation connecting to SQL Server.

    .PARAMETER SqlConnectionOnly
        Instead of returning a rich SMO server object, this command will only return a SqlConnection object when setting this switch.

    .NOTES
        Tags: Connect, Connection
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Connect-DbaInstance

    .EXAMPLE
        Connect-DbaInstance -SqlInstance sql2014

        Creates an SMO Server object that connects using Windows Authentication

    .EXAMPLE
        $wincred = Get-Credential ad\sqladmin
        Connect-DbaInstance -SqlInstance sql2014 -Credential $wincred

        Creates an SMO Server object that connects using alternative Windows credentials

    .EXAMPLE
        $sqlcred = Get-Credential sqladmin
        $server = Connect-DbaInstance -SqlInstance sql2014 -Credential $sqlcred

        Login to sql2014 as SQL login sqladmin.

    .EXAMPLE
        $server = Connect-DbaInstance -SqlInstance sql2014 -ClientName "my connection"

        Creates an SMO Server object that connects using Windows Authentication and uses the client name "my connection". So when you open up profiler or use extended events, you can search for "my connection".

    .EXAMPLE
        $server = Connect-DbaInstance -SqlInstance sql2014 -AppendConnectionString "Packet Size=4096;AttachDbFilename=C:\MyFolder\MyDataFile.mdf;User Instance=true;"

        Creates an SMO Server object that connects to sql2014 using Windows Authentication, then it sets the packet size (this can also be done via -PacketSize) and other connection attributes.

    .EXAMPLE
        $server = Connect-DbaInstance -SqlInstance sql2014 -NetworkProtocol TcpIp -MultiSubnetFailover

        Creates an SMO Server object that connects using Windows Authentication that uses TCP/IP and has MultiSubnetFailover enabled.

    .EXAMPLE
        $server = Connect-DbaInstance sql2016 -ApplicationIntent ReadOnly

        Connects with ReadOnly ApplicationIntent.
#>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("SqlCredential")]
        [PSCredential]$Credential,
        [object[]]$Database,
        [string]$AccessToken,
        [ValidateSet('ReadOnly', 'ReadWrite')]
        [string]$ApplicationIntent,
        [string]$BatchSeparator,
        [string]$ClientName = "dbatools PowerShell module - dbatools.io - custom connection",
        [int]$ConnectTimeout = ([Sqlcollaborative.Dbatools.Connection.ConnectionHost]::SqlConnectionTimeout),
        [switch]$EncryptConnection,
        [string]$FailoverPartner,
        [switch]$IsActiveDirectoryUniversalAuth,
        [int]$LockTimeout,
        [int]$MaxPoolSize,
        [int]$MinPoolSize,
        [switch]$MultipleActiveResultSets,
        [switch]$MultiSubnetFailover,
        [ValidateSet('TcpIp', 'NamedPipes', 'Multiprotocol', 'AppleTalk', 'BanyanVines', 'Via', 'SharedMemory', 'NWLinkIpxSpx')]
        [string]$NetworkProtocol,
        [switch]$NonPooledConnection,
        [int]$PacketSize,
        [int]$PooledConnectionLifetime,
        [ValidateSet('CaptureSql', 'ExecuteAndCaptureSql', 'ExecuteSql')]
        [string]$SqlExecutionModes,
        [int]$StatementTimeout,
        [switch]$TrustServerCertificate,
        [string]$WorkstationId,
        [string]$AppendConnectionString,
        [switch]$SqlConnectionOnly
    )
    begin {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Connect-DbaSqlServer
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-DbaInstance

        $loadedSmoVersion = [AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.Fullname -like "Microsoft.SqlServer.SMO,*" }

        if ($loadedSmoVersion) {
            $loadedSmoVersion = $loadedSmoVersion | ForEach-Object {
                if ($_.Location -match "__") {
                    ((Split-Path (Split-Path $_.Location) -Leaf) -split "__")[0]
                }
                else {
                    ((Get-ChildItem -Path $_.Location).VersionInfo.ProductVersion)
                }
            }
        }
        #'PrimaryFilePath' seems the culprit for slow SMO on databases
        $Fields2000_Db = 'Collation', 'CompatibilityLevel', 'CreateDate', 'ID', 'IsAccessible', 'IsFullTextEnabled', 'IsSystemObject', 'IsUpdateable', 'LastBackupDate', 'LastDifferentialBackupDate', 'LastLogBackupDate', 'Name', 'Owner', 'ReadOnly', 'RecoveryModel', 'ReplicationOptions', 'Status', 'Version'
        $Fields200x_Db = $Fields2000_Db + @('BrokerEnabled', 'DatabaseSnapshotBaseName', 'IsMirroringEnabled', 'Trustworthy')
        $Fields201x_Db = $Fields200x_Db + @('ActiveConnections', 'AvailabilityDatabaseSynchronizationState', 'AvailabilityGroupName', 'ContainmentType', 'EncryptionEnabled')

        $Fields2000_Login = 'CreateDate' , 'DateLastModified' , 'DefaultDatabase' , 'DenyWindowsLogin' , 'IsSystemObject' , 'Language' , 'LanguageAlias' , 'LoginType' , 'Name' , 'Sid' , 'WindowsLoginAccessType'
        $Fields200x_Login = $Fields2000_Login + @('AsymmetricKey', 'Certificate', 'Credential', 'ID', 'IsDisabled', 'IsLocked', 'IsPasswordExpired', 'MustChangePassword', 'PasswordExpirationEnabled', 'PasswordPolicyEnforced')
        $Fields201x_Login = $Fields200x_Login + @('PasswordHashAlgorithm')


    }
    process {
        foreach ($instance in $SqlInstance) {
            if ($instance.Type -like "Server") {
                if ($instance.InputObject.ConnectionContext.IsOpen -eq $false) {
                    $instance.InputObject.ConnectionContext.Connect()
                }
                if ($SqlConnectionOnly) { return $instance.InputObject.ConnectionContext.SqlConnectionObject }
                else { return $instance.InputObject }
            }
            if ($instance.Type -like "SqlConnection") {
                $server = New-Object Microsoft.SqlServer.Management.Smo.Server($instance.InputObject)

                if ($server.ConnectionContext.IsOpen -eq $false) {
                    $server.ConnectionContext.Connect()
                }
                if ($SqlConnectionOnly) { return $server.ConnectionContext.SqlConnectionObject }
                else {
                    if (-not $server.ComputerName) {
                        $parsedcomputername = $server.NetName
                        if (-not $parsedcomputername) {
                            $parsedcomputername = ([dbainstance]$instance).ComputerName
                        }
                        Add-Member -InputObject $server -NotePropertyName ComputerName -NotePropertyValue $parsedcomputername -Force
                    }
                    return $server
                }
            }

            if ($instance.IsConnectionString) { $server = New-Object Microsoft.SqlServer.Management.Smo.Server($instance.InputObject) }
            else { $server = New-Object Microsoft.SqlServer.Management.Smo.Server $instance.FullSmoName }

            if ($AppendConnectionString) {
                $connstring = $server.ConnectionContext.ConnectionString
                $server.ConnectionContext.ConnectionString = "$connstring;$appendconnectionstring"
                $server.ConnectionContext.Connect()
            }
            else {

                $server.ConnectionContext.ApplicationName = $ClientName

                if (Test-Bound -ParameterName 'AccessToken') { $server.ConnectionContext.AccessToken = $AccessToken }
                if (Test-Bound -ParameterName 'BatchSeparator') { $server.ConnectionContext.BatchSeparator = $BatchSeparator }
                if (Test-Bound -ParameterName 'ConnectTimeout') { $server.ConnectionContext.ConnectTimeout = $ConnectTimeout }
                if (Test-Bound -ParameterName 'Database') { $server.ConnectionContext.DatabaseName = $Database }
                if (Test-Bound -ParameterName 'EncryptConnection') { $server.ConnectionContext.EncryptConnection = $true }
                if (Test-Bound -ParameterName 'IsActiveDirectoryUniversalAuth') { $server.ConnectionContext.IsActiveDirectoryUniversalAuth = $true }
                if (Test-Bound -ParameterName 'LockTimeout') { $server.ConnectionContext.LockTimeout = $LockTimeout }
                if (Test-Bound -ParameterName 'MaxPoolSize') { $server.ConnectionContext.MaxPoolSize = $MaxPoolSize }
                if (Test-Bound -ParameterName 'MinPoolSize') { $server.ConnectionContext.MinPoolSize = $MinPoolSize }
                if (Test-Bound -ParameterName 'MultipleActiveResultSets') { $server.ConnectionContext.MultipleActiveResultSets = $true }
                if (Test-Bound -ParameterName 'NetworkProtocol') { $server.ConnectionContext.NetworkProtocol = $NetworkProtocol }
                if (Test-Bound -ParameterName 'NonPooledConnection') { $server.ConnectionContext.NonPooledConnection = $true }
                if (Test-Bound -ParameterName 'PacketSize') { $server.ConnectionContext.PacketSize = $PacketSize }
                if (Test-Bound -ParameterName 'PooledConnectionLifetime') { $server.ConnectionContext.PooledConnectionLifetime = $PooledConnectionLifetime }
                if (Test-Bound -ParameterName 'StatementTimeout') { $server.ConnectionContext.StatementTimeout = $StatementTimeout }
                if (Test-Bound -ParameterName 'SqlExecutionModes') { $server.ConnectionContext.SqlExecutionModes = $SqlExecutionModes }
                if (Test-Bound -ParameterName 'TrustServerCertificate') { $server.ConnectionContext.TrustServerCertificate = $true }
                if (Test-Bound -ParameterName 'WorkstationId') { $server.ConnectionContext.WorkstationId = $WorkstationId }

                $connstring = $server.ConnectionContext.ConnectionString
                if (Test-Bound -ParameterName 'MultiSubnetFailover') { $connstring = "$connstring;MultiSubnetFailover=True" }
                if (Test-Bound -ParameterName 'FailoverPartner') { $connstring = "$connstring;Failover Partner=$FailoverPartner" }
                if (Test-Bound -ParameterName 'ApplicationIntent') { $connstring = "$connstring;ApplicationIntent=$ApplicationIntent" }

                if ($connstring -ne $server.ConnectionContext.ConnectionString) {
                    $server.ConnectionContext.ConnectionString = $connstring
                }

                try {
                    if ($null -ne $Credential.username) {
                        $username = ($Credential.username).TrimStart("\")

                        if ($username -like "*\*") {
                            $username = $username.Split("\")[1]
                            $authtype = "Windows Authentication with Credential"
                            $server.ConnectionContext.LoginSecure = $true
                            $server.ConnectionContext.ConnectAsUser = $true
                            $server.ConnectionContext.ConnectAsUserName = $username
                            $server.ConnectionContext.ConnectAsUserPassword = ($Credential).GetNetworkCredential().Password
                        }
                        else {
                            $authtype = "SQL Authentication"
                            $server.ConnectionContext.LoginSecure = $false
                            $server.ConnectionContext.set_Login($username)
                            $server.ConnectionContext.set_SecurePassword($Credential.Password)
                        }
                    }

                    if ($NonPooled) {
                        $server.ConnectionContext.Connect()
                    }
                    elseif ($authtype -eq "Windows Authentication with Credential") {
                        # Make it connect in a natural way, hard to explain.
                        $null = $server.IsMemberOfWsfcCluster
                    }
                    else {
                        $server.ConnectionContext.SqlConnectionObject.Open()
                    }
                }
                catch {
                    $message = $_.Exception.InnerException.InnerException
                    $message = $message.ToString()
                    $message = ($message -Split '-->')[0]
                    $message = ($message -Split 'at System.Data.SqlClient')[0]
                    $message = ($message -Split 'at System.Data.ProviderBase')[0]
                    throw "Can't connect to $instance`: $message "
                }

            }

            if ($loadedSmoVersion -ge 11) {
                if ($server.VersionMajor -eq 8) {
                    # 2000
                    $initFieldsDb = New-Object System.Collections.Specialized.StringCollection
                    [void]$initFieldsDb.AddRange($Fields2000_Db)
                    $initFieldsLogin = New-Object System.Collections.Specialized.StringCollection
                    [void]$initFieldsLogin.AddRange($Fields2000_Login)
                    $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Database], $initFieldsDb)
                    $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Login], $initFieldsLogin)
                }

                elseif ($server.VersionMajor -eq 9 -or $server.VersionMajor -eq 10) {
                    # 2005 and 2008
                    $initFieldsDb = New-Object System.Collections.Specialized.StringCollection
                    [void]$initFieldsDb.AddRange($Fields200x_Db)
                    $initFieldsLogin = New-Object System.Collections.Specialized.StringCollection
                    [void]$initFieldsLogin.AddRange($Fields200x_Login)
                    $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Database], $initFieldsDb)
                    $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Login], $initFieldsLogin)
                }

                else {
                    # 2012 and above
                    $initFieldsDb = New-Object System.Collections.Specialized.StringCollection
                    [void]$initFieldsDb.AddRange($Fields201x_Db)
                    $initFieldsLogin = New-Object System.Collections.Specialized.StringCollection
                    [void]$initFieldsLogin.AddRange($Fields201x_Login)
                    $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Database], $initFieldsDb)
                    $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Login], $initFieldsLogin)
                }
            }

            if ($SqlConnectionOnly) {
                return $server.ConnectionContext.SqlConnectionObject
            }
            else {
                if (-not $server.ComputerName) {
                    $parsedcomputername = $server.NetName
                    if (-not $parsedcomputername) {
                        $parsedcomputername = ([dbainstance]$instance).ComputerName
                    }
                    Add-Member -InputObject $server -NotePropertyName ComputerName -NotePropertyValue $parsedcomputername -Force
                }
                return $server
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function ConvertTo-DbaDataTable {
    <#
        .SYNOPSIS
            Creates a DataTable for an object.

        .DESCRIPTION
            Creates a DataTable based on an object's properties. This allows you to easily write to SQL Server tables.

            Thanks to Chad Miller, this is based on his script. https://gallery.technet.microsoft.com/scriptcenter/4208a159-a52e-4b99-83d4-8048468d29dd

            If the attempt to convert to datatable fails, try the -Raw parameter for less accurate datatype detection.

        .PARAMETER InputObject
            The object to transform into a DataTable.

        .PARAMETER TimeSpanType
            Specifies the type to convert TimeSpan objects into. Default is 'TotalMilliseconds'. Valid options are: 'Ticks', 'TotalDays', 'TotalHours', 'TotalMinutes', 'TotalSeconds', 'TotalMilliseconds', and 'String'.

        .PARAMETER SizeType
            Specifies the type to convert DbaSize objects to. Default is 'Int64'. Valid options are 'Int32', 'Int64', and 'String'.

        .PARAMETER IgnoreNull
            If this switch is enabled, objects with null values will be ignored (empty rows will be added by default).

        .PARAMETER Raw
            If this switch is enabled, the DataTable will be created with strings. No attempt will be made to parse/determine data types.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: DataTable, Table, Data
            Website: https://dbatools.io/
            Copyright: (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/ConvertTo-DbaDataTable

        .OUTPUTS
            System.Object[]

        .EXAMPLE
            Get-Service | ConvertTo-DbaDataTable

            Creates a DataTable from the output of Get-Service.

        .EXAMPLE
            ConvertTo-DbaDataTable -InputObject $csv.cheesetypes

            Creates a DataTable from the CSV object $csv.cheesetypes.

        .EXAMPLE
            $dblist | ConvertTo-DbaDataTable

            Creates a DataTable from the $dblist object passed in via pipeline.

        .EXAMPLE
            Get-Process | ConvertTo-DbaDataTable -TimeSpanType TotalSeconds

            Creates a DataTable with the running processes and converts any TimeSpan property to TotalSeconds.
    #>
    [CmdletBinding()]
    [OutputType([System.Object[]])]
    param (
        [Parameter(Position = 0,
            Mandatory = $true,
            ValueFromPipeline = $true)]
        [AllowNull()]
        [PSObject[]]$InputObject,
        [Parameter(Position = 1)]
        [ValidateSet("Ticks",
            "TotalDays",
            "TotalHours",
            "TotalMinutes",
            "TotalSeconds",
            "TotalMilliseconds",
            "String")]
        [ValidateNotNullOrEmpty()]
        [string]$TimeSpanType = "TotalMilliseconds",
        [ValidateSet("Int64", "Int32", "String")]
        [string]$SizeType = "Int64",
        [switch]$IgnoreNull,
        [switch]$Raw,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Write-Message -Level Debug -Message "Bound parameters: $($PSBoundParameters.Keys -join ", ")"
        Write-Message -Level Debug -Message "TimeSpanType = $TimeSpanType | SizeType = $SizeType"
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Alias Out-DbaDataTable

        function Convert-Type {
            # This function will check so that the type is an accepted type which could be used when inserting into a table.
            # If a type is accepted (included in the $type array) then it will be passed on, otherwise it will first change type before passing it on.
            # Special types will have both their types converted as well as the value.
            # TimeSpan is a special type and will be converted into the $timespantype. (default: TotalMilliseconds) so that the timespan can be stored in a database further down the line.
            [CmdletBinding()]
            param (
                $type,

                $value,

                $timespantype = 'TotalMilliseconds',

                $sizetype = 'Int64'
            )

            $types = [System.Collections.ArrayList]@(
                'System.Int32',
                'System.UInt32',
                'System.Int16',
                'System.UInt16',
                'System.Int64',
                'System.UInt64',
                'System.Decimal',
                'System.Single',
                'System.Double',
                'System.Byte',
                'System.SByte',
                'System.Boolean',
                'System.DateTime',
                'System.Guid',
                'System.Char'
            )

            # The $special variable is used to mark the return value if a conversion was made on the value itself.
            # If this is set to true the original value will later be ignored when updating the DataTable.
            # And the value returned from this function will be used instead. (cannot modify existing properties)
            $special = $false
            $specialType = ""

            # Special types need to be converted in some way.
            # This attempt is to convert timespan into something that works in a table.
            # I couldn't decide on what to convert it to so the user can decide.
            # If the parameter is not used, TotalMilliseconds will be used as default.
            # Ticks are more accurate but I think milliseconds are more useful most of the time.
            if (($type -eq 'System.TimeSpan') -or ($type -eq 'Sqlcollaborative.Dbatools.Utility.DbaTimeSpan') -or ($type -eq 'Sqlcollaborative.Dbatools.Utility.DbaTimeSpanPretty')) {
                $special = $true
                if ($timespantype -eq 'String') {
                    $value = $value.ToString()
                    $type = 'System.String'
                }
                else {
                    # Let's use Int64 for all other types than string.
                    # We could match the type more closely with the timespantype but that can be added in the future if needed.
                    $value = $value.$timespantype
                    $type = 'System.Int64'
                }
                $specialType = 'Timespan'
            }
            elseif ($type -eq 'Sqlcollaborative.Dbatools.Utility.Size') {
                $special = $true
                switch ($sizetype) {
                    'Int64' {
                        $value = $value.Byte
                        $type = 'System.Int64'
                    }
                    'Int32' {
                        $value = $value.Byte
                        $type = 'System.Int32'
                    }
                    'String' {
                        $value = $value.ToString()
                        $type = 'System.String'
                    }
                }
                $specialType = 'Size'
            }
            elseif (-not ($type -in $types)) {
                # All types which are not found in the array will be converted into strings.
                # In this way we dont ignore it completely and it will be clear in the end why it looks as it does.
                $type = 'System.String'
            }

            # return a hashtable instead of an object. I like hashtables :)
            return @{ type = $type; Value = $value; Special = $special; SpecialType = $specialType }
        }

        function Convert-SpecialType {
            <#
            .SYNOPSIS
                Converts a value for a known column.

            .DESCRIPTION
                Converts a value for a known column.

            .PARAMETER Value
                The value to convert

            .PARAMETER Type
                The special type for which to convert

            .PARAMETER SizeType
                The size type defined by the user

            .PARAMETER TimeSpanType
                The timespan type defined by the user
        #>
            [CmdletBinding()]
            Param (
                $Value,
                [ValidateSet('Timespan', 'Size')] [string]$Type,
                [string]$SizeType,
                [string]$TimeSpanType
            )

            switch ($Type) {
                'Size' {
                    if ($SizeType -eq 'String') { return $Value.ToString() }
                    else { return $Value.Byte }
                }
                'Timespan' {
                    if ($TimeSpanType -eq 'String') {
                        $Value.ToString()
                    }
                    else {
                        $Value.$TimeSpanType
                    }
                }
            }
        }

        function Add-Column {
            <#
            .SYNOPSIS
                Adds a column to the datatable in progress.

            .DESCRIPTION
                Adds a column to the datatable in progress.

            .PARAMETER Property
                The property for which to add a column.

            .PARAMETER DataTable
                Autofilled. The table for which to add a column.

            .PARAMETER TimeSpanType
                Autofilled. How should timespans be handled?

            .PARAMETER SizeType
                Autofilled. How should sizes be handled?

            .PARAMETER Raw
                Autofilled. Whether the column should be string, no matter the input.
        #>
            [CmdletBinding()]
            Param (
                [System.Management.Automation.PSPropertyInfo]$Property,
                [System.Data.DataTable]$DataTable = $datatable,
                [string]$TimeSpanType = $TimeSpanType,
                [string]$SizeType = $SizeType,
                [bool]$Raw = $Raw
            )

            $type = $property.TypeNameOfValue
            try {
                if ($Property.MemberType -like 'ScriptProperty') {
                    $type = $Property.GetType().FullName
                }
            }
            catch { $type = 'System.String' }

            $converted = Convert-Type -type $type -value $property.Value -timespantype $TimeSpanType -sizetype $SizeType

            $column = New-Object System.Data.DataColumn
            $column.ColumnName = $property.Name.ToString()
            if (-not $Raw) {
                $column.DataType = [System.Type]::GetType($converted.type)
            }
            $null = $DataTable.Columns.Add($column)
            $converted
        }

        $datatable = New-Object System.Data.DataTable

        # Accelerate subsequent lookups of columns and special type columns
        $columns = @()
        $specialColumns = @()
        $specialColumnsType = @{ }

        $ShouldCreateColumns = $true
    }

    process {
        #region Handle null objects
        if ($null -eq $InputObject) {
            if (-not $IgnoreNull) {
                $datarow = $datatable.NewRow()
                $datatable.Rows.Add($datarow)
            }

            # Only ends the current process block
            return
        }
        #endregion Handle null objects


        foreach ($object in $InputObject) {
            #region Handle null objects
            if ($null -eq $object) {
                if (-not $IgnoreNull) {
                    $datarow = $datatable.NewRow()
                    $datatable.Rows.Add($datarow)
                }
                continue
            }
            #endregion Handle null objects

            #Handle rows already being System.Data.DataRow
            if ($object.GetType().FullName -eq 'System.Data.DataRow') {
                if ($ShouldCreateColumns) {
                    $datatable = $object.Table.Copy()
                    $ShouldCreateColumns = $false
                }
                continue
            }

            # The new row to insert
            $datarow = $datatable.NewRow()

            #region Process Properties
            $objectProperties = $object.PSObject.Properties
            foreach ($property in $objectProperties) {
                #region Create Columns as needed
                if ($ShouldCreateColumns) {
                    $newColumn = Add-Column -Property $property
                    $columns += $property.Name
                    if ($newColumn.Special) {
                        $specialColumns += $property.Name
                        $specialColumnsType[$property.Name] = $newColumn.SpecialType
                    }
                }
                #endregion Create Columns as needed

                # Handle null properties, as well as properties with access errors
                try {
                    $propValueLength = $property.value.length
                }
                catch {
                    $propValueLength = 0
                }

                #region Insert value into column of row
                if ($propValueLength -gt 0) {
                    # If the typename was a special typename we want to use the value returned from Convert-Type instead.
                    # We might get error if we try to change the value for $property.value if it is read-only. That's why we use $converted.value instead.
                    if ($property.Name -in $specialColumns) {
                        $datarow.Item($property.Name) = Convert-SpecialType -Value $property.value -Type $specialColumnsType[$property.Name] -SizeType $SizeType -TimeSpanType $TimeSpanType
                    }
                    else {
                        if ($property.value.ToString().length -eq 15) {
                            if ($property.value.ToString() -eq 'System.Object[]') {
                                $value = $property.value -join ", "
                            }
                            elseif ($property.value.ToString() -eq 'System.String[]') {
                                $value = $property.value -join ", "
                            }
                            else {
                                $value = $property.value
                            }
                        }
                        else {
                            $value = $property.value
                        }

                        try {
                            $datarow.Item($property.Name) = $value
                        }
                        catch {
                            if ($property.Name -notin $columns) {
                                try {
                                    $newColumn = Add-Column -Property $property
                                    $columns += $property.Name
                                    if ($newColumn.Special) {
                                        $specialColumns += $property.Name
                                        $specialColumnsType[$property.Name] = $newColumn.SpecialType
                                    }

                                    $datarow.Item($property.Name) = $newColumn.Value
                                }
                                catch {
                                    Write-Message -Level Warning -Message "Failed to add property $($property.Name) from $object" -ErrorRecord $_ -Target $object
                                }
                            }
                            else {
                                Write-Message -Level Warning -Message "Failed to add property $($property.Name) from $object" -ErrorRecord $_ -Target $object
                            }
                        }
                    }
                }
                #endregion Insert value into column of row
            }

            $datatable.Rows.Add($datarow)
            # If this is the first non-null object then the columns has just been created.
            # Set variable to false to skip creating columns from now on.
            if ($ShouldCreateColumns) {
                $ShouldCreateColumns = $false
            }
            #endregion Process Properties
        }
    }

    end {
        Write-Message -Level InternalComment -Message "Finished."
        , $datatable
    }
}
function ConvertTo-DbaXESession {
    <#
        .SYNOPSIS
            Uses a slightly modified version of sp_SQLskills_ConvertTraceToExtendedEvents.sql to convert Traces to Extended Events.

        .DESCRIPTION
            Uses a slightly modified version of sp_SQLskills_ConvertTraceToExtendedEvents.sql to convert Traces to Extended Events.

            T-SQL code by: Jonathan M. Kehayias, SQLskills.com. T-SQL can be found in this module directory and at
            https://www.sqlskills.com/blogs/jonathan/converting-sql-trace-to-extended-events-in-sql-server-2012/

        .PARAMETER InputObject
            Specifies a Trace object output by Get-DbaTrace.

        .PARAMETER Name
            The name of the Trace to convert. If the name exists, characters will be appended to it.

        .PARAMETER OutputScriptOnly
            Outputs the T-SQL script to create the XE session and does not execute it.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Trace, ExtendedEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            Get-DbaTrace -SqlInstance sql2017, sql2012 | Where Id -eq 2 | ConvertTo-DbaXESession -Name 'Test'

            Converts Trace with ID 2 to a Session named Test on SQL Server instances named sql2017 and sql2012
            and creates the Session on each respective server.

       .EXAMPLE
            Get-DbaTrace -SqlInstance sql2014 | Out-GridView -PassThru | ConvertTo-DbaXESession -Name 'Test' | Start-DbaXESession

            Converts selected traces on sql2014 to sessions, creates the session, and starts it.

        .EXAMPLE
            Get-DbaTrace -SqlInstance sql2014 | Where Id -eq 1 | ConvertTo-DbaXESession -Name 'Test' -OutputScriptOnly

            Converts trace ID 1 on sql2014 to an Extended Event and outputs the resulting T-SQL.
    #>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory, ValueFromPipeline)]
        [object[]]$InputObject,
        [parameter(Mandatory)]
        [string]$Name,
        [switch]$OutputScriptOnly,
        [switch]$EnableException
    )
    begin {
        $rawsql = Get-Content "$script:PSModuleRoot\bin\sp_SQLskills_ConvertTraceToEEs.sql" -Raw
    }
    process {
        foreach ($trace in $InputObject) {
            if (-not $trace.id -and -not $trace.Parent) {
                Stop-Function -Message "Input is of the wrong type. Use Get-DbaTrace." -Continue
                return
            }

            $server = $trace.Parent

            if ($server.VersionMajor -lt 11) {
                Stop-Function -Message "SQL Server version 2012+ required - $server not supported."
                return
            }

            $tempdb = $server.Databases['tempdb']
            $traceid = $trace.id

            if ((Get-DbaXESession -SqlInstance $server -Session $PSBoundParameters.Name)) {
                $oldname = $name
                $Name = "$name-$traceid"
                Write-Message -Level Output -Message "XE Session $oldname already exists on $server, trying $name."
            }

            if ((Get-DbaXESession -SqlInstance $server -Session $Name)) {
                $oldname = $name
                $Name = "$name-$(Get-Random)"
                Write-Message -Level Output -Message "XE Session $oldname already exists on $server, trying $name."
            }

            $sql = $rawsql.Replace("--TRACEID--", $traceid)
            $sql = $sql.Replace("--SESSIONNAME--", $name)

            try {
                Write-Message -Level Verbose -Message "Executing SQL in tempdb."
                $results = $tempdb.ExecuteWithResults($sql).Tables.Rows.SqlString
            }
            catch {
                Stop-Function -Message "Issue creating, dropping or executing sp_SQLskills_ConvertTraceToExtendedEvents in tempdb on $server." -Target $server -ErrorRecord $_
            }

            $results = $results -join "`r`n"

            if ($OutputScriptOnly) {
                $results
            }
            else {
                Write-Message -Level Verbose -Message "Creating XE Session $name."
                try {
                    $tempdb.ExecuteNonQuery($results)
                }
                catch {
                    Stop-Function -Message "Issue creating extended event $name on $server." -Target $server -ErrorRecord $_
                }
                Get-DbaXESession -SqlInstance $server -Session $name
            }
        }
    }
}
function Copy-DbaAgentAlert {
    <#
        .SYNOPSIS
            Copy-DbaAgentAlert migrates alerts from one SQL Server to another.

        .DESCRIPTION
            By default, all alerts are copied. The -Alert parameter is auto-populated for command-line completion and can be used to copy only specific alerts.

            If the alert already exists on the destination, it will be skipped unless -Force is used.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Alert
            The alert(s) to process. This list is auto-populated from the server. If unspecified, all alerts will be processed.

        .PARAMETER ExcludeAlert
            The alert(s) to exclude. This list is auto-populated from the server.

        .PARAMETER IncludeDefaults
            Copy SQL Agent defaults such as FailSafeEmailAddress, ForwardingServer, and PagerSubjectTemplate.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER Force
            If this switch is enabled, the Alert will be dropped and recreated on Destination.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration, Agent
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaAgentAlert

        .EXAMPLE
            Copy-DbaAgentAlert -Source sqlserver2014a -Destination sqlcluster

            Copies all alerts from sqlserver2014a to sqlcluster using Windows credentials. If alerts with the same name exist on sqlcluster, they will be skipped.

        .EXAMPLE
            Copy-DbaAgentAlert -Source sqlserver2014a -Destination sqlcluster -Alert PSAlert -SourceSqlCredential $cred -Force

            Copies a only the alert named PSAlert from sqlserver2014a to sqlcluster using SQL credentials for sqlserver2014a and Windows credentials for sqlcluster. If a alert with the same name exists on sqlcluster, it will be dropped and recreated because -Force was used.

        .EXAMPLE
            Copy-DbaAgentAlert -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force

            Shows what would happen if the command were executed using force.
    #>
    [cmdletbinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]$SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [object[]]$Alert,
        [object[]]$ExcludeAlert,
        [switch]$IncludeDefaults,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
            $serverAlerts = $sourceServer.JobServer.Alerts
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            $destAlerts = $destServer.JobServer.Alerts
            
            if ($IncludeDefaults -eq $true) {
                if ($PSCmdlet.ShouldProcess($destinstance, "Creating Alert Defaults")) {
                    $copyAgentAlertStatus = [pscustomobject]@{
                        SourceServer = $sourceServer.Name
                        DestinationServer = $destServer.Name
                        Name         = "Alert Defaults"
                        Type         = "Alert Defaults"
                        Status       = $null
                        Notes        = $null
                        DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                    }
                    try {
                        Write-Message -Message "Creating Alert Defaults" -Level Verbose
                        $sql = $sourceServer.JobServer.AlertSystem.Script() | Out-String
                        $sql = $sql -replace [Regex]::Escape("'$source'"), "'$destinstance'"
                        
                        Write-Message -Message $sql -Level Debug
                        $null = $destServer.Query($sql)
                        
                        $copyAgentAlertStatus.Status = "Successful"
                    }
                    catch {
                        $copyAgentAlertStatus.Status = "Failed"
                        $copyAgentAlertStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        Stop-Function -Message "Issue creating alert defaults." -Category InvalidOperation -ErrorRecord $_ -Target $destServer -Continue
                    }
                    $copyAgentAlertStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                }
            }
            
            foreach ($serverAlert in $serverAlerts) {
                $alertName = $serverAlert.name
                $copyAgentAlertStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name         = $alertName
                    Type         = "Agent Alert"
                    Notes        = $null
                    Status       = $null
                    DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                }
                if (($Alert -and $Alert -notcontains $alertName) -or ($ExcludeAlert -and $ExcludeAlert -contains $alertName)) {
                    continue
                }
                
                if ($destAlerts.name -contains $serverAlert.name) {
                    if ($force -eq $false) {
                        if ($PSCmdlet.ShouldProcess($destinstance, "Alert [$alertName] exists at destination. Use -Force to drop and migrate.")) {
                            $copyAgentAlertStatus.Status = "Skipped"
                            $copyAgentAlertStatus.Notes = "Already exists"
                            $copyAgentAlertStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Write-Message -Message "Alert [$alertName] exists at destination. Use -Force to drop and migrate." -Level Verbose
                        }
                        continue
                    }
                    
                    if ($PSCmdlet.ShouldProcess($destinstance, "Dropping alert $alertName and recreating")) {
                        try {
                            Write-Message -Message "Dropping Alert $alertName on $destServer." -Level Verbose
                            
                            $sql = "EXEC msdb.dbo.sp_delete_alert @name = N'$($alertname)';"
                            Write-Message -Message $sql -Level Debug
                            $null = $destServer.Query($sql)
                            $destAlerts.Refresh()
                        }
                        catch {
                            $copyAgentAlertStatus.Status = "Failed"
                            $copyAgentAlertStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Stop-Function -Message "Issue dropping/recreating alert" -Category InvalidOperation -ErrorRecord $_ -Target $destServer -Continue
                        }
                    }
                }
                
                if ($destAlerts | Where-Object { $_.Severity -eq $serverAlert.Severity -and $_.MessageID -eq $serverAlert.MessageID -and $_.DatabaseName -eq $serverAlert.DatabaseName -and $_.EventDescriptionKeyword -eq $serverAlert.EventDescriptionKeyword }) {
                    if ($PSCmdlet.ShouldProcess($destinstance, "Checking for conflicts")) {
                        $conflictMessage = "Alert [$alertName] has already been defined to use"
                        if ($serverAlert.Severity -gt 0) { $conflictMessage += " severity $($serverAlert.Severity)" }
                        if ($serverAlert.MessageID -gt 0) { $conflictMessage += " error number $($serverAlert.MessageID)" }
                        if ($serverAlert.DatabaseName) { $conflictMessage += " on database '$($serverAlert.DatabaseName)'" }
                        if ($serverAlert.EventDescriptionKeyword) { $conflictMessage += " with error text '$($serverAlert.Severity)'" }
                        $conflictMessage += ". Skipping."
                        
                        Write-Message -Level Verbose -Message $conflictMessage
                        $copyAgentAlertStatus.Status = "Skipped"
                        $copyAgentAlertStatus.Notes = $conflictMessage
                        $copyAgentAlertStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    continue
                }
                if ($serverAlert.JobName -and $destServer.JobServer.Jobs.Name -NotContains $serverAlert.JobName) {
                    Write-Message -Level Verbose -Message "Alert [$alertName] has job [$($serverAlert.JobName)] configured as response. The job does not exist on destination $destServer. Skipping."
                    if ($PSCmdlet.ShouldProcess($destinstance, "Checking for conflicts")) {
                        $copyAgentAlertStatus.Status = "Skipped"
                        $copyAgentAlertStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    continue
                }
                
                if ($PSCmdlet.ShouldProcess($destinstance, "Creating Alert $alertName")) {
                    try {
                        Write-Message -Message "Copying Alert $alertName" -Level Verbose
                        $sql = $serverAlert.Script() | Out-String
                        $sql = $sql -replace "@job_id=N'........-....-....-....-............", "@job_id=N'00000000-0000-0000-0000-000000000000"
                        
                        Write-Message -Message $sql -Level Debug
                        $null = $destServer.Query($sql)
                        
                        $copyAgentAlertStatus.Status = "Successful"
                        $copyAgentAlertStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copyAgentAlertStatus.Status = "Failed"
                        $copyAgentAlertStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        Stop-Function -Message "Issue creating alert" -Category InvalidOperation -ErrorRecord $_ -Target $destServer -Continue
                    }
                }
                
                $destServer.JobServer.Alerts.Refresh()
                $destServer.JobServer.Jobs.Refresh()
                
                $newAlert = $destServer.JobServer.Alerts[$alertName]
                $notifications = $serverAlert.EnumNotifications()
                $jobName = $serverAlert.JobName
                
                # JobId = 00000000-0000-0000-0000-000 means the Alert does not execute/is attached to a SQL Agent Job.
                if ($serverAlert.JobId -ne '00000000-0000-0000-0000-000000000000') {
                    $copyAgentAlertStatus = [pscustomobject]@{
                        SourceServer = $sourceServer.Name
                        DestinationServer = $destServer.Name
                        Name         = $alertName
                        Type         = "Agent Alert Job Association"
                        Notes        = "Associated with $jobName"
                        Status       = $null
                        DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                    }
                    if ($PSCmdlet.ShouldProcess($destinstance, "Adding $alertName to $jobName")) {
                        try {
                        <# THERE needs to be validation within this block to see if the $jobName actually exists on the source server. #>
                            Write-Message -Message "Adding $alertName to $jobName" -Level Verbose
                            $newJob = $destServer.JobServer.Jobs[$jobName]
                            $newJobId = ($newJob.JobId) -replace " ", ""
                            $sql = $sql -replace '00000000-0000-0000-0000-000000000000', $newJobId
                            $sql = $sql -replace 'sp_add_alert', 'sp_update_alert'
                            
                            Write-Message -Message $sql -Level Debug
                            $null = $destServer.Query($sql)
                            
                            $copyAgentAlertStatus.Status = "Successful"
                            $copyAgentAlertStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        }
                        catch {
                            $copyAgentAlertStatus.Status = "Failed"
                            $copyAgentAlertStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Stop-Function -Message "Issue adding alert to job" -Category InvalidOperation -ErrorRecord $_ -Target $destServer
                        }
                    }
                }
                
                if ($PSCmdlet.ShouldProcess($destinstance, "Moving Notifications $alertName")) {
                    try {
                        $copyAgentAlertStatus = [pscustomobject]@{
                            SourceServer = $sourceServer.Name
                            DestinationServer = $destServer.Name
                            Name         = $alertName
                            Type         = "Agent Alert Notification"
                            Notes        = $null
                            Status       = $null
                            DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                        }
                        # cant add them this way, we need to modify the existing one or give all options that are supported.
                        foreach ($notify in $notifications) {
                            $notifyCollection = @()
                            if ($notify.UseNetSend -eq $true) {
                                Write-Message -Message "Adding net send" -Level Verbose
                                $notifyCollection += "NetSend"
                            }
                            
                            if ($notify.UseEmail -eq $true) {
                                Write-Message -Message "Adding email" -Level Verbose
                                $notifyCollection += "NotifyEmail"
                            }
                            
                            if ($notify.UsePager -eq $true) {
                                Write-Message -Message "Adding pager" -Level Verbose
                                $notifyCollection += "Pager"
                            }
                            
                            $notifyMethods = $notifyCollection -join ", "
                            $newAlert.AddNotification($notify.OperatorName, [Microsoft.SqlServer.Management.Smo.Agent.NotifyMethods]$notifyMethods)
                        }
                        $copyAgentAlertStatus.Status = "Successful"
                        $copyAgentAlertStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copyAgentAlertStatus.Status = "Failed"
                        $copyAgentAlertStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        Stop-Function -Message "Issue moving notifications for the alert" -Category InvalidOperation -ErrorRecord $_ -Target $destServer
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlAlert
    }
}
#ValidationTags#Messaging#
function Copy-DbaAgentCategory {
    <#
        .SYNOPSIS
            Copy-DbaAgentCategory migrates SQL Agent categories from one SQL Server to another. This is similar to sp_add_category.

            https://msdn.microsoft.com/en-us/library/ms181597.aspx

        .DESCRIPTION
            By default, all SQL Agent categories for Jobs, Operators and Alerts are copied.

            The -OperatorCategories parameter is auto-populated for command-line completion and can be used to copy only specific operator categories.
            The -AgentCategories parameter is auto-populated for command-line completion and can be used to copy only specific agent categories.
            The -JobCategories parameter is auto-populated for command-line completion and can be used to copy only specific job categories.

            If the category already exists on the destination, it will be skipped unless -Force is used.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER CategoryType
            Specifies the Category Type to migrate. Valid options are "Job", "Alert" and "Operator". When CategoryType is specified, all categories from the selected type will be migrated. For granular migrations, use the three parameters below.

        .PARAMETER OperatorCategory
            This parameter is auto-populated for command-line completion and can be used to copy only specific operator categories.

        .PARAMETER AgentCategory
            This parameter is auto-populated for command-line completion and can be used to copy only specific agent categories.

        .PARAMETER JobCategory
            This parameter is auto-populated for command-line completion and can be used to copy only specific job categories.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER Force
            If this switch is enabled, the Category will be dropped and recreated on Destination.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration, Agent
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaAgentCategory

        .EXAMPLE
            Copy-DbaAgentCategory -Source sqlserver2014a -Destination sqlcluster

            Copies all operator categories from sqlserver2014a to sqlcluster using Windows authentication. If operator categories with the same name exist on sqlcluster, they will be skipped.

        .EXAMPLE
            Copy-DbaAgentCategory -Source sqlserver2014a -Destination sqlcluster -OperatorCategory PSOperator -SourceSqlCredential $cred -Force

            Copies a single operator category, the PSOperator operator category from sqlserver2014a to sqlcluster using SQL credentials to authenticate to sqlserver2014a and Windows credentials for sqlcluster. If a operator category with the same name exists on sqlcluster, it will be dropped and recreated because -Force was used.

        .EXAMPLE
            Copy-DbaAgentCategory -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force

            Shows what would happen if the command were executed using force.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldprocess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]$SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [Parameter(ParameterSetName = 'SpecificAlerts')]
        [ValidateSet('Job', 'Alert', 'Operator')]
        [string[]]$CategoryType,
        [string[]]$JobCategory,
        [string[]]$AgentCategory,
        [string[]]$OperatorCategory,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    
    begin {
        function Copy-JobCategory {
            <#
                .SYNOPSIS
                    Copy-JobCategory migrates job categories from one SQL Server to another.

                .DESCRIPTION
                    By default, all job categories are copied. The -JobCategories parameter is auto-populated for command-line completion and can be used to copy only specific job categories.

                    If the associated credential for the category does not exist on the destination, it will be skipped. If the job category already exists on the destination, it will be skipped unless -Force is used.
            #>
            param (
                [string[]]$jobCategories
            )
            
            process {
                
                $serverJobCategories = $sourceServer.JobServer.JobCategories | Where-Object ID -ge 100
                $destJobCategories = $destServer.JobServer.JobCategories | Where-Object ID -ge 100
                
                foreach ($jobCategory in $serverJobCategories) {
                    $categoryName = $jobCategory.Name
                    
                    $copyJobCategoryStatus = [pscustomobject]@{
                        SourceServer = $sourceServer.Name
                        DestinationServer = $destServer.Name
                        Name         = $categoryName
                        Type         = "Agent Job Category"
                        Status       = $null
                        Notes        = $null
                        DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                    }
                    
                    if ($jobCategories.Count -gt 0 -and $jobCategories -notcontains $categoryName) {
                        continue
                    }
                    
                    if ($destJobCategories.Name -contains $jobCategory.name) {
                        if ($force -eq $false) {
                            $copyJobCategoryStatus.Status = "Skipped"
                            $copyJobCategoryStatus.Notes = "Already exists"
                            $copyJobCategoryStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Write-Message -Level Verbose -Message "Job category $categoryName exists at destination. Use -Force to drop and migrate."
                            continue
                        }
                        else {
                            if ($Pscmdlet.ShouldProcess($destinstance, "Dropping job category $categoryName")) {
                                try {
                                    Write-Message -Level Verbose -Message "Dropping Job category $categoryName"
                                    $destServer.JobServer.JobCategories[$categoryName].Drop()
                                }
                                catch {
                                    $copyJobCategoryStatus.Status = "Failed"
                                    $copyJobCategoryStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                    Stop-Function -Message "Issue dropping job category" -Target $categoryName -ErrorRecord $_ -Continue
                                }
                            }
                        }
                    }
                    
                    if ($Pscmdlet.ShouldProcess($destinstance, "Creating Job category $categoryName")) {
                        try {
                            Write-Message -Level Verbose -Message "Copying Job category $categoryName"
                            $sql = $jobCategory.Script() | Out-String
                            Write-Message -Level Debug -Message "SQL Statement: $sql"
                            $destServer.Query($sql)
                            
                            $copyJobCategoryStatus.Status = "Successful"
                            $copyJobCategoryStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        }
                        catch {
                            $copyJobCategoryStatus.Status = "Failed"
                            $copyJobCategoryStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Stop-Function -Message "Issue copying job category" -Target $categoryName -ErrorRecord $_
                        }
                    }
                }
            }
        }
        
        function Copy-OperatorCategory {
            <#
                .SYNOPSIS
                    Copy-OperatorCategory migrates operator categories from one SQL Server to another.

                .DESCRIPTION
                    By default, all operator categories are copied. The -OperatorCategories parameter is auto-populated for command-line completion and can be used to copy only specific operator categories.

                    If the associated credential for the category does not exist on the destination, it will be skipped. If the operator category already exists on the destination, it will be skipped unless -Force is used.
            #>
            [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldprocess = $true)]
            param (
                [string[]]$operatorCategories
            )
            process {
                $serverOperatorCategories = $sourceServer.JobServer.OperatorCategories | Where-Object ID -ge 100
                $destOperatorCategories = $destServer.JobServer.OperatorCategories | Where-Object ID -ge 100
                
                foreach ($operatorCategory in $serverOperatorCategories) {
                    $categoryName = $operatorCategory.Name
                    
                    $copyOperatorCategoryStatus = [pscustomobject]@{
                        SourceServer = $sourceServer.Name
                        DestinationServer = $destServer.Name
                        Type         = "Agent Operator Category"
                        Name         = $categoryName
                        Status       = $null
                        Notes        = $null
                        DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                    }
                    
                    if ($operatorCategories.Count -gt 0 -and $operatorCategories -notcontains $categoryName) {
                        continue
                    }
                    
                    if ($destOperatorCategories.Name -contains $operatorCategory.Name) {
                        if ($force -eq $false) {
                            $copyOperatorCategoryStatus.Status = "Skipped"
                            $copyOperatorCategoryStatus.Notes = "Already exists"
                            $copyOperatorCategoryStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Write-Message -Level Verbose -Message "Operator category $categoryName exists at destination. Use -Force to drop and migrate."
                            continue
                        }
                        else {
                            if ($Pscmdlet.ShouldProcess($destinstance, "Dropping operator category $categoryName and recreating")) {
                                try {
                                    Write-Message -Level Verbose -Message "Dropping Operator category $categoryName"
                                    $destServer.JobServer.OperatorCategories[$categoryName].Drop()
                                    Write-Message -Level Verbose -Message "Copying Operator category $categoryName"
                                    $sql = $operatorCategory.Script() | Out-String
                                    Write-Message -Level Debug -Message $sql
                                    $destServer.Query($sql)
                                }
                                catch {
                                    $copyOperatorCategoryStatus.Status = "Failed"
                                    $copyOperatorCategoryStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                    Stop-Function -Message "Issue dropping operator category" -Target $categoryName -ErrorRecord $_
                                }
                            }
                        }
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Creating Operator category $categoryName")) {
                            try {
                                Write-Message -Level Verbose -Message "Copying Operator category $categoryName"
                                $sql = $operatorCategory.Script() | Out-String
                                Write-Message -Level Debug -Message $sql
                                $destServer.Query($sql)
                                
                                $copyOperatorCategoryStatus.Status = "Successful"
                                $copyOperatorCategoryStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            }
                            catch {
                                $copyOperatorCategoryStatus.Status = "Failed"
                                $copyOperatorCategoryStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                Stop-Function -Message "Issue copying operator category" -Target $categoryName -ErrorRecord $_
                            }
                        }
                    }
                }
            }
        }
        
        function Copy-AlertCategory {
            <#
                .SYNOPSIS
                    Copy-AlertCategory migrates alert categories from one SQL Server to another.

                .DESCRIPTION
                    By default, all alert categories are copied. The -AlertCategories parameter is auto-populated for command-line completion and can be used to copy only specific alert categories.

                    If the associated credential for the category does not exist on the destination, it will be skipped. If the alert category already exists on the destination, it will be skipped unless -Force is used.
            #>
            [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldprocess = $true)]
            param (
                [string[]]$AlertCategories
            )
            
            process {
                if ($sourceServer.VersionMajor -lt 9 -or $destServer.VersionMajor -lt 9) {
                    throw "Server AlertCategories are only supported in SQL Server 2005 and above. Quitting."
                }
                
                $serverAlertCategories = $sourceServer.JobServer.AlertCategories | Where-Object ID -ge 100
                $destAlertCategories = $destServer.JobServer.AlertCategories | Where-Object ID -ge 100
                
                foreach ($alertCategory in $serverAlertCategories) {
                    $categoryName = $alertCategory.Name
                    
                    $copyAlertCategoryStatus = [pscustomobject]@{
                        SourceServer = $sourceServer.Name
                        DestinationServer = $destServer.Name
                        Type         = "Agent Alert Category"
                        Name         = $categoryName
                        Status       = $null
                        Notes        = $null
                        DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                    }
                    
                    if ($alertCategories.Length -gt 0 -and $alertCategories -notcontains $categoryName) {
                        continue
                    }
                    
                    if ($destAlertCategories.Name -contains $alertCategory.name) {
                        if ($force -eq $false) {
                            $copyAlertCategoryStatus.Status = "Skipped"
                            $copyAlertCategoryStatus.Notes = "Already exists"
                            $copyAlertCategoryStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Write-Message -Level Verbose -Message "Alert category $categoryName exists at destination. Use -Force to drop and migrate."
                            continue
                        }
                        else {
                            if ($Pscmdlet.ShouldProcess($destinstance, "Dropping alert category $categoryName and recreating")) {
                                try {
                                    Write-Message -Level Verbose -Message "Dropping Alert category $categoryName"
                                    $destServer.JobServer.AlertCategories[$categoryName].Drop()
                                    Write-Message -Level Verbose -Message "Copying Alert category $categoryName"
                                    $sql = $alertcategory.Script() | Out-String
                                    Write-Message -Level Debug -Message "SQL Statement: $sql"
                                    $destServer.Query($sql)
                                }
                                catch {
                                    $copyAlertCategoryStatus.Status = "Failed"
                                    $copyAlertCategoryStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                    Stop-Function -Message "Issue dropping alert category" -Target $categoryName -ErrorRecord $_
                                }
                            }
                        }
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Creating Alert category $categoryName")) {
                            try {
                                Write-Message -Level Verbose -Message "Copying Alert category $categoryName"
                                $sql = $alertCategory.Script() | Out-String
                                Write-Message -Level Debug -Message $sql
                                $destServer.Query($sql)
                                
                                $copyAlertCategoryStatus.Status = "Successful"
                                $copyAlertCategoryStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            }
                            catch {
                                $copyAlertCategoryStatus.Status = "Failed"
                                $copyAlertCategoryStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                Stop-Function -Message "Issue creating alert category" -Target $categoryName -ErrorRecord $_
                            }
                        }
                    }
                }
            }
        }
        
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            
            if ($CategoryType.count -gt 0) {
                
                switch ($CategoryType) {
                    "Job" {
                        Copy-JobCategory
                    }
                    
                    "Alert" {
                        Copy-AlertCategory
                    }
                    
                    "Operator" {
                        Copy-OperatorCategory
                    }
                }
                continue
            }
            
            if (($OperatorCategory.Count + $AlertCategory.Count + $jobCategory.Count) -gt 0) {
                
                if ($OperatorCategory.Count -gt 0) {
                    Copy-OperatorCategory -OperatorCategories $OperatorCategory
                }
                
                if ($AlertCategory.Count -gt 0) {
                    Copy-AlertCategory -AlertCategories $AlertCategory
                }
                
                if ($jobCategory.Count -gt 0) {
                    Copy-JobCategory -JobCategories $jobCategory
                }
                continue
            }
            Copy-OperatorCategory
            Copy-AlertCategory
            Copy-JobCategory
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlAgentCategory
    }
}
function Copy-DbaAgentJob {
    <#
        .SYNOPSIS
            Copy-DbaAgentJob migrates jobs from one SQL Server to another.

        .DESCRIPTION
            By default, all jobs are copied. The -Job parameter is auto-populated for command-line completion and can be used to copy only specific jobs.

            If the job already exists on the destination, it will be skipped unless -Force is used.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Job
            The job(s) to process. This list is auto-populated from the server. If unspecified, all jobs will be processed.

        .PARAMETER ExcludeJob
            The job(s) to exclude. This list is auto-populated from the server.

        .PARAMETER DisableOnSource
            If this switch is enabled, the job will be disabled on the source server.

        .PARAMETER DisableOnDestination
            If this switch is enabled, the newly migrated job will be disabled on the destination server.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER Force
            If this switch is enabled, the Job will be dropped and recreated on Destination.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration, Agent, Job
            Author: Chrissy LeMaire (@cl), netnerds.net

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaAgentJob

        .EXAMPLE
            Copy-DbaAgentJob -Source sqlserver2014a -Destination sqlcluster

            Copies all jobs from sqlserver2014a to sqlcluster, using Windows credentials. If jobs with the same name exist on sqlcluster, they will be skipped.

        .EXAMPLE
            Copy-DbaAgentJob -Source sqlserver2014a -Destination sqlcluster -Job PSJob -SourceSqlCredential $cred -Force

            Copies a single job, the PSJob job from sqlserver2014a to sqlcluster, using SQL credentials for sqlserver2014a and Windows credentials for sqlcluster. If a job with the same name exists on sqlcluster, it will be dropped and recreated because -Force was used.

        .EXAMPLE
            Copy-DbaAgentJob -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force

            Shows what would happen if the command were executed using force.
    #>
    [cmdletbinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]$SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [object[]]$Job,
        [object[]]$ExcludeJob,
        [switch]$DisableOnSource,
        [switch]$DisableOnDestination,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        
        $serverJobs = $sourceServer.JobServer.Jobs
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            $destJobs = $destServer.JobServer.Jobs
            
            foreach ($serverJob in $serverJobs) {
                $jobName = $serverJob.name
                $jobId = $serverJob.JobId
                
                $copyJobStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name         = $jobName
                    Type         = "Agent Job"
                    Status       = $null
                    Notes        = $null
                    DateTime     = [DbaDateTime](Get-Date)
                }
                
                if ($Job -and $jobName -notin $Job -or $jobName -in $ExcludeJob) {
                    Write-Message -Level Verbose -Message "Job [$jobName] filtered. Skipping."
                    continue
                }
                Write-Message -Message "Working on job: $jobName" -Level Verbose
                $sql = "
                SELECT sp.[name] AS MaintenancePlanName
                FROM msdb.dbo.sysmaintplan_plans AS sp
                INNER JOIN msdb.dbo.sysmaintplan_subplans AS sps
                    ON sps.plan_id = sp.id
                WHERE job_id = '$($jobId)'"
                Write-Message -Message $sql -Level Debug
                
                $MaintenancePlanName = $sourceServer.Query($sql).MaintenancePlanName
                
                if ($MaintenancePlanName) {
                    if ($Pscmdlet.ShouldProcess($destinstance, "Job [$jobName] is associated with Maintenance Plan: $MaintenancePlanNam")) {
                        $copyJobStatus.Status = "Skipped"
                        $copyJobStatus.Notes = "Job is associated with maintenance plan"
                        $copyJobStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        Write-Message -Level Verbose -Message "Job [$jobName] is associated with Maintenance Plan: $MaintenancePlanName"
                    }
                    continue
                }
                
                $dbNames = $serverJob.JobSteps.DatabaseName | Where-Object { $_.Length -gt 0 }
                $missingDb = $dbNames | Where-Object { $destServer.Databases.Name -notcontains $_ }
                
                if ($missingDb.Count -gt 0 -and $dbNames.Count -gt 0) {
                    if ($Pscmdlet.ShouldProcess($destinstance, "Database(s) $missingDb doesn't exist on destination. Skipping job [$jobName].")) {
                        $missingDb = ($missingDb | Sort-Object | Get-Unique) -join ", "
                        $copyJobStatus.Status = "Skipped"
                        $copyJobStatus.Notes = "Job is dependent on database: $missingDb"
                        $copyJobStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        Write-Message -Level Verbose -Message "Database(s) $missingDb doesn't exist on destination. Skipping job [$jobName]."
                    }
                    continue
                }
                
                $missingLogin = $serverJob.OwnerLoginName | Where-Object { $destServer.Logins.Name -notcontains $_ }
                
                if ($missingLogin.Count -gt 0) {
                    if ($force -eq $false) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Login(s) $missingLogin doesn't exist on destination. Use -Force to set owner to [sa]. Skipping job [$jobName].")) {
                            $missingLogin = ($missingLogin | Sort-Object | Get-Unique) -join ", "
                            $copyJobStatus.Status = "Skipped"
                            $copyJobStatus.Notes = "Job is dependent on login $missingLogin"
                            $copyJobStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Write-Message -Level Verbose -Message "Login(s) $missingLogin doesn't exist on destination. Use -Force to set owner to [sa]. Skipping job [$jobName]."
                        }
                        continue
                    }
                }
                
                $proxyNames = $serverJob.JobSteps.ProxyName | Where-Object { $_.Length -gt 0 }
                $missingProxy = $proxyNames | Where-Object { $destServer.JobServer.ProxyAccounts.Name -notcontains $_ }
                
                if ($missingProxy.Count -gt 0 -and $proxyNames.Count -gt 0) {
                    if ($Pscmdlet.ShouldProcess($destinstance, "Proxy Account(s) $($proxyNames[0]) doesn't exist on destination. Skipping job [$jobName].")) {
                        $missingProxy = ($missingProxy | Sort-Object | Get-Unique) -join ", "
                        $copyJobStatus.Status = "Skipped"
                        $copyJobStatus.Notes = "Job is dependent on proxy $($proxyNames[0])"
                        $copyJobStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        Write-Message -Level Verbose -Message "Proxy Account(s) $($proxyNames[0]) doesn't exist on destination. Skipping job [$jobName]."
                    }
                    continue
                }
                
                $operators = $serverJob.OperatorToEmail, $serverJob.OperatorToNetSend, $serverJob.OperatorToPage | Where-Object { $_.Length -gt 0 }
                $missingOperators = $operators | Where-Object { $destServer.JobServer.Operators.Name -notcontains $_ }
                
                if ($missingOperators.Count -gt 0 -and $operators.Count -gt 0) {
                    if ($Pscmdlet.ShouldProcess($destinstance, "Operator(s) $($missingOperator) doesn't exist on destination. Skipping job [$jobName]")) {
                        $missingOperator = ($operators | Sort-Object | Get-Unique) -join ", "
                        $copyJobStatus.Status = "Skipped"
                        $copyJobStatus.Notes = "Job is dependent on operator $missingOperator"
                        $copyJobStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        Write-Message -Level Verbose -Message "Operator(s) $($missingOperator) doesn't exist on destination. Skipping job [$jobName]"
                    }
                    continue
                }
                
                if ($destJobs.name -contains $serverJob.name) {
                    if ($force -eq $false) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Job $jobName exists at destination. Use -Force to drop and migrate.")) {
                            $copyJobStatus.Status = "Skipped"
                            $copyJobStatus.Notes = "Job already exists on destination"
                            $copyJobStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Write-Message -Level Verbose -Message "Job $jobName exists at destination. Use -Force to drop and migrate."
                        }
                        continue
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Dropping job $jobName and recreating")) {
                            try {
                                Write-Message -Message "Dropping Job $jobName" -Level Verbose
                                $destServer.JobServer.Jobs[$jobName].Drop()
                            }
                            catch {
                                $copyJobStatus.Status = "Failed"
                                $copyJobStatus.Notes = (Get-ErrorMessage -Record $_).Message
                                $copyJobStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                Stop-Function -Message "Issue dropping job" -Target $jobName -ErrorRecord $_ -Continue
                            }
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Creating Job $jobName")) {
                    try {
                        Write-Message -Message "Copying Job $jobName" -Level Verbose
                        $sql = $serverJob.Script() | Out-String
                        
                        if ($missingLogin.Count -gt 0 -and $force) {
                            $saLogin = Get-SqlSaLogin -SqlInstance $destServer
                            $sql = $sql -replace [Regex]::Escape("@owner_login_name=N'$missingLogin'"), [Regex]::Escape("@owner_login_name=N'$saLogin'")
                        }
                        
                        Write-Message -Message $sql -Level Debug
                        $destServer.Query($sql)
                        
                        $destServer.JobServer.Jobs.Refresh()
                        $destServer.JobServer.Jobs[$serverJob.name].IsEnabled = $sourceServer.JobServer.Jobs[$serverJob.name].IsEnabled
                        $destServer.JobServer.Jobs[$serverJob.name].Alter()
                    }
                    catch {
                        $copyJobStatus.Status = "Failed"
                        $copyJobStatus.Notes = (Get-ErrorMessage -Record $_)
                        $copyJobStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        Stop-Function -Message "Issue copying job" -Target $jobName -ErrorRecord $_ -Continue
                    }
                }
                
                if ($DisableOnDestination) {
                    if ($Pscmdlet.ShouldProcess($destinstance, "Disabling $jobName")) {
                        Write-Message -Message "Disabling $jobName on $destinstance" -Level Verbose
                        $destServer.JobServer.Jobs[$serverJob.name].IsEnabled = $False
                        $destServer.JobServer.Jobs[$serverJob.name].Alter()
                    }
                }
                
                if ($DisableOnSource) {
                    if ($Pscmdlet.ShouldProcess($source, "Disabling $jobName")) {
                        Write-Message -Message "Disabling $jobName on $source" -Level Verbose
                        $serverJob.IsEnabled = $false
                        $serverJob.Alter()
                    }
                }
                if ($Pscmdlet.ShouldProcess($destinstance, "Reporting status of migration for $jobname")) {
                    $copyJobStatus.Status = "Successful"
                    $copyJobStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlJob
    }
}
function Copy-DbaAgentOperator {
    <#
        .SYNOPSIS
            Copy-DbaAgentOperator migrates operators from one SQL Server to another.

        .DESCRIPTION
            By default, all operators are copied. The -Operators parameter is auto-populated for command-line completion and can be used to copy only specific operators.

            If the associated credentials for the operator do not exist on the destination, it will be skipped. If the operator already exists on the destination, it will be skipped unless -Force is used.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Operator
            The operator(s) to process. This list is auto-populated from the server. If unspecified, all operators will be processed.

        .PARAMETER ExcludeOperator
            The operators(s) to exclude. This list is auto-populated from the server.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER Force
            If this switch is enabled, the Operator will be dropped and recreated on Destination.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration, Agent, Operator
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaAgentOperator

        .EXAMPLE
            Copy-DbaAgentOperator -Source sqlserver2014a -Destination sqlcluster

            Copies all operators from sqlserver2014a to sqlcluster using Windows credentials. If operators with the same name exist on sqlcluster, they will be skipped.

        .EXAMPLE
            Copy-DbaAgentOperator -Source sqlserver2014a -Destination sqlcluster -Operator PSOperator -SourceSqlCredential $cred -Force

            Copies only the PSOperator operator from sqlserver2014a to sqlcluster using SQL credentials for sqlserver2014a and Windows credentials for sqlcluster. If an operator with the same name exists on sqlcluster, it will be dropped and recreated because -Force was used.

        .EXAMPLE
            Copy-DbaAgentOperator -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force

            Shows what would happen if the command were executed using force.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]
        $SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]
        $DestinationSqlCredential,
        [object[]]$Operator,
        [object[]]$ExcludeOperator,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        $serverOperator = $sourceServer.JobServer.Operators
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            
            $destOperator = $destServer.JobServer.Operators
            $failsafe = $destServer.JobServer.AlertSystem | Select-Object FailSafeOperator
            foreach ($sOperator in $serverOperator) {
                $operatorName = $sOperator.Name
                
                $copyOperatorStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name         = $operatorName
                    Type         = "Agent Operator"
                    Status       = $null
                    Notes        = $null
                    DateTime     = [DbaDateTime](Get-Date)
                }
                
                if ($Operator -and $Operator -notcontains $operatorName -or $ExcludeOperator -in $operatorName) {
                    continue
                }
                
                if ($destOperator.Name -contains $sOperator.Name) {
                    if ($force -eq $false) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Operator $operatorName exists at destination. Use -Force to drop and migrate.")) {
                            $copyOperatorStatus.Status = "Skipped"
                            $copyOperatorStatus.Notes = "Already exists"
                            $copyOperatorStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Write-Message -Level Verbose -Message "Operator $operatorName exists at destination. Use -Force to drop and migrate."
                        }
                        continue
                    }
                    else {
                        if ($failsafe.FailSafeOperator -eq $operatorName) {
                            Write-Message -Level Verbose -Message "$operatorName is the failsafe operator. Skipping drop."
                            continue
                        }
                        
                        if ($Pscmdlet.ShouldProcess($destinstance, "Dropping operator $operatorName and recreating")) {
                            try {
                                Write-Message -Level Verbose -Message "Dropping Operator $operatorName"
                                $destServer.JobServer.Operators[$operatorName].Drop()
                            }
                            catch {
                                $copyOperatorStatus.Status = "Failed"
                                $copyOperatorStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                
                                Stop-Function -Message "Issue dropping operator" -Category InvalidOperation -ErrorRecord $_ -Target $destServer -Continue
                            }
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Creating Operator $operatorName")) {
                    try {
                        Write-Message -Level Verbose -Message "Copying Operator $operatorName"
                        $sql = $sOperator.Script() | Out-String
                        Write-Message -Level Debug -Message $sql
                        $destServer.Query($sql)
                        
                        $copyOperatorStatus.Status = "Successful"
                        $copyOperatorStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copyOperatorStatus.Status = "Failed"
                        $copyOperatorStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        Stop-Function -Message "Issue creating operator." -Category InvalidOperation -ErrorRecord $_ -Target $destServer
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlOperator
    }
}
function Copy-DbaAgentProxyAccount {
    <#
        .SYNOPSIS
            Copy-DbaAgentProxyAccount migrates proxy accounts from one SQL Server to another.

        .DESCRIPTION
            By default, all proxy accounts are copied. The -ProxyAccounts parameter is auto-populated for command-line completion and can be used to copy only specific proxy accounts.

            If the associated credential for the account does not exist on the destination, it will be skipped. If the proxy account already exists on the destination, it will be skipped unless -Force is used.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER Force
            If this switch is enabled, the Operator will be dropped and recreated on Destination.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration, Agent
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaAgentProxyAccount

        .EXAMPLE
            Copy-DbaAgentProxyAccount -Source sqlserver2014a -Destination sqlcluster

            Copies all proxy accounts from sqlserver2014a to sqlcluster using Windows credentials. If proxy accounts with the same name exist on sqlcluster, they will be skipped.

        .EXAMPLE
            Copy-DbaAgentProxyAccount -Source sqlserver2014a -Destination sqlcluster -ProxyAccount PSProxy -SourceSqlCredential $cred -Force

            Copies only the PSProxy proxy account from sqlserver2014a to sqlcluster using SQL credentials for sqlserver2014a and Windows credentials for sqlcluster. If a proxy account with the same name exists on sqlcluster, it will be dropped and recreated because -Force was used.

        .EXAMPLE
            Copy-DbaAgentProxyAccount -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force

            Shows what would happen if the command were executed using force.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]
        $SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]
        $DestinationSqlCredential,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 9
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        $serverProxyAccounts = $sourceServer.JobServer.ProxyAccounts
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            
            $destProxyAccounts = $destServer.JobServer.ProxyAccounts
            
            foreach ($proxyAccount in $serverProxyAccounts) {
                $proxyName = $proxyAccount.Name
                
                $copyAgentProxyAccountStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name         = $null
                    Type         = "Agent Proxy"
                    Status       = $null
                    Notes        = $null
                    DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                }
                
                if ($proxyAccounts.Length -gt 0 -and $proxyAccounts -notcontains $proxyName) {
                    continue
                }
                
                # Proxy accounts rely on Credential accounts
                $credentialName = $proxyAccount.CredentialName
                $copyAgentProxyAccountStatus.Name = $credentialName
                $copyAgentProxyAccountStatus.Type = "Credential"
                
                try {
                    $credentialtest = $destServer.Credentials[$CredentialName]
                }
                catch {
                    # don't care
                }
                
                if ($null -eq $credentialtest) {
                    $copyAgentProxyAccountStatus.Status = "Skipped"
                    $copyAgentProxyAccountStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    Write-Message -Level Verbose -Message "Associated credential account, $CredentialName, does not exist on $destinstance. Skipping migration of $proxyName."
                    continue
                }
                
                if ($destProxyAccounts.Name -contains $proxyName) {
                    $copyAgentProxyAccountStatus.Name = $proxyName
                    $copyAgentProxyAccountStatus.Type = "ProxyAccount"
                    
                    if ($force -eq $false) {
                        $copyAgentProxyAccountStatus.Status = "Skipped"
                        $copyAgentProxyAccountStatus
                        Write-Message -Level Verbose -Message "Server proxy account $proxyName exists at destination. Use -Force to drop and migrate."
                        continue
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Dropping server proxy account $proxyName and recreating")) {
                            try {
                                Write-Message -Level Verbose -Message "Dropping server proxy account $proxyName"
                                $destServer.JobServer.ProxyAccounts[$proxyName].Drop()
                            }
                            catch {
                                $copyAgentProxyAccountStatus.Status = "Failed"
                                $copyAgentProxyAccountStatus.Notes = "Could not drop"
                                $copyAgentProxyAccountStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                Stop-Function -Message "Issue dropping proxy account" -Target $proxyName -ErrorRecord $_ -Continue
                            }
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Creating server proxy account $proxyName")) {
                    $copyAgentProxyAccountStatus.Name = $proxyName
                    $copyAgentProxyAccountStatus.Type = "ProxyAccount"
                    
                    try {
                        Write-Message -Level Verbose -Message "Copying server proxy account $proxyName"
                        $sql = $proxyAccount.Script() | Out-String
                        Write-Message -Level Debug -Message $sql
                        $destServer.Query($sql)
                        
                        # Will fixing this misspelled status cause problems downstream?
                        $copyAgentProxyAccountStatus.Status = "Successful"
                        $copyAgentProxyAccountStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $exceptionstring = $_.Exception.InnerException.ToString()
                        if ($exceptionstring -match 'subsystem') {
                            $copyAgentProxyAccountStatus.Status = "Skipping"
                            $copyAgentProxyAccountStatus.Notes = "Failure"
                            $copyAgentProxyAccountStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            
                            Write-Message -Level Verbose -Message "One or more subsystems do not exist on the destination server. Skipping that part."
                        }
                        else {
                            $copyAgentProxyAccountStatus.Status = "Failed"
                            $copyAgentProxyAccountStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            
                            Stop-Function -Message "Issue creating proxy account" -Target $proxyName -ErrorRecord $_
                        }
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlProxyAccount
    }
}
function Copy-DbaAgentSharedSchedule {
    <#
        .SYNOPSIS
            Copy-DbaAgentSharedSchedule migrates shared job schedules from one SQL Server to another.

        .DESCRIPTION
            All shared job schedules are copied.

            If the associated credential for the account does not exist on the destination, it will be skipped. If the shared job schedule already exists on the destination, it will be skipped unless -Force is used.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER Force
            If this switch is enabled, the Operator will be dropped and recreated on Destination.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration, Agent
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaAgentSharedSchedule

        .EXAMPLE
            Copy-DbaAgentSharedSchedule -Source sqlserver2014a -Destination sqlcluster

            Copies all shared job schedules from sqlserver2014a to sqlcluster using Windows credentials. If shared job schedules with the same name exist on sqlcluster, they will be skipped.

        .EXAMPLE
            Copy-DbaAgentSharedSchedule -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force

            Shows what would happen if the command were executed using force.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]
        $SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]
        $DestinationSqlCredential,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 9
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        $serverSchedules = $sourceServer.JobServer.SharedSchedules
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            
            $destSchedules = $destServer.JobServer.SharedSchedules
            foreach ($schedule in $serverSchedules) {
                $scheduleName = $schedule.Name
                $copySharedScheduleStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Type         = "Agent Schedule"
                    Name         = $scheduleName
                    Status       = $null
                    Notes        = $null
                    DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                }
                
                if ($schedules.Length -gt 0 -and $schedules -notcontains $scheduleName) {
                    continue
                }
                
                if ($destSchedules.Name -contains $scheduleName) {
                    if ($force -eq $false) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Shared job schedule $scheduleName exists at destination. Use -Force to drop and migrate.")) {
                            $copySharedScheduleStatus.Status = "Skipped"
                            $copySharedScheduleStatus.Notes = "Already exists"
                            $copySharedScheduleStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Write-Message -Level Verbose -Message "Shared job schedule $scheduleName exists at destination. Use -Force to drop and migrate."
                            continue
                        }
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Schedule [$scheduleName] has associated jobs. Skipping.")) {
                            if ($destServer.JobServer.Jobs.JobSchedules.Name -contains $scheduleName) {
                                $copySharedScheduleStatus.Status = "Skipped"
                                $copySharedScheduleStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                Write-Message -Level Verbose -Message "Schedule [$scheduleName] has associated jobs. Skipping."
                            }
                            continue
                        }
                        else {
                            if ($Pscmdlet.ShouldProcess($destinstance, "Dropping schedule $scheduleName and recreating")) {
                                try {
                                    Write-Message -Level Verbose -Message "Dropping schedule $scheduleName"
                                    $destServer.JobServer.SharedSchedules[$scheduleName].Drop()
                                }
                                catch {
                                    $copySharedScheduleStatus.Status = "Failed"
                                    $copySharedScheduleStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                    Stop-Function -Message "Issue dropping schedule" -Target $scheduleName -ErrorRecord $_ -Continue
                                }
                            }
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Creating schedule $scheduleName")) {
                    try {
                        Write-Message -Level Verbose -Message "Copying schedule $scheduleName"
                        $sql = $schedule.Script() | Out-String
                        
                        Write-Message -Level Debug -Message $sql
                        $destServer.Query($sql)
                        
                        $copySharedScheduleStatus.Status = "Successful"
                        $copySharedScheduleStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copySharedScheduleStatus.Status = "Failed"
                        $copySharedScheduleStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        Stop-Function -Message "Issue creating schedule" -Target $scheduleName -ErrorRecord $_ -Continue
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlSharedSchedule
    }
}
function Copy-DbaBackupDevice {
    <#
        .SYNOPSIS
            Copies backup devices one by one. Copies both SQL code and the backup file itself.

        .DESCRIPTION
            Backups are migrated using Admin shares. If the destination directory does not exist, SQL Server's default backup directory will be used.

            If a backup device with same name exists on destination, it will not be dropped and recreated unless -Force is used.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER BackupDevice
            BackupDevice to be copied. Auto-populated list of devices. If not provided all BackupDevice(s) will be copied.

        .PARAMETER Force
            If this switch is enabled, backup device(s) will be dropped and recreated if they already exists on destination.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration, Backup
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaBackupDevice

        .EXAMPLE
            Copy-DbaBackupDevice -Source sqlserver2014a -Destination sqlcluster

            Copies all server backup devices from sqlserver2014a to sqlcluster using Windows credentials. If backup devices with the same name exist on sqlcluster, they will be skipped.

        .EXAMPLE
            Copy-DbaBackupDevice -Source sqlserver2014a -Destination sqlcluster -BackupDevice backup01 -SourceSqlCredential $cred -Force

            Copies only the backup device named backup01 from sqlserver2014a to sqlcluster using SQL credentials for sqlserver2014a    and Windows credentials for sqlcluster. If a backup device with the same name exists on sqlcluster, it will be dropped and recreated because -Force was used.

        .EXAMPLE
            Copy-DbaBackupDevice -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force

            Shows what would happen if the command were executed using force.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]$SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [object[]]$BackupDevice,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        $serverBackupDevices = $sourceServer.BackupDevices
        $sourceNetBios = $Source.ComputerName
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            $destBackupDevices = $destServer.BackupDevices
            $destNetBios = $destinstance.ComputerName
            
            foreach ($currentBackupDevice in $serverBackupDevices) {
                $deviceName = $currentBackupDevice.Name
                
                $copyBackupDeviceStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name         = $deviceName
                    Type         = "Backup Device"
                    Status       = $null
                    Notes        = $null
                    DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                }
                
                if ($BackupDevice -and $BackupDevice -notcontains $deviceName) {
                    continue
                }
                
                if ($destBackupDevices.Name -contains $deviceName) {
                    if ($force -eq $false) {
                        $copyBackupDeviceStatus.Status = "Skipped"
                        $copyBackupDeviceStatus.Notes = "Already exists"
                        $copyBackupDeviceStatus
                        
                        Write-Message -Level Verbose -Message "backup device $deviceName exists at destination. Use -Force to drop and migrate."
                        continue
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Dropping backup device $deviceName")) {
                            try {
                                Write-Message -Level Verbose -Message "Dropping backup device $deviceName"
                                $destServer.BackupDevices[$deviceName].Drop()
                            }
                            catch {
                                $copyBackupDeviceStatus.Status = "Failed"
                                $copyBackupDeviceStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                
                                Stop-Function -Message "Issue dropping backup device" -Target $deviceName -ErrorRecord $_ -Continue
                            }
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Generating SQL code for $deviceName")) {
                    Write-Message -Level Verbose -Message "Scripting out SQL for $deviceName"
                    try {
                        $sql = $currentBackupDevice.Script() | Out-String
                        $sql = $sql -replace [Regex]::Escape("'$source'"), "'$destinstance'"
                    }
                    catch {
                        $copyBackupDeviceStatus.Status = "Failed"
                        $copyBackupDeviceStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Stop-Function -Message "Issue scripting out backup device" -Target $deviceName -ErrorRecord $_ -Continue
                    }
                }
                
                if ($Pscmdlet.ShouldProcess("console", "Stating that the actual file copy is about to occur")) {
                    Write-Message -Level Verbose -Message "Preparing to copy actual backup file"
                }
                
                $path = Split-Path $sourceServer.BackupDevices[$deviceName].PhysicalLocation
                $destPath = Join-AdminUnc $destNetBios $path
                $sourcepath = Join-AdminUnc $sourceNetBios $sourceServer.BackupDevices[$deviceName].PhysicalLocation
                
                Write-Message -Level Verbose -Message "Checking if directory $destPath exists"
                
                if ($(Test-DbaSqlPath -SqlInstance $destinstance -Path $path) -eq $false) {
                    $backupDirectory = $destServer.BackupDirectory
                    $destPath = Join-AdminUnc $destNetBios $backupDirectory
                    
                    if ($Pscmdlet.ShouldProcess($destinstance, "Updating create code to use new path")) {
                        Write-Message -Level Verbose -Message "$path doesn't exist on $destinstance"
                        Write-Message -Level Verbose -Message "Using default backup directory $backupDirectory"
                        
                        try {
                            Write-Message -Level Verbose -Message "Updating $deviceName to use $backupDirectory"
                            $sql = $sql -replace [Regex]::Escape($path), $backupDirectory
                        }
                        catch {
                            $copyBackupDeviceStatus.Status = "Failed"
                            $copyBackupDeviceStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            
                            Stop-Function -Message "Issue updating script of backup device with new path" -Target $deviceName -ErrorRecord $_ -Continue
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Copying $sourcepath to $destPath using BITSTransfer")) {
                    try {
                        Start-BitsTransfer -Source $sourcepath -Destination $destPath -ErrorAction Stop
                        Write-Message -Level Verbose -Message "Backup device $deviceName successfully copied"
                    }
                    catch {
                        $copyBackupDeviceStatus.Status = "Failed"
                        $copyBackupDeviceStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Stop-Function -Message "Issue copying backup device to destination" -Target $deviceName -ErrorRecord $_ -Continue
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Adding backup device $deviceName")) {
                    Write-Message -Level Verbose -Message "Adding backup device $deviceName on $destinstance"
                    try {
                        $destServer.Query($sql)
                        $destServer.BackupDevices.Refresh()
                        
                        $copyBackupDeviceStatus.Status = "Successful"
                        $copyBackupDeviceStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copyBackupDeviceStatus.Status = "Failed"
                        $copyBackupDeviceStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Stop-Function -Message "Issue adding backup device" -Target $deviceName -ErrorRecord $_ -Continue
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlBackupDevice
    }
}
function Copy-DbaCentralManagementServer {
    <#
        .SYNOPSIS
            Migrates SQL Server Central Management groups and server instances from one SQL Server to another.

        .DESCRIPTION
            Copy-DbaCentralManagementServer copies all groups, subgroups, and server instances from one SQL Server to another.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER CMSGroup
            This is an auto-populated array that contains your Central Management Server top-level groups on Source. You can specify one, many or none.

            If CMSGroup is not specified, all groups in your Central Management Server will be copied.

        .PARAMETER SwitchServerName
            If this switch is enabled, all instance names will be changed from Source to Destination.

            Central Management Server does not allow you to add a shared registered server with the same name as the Configuration Server.

        .PARAMETER Force
            If this switch is enabled, group(s) will be dropped and recreated if they already exists on destination.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaCentralManagementServer

        .EXAMPLE
            Copy-DbaCentralManagementServer -Source sqlserver2014a -Destination sqlcluster

            All groups, subgroups, and server instances are copied from sqlserver's Central Management Server to sqlcluster's Central Management Server.

        .EXAMPLE
            Copy-DbaCentralManagementServer -Source sqlserver2014a -Destination sqlcluster -ServerGroup Group1,Group3

            Top-level groups Group1 and Group3 along with their subgroups and server instances are copied from sqlserver to sqlcluster.

        .EXAMPLE
            Copy-DbaCentralManagementServer -Source sqlserver2014a -Destination sqlcluster -ServerGroup Group1,Group3 -SwitchServerName -SourceSqlCredential $SourceSqlCredential -DestinationSqlCredential $DestinationSqlCredential

            Top-level groups Group1 and Group3 along with their subgroups and server instances are copied from sqlserver to sqlcluster. When adding sql instances to sqlcluster, if the server name of the migrating instance is "sqlcluster", it will be switched to "sqlserver".

            If SwitchServerName is not specified, "sqlcluster" will be skipped.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    Param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]$SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [object[]]$CMSGroup,
        [switch]$SwitchServerName,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        function Invoke-ParseServerGroup {
            [cmdletbinding()]
            param (
                $sourceGroup,
                $destinationGroup,
                $SwitchServerName
            )
            if ($destinationGroup.Name -eq "DatabaseEngineServerGroup" -and $sourceGroup.Name -ne "DatabaseEngineServerGroup") {
                $currentServerGroup = $destinationGroup
                $groupName = $sourceGroup.Name
                $destinationGroup = $destinationGroup.ServerGroups[$groupName]
                
                $copyDestinationGroupStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name         = $groupName
                    Type         = "CMS Destination Group"
                    Status       = $null
                    Notes        = $null
                    DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                }
                
                if ($null -ne $destinationGroup) {
                    
                    if ($force -eq $false) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Checking to see if $groupName exists")) {
                            $copyDestinationGroupStatus.Status = "Skipped"
                            $copyDestinationGroupStatus.Notes = "Already exists"
                            $copyDestinationGroupStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Write-Message -Level Verbose -Message "Destination group $groupName exists at destination. Use -Force to drop and migrate."
                        }
                        continue
                    }
                    if ($Pscmdlet.ShouldProcess($destinstance, "Dropping group $groupName")) {
                        try {
                            Write-Message -Level Verbose -Message "Dropping group $groupName"
                            $destinationGroup.Drop()
                        }
                        catch {
                            $copyDestinationGroupStatus.Status = "Failed"
                            $copyDestinationGroupStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            
                            Stop-Function -Message "Issue dropping group" -Target $groupName -ErrorRecord $_ -Continue
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Creating group $groupName")) {
                    Write-Message -Level Verbose -Message "Creating group $($sourceGroup.Name)"
                    $destinationGroup = New-Object Microsoft.SqlServer.Management.RegisteredServers.ServerGroup($currentServerGroup, $sourceGroup.Name)
                    $destinationGroup.Create()
                    
                    $copyDestinationGroupStatus.Status = "Successful"
                    $copyDestinationGroupStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                }
            }
            
            # Add Servers
            foreach ($instance in $sourceGroup.RegisteredServers) {
                $instanceName = $instance.Name
                $serverName = $instance.ServerName
                
                $copyInstanceStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name         = $instanceName
                    Type         = "CMS Instance"
                    Status       = $null
                    Notes        = $null
                    DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                }
                
                if ($serverName.ToLower() -eq $toCmStore.DomainInstanceName.ToLower()) {
                    if ($Pscmdlet.ShouldProcess($destinstance, "Checking to see if server is the CMS equals current server name")) {
                        if ($SwitchServerName) {
                            $serverName = $fromCmStore.DomainInstanceName
                            $instanceName = $fromCmStore.DomainInstanceName
                            Write-Message -Level Verbose -Message "SwitchServerName was used and new CMS equals current server name. $($toCmStore.DomainInstanceName.ToLower()) changed to $serverName."
                        }
                        else {
                            $copyInstanceStatus.Status = "Skipped"
                            $copyInstanceStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            
                            Write-Message -Level Verbose -Message "$serverName is Central Management Server. Add prohibited. Skipping."
                            continue
                        }
                    }
                }
                
                if ($destinationGroup.RegisteredServers.Name -contains $instanceName) {
                    
                    if ($force -eq $false) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Checking to see if $instanceName in $groupName exists")) {
                            $copyInstanceStatus.Status = "Skipped"
                            $copyInstanceStatus.Notes = "Already exists"
                            $copyInstanceStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            
                            Write-Message -Level Verbose -Message "Instance $instanceName exists in group $groupName at destination. Use -Force to drop and migrate."
                        }
                        continue
                    }
                    
                    if ($Pscmdlet.ShouldProcess($destinstance, "Dropping instance $instanceName from $groupName and recreating")) {
                        try {
                            Write-Message -Level Verbose -Message "Dropping instance $instance from $groupName"
                            $destinationGroup.RegisteredServers[$instanceName].Drop()
                        }
                        catch {
                            $copyInstanceStatus.Status = "Failed"
                            $copyInstanceStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            
                            Stop-Function -Message "Issue dropping instance from group" -Target $instanceName -ErrorRecord $_ -Continue
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Copying $instanceName")) {
                    $newServer = New-Object Microsoft.SqlServer.Management.RegisteredServers.RegisteredServer($destinationGroup, $instanceName)
                    $newServer.ServerName = $serverName
                    $newServer.Description = $instance.Description
                    
                    if ($serverName -ne $fromCmStore.DomainInstanceName) {
                        $newServer.SecureConnectionString = $instance.SecureConnectionString.ToString()
                        $newServer.ConnectionString = $instance.ConnectionString.ToString()
                    }
                    
                    try {
                        $newServer.Create()
                        
                        $copyInstanceStatus.Status = "Successful"
                        $copyInstanceStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copyInstanceStatus.Status = "Failed"
                        $copyInstanceStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        if ($_.Exception -match "same name") {
                            Stop-Function -Message "Could not add Switched Server instance name." -Target $instanceName -ErrorRecord $_ -Continue
                        }
                        else {
                            Stop-Function -Message "Failed to add $serverName" -Target $instanceName -ErrorRecord $_ -Continue
                        }
                    }
                    Write-Message -Level Verbose -Message "Added Server $serverName as $instanceName to $($destinationGroup.Name)"
                }
            }
            
            # Add Groups
            foreach ($fromSubGroup in $sourceGroup.ServerGroups) {
                $fromSubGroupName = $fromSubGroup.Name
                $toSubGroup = $destinationGroup.ServerGroups[$fromSubGroupName]
                
                $copyGroupStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name         = $fromSubGroupName
                    Type         = "CMS Group"
                    Status       = $null
                    Notes        = $null
                    DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                }
                
                if ($null -ne $toSubGroup) {
                    if ($force -eq $false) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Checking to see if subgroup $fromSubGroupName exists")) {
                            $copyGroupStatus.Status = "Skipped"
                            $copyGroupStatus.Notes = "Already exists"
                            $copyGroupStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            
                            Write-Message -Level Verbose -Message "Subgroup $fromSubGroupName exists at destination. Use -Force to drop and migrate."
                        }
                        continue
                    }
                    
                    if ($Pscmdlet.ShouldProcess($destinstance, "Dropping subgroup $fromSubGroupName recreating")) {
                        try {
                            Write-Message -Level Verbose -Message "Dropping subgroup $fromSubGroupName"
                            $toSubGroup.Drop()
                        }
                        catch {
                            $copyGroupStatus.Status = "Failed"
                            $copyGroupStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            
                            Stop-Function -Message "Issue dropping subgroup" -Target $toSubGroup -ErrorRecord $_ -Continue
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Creating group $($fromSubGroup.Name)")) {
                    Write-Message -Level Verbose -Message "Creating group $($fromSubGroup.Name)"
                    $toSubGroup = New-Object Microsoft.SqlServer.Management.RegisteredServers.ServerGroup($destinationGroup, $fromSubGroup.Name)
                    $toSubGroup.create()
                    
                    $copyGroupStatus.Status = "Successful"
                    $copyGroupStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                }
                
                Invoke-ParseServerGroup -sourceGroup $fromSubGroup -destinationgroup $toSubGroup -SwitchServerName $SwitchServerName
            }
        }
        
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 10
            $fromCmStore = Get-DbaRegisteredServerStore -SqlInstance $sourceServer
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
    }
    
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            $toCmStore = Get-DbaRegisteredServerStore -SqlInstance $destServer
            
            $stores = $fromCmStore.DatabaseEngineServerGroup
            if ($CMSGroup) {
                $stores = @();
                foreach ($groupName in $CMSGroup) {
                    $stores += $fromCmStore.DatabaseEngineServerGroup.ServerGroups[$groupName]
                }
            }
            
            foreach ($store in $stores) {
                Invoke-ParseServerGroup -sourceGroup $store -destinationgroup $toCmStore.DatabaseEngineServerGroup -SwitchServerName $SwitchServerName
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlCentralManagementServer
    }
}
function Copy-DbaCredential {
    <#
        .SYNOPSIS
            Copy-DbaCredential migrates SQL Server Credentials from one SQL Server to another while maintaining Credential passwords.

        .DESCRIPTION
            By using password decryption techniques provided by Antti Rantasaari (NetSPI, 2014), this script migrates SQL Server Credentials from one server to another while maintaining username and password.

            Credit: https://blog.netspi.com/decrypting-mssql-database-link-server-passwords/
            License: BSD 3-Clause http://opensource.org/licenses/BSD-3-Clause

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Credential
             This command requires access to the Windows OS via PowerShell remoting. Use this credential to connect to Windows using alternative credentials.

        .PARAMETER Name
            Only include specific names
            Note: if spaces exist in the credential name, you will have to type "" or '' around it.

        .PARAMETER ExcludeName
            Excluded credential names
    
        .PARAMETER Identity
            Only include specific identities
            Note: if spaces exist in the credential identity, you will have to type "" or '' around it.

        .PARAMETER ExcludeIdentity
            Excluded identities

        .PARAMETER Force
            If this switch is enabled, the Credential will be dropped and recreated if it already exists on Destination.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: WSMan, Migration
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires:
                - PowerShell Version 3.0, SQL Server SMO,
                - Administrator access on Windows
                - sysadmin access on SQL Server.
                - DAC access enabled for local (default)
            Limitations: Hasn't been tested thoroughly. Works on Win8.1 and SQL Server 2012 & 2014 so far.

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaCredential

        .EXAMPLE
            Copy-DbaCredential -Source sqlserver2014a -Destination sqlcluster

            Copies all SQL Server Credentials on sqlserver2014a to sqlcluster. If Credentials exist on destination, they will be skipped.

        .EXAMPLE
            Copy-DbaCredential -Source sqlserver2014a -Destination sqlcluster -Name "PowerShell Proxy Account" -Force

            Copies over one SQL Server Credential (PowerShell Proxy Account) from sqlserver to sqlcluster. If the Credential already exists on the destination, it will be dropped and recreated.
    #>
    [CmdletBinding(SupportsShouldProcess)]
    param (
        [parameter(Mandatory)]
        [DbaInstanceParameter]$Source,
        [PSCredential]
        $SourceSqlCredential,
        [PSCredential]
        $Credential,
        [parameter(Mandatory)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [string[]]$Name,
        [string[]]$ExcludeName,
        [Alias('CredentialIdentity')]
        [string[]]$Identity,
        [Alias('ExcludeCredentialIdentity')]
        [string[]]$ExcludeIdentity,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $null = Test-ElevationRequirement -ComputerName $Source.ComputerName

        function Copy-Credential {
            <#
                .SYNOPSIS
                    Copies Credentials from one server to another using a combination of SMO's .Script() and manual password updates.

                .OUTPUT
                    System.Data.DataTable
            #>
            param (
                [string[]]$Credentials,
                [bool]$Force
            )

            Write-Message -Level Verbose -Message "Collecting Credential logins and passwords on $($sourceServer.Name)"
            $sourceCredentials = Get-DecryptedObject -SqlInstance $sourceServer -Type Credential
            $credentialList = Get-DbaCredential -SqlInstance $sourceServer -Name $Name -ExcludeName $ExcludeName -Identity $Identity -ExcludeIdentity $ExcludeIdentity
            
            Write-Message -Level Verbose -Message "Starting migration"
            foreach ($credential in $credentialList) {
                $destServer.Credentials.Refresh()
                $credentialName = $credential.Name

                $copyCredentialStatus = [pscustomobject]@{
                    SourceServer      = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Type              = "Credential"
                    Name              = $credentialName
                    Status            = $null
                    Notes             = $null
                    DateTime          = [DbaDateTime](Get-Date)
                }

                if ($null -ne $destServer.Credentials[$credentialName]) {
                    if (!$force) {
                        $copyCredentialStatus.Status = "Skipping"
                        $copyCredentialStatus.Notes = "Already exists"
                        $copyCredentialStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject

                        Write-Message -Level Verbose -Message "$credentialName exists $($destServer.Name). Skipping."
                        continue
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance.Name, "Dropping $identity")) {
                            $destServer.Credentials[$credentialName].Drop()
                            $destServer.Credentials.Refresh()
                        }
                    }
                }
                
                Write-Message -Level Verbose -Message "Attempting to migrate $credentialName"
                try {
                    $currentCred = $sourceCredentials | Where-Object { $_.Name -eq "[$credentialName]" }
                    $sqlcredentialName = $credentialName.Replace("'", "''")
                    $identity = $currentCred.Identity.Replace("'", "''")
                    $password = $currentCred.Password.Replace("'","''")
                    if ($Pscmdlet.ShouldProcess($destinstance.Name, "Copying $identity")) {
                        $destServer.Query("CREATE CREDENTIAL [$sqlcredentialName] WITH IDENTITY = N'$identity', SECRET = N'$password'")
                        $destServer.Credentials.Refresh()
                        Write-Message -Level Verbose -Message "$credentialName successfully copied"
                    }

                    $copyCredentialStatus.Status = "Successful"
                    $copyCredentialStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                }
                catch {
                    $copyCredentialStatus.Status = "Failed"
                    $copyCredentialStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject

                    Stop-Function -Message "Error creating credential" -Target $credentialName -ErrorRecord $_
                }
            }
        }
        
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 9
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance
            return
        }
        
        if ($null -ne $SourceSqlCredential.Username) {
            Write-Message -Level Verbose -Message "You are using SQL credentials and this script requires Windows admin access to the $Source server. Trying anyway."
        }
        
        $sourceNetBios = Resolve-NetBiosName $sourceServer
        
        Invoke-SmoCheck -SqlInstance $sourceServer
        
        Write-Message -Level Verbose -Message "Checking if Remote Registry is enabled on $source"
        try {
            Invoke-Command2 -ComputerName $sourceNetBios -Credential $credential -ScriptBlock { Get-ItemProperty -Path "HKLM:\SOFTWARE\" }
        }
        catch {
            Stop-Function -Message "Can't connect to registry on $source" -Target $sourceNetBios -ErrorRecord $_
            return
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }
        
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            Invoke-SmoCheck -SqlInstance $destServer
            
            Copy-Credential $credentials -force:$force
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlCredential
    }
}
function Copy-DbaCustomError {
    <#
        .SYNOPSIS
            Copy-DbaCustomError migrates custom errors (user defined messages), by the custom error ID, from one SQL Server to another.

        .DESCRIPTION
            By default, all custom errors are copied. The -CustomError parameter is auto-populated for command-line completion and can be used to copy only specific custom errors.

            If the custom error already exists on the destination, it will be skipped unless -Force is used. The us_english version must be created first. If you drop the us_english version, all the other languages will be dropped for that specific ID as well.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER CustomError
            The custom error(s) to process. This list is auto-populated from the server. If unspecified, all custom errors will be processed.

        .PARAMETER ExcludeCustomError
            The custom error(s) to exclude. This list is auto-populated from the server.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER Force
            If this switch is enabled, the custom error will be dropped and recreated if it already exists on Destination.

        .NOTES
            Tags: Migration, CustomError
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaCustomError

        .EXAMPLE
            Copy-DbaCustomError -Source sqlserver2014a -Destination sqlcluster

            Copies all server custom errors from sqlserver2014a to sqlcluster using Windows credentials. If custom errors with the same name exist on sqlcluster, they will be skipped.

        .EXAMPLE
            Copy-DbaCustomError -Source sqlserver2014a -SourceSqlCredential $scred -Destination sqlcluster -DestinationSqlCredential $dcred -CustomError 60000 -Force

            Copies only the custom error with ID number 60000 from sqlserver2014a to sqlcluster using SQL credentials for sqlserver2014a and Windows credentials for sqlcluster. If a custom error with the same name exists on sqlcluster, it will be updated because -Force was used.

        .EXAMPLE
            Copy-DbaCustomError -Source sqlserver2014a -Destination sqlcluster -ExcludeCustomError 60000 -Force

            Copies all the custom errors found on sqlserver2014a except the custom error with ID number 60000 to sqlcluster. If a custom error with the same name exists on sqlcluster, it will be updated because -Force was used.

        .EXAMPLE
            Copy-DbaCustomError -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force

            Shows what would happen if the command were executed using force.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]
        $SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]
        $DestinationSqlCredential,
        [object[]]$CustomError,
        [object[]]$ExcludeCustomError,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 9
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        $orderedCustomErrors = @($sourceServer.UserDefinedMessages | Where-Object Language -eq "us_english")
        $orderedCustomErrors += $sourceServer.UserDefinedMessages | Where-Object Language -ne "us_english"
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            # US has to go first
            $destCustomErrors = $destServer.UserDefinedMessages
            
            foreach ($currentCustomError in $orderedCustomErrors) {
                $customErrorId = $currentCustomError.ID
                $language = $currentCustomError.Language.ToString()
                
                $copyCustomErrorStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Type         = "Custom error"
                    Name         = $currentCustomError
                    Status       = $null
                    Notes        = $null
                    DateTime     = [DbaDateTime](Get-Date)
                }
                
                if ($CustomError -and ($customErrorId -notin $CustomError -or $customErrorId -in $ExcludeCustomError)) {
                    continue
                }
                
                if ($destCustomErrors.ID -contains $customErrorId) {
                    if ($force -eq $false) {
                        $copyCustomErrorStatus.Status = "Skipped"
                        $copyCustomErrorStatus.Notes = "Already exists"
                        $copyCustomErrorStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Write-Message -Level Verbose -Message "Custom error $customErrorId $language exists at destination. Use -Force to drop and migrate."
                        continue
                    }
                    else {
                        If ($Pscmdlet.ShouldProcess($destinstance, "Dropping custom error $customErrorId $language and recreating")) {
                            try {
                                Write-Message -Level Verbose -Message "Dropping custom error $customErrorId (drops all languages for custom error $customErrorId)"
                                $destServer.UserDefinedMessages[$customErrorId, $language].Drop()
                            }
                            catch {
                                $copyCustomErrorStatus.Status = "Failed"
                                $copyCustomErrorStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                
                                Stop-Function -Message "Issue dropping custom error" -Target $customErrorId -ErrorRecord $_ -Continue
                            }
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Creating custom error $customErrorId $language")) {
                    try {
                        Write-Message -Level Verbose -Message "Copying custom error $customErrorId $language"
                        $sql = $currentCustomError.Script() | Out-String
                        Write-Message -Level Debug -Message $sql
                        $destServer.Query($sql)
                        
                        $copyCustomErrorStatus.Status = "Successful"
                        $copyCustomErrorStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copyCustomErrorStatus.Status = "Failed"
                        $copyCustomErrorStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Stop-Function -Message "Issue creating custom error" -Target $customErrorId -ErrorRecord $_
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlCustomError
    }
}
function Copy-DbaDatabase {
    <#
        .SYNOPSIS
            Migrates SQL Server databases from one SQL Server to another.

        .DESCRIPTION
            This script provides the ability to migrate databases using detach/copy/attach or backup/restore. This script works with named instances, clusters and SQL Server Express Edition.

            By default, databases will be migrated to the destination SQL Server's default data and log directories. You can override this by specifying -ReuseSourceFolderStructure. Filestreams and filegroups are also migrated. Safety is emphasized.

        .PARAMETER Source
            Source SQL Server.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You may specify multiple servers.

            Note that when using -BackupRestore with multiple servers, the backup will only be performed once and backups will be deleted at the end (if you didn't specify -NoBackupCleanup).

            When using -DetachAttach with multiple servers, -Reattach must be specified.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Migrates only specified databases. This list is auto-populated from the server for tab completion. Multiple databases may be specified as a collection.

        .PARAMETER ExcludeDatabase
            Excludes specified databases when performing -AllDatabases migrations. This list is auto-populated from the Source for tab completion.

        .PARAMETER AllDatabases
            If this switch is enabled, all user databases will be migrated. System and support databases will not be migrated. Requires -BackupRestore or -DetachAttach.

        .PARAMETER BackupRestore
            If this switch is enabled, the copy-only backup and restore method will be used to migrate the database(s). This method requires that you specify -NetworkShare in a valid UNC format (\\server\share).

            Backups will be immediately deleted after use unless -NoBackupCleanup is specified.

        .PARAMETER NetworkShare
            Specifies the network location for the backup files. The SQL Server service accounts must have read/write permission on this path.

        .PARAMETER WithReplace
            If this switch is enabled, the restore is executed with WITH REPLACE.

        .PARAMETER NoRecovery
            If this switch is enabled, the restore is executed with WITH NORECOVERY. Ideal for staging.

        .PARAMETER NoBackupCleanup
            If this switch is enabled, backups generated by this cmdlet will not be deleted after they are restored. The default behavior is to delete these backups.

        .PARAMETER NumberFiles
            Number of files to split the backup. Default is 3.

        .PARAMETER DetachAttach
            If this switch is enabled, the detach/copy/attach method is used to perform database migrations. No files are deleted on Source. If Destination attachment fails, the Source database will be reattached. File copies are performed over administrative shares (\\server\x$\mssql) using BITS. If a database is being mirrored, the mirror will be broken prior to migration.

        .PARAMETER Reattach
            If this switch is enabled, all databases are reattached to Source after DetachAttach migration.

        .PARAMETER SetSourceReadOnly
            If this switch is enabled, all migrated databases are set to ReadOnly on Source prior to detach/attach & backup/restore.

            If -Reattach is used, databases are set to read-only after reattaching.

        .PARAMETER ReuseSourceFolderStructure
            If this switch is enabled, databases will be migrated to a data and log directory structure on Destination mirroring that used on Source. By default, the default data and log directories for Destination will be used when the databases are migrated.

            The structure on Source  will be kept exactly, so consider this if you're migrating between different versions and use part of Microsoft's default Sql structure (MSSql12.INSTANCE, etc)

            To reuse Destination folder structure, use the  -WithReplace switch.

        .PARAMETER IncludeSupportDbs
            If this switch is enabled, ReportServer, ReportServerTempDb, SSISDB, and distribution databases will be copied if they exist on Source. A log file named $SOURCE-$destinstance-$date-Sqls.csv will be written to the current directory.

            Use of this switch requires -BackupRestore or -DetachAttach as well.

        .PARAMETER InputObject
            A collection of dbobjects from the pipeline.

        .PARAMETER UseLastBackups
            Use the last full, diff and logs instead of performing backups. Note that the backups must exist in a location accessible by all destination servers, such a network share.

        .PARAMETER NoCopyOnly
             If this switch is enabled, backups will be taken without COPY_ONLY. This will break the LSN backup chain, which will interfere with the restore chain of the database.

            By default this switch is disabled, so backups will be taken with COPY_ONLY. This will preserve the LSN backup chain.

            For more details please refer to this MSDN article - https://msdn.microsoft.com/en-us/library/ms191495.aspx

        .PARAMETER SetSourceOffline
            If this switch is enabled, the Source database will be set to Offline after being copied.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER Force
            If this switch is enabled, existing databases on Destination with matching names from Source will be dropped. If using -DetachReattach, mirrors will be broken and the database(s) dropped from Availability Groups.

        .NOTES
            Tags: Migration, Backup, Restore
            Author: Chrissy LeMaire (@cl), netnerds.net

            Requires: sysadmin access on SQL Servers
            Limitations: Doesn't cover what it doesn't cover (replication, certificates, etc)

            SQL Server 2000 databases cannot be directly migrated to SQL Server 2012 and above.
            Logins within SQL Server 2012 and above logins cannot be migrated to SQL Server 2008 R2 and below.

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaDatabase

        .EXAMPLE
            Copy-DbaDatabase -Source sql2014a -Destination sql2014b -Database TestDB -BackupRestore -NetworkShare \\fileshare\sql\migration

            Migrates a single user database TestDB using Backup and restore from instance sql2014a to sql2014b. Backup files are stored in \\fileshare\sql\migration.

        .EXAMPLE
            Copy-DbaDatabase -Source sql2012 -Destination sql2014, sql2016 -DetachAttach -Reattach

            Databases will be migrated from sql2012 to both sql2014 and sql2016 using the detach/copy files/attach method.The following will be performed: kick all users out of the database, detach all data/log files, move files across the network over an admin share (\\SqlSERVER\M$\MSSql...), attach file on destination server, reattach at source. If the database files (*.mdf, *.ndf, *.ldf) on *destination* exist and aren't in use, they will be overwritten.

        .EXAMPLE
            Copy-DbaDatabase -Source sql2014a -Destination sqlcluster, sql2016 -BackupRestore -UseLastBackups -Force

            Migrates all user databases to sqlcluster and sql2016 using the last Full, Diff and Log backups from sql204a. If the databases exists on the destinations, they will be dropped prior to attach.

            Note that the backups must exist in a location accessible by all destination servers, such a network share.

        .EXAMPLE
            Copy-DbaDatabase -Source sql2014a -Destination sqlcluster -ExcludeDatabase Northwind, pubs -IncludeSupportDbs -Force -BackupRestore -NetworkShare \\fileshare\sql\migration

            Migrates all user databases except for Northwind and pubs by using backup/restore (copy-only). Backup files are stored in \\fileshare\sql\migration. If the database exists on the destination, it will be dropped prior to attach.

            It also includes the support databases (ReportServer, ReportServerTempDb, distribution).
    #>
    [CmdletBinding(DefaultParameterSetName = "DbBackup", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $false)]
        [DbaInstanceParameter]$Source,
        [PSCredential]$SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Alias("All")]
        [parameter(ParameterSetName = "DbBackup")]
        [parameter(ParameterSetName = "DbAttachDetach")]
        [switch]$AllDatabases,
        [parameter(Mandatory = $true, ParameterSetName = "DbBackup")]
        [switch]$BackupRestore,
        [parameter(ParameterSetName = "DbBackup",
                   HelpMessage = "Specify a valid network share in the format \\server\share that can be accessed by your account and the SQL Server service accounts for both Source and Destination.")]
        [string]$NetworkShare,
        [parameter(ParameterSetName = "DbBackup")]
        [switch]$WithReplace,
        [parameter(ParameterSetName = "DbBackup")]
        [switch]$NoRecovery,
        [parameter(ParameterSetName = "DbBackup")]
        [switch]$NoBackupCleanup,
        [parameter(ParameterSetName = "DbBackup")]
        [ValidateRange(1, 64)]
        [int]$NumberFiles = 3,
        [parameter(Mandatory = $true, ParameterSetName = "DbAttachDetach")]
        [switch]$DetachAttach,
        [parameter(ParameterSetName = "DbAttachDetach")]
        [switch]$Reattach,
        [parameter(ParameterSetName = "DbBackup")]
        [parameter(ParameterSetName = "DbAttachDetach")]
        [switch]$SetSourceReadOnly,
        [Alias("ReuseFolderStructure")]
        [parameter(ParameterSetName = "DbBackup")]
        [parameter(ParameterSetName = "DbAttachDetach")]
        [switch]$ReuseSourceFolderStructure,
        [parameter(ParameterSetName = "DbBackup")]
        [parameter(ParameterSetName = "DbAttachDetach")]
        [switch]$IncludeSupportDbs,
        [parameter(ParameterSetName = "DbBackup")]
        [switch]$UseLastBackups,
        [parameter(ValueFromPipeline)]
        [Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
        [switch]$NoCopyOnly,
        [switch]$SetSourceOffline,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        $CopyOnly = -not $NoCopyOnly

        if ($BackupRestore -and (-not $NetworkShare -and -not $UseLastBackups)) {
            Stop-Function -Message "When using -BackupRestore, you must specify -NetworkShare or -UseLastBackups"
            return
        }
        if ($NetworkShare -and $UseLastBackups) {
            Stop-Function -Message "-NetworkShare cannot be used with -UseLastBackups because the backup path is determined by the paths in the last backups"
            return
        }
        if ($DetachAttach -and -not $Reattach -and $Destination.Count -gt 1) {
            Stop-Function -Message "When using -DetachAttach with multiple servers, you must specify -Reattach to reattach database at source"
            return
        }
        function Join-AdminUnc {
            <#
        .SYNOPSIS
        Internal function. Parses a path to make it an admin UNC.
        #>
            [CmdletBinding()]
            param (
                [Parameter(Mandatory = $true)]
                [ValidateNotNullOrEmpty()]
                [string]$servername,
                [Parameter(Mandatory = $true)]
                [ValidateNotNullOrEmpty()]
                [string]$filepath

            )

            if ($script:sameserver) { return $filepath }
            if (-not $filepath) { return }
            if ($filepath.StartsWith("\\")) { return $filepath }

            $servername = $servername.Split("\")[0]

            if ($filepath.length -gt 0 -and $filepath -ne [System.DbNull]::Value) {
                $newpath = Join-Path "\\$servername\" $filepath.replace(':', '$')
                return $newpath
            }
            else { return }
        }

        function Get-SqlFileStructure {
            $dbcollection = @{ };
            $databaseProgressbar = 0

            foreach ($db in $databaseList) {
                Write-Progress -Id 1 -Activity "Processing database file structure" -PercentComplete ($databaseProgressbar / $dbCount * 100) -Status "Processing $databaseProgressbar of $dbCount."
                $dbName = $db.Name
                Write-Message -Level Verbose -Message $dbName

                $databaseProgressbar++
                $dbStatus = $db.status.toString()
                if ($dbStatus.StartsWith("Normal") -eq $false) { continue }
                $destinstancefiles = @{ }; $sourcefiles = @{ }

                $where = "Filetype <> 'LOG' and Filetype <> 'FULLTEXT'"

                $datarows = $dbFileTable.Tables.Select("dbname = '$dbName' and $where")

                # Data Files
                foreach ($file in $datarows) {
                    # Destination File Structure
                    $d = @{ }
                    if ($ReuseSourceFolderStructure) {
                        $d.physical = $file.filename
                    }
                    elseif ($WithReplace) {
                        $name = $file.Name
                        $destfile = $remoteDbFileTable.Tables[0].Select("dbname = '$dbName' and name = '$name'")
                        $d.physical = $destfile.filename

                        if ($null -eq $d.physical) {
                            $directory = Get-SqlDefaultPaths $destServer data
                            $fileName = Split-Path $file.filename -Leaf
                            $d.physical = "$directory\$fileName"
                        }
                    }
                    else {
                        $directory = Get-SqlDefaultPaths $destServer data
                        $fileName = Split-Path $file.filename -Leaf
                        $d.physical = "$directory\$fileName"
                    }
                    $d.logical = $file.Name

                    $d.remotefilename = Join-AdminUNC $destNetBios $d.physical
                    $destinstancefiles.add($file.Name, $d)

                    # Source File Structure
                    $s = @{ }
                    $s.logical = $file.Name
                    $s.physical = $file.filename
                    $s.remotefilename = Join-AdminUNC $sourceNetBios $s.physical
                    $sourcefiles.add($file.Name, $s)
                }

                # Add support for Full Text Catalogs in SQL Server 2005 and below
                if ($sourceServer.VersionMajor -lt 10) {
                    try {
                        $fttable = $null = $sourceServer.Databases[$dbName].ExecuteWithResults('sp_help_fulltext_catalogs')
                        $allrows = $fttable.Tables[0].rows
                    }
                    catch {
                        # Nothing, it's just not enabled
                    }

                    foreach ($ftc in $allrows) {
                        # Destination File Structure
                        $d = @{ }
                        $pre = "sysft_"
                        $name = $ftc.Name
                        $physical = $ftc.Path # RootPath
                        $logical = "$pre$name"
                        if ($ReuseSourceFolderStructure) {
                            $d.physical = $physical
                        }
                        else {
                            $directory = Get-SqlDefaultPaths $destServer data
                            if ($destServer.VersionMajor -lt 10) { $directory = "$directory\FTDATA" }
                            $fileName = Split-Path($physical) -leaf
                            $d.physical = "$directory\$fileName"
                        }
                        $d.logical = $logical
                        $d.remotefilename = Join-AdminUNC $destNetBios $d.physical
                        $destinstancefiles.add($logical, $d)

                        # Source File Structure
                        $s = @{ }
                        $pre = "sysft_"
                        $name = $ftc.Name
                        $physical = $ftc.Path # RootPath
                        $logical = "$pre$name"

                        $s.logical = $logical
                        $s.physical = $physical
                        $s.remotefilename = Join-AdminUNC $sourceNetBios $s.physical
                        $sourcefiles.add($logical, $s)
                    }
                }

                $where = "Filetype = 'LOG'"
                $datarows = $dbFileTable.Tables[0].Select("dbname = '$dbName' and $where")

                # Log Files
                foreach ($file in $datarows) {
                    $d = @{ }
                    if ($ReuseSourceFolderStructure) {
                        $d.physical = $file.filename
                    }
                    elseif ($WithReplace) {
                        $name = $file.Name
                        $destfile = $remoteDbFileTable.Tables[0].Select("dbname = '$dbName' and name = '$name'")
                        $d.physical = $destfile.filename

                        if ($null -eq $d.physical) {
                            $directory = Get-SqlDefaultPaths $destServer data
                            $fileName = Split-Path $file.filename -Leaf
                            $d.physical = "$directory\$fileName"
                        }
                    }
                    else {
                        $directory = Get-SqlDefaultPaths $destServer log
                        $fileName = Split-Path $file.filename -Leaf
                        $d.physical = "$directory\$fileName"
                    }
                    $d.logical = $file.Name
                    $d.remotefilename = Join-AdminUNC $destNetBios $d.physical
                    $destinstancefiles.add($file.Name, $d)

                    $s = @{ }
                    $s.logical = $file.Name
                    $s.physical = $file.filename
                    $s.remotefilename = Join-AdminUNC $sourceNetBios $s.physical
                    $sourcefiles.add($file.Name, $s)
                }

                $location = @{ }
                $location.add("Destination", $destinstancefiles)
                $location.add("Source", $sourcefiles)
                $dbcollection.Add($($db.Name), $location)
            }

            $fileStructure = [pscustomobject]@{ "databases" = $dbcollection }
            Write-Progress -id 1 -Activity "Processing database file structure" -Status "Completed" -Completed
            return $fileStructure
        }

        function Dismount-SqlDatabase {
            [CmdletBinding()]
            param (
                [object]$server,
                [string]$dbName
            )

            $currentdb = $server.databases[$dbName]
            if ($currentdb.IsMirroringEnabled) {
                try {
                    Write-Message -Level Verbose -Message "Breaking mirror for $dbName"
                    $currentdb.ChangeMirroringState([Microsoft.SqlServer.Management.Smo.MirroringOption]::Off)
                    $currentdb.Alter()
                    $currentdb.Refresh()
                    Write-Message -Level Verbose -Message "Could not break mirror for $dbName. Skipping."
                }
                catch {
                    Stop-Function -Message "Issue breaking mirror." -Target $dbName -ErrorRecord $_
                    return $false
                }
            }

            if ($currentdb.AvailabilityGroupName.Length -gt 0) {
                $agName = $currentdb.AvailabilityGroupName
                Write-Message -Level Verbose -Message "Attempting remove from Availability Group $agName."
                try {
                    $server.AvailabilityGroups[$currentdb.AvailabilityGroupName].AvailabilityDatabases[$dbName].Drop()
                    Write-Message -Level Verbose -Message "Successfully removed $dbName from  detach from $agName on $($server.Name)."
                }
                catch {
                    Stop-Function -Message "Could not remove $dbName from $agName on $($server.Name)." -Target $dbName -ErrorRecord $_
                    return $false
                }
            }

            Write-Message -Level Verbose -Message "Attempting detach from $dbName from $source."

            ####### Using Sql to detach does not modify the $currentdb collection #######

            $server.KillAllProcesses($dbName)

            try {
                $sql = "ALTER DATABASE [$dbName] SET SINGLE_USER WITH ROLLBACK IMMEDIATE"
                Write-Message -Level Verbose -Message $sql
                $null = $server.Query($sql)
                Write-Message -Level Verbose -Message "Successfully set $dbName to single-user from $source."
            }
            catch {
                Stop-Function -Message "Issue setting database to single-user." -Target $dbName -ErrorRecord $_
            }

            try {
                $sql = "EXEC master.dbo.sp_detach_db N'$dbName'"
                Write-Message -Level Verbose -Message $sql
                $null = $server.Query($sql)
                Write-Message -Level Verbose -Message "Successfully detached $dbName from $source."
                return $true
            }
            catch {
                Stop-Function -Message "Issue detaching database." -Target $dbName -ErrorRecord $_
                return $false
            }
        }

        function Mount-SqlDatabase {
            [CmdletBinding()]
            param (
                [object]$server,
                [string]$dbName,
                [object]$fileStructure,
                [string]$dbOwner
            )

            if ($null -eq $server.Logins.Item($dbOwner)) {
                try {
                    $dbOwner = ($destServer.logins | Where-Object { $_.id -eq 1 }).Name
                }
                catch {
                    $dbOwner = "sa"
                }
            }
            try {
                $null = $server.AttachDatabase($dbName, $fileStructure, $dbOwner, [Microsoft.SqlServer.Management.Smo.AttachOptions]::None)
                return $true
            }
            catch {
                Stop-Function -Message "Issue mounting database." -ErrorRecord $_
                return $false
            }
        }

        function Start-SqlFileTransfer {
            <#

            SYNOPSIS
            Internal function. Uses BITS to transfer detached files (.mdf, .ndf, .ldf, and filegroups) to
            another server over admin UNC paths. Locations of data files are kept in the
            custom object generated by Get-SqlFileStructure

            #>
            param (
                [object]$fileStructure,
                [string]$dbName
            )

            $copydb = $fileStructure.databases[$dbName]
            $dbsource = $copydb.source
            $dbdestination = $copydb.destination

            foreach ($file in $dbsource.keys) {
                $remotefilename = $dbdestination[$file].remotefilename
                $from = $dbsource[$file].remotefilename
                try {
                    if (Test-Path $from -pathtype container) {
                        $null = New-Item -ItemType Directory -Path $remotefilename -Force
                        Start-BitsTransfer -Source "$from\*.*" -Destination $remotefilename

                        $directories = (Get-ChildItem -recurse $from | Where-Object { $_.PsIsContainer }).FullName
                        foreach ($directory in $directories) {
                            $newdirectory = $directory.replace($from, $remotefilename)
                            $null = New-Item -ItemType Directory -Path $newdirectory -Force
                            Start-BitsTransfer -Source "$directory\*.*" -Destination $newdirectory
                        }
                    }
                    else {
                        Write-Message -Level Verbose -Message "Copying $from for $dbName."
                        Start-BitsTransfer -Source $from -Destination $remotefilename
                    }
                }
                catch {
                    try {
                        # Sometimes BITS trips out temporarily on cloned drives.
                        Start-BitsTransfer -Source $from -Destination $remotefilename
                    }
                    catch {
                        Write-Message -Level Verbose -Message "Start-BitsTransfer did not succeed. Now attempting with Copy-Item - no progress bar will be shown."
                        try {
                            Copy-Item -Path $from -Destination $remotefilename -ErrorAction Stop
                        }
                        catch {
                            Write-Message -Level Verbose -Message "Access denied. This can happen for a number of reasons including issues with cloned disks."
                            Stop-Function -Message "Alternatively, you may need to run PowerShell as Administrator, especially when running on localhost." -Target $from -ErrorRecord $_
                            return
                        }
                    }
                }
            }
            return $true
        }

        function Start-SqlDetachAttach {
            <#

            .SYNOPSIS
            Internal function. Performs checks, then executes Dismount-SqlDatabase on a database, copies its files to the new server,    then performs Mount-SqlDatabase. $sourceServer and $destServer are SMO server objects.

            $fileStructure is a custom object generated by Get-SqlFileStructure

            #>
            [CmdletBinding()]
            param (
                [object]$sourceServer,
                [object]$destServer,
                [object]$fileStructure,
                [string]$dbName
            )

            $destfilestructure = New-Object System.Collections.Specialized.StringCollection
            $sourceFileStructure = New-Object System.Collections.Specialized.StringCollection
            $dbOwner = $sourceServer.databases[$dbName].owner

            if ($null -eq $dbOwner) {
                try {
                    $dbOwner = ($destServer.logins | Where-Object { $_.id -eq 1 }).Name
                }
                catch {
                    $dbOwner = "sa"
                }
            }

            foreach ($file in $fileStructure.databases[$dbName].destination.values) { $null = $destfilestructure.add($file.physical) }
            foreach ($file in $fileStructure.databases[$dbName].source.values) { $null = $sourceFileStructure.add($file.physical) }

            $detachresult = Dismount-SqlDatabase $sourceServer $dbName

            if ($detachresult) {
                $transfer = Start-SqlFileTransfer $fileStructure $dbName
                if ($transfer -eq $false) { Write-Warning "Could not copy files."; return "Could not copy files." }
                $attachresult = Mount-SqlDatabase $destServer $dbName $destfilestructure $dbOwner

                if ($attachresult -eq $true) {
                    # add to added dbs because ATTACH was successful
                    Write-Message -Level Verbose -Message "Successfully attached $dbName to $destinstance."
                    return $true
                }
                else {
                    # add to failed because ATTACH was unsuccessful
                    Write-Message -Level Verbose -Message "Could not attach $dbName."
                    return "Could not attach database."
                }
            }
            else {
                # add to failed because DETACH was unsuccessful
                Write-Message -Level Verbose -Message "Could not detach $dbName."
                return "Could not detach database."
            }
        }
        $backupCollection = @()
    }
    process {
        if (Test-FunctionInterrupt) { return }
        
        # testing twice for whatif reasons
        if ($BackupRestore -and (-not $NetworkShare -and -not $UseLastBackups)) {
            Stop-Function -Message "When using -BackupRestore, you must specify -NetworkShare or -UseLastBackups"
            return
        }
        if ($NetworkShare -and $UseLastBackups) {
            Stop-Function -Message "-NetworkShare cannot be used with -UseLastBackups because the backup path is determined by the paths in the last backups"
            return
        }
        if ($DetachAttach -and -not $Reattach -and $Destination.Count -gt 1) {
            Stop-Function -Message "When using -DetachAttach with multiple servers, you must specify -Reattach to reattach database at source"
            return
        }
        if (($AllDatabases -or $IncludeSupportDbs -or $Database) -and !$DetachAttach -and !$BackupRestore) {
            Stop-Function -Message "You must specify -DetachAttach or -BackupRestore when migrating databases."
            return
        }

        if (-not $AllDatabases -and -not $IncludeSupportDbs -and -not $Database -and -not $InputObject) {
            Stop-Function -Message "You must specify a -AllDatabases or -Database to continue."
            return
        }

        if ($InputObject) {
            $Source = $InputObject[0].Parent
            $Database = $InputObject.Name
        }


        if ($Database -contains "master" -or $Database -contains "msdb" -or $Database -contains "tempdb") {
            Stop-Function -Message "Migrating system databases is not currently supported." -Continue
        }

        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }

        Invoke-SmoCheck -SqlInstance $sourceServer
        $sourceNetBios = $sourceServer.ComputerName

        Write-Message -Level Verbose -Message "Ensuring user databases exist (counting databases)."
        $dbTotal = $sourceServer.Databases.Count

        if ($dbTotal -le 4) {
            Stop-Function -Message "No user databases to migrate. Quitting."
            return
        }

        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }

            if ($sourceServer.ComputerName -eq $destServer.ComputerName) {
                $script:sameserver = $true
            }
            else {
                $script:sameserver = $false
            }

            if ($script:sameserver -and $DetachAttach) {
                if (-not (Test-ElevationRequirement -ComputerName $sourceServer)) { return }
            }

            $destVersionLower = $destServer.VersionMajor -lt $sourceServer.VersionMajor
            $destVersionMinorLow = ($destServer.VersionMajor -eq 10 -and $sourceServer.VersionMajor -eq 10) -and ($destServer.VersionMinor -lt $sourceServer.VersionMinor)

            if ($destVersionLower -or $destVersionMinorLow) {
                Stop-Function -Message "Error: copy database cannot be made from newer $($sourceServer.VersionString) to older $($destServer.VersionString) SQL Server version."
                return
            }

            if ($DetachAttach) {
                if ($sourceServer.ComputerName -eq $env:COMPUTERNAME -or $destServer.ComputerName -eq $env:COMPUTERNAME) {
                    if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
                        Write-Message -Level Verbose -Message "When running DetachAttach locally on the console, it's possible you'll need to Run As Administrator. Trying anyway."
                    }
                }
            }

            if ($NetworkShare) {
                if ($(Test-DbaSqlPath -SqlInstance $sourceServer -Path $NetworkShare) -eq $false) {
                    Write-Message -Level Verbose -Message "$Source may not be able to access $NetworkShare. Trying anyway."
                }

                if ($(Test-DbaSqlPath -SqlInstance $destServer -Path $NetworkShare) -eq $false) {
                    Write-Message -Level Verbose -Message "$destinstance may not be able to access $NetworkShare. Trying anyway."
                }

                if ($NetworkShare.StartsWith('\\')) {
                    try {
                        $shareServer = ($NetworkShare -split "\\")[2]
                        $hostEntry = ([Net.Dns]::GetHostEntry($shareServer)).HostName -split "\."

                        if ($shareServer -ne $hostEntry[0]) {
                            Write-Message -Level Verbose -Message "Using CNAME records for the network share may present an issue if an SPN has not been created. Trying anyway. If it doesn't work, use a different (A record) hostname."
                        }
                    }
                    catch {
                        Stop-Function -Message "Error validating unc path: $_"
                        return
                    }
                }
            }

            $destNetBios = $destserver.ComputerName

            Write-Message -Level Verbose -Message "Performing SMO version check."
            Invoke-SmoCheck -SqlInstance $destServer

            Write-Message -Level Verbose -Message "Checking to ensure the source isn't the same as the destination."
            if ($source -eq $destinstance) {
                Stop-Function -Message "Source and Destination SQL Servers instances are the same. Quitting." -Continue
            }

            if ($NetworkShare.Length -gt 0) {
                Write-Message -Level Verbose -Message "Checking to ensure network path is valid."
                if (!($NetworkShare.StartsWith("\\")) -and !$script:sameserver) {
                    Stop-Function -Message "Network share must be a valid UNC path (\\server\share)." -Continue
                }

                if (-not $script:sameserver) {
                    try {
                        if ((Test-Path $NetworkShare -ErrorAction Stop)) {
                            Write-Message -Level Verbose -Message "$NetworkShare share can be accessed."
                        }
                    }
                    catch {
                        Write-Message -Level Verbose -Message "$NetworkShare share cannot be accessed. Still trying anyway, in case the SQL Server service accounts have access."
                    }
                }
            }

            Write-Message -Level Verbose -Message "Checking to ensure server is not SQL Server 7 or below."
            if ($sourceServer.VersionMajor -lt 8 -and $destServer.VersionMajor -lt 8) {
                Stop-Function -Message "This script can only be run on SQL Server 2000 and above. Quitting." -Continue
            }

            Write-Message -Level Verbose -Message "Checking to ensure detach/attach is not attempted on SQL Server 2000."
            if ($destServer.VersionMajor -lt 9 -and $DetachAttach) {
                Stop-Function -Message "Detach/Attach not supported when destination SQL Server is version 2000. Quitting." -Target $destServer -Continue
            }

            Write-Message -Level Verbose -Message "Checking to ensure SQL Server 2000 migration isn't directly attempted to SQL Server 2012."
            if ($sourceServer.VersionMajor -lt 9 -and $destServer.VersionMajor -gt 10) {
                Stop-Function -Message "SQL Server 2000 databases cannot be migrated to SQL Server versions 2012 and above. Quitting." -Target $destServer -Continue
            }

            Write-Message -Level Verbose -Message "Warning if migration from 2005 to 2012 and above and attach/detach is used."
            if ($sourceServer.VersionMajor -eq 9 -and $destServer.VersionMajor -gt 9 -and !$BackupRestore -and !$Force -and $DetachAttach) {
                Stop-Function -Message "Backup and restore is the safest method for migrating from SQL Server 2005 to other SQL Server versions. Please use the -BackupRestore switch or override this requirement by specifying -Force." -Continue
            }

            if ($sourceServer.Collation -ne $destServer.Collation) {
                Write-Message -Level Verbose -Message "Warning on different collation."
                Write-Message -Level Verbose -Message "Collation on $Source, $($sourceServer.Collation) differs from the $destinstance, $($destServer.Collation)."
            }

            Write-Message -Level Verbose -Message "Ensuring destination server version is equal to or greater than source."
            if ($sourceServer.VersionMajor -ge $destServer.VersionMajor) {
                if ($sourceServer.VersionMinor -gt $destServer.VersionMinor) {
                    Stop-Function -Message "Source SQL Server version build must be <= destination SQL Server for database migration." -Continue
                }
            }

            # SMO's filestreamlevel is sometimes null
            $sql = "select coalesce(SERVERPROPERTY('FilestreamConfiguredLevel'),0) as fs"
            $sourceFilestream = $sourceServer.ConnectionContext.ExecuteScalar($sql)
            $destFilestream = $destServer.ConnectionContext.ExecuteScalar($sql)
            if ($sourceFilestream -gt 0 -and $destFilestream -eq 0) {
                $fsWarning = $true
            }

            Write-Message -Level Verbose -Message "Writing warning about filestream being enabled."
            if ($fsWarning) {
                Write-Message -Level Verbose -Message "FILESTREAM enabled on $source but not $destinstance. Databases that use FILESTREAM will be skipped."
            }

            if ($DetachAttach -eq $true) {
                Write-Message -Level Verbose -Message "Checking access to remote directories."
                $remoteSourcePath = Join-AdminUNC $sourceNetBios (Get-SqlDefaultPaths -SqlInstance $sourceServer -filetype data)

                if ((Test-Path $remoteSourcePath) -ne $true -and $DetachAttach) {
                    Write-Message -Level Warning -Message "Can't access remote Sql directories on $source which is required to perform detach/copy/attach."
                    Write-Message -Level Warning -Message "You can manually try accessing $remoteSourcePath to diagnose any issues."
                    Stop-Function -Message "Halting database migration"
                    return
                }

                $remoteDestPath = Join-AdminUNC $destNetBios (Get-SqlDefaultPaths -SqlInstance $destServer -filetype data)
                If ((Test-Path $remoteDestPath) -ne $true -and $DetachAttach) {
                    Write-Message -Level Warning -Message "Can't access remote Sql directories on $destinstance which is required to perform detach/copy/attach."
                    Write-Message -Level Warning -Message "You can manually try accessing $remoteDestPath to diagnose any issues."
                    Stop-Function -Message "Halting database migration" -Continue
                }
            }

            if (($Database -or $ExcludeDatabase -or $IncludeSupportDbs) -and (!$DetachAttach -and !$BackupRestore)) {
                Stop-Function -Message "You did not select a migration method. Please use -BackupRestore or -DetachAttach."
                return
            }

            if ((!$Database -and !$AllDatabases -and !$IncludeSupportDbs) -and ($DetachAttach -or $BackupRestore)) {
                Stop-Function -Message "You did not select any databases to migrate. Please use -AllDatabases or -Database or -IncludeSupportDbs."
                return
            }

            Write-Message -Level Verbose -Message "Building database list."
            $databaseList = New-Object System.Collections.ArrayList
            $SupportDBs = "ReportServer", "ReportServerTempDB", "distribution"
            foreach ($currentdb in ($sourceServer.Databases | Where-Object IsAccessible)) {
                $dbName = $currentdb.Name
                $dbOwner = $currentdb.Owner

                if ($currentdb.Id -le 4) { continue }
                if ($Database -and $Database -notcontains $dbName) { continue }
                if ($IncludeSupportDBs -eq $false -and $SupportDBs -contains $dbName) { continue }
                if ($IncludeSupportDBs -eq $true -and $SupportDBs -notcontains $dbName) {
                    if ($AllDatabases -eq $false -and $Database.length -eq 0) { continue }
                }
                $null = $databaseList.Add($currentdb)
            }

            Write-Message -Level Verbose -Message "Performing count."
            $dbCount = $databaseList.Count

            Write-Message -Level Verbose -Message "Building file structure inventory for $dbCount databases."

            if ($sourceServer.VersionMajor -eq 8) {
                $sql = "select DB_NAME (dbid) as dbname, name, filename, CASE WHEN groupid = 0 THEN 'LOG' ELSE 'ROWS' END as filetype from sysaltfiles"
            }
            else {
                $sql = "SELECT db.Name AS dbname, type_desc AS FileType, mf.Name, Physical_Name AS filename FROM sys.master_files mf INNER JOIN  sys.databases db ON db.database_id = mf.database_id"
            }

            $dbFileTable = $sourceServer.Databases['master'].ExecuteWithResults($sql)

            if ($destServer.VersionMajor -eq 8) {
                $sql = "select DB_NAME (dbid) as dbname, name, filename, CASE WHEN groupid = 0 THEN 'LOG' ELSE 'ROWS' END as filetype from sysaltfiles"
            }
            else {
                $sql = "SELECT db.Name AS dbname, type_desc AS FileType, mf.Name, Physical_Name AS filename FROM sys.master_files mf INNER JOIN  sys.databases db ON db.database_id = mf.database_id"
            }

            $remoteDbFileTable = $destServer.Databases['master'].ExecuteWithResults($sql)

            $fileStructure = Get-SqlFileStructure -sourceserver $sourceServer -destserver $destServer -databaselist $databaseList -ReuseSourceFolderStructure $ReuseSourceFolderStructure

            $elapsed = [System.Diagnostics.Stopwatch]::StartNew()
            $started = Get-Date
            $script:TimeNow = (Get-Date -UFormat "%m%d%Y%H%M%S")

            if ($AllDatabases -or $ExcludeDatabase.length -gt 0 -or $IncludeSupportDbs -or $Database.length -gt 0) {
                foreach ($currentdb in $databaseList) {
                    $dbName = $currentdb.Name
                    $dbOwner = $currentdb.Owner

                    $copyDatabaseStatus = [pscustomobject]@{
                        SourceServer = $sourceServer.Name
                        DestinationServer = $destServer.Name
                        Name         = $dbName
                        DestinationDatabase = $dbname
                        Type         = "Database"
                        Status       = $null
                        Notes        = $null
                        DateTime     = [DbaDateTime](Get-Date)
                    }

                    Write-Message -Level Verbose -Message "`n######### Database: $dbName #########"
                    $dbStart = Get-Date

                    if ($ExcludeDatabase -contains $dbName) {
                        Write-Message -Level Verbose -Message "$dbName excluded. Skipping."
                        continue
                    }

                    Write-Message -Level Verbose -Message "Checking for accessibility."
                    if ($currentdb.IsAccessible -eq $false) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Skipping $dbName. Database is inaccessible.")) {
                            Write-Message -Level Verbose -Message "Skipping $dbName. Database is inaccessible."
                            
                            $copyDatabaseStatus.Status = "Skipped"
                            $copyDatabaseStatus.Notes = "Database is not accessible"
                            $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        }
                        continue
                    }

                    if ($fsWarning) {
                        $fsRows = $dbFileTable.Tables[0].Select("dbname = '$dbName' and FileType = 'FileStream'")
                        
                        if ($fsRows.Count -gt 0) {
                            if ($Pscmdlet.ShouldProcess($destinstance, "Skipping $dbName (contains FILESTREAM).")) {
                                Write-Message -Level Verbose -Message "Skipping $dbName (contains FILESTREAM)."
                                $copyDatabaseStatus.Status = "Skipped"
                                $copyDatabaseStatus.Notes = "Contains FILESTREAM"
                                $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            }
                            continue
                        }
                    }

                    if ($ReuseSourceFolderStructure) {
                        $fgRows = $dbFileTable.Tables[0].Select("dbname = '$dbName' and FileType = 'ROWS'")[0]
                        $remotePath = Split-Path $fgRows.Filename
                        
                        if (!(Test-DbaSqlPath -SqlInstance $destServer -Path $remotePath)) {
                            if ($Pscmdlet.ShouldProcess($destinstance, "$remotePath does not exist on $destinstance and ReuseSourceFolderStructure was specified")) {
                                # Stop-Function -Message "Cannot resolve $remotePath on $source. `n`nYou have specified ReuseSourceFolderStructure and exact folder structure does not exist. Halting script."
                                $copyDatabaseStatus.Status = "Failed"
                                $copyDatabaseStatus.Notes = "$remotePath does not exist on $destinstance and ReuseSourceFolderStructure was specified" #"Can't resolve $remotePath"
                                $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            }
                            continue
                        }
                    }

                    Write-Message -Level Verbose -Message "Checking Availability Group status."
                    if ($currentdb.AvailabilityGroupName.Length -gt 0 -and !$force -and $DetachAttach) {
                        $agName = $currentdb.AvailabilityGroupName
                        Write-Message -Level Verbose -Message "Database is part of an Availability Group ($agName). Use -Force to drop from $agName and migrate. Alternatively, you can use the safer backup/restore method."
                        continue
                    }

                    $dbStatus = $currentdb.Status.ToString()
                    
                    if ($dbStatus.StartsWith("Normal") -eq $false) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "$dbName is not in a Normal state. Skipping.")) {
                            Write-Message -Level Verbose -Message "$dbName is not in a Normal state. Skipping."
                            
                            $copyDatabaseStatus.Status = "Skipped"
                            $copyDatabaseStatus.Notes = "Not in normal state"
                            $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        }
                        continue
                    }
                    
                    if ($currentdb.ReplicationOptions -ne "None" -and $DetachAttach -eq $true) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "$dbName is part of replication. Skipping.")) {
                            Write-Message -Level Verbose -Message "$dbName is part of replication. Skipping."
                            
                            $copyDatabaseStatus.Status = "Skipped"
                            $copyDatabaseStatus.Notes = "Part of replication"
                            $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        }
                        continue
                    }
                    
                    if ($currentdb.IsMirroringEnabled -and !$force -and $DetachAttach) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Database is being mirrored. Use -Force to break mirror and migrate. Alternatively, you can use the safer backup/restore method.")) {
                            Write-Message -Level Verbose -Message "Database is being mirrored. Use -Force to break mirror and migrate. Alternatively, you can use the safer backup/restore method."
                            
                            $copyDatabaseStatus.Status = "Skipped"
                            $copyDatabaseStatus.Notes = "Database is mirrored"
                            $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        }
                        
                        continue
                    }
                    
                    if (($null -ne $destServer.Databases[$dbName]) -and !$force -and !$WithReplace) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "$dbname exists at destination. Use -Force to drop and migrate. Aborting routine for this database.")) {
                            Write-Message -Level Verbose -Message "$dbname exists at destination. Use -Force to drop and migrate. Aborting routine for this database."
                            
                            $copyDatabaseStatus.Status = "Skipped"
                            $copyDatabaseStatus.Notes = "Already exists"
                            $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        }
                        continue
                    }
                    elseif ($null -ne $destServer.Databases[$dbName] -and $force) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "DROP DATABASE $dbName")) {
                            Write-Message -Level Verbose -Message "$dbName already exists. -Force was specified. Dropping $dbName on $destinstance."
                            $removeresult = Remove-DbaDatabase -SqlInstance $destserver -Database $dbname -Confirm:$false
                            $dropResult = $removeresult.Status -eq 'Dropped'

                            if ($dropResult -eq $false) {
                                Write-Message -Level Verbose -Message "Database could not be dropped. Aborting routine for this database."

                                $copyDatabaseStatus.Status = "Failed"
                                $copyDatabaseStatus.Notes = "Could not drop database"
                                $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                continue
                            }
                        }
                    }

                    if ($force) {
                        $WithReplace = $true
                    }

                    Write-Message -Level Verbose -Message "Started: $dbStart."

                    if ($sourceServer.VersionMajor -ge 9) {
                        $sourceDbOwnerChaining = $sourceServer.Databases[$dbName].DatabaseOwnershipChaining
                        $sourceDbTrustworthy = $sourceServer.Databases[$dbName].Trustworthy
                        $sourceDbBrokerEnabled = $sourceServer.Databases[$dbName].BrokerEnabled
                    }

                    $sourceDbReadOnly = $sourceServer.Databases[$dbName].ReadOnly

                    if ($SetSourceReadOnly) {
                        If ($Pscmdlet.ShouldProcess($source, "Set $dbName to read-only")) {
                            Write-Message -Level Verbose -Message "Setting database to read-only."
                            $result = Update-SqldbReadOnly -SqlInstance $sourceServer -dbname $dbName -readonly:$true

                            if ($result -eq $false) {
                                Write-Message -Level Verbose -Message "Couldn't set database to read-only. Aborting routine for this database."
                                continue
                            }
                        }
                    }

                    if ($BackupRestore) {
                        if ($UseLastBackups) {
                            $whatifmsg = "Gathering last backup information for $dbName from $Source and restoring"
                        }
                        else {
                            $whatifmsg = "Backup $dbName from $source and restoring"
                        }
                        If ($Pscmdlet.ShouldProcess($destinstance, $whatifmsg)) {
                            if ($UseLastBackups) {
                                $backupTmpResult = Get-DbaBackupHistory -SqlInstance $sourceServer -Database $dbName -IncludeCopyOnly -Last
                                if (-not $backupTmpResult) {
                                    $copyDatabaseStatus.Type = "Database (BackupRestore)"
                                    $copyDatabaseStatus.Status = "Failed"
                                    $copyDatabaseStatus.Notes = "No backups for $dbName on $source"
                                    $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                    continue
                                }
                            }
                            else {
                                $backupTmpResult = $backupCollection | Where-Object Database -eq $dbName
                                if (-not $backupTmpResult) {
                                    $backupTmpResult = Backup-DbaDatabase -SqlInstance $sourceServer -Database $dbName -BackupDirectory $NetworkShare -FileCount $numberfiles -CopyOnly:$CopyOnly
                                }
                                if ($backupTmpResult) {
                                    $backupCollection += $backupTmpResult
                                }
                                $backupResult = $BackupTmpResult.BackupComplete
                                if (-not $backupResult) {
                                    $serviceAccount = $sourceServer.ServiceAccount
                                    Write-Message -Level Verbose -Message "Backup Failed. Does SQL Server account $serviceAccount have access to $($NetworkShare)? Aborting routine for this database."

                                    $copyDatabaseStatus.Status = "Failed"
                                    $copyDatabaseStatus.Notes = "Backup failed. Verify service account access to $NetworkShare."
                                    $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                    continue
                                }
                            }
                            Write-Message -Level Verbose -Message "Reuse = $ReuseSourceFolderStructure."
                            try {
                                $msg = $null
                                $restoreResultTmp = $backupTmpResult | Restore-DbaDatabase -SqlInstance $destServer -DatabaseName $dbName -ReuseSourceFolderStructure:$ReuseSourceFolderStructure -NoRecovery:$NoRecovery -TrustDbBackupHistory -WithReplace:$WithReplace -EnableException
                            }
                            catch {
                                $msg = $_.Exception.InnerException.InnerException.InnerException.InnerException.Message
                                Stop-Function -Message "Failure attempting to restore $dbName to $destinstance" -Exception $_.Exception.InnerException.InnerException.InnerException.InnerException
                            }
                            $restoreResult = $restoreResultTmp.RestoreComplete

                            if ($restoreResult -eq $true) {
                                Write-Message -Level Verbose -Message "Successfully restored $dbName to $destinstance."
                                $copyDatabaseStatus.Status = "Successful"
                                $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            }
                            else {
                                if ($ReuseSourceFolderStructure) {
                                    Write-Message -Level Verbose -Message "Failed to restore $dbName to $destinstance. You specified -ReuseSourceFolderStructure. Does the exact same destination directory structure exist?"
                                    Write-Message -Level Verbose -Message "Aborting routine for this database."

                                    $copyDatabaseStatus.Status = "Failed"
                                    $copyDatabaseStatus.Notes = "Failed to restore. ReuseSourceFolderStructure was specified, verify same directory structure exist on destination."
                                    $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                    continue
                                }
                                else {
                                    Write-Message -Level Verbose -Message "Failed to restore $dbName to $destinstance. Aborting routine for this database."

                                    $copyDatabaseStatus.Status = "Failed"
                                    if (-not $msg) {
                                        $msg = "Failed to restore database"
                                    }
                                    $copyDatabaseStatus.Notes = $msg
                                    $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                    continue
                                }
                            }
                            if (-not $NoBackupCleanUp -and $Destination.Count -eq 1) {
                                foreach ($backupFile in ($backupTmpResult.BackupPath)) {
                                    try {
                                        if (Test-Path $backupFile -ErrorAction Stop) {
                                            Write-Message -Level Verbose -Message "Deleting $backupFile."
                                            Remove-Item $backupFile -ErrorAction Stop
                                        }
                                    }
                                    catch {
                                        try {
                                            Write-Message -Level Verbose -Message "Trying alternate SQL method to delete $backupFile."
                                            $sql = "EXEC master.sys.xp_delete_file 0, '$backupFile'"
                                            Write-Message -Level Debug -Message $sql
                                            $null = $sourceServer.Query($sql)
                                        }
                                        catch {
                                            Write-Message -Level Verbose -Message "Cannot delete backup file $backupFile."

                                            # Set NoBackupCleanup so that there's a warning at the end
                                            $NoBackupCleanup = $true
                                        }
                                    }
                                }
                            }
                        }

                        $dbFinish = Get-Date
                        if ($NoRecovery -eq $false) {
                            # needed because the newly restored database doesn't show up
                            $destServer.Databases.Refresh()
                            $dbOwner = $sourceServer.Databases[$dbName].Owner
                            if ($null -eq $dbOwner -or $destServer.Logins.Name -notcontains $dbOwner) {
                                $dbOwner = Get-SaLoginName -SqlInstance $destServer
                            }
                            Write-Message -Level Verbose -Message "Updating database owner to $dbOwner."
                            $OwnerResult = Set-DbaDatabaseOwner -SqlInstance $destServer -Database $dbName -TargetLogin $dbOwner -EnableException
                            if ($OwnerResult.Length -eq 0) {
                                Write-Message -Level Verbose -Message "Failed to update database owner."
                            }
                        }
                    }

                    if ($DetachAttach) {
                        $copyDatabaseStatus.Type = "Database (DetachAttach)"

                        $sourceFileStructure = New-Object System.Collections.Specialized.StringCollection
                        foreach ($file in $fileStructure.Databases[$dbName].Source.Values) {
                            $null = $sourceFileStructure.Add($file.Physical)
                        }

                        $dbOwner = $sourceServer.Databases[$dbName].Owner

                        if ($null -eq $dbOwner -or $destServer.Logins.Name -notcontains $dbOwner) {
                            $dbOwner = Get-SaLoginName -SqlInstance $destServer
                        }

                        if ($Pscmdlet.ShouldProcess($destinstance, "Detach $dbName from $source and attach, then update dbowner")) {
                            $migrationResult = Start-SqlDetachAttach $sourceServer $destServer $fileStructure $dbName

                            $dbFinish = Get-Date

                            if ($reattach -eq $true) {
                                $sourceServer.Databases.Refresh()
                                $destServer.Databases.Refresh()
                                $result = Mount-SqlDatabase $sourceServer $dbName $sourceFileStructure $dbOwner

                                if ($result -eq $true) {
                                    $sourceServer.Databases[$dbName].DatabaseOwnershipChaining = $sourceDbOwnerChaining
                                    $sourceServer.Databases[$dbName].Trustworthy = $sourceDbTrustworthy
                                    $sourceServer.Databases[$dbName].BrokerEnabled = $sourceDbBrokerEnabled
                                    $sourceServer.Databases[$dbName].Alter()

                                    if ($SetSourceReadOnly) {
                                        $null = Update-SqldbReadOnly -SqlInstance $sourceServer -dbname $dbName -readonly $true
                                    }
                                    else {
                                        $null = Update-SqldbReadOnly -SqlInstance $sourceServer -dbname $dbName -readonly $sourceDbReadOnly
                                    }

                                    Write-Message -Level Verbose -Message "Successfully reattached $dbName to $source."
                                }
                                else {
                                    Write-Message -Level Verbose -Message "Could not reattach $dbName to $source."
                                    $copyDatabaseStatus.Status = "Failed"
                                    $copyDatabaseStatus.Notes = "Could not reattach database to $source"
                                    $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                }
                            }

                            if ($migrationResult -eq $true) {
                                Write-Message -Level Verbose -Message "Successfully attached $dbName to $destinstance."
                                $copyDatabaseStatus.Status = "Successful"
                                $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            }
                            else {
                                Write-Message -Level Verbose -Message "Failed to attach $dbName to $destinstance. Aborting routine for this database."

                                $copyDatabaseStatus.Status = "Failed"
                                $copyDatabaseStatus.Notes = "Failed to attach database to destination"
                                $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject

                                continue
                            }
                        }
                    }
                    $destServer.Databases.Refresh()

                    # restore potentially lost settings
                    if ($destServer.VersionMajor -ge 9 -and $NoRecovery -eq $false) {
                        if ($sourceDbOwnerChaining -ne $destServer.Databases[$dbName].DatabaseOwnershipChaining) {
                            if ($Pscmdlet.ShouldProcess($destinstance, "Updating DatabaseOwnershipChaining on $dbName")) {
                                try {
                                    $destServer.Databases[$dbName].DatabaseOwnershipChaining = $sourceDbOwnerChaining
                                    $destServer.Databases[$dbName].Alter()
                                    Write-Message -Level Verbose -Message "Successfully updated DatabaseOwnershipChaining for $sourceDbOwnerChaining on $dbName on $destinstance."
                                }
                                catch {
                                    $copyDatabaseStatus.Status = "Successful - failed to apply DatabaseOwnershipChaining."
                                    $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                    Stop-Function -Message "Failed to update DatabaseOwnershipChaining for $sourceDbOwnerChaining on $dbName on $destinstance." -Target $destinstance -ErrorRecord $_ -Continue
                                }
                            }
                        }

                        if ($sourceDbTrustworthy -ne $destServer.Databases[$dbName].Trustworthy) {
                            if ($Pscmdlet.ShouldProcess($destinstance, "Updating Trustworthy on $dbName")) {
                                try {
                                    $destServer.Databases[$dbName].Trustworthy = $sourceDbTrustworthy
                                    $destServer.Databases[$dbName].Alter()
                                    Write-Message -Level Verbose -Message "Successfully updated Trustworthy to $sourceDbTrustworthy for $dbName on $destinstance"
                                }
                                catch {
                                    $copyDatabaseStatus.Status = "Successful - failed to apply Trustworthy"
                                    $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                    Stop-Function -Message "Failed to update Trustworthy to $sourceDbTrustworthy for $dbName on $destinstance." -Target $destinstance -ErrorRecord $_ -Continue
                                }
                            }
                        }

                        if ($sourceDbBrokerEnabled -ne $destServer.Databases[$dbName].BrokerEnabled) {
                            if ($Pscmdlet.ShouldProcess($destinstance, "Updating BrokerEnabled on $dbName")) {
                                try {
                                    $destServer.Databases[$dbName].BrokerEnabled = $sourceDbBrokerEnabled
                                    $destServer.Databases[$dbName].Alter()
                                    Write-Message -Level Verbose -Message "Successfully updated BrokerEnabled to $sourceDbBrokerEnabled for $dbName on $destinstance."
                                }
                                catch {
                                    $copyDatabaseStatus.Status = "Successful - failed to apply BrokerEnabled"
                                    $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                    Stop-Function -Message "Failed to update BrokerEnabled to $sourceDbBrokerEnabled for $dbName on $destinstance." -Target $destinstance -ErrorRecord $_ -Continue
                                }
                            }
                        }
                    }

                    if ($sourceDbReadOnly -ne $destServer.Databases[$dbName].ReadOnly -and $NoRecovery -eq $false) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Updating ReadOnly status on $dbName")) {
                            $update = Update-SqldbReadOnly -SqlInstance $destServer -dbname $dbName -readonly $sourceDbReadOnly
                            if ($update -eq $true) {
                                Write-Message -Level Verbose -Message "Successfully updated readonly status on $dbName."
                            }
                            else {
                                $copyDatabaseStatus.Status = "Successful - failed to apply ReadOnly."
                                $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                Stop-Function -Message "Failed to update ReadOnly status on $dbName." -Target $destinstance -ErrorRecord $_ -Continue
                            }
                        }
                    }

                    if ($SetSourceOffline -and $sourceServer.databases[$dbName].status -notlike '*offline*') {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Setting $dbName offline on $source")) {
                            Stop-DbaProcess -SqlInstance $sourceServer -Database $dbName
                            Set-DbaDatabaseState -SqlInstance $sourceServer -SqlCredential $SourceSqlCredential -database $dbName -Offline
                        }
                    }

                    $dbTotalTime = $dbFinish - $dbStart
                    $dbTotalTime = ($dbTotalTime.ToString().Split(".")[0])

                    Write-Message -Level Verbose -Message "Finished: $dbFinish."
                    Write-Message -Level Verbose -Message "Elapsed time: $dbTotalTime."

                } # end db by db processing
            }
        }
    }
    end {
        if (Test-FunctionInterrupt) { return }
        if (-not $NoBackupCleanUp -and $Destination.Count -gt 1) {
            foreach ($backupFile in ($backupCollection.BackupPath)) {
                try {
                    if (Test-Path $backupFile -ErrorAction Stop) {
                        Write-Message -Level Verbose -Message "Deleting $backupFile."
                        Remove-Item $backupFile -ErrorAction Stop
                    }
                }
                catch {
                    try {
                        Write-Message -Level Verbose -Message "Trying alternate SQL method to delete $backupFile."
                        $sql = "EXEC master.sys.xp_delete_file 0, '$backupFile'"
                        Write-Message -Level Debug -Message $sql
                        $null = $sourceServer.Query($sql)
                    }
                    catch {
                        Write-Message -Level Verbose -Message "Cannot delete backup file $backupFile."
                    }
                }
            }
        }
        if (Test-FunctionInterrupt) { return }
        if ($null -ne $elapsed) {
            $totalTime = ($elapsed.Elapsed.toString().Split(".")[0])

            Write-Message -Level Verbose -Message "`nDatabase migration finished"
            Write-Message -Level Verbose -Message "Migration started: $started"
            Write-Message -Level Verbose -Message "Migration completed: $(Get-Date)"
            Write-Message -Level Verbose -Message "Total Elapsed time: $totalTime"

            if ($NetworkShare.length -gt 0 -and $NoBackupCleanup) {
                Write-Message -Level Verbose -Message "Backups still exist at $NetworkShare."
            }
        }
        else {
            Write-Message -Level Verbose -Message "No work was done, as we stopped during setup phase"
        }
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlDatabase
    }
}
function Copy-DbaDatabaseAssembly {
    <#
        .SYNOPSIS
            Copy-DbaDatabaseAssembly migrates assemblies from one SQL Server to another.

        .DESCRIPTION
            By default, all assemblies are copied.

            If the assembly already exists on the destination, it will be skipped unless -Force is used.

            This script does not yet copy dependencies or dependent objects.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Assembly
            The assembly(ies) to process. This list is auto-populated from the server. If unspecified, all assemblies will be processed.

        .PARAMETER ExcludeAssembly
            The assembly(ies) to exclude. This list is auto-populated from the server.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER Force
            If this switch is enabled, existing assemblies on Destination with matching names from Source will be dropped.

        .NOTES
            Tags: Migration, Assembly
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            http://dbatools.io/Get-SqlDatabaseAssembly

        .EXAMPLE
            Copy-DbaDatabaseAssembly -Source sqlserver2014a -Destination sqlcluster

            Copies all assemblies from sqlserver2014a to sqlcluster using Windows credentials. If assemblies with the same name exist on sqlcluster, they will be skipped.

        .EXAMPLE
            Copy-DbaDatabaseAssembly -Source sqlserver2014a -Destination sqlcluster -Assembly dbname.assemblyname, dbname3.anotherassembly -SourceSqlCredential $cred -Force

            Copies two assemblies, the dbname.assemblyname and dbname3.anotherassembly from sqlserver2014a to sqlcluster using SQL credentials for sqlserver2014a and Windows credentials for sqlcluster. If an assembly with the same name exists on sqlcluster, it will be dropped and recreated because -Force was used.

            In this example, anotherassembly will be copied to the dbname3 database on the server sqlcluster.

        .EXAMPLE
            Copy-DbaThing -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force

            Shows what would happen if the command were executed using force.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]$SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [object[]]$Assembly,
        [object[]]$ExcludeAssembly,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 9
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        $sourceAssemblies = @()
        foreach ($database in ($sourceServer.Databases | Where-Object IsAccessible)) {
            Write-Message -Level Verbose -Message "Processing $database on source"
            
            try {
                # a bug here requires a try/catch
                $userAssemblies = $database.Assemblies | Where-Object IsSystemObject -eq $false
                foreach ($assembly in $userAssemblies) {
                    $sourceAssemblies += $assembly
                }
            }
            catch { }
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            
            $destAssemblies = @()
            foreach ($database in $destServer.Databases) {
                Write-Message -Level VeryVerbose -Message "Processing $database on destination"
                try {
                    # a bug here requires a try/catch
                    $userAssemblies = $database.Assemblies | Where-Object IsSystemObject -eq $false
                    foreach ($assembly in $userAssemblies) {
                        $destAssemblies += $assembly
                    }
                }
                catch { }
            }
            foreach ($currentAssembly in $sourceAssemblies) {
                $assemblyName = $currentAssembly.Name
                $dbName = $currentAssembly.Parent.Name
                $destDb = $destServer.Databases[$dbName]
                Write-Message -Level VeryVerbose -Message "Processing $assemblyName on $dbname"
                $copyDbAssemblyStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    SourceDatabase = $dbName
                    DestinationServer = $destServer.Name
                    DestinationDatabase = $destDb
                    type         = "Database Assembly"
                    Name         = $assemblyName
                    Status       = $null
                    Notes        = $null
                    DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                }
                
                
                if (!$destDb) {
                    $copyDbAssemblyStatus.Status = "Skipped"
                    $copyDbAssemblyStatus.Notes = "Destination database does not exist"
                    $copyDbAssemblyStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    
                    Write-Message -Level Verbose -Message "Destination database $dbName does not exist. Skipping $assemblyName.";
                    continue
                }
                
                if ((Test-Bound -ParameterName Assembly) -and $Assembly -notcontains "$dbName.$assemblyName" -or $ExcludeAssembly -contains "$dbName.$assemblyName") {
                    continue
                }
                
                if ($currentAssembly.AssemblySecurityLevel -eq "External" -and -not $destDb.Trustworthy) {
                    if ($Pscmdlet.ShouldProcess($destinstance, "Setting $dbName to External")) {
                        Write-Message -Level Verbose -Message "Setting $dbName Security Level to External on $destinstance."
                        $sql = "ALTER DATABASE $dbName SET TRUSTWORTHY ON"
                        try {
                            Write-Message -Level Debug -Message $sql
                            $destServer.Query($sql)
                        }
                        catch {
                            $copyDbAssemblyStatus.Status = "Failed"
                            $copyDbAssemblyStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            
                            Stop-Function -Message "Issue setting security level." -Target $destDb -ErrorRecord $_
                        }
                    }
                }
                
                if ($destServer.Databases[$dbName].Assemblies.Name -contains $currentAssembly.name) {
                    if ($force -eq $false) {
                        $copyDbAssemblyStatus.Status = "Skipped"
                        $copyDbAssemblyStatus.Notes = "Already exists"
                        $copyDbAssemblyStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Write-Message -Level Verbose -Message "Assembly $assemblyName exists at destination in the $dbName database. Use -Force to drop and migrate."
                        continue
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Dropping assembly $assemblyName and recreating")) {
                            try {
                                Write-Message -Level Verbose -Message "Dropping assembly $assemblyName."
                                Write-Message -Level Verbose -Message "This won't work if there are dependencies."
                                $destServer.Databases[$dbName].Assemblies[$assemblyName].Drop()
                                Write-Message -Level Verbose -Message "Copying assembly $assemblyName."
                                $sql = $currentAssembly.Script()
                                Write-Message -Level Debug -Message $sql
                                $destServer.Query($sql, $dbName)
                            }
                            catch {
                                $copyDbAssemblyStatus.Status = "Failed"
                                $copyDbAssemblyStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                
                                Stop-Function -Message "Issue dropping assembly." -Target $assemblyName -ErrorRecord $_ -Continue
                            }
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Creating assembly $assemblyName")) {
                    try {
                        Write-Message -Level Verbose -Message "Copying assembly $assemblyName from database."
                        $sql = $currentAssembly.Script()
                        Write-Message -Level Debug -Message $sql
                        $destServer.Query($sql, $dbName)
                        
                        $copyDbAssemblyStatus.Status = "Successful"
                        $copyDbAssemblyStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                    }
                    catch {
                        $copyDbAssemblyStatus.Status = "Failed"
                        $copyDbAssemblyStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Stop-Function -Message "Issue creating assembly." -Target $assemblyName -ErrorRecord $_
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlDatabaseAssembly
    }
}
function Copy-DbaDatabaseMail {
    <#
    .SYNOPSIS
        Migrates Mail Profiles, Accounts, Mail Servers and Mail Server Configs from one SQL Server to another.

    .DESCRIPTION
        By default, all mail configurations for Profiles, Accounts, Mail Servers and Configs are copied.

    .PARAMETER Source
        Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

    .PARAMETER SourceSqlCredential
        Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

    .PARAMETER Destination
        Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

    .PARAMETER DestinationSqlCredential
        Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

    .PARAMETER Type
        Specifies the object type to migrate. Valid options are "Job", "Alert" and "Operator". When Type is specified, all categories from the selected type will be migrated.

    .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .PARAMETER Force
        If this switch is enabled, existing objects on Destination with matching names from Source will be dropped.

    .NOTES
        Tags: Migration, Mail
        Author: Chrissy LeMaire (@cl), netnerds.net
        Requires: sysadmin access on SQL Servers

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Copy-DbaDatabaseMail

    .EXAMPLE
        Copy-DbaDatabaseMail -Source sqlserver2014a -Destination sqlcluster

        Copies all database mail objects from sqlserver2014a to sqlcluster using Windows credentials. If database mail objects with the same name exist on sqlcluster, they will be skipped.

    .EXAMPLE
        Copy-DbaDatabaseMail -Source sqlserver2014a -Destination sqlcluster -SourceSqlCredential $cred

        Copies all database mail objects from sqlserver2014a to sqlcluster using SQL credentials for sqlserver2014a and Windows credentials for sqlcluster.

    .EXAMPLE
        Copy-DbaDatabaseMail -Source sqlserver2014a -Destination sqlcluster -WhatIf

        Shows what would happen if the command were executed.

    .EXAMPLE
        Copy-DbaDatabaseMail -Source sqlserver2014a -Destination sqlcluster -EnableException

        Performs execution of function, and will throw a terminating exception if something breaks
    #>
    [cmdletbinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [Parameter(ParameterSetName = 'SpecificTypes')]
        [ValidateSet('ConfigurationValues', 'Profiles', 'Accounts', 'mailServers')]
        [string[]]$Type,
        [PSCredential]$SourceSqlCredential,
        [PSCredential]$DestinationSqlCredential,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        function Copy-DbaDatabaseMailConfig {
            [cmdletbinding(SupportsShouldProcess)]
            param ()

            Write-Message -Message "Migrating mail server configuration values." -Level Verbose
            $copyMailConfigStatus = [pscustomobject]@{
                SourceServer      = $sourceServer.Name
                DestinationServer = $destServer.Name
                Name              = "Server Configuration"
                Type              = "Mail Configuration"
                Status            = $null
                Notes             = $null
                DateTime          = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
            }
            if ($pscmdlet.ShouldProcess($destinstance, "Migrating all mail server configuration values.")) {
                try {
                    $sql = $mail.ConfigurationValues.Script() | Out-String
                    $sql = $sql -replace [Regex]::Escape("'$source'"), "'$destinstance'"
                    Write-Message -Message $sql -Level Debug
                    $destServer.Query($sql) | Out-Null
                    $mail.ConfigurationValues.Refresh()
                    $copyMailConfigStatus.Status = "Successful"
                }
                catch {
                    $copyMailConfigStatus.Status = "Failed"
                    $copyMailConfigStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    Stop-Function -Message "Unable to migrate mail configuration." -Category InvalidOperation -InnerErrorRecord $_ -Target $destServer
                }
                $copyMailConfigStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
            }
        }
        
        function Copy-DbaDatabaseAccount {
            [cmdletbinding(SupportsShouldProcess)]
            $sourceAccounts = $sourceServer.Mail.Accounts
            $destAccounts = $destServer.Mail.Accounts

            Write-Message -Message "Migrating accounts." -Level Verbose
            foreach ($account in $sourceAccounts) {
                $accountName = $account.name
                $copyMailAccountStatus = [pscustomobject]@{
                    SourceServer      = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name              = $accountName
                    Type              = "Mail Account"
                    Status            = $null
                    Notes             = $null
                    DateTime          = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                }

                if ($accounts.count -gt 0 -and $accounts -notcontains $accountName) {
                    continue
                }

                if ($destAccounts.name -contains $accountName) {
                    if ($force -eq $false) {
                        If ($pscmdlet.ShouldProcess($destinstance, "Account $accountName exists at destination. Use -Force to drop and migrate.")) {
                            $copyMailAccountStatus.Status = "Skipped"
                            $copyMailAccountStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Write-Message -Message "Account $accountName exists at destination. Use -Force to drop and migrate." -Level Verbose
                        }
                        continue
                    }

                    If ($pscmdlet.ShouldProcess($destinstance, "Dropping account $accountName and recreating.")) {
                        try {
                            Write-Message -Message "Dropping account $accountName." -Level Verbose
                            $destServer.Mail.Accounts[$accountName].Drop()
                            $destServer.Mail.Accounts.Refresh()
                        }
                        catch {
                            $copyMailAccountStatus.Status = "Failed"
                            $copyMailAccountStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Stop-Function -Message "Issue dropping account." -Target $accountName -Category InvalidOperation -InnerErrorRecord $_ -Continue
                        }
                    }
                }

                if ($pscmdlet.ShouldProcess($destinstance, "Migrating account $accountName.")) {
                    try {
                        Write-Message -Message "Copying mail account $accountName." -Level Verbose
                        $sql = $account.Script() | Out-String
                        $sql = $sql -replace [Regex]::Escape("'$source'"), "'$destinstance'"
                        Write-Message -Message $sql -Level Debug
                        $destServer.Query($sql) | Out-Null
                        $copyMailAccountStatus.Status = "Successful"
                    }
                    catch {
                        $copyMailAccountStatus.Status = "Failed"
                        $copyMailAccountStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        Stop-Function -Message "Issue copying mail account." -Target $accountName -Category InvalidOperation -InnerErrorRecord $_
                    }
                    $copyMailAccountStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                }
            }
        }
        
        function Copy-DbaDatabaseMailProfile {

            $sourceProfiles = $sourceServer.Mail.Profiles
            $destProfiles = $destServer.Mail.Profiles

            Write-Message -Message "Migrating mail profiles." -Level Verbose
            foreach ($profile in $sourceProfiles) {

                $profileName = $profile.name
                $copyMailProfileStatus = [pscustomobject]@{
                    SourceServer      = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name              = $profileName
                    Type              = "Mail Profile"
                    Status            = $null
                    Notes             = $null
                    DateTime          = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                }

                if ($profiles.count -gt 0 -and $profiles -notcontains $profileName) {
                    continue
                }

                if ($destProfiles.name -contains $profileName) {
                    if ($force -eq $false) {
                        If ($pscmdlet.ShouldProcess($destinstance, "Profile $profileName exists at destination. Use -Force to drop and migrate.")) {
                            $copyMailProfileStatus.Status = "Skipped"
                            $copyMailProfileStatus.Notes = "Already exists"
                            $copyMailProfileStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Write-Message -Message "Profile $profileName exists at destination. Use -Force to drop and migrate." -Level Verbose
                        }
                        continue
                    }

                    If ($pscmdlet.ShouldProcess($destinstance, "Dropping profile $profileName and recreating.")) {
                        try {
                            Write-Message -Message "Dropping profile $profileName." -Level Verbose
                            $destServer.Mail.Profiles[$profileName].Drop()
                            $destServer.Mail.Profiles.Refresh()
                        }
                        catch {
                            $copyMailProfileStatus.Status = "Failed"
                            $copyMailProfileStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Stop-Function -Message "Issue dropping profile." -Target $profileName -Category InvalidOperation -InnerErrorRecord $_ -Continue
                        }
                    }
                }

                if ($pscmdlet.ShouldProcess($destinstance, "Migrating mail profile $profileName.")) {
                    try {
                        Write-Message -Message "Copying mail profile $profileName." -Level Verbose
                        $sql = $profile.Script() | Out-String
                        $sql = $sql -replace [Regex]::Escape("'$source'"), "'$destinstance'"
                        Write-Message -Message $sql -Level Debug
                        $destServer.Query($sql) | Out-Null
                        $destServer.Mail.Profiles.Refresh()
                        $copyMailProfileStatus.Status = "Successful"
                    }
                    catch {
                        $copyMailProfileStatus.Status = "Failed"
                        $copyMailProfileStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        Stop-Function -Message "Issue copying mail profile." -Target $profileName -Category InvalidOperation -InnerErrorRecord $_
                    }
                    $copyMailProfileStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                }
            }
        }
        
        function Copy-DbaDatabaseMailServer {
            [cmdletbinding(SupportsShouldProcess)]
            $sourceMailServers = $sourceServer.Mail.Accounts.MailServers
            $destMailServers = $destServer.Mail.Accounts.MailServers

            Write-Message -Message "Migrating mail servers." -Level Verbose
            foreach ($mailServer in $sourceMailServers) {
                $mailServerName = $mailServer.name
                $copyMailServerStatus = [pscustomobject]@{
                    SourceServer      = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name              = $mailServerName
                    Type              = "Mail Server"
                    Status            = $null
                    Notes             = $null
                    DateTime          = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                }
                if ($mailServers.count -gt 0 -and $mailServers -notcontains $mailServerName) {
                    continue
                }

                if ($destMailServers.name -contains $mailServerName) {
                    if ($force -eq $false) {
                        if ($pscmdlet.ShouldProcess($destinstance, "Mail server $mailServerName exists at destination. Use -Force to drop and migrate.")) {
                            $copyMailServerStatus.Status = "Skipped"
                            $copyMailServerStatus.Notes = "Already exists"
                            $copyMailServerStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Write-Message -Message "Mail server $mailServerName exists at destination. Use -Force to drop and migrate." -Level Verbose
                        }
                        continue
                    }

                    If ($pscmdlet.ShouldProcess($destinstance, "Dropping mail server $mailServerName and recreating.")) {
                        try {
                            Write-Message -Message "Dropping mail server $mailServerName." -Level Verbose
                            $destServer.Mail.Accounts.MailServers[$mailServerName].Drop()
                        }
                        catch {
                            $copyMailServerStatus.Status = "Failed"
                            $copyMailServerStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Stop-Function -Message "Issue dropping mail server." -Target $mailServerName -Category InvalidOperation -InnerErrorRecord $_ -Continue
                        }
                    }
                }

                if ($pscmdlet.ShouldProcess($destinstance, "Migrating account mail server $mailServerName.")) {
                    try {
                        Write-Message -Message "Copying mail server $mailServerName." -Level Verbose
                        $sql = $mailServer.Script() | Out-String
                        $sql = $sql -replace [Regex]::Escape("'$source'"), "'$destinstance'"
                        Write-Message -Message $sql -Level Debug
                        $destServer.Query($sql) | Out-Null
                        $copyMailServerStatus.Status = "Successful"
                    }
                    catch {
                        $copyMailServerStatus.Status = "Failed"
                        $copyMailServerStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        Stop-Function -Message "Issue copying mail server" -Target $mailServerName -Category InvalidOperation -InnerErrorRecord $_
                    }
                    $copyMailServerStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                }
            }
        }
        
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 9
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        $mail = $sourceServer.mail
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            
            if ($type.Count -gt 0) {
                
                switch ($type) {
                    "ConfigurationValues" {
                        Copy-DbaDatabaseMailConfig
                        $destServer.Mail.ConfigurationValues.Refresh()
                    }
                    
                    "Profiles" {
                        Copy-DbaDatabaseMailProfile
                        $destServer.Mail.Profiles.Refresh()
                    }
                    
                    "Accounts" {
                        Copy-DbaDatabaseAccount
                        $destServer.Mail.Accounts.Refresh()
                    }
                    
                    "mailServers" {
                        Copy-DbaDatabaseMailServer
                    }
                }
                
                continue
            }
            
            if (($profiles.count + $accounts.count + $mailServers.count) -gt 0) {
                
                if ($profiles.count -gt 0) {
                    Copy-DbaDatabaseMailProfile -Profiles $profiles
                    $destServer.Mail.Profiles.Refresh()
                }
                
                if ($accounts.count -gt 0) {
                    Copy-DbaDatabaseAccount -Accounts $accounts
                    $destServer.Mail.Accounts.Refresh()
                }
                
                if ($mailServers.count -gt 0) {
                    Copy-DbaDatabaseMailServer -mailServers $mailServers
                }
                
                continue
            }
            
            Copy-DbaDatabaseMailConfig
            $destServer.Mail.ConfigurationValues.Refresh()
            Copy-DbaDatabaseAccount
            $destServer.Mail.Accounts.Refresh()
            Copy-DbaDatabaseMailProfile
            $destServer.Mail.Profiles.Refresh()
            Copy-DbaDatabaseMailServer
            $copyMailConfigStatus
            $copyMailAccountStatus
            $copyMailProfileStatus
            $copyMailServerStatus
            $enableDBMailStatus
            
        <# ToDo: Use Get/Set-DbaSpConfigure once the dynamic parameters are replaced. #>
            
            if (($sourceDbMailEnabled -eq 1) -and ($destDbMailEnabled -eq 0)) {
                if ($pscmdlet.ShouldProcess($destinstance, "Enabling Database Mail")) {
                    $sourceDbMailEnabled = ($sourceServer.Configuration.DatabaseMailEnabled).ConfigValue
                    Write-Message -Message "$sourceServer DBMail configuration value: $sourceDbMailEnabled." -Level Verbose
                    
                    $destDbMailEnabled = ($destServer.Configuration.DatabaseMailEnabled).ConfigValue
                    Write-Message -Message "$destServer DBMail configuration value: $destDbMailEnabled." -Level Verbose
                    $enableDBMailStatus = [pscustomobject]@{
                        SourceServer = $sourceServer.name
                        DestinationServer = $destServer.name
                        Name         = "Enabled on Destination"
                        Type         = "Mail Configuration"
                        Status       = if ($destDbMailEnabled -eq 1) { "Enabled" } else { $null }
                        DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                    }
                    try {
                        Write-Message -Message "Enabling Database Mail on $destServer." -Level Verbose
                        $destServer.Configuration.DatabaseMailEnabled.ConfigValue = 1
                        $destServer.Alter()
                        $enableDBMailStatus.Status = "Successful"
                    }
                    catch {
                        $enableDBMailStatus.Status = "Failed"
                        $enableDBMailStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        Stop-Function -Message "Cannot enable Database Mail." -Category InvalidOperation -ErrorRecord $_ -Target $destServer
                    }
                    $enableDBMailStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlDatabaseMail
    }
}
function Copy-DbaEndpoint {
    <#
        .SYNOPSIS
            Copy-DbaEndpoint migrates server endpoints from one SQL Server to another.

        .DESCRIPTION
            By default, all endpoints are copied.

            If the endpoint already exists on the destination, it will be skipped unless -Force is used.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Endpoint
            The endpoint(s) to process. This list is auto-populated from the server. If unspecified, all endpoints will be processed.

        .PARAMETER ExcludeEndpoint
            The endpoint(s) to exclude. This list is auto-populated from the server.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER Force
            If this switch is enabled, existing endpoints on Destination with matching names from Source will be dropped.

        .NOTES
            Tags: Migration, Endpoint
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaEndpoint

        .EXAMPLE
            Copy-DbaEndpoint -Source sqlserver2014a -Destination sqlcluster

            Copies all server endpoints from sqlserver2014a to sqlcluster, using Windows credentials. If endpoints with the same name exist on sqlcluster, they will be skipped.

        .EXAMPLE
            Copy-DbaEndpoint -Source sqlserver2014a -SourceSqlCredential $cred -Destination sqlcluster -Endpoint tg_noDbDrop -Force

            Copies only the tg_noDbDrop endpoint from sqlserver2014a to sqlcluster using SQL credentials for sqlserver2014a and Windows credentials for sqlcluster. If an endpoint with the same name exists on sqlcluster, it will be dropped and recreated because -Force was used.

        .EXAMPLE
            Copy-DbaEndpoint -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force

            Shows what would happen if the command were executed using force.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]
        $SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]
        $DestinationSqlCredential,
        [object[]]$Endpoint,
        [object[]]$ExcludeEndpoint,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 9
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        $serverEndpoints = $sourceServer.Endpoints | Where-Object IsSystemObject -eq $false
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            $destEndpoints = $destServer.Endpoints
            
            foreach ($currentEndpoint in $serverEndpoints) {
                $endpointName = $currentEndpoint.Name
                
                $copyEndpointStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name         = $endpointName
                    Type         = "Endpoint"
                    Status       = $null
                    Notes        = $null
                    DateTime     = [DbaDateTime](Get-Date)
                }
                
                if ($Endpoint -and $Endpoint -notcontains $endpointName -or $ExcludeEndpoint -contains $endpointName) {
                    continue
                }
                
                if ($destEndpoints.Name -contains $endpointName) {
                    if ($force -eq $false) {
                        $copyEndpointStatus.Status = "Skipped"
                        $copyEndpointStatus.Notes = "Already exists"
                        $copyEndpointStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Write-Message -Level Verbose -Message "Server endpoint $endpointName exists at destination. Use -Force to drop and migrate."
                        continue
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Dropping server endpoint $endpointName and recreating.")) {
                            try {
                                Write-Message -Level Verbose -Message "Dropping server endpoint $endpointName."
                                $destServer.Endpoints[$endpointName].Drop()
                            }
                            catch {
                                $copyEndpointStatus.Status = "Failed"
                                $copyEndpointStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                
                                Stop-Function -Message "Issue dropping server endpoint." -Target $endpointName -ErrorRecord $_ -Continue
                            }
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Creating server endpoint $endpointName.")) {
                    try {
                        Write-Message -Level Verbose -Message "Copying server endpoint $endpointName."
                        $destServer.Query($currentEndpoint.Script()) | Out-Null
                        
                        $copyEndpointStatus.Status = "Successful"
                        $copyEndpointStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copyEndpointStatus.Status = "Failed"
                        $copyEndpointStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Stop-Function -Message "Issue creating server endpoint." -Target $endpointName -ErrorRecord $_
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlEndpoint
    }
}
function Copy-DbaExtendedEvent {
    <#
        .SYNOPSIS
            Migrates SQL Extended Event Sessions except the two default sessions, AlwaysOn_health and system_health.

        .DESCRIPTION
            Migrates SQL Extended Event Sessions except the two default sessions, AlwaysOn_health and system_health.

            By default, all non-system Extended Events are migrated.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER XeSession
            The Extended Event Session(s) to process. This list is auto-populated from the server. If unspecified, all Extended Event Sessions will be processed.

        .PARAMETER ExcludeXeSession
            The Extended Event Session(s) to exclude. This list is auto-populated from the server.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER Force
            If this switch is enabled, existing Extended Events sessions on Destination with matching names from Source will be dropped.

        .NOTES
            Tags: Migration, ExtendedEvent, XEvent
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaExtendedEvent

        .EXAMPLE
            Copy-DbaExtendedEvent -Source sqlserver2014a -Destination sqlcluster

            Copies all Extended Event sessions from sqlserver2014a to sqlcluster using Windows credentials.

        .EXAMPLE
            Copy-DbaExtendedEvent -Source sqlserver2014a -Destination sqlcluster -SourceSqlCredential $cred

            Copies all Extended Event sessions from sqlserver2014a to sqlcluster using SQL credentials for sqlserver2014a and Windows credentials for sqlcluster.

        .EXAMPLE
            Copy-DbaExtendedEvent -Source sqlserver2014a -Destination sqlcluster -WhatIf

            Shows what would happen if the command were executed.

        .EXAMPLE
            Copy-DbaExtendedEvent -Source sqlserver2014a -Destination sqlcluster -XeSession CheckQueries, MonitorUserDefinedException

            Copies only the Extended Events named CheckQueries and MonitorUserDefinedException from sqlserver2014a to sqlcluster.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]
        $SourceSqlCredential,
        [PSCredential]
        $DestinationSqlCredential,
        [object[]]$XeSession,
        [object[]]$ExcludeXeSession,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 10
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        $sourceSqlConn = $sourceServer.ConnectionContext.SqlConnectionObject
        $sourceSqlStoreConnection = New-Object Microsoft.SqlServer.Management.Sdk.Sfc.SqlStoreConnection $sourceSqlConn
        $sourceStore = New-Object  Microsoft.SqlServer.Management.XEvent.XEStore $sourceSqlStoreConnection
        $storeSessions = $sourceStore.Sessions | Where-Object { $_.Name -notin 'AlwaysOn_health', 'system_health' }
        if ($XeSession) {
            $storeSessions = $storeSessions | Where-Object Name -In $XeSession
        }
        if ($ExcludeXeSession) {
            $storeSessions = $storeSessions | Where-Object Name -NotIn $ExcludeXeSession
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            
            $destSqlConn = $destServer.ConnectionContext.SqlConnectionObject
            $destSqlStoreConnection = New-Object Microsoft.SqlServer.Management.Sdk.Sfc.SqlStoreConnection $destSqlConn
            $destStore = New-Object  Microsoft.SqlServer.Management.XEvent.XEStore $destSqlStoreConnection
            
            Write-Message -Level Verbose -Message "Migrating sessions."
            foreach ($session in $storeSessions) {
                $sessionName = $session.Name
                
                $copyXeSessionStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name         = $sessionName
                    Type         = "Extended Event"
                    Status       = $null
                    Notes        = $null
                    DateTime     = [DbaDateTime](Get-Date)
                }
                
                if ($null -ne $destStore.Sessions[$sessionName]) {
                    if ($force -eq $false) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Extended Event Session '$sessionName' was skipped because it already exists on $destinstance.")) {
                            $copyXeSessionStatus.Status = "Skipped"
                            $copyXeSessionStatus.Notes = "Already exists"
                            $copyXeSessionStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            
                            Write-Message -Level Verbose -Message "Extended Event Session '$sessionName' was skipped because it already exists on $destinstance."
                            Write-Message -Level Verbose -Message "Use -Force to drop and recreate."
                        }
                        continue
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Attempting to drop $sessionName")) {
                            Write-Message -Level Verbose -Message "Extended Event Session '$sessionName' exists on $destinstance."
                            Write-Message -Level Verbose -Message "Force specified. Dropping $sessionName."
                            
                            try {
                                $destStore.Sessions[$sessionName].Drop()
                            }
                            catch {
                                $copyXeSessionStatus.Status = "Failed"
                                $copyXeSessionStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                
                                Stop-Function -Message "Unable to drop session. Moving on." -Target $sessionName -ErrorRecord $_ -Continue
                            }
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Migrating session $sessionName")) {
                    try {
                        $sql = $session.ScriptCreate().GetScript() | Out-String
                        
                        Write-Message -Level Debug -Message $sql
                        Write-Message -Level Verbose -Message "Migrating session $sessionName."
                        $null = $destServer.Query($sql)
                        
                        if ($session.IsRunning -eq $true) {
                            $destStore.Sessions.Refresh()
                            $destStore.Sessions[$sessionName].Start()
                        }
                        
                        $copyXeSessionStatus.Status = "Successful"
                        $copyXeSessionStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copyXeSessionStatus.Status = "Failed"
                        $copyXeSessionStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Stop-Function -Message "Unable to create session." -Target $sessionName -ErrorRecord $_
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlExtendedEvent
    }
}
function Copy-DbaLinkedServer {
    <#
        .SYNOPSIS
            Copy-DbaLinkedServer migrates Linked Servers from one SQL Server to another. Linked Server logins and passwords are migrated as well.

        .DESCRIPTION
            By using password decryption techniques provided by Antti Rantasaari (NetSPI, 2014), this script migrates SQL Server Linked Servers from one server to another, while maintaining username and password.

            Credit: https://blog.netspi.com/decrypting-mssql-database-link-server-passwords/
            License: BSD 3-Clause http://opensource.org/licenses/BSD-3-Clause

        .PARAMETER Source
            Source SQL Server (2005 and above). You must have sysadmin access to both SQL Server and Windows.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server (2005 and above). You must have sysadmin access to both SQL Server and Windows.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER LinkedServer
            The linked server(s) to process - this list is auto-populated from the server. If unspecified, all linked servers will be processed.

        .PARAMETER ExcludeLinkedServer
            The linked server(s) to exclude - this list is auto-populated from the server

        .PARAMETER UpgradeSqlClient
            Upgrade any SqlClient Linked Server to the current Version

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER Force
            By default, if a Linked Server exists on the source and destination, the Linked Server is not copied over. Specifying -force will drop and recreate the Linked Server on the Destination server.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: WSMan, Migration, LinkedServer
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers, Remote Registry & Remote Administration enabled and accessible on source server.

            Limitations: Hasn't been tested thoroughly. Works on Win8.1 and SQL Server 2012 & 2014 so far.
            This just copies the SQL portion. It does not copy files (ie. a local SQLite database, or Microsoft Access DB), nor does it configure ODBC entries.

        .LINK
            https://dbatools.io/Copy-DbaLinkedServer

        .EXAMPLE
            Copy-DbaLinkedServer -Source sqlserver2014a -Destination sqlcluster

            Description
            Copies all SQL Server Linked Servers on sqlserver2014a to sqlcluster. If Linked Server exists on destination, it will be skipped.

        .EXAMPLE
            Copy-DbaLinkedServer -Source sqlserver2014a -Destination sqlcluster -LinkedServer SQL2K5,SQL2k -Force

            Description
            Copies over two SQL Server Linked Servers (SQL2K and SQL2K2) from sqlserver to sqlcluster. If the credential already exists on the destination, it will be dropped.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    Param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]$SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [object[]]$LinkedServer,
        [object[]]$ExcludeLinkedServer,
        [switch]$UpgradeSqlClient,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        $null = Test-ElevationRequirement -ComputerName $Source.ComputerName
        function Copy-DbaLinkedServers {
            param (
                [string[]]$LinkedServer,
                [bool]$force
            )

            Write-Message -Level Verbose -Message "Collecting Linked Server logins and passwords on $($sourceServer.Name)."
            $sourcelogins = Get-DecryptedObject -SqlInstance $sourceServer -Type LinkedServer

            $serverlist = $sourceServer.LinkedServers

            if ($LinkedServer) {
                $serverlist = $serverlist | Where-Object Name -In $LinkedServer
            }
            if ($ExcludeLinkedServer) {
                $serverList = $serverlist | Where-Object Name -NotIn $ExcludeLinkedServer
            }

            foreach ($currentLinkedServer in $serverlist) {
                $provider = $currentLinkedServer.ProviderName
                try {
                    $destServer.LinkedServers.Refresh()
                    $destServer.LinkedServers.LinkedServerLogins.Refresh()
                }
                catch { }

                $linkedServerName = $currentLinkedServer.Name

                $copyLinkedServer = [pscustomobject]@{
                    SourceServer      = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name              = $linkedServerName
                    Type              = "Linked Server"
                    Status            = $null
                    Notes             = $provider
                    DateTime          = [DbaDateTime](Get-Date)
                }

                # This does a check to warn of missing OleDbProviderSettings but should only be checked on SQL on Windows
                if ($destServer.Settings.OleDbProviderSettings.Name.Length -ne 0) {
                    if (!$destServer.Settings.OleDbProviderSettings.Name -contains $provider -and !$provider.StartsWith("SQLN")) {
                        $copyLinkedServer.Status = "Skipped"
                        $copyLinkedServer.Notes = "Missing provider"
                        $copyLinkedServer | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject

                        Write-Message -Level Verbose -Message "$($destServer.Name) does not support the $provider provider. Skipping $linkedServerName."
                        continue
                    }
                }

                if ($null -ne $destServer.LinkedServers[$linkedServerName]) {
                    if (!$force) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "$linkedServerName exists $($destServer.Name). Skipping.")) {
                            $copyLinkedServer.Status = "Skipped"
                            $copyLinkedServer | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            
                            Write-Message -Level Verbose -Message "$linkedServerName exists $($destServer.Name). Skipping."
                        }
                        continue
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Dropping $linkedServerName")) {
                            if ($currentLinkedServer.Name -eq 'repl_distributor') {
                                Write-Message -Level Verbose -Message "repl_distributor cannot be dropped. Not going to try."
                                continue
                            }

                            $destServer.LinkedServers[$linkedServerName].Drop($true)
                            $destServer.LinkedServers.refresh()
                        }
                    }
                }

                Write-Message -Level Verbose -Message "Attempting to migrate: $linkedServerName."
                If ($Pscmdlet.ShouldProcess($destinstance, "Migrating $linkedServerName")) {
                    try {
                        $sql = $currentLinkedServer.Script() | Out-String
                        Write-Message -Level Debug -Message $sql

                        if ($UpgradeSqlClient -and $sql -match "sqlncli") {
                            $destProviders = $destServer.Settings.OleDbProviderSettings | Where-Object { $_.Name -like 'SQLNCLI*' }
                            $newProvider = $destProviders | Sort-Object Name -Descending | Select-Object -First 1 -ExpandProperty Name

                            Write-Message -Level Verbose -Message "Changing sqlncli to $newProvider"
                            $sql = $sql -replace ("sqlncli[0-9]+", $newProvider)
                        }

                        $destServer.Query($sql)
                        $destServer.LinkedServers.Refresh()
                        Write-Message -Level Verbose -Message "$linkedServerName successfully copied."

                        $copyLinkedServer.Status = "Successful"
                        $copyLinkedServer | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copyLinkedServer.Status = "Failed"
                        $copyLinkedServer | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject

                        Stop-Function -Message "Issue adding linked server $destServer." -Target $linkedServerName -InnerErrorRecord $_
                        $skiplogins = $true
                    }
                }

                if ($skiplogins -ne $true) {
                    $destlogins = $destServer.LinkedServers[$linkedServerName].LinkedServerLogins
                    $lslogins = $sourcelogins | Where-Object { $_.Name -eq $linkedServerName }

                    foreach ($login in $lslogins) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Migrating $($login.Login)")) {
                            $currentlogin = $destlogins | Where-Object { $_.RemoteUser -eq $login.Identity }

                            $copyLinkedServer.Type = $login.Identity

                            if ($currentlogin.RemoteUser.length -ne 0) {
                                try {
                                    $currentlogin.SetRemotePassword($login.Password)
                                    $currentlogin.Alter()

                                    $copyLinkedServer.Status = "Successful"
                                    $copyLinkedServer | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                }
                                catch {
                                    $copyLinkedServer.Status = "Failed"
                                    $copyLinkedServer | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject

                                    Stop-Function -Message "Failed to copy login." -Target $login -InnerErrorRecord $_
                                }
                            }
                        }
                    }
                }
            }
        }
        
        if ($null -ne $SourceSqlCredential.Username) {
            Write-Message -Level Verbose -Message "You are using a SQL Credential. Note that this script requires Windows Administrator access on the source server. Attempting with $($SourceSqlCredential.Username)."
        }
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
            return
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        if (!(Test-SqlSa -SqlInstance $sourceServer -SqlCredential $SourceSqlCredential)) {
            Stop-Function -Message "Not a sysadmin on $source. Quitting." -Target $sourceServer
            return
        }
        Write-Message -Level Verbose -Message "Getting NetBios name for $source."
        $sourceNetBios = Resolve-NetBiosName $sourceserver
        
        Write-Message -Level Verbose -Message "Checking if Remote Registry is enabled on $source."
        try {
            Invoke-Command2 -Raw -Credential $Credential -ComputerName $sourceNetBios -ScriptBlock { Get-ItemProperty -Path "HKLM:\SOFTWARE\" } -ErrorAction Stop
        }
        catch {
            Stop-Function -Message "Can't connect to registry on $source." -Target $sourceNetBios -ErrorRecord $_
            return
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }
        
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            if (!(Test-SqlSa -SqlInstance $destServer -SqlCredential $DestinationSqlCredential)) {
                Stop-Function -Message "Not a sysadmin on $destinstance" -Target $destServer -Continue
            }
            
            # Magic happens here
            Copy-DbaLinkedServers $LinkedServer -Force:$force
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlLinkedServer
    }
}
function Copy-DbaLogin {
    <#
        .SYNOPSIS
            Migrates logins from source to destination SQL Servers. Supports SQL Server versions 2000 and newer.

        .DESCRIPTION
            SQL Server 2000: Migrates logins with SIDs, passwords, server roles and database roles.

            SQL Server 2005 & newer: Migrates logins with SIDs, passwords, defaultdb, server roles & securables, database permissions & securables, login attributes (enforce password policy, expiration, etc.)

            The login hash algorithm changed in SQL Server 2012, and is not backwards compatible with previous SQL Server versions. This means that while SQL Server 2000 logins can be migrated to SQL Server 2012, logins created in SQL Server 2012 can only be migrated to SQL Server 2012 and above.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Login
            The login(s) to process. Options for this list are auto-populated from the server. If unspecified, all logins will be processed.

        .PARAMETER ExcludeLogin
            The login(s) to exclude. Options for this list are auto-populated from the server.

        .PARAMETER ExcludeSystemLogin
            If this switch is enabled, NT SERVICE accounts will be skipped.

        .PARAMETER SyncOnly
            If this switch is enabled, only SQL Server login permissions, roles, etc. will be synced. Logins and users will not be added or dropped.  If a matching Login does not exist on the destination, the Login will be skipped.
            Credential removal is not currently supported for this parameter.

        .PARAMETER SyncSaName
            If this switch is enabled, the name of the sa account will be synced between Source and Destination

        .PARAMETER OutFile
            Calls Export-SqlLogin and exports all logins to a T-SQL formatted file. This does not perform a copy, so no destination is required.

        .PARAMETER InputObject
            Takes the parameters required from a Login object that has been piped into the command

        .PARAMETER LoginRenameHashtable
            Pass a hash table into this parameter to be passed into Rename-DbaLogin to update the Login and mappings after the Login is completed.

        .PARAMETER KillActiveConnection
            If this switch and -Force are enabled, all active connections and sessions on Destination will be killed.

            A login cannot be dropped when it has active connections on the instance.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER Force
            If this switch is enabled, the Login(s) will be dropped and recreated on Destination. Logins that own Agent jobs cannot be dropped at this time.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration, Login
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaLogin

        .EXAMPLE
            Copy-DbaLogin -Source sqlserver2014a -Destination sqlcluster -Force

            Copies all logins from Source Destination. If a SQL Login on Source exists on the Destination, the Login on Destination will be dropped and recreated.

            If active connections are found for a login, the copy of that Login will fail as it cannot be dropped.

        .EXAMPLE
            Copy-DbaLogin -Source sqlserver2014a -Destination sqlcluster -Force -KillActiveConnection

            Copies all logins from Source Destination. If a SQL Login on Source exists on the Destination, the Login on Destination will be dropped and recreated.

            If any active connections are found they will be killed.

        .EXAMPLE
            Copy-DbaLogin -Source sqlserver2014a -Destination sqlcluster -Exclude realcajun -SourceSqlCredential $scred -DestinationSqlCredential $dcred

            Copies all Logins from Source to Destination except for realcajun using SQL Authentication to connect to both instances.

            If a Login already exists on the destination, it will not be migrated.

        .EXAMPLE
            Copy-DbaLogin -Source sqlserver2014a -Destination sqlcluster -Login realcajun, netnerds -force

            Copies ONLY Logins netnerds and realcajun. If Login realcajun or netnerds exists on Destination, the existing Login(s) will be dropped and recreated.

        .EXAMPLE
            Copy-DbaLogin -Source sqlserver2014a -Destination sqlcluster -SyncOnly

            Syncs only SQL Server login permissions, roles, etc. Does not add or drop logins or users.

            If a matching Login does not exist on Destination, the Login will be skipped.

        .EXAMPLE
            Copy-DbaLogin -LoginRenameHashtable @{ "OldUser" ="newlogin" } -Source $Sql01 -Destination Localhost -SourceSqlCredential $sqlcred

            Copies OldUser and then renames it to newlogin.

        .EXAMPLE
            Get-DbaLogin -SqlInstance sql2016 | Out-GridView -Passthru | Copy-DbaLogin -Destination sql2017

            Displays all available logins on sql2016 in a grid view, then copies all selected logins to sql2017.
    #>

    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess)]
    Param (
        [parameter(ParameterSetName = "SqlInstance", Mandatory)]
        [DbaInstanceParameter]$Source,
        [PSCredential]$SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [object[]]$Login,
        [object[]]$ExcludeLogin,
        [switch]$ExcludeSystemLogin,
        [switch]$SyncOnly,
        [parameter(ParameterSetName = "Live")]
        [parameter(ParameterSetName = "SqlInstance")]
        [switch]$SyncSaName,
        [parameter(ParameterSetName = "File", Mandatory)]
        [string]$OutFile,
        [parameter(ParameterSetName = "InputObject", ValueFromPipeline)]
        [object]$InputObject,
        [hashtable]$LoginRenameHashtable,
        [switch]$KillActiveConnection,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        function Copy-Login {
            foreach ($sourceLogin in $sourceServer.Logins) {
                $userName = $sourceLogin.name
                
                $copyLoginStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Type         = "Login - $($sourceLogin.LoginType)"
                    Name         = $userName
                    DestinationLogin = $userName
                    SourceLogin  = $userName
                    Status       = $null
                    Notes        = $null
                    DateTime     = [DbaDateTime](Get-Date)
                }
                
                if ($Login -and $Login -notcontains $userName -or $ExcludeLogin -contains $userName) { continue }

                if ($sourceLogin.id -eq 1) { continue }

                if ($userName.StartsWith("##") -or $userName -eq 'sa') {
                    Write-Message -Level Verbose -Message "Skipping $userName."
                    continue
                }

                $serverName = Resolve-NetBiosName $sourceServer

                $currentLogin = $sourceServer.ConnectionContext.truelogin

                if ($currentLogin -eq $userName -and $force) {
                    if ($Pscmdlet.ShouldProcess("console", "Stating $userName is skipped because it is performing the migration.")) {
                        Write-Message -Level Verbose -Message "Cannot drop login performing the migration. Skipping."
                        $copyLoginStatus.Status = "Skipped"
                        $copyLoginStatus.Notes = "Current login"
                        $copyLoginStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    continue
                }
                
                if (($destServer.LoginMode -ne [Microsoft.SqlServer.Management.Smo.ServerLoginMode]::Mixed) -and ($sourceLogin.LoginType -eq [Microsoft.SqlServer.Management.Smo.LoginType]::SqlLogin)) {
                    Write-Message -Level Verbose -Message "$Destination does not have Mixed Mode enabled. [$userName] is an SQL Login. Enable mixed mode authentication after the migration completes to use this type of login."
                }

                $userBase = ($userName.Split("\")[0]).ToLower()

                if ($serverName -eq $userBase -or $userName.StartsWith("NT ")) {
                    if ($sourceServer.ComputerName -ne $destServer.ComputerName) {
                        if ($Pscmdlet.ShouldProcess("console", "Stating $userName was skipped because it is a local machine name.")) {
                            Write-Message -Level Verbose -Message "$userName was skipped because it is a local machine name."
                            $copyLoginStatus.Status = "Skipped"
                            $copyLoginStatus.Notes = "Local machine name"
                            $copyLoginStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        }
                        continue
                    }
                    else {
                        if ($ExcludeSystemLogin) {
                            if ($Pscmdlet.ShouldProcess("console", "$userName was skipped because ExcludeSystemLogin was specified.")) {
                                Write-Message -Level Verbose -Message "$userName was skipped because ExcludeSystemLogin was specified."
                                
                                $copyLoginStatus.Status = "Skipped"
                                $copyLoginStatus.Notes = "System login"
                                $copyLoginStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            }
                            continue
                        }
                        
                        if ($Pscmdlet.ShouldProcess("console", "Stating local login $userName since the source and destination server reside on the same machine.")) {
                            Write-Message -Level Verbose -Message "Copying local login $userName since the source and destination server reside on the same machine."
                        }
                    }
                }
                
                if ($null -ne $destServer.Logins.Item($userName) -and !$force) {
                    if ($Pscmdlet.ShouldProcess("console", "Stating $userName is skipped because it exists at destination.")) {
                        Write-Message -Level Verbose -Message "$userName already exists in destination. Use -Force to drop and recreate."
                        $copyLoginStatus.Status = "Skipped"
                        $copyLoginStatus.Notes = "Already exists"
                        $copyLoginStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    continue
                }
                
                if ($null -ne $destServer.Logins.Item($userName) -and $force) {
                    if ($userName -eq $destServer.ServiceAccount) {
                        if ($Pscmdlet.ShouldProcess("console", "$userName is the destination service account. Skipping drop.")) {
                            Write-Message -Level Verbose -Message "$userName is the destination service account. Skipping drop."
                            
                            $copyLoginStatus.Status = "Skipped"
                            $copyLoginStatus.Notes = "Destination service account"
                            $copyLoginStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        }
                        continue
                    }
                    
                    if ($Pscmdlet.ShouldProcess($destinstance, "Dropping $userName")) {

                        # Kill connections, delete user
                        Write-Message -Level Verbose -Message "Attempting to migrate $userName"
                        Write-Message -Level Verbose -Message "Force was specified. Attempting to drop $userName on $destinstance."

                        try {
                            $ownedDbs = $destServer.Databases | Where-Object Owner -eq $userName

                            foreach ($ownedDb in $ownedDbs) {
                                Write-Message -Level Verbose -Message "Changing database owner for $($ownedDb.name) from $userName to sa."
                                $ownedDb.SetOwner('sa')
                                $ownedDb.Alter()
                            }

                            $ownedJobs = $destServer.JobServer.Jobs | Where-Object OwnerLoginName -eq $userName

                            foreach ($ownedJob in $ownedJobs) {
                                Write-Message -Level Verbose -Message "Changing job owner for $($ownedJob.name) from $userName to sa."
                                $ownedJob.Set_OwnerLoginName('sa')
                                $ownedJob.Alter()
                            }

                            $activeConnections = $destServer.EnumProcesses() | Where-Object Login -eq $userName

                            if ($activeConnections -and $KillActiveConnection) {
                                if (!$destServer.Logins.Item($userName).IsDisabled) {
                                    $disabled = $true
                                    $destServer.Logins.Item($userName).Disable()
                                }

                                $activeConnections | ForEach-Object { $destServer.KillProcess($_.Spid) }
                                Write-Message -Level Verbose -Message "-KillActiveConnection was provided. There are $($activeConnections.Count) active connections killed."
                                # just in case the kill didn't work, it'll leave behind a disabled account
                                if ($disabled) { $destServer.Logins.Item($userName).Enable() }
                            }
                            elseif ($activeConnections) {
                                Write-Message -Level Verbose -Message "There are $($activeConnections.Count) active connections found for the login $userName. Utilize -KillActiveConnection with -Force to kill the connections."
                            }
                            $destServer.Logins.Item($userName).Drop()

                            Write-Message -Level Verbose -Message "Successfully dropped $userName on $destinstance."
                        }
                        catch {
                            $copyLoginStatus.Status = "Failed"
                            $copyLoginStatus.Notes = (Get-ErrorMessage -Record $_).Message
                            $copyLoginStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject

                            Stop-Function -Message "Could not drop $userName." -Category InvalidOperation -ErrorRecord $_ -Target $destServer -Continue 3>$null
                        }
                    }
                }

                if ($Pscmdlet.ShouldProcess($destinstance, "Adding SQL login $userName")) {

                    Write-Message -Level Verbose -Message "Attempting to add $userName to $destinstance."
                    $destLogin = New-Object Microsoft.SqlServer.Management.Smo.Login($destServer, $userName)

                    Write-Message -Level Verbose -Message "Setting $userName SID to source username SID."
                    $destLogin.Set_Sid($sourceLogin.Get_Sid())

                    $defaultDb = $sourceLogin.DefaultDatabase

                    Write-Message -Level Verbose -Message "Setting login language to $($sourceLogin.Language)."
                    $destLogin.Language = $sourceLogin.Language

                    if ($null -eq $destServer.databases[$defaultDb]) {
                        # we end up here when the default database on source doesn't exist on dest
                        # if source login is a sysadmin, then set the default database to master
                        # if not, set it to tempdb (see #303)
                        $OrigdefaultDb = $defaultDb
                        try { $sourcesysadmins = $sourceServer.roles['sysadmin'].EnumMemberNames() }
                        catch { $sourcesysadmins = $sourceServer.roles['sysadmin'].EnumServerRoleMembers() }
                        if ($sourcesysadmins -contains $userName) {
                            $defaultDb = "master"
                        }
                        else {
                            $defaultDb = "tempdb"
                        }
                        Write-Message -Level Verbose -Message "$OrigdefaultDb does not exist on destination. Setting defaultdb to $defaultDb."
                    }

                    Write-Message -Level Verbose -Message "Set $userName defaultdb to $defaultDb."
                    $destLogin.DefaultDatabase = $defaultDb

                    $checkexpiration = "ON"; $checkpolicy = "ON"

                    if ($sourceLogin.PasswordPolicyEnforced -eq $false) { $checkpolicy = "OFF" }

                    if (!$sourceLogin.PasswordExpirationEnabled) { $checkexpiration = "OFF" }

                    $destLogin.PasswordPolicyEnforced = $sourceLogin.PasswordPolicyEnforced
                    $destLogin.PasswordExpirationEnabled = $sourceLogin.PasswordExpirationEnabled

                    # Attempt to add SQL Login User
                    if ($sourceLogin.LoginType -eq "SqlLogin") {
                        $destLogin.LoginType = "SqlLogin"
                        $sourceLoginname = $sourceLogin.name

                        switch ($sourceServer.versionMajor) {
                            0 { $sql = "SELECT CONVERT(VARBINARY(256),password) as hashedpass FROM master.dbo.syslogins WHERE loginname='$sourceLoginname'" }
                            8 { $sql = "SELECT CONVERT(VARBINARY(256),password) as hashedpass FROM dbo.syslogins WHERE name='$sourceLoginname'" }
                            9 { $sql = "SELECT CONVERT(VARBINARY(256),password_hash) as hashedpass FROM sys.sql_logins where name='$sourceLoginname'" }
                            default {
                                $sql = "SELECT CAST(CONVERT(VARCHAR(256), CAST(LOGINPROPERTY(name,'PasswordHash')
                        AS VARBINARY(256)), 1) AS NVARCHAR(max)) AS hashedpass FROM sys.server_principals
                        WHERE principal_id = $($sourceLogin.id)"
                            }
                        }

                        try {
                            $hashedPass = $sourceServer.ConnectionContext.ExecuteScalar($sql)
                        }
                        catch {
                            $hashedPassDt = $sourceServer.Databases['master'].ExecuteWithResults($sql)
                            $hashedPass = $hashedPassDt.Tables[0].Rows[0].Item(0)
                        }

                        if ($hashedPass.GetType().Name -ne "String") {
                            $passString = "0x"; $hashedPass | ForEach-Object { $passString += ("{0:X}" -f $_).PadLeft(2, "0") }
                            $hashedPass = $passString
                        }

                        try {
                            $destLogin.Create($hashedPass, [Microsoft.SqlServer.Management.Smo.LoginCreateOptions]::IsHashed)
                            $destLogin.Refresh()
                            Write-Message -Level Verbose -Message "Successfully added $userName to $destinstance."

                            $copyLoginStatus.Status = "Successful"
                            $copyLoginStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        }
                        catch {
                            try {
                                $sid = "0x"; $sourceLogin.sid | ForEach-Object { $sid += ("{0:X}" -f $_).PadLeft(2, "0") }
                                $sql = "CREATE LOGIN [$userName] WITH PASSWORD = $hashedPass HASHED, SID = $sid,
                                                DEFAULT_DATABASE = [$defaultDb], CHECK_POLICY = $checkpolicy,
                                                CHECK_EXPIRATION = $checkexpiration, DEFAULT_LANGUAGE = [$($sourceLogin.Language)]"

                                $null = $destServer.Query($sql)

                                $destLogin = $destServer.logins[$userName]
                                Write-Message -Level Verbose -Message "Successfully added $userName to $destinstance."

                                $copyLoginStatus.Status = "Successful"
                                $copyLoginStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject

                            }
                            catch {
                                $copyLoginStatus.Status = "Failed"
                                $copyLoginStatus.Notes = (Get-ErrorMessage -Record $_).Message
                                $copyLoginStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject

                                Stop-Function -Message "Failed to add $userName to $destinstance." -Category InvalidOperation -ErrorRecord $_ -Target $destServer -Continue 3>$null
                            }
                        }
                    }
                    # Attempt to add Windows User
                    elseif ($sourceLogin.LoginType -eq "WindowsUser" -or $sourceLogin.LoginType -eq "WindowsGroup") {
                        Write-Message -Level Verbose -Message "Adding as login type $($sourceLogin.LoginType)"
                        $destLogin.LoginType = $sourceLogin.LoginType

                        Write-Message -Level Verbose -Message "Setting language as $($sourceLogin.Language)"
                        $destLogin.Language = $sourceLogin.Language

                        try {
                            $destLogin.Create()
                            $destLogin.Refresh()
                            Write-Message -Level Verbose -Message "Successfully added $userName to $destinstance."

                            $copyLoginStatus.Status = "Successful"
                            $copyLoginStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject

                        }
                        catch {
                            $copyLoginStatus.Status = "Failed"
                            $copyLoginStatus.Notes = (Get-ErrorMessage -Record $_).Message
                            $copyLoginStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject

                            Stop-Function -Message "Failed to add $userName to $destinstance" -Category InvalidOperation -ErrorRecord $_ -Target $destServer -Continue 3>$null
                        }
                    }
                    # This script does not currently support certificate mapped or asymmetric key users.
                    else {
                        Write-Message -Level Verbose -Message "$($sourceLogin.LoginType) logins not supported. $($sourceLogin.name) skipped."

                        $copyLoginStatus.Status = "Skipped"
                        $copyLoginStatus.Notes = "$($sourceLogin.LoginType) not supported"
                        $copyLoginStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject

                        continue
                    }

                    if ($sourceLogin.IsDisabled) {
                        try {
                            $destLogin.Disable()
                        }
                        catch {
                            $copyLoginStatus.Status = "Successful - but could not disable on destination"
                            $copyLoginStatus.Notes = (Get-ErrorMessage -Record $_).Message
                            $copyLoginStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject

                            Stop-Function -Message "$userName disabled on source, could not be disabled on $destinstance." -Category InvalidOperation -ErrorRecord $_ -Target $destServer  3>$null
                        }
                    }
                    if ($sourceLogin.DenyWindowsLogin) {
                        try {
                            $destLogin.DenyWindowsLogin = $true
                        }
                        catch {
                            $copyLoginStatus.Status = "Successful - but could not deny login on destination"
                            $copyLoginStatus.Notes = (Get-ErrorMessage -Record $_).Message
                            $copyLoginStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject

                            Stop-Function -Message "$userName denied login on source, could not be denied login on $destinstance." -Category InvalidOperation -ErrorRecord $_ -Target $destServer 3>$null
                        }
                    }
                }
                if ($Pscmdlet.ShouldProcess($destinstance, "Updating SQL login $userName permissions")) {
                    Update-SqlPermissions -sourceserver $sourceServer -sourcelogin $sourceLogin -destserver $destServer -destlogin $destLogin
                }

                if ($LoginRenameHashtable.Keys -contains $userName) {
                    $NewLogin = $LoginRenameHashtable[$userName]

                    if ($Pscmdlet.ShouldProcess($destinstance, "Renaming SQL Login $userName to $NewLogin")) {
                        try {
                            Rename-DbaLogin -SqlInstance $destServer -Login $userName -NewLogin $NewLogin

                            $copyLoginStatus.DestinationLogin = $NewLogin
                            $copyLoginStatus.Status = "Successful"
                            $copyLoginStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject

                        }
                        catch {
                            $copyLoginStatus.DestinationLogin = $NewLogin
                            $copyLoginStatus.Status = "Failed to rename"
                            $copyLoginStatus.Notes = (Get-ErrorMessage -Record $_).Message
                            $copyLoginStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject

                            Stop-Function -Message "Issue renaming $userName to $NewLogin" -Category InvalidOperation -ErrorRecord $_ -Target $destServer 3>$null
                        }
                    }
                }
            }
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }
        if ($InputObject) {
            $Source = $InputObject[0].Parent.Name
            $Sourceserver = $InputObject[0].Parent
            $Login = $InputObject.Name
        }
        else {
            try {
                Write-Message -Level Verbose -Message "Connecting to $Source"
                $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
                return
            }
        }
        $sourceVersionMajor = $sourceServer.VersionMajor

        if ($OutFile) {
            Export-DbaLogin -SqlInstance $sourceServer -FilePath $OutFile -Login $Login -ExcludeLogin $ExcludeLogin
            continue
        }

        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            
            $destVersionMajor = $destServer.VersionMajor
            if ($sourceVersionMajor -gt 10 -and $destVersionMajor -lt 11) {
                Stop-Function -Message "Login migration from version $sourceVersionMajor to $destVersionMajor is not supported." -Category InvalidOperation -ErrorRecord $_ -Target $sourceServer
            }

            if ($sourceVersionMajor -lt 8 -or $destVersionMajor -lt 8) {
                Stop-Function -Message "SQL Server 7 and below are not supported." -Category InvalidOperation -ErrorRecord $_ -Target $sourceServer
            }
            
            if ($SyncOnly) {
                if ($Pscmdlet.ShouldProcess($destinstance, "Syncing $Login permissions")) {
                    Sync-DbaLoginPermission -Source $sourceServer -Destination $destServer -Login $Login -ExcludeLogin $ExcludeLogin
                    continue
                }
            }
            
            Write-Message -Level Verbose -Message "Attempting Login Migration."
            Copy-Login -sourceserver $sourceServer -destserver $destServer -Login $Login -Exclude $ExcludeLogin

            if ($SyncSaName) {
                $sa = $sourceServer.Logins | Where-Object id -eq 1
                $destSa = $destServer.Logins | Where-Object id -eq 1
                $saName = $sa.Name
                if ($saName -ne $destSa.name) {
                    Write-Message -Level Verbose -Message "Changing sa username to match source ($saName)."
                    if ($Pscmdlet.ShouldProcess($destinstance, "Changing sa username to match source ($saName)")) {
                        $destSa.Rename($saName)
                        $destSa.Alter()
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlLogin
    }
}
function Copy-DbaQueryStoreConfig {
    <#
        .SYNOPSIS
            Copies the configuration of a Query Store enabled database and sets the copied configuration on other databases.

        .DESCRIPTION
            Copies the configuration of a Query Store enabled database and sets the copied configuration on other databases.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2016 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER SourceDatabase
            Specifies the database to copy the Query Store configuration from.

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2016 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER DestinationDatabase
            Specifies a list of databases that will receive a copy of the Query Store configuration of the SourceDatabase.

        .PARAMETER Exclude
            Specifies a list of databases which will NOT receive a copy of the Query Store configuration.

        .PARAMETER AllDatabases
            If this switch is enabled, the Query Store configuration will be copied to all databases on the destination instance.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Enrico van de Laar ( @evdlaar )
            Tags: QueryStore

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-QueryStoreConfig

        .EXAMPLE
            Copy-DbaQueryStoreConfig -Source ServerA\SQL -SourceDatabase AdventureWorks -Destination ServerB\SQL -AllDatabases

            Copy the Query Store configuration of the AdventureWorks database in the ServerA\SQL instance and apply it on all user databases in the ServerB\SQL Instance.

        .EXAMPLE
            Copy-DbaQueryStoreConfig -Source ServerA\SQL -SourceDatabase AdventureWorks -Destination ServerB\SQL -DestinationDatabase WorldWideTraders

            Copy the Query Store configuration of the AdventureWorks database in the ServerA\SQL instance and apply it to the WorldWideTraders database in the ServerB\SQL Instance.
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]$SourceSqlCredential,
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [object]$SourceDatabase,
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [object[]]$DestinationDatabase,
        [object[]]$Exclude,
        [switch]$AllDatabases,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Write-Message -Message "Connecting to source: $Source." -Level Verbose
        try {
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
        }
        catch {
            Stop-Function -Message "Can't connect to $Source." -ErrorRecord $_ -Target $Source
            return
        }
    }
    
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            # Grab the Query Store configuration from the SourceDatabase through the Get-DbaQueryStoreConfig function
            $SourceQSConfig = Get-DbaDbQueryStoreOptions -SqlInstance $sourceServer -Database $SourceDatabase
            
            if (!$DestinationDatabase -and !$Exclude -and !$AllDatabases) {
                Stop-Function -Message "You must specify databases to execute against using either -DestinationDatabase, -Exclude or -AllDatabases." -Continue
            }
            
            foreach ($destinationServer in $destinstance) {
                
                try {
                    Write-Message -Level Verbose -Message "Connecting to $destinstance"
                    $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential
                }
                catch {
                    Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
                }
                
                # We have to exclude all the system databases since they cannot have the Query Store feature enabled
                $dbs = Get-DbaDatabase -SqlInstance $destServer -ExcludeAllSystemDb
                
                if ($DestinationDatabase.count -gt 0) {
                    $dbs = $dbs | Where-Object { $DestinationDatabase -contains $_.Name }
                }
                
                if ($Exclude.count -gt 0) {
                    $dbs = $dbs | Where-Object { $exclude -notcontains $_.Name }
                }
                
                if ($dbs.count -eq 0) {
                    Stop-Function -Message "No matching databases found. Check the spelling and try again." -Continue
                }
                
                foreach ($db in $dbs) {
                    # skipping the database if the source and destination are the same instance
                    if (($sourceServer.Name -eq $destinationServer) -and ($SourceDatabase -eq $db.Name)) {
                        continue
                    }
                    Write-Message -Message "Processing destination database: $db on $destinationServer." -Level Verbose
                    $copyQueryStoreStatus = [pscustomobject]@{
                        SourceServer = $sourceServer.name
                        SourceDatabase = $SourceDatabase
                        DestinationServer = $destinationServer
                        Name         = $db.name
                        Type         = "QueryStore Configuration"
                        Status       = $null
                        DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                    }
                    
                    if ($db.IsAccessible -eq $false) {
                        $copyQueryStoreStatus.Status = "Skipped"
                        Stop-Function -Message "The database $db on server $destinationServer is not accessible. Skipping database." -Continue
                    }
                    
                    Write-Message -Message "Executing Set-DbaQueryStoreConfig." -Level Verbose
                    # Set the Query Store configuration through the Set-DbaQueryStoreConfig function
                    try {
                        $null = Set-DbaDbQueryStoreOptions -SqlInstance $destinationServer -SqlCredential $DestinationSqlCredential `
                                                        -Database $db.name `
                                                        -State $SourceQSConfig.ActualState `
                                                        -FlushInterval $SourceQSConfig.FlushInterval `
                                                        -CollectionInterval $SourceQSConfig.CollectionInterval `
                                                        -MaxSize $SourceQSConfig.MaxSize `
                                                        -CaptureMode $SourceQSConfig.CaptureMode `
                                                        -CleanupMode $SourceQSConfig.CleanupMode `
                                                        -StaleQueryThreshold $SourceQSConfig.StaleQueryThreshold
                        $copyQueryStoreStatus.Status = "Successful"
                    }
                    catch {
                        $copyQueryStoreStatus.Status = "Failed"
                        Stop-Function -Message "Issue setting Query Store on $db." -Target $db -ErrorRecord $_ -Continue
                    }
                    $copyQueryStoreStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                }
            }
        }
    }
}
function Copy-DbaResourceGovernor {
    <#
        .SYNOPSIS
            Migrates Resource Pools

        .DESCRIPTION
            By default, all non-system resource pools are migrated. If the pool already exists on the destination, it will be skipped unless -Force is used.

            The -ResourcePool parameter is auto-populated for command-line completion and can be used to copy only specific objects.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2008 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER ResourcePool
            Specifies the resource pool(s) to process. Options for this list are auto-populated from the server. If unspecified, all resource pools will be processed.

        .PARAMETER ExcludeResourcePool
            Specifies the resource pool(s) to exclude. Options for this list are auto-populated from the server

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER Force
            If this switch is enabled, the policies will be dropped and recreated on Destination.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration, ResourceGovernor
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaResourceGovernor

        .EXAMPLE
            Copy-DbaResourceGovernor -Source sqlserver2014a -Destination sqlcluster

            Copies all extended event policies from sqlserver2014a to sqlcluster using Windows credentials to connect to the SQL Server instances..

        .EXAMPLE
            Copy-DbaResourceGovernor -Source sqlserver2014a -Destination sqlcluster -SourceSqlCredential $cred

            Copies all extended event policies from sqlserver2014a to sqlcluster using SQL credentials to connect to sqlserver2014a and Windows credentials to connect to sqlcluster.

        .EXAMPLE
            Copy-DbaResourceGovernor -Source sqlserver2014a -Destination sqlcluster -WhatIf

            Shows what would happen if the command were executed.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]$SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [object[]]$ResourcePool,
        [object[]]$ExcludeResourcePool,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 10
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        $sourceClassifierFunction = Get-DbaResourceGovernorClassifierFunction -SqlInstance $sourceServer
        
       foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            $destClassifierFunction = Get-DbaResourceGovernorClassifierFunction -SqlInstance $destServer
            
            $copyResourceGovSetting = [pscustomobject]@{
                SourceServer = $sourceServer.Name
                DestinationServer = $destServer.Name
                Type         = "Resource Governor Settings"
                Name         = "All Settings"
                Status       = $null
                Notes        = $null
                DateTime     = [DbaDateTime](Get-Date)
            }
            
            $copyResourceGovClassifierFunc = [pscustomobject]@{
                SourceServer = $sourceServer.Name
                DestinationServer = $destServer.Name
                Type         = "Resource Governor Settings"
                Name         = "Classifier Function"
                Status       = $null
                Notes        = $null
                DateTime     = [DbaDateTime](Get-Date)
            }
            
            if ($Pscmdlet.ShouldProcess($destinstance, "Updating Resource Governor settings")) {
                if ($destServer.Edition -notmatch 'Enterprise' -and $destServer.Edition -notmatch 'Datacenter' -and $destServer.Edition -notmatch 'Developer') {
                    Write-Message -Level Verbose -Message "The resource governor is not available in this edition of SQL Server. You can manipulate resource governor metadata but you will not be able to apply resource governor configuration. Only Enterprise edition of SQL Server supports resource governor."
                }
                else {
                    try {
                        Write-Message -Level Verbose -Message "Managing classifier function."
                        if (!$sourceClassifierFunction) {
                            $copyResourceGovClassifierFunc.Status = "Skipped"
                            $copyResourceGovClassifierFunc.Notes = $null
                            $copyResourceGovClassifierFunc | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        }
                        else {
                            $fullyQualifiedFunctionName = $sourceClassifierFunction.Schema + "." + $sourceClassifierFunction.Name
                            
                            if (!$destClassifierFunction) {
                                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential
                                $destFunction = $destServer.Databases["master"].UserDefinedFunctions[$sourceClassifierFunction.Name]
                                if ($destFunction) {
                                    Write-Message -Level Verbose -Message "Dropping the function with the source classifier function name."
                                    $destFunction.Drop()
                                }
                                
                                Write-Message -Level Verbose -Message "Creating function."
                                $destServer.Query($sourceClassifierFunction.Script())
                                
                                $sql = "ALTER RESOURCE GOVERNOR WITH (CLASSIFIER_FUNCTION = $fullyQualifiedFunctionName);"
                                Write-Message -Level Debug -Message $sql
                                Write-Message -Level Verbose -Message "Mapping Resource Governor classifier function."
                                $destServer.Query($sql)
                                
                                $copyResourceGovClassifierFunc.Status = "Successful"
                                $copyResourceGovClassifierFunc.Notes = "The new classifier function has been created"
                                $copyResourceGovClassifierFunc | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            }
                            else {
                                if ($Force -eq $false) {
                                    $copyResourceGovClassifierFunc.Status = "Skipped"
                                    $copyResourceGovClassifierFunc.Notes = "A classifier function already exists"
                                    $copyResourceGovClassifierFunc | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                }
                                else {
                                    
                                    $sql = "ALTER RESOURCE GOVERNOR WITH (CLASSIFIER_FUNCTION = NULL);"
                                    Write-Message -Level Debug -Message $sql
                                    Write-Message -Level Verbose -Message "Disabling the Resource Governor."
                                    $destServer.Query($sql)
                                    
                                    $sql = "ALTER RESOURCE GOVERNOR RECONFIGURE;"
                                    Write-Message -Level Debug -Message $sql
                                    Write-Message -Level Verbose -Message "Reconfiguring Resource Governor."
                                    $destServer.Query($sql)
                                    
                                    Write-Message -Level Verbose -Message "Dropping the destination classifier function."
                                    $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential
                                    $destFunction = $destServer.Databases["master"].UserDefinedFunctions[$sourceClassifierFunction.Name]
                                    $destClassifierFunction.Drop()
                                    
                                    Write-Message -Level Verbose -Message "Re-creating the Resource Governor classifier function."
                                    $destServer.Query($sourceClassifierFunction.Script())
                                    
                                    $sql = "ALTER RESOURCE GOVERNOR WITH (CLASSIFIER_FUNCTION = $fullyQualifiedFunctionName);"
                                    Write-Message -Level Debug -Message $sql
                                    Write-Message -Level Verbose -Message "Mapping Resource Governor classifier function."
                                    $destServer.Query($sql)
                                    
                                    $copyResourceGovClassifierFunc.Status = "Successful"
                                    $copyResourceGovClassifierFunc.Notes = "The old classifier function has been overwritten."
                                    $copyResourceGovClassifierFunc | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                }
                            }
                        }
                    }
                    catch {
                        $copyResourceGovSetting.Status = "Failed"
                        $copyResourceGovSetting.Notes = (Get-ErrorMessage -Record $_)
                        $copyResourceGovSetting | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Stop-Function -Message "Not able to update settings." -Target $destServer -ErrorRecord $_
                    }
                }
            }
            
            # Pools
            if ($ResourcePool) {
                $pools = $sourceServer.ResourceGovernor.ResourcePools | Where-Object Name -In $ResourcePool
            }
            elseif ($ExcludeResourcePool) {
                $pool = $sourceServer.ResourceGovernor.ResourcePools | Where-Object Name -NotIn $ExcludeResourcePool
            }
            else {
                $pools = $sourceServer.ResourceGovernor.ResourcePools | Where-Object { $_.Name -notin "internal", "default" }
            }
            
            Write-Message -Level Verbose -Message "Migrating pools."
            foreach ($pool in $pools) {
                $poolName = $pool.Name
                
                $copyResourceGovPool = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Type         = "Resource Governor Pool"
                    Name         = $poolName
                    Status       = $null
                    Notes        = $null
                    DateTime     = [DbaDateTime](Get-Date)
                }
                
                if ($null -ne $destServer.ResourceGovernor.ResourcePools[$poolName]) {
                    if ($force -eq $false) {
                        Write-Message -Level Verbose -Message "Pool '$poolName' was skipped because it already exists on $destinstance. Use -Force to drop and recreate."
                        
                        $copyResourceGovPool.Status = "Skipped"
                        $copyResourceGovPool.Notes = "Already exists"
                        $copyResourceGovPool | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        continue
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Attempting to drop $poolName")) {
                            Write-Message -Level Verbose -Message "Pool '$poolName' exists on $destinstance."
                            Write-Message -Level Verbose -Message "Force specified. Dropping $poolName."
                            
                            try {
                                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential
                                $destPool = $destServer.ResourceGovernor.ResourcePools[$poolName]
                                $workloadGroups = $destPool.WorkloadGroups
                                foreach ($workloadGroup in $workloadGroups) {
                                    $workloadGroup.Drop()
                                }
                                $destPool.Drop()
                                $destServer.ResourceGovernor.Alter()
                            }
                            catch {
                                $copyResourceGovPool.Status = "Failed to drop from Destination"
                                $copyResourceGovPool.Notes = (Get-ErrorMessage -Record $_)
                                $copyResourceGovPool | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                
                                Stop-Function -Message "Unable to drop: $_ Moving on." -Target $destPool -ErrorRecord $_ -Continue
                            }
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Migrating pool $poolName")) {
                    try {
                        $sql = $pool.Script() | Out-String
                        Write-Message -Level Debug -Message $sql
                        Write-Message -Level Verbose -Message "Copying pool $poolName."
                        $destServer.Query($sql)
                        
                        $copyResourceGovPool.Status = "Successful"
                        $copyResourceGovPool | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        $workloadGroups = $pool.WorkloadGroups
                        foreach ($workloadGroup in $workloadGroups) {
                            $workgroupName = $workloadGroup.Name
                            
                            $copyResourceGovWorkGroup = [pscustomobject]@{
                                SourceServer = $sourceServer.Name
                                DestinationServer = $destServer.Name
                                Type         = "Resource Governor Pool Workgroup"
                                Name         = $workgroupName
                                Status       = $null
                                Notes        = $null
                                DateTime     = [DbaDateTime](Get-Date)
                            }
                            
                            $sql = $workloadGroup.Script() | Out-String
                            Write-Message -Level Debug -Message $sql
                            Write-Message -Level Verbose -Message "Copying $workgroupName."
                            $destServer.Query($sql)
                            
                            $copyResourceGovWorkGroup.Status = "Successful"
                            $copyResourceGovWorkGroup | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        }
                    }
                    catch {
                        if ($copyResourceGovWorkGroup) {
                            $copyResourceGovWorkGroup.Status = "Failed"
                            $copyResourceGovWorkGroup.Notes = (Get-ErrorMessage -Record $_)
                            $copyResourceGovWorkGroup | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        }
                        Stop-Function -Message "Unable to migrate pool." -Target $pool -ErrorRecord $_
                    }
                }
            }
            
            if ($Pscmdlet.ShouldProcess($destinstance, "Reconfiguring")) {
                if ($destServer.Edition -notmatch 'Enterprise' -and $destServer.Edition -notmatch 'Datacenter' -and $destServer.Edition -notmatch 'Developer') {
                    Write-Message -Level Verbose -Message "The resource governor is not available in this edition of SQL Server. You can manipulate resource governor metadata but you will not be able to apply resource governor configuration. Only Enterprise edition of SQL Server supports resource governor."
                }
                else {
                    
                    Write-Message -Level Verbose -Message "Reconfiguring Resource Governor."
                    try {
                        if (!$sourceServer.ResourceGovernor.Enabled) {
                            $sql = "ALTER RESOURCE GOVERNOR DISABLE"
                            $destServer.Query($sql)
                        }
                        else {
                            $sql = "ALTER RESOURCE GOVERNOR RECONFIGURE"
                            $destServer.Query($sql)
                        }
                    }
                    catch {
                        $altermsg = $_.Exception
                    }
                    
                    
                    $copyResourceGovReconfig = [pscustomobject]@{
                        SourceServer = $sourceServer.Name
                        DestinationServer = $destServer.Name
                        Type         = "Reconfigure Resource Governor"
                        Name         = "Reconfigure Resource Governor"
                        Status       = "Successful"
                        Notes        = $altermsg
                        DateTime     = [DbaDateTime](Get-Date)
                    }
                    $copyResourceGovReconfig | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlResourceGovernor
    }
}
function Copy-DbaServerAudit {
    <#
        .SYNOPSIS
            Copy-DbaServerAudit migrates server audits from one SQL Server to another.

        .DESCRIPTION
            By default, all audits are copied. The -Audit parameter is auto-populated for command-line completion and can be used to copy only specific audits.

            If the audit already exists on the destination, it will be skipped unless -Force is used.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Audit
            The audit(s) to process. Options for this list are auto-populated from the server. If unspecified, all audits will be processed.

        .PARAMETER ExcludeAudit
            The audit(s) to exclude. Options for this list are auto-populated from the server.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER Force
            If this switch is enabled, the audits will be dropped and recreated on Destination.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaServerAudit

        .EXAMPLE
            Copy-DbaServerAudit -Source sqlserver2014a -Destination sqlcluster

            Copies all server audits from sqlserver2014a to sqlcluster, using Windows credentials. If audits with the same name exist on sqlcluster, they will be skipped.

        .EXAMPLE
            Copy-DbaServerAudit -Source sqlserver2014a -Destination sqlcluster -Audit tg_noDbDrop -SourceSqlCredential $cred -Force

            Copies a single audit, the tg_noDbDrop audit from sqlserver2014a to sqlcluster, using SQL credentials for sqlserver2014a and Windows credentials for sqlcluster. If an audit with the same name exists on sqlcluster, it will be dropped and recreated because -Force was used.

        .EXAMPLE
            Copy-DbaServerAudit -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force

            Shows what would happen if the command were executed using force.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]
        $SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]
        $DestinationSqlCredential,
        [object[]]$Audit,
        [object[]]$ExcludeAudit,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 10
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        $serverAudits = $sourceServer.Audits
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            $destAudits = $destServer.Audits
            foreach ($currentAudit in $serverAudits) {
                $auditName = $currentAudit.Name
                
                $copyAuditStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name         = $auditName
                    Type         = "Server Audit"
                    Status       = $null
                    Notes        = $null
                    DateTime     = [DbaDateTime](Get-Date)
                }
                
                if ($Audit -and $auditName -notin $Audit -or $auditName -in $ExcludeAudit) {
                    continue
                }
                
                $sql = $currentAudit.Script() | Out-String
                
                if ($destAudits.Name -contains $auditName) {
                    if ($force -eq $false) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Server audit $auditName exists at destination. Use -Force to drop and migrate.")) {
                            $copyAuditStatus.Status = "Skipped"
                            $copyAuditStatus.Notes = "Already exists"
                            Write-Message -Level Verbose -Message "Server audit $auditName exists at destination. Use -Force to drop and migrate."
                        }
                        continue
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Dropping server audit $auditName")) {
                            try {
                                Write-Message -Level Verbose -Message "Dropping server audit $auditName."
                                foreach ($spec in $destServer.ServerAuditSpecifications) {
                                    if ($auditSpecification.Auditname -eq $auditName) {
                                        $auditSpecification.Drop()
                                    }
                                }
                                
                                $destServer.audits[$auditName].Disable()
                                $destServer.audits[$auditName].Alter()
                                $destServer.audits[$auditName].Drop()
                            }
                            catch {
                                $copyAuditStatus.Status = "Failed"
                                $copyAuditStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                
                                Stop-Function -Message "Issue dropping audit from destination." -Target $auditName -ErrorRecord $_
                            }
                        }
                    }
                }
                
                if ($null -ne ($currentAudit.Filepath) -and -not (Test-DbaSqlPath -SqlInstance $destServer -Path $currentAudit.Filepath)) {
                    if ($Force -eq $false) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "$($currentAudit.Filepath) does not exist on $destinstance. Skipping $auditName. Specify -Force to create the directory.")) {
                            $copyAuditStatus.Status = "Skipped"
                            $copyAuditStatus.Notes = "$($currentAudit.Filepath) does not exist on $destinstance. Skipping $auditName. Specify -Force to create the directory."
                            $copyAuditStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        }
                        continue
                    }
                    else {
                        Write-Message -Level Verbose -Message "Force specified. Creating directory."
                        
                        $destNetBios = Resolve-NetBiosName $destServer
                        $path = Join-AdminUnc $destNetBios $currentAudit.Filepath
                        $root = $currentAudit.Filepath.Substring(0, 3)
                        $rootUnc = Join-AdminUnc $destNetBios $root
                        
                        if ((Test-Path $rootUnc) -eq $true) {
                            if ($Pscmdlet.ShouldProcess($destinstance, "Creating directory $($currentAudit.Filepath)")) {
                                try {
                                    $null = New-DbaSqlDirectory -SqlInstance $destServer -Path $currentAudit.Filepath -EnableException
                                }
                                catch {
                                    Write-Message -Level Warning -Message "Couldn't create directory $($currentAudit.Filepath). Using default data directory."
                                    $datadir = Get-SqlDefaultPaths $destServer data
                                    $sql = $sql.Replace($currentAudit.FilePath, $datadir)
                                }
                            }
                        }
                        else {
                            $datadir = Get-SqlDefaultPaths $destServer data
                            $sql = $sql.Replace($currentAudit.FilePath, $datadir)
                        }
                    }
                }
                if ($Pscmdlet.ShouldProcess($destinstance, "Creating server audit $auditName")) {
                    try {
                        Write-Message -Level Verbose -Message "File path $($currentAudit.Filepath) exists on $destinstance."
                        Write-Message -Level Verbose -Message "Copying server audit $auditName."
                        $destServer.Query($sql)
                        
                        $copyAuditStatus.Status = "Successful"
                        $copyAuditStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copyAuditStatus.Status = "Failed"
                        $copyAuditStatus.Notes = (Get-ErrorMessage -Record $_)
                        $copyAuditStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Stop-Function -Message "Issue creating audit." -Target $auditName -ErrorRecord $_
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlAudit
    }
}
function Copy-DbaServerAuditSpecification {
    <#
        .SYNOPSIS
            Copy-DbaServerAuditSpecification migrates server audit specifications from one SQL Server to another.

        .DESCRIPTION
            By default, all audits are copied. The -AuditSpecification parameter is auto-populated for command-line completion and can be used to copy only specific audits.

            If the audit specification already exists on the destination, it will be skipped unless -Force is used.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER AuditSpecification
            The Server Audit Specification(s) to process. Options for this list are auto-populated from the server. If unspecified, all Server Audit Specifications will be processed.

        .PARAMETER ExcludeAuditSpecification
            The Server Audit Specification(s) to exclude. Options for this list are auto-populated from the server

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER Force
            If this switch is enabled, the Audits Specifications will be dropped and recreated on Destination.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration,ServerAudit,AuditSpecification
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaServerAuditSpecification

        .EXAMPLE
            Copy-DbaServerAuditSpecification -Source sqlserver2014a -Destination sqlcluster

            Copies all server audits from sqlserver2014a to sqlcluster using Windows credentials to connect. If audits with the same name exist on sqlcluster, they will be skipped.

        .EXAMPLE
            Copy-DbaServerAuditSpecification -Source sqlserver2014a -Destination sqlcluster -ServerAuditSpecification tg_noDbDrop -SourceSqlCredential $cred -Force

            Copies a single audit, the tg_noDbDrop audit from sqlserver2014a to sqlcluster using SQL credentials to connect to sqlserver2014a and Windows credentials to connect to sqlcluster. If an audit specification with the same name exists on sqlcluster, it will be dropped and recreated because -Force was used.

        .EXAMPLE
            Copy-DbaServerAuditSpecification -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force

            Shows what would happen if the command were executed using force.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]$SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [object[]]$AuditSpecification,
        [object[]]$ExcludeAuditSpecification,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 10
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        
        if (!(Test-SqlSa -SqlInstance $sourceServer -SqlCredential $SourceSqlCredential)) {
            Stop-Function -Message "Not a sysadmin on $source. Quitting."
            return
        }
        
        $AuditSpecifications = $sourceServer.ServerAuditSpecifications
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            
            if (!(Test-SqlSa -SqlInstance $destServer -SqlCredential $DestinationSqlCredential)) {
                Stop-Function -Message "Not a sysadmin on $destinstance. Quitting."
                return
            }
            
            if ($destServer.VersionMajor -lt $sourceServer.VersionMajor) {
                Stop-Function -Message "Migration from version $($destServer.VersionMajor) to version $($sourceServer.VersionMajor) is not supported."
                return
            }
            $destAudits = $destServer.ServerAuditSpecifications
            foreach ($auditSpec in $AuditSpecifications) {
                $auditSpecName = $auditSpec.Name
                
                $copyAuditSpecStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Type         = "Server Audit Specification"
                    Name         = $auditSpecName
                    Status       = $null
                    Notes        = $null
                    DateTime     = [DbaDateTime](Get-Date)
                }
                
                if ($AuditSpecification -and $auditSpecName -notin $AuditSpecification -or $auditSpecName -in $ExcludeAuditSpecification) {
                    continue
                }
                
                $destServer.Audits.Refresh()
                if ($destServer.Audits.Name -notcontains $auditSpec.AuditName) {
                    if ($Pscmdlet.ShouldProcess($destinstance, "Audit $($auditSpec.AuditName) does not exist on $destinstance. Skipping $auditSpecName.")) {
                        $copyAuditSpecStatus.Status = "Skipped"
                        $copyAuditSpecStatus.Notes = "Audit $($auditSpec.AuditName) does not exist on $destinstance. Skipping $auditSpecName."
                        Write-Message -Level Warning -Message "Audit $($auditSpec.AuditName) does not exist on $destinstance. Skipping $auditSpecName."
                        $copyAuditSpecStatus
                    }
                    continue
                }
                
                if ($destAudits.name -contains $auditSpecName) {
                    if ($force -eq $false) {
                        Write-Message -Level Verbose -Message "Server audit $auditSpecName exists at destination. Use -Force to drop and migrate."
                        
                        $copyAuditSpecStatus.Status = "Skipped"
                        $copyAuditSpecStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        continue
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Dropping server audit $auditSpecName and recreating")) {
                            try {
                                Write-Message -Level Verbose -Message "Dropping server audit $auditSpecName"
                                $destServer.ServerAuditSpecifications[$auditSpecName].Drop()
                            }
                            catch {
                                $copyAuditSpecStatus.Status = "Failed"
                                $copyAuditSpecStatus.Notes = (Get-ErrorMessage -Record $_)
                                $copyAuditSpecStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                
                                Stop-Function -Message "Issue dropping audit spec" -Target $auditSpecName -ErrorRecord $_ -Continue
                            }
                        }
                    }
                }
                if ($Pscmdlet.ShouldProcess($destinstance, "Creating server audit $auditSpecName")) {
                    try {
                        Write-Message -Level Verbose -Message "Copying server audit $auditSpecName"
                        $sql = $auditSpec.Script() | Out-String
                        Write-Message -Level Debug -Message $sql
                        $destServer.Query($sql)
                        
                        $copyAuditSpecStatus.Status = "Successful"
                        $copyAuditSpecStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copyAuditSpecStatus.Status = "Failed"
                        $copyAuditSpecStatus.Notes = (Get-ErrorMessage -Record $_)
                        $copyAuditSpecStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Stop-Function -Message "Issue creating audit spec on destination" -Target $auditSpecName -ErrorRecord $_
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlAuditSpecification
    }
}
function Copy-DbaServerTrigger {
    <#
        .SYNOPSIS
            Copy-DbaServerTrigger migrates server triggers from one SQL Server to another.

        .DESCRIPTION
            By default, all triggers are copied. The -ServerTrigger parameter is auto-populated for command-line completion and can be used to copy only specific triggers.

            If the trigger already exists on the destination, it will be skipped unless -Force is used.

        .PARAMETER Source
            Source SQL Server.You must have sysadmin access and server version must be SQL Server version 2000 or greater.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination Sql Server. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER ServerTrigger
            The Server Trigger(s) to process - this list is auto-populated from the server. If unspecified, all Server Triggers will be processed.

        .PARAMETER ExcludeServerTrigger
            The Server Trigger(s) to exclude - this list is auto-populated from the server

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER Force
            Drops and recreates the Trigger if it exists

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaServerTrigger

        .EXAMPLE
            Copy-DbaServerTrigger -Source sqlserver2014a -Destination sqlcluster

            Copies all server triggers from sqlserver2014a to sqlcluster, using Windows credentials. If triggers with the same name exist on sqlcluster, they will be skipped.

        .EXAMPLE
            Copy-DbaServerTrigger -Source sqlserver2014a -Destination sqlcluster -ServerTrigger tg_noDbDrop -SourceSqlCredential $cred -Force

            Copies a single trigger, the tg_noDbDrop trigger from sqlserver2014a to sqlcluster, using SQL credentials for sqlserver2014a and Windows credentials for sqlcluster. If a trigger with the same name exists on sqlcluster, it will be dropped and recreated because -Force was used.

        .EXAMPLE
            Copy-DbaServerTrigger -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force

            Shows what would happen if the command were executed using force.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]
        $SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]
        $DestinationSqlCredential,
        [object[]]$ServerTrigger,
        [object[]]$ExcludeServerTrigger,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 9
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        $serverTriggers = $sourceServer.Triggers
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            if ($destServer.VersionMajor -lt $sourceServer.VersionMajor) {
                Stop-Function -Message "Migration from version $($destServer.VersionMajor) to version $($sourceServer.VersionMajor) is not supported."
                return
            }
            $destTriggers = $destServer.Triggers
            
            foreach ($trigger in $serverTriggers) {
                $triggerName = $trigger.Name
                
                $copyTriggerStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name         = $triggerName
                    Type         = "Server Trigger"
                    Status       = $null
                    Notes        = $null
                    DateTime     = [DbaDateTime](Get-Date)
                }
                
                if ($ServerTrigger -and $triggerName -notin $ServerTrigger -or $triggerName -in $ExcludeServerTrigger) {
                    continue
                }
                
                if ($destTriggers.Name -contains $triggerName) {
                    if ($force -eq $false) {
                        Write-Message -Level Verbose -Message "Server trigger $triggerName exists at destination. Use -Force to drop and migrate."
                        
                        $copyTriggerStatus.Status = "Skipped"
                        $copyTriggerStatus.Status = "Already exists"
                        $copyTriggerStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        continue
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Dropping server trigger $triggerName and recreating")) {
                            try {
                                Write-Message -Level Verbose -Message "Dropping server trigger $triggerName"
                                $destServer.Triggers[$triggerName].Drop()
                            }
                            catch {
                                $copyTriggerStatus.Status = "Failed"
                                $copyTriggerStatus.Notes = (Get-ErrorMessage -Record $_)
                                $copyTriggerStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                
                                Stop-Function -Message "Issue dropping trigger on destination" -Target $triggerName -ErrorRecord $_ -Continue
                            }
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Creating server trigger $triggerName")) {
                    try {
                        Write-Message -Level Verbose -Message "Copying server trigger $triggerName"
                        $sql = $trigger.Script() | Out-String
                        $sql = $sql -replace "CREATE TRIGGER", "`nGO`nCREATE TRIGGER"
                        $sql = $sql -replace "ENABLE TRIGGER", "`nGO`nENABLE TRIGGER"
                        Write-Message -Level Debug -Message $sql
                        
                        foreach ($query in ($sql -split '\nGO\b')) {
                            $destServer.Query($query) | Out-Null
                        }
                        
                        $copyTriggerStatus.Status = "Successful"
                        $copyTriggerStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copyTriggerStatus.Status = "Failed"
                        $copyTriggerStatus.Notes = (Get-ErrorMessage -Record $_)
                        $copyTriggerStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Stop-Function -Message "Issue creating trigger on destination" -Target $triggerName -ErrorRecord $_
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlServerTrigger
    }
}
function Copy-DbaSpConfigure {
    <#
        .SYNOPSIS
            Copy-DbaSpConfigure migrates configuration values from one SQL Server to another.

        .DESCRIPTION
            By default, all configuration values are copied. The -ConfigName parameter is auto-populated for command-line completion and can be used to copy only specific configs.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER ConfigName
            Specifies the configuration setting to process. Options for this list are auto-populated from the server. If unspecified, all ConfigNames will be processed.

        .PARAMETER ExcludeConfigName
            Specifies the configuration settings to exclude. Options for this list are auto-populated from the server.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration, Configure, SpConfigure
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaSpConfigure

        .EXAMPLE
            Copy-DbaSpConfigure -Source sqlserver2014a -Destination sqlcluster

            Copies all sp_configure settings from sqlserver2014a to sqlcluster

        .EXAMPLE
            Copy-DbaSpConfigure -Source sqlserver2014a -Destination sqlcluster -ConfigName DefaultBackupCompression, IsSqlClrEnabled -SourceSqlCredential $cred -Force

            Copies the values for IsSqlClrEnabled and DefaultBackupCompression from sqlserver2014a to sqlcluster using SQL credentials to authenticate to sqlserver2014a and Windows credentials to authenticate to sqlcluster.

        .EXAMPLE
            Copy-DbaSpConfigure -Source sqlserver2014a -Destination sqlcluster -ExcludeConfigName DefaultBackupCompression, IsSqlClrEnabled

            Copies all configs except for IsSqlClrEnabled and DefaultBackupCompression, from sqlserver2014a to sqlcluster.

        .EXAMPLE
            Copy-DbaSpConfigure -Source sqlserver2014a -Destination sqlcluster -WhatIf

            Shows what would happen if the command were executed.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]$SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [object[]]$ConfigName,
        [object[]]$ExcludeConfigName,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
            $sourceProps = Get-DbaSpConfigure -SqlInstance $sourceServer
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential
                $destProps = Get-DbaSpConfigure -SqlInstance $destServer
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            
            foreach ($sourceProp in $sourceProps) {
                $displayName = $sourceProp.DisplayName
                $sConfigName = $sourceProp.ConfigName
                $sConfiguredValue = $sourceProp.ConfiguredValue
                $requiresRestart = $sourceProp.IsDynamic
                
                $copySpConfigStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name         = $sConfigName
                    Type         = "Configuration Value"
                    Status       = $null
                    Notes        = $null
                    DateTime     = [DbaDateTime](Get-Date)
                }
                
                if ($ConfigName -and $sConfigName -notin $ConfigName -or $sConfigName -in $ExcludeConfigName) {
                    continue
                }
                
                $destProp = $destProps | Where-Object ConfigName -eq $sConfigName
                if (!$destProp) {
                    Write-Message -Level Verbose -Message "Configuration $sConfigName ('$displayName') does not exist on the destination instance."
                    
                    $copySpConfigStatus.Status = "Skipped"
                    $copySpConfigStatus.Notes = "Configuration does not exist on destination"
                    $copySpConfigStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    
                    continue
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Updating $sConfigName [$displayName]")) {
                    try {
                        $destOldConfigValue = $destProp.ConfiguredValue
                        
                        if ($sConfiguredValue -ne $destOldConfigValue) {
                            $result = Set-DbaSpConfigure -SqlInstance $destServer -Name $sConfigName -Value $sConfiguredValue -EnableException -WarningAction SilentlyContinue
                            if ($result) {
                                Write-Message -Level Verbose -Message "Updated $($destProp.ConfigName) ($($destProp.DisplayName)) from $destOldConfigValue to $sConfiguredValue."
                            }
                        }
                        if ($requiresRestart -eq $false) {
                            Write-Message -Level Verbose -Message "Configuration option $sConfigName ($displayName) requires restart."
                            $copySpConfigStatus.Notes = "Requires restart"
                        }
                        $copySpConfigStatus.Status = "Successful"
                        $copySpConfigStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        if ($_.Exception -match 'the same as the') {
                            $copySpConfigStatus.Status = "Successful"
                        }
                        else {
                            $copySpConfigStatus.Status = "Failed"
                            $copySpConfigStatus.Notes = (Get-ErrorMessage -Record $_)
                        }
                        $copySpConfigStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Stop-Function -Message "Could not set $($destProp.ConfigName) to $sConfiguredValue." -Target $sConfigName -ErrorRecord $_
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlSpConfigure
    }
}
function Copy-DbaSqlDataCollector {
    <#
        .SYNOPSIS
            Migrates user SQL Data Collector collection sets. SQL Data Collector configuration is on the agenda, but it's hard.

        .DESCRIPTION
            By default, all data collector objects are migrated. If the object already exists on the destination, it will be skipped unless -Force is used.

            The -CollectionSet parameter is auto-populated for command-line completion and can be used to copy only specific objects.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination Sql Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER CollectionSet
            The collection set(s) to process - this list is auto-populated from the server. If unspecified, all collection sets will be processed.

        .PARAMETER ExcludeCollectionSet
            The collection set(s) to exclude - this list is auto-populated from the server

        .PARAMETER NoServerReconfig
            Upcoming parameter to enable server reconfiguration

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER Force
            If collection sets exists on destination server, it will be dropped and recreated.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration,DataCollection
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaSqlDataCollector

        .EXAMPLE
            Copy-DbaSqlDataCollector -Source sqlserver2014a -Destination sqlcluster

            Copies all Data Collector Objects and Configurations from sqlserver2014a to sqlcluster, using Windows credentials.

        .EXAMPLE
            Copy-DbaSqlDataCollector -Source sqlserver2014a -Destination sqlcluster -SourceSqlCredential $cred

            Copies all Data Collector Objects and Configurations from sqlserver2014a to sqlcluster, using SQL credentials for sqlserver2014a and Windows credentials for sqlcluster.

        .EXAMPLE
            Copy-DbaSqlDataCollector -Source sqlserver2014a -Destination sqlcluster -WhatIf

            Shows what would happen if the command were executed.

        .EXAMPLE
            Copy-DbaSqlDataCollector -Source sqlserver2014a -Destination sqlcluster -CollectionSet 'Server Activity', 'Table Usage Analysis'

            Copies two Collection Sets, Server Activity and Table Usage Analysis, from sqlserver2014a to sqlcluster.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]
        $SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]
        $DestinationSqlCredential,
        [object[]]$CollectionSet,
        [object[]]$ExcludeCollectionSet,
        [switch]$NoServerReconfig,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 10
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        $sourceSqlConn = $sourceServer.ConnectionContext.SqlConnectionObject
        $sourceSqlStoreConnection = New-Object Microsoft.SqlServer.Management.Sdk.Sfc.SqlStoreConnection $sourceSqlConn
        $sourceStore = New-Object Microsoft.SqlServer.Management.Collector.CollectorConfigStore $sourceSqlStoreConnection
        $configDb = $sourceStore.ScriptAlter().GetScript() | Out-String
        $configDb = $configDb -replace [Regex]::Escape("'$source'"), "'$destReplace'"
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            if ($NoServerReconfig -eq $false) {
                if ($Pscmdlet.ShouldProcess($destinstance, "Server reconfiguration not yet supported. Only Collection Set migration will be migrated at this time.")) {
                    Write-Message -Level Verbose -Message "Server reconfiguration not yet supported. Only Collection Set migration will be migrated at this time."
                    $NoServerReconfig = $true
                    
            <# for future use when this support is added #>
                    $copyServerConfigStatus = [pscustomobject]@{
                        SourceServer = $sourceServer.Name
                        DestinationServer = $destServer.Name
                        Name         = $userName
                        Type         = "Data Collection Server Config"
                        Status       = "Skipped"
                        Notes        = "Not supported at this time"
                        DateTime     = [DbaDateTime](Get-Date)
                    }
                    $copyServerConfigStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                }
            }
            $destSqlConn = $destServer.ConnectionContext.SqlConnectionObject
            $destSqlStoreConnection = New-Object Microsoft.SqlServer.Management.Sdk.Sfc.SqlStoreConnection $destSqlConn
            $destStore = New-Object Microsoft.SqlServer.Management.Collector.CollectorConfigStore $destSqlStoreConnection
            
            if (!$NoServerReconfig) {
                if ($Pscmdlet.ShouldProcess($destinstance, "Attempting to modify Data Collector configuration")) {
                    try {
                        $sql = "Unknown at this time"
                        $destServer.Query($sql)
                        $destStore.Alter()
                    }
                    catch {
                        $copyServerConfigStatus.Status = "Failed"
                        $copyServerConfigStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        Stop-Function -Message "Issue modifying Data Collector configuration" -Target $destServer -ErrorRecord $_
                    }
                }
            }
            
            if ($destStore.Enabled -eq $false) {
                Write-Message -Level Verbose -Message "The Data Collector must be setup initially for Collection Sets to be migrated. Setup the Data Collector and try again."
                continue
            }
            
            $storeCollectionSets = $sourceStore.CollectionSets | Where-Object { $_.IsSystem -eq $false }
            if ($CollectionSet) {
                $storeCollectionSets = $storeCollectionSets | Where-Object Name -In $CollectionSet
            }
            if ($ExcludeCollectionSet) {
                $storeCollectionSets = $storeCollectionSets | Where-Object Name -NotIn $ExcludeCollectionSet
            }
            
            Write-Message -Level Verbose -Message "Migrating collection sets"
            foreach ($set in $storeCollectionSets) {
                $collectionName = $set.Name
                
                $copyCollectionSetStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name         = $collectionName
                    Type         = "Collection Set"
                    Status       = $null
                    Notes        = $null
                    DateTime     = [DbaDateTime](Get-Date)
                }
                
                if ($null -ne $destStore.CollectionSets[$collectionName]) {
                    if ($force -eq $false) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Collection Set '$collectionName' was skipped because it already exists on $destinstance. Use -Force to drop and recreate")) {
                            Write-Message -Level Verbose -Message "Collection Set '$collectionName' was skipped because it already exists on $destinstance. Use -Force to drop and recreate"
                            
                            $copyCollectionSetStatus.Status = "Skipped"
                            $copyCollectionSetStatus.Notes = "Already exists"
                            $copyCollectionSetStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        }
                        continue
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Attempting to drop $collectionName")) {
                            Write-Message -Level Verbose -Message "Collection Set '$collectionName' exists on $destinstance"
                            Write-Message -Level Verbose -Message "Force specified. Dropping $collectionName."
                            
                            try {
                                $destStore.CollectionSets[$collectionName].Drop()
                            }
                            catch {
                                $copyCollectionSetStatus.Status = "Failed to drop on destination"
                                $copyCollectionSetStatus.Notes = (Get-ErrorMessage -Record $_)
                                $copyCollectionSetStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                Stop-Function -Message "Issue dropping collection" -Target $collectionName -ErrorRecord $_ -Continue
                            }
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Migrating collection set $collectionName")) {
                    try {
                        $sql = $set.ScriptCreate().GetScript() | Out-String
                        $sql = $sql -replace [Regex]::Escape("'$source'"), "'$destinstance'"
                        Write-Message -Level Debug -Message $sql
                        Write-Message -Level Verbose -Message "Migrating collection set $collectionName"
                        $destServer.Query($sql)
                        
                        $copyCollectionSetStatus.Status = "Successful"
                        $copyCollectionSetStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copyCollectionSetStatus.Status = "Failed to create collection"
                        $copyCollectionSetStatus.Notes = (Get-ErrorMessage -Record $_)
                        
                        Stop-Function -Message "Issue creating collection set" -Target $collectionName -ErrorRecord $_
                    }
                    
                    try {
                        if ($set.IsRunning) {
                            Write-Message -Level Verbose -Message "Starting collection set $collectionName"
                            $destStore.CollectionSets.Refresh()
                            $destStore.CollectionSets[$collectionName].Start()
                        }
                        
                        $copyCollectionSetStatus.Status = "Successful started Collection"
                        $copyCollectionSetStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copyCollectionSetStatus.Status = "Failed to start collection"
                        $copyCollectionSetStatus.Notes = (Get-ErrorMessage -Record $_)
                        $copyCollectionSetStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Stop-Function -Message "Issue starting collection set" -Target $collectionName -ErrorRecord $_
                    }
                }
            }
        }
    }
    end {
        if (Test-FunctionInterrupt) { return }
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlDataCollector
    }
}
function Copy-DbaSqlPolicyManagement {
    <#
        .SYNOPSIS
            Migrates SQL Policy Based Management Objects, including both policies and conditions.

        .DESCRIPTION
            By default, all policies and conditions are copied. If an object already exist on the destination, it will be skipped unless -Force is used.

            The -Policy and -Condition parameters are auto-populated for command-line completion and can be used to copy only specific objects.

        .PARAMETER Source
            Source SQL Server.You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination Sql Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Policy
            The policy(ies) to process - this list is auto-populated from the server. If unspecified, all policies will be processed.

        .PARAMETER ExcludePolicy
            The policy(ies) to exclude - this list is auto-populated from the server

        .PARAMETER Condition
            The condition(s) to process - this list is auto-populated from the server. If unspecified, all conditions will be processed.

        .PARAMETER ExcludeCondition
            The condition(s) to exclude - this list is auto-populated from the server

        .PARAMETER Force
            If policies exists on destination server, it will be dropped and recreated.

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaSqlPolicyManagement

        .EXAMPLE
            Copy-DbaSqlPolicyManagement -Source sqlserver2014a -Destination sqlcluster

            Copies all policies and conditions from sqlserver2014a to sqlcluster, using Windows credentials.

        .EXAMPLE
            Copy-DbaSqlPolicyManagement -Source sqlserver2014a -Destination sqlcluster -SourceSqlCredential $cred

            Copies all policies and conditions from sqlserver2014a to sqlcluster, using SQL credentials for sqlserver2014a and Windows credentials for sqlcluster.

        .EXAMPLE
            Copy-DbaSqlPolicyManagement -Source sqlserver2014a -Destination sqlcluster -WhatIf

            Shows what would happen if the command were executed.

        .EXAMPLE
            Copy-DbaSqlPolicyManagement -Source sqlserver2014a -Destination sqlcluster -Policy 'xp_cmdshell must be disabled'

            Copies only one policy, 'xp_cmdshell must be disabled' from sqlserver2014a to sqlcluster. No conditions are migrated.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]
        $SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]
        $DestinationSqlCredential,
        [object[]]$Policy,
        [object[]]$ExcludePolicy,
        [object[]]$Condition,
        [object[]]$ExcludeCondition,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 10
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        $sourceSqlConn = $sourceServer.ConnectionContext.SqlConnectionObject
        $sourceSqlStoreConnection = New-Object Microsoft.SqlServer.Management.Sdk.Sfc.SqlStoreConnection $sourceSqlConn
        $sourceStore = New-Object  Microsoft.SqlServer.Management.DMF.PolicyStore $sourceSqlStoreConnection
        $storePolicies = $sourceStore.Policies | Where-Object { $_.IsSystemObject -eq $false }
        $storeConditions = $sourceStore.Conditions | Where-Object { $_.IsSystemObject -eq $false }
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            $destSqlConn = $destServer.ConnectionContext.SqlConnectionObject
            $destSqlStoreConnection = New-Object Microsoft.SqlServer.Management.Sdk.Sfc.SqlStoreConnection $destSqlConn
            $destStore = New-Object  Microsoft.SqlServer.Management.DMF.PolicyStore $destSqlStoreConnection
            
            if ($Policy) {
                $storePolicies = $storePolicies | Where-Object Name -In $Policy
            }
            if ($ExcludePolicy) {
                $storePolicies = $storePolicies | Where-Object Name -NotIn $ExcludePolicy
            }
            if ($Condition) {
                $storeConditions = $storeConditions | Where-Object Name -In $Condition
            }
            if ($ExcludeCondition) {
                $storeConditions = $storeConditions | Where-Object Name -NotIn $ExcludeCondition
            }
            
            if ($Policy -and $Condition) {
                $storeConditions = $null
                $storePolicies = $null
            }
            
        <#
                        Conditions
        #>
            
            Write-Message -Level Verbose -Message "Migrating conditions"
            foreach ($condition in $storeConditions) {
                $conditionName = $condition.Name
                
                $copyConditionStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name         = $conditionName
                    Type         = "Policy Condition"
                    Status       = $null
                    Notes        = $null
                    DateTime     = [DbaDateTime](Get-Date)
                }
                
                if ($null -ne $destStore.Conditions[$conditionName]) {
                    if ($force -eq $false) {
                        Write-Message -Level Verbose -Message "condition '$conditionName' was skipped because it already exists on $destinstance. Use -Force to drop and recreate"
                        
                        $copyConditionStatus.Status = "Skipped"
                        $copyConditionStatus.Notes = "Already exists"
                        $copyConditionStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        continue
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Attempting to drop $conditionName")) {
                            Write-Message -Level Verbose -Message "Condition '$conditionName' exists on $destinstance. Force specified. Dropping $conditionName."
                            
                            try {
                                $dependentPolicies = $destStore.Conditions[$conditionName].EnumDependentPolicies()
                                foreach ($dependent in $dependentPolicies) {
                                    $dependent.Drop()
                                    $destStore.Conditions.Refresh()
                                }
                                $destStore.Conditions[$conditionName].Drop()
                            }
                            catch {
                                $copyConditionStatus.Status = "Failed"
                                $copyConditionStatus.Notes = (Get-ErrorMessage -Record $_).Message
                                $copyConditionStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                Stop-Function -Message "Issue dropping condition on $destinstance" -Target $conditionName -ErrorRecord $_ -Continue
                            }
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Migrating condition $conditionName")) {
                    try {
                        $sql = $condition.ScriptCreate().GetScript() | Out-String
                        Write-Message -Level Debug -Message $sql
                        Write-Message -Level Verbose -Message "Copying condition $conditionName"
                        $null = $destServer.Query($sql)
                        $destStore.Conditions.Refresh()
                        
                        $copyConditionStatus.Status = "Successful"
                        $copyConditionStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copyConditionStatus.Status = "Failed"
                        $copyConditionStatus.Notes = (Get-ErrorMessage -Record $_).Message
                        $copyConditionStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Stop-Function -Message "Issue creating condition on $destinstance" -Target $conditionName -ErrorRecord $_
                    }
                }
            }
            
        <#
                        Policies
        #>
            
            Write-Message -Level Verbose -Message "Migrating policies"
            foreach ($policy in $storePolicies) {
                $policyName = $policy.Name
                
                $copyPolicyStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name         = $policyName
                    Type         = "Policy"
                    Status       = $null
                    Notes        = $null
                    DateTime     = [DbaDateTime](Get-Date)
                }
                
                if ($null -ne $destStore.Policies[$policyName]) {
                    if ($force -eq $false) {
                        Write-Message -Level Verbose -Message "Policy '$policyName' was skipped because it already exists on $destinstance. Use -Force to drop and recreate"
                        
                        $copyPolicyStatus.Status = "Skipped"
                        $copyPolicyStatus.Notes = "Already exists"
                        $copyPolicyStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        continue
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Attempting to drop $policyName")) {
                            Write-Message -Level Verbose -Message "Policy '$policyName' exists on $destinstance. Force specified. Dropping $policyName."
                            
                            try {
                                $destStore.Policies[$policyName].Drop()
                                $destStore.Policies.refresh()
                            }
                            catch {
                                $copyPolicyStatus.Status = "Failed"
                                $copyPolicyStatus.Notes = (Get-ErrorMessage -Record $_).Message
                                $copyPolicyStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                
                                Stop-Function -Message "Issue dropping policy on $destinstance" -Target $policyName -ErrorRecord $_ -Continue
                            }
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Migrating policy $policyName")) {
                    try {
                        $destStore.Conditions.Refresh()
                        $destStore.Policies.Refresh()
                        $sql = $policy.ScriptCreateWithDependencies().GetScript() | Out-String
                        Write-Message -Level Debug -Message $sql
                        Write-Message -Level Verbose -Message "Copying policy $policyName"
                        $null = $destServer.Query($sql)
                        
                        $copyPolicyStatus.Status = "Successful"
                        $copyPolicyStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copyPolicyStatus.Status = "Failed"
                        $copyPolicyStatus.Notes = (Get-ErrorMessage -Record $_).Message
                        $copyPolicyStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        # This is usually because of a duplicate dependent from above. Just skip for now.
                        Stop-Function -Message "Issue creating policy on $destinstance" -Target $policyName -ErrorRecord $_ -Continue
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlPolicyManagement
    }
}
function Copy-DbaSqlServerAgent {
    <#
        .SYNOPSIS
            Copy SQL Server Agent from one server to another.

        .DESCRIPTION
            A wrapper function that calls the associated Copy command for each of the object types seen in SSMS under SQL Server Agent. This also copies all of the the SQL Agent properties (job history max rows, DBMail profile name, etc.).

            You must have sysadmin access and server version must be SQL Server version 2000 or greater.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER DisableJobsOnDestination
            If this switch is enabled, the jobs will be disabled on Destination after copying.

        .PARAMETER DisableJobsOnSource
            If this switch is enabled, the jobs will be disabled on Source after copying.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER Force
            If this switch is enabled, existing objects on Destination with matching names from Source will be dropped, then copied.

        .NOTES
            Tags: Migration, SqlServerAgent, SqlAgent
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaSqlServerAgent

        .EXAMPLE
            Copy-DbaSqlServerAgent -Source sqlserver2014a -Destination sqlcluster

            Copies all job server objects from sqlserver2014a to sqlcluster using Windows credentials for authentication. If job objects with the same name exist on sqlcluster, they will be skipped.

        .EXAMPLE
            Copy-DbaSqlServerAgent -Source sqlserver2014a -Destination sqlcluster -SourceSqlCredential $cred

            Copies all job objects from sqlserver2014a to sqlcluster using SQL credentials to authentication to sqlserver2014a and Windows credentials to authenticate to sqlcluster.

        .EXAMPLE
            Copy-DbaSqlServerAgent -Source sqlserver2014a -Destination sqlcluster -WhatIf

            Shows what would happen if the command were executed.
    #>
    [cmdletbinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]$SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [Switch]$DisableJobsOnDestination,
        [Switch]$DisableJobsOnSource,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        Invoke-SmoCheck -SqlInstance $sourceServer
        $sourceAgent = $sourceServer.JobServer
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            Invoke-SmoCheck -SqlInstance $destServer
            # All of these support whatif inside of them
            Copy-DbaAgentCategory -Source $sourceServer -Destination $destServer -Force:$force
            
            $destServer.JobServer.JobCategories.Refresh()
            $destServer.JobServer.OperatorCategories.Refresh()
            $destServer.JobServer.AlertCategories.Refresh()
            
            Copy-DbaAgentOperator -Source $sourceServer -Destination $destServer -Force:$force
            $destServer.JobServer.Operators.Refresh()
            
            Copy-DbaAgentAlert -Source $sourceServer -Destination $destServer -Force:$force -IncludeDefaults
            $destServer.JobServer.Alerts.Refresh()
            
            Copy-DbaAgentProxyAccount -Source $sourceServer -Destination $destServer -Force:$force
            $destServer.JobServer.ProxyAccounts.Refresh()
            
            Copy-DbaAgentSharedSchedule -Source $sourceServer -Destination $destServer -Force:$force
            $destServer.JobServer.SharedSchedules.Refresh()
            
            $destServer.JobServer.Refresh()
            $destServer.Refresh()
            Copy-DbaAgentJob -Source $sourceServer -Destination $destServer -Force:$force -DisableOnDestination:$DisableJobsOnDestination -DisableOnSource:$DisableJobsOnSource
            
            # To do
        <#
            Copy-DbaAgentMasterServer -Source $sourceServer -Destination $destServer -Force:$force
            Copy-DbaAgentTargetServer -Source $sourceServer -Destination $destServer -Force:$force
            Copy-DbaAgentTargetServerGroup -Source $sourceServer -Destination $destServer -Force:$force
        #>
            
        <# Here are the properties which must be migrated separately #>
            $copyAgentPropStatus = [pscustomobject]@{
                SourceServer = $sourceServer.Name
                DestinationServer = $destServer.Name
                Name         = "Server level properties"
                Type         = "Agent Properties"
                Status       = $null
                Notes        = $null
                DateTime     = [DbaDateTime](Get-Date)
            }
            
            if ($Pscmdlet.ShouldProcess($destinstance, "Copying Agent Properties")) {
                try {
                    Write-Message -Level Verbose -Message "Copying SQL Agent Properties"
                    $sql = $sourceAgent.Script() | Out-String
                    $sql = $sql -replace [Regex]::Escape("'$source'"), "'$destinstance'"
                    $sql = $sql -replace [Regex]::Escape("@errorlog_file="), [Regex]::Escape("--@errorlog_file=")
                    $sql = $sql -replace [Regex]::Escape("@auto_start="), [Regex]::Escape("--@auto_start=")
                    Write-Message -Level Debug -Message $sql
                    $null = $destServer.Query($sql)
                    
                    $copyAgentPropStatus.Status = "Successful"
                    $copyAgentPropStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                }
                catch {
                    $message = $_.Exception.InnerException.InnerException.InnerException.Message
                    if (-not $message) { $message = $_.Exception.Message }
                    $copyAgentPropStatus.Status = "Failed"
                    $copyAgentPropStatus.Notes = $message
                    $copyAgentPropStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    Stop-Function -Message $message -Target $destinstance
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlServerAgent
    }
}
#ValidationTags#Messaging#
function Copy-DbaSsisCatalog {
    <#
        .SYNOPSIS
           Copy-DbaSsisCatalog migrates Folders, SSIS projects, and environments from one SQL Server to another.

        .DESCRIPTION
            By default, all folders, projects, and environments are copied. The -Project parameter can be specified to copy only one project, if desired.

            The parameters get more granular from the Folder level. For example, specifying -Folder will only deploy projects/environments from within that folder.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2012 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2012 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Force
            If this switch is enabled, the SSIS Catalog will be dropped and recreated on Destination if it already exists.

        .PARAMETER Project
            Specifies a source Project name.

        .PARAMETER Folder
            Specifies a source folder name.

        .PARAMETER Environment
            Specifies an environment to copy.

        .PARAMETER EnableSqlClr
            If this switch is enabled and Destination does not have the SQL CLR configuration option enabled, user prompts for enabling it on Destination will be skipped. SQL CLR is required for SSISDB.

        .PARAMETER CreateCatalogPassword
            Specifies a secure string to use in creating an SSISDB catalog on Destination. If this is specified, prompts for the password will be skipped.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration, SSIS
            Author: Phil Schwartz (philschwartz.me, @pschwartzzz)

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaSsisCatalog

        .EXAMPLE
            Copy-DbaSsisCatalog -Source sqlserver2014a -Destination sqlcluster

            Copies all folders, environments and SSIS Projects from sqlserver2014a to sqlcluster, using Windows credentials to authenticate to both instances. If folders with the same name exist on the destination they will be skipped, but projects will be redeployed.

        .EXAMPLE
            Copy-DbaSsisCatalog -Source sqlserver2014a -Destination sqlcluster -Project Archive_Tables -SourceSqlCredential $cred -Force

            Copies a single Project, the Archive_Tables Project, from sqlserver2014a to sqlcluster using SQL credentials to authenticate to sqlserver2014a and Windows credentials to authenticate to sqlcluster. If a Project with the same name exists on sqlcluster, it will be deleted and recreated because -Force was used.

        .EXAMPLE
            Copy-DbaSsisCatalog -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force

            Shows what would happen if the command were executed using force.

        .EXAMPLE
            $SecurePW = Read-Host "Enter password" -AsSecureString
            Copy-DbaSsisCatalog -Source sqlserver2014a -Destination sqlcluster -CreateCatalogPassword $SecurePW

            Deploy entire SSIS catalog to an instance without a destination catalog. User prompts for creating the catalog on Destination will be bypassed.

    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$SourceSqlCredential,
        [PSCredential]$DestinationSqlCredential,
        [String]$Project,
        [String]$Folder,
        [String]$Environment,
        [System.Security.SecureString]$CreateCatalogPassword,
        [Switch]$EnableSqlClr,
        [Switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    <# Developer note: The throw calls must stay in this command #>
    begin {
        function Get-RemoteIntegrationService {
            param (
                [Object]$Computer
            )
            $result = Get-DbaSqlService -ComputerName $Computer -Type SSIS
            if ($result) {
                $running = $false
                foreach ($service in $result) {
                    if (!$service.State -eq "Running") {
                        Write-Message -Level Warning -Message "Service $($service.DisplayName) was found on the destination, but is currently not running."
                    }
                    else {
                        Write-Message -Level Verbose -Message "Service $($service.DisplayName) was found running on the destination."
                        $running = $true
                    }
                }
            }
            else {
                throw "No Integration Services service was found on the destination, please ensure the feature is installed and running."
            }
        }

        function Invoke-ProjectDeployment {
            param (
                [String]$Project,
                [String]$Folder
            )
            $sqlConn = New-Object System.Data.SqlClient.SqlConnection
            $sqlConn.ConnectionString = $sourceConnection.ConnectionContext.ConnectionString
            if ($sqlConn.State -eq "Closed") {
                $sqlConn.Open()
            }
            try {
                Write-Message -Level Verbose -Message "Deploying project $Project from folder $Folder."
                $cmd = New-Object System.Data.SqlClient.SqlCommand
                $cmd.CommandType = "StoredProcedure"
                $cmd.connection = $sqlConn
                $cmd.CommandText = "SSISDB.Catalog.get_project"
                $cmd.Parameters.Add("@folder_name", $Folder) | out-null;
                $cmd.Parameters.Add("@project_name", $Project) | out-null;
                [byte[]]$results = $cmd.ExecuteScalar();
                if ($null -ne $results) {
                    $destFolder = $destinationFolders | Where-Object { $_.Name -eq $Folder }
                    $deployedProject = $destFolder.DeployProject($Project, $results)
                    if ($deployedProject.Status -ne "Success") {
                        Stop-Function -Message "An error occurred deploying project $Project." -Target $Project -Continue
                    }
                }
                else {
                    Stop-Function -Message "Failed deploying $Project from folder $Folder." -Target $Project -Continue
                }
            }
            catch {
                Stop-Function -Message "Failed to deploy project." -Target $Project -ErrorRecord $_
            }
            finally {
                if ($sqlConn.State -eq "Open") {
                    $sqlConn.Close()
                }
            }
        }
        function New-CatalogFolder {
            param (
                [String]$Folder,
                [String]$Description,
                [Switch]$Force
            )
            if ($Force) {
                $remove = $destinationFolders | Where-Object { $_.Name -eq $Folder }
                $envs = $remove.Environments.Name
                foreach ($e in $envs) {
                    $remove.Environments[$e].Drop()
                }
                $projs = $remove.Projects.Name
                foreach ($p in $projs) {
                    $remove.Projects[$p].Drop()
                }
                $remove.Drop()
                $destinationCatalog.Alter()
                $destinationCatalog.Refresh()
            }
            Write-Message -Level Verbose -Message "Creating folder $Folder."
            $destFolder = New-Object "$ISNamespace.CatalogFolder" ($destinationCatalog, $Folder, $Description)
            $destFolder.Create()
            $destFolder.Alter()
            $destFolder.Refresh()
        }
        function New-FolderEnvironment {
            param (
                [String]$Folder,
                [String]$Environment,
                [Switch]$Force
            )
            $envDestFolder = $destinationFolders | Where-Object { $_.Name -eq $Folder }
            if ($force) {
                $envDestFolder.Environments[$Environment].Drop()
                $envDestFolder.Alter()
                $envDestFolder.Refresh()
            }
            $srcEnv = ($sourceFolders | Where-Object { $_.Name -eq $Folder }).Environments[$Environment]
            $targetEnv = New-Object "$ISNamespace.EnvironmentInfo" ($envDestFolder, $srcEnv.Name, $srcEnv.Description)
            foreach ($var in $srcEnv.Variables) {
                if ($var.Value.ToString() -eq "") {
                    $finalValue = ""
                }
                else {
                    $finalValue = $var.Value
                }
                $targetEnv.Variables.Add($var.Name, $var.Type, $finalValue, $var.Sensitive, $var.Description)
            }
            Write-Message -Level Verbose -Message "Creating environment $Environment."
            $targetEnv.Create()
            $targetEnv.Alter()
            $targetEnv.Refresh()
        }
        function New-SSISDBCatalog {
            param (
                [System.Security.SecureString]$Password
            )

            if (!$Password) {
                Write-Message -Level Verbose -Message "SSISDB Catalog requires a password."
                $pass1 = Read-Host "Enter a password" -AsSecureString
                $plainTextPass1 = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($pass1))
                $pass2 = Read-Host "Re-enter password" -AsSecureString
                $plainTextPass2 = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($pass2))
                if ($plainTextPass1 -ne $plainTextPass2) {
                    throw "Validation error, passwords entered do not match."
                }
                $plainTextPass = $plainTextPass1
            }
            else {
                $plainTextPass = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password))
            }

            $catalog = New-Object "$ISNamespace.Catalog" ($destinationSSIS, "SSISDB", $plainTextPass)
            $catalog.Create()
            $catalog.Refresh()
        }

        $ISNamespace = "Microsoft.SqlServer.Management.IntegrationServices"
        
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 11
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source integration services."
            $sourceSSIS = New-Object "$ISNamespace.IntegrationServices" $sourceConnection
        }
        catch {
            Stop-Function -Message "There was an error connecting to the source integration services." -Target $sourceConnection -ErrorRecord $_
            return
        }
        
        $sourceCatalog = $sourceSSIS.Catalogs | Where-Object { $_.Name -eq "SSISDB" }
        if (!$sourceCatalog) {
            Stop-Function -Message "The source SSISDB catalog on $Source does not exist."
            return
        }
        $sourceFolders = $sourceCatalog.Folders
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destinationConnection = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 1
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            
            try {
                Get-RemoteIntegrationService -Computer $destinstance
            }
            catch {
                Stop-Function -Message "An error occurred when checking the destination for Integration Services. Is Integration Services installed?" -Target $destinstance -ErrorRecord $_
            }
            
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance integration services."
                $destinationSSIS = New-Object "$ISNamespace.IntegrationServices" $destinationConnection
            }
            catch {
                Stop-Function -Message "There was an error connecting to the destination integration services." -Target $destinationCon -ErrorRecord $_
            }
            
            $destinationCatalog = $destinationSSIS.Catalogs | Where-Object { $_.Name -eq "SSISDB" }
            $destinationFolders = $destinationCatalog.Folders
            
            if (!$destinationCatalog) {
                if (!$destinationConnection.Configuration.IsSqlClrEnabled.ConfigValue) {
                    if ($Pscmdlet.ShouldProcess($destinstance, "Enabling SQL CLR configuration option.")) {
                        if (!$EnableSqlClr) {
                            $message = "The destination does not have SQL CLR configuration option enabled (required by SSISDB), would you like to enable it?"
                            $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Enable SQL CLR on $destinstance."
                            $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Exit."
                            $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
                            $result = $host.ui.PromptForChoice($null, $message, $options, 0)
                            switch ($result) {
                                0 {
                                    continue
                                }
                                1 {
                                    return
                                }
                            }
                        }
                        Write-Message -Level Verbose -Message "Enabling SQL CLR configuration option at the destination."
                        if ($destinationConnection.Configuration.ShowAdvancedOptions.ConfigValue -eq $false) {
                            $destinationConnection.Configuration.ShowAdvancedOptions.ConfigValue = $true
                            $changeback = $true
                        }
                        
                        $destinationConnection.Configuration.IsSqlClrEnabled.ConfigValue = $true
                        
                        if ($changeback -eq $true) {
                            $destinationConnection.Configuration.ShowAdvancedOptions.ConfigValue = $false
                        }
                        $destinationConnection.Configuration.Alter()
                    }
                }
                else {
                    Write-Message -Level Verbose -Message "SQL CLR configuration option is already enabled at the destination."
                }
                if ($Pscmdlet.ShouldProcess($destinstance, "Create destination SSISDB Catalog")) {
                    if (!$CreateCatalogPassword) {
                        $message = "The destination SSISDB catalog does not exist, would you like to create one?"
                        $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Create an SSISDB catalog on $destinstance."
                        $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Exit."
                        $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
                        $result = $host.ui.PromptForChoice($null, $message, $options, 0)
                        switch ($result) {
                            0 {
                                New-SSISDBCatalog
                            }
                            1 {
                                return
                            }
                        }
                    }
                    else {
                        New-SSISDBCatalog -Password $CreateCatalogPassword
                    }
                    
                    $destinationSSIS.Refresh()
                    $destinationCatalog = $destinationSSIS.Catalogs | Where-Object { $_.Name -eq "SSISDB" }
                    $destinationFolders = $destinationCatalog.Folders
                }
                else {
                    throw "The destination SSISDB catalog does not exist."
                }
            }
            if ($folder) {
                if ($sourceFolders.Name -contains $folder) {
                    $srcFolder = $sourceFolders | Where-Object { $_.Name -eq $folder }
                    if ($destinationFolders.Name -contains $folder) {
                        if (!$force) {
                            Write-Message -Level Warning -Message "Integration services catalog folder $folder exists at destination. Use -Force to drop and recreate."
                        }
                        else {
                            if ($Pscmdlet.ShouldProcess($destinstance, "Dropping folder $folder and recreating")) {
                                try {
                                    New-CatalogFolder -Folder $srcFolder.Name -Description $srcFolder.Description -Force
                                }
                                catch {
                                    Stop-Function -Message "Issue dropping folder" -Target $folder -ErrorRecord $_
                                }
                                
                            }
                        }
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Creating folder $folder")) {
                            try {
                                New-CatalogFolder -Folder $srcFolder.Name -Description $srcFolder.Description
                            }
                            catch {
                                Stop-Function -Message "Issue creating folder" -Target $folder -ErrorRecord $_
                            }
                        }
                    }
                }
                else {
                    throw "The source folder provided does not exist in the source Integration Services catalog."
                }
            }
            else {
                foreach ($srcFolder in $sourceFolders) {
                    if ($destinationFolders.Name -notcontains $srcFolder.Name) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Creating folder $($srcFolder.Name)")) {
                            try {
                                New-CatalogFolder -Folder $srcFolder.Name -Description $srcFolder.Description
                            }
                            catch {
                                Stop-Function -Message "Issue creating folder" -Target $srcFolder -ErrorRecord $_ -Continue
                            }
                        }
                    }
                    else {
                        if (!$force) {
                            Write-Message -Level Warning -Message "Integration services catalog folder $($srcFolder.Name) exists at destination. Use -Force to drop and recreate."
                            continue
                        }
                        else {
                            if ($Pscmdlet.ShouldProcess($destinstance, "Dropping folder $($srcFolder.Name) and recreating")) {
                                try {
                                    New-CatalogFolder -Folder $srcFolder.Name -Description $srcFolder.Description -Force
                                }
                                catch {
                                    Stop-Function -Message "Issue dropping folder" -Target $srcFolder -ErrorRecord $_
                                }
                            }
                        }
                    }
                }
            }
            
            # Refresh folders for project and environment deployment
            if ($Pscmdlet.ShouldProcess($destinstance, "Refresh folders for project deployment")) {
                try {
                    $destinationFolders.Alter()
                }
                catch {
                    # Sometimes it says Alter() doesn't exist
                }
                $destinationFolders.Refresh()
            }
            
            if ($folder) {
                $sourceFolders = $sourceFolders | Where-Object { $_.Name -eq $folder }
                if (!$sourceFolders) {
                    throw "The source folder $folder does not exist in the source Integration Services catalog."
                }
            }
            if ($project) {
                $folderDeploy = $sourceFolders | Where-Object { $_.Projects.Name -eq $project }
                if (!$folderDeploy) {
                    throw "The project $project cannot be found in the source Integration Services catalog."
                }
                else {
                    foreach ($f in $folderDeploy) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Deploying project $project from folder $($f.Name)")) {
                            try {
                                Invoke-ProjectDeployment -Folder $f.Name -Project $project
                            }
                            catch {
                                Stop-Function -Message "Issue deploying project" -Target $project -ErrorRecord $_
                            }
                        }
                    }
                }
            }
            else {
                foreach ($curFolder in $sourceFolders) {
                    foreach ($proj in $curFolder.Projects) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Deploying project $($proj.Name) from folder $($curFolder.Name)")) {
                            try {
                                Invoke-ProjectDeployment -Project $proj.Name -Folder $curFolder.Name
                            }
                            catch {
                                Stop-Function -Message "Issue deploying project" -Target $proj -ErrorRecord $_
                            }
                        }
                    }
                }
            }
            
            if ($environment) {
                $folderDeploy = $sourceFolders | Where-Object { $_.Environments.Name -eq $environment }
                if (!$folderDeploy) {
                    throw "The environment $environment cannot be found in the source Integration Services catalog."
                }
                else {
                    foreach ($f in $folderDeploy) {
                        if ($destinationFolders[$f.Name].Environments.Name -notcontains $environment) {
                            if ($Pscmdlet.ShouldProcess($destinstance, "Deploying environment $environment from folder $($f.Name)")) {
                                try {
                                    New-FolderEnvironment -Folder $f.Name -Environment $environment
                                }
                                catch {
                                    Stop-Function -Message "Issue deploying environment" -Target $environment -ErrorRecord $_
                                }
                            }
                        }
                        else {
                            if (!$force) {
                                Write-Message -Level Warning -Message "Integration services catalog environment $environment exists in folder $($f.Name) at destination. Use -Force to drop and recreate."
                            }
                            else {
                                If ($Pscmdlet.ShouldProcess($destinstance, "Dropping existing environment $environment and deploying environment $environment from folder $($f.Name)")) {
                                    try {
                                        New-FolderEnvironment -Folder $f.Name -Environment $environment -Force
                                    }
                                    catch {
                                        Stop-Function -Message "Issue dropping existing environment" -Target $environment -ErrorRecord $_
                                    }
                                }
                            }
                        }
                    }
                }
            }
            else {
                foreach ($curFolder in $sourceFolders) {
                    foreach ($env in $curFolder.Environments) {
                        if ($destinationFolders[$curFolder.Name].Environments.Name -notcontains $env.Name) {
                            if ($Pscmdlet.ShouldProcess($destinstance, "Deploying environment $($env.Name) from folder $($curFolder.Name)")) {
                                try {
                                    New-FolderEnvironment -Environment $env.Name -Folder $curFolder.Name
                                }
                                catch {
                                    Stop-Function -Message "Issue deploying environment" -Target $env -ErrorRecord $_
                                }
                            }
                        }
                        else {
                            if (!$force) {
                                Write-Message -Level Warning -Message "Integration services catalog environment $($env.Name) exists in folder $($curFolder.Name) at destination. Use -Force to drop and recreate."
                                continue
                            }
                            else {
                                if ($Pscmdlet.ShouldProcess($destinstance, "Deploying environment $($env.Name) from folder $($curFolder.Name)")) {
                                    try {
                                        New-FolderEnvironment -Environment $env.Name -Folder $curFolder.Name -Force
                                    }
                                    catch {
                                        Stop-Function -Message "Issue deploying environment" -Target $env -ErrorRecord $_
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlSsisCatalog
    }
}
function Copy-DbaSysDbUserObject {
    <#
        .SYNOPSIS
            Imports all user objects found in source SQL Server's master, msdb and model databases to the destination.

        .DESCRIPTION
            Imports all user objects found in source SQL Server's master, msdb and model databases to the destination. This is useful because many DBAs store backup/maintenance procs/tables/triggers/etc (among other things) in master or msdb.

            It is also useful for migrating objects within the model database.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Classic
            Perform the migration the old way
    
        .PARAMETER Force
            Drop destination objects first. Has no effect if you use Classic. This doesn't work really well, honestly.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration, SystemDatabase, UserObject

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaSysDbUserObject

        .EXAMPLE
            Copy-DbaSysDbUserObject $sourceServer $destserver

            Copies user objects from source to destination
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [DbaInstanceParameter]$Source,
        [PSCredential]$SourceSqlCredential,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [switch]$Force,
        [switch]$Classic,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        function get-sqltypename ($type) {
            switch ($type) {
                "VIEW" { "view" }
                "SQL_TABLE_VALUED_FUNCTION" { "User table valued fsunction" }
                "DEFAULT_CONSTRAINT" { "User default constraint" }
                "SQL_STORED_PROCEDURE" { "User stored procedure" }
                "RULE" { "User rule" }
                "SQL_INLINE_TABLE_VALUED_FUNCTION" { "User inline table valued function" }
                "SQL_TRIGGER" { "User server trigger" }
                "SQL_SCALAR_FUNCTION" { "User scalar function" }
                default { $type }
            }
        }
    }
    process {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        
        if (!(Test-SqlSa -SqlInstance $sourceServer -SqlCredential $SourceSqlCredential)) {
            Stop-Function -Message "Not a sysadmin on $source. Quitting."
            return
        }
        
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            
            if (!(Test-SqlSa -SqlInstance $destServer -SqlCredential $DestinationSqlCredential)) {
                Stop-Function -Message "Not a sysadmin on $destinstance" -Continue
            }
            
            $systemDbs = "master", "model", "msdb"
            
            if (-not $Classic) {
                foreach ($systemDb in $systemDbs) {
                    $smodb = $sourceServer.databases[$systemDb]
                    $destdb = $destserver.databases[$systemDb]
                    
                    $tables = $smodb.Tables | Where-Object IsSystemObject -ne $true
                    $schemas = $smodb.Schemas | Where-Object IsSystemObject -ne $true
                    
                    foreach ($schema in $schemas) {
                        $copyobject = [pscustomobject]@{
                            SourceServer = $sourceServer.Name
                            DestinationServer = $destServer.Name
                            Name         = $schema
                            Type         = "User schema in $systemDb"
                            Status       = $null
                            Notes        = $null
                            DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                        }
                        
                        $destschema = $destdb.Schemas | Where-Object Name -eq $schema.Name
                        $schmadoit = $true
                        
                        if ($destschema) {
                            if (-not $force) {
                                $copyobject.Status = "Skipped"
                                $copyobject.Notes = "$schema exists on destination"
                                $schmadoit = $false
                            }
                            else {
                                if ($PSCmdlet.ShouldProcess($destServer, "Dropping schema $schema in $systemDb")) {
                                    try {
                                        Write-Message -Level Verbose -Message "Force specified. Dropping $schema in $destdb on $destinstance"
                                        $destschema.Drop()
                                    }
                                    catch {
                                        $schmadoit = $false
                                        $copyobject.Status = "Failed"
                                        $copyobject.Notes = $_.Exception.InnerException.InnerException.InnerException.Message
                                    }
                                }
                            }
                        }
                        
                        if ($schmadoit) {
                            $transfer = New-Object Microsoft.SqlServer.Management.Smo.Transfer $smodb
                            $null = $transfer.CopyAllObjects = $false
                            $null = $transfer.Options.WithDependencies = $true
                            $null = $transfer.ObjectList.Add($schema)
                            $sql = $transfer.ScriptTransfer()
                            if ($PSCmdlet.ShouldProcess($destServer, "Attempting to add schema $($schema.Name) to $systemDb")) {
                                try {
                                    Write-Message -Level Debug -Message "$sql"
                                    $null = $destServer.Query($sql, $systemDb)
                                    $copyobject.Status = "Successful"
                                    $copyobject.Notes = "May have also created dependencies"
                                }
                                catch {
                                    $copyobject.Status = "Failed"
                                    $copyobject.Notes = (Get-ErrorMessage -Record $_)
                                }
                            }
                        }
                        
                        $copyobject | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    
                    foreach ($table in $tables) {
                        $copyobject = [pscustomobject]@{
                            SourceServer = $sourceServer.Name
                            DestinationServer = $destServer.Name
                            Name         = $table
                            Type         = "User table in $systemDb"
                            Status       = $null
                            Notes        = $null
                            DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                        }
                        
                        $desttable = $destdb.Tables.Item($table.Name, $table.Schema)
                        $doit = $true
                        
                        if ($desttable) {
                            if (-not $force) {
                                $copyobject.Status = "Skipped"
                                $copyobject.Notes = "$table exists on destination"
                                $doit = $false
                            }
                            else {
                                if ($PSCmdlet.ShouldProcess($destServer, "Dropping table $table in $systemDb")) {
                                    try {
                                        Write-Message -Level Verbose -Message "Force specified. Dropping $table in $destdb on $destinstance"
                                        $desttable.Drop()
                                    }
                                    catch {
                                        $doit = $false
                                        $copyobject.Status = "Failed"
                                        $copyobject.Notes = $_.Exception.InnerException.InnerException.InnerException.Message
                                    }
                                }
                            }
                        }
                        
                        if ($doit) {
                            $transfer = New-Object Microsoft.SqlServer.Management.Smo.Transfer $smodb
                            $null = $transfer.CopyAllObjects = $false
                            $null = $transfer.Options.WithDependencies = $true
                            $null = $transfer.ObjectList.Add($table)
                            $sql = $transfer.ScriptTransfer()
                            if ($PSCmdlet.ShouldProcess($destServer, "Attempting to add table $table to $systemDb")) {
                                try {
                                    Write-Message -Level Debug -Message "$sql"
                                    $null = $destServer.Query($sql, $systemDb)
                                    $copyobject.Status = "Successful"
                                    $copyobject.Notes = "May have also created dependencies"
                                }
                                catch {
                                    $copyobject.Status = "Failed"
                                    $copyobject.Notes = (Get-ErrorMessage -Record $_)
                                }
                            }
                        }
                        $copyobject | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    
                    $userobjects = Get-DbaSqlModule -SqlInstance $sourceserver -Database $systemDb -NoSystemObjects | Sort-Object Type
                    Write-Message -Level Verbose -Message "Copying from $systemDb"
                    foreach ($userobject in $userobjects) {
                        
                        $name = "[$($userobject.SchemaName)].[$($userobject.Name)]"
                        $db = $userobject.Database
                        $type = get-sqltypename $userobject.Type
                        $sql = $userobject.Definition
                        $schema = $userobject.SchemaName
                        
                        $copyobject = [pscustomobject]@{
                            SourceServer = $sourceServer.Name
                            DestinationServer = $destServer.Name
                            Name         = $name
                            Type         = "$type in $systemDb"
                            Status       = $null
                            Notes        = $null
                            DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                        }
                        Write-Message -Level Debug -Message $sql
                        try {
                            Write-Message -Level Verbose -Message "Searching for $name in $db on $destinstance"
                            $result = Get-DbaSqlModule -SqlInstance $destServer -NoSystemObjects -Database $db |
                            Where-Object { $psitem.Name -eq $userobject.Name -and $psitem.Type -eq $userobject.Type }
                            if ($result) {
                                Write-Message -Level Verbose -Message "Found $name in $db on $destinstance"
                                if (-not $Force) {
                                    $copyobject.Status = "Skipped"
                                    $copyobject.Notes = "$name exists on destination"
                                }
                                else {
                                    $smobject = switch ($userobject.Type) {
                                        "VIEW" { $smodb.Views.Item($userobject.Name, $userobject.SchemaName) }
                                        "SQL_STORED_PROCEDURE" { $smodb.StoredProcedures.Item($userobject.Name, $userobject.SchemaName) }
                                        "RULE" { $smodb.Rules.Item($userobject.Name, $userobject.SchemaName) }
                                        "SQL_TRIGGER" { $smodb.Triggers.Item($userobject.Name, $userobject.SchemaName) }
                                        "SQL_TABLE_VALUED_FUNCTION" { $smodb.UserDefinedFunctions.Item($name) }
                                        "SQL_INLINE_TABLE_VALUED_FUNCTION" { $smodb.UserDefinedFunctions.Item($name) }
                                        "SQL_SCALAR_FUNCTION" { $smodb.UserDefinedFunctions.Item($name) }
                                    }
                                    
                                    if ($smobject) {
                                        Write-Message -Level Verbose -Message "Force specified. Dropping $smobject on $destdb on $destinstance using SMO"
                                        $transfer = New-Object Microsoft.SqlServer.Management.Smo.Transfer $smodb
                                        $null = $transfer.CopyAllObjects = $false
                                        $null = $transfer.Options.WithDependencies = $true
                                        $null = $transfer.ObjectList.Add($smobject)
                                        $null = $transfer.Options.ScriptDrops = $true
                                        $dropsql = $transfer.ScriptTransfer()
                                        Write-Message -Level Debug -Message "$dropsql"
                                        if ($PSCmdlet.ShouldProcess($destServer, "Attempting to drop $type $name from $systemDb")) {
                                            $null = $destdb.Query("$dropsql")
                                        }
                                    }
                                    else {
                                        if ($PSCmdlet.ShouldProcess($destServer, "Attempting to drop $type $name from $systemDb using T-SQL")) {
                                            $null = $destdb.Query("DROP FUNCTION $($userobject.name)")
                                        }
                                    }
                                    if ($PSCmdlet.ShouldProcess($destServer, "Attempting to add $type $name to $systemDb")) {
                                        $null = $destdb.Query("$sql")
                                        $copyobject.Status = "Successful"
                                    }
                                }
                            }
                            else {
                                if ($PSCmdlet.ShouldProcess($destServer, "Attempting to add $type $name to $systemDb")) {
                                    $null = $destdb.Query("$sql")
                                    $copyobject.Status = "Successful"
                                }
                            }
                        }
                        catch {
                            try {
                                $smobject = switch ($userobject.Type) {
                                    "VIEW" { $smodb.Views.Item($userobject.Name, $userobject.SchemaName) }
                                    "SQL_STORED_PROCEDURE" { $smodb.StoredProcedures.Item($userobject.Name, $userobject.SchemaName) }
                                    "RULE" { $smodb.Rules.Item($userobject.Name, $userobject.SchemaName) }
                                    "SQL_TRIGGER" { $smodb.Triggers.Item($userobject.Name, $userobject.SchemaName) }
                                }
                                if ($smobject) {
                                    $transfer = New-Object Microsoft.SqlServer.Management.Smo.Transfer $smodb
                                    $null = $transfer.CopyAllObjects = $false
                                    $null = $transfer.Options.WithDependencies = $true
                                    $null = $transfer.ObjectList.Add($smobject)
                                    $sql = $transfer.ScriptTransfer()
                                    Write-Message -Level Debug -Message "$sql"
                                    Write-Message -Level Verbose -Message "Adding $smoobject on $destdb on $destinstance"
                                    if ($PSCmdlet.ShouldProcess($destServer, "Attempting to add $type $name to $systemDb")) {
                                        $null = $destdb.Query("$sql")
                                    }
                                    $copyobject.Status = "Successful"
                                    $copyobject.Notes = "May have also installed dependencies"
                                }
                                else {
                                    $copyobject.Status = "Failed"
                                    $copyobject.Notes = (Get-ErrorMessage -Record $_)
                                }
                            }
                            catch {
                                $copyobject.Status = "Failed"
                                $copyobject.Notes = (Get-ErrorMessage -Record $_)
                            }
                        }
                        $copyobject | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                }
            }
            else {
                foreach ($systemDb in $systemDbs) {
                    $sysdb = $sourceServer.databases[$systemDb]
                    $transfer = New-Object Microsoft.SqlServer.Management.Smo.Transfer $sysdb
                    $transfer.CopyAllObjects = $false
                    $transfer.CopyAllDatabaseTriggers = $true
                    $transfer.CopyAllDefaults = $true
                    $transfer.CopyAllRoles = $true
                    $transfer.CopyAllRules = $true
                    $transfer.CopyAllSchemas = $true
                    $transfer.CopyAllSequences = $true
                    $transfer.CopyAllSqlAssemblies = $true
                    $transfer.CopyAllSynonyms = $true
                    $transfer.CopyAllTables = $true
                    $transfer.CopyAllViews = $true
                    $transfer.CopyAllStoredProcedures = $true
                    $transfer.CopyAllUserDefinedAggregates = $true
                    $transfer.CopyAllUserDefinedDataTypes = $true
                    $transfer.CopyAllUserDefinedTableTypes = $true
                    $transfer.CopyAllUserDefinedTypes = $true
                    $transfer.CopyAllUserDefinedFunctions = $true
                    $transfer.CopyAllUsers = $true
                    $transfer.PreserveDbo = $true
                    $transfer.Options.AllowSystemObjects = $false
                    $transfer.Options.ContinueScriptingOnError = $true
                    $transfer.Options.IncludeDatabaseRoleMemberships = $true
                    $transfer.Options.Indexes = $true
                    $transfer.Options.Permissions = $true
                    $transfer.Options.WithDependencies = $false
                    
                    Write-Message -Level Output -Message "Copying from $systemDb."
                    try {
                        $sqlQueries = $transfer.ScriptTransfer()
                        
                        foreach ($sql in $sqlQueries) {
                            Write-Message -Level Debug -Message "$sql"
                            if ($PSCmdlet.ShouldProcess($destServer, $sql)) {
                                try {
                                    $destServer.Query($sql, $systemDb)
                                }
                                catch {
                                    # Don't care - long story having to do with duplicate stuff
                                }
                            }
                        }
                    }
                    catch {
                        # Don't care - long story having to do with duplicate stuff
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlSysDbUserObjects
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Copy-DbaTableData {
    <#
        .SYNOPSIS
            Copies data between SQL Server tables.

        .DESCRIPTION
            Copies data between SQL Server tables using SQL Bulk Copy.
            The same can be achieved also doing
                $sourcetable = Invoke-SqlCmd2 -ServerInstance instance1 ... -As DataTable
                Write-DbaDataTable -SqlInstance ... -InputObject $sourcetable
            but it will force buffering the contents on the table in memory (high RAM usage for large tables).
            With this function, a streaming copy will be done in the most speedy and least resource-intensive way.

        .PARAMETER SqlInstance
            Source SQL Server.You must have sysadmin access and server version must be SQL Server version 2000 or greater.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination Sql Server. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database to copy the table from.

        .PARAMETER DestinationDatabase
            The database to copy the table to. If not specified, it is assumed to be the same of Database

        .PARAMETER Table
            Define a specific table you would like to use as source. You can specify up to three-part name like db.sch.tbl.
            If the object has special characters please wrap them in square brackets [ ].
            This dbo.First.Table will try to find table named 'Table' on schema 'First' and database 'dbo'.
            The correct way to find table named 'First.Table' on schema 'dbo' is passing dbo.[First.Table]

        .PARAMETER DestinationTable
            The table you want to use as destination. If not specified, it is assumed to be the same of Table

        .PARAMETER Query
            If you want to copy only a portion, specify the query (but please, select all the columns, or nasty things will happen)

        .PARAMETER BatchSize
            The BatchSize for the import defaults to 5000.

        .PARAMETER NotifyAfter
            Sets the option to show the notification after so many rows of import

        .PARAMETER NoTableLock
            If this switch is enabled, a table lock (TABLOCK) will not be placed on the destination table. By default, this operation will lock the destination table while running.

        .PARAMETER CheckConstraints
            If this switch is enabled, the SqlBulkCopy option to process check constraints will be enabled.

            Per Microsoft "Check constraints while data is being inserted. By default, constraints are not checked."

        .PARAMETER FireTriggers
            If this switch is enabled, the SqlBulkCopy option to fire insert triggers will be enabled.

            Per Microsoft "When specified, cause the server to fire the insert triggers for the rows being inserted into the Database."

        .PARAMETER KeepIdentity
            If this switch is enabled, the SqlBulkCopy option to preserve source identity values will be enabled.

            Per Microsoft "Preserve source identity values. When not specified, identity values are assigned by the destination."

        .PARAMETER KeepNulls
            If this switch is enabled, the SqlBulkCopy option to preserve NULL values will be enabled.

            Per Microsoft "Preserve null values in the destination table regardless of the settings for default values. When not specified, null values are replaced by default values where applicable."

        .PARAMETER Truncate
            If this switch is enabled, the destination table will be truncated after prompting for confirmation.

        .PARAMETER BulkCopyTimeOut
            Value in seconds for the BulkCopy operations timeout. The default is 30 seconds.

        .PARAMETER InputObject
            Enables piping of Table objects from Get-DbaTable

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration
            Author: niphlod (Simone Bizzotto)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaTableData

        .EXAMPLE
            Copy-DbaTableData -SqlInstance sql1 -Destination sql2 -Database dbatools_from -Table test_table

            Copies all the data from sql1 to sql2, using the database dbatools_from.

        .EXAMPLE
            Copy-DbaTableData -SqlInstance sql1 -Destination sql2 -Database dbatools_from -DestinationDatabase dbatools_dest -Table test_table

            Copies all the data from sql1 to sql2, using the database dbatools_from as source and dbatools_dest as destination

        .EXAMPLE
            Get-DbaTable -SqlInstance sql1 -Database tempdb -Table tb1, tb2 | Copy-DbaTableData -DestinationTable tb3

            Copies all data from tables tb1 and tb2 in tempdb on sql1 to tb3 in tempdb onsql1

        .EXAMPLE
            Get-DbaTable -SqlInstance sql1 -Database tempdb -Table tb1, tb2 | Copy-DbaTableData -Destination sql2

            Copies data from tbl1 in tempdb on sql1 to tbl1 in tempdb on sql2
            then
            Copies data from tbl2 in tempdb on sql1 to tbl2 in tempdb on sql2

        .EXAMPLE
            Copy-DbaTableData -SqlInstance sql1 -Destination sql2 -Database dbatools_from -Table test_table

            Copies all the data from sql1 to sql2, using the database dbatools_from.

        .EXAMPLE
            Copy-DbaTableData -SqlInstance sql1 -Destination sql2 -Database dbatools_from -Table test_table -KeepIdentity -Truncate

            Copies all the data from sql1 to sql2, using the database dbatools_from, keeping identity columns and truncating the destination

        .EXAMPLE
            Copy-DbaTableData -SqlInstance sql1 -Destination sql2 -Database dbatools_from -Table test_table -KeepIdentity -Truncate

            Copies all the data from sql1 to sql2, using the database dbatools_from, keeping identity columns and truncating the destination

    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [Alias("ServerInstance", "SqlServer", "Source")]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [DbaInstanceParameter]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [string]$Database,
        [string]$DestinationDatabase,
        [string[]]$Table,
        [string]$Query,
        [int]$BatchSize = 50000,
        [int]$NotifyAfter = 5000,
        [string]$DestinationTable,
        [switch]$NoTableLock,
        [switch]$CheckConstraints,
        [switch]$FireTriggers,
        [switch]$KeepIdentity,
        [switch]$KeepNulls,
        [switch]$Truncate,
        [int]$bulkCopyTimeOut = 5000,
        [Parameter(ValueFromPipeline)]
        [Microsoft.SqlServer.Management.Smo.Table[]]$InputObject,
        [switch]$EnableException
    )

    begin {
        # Getting the total rows copied is a challenge. Use SqlBulkCopyExtension.
        # http://stackoverflow.com/questions/1188384/sqlbulkcopy-row-count-when-complete

        $sourcecode = 'namespace System.Data.SqlClient {
            using Reflection;

            public static class SqlBulkCopyExtension
            {
                const String _rowsCopiedFieldName = "_rowsCopied";
                static FieldInfo _rowsCopiedField = null;

                public static int RowsCopiedCount(this SqlBulkCopy bulkCopy)
                {
                    if (_rowsCopiedField == null) _rowsCopiedField = typeof(SqlBulkCopy).GetField(_rowsCopiedFieldName, BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance);
                    return (int)_rowsCopiedField.GetValue(bulkCopy);
                }
            }
        }'

        Add-Type -ReferencedAssemblies System.Data.dll -TypeDefinition $sourcecode -ErrorAction SilentlyContinue
        $bulkCopyOptions = 0
        $options = "TableLock", "CheckConstraints", "FireTriggers", "KeepIdentity", "KeepNulls", "Default"

        foreach ($option in $options) {
            $optionValue = Get-Variable $option -ValueOnly -ErrorAction SilentlyContinue
            if ($option -eq "TableLock" -and (!$NoTableLock)) {
                $optionValue = $true
            }
            if ($optionValue -eq $true) {
                $bulkCopyOptions += $([Data.SqlClient.SqlBulkCopyOptions]::$option).value__
            }
        }
    }

    process {
        if ((Test-Bound -Not -ParameterName Table, SqlInstance) -and (Test-Bound -Not -ParameterName InputObject)) {
            Stop-Function -Message "You must pipe in a table or specify SqlInstance, Database and Table."
            return
        }

        if ($SqlInstance) {
            if ((Test-Bound -Not -ParameterName Database)) {
                Stop-Function -Message "Database is required when passing a SqlInstance" -Target $Table
                return
            }

            try {
                $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $SqlInstance
                return
            }

            if ($Database -notin $server.Databases.Name) {
                Stop-Function -Message "Database $Database doesn't exist on $server"
                return
            }

            try {
                $InputObject += Get-DbaTable -SqlInstance $server -Table $Table -Database $Database -EnableException -Verbose:$false
            }
            catch {
                Stop-Function -Message "Unable to determine source table : $Table"
                return
            }
        }

        foreach ($sqltable in $InputObject) {
            $Database = $sqltable.Parent.Name
            $server = $sqltable.Parent.Parent

            if ((Test-Bound -Not -ParameterName DestinationDatabase)) {
                $DestinationDatabase = $Database
            }

            if ((Test-Bound -Not -ParameterName DestinationTable)) {
                $DestinationTable = $sqltable.Name
            }

            if ((Test-Bound -Not -ParameterName Destination)) {
                $destServer = $server
            }
            else {
                try {
                    $destServer = Connect-SqlInstance -SqlInstance $Destination -SqlCredential $DestinationSqlCredential
                }
                catch {
                    Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Destination
                    return
                }
            }

            if ($DestinationDatabase -notin $destServer.Databases.Name) {
                Stop-Function -Message "Database $DestinationDatabase doesn't exist on $destServer"
                return
            }

            try {
                $desttable = Get-DbaTable -SqlInstance $destServer -Table $DestinationTable -Database $DestinationDatabase -EnableException -Verbose:$false | Select-Object -First 1
            }
            catch {
                Stop-Function -Message "Unable to determine destination table: $DestinationTable"
                return
            }

            if (-not $desttable) {
                Stop-Function -Message "$DestinationTable does not exist on destination"
                return
            }

            $connstring = $destServer.ConnectionContext.ConnectionString

            $fqtnfrom = "$($server.Databases[$Database]).$sqltable"
            $fqtndest = "$($destServer.Databases[$DestinationDatabase]).$desttable"

            if (Test-Bound -ParameterName Query -Not) {
                $Query = "SELECT * FROM $fqtnfrom"
            }
            try {
                if ($Truncate -eq $true) {
                    if ($Pscmdlet.ShouldProcess($destServer, "Truncating table $fqtndest")) {
                        $null = $destServer.Databases[$DestinationDatabase].ExecuteNonQuery("TRUNCATE TABLE $fqtndest")
                    }
                }
                $cmd = $server.ConnectionContext.SqlConnectionObject.CreateCommand()
                $cmd.CommandText = $Query
                if ($server.ConnectionContext.IsOpen -eq $false) {
                    $server.ConnectionContext.SqlConnectionObject.Open()
                }
                $bulkCopy = New-Object Data.SqlClient.SqlBulkCopy("$connstring;Database=$DestinationDatabase", $bulkCopyOptions)
                $bulkCopy.DestinationTableName = $fqtndest
                $bulkCopy.EnableStreaming = $true
                $bulkCopy.BatchSize = $BatchSize
                $bulkCopy.NotifyAfter = $NotifyAfter
                $bulkCopy.BulkCopyTimeOut = $BulkCopyTimeOut

                $elapsed = [System.Diagnostics.Stopwatch]::StartNew()
                # Add RowCount output
                $bulkCopy.Add_SqlRowsCopied({
                        $RowsPerSec = [math]::Round($args[1].RowsCopied / $elapsed.ElapsedMilliseconds * 1000.0, 1)
                        Write-Progress -id 1 -activity "Inserting rows" -Status ([System.String]::Format("{0} rows ({1} rows/sec)", $args[1].RowsCopied, $RowsPerSec))
                    })

                if ($Pscmdlet.ShouldProcess($destServer, "Writing rows to $fqtndest")) {
                    $reader = $cmd.ExecuteReader()
                    $bulkCopy.WriteToServer($reader)
                    $RowsTotal = [System.Data.SqlClient.SqlBulkCopyExtension]::RowsCopiedCount($bulkCopy)
                    $TotalTime = [math]::Round($elapsed.Elapsed.TotalSeconds, 1)
                    Write-Message -Level Verbose -Message "$RowsTotal rows inserted in $TotalTime sec"
                    if ($rowCount -is [int]) {
                        Write-Progress -id 1 -activity "Inserting rows" -status "Complete" -Completed
                    }
                }

                $bulkCopy.Close()
                $bulkCopy.Dispose()
                $reader.Close()

                [pscustomobject]@{
                    SourceInstance       = $server.Name
                    SourceDatabase       = $Database
                    SourceTable          = $sqltable.Name
                    DestinationInstance  = $destServer.name
                    DestinationDatabase  = $DestinationDatabase
                    DestinationTable     = $desttable.Name
                    RowsCopied           = $rowstotal
                    Elapsed              = [prettytimespan]$elapsed.Elapsed
                }
            }
            catch {
                Stop-Function -Message "Something went wrong" -ErrorRecord $_ -Target $server -continue
            }
        }
    }
}
function Copy-DbaXESessionTemplate {
    <#
        .SYNOPSIS
            Copies non-Microsoft templates from the dbatools template repository (\bin\xetemplates\) to $home\Documents\SQL Server Management Studio\Templates\XEventTemplates.

        .DESCRIPTION
            Copies non-Microsoft templates from the dbatools template repository (\bin\xetemplates\) to $home\Documents\SQL Server Management Studio\Templates\XEventTemplates.

            Useful for when you want to use the SSMS GUI.

        .PARAMETER Path
            The path to the template directory. Defaults to the dbatools template repository (\bin\xetemplates\).

        .PARAMETER Destination
            Path to the Destination directory, defaults to $home\Documents\SQL Server Management Studio\Templates\XEventTemplates.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaXESessionTemplate

        .EXAMPLE
            Copy-DbaXESessionTemplate

            Copies non-Microsoft templates from the dbatools template repository (\bin\xetemplates\) to $home\Documents\SQL Server Management Studio\Templates\XEventTemplates.

        .EXAMPLE
            Copy-DbaXESessionTemplate -Path C:\temp\xetemplates

            Copies your templates from C:\temp\xetemplates to $home\Documents\SQL Server Management Studio\Templates\XEventTemplates.

    #>
    [CmdletBinding()]
    param (
        [string[]]$Path = "$script:PSModuleRoot\bin\xetemplates",
        [string]$Destination = "$home\Documents\SQL Server Management Studio\Templates\XEventTemplates",
        [switch]$EnableException
    )
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            if (-not (Test-Path -Path $destinstance)) {
                try {
                    $null = New-Item -ItemType Directory -Path $destinstance -ErrorAction Stop
                }
                catch {
                    Stop-Function -Message "Failure" -ErrorRecord $_ -Target $destinstance
                }
            }
            try {
                $files = (Get-DbaXESessionTemplate -Path $Path | Where-Object Source -ne Microsoft).Path
                foreach ($file in $files) {
                    Write-Message -Level Output -Message "Copying $($file.Name) to $destinstance."
                    Copy-Item -Path $file -Destination $destinstance -ErrorAction Stop
                }
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_ -Target $path
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Disable-DbaAgHadr {
    <#
        .SYNOPSIS
            Disables the Hadr service setting on the specified SQL Server.

        .DESCRIPTION
            In order to build an AG a cluster has to be built and then the Hadr enabled for the SQL Server
            service. This function disables that feature for the SQL Server service.

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to.

        .PARAMETER Credential
            Credential object used to connect to the Windows server itself as a different user

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER Force
            Will restart SQL Server and SQL Server Agent service to apply the change.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Hadr, AG, AvailabilityGroup
            Author: Shawn Melton (@wsmelton | http://blog.wsmelton.info)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Disable-DbaAgHadr

        .EXAMPLE
            Disable-DbaAgHadr -SqlInstance sql2016 -Force

            Sets Hadr service to disabled for the instance sql2016, and restart the service to apply the change.

        .EXAMPLE
            Disable-DbaAgHadr -SqlInstance sql2012\dev1 -Force

            Sets Hadr service to disabled for the instance dev1 on sq2012, and restart the service to apply the change.
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$Credential,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        function GetDbaAgHadr {
            [CmdletBinding()]
            param (
                [parameter(Mandatory = $true, ValueFromPipeline = $true)]
                [Alias("ServerInstance", "SqlServer")]
                [DbaInstanceParameter[]]$SqlInstance,
                [PSCredential]$Credential,
                [Alias('Silent')]
                [switch]$EnableException
            )
            process {
                foreach ($instance in $SqlInstance) {

                    try {
                        $computer = $computerName = $instance.ComputerName
                        $instanceName = $instance.InstanceName
                        Write-Message -Level Verbose -Message "Connecting to $computer"
                        $currentState = Invoke-ManagedComputerCommand -ComputerName $computerName -ScriptBlock { $wmi.Services[$args[0]] | Select-Object IsHadrEnabled } -ArgumentList $instanceName -Credential $Credential
                    }
                    catch {
                        Stop-Function -Message "Failure connecting to $computer" -Category ConnectionError -ErrorRecord $_ -Target $instance
                        return
                    }

                    if ($null -eq $currentState.IsHadrEnabled) {
                        $isenabled = $false
                    }
                    else {
                        $isenabled = $currentState.IsHadrEnabled
                    }
                    [PSCustomObject]@{
                        ComputerName     = $computer
                        InstanceName     = $instanceName
                        SqlInstance      = $instance.FullName
                        IsHadrEnabled    = $isenabled
                    }
                }
            }
        }
    }

    process {
        foreach ($instance in $SqlInstance) {
            $computer = $computerFullName = $instance.ComputerName
            $instanceName = $instance.InstanceName
            if (-not (Test-ElevationRequirement -ComputerName $instance)) {
                return
            }
            $noChange = $false

            switch ($instance.InstanceName) {
                'MSSQLSERVER' { $agentName = 'SQLSERVERAGENT' }
                default { $agentName = "SQLAgent`$$instanceName" }
            }

            try {
                Write-Message -Level Verbose -Message "Checking current Hadr setting for $computer"
                $currentState = GetDbaAgHadr -SqlInstance $instance -Credential $Credential
            }
            catch {
                Stop-Function -Message "Failure to pull current state of Hadr setting on $computer" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $isHadrEnabled = $currentState.IsHadrEnabled
            Write-Message -Level InternalComment -Message "$instance Hadr current value: $isHadrEnabled"

            # hadr results from sql wmi can be iffy, skip the check
            <#
            if (-not $isHadrEnabled) {
                Write-Message -Level Warning -Message "Hadr is already disabled for instance: $($instance.FullName)"
                $noChange = $true
                continue
            }
            #>

            $scriptblock = {
                $instance = $args[0]
                $sqlService = $wmi.Services | Where-Object DisplayName -eq "SQL Server ($instance)"
                $sqlService.ChangeHadrServiceSetting(0)
            }

            if ($noChange -eq $false) {
                if ($PSCmdlet.ShouldProcess($instance, "Changing Hadr from $isHadrEnabled to 0 for $instance")) {
                    try {
                        Invoke-ManagedComputerCommand -ComputerName $computerFullName -Credential $Credential -ScriptBlock $scriptblock -ArgumentList $instancename
                    }
                    catch {
                        Stop-Function -Continue -Message "Failure on $($instance.FullName) | This may be because AlwaysOn Availability Groups feature requires the x86(non-WOW) or x64 Enterprise Edition of SQL Server 2012 (or later version) running on Windows Server 2008 (or later version) with WSFC hotfix KB 2494036 installed."
                    }
                }
                if (Test-Bound 'Force') {
                    if ($PSCmdlet.ShouldProcess($instance, "Force provided, restarting Engine and Agent service for $instance on $computerFullName")) {
                        try {
                            $null = Stop-DbaSqlService -ComputerName $computerFullName -InstanceName $instanceName -Type Agent, Engine
                            $null = Start-DbaSqlService -ComputerName $computerFullName -InstanceName $instanceName -Type Agent, Engine
                        }
                        catch {
                            Stop-Function -Message "Issue restarting $instance" -Target $instance -Continue
                        }
                    }
                }
                $newState = GetDbaAgHadr -SqlInstance $instance -Credential $Credential

                if (Test-Bound -Not -ParameterName Force) {
                    Write-Message -Level Warning -Message "You must restart the SQL Server for it to take effect."
                }

                [PSCustomObject]@{
                    ComputerName   = $newState.ComputerName
                    InstanceName   = $newState.InstanceName
                    SqlInstance    = $newState.SqlInstance
                    IsHadrEnabled  = $false
                }
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#

function Disable-DbaForceNetworkEncryption {
    <#
        .SYNOPSIS
            Disables Force Encryption for a SQL Server instance

        .DESCRIPTION
            Disables Force Encryption for a SQL Server instance. Note that this requires access to the Windows Server, not the SQL instance itself.

            This setting is found in Configuration Manager.

        .PARAMETER SqlInstance
            The target SQL Server. Defaults to localhost.

        .PARAMETER Credential
            Allows you to login to the computer (not SQL Server instance) using alternative Windows credentials.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .EXAMPLE
            Disable-DbaForceNetworkEncryption

            Disables Force Encryption on the default (MSSQLSERVER) instance on localhost - requires (and checks for) RunAs admin.

        .EXAMPLE
            Disable-DbaForceNetworkEncryption -SqlInstance sql01\SQL2008R2SP2

            Disables Force Network Encryption for the SQL2008R2SP2 on sql01. Uses Windows Credentials to both login and modify the registry.

        .EXAMPLE
            Disable-DbaForceNetworkEncryption -SqlInstance sql01\SQL2008R2SP2 -WhatIf

            Shows what would happen if the command were executed.

        .NOTES
            Tags: Certificate

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
    #>
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Low")]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer", "ComputerName")]
        [DbaInstanceParameter[]]$SqlInstance = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {

        foreach ($instance in $sqlinstance) {
            Write-Message -Level VeryVerbose -Message "Processing $instance." -Target $instance
            $null = Test-ElevationRequirement -ComputerName $instance -Continue

            Write-Message -Level Verbose -Message "Resolving hostname."
            $resolved = $null
            $resolved = Resolve-DbaNetworkName -ComputerName $instance -Turbo

            if ($null -eq $resolved) {
                Stop-Function -Message "Can't resolve $instance." -Target $instance -Continue -Category InvalidArgument
            }

            Write-Message -Level Output -Message "Connecting to SQL WMI on $($instance.ComputerName)."
            try {
                $sqlwmi = Invoke-ManagedComputerCommand -ComputerName $resolved.FullComputerName -ScriptBlock { $wmi.Services } -Credential $Credential -ErrorAction Stop | Where-Object DisplayName -eq "SQL Server ($($instance.InstanceName))"
            }
            catch {
                Stop-Function -Message "Failed to access $instance." -Target $instance -Continue -ErrorRecord $_
            }

            $regroot = ($sqlwmi.AdvancedProperties | Where-Object Name -eq REGROOT).Value
            $vsname = ($sqlwmi.AdvancedProperties | Where-Object Name -eq VSNAME).Value
            try {
                $instancename = $sqlwmi.DisplayName.Replace('SQL Server (', '').Replace(')', '') # Don't clown, I don't know regex :(
            }
            catch {
                # Probably because the instance name has been aliased or does not exist or samthin
            }
            $serviceaccount = $sqlwmi.ServiceAccount

            if ([System.String]::IsNullOrEmpty($regroot)) {
                $regroot = $sqlwmi.AdvancedProperties | Where-Object { $_ -match 'REGROOT' }
                $vsname = $sqlwmi.AdvancedProperties | Where-Object { $_ -match 'VSNAME' }

                if (![System.String]::IsNullOrEmpty($regroot)) {
                    $regroot = ($regroot -Split 'Value\=')[1]
                    $vsname = ($vsname -Split 'Value\=')[1]
                }
                else {
                    Stop-Function -Message "Can't find instance $vsname on $instance." -Continue -Category ObjectNotFound -Target $instance
                }
            }

            if ([System.String]::IsNullOrEmpty($vsname)) { $vsname = $instance }

            Write-Message -Level Output -Message "Regroot: $regroot" -Target $instance
            Write-Message -Level Output -Message "ServiceAcct: $serviceaccount" -Target $instance
            Write-Message -Level Output -Message "InstanceName: $instancename" -Target $instance
            Write-Message -Level Output -Message "VSNAME: $vsname" -Target $instance

            $scriptblock = {
                $regpath = "Registry::HKEY_LOCAL_MACHINE\$($args[0])\MSSQLServer\SuperSocketNetLib"
                $cert = (Get-ItemProperty -Path $regpath -Name Certificate).Certificate
                $oldvalue = (Get-ItemProperty -Path $regpath -Name ForceEncryption).ForceEncryption
                Set-ItemProperty -Path $regpath -Name ForceEncryption -Value $false
                $forceencryption = (Get-ItemProperty -Path $regpath -Name ForceEncryption).ForceEncryption

                [pscustomobject]@{
                    ComputerName          = $env:COMPUTERNAME
                    InstanceName          = $args[2]
                    SqlInstance           = $args[1]
                    ForceEncryption       = ($forceencryption -eq $true)
                    CertificateThumbprint = $cert
                }
            }

            if ($PScmdlet.ShouldProcess("local", "Connecting to $instance to modify the ForceEncryption value in $regroot for $($instance.InstanceName)")) {
                try {
                    Invoke-Command2 -ComputerName $resolved.FullComputerName -Credential $Credential -ArgumentList $regroot, $vsname, $instancename -ScriptBlock $scriptblock -ErrorAction Stop
                    Write-Message -Level Critical -Message "Force encryption was successfully set on $($resolved.FullComputerName) for the $instancename instance. You must now restart the SQL Server for changes to take effect." -Target $instance
                }
                catch {
                    Stop-Function -Message "Failed to connect to $($resolved.FullComputerName) using PowerShell remoting!" -ErrorRecord $_ -Target $instance -Continue
                }
            }
        }
    }
}
function Disable-DbaTraceFlag {
    <#
    .SYNOPSIS
        Disable a Global Trace Flag that is currently running

    .DESCRIPTION
        The function will disable a Trace Flag that is currently running globally on the SQL Server instance(s) listed

    .PARAMETER SqlInstance
        Allows you to specify a comma separated list of servers to query.

    .PARAMETER SqlCredential
        Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

    .PARAMETER TraceFlag
        Trace flag number to enable globally

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Tags: TraceFlag
        Author: Garry Bargsley (@gbargsley), http://blog.garrybargsley.com

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Disable-DbaTraceFlag

    .EXAMPLE
        Disable-DbaTraceFlag -SqlInstance sql2016 -TraceFlag 3226
        Disable the globally running trace flag 3226 on SQL Server instance sql2016
#>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory)]
        [int[]]$TraceFlag,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $current = Get-DbaTraceFlag -SqlInstance $server -EnableException

            foreach ($tf in $TraceFlag) {
                $TraceFlagInfo = [pscustomobject]@{
                    SourceServer = $server.ComputerName
                    InstanceName = $server.ServiceName
                    SqlInstance  = $server.DomainInstanceName
                    TraceFlag    = $tf
                    Status       = $null
                    Notes        = $null
                    DateTime     = [DbaDateTime](Get-Date)
                }
                if ($tf -notin $current.TraceFlag) {
                    $TraceFlagInfo.Status = 'Skipped'
                    $TraceFlagInfo.Notes = "Trace Flag is not running."
                    $TraceFlagInfo
                    Write-Message -Level Warning -Message "Trace Flag $tf is not currently running on $instance"
                    continue
                }

                try {
                    $query = "DBCC TRACEOFF ($tf, -1)"
                    $server.Query($query)
                }
                catch {
                    $TraceFlagInfo.Status = "Failed"
                    $TraceFlagInfo.Notes = $_.Exception.Message
                    $TraceFlagInfo
                    Stop-Function -Message "Failure" -ErrorRecord $_ -Target $server -Continue
                }
                $TraceFlagInfo.Status = "Successful"
                $TraceFlagInfo
            }
        }
    }
}
function Dismount-DbaDatabase {
    <#
        .SYNOPSIS
            Detach a SQL Server Database.

        .DESCRIPTION
            This command detaches one or more SQL Server databases. If necessary, -Force can be used to break mirrors and remove databases from availability groups prior to detaching.

        .PARAMETER SqlInstance
            The SQL Server instance.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to detach.

        .PARAMETER FileStructure
            A StringCollection object value that contains a list database files. If FileStructure is not specified, BackupHistory will be used to guess the structure.

        .PARAMETER InputObject
            A collection of databases (such as returned by Get-DbaDatabase), to be detached.

        .PARAMETER UpdateStatistics
            If this switch is enabled, statistics for the database will be updated prior to detaching it.

        .PARAMETER Force
            If this switch is enabled and the database is part of a mirror, the mirror will be broken. If the database is part of an Availability Group, it will be removed from the AG.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Dismount-DbaDatabase

        .EXAMPLE
            Detach-DbaDatabase -SqlInstance sql2016b -Database SharePoint_Config, WSS_Logging

            Detaches SharePoint_Config and WSS_Logging from sql2016b

        .EXAMPLE
            Get-DbaDatabase -SqlInstance sql2016b -Database 'PerformancePoint Service Application_10032db0fa0041df8f913f558a5dc0d4' | Detach-DbaDatabase -Force

            Detaches 'PerformancePoint Service Application_10032db0fa0041df8f913f558a5dc0d4' from sql2016b. Since Force was specified, if the database is part of mirror, the mirror will be broken prior to detaching.

            If the database is part of an Availability Group, it will first be dropped prior to detachment.

            .EXAMPLE
            Get-DbaDatabase -SqlInstance sql2016b -Database WSS_Logging | Detach-DbaDatabase -Force -WhatIf

            Shows what would happen if the command were to execute (without actually executing the detach/break/remove commands).

    #>
    [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = "Default")]
    Param (
        [parameter(Mandatory, ParameterSetName = 'SqlInstance')]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory, ParameterSetName = 'SqlInstance')]
        [string]$Database,
        [parameter(Mandatory, ParameterSetName = 'Pipeline', ValueFromPipeline)]
        [Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
        [Switch]$UpdateStatistics,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($Database) {
                $InputObject += $server.Databases | Where-Object Name -in $Database
            }
            else {
                $InputObject += $server.Databases
            }

            if ($ExcludeDatabase) {
                $InputObject = $InputObject | Where-Object Name -NotIn $ExcludeDatabase
            }
        }

        foreach ($db in $InputObject) {
            $db.Refresh()
            $server = $db.Parent

            if ($db.IsSystemObject) {
                Stop-Function -Message "$db is a system database and cannot be detached using this method." -Target $db -Continue
            }

            Write-Message -Level Verbose -Message "Checking replication status."
            if ($db.ReplicationOptions -ne "None") {
                Stop-Function -Message "Skipping $db  on $server because it is replicated." -Target $db -Continue
            }

            # repeat because different servers could be piped in
            $snapshots = (Get-DbaDbSnapshot -SqlInstance $server).SnapshotOf
            Write-Message -Level Verbose -Message "Checking for snaps"
            if ($db.Name -in $snapshots) {
                Write-Message -Level Warning -Message "Database $db has snapshots, you need to drop them before detaching. Skipping $db on $server."
                Continue
            }

            Write-Message -Level Verbose -Message "Checking mirror status"
            if ($db.IsMirroringEnabled -and !$Force) {
                Stop-Function -Message "$db on $server is being mirrored. Use -Force to break mirror or use the safer backup/restore method." -Target $db -Continue
            }

            Write-Message -Level Verbose -Message "Checking Availability Group status"

            if ($db.AvailabilityGroupName -and !$Force) {
                $ag = $db.AvailabilityGroupName
                Stop-Function -Message "$db on $server is part of an Availability Group ($ag). Use -Force to drop from $ag availability group to detach. Alternatively, you can use the safer backup/restore method." -Target $db -Continue
            }

            $sessions = Get-DbaProcess -SqlInstance $db.Parent -Database $db.Name

            if ($sessions -and !$Force) {
                Stop-Function -Message "$db on $server currently has connected users and cannot be dropped. Use -Force to kill all connections and detach the database." -Target $db -Continue
            }

            if ($force) {

                if ($sessions) {
                    If ($Pscmdlet.ShouldProcess($server, "Killing $($sessions.count) sessions which are connected to $db")) {
                        $null = $sessions | Stop-DbaProcess -ErrorAction SilentlyContinue -WarningAction SilentlyContinue
                    }
                }

                if ($db.IsMirroringEnabled) {
                    If ($Pscmdlet.ShouldProcess($server, "Breaking mirror for $db on $server")) {
                        try {
                            Write-Message -Level Warning -Message "Breaking mirror for $db on $server."
                            $db.ChangeMirroringState([Microsoft.SqlServer.Management.Smo.MirroringOption]::Off)
                            $db.Alter()
                            $db.Refresh()
                        }
                        catch {
                            Stop-Function -Message "Could not break mirror for $db on $server - not detaching." -Target $db -ErrorRecord $_ -Continue
                        }
                    }
                }

                if ($db.AvailabilityGroupName) {
                    $ag = $db.AvailabilityGroupName
                    If ($Pscmdlet.ShouldProcess($server, "Attempting remove $db on $server from Availability Group $ag")) {
                        try {
                            $server.AvailabilityGroups[$ag].AvailabilityDatabases[$db.name].Drop()
                            Write-Message -Level Verbose -Message "Successfully removed $db from  detach from $ag on $server."
                        }
                        catch {
                            if ($_.Exception.InnerException) {
                                $exception = $_.Exception.InnerException.ToString() -Split "System.Data.SqlClient.SqlException: "
                                $exception = " | $(($exception[1] -Split "at Microsoft.SqlServer.Management.Common.ConnectionManager")[0])".TrimEnd()
                            }

                            Stop-Function -Message "Could not remove $db from $ag on $server $exception." -Target $db -ErrorRecord $_ -Continue
                        }
                    }
                }

                $sessions = Get-DbaProcess -SqlInstance $db.Parent -Database $db.Name

                if ($sessions) {
                    If ($Pscmdlet.ShouldProcess($server, "Killing $($sessions.count) sessions which are still connected to $db")) {
                        $null = $sessions | Stop-DbaProcess -ErrorAction SilentlyContinue -WarningAction SilentlyContinue
                    }
                }
            }

            If ($Pscmdlet.ShouldProcess($server, "Detaching $db on $server")) {
                try {
                    $server.DetachDatabase($db.Name, $UpdateStatistics)

                    [pscustomobject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Database     = $db.name
                        DetachResult = "Success"
                    }
                }
                catch {
                    Stop-Function -Message "Failure" -Target $db -ErrorRecord $_ -Continue
                }
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Enable-DbaAgHadr {
    <#
        .SYNOPSIS
            Enables the Hadr service setting on the specified SQL Server.

        .DESCRIPTION
            In order to build an AG a cluster has to be built and then the Hadr enabled for the SQL Server
            service. This function enables that feature for the SQL Server service.

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to.

        .PARAMETER Credential
            Credential object used to connect to the Windows server itself as a different user

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER Force
            Will restart SQL Server and SQL Server Agent service to apply the change.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Hadr, AG, AvailabilityGroup
            Author: Shawn Melton (@wsmelton | http://blog.wsmelton.info)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Enable-DbaAgHadr

        .EXAMPLE
            Enable-DbaAgHadr -SqlInstance sql2016 -Force

            Sets Hadr service to enabled for the instance sql2016, and restart the service to apply the change.

        .EXAMPLE
            Enable-DbaAgHadr -SqlInstance sql2012\dev1 -Force

            Sets Hadr service to disabled for the instance dev1 on sq2012, and restart the service to apply the change.
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$Credential,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        function GetDbaAgHadr {
    <#
        .SYNOPSIS
            Gets the Hadr service setting on the specified SQL Server instance.

        .DESCRIPTION
            Gets the Hadr setting, from the service level, and returns true or false for the specified SQL Server instance.

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to.

        .PARAMETER Credential
            Credential object used to connect to the Windows server itself as a different user

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Hadr, AG, AvailabilityGroup
            Author: Shawn Melton (@wsmelton | http://blog.wsmelton.info)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/GetDbaAgHadr

        .EXAMPLE
            GetDbaAgHadr -SqlInstance sql2016

            Returns a status of the Hadr setting for sql2016 SQL Server instance.
    #>
            [CmdletBinding()]
            param (
                [parameter(Mandatory = $true, ValueFromPipeline = $true)]
                [Alias("ServerInstance", "SqlServer")]
                [DbaInstanceParameter[]]$SqlInstance,
                [PSCredential]$Credential,
                [Alias('Silent')]
                [switch]$EnableException
            )
            process {
                foreach ($instance in $SqlInstance) {

                    try {
                        $computer = $computerName = $instance.ComputerName
                        $instanceName = $instance.InstanceName
                        Write-Message -Level Verbose -Message "Connecting to $computer"
                        $currentState = Invoke-ManagedComputerCommand -ComputerName $computerName -ScriptBlock { $wmi.Services[$args[0]] | Select-Object IsHadrEnabled } -ArgumentList $instanceName -Credential $Credential
                    }
                    catch {
                        Stop-Function -Message "Failure connecting to $computer" -Category ConnectionError -ErrorRecord $_ -Target $instance
                        return
                    }

                    if ($null -eq $currentState.IsHadrEnabled) {
                        $isenabled = $false
                    }
                    else {
                        $isenabled = $currentState.IsHadrEnabled
                    }
                    [PSCustomObject]@{
                        ComputerName     = $computer
                        InstanceName     = $instanceName
                        SqlInstance      = $instance.FullName
                        IsHadrEnabled    = $isenabled
                    }
                }
            }
        }
    }
    process {
        foreach ($instance in $SqlInstance) {
            $computer = $computerFullName = $instance.ComputerName
            $instanceName = $instance.InstanceName
            if (-not (Test-ElevationRequirement -ComputerName $instance)) {
                return
            }
            $noChange = $false

            switch ($instance.InstanceName) {
                'MSSQLSERVER' { $agentName = 'SQLSERVERAGENT' }
                default { $agentName = "SQLAgent`$$instanceName" }
            }

            try {
                Write-Message -Level Verbose -Message "Checking current Hadr setting for $computer"
                $currentState = GetDbaAgHadr -SqlInstance $instance -Credential $Credential
            }
            catch {
                Stop-Function -Message "Failure to pull current state of Hadr setting on $computer" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            $isHadrEnabled = $currentState.IsHadrEnabled
            Write-Message -Level InternalComment -Message "$instance Hadr current value: $isHadrEnabled"

            # hadr results from sql wmi can be iffy, skip the check
            <#
            if ($isHadrEnabled) {
                Write-Message -Level Warning -Message "Hadr is already enabled for instance: $($instance.FullName)"
                $noChange = $true
                continue
            }
            #>

            $scriptblock = {
                $instance = $args[0]
                $sqlService = $wmi.Services | Where-Object DisplayName -eq "SQL Server ($instance)"
                $sqlService.ChangeHadrServiceSetting(1)
            }

            if ($noChange -eq $false) {
                if ($PSCmdlet.ShouldProcess($instance, "Changing Hadr from $isHadrEnabled to 1 for $instance")) {
                    try {
                        Invoke-ManagedComputerCommand -ComputerName $computerFullName -Credential $Credential -ScriptBlock $scriptblock -ArgumentList $instancename
                    }
                    catch {
                        Stop-Function -Continue -Message "Failure on $($instance.FullName) | This may be because AlwaysOn Availability Groups feature requires the x86(non-WOW) or x64 Enterprise Edition of SQL Server 2012 (or later version) running on Windows Server 2008 (or later version) with WSFC hotfix KB 2494036 installed."
                    }
                }
            }

            if (Test-Bound -ParameterName Force) {
                if ($PSCmdlet.ShouldProcess($instance, "Force provided, restarting Engine and Agent service for $instance on $computerFullName")) {
                    try {
                        $null = Stop-DbaSqlService -ComputerName $computerFullName -InstanceName $instanceName -Type Agent, Engine
                        $null = Start-DbaSqlService -ComputerName $computerFullName -InstanceName $instanceName -Type Agent, Engine
                    }
                    catch {
                        Stop-Function -Message "Issue restarting $instance" -Target $instance -Continue
                    }
                }
            }
            $newState = GetDbaAgHadr -SqlInstance $instance -Credential $Credential

            if (Test-Bound -Not -ParameterName Force) {
                Write-Message -Level Warning -Message "You must restart the SQL Server for it to take effect."
            }

            [PSCustomObject]@{
                ComputerName    = $newState.ComputerName
                InstanceName    = $newState.InstanceName
                SqlInstance     = $newState.SqlInstance
                IsHadrEnabled   = $true
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Enable-DbaForceNetworkEncryption {
    <#
        .SYNOPSIS
            Enables Force Encryption for a SQL Server instance.

        .DESCRIPTION
            Enables Force Encryption for a SQL Server instance. Note that this requires access to the Windows Server, not the SQL instance itself.

            This setting is found in Configuration Manager.

        .PARAMETER SqlInstance
            The target SQL Server.

        .PARAMETER Credential
            Allows you to login to the computer (not SQL Server instance) using alternative Windows credentials

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .EXAMPLE
            Enable-DbaForceNetworkEncryption

            Enables Force Encryption on the default (MSSQLSERVER) instance on localhost. Requires (and checks for) RunAs admin.

        .EXAMPLE
            Enable-DbaForceNetworkEncryption -SqlInstance sql01\SQL2008R2SP2

            Enables Force Network Encryption for the SQL2008R2SP2 on sql01. Uses Windows Credentials to both connect and modify the registry.

        .EXAMPLE
            Enable-DbaForceNetworkEncryption -SqlInstance sql01\SQL2008R2SP2 -WhatIf

            Shows what would happen if the command were executed.

        .NOTES
            Tags: Certificate

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
    #>
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Low", DefaultParameterSetName = 'Default')]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer", "ComputerName")]
        [DbaInstanceParameter[]]
        $SqlInstance = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {

        foreach ($instance in $sqlinstance) {
            Write-Message -Level VeryVerbose -Message "Processing $instance." -Target $instance
            $null = Test-ElevationRequirement -ComputerName $instance -Continue

            Write-Message -Level Verbose -Message "Resolving hostname."
            $resolved = $null
            $resolved = Resolve-DbaNetworkName -ComputerName $instance -Turbo

            if ($null -eq $resolved) {
                Stop-Function -Message "Can't resolve $instance." -Target $instance -Continue -Category InvalidArgument
            }

            Write-Message -Level Output -Message "Connecting to SQL WMI on $($instance.ComputerName)."
            try {
                $sqlwmi = Invoke-ManagedComputerCommand -ComputerName $resolved.FullComputerName -ScriptBlock { $wmi.Services } -Credential $Credential -ErrorAction Stop | Where-Object DisplayName -eq "SQL Server ($($instance.InstanceName))"
            }
            catch {
                Stop-Function -Message "Failed to access $instance" -Target $instance -Continue -ErrorRecord $_
            }

            $regroot = ($sqlwmi.AdvancedProperties | Where-Object Name -eq REGROOT).Value
            $vsname = ($sqlwmi.AdvancedProperties | Where-Object Name -eq VSNAME).Value
            try {
                $instancename = $sqlwmi.DisplayName.Replace('SQL Server (', '').Replace(')', '') # Don't clown, I don't know regex :(
            }
            catch {
                # Probably because the instance name has been aliased or does not exist or samthin
            }
            $serviceaccount = $sqlwmi.ServiceAccount

            if ([System.String]::IsNullOrEmpty($regroot)) {
                $regroot = $sqlwmi.AdvancedProperties | Where-Object { $_ -match 'REGROOT' }
                $vsname = $sqlwmi.AdvancedProperties | Where-Object { $_ -match 'VSNAME' }

                if (![System.String]::IsNullOrEmpty($regroot)) {
                    $regroot = ($regroot -Split 'Value\=')[1]
                    $vsname = ($vsname -Split 'Value\=')[1]
                }
                else {
                    Stop-Function -Message "Can't find instance $vsname on $instance." -Continue -Category ObjectNotFound -Target $instance
                }
            }

            if ([System.String]::IsNullOrEmpty($vsname)) { $vsname = $instance }

            Write-Message -Level Output -Message "Regroot: $regroot" -Target $instance
            Write-Message -Level Output -Message "ServiceAcct: $serviceaccount" -Target $instance
            Write-Message -Level Output -Message "InstanceName: $instancename" -Target $instance
            Write-Message -Level Output -Message "VSNAME: $vsname" -Target $instance

            $scriptblock = {
                $regpath = "Registry::HKEY_LOCAL_MACHINE\$($args[0])\MSSQLServer\SuperSocketNetLib"
                $cert = (Get-ItemProperty -Path $regpath -Name Certificate).Certificate
                $oldvalue = (Get-ItemProperty -Path $regpath -Name ForceEncryption).ForceEncryption
                Set-ItemProperty -Path $regpath -Name ForceEncryption -Value $true
                $forceencryption = (Get-ItemProperty -Path $regpath -Name ForceEncryption).ForceEncryption

                [pscustomobject]@{
                    ComputerName          = $env:COMPUTERNAME
                    InstanceName          = $args[2]
                    SqlInstance           = $args[1]
                    ForceEncryption       = ($forceencryption -eq $true)
                    CertificateThumbprint = $cert
                }
            }

            if ($PScmdlet.ShouldProcess("local", "Connecting to $instance to modify the ForceEncryption value in $regroot for $($instance.InstanceName)")) {
                try {
                    Invoke-Command2 -ComputerName $resolved.FullComputerName -Credential $Credential -ArgumentList $regroot, $vsname, $instancename -ScriptBlock $scriptblock -ErrorAction Stop | Select-Object -Property * -ExcludeProperty PSComputerName, RunspaceId, PSShowComputerName
                    Write-Message -Level Critical -Message "Force encryption was successfully set on $($resolved.FullComputerName) for the $instancename instance. You must now restart the SQL Server for changes to take effect." -Target $instance
                }
                catch {
                    Stop-Function -Message "Failed to connect to $($resolved.FullComputerName) using PowerShell remoting!" -ErrorRecord $_ -Target $instance -Continue
                }
            }
        }
    }
}
function Enable-DbaTraceFlag {
    <#
    .SYNOPSIS
        Enable Global Trace Flag(s)
    .DESCRIPTION
        The function will set one or multiple trace flags on the SQL Server instance(s) listed

    .PARAMETER SqlInstance
        Allows you to specify a comma separated list of servers to query.

    .PARAMETER SqlCredential
        Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

    .PARAMETER TraceFlag
        Trace flag number(s) to enable globally

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Tags: TraceFlag
        Author: Garry Bargsley (@gbargsley), http://blog.garrybargsley.com

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Enable-DbaTraceFlag

    .EXAMPLE
        Enable-DbaTraceFlag -SqlInstance sql2016 -TraceFlag 3226
        Enable the trace flag 3226 on SQL Server instance sql2016

    .EXAMPLE
        Enable-DbaTraceFlag -SqlInstance sql2016 -TraceFlag 1117, 1118
        Enable multiple trace flags on SQL Server instance sql2016
#>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory)]
        [int[]]$TraceFlag,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $CurrentRunningTraceFlags = Get-DbaTraceFlag -SqlInstance $server -EnableException

            # We could combine all trace flags but the granularity is worth it
            foreach ($tf in $TraceFlag) {
                $TraceFlagInfo = [PSCustomObject]@{
                    SourceServer = $server.ComputerName
                    InstanceName = $server.ServiceName
                    SqlInstance  = $server.DomainInstanceName
                    TraceFlag    = $tf
                    Status       = $null
                    Notes        = $null
                    DateTime     = [DbaDateTime](Get-Date)
                }
                if ($CurrentRunningTraceFlags.TraceFlag -contains $tf) {
                    $TraceFlagInfo.Status = 'Skipped'
                    $TraceFlagInfo.Notes = "The Trace flag is already running."
                    $TraceFlagInfo
                    Write-Message -Level Warning -Message "The Trace flag [$tf] is already running globally."
                    continue
                }

                try {
                    $query = "DBCC TRACEON ($tf, -1)"
                    $server.Query($query)
                    $server.Refresh()
                }
                catch {
                    $TraceFlagInfo.Status = "Failed"
                    $TraceFlagInfo.Notes = $_.Exception.Message
                    $TraceFlagInfo
                    Stop-Function -Message "Failure" -ErrorRecord $_ -Target $server -Continue
                }
                $TraceFlagInfo.Status = "Successful"
                $TraceFlagInfo
            }
        }
    }
}
function Expand-DbaTLogResponsibly {
    <#
        .SYNOPSIS
            This command will help you to automatically grow your transaction log  file in a responsible way (preventing the generation of too many VLFs).

        .DESCRIPTION
            As you may already know, having a transaction log file with too many Virtual Log Files (VLFs) can hurt your database performance in many ways.

            Example:
                Too many VLFs can cause transaction log backups to slow down and can also slow down database recovery and, in extreme cases, even impact insert/update/delete performance.

                References:
                    http://www.sqlskills.com/blogs/kimberly/transaction-log-vlfs-too-many-or-too-few/
                    http://blogs.msdn.com/b/saponsqlserver/archive/2012/02/22/too-many-virtual-log-files-vlfs-can-cause-slow-database-recovery.aspx
                    http://www.brentozar.com/blitz/high-virtual-log-file-vlf-count/

                In order to get rid of this fragmentation we need to grow the file taking the following into consideration:
                    - How many VLFs are created when we perform a grow operation or when an auto-grow is invoked?

                Note: In SQL Server 2014 this algorithm has changed (http://www.sqlskills.com/blogs/paul/important-change-vlf-creation-algorithm-sql-server-2014/)

            Attention:
                We are growing in MB instead of GB because of known issue prior to SQL 2012:
                    More detail here:
                        http://www.sqlskills.com/BLOGS/PAUL/post/Bug-log-file-growth-broken-for-multiples-of-4GB.aspx
                    and
                        http://connect.microsoft.com/SqlInstance/feedback/details/481594/log-growth-not-working-properly-with-specific-growth-sizes-vlfs-also-not-created-appropriately
                    or
                        https://connect.microsoft.com/SqlInstance/feedback/details/357502/transaction-log-file-size-will-not-grow-exactly-4gb-when-filegrowth-4gb

            Understanding related problems:
                    http://www.sqlskills.com/blogs/kimberly/transaction-log-vlfs-too-many-or-too-few/
                    http://blogs.msdn.com/b/saponsqlserver/archive/2012/02/22/too-many-virtual-log-files-vlfs-can-cause-slow-database-recovery.aspx
                    http://www.brentozar.com/blitz/high-virtual-log-file-vlf-count/

            Known bug before SQL Server 2012
                    http://www.sqlskills.com/BLOGS/PAUL/post/Bug-log-file-growth-broken-for-multiples-of-4GB.aspx
                    http://connect.microsoft.com/SqlInstance/feedback/details/481594/log-growth-not-working-properly-with-specific-growth-sizes-vlfs-also-not-created-appropriately
                    https://connect.microsoft.com/SqlInstance/feedback/details/357502/transaction-log-file-size-will-not-grow-exactly-4gb-when-filegrowth-4gb

            How it works?
                The transaction log will grow in chunks until it reaches the desired size.
                Example: If you have a log file with 8192MB and you say that the target size is 81920MB (80GB) it will grow in chunks of 8192MB until it reaches 81920MB. 8192 -> 16384 -> 24576 ... 73728 -> 81920

        .PARAMETER SqlInstance
            The target SQL Server instance.

        .PARAMETER Database
            The database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER TargetLogSizeMB
            Specifies the target size of the transaction log file in megabytes.

        .PARAMETER IncrementSizeMB
            Specifies the amount the transaction log should grow in megabytes. If this value differs from the suggested value based on your TargetLogSizeMB, you will be prompted to confirm your choice.

            This value will be calculated if not specified.

        .PARAMETER LogFileId
            Specifies the file number(s) of additional transaction log files to grow.

            If this value is not specified, only the first transaction log file will be processed.

        .PARAMETER ShrinkLogFile
            If this switch is enabled, your transaction log files will be shrunk.

        .PARAMETER ShrinkSizeMB
            Specifies the target size of the transaction log file for the shrink operation.

        .PARAMETER BackupDirectory
            Specifies the location of your backups. Backups must be performed to shrink the transaction log.

            If this value is not specified, the SQL Server instance's default backup directory will be used.

         .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER ExcludeDatabase
            The database(s) to exclude. Options for this list are auto-populated from the server.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Storage, Backup
            This script uses Get-DbaDiskSpace dbatools command to get the TLog's drive free space

            Author: Claudio Silva (@ClaudioESSilva)
            Requires: ALTER DATABASE permission
            Limitations: Freespace cannot be validated on the directory where the log file resides in SQL Server 2005.

            Website: https://dbatools.io
            Copyright (C) 2016 Chrissy LeMaire
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Expand-DbaTLogResponsibly

        .EXAMPLE
            Expand-DbaTLogResponsibly -SqlInstance sqlcluster -Database db1 -TargetLogSizeMB 50000

            Grows the transaction log for database db1 on sqlcluster to 50000 MB and calculates the increment size.

        .EXAMPLE
            Expand-DbaTLogResponsibly -SqlInstance sqlcluster -Database db1, db2 -TargetLogSizeMB 10000 -IncrementSizeMB 200

            Grows the transaction logs for databases db1 and db2 on sqlcluster to 1000MB and sets the growth increment to 200MB.

        .EXAMPLE
            Expand-DbaTLogResponsibly -SqlInstance sqlcluster -Database db1 -TargetLogSizeMB 10000 -LogFileId 9

            Grows the transaction log file  with FileId 9 of the db1 database on sqlcluster instance to 10000MB.

        .EXAMPLE
            Expand-DbaTLogResponsibly -SqlInstance sqlcluster -Database (Get-Content D:\DBs.txt) -TargetLogSizeMB 50000

            Grows the transaction log of the databases specified in the file 'D:\DBs.txt' on sqlcluster instance to 50000MB.

        .EXAMPLE
            Expand-DbaTLogResponsibly -SqlInstance SqlInstance -Database db1,db2 -TargetLogSizeMB 100 -IncrementSizeMB 10 -ShrinkLogFile -ShrinkSizeMB 10 -BackupDirectory R:\MSSQL\Backup

            Grows the transaction logs for databases db1 and db2 on SQL server SQLInstance to 100MB, sets the incremental growth to 10MB, shrinks the transaction log to 10MB and uses the directory R:\MSSQL\Backup for the required backups.
    #>
    [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Default')]
    param (
        [parameter(Position = 1, Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [parameter(Position = 3)]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [parameter(Position = 4)]
        [object[]]$ExcludeDatabase,
        [parameter(Position = 5, Mandatory = $true)]
        [int]$TargetLogSizeMB,
        [parameter(Position = 6)]
        [int]$IncrementSizeMB = -1,
        [parameter(Position = 7)]
        [int]$LogFileId = -1,
        [parameter(Position = 8, ParameterSetName = 'Shrink', Mandatory = $true)]
        [switch]$ShrinkLogFile,
        [parameter(Position = 9, ParameterSetName = 'Shrink', Mandatory = $true)]
        [int]$ShrinkSizeMB,
        [parameter(Position = 10, ParameterSetName = 'Shrink')]
        [AllowEmptyString()]
        [string]$BackupDirectory,
        [switch][Alias('Silent')]
        $EnableException
    )

    begin {
        Write-Message -Level Verbose -Message "Set ErrorActionPreference to Inquire."
        $ErrorActionPreference = 'Inquire'

        #Convert MB to KB (SMO works in KB)
        Write-Message -Level Verbose -Message "Convert variables MB to KB (SMO works in KB)."
        [int]$TargetLogSizeKB = $TargetLogSizeMB * 1024
        [int]$LogIncrementSize = $incrementSizeMB * 1024
        [int]$ShrinkSize = $ShrinkSizeMB * 1024
        [int]$SuggestLogIncrementSize = 0
        [bool]$LogByFileID = if ($LogFileId -eq -1) {
            $false
        }
        else {
            $true
        }

        #Set base information
        Write-Message -Level Verbose -Message "Initialize the instance '$SqlInstance'."

        $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential

        if ($ShrinkLogFile -eq $true) {
            if ($BackupDirectory.length -eq 0) {
                $backupdirectory = $server.Settings.BackupDirectory
            }

            $pathexists = Test-DbaSqlPath -SqlInstance $server -Path $backupdirectory

            if ($pathexists -eq $false) {
                Stop-Function -Message "Backup directory does not exist."
            }
        }
    }

    process {

        try {

            [datetime]$initialTime = Get-Date

            #control the iteration number
            $databaseProgressbar = 0;

            Write-Message -Level Verbose -Message "Resolving NetBIOS name."
            $sourcenetbios = Resolve-NetBiosName $server

            $databases = $server.Databases | Where-Object IsAccessible
            Write-Message -Level Verbose -Message "Number of databases found: $($databases.Count)."
            if ($Database) {
                $databases = $databases | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $database = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }

            #go through all databases
            Write-Message -Level Verbose -Message "Processing...foreach database..."
            foreach ($db in $databases.Name) {
                Write-Message -Level Verbose -Message "Working on $db."
                $databaseProgressbar += 1

                #set step to reutilize on logging operations
                [string]$step = "$databaseProgressbar/$($Database.Count)"

                if ($server.Databases[$db]) {
                    Write-Progress `
                        -Id 1 `
                        -Activity "Using database: $db on Instance: '$SqlInstance'" `
                        -PercentComplete ($databaseProgressbar / $Database.Count * 100) `
                        -Status "Processing - $databaseProgressbar of $($Database.Count)"

                    #Validate which file will grow
                    if ($LogByFileID) {
                        $logfile = $server.Databases[$db].LogFiles.ItemById($LogFileId)
                    }
                    else {
                        $logfile = $server.Databases[$db].LogFiles[0]
                    }

                    $numLogfiles = $server.Databases[$db].LogFiles.Count

                    Write-Message -Level Verbose -Message "$step - Use log file: $logfile."
                    $currentSize = $logfile.Size
                    $currentSizeMB = $currentSize / 1024

                    #Get the number of VLFs
                    $initialVLFCount = Test-DbaDbVirtualLogFile -SqlInstance $server -Database $db

                    Write-Message -Level Verbose -Message "$step - Log file current size: $([System.Math]::Round($($currentSize/1024.0), 2)) MB "
                    [long]$requiredSpace = ($TargetLogSizeKB - $currentSize)

                    Write-Message -Level Verbose -Message "Verifying if sufficient space exists ($([System.Math]::Round($($requiredSpace / 1024.0), 2))MB) on the volume to perform this task."

                    [long]$TotalTLogFreeDiskSpaceKB = 0
                    Write-Message -Level Verbose -Message "Get TLog drive free space"
                    [object]$AllDrivesFreeDiskSpace = Get-DbaDiskSpace -ComputerName $sourcenetbios | Select-Object Name, SizeInKB

                    #Verify path using Split-Path on $logfile.FileName in backwards. This way we will catch the LUNs. Example: "K:\Log01" as LUN name. Need to add final backslash if not there
                    $DrivePath = Split-Path $logfile.FileName -parent
                    $DrivePath = if (!($DrivePath.EndsWith("\"))) { "$DrivePath\" }
                    else { $DrivePath }
                    Do {
                        if ($AllDrivesFreeDiskSpace | Where-Object { $DrivePath -eq "$($_.Name)" }) {
                            $TotalTLogFreeDiskSpaceKB = ($AllDrivesFreeDiskSpace | Where-Object { $DrivePath -eq $_.Name }).SizeInKB
                            $match = $true
                            break
                        }
                        else {
                            $match = $false
                            $DrivePath = Split-Path $DrivePath -parent
                            $DrivePath = if (!($DrivePath.EndsWith("\"))) { "$DrivePath\" }
                            else { $DrivePath }
                        }

                    }
                    while (!$match -or ([string]::IsNullOrEmpty($DrivePath)))

                    Write-Message -Level Verbose -Message "Total TLog Free Disk Space in MB: $([System.Math]::Round($($TotalTLogFreeDiskSpaceKB / 1024.0), 2))"

                    if (($TotalTLogFreeDiskSpaceKB -le 0) -or ([string]::IsNullOrEmpty($TotalTLogFreeDiskSpaceKB))) {
                        $title = "Choose increment value for database '$db':"
                        $message = "Cannot validate freespace on drive where the log file resides. Do you wish to continue? (Y/N)"
                        $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Will continue"
                        $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Will exit"
                        $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
                        $result = $host.ui.PromptForChoice($title, $message, $options, 0)
                        #no
                        if ($result -eq 1) {
                            Write-Message -Level Warning -Message "You have cancelled the execution"
                            return
                        }
                    }

                    if ($requiredSpace -gt $TotalTLogFreeDiskSpaceKB) {
                        Write-Message -Level Verbose -Message "There is not enough space on volume to perform this task. `r`n" `
                            "Available space: $([System.Math]::Round($($TotalTLogFreeDiskSpaceKB / 1024.0), 2))MB;`r`n" `
                            "Required space: $([System.Math]::Round($($requiredSpace / 1024.0), 2))MB;"
                        return
                    }
                    else {
                        if ($currentSize -ige $TargetLogSizeKB -and ($ShrinkLogFile -eq $false)) {
                            Write-Message -Level Verbose -Message "$step - [INFO] The T-Log file '$logfile' size is already equal or greater than target size - No action required."
                        }
                        else {
                            Write-Message -Level Verbose -Message "$step - [OK] There is sufficient free space to perform this task."

                            # If SQL Server version is greater or equal to 2012
                            if ($server.Version.Major -ge "11") {
                                switch ($TargetLogSizeMB) {
                                    { $_ -le 64 } { $SuggestLogIncrementSize = 64 }
                                    { $_ -ge 64 -and $_ -lt 256 } { $SuggestLogIncrementSize = 256 }
                                    { $_ -ge 256 -and $_ -lt 1024 } { $SuggestLogIncrementSize = 512 }
                                    { $_ -ge 1024 -and $_ -lt 4096 } { $SuggestLogIncrementSize = 1024 }
                                    { $_ -ge 4096 -and $_ -lt 8192 } { $SuggestLogIncrementSize = 2048 }
                                    { $_ -ge 8192 -and $_ -lt 16384 } { $SuggestLogIncrementSize = 4096 }
                                    { $_ -ge 16384 } { $SuggestLogIncrementSize = 8192 }
                                }
                            }
                            # 2008 R2 or under
                            else {
                                switch ($TargetLogSizeMB) {
                                    { $_ -le 64 } { $SuggestLogIncrementSize = 64 }
                                    { $_ -ge 64 -and $_ -lt 256 } { $SuggestLogIncrementSize = 256 }
                                    { $_ -ge 256 -and $_ -lt 1024 } { $SuggestLogIncrementSize = 512 }
                                    { $_ -ge 1024 -and $_ -lt 4096 } { $SuggestLogIncrementSize = 1024 }
                                    { $_ -ge 4096 -and $_ -lt 8192 } { $SuggestLogIncrementSize = 2048 }
                                    { $_ -ge 8192 -and $_ -lt 16384 } { $SuggestLogIncrementSize = 4000 }
                                    { $_ -ge 16384 } { $SuggestLogIncrementSize = 8000 }
                                }

                                if (($IncrementSizeMB % 4096) -eq 0) {
                                    Write-Message -Level Verbose -Message "Your instance version is below SQL 2012, remember the known BUG mentioned on HELP. `r`nUse Get-Help Expand-DbaTLogFileResponsibly to read help`r`nUse a different value for incremental size.`r`n"
                                    return
                                }
                            }
                            Write-Message -Level Verbose -Message "Instance $server version: $($server.Version.Major) - Suggested TLog increment size: $($SuggestLogIncrementSize)MB"

                            # Shrink Log File to desired size before re-growth to desired size (You need to remove as many VLF's as possible to ensure proper growth)
                            $ShrinkSizeMB = $ShrinkSize / 1024
                            if ($ShrinkLogFile -eq $true) {
                                if ($server.Databases[$db].RecoveryModel -eq [Microsoft.SqlServer.Management.Smo.RecoveryModel]::Simple) {
                                    Write-Message -Level Warning -Message "Database '$db' is in Simple RecoveryModel which does not allow log backups. Do not specify -ShrinkLogFile and -ShrinkSizeMB parameters."
                                    Continue
                                }

                                try {
                                    $sql = "SELECT last_log_backup_lsn FROM sys.database_recovery_status WHERE database_id = DB_ID('$db')"
                                    $sqlResult = $server.ConnectionContext.ExecuteWithResults($sql);

                                    if ($sqlResult.Tables[0].Rows[0]["last_log_backup_lsn"] -is [System.DBNull]) {
                                        Write-Message -Level Warning -Message "First, you need to make a full backup before you can do Tlog backup on database '$db' (last_log_backup_lsn is null)."
                                        Continue
                                    }
                                }
                                catch {
                                    Stop-Function -Message "Can't execute SQL on $server. `r`n $($_)" -Continue
                                }

                                If ($Pscmdlet.ShouldProcess($($server.name), "Backing up TLog for $db")) {
                                    Write-Message -Level Verbose -Message "We are about to backup the Tlog for database '$db' to '$backupdirectory' and shrink the log."
                                    Write-Message -Level Verbose -Message "Starting Size = $currentSizeMB."

                                    $DefaultCompression = $server.Configuration.DefaultBackupCompression.ConfigValue

                                    if ($currentSizeMB -gt $ShrinkSizeMB) {
                                        $backupRetries = 1
                                        Do {
                                            try {
                                                $percent = $null
                                                $backup = New-Object Microsoft.SqlServer.Management.Smo.Backup
                                                $backup.Action = [Microsoft.SqlServer.Management.Smo.BackupActionType]::Log
                                                $backup.BackupSetDescription = "Transaction Log backup of " + $db
                                                $backup.BackupSetName = $db + " Backup"
                                                $backup.Database = $db
                                                $backup.MediaDescription = "Disk"
                                                $dt = get-date -format yyyyMMddHHmmssms
                                                $null = $backup.Devices.AddDevice($backupdirectory + "\" + $db + "_db_" + $dt + ".trn", 'File')
                                                if ($DefaultCompression = $true) {
                                                    $backup.CompressionOption = 1
                                                }
                                                else {
                                                    $backup.CompressionOption = 0
                                                }
                                                $null = [Microsoft.SqlServer.Management.Smo.PercentCompleteEventHandler] {
                                                    Write-Progress -id 2 -ParentId 1 -activity "Backing up $db to $server" -percentcomplete $_.Percent -status ([System.String]::Format("Progress: {0} %", $_.Percent))
                                                }
                                                $backup.add_PercentComplete($percent)
                                                $backup.PercentCompleteNotification = 10
                                                $backup.add_Complete($complete)
                                                Write-Progress -id 2 -ParentId 1 -activity "Backing up $db to $server" -percentcomplete 0 -Status ([System.String]::Format("Progress: {0} %", 0))
                                                $backup.SqlBackup($server)
                                                Write-Progress -id 2 -ParentId 1 -activity "Backing up $db to $server" -status "Complete" -Completed
                                                $logfile.Shrink($ShrinkSizeMB, [Microsoft.SqlServer.Management.SMO.ShrinkMethod]::TruncateOnly)
                                                $logfile.Refresh()
                                            }
                                            catch {
                                                Write-Progress -id 1 -activity "Backup" -status "Failed" -completed
                                                Stop-Function -Message "Backup failed for database" -ErrorRecord $_ -Target $db -Continue
                                                Continue
                                            }

                                        }
                                        while (($logfile.Size / 1024) -gt $ShrinkSizeMB -and ++$backupRetries -lt 6)

                                        $currentSize = $logfile.Size
                                        Write-Message -Level Verbose -Message "TLog backup and truncate for database '$db' finished. Current TLog size after $backupRetries backups is $($currentSize/1024)MB"
                                    }
                                }
                            }

                            # SMO uses values in KB
                            $SuggestLogIncrementSize = $SuggestLogIncrementSize * 1024

                            # If default, use $SuggestedLogIncrementSize
                            if ($IncrementSizeMB -eq -1) {
                                $LogIncrementSize = $SuggestLogIncrementSize
                            }
                            else {
                                $title = "Choose increment value for database '$db':"
                                $message = "The input value for increment size was $([System.Math]::Round($LogIncrementSize/1024, 0))MB. However the suggested value for increment is $($SuggestLogIncrementSize/1024)MB.`r`nDo you want to use the suggested value of $([System.Math]::Round($SuggestLogIncrementSize/1024, 0))MB insted of $([System.Math]::Round($LogIncrementSize/1024, 0))MB"
                                $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Uses recomended size."
                                $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Will use parameter value."
                                $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
                                $result = $host.ui.PromptForChoice($title, $message, $options, 0)
                                #yes
                                if ($result -eq 0) {
                                    $LogIncrementSize = $SuggestLogIncrementSize
                                }
                            }

                            #start growing file
                            If ($Pscmdlet.ShouldProcess($($server.name), "Starting log growth. Increment chunk size: $($LogIncrementSize/1024)MB for database '$db'")) {
                                Write-Message -Level Verbose -Message "Starting log growth. Increment chunk size: $($LogIncrementSize/1024)MB for database '$db'"

                                Write-Message -Level Verbose -Message "$step - While current size less than target log size."

                                while ($currentSize -lt $TargetLogSizeKB) {

                                    Write-Progress `
                                        -Id 2 `
                                        -ParentId 1 `
                                        -Activity "Growing file $logfile on '$db' database" `
                                        -PercentComplete ($currentSize / $TargetLogSizeKB * 100) `
                                        -Status "Remaining - $([System.Math]::Round($($($TargetLogSizeKB - $currentSize) / 1024.0), 2)) MB"

                                    Write-Message -Level Verbose -Message "$step - Verifying if the log can grow or if it's already at the desired size."
                                    if (($TargetLogSizeKB - $currentSize) -lt $LogIncrementSize) {
                                        Write-Message -Level Verbose -Message "$step - Log size is lower than the increment size. Setting current size equals $TargetLogSizeKB."
                                        $currentSize = $TargetLogSizeKB
                                    }
                                    else {
                                        Write-Message -Level Verbose -Message "$step - Grow the $logfile file in $([System.Math]::Round($($LogIncrementSize / 1024.0), 2)) MB"
                                        $currentSize += $LogIncrementSize
                                    }

                                    #When -WhatIf Switch, do not run
                                    if ($PSCmdlet.ShouldProcess("$step - File will grow to $([System.Math]::Round($($currentSize/1024.0), 2)) MB", "This action will grow the file $logfile on database $db to $([System.Math]::Round($($currentSize/1024.0), 2)) MB .`r`nDo you wish to continue?", "Perform grow")) {
                                        Write-Message -Level Verbose -Message "$step - Set size $logfile to $([System.Math]::Round($($currentSize/1024.0), 2)) MB"
                                        $logfile.size = $currentSize

                                        Write-Message -Level Verbose -Message "$step - Applying changes"
                                        $logfile.Alter()
                                        Write-Message -Level Verbose -Message "$step - Changes have been applied"

                                        #Will put the info like VolumeFreeSpace up to date
                                        $logfile.Refresh()
                                    }
                                }

                                Write-Message -Level Verbose -Message "`r`n$step - [OK] Growth process for logfile '$logfile' on database '$db', has been finished."

                                Write-Message -Level Verbose -Message "$step - Grow $logfile log file on $db database finished."
                            }
                        }
                    } #else space available
                }
                #else verifying existence
                else {
                    Write-Message -Level Verbose -Message "Database '$db' does not exist on instance '$SqlInstance'."
                }

                #Get the number of VLFs
                $currentVLFCount = Test-DbaDbVirtualLogFile -SqlInstance $server -Database $db

                [pscustomobject]@{
                    ComputerName    = $server.ComputerName
                    InstanceName    = $server.ServiceName
                    SqlInstance     = $server.DomainInstanceName
                    Database        = $db
                    ID              = $logfile.ID
                    Name            = $logfile.Name
                    LogFileCount    = $numLogfiles
                    InitialSize     = [dbasize]($currentSizeMB * 1024)
                    CurrentSize     = [dbasize]($TargetLogSizeMB * 1024)
                    InitialVLFCount = $initialVLFCount.Total
                    CurrentVLFCount = $currentVLFCount.Total
                } | Select-DefaultView -ExcludeProperty LogFileCount
            } #foreach database
        }
        catch {
            Stop-Function -Message "Logfile $logfile on database $db not processed. Error: $($_.Exception.Message). Line Number:  $($_InvocationInfo.ScriptLineNumber)" -Continue
        }
    }

    end {
        Write-Message -Level Verbose -Message "Process finished $((Get-Date) - ($initialTime))"
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Expand-SqlTLogResponsibly
    }
}
#ValidationTags#Messaging#
function Export-DbaAvailabilityGroup {
    <#
        .SYNOPSIS
            Exports SQL Server Availability Groups to a T-SQL file.

        .DESCRIPTION
            Exports SQL Server Availability Groups creation scripts to a T-SQL file. This is a function that is not available in SSMS.

        .PARAMETER SqlInstance
            The SQL Server instance name. SQL Server 2012 and above supported.

        .PARAMETER FilePath
            The directory name where the output files will be written. A sub directory with the format 'ServerName$InstanceName' will be created. A T-SQL scripts named 'AGName.sql' will be created under this subdirectory for each scripted Availability Group.

        .PARAMETER AvailabilityGroup
            The Availability Group(s) to export - this list is auto-populated from the server. If unspecified, all logins will be processed.

        .PARAMETER ExcludeAvailabilityGroup
            The Availability Group(s) to exclude - this list is auto-populated from the server.

        .PARAMETER NoClobber
            Do not overwrite existing export files.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER WhatIf
            Shows you what it'd output if you were to run the command

        .PARAMETER Confirm
            Confirms each step/line of output

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Hadr, AG, AvailabilityGroup
            Author: Chris Sommer (@cjsommer), cjsommer.com

            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Export-DbaAvailabilityGroup

        .EXAMPLE
            Export-DbaAvailabilityGroup -SqlInstance sql2012

            Exports all Availability Groups from SQL server "sql2012". Output scripts are written to the Documents\SqlAgExports directory by default.

        .EXAMPLE
            Export-DbaAvailabilityGroup -SqlInstance sql2012 -FilePath C:\temp\availability_group_exports

            Exports all Availability Groups from SQL server "sql2012". Output scripts are written to the C:\temp\availability_group_exports directory.

        .EXAMPLE
            Export-DbaAvailabilityGroup -SqlInstance sql2012 -FilePath 'C:\dir with spaces\availability_group_exports' -AvailabilityGroups AG1,AG2

            Exports Availability Groups AG1 and AG2 from SQL server "sql2012". Output scripts are written to the C:\dir with spaces\availability_group_exports directory.

        .EXAMPLE
            Export-DbaAvailabilityGroup -SqlInstance sql2014 -FilePath C:\temp\availability_group_exports -NoClobber

            Exports all Availability Groups from SQL server "sql2014". Output scripts are written to the C:\temp\availability_group_exports directory. If the export file already exists it will not be overwritten.
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$AvailabilityGroup,
        [object[]]$ExcludeAvailabilityGroup,
        [Alias("OutputLocation", "Path")]
        [string]$FilePath = "$([Environment]::GetFolderPath("MyDocuments"))\SqlAgExport",
        [switch]$NoClobber,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($server.IsHadrEnabled -eq $false) {
                Stop-Function -Message "Hadr is not enabled on this instance" -Continue
            }
            else {
                # Get all of the Availability Groups and filter if required
                $ags = $server.AvailabilityGroups
            }

            if (Test-Bound 'AvailabilityGroup') {
                $ags = $ags | Where-Object Name -In $AvailabilityGroup
            }
            if (Test-Bound 'ExcludeAvailabilityGroup') {
                $ags = $ags | Where-Object Name -NotIn $ExcludeAvailabilityGroup
            }

            if ($ags) {

                # Set and create the OutputLocation if it doesn't exist
                $sqlinst = $instance.ToString().Replace('\', '$')
                $outputLocation = "$FilePath\$sqlinst"

                if (!(Test-Path $outputLocation -PathType Container)) {
                    $null = New-Item -Path $outputLocation -ItemType Directory -Force
                }

                # Script each Availability Group
                foreach ($ag in $ags) {
                    $agName = $ag.Name

                    # Set the outfile name
                    if ($AppendDateToOutputFilename.IsPresent) {
                        $formatteddate = (Get-Date -Format 'yyyyMMdd_hhmm')
                        $outFile = "$outputLocation\${AGname}_${formatteddate}.sql"
                    }
                    else {
                        $outFile = "$outputLocation\$agName.sql"
                    }

                    # Check NoClobber and script out the AG
                    if ($NoClobber.IsPresent -and (Test-Path -Path $outFile -PathType Leaf)) {
                        Write-Message -Level Warning -Message "OutputFile $outFile already exists. Skipping due to -NoClobber parameter"
                    }
                    else {
                        Write-Message -Level Verbose -Message "Scripting Availability Group [$agName] on $instance to $outFile"

                        # Create comment block header for AG script
                        "/*" | Out-File -FilePath $outFile -Encoding ASCII -Force
                        " * Created by dbatools 'Export-DbaAvailabilityGroup' cmdlet on '$(Get-Date)'" | Out-File -FilePath $outFile -Encoding ASCII -Append
                        " * See https://dbatools.io/Export-DbaAvailabilityGroup for more help" | Out-File -FilePath $outFile -Encoding ASCII -Append

                        # Output AG and listener names
                        " *" | Out-File -FilePath $outFile -Encoding ASCII -Append
                        " * Availability Group Name: $($ag.name)" | Out-File -FilePath $outFile -Encoding ASCII -Append
                        $ag.AvailabilityGroupListeners | ForEach-Object { " * Listener Name: $($_.name)" } | Out-File -FilePath $outFile -Encoding ASCII -Append

                        # Output all replicas
                        " *" | Out-File -FilePath $outFile -Encoding ASCII -Append
                        $ag.AvailabilityReplicas | ForEach-Object { " * Replica: $($_.name)" } | Out-File -FilePath $outFile -Encoding ASCII -Append

                        # Output all databases
                        " *" | Out-File -FilePath $outFile -Encoding ASCII -Append
                        $ag.AvailabilityDatabases | ForEach-Object { " * Database: $($_.name)" } | Out-File -FilePath $outFile -Encoding ASCII -Append

                        # $ag | Select-Object -Property * | Out-File -FilePath $outFile -Encoding ASCII -Append

                        "*/" | Out-File -FilePath $outFile -Encoding ASCII -Append

                        # Script the AG
                        try {
                            $ag.Script() | Out-File -FilePath $outFile -Encoding ASCII -Append
                            Get-ChildItem $outFile
                        }
                        catch {
                            Stop-Function -ErrorRecord $_ -Message "Error scripting out the availability groups. This is likely due to a bug in SMO." -Continue
                        }
                    }
                }
            }
            else {
                Write-Message -Level Output -Message "No Availability Groups detected on $instance"
            }
        }
    }
}
function Export-DbaDacpac {
    <#
    .SYNOPSIS
    Exports a dacpac from a server.

    .DESCRIPTION
    Using SQLPackage, export a dacpac from an instance of SQL Server.

    Note - Extract from SQL Server is notoriously flaky - for example if you have three part references to external databases it will not work.

    For help with the extract action parameters and properties, refer to https://msdn.microsoft.com/en-us/library/hh550080(v=vs.103).aspx

    .PARAMETER SqlInstance
    SQL Server name or SMO object representing the SQL Server to connect to and publish to.

    .PARAMETER SqlCredential
    Allows you to login to servers using alternative logins instead Integrated, accepts Credential object created by Get-Credential

    .PARAMETER Path
    The directory where the .dacpac files will be exported to. Defaults to documents.

    .PARAMETER Database
    The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

    .PARAMETER ExcludeDatabase
    The database(s) to exclude - this list is auto-populated from the server

    .PARAMETER AllUserDatabases
    Run command against all user databases

    .PARAMETER ExtendedParameters
    Optional parameters used to extract the DACPAC. More information can be found at
    https://msdn.microsoft.com/en-us/library/hh550080.aspx

    .PARAMETER ExtendedProperties
    Optional properties used to extract the DACPAC. More information can be found at
    https://msdn.microsoft.com/en-us/library/hh550080.aspx


    .PARAMETER EnableException
    By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
    Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
    Tags: Migration, Database, Dacpac
    Author: Richie lee (@bzzzt_io)

    Website: https://dbatools.io
    Copyright: (C) Chrissy LeMaire, [email protected]
    License: MIT https://opensource.org/licenses/MIT

    .LINK
    https://dbatools.io/Export-DbaDacpac

    .EXAMPLE
    Export-DbaDacpac -SqlInstance sql2016 -Database SharePoint_Config
    Exports the dacpac for SharePoint_Config on sql2016 to $home\Documents\SharePoint_Config.dacpac

    .EXAMPLE
    $moreprops = "/p:VerifyExtraction=$true /p:CommandTimeOut=10"
    Export-DbaDacpac -SqlInstance sql2016 -Database SharePoint_Config -Path C:\temp -ExtendedProperties $moreprops

    Sets the CommandTimeout to 10 then extracts the dacpac for SharePoint_Config on sql2016 to C:\temp\SharePoint_Config.dacpac then verifies extraction.


    #>
    [CmdletBinding()]
    param
    (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$AllUserDatabases,
        [string]$Path = "$home\Documents",
        [string]$ExtendedParameters,
        [string]$ExtendedProperties,
        [switch]$EnableException
    )

    process {
        if ((Test-Bound -Not -ParameterName Database) -and (Test-Bound -Not -ParameterName ExcludeDatabase) -and (Test-Bound -Not -ParameterName AllUserDatabases)) {
            Stop-Function -Message "You must specify databases to execute against using either -Database, -ExcludeDatabase or -AllUserDatabases"
        }

        if (-not (Test-Path $Path)) {
            Stop-Function -Message "$Path doesn't exist or access denied"
        }

        if ((Get-Item $path) -isnot [System.IO.DirectoryInfo]) {
            Stop-Function -Message "Path must be a directory"
        }

        foreach ($instance in $sqlinstance) {

            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            $cleaninstance = $instance.ToString().Replace('\', '-')

            $dbs = $server.Databases | Where-Object { $_.IsSystemObject -eq $false -and $_.IsAccessible }

            if ($Database) {
                $dbs = $dbs | Where-Object Name -in $Database
                if (-not $dbs.name) {
                    Stop-Function -Message "Database $Database does not exist on $instance" -Target $instance -Continue
                }
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -notin $ExcludeDatabase
            }

            foreach ($db in $dbs) {
                $dbname = $db.name
                $connstring = $server.ConnectionContext.ConnectionString.Replace('"', "'")
                if ($connstring -notmatch 'Database=') {
                    $connstring = "$connstring;Database=$dbname"
                }
                $filename = "$Path\$cleaninstance-$dbname.dacpac"
                Write-Message -Level Verbose -Message "Exporting $filename"
                Write-Message -Level Verbose -Message "Using connection string $connstring"

                $sqlPackageArgs = "/action:Extract /tf:""$filename"" /SourceConnectionString:""$connstring"" $ExtendedParameters $ExtendedProperties"
                $resultstime = [diagnostics.stopwatch]::StartNew()

                try {
                    $startprocess = New-Object System.Diagnostics.ProcessStartInfo
                    $startprocess.FileName = "$script:PSModuleRoot\bin\smo\sqlpackage.exe"
                    $startprocess.Arguments = $sqlPackageArgs
                    $startprocess.RedirectStandardError = $true
                    $startprocess.RedirectStandardOutput = $true
                    $startprocess.UseShellExecute = $false
                    $startprocess.CreateNoWindow = $true
                    $process = New-Object System.Diagnostics.Process
                    $process.StartInfo = $startprocess
                    $process.Start() | Out-Null
                    $process.WaitForExit()
                    $stdout = $process.StandardOutput.ReadToEnd()
                    $stderr = $process.StandardError.ReadToEnd()
                    Write-Message -level Verbose -Message "StandardOutput: $stdout"

                    [pscustomobject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Database     = $dbname
                        Path         = $filename
                        Elapsed      = [prettytimespan]($resultstime.Elapsed)
                    } | Select-DefaultView -ExcludeProperty ComputerName, InstanceName
                }
                catch {
                    Stop-Function -Message "SQLPackage Failure" -ErrorRecord $_ -Continue
                }

                if ($process.ExitCode -ne 0) {
                    Stop-Function -Message "Standard output - $stderr" -Continue
                }
            }
        }
    }
}
function Export-DbaDiagnosticQuery {
    <#
        .SYNOPSIS
            Export-DbaDiagnosticQuery can convert ouput generated by Invoke-DbaDiagnosticQuery to CSV or Excel

        .DESCRIPTION
            The default output format of Invoke-DbaDiagnosticQuery is a custom object. It can also output to CSV and Excel.
            However, CSV output can generate a lot of files and Excel output depends on the ImportExcel module by Doug Fike (https://github.com/dfinke/ImportExcel)
            Export-DbaDiagnosticQuery can be used to convert from the default export type to the other available export types.

        .PARAMETER InputObject
            Specifies the objects to convert

        .PARAMETER ConvertTo
            Specifies the output type. Valid choices are Excel and CSV. CSV is the default.

        .PARAMETER Path
            Specifies the path to the output files.

        .PARAMETER Suffix
            Suffix for the filename. It's datetime by default.

        .PARAMETER NoPlanExport
            Use this switch to suppress exporting of .sqlplan files

        .PARAMETER NoQueryExport
            Use this switch to suppress exporting of .sql files

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Query
            Author: Andre Kamman (@AndreKamman), http://clouddba.io

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Export-DbaDiagnosticQuery

        .EXAMPLE
            Invoke-DbaDiagnosticQuery -SqlInstance sql2016 | Export-DbaDiagnosticQuery -Path c:\temp

            Converts output from Invoke-DbaDiagnosticQuery to multiple CSV files

        .EXAMPLE
            $output = Invoke-DbaDiagnosticQuery -SqlInstance sql2016
            Export-DbaDiagnosticQuery -InputObject $output -ConvertTo Excel

            Converts output from Invoke-DbaDiagnosticQuery to Excel worksheet(s) in the Documents folder
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [object[]]$InputObject,
        [ValidateSet("Excel", "Csv")]
        [string]$ConvertTo = "Csv",
        [System.IO.FileInfo]$Path = [Environment]::GetFolderPath("mydocuments"),
        [string]$Suffix = "$(Get-Date -format 'yyyyMMddHHmmssms')",
        [switch]$NoPlanExport,
        [switch]$NoQueryExport,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        if ($ConvertTo -eq "Excel") {
            try {
                Import-Module ImportExcel -ErrorAction Stop
            }
            catch {
                $message = "Failed to load module, exporting to Excel feature is not available
                            Install the module from: https://github.com/dfinke/ImportExcel
                            Valid alternative conversion format is csv"
                Stop-Function -Message $message
                return
            }
        }

        if (!$(Test-Path $Path)) {
            try {
                New-Item $Path -ItemType Directory -ErrorAction Stop | Out-Null
                Write-Message -Level Output -Message "Created directory $Path"
            }
            catch {
                Stop-Function -Message "Failed to create directory $Path" -Continue
            }
        }

        Function Remove-InvalidFileNameChars {
            [CmdletBinding()]
            param (
                [Parameter(Mandatory = $true,
                    Position = 0,
                    ValueFromPipeline = $true,
                    ValueFromPipelineByPropertyName = $true)]
                [String]$Name
            )
            $Name = $Name.Replace(" ", "-")
            $invalidChars = [IO.Path]::GetInvalidFileNameChars() -join ''
            $re = "[{0}]" -f [RegEx]::Escape($invalidChars)
            return ($Name -replace $re)
        }
    }

    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($row in $InputObject) {
            $result = $row.Result
            $name = $row.Name
            $SqlInstance = $row.SqlInstance.Replace("\", "$")
            $dbname = $row.Database
            $number = $row.Number
            $note = $row.Note

            if ($null -eq $result) {
                Stop-Function -Message "Result was empty for $name" -Target $result -Continue
            }

            $queryname = Remove-InvalidFileNameChars -Name $Name
            $excelfilename = "$Path\$SqlInstance-DQ-$Suffix.xlsx"
            $exceldbfilename = "$Path\$SqlInstance-DQ-$dbname-$Suffix.xlsx"
            $csvdbfilename = "$Path\$SqlInstance-$dbname-DQ-$number-$queryname-$Suffix.csv"
            $csvfilename = "$Path\$SqlInstance-DQ-$number-$queryname-$Suffix.csv"

            $columnnameoptions = "Query Plan", "QueryPlan", "Query_Plan", "query_plan_xml"
            if (($result | Get-Member | Where-Object Name -in $columnnameoptions).Count -gt 0) {
                $plannr = 0
                $columnname = ($result | Get-Member | Where-Object Name -In $columnnameoptions).Name
                foreach ($plan in $result."$columnname") {
                    $plannr += 1
                    if ($row.DatabaseSpecific) {
                        $planfilename = "$Path\$SqlInstance-$dbname-DQ-$number-$queryname-$plannr-$Suffix.sqlplan"
                    }
                    else {
                        $planfilename = "$Path\$SqlInstance-DQ-$number-$queryname-$plannr-$Suffix.sqlplan"
                    }

                    if (!$NoPlanExport) {
                        Write-Message -Level Output -Message "Exporting $planfilename"
                        if ($plan) {$plan | Out-File -FilePath $planfilename}
                    }
                }

                $result = $result | Select-Object * -ExcludeProperty "$columnname"
            }

            $columnnameoptions = "Complete Query Text", "QueryText", "Query Text", "Query_Text", "query_sql_text"
            if (($result | Get-Member | Where-Object Name -In $columnnameoptions ).Count -gt 0) {
                $sqlnr = 0
                $columnname = ($result | Get-Member | Where-Object Name -In $columnnameoptions).Name
                foreach ($sql in $result."$columnname") {
                    $sqlnr += 1
                    if ($row.DatabaseSpecific) {
                        $sqlfilename = "$Path\$SqlInstance-$dbname-DQ-$number-$queryname-$sqlnr-$Suffix.sql"
                    }
                    else {
                        $sqlfilename = "$Path\$SqlInstance-DQ-$number-$queryname-$sqlnr-$Suffix.sql"
                    }

                    if (!$NoQueryExport) {
                        Write-Message -Level Output -Message "Exporting $sqlfilename"
                        if ($sql) {$sql | Out-File -FilePath $sqlfilename}
                    }
                }

                $result = $result | Select-Object * -ExcludeProperty "$columnname"
            }

            switch ($ConvertTo) {
                "Excel" {
                    if ($row.DatabaseSpecific) {
                        Write-Message -Level Output -Message "Exporting $exceldbfilename"
                        $result | Export-Excel -Path $exceldbfilename -WorkSheetname $Name -AutoSize -AutoFilter -BoldTopRow -FreezeTopRow
                    }
                    else {
                        Write-Message -Level Output -Message "Exporting $excelfilename"
                        $result | Export-Excel -Path $excelfilename -WorkSheetname $Name -AutoSize -AutoFilter -BoldTopRow -FreezeTopRow
                    }
                }
                "csv" {
                    if ($row.DatabaseSpecific) {
                        Write-Message -Level Output -Message "Exporting $csvdbfilename"
                        $result | Export-Csv -Path $csvdbfilename -NoTypeInformation -Append
                    }
                    else {
                        Write-Message -Level Output -Message "Exporting $csvfilename"
                        $result | Export-Csv -Path $csvfilename -NoTypeInformation -Append
                    }
                }
            }
        }
    }
}
#ValidationTags#Messaging#
function Export-DbaExecutionPlan {
    <#
        .SYNOPSIS
            Exports execution plans to disk.

        .DESCRIPTION
            Exports execution plans to disk. Can pipe from Export-DbaExecutionPlan

            Thanks to
                https://www.simple-talk.com/sql/t-sql-programming/dmvs-for-query-plan-metadata/
                and
                http://www.scarydba.com/2017/02/13/export-plans-cache-sqlplan-file/
            for the idea and query.

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to.

        .PARAMETER SqlCredential
            Credential object used to connect to the SQL Server as a different user

        .PARAMETER Database
            The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto-populated from the server

        .PARAMETER SinceCreation
            Datetime object used to narrow the results to a date

        .PARAMETER SinceLastExecution
            Datetime object used to narrow the results to a date

        .PARAMETER Path
            The directory where all of the sqlxml files will be exported

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER PipedObject
            Internal parameter

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Performance, ExecutionPlan
            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Export-DbaExecutionPlan

        .EXAMPLE
            Export-DbaExecutionPlan -SqlInstance sqlserver2014a

            Exports all execution plans for sqlserver2014a.

        .EXAMPLE
            Export-DbaExecutionPlan -SqlInstance sqlserver2014a -Database db1, db2 -SinceLastExecution '7/1/2016 10:47:00'

            Exports all execution plans for databases db1 and db2 on sqlserver2014a since July 1, 2016 at 10:47 AM.
    #>
    [cmdletbinding(SupportsShouldProcess = $true, DefaultParameterSetName = "Default")]
    param (
        [parameter(ParameterSetName = 'NotPiped', Mandatory)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [parameter(ParameterSetName = 'NotPiped')]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [parameter(ParameterSetName = 'Piped', Mandatory)]
        [parameter(ParameterSetName = 'NotPiped', Mandatory)]
        [string]$Path,
        [parameter(ParameterSetName = 'NotPiped')]
        [datetime]$SinceCreation,
        [parameter(ParameterSetName = 'NotPiped')]
        [datetime]$SinceLastExecution,
        [Parameter(ParameterSetName = 'Piped', Mandatory, ValueFromPipeline)]
        [object[]]$PipedObject,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        if ($SinceCreation -ne $null) {
            $SinceCreation = $SinceCreation.ToString("yyyy-MM-dd HH:mm:ss")
        }

        if ($SinceLastExecution -ne $null) {
            $SinceLastExecution = $SinceLastExecution.ToString("yyyy-MM-dd HH:mm:ss")
        }

        function Export-Plan {
            param(
                [object]$object
            )
            $instanceName = $object.SqlInstance
            $dbName = $object.DatabaseName
            $queryPosition = $object.QueryPosition
            $sqlHandle = "0x"; $object.SqlHandle | ForEach-Object { $sqlHandle += ("{0:X}" -f $_).PadLeft(2, "0") }
            $sqlHandle = $sqlHandle.TrimStart('0x02000000').TrimEnd('0000000000000000000000000000000000000000')
            $shortName = "$instanceName-$dbName-$queryPosition-$sqlHandle"

            foreach ($queryPlan in $object.BatchQueryPlanRaw) {
                $fileName = "$path\$shortName-batch.sqlplan"

                try {
                    if ($Pscmdlet.ShouldProcess("localhost", "Writing XML file to $fileName")) {
                        $queryPlan.Save($fileName)
                    }
                }
                catch {
                    Stop-Function -Message "Skipped query plan for $fileName because it is null." -Target $fileName -ErrorRecord $_ -Continue
                }
            }

            foreach ($statementPlan in $object.SingleStatementPlanRaw) {
                $fileName = "$path\$shortName.sqlplan"

                try {
                    if ($Pscmdlet.ShouldProcess("localhost", "Writing XML file to $fileName")) {
                        $statementPlan.Save($fileName)
                    }
                }
                catch {
                    Stop-Function -Message "Skipped statement plan for $fileName because it is null." -Target $fileName -ErrorRecord $_ -Continue
                }
            }

            if ($Pscmdlet.ShouldProcess("console", "Showing output object")) {
                Add-Member -Force -InputObject $object -MemberType NoteProperty -Name OutputFile -Value $fileName
                Select-DefaultView -InputObject $object -Property ComputerName, InstanceName, SqlInstance, DatabaseName, SqlHandle, CreationTime, LastExecutionTime, OutputFile
            }
        }
    }

    process {
        if (!(Test-Path $Path)) {
            $null = New-Item -ItemType Directory -Path $Path
        }

        if ($PipedObject) {
            foreach ($object in $pipedobject) {
                Export-Plan $object
                return
            }
        }

        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $select = "SELECT DB_NAME(deqp.dbid) as DatabaseName, OBJECT_NAME(deqp.objectid) as ObjectName,
                    detqp.query_plan AS SingleStatementPlan,
                    deqp.query_plan AS BatchQueryPlan,
                    ROW_NUMBER() OVER ( ORDER BY Statement_Start_offset ) AS QueryPosition,
                    sql_handle as SqlHandle,
                    plan_handle as PlanHandle,
                    creation_time as CreationTime,
                    last_execution_time as LastExecutionTime"

            $from = " FROM sys.dm_exec_query_stats deqs
                        CROSS APPLY sys.dm_exec_text_query_plan(deqs.plan_handle,
                            deqs.statement_start_offset,
                            deqs.statement_end_offset) AS detqp
                        CROSS APPLY sys.dm_exec_query_plan(deqs.plan_handle) AS deqp
                        CROSS APPLY sys.dm_exec_sql_text(deqs.plan_handle) AS execText"

            if ($ExcludeDatabase -or $Database -or $SinceCreation.Length -gt 0 -or $SinceLastExecution.length -gt 0 -or $ExcludeEmptyQueryPlan -eq $true) {
                $where = " WHERE "
            }

            $whereArray = @()

            if ($Database -gt 0) {
                $dbList = $Database -join "','"
                $whereArray += " DB_NAME(deqp.dbid) in ('$dbList') "
            }

            if (Test-Bound 'SinceCreation') {
                Write-Message -Level Verbose -Message "Adding creation time"
                $whereArray += " creation_time >= '$SinceCreation' "
            }

            if (Test-Bound 'SinceLastExecution') {
                Write-Message -Level Verbose -Message "Adding last execution time"
                $whereArray += " last_execution_time >= '$SinceLastExecution' "
            }

            if (Test-Bound 'ExcludeDatabase') {
                $dbList = $ExcludeDatabase -join "','"
                $whereArray += " DB_NAME(deqp.dbid) not in ('$dbList') "
            }

            if (Test-Bound 'ExcludeEmptyQueryPlan') {
                $whereArray += " detqp.query_plan is not null"
            }

            if ($where.Length -gt 0) {
                $whereArray = $whereArray -join " and "
                $where = "$where $whereArray"
            }

            $sql = "$select $from $where"
            Write-Message -Level Debug -Message "SQL Statement: $sql"
            try {
                $dataTable = $server.ConnectionContext.ExecuteWithResults($sql).Tables
            }
            catch {
                Stop-Function -Message "Issue collecting execution plans" -Target $instance -ErroRecord $_ -Continue
            }

            foreach ($row in ($dataTable.Rows)) {
                $sqlHandle = "0x"; $row.sqlhandle | ForEach-Object { $sqlHandle += ("{0:X}" -f $_).PadLeft(2, "0") }
                $planhandle = "0x"; $row.planhandle | ForEach-Object { $planhandle += ("{0:X}" -f $_).PadLeft(2, "0") }

                $object = [pscustomobject]@{
                    ComputerName           = $server.ComputerName
                    InstanceName           = $server.ServiceName
                    SqlInstance            = $server.DomainInstanceName
                    DatabaseName           = $row.DatabaseName
                    SqlHandle              = $sqlHandle
                    PlanHandle             = $planhandle
                    SingleStatementPlan    = $row.SingleStatementPlan
                    BatchQueryPlan         = $row.BatchQueryPlan
                    QueryPosition          = $row.QueryPosition
                    CreationTime           = $row.CreationTime
                    LastExecutionTime      = $row.LastExecutionTime
                    BatchQueryPlanRaw      = [xml]$row.BatchQueryPlan
                    SingleStatementPlanRaw = [xml]$row.SingleStatementPlan
                }
                Export-Plan $object
            }
        }
    }
}
function Export-DbaLogin {
    <#
        .SYNOPSIS
            Exports Windows and SQL Logins to a T-SQL file. Export includes login, SID, password, default database, default language, server permissions, server roles, db permissions, db roles.

        .DESCRIPTION
            Exports Windows and SQL Logins to a T-SQL file. Export includes login, SID, password, default database, default language, server permissions, server roles, db permissions, db roles.

        .PARAMETER SqlInstance
            The SQL Server instance name. SQL Server 2000 and above supported.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Login
            The login(s) to process. Options for this list are auto-populated from the server. If unspecified, all logins will be processed.

        .PARAMETER ExcludeLogin
            The login(s) to exclude. Options for this list are auto-populated from the server.

        .PARAMETER Database
            The database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER FilePath
            The file to write to.

        .PARAMETER NoClobber
            If this switch is enabled, a file already existing at the path specified by FilePath will not be overwritten.

        .PARAMETER Append
            If this switch is enabled, content will be appended to a file already existing at the path specified by FilePath. If the file does not exist, it will be created.

        .PARAMETER NoJobs
            If this switch is enabled, Agent job ownership will not be exported.

        .PARAMETER NoDatabases
            If this switch is enabled, mappings for databases will not be exported.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER ExcludeGoBatchSeparator
            If specified, will NOT script the 'GO' batch separator.

        .PARAMETER DestinationVersion
            To say to which version the script should be generated. If not specified will use instance major version.

        .NOTES
            Tags: Export, Login
            Author: Chrissy LeMaire (@cl), netnerds.net
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Export-DbaLogin

        .EXAMPLE
            Export-DbaLogin -SqlInstance sql2005 -FilePath C:\temp\sql2005-logins.sql

            Exports the logins for SQL Server "sql2005" and writes them to the file "C:\temp\sql2005-logins.sql"

        .EXAMPLE
            Export-DbaLogin -SqlInstance sqlserver2014a -Exclude realcajun -SqlCredential $scred -FilePath C:\temp\logins.sql -Append

            Authenticates to sqlserver2014a using SQL Authentication. Exports all logins except for realcajun to C:\temp\logins.sql, and appends to the file if it exists. If not, the file will be created.

        .EXAMPLE
            Export-DbaLogin -SqlInstance sqlserver2014a -Login realcajun, netnerds -FilePath C:\temp\logins.sql

            Exports ONLY logins netnerds and realcajun FROM sqlserver2014a to the file  C:\temp\logins.sql

        .EXAMPLE
            Export-DbaLogin -SqlInstance sqlserver2014a -Login realcajun, netnerds -Database HR, Accounting

            Exports ONLY logins netnerds and realcajun FROM sqlserver2014a with the permissions on databases HR and Accounting

        .EXAMPLE
            Export-DbaLogin -SqlInstance sqlserver2008 -Login realcajun, netnerds -FilePath C:\temp\login.sql -ExcludeGoBatchSeparator

            Exports ONLY logins netnerds and realcajun FROM sqlserver2008 server, to the C:\temp\login.sql file without the 'GO' batch separator.

        .EXAMPLE
            Export-DbaLogin -SqlInstance sqlserver2008 -Login realcajun -FilePath C:\temp\users.sql -DestinationVersion SQLServer2016

            Exports login realcajun fron sqlsever2008 to the file C:\temp\users.sql with sintax to run on SQL Server 2016
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [object[]]$Login,
        [object[]]$ExcludeLogin,
        [Alias("Databases")]
        [object[]]$Database,
        [Alias("OutFile", "Path", "FileName")]
        [string]$FilePath,
        [Alias("NoOverwrite")]
        [switch]$NoClobber,
        [switch]$Append,
        [switch]$NoDatabases,
        [switch]$NoJobs,
        [Alias('Silent')]
        [switch]$EnableException,
        [switch]$ExcludeGoBatchSeparator,
        [ValidateSet('SQLServer2000', 'SQLServer2005', 'SQLServer2008/2008R2', 'SQLServer2012', 'SQLServer2014', 'SQLServer2016', 'SQLServer2017')]
        [string]$DestinationVersion
    )

    begin {

        if ($FilePath) {
            if ($FilePath -notlike "*\*") {
                $FilePath = ".\$filepath"
            }
            $directory = Split-Path $FilePath
            $exists = Test-Path $directory

            if ($exists -eq $false) {
                Write-Message -Level Warning -Message "Parent directory $directory does not exist."
            }
        }

        $outsql = @()

        $versions = @{
            'SQLServer2000'        = 'Version80'
            'SQLServer2005'        = 'Version90'
            'SQLServer2008/2008R2' = 'Version100'
            'SQLServer2012'        = 'Version110'
            'SQLServer2014'        = 'Version120'
            'SQLServer2016'        = 'Version130'
            'SQLServer2017'        = 'Version140'
        }

        $versionsNumbers = @{
            '8'  = 'Version80'
            '9'  = 'Version90'
            '10' = 'Version100'
            '11' = 'Version110'
            '12' = 'Version120'
            '13' = 'Version130'
            '14' = 'Version140'
        }
    }
    process {
        if (Test-FunctionInterrupt) {
            return
        }

        Write-Message -Level Verbose -Message "Connecting to $sqlinstance."
        $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $sqlcredential

        if ([string]::IsNullOrEmpty($destinationVersion)) {
            #Get compatibility level for scripting the objects
            $scriptVersion = $versionsNumbers[$server.VersionMajor.ToString()]
        }
        else {
            $scriptVersion = $versions[$destinationVersion]
        }

        if ($NoDatabases -eq $false -or $Database) {
            # if we got a database or a list of databases passed
            # and we need to enumerate mappings, login.enumdatabasemappings() takes forever
            # the cool thing though is that database.enumloginmappings() is fast. A lot.
            # if we get a list of databases passed (or even the default list of all the databases)
            # we save outself a call to enumloginmappings if there is no map at all
            $DbMapping = @()
            $DbsToMap = $server.Databases
            if ($Database) {
                $DbsToMap = $DbsToMap | Where-Object Name -in $Database
            }
            foreach ($db in $DbsToMap) {
                if ($db.IsAccessible -eq $false) {
                    continue
                }
                $dbmap = $db.EnumLoginMappings()
                foreach ($el in $dbmap) {
                    $DbMapping += [pscustomobject]@{
                        Database  = $db.Name
                        UserName  = $el.Username
                        LoginName = $el.LoginName
                    }
                }
            }
        }

        foreach ($sourceLogin in $server.Logins) {
            $userName = $sourceLogin.name

            if ($Login -and $Login -notcontains $userName -or $ExcludeLogin -contains $userName) {
                continue
            }

            if ($userName.StartsWith("##") -or $userName -eq 'sa') {
                Write-Message -Level Warning -Message "Skipping $userName."
                continue
            }

            $serverName = $server

            $userBase = ($userName.Split("\")[0]).ToLower()
            if ($serverName -eq $userBase -or $userName.StartsWith("NT ")) {
                if ($Pscmdlet.ShouldProcess("console", "Stating $userName is skipped because it is a local machine name.")) {
                    Write-Message -Level Warning -Message "$userName is skipped because it is a local machine name."
                    continue
                }
            }

            if ($Pscmdlet.ShouldProcess("Outfile", "Adding T-SQL for login $userName")) {
                if ($FilePath) {
                    Write-Message -Level Verbose -Message "Exporting $userName."
                }

                $outsql += "`r`nUSE master`n"
                # Getting some attributes
                $defaultDb = $sourceLogin.DefaultDatabase
                $language = $sourceLogin.Language

                if ($sourceLogin.PasswordPolicyEnforced -eq $false) {
                    $checkPolicy = "OFF"
                }
                else {
                    $checkPolicy = "ON"
                }

                if (!$sourceLogin.PasswordExpirationEnabled) {
                    $checkExpiration = "OFF"
                }
                else {
                    $checkExpiration = "ON"
                }

                # Attempt to script out SQL Login
                if ($sourceLogin.LoginType -eq "SqlLogin") {
                    $sourceLoginName = $sourceLogin.name

                    switch ($server.versionMajor) {
                        0 {
                            $sql = "SELECT CONVERT(VARBINARY(256),password) AS hashedpass FROM master.dbo.syslogins WHERE loginname='$sourceLoginName'"
                        }
                        8 {
                            $sql = "SELECT CONVERT(VARBINARY(256),password) AS hashedpass FROM dbo.syslogins WHERE name='$sourceLoginName'"
                        }
                        9 {
                            $sql = "SELECT CONVERT(VARBINARY(256),password_hash) as hashedpass FROM sys.sql_logins WHERE name='$sourceLoginName'"
                        }
                        default {
                            $sql = "SELECT CAST(CONVERT(varchar(256), CAST(LOGINPROPERTY(name,'PasswordHash') AS VARBINARY(256)), 1) AS NVARCHAR(max)) AS hashedpass FROM sys.server_principals WHERE principal_id = $($sourceLogin.id)"
                        }
                    }

                    try {
                        $hashedPass = $server.ConnectionContext.ExecuteScalar($sql)
                    }
                    catch {
                        $hashedPassDt = $server.Databases['master'].ExecuteWithResults($sql)
                        $hashedPass = $hashedPassDt.Tables[0].Rows[0].Item(0)
                    }

                    if ($hashedPass.GetType().Name -ne "String") {
                        $passString = "0x"; $hashedPass | ForEach-Object {
                            $passString += ("{0:X}" -f $_).PadLeft(2, "0")
                        }
                        $hashedPass = $passString
                    }

                    $sid = "0x"; $sourceLogin.sid | ForEach-Object {
                        $sid += ("{0:X}" -f $_).PadLeft(2, "0")
                    }
                    $outsql += "IF NOT EXISTS (SELECT loginname FROM master.dbo.syslogins WHERE name = '$userName') CREATE LOGIN [$userName] WITH PASSWORD = $hashedPass HASHED, SID = $sid, DEFAULT_DATABASE = [$defaultDb], CHECK_POLICY = $checkPolicy, CHECK_EXPIRATION = $checkExpiration, DEFAULT_LANGUAGE = [$language]"
                }
                # Attempt to script out Windows User
                elseif ($sourceLogin.LoginType -eq "WindowsUser" -or $sourceLogin.LoginType -eq "WindowsGroup") {
                    $outsql += "IF NOT EXISTS (SELECT loginname FROM master.dbo.syslogins WHERE name = '$userName') CREATE LOGIN [$userName] FROM WINDOWS WITH DEFAULT_DATABASE = [$defaultDb], DEFAULT_LANGUAGE = [$language]"
                }
                # This script does not currently support certificate mapped or asymmetric key users.
                else {
                    Write-Message -Level Warning -Message "$($sourceLogin.LoginType) logins not supported. $($sourceLogin.Name) skipped."
                    continue
                }

                if ($sourceLogin.IsDisabled) {
                    $outsql += "ALTER LOGIN [$userName] DISABLE"
                }

                if ($sourceLogin.DenyWindowsLogin) {
                    $outsql += "DENY CONNECT SQL TO [$userName]"
                }
            }

            # Server Roles: sysadmin, bulklogin, etc
            foreach ($role in $server.Roles) {
                $roleName = $role.Name

                # SMO changed over time
                try {
                    $roleMembers = $role.EnumMemberNames()
                }
                catch {
                    $roleMembers = $role.EnumServerRoleMembers()
                }

                if ($roleMembers -contains $userName) {
                    if (($server.VersionMajor -lt 11 -and [string]::IsNullOrEmpty($destinationVersion)) -or ($DestinationVersion -in "SQLServer2000", "SQLServer2005", "SQLServer2008/2008R2")) {
                        $outsql += "EXEC sys.sp_addsrvrolemember @rolename=N'$roleName', @loginame=N'$userName'"
                    }
                    else {
                        $outsql += "ALTER SERVER ROLE [$roleName] ADD MEMBER [$userName]"
                    }
                }
            }

            if ($NoJobs -eq $false) {
                $ownedJobs = $server.JobServer.Jobs | Where-Object { $_.OwnerLoginName -eq $userName }

                foreach ($ownedJob in $ownedJobs) {
                    $outsql += "`n`rUSE msdb`n"
                    $outsql += "EXEC msdb.dbo.sp_update_job @job_name=N'$ownedJob', @owner_login_name=N'$userName'"
                }
            }

            if ($server.VersionMajor -ge 9) {
                # These operations are only supported by SQL Server 2005 and above.
                # Securables: Connect SQL, View any database, Administer Bulk Operations, etc.

                $perms = $server.EnumServerPermissions($userName)
                $outsql += "`n`rUSE master`n"
                foreach ($perm in $perms) {
                    $permState = $perm.permissionstate
                    $permType = $perm.PermissionType
                    $grantor = $perm.grantor

                    if ($permState -eq "GrantWithGrant") {
                        $grantWithGrant = "WITH GRANT OPTION"
                        $permState = "GRANT"
                    }
                    else {
                        $grantWithGrant = $null
                    }

                    $outsql += "$permState $permType TO [$userName] $grantWithGrant AS [$grantor]"
                }

                # Credential mapping. Credential removal not currently supported for Syncs.
                $loginCredentials = $server.Credentials | Where-Object { $_.Identity -eq $sourceLogin.Name }
                foreach ($credential in $loginCredentials) {
                    $credentialName = $credential.Name
                    $outsql += "PRINT '$userName is associated with the $credentialName credential'"
                }
            }

            if ($NoDatabases -eq $false) {
                $dbs = $sourceLogin.EnumDatabaseMappings()

                if ($Database) {
                    $dbs = $dbs | Where-Object { $_.DBName -in $Database }
                }

                # Adding database mappings and securables
                foreach ($db in $dbs) {
                    $dbName = $db.dbname
                    $sourceDb = $server.Databases[$dbName]
                    $dbUserName = $db.username

                    $outsql += "`r`nUSE [$dbName]`n"
                    try {
                        $sql = $server.Databases[$dbName].Users[$dbUserName].Script()
                        $outsql += $sql
                    }
                    catch {
                        Write-Message -Level Warning -Message "User cannot be found in selected database."
                    }

                    # Skipping updating dbowner

                    # Database Roles: db_owner, db_datareader, etc
                    foreach ($role in $sourceDb.Roles) {
                        if ($role.EnumMembers() -contains $dbUserName) {
                            $roleName = $role.Name
                            if (($server.VersionMajor -lt 11 -and [string]::IsNullOrEmpty($destinationVersion)) -or ($DestinationVersion -in "SQLServer2000", "SQLServer2005", "SQLServer2008/2008R2")) {
                                $outsql += "EXEC sys.sp_addrolemember @rolename=N'$roleName', @membername=N'$dbUserName'"
                            }
                            else {
                                $outsql += "ALTER ROLE [$roleName] ADD MEMBER [$dbUserName]"
                            }
                        }
                    }

                    # Connect, Alter Any Assembly, etc
                    $perms = $sourceDb.EnumDatabasePermissions($dbUserName)
                    foreach ($perm in $perms) {
                        $permState = $perm.PermissionState
                        $permType = $perm.PermissionType
                        $grantor = $perm.Grantor

                        if ($permState -eq "GrantWithGrant") {
                            $grantWithGrant = "WITH GRANT OPTION"
                            $permState = "GRANT"
                        }
                        else {
                            $grantWithGrant = $null
                        }

                        $outsql += "$permState $permType TO [$userName] $grantWithGrant AS [$grantor]"
                    }
                }
            }
        }
    }
    end {
        $sql = $sql | Where-Object { $_ -notlike "CREATE USER [dbo] FOR LOGIN * WITH DEFAULT_SCHEMA=[dbo]" }

        if ($ExcludeGoBatchSeparator) {
            $sql = $outsql
        }
        else {
            $sql = $outsql -join "`r`nGO`r`n"
            #add the final GO
            $sql += "`r`nGO"
        }

        if ($FilePath) {
            $sql | Out-File -Encoding UTF8 -FilePath $FilePath -Append:$Append -NoClobber:$NoClobber
            Get-ChildItem $FilePath
        }
        else {
            $sql
        }
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Export-SqlLogin
    }
}
function Export-DbaPfDataCollectorSetTemplate {
    <#
        .SYNOPSIS
            Exports a new Data Collector Set XML Template.

        .DESCRIPTION
            Exports a Data Collector Set XML Template from Get-DbaPfDataCollectorSet. Exports to "$home\Documents\Performance Monitor Templates" by default.

        .PARAMETER ComputerName
            The target computer. Defaults to localhost.

        .PARAMETER Credential
            Allows you to login to $ComputerName using alternative credentials. To use:

            $cred = Get-Credential, then pass $cred object to the -Credential parameter.

        .PARAMETER CollectorSet
            The name of the collector set(s) to export.

        .PARAMETER Path
            The path to export the file. Can be .xml or directory.

        .PARAMETER InputObject
            Accepts the object output by Get-DbaPfDataCollectorSetTemplate via the pipeline.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Performance, DataCollector
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Export-DbaPfDataCollectorSetTemplate

        .EXAMPLE
            Export-DbaPfDataCollectorSetTemplate -ComputerName sql2017 -Path C:\temp\pf

            Exports all data collector sets from to the C:\temp\pf folder.

        .EXAMPLE
            Get-DbaPfDataCollectorSet ComputerName sql2017 -CollectorSet 'System Correlation' | Export-DbaPfDataCollectorSetTemplate -Path C:\temp

            Exports the 'System Correlation' data collector set from sql2017 to C:\temp.
    #>
    [CmdletBinding()]
    param (
        [DbaInstance[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias("DataCollectorSet")]
        [string[]]$CollectorSet,
        [string]$Path = "$home\Documents\Performance Monitor Templates",
        [Parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [switch]$EnableException
    )
    process {
        if ($InputObject.Credential -and (Test-Bound -ParameterName Credential -Not)) {
            $Credential = $InputObject.Credential
        }

        if (-not $InputObject -or ($InputObject -and (Test-Bound -ParameterName ComputerName))) {
            foreach ($computer in $ComputerName) {
                $InputObject += Get-DbaPfDataCollectorSet -ComputerName $computer -Credential $Credential -CollectorSet $CollectorSet
            }
        }

        foreach ($object in $InputObject) {
            if (-not $object.DataCollectorSetObject) {
                Stop-Function -Message "InputObject is not of the right type. Please use Get-DbaPfDataCollectorSet."
                return
            }

            $csname = Remove-InvalidFileNameChars -Name $object.Name

            if ($path.EndsWith(".xml")) {
                $filename = $path
            }
            else {
                $filename = "$path\$csname.xml"
                if (-not (Test-Path -Path $path)) {
                    $null = New-Item -Type Directory -Path $path
                }
            }
            Write-Message -Level Verbose -Message "Wrote $csname to $filename."
            Set-Content -Path $filename -Value $object.Xml -Encoding Unicode
            Get-ChildItem -Path $filename
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Export-DbaRegisteredServer {
    <#
        .SYNOPSIS
            Exports registered servers and registered server groups to file

        .DESCRIPTION
            Exports registered servers and registered server groups to file

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Group
            Exports a specific group.

        .PARAMETER CredentialPersistenceType
            Used to specify how the login and passwords are persisted. Valid values include None, PersistLoginName and PersistLoginNameAndPassword.

        .PARAMETER Path
            The path to the exported file. If no path is specified, one will be created.

        .PARAMETER InputObject
            Enables piping from Get-DbaRegisteredServer, Get-DbaRegisteredServerGroup, CSVs and other objects.

            If importing from CSV or other object, a column named ServerName is required. Optional columns include Name, Description and Group.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.

            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.

            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Chrissy LeMaire (@cl)
            Tags: RegisteredServer, CMS

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Export-DbaRegisteredServer

        .EXAMPLE
           Export-DbaRegisteredServer -SqlInstance sql2008

           Exports all Registered Server and Registered Server Groups on sql2008 to an automatically generated file name in the current directory

        .EXAMPLE
           Export-DbaRegisteredServer -SqlInstance sql2008 -Group hr\Seattle -Path C:\temp\Seattle.xml

           Exports all Registered Server and Registered Server Groups with the Seattle group within the HR group on sql2008 to C:\temp\Seattle.xml

        .EXAMPLE
           Get-DbaRegisteredServer -SqlInstance sql2008, sql2012 | Export-DbaRegisteredServer

           Exports all registered servers on sql2008 and sql2012. Warning - each one will have its own individual file. Consider piping groups.

        .EXAMPLE
           Get-DbaRegisteredServerGroup -SqlInstance sql2008, sql2012 | Export-DbaRegisteredServer

           Exports all registered servers on sql2008 and sql2012, organized by group.
    #>
    [CmdletBinding()]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [string]$Path,
        [ValidateSet("None", "PersistLoginName", "PersistLoginNameAndPassword")]
        [string]$CredentialPersistenceType = "None",
        [switch]$EnableException
    )
    begin {
        if ((Test-Bound -ParameterName Path)) {
            if ($Path -notmatch '\\') {
                $Path = ".\$Path"
            }

            $directory = Split-Path $Path
            if (-not (Test-Path $directory)) {
                New-Item -Path $directory -ItemType Directory
            }
        }
        else {
            $timeNow = (Get-Date -uformat "%m%d%Y%H%M%S")
        }
    }
    process {
        foreach ($instance in $SqlInstance) {
            $InputObject += Get-DbaRegisteredServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Id 1
        }

        foreach ($object in $InputObject) {
            try {
                if ($object -is [Microsoft.SqlServer.Management.RegisteredServers.RegisteredServersStore]) {
                    $object = Get-DbaRegisteredServerGroup -SqlInstance $object.ServerConnection.SqlConnectionObject -Id 1
                }

                if ($object -is [Microsoft.SqlServer.Management.RegisteredServers.RegisteredServer]) {
                    if ((Test-Bound -ParameterName Path -Not)) {
                        $servername = $object.SqlInstance.Replace('\', '$')
                        $regservername = $object.Name.Replace('\', '$')
                        $Path = "$serverName-regserver-$regservername-$timeNow.xml"
                    }
                    $object.Export($Path, $CredentialPersistenceType)
                }
                elseif ($object -is [Microsoft.SqlServer.Management.RegisteredServers.ServerGroup]) {
                    if ((Test-Bound -ParameterName Path -Not)) {
                        $servername = $object.SqlInstance.Replace('\', '$')
                        $regservergroup = $object.Name.Replace('\', '$')
                        $Path = "$serverName-reggroup-$regservergroup-$timeNow.xml"
                    }
                    $object.Export($Path, $CredentialPersistenceType)
                }
                else {
                    Stop-Function -Message "InputObject is not a registered server or server group" -Continue
                }
                Get-ChildItem $Path -ErrorAction Stop
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_
            }
        }
    }
}
function Export-DbaScript {
    <#
        .SYNOPSIS
            Exports scripts from SQL Management Objects (SMO)

        .DESCRIPTION
            Exports scripts from SQL Management Objects

        .PARAMETER InputObject
            A SQL Managment Object such as the one returned from Get-DbaLogin

        .PARAMETER Path
            The output filename and location. If no path is specified, one will be created. If the file already exists, the output will be appended.

        .PARAMETER Encoding
            Specifies the file encoding. The default is UTF8.

            Valid values are:
            -- ASCII: Uses the encoding for the ASCII (7-bit) character set.
            -- BigEndianUnicode: Encodes in UTF-16 format using the big-endian byte order.
            -- Byte: Encodes a set of characters into a sequence of bytes.
            -- String: Uses the encoding type for a string.
            -- Unicode: Encodes in UTF-16 format using the little-endian byte order.
            -- UTF7: Encodes in UTF-7 format.
            -- UTF8: Encodes in UTF-8 format.
            -- Unknown: The encoding type is unknown or invalid. The data can be treated as binary.

        .PARAMETER Passthru
            Output script to console

        .PARAMETER ScriptingOptionsObject
            An SMO Scripting Object that can be used to customize the output - see New-DbaScriptingOption

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed

        .PARAMETER NoClobber
            Do not overwrite file

        .PARAMETER Append
            Append to file

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration, Backup, Export

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Export-DbaScript

        .EXAMPLE
            Get-DbaAgentJob -SqlInstance sql2016 | Export-DbaScript

            Exports all jobs on the SQL Server sql2016 instance using a trusted connection - automatically determines filename as .\sql2016-Job-Export-date.sql

        .EXAMPLE
            Get-DbaAgentJob -SqlInstance sql2016 | Export-DbaScript -Path C:\temp\export.sql -Append

            Exports all jobs on the SQL Server sql2016 instance using a trusted connection - Will append the output to the file C:\temp\export.sql if it already exists

        .EXAMPLE
            Get-DbaAgentJob -SqlInstance sql2016 -Job syspolicy_purge_history, 'Hourly Log Backups' -SqlCredential (Get-Credential sqladmin) | Export-DbaScript -Path C:\temp\export.sql

            Exports only syspolicy_purge_history and 'Hourly Log Backups' to C:temp\export.sql and uses the SQL login "sqladmin" to login to sql2016

        .EXAMPLE
            Get-DbaAgentJob -SqlInstance sql2014 | Export-DbaJob -Passthru | ForEach-Object { $_.Replace('sql2014','sql2016') } | Set-Content -Path C:\temp\export.sql

            Exports jobs and replaces all instances of the servername "sql2014" with "sql2016" then writes to C:\temp\export.sql

        .EXAMPLE
            $options = New-DbaScriptingOption
            $options.ScriptDrops = $false
            $options.WithDependencies = $true
            Get-DbaTable -SqlInstance sql2017 -Database PerformanceStore | Export-DbaScript -ScriptingOptionsObject $options

            Exports Agent Jobs with the Scripting Options ScriptDrops set to $false and WithDependencies set to $true.
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [object[]]$InputObject,
        [Alias("ScriptingOptionObject")]
        [Microsoft.SqlServer.Management.Smo.ScriptingOptions]$ScriptingOptionsObject,
        [string]$Path,
        [ValidateSet('ASCII', 'BigEndianUnicode', 'Byte', 'String', 'Unicode', 'UTF7', 'UTF8', 'Unknown')]
        [string]$Encoding = 'UTF8',
        [switch]$Passthru,
        [switch]$NoClobber,
        [switch]$Append,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $executingUser = [Security.Principal.WindowsIdentity]::GetCurrent().Name
        $commandName = $MyInvocation.MyCommand.Name
        $timeNow = (Get-Date -uformat "%m%d%Y%H%M%S")
        $prefixArray = @()
    }

    process {
        foreach ($object in $InputObject) {

            $typename = $object.GetType().ToString()

            if ($typename.StartsWith('Microsoft.SqlServer.')) {
                $shortype = $typename.Split(".")[-1]
            }
            else {
                Stop-Function -Message "InputObject is of type $typename which is not a SQL Management Object. Only SMO objects are supported." -Category InvalidData -Target $object -Continue
            }

            if ($shortype -in "LinkedServer", "Credential", "Login") {
                Write-Message -Level Warning -Message "Support for $shortype is limited at this time. No passwords, hashed or otherwise, will be exported if they exist."
            }

            # Just gotta add the stuff that Nic Cain added to his script

            if ($shortype -eq "Configuration") {
                Write-Message -Level Warning -Message "Support for $shortype is limited at this time."
            }

            # Find the server object to pass on to the function
            $parent = $object.parent

            do {
                if ($parent.Urn.Type -ne "Server") {
                    $parent = $parent.Parent
                }
            }
            until (($parent.Urn.Type -eq "Server") -or (-not $parent))

            if (-not $parent) {
                Stop-Function -Message "Failed to find valid SMO server object in input: $object." -Category InvalidData -Target $object -Continue
            }

            try {
                $server = $parent
                $serverName = $server.Name.Replace('\', '$')

                if ($ScriptingOptionsObject) {
                    $scripter = New-Object Microsoft.SqlServer.Management.Smo.Scripter $server
                    $scripter.Options = $ScriptingOptionsObject
                }

                if (!$passthru) {
                    if ($path) {
                        $actualPath = $path
                    }
                    else {
                        $actualPath = "$serverName-$shortype-Export-$timeNow.sql"
                    }
                }

                $prefix = "/*`n`tCreated by $executingUser using dbatools $commandName for objects on $serverName at $(Get-Date)`n`tSee https://dbatools.io/$commandName for more information`n*/"

                if ($passthru) {
                    $prefix | Out-String
                }
                else {
                    if ($prefixArray -notcontains $actualPath) {

                        if ((Test-Path -Path $actualPath) -and $NoClobber) {
                            Stop-Function -Message "File already exists. If you want to overwrite it remove the -NoClobber parameter. If you want to append data, please Use -Append parameter." -Target $actualPath -Continue
                        }
                        #Only at the first output we use the passed variables Append & NoClobber. For this execution the next ones need to buse -Append
                        $prefix | Out-File -FilePath $actualPath -Encoding $encoding -Append:$Append -NoClobber:$NoClobber
                        $prefixArray += $actualPath
                    }
                }

                if ($Pscmdlet.ShouldProcess($env:computername, "Exporting $object from $server to $actualPath")) {
                    Write-Message -Level Verbose -Message "Exporting $object"

                    if ($passthru) {
                        if ($ScriptingOptionsObject) {
                            foreach ($script in $scripter.EnumScript($object)) {
                                $script | Out-String
                            }
                        }
                        else {
                            $object.Script() | Out-String
                        }
                    }
                    else {
                        if ($ScriptingOptionsObject) {
                            foreach ($script in $scripter.EnumScript($object)) {
                                $script | Out-File -FilePath $actualPath -Encoding $encoding -Append
                            }
                        }
                        else {
                            $object.Script() | Out-File -FilePath $actualPath -Encoding $encoding -Append
                        }
                    }
                }

                if (!$passthru) {
                    Write-Message -Level Output -Message "Exported $object on $($server.Name) to $actualPath"
                }
            }
            catch {
                $message = $_.Exception.InnerException.InnerException.InnerException.Message
                if (-not $message) {
                    $message = $_.Exception
                }
                Stop-Function -Message "Failure on $($server.Name) | $message" -Target $server
            }
        }
    }
}
function Export-DbaSpConfigure {
    <#
        .SYNOPSIS
            Exports advanced sp_configure global configuration options to sql file.

        .DESCRIPTION
            Exports advanced sp_configure global configuration options to sql file.

        .PARAMETER SqlInstance
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2005 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Path
            Specifies the path to a file which will contain the sp_configure queries necessary to replicate the configuration settings on another instance. This file is suitable for input into Import-DbaSPConfigure.

        .PARAMETER Whatif
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .NOTES
            Tags: SpConfig, Configure, Configuration
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            Export-DbaSpConfigure -SqlInstance sourceserver -Path C:\temp\sp_configure.sql

            Exports the SPConfigure settings on sourceserver to the file C:\temp\sp_configure.sql

        .OUTPUTS
            File to disk, and string path.

    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [string]$Path,
        [PSCredential]$SqlCredential
    )

    begin {
        $server = Connect-SqlInstance $sqlinstance $SqlCredential

        if ($server.versionMajor -lt 9) {
            Write-Error "Windows 2000 is not supported for sp_configure export."
            break
        }

        if ($path.length -eq 0) {
            $timenow = (Get-Date -uformat "%m%d%Y%H%M%S")
            $mydocs = [Environment]::GetFolderPath('MyDocuments')
            $path = "$mydocs\$($server.name.replace('\', '$'))-$timenow-sp_configure.sql"
        }

    }

    process {
        try {
            Set-Content -Path $path "EXEC sp_configure 'show advanced options' , 1;  RECONFIGURE WITH OVERRIDE"
        }
        catch {
            throw "Can't write to $path"
        }

        $server.Configuration.ShowAdvancedOptions.ConfigValue = $true
        $server.Configuration.Alter($true)
        foreach ($sourceprop in $server.Configuration.Properties) {
            $displayname = $sourceprop.DisplayName
            $configvalue = $sourceprop.ConfigValue
            Add-Content -Path $path "EXEC sp_configure '$displayname' , $configvalue;"
        }
        Add-Content -Path $path "EXEC sp_configure 'show advanced options' , 0;"
        Add-Content -Path $Path "RECONFIGURE WITH OVERRIDE"
        $server.Configuration.ShowAdvancedOptions.ConfigValue = $false
        $server.Configuration.Alter($true)
        return $path
    }

    end {
        $server.ConnectionContext.Disconnect()

        If ($Pscmdlet.ShouldProcess("console", "Showing finished message")) {
            Write-Output "Server configuration export finished"
        }
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Export-SqlSpConfigure
    }
}
function Export-DbaUser {
    <#
        .SYNOPSIS
            Exports users creation and its permissions to a T-SQL file or host.

        .DESCRIPTION
            Exports users creation and its permissions to a T-SQL file or host. Export includes user, create and add to role(s), database level permissions, object level permissions.

        .PARAMETER SqlInstance
            The SQL Server instance name. SQL Server 2000 and above supported.

        .PARAMETER SqlCredential
            Allows you to login to servers using alternative credentials

            $scred = Get-Credential, then pass $scred object to the -SqlCredential parameter

            Windows Authentication will be used if SqlCredential is not specified

        .PARAMETER Database
            The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto-populated from the server

        .PARAMETER User
            Export only the specified database user(s). If not specified will export all users from the database(s)

        .PARAMETER DestinationVersion
            To say to which version the script should be generated. If not specified will use database compatibility level

        .PARAMETER FilePath
            The file to write to.

        .PARAMETER NoClobber
            Do not overwrite file

        .PARAMETER Append
            Append to file

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER ScriptingOptionsObject
            A Microsoft.SqlServer.Management.Smo.ScriptingOptions object with the options that you want to use to generate the t-sql script.
            You can use the NEw-DbaScriptingOption to generate it.

        .PARAMETER ExcludeGoBatchSeparator
            If specified, will NOT script the 'GO' batch separator.

        .NOTES
            Tags: User, Export
            Author: Claudio Silva (@ClaudioESSilva)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Export-DbaUser

        .EXAMPLE
            Export-DbaUser -SqlInstance sql2005 -FilePath C:\temp\sql2005-users.sql

            Exports SQL for the users in server "sql2005" and writes them to the file "C:\temp\sql2005-users.sql"

        .EXAMPLE
            Export-DbaUser -SqlInstance sqlserver2014a $scred -FilePath C:\temp\users.sql -Append

            Authenticates to sqlserver2014a using SQL Authentication. Exports all users to C:\temp\users.sql, and appends to the file if it exists. If not, the file will be created.

        .EXAMPLE
            Export-DbaUser -SqlInstance sqlserver2014a -User User1, User2 -FilePath C:\temp\users.sql

            Exports ONLY users User1 and User2 fron sqlsever2014a to the file  C:\temp\users.sql

        .EXAMPLE
            Export-DbaUser -SqlInstance sqlserver2008 -User User1 -FilePath C:\temp\users.sql -DestinationVersion SQLServer2016

            Exports user User1 fron sqlsever2008 to the file C:\temp\users.sql with sintax to run on SQL Server 2016

        .EXAMPLE
            Export-DbaUser -SqlInstance sqlserver2008 -Database db1,db2 -FilePath C:\temp\users.sql

            Exports ONLY users from db1 and db2 database on sqlserver2008 server, to the C:\temp\users.sql file.

        .EXAMPLE
            $options = New-DbaScriptingOption
            $options.ScriptDrops = $false
            $options.WithDependencies = $true

            Export-DbaUser -SqlInstance sqlserver2008 -Database db1,db2 -FilePath C:\temp\users.sql -ScriptingOptionsObject $options

            Exports ONLY users from db1 and db2 database on sqlserver2008 server, to the C:\temp\users.sql file.
            It will not script drops but will script dependencies.

        .EXAMPLE
            Export-DbaUser -SqlInstance sqlserver2008 -Database db1,db2 -FilePath C:\temp\users.sql -ExcludeGoBatchSeparator

            Exports ONLY users from db1 and db2 database on sqlserver2008 server, to the C:\temp\users.sql file without the 'GO' batch separator.

    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    [OutputType([String])]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [object[]]$User,
        [ValidateSet('SQLServer2000', 'SQLServer2005', 'SQLServer2008/2008R2', 'SQLServer2012', 'SQLServer2014', 'SQLServer2016', 'SQLServer2017')]
        [string]$DestinationVersion,
        [Alias("OutFile", "Path", "FileName")]
        [string]$FilePath,
        [Alias("NoOverwrite")]
        [switch]$NoClobber,
        [switch]$Append,
        [Alias('Silent')]
        [switch]$EnableException,
        [Microsoft.SqlServer.Management.Smo.ScriptingOptions]$ScriptingOptionsObject = $null,
        [switch]$ExcludeGoBatchSeparator
    )

    begin {
        if ($FilePath) {
            if ($FilePath -notlike "*\*") { $FilePath = ".\$filepath" }
            $directory = Split-Path $FilePath
            $exists = Test-Path $directory

            if ($exists -eq $false) {
                Stop-Function -Message "Parent directory $directory does not exist"
                return
            }
        }

        $outsql = @()

        $versions = @{
            'SQLServer2000'        = 'Version80'
            'SQLServer2005'        = 'Version90'
            'SQLServer2008/2008R2' = 'Version100'
            'SQLServer2012'        = 'Version110'
            'SQLServer2014'        = 'Version120'
            'SQLServer2016'        = 'Version130'
            'SQLServer2017'        = 'Version140'
        }

        $versionName = @{
            'Version80'  = 'SQLServer2000'
            'Version90'  = 'SQLServer2005'
            'Version100' = 'SQLServer2008/2008R2'
            'Version110' = 'SQLServer2012'
            'Version120' = 'SQLServer2014'
            'Version130' = 'SQLServer2016'
            'Version140' = 'SQLServer2017'
        }

    }
    process {
        if (Test-FunctionInterrupt) { return }

        try {
            Write-Message -Level Verbose -Message "Connecting to $sqlinstance"
            $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
        }

        if (!$database) {
            $databases = $server.Databases | Where-Object { $ExcludeDatabase -notcontains $_.Name -and $_.IsAccessible -eq $true }
        }
        else {
            if ($pipedatabase) {
                $source = $pipedatabase[0].parent.name
                $databases = $pipedatabase.name
            }
            else {
                $databases = $server.Databases | Where-Object { $_.IsAccessible -eq $true -and ($database -contains $_.Name) }
            }
        }

        if ($exclude) {
            $databases = $databases | Where-Object Name -notin $ExcludeDatabase
        }

        if (@($databases).Count -gt 0) {

            #Database Permissions
            foreach ($db in $databases) {
                if ([string]::IsNullOrEmpty($destinationVersion)) {
                    #Get compatibility level for scripting the objects
                    $scriptVersion = $db.CompatibilityLevel
                }
                else {
                    $scriptVersion = $versions[$destinationVersion]
                }
                $versionNameDesc = $versionName[$scriptVersion.ToString()]

                #If not passed create new ScriptingOption. Otherwise use the one that was passed
                if ($null -eq $ScriptingOptionsObject) {
                    $ScriptingOptionsObject = New-DbaScriptingOption
                    $ScriptingOptionsObject.TargetServerVersion = [Microsoft.SqlServer.Management.Smo.SqlServerVersion]::$scriptVersion
                    $ScriptingOptionsObject.AllowSystemObjects = $false
                    $ScriptingOptionsObject.IncludeDatabaseRoleMemberships = $true
                    $ScriptingOptionsObject.ContinueScriptingOnError = $false
                    $ScriptingOptionsObject.IncludeDatabaseContext = $false
                    $ScriptingOptionsObject.IncludeIfNotExists = $true
                }

                Write-Message -Level Output -Message "Validating users on database $db"

                if ($User.Count -eq 0) {
                    $users = $db.Users | Where-Object { $_.IsSystemObject -eq $false -and $_.Name -notlike "##*" }
                }
                else {
                    if ($pipedatabase) {
                        $source = $pipedatabase[3].parent.name
                        $users = $pipedatabase.name
                    }
                    else {
                        $users = $db.Users | Where-Object { $User -contains $_.Name -and $_.IsSystemObject -eq $false -and $_.Name -notlike "##*" }
                    }
                }
                # Store roles between users so if we hit the same one we dont create it again
                $roles = @()
                if ($users.Count -gt 0) {
                    foreach ($dbuser in $users) {
                        Write-Message -Level Output -Message "Generating script for user $dbuser"

                        #setting database
                        $outsql += "USE [" + $db.Name + "]"

                        try {
                            #Fixed Roles #Dependency Issue. Create Role, before add to role.
                            foreach ($rolePermission in ($db.Roles | Where-Object { $_.IsFixedRole -eq $false })) {
                                foreach ($rolePermissionScript in $rolePermission.Script($ScriptingOptionsObject)) {
                                    if ($rolePermission.ToString() -notin $roles) {
                                        $roles += , $rolePermission.ToString()
                                        $outsql += "$($rolePermissionScript.ToString())"
                                    }

                                }
                            }

                            #Database Create User(s) and add to Role(s)
                            foreach ($dbUserPermissionScript in $dbuser.Script($ScriptingOptionsObject)) {
                                if ($dbuserPermissionScript.Contains("sp_addrolemember")) {
                                    $execute = "EXEC "
                                }
                                else {
                                    $execute = ""
                                }
                                $outsql += "$execute$($dbUserPermissionScript.ToString())"
                            }

                            #Database Permissions
                            foreach ($databasePermission in $db.EnumDatabasePermissions() | Where-Object { @("sa", "dbo", "information_schema", "sys") -notcontains $_.Grantee -and $_.Grantee -notlike "##*" -and ($dbuser.Name -contains $_.Grantee) }) {
                                if ($databasePermission.PermissionState -eq "GrantWithGrant") {
                                    $withGrant = " WITH GRANT OPTION"
                                    $grantDatabasePermission = 'GRANT'
                                }
                                else {
                                    $withGrant = " "
                                    $grantDatabasePermission = $databasePermission.PermissionState.ToString().ToUpper()
                                }

                                $outsql += "$($grantDatabasePermission) $($databasePermission.PermissionType) TO [$($databasePermission.Grantee)]$withGrant AS [$($databasePermission.Grantor)];"
                            }

                            #Database Object Permissions
                            # NB: This is a bit of a mess for a couple of reasons
                            # 1. $db.EnumObjectPermissions() doesn't enumerate all object types
                            # 2. Some (x)Collection types can have EnumObjectPermissions() called
                            #    on them directly (e.g. AssemblyCollection); others can't (e.g.
                            #    ApplicationRoleCollection). Those that can't we iterate the
                            #    collection explicitly and add each object's permission.

                            $perms = New-Object System.Collections.ArrayList

                            $null = $perms.AddRange($db.EnumObjectPermissions($dbuser.Name))

                            foreach ($item in $db.ApplicationRoles) {
                                $null = $perms.AddRange($item.EnumObjectPermissions($dbuser.Name))
                            }

                            foreach ($item in $db.Assemblies) {
                                $null = $perms.AddRange($item.EnumObjectPermissions($dbuser.Name))
                            }

                            foreach ($item in $db.Certificates) {
                                $null = $perms.AddRange($item.EnumObjectPermissions($dbuser.Name))
                            }

                            foreach ($item in $db.DatabaseRoles) {
                                $null = $perms.AddRange($item.EnumObjectPermissions($dbuser.Name))
                            }

                            foreach ($item in $db.FullTextCatalogs) {
                                $null = $perms.AddRange($item.EnumObjectPermissions($dbuser.Name))
                            }

                            foreach ($item in $db.FullTextStopLists) {
                                $null = $perms.AddRange($item.EnumObjectPermissions($dbuser.Name))
                            }

                            foreach ($item in $db.SearchPropertyLists) {
                                $null = $perms.AddRange($item.EnumObjectPermissions($dbuser.Name))
                            }

                            foreach ($item in $db.ServiceBroker.MessageTypes) {
                                $null = $perms.AddRange($item.EnumObjectPermissions($dbuser.Name))
                            }

                            foreach ($item in $db.RemoteServiceBindings) {
                                $null = $perms.AddRange($item.EnumObjectPermissions($dbuser.Name))
                            }

                            foreach ($item in $db.ServiceBroker.Routes) {
                                $null = $perms.AddRange($item.EnumObjectPermissions($dbuser.Name))
                            }

                            foreach ($item in $db.ServiceBroker.ServiceContracts) {
                                $null = $perms.AddRange($item.EnumObjectPermissions($dbuser.Name))
                            }

                            foreach ($item in $db.ServiceBroker.Services) {
                                $null = $perms.AddRange($item.EnumObjectPermissions($dbuser.Name))
                            }

                            if ($scriptVersion -ne "Version80") {
                                foreach ($item in $db.AsymmetricKeys) {
                                    $null = $perms.AddRange($item.EnumObjectPermissions($dbuser.Name))
                                }
                            }

                            foreach ($item in $db.SymmetricKeys) {
                                $null = $perms.AddRange($item.EnumObjectPermissions($dbuser.Name))
                            }

                            foreach ($item in $db.XmlSchemaCollections) {
                                $null = $perms.AddRange($item.EnumObjectPermissions($dbuser.Name))
                            }

                            foreach ($objectPermission in $perms | Where-Object { @("sa", "dbo", "information_schema", "sys") -notcontains $_.Grantee -and $_.Grantee -notlike "##*" -and $_.Grantee -eq $dbuser.Name }) {
                                switch ($objectPermission.ObjectClass) {
                                    'ApplicationRole' {
                                        $object = 'APPLICATION ROLE::[{0}]' -f $objectPermission.ObjectName
                                    }
                                    'AsymmetricKey' {
                                        $object = 'ASYMMETRIC KEY::[{0}]' -f $objectPermission.ObjectName
                                    }
                                    'Certificate' {
                                        $object = 'CERTIFICATE::[{0}]' -f $objectPermission.ObjectName
                                    }
                                    'DatabaseRole' {
                                        $object = 'ROLE::[{0}]' -f $objectPermission.ObjectName
                                    }
                                    'FullTextCatalog' {
                                        $object = 'FULLTEXT CATALOG::[{0}]' -f $objectPermission.ObjectName
                                    }
                                    'FullTextStopList' {
                                        $object = 'FULLTEXT STOPLIST::[{0}]' -f $objectPermission.ObjectName
                                    }
                                    'MessageType' {
                                        $object = 'Message Type::[{0}]' -f $objectPermission.ObjectName
                                    }
                                    'ObjectOrColumn' {
                                        if ($scriptVersion -ne "Version80") {
                                            $object = 'OBJECT::[{0}].[{1}]' -f $objectPermission.ObjectSchema, $objectPermission.ObjectName
                                            if ($null -ne $objectPermission.ColumnName) {
                                                $object += '([{0}])' -f $objectPermission.ColumnName
                                            }
                                        }
                                        #At SQL Server 2000 OBJECT did not exists
                                        else {
                                            $object = '[{0}].[{1}]' -f $objectPermission.ObjectSchema, $objectPermission.ObjectName
                                        }
                                    }
                                    'RemoteServiceBinding' {
                                        $object = 'REMOTE SERVICE BINDING::[{0}]' -f $objectPermission.ObjectName
                                    }
                                    'Schema' {
                                        $object = 'SCHEMA::[{0}]' -f $objectPermission.ObjectName
                                    }
                                    'SearchPropertyList' {
                                        $object = 'SEARCH PROPERTY LIST::[{0}]' -f $objectPermission.ObjectName
                                    }
                                    'Service' {
                                        $object = 'SERVICE::[{0}]' -f $objectPermission.ObjectName
                                    }
                                    'ServiceContract' {
                                        $object = 'CONTRACT::[{0}]' -f $objectPermission.ObjectName
                                    }
                                    'ServiceRoute' {
                                        $object = 'ROUTE::[{0}]' -f $objectPermission.ObjectName
                                    }
                                    'SqlAssembly' {
                                        $object = 'ASSEMBLY::[{0}]' -f $objectPermission.ObjectName
                                    }
                                    'SymmetricKey' {
                                        $object = 'SYMMETRIC KEY::[{0}]' -f $objectPermission.ObjectName
                                    }
                                    'User' {
                                        $object = 'USER::[{0}]' -f $objectPermission.ObjectName
                                    }
                                    'UserDefinedType' {
                                        $object = 'TYPE::[{0}].[{1}]' -f $objectPermission.ObjectSchema, $objectPermission.ObjectName
                                    }
                                    'XmlNamespace' {
                                        $object = 'XML SCHEMA COLLECTION::[{0}]' -f $objectPermission.ObjectName
                                    }
                                }

                                if ($objectPermission.PermissionState -eq "GrantWithGrant") {
                                    $withGrant = " WITH GRANT OPTION"
                                    $grantObjectPermission = 'GRANT'
                                }
                                else {
                                    $withGrant = " "
                                    $grantObjectPermission = $objectPermission.PermissionState.ToString().ToUpper()
                                }

                                $outsql += "$grantObjectPermission $($objectPermission.PermissionType) ON $object TO [$($objectPermission.Grantee)]$withGrant AS [$($objectPermission.Grantor)];"
                            }

                        }
                        catch {
                            Stop-Function -Message "This user may be using functionality from $($versionName[$db.CompatibilityLevel.ToString()]) that does not exist on the destination version ($versionNameDesc)." -Continue -InnerErrorRecord $_ -Target $db
                        }
                    }
                }
                else {
                    Write-Message -Level Output -Message "No users found on database '$db'"
                }

                #reset collection
                $users = $null
            }
        }
        else {
            Write-Message -Level Output -Message "No users found on instance '$server'"
        }
    }

    end {
        if (Test-FunctionInterrupt) { return }

        if ($ExcludeGoBatchSeparator) {
            $sql = $outsql
        }
        else {
            $sql = $outsql -join "`r`nGO`r`n"
            #add the final GO
            $sql += "`r`nGO"
        }

        if ($FilePath) {
            $sql | Out-File -Encoding UTF8 -FilePath $FilePath -Append:$Append -NoClobber:$NoClobber
        }
        else {
            $sql
        }
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Export-SqlUser
    }
}
function Export-DbaXECsv {
    <#
        .SYNOPSIS
            Exports Extended Events to a CSV file.

        .DESCRIPTION
            Exports Extended Events to a CSV file.

        .PARAMETER Path
            Specifies the InputObject to the output CSV file

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER InputObject
            Allows Piping

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Author: Gianluca Sartori (@spaghettidba)

            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
            SmartTarget: by Gianluca Sartori (@spaghettidba)

        .LINK
            https://dbatools.io/Export-DbaXECsv

        .EXAMPLE
            Get-ChildItem -Path C:\temp\sample.xel | Export-DbaXECsv -Path c:\temp\sample.csv

            Writes Extended Events data to the file "C:\temp\events.csv".

         .EXAMPLE
            Get-DbaXESession -SqlInstance sql2014 -Session deadlocks | Export-DbaXECsv -Path c:\temp\events.csv

            Writes Extended Events data to the file "C:\temp\events.csv".
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias('FullName')]
        [object[]]$InputObject,
        [parameter(Mandatory)]
        [string]$Path,
        [switch]$EnableException
    )
    begin {
        try {
            Add-Type -Path "$script:PSModuleRoot\bin\XESmartTarget\XESmartTarget.Core.dll" -ErrorAction Stop
        }
        catch {
            Stop-Function -Message "Could not load XESmartTarget.Core.dll" -ErrorRecord $_ -Target "XESmartTarget"
            return
        }

        function Get-FileFromXE ($InputObject) {
            if ($InputObject.TargetFile) {
                if ($InputObject.TargetFile.Length -eq 0) {
                    Stop-Function -Message "This session does not have an associated Target File."
                    return
                }

                $instance = [dbainstance]$InputObject.ComputerName

                if ($instance.IsLocalHost) {
                    $xelpath = $InputObject.TargetFile
                }
                else {
                    $xelpath = $InputObject.RemoteTargetFile
                }

                if ($xelpath -notmatch ".xel") {
                    $xelpath = "$xelpath*.xel"
                }

                try {
                    Get-ChildItem -Path $xelpath -ErrorAction Stop
                }
                catch {
                    Stop-Function -Message "Failure" -ErrorRecord $_
                }
            }
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }

        $getfiles = Get-FileFromXE $InputObject

        if ($getfiles) {
            $InputObject += $getfiles
        }

        foreach ($file in $InputObject) {
            if ($file -is [System.String]) {
                $currentfile = $file
            }
            elseif ($file -is [System.IO.FileInfo]) {
                $currentfile = $file.FullName
            }
            elseif ($file -is [Microsoft.SqlServer.Management.XEvent.Session]) {
                # it was taken care of above
                continue
            }
            else {
                Stop-Function -Message "Unsupported file type."
                return
            }

            $accessible = Test-Path -Path $currentfile
            $whoami = whoami

            if (-not $accessible) {
                if ($file.Status -eq "Stopped") { continue }
                Stop-Function -Continue -Message "$currentfile cannot be accessed from $($env:COMPUTERNAME). Does $whoami have access?"
            }

            if (-not (Test-Path $Path)) {
                if ([String]::IsNullOrEmpty([IO.Path]::GetExtension($Path))) {
                    New-Item $Path -ItemType directory | Out-Null
                    $outDir = $Path
                    $outFile = [IO.Path]::GetFileNameWithoutExtension($currentfile) + ".csv"
                }
                else {
                    $outDir = [IO.Path]::GetDirectoryName($Path)
                    $outFile = [IO.Path]::GetFileName($Path)
                }
            }
            else {
                if ((Get-Item $Path) -is [System.IO.DirectoryInfo]) {
                    $outDir = $Path
                    $outFile = [IO.Path]::GetFileNameWithoutExtension($currentfile) + ".csv"
                }
                else {
                    $outDir = [IO.Path]::GetDirectoryName($Path)
                    $outFile = [IO.Path]::GetFileName($Path)
                }
            }

            $adapter = New-Object XESmartTarget.Core.Utils.XELFileCSVAdapter
            $adapter.InputFile = $currentfile
            $adapter.OutputFile = (Join-Path $outDir $outFile)

            try {
                $adapter.Convert()
                $file = Get-ChildItem -Path $adapter.OutputFile

                if ($file.Length -eq 0) {
                    Remove-Item -Path $adapter.OutputFile
                }
                else {
                    $file
                }
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_ -Target "XESmartTarget" -Continue
            }
        }
    }
}
function Export-DbaXESessionTemplate {
    <#
        .SYNOPSIS
            Exports an XESession XML Template.

        .DESCRIPTION
            Exports an XESession XML Template either from the dbatools repository or a file you specify. Exports to "$home\Documents\SQL Server Management Studio\Templates\XEventTemplates" by default

        .PARAMETER SqlInstance
            Target SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Session
            The Name of the session(s) to export.

        .PARAMETER Path
            The path to export the file into. Can be .xml or directory.

        .PARAMETER InputObject
            Specifies an XE Session output by Get-DbaXESession.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Export-DbaXESessionTemplate

        .EXAMPLE
            Export-DbaXESessionTemplate -SqlInstance sql2017 -Path C:\temp\xe

            Exports XE Session Template to the C:\temp\xe folder.

        .EXAMPLE
            Get-DbaXESession -SqlInstance sql2017 -Session session_health | Export-DbaXESessionTemplate -Path C:\temp

            Returns a new XE Session object from sql2017 then adds an event, an action then creates it.

    #>
    [CmdletBinding()]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Session,
        [string]$Path = "$home\Documents\SQL Server Management Studio\Templates\XEventTemplates",
        [Parameter(ValueFromPipeline)]
        [Microsoft.SqlServer.Management.XEvent.Session[]]$InputObject,
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $InputObject += Get-DbaXESession -SqlInstance $instance -SqlCredential $SqlCredential -Session $Session -EnableException
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
        }

        foreach ($xes in $InputObject) {
            $xesname = Remove-InvalidFileNameChars -Name $xes.Name

            if (-not (Test-Path -Path $Path)) {
                Stop-Function -Message "$Path does not exist." -Target $Path
            }

            if ($path.EndsWith(".xml")) {
                $filename = $path
            }
            else {
                $filename = "$path\$xesname.xml"
            }
            Write-Message -Level Verbose -Message "Wrote $xesname to $filename"
            [Microsoft.SqlServer.Management.XEvent.XEStore]::SaveSessionToTemplate($xes, $filename, $true)
            Get-ChildItem -Path $filename
        }
    }
}
function Find-DbaAgentJob {
    <#
        .SYNOPSIS
            Find-DbaAgentJob finds agent job/s that fit certain search filters.

        .DESCRIPTION
            This command filters SQL Agent jobs giving the DBA a list of jobs that may need attention or could possibly be options for removal.

        .PARAMETER SqlInstance
            The SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SqlCredential
            Allows you to login to servers using SQL Logins as opposed to Windows Auth/Integrated/Trusted.

        .PARAMETER JobName
            Filter agent jobs to only the name(s) you list.
            Supports regular expression (e.g. MyJob*) being passed in.

        .PARAMETER ExcludeJobName
            Allows you to enter an array of agent job names to ignore

        .PARAMETER StepName
            Filter based on StepName.
            Supports regular expression (e.g. MyJob*) being passed in.

        .PARAMETER LastUsed
            Find all jobs that havent ran in the INT number of previous day(s)

        .PARAMETER IsDisabled
            Find all jobs that are disabled

        .PARAMETER IsFailed
            Find all jobs that have failed

        .PARAMETER IsNotScheduled
            Find all jobs with no schedule assigned

        .PARAMETER IsNoEmailNotification
            Find all jobs without email notification configured

        .PARAMETER Category
            Filter based on agent job categories

        .PARAMETER Owner
            Filter based on owner of the job/s

        .PARAMETER Since
            Datetime object used to narrow the results to a date

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Agent, Job
            Author: Stephen Bennett (https://sqlnotesfromtheunderground.wordpress.com/)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Find-DbaAgentJob

        .EXAMPLE
            Find-DbaAgentJob -SqlInstance Dev01 -JobName backup*

            Returns all agent job(s) that have backup in the name

        .EXAMPLE
            Find-DbaAgentJob -SqlInstance Dev01, Dev02 -JobName Mybackup

            Returns all agent job(s) that are named exactly Mybackup

        .EXAMPLE
            Find-DbaAgentJob -SqlInstance Dev01 -LastUsed 10

            Returns all agent job(s) that have not ran in 10 days

        .EXAMPLE
            Find-DbaAgentJob -SqlInstance Dev01 -IsDisabled -IsNoEmailNotification -IsNotScheduled

            Returns all agent job(s) that are either disabled, have no email notification or don't have a schedule. returned with detail

        .EXAMPLE
            $servers | Find-DbaAgentJob -IsFailed | Start-DbaAgentJob

            Finds all failed job then starts them. Consider using a -WhatIf at the end of Start-DbaAgentJob to see what it'll do first

        .EXAMPLE
            Find-DbaAgentJob -SqlInstance Dev01 -LastUsed 10 -Exclude "Yearly - RollUp Workload", "SMS - Notification"

            Returns all agent jobs that havent ran in the last 10 ignoring jobs "Yearly - RollUp Workload" and "SMS - Notification"

        .EXAMPLE
            Find-DbaAgentJob -SqlInstance Dev01 -Category "REPL-Distribution", "REPL-Snapshot" -Detailed | Format-Table -AutoSize -Wrap

            Returns all job/s on Dev01 that are in either category "REPL-Distribution" or "REPL-Snapshot" with detailed output

        .EXAMPLE
            Find-DbaAgentJob -SqlInstance Dev01, Dev02 -IsFailed -Since '7/1/2016 10:47:00'

            Returns all agent job(s) that have failed since July of 2016 (and still have history in msdb)

        .EXAMPLE
            Get-DbaRegisteredServer -SqlInstance CMSServer -Group Production | Find-DbaAgentJob -Disabled -IsNotScheduled | Format-Table -AutoSize -Wrap

            Queries CMS server to return all SQL instances in the Production folder and then list out all agent jobs that have either been disabled or have no schedule.
    #>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]
        $SqlCredential,
        [Alias("Name")]
        [string[]]$JobName,
        [string[]]$ExcludeJobName,
        [string[]]$StepName,
        [int]$LastUsed,
        [Alias("Disabled")]
        [switch]$IsDisabled,
        [Alias("Failed")]
        [switch]$IsFailed,
        [Alias("NoSchedule")]
        [switch]$IsNotScheduled,
        [Alias("NoEmailNotification")]
        [switch]$IsNoEmailNotification,
        [string[]]$Category,
        [string]$Owner,
        [datetime]$Since,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        if ($IsFailed, [boolean]$JobName, [boolean]$StepName, [boolean]$LastUsed.ToString(), $IsDisabled, $IsNotScheduled, $IsNoEmailNotification, [boolean]$Category, [boolean]$Owner, [boolean]$ExcludeJobName -notcontains $true) {
            Stop-Function -Message "At least one search term must be specified"
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Running Scan on: $instance"

            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $jobs = $server.JobServer.jobs
            $output = @()

            if ($IsFailed) {
                Write-Message -Level Verbose -Message "Checking for failed jobs."
                $output += $jobs | Where-Object LastRunOutcome -eq "Failed"
            }

            if ($JobName) {
                Write-Message -Level Verbose -Message "Retrieving jobs by their name."
                $output += Get-JobList -SqlInstance $server -JobFilter $JobName
            }

            if ($StepName) {
                Write-Message -Level Verbose -Message "Retrieving jobs by their step names."
                $output += Get-JobList -SqlInstance $server -StepFilter $StepName
            }

            if ($LastUsed) {
                $DaysBack = $LastUsed * -1
                $SinceDate = (Get-date).AddDays($DaysBack)
                Write-Message -Level Verbose -Message "Finding job/s not ran in last $LastUsed days"
                $output += $jobs | Where-Object { $_.LastRunDate -le $SinceDate }
            }

            if ($IsDisabled) {
                Write-Message -Level Verbose -Message "Finding job/s that are disabled"
                $output += $jobs | Where-Object IsEnabled -eq $false
            }

            if ($IsNotScheduled) {
                Write-Message -Level Verbose -Message "Finding job/s that have no schedule defined"
                $output += $jobs | Where-Object HasSchedule -eq $false
            }
            if ($IsNoEmailNotification) {
                Write-Message -Level Verbose -Message "Finding job/s that have no email operator defined"
                $output += $jobs | Where-Object { [string]::IsNullOrEmpty($_.OperatorToEmail) -eq $true }
            }

            if ($Category) {
                Write-Message -Level Verbose -Message "Finding job/s that have the specified category defined"
                $output += $jobs | Where-Object { $Category -contains $_.Category }
            }

            if ($Owner) {
                Write-Message -Level Verbose -Message "Finding job/s with owner critera"
                if ($Owner -match "-") {
                    $OwnerMatch = $Owner -replace "-", ""
                    Write-Message -Level Verbose -Message "Checking for jobs that NOT owned by: $OwnerMatch"
                    $output += $server.JobServer.jobs | Where-Object { $OwnerMatch -notcontains $_.OwnerLoginName }
                }
                else {
                    Write-Message -Level Verbose -Message "Checking for jobs that are owned by: $owner"
                    $output += $server.JobServer.jobs | Where-Object { $Owner -contains $_.OwnerLoginName }
                }
            }

            if ($Exclude) {
                Write-Message -Level Verbose -Message "Excluding job/s based on Exclude"
                $output = $output | Where-Object { $Exclude -notcontains $_.Name }
            }

            if ($Since) {
                #$Since = $Since.ToString("yyyy-MM-dd HH:mm:ss")
                Write-Message -Level Verbose -Message "Getting only jobs whose LastRunDate is greater than or equal to $since"
                $output = $output | Where-Object { $_.LastRunDate -ge $since }
            }

            $jobs = $output | Select-Object -Unique

            foreach ($job in $jobs) {
                Add-Member -Force -InputObject $job -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                Add-Member -Force -InputObject $job -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                Add-Member -Force -InputObject $job -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                Add-Member -Force -InputObject $job -MemberType NoteProperty -Name JobName -value $job.Name


                Select-DefaultView -InputObject $job -Property ComputerName, InstanceName, SqlInstance, Name, Category, OwnerLoginName, CurrentRunStatus, CurrentRunRetryAttempt, 'IsEnabled as Enabled', LastRunDate, LastRunOutcome, DateCreated, HasSchedule, OperatorToEmail, 'DateCreated as CreateDate'
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Find-DbaBackup {
    <#
        .SYNOPSIS
            Finds SQL Server backups on disk.

        .DESCRIPTION
            Provides all of the same functionality for finding SQL backups to remove from disk as a standard maintenance plan would.

            As an addition you have the ability to check the Archive bit on files before deletion. This will allow you to ensure backups have been archived to your archive location before removal.

        .PARAMETER Path
            Specifies the name of the base level folder to search for backup files.

        .PARAMETER BackupFileExtension
            Specifies the filename extension of the backup files you wish to find (typically 'bak', 'trn' or 'log'). Do not include the period.

        .PARAMETER RetentionPeriod
            Specifies the retention period for backup files. Correct format is ##U.

            ## is the retention value and must be an integer value
            U signifies the units where the valid units are:
            h = hours
            d = days
            w = weeks
            m = months

            Formatting Examples:
            '48h' = 48 hours
            '7d' = 7 days
            '4w' = 4 weeks
            '1m' = 1 month

        .PARAMETER CheckArchiveBit
            If this switch is enabled, the filesystem Archive bit is checked.
            If this bit is set (which translates to "it has not been backed up to another location yet"), the file won't be included.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Backup
            Author: Chris Sommer, @cjsommer, www.cjsommer.com

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Find-DbaBackup

        .EXAMPLE
            Find-DbaBackup -Path 'C:\MSSQL\SQL Backup\' -BackupFileExtension trn -RetentionPeriod 48h

            '*.trn' files in 'C:\MSSQL\SQL Backup\' and all subdirectories that are more than 48 hours old will be included.

        .EXAMPLE
            Find-DbaBackup -Path 'C:\MSSQL\Backup\' -BackupFileExtension bak -RetentionPeriod 7d -CheckArchiveBit

            '*.bak' files in 'C:\MSSQL\Backup\' and all subdirectories that are more than 7 days old will be included, but only if the files have been backed up to another location as verified by checking the Archive bit.

    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, HelpMessage = "Full path to the root level backup folder (ex. 'C:\SQL\Backups'")]
        [Alias("BackupFolder")]
        [string]$Path,
        [parameter(Mandatory = $true, HelpMessage = "Backup File extension to remove (ex. bak, trn, dif)")]
        [string]$BackupFileExtension ,
        [parameter(Mandatory = $true, HelpMessage = "Backup retention period. (ex. 24h, 7d, 4w, 6m)")]
        [string]$RetentionPeriod ,
        [parameter(Mandatory = $false)]
        [switch]$CheckArchiveBit = $false ,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        ### Local Functions
        function Convert-UserFriendlyRetentionToDatetime {
            [cmdletbinding()]
            param (
                [string]$UserFriendlyRetention
            )

            <#
            Convert a user friendly retention value into a datetime.
            The last character of the string will indicate units (validated)
            Valid units are: (h = hours, d = days, w = weeks, m = months)

            The preceeding characters are the value and must be an integer (validated)

            Examples:
                '48h' = 48 hours
                '7d' = 7 days
                '4w' = 4 weeks
                '1m' = 1 month
            #>

            [int]$Length = ($UserFriendlyRetention).Length
            $Value = ($UserFriendlyRetention).Substring(0, $Length - 1)
            $Units = ($UserFriendlyRetention).Substring($Length - 1, 1)

            # Validate that $Units is an accepted unit of measure
            if ( $Units -notin @('h', 'd', 'w', 'm') ) {
                throw "RetentionPeriod '$UserFriendlyRetention' units invalid! See Get-Help for correct formatting and examples."
            }

            # Validate that $Value is an INT
            if ( ![int]::TryParse($Value, [ref]"") ) {
                throw "RetentionPeriod '$UserFriendlyRetention' format invalid! See Get-Help for correct formatting and examples."
            }

            switch ($Units) {
                'h' { $UnitString = 'Hours'; [datetime]$ReturnDatetime = (Get-Date).AddHours( - $Value)  }
                'd' { $UnitString = 'Days'; [datetime]$ReturnDatetime = (Get-Date).AddDays( - $Value)   }
                'w' { $UnitString = 'Weeks'; [datetime]$ReturnDatetime = (Get-Date).AddDays( - $Value * 7) }
                'm' { $UnitString = 'Months'; [datetime]$ReturnDatetime = (Get-Date).AddMonths( - $Value) }
            }
            $ReturnDatetime
        }

        # Validations
        # Ensure BackupFileExtension does not begin with a .
        if ($BackupFileExtension -match "^[.]") {
            Write-Message -Level Warning -Message "Parameter -BackupFileExtension begins with a period '$BackupFileExtension'. A period is automatically prepended to -BackupFileExtension and need not be passed in."
        }
        # Ensure Path is a proper path
        if (!(Test-Path $Path -PathType 'Container')) {
            Stop-Function -Message "$Path not found"
        }

    }
    process {
        if (Test-FunctionInterrupt) { return }
        # Process stuff
        Write-Message -Message "Finding backups on $Path" -Level Verbose
        # Convert Retention Value to an actual DateTime
        try {
            $RetentionDate = Convert-UserFriendlyRetentionToDatetime -UserFriendlyRetention $RetentionPeriod
            Write-Message -Message "Backup Retention Date set to $RetentionDate" -Level Verbose
        }
        catch {
            Stop-Function -Message "Failed to interpret retention time!" -ErrorRecord $_
        }

        # Filter out unarchived files if -CheckArchiveBit parameter is used
        if ($CheckArchiveBit) {
            Write-Message -Message "Removing only archived files." -Level Verbose
            filter DbaArchiveBitFilter {
                if ($_.Attributes -notmatch "Archive") {
                    $_
                }
            }
        }
        else {
            filter DbaArchiveBitFilter {
                $_
            }
        }
        # Enumeration may take a while. Without resorting to "esoteric" file listing facilities
        # and given we need to fetch at least the LastWriteTime, let's just use "streaming" processing
        # here to avoid issues like described in #970
        Get-ChildItem $Path -Filter "*.$BackupFileExtension" -File -Recurse -ErrorAction SilentlyContinue -ErrorVariable EnumErrors |
            Where-Object LastWriteTime -lt $RetentionDate | DbaArchiveBitFilter
        if ($EnumErrors) {
            Write-Message "Errors encountered enumerating files." -Level Warning -ErrorRecord $EnumErrors
        }
    }
}
#ValidationTags#Messaging#
function Find-DbaCommand {
    <#
        .SYNOPSIS
            Finds dbatools commands searching through the inline help text

        .DESCRIPTION
            Finds dbatools commands searching through the inline help text, building a consolidated json index and querying it because Get-Help is too slow

        .PARAMETER Tag
            Finds all commands tagged with this auto-populated tag

        .PARAMETER Author
            Finds all commands tagged with this author

        .PARAMETER MinimumVersion
            Finds all commands tagged with this auto-populated minimum version

        .PARAMETER MaximumVersion
            Finds all commands tagged with this auto-populated maximum version

        .PARAMETER Rebuild
            Rebuilds the index

        .PARAMETER Pattern
            Searches help for all commands in dbatools for the specified pattern and displays all results

        .PARAMETER Confirm
            Confirms overwrite of index

        .PARAMETER WhatIf
            Displays what would happen if the command is run

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Find,Help,Command
            Author: Simone Bizzotto

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Find-DbaCommand

        .EXAMPLE
            Find-DbaCommand "snapshot"

            For lazy typers: finds all commands searching the entire help for "snapshot"

        .EXAMPLE
            Find-DbaCommand -Pattern "snapshot"

            For rigorous typers: finds all commands searching the entire help for "snapshot"

        .EXAMPLE
            Find-DbaCommand -Tag copy

            Finds all commands tagged with "copy"

        .EXAMPLE
            Find-DbaCommand -Tag copy,user

            Finds all commands tagged with BOTH "copy" and "user"

        .EXAMPLE
            Find-DbaCommand -Author chrissy

            Finds every command whose author contains our beloved "chrissy"

        .EXAMPLE
            Find-DbaCommand -Author chrissy -Tag copy

            Finds every command whose author contains our beloved "chrissy" and it tagged as "copy"

        .EXAMPLE
            Find-DbaCommand -Pattern snapshot -Rebuild

            Finds all commands searching the entire help for "snapshot", rebuilding the index (good for developers)
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [String]$Pattern,
        [String[]]$Tag,
        [String]$Author,
        [String]$MinimumVersion,
        [String]$MaximumVersion,
        [switch]$Rebuild,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        $tagsRex = ([regex]'(?m)^[\s]{0,15}Tags:(.*)$')
        $authorRex = ([regex]'(?m)^[\s]{0,15}Author:(.*)$')
        $minverRex = ([regex]'(?m)^[\s]{0,15}MinimumVersion:(.*)$')
        $maxverRex = ([regex]'(?m)^[\s]{0,15}MaximumVersion:(.*)$')

        function Get-DbaHelp([String]$commandName) {
            $thishelp = Get-Help $commandName -Full
            $thebase = @{ }
            $thebase.CommandName = $commandName
            $thebase.Name = $thishelp.Name

            ## fetch the description
            $thebase.Description = $thishelp.Description.Text

            ## fetch examples
            $thebase.Examples = $thishelp.Examples | Out-String -Width 120

            ## fetch help link
            $thebase.Links = ($thishelp.relatedLinks).NavigationLink.Uri

            ## fetch the synopsis
            $thebase.Synopsis = $thishelp.Synopsis

            ## store notes
            $as = $thishelp.AlertSet | Out-String -Width 120

            ## fetch the tags
            $tags = $tagsrex.Match($as).Groups[1].Value
            if ($tags) {
                $thebase.Tags = $tags.Split(',').Trim()
            }
            ## fetch the author
            $author = $authorRex.Match($as).Groups[1].Value
            if ($author) {
                $thebase.Author = $author.Trim()
            }

            ## fetch MinimumVersion
            $MinimumVersion = $minverRex.Match($as).Groups[1].Value
            if ($MinimumVersion) {
                $thebase.MinimumVersion = $MinimumVersion.Trim()
            }

            ## fetch MaximumVersion
            $MaximumVersion = $maxverRex.Match($as).Groups[1].Value
            if ($MaximumVersion) {
                $thebase.MaximumVersion = $MaximumVersion.Trim()
            }

            [pscustomobject]$thebase
        }

        function Get-DbaIndex() {
            if ($Pscmdlet.ShouldProcess($dest, "Recreating index")) {
                $dbamodule = Get-Module -Name dbatools
                $allCommands = $dbamodule.ExportedCommands.Values | Where-Object CommandType -EQ 'Function'

                $helpcoll = New-Object System.Collections.Generic.List[System.Object]
                foreach ($command in $allCommands) {
                    $x = Get-DbaHelp "$command"
                    $helpcoll.Add($x)
                }
                # $dest = Get-DbaConfigValue -Name 'Path.TagCache' -Fallback "$(Resolve-Path $PSScriptRoot\..)\dbatools-index.json"
                $dest = "$moduleDirectory\bin\dbatools-index.json"
                $helpcoll | ConvertTo-Json | Out-File $dest -Encoding UTF8
            }
        }

        $moduleDirectory = (Get-Module -Name dbatools).ModuleBase
    }
    process {
        $Pattern = $Pattern.TrimEnd("s")
        $idxFile = "$moduleDirectory\bin\dbatools-index.json"
        if (!(Test-Path $idxFile) -or $Rebuild) {
            Write-Message -Level Verbose -Message "Rebuilding index into $idxFile"
            $swRebuild = [system.diagnostics.stopwatch]::StartNew()
            Get-DbaIndex
            Write-Message -Level Verbose -Message "Rebuild done in $($swRebuild.ElapsedMilliseconds)ms"
        }
        $consolidated = Get-Content -Raw $idxFile | ConvertFrom-Json
        $result = $consolidated
        if ($Pattern.Length -gt 0) {
            $result = $result | Where-Object { $_.PsObject.Properties.Value -like "*$Pattern*" }
        }

        if ($Tag.Length -gt 0) {
            foreach ($t in $Tag) {
                $result = $result | Where-Object Tags -Contains $t
            }
        }

        if ($Author.Length -gt 0) {
            $result = $result | Where-Object Author -Like "*$Author*"
        }

        if ($MinimumVersion.Length -gt 0) {
            $result = $result | Where-Object MinimumVersion -GE $MinimumVersion
        }

        if ($MaximumVersion.Length -gt 0) {
            $result = $result | Where-Object MaximumVersion -LE $MaximumVersion
        }

        Select-DefaultView -InputObject $result -Property CommandName, Synopsis
    }
}
#ValidationTags#Messaging#
function Find-DbaDatabase {
    <#
        .SYNOPSIS
            Find database/s on multiple servers that match criteria you input

        .DESCRIPTION
            Allows you to search SQL Server instances for database that have either the same name, owner or service broker guid.

            There a several reasons for the service broker guid not matching on a restored database primarily using alter database new broker. or turn off broker to return a guid of 0000-0000-0000-0000.

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to.

        .PARAMETER SqlCredential
            Credential object used to connect to the SQL Server as a different user

        .PARAMETER Property
            What you would like to search on. Either Database Name, Owner, or Service Broker GUID. Database name is the default.

        .PARAMETER Pattern
            Value that is searched for. This is a regular expression match but you can just use a plain ol string like 'dbareports'

        .PARAMETER Exact
            Search for an exact match instead of a pattern

        .PARAMETER Detailed
            Output all properties, will be depreciated in 1.0.0 release.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database
            Author: Stephen Bennett: https://sqlnotesfromtheunderground.wordpress.com/

            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Find-DbaDatabase

        .EXAMPLE
            Find-DbaDatabase -SqlInstance "DEV01", "DEV02", "UAT01", "UAT02", "PROD01", "PROD02" -Pattern Report

            Returns all database from the SqlInstances that have a database with Report in the name

        .EXAMPLE
            Find-DbaDatabase -SqlInstance "DEV01", "DEV02", "UAT01", "UAT02", "PROD01", "PROD02" -Pattern TestDB -Exact | Select-Object *

            Returns all database from the SqlInstances that have a database named TestDB with a detailed output.

        .EXAMPLE
            Find-DbaDatabase -SqlInstance "DEV01", "DEV02", "UAT01", "UAT02", "PROD01", "PROD02" -Property ServiceBrokerGuid -Pattern '-faeb-495a-9898-f25a782835f5' | Select-Object *

            Returns all database from the SqlInstances that have the same Service Broker GUID with a deatiled output
    #>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [ValidateSet('Name', 'ServiceBrokerGuid', 'Owner')]
        [string]$Property = 'Name',
        [parameter(Mandatory = $true)]
        [string]$Pattern,
        [switch]$Exact,
        [switch]$Detailed,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter Detailed
    }
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($exact -eq $true) {
                $dbs = $server.Databases | Where-Object IsAccessible | Where-Object { $_.$property -eq $pattern }
            }
            else {
                try {
                    $dbs = $server.Databases | Where-Object IsAccessible | Where-Object { $_.$property.ToString() -match $pattern }
                }
                catch {
                    # they probably put asterisks thinking it's a like
                    $Pattern = $Pattern -replace '\*', ''
                    $Pattern = $Pattern -replace '\%', ''
                    $dbs = $server.Databases | Where-Object { $_.$property.ToString() -match $pattern }
                }
            }

            foreach ($db in $dbs) {

                $extendedproperties = @()
                foreach ($xp in $db.ExtendedProperties) {
                    $extendedproperties += [PSCustomObject]@{
                        Name  = $db.ExtendedProperties[$xp.Name].Name
                        Value = $db.ExtendedProperties[$xp.Name].Value
                    }
                }

                if ($extendedproperties.count -eq 0) { $extendedproperties = 0 }

                [PSCustomObject]@{
                    ComputerName       = $server.ComputerName
                    InstanceName       = $server.ServiceName
                    SqlInstance        = $server.Name
                    Name               = $db.Name
                    SizeMB             = $db.Size
                    Owner              = $db.Owner
                    CreateDate         = $db.CreateDate
                    ServiceBrokerGuid  = $db.ServiceBrokerGuid
                    Tables             = ($db.Tables | Where-Object { $_.IsSystemObject -eq $false }).Count
                    StoredProcedures   = ($db.StoredProcedures | Where-Object { $_.IsSystemObject -eq $false }).Count
                    Views              = ($db.Views | Where-Object { $_.IsSystemObject -eq $false }).Count
                    ExtendedProperties = $extendedproperties
                    Database           = $db
                } | Select-DefaultView -ExcludeProperty Database, ExtendedProperties, ServiceBrokerGuid, StoredProcedures, Tables, Views
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Find-DbaDbGrowthEvent {
    <#
        .SYNOPSIS
            Finds any database AutoGrow events in the Default Trace.

        .DESCRIPTION
            Finds any database AutoGrow events in the Default Trace.

            The following events are included:
                92 - Data File Auto Grow
                93 - Log File Auto Grow
                94 - Data File Auto Shrink
                95 - Log File Auto Shrink

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto-populated from the server

        .PARAMETER EventType
            Provide a filter on growth event type to filter the results.

            Allowed values: Growth, Shrink

        .PARAMETER FileType
            Provide a filter on file type to filter the results.

            Allowed vaules: Data, Log

        .PARAMETER UseLocalTime
            Return the local time of the instance instead of converting to UTC.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: AutoGrow,Growth,Database
            Author: Aaron Nelson
            Query Extracted from SQL Server Management Studio (SSMS) 2016.

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Find-DbaDatabaseGrowthEvent

        .EXAMPLE
            Find-DbaDatabaseGrowthEvent -SqlInstance localhost

            Returns any database AutoGrow events in the Default Trace with UTC time for the instance for every database on the localhost instance.

        .EXAMPLE
            Find-DbaDatabaseGrowthEvent -SqlInstance localhost -UseLocalTime

            Returns any database AutoGrow events in the Default Trace with the local time of the instance for every database on the localhost instance.

        .EXAMPLE
            Find-DbaDatabaseGrowthEvent -SqlInstance ServerA\SQL2016, ServerA\SQL2014

            Returns any database AutoGrow events in the Default Traces for every database on ServerA\sql2016 & ServerA\SQL2014.

        .EXAMPLE
            Find-DbaDatabaseGrowthEvent -SqlInstance ServerA\SQL2016 | Format-Table -AutoSize -Wrap

            Returns any database AutoGrow events in the Default Trace for every database on the ServerA\SQL2016 instance in a table format.

        .EXAMPLE
            Find-DbaDatabaseGrowthEvent -SqlInstance ServerA\SQL2016 -EventType Shrink

            Returns any database Auto Shrink events in the Default Trace for every database on the ServerA\SQL2016 instance.

        .EXAMPLE
            Find-DbaDatabaseGrowthEvent -SqlInstance ServerA\SQL2016 -EventType Growth -FileType Data

            Returns any database Auto Growth events on data files in the Default Trace for every database on the ServerA\SQL2016 instance.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [ValidateSet('Growth', 'Shrink')]
        [string]$EventType,
        [ValidateSet('Data', 'Log')]
        [string]$FileType,
        [switch]$UseLocalTime,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $eventClass = New-Object System.Collections.ArrayList
        92..95 | ForEach-Object { $null = $eventClass.Add($_) }

        if (Test-Bound 'EventType', 'FileType') {
            switch ($FileType) {
                'Data' {
                    <# should only contain events for data: 92 (grow), 94 (shrink) #>
                    $eventClass.Remove(93)
                    $eventClass.Remove(95)
                }
                'Log' {
                    <# should only contain events for log: 93 (grow), 95 (shrink) #>
                    $eventClass.Remove(92)
                    $eventClass.Remove(94)
                }
            }
            switch ($EventType) {
                'Growth' {
                    <# should only contain events for growth: 92 (data), 93 (log) #>
                    $eventClass.Remove(94)
                    $eventClass.Remove(95)
                }
                'Shrink' {
                    <# should only contain events for shrink: 94 (data), 95 (log) #>
                    $eventClass.Remove(92)
                    $eventClass.Remove(93)
                }
            }
        }

        $eventClassFilter = $eventClass -join ","

        $sql = "
            BEGIN TRY
                IF (SELECT CONVERT(INT,[value_in_use]) FROM sys.configurations WHERE [name] = 'default trace enabled' ) = 1
                    BEGIN
                        DECLARE @curr_tracefilename VARCHAR(500);
                        DECLARE @base_tracefilename VARCHAR(500);
                        DECLARE @indx INT;

                        SELECT @curr_tracefilename = [path]
                        FROM sys.traces
                        WHERE is_default = 1 ;

                        SET @curr_tracefilename = REVERSE(@curr_tracefilename);
                        SELECT @indx  = PATINDEX('%\%', @curr_tracefilename);
                        SET @curr_tracefilename = REVERSE(@curr_tracefilename);
                        SET @base_tracefilename = LEFT( @curr_tracefilename,LEN(@curr_tracefilename) - @indx) + '\log.trc';

                        SELECT
                            SERVERPROPERTY('MachineName') AS ComputerName,
                            ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
                            SERVERPROPERTY('ServerName') AS SqlInstance,
                            CONVERT(INT,(DENSE_RANK() OVER (ORDER BY [StartTime] DESC))%2) AS OrderRank,
                                CONVERT(INT, [EventClass]) AS EventClass,
                            [DatabaseName],
                            [Filename],
                            CONVERT(INT,(Duration/1000)) AS Duration,
                            $(if (-not $UseLocalTime) { "
                            DATEADD (MINUTE, DATEDIFF(MINUTE, GETDATE(), GETUTCDATE()), [StartTime]) AS StartTime,  -- Convert to UTC time
                            DATEADD (MINUTE, DATEDIFF(MINUTE, GETDATE(), GETUTCDATE()), [EndTime]) AS EndTime,  -- Convert to UTC time"
                            }
                            else { "
                            [StartTime] AS StartTime,
                            [EndTime] AS EndTime,"
                            })
                            ([IntegerData]*8.0/1024) AS ChangeInSize,
                            ApplicationName,
                            HostName,
                            SessionLoginName,
                            SPID
                        FROM::fn_trace_gettable( @base_tracefilename, DEFAULT )
                        WHERE
                            [EventClass] IN ($eventClassFilter)
                            AND [ServerName] = @@SERVERNAME
                            AND [DatabaseName] IN (_DatabaseList_)
                        ORDER BY [StartTime] DESC;
                    END
                ELSE
                    SELECT
                        SERVERPROPERTY('MachineName') AS ComputerName,
                        ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
                        SERVERPROPERTY('ServerName') AS SqlInstance,
                        -100 AS [OrderRank],
                        -1 AS [OrderRank],
                        0 AS [EventClass],
                        0 [DatabaseName],
                        0 AS [Filename],
                        0 AS [Duration],
                        0 AS [StartTime],
                        0 AS [EndTime],
                        0 AS ChangeInSize,
                        0 AS [ApplicationName],
                        0 AS [HostName],
                        0 AS [SessionLoginName],
                        0 AS [SPID]
            END	TRY
            BEGIN CATCH
                SELECT
                    SERVERPROPERTY('MachineName') AS ComputerName,
                    ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
                    SERVERPROPERTY('ServerName') AS SqlInstance,
                    -100 AS [OrderRank],
                    -100 AS [OrderRank],
                    ERROR_NUMBER() AS [EventClass],
                    ERROR_SEVERITY() AS [DatabaseName],
                    ERROR_STATE() AS [Filename],
                    ERROR_MESSAGE() AS [Duration],
                    1 AS [StartTime],
                    1 AS [EndTime],
                    1 AS [ChangeInSize],
                    1 AS [ApplicationName],
                    1 AS [HostName],
                    1 AS [SessionLoginName],
                    1 AS [SPID]
            END CATCH"

        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Find-DbaDatabaseGrowthEvent
    }
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $dbs = $server.Databases

            if ($Database) {
                $dbs = $dbs | Where-Object Name -In $Database
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            #Create dblist name in 'bd1', 'db2' format
            $dbsList = "'$($($dbs | ForEach-Object {$_.Name}) -join "','")'"
            Write-Message -Level Verbose -Message "Executing query against $dbsList on $instance"

            $sql = $sql -replace '_DatabaseList_', $dbsList
            Write-Message -Level Debug -Message "Executing SQL Statement:`n $sql"

            $defaults = 'ComputerName', 'InstanceName', 'SqlInstance', 'EventClass', 'DatabaseName', 'Filename', 'Duration', 'StartTime', 'EndTime', 'ChangeInSize', 'ApplicationName', 'HostName'

            try {
                Select-DefaultView -InputObject $server.Query($sql) -Property $defaults
            }
            catch {
                Stop-Function -Message "Issue collecting data on $server" -Target $server -ErrorRecord $_ -Exception $_.Exception.InnerException.InnerException.InnerException -Continue
            }
        }
    }
}
function Find-DbaDisabledIndex {
    <#
        .SYNOPSIS
            Find Disabled indexes

        .DESCRIPTION
            This command will help you to find disabled indexes on a database or a list of databases.

        .PARAMETER SqlInstance
            The SQL Server you want to check for disabled indexes.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies the database(s) to exclude from processing. Options for this list are auto-populated from the server.

        .PARAMETER NoClobber
            If this switch is enabled, the output file will not be overwritten.

        .PARAMETER Append
            If this switch is enabled, content will be appended to the output file.

            .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Index
            Author: Jason Squires, sqlnotnull.com

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Find-DbadisabledIndex

        .EXAMPLE
            Find-DbadisabledIndex -SqlInstance sql2005

            Generates the SQL statements to drop the selected disabled indexes on server "sql2005".

        .EXAMPLE
            Find-DbadisabledIndex -SqlInstance sqlserver2016 -SqlCredential $cred

            Generates the SQL statements to drop the selected disabled indexes on server "sqlserver2016", using SQL Authentication to connect to the database.

        .EXAMPLE
            Find-DbadisabledIndex -SqlInstance sqlserver2016 -Database db1, db2

            Generates the SQL Statement to drop selected indexes in databases db1 & db2 on server "sqlserver2016".

        .EXAMPLE
            Find-DbadisabledIndex -SqlInstance sqlserver2016

            Generates the SQL statements to drop selected indexes on all user databases.

    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]
        $SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$NoClobber,
        [switch]$Append,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $sql = "
        SELECT DB_NAME() AS 'DatabaseName'
        ,s.name AS 'SchemaName'
        ,t.name AS 'TableName'
        ,i.object_id AS ObjectId
        ,i.name AS 'IndexName'
        ,i.index_id as 'IndexId'
        ,i.type_desc as 'TypeDesc'
        FROM sys.tables t
        JOIN sys.schemas s
            ON t.schema_id = s.schema_id
        JOIN sys.indexes i
            ON i.object_id = t.object_id
        WHERE i.is_disabled = 1"
    }
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential  -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($Database) {
                $databases = $server.Databases | Where-Object Name -in $database
            }
            else {
                $databases = $server.Databases | Where-Object IsAccessible -eq $true
            }

            if ($databases.Count -gt 0) {
                foreach ($db in $databases.name) {

                    if ($ExcludeDatabase -contains $db -or $null -eq $server.Databases[$db]) {
                        continue
                    }

                    try {
                        if ($PSCmdlet.ShouldProcess($db, "Getting disabled indexes")) {
                            Write-Message -Level Verbose -Message "Getting indexes from database '$db'."
                            Write-Message -Level Debug -Message "SQL Statement: $sql"
                            $disabledIndex = $server.Databases[$db].ExecuteWithResults($sql)

                            if ($disabledIndex.Tables[0].Rows.Count -gt 0) {
                                $results = $disabledIndex.Tables[0];
                                if ($results.Count -gt 0 -or !([string]::IsNullOrEmpty($results))) {
                                    foreach ($index in $results) {
                                        $index
                                    }
                                }
                            }
                            else {
                                Write-Message -Level Verbose -Message "No Disabled indexes found!"
                            }
                        }
                    }
                    catch {
                        Stop-Function -Message "Issue gathering indexes" -Category InvalidOperation -InnerErrorRecord $_ -Target $db
                    }
                }
            }
            else {
                Write-Message -Level Verbose -Message "There are no databases to analyse."
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-SqlDisabledIndex
    }
}
function Find-DbaDuplicateIndex {
    <#
        .SYNOPSIS
            Find duplicate and overlapping indexes.

        .DESCRIPTION
            This command will help you to find duplicate and overlapping indexes on a database or a list of databases.

            On SQL Server 2008 and higher, the IsFiltered property will also be checked

            Also tells how much space you can save by dropping the index.

            We show the type of compression so you can make a more considered decision.

            For now only supports CLUSTERED and NONCLUSTERED indexes.

            You can select the indexes you want to drop on the gridview and when clicking OK, the DROP statement will be generated.

            Output:
                TableName
                IndexName
                KeyColumns
                IncludedColumns
                IndexSizeMB
                IndexType
                CompressionDescription (When 2008+)
                [RowCount]
                IsDisabled
                IsFiltered (When 2008+)

        .PARAMETER SqlInstance
            The SQL Server you want to check for duplicate indexes.

        .PARAMETER SqlCredential
             Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER IncludeOverlapping
            If this switch is enabled, indexes which are partially duplicated will be returned.

            Example: If the first key column is the same between two indexes, but one has included columns and the other not, this will be shown.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER Force
            If this switch is enabled, the DROP statement(s) will be executed instead of being written to the output file.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Index
            Author: Claudio Silva (@ClaudioESSilva)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Find-DbaDuplicateIndex

        .EXAMPLE
            Find-DbaDuplicateIndex -SqlInstance sql2005 | Out-File -FilePath C:\temp\sql2005-DuplicateIndexes.sql

            Generates SQL statements to drop the selected duplicate indexes in server "sql2005" and writes them to the file "C:\temp\sql2005-DuplicateIndexes.sql"

        .EXAMPLE
            Find-DbaDuplicateIndex -SqlInstance sql2005 | Out-File -FilePath C:\temp\sql2005-DuplicateIndexes.sql -Append

            Generates SQL statements to drop the selected duplicate indexes and writes/appends them to the file "C:\temp\sql2005-DuplicateIndexes.sql"

        .EXAMPLE
            Find-DbaDuplicateIndex -SqlInstance sqlserver2014a -SqlCredential $cred

            Finds exact duplicate indexes on all user databases present on sqlserver2014a, using SQL authentication.

        .EXAMPLE
            Find-DbaDuplicateIndex -SqlInstance sqlserver2014a -Database db1, db2

            Finds exact duplicate indexes on the db1 and db2 databases.

        .EXAMPLE
            Find-DbaDuplicateIndex -SqlInstance sqlserver2014a -IncludeOverlapping

            Finds both duplicate and overlapping indexes on all user databases.

    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [switch]$IncludeOverlapping,
        [switch]$EnableException
    )

    begin {
        $exactDuplicateQuery2005 = "
            WITH CTE_IndexCols
            AS (
                SELECT i.[object_id]
                    ,i.index_id
                    ,OBJECT_SCHEMA_NAME(i.[object_id]) AS SchemaName
                    ,OBJECT_NAME(i.[object_id]) AS TableName
                    ,NAME AS IndexName
                    ,ISNULL(STUFF((
                                SELECT ', ' + col.NAME + ' ' + CASE
                                        WHEN idxCol.is_descending_key = 1
                                            THEN 'DESC'
                                        ELSE 'ASC'
                                        END -- Include column order (ASC / DESC)
                                FROM sys.index_columns idxCol
                                INNER JOIN sys.columns col ON idxCol.[object_id] = col.[object_id]
                                    AND idxCol.column_id = col.column_id
                                WHERE i.[object_id] = idxCol.[object_id]
                                    AND i.index_id = idxCol.index_id
                                    AND idxCol.is_included_column = 0
                                ORDER BY idxCol.key_ordinal
                                FOR XML PATH('')
                                ), 1, 2, ''), '') AS KeyColumns
                    ,ISNULL(STUFF((
                                SELECT ', ' + col.NAME + ' ' + CASE
                                        WHEN idxCol.is_descending_key = 1
                                            THEN 'DESC'
                                        ELSE 'ASC'
                                        END -- Include column order (ASC / DESC)
                                FROM sys.index_columns idxCol
                                INNER JOIN sys.columns col ON idxCol.[object_id] = col.[object_id]
                                    AND idxCol.column_id = col.column_id
                                WHERE i.[object_id] = idxCol.[object_id]
                                    AND i.index_id = idxCol.index_id
                                    AND idxCol.is_included_column = 1
                                ORDER BY idxCol.key_ordinal
                                FOR XML PATH('')
                                ), 1, 2, ''), '') AS IncludedColumns
                    ,i.[type_desc] AS IndexType
                    ,i.is_disabled AS IsDisabled
                FROM sys.indexes AS i
                WHERE i.index_id > 0 -- Exclude HEAPS
                    AND i.[type_desc] IN (
                        'CLUSTERED'
                        ,'NONCLUSTERED'
                        )
                    AND OBJECT_SCHEMA_NAME(i.[object_id]) <> 'sys'
                )
                ,CTE_IndexSpace
            AS (
                SELECT s.[object_id]
                    ,s.index_id
                    ,SUM(s.[used_page_count]) * 8 / 1024.0 AS IndexSizeMB
                    ,SUM(p.[rows]) AS [RowCount]
                FROM sys.dm_db_partition_stats AS s
                INNER JOIN sys.partitions p WITH (NOLOCK) ON s.[partition_id] = p.[partition_id]
                    AND s.[object_id] = p.[object_id]
                    AND s.index_id = p.index_id
                WHERE s.index_id > 0 -- Exclude HEAPS
                    AND OBJECT_SCHEMA_NAME(s.[object_id]) <> 'sys'
                GROUP BY s.[object_id]
                    ,s.index_id
                )
            SELECT DB_NAME() AS DatabaseName
                ,CI1.SchemaName + '.' + CI1.TableName AS 'TableName'
                ,CI1.IndexName
                ,CI1.KeyColumns
                ,CI1.IncludedColumns
                ,CI1.IndexType
                ,CSPC.IndexSizeMB
                ,CSPC.[RowCount]
                ,CI1.IsDisabled
            FROM CTE_IndexCols AS CI1
            INNER JOIN CTE_IndexSpace AS CSPC ON CI1.[object_id] = CSPC.[object_id]
                AND CI1.index_id = CSPC.index_id
            WHERE EXISTS (
                    SELECT 1
                    FROM CTE_IndexCols CI2
                    WHERE CI1.SchemaName = CI2.SchemaName
                        AND CI1.TableName = CI2.TableName
                        AND CI1.KeyColumns = CI2.KeyColumns
                        AND CI1.IncludedColumns = CI2.IncludedColumns
                        AND CI1.IndexName <> CI2.IndexName
                    )"

        $overlappingQuery2005 = "
            WITH CTE_IndexCols
            AS (
                SELECT i.[object_id]
                    ,i.index_id
                    ,OBJECT_SCHEMA_NAME(i.[object_id]) AS SchemaName
                    ,OBJECT_NAME(i.[object_id]) AS TableName
                    ,NAME AS IndexName
                    ,ISNULL(STUFF((
                                SELECT ', ' + col.NAME + ' ' + CASE
                                        WHEN idxCol.is_descending_key = 1
                                            THEN 'DESC'
                                        ELSE 'ASC'
                                        END -- Include column order (ASC / DESC)
                                FROM sys.index_columns idxCol
                                INNER JOIN sys.columns col ON idxCol.[object_id] = col.[object_id]
                                    AND idxCol.column_id = col.column_id
                                WHERE i.[object_id] = idxCol.[object_id]
                                    AND i.index_id = idxCol.index_id
                                    AND idxCol.is_included_column = 0
                                ORDER BY idxCol.key_ordinal
                                FOR XML PATH('')
                                ), 1, 2, ''), '') AS KeyColumns
                    ,ISNULL(STUFF((
                                SELECT ', ' + col.NAME + ' ' + CASE
                                        WHEN idxCol.is_descending_key = 1
                                            THEN 'DESC'
                                        ELSE 'ASC'
                                        END -- Include column order (ASC / DESC)
                                FROM sys.index_columns idxCol
                                INNER JOIN sys.columns col ON idxCol.[object_id] = col.[object_id]
                                    AND idxCol.column_id = col.column_id
                                WHERE i.[object_id] = idxCol.[object_id]
                                    AND i.index_id = idxCol.index_id
                                    AND idxCol.is_included_column = 1
                                ORDER BY idxCol.key_ordinal
                                FOR XML PATH('')
                                ), 1, 2, ''), '') AS IncludedColumns
                    ,i.[type_desc] AS IndexType
                    ,i.is_disabled AS IsDisabled
                FROM sys.indexes AS i
                WHERE i.index_id > 0 -- Exclude HEAPS
                    AND i.[type_desc] IN (
                        'CLUSTERED'
                        ,'NONCLUSTERED'
                        )
                    AND OBJECT_SCHEMA_NAME(i.[object_id]) <> 'sys'
                )
                ,CTE_IndexSpace
            AS (
                SELECT s.[object_id]
                    ,s.index_id
                    ,SUM(s.[used_page_count]) * 8 / 1024.0 AS IndexSizeMB
                    ,SUM(p.[rows]) AS [RowCount]
                FROM sys.dm_db_partition_stats AS s
                INNER JOIN sys.partitions p WITH (NOLOCK) ON s.[partition_id] = p.[partition_id]
                    AND s.[object_id] = p.[object_id]
                    AND s.index_id = p.index_id
                WHERE s.index_id > 0 -- Exclude HEAPS
                    AND OBJECT_SCHEMA_NAME(s.[object_id]) <> 'sys'
                GROUP BY s.[object_id]
                    ,s.index_id
                )
            SELECT DB_NAME() AS DatabaseName
                ,CI1.SchemaName + '.' + CI1.TableName AS 'TableName'
                ,CI1.IndexName
                ,CI1.KeyColumns
                ,CI1.IncludedColumns
                ,CI1.IndexType
                ,CSPC.IndexSizeMB
                ,CSPC.[RowCount]
                ,CI1.IsDisabled
            FROM CTE_IndexCols AS CI1
            INNER JOIN CTE_IndexSpace AS CSPC ON CI1.[object_id] = CSPC.[object_id]
                AND CI1.index_id = CSPC.index_id
            WHERE EXISTS (
                    SELECT 1
                    FROM CTE_IndexCols CI2
                    WHERE CI1.SchemaName = CI2.SchemaName
                        AND CI1.TableName = CI2.TableName
                        AND (
                            (
                                CI1.KeyColumns LIKE CI2.KeyColumns + '%'
                                AND SUBSTRING(CI1.KeyColumns, LEN(CI2.KeyColumns) + 1, 1) = ' '
                                )
                            OR (
                                CI2.KeyColumns LIKE CI1.KeyColumns + '%'
                                AND SUBSTRING(CI2.KeyColumns, LEN(CI1.KeyColumns) + 1, 1) = ' '
                                )
                            )
                        AND CI1.IndexName <> CI2.IndexName
                    )"

        # Support Compression 2008+
        $exactDuplicateQuery = "
            WITH CTE_IndexCols
            AS (
                SELECT i.[object_id]
                    ,i.index_id
                    ,OBJECT_SCHEMA_NAME(i.[object_id]) AS SchemaName
                    ,OBJECT_NAME(i.[object_id]) AS TableName
                    ,NAME AS IndexName
                    ,ISNULL(STUFF((
                                SELECT ', ' + col.NAME + ' ' + CASE
                                        WHEN idxCol.is_descending_key = 1
                                            THEN 'DESC'
                                        ELSE 'ASC'
                                        END -- Include column order (ASC / DESC)
                                FROM sys.index_columns idxCol
                                INNER JOIN sys.columns col ON idxCol.[object_id] = col.[object_id]
                                    AND idxCol.column_id = col.column_id
                                WHERE i.[object_id] = idxCol.[object_id]
                                    AND i.index_id = idxCol.index_id
                                    AND idxCol.is_included_column = 0
                                ORDER BY idxCol.key_ordinal
                                FOR XML PATH('')
                                ), 1, 2, ''), '') AS KeyColumns
                    ,ISNULL(STUFF((
                                SELECT ', ' + col.NAME + ' ' + CASE
                                        WHEN idxCol.is_descending_key = 1
                                            THEN 'DESC'
                                        ELSE 'ASC'
                                        END -- Include column order (ASC / DESC)
                                FROM sys.index_columns idxCol
                                INNER JOIN sys.columns col ON idxCol.[object_id] = col.[object_id]
                                    AND idxCol.column_id = col.column_id
                                WHERE i.[object_id] = idxCol.[object_id]
                                    AND i.index_id = idxCol.index_id
                                    AND idxCol.is_included_column = 1
                                ORDER BY idxCol.key_ordinal
                                FOR XML PATH('')
                                ), 1, 2, ''), '') AS IncludedColumns
                    ,i.[type_desc] AS IndexType
                    ,i.is_disabled AS IsDisabled
                    ,i.has_filter AS IsFiltered
                FROM sys.indexes AS i
                WHERE i.index_id > 0 -- Exclude HEAPS
                    AND i.[type_desc] IN (
                        'CLUSTERED'
                        ,'NONCLUSTERED'
                        )
                    AND OBJECT_SCHEMA_NAME(i.[object_id]) <> 'sys'
                )
                ,CTE_IndexSpace
            AS (
                SELECT s.[object_id]
                    ,s.index_id
                    ,SUM(s.[used_page_count]) * 8 / 1024.0 AS IndexSizeMB
                    ,SUM(p.[rows]) AS [RowCount]
                    ,p.data_compression_desc AS CompressionDescription
                FROM sys.dm_db_partition_stats AS s
                INNER JOIN sys.partitions p WITH (NOLOCK) ON s.[partition_id] = p.[partition_id]
                    AND s.[object_id] = p.[object_id]
                    AND s.index_id = p.index_id
                WHERE s.index_id > 0 -- Exclude HEAPS
                    AND OBJECT_SCHEMA_NAME(s.[object_id]) <> 'sys'
                GROUP BY s.[object_id]
                    ,s.index_id
                    ,p.data_compression_desc
                )
            SELECT DB_NAME() AS DatabaseName
                ,CI1.SchemaName + '.' + CI1.TableName AS 'TableName'
                ,CI1.IndexName
                ,CI1.KeyColumns
                ,CI1.IncludedColumns
                ,CI1.IndexType
                ,CSPC.IndexSizeMB
                ,CSPC.CompressionDescription
                ,CSPC.[RowCount]
                ,CI1.IsDisabled
                ,CI1.IsFiltered
            FROM CTE_IndexCols AS CI1
            INNER JOIN CTE_IndexSpace AS CSPC ON CI1.[object_id] = CSPC.[object_id]
                AND CI1.index_id = CSPC.index_id
            WHERE EXISTS (
                    SELECT 1
                    FROM CTE_IndexCols CI2
                    WHERE CI1.SchemaName = CI2.SchemaName
                        AND CI1.TableName = CI2.TableName
                        AND CI1.KeyColumns = CI2.KeyColumns
                        AND CI1.IncludedColumns = CI2.IncludedColumns
                        AND CI1.IsFiltered = CI2.IsFiltered
                        AND CI1.IndexName <> CI2.IndexName
                    )"

        $overlappingQuery = "
            WITH CTE_IndexCols AS
            (
                SELECT
                        i.[object_id]
                        ,i.index_id
                        ,OBJECT_SCHEMA_NAME(i.[object_id]) AS SchemaName
                        ,OBJECT_NAME(i.[object_id]) AS TableName
                        ,Name AS IndexName
                        ,ISNULL(STUFF((SELECT ', ' + col.NAME + ' ' + CASE
                                                                    WHEN idxCol.is_descending_key = 1 THEN 'DESC'
                                                                    ELSE 'ASC'
                                                                END -- Include column order (ASC / DESC)
                                FROM sys.index_columns idxCol
                                    INNER JOIN sys.columns col
                                    ON idxCol.[object_id] = col.[object_id]
                                    AND idxCol.column_id = col.column_id
                                WHERE i.[object_id] = idxCol.[object_id]
                                AND i.index_id = idxCol.index_id
                                AND idxCol.is_included_column = 0
                                ORDER BY idxCol.key_ordinal
                        FOR XML PATH('')), 1, 2, ''), '') AS KeyColumns
                        ,ISNULL(STUFF((SELECT ', ' + col.NAME + ' ' + CASE
                                                                    WHEN idxCol.is_descending_key = 1 THEN 'DESC'
                                                                    ELSE 'ASC'
                                                                END -- Include column order (ASC / DESC)
                                FROM sys.index_columns idxCol
                                    INNER JOIN sys.columns col
                                    ON idxCol.[object_id] = col.[object_id]
                                    AND idxCol.column_id = col.column_id
                                WHERE i.[object_id] = idxCol.[object_id]
                                AND i.index_id = idxCol.index_id
                                AND idxCol.is_included_column = 1
                                ORDER BY idxCol.key_ordinal
                        FOR XML PATH('')), 1, 2, ''), '') AS IncludedColumns
                        ,i.[type_desc] AS IndexType
                        ,i.is_disabled AS IsDisabled
                        ,i.has_filter AS IsFiltered
                FROM sys.indexes AS i
                WHERE i.index_id > 0 -- Exclude HEAPS
                AND i.[type_desc] IN ('CLUSTERED', 'NONCLUSTERED')
                AND OBJECT_SCHEMA_NAME(i.[object_id]) <> 'sys'
            ),
            CTE_IndexSpace AS
            (
            SELECT
                        s.[object_id]
                        ,s.index_id
                        ,SUM(s.[used_page_count]) * 8 / 1024.0 AS IndexSizeMB
                        ,SUM(p.[rows]) AS [RowCount]
                        ,p.data_compression_desc AS CompressionDescription
                FROM sys.dm_db_partition_stats AS s
                    INNER JOIN sys.partitions p WITH (NOLOCK)
                    ON s.[partition_id] = p.[partition_id]
                    AND s.[object_id] = p.[object_id]
                    AND s.index_id = p.index_id
                WHERE s.index_id > 0 -- Exclude HEAPS
                    AND OBJECT_SCHEMA_NAME(s.[object_id]) <> 'sys'
                GROUP BY s.[object_id], s.index_id, p.data_compression_desc
            )
            SELECT
                    DB_NAME() AS DatabaseName
                    ,CI1.SchemaName + '.' + CI1.TableName AS 'TableName'
                    ,CI1.IndexName
                    ,CI1.KeyColumns
                    ,CI1.IncludedColumns
                    ,CI1.IndexType
                    ,CSPC.IndexSizeMB
                    ,CSPC.CompressionDescription
                    ,CSPC.[RowCount]
                    ,CI1.IsDisabled
                    ,CI1.IsFiltered
            FROM CTE_IndexCols AS CI1
                INNER JOIN CTE_IndexSpace AS CSPC
                ON CI1.[object_id] = CSPC.[object_id]
                AND CI1.index_id = CSPC.index_id
            WHERE EXISTS (SELECT 1
                            FROM CTE_IndexCols CI2
                        WHERE CI1.SchemaName = CI2.SchemaName
                            AND CI1.TableName = CI2.TableName
                            AND (
                                        (CI1.KeyColumns like CI2.KeyColumns + '%' and SUBSTRING(CI1.KeyColumns,LEN(CI2.KeyColumns)+1,1) = ' ')
                                    OR (CI2.KeyColumns like CI1.KeyColumns + '%' and SUBSTRING(CI2.KeyColumns,LEN(CI1.KeyColumns)+1,1) = ' ')
                                )
                            AND CI1.IsFiltered = CI2.IsFiltered
                            AND CI1.IndexName <> CI2.IndexName
                        )"
    }
    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $sqlinstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($database) {
                $databases = $server.Databases | Where-Object Name -in $database
            }
            else {
                $databases = $server.Databases | Where-Object IsAccessible -eq $true
            }

            foreach ($db in $databases) {
                try {
                    Write-Message -Level Verbose -Message "Getting indexes from database '$db'."

                    $query = if ($server.versionMajor -eq 9) {
                        if ($IncludeOverlapping) { $overlappingQuery2005 }
                        else { $exactDuplicateQuery2005 }
                    }
                    else {
                        if ($IncludeOverlapping) { $overlappingQuery }
                        else { $exactDuplicateQuery }
                    }

                    $db.Query($query)

                }
                catch {
                    Stop-Function -Message "Query failure" -Target $db
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-SqlDuplicateIndex
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Find-DbaInstance {
    <#
        .SYNOPSIS
            Search for SQL Server Instances.

        .DESCRIPTION
            This function searches for SQL Server Instances.

            It supports a variety of scans for this purpose which can be separated in two categories:
            - Discovery
            - Scan

            Discovery:
            This is where it compiles a list of computers / addresses to check.
            It supports several methods of generating such lists (including Active Directory lookup or IP Ranges), but also supports specifying a list of computers to check.
            - For details on discovery, see the documentation on the '-DiscoveryType' parameter
            - For details on explicitly providing a list, see the documentation on the '-ComputerName' parameter

            Scan:
            Once a list of computers has been provided, this command will execute a variety of actions to determine any instances present for each of them.
            This is described in more detail in the documentation on the '-ScanType' parameter.
            Additional parameters allow more granular control over individual scans (e.g. Credentials to use).

            Note on logging and auditing:
            The Discovery phase is unproblematic since it is non-intrusive, however during the scan phase, all targeted computers may be accessed repeatedly.
            This may cause issues with security teams, due to many logon events and possibly failed authentication.
            This action constitutes a network scan, which may be illegal depending on the nation you are in and whether you own the network you scan.
            If you are unsure whether you may use this command in your environment, check the detailed description on the '-ScanType' parameter and contact your IT security team for advice.

        .PARAMETER ComputerName
            The computer to scan. Can be a variety of input types, including text or the output of Get-ADComputer.
            Any extra instance information (such as connection strings or live sql server connections) beyond the computername will be discarded.

        .PARAMETER DiscoveryType
            The mechanisms to be used to discover instances.
            Supports any combination of:
            - Service Principal Name lookup ('Domain'; from Active Directory)
            - SQL Instance Enumeration ('DataSourceEnumeration'; same as SSMS uses)
            - IP Address range ('IPRange'; all IP Addresses will be scanned)

            SPN Lookup:
            The function tries to connect active directory to look up all computers with registered SQL Instances.
            Not all instances need to be registered properly, making this not 100% reliable.
            By default, your nearest Domain Controller is contacted for this scan.
            However it is possible to explicitly state the DC to contact using its DistinguishedName and the '-DomainController' parameter.
            If credentials were specified using the '-Credential' parameter, those same credentials are used to perform this lookup, allowing the scan of other domains.

            SQL Instance Enumeration:
            This uses the default UDP Broadcast based instance enumeration used by SSMS to detect instances.
            Note that the result from this is not used in the actual scan, but only to compile a list of computers to scan.
            To enable the same results for the scan, ensure that the 'Browser' scan is enabled.

            IP Address range:
            This 'Discovery' uses a range of IPAddresses and simply passes them on to be tested.
            See the 'Description' part of help on security issues of network scanning.
            By default, it will enumerate all ethernet network adapters on the local computer and scan the entire subnet they are on.
            By using the '-IpAddress' parameter, custom network ranges can be specified.

        .PARAMETER Credential
            The credentials to use on windows network connection.
            These credentials are used for:
            - Contact to domain controllers for SPN lookups (only if explicit Domain Controller is specified)
            - CIM/WMI contact to the scanned computers during the scan phase (see the '-ScanType' parameter documentation on affected scans).

        .PARAMETER SqlCredential
            The credentials used to connect to SqlInstances to during the scan phase.
            See the '-ScanType' parameter documentation on affected scans.

        .PARAMETER ScanType
            The scans are the individual methods used to retrieve information about the scanned computer and any potentially installed instances.
            This parameter is optional, by default all scans except for establishing an actual SQL connection are performed.
            Scans can be specified in any arbitrary combination, however at least one instance detecting scan needs to be specified in order for data to be returned.

            Scans:
            DNSResolve
            - Tries resolving the computername in DNS
            Ping
            - Tries pinging the computer. Failure will NOT terminate scans.
            SQLService
            - Tries listing all SQL Services using CIM/WMI
            - This scan uses credentials specified in the '-Credential' parameter if any.
            - This scan detects instances.
            - Success in this scan guarantees high confidence (See parameter '-MinimumConfidence' for details).
            Browser
            - Tries discovering all instances via the browser service
            - This scan detects instances.
            TCPPort
            - Tries connecting to the TCP Ports.
            - By default, port 1433 is connected to.
            - The parameter '-TCPPort' can be used to provide a list of port numbers to scan.
            - This scan detects possible instances. Since other services might bind to a given port, this is not the most reliable test.
            - This scan is also used to validate found SPNs if both scans are used in combination
            SqlConnect
            - Tries to establish a SQL connection to the server
            - Uses windows credentials by default
            - Specify custom credentials using the '-SqlCredential' parameter
            - This scan is not used by default
            - Success in this scan guarantees high confidence (See parameter '-MinimumConfidence' for details).
            SPN
            - Tries looking up the Service Principal Names for each instance
            - Will use the nearest Domain Controller by default
            - Target a specific domain controller using the '-DomainController' parameter
            - If using the '-DomainController' parameter, use the '-Credential' parameter to specify the credentials used to connect

        .PARAMETER IpAddress
            This parameter can be used to override the defaults for the IPRange discovery.
            This parameter accepts a list of strings supporting any combination of:
            - Plain IP Addresses (e.g.: "10.1.1.1")
            - IP Address Ranges (e.g.: "10.1.1.1-10.1.1.5")
            - IP Address & Subnet Mask (e.g.: "10.1.1.1/255.255.255.0")
            - IP Address & Subnet Length: (e.g.: "10.1.1.1/24)
            Overlapping addresses will not result in duplicate scans.

        .PARAMETER DomainController
            The domain controller to contact for SPN lookups / searches.
            Uses the credentials from the '-Credential' parameter if specified.

        .PARAMETER TCPPort
            The ports to scan in the TCP Port Scan method.
            Defaults to 1433.

        .PARAMETER MinimumConfidence
            This command tries to discover instances, which isn't always a sure thing.
            Depending on the number and type of scans completed, we have different levels of confidence in our results.
            By default, we will return anything that we have at least a low confidence of being an instance.
            These are the confidence levels we support and how they are determined:
            - High: Established SQL Connection (including rejection for bad credentials) or service scan.
            - Medium: Browser reply or a combination of TCPConnect _and_ SPN test.
            - Low: Either TCPConnect _or_ SPN
            - None: Computer existence could be verified, but no sign of an SQL Instance

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Instance, Connect, SqlServer
            Author: Scott Sutherland, 2018 NetSPI

            Conversion & Refactoring by: Friedrich Weinmann
            Outside resources used and modified:
            https://gallery.technet.microsoft.com/scriptcenter/List-the-IP-addresses-in-a-60c5bb6b

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Find-DbaInstance

        .EXAMPLE
            PS C:\> Find-DbaInstance -DiscoveryType Domain,DataSourceEnumeration

            Performs a network search for SQL Instances by:
            - Looking up the Service Principal Names of computers in active directory
            - Using the UDP broadcast based auto-discovery of SSMS
            After that it will extensively scan all hosts thus discovered for instances.

        .EXAMPLE
            PS C:\> Find-DbaInstance -DiscoveryType All

            Performs a network search for SQL Instances, using all discovery protocols:
            - Active directory search for Service Principal Names
            - SQL Instance Enumeration (same as SSMS does)
            - All IPAddresses in the current computer's subnets of all connected network interfaces
            Note: This scan will take a long time, due to including the IP Scan

        .EXAMPLE
            PS C:\> Get-ADComputer -Filter "*" | Find-DbaInstance

            Scans all computers in the domain for SQL Instances, using a deep probe:
            - Tries resolving the name in DNS
            - Tries pinging the computer
            - Tries listing all SQL Services using CIM/WMI
            - Tries discovering all instances via the browser service
            - Tries connecting to the default TCP Port (1433)
            - Tries connecting to the TCP port of each discovered instance
            - Tries to establish a SQL connection to the server using default windows credentials
            - Tries looking up the Service Principal Names for each instance

        .EXAMPLE
            PS C:\> Get-Content .\servers.txt | Find-DbaInstance -SqlCredential $cred -ScanType Browser,SqlConnect

            Reads all servers from the servers.txt file (one server per line),
            then scans each of them for instances using the browser service
            and finally attempts to connect to each instance found using the specified credentials.
    #>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, ParameterSetName = 'Computer', ValueFromPipeline = $true)]
        [DbaInstance[]]$ComputerName,
        [Parameter(Mandatory = $true, ParameterSetName = 'Discover')]
        [Sqlcollaborative.Dbatools.Discovery.DbaInstanceDiscoveryType]$DiscoveryType,
        [System.Management.Automation.PSCredential]$Credential,
        [System.Management.Automation.PSCredential]$SqlCredential,
        [Sqlcollaborative.Dbatools.Discovery.DbaInstanceScanType]$ScanType = "Default",
        [Parameter(ParameterSetName = 'Discover')]
        [string[]]$IpAddress,
        [string]$DomainController,
        [int[]]$TCPPort = 1433,
        [Sqlcollaborative.Dbatools.Discovery.DbaInstanceConfidenceLevel]$MinimumConfidence = 'Low',
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        # TCPPort = 1 | SqlService = 4 | SPN = 16 | Browser = 32
        if (-not ($ScanType -band 53)) {
            Stop-Function -Message "Invalid Scan Types specified: $ScanType | Specify at least one of the following types: Browser, SqlService, SPN, TCPPort, Default or All. Otherwise no detection will be possible." -EnableException $EnableException -Category InvalidArgument
            return
        }

        #region Utility Functions
        function Test-SqlInstance {
            <#
            .SYNOPSIS
                Performs the actual scanning logic

            .DESCRIPTION
                Performs the actual scanning logic
                Each potential target is accessed using the specified scan routines.

            .PARAMETER Target
                The target to scan.

            .EXAMPLE
                PS C:\> Test-SqlInstance
        #>
            [CmdletBinding()]
            param (
                [Parameter(ValueFromPipeline = $true)][DbaInstance[]]$Target,
                [PSCredential]$Credential,
                [PSCredential]$SqlCredential,
                [Sqlcollaborative.Dbatools.Discovery.DbaInstanceScanType]$ScanType,
                [string]$DomainController,
                [int[]]$TCPPort = 1433,
                [Sqlcollaborative.Dbatools.Discovery.DbaInstanceConfidenceLevel]$MinimumConfidence,
                [switch]$EnableException
            )

            begin {
                [System.Collections.ArrayList]$computersScanned = @()
            }

            process {
                foreach ($computer in $Target) {
                    if ($computersScanned.Contains($computer.ComputerName)) {
                        continue
                    }
                    else {
                        $null = $computersScanned.Add($computer.ComputerName)
                    }
                    Write-Message -Level Verbose -Message "Processing: $($computer)" -Target $computer -FunctionName Find-DbaInstance

                    #region Null variables to prevent scope lookup on conditional existence
                    $resolution = $null
                    $pingReply = $null
                    $sPNs = @()
                    $ports = @()
                    $browseResult = $null
                    $services = @()
                    $serverObject = $null
                    $browseFailed = $false
                    #endregion Null variables to prevent scope lookup on conditional existence

                    #region Gather data
                    if ($ScanType -band [Sqlcollaborative.Dbatools.Discovery.DbaInstanceScanType]::DNSResolve) {
                        try { $resolution = [System.Net.Dns]::GetHostEntry($computer.ComputerName) }
                        catch { }
                    }

                    if ($ScanType -band [Sqlcollaborative.Dbatools.Discovery.DbaInstanceScanType]::Ping) {
                        $ping = New-Object System.Net.NetworkInformation.Ping
                        try { $pingReply = $ping.Send($computer.ComputerName) }
                        catch { }
                    }

                    if ($ScanType -band [Sqlcollaborative.Dbatools.Discovery.DbaInstanceScanType]::SPN) {
                        $computerByName = $computer.ComputerName
                        if ($resolution.HostName) { $computerByName = $resolution.HostName }
                        if ($computerByName -notmatch "$([dbargx]::IPv4)|$([dbargx]::IPv6)") {
                            try { $sPNs = Get-DomainSPN -DomainController $DomainController -Credential $Credential -ComputerName $computerByName -GetSPN }
                            catch { }
                        }
                    }

                    if ($ScanType -band [Sqlcollaborative.Dbatools.Discovery.DbaInstanceScanType]::TCPPort) {
                        $ports = $TCPPort | Test-TcpPort -ComputerName $computer
                    }

                    if ($ScanType -band [Sqlcollaborative.Dbatools.Discovery.DbaInstanceScanType]::Browser) {
                        try {
                            $browseResult = Get-SQLInstanceBrowserUDP -ComputerName $computer -EnableException
                        }
                        catch {
                            $browseFailed = $true
                        }
                    }

                    if ($ScanType -band [Sqlcollaborative.Dbatools.Discovery.DbaInstanceScanType]::SqlService) {
                        if ($Credential) { $services = Get-DbaSqlService -ComputerName $computer -Credential $Credential -EnableException -ErrorAction Ignore -WarningAction SilentlyCOntinue }
                        else { $services = Get-DbaSqlService -ComputerName $computer -ErrorAction Ignore -WarningAction SilentlyContinue }
                    }
                    #endregion Gather data

                    #region Gather list of found instance indicators
                    $instanceNames = @()
                    if ($Services) {
                        $Services | Select-Object -ExpandProperty InstanceName -Unique | Where-Object { $_ -and ($instanceNames -notcontains $_) } | ForEach-Object {
                            $instanceNames += $_
                        }
                    }
                    if ($browseResult) {
                        $browseResult | Select-Object -ExpandProperty InstanceName -Unique | Where-Object { $_ -and ($instanceNames -notcontains $_) } | ForEach-Object {
                            $instanceNames += $_
                        }
                    }

                    $portsDetected = @()
                    foreach ($portResult in $ports) {
                        if ($portResult.IsOpen) { $portsDetected += $portResult.Port }
                    }
                    foreach ($sPN in $sPNs) {
                        try { $inst = $sPN.Split(':')[1] }
                        catch { continue }

                        try {
                            [int]$portNumber = $inst
                            if ($portNumber -and ($portsDetected -notcontains $portNumber)) {
                                $portsDetected += $portNumber
                            }
                        }
                        catch {
                            if ($inst -and ($instanceNames -notcontains $inst)) {
                                $instanceNames += $inst
                            }
                        }
                    }
                    #endregion Gather list of found instance indicators

                    #region Case: Nothing found
                    if ((-not $instanceNames) -and (-not $portsDetected)) {
                        if ($resolution -or ($pingReply.Status -like "Success")) {
                            if ($MinimumConfidence -eq [Sqlcollaborative.Dbatools.Discovery.DbaInstanceConfidenceLevel]::None) {
                                New-Object Sqlcollaborative.Dbatools.Discovery.DbaInstanceReport -Property @{
                                    MachineName  = $computer.ComputerName
                                    ComputerName = $computer.ComputerName
                                    Ping         = $pingReply.Status -like 'Success'
                                }
                            }
                            else {
                                Write-Message -Level Verbose -Message "Computer $computer could be contacted, but no trace of an SQL Instance was found. Skipping..." -Target $computer -FunctionName Find-DbaInstance
                            }
                        }
                        else {
                            Write-Message -Level Verbose -Message "Computer $computer could not be contacted, skipping." -Target $computer -FunctionName Find-DbaInstance
                        }

                        continue
                    }
                    #endregion Case: Nothing found

                    [System.Collections.ArrayList]$masterList = @()

                    #region Case: Named instance found
                    foreach ($instance in $instanceNames) {
                        $object = New-Object Sqlcollaborative.Dbatools.Discovery.DbaInstanceReport
                        $object.MachineName = $computer.ComputerName
                        $object.ComputerName = $computer.ComputerName
                        $object.InstanceName = $instance
                        $object.DnsResolution = $resolution
                        $object.Ping = $pingReply.Status -like 'Success'
                        $object.ScanTypes = $ScanType
                        $object.Services = $services | Where-Object InstanceName -EQ $instance
                        $object.SystemServices = $services | Where-Object { -not $_.InstanceName }
                        $object.SPNs = $sPNs

                        if ($result = $browseResult | Where-Object InstanceName -EQ $instance) {
                            $object.BrowseReply = $result
                        }
                        if ($ports) {
                            $object.PortsScanned = $ports
                        }

                        if ($object.BrowseReply) {
                            $object.Confidence = 'Medium'
                            if ($object.BrowseReply.TCPPort) {
                                $object.Port = $object.BrowseReply.TCPPort

                                $object.PortsScanned | Where-Object Port -EQ $object.Port | ForEach-Object {
                                    $object.TcpConnected = $_.IsOpen
                                }
                            }
                        }
                        if ($object.Services) {
                            $object.Confidence = 'High'

                            $engine = $object.Services | Where-Object ServiceType -EQ "Engine"
                            switch ($engine.State) {
                                "Running" { $object.Availability = 'Available' }
                                "Stopped" { $object.Availability = 'Unavailable' }
                                default { $object.Availability = 'Unknown' }
                            }
                        }

                        $object.Timestamp = Get-Date

                        $masterList += $object
                    }
                    #endregion Case: Named instance found

                    #region Case: Port number found
                    foreach ($port in $portsDetected) {
                        if ($masterList.Port -contains $port) { continue }

                        $object = New-Object Sqlcollaborative.Dbatools.Discovery.DbaInstanceReport
                        $object.MachineName = $computer.ComputerName
                        $object.ComputerName = $computer.ComputerName
                        $object.Port = $port
                        $object.DnsResolution = $resolution
                        $object.Ping = $pingReply.Status -like 'Success'
                        $object.ScanTypes = $ScanType
                        $object.SystemServices = $services | Where-Object { -not $_.InstanceName }
                        $object.SPNs = $sPNs
                        $object.Confidence = 'Low'
                        if ($ports) {
                            $object.PortsScanned = $ports

                            if (($ports | Where-Object IsOpen).Port -eq 1433) {
                                $object.Confidence = 'Medium'
                            }
                        }

                        if (($ports.Port -contains $port) -and ($sPNs | Where-Object { $_ -like "*:$port" })) {
                            $object.Confidence = 'Medium'
                        }

                        $object.PortsScanned | Where-Object Port -EQ $object.Port | ForEach-Object {
                            $object.TcpConnected = $_.IsOpen
                        }
                        $object.Timestamp = Get-Date

                        if ($masterList.SqlInstance -contains $object.SqlInstance) {
                            continue
                        }

                        $masterList += $object
                    }
                    #endregion Case: Port number found

                    if ($ScanType -band [Sqlcollaborative.Dbatools.Discovery.DbaInstanceScanType]::SqlConnect) {
                        $instanceHash = @{ }
                        $toDelete = @()
                        foreach ($dataSet in $masterList) {
                            try {
                                $server = Connect-SqlInstance -SqlInstance $dataSet.FullSmoName -SqlCredential $SqlCredential
                                $dataSet.SqlConnected = $true
                                $dataSet.Confidence = 'High'

                                # Remove duplicates
                                if ($instanceHash.ContainsKey($server.DomainInstanceName)) {
                                    $toDelete += $dataSet
                                }
                                else {
                                    $instanceHash[$server.DomainInstanceName] = $dataSet

                                    try {
                                        $dataSet.MachineName = $server.ComputerNamePhysicalNetBIOS
                                    }
                                    catch { }
                                }
                            }
                            catch {
                                # Error class definitions
                                # https://docs.microsoft.com/en-us/sql/relational-databases/errors-events/database-engine-error-severities
                                # 24 or less means an instance was found, but had some issues

                                #region Processing error (Access denied, server error, ...)
                                if ($_.Exception.InnerException.Errors.Class -lt 25) {
                                    # There IS an SQL Instance and it listened to network traffic
                                    $dataSet.SqlConnected = $true
                                    $dataSet.Confidence = 'High'
                                }
                                #endregion Processing error (Access denied, server error, ...)

                                #region Other connection errors
                                else {
                                    $dataSet.SqlConnected = $false
                                }
                                #endregion Other connection errors
                            }
                        }

                        foreach ($item in $toDelete) {
                            $masterList.Remove($item)
                        }
                    }

                    $masterList
                }
            }
        }

        function Get-DomainSPN {
            <#
            .SYNOPSIS
                Returns all computernames with registered MSSQL SPNs.

            .DESCRIPTION
                Returns all computernames with registered MSSQL SPNs.

            .PARAMETER DomainController
                The domain controller to ask.

            .PARAMETER Credential
                The credentials to use while asking.

            .PARAMETER ComputerName
                Filter by computername

            .PARAMETER GetSPN
                Returns the service SPNs instead of the hostname

            .EXAMPLE
                PS C:\> Get-DomainSPN -DomainController $DomainController -Credential $Credential

                Returns all computernames with MSQL SPNs known to $DomainController, assuming credentials are valid.
        #>
            [CmdletBinding()]
            param (
                [string]$DomainController,
                [object]$Credential,
                [string]$ComputerName = "*",
                [switch]$GetSPN
            )

            try {
                if ($DomainController) {
                    if ($Credential) {
                        $entry = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList "LDAP://$DomainController", $Credential.UserName, $Credential.GetNetworkCredential().Password
                    }
                    else {
                        $entry = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList "LDAP://$DomainController"
                    }
                }
                else {
                    $entry = [ADSI]''
                }
                $objSearcher = New-Object -TypeName System.DirectoryServices.DirectorySearcher -ArgumentList $entry

                $objSearcher.PageSize = 200
                $objSearcher.Filter = "(&(objectcategory=computer)(servicePrincipalName=MSSQLsvc*)(|(name=$ComputerName)(dnshostname=$ComputerName)))"
                $objSearcher.SearchScope = 'Subtree'

                $results = $objSearcher.FindAll()
                foreach ($computer in $results) {
                    if ($GetSPN) {
                        $computer.Properties["serviceprincipalname"] | Where-Object { $_ -like "MSSQLsvc*:*" }
                    }
                    else {
                        if ($computer.Properties["dnshostname"]) {
                            $computer.Properties["dnshostname"][0]
                        }
                        else {
                            $computer.Properties["name"][0]
                        }
                    }
                }
            }
            catch {
                throw
            }
        }

        function Get-SQLInstanceBrowserUDP {
            <#
            .SYNOPSIS
                Requests a list of instances from the browser service.

            .DESCRIPTION
                Requests a list of instances from the browser service.

            .PARAMETER ComputerName
                Computer name or IP address to enumerate SQL Instance from.

            .PARAMETER UDPTimeOut
                Timeout in seconds. Longer timeout = more accurate.

            .PARAMETER EnableException
                By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
                This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
                Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

            .EXAMPLE
                PS C:\> Get-SQLInstanceBrowserUDP -ComputerName 'sql2017'

                Contacts the browsing service on sql2017 and requests its instance information.

            .NOTES
                Original Author: Eric Gruber
                Editors:
                - Scott Sutherland (Pipeline and timeout mods)
                - Friedrich Weinmann (Cleanup & dbatools Standardization)

        #>
            [CmdletBinding()]
            param (
                [Parameter(Mandatory = $true, ValueFromPipeline = $true)][DbaInstance[]]$ComputerName,
                [int]$UDPTimeOut = 2,
                [switch]$EnableException
            )

            process {
                foreach ($computer in $ComputerName) {
                    try {
                        #region Connect to browser service and receive response
                        $UDPClient = New-Object -TypeName System.Net.Sockets.Udpclient
                        $UDPClient.Client.ReceiveTimeout = $UDPTimeOut * 1000
                        $UDPClient.Connect($computer.ComputerName, 1434)
                        $UDPPacket = 0x03
                        $UDPEndpoint = New-Object -TypeName System.Net.IpEndPoint -ArgumentList ([System.Net.Ipaddress]::Any, 0)
                        $UDPClient.Client.Blocking = $true
                        [void]$UDPClient.Send($UDPPacket, $UDPPacket.Length)
                        $BytesRecived = $UDPClient.Receive([ref]$UDPEndpoint)
                        # Skip first three characters, since those contain trash data (SSRP metadata)
                        #$Response = [System.Text.Encoding]::ASCII.GetString($BytesRecived[3..($BytesRecived.Length - 1)])
                        $Response = [System.Text.Encoding]::ASCII.GetString($BytesRecived)
                        #endregion Connect to browser service and receive response

                        #region Parse Output
                        $Response | Select-String "(ServerName;(\w+);InstanceName;(\w+);IsClustered;(\w+);Version;(\d+\.\d+\.\d+\.\d+);(tcp;(\d+)){0,1})" -AllMatches | Select-Object -ExpandProperty Matches | ForEach-Object {
                            $obj = New-Object Sqlcollaborative.Dbatools.Discovery.DbaBrowserReply -Property @{
                                MachineName  = $computer.ComputerName
                                ComputerName = $_.Groups[2].Value
                                SqlInstance  = "$($_.Groups[2].Value)\$($_.Groups[3].Value)"
                                InstanceName = $_.Groups[3].Value
                                Version      = $_.Groups[5].Value
                                IsClustered  = "Yes" -eq $_.Groups[4].Value
                            }
                            if ($_.Groups[7].Success) {
                                $obj.TCPPort = $_.Groups[7].Value
                            }
                            $obj
                        }
                        #endregion Parse Output

                        $UDPClient.Close()
                    }
                    catch {
                        try {
                            $UDPClient.Close()
                        }
                        catch {
                        }

                        if ($EnableException) { throw }
                    }
                }
            }
        }

        function Test-TcpPort {
            <#
            .SYNOPSIS
                Tests whether a TCP Port is open or not.

            .DESCRIPTION
                Tests whether a TCP Port is open or not.

            .PARAMETER ComputerName
                The name of the computer to scan.

            .PARAMETER Port
                The port(s) to scan.

            .EXAMPLE
                PS C:\> $ports | Test-TcpPort -ComputerName "foo"

                Tests for each port in $ports whether the TCP port is open on computer "foo"
        #>
            [CmdletBinding()]
            param (
                [DbaInstance]$ComputerName,
                [Parameter(ValueFromPipeline = $true)][int[]]$Port
            )

            begin {
                $client = New-Object Net.Sockets.TcpClient
            }
            process {
                foreach ($item in $Port) {
                    try {
                        $client.Connect($ComputerName.ComputerName, $item)
                        if ($client.Connected) {
                            $client.Close()
                            New-Object -TypeName Sqlcollaborative.Dbatools.Discovery.DbaPortReport -ArgumentList $ComputerName.ComputerName, $item, $true
                        }
                        else {
                            New-Object -TypeName Sqlcollaborative.Dbatools.Discovery.DbaPortReport -ArgumentList $ComputerName.ComputerName, $item, $false
                        }
                    }
                    catch {
                        New-Object -TypeName Sqlcollaborative.Dbatools.Discovery.DbaPortReport -ArgumentList $ComputerName.ComputerName, $item, $false
                    }
                }
            }
        }

        function Get-IPrange {
            <#
            .SYNOPSIS
                Get the IP addresses in a range

            .DESCRIPTION
                A detailed description of the Get-IPrange function.

            .PARAMETER Start
                A description of the Start parameter.

            .PARAMETER End
                A description of the End parameter.

            .PARAMETER IPAddress
                A description of the IPAddress parameter.

            .PARAMETER Mask
                A description of the Mask parameter.

            .PARAMETER Cidr
                A description of the Cidr parameter.

            .EXAMPLE
                Get-IPrange -Start 192.168.8.2 -End 192.168.8.20

            .EXAMPLE
                Get-IPrange -IPAddress 192.168.8.2 -Mask 255.255.255.0

            .EXAMPLE
                Get-IPrange -IPAddress 192.168.8.3 -Cidr 24

            .NOTES
                Author: BarryCWT
                Reference: https://gallery.technet.microsoft.com/scriptcenter/List-the-IP-addresses-in-a-60c5bb6b
        #>

            param
            (
                [string]$Start,
                [string]$End,
                [string]$IPAddress,
                [string]$Mask,
                [int]$Cidr
            )

            function IP-toINT64 {
                param ($ip)

                $octets = $ip.split(".")
                return [int64]([int64]$octets[0] * 16777216 + [int64]$octets[1] * 65536 + [int64]$octets[2] * 256 + [int64]$octets[3])
            }

            function INT64-toIP {
                param ([int64]$int)

                return ([System.Net.IPAddress](([math]::truncate($int/16777216)).tostring() + "." + ([math]::truncate(($int % 16777216)/65536)).tostring() + "." + ([math]::truncate(($int % 65536)/256)).tostring() + "." + ([math]::truncate($int % 256)).tostring()))
            }

            if ($Cidr) {
                $maskaddr = [Net.IPAddress]::Parse((INT64-toIP -int ([convert]::ToInt64(("1" * $Cidr + "0" * (32 - $Cidr)), 2))))
            }
            if ($Mask) {
                $maskaddr = [Net.IPAddress]::Parse($Mask)
            }
            if ($IPAddress) {
                $ipaddr = [Net.IPAddress]::Parse($IPAddress)
                $networkaddr = new-object net.ipaddress ($maskaddr.address -band $ipaddr.address)
                $broadcastaddr = new-object net.ipaddress (([system.net.ipaddress]::parse("255.255.255.255").address -bxor $maskaddr.address -bor $networkaddr.address))
                $startaddr = IP-toINT64 -ip $networkaddr.ipaddresstostring
                $endaddr = IP-toINT64 -ip $broadcastaddr.ipaddresstostring
            }
            else {
                $startaddr = IP-toINT64 -ip $Start
                $endaddr = IP-toINT64 -ip $End
            }

            for ($i = $startaddr; $i -le $endaddr; $i++) {
                INT64-toIP -int $i
            }
        }

        function Resolve-IPRange {
            <#
            .SYNOPSIS
                Returns a number of IPAddresses based on range specified.

            .DESCRIPTION
                Returns a number of IPAddresses based on range specified.
                Warning: A too large range can lead to memory exceptions.

                Scans subnet of active computer if no address is specified.

            .PARAMETER IpAddress
                The address / range / mask / cidr to scan. Example input:
                - 10.1.1.1
                - 10.1.1.1/24
                - 10.1.1.1-10.1.1.254
                - 10.1.1.1/255.255.255.0
        #>
            [CmdletBinding()]
            param (
                [AllowEmptyString()][string]$IpAddress
            )

            #region Scan defined range
            if ($IpAddress) {
                #region Determine processing mode
                $mode = 'Unknown'
                if ($IpAddress -like "*/*") {
                    $parts = $IpAddress.Split("/")

                    $address = $parts[0]
                    if ($parts[1] -match ([dbargx]::IPv4)) {
                        $mask = $parts[1]
                        $mode = 'Mask'
                    }
                    elseif ($parts[1] -as [int]) {
                        $cidr = [int]$parts[1]

                        if (($cidr -lt 8) -or ($cidr -gt 31)) {
                            throw "$IpAddress does not contain a valid cidr mask!"
                        }

                        $mode = 'CIDR'
                    }
                    else {
                        throw "$IpAddress is not a valid IP Range!"
                    }
                }
                elseif ($IpAddress -like "*-*") {
                    $rangeStart = $IpAddress.Split("-")[0]
                    $rangeEnd = $IpAddress.Split("-")[1]

                    if ($rangeStart -notmatch ([dbargx]::IPv4)) {
                        throw "$IpAddress is not a valid IP Range!"
                    }
                    if ($rangeEnd -notmatch ([dbargx]::IPv4)) {
                        throw "$IpAddress is not a valid IP Range!"
                    }

                    $mode = 'Range'
                }
                else {
                    if ($IpAddress -notmatch ([dbargx]::IPv4)) {
                        throw "$IpAddress is not a valid IP Address!"
                    }
                    return $IpAddress
                }
                #endregion Determine processing mode

                switch ($mode) {
                    'CIDR' {
                        Get-IPrange -IPAddress $address -Cidr $cidr
                    }
                    'Mask' {
                        Get-IPrange -IPAddress $address -Mask $mask
                    }
                    'Range' {
                        Get-IPrange -Start $rangeStart -End $rangeEnd
                    }
                }
            }
            #endregion Scan defined range

            #region Scan own computer range
            else {
                foreach ($interface in ([System.Net.NetworkInformation.NetworkInterface]::GetAllNetworkInterfaces() | Where-Object NetworkInterfaceType -Like '*Ethernet*')) {
                    foreach ($property in ($interface.GetIPProperties().UnicastAddresses | Where-Object { $_.Address.AddressFamily -like "InterNetwork" })) {
                        Get-IPrange -IPAddress $property.Address -Cidr $property.PrefixLength
                    }
                }
            }
            #endregion Scan own computer range
        }
        #endregion Utility Functions

        #region Build parameter Splat for scan
        $paramTestSqlInstance = @{
            ScanType          = $ScanType
            TCPPort           = $TCPPort
            EnableException   = $EnableException
            MinimumConfidence = $MinimumConfidence
        }

        # Only specify when passed by user to avoid credential prompts on PS3/4
        if ($SqlCredential) {
            $paramTestSqlInstance["SqlCredential"] = $SqlCredential
        }
        if ($Credential) {
            $paramTestSqlInstance["Credential"] = $Credential
        }
        if ($DomainController) {
            $paramTestSqlInstance["DomainController"] = $DomainController
        }
        #endregion Build parameter Splat for scan

        # Prepare item processing in a pipeline compliant way
        $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Test-SqlInstance', [System.Management.Automation.CommandTypes]::Function)
        $scriptCmd = {
            & $wrappedCmd @paramTestSqlInstance
        }
        $steppablePipeline = $scriptCmd.GetSteppablePipeline()
        $steppablePipeline.Begin($true)
    }

    process {
        if (Test-FunctionInterrupt) { return }
        #region Process items or discover stuff
        switch ($PSCmdlet.ParameterSetName) {
            'Computer' {
                $ComputerName | Invoke-SteppablePipeline -Pipeline $steppablePipeline
            }
            'Discover' {
                #region Discovery: DataSource Enumeration
                if ($DiscoveryType -band ([Sqlcollaborative.Dbatools.Discovery.DbaInstanceDiscoveryType]::DataSourceEnumeration)) {
                    try {
                        # Discover instances
                        foreach ($instance in ([System.Data.Sql.SqlDataSourceEnumerator]::Instance.GetDataSources())) {
                            if ($instance.InstanceName -ne [System.DBNull]::Value) {
                                $steppablePipeline.Process("$($instance.Servername)\$($instance.InstanceName)")
                            }
                            else {
                                $steppablePipeline.Process($instance.Servername)
                            }
                        }
                    }
                    catch {
                        Write-Message -Level Warning -Message "Datasource enumeration failed" -ErrorRecord $_ -EnableException $EnableException.ToBool()
                    }
                }
                #endregion Discovery: DataSource Enumeration

                #region Discovery: SPN Search
                if ($DiscoveryType -band ([Sqlcollaborative.Dbatools.Discovery.DbaInstanceDiscoveryType]::Domain)) {
                    try {
                        Get-DomainSPN -DomainController $DomainController -Credential $Credential -ErrorAction Stop | Invoke-SteppablePipeline -Pipeline $steppablePipeline
                    }
                    catch {
                        Write-Message -Level Warning -Message "Failed to execute Service Principal Name discovery" -ErrorRecord $_ -EnableException $EnableException.ToBool()
                    }
                }
                #endregion Discovery: SPN Search

                #region Discovery: IP Range
                if ($DiscoveryType -band ([Sqlcollaborative.Dbatools.Discovery.DbaInstanceDiscoveryType]::IPRange)) {
                    if ($IpAddress) {
                        foreach ($address in $IpAddress) {
                            Resolve-IPRange -IpAddress $address | Invoke-SteppablePipeline -Pipeline $steppablePipeline
                        }
                    }
                    else {
                        Resolve-IPRange | Invoke-SteppablePipeline -Pipeline $steppablePipeline
                    }
                }
                #endregion Discovery: IP Range
            }
            default {
                Stop-Function -Message "Invalid parameterset, some developer probably had a beer too much. Please file an issue so we can fix this" -EnableException $EnableException
                return
            }
        }
        #endregion Process items or discover stuff
    }

    end {
        if (Test-FunctionInterrupt) {
            return
        }
        $steppablePipeline.End()
    }
}
#ValidationTags#Messaging#
function Find-DbaLoginInGroup {
    <#
        .SYNOPSIS
            Finds Logins in Active Directory groups that have logins on the SQL Instance.

        .DESCRIPTION
            Outputs all the active directory groups members for a server, or limits it to find a specific AD user in the groups

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a
            collection and receive pipeline input.

        .PARAMETER SqlCredential
            PSCredential object to connect under. If not specified, current Windows login will be used.

        .PARAMETER Login
            Find all AD Groups used on the instance that an individual login is a member of.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Login, AD, ActiveDirectory, Group, Security
            Author: Stephen Bennett, https://sqlnotesfromtheunderground.wordpress.com/
            Author: Simone Bizzotto, @niphlod

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Find-DbaLoginInGroup

        .EXAMPLE
            Find-DbaLoginInGroup -SqlInstance DEV01 -Login "MyDomain\Stephen.Bennett"

            Returns all active directory groups with logins on Sql Instance DEV01 that contain the AD user Stephen.Bennett.

        .EXAMPLE
            Find-DbaLoginInGroup -SqlInstance DEV01

            Returns all active directory users within all windows AD groups that have logins on the instance.

        .EXAMPLE
            Find-DbaLoginInGroup -SqlInstance DEV01 | Where-Object Login -like '*stephen*'

            Returns all active directory users within all windows AD groups that have logins on the instance whose login contains 'stephen'

    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$Login,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        try {
            Add-Type -AssemblyName System.DirectoryServices.AccountManagement
        }
        catch {
            Stop-Function -Message "Failed to load Assembly needed" -ErrorRecord $_
        }

        function Get-AllLogins {
            param
            (
                [string]$ADGroup,
                [string[]]$discard,
                [string]$ParentADGroup
            )
            begin {
                $output = @()
            }
            process {
                try {
                    $domain = $AdGroup.Split("\")[0]
                    $ads = New-Object System.DirectoryServices.AccountManagement.PrincipalContext('Domain', $domain)
                    [string]$groupName = $AdGroup
                    $group = [System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity($ads, $groupName);
                    $subgroups = @()
                    foreach ($member in $group.Members) {
                        $memberDomain = $member.DistinguishedName -Split "," | Where-Object { $_ -like "DC=*" } | Select-Object -first 1 | ForEach-Object { $_.ToUpper() -replace "DC=", '' }
                        if ($member.StructuralObjectClass -eq "group") {
                            $fullName = $memberDomain + "\" + $member.SamAccountName
                            if ($fullName -in $discard) {
                                Write-Message -Level Verbose -Message "skipping $fullName, already enumerated"
                                continue
                            }
                            else {
                                $subgroups += $fullName
                            }
                        }
                        else {
                            $output += [PSCustomObject]@{
                                SqlInstance        = $server.Name
                                InstanceName       = $server.ServiceName
                                ComputerName       = $server.ComputerName
                                Login              = $memberDomain + "\" + $member.SamAccountName
                                DisplayName        = $member.DisplayName
                                MemberOf           = $AdGroup
                                ParentADGroupLogin = $ParentADGroup
                            }
                        }
                    }
                }
                catch {
                    Stop-Function -Message "Failed to connect to Group: $member." -Target $member -ErrorRecord $_
                }
                $discard += $ADGroup
                foreach ($gr in $subgroups) {
                    if ($gr -notin $discard) {
                        $discard += $gr
                        Write-Message -Level Verbose -Message "Looking at $gr, recursively."
                        Get-AllLogins -ADGroup $gr -discard $discard -ParentADGroup $ParentADGroup
                    }
                }
            }
            end {
                $output
            }
        }
    }

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $AdGroups = $server.Logins | Where-Object { $_.LoginType -eq "WindowsGroup" -and $_.Name -ne "BUILTIN\Administrators" -and $_.Name -notlike "*NT SERVICE*" }

            foreach ($AdGroup in $AdGroups) {
                Write-Message -Level Verbose -Message "Looking at Group: $AdGroup"
                $ADGroupOut += Get-AllLogins $AdGroup.Name -ParentADGroup $AdGroup.Name
            }

            if (-not $Login) {
                $res = $ADGroupOut
            }
            else {
                $res = $ADGroupOut | Where-Object { $Login -contains $_.Login }
                if ($res.Length -eq 0) {
                    Write-Message -Level Warning -Message "No logins matching $($Login -join ',') found connecting to $server"
                    continue
                }
            }
            Select-DefaultView -InputObject $res -Property SqlInstance, Login, DisplayName, MemberOf, ParentADGroupLogin
        }
    }
}
#ValidationTags#FlowControl,Pipeline#
function Find-DbaOrphanedFile {
    <#
        .SYNOPSIS
            Find-DbaOrphanedFile finds orphaned database files. Orphaned database files are files not associated with any attached database.

        .DESCRIPTION
            This command searches all directories associated with SQL database files for database files that are not currently in use by the SQL Server instance.

            By default, it looks for orphaned .mdf, .ldf and .ndf files in the root\data directory, the default data path, the default log path, the system paths and any directory in use by any attached directory.

            You can specify additional filetypes using the -FileType parameter, and additional paths to search using the -Path parameter.

        .PARAMETER SqlInstance
            The SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Path
            Specifies one or more directories to search in addition to the default data and log directories.

        .PARAMETER FileType
            Specifies file extensions other than mdf, ldf and ndf to search for. Do not include the dot (".") when specifying the extension.

        .PARAMETER LocalOnly
            If this switch is enabled, only local filenames will be returned. Using this switch with multiple servers is not recommended since it does not return the associated server name.

        .PARAMETER RemoteOnly
            If this switch is enabled, only remote filenames will be returned.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Orphan, Database, DatabaseFile
            Author: Sander Stad (@sqlstad), sqlstad.nl
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

            Thanks to Paul Randal's notes on FILESTREAM which can be found at http://www.sqlskills.com/blogs/paul/filestream-directory-structure/

        .LINK
            https://dbatools.io/Find-DbaOrphanedFile

        .EXAMPLE
            Find-DbaOrphanedFile -SqlInstance sqlserver2014a

            Connects to sqlserver2014a, authenticating with Windows credentials, and searches for orphaned files. Returns server name, local filename, and unc path to file.

        .EXAMPLE
            Find-DbaOrphanedFile -SqlInstance sqlserver2014a -SqlCredential $cred

            Connects to sqlserver2014a, authenticating with SQL Server authentication, and searches for orphaned files. Returns server name, local filename, and unc path to file.

        .EXAMPLE
            Find-DbaOrphanedFile -SqlInstance sql2014 -Path 'E:\Dir1', 'E:\Dir2'

            Finds the orphaned files in "E:\Dir1" and "E:Dir2" in addition to the default directories.

        .EXAMPLE
            Find-DbaOrphanedFile -SqlInstance sql2014 -LocalOnly

            Returns only the local filepaths for orphaned files.

        .EXAMPLE
            Find-DbaOrphanedFile -SqlInstance sql2014 -RemoteOnly

            Returns only the remote filepath for orphaned files.

        .EXAMPLE
            Find-DbaOrphanedFile -SqlInstance sql2014, sql2016 -FileType fsf, mld

            Finds the orphaned ending with ".fsf" and ".mld" in addition to the default filetypes ".mdf", ".ldf", ".ndf" for both the servers sql2014 and sql2016.

    #>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [parameter(Mandatory = $false)]
        [object]$SqlCredential,
        [parameter(Mandatory = $false)]
        [string[]]$Path,
        [string[]]$FileType,
        [switch]$LocalOnly,
        [switch]$RemoteOnly,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        function Get-SQLDirTreeQuery {
            param($PathList)
            # use sysaltfiles in lower versions

            $q1 = "CREATE TABLE #enum ( id int IDENTITY, fs_filename nvarchar(512), depth int, is_file int, parent nvarchar(512) ); DECLARE @dir nvarchar(512);"
            $q2 = "SET @dir = 'dirname';

                INSERT INTO #enum( fs_filename, depth, is_file )
                EXEC xp_dirtree @dir, 1, 1;

                UPDATE #enum
                SET parent = @dir,
                fs_filename = ltrim(rtrim(fs_filename))
                WHERE parent IS NULL;"

            $query_files_sql = "SELECT e.fs_filename AS filename, e.parent
                    FROM #enum AS e
                    WHERE e.fs_filename NOT IN( 'xtp', '5', '`$FSLOG', '`$HKv2', 'filestream.hdr' )
                    AND is_file = 1;"

            # build the query string based on how many directories they want to enumerate
            $sql = $q1
            $sql += $($PathList | Where-Object { $_ -ne '' } | ForEach-Object { "$([System.Environment]::Newline)$($q2 -Replace 'dirname', $_)" })
            $sql += $query_files_sql
            Write-Message -Level Debug -Message $sql
            return $sql
        }
        function Get-SqlFileStructure {
            param
            (
                [Parameter(Mandatory = $true, Position = 1)]
                [Microsoft.SqlServer.Management.Smo.SqlSmoObject]$smoserver
            )
            if ($smoserver.versionMajor -eq 8) {
                $sql = "select filename from sysaltfiles"
            }
            else {
                $sql = "select physical_name as filename from sys.master_files"
            }

            $dbfiletable = $smoserver.ConnectionContext.ExecuteWithResults($sql)
            $ftfiletable = $dbfiletable.Tables[0].Clone()
            $dbfiletable.Tables[0].TableName = "data"

            # Add support for Full Text Catalogs in Sql Server 2005 and below
            if ($server.VersionMajor -lt 10) {
                $databaselist = $smoserver.Databases | Select-Object -property  Name, IsFullTextEnabled
                foreach ($db in $databaselist) {
                    if ($db.IsFullTextEnabled -eq $false) {
                        continue
                    }
                    $database = $db.name
                    $fttable = $null = $smoserver.Databases[$database].ExecuteWithResults('sp_help_fulltext_catalogs')
                    foreach ($ftc in $fttable.Tables[0].rows) {
                        $null = $ftfiletable.Rows.add($ftc.Path)
                    }
                }
            }

            $null = $dbfiletable.Tables.Add($ftfiletable)
            return $dbfiletable.Tables.Filename
        }

        function Format-Path {
            param ($path)

            $path = $path.Trim()
            #Thank you windows 2000
            $path = $path -replace '[^A-Za-z0-9 _\.\-\\:]', '__'
            return $path
        }

        $FileType += "mdf", "ldf", "ndf"
        $systemfiles = "distmdl.ldf", "distmdl.mdf", "mssqlsystemresource.ldf", "mssqlsystemresource.mdf"

        $FileTypeComparison = $FileType | ForEach-Object {$_.ToLower()} | Where-Object { $_ } | Sort-Object | Get-Unique
    }

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            # Reset all the arrays
            $dirtreefiles = $valid = $paths = $matching = @()

            $filestructure = Get-SqlFileStructure $server

            # Get any paths associated with current data and log files
            foreach ($file in $filestructure) {
                $paths += Split-Path -Path $file -Parent
            }

            # Get the default data and log directories from the instance
            Write-Message -Level Debug -Message "Adding paths"
            $paths += $server.RootDirectory + "\DATA"
            $paths += Get-SqlDefaultPaths $server data
            $paths += Get-SqlDefaultPaths $server log
            $paths += $server.MasterDBPath
            $paths += $server.MasterDBLogPath
            $paths += $Path
            $paths = $paths | ForEach-Object { "$_".TrimEnd("\") } | Sort-Object | Get-Unique
            $sql = Get-SQLDirTreeQuery $paths
            $datatable = $server.Databases['master'].ExecuteWithResults($sql).Tables[0]

            foreach ($row in $datatable) {
                $fullpath = [IO.Path]::combine($row.parent, $row.filename)
                $dirtreefiles += [pscustomobject]@{
                    FullPath   = $fullpath
                    Comparison = [IO.Path]::GetFullPath($(Format-Path $fullpath))
                }
            }
            $dirtreefiles = $dirtreefiles | Where-Object { $_ } | Sort-Object Comparison -Unique

            foreach ($file in $filestructure) {
                $valid += [IO.Path]::GetFullPath($(Format-Path $file))
            }

            $valid = $valid | Sort-Object | Get-Unique

            foreach ($file in $dirtreefiles.Comparison) {
                foreach ($type in $FileTypeComparison) {
                    if ($file.ToLower().EndsWith($type)) {
                        $matching += $file
                        break
                    }
                }
            }

            $dirtreematcher = @{}
            foreach ($el in $dirtreefiles) {
                $dirtreematcher[$el.Comparison] = $el.Fullpath
            }

            foreach ($file in $matching) {
                if ($file -notin $valid) {
                    $fullpath = $dirtreematcher[$file]

                    $filename = Split-Path $fullpath -Leaf

                    if ($filename -in $systemfiles) { continue }

                    $result = [pscustomobject]@{
                        Server         = $server.name
                        ComputerName   = $server.ComputerName
                        InstanceName   = $server.ServiceName
                        SqlInstance    = $server.DomainInstanceName
                        Filename       = $fullpath
                        RemoteFilename = Join-AdminUnc -Servername $server.ComputerName -Filepath $fullpath
                    }

                    if ($LocalOnly -eq $true) {
                        ($result | Select-Object filename).filename
                        continue
                    }

                    if ($RemoteOnly -eq $true) {
                        ($result | Select-Object remotefilename).remotefilename
                        continue
                    }

                    $result | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, Filename, RemoteFilename

                }
            }

        }
    }
    end {
        if ($result.count -eq 0) {
            Write-Message -Level Verbose -Message "No orphaned files found"
        }
    }
}
function Find-DbaSimilarTable {
    <#
        .SYNOPSIS
            Returns all tables/views that are similar in structure by comparing the column names of matching and matched tables/views

        .DESCRIPTION
            This function can either run against specific databases or all databases searching all/specific tables and views including in system databases.
            Typically one would use this to find for example archive version(s) of a table whose structures are similar.
            This can also be used to find tables/views that are very similar to a given table/view structure to see where a table/view might be used.

            More information can be found here: https://sqljana.wordpress.com/2017/03/31/sql-server-find-tables-with-similar-table-structure/

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto-populated from the server

        .PARAMETER SchemaName
            If you are looking in a specific schema whose table structures is to be used as reference structure, provide the name of the schema.
            If no schema is provided, looks at all schemas

        .PARAMETER TableName
            If you are looking in a specific table whose structure is to be used as reference structure, provide the name of the table.
            If no table is provided, looks at all tables
            If the table name exists in multiple schemas, all of them would qualify

        .PARAMETER ExcludeViews
            By default, views are included. You can exclude them by setting this switch to $false
            This excludes views in both matching and matched list

        .PARAMETER IncludeSystemDatabases
            By default system databases are ignored but you can include them within the search using this parameter

        .PARAMETER MatchPercentThreshold
            The minimum percentage of column names that should match between the matching and matched objects.
            Entries with no matches are eliminated

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Table
            Author: Jana Sattainathan (@SQLJana - http://sqljana.wordpress.com)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Find-DbaSimilarTable

        .EXAMPLE
            Find-DbaSimilarTable -SqlInstance DEV01

            Searches all user database tables and views for each, returns all tables or views with their matching tables/views and match percent

        .EXAMPLE
            Find-DbaSimilarTable -SqlInstance DEV01 -Database AdventureWorks

            Searches AdventureWorks database and lists tables/views and their corresponding matching tables/views with match percent

        .EXAMPLE
            Find-DbaSimilarTable -SqlInstance DEV01 -Database AdventureWorks -SchemaName HumanResource

            Searches AdventureWorks database and lists tables/views in the HumanResource schema with their corresponding matching tables/views with match percent

        .EXAMPLE
            Find-DbaSimilarTable -SqlInstance DEV01 -Database AdventureWorks -SchemaName HumanResource -Table Employee

            Searches AdventureWorks database and lists tables/views in the HumanResource schema and table Employee with its corresponding matching tables/views with match percent

        .EXAMPLE
            Find-DbaSimilarTable -SqlInstance DEV01 -Database AdventureWorks -MatchPercentThreshold 60

            Searches AdventureWorks database and lists all tables/views with its corresponding matching tables/views with match percent greater than or equal to 60
    #>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [string]$SchemaName,
        [string]$TableName,
        [switch]$ExcludeViews,
        [switch]$IncludeSystemDatabases,
        [int]$MatchPercentThreshold,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $everyServerVwCount = 0

        $sqlSelect = "WITH ColCountsByTable
                AS
                (
                      SELECT
                            c.TABLE_CATALOG,
                            c.TABLE_SCHEMA,
                            c.TABLE_NAME,
                            COUNT(1) AS Column_Count
                      FROM INFORMATION_SCHEMA.COLUMNS c
                      GROUP BY
                            c.TABLE_CATALOG,
                            c.TABLE_SCHEMA,
                            c.TABLE_NAME
                )
                SELECT
                      100 * COUNT(c2.COLUMN_NAME) /*Matching_Column_Count*/ / MIN(ColCountsByTable.Column_Count) /*Column_Count*/ AS MatchPercent,
                      DENSE_RANK() OVER(ORDER BY c.TABLE_CATALOG, c.TABLE_SCHEMA, c.TABLE_NAME) TableNameRankInDB,
                      c.TABLE_CATALOG AS DatabaseName,
                      c.TABLE_SCHEMA AS SchemaName,
                      c.TABLE_NAME AS TableName,
                      t.TABLE_TYPE AS TableType,
                      MIN(ColCountsByTable.Column_Count) AS ColumnCount,
                      c2.TABLE_CATALOG AS MatchingDatabaseName,
                      c2.TABLE_SCHEMA AS MatchingSchemaName,
                      c2.TABLE_NAME AS MatchingTableName,
                      t2.TABLE_TYPE AS MatchingTableType,
                      COUNT(c2.COLUMN_NAME) AS MatchingColumnCount
                FROM INFORMATION_SCHEMA.TABLES t
                      INNER JOIN INFORMATION_SCHEMA.COLUMNS c
                            ON t.TABLE_CATALOG = c.TABLE_CATALOG
                                  AND t.TABLE_SCHEMA = c.TABLE_SCHEMA
                                  AND t.TABLE_NAME = c.TABLE_NAME
                      INNER JOIN ColCountsByTable
                            ON t.TABLE_CATALOG = ColCountsByTable.TABLE_CATALOG
                                  AND t.TABLE_SCHEMA = ColCountsByTable.TABLE_SCHEMA
                                  AND t.TABLE_NAME = ColCountsByTable.TABLE_NAME
                      LEFT OUTER JOIN INFORMATION_SCHEMA.COLUMNS c2
                            ON t.TABLE_NAME != c2.TABLE_NAME
                                  AND c.COLUMN_NAME = c2.COLUMN_NAME
                      LEFT JOIN INFORMATION_SCHEMA.TABLES t2
                            ON c2.TABLE_NAME = t2.TABLE_NAME"

        $sqlWhere = "
                WHERE "

        $sqlGroupBy = "
                GROUP BY
                      c.TABLE_CATALOG,
                      c.TABLE_SCHEMA,
                      c.TABLE_NAME,
                      t.TABLE_TYPE,
                      c2.TABLE_CATALOG,
                      c2.TABLE_SCHEMA,
                      c2.TABLE_NAME,
                      t2.TABLE_TYPE "

        $sqlHaving = "
                HAVING
                    /*Match_Percent should be greater than 0 at minimum!*/
                    "

        $sqlOrderBy = "
                ORDER BY
                      MatchPercent DESC"


        $sql = ''
        $wherearray = @()

        if ($ExcludeViews) {
            $wherearray += " (t.TABLE_TYPE <> 'VIEW' AND t2.TABLE_TYPE <> 'VIEW') "
        }

        if ($SchemaName) {
            $wherearray += (" (c.TABLE_SCHEMA = '{0}') " -f $SchemaName.Replace("'", "''")) #Replace single quotes with two single quotes!
        }

        if ($TableName) {
            $wherearray += (" (c.TABLE_NAME = '{0}') " -f $TableName.Replace("'", "''")) #Replace single quotes with two single quotes!

        }

        if ($wherearray.length -gt 0) {
            $sqlWhere = "$sqlWhere " + ($wherearray -join " AND ")
        }
        else {
            $sqlWhere = ""
        }


        $matchThreshold = 0
        if ($MatchPercentThreshold) {
            $matchThreshold = $MatchPercentThreshold
        }
        else {
            $matchThreshold = 0
        }

        $sqlHaving += (" (100 * COUNT(c2.COLUMN_NAME) / MIN(ColCountsByTable.Column_Count) >= {0}) " -f $matchThreshold)



        $sql = "$sqlSelect $sqlWhere $sqlGroupBy $sqlHaving $sqlOrderBy"

        Write-Message -Level Debug -Message $sql

    }

    process {
        foreach ($Instance in $SqlInstance) {

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }


            #Use IsAccessible instead of Status -eq 'normal' because databases that are on readable secondaries for AG or mirroring replicas will cause errors to be thrown
            if ($IncludeSystemDatabases) {
                $dbs = $server.Databases | Where-Object { $_.IsAccessible -eq $true }
            }
            else {
                $dbs = $server.Databases | Where-Object { $_.IsAccessible -eq $true -and $_.IsSystemObject -eq $false }
            }

            if ($Database) {
                $dbs = $server.Databases | Where-Object Name -In $Database
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }


            $totalCount = 0
            $dbCount = $dbs.count
            foreach ($db in $dbs) {

                Write-Message -Level Verbose -Message "Searching on database $db"
                $rows = $db.Query($sql)

                foreach ($row in $rows) {
                    [PSCustomObject]@{
                        ComputerName              = $server.ComputerName
                        InstanceName              = $server.ServiceName
                        SqlInstance               = $server.DomainInstanceName
                        Table                     = "$($row.DatabaseName).$($row.SchemaName).$($row.TableName)"
                        MatchingTable             = "$($row.MatchingDatabaseName).$($row.MatchingSchemaName).$($row.MatchingTableName)"
                        MatchPercent              = $row.MatchPercent
                        OriginalDatabaseName      = $row.DatabaseName
                        OriginalSchemaName        = $row.SchemaName
                        OriginalTableName         = $row.TableName
                        OriginalTableNameRankInDB = $row.TableNameRankInDB
                        OriginalTableType         = $row.TableType
                        OriginalColumnCount       = $row.ColumnCount
                        MatchingDatabaseName      = $row.MatchingDatabaseName
                        MatchingSchemaName        = $row.MatchingSchemaName
                        MatchingTableName         = $row.MatchingTableName
                        MatchingTableType         = $row.MatchingTableType
                        MatchingColumnCount       = $row.MatchingColumnCount
                    }
                }

                $vwCount = $vwCount + $rows.Count
                $totalCount = $totalCount + $rows.Count
                $everyServerVwCount = $everyServerVwCount + $rows.Count

                Write-Message -Level Verbose -Message "Found $vwCount tables/views in $db"
            }

            Write-Message -Level Verbose -Message "Found $totalCount total tables/views in $dbCount databases"
        }
    }
    end {
        Write-Message -Level Verbose -Message "Found $everyServerVwCount total tables/views"
    }
}
function Find-DbaStoredProcedure {
    <#
        .SYNOPSIS
            Returns all stored procedures that contain a specific case-insensitive string or regex pattern.

        .DESCRIPTION
            This function can either run against specific databases or all databases searching all user or user and system stored procedures.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto-populated from the server

        .PARAMETER Pattern
            String pattern that you want to search for in the stored procedure textbody

        .PARAMETER IncludeSystemObjects
            By default, system stored procedures are ignored but you can include them within the search using this parameter.

            Warning - this will likely make it super slow if you run it on all databases.

        .PARAMETER IncludeSystemDatabases
            By default system databases are ignored but you can include them within the search using this parameter

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: StoredProcedure, Proc
            Author: Stephen Bennett, https://sqlnotesfromtheunderground.wordpress.com/

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Find-DbaStoredProcedure

        .EXAMPLE
            Find-DbaStoredProcedure -SqlInstance DEV01 -Pattern whatever

            Searches all user databases stored procedures for "whatever" in the textbody

        .EXAMPLE
            Find-DbaStoredProcedure -SqlInstance sql2016 -Pattern '\w+@\w+\.\w+'

            Searches all databases for all stored procedures that contain a valid email pattern in the textbody

        .EXAMPLE
            Find-DbaStoredProcedure -SqlInstance DEV01 -Database MyDB -Pattern 'some string' -Verbose

            Searches in "mydb" database stored procedures for "some string" in the textbody

        .EXAMPLE
            Find-DbaStoredProcedure -SqlInstance sql2016 -Database MyDB -Pattern RUNTIME -IncludeSystemObjects

            Searches in "mydb" database stored procedures for "runtime" in the textbody
    #>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [parameter(Mandatory = $true)]
        [string]$Pattern,
        [switch]$IncludeSystemObjects,
        [switch]$IncludeSystemDatabases,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $sql = "SELECT OBJECT_SCHEMA_NAME(p.object_id) as ProcSchema, p.name, m.definition as TextBody FROM sys.sql_modules m, sys.procedures p WHERE m.object_id = p.object_id"
        if (!$IncludeSystemObjects) { $sql = "$sql AND p.is_ms_shipped = 0" }
        $everyserverspcount = 0
    }
    process {
        foreach ($Instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $Instance"
                $server = Connect-SqlInstance -SqlInstance $Instance -SqlCredential $SqlCredential
            }
            catch {
                Write-Message -Level Warning -Message "Failed to connect to: $Instance"
                continue
            }

            if ($server.versionMajor -lt 9) {
                Write-Message -Level Warning -Message "This command only supports SQL Server 2005 and above."
                Continue
            }

            if ($IncludeSystemDatabases) {
                $dbs = $server.Databases | Where-Object { $_.Status -eq "normal" }
            }
            else {
                $dbs = $server.Databases | Where-Object { $_.Status -eq "normal" -and $_.IsSystemObject -eq $false }
            }

            if ($Database) {
                $dbs = $dbs | Where-Object Name -In $Database
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            $totalcount = 0
            $dbcount = $dbs.count
            foreach ($db in $dbs) {
                Write-Message -Level Verbose -Message "Searching on database $db"

                # If system objects aren't needed, find stored procedure text using SQL
                # This prevents SMO from having to enumerate

                if (!$IncludeSystemObjects) {
                    Write-Message -Level Debug -Message $sql
                    $rows = $db.ExecuteWithResults($sql).Tables.Rows
                    $sproccount = 0

                    foreach ($row in $rows) {
                        $totalcount++; $sproccount++; $everyserverspcount++

                        $procSchema = $row.ProcSchema
                        $proc = $row.Name

                        Write-Message -Level Verbose -Message "Looking in stored procedure: $procSchema.$proc textBody for $pattern"
                        if ($row.TextBody -match $Pattern) {
                            $sp = $db.StoredProcedures | Where-Object {$_.Schema -eq $procSchema -and $_.Name -eq $proc}

                            $StoredProcedureText = $sp.TextBody.split("`n")
                            $spTextFound = $StoredProcedureText | Select-String -Pattern $Pattern | ForEach-Object { "(LineNumber: $($_.LineNumber)) $($_.ToString().Trim())" }

                            [PSCustomObject]@{
                                ComputerName             = $server.ComputerName
                                SqlInstance              = $server.ServiceName
                                Database                 = $db.Name
                                Schema                   = $sp.Schema
                                Name                     = $sp.Name
                                Owner                    = $sp.Owner
                                IsSystemObject           = $sp.IsSystemObject
                                CreateDate               = $sp.CreateDate
                                LastModified             = $sp.DateLastModified
                                StoredProcedureTextFound = $spTextFound -join "`n"
                                StoredProcedure          = $sp
                                StoredProcedureFullText  = $sp.TextBody
                            } | Select-DefaultView -ExcludeProperty StoredProcedure, StoredProcedureFullText
                        }
                    }
                }
                else {
                    $storedprocedures = $db.StoredProcedures

                    foreach ($sp in $storedprocedures) {
                        $totalcount++; $sproccount++; $everyserverspcount++

                        $procSchema = $sp.Schema
                        $proc = $sp.Name

                        Write-Message -Level Verbose -Message "Looking in stored procedure $procSchema.$proc textBody for $pattern"
                        if ($sp.TextBody -match $Pattern) {

                            $StoredProcedureText = $sp.TextBody.split("`n")
                            $spTextFound = $StoredProcedureText | Select-String -Pattern $Pattern | ForEach-Object { "(LineNumber: $($_.LineNumber)) $($_.ToString().Trim())" }

                            [PSCustomObject]@{
                                ComputerName             = $server.ComputerName
                                SqlInstance              = $server.ServiceName
                                Database                 = $db.Name
                                Schema                   = $sp.Schema
                                Name                     = $sp.Name
                                Owner                    = $sp.Owner
                                IsSystemObject           = $sp.IsSystemObject
                                CreateDate               = $sp.CreateDate
                                LastModified             = $sp.DateLastModified
                                StoredProcedureTextFound = $spTextFound -join "`n"
                                StoredProcedure          = $sp
                                StoredProcedureFullText  = $sp.TextBody
                            } | Select-DefaultView -ExcludeProperty StoredProcedure, StoredProcedureFullText
                        }
                    }
                }
                Write-Message -Level Verbose -Message "Evaluated $sproccount stored procedures in $db"
            }
            Write-Message -Level Verbose -Message "Evaluated $totalcount total stored procedures in $dbcount databases"
        }
    }
    end {
        Write-Message -Level Verbose -Message "Evaluated $everyserverspcount total stored procedures"
    }
}
function Find-DbaTrigger {
    <#
        .SYNOPSIS
            Returns all triggers that contain a specific case-insensitive string or regex pattern.

        .DESCRIPTION
            This function search on Instance, Database and Object level.
            If you specify one or more databases, search on Server level will not be preformed.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto-populated from the server

        .PARAMETER Pattern
            String pattern that you want to search for in the trigger textbody

        .PARAMETER TriggerLevel
            Allows specify the trigger level that you want to search. By default is All (Server, Database, Object).

        .PARAMETER IncludeSystemObjects
            By default, system triggers are ignored but you can include them within the search using this parameter.

            Warning - this will likely make it super slow if you run it on all databases.

        .PARAMETER IncludeSystemDatabases
            By default system databases are ignored but you can include them within the search using this parameter

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Trigger
            Author: Cláudio Silva, @ClaudioESSilva

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Find-DbaTrigger

        .EXAMPLE
            Find-DbaTrigger -SqlInstance DEV01 -Pattern whatever

            Searches all user databases triggers for "whatever" in the textbody

        .EXAMPLE
            Find-DbaTrigger -SqlInstance sql2016 -Pattern '\w+@\w+\.\w+'

            Searches all databases for all triggers that contain a valid email pattern in the textbody

        .EXAMPLE
            Find-DbaTrigger -SqlInstance DEV01 -Database MyDB -Pattern 'some string' -Verbose

            Searches in "mydb" database triggers for "some string" in the textbody

        .EXAMPLE
            Find-DbaTrigger -SqlInstance sql2016 -Database MyDB -Pattern RUNTIME -IncludeSystemObjects

            Searches in "mydb" database triggers for "runtime" in the textbody
    #>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [parameter(Mandatory = $true)]
        [string]$Pattern,
        [ValidateSet('All', 'Server', 'Database', 'Object')]
        [string]$TriggerLevel = 'All',
        [switch]$IncludeSystemObjects,
        [switch]$IncludeSystemDatabases,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $sqlDatabaseTriggers = "SELECT tr.name, m.definition as TextBody FROM sys.sql_modules m, sys.triggers tr WHERE m.object_id = tr.object_id AND tr.parent_class = 0"

        $sqlTableTriggers = "SELECT OBJECT_SCHEMA_NAME(tr.parent_id) TableSchema, OBJECT_NAME(tr.parent_id) AS TableName, tr.name, m.definition as TextBody FROM sys.sql_modules m, sys.triggers tr WHERE m.object_id = tr.object_id AND tr.parent_class = 1"
        if (!$IncludeSystemObjects) { $sqlTableTriggers = "$sqlTableTriggers AND tr.is_ms_shipped = 0" }

        $everyserverstcount = 0
    }
    process {
        foreach ($Instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $Instance"
                $server = Connect-SqlInstance -SqlInstance $Instance -SqlCredential $SqlCredential
            }
            catch {
                Write-Message -Level Warning -Message "Failed to connect to: $Instance"
                continue
            }

            if ($server.versionMajor -lt 9) {
                Write-Message -Level Warning -Message "This command only supports SQL Server 2005 and above."
                Continue
            }

            #search at instance level. Only if no database was specified
            if ((-Not $Database) -and ($TriggerLevel -in @('All', 'Server'))) {
                foreach ($trigger in $server.Triggers) {
                    $everyserverstcount++; $triggercount++
                    Write-Message -Level Debug -Message "Looking in Trigger: $trigger TextBody for $pattern"
                    if ($trigger.TextBody -match $Pattern) {

                        $triggerText = $trigger.TextBody.split("`n`r")
                        $trTextFound = $triggerText | Select-String -Pattern $Pattern | ForEach-Object { "(LineNumber: $($_.LineNumber)) $($_.ToString().Trim())" }

                        [PSCustomObject]@{
                            ComputerName     = $server.ComputerName
                            SqlInstance      = $server.ServiceName
                            TriggerLevel     = "Server"
                            Database         = $null
                            Object           = $null
                            Name             = $trigger.Name
                            IsSystemObject   = $trigger.IsSystemObject
                            CreateDate       = $trigger.CreateDate
                            LastModified     = $trigger.DateLastModified
                            TriggerTextFound = $trTextFound -join "`n"
                            Trigger          = $trigger
                            TriggerFullText  = $trigger.TextBody
                        } | Select-DefaultView -ExcludeProperty Trigger, TriggerFullText
                    }
                }
                Write-Message -Level Verbose -Message "Evaluated $triggercount triggers in $server"
            }

            if ($IncludeSystemDatabases) {
                $dbs = $server.Databases | Where-Object { $_.Status -eq "normal" }
            }
            else {
                $dbs = $server.Databases | Where-Object { $_.Status -eq "normal" -and $_.IsSystemObject -eq $false }
            }

            if ($Database) {
                $dbs = $dbs | Where-Object Name -In $Database
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            $totalcount = 0
            $dbcount = $dbs.count

            if ($TriggerLevel -in @('All', 'Database', 'Object')) {
                foreach ($db in $dbs) {

                    Write-Message -Level Verbose -Message "Searching on database $db"

                    # If system objects aren't needed, find trigger text using SQL
                    # This prevents SMO from having to enumerate

                    if (!$IncludeSystemObjects) {
                        if ($TriggerLevel -in @('All', 'Database')) {
                            #Get Database Level triggers (DDL)
                            Write-Message -Level Debug -Message $sqlDatabaseTriggers
                            $rows = $db.ExecuteWithResults($sqlDatabaseTriggers).Tables.Rows
                            $triggercount = 0

                            foreach ($row in $rows) {
                                $totalcount++; $triggercount++; $everyserverstcount++

                                $trigger = $row.name

                                Write-Message -Level Verbose -Message "Looking in trigger $trigger for textBody with pattern $pattern on database $db"
                                if ($row.TextBody -match $Pattern) {
                                    $tr = $db.Triggers | Where-Object name -eq $row.name

                                    $triggerText = $tr.TextBody.split("`n`r")
                                    $trTextFound = $triggerText | Select-String -Pattern $Pattern | ForEach-Object { "(LineNumber: $($_.LineNumber)) $($_.ToString().Trim())" }

                                    [PSCustomObject]@{
                                        ComputerName     = $server.ComputerName
                                        SqlInstance      = $server.ServiceName
                                        TriggerLevel     = "Database"
                                        Database         = $db.name
                                        Object           = $tr.Parent
                                        Name             = $tr.Name
                                        IsSystemObject   = $tr.IsSystemObject
                                        CreateDate       = $tr.CreateDate
                                        LastModified     = $tr.DateLastModified
                                        TriggerTextFound = $trTextFound -join "`n"
                                        Trigger          = $tr
                                        TriggerFullText  = $tr.TextBody
                                    } | Select-DefaultView -ExcludeProperty Trigger, TriggerFullText
                                }
                            }
                        }

                        if ($TriggerLevel -in @('All', 'Object')) {
                            #Get Object Level triggers (DML)
                            Write-Message -Level Debug -Message $sqlTableTriggers
                            $rows = $db.ExecuteWithResults($sqlTableTriggers).Tables.Rows
                            $triggercount = 0

                            foreach ($row in $rows) {
                                $totalcount++; $triggercount++; $everyserverstcount++

                                $trigger = $row.name
                                $triggerParentSchema = $row.TableSchema
                                $triggerParent = $row.TableName

                                Write-Message -Level Verbose -Message "Looking in trigger $trigger for textBody with pattern $pattern in object $triggerParentSchema.$triggerParent at database $db"
                                if ($row.TextBody -match $Pattern) {

                                    $tr = ($db.Tables | Where-Object {$_.Name -eq $triggerParent -and $_.Schema -eq $triggerParentSchema}).Triggers | Where-Object name -eq $row.name

                                    $triggerText = $tr.TextBody.split("`n`r")
                                    $trTextFound = $triggerText | Select-String -Pattern $Pattern | ForEach-Object { "(LineNumber: $($_.LineNumber)) $($_.ToString().Trim())" }

                                    [PSCustomObject]@{
                                        ComputerName     = $server.ComputerName
                                        SqlInstance      = $server.ServiceName
                                        TriggerLevel     = "Object"
                                        Database         = $db.name
                                        Object           = $tr.Parent
                                        Name             = $tr.Name
                                        IsSystemObject   = $tr.IsSystemObject
                                        CreateDate       = $tr.CreateDate
                                        LastModified     = $tr.DateLastModified
                                        TriggerTextFound = $trTextFound -join "`n"
                                        Trigger          = $tr
                                        TriggerFullText  = $tr.TextBody
                                    } | Select-DefaultView -ExcludeProperty Trigger, TriggerFullText
                                }
                            }
                        }
                    }
                    else {
                        if ($TriggerLevel -in @('All', 'Database')) {
                            #Get Database Level triggers (DDL)
                            $triggers = $db.Triggers

                            $triggercount = 0

                            foreach ($tr in $triggers) {
                                $totalcount++; $triggercount++; $everyserverstcount++
                                $trigger = $tr.Name

                                Write-Message -Level Verbose -Message "Looking in trigger $trigger for textBody with pattern $pattern on database $db"
                                if ($tr.TextBody -match $Pattern) {

                                    $triggerText = $tr.TextBody.split("`n`r")
                                    $trTextFound = $triggerText | Select-String -Pattern $Pattern | ForEach-Object { "(LineNumber: $($_.LineNumber)) $($_.ToString().Trim())" }

                                    [PSCustomObject]@{
                                        ComputerName     = $server.ComputerName
                                        SqlInstance      = $server.ServiceName
                                        TriggerLevel     = "Database"
                                        Database         = $db.name
                                        Object           = $tr.Parent
                                        Name             = $tr.Name
                                        IsSystemObject   = $tr.IsSystemObject
                                        CreateDate       = $tr.CreateDate
                                        LastModified     = $tr.DateLastModified
                                        TriggerTextFound = $trTextFound -join "`n"
                                        Trigger          = $tr
                                        TriggerFullText  = $tr.TextBody
                                    } | Select-DefaultView -ExcludeProperty Trigger, TriggerFullText
                                }
                            }
                        }

                        if ($TriggerLevel -in @('All', 'Object')) {
                            #Get Object Level triggers (DML)
                            $triggers = $db.Tables | ForEach-Object {$_.Triggers}

                            $triggercount = 0

                            foreach ($tr in $triggers) {
                                $totalcount++; $triggercount++; $everyserverstcount++
                                $trigger = $tr.Name

                                Write-Message -Level Verbose -Message "Looking in trigger $trigger for textBody with pattern $pattern in object $($tr.Parent) at database $db"
                                if ($tr.TextBody -match $Pattern) {

                                    $triggerText = $tr.TextBody.split("`n`r")
                                    $trTextFound = $triggerText | Select-String -Pattern $Pattern | ForEach-Object { "(LineNumber: $($_.LineNumber)) $($_.ToString().Trim())" }

                                    [PSCustomObject]@{
                                        ComputerName     = $server.ComputerName
                                        SqlInstance      = $server.ServiceName
                                        TriggerLevel     = "Object"
                                        Database         = $db.name
                                        Object           = $tr.Parent
                                        Name             = $tr.Name
                                        IsSystemObject   = $tr.IsSystemObject
                                        CreateDate       = $tr.CreateDate
                                        LastModified     = $tr.DateLastModified
                                        TriggerTextFound = $trTextFound -join "`n"
                                        Trigger          = $tr
                                        TriggerFullText  = $tr.TextBody
                                    } | Select-DefaultView -ExcludeProperty Trigger, TriggerFullText
                                }
                            }
                        }
                    }
                    Write-Message -Level Verbose -Message "Evaluated $triggercount triggers in $db"
                }
            }
            Write-Message -Level Verbose -Message "Evaluated $totalcount total triggers in $dbcount databases"
        }
    }
    end {
        Write-Message -Level Verbose -Message "Evaluated $everyserverstcount total triggers"
    }
}
function Find-DbaUnusedIndex {
    <#
        .SYNOPSIS
            Find Unused indexes

        .DESCRIPTION
            This command will help you to find Unused indexes on a database or a list of databases

            Also tells how much space you can save by dropping the index.
            We show the type of compression so you can make a more considered decision.
            For now only supported for CLUSTERED and NONCLUSTERED indexes

            You can select the indexes you want to drop on the gridview and by clicking OK the drop statement will be generated.

        .PARAMETER SqlInstance
            The SQL Server you want to check for unused indexes.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies the database(s) to exclude from processing. Options for this list are auto-populated from the server.

        .PARAMETER FilePath
            Specifies the path of a file to write the DROP statements to.

        .PARAMETER NoClobber
            If this switch is enabled, the output file will not be overwritten.

        .PARAMETER Append
            If this switch is enabled, content will be appended to the output file.

        .PARAMETER IgnoreUptime
            Less than 7 days uptime can mean that analysis of unused indexes is unreliable, and normally no results will be returned. By setting this option results will be returned even if the Instance has been running for less that 7 days.

            .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Index
            Author: Aaron Nelson (@SQLvariant), SQLvariant.com

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Find-DbaUnusedIndex

        .EXAMPLE
            Find-DbaUnusedIndex -SqlInstance sql2005 -FilePath C:\temp\sql2005-UnusedIndexes.sql

            Generates the SQL statements to drop the selected unused indexes on server "sql2005". The statements are written to the file "C:\temp\sql2005-UnusedIndexes.sql"

        .EXAMPLE
            Find-DbaUnusedIndex -SqlInstance sql2005 -FilePath C:\temp\sql2005-UnusedIndexes.sql -Append

            Generates the SQL statements to drop the selected unused indexes on server "sql2005". The statements are written to the file "C:\temp\sql2005-UnusedIndexes.sql", appending if the file already exists.

        .EXAMPLE
            Find-DbaUnusedIndex -SqlInstance sqlserver2016 -SqlCredential $cred

            Generates the SQL statements to drop the selected unused indexes on server "sqlserver2016", using SQL Authentication to connect to the database.

        .EXAMPLE
            Find-DbaUnusedIndex -SqlInstance sqlserver2016 -Database db1, db2

            Generates the SQL Statement to to drop selected indexes in databases db1 & db2 on server "sqlserver2016".

        .EXAMPLE
            Find-DbaUnusedIndex -SqlInstance sqlserver2016

            Generates the SQL statements to drop selected indexes on all user databases.

        .EXAMPLE
            Fine-DbaUnusedIndex -SqlInstance sqlserver2016 -IgnoreUptime

            Generates the SQL statements to drop selected indexes on all user databases even if the instance has been online for less than 7 days.
            Note that results may not have enough detail for all indexes, so care should be taken when using them or the generated scripts. Best practice is to allow a full week to capture the mmajority of index use cases

    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Alias("OutFile", "Path")]
        [string]$FilePath,
        [switch]$NoClobber,
        [switch]$Append,
        [switch]$IgnoreUptime,
        [switch][Alias('Silent')]
        $EnableException
    )

    begin {

        # Support Compression 2008+
        $unusedQuery = "
        SELECT DB_NAME(database_id) AS 'DatabaseName'
        ,s.name AS 'SchemaName'
        ,t.name AS 'TableName'
        ,i.object_id AS ObjectId
        ,i.name AS 'IndexName'
        ,i.index_id as 'IndexId'
        ,i.type_desc as 'TypeDesc'
        ,user_seeks as 'UserSeeks'
        ,user_scans as 'UserScans'
        ,user_lookups  as 'UserLookups'
        ,user_updates  as 'UserUpdates'
        ,last_user_seek  as 'LastUserSeek'
        ,last_user_scan  as 'LastUserScan'
        ,last_user_lookup  as 'LastUserLookup'
        ,last_user_UPDATE  as 'LastUserUpdate'
        ,system_seeks  as 'SystemSeeks'
        ,system_scans  as 'SystemScans'
        ,system_lookups  as 'SystemLookup'
        ,system_updates  as 'SystemUpdates'
        ,last_system_seek  as 'LastSystemSeek'
        ,last_system_scan  as 'LastSystemScan'
        ,last_system_lookup  as 'LastSystemLookup'
        ,last_system_update as 'LastSystemUpdate'
        FROM SYS.TABLES T
        JOIN SYS.SCHEMAS S
            ON T.schema_id = s.schema_id
        JOIN SYS.indexes i
            ON i.object_id = t.object_id LEFT OUTER
        JOIN sys.dm_db_index_usage_stats iu
            ON iu.object_id = i.object_id
                AND iu.index_id = i.index_id
        WHERE iu.database_id = DB_ID()
                AND OBJECTPROPERTY(i.[object_id], 'IsMSShipped') = 0
                AND user_seeks = 0
                AND user_scans = 0
                AND user_lookups = 0
                AND i.type_desc NOT IN ('HEAP', 'CLUSTERED COLUMNSTORE')"

        if ($FilePath.Length -gt 0) {
            if ($FilePath -notlike "*\*") {
                $FilePath = ".\$FilePath"
            }
            $directory = Split-Path $FilePath
            $exists = Test-Path $directory

            if ($exists -eq $false) {
                Stop-Function -Message "Parent directory $directory does not exist."
                return
            }
        }

        Write-Message -Level Output -Message "Connecting to SQL Server."
        $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
    }
    process {
        if (Test-FunctionInterrupt) { return }

        if ($server.VersionMajor -lt 9) {
            Stop-Function -Message "This function does not support versions lower than SQL Server 2005 (v9)."
            return
        }

        $lastRestart = $server.Databases['tempdb'].CreateDate
        $endDate = Get-Date -Date $lastRestart
        $diffDays = (New-TimeSpan -Start $endDate -End (Get-Date)).Days

        if ($diffDays -le 6) {
            if ($IgnoreUptime -ne $true) {
                Stop-Function -Message "The SQL Service was restarted on $lastRestart, which is not long enough for a solid evaluation."
                return
            }
            else {
                Write-Message -Level Warning -Message "The SQL Service was restarted on $lastRestart, which is not long enough for a solid evaluation."
            }
        }

        <#
            Validate if server version is:
                - sql 2012 and if have SP3 CU3 (Build 6537) or higher
                - sql 2014 and if have SP2 (Build 5000) or higher
            If the major version is the same but the build is lower, throws the message
        #>
        if (
            ($server.VersionMajor -eq 11 -and $server.BuildNumber -lt 6537) `
            -or ($server.VersionMajor -eq 12 -and $server.BuildNumber -lt 5000)
        ) {
            Stop-Function -Message "This SQL version has a known issue. Rebuilding an index clears any existing row entry from sys.dm_db_index_usage_stats for that index.`r`nPlease refer to connect item: https://support.microsoft.com/en-us/help/3160407/fix-sys-dm-db-index-usage-stats-missing-information-after-index-rebuil"
            return
        }

        if ($diffDays -le 33) {
            Write-Message -Level Warning -Message "The SQL Service was restarted on $lastRestart, which may not be long enough for a solid evaluation."
        }

        if ($pipedatabase.Length -gt 0) {
            $database = $pipedatabase.name
        }

        if ($database.Count -eq 0) {
            $database = ($server.Databases | Where-Object { $_.IsSystemObject -eq 0 -and $_.IsAccessible }).Name
        }

        if ($database.Count -gt 0) {
            foreach ($db in $database) {
                if ($ExcludeDatabase -contains $db -or $null -eq $server.Databases[$db]) {
                    continue
                }
                if ($server.Databases[$db].IsAccessible -eq $false) {
                    Write-Message -Level Warning -Message "Database [$db] is not accessible."
                    continue
                }
                try {
                    Write-Message -Level Output -Message "Getting indexes from database '$db'."

                    $sql = $unusedQuery

                    $unusedIndex = $server.Databases[$db].ExecuteWithResults($sql)

                    $scriptGenerated = $false

                    if ($unusedIndex.Tables[0].Rows.Count -gt 0) {
                        $indexesToDrop = $unusedIndex.Tables[0]

                        if ($indexesToDrop.Count -gt 0 -or !([string]::IsNullOrEmpty($indexesToDrop))) {

                            foreach ($index in $indexesToDrop) {
                                if ($FilePath.Length -gt 0) {
                                    Write-Message -Level Output -Message "Exporting $($index.TableName).$($index.IndexName)"
                                    $sqlout += "USE [$($index.DatabaseName)]`r`n"
                                    $sqlout += "GO`r`n"
                                    $sqlout += "IF EXISTS (SELECT 1 FROM sys.indexes WHERE [object_id] = OBJECT_ID('$($index.SchemaName).$($index.TableName)') AND name = '$($index.IndexName)')`r`n"
                                    $sqlout += "DROP INDEX $($index.SchemaName).$($index.TableName).$($index.IndexName)`r`n"
                                    $sqlout += "GO`r`n`r`n"`

                                }
                            }

                            if ($FilePath.Length -gt 0) {
                                $sqlout | Out-File -FilePath $FilePath -Append:$Append -NoClobber:$NoClobber
                            }
                            else {
                                $indexesToDrop
                            }

                            $scriptGenerated = $true
                        }
                    }
                    else {
                        Write-Message -Level Output -Message "No Unused indexes found!"
                    }
                }
                catch {
                    Stop-Function -Message "Issue gathering indexes" -Category InvalidOperation -ErrorRecord $_ -Target $db
                }
            }

            if ($scriptGenerated) {
                Write-Message -Level Warning -Message "Confirm the generated script before execute!"
            }
            if ($FilePath.Length -gt 0) {
                Write-Message -Level Output -Message "Script generated to $FilePath"
            }
        }
        else {
            Write-Message -Level Output -Message "There are no databases to analyse."
        }
    }
    end {
        if (Test-FunctionInterrupt) {
            return
        }
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Get-SqlUnusedIndex
    }
}
#ValidationTags#Messaging#
function Find-DbaUserObject {
    <#
        .SYNOPSIS
            Searches SQL Server to find user-owned objects (ie. not dbo or sa) or for any object owned by a specific user specified by the Pattern parameter.

        .DESCRIPTION
            Looks at the below list of objects to see if they are either owned by a user or a specific user (using the parameter -Pattern)
                Database Owner
                Agent Job Owner
                Used in Credential
                USed in Proxy
                SQL Agent Steps using a Proxy
                Endpoints
                Server Roles
                Database Schemas
                Database Roles
                Database Assembles
                Database Synonyms

        .PARAMETER SqlInstance
            SqlInstance name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Pattern
            The regex pattern that the command will search for

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Object
            Author: Stephen Bennett, https://sqlnotesfromtheunderground.wordpress.com/
            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Find-DbaUserObject

        .EXAMPLE
            Find-DbaUserObject -SqlInstance DEV01 -Pattern ad\stephen

            Searches user objects for owner ad\stephen

        .EXAMPLE
            Find-DbaUserObject -SqlInstance DEV01 -Verbose

            Shows all user owned (non-sa, non-dbo) objects and verbose output
    #>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlInstances")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string]$Pattern,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        if ($Pattern -match '^[\w\d\.-]+\\[\w\d\.-]+$') {
            Write-Message -Level Verbose -Message "Too few slashes, adding extra as required by regex"
            $Pattern = $Pattern.Replace('\', '\\')
        }
    }
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $saname = Get-SaLoginName $server

            ## Credentials
            if (-not $pattern) {
                Write-Message -Level Verbose -Message "Gathering data on instance objects"
                $creds = $server.Credentials
                $proxies = $server.JobServer.ProxyAccounts
                $endPoints = $server.Endpoints | Where-Object { $_.Owner -ne $saname }

                Write-Message -Level Verbose -Message "Gather data on Agent Jobs ownership"
                $jobs = $server.JobServer.Jobs | Where-Object { $_.OwnerLoginName -ne $saname }
            }
            else {
                Write-Message -Level Verbose -Message "Gathering data on instance objects"
                $creds = $server.Credentials | Where-Object { $_.Identity -match $pattern }
                $proxies = $server.JobServer.ProxyAccounts | Where-Object { $_.CredentialIdentity -match $pattern }
                $endPoints = $server.Endpoints | Where-Object { $_.Owner -match $pattern }

                Write-Message -Level Verbose -Message "Gather data on Agent Jobs ownership"
                $jobs = $server.JobServer.Jobs | Where-Object { $_.OwnerLoginName -match $pattern }
            }

            ## dbs
            if (-not $pattern) {
                foreach ($db in $server.Databases | Where-Object { $_.Owner -ne $saname }) {
                    Write-Message -Level Verbose -Message "checking if $db is owned "

                    [PSCustomObject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Type         = "Database"
                        Owner        = $db.Owner
                        Name         = $db.Name
                        Parent       = $db.Parent.Name
                    }
                }
            }
            else {
                foreach ($db in $server.Databases | Where-Object { $_.Owner -match $pattern }) {
                    [PSCustomObject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Type         = "Database"
                        Owner        = $db.Owner
                        Name         = $db.Name
                        Parent       = $db.Parent.Name
                    }
                }
            }

            ## agent jobs
            if (-not $pattern) {
                foreach ($job in $server.JobServer.Jobs | Where-Object { $_.OwnerLoginName -ne $saname }) {
                    [PSCustomObject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Type         = "Agent Job"
                        Owner        = $job.OwnerLoginName
                        Name         = $job.Name
                        Parent       = $job.Parent.Name
                    }
                }
            }
            else {
                foreach ($job in $server.JobServer.Jobs | Where-Object { $_.OwnerLoginName -match $pattern }) {
                    [PSCustomObject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Type         = "Agent Job"
                        Owner        = $job.OwnerLoginName
                        Name         = $job.Name
                        Parent       = $job.Parent.Name
                    }
                }
            }

            ## credentials
            foreach ($cred in $creds) {
                ## list credentials using the account

                [PSCustomObject]@{
                    ComputerName = $server.ComputerName
                    InstanceName = $server.ServiceName
                    SqlInstance  = $server.DomainInstanceName
                    Type         = "Credential"
                    Owner        = $cred.Identity
                    Name         = $cred.Name
                    Parent       = $cred.Parent.Name
                }
            }

            ## proxies
            foreach ($proxy in $proxies) {
                [PSCustomObject]@{
                    ComputerName = $server.ComputerName
                    InstanceName = $server.ServiceName
                    SqlInstance  = $server.DomainInstanceName
                    Type         = "Proxy"
                    Owner        = $proxy.CredentialIdentity
                    Name         = $proxy.Name
                    Parent       = $proxy.Parent.Name
                }

                ## list agent jobs steps using proxy
                foreach ($job in $server.JobServer.Jobs) {
                    foreach ($step in $job.JobSteps | Where-Object { $_.ProxyName -eq $proxy.Name }) {
                        [PSCustomObject]@{
                            ComputerName = $server.ComputerName
                            InstanceName = $server.ServiceName
                            SqlInstance  = $server.DomainInstanceName
                            Type         = "Agent Step"
                            Owner        = $step.ProxyName
                            Name         = $step.Name
                            Parent       = $step.Parent.Name #$step.Name
                        }
                    }
                }
            }


            ## endpoints
            foreach ($endPoint in $endPoints) {
                [PSCustomObject]@{
                    ComputerName = $server.ComputerName
                    InstanceName = $server.ServiceName
                    SqlInstance  = $server.DomainInstanceName
                    Type         = "Endpoint"
                    Owner        = $endpoint.Owner
                    Name         = $endPoint.Name
                    Parent       = $endPoint.Parent.Name
                }
            }

            ## Server Roles
            if (-not $pattern) {
                foreach ($role in $server.Roles | Where-Object { $_.Owner -ne $saname }) {
                    Write-Message -Level Verbose -Message "checking if $db is owned "
                    [PSCustomObject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Type         = "Server Role"
                        Owner        = $role.Owner
                        Name         = $role.Name
                        Parent       = $role.Parent.Name
                    }
                }
            }
            else {
                foreach ($role in $server.Roles | Where-Object { $_.Owner -match $pattern }) {
                    [PSCustomObject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Type         = "Server Role"
                        Owner        = $role.Owner
                        Name         = $role.Name
                        Parent       = $role.Parent.Name
                    }
                }
            }

            ## Loop internal database
            foreach ($db in $server.Databases | Where-Object IsAccessible) {
                Write-Message -Level Verbose -Message "Gather user owned object in database: $db"
                ##schemas
                $sysSchemas = "DatabaseMailUserRole", "db_ssisadmin", "db_ssisltduser", "db_ssisoperator", "SQLAgentOperatorRole", "SQLAgentReaderRole", "SQLAgentUserRole", "TargetServersRole", "RSExecRole"

                if (-not $pattern) {
                    $schemas = $db.Schemas | Where-Object { $_.IsSystemObject -eq 0 -and $_.Owner -ne "dbo" -and $sysSchemas -notcontains $_.Owner }
                }
                else {
                    $schemas = $db.Schemas | Where-Object { $_.IsSystemObject -eq 0 -and $_.Owner -match $pattern -and $sysSchemas -notcontains $_.Owner }
                }
                foreach ($schema in $schemas) {
                    [PSCustomObject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Type         = "Schema"
                        Owner        = $schema.Owner
                        Name         = $schema.Name
                        Parent       = $schema.Parent.Name
                    }
                }

                ## database roles
                if (-not $pattern) {
                    $roles = $db.Roles | Where-Object { $_.IsSystemObject -eq 0 -and $_.Owner -ne "dbo" }
                }
                else {
                    $roles = $db.Roles | Where-Object { $_.IsSystemObject -eq 0 -and $_.Owner -match $pattern }
                }
                foreach ($role in $roles) {
                    [PSCustomObject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Type         = "Database Role"
                        Owner        = $role.Owner
                        Name         = $role.Name
                        Parent       = $role.Parent.Name
                    }
                }

                ## assembly
                if (-not $pattern) {
                    $assemblies = $db.Assemblies | Where-Object { $_.IsSystemObject -eq 0 -and $_.Owner -ne "dbo" }
                }
                else {
                    $assemblies = $db.Assemblies | Where-Object { $_.IsSystemObject -eq 0 -and $_.Owner -match $pattern }
                }

                foreach ($assembly in $assemblies) {
                    [PSCustomObject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Type         = "Database Assembly"
                        Owner        = $assembly.Owner
                        Name         = $assembly.Name
                        Parent       = $assembly.Parent.Name
                    }
                }

                ## synonyms
                if (-not $pattern) {
                    $synonyms = $db.Synonyms | Where-Object { $_.IsSystemObject -eq 0 -and $_.Owner -ne "dbo" }
                }
                else {
                    $synonyms = $db.Synonyms | Where-Object { $_.IsSystemObject -eq 0 -and $_.Owner -match $pattern }
                }

                foreach ($synonym in $synonyms) {
                    [PSCustomObject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Type         = "Database Synonyms"
                        Owner        = $synonym.Owner
                        Name         = $synonym.Name
                        Parent       = $synonym.Parent.Name
                    }
                }
            }
        }
    }
}
function Find-DbaView {
    <#
        .SYNOPSIS
            Returns all views that contain a specific case-insensitive string or regex pattern.

        .DESCRIPTION
            This function can either run against specific databases or all databases searching all user or user and system views.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto-populated from the server

        .PARAMETER Pattern
            String pattern that you want to search for in the view textbody

        .PARAMETER IncludeSystemObjects
            By default, system views are ignored but you can include them within the search using this parameter.

            Warning - this will likely make it super slow if you run it on all databases.

        .PARAMETER IncludeSystemDatabases
            By default system databases are ignored but you can include them within the search using this parameter

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: View
            Author: Cláudio Silva (@ClaudioESSilva)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Find-DbaView

        .EXAMPLE
            Find-DbaView -SqlInstance DEV01 -Pattern whatever

            Searches all user databases views for "whatever" in the textbody

        .EXAMPLE
            Find-DbaView -SqlInstance sql2016 -Pattern '\w+@\w+\.\w+'

            Searches all databases for all views that contain a valid email pattern in the textbody

        .EXAMPLE
            Find-DbaView -SqlInstance DEV01 -Database MyDB -Pattern 'some string' -Verbose

            Searches in "mydb" database views for "some string" in the textbody

        .EXAMPLE
            Find-DbaView -SqlInstance sql2016 -Database MyDB -Pattern RUNTIME -IncludeSystemObjects

            Searches in "mydb" database views for "runtime" in the textbody
    #>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [parameter(Mandatory = $true)]
        [string]$Pattern,
        [switch]$IncludeSystemObjects,
        [switch]$IncludeSystemDatabases,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $sql = "SELECT OBJECT_SCHEMA_NAME(vw.object_id) as ViewSchema, vw.name, m.definition as TextBody FROM sys.sql_modules m, sys.views vw WHERE m.object_id = vw.object_id"
        if (!$IncludeSystemObjects) { $sql = "$sql AND vw.is_ms_shipped = 0" }
        $everyservervwcount = 0
    }
    process {
        foreach ($Instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $Instance"
                $server = Connect-SqlInstance -SqlInstance $Instance -SqlCredential $SqlCredential
            }
            catch {
                Write-Message -Level Warning -Message "Failed to connect to: $Instance"
                continue
            }

            if ($server.versionMajor -lt 9) {
                Write-Message -Level Warning -Message "This command only supports SQL Server 2005 and above."
                Continue
            }

            if ($IncludeSystemDatabases) {
                $dbs = $server.Databases | Where-Object { $_.Status -eq "normal" }
            }
            else {
                $dbs = $server.Databases | Where-Object { $_.Status -eq "normal" -and $_.IsSystemObject -eq $false }
            }

            if ($Database) {
                $dbs = $dbs | Where-Object Name -In $Database
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            $totalcount = 0
            $dbcount = $dbs.count
            foreach ($db in $dbs) {
                Write-Message -Level Verbose -Message "Searching on database $db"

                # If system objects aren't needed, find view text using SQL
                # This prevents SMO from having to enumerate

                if (!$IncludeSystemObjects) {
                    Write-Message -Level Debug -Message $sql
                    $rows = $db.ExecuteWithResults($sql).Tables.Rows
                    $vwcount = 0

                    foreach ($row in $rows) {
                        $totalcount++; $vwcount++; $everyservervwcount++

                        $viewSchema = $row.ViewSchema
                        $view = $row.name

                        Write-Message -Level Verbose -Message "Looking in View: $viewSchema.$view TextBody for $pattern"
                        if ($row.TextBody -match $Pattern) {
                            $vw = $db.Views | Where-Object {$_.Schema -eq $viewSchema -and $_.Name -eq $view}

                            $viewText = $vw.TextBody.split("`n`r")
                            $vwTextFound = $viewText | Select-String -Pattern $Pattern | ForEach-Object { "(LineNumber: $($_.LineNumber)) $($_.ToString().Trim())" }

                            [PSCustomObject]@{
                                ComputerName   = $server.ComputerName
                                SqlInstance    = $server.ServiceName
                                Database       = $db.Name
                                Schema         = $vw.Schema
                                Name           = $vw.Name
                                Owner          = $vw.Owner
                                IsSystemObject = $vw.IsSystemObject
                                CreateDate     = $vw.CreateDate
                                LastModified   = $vw.DateLastModified
                                ViewTextFound  = $vwTextFound -join "`n"
                                View           = $vw
                                ViewFullText   = $vw.TextBody
                            } | Select-DefaultView -ExcludeProperty View, ViewFullText
                        }
                    }
                }
                else {
                    $Views = $db.Views

                    foreach ($vw in $Views) {
                        $totalcount++; $vwcount++; $everyservervwcount++

                        $viewSchema = $row.ViewSchema
                        $view = $vw.Name

                        Write-Message -Level Verbose -Message "Looking in View: $viewSchema.$view TextBody for $pattern"
                        if ($vw.TextBody -match $Pattern) {

                            $viewText = $vw.TextBody.split("`n`r")
                            $vwTextFound = $viewText | Select-String -Pattern $Pattern | ForEach-Object { "(LineNumber: $($_.LineNumber)) $($_.ToString().Trim())" }

                            [PSCustomObject]@{
                                ComputerName   = $server.ComputerName
                                SqlInstance    = $server.ServiceName
                                Database       = $db.Name
                                Schema         = $vw.Schema
                                Name           = $vw.Name
                                Owner          = $vw.Owner
                                IsSystemObject = $vw.IsSystemObject
                                CreateDate     = $vw.CreateDate
                                LastModified   = $vw.DateLastModified
                                ViewTextFound  = $vwTextFound -join "`n"
                                View           = $vw
                                ViewFullText   = $vw.TextBody
                            } | Select-DefaultView -ExcludeProperty View, ViewFullText
                        }
                    }
                }
                Write-Message -Level Verbose -Message "Evaluated $vwcount views in $db"
            }
            Write-Message -Level Verbose -Message "Evaluated $totalcount total views in $dbcount databases"
        }
    }
    end {
        Write-Message -Level Verbose -Message "Evaluated $everyservervwcount total views"
    }
}
function Format-DbaBackupInformation {
    <#
        .SYNOPSIS
            Transforms the data in a dbatools backuphistory object for a restore

        .DESCRIPTION
            Performs various mapping on Backup History, ready restoring
            Options include changing restore paths, backup paths, database name and many others

        .PARAMETER BackupHistory
            A dbatools backupHistory object, normally this will have been created using Select-DbaBackupInformation

        .PARAMETER ReplaceDatabaseName
            If a single value is provided, this will be replaced do all occurrences a database name
            If a Hashtable is passed in, each database name mention will be replaced as specified. If a database's name does not appear it will not be replace
            DatabaseName will also be replaced where it  occurs in the file paths of data and log files.
            Please note, that this won't change the Logical Names of datafiles, that has to be done with a separate Alter DB call

        .PARAMETER DatabaseNamePrefix
            This string will be prefixed to all restored database's name

        .PARAMETER DataFileDirectory
            This will move ALL restored files to this location during the restore

        .PARAMETER LogFileDirectory
            This will move all log files to this location, overriding DataFileDirectory

        .PARAMETER DestinationFileStreamDirectory
            This move the FileStream folder and contents to the new location, overriding DataFileDirectory

        .PARAMETER FileNamePrefix
            This string will  be prefixed to all restored files (Data and Log)

        .PARAMETER RebaseBackupFolder
            Use this to rebase where your backups are stored.

        .PARAMETER Continue
            Indicates that this is a continuing restore

        .PARAMETER DatabaseFilePrefix
            A string that will be prefixed to every file restored

        .PARAMETER DatabaseFileSuffix
            A string that will be suffixed to every file restored

        .PARAMETER ReplaceDbNameInFile
            If set, will replace the old databasename with the new name if it occurs in the file name

        .PARAMETER FileMapping
            A hashtable that can be used to move specific files to a location.
            $FileMapping = @{'DataFile1'='c:\restoredfiles\Datafile1.mdf';'DataFile3'='d:\DataFile3.mdf'}
            And files not specified in the mapping will be restored to their original location
            This Parameter is exclusive with DestinationDataDirectory
            If specified, this will override any other file renaming/relocation options.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: DisasterRecovery, Backup, Restore
            Author:Stuart Moore (@napalmgram stuart-moore.com )

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Format-DbaBackupInformation

        .EXAMPLE
            $History | Format-DbaBackupInformation -ReplaceDatabaseName NewDb

            Changes as databasename references to NewDb, both in the database name and any restore paths. Note, this will fail if the BackupHistory object contains backups for more than 1 database

        .EXAMPLE
            $History | Format-DbaBackupInformation -ReplaceDatabaseName @{'OldB'='NewDb';'ProdHr'='DevHr'}

            Will change all occurences of original database name in the backup history (names and restore paths) using the mapping in the hashtable.
            In this example any occurance of OldDb will be replaced with NewDb and ProdHr with DevPR

        .EXAMPLE
            $History | Format-DbaBackupInformation -DataFileDirectory 'D:\DataFiles\' -LogFileDirectory 'E:\LogFiles\

            This example with change the restore path for all datafiles (everything that is not a log file) to d:\datafiles
            And all Transaction Log files will be restored to E:\Logfiles

        .EXAMPLE
            $History | Formate-DbaBackupInformation -RebaseBackupFolder f:\backups

            This example changes the location that SQL Server will look for the backups. This is useful if you've moved the backups to a different location
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [object[]]$BackupHistory,
        [object]$ReplaceDatabaseName,
        [switch]$ReplaceDbNameInFile,
        [string]$DataFileDirectory,
        [string]$LogFileDirectory,
        [string]$DestinationFileStreamDirectory,
        [string]$DatabaseNamePrefix,
        [string]$DatabaseFilePrefix,
        [string]$DatabaseFileSuffix,
        [string]$RebaseBackupFolder,
        [switch]$Continue,
        [hashtable]$FileMapping,
        [switch]$EnableException
    )
    Begin {

        Write-Message -Message "Starting" -Level Verbose
        if ($null -ne $ReplaceDatabaseName) {
            if ($ReplaceDatabaseName -is [string] -or $ReplaceDatabaseName.ToString() -ne 'System.Collections.Hashtable') {
                Write-Message -Message "String passed in for DB rename" -Level Verbose
                $ReplaceDatabaseNameType = 'single'
            }
            elseif ($ReplaceDatabaseName -is [HashTable] -or $ReplaceDatabaseName.ToString() -eq 'System.Collections.Hashtable' ) {
                Write-Message -Message "Hashtable passed in for DB rename" -Level Verbose
                $ReplaceDatabaseNameType = 'multi'
            }
            else {
                Write-Message -Message "ReplacemenDatabaseName is $($ReplaceDatabaseName.Gettype().ToString()) - $ReplaceDatabaseName" -level Verbose
            }
        }
        if ((Test-Bound -Parameter DataFileDirectory) -and $DataFileDirectory[-1] -eq '\' ) {
            $DataFileDirectory = $DataFileDirectory.substring(0, $DataFileDirectory.length - 1)
        }
        if ((Test-Bound -Parameter DestinationFileStreamDirectory) -and $DestinationFileStreamDirectory[-1] -eq '\' ) {
            $DestinationFileStreamDirectory = $DestinationFileStreamDirectory.substring(0, $DestinationFileStreamDirectory.length - 1)
        }
        if ((Test-Bound -Parameter LogFileDirectory) -and $LogFileDirectory[-1] -eq '\' ) {
            $LogFileDirectory = $LogFileDirectory.substring(0, $LogFileDirectory.length - 1)
        }
        if ((Test-Bound -Parameter RebaseBackupFolder) -and $RebaseBackupFolder[-1] -eq '\' ) {
            $RebaseBackupFolder = $RebaseBackupFolder.substring(0, $RebaseBackupFolder.length - 1)
        }
    }


    Process {

        ForEach ($History in $BackupHistory) {
            if ("OriginalDatabase" -notin $History.PSobject.Properties.name) {
                $History | Add-Member -Name 'OriginalDatabase' -Type NoteProperty -Value $History.Database
            }
            if ("OriginalFileList" -notin $History.PSobject.Properties.name) {
                $History | Add-Member -Name 'OriginalFileList' -Type NoteProperty -Value ''
                $History | ForEach-Object {$_.OriginalFileList = $_.FileList}
            }
            if ("OriginalFullName" -notin $History.PSobject.Properties.name) {
                $History | Add-Member -Name 'OriginalFullName' -Type NoteProperty -Value $History.FullName
            }
            if ("IsVerified" -notin $History.PSobject.Properties.name) {
                $History | Add-Member -Name 'IsVerified' -Type NoteProperty -Value $False
            }
            Switch ($History.Type) {
                'Full' {$History.Type = 'Database'}
                'Differential' {$History.Type = 'Database Differential'}
                'Log' {$History.Type = 'Transaction Log'}
            }


            if ($ReplaceDatabaseNameType -eq 'single' -and $ReplaceDatabaseName -ne '' ) {
                $History.Database = $ReplaceDatabaseName
                $ReplaceMentName = $ReplaceDatabaseName
                Write-Message -Message "New DbName (String) = $($History.Database)" -Level Verbose
            }
            elseif ($ReplaceDatabaseNameType -eq 'multi') {
                if ($null -ne $ReplaceDatabaseName[$History.Database]) {
                    $History.Database = $ReplaceDatabaseName[$History.Database]
                    $ReplacementName = $ReplaceDatabaseName[$History.Database]
                    Write-Message -Message "New DbName (Hash) = $($History.Database)" -Level Verbose
                }
            }
            $History.Database = $DatabaseNamePrefix + $History.Database
            if ($true -ne $Continue) {
                $History.FileList | ForEach-Object {
                    if ($null -ne $FileMapping ) {
                        if ($null -ne $FileMapping[$_.LogicalName]) {
                            $_.PhysicalName = $FileMapping[$_.LogicalName]
                        }
                    }
                    else {
                        if ($ReplaceDbNameInFile -eq $true) {
                            $_.PhysicalName = $_.PhysicalName -Replace $History.OriginalDatabase, $History.Database
                        }
                        Write-message -Message " 1 PhysicalName = $($_.PhysicalName) " -Level Verbose
                        $Pname = [System.Io.FileInfo]$_.PhysicalName
                        $RestoreDir = $Pname.DirectoryName
                        if ($_.Type -eq 'D' -or $_.FileType -eq 'D') {
                            if ('' -ne $DataFileDirectory) {
                                $RestoreDir = $DataFileDirectory
                            }
                        }
                        elseif ($_.Type -eq 'L' -or $_.FileType -eq 'L') {
                            if ('' -ne $LogFileDirectory) {
                                $RestoreDir = $LogFileDirectory
                            }
                            elseif ('' -ne $DataFileDirectory) {
                                $RestoreDir = $DataFileDirectory
                            }
                        }
                        elseif ($_.Type -eq 'S' -or $_.FileType -eq 'S') {
                            if ('' -ne $DestinationFileStreamDirectory) {
                                $RestoreDir = $DestinationFileStreamDirectory
                            }
                            elseif ('' -ne $DataFileDirectory) {
                                $RestoreDir = $DataFileDirectory
                            }
                        }

                        $_.PhysicalName = $RestoreDir + "\" + $DatabaseFilePrefix + $Pname.BaseName + $DatabaseFileSuffix + $pname.extension
                        Write-message -Message "PhysicalName = $($_.PhysicalName) " -Level Verbose
                    }
                }
            }
            if ($null -ne $RebaseBackupFolder -and $History.FullName[0] -notmatch 'http') {
                $History.FullName | ForEach-Object {
                    $file = [System.IO.FileInfo]$_
                    $_ = $RebaseBackupFolder + "\" + $file.BaseName + $file.Extension
                }
            }
            $History
        }
    }
}
function Get-DbaAgDatabase {
    <#
        .SYNOPSIS
            Outputs the databases involved in the Availability Group(s) found on the server.

        .DESCRIPTION
            Default view provides most common set of properties for information on the database in an Availability Group(s).

            Information returned on the database will be specific to that replica, whether it is primary or a secondary.

            This command will return an SMO object, but it is the AvailabilityDatabases object and not the Server.Databases object.

        .PARAMETER SqlInstance
            The SQL Server instance. Server version must be SQL Server version 2012 or higher.

        .PARAMETER SqlCredential
            Allows you to login to servers using SQL Logins instead of Windows Authentication (AKA Integrated or Trusted).

        .PARAMETER AvailabilityGroup
            Specify the Availability Group name that you want to get information on.

        .PARAMETER Database
            Specify the database(s) to pull information for. This list is auto-populated from the server for tab completion. Multiple databases can be specified. If none are specified all databases will be processed.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Hadr, AG, AvailabilityGroup, Replica
            Author: Shawn Melton (@wsmelton)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaAgDatabase

        .EXAMPLE
            Get-DbaAgDatabase -SqlInstance sqlserver2014a

            Returns basic information on all the databases in each Availability Group found on sqlserver2014a

        .EXAMPLE
            Get-DbaAgDatabase -SqlInstance sqlserver2014a -AvailabilityGroup AG-a

            Returns basic information on all the databases in the Availability Group AG-a on sqlserver2014a

        .EXAMPLE
            Get-DbaAgDatabase -SqlInstance sqlserver2014a -AvailabilityGroup AG-a -Database AG-Database

            Returns basic information on the database AG-Database found in the Availability Group AG-a on server sqlserver2014a
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(ValueFromPipeline = $true)]
        [object[]]$AvailabilityGroup,
        [object[]]$Database,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($serverName in $SqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $serverName -SqlCredential $SqlCredential -MinimumVersion 11
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($server.IsHadrEnabled -eq $false) {
                Stop-Function -Message "Availability Group (HADR) is not configured for the instance: $serverName." -Target $serverName -Continue
            }

            $ags = $server.AvailabilityGroups
            if ($AvailabilityGroup) {
                $ags = $ags | Where-Object Name -in $AvailabilityGroup
            }

            foreach ($ag in $ags) {
                $agDatabases = $ag.AvailabilityDatabases
                foreach ($agDb in $agDatabases) {
                    if ($Database -and $agDb.Name -notmatch $Database) {
                        continue
                    }

                    Add-Member -Force -InputObject $agDb -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                    Add-Member -Force -InputObject $agDb -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                    Add-Member -Force -InputObject $agDb -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                    Add-Member -Force -InputObject $agDb -MemberType NoteProperty -Name Replica -value $server.ComputerName

                    $defaults = 'ComputerName', 'InstanceName', 'SqlInstance', 'Parent as AvailabilityGroup', 'Replica', 'Name as DatabaseName', 'SynchronizationState', 'IsFailoverReady', 'IsJoined', 'IsSuspended'
                    Select-DefaultView -InputObject $agDb -Property $defaults
                }
            }
        }
    }
}
function Get-DbaAgentAlert {
    <#
        .SYNOPSIS
            Returns all SQL Agent alerts on a SQL Server Agent.

        .DESCRIPTION
            This function returns SQL Agent alerts.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .NOTES
            Author: Klaas Vandenberghe ( @PowerDBAKlaas )
            Tags: Agent, SMO
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .LINK
            https://dbatools.io/Get-DbaAgentAlert

        .EXAMPLE
            Get-DbaAgentAlert -SqlInstance ServerA,ServerB\instanceB
            Returns all SQL Agent alerts on serverA and serverB\instanceB

        .EXAMPLE
            'serverA','serverB\instanceB' | Get-DbaAgentAlert
            Returns all SQL Agent alerts  on serverA and serverB\instanceB
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "Instance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]
        $SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException

    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            Write-Message -Level Verbose -Message "Getting Edition from $server"
            Write-Message -Level Verbose -Message "$server is a $($server.Edition)"

            if ($server.Edition -like 'Express*') {
                Stop-Function -Message "There is no SQL Agent on $server, it's a $($server.Edition)" -Continue
            }

            $defaults = "ComputerName", "SqlInstance", "InstanceName", "Name", "ID", "JobName", "AlertType", "CategoryName", "Severity", "IsEnabled", "DelayBetweenResponses", "LastRaised", "OccurrenceCount"

            $alerts = $server.Jobserver.Alerts

            foreach ($alert in $alerts) {
                $lastraised = [dbadatetime]$alert.LastOccurrenceDate

                Add-Member -Force -InputObject $alert -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                Add-Member -Force -InputObject $alert -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                Add-Member -Force -InputObject $alert -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                Add-Member -Force -InputObject $alert -MemberType NoteProperty Notifications -value $alert.EnumNotifications()
                Add-Member -Force -InputObject $alert -MemberType NoteProperty LastRaised -value $lastraised

                Select-DefaultView -InputObject $alert -Property $defaults
            }
        }
    }
}
#ValidationTags#Messaging#
function Get-DbaAgentJob {
    <#
        .SYNOPSIS
            Gets SQL Agent Job information for each instance(s) of SQL Server.

        .DESCRIPTION
            The Get-DbaAgentJob returns connected SMO object for SQL Agent Job information for each instance(s) of SQL Server.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Job
            The job(s) to process - this list is auto-populated from the server. If unspecified, all jobs will be processed.

        .PARAMETER ExcludeJob
            The job(s) to exclude - this list is auto-populated from the server.

        .PARAMETER NoDisabledJobs
            Switch will exclude disabled jobs from the output.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Job, Agent
            Author: Garry Bargsley (@gbargsley), http://blog.garrybargsley.com

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaAgentJob

        .EXAMPLE
            Get-DbaAgentJob -SqlInstance localhost

            Returns all SQL Agent Jobs on the local default SQL Server instance

        .EXAMPLE
            Get-DbaAgentJob -SqlInstance localhost, sql2016

            Returns all SQl Agent Jobs for the local and sql2016 SQL Server instances

        .EXAMPLE
            Get-DbaAgentJob -SqlInstance localhost -Job BackupData, BackupDiff

            Returns all SQL Agent Jobs named BackupData and BackupDiff from the local SQL Server instance.

        .EXAMPLE
            Get-DbaAgentJob -SqlInstance localhost -ExcludeJob BackupDiff

            Returns all SQl Agent Jobs for the local SQL Server instances, except the BackupDiff Job.

        .EXAMPLE
            Get-DbaAgentJob -SqlInstance localhost -NoDisabledJobs

            Returns all SQl Agent Jobs for the local SQL Server instances, excluding the disabled jobs.

        .EXAMPLE
            $servers | Get-DbaAgentJob | Out-GridView -Passthru | Start-DbaAgentJob -WhatIf

            Find all of your Jobs from servers in the $server collection, select the jobs you want to start then see jobs would start if you ran Start-DbaAgentJob
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Job,
        [object[]]$ExcludeJob,
        [switch]$NoDisabledJobs,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $jobs = $server.JobServer.Jobs

            if ($Job) {
                $jobs = $jobs | Where-Object Name -In $Job
            }
            if ($ExcludeJob) {
                $jobs = $jobs | Where-Object Name -NotIn $ExcludeJob
            }
            if ($NoDisabledJobs) {
                $jobs = $Jobs | Where-Object IsEnabled -eq $true
            }

            foreach ($agentJob in $jobs) {
                Add-Member -Force -InputObject $agentJob -MemberType NoteProperty -Name ComputerName -value $agentJob.Parent.Parent.ComputerName
                Add-Member -Force -InputObject $agentJob -MemberType NoteProperty -Name InstanceName -value $agentJob.Parent.Parent.ServiceName
                Add-Member -Force -InputObject $agentJob -MemberType NoteProperty -Name SqlInstance -value $agentJob.Parent.Parent.DomainInstanceName

                Select-DefaultView -InputObject $agentJob -Property ComputerName, InstanceName, SqlInstance, Name, Category, OwnerLoginName, CurrentRunStatus, CurrentRunRetryAttempt, 'IsEnabled as Enabled', LastRunDate, LastRunOutcome, DateCreated, HasSchedule, OperatorToEmail, 'DateCreated as CreateDate'
            }
        }
    }
}
function Get-DbaAgentJobCategory {
    <#
        .SYNOPSIS
            Get-DbaAgentJobCategory retrieves the job categories.

        .DESCRIPTION
            Get-DbaAgentJobCategory makes it possible to retrieve the job categories.

        .PARAMETER SqlInstance
             SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Category
            The name of the category to filter out. If no category is used all catgories will be returned.

        .PARAMETER CategoryType
            The type of category. This can be "LocalJob", "MultiServerJob" or "None".
            If no category is used all catgories types will be returned.

        .PARAMETER Force
            The force parameter will ignore some errors in the parameters and assume defaults.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Agent, Job, JobCategory
            Author: Sander Stad (@sqlstad, sqlstad.nl)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaAgentJobCategory

        .EXAMPLE
            Get-DbaAgentJobCategory -SqlInstance sql1

            Return all the job categories.

        .EXAMPLE
            Get-DbaAgentJobCategory -SqlInstance sql1 -Category 'Log Shipping'

            Return all the job categories that have the name 'Log Shipping'.

        .EXAMPLE
            Get-DbaAgentJobCategory -SqlInstance sstad-pc -CategoryType MultiServerJob

            Return all the job categories that have a type MultiServerJob.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [ValidateNotNullOrEmpty()]
        [string[]]$Category,
        [ValidateSet("LocalJob", "MultiServerJob", "None")]
        [string]$CategoryType,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {

        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance."
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            # get all the job categories
            $jobCategories = $server.JobServer.JobCategories |
                Where-Object {
                ($_.Name -in $Category -or !$Category) -and
                ($_.CategoryType -in $CategoryType -or !$CategoryType)
            }

            # Set the default output
            $defaults = 'ComputerName', 'InstanceName', 'SqlInstance', 'Name', 'ID', 'CategoryType', 'JobCount'

            # Loop through each of the categories
            try {
                foreach ($cat in $jobCategories) {

                    # Get the jobs associated with the category
                    $jobCount = ($server.JobServer.Jobs | Where-Object {$_.CategoryID -eq $cat.ID}).Count

                    # Add new properties to the category object
                    Add-Member -Force -InputObject $cat -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                    Add-Member -Force -InputObject $cat -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                    Add-Member -Force -InputObject $cat -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                    Add-Member -Force -InputObject $cat -MemberType NoteProperty -Name JobCount -Value $jobCount

                    # Show the result
                    Select-DefaultView -InputObject $cat -Property $defaults
                }
            }
            catch {
                Stop-Function -ErrorRecord $_ -Target $instance -Message "Failure. Collection may have been modified" -Continue
            }

        } # for each instance

    } # end process

    end {
        if (Test-FunctionInterrupt) { return }
        Write-Message -Message "Finished retrieving job category." -Level Verbose
    }

}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaAgentJobHistory {
    <#
        .SYNOPSIS
            Gets execution history of SQL Agent Job on instance(s) of SQL Server.

        .DESCRIPTION
            Get-DbaAgentJobHistory returns all information on the executions still available on each instance(s) of SQL Server submitted.
            The cleanup of SQL Agent history determines how many records are kept.

            https://msdn.microsoft.com/en-us/library/ms201680.aspx
            https://msdn.microsoft.com/en-us/library/microsoft.sqlserver.management.smo.agent.jobhistoryfilter(v=sql.120).aspx

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Job
            The name of the job from which the history is wanted. If unspecified, all jobs will be processed.

        .PARAMETER ExcludeJob
            The job(s) to exclude - this list is auto-populated from the server

        .PARAMETER StartDate
            The DateTime starting from which the history is wanted. If unspecified, all available records will be processed.

        .PARAMETER EndDate
            The DateTime before which the history is wanted. If unspecified, all available records will be processed.

        .PARAMETER NoJobSteps
            Use this switch to discard all job steps, and return only the job totals

        .PARAMETER WithOutputFile
            Use this switch to retrieve the output file (only if you want step details). Bonus points, we handle the quirks
            of SQL Agent tokens to the best of our knowledge (https://technet.microsoft.com/it-it/library/ms175575(v=sql.110).aspx)

        .PARAMETER JobCollection
            An array of SMO jobs

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Job, Agent
            Author: Klaas Vandenberghe ( @PowerDbaKlaas )
            Editor: niphlod

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaAgentJobHistory

        .EXAMPLE
            Get-DbaAgentJobHistory -SqlInstance localhost

            Returns all SQL Agent Job execution results on the local default SQL Server instance.

        .EXAMPLE
            Get-DbaAgentJobHistory -SqlInstance localhost, sql2016

            Returns all SQL Agent Job execution results for the local and sql2016 SQL Server instances.

        .EXAMPLE
            'sql1','sql2\Inst2K17' | Get-DbaAgentJobHistory

            Returns all SQL Agent Job execution results for sql1 and sql2\Inst2K17.

        .EXAMPLE
            Get-DbaAgentJobHistory -SqlInstance sql2\Inst2K17 | select *

            Returns all properties for all SQl Agent Job execution results on sql2\Inst2K17.

        .EXAMPLE
            Get-DbaAgentJobHistory -SqlInstance sql2\Inst2K17 -Job 'Output File Cleanup'

            Returns all properties for all SQl Agent Job execution results of the 'Output File Cleanup' job on sql2\Inst2K17.


        .EXAMPLE
            Get-DbaAgentJobHistory -SqlInstance sql2\Inst2K17 -Job 'Output File Cleanup' -WithOutputFile

            Returns all properties for all SQl Agent Job execution results of the 'Output File Cleanup' job on sql2\Inst2K17,
            with additional properties that show the output filename path

        .EXAMPLE
            Get-DbaAgentJobHistory -SqlInstance sql2\Inst2K17 -NoJobSteps

            Returns the SQL Agent Job execution results for the whole jobs on sql2\Inst2K17, leaving out job step execution results.

        .EXAMPLE
            Get-DbaAgentJobHistory -SqlInstance sql2\Inst2K17 -StartDate '2017-05-22' -EndDate '2017-05-23 12:30:00'

            Returns the SQL Agent Job execution results between 2017/05/22 00:00:00 and 2017/05/23 12:30:00 on sql2\Inst2K17.

        .EXAMPLE
            Get-DbaAgentJob -SqlInstance sql2016 | Where Name -match backup | Get-DbaAgentJobHistory

            Gets all jobs with the name that match the regex pattern "backup" and then gets the job history from those. You can also use -Like *backup* in this example.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    param (
        [parameter(Mandatory, ValueFromPipeline, ParameterSetName = "Server")]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]
        $SqlCredential,
        [object[]]$Job,
        [object[]]$ExcludeJob,
        [DateTime]$StartDate = "1900-01-01",
        [DateTime]$EndDate = $(Get-Date),
        [switch]$NoJobSteps,
        [switch]$WithOutputFile,
        [parameter(Mandatory, ValueFromPipeline, ParameterSetName = "Collection")]
        [Microsoft.SqlServer.Management.Smo.Agent.Job]$JobCollection,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $filter = New-Object Microsoft.SqlServer.Management.Smo.Agent.JobHistoryFilter
        $filter.StartRunDate = $StartDate
        $filter.EndRunDate = $EndDate


        if ($NoJobSteps -and $WithOutputFile) {
            Stop-Function -Message "You can't use -NoJobSteps and -WithOutputFile together"
        }

        function Get-JobHistory {
            [CmdletBinding()]
            param (
                $Server,
                $Job,
                [switch]$WithOutputFile
            )
            $tokenrex = [regex]'\$\((?<method>[^()]+)\((?<tok>[^)]+)\)\)|\$\((?<tok>[^)]+)\)'
            $propmap = @{
                'INST'      = $Server.ServiceName
                'MACH'      = $Server.ComputerName
                'SQLDIR'    = $Server.InstallDataDirectory
                'SQLLOGDIR' = $Server.ErrorLogPath
                #'STEPCT' loop number ?
                'SRVR'      = $Server.DomainInstanceName
                # WMI( property ) impossible
            }


            $squote_rex = [regex]"(?<!')'(?!')"
            $dquote_rex = [regex]'(?<!")"(?!")'
            $rbrack_rex = [regex]'(?<!])](?!])'

            function Resolve-TokenEscape($method, $value) {
                if (!$method) {
                    return $value
                }
                $value = switch ($method) {
                    'ESCAPE_SQUOTE' { $squote_rex.Replace($value, "''") }
                    'ESCAPE_DQUOTE' { $dquote_rex.Replace($value, '""') }
                    'ESCAPE_RBRACKET' { $rbrack_rex.Replace($value, ']]') }
                    'ESCAPE_NONE' { $value }
                    default { $value }
                }
                return $value
            }

            #'STEPID' =  stepid
            #'STRTTM' job begin time
            #'STRTDT' job begin date
            #'JOBID' = JobId
            function Resolve-JobToken($exec, $outfile, $outcome) {
                $n = $tokenrex.Matches($outfile)
                foreach ($x in $n) {
                    $tok = $x.Groups['tok'].Value
                    $EscMethod = $x.Groups['method'].Value
                    if ($propmap.containskey($tok)) {
                        $repl = Resolve-TokenEscape -method $EscMethod -value $propmap[$tok]
                        $outfile = $outfile.Replace($x.Value, $repl)
                    }
                    elseif ($tok -eq 'STEPID') {
                        $repl = Resolve-TokenEscape -method $EscMethod -value $exec.StepID
                        $outfile = $outfile.Replace($x.Value, $repl)
                    }
                    elseif ($tok -eq 'JOBID') {
                        # convert(binary(16), ?)
                        $repl = @('0x') + @($exec.JobID.ToByteArray() | foreach { $_.ToString('X2') }) -join ''
                        $repl = Resolve-TokenEscape -method $EscMethod -value $repl
                        $outfile = $outfile.Replace($x.Value, $repl)
                    }
                    elseif ($tok -eq 'STRTDT') {
                        $repl = Resolve-TokenEscape -method $EscMethod -value $outcome.RunDate.toString('yyyyMMdd')
                        $outfile = $outfile.Replace($x.Value, $repl)
                    }
                    elseif ($tok -eq 'STRTTM') {
                        $repl = Resolve-TokenEscape -method $EscMethod -value ([int]$outcome.RunDate.toString('HHmmss')).toString()
                        $outfile = $outfile.Replace($x.Value, $repl)
                    }
                    elseif ($tok -eq 'DATE') {
                        $repl = Resolve-TokenEscape -method $EscMethod -value $exec.RunDate.toString('yyyyMMdd')
                        $outfile = $outfile.Replace($x.Value, $repl)
                    }
                    elseif ($tok -eq 'TIME') {
                        $repl = Resolve-TokenEscape -method $EscMethod -value ([int]$exec.RunDate.toString('HHmmss')).toString()
                        $outfile = $outfile.Replace($x.Value, $repl)
                    }
                }
                return $outfile
            }
            try {
                Write-Message -Message "Attempting to get job history from $instance" -Level Verbose
                if ($Job) {
                    foreach ($currentjob in $Job) {
                        $filter.JobName = $currentjob
                        $executions += $server.JobServer.EnumJobHistory($filter)
                    }
                }
                else {
                    $executions = $server.JobServer.EnumJobHistory($filter)
                }
                if ($NoJobSteps) {
                    $executions = $executions | Where-Object { $_.StepID -eq 0 }
                }

                if ($WithOutputFile) {
                    $outmap = @{}
                    $outfiles = Get-DbaAgentJobOutputFile -SqlInstance $Server -SqlCredential $SqlCredential -Job $Job

                    foreach ($out in $outfiles) {
                        if (!$outmap.ContainsKey($out.Job)) {
                            $outmap[$out.Job] = @{}
                        }
                        $outmap[$out.Job][$out.StepId] = $out.OutputFileName
                    }
                }
                $outcome = [pscustomobject]@{}
                foreach ($execution in $executions) {
                    $status = switch ($execution.RunStatus) {
                        0 { "Failed" }
                        1 { "Succeeded" }
                        2 { "Retry" }
                        3 { "Canceled" }
                    }

                    Add-Member -Force -InputObject $execution -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                    Add-Member -Force -InputObject $execution -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                    Add-Member -Force -InputObject $execution -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                    $DurationInSeconds = ($execution.RunDuration % 100) + [int]( ($execution.RunDuration % 10000 ) / 100 ) * 60 + [int]( ($execution.RunDuration % 1000000 ) / 10000 ) * 60 * 60
                    Add-Member -Force -InputObject $execution -MemberType NoteProperty -Name StartDate -value ([dbadatetime]$execution.RunDate)
                    Add-Member -Force -InputObject $execution -MemberType NoteProperty -Name EndDate -value ([dbadatetime]$execution.RunDate.AddSeconds($DurationInSeconds))
                    Add-Member -Force -InputObject $execution -MemberType NoteProperty -Name Duration -value ([prettytimespan](New-TimeSpan -Seconds $DurationInSeconds))
                    Add-Member -Force -InputObject $execution -MemberType NoteProperty -Name Status -value $status
                    if ($WithOutputFile) {
                        if ($execution.StepID -eq 0) {
                            $outcome = $execution
                        }
                        try {
                            $outname = $outmap[$execution.JobName][$execution.StepID]
                            $outname = Resolve-JobToken -exec $execution -outcome $outcome -outfile $outname
                            $outremote = Join-AdminUNC $Server.ComputerName $outname
                        }
                        catch {
                            $outname = ''
                            $outremote = ''
                        }
                        Add-Member -Force -InputObject $execution -MemberType NoteProperty -Name OutputFileName -value $outname
                        Add-Member -Force -InputObject $execution -MemberType NoteProperty -Name RemoteOutputFileName -value $outremote
                        Select-DefaultView -InputObject $execution -Property ComputerName, InstanceName, SqlInstance, 'JobName as Job', StepName, RunDate, StartDate, EndDate, Duration, Status, OperatorEmailed, Message, OutputFileName, RemoteOutputFileName
                    }
                    else {
                        Select-DefaultView -InputObject $execution -Property ComputerName, InstanceName, SqlInstance, 'JobName as Job', StepName, RunDate, StartDate, EndDate, Duration, Status, OperatorEmailed, Message
                    }

                }
            }
            catch {
                Stop-Function -Message "Could not get Agent Job History from $instance" -Target $instance -Continue
            }
        }
    }

    process {

        if (Test-FunctionInterrupt) { return }

        if ($JobCollection) {
            foreach ($currentjob in $JobCollection) {
                Get-JobHistory -Server $currentjob.Parent.Parent -Job $currentjob.Name -WithOutputFile:$WithOutputFile
            }
        }

        foreach ($instance in $SqlInstance) {
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }


            if ($ExcludeJob) {
                $jobs = $server.JobServer.Jobs.Name | Where-Object { $_ -notin $ExcludeJob }
                foreach ($currentjob in $jobs) {
                    Get-JobHistory -Server $server -Job $currentjob -WithOutputFile:$WithOutputFile
                }
            }
            else {
                Get-JobHistory -Server $server -Job $Job -WithOutputFile:$WithOutputFile
            }
        }
    }
}
function Get-DbaAgentJobOutputFile {
    <#
        .Synopsis
            Returns the Output File for each step of one or many agent job with the Job Names provided dynamically if
            required for one or more SQL Instances

        .DESCRIPTION
            This function returns for one or more SQL Instances the output file value for each step of one or many agent job with the Job Names
            provided dynamically. It will not return anything if there is no Output File

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SQLCredential
            Credential object used to connect to the SQL Server as a different user be it Windows or SQL Server. Windows users are determiend by
            the existence of a backslash, so if you are intending to use an alternative Windows connection instead of a SQL login, ensure it
            contains a backslash.

        .PARAMETER Job
            The job(s) to process - this list is auto-populated from the server. If unspecified, all jobs will be processed.

        .PARAMETER ExcludeJob
            The job(s) to exclude - this list is auto-populated from the server

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Agent, Job
            Author: Rob Sewell (https://sqldbawithabeard.com)
            Editor: niphlod

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            Get-DbaAgentJobOutputFile -SqlInstance SERVERNAME -Job 'The Agent Job'

            This will return the configured paths to the output files for each of the job step of the The Agent Job Job
            on the SERVERNAME instance

        .EXAMPLE
            Get-DbaAgentJobOutputFile -SqlInstance SERVERNAME

            This will return the configured paths to the output files for each of the job step of all the Agent Jobs
            on the SERVERNAME instance

        .EXAMPLE
            Get-DbaAgentJobOutputFile -SqlInstance SERVERNAME,SERVERNAME2 -Job 'The Agent Job'

            This will return the configured paths to the output files for each of the job step of the The Agent Job Job
            on the SERVERNAME instance and SERVERNAME2

        .EXAMPLE
            $Servers = 'SERVER','SERVER\INSTANCE1'
            Get-DbaAgentJobOutputFile -SqlInstance $Servers -Job 'The Agent Job' -OpenFile

            This will return the configured paths to the output files for each of the job step of the The Agent Job Job
            on the SERVER instance and the SERVER\INSTANCE1 and open the files if they are available

        .EXAMPLE
            Get-DbaAgentJobOutputFile -SqlInstance SERVERNAME  | Out-GridView

            This will return the configured paths to the output files for each of the job step of all the Agent Jobs
            on the SERVERNAME instance and Pipe them to Out-GridView

        .EXAMPLE
            (Get-DbaAgentJobOutputFile -SqlInstance SERVERNAME | ogv -PassThru).FileName | Invoke-Item

            This will return the configured paths to the output files for each of the job step of all the Agent Jobs
            on the SERVERNAME instance and Pipe them to Out-GridView and enable you to choose the output
            file and open it

        .EXAMPLE
            Get-DbaAgentJobOutputFile -SqlInstance SERVERNAME -Verbose

            This will return the configured paths to the output files for each of the job step of all the Agent Jobs
            on the SERVERNAME instance and also show the job steps without an output file
    #>
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true, HelpMessage = 'The SQL Server Instance',
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false,
            Position = 0)]
        [ValidateNotNull()]
        [ValidateNotNullOrEmpty()]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Parameter(Mandatory = $false, HelpMessage = 'SQL Credential',
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false,
            Position = 1)]
        [PSCredential]$SqlCredential,
        [object[]]$Job,
        [object[]]$ExcludeJob,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $sqlinstance) {
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $jobs = $Server.JobServer.Jobs
            if ($Job) {
                $jobs = $jobs | Where-Object Name -In $Job
            }
            if ($ExcludeJob) {
                $jobs = $jobs | Where-Object Name -NotIn $ExcludeJob
            }
            foreach ($j in $Jobs) {
                foreach ($Step in $j.JobSteps) {
                    if ($Step.OutputFileName) {
                        [pscustomobject]@{
                            ComputerName         = $server.ComputerName
                            InstanceName         = $server.ServiceName
                            SqlInstance          = $server.DomainInstanceName
                            Job                  = $j.Name
                            JobStep              = $Step.Name
                            OutputFileName       = $Step.OutputFileName
                            RemoteOutputFileName = Join-AdminUNC $Server.ComputerName $Step.OutputFileName
                            StepId               = $Step.Id
                        } | Select-DefaultView -ExcludeProperty StepId
                    }
                    else {
                        Write-Message -Level Verbose -Message "$step for $j has no output file"
                    }
                }
            }
        }
    }
}
function Get-DbaAgentJobStep {
    <#
        .SYNOPSIS
            Gets SQL Agent Job Step information for each instance(s) of SQL Server.

        .DESCRIPTION
            The Get-DbaAgentJobStep returns connected SMO object for SQL Agent Job Step for each instance(s) of SQL Server.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Job
            The job(s) to process - this list is auto-populated from the server. If unspecified, all jobs will be processed.

        .PARAMETER ExcludeJob
            The job(s) to exclude - this list is auto-populated from the server.

        .PARAMETER NoDisabledJobs
            Switch will exclude disabled jobs from the output.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Job, Agent
            Author: Klaas Vandenberghe (@PowerDbaKlaas), http://powerdba.eu

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaAgentJobStep

        .EXAMPLE
            Get-DbaAgentJobStep -SqlInstance localhost

            Returns all SQL Agent Job Steps on the local default SQL Server instance

        .EXAMPLE
            Get-DbaAgentJobStep -SqlInstance localhost, sql2016

            Returns all SQl Agent Job Steps for the local and sql2016 SQL Server instances

        .EXAMPLE
            Get-DbaAgentJobStep -SqlInstance localhost -Job BackupData, BackupDiff

            Returns all SQL Agent Job Steps for the jobs named BackupData and BackupDiff from the local SQL Server instance.

        .EXAMPLE
            Get-DbaAgentJobStep -SqlInstance localhost -ExcludeJob BackupDiff

            Returns all SQl Agent Job Steps for the local SQL Server instances, except for the BackupDiff Job.

        .EXAMPLE
            Get-DbaAgentJobStep -SqlInstance localhost -NoDisabledJobs

            Returns all SQl Agent Job Steps for the local SQL Server instances, excluding the disabled jobs.

        .EXAMPLE
            $servers | Get-DbaAgentJobStep

            Find all of your Job Steps from servers in the $server collection
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]
        $SqlCredential,
        [object[]]$Job,
        [object[]]$ExcludeJob,
        [switch]$NoDisabledJobs,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            Write-Message -Level Verbose -Message "Collecting jobs on $instance"
            $jobs = $server.JobServer.Jobs

            if ($Job) {
                $jobs = $jobs | Where-Object Name -In $Job
            }
            if ($ExcludeJob) {
                $jobs = $jobs | Where-Object Name -NotIn $ExcludeJob
            }
            if ($NoDisabledJobs) {
                $jobs = $Jobs | Where-Object IsEnabled -eq $true
            }
            Write-Message -Level Verbose -Message "Collecting job steps on $instance"
            foreach ($agentJobStep in $jobs.jobsteps) {
                Add-Member -Force -InputObject $agentJobStep -MemberType NoteProperty -Name ComputerName -value $agentJobStep.Parent.Parent.Parent.ComputerName
                Add-Member -Force -InputObject $agentJobStep -MemberType NoteProperty -Name InstanceName -value $agentJobStep.Parent.Parent.Parent.ServiceName
                Add-Member -Force -InputObject $agentJobStep -MemberType NoteProperty -Name SqlInstance -value $agentJobStep.Parent.Parent.Parent.DomainInstanceName
                Add-Member -Force -InputObject $agentJobStep -MemberType NoteProperty -Name AgentJob -value $agentJobStep.Parent.Name

                Select-DefaultView -InputObject $agentJobStep -Property ComputerName, InstanceName, SqlInstance, AgentJob, Name, SubSystem, LastRunDate, LastRunOutcome, State
            }
        }
    }
}
function Get-DbaAgentLog {
    <#
    .SYNOPSIS
        Gets the "SQL Agent Error Log" of an instance

    .DESCRIPTION
        Gets the "SQL Agent Error Log" of an instance. Returns all 10 error logs by default.

    .PARAMETER SqlInstance
        SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

    .PARAMETER SqlCredential
        Allows you to login to servers using SQL Logins as opposed to Windows Auth/Integrated/Trusted.

    .PARAMETER LogNumber
        An Int32 value that specifies the index number of the error log required. Error logs are listed 0 through 9 where 0 is the current error log and 9 is the oldest.

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Tags: Logging
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Get-DbaAgentLog

    .EXAMPLE
        Get-DbaAgentLog -SqlInstance sql01\sharepoint

        Returns the entire error log for the SQL Agent on sql01\sharepoint

    .EXAMPLE
        Get-DbaAgentLog -SqlInstance sql01\sharepoint -LogNumber 3, 6

        Returns log numbers 3 and 6 for the SQL Agent on sql01\sharepoint

    .EXAMPLE
        $servers = "sql2014","sql2016", "sqlcluster\sharepoint"
        $servers | Get-DbaAgentLog -LogNumber 0

        Returns the most recent SQL Agent error logs for "sql2014","sql2016" and "sqlcluster\sharepoint"
#>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [ValidateRange(0, 9)]
        [int[]]$LogNumber,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($LogNumber) {
                foreach ($number in $lognumber) {
                    try {
                        foreach ($object in $server.JobServer.ReadErrorLog($number)) {
                            Write-Message -Level Verbose -Message "Processing $object"
                            Add-Member -Force -InputObject $object -MemberType NoteProperty ComputerName -value $server.ComputerName
                            Add-Member -Force -InputObject $object -MemberType NoteProperty InstanceName -value $server.ServiceName
                            Add-Member -Force -InputObject $object -MemberType NoteProperty SqlInstance -value $server.DomainInstanceName

                            # Select all of the columns you'd like to show
                            Select-DefaultView -InputObject $object -Property ComputerName, InstanceName, SqlInstance, LogDate, ProcessInfo, Text
                        }
                    }
                    catch {
                        Stop-Function -Continue -Target $server -Message "Could not read from SQL Server Agent"
                    }
                }
            }
            else {
                try {
                    foreach ($object in $server.JobServer.ReadErrorLog()) {
                        Write-Message -Level Verbose -Message "Processing $object"
                        Add-Member -Force -InputObject $object -MemberType NoteProperty ComputerName -value $server.ComputerName
                        Add-Member -Force -InputObject $object -MemberType NoteProperty InstanceName -value $server.ServiceName
                        Add-Member -Force -InputObject $object -MemberType NoteProperty SqlInstance -value $server.DomainInstanceName

                        # Select all of the columns you'd like to show
                        Select-DefaultView -InputObject $object -Property ComputerName, InstanceName, SqlInstance, LogDate, ProcessInfo, Text
                    }
                }
                catch {
                    Stop-Function -Continue -Target $server -Message "Could not read from SQL Server Agent"
                }
            }
        }
    }
}
function Get-DbaAgentOperator {
    <#
        .SYNOPSIS
            Returns all SQL Agent operators on a SQL Server Agent.

        .DESCRIPTION
            This function returns SQL Agent operators.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Operator
            The operator(s) to process - this list is auto-populated from the server. If unspecified, all operators will be processed.

        .PARAMETER ExcludeOperator
            The operator(s) to exclude - this list is auto-populated from the server

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Agent, Operator
            Author: Klaas Vandenberghe ( @PowerDBAKlaas )

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaAgentOperator

        .EXAMPLE
            Get-DbaAgentOperator -SqlInstance ServerA,ServerB\instanceB

            Returns any SQL Agent operators on serverA and serverB\instanceB

        .EXAMPLE
            'ServerA','ServerB\instanceB' | Get-DbaAgentOperator

            Returns all SQL Agent operators  on serverA and serverB\instanceB

        .EXAMPLE
            Get-DbaAgentOperator -SqlInstance ServerA -Operator Dba1,Dba2

            Returns only the SQL Agent Operators Dba1 and Dba2 on ServerA.

        .EXAMPLE
            Get-DbaAgentOperator -SqlInstance ServerA,ServerB -ExcludeOperator Dba3

            Returns all the SQL Agent operators on ServerA and ServerB, except the Dba3 operator.
    #>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]
        $SqlCredential,
        [object[]]$Operator,
        [object[]]$ExcludeOperator,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            Write-Message -Level Verbose -Message "Getting Edition from $server"
            Write-Message -Level Verbose -Message "$server is a $($server.Edition)"

            if ($server.Edition -like 'Express*') {
                Stop-Function -Message "There is no SQL Agent on $server, it's a $($server.Edition)" -Continue -Target $server
            }

            $defaults = "ComputerName", "SqlInstance", "InstanceName", "Name", "ID", "Enabled as IsEnabled", "EmailAddress", "LastEmail"

            if ($Operator) {
                $operators = $server.JobServer.Operators | Where-Object Name -In $Operator
            }
            elseif ($ExcludeOperator) {
                $operators = $server.JobServer.Operators | Where-Object Name -NotIn $ExcludeOperator
            }
            else {
                $operators = $server.JobServer.Operators
            }

            foreach ($operat in $operators) {

                $jobs = $server.JobServer.jobs | Where-Object { $_.OperatorToEmail, $_.OperatorToNetSend, $_.OperatorToPage -contains $operat.Name }
                $lastemail = [dbadatetime]$operat.LastEmailDate

                Add-Member -Force -InputObject $operat -MemberType NoteProperty -Name ComputerName -Value $server.ComputerName
                Add-Member -Force -InputObject $operat -MemberType NoteProperty -Name InstanceName -Value $server.ServiceName
                Add-Member -Force -InputObject $operat -MemberType NoteProperty -Name SqlInstance -Value $server.DomainInstanceName
                Add-Member -Force -InputObject $operat -MemberType NoteProperty -Name RelatedJobs -Value $jobs
                Add-Member -Force -InputObject $operat -MemberType NoteProperty -Name LastEmail -Value $lastemail
                Select-DefaultView -InputObject $operat -Property $defaults
            }
        }
    }
}
function Get-DbaAgentProxy {
    <#
        .SYNOPSIS
            Returns all SQL Agent proxies on a SQL Server Agent.

        .DESCRIPTION
            This function returns SQL Agent proxies.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Chrissy LeMaire ( @cl )
            Tags: Agent, SMO
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaAgentProxy

        .EXAMPLE
            Get-DbaAgentProxy -SqlInstance ServerA,ServerB\instanceB
            Returns all SQL Agent proxies on serverA and serverB\instanceB

        .EXAMPLE
            'serverA','serverB\instanceB' | Get-DbaAgentProxy
            Returns all SQL Agent proxies  on serverA and serverB\instanceB
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "Instance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            
            Write-Message -Level Verbose -Message "Getting Edition from $server"
            Write-Message -Level Verbose -Message "$server is a $($server.Edition)"
            
            if ($server.Edition -like 'Express*') {
                Stop-Function -Message "There is no SQL Agent on $server, it's a $($server.Edition)" -Continue
            }
            
            $defaults = "ComputerName", "SqlInstance", "InstanceName", "Name", "ID", "CredentialID", "CredentialIdentity", "CredentialName", "Description", "IsEnabled"
            
            $proxies = $server.Jobserver.ProxyAccounts
            
            foreach ($proxy in $proxies) {
                Add-Member -Force -InputObject $proxy -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                Add-Member -Force -InputObject $proxy -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                Add-Member -Force -InputObject $proxy -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                Select-DefaultView -InputObject $proxy -Property $defaults
            }
        }
    }
}
function Get-DbaAgentSchedule {
    <#
        .SYNOPSIS
            Returns all SQL Agent Shared Schedules on a SQL Server Agent.

        .DESCRIPTION
            This function returns SQL Agent Shared Schedules.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Schedule
            Parameter to filter the schedules returned

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Agent, Schedule
            Author: Chris McKeown (@devopsfu), http://www.devopsfu.com

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaAgentSchedule

        .EXAMPLE
            Get-DbaAgentSchedule -SqlInstance localhost

            Returns all SQL Agent Shared Schedules on the local default SQL Server instance

        .EXAMPLE
            Get-DbaAgentSchedule -SqlInstance localhost, sql2016

            Returns all SQL Agent Shared Schedules for the local and sql2016 SQL Server instances
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "Instance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Schedules")]
        [object[]]$Schedule,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        function Get-ScheduleDescription {
            param (
                [Parameter(Mandatory = $true)]
                [ValidateNotNullOrEmpty()]
                [object]$Schedule

            )

            # Get the culture to make sure the right date and time format is displayed
            $datetimeFormat = (Get-culture).DateTimeFormat

            # Set the intial description
            $description = ""

            # Get the date and time values
            $startDate = Get-Date $Schedule.ActiveStartDate -format $datetimeFormat.ShortDatePattern
            $startTime = Get-Date ($Schedule.ActiveStartTimeOfDay.ToString()) -format $datetimeFormat.LongTimePattern
            $endDate = Get-Date $Schedule.ActiveEndDate -format $datetimeFormat.ShortDatePattern
            $endTime = Get-Date ($Schedule.ActiveEndTimeOfDay.ToString()) -format $datetimeFormat.LongTimePattern

            # Start setting the description based on the frequency type
            switch ($schedule.FrequencyTypes) {
                {($_ -eq 1) -or ($_ -eq "Once")} { $description += "Occurs on $startDate at $startTime" }
                {($_ -in 4, 8, 16, 32) -or ($_ -in "Daily", "Weekly", "Monthly")} { $description += "Occurs every "}
                {($_ -eq 64) -or ($_ -eq "AutoStart")} {$description += "Start automatically when SQL Server Agent starts "}
                {($_ -eq 128) -or ($_ -eq "OnIdle")} {$description += "Start whenever the CPUs become idle"}
            }

            # Check the frequency types for daily or weekly i.e.
            switch ($schedule.FrequencyTypes) {
                # Daily
                {$_ -in 4, "Daily"} {
                    if ($Schedule.FrequencyInterval -eq 1) {
                        $description += "day "
                    }
                    elseif ($Schedule.FrequencyInterval -gt 1) {
                        $description += "$($Schedule.FrequencyInterval) day(s) "
                    }
                }

                # Weekly
                {$_ -in 8, "Weekly"} {
                    # Check if it's for one or more weeks
                    if ($Schedule.FrequencyRecurrenceFactor -eq 1) {
                        $description += "week on "
                    }
                    elseif ($Schedule.FrequencyRecurrenceFactor -gt 1) {
                        $description += "$($Schedule.FrequencyRecurrenceFactor) week(s) on "
                    }

                    # Save the interval for the loop
                    $frequencyInterval = $Schedule.FrequencyInterval

                    # Create the array to hold the days
                    $days = ($false, $false, $false, $false, $false, $false, $false)

                    # Loop through the days
                    while ($frequencyInterval -gt 0) {

                        switch ($FrequenctInterval) {
                            {($frequencyInterval - 64) -ge 0} {
                                $days[5] = "Saturday"
                                $frequencyInterval -= 64
                            }
                            {($frequencyInterval - 32) -ge 0} {
                                $days[4] = "Friday"
                                $frequencyInterval -= 32
                            }
                            {($frequencyInterval - 16) -ge 0} {
                                $days[3] = "Thursday"
                                $frequencyInterval -= 16
                            }
                            {($frequencyInterval - 8) -ge 0} {
                                $days[2] = "Wednesday"
                                $frequencyInterval -= 8
                            }
                            {($frequencyInterval - 4) -ge 0} {
                                $days[1] = "Tuesday"
                                $frequencyInterval -= 4
                            }
                            {($frequencyInterval - 2) -ge 0} {
                                $days[0] = "Monday"
                                $frequencyInterval -= 2
                            }
                            {($frequencyInterval - 1) -ge 0} {
                                $days[6] = "Sunday"
                                $frequencyInterval -= 1
                            }
                        }

                    }

                    # Add the days to the description by selecting the days and exploding the array
                    $description += ($days | Where-Object {$_ -ne $false}) -join ", "
                    $description += " "

                }

                # Monthly
                {$_ -in 16, "Monthly"} {
                    # Check if it's for one or more months
                    if ($Schedule.FrequencyRecurrenceFactor -eq 1) {
                        $description += "month "
                    }
                    elseif ($Schedule.FrequencyRecurrenceFactor -gt 1) {
                        $description += "$($Schedule.FrequencyRecurrenceFactor) month(s) "
                    }

                    # Add the interval
                    $description += "on day $($Schedule.FrequencyInterval) of that month "
                }

                # Monthly relative
                {$_ -in 32, "MonthlyRelative"} {
                    # Check for the relative day
                    switch ($Schedule.FrequencyRelativeIntervals) {
                        {$_ -in 1, "First"} {$description += "first "}
                        {$_ -in 2, "Second"} {$description += "second "}
                        {$_ -in 4, "Third"} {$description += "third "}
                        {$_ -in 8, "Fourth"} {$description += "fourth "}
                        {$_ -in 16, "Last"} {$description += "last "}
                    }

                    # Get the relative day of the week
                    switch ($Schedule.FrequencyInterval) {
                        1 { $description += "Sunday "}
                        2 { $description += "Monday "}
                        3 { $description += "Tuesday "}
                        4 { $description += "Wednesday "}
                        5 { $description += "Thursday "}
                        6 { $description += "Friday "}
                        7 { $description += "Saturday "}
                        8 { $description += "Day "}
                        9 { $description += "Weekday "}
                        10 { $description += "Weekend day "}
                    }

                    $description += "of every $($Schedule.FrequencyRecurrenceFactor) month(s) "

                }
            }

            # Check the frequency type
            if ($schedule.FrequencyTypes -notin 64, 128) {

                # Check the subday types for minutes or hours i.e.
                if ($schedule.FrequencySubDayInterval -in 0, 1) {
                    $description += "at $startTime. "
                }
                else {

                    switch ($Schedule.FrequencySubDayTypes) {
                        {$_ -in 2, "Seconds"} { $description += "every $($schedule.FrequencySubDayInterval) second(s) "}
                        {$_ -in 4, "Minutes"} {$description += "every $($schedule.FrequencySubDayInterval) minute(s) " }
                        {$_ -in 8, "Hours"} { $description += "every $($schedule.FrequencySubDayInterval) hour(s) " }
                    }

                    $description += "between $startTime and $endTime. "
                }

                # Check if an end date has been given
                if ($Schedule.ActiveEndDate.Year -eq 9999) {
                    $description += "Schedule will be used starting on $startDate."
                }
                else {
                    $description += "Schedule will used between $startDate and $endDate."
                }
            }

            return $description
        }
    }

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($server.Edition -like 'Express*') {
                Stop-Function -Message "$($server.Edition) does not support SQL Server Agent. Skipping $server." -Continue
            }

            if ($Schedule) {
                $scheduleCollection = $server.JobServer.SharedSchedules | Where-Object { $_.Name -in $Schedule }
            }
            else {
                $scheduleCollection = $server.JobServer.SharedSchedules
            }

        }

        $defaults = "ComputerName", "InstanceName", "SqlInstance", "Name as ScheduleName", "ActiveEndDate", "ActiveEndTimeOfDay", "ActiveStartDate", "ActiveStartTimeOfDay", "DateCreated", "FrequencyInterval", "FrequencyRecurrenceFactor", "FrequencyRelativeIntervals", "FrequencySubDayInterval", "FrequencySubDayTypes", "FrequencyTypes", "IsEnabled", "JobCount", "Description"

        foreach ($schedule in $scheduleCollection) {
            $description = Get-ScheduleDescription -Schedule $schedule

            Add-Member -Force -InputObject $schedule -MemberType NoteProperty ComputerName -value $server.ComputerName
            Add-Member -Force -InputObject $schedule -MemberType NoteProperty InstanceName -value $server.ServiceName
            Add-Member -Force -InputObject $schedule -MemberType NoteProperty SqlInstance -value $server.DomainInstanceName
            Add-Member -Force -InputObject $schedule -MemberType NoteProperty Description -Value $description

            Select-DefaultView -InputObject $schedule -Property $defaults
        }

    }
}
function Get-DbaAgHadr {
    <#
        .SYNOPSIS
            Gets the Hadr service setting on the specified SQL Server instance.

        .DESCRIPTION
            Gets the Hadr setting, from the service level, and returns true or false for the specified SQL Server instance.

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to.

        .PARAMETER SqlCredential
            Credential object used to connect to the SQL instance

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Hadr, AG, AvailabilityGroup
            Author: Shawn Melton (@wsmelton | http://blog.wsmelton.info)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaAgHadr

        .EXAMPLE
            Get-DbaAgHadr -SqlInstance sql2016

            Returns a status of the Hadr setting for sql2016 SQL Server instance.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            Add-Member -Force -InputObject $server -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
            Add-Member -Force -InputObject $server -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
            Add-Member -Force -InputObject $server -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName

            Select-DefaultView -InputObject $server -Property 'ComputerName', 'InstanceName', 'SqlInstance', 'IsHadrEnabled'
        }
    }
}
function Get-DbaAgListener {
    <#
        .SYNOPSIS
            Outputs the name of the Listener for the Availability Group(s) found on the server.

        .DESCRIPTION
            Default view provides most common set of properties for information on the database in an Availability Group(s).

            Information returned on the database will be specific to that replica, whether it is primary or a secondary.

            This command will return an SMO object, but it is the AvailabilityDatabases object  and not the Server.Databases object.

        .PARAMETER SqlInstance
            The SQL Server instance. Server version must be SQL Server version 2012 or higher.

        .PARAMETER SqlCredential
            Allows you to login to servers using SQL Logins instead of Windows Authentication (AKA Integrated or Trusted).

        .PARAMETER AvailabilityGroup
            Specify the Availability Group name that you want to get information on.

        .PARAMETER Listener
            Specify the Listener name that you want to get information on.

        .PARAMETER InputObject
            Piped in Availability Group objects

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: AG, AvailabilityGroup, Listener
            Author: Viorel Ciucu (@viorelciucu)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaAgListener

        .EXAMPLE
            Get-DbaAgListener -SqlInstance sqlserver2014a

            Returns basic information on the listener found on sqlserver2014a

        .EXAMPLE
            Get-DbaAgListener -SqlInstance sqlserver2014a -AvailabilityGroup AG-a

            Returns basic information on the listener found on sqlserver2014a in the Availability Group AG-a

    #>
    [CmdletBinding()]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(ValueFromPipeline = $true)]
        [string[]]$AvailabilityGroup,
        [string[]]$Listener,
        [object[]]$InputObject,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            $InputObject += Get-DbaAvailabilityGroup -SqlInstance $instance -SqlCredential $SqlCredential -AvailabilityGroup $AvailabilityGroup
        }
        if (Test-Bound -ParameterName Listener) {
            $InputObject = $InputObject | Where-Object { $_.AvailabilityGroupListeners.Name -contains $Listener }
        }

        $defaults = 'ComputerName', 'InstanceName', 'SqlInstance', 'AvailabilityGroup', 'Name', 'PortNumber', 'ClusterIPConfiguration'
        foreach ($aglistener in $InputObject.AvailabilityGroupListeners) {
            $server = $aglistener.Parent.Parent
            Add-Member -Force -InputObject $aglistener -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
            Add-Member -Force -InputObject $aglistener -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
            Add-Member -Force -InputObject $aglistener -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
            Add-Member -Force -InputObject $aglistener -MemberType NoteProperty -Name AvailabilityGroup -value $aglistener.Parent.Name
            Select-DefaultView -InputObject $aglistener -Property $defaults
        }
    }
}
function Get-DbaAgReplica {
    <#
        .SYNOPSIS
            Outputs the Availability Group(s)' Replica object found on the server.

        .DESCRIPTION
            Default view provides most common set of properties for information on the Availability Group(s)' Replica.

        .PARAMETER SqlInstance
            The SQL Server instance. Server version must be SQL Server version 2012 or higher.

        .PARAMETER SqlCredential
            Allows you to login to servers using SQL Logins as opposed to Windows Auth/Integrated/Trusted.

        .PARAMETER AvailabilityGroup
            Specify the Availability Group name that you want to get information on.

        .PARAMETER Replica
            Specify the replica to pull information on, is dependent up name that you want to get information on.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: AG, AvailabilityGroup, Replica
            Author: Shawn Melton (@wsmelton) | Chrissy LeMaire (@ctrlb)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaAgReplica

        .EXAMPLE
            Get-DbaAgReplica -SqlInstance sqlserver2014a

            Returns basic information on all the Availability Group(s) replica(s) found on sqlserver2014a

        .EXAMPLE
            Get-DbaAgReplica -SqlInstance sqlserver2014a -AvailabilityGroup AG-a

            Shows basic information on the replica(s) found on Availability Group AG-a on sqlserver2014a

        .EXAMPLE
            Get-DbaAgReplica -SqlInstance sqlserver2014a | Select *

            Returns full object properties on all Availability Group(s) replica(s) on sqlserver2014a
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(ValueFromPipeline = $true)]
        [object[]]$AvailabilityGroup,
        [object[]]$Replica,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($serverName in $SqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $serverName -SqlCredential $SqlCredential -MinimumVersion 11
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($server.IsHadrEnabled -eq $false) {
                Stop-Function -Message "Availability Group (HADR) is not configured for the instance: $serverName" -Target $serverName -Continue
            }

            $ags = $server.AvailabilityGroups
            if ($AvailabilityGroup) {
                $ags = $ags | Where-Object Name -in $AvailabilityGroup
            }

            foreach ($ag in $ags) {
                $replicas = $ag.AvailabilityReplicas
                foreach ($currentReplica in $replicas) {
                    if ($Replica -and $currentReplica.Name -notmatch $Replica) {
                        continue
                    }

                    Add-Member -Force -InputObject $currentReplica -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                    Add-Member -Force -InputObject $currentReplica -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                    Add-Member -Force -InputObject $currentReplica -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName

                    $defaults = 'ComputerName', 'InstanceName', 'SqlInstance', 'Parent as AvailabilityGroup', 'Name as Replica', 'Role', 'ConnectionState', 'RollupSynchronizationState', 'AvailabilityMode', 'BackupPriority', 'EndpointUrl', 'SessionTimeout', 'FailoverMode', 'ReadonlyRoutingList'
                    Select-DefaultView -InputObject $currentReplica -Property $defaults
                }
            }
        }
    }
}
function Get-DbaAvailabilityGroup {
    <#
        .SYNOPSIS
            Outputs the Availability Group(s) object found on the server.

        .DESCRIPTION
            Default view provides most common set of properties for information on the Availability Group(s).

        .PARAMETER SqlInstance
            The SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2012 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER AvailabilityGroup
            Specifies the Availability Group name that you want to get information on.

        .PARAMETER IsPrimary
            If this switch is enabled, a boolean indicating whether SqlInstance is the Primary replica in the AG is returned.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Hadr, AG, AvailabilityGroup
            Author: Shawn Melton (@wsmelton) | Chrissy LeMaire (@ctrlb)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaAvailabilityGroup

        .EXAMPLE
            Get-DbaAvailabilityGroup -SqlInstance sqlserver2014a

            Returns basic information on all the Availability Group(s) found on sqlserver2014a.

        .EXAMPLE
            Get-DbaAvailabilityGroup -SqlInstance sqlserver2014a -AvailabilityGroup AG-a

            Shows basic information on the Availability Group AG-a on sqlserver2014a.

        .EXAMPLE
            Get-DbaAvailabilityGroup -SqlInstance sqlserver2014a | Select *

            Returns full object properties on all Availability Group(s) on sqlserver2014a.

        .EXAMPLE
            Get-DbaAvailabilityGroup -SqlInstance sqlserver2014a -AvailabilityGroup AG-a -IsPrimary

            Returns true/false if the server, sqlserver2014a, is the primary replica for AG-a Availability Group.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$AvailabilityGroup,
        [switch]$IsPrimary,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($serverName in $SqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $serverName -SqlCredential $SqlCredential -MinimumVersion 11
            }
            catch {
                Stop-Function -Message "Failure." -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($server.IsHadrEnabled -eq $false) {
                Stop-Function -Message "Availability Group (HADR) is not configured for the instance: $serverName." -Target $serverName -Continue
            }

            $ags = $server.AvailabilityGroups
            if ($AvailabilityGroup) {
                $ags = $ags | Where-Object Name -in $AvailabilityGroup
            }

            foreach ($ag in $ags) {
                Add-Member -Force -InputObject $ag -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                Add-Member -Force -InputObject $ag -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                Add-Member -Force -InputObject $ag -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName

                if ($IsPrimary) {
                    $defaults = 'ComputerName', 'InstanceName', 'SqlInstance', 'Name as AvailabilityGroup', 'IsPrimary'
                    $value = $false
                    if ($ag.PrimaryReplicaServerName -eq $server.Name) {
                        $value = $true
                    }
                    Add-Member -Force -InputObject $ag -MemberType NoteProperty -Name IsPrimary -Value $value
                    Select-DefaultView -InputObject $ag -Property $defaults
                }
                else {
                    $defaults = 'ComputerName', 'InstanceName', 'SqlInstance', 'LocalReplicaRole', 'Name as AvailabilityGroup', 'PrimaryReplicaServerName as PrimaryReplica', 'AutomatedBackupPreference', 'AvailabilityReplicas', 'AvailabilityDatabases', 'AvailabilityGroupListeners'
                    Select-DefaultView -InputObject $ag -Property $defaults
                }
            }
        }
    }
}
function Get-DbaAvailableCollation {
    <#
        .SYNOPSIS
            Function to get available collations for a given SQL Server

        .DESCRIPTION
            The Get-DbaAvailableCollation function returns the list of collations available on each SQL Server.
            Only the connect permission is required to get this information.

        .PARAMETER SqlInstance
            The SQL Server instance, or instances. Only connect permission is required.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Collation, Configuration
            Author: Bryan Hamby (@galador)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaAvailableCollation

        .EXAMPLE
            Get-DbaAvailableCollation -SqlInstance sql2016

            Gets all the collations from server sql2016 using NT authentication
    #>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        #Functions to get/cache the code page and language description.
        #It runs about 9x faster caching these (2 vs 18 seconds) in my test,
        #since there are so many duplicates

        #No longer supported by Windows, but still shows up in SQL Server
        #http://www.databaseteam.org/1-ms-sql-server/982faddda7a789a1.htm
        $locales = @{66577 = "Japanese_Unicode"}
        $codePages = @{}

        function Get-LocaleDescription ($LocaleId) {
            if ($locales.ContainsKey($LocaleId)) {
                $localeName = $locales.Get_Item($LocaleId)
            }
            else {
                try {
                    $localeName = (Get-Language $LocaleId).DisplayName
                }
                catch {
                    $localeName = $null
                }
                $locales.Set_Item($LocaleId, $localeName)
            }
            return $localeName
        }

        function Get-CodePageDescription ($codePageId) {
            if ($codePages.ContainsKey($codePageId)) {
                $codePageName = $codePages.Get_Item($codePageId)
            }
            else {
                try {
                    $codePageName = (Get-CodePage $codePageId).EncodingName
                }
                catch {
                    $codePageName = $null
                }
                $codePages.Set_Item($codePageId, $codePageName)
            }
            return $codePageName
        }
    }

    process {
        foreach ($Instance in $sqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $availableCollations = $server.EnumCollations()
            foreach ($collation in $availableCollations) {
                Add-Member -Force -InputObject $collation -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                Add-Member -Force -InputObject $collation -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                Add-Member -Force -InputObject $collation -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                Add-Member -Force -InputObject $collation -MemberType NoteProperty -Name CodePageName -Value (Get-CodePageDescription $collation.CodePage)
                Add-Member -Force -InputObject $collation -MemberType NoteProperty -Name LocaleName -Value (Get-LocaleDescription $collation.LocaleID)
            }

            Select-DefaultView -InputObject $availableCollations -Property ComputerName, InstanceName, SqlInstance, Name, CodePage, CodePageName, LocaleID, LocaleName, Description
        }
    }
}
#ValidationTags#Messaging#
function Get-DbaBackupDevice {
    <#
        .SYNOPSIS
            Gets SQL Backup Device information for each instance(s) of SQL Server.

        .DESCRIPTION
            The Get-DbaBackupDevice command gets SQL Backup Device information for each instance(s) of SQL Server.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function
            to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Backup
            Author: Garry Bargsley (@gbargsley), http://blog.garrybargsley.com

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaBackupDevice

        .EXAMPLE
            Get-DbaBackupDevice -SqlInstance localhost

            Returns all Backup Devices on the local default SQL Server instance

        .EXAMPLE
            Get-DbaBackupDevice -SqlInstance localhost, sql2016

            Returns all Backup Devices for the local and sql2016 SQL Server instances
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            foreach ($backupDevice in $server.BackupDevices) {
                Add-Member -Force -InputObject $backupDevice -MemberType NoteProperty -Name ComputerName -value $backupDevice.Parent.ComputerName
                Add-Member -Force -InputObject $backupDevice -MemberType NoteProperty -Name InstanceName -value $backupDevice.Parent.ServiceName
                Add-Member -Force -InputObject $backupDevice -MemberType NoteProperty -Name SqlInstance -value $backupDevice.Parent.DomainInstanceName

                Select-DefaultView -InputObject $backupDevice -Property ComputerName, InstanceName, SqlInstance, Name, BackupDeviceType, PhysicalLocation, SkipTapeLabel
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaBackupHistory {
    <#
        .SYNOPSIS
            Returns backup history details for databases on a SQL Server.

        .DESCRIPTION
            Returns backup history details for some or all databases on a SQL Server.

            You can even get detailed information (including file path) for latest full, differential and log files.

            Backups taken with the CopyOnly option will NOT be returned, unless the IncludeCopyOnly switch is present

            Reference: http://www.sqlhub.com/2011/07/find-your-backup-history-in-sql-server.html

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Credential object used to connect to the SQL Server Instance as a different user. This can be a Windows or SQL Server account. Windows users are determined by the existence of a backslash, so if you are intending to use an alternative Windows connection instead of a SQL login, ensure it contains a backslash.

        .PARAMETER Database
            Specifies one or more database(s) to process. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies one or more database(s) to exclude from processing.

        .PARAMETER IncludeCopyOnly
            By default Get-DbaBackupHistory will ignore backups taken with the CopyOnly option. This switch will include them

        .PARAMETER Force
            If this switch is enabled, a large amount of information is returned, similar to what SQL Server itself returns.

        .PARAMETER Since
            Specifies a DateTime object to use as the starting point for the search for backups.

        .PARAMETER Last
            If this switch is enabled, the most recent full chain of full, diff and log backup sets is returned.

        .PARAMETER LastFull
            If this switch is enabled, the most recent full backup set is returned.

        .PARAMETER LastDiff
            If this switch is enabled, the most recent differential backup set is returned.

        .PARAMETER LastLog
            If this switch is enabled, the most recent log backup is returned.

        .PARAMETER DeviceType
            Specifies a filter for backup sets based on DeviceTypes. Valid options are 'Disk','Permanent Disk Device', 'Tape', 'Permanent Tape Device','Pipe','Permanent Pipe Device','Virtual Device', in addition to custom integers for your own DeviceTypes.

        .PARAMETER Raw
            If this switch is enabled, one object per backup file is returned. Otherwise, media sets (striped backups across multiple files) will be grouped into a single return object.

        .PARAMETER Type
            Specifies one or more types of backups to return. Valid options are 'Full', 'Log', 'Differential', 'File', 'Differential File', 'Partial Full', and 'Partial Differential'. Otherwise, all types of backups will be returned unless one of the -Last* switches is enabled.

        .PARAMETER LastLsn
            Specifies a minimum LSN to use in filtering backup history. Only backups with an LSN greater than this value will be returned, which helps speed the retrieval process.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: DisasterRecovery, Backup
            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaBackupHistory

        .EXAMPLE
            Get-DbaBackupHistory -SqlInstance SqlInstance2014a

            Returns server name, database, username, backup type, date for all backups databases on SqlInstance2014a. This may return many rows; consider using filters that are included in other examples.

        .EXAMPLE
            $cred = Get-Credential sqladmin
            Get-DbaBackupHistory -SqlInstance SqlInstance2014a -SqlCredential $cred

            Does the same as above but logs in as SQL user "sqladmin"

        .EXAMPLE
            Get-DbaBackupHistory -SqlInstance SqlInstance2014a -Database db1, db2 -Since '7/1/2016 10:47:00'

            Returns backup information only for databases db1 and db2 on SqlInstance2014a since July 1, 2016 at 10:47 AM.

        .EXAMPLE
            Get-DbaBackupHistory -SqlInstance sql2014 -Database AdventureWorks2014, pubs -Force | Format-Table

            Returns information only for AdventureWorks2014 and pubs and formats the results as a table.

        .EXAMPLE
            Get-DbaBackupHistory -SqlInstance sql2014 -Database AdventureWorks2014 -Last

            Returns information about the most recent full, differential and log backups for AdventureWorks2014 on sql2014.

        .EXAMPLE
            Get-DbaBackupHistory -SqlInstance sql2014 -Database AdventureWorks2014 -Last -DeviceType Disk

            Returns information about the most recent full, differential and log backups for AdventureWorks2014 on sql2014, but only for backups to disk.

        .EXAMPLE
            Get-DbaBackupHistory -SqlInstance sql2014 -Database AdventureWorks2014 -Last -DeviceType 148,107

            Returns information about the most recent full, differential and log backups for AdventureWorks2014 on sql2014, but only for backups with device_type 148 and 107.

        .EXAMPLE
            Get-DbaBackupHistory -SqlInstance sql2014 -Database AdventureWorks2014 -LastFull

            Returns information about the most recent full backup for AdventureWorks2014 on sql2014.

        .EXAMPLE
            Get-DbaBackupHistory -SqlInstance sql2014 -Database AdventureWorks2014 -Type Full

            Returns information about all Full backups for AdventureWorks2014 on sql2014.

        .EXAMPLE
            Get-DbaRegisteredServer -SqlInstance sql2016 | Get-DbaBackupHistory

            Returns database backup information for every database on every server listed in the Central Management Server on sql2016.

        .EXAMPLE
            Get-DbaBackupHistory -SqlInstance SqlInstance2014a, sql2016 -Force

            Returns detailed backup history for all databases on SqlInstance2014a and sql2016.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]
        $SqlInstance,
        [Alias("Credential")]
        [PsCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$IncludeCopyOnly,
        [Parameter(ParameterSetName = "NoLast")]
        [switch]$Force,
        [Parameter(ParameterSetName = "NoLast")]
        [DateTime]$Since,
        [Parameter(ParameterSetName = "Last")]
        [switch]$Last,
        [Parameter(ParameterSetName = "Last")]
        [switch]$LastFull,
        [Parameter(ParameterSetName = "Last")]
        [switch]$LastDiff,
        [Parameter(ParameterSetName = "Last")]
        [switch]$LastLog,
        [string[]]$DeviceType,
        [switch]$Raw,
        [bigint]$LastLsn,
        [ValidateSet("Full", "Log", "Differential", "File", "Differential File", "Partial Full", "Partial Differential")]
        [string[]]$Type,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Write-Message -Level System -Message "Active Parameter set: $($PSCmdlet.ParameterSetName)."
        Write-Message -Level System -Message "Bound parameters: $($PSBoundParameters.Keys -join ", ")"

        $deviceTypeMapping = @{
            'Disk'                  = 2
            'Permanent Disk Device' = 102
            'Tape'                  = 5
            'Permanent Tape Device' = 105
            'Pipe'                  = 6
            'Permanent Pipe Device' = 106
            'Virtual Device'        = 7
            'URL'                   = 9
        }
        $deviceTypeFilter = @()
        foreach ($devType in $DeviceType) {
            if ($devType -in $deviceTypeMapping.Keys) {
                $deviceTypeFilter += $deviceTypeMapping[$devType]
            }
            else {
                $deviceTypeFilter += $devType
            }
        }
        $backupTypeMapping = @{
            'Log'                  = 'L'
            'Full'                 = 'D'
            'File'                 = 'F'
            'Differential'         = 'I'
            'Differential File'    = 'G'
            'Partial Full'         = 'P'
            'Partial Differential' = 'Q'
        }
        $backupTypeFilter = @()
        foreach ($typeFilter in $Type) {
            $backupTypeFilter += $backupTypeMapping[$typeFilter]
        }

    }

    process {
        foreach ($instance in $SqlInstance) {

            try {
                Write-Message -Level Verbose -Message "Connecting to $instance." -Target $instance
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($server.VersionMajor -ge 10) {
                $compressedFlag = $true
                # 2008 introduced compressed_backup_size
                $backupCols = "
                backupset.backup_size AS TotalSize,
                backupset.compressed_backup_size as CompressedBackupSize"
            }
            else {
                $compressedFlag = $false
                $backupCols = "
                backupset.backup_size AS TotalSize,
                NULL as CompressedBackupSize"
            }

            $databases = @()
            if ($null -ne $Database) {
                foreach ($db in $Database) {
                    $databases += [PSCustomObject]@{name = $db}
                }
            }
            else {
                $databases = $server.Databases
            }
            if ($ExcludeDatabase) {
                $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }
            foreach ($d in $deviceTypeFilter) {
                $deviceTypeFilterRight = "IN ('" + ($deviceTypeFilter -Join "','") + "')"
            }

            foreach ($b in $backupTypeFilter) {
                $backupTypeFilterRight = "IN ('" + ($backupTypeFilter -Join "','") + "')"
            }

            if ($last) {
                foreach ($db in $databases) {

                    #Get the full and build upwards
                    $allBackups = @()
                    $allBackups += $fullDb = Get-DbaBackupHistory -SqlInstance $server -Database $db.Name -LastFull -raw:$Raw -DeviceType $DeviceType -IncludeCopyOnly:$IncludeCopyOnly
                    $diffDb = Get-DbaBackupHistory -SqlInstance $server -Database $db.Name -LastDiff -raw:$Raw -DeviceType $DeviceType -IncludeCopyOnly:$IncludeCopyOnly
                    if ($diffDb.LastLsn -gt $fullDb.LastLsn -and $diffDb.DatabaseBackupLSN -eq $fullDb.CheckPointLSN ) {
                        Write-Message -Level Verbose -Message "Valid Differential backup "
                        $allBackups += $diffDb
                        $tlogStartDsn = ($diffDb.FirstLsn -as [bigint])
                    }
                    else {
                        Write-Message -Level Verbose -Message "No Diff found"
                        try {
                            [bigint]$tlogStartDsn = $fullDb.FirstLsn.ToString()
                        }
                        catch {
                            continue
                        }
                    }
                    $allBackups += Get-DbaBackupHistory -SqlInstance $server -Database $db.Name -raw:$raw -DeviceType $DeviceType -LastLsn $tlogStartDsn -IncludeCopyOnly:$IncludeCopyOnly | Where-Object {
                        $_.Type -eq 'Log' -and [bigint]$_.LastLsn -gt [bigint]$tlogStartDsn -and [bigint]$_.DatabaseBackupLSN -eq [bigint]$fullDb.CheckPointLSN -and $_.LastRecoveryForkGuid -eq $fullDb.LastRecoveryForkGuid
                    }
                    #This line does the output for -Last!!!
                    $allBackups |  Sort-Object -Property LastLsn, Type
                }
                continue
            }

            if ($LastFull -or $LastDiff -or $LastLog) {
                if ($LastFull) {
                    $first = 'D'; $second = 'P'
                }
                if ($LastDiff) {
                    $first = 'I'; $second = 'Q'
                }
                if ($LastLog) {
                    $first = 'L'; $second = 'L'
                }
                $databases = $databases | Select-Object -Unique -Property Name
                $sql = ""
                foreach ($db in $databases) {
                    Write-Message -Level Verbose -Message "Processing $($db.name)" -Target $db
                    $whereCopyOnly = $null
                    if ($true -ne $IncludeCopyOnly) {
                        $whereCopyOnly = " AND is_copy_only='0' "
                    }
                    if ($deviceTypeFilter) {
                        $devTypeFilterWhere = "AND mediafamily.device_type $deviceTypeFilterRight"
                    }
                    # recap for future editors (as this has been discussed over and over):
                    #   - original editors (from hereon referred as "we") rank over backupset.last_lsn desc, backupset.backup_finish_date desc for a good reason: DST
                    #     all times are recorded with the timezone of the server
                    #   - we thought about ranking over backupset.backup_set_id desc, backupset.last_lsn desc, backupset.backup_finish_date desc
                    #     but there is no explicit documentation about "when" a row gets inserted into backupset. Theoretically it _could_
                    #     happen that backup_set_id for the same database has not the same order of last_lsn.
                    #   - given ultimately to restore something lsn IS the source of truth, we decided to trust that and only that
                    #   - we know that sometimes it happens to drop a database without deleting the history. Assuming then to create a database with the same name,
                    #     and given the lsn are composed in the first part by the VLF SeqID, it happens seldomly that for the same database_name backupset holds
                    #     last_lsn out of order. To avoid this behaviour, we filter by database_guid choosing the guid that has MAX(backup_finish_date), as we know
                    #     last_lsn cannot be out-of-order for the same database, and the same database cannot have different database_guid
                    $sql += "
                                SELECT
                                    a.BackupSetRank,
                                    a.Server,
                                    a.[Database],
                                    a.Username,
                                    a.Start,
                                    a.[End],
                                    a.Duration,
                                    a.[Path],
                                    a.Type,
                                    a.TotalSize,
                                    a.CompressedBackupSize,
                                    a.MediaSetId,
                                    a.BackupSetID,
                                    a.Software,
                                    a.position,
                                    a.first_lsn,
                                    a.database_backup_lsn,
                                    a.checkpoint_lsn,
                                    a.last_lsn,
                                    a.first_lsn as 'FirstLSN',
                                    a.database_backup_lsn as 'DatabaseBackupLsn',
                                    a.checkpoint_lsn as 'CheckpointLsn',
                                    a.last_lsn as 'LastLsn',
                                    a.software_major_version,
                                    a.DeviceType,
                                    a.is_copy_only,
                                    a.last_recovery_fork_guid
                                FROM (SELECT
                                  RANK() OVER (ORDER BY backupset.last_lsn desc, backupset.backup_finish_date DESC) AS 'BackupSetRank',
                                  backupset.database_name AS [Database],
                                  backupset.user_name AS Username,
                                  backupset.backup_start_date AS Start,
                                  backupset.server_name as [Server],
                                  backupset.backup_finish_date AS [End],
                                  DATEDIFF(SECOND, backupset.backup_start_date, backupset.backup_finish_date) AS Duration,
                                  mediafamily.physical_device_name AS Path,
                                  $backupCols,
                                  CASE backupset.type
                                    WHEN 'L' THEN 'Log'
                                    WHEN 'D' THEN 'Full'
                                    WHEN 'F' THEN 'File'
                                    WHEN 'I' THEN 'Differential'
                                    WHEN 'G' THEN 'Differential File'
                                    WHEN 'P' THEN 'Partial Full'
                                    WHEN 'Q' THEN 'Partial Differential'
                                    ELSE NULL
                                  END AS Type,
                                  backupset.media_set_id AS MediaSetId,
                                  mediafamily.media_family_id as mediafamilyid,
                                  backupset.backup_set_id as BackupSetID,
                                  CASE mediafamily.device_type
                                    WHEN 2 THEN 'Disk'
                                    WHEN 102 THEN 'Permanent Disk Device'
                                    WHEN 5 THEN 'Tape'
                                    WHEN 105 THEN 'Permanent Tape Device'
                                    WHEN 6 THEN 'Pipe'
                                    WHEN 106 THEN 'Permanent Pipe Device'
                                    WHEN 7 THEN 'Virtual Device'
                                    WHEN 9 THEN 'URL'
                                    ELSE 'Unknown'
                                    END AS DeviceType,
                                  backupset.position,
                                  backupset.first_lsn,
                                  backupset.database_backup_lsn,
                                  backupset.checkpoint_lsn,
                                  backupset.last_lsn,
                                  backupset.software_major_version,
                                  mediaset.software_name AS Software,
                                  backupset.is_copy_only,
                                  backupset.last_recovery_fork_guid,
                                  backupset.recovery_model
                                FROM msdb..backupmediafamily AS mediafamily
                                JOIN msdb..backupmediaset AS mediaset
                                  ON mediafamily.media_set_id = mediaset.media_set_id
                                JOIN msdb..backupset AS backupset
                                  ON backupset.media_set_id = mediaset.media_set_id
                                JOIN (
                                    SELECT DISTINCT database_guid, database_name, backup_finish_date
                                    FROM msdb..backupset
                                    WHERE backupset.database_name = '$($db.Name)'
                                ) dbguid
                                  ON dbguid.database_name = backupset.database_name
                                  AND dbguid.database_guid = backupset.database_guid
                                JOIN (
                                    SELECT database_name, MAX(backup_finish_date) max_finish_date
                                    FROM msdb..backupset
                                    WHERE backupset.database_name = '$($db.Name)'
                                    GROUP BY database_name
                                ) dbguid_support
                                  ON dbguid_support.database_name = backupset.database_name
                                  AND dbguid.backup_finish_date = dbguid_support.max_finish_date
                                WHERE backupset.database_name = '$($db.Name)' $whereCopyOnly
                                AND (type = '$first' OR type = '$second')
                                $devTypeFilterWhere
                                ) AS a
                                WHERE a.BackupSetRank = 1
                                ORDER BY a.Type;
                                "
                }
                $sql = $sql -join "; "
            }
            else {
                if ($Force -eq $true) {
                    $select = "SELECT * "
                }
                else {
                    $select = "
                            SELECT
                              backupset.database_name AS [Database],
                              backupset.user_name AS Username,
                              backupset.server_name as [server],
                              backupset.backup_start_date AS [Start],
                              backupset.backup_finish_date AS [End],
                              DATEDIFF(SECOND, backupset.backup_start_date, backupset.backup_finish_date) AS Duration,
                              mediafamily.physical_device_name AS Path,
                              $backupCols,
                              CASE backupset.type
                                WHEN 'L' THEN 'Log'
                                WHEN 'D' THEN 'Full'
                                WHEN 'F' THEN 'File'
                                WHEN 'I' THEN 'Differential'
                                WHEN 'G' THEN 'Differential File'
                                WHEN 'P' THEN 'Partial Full'
                                WHEN 'Q' THEN 'Partial Differential'
                                ELSE NULL
                              END AS Type,
                              backupset.media_set_id AS MediaSetId,
                              mediafamily.media_family_id as MediaFamilyId,
                              backupset.backup_set_id as BackupSetId,
                              CASE mediafamily.device_type
                                WHEN 2 THEN 'Disk'
                                WHEN 102 THEN 'Permanent Disk Device'
                                WHEN 5 THEN 'Tape'
                                WHEN 105 THEN 'Permanent Tape Device'
                                WHEN 6 THEN 'Pipe'
                                WHEN 106 THEN 'Permanent Pipe Device'
                                WHEN 7 THEN 'Virtual Device'
                                WHEN 9 THEN 'URL'
                                ELSE 'Unknown'
                              END AS DeviceType,
                              backupset.position,
                              backupset.first_lsn,
                              backupset.database_backup_lsn,
                              backupset.checkpoint_lsn,
                              backupset.last_lsn,
                              backupset.first_lsn as 'FirstLSN',
                              backupset.database_backup_lsn as 'DatabaseBackupLsn',
                              backupset.checkpoint_lsn as 'CheckpointLsn',
                              backupset.last_lsn as 'LastLsn',
                              backupset.software_major_version,
                              mediaset.software_name AS Software,
                              backupset.is_copy_only,
                              backupset.last_recovery_fork_guid,
                              backupset.recovery_model"
                }

                $from = " FROM msdb..backupmediafamily mediafamily
                             INNER JOIN msdb..backupmediaset mediaset ON mediafamily.media_set_id = mediaset.media_set_id
                             INNER JOIN msdb..backupset backupset ON backupset.media_set_id = mediaset.media_set_id"
                if ($Database -or $Since -or $Last -or $LastFull -or $LastLog -or $LastDiff -or $deviceTypeFilter -or $LastLsn -or $backupTypeFilter) {
                    $where = " WHERE "
                }

                $whereArray = @()

                if ($Database.length -gt 0) {
                    $dbList = $Database -join "','"
                    $whereArray += "database_name IN ('$dbList')"
                }

                if ($true -ne $IncludeCopyOnly) {
                    $whereArray += "is_copy_only='0'"
                }

                if ($Last -or $LastFull -or $LastLog -or $LastDiff) {
                    $tempWhere = $whereArray -join " AND "
                    $whereArray += "type = 'Full' AND mediaset.media_set_id = (SELECT TOP 1 mediaset.media_set_id $from $tempWhere ORDER BY backupset.last_lsn DESC)"
                }

                if ($null -ne $Since) {
                    $whereArray += "backupset.backup_finish_date >= '$($Since.ToString("yyyy-MM-ddTHH:mm:ss"))'"
                }

                if ($deviceTypeFilter) {
                    $whereArray += "mediafamily.device_type $deviceTypeFilterRight"
                }
                if ($backupTypeFilter) {
                    $whereArray += "backupset.type $backupTypeFilterRight"
                }

                if ($LastLsn) {
                    $whereArray += "backupset.last_lsn > $LastLsn"
                }
                if ($where.Length -gt 0) {
                    $whereArray = $whereArray -join " AND "
                    $where = "$where $whereArray"
                }

                $sql = "$select $from $where ORDER BY backupset.last_lsn DESC"
            }

            Write-Message -Level Debug -Message "SQL Statement: `n$sql"
            Write-Message -Level SomewhatVerbose -Message "Executing sql query."
            $results = $server.ConnectionContext.ExecuteWithResults($sql).Tables.Rows | Select-Object * -ExcludeProperty BackupSetRank, RowError, RowState, Table, ItemArray, HasErrors

            if ($raw) {
                Write-Message -Level SomewhatVerbose -Message "Processing as Raw Output."
                $results | Select-Object *, @{ Name = "FullName"; Expression = { $_.Path } }
                Write-Message -Level SomewhatVerbose -Message "$($results.Count) result sets found."
            }
            else {
                Write-Message -Level SomewhatVerbose -Message "Processing as grouped output."
                $groupedResults = $results | Group-Object -Property BackupsetId
                Write-Message -Level SomewhatVerbose -Message "$($groupedResults.Count) result-groups found."
                $groupResults = @()
                $backupSetIds = $groupedResults.Name
                $backupSetIdsList = $backupSetIds -Join ","
                if ($groupedResults.Count -gt 0) {
                    $backupSetIdsWhere = "backup_set_id IN ($backupSetIdsList)"
                    $fileAllSql = "SELECT backup_set_id, file_type as FileType, logical_name as LogicalName, physical_name as PhysicalName
                                   FROM msdb..backupfile WHERE $backupSetIdsWhere"
                    Write-Message -Level Debug -Message "FileSQL: $fileAllSql"
                    $fileListResults = $server.Query($fileAllSql)
                }
                else {
                    $fileListResults = @()
                }
                $fileListHash = @{}
                foreach ($fl in $fileListResults) {
                    if (-not($fileListHash.ContainsKey($fl.backup_set_id))) {
                        $fileListHash[$fl.backup_set_id] = @()
                    }
                    $fileListHash[$fl.backup_set_id] += $fl
                }
                foreach ($group in $groupedResults) {
                    $commonFields = $group.Group[0]
                    $groupLength = $group.Group.Count
                    if ($groupLength -eq 1) {
                        $start = $commonFields.Start
                        $end = $commonFields.End
                        $duration = New-TimeSpan -Seconds $commonFields.Duration
                    }
                    else {
                        $start = ($group.Group.Start | Measure-Object -Minimum).Minimum
                        $end = ($group.Group.End | Measure-Object -Maximum).Maximum
                        $duration = New-TimeSpan -Seconds ($group.Group.Duration | Measure-Object -Maximum).Maximum
                    }
                    $compressedBackupSize = $commonFields.CompressedBackupSize
                    if ($compressedFlag -eq $true) {
                        $ratio = [Math]::Round(($commonFields.TotalSize) / ($compressedBackupSize), 2)
                    }
                    else {
                        $compressedBackupSize = $null
                        $ratio = 1
                    }
                    $historyObject = New-Object Sqlcollaborative.Dbatools.Database.BackupHistory
                    $historyObject.ComputerName = $server.ComputerName
                    $historyObject.InstanceName = $server.ServiceName
                    $historyObject.SqlInstance = $server.DomainInstanceName
                    $historyObject.Database = $commonFields.Database
                    $historyObject.UserName = $commonFields.UserName
                    $historyObject.Start = $start
                    $historyObject.End = $end
                    $historyObject.Duration = $duration
                    $historyObject.Path = $group.Group.Path
                    $historyObject.TotalSize = $commonFields.TotalSize
                    $historyObject.CompressedBackupSize = $compressedBackupSize
                    $historyObject.CompressionRatio = $ratio
                    $historyObject.Type = $commonFields.Type
                    $historyObject.BackupSetId = $commonFields.BackupSetId
                    $historyObject.DeviceType = $commonFields.DeviceType
                    $historyObject.Software = $commonFields.Software
                    $historyObject.FullName = $group.Group.Path
                    $historyObject.FileList = $fileListHash[$commonFields.BackupSetID] | Select-Object FileType, LogicalName, PhysicalName
                    $historyObject.Position = $commonFields.Position
                    $historyObject.FirstLsn = $commonFields.First_LSN
                    $historyObject.DatabaseBackupLsn = $commonFields.database_backup_lsn
                    $historyObject.CheckpointLsn = $commonFields.checkpoint_lsn
                    $historyObject.LastLsn = $commonFields.Last_Lsn
                    $historyObject.SoftwareVersionMajor = $commonFields.Software_Major_Version
                    $historyObject.IsCopyOnly = ($commonFields.is_copy_only -eq 1)
                    $historyObject.LastRecoveryForkGuid = $commonFields.last_recovery_fork_guid
                    $historyObject.RecoveryModel = $commonFields.Recovery_Model
                    $historyObject
                }
                $groupResults | Sort-Object -Property LastLsn, Type
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaBackupInformation {
    <#
        .SYNOPSIS
            Scan backup files and creates a set, compatible with Restore-DbaDatabase

        .DESCRIPTION
            Upon being passed a list of potential backups files this command will scan the files, select those that contain SQL Server
            backup sets. It will then filter those files down to a set

            The function defaults to working on a remote instance. This means that all paths passed in must be relative to the remote instance.
            XpDirTree will be used to perform the file scans

            Various means can be used to pass in a list of files to be considered. The default is to non recursively scan the folder
            passed in.

        .PARAMETER Path
            Path to SQL Server backup files.

            Paths passed in as strings will be scanned using the desired method, default is a non recursive folder scan
            Accepts multiple paths separated by ','

            Or it can consist of FileInfo objects, such as the output of Get-ChildItem or Get-Item. This allows you to work with
            your own file structures as needed

        .PARAMETER SqlInstance
            The SQL Server instance to be used to read the headers of the backup files

        .PARAMETER SqlCredential
            Allows you to login to servers using SQL Logins as opposed to Windows Auth/Integrated/Trusted.

        .PARAMETER DatabaseName
            An array of Database Names to filter by. If empty all databases are returned.

        .PARAMETER SourceInstance
            If provided only backup originating from this destination will be returned. This SQL instance will not be connected to or involved in this work

        .PARAMETER NoXpDirTree
            If this switch is set, then Files will be parsed as locally files. This can cause failures if the running user can see files that the parsing SQL Instance cannot

        .PARAMETER DirectoryRecurse
            If specified the provided path/directory will be traversed (only applies if not using XpDirTree)

        .PARAMETER Anonymise
            If specified we will output the results with ComputerName, InstanceName, Database, UserName, and Paths hashed out
            This options is mainly for use if we need you to submit details for fault finding to the dbatools team

        .PARAMETER ExportPath
            If specified the output will export via CliXml format to the specified file. This allows you to store the backup history object for later usage, or move it between computers

        .PARAMETER NoClobber
            If specified will stop Export from overwriting an existing file, the default is to overwrite

        .PARAMETER PassThru
            When data is exported the cmdlet will return no other output, this switch means it will also return the normal output which can be then piped into another command

        .PARAMETER MaintenanceSolution
            This switch tells the function that the folder is the root of a Ola Hallengren backup folder

        .PARAMETER IgnoreLogBackup
            This switch only works with the MaintenanceSolution switch. With an Ola Hallengren style backup we can be sure that the LOG folder contains only log backups and skip it.
            For all other scenarios we need to read the file headers to be sure.

        .PARAMETER AzureCredential
            The name of the SQL Server credential to be used if restoring from an Azure hosted backup

        .PARAMETER Import
            When specified along with a path the command will import a previously exported BackupHistory object from an xml file.

        .PARAMETER EnableException
            Replaces user friendly yellow warnings with bloody red exceptions of doom!
            Use this if you want the function to throw terminating errors you want to catch.

        .EXAMPLE
            Get-DbaBackupInformation -SqlInstance Server1 -Path c:\backups\ -DirectoryRecurse

            Will use the Server1 instance to recursively read all backup files under c:\backups, and return a dbatools BackupHistory object

        .NOTES
            Tags: DisasterRecovery, Backup, Restore

            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaBackupInformation

        .EXAMPLE
            Get-DbaBackupInformation -SqlInstance Server1 -Path c:\backups\ -DirectoryRecurse -ExportPath c:\store\BackupHistory.xml

            #Copy the file  c:\store\BackupHistory.xml to another machine via preferred technique, and the on 2nd machine:

            Get-DbaBackupInformation -Import -Path  c:\store\BackupHistory.xml | Restore-DbaDatabase -SqlInstance Server2 -TrustDbBackupHistory

            This allows you to move backup history across servers, or to preserve backup history even after the original server has been purged

        .EXAMPLE
            Get-DbaBackupInformation -SqlInstance Server1 -Path c:\backups\ -DirectoryRecurse -ExportPath c:\store\BackupHistory.xml -PassThru |
                    Restore-DbaDatabase -SqlInstance Server2 -TrustDbBackupHistory

            In this example we gather backup information, export it to an xml file, and then pass it on through to Restore-DbaDatabase
            This allows us to repeat the restore without having to scan all the backup files again

        .EXAMPLE
            Get-ChildItem c:\backups\ -recurse -files |
                Where {$_.extension -in ('.bak','.trn') -and $_.LastWriteTime -gt (get-date).AddMonths(-1)} |
                Get-DbaBackupInformation -SqlInstance Server1 -ExportPath c:\backupHistory.xml

            This lets you keep a record of all backup history from the last month on hand to speed up refreshes

        .EXAMPLE
            $Backups = Get-DbaBackupInformation -SqlInstance Server1 -Path \\network\backups
            $Backups += Get-DbaBackupInformation -SqlInstance Server2 -NoXpDirTree -Path c:\backups

            Scan the unc folder \\network\backups with Server1, and then scan the C:\backups folder on
            Server2 not using xp_dirtree, adding the results to the first set.

        .EXAMPLE
            $Backups = Get-DbaBackupInformation -SqlInstance Server1 -Path \\network\backups -MaintenanceSolution

            When MaintenanceSolution is indicated we know we are dealing with the output from Ola Hallengren's backup scripts. So we make sure that a FULL folder exists in the first level of Path, if not we shortcut scanning all the files as we have nothing to work with

        .EXAMPLE
            $Backups = Get-DbaBackupInformation -SqlInstance Server1 -Path \\network\backups -MaintenanceSolution -IgnoreLogBackup

            As we know we are dealing with an Ola Hallengren style backup folder from the MaintenanceSolution switch, when IgnoreLogBackup is also included we can ignore the LOG folder to skip any scanning of log backups. Note this also means then WON'T be restored
    #>
    [CmdletBinding( DefaultParameterSetName = "Create")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [object[]]$Path,
        [parameter(Mandatory = $true, ParameterSetName = "Create")]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [parameter(ParameterSetName = "Create")]
        [PSCredential]$SqlCredential,
        [string[]]$DatabaseName,
        [string[]]$SourceInstance,
        [parameter(ParameterSetName = "Create")]
        [Switch]$NoXpDirTree,
        [parameter(ParameterSetName = "Create")]
        [switch]$DirectoryRecurse,
        [switch]$EnableException,
        [switch]$MaintenanceSolution,
        [switch]$IgnoreLogBackup,
        [string]$ExportPath,
        [string]$AzureCredential,
        [parameter(ParameterSetName = "Import")]
        [switch]$Import,
        [switch][Alias('Anonymize')]$Anonymise,
        [Switch]$NoClobber,
        [Switch]$PassThru

    )
    begin {
        function Get-HashString {
            param(
                [String]$InString
            )

            $StringBuilder = New-Object System.Text.StringBuilder
            [System.Security.Cryptography.HashAlgorithm]::Create("md5").ComputeHash([System.Text.Encoding]::UTF8.GetBytes($InString))| ForEach-Object {
                [Void]$StringBuilder.Append($_.ToString("x2"))
            }
            return $StringBuilder.ToString()
        }
        Write-Message -Level InternalComment -Message "Starting"
        Write-Message -Level Debug -Message "Parameters bound: $($PSBoundParameters.Keys -join ", ")"

        if (Test-Bound -ParameterName ExportPath) {
            if ($true -eq $NoClobber) {
                if (Test-Path $ExportPath) {
                    Stop-Function -Message "$ExportPath exists and NoClobber set"
                    return
                }
            }
        }
        if ($PSCmdlet.ParameterSetName -eq "Create") {
            try {
                $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                return
            }
        }

        if ($true -eq $IgnoreLogBackup -and $true -ne $MaintenanceSolution) {
            Write-Message -Message "IgnoreLogBackup can only by used with MaintenanceSolution. Will not be used" -Level Warning
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }
        if ((Test-Bound -Parameter Import) -and ($true -eq $Import)) {
            foreach ($f in $Path) {
                if (Test-Path -Path $f) {
                    $GroupResults += Import-CliXml -Path $f
                    foreach ($group in  $GroupResults) {
                        $Group.FirstLsn = [BigInt]$group.FirstLSN.ToString()
                        $Group.CheckpointLSN = [BigInt]$group.CheckpointLSN.ToString()
                        $Group.DatabaseBackupLsn = [BigInt]$group.DatabaseBackupLsn.ToString()
                        $Group.LastLsn = [BigInt]$group.LastLsn.ToString()
                    }
                }
                else {
                    Write-Message -Message "$f does not exist or is unreadable" -Level Warning
                }
            }
        }
        else {
            $Files = @()
            $groupResults = @()
            if ($Path[0] -match 'http') { $NoXpDirTree = $true }
            if ($NoXpDirTree -ne $true) {
                foreach ($f in $path) {
                    if ([System.IO.Path]::GetExtension($f).Length -gt 1) {
                        if ("Fullname" -notin $f.PSobject.Properties.name) {
                            $f = $f | Select-Object *, @{ Name = "FullName"; Expression = { $f } }
                        }
                        Write-Message -Message "Testing a single file $f " -Level Verbose
                        if ((Test-DbaSqlPath -Path $f.fullname -SqlInstance $server)) {
                            $files += $f
                        }
                        else {
                            Write-Message -Level Verbose -Message "$server cannot 'see' file $($f.FullName)"
                        }
                    }
                    elseif ($True -eq $MaintenanceSolution) {
                        if ($true -eq $IgnoreLogBackup -and [System.IO.Path]::GetDirectoryName($f) -like '*LOG') {
                            Write-Message -Level Verbose -Message "Skipping Log Backups as requested"
                        }
                        else {
                            Write-Message -Level Verbose -Message "OLA - Getting folder contents"
                            $Files += Get-XpDirTreeRestoreFile -Path $f -SqlInstance $server
                        }
                    }
                    else {
                        Write-Message -Message "Testing a folder $f" -Level Verbose
                        $Files += $Check = Get-XpDirTreeRestoreFile -Path $f -SqlInstance $server
                        if ($null -eq $check) {
                            Write-Message -Message "Nothing returned from $f" -Level Verbose
                        }
                    }
                }
            }
            else {
                ForEach ($f in $path) {
                    Write-Message -Level VeryVerbose -Message "Not using sql for $f"
                    if ($f -is [System.IO.FileSystemInfo]) {
                        if ($f.PsIsContainer -eq $true -and $true -ne $MaintenanceSolution) {
                            Write-Message -Level VeryVerbose -Message "folder $($f.fullname)"
                            $Files += Get-ChildItem -Path $f.fullname -File -Recurse:$DirectoryRecurse
                        }
                        elseif ($f.PsIsContainer -eq $true -and $true -eq $MaintenanceSolution) {
                            if ($IgnoreLogBackup -and $f -notlike '*LOG' ) {
                                Write-Message -Level Verbose -Message "Skipping Log backups for Maintenance backups"
                            }
                            else {
                                $Files += Get-ChildItem -Path $f.fullname -File -Recurse:$DirectoryRecurse
                            }
                        }
                        elseif ($true -eq $MaintenanceSolution) {
                            $Files += Get-ChildItem -Path $f.fullname -Recurse:$DirectoryRecurse
                        }
                        else {
                            Write-Message -Level VeryVerbose -Message "File"
                            $Files += $f.fullname
                        }
                    }
                    else {
                        if ($true -eq $MaintenanceSolution) {
                            $Files += Get-XpDirTreeRestoreFile -Path $f\FULL -SqlInstance $server -NoRecurse
                            $Files += Get-XpDirTreeRestoreFile -Path $f\DIFF -SqlInstance $server -NoRecurse
                            $Files += Get-XpDirTreeRestoreFile -Path $f\LOG -SqlInstance $server -NoRecurse
                        }
                        else {
                            Write-Message -Level VeryVerbose -Message "File"
                            $Files += $f
                        }
                    }
                }
            }

            if ($True -eq $MaintenanceSolution -and $True -eq $IgnoreLogBackup) {
                Write-Message -Level Verbose -Message "Skipping Log Backups as requested"
                $Files = $Files | Where-Object {$_.FullName -notlike '*\LOG\*'}
            }

            Write-Message -Level Verbose -Message "Reading backup headers of $($Files.Count) files"
            try {
                $FileDetails = Read-DbaBackupHeader -SqlInstance $server -Path $Files -AzureCredential $AzureCredential -EnableException
            }
            catch {
                Stop-Function -Message "Failure reading backup header" -ErrorRecord $_ -Target $server -Continue
            }
            
            $groupdetails = $FileDetails | group-object -Property BackupSetGUID

            foreach ($Group in $GroupDetails) {
                $historyObject = New-Object Sqlcollaborative.Dbatools.Database.BackupHistory
                $historyObject.ComputerName = $group.group[0].MachineName
                $historyObject.InstanceName = $group.group[0].ServiceName
                $historyObject.SqlInstance = $group.group[0].ServerName
                $historyObject.Database = $group.Group[0].DatabaseName
                $historyObject.UserName = $group.Group[0].UserName
                $historyObject.Start = [DateTime]$group.Group[0].BackupStartDate
                $historyObject.End = [DateTime]$group.Group[0].BackupFinishDate
                $historyObject.Duration = ([DateTime]$group.Group[0].BackupFinishDate - [DateTime]$group.Group[0].BackupStartDate)
                $historyObject.Path = [string[]]$Group.Group.BackupPath
                $historyObject.FileList = ($group.Group.FileList | select-object Type, LogicalName, PhysicalName)
                $historyObject.TotalSize = ($Group.Group.BackupSize | Measure-Object -Sum).Sum
                $HistoryObject.CompressedBackupSize = ($Group.Group.CompressedBackupSize | Measure-Object -Sum).Sum
                $historyObject.Type = $group.Group[0].BackupTypeDescription
                $historyObject.BackupSetId = $group.group[0].BackupSetGUID
                $historyObject.DeviceType = 'Disk'
                $historyObject.FullName = $Group.Group.BackupPath
                $historyObject.Position = $group.Group[0].Position
                $historyObject.FirstLsn = $group.Group[0].FirstLSN
                $historyObject.DatabaseBackupLsn = $group.Group[0].DatabaseBackupLSN
                $historyObject.CheckpointLSN = $group.Group[0].CheckpointLSN
                $historyObject.LastLsn = $group.Group[0].LastLsn
                $historyObject.SoftwareVersionMajor = $group.Group[0].SoftwareVersionMajor
                $historyObject.RecoveryModel = $group.Group.RecoveryModel
                $groupResults += $historyObject
            }
        }
        if (Test-Bound 'SourceInstance') {
            $groupResults = $groupResults | Where-Object {$_.InstanceName -in $SourceInstance}
        }

        if (Test-Bound 'DatabaseName') {
            $groupResults = $groupResults | Where-Object {$_.Database -in $DatabaseName}
        }
        if ($true -eq $Anonymise) {
            foreach ($group in $GroupResults) {
                $group.ComputerName = Get-HashString -InString $group.ComputerName
                $group.InstanceName = Get-HashString -InString $group.InstanceName
                $group.SqlInstance = Get-HashString -InString $group.SqlInstance
                $group.Database = Get-HashString -InString $group.Database
                $group.UserName = Get-HashString -InString $group.UserName
                $group.Path = Get-HashString -InString  $Group.Path
                $group.FullName = Get-HashString -InString $Group.Fullname
            }
        }
        if ((Test-Bound -parameterName exportpath) -and $null -ne $ExportPath) {
            $groupResults | Export-CliXml -Path $ExportPath -Depth 5 -NoClobber:$NoClobber
            if ($true -ne $PassThru) {
                return
            }
        }
        $groupResults | Sort-Object -Property End -Descending
    }
}
function Get-DbaClientAlias {
    <#
    .SYNOPSIS
    Creates/updates a sql alias for the specified server - mimics cliconfg.exe

    .DESCRIPTION
    Creates/updates a SQL Server alias by altering HKLM:\SOFTWARE\Microsoft\MSSQLServer\Client

    .PARAMETER ComputerName
    The target computer where the alias will be created

    .PARAMETER Credential
    Allows you to login to remote computers using alternative credentials

    .PARAMETER EnableException
    By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
    Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
    Tags: Alias

    Website: https://dbatools.io
    Copyright: (C) Chrissy LeMaire, [email protected]
    License: MIT https://opensource.org/licenses/MIT

    .LINK
    https://dbatools.io/Get-DbaClientAlias

        .EXAMPLE
    Get-DbaClientAlias
    Gets all SQL Server client aliases on the local computer

    .EXAMPLE
    Get-DbaClientAlias -ComputerName workstationx
    Gets all SQL Server client aliases on Workstationx
#>
    [CmdletBinding()]
    Param (
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($computer in $ComputerName) {
            $scriptblock = {

                function Get-ItemPropertyValue {
                    Param (
                        [parameter()]
                        [String]$Path,
                        [parameter()]
                        [String]$Name
                    )
                    (Get-ItemProperty -LiteralPath $Path -Name $Name).$Name
                }

                $basekeys = "HKLM:\SOFTWARE\WOW6432Node\Microsoft\MSSQLServer", "HKLM:\SOFTWARE\Microsoft\MSSQLServer"

                foreach ($basekey in $basekeys) {

                    if ((Test-Path $basekey) -eq $false) {
                        Write-Warning "Base key ($basekey) does not exist. Quitting."
                        continue
                    }

                    $client = "$basekey\Client"

                    if ((Test-Path $client) -eq $false) {
                        continue
                    }

                    $connect = "$client\ConnectTo"

                    if ((Test-Path $connect) -eq $false) {
                        continue
                    }

                    if ($basekey -like "*WOW64*") {
                        $architecture = "32-bit"
                    }
                    else {
                        $architecture = "64-bit"
                    }

                    # "Creating/updating alias for $ComputerName for $architecture"
                    $all = Get-Item -Path $connect
                    foreach ($entry in $all.Property) {
                        $value = Get-ItemPropertyValue -Path $connect -Name $entry
                        $clean = $value.Replace('DBNMPNTW,', '').Replace('DBMSSOCN,', '')
                        if ($value.StartsWith('DBMSSOCN')) { $protocol = 'TCP/IP' } else { $protocol = 'Named Pipes' }

                        [pscustomobject]@{
                            ComputerName   = $env:COMPUTERNAME
                            NetworkLibrary = $protocol
                            ServerName     = $clean
                            AliasName      = $entry
                            AliasString    = $value
                            Architecture   = $architecture
                        }
                    }
                }
            }

            if ($PScmdlet.ShouldProcess($computer, "Getting aliases")) {
                try {
                    Invoke-Command2 -ComputerName $computer -Credential $Credential -ScriptBlock $scriptblock -ErrorAction Stop |
                        Select-DefaultView -Property ComputerName, Architecture, NetworkLibrary, ServerName, AliasName
                }
                catch {
                    Stop-Function -Message "Failure" -ErrorRecord $_ -Target $computer -Continue
                }
            }
        }
    }
}
function Get-DbaClientProtocol {
    <#
        .SYNOPSIS
            Gets the SQL Server related client protocols on a computer.

        .DESCRIPTION
            Gets the SQL Server related client protocols on one or more computers.

            Requires Local Admin rights on destination computer(s).
            The client protocols can be enabled and disabled when retrieved via WSMan.

        .PARAMETER ComputerName
            The SQL Server (or server in general) that you're connecting to. This command handles named instances.

        .PARAMETER Credential
            Credential object used to connect to the computer as a different user.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Protocol
            Author: Klaas Vandenberghe ( @PowerDBAKlaas )

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaClientProtocol

        .EXAMPLE
            Get-DbaClientProtocol -ComputerName sqlserver2014a

            Gets the SQL Server related client protocols on computer sqlserver2014a.

        .EXAMPLE
            'sql1','sql2','sql3' | Get-DbaClientProtocol

            Gets the SQL Server related client protocols on computers sql1, sql2 and sql3.

        .EXAMPLE
            Get-DbaClientProtocol -ComputerName sql1,sql2 | Out-Gridview

            Gets the SQL Server related client protocols on computers sql1 and sql2, and shows them in a grid view.

        .EXAMPLE
            (Get-DbaClientProtocol -ComputerName sql2 | Where { $_.DisplayName = 'via' }).Disable()

            Disables the VIA ClientNetworkProtocol on computer sql2.
            If succesfull, returncode 0 is shown.
#>
    [CmdletBinding()]
    param (
        [parameter(ValueFromPipeline)]
        [Alias("cn", "host", "Server")]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential] $Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ( $computer in $ComputerName.ComputerName ) {
            $server = Resolve-DbaNetworkName -ComputerName $computer -Credential $credential
            if ( $server.FullComputerName ) {
                $computer = $server.FullComputerName
                Write-Message -Level Verbose -Message "Getting SQL Server namespace on $computer"
                $namespace = Get-DbaCmObject -ComputerName $computer -Namespace root\Microsoft\SQLServer -Query "Select * FROM __NAMESPACE WHERE Name LIke 'ComputerManagement%'" -ErrorAction SilentlyContinue |
                    Where-Object {(Get-DbaCmObject -ComputerName $computer -Namespace $("root\Microsoft\SQLServer\" + $_.Name) -ClassName ClientNetworkProtocol -ErrorAction SilentlyContinue).count -gt 0} |
                    Sort-Object Name -Descending | Select-Object -First 1

                if ( $namespace.Name ) {
                    Write-Message -Level Verbose -Message "Getting Cim class ClientNetworkProtocol in Namespace $($namespace.Name) on $computer"
                    try {
                        $prot = Get-DbaCmObject -ComputerName $computer -Namespace $("root\Microsoft\SQLServer\" + $namespace.Name) -ClassName ClientNetworkProtocol -ErrorAction SilentlyContinue

                        $prot | Add-Member -Force -MemberType ScriptProperty -Name IsEnabled -Value { switch ( $this.ProtocolOrder ) { 0 { $false } default { $true } } }
                        $prot | Add-Member -Force -MemberType ScriptMethod -Name Enable -Value {Invoke-CimMethod -MethodName SetEnable -InputObject $this }
                        $prot | Add-Member -Force -MemberType ScriptMethod -Name Disable -Value {Invoke-CimMethod -MethodName SetDisable -InputObject $this }

                        foreach ( $protocol in $prot ) {
                            Select-DefaultView -InputObject $protocol -Property 'PSComputerName as ComputerName', 'ProtocolDisplayName as DisplayName', 'ProtocolDll as DLL', 'ProtocolOrder as Order', 'IsEnabled'
                        }
                    }
                    catch {
                        Write-Message -Level Warning -Message "No Sql ClientNetworkProtocol found on $computer"
                    }
                } #if namespace
                else {
                    Write-Message -Level Warning -Message "No ComputerManagement Namespace on $computer. Please note that this function is available from SQL 2005 up."
                } #else no namespace
            } #if computername
            else {
                Write-Message -Level Warning -Message "Failed to connect to $computer"
            }
        } #foreach computer
    }
}
function Get-DbaClusterNode {
    <#
        .SYNOPSIS
            Returns the node(s) of a SQL Cluster.

        .DESCRIPTION
            Returns the name of the current node(s) in the SQL Server cluster.

            If the -ActiveNode Parameter is passed it only returns the name of the Server currently hosting the clustered instance.

        .PARAMETER SqlInstance
            Specifies the SQL Server clustered instance to check.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER ActiveNode
            If this parameter is selected the cmdlet will only return the Active Node in the cluster.

        .PARAMETER Detailed
            Output all properties, will be deprecated in 1.0.0 release.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Cluster, WSFC, FCI
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaClusterNode

        .EXAMPLE
            Get-DbaClusterNode -SqlInstance sqlcluster

            Returns all nodes in the cluster and details about each node.

        .EXAMPLE
            Get-DbaClusterNode -SqlInstance sqlcluster -ActiveNode

            Returns the name of the active node in the cluster

    #>
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [switch]$ActiveNode,
        [switch]$Detailed,
        [alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter Detailed
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Alias Get-DbaClusterActiveNode
        $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential -MinimumVersion 10
    }

    process {
        if ($server.IsClustered -eq $false) {
            Stop-Function -Message "Not a clusterd instance." -Category ConnectionError -ErrorRecord $_ -Target $SqlInstance -Continue
        }

        # If the -ActiveNode switch is selected only the primary node is returned.
        if ($ActiveNode) {
            try {
                $sql = "SELECT * FROM sys.dm_os_cluster_nodes where is_current_owner = 1"
                $datatable = $server.query($sql)

                [PSCustomObject]@{
                    ComputerName      = $datatable.nodename
                    InstanceName      = $server.ServiceName
                    SqlInstance       = $server.DomainInstanceName
                    Status            = $datatable.Status
                    StatusDescription = $datatable.StatusDescription
                    CurrentOwner      = $datatable.is_current_owner
                } | Select-DefaultView -Property ComputerName
            }
            catch {
                Stop-Function -Message "Unable to query sys.dm_os_cluster_nodes on $server." -ErrorRecord $_ -Target $SqlInstance -Continue
            }
        }
        #Default Execution of this function
        else {
            try {
                $sql = "SELECT * FROM sys.dm_os_cluster_nodes"
                $datatable = $server.query($sql)

                foreach ($data in $datatable) {
                    [PSCustomObject]@{
                        ComputerName      = $data.nodename
                        InstanceName      = $server.ServiceName
                        SqlInstance       = $server.DomainInstanceName
                        Status            = $data.Status
                        StatusDescription = $data.StatusDescription
                        CurrentOwner      = $data.is_current_owner
                    } | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, StatusDescription, CurrentOwner
                }
            }
            catch {
                Stop-Function -Message "Unable to query sys.dm_os_cluster_nodes on $server." -ErrorRecord $_ -Target $SqlInstance -Continue
            }
        }
    }
}
function Get-DbaCmConnection {
    <#
        .SYNOPSIS
            Retrieves windows management connections from the cache

        .DESCRIPTION
            Retrieves windows management connections from the cache

        .PARAMETER ComputerName
            The computername to ComputerName for.

        .PARAMETER UserName
            Username on credentials to look for. Will not find connections using the default windows credentials.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ComputerManagement, CIM
            Author: Fred Winmann (@FredWeinmann)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaCmConnection

        .EXAMPLE
            Get-DbaCmConnection

            List all cached connections.

        .EXAMPLE
            Get-DbaCmConnection sql2014

            List the cached connection - if any - to the server sql2014.

        .EXAMPLE
            Get-DbaCmConnection -UserName "*charles*"

            List all cached connection that use a username containing "charles" as default or override credentials.
    #>
    [CmdletBinding()]
    param
    (
        [Parameter(Position = 0, ValueFromPipeline = $true)]
        [Alias('Filter')]
        [String[]]
        $ComputerName = "*",

        [String]
        $UserName = "*",

        [switch]
        [Alias('Silent')]$EnableException
    )

    BEGIN {
        Write-Message -Level InternalComment -Message "Starting"
        Write-Message -Level Verbose -Message "Bound parameters: $($PSBoundParameters.Keys -join ", ")"
    }
    PROCESS {
        foreach ($name in $ComputerName) {
            Write-Message -Level VeryVerbose -Message "Processing search. ComputerName: '$name' | Username: '$UserName'"
            ([Sqlcollaborative.Dbatools.Connection.ConnectionHost]::Connections.Values | Where-Object { ($_.ComputerName -like $name) -and ($_.Credentials.UserName -like $UserName) })
        }
    }
    END {
        Write-Message -Level InternalComment -Message "Ending"
    }
}
function Get-DbaCmObject {
    <#
        .SYNOPSIS
            Retrieves Wmi/Cim-Style information from computers.

        .DESCRIPTION
            This function centralizes all requests for information retrieved from Get-WmiObject or Get-CimInstance.
            It uses different protocols as available in this order:
            - Cim over WinRM
            - Cim over DCOM
            - Wmi
            - Wmi over PowerShell Remoting
            It remembers channels that didn't work and will henceforth avoid them. It remembers invalid credentials and will avoid reusing them.
            Much of its behavior can be configured using Test-DbaWmConnection.

        .PARAMETER ClassName
            The name of the class to retrieve.

        .PARAMETER Query
            The Wmi/Cim query tu run against the server.

        .PARAMETER ComputerName
            The computer(s) to connect to. Defaults to localhost.

        .PARAMETER Credential
            Credentials to use. Invalid credentials will be stored in a credentials cache and not be reused.

        .PARAMETER Namespace
            The namespace of the class to use.

        .PARAMETER DoNotUse
            Connection Protocols that should not be used.

        .PARAMETER Force
            Overrides some checks that might otherwise halt execution as a precaution
            - Ignores timeout on bad connections

        .PARAMETER SilentlyContinue
            Use in conjunction with the -EnableException switch.
            By default, Get-DbaCmObject will throw a terminating exception when connecting to a target is impossible in exception enabled mode.
            Setting this switch will cause it write a non-terminating exception and continue with the next computer.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ComputerManagement, CIM
            Author: Fred Winmann (@FredWeinmann)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaCmObject

        .EXAMPLE
            Get-DbaCmObject win32_OperatingSystem

            Retrieves the common operating system information from the local computer.

        .EXAMPLE
            Get-DbaCmObject -Computername "sql2014" -ClassName Win32_OperatingSystem -Credential $cred -DoNotUse CimRM

            Retrieves the common operating system information from the server sql2014.
            It will use the credewntials stored in $cred to connect, unless they are known to not work, in which case they will default to windows credentials (unless another default has been set).
    #>
    [CmdletBinding(DefaultParameterSetName = "Class")]
    param (
        [Parameter(Mandatory = $true, Position = 0, ParameterSetName = "Class")]
        [Alias('Class')]
        [string]
        $ClassName,

        [Parameter(Mandatory = $true, Position = 0, ParameterSetName = "Query")]
        [string]
        $Query,

        [Parameter(ValueFromPipeline = $true)]
        [Sqlcollaborative.Dbatools.Parameter.DbaCmConnectionParameter[]]
        $ComputerName = $env:COMPUTERNAME,

        [System.Management.Automation.PSCredential]
        $Credential,

        [string]
        $Namespace = "root\cimv2",

        [Sqlcollaborative.Dbatools.Connection.ManagementConnectionType[]]
        $DoNotUse = "None",

        [switch]
        $Force,

        [switch]
        $SilentlyContinue,

        [switch]
        [Alias('Silent')]$EnableException
    )

    Begin {
        #region Configuration Values
        $disable_cache = [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::DisableCache

        Write-Message -Level Verbose -Message "Configuration loaded | Cache disabled: $disable_cache"
        #endregion Configuration Values

        $ParSet = $PSCmdlet.ParameterSetName
    }
    Process {
        :main foreach ($connectionObject in $ComputerName) {
            if (-not $connectionObject.Success) { Stop-Function -Message "Failed to interpret input: $($connectionObject.Input)" -Category InvalidArgument -Target $connectionObject.Input -Continue -SilentlyContinue:$SilentlyContinue }

            # Since all connection caching runs using lower-case strings, making it lowercase here simplifies things.
            $computer = $connectionObject.Connection.ComputerName.ToLower()

            Write-Message -Message "[$computer] Retrieving Management Information" -Level VeryVerbose -Target $computer

            $connection = $connectionObject.Connection

            # Ensure using the right credentials
            try { $cred = $connection.GetCredential($Credential) }
            catch {
                $message = "Bad credentials! "
                if ($Credential) { $message += "The credentials for $($Credential.UserName) are known to not work. " }
                else { $message += "The windows credentials are known to not work. " }
                if ($connection.EnableCredentialFailover -or $connection.OverrideExplicitCredential) { $message += "The connection is configured to use credentials that are known to be good, but none have been registered yet. " }
                elseif ($connection.Credentials) { $message += "Working credentials are known for $($connection.Credentials.UserName), however the connection is not configured to automatically use them. This can be done using 'Set-DbaCmConnection -ComputerName $connection -OverrideExplicitCredential' " }
                elseif ($connection.UseWindowsCredentials) { $message += "The windows credentials are known to work, however the connection is not configured to automatically use them. This can be done using 'Set-DbaCmConnection -ComputerName $connection -OverrideExplicitCredential' " }
                $message += $_.Exception.Message
                Stop-Function -Message $message -ErrorRecord $_ -Target $connection -Continue -OverrideExceptionMessage
            }

            # Flags-Enumerations cannot be added in PowerShell 4 or older.
            # Thus we create a string and convert it afterwards.
            $enabledProtocols = "None"
            if ($connection.CimRM -notlike "Disabled") { $enabledProtocols += ", CimRM" }
            if ($connection.CimDCOM -notlike "Disabled") { $enabledProtocols += ", CimDCOM" }
            if ($connection.Wmi -notlike "Disabled") { $enabledProtocols += ", Wmi" }
            if ($connection.PowerShellRemoting -notlike "Disabled") { $enabledProtocols += ", PowerShellRemoting" }
            [Sqlcollaborative.Dbatools.Connection.ManagementConnectionType]$enabledProtocols = $enabledProtocols

            # Create list of excluded connection types (Duplicates don't matter)
            $excluded = @()
            foreach ($item in $DoNotUse) { $excluded += $item }

            :sub while ($true) {
                try { $conType = $connection.GetConnectionType(($excluded -join ","), $Force) }
                catch {
                    if (-not $disable_cache) { [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::Connections[$computer] = $connection }
                    Stop-Function -Message "[$computer] Unable to find a connection to the target system. Ensure the name is typed correctly, and the server allows any of the following protocols: $enabledProtocols" -Target $computer -Category OpenError -Continue -ContinueLabel "main" -SilentlyContinue:$SilentlyContinue -ErrorRecord $_
                }

                switch ($conType.ToString()) {
                    #region CimRM
                    "CimRM" {
                        Write-Message -Level Verbose -Message "[$computer] Accessing computer using Cim over WinRM"
                        try {
                            if ($ParSet -eq "Class") { $connection.GetCimRMInstance($cred, $ClassName, $Namespace) }
                            else { $connection.QueryCimRMInstance($cred, $Query, "WQL", $Namespace) }

                            Write-Message -Level Verbose -Message "[$computer] Accessing computer using Cim over WinRM - Success!"
                            $connection.ReportSuccess('CimRM')
                            $connection.AddGoodCredential($cred)
                            if (-not $disable_cache) { [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::Connections[$computer] = $connection }
                            continue main
                        }
                        catch {
                            Write-Message -Level Verbose -Message "[$computer] Accessing computer using Cim over WinRM - Failed!"

                            # 1 = Generic runtime error
                            if ($_.Exception.InnerException.StatusCode -eq 1) {
                                # 0x8007052e, 0x80070005 : Authentication error, bad credential
                                if (($_.Exception.InnerException -eq 0x8007052e) -or ($_.Exception.InnerException -eq 0x80070005)) {
                                    # Ignore the global setting for bad credential cache disabling, since the connection object is aware of that state and will ignore input if it should.
                                    # This is due to the ability to locally override the global setting, thus it must be done on the object and can then be done in code
                                    $connection.AddBadCredential($cred)
                                    if (-not $disable_cache) { [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::Connections[$computer] = $connection }
                                    Stop-Function -Message "[$computer] Invalid connection credentials" -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue -OverrideExceptionMessage
                                }
                                elseif ($_.Exception.InnerException.MessageId -eq "HRESULT 0x80041013") {
                                    if ($ParSet -eq "Class") { Stop-Function -Message "[$computer] Failed to access $class in namespace $Namespace!" -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue -Exception $_.Exception.InnerException }
                                    else { Stop-Function -Message "[$computer] Failed to execute $query in namespace $Namespace!" -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue -Exception $_.Exception.InnerException }
                                }
                                else {
                                    $connection.ReportFailure('CimRM')
                                    $excluded += "CimRM"
                                    continue sub
                                }
                            }

                            # 2 = Access to specific resource denied
                            elseif ($_.Exception.InnerException.StatusCode -eq 2) {
                                Stop-Function -Message "[$computer] Access to computer granted, but access to $Namespace\$ClassName denied!" -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue -OverrideExceptionMessage
                            }

                            # 3 = Invalid Namespace
                            elseif ($_.Exception.InnerException.StatusCode -eq 3) {
                                Stop-Function -Message "[$computer] Invalid namespace: $Namespace" -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue -OverrideExceptionMessage
                            }
                            # 5 = Invalid Class
                            # See here for code reference: https://msdn.microsoft.com/en-us/library/cc150671(v=vs.85).aspx
                            elseif ($_.Exception.InnerException.StatusCode -eq 5) {
                                Stop-Function -Message "[$computer] Invalid class name ($ClassName), not found in current namespace ($Namespace)" -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue -OverrideExceptionMessage
                            }

                            # 0 & ExtendedStatus = Weird issue beyond the scope of the CIM standard. Often a server-side issue
                            elseif (($_.Exception.InnerException.StatusCode -eq 0) -and ($_.Exception.InnerException.ErrorData.original_error -like "__ExtendedStatus")) {
                                Stop-Function -Message "[$computer] Something went wrong when looking for $ClassName, in $Namespace. This often indicates issues with the target system." -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue
                            }
                            else {
                                $connection.ReportFailure('CimRM')
                                $excluded += "CimRM"
                                continue sub
                            }
                        }
                    }
                    #endregion CimRM

                    #region CimDCOM
                    "CimDCOM" {
                        Write-Message -Level Verbose -Message "[$computer] Accessing computer using Cim over DCOM"
                        try {
                            if ($ParSet -eq "Class") { $connection.GetCimDCOMInstance($cred, $ClassName, $Namespace) }
                            else { $connection.QueryCimDCOMInstance($cred, $Query, "WQL", $Namespace) }

                            Write-Message -Level Verbose -Message "[$computer] Accessing computer using Cim over DCOM - Success!"
                            $connection.ReportSuccess('CimDCOM')
                            $connection.AddGoodCredential($cred)
                            if (-not $disable_cache) { [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::Connections[$computer] = $connection }
                            continue main
                        }
                        catch {
                            Write-Message -Level Verbose -Message "[$computer] Accessing computer using Cim over DCOM - Failed!"

                            # 1 = Generic runtime error
                            if ($_.Exception.InnerException.StatusCode -eq 1) {
                                # 0x8007052e, 0x80070005 : Authentication error, bad credential
                                if (($_.Exception.InnerException -eq 0x8007052e) -or ($_.Exception.InnerException -eq 0x80070005)) {
                                    # Ignore the global setting for bad credential cache disabling, since the connection object is aware of that state and will ignore input if it should.
                                    # This is due to the ability to locally override the global setting, thus it must be done on the object and can then be done in code
                                    $connection.AddBadCredential($cred)
                                    if (-not $disable_cache) { [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::Connections[$computer] = $connection }
                                    Stop-Function -Message "[$computer] Invalid connection credentials" -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue -OverrideExceptionMessage
                                }
                                elseif ($_.Exception.InnerException.MessageId -eq "HRESULT 0x80041013") {
                                    if ($ParSet -eq "Class") { Stop-Function -Message "[$computer] Failed to access $class in namespace $Namespace!" -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue -Exception $_.Exception.InnerException }
                                    else { Stop-Function -Message "[$computer] Failed to execute $query in namespace $Namespace!" -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue -Exception $_.Exception.InnerException }
                                }
                                else {
                                    $connection.ReportFailure('CimDCOM')
                                    $excluded += "CimDCOM"
                                    continue sub
                                }
                            }

                            # 2 = Access to specific resource denied
                            elseif ($_.Exception.InnerException.StatusCode -eq 2) {
                                Stop-Function -Message "[$computer] Access to computer granted, but access to $Namespace\$ClassName denied!" -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue -OverrideExceptionMessage
                            }

                            # 3 = Invalid Namespace
                            elseif ($_.Exception.InnerException.StatusCode -eq 3) {
                                Stop-Function -Message "[$computer] Invalid namespace: $Namespace" -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue -OverrideExceptionMessage
                            }

                            # 5 = Invalid Class
                            # See here for code reference: https://msdn.microsoft.com/en-us/library/cc150671(v=vs.85).aspx
                            elseif ($_.Exception.InnerException.StatusCode -eq 5) {
                                Stop-Function -Message "[$computer] Invalid class name ($ClassName), not found in current namespace ($Namespace)" -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue -OverrideExceptionMessage
                            }

                            # 0 & ExtendedStatus = Weird issue beyond the scope of the CIM standard. Often a server-side issue
                            elseif (($_.Exception.InnerException.StatusCode -eq 0) -and ($_.Exception.InnerException.ErrorData.original_error -like "__ExtendedStatus")) {
                                Stop-Function -Message "[$computer] Something went wrong when looking for $ClassName, in $Namespace. This often indicates issues with the target system." -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue
                            }

                            else {
                                $connection.ReportFailure('CimDCOM')
                                $excluded += "CimDCOM"
                                continue sub
                            }
                        }
                    }
                    #endregion CimDCOM

                    #region Wmi
                    "Wmi" {
                        Write-Message -Level Verbose -Message "[$computer] Accessing computer using WMI"
                        try {
                            switch ($ParSet) {
                                "Class" {
                                    $parameters = @{
                                        ComputerName = $computer
                                        ClassName    = $ClassName
                                        ErrorAction  = 'Stop'
                                    }
                                    if ($cred) { $parameters["Credential"] = $cred }
                                    if (Test-Bound "Namespace") { $parameters["Namespace"] = $Namespace }

                                }
                                "Query" {
                                    $parameters = @{
                                        ComputerName = $computer
                                        Query        = $Query
                                        ErrorAction  = 'Stop'
                                    }
                                    if ($cred) { $parameters["Credential"] = $cred }
                                    if (Test-Bound "Namespace") { $parameters["Namespace"] = $Namespace }
                                }
                            }

                            Get-WmiObject @parameters

                            Write-Message -Level Verbose -Message "[$computer] Accessing computer using WMI - Success!"
                            $connection.ReportSuccess('Wmi')
                            $connection.AddGoodCredential($cred)
                            if (-not $disable_cache) { [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::Connections[$computer] = $connection }
                            continue main
                        }
                        catch {
                            Write-Message -Level Verbose -Message "[$computer] Accessing computer using WMI - Failed!" -ErrorRecord $_

                            if ($_.CategoryInfo.Reason -eq "UnauthorizedAccessException") {
                                # Ignore the global setting for bad credential cache disabling, since the connection object is aware of that state and will ignore input if it should.
                                # This is due to the ability to locally override the global setting, thus it must be done on the object and can then be done in code
                                $connection.AddBadCredential($cred)
                                if (-not $disable_cache) { [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::Connections[$computer] = $connection }
                                Stop-Function -Message "[$computer] Invalid connection credentials" -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue
                            }
                            elseif ($_.CategoryInfo.Category -eq "InvalidType") {
                                Stop-Function -Message "[$computer] Invalid class name ($ClassName), not found in current namespace ($Namespace)" -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue
                            }
                            elseif ($_.Exception.ErrorCode -eq "ProviderLoadFailure") {
                                Stop-Function -Message "[$computer] Failed to access: $ClassName, in namespace: $Namespace - There was a provider error. This indicates a potential issue with WMI on the server side." -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue
                            }
                            else {
                                $connection.ReportFailure('Wmi')
                                $excluded += "Wmi"
                                continue sub
                            }
                        }
                    }
                    #endregion Wmi

                    #region PowerShell Remoting
                    "PowerShellRemoting" {
                        try {
                            Write-Message -Level Verbose -Message "[$computer] Accessing computer using PowerShell Remoting"
                            $scp_string = "Get-WmiObject -Class $ClassName -ErrorAction Stop"
                            if ($PSBoundParameters.ContainsKey("Namespace")) { $scp_string += " -Namespace $Namespace" }

                            $parameters = @{
                                ScriptBlock  = ([System.Management.Automation.ScriptBlock]::Create($scp_string))
                                ComputerName = $ComputerName
                                ErrorAction  = 'Stop'
                            }
                            if ($Credential) { $parameters["Credential"] = $Credential }
                            Invoke-Command @parameters

                            Write-Message -Level Verbose -Message "[$computer] Accessing computer using PowerShell Remoting - Success!"
                            $connection.ReportSuccess('PowerShellRemoting')
                            $connection.AddGoodCredential($cred)
                            if (-not $disable_cache) { [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::Connections[$computer] = $connection }
                            continue main
                        }
                        catch {
                            # Will always consider authenticated, since any call with credentials to a server that doesn't exist will also carry invalid credentials error.
                            # There simply is no way to differentiate between actual authentication errors and server not reached
                            $connection.ReportFailure('PowerShellRemoting')
                            $excluded += "PowerShellRemoting"
                            continue sub
                        }
                    }
                    #endregion PowerShell Remoting
                }
            }
        }
    }
    End {

    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaComputerCertificate {
    <#
        .SYNOPSIS
            Simplifies finding computer certificates that are candidates for using with SQL Server's network encryption

        .DESCRIPTION
            Gets computer certificates on localhost that are candidates for using with SQL Server's network encryption

        .PARAMETER ComputerName
            The target SQL Server - defaults to localhost. If target is a cluster, you must specify the distinct nodes.

        .PARAMETER Credential
            Allows you to login to $ComputerName using alternative credentials.

        .PARAMETER Store
            Certificate store - defaults to LocalMachine

        .PARAMETER Folder
            Certificate folder - defaults to My (Personal)

        .PARAMETER Path
            The path to a certificate - basically changes the path into a certificate object

        .PARAMETER Thumbprint
            Return certificate based on thumbprint

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Certificate
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            Get-DbaComputerCertificate
            Gets computer certificates on localhost that are candidates for using with SQL Server's network encryption

        .EXAMPLE
            Get-DbaComputerCertificate -ComputerName sql2016

            Gets computer certificates on sql2016 that are candidates for using with SQL Server's network encryption

        .EXAMPLE
            Get-DbaComputerCertificate -ComputerName sql2016 -Thumbprint 8123472E32AB412ED4288888B83811DB8F504DED, 04BFF8B3679BB01A986E097868D8D494D70A46D6

            Gets computer certificates on sql2016 that match thumbprints 8123472E32AB412ED4288888B83811DB8F504DED or 04BFF8B3679BB01A986E097868D8D494D70A46D6
    #>
    [CmdletBinding()]
    param (
        [parameter(ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer", "SqlInstance")]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [string]$Store = "LocalMachine",
        [string]$Folder = "My",
        [string]$Path,
        [string[]]$Thumbprint,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        #region Scriptblock for remoting
        $scriptblock = {
            param (
                $Thumbprint,
                $Store,
                $Folder,
                $Path
            )

            if ($Path) {
                $bytes = [System.IO.File]::ReadAllBytes($path)
                $Certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
                $Certificate.Import($bytes, $null, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::DefaultKeySet)
                return $Certificate
            }

            if ($Thumbprint) {
                try {
                    Write-Verbose "Searching Cert:\$Store\$Folder"
                    Get-ChildItem "Cert:\$Store\$Folder" -Recurse | Where-Object Thumbprint -in $Thumbprint
                }
                catch {
                    # don't care - there's a weird issue with remoting where an exception gets thrown for no apparent reason
                }
            }
            else {
                try {
                    Write-Verbose "Searching Cert:\$Store\$Folder"
                    Get-ChildItem "Cert:\$Store\$Folder" -Recurse | Where-Object { "$($_.EnhancedKeyUsageList)" -match '1\.3\.6\.1\.5\.5\.7\.3\.1' }
                }
                catch {
                    # still don't care
                }
            }
        }
        #endregion Scriptblock for remoting
    }

    process {
        foreach ($computer in $computername) {
            try {
                Invoke-Command2 -ComputerName $computer -Credential $Credential -ScriptBlock $scriptblock -ArgumentList $thumbprint, $Store, $Folder, $Path -ErrorAction Stop | Select-DefaultView -Property FriendlyName, DnsNameList, Thumbprint, NotBefore, NotAfter, Subject, Issuer
            }
            catch {
                Stop-Function -Message "Issue connecting to computer" -ErrorRecord $_ -Target $computer -Continue
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaComputerSystem {
    <#
        .SYNOPSIS
            Gets computer system information from the server.

        .DESCRIPTION
            Gets computer system information from the server and returns as an object.

        .PARAMETER ComputerName
            Target computer(s). If no computer name is specified, the local computer is targeted

        .PARAMETER Credential
            Alternate credential object to use for accessing the target computer(s).

        .PARAMETER IncludeAws
            If computer is hosted in AWS Infrastructure as a Service (IaaS), additional information will be included.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ServerInfo
            Author: Shawn Melton (@wsmelton | http://blog.wsmelton.info)

            Website: https: //dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaComputerSystem

        .EXAMPLE
            Get-DbaComputerSystem

            Returns information about the local computer's computer system

        .EXAMPLE
            Get-DbaComputerSystem -ComputerName sql2016

            Returns information about the sql2016's computer system

        .EXAMPLE
            Get-DbaComputerSystem -ComputerName sql2016 -IncludeAws

            Returns information about the sql2016's computer system and includes additional properties around the EC2 instance.
    #>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Alias("cn", "host", "Server")]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [switch]$IncludeAws,
        [switch][Alias('Silent')]
        $EnableException
    )
    process {
        foreach ($computer in $ComputerName) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $computer"
                $server = Resolve-DbaNetworkName -ComputerName $computer.ComputerName -Credential $Credential

                $computerResolved = $server.FullComputerName

                if (!$computerResolved) {
                    Stop-Function -Message "Unable to resolve hostname of $computer. Skipping." -Continue
                }

                if (Test-Bound "Credential") {
                    $computerSystem = Get-DbaCmObject -ClassName Win32_ComputerSystem -ComputerName $computerResolved -Credential $Credential
                }
                else {
                    $computerSystem = Get-DbaCmObject -ClassName Win32_ComputerSystem -ComputerName $computerResolved
                }

                $adminPasswordStatus =
                switch ($computerSystem.AdminPasswordStatus) {
                    0 { "Disabled" }
                    1 { "Enabled" }
                    2 { "Not Implemented" }
                    3 { "Unknown" }
                    default { "Unknown" }
                }

                $domainRole =
                switch ($computerSystem.DomainRole) {
                    0 { "Standalone Workstation" }
                    1 { "Member Workstation" }
                    2 { "Standalone Server" }
                    3 { "Member Server" }
                    4 { "Backup Domain Controller" }
                    5 { "Primary Domain Controller" }
                }

                $isHyperThreading = $false
                if ($computerSystem.NumberOfLogicalProcessors -gt $computerSystem.NumberofProcessors) {
                    $isHyperThreading = $true
                }

                if ($IncludeAws) {
                    $isAws = Invoke-Command2 -ComputerName $computerResolved -Credential $Credential -ScriptBlock { ((Invoke-WebRequest -TimeoutSec 15 -Uri 'http://169.254.169.254').StatusCode) -eq 200 } -Raw

                    if ($isAws) {
                        $scriptBlock = {
                            [PSCustomObject]@{
                                AmiId                 = (Invoke-WebRequest -Uri 'http://169.254.169.254/latest/meta-data/ami-id').Content
                                IamRoleArn            = ((Invoke-WebRequest -Uri 'http://169.254.169.254/latest/meta-data/iam/info').Content | ConvertFrom-Json).InstanceProfileArn
                                InstanceId            = (Invoke-WebRequest -Uri 'http://169.254.169.254/latest/meta-data/instance-id').Content
                                InstanceType          = (Invoke-WebRequest -Uri 'http://169.254.169.254/latest/meta-data/instance-type').Content
                                AvailabilityZone      = (Invoke-WebRequest -Uri 'http://169.254.169.254/latest/meta-data/placement/availability-zone').Content
                                PublicHostname        = (Invoke-WebRequest -Uri 'http://169.254.169.254/latest/meta-data/public-hostname').Content
                            }
                        }
                        $awsProps = Invoke-Command2 -ComputerName $computerResolved -Credential $Credential -ScriptBlock $scriptBlock
                    }
                    else {
                        Write-Message -Level Warning -Message "$computerResolved was not found to be an EC2 instance. Verify http://169.254.169.254 is accessible on the computer."
                    }
                }
                $inputObject = [PSCustomObject]@{
                    ComputerName                 = $computerResolved
                    Domain                       = $computerSystem.Domain
                    DomainRole                   = $domainRole
                    Manufacturer                 = $computerSystem.Manufacturer
                    Model                        = $computerSystem.Model
                    SystemFamily                 = $computerSystem.SystemFamily
                    SystemSkuNumber              = $computerSystem.SystemSKUNumber
                    SystemType                   = $computerSystem.SystemType
                    NumberLogicalProcessors      = $computerSystem.NumberOfLogicalProcessors
                    NumberProcessors             = $computerSystem.NumberOfProcessors
                    IsHyperThreading             = $isHyperThreading
                    TotalPhysicalMemory          = [DbaSize]$computerSystem.TotalPhysicalMemory
                    IsDaylightSavingsTime        = $computerSystem.EnableDaylightSavingsTime
                    DaylightInEffect             = $computerSystem.DaylightInEffect
                    DnsHostName                  = $computerSystem.DNSHostName
                    IsSystemManagedPageFile      = $computerSystem.AutomaticManagedPagefile
                    AdminPasswordStatus          = $adminPasswordStatus
                }
                if ($IncludeAws -and $isAws) {
                    Add-Member -Force -InputObject $inputObject -MemberType NoteProperty -Name AwsAmiId -Value $awsProps.AmiId
                    Add-Member -Force -InputObject $inputObject -MemberType NoteProperty -Name AwsIamRoleArn -Value $awsProps.IamRoleArn
                    Add-Member -Force -InputObject $inputObject -MemberType NoteProperty -Name AwsEc2InstanceId -Value $awsProps.InstanceId
                    Add-Member -Force -InputObject $inputObject -MemberType NoteProperty -Name AwsEc2InstanceType -Value $awsProps.InstanceType
                    Add-Member -Force -InputObject $inputObject -MemberType NoteProperty -Name AwsAvailabilityZone -Value $awsProps.AvailabilityZone
                    Add-Member -Force -InputObject $inputObject -MemberType NoteProperty -Name AwsPublicHostName -Value $awsProps.PublicHostname
                }
                $excludes = 'SystemSkuNumber', 'IsDaylightSavingsTime', 'DaylightInEffect', 'DnsHostName', 'AdminPasswordStatus'
                Select-DefaultView -InputObject $inputObject -ExcludeProperty $excludes
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_ -Target $computer -Continue
            }
        }
    }
}
function Get-DbaConfig {
    <#
        .SYNOPSIS
            Retrieves configuration elements by name.

        .DESCRIPTION
            Retrieves configuration elements by name.
            Can be used to search the existing configuration list.

        .PARAMETER FullName
            Default: "*"
            Search for configurations using the full name

        .PARAMETER Name
            Default: "*"
            The name of the configuration element(s) to retrieve.
            May be any string, supports wildcards.

        .PARAMETER Module
            Default: "*"
            Search configuration by module.

        .PARAMETER Force
            Overrides the default behavior and also displays hidden configuration values.

        .NOTES
            Tags: Config, Module
            Author: Friedrich Weinmann

        .EXAMPLE
            PS C:\> Get-DbaConfig 'Mail.To'

            Retrieves the configuration element for the key "Mail.To"

        .EXAMPLE
            PS C:\> Get-DbaConfig -Force

            Retrieve all configuration elements from all modules, even hidden ones.
    #>
    [CmdletBinding(DefaultParameterSetName = "FullName")]
    Param (
        [Parameter(ParameterSetName = "FullName", Position = 0)]
        [string]
        $FullName = "*",

        [Parameter(ParameterSetName = "Module", Position = 1)]
        [string]
        $Name = "*",

        [Parameter(ParameterSetName = "Module", Position = 0)]
        [string]
        $Module = "*",

        [switch]
        $Force
    )

    switch ($PSCmdlet.ParameterSetName) {
        "Module" {
            $Name = $Name.ToLower()
            $Module = $Module.ToLower()

            [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations.Values | Where-Object { ($_.Name -like $Name) -and ($_.Module -like $Module) -and ((-not $_.Hidden) -or ($Force)) } | Sort-Object Module, Name
        }

        "FullName" {
            [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations.Values | Where-Object { ("$($_.Module).$($_.Name)" -like $FullName) -and ((-not $_.Hidden) -or ($Force)) } | Sort-Object Module, Name
        }
    }
}
function Get-DbaConfigValue {
    <#
        .SYNOPSIS
            Returns the configuration value stored under the specified name.

        .DESCRIPTION
            Returns the configuration value stored under the specified name.
            It requires the full name (<Module>.<Name>) and is usually only called by functions.

        .PARAMETER FullName
            The full name (<Module>.<Name>) of the configured value to return.

        .PARAMETER Fallback
            A fallback value to use, if no value was registered to a specific configuration element.
            This basically is a default value that only applies on a "per call" basis, rather than a system-wide default.

        .PARAMETER NotNull
            By default, this function returns null if one tries to retrieve the value from either a Configuration that does not exist or a Configuration whose value was set to null.
            However, sometimes it may be important that some value was returned.
            By specifying this parameter, the function will throw an error if no value was found at all.

        .EXAMPLE
            PS C:\> Get-DbaConfigValue -Name 'System.MailServer'

            Returns the configured value that was assigned to the key 'System.MailServer'

        .EXAMPLE
            PS C:\> Get-DbaConfigValue -Name 'Default.CoffeeMilk' -Fallback 0

            Returns the configured value for 'Default.CoffeeMilk'. If no such value is configured, it returns '0' instead.

        .NOTES
            Author: Friedrich Weinmann
            Tags: Config
    #>
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSPossibleIncorrectComparisonWithNull", "")]
    [CmdletBinding()]
    Param (
        [Alias('Name')]
        [Parameter(Mandatory = $true)]
        [string]
        $FullName,

        [object]
        $Fallback,

        [switch]
        $NotNull
    )

    $FullName = $FullName.ToLower()

    $temp = $null
    $temp = [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations[$FullName].Value
    if ($temp -eq $null) { $temp = $Fallback }

    if ($NotNull -and ($temp -eq $null)) {
        Stop-Function -Message "No Configuration Value available for $Name" -EnableException $true -Category InvalidData -Target $FullName
    }
    else {
        return $temp
    }
}
#ValidationTags#CodeStyle,Messaging,FlowControl,Pipeline#
function Get-DbaConnection {
    <#
        .SYNOPSIS
            Returns a bunch of information from dm_exec_connections.

        .DESCRIPTION
            Returns a bunch of information from dm_exec_connections which, according to Microsoft:
            "Returns information about the connections established to this instance of SQL Server and the details of each connection. Returns server wide connection information for SQL Server. Returns current database connection information for SQL Database."
    
        .PARAMETER SqlInstance
            The target SQL Server instance. Server(s) must be SQL Server 2005 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Connection
            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaConnection

        .EXAMPLE
            Get-DbaConnection -SqlInstance sql2016, sql2017
            
            Returns client connection information from sql2016 and sql2017
    #>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential", "Cred")]
        [PSCredential]$SqlCredential,
        [switch]$EnableException
    )
    
    begin {
        $sql = "SELECT  SERVERPROPERTY('MachineName') AS ComputerName,
                            ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
                            SERVERPROPERTY('ServerName') AS SqlInstance,
                            session_id as SessionId, most_recent_session_id as MostRecentSessionId, connect_time as ConnectTime,
                            net_transport as Transport, protocol_type as ProtocolType, protocol_version as ProtocolVersion,
                            endpoint_id as EndpointId, encrypt_option as EncryptOption, auth_scheme as AuthScheme, node_affinity as NodeAffinity,
                            num_reads as Reads, num_writes as Writes, last_read as LastRead, last_write as LastWrite,
                            net_packet_size as PacketSize, client_net_address as ClientNetworkAddress, client_tcp_port as ClientTcpPort,
                            local_net_address as ServerNetworkAddress, local_tcp_port as ServerTcpPort, connection_id as ConnectionId,
                            parent_connection_id as ParentConnectionId, most_recent_sql_handle as MostRecentSqlHandle
                            FROM sys.dm_exec_connections"
    }
    
    process {
        foreach ($instance in $SqlInstance) {
            
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            
            Write-Message -Level Debug -Message "Getting results for the following query: $sql."
            try {
                $server.Query($sql)
            }
            catch {
                Stop-Function -Message "Failure" -Target $server -Exception $_ -Continue
            }
        }
    }
}
function Get-DbaCpuUsage {
<#
    .SYNOPSIS
        Provides detailed CPU usage information about a SQL Server's process

    .DESCRIPTION
        "If there are a lot of processes running on your instance and the CPU is very high,
        then it's hard to find the exact process eating up your CPU using just the SQL Server
        tools. One way to correlate the data between what is running within SQL Server and at
        the Windows level is to use SPID and KPID values to get the exact process."

        This command automates that process.

        References: https://www.mssqltips.com/sqlservertip/2454/how-to-find-out-how-much-cpu-a-sql-server-process-is-really-using/

        Note: This command returns results from all SQL instances on the destionation server but the process
        column is specific to -SqlInstance passed.

    .PARAMETER SqlInstance
        Allows you to specify a comma separated list of servers to query.

    .PARAMETER SqlCredential
        Allows you to login to the SQL instance using alternative credentials.

    .PARAMETER Credential
        Allows you to login to the Windows Server using alternative credentials.

    .PARAMETER Threshold
        CPU threshold.

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Tags: CPU
        dbatools PowerShell module (https://dbatools.io, [email protected])
        Copyright (C) 2016 Chrissy LeMaire
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Get-DbaCpuUsage

    .EXAMPLE
        Get-DbaCpuUsage -SqlInstance sql2017

        Logs into the SQL Server instance "sql2017" and also the Computer itself (via WMI) to gather information

    .EXAMPLE
        $usage = Get-DbaCpuUsage -SqlInstance sql2017
        $usage.Process

        Explores the processes (from Get-DbaProcess) associated with the usage results

    .EXAMPLE
        Get-DbaCpuUsage -SqlInstance sql2017 -SqlCredential (Get-Credential sqladmin) -Credential (Get-Credential ad\sqldba)

        Logs into the SQL instance using the SQL Login 'sqladmin' and then Windows instance as 'ad\sqldba'
#>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [PSCredential]$Credential,
        [int]$Threshold = 0,
        [switch]$EnableException
    )
    begin {
        # This can likely be enumerated but I don't know hows
        $threadstates = [pscustomobject]@{
            0 = 'Initialized. It is recognized by the microkernel.'
            1 = 'Ready. It is prepared to run on the next available processor.'
            2 = 'Running. It is executing.'
            3 = 'Standby. It is about to run. Only one thread may be in this state at a time.'
            4 = 'Terminated. It is finished executing.'
            5 = 'Waiting. It is not ready for the processor. When ready, it will be rescheduled.'
            6 = 'Transition. The thread is waiting for resources other than the processor.'
            7 = 'Unknown. The thread state is unknown.'
        }

        $threadwaitreasons = [pscustomobject]@{
            0 = 'Executive'
            1 = 'FreePage'
            2 = 'PageIn'
            3 = 'PoolAllocation'
            4 = 'ExecutionDelay'
            5 = 'FreePage'
            6 = 'PageIn'
            7 = 'Executive'
            8 = 'FreePage'
            9 = 'PageIn'
            10 = 'PoolAllocation'
            11 = 'ExecutionDelay'
            12 = 'FreePage'
            13 = 'PageIn'
            14 = 'EventPairHigh'
            15 = 'EventPairLow'
            16 = 'LPCReceive'
            17 = 'LPCReply'
            18 = 'VirtualMemory'
            19 = 'PageOut'
            20 = 'Unknown'
        }
    }
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $processes = Get-DbaProcess -SqlInstance $server
            $threads = Get-DbaCmObject -ComputerName $instance.ComputerName -ClassName Win32_PerfFormattedData_PerfProc_Thread -Credential $Credential | Where-Object { $_.Name -like 'sql*' -and $_.PercentProcessorTime -ge $Threshold }

            if ($server.VersionMajor -eq 8) {
                $spidcollection = $server.Query("select spid, kpid from sysprocesses")
            }
            else {
                $spidcollection = $server.Query("select t.os_thread_id as kpid, s.session_id as spid
            from sys.dm_exec_sessions s
            join sys.dm_exec_requests er on s.session_id = er.session_id
            join sys.dm_os_workers w on er.task_address = w.task_address
            join sys.dm_os_threads t on w.thread_address = t.thread_address")
            }

            foreach ($thread in $threads) {
                $spid = ($spidcollection | Where-Object kpid -eq $thread.IDThread).spid
                $process = $processes | Where-Object spid -eq $spid
                $threadwaitreason = $thread.ThreadWaitReason
                $threadstate = $thread.ThreadState
                $ThreadStateValue = $threadstates.$threadstate
                $ThreadWaitReasonValue = $threadwaitreasons.$threadwaitreason

                Add-Member -Force -InputObject $thread -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                Add-Member -Force -InputObject $thread -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                Add-Member -Force -InputObject $thread -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                Add-Member -Force -InputObject $thread -MemberType NoteProperty -Name Processes -Value ($processes | Where-Object HostProcessID -eq $thread.IDProcess)
                Add-Member -Force -InputObject $thread -MemberType NoteProperty -Name ThreadStateValue -Value $ThreadStateValue
                Add-Member -Force -InputObject $thread -MemberType NoteProperty -Name ThreadWaitReasonValue -Value $ThreadWaitReasonValue
                Add-Member -Force -InputObject $thread -MemberType NoteProperty -Name Process -Value $process
                Add-Member -Force -InputObject $thread -MemberType NoteProperty -Name Query -Value $process.LastQuery
                Add-Member -Force -InputObject $thread -MemberType NoteProperty -Name Spid -Value $spid

                Select-DefaultView -InputObject $thread -Property ComputerName, InstanceName, SqlInstance, Name, ContextSwitchesPersec, ElapsedTime, IDProcess, Spid, PercentPrivilegedTime, PercentProcessorTime, PercentUserTime, PriorityBase, PriorityCurrent, StartAddress, ThreadStateValue, ThreadWaitReasonValue, Process, Query
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,CodeStyle#
function Get-DbaCredential {
    <#
        .SYNOPSIS
            Gets SQL Credential information for each instance(s) of SQL Server.

        .DESCRIPTION
            The Get-DbaCredential command gets SQL Credential information for each instance(s) of SQL Server.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function
            to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Name
            Only include specific names
            Note: if spaces exist in the credential name, you will have to type "" or '' around it.

        .PARAMETER ExcludeName
            Excluded credential names

        .PARAMETER Identity
            Only include specific identities
            Note: if spaces exist in the credential identity, you will have to type "" or '' around it.

        .PARAMETER ExcludeIdentity
            Excluded identities

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Credential
            Author: Garry Bargsley (@gbargsley), http://blog.garrybargsley.com

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaCredential

        .EXAMPLE
            Get-DbaCredential -SqlInstance localhost

            Returns all SQL Credentials on the local default SQL Server instance

        .EXAMPLE
            Get-DbaCredential -SqlInstance localhost, sql2016 -Name 'PowerShell Proxy'

            Returns the SQL Credentials named 'PowerShell Proxy' for the local and sql2016 SQL Server instances

        .EXAMPLE
            Get-DbaCredential -SqlInstance localhost, sql2016 -Identity ad\powershell

            Returns the SQL Credentials for the account 'ad\powershell' on the local and sql2016 SQL Server instances
    #>
    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "")]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$Name,
        [string[]]$ExcludeName,
        [Alias('CredentialIdentity')]
        [string[]]$Identity,
        [Alias('ExcludeCredentialIdentity')]
        [string[]]$ExcludeIdentity,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $credential = $server.Credentials

            if ($Name) {
                $credential = $credential | Where-Object { $Name -contains $_.Name }
            }

            if ($ExcludeName) {
                $credential = $credential | Where-Object { $ExcludeName -notcontains $_.Name }
            }

            if ($Identity) {
                $credential = $credential | Where-Object { $Identity -contains $_.Identity }
            }

            if ($ExcludeIdentity) {
                $credential = $credential | Where-Object { $ExcludeIdentity -notcontains $_.Identity }
            }

            foreach ($currentcredential in $credential) {
                Add-Member -Force -InputObject $currentcredential -MemberType NoteProperty -Name ComputerName -value $currentcredential.Parent.ComputerName
                Add-Member -Force -InputObject $currentcredential -MemberType NoteProperty -Name InstanceName -value $currentcredential.Parent.ServiceName
                Add-Member -Force -InputObject $currentcredential -MemberType NoteProperty -Name SqlInstance -value $currentcredential.Parent.DomainInstanceName

                Select-DefaultView -InputObject $currentcredential -Property ComputerName, InstanceName, SqlInstance, ID, Name, Identity, MappedClassType, ProviderName
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,CodeStyle#
function Get-DbaCustomError {
    <#
        .SYNOPSIS
            Gets SQL Custom Error Message information for each instance(s) of SQL Server.

        .DESCRIPTION
            The Get-DbaCustomError command gets SQL Custom Error Message information for each instance(s) of SQL Server.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function
            to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: CustomError
            Author: Garry Bargsley (@gbargsley), http://blog.garrybargsley.com
            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaCustomError

        .EXAMPLE
            Get-DbaCustomError -SqlInstance localhost

            Returns all Custom Error Message(s) on the local default SQL Server instance

        .EXAMPLE
            Get-DbaCustomError -SqlInstance localhost, sql2016

            Returns all Custom Error Message(s) for the local and sql2016 SQL Server instances
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            foreach ($customError in $server.UserDefinedMessages) {
                Add-Member -Force -InputObject $customError -MemberType NoteProperty -Name ComputerName -value $customError.Parent.ComputerName
                Add-Member -Force -InputObject $customError -MemberType NoteProperty -Name InstanceName -value $customError.Parent.ServiceName
                Add-Member -Force -InputObject $customError -MemberType NoteProperty -Name SqlInstance -value $customError.Parent.DomainInstanceName

                Select-DefaultView -InputObject $customError -Property ComputerName, InstanceName, SqlInstance, ID, Text, LanguageID, Language
            }
        }
    }
}
#ValidationTags#CodeStyle,Messaging,FlowControl,Pipeline#
function Get-DbaDatabase {
    <#
        .SYNOPSIS
            Gets SQL Database information for each database that is present on the target instance(s) of SQL Server.

        .DESCRIPTION
            The Get-DbaDatabase command gets SQL database information for each database that is present on the target instance(s) of
            SQL Server. If the name of the database is provided, the command will return only the specific database information.

         .PARAMETER SqlInstance
            The SQL Server instance to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies one or more database(s) to process. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies one or more database(s) to exclude from processing.

        .PARAMETER ExcludeAllUserDb
            If this switch is enabled, only databases which are not User databases will be processed.

            This parameter cannot be used with -ExcludeAllSystemDb.

        .PARAMETER ExcludeAllSystemDb
            If this switch is enabled, only databases which are not System databases will be processed.

            This parameter cannot be used with -ExcludeAllUserDb.

        .PARAMETER Status
            Specifies one or more database statuses to filter on. Only databases in the status(es) listed will be returned. Valid options for this parameter are 'Emergency', 'Normal', 'Offline', 'Recovering', 'Restoring', 'Standby', and 'Suspect'.

        .PARAMETER Access
            Filters databases returned by their access type. Valid options for this parameter are 'ReadOnly' and 'ReadWrite'. If omitted, no filtering is performed.

        .PARAMETER Owner
            Specifies one or more database owners. Only databases owned by the listed owner(s) will be returned.

        .PARAMETER Encrypted
            If this switch is enabled, only databases which have Transparent Data Encryption (TDE) enabled will be returned.

        .PARAMETER RecoveryModel
            Filters databases returned by their recovery model. Valid options for this parameter are 'Full', 'Simple', and 'BulkLogged'.

        .PARAMETER NoFullBackup
            If this switch is enabled, only databases without a full backup recorded by SQL Server will be returned. This will also indicate which of these databases only have CopyOnly full backups.

        .PARAMETER NoFullBackupSince
            Only databases which haven't had a full backup since the specified DateTime will be returned.

        .PARAMETER NoLogBackup
            If this switch is enabled, only databases without a log backup recorded by SQL Server will be returned. This will also indicate which of these databases only have CopyOnly log backups.

        .PARAMETER NoLogBackupSince
            Only databases which haven't had a log backup since the specified DateTime will be returned.

        .PARAMETER IncludeLastUsed
            If this switch is enabled, the last used read & write times for each database will be returned. This data is retrieved from sys.dm_db_index_usage_stats which is reset when SQL Server is restarted.

        .PARAMETER OnlyAccessible
           If this switch is enabled, only accessible databases are returned (huge speedup in SMO enumeration)

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database
            Author: Garry Bargsley (@gbargsley | http://blog.garrybargsley.com)
            Author: Klaas Vandenberghe ( @PowerDbaKlaas )
            Author: Simone Bizzotto ( @niphlod )

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaDatabase

        .EXAMPLE
            Get-DbaDatabase -SqlInstance localhost

            Returns all databases on the local default SQL Server instance.

        .EXAMPLE
            Get-DbaDatabase -SqlInstance localhost -ExcludeAllUserDb

            Returns only the system databases on the local default SQL Server instance.

        .EXAMPLE
            Get-DbaDatabase -SqlInstance localhost -ExcludeAllSystemDb

            Returns only the user databases on the local default SQL Server instance.

        .EXAMPLE
            'localhost','sql2016' | Get-DbaDatabase

            Returns databases on multiple instances piped into the function.

        .EXAMPLE
            Get-DbaDatabase -SqlInstance SQL1\SQLExpress -RecoveryModel full,Simple

            Returns only the user databases in Full or Simple recovery model from SQL Server instance SQL1\SQLExpress.

        .EXAMPLE
            Get-DbaDatabase -SqlInstance SQL1\SQLExpress -Status Normal

            Returns only the user databases with status 'normal' from SQL Server instance SQL1\SQLExpress.

        .EXAMPLE
            Get-DbaDatabase -SqlInstance SQL1\SQLExpress -IncludeLastUsed

            Returns the databases from SQL Server instance SQL1\SQLExpress and includes the last used information
            from the sys.dm_db_index_usage_stats DMV.

        .EXAMPLE
            Get-DbaDatabase -SqlInstance SQL1\SQLExpress,SQL2 -ExcludeDatabase model,master

            Returns all databases except master and model from SQL Server instances SQL1\SQLExpress and SQL2.

        .EXAMPLE
            Get-DbaDatabase -SqlInstance SQL1\SQLExpress,SQL2 -Encrypted

            Returns only databases using TDE from SQL Server instances SQL1\SQLExpress and SQL2.

        .EXAMPLE
            Get-DbaDatabase -SqlInstance SQL1\SQLExpress,SQL2 -Access ReadOnly

            Returns only read only databases from SQL Server instances SQL1\SQLExpress and SQL2.

        .EXAMPLE
            Get-DbaDatabase -SqlInstance SQL2,SQL3 -Database OneDB,OtherDB

            Returns databases 'OneDb' and 'OtherDB' from SQL Server instances SQL2 and SQL3 if databases by those names exist on those instances.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    Param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Alias("SystemDbOnly", "NoUserDb")]
        [switch]$ExcludeAllUserDb,
        [Alias("UserDbOnly", "NoSystemDb")]
        [switch]$ExcludeAllSystemDb,
        [string[]]$Owner,
        [switch]$Encrypted,
        [ValidateSet('EmergencyMode', 'Normal', 'Offline', 'Recovering', 'Restoring', 'Standby', 'Suspect')]
        [string[]]$Status = @('EmergencyMode', 'Normal', 'Offline', 'Recovering', 'Restoring', 'Standby', 'Suspect'),
        [ValidateSet('ReadOnly', 'ReadWrite')]
        [string]$Access,
        [ValidateSet('Full', 'Simple', 'BulkLogged')]
        [string[]]$RecoveryModel = @('Full', 'Simple', 'BulkLogged'),
        [switch]$NoFullBackup,
        [datetime]$NoFullBackupSince,
        [switch]$NoLogBackup,
        [datetime]$NoLogBackupSince,
        [Alias('Silent')]
        [switch]$EnableException,
        [switch]$IncludeLastUsed,
        [switch]$OnlyAccessible
    )

    begin {

        if ($ExcludeAllUserDb -and $ExcludeAllSystemDb) {
            Stop-Function -Message "You cannot specify both ExcludeAllUserDb and ExcludeAllSystemDb." -Continue -EnableException $EnableException
        }

    }
    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if (!$IncludeLastUsed) {
                $dblastused = $null
            }
            else {
                ## Get last used information from the DMV
                $querylastused = "WITH agg AS
                (
                  SELECT
                       max(last_user_seek) last_user_seek,
                       max(last_user_scan) last_user_scan,
                       max(last_user_lookup) last_user_lookup,
                       max(last_user_update) last_user_update,
                       sd.name dbname
                   FROM
                       sys.dm_db_index_usage_stats, master..sysdatabases sd
                   WHERE
                     database_id = sd.dbid AND database_id > 4
                      group by sd.name
                )
                SELECT
                   dbname,
                   last_read = MAX(last_read),
                   last_write = MAX(last_write)
                FROM
                (
                   SELECT dbname, last_user_seek, NULL FROM agg
                   UNION ALL
                   SELECT dbname, last_user_scan, NULL FROM agg
                   UNION ALL
                   SELECT dbname, last_user_lookup, NULL FROM agg
                   UNION ALL
                   SELECT dbname, NULL, last_user_update FROM agg
                ) AS x (dbname, last_read, last_write)
                GROUP BY
                   dbname
                ORDER BY 1;"
                # put a function around this to enable Pester Testing and also to ease any future changes
                function Invoke-QueryDBlastUsed {
                    $server.Query($querylastused)
                }
                $dblastused = Invoke-QueryDBlastUsed
            }

            if ($ExcludeAllUserDb) {
                $DBType = @($true)
            }
            elseif ($ExcludeAllSystemDb) {
                $DBType = @($false)
            }
            else {
                $DBType = @($false, $true)
            }

            $AccessibleFilter = switch ($OnlyAccessible) {
                $true { @($true) }
                default { @($true, $false) }
            }

            $Readonly = switch ($Access) {
                'Readonly' { @($true) }
                'ReadWrite' { @($false) }
                default { @($true, $false) }
            }
            $Encrypt = switch (Test-Bound $Encrypted) {
                $true { @($true) }
                default { @($true, $false, $null) }
            }
            function Invoke-QueryRawDatabases {
                if ($server.VersionMajor -eq 8) {
                    $server.Query("SELECT *, SUSER_NAME(sid) AS [Owner] FROM master.dbo.sysdatabases")
                }
                else {
                    $server.Query("SELECT *, SUSER_NAME(owner_sid) AS [Owner] FROM sys.databases")
                }
            }
            $backed_info = Invoke-QueryRawDatabases
            $backed_info = $backed_info | Where-Object {
                ($_.name -in $Database -or !$Database) -and
                ($_.name -notin $ExcludeDatabase -or !$ExcludeDatabase) -and
                ($_.Owner -in $Owner -or !$Owner) -and
                ($_.state -ne 6 -or !$OnlyAccessible)
            }

            $inputObject = @()
            foreach($dt in $backed_info) {
                $inputObject += $server.Databases[$dt.name]
            }
            $inputobject = $inputObject |
                Where-Object {
                ($_.Name -in $Database -or !$Database) -and
                ($_.Name -notin $ExcludeDatabase -or !$ExcludeDatabase) -and
                ($_.Owner -in $Owner -or !$Owner) -and
                $_.ReadOnly -in $Readonly -and
                $_.IsAccessible -in $AccessibleFilter -and
                $_.IsSystemObject -in $DBType -and
                ((Compare-Object @($_.Status.tostring().split(',').trim()) $Status -ExcludeDifferent -IncludeEqual).inputobject.count -ge 1 -or !$status) -and
                $_.RecoveryModel -in $RecoveryModel -and
                $_.EncryptionEnabled -in $Encrypt
            }
            if ($NoFullBackup -or $NoFullBackupSince) {
                $dabs = (Get-DbaBackupHistory -SqlInstance $server -LastFull )
                if ($null -ne $NoFullBackupSince) {
                    $dabsWithinScope = ($dabs | Where-Object End -lt $NoFullBackupSince)

                    $inputobject = $inputobject | Where-Object { $_.Name -in $dabsWithinScope.Database -and $_.Name -ne 'tempdb' }
                }
                else {
                    $inputObject = $inputObject | Where-Object { $_.Name -notin $dabs.Database -and $_.Name -ne 'tempdb' }
                }

            }
            if ($NoLogBackup -or $NoLogBackupSince) {
                $dabs = (Get-DbaBackupHistory -SqlInstance $server -LastLog )
                if ($null -ne $NoLogBackupSince) {
                    $dabsWithinScope = ($dabs | Where-Object End -lt $NoLogBackupSince)
                    $inputobject = $inputobject |
                        Where-Object { $_.Name -in $dabsWithinScope.Database -and $_.Name -ne 'tempdb' -and $_.RecoveryModel -ne 'Simple' }
                }
                else {
                    $inputobject = $inputObject |
                        Where-Object { $_.Name -notin $dabs.Database -and $_.Name -ne 'tempdb' -and $_.RecoveryModel -ne 'Simple' }
                }
            }

            $defaults = 'ComputerName', 'InstanceName', 'SqlInstance', 'Name', 'Status', 'IsAccessible', 'RecoveryModel',
            'LogReuseWaitStatus', 'Size as SizeMB', 'CompatibilityLevel as Compatibility', 'Collation', 'Owner',
            'LastBackupDate as LastFullBackup', 'LastDifferentialBackupDate as LastDiffBackup',
            'LastLogBackupDate as LastLogBackup'

            if ($NoFullBackup -or $NoFullBackupSince -or $NoLogBackup -or $NoLogBackupSince) {
                $defaults += ('Notes')
            }
            if ($IncludeLastUsed) {
                # Add Last Used to the default view
                $defaults += ('LastRead as LastIndexRead', 'LastWrite as LastIndexWrite')
            }

            try {
                foreach ($db in $inputobject) {

                    $Notes = $null
                    if ($NoFullBackup -or $NoFullBackupSince) {
                        if (@($db.EnumBackupSets()).count -eq @($db.EnumBackupSets() | Where-Object { $_.IsCopyOnly }).count -and (@($db.EnumBackupSets()).count -gt 0)) {
                            $Notes = "Only CopyOnly backups"
                        }
                    }

                    $lastusedinfo = $dblastused | Where-Object { $_.dbname -eq $db.name }
                    Add-Member -Force -InputObject $db -MemberType NoteProperty BackupStatus -value $Notes
                    Add-Member -Force -InputObject $db -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                    Add-Member -Force -InputObject $db -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                    Add-Member -Force -InputObject $db -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                    Add-Member -Force -InputObject $db -MemberType NoteProperty -Name LastRead -value $lastusedinfo.last_read
                    Add-Member -Force -InputObject $db -MemberType NoteProperty -Name LastWrite -value $lastusedinfo.last_write
                    Select-DefaultView -InputObject $db -Property $defaults
                    #try { $server.Databases.Refresh() } catch {}
                }
            }
            catch {
                Stop-Function -ErrorRecord $_ -Target $instance -Message "Failure. Collection may have been modified. If so, please use parens (Get-DbaDatabase ....) | when working with commands that modify the collection such as Remove-DbaDatabase." -Continue
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,CodeStyle#
function Get-DbaDatabaseAssembly {
    <#
        .SYNOPSIS
            Gets SQL Database Assembly information for each instance(s) of SQL Server.

        .DESCRIPTION
            The Get-DbaDatabaseAssembly command gets SQL Database Assembly information for each instance(s) of SQL Server.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function
            to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Assembly, Database
            Author: Garry Bargsley (@gbargsley), http://blog.garrybargsley.com
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaDatabaseAssembly

        .EXAMPLE
            Get-DbaDatabaseAssembly -SqlInstance localhost

            Returns all Database Assembly on the local default SQL Server instance

        .EXAMPLE
            Get-DbaDatabaseAssembly -SqlInstance localhost, sql2016

            Returns all Database Assembly for the local and sql2016 SQL Server instances
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            foreach ($database in ($server.Databases | Where-Object IsAccessible)) {
                try {
                    foreach ($assembly in $database.assemblies) {

                        Add-Member -Force -InputObject $assembly -MemberType NoteProperty -Name ComputerName -value $assembly.Parent.Parent.ComputerName
                        Add-Member -Force -InputObject $assembly -MemberType NoteProperty -Name InstanceName -value $assembly.Parent.Parent.ServiceName
                        Add-Member -Force -InputObject $assembly -MemberType NoteProperty -Name SqlInstance -value $assembly.Parent.Parent.DomainInstanceName

                        Select-DefaultView -InputObject $assembly -Property ComputerName, InstanceName, SqlInstance, ID, Name, Owner, 'AssemblySecurityLevel as SecurityLevel', CreateDate, IsSystemObject, Version
                    }
                }
                catch {
                    Stop-Function -Message "Issue pulling assembly information" -Target $assembly -ErrorRecord $_ -Continue
                }
            }
        }
    }
}
function Get-DbaDatabaseEncryption {
    <#
        .SYNOPSIS
            Returns a summary of encryption used on databases passed to it.

        .DESCRIPTION
            Shows if a database has Transparent Data Encryption (TDE), any certificates, asymmetric keys or symmetric keys with details for each.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto-populated from the server.

        .PARAMETER IncludeSystemDBs
            Switch parameter that when used will display system database information.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Encryption, Database
            Author: Stephen Bennett, https://sqlnotesfromtheunderground.wordpress.com/
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaDatabaseEncryption

        .EXAMPLE
            Get-DbaDatabaseEncryption -SqlInstance DEV01

            List all encryption found on the instance by database

        .EXAMPLE
            Get-DbaDatabaseEncryption -SqlInstance DEV01 -Database MyDB

            List all encryption found for the MyDB database.

        .EXAMPLE
            Get-DbaDatabaseEncryption -SqlInstance DEV01 -ExcludeDatabase MyDB

            List all encryption found for all databases except MyDB.

        .EXAMPLE
            Get-DbaDatabaseEncryption -SqlInstance DEV01 -IncludeSystemDBs

            List all encryption found for all databases including the system databases.
    #>
    [CmdletBinding()]
    param ([parameter(ValueFromPipeline, Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$IncludeSystemDBs,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            #For each SQL Server in collection, connect and get SMO object
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            #If IncludeSystemDBs is true, include systemdbs
            #only look at online databases (Status equal normal)
            try {
                if ($Database) {
                    $dbs = $server.Databases | Where-Object Name -In $Database
                }
                elseif ($IncludeSystemDBs) {
                    $dbs = $server.Databases | Where-Object IsAccessible
                }
                else {
                    $dbs = $server.Databases | Where-Object { $_.IsAccessible -and $_.IsSystemObject -eq 0 }
                }

                if ($ExcludeDatabase) {
                    $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
                }
            }
            catch {
                Stop-Function -Message "Unable to gather dbs for $instance" -Target $instance -Continue
            }

            foreach ($db in $dbs) {
                Write-Message -Level Verbose -Message "Processing $db"

                if ($db.EncryptionEnabled -eq $true) {
                    [PSCustomObject]@{
                        ComputerName             = $server.ComputerName
                        InstanceName             = $server.ServiceName
                        SqlInstance              = $server.DomainInstanceName
                        Database                 = $db.Name
                        Encryption               = "EncryptionEnabled (TDE)"
                        Name                     = $null
                        LastBackup               = $null
                        PrivateKeyEncryptionType = $null
                        EncryptionAlgorithm      = $null
                        KeyLength                = $null
                        Owner                    = $null
                        Object                   = $null
                        ExpirationDate           = $null
                    }

                }

                foreach ($cert in $db.Certificates) {
                    [PSCustomObject]@{
                        ComputerName             = $server.ComputerName
                        InstanceName             = $server.ServiceName
                        SqlInstance              = $server.DomainInstanceName
                        Database                 = $db.Name
                        Encryption               = "Certificate"
                        Name                     = $cert.Name
                        LastBackup               = $cert.LastBackupDate
                        PrivateKeyEncryptionType = $cert.PrivateKeyEncryptionType
                        EncryptionAlgorithm      = $null
                        KeyLength                = $null
                        Owner                    = $cert.Owner
                        Object                   = $cert
                        ExpirationDate           = $cert.ExpirationDate
                    }

                }

                foreach ($ak in $db.AsymmetricKeys) {
                    [PSCustomObject]@{
                        ComputerName             = $server.ComputerName
                        InstanceName             = $server.ServiceName
                        SqlInstance              = $server.DomainInstanceName
                        Database                 = $db.Name
                        Encryption               = "Asymmetric key"
                        Name                     = $ak.Name
                        LastBackup               = $null
                        PrivateKeyEncryptionType = $ak.PrivateKeyEncryptionType
                        EncryptionAlgorithm      = $ak.KeyEncryptionAlgorithm
                        KeyLength                = $ak.KeyLength
                        Owner                    = $ak.Owner
                        Object                   = $ak
                        ExpirationDate           = $null
                    }

                }
                foreach ($sk in $db.SymmetricKeys) {
                    [PSCustomObject]@{
                        Server                   = $server.name
                        Instance                 = $server.InstanceName
                        Database                 = $db.Name
                        Encryption               = "Symmetric key"
                        Name                     = $sk.Name
                        LastBackup               = $null
                        PrivateKeyEncryptionType = $sk.PrivateKeyEncryptionType
                        EncryptionAlgorithm      = $ak.EncryptionAlgorithm
                        KeyLength                = $sk.KeyLength
                        Owner                    = $sk.Owner
                        Object                   = $sk
                        ExpirationDate           = $null
                    }
                }
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaDatabaseFile {
    <#
    .SYNOPSIS
    Returns detailed information about database files.

    .DESCRIPTION
    Returns detailed information about database files. Does not use SMO - SMO causes enumeration and this command avoids that.

    .PARAMETER SqlInstance
    The target SQL Server instance(s)

    .PARAMETER SqlCredential
    Credentials to connect to the SQL Server instance if the calling user doesn't have permission

    .PARAMETER Database
    The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

    .PARAMETER ExcludeDatabase
    The database(s) to exclude - this list is auto-populated from the server

    .PARAMETER InputObject
    A piped collection of database objects

    .PARAMETER EnableException
    By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
    Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
    Author: Stuart Moore (@napalmgram), stuart-moore.com
    Tags: Database
    Website: https://dbatools.io
    Copyright: (C) Chrissy LeMaire, [email protected]
    License: MIT https://opensource.org/licenses/MIT

    .EXAMPLE
    Get-DbaDatabaseFile -SqlInstance sql2016

    Will return an object containing all filegroups and their contained files for every database on the sql2016 SQL Server instance

    .EXAMPLE
    Get-DbaDatabaseFile -SqlInstance sql2016 -Database Impromptu

    Will return an object containing all filegroups and their contained files for the Impromptu Database on the sql2016 SQL Server instance

    .EXAMPLE
    Get-DbaDatabaseFile -SqlInstance sql2016 -Database Impromptu, Trading

    Will return an object containing all filegroups and their contained files for the Impromptu and Trading databases on the sql2016 SQL Server instance

    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    param (
        [parameter(ParameterSetName = "Pipe", Mandatory, ValueFromPipeline)]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [object[]]$InputObject,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {

        foreach ($instance in $sqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $sql = "select
            fg.name as FileGroupName,
            df.file_id as 'ID',
            df.Type,
            df.type_desc as TypeDescription,
            df.name as LogicalName,
            mf.physical_name as PhysicalName,
            df.state_desc as State,
            df.max_size as MaxSize,
            case mf.is_percent_growth when 1 then df.growth else df.Growth*8 end as Growth,
            fileproperty(df.name, 'spaceused') as UsedSpace,
            df.size as Size,
            vfs.size_on_disk_bytes as size_on_disk_bytes,
            case df.state_desc when 'OFFLINE' then 'True' else 'False' End as IsOffline,
            case mf.is_read_only when 1 then 'True' when 0 then 'False' End as IsReadOnly,
            case mf.is_media_read_only when 1 then 'True' when 0 then 'False' End as IsReadOnlyMedia,
            case mf.is_sparse when 1 then 'True' when 0 then 'False' End as IsSparse,
            case mf.is_percent_growth when 1 then 'Percent' when 0 then 'kb' End as GrowthType,
            case mf.is_read_only when 1 then 'True' when 0 then 'False' End as IsReadOnly,
            vfs.num_of_writes as NumberOfDiskWrites,
            vfs.num_of_reads as NumberOfDiskReads,
            vfs.num_of_bytes_read as BytesReadFromDisk,
            vfs.num_of_bytes_written as BytesWrittenToDisk,
            fg.data_space_id as FileGroupDataSpaceId,
            fg.Type as FileGroupType,
            fg.type_desc as FileGroupTypeDescription,
            case fg.is_default When 1 then 'True' when 0 then 'False' end as FileGroupDefault,
            fg.is_read_only as FileGroupReadOnly"

            $sqlfrom = "from sys.database_files df
            left outer join  sys.filegroups fg on df.data_space_id=fg.data_space_id
            inner join sys.dm_io_virtual_file_stats(db_id(),NULL) vfs on df.file_id=vfs.file_id
            inner join sys.master_files mf on df.file_id = mf.file_id
            and mf.database_id = db_id()"

            $sql2008 = ",vs.available_bytes as 'VolumeFreeSpace'"
            $sql2008from = "cross apply sys.dm_os_volume_stats(db_id(),df.file_id) vs"

            $sql2000 = "select
            fg.groupname as FileGroupName,
            df.fileid as ID,
            CONVERT(INT,df.status & 0x40) / 64 as Type,
            case CONVERT(INT,df.status & 0x40) / 64 when 1 then 'LOG' else 'ROWS' end as TypeDescription,
            df.name as LogicalName,
            df.filename as PhysicalName,
            'Existing' as State,
            df.maxsize as MaxSize,
            case CONVERT(INT,df.status & 0x100000) / 1048576 when 1 then df.growth when 0 then df.growth*8 End as Growth,
            fileproperty(df.name, 'spaceused') as UsedSpace,
            df.size as Size,
            case CONVERT(INT,df.status & 0x20000000) / 536870912 when 1 then 'True' else 'False' End as IsOffline,
            case CONVERT(INT,df.status & 0x10) / 16 when 1 then 'True' when 0 then 'False' End as IsReadOnly,
            case CONVERT(INT,df.status & 0x1000) / 4096 when 1 then 'True' when 0 then 'False' End as IsReadOnlyMedia,
            case CONVERT(INT,df.status & 0x10000000) / 268435456 when 1 then 'True' when 0 then 'False' End as IsSparse,
            case CONVERT(INT,df.status & 0x100000) / 1048576 when 1 then 'Percent' when 0 then 'kb' End as GrowthType,
            case CONVERT(INT,df.status & 0x1000) / 4096 when 1 then 'True' when 0 then 'False' End as IsReadOnly,
            fg.groupid as FileGroupDataSpaceId,
            NULL as FileGroupType,
            NULL AS FileGroupTypeDescription,
            CAST(fg.Status & 0x10 as BIT) as FileGroupDefault,
            CAST(fg.Status & 0x8 as BIT) as FileGroupReadOnly
            from sysfiles df
            left outer join  sysfilegroups fg on df.groupid=fg.groupid"

            if ($Database) {
                $InputObject = $server.Databases | Where-Object Name -in $database
            }
            else {
                $InputObject = $server.Databases
            }

            if ($ExcludeDatabase) {
                $InputObject = $InputObject | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $InputObject) {
                if (!$db.IsAccessible) {
                    Write-Message -Level Warning -Message "Database $db is not accessible. Skipping"
                    continue
                }
                Write-Message -Level Verbose -Message "Querying database $db"

                $version = Test-DbaDatabaseCompatibility -SqlInstance $server -Database $db.Name | Select-Object DatabaseCompatibility
                $version = + ($version.DatabaseCompatibility.ToString().replace("Version", "")) / 10

                if ($version -ge 11) {
                    $query = ($sql, $sql2008, $sqlfrom, $sql2008from) -Join "`n"
                }
                elseif ($version -ge 9) {
                    $query = ($sql, $sqlfrom) -Join "`n"
                }
                else {
                    $query = $sql2000
                }

                Write-Message -Level Debug -Message "SQL Statement: $query"

                $results = $server.Query($query, $db.Name)

                foreach ($result in $results) {
                    $size = [dbasize]($result.Size * 8192)
                    $usedspace = [dbasize]($result.UsedSpace * 8192)
                    $maxsize = $result.MaxSize
                    # calculation is done here because for snapshots or sparse files size is not the "virtual" size
                    # (master_files.Size) but the currently allocated one (dm_io_virtual_file_stats.size_on_disk_bytes)
                    $AvailableSpace = $size - $usedspace
                    if ($result.size_on_disk_bytes) {
                        $size = [dbasize]($result.size_on_disk_bytes)
                    }
                    if ($maxsize -gt -1) {
                        $maxsize = [dbasize]($result.MaxSize * 8192)
                    }
                    else {
                        $maxsize = [dbasize]($result.MaxSize)
                    }

                    if ($result.VolumeFreeSpace) {
                        $VolumeFreeSpace = [dbasize]$result.VolumeFreeSpace
                    }
                    else {
                        # to get drive free space for each drive that a database has files on
                        # when database compatibility lower than 110. Lets do this with query2
                        $query2 = @'
-- to get drive free space for each drive that a database has files on
DECLARE @FixedDrives TABLE(Drive CHAR(1), MB_Free BIGINT);
INSERT @FixedDrives EXEC sys.xp_fixeddrives;

SELECT DISTINCT fd.MB_Free, LEFT(df.physical_name, 1) AS [Drive]
FROM @FixedDrives AS fd
INNER JOIN sys.database_files AS df
ON fd.Drive = LEFT(df.physical_name, 1);
'@
                        # if the server has one drive xp_fixeddrives returns one row, but we still need $disks to be an array.
                        $disks = @($server.Query($query2, $db.Name))
                        $MbFreeColName = $disks[0].psobject.Properties.Name
                        # get the free MB value for the drive in question
                        $free = $disks | Where-Object { $_.drive -eq $result.PhysicalName.Substring(0, 1) } | Select-Object $MbFreeColName
                        
                        $VolumeFreeSpace = [dbasize](($free.MB_Free) * 1024 * 1024)
                    }
                    if ($result.GrowthType -eq "Percent") {
                        $nextgrowtheventadd = [dbasize]($result.size * ($result.Growth * 0.01) * 1024)
                    }
                    else {
                        $nextgrowtheventadd = [dbasize]($result.Growth * 8 * 1024)
                    }
                    if ( ($nextgrowtheventadd.Byte -gt ($MaxSize.Byte - $size.Byte)) -and $maxsize -gt 0 ) { [dbasize]$nextgrowtheventadd = 0 }

                    [PSCustomObject]@{
                        ComputerName             = $server.ComputerName
                        InstanceName             = $server.ServiceName
                        SqlInstance              = $server.DomainInstanceName
                        Database                 = $db.name
                        FileGroupName            = $result.FileGroupName
                        ID                       = $result.ID
                        Type                     = $result.Type
                        TypeDescription          = $result.TypeDescription
                        LogicalName              = $result.LogicalName.Trim()
                        PhysicalName             = $result.PhysicalName.Trim()
                        State                    = $result.State
                        MaxSize                  = $maxsize
                        Growth                   = $result.Growth
                        GrowthType               = $result.GrowthType
                        NextGrowthEventSize      = $nextgrowtheventadd
                        Size                     = $size
                        UsedSpace                = $usedspace
                        AvailableSpace           = $AvailableSpace
                        IsOffline                = $result.IsOffline
                        IsReadOnly               = $result.IsReadOnly
                        IsReadOnlyMedia          = $result.IsReadOnlyMedia
                        IsSparse                 = $result.IsSparse
                        NumberOfDiskWrites       = $result.NumberOfDiskWrites
                        NumberOfDiskReads        = $result.NumberOfDiskReads
                        ReadFromDisk             = [dbasize]$result.BytesReadFromDisk
                        WrittenToDisk            = [dbasize]$result.BytesWrittenToDisk
                        VolumeFreeSpace          = $VolumeFreeSpace
                        FileGroupDataSpaceId     = $result.FileGroupDataSpaceId
                        FileGroupType            = $result.FileGroupType
                        FileGroupTypeDescription = $result.FileGroupTypeDescription
                        FileGroupDefault         = $result.FileGroupDefault
                        FileGroupReadOnly        = $result.FileGroupReadOnly
                    }
                }
            }
        }
    }
}
function Get-DbaDatabaseMasterKey {
    <#
.SYNOPSIS
Gets specified database master key

.DESCRIPTION
Gets specified database master key

.PARAMETER SqlInstance
The target SQL Server instance

.PARAMETER SqlCredential
Allows you to login to SQL Server using alternative credentials

.PARAMETER Database
Get master key from specific database

.PARAMETER ExcludeDatabase
The database(s) to exclude - this list is auto-populated from the server

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Certificate, Database

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
Get-DbaDatabaseMasterKey -SqlInstance sql2016

Gets all master database keys

.EXAMPLE
Get-DbaDatabaseMasterKey -SqlInstance Server1 -Database db1

Gets the master key for the db1 database

#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $databases = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                $databases = $databases | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $databases) {
                if (!$db.IsAccessible) {
                    Write-Message -Level Warning -Message "Database $db is not accessible. Skipping."
                    continue
                }

                $masterkey = $db.MasterKey

                if (!$masterkey) {
                    Write-Message -Message "No master key exists in the $db database on $instance" -Target $db -Level Verbose
                    continue
                }

                Add-Member -Force -InputObject $masterkey -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                Add-Member -Force -InputObject $masterkey -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                Add-Member -Force -InputObject $masterkey -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                Add-Member -Force -InputObject $masterkey -MemberType NoteProperty -Name Database -value $db.Name

                Select-DefaultView -InputObject $masterkey -Property ComputerName, InstanceName, SqlInstance, Database, CreateDate, DateLastModified, IsEncryptedByServer
            }
        }
    }
}
function Get-DbaDatabasePartitionFunction {
    <#
.SYNOPSIS
Gets database Partition Functions

.DESCRIPTION
Gets database Partition Functions

.PARAMETER SqlInstance
The target SQL Server instance(s)

.PARAMETER SqlCredential
Allows you to login to SQL Server using alternative credentials

.PARAMETER Database
To get users from specific database(s)

.PARAMETER ExcludeDatabase
The database(s) to exclude - this list is auto populated from the server

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Database
Author: Klaas Vandenberghe ( @PowerDbaKlaas )

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
Get-DbaDatabasePartitionFunction -SqlInstance sql2016

Gets all database Partition Functions

.EXAMPLE
Get-DbaDatabasePartitionFunction -SqlInstance Server1 -Database db1

Gets the Partition Functions for the db1 database

.EXAMPLE
Get-DbaDatabasePartitionFunction -SqlInstance Server1 -ExcludeDatabase db1

Gets the Partition Functions for all databases except db1

.EXAMPLE
'Sql1','Sql2/sqlexpress' | Get-DbaDatabasePartitionFunction

Gets the Partition Functions for the databases on Sql1 and Sql2/sqlexpress

#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $databases = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                $databases = $databases | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $databases) {
                if (!$db.IsAccessible) {
                    Write-Message -Level Warning -Message "Database $db is not accessible. Skipping."
                    continue
                }

                $partitionfunctions = $db.partitionfunctions

                if (!$partitionfunctions) {
                    Write-Message -Message "No Partition Functions exist in the $db database on $instance" -Target $db -Level Verbose
                    continue
                }

                $partitionfunctions | foreach {

                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name Database -value $db.Name

                    Select-DefaultView -InputObject $_ -Property ComputerName, InstanceName, SqlInstance, Database, CreateDate, Name, NumberOfPartitions
                }
            }
        }
    }
}
function Get-DbaDatabasePartitionScheme {
    <#
.SYNOPSIS
Gets database Partition Schemes

.DESCRIPTION
Gets database Partition Schemes

.PARAMETER SqlInstance
The target SQL Server instance(s)

.PARAMETER SqlCredential
Allows you to login to SQL Server using alternative credentials

.PARAMETER Database
To get users from specific database(s)

.PARAMETER ExcludeDatabase
The database(s) to exclude - this list is auto populated from the server

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Database
Author: Klaas Vandenberghe ( @PowerDbaKlaas )

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
Get-DbaDatabasePartitionScheme -SqlInstance sql2016

Gets all database Partition Schemes

.EXAMPLE
Get-DbaDatabasePartitionScheme -SqlInstance Server1 -Database db1

Gets the Partition Schemes for the db1 database

.EXAMPLE
Get-DbaDatabasePartitionScheme -SqlInstance Server1 -ExcludeDatabase db1

Gets the Partition Schemes for all databases except db1

.EXAMPLE
'Sql1','Sql2/sqlexpress' | Get-DbaDatabasePartitionScheme

Gets the Partition Schemes for the databases on Sql1 and Sql2/sqlexpress

#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $databases = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                $databases = $databases | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $databases) {
                if (!$db.IsAccessible) {
                    Write-Message -Level Warning -Message "Database $db is not accessible. Skipping."
                    continue
                }

                $PartitionSchemes = $db.PartitionSchemes

                if (!$PartitionSchemes) {
                    Write-Message -Message "No Partition Schemes exist in the $db database on $instance" -Target $db -Level Verbose
                    continue
                }

                $PartitionSchemes | foreach {

                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name Database -value $db.Name

                    Select-DefaultView -InputObject $_ -Property ComputerName, InstanceName, SqlInstance, Database, Name, PartitionFunction
                }
            }
        }
    }
}
function Get-DbaDatabaseSpace {
    <#
        .SYNOPSIS
            Returns database file space information for database files on a SQL instance.

        .DESCRIPTION
            This function returns database file space information for a SQL Instance or group of SQL Instances. Information is based on a query against sys.database_files and the FILEPROPERTY function to query and return information.

            File free space script borrowed and modified from Glenn Berry's DMV scripts (http://www.sqlskills.com/blogs/glenn/category/dmv-queries/)

        .PARAMETER SqlInstance
            Specifies the SQL Server instance(s) to scan.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies the database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies the database(s) to exclude from processing. Options for this list are auto-populated from the server.

        .PARAMETER IncludeSystemDBs
            If this switch is enabled, system databases will be processed. By default, only user databases are processed.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database, Space, Storage
            Author: Michael Fal (@Mike_Fal), http://mikefal.net
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaDatabaseSpace

        .EXAMPLE
            Get-DbaDatabaseSpace -SqlInstance localhost

            Returns all user database files and free space information for the localhost.

        .EXAMPLE
            Get-DbaDatabaseSpace -SqlInstance localhost | Where-Object {$_.PercentUsed -gt 80}

            Returns all user database files and free space information for the local host. Filters the output object by any files that have a percent used of greater than 80%.

        .EXAMPLE
            'localhost','localhost\namedinstance' | Get-DbaDatabaseSpace

            Returns all user database files and free space information for the localhost and localhost\namedinstance SQL Server instances. Processes data via the pipeline.

        .EXAMPLE
            Get-DbaDatabaseSpace -SqlInstance localhost -Database db1, db2

            Returns database files and free space information for the db1 and db2 on localhost.
    #>
    [CmdletBinding()]
    param ([parameter(ValueFromPipeline, Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [System.Management.Automation.PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$IncludeSystemDBs,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Write-Message -Level System -Message "Bound parameters: $($PSBoundParameters.Keys -join ", ")."

        $sql = "SELECT SERVERPROPERTY('MachineName') AS ComputerName,
                                   ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
                                   SERVERPROPERTY('ServerName') AS SqlInstance,
                    DB_NAME() as DBName
                    ,f.name AS [FileName]
                    ,fg.name AS [Filegroup]
                    ,f.physical_name AS [PhysicalName]
                    ,f.type_desc AS [FileType]
                    ,CAST(CAST(FILEPROPERTY(f.name, 'SpaceUsed') AS int)/128.0 AS FLOAT) as [UsedSpaceMB]
                    ,CAST(f.size/128.0 - CAST(FILEPROPERTY(f.name, 'SpaceUsed') AS int)/128.0 AS FLOAT) AS [FreeSpaceMB]
                    ,CAST((f.size/128.0) AS FLOAT) AS [FileSizeMB]
                    ,CAST((FILEPROPERTY(f.name, 'SpaceUsed')/(f.size/1.0)) * 100 as FLOAT) as [PercentUsed]
                    ,CAST((f.growth/128.0) AS FLOAT) AS [GrowthMB]
                    ,CASE is_percent_growth WHEN 1 THEN 'pct' WHEN 0 THEN 'MB' ELSE 'Unknown' END AS [GrowthType]
                    ,CASE f.max_size WHEN -1 THEN 2147483648. ELSE CAST((f.max_size/128.0) AS FLOAT) END AS [MaxSizeMB]
                    ,CAST((f.size/128.0) AS FLOAT) - CAST(CAST(FILEPROPERTY(f.name, 'SpaceUsed') AS int)/128.0 AS FLOAT) AS [SpaceBeforeAutoGrow]
                    ,CASE f.max_size	WHEN (-1)
                                        THEN CAST(((2147483648.) - CAST(FILEPROPERTY(f.name, 'SpaceUsed') AS int))/128.0 AS FLOAT)
                                        ELSE CAST((f.max_size - CAST(FILEPROPERTY(f.name, 'SpaceUsed') AS int))/128.0 AS FLOAT)
                                        END AS [SpaceBeforeMax]
                    ,CASE f.growth	WHEN 0 THEN 0.00
                                    ELSE	CASE f.is_percent_growth	WHEN 0
                                                    THEN	CASE f.max_size
                                                            WHEN (-1)
                                                            THEN CAST(((((2147483648.)-f.Size)/f.Growth)*f.Growth)/128.0 AS FLOAT)
                                                            ELSE CAST((((f.max_size-f.Size)/f.Growth)*f.Growth)/128.0 AS FLOAT)
                                                            END
                                                    WHEN 1
                                                    THEN	CASE f.max_size
                                                            WHEN (-1)
                                                            THEN CAST(CONVERT([int],f.Size*power((1)+CONVERT([float],f.Growth)/(100),CONVERT([int],log10(CONVERT([float],(2147483648.))/CONVERT([float],f.Size))/log10((1)+CONVERT([float],f.Growth)/(100)))))/128.0 AS FLOAT)
                                                            ELSE CAST(CONVERT([int],f.Size*power((1)+CONVERT([float],f.Growth)/(100),CONVERT([int],log10(CONVERT([float],f.Max_Size)/CONVERT([float],f.Size))/log10((1)+CONVERT([float],f.Growth)/(100)))))/128.0 AS FLOAT)
                                                            END
                                                    ELSE (0)
                                                    END
                                    END AS [PossibleAutoGrowthMB]
                    , CASE f.max_size	WHEN -1 THEN 0
                                        ELSE CASE f.growth
                                                WHEN 0 THEN (f.max_size - f.size)/128
                                                ELSE	CASE f.is_percent_growth
                                                        WHEN 0
                                                        THEN CAST((f.max_size - f.size - (	CONVERT(FLOAT,FLOOR((f.max_size-f.Size)/f.Growth)*f.Growth)))/128.0 AS FLOAT)
                                                        ELSE CAST((f.max_size - f.size - (	CONVERT([int],f.Size*power((1)+CONVERT([float],f.Growth)/(100),CONVERT([int],log10(CONVERT([float],f.Max_Size)/CONVERT([float],f.Size))/log10((1)+CONVERT([float],f.Growth)/(100)))))))/128.0 AS FLOAT)
                                                        END
                                                END
                                    END AS [UnusableSpaceMB]

                FROM sys.database_files AS f WITH (NOLOCK)
                LEFT OUTER JOIN sys.filegroups AS fg WITH (NOLOCK)
                ON f.data_space_id = fg.data_space_id"
    }

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level VeryVerbose -Message "Connecting to $instance." -Target $instance
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failed to process Instance $Instance." -ErrorRecord $_ -Target $instance -Continue
            }

            if ($server.VersionMajor -lt 9) {
                Write-Message -Level Warning -Message "SQL Server 2000 not supported. $server skipped."
                continue
            }

            #If IncludeSystemDBs is true, include systemdbs
            #look at all databases, online/offline/accessible/inaccessible and tell user if a db can't be queried.
            try {
                if (Test-Bound "Database") {
                    $dbs = $server.Databases | Where-Object Name -In $Database
                }
                elseif ($IncludeSystemDBs) {
                    $dbs = $server.Databases | Where-Object IsAccessible
                }
                else {
                    $dbs = $server.Databases | Where-Object { $_.IsAccessible -and $_.IsSystemObject -eq 0 }
                }

                if (Test-Bound "ExcludeDatabase") {
                    $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
                }
            }
            catch {
                Stop-Function -Message "Unable to gather databases for $instance." -ErrorRecord $_ -Continue
            }

            foreach ($db in $dbs) {
                try {
                    Write-Message -Level Verbose -Message "Querying $instance - $db."
                    If ($db.status -ne 'Normal' -or $db.IsAccessible -eq $false) {
                        Write-Message -Level Warning -Message "$db is not accessible." -Target $db
                        continue
                    }
                    #Execute query against individual database and add to output
                    foreach ($row in ($db.ExecuteWithResults($sql)).Tables.Rows) {
                        if ($row.UsedSpaceMB -is [System.DBNull]) {
                            $UsedMB = 0
                        }
                        else {
                            $UsedMB = [Math]::Round($row.UsedSpaceMB)
                        }
                        if ($row.FreeSpaceMB -is [System.DBNull]) {
                            $FreeMB = 0
                        }
                        else {
                            $FreeMB = [Math]::Round($row.FreeSpaceMB)
                        }
                        if ($row.PercentUsed -is [System.DBNull]) {
                            $PercentUsed = 0
                        }
                        else {
                            $PercentUsed = [Math]::Round($row.PercentUsed)
                        }
                        if ($row.SpaceBeforeMax -is [System.DBNull]) {
                            $SpaceUntilMax = 0
                        }
                        else {
                            $SpaceUntilMax = [Math]::Round($row.SpaceBeforeMax)
                        }
                        if ($row.UnusableSpaceMB -is [System.DBNull]) {
                            $UnusableSpace = 0
                        }
                        else {
                            $UnusableSpace = [Math]::Round($row.UnusableSpaceMB)
                        }

                        [pscustomobject]@{
                            ComputerName         = $server.ComputerName
                            InstanceName         = $server.ServiceName
                            SqlInstance          = $server.DomainInstanceName
                            Database             = $row.DBName
                            FileName             = $row.FileName
                            FileGroup            = $row.FileGroup
                            PhysicalName         = $row.PhysicalName
                            FileType             = $row.FileType
                            UsedSpaceMB          = $UsedMB
                            FreeSpaceMB          = $FreeMB
                            FileSizeMB           = $row.FileSizeMB
                            PercentUsed          = $PercentUsed
                            AutoGrowth           = $row.GrowthMB
                            AutoGrowType         = $row.GrowthType
                            SpaceUntilMaxSizeMB  = $SpaceUntilMax
                            AutoGrowthPossibleMB = $row.PossibleAutoGrowthMB
                            UnusableSpaceMB      = $UnusableSpace
                        }
                    }
                }
                catch {
                    Stop-Function -Message "Unable to query $instance - $db." -Target $db -ErrorRecord $_ -Continue
                }
            }
        }
    }

    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Get-DbaDatabaseFreeSpace
    }
}

#ValidationTags#Messaging,FlowControl,Pipeline#
function Get-DbaDatabaseState {
    <#
.SYNOPSIS
Gets various options for databases, hereby called "states"

.DESCRIPTION
Gets some common "states" on databases:
 - "RW" options : READ_ONLY or READ_WRITE
 - "Status" options : ONLINE, OFFLINE, EMERGENCY, RESTORING
 - "Access" options : SINGLE_USER, RESTRICTED_USER, MULTI_USER

Returns an object with SqlInstance, Database, RW, Status, Access

.PARAMETER SqlInstance
The SQL Server that you're connecting to

.PARAMETER SqlCredential
Credential object used to connect to the SQL Server as a different user

.PARAMETER Database
The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

.PARAMETER ExcludeDatabase
The database(s) to exclude - this list is auto-populated from the server

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Database
Author: niphlod

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Get-DbaDatabaseState

.EXAMPLE
Get-DbaDatabaseState -SqlInstance sqlserver2014a

Gets options for all databases of the sqlserver2014a instance

.EXAMPLE
Get-DbaDatabaseState -SqlInstance sqlserver2014a -Database HR, Accounting

Gets options for both HR and Accounting database of the sqlserver2014a instance

.EXAMPLE
Get-DbaDatabaseState -SqlInstance sqlserver2014a -Exclude HR

Gets options for all databases of the sqlserver2014a instance except HR

.EXAMPLE
'sqlserver2014a', 'sqlserver2014b' | Get-DbaDatabaseState

Gets options for all databases of sqlserver2014a and sqlserver2014b instances

#>
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseLiteralInitializerForHashtable", "")]
    [CmdletBinding()]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {

        $DbStatesQuery = @'
SELECT
Name   = name,
Access = user_access_desc,
Status = state_desc,
RW     = CASE WHEN is_read_only = 0 THEN 'READ_WRITE' ELSE 'READ_ONLY' END
FROM sys.databases
'@

    }
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            $dbStates = $server.Query($DbStatesQuery)
            $dbs = $dbStates | Where-Object { @('master', 'model', 'msdb', 'tempdb', 'distribution') -notcontains $_.Name }
            if ($Database) {
                $dbs = $dbs | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }
            # "normal" hashtable doesn't account for case sensitivity
            $dbStatesHash = New-Object -TypeName System.Collections.Hashtable
            foreach ($db in $dbStates) {
                $dbStatesHash.Add($db.Name, [pscustomobject]@{
                        Access = $db.Access
                        Status = $db.Status
                        RW     = $db.RW
                    })
            }
            foreach ($db in $dbs) {
                $db_status = $dbStatesHash[$db.Name]
                [PSCustomObject]@{
                    SqlInstance  = $server.Name
                    InstanceName = $server.ServiceName
                    ComputerName = $server.ComputerName
                    DatabaseName = $db.Name
                    RW           = $db_status.RW
                    Status       = $db_status.Status
                    Access       = $db_status.Access
                    Database     = $server.Databases[$db.Name]
                } | Select-DefaultView -ExcludeProperty Database
            }
        }
    }
}

function Get-DbaDatabaseUdf {
    <#
.SYNOPSIS
Gets database User Defined Functions

.DESCRIPTION
Gets database User Defined Functions

.PARAMETER SqlInstance
The target SQL Server instance(s)

.PARAMETER SqlCredential
Allows you to login to SQL Server using alternative credentials

.PARAMETER Database
To get User Defined Functions from specific database(s)

.PARAMETER ExcludeDatabase
The database(s) to exclude - this list is auto populated from the server

.PARAMETER ExcludeSystemUdf
This switch removes all system objects from the UDF collection

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Security, Database
Author: Klaas Vandenberghe ( @PowerDbaKlaas )

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
Get-DbaDatabaseUdf -SqlInstance sql2016

Gets all database User Defined Functions

.EXAMPLE
Get-DbaDatabaseUdf -SqlInstance Server1 -Database db1

Gets the User Defined Functions for the db1 database

.EXAMPLE
Get-DbaDatabaseUdf -SqlInstance Server1 -ExcludeDatabase db1

Gets the User Defined Functions for all databases except db1

.EXAMPLE
Get-DbaDatabaseUdf -SqlInstance Server1 -ExcludeSystemUdf

Gets the User Defined Functions for all databases that are not system objects (there can be 100+ system User Defined Functions in each DB)

.EXAMPLE
'Sql1','Sql2/sqlexpress' | Get-DbaDatabaseUdf

Gets the User Defined Functions for the databases on Sql1 and Sql2/sqlexpress

#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$ExcludeSystemUdf,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $databases = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                $databases = $databases | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $databases) {

                $UserDefinedFunctions = $db.UserDefinedFunctions

                if (!$UserDefinedFunctions) {
                    Write-Message -Message "No User Defined Functions exist in the $db database on $instance" -Target $db -Level Verbose
                    continue
                }
                if (Test-Bound -ParameterName ExcludeSystemUdf) {
                    $UserDefinedFunctions = $UserDefinedFunctions | Where-Object { $_.IsSystemObject -eq $false }
                }

                $UserDefinedFunctions | foreach {

                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name Database -value $db.Name

                    Select-DefaultView -InputObject $_ -Property ComputerName, InstanceName, SqlInstance, Database, Schema, CreateDate, DateLastModified, Name, DataType
                }
            }
        }
    }
}
function Get-DbaDatabaseUser {
    <#
.SYNOPSIS
Gets database users

.DESCRIPTION
Gets database users

.PARAMETER SqlInstance
The target SQL Server instance(s)

.PARAMETER SqlCredential
Allows you to login to SQL Server using alternative credentials

.PARAMETER Database
To get users from specific database(s)

.PARAMETER ExcludeDatabase
The database(s) to exclude - this list is auto populated from the server

.PARAMETER ExcludeSystemUser
This switch removes all system objects from the user collection

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Security, Database
Author: Klaas Vandenberghe ( @PowerDbaKlaas )

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
Get-DbaDatabaseUser -SqlInstance sql2016

Gets all database users

.EXAMPLE
Get-DbaDatabaseUser -SqlInstance Server1 -Database db1

Gets the users for the db1 database

.EXAMPLE
Get-DbaDatabaseUser -SqlInstance Server1 -ExcludeDatabase db1

Gets the users for all databases except db1

.EXAMPLE
Get-DbaDatabaseUser -SqlInstance Server1 -ExcludeSystemUser

Gets the users for all databases that are not system objects, like 'dbo', 'guest' or 'INFORMATION_SCHEMA'

.EXAMPLE
'Sql1','Sql2/sqlexpress' | Get-DbaDatabaseUser

Gets the users for the databases on Sql1 and Sql2/sqlexpress

#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$ExcludeSystemUser,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $databases = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                $databases = $databases | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $databases) {

                $users = $db.users

                if (!$users) {
                    Write-Message -Message "No users exist in the $db database on $instance" -Target $db -Level Verbose
                    continue
                }
                if (Test-Bound -ParameterName ExcludeSystemUser) {
                    $users = $users | Where-Object { $_.IsSystemObject -eq $false }
                }

                $users | foreach {

                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name Database -value $db.Name

                    Select-DefaultView -InputObject $_ -Property ComputerName, InstanceName, SqlInstance, Database, CreateDate, DateLastModified, Name, Login, LoginType, AuthenticationType, State, HasDbAccess, DefaultSchema
                }
            }
        }
    }
}
function Get-DbaDatabaseView {
    <#
        .SYNOPSIS
            Gets database views for each SqlInstance.

        .DESCRIPTION
            Gets database views for each SqlInstance.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            To get views from specific database(s) - this list is auto populated from the server.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto populated from the server.

        .PARAMETER ExcludeSystemView
            This switch removes all system objects from the view collection.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Security, Database
            Author: Klaas Vandenberghe ( @PowerDbaKlaas )

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            Get-DbaDatabaseView -SqlInstance sql2016

            Gets all database views

        .EXAMPLE
            Get-DbaDatabaseView -SqlInstance Server1 -Database db1

            Gets the views for the db1 database

        .EXAMPLE
            Get-DbaDatabaseView -SqlInstance Server1 -ExcludeDatabase db1

            Gets the views for all databases except db1

        .EXAMPLE
            Get-DbaDatabaseView -SqlInstance Server1 -ExcludeSystemView

            Gets the views for all databases that are not system objects (there can be 400+ system views in each DB)

        .EXAMPLE
            'Sql1','Sql2/sqlexpress' | Get-DbaDatabaseView

            Gets the views for the databases on Sql1 and Sql2/sqlexpress

#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$ExcludeSystemView,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $databases = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                $databases = $databases | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $databases) {
                $views = $db.views

                if (!$views) {
                    Write-Message -Message "No views exist in the $db database on $instance" -Target $db -Level Verbose
                    continue
                }
                if (Test-Bound -ParameterName ExcludeSystemView) {
                    $views = $views | Where-Object { $_.IsSystemObject -eq $false }
                }

                $views | Foreach-Object {

                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name Database -value $db.Name

                    Select-DefaultView -InputObject $_ -Property ComputerName, InstanceName, SqlInstance, Database, Schema, CreateDate, DateLastModified, Name
                }
            }
        }
    }
}
function Get-DbaDbCertificate {
    <#
.SYNOPSIS
Gets database certificates

.DESCRIPTION
Gets database certificates

.PARAMETER SqlInstance
The target SQL Server instance

.PARAMETER SqlCredential
Allows you to login to SQL Server using alternative credentials

.PARAMETER Database
Get certificate from specific database

.PARAMETER ExcludeDatabase
Database(s) to ignore when retrieving certificates.

.PARAMETER Certificate
Get specific certificate

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Certificate
Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
Get-DbaDbCertificate -SqlInstance sql2016

Gets all certificates

.EXAMPLE
Get-DbaDbCertificate -SqlInstance Server1 -Database db1

Gets the certificate for the db1 database

.EXAMPLE
Get-DbaDbCertificate -SqlInstance Server1 -Database db1 -Certificate cert1

Gets the cert1 certificate within the db1 database

#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [object[]]$Certificate,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Get-DbaDatabaseCertificate
    }
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $databases = Get-DbaDatabase -SqlInstance $server | Where-Object IsAccessible

            if ($Database) {
                $databases = $databases | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $databases) {
                if (!$db.IsAccessible) {
                    Write-Message -Level Warning -Message "$db is not accessible, skipping"
                    continue
                }
                $dbName = $db.Name
                $currentdb = $server.Databases[$dbName]

                if ($null -eq $currentdb) {
                    Write-Message -Message "Database '$db' does not exist on $instance" -Target $currentdb -Level Verbose
                    continue
                }

                if ($null -eq $currentdb.Certificates) {
                    Write-Message -Message "No certificate exists in the $db database on $instance" -Target $currentdb -Level Verbose
                    continue
                }

                $certs = $currentdb.Certificates
                if ($Certificate) {
                    $certs = $certs | Where-Object Name -in $Certificate
                }

                foreach ($cert in $certs) {

                    Add-Member -Force -InputObject $cert -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                    Add-Member -Force -InputObject $cert -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                    Add-Member -Force -InputObject $cert -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                    Add-Member -Force -InputObject $cert -MemberType NoteProperty -Name Database -value $currentdb.Name

                    Select-DefaultView -InputObject $cert -Property ComputerName, InstanceName, SqlInstance, Database, Name, Subject, StartDate, ActiveForServiceBrokerDialog, ExpirationDate, Issuer, LastBackupDate, Owner, PrivateKeyEncryptionType, Serial
                }
            }
        }
    }
}
function Get-DbaDbCheckConstraint {
    <#
        .SYNOPSIS
            Gets database Check constraints.

        .DESCRIPTION
            Gets database Checks constraints.

        .PARAMETER SqlInstance
            The target SQL Server instance(s)

        .PARAMETER SqlCredential
            Allows you to login to SQL Server using alternative credentials

        .PARAMETER Database
            To get Checks from specific database(s)

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto populated from the server

        .PARAMETER ExcludeSystemTable
            This switch removes all system objects from the table collection

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database
            Author: Cláudio Silva ( @ClaudioESSilva | https://claudioessilva.eu)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            Get-DbaDbCheckConstraint -SqlInstance sql2016

            Gets all database check constraints.

        .EXAMPLE
            Get-DbaDbCheckConstraint -SqlInstance Server1 -Database db1

            Gets the check constraints for the db1 database.

        .EXAMPLE
            Get-DbaDbCheckConstraint -SqlInstance Server1 -ExcludeDatabase db1

            Gets the check constraints for all databases except db1.

        .EXAMPLE
            Get-DbaDbCheckConstraint -SqlInstance Server1 -ExcludeSystemTable

            Gets the check constraints for all databases that are not system objects.

        .EXAMPLE
            'Sql1','Sql2/sqlexpress' | Get-DbaDbCheckConstraint

            Gets the check constraints for the databases on Sql1 and Sql2/sqlexpress.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$ExcludeSystemTable,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $databases = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                $databases = $databases | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $databases) {
                if (!$db.IsAccessible) {
                    Write-Message -Level Warning -Message "Database $db is not accessible. Skipping."
                    continue
                }

                foreach($tbl in $db.Tables) {
                    if ( (Test-Bound -ParameterName ExcludeSystemTable) -and $tbl.IsSystemObject ) {
                        continue
                    }

                    if ($tbl.Checks.Count -eq 0) {
                        Write-Message -Message "No Checks exist in $tbl table on the $db database on $instance" -Target $tbl -Level Verbose
                        continue
                    }

                    foreach ($ck in $tbl.Checks) {
                        Add-Member -Force -InputObject $ck -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                        Add-Member -Force -InputObject $ck -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                        Add-Member -Force -InputObject $ck -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                        Add-Member -Force -InputObject $ck -MemberType NoteProperty -Name Database -value $db.Name

                        $defaults = 'ComputerName', 'InstanceName', 'SqlInstance', 'Database', 'Parent', 'ID', 'CreateDate',
                        'DateLastModified', 'Name', 'IsEnabled', 'IsChecked', 'NotForReplication', 'Text', 'State'
                        Select-DefaultView -InputObject $ck -Property $defaults
                    }
                }
            }
        }
    }
}
function Get-DbaDbCompression {
    <#
        .SYNOPSIS
            Gets tables and indexes size and current compression settings.

        .DESCRIPTION
            This function gets the current size and compression for all objects in the specified database(s), if no database is specified it will return all objects in all user databases.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process - this list is auto populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto populated from the server.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Jess Pomfret (@jpomfret jesspomfret.com)
            Tags: Compression, Table, Database
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            Get-DbaDbCompression -SqlInstance localhost

            Returns objects size and current compression level for all user databases.

        .EXAMPLE
            Get-DbaDbCompression -SqlInstance localhost -Database TestDatabase

            Returns objects size and current compression level for objects within the TestDatabase database.

            .EXAMPLE
            Get-DbaDbCompression -SqlInstance localhost -ExcludeDatabase TestDatabases

            Returns objects size and current compression level for objects in all databases except the TestDatabase database.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level VeryVerbose -Message "Connecting to $instance" -Target $instance
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failed to process Instance $Instance" -ErrorRecord $_ -Target $instance -Continue
            }

            try {
                $dbs = $server.Databases | Where-Object { $_.IsAccessible -and $_.IsSystemObject -eq 0 }

                if ($Database) {
                    $dbs = $dbs | Where-Object { $_.Name -In $Database }
                }

                if ($ExcludeDatabase) {
                    $dbs = $dbs | Where-Object { $_.Name -NotIn $ExcludeDatabase }
                }
            }
            catch {
                Stop-Function -Message "Unable to gather list of databases for $instance" -Target $instance -ErrorRecord $_ -Continue
            }

            foreach ($db in $dbs) {
                try {
                    foreach ($obj in $server.Databases[$($db.name)].Tables) {
                        if ($obj.HasHeapIndex) {
                            foreach ($p in $obj.PhysicalPartitions) {
                                [pscustomobject]@{
                                    ComputerName        = $server.ComputerName
                                    InstanceName        = $server.ServiceName
                                    SqlInstance         = $server.DomainInstanceName
                                    Database            = $db.Name
                                    Schema              = $obj.Schema
                                    TableName           = $obj.Name
                                    IndexName           = $null
                                    Partition           = $p.PartitionNumber
                                    IndexID             = 0
                                    IndexType           = "Heap"
                                    DataCompression     = $p.DataCompression
                                    SizeCurrent         = [dbasize]($obj.DataSpaceUsed * 1024)
                                    RowCount            = $obj.RowCount
                                }
                            }
                        }

                        foreach ($index in $obj.Indexes) {
                            foreach ($p in $index.PhysicalPartitions) {
                                [pscustomobject]@{
                                    ComputerName        = $server.ComputerName
                                    InstanceName        = $server.ServiceName
                                    SqlInstance         = $server.DomainInstanceName
                                    Database            = $db.Name
                                    Schema              = $obj.Schema
                                    TableName           = $obj.Name
                                    IndexName           = $index.Name
                                    Partition           = $p.PartitionNumber
                                    IndexID             = $index.ID
                                    IndexType           = $index.IndexType
                                    DataCompression     = $p.DataCompression
                                    SizeCurrent         = if($index.IndexType -eq "ClusteredIndex") { [dbasize]($obj.DataSpaceUsed * 1024) } else { [dbasize]($index.SpaceUsed * 1024) }
                                    RowCount            = $p.RowCount
                                }
                            }
                        }

                    }
                }
                catch {
                    Stop-Function -Message "Unable to query $instance - $db" -Target $db -ErrorRecord $_ -Continue
                }

            }
        }
    }
}
function Get-DbaDbExtentDiff {
    <#
        .SYNOPSIS
            What percentage of a database has changed since the last full backup

        .DESCRIPTION
            This is only an implementation of the script created by Paul S. Randal to find what percentage of a database has changed since the last full backup.
            https://www.sqlskills.com/blogs/paul/new-script-how-much-of-the-database-has-changed-since-the-last-full-backup/

        .PARAMETER SqlInstance
            The target SQL Server instance

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto-populated from the server

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Backup, Database
            Author: Viorel Ciucu, [email protected], cviorel.com

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: GNU GPL v3 https://opensource.org/licenses/GPL-3.0

        .LINK
            http://dbatools.io/Get-DbaDbExtentDiff

        .EXAMPLE
            Get the changes for the DBA database.
            Get-DbaDbExtentDiff -SqlInstance SQL2016 -Database DBA

        .EXAMPLE
            Get the changes for the DB01 database on multiple servers.
            Get-DbaDbExtentDiff -SqlInstance $SQL2017N1, $SQL2017N2, $SQL2016 -Database DB01 -SqlCredential $Cred
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias('ServerInstance', 'SqlServer')]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$EnableException
    )

    begin {
        $rex = [regex]':(?<extent>[\d]+)\)'
        function Get-DbaExtent ([string[]]$field) {
            $res = 0
            foreach ($f in $field) {
                $extents = $rex.Matches($f)
                if ($extents.Count -eq 1) {
                    $res += 1
                }
                else {
                    $pages = [int]$extents[1].Groups['extent'].Value - [int]$extents[0].Groups['extent'].Value
                    $res += $pages / 8 + 1
                }
            }
            return $res
        }
    }

    process {

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-DbaInstance -SqlInstance $instance -SqlCredential $SqlCredential -NonPooled
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $dbs = $server.Databases

            if ($Database) {
                $dbs = $dbs | Where-Object Name -In $Database
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            $sourcedbs = @()
            foreach ($db in $dbs) {
                if ($db.IsAccessible -ne $true) {
                    Write-Message -Level Verbose -Message "$db is not accessible on $instance, skipping"
                }
                else {
                    $sourcedbs += $db
                }
            }

            if ($server.VersionMajor -ge 14 ) {
                foreach ($db in $sourcedbs) {
                    $DBCCPageQueryDMV = "
                        SELECT
                        SUM(total_page_count) / 8 as [ExtentsTotal],
                        SUM(modified_extent_page_count) / 8 as [ExtentsChanged],
                        100.0 * SUM(modified_extent_page_count)/SUM(total_page_count) as [ChangedPerc]
                        FROM sys.dm_db_file_space_usage
                    "
                    $DBCCPageResults = $server.Query($DBCCPageQueryDMV, $db.Name)
                    [pscustomobject]@{
                        ComputerName   = $server.ComputerName
                        InstanceName   = $server.ServiceName
                        SqlInstance    = $server.DomainInstanceName
                        DatabaseName   = $db.Name
                        ExtentsTotal   = $DBCCPageResults.ExtentsTotal
                        ExtentsChanged = $DBCCPageResults.ExtentsChanged
                        ChangedPerc    = [math]::Round($DBCCPageResults.ChangedPerc, 2)
                    }
                }
            }
            else {
                $MasterFilesQuery = "
                        SELECT [file_id], [size], database_id, db_name(database_id) as dbname FROM master.sys.master_files
                        WHERE [type_desc] = N'ROWS'
                    "
                $MasterFiles = $server.Query($MasterFilesQuery)
                $MasterFiles = $MasterFiles | Where-Object dbname -In $sourcedbs.Name
                $MasterFilesGrouped = $MasterFiles | Group-Object -Property dbname

                foreach ($db in $MasterFilesGrouped) {
                    $sizeTotal = 0
                    $dbExtents = @()
                    foreach ($results in $db.Group) {
                        $extentID = 0
                        $sizeTotal = $sizeTotal + $results.size / 8
                        while ($extentID -lt $results.size) {
                            $pageID = $extentID + 6
                            $DBCCPageQuery = "DBCC PAGE ('$($results.dbname)', $($results.file_id), $pageID, 3)  WITH TABLERESULTS, NO_INFOMSGS"
                            $DBCCPageResults = $server.Query($DBCCPageQuery)
                            $dbExtents += $DBCCPageResults | Where-Object { $_.VALUE -eq '    CHANGED' -And $_.ParentObject -like 'DIFF_MAP*'}
                            $extentID = $extentID + 511232
                        }
                    }
                    $extents = Get-DbaExtent $dbExtents.Field
                    [pscustomobject]@{
                        ComputerName   = $server.ComputerName
                        InstanceName   = $server.ServiceName
                        SqlInstance    = $server.DomainInstanceName
                        DatabaseName   = $db.Name
                        ExtentsTotal   = $sizeTotal
                        ExtentsChanged = $extents
                        ChangedPerc    = [math]::Round(($extents / $sizeTotal * 100), 2)
                    }
                }
            }
        }
    }
}
function Get-DbaDbForeignKey {
    <#
        .SYNOPSIS
            Gets database Foreign Keys.

        .DESCRIPTION
            Gets database Foreign Keys.

        .PARAMETER SqlInstance
            The target SQL Server instance(s)

        .PARAMETER SqlCredential
            Allows you to login to SQL Server using alternative credentials

        .PARAMETER Database
            To get Foreign Keys from specific database(s)

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto populated from the server

        .PARAMETER ExcludeSystemTable
            This switch removes all system objects from the tables collection

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database,ForeignKey, Table
            Author: Cláudio Silva ( @ClaudioESSilva | https://claudioessilva.eu)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            Get-DbaDbForeignKey -SqlInstance sql2016

            Gets all database Foreign Keys.

        .EXAMPLE
            Get-DbaDbForeignKey -SqlInstance Server1 -Database db1

            Gets the Foreign Keys for the db1 database.

        .EXAMPLE
            Get-DbaDbForeignKey -SqlInstance Server1 -ExcludeDatabase db1

            Gets the Foreign Keys for all databases except db1.

        .EXAMPLE
            Get-DbaDbForeignKey -SqlInstance Server1 -ExcludeSystemTable

            Gets the Foreign Keys from all tables that are not system objects from all databases.

        .EXAMPLE
            'Sql1','Sql2/sqlexpress' | Get-DbaDbForeignKey

            Gets the Foreign Keys for the databases on Sql1 and Sql2/sqlexpress.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$ExcludeSystemTable,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $databases = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                $databases = $databases | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $databases) {
                if (!$db.IsAccessible) {
                    Write-Message -Level Warning -Message "Database $db is not accessible. Skipping."
                    continue
                }

                foreach($tbl in $db.Tables) {
                    if ( (Test-Bound -ParameterName ExcludeSystemTable) -and $tbl.IsSystemObject ) {
                        continue
                    }

                    if ($tbl.ForeignKeys.Count -eq 0) {
                        Write-Message -Message "No Foreign Keys exist in $tbl table on the $db database on $instance" -Target $tbl -Level Verbose
                        continue
                    }

                    foreach ($fk in $tbl.ForeignKeys) {
                        Add-Member -Force -InputObject $fk -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                        Add-Member -Force -InputObject $fk -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                        Add-Member -Force -InputObject $fk -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                        Add-Member -Force -InputObject $fk -MemberType NoteProperty -Name Database -value $db.Name

                        $defaults = 'ComputerName', 'InstanceName', 'SqlInstance', 'Database', 'Table', 'ID', 'CreateDate',
                        'DateLastModified', 'Name', 'IsEnabled', 'IsChecked', 'NotForReplication', 'ReferencedKey', 'ReferencedTable', 'ReferencedTableSchema'
                        Select-DefaultView -InputObject $fk -Property $defaults
                    }
                }
            }
        }
    }
}
function Get-DbaDbMailHistory {
    <#
    .SYNOPSIS
        Gets the history of mail sent from a SQL instance

    .DESCRIPTION
        Gets the history of mail sent from a SQL instance

    .PARAMETER SqlInstance
        The SQL Server instance, or instances.

    .PARAMETER SqlCredential
        Allows you to login to servers using SQL Logins as opposed to Windows Auth/Integrated/Trusted.

    .PARAMETER Since
    Datetime object used to narrow the results to the send request date

    .PARAMETER Status
    Narrow the results by status. Valid values include Unsent, Sent, Failed and Retrying

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Tags: Logging
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Get-DbaDbMailHistory

    .EXAMPLE
        Get-DbaDbMailHistory -SqlInstance sql01\sharepoint

        Returns the entire dbmail history on sql01\sharepoint

    .EXAMPLE
        Get-DbaDbMailHistory -SqlInstance sql01\sharepoint | Select *

        Returns the entire dbmail history on sql01\sharepoint then return a bunch more columns

    .EXAMPLE
        $servers = "sql2014","sql2016", "sqlcluster\sharepoint"
        $servers | Get-DbaDbMailHistory

        Returns the all dbmail history for "sql2014","sql2016" and "sqlcluster\sharepoint"

#>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [DateTime]$Since,
        [ValidateSet('Unsent', 'Sent', 'Failed', 'Retrying')]
        [string]$Status,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category Connectiondbmail -dbmailRecord $_ -Target $instance -Continue
            }

            $sql = "SELECT SERVERPROPERTY('MachineName') AS ComputerName,
                    ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
                    SERVERPROPERTY('ServerName') AS SqlInstance,
                    mailitem_id as MailItemId,
                    a.profile_id as ProfileId,
                    p.name as Profile,
                    recipients as Recipients,
                    copy_recipients as CopyRecipients,
                    blind_copy_recipients as BlindCopyRecipients,
                    subject as Subject,
                    body as Body,
                    body_format as BodyFormat,
                    importance as Importance,
                    sensitivity as Sensitivity,
                    file_attachments as FileAttachments,
                    attachment_encoding as AttachmentEncoding,
                    query as Query,
                    execute_query_database as ExecuteQueryDatabase,
                    attach_query_result_as_file as AttachQueryResultAsFile,
                    query_result_header as QueryResultHeader,
                    query_result_width as QueryResultWidth,
                    query_result_separator as QueryResultSeparator,
                    exclude_query_output as ExcludeQueryOutput,
                    append_query_error as AppendQueryError,
                    send_request_date as SendRequestDate,
                    send_request_user as SendRequestUser,
                    sent_account_id as SentAccountId,
                    CASE sent_status
                    WHEN 'unsent' THEN 'Unsent'
                    WHEN 'sent' THEN 'Sent'
                    WHEN 'failed' THEN 'Failed'
                    WHEN 'retrying' THEN 'Retrying'
                    END AS SentStatus,
                    sent_date as SentDate,
                    last_mod_date as LastModDate,
                    a.last_mod_user as LastModUser
                    from msdb.dbo.sysmail_allitems a
                    join msdb.dbo.sysmail_profile p
                    on a.profile_id = p.profile_id"

            if ($Since -or $Status) {
                $wherearray = @()

                if ($Since) {
                    $wherearray += "send_request_date >= '$($Since.ToString("yyyy-MM-ddTHH:mm:ss"))'"
                }

                if ($Status) {
                    $Status = $Status -join "', '"
                    $wherearray += "sent_status in ('$Status')"
                }

                $wherearray = $wherearray -join ' and '
                $where = "where $wherearray"
                $sql = "$sql $where"
            }

            Write-Message -Level Debug -Message $sql

            try {
                $server.Query($sql) | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, Profile, Recipients, CopyRecipients, BlindCopyRecipients, Subject, Importance, Sensitivity, FileAttachments, AttachmentEncoding, SendRequestDate, SendRequestUser, SentStatus, SentDate
            }
            catch {
                Stop-Function -Message "Query failure" -ErrorRecord $_ -Continue
            }
        }
    }
}
function Get-DbaDbMailLog {
    <#
    .SYNOPSIS
        Gets the DBMail log from a SQL instance

    .DESCRIPTION
        Gets the DBMail log from a SQL instance

    .PARAMETER SqlInstance
        The SQL Server instance, or instances.

    .PARAMETER SqlCredential
        Allows you to login to servers using SQL Logins as opposed to Windows Auth/Integrated/Trusted.

    .PARAMETER Since
    Datetime object used to narrow the results to the send request date

    .PARAMETER Type
    Narrow the results by type. Valid values include Error, Warning, Success, Information, Internal

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Tags: Logging
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Get-DbaDbMailLog

    .EXAMPLE
        Get-DbaDbMailLog -SqlInstance sql01\sharepoint

        Returns the entire dbmail log on sql01\sharepoint

    .EXAMPLE
        Get-DbaDbMailLog -SqlInstance sql01\sharepoint | Select *

        Returns the entire dbmail log on sql01\sharepoint then return a bunch more columns

    .EXAMPLE
        $servers = "sql2014","sql2016", "sqlcluster\sharepoint"
        $servers | Get-DbaDbMailLog -Type Error, Information

        Returns only the Error and Information dbmail log for "sql2014","sql2016" and "sqlcluster\sharepoint"

#>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [DateTime]$Since,
        [ValidateSet('Error', 'Warning', 'Success', 'Information', 'Internal')]
        [string[]]$Type,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category Connectiondbmail -dbmailRecord $_ -Target $instance -Continue
            }

            $sql = "SELECT SERVERPROPERTY('MachineName') AS ComputerName,
            ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
            SERVERPROPERTY('ServerName') AS SqlInstance,
            log_id as LogId,
            CASE event_type
            WHEN 'error' THEN 'Error'
            WHEN 'warning' THEN 'Warning'
            WHEN 'information' THEN 'Information'
            WHEN 'success' THEN 'Success'
            WHEN 'internal' THEN 'Internal'
            ELSE event_type
            END as EventType,
            log_date as LogDate,
            REPLACE(description, CHAR(10)+')', '') as Description,
            process_id as ProcessId,
            mailitem_id as MailItemId,
            account_id as AccountId,
            last_mod_date as LastModDate,
            last_mod_user as LastModUser,
            last_mod_user as [Login]
            FROM msdb.dbo.sysmail_event_log"

            if ($Since -or $Type) {
                $wherearray = @()

                if ($Since) {
                    $wherearray += "log_date >= '$($Since.ToString("yyyy-MM-ddTHH:mm:ss"))'"
                }

                if ($Type) {
                    $combinedtype = $Type -join "', '"
                    $wherearray += "event_type in ('$combinedtype')"
                }

                $wherearray = $wherearray -join ' and '
                $where = "where $wherearray"
                $sql = "$sql $where"
            }

            Write-Message -Level Debug -Message $sql

            try {
                $server.Query($sql) | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, LogDate, EventType, Description, Login
            }
            catch {
                Stop-Function -Message "Query failure" -InnerErrorRecord $_ -Continue
            }
        }
    }
}
#ValidationTags#CodeStyle,Messaging,FlowControl,Pipeline#
function Get-DbaDbPageInfo {
    <#
        .SYNOPSIS
            Get-DbaDbPageInfo will return page information for a database

        .DESCRIPTION
            Get-DbaDbPageInfo is able to return information about the pages in a database.
            It's possible to return the information for multiple databases and filter on specific databases, schemas and tables.

        .PARAMETER SqlInstance
            The target SQL Server instance(s)

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Filter to only get specific databases

        .PARAMETER Schema
            Filter to only get specific schemas

        .PARAMETER Table
            Filter to only get specific tables

        .PARAMETER InputObject
            Enables piping from Get-DbaDatabase

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database, Page
            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaDbPageInfo

        .EXAMPLE
            Get-DbaDbPageInfo -SqlInstance sql2017

            Returns page information for all databases on sql2017

        .EXAMPLE
            Get-DbaDbPageInfo -SqlInstance sql2017, sql2016 -Database testdb

            Returns page information for the testdb on sql2017 and sql2016

        .EXAMPLE
            $servers | Get-DbaDatabase -Database testdb | Get-DbaDbPageInfo

            Returns page information for the testdb on all $servers
    #>
    [CmdLetBinding()]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$Database,
        [string[]]$Schema,
        [string[]]$Table,
        [parameter(ValueFromPipeline)]
        [Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
        [switch]$EnableException
    )
    begin {
        $sql = "SELECT SERVERPROPERTY('MachineName') AS ComputerName,
        ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
        SERVERPROPERTY('ServerName') AS SqlInstance, [Database] = DB_NAME(DB_ID()),
        ss.name AS [Schema], st.name AS [Table], dbpa.page_type_desc AS PageType,
                        dbpa.page_free_space_percent AS PageFreePercent,
                        IsAllocated =
                          CASE dbpa.is_allocated
                             WHEN 0 THEN 'False'
                             WHEN 1 THEN 'True'
                          END,
                        IsMixedPage =
                          CASE dbpa.is_mixed_page_allocation
                             WHEN 0 THEN 'False'
                             WHEN 1 THEN 'True'
                          END
                        FROM sys.dm_db_database_page_allocations(DB_ID(), NULL, NULL, NULL, 'DETAILED') AS dbpa
                        INNER JOIN sys.tables AS st ON st.object_id = dbpa.object_id
                        INNER JOIN sys.schemas AS ss ON ss.schema_id = st.schema_id"

        if ($Schema) {
            $sql = "$sql WHERE ss.name IN ('$($Schema -join "','")')"
        }

        if ($Table) {
            if ($schema) {
                $sql = "$sql AND st.name IN ('$($Table -join "','")')"
            }
            else {
                $sql = "$sql WHERE st.name IN ('$($Table -join "','")')"
            }
        }
    }
    process {
        # Loop through all the instances
        foreach ($instance in $SqlInstance) {

            # Try connecting to the instance
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 11
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($Database) {
                $InputObject += $server.Databases | Where-Object { $_.Name -in $Database }
            }
            else {
                $InputObject += $server.Databases
            }
        }

        # Loop through each of databases
        foreach ($db in $InputObject) {
            # Revalidate the version of the server in case db is piped in
            try {
                if ($db.Parent.VersionMajor -ge 11) {
                    $db.Query($sql)
                }
                else
                {
                    Stop-Function -Message "Unsupported SQL Server version" -Target $db -Continue
                }
            }
            catch {
                Stop-Function -Message "Something went wrong executing the query" -ErrorRecord $_ -Target $instance -Continue
            }
        }
    }
}
#ValidationTags#CodeStyle,Messaging,FlowControl,Pipeline#
function Get-DbaDbQueryStoreOptions {
    <#
        .SYNOPSIS
        Get the Query Store configuration for Query Store enabled databases.

        .DESCRIPTION
        Retrieves and returns the Query Store configuration for every database that has the Query Store feature enabled.

        .OUTPUTS
        Microsoft.SqlServer.Management.Smo.QueryStoreOptions

        .PARAMETER SqlInstance
        The SQL Server that you're connecting to.

        .PARAMETER SqlCredential
        SqlCredential object used to connect to the SQL Server as a different user.

        .PARAMETER Database
        The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
        The database(s) to exclude - this list is auto-populated from the server

        .PARAMETER EnableException
                By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
                This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
                Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
        Tags: QueryStore
        Author: Enrico van de Laar ( @evdlaar )
        Author: Klaas Vandenberghe ( @PowerDBAKlaas )

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

        .LINK
        https://dbatools.io/Get-DbaQueryStoreOptions

        .EXAMPLE
        Get-DbaDbQueryStoreOptions -SqlInstance ServerA\sql

        Returns Query Store configuration settings for every database on the ServerA\sql instance.

        .EXAMPLE
        Get-DbaDbQueryStoreOptions -SqlInstance ServerA\sql | Where-Object {$_.ActualState -eq "ReadWrite"}

        Returns the Query Store configuration for all databases on ServerA\sql where the Query Store feature is in Read/Write mode.

        .EXAMPLE
        Get-DbaDbQueryStoreOptions -SqlInstance localhost | format-table -AutoSize -Wrap

        Returns Query Store configuration settings for every database on the ServerA\sql instance inside a table format.

#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        $ExcludeDatabase += 'master', 'tempdb'
    }
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 13
            }
            catch {
                Write-Message -Level Warning -Message "Can't connect to $instance. Moving on."
                continue
            }

            # We have to exclude all the system databases since they cannot have the Query Store feature enabled
            $dbs = Get-DbaDatabase -SqlInstance $server -ExcludeDatabase $ExcludeDatabase -Database $Database | Where-Object IsAccessible

            foreach ($db in $dbs) {
                Write-Message -Level Verbose -Message "Processing $($db.Name) on $instance"
                $QSO = $db.QueryStoreOptions

                Add-Member -Force -InputObject $QSO -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                Add-Member -Force -InputObject $QSO -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                Add-Member -Force -InputObject $QSO -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                Add-Member -Force -InputObject $QSO -MemberType NoteProperty Database -value $db.Name
                Select-DefaultView -InputObject $QSO -Property ComputerName, InstanceName, SqlInstance, Database, ActualState, DataFlushIntervalInSeconds, StatisticsCollectionIntervalInMinutes, MaxStorageSizeInMB, CurrentStorageSizeInMB, QueryCaptureMode, SizeBasedCleanupMode, StaleQueryThresholdInDays
            }
        }
    }
}
function Get-DbaDbRecoveryModel {
    <#
        .SYNOPSIS
            Get-DbaDbRecoveryModel displays the Recovery Model.

        .DESCRIPTION
            Get-DbaDbRecoveryModel displays the Recovery Model for all databases. This is the default, you can filter using -Database, -ExcludeDatabase, -RecoveryModel

        .PARAMETER SqlInstance
            The SQL Server instance.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process - this list is auto-populated from the server. if unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto-populated from the server

        .PARAMETER RecoveryModel
            Filters the output based on Recovery Model. Valid options are Simple, Full and BulkLogged

            Details about the recovery models can be found here:
            https://docs.microsoft.com/en-us/sql/relational-databases/backup-restore/recovery-models-sql-server

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Recovery, RecoveryModel, Simple, Full, Bulk, BulkLogged
            Author: Viorel Ciucu (@viorelciucu), https://www.cviorel.com

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaDbRecoveryModel

        .EXAMPLE
            Get-DbaDbRecoveryModel -SqlInstance sql2014 -RecoveryModel BulkLogged -Verbose

            Gets all databases on SQL Server instance sql2014 having RecoveryModel set to BulkLogged

        .EXAMPLE
            Get-DbaDbRecoveryModel -SqlInstance sql2014 -Database TestDB

            Gets recovery model information for TestDB. If TestDB does not exist on the instance we don't return anythig.

    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [ValidateSet('Simple', 'Full', 'BulkLogged')]
        [string[]]$RecoveryModel,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$EnableException
    )
    begin {
        $defaults = 'ComputerName', 'InstanceName', 'SqlInstance', 'Name', 'Status', 'IsAccessible', 'RecoveryModel',
        'LastBackupDate as LastFullBackup', 'LastDifferentialBackupDate as LastDiffBackup',
        'LastLogBackupDate as LastLogBackup'
    }
    process {
        $params = @{
            SqlInstance     = $SqlInstance
            SqlCredential   = $SqlCredential
            Database        = $Database
            ExcludeDatabase = $ExcludeDatabase
            EnableException = $EnableException
        }

        if ($RecoveryModel) {
            Get-DbaDatabase @params | Where-Object RecoveryModel -in $RecoveryModel | Where-Object IsAccessible | Select-DefaultView -Property $defaults
        }
        else {
            Get-DbaDatabase @params | Select-DefaultView -Property $defaults
        }
    }
}
function Get-DbaDbRole {
    <#
.SYNOPSIS
Get database roles on a Sql instance.

.DESCRIPTION
Get database roles on a Sql instance.

Default output includes columns SQLServer, Database, Role.

.PARAMETER SQLInstance
The SQL Server that you're connecting to.

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Database
The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

.PARAMETER ExcludeDatabase
The database(s) to exclude - this list is auto-populated from the server

.PARAMETER ExcludeFixedRole
Excludes all fixed roles.

.PARAMETER Credential
Credential object used to connect to the SQL Server as a different user.

.PARAMETER EnableException
    By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
    Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Roles, Database, Security
Author: Klaas Vandenberghe ( @PowerDBAKlaas )

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
 https://dbatools.io/Get-DbaDbRole

.EXAMPLE
Get-DbaDbRole -SqlInstance ServerA

Returns a custom object displaying SQLServer, Database, Role for all DatabaseRoles on sql instance ServerA.

.EXAMPLE
Get-DbaDbRole -SqlInstance ServerA | Out-Gridview

Returns a gridview displaying SQLServer, Database, Role for all DatabaseRoles on sql instance ServerA.

.EXAMPLE
Get-DbaDbRole -SqlInstance ServerB\sql16 -ExcludeDatabase DBADB,TestDB

Returns SQLServer, Database, Role for DatabaseRoles on sql instance ServerB\sql16, except those in databases DBADB and TestDB.

.EXAMPLE
'ServerB\sql16','ServerA' | Get-DbaDbRole

Returns SQLServer, Database, Role for DatabaseRoles on sql instances ServerA and ServerB\sql16.

.EXAMPLE
Get-DbaDbRole -SqlInstance ServerB\sql16 -Database AccountingDB

Returns SQLServer, Database, Role for DatabaseRoles in database AccountingDB on sql instance ServerB\sql16.

.EXAMPLE
Get-DbaDbRole -SqlInstance ServerB\sql16 -ExcludeFixedRoles

Returns SQLServer, Database, Role for DatabaseRoles on sql instance ServerB\sql16, but not the fixed roles.

#>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias('SqlServer', 'ServerInstance')]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$ExcludeFixedRole,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {

        foreach ($instance in $sqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $dbs = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                Write-Message -Level Verbose -Message "Databases to check: $Database"
                $dbs = $dbs | Where-Object Name -In $Database
            }

            if ($ExcludeDatabase) {
                Write-Message -Level Verbose -Message "Databases excluded from check: $ExcludeDatabase"
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $dbs) {
                Write-Message -Level Verbose -Message "Checking accessibility of $db on $instance"

                if ($db.IsAccessible -ne $true) {
                    Write-Message -Level Warning -Message "Database $db on $instance is not accessible"
                    continue
                }

                $dbroles = $db.roles
                Write-Message -Level Verbose -Message "Getting Database Roles for $db on $instance"

                if ($ExcludeFixedRole) {
                    $dbroles = $dbroles | Where-Object IsFixedRole -eq $false
                }

                foreach ($dbrole in $dbroles) {
                    Add-Member -Force -InputObject $dbrole -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                    Add-Member -Force -InputObject $dbrole -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                    Add-Member -Force -InputObject $dbrole -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                    Add-Member -Force -InputObject $dbrole -MemberType NoteProperty -Name Database -value $db.Name

                    Select-DefaultView -InputObject $dbrole -Property ComputerName, InstanceName, SqlInstance, Database, Name, Owner, CreateDate, DateLastModified, IsFixedRole
                }
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaDbSnapshot {
    <#
    .SYNOPSIS
        Get database snapshots with details
    .DESCRIPTION
        Retrieves the list of database snapshot available, along with their base (the db they are the snapshot of) and creation time
    .PARAMETER SqlInstance
        The SQL Server that you're connecting to.
    .PARAMETER SqlCredential
        Credential object used to connect to the SQL Server as a different user
    .PARAMETER Database
        Return information for only specific databases
    .PARAMETER ExcludeDatabase
        The database(s) to exclude - this list is auto-populated from the server
    .PARAMETER Snapshot
        Return information for only specific snapshots
    .PARAMETER ExcludeSnapshot
        The snapshot(s) to exclude - this list is auto-populated from the server
    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
    .NOTES
        Tags: Snapshot
        Author: niphlod
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT
    .LINK
         https://dbatools.io/Get-DbaDbSnapshot
    .EXAMPLE
        Get-DbaDbSnapshot -SqlInstance sqlserver2014a
        Returns a custom object displaying Server, Database, DatabaseCreated, SnapshotOf, SizeMB, DatabaseCreated
    .EXAMPLE
        Get-DbaDbSnapshot -SqlInstance sqlserver2014a -Database HR, Accounting
        Returns information for database snapshots having HR and Accounting as base dbs
    .EXAMPLE
        Get-DbaDbSnapshot -SqlInstance sqlserver2014a -Snapshot HR_snapshot, Accounting_snapshot
        Returns information for database snapshots HR_snapshot and Accounting_snapshot
#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [object[]]$Snapshot,
        [object[]]$ExcludeSnapshot,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            $dbs = $server.Databases | Where-Object DatabaseSnapshotBaseName
            if ($Database) {
                $dbs = $dbs | Where-Object { $Database -contains $_.DatabaseSnapshotBaseName }
            }
            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object { $ExcludeDatabase -notcontains $_.DatabaseSnapshotBaseName }
            }
            if ($Snapshot) {
                $dbs = $dbs | Where-Object { $Snapshot -contains $_.Name }
            }
            if (!$Snapshot -and !$Database) {
                $dbs = $dbs | Where-Object IsDatabaseSnapshot -eq $true | Sort-Object DatabaseSnapshotBaseName, Name
            }
            if ($ExcludeSnapshot) {
                $dbs = $dbs | Where-Object { $ExcludeSnapshot -notcontains $_.Name }
            }
            foreach ($db in $dbs) {
                try {
                    $BytesOnDisk = $db.Query("SELECT SUM(BytesOnDisk) AS BytesOnDisk FROM fn_virtualfilestats(DB_ID(),NULL) S JOIN sys.databases D on D.database_id = S.dbid", $db.Name)
                    Add-Member -Force -InputObject $db -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                    Add-Member -Force -InputObject $db -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                    Add-Member -Force -InputObject $db -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                    Add-Member -Force -InputObject $db -MemberType NoteProperty -Name DiskUsage -value ([dbasize]($BytesOnDisk.BytesOnDisk))
                    Select-DefaultView -InputObject $db -Property ComputerName, InstanceName, SqlInstance, Name, 'DatabaseSnapshotBaseName as SnapshotOf', CreateDate, DiskUsage
                }
                catch {
                    Stop-Function -Message "Failure" -ErrorRecord $_ -Target $db -Continue
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Get-DbaDatabaseSnapshot
    }
}
function Get-DbaDbStoredProcedure {
    <#
        .SYNOPSIS
            Gets database Stored Procedures

        .DESCRIPTION
            Gets database Stored Procedures

        .PARAMETER SqlInstance
            The target SQL Server instance(s)

        .PARAMETER SqlCredential
            Allows you to login to SQL Server using alternative credentials

        .PARAMETER Database
            To get Stored Procedures from specific database(s)

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto populated from the server

        .PARAMETER ExcludeSystemSp
            This switch removes all system objects from the Stored Procedure collection

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database, StoredProcedure, Proc
            Author: Klaas Vandenberghe ( @PowerDbaKlaas )

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            Get-DbaDbStoredProcedure -SqlInstance sql2016

            Gets all database Stored Procedures

        .EXAMPLE
            Get-DbaDbStoredProcedure -SqlInstance Server1 -Database db1

            Gets the Stored Procedures for the db1 database

        .EXAMPLE
            Get-DbaDbStoredProcedure -SqlInstance Server1 -ExcludeDatabase db1

            Gets the Stored Procedures for all databases except db1

        .EXAMPLE
            Get-DbaDbStoredProcedure -SqlInstance Server1 -ExcludeSystemSp

            Gets the Stored Procedures for all databases that are not system objects

        .EXAMPLE
            'Sql1','Sql2/sqlexpress' | Get-DbaDbStoredProcedure

            Gets the Stored Procedures for the databases on Sql1 and Sql2/sqlexpress
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$ExcludeSystemSp,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $databases = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                $databases = $databases | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $databases) {
                if (!$db.IsAccessible) {
                    Write-Message -Level Warning -Message "Database $db is not accessible. Skipping."
                    continue
                }
                if ($db.StoredProcedures.Count -eq 0) {
                    Write-Message -Message "No Stored Procedures exist in the $db database on $instance" -Target $db -Level Output
                    continue
                }

                foreach ($proc in $db.StoredProcedures) {
                    if ( (Test-Bound -ParameterName ExcludeSystemSp) -and $proc.IsSystemObject ) {
                        continue
                    }

                    Add-Member -Force -InputObject $proc -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                    Add-Member -Force -InputObject $proc -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                    Add-Member -Force -InputObject $proc -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                    Add-Member -Force -InputObject $proc -MemberType NoteProperty -Name Database -value $db.Name

                    $defaults = 'ComputerName', 'InstanceName', 'SqlInstance', 'Database', 'Schema', 'ID as ObjectId', 'CreateDate',
                    'DateLastModified', 'Name', 'ImplementationType', 'Startup'
                    Select-DefaultView -InputObject $proc -Property $defaults
                }
            }
        }
    }
}
#ValidationTags#CodeStyle,Messaging,FlowControl,Pipeline#
function Get-DbaDbVirtualLogFile {
    <#
        .SYNOPSIS
            Returns database virtual log file information for database files on a SQL instance.

        .DESCRIPTION
            Having a transaction log file with too many virtual log files (VLFs) can hurt database performance.

            Too many VLFs can cause transaction log backups to slow down and can also slow down database recovery and, in extreme cases, even affect insert/update/delete performance.

            References:
                http://www.sqlskills.com/blogs/kimberly/transaction-log-vlfs-too-many-or-too-few/
                http://blogs.msdn.com/b/saponsqlserver/archive/2012/02/22/too-many-virtual-log-files-vlfs-can-cause-slow-database-recovery.aspx

            If you've got a high number of VLFs, you can use Expand-SqlTLogResponsibly to reduce the number.

        .PARAMETER SqlInstance
            Specifies the SQL Server instance(s) to scan.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies the database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies the database(s) to exclude from processing. Options for this list are auto-populated from the server.

        .PARAMETER IncludeSystemDBs
            If this switch is enabled, system database information will be displayed.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: VLF, Database, LogFile

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaDbVirtualLogFile

        .EXAMPLE
            Get-DbaDbVirtualLogFile -SqlInstance sqlcluster

            Returns all user database virtual log file details for the sqlcluster instance.

        .EXAMPLE
            Get-DbaDbVirtualLogFile -SqlInstance sqlserver | Group-Object -Property Database | Where-Object Count -gt 50

            Returns user databases that have 50 or more VLFs.

        .EXAMPLE
            @('sqlserver','sqlcluster') | Get-DbaDbVirtualLogFile

            Returns all VLF information for the sqlserver and sqlcluster SQL Server instances. Processes data via the pipeline.

        .EXAMPLE
            Get-DbaDbVirtualLogFile -SqlInstance sqlcluster -Database db1, db2

            Returns the VLF counts for the db1 and db2 databases on sqlcluster.
    #>
    [CmdletBinding()]
    [OutputType([System.Collections.ArrayList])]
    param ([parameter(ValueFromPipeline, Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$IncludeSystemDBs,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $dbs = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                $dbs = $dbs | Where-Object Name -in $Database
            }
            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            if (!$IncludeSystemDBs) {
                $dbs = $dbs | Where-Object IsSystemObject -eq $false
            }

            foreach ($db in $dbs) {
                try {
                    $data = $db.Query("DBCC LOGINFO")

                    foreach ($d in $data) {
                        [pscustomobject]@{
                            ComputerName   = $server.ComputerName
                            InstanceName   = $server.ServiceName
                            SqlInstance    = $server.DomainInstanceName
                            Database       = $db.Name
                            RecoveryUnitId = $d.RecoveryUnitId
                            FileId         = $d.FileId
                            FileSize       = $d.FileSize
                            StartOffset    = $d.StartOffset
                            FSeqNo         = $d.FSeqNo
                            Status         = $d.Status
                            Parity         = $d.Parity
                            CreateLsn      = $d.CreateLSN
                        }
                    }
                }
                catch {
                    Stop-Function -Message "Unable to query $($db.name) on $instance." -ErrorRecord $_ -Target $db -Continue
                }
            }
        }
    }
}
function Get-DbaDefaultPath {
    <#
    .SYNOPSIS
        Gets the default SQL Server paths for data, logs and backups

    .DESCRIPTION
        Gets the default SQL Server paths for data, logs and backups

    .PARAMETER SqlInstance
        The SQL Server instance, or instances.

    .PARAMETER SqlCredential
        Allows you to login to servers using SQL Logins as opposed to Windows Auth/Integrated/Trusted.

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Tags: Config
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Get-DbaDefaultPath

    .EXAMPLE
        Get-DbaDefaultPath -SqlInstance sql01\sharepoint

        Returns the default file paths for sql01\sharepoint

    .EXAMPLE
        $servers = "sql2014","sql2016", "sqlcluster\sharepoint"
        $servers | Get-DbaDefaultPath

        Returns the default file paths for "sql2014","sql2016" and "sqlcluster\sharepoint"

#>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -AzureUnsupported
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $dataPath = $server.DefaultFile
            if ($dataPath.Length -eq 0) {
                $dataPath = $server.ConnectionContext.ExecuteScalar("SELECT SERVERPROPERTY('InstanceDefaultdataPath')")
            }

            if ($dataPath -eq [System.DBNull]::Value -or $dataPath.Length -eq 0) {
                $dataPath = Split-Path (Get-DbaDatabase -SqlInstance $server -Database model).FileGroups[0].Files[0].FileName
            }

            if ($dataPath.Length -eq 0) {
                $dataPath = $server.Information.MasterDbPath
            }

            $logPath = $server.DefaultLog

            if ($logPath.Length -eq 0) {
                $logPath = $server.ConnectionContext.ExecuteScalar("SELECT SERVERPROPERTY('InstanceDefaultLogPath')")
            }

            if ($logPath -eq [System.DBNull]::Value -or $logPath.Length -eq 0) {
                $logPath = Split-Path (Get-DbaDatabase -SqlInstance $server -Database model).LogFiles.FileName
            }

            if ($logPath.Length -eq 0) {
                $logPath = $server.Information.MasterDbLogPath
            }

            $dataPath = $dataPath.Trim().TrimEnd("\")
            $logPath = $logPath.Trim().TrimEnd("\")

            [PSCustomObject]@{
                ComputerName = $server.ComputerName
                InstanceName = $server.ServiceName
                SqlInstance  = $server.DomainInstanceName
                Data         = $dataPath
                Log          = $logPath
                Backup       = $server.BackupDirectory
                ErrorLog     = $server.ErrorlogPath
            }
        }
    }
}
function Get-DbaDependency {
    <#
        .SYNOPSIS
            Finds object dependencies and their relevant creation scripts.

        .DESCRIPTION
            This function recursively finds all objects that depends on the input.
            It will then retrieve rich information from them, including their creation scripts and the order in which it should be applied.

            By using the 'Parents' switch, the function will instead retrieve all items that the input depends on (including their creation scripts).

            For more details on dependency, see:
            https://technet.microsoft.com/en-us/library/ms345449(v=sql.105).aspx

        .PARAMETER InputObject
            The SMO object to parse

        .PARAMETER AllowSystemObjects
            Normally, system objects are ignored by this function as dependencies.
            This switch overrides that behavior.

        .PARAMETER Parents
            Causes the function to retrieve all objects that the input depends on, rather than retrieving everything that depends on the input.

        .PARAMETER IncludeSelf
            Includes the object whose dependencies are retrieves itself.
            Useful when exporting an entire logic structure in order to recreate it in another database.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER IncludeScript
            Setting this switch will cause the function to also retrieve the creation script of the dependency.

        .NOTES
            Tags: Database, Dependent, Dependency, Object
            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaDependency

        .EXAMPLE
            $table = (Get-DbaDatabase -SqlInstance sql2012 -Database Northwind).tables | Where Name -eq Customers
            $table | Get-DbaDependency

            Returns everything that depends on the "Customers" table
    #>
    [CmdletBinding()]
    Param (
        [Parameter(ValueFromPipeline = $true)]
        $InputObject,

        [switch]
        $AllowSystemObjects,

        [switch]
        $Parents,

        [switch]
        $IncludeSelf,

        [switch]
        [Alias('Silent')]$EnableException
    )

    Begin {
        #region Utility functions
        function Get-DependencyTree {
            [CmdletBinding()]
            Param (
                $Object,

                $Server,

                [bool]
                $AllowSystemObjects,

                [bool]
                $EnumParents,

                [string]
                $FunctionName,

                [bool]
                $EnableException
            )

            $scripter = New-Object Microsoft.SqlServer.Management.Smo.Scripter
            $options = New-Object Microsoft.SqlServer.Management.Smo.ScriptingOptions
            $options.DriAll = $true
            $options.AllowSystemObjects = $AllowSystemObjects
            $options.WithDependencies = $true
            $scripter.Options = $options
            $scripter.Server = $Server

            $urnCollection = New-Object Microsoft.SqlServer.Management.Smo.UrnCollection

            Write-Message -EnableException $EnableException -Level 5 -Message "Adding $Object which is a $($Object.urn.Type)" -FunctionName $FunctionName
            $urnCollection.Add([Microsoft.SqlServer.Management.Sdk.Sfc.Urn]$Object.urn)

            #now we set up an event listnenr go get progress reports
            $progressReportEventHandler = [Microsoft.SqlServer.Management.Smo.ProgressReportEventHandler] {
                $name = $_.Current.GetAttribute('Name');
                Write-Message -EnableException $EnableException -Level 5 -Message "Analysed $name" -FunctionName $FunctionName
            }
            $scripter.add_DiscoveryProgress($progressReportEventHandler)

            return $scripter.DiscoverDependencies($urnCollection, $EnumParents)
        }

        function Read-DependencyTree {
            [CmdletBinding()]
            Param (
                [System.Object]
                $InputObject,

                [int]
                $Tier,

                [System.Object]
                $Parent,

                [bool]
                $EnumParents
            )

            Add-Member -Force -InputObject $InputObject -Name Parent -Value $Parent -MemberType NoteProperty
            if ($EnumParents) { Add-Member -Force -InputObject $InputObject -Name Tier -Value ($Tier * -1) -MemberType NoteProperty -PassThru }
            else { Add-Member -Force -InputObject $InputObject -Name Tier -Value $Tier -MemberType NoteProperty -PassThru }

            if ($InputObject.HasChildNodes) { Read-DependencyTree -InputObject $InputObject.FirstChild -Tier ($Tier + 1) -Parent $InputObject -EnumParents $EnumParents }
            if ($InputObject.NextSibling) { Read-DependencyTree -InputObject $InputObject.NextSibling -Tier $Tier -Parent $Parent -EnumParents $EnumParents }
        }

        function Get-DependencyTreeNodeDetail {
            [CmdletBinding()]
            Param (
                [Parameter(ValueFromPipeline = $true)]
                $SmoObject,

                $Server,

                $OriginalResource,

                [bool]
                $AllowSystemObjects
            )

            Begin {
                $scripter = New-Object Microsoft.SqlServer.Management.Smo.Scripter
                $options = New-Object Microsoft.SqlServer.Management.Smo.ScriptingOptions
                $options.DriAll = $true
                $options.AllowSystemObjects = $AllowSystemObjects
                $options.WithDependencies = $true
                $scripter.Options = $options
                $scripter.Server = $Server
            }

            process {
                foreach ($Item in $SmoObject) {
                    $richobject = $Server.GetSmoObject($Item.urn)
                    $parent = $Server.GetSmoObject($Item.Parent.Urn)

                    $NewObject = New-Object Sqlcollaborative.Dbatools.Database.Dependency
                    $NewObject.ComputerName = $server.ComputerName
                    $NewObject.ServiceName = $server.ServiceName
                    $NewObject.SqlInstance = $server.DomainInstanceName
                    $NewObject.Dependent = $richobject.Name
                    $NewObject.Type = $Item.Urn.Type
                    $NewObject.Owner = $richobject.Owner
                    $NewObject.IsSchemaBound = $Item.IsSchemaBound
                    $NewObject.Parent = $parent.Name
                    $NewObject.ParentType = $parent.Urn.Type
                    $NewObject.Tier = $Item.Tier
                    $NewObject.Object = $richobject
                    $NewObject.Urn = $richobject.Urn
                    $NewObject.OriginalResource = $OriginalResource

                    $SQLscript = $scripter.EnumScriptWithList($richobject)

                    # I can't remember how to remove these options and their syntax is breaking stuff
                    $SQLscript = $SQLscript -replace "SET ANSI_NULLS ON", ""
                    $SQLscript = $SQLscript -replace "SET QUOTED_IDENTIFIER ON", ""
                    $NewObject.Script = "$SQLscript `r`ngo"

                    $NewObject
                }
            }
        }

        function Select-DependencyPrecedence {
            [CmdletBinding()]
            Param (
                [Parameter(ValueFromPipeline = $true)]
                $Dependency
            )

            Begin {
                $list = @()
            }
            Process {
                foreach ($dep in $Dependency) {
                    # Killing the pipeline is generally a bad idea, but since we have to group and sort things, we have not really a choice
                    $list += $dep
                }
            }
            End {
                $list | Group-Object -Property Object | ForEach-Object { $_.Group | Sort-Object -Property Tier -Descending | Select-Object -First 1 } | Sort-Object Tier
            }
        }
        #endregion Utility functions
    }
    Process {
        foreach ($Item in $InputObject) {
            Write-Message -EnableException $EnableException -Level Verbose -Message "Processing: $Item"
            if ($null -eq $Item.urn) {
                Stop-Function -Message "$Item is not a valid SMO object" -EnableException $EnableException -Category InvalidData -Continue -Target $Item
            }

            # Find the server object to pass on to the function
            $parent = $Item.parent

            do { $parent = $parent.parent }
            until (($parent.urn.type -eq "Server") -or (-not $parent))

            if (-not $parent) {
                Stop-Function -Message "Failed to find valid server object in input: $Item" -EnableException $EnableException -Category InvalidData -Continue -Target $Item
            }

            $server = $parent

            $tree = Get-DependencyTree -Object $Item -AllowSystemObjects $false -Server $server -FunctionName (Get-PSCallStack)[0].COmmand -EnableException $EnableException -EnumParents $Parents
            $limitCount = 2
            if ($IncludeSelf) { $limitCount = 1 }
            if ($tree.Count -lt $limitCount) {
                Write-Message -Message "No dependencies detected for $($Item)" -Level Host
                continue
            }

            if ($IncludeSelf) { $resolved = Read-DependencyTree -InputObject $tree.FirstChild -Tier 0 -Parent $tree.FirstChild -EnumParents $Parents }
            else { $resolved = Read-DependencyTree -InputObject $tree.FirstChild.FirstChild -Tier 1 -Parent $tree.FirstChild -EnumParents $Parents }
            $resolved | Get-DependencyTreeNodeDetail -Server $server -OriginalResource $Item -AllowSystemObjects $AllowSystemObjects | Select-DependencyPrecedence
        }
    }
}
function Get-DbaDetachedDatabaseInfo {
    <#
        .SYNOPSIS
            Get detailed information about detached SQL Server database files.

        .DESCRIPTION
            Gathers the following information from detached database files: database name, SQL Server version (compatibility level), collation, and file structure.

            "Data files" and "Log file" report the structure of the data and log files as they were when the database was detached. "Database version" is the compatibility level.

            MDF files are most easily read by using a SQL Server to interpret them. Because of this, you must specify a SQL Server and the path must be relative to the SQL Server.

        .PARAMETER SqlInstance
            Source SQL Server. This instance must be online and is required to parse the information contained with in the detached database file.

            This function will not attach the database file, it will only use SQL Server to read its contents.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Path
            Specifies the path to the MDF file to be read. This path must be readable by the SQL Server service account. Ideally, the MDF will be located on the SQL Server itself, or on a network share to which the SQL Server service account has access.

        .NOTES
            Tags: Database, Detach

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaDetachedDatabaseInfo

        .EXAMPLE
            Get-DbaDetachedDatabaseInfo -SqlInstance sql2016 -Path M:\Archive\mydb.mdf

            Returns information about the detached database file M:\Archive\mydb.mdf using the SQL Server instance sql2016. The M drive is relative to the SQL Server instance.
     #>

    [CmdletBinding(DefaultParameterSetName = "Default")]
    Param (
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [parameter(Mandatory = $true)]
        [Alias("Mdf")]
        [string]$Path,
        [PSCredential]$SqlCredential
    )

    begin {
        function Get-MdfFileInfo {
            $datafiles = New-Object System.Collections.Specialized.StringCollection
            $logfiles = New-Object System.Collections.Specialized.StringCollection

            $servername = $server.name
            $serviceaccount = $server.ServiceAccount

            $exists = Test-DbaSqlPath -SqlInstance $server -Path $Path

            if ($exists -eq $false) {
                throw "$servername cannot access the file $path. Does the file exist and does the service account ($serviceaccount) have access to the path?"
            }

            try {
                $detachedDatabaseInfo = $server.DetachedDatabaseInfo($path)
                $dbname = ($detachedDatabaseInfo | Where-Object { $_.Property -eq "Database name" }).Value
                $exactdbversion = ($detachedDatabaseInfo | Where-Object { $_.Property -eq "Database version" }).Value
                $collationid = ($detachedDatabaseInfo | Where-Object { $_.Property -eq "Collation" }).Value
            }
            catch {
                throw "$servername cannot read the file $path. Is the database detached?"
            }

            switch ($exactdbversion) {
                852 { $dbversion = "SQL Server 2016" }
                829 { $dbversion = "SQL Server 2016 Prerelease" }
                782 { $dbversion = "SQL Server 2014" }
                706 { $dbversion = "SQL Server 2012" }
                684 { $dbversion = "SQL Server 2012 CTP1" }
                661 { $dbversion = "SQL Server 2008 R2" }
                660 { $dbversion = "SQL Server 2008 R2" }
                655 { $dbversion = "SQL Server 2008 SP2+" }
                612 { $dbversion = "SQL Server 2005" }
                611 { $dbversion = "SQL Server 2005" }
                539 { $dbversion = "SQL Server 2000" }
                515 { $dbversion = "SQL Server 7.0" }
                408 { $dbversion = "SQL Server 6.5" }
                default { $dbversion = "Unknown" }
            }

            $collationsql = "SELECT name FROM fn_helpcollations() where collationproperty(name, N'COLLATIONID')  = $collationid"

            try {
                $dataset = $server.databases['master'].ExecuteWithResults($collationsql)
                $collation = "$($dataset.Tables[0].Rows[0].Item(0))"
            }
            catch {
                $collation = $collationid
            }

            if ($collation.length -eq 0) { $collation = $collationid }

            try {
                foreach ($file in $server.EnumDetachedDatabaseFiles($path)) {
                    $datafiles += $file
                }

                foreach ($file in $server.EnumDetachedLogFiles($path)) {
                    $logfiles += $file
                }
            }
            catch {
                throw "$servername unable to enumerate database or log structure information for $path"
            }

            $mdfinfo = [pscustomobject]@{
                Name         = $dbname
                Version      = $dbversion
                ExactVersion = $exactdbversion
                Collation    = $collation
                DataFiles    = $datafiles
                LogFiles     = $logfiles
            }

            return $mdfinfo
        }
    }

    process {

        $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
        $mdfinfo = Get-MdfFileInfo $server $path

    }

    end {
        $server.ConnectionContext.Disconnect()
        return $mdfinfo
    }
}
#ValidationTags#CodeStyle,Messaging,FlowControl,Pipeline#
function Get-DbaDiskSpace {
    <#
        .SYNOPSIS
            Displays disk information for all local disk on a server.

        .DESCRIPTION
            Returns a custom object with server name, name of disk, label of disk, total size, free size, percent free, block size and filesystem.

            By default, this function only shows drives of types 2 and 3 (removable disk and local disk).

            Requires Windows administrator access on SQL Servers

        .PARAMETER ComputerName
            The target computer. Defaults to localhost.

        .PARAMETER Credential
            Credential object used to connect to the computer as a different user.

        .PARAMETER Unit
            This parameter has been deprecated and will be removed in 1.0.0
            All properties previously generated through this command are present at the same time, but hidden by default.

        .PARAMETER CheckForSql
            If this switch is enabled, disks will be checked for SQL Server data and log files. Windows Authentication is always used for this.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER ExcludeDrive
            Filter out drives - format is C:\

        .PARAMETER Detailed
            Output all properties, will be deprecated in 1.0.0 release. Use Force Instead

        .PARAMETER CheckFragmentation
            If this switch is enabled, fragmentation of all filesystems will be checked.

            This will increase the runtime of the function by seconds or even minutes per volume.

        .PARAMETER Force
            Enabling this switch will cause the command to include ALL drives.
            By default, only local disks and removable disks are shown, and hidden volumes are excluded.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .NOTES
            Tags: Storage, Disk
            Author: Chrissy LeMaire ([email protected]) & Jakob Bindslet ([email protected])

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaDiskSpace

        .EXAMPLE
            Get-DbaDiskSpace -ComputerName srv0042

            Get disk space for the server srv0042.

        .EXAMPLE
            Get-DbaDiskSpace -ComputerName srv0042 -Unit MB

            Get disk space for the server srv0042 and displays in megabytes (MB).

        .EXAMPLE
            Get-DbaDiskSpace -ComputerName srv0042, srv0007 -Unit TB

            Get disk space from two servers and displays in terabytes (TB).

        .EXAMPLE
            Get-DbaDiskSpace -ComputerName srv0042 -Force

            Get all disk and volume space information.

        .EXAMPLE
            Get-DbaDiskSpace -ComputerName srv0042 -ExcludeDrive 'C:\'

            Get all disk and volume space information.
    #>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Alias('ServerInstance', 'SqlInstance', 'SqlServer')]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [ValidateSet('Bytes', 'KB', 'MB', 'GB', 'TB', 'PB')]
        [string]$Unit = 'GB',
        [switch]$CheckForSql,
        [PSCredential]$SqlCredential,
        [string[]]$ExcludeDrive,
        [Alias('Detailed', 'AllDrives')]
        [switch]$CheckFragmentation,
        [switch]$Force,
        [switch][Alias('Silent')]
        $EnableException
    )

    begin {
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter Detailed
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter AllDrives
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter Unit

        $condition = " WHERE DriveType = 2 OR DriveType = 3"
        if (Test-Bound 'Force') {
            $condition = ""
        }

        # Keep track of what computer was already processed to avoid duplicates
        $processed = New-Object System.Collections.ArrayList

        <# In order to support properly identifying if a disk/volume is involved with ANY instance on a given computer #>
        $sqlDisks = New-Object System.Collections.ArrayList
    }

    process {
        foreach ($computer in $ComputerName) {
            if ($computer.ComputerName -notin $processed) {
                $null = $processed.Add($computer.ComputerName)
                Write-Message -Level VeryVerbose -Message "Connecting to $computer." -Target $computer.ComputerName
            }
            else {
                continue
            }

            try {
                $disks = Get-DbaCmObject -ComputerName $computer.ComputerName -Query "SELECT * FROM Win32_Volume$condition" -Credential $Credential -Namespace root\CIMv2 -ErrorAction Stop -WarningAction SilentlyContinue -EnableException
            }
            catch {
                Stop-Function -Message "Failed to connect to $computer." -EnableException $EnableException -ErrorRecord $_ -Target $computer.ComputerName -Continue
            }

            if ($CheckForSql) {
                try {
                    $sqlServices = Get-DbaSqlService -ComputerName $computer -Type Engine
                }
                catch {
                    Write-Message -Level Warning -Message "Failed to connect to $computer to gather SQL Server instances, will not be reporting SQL Information." -ErrorRecord $_ -OverrideExceptionMessage -Target $computer.ComputerName
                }

                Write-Message -Level Verbose -Message "Instances found on $($computer): $($sqlServices.InstanceName.Count)"
                if ($sqlServices.InstanceName.Count -gt 0) {
                    foreach ($sqlService in $sqlServices) {
                        if ($sqlService.InstanceName -eq "MSSQLSERVER") {
                            $instanceName = $sqlService.ComputerName
                        }
                        else {
                            $instanceName = "$($sqlService.ComputerName)\$($sqlService.InstanceName)"
                        }
                        Write-Message -Level VeryVerbose -Message "Processing instance $($instanceName)"
                        try {
                            $server = Connect-SqlInstance -SqlInstance $instanceName -SqlCredential $SqlCredential
                            if ($server.Version.Major -lt 9) {
                                $sql = "SELECT DISTINCT SUBSTRING(physical_name, 1, LEN(physical_name) - CHARINDEX('\', REVERSE(physical_name)) + 1) AS SqlDisk FROM sysaltfiles"
                            }
                            else {
                                $sql = "SELECT DISTINCT SUBSTRING(physical_name, 1, LEN(physical_name) - CHARINDEX('\', REVERSE(physical_name)) + 1) AS SqlDisk FROM sys.master_files"
                            }
                            $results = $server.Query($sql)
                            if ($results.SqlDisk.Count -gt 0) {
                                foreach ($sqlDisk in $results.SqlDisk) {
                                    if (-not $sqlDisks.Contains($sqlDisk)) {
                                        $null = $sqlDisks.Add($sqlDisk)
                                    }
                                }
                            }
                        }
                        catch {
                            Write-Message -Level Warning -Message "Failed to connect to $instanceName on $computer. SQL information may not be accurate or services have been stopped." -ErrorRecord $_ -OverrideExceptionMessage -Target $computer.ComputerName
                        }
                    }
                }
            }

            foreach ($disk in $disks) {
                if ($disk.Name -in $ExcludeDrive) {
                    continue
                }
                if ($disk.Name.StartsWith('\\') -and (-not $Force)) {
                    Write-Message -Level Verbose -Message "Skipping disk: $($disk.Name)" -Target $computer.ComputerName
                    continue
                }

                Write-Message -Level Verbose -Message "Processing disk: $($disk.Name)" -Target $computer.ComputerName

                $info = New-Object Sqlcollaborative.Dbatools.Computer.DiskSpace
                $info.ComputerName = $computer.ComputerName
                $info.Name = $disk.Name
                $info.Label = $disk.Label
                $info.Capacity = $disk.Capacity
                $info.Free = $disk.Freespace
                $info.BlockSize = $disk.BlockSize
                $info.FileSystem = $disk.FileSystem
                $info.Type = $disk.DriveType

                if ($CheckForSql) {
                    $drivePath = $disk.Name
                    $info.IsSqlDisk = $false
                    foreach ($sqlDisk in $sqlDisks) {
                        if ($sqlDisk -like ($drivePath + '*')) {
                            $info.IsSqlDisk = $true
                            break
                        }
                    }
                }
                $info
            }
        }
    }
}
function Get-DbaDistributor {
    <#
    .SYNOPSIS
        Gets the information about a replication distributor for a given SQL Server instance.

    .DESCRIPTION
        This function locates and enumerates distributor information for a given SQL Server instance.

    .PARAMETER SqlInstance
        Allows you to specify a comma separated list of servers to query.

    .PARAMETER SqlCredential
        Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Author: William Durkin, @sql_williamd
        Tags: Replication
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Get-DbaDistributor

    .EXAMPLE
        Get-DbaDistributor -SqlInstance sql2008, sqlserver2012
        Retrieve distributor information for servers sql2008 and sqlserver2012.
    #>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [parameter(Position = 1)]
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        if ($null -eq [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.RMO")) {
            Stop-Function -Message "Replication management objects not available. Please install SQL Server Management Studio."
        }
    }

    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            # connect to the instance
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            Write-Message -Level Verbose -Message "Attempting to retrieve distributor information from $instance"

            # Connect to the distributor of the instance
            try {
                $sourceSqlConn = $server.ConnectionContext.SqlConnectionObject
                $distributor = New-Object Microsoft.SqlServer.Replication.ReplicationServer $sourceSqlConn
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            Add-Member -Force -InputObject $distributor -MemberType NoteProperty -Name ComputerName -Value $server.ComputerName
            Add-Member -Force -InputObject $distributor -MemberType NoteProperty -Name InstanceName -Value $server.ServiceName
            Add-Member -Force -InputObject $distributor -MemberType NoteProperty -Name SqlInstance -Value $server.DomainInstanceName

            Select-DefaultView -InputObject $distributor -Property ComputerName, InstanceName, SqlInstance, IsPublisher, IsDistributor, DistributionServer, DistributionDatabase, DistributorInstalled, DistributorAvailable, HasRemotePublisher
        }
    }
}
function Get-DbaDump {
    <#
        .SYNOPSIS
            Locate a SQL Server that has generated any memory dump files.

        .DESCRIPTION
            The type of dump included in the search include minidump, all-thread dump, or a full dump.  The files have an extendion of .mdmp.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Engine, Corruption
            Author: Garry Bargsley (@gbargsley), http://blog.garrybargsley.com

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaDump

        .EXAMPLE
            Get-DbaDump -SqlInstance sql2016

            Shows the detailed information for memory dump(s) located on sql2016 instance


        .EXAMPLE
            Get-DbaDump -SqlInstance sql2016 -SqlCredential (Get-Credential sqladmin)

            Shows the detailed information for memory dump(s) located on sql2016 instance. Logs into the SQL Server using the SQL login 'sqladmin'

    #>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [switch]$EnableException
    )
    begin {
        $sql = "SELECT filename,  creation_time,  size_in_bytes FROM sys.dm_server_memory_dumps"
    }

    process {
        foreach ($instance in $SqlInstance) {
            $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential

            if ($server.versionMajor -lt 11 -and (-not ($server.versionMajor -eq 10 -and $server.versionMinor -eq 50))) {
                Stop-Function -Message "This function does not support versions lower than SQL Server 2008 R2 (v10.50). Skipping server '$instance'" -Continue
            }

            try {
                foreach ($result in $server.Query($sql)) {
                    [PSCustomObject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        FileName     = $result.filename
                        CreationTime = $result.creation_time
                        Size         = [dbasize]$result.size_in_bytes
                    }
                }
            }
            catch {
                Stop-Function -Message "Issue collecting data on $server" -Target $server -ErrorRecord $_ -Continue
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,CodeStyle#
function Get-DbaEndpoint {
    <#
        .SYNOPSIS
            Gets SQL Endpoint(s) information for each instance(s) of SQL Server.

        .DESCRIPTION
            The Get-DbaEndpoint command gets SQL Endpoint(s) information for each instance(s) of SQL Server.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function
            to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Endpoint
            Author: Garry Bargsley (@gbargsley), http://blog.garrybargsley.com

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaEndpoint

        .EXAMPLE
            Get-DbaEndpoint -SqlInstance localhost

            Returns all Endpoint(s) on the local default SQL Server instance

        .EXAMPLE
            Get-DbaEndpoint -SqlInstance localhost, sql2016

            Returns all Endpoint(s) for the local and sql2016 SQL Server instances
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            foreach ($endpoint in $server.Endpoints) {
                Add-Member -Force -InputObject $endpoint -MemberType NoteProperty -Name ComputerName -value $endpoint.Parent.ComputerName
                Add-Member -Force -InputObject $endpoint -MemberType NoteProperty -Name InstanceName -value $endpoint.Parent.ServiceName
                Add-Member -Force -InputObject $endpoint -MemberType NoteProperty -Name SqlInstance -value $endpoint.Parent.DomainInstanceName

                Select-DefaultView -InputObject $endpoint -Property ComputerName, InstanceName, SqlInstance, ID, Name, EndpointType, Owner, IsAdminEndpoint, IsSystemObject
            }
        }
    }
}
function Get-DbaErrorLog {
    <#
        .SYNOPSIS
            Gets the "SQL Error Log" of an instance

        .DESCRIPTION
            Gets the "SQL Error Log" of an instance. Returns all 10 error logs by default.

        .PARAMETER SqlInstance
            The SQL Server instance, or instances.

        .PARAMETER SqlCredential
            Allows you to login to servers using SQL Logins as opposed to Windows Auth/Integrated/Trusted.

        .PARAMETER LogNumber
            An Int32 value that specifies the index number of the error log required.
            Error logs are listed 0 through 99, where 0 is the current error log and 99 is potential oldest log file.

            SQL Server errorlog rollover defaults to 6, but can be increased to 99. https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/scm-services-configure-sql-server-error-logs

        .PARAMETER Source
            Filter results based on the Source of the error (e.g. Logon, Server, etc.)

        .PARAMETER Text
            Filter results based on a pattern of text (e.g. "login failed", "error: 12345").

        .PARAMETER After
            Filter the results based on datetime value.

        .PARAMETER Before
            Filter the results based on datetime value.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Instance, ErrorLog
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaErrorLog

        .EXAMPLE
            Get-DbaErrorLog -SqlInstance sql01\sharepoint

            Returns every log entry from sql01\sharepoint SQL Server instance.

        .EXAMPLE
            Get-DbaErrorLog -SqlInstance sql01\sharepoint -LogNumber 3, 6

            Returns all log entries for log number 3 and 6 on sql01\sharepoint SQL Server instance.

        .EXAMPLE
            Get-DbaErrorLog -SqlInstance sql01\sharepoint -Source Logon

            Returns every log entry, with a source of Logon, from sql01\sharepoint SQL Server instance.

        .EXAMPLE
            Get-DbaErrorLog -SqlInstance sql01\sharepoint -LogNumber 3 -Text "login failed"

            Returns every log entry for log number 3, with "login failed" in the text, from sql01\sharepoint SQL Server instance.

        .EXAMPLE
            $servers = "sql2014","sql2016", "sqlcluster\sharepoint"
            $servers | Get-DbaErrorLog -LogNumber 0

            Returns the most recent SQL Server error logs for "sql2014","sql2016" and "sqlcluster\sharepoint"

        .EXAMPLE
            Get-DbaErrorLog -SqlInstance sql01\sharepoint -After '11/14/2006 00:00'

            Returns every log entry found after the date 11/14/2006 00:00 from sql101\sharepoint SQL Server instance.

        .EXAMPLE
            Get-DbaErrorLog -SqlInstance sql01\sharepoint -Before '08/16/2016 00:00'

            Returns every log entry found before the date 08/16/2016 00:00 from sql101\sharepoint SQL Server instance.
        #>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [ValidateRange(0, 99)]
        [int[]]$LogNumber,
        [object[]]$Source,
        [string]$Text,
        [datetime]$After,
        [datetime]$Before,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Get-DbaSqlLog
    }
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($LogNumber) {
                foreach ($number in $lognumber) {
                    foreach ($object in $server.ReadErrorLog($number)) {
                        if ( ($Source -and $object.ProcessInfo -ne $Source) -or ($Text -and $object.Text -notlike "*$Text*") -or ($After -and $object.LogDate -lt $After) -or ($Before -and $object.LogDate -gt $Before) ) {
                            continue
                        }
                        Write-Message -Level Verbose -Message "Processing $object"
                        Add-Member -Force -InputObject $object -MemberType NoteProperty ComputerName -value $server.ComputerName
                        Add-Member -Force -InputObject $object -MemberType NoteProperty InstanceName -value $server.ServiceName
                        Add-Member -Force -InputObject $object -MemberType NoteProperty SqlInstance -value $server.DomainInstanceName

                        # Select all of the columns you'd like to show
                        Select-DefaultView -InputObject $object -Property ComputerName, InstanceName, SqlInstance, LogDate, 'ProcessInfo as Source', Text
                    }
                }
            }
            else {
                foreach ($object in $server.ReadErrorLog()) {
                    if ( ($Source -and $object.ProcessInfo -ne $Source) -or ($Text -and $object.Text -notlike "*$Text*") -or ($After -and $object.LogDate -lt $After) -or ($Before -and $object.LogDate -gt $Before) ) {
                        continue
                    }
                    Write-Message -Level Verbose -Message "Processing $object"
                    Add-Member -Force -InputObject $object -MemberType NoteProperty ComputerName -value $server.ComputerName
                    Add-Member -Force -InputObject $object -MemberType NoteProperty InstanceName -value $server.ServiceName
                    Add-Member -Force -InputObject $object -MemberType NoteProperty SqlInstance -value $server.DomainInstanceName

                    # Select all of the columns you'd like to show
                    Select-DefaultView -InputObject $object -Property ComputerName, InstanceName, SqlInstance, LogDate, 'ProcessInfo as Source', Text
                }
            }
        }
    }
}
function Get-DbaErrorLogConfig {
    <#
        .SYNOPSIS
            Pulls the configuration for the ErrorLog on a given SQL Server instance
    
        .DESCRIPTION
            Pulls the configuration for the ErrorLog on a given SQL Server instance.

            Includes error log path, number of log files configured and size (SQL Server 2012+ only)

        .PARAMETER SqlInstance
            The target SQL Server instance(s)

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Instance, ErrorLog
            Author: Shawn Melton (@wsmelton)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaErrorLogConfig

       .EXAMPLE
            Get-DbaErrorLogConfig -SqlInstance server2017,server2014

            Returns error log configuration for server2017 and server2014
    #>
    [cmdletbinding()]
    param (
        [Parameter(ValueFromPipeline, Mandatory)]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $numLogs = $server.NumberOfLogFiles
            $logSize =
            if ($server.VersionMajor -ge 11) {
                [dbasize]($server.ErrorLogSizeKb * 1024)
            }
            else {
                $null
            }

            [PSCustomObject]@{
                ComputerName       = $server.ComputerName
                InstanceName       = $server.ServiceName
                SqlInstance        = $server.DomainInstanceName
                LogCount           = $numLogs
                LogSize            = $logSize
                LogPath            = $server.ErrorLogPath
            }
        }
    }
}
function Get-DbaEstimatedCompletionTime {
    <#
.SYNOPSIS
Gets execution and estimated completion time information for queries

.DESCRIPTION
Gets execution and estimated completion time information for queries

Percent complete will show for the following commands

ALTER INDEX REORGANIZE
AUTO_SHRINK option with ALTER DATABASE
BACKUP DATABASE
DBCC CHECKDB
DBCC CHECKFILEGROUP
DBCC CHECKTABLE
DBCC INDEXDEFRAG
DBCC SHRINKDATABASE
DBCC SHRINKFILE
RECOVERY
RESTORE DATABASE
ROLLBACK
TDE ENCRYPTION

For additional information, check out https://blogs.sentryone.com/loriedwards/patience-dm-exec-requests/ and https://docs.microsoft.com/en-us/sql/relational-databases/system-dynamic-management-views/sys-dm-exec-requests-transact-sql

.PARAMETER SqlInstance
The SQL Server that you're connecting to.

.PARAMETER SqlCredential
SqlCredential object used to connect to the SQL Server as a different user.

.PARAMETER Database
The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

.PARAMETER ExcludeDatabase
The database(s) to exclude - this list is auto-populated from the server

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Database
Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Get-DbaEstimatedCompletionTime

.EXAMPLE
Get-DbaEstimatedCompletionTime -SqlInstance sql2016

Gets estimated completion times for queries performed against the entire server

.EXAMPLE
Get-DbaEstimatedCompletionTime -SqlInstance sql2016 | Select *

Gets estimated completion times for queries performed against the entire server PLUS the SQL query text of each command

.EXAMPLE
Get-DbaEstimatedCompletionTime -SqlInstance sql2016 | Where-Object { $_.Text -match 'somequerytext' }

Gets results for commands whose queries only match specific text (match is like LIKE but way more powerful)

.EXAMPLE
Get-DbaEstimatedCompletionTime -SqlInstance sql2016 -Database Northwind,pubs,Adventureworks2014

Gets estimated completion times for queries performed against the Northwind, pubs, and Adventureworks2014 databases

#>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $sql = "SELECT
                DB_NAME(r.database_id) as [Database],
                USER_NAME(r.user_id) as [Login],
                Command,
                start_time as StartTime,
                percent_complete as PercentComplete,

                  RIGHT('00000' + CAST(((DATEDIFF(s,start_time,GetDate()))/3600) as varchar),
                                CASE
                                    WHEN LEN(((DATEDIFF(s,start_time,GetDate()))/3600)) < 2 THEN 2
                                    ELSE LEN(((DATEDIFF(s,start_time,GetDate()))/3600))
                                 END)  + ':'
                + RIGHT('00' + CAST((DATEDIFF(s,start_time,GetDate())%3600)/60 as varchar), 2) + ':'
                + RIGHT('00' + CAST((DATEDIFF(s,start_time,GetDate())%60) as varchar), 2) as RunningTime,

                  RIGHT('00000' + CAST((estimated_completion_time/3600000) as varchar),
                        CASE
                                    WHEN LEN((estimated_completion_time/3600000)) < 2 THEN 2
                                    ELSE LEN((estimated_completion_time/3600000))
                         END)  + ':'
                + RIGHT('00' + CAST((estimated_completion_time %3600000)/60000 as varchar), 2) + ':'
                + RIGHT('00' + CAST((estimated_completion_time %60000)/1000 as varchar), 2) as EstimatedTimeToGo,
                dateadd(second,estimated_completion_time/1000, getdate()) as EstimatedCompletionTime,
                s.Text
             FROM sys.dm_exec_requests r
            CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) s"
    }

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential

            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($Database) {
                $includedatabases = $Database -join "','"
                $sql = "$sql WHERE DB_NAME(r.database_id) in ('$includedatabases')"
            }

            if ($ExcludeDatabase) {
                $excludedatabases = $ExcludeDatabase -join "','"
                $sql = "$sql WHERE DB_NAME(r.database_id) not in ('$excludedatabases')"
            }

            Write-Message -Level Debug -Message $sql
            foreach ($row in ($server.Query($sql))) {
                [pscustomobject]@{
                    ComputerName            = $server.ComputerName
                    InstanceName            = $server.ServiceName
                    SqlInstance             = $server.DomainInstanceName
                    Database                = $row.Database
                    Login                   = $row.Login
                    Command                 = $row.Command
                    PercentComplete         = $row.PercentComplete
                    StartTime               = $row.StartTime
                    RunningTime             = $row.RunningTime
                    EstimatedTimeToGo       = $row.EstimatedTimeToGo
                    EstimatedCompletionTime = $row.EstimatedCompletionTime
                    Text                    = $row.Text
                } | Select-DefaultView -ExcludeProperty Text
            }
        }
    }
}

function Get-DbaExecutionPlan {
    <#
.SYNOPSIS
Gets execution plans and metadata

.DESCRIPTION
Gets execution plans and metadata. Can pipe to Export-DbaExecutionPlan :D

Thanks to
    https://www.simple-talk.com/sql/t-sql-programming/dmvs-for-query-plan-metadata/
    and
    http://www.scarydba.com/2017/02/13/export-plans-cache-sqlplan-file/
for the idea and query.

.PARAMETER SqlInstance
The SQL Server that you're connecting to.

.PARAMETER SqlCredential
Credential object used to connect to the SQL Server as a different user

.PARAMETER Database
Return restore information for only specific databases. These are only the databases that currently exist on the server.

.PARAMETER ExcludeDatabase
Return restore information for all but these specific databases

.PARAMETER SinceCreation
Datetime object used to narrow the results to a date

.PARAMETER SinceLastExecution
Datetime object used to narrow the results to a date

.PARAMETER ExcludeEmptyQueryPlan
Exclude results with empty query plan

.PARAMETER Force
Returns a ton of raw information about the execution plans

.PARAMETER EnableException
    By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
    Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.


.NOTES
Tags: Performance
dbatools PowerShell module (https://dbatools.io, [email protected])
Copyright (C) 2016 Chrissy LeMaire
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Get-DbaExecutionPlan

.EXAMPLE
Get-DbaExecutionPlan -SqlInstance sqlserver2014a

Gets all execution plans on  sqlserver2014a

.EXAMPLE
Get-DbaExecutionPlan -SqlInstance sqlserver2014a -Database db1, db2 -SinceLastExecution '7/1/2016 10:47:00'

Gets all execution plans for databases db1 and db2 on sqlserver2014a since July 1, 2016 at 10:47 AM.

.EXAMPLE
Get-DbaExecutionPlan -SqlInstance sqlserver2014a, sql2016 -Exclude db1 | Format-Table

Gets execution plan info for all databases except db1 on sqlserver2014a and sql2016 and makes the output pretty

.EXAMPLE
Get-DbaExecutionPlan -SqlInstance sql2014 -Database AdventureWorks2014, pubs -Force

Gets super detailed information for execution plans on only for AdventureWorks2014 and pubs

#>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [datetime]$SinceCreation,
        [datetime]$SinceLastExecution,
        [switch]$ExcludeEmptyQueryPlan,
        [switch]$Force,
        [switch]$EnableException
    )

    begin {

        if ($SinceCreation -ne $null) {
            $SinceCreation = $SinceCreation.ToString("yyyy-MM-dd HH:mm:ss")
        }

        if ($SinceLastExecution -ne $null) {
            $SinceLastExecution = $SinceLastExecution.ToString("yyyy-MM-dd HH:mm:ss")
        }
    }
    process {

        foreach ($instance in $sqlinstance) {
            try {
                try {
                    Write-Message -Level Verbose -Message "Connecting to $instance."
                    $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential -MinimumVersion 9
                }
                catch {
                    Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                }

                if ($force -eq $true) {
                    $select = "SELECT * "
                }
                else {
                    $select = "SELECT DB_NAME(deqp.dbid) as DatabaseName, OBJECT_NAME(deqp.objectid) as ObjectName,
                    detqp.query_plan AS SingleStatementPlan,
                    deqp.query_plan AS BatchQueryPlan,
                    ROW_NUMBER() OVER ( ORDER BY Statement_Start_offset ) AS QueryPosition,
                    sql_handle as SqlHandle,
                    plan_handle as PlanHandle,
                    creation_time as CreationTime,
                    last_execution_time as LastExecutionTime"
                }

                $from = " FROM sys.dm_exec_query_stats deqs
                        CROSS APPLY sys.dm_exec_text_query_plan(deqs.plan_handle,
                            deqs.statement_start_offset,
                            deqs.statement_end_offset) AS detqp
                        CROSS APPLY sys.dm_exec_query_plan(deqs.plan_handle) AS deqp
                        CROSS APPLY sys.dm_exec_sql_text(deqs.plan_handle) AS execText"

                if ($ExcludeDatabase -or $Database -or $SinceCreation.length -gt 0 -or $SinceLastExecution.length -gt 0 -or $ExcludeEmptyQueryPlan -eq $true) {
                    $where = " WHERE "
                }

                $wherearray = @()

                if ($Database) {
                    $dblist = $Database -join "','"
                    $wherearray += " DB_NAME(deqp.dbid) in ('$dblist') "
                }

                if ($null -ne $SinceCreation) {
                    Write-Message -Level Verbose -Message "Adding creation time"
                    $wherearray += " creation_time >= '$SinceCreation' "
                }

                if ($null -ne $SinceLastExecution) {
                    Write-Message -Level Verbose -Message "Adding last exectuion time"
                    $wherearray += " last_execution_time >= '$SinceLastExecution' "
                }

                if ($ExcludeDatabase) {
                    $dblist = $ExcludeDatabase -join "','"
                    $wherearray += " DB_NAME(deqp.dbid) not in ('$dblist') "
                }

                if ($ExcludeEmptyQueryPlan) {
                    $wherearray += " detqp.query_plan is not null"
                }

                if ($where.length -gt 0) {
                    $wherearray = $wherearray -join " and "
                    $where = "$where $wherearray"
                }

                $sql = "$select $from $where"
                Write-Message -Level Debug -Message $sql

                if ($Force -eq $true) {
                    $server.Query($sql)
                }
                else {
                    foreach ($row in $server.Query($sql)) {
                        $simple = ([xml]$row.SingleStatementPlan).ShowPlanXML.BatchSequence.Batch.Statements.StmtSimple
                        $sqlhandle = "0x"; $row.sqlhandle | ForEach-Object { $sqlhandle += ("{0:X}" -f $_).PadLeft(2, "0") }
                        $planhandle = "0x"; $row.planhandle | ForEach-Object { $planhandle += ("{0:X}" -f $_).PadLeft(2, "0") }
                        $planWarnings = $simple.QueryPlan.Warnings.PlanAffectingConvert;

                        [pscustomobject]@{
                            ComputerName                      = $server.ComputerName
                            InstanceName                      = $server.ServiceName
                            SqlInstance                       = $server.DomainInstanceName
                            DatabaseName                      = $row.DatabaseName
                            ObjectName                        = $row.ObjectName
                            QueryPosition                     = $row.QueryPosition
                            SqlHandle                         = $SqlHandle
                            PlanHandle                        = $PlanHandle
                            CreationTime                      = $row.CreationTime
                            LastExecutionTime                 = $row.LastExecutionTime
                            StatementCondition                = ([xml]$row.SingleStatementPlan).ShowPlanXML.BatchSequence.Batch.Statements.StmtCond
                            StatementSimple                   = $simple
                            StatementId                       = $simple.StatementId
                            StatementCompId                   = $simple.StatementCompId
                            StatementType                     = $simple.StatementType
                            RetrievedFromCache                = $simple.RetrievedFromCache
                            StatementSubTreeCost              = $simple.StatementSubTreeCost
                            StatementEstRows                  = $simple.StatementEstRows
                            SecurityPolicyApplied             = $simple.SecurityPolicyApplied
                            StatementOptmLevel                = $simple.StatementOptmLevel
                            QueryHash                         = $simple.QueryHash
                            QueryPlanHash                     = $simple.QueryPlanHash
                            StatementOptmEarlyAbortReason     = $simple.StatementOptmEarlyAbortReason
                            CardinalityEstimationModelVersion = $simple.CardinalityEstimationModelVersion

                            ParameterizedText                 = $simple.ParameterizedText
                            StatementSetOptions               = $simple.StatementSetOptions
                            QueryPlan                         = $simple.QueryPlan
                            BatchConditionXml                 = ([xml]$row.BatchQueryPlan).ShowPlanXML.BatchSequence.Batch.Statements.StmtCond
                            BatchSimpleXml                    = ([xml]$row.BatchQueryPlan).ShowPlanXML.BatchSequence.Batch.Statements.StmtSimple
                            BatchQueryPlanRaw                 = [xml]$row.BatchQueryPlan
                            SingleStatementPlanRaw            = [xml]$row.SingleStatementPlan
                            PlanWarnings                      = $planWarnings
                        } | Select-DefaultView -ExcludeProperty BatchQueryPlan, SingleStatementPlan, BatchConditionXmlRaw, BatchQueryPlanRaw, SingleStatementPlanRaw, PlanWarnings
                    }
                }
            }
            catch {
                Stop-Function -Message "Query Failure Failure" -ErrorRecord $_ -Target $instance -Continue
            }
        }
    }
}
function Get-DbaFile {
    <#
.SYNOPSIS
Get-DbaFile finds files in any directory specified on a remote SQL Server

.DESCRIPTION
This command searches all specified directories, allowing a DBA to see file information on a server without direct access

You can filter by extension using the -FileType parameter. By default, the default data directory will be returned. You can provide and additional paths to search using the -Path parameter.

Thanks to serg-52 for the query:  https://www.sqlservercentral.com/Forums/Topic1642213-391-1.aspx

.PARAMETER SqlInstance
The SQL Server instance.

.PARAMETER SqlCredential
Allows you to login to servers using alternative credentials

.PARAMETER Path
Used to specify extra directories to search in addition to the default data directory.

.PARAMETER FileType
Used to specify filter by filetype. No dot required, just pass the extension.

.PARAMETER Depth
Used to specify recursive folder depth.  Default is 1, non-recursive.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Discovery
Author: Brandon Abshire, netnerds.net

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Get-DbaFile

.EXAMPLE
Get-DbaFile -SqlInstance sqlserver2014a -Path E:\Dir1
Logs into the SQL Server "sqlserver2014a" using Windows credentials and searches E:\Dir for all files

.EXAMPLE
Get-DbaFile -SqlInstance sqlserver2014a -SqlCredential $cred -Path 'E:\sql files'
Logs into the SQL Server "sqlserver2014a" using alternative credentials and returns all files in 'E:\sql files'

.EXAMPLE
$all = Get-DbaDefaultPath -SqlInstance sql2014
Get-DbaFile -SqlInstance sql2014 -Path $all.Data, $all.Log, $all.Backup -Depth 3
Returns the files in the default data, log and backup directories on sql2014, 3 directories deep (recursively).

.EXAMPLE
Get-DbaFile -SqlInstance sql2014 -Path 'E:\Dir1', 'E:\Dir2'
Returns the files in "E:\Dir1" and "E:Dir2" on sql2014

.EXAMPLE
Get-DbaFile -SqlInstance -Path 'E:\Dir1' sql2014, sql2016 -FileType fsf, mld
Finds files in E:\Dir1 ending with ".fsf" and ".mld" for both the servers sql2014 and sql2016.

.EXAMPLE
Get-DbaFile -SqlInstance -Path 'E:\Dir1' sql2014, sql2016 -FileType fsf, mld
Finds files in E:\Dir1 ending with ".fsf" and ".mld" for both the servers sql2014 and sql2016.
#>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$Path,
        [string[]]$FileType,
        [int]$Depth = 1,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        $sql = ""

        function Get-SQLDirTreeQuery {
            param
            (
                $PathList
            )

            $q1 += "DECLARE @myPath nvarchar(4000);
                    DECLARE @depth SMALLINT = $Depth;

                    IF OBJECT_ID('tempdb..#DirectoryTree') IS NOT NULL
                    DROP TABLE #DirectoryTree;

                    CREATE TABLE #DirectoryTree (
                       id int IDENTITY(1,1)
                       ,subdirectory nvarchar(512)
                       ,depth int
                       ,isfile bit
                       , ParentDirectory int
                       ,flag tinyint default(0));"

            $q2 = "SET @myPath = 'dirname'
                    -- top level directory
                    INSERT #DirectoryTree (subdirectory,depth,isfile)
                       VALUES (@myPath,0,0);
                    -- all the rest under top level
                    INSERT #DirectoryTree (subdirectory,depth,isfile)
                       EXEC master.sys.xp_dirtree @myPath,@depth,1;


                    UPDATE #DirectoryTree
                       SET ParentDirectory = (
                          SELECT MAX(Id) FROM #DirectoryTree
                          WHERE Depth = d.Depth - 1 AND Id < d.Id   )
                    FROM #DirectoryTree d
                    WHERE ParentDirectory is NULL;"

            $query_files_sql = "-- SEE all with full paths
                    WITH dirs AS (
                        SELECT
                           Id,subdirectory,depth,isfile,ParentDirectory,flag
                           , CAST (null AS NVARCHAR(MAX)) AS container
                           , CAST([subdirectory] AS NVARCHAR(MAX)) AS dpath
                           FROM #DirectoryTree
                           WHERE ParentDirectory IS NULL
                        UNION ALL
                        SELECT
                           d.Id,d.subdirectory,d.depth,d.isfile,d.ParentDirectory,d.flag
                           , dpath as container
                           , dpath +'\'+d.[subdirectory]
                        FROM #DirectoryTree AS d
                        INNER JOIN dirs ON  d.ParentDirectory = dirs.id
                        WHERE dpath NOT LIKE '%RECYCLE.BIN%'
                    )
                    SELECT subdirectory as filename, container as filepath, isfile, dpath as fullpath FROM dirs
                    WHERE container IS NOT NULL
                    -- Dir style ordering
                    ORDER BY container, isfile, subdirectory"

            # build the query string based on how many directories they want to enumerate
            $sql = $q1
            $sql += $($PathList | Where-Object { $_ -ne '' } | ForEach-Object { "$([System.Environment]::Newline)$($q2 -Replace 'dirname', $_)" })
            $sql += $query_files_sql
            #Write-Message -Level Debug -Message $sql
            return $sql
        }

        function Format-Path {
            param ($path)
            $path = $path.Trim()
            #Thank you windows 2000
            $path = $path -replace '[^A-Za-z0-9 _\.\-\\:]', '__'
            return $path
        }

        if ($FileType) {
            $FileTypeComparison = $FileType | ForEach-Object { $_.ToLower() } | Where-Object { $_ } | Sort-Object | Get-Unique
        }
    }

    process {
        foreach ($instance in $SqlInstance) {

            $paths = @()
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            # Get the default data and log directories from the instance
            if (-not (Test-Bound -ParameterName Path)) { $Path = (Get-DbaDefaultPath -SqlInstance $server).Data }

            Write-Message -Level Verbose -Message "Adding paths"
            $sql = Get-SQLDirTreeQuery $Path
            Write-Message -Level Debug -Message $sql

            # This should remain as not .Query() to be compat with a PSProvider Chrissy is working on
            $datatable = $server.ConnectionContext.ExecuteWithResults($sql).Tables.Rows

            Write-Message -Level Verbose -Message "$($datatable.Rows.Count) files found."
            if ($FileTypeComparison) {
                foreach ($row in $datatable) {
                    foreach ($type in $FileTypeComparison) {
                        if ($row.filename.ToLower().EndsWith(".$type")) {
                            [pscustomobject]@{
                                ComputerName   = $server.ComputerName
                                InstanceName   = $server.ServiceName
                                SqlInstance    = $server.DomainInstanceName
                                Filename       = $row.fullpath
                                RemoteFilename = Join-AdminUnc -Servername $server.ComputerName -Filepath $row.fullpath
                            } | Select-DefaultView -ExcludeProperty ComputerName, InstanceName, RemoteFilename
                        }
                    }
                }
            }
            else {
                foreach ($row in $datatable) {
                    [pscustomobject]@{
                        ComputerName   = $server.ComputerName
                        InstanceName   = $server.ServiceName
                        SqlInstance    = $server.DomainInstanceName
                        Filename       = $row.fullpath
                        RemoteFilename = Join-AdminUnc -Servername $server.ComputerName -Filepath $row.fullpath
                    } | Select-DefaultView -ExcludeProperty ComputerName, InstanceName, RemoteFilename
                }
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#

function Get-DbaForceNetworkEncryption {
    <#
    .SYNOPSIS
        Gets Force Encryption settings for a SQL Server instance

    .DESCRIPTION
        Gets Force Encryption settings for a SQL Server instance. Note that this requires access to the Windows Server - not the SQL instance itself.

        This setting is found in Configuration Manager.

    .PARAMETER SqlInstance
        The target SQL Server - defaults to localhost.

    .PARAMETER Credential
        Allows you to login to the computer (not sql instance) using alternative Windows credentials

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .PARAMETER WhatIf
        Shows what would happen if the command were to run. No actions are actually performed

    .PARAMETER Confirm
        Prompts you for confirmation before executing any changing operations within the command

    .EXAMPLE
        Get-DbaForceNetworkEncryption

        Gets Force Encryption properties on the default (MSSQLSERVER) instance on localhost - requires (and checks for) RunAs admin.

    .EXAMPLE
        Get-DbaForceNetworkEncryption -SqlInstance sql01\SQL2008R2SP2

        Gets Force Network Encryption for the SQL2008R2SP2 on sql01. Uses Windows Credentials to both login and view the registry.

    .NOTES
        Tags: Certificate

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT
#>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer", "ComputerName")]
        [DbaInstanceParameter[]]
        $SqlInstance = $env:COMPUTERNAME,

        [PSCredential]

        $Credential,

        [switch]
        [Alias('Silent')]$EnableException
    )
    process {

        foreach ($instance in $SqlInstance) {
            Write-Message -Level VeryVerbose -Message "Processing $instance" -Target $instance
            $null = Test-ElevationRequirement -ComputerName $instance -Continue

            Write-Message -Level Verbose -Message "Resolving hostname"
            $resolved = $null
            $resolved = Resolve-DbaNetworkName -ComputerName $instance

            if ($null -eq $resolved) {
                Stop-Function -Message "Can't resolve $instance" -Target $instance -Continue -Category InvalidArgument
            }

            Write-Message -Level Verbose -Message "Connecting to SQL WMI on $($instance.ComputerName)"
            try {
                $sqlwmi = Invoke-ManagedComputerCommand -ComputerName $resolved.FullComputerName -ScriptBlock { $wmi.Services } -Credential $Credential -ErrorAction Stop | Where-Object DisplayName -eq "SQL Server ($($instance.InstanceName))"
            }
            catch {
                Stop-Function -Message "Failed to access $instance" -Target $instance -Continue -ErrorRecord $_
            }

            $regroot = ($sqlwmi.AdvancedProperties | Where-Object Name -eq REGROOT).Value
            $vsname = ($sqlwmi.AdvancedProperties | Where-Object Name -eq VSNAME).Value
            try {
                $instancename = $sqlwmi.DisplayName.Replace('SQL Server (', '').Replace(')', '') # Don't clown, I don't know regex :(
            }
            catch {
                # Probably because the instance name has been aliased or does not exist or samthin
            }
            $serviceaccount = $sqlwmi.ServiceAccount

            if ([System.String]::IsNullOrEmpty($regroot)) {
                $regroot = $sqlwmi.AdvancedProperties | Where-Object { $_ -match 'REGROOT' }
                $vsname = $sqlwmi.AdvancedProperties | Where-Object { $_ -match 'VSNAME' }

                if (![System.String]::IsNullOrEmpty($regroot)) {
                    $regroot = ($regroot -Split 'Value\=')[1]
                    $vsname = ($vsname -Split 'Value\=')[1]
                }
                else {
                    Stop-Function -Message "Can't find instance $vsname on $instance" -Continue -Category ObjectNotFound -Target $instance
                }
            }

            if ([System.String]::IsNullOrEmpty($vsname)) { $vsname = $instance }

            Write-Message -Level Verbose -Message "Regroot: $regroot" -Target $instance
            Write-Message -Level Verbose -Message "ServiceAcct: $serviceaccount" -Target $instance
            Write-Message -Level Verbose -Message "InstanceName: $instancename" -Target $instance
            Write-Message -Level Verbose -Message "VSNAME: $vsname" -Target $instance

            $scriptblock = {
                $regpath = "Registry::HKEY_LOCAL_MACHINE\$($args[0])\MSSQLServer\SuperSocketNetLib"
                $cert = (Get-ItemProperty -Path $regpath -Name Certificate).Certificate
                $forceencryption = (Get-ItemProperty -Path $regpath -Name ForceEncryption).ForceEncryption

                # [pscustomobject] doesn't always work, unsure why. so return hashtable then turn it into  pscustomobject on client
                @{
                    ComputerName          = $env:COMPUTERNAME
                    InstanceName          = $args[2]
                    SqlInstance           = $args[1]
                    ForceEncryption       = ($forceencryption -eq $true)
                    CertificateThumbprint = $cert
                }
            }

            if ($PScmdlet.ShouldProcess("local", "Connecting to $instance")) {
                try {
                    $results = Invoke-Command2 -ComputerName $resolved.FullComputerName -Credential $Credential -ArgumentList $regroot, $vsname, $instancename -ScriptBlock $scriptblock -ErrorAction Stop -Raw
                    foreach ($result in $results) {
                        [pscustomobject]$result
                    }
                }
                catch {
                    Stop-Function -Message "Failed to connect to $($resolved.FullComputerName) using PowerShell remoting!" -ErrorRecord $_ -Target $instance -Continue
                }
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaHelpIndex {
    <#
        .SYNOPSIS
            Returns size, row and configuration information for indexes in databases.

        .DESCRIPTION
            This function will return detailed information on indexes (and optionally statistics) for all indexes in a database, or a given index should one be passed along.
            As this uses SQL Server DMVs to access the data it will only work in 2005 and up (sorry folks still running SQL Server 2000).
            For performance reasons certain statistics information will not be returned from SQL Server 2005 if an ObjectName is not provided.

            The data includes:
                - ObjectName: the table containing the index
                - IndexType: clustered/non-clustered/columnstore and whether the index is unique/primary key
                - KeyColumns: the key columns of the index
                - IncludeColumns: any include columns in the index
                - FilterDefinition: any filter that may have been used in the index
                - DataCompression: row/page/none depending upon whether or not compression has been used
                - IndexReads: the number of reads of the index since last restart or index rebuild
                - IndexUpdates: the number of writes to the index since last restart or index rebuild
                - SizeKB: the size the index in KB
                - IndexRows: the number of the rows in the index (note filtered indexes will have fewer rows than exist in the table)
                - IndexLookups: the number of lookups that have been performed (only applicable for the heap or clustered index)
                - MostRecentlyUsed: when the index was most recently queried (default to 1900 for when never read)
                - StatsSampleRows: the number of rows queried when the statistics were built/rebuilt (not included in SQL Server 2005 unless ObjectName is specified)
                - StatsRowMods: the number of changes to the statistics since the last rebuild
                - HistogramSteps: the number of steps in the statistics histogram (not included in SQL Server 2005 unless ObjectName is specified)
                - StatsLastUpdated: when the statistics were last rebuilt (not included in SQL Server 2005 unless ObjectName is specified)

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process. This list is auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude. This list is auto-populated from the server.

        .PARAMETER ObjectName
            The name of a table for which you want to obtain the index information. If the two part naming convention for an object is not used it will use the default schema for the executing user. If not passed it will return data on all indexes in a given database.

        .PARAMETER IncludeStats
            If this switch is enabled, statistics as well as indexes will be returned in the output (statistics information such as the StatsRowMods will always be returned for indexes).

        .PARAMETER IncludeDataTypes
            If this switch is enabled, the output will include the data type of each column that makes up a part of the index definition (key and include columns).

        .PARAMETER IncludeFragmentation
            If this switch is enabled, the output will include fragmentation information.

        .PARAMETER InputObject
           Allows piping from Get-DbaDatabase
   
        .PARAMETER Raw
            If this switch is enabled, results may be less user-readable but more suitable for processing by other code.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Index
            Author: Nic Cain, https://sirsql.net/

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaHelpIndex

        .EXAMPLE
            Get-DbaHelpIndex -SqlInstance localhost -Database MyDB

            Returns information on all indexes on the MyDB database on the localhost.

        .EXAMPLE
            Get-DbaHelpIndex -SqlInstance localhost -Database MyDB,MyDB2

            Returns information on all indexes on the MyDB & MyDB2 databases.

        .EXAMPLE
            Get-DbaHelpIndex -SqlInstance localhost -Database MyDB -ObjectName dbo.Table1

            Returns index information on the object dbo.Table1 in the database MyDB.

        .EXAMPLE
            Get-DbaHelpIndex -SqlInstance localhost -Database MyDB -ObjectName dbo.Table1 -IncludeStats

            Returns information on the indexes and statistics for the table dbo.Table1 in the MyDB database.

        .EXAMPLE
            Get-DbaHelpIndex -SqlInstance localhost -Database MyDB -ObjectName dbo.Table1 -IncludeDataTypes

            Returns the index information for the table dbo.Table1 in the MyDB database, and includes the data types for the key and include columns.

        .EXAMPLE
            Get-DbaHelpIndex -SqlInstance localhost -Database MyDB -ObjectName dbo.Table1 -Raw

            Returns the index information for the table dbo.Table1 in the MyDB database, and returns the numerical data without localized separators.

        .EXAMPLE
            Get-DbaHelpIndex -SqlInstance localhost -Database MyDB -IncludeStats -Raw

            Returns the index information for all indexes in the MyDB database as well as their statistics, and formats the numerical data without localized separators.

        .EXAMPLE
            Get-DbaHelpIndex -SqlInstance localhost -Database MyDB -IncludeFragmentation

            Returns the index information for all indexes in the MyDB database as well as their fragmentation
    
        .EXAMPLE
            Get-DbaDatabase -SqlInstance sql2017 -Database MyDB | Get-DbaHelpIndex

            Returns the index information for all indexes in the MyDB database
  #>
    [CmdletBinding()]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Parameter(ValueFromPipeline)]
        [Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
        [string]$ObjectName,
        [switch]$IncludeStats,
        [switch]$IncludeDataTypes,
        [switch]$Raw,
        [switch]$IncludeFragmentation,
        [Alias('Silent')]
        [switch]$EnableException
    )
    
    begin {
        
        #Add the table predicate to the query
        if (!$ObjectName) {
            $TablePredicate = "DECLARE @TableName NVARCHAR(256);";
        }
        else {
            $TablePredicate = "DECLARE @TableName NVARCHAR(256); SET @TableName = '$ObjectName';";
        }
        
        #Add Fragmentation info if requested
        $FragSelectColumn = ", NULL as avg_fragmentation_in_percent"
        $FragJoin = ''
        $OutputProperties = 'DatabaseName,ObjectName,IndexName,IndexType,KeyColumns,IncludeColumns,FilterDefinition,DataCompression,IndexReads,IndexUpdates,SizeKB,IndexRows,IndexLookups,MostRecentlyUsed,StatsSampleRows,StatsRowMods,HistogramSteps,StatsLastUpdated'
        if ($IncludeFragmentation) {
            $FragSelectColumn = ', pstat.avg_fragmentation_in_percent'
            $FragJoin = "LEFT JOIN sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL , 'DETAILED') pstat
             ON pstat.database_id = ustat.database_id
             AND pstat.object_id = ustat.object_id
             AND pstat.index_id = ustat.index_id"
            $OutputProperties = 'DatabaseName,ObjectName,IndexName,IndexType,KeyColumns,IncludeColumns,FilterDefinition,DataCompression,IndexReads,IndexUpdates,SizeKB,IndexRows,IndexLookups,MostRecentlyUsed,StatsSampleRows,StatsRowMods,HistogramSteps,StatsLastUpdated,IndexFragInPercent'
        }
        $OutputProperties = $OutputProperties.Split(',')
        #Figure out if we are including stats in the results
        if ($IncludeStats) {
            $IncludeStatsPredicate = "";
        }
        else {
            $IncludeStatsPredicate = "WHERE IndexType != 'STATISTICS'";
        }
        
        #Data types being returns with the results?
        if ($IncludeDataTypes) {
            $IncludeDataTypesPredicate = 'DECLARE @IncludeDataTypes BIT; SET @IncludeDataTypes = 1';
        }
        else {
            $IncludeDataTypesPredicate = 'DECLARE @IncludeDataTypes BIT; SET @IncludeDataTypes = 0';
        }
        
        #region SizesQuery
        $SizesQuery = "
            SET NOCOUNT ON;
            SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

            $TablePredicate
            $IncludeDataTypesPredicate
            ;

        DECLARE @IndexUsageStats TABLE
            (
            object_id INT ,
            index_id INT ,
            user_scans BIGINT ,
            user_seeks BIGINT ,
            user_updates BIGINT ,
            user_lookups BIGINT ,
            last_user_lookup DATETIME2(0) ,
            last_user_scan DATETIME2(0) ,
            last_user_seek DATETIME2(0) ,
            avg_fragmentation_in_percent FLOAT
            );

        DECLARE @StatsInfo TABLE
            (
            object_id INT ,
            stats_id INT ,
            stats_column_name NVARCHAR(128) ,
            stats_column_id INT ,
            stats_name NVARCHAR(128) ,
            stats_last_updated DATETIME2(0) ,
            stats_sampled_rows BIGINT ,
            rowmods BIGINT ,
            histogramsteps INT ,
            StatsRows BIGINT ,
            FullObjectName NVARCHAR(256)
            );

        INSERT  INTO @IndexUsageStats
                ( object_id ,
                index_id ,
                user_scans ,
                user_seeks ,
                user_updates ,
                user_lookups ,
                last_user_lookup ,
                last_user_scan ,
                last_user_seek ,
                avg_fragmentation_in_percent
                )
                SELECT  ustat.object_id ,
                        ustat.index_id ,
                        ustat.user_scans ,
                        ustat.user_seeks ,
                        ustat.user_updates ,
                        ustat.user_lookups ,
                        ustat.last_user_lookup ,
                        ustat.last_user_scan ,
                        ustat.last_user_seek
                        $FragSelectColumn
                FROM    sys.dm_db_index_usage_stats ustat
                $FragJoin
                WHERE   ustat.database_id = DB_ID();

        INSERT  INTO @StatsInfo
                ( object_id ,
                stats_id ,
                stats_column_name ,
                stats_column_id ,
                stats_name ,
                stats_last_updated ,
                stats_sampled_rows ,
                rowmods ,
                histogramsteps ,
                StatsRows ,
                FullObjectName
                )
                SELECT  s.object_id ,
                        s.stats_id ,
                        c.name ,
                        sc.stats_column_id ,
                        s.name ,
                        sp.last_updated ,
                        sp.rows_sampled ,
                        sp.modification_counter ,
                        sp.steps ,
                        sp.rows ,
                        QUOTENAME(sch.name) + '.' + QUOTENAME(t.name) AS FullObjectName
                FROM    [sys].[stats] AS [s]
                        INNER JOIN sys.stats_columns sc ON s.stats_id = sc.stats_id
                                                        AND s.object_id = sc.object_id
                        INNER JOIN sys.columns c ON c.object_id = sc.object_id
                                                    AND c.column_id = sc.column_id
                        INNER JOIN sys.tables t ON c.object_id = t.object_id
                        INNER JOIN sys.schemas sch ON sch.schema_id = t.schema_id
                        OUTER APPLY sys.dm_db_stats_properties([s].[object_id],
                                                            [s].[stats_id]) AS [sp]
                WHERE   s.object_id = CASE WHEN @TableName IS NULL THEN s.object_id
                                        else OBJECT_ID(@TableName)
                                    END;


        ;
        WITH    cteStatsInfo
                AS ( SELECT   object_id ,
                                si.stats_id ,
                                si.stats_name ,
                                STUFF((SELECT   N', ' + stats_column_name
                                    FROM     @StatsInfo si2
                                    WHERE    si2.object_id = si.object_id
                                                AND si2.stats_id = si.stats_id
                                    ORDER BY si2.stats_column_id
                                FOR   XML PATH(N'') ,
                                        TYPE).value(N'.[1]', N'nvarchar(1000)'), 1,
                                    2, N'') AS StatsColumns ,
                                MAX(si.stats_sampled_rows) AS SampleRows ,
                                MAX(si.rowmods) AS RowMods ,
                                MAX(si.histogramsteps) AS HistogramSteps ,
                                MAX(si.stats_last_updated) AS StatsLastUpdated ,
                                MAX(si.StatsRows) AS StatsRows,
                                FullObjectName
                    FROM     @StatsInfo si
                    GROUP BY si.object_id ,
                                si.stats_id ,
                                si.stats_name ,
                                si.FullObjectName
                    ),
                cteIndexSizes
                AS ( SELECT   object_id ,
                                index_id ,
                                CASE WHEN index_id < 2
                                    THEN ( ( SUM(in_row_data_page_count
                                                + lob_used_page_count
                                                + row_overflow_used_page_count)
                                            * 8192 ) / 1024 )
                                    else ( ( SUM(used_page_count) * 8192 ) / 1024 )
                                END AS SizeKB
                    FROM     sys.dm_db_partition_stats
                    GROUP BY object_id ,
                                index_id
                    ),
                cteRows
                AS ( SELECT   object_id ,
                                index_id ,
                                SUM(rows) AS IndexRows
                    FROM     sys.partitions
                    GROUP BY object_id ,
                                index_id
                    ),
                cteIndex
                AS ( SELECT   OBJECT_NAME(c.object_id) AS ObjectName ,
                                c.object_id ,
                                c.index_id ,
                                i.name COLLATE SQL_Latin1_General_CP1_CI_AS AS name ,
                                c.index_column_id ,
                                c.column_id ,
                                c.is_included_column ,
                                CASE WHEN @IncludeDataTypes = 0
                                        AND c.is_descending_key = 1
                                    THEN sc.name + ' DESC'
                                    WHEN @IncludeDataTypes = 0
                                        AND c.is_descending_key = 0 THEN sc.name
                                    WHEN @IncludeDataTypes = 1
                                        AND c.is_descending_key = 1
                                        AND c.is_included_column = 0
                                    THEN sc.name + ' DESC (' + t.name + ') '
                                    WHEN @IncludeDataTypes = 1
                                        AND c.is_descending_key = 0
                                        AND c.is_included_column = 0
                                    THEN sc.name + ' (' + t.name + ')'
                                    else sc.name
                                END AS ColumnName ,
                                i.filter_definition ,
                                ISNULL(dd.user_scans, 0) AS user_scans ,
                                ISNULL(dd.user_seeks, 0) AS user_seeks ,
                                ISNULL(dd.user_updates, 0) AS user_updates ,
                                ISNULL(dd.user_lookups, 0) AS user_lookups ,
                                CONVERT(DATETIME2(0), ISNULL(dd.last_user_lookup,
                                                            '1901-01-01')) AS LastLookup ,
                                CONVERT(DATETIME2(0), ISNULL(dd.last_user_scan,
                                                            '1901-01-01')) AS LastScan ,
                                CONVERT(DATETIME2(0), ISNULL(dd.last_user_seek,
                                                            '1901-01-01')) AS LastSeek ,
                                i.fill_factor ,
                                c.is_descending_key ,
                                p.data_compression_desc ,
                                i.type_desc ,
                                i.is_unique ,
                                i.is_unique_constraint ,
                                i.is_primary_key ,
                                ci.SizeKB ,
                                cr.IndexRows ,
                                QUOTENAME(sch.name) + '.' + QUOTENAME(tbl.name) AS FullObjectName ,
                                ISNULL(dd.avg_fragmentation_in_percent, 0) as avg_fragmentation_in_percent
                    FROM     sys.indexes i
                                JOIN sys.index_columns c ON i.object_id = c.object_id
                                                            AND i.index_id = c.index_id
                                JOIN sys.columns sc ON c.object_id = sc.object_id
                                                    AND c.column_id = sc.column_id
                                INNER JOIN sys.tables tbl ON c.object_id = tbl.object_id
                                INNER JOIN sys.schemas sch ON sch.schema_id = tbl.schema_id
                                LEFT JOIN sys.types t ON sc.user_type_id = t.user_type_id
                                LEFT JOIN @IndexUsageStats dd ON i.object_id = dd.object_id
                                                                AND i.index_id = dd.index_id --and dd.database_id = db_id()
                                JOIN sys.partitions p ON i.object_id = p.object_id
                                                        AND i.index_id = p.index_id
                                JOIN cteIndexSizes ci ON i.object_id = ci.object_id
                                                        AND i.index_id = ci.index_id
                                JOIN cteRows cr ON i.object_id = cr.object_id
                                                AND i.index_id = cr.index_id
                    WHERE    i.object_id = CASE WHEN @TableName IS NULL
                                                THEN i.object_id
                                                else OBJECT_ID(@TableName)
                                            END
                    ),
                cteResults
                AS ( SELECT   ci.FullObjectName ,
                                ci.object_id ,
                                MAX(index_id) AS Index_Id ,
                                ci.type_desc
                                + CASE WHEN ci.is_primary_key = 1
                                    THEN ' (PRIMARY KEY)'
                                    WHEN ci.is_unique_constraint = 1
                                    THEN ' (UNIQUE CONSTRAINT)'
                                    WHEN ci.is_unique = 1 THEN ' (UNIQUE)'
                                    else ''
                                END AS IndexType ,
                                name AS IndexName ,
                                STUFF((SELECT   N', ' + ColumnName
                                    FROM     cteIndex ci2
                                    WHERE    ci2.name = ci.name
                                                AND ci2.is_included_column = 0
                                    GROUP BY ci2.index_column_id ,
                                                ci2.ColumnName
                                    ORDER BY ci2.index_column_id
                                FOR   XML PATH(N'') ,
                                        TYPE).value(N'.[1]', N'nvarchar(1000)'), 1,
                                    2, N'') AS KeyColumns ,
                                ISNULL(STUFF((SELECT    N',  ' + ColumnName
                                            FROM      cteIndex ci3
                                            WHERE     ci3.name = ci.name
                                                        AND ci3.is_included_column = 1
                                            GROUP BY  ci3.index_column_id ,
                                                        ci3.ColumnName
                                            ORDER BY  ci3.index_column_id
                                    FOR   XML PATH(N'') ,
                                                TYPE).value(N'.[1]',
                                                            N'nvarchar(1000)'), 1, 2,
                                            N''), '') AS IncludeColumns ,
                                ISNULL(filter_definition, '') AS FilterDefinition ,
                                ci.fill_factor ,
                                CASE WHEN ci.data_compression_desc = 'NONE' THEN ''
                                    else ci.data_compression_desc
                                END AS DataCompression ,
                                MAX(ci.user_seeks) + MAX(ci.user_scans)
                                + MAX(ci.user_lookups) AS IndexReads ,
                                MAX(ci.user_lookups) AS IndexLookups ,
                                ci.user_updates AS IndexUpdates ,
                                ci.SizeKB AS SizeKB ,
                                ci.IndexRows AS IndexRows ,
                                CASE WHEN LastScan > LastSeek
                                        AND LastScan > LastLookup THEN LastScan
                                    WHEN LastSeek > LastScan
                                        AND LastSeek > LastLookup THEN LastSeek
                                    WHEN LastLookup > LastScan
                                        AND LastLookup > LastSeek THEN LastLookup
                                    else ''
                                END AS MostRecentlyUsed ,
                                AVG(ci.avg_fragmentation_in_percent) as avg_fragmentation_in_percent
                    FROM     cteIndex ci
                    GROUP BY ci.ObjectName ,
                                ci.name ,
                                ci.filter_definition ,
                                ci.object_id ,
                                ci.LastLookup ,
                                ci.LastSeek ,
                                ci.LastScan ,
                                ci.user_updates ,
                                ci.fill_factor ,
                                ci.data_compression_desc ,
                                ci.type_desc ,
                                ci.is_primary_key ,
                                ci.is_unique ,
                                ci.is_unique_constraint ,
                                ci.SizeKB ,
                                ci.IndexRows ,
                                ci.FullObjectName
                    ),
                AllResults
                AS ( SELECT   c.FullObjectName ,
                                ISNULL(IndexType, 'STATISTICS') AS IndexType ,
                                ISNULL(IndexName, si.stats_name) AS IndexName ,
                                ISNULL(KeyColumns, si.StatsColumns) AS KeyColumns ,
                                ISNULL(IncludeColumns, '') AS IncludeColumns ,
                                FilterDefinition ,
                                fill_factor AS [FillFactor] ,
                                DataCompression ,
                                IndexReads ,
                                IndexUpdates ,
                                SizeKB ,
                                IndexRows ,
                                IndexLookups ,
                                MostRecentlyUsed ,
                                SampleRows AS StatsSampleRows ,
                                RowMods AS StatsRowMods ,
                                si.HistogramSteps ,
                                si.StatsLastUpdated ,
                                avg_fragmentation_in_percent AS IndexFragInPercent,
                                1 AS Ordering
                    FROM     cteResults c
                                INNER JOIN cteStatsInfo si ON si.object_id = c.object_id
                                                            AND si.stats_id = c.Index_Id
                    UNION
                    SELECT   QUOTENAME(sch.name) + '.' + QUOTENAME(tbl.name) AS FullObjectName ,
                                'STATISTICS' ,
                                stats_name ,
                                StatsColumns ,
                                '' ,
                                '' AS FilterDefinition ,
                                '' AS Fill_Factor ,
                                '' AS DataCompression ,
                                '' AS IndexReads ,
                                '' AS IndexUpdates ,
                                '' AS SizeKB ,
                                StatsRows AS IndexRows ,
                                '' AS IndexLookups ,
                                '' AS MostRecentlyUsed ,
                                SampleRows AS StatsSampleRows ,
                                RowMods AS StatsRowMods ,
                                csi.HistogramSteps ,
                                csi.StatsLastUpdated ,
                                '' AS IndexFragInPercent ,
                                2
                    FROM     cteStatsInfo csi
                    INNER JOIN sys.tables tbl ON csi.object_id = tbl.object_id
                                INNER JOIN sys.schemas sch ON sch.schema_id = tbl.schema_id
                    WHERE    stats_id NOT IN (
                                SELECT  stats_id
                                FROM    cteResults c
                                        INNER JOIN cteStatsInfo si ON si.object_id = c.object_id
                                                                    AND si.stats_id = c.Index_Id )
                    )
            SELECT  FullObjectName ,
                    ISNULL(IndexType, 'STATISTICS') AS IndexType ,
                    IndexName ,
                    KeyColumns ,
                    ISNULL(IncludeColumns, '') AS IncludeColumns ,
                    FilterDefinition ,
                    [FillFactor] AS [FillFactor] ,
                    DataCompression ,
                    IndexReads ,
                    IndexUpdates ,
                    SizeKB ,
                    IndexRows ,
                    IndexLookups ,
                    MostRecentlyUsed ,
                    StatsSampleRows ,
                    StatsRowMods ,
                    HistogramSteps ,
                    StatsLastUpdated ,
                    IndexFragInPercent
            FROM    AllResults
                    $IncludeStatsPredicate
        OPTION  ( RECOMPILE );
        "
        #endRegion SizesQuery
        
        
        #region sizesQuery2005
        $SizesQuery2005 = "
        SET NOCOUNT ON;
        SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

        $TablePredicate
        $IncludeDataTypesPredicate
        ;

        DECLARE @AllResults TABLE
            (
                RowNum INT ,
                FullObjectName	NVARCHAR(300) ,
                IndexType	NVARCHAR(256) ,
                IndexName	NVARCHAR(256) ,
                KeyColumns	NVARCHAR(2000) ,
                IncludeColumns	NVARCHAR(2000) ,
                FilterDefinition	NVARCHAR(100) ,
                [FillFactor]	TINYINT ,
                DataCompression	CHAR(4) ,
                IndexReads	BIGINT ,
                IndexUpdates	BIGINT ,
                SizeKB	BIGINT ,
                IndexRows	BIGINT ,
                IndexLookups	BIGINT ,
                MostRecentlyUsed	DATETIME ,
                StatsSampleRows	BIGINT ,
                StatsRowMods	BIGINT ,
                HistogramSteps	INT	,
                StatsLastUpdated	DATETIME ,
                object_id BIGINT ,
                index_id BIGINT
            );

        DECLARE @IndexUsageStats TABLE
            (
            object_id INT ,
            index_id INT ,
            user_scans BIGINT ,
            user_seeks BIGINT ,
            user_updates BIGINT ,
            user_lookups BIGINT ,
            last_user_lookup DATETIME ,
            last_user_scan DATETIME ,
            last_user_seek DATETIME ,
            avg_fragmentation_in_percent FLOAT
            );

        DECLARE @StatsInfo TABLE
            (
            object_id INT ,
            stats_id INT ,
            stats_column_name NVARCHAR(128) ,
            stats_column_id INT ,
            stats_name NVARCHAR(128) ,
            stats_last_updated DATETIME ,
            stats_sampled_rows BIGINT ,
            rowmods BIGINT ,
            histogramsteps INT ,
            StatsRows BIGINT ,
            FullObjectName NVARCHAR(256)
            );

        INSERT  INTO @IndexUsageStats
                ( object_id ,
                index_id ,
                user_scans ,
                user_seeks ,
                user_updates ,
                user_lookups ,
                last_user_lookup ,
                last_user_scan ,
                last_user_seek ,
                avg_fragmentation_in_percent
                )
                SELECT  ustat.object_id ,
                        ustat.index_id ,
                        ustat.user_scans ,
                        ustat.user_seeks ,
                        ustat.user_updates ,
                        ustat.user_lookups ,
                        ustat.last_user_lookup ,
                        ustat.last_user_scan ,
                        ustat.last_user_seek
                        $FragSelectColumn
                FROM    sys.dm_db_index_usage_stats ustat
                $FragJoin
                WHERE   database_id = DB_ID();


        INSERT  INTO @StatsInfo
                ( object_id ,
                stats_id ,
                stats_column_name ,
                stats_column_id ,
                stats_name ,
                stats_last_updated ,
                stats_sampled_rows ,
                rowmods ,
                histogramsteps ,
                StatsRows ,
                FullObjectName
                )
                SELECT  s.object_id ,
                        s.stats_id ,
                        c.name ,
                        sc.stats_column_id ,
                        s.name ,
                        NULL AS last_updated ,
                        NULL AS rows_sampled ,
                        NULL AS modification_counter ,
                        NULL AS steps ,
                        NULL AS rows ,
                        QUOTENAME(sch.name) + '.' + QUOTENAME(t.name) AS FullObjectName
                FROM    [sys].[stats] AS [s]
                        INNER JOIN sys.stats_columns sc ON s.stats_id = sc.stats_id
                                                        AND s.object_id = sc.object_id
                        INNER JOIN sys.columns c ON c.object_id = sc.object_id
                                                    AND c.column_id = sc.column_id
                        INNER JOIN sys.tables t ON c.object_id = t.object_id
                        INNER JOIN sys.schemas sch ON sch.schema_id = t.schema_id
                    --   OUTER APPLY sys.dm_db_stats_properties([s].[object_id],
                    --                                        [s].[stats_id]) AS [sp]
                WHERE   s.object_id = CASE WHEN @TableName IS NULL THEN s.object_id
                                        else OBJECT_ID(@TableName)
                                    END;


        ;
        WITH    cteStatsInfo
                AS ( SELECT   object_id ,
                                si.stats_id ,
                                si.stats_name ,
                                STUFF((SELECT   N', ' + stats_column_name
                                    FROM     @StatsInfo si2
                                    WHERE    si2.object_id = si.object_id
                                                AND si2.stats_id = si.stats_id
                                    ORDER BY si2.stats_column_id
                                FOR   XML PATH(N'') ,
                                        TYPE).value(N'.[1]', N'nvarchar(1000)'), 1,
                                    2, N'') AS StatsColumns ,
                                MAX(si.stats_sampled_rows) AS SampleRows ,
                                MAX(si.rowmods) AS RowMods ,
                                MAX(si.histogramsteps) AS HistogramSteps ,
                                MAX(si.stats_last_updated) AS StatsLastUpdated ,
                                MAX(si.StatsRows) AS StatsRows,
                                FullObjectName
                    FROM     @StatsInfo si
                    GROUP BY si.object_id ,
                                si.stats_id ,
                                si.stats_name ,
                                si.FullObjectName
                    ),
                cteIndexSizes
                AS ( SELECT   object_id ,
                                index_id ,
                                CASE WHEN index_id < 2
                                    THEN ( ( SUM(in_row_data_page_count
                                                + lob_used_page_count
                                                + row_overflow_used_page_count)
                                            * 8192 ) / 1024 )
                                    else ( ( SUM(used_page_count) * 8192 ) / 1024 )
                                END AS SizeKB
                    FROM     sys.dm_db_partition_stats
                    GROUP BY object_id ,
                                index_id
                    ),
                cteRows
                AS ( SELECT   object_id ,
                                index_id ,
                                SUM(rows) AS IndexRows
                    FROM     sys.partitions
                    GROUP BY object_id ,
                                index_id
                    ),
                cteIndex
                AS ( SELECT   OBJECT_NAME(c.object_id) AS ObjectName ,
                                c.object_id ,
                                c.index_id ,
                                i.name COLLATE SQL_Latin1_General_CP1_CI_AS AS name ,
                                c.index_column_id ,
                                c.column_id ,
                                c.is_included_column ,
                                CASE WHEN @IncludeDataTypes = 0
                                        AND c.is_descending_key = 1
                                    THEN sc.name + ' DESC'
                                    WHEN @IncludeDataTypes = 0
                                        AND c.is_descending_key = 0 THEN sc.name
                                    WHEN @IncludeDataTypes = 1
                                        AND c.is_descending_key = 1
                                        AND c.is_included_column = 0
                                    THEN sc.name + ' DESC (' + t.name + ') '
                                    WHEN @IncludeDataTypes = 1
                                        AND c.is_descending_key = 0
                                        AND c.is_included_column = 0
                                    THEN sc.name + ' (' + t.name + ')'
                                    else sc.name
                                END AS ColumnName ,
                                '' AS filter_definition ,
                                ISNULL(dd.user_scans, 0) AS user_scans ,
                                ISNULL(dd.user_seeks, 0) AS user_seeks ,
                                ISNULL(dd.user_updates, 0) AS user_updates ,
                                ISNULL(dd.user_lookups, 0) AS user_lookups ,
                                CONVERT(DATETIME, ISNULL(dd.last_user_lookup,
                                                            '1901-01-01')) AS LastLookup ,
                                CONVERT(DATETIME, ISNULL(dd.last_user_scan,
                                                            '1901-01-01')) AS LastScan ,
                                CONVERT(DATETIME, ISNULL(dd.last_user_seek,
                                                            '1901-01-01')) AS LastSeek ,
                                i.fill_factor ,
                                c.is_descending_key ,
                                'NONE' as data_compression_desc ,
                                i.type_desc ,
                                i.is_unique ,
                                i.is_unique_constraint ,
                                i.is_primary_key ,
                                ci.SizeKB ,
                                cr.IndexRows ,
                                QUOTENAME(sch.name) + '.' + QUOTENAME(tbl.name) AS FullObjectName ,
                                ISNULL(dd.avg_fragmentation_in_percent, 0) as avg_fragmentation_in_percent
                    FROM     sys.indexes i
                                JOIN sys.index_columns c ON i.object_id = c.object_id
                                                            AND i.index_id = c.index_id
                                JOIN sys.columns sc ON c.object_id = sc.object_id
                                                    AND c.column_id = sc.column_id
                                INNER JOIN sys.tables tbl ON c.object_id = tbl.object_id
                                INNER JOIN sys.schemas sch ON sch.schema_id = tbl.schema_id
                                LEFT JOIN sys.types t ON sc.user_type_id = t.user_type_id
                                LEFT JOIN @IndexUsageStats dd ON i.object_id = dd.object_id
                                                                AND i.index_id = dd.index_id --and dd.database_id = db_id()
                                JOIN sys.partitions p ON i.object_id = p.object_id
                                                        AND i.index_id = p.index_id
                                JOIN cteIndexSizes ci ON i.object_id = ci.object_id
                                                        AND i.index_id = ci.index_id
                                JOIN cteRows cr ON i.object_id = cr.object_id
                                                AND i.index_id = cr.index_id
                    WHERE    i.object_id = CASE WHEN @TableName IS NULL
                                                THEN i.object_id
                                                else OBJECT_ID(@TableName)
                                            END
                    ),
                cteResults
                AS ( SELECT   ci.FullObjectName ,
                                ci.object_id ,
                                MAX(index_id) AS Index_Id ,
                                ci.type_desc
                                + CASE WHEN ci.is_primary_key = 1
                                    THEN ' (PRIMARY KEY)'
                                    WHEN ci.is_unique_constraint = 1
                                    THEN ' (UNIQUE CONSTRAINT)'
                                    WHEN ci.is_unique = 1 THEN ' (UNIQUE)'
                                    else ''
                                END AS IndexType ,
                                name AS IndexName ,
                                STUFF((SELECT   N', ' + ColumnName
                                    FROM     cteIndex ci2
                                    WHERE    ci2.name = ci.name
                                                AND ci2.is_included_column = 0
                                    GROUP BY ci2.index_column_id ,
                                                ci2.ColumnName
                                    ORDER BY ci2.index_column_id
                                FOR   XML PATH(N'') ,
                                        TYPE).value(N'.[1]', N'nvarchar(1000)'), 1,
                                    2, N'') AS KeyColumns ,
                                ISNULL(STUFF((SELECT    N',  ' + ColumnName
                                            FROM      cteIndex ci3
                                            WHERE     ci3.name = ci.name
                                                        AND ci3.is_included_column = 1
                                            GROUP BY  ci3.index_column_id ,
                                                        ci3.ColumnName
                                            ORDER BY  ci3.index_column_id
                                    FOR   XML PATH(N'') ,
                                                TYPE).value(N'.[1]',
                                                            N'nvarchar(1000)'), 1, 2,
                                            N''), '') AS IncludeColumns ,
                                ISNULL(filter_definition, '') AS FilterDefinition ,
                                ci.fill_factor ,
                                CASE WHEN ci.data_compression_desc = 'NONE' THEN ''
                                    else ci.data_compression_desc
                                END AS DataCompression ,
                                MAX(ci.user_seeks) + MAX(ci.user_scans)
                                + MAX(ci.user_lookups) AS IndexReads ,
                                MAX(ci.user_lookups) AS IndexLookups ,
                                ci.user_updates AS IndexUpdates ,
                                ci.SizeKB AS SizeKB ,
                                ci.IndexRows AS IndexRows ,
                                CASE WHEN LastScan > LastSeek
                                        AND LastScan > LastLookup THEN LastScan
                                    WHEN LastSeek > LastScan
                                        AND LastSeek > LastLookup THEN LastSeek
                                    WHEN LastLookup > LastScan
                                        AND LastLookup > LastSeek THEN LastLookup
                                    else ''
                                END AS MostRecentlyUsed ,
                                AVG(ci.avg_fragmentation_in_percent) as avg_fragmentation_in_percent
                    FROM     cteIndex ci
                    GROUP BY ci.ObjectName ,
                                ci.name ,
                                ci.filter_definition ,
                                ci.object_id ,
                                ci.LastLookup ,
                                ci.LastSeek ,
                                ci.LastScan ,
                                ci.user_updates ,
                                ci.fill_factor ,
                                ci.data_compression_desc ,
                                ci.type_desc ,
                                ci.is_primary_key ,
                                ci.is_unique ,
                                ci.is_unique_constraint ,
                                ci.SizeKB ,
                                ci.IndexRows ,
                                ci.FullObjectName
                    ), AllResults AS
                        (		 SELECT   c.FullObjectName ,
                                ISNULL(IndexType, 'STATISTICS') AS IndexType ,
                                ISNULL(IndexName, '') AS IndexName ,
                                ISNULL(KeyColumns, '') AS KeyColumns ,
                                ISNULL(IncludeColumns, '') AS IncludeColumns ,
                                FilterDefinition ,
                                fill_factor AS [FillFactor] ,
                                DataCompression ,
                                IndexReads ,
                                IndexUpdates ,
                                SizeKB ,
                                IndexRows ,
                                IndexLookups ,
                                MostRecentlyUsed ,
                                NULL AS StatsSampleRows ,
                                NULL AS StatsRowMods ,
                                NULL AS HistogramSteps ,
                                NULL AS StatsLastUpdated ,
                                avg_fragmentation_in_percent as IndexFragInPercent,
                                1 AS Ordering ,
                                c.object_id ,
                                c.Index_Id
                    FROM     cteResults c
                                INNER JOIN cteStatsInfo si ON si.object_id = c.object_id
                                                            AND si.stats_id = c.Index_Id
                        UNION
                    SELECT   QUOTENAME(sch.name) + '.' + QUOTENAME(tbl.name) AS FullObjectName ,
                                'STATISTICS' ,
                                stats_name ,
                                StatsColumns ,
                                '' ,
                                '' AS FilterDefinition ,
                                '' AS Fill_Factor ,
                                '' AS DataCompression ,
                                '' AS IndexReads ,
                                '' AS IndexUpdates ,
                                '' AS SizeKB ,
                                StatsRows AS IndexRows ,
                                '' AS IndexLookups ,
                                '' AS MostRecentlyUsed ,
                                SampleRows AS StatsSampleRows ,
                                RowMods AS StatsRowMods ,
                                csi.HistogramSteps ,
                                csi.StatsLastUpdated ,
                                '' as IndexFragInPercent,
                                2 ,
                                csi.object_id ,
                                csi.stats_id
                    FROM     cteStatsInfo csi
                    INNER JOIN sys.tables tbl ON csi.object_id = tbl.object_id
                                INNER JOIN sys.schemas sch ON sch.schema_id = tbl.schema_id
                                LEFT JOIN (SELECT si.object_id, si.stats_id
                                            FROM    cteResults c
                                            INNER JOIN cteStatsInfo si ON si.object_id = c.object_id
                                                                    AND si.stats_id = c.Index_Id ) AS x on csi.object_id = x.object_id and csi.stats_id = x.stats_id
                        WHERE x.object_id is null
                    )
            INSERT INTO @AllResults
            SELECT  row_number() OVER (ORDER BY FullObjectName) AS RowNum ,
                    FullObjectName ,
                    ISNULL(IndexType, 'STATISTICS') AS IndexType ,
                    IndexName ,
                    KeyColumns ,
                    ISNULL(IncludeColumns, '') AS IncludeColumns ,
                    FilterDefinition ,
                    [FillFactor] AS [FillFactor] ,
                    DataCompression ,
                    IndexReads ,
                    IndexUpdates ,
                    SizeKB ,
                    IndexRows ,
                    IndexLookups ,
                    MostRecentlyUsed ,
                    StatsSampleRows ,
                    StatsRowMods ,
                    HistogramSteps ,
                    StatsLastUpdated ,
                    IndexFragInPercent ,
                    object_id ,
                    index_id
            FROM    AllResults
                    $IncludeStatsPredicate
        OPTION  ( RECOMPILE );

        /* Only update the stats data on 2005 for a single table, otherwise the run time for this is a potential problem for large table/index volumes */
        if @TableName IS NOT NULL
        BEGIN

            DECLARE @StatsInfo2005 TABLE (Name nvarchar(128), Updated DATETIME, Rows BIGINT, RowsSampled BIGINT, Steps INT, Density INT, AverageKeyLength INT, StringIndex NVARCHAR(20))

            DECLARE @SqlCall NVARCHAR(2000), @RowNum INT;
            SELECT @RowNum = min(RowNum) FROM @AllResults;
            WHILE @RowNum IS NOT NULL
            BEGIN
                SELECT @SqlCall = 'dbcc show_statistics('+FullObjectName+', '+IndexName+') with stat_header' FROM @AllResults WHERE RowNum = @RowNum;
                INSERT INTO @StatsInfo2005 exec (@SqlCall);
                UPDATE @AllResults
                    SET StatsSampleRows = RowsSampled,
                    HistogramSteps = Steps,
                    StatsLastUpdated = Updated
                    FROM @StatsInfo2005
                    WHERE RowNum = @RowNum;
                DELETE FROM @StatsInfo2005
                SELECT @RowNum = min(RowNum) FROM @AllResults WHERE RowNum > @RowNum;
            END;

        END;

        UPDATE a
        SET a.StatsRowMods = i.rowmodctr
        FROM @AllResults a
            JOIN sys.sysindexes i ON a.object_id = i.id AND a.index_id = i.indid;

        SELECT	FullObjectName ,
                IndexType ,
                IndexName ,
                KeyColumns ,
                IncludeColumns ,
                FilterDefinition ,
                [FillFactor] ,
                DataCompression ,
                IndexReads ,
                IndexUpdates ,
                SizeKB ,
                IndexRows ,
                IndexLookups ,
                MostRecentlyUsed ,
                StatsSampleRows ,
                StatsRowMods ,
                HistogramSteps ,
                StatsLastUpdated ,
                IndexFragInPercent
        FROM @AllResults;"
        
        #endregion sizesQuery2005
    }
    process {
        Write-Message -Level Debug -Message $SizesQuery
        Write-Message -Level Debug -Message $SizesQuery2005
        
        foreach ($instance in $SqlInstance) {
            
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            
            $InputObject += Get-DbaDatabase -SqlInstance $server -Database $Database -ExcludeDatabase $ExcludeDatabase
        }
        
        foreach ($db in $InputObject) {
            $server = $db.Parent
            
            #Need to check the version of SQL
            if ($server.versionMajor -ge 10) {
                $indexesQuery = $SizesQuery
            }
            else {
                $indexesQuery = $SizesQuery2005
            }
            
            if (!$db.IsAccessible) {
                Stop-Function -Message "$db is not accessible. Skipping." -Continue
            }
            
            Write-Message -Level Debug -Message "$indexesQuery"
            try {
                $IndexDetails = $db.Query($indexesQuery)
                
                if (!$Raw) {
                    foreach ($detail in $IndexDetails) {
                        $recentlyused = [datetime]$detail.MostRecentlyUsed
                        
                        if ($recentlyused.year -eq 1900) {
                            $recentlyused = $null
                        }
                        
                        [pscustomobject]@{
                            ComputerName  = $server.ComputerName
                            InstanceName  = $server.ServiceName
                            SqlInstance   = $server.DomainInstanceName
                            Database     = $db.Name
                            Object        = $detail.FullObjectName
                            Index         = $detail.IndexName
                            IndexType     = $detail.IndexType
                            KeyColumns    = $detail.KeyColumns
                            IncludeColumns = $detail.IncludeColumns
                            FilterDefinition = $detail.FilterDefinition
                            DataCompression = $detail.DataCompression
                            IndexReads    = "{0:N0}" -f $detail.IndexReads
                            IndexUpdates  = "{0:N0}" -f $detail.IndexUpdates
                            SizeKB        = "{0:N0}" -f $detail.SizeKB
                            IndexRows     = "{0:N0}" -f $detail.IndexRows
                            IndexLookups  = "{0:N0}" -f $detail.IndexLookups
                            MostRecentlyUsed = $recentlyused
                            StatsSampleRows = "{0:N0}" -f $detail.StatsSampleRows
                            StatsRowMods  = "{0:N0}" -f $detail.StatsRowMods
                            HistogramSteps = $detail.HistogramSteps
                            StatsLastUpdated = $detail.StatsLastUpdated
                            IndexFragInPercent = "{0:F2}" -f $detail.IndexFragInPercent
                        } | Select-DefaultView -Property $OutputProperties
                    }
                }
                
                else {
                    foreach ($detail in $IndexDetails) {
                        $recentlyused = [datetime]$detail.MostRecentlyUsed
                        
                        if ($recentlyused.year -eq 1900) {
                            $recentlyused = $null
                        }
                        
                        [pscustomobject]@{
                            ComputerName   = $server.ComputerName
                            InstanceName   = $server.ServiceName
                            SqlInstance    = $server.DomainInstanceName
                            Database       = $db.Name
                            Object         = $detail.FullObjectName
                            Index          = $detail.IndexName
                            IndexType      = $detail.IndexType
                            KeyColumns     = $detail.KeyColumns
                            IncludeColumns = $detail.IncludeColumns
                            FilterDefinition = $detail.FilterDefinition
                            DataCompression = $detail.DataCompression
                            IndexReads     = $detail.IndexReads
                            IndexUpdates   = $detail.IndexUpdates
                            SizeKB         = $detail.SizeKB
                            IndexRows      = $detail.IndexRows
                            IndexLookups   = $detail.IndexLookups
                            MostRecentlyUsed = $recentlyused
                            StatsSampleRows = $detail.StatsSampleRows
                            StatsRowMods   = $detail.StatsRowMods
                            HistogramSteps = $detail.HistogramSteps
                            StatsLastUpdated = $detail.StatsLastUpdated
                            IndexFragInPercent = $detail.IndexFragInPercent
                        } | Select-DefaultView -Property $OutputProperties
                    }
                }
            }
            catch {
                Stop-Function -Continue -ErrorRecord $_ -Message "Cannot process $db on $server"
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaLastBackup {
    <#
        .SYNOPSIS
            Get date/time for last known backups of databases.

        .DESCRIPTION
            Retrieves and compares the date/time for the last known backups, as well as the creation date/time for the database.

            Default output includes columns Server, Database, RecoveryModel, LastFullBackup, LastDiffBackup, LastLogBackup, SinceFull, SinceDiff, SinceLog, Status, DatabaseCreated, DaysSinceDbCreated.

        .PARAMETER SqlInstance
            The SQL Server instance to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies one or more database(s) to process. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies one or more database(s) to exclude from processing.

        .PARAMETER EnableException
            If this switch is enabled exceptions will be thrown to the caller, which will need to perform its own exception processing. Otherwise, the function will try to catch the exception, interpret it and provide a friendly error message.

        .NOTES
            Tags: DisasterRecovery, Backup
            Author: Klaas Vandenberghe ( @PowerDBAKlaas )

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaLastBackup

        .EXAMPLE
            Get-DbaLastBackup -SqlInstance ServerA\sql987

            Returns a custom object displaying Server, Database, RecoveryModel, LastFullBackup, LastDiffBackup, LastLogBackup, SinceFull, SinceDiff, SinceLog, Status, DatabaseCreated, DaysSinceDbCreated

        .EXAMPLE
            Get-DbaLastBackup -SqlInstance ServerA\sql987

            Returns a custom object with Server name, Database name, and the date the last time backups were performed.

        .EXAMPLE
            Get-DbaLastBackup -SqlInstance ServerA\sql987 | Select *

            Returns a custom object with Server name, Database name, and the date the last time backups were performed, and also recoverymodel and calculations on how long ago backups were taken and what the status is.

        .EXAMPLE
            Get-DbaLastBackup -SqlInstance ServerA\sql987 | Select * | Out-Gridview

            Returns a gridview displaying Server, Database, RecoveryModel, LastFullBackup, LastDiffBackup, LastLogBackup, SinceFull, SinceDiff, SinceLog, Status, DatabaseCreated, DaysSinceDbCreated.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        function Get-DbaDateOrNull ($TimeSpan) {
            if ($TimeSpan -eq 0) {
                return $null
            }
            return $TimeSpan
        }
        $StartOfTime = [DbaTimeSpan](New-TimeSpan -Start ([datetime]0))
    }
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            
            $dbs = $server.Databases | Where-Object { $_.name -ne 'tempdb' }

            if ($Database) {
                $dbs = $dbs | Where-Object Name -In $Database
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }
            # Get-DbaBackupHistory -Last would make the job in one query but SMO's (and this) report the last backup of this type irregardless of the chain
            $FullHistory = Get-DbaBackupHistory -SqlInstance $server -Database $dbs.Name -LastFull -IncludeCopyOnly -Raw
            $DiffHistory = Get-DbaBackupHistory -SqlInstance $server -Database $dbs.Name -LastDiff -IncludeCopyOnly -Raw
            $IncrHistory = Get-DbaBackupHistory -SqlInstance $server -Database $dbs.Name -LastLog -IncludeCopyOnly -Raw
            foreach ($db in $dbs) {
                Write-Message -Level Verbose -Message "Processing $db on $instance"

                if ($db.IsAccessible -eq $false) {
                    Write-Message -Level Warning -Message "The database $db on server $instance is not accessible. Skipping database."
                    Continue
                }
                $LastFullBackup = ($FullHistory | Where-Object Database -eq $db.Name | Sort-Object -Property End -Descending | Select-Object -First 1).End
                if ($null -ne $LastFullBackup) {
                    $SinceFull_ = [DbaTimeSpan](New-TimeSpan -Start $LastFullBackup)
                }
                else {
                    $SinceFull_ = $StartOfTime
                }

                $LastDiffBackup = ($DiffHistory | Where-Object Database -eq $db.Name | Sort-Object -Property End -Descending | Select-Object -First 1).End
                if ($null -ne $LastDiffBackup) {
                    $SinceDiff_ = [DbaTimeSpan](New-TimeSpan -Start $LastDiffBackup)
                }
                else {
                    $SinceDiff_ = $StartOfTime
                }

                $LastIncrBackup = ($IncrHistory | Where-Object Database -eq $db.Name | Sort-Object -Property End -Descending | Select-Object -First 1).End
                if ($null -ne $LastIncrBackup) {
                    $SinceLog_ = [DbaTimeSpan](New-TimeSpan -Start $LastIncrBackup)
                }
                else {
                    $SinceLog_ = $StartOfTime
                }

                $daysSinceDbCreated = (New-TimeSpan -Start $db.createDate).Days

                if ($daysSinceDbCreated -lt 1 -and $SinceFull_ -eq 0) {
                    $Status = 'New database, not backed up yet'
                }
                elseif ($SinceFull_.Days -gt 0 -and $SinceDiff_.Days -gt 0) {
                    $Status = 'No Full or Diff Back Up in the last day'
                }
                elseif ($db.RecoveryModel -eq "Full" -and $SinceLog_.Hours -gt 0) {
                    $Status = 'No Log Back Up in the last hour'
                }
                else {
                    $Status = 'OK'
                }

                $result = [PSCustomObject]@{
                    ComputerName       = $server.ComputerName
                    InstanceName       = $server.ServiceName
                    SqlInstance        = $server.DomainInstanceName
                    Database           = $db.Name
                    RecoveryModel      = $db.RecoveryModel
                    LastFullBackup     = [DbaDateTime]$LastFullBackup
                    LastDiffBackup     = [DbaDateTime]$LastDiffBackup
                    LastLogBackup      = [DbaDateTime]$LastIncrBackup
                    SinceFull          = Get-DbaDateOrNull -TimeSpan $SinceFull_
                    SinceDiff          = Get-DbaDateOrNull -TimeSpan $SinceDiff_
                    SinceLog           = Get-DbaDateOrNull -TimeSpan $SinceLog_
                    DatabaseCreated    = $db.createDate
                    DaysSinceDbCreated = $daysSinceDbCreated
                    Status             = $status
                }
                Select-DefaultView -InputObject $result -Property ComputerName, InstanceName, SqlInstance, Database, LastFullBackup, LastDiffBackup, LastLogBackup
            }
        }
    }
}
function Get-DbaLastGoodCheckDb {
    <#
        .SYNOPSIS
            Get date/time for last known good DBCC CHECKDB

        .DESCRIPTION
            Retrieves and compares the date/time for the last known good DBCC CHECKDB, as well as the creation date/time for the database.

            This function supports SQL Server 2005 and higher.

            Please note that this script uses the DBCC DBINFO() WITH TABLERESULTS. DBCC DBINFO has several known weak points, such as:
            - DBCC DBINFO is an undocumented feature/command.
            - The LastKnowGood timestamp is updated when a DBCC CHECKFILEGROUP is performed.
            - The LastKnowGood timestamp is updated when a DBCC CHECKDB WITH PHYSICAL_ONLY is performed.
            - The LastKnowGood timestamp does not get updated when a database in READ_ONLY.

            An empty ($null) LastGoodCheckDb result indicates that a good DBCC CHECKDB has never been performed.

            SQL Server 2008R2 has a "bug" that causes each databases to possess two dbi_dbccLastKnownGood fields, instead of the normal one.

            This script will only display this function to only display the newest timestamp. If -Verbose is specified, the function will announce every time more than one dbi_dbccLastKnownGood fields is encountered.

        .PARAMETER SqlInstance
            The SQL Server instance to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies one or more database(s) to process. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies one or more database(s) to exclude from processing.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: CHECKDB, Database
            Author: Jakob Bindslet ([email protected])

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            DBCC CHECKDB:
                https://msdn.microsoft.com/en-us/library/ms176064.aspx
                http://www.sqlcopilot.com/dbcc-checkdb.html
            Data Purity:
                http://www.sqlskills.com/blogs/paul/checkdb-from-every-angle-how-to-tell-if-data-purity-checks-will-be-run/
                https://www.mssqltips.com/sqlservertip/1988/ensure-sql-server-data-purity-checks-are-performed/

        .EXAMPLE
            Get-DbaLastGoodCheckDb -SqlInstance ServerA\sql987

            Returns a custom object displaying Server, Database, DatabaseCreated, LastGoodCheckDb, DaysSinceDbCreated, DaysSinceLastGoodCheckDb, Status and DataPurityEnabled

        .EXAMPLE
            Get-DbaLastGoodCheckDb -SqlInstance ServerA\sql987 -SqlCredential (Get-Credential sqladmin) | Format-Table -AutoSize

            Returns a formatted table displaying Server, Database, DatabaseCreated, LastGoodCheckDb, DaysSinceDbCreated, DaysSinceLastGoodCheckDb, Status and DataPurityEnabled. Authenticates using SQL Server authentication.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($server.versionMajor -lt 9) {
                Stop-Function -Message "Get-DbaLastGoodCheckDb is only supported on SQL Server 2005 and above. Skipping Instance." -Continue -Target $instance
            }

            $dbs = $server.Databases

            if ($Database) {
                $dbs = $dbs | Where-Object Name -In $Database
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $dbs) {
                Write-Message -Level Verbose -Message "Processing $db on $instances."

                if ($db.IsAccessible -eq $false) {
                    Stop-Function -Message "The database $db is not accessible. Skipping database." -Continue -Target $db
                }

                $sql = "DBCC DBINFO ([$($db.name)]) WITH TABLERESULTS"
                Write-Message -Level Debug -Message "T-SQL: $sql"

                $resultTable = $db.ExecuteWithResults($sql).Tables[0]
                [datetime[]]$lastKnownGoodArray = $resultTable | Where-Object Field -eq 'dbi_dbccLastKnownGood' | Select-Object -ExpandProperty Value

                ## look for databases with two or more occurrences of the field dbi_dbccLastKnownGood
                if ($lastKnownGoodArray.count -ge 2) {
                    Write-Message -Level Verbose -Message "The database $db has $($lastKnownGoodArray.count) dbi_dbccLastKnownGood fields. This script will only use the newest!"
                }
                [datetime]$lastKnownGood = $lastKnownGoodArray | Sort-Object -Descending | Select-Object -First 1

                [int]$createVersion = ($resultTable | Where-Object Field -eq 'dbi_createVersion').Value
                [int]$dbccFlags = ($resultTable | Where-Object Field -eq 'dbi_dbccFlags').Value

                if (($createVersion -lt 611) -and ($dbccFlags -eq 0)) {
                    $dataPurityEnabled = $false
                }
                else {
                    $dataPurityEnabled = $true
                }

                $daysSinceCheckDb = (New-TimeSpan -Start $lastKnownGood -End (Get-Date)).Days
                $daysSinceDbCreated = (New-TimeSpan -Start $db.createDate -End (Get-Date)).TotalDays

                if ($daysSinceCheckDb -lt 7) {
                    $Status = 'Ok'
                }
                elseif ($daysSinceDbCreated -lt 7) {
                    $Status = 'New database, not checked yet'
                }
                else {
                    $Status = 'CheckDB should be performed'
                }

                if ($lastKnownGood -eq '1/1/1900 12:00:00 AM') {
                    Remove-Variable -Name lastKnownGood, daysSinceCheckDb
                }

                [PSCustomObject]@{
                    ComputerName             = $server.ComputerName
                    InstanceName             = $server.ServiceName
                    SqlInstance              = $server.DomainInstanceName
                    Database                 = $db.name
                    DatabaseCreated          = $db.createDate
                    LastGoodCheckDb          = $lastKnownGood
                    DaysSinceDbCreated       = $daysSinceDbCreated
                    DaysSinceLastGoodCheckDb = $daysSinceCheckDb
                    Status                   = $status
                    DataPurityEnabled        = $dataPurityEnabled
                    CreateVersion            = $createVersion
                    DbccFlags                = $dbccFlags
                }
            }
        }
    }
}
function Get-DbaLinkedServer {
    <#
        .SYNOPSIS
            Gets all linked servers and summary of information from the sql servers listed

        .DESCRIPTION
            Retrieves information about each linked server on the instance

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function
            to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER LinkedServer
            The linked server(s) to process - this list is auto-populated from the server. If unspecified, all linked servers will be processed.

        .PARAMETER ExcludeLinkedServer
            The linked server(s) to exclude - this list is auto-populated from the server

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: LinkedServer, Linked
            Author: Stephen Bennett ( https://sqlnotesfromtheunderground.wordpress.com/ )

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaLinkedServer

        .EXAMPLE
            Get-DbaLinkedServer -SqlInstance DEV01

            Returns all Linked Servers for the SQL Server instance DEV01
    #>
    [CmdletBinding(DefaultParameterSetName = 'Default')]
    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$LinkedServer,
        [object[]]$ExcludeLinkedServer,
        [Alias('Silent')]
        [switch]$EnableException
    )
    foreach ($Instance in $SqlInstance) {
        try {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
        }

        $lservers = $server.LinkedServers

        if ($LinkedServer) {
            $lservers = $lservers | Where-Object { $_.Name -in $LinkedServer }
        }
        if ($ExcludeLinkedServer) {
            $lservers = $lservers | Where-Object { $_.Name -notin $ExcludeLinkedServer }
        }

        foreach ($ls in $lservers) {
            Add-Member -Force -InputObject $ls -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
            Add-Member -Force -InputObject $ls -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
            Add-Member -Force -InputObject $ls -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
            Add-Member -Force -InputObject $ls -MemberType NoteProperty -Name Impersonate -value $ls.LinkedServerLogins.Impersonate
            Add-Member -Force -InputObject $ls -MemberType NoteProperty -Name RemoteUser -value $ls.LinkedServerLogins.RemoteUser

            Select-DefaultView -InputObject $ls -Property ComputerName, InstanceName, SqlInstance, Name, 'DataSource as RemoteServer', ProductName, Impersonate, RemoteUser, 'DistPublisher as Publisher', Distributor, DateLastModified
        }
    }
}
function Get-DbaLocaleSetting {
    <#
      .SYNOPSIS
      Gets the Locale settings on a computer.

      .DESCRIPTION
      Gets the Locale settings on one or more computers.

      Requires Local Admin rights on destination computer(s).

      .PARAMETER ComputerName
      The SQL Server (or server in general) that you're connecting to. This command handles named instances.

      .PARAMETER Credential
      Credential object used to connect to the computer as a different user.

      .PARAMETER EnableException
      By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
      This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
      Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

      .NOTES
      Author: Klaas Vandenberghe ( @PowerDBAKlaas )
      Tags: OS
      dbatools PowerShell module (https://dbatools.io)
      Copyright (C) 2016 Chrissy LeMaire
      License: MIT https://opensource.org/licenses/MIT

      .LINK
      https://dbatools.io/Get-DbaLocaleSetting

      .EXAMPLE
      Get-DbaLocaleSetting -ComputerName sqlserver2014a

      Gets the Locale settings on computer sqlserver2014a.

      .EXAMPLE
      'sql1','sql2','sql3' | Get-DbaLocaleSetting

      Gets the Locale settings on computers sql1, sql2 and sql3.

      .EXAMPLE
      Get-DbaLocaleSetting -ComputerName sql1,sql2 | Out-Gridview

      Gets the Locale settings on computers sql1 and sql2, and shows them in a grid view.

  #>
    [CmdletBinding()]
    Param (
        [parameter(ValueFromPipeline)]
        [Alias("cn", "host", "Server")]
        [string[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential] $Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    BEGIN {
        $ComputerName = $ComputerName | ForEach-Object {$_.split("\")[0]} | Select-Object -Unique
        $sessionoption = New-CimSessionOption -Protocol DCom
        $keyname = "Control Panel\International"
        $NS = 'root\cimv2'
        $Reg = 'StdRegProv'
        [UInt32]$CIMHiveCU = 2147483649
    }
    PROCESS {
        foreach ($computer in $ComputerName) {
            $props = @{ "ComputerName" = $computer }
            $Server = Resolve-DbaNetworkName -ComputerName $Computer -Credential $credential
            if ( $Server.FullComputerName ) {
                $Computer = $server.FullComputerName
                Write-Message -Level Verbose -Message "Creating CIMSession on $computer over WSMan"
                $CIMsession = New-CimSession -ComputerName $Computer -ErrorAction SilentlyContinue -Credential $Credential
                if ( -not $CIMSession ) {
                    Write-Message -Level Verbose -Message "Creating CIMSession on $computer over WSMan failed. Creating CIMSession on $computer over DCom"
                    $CIMsession = New-CimSession -ComputerName $Computer -SessionOption $sessionoption -ErrorAction SilentlyContinue -Credential $Credential
                }
                if ( $CIMSession ) {
                    Write-Message -Level Verbose -Message "Getting properties from Registry Key"
                    $PropNames = Invoke-CimMethod -CimSession $CIMsession -Namespace $NS -ClassName $Reg -MethodName enumvalues -Arguments @{hDefKey = $CIMHiveCU; sSubKeyName = $keyname} |
                        Select-Object -ExpandProperty snames

                    foreach ($Name in $PropNames) {
                        $sValue = Invoke-CimMethod -CimSession $CIMsession -Namespace $NS -ClassName $Reg -MethodName GetSTRINGvalue -Arguments @{hDefKey = $CIMHiveCU; sSubKeyName = $keyname; sValueName = $Name} |
                            Select-Object -ExpandProperty svalue
                        $props.add($Name, $sValue)
                    }
                    [PSCustomObject]$props
                } #if CIMSession
                else {
                    Write-Message -Level Warning -Message "Can't create CIMSession on $computer"
                }
            } #if computername
            else {
                Write-Message -Level Warning -Message "Can't connect to $computer"
            }
        } #foreach computer
    } #PROCESS
} #function
function Get-DbaLogin {
    <#
        .SYNOPSIS
            Function to get an SMO login object of the logins for a given SQL Instance. Takes a server object from the pipe

        .DESCRIPTION
            The Get-DbaLogin function returns an SMO Login object for the logins passed, if there are no users passed it will return all logins.

        .PARAMETER SqlInstance
            The SQL Server instance, or instances.You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SqlCredential
            Allows you to login to servers using SQL Logins as opposed to Windows Auth/Integrated/Trusted.

        .PARAMETER Login
            The login(s) to process - this list is auto-populated from the server. If unspecified, all logins will be processed.

        .PARAMETER ExcludeLogin
            The login(s) to exclude - this list is auto-populated from the server

        .PARAMETER IncludeFilter
            A list of logins to include - accepts wildcard patterns

        .PARAMETER ExcludeFilter
            A list of logins to exclude - accepts wildcard patterns

        .PARAMETER NoSystem
            A Switch to remove System Logins from the output.

        .PARAMETER SQLLogins
            A Switch to return Logins of type SQLLogin only.

        .PARAMETER WindowsLogins
            A Switch to return Logins of type Windows only.

        .PARAMETER Locked
            A Switch to return locked Logins.

        .PARAMETER Disabled
            A Switch to return disabled Logins.

        .PARAMETER HasAccess
            A Switch to return Logins that have access to the instance of SQL Server.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Login, Security
            Author: Mitchell Hamann (@SirCaptainMitch)
            Author: Klaas Vandenberghe (@powerdbaklaas)
            Author: Robert Corrigan (@rjcorrig)
            Author: Rob Sewell (@SQLDBaWithBeard)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaLogin

        .EXAMPLE
            Get-DbaLogin -SqlInstance sql2016

            Gets all the logins from server sql2016 using NT authentication and returns the SMO login objects

        .EXAMPLE
            Get-DbaLogin -SqlInstance sql2016 -SqlCredential $sqlcred

            Gets all the logins for a given SQL Server using a passed credential object and returns the SMO login objects

        .EXAMPLE
            Get-DbaLogin -SqlInstance sql2016 -SqlCredential $sqlcred -Login dbatoolsuser,TheCaptain

            Get specific logins from server sql2016 returned as SMO login objects.

        .EXAMPLE
            Get-DbaLogin -SqlInstance sql2016 -IncludeFilter '##*','NT *'

            Get all user objects from server sql2016 beginning with '##' or 'NT ', returned as SMO login objects.

        .EXAMPLE
            Get-DbaLogin -SqlInstance sql2016 -ExcludeLogin dbatoolsuser

            Get all user objects from server sql2016 except the login dbatoolsuser, returned as SMO login objects.

        .EXAMPLE
            Get-DbaLogin -SqlInstance sql2016 -WindowsLogins

            Get all user objects from server sql2016 that are Windows Logins

        .EXAMPLE
            Get-DbaLogin -SqlInstance sql2016 -WindowsLogins -IncludeFilter *Rob*

            Get all user objects from server sql2016 that are Windows Logins and have Rob in the name

        .EXAMPLE
            Get-DbaLogin -SqlInstance sql2016 -SQLLogins

            Get all user objects from server sql2016 that are SQLLogins

        .EXAMPLE
            Get-DbaLogin -SqlInstance sql2016 -SQLLogins -IncludeFilter *Rob*

            Get all user objects from server sql2016 that are SQLLogins  and have Rob in the name

        .EXAMPLE
            Get-DbaLogin -SqlInstance sql2016 -NoSystem

            Get all user objects from server sql2016 that are not system objects

        .EXAMPLE
            Get-DbaLogin -SqlInstance sql2016 -ExcludeFilter '##*','NT *'

            Get all user objects from server sql2016 except any beginning with '##' or 'NT ', returned as SMO login objects.

        .EXAMPLE
            'sql2016', 'sql2014' | Get-DbaLogin -SqlCredential $sqlcred

            Using Get-DbaLogin on the pipeline, you can also specify which names you would like with -Login.

        .EXAMPLE
            'sql2016', 'sql2014' | Get-DbaLogin -SqlCredential $sqlcred -Locked

            Using Get-DbaLogin on the pipeline to get all locked logins on servers sql2016 and sql2014.

        .EXAMPLE
            'sql2016', 'sql2014' | Get-DbaLogin -SqlCredential $sqlcred -HasAccess -Disabled

            Using Get-DbaLogin on the pipeline to get all Disabled logins that have access on servers sql2016 or sql2014.
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Login,
        [object[]]$IncludeFilter,
        [object[]]$ExcludeLogin,
        [object[]]$ExcludeFilter,
        [switch]$NoSystem,
        [switch]$SQLLogins,
        [switch]$WindowsLogins,
        [switch]$HasAccess,
        [switch]$Locked,
        [switch]$Disabled,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $serverLogins = $server.Logins

            if ($Login) {
                $serverLogins = $serverLogins | Where-Object Name -in $Login
            }

            if ($NoSystem) {
                $serverLogins = $serverLogins | Where-Object IsSystemObject -eq $false
            }

            if ($SQLLogins) {
                $serverLogins = $serverLogins | Where-Object LoginType -eq 'SqlLogin'
            }

            if ($WindowsLogins) {
                $serverLogins = $serverLogins | Where-Object LoginType -eq 'WindowsUser'
            }

            if ($IncludeFilter) {
                $serverLogins = $serverLogins | Where-Object {
                    foreach ($filter in $IncludeFilter) {
                        if ($_.Name -like $filter) {
                            return $true;
                        }
                    }
                }
            }

            if ($ExcludeLogin) {
                $serverLogins = $serverLogins | Where-Object Name -NotIn $ExcludeLogin
            }

            if ($ExcludeFilter) {
                foreach ($filter in $ExcludeFilter) {
                    $serverLogins = $serverLogins | Where-Object Name -NotLike $filter
                }
            }

            if ($HasAccess) {
                $serverLogins = $serverLogins | Where-Object HasAccess
            }

            if ($Locked) {
                $serverLogins = $serverLogins | Where-Object IsLocked
            }

            if ($Disabled) {
                $serverLogins = $serverLogins | Where-Object IsDisabled
            }

            foreach ($serverLogin in $serverlogins) {
                Write-Message -Level Verbose -Message "Processing $serverLogin on $instance"

                if ($server.VersionMajor -gt 9) {
                    # There's no reliable method to get last login time with SQL Server 2000, so only show on 2005+
                    Write-Message -Level Verbose -Message "Getting last login time"
                    $sql = "SELECT MAX(login_time) AS [login_time] FROM sys.dm_exec_sessions WHERE login_name = '$($serverLogin.name)'"
                    Add-Member -Force -InputObject $serverLogin -MemberType NoteProperty -Name LastLogin -Value $server.ConnectionContext.ExecuteScalar($sql)
                }
                else {
                    Add-Member -Force -InputObject $serverLogin -MemberType NoteProperty -Name LastLogin -Value $null
                }

                Add-Member -Force -InputObject $serverLogin -MemberType NoteProperty -Name ComputerName -Value $server.ComputerName
                Add-Member -Force -InputObject $serverLogin -MemberType NoteProperty -Name InstanceName -Value $server.ServiceName
                Add-Member -Force -InputObject $serverLogin -MemberType NoteProperty -Name SqlInstance -Value $server.DomainInstanceName

                Select-DefaultView -InputObject $serverLogin -Property ComputerName, InstanceName, SqlInstance, Name, LoginType, CreateDate, LastLogin, HasAccess, IsLocked, IsDisabled
            }
        }
    }
}
function Get-DbaLogShippingError {
    <#
        .SYNOPSIS
            Get-DbaLogShippingError returns all the log shipping errors that occurred

        .DESCRIPTION
            When your log shipping fails it's sometimes hard to see why is fails.
            Using this function you'll be able to find out what went wrong in a short amount of time.

        .PARAMETER SqlInstance
            SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Allows you to filter the results to only return the databases you're interested in. This can be one or more values separated by commas.
            This is not a wildcard and should be the exact database name. See examples for more info.

        .PARAMETER ExcludeDatabase
            Allows you to filter the results to only return the databases you're not interested in. This can be one or more values separated by commas.
            This is not a wildcard and should be the exact database name.

        .PARAMETER Action
            Filter to get the log shipping action that has occurred like Backup, Copy, Restore.
            By default all the actions are returned.

        .PARAMETER DateTimeFrom
            Filter the results based on the date starting from datetime X

        .PARAMETER DateTimeTo
            Filter the results based on the date ending with datetime X

        .PARAMETER Primary
            Allows to filter the results to only return values that apply to the primary instance.

        .PARAMETER Secondary
            Allows to filter the results to only return values that apply to the secondary instance.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: LogShipping
            Author: Sander Stad (@sqlstad, sqlstad.nl)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaLogShippingError

        .EXAMPLE
            Get-DbaLogShippingError -SqlInstance sql1

            Get all the log shipping errors that occurred

        .EXAMPLE
            Get-DbaLogShippingError -SqlInstance sql1 -Action Backup

            Get the errors that have something to do with the backup of the databases

        .EXAMPLE
            Get-DbaLogShippingError -SqlInstance sql1 -Secondary

            Get the errors that occurred on the secondary instance.
            This will return the copy of the restore actions because those only occur on the secondary instance

        .EXAMPLE
            Get-DbaLogShippingError -SqlInstance sql1 -DateTimeFrom "01/05/2018"

            Get the errors that have occurred from "01/05/2018". This can also be of format "yyyy-MM-dd"

        .EXAMPLE
            Get-DbaLogShippingError -SqlInstance sql1 -Secondary -DateTimeFrom "01/05/2018" -DateTimeTo "2018-01-07"

            Get the errors that have occurred between "01/05/2018" and "01/07/2018".
            See that is doesn't matter how the date is represented.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$Database,
        [string[]]$ExcludeDatabase,
        [ValidateSet("Backup", "Copy", "Restore")]
        [string[]]$Action,
        [datetime]$DateTimeFrom,
        [datetime]$DateTimeTo,
        [switch]$Primary,
        [switch]$Secondary,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {

        # Create array list to hold the results
        $collection = New-Object System.Collections.ArrayList

    }

    process {
        foreach ($instance in $sqlinstance) {
            # Try connecting to the instance
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($server.EngineEdition -match "Express") {
                Write-Message -Level Warning -Message "$instance is Express Edition which does not support Log Shipping"
                continue
            }

            $query = "
CREATE TABLE #DatabaseID
(
    DatabaseName VARCHAR(128),
    DatabaseID UNIQUEIDENTIFIER,
    Instance VARCHAR(20)
);

INSERT INTO #DatabaseID
(
    DatabaseName,
    DatabaseID,
    Instance
)
SELECT secondary_database,
        secondary_id,
        'Secondary'
FROM msdb.dbo.log_shipping_secondary_databases;


INSERT INTO #DatabaseID
(
    DatabaseName,
    DatabaseID,
    Instance
)
SELECT primary_database,
        primary_id,
        'Primary'
FROM msdb.dbo.log_shipping_primary_databases;


SELECT di.DatabaseName,
        di.Instance,
        CASE lsmed.[agent_type]
            WHEN 0 THEN
                'Backup'
            WHEN 1 THEN
                'Copy'
            WHEN 2 THEN
                'Restore'
            ELSE
                ''
        END AS [Action],
        lsmed.[session_id] AS SessionID,
        lsmed.[sequence_number] AS SequenceNumber,
        lsmed.[log_time] AS LogTime,
        lsmed.[message] AS [Message]
FROM msdb.dbo.log_shipping_monitor_error_detail AS lsmed
    INNER JOIN #DatabaseID AS di
        ON di.DatabaseID = lsmed.agent_id
ORDER BY lsmed.[log_time],
            lsmed.[database_name],
            lsmed.[agent_type],
            lsmed.[session_id],
            lsmed.[sequence_number];

DROP TABLE #DatabaseID;"

            # Get the log shipping errors
            $results = $server.Query($query)

            if ($results.Count -ge 1) {

                # Filter the results
                if ($Database) {
                    $results = $results | Where-Object { $_.DatabaseName -in $Database }
                }

                if ($Action) {
                    $results = $results | Where-Object { $_.Action -in $Action }
                }

                if ($DateTimeFrom) {
                    $results = $results | Where-Object {$_.Logtime -ge $DateTimeFrom}
                }

                if ($DateTimeTo) {
                    $results = $results | Where-Object {$_.Logtime -le $DateTimeTo}
                }

                if ($Primary) {
                    $results = $results | Where-Object {$_.Instance -eq 'Primary'}
                }

                if ($Secondary) {
                    $results = $results | Where-Object {$_.Instance -eq 'Secondary'}
                }

                # Loop through each of the results
                foreach ($result in $results) {
                    # Set up the custom object
                    $null = $collection.Add([PSCustomObject]@{
                            ComputerName   = $server.ComputerName
                            InstanceName   = $server.ServiceName
                            SqlInstance    = $server.DomainInstanceName
                            Database       = $result.DatabaseName
                            Instance       = $result.Instance
                            Action         = $result.Action
                            SessionID      = $result.SessionID
                            SequenceNumber = $result.SequenceNumber
                            LogTime        = $result.LogTime
                            Message        = $result.Message
                        })

                } # for each result
            }
            else {
                Write-Message -Message "No log shipping errors found" -Level Verbose
            }

        } # foreach instance

        return $collection

    } # end process

}

function Get-DbaMaintenanceSolutionLog {
    <#
        .SYNOPSIS
            Reads the log files generated by the IndexOptimize Agent Job from Ola Hallengren's MaintenanceSolution.

        .DESCRIPTION
            Ola wrote a .sql script to get the content from the commandLog table. However, if LogToTable='N', there will be no logging in that table. This function reads the text files that are written in the SQL Instance's Log directory.

        .PARAMETER SqlInstance
            The SQL Server instance.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER LogType
            Accepts 'IndexOptimize', 'DatabaseBackup', 'DatabaseIntegrityCheck'. ATM only IndexOptimize parsing is available

        .PARAMETER Since
            Consider only files generated since this date

        .PARAMETER Path
            Where to search for log files. By default it's the SQL instance errorlogpath path

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Ola, Maintenance
            Author: Klaas Vandenberghe ( @powerdbaklaas )
            Author: Simone Bizzotto ( @niphlod )

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaMaintenanceSolutionLog

        .EXAMPLE
            Get-DbaMaintenanceSolutionLog -SqlInstance sqlserver2014a

            Gets the outcome of the IndexOptimize job on sql instance sqlserver2014a.

        .EXAMPLE
            Get-DbaMaintenanceSolutionLog -SqlInstance sqlserver2014a -SqlCredential $credential

            Gets the outcome of the IndexOptimize job on sqlserver2014a, using SQL Authentication.

        .EXAMPLE
            'sqlserver2014a', 'sqlserver2020test' | Get-DbaMaintenanceSolutionLog

            Gets the outcome of the IndexOptimize job on sqlserver2014a and sqlserver2020test.

        .EXAMPLE
            Get-DbaMaintenanceSolutionLog -SqlInstance sqlserver2014a -Path 'D:\logs\maintenancesolution\'

            Gets the outcome of the IndexOptimize job on sqlserver2014a, reading the log files in their custom location.

        .EXAMPLE
            Get-DbaMaintenanceSolutionLog -SqlInstance sqlserver2014a -Since '2017-07-18'

            Gets the outcome of the IndexOptimize job on sqlserver2014a, starting from july 18, 2017.

        .EXAMPLE
        Get-DbaMaintenanceSolutionLog -SqlInstance sqlserver2014a -LogType IndexOptimize

        Gets the outcome of the IndexOptimize job on sqlserver2014a, the other options are not yet available! sorry
    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [ValidateSet('IndexOptimize', 'DatabaseBackup', 'DatabaseIntegrityCheck')]
        [string[]]$LogType = 'IndexOptimize',
        [datetime]$Since,
        [string]$Path,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        function process-block ($block) {
            $fresh = @{
                'ObjectType'     = $null
                'IndexType'      = $null
                'ImageText'      = $null
                'NewLOB'         = $null
                'FileStream'     = $null
                'ColumnStore'    = $null
                'AllowPageLocks' = $null
                'PageCount'      = $null
                'Fragmentation'  = $null
                'Error'          = $null
            }
            foreach ($l in $block) {
                $splitted = $l -split ': ', 2
                if (($splitted.Length -ne 2) -or ($splitted[0].length -gt 20)) {
                    if ($null -eq $fresh['Error']) {
                        $fresh['Error'] = New-Object System.Collections.ArrayList
                    }
                    $null = $fresh['Error'].Add($l)
                    continue
                }
                $k = $splitted[0]
                $v = $splitted[1]
                if ($k -eq 'Date and Time') {
                    # this is the end date, we already parsed the start date of the block
                    if ($fresh.ContainsKey($k)) {
                        continue
                    }
                }
                $fresh[$k] = $v
            }
            if ($fresh.ContainsKey('Command')) {
                if ($fresh['Command'] -match '(SET LOCK_TIMEOUT (?<timeout>\d+); )?ALTER INDEX \[(?<index>[^\]]+)\] ON \[(?<database>[^\]]+)\]\.\[(?<schema>[^]]+)\]\.\[(?<table>[^\]]+)\] (?<action>[^\ ]+)( PARTITION = (?<partition>\d+))? WITH \((?<options>[^\)]+)') {
                    $fresh['Index'] = $Matches.index
                    $fresh['Statistics'] = $null
                    $fresh['Schema'] = $Matches.Schema
                    $fresh['Table'] = $Matches.Table
                    $fresh['Action'] = $Matches.action
                    $fresh['Options'] = $Matches.options
                    $fresh['Timeout'] = $Matches.timeout
                    $fresh['Partition'] = $Matches.partition
                }
                elseif ($fresh['Command'] -match '(SET LOCK_TIMEOUT (?<timeout>\d+); )?UPDATE STATISTICS \[(?<database>[^\]]+)\]\.\[(?<schema>[^]]+)\]\.\[(?<table>[^\]]+)\] \[(?<stat>[^\]]+)\]') {
                    $fresh['Index'] = $null
                    $fresh['Statistics'] = $Matches.stat
                    $fresh['Schema'] = $Matches.Schema
                    $fresh['Table'] = $Matches.Table
                    $fresh['Action'] = $null
                    $fresh['Options'] = $null
                    $fresh['Timeout'] = $Matches.timeout
                    $fresh['Partition'] = $null
                }
            }
            if ($fresh.ContainsKey('Comment')) {
                $commentparts = $fresh['Comment'] -split ', '
                foreach ($part in $commentparts) {
                    $indkey, $indvalue = $part -split ': ', 2
                    if ($fresh.ContainsKey($indkey)) {
                        $fresh[$indkey] = $indvalue
                    }
                }
            }
            if ($null -ne $fresh['Error']) {
                $fresh['Error'] = $fresh['Error'] -join "`n"
            }

            return $fresh
        }
    }
    process {
        foreach ($instance in $sqlinstance) {
            $logdir = $logfiles = $null
            $computername = $instance.ComputerName
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Can't connect to $instance" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            if ($logtype -ne 'IndexOptimize') {
                Write-Message -Level Warning -Message "Parsing $logtype is not supported at the moment"
                Continue
            }
            if ($Path) {
                $logdir = Join-AdminUnc -Servername $server.ComputerName -Filepath $Path
            }
            else {
                $logdir = Join-AdminUnc -Servername $server.ComputerName -Filepath $server.errorlogpath # -replace '^(.):', "\\$computername\`$1$"
            }
            if (!$logdir) {
                Write-Message -Level Warning -Message "No log directory returned from $instance"
                Continue
            }

            Write-Message -Level Verbose -Message "Log directory on $computername is $logdir"
            if (! (Test-Path $logdir)) {
                Write-Message -Level Warning -Message "Directory $logdir is not accessible"
                continue
            }
            $logfiles = [System.IO.Directory]::EnumerateFiles("$logdir", "IndexOptimize_*.txt")
            if ($Since) {
                $filteredlogs = @()
                foreach ($l in $logfiles) {
                    $base = $($l.Substring($l.Length - 15, 15))
                    try {
                        $datefile = [DateTime]::ParseExact($base, 'yyyyMMdd_HHmmss', $null)
                    }
                    catch {
                        $datefile = Get-ItemProperty -Path $l | select -ExpandProperty CreationTime
                    }
                    if ($datefile -gt $since) {
                        $filteredlogs += $l
                    }
                }
                $logfiles = $filteredlogs
            }
            if (! $logfiles.count -ge 1) {
                Write-Message -Level Warning -Message "No log files returned from $computername"
                Continue
            }
            $instanceinfo = @{ }
            $instanceinfo['ComputerName'] = $server.ComputerName
            $instanceinfo['InstanceName'] = $server.ServiceName
            $instanceinfo['SqlInstance'] = $server.Name

            foreach ($File in $logfiles) {
                Write-Message -Level Verbose -Message "Reading $file"
                $text = New-Object System.IO.StreamReader -ArgumentList "$File"
                $block = New-Object System.Collections.ArrayList
                $remember = @{}
                while ($line = $text.ReadLine()) {

                    $real = $line.Trim()
                    if ($real.Length -eq 0) {
                        $processed = process-block $block
                        if ('Procedure' -in $processed.Keys) {
                            $block = New-Object System.Collections.ArrayList
                            continue
                        }
                        if ('Database' -in $processed.Keys) {
                            Write-Message -Level Verbose -Message "Index and Stats Optimizations on Database $($processed.Database) on $computername"
                            $processed.Remove('Is accessible')
                            $processed.Remove('User access')
                            $processed.Remove('Date and time')
                            $processed.Remove('Standby')
                            $processed.Remove('Recovery Model')
                            $processed.Remove('Updateability')
                            $processed['Database'] = $processed['Database'].Trim('[]')
                            $remember = $processed.Clone()
                        }
                        else {
                            foreach ($k in $processed.Keys) {
                                $remember[$k] = $processed[$k]
                            }
                            $remember.Remove('Command')
                            $remember['StartTime'] = [dbadatetime]([DateTime]::ParseExact($remember['Date and time'] , "yyyy-MM-dd HH:mm:ss", $null))
                            $remember.Remove('Date and time')
                            $remember['Duration'] = ($remember['Duration'] -as [timespan])
                            [pscustomobject]$remember
                        }
                        $block = New-Object System.Collections.ArrayList
                    }
                    else {
                        $null = $block.Add($real)
                    }
                }
                $text.close()
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaMaxMemory {
    <#
        .SYNOPSIS
            Gets the 'Max Server Memory' configuration setting and the memory of the server.  Works on SQL Server 2000-2014.

        .DESCRIPTION
            This command retrieves the SQL Server 'Max Server Memory' configuration setting as well as the total  physical installed on the server.

        .PARAMETER SqlInstance
            Allows you to specify a comma separated list of servers to query.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: MaxMemory, Memory
            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaMaxMemory

        .EXAMPLE
            Get-DbaMaxMemory -SqlInstance sqlcluster,sqlserver2012

            Get memory settings for all servers within the SQL Server Central Management Server "sqlcluster".

        .EXAMPLE
            Get-DbaMaxMemory -SqlInstance sqlcluster | Where-Object { $_.SqlMaxMB -gt $_.TotalMB }

            Find all servers in Server Central Management Server that have 'Max Server Memory' set to higher than the total memory of the server (think 2147483647)
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $totalMemory = $server.PhysicalMemory

            # Some servers under-report by 1MB.
            if (($totalMemory % 1024) -ne 0) {
                $totalMemory = $totalMemory + 1
            }

            [pscustomobject]@{
                ComputerName = $server.ComputerName
                InstanceName = $server.ServiceName
                SqlInstance  = $server.DomainInstanceName
                TotalMB      = [int]$totalMemory
                SqlMaxMB     = [int]$server.Configuration.MaxServerMemory.ConfigValue
            } | Select-DefaultView -ExcludeProperty Server
        }
    }
}
#ValidationTags#Messaging,CodeStyle#
function Get-DbaMemoryUsage {
    <#
        .SYNOPSIS
            Get amount of memory in use by *all* SQL Server components and instances

        .DESCRIPTION
            Retrieves the amount of memory per performance counter. Default output includes columns Server, counter instance, counter, number of pages, memory in KB, memory in MB
            SSAS and SSIS are included.

            SSRS does not have memory counters, only memory shrinks and memory pressure state.

            This function requires local admin role on the targeted computers.

        .PARAMETER ComputerName
            The Windows Server that you are connecting to. Note that this will return all instances, but Out-GridView makes it easy to filter to specific instances.

        .PARAMETER Credential
            Credential object used to connect to the SQL Server as a different user

        .PARAMETER Simple
            Shows concise information including Server name, Database name, and the date the last time backups were performed

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Memory
            Author: Klaas Vandenberghe ( @PowerDBAKlaas )

            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

            SSIS Counters: https://msdn.microsoft.com/en-us/library/ms137622.aspx

        .LINK
            https://dbatools.io/Get-DbaMemoryUsage

        .EXAMPLE
            Get-DbaMemoryUsage -ComputerName ServerA

            Returns a custom object displaying Server, counter instance, counter, number of pages, memory in KB, memory in MB

        .EXAMPLE
            Get-DbaMemoryUsage -ComputerName ServerA\sql987 -Simple

            Returns a custom object with Server, counter instance, counter, number of pages, memory in KB, memory in MB

        .EXAMPLE
            Get-DbaMemoryUsage -ComputerName ServerA\sql987 | Out-Gridview

            Returns a gridview displaying Server, counter instance, counter, number of pages, memory in KB, memory in MB
    #>
    [CmdletBinding()]
    param (
        [parameter(ValueFromPipeline)]
        [Alias("Host", "cn", "Server")]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [switch]$Simple,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        if ($Simple) {
            $Memcounters = '(Total Server Memory |Target Server Memory |Connection Memory |Lock Memory |SQL Cache Memory |Optimizer Memory |Granted Workspace Memory |Cursor memory usage|Maximum Workspace)'
            $Plancounters = 'total\)\\cache pages'
            $BufManpagecounters = 'Total pages'
            $SSAScounters = '(\\memory usage)'
            $SSIScounters = '(memory)'
        }
        else {
            $Memcounters = '(Total Server Memory |Target Server Memory |Connection Memory |Lock Memory |SQL Cache Memory |Optimizer Memory |Granted Workspace Memory |Cursor memory usage|Maximum Workspace)'
            $Plancounters = '(cache pages|procedure plan|ad hoc sql plan|prepared SQL Plan)'
            $BufManpagecounters = '(Free pages|Reserved pages|Stolen pages|Total pages|Database pages|target pages|extension .* pages)'
            $SSAScounters = '(\\memory )'
            $SSIScounters = '(memory)'
        }

        $scriptblock = {
            param ($Memcounters,
                $Plancounters,
                $BufManpagecounters,
                $SSAScounters,
                $SSIScounters)
            Write-Verbose "Searching for Memory Manager Counters on $Computer"
            try {
                $availablecounters = (Get-Counter -ListSet '*sql*:Memory Manager*' -ErrorAction SilentlyContinue).paths
                (Get-Counter -Counter $availablecounters -ErrorAction SilentlyContinue).countersamples |
                    Where-Object { $_.Path -match $Memcounters } |
                    ForEach-Object {
                    $instance = (($_.Path.split("\")[-2]).replace("mssql`$", "")).split(':')[0]
                    if ($instance -eq 'sqlserver') { $instance = 'mssqlserver' }
                    [PSCustomObject]@{
                        ComputerName    = $env:computername
                        SqlInstance     = $instance
                        CounterInstance = (($_.Path.split("\")[-2]).replace("mssql`$", "")).split(':')[1]
                        Counter         = $_.Path.split("\")[-1]
                        Pages           = $null
                        MemKB           = $_.cookedvalue
                        MemMB           = $_.cookedvalue / 1024
                    }
                }
            }
            catch {
                Write-Verbose "No Memory Manager Counters on $Computer"
            }

            Write-Verbose "Searching for Plan Cache Counters on $Computer"
            try {
                $availablecounters = (Get-Counter -ListSet '*sql*:Plan Cache*' -ErrorAction SilentlyContinue).paths
                (Get-Counter -Counter $availablecounters -ErrorAction SilentlyContinue).countersamples |
                    Where-Object { $_.Path -match $Plancounters } |
                    ForEach-Object {
                    $instance = (($_.Path.split("\")[-2]).replace("mssql`$", "")).split(':')[0]
                    if ($instance -eq 'sqlserver') { $instance = 'mssqlserver' }
                    [PSCustomObject]@{
                        ComputerName    = $env:computername
                        SqlInstance     = $instance
                        CounterInstance = (($_.Path.split("\")[-2]).replace("mssql`$", "")).split(':')[1]
                        Counter         = $_.Path.split("\")[-1]
                        Pages           = $_.cookedvalue
                        MemKB           = $_.cookedvalue * 8192 / 1024
                        MemMB           = $_.cookedvalue * 8192 / 1048576
                    }
                }
            }
            catch {
                Write-Verbose "No Plan Cache Counters on $Computer"
            }

            Write-Verbose "Searching for Buffer Manager Counters on $Computer"
            try {
                $availablecounters = (Get-Counter -ListSet "*Buffer Manager*" -ErrorAction SilentlyContinue).paths
                (Get-Counter -Counter $availablecounters -ErrorAction SilentlyContinue).countersamples |
                    Where-Object { $_.Path -match $BufManpagecounters } |
                    ForEach-Object {
                    $instance = (($_.Path.split("\")[-2]).replace("mssql`$", "")).split(':')[0]
                    if ($instance -eq 'sqlserver') { $instance = 'mssqlserver' }
                    [PSCustomObject]@{
                        ComputerName    = $env:computername
                        SqlInstance     = $instance
                        CounterInstance = (($_.Path.split("\")[-2]).replace("mssql`$", "")).split(':')[1]
                        Counter         = $_.Path.split("\")[-1]
                        Pages           = $_.cookedvalue
                        MemKB           = $_.cookedvalue * 8192 / 1024.0
                        MemMB           = $_.cookedvalue * 8192 / 1048576.0
                    }
                }
            }
            catch {
                Write-Verbose "No Buffer Manager Counters on $Computer"
            }

            Write-Verbose "Searching for SSAS Counters on $Computer"
            try {
                $availablecounters = (Get-Counter -ListSet "MSAS*:Memory" -ErrorAction SilentlyContinue).paths
                (Get-Counter -Counter $availablecounters -ErrorAction SilentlyContinue).countersamples |
                    Where-Object { $_.Path -match $SSAScounters } |
                    ForEach-Object {
                    $instance = (($_.Path.split("\")[-2]).replace("mssql`$", "")).split(':')[0]
                    if ($instance -eq 'sqlserver') { $instance = 'mssqlserver' }
                    [PSCustomObject]@{
                        ComputerName    = $env:COMPUTERNAME
                        SqlInstance     = $instance
                        CounterInstance = (($_.Path.split("\")[-2]).replace("mssql`$", "")).split(':')[1]
                        Counter         = $_.Path.split("\")[-1]
                        Pages           = $null
                        MemKB           = $_.cookedvalue
                        MemMB           = $_.cookedvalue / 1024
                    }
                }
            }
            catch {
                Write-Verbose "No SSAS Counters on $Computer"
            }

            Write-Verbose "Searching for SSIS Counters on $Computer"
            try {
                $availablecounters = (Get-Counter -ListSet "*SSIS*" -ErrorAction SilentlyContinue).paths
                (Get-Counter -Counter $availablecounters -ErrorAction SilentlyContinue).countersamples |
                    Where-Object { $_.Path -match $SSIScounters } |
                    ForEach-Object {
                    $instance = (($_.Path.split("\")[-2]).replace("mssql`$", "")).split(':')[0]
                    if ($instance -eq 'sqlserver') { $instance = 'mssqlserver' }
                    [PSCustomObject]@{
                        ComputerName    = $env:computername
                        SqlInstance     = $instance
                        CounterInstance = (($_.Path.split("\")[-2]).replace("mssql`$", "")).split(':')[1]
                        Counter         = $_.Path.split("\")[-1]
                        Pages           = $null
                        MemKB           = $_.cookedvalue / 1024
                        MemMB           = $_.cookedvalue / 1024 / 1024
                    }
                }
            }
            catch {
                Write-Verbose "No SSIS Counters on $Computer"
            }
        }
    }

    process {
        foreach ($Computer in $ComputerName.ComputerName) {
            $reply = Resolve-DbaNetworkName -ComputerName $computer -Credential $Credential -ErrorAction SilentlyContinue
            if ($reply.FullComputerName) {
                $Computer = $reply.FullComputerName
                try {
                    Write-Message -Level Verbose -Message "Connecting to $Computer"
                    Invoke-Command2 -ComputerName $Computer -Credential $Credential -ScriptBlock $scriptblock -argumentlist $Memcounters, $Plancounters, $BufManpagecounters, $SSAScounters, $SSIScounters
                }
                catch {
                    Stop-Function -Continue -Message "Failure" -ErrorRecord $_ -Target $computer -Continue
                }
            }
            else {
                Write-Message -Level Warning -Message "Can't resolve $Computer."
                Continue
            }
        }
    }
}
#ValidationTags#Messaging#
function Get-DbaMsdtc {
    <#
        .SYNOPSIS
            Displays information about the Distributed Transaction Coordinator (MSDTC) on a server

        .DESCRIPTION
            Returns a custom object with Computer name, state of the MSDTC Service, security settings of MSDTC and CID's

            Requires: Windows administrator access on Servers

        .PARAMETER ComputerName
            The SQL Server (or server in general) that you're connecting to.

        .NOTES
            Tags: Msdtc, dtc
            Author: Klaas Vandenberghe ( powerdbaklaas )

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaMsdtc

        .EXAMPLE
            Get-DbaMsdtc -ComputerName srv0042

            Get DTC status for the server srv0042

        .EXAMPLE
            $Computers = (Get-Content D:\configfiles\SQL\MySQLInstances.txt | % {$_.split('\')[0]})
            $Computers | Get-DbaMsdtc

            Get DTC status for all the computers in a .txt file

        .EXAMPLE
            Get-DbaMsdtc -Computername $Computers | where { $_.dtcservicestate -ne 'running' }

            Get DTC status for all the computers where the MSDTC Service is not running

        .EXAMPLE
            Get-DbaMsdtc -ComputerName srv0042 | Out-Gridview

            Get DTC status for the computer srv0042 and show in a grid view
    #>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Alias('cn', 'host', 'Server')]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME
    )

    begin {
        $ComputerName = $ComputerName | ForEach-Object {$_.split("\")[0]} | Select-Object -Unique
        $query = "Select * FROM Win32_Service WHERE Name = 'MSDTC'"
        $dtcSecurity = {
            Get-ItemProperty -Path HKLM:\Software\Microsoft\MSDTC\Security |
                Select-Object PSPath, PSComputerName, AccountName, networkDTCAccess,
            networkDTCAccessAdmin, networkDTCAccessClients, networkDTCAccessInbound,
            networkDTCAccessOutBound, networkDTCAccessTip, networkDTCAccessTransactions, XATransactions
        }
        $dtcCids = {
            New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT | Out-Null
            Get-ItemProperty -Path HKCR:\CID\*\Description |
                Select-Object @{ l = 'Data'; e = { $_.'(default)' } }, @{ l = 'CID'; e = { $_.PSParentPath.split('\')[-1] } }
            Remove-PSDrive -Name HKCR | Out-Null
        }
    }
    process {
        foreach ($computer in $ComputerName) {
            $reg = $cids = $null
            $cidHash = @{}
            if ( Test-PSRemoting -ComputerName $computer ) {
                $dtcservice = $null
                Write-Message -Level Verbose -Message "Getting DTC on $computer via WSMan"
                $dtcservice = Get-Ciminstance -ComputerName $computer -Query $query
                if ( $null -eq $dtcservice ) {
                    Write-Warning "Can't connect to CIM on $computer via WSMan"
                }

                Write-Message -Level Verbose -Message "Getting MSDTC Security Registry Values on $computer"
                $reg = Invoke-Command -ComputerName $computer -ScriptBlock $dtcSecurity
                if ( $null -eq $reg ) {
                    Write-Message -Level Warning -Message "Can't connect to MSDTC Security registry on $computer"
                }
                Write-Message -Level Verbose -Message "Getting MSDTC CID Registry Values on $computer"
                $cids = Invoke-Command -ComputerName $computer -ScriptBlock $dtcCids
                if ( $null -ne $cids ) {
                    foreach ($key in $cids) {
                        $cidHash.Add($key.Data, $key.CID)
                    }
                }
                else {
                    Write-Message -Level Warning -Message "Can't connect to MSDTC CID registry on $computer"
                }
            }
            else {
                Write-Message -Level Verbose -Message "PSRemoting is not enabled on $computer"
                try {
                    Write-Message -Level Verbose -Message "Failed To get DTC via WinRM. Getting DTC on $computer via DCom"
                    $SessionParams = @{ }
                    $SessionParams.ComputerName = $Computer
                    $SessionParams.SessionOption = (New-CimSessionOption -Protocol Dcom)
                    $Session = New-CimSession @SessionParams
                    $dtcservice = Get-Ciminstance -CimSession $Session -Query $query
                }
                catch {
                    Stop-Function -Message "Can't connect to CIM on $computer via DCom" -Target $computer -ErrorRecord $_ -Continue
                }
            }
            if ( $dtcservice ) {
                [PSCustomObject]@{
                    ComputerName                 = $dtcservice.PSComputerName
                    DTCServiceName               = $dtcservice.DisplayName
                    DTCServiceState              = $dtcservice.State
                    DTCServiceStatus             = $dtcservice.Status
                    DTCServiceStartMode          = $dtcservice.StartMode
                    DTCServiceAccount            = $dtcservice.StartName
                    DTCCID_MSDTC                 = $cidHash['MSDTC']
                    DTCCID_MSDTCUIS              = $cidHash['MSDTCUIS']
                    DTCCID_MSDTCTIPGW            = $cidHash['MSDTCTIPGW']
                    DTCCID_MSDTCXATM             = $cidHash['MSDTCXATM']
                    networkDTCAccess             = $reg.networkDTCAccess
                    networkDTCAccessAdmin        = $reg.networkDTCAccessAdmin
                    networkDTCAccessClients      = $reg.networkDTCAccessClients
                    networkDTCAccessInbound      = $reg.networkDTCAccessInbound
                    networkDTCAccessOutBound     = $reg.networkDTCAccessOutBound
                    networkDTCAccessTip          = $reg.networkDTCAccessTip
                    networkDTCAccessTransactions = $reg.networkDTCAccessTransactions
                    XATransactions               = $reg.XATransactions
                }
            }
        }
    }
}
function Get-DbaNetworkActivity {
    <#
      .SYNOPSIS
      Gets the Current traffic on every Network Interface on a computer.

      .DESCRIPTION
      Gets the Current traffic on every Network Interface on a computer.
      See https://msdn.microsoft.com/en-us/library/aa394293(v=vs.85).aspx

      Requires Local Admin rights on destination computer(s).

      .PARAMETER ComputerName
      The SQL Server (or server in general) that you're connecting to. This command handles named instances.

      .PARAMETER Credential
      Credential object used to connect to the computer as a different user.

      .PARAMETER EnableException
      By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
      This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
      Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

      .NOTES
      Author: Klaas Vandenberghe ( @PowerDBAKlaas )
      Tags: Network
      dbatools PowerShell module (https://dbatools.io)
      Copyright (C) 2016 Chrissy LeMaire
      License: MIT https://opensource.org/licenses/MIT

      .LINK
      https://dbatools.io/Get-DbaNetworkActivity

      .EXAMPLE
      Get-DbaNetworkActivity -ComputerName sqlserver2014a

      Gets the Current traffic on every Network Interface on computer sqlserver2014a.

      .EXAMPLE
      'sql1','sql2','sql3' | Get-DbaNetworkActivity

      Gets the Current traffic on every Network Interface on computers sql1, sql2 and sql3.

      .EXAMPLE
      Get-DbaNetworkActivity -ComputerName sql1,sql2 | Out-Gridview

      Gets the Current traffic on every Network Interface on computers sql1 and sql2, and shows them in a grid view.

  #>
    [CmdletBinding()]
    Param (
        [parameter(ValueFromPipeline)]
        [Alias("cn", "host", "Server")]
        [string[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential] $Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    BEGIN {
        $ComputerName = $ComputerName | ForEach-Object {$_.split("\")[0]} | Select-Object -Unique
        $sessionoption = New-CimSessionOption -Protocol DCom
    }
    PROCESS {
        foreach ($computer in $ComputerName) {
            $Server = Resolve-DbaNetworkName -ComputerName $Computer -Credential $credential
            if ( $Server.FullComputerName ) {
                $Computer = $server.FullComputerName
                Write-Message -Level Verbose -Message "Creating CIMSession on $computer over WSMan"
                $CIMsession = New-CimSession -ComputerName $Computer -ErrorAction SilentlyContinue -Credential $Credential
                if ( -not $CIMSession ) {
                    Write-Message -Level Verbose -Message "Creating CIMSession on $computer over WSMan failed. Creating CIMSession on $computer over DCom"
                    $CIMsession = New-CimSession -ComputerName $Computer -SessionOption $sessionoption -ErrorAction SilentlyContinue -Credential $Credential
                }
                if ( $CIMSession ) {
                    Write-Message -Level Verbose -Message "Getting properties for Network Interfaces on $computer"
                    $NICs = Get-CimInstance -CimSession $CIMSession -ClassName Win32_PerfFormattedData_Tcpip_NetworkInterface
                    $NICs | Add-Member -Force -MemberType ScriptProperty -Name ComputerName -Value { $computer }
                    $NICs | Add-Member -Force -MemberType ScriptProperty -Name Bandwith -Value { switch ( $this.CurrentBandWidth ) { 10000000000 { '10Gb' } 1000000000 { '1Gb' } 100000000 { '100Mb' } 10000000 { '10Mb' } 1000000 { '1Mb' } 100000 { '100Kb' } default { 'Low' } } }
                    foreach ( $NIC in $NICs ) { Select-DefaultView -InputObject $NIC -Property 'ComputerName', 'Name as NIC', 'BytesReceivedPersec', 'BytesSentPersec', 'BytesTotalPersec', 'Bandwidth'}
                } #if CIMSession
                else {
                    Write-Message -Level Warning -Message "Can't create CIMSession on $computer"
                }
            } #if computername
            else {
                Write-Message -Level Warning -Message "can't connect to $computer"
            }
        } #foreach computer
    } #PROCESS
} #function
function Get-DbaNetworkCertificate {
    <#
.SYNOPSIS
Simplifies finding computer certificates that are candidates for using with SQL Server's network encryption

.DESCRIPTION
Gets computer certificates on localhost that are candidates for using with SQL Server's network encryption

.PARAMETER ComputerName
The target SQL Server - defaults to localhost. If target is a cluster, you must specify the distinct nodes.

.PARAMETER Credential
Allows you to login to $ComputerName using alternative credentials.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Certificate

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
Get-DbaNetworkCertificate
Gets computer certificates on localhost that are candidates for using with SQL Server's network encryption

.EXAMPLE
Get-DbaNetworkCertificate -ComputerName sql2016

Gets computer certificates on sql2016 that are being used for SQL Server network encryption

#>
    [CmdletBinding()]
    param (
        [parameter(ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer", "SqlInstance")]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($computer in $computername) {

            Write-Message -Level Verbose -Message "Connecting to SQL WMI on $($computer.ComputerName)"
            try {
                $sqlwmis = Invoke-ManagedComputerCommand -ComputerName $computer.ComputerName -ScriptBlock { $wmi.Services } -Credential $Credential -ErrorAction Stop | Where-Object DisplayName -match "SQL Server \("
            }
            catch {
                Stop-Function -Message $_ -Target $sqlwmi -Continue
            }

            foreach ($sqlwmi in $sqlwmis) {

                $regroot = ($sqlwmi.AdvancedProperties | Where-Object Name -eq REGROOT).Value
                $vsname = ($sqlwmi.AdvancedProperties | Where-Object Name -eq VSNAME).Value
                $instancename = $sqlwmi.DisplayName.Replace('SQL Server (', '').Replace(')', '') # Don't clown, I don't know regex :(
                $serviceaccount = $sqlwmi.ServiceAccount

                if ([System.String]::IsNullOrEmpty($regroot)) {
                    $regroot = $sqlwmi.AdvancedProperties | Where-Object { $_ -match 'REGROOT' }
                    $vsname = $sqlwmi.AdvancedProperties | Where-Object { $_ -match 'VSNAME' }

                    if (![System.String]::IsNullOrEmpty($regroot)) {
                        $regroot = ($regroot -Split 'Value\=')[1]
                        $vsname = ($vsname -Split 'Value\=')[1]
                    }
                    else {
                        Write-Message -Level Warning -Message "Can't find instance $vsname on $env:COMPUTERNAME"
                        return
                    }
                }

                if ([System.String]::IsNullOrEmpty($vsname)) { $vsname = $computer }

                Write-Message -Level Verbose -Message "Regroot: $regroot"
                Write-Message -Level Verbose -Message "ServiceAcct: $serviceaccount"
                Write-Message -Level Verbose -Message "InstanceName: $instancename"
                Write-Message -Level Verbose -Message "VSNAME: $vsname"

                $scriptblock = {
                    $regroot = $args[0]
                    $serviceaccount = $args[1]
                    $instancename = $args[2]
                    $vsname = $args[3]

                    $regpath = "Registry::HKEY_LOCAL_MACHINE\$regroot\MSSQLServer\SuperSocketNetLib"

                    $thumbprint = (Get-ItemProperty -Path $regpath -Name Certificate -ErrorAction SilentlyContinue).Certificate

                    try {
                        $cert = Get-ChildItem Cert:\LocalMachine -Recurse -ErrorAction Stop | Where-Object Thumbprint -eq $Thumbprint
                    }
                    catch {
                        # Don't care - sometimes there's errors that are thrown for apparent good reason
                    }

                    if (!$cert) { continue }

                    [pscustomobject]@{
                        ComputerName   = $env:COMPUTERNAME
                        InstanceName   = $instancename
                        SqlInstance    = $vsname
                        ServiceAccount = $serviceaccount
                        FriendlyName   = $cert.FriendlyName
                        DnsNameList    = $cert.DnsNameList
                        Thumbprint     = $cert.Thumbprint
                        Generated      = $cert.NotBefore
                        Expires        = $cert.NotAfter
                        IssuedTo       = $cert.Subject
                        IssuedBy       = $cert.Issuer
                        Certificate    = $cert
                    }
                }

                Write-Message -Level Verbose -Message "Connecting to $computer to get a list of certs"
                try {
                    Invoke-Command2 -ComputerName $computer.ComputerName -Credential $Credential -ArgumentList $regroot, $serviceaccount, $instancename, $vsname -ScriptBlock $scriptblock -ErrorAction Stop |
                        Select-DefaultView -ExcludeProperty Certificate
                }
                catch {
                    Stop-Function -Message $_ -ErrorRecord $_ -Target $ComputerName -Continue
                }
            }
        }
    }
}
function Get-DbaOpenTransaction {
    <#
        .SYNOPSIS
            Displays all open transactions.

        .DESCRIPTION
            This command is based on open transaction script published by Paul Randal.
            Reference: https://www.sqlskills.com/blogs/paul/script-open-transactions-with-text-and-plans/

        .PARAMETER SqlInstance
            The SQL Server instance

        .PARAMETER SqlCredential
            Connect using alternative credentials

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database, Process, Session, ActivityMonitor
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaOpenTransaction

        .EXAMPLE
            Get-DbaOpenTransaction -SqlInstance sqlserver2014a

            Returns open transactions for sqlserver2014a

        .EXAMPLE
            Get-DbaOpenTransaction -SqlInstance sqlserver2014a -SqlCredential (Get-Credential sqladmin)

            Logs into sqlserver2014a using the login "sqladmin"
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [switch]$EnableException
    )

    begin {
        $sql = "
            SELECT  SERVERPROPERTY('MachineName') AS ComputerName,
            ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
            SERVERPROPERTY('ServerName') AS SqlInstance,
            [s_tst].[session_id] as Spid,
            [s_es].[login_name] as Login,
            DB_NAME (s_tdt.database_id) AS [Database],
            [s_tdt].[database_transaction_begin_time] AS [BeginTime],
            [s_tdt].[database_transaction_log_bytes_used] AS [LogBytesUsed],
            [s_tdt].[database_transaction_log_bytes_reserved] AS [LogBytesReserved],
            [s_est].text AS [LastQuery],
            [s_eqp].[query_plan] AS [LastPlan]
            FROM
                sys.dm_tran_database_transactions [s_tdt]
            JOIN
                sys.dm_tran_session_transactions [s_tst]
            ON
                [s_tst].[transaction_id] = [s_tdt].[transaction_id]
            JOIN
                sys.[dm_exec_sessions] [s_es]
            ON
                [s_es].[session_id] = [s_tst].[session_id]
            JOIN
                sys.dm_exec_connections [s_ec]
            ON
                [s_ec].[session_id] = [s_tst].[session_id]
            LEFT OUTER JOIN
                sys.dm_exec_requests [s_er]
            ON
                [s_er].[session_id] = [s_tst].[session_id]
            CROSS APPLY
                sys.dm_exec_sql_text ([s_ec].[most_recent_sql_handle]) AS [s_est]
            OUTER APPLY
                sys.dm_exec_query_plan ([s_er].[plan_handle]) AS [s_eqp]
            ORDER BY
                [BeginTime] ASC"
    }
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $server.Query($sql)
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaOperatingSystem {
    <#
        .SYNOPSIS
            Gets operating system information from the server.

        .DESCRIPTION
            Gets operating system information from the server and returns as an object.

        .PARAMETER ComputerName
            Target computer(s). If no computer name is specified, the local computer is targeted

        .PARAMETER Credential
            Alternate credential object to use for accessing the target computer(s).

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ServerInfo, OperatingSystem
            Author: Shawn Melton (@wsmelton | http://blog.wsmelton.info)

            Website: https: //dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaOperatingSystem

        .EXAMPLE
            Get-DbaOperatingSystem

            Returns information about the local computer's operating system

        .EXAMPLE
            Get-DbaOperatingSystem -ComputerName sql2016

            Returns information about the sql2016's operating system
    #>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Alias("cn", "host", "Server")]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($computer in $ComputerName) {
            Write-Message -Level Verbose -Message "Connecting to $computer"
            $server = Resolve-DbaNetworkName -ComputerName $computer.ComputerName -Credential $Credential

            $computerResolved = $server.FullComputerName

            if (!$computerResolved) {
                Write-Message -Level Warning -Message "Unable to resolve hostname of $computer. Skipping."
                continue
            }

            try {
                $psVersion = Invoke-Command2 -ComputerName $computerResolved -Credential $Credential -ScriptBlock { $PSVersionTable.PSVersion }
            }
            catch {
                Stop-Function -Message "Failure collecting PowerShell version on $computer" -Target $computer -ErrorRecord $_
                return
            }

            try {
                if (Test-Bound "Credential") {
                    $os = Get-DbaCmObject -ClassName Win32_OperatingSystem -ComputerName $computerResolved -Credential $Credential -EnableException
                }
                else {
                    $os = Get-DbaCmObject -ClassName Win32_OperatingSystem -ComputerName $computerResolved -EnableException
                }
            }
            catch {
                Stop-Function -Message "Failure collecting OS information on $computer" -Target $computer -ErrorRecord $_
                return
            }

            try {
                if (Test-Bound "Credential") {
                    $tz = Get-DbaCmObject -ClassName Win32_TimeZone -ComputerName $computerResolved -Credential $Credential -EnableException
                }
                else {
                    $tz = Get-DbaCmObject -ClassName Win32_TimeZone -ComputerName $computerResolved -EnableException
                }
            }
            catch {
                Stop-Function -Message "Failure collecting TimeZone information on $computer" -Target $computer -ErrorRecord $_
                return
            }

            try {
                if (Test-Bound "Credential") {
                    $powerPlan = Get-DbaCmObject -ClassName Win32_PowerPlan -Namespace "root\cimv2\power" -ComputerName $computerResolved -Credential $Credential -EnableException | Select-Object ElementName, InstanceId, IsActive
                }
                else {
                    $powerPlan = Get-DbaCmObject -ClassName Win32_PowerPlan -Namespace "root\cimv2\power" -ComputerName $computerResolved -EnableException | Select-Object ElementName, InstanceId, IsActive
                }
            }
            catch {
                Stop-Function -Message "Failure collecting PowerPlan information on $computer" -Target $computer -ErrorRecord $_
                return
            }

            $activePowerPlan = ($powerPlan | Where-Object IsActive).ElementName -join ','
            $language = Get-Language $os.OSLanguage

            [PSCustomObject]@{
                ComputerName             = $computerResolved
                Manufacturer             = $os.Manufacturer
                Organization             = $os.Organization
                Architecture             = $os.OSArchitecture
                Version                  = $os.Version
                Build                    = $os.BuildNumber
                Caption                  = $os.Caption
                InstallDate              = [DbaDateTime]$os.InstallDate
                LastBootTime             = [DbaDateTime]$os.LastBootUpTime
                LocalDateTime            = [DbaDateTime]$os.LocalDateTime
                PowerShellVersion        = "$($psVersion.Major).$($psVersion.Minor)"
                TimeZone                 = $tz.Caption
                TimeZoneStandard         = $tz.StandardName
                TimeZoneDaylight         = $tz.DaylightName
                BootDevice               = $os.BootDevice
                TotalVisibleMemory       = [DbaSize]($os.TotalVisibleMemorySize * 1024)
                FreePhysicalMemory       = [DbaSize]($os.FreePhysicalMemory * 1024)
                TotalVirtualMemory       = [DbaSize]($os.TotalVirtualMemorySize * 1024)
                FreeVirtualMemory        = [DbaSize]($os.FreeVirtualMemory * 1024)
                ActivePowerPlan          = $activePowerPlan
                Language                 = $language.Name
                LanguageId               = $language.LCID
                LanguageKeyboardLayoutId = $language.KeyboardLayoutId
                LanguageTwoLetter        = $language.TwoLetterISOLanguageName
                LanguageThreeLetter      = $language.ThreeLetterISOLanguageName
                LanguageAlias            = $language.DisplayName
                LanguageNative           = $language.NativeName
                CodeSet                  = $os.CodeSet
                CountryCode              = $os.CountryCode
                Locale                   = $os.Locale
            } | Select-DefaultView -Property ComputerName, Manufacturer, Organization, Architecture, Version, Caption, LastBootTime, LocalDateTime, PowerShellVersion, TimeZone, TotalVisibleMemory, ActivePowerPlan, LanguageNative
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaOrphanUser {
    <#
        .SYNOPSIS
            Get orphaned users.

        .DESCRIPTION
            An orphan user is defined by a user that does not have their matching login. (Login property = "").

        .PARAMETER SqlInstance
            The SQL Server Instance to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies the database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies the database(s) to exclude from processing. Options for this list are auto-populated from the server

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Orphan, Database, User, Security, Login
            Author: Claudio Silva (@ClaudioESSilva)
            Author: Garry Bargsley (@gbargsley)
            Editor: Simone Bizzotto (@niphlod)
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
        .LINK

            https://dbatools.io/Get-DbaOrphanUser

        .EXAMPLE
            Get-DbaOrphanUser -SqlInstance localhost\sql2016
            Finds all orphan users without matching Logins in all databases present on server 'localhost\sql2016'.

        .EXAMPLE
            Get-DbaOrphanUser -SqlInstance localhost\sql2016 -SqlCredential $cred
            Finds all orphan users without matching Logins in all databases present on server 'localhost\sql2016'. SQL Server authentication will be used in connecting to the server.

        .EXAMPLE
            Get-DbaOrphanUser -SqlInstance localhost\sql2016 -Database db1
            Finds orphan users without matching Logins in the db1 database present on server 'localhost\sql2016'.

    #>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Write-Message -Level Warning -Message "Failed to connect to: $instance."
                continue
            }
            $DatabaseCollection = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                $DatabaseCollection = $DatabaseCollection | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $DatabaseCollection = $DatabaseCollection | Where-Object Name -NotIn $ExcludeDatabase
            }

            if ($DatabaseCollection.Count -gt 0) {
                foreach ($db in $DatabaseCollection) {
                    try {
                        #if SQL 2012 or higher only validate databases with ContainmentType = NONE
                        if ($server.versionMajor -gt 10) {
                            if ($db.ContainmentType -ne [Microsoft.SqlServer.Management.Smo.ContainmentType]::None) {
                                Write-Message -Level Warning -Message "Database '$db' is a contained database. Contained databases can't have orphaned users. Skipping validation."
                                Continue
                            }
                        }
                        Write-Message -Level Verbose -Message "Validating users on database '$db'."
                        $UsersToWork = $db.Users | Where-Object { $_.Login -eq "" -and ($_.ID -gt 4) -and ($_.Sid.Length -gt 16 -and $_.LoginType -eq [Microsoft.SqlServer.Management.Smo.LoginType]::SqlLogin) -eq $false }

                        if ($UsersToWork.Count -gt 0) {
                            Write-Message -Level Verbose -Message "Orphan users found"
                            foreach ($user in $UsersToWork) {
                                [PSCustomObject]@{
                                    ComputerName = $server.ComputerName
                                    InstanceName = $server.ServiceName
                                    SqlInstance  = $server.DomainInstanceName
                                    DatabaseName = $db.Name
                                    User         = $user.Name
                                }
                            }
                        }
                        else {
                            Write-Message -Level Verbose -Message "No orphan users found on database '$db'."
                        }
                        #reset collection
                        $UsersToWork = $null
                    }
                    catch {
                        Stop-Function -Message $_ -Continue
                    }
                }
            }
            else {
                Write-Message -Level VeryVerbose -Message "There are no databases to analyse."
            }
        }
    }

}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#

function Get-DbaPageFileSetting {
<#
    .SYNOPSIS
        Returns information on the pagefile configuration of the target computer.
    
    .DESCRIPTION
        This command uses CIM (or other, related computer management tools) to detect the pagefile configuration of the target compuer(s).
        Note that this may require local administrator privileges for the relevant computers.
    
    .PARAMETER ComputerName
        The Server that you're connecting to.
        This can be the name of a computer, a SMO object, an IP address, an AD COmputer object, a connection string or a SQL Instance.
    
    .PARAMETER Credential
        Credential object used to connect to the Computer as a different user
    
    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
    
    .NOTES
        Tags: CIM
        Author: Klaas Vandenberghe ( @PowerDBAKlaas )
        
        dbatools PowerShell module (https://dbatools.io)
        Copyright (C) 2016 Chrissy LeMaire
        License: MIT https://opensource.org/licenses/MIT
    
    .EXAMPLE
        Get-DbaPageFileSetting -ComputerName ServerA,ServerB
        
        Returns a custom object displaying ComputerName, AutoPageFile, FileName, Status, LastModified, LastAccessed, AllocatedBaseSize, InitialSize, MaximumSize, PeakUsage, CurrentUsage  for ServerA and ServerB
    
    .EXAMPLE
        'ServerA' | Get-DbaPageFileSetting
        
        Returns a custom object displaying ComputerName, AutoPageFile, FileName, Status, LastModified, LastAccessed, AllocatedBaseSize, InitialSize, MaximumSize, PeakUsage, CurrentUsage  for ServerA
    
    .LINK
        https://dbatools.io/Get-DbaPageFileSetting
#>
    
    [CmdletBinding()]
    param (
        [Parameter(Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias("cn", "host", "ServerInstance", "Server", "SqlServer")]
        [DbaInstance]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($computer in $ComputerName) {
            Write-Message -Level VeryVerbose -Message "Connecting to $($computer.ComputerName)" -Target $computer
            $splatDbaCmObject = @{
                ComputerName   = $computer
                EnableException = $true
            }
            if ($Credential) { $splatDbaCmObject["Credential"] = $Credential }
            
            try {
                $compSys = Get-DbaCmObject @splatDbaCmObject -Query "SELECT * FROM win32_computersystem"
                if (-not $CompSys.automaticmanagedpagefile) {
                    $pagefiles = Get-DbaCmObject @splatDbaCmObject -Query "SELECT * FROM win32_pagefile"
                    $pagefileUsages = Get-DbaCmObject @splatDbaCmObject -Query "SELECT * FROM win32_pagefileUsage"
                    $pagefileSettings = Get-DbaCmObject @splatDbaCmObject -Query "SELECT * FROM win32_pagefileSetting"
                }
            }
            catch {
                Stop-Function -Message "Failed to retrieve information from $($computer.ComputerName)" -ErrorRecord $_ -Target $computer -Continue
            }
            
            if (-not $CompSys.automaticmanagedpagefile) {
                foreach ($file in $pagefiles) {
                    $settings = $pagefileSettings | Where-Object Name -EQ $file.Name
                    $usage = $pagefileUsages | Where-Object Name -EQ $file.Name
                    
                    # pagefile is not automatic managed, so return settings
                    New-Object Sqlcollaborative.Dbatools.Computer.PageFileSetting -Property @{
                        ComputerName          = $computer.ComputerName
                        AutoPageFile          = $CompSys.automaticmanagedpagefile
                        FileName              = $file.name
                        Status                = $file.status
                        SystemManaged         = ($settings.InitialSize -eq 0) -and ($settings.MaximumSize -eq 0)
                        LastModified          = $file.LastModified
                        LastAccessed          = $file.LastAccessed
                        AllocatedBaseSize     = $usage.AllocatedBaseSize # in MB, between Initial and Maximum Size
                        InitialSize           = $settings.InitialSize # in MB
                        MaximumSize           = $settings.MaximumSize # in MB
                        PeakUsage             = $usage.peakusage # in MB
                        CurrentUsage          = $usage.currentusage # in MB
                    }
                }
            }
            else {
                # pagefile is automatic managed, so there are no settings
                New-Object Sqlcollaborative.Dbatools.Computer.PageFileSetting -Property @{
                    ComputerName          = $computer
                    AutoPageFile          = $CompSys.automaticmanagedpagefile
                    FileName              = $null
                    Status                = $null
                    SystemManaged         = $null
                    LastModified          = $null
                    LastAccessed          = $null
                    AllocatedBaseSize     = $null
                    InitialSize           = $null
                    MaximumSize           = $null
                    PeakUsage             = $null
                    CurrentUsage          = $null
                }
            }
        }
    }
}
function Get-DbaPermission {
    <#
        .SYNOPSIS
            Get a list of Server and Database level permissions

        .DESCRIPTION
            Retrieves a list of permissions

            Permissions link principals to securables.
            Principals exist on Windows, Instance and Database level.
            Securables exist on Instance and Database level.
            A permission state can be GRANT, DENY or REVOKE.
            The permission type can be SELECT, CONNECT, EXECUTE and more.

            See https://msdn.microsoft.com/en-us/library/ms191291.aspx for more information

        .PARAMETER SqlInstance
            The SQL Server instance to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies one or more database(s) to process. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies one or more database(s) to exclude from processing.

        .PARAMETER IncludeServerLevel
            If this switch is enabled, information about Server Level Permissions will be output.

        .PARAMETER NoSystemObjects
            If this switch is enabled, permissions on system securables will be excluded.

        .PARAMETER EnableException
            If this switch is enabled exceptions will be thrown to the caller, which will need to perform its own exception processing. Otherwise, the function will try to catch the exception, interpret it and provide a friendly error message.

        .NOTES
            Tags: Permissions, Databases
            Author: Klaas Vandenberghe ( @PowerDBAKlaas )

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaPermission

        .EXAMPLE
            Get-DbaPermission -SqlInstance ServerA\sql987

            Returns a custom object with Server name, Database name, permission state, permission type, grantee and securable.

        .EXAMPLE
            Get-DbaPermission -SqlInstance ServerA\sql987 | Format-Table -AutoSize

            Returns a formatted table displaying Server, Database, permission state, permission type, grantee, granteetype, securable and securabletype.

        .EXAMPLE
            Get-DbaPermission -SqlInstance ServerA\sql987 -NoSystemObjects -IncludeServerLevel

            Returns a custom object with Server name, Database name, permission state, permission type, grantee and securable
            in all databases and on the server level, but not on system securables.

        .EXAMPLE
            Get-DbaPermission -SqlInstance sql2016 -Database master

            Returns a custom object with permissions for the master database.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$IncludeServerLevel,
        [switch]$NoSystemObjects,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        if ($NoSystemObjects) {
            $ExcludeSystemObjectssql = "WHERE major_id > 0 "
        }

        $ServPermsql = "SELECT SERVERPROPERTY('MachineName') AS ComputerName,
                       ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
                       SERVERPROPERTY('ServerName') AS SqlInstance
                        , [Database] = ''
                        , [PermState] = state_desc
                        , [PermissionName] = permission_name
                        , [SecurableType] = COALESCE(o.type_desc,sp.class_desc)
                        , [Securable] = CASE	WHEN class = 100 THEN @@SERVERNAME
                                                WHEN class = 105 THEN OBJECT_NAME(major_id)
                                                ELSE OBJECT_NAME(major_id)
                                                END
                        , [Grantee] = SUSER_NAME(grantee_principal_id)
                        , [GranteeType] = pr.type_desc
                        , [revokeStatement] = 'REVOKE ' + permission_name + ' ' + COALESCE(OBJECT_NAME(major_id),'') + ' FROM [' + SUSER_NAME(grantee_principal_id) + ']'
                        , [grantStatement] = 'GRANT ' + permission_name + ' ' + COALESCE(OBJECT_NAME(major_id),'') + ' TO [' + SUSER_NAME(grantee_principal_id) + ']'
                    FROM sys.server_permissions sp
                        JOIN sys.server_principals pr ON pr.principal_id = sp.grantee_principal_id
                        LEFT OUTER JOIN sys.all_objects o ON o.object_id = sp.major_id

                    $ExcludeSystemObjectssql

                    UNION ALL
                    SELECT	  SERVERPROPERTY('MachineName') AS ComputerName
                            , ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName
                            , SERVERPROPERTY('ServerName') AS SqlInstance
                            , [database] = ''
                            , [PermState] = 'GRANT'
                            , [PermissionName] = pb.[permission_name]
                            , [SecurableType] = pb.class_desc
                            , [Securable] = @@SERVERNAME
                            , [Grantee] = spr.name
                            , [GranteeType] = spr.type_desc
                            , [revokestatement] = ''
                            , [grantstatement] = ''
                    FROM sys.server_principals AS spr
                    INNER JOIN sys.fn_builtin_permissions('SERVER') AS pb ON
                        spr.[name]='bulkadmin' AND pb.[permission_name]='ADMINISTER BULK OPERATIONS'
                        OR
                        spr.[name]='dbcreator' AND pb.[permission_name]='CREATE ANY DATABASE'
                        OR
                        spr.[name]='diskadmin' AND pb.[permission_name]='ALTER RESOURCES'
                        OR
                        spr.[name]='processadmin' AND pb.[permission_name] IN ('ALTER ANY CONNECTION', 'ALTER SERVER STATE')
                        OR
                        spr.[name]='sysadmin' AND pb.[permission_name]='CONTROL SERVER'
                        OR
                        spr.[name]='securityadmin' AND pb.[permission_name]='ALTER ANY LOGIN'
                        OR
                        spr.[name]='serveradmin'  AND pb.[permission_name] IN ('ALTER ANY ENDPOINT', 'ALTER RESOURCES','ALTER SERVER STATE', 'ALTER SETTINGS','SHUTDOWN', 'VIEW SERVER STATE')
                        OR
                        spr.[name]='setupadmin' AND pb.[permission_name]='ALTER ANY LINKED SERVER'
                    WHERE spr.[type]='R'
                    ;"

        $DBPermsql = "SELECT SERVERPROPERTY('MachineName') AS ComputerName,
                    ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
                    SERVERPROPERTY('ServerName') AS SqlInstance
                    , [Database] = DB_NAME()
                    , [PermState] = state_desc
                    , [PermissionName] = permission_name
                    , [SecurableType] = COALESCE(o.type_desc,dp.class_desc)
                    , [Securable] = CASE	WHEN class = 0 THEN DB_NAME()
                                            WHEN class = 1 THEN ISNULL(s.name + '.','')+OBJECT_NAME(major_id)
                                            WHEN class = 3 THEN SCHEMA_NAME(major_id)
                                            WHEN class = 6 THEN SCHEMA_NAME(t.schema_id)+'.' + t.name
                                            END
                    , [Grantee] = USER_NAME(grantee_principal_id)
                    , [GranteeType] = pr.type_desc
                    , [revokeStatement] = 'REVOKE ' + permission_name + ' ON ' + isnull(schema_name(o.object_id)+'.','')+OBJECT_NAME(major_id)+ ' FROM [' + USER_NAME(grantee_principal_id) + ']'
                    , [grantStatement] = 'GRANT ' + permission_name + ' ON ' + isnull(schema_name(o.object_id)+'.','')+OBJECT_NAME(major_id)+ ' TO [' + USER_NAME(grantee_principal_id) + ']'
                FROM sys.database_permissions dp
                    JOIN sys.database_principals pr ON pr.principal_id = dp.grantee_principal_id
                    LEFT OUTER JOIN sys.all_objects o ON o.object_id = dp.major_id
                    LEFT OUTER JOIN sys.schemas s ON s.schema_id = o.schema_id
                    LEFT OUTER JOIN sys.types t on t.user_type_id = dp.major_id

                $ExcludeSystemObjectssql

                UNION ALL
                SELECT	  SERVERPROPERTY('MachineName') AS ComputerName
                        , ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName
                        , SERVERPROPERTY('ServerName') AS SqlInstance
                        , [database] = DB_NAME()
                        , [PermState] = ''
                        , [PermissionName] = p.[permission_name]
                        , [SecurableType] = p.class_desc
                        , [Securable] = DB_NAME()
                        , [Grantee] = dp.name
                        , [GranteeType] = dp.type_desc
                        , [revokestatement] = ''
                        , [grantstatement] = ''
                FROM sys.database_principals AS dp
                INNER JOIN sys.fn_builtin_permissions('DATABASE') AS p ON
                    dp.[name]='db_accessadmin' AND p.[permission_name] IN ('ALTER ANY USER', 'CREATE SCHEMA')
                    OR
                    dp.[name]='db_backupoperator' AND p.[permission_name] IN ('BACKUP DATABASE', 'BACKUP LOG', 'CHECKPOINT')
                    OR
                    dp.[name] IN ('db_datareader', 'db_denydatareader') AND p.[permission_name]='SELECT'
                    OR
                    dp.[name] IN ('db_datawriter', 'db_denydatawriter') AND p.[permission_name] IN ('INSERT', 'DELETE', 'UPDATE')
                    OR
                    dp.[name]='db_ddladmin' AND
                    p.[permission_name] IN ('ALTER ANY ASSEMBLY', 'ALTER ANY ASYMMETRIC KEY',
                                            'ALTER ANY CERTIFICATE', 'ALTER ANY CONTRACT',
                                            'ALTER ANY DATABASE DDL TRIGGER', 'ALTER ANY DATABASE EVENT',
                                            'NOTIFICATION', 'ALTER ANY DATASPACE', 'ALTER ANY FULLTEXT CATALOG',
                                            'ALTER ANY MESSAGE TYPE', 'ALTER ANY REMOTE SERVICE BINDING',
                                            'ALTER ANY ROUTE', 'ALTER ANY SCHEMA', 'ALTER ANY SERVICE',
                                            'ALTER ANY SYMMETRIC KEY', 'CHECKPOINT', 'CREATE AGGREGATE',
                                            'CREATE DEFAULT', 'CREATE FUNCTION', 'CREATE PROCEDURE',
                                            'CREATE QUEUE', 'CREATE RULE', 'CREATE SYNONYM', 'CREATE TABLE',
                                            'CREATE TYPE', 'CREATE VIEW', 'CREATE XML SCHEMA COLLECTION',
                                            'REFERENCES')
                    OR
                    dp.[name]='db_owner' AND p.[permission_name]='CONTROL'
                    OR
                    dp.[name]='db_securityadmin' AND p.[permission_name] IN ('ALTER ANY APPLICATION ROLE', 'ALTER ANY ROLE', 'CREATE SCHEMA', 'VIEW DEFINITION')

                WHERE dp.[type]='R'
                    AND dp.is_fixed_role=1
                ;"
    }

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance."

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($IncludeServerLevel) {
                Write-Message -Level Debug -Message "T-SQL: $ServPermsql"
                $server.Query($ServPermsql)
            }

            $dbs = $server.Databases

            if ($Database) {
                $dbs = $dbs | Where-Object Name -In $Database
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $dbs) {
                Write-Message -Level Verbose -Message "Processing $db on $instance."

                if ($db.IsAccessible -eq $false) {
                    Write-Warning "The database $db is not accessible. Skipping database."
                    Continue
                }

                Write-Message -Level Debug -Message "T-SQL: $DBPermsql"
                $db.ExecuteWithResults($DBPermsql).Tables.Rows
            }
        }
    }
}
function Get-DbaPfAvailableCounter {
    <#
        .SYNOPSIS
            Gathers list of all available counters on local or remote machines.

        .DESCRIPTION
            Gathers list of all available counters on local or remote machines. Note, if you pass a credential object, it will be included in the output for easy reuse in your next piped command.

            Thanks to Daniel Streefkerk for this super fast way of counters
            https://daniel.streefkerkonline.com/2016/02/18/use-powershell-to-list-all-windows-performance-counters-and-their-numeric-ids

        .PARAMETER ComputerName
            The target computer. Defaults to localhost.

        .PARAMETER Credential
            Allows you to login to servers using alternative credentials. To use:

            $scred = Get-Credential, then pass $scred object to the -Credential parameter.

        .PARAMETER Pattern
            Specify a pattern for filtering.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Performance, DataCollector, PerfCounter
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaPfAvailableCounter

        .EXAMPLE
            Get-DbaPfAvailableCounter

            Gets all available counters on the local machine.

        .EXAMPLE
            Get-DbaPfAvailableCounter -Pattern *sql*

            Gets all counters matching sql on the local machine.

        .EXAMPLE
            Get-DbaPfAvailableCounter -ComputerName sql2017 -Pattern *sql*

            Gets all counters matching sql on the remote server sql2017.

        .EXAMPLE
            Get-DbaPfAvailableCounter -Pattern *sql*

            Gets all counters matching sql on the local machine.

        .EXAMPLE
            Get-DbaPfAvailableCounter -Pattern *sql* | Add-DbaPfDataCollectorCounter -CollectorSet 'Test Collector Set' -Collector DataCollector01

           Adds all counters matching "sql" to the DataCollector01 within the 'Test Collector Set' CollectorSet.
    #>
    [CmdletBinding()]
    param (
        [DbaInstance[]]$ComputerName = $env:ComputerName,
        [PSCredential]$Credential,
        [string]$Pattern,
        [switch]$EnableException
    )
    begin {
        $scriptblock = {
            $counters = Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\009' -Name 'counter' | Select-Object -ExpandProperty Counter |
                Where-Object { $_ -notmatch '[0-90000]' } | Sort-Object | Get-Unique

            foreach ($counter in $counters) {
                [pscustomobject]@{
                    ComputerName = $env:COMPUTERNAME
                    Name         = $counter
                    Credential   = $args
                }
            }
        }

        # In case people really want a "like" search, which is slower
        $Pattern = $Pattern.Replace("*", ".*").Replace("..*", ".*")
    }
    process {
        foreach ($computer in $ComputerName) {
            Write-Message -Level Verbose -Message "Connecting to $computer using Invoke-Command."

            try {
                if ($pattern) {
                    Invoke-Command2 -ComputerName $computer -Credential $Credential -ScriptBlock $scriptblock -ArgumentList $credential -ErrorAction Stop |
                        Where-Object Name -match $pattern | Select-DefaultView -ExcludeProperty Credential
                }
                else {
                    Invoke-Command2 -ComputerName $computer -Credential $Credential -ScriptBlock $scriptblock -ArgumentList $credential -ErrorAction Stop |
                        Select-DefaultView -ExcludeProperty Credential
                }
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_ -Target $computer -Continue
            }
        }
    }
}
function Get-DbaPfDataCollector {
    <#
        .SYNOPSIS
            Gets Performance Monitor Data Collectors.

        .DESCRIPTION
           Gets Performance Monitor Data Collectors.

        .PARAMETER ComputerName
            The target computer. Defaults to localhost.

        .PARAMETER Credential
            Allows you to login to servers using alternative credentials. To use:

            $scred = Get-Credential, then pass $scred object to the -Credential parameter.

        .PARAMETER CollectorSet
            The Collector Set name.

        .PARAMETER Collector
            The Collector name.

        .PARAMETER InputObject
            Accepts the object output by Get-DbaPfDataCollectorSet via the pipeline.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: PerfMon

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaPfDataCollector

        .EXAMPLE
            Get-DbaPfDataCollector

            Gets all Collectors on localhost.

        .EXAMPLE
            Get-DbaPfDataCollector -ComputerName sql2017

            Gets all Collectors on sql2017.

        .EXAMPLE
            Get-DbaPfDataCollector -ComputerName sql2017, sql2016 -Credential (Get-Credential) -CollectorSet 'System Correlation'

            Gets all Collectors for the 'System Correlation' CollectorSet on sql2017 and sql2016 using alternative credentials.

        .EXAMPLE
            Get-DbaPfDataCollectorSet -CollectorSet 'System Correlation' | Get-DbaPfDataCollector

            Gets all Collectors for the 'System Correlation' CollectorSet.
    #>
    [CmdletBinding()]
    param (
        [DbaInstance[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias("DataCollectorSet")]
        [string[]]$CollectorSet,
        [Alias("DataCollector")]
        [string[]]$Collector,
        [parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [switch]$EnableException
    )
    begin {
        $columns = 'ComputerName', 'DataCollectorSet', 'Name', 'DataCollectorType', 'DataSourceName', 'FileName', 'FileNameFormat', 'FileNameFormatPattern', 'LatestOutputLocation', 'LogAppend', 'LogCircular', 'LogFileFormat', 'LogOverwrite', 'SampleInterval', 'SegmentMaxRecords', 'Counters'
    }
    process {
        if ($InputObject.Credential -and (Test-Bound -ParameterName Credential -Not)) {
            $Credential = $InputObject.Credential
        }

        if (-not $InputObject -or ($InputObject -and (Test-Bound -ParameterName ComputerName))) {
            foreach ($computer in $ComputerName) {
                $InputObject += Get-DbaPfDataCollectorSet -ComputerName $computer -Credential $Credential -CollectorSet $CollectorSet
            }
        }

        if ($InputObject) {
            if (-not $InputObject.DataCollectorSetObject) {
                Stop-Function -Message "InputObject is not of the right type. Please use Get-DbaPfDataCollectorSet."
                return
            }
        }

        foreach ($set in $InputObject) {
            $collectorxml = ([xml]$set.Xml).DataCollectorSet.PerformanceCounterDataCollector
            foreach ($col in $collectorxml) {
                if ($Collector -and $Collector -notcontains $col.Name) {
                    continue
                }

                $outputlocation = $col.LatestOutputLocation
                if ($outputlocation) {
                    $dir = ($outputlocation).Replace(':', '$')
                    $remote = "\\$($set.ComputerName)\$dir"
                }
                else {
                    $remote = $null
                }

                [pscustomobject]@{
                    ComputerName               = $set.ComputerName
                    DataCollectorSet           = $set.Name
                    Name                       = $col.Name
                    FileName                   = $col.FileName
                    DataCollectorType          = $col.DataCollectorType
                    FileNameFormat             = $col.FileNameFormat
                    FileNameFormatPattern      = $col.FileNameFormatPattern
                    LogAppend                  = $col.LogAppend
                    LogCircular                = $col.LogCircular
                    LogOverwrite               = $col.LogOverwrite
                    LatestOutputLocation       = $col.LatestOutputLocation
                    DataCollectorSetXml        = $set.Xml
                    RemoteLatestOutputLocation = $remote
                    DataSourceName             = $col.DataSourceName
                    SampleInterval             = $col.SampleInterval
                    SegmentMaxRecords          = $col.SegmentMaxRecords
                    LogFileFormat              = $col.LogFileFormat
                    Counters                   = $col.Counter
                    CounterDisplayNames        = $col.CounterDisplayName
                    CollectorXml               = $col
                    DataCollectorObject        = $true
                    Credential                 = $Credential
                } | Select-DefaultView -Property $columns
            }
        }
    }
}
function Get-DbaPfDataCollectorCounter {
    <#
        .SYNOPSIS
            Gets Performance Counters.

        .DESCRIPTION
            Gets Performance Counters.

        .PARAMETER ComputerName
            The target computer. Defaults to localhost.

        .PARAMETER Credential
            Allows you to login to servers using alternative credentials. To use:

            $scred = Get-Credential, then pass $scred object to the -Credential parameter.

        .PARAMETER CollectorSet
            The Collector Set name.
  
        .PARAMETER Collector
            The Collector name.
   
        .PARAMETER Counter
            The Counter name to capture. This must be in the form of '\Processor(_Total)\% Processor Time'.
    
        .PARAMETER InputObject
            Accepts the object output by Get-DbaPfDataCollectorSet via the pipeline.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
    
        .NOTES
            Tags: PerfMon

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
    
        .LINK
            https://dbatools.io/Get-DbaPfDataCollectorCounter

        .EXAMPLE
            Get-DbaPfDataCollectorCounter
    
            Gets all counters for all Collector Sets on localhost.

        .EXAMPLE
            Get-DbaPfDataCollectorCounter -ComputerName sql2017
    
            Gets all counters for all Collector Sets on  on sql2017.
    
        .EXAMPLE
            Get-DbaPfDataCollectorCounter -ComputerName sql2017 -Counter '\Processor(_Total)\% Processor Time'

            Gets the '\Processor(_Total)\% Processor Time' counter on sql2017.
    
        .EXAMPLE
            Get-DbaPfDataCollectorCounter -ComputerName sql2017, sql2016 -Credential (Get-Credential) -CollectorSet 'System Correlation'
    
            Gets all counters for the 'System Correlation' CollectorSet on sql2017 and sql2016 using alternative credentials.
    
        .EXAMPLE
            Get-DbaPfDataCollectorSet -CollectorSet 'System Correlation' | Get-DbaPfDataCollector | Get-DbaPfDataCollectorCounter
    
            Gets all counters for the 'System Correlation' CollectorSet.
    #>
    [CmdletBinding()]
    param (
        [DbaInstance[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias("DataCollectorSet")]
        [string[]]$CollectorSet,
        [Alias("DataCollector")]
        [string[]]$Collector,
        [string[]]$Counter,
        [parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [switch]$EnableException
    )
    begin {
        $columns = 'ComputerName', 'Name', 'DataCollectorSet', 'Counters', 'DataCollectorType', 'DataSourceName', 'FileName', 'FileNameFormat', 'FileNameFormatPattern', 'LatestOutputLocation', 'LogAppend', 'LogCircular', 'LogFileFormat', 'LogOverwrite', 'SampleInterval', 'SegmentMaxRecords'
    }
    process {
        if ($InputObject.Credential -and (Test-Bound -ParameterName Credential -Not)) {
            $Credential = $InputObject.Credential
        }
        
        if (-not $InputObject -or ($InputObject -and (Test-Bound -ParameterName ComputerName))) {
            foreach ($computer in $ComputerName) {
                $InputObject += Get-DbaPfDataCollector -ComputerName $computer -Credential $Credential -CollectorSet $CollectorSet -Collector $Collector
            }
        }
        
        if ($InputObject) {
            if (-not $InputObject.DataCollectorObject) {
                Stop-Function -Message "InputObject is not of the right type. Please use Get-DbaPfDataCollector."
                return
            }
        }
        
        foreach ($counterobject in $InputObject) {
            foreach ($countername in $counterobject.Counters) {
                if ($Counter -and $Counter -notcontains $countername) { continue }
                [pscustomobject]@{
                    ComputerName        = $counterobject.ComputerName
                    DataCollectorSet    = $counterobject.DataCollectorSet
                    DataCollector       = $counterobject.Name
                    DataCollectorSetXml = $counterobject.DataCollectorSetXml
                    Name                = $countername
                    FileName            = $counterobject.FileName
                    CounterObject       = $true
                    Credential          = $Credential
                } | Select-DefaultView -ExcludeProperty DataCollectorObject, Credential, CounterObject, DataCollectorSetXml
            }
        }
    }
}
function Get-DbaPfDataCollectorCounterSample {
    <#
        .SYNOPSIS
            Gets Performance Counter Samples.

        .DESCRIPTION
            Gets Performance Counter Samples.

        .PARAMETER ComputerName
            The target computer. Defaults to localhost.

        .PARAMETER Credential
            Allows you to login to servers using alternative credentials. To use:

            $scred = Get-Credential, then pass $scred object to the -Credential parameter.

        .PARAMETER CollectorSet
            The Collector Set name.

        .PARAMETER Collector
            The Collector name.

        .PARAMETER Counter
            The Counter name. This must be in the form of '\Processor(_Total)\% Processor Time'.

        .PARAMETER Continuous
           If this switch is enabled, samples will be retrieved continuously until you press CTRL+C. By default, this command gets only one counter sample. You can use the SampleInterval parameter to set the interval for continuous sampling.

        .PARAMETER ListSet
            Gets the specified performance counter sets on the computers. Enter the names of the counter sets. Wildcards are permitted.

        .PARAMETER MaxSamples
            Specifies the number of samples to get from each counter. The default is 1 sample. To get samples continuously (no maximum sample size), use the Continuous parameter.

            To collect a very large data set, consider running a Get-DbaPfDataCollectorCounterSample command as a Windows PowerShell background job.

        .PARAMETER SampleInterval
            Specifies the time between samples in seconds. The minimum value and the default value are 1 second.

        .PARAMETER InputObject
            Accepts the object output by Get-DbaPfDataCollectorCounter via the pipeline.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: PerfMon

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaPfDataCollectorCounterSample

        .EXAMPLE
            Get-DbaPfDataCollectorCounterSample

            Gets a single sample for all counters for all Collector Sets on localhost.

        .EXAMPLE
            Get-DbaPfDataCollectorCounterSample -Counter '\Processor(_Total)\% Processor Time'

            Gets a single sample for all counters for all Collector Sets on localhost.

        .EXAMPLE
            Get-DbaPfDataCollectorCounter -ComputerName sql2017, sql2016 | Out-GridView -PassThru | Get-DbaPfDataCollectorCounterSample -MaxSamples 10

            Gets 10 samples for all counters for all Collector Sets for servers sql2016 and sql2017.

        .EXAMPLE
            Get-DbaPfDataCollectorCounterSample -ComputerName sql2017

            Gets a single sample for all counters for all Collector Sets on sql2017.

        .EXAMPLE
            Get-DbaPfDataCollectorCounterSample -ComputerName sql2017, sql2016 -Credential (Get-Credential) -CollectorSet 'System Correlation'

            Gets a single sample for all counters for the 'System Correlation' CollectorSet on sql2017 and sql2016 using alternative credentials.

        .EXAMPLE
            Get-DbaPfDataCollectorCounterSample -CollectorSet 'System Correlation'

            Gets a single sample for all counters for the 'System Correlation' CollectorSet.
    #>
    [CmdletBinding()]
    param (
        [DbaInstance[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias("DataCollectorSet")]
        [string[]]$CollectorSet,
        [Alias("DataCollector")]
        [string[]]$Collector,
        [string[]]$Counter,
        [switch]$Continuous,
        [switch[]]$ListSet,
        [int]$MaxSamples,
        [int]$SampleInterval,
        [parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [switch]$EnableException
    )
    process {
        
        if ($InputObject.Credential -and (Test-Bound -ParameterName Credential -Not)) {
            $Credential = $InputObject.Credential
        }
        
        if ($InputObject.Counter -and (Test-Bound -ParameterName Counter -Not)) {
            $Counter = $InputObject.Counter
        }
        
        if (-not $InputObject -or ($InputObject -and (Test-Bound -ParameterName ComputerName))) {
            foreach ($computer in $ComputerName) {
                $InputObject += Get-DbaPfDataCollectorCounter -ComputerName $computer -Credential $Credential -CollectorSet $CollectorSet -Collector $Collector
            }
        }
        
        if ($InputObject) {
            if (-not $InputObject.CounterObject) {
                Stop-Function -Message "InputObject is not of the right type. Please use Get-DbaPfDataCollectorCounter."
                return
            }
        }
        
        foreach ($counterobject in $InputObject) {
            if ((Test-Bound -ParameterName Counter) -and ($Counter -notcontains $counterobject.Name)) { continue }
            $params = @{
                Counter = $counterobject.Name
            }
            
            if (-not ([dbainstance]$counterobject.ComputerName).IsLocalHost) {
                $params.Add("ComputerName", $counterobject.ComputerName)
            }
            
            if ($Credential) {
                $params.Add("Credential", $Credential)
            }
            
            if ($Continuous) {
                $params.Add("Continuous", $Continuous)
            }
            
            if ($ListSet) {
                $params.Add("ListSet", $ListSet)
            }
            
            if ($MaxSamples) {
                $params.Add("MaxSamples", $MaxSamples)
            }
            
            if ($SampleInterval) {
                $params.Add("SampleInterval", $SampleInterval)
            }
            
            if ($Continuous) {
                Get-Counter @params
            }
            else {
                try {
                    $pscounters = Get-Counter @params -ErrorAction Stop
                }
                catch {
                    Stop-Function -Message "Failure for $($counterobject.Name) on $($counterobject.ComputerName)." -ErrorRecord $_ -Continue
                }
                
                foreach ($pscounter in $pscounters) {
                    foreach ($sample in $pscounter.CounterSamples) {
                        [pscustomobject]@{
                            ComputerName           = $counterobject.ComputerName
                            DataCollectorSet       = $counterobject.DataCollectorSet
                            DataCollector          = $counterobject.DataCollector
                            Name                   = $counterobject.Name
                            Timestamp              = $pscounter.Timestamp
                            Path                   = $sample.Path
                            InstanceName           = $sample.InstanceName
                            CookedValue            = $sample.CookedValue
                            RawValue               = $sample.RawValue
                            SecondValue            = $sample.SecondValue
                            MultipleCount          = $sample.MultipleCount
                            CounterType            = $sample.CounterType
                            SampleTimestamp        = $sample.Timestamp
                            SampleTimestamp100NSec = $sample.Timestamp100NSec
                            Status                 = $sample.Status
                            DefaultScale           = $sample.DefaultScale
                            TimeBase               = $sample.TimeBase
                            Sample                 = $pscounter.CounterSamples
                            CounterSampleObject    = $true
                        } | Select-DefaultView -ExcludeProperty Sample, CounterSampleObject
                    }
                }
            }
        }
    }
}
function Get-DbaPfDataCollectorSet {
    <#
        .SYNOPSIS
            Gets Performance Monitor Data Collector Set.

        .DESCRIPTION
            Gets Performance Monitor Data Collector Set.

        .PARAMETER ComputerName
            The target computer. Defaults to localhost.

        .PARAMETER Credential
            Allows you to login to servers using alternative credentials. To use:

            $scred = Get-Credential, then pass $scred object to the -Credential parameter.

        .PARAMETER CollectorSet
            The Collector set name.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: PerfMon

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaPfDataCollectorSet

        .EXAMPLE
            Get-DbaPfDataCollectorSet

            Gets all Collector Sets on localhost.

        .EXAMPLE
            Get-DbaPfDataCollectorSet -ComputerName sql2017

            Gets all Collector Sets on sql2017.

        .EXAMPLE
            Get-DbaPfDataCollectorSet -ComputerName sql2017 -Credential (Get-Credential) -CollectorSet 'System Correlation'

            Gets the 'System Correlation' CollectorSet on sql2017 using alternative credentials.

        .EXAMPLE
            Get-DbaPfDataCollectorSet | Select *

            Displays extra columns and also exposes the original COM object in DataCollectorSetObject.
    #>
    [CmdletBinding()]
    param (
        [parameter(ValueFromPipeline)]
        [DbaInstance[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias("DataCollectorSet")]
        [string[]]$CollectorSet,
        [switch]$EnableException
    )

    begin {
        $setscript = {
            # Get names / status info
            $schedule = New-Object -ComObject "Schedule.Service"
            $schedule.Connect()
            $folder = $schedule.GetFolder("Microsoft\Windows\PLA")
            $tasks = @()
            $tasknumber = 0
            $done = $false
            do {
                try {
                    $task = $folder.GetTasks($tasknumber)
                    $tasknumber++
                    if ($task) {
                        $tasks += $task
                    }
                }
                catch {
                    $done = $true
                }
            }
            while ($done -eq $false)
            $null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($schedule)

            if ($args[0]) {
                $tasks = $tasks | Where-Object Name -in $args[0]
            }

            $sets = New-Object -ComObject Pla.DataCollectorSet
            foreach ($task in $tasks) {
                $setname = $task.Name
                switch ($task.State) {
                    0 { $state = "Unknown" }
                    1 { $state = "Disabled" }
                    2 { $state = "Queued" }
                    3 { $state = "Ready" }
                    4 { $state = "Running" }
                }

                try {
                    # Query changes $sets so work from there
                    $sets.Query($setname, $null)
                    $set = $sets.PSObject.Copy()

                    $outputlocation = $set.OutputLocation
                    $latestoutputlocation = $set.LatestOutputLocation

                    if ($outputlocation) {
                        $dir = (Split-Path $outputlocation).Replace(':', '$')
                        $remote = "\\$env:COMPUTERNAME\$dir"
                    }
                    else {
                        $remote = $null
                    }

                    if ($latestoutputlocation) {
                        $dir = ($latestoutputlocation).Replace(':', '$')
                        $remotelatest = "\\$env:COMPUTERNAME\$dir"
                    }
                    else {
                        $remote = $null
                    }

                    [pscustomobject]@{
                        ComputerName               = $env:COMPUTERNAME
                        Name                       = $setname
                        LatestOutputLocation       = $set.LatestOutputLocation
                        OutputLocation             = $set.OutputLocation
                        RemoteOutputLocation       = $remote
                        RemoteLatestOutputLocation = $remotelatest
                        RootPath                   = $set.RootPath
                        Duration                   = $set.Duration
                        Description                = $set.Description
                        DescriptionUnresolved      = $set.DescriptionUnresolved
                        DisplayName                = $set.DisplayName
                        DisplayNameUnresolved      = $set.DisplayNameUnresolved
                        Keywords                   = $set.Keywords
                        Segment                    = $set.Segment
                        SegmentMaxDuration         = $set.SegmentMaxDuration
                        SegmentMaxSize             = $set.SegmentMaxSize
                        SerialNumber               = $set.SerialNumber
                        Server                     = $set.Server
                        Status                     = $set.Status
                        Subdirectory               = $set.Subdirectory
                        SubdirectoryFormat         = $set.SubdirectoryFormat
                        SubdirectoryFormatPattern  = $set.SubdirectoryFormatPattern
                        Task                       = $set.Task
                        TaskRunAsSelf              = $set.TaskRunAsSelf
                        TaskArguments              = $set.TaskArguments
                        TaskUserTextArguments      = $set.TaskUserTextArguments
                        Schedules                  = $set.Schedules
                        SchedulesEnabled           = $set.SchedulesEnabled
                        UserAccount                = $set.UserAccount
                        Xml                        = $set.Xml
                        Security                   = $set.Security
                        StopOnCompletion           = $set.StopOnCompletion
                        State                      = $state.Trim()
                        DataCollectorSetObject     = $true
                        TaskObject                 = $task
                        Credential                 = $args[1]
                    }
                }
                catch {
                    Write-Warning -Message "Issue with getting Collector Set $setname on $env:Computername : $_."
                    continue
                }
            }
        }

        $columns = 'ComputerName', 'Name', 'DisplayName', 'Description', 'State', 'Duration', 'OutputLocation', 'LatestOutputLocation',
        'RootPath', 'SchedulesEnabled', 'Segment', 'SegmentMaxDuration', 'SegmentMaxSize',
        'SerialNumber', 'Server', 'StopOnCompletion', 'Subdirectory', 'SubdirectoryFormat',
        'SubdirectoryFormatPattern', 'Task', 'TaskArguments', 'TaskRunAsSelf', 'TaskUserTextArguments', 'UserAccount'
    }
    process {
        foreach ($computer in $ComputerName.ComputerName) {
            Write-Message -Level Verbose -Message "Connecting to $computer using Invoke-Command."
            try {
                Invoke-Command2 -ComputerName $computer -Credential $Credential -ScriptBlock $setscript -ArgumentList $CollectorSet, $Credential -ErrorAction Stop | Select-DefaultView -Property $columns
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_ -Target $computer -Continue
            }
        }
    }
}
function Get-DbaPfDataCollectorSetTemplate {
    <#
        .SYNOPSIS
            Parses Perf Monitor templates. Defaults to parsing templates in the dbatools template repository (\bin\perfmontemplates\).

        .DESCRIPTION
            Parses Perf Monitor XML templates. Defaults to parsing templates in the dbatools template repository (\bin\perfmontemplates\).

        .PARAMETER Path
            The path to the template directory. Defaults to the dbatools template repository (\bin\perfmontemplates\).

        .PARAMETER Pattern
            Specify a pattern for filtering. Alternatively, you can use Out-GridView -Passthru to select objects and pipe them to Import-DbaPfDataCollectorSetTemplate.

        .PARAMETER Template
            Specifies one or more of the templates provided by dbatools. Press tab to cycle through the list to the options.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Performance, DataCollector, PerfCounter
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaPfDataCollectorSetTemplate

        .EXAMPLE
            Get-DbaPfDataCollectorSetTemplate

            Returns information about all the templates in the local dbatools repository.

        .EXAMPLE
            Get-DbaPfDataCollectorSetTemplate | Out-GridView -PassThru | Import-DbaPfDataCollectorSetTemplate -ComputerName sql2017 | Start-DbaPfDataCollectorSet

            Allows you to select a template, then deploys it to sql2017 and immediately starts the DataCollectorSet.

        .EXAMPLE
            Get-DbaPfDataCollectorSetTemplate | Select-Object *

            Returns more information about the template, including the full path/filename.
    #>
    [CmdletBinding()]
    param (
        [string[]]$Path = "$script:PSModuleRoot\bin\perfmontemplates\collectorsets",
        [string]$Pattern,
        [string[]]$Template,
        [switch]$EnableException
    )
    begin {
        $metadata = Import-Clixml "$script:PSModuleRoot\bin\perfmontemplates\collectorsets.xml"
        # In case people really want a "like" search, which is slower
        $Pattern = $Pattern.Replace("*", ".*").Replace("..*", ".*")
    }
    process {
        foreach ($directory in $Path) {
            $files = Get-ChildItem "$directory\*.xml"

            if ($Template) {
                $files = $files | Where-Object BaseName -in $Template
            }

            foreach ($file in $files) {
                try {
                    $xml = [xml](Get-Content $file)
                }
                catch {
                    Stop-Function -Message "Failure" -ErrorRecord $_ -Target $file -Continue
                }

                foreach ($dataset in $xml.DataCollectorSet) {
                    $meta = $metadata | Where-Object Name -eq $dataset.name
                    if ($Pattern) {
                        if (
                            ($dataset.Name -match $Pattern) -or
                            ($dataset.Description -match $Pattern)
                        ) {
                            [pscustomobject]@{
                                Name        = $dataset.name
                                Source      = $meta.Source
                                UserAccount = $dataset.useraccount
                                Description = $dataset.Description
                                Path        = $file
                                File        = $file.Name
                            } | Select-DefaultView -ExcludeProperty File, Path
                        }
                    }
                    else {
                        [pscustomobject]@{
                            Name        = $dataset.name
                            Source      = $meta.Source
                            UserAccount = $dataset.useraccount
                            Description = $dataset.Description
                            Path        = $file
                            File        = $file.Name
                        } | Select-DefaultView -ExcludeProperty File, Path
                    }
                }
            }
        }
    }
}
function Get-DbaPlanCache {
    <#
        .SYNOPSIS
            Provides information about adhoc and prepared plan cache usage

        .DESCRIPTION
            Checks ahoc and prepared plan cache for each database, if over 100 MBS you should consider you using Remove-DbaQueryPlan to clear the plan caches or turning on optimize for adhoc workloads configuration is running 2008 or later.

            References: https://www.sqlskills.com/blogs/kimberly/plan-cache-adhoc-workloads-and-clearing-the-single-use-plan-cache-bloat/

            Note: This command returns results from all SQL server instances on the destination server but the process column is specific to -SqlInstance passed.

        .PARAMETER SqlInstance
            The target SQL Server instance.

        .PARAMETER SqlCredential
           Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Memory
            Author: Tracy Boggiano, databasesuperhero.com

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: GNU GPL v3 https://opensource.org/licenses/GPL-3.0

        .LINK
            https://dbatools.io/Get-DbaPlanCache

        .EXAMPLE
            Get-DbaPlanCache -SqlInstance sql2017

            Returns the single use plan cashe usage information for SQL Server instance 2017

        .EXAMPLE
            Get-DbaPlanCache -SqlInstance sql2017

            Returns the single use plan cashe usage information for SQL Server instance 2017

        .EXAMPLE
            Get-DbaPlanCache -SqlInstance sql2017 -SqlCredential (Get-Credential sqladmin)

            Returns the single use plan cashe usage information for SQL Server instance 2017 using login 'sqladmin'
    #>
        [CmdletBinding()]
        Param (
            [parameter(Mandatory, ValueFromPipeline)]
            [Alias("ServerInstance", "SqlServer", "SqlServers")]
            [DbaInstanceParameter[]]$SqlInstance,
            [PSCredential]$SqlCredential,
            [switch]$EnableException
        )
        begin {
            $Sql = "SELECT SERVERPROPERTY('MachineName') AS ComputerName,
        ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
        SERVERPROPERTY('ServerName') AS SqlInstance, MB = sum(cast((CASE WHEN usecounts = 1 AND objtype IN ('Adhoc', 'Prepared') THEN size_in_bytes ELSE 0 END) as decimal(12, 2))) / 1024 / 1024,
        UseCount = sum(CASE WHEN usecounts = 1 AND objtype IN ('Adhoc', 'Prepared') THEN 1 ELSE 0 END)
        FROM sys.dm_exec_cached_plans;"
        }

        process {
            foreach ($instance in $SqlInstance) {
                try {
                    Write-Message -Level Verbose -Message "Connecting to $instance"
                    $server = Connect-DbaInstance -SqlInstance $instance -SqlCredential $sqlcredential
                }
                catch {
                    Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                }

                $results = $server.Query($sql)
                $size = [dbasize]($results.MB*1024*1024)
                Add-Member -Force -InputObject $results -MemberType NoteProperty -Name Size -Value $size

                Select-DefaultView -InputObject $results -Property ComputerName, InstanceName, SqlInstance, Size, UseCount
            }
        }
    }
function Get-DbaPolicy {
    <#
    .SYNOPSIS
    Returns polices from policy based management from an instance.

    .DESCRIPTION
    Returns details of policies with the option to filter on Category and SystemObjects.

    .PARAMETER SqlInstance
    SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

    .PARAMETER SqlCredential
    Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

    .PARAMETER Policy
    Filters results to only show specific policy

    .PARAMETER Category
    Filters results to only show policies in the category selected

    .PARAMETER IncludeSystemObject
    By default system objects are filtered out. Use this parameter to INCLUDE them .

    .PARAMETER EnableException
    By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
    Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
    Author: Stephen Bennett (https://sqlnotesfromtheunderground.wordpress.com/)
    Tags: Policy, PoilcyBasedManagement

    Website: https://dbatools.io
    Copyright: (C) Chrissy LeMaire, [email protected]
    License: MIT https://opensource.org/licenses/MIT

    .LINK
    https://dbatools.io/Get-DbaPolicy

    .EXAMPLE
    Get-DbaPolicy -SqlInstance sql2016

    Returns all policies from sql2016 server

    .EXAMPLE
    Get-DbaPolicy -SqlInstance sql2016 -SqlCredential $cred

    Uses a credential $cred to connect and return all policies from sql2016 instance

    .EXAMPLE
    Get-DbaPolicy -SqlInstance sql2016 -Category MorningCheck

    Returns all policies from sql2016 server that part of the PolicyCategory MorningCheck
#>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [string[]]$Policy,
        [string[]]$Category,
        [switch]$IncludeSystemObject,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            try {
                $sqlStoreConnection = New-Object Microsoft.SqlServer.Management.Sdk.Sfc.SqlStoreConnection $server.ConnectionContext.SqlConnectionObject
                # DMF is the Declarative Management Framework, Policy Based Management's old name
                $store = New-Object Microsoft.SqlServer.Management.DMF.PolicyStore $sqlStoreConnection
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $server -Continue
            }

            $allpolicies = $store.Policies

            if (-not $IncludeSystemObject) {
                $allpolicies = $allpolicies | Where-Object { $_.IsSystemObject -eq 0 }
            }

            if ($Category) {
                $allpolicies = $allpolicies | Where-Object { $_.PolicyCategory -in $Category }
            }

            if ($Policy) {
                $allpolicies = $allpolicies | Where-Object { $_.Name -in $Policy }
            }

            foreach ($currentpolicy in $allpolicies) {
                Write-Message -Level Verbose -Message "Processing $currentpolicy"
                Add-Member -Force -InputObject $currentpolicy -MemberType NoteProperty ComputerName -value $server.ComputerName
                Add-Member -Force -InputObject $currentpolicy -MemberType NoteProperty InstanceName -value $server.ServiceName
                Add-Member -Force -InputObject $currentpolicy -MemberType NoteProperty SqlInstance -value $server.DomainInstanceName

                Select-DefaultView -InputObject $currentpolicy -ExcludeProperty HelpText, HelpLink, Urn, Properties, Metadata, Parent, IdentityKey, HasScript, PolicyEvaluationStarted, ConnectionProcessingStarted, TargetProcessed, ConnectionProcessingFinished, PolicyEvaluationFinished, PropertyMetadataChanged, PropertyChanged
            }
        }
    }
}
function Get-DbaPrivilege {
    <#
      .SYNOPSIS
      Gets the users with local privileges on one or more computers.

      .DESCRIPTION
      Gets the users with local privileges 'Lock Pages in Memory', 'Instant File Initialization', 'Logon as Batch' on one or more computers.

      Requires Local Admin rights on destination computer(s).

      .PARAMETER ComputerName
      The SQL Server (or server in general) that you're connecting to. This command handles named instances.

      .PARAMETER Credential
      Credential object used to connect to the computer as a different user.

      .PARAMETER EnableException
      By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
      This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
      Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

      .NOTES
      Author: Klaas Vandenberghe ( @PowerDBAKlaas )
      Tags: Privilege
      Website: https://dbatools.io
      Copyright: (C) Chrissy LeMaire, [email protected]
      License: MIT https://opensource.org/licenses/MIT

    .LINK
      https://dbatools.io/Get-DbaPrivilege

      .EXAMPLE
      Get-DbaPrivilege -ComputerName sqlserver2014a

      Gets the local privileges on computer sqlserver2014a.

      .EXAMPLE
      'sql1','sql2','sql3' | Get-DbaPrivilege

      Gets the local privileges on computers sql1, sql2 and sql3.

      .EXAMPLE
      Get-DbaPrivilege -ComputerName sql1,sql2 | Out-Gridview

      Gets the local privileges on computers sql1 and sql2, and shows them in a grid view.

  #>
    [CmdletBinding()]
    Param (
        [parameter(ValueFromPipeline)]
        [Alias("cn", "host", "Server")]
        [dbainstanceparameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [switch][Alias('Silent')]
        $EnableException
    )

    begin {
        $ResolveSID = @"
    function Convert-SIDToUserName ([string] `$SID ) {
      `$objSID = New-Object System.Security.Principal.SecurityIdentifier (`"`$SID`")
      `$objUser = `$objSID.Translate( [System.Security.Principal.NTAccount])
      `$objUser.Value
    }
"@
        $ComputerName = $ComputerName.ComputerName | Select-Object -Unique
    }
    process {
        foreach ($computer in $ComputerName) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $computer"
                if (Test-PSRemoting -ComputerName $Computer) {
                    Write-Message -Level Verbose -Message "Getting Privileges on $Computer"
                    $Priv = $null
                    $Priv = Invoke-Command2 -Raw -ComputerName $computer -Credential $Credential -ScriptBlock {
                        $temp = ([System.IO.Path]::GetTempPath()).TrimEnd(""); secedit /export /cfg $temp\secpolByDbatools.cfg > $NULL;
                        Get-Content $temp\secpolByDbatools.cfg | Where-Object { $_ -match "SeBatchLogonRight" -or $_ -match 'SeManageVolumePrivilege' -or $_ -match 'SeLockMemoryPrivilege' }
                    }

                    Write-Message -Level Verbose -Message "Getting Batch Logon Privileges on $Computer"
                    $BL = Invoke-Command2 -Raw -ComputerName $computer -Credential $Credential -ArgumentList $ResolveSID -ScriptBlock {
                        Param ($ResolveSID)
                        . ([ScriptBlock]::Create($ResolveSID))
                        $temp = ([System.IO.Path]::GetTempPath()).TrimEnd("");
                        (Get-Content $temp\secpolByDbatools.cfg | Where-Object { $_ -match "SeBatchLogonRight" }).substring(20).split(",").replace("`*", "") |
                        ForEach-Object { Convert-SIDToUserName -SID $_ }
                    } -ErrorAction SilentlyContinue
                    if ($BL.count -eq 0) {
                        Write-Message -Level Verbose -Message "No users with Batch Logon Rights on $computer"
                    }

                    Write-Message -Level Verbose -Message "Getting Instant File Initialization Privileges on $Computer"
                    $ifi = Invoke-Command2 -Raw -ComputerName $computer -Credential $Credential -ArgumentList $ResolveSID -ScriptBlock {
                        Param ($ResolveSID)
                        . ([ScriptBlock]::Create($ResolveSID))
                        $temp = ([System.IO.Path]::GetTempPath()).TrimEnd("");
                        (Get-Content $temp\secpolByDbatools.cfg | Where-Object { $_ -like 'SeManageVolumePrivilege*' }).substring(26).split(",").replace("`*", "") |
                        ForEach-Object { Convert-SIDToUserName -SID $_ }
                    } -ErrorAction SilentlyContinue
                    if ($ifi.count -eq 0) {
                        Write-Message -Level Verbose -Message "No users with Instant File Initialization Rights on $computer"
                    }

                    Write-Message -Level Verbose -Message "Getting Lock Pages in Memory Privileges on $Computer"
                    $lpim = Invoke-Command2 -Raw -ComputerName $computer -Credential $Credential -ArgumentList $ResolveSID -ScriptBlock {
                        Param ($ResolveSID)
                        . ([ScriptBlock]::Create($ResolveSID))
                        $temp = ([System.IO.Path]::GetTempPath()).TrimEnd("");
                        (Get-Content $temp\secpolByDbatools.cfg | Where-Object { $_ -like 'SeLockMemoryPrivilege*' }).substring(24).split(",").replace("`*", "") |
                        ForEach-Object { Convert-SIDToUserName -SID $_ }
                    } -ErrorAction SilentlyContinue

                    if ($lpim.count -eq 0) {
                        Write-Message -Level Verbose -Message "No users with Lock Pages in Memory Rights on $computer"
                    }
                    $users = @() + $BL + $ifi + $lpim | Select-Object -Unique
                    $users | ForEach-Object {
                        [PSCustomObject]@{
                            ComputerName                           = $computer
                            User                                   = $_
                            LogonAsBatchPrivilege                  = $BL -contains $_
                            InstantFileInitializationPrivilege     = $ifi -contains $_
                            LockPagesInMemoryPrivilege             = $lpim -contains $_
                        }
                    }
                    Write-Message -Level Verbose -Message "Removing secpol file on $computer"
                    Invoke-Command2 -Raw -ComputerName $computer -Credential $Credential -ScriptBlock { $temp = ([System.IO.Path]::GetTempPath()).TrimEnd(""); Remove-Item $temp\secpolByDbatools.cfg -Force > $NULL }
                }
                else {
                    Write-Message -Level Warning -Message "Failed to connect to $Computer"
                }
            }
            catch {
                Stop-Function -Continue -Message "Failure" -ErrorRecord $_ -Target $computer
            }
        }
    }
}
function Get-DbaProcess {
    <#
        .SYNOPSIS
            This command displays SQL Server processes.

        .DESCRIPTION
            This command displays processes associated with a spid, login, host, program or database.

            Thanks to Michael J Swart at https://sqlperformance.com/2017/07/sql-performance/find-database-connection-leaks for the query to get the last executed SQL statement, minutesasleep and host process ID.

        .PARAMETER SqlInstance
            The SQL Server instance to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Spid
            Specifies one or more process IDs (Spid) to be displayed. Options for this parameter are auto-populated from the server.

        .PARAMETER Login
            Specifies one or more Login names with active processes to look for. Options for this parameter are auto-populated from the server.

        .PARAMETER Hostname
            Specifies one or more hostnames with active processes to look for. Options for this parameter are auto-populated from the server.

        .PARAMETER Program
            Specifies one or more program names with active processes to look for. Options for this parameter are auto-populated from the server.

        .PARAMETER Database
            Specifies one or more databases with active processes to look for. Options for this parameter are auto-populated from the server.

        .PARAMETER ExcludeSpid
            Specifies one ore more process IDs to exclude from display. Options for this parameter are auto-populated from the server.

            This is the last filter to run, so even if a Spid matches another filter, it will be excluded by this filter.

        .PARAMETER NoSystemSpid
            If this switch is enabled, system Spids will be ignored.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Process, Session, ActivityMonitor
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaProcess

        .EXAMPLE
            Get-DbaProcess -SqlInstance sqlserver2014a -Login base\ctrlb, sa

            Shows information about the processes for base\ctrlb and sa on sqlserver2014a. Windows Authentication is used in connecting to sqlserver2014a.

        .EXAMPLE
            Get-DbaProcess -SqlInstance sqlserver2014a -SqlCredential $credential -Spid 56, 77

            Shows information about the processes for spid 56 and 57. Uses alternative (SQL or Windows) credentials to authenticate to sqlserver2014a.

        .EXAMPLE
            Get-DbaProcess -SqlInstance sqlserver2014a -Program 'Microsoft SQL Server Management Studio'

            Shows information about the processes that were created in Microsoft SQL Server Management Studio.

        .EXAMPLE
            Get-DbaProcess -SqlInstance sqlserver2014a -Host workstationx, server100

            Shows information about the processes that were initiated by hosts (computers/clients) workstationx and server 1000.
    #>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [int[]]$Spid,
        [int[]]$ExcludeSpid,
        [string[]]$Database,
        [string[]]$Login,
        [string[]]$Hostname,
        [string[]]$Program,
        [switch]$NoSystemSpid,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $sqlinstance) {

            Write-Message -Message "Connecting to $instance." -Level Verbose
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Could not connect to Sql Server instance $instance : $_" -Target $instance -ErrorRecord $_ -Continue
            }

            $sql = "SELECT datediff(minute, s.last_request_end_time, getdate()) as MinutesAsleep, s.session_id as spid, s.host_process_id as HostProcessId, t.text as Query,
                    s.login_time as LoginTime,s.client_version as ClientVersion, s.last_request_start_time as LastRequestStartTime, s.last_request_end_time as LastRequestEndTime,
                    c.net_transport as NetTransport, c.encrypt_option as EncryptOption, c.auth_scheme as AuthScheme, c.net_packet_size as NetPacketSize, c.client_net_address as ClientNetAddress
                    FROM sys.dm_exec_connections c join sys.dm_exec_sessions s on c.session_id = s.session_id cross apply sys.dm_exec_sql_text(c.most_recent_sql_handle) t"

            if ($server.VersionMajor -gt 8) {
                $results = $server.Query($sql)
            }
            else {
                $results = $null
            }

            $allsessions = @()

            $processes = $server.EnumProcesses()

            if ($Login) {
                $allsessions += $processes | Where-Object { $_.Login -in $Login -and $_.Spid -notin $allsessions.Spid }
            }

            if ($Spid) {
                $allsessions += $processes | Where-Object { ($_.Spid -in $Spid -or $_.BlockingSpid -in $Spid) -and $_.Spid -notin $allsessions.Spid }
            }

            if ($Hostname) {
                $allsessions += $processes | Where-Object { $_.Host -in $Hostname -and $_.Spid -notin $allsessions.Spid }
            }

            if ($Program) {
                $allsessions += $processes | Where-Object { $_.Program -in $Program -and $_.Spid -notin $allsessions.Spid }
            }

            if ($Database) {
                $allsessions += $processes | Where-Object { $Database -contains $_.Database -and $_.Spid -notin $allsessions.Spid }
            }

            if (Test-Bound -not 'Login', 'Spid', 'ExcludeSpid', 'Hostname', 'Program', 'Database') {
                $allsessions = $processes
            }

            if ($NoSystemSpid -eq $true) {
                $allsessions = $allsessions | Where-Object { $_.Spid -gt 50 }
            }

            if ($Exclude) {
                $allsessions = $allsessions | Where-Object { $Exclude -notcontains $_.SPID -and $_.Spid -notin $allsessions.Spid }
            }

            foreach ($session in $allsessions) {

                if ($session.Status -eq "") {
                    $status = "sleeping"
                }
                else {
                    $status = $session.Status
                }

                if ($session.Command -eq "") {
                    $command = "AWAITING COMMAND"
                }
                else {
                    $command = $session.Command
                }

                $row = $results | Where-Object { $_.Spid -eq $session.Spid }

                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name Parent -value $server
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name Status -value $status
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name Command -value $command
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name HostProcessId -value $row.HostProcessId
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name MinutesAsleep -value $row.MinutesAsleep
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name LoginTime -value $row.LoginTime
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name ClientVersion -value $row.ClientVersion
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name LastRequestStartTime -value $row.LastRequestStartTime
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name LastRequestEndTime -value $row.LastRequestEndTime
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name NetTransport -value $row.NetTransport
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name EncryptOption -value $row.EncryptOption
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name AuthScheme -value $row.AuthScheme
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name NetPacketSize -value $row.NetPacketSize
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name ClientNetAddress -value $row.ClientNetAddress
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name LastQuery -value $row.Query

                Select-DefaultView -InputObject $session -Property ComputerName, InstanceName, SqlInstance, Spid, Login, LoginTime, Host, Database, BlockingSpid, Program, Status, Command, Cpu, MemUsage, LastRequestStartTime, LastRequestEndTime, MinutesAsleep, ClientNetAddress, NetTransport, EncryptOption, AuthScheme, NetPacketSize, ClientVersion, HostProcessId, IsSystem, LastQuery
            }
        }
    }
}
function Get-DbaQueryExecutionTime {
    <#
.SYNOPSIS
Displays Stored Procedures and Ad hoc queries with the highest execution times.  Works on SQL Server 2008 and above.

.DESCRIPTION
Quickly find slow query executions within a database.  Results will include stored procedures and individual SQL statements.

.PARAMETER SqlInstance
Allows you to specify a comma separated list of servers to query.

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Database
The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

.PARAMETER ExcludeDatabase
The database(s) to exclude - this list is auto-populated from the server

.PARAMETER MaxResultsPerDb
Allows you to limit the number of results returned, as many systems can have very large amounts of query plans.  Default value is 100 results.

.PARAMETER MinExecs
Allows you to limit the scope to queries that have been executed a minimum number of time. Default value is 100 executions.

.PARAMETER MinExecMs
Allows you to limit the scope to queries with a specified average execution time.  Default value is 500 (ms).

.PARAMETER NoSystemDb
Allows you to suppress output on system databases

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Query, Performance
Author: Brandon Abshire, netnerds.net

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Get-DbaQueryExecutionTime

.EXAMPLE
Get-DbaQueryExecutionTime -SqlInstance sql2008, sqlserver2012

Return the top 100 slowest stored procedures or statements for servers sql2008 and sqlserver2012.

.EXAMPLE
Get-DbaQueryExecutionTime -SqlInstance sql2008 -Database TestDB

Return the top 100 slowest stored procedures or statements on server sql2008 for only the TestDB database.

.EXAMPLE
Get-DbaQueryExecutionTime -SqlInstance sql2008 -Database TestDB -MaxResultsPerDb 100 -MinExecs 200 -MinExecMs 1000

Return the top 100 slowest stored procedures or statements on server sql2008 for only the TestDB database,
limiting results to queries with more than 200 total executions and an execution time over 1000ms or higher.
#>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [parameter(Position = 1, Mandatory = $false)]
        [int]$MaxResultsPerDb = 100,
        [parameter(Position = 2, Mandatory = $false)]
        [int]$MinExecs = 100,
        [parameter(Position = 3, Mandatory = $false)]
        [int]$MinExecMs = 500,
        [parameter(Position = 4, Mandatory = $false)]
        [switch]$NoSystemDb,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $sql = ";With StatsCTE AS
            (
                SELECT
                    DB_NAME() as DatabaseName,
                    (total_worker_time / execution_count) / 1000 AS AvgExec_ms ,
                    execution_count ,
                    max_worker_time / 1000 AS MaxExec_ms ,
                    OBJECT_NAME(object_id) as ProcName,
                    object_id,
                    type_desc,
                    cached_time,
                    last_execution_time,
                    total_worker_time / 1000 as total_worker_time_ms,
                    total_elapsed_time / 1000 as total_elapsed_time_ms,
                    OBJECT_NAME(object_id) as SQLText,
                    OBJECT_NAME(object_id) as full_statement_text
                FROM    sys.dm_exec_procedure_stats
                WHERE   database_id = DB_ID()"

        if ($MinExecs) { $sql += "`n AND execution_count >= " + $MinExecs }
        if ($MinExecMs) { $sql += "`n AND (total_worker_time / execution_count) / 1000 >= " + $MinExecMs }

        $sql += "`n UNION
            SELECT
                DB_NAME() as DatabaseName,
                ( qs.total_worker_time / qs.execution_count ) / 1000 AS AvgExec_ms ,
                qs.execution_count ,
                qs.max_worker_time / 1000 AS MaxExec_ms ,
                OBJECT_NAME(st.objectid) as ProcName,
                   st.objectid as [object_id],
                   'STATEMENT' as type_desc,
                   '1901-01-01 00:00:00' as cached_time,
                    qs.last_execution_time,
                    qs.total_worker_time / 1000 as total_worker_time_ms,
                    qs.total_elapsed_time / 1000 as total_elapsed_time_ms,
                    SUBSTRING(st.text, (qs.statement_start_offset/2)+1, 50) + '...' AS SQLText,
                    SUBSTRING(st.text, (qs.statement_start_offset/2)+1,
                        ((CASE qs.statement_end_offset
                          WHEN -1 THEN DATALENGTH(st.text)
                         ELSE qs.statement_end_offset
                         END - qs.statement_start_offset)/2) + 1) AS full_statement_text
            FROM    sys.dm_exec_query_stats qs
            CROSS APPLY sys.dm_exec_plan_attributes(qs.plan_handle) as pa
            CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) as st
            WHERE st.dbid = DB_ID() OR (pa.attribute = 'dbid' and pa.value = DB_ID())"

        if ($MinExecs) { $sql += "`n AND execution_count >= " + $MinExecs }
        if ($MinExecMs) { $sql += "`n AND (total_worker_time / execution_count) / 1000 >= " + $MinExecMs }

        if ($MaxResultsPerDb) { $sql += ")`n SELECT TOP " + $MaxResultsPerDb }
        else {
            $sql += ")
                        SELECT "
        }

        $sql += "`n     DatabaseName,
                        AvgExec_ms,
                        execution_count,
                        MaxExec_ms,
                        ProcName,
                        object_id,
                        type_desc,
                        cached_time,
                        last_execution_time,
                        total_worker_time_ms,
                        total_elapsed_time_ms,
                        SQLText,
                        full_statement_text
                    FROM StatsCTE "

        if ($MinExecs -or $MinExecMs) {
            $sql += "`n WHERE `n"

            if ($MinExecs) {
                $sql += " execution_count >= " + $MinExecs
            }

            if ($MinExecMs -gt 0 -and $MinExecs) {
                $sql += "`n AND AvgExec_ms >= " + $MinExecMs
            }
            elseif ($MinExecMs) {
                $sql += "`n AvgExecs_ms >= " + $MinExecMs
            }
        }

        $sql += "`n ORDER BY AvgExec_ms DESC"
    }
    process {
        if (!$MaxResultsPerDb -and !$MinExecs -and !$MinExecMs) {
            Write-Message -Level Warning -Message "Results may take time, depending on system resources and size of buffer cache."
            Write-Message -Level Warning -Message "Consider limiting results using -MaxResultsPerDb, -MinExecs and -MinExecMs parameters."
        }

        foreach ($instance in $SqlInstance) {
            Write--Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $dbs = $server.Databases
            if ($Database) {
                $dbs = $dbs | Where-Object Name -In $Database
            }

            if ($NoSystemDb) {
                $dbs = $dbs | Where-Object { $_.IsSystemObject -eq $false }
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $dbs) {
                Write-Message -Level Verbose -Message "Processing $db on $instance"

                if ($db.IsAccessible -eq $false) {
                    Write-Message -Level Warning -Message "The database $db is not accessible. Skipping database."
                    continue
                }

                try {
                    foreach ($row in $db.ExecuteWithResults($sql).Tables.Rows) {
                        [PSCustomObject]@{
                            ComputerName       = $server.ComputerName
                            InstanceName       = $server.ServiceName
                            SqlInstance        = $server.DomainInstanceName
                            Database           = $row.DatabaseName
                            ProcName           = $row.ProcName
                            ObjectID           = $row.object_id
                            TypeDesc           = $row.type_desc
                            Executions         = $row.Execution_Count
                            AvgExecMs          = $row.AvgExec_ms
                            MaxExecMs          = $row.MaxExec_ms
                            CachedTime         = $row.cached_time
                            LastExecTime       = $row.last_execution_time
                            TotalWorkerTimeMs  = $row.total_worker_time_ms
                            TotalElapsedTimeMs = $row.total_elapsed_time_ms
                            SQLText            = $row.SQLText
                            FullStatementText  = $row.full_statement_text
                        } | Select-DefaultView -ExcludeProperty FullStatementText
                    }
                }
                catch {
                    Stop-Function -Message "Could not process $db on $instance" -Target $db -ErrorRecord $_ -Continue
                }
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaRegisteredServer {
    <#
        .SYNOPSIS
            Gets list of SQL Server objects stored in SQL Server Central Management Server (CMS).

        .DESCRIPTION
            Returns an array of servers found in the CMS.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Name
            Specifies one or more names to include. Name is the visible name in SSMS CMS interface (labeled Registered Server Name)

        .PARAMETER ServerName
            Specifies one or more server names to include. Server Name is the actual instance name (labeled Server Name)

        .PARAMETER Group
            Specifies one or more groups to include from SQL Server Central Management Server.

        .PARAMETER ExcludeGroup
            Specifies one or more Central Management Server groups to exclude.

        .PARAMETER ExcludeCmsServer
            Deprecated, now follows the Microsoft convention of not including it by default. If you'd like to include the CMS Server, use -IncludeSelf

        .PARAMETER Id
            Get server by Id(s)

        .PARAMETER IncludeSelf
            If this switch is enabled, the CMS server itself will be included in the results, along with all other Registered Servers.

        .PARAMETER ResolveNetworkName
            If this switch is enabled, the NetBIOS name and IP address(es) of each server will be returned.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.

            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.

            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Bryan Hamby (@galador)
            Tags: RegisteredServer, CMS

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaRegisteredServer

        .EXAMPLE
            Get-DbaRegisteredServer -SqlInstance sqlserver2014a

            Gets a list of servers from the CMS on sqlserver2014a, using Windows Credentials.

        .EXAMPLE
            Get-DbaRegisteredServer -SqlInstance sqlserver2014a -IncludeSelf

            Gets a list of servers from the CMS on sqlserver2014a and includes sqlserver2014a in the output results.

        .EXAMPLE
            Get-DbaRegisteredServer -SqlInstance sqlserver2014a -SqlCredential $credential | Select-Object -Unique -ExpandProperty ServerName

            Returns only the server names from the CMS on sqlserver2014a, using SQL Authentication to authenticate to the server.

        .EXAMPLE
            Get-DbaRegisteredServer -SqlInstance sqlserver2014a -Group HR, Accounting

            Gets a list of servers in the HR and Accounting groups from the CMS on sqlserver2014a.

        .EXAMPLE
            Get-DbaRegisteredServer -SqlInstance sqlserver2014a -Group HR\Development

            Returns a list of servers in the HR and sub-group Development from the CMS on sqlserver2014a.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$Name,
        [string[]]$ServerName,
        [Alias("Groups")]
        [object[]]$Group,
        [object[]]$ExcludeGroup,
        [int[]]$Id,
        [switch]$IncludeSelf,
        [switch]$ExcludeCmsServer,
        [switch]$ResolveNetworkName,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        if ($ResolveNetworkName) {
            $defaults = 'ComputerName', 'FQDN', 'IPAddress', 'Name', 'ServerName', 'Group', 'Description'
        }
        $defaults = 'Name', 'ServerName', 'Group', 'Description'
    }
    process {
        $servers = @()
        foreach ($instance in $SqlInstance) {
            if ($Group) {
                Write-Message -Level Verbose -Message "Connecting to $instance to search for $group"
                $groupservers = Get-DbaRegisteredServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Group $Group -ExcludeGroup $ExcludeGroup
                if ($groupservers) {
                    $servers += $groupservers.GetDescendantRegisteredServers()
                }
            }
            else {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                try {
                    $serverstore = Get-DbaRegisteredServerStore -SqlInstance $instance -SqlCredential $SqlCredential -EnableException
                }
                catch {
                    Stop-Function -Message "Cannot access Central Management Server '$instance'." -ErrorRecord $_ -Continue
                }
                $servers += ($serverstore.DatabaseEngineServerGroup.GetDescendantRegisteredServers())
                $serverstore.ServerConnection.Disconnect()
            }
        }
        
        if ($Name) {
            Write-Message -Level Verbose -Message "Filtering by name for $name"
            $servers = $servers | Where-Object Name -in $Name
        }

        if ($ServerName) {
            Write-Message -Level Verbose -Message "Filtering by servername for $servername"
            $servers = $servers | Where-Object ServerName -in $ServerName
        }

        if ($Id) {
            Write-Message -Level Verbose -Message "Filtering by id for $Id (1 = default/root)"
            $servers = $servers | Where-Object Id -in $Id
        }

        if ($ExcludeGroup) {
            $excluded = Get-DbaRegisteredServer $serverstore.ServerConnection.SqlConnectionObject -Group $ExcludeGroup
            Write-Message -Level Verbose -Message "Excluding $ExcludeGroup"
            $servers = $servers | Where-Object { $_.Urn.Value -notin $excluded.Urn.Value }
        }

        foreach ($server in $servers) {
            $groupname = Get-RegServerGroupReverseParse $server
            if ($groupname -eq $server.Name) {
                $groupname = $null
            }
            else {
                $groupname = ($groupname).Split("\")
                $groupname = $groupname[0 .. ($groupname.Count - 2)]
                $groupname = ($groupname -join "\")
            }


            Add-Member -Force -InputObject $server -MemberType NoteProperty -Name ComputerName -value $serverstore.ComputerName
            Add-Member -Force -InputObject $server -MemberType NoteProperty -Name InstanceName -value $serverstore.InstanceName
            Add-Member -Force -InputObject $server -MemberType NoteProperty -Name SqlInstance -value $serverstore.SqlInstance
            Add-Member -Force -InputObject $server -MemberType NoteProperty -Name Group -value $groupname
            Add-Member -Force -InputObject $server -MemberType NoteProperty -Name FQDN -Value $null
            Add-Member -Force -InputObject $server -MemberType NoteProperty -Name IPAddress -Value $null

            if ($ResolveNetworkName) {
                try {
                    $lookup = Resolve-DbaNetworkName $server.ServerName -Turbo
                    $server.ComputerName = $lookup.ComputerName
                    $server.FQDN = $lookup.FQDN
                    $server.IPAddress = $lookup.IPAddress
                }
                catch {
                    try {
                        $lookup = Resolve-DbaNetworkName $server.ServerName
                        $server.ComputerName = $lookup.ComputerName
                        $server.FQDN = $lookup.FQDN
                        $server.IPAddress = $lookup.IPAddress
                    }
                    catch { }
                }
            }
            Add-Member -Force -InputObject $server -MemberType ScriptMethod -Name ToString -Value { $this.ServerName }
            Select-DefaultView -InputObject $server -Property $defaults
        }

        if ($IncludeSelf -and $servers) {
            Write-Message -Level Verbose -Message "Adding CMS instance"
            $self = $servers[0].PsObject.Copy()
            $self | Add-Member -MemberType NoteProperty -Name Name -Value "CMS Instance" -Force
            $self.ServerName = $instance
            $self.Description = $null
            $self.SecureConnectionString = $null
            Select-DefaultView -InputObject $self -Property $defaults
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Parameter ExcludeCmsServer
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Get-DbaRegisteredServerName
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Get-SqlRegisteredServerName
    }
}
function Get-DbaRegisteredServerGroup {
    <#
        .SYNOPSIS
            Gets list of Server Groups objects stored in SQL Server Central Management Server (CMS).

        .DESCRIPTION
            Returns an array of Server Groups found in the CMS.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Group
            Specifies one or more groups to include from SQL Server Central Management Server.

        .PARAMETER ExcludeGroup
            Specifies one or more Central Management Server groups to exclude.

        .PARAMETER Id
            Get group by Id(s). This parameter only works if the group has a registered server in it.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.

            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.

            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Tony Wilhelm (@tonywsql)
            Tags: RegisteredServer, CMS

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaRegisteredServerGroup

        .EXAMPLE
            Get-DbaRegisteredServerGroup -SqlInstance sqlserver2014a

            Gets the top level groups from the CMS on sqlserver2014a, using Windows Credentials.

        .EXAMPLE
            Get-DbaRegisteredServerGroup -SqlInstance sqlserver2014a -SqlCredential $credential

            Gets the top level groups from the CMS on sqlserver2014a, using alternative credentials to authenticate to the server.

        .EXAMPLE
            Get-DbaRegisteredServerGroup -SqlInstance sqlserver2014a -Group HR, Accounting

            Gets the HR and Accounting groups from the CMS on sqlserver2014a.

        .EXAMPLE
            Get-DbaRegisteredServerGroup -SqlInstance sqlserver2014a -Group HR\Development

            Returns the sub-group Development of the HR group from the CMS on sqlserver2014a.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Group,
        [object[]]$ExcludeGroup,
        [int[]]$Id,
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Get-DbaRegisteredServerStore -SqlInstance $instance -SqlCredential $SqlCredential -EnableException
            }
            catch {
                Stop-Function -Message "Cannot access Central Management Server '$instance'" -ErrorRecord $_ -Continue
            }

            $groups = @()

            if ($group) {
                foreach ($currentgroup in $Group) {
                    Write-Message -Level Verbose -Message "Processing $currentgroup"
                    if ($currentgroup -is [Microsoft.SqlServer.Management.RegisteredServers.ServerGroup]) {
                        $currentgroup = Get-RegServerGroupReverseParse -object $currentgroup
                    }
                    
                    if ($currentgroup -match 'DatabaseEngineServerGroup\\') {
                        $currentgroup = $currentgroup.Replace('DatabaseEngineServerGroup\', '')
                    }
                    
                    if ($currentgroup -match '\\') {
                        $split = $currentgroup.Split('\\')
                        $i = 0
                        $groupobject = $server.DatabaseEngineServerGroup
                        do {
                            if ($groupobject) {
                                $groupobject = $groupobject.ServerGroups[$split[$i]]
                                Write-Message -Level Verbose -Message "Parsed $($groupobject.Name)"
                            }
                        }
                        until ($i++ -eq $split.GetUpperBound(0))
                        if ($groupobject) {
                            $groups += $groupobject
                        }
                    }
                    else {
                        try {
                            $thisgroup = $server.DatabaseEngineServerGroup.ServerGroups[$currentgroup]
                            if ($thisgroup) {
                                Write-Message -Level Verbose -Message "Added $($thisgroup.Name)"
                                $groups += $thisgroup
                            }
                        }
                        catch { }
                    }
                }
            }
            else {
                Write-Message -Level Verbose -Message "Added all root server groups"
                $groups = $server.DatabaseEngineServerGroup.ServerGroups
            }

            if ($Group -eq 'DatabaseEngineServerGroup') {
                Write-Message -Level Verbose -Message "Added root group"
                $groups = $server.DatabaseEngineServerGroup
            }

            if ($ExcludeGroup) {
                $excluded = Get-DbaRegisteredServer $server -Group $ExcludeGroup
                Write-Message -Level Verbose -Message "Excluding $ExcludeGroup"
                $groups = $groups | Where-Object { $_.Urn.Value -notin $excluded.Urn.Value }
            }

            if ($Id) {
                Write-Message -Level Verbose -Message "Filtering for id $Id. Id 1 = default."
                if ($Id -eq 1) {
                    $groups = $server.DatabaseEngineServerGroup | Where-Object Id -in $Id
                }
                else {
                    $groups = $server.DatabaseEngineServerGroup.GetDescendantRegisteredServers().Parent | Where-Object Id -in $Id
                }
            }
            $server.ServerConnection.Disconnect()
            foreach ($groupobject in $groups) {
                Add-Member -Force -InputObject $groupobject -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                Add-Member -Force -InputObject $groupobject -MemberType NoteProperty -Name InstanceName -value $server.InstanceName
                Add-Member -Force -InputObject $groupobject -MemberType NoteProperty -Name SqlInstance -value $server.SqlInstance

                Select-DefaultView -InputObject $groupobject -Property ComputerName, InstanceName, SqlInstance, Name, DisplayName, Description, ServerGroups, RegisteredServers
            }
        }
    }
}
function Get-DbaRegisteredServerStore {
    <#
        .SYNOPSIS
            Returns a SQL Server Registered Server Store Object

        .DESCRIPTION
            Returns a SQL Server Registered Server Store object - useful for working with Central Management Store

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function
            to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: RegisteredServer,CMS
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaRegisteredServerStore

        .EXAMPLE
            Get-DbaRegisteredServerStore -SqlInstance sqlserver2014a

            Returns a SQL Server Registered Server Store Object from sqlserver2014a

        .EXAMPLE
            Get-DbaRegisteredServerStore -SqlInstance sqlserver2014a -SqlCredential (Get-Credential sqladmin)

            Returns a SQL Server Registered Server Store Object from sqlserver2014a  by logging in with the sqladmin login
    #>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            try {
                $store = New-Object Microsoft.SqlServer.Management.RegisteredServers.RegisteredServersStore($server.ConnectionContext.SqlConnectionObject)
            }
            catch {
                Stop-Function -Message "Cannot access Central Management Server on $instance" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            Add-Member -Force -InputObject $store -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
            Add-Member -Force -InputObject $store -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
            Add-Member -Force -InputObject $store -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName

            Select-DefaultView -InputObject $store -ExcludeProperty ServerConnection, DomainInstanceName, DomainName, Urn, Properties, Metadata, Parent, ConnectionContext, PropertyMetadataChanged, PropertyChanged
        }
    }
}
function Get-DbaResourceGovernorClassifierFunction {
<#
.SYNOPSIS
Gets the Resource Governor custom classifier Function

.DESCRIPTION
Gets the Resource Governor custom classifier Function which is used for customize the workload groups usage

.PARAMETER SqlInstance
The target SQL Server instance(s)

.PARAMETER SqlCredential
Allows you to login to SQL Server using alternative credentials

.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Migration, ResourceGovernor
Author: Alessandro Alpi (@suxstellino), alessandroalpi.blog
Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Get-DbaResourceGovernorClassifierFunction

.EXAMPLE
Get-DbaResourceGovernorClassifierFunction -SqlInstance sql2016

Gets the classifier function object of the SqlInstance sql2016

.EXAMPLE
'Sql1','Sql2/sqlexpress' | Get-DbaResourceGovernorClassifierFunction

Gets the classifier function object on Sql1 and Sql2/sqlexpress instances

#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $classifierFunction = $null

            foreach ($currentFunction in $server.Databases["master"].UserDefinedFunctions)
            {
                $fullyQualifiedFunctionName = [string]::Format("[{0}].[{1}]", $currentFunction.Schema, $currentFunction.Name)
                if ($fullyQualifiedFunctionName -eq $server.ResourceGovernor.ClassifierFunction)
                {
                    $classifierFunction = $currentFunction
                }
            }

            if ($classifierFunction) {
                Add-Member -Force -InputObject $classifierFunction -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                Add-Member -Force -InputObject $classifierFunction -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                Add-Member -Force -InputObject $classifierFunction -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                Add-Member -Force -InputObject $classifierFunction -MemberType NoteProperty -Name Database -value 'master'
            }

            Select-DefaultView -InputObject $classifierFunction -Property ComputerName, InstanceName, SqlInstance, Database, Schema, CreateDate, DateLastModified, Name, DataType
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaRestoreHistory {
    <#
        .SYNOPSIS
            Returns restore history details for databases on a SQL Server.

        .DESCRIPTION
            By default, this command will return the server name, database, username, restore type, date, from file and to files.

            Thanks to https://www.mssqltips.com/SqlInstancetip/1724/when-was-the-last-time-your-sql-server-database-was-restored/ for the query and https://sqlstudies.com/2016/07/27/when-was-this-database-restored/ for the idea.

        .PARAMETER SqlInstance
            Specifies the SQL Server instance(s) to operate on. Requires SQL Server 2005 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies the database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies the database(s) to exclude from processing. Options for this list are auto-populated from the server.

        .PARAMETER Since
            Specifies a datetime to use as the starting point for searching backup history.

        .PARAMETER Force
            Deprecated.

        .PARAMETER Last
            If this switch is enabled, the last restore action performed on each database is returned.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: DisasterRecovery, Backup, Restore

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaRestoreHistory

        .EXAMPLE
            Get-DbaRestoreHistory -SqlInstance sql2016

            Returns server name, database, username, restore type, date for all restored databases on sql2016.

        .EXAMPLE
            Get-DbaRestoreHistory -SqlInstance sql2016 -Database db1, db2 -Since '7/1/2016 10:47:00'

            Returns restore information only for databases db1 and db2 on sql2016 since July 1, 2016 at 10:47 AM.

        .EXAMPLE
            Get-DbaRestoreHistory -SqlInstance sql2014, sql2016 -Exclude db1

            Lots of detailed information for all databases except db1 on sql2014 and sql2016.

        .EXAMPLE
            Get-DbaRestoreHistory -SqlInstance sql2014 -Database AdventureWorks2014, pubs | Format-Table

            Adds From and To file information to output, returns information only for AdventureWorks2014 and pubs, and formats the data as a table.

        .EXAMPLE
            Get-DbaRegisteredServer -SqlInstance sql2016 | Get-DbaRestoreHistory

            Returns database restore information for every database on every server listed in the Central Management Server on sql2016.

    #>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [datetime]$Since,
        [switch]$Force,
        [switch]$Last,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Test-DbaDeprecation -DeprecatedOn "1.0.0.0" -EnableException:$false -Parameter 'Force'

        if ($Since -ne $null) {
            $Since = $Since.ToString("yyyy-MM-ddTHH:mm:ss")
        }
    }

    process {
        foreach ($instance in $SqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
                $computername = $server.ComputerName
                $instancename = $server.ServiceName
                $servername = $server.DomainInstanceName

                if ($force -eq $true) {
                    $select = "SELECT '$computername' AS [ComputerName],
                    '$instancename' AS [InstanceName],
                    '$servername' AS [SqlInstance], * "
                }
                else {
                    $select = "SELECT
                    '$computername' AS [ComputerName],
                    '$instancename' AS [InstanceName],
                    '$servername' AS [SqlInstance],
                     rsh.destination_database_name AS [Database],
                     --rsh.restore_history_id as RestoreHistoryID,
                     rsh.user_name AS [Username],
                     CASE
                         WHEN rsh.restore_type = 'D' THEN 'Database'
                         WHEN rsh.restore_type = 'F' THEN 'File'
                         WHEN rsh.restore_type = 'G' THEN 'Filegroup'
                         WHEN rsh.restore_type = 'I' THEN 'Differential'
                         WHEN rsh.restore_type = 'L' THEN 'Log'
                         WHEN rsh.restore_type = 'V' THEN 'Verifyonly'
                         WHEN rsh.restore_type = 'R' THEN 'Revert'
                         ELSE rsh.restore_type
                     END AS [RestoreType],
                     rsh.restore_date AS [Date],
                     ISNULL(STUFF((SELECT ', ' + bmf.physical_device_name
                                    FROM msdb.dbo.backupmediafamily bmf
                                   WHERE bmf.media_set_id = bs.media_set_id
                                 FOR XML PATH('')), 1, 2, ''), '') AS [From],
                     ISNULL(STUFF((SELECT ', ' + rf.destination_phys_name
                                    FROM msdb.dbo.restorefile rf
                                   WHERE rsh.restore_history_id = rf.restore_history_id
                                 FOR XML PATH('')), 1, 2, ''), '') AS [To],
                    bs.first_lsn,
                    bs.last_lsn,
                    bs.checkpoint_lsn,
                    bs.database_backup_lsn,
                    bs.backup_finish_date,
                    bs.backup_finish_date AS BackupFinishDate
                    "
                }

                $from = " FROM msdb.dbo.restorehistory rsh
                    INNER JOIN msdb.dbo.backupset bs ON rsh.backup_set_id = bs.backup_set_id"

                if ($ExcludeDatabase -or $Database -or $Since -or $last) {
                    $where = " WHERE "
                }

                $wherearray = @()

                if ($ExcludeDatabase) {
                    $dblist = $ExcludeDatabase -join "','"
                    $wherearray += " destination_database_name not in ('$dblist')"
                }

                if ($Database) {
                    $dblist = $Database -join "','"
                    $wherearray += "destination_database_name in ('$dblist')"
                }

                if ($null -ne $Since) {
                    $wherearray += "rsh.restore_date >= '$since'"
                }


                if ($last) {
                    $wherearray += "rsh.backup_set_id in
                        (select max(backup_set_id) from msdb.dbo.restorehistory
                        group by destination_database_name
                        )"
                }

                if ($where.length -gt 0) {
                    $wherearray = $wherearray -join " and "
                    $where = "$where $wherearray"
                }

                $sql = "$select $from $where"

                Write-Message -Level Debug -Message $sql

                $results = $server.ConnectionContext.ExecuteWithResults($sql).Tables.Rows
                if ($last) {
                    $ga = $results | Group-Object Database
                    $tmpres = @()
                    foreach($g in $ga) {
                        $tmpres += $g.Group | Sort-Object -Property Date -Descending | Select-Object -First 1
                    }
                    $results = $tmpres
                }
                $results | Select-DefaultView -ExcludeProperty first_lsn, last_lsn, checkpoint_lsn, database_backup_lsn, backup_finish_date
            }
            catch {
                Stop-Function -Message "Failure" -Target $SqlInstance -Error $_ -Exception $_.Exception.InnerException -Continue
            }
        }
    }
}
function Get-DbaRoleMember {
    <#
.SYNOPSIS
Get members of all roles on a Sql instance.

.DESCRIPTION
Get members of all roles on a Sql instance.

Default output includes columns SQLServer, Database, Role, Member.

.PARAMETER SQLInstance
The SQL Server that you're connecting to.

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Database
The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

.PARAMETER ExcludeDatabase
The database(s) to exclude - this list is auto-populated from the server

.PARAMETER IncludeServerLevel
Shows also information on Server Level Permissions.

.PARAMETER NoFixedRole
Excludes all members of fixed roles.

.PARAMETER Credential
Credential object used to connect to the SQL Server as a different user.

.NOTES
Tags: Role, Database, Security, Login
Author: Klaas Vandenberghe ( @PowerDBAKlaas )

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
 https://dbatools.io/Get-DbaRoleMember

.EXAMPLE
Get-DbaRoleMember -SqlInstance ServerA

Returns a custom object displaying SQLServer, Database, Role, Member for all DatabaseRoles.

.EXAMPLE
Get-DbaRoleMember -SqlInstance sql2016 | Out-Gridview

Returns a gridview displaying SQLServer, Database, Role, Member for all DatabaseRoles.

.EXAMPLE
Get-DbaRoleMember -SqlInstance ServerA\sql987 -IncludeServerLevel

Returns a gridview displaying SQLServer, Database, Role, Member for both ServerRoles and DatabaseRoles.

#>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias('SqlServer', 'ServerInstance')]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$IncludeServerLevel,
        [switch]$NoFixedRole
    )

    process {

        foreach ($instance in $sqlinstance) {
            Write-Verbose "Connecting to $Instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Write-Warning "Failed to connect to $instance"
                continue
            }

            if ($IncludeServerLevel) {
                Write-Verbose "Server Role Members included"
                $instroles = $null
                Write-Verbose "Getting Server Roles on $instance"
                $instroles = $server.roles
                if ($NoFixedRole) {
                    $instroles = $instroles | Where-Object { $_.isfixedrole -eq $false }
                }
                ForEach ($instrole in $instroles) {
                    Write-Verbose "Getting Server Role Members for $instrole on $instance"
                    $irmembers = $null
                    $irmembers = $instrole.enumserverrolemembers()
                    ForEach ($irmem in $irmembers) {
                        [PSCustomObject]@{
                            SQLInstance = $instance
                            Database    = $null
                            Role        = $instrole.name
                            Member      = $irmem.tostring()
                        }
                    }
                }
            }

            $dbs = $server.Databases

            if ($Database) {
                $dbs = $dbs | Where-Object Name -In $Database
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $dbs) {
                Write-Verbose "Checking accessibility of $db on $instance"

                if ($db.IsAccessible -ne $true) {
                    Write-Warning "Database $db on $instance is not accessible"
                    continue
                }

                $dbroles = $db.roles
                Write-Verbose "Getting Database Roles for $db on $instance"

                if ($NoFixedRole) {
                    $dbroles = $dbroles | Where-Object { $_.isfixedrole -eq $false }
                }

                foreach ($dbrole in $dbroles) {
                    Write-Verbose "Getting Database Role Members for $dbrole in $db on $instance"
                    $dbmembers = $dbrole.enummembers()
                    ForEach ($dbmem in $dbmembers) {
                        [PSCustomObject]@{
                            SqlInstance = $instance
                            Database    = $db.name
                            Role        = $dbrole.name
                            Member      = $dbmem.tostring()
                        }
                    }
                }
            }
        }
    }
}
function Get-DbaRunningJob {
    <#
        .SYNOPSIS
            Returns all non-idle Agent jobs running on the server.

        .DESCRIPTION
            This function returns agent jobs that active on the SQL Server instance when calling the command. The information is gathered the SMO JobServer.jobs and be returned either in detailed or standard format.

        .PARAMETER SqlInstance
            The SQL Server instance to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Process, Session, ActivityMonitor, Agent, Job
            Author: Stephen Bennett, https://sqlnotesfromtheunderground.wordpress.com/
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaRunningJob

        .EXAMPLE
            Get-DbaRunningJob -SqlInstance localhost

            Returns any active jobs on localhost.

        .EXAMPLE
            Get-DbaRunningJob -SqlInstance localhost -Detailed

            Returns a detailed output of any active jobs on localhost.

        .EXAMPLE
            'localhost','localhost\namedinstance' | Get-DbaRunningJob

            Returns all active jobs on multiple instances piped into the function.
    #>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failed to connect to: $Server." -Target $server -ErrorRecord $_ -Continue
            }

            $jobs = $server.JobServer.jobs | Where-Object { $_.CurrentRunStatus -ne 'Idle' }

            if (!$jobs) {
                Write-Message -Level Verbose -Message "No Jobs are currently running on: $Server."
            }
            else {
                foreach ($job in $jobs) {
                    [pscustomobject]@{
                        ComputerName     = $server.ComputerName
                        InstanceName     = $server.ServiceName
                        SqlInstance      = $server.DomainInstanceName
                        Name             = $job.name
                        Category         = $job.Category
                        CurrentRunStatus = $job.CurrentRunStatus
                        CurrentRunStep   = $job.CurrentRunStep
                        HasSchedule      = $job.HasSchedule
                        LastRunDate      = $job.LastRunDate
                        LastRunOutcome   = $job.LastRunOutcome
                        JobStep          = $job.JobSteps
                    }
                }
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaSchemaChangeHistory {
    <#
    .SYNOPSIS
    Gets DDL changes logged in the system trace.

    .DESCRIPTION
    Queries the default system trace for any DDL changes in the specified timeframe
    Only works with SQL 2005 and later, as the system trace didn't exist before then

    .PARAMETER SqlInstance
    SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function
    to be executed against multiple SQL Server instances.

    .PARAMETER SqlCredential
    Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

    .PARAMETER Database
    The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

    .PARAMETER ExcludeDatabase
    The database(s) to exclude - this list is auto-populated from the server

    .PARAMETER Since
    A date from which DDL changes should be returned. Default is to start at the beggining of the current trace file

    .PARAMETER Object
    The name of a SQL Server object you want to look for changes on

    .PARAMETER EnableException
    By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
    Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
    Tags: Migration, Backup, Database
    Author: Stuart Moore (@napalmgram - http://stuart-moore.com)

    Website: https://dbatools.io
    Copyright: (C) Chrissy LeMaire, [email protected]
    License: MIT https://opensource.org/licenses/MIT

    .LINK
    https://dbatools.io/Get-DbaSchemaChangeHistory

    .EXAMPLE
    Get-DbaSchemaChangeHistory -SqlInstance localhost

    Returns all DDL changes made in all databases on the SQL Server instance localhost since the system trace began

    .EXAMPLE
    Get-DbaSchemaChangeHistory -SqlInstance localhost -Since (Get-Date).AddDays(-7)

    Returns all DDL changes made in all databases on the SQL Server instance localhost in the last 7 days

    .EXAMPLE
    Get-DbaSchemaChangeHistory -SqlInstance localhost -Database Finance, Prod -Since (Get-Date).AddDays(-7)

    Returns all DDL changes made in the Prod and Finance databases on the SQL Server instance localhost in the last 7 days

    .EXAMPLE
    Get-DbaSchemaChangeHistory -SqlInstance localhost -Database Finance -Object AccountsTable -Since (Get-Date).AddDays(-7)

    Returns all DDL changes made  to the AccountsTable object in the Finance database on the SQL Server instance localhost in the last 7 days

    #>

    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [DbaDateTime]$Since,
        [string[]]$Object,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            if ($Server.Version.Major -le 8) {
                Stop-Function -Message "This command doesn't support SQL Server 2000, sorry about that"
                return
            }
            $TraceFileQuery = "select path from sys.traces where is_default = 1"

            $TraceFile = $server.Query($TraceFileQuery) | Select-Object Path

            $Databases = $server.Databases

            if ($Database) { $Databases = $Databases | Where-Object Name -in $database }

            if ($ExcludeDatabase) { $Databases = $Databases | Where-Object Name -notin $ExcludeDatabase }

            foreach ($db in $Databases) {
                if ($db.IsAccessible -eq $false) {
                    Write-Message -Level Verbose -Message "$($db.name) is not accessible, skipping"
                }

                $sql = "select SERVERPROPERTY('MachineName') AS ComputerName,
                        ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
                        SERVERPROPERTY('ServerName') AS SqlInstance,
                        tt.databasename as 'DatabaseName',
                        starttime as 'DateModified',
                        Sessionloginname as 'LoginName',
                        NTusername as 'UserName',
                        applicationname as 'ApplicationName',
                        case eventclass
                            When '46' Then 'Create'
                            when '47' Then 'Drop'
                            when '164' then 'Alter'
                        end as 'DDLOperation',
                        s.name+'.'+o.name as 'Object',
                        o.type_desc as 'ObjectType'
                        from
                        sys.objects o  inner join
                        sys.schemas s on s.schema_id=o.schema_id
                        cross apply (select * from ::fn_trace_gettable('$($TraceFile.path)',default) where ObjectID=o.object_id ) tt
                        where tt.objecttype not in (21587)
                        and tt.DatabaseID=db_id()
                        and tt.EventSubClass=0"

                if ($null -ne $since) {
                    $sql = $sql + " and tt.StartTime>'$Since' "
                }
                if ($null -ne $object) {
                    $sql = $sql + " and o.name in ('$($object -join ''',''')') "
                }

                $sql = $sql + " order by tt.StartTime asc"
                Write-Message -Level Verbose -Message "Querying Database $db on $instance"
                Write-Message -Level Debug -Message "SQL: $sql"

                $db.Query($sql) | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, DatabaseName, DateModified, LoginName, UserName, ApplicationName, DDLOperation, Object, ObjectType
            }
        }
    }
}

function Get-DbaServerAudit {
    <#
        .SYNOPSIS
            Gets SQL Security Audit information for each instance(s) of SQL Server.

        .DESCRIPTION
            The Get-DbaServerAudit command gets SQL Security Audit information for each instance(s) of SQL Server.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function
            to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Audit
            Return only specific audits

        .PARAMETER ExcludeAudit
            Exclude specific audits

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Audit, Security, SqlAudit
            Author: Garry Bargsley (@gbargsley), http://blog.garrybargsley.com

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaServerAudit

        .EXAMPLE
            Get-DbaServerAudit -SqlInstance localhost

            Returns all Security Audits on the local default SQL Server instance

        .EXAMPLE
            Get-DbaServerAudit -SqlInstance localhost, sql2016

            Returns all Security Audits for the local and sql2016 SQL Server instances
    #>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [string[]]$Audit,
        [string[]]$ExcludeAudit,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $audits = $server.Audits

            if (Test-Bound -ParameterName Audit) {
                $audits = $audits | Where-Object Name -in $Audit
            }
            if (Test-Bound -ParameterName ExcludeAudit) {
                $audits = $audits | Where-Object Name -notin $ExcludeAudit
            }

            foreach ($currentaudit in $audits) {
                $directory = $currentaudit.FilePath.TrimEnd("\")
                $filename = $currentaudit.FileName
                $fullname = "$directory\$filename"
                $remote = $fullname.Replace(":", "$")
                $remote = "\\$($currentaudit.Parent.ComputerName)\$remote"

                Add-Member -Force -InputObject $currentaudit -MemberType NoteProperty -Name ComputerName -value $currentaudit.Parent.ComputerName
                Add-Member -Force -InputObject $currentaudit -MemberType NoteProperty -Name InstanceName -value $currentaudit.Parent.ServiceName
                Add-Member -Force -InputObject $currentaudit -MemberType NoteProperty -Name SqlInstance -value $currentaudit.Parent.DomainInstanceName
                Add-Member -Force -InputObject $currentaudit -MemberType NoteProperty -Name FullName -value $fullname
                Add-Member -Force -InputObject $currentaudit -MemberType NoteProperty -Name RemoteFullName -value $remote

                Select-DefaultView -InputObject $currentaudit -Property ComputerName, InstanceName, SqlInstance, Name, 'Enabled as IsEnabled', FullName
            }
        }
    }
}
function Get-DbaServerAuditSpecification {
    <#
        .SYNOPSIS
            Gets SQL Security Audit Specification information for each instance(s) of SQL Server.

        .DESCRIPTION
            The Get-DbaServerAuditSpecification command gets SQL Security Audit Specification information for each instance(s) of SQL Server.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function
            to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Audit, Security, SqlAudit
            Author: Garry Bargsley (@gbargsley), http://blog.garrybargsley.com

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaServerAuditSpecification

        .EXAMPLE
            Get-DbaServerAuditSpecification -SqlInstance localhost

            Returns all Security Audit Specifications on the local default SQL Server instance

        .EXAMPLE
            Get-DbaServerAuditSpecification -SqlInstance localhost, sql2016

            Returns all Security Audit Specifications for the local and sql2016 SQL Server instances
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Verbose "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($server.versionMajor -lt 10) {
                Write-Warning "Server Audits are only supported in SQL Server 2008 and above. Quitting."
                continue
            }

            foreach ($auditSpecification in $server.ServerAuditSpecifications) {
                Add-Member -Force -InputObject $auditSpecification -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                Add-Member -Force -InputObject $auditSpecification -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                Add-Member -Force -InputObject $auditSpecification -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName

                Select-DefaultView -InputObject $auditSpecification -Property ComputerName, InstanceName, SqlInstance, ID, Name, AuditName, Enabled, CreateDate, DateLastModified, Guid
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-SqlServerAuditSpecification
    }
}
function Get-DbaServerInstallDate {
    <#
.SYNOPSIS
Returns the install date of a SQL Instance and Windows Server, depending on what is passed.

.DESCRIPTION
By default, this command returns for each SQL Instance instance passed in:
SQL Instance install date, formatted as a string
Hosting Windows server install date, formatted as a string

.PARAMETER SqlInstance
The SQL Server that you're connecting to.

.PARAMETER SqlCredential
Credential object used to connect to the SQL Server as a different user

.PARAMETER Credential
Credential object used to connect to the SQL Server as a different user

.PARAMETER IncludeWindows
Includes the Windows Server Install date information

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: CIM
Author: Mitchell Hamann (@SirCaptainMitch), mitchellhamann.com

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Get-DbaServerInstallDate

.EXAMPLE
Get-DbaServerInstallDate -SqlInstance SqlBox1\Instance2

Returns an object with SQL Instance Install date as a string and the Windows install date as string.

.EXAMPLE
Get-DbaServerInstallDate -SqlInstance winserver\sqlexpress, sql2016

Returns an object with SQL Instance Install date as a string and the Windows install date as a string for both SQLInstances that are passed to the cmdlet.

.EXAMPLE
Get-DbaServerInstallDate -SqlInstance sqlserver2014a, sql2016

Returns an object with only the SQL Server Install date as a string.

.EXAMPLE
Get-DbaServerInstallDate -SqlInstance sqlserver2014a, sql2016 -IncludeWindows

Returns an object with the Windows Install date and the SQL install date as a string.

.EXAMPLE
Get-DbaRegisteredServer -SqlInstance sql2014 | Get-DbaServerInstallDate

Returns an object with SQL Instance install date as a string for every server listed in the Central Management Server on sql2014

#>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer", "ComputerName")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]
        $SqlCredential,
        [PSCredential]
        $Credential,
        [Switch]$IncludeWindows,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level VeryVerbose -Message "Connecting to $instance" -Target $instance
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failed to process Instance $Instance" -ErrorRecord $_ -Target $instance -Continue
            }

            if ($server.VersionMajor -ge 9) {
                Write-Message -Level Verbose -Message "Getting Install Date for: $instance"
                $sql = "SELECT create_date FROM sys.server_principals WHERE sid = 0x010100000000000512000000"
                [DbaDateTime]$sqlInstallDate = $server.Query($sql, 'master', $true).create_date

            }
            else {
                Write-Message -Level Verbose -Message "Getting Install Date for: $instance"
                $sql = "SELECT schemadate FROM sysservers"
                [DbaDateTime]$sqlInstallDate = $server.Query($sql, 'master', $true).create_date
            }

            $WindowsServerName = $server.ComputerNamePhysicalNetBIOS

            if ($IncludeWindows) {
                try {
                    [DbaDateTime]$windowsInstallDate = (Get-DbaCmObject -ClassName win32_OperatingSystem -ComputerName $WindowsServerName -Credential $Credential -EnableException).InstallDate
                }
                catch {
                    Stop-Function -Message "Failed to connect to: $WindowsServerName" -Continue -Target $instance -ErrorRecord $_
                }
            }

            $object = [PSCustomObject]@{
                ComputerName       = $server.ComputerName
                InstanceName       = $server.ServiceName
                SqlInstance        = $server.DomainInstanceName
                SqlInstallDate     = $sqlInstallDate
                WindowsInstallDate = $windowsInstallDate
            }

            if ($IncludeWindows) {
                Select-DefaultView -InputObject $object -Property ComputerName, InstanceName, SqlInstance, SqlInstallDate, WindowsInstallDate
            }
            else {
                Select-DefaultView -InputObject $object -Property ComputerName, InstanceName, SqlInstance, SqlInstallDate
            }

        }
    }
}
function Get-DbaServerProtocol {
    <#
    .SYNOPSIS
    Gets the SQL Server related server protocols on a computer.

    .DESCRIPTION
    Gets the SQL Server related server protocols on one or more computers.

    Requires Local Admin rights on destination computer(s).
    The server protocols can be enabled and disabled when retrieved via WSMan.

    .PARAMETER ComputerName
    The SQL Server (or server in general) that you're connecting to. This command handles named instances.

    .PARAMETER Credential
    Credential object used to connect to the computer as a different user.

   .PARAMETER EnableException
   By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
   This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
   Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
    Author: Klaas Vandenberghe ( @PowerDBAKlaas )
    Tags: Protocol
    dbatools PowerShell module (https://dbatools.io)
    Copyright (C) 2016 Chrissy LeMaire
    License: MIT https://opensource.org/licenses/MIT

    .LINK
    https://dbatools.io/Get-DbaServerProtocol

    .EXAMPLE
    Get-DbaServerProtocol -ComputerName sqlserver2014a

    Gets the SQL Server related server protocols on computer sqlserver2014a.

    .EXAMPLE
    'sql1','sql2','sql3' | Get-DbaServerProtocol

    Gets the SQL Server related server protocols on computers sql1, sql2 and sql3.

    .EXAMPLE
    Get-DbaServerProtocol -ComputerName sql1,sql2 | Out-Gridview

    Gets the SQL Server related server protocols on computers sql1 and sql2, and shows them in a grid view.

    .EXAMPLE
    (Get-DbaServerProtocol -ComputerName sql1 | Where { $_.DisplayName = 'via' }).Disable()

    Disables the VIA ServerNetworkProtocol on computer sql1.
    If successful, returncode 0 is shown.

#>
    [CmdletBinding()]
    Param (
        [parameter(ValueFromPipeline)]
        [Alias("cn", "host", "Server")]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($Computer in $ComputerName.ComputerName) {
            $Server = Resolve-DbaNetworkName -ComputerName $Computer -Credential $credential
            if ($Server.FullComputerName) {
                $Computer = $server.FullComputerName
                Write-Message -Level Verbose -Message "Getting SQL Server namespace on $computer"
                $namespace = Get-DbaCmObject -ComputerName $Computer -NameSpace root\Microsoft\SQLServer -Query "Select * FROM __NAMESPACE WHERE Name Like 'ComputerManagement%'" -ErrorAction SilentlyContinue |
                    Where-Object { (Get-DbaCmObject -ComputerName $Computer -Namespace $("root\Microsoft\SQLServer\" + $_.Name) -ClassName ServerNetworkProtocol -ErrorAction SilentlyContinue).count -gt 0 } |
                    Sort-Object Name -Descending | Select-Object -First 1
                if ($namespace.Name) {
                    Write-Message -Level Verbose -Message "Getting Cim class ServerNetworkProtocol in Namespace $($namespace.Name) on $Computer"
                    try {
                        $prot = Get-DbaCmObject -ComputerName $Computer -Namespace $("root\Microsoft\SQLServer\" + $namespace.Name) -ClassName ServerNetworkProtocol -ErrorAction SilentlyContinue
                        $prot | Add-Member -Force -MemberType ScriptMethod -Name Enable -Value { Invoke-CimMethod -MethodName SetEnable -InputObject $this }
                        $prot | Add-Member -Force -MemberType ScriptMethod -Name Disable -Value { Invoke-CimMethod -MethodName SetDisable -InputObject $this }
                        foreach ($protocol in $prot) { Select-DefaultView -InputObject $protocol -Property 'PSComputerName as ComputerName', 'InstanceName', 'ProtocolDisplayName as DisplayName', 'ProtocolName as Name', 'MultiIpconfigurationSupport as MultiIP', 'Enabled as IsEnabled' }
                    }
                    catch {
                        Write-Message -Level Warning -Message "No Sql ServerNetworkProtocol found on $Computer"
                    }
                }
                else {
                    Write-Message -Level Warning -Message "No ComputerManagement Namespace on $Computer. Please note that this function is available from SQL 2005 up."
                }
            }
            else {
                Write-Message -Level Warning -Message "Failed to connect to $Computer"
            }
        }
    }
}
function Get-DbaServerRole {
    <#
        .SYNOPSIS
            Gets the list of server-level roles.

        .DESCRIPTION
            Gets the list of server-level roles for SQL Server instance.

        .PARAMETER SqlInstance
            The SQL Server instance. Server version must be SQL Server version 2005 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER ServerRole
            Server-Level role to filter results to that role only.

        .PARAMETER ExcludeServerRole
            Server-Level role to exclude from results.

        .PARAMETER ExcludeFixedRole
            Filter the fixed server-level roles. Only applies to SQL Server 2017 that supports creation of server-level roles.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message. This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting. Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ServerRole, Security
            Original Author: Shawn Melton (@wsmelton)

            Website: https: //dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaServerRole

        .EXAMPLE
            Get-DbaServerRole -SqlInstance sql2016a

            Outputs list of server-level roles for sql2016a instance.

        .EXAMPLE
            Get-DbaServerRole -SqlInstance sql2017a -ExcludeFixedRole

            Outputs the server-level role(s) that are not fixed roles on sql2017a instance.
    #>
    [CmdletBinding()]
    param (
        [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$ServerRole,
        [object[]]$ExcludeServerRole,
        [switch]$ExcludeFixedRole,
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $roles = $server.Roles

            if ($ServerRole) {
                $roles = $roles | Where-Object Name -In $ServerRole
            }
            if ($ExcludeServerRole) {
                $roles = $roles | Where-Object Name -NotIn $ExcludeServerRole
            }
            if ($ExcludeFixedRole) {
                $roles = $roles | Where-Object IsFixedRole -eq $false
            }

            foreach ($role in $roles) {
                $members = $role.EnumMemberNames()

                Add-Member -Force -InputObject $role -MemberType NoteProperty -Name Login -Value $members
                Add-Member -Force -InputObject $role -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                Add-Member -Force -InputObject $role -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                Add-Member -Force -InputObject $role -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName

                $default = 'ComputerName', 'InstanceName', 'SqlInstance', 'Name as Role', 'IsFixedRole', 'DateCreated', 'DateModified'
                Select-DefaultView -InputObject $role -Property $default
            }
        }
    }
}
function Get-DbaSpConfigure {
    <#
        .SYNOPSIS
            Returns all server level system configuration (sys.configuration/sp_configure) information

        .DESCRIPTION
            This function returns server level system configuration (sys.configuration/sp_configure) information. The information is gathered through SMO Configuration.Properties.
            The data includes the default value for each configuration, for quick identification of values that may have been changed.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a
            collection and receive pipeline input

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Name
            Return only specific configurations -- auto-populated from source server

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: SpConfig, Configure, Configuration
            Author: Nic Cain, https://sirsql.net/

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaSpConfigure

        .EXAMPLE
            Get-DbaSpConfigure -SqlInstance localhost

            Returns server level configuration data on the localhost (ServerName, Name, DisplayName, Description, IsAdvanced, IsDynamic, MinValue, MaxValue, ConfiguredValue, RunningValue, DefaultValue, IsRunningDefaultValue)

        .EXAMPLE
            'localhost','localhost\namedinstance' | Get-DbaSpConfigure

            Returns system configuration information on multiple instances piped into the function

        .EXAMPLE
            Get-DbaSpConfigure -SqlInstance localhost

            Returns server level configuration data on the localhost (ServerName, Name, DisplayName, Description, IsAdvanced, IsDynamic, MinValue, MaxValue, ConfiguredValue, RunningValue, DefaultValue, IsRunningDefaultValue)

        .EXAMPLE
            Get-DbaSpConfigure -SqlInstance sql2012 -Name MaxServerMemory

            Returns only the system configuration for MaxServerMemory. Configs is auto-populated for tabbing convenience.
        #>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Config", "ConfigName")]
        [string[]]$Name,
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Write-Warning "Failed to connect to: $instance"
                continue
            }

            #Get a list of the configuration property parents, and exclude the Parent, Properties values
            $proplist = Get-Member -InputObject $server.Configuration -MemberType Property -Force | Select-Object Name | Where-Object { $_.Name -ne "Parent" -and $_.Name -ne "Properties" }

            if ($Name) {
                $proplist = $proplist | Where-Object { $_.Name -in $Name }
            }

            #Grab the default sp_configure property values from the external function
            $defaultConfigs = (Get-SqlDefaultSpConfigure -SqlVersion $server.VersionMajor).psobject.properties;

            #Iterate through the properties to get the configuration settings
            foreach ($prop in $proplist) {
                $propInfo = $server.Configuration.$($prop.Name)
                $defaultConfig = $defaultConfigs | Where-Object { $_.Name -eq $propInfo.DisplayName };

                if ($defaultConfig.Value -eq $propInfo.RunValue) { $isDefault = $true }
                else { $isDefault = $false }

                #Ignores properties that are not valid on this version of SQL
                if (!([string]::IsNullOrEmpty($propInfo.RunValue))) {
                    # some displaynames were empty
                    $displayname = $propInfo.DisplayName
                    if ($displayname.Length -eq 0) { $displayname = $prop.Name }

                    [pscustomobject]@{
                        ServerName            = $server.Name
                        ComputerName          = $server.ComputerName
                        InstanceName          = $server.ServiceName
                        SqlInstance           = $server.DomainInstanceName
                        Name                  = $prop.Name
                        DisplayName           = $displayname
                        Description           = $propInfo.Description
                        IsAdvanced            = $propInfo.IsAdvanced
                        IsDynamic             = $propInfo.IsDynamic
                        MinValue              = $propInfo.Minimum
                        MaxValue              = $propInfo.Maximum
                        ConfiguredValue       = $propInfo.ConfigValue
                        RunningValue          = $propInfo.RunValue
                        DefaultValue          = $defaultConfig.Value
                        IsRunningDefaultValue = $isDefault
                        Parent                = $server
                        ConfigName            = $prop.Name
                    } | Select-DefaultView -ExcludeProperty ServerName, Parent, ConfigName
                }
            }
        }
    }
}
#ValidationTags#FlowControl,Pipeline#
function Get-DbaSpn {
    <#
        .SYNOPSIS
            Returns a list of set service principal names for a given computer/AD account

        .DESCRIPTION
            Get a list of set SPNs. SPNs are set at the AD account level. You can either retrieve set SPNs for a computer, or any SPNs set for
            a given active directory account. You can query one, or both. You'll get a list of every SPN found for either search term.

        .PARAMETER ComputerName
            The servers you want to return set SPNs for. This is defaulted automatically to localhost.

        .PARAMETER AccountName
            The accounts you want to retrieve set SPNs for.

        .PARAMETER Credential
            User credential to connect to the remote servers or active directory.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: SPN
            Author: Drew Furgiuele (@pittfurg), http://www.port1433.com

            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaSpn

        .EXAMPLE
            Get-DbaSpn -ServerName SQLSERVERA -Credential (Get-Credential)

            Returns a custom object with SearchTerm (ServerName) and the SPNs that were found

        .EXAMPLE
            Get-DbaSpn -AccountName domain\account -Credential (Get-Credential)

            Returns a custom object with SearchTerm (domain account) and the SPNs that were found

        .EXAMPLE
            Get-DbaSpn -ServerName SQLSERVERA,SQLSERVERB -Credential (Get-Credential)

            Returns a custom object with SearchTerm (ServerName) and the SPNs that were found for multiple computers
    #>
    [cmdletbinding()]
    param (
        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [string[]]$ComputerName,
        [Parameter(Mandatory = $false)]
        [string[]]$AccountName,
        [Parameter(Mandatory = $false)]
        [PSCredential]$Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        Function Process-Account ($AccountName) {

            ForEach ($account in $AccountName) {
                Write-Message -Message "Looking for account $account..." -Level Verbose
                $searchfor = 'User'
                if ($account.EndsWith('$')) {
                    $searchfor = 'Computer'
                }
                try {
                    $Result = Get-DbaADObject -ADObject $account -Type $searchfor -Credential $Credential -EnableException
                }
                catch {
                    Write-Message -Message "AD lookup failure. This may be because the domain cannot be resolved for the SQL Server service account ($Account)." -Level Warning
                    continue
                }
                if ($Result.Count -gt 0) {
                    try {
                        $results = $Result.GetUnderlyingObject()
                        $spns = $results.Properties.servicePrincipalName
                    }
                    catch {
                        Write-Message -Message "The SQL Service account ($Account) has been found, but you don't have enough permission to inspect its SPNs" -Level Warning
                        continue
                    }
                }
                else {
                    Write-Message -Message "The SQL Service account ($Account) has not been found" -Level Warning
                    continue
                }

                foreach ($spn in $spns) {
                    if ($spn -match "\:") {
                        try {
                            $port = [int]($spn -Split "\:")[1]
                        }
                        catch {
                            $port = $null
                        }
                        if ($spn -match "\/") {
                            $serviceclass = ($spn -Split "\/")[0]
                        }
                    }
                    [pscustomobject] @{
                        Input        = $Account
                        AccountName  = $Account
                        ServiceClass = "MSSQLSvc" # $serviceclass
                        Port         = $port
                        SPN          = $spn
                    }
                }
            }
        }
        if ($ComputerName.Count -eq 0 -and $AccountName.Count -eq 0) {
            $ComputerName = @($env:COMPUTERNAME)
        }
    }

    process {

        foreach ($computer in $ComputerName) {
            if ($computer) {
                if ($computer.EndsWith('$')) {
                    Write-Message -Message "$computer is an account name. Processing as account." -Level Verbose
                    Process-Account -AccountName $computer
                    continue
                }
            }

            Write-Message -Message "Getting SQL Server SPN for $computer" -Level Verbose
            $spns = Test-DbaSpn -ComputerName $computer -Credential $Credential

            $sqlspns = 0
            $spncount = $spns.count
            Write-Message -Message "Calculated $spncount SQL SPN entries that should exist for $computer" -Level Verbose
            foreach ($spn in $spns | Where-Object { $_.IsSet -eq $true }) {
                $sqlspns++

                if ($accountName) {
                    if ($accountName -eq $spn.InstanceServiceAccount) {
                        [pscustomobject] @{
                            Input        = $computer
                            AccountName  = $spn.InstanceServiceAccount
                            ServiceClass = "MSSQLSvc"
                            Port         = $spn.Port
                            SPN          = $spn.RequiredSPN
                        }
                    }
                }
                else {
                    [pscustomobject] @{
                        Input        = $computer
                        AccountName  = $spn.InstanceServiceAccount
                        ServiceClass = "MSSQLSvc"
                        Port         = $spn.Port
                        SPN          = $spn.RequiredSPN
                    }
                }
            }
            Write-Message -Message "Found $sqlspns set SQL SPN entries for $computer" -Level Verbose
        }

        if ($AccountName) {
            foreach ($account in $AccountName) {
                Process-Account -AccountName $account
            }
        }
    }
}
function Get-DbaSqlBuildReference {
    <#
    .SYNOPSIS
        Returns SQL Server Build infos on a SQL instance

    .DESCRIPTION
        Returns info about the specific build of a SQL instance, including the SP, the CU and the reference KB, wherever possible.
        It also includes End Of Support dates as specified on Microsoft Lifecycle Policy

    .PARAMETER Build
        Instead of connecting to a real instance, pass a string identifying the build to get the info back.

    .PARAMETER SqlInstance
        Target any number of instances, in order to return their build state.

    .PARAMETER SqlCredential
        When connecting to an instance, use the credentials specified.

    .PARAMETER Update
        Looks online for the most up to date reference, replacing the local one.

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .EXAMPLE
        Get-DbaSqlBuildReference -Build "12.00.4502"

        Returns information about a build identified by  "12.00.4502" (which is SQL 2014 with SP1 and CU11)

    .EXAMPLE
        Get-DbaSqlBuildReference -Build "12.00.4502" -Update

        Returns information about a build trying to fetch the most up to date index online. When the online version is newer, the local one gets overwritten

    .EXAMPLE
        Get-DbaSqlBuildReference -Build "12.0.4502","10.50.4260"

        Returns information builds identified by these versions strings

    .EXAMPLE
        Get-DbaRegisteredServer -SqlInstance sqlserver2014a | Get-DbaSqlBuildReference

        Integrate with other commandlets to have builds checked for all your registered servers on sqlserver2014a

    .NOTES
        Author: niphlod
        Editor: Fred
        Tags: SqlBuild

        dbatools PowerShell module (https://dbatools.io, [email protected])
        Copyright (C) 2016 Chrissy LeMaire
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Get-DbaSqlBuildReference
#>
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
    [CmdletBinding()]
    Param (
        [version[]]
        $Build,

        [parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]
        $SqlInstance,

        [Alias("Credential")]
        [PsCredential]
        $SqlCredential,

        [switch]
        $Update,

        [switch]
        [Alias('Silent')]$EnableException
    )

    begin {
        #region Helper functions
        function Get-DbaSqlBuildReferenceIndex {
            [CmdletBinding()]
            Param (
                [string]
                $Moduledirectory,

                [bool]
                $Update,

                [bool]
                $EnableException
            )

            $orig_idxfile = "$Moduledirectory\bin\dbatools-buildref-index.json"
            $DbatoolsData = Get-DbaConfigValue -Name 'Path.DbatoolsData'
            $writable_idxfile = Join-Path $DbatoolsData "dbatools-buildref-index.json"

            if (-not (Test-Path $orig_idxfile)) {
                Write-Message -Level Warning -Message "Unable to read local SQL build reference file. Check your module integrity!"
            }

            if ((-not (Test-Path $orig_idxfile)) -and (-not (Test-Path $writable_idxfile))) {
                throw "Build reference file not found, check module health!"
            }

            # If no writable copy exists, create one and return the module original
            if (-not (Test-Path $writable_idxfile)) {
                Copy-Item -Path $orig_idxfile -Destination $writable_idxfile -Force -ErrorAction Stop
                $result = Get-Content $orig_idxfile -Raw | ConvertFrom-Json
            }

            # Else, if both exist, update the writeable if necessary and return the current version
            elseif (Test-Path $orig_idxfile) {
                $module_content = Get-Content $orig_idxfile -Raw | ConvertFrom-Json
                $data_content = Get-Content $writable_idxfile -Raw | ConvertFrom-Json

                $module_time = Get-Date $module_content.LastUpdated
                $data_time = Get-Date $data_content.LastUpdated

                $offline_time = $module_time
                if ($module_time -gt $data_time) {
                    Copy-Item -Path $orig_idxfile -Destination $writable_idxfile -Force -ErrorAction Stop
                    $result = $module_content
                }
                else {
                    $result = $data_content
                    $offline_time = $data_time
                }
                # If Update is passed, try to fetch from online resource and store into the writeable
                if ($Update) {
                    $WebContent = Get-DbaSqlBuildReferenceIndexOnline -EnableException $EnableException
                    if ($null -ne $WebContent) {
                        $webdata_content = $WebContent.Content | ConvertFrom-Json
                        $webdata_time = Get-Date $webdata_content.LastUpdated
                        if ($webdata_time -gt $offline_time) {
                            Write-Message -Level Output -Message "Index updated correctly, last update on: $(Get-Date -Date $webdata_time -Format s), was $(Get-Date -Date $offline_time -Format s)"
                            $WebContent.Content | Out-File $writable_idxfile -Encoding utf8 -ErrorAction Stop
                            $result = Get-Content $writable_idxfile -Raw | ConvertFrom-Json
                        }
                    }
                }
            }

            # Else if the module version of the file no longer exists, but the writable version exists, return the writable version
            else {
                $result = Get-Content $writable_idxfile -Raw | ConvertFrom-Json
            }

            $LastUpdated = Get-Date -Date $result.LastUpdated
            if ($LastUpdated -lt (Get-Date).AddDays(-45)) {
                Write-Message -Level Warning -Message "Index is stale, last update on: $(Get-Date -Date $LastUpdated -Format s), try the -Update parameter to fetch the most up to date index"
            }

            $result.Data | Select-Object @{ Name = "VersionObject"; Expression = { [version]$_.Version } }, *
        }

        function Get-DbaSqlBuildReferenceIndexOnline {
            [CmdletBinding()]
            Param (
                [bool]
                $EnableException
            )
            $url = Get-DbaConfigValue -Name 'assets.sqlbuildreference'
            try {
                $WebContent = Invoke-WebRequest $url -ErrorAction Stop
            }
            catch {
                try {
                    Write-Message -Level Verbose -Message "Probably using a proxy for internet access, trying default proxy settings"
                    (New-Object System.Net.WebClient).Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
                    $WebContent = Invoke-WebRequest $url -ErrorAction Stop
                }
                catch {
                    Write-Message -Level Warning -Message "Couldn't download updated index from $url"
                    return
                }
            }
            return $WebContent
        }

        function Resolve-DbaSqlBuild {
            [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
            [CmdletBinding()]
            [OutputType([System.Collections.Hashtable])]
            Param (
                [version]
                $Build,

                $Data,

                [bool]
                $EnableException
            )

            Write-Message -Level Verbose -Message "Looking for $Build"

            $IdxVersion = $Data | Where-Object Version -like "$($Build.Major).$($Build.Minor).*"
            $Detected = @{ }
            $Detected.MatchType = 'Approximate'
            Write-Message -Level Verbose -Message "We have $($IdxVersion.Length) builds in store for this Release"
            If ($IdxVersion.Length -eq 0) {
                Write-Message -Level Warning -Message "No info in store for this Release"
                $Detected.Warning = "No info in store for this Release"
            }
            else {
                $LastVer = $IdxVersion[0]
            }
            foreach ($el in $IdxVersion) {
                if ($null -ne $el.Name) {
                    $Detected.Name = $el.Name
                }
                if ($el.VersionObject -gt $Build) {
                    $Detected.MatchType = 'Approximate'
                    $Detected.Warning = "$Build not found, closest build we have is $($LastVer.Version)"
                    break
                }
                $LastVer = $el
                if ($null -ne $el.SP) {
                    $Detected.SP = $el.SP
                    $Detected.CU = $null
                }
                if ($null -ne $el.CU) {
                    $Detected.CU = $el.CU
                }
                if ($null -ne $el.SupportedUntil) {
                    $Detected.SupportedUntil = (Get-Date -date $el.SupportedUntil)
                }
                $Detected.KB = $el.KBList
                if ($el.Version -eq $Build) {
                    $Detected.MatchType = 'Exact'
                    break
                }
            }
            return $Detected
        }
        #endregion Helper functions

        $moduledirectory = $MyInvocation.MyCommand.Module.ModuleBase

        try {
            $IdxRef = Get-DbaSqlBuildReferenceIndex -Moduledirectory $moduledirectory -Update $Update -EnableException $EnableException
        }
        catch {
            Stop-Function -Message "Error loading SQL build reference" -ErrorRecord $_
            return
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $SqlInstance) {
            #region Ensure the connection is established
            try {
                Write-Message -Level VeryVerbose -Message "Connecting to $instance" -Target $instance
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failed to process Instance $Instance" -ErrorRecord $_ -Target $instance -Continue
            }

            try {
                $null = $server.Version.ToString()
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            #endregion Ensure the connection is established

            $Detected = Resolve-DbaSqlBuild -Build $server.Version -Data $IdxRef -EnableException $EnableException

            [PSCustomObject]@{
                SqlInstance    = $server.DomainInstanceName
                Build          = $server.Version
                NameLevel      = $Detected.Name
                SPLevel        = $Detected.SP
                CULevel        = $Detected.CU
                KBLevel        = $Detected.KB
                SupportedUntil = $Detected.SupportedUntil
                MatchType      = $Detected.MatchType
                Warning        = $Detected.Warning
            }
        }

        foreach ($buildstr in $Build) {
            $Detected = Resolve-DbaSqlBuild -Build $buildstr -Data $IdxRef -EnableException $EnableException

            [PSCustomObject]@{
                SqlInstance    = $null
                Build          = $buildstr
                NameLevel      = $Detected.Name
                SPLevel        = $Detected.SP
                CULevel        = $Detected.CU
                KBLevel        = $Detected.KB
                SupportedUntil = $Detected.SupportedUntil
                MatchType      = $Detected.MatchType
                Warning        = $Detected.Warning
            } | Select-DefaultView -ExcludeProperty SqlInstance
        }
    }
}
#ValidationTags#CodeStyle,Messaging,FlowControl,Pipeline#
function Get-DbaSqlFeature {
    <#
        .SYNOPSIS
             Runs the SQL Server feature discovery report (setup.exe /Action=RunDiscovery)

        .DESCRIPTION
            Runs the SQL Server feature discovery report (setup.exe /Action=RunDiscovery)

            Inspired by Dave Mason's (@BeginTry) post at
            https://itsalljustelectrons.blogspot.be/2018/04/SQL-Server-Discovery-Report.html

            Assumptions:
            1. The sub-folder "Microsoft SQL Server" exists in $env:ProgramFiles,
                even if SQL was installed to a non-default path. This has been
                verified on SQL 2008R2 and SQL 2012. Further verification may be needed.
            2. The discovery report displays installed components for the version of SQL
                Server associated with setup.exe, along with installed components of all
                lesser versions of SQL Server that are installed.

        .PARAMETER ComputerName
            The target computer. If the target is not localhost, it must have PowerShell remoting enabled.

            Note that this is not the SqlInstance, but rather the ComputerName

        .PARAMETER Credential
            Allows you to login to servers using alternative credentials. To use:

            $cred = Get-Credential, then pass $cred object to the -Credential parameter.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Feature, Component
            Author: Chrissy LeMaire (@cl)
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaSqlFeature

        .EXAMPLE
            Get-DbaSqlFeature -ComputerName sql2017, sql2016, sql2005

            Gets all SQL Server features for all instances on sql2017, sql2016 and sql2005.

        .EXAMPLE
            Get-DbaSqlFeature -Verbose

            Gets all SQL Server features for all instances on localhost. Outputs to screen if no instances are found.

        .EXAMPLE
            Get-DbaSqlFeature -ComputerName sql2017 -Credential (Get-Credential ad\sqladmin)

            Gets all SQL Server features for all instances on sql2017 using the ad\sqladmin credential (which has access to the Windows Server).
    #>
    [CmdletBinding()]
    Param (
        [parameter(ValueFromPipeline)]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [switch]$EnableException
    )

    begin {
        $scriptblock = {
            $setup = Get-ChildItem -Recurse -Include setup.exe -Path "$env:ProgramFiles\Microsoft SQL Server" -ErrorAction SilentlyContinue |
                Where-Object { $_.FullName -match 'Setup Bootstrap\\SQL' -or $_.FullName -match 'Bootstrap\\Release\\Setup.exe' -or $_.FullName -match 'Bootstrap\\Setup.exe' } |
                Sort-Object FullName -Descending | Select-Object -First 1
            if ($setup) {
                $null = Start-Process -FilePath $setup.FullName -ArgumentList "/Action=RunDiscovery /q" -Wait
                $parent = Split-Path (Split-Path $setup.Fullname)
                $xmlfile = Get-ChildItem -Recurse -Include SqlDiscoveryReport.xml -Path $parent | Sort-Object LastWriteTime -Descending | Select-Object -First 1

                if ($xmlfile) {
                    $xml = [xml](Get-Content -Path $xmlfile)
                    $xml.ArrayOfDiscoveryInformation.DiscoveryInformation
                }
            }
        }
    }

    process {
        foreach ($computer in $ComputerName) {
            try {
                $results = Invoke-Command2 -ComputerName $Computer -ScriptBlock $scriptblock -Credential $Credential -Raw

                if (-not $results) {
                    Write-Message -Level Verbose -Message "No features found on $computer"
                }

                foreach ($result in $results) {
                    [pscustomobject]@{
                        ComputerName = $computer
                        Product      = $result.Product
                        Instance     = $result.Instance
                        InstanceID   = $result.InstanceID
                        Feature      = $result.Feature
                        Language     = $result.Language
                        Edition      = $result.Edition
                        Version      = $result.Version
                        Clustered    = $result.Clustered
                        Configured   = $result.Configured
                    }
                }
            }
            catch {
                Stop-Function -Continue -ErrorRecord $_ -Message "Failure"
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaSqlInstanceProperty {
    <#
        .SYNOPSIS
            Gets SQL Server instance properties of one or more instance(s) of SQL Server.

        .DESCRIPTION
            The Get-DbaSqlInstanceProperty command gets SQL Server instance properties from the SMO object sqlserver.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER InstanceProperty
            SQL Server instance property(ies) to include.

        .PARAMETER ExcludeInstanceProperty
            SQL Server instance property(ies) to exclude.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Instance, Configure, Configuration
            Author: Klaas Vandenberghe (@powerdbaklaas)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaSqlInstanceProperty

        .EXAMPLE
            Get-DbaSqlInstanceProperty -SqlInstance localhost

            Returns SQL Server instance properties on the local default SQL Server instance

        .EXAMPLE
            Get-DbaSqlInstanceProperty -SqlInstance sql2, sql4\sqlexpress

            Returns SQL Server instance properties on default instance on sql2 and sqlexpress instance on sql4

        .EXAMPLE
            'sql2','sql4' | Get-DbaSqlInstanceProperty

            Returns SQL Server instance properties on sql2 and sql4

        .EXAMPLE
            Get-DbaSqlInstanceProperty -SqlInstance sql2,sql4 -InstanceProperty DefaultFile

            Returns SQL Server instance property DefaultFile on instance sql2 and sql4

        .EXAMPLE
            Get-DbaSqlInstanceProperty -SqlInstance sql2,sql4 -ExcludeInstanceProperty DefaultFile

            Returns all SQL Server instance properties except DefaultFile on instance sql2 and sql4

        .EXAMPLE
            $cred = Get-Credential sqladmin
            Get-DbaSqlInstanceProperty -SqlInstance sql2 -SqlCredential $cred

            Connects using sqladmin credential and returns SQL Server instance properties from sql2
    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$InstanceProperty,
        [object[]]$ExcludeInstanceProperty,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            try {
                $infoProperties = $server.Information.Properties

                if ($InstanceProperty) {
                    $infoProperties = $infoProperties | Where-Object Name -In $InstanceProperty
                }
                if ($ExcludeInstanceProperty) {
                    $infoProperties = $infoProperties | Where-Object Name -NotIn $ExcludeInstanceProperty
                }
                foreach ($prop in $infoProperties) {
                    Add-Member -Force -InputObject $prop -MemberType NoteProperty -Name ComputerName -Value $server.ComputerName
                    Add-Member -Force -InputObject $prop -MemberType NoteProperty -Name InstanceName -Value $server.ServiceName
                    Add-Member -Force -InputObject $prop -MemberType NoteProperty -Name SqlInstance -Value $server.DomainInstanceName
                    Add-Member -Force -InputObject $prop -MemberType NoteProperty -Name PropertyType -Value 'Information'
                    Select-DefaultView -InputObject $prop -Property ComputerName, InstanceName, SqlInstance, Name, Value, PropertyType
                }
            }
            catch {
                Stop-Function -Message "Issue gathering information properties for $instance." -Target $instance -ErrorRecord $_ -Continue
            }

            try {
                $userProperties = $server.UserOptions.Properties

                if ($InstanceProperty) {
                    $userProperties = $userProperties | Where-Object Name -In $InstanceProperty
                }
                if ($ExcludeInstanceProperty) {
                    $userProperties = $userProperties | Where-Object Name -NotIn $ExcludeInstanceProperty
                }
                foreach ($prop in $userProperties) {
                    Add-Member -Force -InputObject $prop -MemberType NoteProperty -Name ComputerName -Value $server.ComputerName
                    Add-Member -Force -InputObject $prop -MemberType NoteProperty -Name InstanceName -Value $server.ServiceName
                    Add-Member -Force -InputObject $prop -MemberType NoteProperty -Name SqlInstance -Value $server.DomainInstanceName
                    Add-Member -Force -InputObject $prop -MemberType NoteProperty -Name PropertyType -Value 'UserOption'
                    Select-DefaultView -InputObject $prop -Property ComputerName, InstanceName, SqlInstance, Name, Value, PropertyType
                }
            }
            catch {
                Stop-Function -Message "Issue gathering user options for $instance." -Target $instance -ErrorRecord $_ -Continue
            }

            try {
                $settingProperties = $server.Settings.Properties

                if ($InstanceProperty) {
                    $settingProperties = $settingProperties | Where-Object Name -In $InstanceProperty
                }
                if ($ExcludeInstanceProperty) {
                    $settingProperties = $settingProperties | Where-Object Name -NotIn $ExcludeInstanceProperty
                }
                foreach ($prop in $settingProperties) {
                    Add-Member -Force -InputObject $prop -MemberType NoteProperty -Name ComputerName -Value $server.ComputerName
                    Add-Member -Force -InputObject $prop -MemberType NoteProperty -Name InstanceName -Value $server.ServiceName
                    Add-Member -Force -InputObject $prop -MemberType NoteProperty -Name SqlInstance -Value $server.DomainInstanceName
                    Add-Member -Force -InputObject $prop -MemberType NoteProperty -Name PropertyType -Value 'Setting'
                    Select-DefaultView -InputObject $prop -Property ComputerName, InstanceName, SqlInstance, Name, Value, PropertyType
                }
            }
            catch {
                Stop-Function -Message "Issue gathering settings for $instance." -Target $instance -ErrorRecord $_ -Continue
            }
        }
    }
}
function Get-DbaSqlInstanceUserOption {
    <#
        .SYNOPSIS
            Gets SQL Instance user options of one or more instance(s) of SQL Server.

        .DESCRIPTION
            The Get-DbaSqlInstanceUserOption command gets SQL Instance user options from the SMO object sqlserver.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to.
            This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Instance, Configure, UserOption
            Author: Klaas Vandenberghe (@powerdbaklaas)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaSqlInstanceUserOption

        .EXAMPLE
            Get-DbaSqlInstanceUserOption -SqlInstance localhost

            Returns SQL Instance user options on the local default SQL Server instance

        .EXAMPLE
            Get-DbaSqlInstanceUserOption -SqlInstance sql2, sql4\sqlexpress

            Returns SQL Instance user options on default instance on sql2 and sqlexpress instance on sql4

        .EXAMPLE
            'sql2','sql4' | Get-DbaSqlInstanceUserOption

            Returns SQL Instance user options on sql2 and sql4
    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            $props = $server.useroptions.properties
            foreach ($prop in $props) {
                Add-Member -Force -InputObject $prop -MemberType NoteProperty -Name ComputerName -Value $server.ComputerName
                Add-Member -Force -InputObject $prop -MemberType NoteProperty -Name InstanceName -Value $server.ServiceName
                Add-Member -Force -InputObject $prop -MemberType NoteProperty -Name SqlInstance -Value $server.DomainInstanceName
                Select-DefaultView -InputObject $prop -Property ComputerName, InstanceName, SqlInstance, Name, Value
            }
        }
    }
}
function Get-DbaSqlManagementObject {
    <#
        .SYNOPSIS
            Gets SQL Mangaement Object versions installed on the machine.

        .DESCRIPTION
            The Get-DbaSqlManagementObject returns an object with the Version and the
            Add-Type Load Template for each version on the server.

        .PARAMETER ComputerName
            The name of the target you would like to check

        .PARAMETER Credential
            This command uses Windows credentials. This parameter allows you to connect remotely as a different user.

        .PARAMETER VersionNumber
            This is the specific version number you are looking for. The function will look
            for that version only.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: SMO
            Author: Ben Miller (@DBAduck - http://dbaduck.com)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaSqlManagementObject

        .EXAMPLE
            Get-DbaSqlManagementObject

            Returns all versions of SMO on the computer

        .EXAMPLE
            Get-DbaSqlManagementObject -VersionNumber 13

            Returns just the version specified. If the version does not exist then it will return nothing.

    #>
    [CmdletBinding()]
    param (
        [parameter(ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer", "SqlInstance")]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]
        $Credential,
        [int]$VersionNumber,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        if (!$VersionNumber) {
            $VersionNumber = 0
        }
        $scriptblock = {
            $VersionNumber = [int]$args[0]

            Write-Verbose -Message "Checking currently loaded SMO version"
            $loadedversion = [AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.Fullname -like "Microsoft.SqlServer.SMO,*" }
            if ($loadedversion) {
                $loadedversion = $loadedversion | ForEach-Object {
                    if ($_.Location -match "__") {
                        ((Split-Path (Split-Path $_.Location) -Leaf) -split "__")[0]
                    }
                    else {
                        ((Get-ChildItem -Path $_.Location).VersionInfo.ProductVersion)
                    }
                }
            }

            Write-Verbose -Message "Looking for included smo library"
            $localversion = [version](Get-ChildItem -Path "$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.Smo.dll").VersionInfo.ProductVersion

            foreach ($version in $localversion) {
                if ($VersionNumber -eq 0) {
                    Write-Verbose -Message "Did not pass a version"
                    [PSCustomObject]@{
                        ComputerName = $env:COMPUTERNAME
                        Version      = $localversion
                        Loaded       = $loadedversion -contains $localversion
                        LoadTemplate = "Add-Type -Path $("$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.Smo.dll")"
                    }
                }
                else {
                    Write-Verbose -Message "Passed version $VersionNumber, looking for that specific version"
                    if ($localversion.ToString().StartsWith("$VersionNumber.")) {
                        Write-Verbose -Message "Found the Version $VersionNumber"
                        [PSCustomObject]@{
                            ComputerName = $env:COMPUTERNAME
                            Version      = $localversion
                            Loaded       = $loadedversion -contains $localversion
                            LoadTemplate = "Add-Type -Path $("$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.Smo.dll")"
                        }
                    }
                }
            }

            Write-Verbose -Message "Looking for SMO in the Global Assembly Cache"
            $smolist = (Get-ChildItem -Path "$env:SystemRoot\assembly\GAC_MSIL\Microsoft.SqlServer.Smo" | Sort-Object Name -Descending).Name

            foreach ($version in $smolist) {
                $array = $version.Split("__")
                if ($VersionNumber -eq 0) {
                    Write-Verbose -Message "Did not pass a version, looking for all versions"
                    $currentversion = $array[0]
                    [PSCustomObject]@{
                        ComputerName = $env:COMPUTERNAME
                        Version      = $currentversion
                        Loaded       = $loadedversion -contains $currentversion
                        LoadTemplate = "Add-Type -AssemblyName `"Microsoft.SqlServer.Smo, Version=$($array[0]), Culture=neutral, PublicKeyToken=89845dcd8080cc91`""
                    }
                }
                else {
                    Write-Verbose -Message "Passed version $VersionNumber, looking for that specific version"
                    if ($array[0].StartsWith("$VersionNumber.")) {
                        Write-Verbose -Message "Found the Version $VersionNumber"
                        $currentversion = $array[0]
                        [PSCustomObject]@{
                            ComputerName = $env:COMPUTERNAME
                            Version      = $currentversion
                            Loaded       = $loadedversion -contains $currentversion
                            LoadTemplate = "Add-Type -AssemblyName `"Microsoft.SqlServer.Smo, Version=$($array[0]), Culture=neutral, PublicKeyToken=89845dcd8080cc91`""
                        }
                    }
                }
            }
        }
    }

    process {
        foreach ($computer in $ComputerName.ComputerName) {
            try {
                Write-Message -Level Verbose -Message "Executing scriptblock against $computer"
                Invoke-Command2 -ComputerName $computer -ScriptBlock $scriptblock -Credential $Credential -ArgumentList $VersionNumber -ErrorAction Stop
            }
            catch {
                Stop-Function -Continue -Message "Failure" -ErrorRecord $_ -Target $ComputerName
            }
        }
    }
}
function Get-DbaSqlModule {
    <#
    .SYNOPSIS
    Displays all objects in sys.sys_modules after specified modification date.  Works on SQL Server 2008 and above.

    .DESCRIPTION
    Quickly find modules (Stored Procs, Functions, Views, Constraints, Rules, Triggers, etc) that have been modified in a database, or across all databases.
    Results will exclude the module definition, but can be queried explicitly.

    .PARAMETER SqlInstance
    Allows you to specify a comma separated list of servers to query.

    .PARAMETER SqlCredential
    Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

    .PARAMETER Database
    The database(s) to process. If unspecified, all databases will be processed.

    .PARAMETER ExcludeDatabase
    The database(s) to exclude.

    .PARAMETER ModifiedSince
    DateTime value to use as minimum modified date of module.

    .PARAMETER Type
    Limit by specific type of module. Valid choices include: View, TableValuedFunction, DefaultConstraint, StoredProcedure, Rule, InlineTableValuedFunction, Trigger, ScalarFunction

    .PARAMETER NoSystemDb
    Allows you to suppress output on system databases

    .PARAMETER NoSystemObjects
    Allows you to suppress output on system objects

    .PARAMETER EnableException
    By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
    Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
    Author: Brandon Abshire, netnerds.net
    Tags: StoredProcedure, Trigger

    Website: https://dbatools.io
    Copyright: (C) Chrissy LeMaire, [email protected]
    License: MIT https://opensource.org/licenses/MIT

    .LINK
    https://dbatools.io/Get-DbaSqlModule

    .EXAMPLE
    Get-DbaSqlModule -SqlServer sql2008, sqlserver2012
    Return all modules for servers sql2008 and sqlserver2012 sorted by Database, Modify_Date ASC.

    .EXAMPLE
    Get-DbaSqlModule -SqlServer sql2008, sqlserver2012 | Select *
    Shows hidden definition column (informative wall of text).

    .EXAMPLE
    Get-DbaSqlModule -SqlServer sql2008 -Database TestDB -ModifiedSince "01/01/2017 10:00:00 AM"
    Return all modules on server sql2008 for only the TestDB database with a modified date after 01/01/2017 10:00:00 AM.

    .EXAMPLE
    Get-DbaSqlModule -SqlServer sql2008 -Type View, Trigger, ScalarFunction
    Return all modules on server sql2008 for all databases that are triggers, views or scalar functions.
#>
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [datetime]$ModifiedSince = "01/01/1900",
        [ValidateSet("View", "TableValuedFunction", "DefaultConstraint", "StoredProcedure", "Rule", "InlineTableValuedFunction", "Trigger", "ScalarFunction")]
        [string[]]$Type,
        [switch]$NoSystemDb,
        [switch]$NoSystemObjects,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {

        $types = @()

        foreach ($t in $type) {
            if ($t -eq "View") { $types += "VIEW" }
            if ($t -eq "TableValuedFunction") { $types += "SQL_TABLE_VALUED_FUNCTION" }
            if ($t -eq "DefaultConstraint") { $types += "DEFAULT_CONSTRAINT" }
            if ($t -eq "StoredProcedure") { $types += "SQL_STORED_PROCEDURE" }
            if ($t -eq "Rule") { $types += "RULE" }
            if ($t -eq "InlineTableValuedFunction") { $types += "SQL_INLINE_TABLE_VALUED_FUNCTION" }
            if ($t -eq "Trigger") { $types += "SQL_TRIGGER" }
            if ($t -eq "ScalarFunction") { $types += "SQL_SCALAR_FUNCTION" }
        }


        $sql = "SELECT  DB_NAME() AS DatabaseName,
        so.name AS ModuleName,
        so.object_id ,
        SCHEMA_NAME(so.schema_id) AS SchemaName ,
        so.parent_object_id ,
        so.type ,
        so.type_desc ,
        so.create_date ,
        so.modify_date ,
        so.is_ms_shipped ,
        sm.definition,
         OBJECTPROPERTY(so.object_id, 'ExecIsStartUp') as startup
        FROM sys.sql_modules sm
        LEFT JOIN sys.objects so ON sm.object_id = so.object_id
        WHERE so.modify_date >= '$($ModifiedSince)'"
        if ($NoSystemObjects) {
            $sql += "`n AND so.is_ms_shipped = 0"
        }
        if ($Type) {
            $sqltypes = $types -join "','"
            $sql += " AND type_desc in ('$sqltypes')"
        }
        $sql += "`n ORDER BY so.modify_date"
    }

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $databases = Get-DbaDatabase -SqlInstance $server

            if ($Database) {
                $databases = $databases | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }


            foreach ($db in $databases) {

                Write-Message -Level Verbose -Message "Processing $db on $instance"

                if ($db.IsAccessible -eq $false) {
                    Stop-Function -Message "The database $db is not accessible. Skipping database." -Target $db -Continue
                }

                foreach ($row in $server.Query($sql, $db.name)) {
                    [PSCustomObject]@{
                        ComputerName  = $server.ComputerName
                        InstanceName  = $server.ServiceName
                        SqlInstance   = $server.DomainInstanceName
                        Database      = $row.DatabaseName
                        Name          = $row.ModuleName
                        ObjectID      = $row.object_id
                        SchemaName    = $row.SchemaName
                        Type          = $row.type_desc
                        CreateDate    = $row.create_date
                        ModifyDate    = $row.modify_date
                        IsMsShipped   = $row.is_ms_shipped
                        ExecIsStartUp = $row.startup
                        Definition    = $row.definition
                    } | Select-DefaultView -ExcludeProperty Definition
                }
            }
        }
    }
}
function Get-DbaSqlProductKey {
    <#
.SYNOPSIS
Gets SQL Server Product Keys from local or destination SQL Servers. Works with SQL Server 2005-2016

.DESCRIPTION
Using a string of servers, a text file, or Central Management Server to provide a list of servers, this script will go to each server and get the product key for all installed instances. Clustered instances are supported as well. Requires regular user access to the SQL instances, SMO installed locally, Remote Registry enabled and accessible by the account running the script.

Uses key decoder by Jakob Bindslet (http://goo.gl/1jiwcB)

.PARAMETER SqlInstances
A comma separated list of servers. This can be the NetBIOS name, IP, or SQL instance name

.PARAMETER SqlCms
Compiles list of servers to inventory using all servers stored within a Central Management Server. Requires having SQL Management Studio installed.

.PARAMETER ServersFromFile
Uses a text file as input. The file must be formatted as such:
sqlserver1
sqlserver2

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.NOTES
Author: Chrissy LeMaire (@cl), netnerds.net
Tags: SQL, Product Key

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Get-DbaSqlProductKey

.EXAMPLE
Get-DbaSqlProductKey winxp, sqlservera, sqlserver2014a, win2k8
Gets SQL Server versions, editions and product keys for all instances within each server or workstation.

.EXAMPLE
Get-DbaSqlProductKey -SqlCms sqlserver01
Gets SQL Server versions, editions and product keys for all instances within sqlserver01's Central Management Server

.EXAMPLE
Get-DbaSqlProductKey -ServersFromFile C:\Scripts\servers.txt
Gets SQL Server versions, editions and product keys for all instances listed within C:\Scripts\servers.txt
#>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    Param (
        [parameter(Position = 0)]
        [Alias("ServerInstance", "SqlServer")]
        [string[]]$SqlInstances,
        # Central Management Server

        [string]$SqlCms,
        # File with one server per line

        [string]$ServersFromFile,
        [PSCredential]$SqlCredential
    )

    BEGIN {

        Function Unlock-SqlInstanceKey {
            [CmdletBinding()]
            param (
                [Parameter(Mandatory = $true)]
                [byte[]]$data,
                [int]$version
            )
            try {
                if ($version -ge 11) { $binArray = ($data)[0..66] }
                else { $binArray = ($data)[52..66] }
                $charsArray = "B", "C", "D", "F", "G", "H", "J", "K", "M", "P", "Q", "R", "T", "V", "W", "X", "Y", "2", "3", "4", "6", "7", "8", "9"
                for ($i = 24; $i -ge 0; $i--) {
                    $k = 0
                    for ($j = 14; $j -ge 0; $j--) {
                        $k = $k * 256 -bxor $binArray[$j]
                        $binArray[$j] = [math]::truncate($k / 24)
                        $k = $k % 24
                    }
                    $productKey = $charsArray[$k] + $productKey
                    if (($i % 5 -eq 0) -and ($i -ne 0)) {
                        $productKey = "-" + $productKey
                    }
                }
            }
            catch { $productkey = "Cannot decode product key." }
            return $productKey
        }
    }

    PROCESS {

        if ($SqlCms) {
            if ($null -eq [Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Management.RegisteredServers"))
            { throw "Can't load CMS assemblies. You must have SQL Server Management Studio installed to use the -SqlCms switch." }

            Write-Verbose "Gathering SQL Servers names from Central Management Server"
            $server = Connect-SqlInstance -SqlInstance $SqlCms -SqlCredential $SqlCredential
            $sqlconnection = $server.ConnectionContext.SqlConnectionObject

            try { $cmstore = New-Object Microsoft.SqlServer.Management.RegisteredServers.RegisteredServersStore($sqlconnection) }
            catch { throw "Cannot access Central Management Server" }
            $dbstore = $cmstore.DatabaseEngineServerGroup
            $SqlInstances = $dbstore.GetDescendantRegisteredServers().servername
            # Add the CM server itself, which can't be stored in the CM server.
            $servers += $SqlCms
            $basenames = @()
            foreach ($server in $SqlInstances) { $basenames += $server.Split("\")[0] }
            $SqlInstances = $basenames | Get-Unique
        }

        If ($ServersFromFile) {
            if ((Test-Path $ServersFromFile) -eq $false) { throw "Could not find file: $ServersFromFile" }
            $SqlInstances = Get-Content $ServersFromFile
        }

        if ([string]::IsNullOrEmpty($SqlInstances)) { $SqlInstances = $env:computername }

        $basepath = "SOFTWARE\Microsoft\Microsoft SQL Server"
        # Loop through each server
        $objectCollection = @()
        foreach ($servername in $SqlInstances) {
            $servername = $servername.Split("\")[0]

            if ($servername -eq "." -or $servername -eq "localhost" -or $servername -eq $env:computername) {
                $localmachine = [Microsoft.Win32.RegistryHive]::LocalMachine
                $defaultview = [Microsoft.Win32.RegistryView]::Default
                $reg = [Microsoft.Win32.RegistryKey]::OpenBaseKey($localmachine, $defaultview)
            }
            else {
                # Get IP for remote registry access. It's the most reliable.
                try { $ipaddr = ([System.Net.Dns]::GetHostAddresses($servername)).IPAddressToString }
                catch { Write-Warning "Can't resolve $servername. Skipping."; continue }

                try {
                    $reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("LocalMachine", $ipaddr)
                }
                catch { Write-Warning "Can't access registry for $servername. Is the Remote Registry service started?"; continue }
            }

            $instances = $reg.OpenSubKey("$basepath\Instance Names\SQL", $false)
            if ($instances -eq $null) { Write-Warning "No instances found on $servername. Skipping."; continue }
            # Get Product Keys for all instances on the server.
            foreach ($instance in $instances.GetValueNames()) {
                if ($instance -eq "MSSQLSERVER") { $SqlInstance = $servername }
                else { $SqlInstance = "$servername\$instance" }

                $subkeys = $reg.OpenSubKey("$basepath", $false)
                $instancekey = $subkeys.GetSubKeynames() | Where-Object { $_ -like "*.$instance" }
                if ($null -eq $instancekey) { $instancekey = $instance } # SQL 2k5

                # Cluster instance hostnames are required for SMO connection
                $cluster = $reg.OpenSubKey("$basepath\$instancekey\Cluster", $false)
                if ($cluster -ne $null) {
                    $clustername = $cluster.GetValue("ClusterName")
                    if ($instance -eq "MSSQLSERVER") { $SqlInstance = $clustername }
                    else { $SqlInstance = "$clustername\$instance" }
                }

                Write-Verbose "Connecting to $SqlInstance"
                try { $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential }
                catch { Write-Warning "Can't connect to $SqlInstance or access denied. Skipping."; continue }

                $servicePack = $server.ProductLevel
                Write-Debug "$servername $instance version is $($server.VersionMajor)"
                switch ($server.VersionMajor) {
                    9 {
                        $sqlversion = "SQL Server 2005 $servicePack"
                        $findkeys = $reg.OpenSubKey("$basepath\90\ProductID", $false)
                        foreach ($findkey in $findkeys.GetValueNames()) {
                            if ($findkey -like "DigitalProductID*") { $key = "$basepath\90\ProductID\$findkey" }
                        }
                    }
                    10 {
                        $sqlversion = "SQL Server 2008 $servicePack"
                        $key = "$basepath\MSSQL10"
                        if ($server.VersionMinor -eq 50) { $key += "_50"; $sqlversion = "SQL Server 2008 R2 $servicePack" }
                        $key += ".$instance\Setup\DigitalProductID"
                    }
                    11 { $key = "$basepath\110\Tools\Setup\DigitalProductID"; $sqlversion = "SQL Server 2012 $servicePack" }
                    12 { $key = "$basepath\120\Tools\Setup\DigitalProductID"; $sqlversion = "SQL Server 2014 $servicePack" }
                    13 { $key = "$basepath\130\Tools\Setup\DigitalProductID"; $sqlversion = "SQL Server 2016 $servicePack" }
                    default { Write-Warning "SQL version not currently supported."; continue }
                }
                if ($server.Edition -notlike "*Express*") {
                    try {
                        $subkey = Split-Path $key; $binaryvalue = Split-Path $key -leaf
                        $binarykey = $($reg.OpenSubKey($subkey)).GetValue($binaryvalue)
                    }
                    catch { $sqlkey = "Could not connect." }
                    try { $sqlkey = Unlock-SqlInstanceKey $binarykey $server.VersionMajor }
                    catch { }
                }
                else { $sqlkey = "SQL Server Express Edition" }
                $server.ConnectionContext.Disconnect()

                $object = New-Object PSObject -Property @{
                    "SQL Instance" = $SqlInstance
                    "SQL Version"  = $sqlversion
                    "SQL Edition"  = $server.Edition
                    "Product Key"  = $sqlkey
                }
                $objectCollection += $object
            }
            $reg.Close()
        }
        $objectCollection | Select "SQL Instance", "SQL Version", "SQL Edition", "Product Key"
    }

    END {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-SqlServerKey
    }
}
function Get-DbaSqlRegistryRoot {
    <#
.SYNOPSIS
Uses SQL WMI to find the Registry Root of each SQL Server instance on a computer

.DESCRIPTION
Uses SQL WMI to find the Registry Root of each SQL Server instance on a computer

.PARAMETER ComputerName
The target computer. This is not a SQL Server service, though if you pass a named SQL instance, it'll parse properly down to the computer name

.PARAMETER Credential
Allows you to login to $ComputerName using alternative Windows credentials

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Configuration, Registry

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
Get-DbaSqlRegistryRoot
Gets the registry root for all instances on localhost

.EXAMPLE
Get-DbaSqlRegistryRoot -ComputerName server1

Gets the registry root for all instances on server1

#>
    [CmdletBinding()]
    param (
        [parameter(ValueFromPipeline)]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($computer in $computername) {
            Write-Message -Level Verbose -Message "Connecting to SQL WMI on $($computer.ComputerName)"
            try {
                $sqlwmis = Invoke-ManagedComputerCommand -ComputerName $computer.ComputerName -ScriptBlock { $wmi.Services } -Credential $Credential -ErrorAction Stop | Where-Object DisplayName -match "SQL Server \("
            }
            catch {
                Stop-Function -Message $_ -Target $sqlwmi -Continue
            }

            foreach ($sqlwmi in $sqlwmis) {

                $regroot = ($sqlwmi.AdvancedProperties | Where-Object Name -eq REGROOT).Value
                $vsname = ($sqlwmi.AdvancedProperties | Where-Object Name -eq VSNAME).Value
                $instancename = $sqlwmi.DisplayName.Replace('SQL Server (', '').Replace(')', '') # Don't clown, I don't know regex :(

                if ([System.String]::IsNullOrEmpty($regroot)) {
                    $regroot = $sqlwmi.AdvancedProperties | Where-Object { $_ -match 'REGROOT' }
                    $vsname = $sqlwmi.AdvancedProperties | Where-Object { $_ -match 'VSNAME' }

                    if (![System.String]::IsNullOrEmpty($regroot)) {
                        $regroot = ($regroot -Split 'Value\=')[1]
                        $vsname = ($vsname -Split 'Value\=')[1]
                    }
                    else {
                        Write-Message -Level Warning -Message "Can't find instance $vsname on $env:COMPUTERNAME"
                        return
                    }
                }

                # vsname takes care of clusters
                if ([System.String]::IsNullOrEmpty($vsname)) {
                    $vsname = $computer
                    if ($instancename -ne "MSSQLSERVER") {
                        $vsname = "$computer\$instancename"
                    }
                }

                Write-Message -Level Verbose -Message "Regroot: $regroot"
                Write-Message -Level Verbose -Message "InstanceName: $instancename"
                Write-Message -Level Verbose -Message "VSNAME: $vsname"

                [pscustomobject]@{
                    ComputerName = $computer.ComputerName
                    InstanceName = $instancename
                    SqlInstance  = $vsname
                    Hive         = "HKLM"
                    Path         = $regroot
                    RegistryRoot = "HKLM:\$regroot"
                }
            }
        }
    }
}
function Get-DbaSqlService {
    <#
        .SYNOPSIS
            Gets the SQL Server related services on a computer.

        .DESCRIPTION
            Gets the SQL Server related services on one or more computers.

        .PARAMETER ComputerName
            The SQL Server (or server in general) that you're connecting to. This command handles named instances.

        .PARAMETER InstanceName
            Only returns services that belong to the specific instances.

        .PARAMETER Credential
            Credential object used to connect to the computer as a different user.

        .PARAMETER Type
            Use -Type to collect only services of the desired SqlServiceType.
            Can be one of the following: "Agent","Browser","Engine","FullText","SSAS","SSIS","SSRS"

        .PARAMETER ServiceName
            Can be used to specify service names explicitly, without looking for service types/instances.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Service, SqlServer, Instance, Connect
            Author: Klaas Vandenberghe ( @PowerDBAKlaas )

            Requires Local Admin rights on destination computer(s).

            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaSqlService

        .EXAMPLE
            Get-DbaSqlService -ComputerName sqlserver2014a

            Gets the SQL Server related services on computer sqlserver2014a.

        .EXAMPLE
            'sql1','sql2','sql3' | Get-DbaSqlService

            Gets the SQL Server related services on computers sql1, sql2 and sql3.

        .EXAMPLE
            Get-DbaSqlService -ComputerName sql1,sql2 | Out-GridView

            Gets the SQL Server related services on computers sql1 and sql2, and shows them in a grid view.

        .EXAMPLE
            Get-DbaSqlService -ComputerName $MyServers -Type SSRS

            Gets the SQL Server related services of type "SSRS" (Reporting Services) on computers in the variable MyServers.

        .EXAMPLE
            $services = Get-DbaSqlService -ComputerName sql1 -Type Agent,Engine
            $services.ChangeStartMode('Manual')

            Gets the SQL Server related services of types Sql Agent and DB Engine on computer sql1 and changes their startup mode to 'Manual'.

        .EXAMPLE
            (Get-DbaSqlService sql1 -Type Engine).Restart($true)

            Calls a Restart method for each Engine service on computer sql1 with -Force option.
    #>
    [CmdletBinding(DefaultParameterSetName = "Search")]
    Param (
        [parameter(ValueFromPipeline = $true, Position = 1)]
        [Alias("cn", "host", "Server")]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [Parameter(ParameterSetName = "Search")]
        [Alias("Instance")]
        [string[]]$InstanceName,
        [PSCredential]$Credential,
        [Parameter(ParameterSetName = "Search")]
        [ValidateSet("Agent", "Browser", "Engine", "FullText", "SSAS", "SSIS", "SSRS")]
        [string[]]$Type,
        [Parameter(ParameterSetName = "ServiceName")]
        [string[]]$ServiceName,
        [Alias('Silent')]
        [switch]$EnableException
    )

    BEGIN {
        #Dictionary to transform service type IDs into the names from Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer.Services.Type
        $ServiceIdMap = @(
            @{ Name = "Engine"; Id = 1 },
            @{ Name = "Agent"; Id = 2 },
            @{ Name = "FullText"; Id = 3, 9 },
            @{ Name = "SSIS"; Id = 4 },
            @{ Name = "SSAS"; Id = 5 },
            @{ Name = "SSRS"; Id = 6 },
            @{ Name = "Browser"; Id = 7 },
            @{ Name = "Unknown"; Id = 8 }
        )
        if ($PsCmdlet.ParameterSetName -match 'Search') {
            if ($Type) {
                $searchClause = ""
                foreach ($itemType in $Type) {
                    foreach ($id in ($ServiceIdMap | Where-Object { $_.Name -eq $itemType }).Id) {
                        if ($searchClause) { $searchClause += ' OR ' }
                        $searchClause += "SQLServiceType = $id"
                    }
                }
            }
            else {
                $searchClause = "SQLServiceType > 0"
            }
        }
        elseif ($PsCmdlet.ParameterSetName -match 'ServiceName') {
            if ($ServiceName) {
                $searchClause = ""
                foreach ($sn in $ServiceName) {
                    if ($searchClause) { $searchClause += ' OR ' }
                    $searchClause += "ServiceName = '$sn'"
                }
            }
            else {
                $searchClause = "SQLServiceType > 0"
            }
        }
    }
    PROCESS {
        foreach ($Computer in $ComputerName.ComputerName) {
            $Server = Resolve-DbaNetworkName -ComputerName $Computer -Credential $credential
            if ($Server.FullComputerName) {
                $Computer = $server.FullComputerName
                Write-Message -Level VeryVerbose -Message "Getting SQL Server namespace on $Computer" -Target $Computer
                try { $namespaces = Get-DbaCmObject -ComputerName $Computer -NameSpace root\Microsoft\SQLServer -Query "Select Name FROM __NAMESPACE WHERE Name Like 'ComputerManagement%'" -EnableException -Credential $credential | Sort-Object Name -Descending }
                catch { }
                if ($namespaces) {
                    $servicesTemp = @()

                    ForEach ($namespace in $namespaces) {
                        try {
                            Write-Message -Level Verbose -Message "Getting Cim class SqlService in Namespace $($namespace.Name) on $Computer." -Target $Computer
                            foreach ($service in (Get-DbaCmObject -ComputerName $Computer -Namespace "root\Microsoft\SQLServer\$($namespace.Name)" -Query "SELECT * FROM SqlService WHERE $searchClause" -EnableException -Credential $credential)) {
                                $servicesTemp += New-Object PSObject -Property @{
                                    Name      = $service.ServiceName
                                    Namespace = $namespace.Name
                                    Service   = $service
                                }
                            }
                        }
                        catch {
                            Write-Message -Level Verbose -EnableException $EnableException.ToBool() -Message "Failed to acquire services from namespace $($namespace.Name)." -Target $Computer -ErrorRecord $_
                        }
                    }

                    $services = ($servicesTemp | Group-Object Name | ForEach-Object { $_.Group | Sort-Object Namespace -Descending | Select-Object -First 1 }).Service

                    if ($services) {
                        Write-Message -Level Verbose -Message "Creating output objects"
                        ForEach ($service in $services) {
                            Add-Member -Force -InputObject $service -MemberType NoteProperty -Name ComputerName -Value $service.HostName
                            Add-Member -Force -InputObject $service -MemberType NoteProperty -Name ServiceType -Value ($ServiceIdMap | Where-Object { $_.Id -contains $service.SQLServiceType }).Name
                            Add-Member -Force -InputObject $service -MemberType NoteProperty -Name State -Value $(switch ($service.State) { 1 { 'Stopped' } 2 { 'Start Pending' }  3 { 'Stop Pending' } 4 { 'Running' } })
                            Add-Member -Force -InputObject $service -MemberType NoteProperty -Name StartMode -Value $(switch ($service.StartMode) { 1 { 'Unknown' } 2 { 'Automatic' }  3 { 'Manual' } 4 { 'Disabled' } })

                            if ($service.ServiceName -in ("MSSQLSERVER", "SQLSERVERAGENT", "ReportServer", "MSSQLServerOLAPService")) {
                                $instance = "MSSQLSERVER"
                            }
                            else {
                                if ($service.ServiceType -in @("Agent", "Engine", "SSRS", "SSAS")) {
                                    if ($service.ServiceName.indexof('$') -ge 0) {
                                        $instance = $service.ServiceName.split('$')[1]
                                    }
                                    else {
                                        $instance = "Unknown"
                                    }
                                }
                                else {
                                    $instance = ""
                                }
                            }
                            $priority = switch ($service.ServiceType) {
                                "Engine" { 200 }
                                default { 100 }
                            }
                            #If only specific instances are selected
                            if (!$InstanceName -or $instance -in $InstanceName) {
                                #Add other properties and methods
                                Add-Member -Force -InputObject $service -NotePropertyName InstanceName -NotePropertyValue $instance
                                Add-Member -Force -InputObject $service -NotePropertyName ServicePriority -NotePropertyValue $priority
                                Add-Member -Force -InputObject $service -MemberType ScriptMethod -Name "Stop" -Value {
                                    Param ([bool]$Force = $false)
                                    Stop-DbaSqlService -InputObject $this -Force:$Force
                                }
                                Add-Member -Force -InputObject $service -MemberType ScriptMethod -Name "Start" -Value { Start-DbaSqlService -InputObject $this }
                                Add-Member -Force -InputObject $service -MemberType ScriptMethod -Name "Restart" -Value {
                                    Param ([bool]$Force = $false)
                                    Restart-DbaSqlService -InputObject $this -Force:$Force
                                }
                                Add-Member -Force -InputObject $service -MemberType ScriptMethod -Name "ChangeStartMode" -Value {
                                    Param (
                                        [parameter(Mandatory = $true)]
                                        [string]$Mode
                                    )
                                    $supportedModes = @("Automatic", "Manual", "Disabled")
                                    if ($Mode -notin $supportedModes) {
                                        Stop-Function -Message ("Incorrect mode '$Mode'. Use one of the following values: {0}" -f ($supportedModes -join ' | ')) -EnableException $false -FunctionName 'Get-DbaSqlService'
                                        Return
                                    }
                                    Set-ServiceStartMode -InputObject $this -Mode $Mode -ErrorAction Stop
                                    $this.StartMode = $Mode
                                }
                                Select-DefaultView -InputObject $service -Property ComputerName, ServiceName, ServiceType, InstanceName, DisplayName, StartName, State, StartMode -TypeName DbaSqlService
                            }
                        }
                    }
                    else {
                        Stop-Function -EnableException $EnableException -Message "No Sql Services found on $Computer" -Continue
                    }
                }
                else {
                    Stop-Function -EnableException $EnableException -Message "No ComputerManagement Namespace on $Computer. Please note that this function is available from SQL 2005 up." -Continue
                }
            }
            else {
                Stop-Function -EnableException $EnableException -Message "Failed to connect to $Computer" -Continue
            }
        }
    }
}
function Get-DbaSsisEnvironmentVariable {
    <#
        .SYNOPSIS
            This command gets specified SSIS Environment and all its variables

        .DESCRIPTION
            This command gets all variables from specified environment from SSIS Catalog. All sensitive values are decrypted.
            The function communicates directly with SSISDB database, "SQL Server Integration Services" service isn't queried there.
            Each parameter (besides SqlInstance and SqlCredential) acts as the filter to only include or exclude particular element

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to.
            This can be a collection and receive pipeline input to allow the function
            to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Environment
            The SSIS Environments names that we want to get variables from

        .PARAMETER EnvironmentExclude
            The SSIS Environments to exclude. Acts as a filter for environments, best used without 'Environment' parameter
            to get variables for all environments but excluded ones

        .PARAMETER Folder
            The Folders names that contain the environments

        .PARAMETER FolderExclude
            The Folders names to exclude. Acts as a filter for folders containing environments, best user without 'Folder' parameter
            to get variables for all folders but excluded ones

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: SSIS, SSISDB, Variable
            Author: Bartosz Ratajczyk ( @b_ratajczyk )

            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaSsisEnvironmentVariable

        .EXAMPLE
            Get-DbaSsisEnvironmentVariable -SqlInstance localhost -Environment DEV -Folder DWH_ETL

            Gets variables of 'DEV' environment located in 'DWH_ETL' folder on 'localhost' Server

        .EXAMPLE
            Get-DbaSsisEnvironmentVariable -SqlInstance localhost -Environment DEV -Folder DWH_ETL, DEV2, QA

            Gets variables of 'DEV' environment(s) located in folders 'DWH_ETL', 'DEV2' and 'QA' on 'localhost' server

        .EXAMPLE
            Get-DbaSsisEnvironmentVariable -SqlInstance localhost -Environment DEV -FolderExclude DWH_ETL, DEV2, QA

            Gets variables of 'DEV' environments located in folders other than 'DWH_ETL', 'DEV2' and 'QA' on 'localhost' server

        .EXAMPLE
            Get-DbaSsisEnvironmentVariable -SqlInstance localhost -Environment DEV, PROD -Folder DWH_ETL, DEV2, QA

            Gets variables of 'DEV' and 'PROD' environment(s) located in folders 'DWH_ETL', 'DEV2' and 'QA' on 'localhost' server

        .EXAMPLE
            Get-DbaSsisEnvironmentVariable -SqlInstance localhost -EnvironmentExclude DEV, PROD -Folder DWH_ETL, DEV2, QA

            Gets variables of environments other than 'DEV' and 'PROD' located in folders 'DWH_ETL', 'DEV2' and 'QA' on 'localhost' server

        .EXAMPLE
            Get-DbaSsisEnvironmentVariable -SqlInstance localhost -EnvironmentExclude DEV, PROD -FolderExclude DWH_ETL, DEV2, QA

            Gets variables of environments other than 'DEV' and 'PROD' located in folders other than 'DWH_ETL', 'DEV2' and 'QA' on 'localhost' server

        .EXAMPLE
            'localhost' | Get-DbaSsisEnvironmentVariable -EnvironmentExclude DEV, PROD

            Gets all SSIS environments except 'DEV' and 'PROD' from 'localhost' server. The server name comes from pipeline

        .EXAMPLE
            'SRV1', 'SRV3' | Get-DbaSsisEnvironmentVariable

            Gets all SSIS environments from 'SRV1' and 'SRV3' servers. The server's names come from pipeline

        .EXAMPLE
            'SRV1', 'SRV2' | Get-DbaSsisEnvironmentVariable DEV | Out-GridView

            Gets all variables from 'DEV' Environment(s) on servers 'SRV1' and 'SRV2' and outputs it as the GridView.
            The server names come from the pipeline.

        .EXAMPLE
            'localhost' | Get-DbaSsisEnvironmentVariable -EnvironmentExclude DEV, PROD | Select-Object -Property Name, Value | Where-Object {$_.Name -match '^a'} | Out-GridView

            Gets all variables from Environments other than 'DEV' and 'PROD' on 'localhost' server,
            selects Name and Value properties for variables that names start with letter 'a' and outputs it as the GridView
    #>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias('SqlServer', 'ServerInstance')]
        [DbaInstanceParameter[]]$SqlInstance,
        [Parameter(Mandatory = $false)]
        [PSCredential]$SqlCredential,
        [parameter(Mandatory = $false)]
        [object[]]$Environment,
        [parameter(Mandatory = $false)]
        [object[]]$EnvironmentExclude,
        [parameter(Mandatory = $false)]
        [object[]]$Folder,
        [parameter(Mandatory = $false)]
        [object[]]$FolderExclude,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Message "Connecting to $instance" -Level Verbose
                $server = Connect-SqlInstance -SqlInstance $instance -MinimumVersion 11
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            try {
                $ISNamespace = "Microsoft.SqlServer.Management.IntegrationServices"

                Write-Message -Message "Connecting to SSIS Catalog on $instance" -Level Verbose
                $SSIS = New-Object "$ISNamespace.IntegrationServices" $server
            }
            catch {
                Stop-Function -Message "Could not connect to SSIS Catalog on $instance or current SMO library does not support SSIS catalog"
                return
            }

            Write-Message -Message "Fetching SSIS Catalog and its folders" -Level Verbose
            $catalog = $SSIS.Catalogs | Where-Object { $_.Name -eq "SSISDB" }

            # get all folders names if none provided
            if ($null -eq $Folder) {
                $searchFolders = $catalog.Folders.Name
            }
            else {
                $searchFolders = $Folder
            }

            # filter unwanted folders
            if ($FolderExclude) {
                $searchFolders = $searchFolders | Where-Object { $_ -notin $FolderExclude }
            }

            if ($null -eq $searchFolders) {
                Write-Message -Message "Instance: $instance > -Folder and -FolderExclude filters return an empty collection. Skipping" -Level Warning
            }
            else {
                foreach ($f in $searchFolders) {
                    # get all environments names if none provided
                    if ($null -eq $Environment) {
                        $searchEnvironments = $catalog.Folders.Environments.Name
                    }
                    else {
                        $searchEnvironments = $Environment
                    }

                    #filter unwanted environments
                    if ($EnvironmentExclude) {
                        $searchEnvironments = $searchEnvironments | Where-Object { $_ -notin $EnvironmentExclude }
                    }

                    if ($null -eq $searchEnvironments) {
                        Write-Message -Message "Instance: $instance / Folder: $f > -Environment and -EnvironmentExclude filters return an empty collection. Skipping." -Level Warning
                    }
                    else {
                        $Environments = $catalog.Folders[$f].Environments | Where-Object { $_.Name -in $searchEnvironments }

                        foreach ($e in $Environments) {
                            #encryption handling
                            $encKey = 'MS_Enckey_Env_' + $e.EnvironmentId
                            $encCert = 'MS_Cert_Env_' + $e.EnvironmentId

                            <#
                            SMO does not return sensitive values (gets data from catalog.environment_variables)
                            We have to manually query internal.environment_variables instead and use symmetric keys
                            within T-SQL code
                            #>

                            $sql = @"
                            OPEN SYMMETRIC KEY $encKey DECRYPTION BY CERTIFICATE $encCert;

                            SELECT
                                ev.variable_id,
                                ev.name,
                                ev.description,
                                ev.type,
                                ev.sensitive,
                                value = ev.value,
                                ev.sensitive_value,
                                ev.base_data_type,
                                decrypted = decrypted.value
                            FROM internal.environment_variables ev

                                CROSS APPLY (
                                    SELECT
                                        value   = CASE base_data_type
                                                    WHEN 'nvarchar' THEN CONVERT(NVARCHAR(MAX), DECRYPTBYKEY(sensitive_value))
                                                    WHEN 'bit' THEN CONVERT(NVARCHAR(MAX), CONVERT(bit, DECRYPTBYKEY(sensitive_value)))
                                                    WHEN 'datetime' THEN CONVERT(NVARCHAR(MAX), CONVERT(datetime2(0), DECRYPTBYKEY(sensitive_value)))
                                                    WHEN 'single' THEN CONVERT(NVARCHAR(MAX), CONVERT(DECIMAL(38, 18), DECRYPTBYKEY(sensitive_value)))
                                                    WHEN 'float' THEN CONVERT(NVARCHAR(MAX), CONVERT(DECIMAL(38, 18), DECRYPTBYKEY(sensitive_value)))
                                                    WHEN 'decimal' THEN CONVERT(NVARCHAR(MAX), CONVERT(DECIMAL(38, 18), DECRYPTBYKEY(sensitive_value)))
                                                    WHEN 'tinyint' THEN CONVERT(NVARCHAR(MAX), CONVERT(tinyint, DECRYPTBYKEY(sensitive_value)))
                                                    WHEN 'smallint' THEN CONVERT(NVARCHAR(MAX), CONVERT(smallint, DECRYPTBYKEY(sensitive_value)))
                                                    WHEN 'int' THEN CONVERT(NVARCHAR(MAX), CONVERT(INT, DECRYPTBYKEY(sensitive_value)))
                                                    WHEN 'bigint' THEN CONVERT(NVARCHAR(MAX), CONVERT(bigint, DECRYPTBYKEY(sensitive_value)))
                                                END
                                ) decrypted
                            WHERE environment_id = $($e.EnvironmentId);
                            CLOSE SYMMETRIC KEY $encKey;
"@

                            $ssisVariables = $server.Query($sql, "SSISDB")

                            foreach ($variable in $ssisVariables) {
                                if ($variable.sensitive -eq $true) {
                                    $value = $variable.decrypted
                                }
                                else {
                                    $value = $variable.value
                                }

                                [PSCustomObject]@{
                                    ComputerName = $server.ComputerName
                                    InstanceName = $server.ServiceName
                                    SqlInstance  = $server.DomainInstanceName
                                    Folder       = $f
                                    Environment  = $e.Name
                                    Id           = $variable.variable_id
                                    Name         = $variable.Name
                                    Description  = $variable.description
                                    Type         = $variable.type
                                    IsSensitive  = $variable.sensitive
                                    BaseDataType = $variable.base_data_type
                                    Value        = $value
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
#ValidationTags#Messaging#
function Get-DbaSsisExecutionHistory {
    <#
        .SYNOPSIS
           Get-DbaSsisHistory Retreives SSIS project and package execution History, and environments from one SQL Server to another.

        .DESCRIPTION
            This command gets execution history for SSIS executison given one or more instances and can be filtered by Project, Environment,Folder or Status.
        
        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to.
            This can be a collection and receive pipeline input to allow the function
            to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Project
            Specifies a filter by project

        .PARAMETER Folder
            Specifies a filter by folder
        
        .PARAMETER Environment
            Specifies a filter by environment

        .PARAMETER Status
            Specifies a filter by status (created,running,cancelled,failed,pending,halted,succeeded,stopping,completed)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration, SSIS
            Author: Chris Tucker (ChrisTucker, @ChrisTuc47368095)

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaSsisExecutionHistory

        .EXAMPLE
            Get-DbaSsisExecutionHistory -SqlInstance SMTQ01 -Folder SMTQ_PRC

            Get all history items for SMTQ01 in folder SMTQ_PRC.

        .EXAMPLE
            Get-DbaSsisExecutionHistory -SqlInstance SMTQ01 -Status Failed,Cancelled
            
            Gets all failed or canceled executions for SMTQ01.

        .EXAMPLE
            Get-DbaSsisExecutionHistory -SqlInstance SMTQ01,SMTQ02 -Status Failed,Cancelled -Whatif

            Shows what would happen if the command were executed and would return the SQL statement that would be executed per instance.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [ValidateSet("Created", "Running", "Cancelled", "Failed", "Pending", "Halted", "Succeeded", "Stopping", "Completed")]
        [String[]]$Status,
        [String[]]$Project,
        [String[]]$Folder,
        [String[]]$Environment,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        $statuses = @{
            'Created'   = 1
            'Running'   = 2
            'Cancelled' = 3
            'Failed'    = 4
            'Pending'   = 5
            'Halted'    = 6
            'Succeeded' = 7
            'Stopping'  = 8
            'Completed' = 9
        }
        if ($Status) {
            $csv = ($statuses[$Status] -join ',')
            $statusq = "AND e.[Status] in ($csv)"
        }
        else {
            $statusq = ''
        }
        
        if ($Project) {
            $csv = "`"" + ($Project -join '","') + "`""
            $projectq = "AND e.[ProjectName] in ($csv)"
        }
        else {
            $projectq = ''
        }
        
        if ($Folder) {
            $csv = "`'" + ($Folder -join "'", "'") + "`'"
            $folderq = "AND e.[FolderName] in ($csv)"
        }
        else {
            $folderq = ''
        }
        
        if ($Environment) {
            $csv = "`'" + ($Environment -join "'", "'") + "`'"
            $environmentq = "AND e.[Environment] in ($csv)"
        }
        else {
            $environmentq = ''
        }
        
        $sql = "
            WITH
            cteLoglevel as (
                SELECT
                    execution_id as ExecutionID,
                    cast(parameter_value AS INT) AS LoggingLevel
                FROM
                    [catalog].[execution_parameter_values]
                WHERE
                    parameter_name = 'LOGGING_LEVEL'
            )
            , cteStatus AS (
                SELECT
                     [key]
                    ,[code]
                FROM (
                    VALUES
                          ( 1,'Created'  )
                        , ( 2,'Running'  )
                        , ( 3,'Cancelled')
                        , ( 4,'Failed'   )
                        , ( 5,'Pending'  )
                        , ( 6,'Halted'   )
                        , ( 7,'Succeeded')
                        , ( 8,'Stopping' )
                        , ( 9,'Completed')
                ) codes([key],[code])
            )
            SELECT
                      e.execution_id as ExecutionID
                    , e.folder_name as FolderName
                    , e.project_name as ProjectName
                    , e.package_name as PackageName
                    , e.project_lsn as ProjectLsn
                    , Environment = isnull(e.environment_folder_name, '') + isnull('\' + e.environment_name,  '')
                    , s.code AS StatusCode
                    , start_time as StartTime
                    , end_time as EndTime
                    , ElapsedMinutes = DATEDIFF(ss, e.start_time, e.end_time)
                    , l.LoggingLevel
            FROM
                [catalog].executions e
                LEFT OUTER JOIN cteLoglevel l
                    ON e.execution_id = l.ExecutionID
                LEFT OUTER JOIN cteStatus s
                    ON s.[key] = e.status
            WHERE 1=1
                $statusq
                $projectq
                $folderq
                $environmentq
                OPTION  ( RECOMPILE );"
    }
    process {
        foreach ($instance in $SqlInstance) {
            $results = Invoke-DbaSqlQuery -SqlInstance $instance -Database SSISDB -Query $sql -as PSObject -SqlCredential $SqlCredential
            foreach ($row in $results) {
                $row.start_time = [dbadatetime]$row.StartTime.DateTime
                $row.end_time = [dbadatetime]$row.EndTime.DateTime
                $row
            }
        }
    }
}
function Get-DbaStartupParameter {
    <#
    .SYNOPSIS
        Displays values for a detailed list of SQL Server Startup Parameters.

    .DESCRIPTION
        Displays values for a detailed list of SQL Server Startup Parameters including Master Data Path, Master Log path, Error Log, Trace Flags, Parameter String and much more.

        This command relies on remote Windows Server (SQL WMI/WinRm) access. You can pass alternative Windows credentials by using the -Credential parameter.

        See https://msdn.microsoft.com/en-us/library/ms190737.aspx for more information.

    .PARAMETER SqlInstance
        The SQL Server instance to connect to.

    .PARAMETER Credential
        Allows you to login to servers using alternate Windows credentials.

        $scred = Get-Credential, then pass $scred object to the -Credential parameter.

    .PARAMETER Simple
        If this switch is enabled, simplified output will be produced including only Server, Master Data Path, Master Log path, ErrorLog, TraceFlags and ParameterString.

    .PARAMETER EnableException
        If this switch is enabled, exceptions will be thrown to the caller, which will need to perform its own exception processing. Otherwise, the function will try to catch the exception, interpret it and provide a friendly error message.

    .EXAMPLE
        Get-DbaStartupParameter -SqlInstance sql2014

        Logs into SQL WMI as the current user then displays the values for numerous startup parameters.

    .EXAMPLE
        $wincred = Get-Credential ad\sqladmin
        Get-DbaStartupParameter -SqlInstance sql2014 -Credential $wincred -Simple

        Logs in to WMI using the ad\sqladmin credential and gathers simplified information about the SQL Server Startup Parameters.

    .NOTES
        Tags: WSMan, SQLWMI, Memory
        dbatools PowerShell module (https://dbatools.io)
        Copyright (C) 2016 Chrissy LeMaire
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Get-DbaStartupParameter
#>
    [CmdletBinding()]
    param ([parameter(ValueFromPipeline, Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("SqlCredential")]
        [PSCredential]$Credential,
        [switch]$Simple,
        [switch]
        [Alias('Silent')]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                $computerName = $instance.ComputerName
                $instanceName = $instance.InstanceName
                $ogInstance = $instance.FullSmoName

                $computerName = (Resolve-DbaNetworkName -ComputerName $computerName).FullComputerName

                Write-Message -Level Verbose -message "Connecting to $computerName"

                if ($instanceName.Length -eq 0) { $instanceName = "MSSQLSERVER" }

                $displayname = "SQL Server ($instanceName)"

                $Scriptblock = {
                    $computerName = $args[0]
                    $displayname = $args[1]

                    $wmisvc = $wmi.Services | Where-Object DisplayName -eq $displayname

                    $params = $wmisvc.StartupParameters -split ';'

                    $masterdata = $params | Where-Object { $_.StartsWith('-d') }
                    $masterlog = $params | Where-Object { $_.StartsWith('-l') }
                    $errorlog = $params | Where-Object { $_.StartsWith('-e') }
                    $traceflags = $params | Where-Object { $_.StartsWith('-T') }

                    $debugflag = $params | Where-Object { $_.StartsWith('-t') }

                    if ($debugflag.length -ne 0) {
                        Write-Message -Level Warning "$instance is using the lowercase -t trace flag. This is for internal debugging only. Please ensure this was intentional."
                    }
                    #>

                    if ($traceflags.length -eq 0) {
                        $traceflags = "None"
                    }
                    else {
                        $traceflags = $traceflags.substring(2)
                    }

                    if ($Simple -eq $true) {
                        [PSCustomObject]@{
                            ComputerName    = $computerName
                            InstanceName    = $instanceName
                            SqlInstance     = $ogInstance
                            MasterData      = $masterdata.TrimStart('-d')
                            MasterLog       = $masterlog.TrimStart('-l')
                            ErrorLog        = $errorlog.TrimStart('-e')
                            TraceFlags      = $traceflags -join ','
                            ParameterString = $wmisvc.StartupParameters
                        }
                    }
                    else {
                        # From https://msdn.microsoft.com/en-us/library/ms190737.aspx

                        $commandpromptparm = $params | Where-Object { $_ -eq '-c' }
                        $minimalstartparm = $params | Where-Object { $_ -eq '-f' }
                        $memorytoreserve = $params | Where-Object { $_.StartsWith('-g') }
                        $noeventlogsparm = $params | Where-Object { $_ -eq '-n' }
                        $instancestartparm = $params | Where-Object { $_ -eq '-s' }
                        $disablemonitoringparm = $params | Where-Object { $_ -eq '-x' }
                        $increasedextentsparm = $params | Where-Object { $_ -ceq '-E' }

                        $minimalstart = $noeventlogs = $instancestart = $disablemonitoring = $false
                        $increasedextents = $commandprompt = $singleuser = $false

                        if ($null -ne $commandpromptparm) {
                            $commandprompt = $true
                        }
                        if ($null -ne $minimalstartparm) {
                            $minimalstart = $true
                        }
                        if ($null -eq $memorytoreserve) {
                            $memorytoreserve = 0
                        }
                        if ($null -ne $noeventlogsparm) {
                            $noeventlogs = $true
                        }
                        if ($null -ne $instancestartparm) {
                            $instancestart = $true
                        }
                        if ($null -ne $disablemonitoringparm) {
                            $disablemonitoring = $true
                        }
                        if ($null -ne $increasedextentsparm) {
                            $increasedextents = $true
                        }

                        $singleuserparm = $params | Where-Object { $_.StartsWith('-m') }

                        if ($singleuserparm.length -ne 0) {
                            $singleuser = $true
                            $singleuserdetails = $singleuserparm.TrimStart('-m')
                        }

                        [PSCustomObject]@{
                            ComputerName         = $computerName
                            InstanceName         = $instanceName
                            SqlInstance          = $ogInstance
                            MasterData           = $masterdata -replace '^-[dD]', ''
                            MasterLog            = $masterlog  -replace '^-[lL]', ''
                            ErrorLog             = $errorlog   -replace '^-[eE]', ''
                            TraceFlags           = $traceflags -join ','
                            CommandPromptStart   = $commandprompt
                            MinimalStart         = $minimalstart
                            MemoryToReserve      = $memorytoreserve
                            SingleUser           = $singleuser
                            SingleUserName       = $singleuserdetails
                            NoLoggingToWinEvents = $noeventlogs
                            StartAsNamedInstance = $instancestart
                            DisableMonitoring    = $disablemonitoring
                            IncreasedExtents     = $increasedextents
                            ParameterString      = $wmisvc.StartupParameters
                        }
                    }
                }

                # This command is in the internal function
                # It's sorta like Invoke-Command.
                if ($credential) {
                    Invoke-ManagedComputerCommand -Server $computerName -Credential $credential -ScriptBlock $Scriptblock -ArgumentList $computerName, $displayname
                }
                else {
                    Invoke-ManagedComputerCommand -Server $computerName -ScriptBlock $Scriptblock -ArgumentList $computerName, $displayname
                }
            }
            catch {
                Stop-Function -Message "$instance failed." -ErrorRecord $_ -Continue -Target $instance
            }
        }
    }
}
function Get-DbaSuspectPage {
    <#
        .SYNOPSIS
        Returns data that is stored in SQL for Suspect Pages on the specified SQL Server Instance

        .DESCRIPTION
        This function returns any records that were stored due to suspect pages in databases on a SQL Server Instance.

        .PARAMETER SqlInstance
        A SQL Server instance to connect to

        .PARAMETER SqlCredential
        A credential to use to connect to the SQL Instance rather than using Windows Authentication

        .PARAMETER Database
        The database to return. If unspecified, all records will be returned.

        .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
        Tags: Pages, DBCC
        Author: Garry Bargsley (@gbargsley), http://blog.garrybargsley.com

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
        Get-DbaSuspectPage -SqlInstance sql2016

        Retrieve any records stored for Suspect Pages on the sql2016 SQL Server.

        .EXAMPLE
        Get-DbaSuspectPage -SqlInstance sql2016 -Database Test

        Retrieve any records stored for Suspect Pages on the sql2016 SQL Server and the Test database only.

#>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [object]$Database,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {

        foreach ($instance in $sqlinstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                return
            }

            $sql = "Select
            DB_NAME(database_id) as DBName,
            file_id,
            page_id,
            CASE event_type
            WHEN 1 THEN '823 or 824 or Torn Page'
            WHEN 2 THEN 'Bad Checksum'
            WHEN 3 THEN 'Torn Page'
            WHEN 4 THEN 'Restored'
            WHEN 5 THEN 'Repaired (DBCC)'
            WHEN 7 THEN 'Deallocated (DBCC)'
            END as EventType,
            error_count,
            last_update_date
            from msdb.dbo.suspect_pages"

            try {
                $results = $server.Query($sql)
            }
            catch {
                Stop-Function -Message "Issue collecting data on $server" -Target $server -ErrorRecord $_ -Continue
            }

            if ($Database) {
                $results = $results | Where-Object DBName -EQ $Database
            }

        }
        foreach ($row in $results) {
            [PSCustomObject]@{
                ComputerName   = $server.ComputerName
                InstanceName   = $server.ServiceName
                SqlInstance    = $server.DomainInstanceName
                Database       = $row.DBName
                FileId         = $row.file_id
                PageId         = $row.page_id
                EventType      = $row.EventType
                ErrorCount     = $row.error_count
                LastUpdateDate = $row.last_update_date
            }
        }
    }
}
function Get-DbaTable {
    <#
.SYNOPSIS
Returns a summary of information on the tables

.DESCRIPTION
Shows table information around table row and data sizes and if it has any table type information.

.PARAMETER SqlInstance
SQL Server name or SMO object representing the SQL Server to connect to. This can be a
collection and receive pipeline input

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Database
The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

.PARAMETER ExcludeDatabase
The database(s) to exclude - this list is auto-populated from the server

.PARAMETER IncludeSystemDBs
Switch parameter that when used will display system database information

.PARAMETER Table
Define a specific table you would like to query. You can specify up to three-part name like db.sch.tbl.
If the object has special characters please wrap them in square brackets [ ].
This dbo.First.Table will try to find table named 'Table' on schema 'First' and database 'dbo'.
The correct way to find table named 'First.Table' on schema 'dbo' is passing dbo.[First.Table]

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Database, Tables
Author: Stephen Bennett, https://sqlnotesfromtheunderground.wordpress.com/

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Get-DbaTable

.EXAMPLE
Get-DbaTable -SqlInstance DEV01 -Database Test1
Return all tables in the Test1 database

.EXAMPLE
Get-DbaTable -SqlInstance DEV01 -Database MyDB -Table MyTable
Return only information on the table MyTable from the database MyDB

.EXAMPLE
Get-DbaTable -SqlInstance DEV01 -Table MyTable
Returns information on table called MyTable if it exists in any database on the server, under any schema

.EXAMPLE
Get-DbaTable -SqlInstance DEV01 -Table dbo.[First.Table]
Returns information on table called First.Table on schema dbo if it exists in any database on the server

.EXAMPLE
'localhost','localhost\namedinstance' | Get-DbaTable -Database DBA -Table Commandlog
Returns information on the CommandLog table in the DBA database on both instances localhost and the named instance localhost\namedinstance

#>
    [CmdletBinding()]
    param ([parameter(ValueFromPipeline, Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$IncludeSystemDBs,
        [string[]]$Table,
        [switch][Alias('Silent')]
        $EnableException
    )

    begin {
        if ($Table) {
            $fqtns = @()
            foreach ($t in $Table) {
                $splitName = [regex]::Matches($t, "(\[.+?\])|([^\.]+)").Value
                $dotcount = $splitName.Count

                $splitDb = $Schema = $null

                switch ($dotcount) {
                    1 {
                        $tbl = $t
                    }
                    2 {
                        $schema = $splitName[0]
                        $tbl = $splitName[1]
                    }
                    3 {
                        $splitDb = $splitName[0]
                        $schema = $splitName[1]
                        $tbl = $splitName[2]
                    }
                    default {
                        Write-Message -Level Warning -Message "Please make sure that you are using up to three-part names. If your search value contains '.' character you must use [ ] to wrap the name. The value $t is not a valid name."
                        Continue
                    }
                }

                if ($splitDb -like "[[]*[]]") {
                    $splitDb = $splitDb.Substring(1, ($splitDb.Length - 2))
                }

                if ($schema -like "[[]*[]]") {
                    $schema = $schema.Substring(1, ($schema.Length - 2))
                }

                if ($tbl -like "[[]*[]]") {
                    $tbl = $tbl.Substring(1, ($tbl.Length - 2))
                }

                $fqtns += [PSCustomObject] @{
                    Database = $splitDb
                    Schema   = $Schema
                    Table    = $tbl
                }
            }
        }
    }

    process {
        foreach ($instance in $sqlinstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            try {
                #only look at online databases (Status equal normal)
                $dbs = $server.Databases | Where-Object IsAccessible

                #If IncludeSystemDBs is false, exclude systemdbs
                if (!$IncludeSystemDBs -and !$Database) {
                    $dbs = $dbs | Where-Object { !$_.IsSystemObject }
                }

                if ($Database) {
                    $dbs = $dbs | Where-Object { $Database -contains $_.Name }
                }

                if ($ExcludeDatabase) {
                    $dbs = $dbs | Where-Object { $ExcludeDatabase -notcontains $_.Name }
                }
            }
            catch {
                Stop-Function -Message "Unable to gather dbs for $instance" -Target $instance -Continue -ErrorRecord $_
            }

            foreach ($db in $dbs) {
                Write-Message -Level Verbose -Message "Processing $db"

                if ($fqtns) {
                    $tables = @()
                    foreach ($fqtn in $fqtns) {
                        # If the user specified a database in a three-part name, and it's not the
                        # database currently being processed, skip this table.
                        if ($fqtn.Database) {
                            if ($fqtn.Database -ne $db.Name) {
                                continue
                            }
                        }

                        $tbl = $db.tables | Where-Object { $_.Name -in $fqtn.Table -and $fqtn.Schema -in ($_.Schema, $null) -and $fqtn.Database -in ($_.Parent.Name, $null) }

                        if (-not $tbl) {
                            Write-Message -Level Verbose -Message "Could not find table $($fqtn.Table) in $db on $server"
                        }
                        $tables += $tbl
                    }
                }
                else {
                    $tables = $db.Tables
                }

                foreach ($sqltable in $tables) {
                    $sqltable | Add-Member -Force -MemberType NoteProperty -Name ComputerName -Value $server.ComputerName
                    $sqltable | Add-Member -Force -MemberType NoteProperty -Name InstanceName -Value $server.ServiceName
                    $sqltable | Add-Member -Force -MemberType NoteProperty -Name SqlInstance -Value $server.DomainInstanceName
                    $sqltable | Add-Member -Force -MemberType NoteProperty -Name Database -Value $db.Name

                    $defaultprops = "ComputerName", "InstanceName", "SqlInstance", "Database", "Schema", "Name", "IndexSpaceUsed", "DataSpaceUsed", "RowCount", "HasClusteredIndex", "IsFileTable", "IsMemoryOptimized", "IsPartitioned", "FullTextIndex", "ChangeTrackingEnabled"

                    Select-DefaultView -InputObject $sqltable -Property $defaultprops
                }
            }
        }
    }
}
function Get-DbaTcpPort {
    <#
        .SYNOPSIS
            Returns the TCP port used by the specified SQL Server.

        .DESCRIPTION
            By default, this function returns just the TCP port used by the specified SQL Server.

            If -Detailed is specified, the server name, IPAddress (ipv4 and ipv6), port number and an indicator of whether or not the port assignment is static are returned.

            Remote sqlwmi is used by default. If this doesn't work, then remoting is used. If neither work, it defaults to T-SQL which can provide only the port.

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to.

        .PARAMETER SqlCredential
            Allows you to connect to servers using alternate Windows credentials

            $scred = Get-Credential, then pass $scred object to the -SqlCredential parameter.

        .PARAMETER Detailed
            If this switch is enabled, an object with server name, IPAddress (ipv4 and ipv6), port and static ($true/$false) for one or more SQL Servers is returned.

        .PARAMETER ExcludeIpv6
            If this switch is enabled, IPv6 information is excluded from detailed output.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: SQLWMI, tcp

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaTcpPort

        .EXAMPLE
            Get-DbaTcpPort -SqlInstance sqlserver2014a

            Returns just the port number for the default instance on sqlserver2014a.

        .EXAMPLE
            Get-DbaTcpPort -SqlInstance winserver\sqlexpress, sql2016

            Returns an object with server name and port number for the sqlexpress on winserver and the default instance on sql2016.

        .EXAMPLE
            Get-DbaTcpPort -SqlInstance sqlserver2014a, sql2016 -Detailed

            Returns an object with server name, IPAddress (ipv4 and ipv6), port and static ($true/$false) for sqlserver2014a and sql2016.

            Remote sqlwmi is used by default. If this doesn't work, then remoting is used. If neither work, it defaults to T-SQL which can provide only the port.

        .EXAMPLE
            Get-DbaRegisteredServer -SqlInstance sql2014 | Get-DbaTcpPort -ExcludeIpv6 -Detailed

            Returns an object with server name, IPAddress (just ipv4), port and static ($true/$false) for every server listed in the Central Management Server on sql2014.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [switch]$Detailed,
        [Alias("Ipv4")]
        [switch]$ExcludeIpv6,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            if ($detailed -eq $true) {
                try {
                    $scriptblock = {
                        $instance = $args[0]

                        Add-Type -AssemblyName Microsoft.VisualBasic

                        foreach ($servername in $wmi.ServerInstances) {
                            $instanceName = $servername.Name
                            $wmiinstance = $wmi.Services | Where-Object { $_.DisplayName -eq "SQL Server ($instanceName)" }
                            $vsname = ($wmiinstance.AdvancedProperties | Where-Object { $_ -match 'VSNAME' }).Value

                            if ($vsname.length -eq 0) {
                                $vsname = "$instance\$instanceName"
                            }

                            $vsname = $vsname.Replace("\MSSQLSERVER", "")

                            try {
                                $regroot = ($wmiinstance.AdvancedProperties | Where-Object { $_ -match 'REGROOT' }).Value
                                $dacport = (Get-ItemProperty "HKLM:\$regroot\MSSQLServer\SuperSocketNetLib\AdminConnection\Tcp").TcpDynamicPorts

                                [PsCustomObject]@{
                                    ComputerName = $instance
                                    InstanceName = $instanceName
                                    SqlInstance  = $vsname
                                    IPAddress    = "0.0.0.0"
                                    Port         = $dacport
                                    Static       = $false
                                    Type         = "DAC"
                                }
                            }
                            catch {
                                # it's just not our day
                            }

                            $tcp = $servername.ServerProtocols | Where-Object Name -eq Tcp
                            $ips = $tcp.IPAddresses

                            # This is a remote command so do not use Write-message
                            Write-Verbose "Parsing information for $($ips.count) IP addresses."
                            foreach ($ip in $ips) {
                                $props = $ip.IPAddressProperties | Where-Object { $_.Name -eq "TcpPort" -or $_.Name -eq "TcpDynamicPorts" }

                                foreach ($prop in $props) {
                                    if ([Microsoft.VisualBasic.Information]::IsNumeric($prop.value)) {
                                        $port = $prop.value
                                        if ($prop.name -eq 'TcpPort') {
                                            $static = $true
                                        }
                                        else {
                                            $static = $false
                                        }
                                        break
                                    }
                                }

                                [PsCustomObject]@{
                                    ComputerName = $instance
                                    InstanceName = $instanceName
                                    SqlInstance  = $vsname
                                    IPAddress    = $ip.IPAddress.IPAddressToString
                                    Port         = $port
                                    Static       = $static
                                    Type         = "Normal"
                                }
                            }
                        }
                    }

                    $computer = $instance.ComputerName
                    $resolved = Resolve-DbaNetworkName -ComputerName $instance -Verbose:$false
                    $computername = $resolved.FullComputerName

                    try {
                        Write-Message -Level Verbose -Message "Trying with ComputerName ($computer)."
                        $someIps = Invoke-ManagedComputerCommand -ComputerName $computer -ArgumentList $computer -ScriptBlock $scriptblock
                    }
                    catch {
                        Write-Message -Level Verbose -Message "Trying with FullComputerName because ComputerName failed."
                        $someIps = Invoke-ManagedComputerCommand -ComputerName $computername -ArgumentList $fqdn -ScriptBlock $scriptblock
                    }
                }
                catch {
                    Stop-Function -Message "Could not get detailed information." -Target $instance -ErrorRecord $_
                }

                $cleanedUp = $someIps | Sort-Object IPAddress

                if ($ExcludeIpv6) {
                    $octet = '(?:0?0?[0-9]|0?[1-9][0-9]|1[0-9]{2}|2[0-5][0-5]|2[0-4][0-9])'
                    [regex]$ipv4 = "^(?:$octet\.){3}$octet$"
                    $cleanedUp = $cleanedUp | Where-Object { $_.IPAddress -match $ipv4 }
                }

                $cleanedUp
            }

            if ($Detailed -eq $false -or ($Detailed -eq $true -and $null -eq $someIps)) {
                try {
                    $server = Connect-SqlInstance -SqlInstance "TCP:$instance" -SqlCredential $SqlCredential -MinimumVersion 9
                }
                catch {
                    Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $servername -Continue
                }

                # WmiComputer can be unreliable :( Use T-SQL
                $sql = "SELECT local_tcp_port FROM sys.dm_exec_connections WHERE session_id = @@SPID"
                $port = $server.Query($sql)

                [PSCustomObject]@{
                    ComputerName = $server.ComputerName
                    InstanceName = $server.ServiceName
                    SqlInstance  = $server.DomainInstanceName
                    Port         = $port.local_tcp_port
                }
            }
        }
    }
}
function Get-DbaTempdbUsage {
    <#
        .SYNOPSIS
        Gets Tempdb usage for running queries.

        .DESCRIPTION
        This function queries DMVs for running sessions using Tempdb and returns results if those sessions have user or internal space allocated or deallocated against them.

        .PARAMETER SqlInstance
        The SQL Instance you are querying against.

        .PARAMETER SqlCredential
        If you want to use alternative credentials to connect to the server.

        .PARAMETER WhatIf
        Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
        Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Tempdb, Space
            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaTempdbUsage

        .EXAMPLE
            Get-DbaTempdbUsage -SqlInstance localhost\SQLDEV2K14

            Gets tempdb usage for localhost\SQLDEV2K14
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($server.VersionMajor -le 9) {
                Stop-Function -Message "This function is only supported in SQL Server 2008 or higher." -Continue
            }

            $sql = "SELECT  SERVERPROPERTY('MachineName') AS ComputerName,
        ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
        SERVERPROPERTY('ServerName') AS SqlInstance,
        t.session_id AS Spid,
        r.command AS StatementCommand,
        SUBSTRING(   est.[text],
                     (r.statement_start_offset / 2) + 1,
                     ((CASE r.statement_end_offset
                            WHEN-1
                            THEN DATALENGTH(est.[text])
                            ELSE
                            r.statement_end_offset
                       END - r.statement_start_offset
                      ) / 2
                     ) + 1
                 ) AS QueryText,
        QUOTENAME(DB_NAME(r.database_id)) + N'.' + QUOTENAME(OBJECT_SCHEMA_NAME(est.objectid, est.dbid)) + N'.'
        + QUOTENAME(OBJECT_NAME(est.objectid, est.dbid)) AS ProcedureName,
        r.start_time AS StartTime,
        tdb.UserObjectAllocated * 8 AS CurrentUserAllocatedKB,
        (t.user_objects_alloc_page_count + tdb.UserObjectAllocated) * 8 AS TotalUserAllocatedKB,
        tdb.UserObjectDeallocated * 8 AS UserDeallocatedKB,
        (t.user_objects_dealloc_page_count + tdb.UserObjectDeallocated) * 8 AS TotalUserDeallocatedKB,
        tdb.InternalObjectAllocated * 8 AS InternalAllocatedKB,
        (t.internal_objects_alloc_page_count + tdb.InternalObjectAllocated) * 8 AS TotalInternalAllocatedKB,
        tdb.InternalObjectDeallocated * 8 AS InternalDeallocatedKB,
        (t.internal_objects_dealloc_page_count + tdb.InternalObjectDeallocated) * 8 AS TotalInternalDeallocatedKB,
        r.reads AS RequestedReads,
        r.writes AS RequestedWrites,
        r.logical_reads AS RequestedLogicalReads,
        r.cpu_time AS RequestedCPUTime,
        s.is_user_process AS IsUserProcess,
        s.[status] AS [Status],
        DB_NAME(r.database_id) AS [Database],
        s.login_name AS LoginName,
        s.original_login_name AS OriginalLoginName,
        s.nt_domain AS NTDomain,
        s.nt_user_name AS NTUserName,
        s.[host_name] AS HostName,
        s.[program_name] AS ProgramName,
        s.login_time AS LoginTime,
        s.last_request_start_time AS LastRequestedStartTime,
        s.last_request_end_time AS LastRequestedEndTime
FROM    sys.dm_db_session_space_usage AS t
INNER JOIN sys.dm_exec_sessions AS s
    ON s.session_id = t.session_id
LEFT JOIN sys.dm_exec_requests AS r
    ON r.session_id = s.session_id
LEFT JOIN
          (   SELECT    _tsu.session_id,
                        _tsu.request_id,
                        SUM(_tsu.user_objects_alloc_page_count)       AS UserObjectAllocated,
                        SUM(_tsu.user_objects_dealloc_page_count)     AS UserObjectDeallocated,
                        SUM(_tsu.internal_objects_alloc_page_count)   AS InternalObjectAllocated,
                        SUM(_tsu.internal_objects_dealloc_page_count) AS InternalObjectDeallocated
              FROM      tempdb.sys.dm_db_task_space_usage AS _tsu
              GROUP BY  _tsu.session_id,
                        _tsu.request_id
          ) AS tdb
    ON  tdb.session_id = r.session_id
   AND  tdb.request_id = r.request_id
OUTER APPLY sys.dm_exec_sql_text(r.[sql_handle]) AS est
WHERE   t.session_id != @@SPID
  AND   (tdb.UserObjectAllocated - tdb.UserObjectDeallocated + tdb.InternalObjectAllocated - tdb.InternalObjectDeallocated) != 0
OPTION (RECOMPILE);"

            $server.Query($sql)
        }
    }
}
function Get-DbatoolsLog {
    
<#
    .SYNOPSIS
        Returns log entries for dbatools
    
    .DESCRIPTION
        Returns log entries for dbatools. Handy when debugging or developing a script using it.
    
    .PARAMETER FunctionName
        Default: "*"
        Only messages written by similar functions will be returned.
    
    .PARAMETER ModuleName
        Default: "*"
        Only messages written by commands from similar modules will be returned.
    
    .PARAMETER Target
        Only messags handling the specified target will be returned.
    
    .PARAMETER Tag
        Only messages containing one of these tags will be returned.
    
    .PARAMETER Last
        Only messages written by the last X executions will be returned.
        Uses Get-History to determine execution. Ignores Get-message commands.
        By default, this will also include messages from other runspaces. If your command executes in parallel, that's useful.
        If it doesn't and you were offloading executions to other runspaces, consider also filtering by runspace using '-Runspace'
    
    .PARAMETER Skip
        How many executions to skip when specifying '-Last'.
        Has no effect without the '-Last' parameter.
    
    .PARAMETER Runspace
        The guid of the runspace to return messages from.
        By default, messages from all runspaces are returned.
        Run the following line to see the list of guids:
        
        Get-Runspace | ft Id, Name, InstanceId -Autosize
    
    .PARAMETER Level
        Limit the message selection by level.
        Message levels have a numeric value, making it easier to select a range:
        
        -Level (1..6)
        
        Will select the first 6 levels (Critical - SomewhatVerbose).
    
    .PARAMETER Errors
        Instead of log entries, the error entries will be retrieved
    
    .EXAMPLE
        Get-DbatoolsLog
        
        Returns all log entries currently in memory.
    
    .EXAMPLE
        Get-DbatoolsLog -Target "a" -Last 1 -Skip 1
        
        Returns all log entries that targeted the object "a" in the second last execution sent.
    
    .EXAMPLE
        Get-DbatoolsLog -Tag "fail" -Last 5
        
        Returns all log entries within the last 5 executions that contained the tag "fail"
#>
    [CmdletBinding()]
    param (
        [string]
        $FunctionName = "*",
        
        [string]
        $ModuleName = "*",
        
        [AllowNull()]
        $Target,
        
        [string[]]
        $Tag,
        
        [int]
        $Last,
        
        [int]
        $Skip = 0,
        
        [guid]
        $Runspace,
        
        [Sqlcollaborative.Dbatools.Message.MessageLevel[]]
        $Level,
        
        [switch]
        $Errors
    )
    
    begin {
        
    }
    
    process {
        if ($Errors) { $messages = [Sqlcollaborative.Dbatools.Message.LogHost]::GetErrors() | Where-Object { ($_.FunctionName -like $FunctionName) -and ($_.ModuleName -like $ModuleName) } }
        else { $messages = [Sqlcollaborative.Dbatools.Message.LogHost]::GetLog() | Where-Object { ($_.FunctionName -like $FunctionName) -and ($_.ModuleName -like $ModuleName) } }
        
        if (Test-Bound -ParameterName Target) {
            $messages = $messages | Where-Object TargetObject -EQ $Target
        }
        
        if (Test-Bound -ParameterName Tag) {
            $messages = $messages | Where-Object { $_.Tags | Where-Object { $_ -in $Tag } }
        }
        
        if (Test-Bound -ParameterName Runspace) {
            $messages = $messages | Where-Object Runspace -EQ $Runspace
        }
        
        if (Test-Bound -ParameterName Last) {
            $history = Get-History | Where-Object CommandLine -NotLike "Get-DbatoolsLog*" | Select-Object -Last $Last -Skip $Skip
            $start = $history[0].StartExecutionTime
            $end = $history[-1].EndExecutionTime
            
            $messages = $messages | Where-Object { ($_.Timestamp -gt $start) -and ($_.Timestamp -lt $end) -and ($_.Runspace -eq ([System.Management.Automation.Runspaces.Runspace]::DefaultRunspace.InstanceId)) }
        }
        
        if (Test-Bound -ParameterName Level) {
            $messages = $messages | Where-Object Level -In $Level
        }
        
        return $messages
    }
    
    end {
        
    }
}
function Get-DbaTopResourceUsage {
    <#
    .SYNOPSIS
        Returns the top 20 resource consumers for cached queries based on four different metrics: duration, frequency, IO, and CPU.

    .DESCRIPTION
        Returns the top 20 resource consumers for cached queries based on four different metrics: duration, frequency, IO, and CPU.

        This command is based off of queries provided by Michael J. Swart at http://michaeljswart.com/go/Top20

        Per Michael: "I've posted queries like this before, and others have written many other versions of this query. All these queries are based on sys.dm_exec_query_stats."

    .PARAMETER SqlInstance
        Allows you to specify a comma separated list of servers to query.

    .PARAMETER SqlCredential
        Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

    .PARAMETER Database
        The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

    .PARAMETER ExcludeDatabase
        The database(s) to exclude - this list is auto-populated from the server

    .PARAMETER ExcludeSystem
        This will exclude system objects like replication procedures from being returned.

    .PARAMETER Type
        By default, all Types run but you can specify one or more of the following: Duration, Frequency, IO, or CPU

    .PARAMETER Limit
        By default, these query the Top 20 worst offenders (though more than 20 results can be returend if each of the top 20 have more than 1 subsequent result)

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Tags: Query, Performance
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Get-DbaTopResourceUsage

    .EXAMPLE
        Get-DbaTopResourceUsage -SqlInstance sql2008, sql2012
        Return the 80 (20 x 4 types) top usage results by duration, frequency, IO, and CPU servers for servers sql2008 and sql2012

    .EXAMPLE
        Get-DbaTopResourceUsage -SqlInstance sql2008 -Type Duration, Frequency -Database TestDB
        Return the highest usage by duration (top 20) and frequency (top 20) for the TestDB on sql2008

    .EXAMPLE
        Get-DbaTopResourceUsage -SqlInstance sql2016 -Limit 30
        Return the highest usage by duration (top 30) and frequency (top 30) for the TestDB on sql2016

    .EXAMPLE
    Get-DbaTopResourceUsage -SqlInstance sql2008, sql2012 -ExcludeSystem
        Return the 80 (20 x 4 types) top usage results by duration, frequency, IO, and CPU servers for servers sql2008 and sql2012 without any System Objects


    .EXAMPLE
        Get-DbaTopResourceUsage -SqlInstance sql2016| Select *
        Return all the columns plus the QueryPlan column
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [ValidateSet("All", "Duration", "Frequency", "IO", "CPU")]
        [string[]]$Type = "All",
        [int]$Limit = 20,
        [Alias('Silent')]
        [switch]$EnableException,
        [switch]$ExcludeSystem
    )

    begin {

        $instancecolumns = " SERVERPROPERTY('MachineName') AS ComputerName,
        ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
        SERVERPROPERTY('ServerName') AS SqlInstance, "

        if ($database) {
            $wheredb = " and coalesce(db_name(st.dbid), db_name(cast(pa.value AS INT)), 'Resource') in ('$($database -join '', '')')"
        }

        if ($ExcludeDatabase) {
            $wherenotdb = " and coalesce(db_name(st.dbid), db_name(cast(pa.value AS INT)), 'Resource') notin '$($excludedatabase -join '', '')'"
        }

        if ($ExcludeSystem) {
            $whereexcludesystem = " AND coalesce(object_name(st.objectid, st.dbid), '<none>') NOT LIKE 'sp_MS%' "
        }
        $duration = ";with long_queries as
                        (
                            select top $Limit
                                query_hash,
                                sum(total_elapsed_time) elapsed_time
                            from sys.dm_exec_query_stats
                            where query_hash <> 0x0
                            group by query_hash
                            order by sum(total_elapsed_time) desc
                        )
                        select $instancecolumns
                            coalesce(db_name(st.dbid), db_name(cast(pa.value AS INT)), 'Resource') AS [Database],
                            coalesce(object_name(st.objectid, st.dbid), '<none>') as ObjectName,
                            qs.query_hash as QueryHash,
                            qs.total_elapsed_time / 1000 as TotalElapsedTimeMs,
                            qs.execution_count as ExecutionCount,
                            cast((total_elapsed_time / 1000) / (execution_count + 0.0) as money) as AverageDurationMs,
                            lq.elapsed_time / 1000 as QueryTotalElapsedTimeMs,
                            SUBSTRING(st.TEXT,(qs.statement_start_offset + 2) / 2,
                                (CASE
                                    WHEN qs.statement_end_offset = -1  THEN LEN(CONVERT(NVARCHAR(MAX),st.text)) * 2
                                    ELSE qs.statement_end_offset
                                    END - qs.statement_start_offset) / 2) as QueryText,
                            qp.query_plan as QueryPlan
                        from sys.dm_exec_query_stats qs
                        join long_queries lq
                            on lq.query_hash = qs.query_hash
                        cross apply sys.dm_exec_sql_text(qs.sql_handle) st
                        cross apply sys.dm_exec_query_plan (qs.plan_handle) qp
                        outer apply sys.dm_exec_plan_attributes(qs.plan_handle) pa
                        where pa.attribute = 'dbid' $wheredb $wherenotdb $whereexcludesystem
                        order by lq.elapsed_time desc,
                            lq.query_hash,
                            qs.total_elapsed_time desc
                        option (recompile)"

        $frequency = ";with frequent_queries as
                        (
                            select top $Limit
                                query_hash,
                                sum(execution_count) executions
                            from sys.dm_exec_query_stats
                            where query_hash <> 0x0
                            group by query_hash
                            order by sum(execution_count) desc
                        )
                        select $instancecolumns
                            coalesce(db_name(st.dbid), db_name(cast(pa.value AS INT)), 'Resource') AS [Database],
                            coalesce(object_name(st.objectid, st.dbid), '<none>') as ObjectName,
                            qs.query_hash as QueryHash,
                            qs.execution_count as ExecutionCount,
                            executions as QueryTotalExecutions,
                            SUBSTRING(st.TEXT,(qs.statement_start_offset + 2) / 2,
                                (CASE
                                    WHEN qs.statement_end_offset = -1  THEN LEN(CONVERT(NVARCHAR(MAX),st.text)) * 2
                                    ELSE qs.statement_end_offset
                                    END - qs.statement_start_offset) / 2) as QueryText,
                            qp.query_plan as QueryPlan
                        from sys.dm_exec_query_stats qs
                        join frequent_queries fq
                            on fq.query_hash = qs.query_hash
                        cross apply sys.dm_exec_sql_text(qs.sql_handle) st
                        cross apply sys.dm_exec_query_plan (qs.plan_handle) qp
                        outer apply sys.dm_exec_plan_attributes(qs.plan_handle) pa
                        where pa.attribute = 'dbid'  $wheredb $wherenotdb $whereexcludesystem
                        order by fq.executions desc,
                            fq.query_hash,
                            qs.execution_count desc
                        option (recompile)"

        $io = ";with high_io_queries as
                (
                    select top $Limit
                        query_hash,
                        sum(total_logical_reads + total_logical_writes) io
                    from sys.dm_exec_query_stats
                    where query_hash <> 0x0
                    group by query_hash
                    order by sum(total_logical_reads + total_logical_writes) desc
                )
                select $instancecolumns
                    coalesce(db_name(st.dbid), db_name(cast(pa.value AS INT)), 'Resource') AS [Database],
                    coalesce(object_name(st.objectid, st.dbid), '<none>') as ObjectName,
                    qs.query_hash as QueryHash,
                    qs.total_logical_reads + total_logical_writes as TotalIO,
                    qs.execution_count as ExecutionCount,
                    cast((total_logical_reads + total_logical_writes) / (execution_count + 0.0) as money) as AverageIO,
                    io as QueryTotalIO,
                    SUBSTRING(st.TEXT,(qs.statement_start_offset + 2) / 2,
                        (CASE
                            WHEN qs.statement_end_offset = -1  THEN LEN(CONVERT(NVARCHAR(MAX),st.text)) * 2
                            ELSE qs.statement_end_offset
                            END - qs.statement_start_offset) / 2) as QueryText,
                    qp.query_plan as QueryPlan
                from sys.dm_exec_query_stats qs
                join high_io_queries fq
                    on fq.query_hash = qs.query_hash
                cross apply sys.dm_exec_sql_text(qs.sql_handle) st
                cross apply sys.dm_exec_query_plan (qs.plan_handle) qp
                outer apply sys.dm_exec_plan_attributes(qs.plan_handle) pa
                where pa.attribute = 'dbid' $wheredb $wherenotdb $whereexcludesystem
                order by fq.io desc,
                    fq.query_hash,
                    qs.total_logical_reads + total_logical_writes desc
                option (recompile)"

        $cpu = ";with high_cpu_queries as
                (
                    select top $Limit
                        query_hash,
                        sum(total_worker_time) cpuTime
                    from sys.dm_exec_query_stats
                    where query_hash <> 0x0
                    group by query_hash
                    order by sum(total_worker_time) desc
                )
                select $instancecolumns
                    coalesce(db_name(st.dbid), db_name(cast(pa.value AS INT)), 'Resource') AS [Database],
                    coalesce(object_name(st.objectid, st.dbid), '<none>') as ObjectName,
                    qs.query_hash as QueryHash,
                    qs.total_worker_time as CpuTime,
                    qs.execution_count as ExecutionCount,
                    cast(total_worker_time / (execution_count + 0.0) as money) as AverageCpuMs,
                    cpuTime as QueryTotalCpu,
                    SUBSTRING(st.TEXT,(qs.statement_start_offset + 2) / 2,
                        (CASE
                            WHEN qs.statement_end_offset = -1  THEN LEN(CONVERT(NVARCHAR(MAX),st.text)) * 2
                            ELSE qs.statement_end_offset
                            END - qs.statement_start_offset) / 2) as QueryText,
                    qp.query_plan as QueryPlan
                from sys.dm_exec_query_stats qs
                join high_cpu_queries hcq
                    on hcq.query_hash = qs.query_hash
                cross apply sys.dm_exec_sql_text(qs.sql_handle) st
                cross apply sys.dm_exec_query_plan (qs.plan_handle) qp
                outer apply sys.dm_exec_plan_attributes(qs.plan_handle) pa
                where pa.attribute = 'dbid' $wheredb $wherenotdb $whereexcludesystem
                order by hcq.cpuTime desc,
                    hcq.query_hash,
                    qs.total_worker_time desc
                option (recompile)"
    }

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            if ($server.ConnectionContext.StatementTimeout -ne 0) {
                $server.ConnectionContext.StatementTimeout = 0
            }

            if ($Type -in "All", "Duration") {
                try {
                    Write-Message -Level Debug -Message "Executing SQL: $duration"
                    $server.Query($duration) | Select-DefaultView -ExcludeProperty QueryPlan
                }
                catch {
                    Stop-Function -Message "Failure executing query for duration." -ErrorRecord $_ -Target $server -Continue
                }
            }

            if ($Type -in "All", "Frequency") {
                try {
                    Write-Message -Level Debug -Message "Executing SQL: $frequency"
                    $server.Query($frequency) | Select-DefaultView -ExcludeProperty QueryPlan
                }
                catch {
                    Stop-Function -Message "Failure executing query for frequency." -ErrorRecord $_ -Target $server -Continue
                }
            }

            if ($Type -in "All", "IO") {
                try {
                    Write-Message -Level Debug -Message "Executing SQL: $io"
                    $server.Query($io) | Select-DefaultView -ExcludeProperty QueryPlan
                }
                catch {
                    Stop-Function -Message "Failure executing query for IO." -ErrorRecord $_ -Target $server -Continue
                }
            }

            if ($Type -in "All", "CPU") {
                try {
                    Write-Message -Level Debug -Message "Executing SQL: $cpu"
                    $server.Query($cpu) | Select-DefaultView -ExcludeProperty QueryPlan
                }
                catch {
                    Stop-Function -Message "Failure executing query for CPU." -ErrorRecord $_ -Target $server -Continue
                }
            }
        }
    }
}
function Get-DbaTrace {
    <#
        .SYNOPSIS
        Gets a list of trace(s) from specified SQL Server Instance

        .DESCRIPTION
        This function returns a list of traces on a SQL Server instance and identifies the default trace file

        .PARAMETER SqlInstance
        A SQL Server instance to connect to

        .PARAMETER SqlCredential
        A credential to use to connect to the SQL Instance rather than using Windows Authentication

        .PARAMETER Id
        The id(s) of the Trace

        .PARAMETER Default
        Switch that will only return the information for the default system trace

        .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
        Tags: Security, Trace

        Author: Garry Bargsley (@gbargsley), http://blog.garrybargsley.com

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
        Get-DbaTrace -SqlInstance sql2016

        Lists all the tracefiles on the sql2016 SQL Server.

        .EXAMPLE
        Get-DbaTrace -SqlInstance sql2016 -Default

        Lists the default trace information on the sql2016 SQL Server.

#>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [int[]]$Id,
        [switch]$Default,
        [switch][Alias('Silent')]
        $EnableException
    )
    begin {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Get-DbaTraceFile

        # A Microsoft.SqlServer.Management.Trace.TraceServer class exists but is buggy
        # and requires x86 PowerShell. So we'll go with T-SQL.
        $sql = "SELECT id, status, path, max_size, stop_time, max_files, is_rowset, is_rollover, is_shutdown, is_default, buffer_count, buffer_size, file_position, reader_spid, start_time, last_event_time, event_count, dropped_event_count FROM sys.traces"

        if ($Id) {
            $idstring = $Id -join ","
            $sql = "$sql WHERE id in ($idstring)"
        }
    }
    process {
        foreach ($instance in $SqlInstance) {

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                return
            }

            try {
                $results = $server.Query($sql)
            }
            catch {
                Stop-Function -Message "Issue collecting trace data on $server" -Target $server -ErrorRecord $_
            }

            if ($Default) {
                $results = $results | Where-Object { $_.is_default }
            }

            foreach ($row in $results) {
                if ($row.Path.ToString().Length -gt 0) {
                    $remotefile = Join-AdminUnc -servername $server.ComputerName -filepath $row.path
                }
                else {
                    $remotefile = $null
                }

                [PSCustomObject]@{
                    ComputerName             = $server.ComputerName
                    InstanceName             = $server.ServiceName
                    SqlInstance              = $server.DomainInstanceName
                    Id                       = $row.id
                    Status                   = $row.status
                    IsRunning                = ($row.status -eq 1)
                    Path                     = $row.path
                    RemotePath               = $remotefile
                    MaxSize                  = $row.max_size
                    StopTime                 = $row.stop_time
                    MaxFiles                 = $row.max_files
                    IsRowset                 = $row.is_rowset
                    IsRollover               = $row.is_rollover
                    IsShutdown               = $row.is_shutdown
                    IsDefault                = $row.is_default
                    BufferCount              = $row.buffer_count
                    BufferSize               = $row.buffer_size
                    FilePosition             = $row.file_position
                    ReaderSpid               = $row.reader_spid
                    StartTime                = $row.start_time
                    LastEventTime            = $row.last_event_time
                    EventCount               = $row.event_count
                    DroppedEventCount        = $row.dropped_event_count
                    Parent                   = $server
                    SqlCredential            = $SqlCredential
                } | Select-DefaultView -ExcludeProperty Parent, RemotePath, RemoStatus, SqlCredential
            }
        }
    }
}
function Get-DbaTraceFlag {
    <#
        .SYNOPSIS
            Get global Trace Flag(s) information for each instance(s) of SQL Server.

        .DESCRIPTION
            Returns Trace Flags that are enabled globally on each instance(s) of SQL Server as an object.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER TraceFlag
            Use this switch to filter to a specific Trace Flag.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: TraceFlag
            Author: Kevin Bullen (@sqlpadawan)

            References:  https://docs.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-traceon-trace-flags-transact-sql

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaTraceFlag

        .EXAMPLE
            Get-DbaTraceFlag -SqlInstance localhost

            Returns all Trace Flag information on the local default SQL Server instance

        .EXAMPLE
            Get-DbaTraceFlag -SqlInstance localhost, sql2016

            Returns all Trace Flag(s) for the local and sql2016 SQL Server instances

        .EXAMPLE
            Get-DbaTraceFlag -SqlInstance localhost -TraceFlag 4199,3205

            Returns Trace Flag status for TF 4199 and 3205 for the local SQL Server instance if they are enabled.
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]
        $SqlCredential,
        [int[]]$TraceFlag,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $tflags = $server.EnumActiveGlobalTraceFlags()

            if ($tFlags.Rows.Count -eq 0) {
                Write-Message -Level Output -Message "No global trace flags enabled"
                return
            }

            if ($TraceFlag) {
                $tflags = $tflags | Where-Object TraceFlag -In $TraceFlag
            }

            foreach ($tflag in $tflags) {
                [pscustomobject]@{
                    ComputerName = $server.ComputerName
                    InstanceName = $server.ServiceName
                    SqlInstance  = $server.DomainInstanceName
                    TraceFlag    = $tflag.TraceFlag
                    Global       = $tflag.Global
                    Session      = $tflag.Session
                    Status       = $tflag.Status
                } | Select-DefaultView -ExcludeProperty 'Session'
            }
        }
    }
}
function Get-DbaTrigger {
    <#
.SYNOPSIS
Get all existing triggers on one or more SQL instances.

.DESCRIPTION
Get all existing triggers on one or more SQL instances.

Default output includes columns ComputerName, SqlInstance, Database, TriggerName, IsEnabled and DateLastModified.

.PARAMETER SqlInstance
The SQL Instance that you're connecting to.

.PARAMETER SqlCredential
SqlCredential object used to connect to the SQL Server as a different user.

.PARAMETER Database
The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

.PARAMETER ExcludeDatabase
The database(s) to exclude - this list is auto-populated from the server

.NOTES
Tags: Database, Triggers
Author: Klaas Vandenberghe ( @PowerDBAKlaas )

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
 https://dbatools.io/Get-DbaTrigger

.EXAMPLE
Get-DbaTrigger -SqlInstance ComputerA\sql987

Returns a custom object displaying ComputerName, SqlInstance, Database, TriggerName, IsEnabled and DateLastModified.

.EXAMPLE
Get-DbaTrigger -SqlInstance 'ComputerA\sql987','ComputerB'

Returns a custom object displaying ComputerName, SqlInstance, Database, TriggerName, IsEnabled and DateLastModified from two instances.

.EXAMPLE
Get-DbaTrigger -SqlInstance ComputerA\sql987 | Out-Gridview

Returns a gridview displaying ComputerName, SqlInstance, Database, TriggerName, IsEnabled and DateLastModified.

.EXAMPLE
'ComputerA\sql987','ComputerB' | Get-DbaTrigger | Out-Gridview

Returns a custom object displaying ComputerName, SqlInstance, Database, TriggerName, IsEnabled and DateLastModified from two instances.

#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer", "instance")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase
    )

    process {
        foreach ($Instance in $SqlInstance) {
            Write-Verbose "Connecting to $Instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $Instance -SqlCredential $SqlCredential -Erroraction SilentlyContinue
            }
            catch {
                Write-Warning "Can't connect to $Instance"
                continue
            }

            Write-Verbose "Getting Server Level Triggers on $Instance"
            $server.Triggers |
                ForEach-Object {
                [PSCustomObject]@{
                    ComputerName     = $server.ComputerName
                    InstanceName     = $server.ServiceName
                    SqlInstance      = $server.DomainInstanceName
                    TriggerLevel     = "Server"
                    Database         = $null
                    TriggerName      = $_.Name
                    Status           = switch ($_.IsEnabled) { $true { "Enabled" } $false { "Disabled" } }
                    DateLastModified = $_.DateLastModified
                }
            }

            Write-Verbose "Getting Database Level Triggers on $Instance"
            $dbs = $server.Databases | Where-Object { $_.status -eq 'Normal' }

            if ($Database) {
                $dbs = $dbs | Where-Object Name -in $Database
            }
            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -notin $ExcludeDatabase
            }

            $dbs |
                ForEach-Object {
                $DatabaseName = $_.Name
                Write-Verbose "Getting Database Level Triggers on Database $DatabaseName on $Instance"
                $_.Triggers |
                    ForEach-Object {
                    [PSCustomObject]@{
                        ComputerName     = $server.ComputerName
                        InstanceName     = $server.ServiceName
                        SqlInstance      = $server.DomainInstanceName
                        TriggerLevel     = "Database"
                        Database         = $DatabaseName
                        TriggerName      = $_.Name
                        Status           = switch ($_.IsEnabled) { $true { "Enabled" } $false { "Disabled" } }
                        DateLastModified = $_.DateLastModified
                    }
                }
            }
        }
    }
}
function Get-DbaUptime {
    <#
        .SYNOPSIS
            Returns the uptime of the SQL Server instance, and if required the hosting windows server

        .DESCRIPTION
            By default, this command returns for each SQL Server instance passed in:
            SQL Instance last startup time, Uptime as a PS TimeSpan, Uptime as a formatted string
            Hosting Windows server last startup time, Uptime as a PS TimeSpan, Uptime as a formatted string

        .PARAMETER SqlInstance
            The SQL Server instance that you're connecting to.

        .PARAMETER SqlCredential
            Allows you to login to servers using SQL Logins instead of Windows Authentication (AKA Integrated or Trusted). To use:

            $scred = Get-Credential, then pass $scred object to the -SqlCredential parameter.

            Windows Authentication will be used if SqlCredential is not specified. SQL Server does not accept Windows credentials being passed as credentials.

            To connect to SQL Server as a different Windows user, run PowerShell as that user.

        .PARAMETER Credential
            Allows you to login to the computer (not SQL Server instance) using alternative Windows credentials.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: CIM
            Author: Stuart Moore (@napalmgram), stuart-moore.com

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaUptime

        .EXAMPLE
            Get-DbaUptime -SqlInstance SqlBox1\Instance2

            Returns an object with SQL Server start time, uptime as TimeSpan object, uptime as a string, and Windows host boot time, host uptime as TimeSpan objects and host uptime as a string for the sqlexpress instance on winserver

        .EXAMPLE
            Get-DbaUptime -SqlInstance winserver\sqlexpress, sql2016

            Returns an object with SQL Server start time, uptime as TimeSpan object, uptime as a string, and Windows host boot time, host uptime as TimeSpan objects and host uptime as a string for the sqlexpress instance on host winserver  and the default instance on host sql2016

        .EXAMPLE
            Get-DbaRegisteredServer -SqlInstance sql2014 | Get-DbaUptime

            Returns an object with SQL Server start time, uptime as TimeSpan object, uptime as a string, and Windows host boot time, host uptime as TimeSpan objects and host uptime as a string for every server listed in the Central Management Server on sql2014

    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer", "ComputerName")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [PSCredential]$Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $nowutc = (Get-Date).ToUniversalTime()
    }
    process {
        foreach ($instance in $SqlInstance) {
            if ($instance.Gettype().FullName -eq [System.Management.Automation.PSCustomObject] ) {
                $servername = $instance.SqlInstance
            }
            elseif ($instance.Gettype().FullName -eq [Microsoft.SqlServer.Management.Smo.Server]) {
                $servername = $instance.ComputerName
            }
            else {
                $servername = $instance.ComputerName;
            }

            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            Write-Message -Level Verbose -Message "Getting start times for $servername"
            #Get tempdb creation date
            [dbadatetime]$SQLStartTime = $server.Databases["tempdb"].CreateDate
            $SQLUptime = New-TimeSpan -Start $SQLStartTime.ToUniversalTime() -End $nowutc
            $SQLUptimeString = "{0} days {1} hours {2} minutes {3} seconds" -f $($SQLUptime.Days), $($SQLUptime.Hours), $($SQLUptime.Minutes), $($SQLUptime.Seconds)

            $WindowsServerName = (Resolve-DbaNetworkName $servername -Credential $Credential).FullComputerName

            try {
                Write-Message -Level Verbose -Message "Getting WinBootTime via CimInstance for $servername"
                $WinBootTime = (Get-DbaOperatingSystem -ComputerName $windowsServerName -Credential $Credential -ErrorAction SilentlyContinue).LastBootTime
                $WindowsUptime = New-TimeSpan -start $WinBootTime.ToUniversalTime() -end $nowutc
                $WindowsUptimeString = "{0} days {1} hours {2} minutes {3} seconds" -f $($WindowsUptime.Days), $($WindowsUptime.Hours), $($WindowsUptime.Minutes), $($WindowsUptime.Seconds)
            }
            catch {
                try {
                    Write-Message -Level Verbose -Message "Getting WinBootTime via CimInstance DCOM"
                    $CimOption = New-CimSessionOption -Protocol DCOM
                    $CimSession = New-CimSession -Credential:$Credential -ComputerName $WindowsServerName -SessionOption $CimOption
                    [dbadatetime]$WinBootTime = ($CimSession | Get-CimInstance -ClassName Win32_OperatingSystem).LastBootUpTime
                    $WindowsUptime = New-TimeSpan -start $WinBootTime.ToUniversalTime() -end $nowutc
                    $WindowsUptimeString = "{0} days {1} hours {2} minutes {3} seconds" -f $($WindowsUptime.Days), $($WindowsUptime.Hours), $($WindowsUptime.Minutes), $($WindowsUptime.Seconds)
                }
                catch {
                    Stop-Function -Message "Failure getting WinBootTime" -ErrorRecord $_ -Target $instance -Continue
                }
            }

            [PSCustomObject]@{
                ComputerName     = $WindowsServerName
                InstanceName     = $server.ServiceName
                SqlServer        = $server.Name
                SqlUptime        = $SQLUptime
                WindowsUptime    = $WindowsUptime
                SqlStartTime     = $SQLStartTime
                WindowsBootTime  = $WinBootTime
                SinceSqlStart    = $SQLUptimeString
                SinceWindowsBoot = $WindowsUptimeString
            }
        }
    }
}
function Get-DbaUserLevelPermission {
    <#
    .SYNOPSIS
        Displays detailed permissions information for the server and database roles and securables.

    .DESCRIPTION
        This command will display all server logins, server level securable, database logins and database securables.

        DISA STIG implementators will find this command useful as it uses Permissions.sql provided by DISA.

        Note that if you Ctrl-C out of this command and end it prematurely, it will leave behind a STIG schema in tempdb.

    .PARAMETER SqlInstance
        Allows you to specify a comma separated list of servers to query.

    .PARAMETER SqlCredential
        Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

    .PARAMETER Database
        The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

    .PARAMETER ExcludeDatabase
        The database(s) to exclude - this list is auto-populated from the server

    .PARAMETER ExcludeSystemDatabase
        Allows you to suppress output on system databases

    .PARAMETER IncludePublicGuest
        Allows you to include output for public and guest grants.

    .PARAMETER IncludeSystemObjects
        Allows you to include output on sys schema objects.

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
    Tags: Discovery, Permissions, Security
    Author: Brandon Abshire, netnerds.net

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Get-DbaUserLevelPermission

    .EXAMPLE
        Get-DbaUserLevelPermission -SqlInstance sql2008, sqlserver2012
        Check server and database permissions for servers sql2008 and sqlserver2012.

    .EXAMPLE
        Get-DbaUserLevelPermission -SqlInstance sql2008 -Database TestDB
        Check server and database permissions on server sql2008 for only the TestDB database

    .EXAMPLE
        Get-DbaUserLevelPermission -SqlInstance sql2008 -Database TestDB -IncludePublicGuest -IncludeSystemObjects
        Check server and database permissions on server sql2008 for only the TestDB database,
        including public and guest grants, and sys schema objects.
    #>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [parameter(Position = 1, Mandatory = $false)]
        [switch]$ExcludeSystemDatabase,
        [switch]$IncludePublicGuest,
        [switch]$IncludeSystemObjects,
        [Alias('Silent')]
        [switch]$EnableException
    )

    BEGIN {

        $sql = [System.IO.File]::ReadAllText("$script:PSModuleRoot\bin\stig.sql")

        $endSQL = "	   BEGIN TRY DROP FUNCTION STIG.server_effective_permissions END TRY BEGIN CATCH END CATCH;
                       GO
                       BEGIN TRY DROP VIEW STIG.server_permissions END TRY BEGIN CATCH END CATCH;
                       GO
                       BEGIN TRY DROP FUNCTION STIG.members_of_server_role END TRY BEGIN CATCH END CATCH;
                       GO
                       BEGIN TRY DROP FUNCTION STIG.server_roles_of END TRY BEGIN CATCH END CATCH;
                       GO
                       BEGIN TRY DROP VIEW STIG.server_role_members END TRY BEGIN CATCH END CATCH;
                       GO
                       BEGIN TRY DROP FUNCTION STIG.database_effective_permissions END TRY BEGIN CATCH END CATCH;
                       GO
                       BEGIN TRY DROP VIEW STIG.database_permissions END TRY BEGIN CATCH END CATCH;
                       GO
                       BEGIN TRY DROP FUNCTION STIG.members_of_db_role END TRY BEGIN CATCH END CATCH;
                       GO
                       BEGIN TRY DROP FUNCTION STIG.database_roles_of END TRY BEGIN CATCH END CATCH;
                       GO
                       BEGIN TRY DROP VIEW STIG.database_role_members END TRY BEGIN CATCH END CATCH;
                       GO
                       BEGIN TRY DROP SCHEMA STIG END TRY BEGIN CATCH END CATCH;
                       GO"


        $serverSQL = "SELECT  'SERVER LOGINS' AS Type ,
                                    sl.name AS Member ,
                                    ISNULL(srm.role, 'None') AS [Role/Securable/Class] ,
                                    ' ' AS [Schema/Owner] ,
                                    ' ' AS [Securable] ,
                                    ' ' AS [Grantee Type] ,
                                    ' ' AS [Grantee] ,
                                    ' ' AS [Permission] ,
                                    ' ' AS [State] ,
                                    ' ' AS [Grantor] ,
                                    ' ' AS [Grantor Type] ,
                                    ' ' AS [Source View]
                            FROM    master.sys.syslogins sl
                                    LEFT JOIN tempdb.[STIG].[server_role_members] srm ON sl.name = srm.member
                            WHERE   sl.name NOT LIKE 'NT %'
                                    AND sl.name NOT LIKE '##%'
                            UNION
                            SELECT  'SERVER SECURABLES' AS Type ,
                                    sl.name ,
                                    sp.[Securable Class] COLLATE SQL_Latin1_General_CP1_CI_AS ,
                                    ' ' ,
                                    sp.[Securable] ,
                                    sp.[Grantee Type] COLLATE SQL_Latin1_General_CP1_CI_AS ,
                                    sp.Grantee ,
                                    sp.Permission COLLATE SQL_Latin1_General_CP1_CI_AS ,
                                    sp.State COLLATE SQL_Latin1_General_CP1_CI_AS ,
                                    sp.Grantor ,
                                    sp.[Grantor Type] COLLATE SQL_Latin1_General_CP1_CI_AS ,
                                    sp.[Source View]
                            FROM    master.sys.syslogins sl
                                    LEFT JOIN tempdb.[STIG].[server_permissions] sp ON sl.name = sp.Grantee
                            WHERE   sl.name NOT LIKE 'NT %'
                                    AND sl.name NOT LIKE '##%';"

        $dbSQL = "SELECT  'DB ROLE MEMBERS' AS type ,
                                Member ,
                                Role ,
                                ' ' AS [Schema/Owner] ,
                                ' ' AS [Securable] ,
                                ' ' AS [Grantee Type] ,
                                ' ' AS [Grantee] ,
                                ' ' AS [Permission] ,
                                ' ' AS [State] ,
                                ' ' AS [Grantor] ,
                                ' ' AS [Grantor Type] ,
                                ' ' AS [Source View]
                        FROM    tempdb.[STIG].[database_role_members]
                        UNION
                        SELECT DISTINCT
                                'DB SECURABLES' AS Type ,
                                drm.member ,
                                dp.[Securable Type or Class] COLLATE SQL_Latin1_General_CP1_CI_AS ,
                                dp.[Schema/Owner] ,
                                dp.Securable ,
                                dp.[Grantee Type] COLLATE SQL_Latin1_General_CP1_CI_AS ,
                                dp.Grantee ,
                                dp.Permission COLLATE SQL_Latin1_General_CP1_CI_AS ,
                                dp.State COLLATE SQL_Latin1_General_CP1_CI_AS ,
                                dp.Grantor ,
                                dp.[Grantor Type] COLLATE SQL_Latin1_General_CP1_CI_AS ,
                                dp.[Source View]
                        FROM    tempdb.[STIG].[database_role_members] drm
                                LEFT JOIN tempdb.[STIG].[database_permissions] dp ON ( drm.member = dp.grantee
                                                                                      OR drm.role = dp.grantee
                                                                                     )
                        WHERE	dp.Grantor IS NOT NULL
                                AND [Schema/Owner] <> 'sys'"

        if ($IncludePublicGuest) { $dbSQL = $dbSQL.Replace("LEFT JOIN", "FULL JOIN") }
        if ($IncludeSystemObjects) { $dbSQL = $dbSQL.Replace("AND [Schema/Owner] <> 'sys'", "") }

    }

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $dbs = $server.Databases

            if ($Database) {
                $dbs = $dbs | Where-Object { $Database -contains $_.Name }
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            if ($ExcludeSystemDatabase) {
                $dbs = $dbs | Where-Object IsSystemObject -eq $false
            }

            #reset $serverDT
            $serverDT = $null

            foreach ($db in $dbs) {
                Write-Message -Level Verbose -Message "Processing $db on $instance"

                if ($db.IsAccessible -eq $false) {
                    Stop-Function -Message "The database $db is not accessible" -Continue
                }

                $sql = $sql.Replace("<TARGETDB>", $db.Name)

                #Create objects in active database
                Write-Message -Level Verbose -Message "Creating objects"
                try { $db.ExecuteNonQuery($sql) } catch {} # sometimes it complains about not being able to drop the stig schema if the person Ctrl-C'd before.

                #Grab permissions data
                if (-not $serverDT) {
                    Write-Message -Level Verbose -Message "Building data table for server objects"

                    try { $serverDT = $db.Query($serverSQL) } catch { }

                    foreach ($row in $serverDT) {
                        [PSCustomObject]@{
                            ComputerName       = $server.ComputerName
                            InstanceName       = $server.ServiceName
                            SqlInstance        = $server.DomainInstanceName
                            Object             = 'SERVER'
                            Type               = $row.Type
                            Member             = $row.Member
                            RoleSecurableClass = $row.'Role/Securable/Class'
                            SchemaOwner        = $row.'Schema/Owner'
                            Securable          = $row.Securable
                            GranteeType        = $row.'Grantee Type'
                            Grantee            = $row.Grantee
                            Permission         = $row.Permission
                            State              = $row.State
                            Grantor            = $row.Grantor
                            GrantorType        = $row.'Grantor Type'
                            SourceView         = $row.'Source View'
                        }
                    }
                }

                Write-Message -Level Verbose -Message "Building data table for $db objects"
                try { $dbDT = $db.Query($dbSQL) } catch { }

                foreach ($row in $dbDT) {
                    [PSCustomObject]@{
                        ComputerName       = $server.ComputerName
                        InstanceName       = $server.ServiceName
                        SqlInstance        = $server.DomainInstanceName
                        Object             = $db.Name
                        Type               = $row.Type
                        Member             = $row.Member
                        RoleSecurableClass = $row.'Role/Securable/Class'
                        SchemaOwner        = $row.'Schema/Owner'
                        Securable          = $row.Securable
                        GranteeType        = $row.'Grantee Type'
                        Grantee            = $row.Grantee
                        Permission         = $row.Permission
                        State              = $row.State
                        Grantor            = $row.Grantor
                        GrantorType        = $row.'Grantor Type'
                        SourceView         = $row.'Source View'
                    }
                }

                #Delete objects
                Write-Message -Level Verbose -Message "Deleting objects"
                try { $db.ExecuteNonQuery($endSQL) } catch { }
                $sql = $sql.Replace($db.Name, "<TARGETDB>")

                #Sashay Away
            }
        }
    }
}
function Get-DbaWaitingTask {
    <#
        .SYNOPSIS
            Displays waiting task.

        .DESCRIPTION
            This command is based on waiting task T-SQL script published by Paul Randal.
            Reference: https://www.sqlskills.com/blogs/paul/updated-sys-dm_os_waiting_tasks-script-2/

        .PARAMETER SqlInstance
            The SQL Server instance. Server version must be SQL Server version XXXX or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Spid
            Find the waiting task of one or more specific process ids

        .PARAMETER IncludeSystemSpid
            If this switch is enabled, the output will include the system sessions.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Waits,Task,WaitTask
            Author: Shawn Melton (@wsmelton)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaWaitingTask

        .EXAMPLE
            Get-DbaWaitingTask -SqlInstance sqlserver2014a

            Returns the waiting task for all sessions on sqlserver2014a

        .EXAMPLE
            Get-DbaWaitingTask -SqlInstance sqlserver2014a -IncludeSystemSpid

            Returns the waiting task for all sessions (user and system) on sqlserver2014a
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(ValueFromPipelineByPropertyName = $true)]
        [object[]]$Spid,
        [switch]$IncludeSystemSpid,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $sql = "
            SELECT
                [owt].[session_id] AS [Spid],
                [owt].[exec_context_id] AS [Thread],
                [ot].[scheduler_id] AS [Scheduler],
                [owt].[wait_duration_ms] AS [WaitMs],
                [owt].[wait_type] AS [WaitType],
                [owt].[blocking_session_id] AS [BlockingSpid],
                [owt].[resource_description] AS [ResourceDesc],
                CASE [owt].[wait_type]
                    WHEN N'CXPACKET' THEN
                        RIGHT ([owt].[resource_description],
                            CHARINDEX (N'=', REVERSE ([owt].[resource_description])) - 1)
                    ELSE NULL
                END AS [NodeId],
                [eqmg].[dop] AS [Dop],
                [er].[database_id] AS [DbId],
                [est].text AS [SqlText],
                [eqp].[query_plan] AS [QueryPlan],
                CAST ('https://www.sqlskills.com/help/waits/' + [owt].[wait_type] as XML) AS [URL]
            FROM sys.dm_os_waiting_tasks [owt]
            INNER JOIN sys.dm_os_tasks [ot] ON
                [owt].[waiting_task_address] = [ot].[task_address]
            INNER JOIN sys.dm_exec_sessions [es] ON
                [owt].[session_id] = [es].[session_id]
            INNER JOIN sys.dm_exec_requests [er] ON
                [es].[session_id] = [er].[session_id]
            FULL JOIN sys.dm_exec_query_memory_grants [eqmg] ON
                [owt].[session_id] = [eqmg].[session_id]
            OUTER APPLY sys.dm_exec_sql_text ([er].[sql_handle]) [est]
            OUTER APPLY sys.dm_exec_query_plan ([er].[plan_handle]) [eqp]
            WHERE
                [es].[is_user_process] = $(if (Test-Bound 'IncludeSystemSpid') {0} else {1})
            ORDER BY
                [owt].[session_id],
                [owt].[exec_context_id]
            OPTION(RECOMPILE);"
    }
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $results = $server.Query($sql)
            foreach ($row in $results) {
                if (Test-Bound 'Spid') {
                    if ($row.Spid -notin $Spid) { continue }
                }

                [PSCustomObject]@{
                    ComputerName = $server.ComputerName
                    InstanceName = $server.ServiceName
                    SqlInstance  = $server.DomainInstanceName
                    Spid         = $row.Spid
                    Thread       = $row.Thread
                    Scheduler    = $row.Scheduler
                    WaitMs       = $row.WaitMs
                    WaitType     = $row.WaitType
                    BlockingSpid = $row.BlockingSpid
                    ResourceDesc = $row.ResourceDesc
                    NodeId       = $row.NodeId
                    Dop          = $row.Dop
                    DbId         = $row.DbId
                    SqlText      = $row.SqlText
                    QueryPlan    = $row.QueryPlan
                    InfoUrl      = $row.InfoUrl
                } | Select-DefaultView -ExcludeProperty 'SqlText', 'QueryPlan', 'InfoUrl'
            }
        }
    }
}
function Get-DbaWaitResource {
    <#
    .SYNOPSIS
        Returns the resource being waited upon

    .DESCRIPTION
        Given a wait resource in the form of:
            'PAGE: 10:1:9180084 '
        returns the database, data file and the system object which is being waited up.
        Given a wait resource in the form of:
            'KEY: 7:35457594073541168 (de21f92a1572)'
        returns the database, object and index that is being waited on, With the -row switch the row data will also be returned.
    .PARAMETER SqlInstance
        The SQL Server instance to restore to.

    .PARAMETER SqlCredential
        Allows you to login to servers using SQL Logins as opposed to Windows Auth/Integrated/Trusted.

    .PARAMETER WaitResource
        The waitresource value as supplied in sys.dm_exec_requests

    .PARAMETER Row
        If this switch provided also returns the value of the row being waited on with KEY wait resources

    .PARAMETER EnableException
        Replaces user friendly yellow warnings with bloody red exceptions of doom!
        Use this if you want the function to throw terminating errors you want to catch.

    .EXAMPLE
        Get-DbaWaitResource -SqlInstance server1 -WaitResource 'PAGE: 10:1:9180084'

        Will return an object containing; database name, data file name, schema name and the object which owns the resource

    .EXAMPLE
        Get-DbaWaitResource -Sql Instance server2 -WaitResource 'KEY: 7:35457594073541168 (de21f92a1572)'

        Will return an object containing; database name, schema name and index name which is being waited on.

    .EXAMPLE
        Get-DbaWaitResource -Sql Instance server2 -WaitResource 'KEY: 7:35457594073541168 (de21f92a1572)' -row

        Will return an object containing; database name, schema name and index name which is being waited on, and in addition the contents of the locked row at the time the command is run.

    .NOTES
        Tags: Pages, DBCC
        Author: Stuart Moore (@napalmgram), stuart-moore.com

        dbatools PowerShell module (https://dbatools.io, [email protected])
        Copyright (C) 2016 Chrissy LeMaire
        License: MIT https://opensource.org/licenses/MIT
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance]$SqlInstance,
        [PsCredential]$SqlCredential,
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [string]$WaitResource,
        [switch]$Row,
        [switch]$EnableException
    )

    process {
        if ($WaitResource -notmatch '^PAGE: [0-9]*:[0-9]*:[0-9]*$' -and $WaitResource -notmatch '^KEY: [0-9]*:[0-9]* \([a-f0-9]*\)$'){
           Stop-Function -Message "Row input - $WaitResource - Improperly formatted"
           return
        }

        try {
            $server = Connect-SqlInstance -SqlInstance $sqlinstance -SqlCredential $SqlCredential
        }
        catch {
            Write-Message -Level Warning -Message "Cannot connect to $SqlInstance"
        }

        $null = $WaitResource -match '^(?<Type>[A-Z]*): (?<dbid>[0-9]*):*'
        $ResourceType = $matches.Type
        $DbId = $matches.DbId
        $DbName = ($server.Databases | Where-Object ID -eq $dbid).Name
        if ($null -eq $DbName){
            stop-function -Message "Database with id $dbid does not exist on $server"
            return
        }
        if ($ResourceType -eq 'PAGE'){
            $null = $WaitResource -match '^(?<Type>[A-Z]*): (?<dbid>[0-9]*):(?<FileID>[0-9]*):(?<PageID>[0-9]*)$'
            $DataFileSql = "select name, physical_name from sys.master_files where database_id=$DbID and file_ID=$($matches.FileID);"
            $DataFile = $server.query($DataFileSql)
            if ($null -eq $DataFile){
                Write-Message -Level Warning -Message "Datafile with id $($matches.FileID) for $dbname not found"
                return
            }
            $ObjectIdSQL = "dbcc traceon (3604); dbcc page ($dbid,$($matches.fileID),$($matches.PageID),2) with tableresults;"
            try {
                $ObjectID = ($server.databases[$dbname].Query($ObjectIdSQL) | Where-Object Field -eq 'Metadata: ObjectId').Value
            }
            catch {
                Stop-Function -Message "You've requested a page beyond the end of the database, exiting"
                return
            }
            if ($null -eq $ObjectID){
            Write-Message -Level Warning -Message "Object not found, could have been delete, or a transcription error when copying the Wait_resource to PowerShell"
            return
            }
            $ObjectSql = "select SCHEMA_NAME(schema_id) as SchemaName, name, type_desc from sys.all_objects where object_id=$objectID;"
            $Object = $server.databases[$dbname].query($ObjectSql)
            if ($null -eq $Object){
                Write-Message -Warning "Object could not be found. Could have been removed, or could be a transcription error copying the Wait_resource to sowerShell"
            }
            [PsCustomObject]@{
                DatabaseID = $DbId
                DatabaseName = $DbName
                DataFileName = $Datafile.name
                DataFilePath = $DataFile.physical_name
                ObjectID = $ObjectID
                ObjectName = $Object.Name
                ObjectSchema = $Object.SchemaName
                ObjectType = $Object.type_desc
            }
        }
        if ($ResourceType -eq 'KEY'){
            $null = $WaitResource -match '^(?<Type>[A-Z]*): (?<dbid>[0-9]*):(?<frodo>[0-9]*) (?<physloc>\(.*\))$'
            $IndexSql = "select
                            sp.object_id as ObjectID,
                            OBJECT_SCHEMA_NAME(sp.object_id) as SchemaName,
                            sao.name as ObjectName,
                            si.name as IndexName
                        from
                            sys.partitions sp inner join sys.indexes si on sp.index_id=si.index_id and sp.object_id=si.object_id
                                inner join sys.all_objects sao on sp.object_id=sao.object_id
                        where
                            hobt_id = $($matches.frodo);
                "
            $Index = $server.databases[$dbname].Query($IndexSql)
            if ($null -eq $Index){
                Write-Message -Level Warning -Message "Heap or B-Tree with ID $($matches.frodo) can not be found in $dbname on $server"
                return
            }
            $output = [PsCustomObject]@{
                DatabaseID = $DbId
                DatabaseName = $DbName
                SchemaName = $Index.SchemaName
                IndexName = $Index.IndexName
                ObjectID = $index.ObjectID
                Objectname = $index.ObjectName
                HobtID = $matches.frodo
            }
            if ($row -eq $True){
                $DataSql = "select * from $($Index.SchemaName).$($Index.ObjectName) with (NOLOCK) where %%lockres%% ='$($matches.physloc)'"
                $Data = $server.databases[$dbname].query($DataSql)
                if ($null -eq $data){
                    Write-Message -Level warning -Message "Could not retrieve the data. It may have been deleted or moved since the wait resource value was generated"
                }
                else{
                    $output | Add-Member -Type NoteProperty -Name ObjectData -Value $Data
                    $output | Select-Object * -ExpandProperty ObjectData
                }
            }
            else {
                $output
            }
        }
    }
}
function Get-DbaWaitStatistic {
    <#
        .SYNOPSIS
            Displays wait statistics

        .DESCRIPTION
            This command is based off of Paul Randal's post "Wait statistics, or please tell me where it hurts"

            Returns:
                        WaitType
                        Category
                        WaitSeconds
                        ResourceSeconds
                        SignalSeconds
                        WaitCount
                        Percentage
                        AverageWaitSeconds
                        AverageResourceSeconds
                        AverageSignalSeconds
                        URL

            Reference: https://www.sqlskills.com/blogs/paul/wait-statistics-or-please-tell-me-where-it-hurts/

        .PARAMETER SqlInstance
            The SQL Server instance. Server version must be SQL Server version 2005 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Threshold
            Threshold, in percentage of all waits on the system. Default per Paul's post is 95%.

        .PARAMETER IncludeIgnorable
            Some waits are no big deal and can be safely ignored in most circumstances. If you've got weird issues with mirroring or AGs.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: WaitStatistic
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaWaitStatistic

        .EXAMPLE
            Get-DbaWaitStatistic -SqlInstance sql2008, sqlserver2012

            Check wait statistics for servers sql2008 and sqlserver2012

        .EXAMPLE
            Get-DbaWaitStatistic -SqlInstance sql2008 -Threshold 98 -IncludeIgnorable

            Check wait statistics on server sql2008 for thresholds above 98% and include wait stats that are most often, but not always, ignorable

        .EXAMPLE
            Get-DbaWaitStatistic -SqlInstance sql2008 | Select *

            Shows detailed notes, if available, from Paul's post

        .EXAMPLE
            $output = Get-DbaWaitStatistic -SqlInstance sql2008 -Threshold 100 -IncludeIgnorable | Select * | ConvertTo-DbaDataTable

            Collects all Wait Statistics (including ignorable waits) on server sql2008 into a Data Table.


        .EXAMPLE
            $output = Get-DbaWaitStatistic -SqlInstance sql2008
            $output
            foreach ($row in ($output | Sort-Object -Unique Url)) { Start-Process ($row).Url }

            Displays the output then loads the associated sqlskills website for each result. Opens one tab per unique URL.
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [int]$Threshold = 95,
        [switch]$IncludeIgnorable,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {

        $details = [pscustomobject]@{
            CXPACKET                         = "This indicates parallelism, not necessarily that there's a problem. The coordinator thread in a parallel query always accumulates these waits. If the parallel threads are not given equal amounts of work to do, or one thread blocks, the waiting threads will also accumulate CXPACKET waits, which will make them aggregate a lot faster - this is a problem. One thread may have a lot more to do than the others, and so the whole query is blocked while the long-running thread completes. If this is combined with a high number of PAGEIOLATCH_XX waits, it could be large parallel table scans going on because of incorrect non-clustered indexes, or a bad query plan. If neither of these are the issue, you might want to try setting MAXDOP to 4, 2, or 1 for the offending queries (or possibly the whole instance). Make sure that if you have a NUMA system that you try setting MAXDOP to the number of cores in a single NUMA node first to see if that helps the problem. You also need to consider the MAXDOP effect on a mixed-load system. Play with the cost threshold for parallelism setting (bump it up to, say, 25) before reducing the MAXDOP of the whole instance. And don't forget Resource Governor in Enterprise Edition of  SQL Server 2008 onward that allows DOP governing for a particular group of connections to the server."
            PAGEIOLATCH_XX                   = "This is where SQL Server is waiting for a data page to be read from disk into memory. It may indicate a bottleneck at the IO subsystem level (which is a common knee-jerk response to seeing these), but why is the I/O subsystem having to service so many reads? It could be buffer pool/memory pressure (i.e. not enough memory for the workload), a sudden change in query plans causing a large parallel scan instead of a seek, plan cache bloat, or a number of other things. Don't assume the root cause is the I/O subsystem."
            ASYNC_NETWORK_IO                 = "This is usually where SQL Server is waiting for a client to finish consuming data. It could be that the client has asked for a very large amount of data or just that it's consuming it reeeeeally slowly because of poor programming – I rarely see this being a network issue. Clients often process one row at a time – called RBAR or Row-By-Agonizing-Row – instead of caching the data on the client and acknowledging to SQL Server immediately."
            WRITELOG                         = "This is the log management system waiting for a log flush to disk. It commonly indicates that the I/O subsystem can't keep up with the log flush volume, but on very high-volume systems it could also be caused by internal log flush limits, that may mean you have to split your workload over multiple databases or even make your transactions a little longer to reduce log flushes. To be sure it is the I/O subsystem, use the DMV sys.dm_io_virtual_file_stats to examine the I/O latency for the log file and see if it correlates to the average WRITELOG time. If WRITELOG is longer, you've got internal contention and need to shard. If not, investigate why you're creating so much transaction log."
            BROKER_RECEIVE_WAITFOR           = "This is just Service Broker waiting around for new messages to receive. I would add this to the list of waits to filter out and re-run the wait stats query."
            MSQL_XP                          = "This is SQL Server waiting for an extended stored-proc to finish. This could indicate a problem in your XP code."
            OLEDB                            = "As its name suggests, this is a wait for something communicating using OLEDB – e.g. a linked server. However, OLEDB is also used by all DMVs and by DBCC CHECKDB, so don't assume linked servers are the problem – it could be a third-party monitoring tool making excessive DMV calls. If it *is* a linked server (wait times in the 10s or 100s of milliseconds), go to the linked server and do wait stats analysis there to figure out what the performance issue is there."
            BACKUPIO                         = "This can show up when you're backing up to a slow I/O subsystem, like directly to tape, which is slooooow, or over a network."
            LCK_M_XX                         = "This is simply the thread waiting for a lock to be granted and indicates blocking problems. These could be caused by unwanted lock escalation or bad programming, but could also be from I/Os taking a long time causing locks to be held for longer than usual. Look at the resource associated with the lock using the DMV sys.dm_os_waiting_tasks. Don't assume that locking is the root cause."
            ONDEMAND_TASK_QUEUE              = "This is normal and is part of the background task system (e.g. deferred drop, ghost cleanup).  I would add this to the list of waits to filter out and re-run the wait stats query."
            BACKUPBUFFER                     = "This commonly show up with BACKUPIO and is a backup thread waiting for a buffer to write backup data into."
            IO_COMPLETION                    = "This is SQL Server waiting for non-data page I/Os to complete and could be an indication that the I/O subsystem is overloaded if the latencies look high (see Are I/O latencies killing your performance?)"
            SOS_SCHEDULER_YIELD              = "This is code running that doesn't hit any resource waits."
            DBMIRROR_EVENTS_QUEUE            = "These two are database mirroring just sitting around waiting for something to do. I would add these to the list of waits to filter out and re-run the wait stats query."
            DBMIRRORING_CMD                  = "These two are database mirroring just sitting around waiting for something to do. I would add these to the list of waits to filter out and re-run the wait stats query."
            PAGELATCH_XX                     = "This is contention for access to in-memory copies of pages. The most well-known cases of these are the PFS and SGAM contention that can occur in tempdb under certain workloads. To find out what page the contention is on, you'll need to use the DMV sys.dm_os_waiting_tasks to figure out what page the latch is for. For tempdb issues, Robert Davis (blog | twitter) has a good post showing how to do this. Another common cause I've seen is an index hot-spot with concurrent inserts into an index with an identity value key."
            LATCH_XX                         = "This is contention for some non-page structure inside SQL Server – so not related to I/O or data at all. These can be hard to figure out and you're going to be using the DMV sys.dm_os_latch_stats. More on this in my Latches category."
            PREEMPTIVE_OS_PIPEOPS            = "This is SQL Server switching to preemptive scheduling mode to call out to Windows for something, and this particular wait is usually from using xp_cmdshell. These were added for 2008 and aren't documented anywhere except through the links to my waits library."
            THREADPOOL                       = "This says that there aren't enough worker threads on the system to satisfy demand. Commonly this is large numbers of high-DOP queries trying to execute and taking all the threads from the thread pool."
            BROKER_TRANSMITTER               = "This is just Service Broker waiting around for new messages to send. I would add this to the list of waits to filter out and re-run the wait stats query."
            SQLTRACE_WAIT_ENTRIES            = "Part of SQL Trace. I would add this to the list of waits to filter out and re-run the wait stats query."
            DBMIRROR_DBM_MUTEX               = "This one is undocumented and is contention for the send buffer that database mirroring shares between all the mirroring sessions on a server. It could indicate that you've got too many mirroring sessions."
            RESOURCE_SEMAPHORE               = "This is queries waiting for execution memory (the memory used to process the query operators – like a sort). This could be memory pressure or a very high concurrent workload."
            PREEMPTIVE_OS_AUTHENTICATIONOPS  = "These are SQL Server switching to preemptive scheduling mode to call out to Windows for something. These were added for 2008 and aren't documented anywhere except through the links to my waits library."
            PREEMPTIVE_OS_GENERICOPS         = "These are SQL Server switching to preemptive scheduling mode to call out to Windows for something. These were added for 2008 and aren't documented anywhere except through the links to my waits library."
            SLEEP_BPOOL_FLUSH                = "This is normal to see and indicates that checkpoint is throttling itself to avoid overloading the IO subsystem. I would add this to the list of waits to filter out and re-run the wait stats query."
            MSQL_DQ                          = "This is SQL Server waiting for a distributed query to finish. This could indicate a problem with the distributed query, or it could just be normal."
            RESOURCE_SEMAPHORE_QUERY_COMPILE = "When there are too many concurrent query compilations going on, SQL Server will throttle them. I don't remember the threshold, but this can indicate excessive recompilation, or maybe single-use plans."
            DAC_INIT                         = "This is the Dedicated Admin Connection initializing."
            MSSEARCH                         = "This is normal to see for full-text operations.  If this is the highest wait, it could mean your system is spending most of its time doing full-text queries. You might want to consider adding this to the filter list."
            PREEMPTIVE_OS_FILEOPS            = "These are SQL Server switching to preemptive scheduling mode to call out to Windows for something. These were added for 2008 and aren't documented anywhere except through the links to my waits library."
            PREEMPTIVE_OS_LIBRARYOPS         = "These are SQL Server switching to preemptive scheduling mode to call out to Windows for something. These were added for 2008 and aren't documented anywhere except through the links to my waits library."
            PREEMPTIVE_OS_LOOKUPACCOUNTSID   = "These are SQL Server switching to preemptive scheduling mode to call out to Windows for something. These were added for 2008 and aren't documented anywhere except through the links to my waits library."
            PREEMPTIVE_OS_QUERYREGISTRY      = "These are SQL Server switching to preemptive scheduling mode to call out to Windows for something. These were added for 2008 and aren't documented anywhere except through the links to my waits library."
            SQLTRACE_LOCK                    = "Part of SQL Trace. I would add this to the list of waits to filter out and re-run the wait stats query."
        }

        # Thanks Brentg Ozar via https://gist.github.com/BrentOzar/42e82ee0603a1917c17d74c3fca26d34
        # Thanks Marcin Gminski‏ via https://www.dropbox.com/s/x3zr7u18tc1ojey/WaitStats.sql?dl=0

        $category = [pscustomobject]@{
            ASYNC_IO_COMPLETION                             = 'Other Disk IO'
            ASYNC_NETWORK_IO                                = 'Network IO'
            BACKUPIO                                        = 'Other Disk IO'
            BROKER_CONNECTION_RECEIVE_TASK                  = 'Service Broker'
            BROKER_DISPATCHER                               = 'Service Broker'
            BROKER_ENDPOINT_STATE_MUTEX                     = 'Service Broker'
            BROKER_EVENTHANDLER                             = 'Service Broker'
            BROKER_FORWARDER                                = 'Service Broker'
            BROKER_INIT                                     = 'Service Broker'
            BROKER_MASTERSTART                              = 'Service Broker'
            BROKER_RECEIVE_WAITFOR                          = 'User Wait'
            BROKER_REGISTERALLENDPOINTS                     = 'Service Broker'
            BROKER_SERVICE                                  = 'Service Broker'
            BROKER_SHUTDOWN                                 = 'Service Broker'
            BROKER_START                                    = 'Service Broker'
            BROKER_TASK_SHUTDOWN                            = 'Service Broker'
            BROKER_TASK_STOP                                = 'Service Broker'
            BROKER_TASK_SUBMIT                              = 'Service Broker'
            BROKER_TO_FLUSH                                 = 'Service Broker'
            BROKER_TRANSMISSION_OBJECT                      = 'Service Broker'
            BROKER_TRANSMISSION_TABLE                       = 'Service Broker'
            BROKER_TRANSMISSION_WORK                        = 'Service Broker'
            BROKER_TRANSMITTER                              = 'Service Broker'
            CHECKPOINT_QUEUE                                = 'Idle'
            CHKPT                                           = 'Tran Log IO'
            CLR_AUTO_EVENT                                  = 'SQL CLR'
            CLR_CRST                                        = 'SQL CLR'
            CLR_JOIN                                        = 'SQL CLR'
            CLR_MANUAL_EVENT                                = 'SQL CLR'
            CLR_MEMORY_SPY                                  = 'SQL CLR'
            CLR_MONITOR                                     = 'SQL CLR'
            CLR_RWLOCK_READER                               = 'SQL CLR'
            CLR_RWLOCK_WRITER                               = 'SQL CLR'
            CLR_SEMAPHORE                                   = 'SQL CLR'
            CLR_TASK_START                                  = 'SQL CLR'
            CLRHOST_STATE_ACCESS                            = 'SQL CLR'
            CMEMPARTITIONED                                 = 'Memory'
            CMEMTHREAD                                      = 'Memory'
            CXPACKET                                        = 'Parallelism'
            DBMIRROR_DBM_EVENT                              = 'Mirroring'
            DBMIRROR_DBM_MUTEX                              = 'Mirroring'
            DBMIRROR_EVENTS_QUEUE                           = 'Mirroring'
            DBMIRROR_SEND                                   = 'Mirroring'
            DBMIRROR_WORKER_QUEUE                           = 'Mirroring'
            DBMIRRORING_CMD                                 = 'Mirroring'
            DTC                                             = 'Transaction'
            DTC_ABORT_REQUEST                               = 'Transaction'
            DTC_RESOLVE                                     = 'Transaction'
            DTC_STATE                                       = 'Transaction'
            DTC_TMDOWN_REQUEST                              = 'Transaction'
            DTC_WAITFOR_OUTCOME                             = 'Transaction'
            DTCNEW_ENLIST                                   = 'Transaction'
            DTCNEW_PREPARE                                  = 'Transaction'
            DTCNEW_RECOVERY                                 = 'Transaction'
            DTCNEW_TM                                       = 'Transaction'
            DTCNEW_TRANSACTION_ENLISTMENT                   = 'Transaction'
            DTCPNTSYNC                                      = 'Transaction'
            EE_PMOLOCK                                      = 'Memory'
            EXCHANGE                                        = 'Parallelism'
            EXTERNAL_SCRIPT_NETWORK_IOF                     = 'Network IO'
            FCB_REPLICA_READ                                = 'Replication'
            FCB_REPLICA_WRITE                               = 'Replication'
            FT_COMPROWSET_RWLOCK                            = 'Full Text Search'
            FT_IFTS_RWLOCK                                  = 'Full Text Search'
            FT_IFTS_SCHEDULER_IDLE_WAIT                     = 'Idle'
            FT_IFTSHC_MUTEX                                 = 'Full Text Search'
            FT_IFTSISM_MUTEX                                = 'Full Text Search'
            FT_MASTER_MERGE                                 = 'Full Text Search'
            FT_MASTER_MERGE_COORDINATOR                     = 'Full Text Search'
            FT_METADATA_MUTEX                               = 'Full Text Search'
            FT_PROPERTYLIST_CACHE                           = 'Full Text Search'
            FT_RESTART_CRAWL                                = 'Full Text Search'
            'FULLTEXT GATHERER'                             = 'Full Text Search'
            HADR_AG_MUTEX                                   = 'Replication'
            HADR_AR_CRITICAL_SECTION_ENTRY                  = 'Replication'
            HADR_AR_MANAGER_MUTEX                           = 'Replication'
            HADR_AR_UNLOAD_COMPLETED                        = 'Replication'
            HADR_ARCONTROLLER_NOTIFICATIONS_SUBSCRIBER_LIST = 'Replication'
            HADR_BACKUP_BULK_LOCK                           = 'Replication'
            HADR_BACKUP_QUEUE                               = 'Replication'
            HADR_CLUSAPI_CALL                               = 'Replication'
            HADR_COMPRESSED_CACHE_SYNC                      = 'Replication'
            HADR_CONNECTIVITY_INFO                          = 'Replication'
            HADR_DATABASE_FLOW_CONTROL                      = 'Replication'
            HADR_DATABASE_VERSIONING_STATE                  = 'Replication'
            HADR_DATABASE_WAIT_FOR_RECOVERY                 = 'Replication'
            HADR_DATABASE_WAIT_FOR_RESTART                  = 'Replication'
            HADR_DATABASE_WAIT_FOR_TRANSITION_TO_VERSIONING = 'Replication'
            HADR_DB_COMMAND                                 = 'Replication'
            HADR_DB_OP_COMPLETION_SYNC                      = 'Replication'
            HADR_DB_OP_START_SYNC                           = 'Replication'
            HADR_DBR_SUBSCRIBER                             = 'Replication'
            HADR_DBR_SUBSCRIBER_FILTER_LIST                 = 'Replication'
            HADR_DBSEEDING                                  = 'Replication'
            HADR_DBSEEDING_LIST                             = 'Replication'
            HADR_DBSTATECHANGE_SYNC                         = 'Replication'
            HADR_FABRIC_CALLBACK                            = 'Replication'
            HADR_FILESTREAM_BLOCK_FLUSH                     = 'Replication'
            HADR_FILESTREAM_FILE_CLOSE                      = 'Replication'
            HADR_FILESTREAM_FILE_REQUEST                    = 'Replication'
            HADR_FILESTREAM_IOMGR                           = 'Replication'
            HADR_FILESTREAM_IOMGR_IOCOMPLETION              = 'Replication'
            HADR_FILESTREAM_MANAGER                         = 'Replication'
            HADR_FILESTREAM_PREPROC                         = 'Replication'
            HADR_GROUP_COMMIT                               = 'Replication'
            HADR_LOGCAPTURE_SYNC                            = 'Replication'
            HADR_LOGCAPTURE_WAIT                            = 'Replication'
            HADR_LOGPROGRESS_SYNC                           = 'Replication'
            HADR_NOTIFICATION_DEQUEUE                       = 'Replication'
            HADR_NOTIFICATION_WORKER_EXCLUSIVE_ACCESS       = 'Replication'
            HADR_NOTIFICATION_WORKER_STARTUP_SYNC           = 'Replication'
            HADR_NOTIFICATION_WORKER_TERMINATION_SYNC       = 'Replication'
            HADR_PARTNER_SYNC                               = 'Replication'
            HADR_READ_ALL_NETWORKS                          = 'Replication'
            HADR_RECOVERY_WAIT_FOR_CONNECTION               = 'Replication'
            HADR_RECOVERY_WAIT_FOR_UNDO                     = 'Replication'
            HADR_REPLICAINFO_SYNC                           = 'Replication'
            HADR_SEEDING_CANCELLATION                       = 'Replication'
            HADR_SEEDING_FILE_LIST                          = 'Replication'
            HADR_SEEDING_LIMIT_BACKUPS                      = 'Replication'
            HADR_SEEDING_SYNC_COMPLETION                    = 'Replication'
            HADR_SEEDING_TIMEOUT_TASK                       = 'Replication'
            HADR_SEEDING_WAIT_FOR_COMPLETION                = 'Replication'
            HADR_SYNC_COMMIT                                = 'Replication'
            HADR_SYNCHRONIZING_THROTTLE                     = 'Replication'
            HADR_TDS_LISTENER_SYNC                          = 'Replication'
            HADR_TDS_LISTENER_SYNC_PROCESSING               = 'Replication'
            HADR_THROTTLE_LOG_RATE_GOVERNOR                 = 'Log Rate Governor'
            HADR_TIMER_TASK                                 = 'Replication'
            HADR_TRANSPORT_DBRLIST                          = 'Replication'
            HADR_TRANSPORT_FLOW_CONTROL                     = 'Replication'
            HADR_TRANSPORT_SESSION                          = 'Replication'
            HADR_WORK_POOL                                  = 'Replication'
            HADR_WORK_QUEUE                                 = 'Replication'
            HADR_XRF_STACK_ACCESS                           = 'Replication'
            INSTANCE_LOG_RATE_GOVERNOR                      = 'Log Rate Governor'
            IO_COMPLETION                                   = 'Other Disk IO'
            IO_QUEUE_LIMIT                                  = 'Other Disk IO'
            IO_RETRY                                        = 'Other Disk IO'
            LATCH_DT                                        = 'Latch'
            LATCH_EX                                        = 'Latch'
            LATCH_KP                                        = 'Latch'
            LATCH_NL                                        = 'Latch'
            LATCH_SH                                        = 'Latch'
            LATCH_UP                                        = 'Latch'
            LAZYWRITER_SLEEP                                = 'Idle'
            LCK_M_BU                                        = 'Lock'
            LCK_M_BU_ABORT_BLOCKERS                         = 'Lock'
            LCK_M_BU_LOW_PRIORITY                           = 'Lock'
            LCK_M_IS                                        = 'Lock'
            LCK_M_IS_ABORT_BLOCKERS                         = 'Lock'
            LCK_M_IS_LOW_PRIORITY                           = 'Lock'
            LCK_M_IU                                        = 'Lock'
            LCK_M_IU_ABORT_BLOCKERS                         = 'Lock'
            LCK_M_IU_LOW_PRIORITY                           = 'Lock'
            LCK_M_IX                                        = 'Lock'
            LCK_M_IX_ABORT_BLOCKERS                         = 'Lock'
            LCK_M_IX_LOW_PRIORITY                           = 'Lock'
            LCK_M_RIn_NL                                    = 'Lock'
            LCK_M_RIn_NL_ABORT_BLOCKERS                     = 'Lock'
            LCK_M_RIn_NL_LOW_PRIORITY                       = 'Lock'
            LCK_M_RIn_S                                     = 'Lock'
            LCK_M_RIn_S_ABORT_BLOCKERS                      = 'Lock'
            LCK_M_RIn_S_LOW_PRIORITY                        = 'Lock'
            LCK_M_RIn_U                                     = 'Lock'
            LCK_M_RIn_U_ABORT_BLOCKERS                      = 'Lock'
            LCK_M_RIn_U_LOW_PRIORITY                        = 'Lock'
            LCK_M_RIn_X                                     = 'Lock'
            LCK_M_RIn_X_ABORT_BLOCKERS                      = 'Lock'
            LCK_M_RIn_X_LOW_PRIORITY                        = 'Lock'
            LCK_M_RS_S                                      = 'Lock'
            LCK_M_RS_S_ABORT_BLOCKERS                       = 'Lock'
            LCK_M_RS_S_LOW_PRIORITY                         = 'Lock'
            LCK_M_RS_U                                      = 'Lock'
            LCK_M_RS_U_ABORT_BLOCKERS                       = 'Lock'
            LCK_M_RS_U_LOW_PRIORITY                         = 'Lock'
            LCK_M_RX_S                                      = 'Lock'
            LCK_M_RX_S_ABORT_BLOCKERS                       = 'Lock'
            LCK_M_RX_S_LOW_PRIORITY                         = 'Lock'
            LCK_M_RX_U                                      = 'Lock'
            LCK_M_RX_U_ABORT_BLOCKERS                       = 'Lock'
            LCK_M_RX_U_LOW_PRIORITY                         = 'Lock'
            LCK_M_RX_X                                      = 'Lock'
            LCK_M_RX_X_ABORT_BLOCKERS                       = 'Lock'
            LCK_M_RX_X_LOW_PRIORITY                         = 'Lock'
            LCK_M_S                                         = 'Lock'
            LCK_M_S_ABORT_BLOCKERS                          = 'Lock'
            LCK_M_S_LOW_PRIORITY                            = 'Lock'
            LCK_M_SCH_M                                     = 'Lock'
            LCK_M_SCH_M_ABORT_BLOCKERS                      = 'Lock'
            LCK_M_SCH_M_LOW_PRIORITY                        = 'Lock'
            LCK_M_SCH_S                                     = 'Lock'
            LCK_M_SCH_S_ABORT_BLOCKERS                      = 'Lock'
            LCK_M_SCH_S_LOW_PRIORITY                        = 'Lock'
            LCK_M_SIU                                       = 'Lock'
            LCK_M_SIU_ABORT_BLOCKERS                        = 'Lock'
            LCK_M_SIU_LOW_PRIORITY                          = 'Lock'
            LCK_M_SIX                                       = 'Lock'
            LCK_M_SIX_ABORT_BLOCKERS                        = 'Lock'
            LCK_M_SIX_LOW_PRIORITY                          = 'Lock'
            LCK_M_U                                         = 'Lock'
            LCK_M_U_ABORT_BLOCKERS                          = 'Lock'
            LCK_M_U_LOW_PRIORITY                            = 'Lock'
            LCK_M_UIX                                       = 'Lock'
            LCK_M_UIX_ABORT_BLOCKERS                        = 'Lock'
            LCK_M_UIX_LOW_PRIORITY                          = 'Lock'
            LCK_M_X                                         = 'Lock'
            LCK_M_X_ABORT_BLOCKERS                          = 'Lock'
            LCK_M_X_LOW_PRIORITY                            = 'Lock'
            LOGBUFFER                                       = 'Tran Log IO'
            LOGMGR                                          = 'Tran Log IO'
            LOGMGR_FLUSH                                    = 'Tran Log IO'
            LOGMGR_PMM_LOG                                  = 'Tran Log IO'
            LOGMGR_QUEUE                                    = 'Idle'
            LOGMGR_RESERVE_APPEND                           = 'Tran Log IO'
            MEMORY_ALLOCATION_EXT                           = 'Memory'
            MEMORY_GRANT_UPDATE                             = 'Memory'
            MSQL_XACT_MGR_MUTEX                             = 'Transaction'
            MSQL_XACT_MUTEX                                 = 'Transaction'
            MSSEARCH                                        = 'Full Text Search'
            NET_WAITFOR_PACKET                              = 'Network IO'
            ONDEMAND_TASK_QUEUE                             = 'Idle'
            PAGEIOLATCH_DT                                  = 'Buffer IO'
            PAGEIOLATCH_EX                                  = 'Buffer IO'
            PAGEIOLATCH_KP                                  = 'Buffer IO'
            PAGEIOLATCH_NL                                  = 'Buffer IO'
            PAGEIOLATCH_SH                                  = 'Buffer IO'
            PAGEIOLATCH_UP                                  = 'Buffer IO'
            PAGELATCH_DT                                    = 'Buffer Latch'
            PAGELATCH_EX                                    = 'Buffer Latch'
            PAGELATCH_KP                                    = 'Buffer Latch'
            PAGELATCH_NL                                    = 'Buffer Latch'
            PAGELATCH_SH                                    = 'Buffer Latch'
            PAGELATCH_UP                                    = 'Buffer Latch'
            POOL_LOG_RATE_GOVERNOR                          = 'Log Rate Governor'
            PREEMPTIVE_ABR                                  = 'Preemptive'
            PREEMPTIVE_CLOSEBACKUPMEDIA                     = 'Preemptive'
            PREEMPTIVE_CLOSEBACKUPTAPE                      = 'Preemptive'
            PREEMPTIVE_CLOSEBACKUPVDIDEVICE                 = 'Preemptive'
            PREEMPTIVE_CLUSAPI_CLUSTERRESOURCECONTROL       = 'Preemptive'
            PREEMPTIVE_COM_COCREATEINSTANCE                 = 'Preemptive'
            PREEMPTIVE_COM_COGETCLASSOBJECT                 = 'Preemptive'
            PREEMPTIVE_COM_CREATEACCESSOR                   = 'Preemptive'
            PREEMPTIVE_COM_DELETEROWS                       = 'Preemptive'
            PREEMPTIVE_COM_GETCOMMANDTEXT                   = 'Preemptive'
            PREEMPTIVE_COM_GETDATA                          = 'Preemptive'
            PREEMPTIVE_COM_GETNEXTROWS                      = 'Preemptive'
            PREEMPTIVE_COM_GETRESULT                        = 'Preemptive'
            PREEMPTIVE_COM_GETROWSBYBOOKMARK                = 'Preemptive'
            PREEMPTIVE_COM_LBFLUSH                          = 'Preemptive'
            PREEMPTIVE_COM_LBLOCKREGION                     = 'Preemptive'
            PREEMPTIVE_COM_LBREADAT                         = 'Preemptive'
            PREEMPTIVE_COM_LBSETSIZE                        = 'Preemptive'
            PREEMPTIVE_COM_LBSTAT                           = 'Preemptive'
            PREEMPTIVE_COM_LBUNLOCKREGION                   = 'Preemptive'
            PREEMPTIVE_COM_LBWRITEAT                        = 'Preemptive'
            PREEMPTIVE_COM_QUERYINTERFACE                   = 'Preemptive'
            PREEMPTIVE_COM_RELEASE                          = 'Preemptive'
            PREEMPTIVE_COM_RELEASEACCESSOR                  = 'Preemptive'
            PREEMPTIVE_COM_RELEASEROWS                      = 'Preemptive'
            PREEMPTIVE_COM_RELEASESESSION                   = 'Preemptive'
            PREEMPTIVE_COM_RESTARTPOSITION                  = 'Preemptive'
            PREEMPTIVE_COM_SEQSTRMREAD                      = 'Preemptive'
            PREEMPTIVE_COM_SEQSTRMREADANDWRITE              = 'Preemptive'
            PREEMPTIVE_COM_SETDATAFAILURE                   = 'Preemptive'
            PREEMPTIVE_COM_SETPARAMETERINFO                 = 'Preemptive'
            PREEMPTIVE_COM_SETPARAMETERPROPERTIES           = 'Preemptive'
            PREEMPTIVE_COM_STRMLOCKREGION                   = 'Preemptive'
            PREEMPTIVE_COM_STRMSEEKANDREAD                  = 'Preemptive'
            PREEMPTIVE_COM_STRMSEEKANDWRITE                 = 'Preemptive'
            PREEMPTIVE_COM_STRMSETSIZE                      = 'Preemptive'
            PREEMPTIVE_COM_STRMSTAT                         = 'Preemptive'
            PREEMPTIVE_COM_STRMUNLOCKREGION                 = 'Preemptive'
            PREEMPTIVE_CONSOLEWRITE                         = 'Preemptive'
            PREEMPTIVE_CREATEPARAM                          = 'Preemptive'
            PREEMPTIVE_DEBUG                                = 'Preemptive'
            PREEMPTIVE_DFSADDLINK                           = 'Preemptive'
            PREEMPTIVE_DFSLINKEXISTCHECK                    = 'Preemptive'
            PREEMPTIVE_DFSLINKHEALTHCHECK                   = 'Preemptive'
            PREEMPTIVE_DFSREMOVELINK                        = 'Preemptive'
            PREEMPTIVE_DFSREMOVEROOT                        = 'Preemptive'
            PREEMPTIVE_DFSROOTFOLDERCHECK                   = 'Preemptive'
            PREEMPTIVE_DFSROOTINIT                          = 'Preemptive'
            PREEMPTIVE_DFSROOTSHARECHECK                    = 'Preemptive'
            PREEMPTIVE_DTC_ABORT                            = 'Preemptive'
            PREEMPTIVE_DTC_ABORTREQUESTDONE                 = 'Preemptive'
            PREEMPTIVE_DTC_BEGINTRANSACTION                 = 'Preemptive'
            PREEMPTIVE_DTC_COMMITREQUESTDONE                = 'Preemptive'
            PREEMPTIVE_DTC_ENLIST                           = 'Preemptive'
            PREEMPTIVE_DTC_PREPAREREQUESTDONE               = 'Preemptive'
            PREEMPTIVE_FILESIZEGET                          = 'Preemptive'
            PREEMPTIVE_FSAOLEDB_ABORTTRANSACTION            = 'Preemptive'
            PREEMPTIVE_FSAOLEDB_COMMITTRANSACTION           = 'Preemptive'
            PREEMPTIVE_FSAOLEDB_STARTTRANSACTION            = 'Preemptive'
            PREEMPTIVE_FSRECOVER_UNCONDITIONALUNDO          = 'Preemptive'
            PREEMPTIVE_GETRMINFO                            = 'Preemptive'
            PREEMPTIVE_HADR_LEASE_MECHANISM                 = 'Preemptive'
            PREEMPTIVE_HTTP_EVENT_WAIT                      = 'Preemptive'
            PREEMPTIVE_HTTP_REQUEST                         = 'Preemptive'
            PREEMPTIVE_LOCKMONITOR                          = 'Preemptive'
            PREEMPTIVE_MSS_RELEASE                          = 'Preemptive'
            PREEMPTIVE_ODBCOPS                              = 'Preemptive'
            PREEMPTIVE_OLE_UNINIT                           = 'Preemptive'
            PREEMPTIVE_OLEDB_ABORTORCOMMITTRAN              = 'Preemptive'
            PREEMPTIVE_OLEDB_ABORTTRAN                      = 'Preemptive'
            PREEMPTIVE_OLEDB_GETDATASOURCE                  = 'Preemptive'
            PREEMPTIVE_OLEDB_GETLITERALINFO                 = 'Preemptive'
            PREEMPTIVE_OLEDB_GETPROPERTIES                  = 'Preemptive'
            PREEMPTIVE_OLEDB_GETPROPERTYINFO                = 'Preemptive'
            PREEMPTIVE_OLEDB_GETSCHEMALOCK                  = 'Preemptive'
            PREEMPTIVE_OLEDB_JOINTRANSACTION                = 'Preemptive'
            PREEMPTIVE_OLEDB_RELEASE                        = 'Preemptive'
            PREEMPTIVE_OLEDB_SETPROPERTIES                  = 'Preemptive'
            PREEMPTIVE_OLEDBOPS                             = 'Preemptive'
            PREEMPTIVE_OS_ACCEPTSECURITYCONTEXT             = 'Preemptive'
            PREEMPTIVE_OS_ACQUIRECREDENTIALSHANDLE          = 'Preemptive'
            PREEMPTIVE_OS_AUTHENTICATIONOPS                 = 'Preemptive'
            PREEMPTIVE_OS_AUTHORIZATIONOPS                  = 'Preemptive'
            PREEMPTIVE_OS_AUTHZGETINFORMATIONFROMCONTEXT    = 'Preemptive'
            PREEMPTIVE_OS_AUTHZINITIALIZECONTEXTFROMSID     = 'Preemptive'
            PREEMPTIVE_OS_AUTHZINITIALIZERESOURCEMANAGER    = 'Preemptive'
            PREEMPTIVE_OS_BACKUPREAD                        = 'Preemptive'
            PREEMPTIVE_OS_CLOSEHANDLE                       = 'Preemptive'
            PREEMPTIVE_OS_CLUSTEROPS                        = 'Preemptive'
            PREEMPTIVE_OS_COMOPS                            = 'Preemptive'
            PREEMPTIVE_OS_COMPLETEAUTHTOKEN                 = 'Preemptive'
            PREEMPTIVE_OS_COPYFILE                          = 'Preemptive'
            PREEMPTIVE_OS_CREATEDIRECTORY                   = 'Preemptive'
            PREEMPTIVE_OS_CREATEFILE                        = 'Preemptive'
            PREEMPTIVE_OS_CRYPTACQUIRECONTEXT               = 'Preemptive'
            PREEMPTIVE_OS_CRYPTIMPORTKEY                    = 'Preemptive'
            PREEMPTIVE_OS_CRYPTOPS                          = 'Preemptive'
            PREEMPTIVE_OS_DECRYPTMESSAGE                    = 'Preemptive'
            PREEMPTIVE_OS_DELETEFILE                        = 'Preemptive'
            PREEMPTIVE_OS_DELETESECURITYCONTEXT             = 'Preemptive'
            PREEMPTIVE_OS_DEVICEIOCONTROL                   = 'Preemptive'
            PREEMPTIVE_OS_DEVICEOPS                         = 'Preemptive'
            PREEMPTIVE_OS_DIRSVC_NETWORKOPS                 = 'Preemptive'
            PREEMPTIVE_OS_DISCONNECTNAMEDPIPE               = 'Preemptive'
            PREEMPTIVE_OS_DOMAINSERVICESOPS                 = 'Preemptive'
            PREEMPTIVE_OS_DSGETDCNAME                       = 'Preemptive'
            PREEMPTIVE_OS_DTCOPS                            = 'Preemptive'
            PREEMPTIVE_OS_ENCRYPTMESSAGE                    = 'Preemptive'
            PREEMPTIVE_OS_FILEOPS                           = 'Preemptive'
            PREEMPTIVE_OS_FINDFILE                          = 'Preemptive'
            PREEMPTIVE_OS_FLUSHFILEBUFFERS                  = 'Preemptive'
            PREEMPTIVE_OS_FORMATMESSAGE                     = 'Preemptive'
            PREEMPTIVE_OS_FREECREDENTIALSHANDLE             = 'Preemptive'
            PREEMPTIVE_OS_FREELIBRARY                       = 'Preemptive'
            PREEMPTIVE_OS_GENERICOPS                        = 'Preemptive'
            PREEMPTIVE_OS_GETADDRINFO                       = 'Preemptive'
            PREEMPTIVE_OS_GETCOMPRESSEDFILESIZE             = 'Preemptive'
            PREEMPTIVE_OS_GETDISKFREESPACE                  = 'Preemptive'
            PREEMPTIVE_OS_GETFILEATTRIBUTES                 = 'Preemptive'
            PREEMPTIVE_OS_GETFILESIZE                       = 'Preemptive'
            PREEMPTIVE_OS_GETFINALFILEPATHBYHANDLE          = 'Preemptive'
            PREEMPTIVE_OS_GETLONGPATHNAME                   = 'Preemptive'
            PREEMPTIVE_OS_GETPROCADDRESS                    = 'Preemptive'
            PREEMPTIVE_OS_GETVOLUMENAMEFORVOLUMEMOUNTPOINT  = 'Preemptive'
            PREEMPTIVE_OS_GETVOLUMEPATHNAME                 = 'Preemptive'
            PREEMPTIVE_OS_INITIALIZESECURITYCONTEXT         = 'Preemptive'
            PREEMPTIVE_OS_LIBRARYOPS                        = 'Preemptive'
            PREEMPTIVE_OS_LOADLIBRARY                       = 'Preemptive'
            PREEMPTIVE_OS_LOGONUSER                         = 'Preemptive'
            PREEMPTIVE_OS_LOOKUPACCOUNTSID                  = 'Preemptive'
            PREEMPTIVE_OS_MESSAGEQUEUEOPS                   = 'Preemptive'
            PREEMPTIVE_OS_MOVEFILE                          = 'Preemptive'
            PREEMPTIVE_OS_NETGROUPGETUSERS                  = 'Preemptive'
            PREEMPTIVE_OS_NETLOCALGROUPGETMEMBERS           = 'Preemptive'
            PREEMPTIVE_OS_NETUSERGETGROUPS                  = 'Preemptive'
            PREEMPTIVE_OS_NETUSERGETLOCALGROUPS             = 'Preemptive'
            PREEMPTIVE_OS_NETUSERMODALSGET                  = 'Preemptive'
            PREEMPTIVE_OS_NETVALIDATEPASSWORDPOLICY         = 'Preemptive'
            PREEMPTIVE_OS_NETVALIDATEPASSWORDPOLICYFREE     = 'Preemptive'
            PREEMPTIVE_OS_OPENDIRECTORY                     = 'Preemptive'
            PREEMPTIVE_OS_PDH_WMI_INIT                      = 'Preemptive'
            PREEMPTIVE_OS_PIPEOPS                           = 'Preemptive'
            PREEMPTIVE_OS_PROCESSOPS                        = 'Preemptive'
            PREEMPTIVE_OS_QUERYCONTEXTATTRIBUTES            = 'Preemptive'
            PREEMPTIVE_OS_QUERYREGISTRY                     = 'Preemptive'
            PREEMPTIVE_OS_QUERYSECURITYCONTEXTTOKEN         = 'Preemptive'
            PREEMPTIVE_OS_REMOVEDIRECTORY                   = 'Preemptive'
            PREEMPTIVE_OS_REPORTEVENT                       = 'Preemptive'
            PREEMPTIVE_OS_REVERTTOSELF                      = 'Preemptive'
            PREEMPTIVE_OS_RSFXDEVICEOPS                     = 'Preemptive'
            PREEMPTIVE_OS_SECURITYOPS                       = 'Preemptive'
            PREEMPTIVE_OS_SERVICEOPS                        = 'Preemptive'
            PREEMPTIVE_OS_SETENDOFFILE                      = 'Preemptive'
            PREEMPTIVE_OS_SETFILEPOINTER                    = 'Preemptive'
            PREEMPTIVE_OS_SETFILEVALIDDATA                  = 'Preemptive'
            PREEMPTIVE_OS_SETNAMEDSECURITYINFO              = 'Preemptive'
            PREEMPTIVE_OS_SQLCLROPS                         = 'Preemptive'
            PREEMPTIVE_OS_SQMLAUNCH                         = 'Preemptive'
            PREEMPTIVE_OS_VERIFYSIGNATURE                   = 'Preemptive'
            PREEMPTIVE_OS_VERIFYTRUST                       = 'Preemptive'
            PREEMPTIVE_OS_VSSOPS                            = 'Preemptive'
            PREEMPTIVE_OS_WAITFORSINGLEOBJECT               = 'Preemptive'
            PREEMPTIVE_OS_WINSOCKOPS                        = 'Preemptive'
            PREEMPTIVE_OS_WRITEFILE                         = 'Preemptive'
            PREEMPTIVE_OS_WRITEFILEGATHER                   = 'Preemptive'
            PREEMPTIVE_OS_WSASETLASTERROR                   = 'Preemptive'
            PREEMPTIVE_REENLIST                             = 'Preemptive'
            PREEMPTIVE_RESIZELOG                            = 'Preemptive'
            PREEMPTIVE_ROLLFORWARDREDO                      = 'Preemptive'
            PREEMPTIVE_ROLLFORWARDUNDO                      = 'Preemptive'
            PREEMPTIVE_SB_STOPENDPOINT                      = 'Preemptive'
            PREEMPTIVE_SERVER_STARTUP                       = 'Preemptive'
            PREEMPTIVE_SETRMINFO                            = 'Preemptive'
            PREEMPTIVE_SHAREDMEM_GETDATA                    = 'Preemptive'
            PREEMPTIVE_SNIOPEN                              = 'Preemptive'
            PREEMPTIVE_SOSHOST                              = 'Preemptive'
            PREEMPTIVE_SOSTESTING                           = 'Preemptive'
            PREEMPTIVE_SP_SERVER_DIAGNOSTICS                = 'Preemptive'
            PREEMPTIVE_STARTRM                              = 'Preemptive'
            PREEMPTIVE_STREAMFCB_CHECKPOINT                 = 'Preemptive'
            PREEMPTIVE_STREAMFCB_RECOVER                    = 'Preemptive'
            PREEMPTIVE_STRESSDRIVER                         = 'Preemptive'
            PREEMPTIVE_TESTING                              = 'Preemptive'
            PREEMPTIVE_TRANSIMPORT                          = 'Preemptive'
            PREEMPTIVE_UNMARSHALPROPAGATIONTOKEN            = 'Preemptive'
            PREEMPTIVE_VSS_CREATESNAPSHOT                   = 'Preemptive'
            PREEMPTIVE_VSS_CREATEVOLUMESNAPSHOT             = 'Preemptive'
            PREEMPTIVE_XE_CALLBACKEXECUTE                   = 'Preemptive'
            PREEMPTIVE_XE_CX_FILE_OPEN                      = 'Preemptive'
            PREEMPTIVE_XE_CX_HTTP_CALL                      = 'Preemptive'
            PREEMPTIVE_XE_DISPATCHER                        = 'Preemptive'
            PREEMPTIVE_XE_ENGINEINIT                        = 'Preemptive'
            PREEMPTIVE_XE_GETTARGETSTATE                    = 'Preemptive'
            PREEMPTIVE_XE_SESSIONCOMMIT                     = 'Preemptive'
            PREEMPTIVE_XE_TARGETFINALIZE                    = 'Preemptive'
            PREEMPTIVE_XE_TARGETINIT                        = 'Preemptive'
            PREEMPTIVE_XE_TIMERRUN                          = 'Preemptive'
            PREEMPTIVE_XETESTING                            = 'Preemptive'
            PWAIT_HADR_ACTION_COMPLETED                     = 'Replication'
            PWAIT_HADR_CHANGE_NOTIFIER_TERMINATION_SYNC     = 'Replication'
            PWAIT_HADR_CLUSTER_INTEGRATION                  = 'Replication'
            PWAIT_HADR_FAILOVER_COMPLETED                   = 'Replication'
            PWAIT_HADR_JOIN                                 = 'Replication'
            PWAIT_HADR_OFFLINE_COMPLETED                    = 'Replication'
            PWAIT_HADR_ONLINE_COMPLETED                     = 'Replication'
            PWAIT_HADR_POST_ONLINE_COMPLETED                = 'Replication'
            PWAIT_HADR_SERVER_READY_CONNECTIONS             = 'Replication'
            PWAIT_HADR_WORKITEM_COMPLETED                   = 'Replication'
            PWAIT_HADRSIM                                   = 'Replication'
            PWAIT_RESOURCE_SEMAPHORE_FT_PARALLEL_QUERY_SYNC = 'Full Text Search'
            QUERY_TRACEOUT                                  = 'Tracing'
            REPL_CACHE_ACCESS                               = 'Replication'
            REPL_HISTORYCACHE_ACCESS                        = 'Replication'
            REPL_SCHEMA_ACCESS                              = 'Replication'
            REPL_TRANFSINFO_ACCESS                          = 'Replication'
            REPL_TRANHASHTABLE_ACCESS                       = 'Replication'
            REPL_TRANTEXTINFO_ACCESS                        = 'Replication'
            REPLICA_WRITES                                  = 'Replication'
            REQUEST_FOR_DEADLOCK_SEARCH                     = 'Idle'
            RESERVED_MEMORY_ALLOCATION_EXT                  = 'Memory'
            RESOURCE_SEMAPHORE                              = 'Memory'
            RESOURCE_SEMAPHORE_QUERY_COMPILE                = 'Compilation'
            SLEEP_BPOOL_FLUSH                               = 'Idle'
            SLEEP_BUFFERPOOL_HELPLW                         = 'Idle'
            SLEEP_DBSTARTUP                                 = 'Idle'
            SLEEP_DCOMSTARTUP                               = 'Idle'
            SLEEP_MASTERDBREADY                             = 'Idle'
            SLEEP_MASTERMDREADY                             = 'Idle'
            SLEEP_MASTERUPGRADED                            = 'Idle'
            SLEEP_MEMORYPOOL_ALLOCATEPAGES                  = 'Idle'
            SLEEP_MSDBSTARTUP                               = 'Idle'
            SLEEP_RETRY_VIRTUALALLOC                        = 'Idle'
            SLEEP_SYSTEMTASK                                = 'Idle'
            SLEEP_TASK                                      = 'Idle'
            SLEEP_TEMPDBSTARTUP                             = 'Idle'
            SLEEP_WORKSPACE_ALLOCATEPAGE                    = 'Idle'
            SOS_SCHEDULER_YIELD                             = 'CPU'
            SQLCLR_APPDOMAIN                                = 'SQL CLR'
            SQLCLR_ASSEMBLY                                 = 'SQL CLR'
            SQLCLR_DEADLOCK_DETECTION                       = 'SQL CLR'
            SQLCLR_QUANTUM_PUNISHMENT                       = 'SQL CLR'
            SQLTRACE_BUFFER_FLUSH                           = 'Idle'
            SQLTRACE_FILE_BUFFER                            = 'Tracing'
            SQLTRACE_FILE_READ_IO_COMPLETION                = 'Tracing'
            SQLTRACE_FILE_WRITE_IO_COMPLETION               = 'Tracing'
            SQLTRACE_INCREMENTAL_FLUSH_SLEEP                = 'Idle'
            SQLTRACE_PENDING_BUFFER_WRITERS                 = 'Tracing'
            SQLTRACE_SHUTDOWN                               = 'Tracing'
            SQLTRACE_WAIT_ENTRIES                           = 'Idle'
            THREADPOOL                                      = 'Worker Thread'
            TRACE_EVTNOTIF                                  = 'Tracing'
            TRACEWRITE                                      = 'Tracing'
            TRAN_MARKLATCH_DT                               = 'Transaction'
            TRAN_MARKLATCH_EX                               = 'Transaction'
            TRAN_MARKLATCH_KP                               = 'Transaction'
            TRAN_MARKLATCH_NL                               = 'Transaction'
            TRAN_MARKLATCH_SH                               = 'Transaction'
            TRAN_MARKLATCH_UP                               = 'Transaction'
            TRANSACTION_MUTEX                               = 'Transaction'
            WAIT_FOR_RESULTS                                = 'User Wait'
            WAITFOR                                         = 'User Wait'
            WRITE_COMPLETION                                = 'Other Disk IO'
            WRITELOG                                        = 'Tran Log IO'
            XACT_OWN_TRANSACTION                            = 'Transaction'
            XACT_RECLAIM_SESSION                            = 'Transaction'
            XACTLOCKINFO                                    = 'Transaction'
            XACTWORKSPACE_MUTEX                             = 'Transaction'
            XE_DISPATCHER_WAIT                              = 'Idle'
            XE_TIMER_EVENT                                  = 'Idle'
            ABR                                             = 'Other'
            ASSEMBLY_LOAD                                   = 'SQLCLR'
            ASYNC_DISKPOOL_LOCK                             = 'Buffer I/O'
            BACKUP                                          = 'Backup'
            BACKUP_CLIENTLOCK                               = 'Backup'
            BACKUP_OPERATOR                                 = 'Backup'
            BACKUPBUFFER                                    = 'Backup'
            BACKUPTHREAD                                    = 'Backup'
            BAD_PAGE_PROCESS                                = 'Other'
            BUILTIN_HASHKEY_MUTEX                           = 'Other'
            CHECK_PRINT_RECORD                              = 'Other'
            CPU                                             = 'CPU'
            CURSOR                                          = 'Other'
            CURSOR_ASYNC                                    = 'Other'
            DAC_INIT                                        = 'Other'
            DBCC_COLUMN_TRANSLATION_CACHE                   = 'Other'
            DBTABLE                                         = 'Other'
            DEADLOCK_ENUM_MUTEX                             = 'Latch'
            DEADLOCK_TASK_SEARCH                            = 'Other'
            DEBUG                                           = 'Other'
            DISABLE_VERSIONING                              = 'Other'
            DISKIO_SUSPEND                                  = 'Backup'
            DLL_LOADING_MUTEX                               = 'Other'
            DROPTEMP                                        = 'Other'
            DUMP_LOG_COORDINATOR                            = 'Other'
            DUMP_LOG_COORDINATOR_QUEUE                      = 'Other'
            DUMPTRIGGER                                     = 'Other'
            EC                                              = 'Other'
            EE_SPECPROC_MAP_INIT                            = 'Other'
            ENABLE_VERSIONING                               = 'Other'
            ERROR_REPORTING_MANAGER                         = 'Other'
            EXECSYNC                                        = 'Parallelism'
            EXECUTION_PIPE_EVENT_INTERNAL                   = 'Other'
            FAILPOINT                                       = 'Other'
            FS_GARBAGE_COLLECTOR_SHUTDOWN                   = 'SQLCLR'
            FSAGENT                                         = 'Idle'
            FT_RESUME_CRAWL                                 = 'Other'
            GUARDIAN                                        = 'Other'
            HTTP_ENDPOINT_COLLCREATE                        = 'Other'
            HTTP_ENUMERATION                                = 'Other'
            HTTP_START                                      = 'Other'
            IMP_IMPORT_MUTEX                                = 'Other'
            IMPPROV_IOWAIT                                  = 'Other'
            INDEX_USAGE_STATS_MUTEX                         = 'Latch'
            INTERNAL_TESTING                                = 'Other'
            IO_AUDIT_MUTEX                                  = 'Other'
            KSOURCE_WAKEUP                                  = 'Idle'
            KTM_ENLISTMENT                                  = 'Other'
            KTM_RECOVERY_MANAGER                            = 'Other'
            KTM_RECOVERY_RESOLUTION                         = 'Other'
            LOWFAIL_MEMMGR_QUEUE                            = 'Memory'
            MIRROR_SEND_MESSAGE                             = 'Other'
            MISCELLANEOUS                                   = 'Other'
            MSQL_DQ                                         = 'Network I/O'
            MSQL_SYNC_PIPE                                  = 'Other'
            MSQL_XP                                         = 'Other'
            OLEDB                                           = 'Network I/O'
            PARALLEL_BACKUP_QUEUE                           = 'Other'
            PRINT_ROLLBACK_PROGRESS                         = 'Other'
            QNMANAGER_ACQUIRE                               = 'Other'
            QPJOB_KILL                                      = 'Other'
            QPJOB_WAITFOR_ABORT                             = 'Other'
            QRY_MEM_GRANT_INFO_MUTEX                        = 'Other'
            QUERY_ERRHDL_SERVICE_DONE                       = 'Other'
            QUERY_EXECUTION_INDEX_SORT_EVENT_OPEN           = 'Other'
            QUERY_NOTIFICATION_MGR_MUTEX                    = 'Other'
            QUERY_NOTIFICATION_SUBSCRIPTION_MUTEX           = 'Other'
            QUERY_NOTIFICATION_TABLE_MGR_MUTEX              = 'Other'
            QUERY_NOTIFICATION_UNITTEST_MUTEX               = 'Other'
            QUERY_OPTIMIZER_PRINT_MUTEX                     = 'Other'
            QUERY_REMOTE_BRICKS_DONE                        = 'Other'
            RECOVER_CHANGEDB                                = 'Other'
            REQUEST_DISPENSER_PAUSE                         = 'Other'
            RESOURCE_QUEUE                                  = 'Idle'
            RESOURCE_SEMAPHORE_MUTEX                        = 'Compilation'
            RESOURCE_SEMAPHORE_SMALL_QUERY                  = 'Compilation'
            SEC_DROP_TEMP_KEY                               = 'Other'
            SEQUENTIAL_GUID                                 = 'Other'
            SERVER_IDLE_CHECK                               = 'Idle'
            SHUTDOWN                                        = 'Other'
            SNI_CRITICAL_SECTION                            = 'Other'
            SNI_HTTP_ACCEPT                                 = 'Idle'
            SNI_HTTP_WAITFOR_0_DISCON                       = 'Other'
            SNI_LISTENER_ACCESS                             = 'Other'
            SNI_TASK_COMPLETION                             = 'Other'
            SOAP_READ                                       = 'Full Text Search'
            SOAP_WRITE                                      = 'Full Text Search'
            SOS_CALLBACK_REMOVAL                            = 'Other'
            SOS_DISPATCHER_MUTEX                            = 'Other'
            SOS_LOCALALLOCATORLIST                          = 'Other'
            SOS_OBJECT_STORE_DESTROY_MUTEX                  = 'Other'
            SOS_PROCESS_AFFINITY_MUTEX                      = 'Other'
            SOS_RESERVEDMEMBLOCKLIST                        = 'Memory'
            SOS_STACKSTORE_INIT_MUTEX                       = 'Other'
            SOS_SYNC_TASK_ENQUEUE_EVENT                     = 'Other'
            SOS_VIRTUALMEMORY_LOW                           = 'Memory'
            SOSHOST_EVENT                                   = 'Other'
            SOSHOST_INTERNAL                                = 'Other'
            SOSHOST_MUTEX                                   = 'Other'
            SOSHOST_RWLOCK                                  = 'Other'
            SOSHOST_SEMAPHORE                               = 'Other'
            SOSHOST_SLEEP                                   = 'Other'
            SOSHOST_TRACELOCK                               = 'Other'
            SOSHOST_WAITFORDONE                             = 'Other'
            SQLSORT_NORMMUTEX                               = 'Other'
            SQLSORT_SORTMUTEX                               = 'Other'
            SQLTRACE_LOCK                                   = 'Other'
            SRVPROC_SHUTDOWN                                = 'Other'
            TEMPOBJ                                         = 'Other'
            TIMEPRIV_TIMEPERIOD                             = 'Other'
            UTIL_PAGE_ALLOC                                 = 'Memory'
            VIA_ACCEPT                                      = 'Other'
            VIEW_DEFINITION_MUTEX                           = 'Latch'
            WAITFOR_TASKSHUTDOWN                            = 'Idle'
            WAITSTAT_MUTEX                                  = 'Other'
            WCC                                             = 'Other'
            WORKTBL_DROP                                    = 'Other'
            XE_BUFFERMGR_ALLPROCECESSED_EVENT               = 'Other'
            XE_BUFFERMGR_FREEBUF_EVENT                      = 'Other'
            XE_DISPATCHER_JOIN                              = 'Other'
            XE_MODULEMGR_SYNC                               = 'Other'
            XE_OLS_LOCK                                     = 'Other'
            XE_SERVICES_MUTEX                               = 'Other'
            XE_SESSION_CREATE_SYNC                          = 'Other'
            XE_SESSION_SYNC                                 = 'Other'
            XE_STM_CREATE                                   = 'Other'
            XE_TIMER_MUTEX                                  = 'Other'
            XE_TIMER_TASK_DONE                              = 'Other'
        }

        $ignorable = 'BROKER_EVENTHANDLER', 'BROKER_RECEIVE_WAITFOR', 'BROKER_TASK_STOP',
        'BROKER_TO_FLUSH', 'BROKER_TRANSMITTER', 'CHECKPOINT_QUEUE',
        'CHKPT', 'CLR_AUTO_EVENT', 'CLR_MANUAL_EVENT', 'CLR_SEMAPHORE', 'CXCONSUMER',
        'DBMIRROR_DBM_EVENT', 'DBMIRROR_EVENTS_QUEUE', 'DBMIRROR_WORKER_QUEUE',
        'DBMIRRORING_CMD', 'DIRTY_PAGE_POLL', 'DISPATCHER_QUEUE_SEMAPHORE',
        'EXECSYNC', 'FSAGENT', 'FT_IFTS_SCHEDULER_IDLE_WAIT', 'FT_IFTSHC_MUTEX',
        'HADR_CLUSAPI_CALL', 'HADR_FILESTREAM_IOMGR_IOCOMPLETION', 'HADR_LOGCAPTURE_WAIT',
        'HADR_NOTIFICATION_DEQUEUE', 'HADR_TIMER_TASK', 'HADR_WORK_QUEUE',
        'KSOURCE_WAKEUP', 'LAZYWRITER_SLEEP', 'LOGMGR_QUEUE',
        'MEMORY_ALLOCATION_EXT', 'ONDEMAND_TASK_QUEUE',
        'PARALLEL_REDO_DRAIN_WORKER', 'PARALLEL_REDO_LOG_CACHE', 'PARALLEL_REDO_TRAN_LIST', 'PARALLEL_REDO_WORKER_SYNC',
        'PREEMPTIVE_SP_SERVER_DIAGNOSTICS',
        'PARALLEL_REDO_WORKER_WAIT_WORK', 'PREEMPTIVE_HADR_LEASE_MECHANISM',
        'PREEMPTIVE_OS_LIBRARYOPS', 'PREEMPTIVE_OS_COMOPS', 'PREEMPTIVE_OS_CRYPTOPS',
        'PREEMPTIVE_OS_PIPEOPS', 'PREEMPTIVE_OS_AUTHENTICATIONOPS',
        'PREEMPTIVE_OS_GENERICOPS', 'PREEMPTIVE_OS_VERIFYTRUST',
        'PREEMPTIVE_OS_FILEOPS', 'PREEMPTIVE_OS_DEVICEOPS', 'PREEMPTIVE_OS_QUERYREGISTRY',
        'PREEMPTIVE_OS_WRITEFILE', 'PREEMPTIVE_XE_CALLBACKEXECUTE', 'PREEMPTIVE_XE_DISPATCHER',
        'PREEMPTIVE_XE_GETTARGETSTATE', 'PREEMPTIVE_XE_SESSIONCOMMIT',
        'PREEMPTIVE_XE_TARGETINIT', 'PREEMPTIVE_XE_TARGETFINALIZE',
        'PWAIT_ALL_COMPONENTS_INITIALIZED', 'PWAIT_DIRECTLOGCONSUMER_GETNEXT',
        'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP', 'QDS_ASYNC_QUEUE',
        'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP', 'REDO_THREAD_PENDING_WORK',
        'QDS_SHUTDOWN_QUEUE', 'REQUEST_FOR_DEADLOCK_SEARCH',
        'RESOURCE_QUEUE', 'SERVER_IDLE_CHECK', 'SLEEP_BPOOL_FLUSH', 'SLEEP_DBSTARTUP',
        'SLEEP_DCOMSTARTUP', 'SLEEP_MASTERDBREADY', 'SLEEP_MASTERMDREADY',
        'SLEEP_MASTERUPGRADED', 'SLEEP_MSDBSTARTUP', 'SLEEP_SYSTEMTASK', 'SLEEP_TASK',
        'SLEEP_TEMPDBSTARTUP', 'SNI_HTTP_ACCEPT', 'SP_SERVER_DIAGNOSTICS_SLEEP',
        'SQLTRACE_BUFFER_FLUSH', 'SQLTRACE_INCREMENTAL_FLUSH_SLEEP', 'SQLTRACE_WAIT_ENTRIES',
        'WAIT_FOR_RESULTS', 'WAITFOR', 'WAITFOR_TASKSHUTDOWN', 'WAIT_XTP_HOST_WAIT',
        'WAIT_XTP_OFFLINE_CKPT_NEW_LOG', 'WAIT_XTP_CKPT_CLOSE', 'WAIT_XTP_RECOVERY',
        'XE_BUFFERMGR_ALLPROCESSED_EVENT', 'XE_DISPATCHER_JOIN',
        'XE_DISPATCHER_WAIT', 'XE_LIVE_TARGET_TVF', 'XE_TIMER_EVENT'

        if ($IncludeIgnorable) {
            $sql = "WITH [Waits] AS
                (SELECT
                    [wait_type],
                    [wait_time_ms] / 1000.0 AS [WaitS],
                    ([wait_time_ms] - [signal_wait_time_ms]) / 1000.0 AS [ResourceS],
                    [signal_wait_time_ms] / 1000.0 AS [SignalS],
                    [waiting_tasks_count] AS [WaitCount],
                    100.0 * [wait_time_ms] / SUM ([wait_time_ms]) OVER() AS [Percentage],
                    ROW_NUMBER() OVER(ORDER BY [wait_time_ms] DESC) AS [RowNum]
                FROM sys.dm_os_wait_stats
                WHERE [waiting_tasks_count] > 0
                )
                SELECT
                    MAX ([W1].[wait_type]) AS [WaitType],
                    CAST (MAX ([W1].[WaitS]) AS DECIMAL (16,2)) AS [WaitSeconds],
                    CAST (MAX ([W1].[ResourceS]) AS DECIMAL (16,2)) AS [ResourceSeconds],
                    CAST (MAX ([W1].[SignalS]) AS DECIMAL (16,2)) AS [SignalSeconds],
                    MAX ([W1].[WaitCount]) AS [WaitCount],
                    CAST (MAX ([W1].[Percentage]) AS DECIMAL (5,2)) AS [Percentage],
                    CAST ((MAX ([W1].[WaitS]) / MAX ([W1].[WaitCount])) AS DECIMAL (16,4)) AS [AvgWaitSeconds],
                    CAST ((MAX ([W1].[ResourceS]) / MAX ([W1].[WaitCount])) AS DECIMAL (16,4)) AS [AvgResSeconds],
                    CAST ((MAX ([W1].[SignalS]) / MAX ([W1].[WaitCount])) AS DECIMAL (16,4)) AS [AvgSigSeconds],
                    CAST ('https://www.sqlskills.com/help/waits/' + MAX ([W1].[wait_type]) as XML) AS [URL]
                FROM [Waits] AS [W1]
                INNER JOIN [Waits] AS [W2]
                    ON [W2].[RowNum] <= [W1].[RowNum]
                GROUP BY [W1].[RowNum] HAVING SUM ([W2].[Percentage]) - MAX([W1].[Percentage]) < $Threshold"
            }
            else {
            $IgnorableList = "'$($ignorable -join "','")'"
            $sql = "WITH [Waits] AS
                (SELECT
                    [wait_type],
                    [wait_time_ms] / 1000.0 AS [WaitS],
                    ([wait_time_ms] - [signal_wait_time_ms]) / 1000.0 AS [ResourceS],
                    [signal_wait_time_ms] / 1000.0 AS [SignalS],
                    [waiting_tasks_count] AS [WaitCount],
                    100.0 * [wait_time_ms] / SUM ([wait_time_ms]) OVER() AS [Percentage],
                    ROW_NUMBER() OVER(ORDER BY [wait_time_ms] DESC) AS [RowNum]
                FROM sys.dm_os_wait_stats
                WHERE [waiting_tasks_count] > 0
                AND Cast([wait_type] as VARCHAR(60)) NOT IN ($IgnorableList)
                )
                SELECT
                    MAX ([W1].[wait_type]) AS [WaitType],
                    CAST (MAX ([W1].[WaitS]) AS DECIMAL (16,2)) AS [WaitSeconds],
                    CAST (MAX ([W1].[ResourceS]) AS DECIMAL (16,2)) AS [ResourceSeconds],
                    CAST (MAX ([W1].[SignalS]) AS DECIMAL (16,2)) AS [SignalSeconds],
                    MAX ([W1].[WaitCount]) AS [WaitCount],
                    CAST (MAX ([W1].[Percentage]) AS DECIMAL (5,2)) AS [Percentage],
                    CAST ((MAX ([W1].[WaitS]) / MAX ([W1].[WaitCount])) AS DECIMAL (16,4)) AS [AvgWaitSeconds],
                    CAST ((MAX ([W1].[ResourceS]) / MAX ([W1].[WaitCount])) AS DECIMAL (16,4)) AS [AvgResSeconds],
                    CAST ((MAX ([W1].[SignalS]) / MAX ([W1].[WaitCount])) AS DECIMAL (16,4)) AS [AvgSigSeconds],
                    CAST ('https://www.sqlskills.com/help/waits/' + MAX ([W1].[wait_type]) as XML) AS [URL]
                FROM [Waits] AS [W1]
                INNER JOIN [Waits] AS [W2]
                    ON [W2].[RowNum] <= [W1].[RowNum]
                GROUP BY [W1].[RowNum] HAVING SUM ([W2].[Percentage]) - MAX([W1].[Percentage]) < $Threshold"

            }
        Write-Message -Level Debug -Message $sql
    }
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            Write-Message -Level Verbose -Message "Connected to $instance"
            if ($IncludeIgnorable) {
                $excludeColumns = 'Notes'
            }
            else {
                $excludeColumns = 'Notes', 'Ignorable'
            }

            foreach ($row in $server.Query($sql)) {
                $waitType = $row.WaitType
                if (-not $IncludeIgnorable) {
                    if ($ignorable -contains $waitType) { continue }
                }

                [PSCustomObject]@{
                    ComputerName           = $server.ComputerName
                    InstanceName           = $server.ServiceName
                    SqlInstance            = $server.DomainInstanceName
                    WaitType               = $waitType
                    Category               = ($category).$waitType
                    WaitSeconds            = $row.WaitSeconds
                    ResourceSeconds        = $row.ResourceSeconds
                    SignalSeconds          = $row.SignalSeconds
                    WaitCount              = $row.WaitCount
                    Percentage             = $row.Percentage
                    AverageWaitSeconds     = $row.AvgWaitSeconds
                    AverageResourceSeconds = $row.AvgResSeconds
                    AverageSignalSeconds   = $row.AvgSigSeconds
                    Ignorable              = ($ignorable -contains $waitType)
                    URL                    = $row.URL
                    Notes                  = ($details).$waitType
                } | Select-DefaultView -ExcludeProperty $excludeColumns
            }
        }
    }
}
function Get-DbaWindowsLog {
    <#
    .SYNOPSIS
        Gets Windows Application events associated with an instance

    .DESCRIPTION
        Gets Windows Application events associated with an instance

    .PARAMETER SqlInstance
        The instance(s) to retrieve the event logs from

    .PARAMETER Start
        Default: 1970
        Retrieve all events starting from this timestamp.

    .PARAMETER End
        Default: Now
        Retrieve all events that happened before this timestamp

    .PARAMETER Credential
        Credential to be used to connect to the Server. Note this is a Windows credential, as this command requires we communicate with the computer and not with the SQL instance.

    .PARAMETER MaxThreads
        Default: Unlimited
        The maximum number of parallel threads used on the local computer.
        Given that those will mostly be waiting for the remote system, there is usually no need to limit this.

    .PARAMETER MaxRemoteThreads
        Default: 2
        The maximum number of parallel threads that are executed on the target sql server.
        These processes will cause considerable CPU load, so a low limit is advisable in most scenarios.
        Any value lower than 1 disables the limit

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Tags: Logging
        Author: Drew Furgiuele
        Editor: Friedrich "Fred" Weinmann
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Get-DbaWindowsLog

    .EXAMPLE
        $ErrorLogs = Get-DbaWindowsLog -SqlInstance sql01\sharepoint
        $ErrorLogs | Where-Object ErrorNumber -eq 18456

        Returns all lines in the errorlogs that have event number 18456 in them

#>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]
        $SqlInstance = $env:COMPUTERNAME,

        [DateTime]
        $Start = "1/1/1970 00:00:00",

        [DateTime]
        $End = (Get-Date),


        [System.Management.Automation.PSCredential]
        $Credential,

        [int]
        $MaxThreads = 0,

        [int]
        $MaxRemoteThreads = 2,

        [switch]
        [Alias('Silent')]$EnableException
    )

    begin {
        Write-Message -Level Debug -Message "Bound parameters: $($PSBoundParameters.Keys -join ", ")"

        #region Helper Functions
        function Start-Runspace {
            $Powershell = [PowerShell]::Create().AddScript($scriptBlock_ParallelRemoting).AddParameter("SqlInstance", $instance).AddParameter("Start", $Start).AddParameter("End", $End).AddParameter("Credential", $Credential).AddParameter("MaxRemoteThreads", $MaxRemoteThreads).AddParameter("ScriptBlock", $scriptBlock_RemoteExecution)
            $Powershell.RunspacePool = $RunspacePool
            Write-Message -Level Verbose -Message "Launching remote runspace against <c='green'>$instance</c>" -Target $instance
            $null = $RunspaceCollection.Add((New-Object -TypeName PSObject -Property @{ Runspace = $PowerShell.BeginInvoke(); PowerShell = $PowerShell; Instance = $instance.FullSmoName }))
        }

        function Receive-Runspace {
            [Parameter()]
            Param (
                [switch]
                $Wait
            )

            do {
                foreach ($Run in $RunspaceCollection.ToArray()) {
                    if ($Run.Runspace.IsCompleted) {
                        Write-Message -Level Verbose -Message "Receiving results from <c='green'>$($Run.Instance)</c>" -Target $Run.Instance
                        $Run.PowerShell.EndInvoke($Run.Runspace)
                        $Run.PowerShell.Dispose()
                        $RunspaceCollection.Remove($Run)
                    }
                }

                if ($Wait -and ($RunspaceCollection.Count -gt 0)) { Start-Sleep -Milliseconds 250 }
            }
            while ($Wait -and ($RunspaceCollection.Count -gt 0))
        }
        #endregion Helper Functions

        #region Scriptblocks
        $scriptBlock_RemoteExecution = {
            Param (
                [System.DateTime]
                $Start,

                [System.DateTime]
                $End,

                [string]
                $InstanceName,

                [int]
                $Throttle
            )

            #region Helper function
            function Convert-ErrorRecord {
                Param (
                    $Line
                )

                if (Get-Variable -Name codesAndStuff -Scope 1) {
                    $line2 = (Get-Variable -Name codesAndStuff -Scope 1).Value
                    Remove-Variable -Name codesAndStuff -Scope 1

                    $groups = [regex]::Matches($line2, '^([\d- :]+.\d\d) (\w+)[ ]+Error: (\d+), Severity: (\d+), State: (\d+)').Groups
                    $groups2 = [regex]::Matches($line, '^[\d- :]+.\d\d \w+[ ]+(.*)$').Groups

                    New-Object PSObject -Property @{
                        Timestamp   = [DateTime]::ParseExact($groups[1].Value, "yyyy-MM-dd HH:mm:ss.ff", $null)
                        Spid        = $groups[2].Value
                        Message     = $groups2[1].Value
                        ErrorNumber = [int]($groups[3].Value)
                        Severity    = [int]($groups[4].Value)
                        State       = [int]($groups[5].Value)
                    }
                }

                if ($Line -match '^\d{4}-\d\d-\d\d \d\d:\d\d:\d\d\.\d\d[\w ]+((\w+): (\d+)[,\.]\s?){3}') {
                    Set-Variable -Name codesAndStuff -Value $Line -Scope 1
                }
            }
            #endregion Helper function

            #region Script that processes an individual file
            $scriptBlock = {
                Param (
                    [System.IO.FileInfo]
                    $File
                )

                try {
                    $stream = New-Object System.IO.FileStream($File.FullName, "Open", "Read", "ReadWrite, Delete")
                    $reader = New-Object System.IO.StreamReader($stream)

                    while (-not $reader.EndOfStream) {
                        Convert-ErrorRecord -Line $reader.ReadLine()
                    }
                }
                catch { }
            }
            #endregion Script that processes an individual file

            #region Gather list of files to process
            $eventSource = "MSSQLSERVER"
            if ($InstanceName -notmatch "^DEFAULT$|^MSSQLSERVER$") {
                $eventSource = 'MSSQL$' + $InstanceName
            }

            $event = Get-WinEvent -FilterHashtable @{
                LogName      = "Application"
                ID           = 17111
                ProviderName = $eventSource
            } -MaxEvents 1 -ErrorAction SilentlyContinue

            if (-not $event) { return }

            $path = $event.Properties[0].Value
            $errorLogPath = Split-Path -Path $path
            $errorLogFileName = Split-Path -Path $path -Leaf
            $errorLogFiles = Get-ChildItem -Path $errorLogPath | Where-Object { ($_.Name -like "$errorLogFileName*") -and ($_.LastWriteTime -gt $Start) -and ($_.CreationTime -lt $End) }
            #endregion Gather list of files to process

            #region Prepare Runspaces
            [Collections.Arraylist]$RunspaceCollection = @()

            $InitialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
            $Command = Get-Item function:Convert-ErrorRecord
            $InitialSessionState.Commands.Add((New-Object System.Management.Automation.Runspaces.SessionStateFunctionEntry($command.Name, $command.Definition)))

            $RunspacePool = [RunspaceFactory]::CreateRunspacePool($InitialSessionState)
            $null = $RunspacePool.SetMinRunspaces(1)
            if ($Throttle -gt 0) { $null = $RunspacePool.SetMaxRunspaces($Throttle) }
            $RunspacePool.Open()
            #endregion Prepare Runspaces

            #region Process Error files
            $countDone = 0
            $countStarted = 0
            $countTotal = ($errorLogFiles | Measure-Object).Count

            while ($countDone -lt $countTotal) {
                while (($RunspacePool.GetAvailableRunspaces() -gt 0) -and ($countStarted -lt $countTotal)) {
                    $Powershell = [PowerShell]::Create().AddScript($scriptBlock).AddParameter("File", $errorLogFiles[$countStarted])
                    $Powershell.RunspacePool = $RunspacePool
                    $null = $RunspaceCollection.Add((New-Object -TypeName PSObject -Property @{ Runspace = $PowerShell.BeginInvoke(); PowerShell = $PowerShell }))
                    $countStarted++
                }

                foreach ($Run in $RunspaceCollection.ToArray()) {
                    if ($Run.Runspace.IsCompleted) {
                        $Run.PowerShell.EndInvoke($Run.Runspace) | Where-Object { ($_.Timestamp -gt $Start) -and ($_.Timestamp -lt $End) }
                        $Run.PowerShell.Dispose()
                        $RunspaceCollection.Remove($Run)
                        $countDone++
                    }
                }

                Start-Sleep -Milliseconds 250
            }
            $RunspacePool.Close()
            $RunspacePool.Dispose()
            #endregion Process Error files
        }

        $scriptBlock_ParallelRemoting = {
            Param (
                [DbaInstanceParameter]
                $SqlInstance,

                [DateTime]
                $Start,

                [DateTime]
                $End,

                [object]
                $Credential,

                [int]
                $MaxRemoteThreads,

                [System.Management.Automation.ScriptBlock]
                $ScriptBlock
            )

            $params = @{
                ArgumentList = $Start, $End, $SqlInstance.InstanceName, $MaxRemoteThreads
                ScriptBlock  = $ScriptBlock
            }
            if (-not $SqlInstance.IsLocalhost) { $params["ComputerName"] = $SqlInstance.ComputerName }
            if ($Credential) { $params["Credential"] = $Credential }

            Invoke-Command @params | Select-Object @{ n = "InstanceName"; e = { $SqlInstance.FullSmoName } }, Timestamp, Spid, Severity, ErrorNumber, State, Message
        }
        #endregion Scriptblocks

        #region Setup Runspace
        [Collections.Arraylist]$RunspaceCollection = @()
        $InitialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
        $RunspacePool = [RunspaceFactory]::CreateRunspacePool($InitialSessionState)
        $RunspacePool.SetMinRunspaces(1) | Out-Null
        if ($MaxThreads -gt 0) { $null = $RunspacePool.SetMaxRunspaces($MaxThreads) }
        $RunspacePool.Open()

        $countStarted = 0
        $countReceived = 0
        #endregion Setup Runspace
    }

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level VeryVerbose -Message "Processing <c='green'>$instance</c>" -Target $instance
            Start-Runspace
            Receive-Runspace
        }
    }

    end {
        Receive-Runspace -Wait
        $RunspacePool.Close()
        $RunspacePool.Dispose()
    }
}
function Get-DbaXEObject {
    <#
        .SYNOPSIS
            Gets a list of trace(s) from specified SQL Server instance(s).

        .DESCRIPTION
            This function returns a list of Traces on the specified SQL Server instance(s) and identifies the default Trace File

        .PARAMETER SqlInstance
            Target SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Type
            Used to specify the type. Valid types include:

                Action
                Event
                Map
                Message
                PredicateComparator
                PredicateSource
                Target
                Type

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message. This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting. Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            Get-DbaXEObject -SqlInstance sql2016

            Lists all the XE Objects on the sql2016 SQL Server.

        .EXAMPLE
            Get-DbaXEObject -SqlInstance sql2017 -Type Action, Event

            Lists all the XE Objects of type Action and Event on the sql2017 SQL Server.

    #>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [ValidateSet("Type", "Event", "Target", "Action", "Map", "Message", "PredicateComparator", "PredicateSource")]
        [string[]]$Type,
        [switch]$EnableException
    )
    begin {
        if ($Type) {
            $join = $Type -join "','"
            $where = "AND o.object_type in ('$join')"
            $where.Replace("PredicateComparator", "pred_compare")
            $where.Replace("PredicateSource", "pred_source")
        }
        $sql = "SELECT  SERVERPROPERTY('MachineName') AS ComputerName,
                ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
                SERVERPROPERTY('ServerName') AS SqlInstance,
                p.name AS PackageName,
                ObjectType =
                      CASE o.object_type
                         WHEN 'type' THEN 'Type'
                         WHEN 'event' THEN 'Event'
                         WHEN 'target' THEN 'Target'
                         WHEN 'pred_compare' THEN 'PredicateComparator'
                         WHEN 'pred_source' THEN 'PredicateSource'
                         WHEN 'action' THEN 'Action'
                         WHEN 'map' THEN 'Map'
                         WHEN 'message' THEN 'Message'
                         ELSE o.object_type
                      END,
                o.object_type as ObjectTypeRaw,
                o.name AS TargetName,
                o.description as Description
                FROM sys.dm_xe_packages AS p
                JOIN sys.dm_xe_objects AS o ON p.guid = o.package_guid
                WHERE (p.capabilities IS NULL OR p.capabilities & 1 = 0)
                $where
                AND (o.capabilities IS NULL OR o.capabilities & 1 = 0)
                ORDER BY o.object_type
                "
    }
    process {
        foreach ($instance in $SqlInstance) {

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                return
            }

            try {
                $server.Query($sql) | Select-DefaultView -ExcludeProperty ComputerName, InstanceName, ObjectTypeRaw
            }
            catch {
                Stop-Function -Message "Issue collecting trace data on $server." -Target $server -ErrorRecord $_
            }
        }
    }
}
function Get-DbaXESession {
    <#
        .SYNOPSIS
            Gets a list of Extended Events Sessions from the specified SQL Server instance(s).

        .DESCRIPTION
            Retrieves a list of Extended Events Sessions present on the specified SQL Server instance(s).

        .PARAMETER SqlInstance
            Target SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Session
            Only return specific sessions. Options for this parameter are auto-populated from the server.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Author: Klaas Vandenberghe ( @PowerDBAKlaas )

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaXESession

        .EXAMPLE
            Get-DbaXESession -SqlInstance ServerA\sql987

            Returns a custom object with ComputerName, SQLInstance, Session, StartTime, Status and other properties.

        .EXAMPLE
            Get-DbaXESession -SqlInstance ServerA\sql987 | Format-Table ComputerName, SqlInstance, Session, Status -AutoSize

            Returns a formatted table displaying ComputerName, SqlInstance, Session, and Status.

        .EXAMPLE
            'ServerA\sql987','ServerB' | Get-DbaXESession

            Returns a custom object with ComputerName, SqlInstance, Session, StartTime, Status and other properties, from multiple SQL instances.

    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Sessions")]
        [object[]]$Session,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-DbaXEsSession
    }

    process {

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 11 -AzureUnsupported
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $SqlConn = $server.ConnectionContext.SqlConnectionObject
            $SqlStoreConnection = New-Object Microsoft.SqlServer.Management.Sdk.Sfc.SqlStoreConnection $SqlConn
            $XEStore = New-Object  Microsoft.SqlServer.Management.XEvent.XEStore $SqlStoreConnection
            Write-Message -Level Verbose -Message "Getting XEvents Sessions on $instance."

            $xesessions = $XEStore.sessions

            if ($Session) {
                $xesessions = $xesessions | Where-Object { $_.Name -in $Session }
            }

            foreach ($x in $xesessions) {
                $status = switch ($x.IsRunning) { $true { "Running" } $false { "Stopped" } }
                $files = $x.Targets.TargetFields | Where-Object Name -eq Filename | Select-Object -ExpandProperty Value

                $filecollection = $remotefile = @()

                if ($files) {
                    foreach ($file in $files) {
                        if ($file -notmatch ':\\' -and $file -notmatch '\\\\') {
                            $directory = $server.ErrorLogPath.TrimEnd("\")
                            $file = "$directory\$file"
                        }
                        $filecollection += $file
                        $remotefile += Join-AdminUnc -servername $server.ComputerName -filepath $file
                    }
                }

                Add-Member -Force -InputObject $x -MemberType NoteProperty -Name ComputerName -Value $server.ComputerName
                Add-Member -Force -InputObject $x -MemberType NoteProperty -Name InstanceName -Value $server.ServiceName
                Add-Member -Force -InputObject $x -MemberType NoteProperty -Name SqlInstance -Value $server.DomainInstanceName
                Add-Member -Force -InputObject $x -MemberType NoteProperty -Name Status -Value $status
                Add-Member -Force -InputObject $x -MemberType NoteProperty -Name Session -Value $x.Name
                Add-Member -Force -InputObject $x -MemberType NoteProperty -Name TargetFile -Value $filecollection
                Add-Member -Force -InputObject $x -MemberType NoteProperty -Name RemoteTargetFile -Value $remotefile
                Add-Member -Force -InputObject $x -MemberType NoteProperty -Name Parent -Value $server
                Add-Member -Force -InputObject $x -MemberType NoteProperty -Name Store -Value $XEStore
                Select-DefaultView -InputObject $x -Property ComputerName, InstanceName, SqlInstance, Name, Status, StartTime, AutoStart, State, Targets, TargetFile, Events, MaxMemory, MaxEventSize
            }
        }
    }
}
function Get-DbaXESessionTarget {
    <#
        .SYNOPSIS
            Get a list of Extended Events Session Targets from the specified SQL Server instance(s).

        .DESCRIPTION
            Retrieves a list of Extended Events Session Targets from the specified SQL Server instance(s).

        .PARAMETER SqlInstance
            Target SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Session
            Only return a specific session. Options for this parameter are auto-populated from the server.

        .PARAMETER Target
            Only return a specific target.

        .PARAMETER InputObject
            Specifies an XE session returned by Get-DbaXESession to search.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaXESessionTarget

        .EXAMPLE
            Get-DbaXESessionTarget -SqlInstance ServerA\sql987 -Session system_health

            Shows targets for the system_health session on ServerA\sql987.

        .EXAMPLE
            Get-DbaXESession -SqlInstance sql2016 -Session system_health | Get-DbaXESessionTarget

            Returns the targets for the system_health session on sql2016.

        .EXAMPLE
            Get-DbaXESession -SqlInstance sql2016 -Session system_health | Get-DbaXESessionTarget -Target package0.event_file

            Return only the package0.event_file target for the system_health session on sql2016.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    param (
        [parameter(ValueFromPipeline, ParameterSetName = "instance", Mandatory)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$Session,
        [string[]]$Target,
        [parameter(ValueFromPipeline, ParameterSetName = "piped", Mandatory)]
        [Microsoft.SqlServer.Management.XEvent.Session[]]$InputObject,
        [switch][Alias('Silent')]
        $EnableException
    )

    begin {
        function Get-Target {
            [CmdletBinding()]
            param (
                $Sessions,
                $Session,
                $Server,
                $Target
            )

            foreach ($xsession in $Sessions) {

                if ($null -eq $server) {
                    $server = $xsession.Parent
                }

                if ($Session -and $xsession.Name -notin $Session) { continue }
                $status = switch ($xsession.IsRunning) { $true { "Running" } $false { "Stopped" } }
                $sessionname = $xsession.Name

                foreach ($xtarget in $xsession.Targets) {
                    if ($Target -and $xtarget.Name -notin $Target) { continue }

                    $files = $xtarget.TargetFields | Where-Object Name -eq Filename | Select-Object -ExpandProperty Value

                    $filecollection = $remotefile = @()

                    if ($files) {
                        foreach ($file in $files) {
                            if ($file -notmatch ':\\' -and $file -notmatch '\\\\') {
                                $directory = $server.ErrorLogPath.TrimEnd("\")
                                $file = "$directory\$file"
                            }
                            $filecollection += $file
                            $remotefile += Join-AdminUnc -servername $server.ComputerName -filepath $file
                        }
                    }

                    Add-Member -Force -InputObject $xtarget -MemberType NoteProperty -Name ComputerName -Value $server.ComputerName
                    Add-Member -Force -InputObject $xtarget -MemberType NoteProperty -Name InstanceName -Value $server.ServiceName
                    Add-Member -Force -InputObject $xtarget -MemberType NoteProperty -Name SqlInstance -Value $server.DomainInstanceName
                    Add-Member -Force -InputObject $xtarget -MemberType NoteProperty -Name Session -Value $sessionname
                    Add-Member -Force -InputObject $xtarget -MemberType NoteProperty -Name SessionStatus -Value $status
                    Add-Member -Force -InputObject $xtarget -MemberType NoteProperty -Name TargetFile -Value $filecollection
                    Add-Member -Force -InputObject $xtarget -MemberType NoteProperty -Name RemoteTargetFile -Value $remotefile

                    Select-DefaultView -InputObject $xtarget -Property ComputerName, InstanceName, SqlInstance, Session, SessionStatus, Name, ID, 'TargetFields as Field', PackageName, 'TargetFile as File', Description, ScriptName
                }
            }
        }
    }

    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $SqlInstance) {
            $InputObject += Get-DbaXESession -SqlInstance $instance -SqlCredential $SqlCredential -Session $Session
        }
        Get-Target -Sessions $InputObject -Session $Session -Target $Target
    }
}
function Get-DbaXESessionTargetFile {
    <#
        .SYNOPSIS
            Get a file system object from the Extended Events Session Target Files.

        .DESCRIPTION
            Get a file system object from the Extended Events Session Target Files.

            Note: this performs a Get-ChildItem on remote servers if the specified target SQL Server is remote.

        .PARAMETER SqlInstance
            The target SQL Server

        .PARAMETER SqlCredential
            Login to SQL instnace with alternative credentials

        .PARAMETER Session
            Only return files from a specific session. Options for this parameter are auto-populated from the server.

        .PARAMETER Target
            Only return files from a specific target.

        .PARAMETER InputObject
            Allows results from piping in Get-DbaXESessionTarget.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaXESessionTargetFile

        .EXAMPLE
            Get-DbaXESessionTargetFile -SqlInstance sql2017 -Session 'Long Running Queries'

            Shows Target Files for the 'Long Running Queries' session on sql2017.

        .EXAMPLE
            Get-DbaXESession -SqlInstance sql2016 -Session 'Long Running Queries' | Get-DbaXESessionTarget | Get-DbaXESessionTargetFile

            Returns the Target Files for the system_health session on sql2016.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    param (
        [parameter(ValueFromPipeline, ParameterSetName = "instance", Mandatory)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$Session,
        [string[]]$Target,
        [parameter(ValueFromPipeline, ParameterSetName = "piped", Mandatory)]
        [Microsoft.SqlServer.Management.XEvent.Target[]]$InputObject,
        [switch]$EnableException
    )

    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $SqlInstance) {
            $InputObject += Get-DbaXESessionTarget -SqlInstance $instance -SqlCredential $SqlCredential -Session $Session -Target $Target | Where-Object File -ne $null
        }

        foreach ($object in $InputObject) {
            $computer = [dbainstance]$object.ComputerName
            try {
                if ($computer.IsLocal) {
                    $file = $object.TargetFile
                    Write-Message -Level Verbose -Message "Getting $file"
                    Get-ChildItem "$file*" -ErrorAction Stop
                }
                else {
                    $file = $object.RemoteTargetFile
                    Write-Message -Level Verbose -Message "Getting $file"
                    Get-ChildItem -Recurse "$file*" -ErrorAction Stop
                }
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_
            }
        }
    }
}
function Get-DbaXESessionTemplate {
    <#
        .SYNOPSIS
            Parses Extended Event XML templates. Defaults to parsing templates in the dbatools template repository (\bin\xetemplates\).

        .DESCRIPTION
            Parses Extended Event XML templates. Defaults to parsing templates in the dbatools template repository (\bin\xetemplates\).

            The default repository contains templates from:
                    Microsoft's Templates that come with SSMS
                    Jes Borland's "Everyday Extended Events" presentation and GitHub repository (https://github.com/grrlgeek/extended-events)
                    Christian Gräfe's XE Repo: https://github.com/chrgraefe/sqlscripts/blob/master/XE-Events/
                    Erin Stellato's Blog: https://www.sqlskills.com/blogs/erin/

            Some profile templates converted using:
                    sp_SQLskills_ConvertTraceToExtendedEvents.sql
                    Jonathan M. Kehayias, SQLskills.com
                    http://sqlskills.com/blogs/jonathan

        .PARAMETER Path
            The path to the template directory. Defaults to the dbatools template repository (\bin\xetemplates\).

        .PARAMETER Pattern
            Specify a pattern for filtering. Alternatively, you can use Out-GridView -Passthru to select objects and pipe them to Import-DbaXESessionTemplate

        .PARAMETER Template
            Specifies one or more of the templates provided by dbatools. Press tab to cycle through the list of options.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaXESessionTemplate

        .EXAMPLE
            Get-DbaXESessionTemplate

            Returns information about all the templates in the local dbatools repository.

        .EXAMPLE
            Get-DbaXESessionTemplate | Out-GridView -PassThru | Import-DbaXESessionTemplate -SqlInstance sql2017 | Start-DbaXESession

            Allows you to select a Session template, then import it to the specified instance and start the session.

        .EXAMPLE
            Get-DbaXESessionTemplate -Path "$home\Documents\SQL Server Management Studio\Templates\XEventTemplates"

            Returns information about all the templates in your local XEventTemplates repository.

        .EXAMPLE
            Get-DbaXESessionTemplate -Pattern duration

            Returns information about all the templates that match the word "duration" in the title, category or body.

        .EXAMPLE
            Get-DbaXESessionTemplate | Select-Object *

            Returns more information about the template, including the full path/filename.
        #>

    [CmdletBinding()]
    param (
        [string[]]$Path = "$script:PSModuleRoot\bin\xetemplates",
        [string]$Pattern,
        [string[]]$Template,
        [switch]$EnableException
    )
    begin {
        $metadata = Import-Clixml "$script:PSModuleRoot\bin\xetemplates-metadata.xml"
        # In case people really want a "like" search, which is slower
        $Pattern = $Pattern.Replace("*", ".*").Replace("..*", ".*")
    }
    process {
        foreach ($directory in $Path) {
            $files = Get-ChildItem "$directory\*.xml"

            if ($Template) {
                $files = $files | Where-Object BaseName -in $Template
            }

            foreach ($file in $files) {
                try {
                    $xml = [xml](Get-Content $file)
                }
                catch {
                    Stop-Function -Message "Failure" -ErrorRecord $_ -Target $file -Continue
                }

                foreach ($session in $xml.event_sessions) {
                    $meta = $metadata | Where-Object Name -eq $session.event_session.name
                    if ($Pattern) {
                        if (
                            # There's probably a better way to do this
                            ($session.event_session.name -match $Pattern) -or
                            ($session.event_session.TemplateCategory.'#text' -match $Pattern) -or
                            ($session.event_session.TemplateSource -match $Pattern) -or
                            ($session.event_session.TemplateDescription.'#text' -match $Pattern) -or
                            ($session.event_session.TemplateName.'#text' -match $Pattern) -or
                            ($meta.Source -match $Pattern)
                        ) {
                            [pscustomobject]@{
                                Name          = $session.event_session.name
                                Category      = $session.event_session.TemplateCategory.'#text'
                                Source        = $meta.Source
                                Compatibility = ("$($meta.Compatibility)").ToString().Replace(",", "")
                                Description   = $session.event_session.TemplateDescription.'#text'
                                TemplateName  = $session.event_session.TemplateName.'#text'
                                Path          = $file
                                File          = $file.Name
                            } | Select-DefaultView -ExcludeProperty File, TemplateName, Path
                        }
                    }
                    else {
                        [pscustomobject]@{
                            Name          = $session.event_session.name
                            Category      = $session.event_session.TemplateCategory.'#text'
                            Source        = $meta.Source
                            Compatibility = $meta.Compatibility.ToString().Replace(",", "")
                            Description   = $session.event_session.TemplateDescription.'#text'
                            TemplateName  = $session.event_session.TemplateName.'#text'
                            Path          = $file
                            File          = $file.Name
                        } | Select-DefaultView -ExcludeProperty File, TemplateName, Path
                    }
                }
            }
        }
    }
}
function Get-DbaXESmartTarget {
    <#
        .SYNOPSIS
            Gets an XESmartTarget PowerShell Job created by Start-DbaXESmartTarget.

        .DESCRIPTION
            Gets an XESmartTarget PowerShell Job created by Start-DbaXESmartTarget.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
            SmartTarget: by Gianluca Sartori (@spaghettidba)

        .LINK
            https://dbatools.io/Get-DbaXESmartTarget
            https://github.com/spaghettidba/XESmartTarget/wiki

        .EXAMPLE
            Get-DbaXESmartTarget

            Gets an XESmartTarget PowerShell Job created by Start-DbaXESmartTarget.

    #>
    [CmdletBinding()]
    param (
        [switch]$EnableException
    )
    process {
        try {
            Get-Job | Where-Object Name -Match SmartTarget | Select-Object -Property ID, Name, State
        }
        catch {
            Stop-Function -Message "Failure" -ErrorRecord $_
        }
    }
}
function Get-DbaXEStore {
    <#
        .SYNOPSIS
            Get a Extended Events store

        .DESCRIPTION
            Get a Extended Events store

       .PARAMETER SqlInstance
            Target SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaXEStore

        .EXAMPLE
            Get-DbaXEStore -SqlInstance ServerA\sql987

            Returns an XEvent Store.

    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 11
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $SqlConn = $server.ConnectionContext.SqlConnectionObject
            $SqlStoreConnection = New-Object Microsoft.SqlServer.Management.Sdk.Sfc.SqlStoreConnection $SqlConn
            $store = New-Object  Microsoft.SqlServer.Management.XEvent.XEStore $SqlStoreConnection

            Add-Member -Force -InputObject $store -MemberType NoteProperty -Name ComputerName -Value $server.ComputerName
            Add-Member -Force -InputObject $store -MemberType NoteProperty -Name InstanceName -Value $server.ServiceName
            Add-Member -Force -InputObject $store -MemberType NoteProperty -Name SqlInstance -Value $server.DomainInstanceName
            Select-DefaultView -InputObject $store -Property ComputerName, InstanceName, SqlInstance, ServerName, Sessions, Packages, RunningSessionCount
        }
    }
}
function Import-DbaCsvToSql {
    <#
        .SYNOPSIS
            Efficiently imports very large (and small) CSV files into SQL Server using only the .NET Framework and PowerShell.

        .DESCRIPTION
            Import-DbaCsvToSql takes advantage of .NET's super fast SqlBulkCopy class to import CSV files into SQL Server at up to 90,000 rows a second.

            The entire import is contained within a transaction, so if a failure occurs or the script is aborted, no changes will persist.

            If the table specified does not exist, it will be automatically created using best guessed data types. In addition, the destination table can be truncated prior to import.

            The Query parameter will be used to import only the data returned from a SQL Query executed against the CSV file(s). This function supports a number of bulk copy options. Please see parameter list for details.

        .PARAMETER CSV
            Specifies path to the CSV file(s) to be imported. Multiple files may be imported if they are formatted similarly.

            If no file is specified, a dialog box will appear to select your file(s).

        .PARAMETER FirstRowColumns
            If this switch is enabled, the first row in the file will be used as column names for the data being imported.

            If the first row does not contain column names and -Query is specified, use field names "column1, column2, column3" and so on.

        .PARAMETER Delimiter
            Specifies the delimiter used in the imported file(s). If no delimiter is specified, comma is assumed.

            Valid delimiters are '`t`, '|', ';',' ' and ',' (tab, pipe, semicolon, space, and comma).

        .PARAMETER SingleColumn
            Specifies that the file contains a single column of data

        .PARAMETER SqlInstance
            The SQL Server Instance to import data into.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies the name of the database the CSV will be imported into. Options for this this parameter are  auto-populated from the server.

        .PARAMETER Schema
            Specifies the schema in which the SQL table or view where CSV will be imported into resides. Default is dbo

            If a schema name is not specified, and a CSV name with multiple dots is specified (ie; something.data.csv) then this will be interpreted as a request to import into a table [data] in the schema [something].

            If a schema does not currently exist, it will be created, after a prompt to confirm this. Authorization will be set to dbo by default

        .PARAMETER Table
            Specifies the SQL table or view where CSV will be imported into.

            If a table name is not specified, the table name will be automatically determined from the filename, and a prompt will appear to confirm the table name.

            If a table does not currently exist, it will created.  SQL datatypes are determined from the first row of the CSV that contains data (skips first row if -FirstRowColumns is specified). Datatypes used are: bigint, numeric, datetime and varchar(MAX).

            If the automatically generated table datatypes do not work for you, please create the table prior to import.

        .PARAMETER Truncate
            If this switch is enabled, the destination table will be truncated prior to import.

        .PARAMETER Safe
            If this switch is enabled, OleDb is used to import the records. By default, Import-DbaCsvToSql uses StreamReader for imports. StreamReader is super fast, but may not properly parse some files.

            When using OleDb the import will be slower but more predictable when it comes to parsing CSV files. A schema.ini is automatically generated for best results. If schema.ini currently exists in the directory, it will be moved to a temporary location, then moved back.

            OleDB also enables the script to use the -Query parameter, which enables you to import specific subsets of data within a CSV file. OleDB imports at up to 21,000 rows/sec.

        .PARAMETER Turbo
            If this switch is enabled, a Table Lock will be created for the import to make the import run as fast as possible. Depending upon the number of columns and datatypes, this may be over 90,000 records per second.

            This switch cannot be used in conjunction with -Query.

            Remember the Turbo button? This one actually works. Turbo is mega fast, but may not handle some datatypes as well as other methods.

            If your CSV file is rather vanilla and doesn't have a ton of NULLs, Turbo may work well for you.

        .PARAMETER First
            Specifies the number of rows to import. If this parameter is omitted, the entire file is imported. Row counts start at the top of the file, but skip the first row if -FirstRowColumns is specified.

            Use -Query if you need advanced First (TOP) functionality.

        .PARAMETER Query
            Specifies a query to execute against the CSV data to select/modify the data being imported.

            To make command line queries easy, this module will convert the word "csv" to the actual CSV formatted table name. If the FirstRowColumns switch is not used, the query should use column1, column2, column3, etc.

            Cannot be used in conjunction with -Turbo or -First. When -Query is specified, the slower import method, OleDb, will be used.

        .PARAMETER NotifyAfter
            Specifies the import row count interval for reporting progress. A notification will be shown after each group of this many rows has been imported.

        .PARAMETER BatchSize
            Specifies the batch size for the import. Defaults to 50000.

        .PARAMETER TableLock
            If this switch is enabled, the SqlBulkCopy option to acquire a table lock will be used. This is automatically used if -Turbo is enabled.

            Per Microsoft "Obtain a bulk update lock for the duration of the bulk copy operation. When not
            specified, row locks are used."

        .PARAMETER CheckConstraints
            If this switch is enabled, the SqlBulkCopy option to check constraints will be used.

            Per Microsoft "Check constraints while data is being inserted. By default, constraints are not checked."

        .PARAMETER FireTriggers
            If this switch is enabled, the SqlBulkCopy option to allow insert triggers to be executed will be used.

            Per Microsoft "When specified, cause the server to fire the insert triggers for the rows being inserted into the database."

        .PARAMETER KeepIdentity
            If this switch is enabled, the SqlBulkCopy option to keep identity values from the source will be used.

            Per Microsoft "Preserve source identity values. When not specified, identity values are assigned by the destination."

        .PARAMETER KeepNulls
            If this switch is enabled, the SqlBulkCopy option to keep NULL values in the table will be used.

            Per Microsoft "Preserve null values in the destination table regardless of the settings for default values. When not specified, null values are replaced by default values where applicable."

        .NOTES
            Tags: Migration
            Author: Chrissy LeMaire (@cl), netnerds.net
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://blog.netnerds.net/2015/09/Import-DbaCsvtosql-super-fast-csv-to-sql-server-import-powershell-module/

        .EXAMPLE
            Import-DbaCsvToSql -Csv C:\temp\housing.csv -SqlInstance sql001 -Database markets

            Imports the entire comma-delimited housing.csv to the SQL "markets" database on a SQL Server named sql001.

            Since a table name was not specified, the table name is automatically determined from filename as "housing" and a prompt will appear to confirm table name.

            The first row is not skipped, as it does not contain column names.

        .EXAMPLE
            Import-DbaCsvToSql -Csv .\housing.csv -SqlInstance sql001 -Database markets -Table housing -First 100000 -Safe -Delimiter "`t" -FirstRowColumns

            Imports the first 100,000 rows of the tab delimited housing.csv file to the "housing" table in the "markets" database on a SQL Server named sql001. Since -Safe was specified, the OleDB method will be used for the bulk import. The first row is skipped, as it contains column names.

        .EXAMPLE
            Import-DbaCsvToSql -csv C:\temp\huge.txt -SqlInstance sqlcluster -Database locations -Table latitudes -Delimiter "|" -Turbo

            Imports all records from the pipe delimited huge.txt file using the fastest method possible into the latitudes table within the locations database. Obtains a table lock for the duration of the bulk copy operation. This specific command has been used
            to import over 10.5 million rows in 2 minutes.

        .EXAMPLE
            Import-DbaCsvToSql -Csv C:\temp\housing.csv, .\housing2.csv -SqlInstance sql001 -Database markets -Table housing -Delimiter "`t" -query "select top 100000 column1, column3 from csv" -Truncate

            Truncates the "housing" table, then imports columns 1 and 3 of the first 100000 rows of the tab-delimited housing.csv in the C:\temp directory, and housing2.csv in the current directory. Since the query is executed against both files, a total of 200,000 rows will be imported.

        .EXAMPLE
            Import-DbaCsvToSql -Csv C:\temp\housing.csv -SqlInstance sql001 -Database markets -Table housing -query "select address, zip from csv where state = 'Louisiana'" -FirstRowColumns -Truncate -FireTriggers

            Uses the first line to determine CSV column names. Truncates the "housing" table on the SQL Server, then imports the address and zip columns from all records in the housing.csv where the state equals Louisiana.

            Triggers are fired for all rows. Note that this does slightly slow down the import.

        .EXAMPLE
            Import-DbaCsvToSql -Csv c:\temp\SingleColumn.csv -SqlInstance sql001 -Database markets -Table TempTable -SingleColumn

            Upload the single column Csv SingleColumn.csv to Temptable which has just one column

        .EXAMPLE
            Import-DbaCsvToSql -Csv "\\FileServer\To Import\housing.csv" -SqlInstance sql001 -Database markets

            Imports the entire comma-delimited housing.csv located in the share named "To Import" on FileServer to the SQL "markets" database on a SQL Server named sql001.

        .EXAMPLE
            Import-DbaCsvToSql -Csv '\\FileServer\R$\To Import\housing.csv' -SqlInstance sql001 -Database markets

            Imports the entire comma-delimited housing.csv located in the directory R:\To Import on FileServer using the administrative share to the SQL "markets" database on a SQL Server named sql001.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    Param (
        [string[]]$Csv,
        [Parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [object]$SqlCredential,
        [string]$Table,
        [string]$Schema = "dbo",
        [switch]$Truncate,
        [ValidateSet("`t", "|", ";", " ", ",")]
        [string]$Delimiter = ",",
        [switch]$SingleColumn,
        [switch]$FirstRowColumns,
        [parameter(ParameterSetName = "reader")]
        [switch]$Turbo,
        [parameter(ParameterSetName = "ole")]
        [switch]$Safe,
        [int]$First = 0,
        [parameter(ParameterSetName = "ole")]
        [string]$Query = "select * from csv",
        [int]$BatchSize = 50000,
        [int]$NotifyAfter,
        [switch]$TableLock,
        [switch]$CheckConstraints,
        [switch]$FireTriggers,
        [switch]$KeepIdentity,
        [switch]$KeepNulls,
        #[Parameter(DontShow)]
        [switch]$shellswitch,
        #[Parameter(DontShow)]
        [string]$SqlCredentialPath
    )

    DynamicParam {

        if ($SqlInstance.length -gt 0) {
            # Auto populate database list from specified sqlserver
            $paramconn = New-Object System.Data.SqlClient.SqlConnection

            if ($SqlCredentialPath.length -gt 0) {
                $SqlCredential = Import-CliXml $SqlCredentialPath
            }

            if ($SqlCredential.count -eq 0 -or $null -eq $SqlCredential) {
                $paramconn.ConnectionString = "Data Source=$SqlInstance;Integrated Security=True;"
            }
            else {
                $paramconn.ConnectionString = "Data Source=$SqlInstance;User Id=$($SqlCredential.UserName); Password=$($SqlCredential.GetNetworkCredential().Password);"
            }

            try {
                $paramconn.Open()
                $sql = "select name from master.dbo.sysdatabases"
                $paramcmd = New-Object System.Data.SqlClient.SqlCommand($sql, $paramconn, $null)
                $paramdt = New-Object System.Data.DataTable
                $paramdt.Load($paramcmd.ExecuteReader())
                $databaselist = $paramdt.rows.name
                $null = $paramcmd.Dispose()
                $null = $paramconn.Close()
                $null = $paramconn.Dispose()
            }
            catch {
                # But if the routine fails, at least let them specify a database manually
                $databaselist = ""
            }

            # Reusable parameter setup
            $newparams = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
            $attributes = New-Object System.Management.Automation.ParameterAttribute
            $attributes.Mandatory = $false

            # Database list parameter setup
            $dbattributes = New-Object -Type System.Collections.ObjectModel.Collection[System.Attribute]
            $dbattributes.Add($attributes)
            # If a list of databases were returned, populate the parameter set
            if ($databaselist.length -gt 0) {
                $dbvalidationset = New-Object System.Management.Automation.ValidateSetAttribute -ArgumentList $databaselist
                $dbattributes.Add($dbvalidationset)
            }

            $Database = New-Object -Type System.Management.Automation.RuntimeDefinedParameter("Database", [String], $dbattributes)
            $newparams.Add("Database", $Database)
            return $newparams
        }
    }

    begin {
        function Get-Columns {
            <#
                .SYNOPSIS
                    TextFieldParser will be used instead of an OleDbConnection.
                    This is because the OleDbConnection driver may not exist on x64.

                .EXAMPLE
                    $columns = Get-Columns -Csv .\myfile.csv -Delimiter "," -FirstRowColumns $true

                .OUTPUTS
                    Array of column names
            #>

            param (
                [Parameter(Mandatory = $true)]
                [string[]]$Csv,
                [Parameter(Mandatory = $true)]
                [string]$Delimiter,
                [Parameter(Mandatory = $true)]
                [bool]$FirstRowColumns
            )

            $columnparser = New-Object Microsoft.VisualBasic.FileIO.TextFieldParser($csv[0])
            $columnparser.TextFieldType = "Delimited"
            $columnparser.SetDelimiters($Delimiter)
            $rawcolumns = $columnparser.ReadFields()

            if ($FirstRowColumns -eq $true) {
                $columns = ($rawcolumns | ForEach-Object { $_ -Replace '"' } | Select-Object -Property @{ Name = "name"; Expression = { "[$_]" } }).name
            }
            else {
                $columns = @()
                foreach ($number in 1..$rawcolumns.count) {
                    $columns += "[column$number]"
                }
            }

            $columnparser.Close()
            $columnparser.Dispose()
            return $columns
        }

        function Get-ColumnText {
            <#
                .SYNOPSIS
                    Returns an array of data, which can later be parsed for potential datatypes.

                .EXAMPLE
                    $columns = Get-Columns -Csv .\myfile.csv -Delimiter ","

                .OUTPUTS
                    Array of column data
             #>
            param (
                [Parameter(Mandatory = $true)]
                [string[]]$Csv,
                [Parameter(Mandatory = $true)]
                [string]$Delimiter
            )
            $columnparser = New-Object Microsoft.VisualBasic.FileIO.TextFieldParser($csv[0])
            $columnparser.TextFieldType = "Delimited"
            $columnparser.SetDelimiters($Delimiter)
            $line = $columnparser.ReadLine()
            # Skip a line, in case first line are column names
            $line = $columnparser.ReadLine()
            $datatext = $columnparser.ReadFields()
            $columnparser.Close()
            $columnparser.Dispose()
            return $datatext
        }

        function Write-Schemaini {
            <#
                .SYNOPSIS
                    Unfortunately, passing delimiter within the OleDBConnection connection string is unreliable, so we'll use schema.ini instead. The default delimiter in Windows changes depending on country, so we'll do this for every delimiter, even commas.

                    Get OLE datatypes based on best guess of column data within the -Columns parameter.

                    Sometimes SQL will accept a datetime that OLE won't, so Text will be used for datetime.

                .EXAMPLE
                    $columns = Get-Columns -Csv C:\temp\myfile.csv -Delimiter ","
                    $movedschemainis = Write-Schemaini -Csv  C:\temp\myfile.csv -Columns $columns -ColumnText $columntext -Delimiter "," -FirstRowColumns $true

                .OUTPUTS
                    Creates new schema files, that look something like this:

                    [housingdata.csv]
                    Format=Delimited(,)
                    ColNameHeader=True
                    Col1="House ID" Long
                    Col2="Description" Memo
                    Col3="Price" Double

                    Returns an array of existing schema files that have been moved, if any.
             #>
            param (
                [Parameter(Mandatory = $true)]
                [string[]]$Csv,
                [Parameter(Mandatory = $true)]
                [string[]]$Columns,
                [string[]]$ColumnText,
                [Parameter(Mandatory = $true)]
                [string]$Delimiter,
                [Parameter(Mandatory = $true)]
                [bool]$FirstRowColumns
            )

            $movedschemainis = @{ }
            foreach ($file in $csv) {
                $directory = Split-Path $file
                $schemaexists = Test-Path "$directory\schema.ini"
                if ($schemaexists -eq $true) {
                    $newschemaname = "$env:TEMP\$(Split-Path $file -leaf)-schema.ini"
                    $movedschemainis.Add($newschemaname, "$directory\schema.ini")
                    Move-Item "$directory\schema.ini" $newschemaname -Force
                }

                $filename = Split-Path $file -leaf; $directory = Split-Path $file
                Add-Content -Path "$directory\schema.ini" -Value "[$filename]"
                Add-Content -Path "$directory\schema.ini" -Value "Format=Delimited($InternalDelimiter)"
                Add-Content -Path "$directory\schema.ini" -Value "ColNameHeader=$FirstRowColumns"

                $index = 0
                $olecolumns = ($columns | ForEach-Object { $_ -Replace "\[|\]", '"' })

                foreach ($datatype in $columntext) {
                    $olecolumnname = $olecolumns[$index]
                    $index++

                    try {
                        [System.Guid]::Parse($datatype) | Out-Null; $isguid = $true
                    }
                    catch {
                        $isguid = $false
                    }

                    if ($isguid -eq $true) {
                        $oledatatype = "Text"
                    }
                    elseif ([int64]::TryParse($datatype, [ref]0) -eq $true) {
                        $oledatatype = "Long"
                    }
                    elseif ([double]::TryParse($datatype, [ref]0) -eq $true) {
                        $oledatatype = "Double"
                    }
                    elseif ([datetime]::TryParse($datatype, [ref]0) -eq $true) {
                        $oledatatype = "Text"
                    }
                    else {
                        $oledatatype = "Memo"
                    }

                    Add-Content -Path "$directory\schema.ini" -Value "Col$($index)`=$olecolumnname $oledatatype"
                }
            }
            return $movedschemainis
        }

        function New-SqlTable {
            <#
                .SYNOPSIS
                    Creates new Table using existing SqlCommand.

                    SQL datatypes based on best guess of column data within the -ColumnText parameter.
                    Columns parameter determine column names.

                .EXAMPLE
                    New-SqlTable -Csv $Csv -Delimiter $InternalDelimiter -Columns $columns -ColumnText $columntext -SqlConn $sqlconn -Transaction $transaction

                .OUTPUTS
                    Creates new table
            #>

            param (
                [Parameter(Mandatory = $true)]
                [string[]]$Csv,
                [Parameter(Mandatory = $true)]
                [string]$Delimiter,
                [string[]]$Columns,
                [string[]]$ColumnText,
                [System.Data.SqlClient.SqlConnection]$sqlconn,
                [System.Data.SqlClient.SqlTransaction]$transaction
            )
            # Get SQL datatypes by best guess on first data row
            $sqldatatypes = @(); $index = 0

            foreach ($column in $columntext) {
                $sqlcolumnname = $Columns[$index]
                $index++

                # bigint, float, and datetime are more accurate, but it didn't work
                # as often as it should have, so we'll just go for a smaller datatype
                if ([int64]::TryParse($column, [ref]0) -eq $true) {
                    $sqldatatype = "varchar(255)"
                }
                elseif ([double]::TryParse($column, [ref]0) -eq $true) {
                    $sqldatatype = "varchar(255)"
                }
                elseif ([datetime]::TryParse($column, [ref]0) -eq $true) {
                    $sqldatatype = "varchar(255)"
                }
                else {
                    $sqldatatype = "varchar(MAX)"
                }

                $sqldatatypes += "$sqlcolumnname $sqldatatype"
            }

            $sql = "BEGIN CREATE TABLE [$schema].[$table] ($($sqldatatypes -join ' NULL,')) END"
            $sqlcmd = New-Object System.Data.SqlClient.SqlCommand($sql, $sqlconn, $transaction)
            try {
                $null = $sqlcmd.ExecuteNonQuery()
            }
            catch {
                $errormessage = $_.Exception.Message.ToString()
                throw "Failed to execute $sql. `nDid you specify the proper delimiter? `n$errormessage"
            }

            Write-Output "[*] Successfully created table $schema.$table with the following column definitions:`n $($sqldatatypes -join "`n ")"
            # Write-Warning "All columns are created using a best guess, and use their maximum datatype."
            Write-Warning "This is inefficient but allows the script to import without issues."
            Write-Warning "Consider creating the table first using best practices if the data will be used in production."
        }


        if ($shellswitch -eq $false) { Write-Output "[*] Started at $(Get-Date)" }

        # Load the basics
        [void][Reflection.Assembly]::LoadWithPartialName("System.Data")
        [void][Reflection.Assembly]::LoadWithPartialName("Microsoft.VisualBasic")
        [void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

        # Getting the total rows copied is a challenge. Use SqlBulkCopyExtension.
        # http://stackoverflow.com/questions/1188384/sqlbulkcopy-row-count-when-complete

        $source = 'namespace System.Data.SqlClient
        {
            using Reflection;

            public static class SqlBulkCopyExtension
            {
                const String _rowsCopiedFieldName = "_rowsCopied";
                static FieldInfo _rowsCopiedField = null;

                public static int RowsCopiedCount(this SqlBulkCopy bulkCopy)
                {
                    if (_rowsCopiedField == null) _rowsCopiedField = typeof(SqlBulkCopy).GetField(_rowsCopiedFieldName, BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance);
                    return (int)_rowsCopiedField.GetValue(bulkCopy);
                }
            }
        }
    '
        Add-Type -ReferencedAssemblies 'System.Data.dll' -TypeDefinition $source -ErrorAction SilentlyContinue
    }

    process {
        # turbo mode requires a table lock, or it's just regular fast
        if ($turbo -eq $true) {
            $tablelock = $true
        }

        # Hack to get around the delimter parameter ValidateSet
        if ($SingleColumn -eq $true) {
            $InternalDelimiter = ''
        }
        else {
            $InternalDelimiter = $Delimiter
        }

        # The query parameter requires OleDB which is invoked by the "safe" variable
        # Actually, a select could be performed on the datatable used in StreamReader, too.
        # Maybe that will be done later.
        if ($query -ne "select * from csv") {
            $safe = $true
        }

        if ($first -gt 0 -and $query -ne "select * from csv") {
            throw "Cannot use both -Query and -First. If a query is necessary, use TOP $first within your SQL statement."
        }

        # In order to support -First in both Streamreader, and OleDb imports, the query must be modified slightly.
        if ($first -gt 0) {
            $query = "select top $first * from csv"
        }

        # If shell switch occured, and encrypted SQL credentials were written to disk, create $SqlCredential
        if ($SqlCredentialPath.length -gt 0) {
            $SqlCredential = Import-CliXml $SqlCredentialPath
        }

        # Get Database string from RuntimeDefinedParameter if required
        if ($database -isnot [string]) {
            $database = $PSBoundParameters.Database
        }
        if ($database.length -eq 0) {
            throw "You must specify a database."
        }

        # Check to ensure a Windows account wasn't used as a SQL Credential
        if ($SqlCredential.count -gt 0 -and $SqlCredential.UserName -like "*\*") {
            throw "Only SQL Logins can be used as a SqlCredential."
        }

        # If no CSV was specified, prompt the user to select one.
        if ($csv.length -eq 0) {
            $fd = New-Object System.Windows.Forms.OpenFileDialog
            $fd.InitialDirectory = [environment]::GetFolderPath("MyDocuments")
            $fd.Filter = "CSV Files (*.csv;*.tsv;*.txt)|*.csv;*.tsv;*.txt"
            $fd.Title = "Select one or more CSV files"
            $fd.MultiSelect = $true
            $null = $fd.showdialog()
            $csv = $fd.filenames
            if ($csv.length -eq 0) {
                throw "No CSV file selected."
            }
        }
        else {
            foreach ($file in $csv) {
                $exists = Test-Path $file
                if ($exists -eq $false) {
                    throw "$file does not exist"
                }
            }
        }

        # Resolve the full path of each CSV
        $resolvedcsv = @()
        foreach ($file in $csv) {
            $resolvedcsv += (Resolve-Path $file).ProviderPath
        }
        $csv = $resolvedcsv

        # UniqueIdentifier kills OLE DB / SqlBulkCopy imports. Check to see if destination table contains this datatype.
        if ($safe -eq $true) {
            $sqlcheckconn = New-Object System.Data.SqlClient.SqlConnection
            if ($SqlCredential.count -eq 0 -or $null -eq $SqlCredential) {
                $sqlcheckconn.ConnectionString = "Data Source=$SqlInstance;Integrated Security=True;Connection Timeout=3; Initial Catalog=master"
            }
            else {
                $username = ($SqlCredential.UserName).TrimStart("\")
                $sqlcheckconn.ConnectionString = "Data Source=$SqlInstance;User Id=$username; Password=$($SqlCredential.GetNetworkCredential().Password);Connection Timeout=3; Initial Catalog=master"
            }

            try {
                $sqlcheckconn.Open()
            }
            catch {
                throw $_.Exception
            }

            # Ensure database exists
            $sql = "select count(*) from master.dbo.sysdatabases where name = '$database'"
            $sqlcheckcmd = New-Object System.Data.SqlClient.SqlCommand($sql, $sqlcheckconn)
            $dbexists = $sqlcheckcmd.ExecuteScalar()
            if ($dbexists -eq $false) {
                throw "Database does not exist on $SqlInstance"
            }

            # Change database after the fact, because if db doesn't exist, the login would fail.
            $sqlcheckconn.ChangeDatabase($database)

            $sql = "SELECT t.name as datatype FROM sys.columns c
                JOIN sys.types t ON t.system_type_id = c.system_type_id
                WHERE c.object_id = object_id('$schema.$table') and t.name != 'sysname'"
            $sqlcheckcmd = New-Object System.Data.SqlClient.SqlCommand($sql, $sqlcheckconn)
            $sqlcolumns = New-Object System.Data.DataTable
            $sqlcolumns.load($sqlcheckcmd.ExecuteReader("CloseConnection"))
            $sqlcheckconn.Dispose()
            if ($sqlcolumns.datatype -contains "UniqueIdentifier") {
                throw "UniqueIdentifier not supported by OleDB/SqlBulkCopy. Query and Safe cannot be supported."
            }
        }

        if ($safe -eq $true) {
            # Check for drivers. First, ACE (Access) if file is smaller than 2GB, then JET
            # ACE doesn't handle files larger than 2gb. What gives?
            foreach ($file in $csv) {
                $filesize = (Get-ChildItem $file).Length / 1GB
                if ($filesize -gt 1.99) {
                    $jetonly = $true
                }
            }

            if ($jetonly -ne $true) {
                $provider = (New-Object System.Data.OleDb.OleDbEnumerator).GetElements() | Where-Object { $_.SOURCES_NAME -like "Microsoft.ACE.OLEDB.*" }
            }

            if ($null -eq $provider) {
                $provider = (New-Object System.Data.OleDb.OleDbEnumerator).GetElements() | Where-Object { $_.SOURCES_NAME -like "Microsoft.Jet.OLEDB.*" }
            }

            # If a suitable provider cannot be found (If x64 and Access hasn't been installed)
            # switch to x86, because it natively supports JET
            if ($null -ne $provider) {
                if ($provider -is [system.array]) {
                    $provider = $provider[$provider.GetUpperBound(0)].SOURCES_NAME
                }
                else {
                    $provider = $provider.SOURCES_NAME
                }
            }

            # If a provider doesn't exist, it is necessary to switch to x86 which natively supports JET.
            if ($null -eq $provider) {
                # While Install-Module takes care of installing modules to x86 and x64, Import-Module doesn't.
                # Because of this, the Module must be exported, written to file, and imported in the x86 shell.
                $definition = (Get-Command Import-DbaCsvToSql).Definition
                $function = "Function Import-DbaCsvToSql { $definition }"
                Set-Content "$env:TEMP\Import-DbaCsvToSql.psm1" $function

                # Encode the SQL string, since some characters may mess up after being passed a second time.
                $bytes = [System.Text.Encoding]::UTF8.GetBytes($query)
                $query = [System.Convert]::ToBase64String($bytes)

                # Put switches back into proper format
                $switches = @()
                $options = "TableLock", "CheckConstraints", "FireTriggers", "KeepIdentity", "KeepNulls", "Default", "Truncate", "FirstRowColumns", "Safe"
                foreach ($option in $options) {
                    $optionValue = Get-Variable $option -ValueOnly -ErrorAction SilentlyContinue
                    if ($optionValue -eq $true) {
                        $switches += "-$option"
                    }
                }

                # Perform the actual switch, which removes any registered Import-DbaCsvToSql modules
                # Then imports, and finally re-executes the command.
                $csv = $csv -join ","; $switches = $switches -join " "
                if ($SqlCredential.count -gt 0) {
                    $SqlCredentialPath = "$env:TEMP\sqlcredential.xml"
                    Export-CliXml -InputObject $SqlCredential $SqlCredentialPath
                }
                $command = "Import-DbaCsvToSql -Csv $csv -SqlInstance '$SqlInstance'-Database '$database' -Table '$table' -Delimiter '$InternalDelimiter' -First $First -Query '$query' -Batchsize $BatchSize -NotifyAfter $NotifyAfter $switches -shellswitch"

                if ($SqlCredentialPath.length -gt 0) {
                    $command += " -SqlCredentialPath $SqlCredentialPath"
                }
                Write-Verbose "Switching to x86 shell, then switching back."
                &"$env:windir\syswow64\windowspowershell\v1.0\powershell.exe" "$command"
                return
            }
        }

        # Do the first few lines contain the specified delimiter?
        foreach ($file in $csv) {
            try { $firstfewlines = Get-Content $file -First 3 -ErrorAction Stop }
            catch { throw "$file is in use." }
            if ($SingleColumn -ne $true ) {
                foreach ($line in $firstfewlines) {
                    if (($line -match $InternalDelimiter) -eq $false) {
                        throw "Delimiter $InternalDelimiter not found in first row of $file."
                    }
                }
            }
        }

        # If more than one csv specified, check to ensure number of columns match
        if ($csv -is [system.array]) {
            if ($SingleColumn -ne $true) {
                $numberofcolumns = ((Get-Content $csv[0] -First 1 -ErrorAction Stop) -Split $InternalDelimiter).Count

                foreach ($file in $csv) {
                    $firstline = Get-Content $file -First 1 -ErrorAction Stop
                    $newnumcolumns = ($firstline -Split $InternalDelimiter).Count
                    if ($newnumcolumns -ne $numberofcolumns) {
                        throw "Multiple csv file mismatch. Do both use the same delimiter and have the same number of columns?"
                    }
                }
            }
        }

        # Automatically generate Table name if not specified, then prompt user to confirm
        if ($table.length -eq 0) {
            $table = [IO.Path]::GetFileNameWithoutExtension($csv[0])

            #Count the dots in the file name.
            #1 dot, treat it as schema.table naming
            #2 or more dots, really should catch it as bad practice, but the rest of the script appears to let it pass
            if (($table.ToCharArray() | Where-Object {$_ -eq '.'} | Measure-Object).count -gt 0) {
                if (($schema -ne $table.Split('.')[0]) -and ($schema -ne 'dbo')) {
                    $title = "Conflicting schema names specified"
                    $message = "Please confirm which schema you want to use."
                    $schemaA = New-Object System.Management.Automation.Host.ChoiceDescription "&A - $schema", "Use schema name $schema for import."
                    $schemaB = New-Object System.Management.Automation.Host.ChoiceDescription "&B - $($table.Split('.')[0])", "Use schema name $($table.Split('.')[0]) for import."
                    $options = [System.Management.Automation.Host.ChoiceDescription[]]($schemaA, $schemaB)
                    $result = $host.ui.PromptForChoice($title, $message, $options, 0)
                    if ($result -eq 1) {
                        $schema = $table.Split('.')[0]
                        $tmparray = $table.split('.')
                        $table = $tmparray[1..$tmparray.Length] -join '.'
                    }
                }

            }
            else {
                $title = "Table name not specified."
                $message = "Would you like to use the automatically generated name: $table"
                $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Uses table name $table for import."
                $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Allows you to specify an alternative table name."
                $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
                $result = $host.ui.PromptForChoice($title, $message, $options, 0)
                if ($result -eq 1) {
                    do {
                        $table = Read-Host "Please enter a table name"
                    }
                    while ($table.Length -eq 0)
                }

            }
        }

        # If the shell has switched, decode the $query string.
        if ($shellswitch -eq $true) {
            $bytes = [System.Convert]::FromBase64String($Query)
            $query = [System.Text.Encoding]::UTF8.GetString($bytes)
            $csv = $csv -Split ","
        }

        # Create columns based on first data row of first csv.
        if ($SingleColumn -ne $true) {
            Write-Output "[*] Calculating column names and datatypes"
            $columns = Get-Columns -Csv $Csv -Delimiter $InternalDelimiter -FirstRowColumns $FirstRowColumns
            if ($columns.count -gt 255 -and $safe -eq $true) {
                throw "CSV must contain fewer than 256 columns."
            }
        }

        if ($SingleColumn -ne $true) {
            $columntext = Get-ColumnText -Csv $Csv -Delimiter $InternalDelimiter
        }

        # OLEDB method requires extra checks
        if ($safe -eq $true) {
            # Advanced SQL queries may not work (SqlBulkCopy likes a 1 to 1 mapping), so warn the user.
            if ($Query -match "GROUP BY" -or $Query -match "COUNT") {
                Write-Warning "Script doesn't really support the specified query. This probably won't work, but will be attempted anyway."
            }

            # Check for proper SQL syntax, which for the purposes of this module must include the word "table"
            if ($query.ToLower() -notmatch "\bcsv\b") {
                throw "SQL statement must contain the word 'csv'. Please see this module's documentation for more details."
            }

            # In order to ensure consistent results, a schema.ini file must be created.
            # If a schema.ini already exists, it will be moved to TEMP temporarily.
            Write-Verbose "Creating schema.ini"
            $movedschemainis = Write-Schemaini -Csv $Csv -Columns $columns -Delimiter "$InternalDelimiter" -FirstRowColumns $FirstRowColumns -ColumnText $columntext
        }

        # Display SQL Server Login info
        if ($sqlcredential.count -gt 0) {
            $username = "SQL login $($SqlCredential.UserName)"
        }
        else {
            $username = "Windows login $(whoami)"
        }
        # Open Connection to SQL Server
        Write-Output "[*] Logging into $SqlInstance as $username"
        $sqlconn = New-Object System.Data.SqlClient.SqlConnection
        if ($SqlCredential.count -eq 0) {
            $sqlconn.ConnectionString = "Data Source=$SqlInstance;Integrated Security=True;Connection Timeout=3; Initial Catalog=master"
        }
        else {
            $sqlconn.ConnectionString = "Data Source=$SqlInstance;User Id=$($SqlCredential.UserName); Password=$($SqlCredential.GetNetworkCredential().Password);Connection Timeout=3; Initial Catalog=master"
        }

        try {
            $sqlconn.Open()
        }
        catch {
            throw "Could not open SQL Server connection. Is $SqlInstance online?"
        }

        # Everything will be contained within 1 transaction, even creating a new table if required
        # and truncating the table, if specified.
        $transaction = $sqlconn.BeginTransaction()

        # Ensure database exists
        $sql = "select count(*) from master.dbo.sysdatabases where name = '$database'"
        $sqlcmd = New-Object System.Data.SqlClient.SqlCommand($sql, $sqlconn, $transaction)
        $dbexists = $sqlcmd.ExecuteScalar()
        if ($dbexists -eq $false) {
            throw "Database does not exist on $SqlInstance"
        }
        Write-Output "[*] Database exists"

        $sqlconn.ChangeDatabase($database)

        # Enure Schema exists
        $sql = "select count(*) from $database.sys.schemas where name='$schema'"
        $sqlcmd = New-Object System.Data.SqlClient.SqlCommand($sql, $sqlconn, $transaction)
        $schemaexists = $sqlcmd.ExecuteScalar()

        # If Schema doesn't exist create it
        # Defaulting to dbo.
        if ($schemaexists -eq $false) {
            Write-Output "[*] Creating schema $schema"
            $sql = "CREATE SCHEMA [$schema] AUTHORIZATION dbo"
            $sqlcmd = New-Object System.Data.SqlClient.SqlCommand($sql, $sqlconn, $transaction)
            try {
                $null = $sqlcmd.ExecuteNonQuery()
            }
            catch {
                Write-Warning "Could not create $schema"
            }

        }

        # Ensure table exists
        $sql = "select count(*) from $database.sys.tables where name = '$table' and schema_id=schema_id('$schema')"
        $sqlcmd = New-Object System.Data.SqlClient.SqlCommand($sql, $sqlconn, $transaction)
        $tablexists = $sqlcmd.ExecuteScalar()

        # Create the table if required. Remember, this will occur within a transaction, so if the script fails, the
        # new table will no longer exist.
        if ($tablexists -eq $false) {
            Write-Output "[*] Table does not exist"
            Write-Output "[*] Creating table"
            New-SqlTable -Csv $Csv -Delimiter $InternalDelimiter -Columns $columns -ColumnText $columntext -SqlConn $sqlconn -Transaction $transaction
        }
        else {
            Write-Output "[*] Table exists"
        }

        # Truncate if specified. Remember, this will occur within a transaction, so if the script fails, the
        # truncate will not be committed.
        if ($truncate -eq $true) {
            Write-Output "[*] Truncating table"
            $sql = "TRUNCATE TABLE [$schema].[$table]"
            $sqlcmd = New-Object System.Data.SqlClient.SqlCommand($sql, $sqlconn, $transaction)
            try {
                $null = $sqlcmd.ExecuteNonQuery()
            }
            catch {
                Write-Warning "Could not truncate $schema.$table"
            }
        }

        # Get columns for column mapping
        if ($null -eq $columnMappings) {
            $olecolumns = ($columns | ForEach-Object { $_ -Replace "\[|\]" })
            $sql = "select name from sys.columns where object_id = object_id('$schema.$table') order by column_id"
            $sqlcmd = New-Object System.Data.SqlClient.SqlCommand($sql, $sqlconn, $transaction)
            $sqlcolumns = New-Object System.Data.DataTable
            $sqlcolumns.Load($sqlcmd.ExecuteReader())
        }

        # Time to import!
        $elapsed = [System.Diagnostics.Stopwatch]::StartNew()

        # Process each CSV file specified
        foreach ($file in $csv) {

            # Dynamically set NotifyAfter if it wasn't specified
            if ($notifyAfter -eq 0) {
                if ($resultcount -is [int]) {
                    $notifyafter = $resultcount / 10
                }
                else {
                    $notifyafter = 50000
                }
            }

            # Setup bulk copy
            Write-Output "[*] Starting bulk copy for $(Split-Path $file -Leaf)"

            # Setup bulk copy options
            $bulkCopyOptions = @()
            $options = "TableLock", "CheckConstraints", "FireTriggers", "KeepIdentity", "KeepNulls", "Default", "Truncate"
            foreach ($option in $options) {
                $optionValue = Get-Variable $option -ValueOnly -ErrorAction SilentlyContinue
                if ($optionValue -eq $true) {
                    $bulkCopyOptions += "$option"
                }
            }
            $bulkCopyOptions = $bulkCopyOptions -join " & "

            # Create SqlBulkCopy using default options, or options specified in command line.
            if ($bulkCopyOptions.count -gt 1) {
                $bulkcopy = New-Object Data.SqlClient.SqlBulkCopy($oleconnstring, $bulkCopyOptions, $transaction)
            }
            else {
                $bulkcopy = New-Object Data.SqlClient.SqlBulkCopy($sqlconn, "Default", $transaction)
            }

            $bulkcopy.DestinationTableName = "[$schema].[$table]"
            $bulkcopy.bulkcopyTimeout = 0
            $bulkCopy.BatchSize = $BatchSize
            $bulkCopy.NotifyAfter = $NotifyAfter

            if ($safe -eq $true) {
                # Setup bulkcopy mappings
                for ($columnid = 0; $columnid -lt $sqlcolumns.rows.count; $columnid++) {
                    $null = $bulkCopy.ColumnMappings.Add($olecolumns[$columnid], $sqlcolumns.rows[$columnid].ItemArray[0])
                }

                # Setup the connection string. Data Source is the directory that contains the csv.
                # The file name is also the table name, but with a "#" instead of a "."
                $datasource = Split-Path $file
                $tablename = (Split-Path $file -leaf).Replace(".", "#")
                $oleconnstring = "Provider=$provider;Data Source=$datasource;Extended Properties='text';"

                # To make command line queries easier, let the user just specify "csv" instead of the
                # OleDbconnection formatted name (file.csv -> file#csv)
                $sql = $Query -replace "\bcsv\b", " [$tablename]"

                # Setup the OleDbconnection
                $oleconn = New-Object System.Data.OleDb.OleDbconnection
                $oleconn.ConnectionString = $oleconnstring

                # Setup the OleDBCommand
                $olecmd = New-Object System.Data.OleDB.OleDBCommand
                $olecmd.Connection = $oleconn
                $olecmd.CommandText = $sql

                try {
                    $oleconn.Open()
                }
                catch {
                    throw "Could not open OLEDB connection."
                }

                # Attempt to get the number of results so that a nice progress bar can be displayed.
                # This takes extra time, and files over 100MB take too long, so just skip them.
                if ($sql -match "GROUP BY") {
                    Write-Warning -Message "Query contains GROUP BY clause. Skipping result count."
                }
                else {
                    Write-Output "[*] Determining total rows to be copied. This may take a few seconds."
                }

                if ($sql -match "\bselect top\b") {
                    try {
                        $split = $sql -split "\bselect top \b"
                        $resultcount = [int]($split[1].Trim().Split()[0])
                        Write-Output "[*] Attempting to fetch $resultcount rows"
                    }
                    catch {
                        Write-Warning "Couldn't determine total rows to be copied."
                    }
                }
                elseif ($sql -notmatch "GROUP BY") {
                    $filesize = (Get-ChildItem $file).Length / 1MB
                    if ($filesize -lt 100) {
                        try {
                            $split = $sql -split "\bfrom\b"
                            $sqlcount = "select count(*) from $($split[1])"
                            # Setup the OleDBCommand
                            $olecmd = New-Object System.Data.OleDB.OleDBCommand
                            $olecmd.Connection = $oleconn
                            $olecmd.CommandText = $sqlcount
                            $resultcount = [int]($olecmd.ExecuteScalar())
                            Write-Output "[*] $resultcount rows will be copied"
                        }
                        catch {
                            Write-Warning "Couldn't determine total rows to be copied"
                        }
                    }
                    else {
                        Write-Output "[*] File is too large for efficient result count; progress bar will not be shown."
                    }
                }
            }

            # Write to server :D
            try {
                if ($safe -ne $true) {
                    # Check to ensure batchsize isn't equal to 0
                    if ($batchsize -eq 0) {
                        write-warning "Invalid batchsize for this operation. Increasing to 50k"
                        $batchsize = 50000
                    }

                    # Open the text file from disk
                    $reader = New-Object System.IO.StreamReader($file)
                    if ($FirstRowColumns -eq $true) {
                        $null = $reader.readLine()
                    }

                    # Create the reusable datatable. Columns will be genereated using info from SQL.
                    $datatable = New-Object System.Data.DataTable

                    # Get table column info from SQL Server
                    $sql = "SELECT c.name as colname, t.name as datatype, c.max_length, c.is_nullable FROM sys.columns c
                        JOIN sys.types t ON t.system_type_id = c.system_type_id
                        WHERE c.object_id = object_id('$schema.$table') and t.name != 'sysname'
                        order by c.column_id"
                    $sqlcmd = New-Object System.Data.SqlClient.SqlCommand($sql, $sqlconn, $transaction)
                    $sqlcolumns = New-Object System.Data.DataTable
                    $sqlcolumns.load($sqlcmd.ExecuteReader())

                    foreach ($sqlcolumn in $sqlcolumns) {
                        $datacolumn = $datatable.Columns.Add()
                        $colname = $sqlcolumn.colname
                        $datacolumn.AllowDBNull = $sqlcolumn.is_nullable
                        $datacolumn.ColumnName = $colname
                        $datacolumn.DefaultValue = [DBnull]::Value
                        $datacolumn.Datatype = [string]

                        # The following data types can sometimes cause issues when they are null
                        # so we will treat them differently
                        $convert = "bigint", "DateTimeOffset", "UniqueIdentifier", "smalldatetime", "datetime"
                        if ($convert -notcontains $sqlcolumn.datatype -and $turbo -ne $true) {
                            $null = $bulkCopy.ColumnMappings.Add($datacolumn.ColumnName, $sqlcolumn.colname)
                        }
                    }
                    # For the columns that cause trouble, we'll add an additional column to the datatable
                    # which will perform a conversion.
                    # Setting $column.datatype alone doesn't work as well as setting+converting.
                    if ($turbo -ne $true) {
                        $calcolumns = $sqlcolumns | Where-Object { $convert -contains $_.datatype }
                        foreach ($calcolumn in $calcolumns) {
                            $colname = $calcolumn.colname
                            $null = $newcolumn = $datatable.Columns.Add()
                            $null = $newcolumn.ColumnName = "computed$colname"
                            switch ($calcolumn.datatype) {
                                "bigint" {
                                    $netdatatype = "System.Int64";
                                    $newcolumn.Datatype = [int64]
                                }
                                "DateTimeOffset" {
                                    $netdatatype = "System.DateTimeOffset";
                                    $newcolumn.Datatype = [DateTimeOffset]
                                }
                                "UniqueIdentifier" {
                                    $netdatatype = "System.Guid";
                                    $newcolumn.Datatype = [Guid]
                                }
                                {"smalldatetime", "datetime" -contains $_ } {
                                    $netdatatype = "System.DateTime";
                                    $newcolumn.Datatype = [DateTime]
                                }
                            }
                            # Use a data column expression to facilitate actual conversion
                            $null = $newcolumn.Expression = "Convert($colname, $netdatatype)"
                            $null = $bulkCopy.ColumnMappings.Add($newcolumn.ColumnName, $calcolumn.colname)
                        }
                    }

                    # Check to see if file has quote identified data (ie. "first","second","third")
                    $quoted = $false
                    $checkline = Get-Content $file -Last 1
                    $checkcolumns = $checkline.Split($InternalDelimiter)
                    foreach ($checkcolumn in $checkcolumns) {
                        if ($checkcolumn.StartsWith('"') -and $checkcolumn.EndsWith('"')) {
                            $quoted = $true
                        }
                    }

                    if ($quoted -eq $true) {
                        Write-Warning "The CSV file appears to use quoted identifiers. This may take a little longer."
                        # Thanks for this, Chris! http://www.schiffhauer.com/c-split-csv-values-with-a-regular-expression/
                        $pattern = "((?<=`")[^`"]*(?=`"($InternalDelimiter|$)+)|(?<=$InternalDelimiter|^)[^$InternalDelimiter`"]*(?=$InternalDelimiter|$))"
                    }
                    if ($turbo -eq $true -and $first -eq 0) {
                        while ($null -ne ($line = $reader.ReadLine())) {
                            $i++
                            if ($quoted -eq $true) {
                                $null = $datatable.Rows.Add(($line.TrimStart('"').TrimEnd('"')) -Split "`"$InternalDelimiter`"")
                            }
                            else {
                                $row = $datatable.Rows.Add($line.Split($InternalDelimiter))
                            }

                            if (($i % $batchsize) -eq 0) {
                                $bulkcopy.WriteToServer($datatable)
                                Write-Output "[*] $i rows have been inserted in $([math]::Round($elapsed.Elapsed.TotalSeconds, 2)) seconds."
                                $datatable.Clear()
                            }
                        }
                    }
                    else {
                        if ($turbo -eq $true -and $first -gt 0) { Write-Warning -Message "Using -First makes turbo a little slower." }
                        # Start import!
                        while ($null -ne ($line = $reader.ReadLine())) {
                            $i++
                            try {
                                if ($quoted -eq $true) {
                                    $row = $datatable.Rows.Add(($line.TrimStart('"').TrimEnd('"')) -Split $pattern)
                                }
                                else {
                                    $row = $datatable.Rows.Add($line.Split($InternalDelimiter))
                                }
                            }
                            catch {
                                $row = $datatable.NewRow()
                                try {
                                    $tempcolumn = $line.Split($InternalDelimiter)
                                    $colnum = 0
                                    foreach ($column in $tempcolumn) {
                                        if ($column.length -ne 0) {
                                            $row.item($colnum) = $column
                                        }
                                        else {
                                            $row.item($colnum) = [DBnull]::Value
                                        }
                                        $colnum++
                                    }
                                    $newrow = $datatable.Rows.Add($row)
                                }
                                catch {
                                    Write-Warning "The following line ($i) is causing issues:"
                                    Write-Output $line.Replace($InternalDelimiter, "`n")

                                    if ($quoted -eq $true) {
                                        Write-Warning "The import has failed, likely because the quoted data was a little too inconsistent. Try using the -Safe parameter."
                                    }

                                    Write-Verbose "Column datatypes:"
                                    foreach ($c in $datatable.columns) {
                                        Write-Verbose "$($c.columnname) = $($c.datatype)"
                                    }
                                    Write-Error $_.Exception.Message
                                    break
                                }
                            }

                            if (($i % $batchsize) -eq 0 -or $i -eq $first) {
                                $bulkcopy.WriteToServer($datatable)
                                Write-Output "[*] $i rows have been inserted in $([math]::Round($elapsed.Elapsed.TotalSeconds, 2)) seconds."
                                $datatable.Clear()
                                if ($i -eq $first) {
                                    break
                                }
                            }
                        }
                    }
                    # Add in all the remaining rows since the last clear
                    if ($datatable.Rows.Count -gt 0) {
                        $bulkcopy.WriteToServer($datatable)
                        $datatable.Clear()
                    }
                }
                else {
                    # Add rowcount output
                    $bulkCopy.Add_SqlRowscopied( {
                            $script:totalrows = $args[1].RowsCopied
                            if ($resultcount -is [int]) {
                                $percent = [int](($script:totalrows / $resultcount) * 100)
                                $timetaken = [math]::Round($elapsed.Elapsed.TotalSeconds, 2)
                                Write-Progress -id 1 -activity "Inserting $resultcount rows" -percentcomplete $percent -status ([System.String]::Format("Progress: {0} rows ({1}%) in {2} seconds", $script:totalrows, $percent, $timetaken))
                            }
                            else {
                                Write-Host "$($script:totalrows) rows copied in $([math]::Round($elapsed.Elapsed.TotalSeconds, 2)) seconds."
                            }
                        })

                    $bulkCopy.WriteToServer($olecmd.ExecuteReader("SequentialAccess"))
                    if ($resultcount -is [int]) {
                        Write-Progress -id 1 -activity "Inserting $resultcount rows" -status "Complete" -Completed
                    }

                }
                $completed = $true
            }
            catch {
                # If possible, give more information about common errors.
                if ($resultcount -is [int]) { Write-Progress -id 1 -activity "Inserting $resultcount rows" -status "Failed" -Completed }
                $errormessage = $_.Exception.Message.ToString()
                $completed = $false
                if ($errormessage -like "*for one or more required parameters*") {

                    Write-Error -Message "Looks like your SQL syntax may be invalid. `nCheck the documentation for more information or start with a simple -Query 'select top 10 * from csv'."
                    Write-Error -Message "Valid CSV columns are $columns."

                }
                elseif ($errormessage -match "invalid column length") {

                    # Get more information about malformed CSV input
                    $pattern = @("\d+")
                    $match = [regex]::matches($errormessage, @("\d+"))
                    $index = [int]($match.groups[1].Value) - 1
                    $sql = "select name, max_length from sys.columns where object_id = object_id('$table') and column_id = $index"
                    $sqlcmd = New-Object System.Data.SqlClient.SqlCommand($sql, $sqlconn, $transaction)
                    $datatable = New-Object System.Data.DataTable
                    $datatable.load($sqlcmd.ExecuteReader())
                    $column = $datatable.name
                    $length = $datatable.max_length

                    if ($safe -eq $true) {
                        Write-Warning "Column $index ($column) contains data with a length greater than $length."
                        Write-Warning "SqlBulkCopy makes it pretty much impossible to know which row caused the issue, but it's somewhere after row $($script:totalrows)."
                    }
                }
                elseif ($errormessage -match "does not allow DBNull" -or $errormessage -match "The given value of type") {

                    if ($tablexists -eq $false) {
                        Write-Error "Looks like the datatype prediction didn't work out. Please create the table manually with proper datatypes then rerun the import script."
                    }
                    else {
                        $sql = "select name from sys.columns where object_id = object_id('$table') order by column_id"
                        $sqlcmd = New-Object System.Data.SqlClient.SqlCommand($sql, $sqlconn, $transaction)
                        $datatable = New-Object System.Data.DataTable
                        $datatable.Load($sqlcmd.ExecuteReader())
                        $olecolumns = ($columns | ForEach-Object { $_ -Replace "\[|\]" }) -join ', '
                        Write-Warning "Datatype mismatch."
                        Write-Output "[*] This is sometimes caused by null handling in SqlBulkCopy, quoted data, or the first row being column names and not data (-FirstRowColumns)."
                        Write-Output "[*] This could also be because the data types don't match or the order of the columns within the CSV/SQL statement "
                        Write-Output "[*] do not line up with the order of the table within the SQL Server.`n"
                        Write-Output "[*] CSV order: $olecolumns`n"
                        Write-Output "[*] SQL order: $($datatable.rows.name -join ', ')`n"
                        Write-Output "[*] If this is the case, you can reorder columns by using the -Query parameter or execute the import against a view.`n"
                        if ($safe -eq $false) {
                            Write-Output "[*] You can also try running this import using the -Safe parameter, which handles quoted text well.`n"
                        }
                        Write-Error "`n$errormessage"
                    }


                }
                elseif ($errormessage -match "Input string was not in a correct format" -or $errormessage -match "The given ColumnName") {
                    Write-Warning "CSV contents may be malformed."
                    Write-Error $errormessage
                }
                else { Write-Error $errormessage }
            }
        }

        if ($completed -eq $true) {
            # "Note: This count does not take into consideration the number of rows actually inserted when Ignore Duplicates is set to ON."
            $null = $transaction.Commit()

            if ($safe -eq $false) {
                Write-Output "[*] $i total rows copied"
            }
            else {
                $total = [System.Data.SqlClient.SqlBulkCopyExtension]::RowsCopiedCount($bulkcopy)
                Write-Output "[*] $total total rows copied"
            }
        }
        else {
            Write-Output "[*] Transaction rolled back."
            Write-Output "[*] (Was the proper parameter specified? Is the first row the column name?)."
        }

        # Script is finished. Show elapsed time.
        $totaltime = [math]::Round($elapsed.Elapsed.TotalSeconds, 2)
        Write-Output "[*] Total Elapsed Time for bulk insert: $totaltime seconds"
    }

    End {
        # Close everything just in case & ignore errors
        try {
            $null = $sqlconn.close(); $null = $sqlconn.Dispose(); $null = $oleconn.close;
            $null = $olecmd.Dispose(); $null = $oleconn.Dispose(); $null = $bulkCopy.close();
            $null = $bulkcopy.dispose(); $null = $reader.close; $null = $reader.dispose()
        }
        catch {

        }

        # Delete all the temp files
        if ($SqlCredentialPath.length -gt 0) {
            if ((Test-Path $SqlCredentialPath) -eq $true) {
                $null = cmd /c "del $SqlCredentialPath"
            }
        }

        if ($shellswitch -eq $false -and $safe -eq $true) {
            # Delete new schema files
            Write-Verbose "Removing automatically generated schema.ini."
            foreach ($file in $csv) {
                $directory = Split-Path $file
                $null = cmd /c "del $directory\schema.ini" | Out-Null
            }

            # If a shell switch occured, delete the temporary module file.
            if ((Test-Path "$env:TEMP\Import-DbaCsvToSql.psm1") -eq $true) {
                cmd /c "del $env:TEMP\Import-DbaCsvToSql.psm1" | Out-Null
            }

            # Move original schema.ini's back if they existed
            if ($movedschemainis.count -gt 0) {
                foreach ($item in $movedschemainis) {
                    Write-Verbose "Moving $($item.keys) back to $($item.values)."
                    $null = cmd /c "move $($item.keys) $($item.values)"
                }
            }
            Write-Output "[*] Finished at $(Get-Date)"
        }
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Import-CsvToSql
    }
}
function Import-DbaPfDataCollectorSetTemplate {
    <#
        .SYNOPSIS
            Imports a new Performance Monitor Data Collector Set Template either from the dbatools repository or a file you specify.

        .DESCRIPTION
            Imports a new Performance Monitor Data Collector Set Template either from the dbatools repository or a file you specify.
            When importing data collector sets from the local instance, Run As Admin is required.

            Note: The included counters will be added for all SQL instances on the machine by default.
            For specific instances in addition to the default, use -Instance.

            See https://msdn.microsoft.com/en-us/library/windows/desktop/aa371952 for more information

        .PARAMETER ComputerName
            The target computer. Defaults to localhost.

        .PARAMETER Credential
            Allows you to login to servers using alternative credentials. To use:

            $scred = Get-Credential, then pass $scred object to the -Credential parameter.

        .PARAMETER Path
            The path to the xml file or files.

        .PARAMETER Template
            From one or more of the templates from the dbatools repository. Press Tab to cycle through the available options.

        .PARAMETER RootPath
            Sets the base path where the subdirectories are created.

        .PARAMETER DisplayName
            Sets the display name of the data collector set.

        .PARAMETER SchedulesEnabled
            If this switch is enabled, sets a value that indicates whether the schedules are enabled.

        .PARAMETER Segment
            Sets a value that indicates whether PLA creates new logs if the maximum size or segment duration is reached before the data collector set is stopped.

        .PARAMETER SegmentMaxDuration
            Sets the duration that the data collector set can run before it begins writing to new log files.

        .PARAMETER SegmentMaxSize
            Sets the maximum size of any log file in the data collector set.

        .PARAMETER Subdirectory
            Sets a base subdirectory of the root path where the next instance of the data collector set will write its logs.

        .PARAMETER SubdirectoryFormat
            Sets flags that describe how to decorate the subdirectory name. PLA appends the decoration to the folder name. For example, if you specify plaMonthDayHour, PLA appends the current month, day, and hour values to the folder name. If the folder name is MyFile, the result could be MyFile110816.

        .PARAMETER SubdirectoryFormatPattern
            Sets a format pattern to use when decorating the folder name. Default is 'yyyyMMdd\-NNNNNN'.

        .PARAMETER Task
            Sets the name of a Task Scheduler job to start each time the data collector set stops, including between segments.

        .PARAMETER TaskRunAsSelf
            If this switch is enabled, sets a value that determines whether the task runs as the data collector set user or as the user specified in the task.

        .PARAMETER TaskArguments
            Sets the command-line arguments to pass to the Task Scheduler job specified in the IDataCollectorSet::Task property.
            See https://msdn.microsoft.com/en-us/library/windows/desktop/aa371992 for more information.

        .PARAMETER TaskUserTextArguments
            Sets the command-line arguments that are substituted for the {usertext} substitution variable in the IDataCollectorSet::TaskArguments property.
            See https://msdn.microsoft.com/en-us/library/windows/desktop/aa371993 for more information.

        .PARAMETER StopOnCompletion
            If this switch is enabled, sets a value that determines whether the data collector set stops when all the data collectors in the set are in a completed state.

        .PARAMETER Instance
            By default, the template will be applied to all instances. If you want to set specific ones in addition to the default, supply just the instance name.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Performance, DataCollector, PerfCounter
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Import-DbaPfDataCollectorSetTemplate

        .EXAMPLE
            Import-DbaPfDataCollectorSetTemplate -ComputerName sql2017 -Template 'Long Running Query'

            Creates a new data collector set named 'Long Running Query' from the dbatools repository on the SQL Server sql2017.

        .EXAMPLE
            Import-DbaPfDataCollectorSetTemplate -ComputerName sql2017 -Template 'Long Running Query' -DisplayName 'New Long running query' -Confirm

            Creates a new data collector set named "New Long Running Query" using the 'Long Running Query' template. Forces a confirmation if the template exists.

        .EXAMPLE
            Get-DbaPfDataCollectorSet -ComputerName sql2017 -Session db_ola_health | Remove-DbaPfDataCollectorSet
            Import-DbaPfDataCollectorSetTemplate -ComputerName sql2017 -Template db_ola_health | Start-DbaPfDataCollectorSet

            Imports a session if it exists, then recreates it using a template.

        .EXAMPLE
            Get-DbaPfDataCollectorSetTemplate | Out-GridView -PassThru | Import-DbaPfDataCollectorSetTemplate -ComputerName sql2017

            Allows you to select a Session template then import to an instance named sql2017.

        .EXAMPLE
            Import-DbaPfDataCollectorSetTemplate -ComputerName sql2017 -Template 'Long Running Query' -Instance SHAREPOINT

            Creates a new data collector set named 'Long Running Query' from the dbatools repository on the SQL Server sql2017 for both the default and the SHAREPOINT instance.

            If you'd like to remove counters for the default instance, use Remove-DbaPfDataCollectorCounter.
    #>
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Low")]
    param (
        [parameter(ValueFromPipeline)]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [string]$DisplayName,
        [switch]$SchedulesEnabled,
        [string]$RootPath,
        [switch]$Segment,
        [int]$SegmentMaxDuration,
        [int]$SegmentMaxSize,
        [string]$Subdirectory,
        [int]$SubdirectoryFormat = 3,
        [string]$SubdirectoryFormatPattern = 'yyyyMMdd\-NNNNNN',
        [string]$Task,
        [switch]$TaskRunAsSelf,
        [string]$TaskArguments,
        [string]$TaskUserTextArguments,
        [switch]$StopOnCompletion,
        [parameter(ValueFromPipelineByPropertyName)]
        [Alias("FullName")]
        [string[]]$Path,
        [string[]]$Template,
        [string[]]$Instance,
        [switch]$EnableException
    )
    begin {
        $metadata = Import-Clixml "$script:PSModuleRoot\bin\perfmontemplates\collectorsets.xml"

        $setscript = {
            $setname = $args[0]; $templatexml = $args[1]
            $collectorset = New-Object -ComObject Pla.DataCollectorSet
            $collectorset.SetXml($templatexml)
            $null = $collectorset.Commit($setname, $null, 0x0003) #add or modify.
            $null = $collectorset.Query($setname, $Null)
        }

        $instancescript = {
            $services = Get-Service -DisplayName *sql* | Select-Object -ExpandProperty DisplayName
            [regex]::matches($services, '(?<=\().+?(?=\))').Value | Where-Object { $PSItem -ne 'MSSQLSERVER' } | Select-Object -Unique
        }
    }
    process {
        if ((Test-Bound -ParameterName Path -Not) -and (Test-Bound -ParameterName Template -Not)) {
            Stop-Function -Message "You must specify Path or Template"
        }

        if (($Path.Count -gt 1 -or $Template.Count -gt 1) -and (Test-Bound -ParameterName Template)) {
            Stop-Function -Message "Name cannot be specified with multiple files or templates because the Session will already exist"
        }

        foreach ($computer in $ComputerName) {
            $null = Test-ElevationRequirement -ComputerName $computer -Continue

            foreach ($file in $template) {
                $templatepath = "$script:PSModuleRoot\bin\perfmontemplates\collectorsets\$file.xml"
                if ((Test-Path $templatepath)) {
                    $Path += $templatepath
                }
                else {
                    Stop-Function -Message "Invalid template ($templatepath does not exist)" -Continue
                }
            }

            foreach ($file in $Path) {

                if ((Test-Bound -ParameterName DisplayName -Not)) {
                    Set-Variable -Name DisplayName -Value (Get-ChildItem -Path $file).BaseName
                }

                $Name = $DisplayNameUnresolved = $DisplayName

                Write-Message -Level Verbose -Message "Processing $file for $computer"

                if ((Test-Bound -ParameterName RootPath -Not)) {
                    Set-Variable -Name RootName -Value "%systemdrive%\PerfLogs\Admin\$Name"
                }

                # Perform replace
                $temp = ([System.IO.Path]::GetTempPath()).TrimEnd("").TrimEnd("\")
                $tempfile = "$temp\import-dbatools-perftemplate.xml"

                try {
                    # Get content
                    $contents = Get-Content $file -ErrorAction Stop

                    # Replace content
                    $replacements = 'RootPath', 'DisplayName', 'SchedulesEnabled', 'Segment', 'SegmentMaxDuration', 'SegmentMaxSize', 'SubdirectoryFormat', 'SubdirectoryFormatPattern', 'Task', 'TaskRunAsSelf', 'TaskArguments', 'TaskUserTextArguments', 'StopOnCompletion', 'DisplayNameUnresolved'

                    foreach ($replacement in $replacements) {
                        $phrase = "<$replacement></$replacement>"
                        $value = (Get-Variable -Name $replacement).Value
                        if ($value -eq $false) {
                            $value = "0"
                        }
                        if ($value -eq $true) {
                            $value = "1"
                        }
                        $replacephrase = "<$replacement>$value</$replacement>"
                        $contents = $contents.Replace($phrase, $replacephrase)
                    }

                    # Set content
                    $null = Set-Content -Path $tempfile -Value $contents -Encoding Unicode
                    $xml = [xml](Get-Content $tempfile -ErrorAction Stop)
                    $plainxml = Get-Content $tempfile -ErrorAction Stop -Raw
                    $file = $tempfile
                }
                catch {
                    Stop-Function -Message "Failure" -ErrorRecord $_ -Target $file -Continue
                }
                if (-not $xml.DataCollectorSet) {
                    Stop-Function -Message "$file is not a valid Performance Monitor template document" -Continue
                }

                try {
                    Write-Message -Level Verbose -Message "Importing $file as $name "
                    Write-Message -Level Verbose -Message "Connecting to $computer using Invoke-Command"

                    if ($instance) {
                        $instances = $instance
                    }
                    else {
                        $instances = Invoke-Command2 -ComputerName $computer -Credential $Credential -ScriptBlock $instancescript -ErrorAction Stop -Raw
                    }

                    $scriptblock = {
                        try {
                            $results = Invoke-Command2 -ComputerName $computer -Credential $Credential -ScriptBlock $setscript -ArgumentList $Name, $plainxml -ErrorAction Stop
                            Write-Message -Level Verbose -Message " $results"
                        }
                        catch {
                            Stop-Function -Message "Failure starting $setname on $computer" -ErrorRecord $_ -Target $computer -Continue
                        }
                    }

                    if ((Get-DbaPfDataCollectorSet -ComputerName $computer -CollectorSet $Name)) {
                        if ($Pscmdlet.ShouldProcess($computer, "CollectorSet $Name already exists. Modify?")) {
                            Invoke-Command -Scriptblock $scriptblock
                            $output = Get-DbaPfDataCollectorSet -ComputerName $computer -CollectorSet $Name
                        }
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($computer, "Importing collector set $Name")) {
                            Invoke-Command -Scriptblock $scriptblock
                            $output = Get-DbaPfDataCollectorSet -ComputerName $computer -CollectorSet $Name
                        }
                    }

                    $newcollection = @()
                    foreach ($instance in $instances) {
                        $datacollector = Get-DbaPfDataCollectorSet -ComputerName $computer -CollectorSet $Name | Get-DbaPfDataCollector
                        $sqlcounters = $datacollector | Get-DbaPfDataCollectorCounter | Where-Object { $_.Name -match 'sql.*\:' -and $_.Name -notmatch 'sqlclient' } | Select-Object -ExpandProperty Name

                        foreach ($counter in $sqlcounters) {
                            $split = $counter.Split(":")
                            $firstpart = switch ($split[0]) {
                                'SQLServer' { 'MSSQL' }
                                '\SQLServer' { '\MSSQL' }
                                default { $split[0] }
                            }
                            $secondpart = $split[-1]
                            $finalcounter = "$firstpart`$$instance`:$secondpart"
                            $newcollection += $finalcounter
                        }
                    }

                    if ($newcollection.Count) {
                        if ($Pscmdlet.ShouldProcess($computer, "Adding $($newcollection.Count) additional counters")) {
                            $null = Add-DbaPfDataCollectorCounter -InputObject $datacollector -Counter $newcollection
                        }
                    }

                    Remove-Item $tempfile -ErrorAction SilentlyContinue
                    $output
                }
                catch {
                    Stop-Function -Message "Failure" -ErrorRecord $_ -Target $store -Continue
                }
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Import-DbaRegisteredServer {
    <#
        .SYNOPSIS
            Imports registered servers and registered server groups to SQL Server Central Management Server (CMS)

        .DESCRIPTION
            Imports registered servers and registered server groups to SQL Server Central Management Server (CMS)

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Group
            Imports to specific group

        .PARAMETER Path
            Optional path to exported reg server XML

        .PARAMETER InputObject
            Enables piping from Get-DbaRegisteredServer, Get-DbaRegisteredServerGroup, CSVs and other objects.

            If importing from CSV or other object, a column named ServerName is required. Optional columns include Name, Description and Group.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.

            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.

            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Chrissy LeMaire (@cl)
            Tags: RegisteredServer, CMS

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Import-DbaRegisteredServer

        .EXAMPLE
           Import-DbaRegisteredServer -SqlInstance sql2012 -Path C:\temp\corp-regservers.xml

           Imports C:\temp\corp-regservers.xml to the CMS on sql2012

        .EXAMPLE
           Import-DbaRegisteredServer -SqlInstance sql2008 -Group hr\Seattle -Path C:\temp\Seattle.xml

           Imports C:\temp\Seattle.xml to Seattle subgroup within the hr group on sql2008

        .EXAMPLE
           Get-DbaRegisteredServer -SqlInstance sql2008, sql2012 | Import-DbaRegisteredServer -SqlInstance sql2017

           Imports all registered servers from sql2008 and sql2012 to sql2017

        .EXAMPLE
           Get-DbaRegisteredServerGroup -SqlInstance sql2008 -Group hr\Seattle | Import-DbaRegisteredServer -SqlInstance sql2017 -Group Seattle

           Imports all registered servers from the hr\Seattle group on sql2008 to the Seattle group on sql2017

    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("FullName")]
        [string[]]$Path,
        [parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [object]$Group,
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            # Prep to import from file
            if ((Test-Bound -ParameterName Path)) {
                $InputObject += Get-ChildItem -Path $Path
            }
            if ((Test-Bound -ParameterName Group) -and (Test-Bound -Not -ParameterName Path)) {
                if ($Group -is [Microsoft.SqlServer.Management.RegisteredServers.ServerGroup]) {
                    $groupobject = $Group
                }
                else {
                    $groupobject = Get-DbaRegisteredServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Group $Group
                }
                if (-not $groupobject) {
                    Stop-Function -Message "Group $Group cannot be found on $instance" -Target $instance -Continue
                }
            }

            foreach ($object in $InputObject) {
                if ($object -is [Microsoft.SqlServer.Management.RegisteredServers.RegisteredServer]) {

                    $groupexists = Get-DbaRegisteredServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Group $object.Parent.Name
                    if (-not $groupexists) {
                        $groupexists = Add-DbaRegisteredServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Name $object.Parent.Name
                    }
                    Add-DbaRegisteredServer -SqlInstance $instance -SqlCredential $SqlCredential -Name $object.Name -ServerName $object.ServerName -Description $object.Description -Group $groupexists
                }
                elseif ($object -is [Microsoft.SqlServer.Management.RegisteredServers.ServerGroup]) {
                    foreach ($regserver in $object.RegisteredServers) {
                        $groupexists = Get-DbaRegisteredServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Group $regserver.Parent.Name
                        if (-not $groupexists) {
                            $groupexists = Add-DbaRegisteredServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Name $regserver.Parent.Name
                        }
                        Add-DbaRegisteredServer -SqlInstance $instance -SqlCredential $SqlCredential -Name $regserver.Name -ServerName $regserver.ServerName -Description $regserver.Description -Group $groupexists
                    }
                }
                elseif ($object -is [System.IO.FileInfo]) {
                    if ((Test-Bound -ParameterName Group)) {
                        if ($Group -is [Microsoft.SqlServer.Management.RegisteredServers.ServerGroup]) {
                            $reggroups = $Group
                        }
                        else {
                            $reggroups = Get-DbaRegisteredServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Group $Group
                        }
                    }
                    else {
                        $reggroups = Get-DbaRegisteredServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Id 1
                    }

                    foreach ($file in $object) {
                        if (-not (Test-Path -Path $file)) {
                            Stop-Function -Message "$file cannot be found" -Target $file -Continue
                        }

                        foreach ($reggroup in $reggroups) {
                            try {
                                Write-Message -Level Verbose -Message "Importing $file to $($reggroup.Name) on $instance"
                                $urnlist = $reggroup.RegisteredServers.Urn.Value
                                $reggroup.Import($file.FullName)
                                Get-DbaRegisteredServer -SqlInstance $instance -SqlCredential $SqlCredential | Where-Object { $_.Urn.Value -notin $urnlist }
                            }
                            catch {
                                Stop-Function -Message "Failure attempting to import $file to $instance" -ErrorRecord $_ -Continue
                            }
                        }
                    }
                }
                else {
                    if (-not $object.ServerName) {
                        Stop-Function -Message "Property 'ServerName' not found in InputObject. No servers added." -Continue
                    }
                    Add-DbaRegisteredServer -SqlInstance $instance -SqlCredential $SqlCredential -Name $object.Name -ServerName $object.ServerName -Description $object.Description -Group $groupobject
                }
            }
        }
    }
}
function Import-DbaSpConfigure {
    <#
        .SYNOPSIS
            Updates sp_configure settings on destination server.

        .DESCRIPTION
            Updates sp_configure settings on destination server.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER SqlInstance
            Specifies a SQL Server instance to set up sp_configure values on using a SQL file.

        .PARAMETER SqlCredential
            Use this SQL credential if you are setting up sp_configure values from a SQL file.

            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Path
            Specifies the path to a SQL script file holding sp_configure queries for each of the settings to be changed. Export-DbaSPConfigure creates a suitable file as its output.

        .PARAMETER Force
            If this switch is enabled, no version check between Source and Destination is performed. By default, the major and minor versions of Source and Destination must match when copying sp_configure settings.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .NOTES
            dbatools PowerShell module (https://dbatools.io, [email protected])
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            Import-DbaSpConfigure sqlserver sqlcluster $SourceSqlCredential $DestinationSqlCredential

            Imports the sp_configure settings from the source server sqlserver and sets them on the sqlcluster server
            using the SQL credentials stored in the variables

        .EXAMPLE
            Import-DbaSpConfigure -SqlInstance sqlserver -Path .\spconfig.sql -SqlCredential $SqlCredential

            Imports the sp_configure settings from the file .\spconfig.sql and sets them on the sqlcluster server
            using the SQL credential stored in the variables

        .OUTPUTS
            $true if success
            $false if failure

    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [Parameter(ParameterSetName = "ServerCopy")]
        [DbaInstanceParameter]$Source,
        [Parameter(ParameterSetName = "ServerCopy")]
        [DbaInstanceParameter]$Destination,
        [Parameter(ParameterSetName = "ServerCopy")]
        [PSCredential]$SourceSqlCredential,
        [Parameter(ParameterSetName = "ServerCopy")]
        [PSCredential]$DestinationSqlCredential,
        [Parameter(ParameterSetName = "FromFile")]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [Parameter(ParameterSetName = "FromFile")]
        [string]$Path,
        [Parameter(ParameterSetName = "FromFile")]
        [PSCredential]$SqlCredential,
        [switch]$Force

    )
    begin {

        if ($Path.length -eq 0) {
            $sourceserver = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
            $destserver = Connect-SqlInstance -SqlInstance $Destination -SqlCredential $DestinationSqlCredential

            $source = $sourceserver.DomainInstanceName
            $destination = $destserver.DomainInstanceName
        }
        else {
            $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
            if ((Test-Path $Path) -eq $false) {
                throw "File Not Found"
            }
        }

    }
    process {
        if ($Path.length -eq 0) {
            if ($Pscmdlet.ShouldProcess($destination, "Export sp_configure")) {
                $sqlfilename = Export-SqlSpConfigure $sourceserver
            }

            if ($sourceserver.versionMajor -ne $destserver.versionMajor -and $force -eq $false) {
                Write-Warning "Source SQL Server major version and Destination SQL Server major version must match for sp_configure migration. Use -Force to override this precaution or check the exported sql file, $sqlfilename, and run manually."
                return
            }

            If ($Pscmdlet.ShouldProcess($destination, "Execute sp_configure")) {
                $sourceserver.Configuration.ShowAdvancedOptions.ConfigValue = $true
                $sourceserver.Query("RECONFIGURE WITH OVERRIDE") | Out-Null
                $destserver.Configuration.ShowAdvancedOptions.ConfigValue = $true
                $destserver.Query("RECONFIGURE WITH OVERRIDE") | Out-Null

                $destprops = $destserver.Configuration.Properties

                foreach ($sourceprop in $sourceserver.Configuration.Properties) {
                    $displayname = $sourceprop.DisplayName

                    $destprop = $destprops | where-object { $_.Displayname -eq $displayname }
                    if ($null -ne $destprop) {
                        try {
                            $destprop.configvalue = $sourceprop.configvalue
                            $destserver.Query("RECONFIGURE WITH OVERRIDE") | Out-Null
                            Write-Output "updated $($destprop.displayname) to $($sourceprop.configvalue)."
                        }
                        catch {
                            Write-Error "Could not $($destprop.displayname) to $($sourceprop.configvalue). Feature may not be supported."
                        }
                    }
                }
                try {
                    $destserver.Configuration.Alter()
                }
                catch {
                    $needsrestart = $true
                }

                $sourceserver.Configuration.ShowAdvancedOptions.ConfigValue = $false
                $sourceserver.Query("RECONFIGURE WITH OVERRIDE") | Out-Null
                $destserver.Configuration.ShowAdvancedOptions.ConfigValue = $false
                $destserver.Query("RECONFIGURE WITH OVERRIDE") | Out-Null

                if ($needsrestart -eq $true) {
                    Write-Warning "Some configuration options will be updated once SQL Server is restarted."
                }
                else {
                    Write-Output "Configuration option has been updated."
                }
            }

            if ($Pscmdlet.ShouldProcess($destination, "Removing temp file")) {
                Remove-Item $sqlfilename -ErrorAction SilentlyContinue
            }

        }
        else {
            if ($Pscmdlet.ShouldProcess($destination, "Importing sp_configure from $Path")) {
                $server.Configuration.ShowAdvancedOptions.ConfigValue = $true
                $sql = Get-Content $Path
                foreach ($line in $sql) {
                    try {
                        $server.Query($line) | Out-Null
                        Write-Output "Successfully executed $line."
                    }
                    catch {
                        Write-Error "$line failed. Feature may not be supported."
                    }
                }
                $server.Configuration.ShowAdvancedOptions.ConfigValue = $false
                Write-Warning "Some configuration options will be updated once SQL Server is restarted."
            }
        }
    }
    end {
        if ($Path.length -gt 0) {
            $server.ConnectionContext.Disconnect()
        }
        else {
            $sourceserver.ConnectionContext.Disconnect()
            $destserver.ConnectionContext.Disconnect()
        }

        If ($Pscmdlet.ShouldProcess("console", "Showing finished message")) {
            Write-Output "SQL Server configuration options migration finished."
        }

        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Import-SqlSpConfigure
    }
}
function Import-DbaXESessionTemplate {
    <#
        .SYNOPSIS
            Imports a new XESession XML Template

        .DESCRIPTION
            Imports a new XESession XML Template either from the dbatools repository or a file you specify.

        .PARAMETER SqlInstance
            Target SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Name
            The Name of the session to create.

        .PARAMETER Path
            The path to the xml file or files for the session(s).

        .PARAMETER Template
            Specifies the name of one of the templates from the dbatools repository. Press tab to cycle through the provided templates.

        .PARAMETER TargetFilePath
            By default, files will be created in the default xel directory. Use TargetFilePath to change all instances of
            filename = "file.xel" to filename = "$TargetFilePath\file.xel". Only specify the directory, not the file itself.

            This path is relative to the destination directory

        .PARAMETER TargetFileMetadataPath
            By default, files will be created in the default xem directory. Use TargetFileMetadataPath to change all instances of
            filename = "file.xem" to filename = "$TargetFilePath\file.xem". Only specify the directory, not the file itself.

            This path is relative to the destination directory

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Import-DbaXESessionTemplate

        .EXAMPLE
            Import-DbaXESessionTemplate -SqlInstance sql2017 -Template db_query_wait_stats

            Creates a new XESession named db_query_wait_stats from the dbatools repository to the SQL Server sql2017.

        .EXAMPLE
            Import-DbaXESessionTemplate -SqlInstance sql2017 -Template db_query_wait_stats -Name "Query Wait Stats"

            Creates a new XESession named "Query Wait Stats" using the db_query_wait_stats template.

        .EXAMPLE
            Get-DbaXESession -SqlInstance sql2017 -Session db_ola_health | Remove-DbaXESession
            Import-DbaXESessionTemplate -SqlInstance sql2017 -Template db_ola_health | Start-DbaXESession

            Imports a session if it exists, then recreates it using a template.

        .EXAMPLE
            Get-DbaXESessionTemplate | Out-GridView -PassThru | Import-DbaXESessionTemplate -SqlInstance sql2017

            Allows you to select a Session template then import to an instance named sql2017.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string]$Name,
        [parameter(ValueFromPipelineByPropertyName)]
        [Alias("FullName")]
        [string[]]$Path,
        [string[]]$Template,
        [string]$TargetFilePath,
        [string]$TargetFileMetadataPath,
        [switch]$EnableException
    )
    begin {
        $metadata = Import-Clixml "$script:PSModuleRoot\bin\xetemplates-metadata.xml"
    }
    process {
        if ((Test-Bound -ParameterName Path -Not) -and (Test-Bound -ParameterName Template -Not)) {
            Stop-Function -Message "You must specify Path or Template."
        }

        if (($Path.Count -gt 1 -or $Template.Count -gt 1) -and (Test-Bound -ParameterName Template)) {
            Stop-Function -Message "Name cannot be specified with multiple files or templates because the Session will already exist."
        }

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 11
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $SqlConn = $server.ConnectionContext.SqlConnectionObject
            $SqlStoreConnection = New-Object Microsoft.SqlServer.Management.Sdk.Sfc.SqlStoreConnection $SqlConn
            $store = New-Object  Microsoft.SqlServer.Management.XEvent.XEStore $SqlStoreConnection

            foreach ($file in $template) {
                $templatepath = "$script:PSModuleRoot\bin\xetemplates\$file.xml"
                if ((Test-Path $templatepath)) {
                    $Path += $templatepath
                }
                else {
                    Stop-Function -Message "Invalid template ($templatepath does not exist)." -Continue
                }
            }

            foreach ($file in $Path) {

                if ((Test-Bound -Not -ParameterName TargetFilePath)) {
                    Write-Message -Level Verbose -Message "Importing $file to $instance"
                    try {
                        $xml = [xml](Get-Content $file -ErrorAction Stop)
                    }
                    catch {
                        Stop-Function -Message "Failure" -ErrorRecord $_ -Target $file -Continue
                    }
                }
                else {
                    Write-Message -Level Verbose -Message "TargetFilePath specified, changing all file locations in $file for $instance."
                    Write-Message -Level Verbose -Message "TargetFileMetadataPath specified, changing all metadata file locations in $file for $instance."

                    # Handle whatever people specify
                    $TargetFilePath = $TargetFilePath.TrimEnd("\")
                    $TargetFileMetadataPath = $TargetFileMetadataPath.TrimEnd("\")
                    $TargetFilePath = "$TargetFilePath\"
                    $TargetFileMetadataPath = "$TargetFileMetadataPath\"

                    # Perform replace
                    $xelphrase = 'name="filename" value="'
                    $xemphrase = 'name="metadatafile" value="'

                    try {
                        $basename = (Get-ChildItem $file).Basename
                        $contents = Get-Content $file -ErrorAction Stop
                        $contents = $contents.Replace($xelphrase, "$xelphrase$TargetFilePath")
                        $contents = $contents.Replace($xemphrase, "$xemphrase$TargetFileMetadataPath")
                        $temp = ([System.IO.Path]::GetTempPath()).TrimEnd("").TrimEnd("\")
                        $tempfile = "$temp\$basename"
                        $null = Set-Content -Path $tempfile -Value $contents -Encoding UTF8
                        $xml = [xml](Get-Content $tempfile -ErrorAction Stop)
                        $file = $tempfile
                    }
                    catch {
                        Stop-Function -Message "Failure" -ErrorRecord $_ -Target $file -Continue
                    }

                    Write-Message -Level Verbose -Message "$TargetFilePath does not exist on $server, creating now."
                    try {
                        if (-not (Test-DbaSqlPath -SqlInstance $server -Path $TargetFilePath)) {
                            $null = New-DbaSqlDirectory -SqlInstance $server -Path $TargetFilePath
                        }
                    }
                    catch {
                        Stop-Function -Message "Failure" -ErrorRecord $_ -Target $file -Continue
                    }
                }

                if (-not $xml.event_sessions) {
                    Stop-Function -Message "$file is not a valid XESession template document." -Continue
                }

                if ((Test-Bound -ParameterName Name -not)) {
                    $Name = (Get-ChildItem $file).BaseName
                }

                # This could be done better but not today
                $no2012 = ($metadata | Where-Object Compatibility -gt 2012).Name
                $no2014 = ($metadata | Where-Object Compatibility -gt 2014).Name

                if ($Name -in $no2012 -and $server.VersionMajor -eq 11) {
                    Stop-Function -Message "$Name is not supported in SQL Server 2012 ($server)" -Continue
                }

                if ($Name -in $no2014 -and $server.VersionMajor -eq 12) {
                    Stop-Function -Message "$Name is not supported in SQL Server 2014 ($server)" -Continue
                }

                if ((Get-DbaXESession -SqlInstance $server -Session $Name)) {
                    Stop-Function -Message "$Name already exists on $instance" -Continue
                }

                try {
                    Write-Message -Level Verbose -Message "Importing $file as $name "
                    $session = $store.CreateSessionFromTemplate($Name, $file)
                    $session.Create()
                    if ($file -eq $tempfile) {
                        Remove-Item $tempfile -ErrorAction SilentlyContinue
                    }
                    Get-DbaXESession -SqlInstance $server -Session $session.Name
                }
                catch {
                    Stop-Function -Message "Failure" -ErrorRecord $_ -Target $store -Continue
                }
            }
        }
    }
}
#ValidationTags#CodeStyle,Messaging,FlowControl,Pipeline#
function Install-DbaFirstResponderKit {
    <#
        .SYNOPSIS
            Installs or updates the First Responder Kit stored procedures.

        .DESCRIPTION
            Downloads, extracts and installs the First Responder Kit stored procedures:
            sp_Blitz, sp_BlitzWho, sp_BlitzFirst, sp_BlitzIndex, sp_BlitzCache and sp_BlitzTrace, etc.

            First Responder Kit links:
            http://FirstResponderKit.org
            https://github.com/BrentOzarULTD/SQL-Server-First-Responder-Kit

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies the database to instal the First Responder Kit stored procedures into

        .PARAMETER Branch
            Specifies an alternate branch of the First Responder Kit to install. (master or dev)

        .PARAMETER Confirm
            Prompts to confirm actions

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: BrentOzar, FRK, FirstResponderKit
            Author: Tara Kizer, Brent Ozar Unlimited (https://www.brentozar.com/)
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Install-DbaFirstResponderKit

        .EXAMPLE
            Install-DbaFirstResponderKit -SqlInstance server1 -Database master

            Logs into server1 with Windows authentication and then installs the FRK in the master database.

        .EXAMPLE
            Install-DbaFirstResponderKit -SqlInstance server1\instance1 -Database DBA

            Logs into server1\instance1 with Windows authentication and then installs the FRK in the DBA database.

        .EXAMPLE
            Install-DbaFirstResponderKit -SqlInstance server1\instance1 -Database master -SqlCredential $cred

            Logs into server1\instance1 with SQL authentication and then installs the FRK in the master database.

        .EXAMPLE
            Install-DbaFirstResponderKit -SqlInstance sql2016\standardrtm, sql2016\sqlexpress, sql2014

            Logs into sql2016\standardrtm, sql2016\sqlexpress and sql2014 with Windows authentication and then installs the FRK in the master database.

        .EXAMPLE
            $servers = "sql2016\standardrtm", "sql2016\sqlexpress", "sql2014"
            $servers | Install-DbaFirstResponderKit

            Logs into sql2016\standardrtm, sql2016\sqlexpress and sql2014 with Windows authentication and then installs the FRK in the master database.

        .EXAMPLE
            Install-DbaFirstResponderKit -SqlInstance sql2016 -Branch dev

            Installs the dev branch version of the FRK in the master database on sql2016 instance.
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [ValidateSet('master', 'dev')]
        [string]$Branch = "master",
        [object]$Database = "master",
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $url = "https://codeload.github.com/BrentOzarULTD/SQL-Server-First-Responder-Kit/zip/$Branch"

        $temp = ([System.IO.Path]::GetTempPath()).TrimEnd("\")
        $zipfile = "$temp\SQL-Server-First-Responder-Kit-$Branch.zip"
        $zipfolder = "$temp\SQL-Server-First-Responder-Kit-$Branch\"

        if ($zipfile | Test-Path) {
            Remove-Item -Path $zipfile -ErrorAction SilentlyContinue
        }

        if ($zipfolder | Test-Path) {
            Remove-Item -Path $zipfolder -Recurse -ErrorAction SilentlyContinue
        }

        $null = New-Item -ItemType Directory -Path $zipfolder -ErrorAction SilentlyContinue

        Write-Message -Level Verbose -Message "Downloading and unzipping the First Responder Kit zip file."

        try {
            $oldSslSettings = [System.Net.ServicePointManager]::SecurityProtocol
            [System.Net.ServicePointManager]::SecurityProtocol = "Tls12"
            try {
                $wc = New-Object System.Net.WebClient
                $wc.DownloadFile($url, $zipfile)
            }
            catch {
                # Try with default proxy and usersettings
                $wc = New-Object System.Net.WebClient
                $wc.Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
                $wc.DownloadFile($url, $zipfile)
            }
            [System.Net.ServicePointManager]::SecurityProtocol = $oldSslSettings

            # Unblock if there's a block
            Unblock-File $zipfile -ErrorAction SilentlyContinue

            Expand-Archive -Path $zipfile -DestinationPath $zipfolder -Force

            Remove-Item -Path $zipfile
        }
        catch {
            Stop-Function -Message "Couldn't download the First Responder Kit. Download and install manually from https://github.com/BrentOzarULTD/SQL-Server-First-Responder-Kit/archive/$Branch.zip." -ErrorRecord $_
            return
        }
    }

    process {
        if (Test-FunctionInterrupt) {
            return
        }

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure." -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            Write-Message -Level Verbose -Message "Starting installing/updating the First Responder Kit stored procedures in $database on $instance."
            $allprocedures_query = "select name from sys.procedures where is_ms_shipped = 0"
            $allprocedures = ($server.Query($allprocedures_query, $Database)).Name
            # Install/Update each FRK stored procedure
            foreach ($script in (Get-ChildItem $zipfolder -Recurse -Filter "sp_*.sql")) {
                $scriptname = $script.Name
                $scriptError = $false
                if ($scriptname -ne "sp_BlitzRS.sql") {

                    if ($scriptname -eq "sp_BlitzQueryStore.sql") {
                        if ($server.VersionMajor -lt 13) { continue }
                    }
                    if ($Pscmdlet.ShouldProcess($instance, "installing/updating $scriptname in $database.")) {
                        try {
                            Invoke-DbaSqlQuery -SqlInstance $server -Database $Database -File $script.FullName -EnableException -Verbose:$false
                        } catch {
                            Write-Message -Level Warning -Message "Could not execute at least one portion of $scriptname in $Database on $instance." -ErrorRecord $_
                            $scriptError = $true
                        }
                        $baseres = @{
                            ComputerName = $server.ComputerName
                            InstanceName = $server.ServiceName
                            SqlInstance  = $server.DomainInstanceName
                            Database     = $Database
                            Name         = $script.BaseName
                        }
                        if ($scriptError) {
                            $baseres['Status'] = 'Error'
                        } elseif ($script.BaseName -in $allprocedures) {
                            $baseres['Status'] = 'Updated'
                        }
                        else {
                            $baseres['Status'] = 'Installed'
                        }
                        [PSCustomObject]$baseres
                    }
                }
            }
            Write-Message -Level Verbose -Message "Finished installing/updating the First Responder Kit stored procedures in $database on $instance."
        }
    }
}
function Install-DbaMaintenanceSolution {
    <#
        .SYNOPSIS
            Download and Install SQL Server Maintenance Solution created by Ola Hallengren (https://ola.hallengren.com)
        .DESCRIPTION
            This script will download and install the latest version of SQL Server Maintenance Solution created by Ola Hallengren

        .PARAMETER SqlInstance
            The target SQL Server instance onto which the Maintenance Solution will be installed.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database where Ola Hallengren's solution will be installed. Defaults to master.

        .PARAMETER BackupLocation
            Location of the backup root directory. If this is not supplied, the default backup directory will be used.

        .PARAMETER CleanupTime
            Time in hours, after which backup files are deleted.

        .PARAMETER OutputFileDirectory
            Specify the output file directory where the Maintenance Solution will write to.

        .PARAMETER ReplaceExisting
            If this switch is enabled, objects already present in the target database will be dropped and recreated.

        .PARAMETER LogToTable
            If this switch is enabled, the Maintenance Solution will be configured to log commands to a table.

        .PARAMETER Solution
            Specifies which portion of the Maintenance solution to install. Valid values are All (full solution), Backup, IntegrityCheck and IndexOptimize.

        .PARAMETER InstallJobs
            If this switch is enabled, the corresponding SQL Agent Jobs will be created.

       .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Ola, Maintenance
            Author: Viorel Ciucu, [email protected], cviorel.com

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            http://dbatools.io/Install-DbaMaintenanceSolution

        .EXAMPLE
            Install-DbaMaintenanceSolution -SqlInstance RES14224 -Database DBA -CleanupTime 72

            Installs Ola Hallengren's Solution objects on RES14224 in the DBA database.
            Backups will default to the default Backup Directory.
            If the Maintenance Solution already exists, the script will be halted.

        .EXAMPLE
            Install-DbaMaintenanceSolution -SqlInstance RES14224 -Database DBA -BackupLocation "Z:\SQLBackup" -CleanupTime 72

            This will create the Ola Hallengren's Solution objects. Existing objects are not affected in any way.

        .EXAMPLE
            Install-DbaMaintenanceSolution -SqlInstance RES14224 -Database DBA -BackupLocation "Z:\SQLBackup" -CleanupTime 72 -ReplaceExisting

            This will drop and then recreate the Ola Hallengren's Solution objects
            The cleanup script will drop and recreate:
                - TABLE [dbo].[CommandLog]
                - STORED PROCEDURE [dbo].[CommandExecute]
                - STORED PROCEDURE [dbo].[DatabaseBackup]
                - STORED PROCEDURE [dbo].[DatabaseIntegrityCheck]
                - STORED PROCEDURE [dbo].[IndexOptimize]

            The following SQL Agent jobs will be deleted:
                - 'Output File Cleanup'
                - 'IndexOptimize - USER_DATABASES'
                - 'sp_delete_backuphistory'
                - 'DatabaseBackup - USER_DATABASES - LOG'
                - 'DatabaseBackup - SYSTEM_DATABASES - FULL'
                - 'DatabaseBackup - USER_DATABASES - FULL'
                - 'sp_purge_jobhistory'
                - 'DatabaseIntegrityCheck - SYSTEM_DATABASES'
                - 'CommandLog Cleanup'
                - 'DatabaseIntegrityCheck - USER_DATABASES'
                - 'DatabaseBackup - USER_DATABASES - DIFF'
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias('ServerInstance', 'SqlServer')]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object]$Database = "master",
        [string]$BackupLocation,
        [int]$CleanupTime,
        [string]$OutputFileDirectory,
        [switch]$ReplaceExisting,
        [switch]$LogToTable,
        [ValidateSet('All', 'Backup', 'IntegrityCheck', 'IndexOptimize')]
        [string]$Solution = 'All',
        [switch]$InstallJobs,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -NonPooled
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ((Test-Bound -Parameter ReplaceExisting -Not)) {
                $procs = Get-DbaSqlModule -SqlInstance $server -Database $Database | Where-Object Name -in 'CommandExecute', 'DatabaseBackup', 'DatabaseIntegrityCheck', 'IndexOptimize'
                $table = Get-DbaTable -SqlInstance $server -Database $Database -Table CommandLog -IncludeSystemDBs  | Where-Object Database -eq $Database

                if ($null -ne $procs -or $null -ne $table) {
                    Stop-Function -Message "The Maintenance Solution already exists in $Database on $instance. Use -ReplaceExisting to automatically drop and recreate."
                    return
                }
            }

            if ((Test-Bound -Parameter BackupLocation -Not)) {
                $BackupLocation = (Get-DbaDefaultPath -SqlInstance $server).Backup
            }

            Write-Message -Level Output -Message "Ola Hallengren's solution will be installed on database $Database."

            $db = $server.Databases[$Database]

            if ($InstallJobs -and $Solution -ne 'All') {
                Stop-Function -Message "To create SQL Agent jobs you need to use '-Solution All' and '-InstallJobs Create'."
                return
            }

            if ($ReplaceExisting -eq $true) {
                Write-Message -Level Verbose -Message "If Ola Hallengren's scripts are found, we will drop and recreate them!"
            }

            if ($CleanupTime -ne 0 -and $InstallJobs -eq $false) {
                Write-Message -Level Output -Message "CleanupTime $CleanupTime value will be ignored because you chose not to create SQL Agent Jobs."
            }

            # Required
            $required = @('CommandExecute.sql')

            if ($LogToTable) {
                $required += 'CommandLog.sql'
            }

            if ($Solution -match 'Backup') {
                $required += 'DatabaseBackup.sql'
            }

            if ($Solution -match 'IntegrityCheck') {
                $required += 'DatabaseIntegrityCheck.sql'
            }

            if ($Solution -match 'IndexOptimize') {
                $required += 'IndexOptimize.sql'
            }

            if ($Solution -match 'All') {
                $required += 'MaintenanceSolution.sql'
            }

            $temp = ([System.IO.Path]::GetTempPath()).TrimEnd("\")
            $zipfile = "$temp\ola.zip"

            # Start the download
            $url = "https://github.com/olahallengren/sql-server-maintenance-solution/archive/master.zip"
            try {
                Start-BitsTransfer -Source $url -DisplayName 'Downloading SQL Server Maintenance Solution - https://ola.hallengren.com' -Destination $zipfile -ErrorAction Stop
            }
            catch {
                Stop-Function -Message "You need to re-run the script, there is a problem with the proxy or the download link has changed." -ErrorRecord $_
            }

            # Unblock if there's a block
            Unblock-File $zipfile -ErrorAction SilentlyContinue

            $path = "$temp\sql-server-maintenance-solution-master"

            # We don't like default parameters messed with so we start clean
            if ((Test-Path $path)) {
                Remove-Item -Path $temp\sql-server-maintenance-solution-master -Recurse -Force -ErrorAction SilentlyContinue
            }

            # internal if it doesn't exist
            Expand-Archive -Path $zipfile -DestinationPath $temp -Force
            Remove-Item -Path $zipfile

            $listOfFiles = Get-ChildItem -Filter "*.sql" -Path $path | Select-Object -ExpandProperty FullName

            # In which database we install
            if ($Database -ne 'master') {
                $findDB = 'USE [master]'
                $replaceDB = 'USE [' + $Database + ']'
                foreach ($file in $listOfFiles) {
                    (Get-Content -Path $file -Raw).Replace($findDB, $replaceDB) | Set-Content -Path $file
                }
            }

            # Backup location
            if ($BackupLocation) {
                $findBKP = 'SET @BackupDirectory     = NULL'
                $replaceBKP = 'SET @BackupDirectory     = N''' + $BackupLocation + ''''
                foreach ($file in $listOfFiles) {
                    (Get-Content -Path $file -Raw).Replace($findBKP, $replaceBKP) | Set-Content -Path $file
                }
            }

            # CleanupTime
            if ($CleanupTime -ne 0) {
                $findCleanupTime = 'SET @CleanupTime         = NULL'
                $replaceCleanupTime = 'SET @CleanupTime         = ' + $CleanupTime
                foreach ($file in $listOfFiles) {
                    (Get-Content -Path $file -Raw).Replace($findCleanupTime, $replaceCleanupTime) | Set-Content -Path $file
                }
            }

            # OutputFileDirectory
            if ($OutputFileDirectory.Length -gt 0) {
                $findOutputFileDirectory = 'SET @OutputFileDirectory = NULL'
                $replaceOutputFileDirectory = 'SET @OutputFileDirectory = N''' + $OutputFileDirectory + ''''
                foreach ($file in $listOfFiles) {
                    (Get-Content -Path $file -Raw).Replace($findOutputFileDirectory, $replaceOutputFileDirectory) | Set-Content -Path $file
                }

            }

            # LogToTable
            if (!$LogToTable) {
                $findLogToTable = "SET @LogToTable          = 'Y'"
                $replaceLogToTable = "SET @LogToTable          = 'N'"
                foreach ($file in $listOfFiles) {
                    (Get-Content -Path $file -Raw).Replace($findLogToTable, $replaceLogToTable) | Set-Content -Path $file
                }
            }

            # Create Jobs
            if ($InstallJobs -eq $false) {
                $findCreateJobs = "SET @CreateJobs          = 'Y'"
                $replaceCreateJobs = "SET @CreateJobs          = 'N'"
                foreach ($file in $listOfFiles) {
                    (Get-Content -Path $file -Raw).Replace($findCreateJobs, $replaceCreateJobs) | Set-Content -Path $file
                }
            }

            $CleanupQuery = $null
            if ($ReplaceExisting) {
                [string]$CleanupQuery = $("
                            IF OBJECT_ID('[dbo].[CommandLog]', 'U') IS NOT NULL
                                DROP TABLE [dbo].[CommandLog];
                            IF OBJECT_ID('[dbo].[CommandExecute]', 'P') IS NOT NULL
                                DROP PROCEDURE [dbo].[CommandExecute];
                            IF OBJECT_ID('[dbo].[DatabaseBackup]', 'P') IS NOT NULL
                                DROP PROCEDURE [dbo].[DatabaseBackup];
                            IF OBJECT_ID('[dbo].[DatabaseIntegrityCheck]', 'P') IS NOT NULL
                                DROP PROCEDURE [dbo].[DatabaseIntegrityCheck];
                            IF OBJECT_ID('[dbo].[IndexOptimize]', 'P') IS NOT NULL
                                DROP PROCEDURE [dbo].[IndexOptimize];
                            ")

                Write-Message -Level Output -Message "Dropping objects created by Ola's Maintenance Solution"
                $null = $db.Query($CleanupQuery)

                # Remove Ola's Jobs
                if ($InstallJobs -and $ReplaceExisting) {
                    Write-Message -Level Output -Message "Removing existing SQL Agent Jobs created by Ola's Maintenance Solution."
                    $jobs = Get-DbaAgentJob -SqlInstance $server | Where-Object Description -match "hallengren"
                    if ($jobs) {
                        $jobs | ForEach-Object { Remove-DbaAgentJob -SqlInstance $instance -Job $_.name }
                    }
                }
            }

            try {
                Write-Message -Level Output -Message "Installing on server $SqlInstance, database $Database."

                foreach ($file in $listOfFiles) {
                    $shortFileName = Split-Path $file -Leaf
                    if ($required.Contains($shortFileName)) {
                        Write-Message -Level Output -Message "Installing $file."
                        $sql = [IO.File]::ReadAllText($file)
                        try {
                            foreach ($query in ($sql -Split "\nGO\b")) {
                                $null = $db.Query($query)
                            }
                        }
                        catch {
                            Stop-Function -Message "Could not execute $file in $Database on $instance." -ErrorRecord $_ -Target $db -Continue
                        }
                    }
                }
            }
            catch {
                Stop-Function -Message "Could not execute $file in $Database on $instance." -ErrorRecord $_ -Target $db -Continue
            }
        }

        if ((Test-Path $path)) {
            Remove-Item -Path $temp\sql-server-maintenance-solution-master -Recurse -Force -ErrorAction SilentlyContinue
        }

        # Only here due to need for non-pooled connection in this command
        try {
            $server.ConnectionContext.Disconnect()
        }
        catch {
        }

        Write-Message -Level Output -Message "Installation complete."
    }
}
function Install-DbaWatchUpdate {
    <#
        .SYNOPSIS
            Adds the scheduled task to support Watch-DbaUpdate.

        .DESCRIPTION
            Adds the scheduled task to support Watch-DbaUpdate.

        .PARAMETER TaskName
            Provide custom name for the Scheduled Task

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Module
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Install-DbaWatchUpdate

        .EXAMPLE
            Install-DbaWatchUpdate

            Adds the scheduled task needed by Watch-DbaUpdate

        .EXAMPLE
            Install-DbaWatchUpdate -TaskName MyScheduledTask

            Will create the scheduled task as the name MyScheduledTask
    #>
    [cmdletbinding(SupportsShouldProcess)]
    param(
        [string]$TaskName = 'dbatools version check',
        [switch]$EnableException
    )
    process {
        if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Validate Version of OS") ) {
            if (([Environment]::OSVersion).Version.Major -lt 10) {
                Stop-Function -Message "This command only supports Windows 10 and above"
            }
        }
        $script = {
            try {
                # create a task, check every 3 hours
                $action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument '-NoProfile -NoLogo -NonInteractive -WindowStyle Hidden Watch-DbaUpdate'
                $trigger = New-ScheduledTaskTrigger -Once -At (Get-Date).Date -RepetitionInterval (New-TimeSpan -Hours 1)
                $principal = New-ScheduledTaskPrincipal -LogonType S4U -UserId (whoami)
                $settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit ([timespan]::Zero) -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -RunOnlyIfNetworkAvailable -DontStopOnIdleEnd
                $task = Register-ScheduledTask -Principal $principal -TaskName 'dbatools version check' -Action $action -Trigger $trigger -Settings $settings -ErrorAction Stop
            }
            catch {
                # keep moving
            }
        }

        if ($null -eq (Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue)) {
            # Needs admin creds to setup the kind of PowerShell window that doesn't appear for a millisecond
            # which is a millisecond too long
            if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Validate running in RunAs mode")) {
                if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
                    Write-Message -Level Warning -Message "This command has to run using RunAs mode (privileged) to create the Scheduled Task. This will only happen once."
                    if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Starting process in RunAs mode") ) {
                        Start-Process powershell -Verb runAs -ArgumentList Install-DbaWatchUpdate -Wait
                    }
                }

            }
            if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Creating scheduled task $TaskName")) {
                try {
                    Invoke-Command -ScriptBlock $script -ErrorAction Stop

                    if ((Get-Location).Path -ne "$env:WINDIR\system32") {
                        Write-Message -Level Output -Message "Scheduled Task [$TaskName] created! A notification should appear momentarily. Here's something cute to look at in the interim."
                        Show-Notification -Title "dbatools wants you" -Text "come hang out at dbatools.io/slack"
                    }
                }
                catch {
                    Stop-Function -Message "Could not create scheduled task $TaskName" -Target $env:COMPUTERNAME -ErrorRecord $_
                }
            }
            if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Checking scheduled task was created")) {
                # double check
                if ($null -eq (Get-ScheduledTask -TaskName "dbatools version check" -ErrorAction SilentlyContinue)) {
                    Write-Message -Level Warning -Message "Scheduled Task was not created."
                }
            }
        }
        else {
            Write-Message -Level Output -Message "Scheduled Task $TaskName is already installed on this machine."
        }
    }
}
function Install-DbaWhoIsActive {
    <#
        .SYNOPSIS
            Automatically installs or updates sp_WhoisActive by Adam Machanic.

        .DESCRIPTION
            This command downloads, extracts and installs sp_WhoisActive with Adam's permission. To read more about sp_WhoisActive, please visit http://whoisactive.com and http://sqlblog.com/blogs/adam_machanic/archive/tags/who+is+active/default.aspx

            Please consider donating to Adam if you find this stored procedure helpful: http://tinyurl.com/WhoIsActiveDonate

            Note that you will be prompted a bunch of times to confirm an action.

        .PARAMETER SqlInstance
            The SQL Server instance. Server version must be SQL Server version 2005 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database to install sp_WhoisActive into. This parameter is mandatory when executing this command unattended.

        .PARAMETER LocalFile
            Specifies the path to a local file to install sp_WhoisActive from. This can be either the zipfile as distributed by the website or the expanded SQL script. If this parameter is not specified, the latest version will be downloaded and installed from https://whoisactive.com/

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER Force
            If this switch is enabled, the sp_WhoisActive will be downloaded from the internet even if previously cached.

        .EXAMPLE
            Install-DbaWhoIsActive -SqlInstance sqlserver2014a -Database master

            Downloads sp_WhoisActive from the internet and installs to sqlserver2014a's master database. Connects to SQL Server using Windows Authentication.

        .EXAMPLE
            Install-DbaWhoIsActive -SqlInstance sqlserver2014a -SqlCredential $cred

            Pops up a dialog box asking which database on sqlserver2014a you want to install the procedure into. Connects to SQL Server using SQL Authentication.

        .EXAMPLE
            Install-DbaWhoIsActive -SqlInstance sqlserver2014a -Database master -LocalFile c:\SQLAdmin\whoisactive_install.sql

            Installs sp_WhoisActive to sqlserver2014a's master database from the local file whoisactive_install.sql

        .EXAMPLE
            $instances = Get-DbaRegisteredServer sqlserver
            Install-DbaWhoIsActive -SqlInstance $instances -Database master

        .NOTES
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Install-DbaWhoIsActive
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PsCredential]$SqlCredential,
        [parameter(Mandatory = $false)]
        [ValidateScript( { Test-Path -Path $_ -PathType Leaf })]
        [string]$LocalFile,
        [object]$Database,
        [switch][Alias('Silent')]
        $EnableException,
        [switch]$Force
    )

    begin {
        $DbatoolsData = Get-DbaConfigValue -FullName "Path.DbatoolsData"
        $temp = ([System.IO.Path]::GetTempPath()).TrimEnd("\")
        $zipfile = "$temp\spwhoisactive.zip"

        if ($LocalFile -eq $null -or $LocalFile.Length -eq 0) {
            $baseUrl = "http://whoisactive.com/downloads"
            $latest = ((Invoke-WebRequest -UseBasicParsing -uri http://whoisactive.com/downloads).Links | where-object { $PSItem.href -match "who_is_active" } | Select-Object href -First 1).href
            $LocalCachedCopy = Join-Path -Path $DbatoolsData -ChildPath $latest;

            if ((Test-Path -Path $LocalCachedCopy -PathType Leaf) -and (-not $Force)) {
                Write-Message -Level Verbose -Message "Locally-cached copy exists, skipping download."
                if ($PSCmdlet.ShouldProcess($env:computername, "Copying sp_WhoisActive from local cache for installation")) {
                    Copy-Item -Path $LocalCachedCopy -Destination $zipfile;
                }
            }
            else {
                if ($PSCmdlet.ShouldProcess($env:computername, "Downloading sp_WhoisActive")) {
                    try {
                        Write-Message -Level Verbose -Message "Downloading sp_WhoisActive zip file, unzipping and installing."
                        $url = $baseUrl + "/" + $latest
                        try {
                            Invoke-WebRequest $url -OutFile $zipfile -ErrorAction Stop -UseBasicParsing
                            Copy-Item -Path $zipfile -Destination $LocalCachedCopy
                        }
                        catch {
                            #try with default proxy and usersettings
                            (New-Object System.Net.WebClient).Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
                            Invoke-WebRequest $url -OutFile $zipfile -ErrorAction Stop -UseBasicParsing
                        }
                    }
                    catch {
                        Stop-Function -Message "Couldn't download sp_WhoisActive. Please download and install manually from $url." -ErrorRecord $_
                        return
                    }
                }
            }
        }
        else {
            # Look local
            if ($PSCmdlet.ShouldProcess($env:computername, "Copying local file to temp directory")) {

                if ($LocalFile.EndsWith("zip")) {
                    Copy-Item -Path $LocalFile -Destination $zipfile -Force
                }
                else {
                    Copy-Item -Path $LocalFile -Destination (Join-Path -path $temp -childpath "whoisactivelocal.sql")
                }
            }
        }
        if ($LocalFile -eq $null -or $LocalFile.Length -eq 0 -or $LocalFile.EndsWith("zip")) {
            # Unpack
            # Unblock if there's a block
            if ($PSCmdlet.ShouldProcess($env:computername, "Unpacking zipfile")) {

                Unblock-File $zipfile -ErrorAction SilentlyContinue

                if (Get-Command -ErrorAction SilentlyContinue -Name "Expand-Archive") {
                    try {
                        Expand-Archive -Path $zipfile -DestinationPath $temp -Force
                    }
                    catch {
                        Stop-Function -Message "Unable to extract $zipfile. Archive may not be valid." -ErrorRecord $_
                        return
                    }
                }
                else {
                    # Keep it backwards compatible
                    $shell = New-Object -ComObject Shell.Application
                    $zipPackage = $shell.NameSpace($zipfile)
                    $destinationFolder = $shell.NameSpace($temp)
                    Get-ChildItem "$temp\who*active*.sql" | Remove-Item
                    $destinationFolder.CopyHere($zipPackage.Items())
                }
                Remove-Item -Path $zipfile
            }
            $sqlfile = (Get-ChildItem "$temp\who*active*.sql" -ErrorAction SilentlyContinue | Select-Object -First 1).FullName
        }
        else {
            $sqlfile = $LocalFile
        }

        if ($PSCmdlet.ShouldProcess($env:computername, "Reading SQL file into memory")) {
            Write-Message -Level Verbose -Message "Using $sqlfile."

            $sql = [IO.File]::ReadAllText($sqlfile)
            $sql = $sql -replace 'USE master', ''
            $batches = $sql -split "GO\r\n"
        }
    }

    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if (-not $Database) {
                if ($PSCmdlet.ShouldProcess($instance, "Prompting with GUI list of databases")) {
                    $Database = Show-DbaDatabaseList -SqlInstance $server -Title "Install sp_WhoisActive" -Header "To deploy sp_WhoisActive, select a database or hit cancel to quit." -DefaultDb "master"

                    if (-not $Database) {
                        Stop-Function -Message "You must select a database to install the procedure." -Target $Database
                        return
                    }

                    if ($Database -ne 'master') {
                        Write-Message -Level Warning -Message "You have selected a database other than master. When you run Invoke-DbaWhoIsActive in the future, you must specify -Database $Database."
                    }
                }
            }
            if ($PSCmdlet.ShouldProcess($instance, "Installing sp_WhoisActive")) {
                try {
                    $ProcedureExists_Query = "select COUNT(*) [proc_count] from sys.procedures where is_ms_shipped = 0 and name like '%sp_WhoisActive%'"

                    if ($server.Databases[$Database]) {
                        $ProcedureExists = ($server.Query($ProcedureExists_Query, $Database)).proc_count
                        foreach ($batch in $batches) {
                            try {
                                $null = $server.databases[$Database].ExecuteNonQuery($batch)
                            }
                            catch {
                                Stop-Function -Message "Failed to install stored procedure." -ErrorRecord $_ -Continue -Target $instance
                            }
                        }

                        if ($ProcedureExists -gt 0) {
                            $status = 'Updated'
                        }
                        else {
                            $status = 'Installed'
                        }
                        [PSCustomObject]@{
                            ComputerName = $server.ComputerName
                            InstanceName = $server.ServiceName
                            SqlInstance  = $server.DomainInstanceName
                            Database     = $Database
                            Name         = 'sp_WhoisActive'
                            Status       = $status
                        }
                    }
                    else {
                        Stop-Function -Message "Failed to find database $Database on $instance or $Database is not writeable." -ErrorRecord $_ -Continue -Target $instance
                    }

                }
                catch {
                    Stop-Function -Message "Failed to install stored procedure." -ErrorRecord $_ -Continue -Target $instance
                }

            }
        }
    }
    end {
        if ($PSCmdlet.ShouldProcess($env:computername, "Post-install cleanup")) {
            Get-Item $sqlfile | Remove-Item
        }
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Install-SqlWhoIsActive
    }
}
function Invoke-DbaAdvancedRestore {
    <#
        .SYNOPSIS
            Allows the restore of modified BackupHistory Objects
            For 90% of users Restore-DbaDatabase should be your point of access to this function. The other 10% use it at their own risk

        .DESCRIPTION
            This is the final piece in the Restore-DbaDatabase Stack. Usually a BackupHistory object will arrive here from Restore-DbaDatabse via the following pipeline:
            Get-DbaBackupInformation  | Select-DbaBackupInformation | Format-DbaBackupInformation | Test-DbaBackupInformation | Invoke-DbaAdvancedRestore

            We have exposed these functions publicly to allow advanced users to perform operations that we don't support, or won't add as they would make things too complex for the majority of our users

            For example if you wanted to do some very complex redirection during a migration, then doing the rewrite of destinations may be better done with your own custom scripts rather than via Format-DbaBackupInformation

            We would recommend ALWAYS pushing your input through Test-DbaBackupInformation just to make sure that it makes sense to us.

        .PARAMETER BackupHistory
            The BackupHistory object to be restored.
            Can be passed in on the pipeline

        .PARAMETER SqlInstance
            The SqlInstance to which the backups should be restored

        .PARAMETER SqlCredential
            SqlCredential to be used to connect to the target SqlInstance

        .PARAMETER OutputScriptOnly
            If set, the restore will not be performed, but the T-SQL scripts to perform it will be returned

        .PARAMETER VerifyOnly
            If set, performs a Verify of the backups rather than a full restore

        .PARAMETER RestoreTime
            Point in Time to which the database should be restored.

            This should be the same value or earlier, as used in the previous pipeline stages

        .PARAMETER StandbyDirectory
            A folder path where a standby file should be created to put the recovered databases in a standby mode

        .PARAMETER NoRecovery
            Leave the database in a restoring state so that further restore may be made

        .PARAMETER MaxTransferSize
            Parameter to set the unit of transfer. Values must be a multiple by 64kb

        .PARAMETER Blocksize
            Specifies the block size to use. Must be one of 0.5kb,1kb,2kb,4kb,8kb,16kb,32kb or 64kb
            Can be specified in bytes
            Refer to https://msdn.microsoft.com/en-us/library/ms178615.aspx for more detail

        .PARAMETER BufferCount
            Number of I/O buffers to use to perform the operation.
            Refer to https://msdn.microsoft.com/en-us/library/ms178615.aspx for more detail

        .PARAMETER Continue
            Indicates that the restore is continuing a restore, so target database must be in Recovering or Standby states

        .PARAMETER AzureCredential
            AzureCredential required to connect to blob storage holding the backups

        .PARAMETER WithReplace
            Indicated that if the database already exists it should be replaced

        .PARAMETER KeepCDC
            Indicates whether CDC information should be restored as part of the database

        .PARAMETER PageRestore
            The output from Get-DbaSuspect page containing the suspect pages to be restored.

        .PARAMETER WhatIf
            Shows what would happen if the cmdlet runs. The cmdlet is not run.

        .PARAMETER Confirm
            Prompts you for confirmation before running the cmdlet.

        .PARAMETER EnableException
            Replaces user friendly yellow warnings with bloody red exceptions of doom!
            Use this if you want the function to throw terminating errors you want to catch.

        .NOTES
            Tags: Restore, Backup
            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Invoke-DbaAdvancedRestore

        .EXAMPLE
            $BackupHistory | Invoke-DbaAdvancedRestore -SqlInstance MyInstance

            Will restore all the backups in the BackupHistory object according to the transformations it contains

        .EXAMPLE
            $BackupHistory | Invoke-DbaAdvancedRestore -SqlInstance MyInstance -OutputScriptOnly
            $BackupHistory | Invoke-DbaAdvancedRestore -SqlInstance MyInstance

            First generates just the T-SQL restore scripts so they can be sanity checked, and then if they are good perform the full restore. By reusing the BackupHistory object there is no need to rescan all the backup files again
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Object[]]$BackupHistory,
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [switch]$OutputScriptOnly,
        [switch]$VerifyOnly,
        [datetime]$RestoreTime = (Get-Date).AddDays(2),
        [string]$StandbyDirectory,
        [switch]$NoRecovery,
        [int]$MaxTransferSize,
        [int]$BlockSize,
        [int]$BufferCount,
        [switch]$Continue,
        [string]$AzureCredential,
        [switch]$WithReplace,
        [switch]$KeepCDC,
        [object[]]$PageRestore,
        [switch]$EnableException
    )
    begin {
        try {
            $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            return
        }
        if ($KeepCDC -and ($NoRecovery -or ('' -ne $StandbyDirectory))) {
            Stop-Function -Category InvalidArgument -Message "KeepCDC cannot be specified with Norecovery or Standby as it needs recovery to work"
            return
        }

        if ($null -ne $PageRestore) {
            Write-Message -Message "Doing Page Recovery" -Level Verbose
            $tmpPages = @()
            foreach ($Page in $PageRestore) {
                $tmppages += "$($Page.FileId):$($Page.PageID)"
            }
            $NoRecovery = $True
            $Pages = $tmpPages -join ','
        }
        #$OutputScriptOnly  = $false
        $InternalHistory = @()
    }
    process {
        foreach ($bh in $BackupHistory) {
            $InternalHistory += $bh
        }
    }
    end {
        if (Test-FunctionInterrupt) { return }
        $Databases = $InternalHistory.Database | Select-Object -Unique
        foreach ($Database in $Databases) {
            $DatabaseRestoreStartTime = Get-Date
            if ($Database -in $Server.Databases.Name) {
                if (-not $OutputScriptOnly -and -not $VerifyOnly) {
                    if ($Pscmdlet.ShouldProcess("Killing processes in $Database on $SqlInstance as it exists and WithReplace specified  `n", "Cannot proceed if processes exist, ", "Database Exists and WithReplace specified, need to kill processes to restore")) {
                        try {
                            Write-Message -Level Verbose -Message "Killing processes on $Database"
                            $null = Stop-DbaProcess -SqlInstance $Server -Database $Database -WarningAction Silentlycontinue
                            $null = $server.Query("Alter database $Database set offline with rollback immediate; alter database $Database set restricted_user; Alter database $Database set online with rollback immediate", 'master')
                            $server.ConnectionContext.Connect()
                        }
                        catch {
                            Write-Message -Level Verbose -Message "No processes to kill in $Database"
                        }
                    }
                }
                elseif (-not $WithReplace -and (-not $VerifyOnly)) {
                    Stop-Function -Message "$Database exists and WithReplace not specified, stopping" -EnableException $EnableException
                    return
                }
            }
            Write-Message -Message "WithReplace  = $WithReplace" -Level Debug
            $backups = @($InternalHistory | Where-Object {$_.Database -eq $Database} | Sort-Object -Property Type, FirstLsn)
            $BackupCnt = 1
            foreach ($backup in $backups) {
                $FileRestoreStartTime = Get-Date
                $Restore = New-Object Microsoft.SqlServer.Management.Smo.Restore
                if (($backup -ne $backups[-1]) -or $true -eq $NoRecovery) {
                    $Restore.NoRecovery = $True
                }
                elseif ($backup -eq $backups[-1] -and '' -ne $StandbyDirectory) {
                    $Restore.StandbyFile = $StandByDirectory + "\" + $Database + (get-date -Format yyyMMddHHmmss) + ".bak"
                    Write-Message -Level Verbose -Message "Setting standby on last file $($Restore.StandbyFile)"
                }
                else {
                    $Restore.NoRecovery = $False
                }
                if ($restoretime -gt (Get-Date) -or $Restore.RestoreTime -gt (Get-Date) -or $backup.RecoveryModel -eq 'Simple') {
                    $Restore.ToPointInTime = $null
                }
                else {
                    if ($RestoreTime -ne $Restore.RestoreTime) {
                        $Restore.ToPointInTime = $backup.RestoreTime
                    }
                    else {
                        $Restore.ToPointInTime = $RestoreTime
                    }
                }
                $Restore.Database = $database
                $Restore.ReplaceDatabase = $WithReplace
                if ($MaxTransferSize) {
                    $Restore.MaxTransferSize = $MaxTransferSize
                }
                if ($BufferCount) {
                    $Restore.BufferCount = $BufferCount
                }
                if ($BlockSize) {
                    $Restore.Blocksize = $BlockSize
                }
                if ($true -ne $Continue -and ($null -eq $Pages)) {
                    foreach ($file in $backup.FileList) {
                        $MoveFile = New-Object Microsoft.SqlServer.Management.Smo.RelocateFile
                        $MoveFile.LogicalFileName = $File.LogicalName
                        $MoveFile.PhysicalFileName = $File.PhysicalName
                        $null = $Restore.RelocateFiles.Add($MoveFile)
                    }
                }
                $Action = switch ($backup.Type) {
                    '1' {'Database'}
                    '2' {'Log'}
                    '5' {'Database'}
                    'Transaction Log' {'Log'}
                    Default {'Database'}
                }

                Write-Message -Level Debug -Message "restore action = $Action"
                $Restore.Action = $Action
                foreach ($File in $backup.FullName) {
                    Write-Message -Message "Adding device $file" -Level Debug
                    $Device = New-Object -TypeName Microsoft.SqlServer.Management.Smo.BackupDeviceItem
                    $Device.Name = $file
                    if ($file.StartsWith("http")) {
                        $Device.devicetype = "URL"
                    }
                    else {
                        $Device.devicetype = "File"
                    }
                    
                    if ($AzureCredential) {
                        $Restore.CredentialName = $AzureCredential
                    }
                    
                    $Restore.FileNumber = $backup.Position
                    $Restore.Devices.Add($Device)
                }
                Write-Message -Level Verbose -Message "Performing restore action"
                $ConfirmMessage = "`n Restore Database $Database on $SqlInstance `n from files: $RestoreFileNames `n with these file moves: `n $LogicalFileMovesString `n $ConfirmPointInTime `n"
                if ($Pscmdlet.ShouldProcess("$Database on $SqlInstance `n `n", $ConfirmMessage)) {
                    try {
                        $RestoreComplete = $true
                        if ($KeepCDC -and $Restore.NoRecovery -eq $false) {
                            $script = $Restore.Script($server)
                            if ($script -like '*WITH*') {
                                $script = $script.TrimEnd() + ' , KEEP_CDC'
                            }
                            else {
                                $script = $script.TrimEnd() + ' WITH KEEP_CDC'
                            }
                            if ($true -ne $OutputScriptOnly) {
                                Write-Progress -id 2 -activity "Restoring $Database to $sqlinstance - Backup $BackupCnt of $($Backups.count)" -percentcomplete 0 -status ([System.String]::Format("Progress: {0} %", 0))
                                $null = $server.ConnectionContext.ExecuteNonQuery($script)
                                Write-Progress -id 2 -activity "Restoring $Database to $sqlinstance - Backup $BackupCnt of $($Backups.count)" -status "Complete" -Completed
                            }
                        }
                        elseif ($null -ne $Pages -and $Action -eq 'Database') {
                            $script = $Restore.Script($server)
                            $script = $script -replace "] FROM", "] PAGE='$pages' FROM"
                            if ($true -ne $OutputScriptOnly) {
                                Write-Progress -id 2 -activity "Restoring $Database to $sqlinstance - Backup $BackupCnt of $($Backups.count)" -percentcomplete 0 -status ([System.String]::Format("Progress: {0} %", 0))
                                $null = $server.ConnectionContext.ExecuteNonQuery($script)
                                Write-Progress -id 2 -activity "Restoring $Database to $sqlinstance - Backup $BackupCnt of $($Backups.count)" -status "Complete" -Completed
                            }
                        }
                        elseif ($OutputScriptOnly) {
                            $script = $Restore.Script($server)
                        }
                        elseif ($VerifyOnly) {
                            Write-Message -Message "VerifyOnly restore" -Level Verbose
                            Write-Progress -id 2 -activity "Verifying $Database backup file on $sqlinstance - Backup $BackupCnt of $($Backups.count)" -percentcomplete 0 -status ([System.String]::Format("Progress: {0} %", 0))
                            $Verify = $Restore.SqlVerify($server)
                            Write-Progress -id 2 -activity "Verifying $Database backup file on $sqlinstance - Backup $BackupCnt of $($Backups.count)" -status "Complete" -Completed
                            if ($verify -eq $true) {
                                Write-Message -Message "VerifyOnly restore Succeeded" -Level Verbose
                                return "Verify successful"
                            }
                            else {
                                Write-Message -Message "VerifyOnly restore Failed" -Level Verbose
                                return "Verify failed"
                            }
                        }
                        else {
                            $outerProgress = $BackupCnt/$Backups.Count*100
                            if ($BackupCnt -eq 1) {
                                Write-Progress -id 2 -ParentId 1 -Activity "Restoring $Database to $sqlinstance - Backup $BackupCnt of $($Backups.count)" -percentcomplete 0
                            }
                            Write-Progress -id 3 -ParentId 2 -Activity "Restore $($backup.FullName -Join ',')" -percentcomplete 0
                            $script = $Restore.Script($Server)
                            $percentcomplete = [Microsoft.SqlServer.Management.Smo.PercentCompleteEventHandler] {
                                Write-Progress -id 3 -ParentId 2 -Activity "Restore $($backup.FullName -Join ',')" -percentcomplete $_.Percent -status ([System.String]::Format("Progress: {0} %", $_.Percent))
                            }
                            $Restore.add_PercentComplete($percentcomplete)
                            $Restore.PercentCompleteNotification = 1
                            $Restore.SqlRestore($Server)
                            Write-Progress -id 3 -ParentId 2 -Activity "Restore $($backup.FullName -Join ',')" -Completed
                            Write-Progress -id 2 -ParentId 1 -Activity "Restoring $Database to $sqlinstance - Backup $BackupCnt of $($Backups.count)" -percentcomplete $outerProgress -status ([System.String]::Format("Progress: {0:N2} %", $outerProgress))
                        }
                    }
                    catch {
                        Write-Message -Level Verbose -Message "Failed, Closing Server connection"
                        $RestoreComplete = $False
                        $ExitError = $_.Exception.InnerException
                        Stop-Function -Message "Failed to restore db $Database, stopping" -ErrorRecord $_
                        return
                    }
                    finally {

                        if ($OutputScriptOnly -eq $false) {
                            [PSCustomObject]@{
                                SqlInstance            = $SqlInstance
                                DatabaseName           = $backup.Database
                                DatabaseOwner          = $server.ConnectionContext.TrueLogin
                                NoRecovery             = $Restore.NoRecovery
                                WithReplace            = $WithReplace
                                RestoreComplete        = $RestoreComplete
                                BackupFilesCount       = $backup.FullName.Count
                                RestoredFilesCount     = $backup.Filelist.PhysicalName.count
                                BackupSizeMB           = if ([bool]($backup.psobject.Properties.Name -contains 'TotalSize')) { [Math]::Round(($backup | Measure-Object -Property TotalSize -Sum).Sum / 1mb, 2) } else { $null }
                                CompressedBackupSizeMB = if ([bool]($backup.psobject.Properties.Name -contains 'CompressedBackupSize')) { [Math]::Round(($backup | Measure-Object -Property CompressedBackupSize -Sum).Sum / 1mb, 2) } else { $null }
                                BackupFile             = $backup.FullName -Join ','
                                RestoredFile           = $((Split-Path $backup.FileList.PhysicalName -Leaf) | Sort-Object -Unique) -Join ','
                                RestoredFileFull       = ($backup.Filelist.PhysicalName -Join ',')
                                RestoreDirectory       = ((Split-Path $backup.FileList.PhysicalName) | Sort-Object -Unique) -Join ','
                                BackupSize             = if ([bool]($backup.psobject.Properties.Name -contains 'TotalSize')) { ($backup | Measure-Object -Property TotalSize -Sum).Sum } else { $null }
                                CompressedBackupSize   = if ([bool]($backup.psobject.Properties.Name -contains 'CompressedBackupSize')) { ($backup | Measure-Object -Property CompressedBackupSize -Sum).Sum } else { $null }
                                Script                 = $script
                                BackupFileRaw          = ($backups.Fullname)
                                FileRestoreTime        = New-TimeSpan -Seconds ((Get-Date)-$FileRestoreStartTime).TotalSeconds
                                DatabaseRestoreTime    = New-TimeSpan -Seconds ((Get-Date)-$DatabaseRestoreStartTime).TotalSeconds
                                ExitError              = $ExitError
                            } | Select-DefaultView -ExcludeProperty BackupSize, CompressedBackupSize, ExitError, BackupFileRaw, RestoredFileFull
                        }
                        else {
                            $script
                        }
                        if ($Restore.Devices.Count -gt 0) {
                            $Restore.Devices.Clear()
                        }
                        Write-Message -Level Verbose -Message "Succeeded, Closing Server connection"
                        $server.ConnectionContext.Disconnect()
                    }
                }
                Write-Progress -id 1 -Activity "Restoring" -Completed
                Write-Progress -id 2 -Activity "Restoring" -Completed
                $BackupCnt++
            }
            if ($server.ConnectionContext.exists) {
                $server.ConnectionContext.Disconnect()
            }
        }
    }
}
function Invoke-DbaBalanceDataFiles {
    <#
        .SYNOPSIS
            Re-balance data between data files

        .DESCRIPTION
            When you have a large database with a single data file and add another file, SQL Server will only use the new file until it's about the same size.
            You may want to balance the data between all the data files.

            The function will check the server version and edition to see if the it allows for online index rebuilds.
            If the server does support it, it will try to rebuild the index online.
            If the server doesn't support it, it will rebuild the index offline. Be carefull though, this can cause downtime

            The tables must have a clustered index to be able to balance out the data.
            The function does NOT yet support heaps.

            The function will also check if the file groups are subject to balance out.
            A file group whould have at least have 2 data files and should be writable.
            If a table is within such a file group it will be subject for processing. If not the table will be skipped.

        .PARAMETER SqlInstance
            The target SQL Server instance or instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process.

        .PARAMETER Table
            The tables(s) of the database to process. If unspecified, all tables will be processed.

        .PARAMETER RebuildOffline
            Will set all the indexes to rebuild offline.
            This option is also needed when the server version is below 2005.

        .PARAMETER WhatIf
            Shows what would happen if the command were to run

        .PARAMETER Confirm
            Prompts for confirmation of every step. For example:

            The server does not support online rebuilds of indexes.
            Do you want to rebuild the indexes offline?
            [Y] Yes  [N] No   [?] Help (default is "Y"):

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER Force
            This will disable the check for enough disk space for the action to be successful.
            Use this with caution!!

        .NOTES
            Tags: Database, FileManagement, File, Space
            Author: Sander Stad (@sqlstad, sqlstad.nl)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            Invoke-DbaBalanceDataFiles -SqlInstance sql1 -Database db1

            This command will distribute the data in database db1 on instance sql1

        .EXAMPLE
            Invoke-DbaBalanceDataFiles -SqlInstance sql1 -Database db1 | Select-Object -ExpandProperty DataFilesEnd

            This command will distribute the data in database db1 on instance sql1

        .EXAMPLE
            Invoke-DbaBalanceDataFiles -SqlInstance sql1 -Database db1 -Table table1,table2,table5

            This command will distribute the data for only the tables table1,table2 and table5

        .EXAMPLE
            Invoke-DbaBalanceDataFiles -SqlInstance sql1 -Database db1 -RebuildOffline

            This command will consider the fact that there might be a SQL Server edition that does not support online rebuilds of indexes.
            By supplying this parameter you give permission to do the rebuilds offline if the edition does not support it.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(ParameterSetName = "Pipe", Mandatory = $true)]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [Alias("Tables")]
        [object[]]$Table,
        [switch]$RebuildOffline,
        [switch]$EnableException,
        [switch]$Force
    )

    process {

        Write-Message -Message "Starting balancing out data files" -Level Verbose

        # Set the initial success flag
        [bool]$success = $true

        foreach ($instance in $sqlinstance) {
            # Try connecting to the instance
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $Server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            # Check the database parameter
            if ($Database) {
                if ($Database -notin $server.Databases.Name) {
                    Stop-Function -Message "One or more databases cannot be found on instance on instance $instance" -Target $instance -Continue
                }

                $DatabaseCollection = $server.Databases | Where-Object { $_.Name -in $Database }
            }
            else {
                Stop-Function -Message "Please supply a database to balance out" -Target $instance -Continue
            }

            # Get the server version
            $serverVersion = $server.Version.Major

            # Check edition of the sql instance
            if ($RebuildOffline) {
                Write-Message -Message "Continuing with offline rebuild." -Level Verbose
            }
            elseif (-not $RebuildOffline -and ($serverVersion -lt 9 -or (([string]$Server.Edition -notmatch "Developer") -and ($Server.Edition -notmatch "Enterprise")))) {
                # Set up the confirm part
                $message = "The server does not support online rebuilds of indexes. `nDo you want to rebuild the indexes offline?"
                $choiceYes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Answer Yes."
                $choiceNo = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Answer No."
                $options = [System.Management.Automation.Host.ChoiceDescription[]]($choiceYes, $choiceNo)
                $result = $host.ui.PromptForChoice($title, $message, $options, 0)

                # Check the result from the confirm
                switch ($result) {
                    # If yes
                    0 {
                        # Set the option to generate a full backup
                        Write-Message -Message "Continuing with offline rebuild." -Level Verbose

                        [bool]$supportOnlineRebuild = $false
                    }
                    1 {
                        Stop-Function -Message "You chose to not allow offline rebuilds of indexes. Use -RebuildOffline" -Target $instance
                        return
                    }
                } # switch
            }
            elseif ($serverVersion -ge 9 -and (([string]$Server.Edition -like "Developer*") -or ($Server.Edition -like "Enterprise*"))) {
                [bool]$supportOnlineRebuild = $true
            }

            # Loop through each of the databases
            foreach ($db in $DatabaseCollection) {
                $dataFilesStarting = Get-DbaDatabaseFile -SqlInstance $server -Database $db.Name | Where-Object { $_.TypeDescription -eq 'ROWS' } | Select-Object ID, LogicalName, PhysicalName, Size, UsedSpace, AvailableSpace | Sort-Object ID

                if (-not $Force) {
                    # Check the amount of disk space available
                    $query = "SELECT SUBSTRING(physical_name, 0, 4) AS 'Drive' ,
                                        SUM(( size * 8 ) / 1024) AS 'SizeMB'
                                FROM	sys.master_files
                                WHERE	DB_NAME(database_id) = '$($db.Name)'
                                GROUP BY SUBSTRING(physical_name, 0, 4)"
                    # Execute the query
                    $dbDiskUsage = $Server.Query($query)

                    # Get the free space for each drive
                    $result = $Server.Query("xp_fixeddrives")
                    $MbFreeColName = $result[0].psobject.Properties.Name[1]
                    $diskFreeSpace = $result | Select-Object Drive, @{ Name = 'FreeMB'; Expression = { $_.$MbFreeColName } }

                    # Loop through each of the drives to see if the size of files on that
                    # particular disk do not exceed the free space of that disk
                    foreach ($d in $dbDiskUsage) {
                        $freeSpace = $diskFreeSpace | Where-Object { $_.Drive -eq $d.Drive.Trim(':\') } | Select-Object FreeMB
                        if ($d.SizeMB -gt $freeSpace.FreeMB) {
                            # Set the success flag
                            $success = $false

                            Stop-Function -Message "The available space may not be sufficient to continue the process. Please use -Force to try anyway." -Target $instance -Continue
                            return
                        }
                    }
                }

                # Create the start time
                $start = Get-Date

                # Check if the function needs to continue
                if ($success) {

                    # Get the database files before all the alterations
                    Write-Message -Message "Retrieving data files before data move" -Level Verbose
                    Write-Message -Message "Processing database $db" -Level Verbose

                    # Check the datafiles of the database
                    $dataFiles = Get-DbaDatabaseFile -SqlInstance $instance -Database $db | Where-Object { $_.TypeDescription -eq 'ROWS' }
                    if ($dataFiles.Count -eq 1) {
                        # Set the success flag
                        $success = $false

                        Stop-Function -Message "Database $db only has one data file. Please add a data file to balance out the data" -Target $instance -Continue
                    }

                    # Check the tables parameter
                    if ($Table) {
                        if ($Table -notin $db.Table) {
                            # Set the success flag
                            $success = $false

                            Stop-Function -Message "One or more tables cannot be found in database $db on instance $instance" -Target $instance -Continue
                        }

                        $TableCollection = $db.Tables | Where-Object { $_.Name -in $Table }
                    }
                    else {
                        $TableCollection = $db.Tables
                    }

                    # Get the database file groups and check the aount of data files
                    Write-Message -Message "Retrieving file groups" -Level Verbose
                    $fileGroups = $Server.Databases[$db.Name].FileGroups

                    # ARray to hold the file groups with properties
                    $balanceableTables = @()

                    # Loop through each of the file groups

                    foreach ($fg in $fileGroups) {

                        # If there is less than 2 files balancing out data is not possible
                        if (($fg.Files.Count -ge 2) -and ($fg.Readonly -eq $false)) {
                            $balanceableTables += $fg.EnumObjects() | Where-Object { $_.GetType().Name -eq 'Table' }
                        }
                    }

                    $unsuccessfulTables = @()

                    # Loop through each of the tables
                    foreach ($tbl in $TableCollection) {

                        # Chck if the table balanceable
                        if ($tbl.Name -in $balanceableTables.Name) {

                            Write-Message -Message "Processing table $tbl" -Level Verbose

                            # Chck the tables and get the clustered indexes
                            if ($TableCollection.Indexes.Count -lt 1) {
                                # Set the success flag
                                $success = $false

                                Stop-Function -Message "Table $tbl does not contain any indexes" -Target $instance -Continue
                            }
                            else {

                                # Get all the clustered indexes for the table
                                $clusteredIndexes = $TableCollection.Indexes | Where-Object { $_.IndexType -eq 'ClusteredIndex' }

                                if ($clusteredIndexes.Count -lt 1) {
                                    # Set the success flag
                                    $success = $false

                                    Stop-Function -Message "No clustered indexes found in table $tbl" -Target $instance -Continue
                                }
                            }

                            # Loop through each of the clustered indexes and rebuild them
                            Write-Message -Message "$($clusteredIndexes.Count) clustered index(es) found for table $tbl" -Level Verbose
                            if ($PSCmdlet.ShouldProcess("Rebuilding indexes to balance data")) {
                                foreach ($ci in $clusteredIndexes) {

                                    Write-Message -Message "Rebuilding index $($ci.Name)" -Level Verbose

                                    # Get the original index operation
                                    [bool]$originalIndexOperation = $ci.OnlineIndexOperation

                                    # Set the rebuild option to be either offline or online
                                    if ($RebuildOffline) {
                                        $ci.OnlineIndexOperation = $false
                                    }
                                    elseif ($serverVersion -ge 9 -and $supportOnlineRebuild -and -not $RebuildOffline) {
                                        Write-Message -Message "Setting the index operation for index $($ci.Name) to online" -Level Verbose
                                        $ci.OnlineIndexOperation = $true
                                    }

                                    # Rebuild the index
                                    try {
                                        Write-Message -Message "Rebuilding index $($ci.Name)" -Level Verbose
                                        $ci.Rebuild()

                                        # Set the success flag
                                        $success = $true
                                    }
                                    catch {
                                        # Set the original index operation back for the index
                                        $ci.OnlineIndexOperation = $originalIndexOperation

                                        # Set the success flag
                                        $success = $false

                                        Stop-Function -Message "Something went wrong rebuilding index $($ci.Name). `n$($_.Exception.Message)" -ErrorRecord $_ -Target $instance -Continue
                                    }

                                    # Set the original index operation back for the index
                                    Write-Message -Message "Setting the index operation for index $($ci.Name) back to the original value" -Level Verbose
                                    $ci.OnlineIndexOperation = $originalIndexOperation

                                } # foreach index

                            } # if process

                        } # if table is balanceable
                        else {
                            # Add the table to the unsuccessful array
                            $unsuccessfulTables += $tbl.Name

                            # Set the success flag
                            $success = $false

                            Write-Message -Message "Table $tbl cannot be balanced out" -Level Verbose
                        }

                    } #foreach table
                }

                # Create the end time
                $end = Get-Date

                # Create the time span
                $timespan = New-TimeSpan -Start $start -End $end
                $ts = [timespan]::fromseconds($timespan.TotalSeconds)
                $elapsed = "{0:HH:mm:ss}" -f ([datetime]$ts.Ticks)

                # Get the database files after all the alterations
                Write-Message -Message "Retrieving data files after data move" -Level Verbose
                $dataFilesEnding = Get-DbaDatabaseFile -SqlInstance $server -Database $db.Name | Where-Object { $_.TypeDescription -eq 'ROWS' } | Select-Object ID, LogicalName, PhysicalName, Size, UsedSpace, AvailableSpace | Sort-Object ID

                [pscustomobject]@{
                    ComputerName   = $server.ComputerName
                    InstanceName   = $server.ServiceName
                    SqlInstance    = $server.DomainInstanceName
                    Database       = $db.Name
                    Start          = $start
                    End            = $end
                    Elapsed        = $elapsed
                    Success        = $success
                    Unsuccessful   = $unsuccessfulTables -join ","
                    DataFilesStart = $dataFilesStarting
                    DataFilesEnd   = $dataFilesEnding
                }

            } # foreach database

        } # end process
    }
}
function Invoke-DbaCycleErrorLog {
    <#
        .SYNOPSIS
            Cycles the current instance or agent log.

        .DESCRIPTION
            Cycles the current error log for the instance (SQL Server) and/or SQL Server Agent.

        .PARAMETER SqlInstance
            The SQL Server instance holding the databases to be removed.You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Type
            The log to cycle.
            Accepts: instance or agent.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Log, Cycle
            Author: Shawn Melton (@wsmelton | http://blog.wsmelton.info)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Invoke-DbaCycleLog

        .EXAMPLE
            Invoke-DbaCycleLog -SqlInstance sql2016 -Type agent

            Cycles the current error log for the SQL Server Agent on SQL Server instance sql2016

        .EXAMPLE
            Invoke-DbaCycleLog -SqlInstance sql2016 -Type instance

            Cycles the current error log for the SQL Server instance on SQL Server instance sql2016

        .EXAMPLE
            Invoke-DbaCycleLog -SqlInstance sql2016

            Cycles the current error log for both SQL Server instance and SQL Server Agent on SQL Server instance sql2016
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Low')]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [ValidateSet('instance', 'agent')]
        [string]$Type,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        if (Test-Bound 'Type') {
            if ($Type -notin 'instance', 'agent') {
                Stop-Function -Message "The type provided [$Type] for $SqlInstance is not an accepted value. Please use 'Instance' or 'Agent'"
                return
            }
        }
        $logToCycle = @()
        switch ($Type) {
            'agent' {
                $sql = "EXEC msdb.dbo.sp_cycle_agent_errorlog;"
                $logToCycle = $Type
            }
            'instance' {
                $sql = "EXEC master.dbo.sp_cycle_errorlog;"
                $logToCycle = $Type
            }
            default {
                $sql = "
                    EXEC master.dbo.sp_cycle_errorlog;
                    EXEC msdb.dbo.sp_cycle_agent_errorlog;"
                $logToCycle = 'instance', 'agent'
            }
        }

    }
    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            try {
                $logs = $logToCycle -join ','
                if ($Pscmdlet.ShouldProcess($server, "Cycle the log(s): $logs")) {
                    $null = $server.Query($sql)
                    [pscustomobject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        LogType      = $logToCycle
                        IsSuccessful = $true
                        Notes        = $null
                    }
                }
            }
            catch {
                [pscustomobject]@{
                    ComputerName = $server.ComputerName
                    InstanceName = $server.ServiceName
                    SqlInstance  = $server.DomainInstanceName
                    LogType      = $logToCycle
                    IsSuccessful = $false
                    Notes        = $_.Exception
                }
                Stop-Function -Message "Issue cycling $logs on $server" -Target $server -ErrorRecord $_ -Exception $_.Exception -Continue
            }
        }
    }
}
function Invoke-DbaDatabaseClone {
    <#
    .SYNOPSIS
        Clones a database schema and statistics

    .DESCRIPTION
        Clones a database schema and statistics.

        This can be useful for testing query performance without requiring all the space needed for the data in the database.

        Read more at sqlperformance: https://sqlperformance.com/2016/08/sql-statistics/expanding-dbcc-clonedatabase

        Thanks to Microsoft Tiger Team for the code and idea https://github.com/Microsoft/tigertoolbox/

    .PARAMETER SqlInstance
        Allows you to specify a comma separated list of servers to query.

    .PARAMETER SqlCredential
        Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

    .PARAMETER Database
        The database to clone - this list is auto-populated from the server.

    .PARAMETER CloneDatabase
        The name(s) to clone to.

    .PARAMETER UpdateStatistics
        Update the statistics prior to cloning (per Microsoft Tiger Team formula)

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Tags: Statistics, Performance
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Invoke-DbaDatabaseClone

    .EXAMPLE
        Invoke-DbaDatabaseClone -SqlInstance sql2016 -Database mydb -CloneDatabase myclone
        Clones mydb to myclone on sql2016

    .EXAMPLE
        Invoke-DbaDatabaseClone -SqlInstance sql2016 -Database mydb -CloneDatabase myclone, myclone2 -UpdateStatistics
        Updates the statistics of mydb then clones to myclone and myclone2

    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory, ValueFromPipeline)]
        [object]$Database,
        [string[]]$CloneDatabase,
        [switch]$UpdateStatistics,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {

        if (-not $Database.Name -and -not $SqlInstance) {
            Stop-Function -Message "You must specify a server name if you did not pipe a database"
        }

        $updatestats = "declare @out table(id int identity(1,1),s sysname, o sysname, i sysname, stats_stream varbinary(max), rows bigint, pages bigint)
                    declare @dbcc table(stats_stream varbinary(max), rows bigint, pages bigint)
                    declare c cursor for
                           select object_schema_name(object_id) s, object_name(object_id) o, name i
                           from sys.indexes
                           where type_desc in ('CLUSTERED COLUMNSTORE', 'NONCLUSTERED COLUMNSTORE')
                    declare @s sysname, @o sysname, @i sysname
                    open c
                    fetch next from c into @s, @o, @i
                    while @@FETCH_STATUS = 0 begin
                           declare @showStats nvarchar(max) = N'DBCC SHOW_STATISTICS(""' + quotename(@s) + '.' + quotename(@o) + '"", ' + quotename(@i) + ') with stats_stream'
                           insert @dbcc exec sp_executesql @showStats
                           insert @out select @s, @o, @i, stats_stream, rows, pages from @dbcc
                           delete @dbcc
                           fetch next from c into @s, @o, @i
                    end
                    close c
                    deallocate c


                    declare @sql nvarchar(max);
                    declare @id int;

                    select top 1 @id=id,@sql=
                    'UPDATE STATISTICS ' + quotename(s) + '.' + quotename(o)  + '(' + quotename(i)
                    + ') with stats_stream = ' + convert(nvarchar(max), stats_stream, 1)
                    + ', rowcount = ' + convert(nvarchar(max), rows) + ', pagecount = '  + convert(nvarchar(max), pages)
                    from @out

                    WHILE (@@ROWCOUNT <> 0)
                    BEGIN
                        exec sp_executesql @sql
                        delete @out where id = @id
                        select top 1 @id=id,@sql=
                        'UPDATE STATISTICS ' + quotename(s) + '.' + quotename(o)  + '(' + quotename(i)
                        + ') with stats_stream = ' + convert(nvarchar(max), stats_stream, 1)
                        + ', rowcount = ' + convert(nvarchar(max), rows) + ', pagecount = '  + convert(nvarchar(max), pages)
                        from @out
                    END"

    }

    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 12
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $sql2012min = [version]"11.0.7001.0" # SQL 2012 SP4
            $sql2014min = [version]"12.0.5000.0" # SQL 2014 SP2
            $sql2016min = [version]"13.0.4001.0" # SQL 2016 SP1


            if ($server.VersionMajor -eq 11 -and $server.Version -lt $sql2012min) {
                Stop-Function -Message "Unsupported version for $instance. SQL Server 2012 SP4 and above required." -Target $server -Continue
            }

            if ($server.VersionMajor -eq 12 -and $server.Version -lt $sql2014min) {
                Stop-Function -Message "Unsupported version for $instance. SQL Server 2014 SP2 and above required." -Target $server -Continue
            }

            if ($server.VersionMajor -eq 13 -and $server.Version -lt $sql2016min) {
                Stop-Function -Message "Unsupported version for $instance. SQL Server 2016 SP1 and above required." -Target $server -Continue
            }

            if (-not $Database.Name) {
                [Microsoft.SqlServer.Management.Smo.Database]$database = $server.Databases[$database]
            }

            if ($Database.IsSystemObject) {
                Stop-Function -Message "Only user databases are supported" -Target $instance -Continue
            }

            if (-not $Database.name) {
                Stop-Function -Message "Database not found" -Target $instance -Continue
            }

            if ($UpdateStatistics) {
                try {
                    Write-Message -Level Verbose -Message "Updating statistics"
                    $null = $database.Query($updatestats)
                }
                catch {
                    Stop-Function -Message "Failure" -ErrorRecord $_ -Target $server -Continue
                }
            }

            $dbname = $database.Name

            foreach ($db in $CloneDatabase) {
                Write-Message -Level Verbose -Message "Cloning $db from $database"
                if ($server.Databases[$db]) {
                    Stop-Function -Message "Destination clone database $db already exists" -Target $instance -Continue
                }
                else {
                    try {
                        $sql = "dbcc clonedatabase('$dbname','$db')"
                        $null = $database.Query($sql)
                        $server.Databases.Refresh()
                        Get-DbaDatabase -SqlInstance $server -Database $db
                    }
                    catch {
                        Stop-Function -Message "Failure" -ErrorRecord $_ -Target $server -Continue
                    }
                }
            }
        }
    }
}
function Invoke-DbaDatabaseShrink {
    <#
        .SYNOPSIS
            Shrinks all files in a database. This is a command that should rarely be used.

            - Shrinks can cause severe index fragmentation (to the tune of 99%)
            - Shrinks can cause massive growth in the database's transaction log
            - Shrinks can require a lot of time and system resources to perform data movement

        .DESCRIPTION
            Shrinks all files in a database. Databases should be shrunk only when completely necessary.

            Many awesome SQL people have written about why you should not shrink your data files. Paul Randal and Kalen Delaney wrote great posts about this topic:

                http://www.sqlskills.com/blogs/paul/why-you-should-not-shrink-your-data-files
                http://sqlmag.com/sql-server/shrinking-data-files

            However, there are some cases where a database will need to be shrunk. In the event that you must shrink your database:

            1. Ensure you have plenty of space for your T-Log to grow
            2. Understand that shrinks require a lot of CPU and disk resources
            3. Consider running DBCC INDEXDEFRAG or ALTER INDEX ... REORGANIZE after the shrink is complete.

        .PARAMETER SqlInstance
            The target SQL Server instances

        .PARAMETER SqlCredential
            SqlCredential object used to connect to the SQL Server as a different user.

        .PARAMETER Database
            The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto-populated from the server

        .PARAMETER AllUserDatabases
            Run command against all user databases

        .PARAMETER PercentFreeSpace
            Specifies how much to reduce the database in percent, defaults to 0.

        .PARAMETER ShrinkMethod
            Specifies the method that is used to shrink the database
                Default
                    Data in pages located at the end of a file is moved to pages earlier in the file. Files are truncated to reflect allocated space.
                EmptyFile
                    Migrates all of the data from the referenced file to other files in the same filegroup. (DataFile and LogFile objects only).
                NoTruncate
                    Data in pages located at the end of a file is moved to pages earlier in the file.
                TruncateOnly
                    Data distribution is not affected. Files are truncated to reflect allocated space, recovering free space at the end of any file.

        .PARAMETER StatementTimeout
            Timeout in minutes. Defaults to infinity (shrinks can take a while.)

        .PARAMETER LogsOnly
            Deprecated. Use FileType instead

        .PARAMETER FileType
            Specifies the files types that will be shrunk
                All
                    All Data and Log files are shrunk, using database shrink (Default)
                Data
                    Just the Data files are shrunk using file shrink
                Log
                    Just the Log files are shrunk using file shrink

        .PARAMETER ExcludeIndexStats
            Exclude statistics about fragmentation

        .PARAMETER ExcludeUpdateUsage
            Exclude DBCC UPDATE USAGE for database

        .PARAMETER WhatIf
            Shows what would happen if the command were to run

        .PARAMETER Confirm
            Prompts for confirmation of every step. For example:

            Are you sure you want to perform this action?
            Performing the operation "Shrink database" on target "pubs on SQL2016\VNEXT".
            [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Shrink, Database

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Invoke-DbaDatabaseShrink

        .EXAMPLE
            Invoke-DbaDatabaseShrink -SqlInstance sql2016 -Database Northwind,pubs,Adventureworks2014

            Shrinks Northwind, pubs and Adventureworks2014 to have as little free space as possible.

        .EXAMPLE
            Invoke-DbaDatabaseShrink -SqlInstance sql2014 -Database AdventureWorks2014 -PercentFreeSpace 50

            Shrinks AdventureWorks2014 to have 50% free space. So let's say AdventureWorks2014 was 1GB and it's using 100MB space. The database free space would be reduced to 50MB.

        .EXAMPLE
            Invoke-DbaDatabaseShrink -SqlInstance sql2012 -AllUserDatabases

            Shrinks all databases on SQL2012 (not ideal for production)
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Low')]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$AllUserDatabases,
        [ValidateRange(0, 99)]
        [int]$PercentFreeSpace = 0,
        [ValidateSet('Default', 'EmptyFile', 'NoTruncate', 'TruncateOnly')]
        [string]$ShrinkMethod = "Default",
        [ValidateSet('All', 'Data', 'Log')]
        [string]$FileType = "All",
        [int]$StatementTimeout = 0,
        [switch]$LogsOnly,
        [switch]$ExcludeIndexStats,
        [switch]$ExcludeUpdateUsage,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        if ($LogsOnly) {
            Test-DbaDeprecation -DeprecatedOn "1.0.0" -Parameter "LogsOnly"
            $FileType = 'Log'
        }

        $StatementTimeoutSeconds = $StatementTimeout * 60

        $sql = "SELECT
                  avg(avg_fragmentation_in_percent) as [avg_fragmentation_in_percent]
                , max(avg_fragmentation_in_percent) as [max_fragmentation_in_percent]
                FROM sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL, NULL, NULL) AS indexstats
                WHERE indexstats.avg_fragmentation_in_percent > 0 AND indexstats.page_count > 100
                GROUP BY indexstats.database_id"
    }

    process {
        if (!$Database -and !$ExcludeDatabase -and !$AllUserDatabases) {
            Stop-Function -Message "You must specify databases to execute against using either -Databases, -Exclude or -AllUserDatabases" -Continue
        }

        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            # changing statement timeout to $StatementTimeout
            if ($StatementTimeout -eq 0) {
                Write-Message -Level Verbose -Message "Changing statement timeout to infinity"
            }
            else {
                Write-Message -Level Verbose -Message "Changing statement timeout to $StatementTimeout minutes"
            }
            $server.ConnectionContext.StatementTimeout = $StatementTimeoutSeconds

            $dbs = $server.Databases | Where-Object { $_.IsSystemObject -eq $false -and $_.IsAccessible }

            if ($Database) {
                $dbs = $dbs | Where-Object Name -In $Database
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $dbs) {
                Write-Message -Level Verbose -Message "Processing $db on $instance"

                if ($db.IsDatabaseSnapshot) {
                    Write-Message -Level Warning -Message "The database $db on server $instance is a snapshot and cannot be shrunk. Skipping database."
                    continue
                }

                $startingSize = $db.Size
                $spaceAvailableMB = $db.SpaceAvailable / 1024
                $spaceUsed = $startingSize - $spaceAvailableMB
                $desiredSpaceAvailable = ($PercentFreeSpace * $spaceUsed) / 100

                Write-Message -Level Verbose -Message "Starting Size (MB): $startingSize"
                Write-Message -Level Verbose -Message "Starting Freespace (MB): $([int]$spaceAvailableMB)"
                Write-Message -Level Verbose -Message "Desired Freespace (MB): $([int]$desiredSpaceAvailable)"

                if (($db.SpaceAvailable / 1024) -le $desiredSpaceAvailable) {
                    Write-Message -Level Warning -Message "Space Available ($spaceAvailableMB) is less than or equal to the desired outcome ($desiredSpaceAvailable)"
                }
                else {
                    if ($Pscmdlet.ShouldProcess("$db on $instance", "Shrinking from $([int]$spaceAvailableMB) MB space available to $([int]$desiredSpaceAvailable) MB space available")) {
                        if ($server.VersionMajor -gt 8 -and $ExcludeIndexStats -eq $false) {
                            Write-Message -Level Verbose -Message "Getting starting average fragmentation"
                            $dataRow = $server.Query($sql, $db.name)
                            $startingFrag = $dataRow.avg_fragmentation_in_percent
                            $startingTopFrag = $dataRow.max_fragmentation_in_percent
                        }
                        else {
                            $startingTopFrag = $startingFrag = $null
                        }

                        $start = Get-Date

                        switch ($FileType) {
                            'Log' {
                                try {
                                    Write-Message -Level Verbose -Message "Beginning shrink of log files"
                                    $db.LogFiles.Shrink($desiredSpaceAvailable, $ShrinkMethod)
                                    $db.Refresh()
                                    $success = $true
                                    $notes = $null
                                }
                                catch {
                                    $success = $false
                                    Stop-Function -message "Shrink Failed:  $($_.Exception.InnerException)"  -EnableException $EnableException -ErrorRecord $_ -Continue
                                    continue
                                }
                            }
                            'Data' {
                                try {
                                    Write-Message -Level Verbose -Message "Beginning shrink of data files"
                                    foreach ($fileGroup in $db.FileGroups) {
                                        foreach ($file in $fileGroup.Files) {
                                            Write-Message -Level Verbose -Message "Beginning shrink of $($file.Name)"
                                            $file.Shrink($desiredSpaceAvailable, $ShrinkMethod)
                                        }
                                    }
                                    $db.Refresh()
                                    Write-Message -Level Verbose -Message "Recalculating space usage"
                                    if (-not $ExcludeUpdateUsage) { $db.RecalculateSpaceUsage() }
                                    $success = $true
                                    $notes = $null
                                }
                                catch {
                                    $success = $false
                                    Stop-Function -message "Shrink Failed:  $($_.Exception.InnerException)" -EnableException $EnableException -ErrorRecord $_ -Continue
                                    continue
                                }
                            }
                            default {
                                try {
                                    Write-Message -Level Verbose -Message "Beginning shrink of entire database"
                                    $db.Shrink($desiredSpaceAvailable, $ShrinkMethod)
                                    $db.Refresh()
                                    Write-Message -Level Verbose -Message "Recalculating space usage"
                                    if (-not $ExcludeUpdateUsage) { $db.RecalculateSpaceUsage() }
                                    $success = $true
                                    $notes = $null
                                }
                                catch {
                                    $success = $false
                                    Stop-Function -message "Shrink Failed:  $($_.Exception.InnerException)" -EnableException $EnableException -ErrorRecord $_ -Continue
                                    continue
                                }
                            }
                        }

                        $end = Get-Date
                        $dbSize = $db.Size
                        $newSpaceAvailableMB = $db.SpaceAvailable / 1024

                        Write-Message -Level Verbose -Message "Final database size: $([int]$dbSize) MB"
                        Write-Message -Level Verbose -Message "Final space available: $([int]$newSpaceAvailableMB) MB"

                        if ($server.VersionMajor -gt 8 -and $ExcludeIndexStats -eq $false -and $success -and $FileType -ne 'Log') {
                            Write-Message -Level Verbose -Message "Getting ending average fragmentation"
                            $dataRow = $server.Query($sql, $db.name)
                            $endingDefrag = $dataRow.avg_fragmentation_in_percent
                            $endingTopDefrag = $dataRow.max_fragmentation_in_percent
                        }
                        else {
                            $endingTopDefrag = $endingDefrag = $null
                        }

                        $timSpan = New-TimeSpan -Start $start -End $end
                        $ts = [TimeSpan]::fromseconds($timSpan.TotalSeconds)
                        $elapsed = "{0:HH:mm:ss}" -f ([datetime]$ts.Ticks)
                    }
                }

                if ($Pscmdlet.ShouldProcess("$db on $instance", "Showing results")) {
                    if ($null -eq $notes -and $FileType -ne 'Log') {
                        $notes = "Database shrinks can cause massive index fragmentation and negatively impact performance. You should now run DBCC INDEXDEFRAG or ALTER INDEX ... REORGANIZE"
                    }
                    $object = [PSCustomObject]@{
                        ComputerName                  = $server.ComputerName
                        InstanceName                  = $server.ServiceName
                        SqlInstance                   = $server.DomainInstanceName
                        Database                      = $db.name
                        Start                         = $start
                        End                           = $end
                        Elapsed                       = $elapsed
                        Success                       = $success
                        StartingTotalSizeMB           = [math]::Round($startingSize, 2)
                        StartingUsedMB                = [math]::Round($spaceUsed, 2)
                        FinalTotalSizeMB              = [math]::Round($db.size, 2)
                        StartingAvailableMB           = [math]::Round($spaceAvailableMB, 2)
                        DesiredAvailableMB            = [math]::Round($desiredSpaceAvailable, 2)
                        FinalAvailableMB              = [math]::Round(($db.SpaceAvailable / 1024), 2)
                        StartingAvgIndexFragmentation = [math]::Round($startingFrag, 1)
                        EndingAvgIndexFragmentation   = [math]::Round($endingDefrag, 1)
                        StartingTopIndexFragmentation = [math]::Round($startingTopFrag, 1)
                        EndingTopIndexFragmentation   = [math]::Round($endingTopDefrag, 1)
                        Notes                         = $notes
                    }

                    if ($ExcludeIndexStats) {
                        Select-DefaultView -InputObject $object -ExcludeProperty StartingAvgIndexFragmentation, EndingAvgIndexFragmentation, StartingTopIndexFragmentation, EndingTopIndexFragmentation
                    }
                    else {
                        $object
                    }
                }
            }
        }
    }
}

function Invoke-DbaDatabaseUpgrade {
    <#
    .SYNOPSIS
    Take a database and upgrades it to compatibility of the SQL Instance its hosted on. Based on https://thomaslarock.com/2014/06/upgrading-to-sql-server-2014-a-dozen-things-to-check/

    .DESCRIPTION
    Updates compatibility level, then runs CHECKDB with data_purity, DBCC updateusage, sp_updatestats and finally sp_refreshview against all user views.

    .PARAMETER SqlInstance
    The SQL Server that you're connecting to.

    .PARAMETER SqlCredential
    SqlCredential object used to connect to the SQL Server as a different user.

    .PARAMETER Database
    The database(s) to process - this list is autopopulated from the server. If unspecified, all databases will be processed.

    .PARAMETER ExcludeDatabase
    The database(s) to exclude - this list is autopopulated from the server

    .PARAMETER AllUserDatabases
    Run command against all user databases

    .PARAMETER Force
    Don't skip over databases that are already at the same level the instance is

    .PARAMETER NoCheckDb
    Skip checkdb

    .PARAMETER NoUpdateUsage
    Skip usage update

    .PARAMETER NoUpdateStats
    Skip stats update

    .PARAMETER NoRefreshView
    Skip view update

    .PARAMETER InputObject
    A collection of databases (such as returned by Get-DbaDatabase)

    .PARAMETER WhatIf
    Shows what would happen if the command were to run

    .PARAMETER Confirm
    Prompts for confirmation of every step. For example:

    Are you sure you want to perform this action?
    Performing the operation "Update database" on target "pubs on SQL2016\VNEXT".
    [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):

    .PARAMETER EnableException
    By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
    Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Tags: Shrink, Database
        Author: Stephen Bennett, https://sqlnotesfromtheunderground.wordpress.com/

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT


    .LINK
        https://dbatools.io/Invoke-DbaDatabaseUpgrade

    .EXAMPLE
        Invoke-DbaDatabaseUpgrade -SqlInstance PRD-SQL-MSD01 -Database Test

        Runs the below processes against the databases
        -- Puts compatibility of database to level of SQL Instance
        -- Runs CHECKDB DATA_PURITY
        -- Runs DBCC UPDATESUSAGE
        -- Updates all users statistics
        -- Runs sp_refreshview against every view in the database

    .EXAMPLE
        Invoke-DbaDatabaseUpgrade -SqlInstance PRD-SQL-INT01 -Database Test -NoRefreshView

        Runs the upgrade command skipping the sp_refreshview update on all views

    .EXAMPLE
        Invoke-DbaDatabaseUpgrade -SqlInstance PRD-SQL-INT01 -Database Test -Force

        If database Test is already at the correct compatibility, runs every necessary step

    .EXAMPLE
        Get-DbaDatabase -SqlInstance sql2016 | Out-GridView -Passthru | Invoke-DbaDatabaseUpgrade

        Get only specific databases using GridView and pass those to Invoke-DbaDatabaseUpgrade
#>
    [CmdletBinding(SupportsShouldProcess)]
    Param (
        [parameter(Position = 0)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [System.Management.Automation.PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$NoCheckDb,
        [switch]$NoUpdateUsage,
        [switch]$NoUpdateStats,
        [switch]$NoRefreshView,
        [switch]$AllUserDatabases,
        [switch]$Force,
        [parameter(ValueFromPipeline)]
        [Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {

        if (Test-Bound -not 'SqlInstance', 'InputObject') {
            Write-Message -Level Warning -Message "You must specify either a SQL instance or pipe a database collection"
            continue
        }

        if (Test-Bound -not 'Database', 'InputObject', 'ExcludeDatabase', 'AllUserDatabases') {
            Write-Message -Level Warning -Message "You must explicitly specify a database. Use -Database, -ExcludeDatabase, -AllUserDatabases or pipe a database collection"
            continue
        }

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level VeryVerbose -Message "Connecting to <c='green'>$instance</c>" -Target $instance
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
                $server.ConnectionContext.StatementTimeout = [Int32]::MaxValue
            }
            catch {
                Stop-Function -Message "Failed to process Instance $Instance" -ErrorRecord $_ -Target $instance -Continue
            }
            $InputObject += $server.Databases | Where-Object IsAccessible
        }

        $InputObject = $InputObject | Where-Object { $_.IsSystemObject -eq $false }
        if ($Database) {
            $InputObject = $InputObject | Where-Object { $_.Name -contains $Database }
        }
        if ($ExcludeDatabase) {
            $InputObject = $InputObject | Where-Object { $_.Name -notcontains $ExcludeDatabase }
        }

        foreach ($db in $InputObject) {
            # create objects to use in updates
            $server = $db.Parent
            $ServerVersion = $server.VersionMajor
            Write-Message -Level Verbose -Message "SQL Server is using Version: $ServerVersion"

            $ogcompat = $db.CompatibilityLevel
            $dbname = $db.Name
            $dbversion = switch ($db.CompatibilityLevel) {
                "Version100" { 10 } # SQL Server 2008
                "Version110" { 11 } # SQL Server 2012
                "Version120" { 12 } # SQL Server 2014
                "Version130" { 13 } # SQL Server 2016
                "Version140" { 14 } # SQL Server 2017
                default { 9 } # SQL Server 2005
            }
            if (-not $Force) {
                # skip over databases at the correct level, unless -Force
                if ($dbversion -ge $ServerVersion) {
                    Write-Message -Level VeryVerbose -Message "Skipping $db because compatibility is at the correct level. Use -Force if you want to run all the additional steps"
                    continue
                }
            }
            Write-Message -Level Verbose -Message "Updating $db compatibility to SQL Instance level"
            if ($dbversion -lt $ServerVersion) {
                If ($Pscmdlet.ShouldProcess($server, "Updating $db version on $server from $dbversion to $ServerVersion")) {
                    $Comp = $ServerVersion * 10
                    $tsqlComp = "ALTER DATABASE $db SET COMPATIBILITY_LEVEL = $Comp"
                    try {
                        $db.ExecuteNonQuery($tsqlComp)
                        $comResult = $Comp
                    }
                    catch {
                        Write-Message -Level Warning -Message "Failed run Compatibility Upgrade" -ErrorRecord $_ -Target $instance
                        $comResult = "Fail"
                    }
                }
            }
            else {
                $comResult = "No change"
            }

            if (!($NoCheckDb)) {
                Write-Message -Level Verbose -Message "Updating $db with DBCC CHECKDB DATA_PURITY"
                If ($Pscmdlet.ShouldProcess($server, "Updating $db with DBCC CHECKDB DATA_PURITY")) {
                    $tsqlCheckDB = "DBCC CHECKDB ('$dbname') WITH DATA_PURITY, NO_INFOMSGS"
                    try {
                        $db.ExecuteNonQuery($tsqlCheckDB)
                        $DataPurityResult = "Success"
                    }
                    catch {
                        Write-Message -Level Warning -Message "Failed run DBCC CHECKDB with DATA_PURITY on $db" -ErrorRecord $_ -Target $instance
                        $DataPurityResult = "Fail"
                    }
                }
            }
            else {
                Write-Message -Level Verbose -Message "Ignoring CHECKDB DATA_PURITY"
            }

            if (!($NoUpdateUsage)) {
                Write-Message -Level Verbose -Message "Updating $db with DBCC UPDATEUSAGE"
                If ($Pscmdlet.ShouldProcess($server, "Updating $db with DBCC UPDATEUSAGE")) {
                    $tsqlUpdateUsage = "DBCC UPDATEUSAGE ($db) WITH NO_INFOMSGS;"
                    try {
                        $db.ExecuteNonQuery($tsqlUpdateUsage)
                        $UpdateUsageResult = "Success"
                    }
                    catch {
                        Write-Message -Level Warning -Message "Failed to run DBCC UPDATEUSAGE on $db" -ErrorRecord $_ -Target $instance
                        $UpdateUsageResult = "Fail"
                    }
                }
            }
            else {
                Write-Message -Level Verbose -Message "Ignore DBCC UPDATEUSAGE"
                $UpdateUsageResult = "Skipped"
            }

            if (!($NoUpdatestats)) {
                Write-Message -Level Verbose -Message "Updating $db statistics"
                If ($Pscmdlet.ShouldProcess($server, "Updating $db statistics")) {
                    $tsqlStats = "EXEC sp_updatestats;"
                    try {
                        $db.ExecuteNonQuery($tsqlStats)
                        $UpdateStatsResult = "Success"
                    }
                    catch {
                        Write-Message -Level Warning -Message "Failed to run sp_updatestats on $db" -ErrorRecord $_ -Target $instance
                        $UpdateStatsResult = "Fail"
                    }
                }
            }
            else {
                Write-Message -Level Verbose -Message "Ignoring sp_updatestats"
                $UpdateStatsResult = "Skipped"
            }

            if (!($NoRefreshView)) {
                Write-Message -Level Verbose -Message "Refreshing $db Views"
                $dbViews = $db.Views | Where-Object IsSystemObject -eq $false
                $RefreshViewResult = "Success"
                foreach ($dbview in $dbviews) {
                    $viewName = $dbView.Name
                    $viewSchema = $dbView.Schema
                    $fullName = $viewSchema + "." + $viewName

                    $tsqlupdateView = "EXECUTE sp_refreshview N'$fullName';  "

                    If ($Pscmdlet.ShouldProcess($server, "Refreshing view $fullName on $db")) {
                        try {
                            $db.ExecuteNonQuery($tsqlupdateView)
                        }
                        catch {
                            Write-Message -Level Warning -Message "Failed update view $fullName on $db" -ErrorRecord $_ -Target $instance
                            $RefreshViewResult = "Fail"
                        }
                    }
                }
            }
            else {
                Write-Message -Level Verbose -Message "Ignore View Refreshes"
                $RefreshViewResult = "Skipped"
            }

            If ($Pscmdlet.ShouldProcess("console", "Outputting object")) {
                $db.Refresh()

                [PSCustomObject]@{
                    ComputerName          = $server.ComputerName
                    InstanceName          = $server.ServiceName
                    SqlInstance           = $server.DomainInstanceName
                    Database              = $db.name
                    OriginalCompatibility = $ogcompat.ToString().Replace('Version', '')
                    CurrentCompatibility  = $db.CompatibilityLevel.ToString().Replace('Version', '')
                    Compatibility         = $comResult
                    DataPurity            = $DataPurityResult
                    UpdateUsage           = $UpdateUsageResult
                    UpdateStats           = $UpdateStatsResult
                    RefreshViews          = $RefreshViewResult
                }
            }
        }
    }
}
function Invoke-DbaDbDecryptObject {
    <#
        .SYNOPSIS
            Invoke-DbaDbDecryptObject returns the decrypted version of an object

        .DESCRIPTION
            When a procedure or a function is created with encryption and you lost the code you're in trouble.
            You cannot alter the object or view the definition.
            With this command you can search for the object and decrypt the it.

            The command will output the results to the console.
            There is an option to export all the results to a folder creating .sql files.

            Make sure the instance allowed dedicated administrator connections (DAC).
            The binary versions of the objects can only be retrieved using a DAC connection.
            You can check the DAC connection with:
            'Get-DbaSpConfigure -SqlInstance [yourinstance] -ConfigName RemoteDacConnectionsEnabled'
            It should say 1 in the ConfiguredValue.

            To change the configurations you can use the Set-DbaSpConfigure command:
            'Set-DbaSpConfigure -SqlInstance [yourinstance] -ConfigName RemoteDacConnectionsEnabled -Value 1'
            In some cases you may need to reboot the instance.

        .PARAMETER SqlInstance
            The target SQL Server instance

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Database to look through for the object.

        .PARAMETER ObjectName
            The name of the object to search for in the database.

        .PARAMETER EncodingType
            The encoding that's used to decrypt and encrypt values.

        .PARAMETER ExportDestination
            Used for exporting the results to.
            The destiation will use the instance name, database name and object type i.e.: C:\temp\decrypt\SQLDB1\DB1\StoredProcedure

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Encryption, Decrypt, Database
            Author: Sander Stad (@sqlstad, sqlstad.nl)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Invoke-DbaDbDecryptObject

        .EXAMPLE
            Invoke-DbaDbDecryptObject -SqlInstance SQLDB1 -Database DB1 -ObjectName Function1

            Decrypt object "Function1" in DB1 of instance SQLDB1 and output the data to the user.

        .EXAMPLE
            Invoke-DbaDbDecryptObject -SqlInstance SQLDB1 -Database DB1 -ObjectName Function1 -ExportDestination C:\temp\decrypt

            Decrypt object "Function1" in DB1 of instance SQLDB1 and output the data to the folder "C:\temp\decrypt".

        .EXAMPLE
            Invoke-DbaDbDecryptObject -SqlInstance SQLDB1 -Database DB1 -ExportDestination C:\temp\decrypt

            Decrypt all objects in DB1 of instance SQLDB1 and output the data to the folder "C:\temp\decrypt"

        .EXAMPLE
            Invoke-DbaDbDecryptObject -SqlInstance SQLDB1 -Database DB1 -ObjectName Function1, Function2

            Decrypt objects "Function1" and "Function2" and output the data to the user.

        .EXAMPLE
            "SQLDB1" | Invoke-DbaDbDecryptObject -Database DB1 -ObjectName Function1, Function2

            Decrypt objects "Function1" and "Function2" and output the data to the user using a pipeline for the instance.
    #>
   [CmdletBinding()]
    param(
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory = $true)]
        [object[]]$Database,
        [string[]]$ObjectName,
        [ValidateSet('ASCII', 'UTF8')]
        [string]$EncodingType = 'ASCII',
        [string]$ExportDestination,
        [switch]$EnableException
    )

    begin {

        function Invoke-DecryptData() {
            param(
                [parameter(Mandatory = $true)]
                [byte[]]$Secret,
                [parameter(Mandatory = $true)]
                [byte[]]$KnownPlain,
                [parameter(Mandatory = $true)]
                [byte[]]$KnownSecret
            )

            # Declare pointers
            [int]$i = 0

            # Loop through each of the characters and apply an XOR to decrypt the data
            $result = $(

                # Loop through the byte string
                while ($i -lt $Secret.Length) {

                    # Compare the byte string character to the key character using XOR
                    if ($i -lt $Secret.Length) {
                        $Secret[$i] -bxor $KnownPlain[$i] -bxor $KnownSecret[$i]
                    }

                    # Increment the byte string indicator
                    $i += 2

                } # end while loop

            ) # end data value

            # Get the string value from the data
            $decryptedData = $Encoding.GetString($result)

            # Return the decrypted data
            return $decryptedData
        }

        # Create array list to hold the results
        $objectCollection = New-Object System.Collections.ArrayList

        # Set the encoding
        if ($EncodingType -eq 'ASCII') {
            $encoding = [System.Text.Encoding]::ASCII
        }
        elseif ($EncodingType -eq 'UTF8') {
            $encoding = [System.Text.Encoding]::UTF8
        }

        # Check the export parameter
        if ($ExportDestination -and -not (Test-Path $ExportDestination)) {
            try {
                # Create the new destination
                New-Item -Path $ExportDestination -ItemType Directory -Force | Out-Null
            }
            catch {
                Stop-Function -Message "Couldn't create destination folder $ExportDestination" -ErrorRecord $_ -Target $instance -Continue
            }
        }

    }

    process {

        if (Test-FunctionInterrupt) { return }

        # Loop through all the instances
        foreach ($instance in $SqlInstance) {

            # Check the configuration of the intance to see if the DAC is enabled
            $config = Get-DbaSpConfigure -SqlInstance $instance -ConfigName RemoteDacConnectionsEnabled
            if ($config.ConfiguredValue -ne 1) {
                Stop-Function -Message "DAC is not enabled for instance $instance.`nPlease use 'Set-DbaSpConfigure -SqlInstance $instance -ConfigName RemoteDacConnectionsEnabled -Value 1' to configure the instance to allow DAC connections" -Target $instance -Continue
            }

            # Try to connect to instance
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = New-Object Microsoft.SqlServer.Management.Smo.Server "ADMIN:$instance"
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            # Get all the databases that compare to the database parameter
            $databaseCollection = $server.Databases | Where-Object {$_.Name -in $Database}

            # Loop through each of databases
            foreach ($db in $databaseCollection) {
                # Get the objects
                if($ObjectName){
                    $storedProcedures = @($db.StoredProcedures | Where-Object {$_.Name -in $ObjectName -and $_.IsEncrypted -eq $true} | Select-Object Name, Schema, @{N = "ObjectType"; E = {'StoredProcedure'}}, @{N = "SubType"; E = {''}})
                    $functions = @($db.UserDefinedFunctions | Where-Object {$_.Name -in $ObjectName -and $_.IsEncrypted -eq $true} | Select-Object Name, Schema, @{N = "ObjectType"; E = {"UserDefinedFunction"}}, @{N = "SubType"; E = {$_.FunctionType.ToString().Trim()}})
                    $views = @($db.Views | Where-Object {$_.Name -in $ObjectName -and $_.IsEncrypted -eq $true} | Select-Object Name, Schema, @{N = "ObjectType"; E = {'View'}}, @{N = "SubType"; E = {''}})
                }
                else{
                    # Get all encrypted objects
                    $storedProcedures = @($db.StoredProcedures | Where-Object {$_.IsEncrypted -eq $true} | Select-Object Name, Schema, @{N = "ObjectType"; E = {'StoredProcedure'}}, @{N = "SubType"; E = {''}})
                    $functions = @($db.UserDefinedFunctions | Where-Object {$_.IsEncrypted -eq $true} | Select-Object Name, Schema, @{N = "ObjectType"; E = {"UserDefinedFunction"}}, @{N = "SubType"; E = {$_.FunctionType.ToString().Trim()}})
                    $views = @($db.Views | Where-Object {$_.IsEncrypted -eq $true} | Select-Object Name, Schema, @{N = "ObjectType"; E = {'View'}}, @{N = "SubType"; E = {''}})
                }

                <# Get all the objects
                $storedProcedures = @($db.StoredProcedures | Where-Object {$_.Name -in $ObjectName -and $_.IsEncrypted -eq $true} | Select-Object Name, Schema, @{N = "ObjectType"; E = {'StoredProcedure'}}, @{N = "SubType"; E = {''}})
                $functions = @($db.UserDefinedFunctions | Where-Object {$_.Name -in $ObjectName -and $_.IsEncrypted -eq $true} | Select-Object Name, Schema, @{N = "ObjectType"; E = {"UserDefinedFunction"}}, @{N = "SubType"; E = {$_.FunctionType.ToString().Trim()}})
                $views = @($db.Views | Where-Object {$_.Name -in $ObjectName -and $_.IsEncrypted -eq $true} | Select-Object Name, Schema, @{N = "ObjectType"; E = {'View'}}, @{N = "SubType"; E = {''}})
                #>

                # Check if there are any objects
                if ($storedProcedures.Count -ge 1) {
                    $objectCollection += $storedProcedures
                }
                if ($functions.Count -ge 1) {
                    $objectCollection += $functions
                }
                if ($views.Count -ge 1) {
                    $objectCollection += $views
                }

                # Loop through all the objects
                foreach ($object in $objectCollection) {

                    # Setup the query to get the secret
                    $querySecret = "SELECT imageval AS Value FROM sys.sysobjvalues WHERE objid = OBJECT_ID('$($object.Name)')"

                    # Get the result of the secret query
                    try {
                        $secret = $server.Databases[$db.Name].Query($querySecret)
                    }
                    catch {
                        Stop-Function -Message "Couldn't retrieve secret from $instance" -ErrorRecord $_ -Target $instance -Continue
                    }

                    # Check if at least a value came back
                    if ($secret) {

                        # Setup a known plain command and get the binary version of it
                        switch ($object.ObjectType) {

                            'StoredProcedure' {
                                $queryKnownPlain = (" " * $secret.Value.Length) + "ALTER PROCEDURE $($object.Schema).$($object.Name) WITH ENCRYPTION AS RETURN 0;"
                            }
                            'UserDefinedFunction' {

                                switch ($object.SubType) {
                                    'Inline' {
                                        $queryKnownPlain = (" " * $secret.value.length) + "ALTER FUNCTION $($object.Schema).$($object.Name)() RETURNS TABLE WITH ENCRYPTION AS BEGIN RETURN SELECT 0 i END;"
                                    }
                                    'Scalar' {
                                        $queryKnownPlain = (" " * $secret.value.length) + "ALTER FUNCTION $($object.Schema).$($object.Name)() RETURNS INT WITH ENCRYPTION AS BEGIN RETURN 0 END;"
                                    }
                                    'Table' {
                                        $queryKnownPlain = (" " * $secret.value.length) + "ALTER FUNCTION $($object.Schema).$($object.Name)() RETURNS @r TABLE(i INT) WITH ENCRYPTION AS BEGIN RETURN END;"
                                    }
                                }
                            }
                            'View' {
                                $queryKnownPlain = (" " * $secret.Value.Length) + "ALTER VIEW $($object.Schema).$($object.Name) WITH ENCRYPTION AS SELECT NULL AS [Value];"
                            }
                        }

                        # Convert the known plain into binary
                        if ($queryKnownPlain) {
                            try {
                                $knownPlain = $encoding.GetBytes(($queryKnownPlain))
                            }
                            catch {
                                Stop-Function -Message "Couldn't convert the known plain to binary" -ErrorRecord $_ -Target $instance -Continue
                            }
                        }
                        else {
                            Stop-Function -Message "Something went wrong setting up the known plain" -ErrorRecord $_ -Target $instance -Continue
                        }

                        # Setup the query to change the object in SQL Server and roll it back getting the encrypted version
                        $queryKnownSecret = "
                            BEGIN TRANSACTION;
                                EXEC ('$queryKnownPlain');
                                SELECT imageval AS Value
                                FROM sys.sysobjvalues
                                WHERE objid = OBJECT_ID('$($object.Name)');
                            ROLLBACK;
                        "

                        # Get the result for the known encrypted
                        try {
                            $knownSecret = $server.Databases[$db.Name].Query($queryKnownSecret)
                        }
                        catch {
                            Stop-Function -Message "Couldn't retrieve known secret from $instance" -ErrorRecord $_ -Target $instance -Continue
                        }

                        # Get the result
                        $result = Invoke-DecryptData -Secret $secret.value -KnownPlain $knownPlain -KnownSecret $knownSecret.value

                        # Check if the results need to be exported
                        if ($ExportDestination) {
                            # make up the file name
                            $filename = "$($object.Schema).$($object.Name).sql"

                            # Check the export destination
                            if ($ExportDestination.EndsWith("\")) {
                                $destinationFolder = "$ExportDestination$instance\$($db.Name)\$($object.ObjectType)\"
                            }
                            else {
                                $destinationFolder = "$ExportDestination\$instance\$($db.Name)\$($object.ObjectType)\"
                            }

                            # Check if the destination folder exists
                            if (-not (Test-Path $destinationFolder)) {
                                try {
                                    # Create the new destination
                                    New-Item -Path $destinationFolder -ItemType Directory -Force:$Force | Out-Null
                                }
                                catch {
                                    Stop-Function -Message "Couldn't create destination folder $destinationFolder" -ErrorRecord $_ -Target $instance -Continue
                                }
                            }

                            # Combine the destination folder and the file name to get the path
                            $filePath = $destinationFolder + $filename

                            # Export the result
                            try {
                                $result | Out-File -FilePath $filePath -Force
                            }
                            catch {
                                Stop-Function -Message "Couldn't export the results of $($object.Name) to $filePath" -ErrorRecord $_ -Target $instance -Continue
                            }

                        }

                        # Add the results to the custom object
                        [PSCustomObject]@{
                                ComputerName    = $server.ComputerName
                                InstanceName    = $server.ServiceName
                                SqlInstance     = $server.DomainInstanceName
                                Database        = $db.Name
                                Type            = $object.ObjectType
                                Schema          = $object.Schema
                                Name            = $object.Name
                                FullName        = "$($object.Schema).$($object.Name)"
                                Script          = $result
                            }

                    } # end if secret

                } # end for each object

            } # end for each database

        } # end for each instance

    } # process

    end {
        if (Test-FunctionInterrupt) { return }

        Write-Message -Message "Finished decrypting data" -Level Verbose
    }
}
function Invoke-DbaDiagnosticQuery {
    <#
    .SYNOPSIS
    Invoke-DbaDiagnosticQuery runs the scripts provided by Glenn Berry's DMV scripts on specified servers

    .DESCRIPTION
    This is the main function of the Sql Server Diagnostic Queries related functions in dbatools.
    The diagnostic queries are developed and maintained by Glenn Berry and they can be found here along with a lot of documentation:
    http://www.sqlskills.com/blogs/glenn/category/dmv-queries/

    The most recent version of the diagnostic queries are included in the dbatools module.
    But it is possible to download a newer set or a specific version to an alternative location and parse and run those scripts.
    It will run all or a selection of those scripts on one or multiple servers and return the result as a PowerShell Object

    .PARAMETER SqlInstance
    The target SQL Server. Can be either a string or SMO server

    .PARAMETER SqlCredential
    Allows alternative Windows or SQL login credentials to be used

    .PARAMETER Path
    Alternate path for the diagnostic scripts

    .PARAMETER Database
    The database(s) to process. If unspecified, all databases will be processed

    .PARAMETER ExcludeDatabase
    The database(s) to exclude

    .PARAMETER UseSelectionHelper
    Provides a gridview with all the queries to choose from and will run the selection made by the user on the Sql Server instance specified.

    .PARAMETER QueryName
    Only run specific query

    .PARAMETER InstanceOnly
    Run only instance level queries

    .PARAMETER DatabaseSpecific
    Run only database level queries

    .PARAMETER NoQueryTextColumn
    Use this switch to exclude the [Complete Query Text] column from relevant queries

    .PARAMETER NoPlanColumn
    Use this switch to exclude the [Query Plan] column from relevant queries

    .PARAMETER NoColumnParsing
    Does not parse the [Complete Query Text] and [Query Plan] columns and disregards the NoQueryTextColumn and NoColumnParsing switches

    .PARAMETER EnableException
    By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
    Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .PARAMETER Confirm
    Prompts to confirm certain actions

    .PARAMETER WhatIf
    Shows what would happen if the command would execute, but does not actually perform the command

    .PARAMETER OutputPath
    Directory to parsed diagnostict queries to. This will split them based on server, databasename, and query.

    .PARAMETER ExportQueries
    Use this switch to export the diagnostic queries to sql files. I
    nstead of running the queries, the server will be evaluated to find the appropriate queries to run based on SQL Version.
    These sql files will then be created in the OutputDirectory.



    .NOTES
    Tags: Database, DMV
    Author: André Kamman (@AndreKamman), http://clouddba.io
    Website: https://dbatools.io
    Copyright: (C) Chrissy LeMaire, [email protected]
    License: MIT https://opensource.org/licenses/MIT

    .LINK
    https://dbatools.io/Invoke-DbaDiagnosticQuery

    .EXAMPLE
    Invoke-DbaDiagnosticQuery -SqlInstance sql2016

    Run the selection made by the user on the Sql Server instance specified.

    .EXAMPLE
    Invoke-DbaDiagnosticQuery -SqlInstance sql2016 -UseSelectionHelper | Export-DbaDiagnosticQuery -Path C:\temp\gboutput

    Provides a gridview with all the queries to choose from and will run the selection made by the user on the SQL Server instance specified.
    Then it will export the results to Export-DbaDiagnosticQuery.

    .Example
    # Exporting Queries To SQL Files
    - Parse the appropriate diagnostic queries by connecting to server to get version matched queries
    - Instead of running the diagnostic queries, export them to SQL files

    # Export All Queries to Disk
    Invoke-DbaDiagnosticQuery -sqlinstance localhost -ExportQueries -outputpath "C:\temp\DiagnosticQueries"

    # Export Database Specific Queries for all User Dbs
    Invoke-DbaDiagnosticQuery -sqlinstance localhost -DatabaseSpecific -DatabaseName 'tempdb' -ExportQueries -outputpath "C:\temp\DiagnosticQueries"

    # Export Database Specific Queries For One Target Database
    Invoke-DbaDiagnosticQuery -sqlinstance localhost -DatabaseSpecific -DatabaseName 'tempdb' -ExportQueries -outputpath "C:\temp\DiagnosticQueries"

    # Export Database Specific Queries For One Target Database and One Specific Query
    Invoke-DbaDiagnosticQuery -sqlinstance localhost -DatabaseSpecific -DatabaseName 'tempdb' -ExportQueries -outputpath "C:\temp\DiagnosticQueries" -queryname 'Database-scoped Configurations'

    # Choose Queries To Export
    Invoke-DbaDiagnosticQuery -sqlinstance localhost -UseSelectionHelper

    This will export with SqlInstance, DatabaseName, and QueryName as appropriate based on query.

    .Example
    # Export Queries (not run) as returned object

    [System.Management.Automation.PSObject[]]$results = Invoke-DbaDiagnosticQuery -SqlInstance localhost -whatif

    Parse the appropriate diagnostic queries by connecting to server, and instead of running them, return as [pscustomobject[]] to work with further

    .Example
    # Database Specific Handling
    $results = Invoke-DbaDiagnosticQuery -SqlInstance $script:instance2 -DatabaseSpecific -queryname 'Database-scoped Configurations' -databasename $database

    Run diagnostic queries targeted at specific database, and only run database level queries against this database.

    #>

    [CmdletBinding(SupportsShouldProcess)]
    [outputtype([pscustomobject[]])]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,

        [Alias('DatabaseName')]
        [object[]]$Database,

        [object[]]$ExcludeDatabase,

        [Alias('Credential')]
        [PSCredential]$SqlCredential,
        [System.IO.FileInfo]$Path,
        [string[]]$QueryName,
        [switch]$UseSelectionHelper,
        [switch]$InstanceOnly,
        [switch]$DatabaseSpecific,
        [Switch]$NoQueryTextColumn,
        [Switch]$NoPlanColumn,
        [Switch]$NoColumnParsing,

        [string]$OutputPath,
        [switch]$ExportQueries,

        [switch][Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $ProgressId = Get-Random

        function Invoke-DiagnosticQuerySelectionHelper {
            [CmdletBinding()]
            Param (
                [parameter(Mandatory = $true)]
                $ParsedScript
            )

            $ParsedScript | Select-Object QueryNr, QueryName, DBSpecific, Description | Out-GridView -Title "Diagnostic Query Overview" -OutputMode Multiple | Sort-Object QueryNr | Select-Object -ExpandProperty QueryName

        }

        Write-Message -Level Verbose -Message "Interpreting DMV Script Collections"

        $module = Get-Module -Name dbatools
        $base = $module.ModuleBase

        if (!$Path) {
            $Path = "$base\bin\diagnosticquery"
        }

        $scriptversions = @()
        $scriptfiles = Get-ChildItem "$Path\SQLServerDiagnosticQueries_*_*.sql"

        if (!$scriptfiles) {
            Write-Message -Level Warning -Message "Diagnostic scripts not found in $Path. Using the ones within the module."

            $Path = "$base\bin\diagnosticquery"

            $scriptfiles = Get-ChildItem "$base\bin\diagnosticquery\SQLServerDiagnosticQueries_*_*.sql"
            if (!$scriptfiles) {
                Stop-Function -Message "Unable to download scripts, do you have an internet connection? $_" -ErrorRecord $_
                return
            }
        }

        [int[]]$filesort = $null

        foreach ($file in $scriptfiles) {
            $filesort += $file.BaseName.Split("_")[2]
        }

        $currentdate = $filesort | Sort-Object -Descending | Select-Object -First 1

        foreach ($file in $scriptfiles) {
            if ($file.BaseName.Split("_")[2] -eq $currentdate) {
                $parsedscript = Invoke-DbaDiagnosticQueryScriptParser -filename $file.fullname -NoQueryTextColumn:$NoQueryTextColumn -NoPlanColumn:$NoPlanColumn -NoColumnParsing:$NoColumnParsing

                $newscript = [pscustomobject]@{
                    Version = $file.Basename.Split("_")[1]
                    Script  = $parsedscript
                }
                $scriptversions += $newscript
            }
        }
    }

    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $SqlInstance) {
            $counter = 0
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            Write-Message -Level Verbose -Message "Collecting diagnostic query data from server: $instance"

            if ($server.VersionMinor -eq 50) {
                $version = "2008R2"
            }
            else {
                $version = switch ($server.VersionMajor) {
                    9 { "2005" }
                    10 { "2008" }
                    11 { "2012" }
                    12 { "2014" }
                    13 { "2016" }
                    14 { "2017" }
                }
            }

            if (!$instanceOnly) {
                if (-not $Database) {
                    $databases = (Get-DbaDatabase -SqlInstance $server -ExcludeAllSystemDb -ExcludeDatabase $ExcludeDatabase).Name
                }
                else {
                    $databases = (Get-DbaDatabase -SqlInstance $server -ExcludeAllSystemDb -Database $Database -ExcludeDatabase $ExcludeDatabase).Name
                }
            }

            $parsedscript = $scriptversions | Where-Object -Property Version -eq $version | Select-Object -ExpandProperty Script

            if ($null -eq $first) { $first = $true }
            if ($UseSelectionHelper -and $first) {
                $QueryName = Invoke-DiagnosticQuerySelectionHelper $parsedscript
                $first = $false
            }
            #since some database level queries can take longer (such as fragmentation) calculate progress with database specific queries * count of databases to run against into context
            $CountOfDatabases = ($databases).Count


            if ($QueryName.Count -ne 0) {
                #if running all queries, then calculate total to run by instance queries count + (db specific count * databases to run each against)
                $countDBSpecific = @($parsedscript | Where-Object {$_.QueryName -in $QueryName -and $_.DBSpecific -eq $true}).Count
                $countInstanceSpecific = @($parsedscript | Where-Object {$_.QueryName -in $QueryName -and $_.DBSpecific -eq $false}).Count
            }
            else {
                #if narrowing queries to database specific, calculate total to process based on instance queries count + (db specific count * databases to run each against)
                $countDBSpecific = @($parsedscript | Where-Object DBSpecific).Count
                $countInstanceSpecific = @($parsedscript | Where-Object DBSpecific -eq $false).Count

            }
            if (!$instanceonly -and !$DatabaseSpecific -and !$QueryName) {
                $scriptcount = $countInstanceSpecific + ($countDBSpecific * $CountOfDatabases )
            }
            elseif ($instanceOnly) {
                $scriptcount = $countInstanceSpecific
            }
            elseif ($DatabaseSpecific) {
                $scriptcount = $countDBSpecific * $CountOfDatabases
            }
            elseif ($QueryName.Count -ne 0) {
                $scriptcount = $countInstanceSpecific + ($countDBSpecific * $CountOfDatabases )


            }

            foreach ($scriptpart in $parsedscript) {

                if (($QueryName.Count -ne 0) -and ($QueryName -notcontains $scriptpart.QueryName)) { continue }
                if (!$scriptpart.DBSpecific -and !$DatabaseSpecific) {
                    if ($ExportQueries) {
                        $null = New-Item -Path $OutputPath -ItemType Directory -Force
                        $FileName = Remove-InvalidFileNameChars ('{0}.sql' -f $Scriptpart.QueryName)
                        $FullName = Join-Path $OutputPath $FileName
                        Write-Message -Level Verbose -Message  "Creating file: $FullName"
                        $scriptPart.Text | out-file -FilePath $FullName -Encoding UTF8 -force
                        continue
                    }

                    if ($PSCmdlet.ShouldProcess($instance, $scriptpart.QueryName)) {

                        if (-not $EnableException) {
                            $Counter++
                            Write-Progress -Id $ProgressId -ParentId 0 -Activity "Collecting diagnostic query data from $instance" -Status "Processing $counter of $scriptcount" -CurrentOperation $scriptpart.QueryName -PercentComplete (($counter / $scriptcount) * 100)
                        }

                        try {
                            $result = $server.Query($scriptpart.Text)
                            Write-Message -Level Verbose -Message "Processed $($scriptpart.QueryName) on $instance"
                            if (!$result) {
                                [pscustomobject]@{
                                    ComputerName     = $server.ComputerName
                                    InstanceName     = $server.ServiceName
                                    SqlInstance      = $server.DomainInstanceName
                                    Number           = $scriptpart.QueryNr
                                    Name             = $scriptpart.QueryName
                                    Description      = $scriptpart.Description
                                    DatabaseSpecific = $scriptpart.DBSpecific
                                    Database         = $null
                                    Notes            = "Empty Result for this Query"
                                    Result           = $null
                                }
                                Write-Message -Level Verbose -Message ("Empty result for Query {0} - {1} - {2}" -f $scriptpart.QueryNr, $scriptpart.QueryName, $scriptpart.Description)
                            }
                        }
                        catch {
                            Write-Message -Level Verbose -Message ('Some error has occured on Server: {0} - Script: {1}, result unavailable' -f $instance, $scriptpart.QueryName) -Target $instance -ErrorRecord $_
                        }
                        if ($result) {
                            [pscustomobject]@{
                                ComputerName     = $server.ComputerName
                                InstanceName     = $server.ServiceName
                                SqlInstance      = $server.DomainInstanceName
                                Number           = $scriptpart.QueryNr
                                Name             = $scriptpart.QueryName
                                Description      = $scriptpart.Description
                                DatabaseSpecific = $scriptpart.DBSpecific
                                Database         = $null
                                Notes            = $null
                                #Result           = Select-DefaultView -InputObject $result -Property *
                                #Not using Select-DefaultView because excluding the fields below doesn't seem to work
                                Result           = $result | Select-Object * -ExcludeProperty 'Item', 'RowError', 'RowState', 'Table', 'ItemArray', 'HasErrors'
                            }

                        }
                    }
                    else {
                        # if running WhatIf, then return the queries that would be run as an object, not just whatif output

                        [pscustomobject]@{
                            ComputerName     = $server.ComputerName
                            InstanceName     = $server.ServiceName
                            SqlInstance      = $server.DomainInstanceName
                            Number           = $scriptpart.QueryNr
                            Name             = $scriptpart.QueryName
                            Description      = $scriptpart.Description
                            DatabaseSpecific = $scriptpart.DBSpecific
                            Database         = $null
                            Notes            = "WhatIf - Bypassed Execution"
                            Result           = $null
                        }
                    }

                }
                elseif ($scriptpart.DBSpecific -and !$instanceOnly) {

                    foreach ($currentdb in $databases) {
                        if ($ExportQueries) {
                            $null = New-Item -Path $OutputPath -ItemType Directory -Force
                            $FileName = Remove-InvalidFileNameChars ('{0}-{1}-{2}.sql' -f $server.DomainInstanceName, $currentDb, $Scriptpart.QueryName)
                            $FullName = Join-Path $OutputPath $FileName
                            Write-Message -Level Verbose -Message  "Creating file: $FullName"
                            $scriptPart.Text | out-file -FilePath $FullName -encoding UTF8 -force
                            continue
                        }


                        if ($PSCmdlet.ShouldProcess(('{0} ({1})' -f $instance, $currentDb), $scriptpart.QueryName)) {

                            if (-not $EnableException) {
                                $Counter++
                                Write-Progress -Id $ProgressId -ParentId 0 -Activity "Collecting diagnostic query data from $($currentDb) on $instance" -Status ('Processing {0} of {1}' -f $counter, $scriptcount) -CurrentOperation $scriptpart.QueryName -PercentComplete (($Counter / $scriptcount) * 100)
                            }

                            Write-Message -Level Verbose -Message "Collecting diagnostic query data from $($currentDb) for $($scriptpart.QueryName) on $instance"
                            try {
                                $result = $server.Query($scriptpart.Text, $currentDb)
                                if (!$result) {
                                    [pscustomobject]@{
                                        ComputerName     = $server.ComputerName
                                        InstanceName     = $server.ServiceName
                                        SqlInstance      = $server.DomainInstanceName
                                        Number           = $scriptpart.QueryNr
                                        Name             = $scriptpart.QueryName
                                        Description      = $scriptpart.Description
                                        DatabaseSpecific = $scriptpart.DBSpecific
                                        Database         = $currentdb
                                        Notes            = "Empty Result for this Query"
                                        Result           = $null
                                    }
                                    Write-Message -Level Verbose -Message ("Empty result for Query {0} - {1} - {2}" -f $scriptpart.QueryNr, $scriptpart.QueryName, $scriptpart.Description) -Target $scriptpart -ErrorRecord $_
                                }
                            }
                            catch {
                                Write-Message -Level Verbose -Message ('Some error has occured on Server: {0} - Script: {1} - Database: {2}, result will not be saved' -f $instance, $scriptpart.QueryName, $currentDb) -Target $currentdb -ErrorRecord $_
                            }

                            if ($result){
                                [pscustomobject]@{
                                    ComputerName     = $server.ComputerName
                                    InstanceName     = $server.ServiceName
                                    SqlInstance      = $server.DomainInstanceName
                                    Number           = $scriptpart.QueryNr
                                    Name             = $scriptpart.QueryName
                                    Description      = $scriptpart.Description
                                    DatabaseSpecific = $scriptpart.DBSpecific
                                    Database         = $currentDb
                                    Notes            = $null
                                    #Result           = Select-DefaultView -InputObject $result -Property *
                                    #Not using Select-DefaultView because excluding the fields below doesn't seem to work
                                    Result           = $result | Select-Object * -ExcludeProperty 'Item', 'RowError', 'RowState', 'Table', 'ItemArray', 'HasErrors'
                                }
                            }
                        }
                        else {
                            # if running WhatIf, then return the queries that would be run as an object, not just whatif output

                            [pscustomobject]@{
                                ComputerName     = $server.ComputerName
                                InstanceName     = $server.ServiceName
                                SqlInstance      = $server.DomainInstanceName
                                Number           = $scriptpart.QueryNr
                                Name             = $scriptpart.QueryName
                                Description      = $scriptpart.Description
                                DatabaseSpecific = $scriptpart.DBSpecific
                                Database         = $null
                                Notes            = "WhatIf - Bypassed Execution"
                                Result           = $null
                            }
                        }
                    }
                }
            }
        }
    }
    end {
        Write-Progress -Id $ProgressId -Activity 'Invoke-DbaDiagnosticQuery' -Completed
    }
}
function Invoke-DbaLogShipping {
    <#
        .SYNOPSIS
            Invoke-DbaLogShipping sets up log shipping for one or more databases

        .DESCRIPTION
            Invoke-DbaLogShipping helps to easily set up log shipping for one or more databases.

            This function will make a lot of decisions for you assuming you want default values like a daily interval for the schedules with a 15 minute interval on the day.
            There are some settings that cannot be made by the function and they need to be prepared before the function is executed.

            The following settings need to be made before log shipping can be initiated:
            - Backup destination (the folder and the privileges)
            - Copy destination (the folder and the privileges)

            * Privileges
            Make sure your agent service on both the primary and the secondary instance is an Active Directory account.
            Also have the credentials ready to set the folder permissions

            ** Network share
            The backup destination needs to be shared and have the share privileges of FULL CONTROL to Everyone.

            ** NTFS permissions
            The backup destination must have at least read/write permissions for the primary instance agent account.
            The backup destination must have at least read permissions for the secondary instance agent account.
            The copy destination must have at least read/write permission for the secondary instance agent acount.

        .PARAMETER SourceSqlInstance
            Source SQL Server instance which contains the databases to be log shipped.
            You must have sysadmin access and server version must be SQL Server version 2000 or greater.

        .PARAMETER DestinationSqlInstance
            Destination SQL Server instance which contains the databases to be log shipped.
            You must have sysadmin access and server version must be SQL Server version 2000 or greater.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER SourceCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER DestinationCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Database to set up log shipping for.

        .PARAMETER BackupNetworkPath
            The backup unc path to place the backup files. This is the root directory.
            A directory with the name of the database will be created in this path.

        .PARAMETER BackupLocalPath
            If the backup path is locally for the source server you can also set this value.

        .PARAMETER BackupJob
            Name of the backup that will be created in the SQL Server agent.
            The parameter works as a prefix where the name of the database will be added to the backup job name.
            The default is "LSBackup_[databasename]"

        .PARAMETER BackupRetention
            The backup retention period in minutes. Default is 4320 / 72 hours

        .PARAMETER BackupSchedule
            Name of the backup schedule created for the backup job.
            The parameter works as a prefix where the name of the database will be added to the backup job schedule name.
            Default is "LSBackupSchedule_[databasename]"

        .PARAMETER BackupScheduleDisabled
            Parameter to set the backup schedule to disabled upon creation.
            By default the schedule is enabled.

        .PARAMETER BackupScheduleFrequencyType
            A value indicating when a job is to be executed.
            Allowed values are "Daily", "AgentStart", "IdleComputer"

        .PARAMETER BackupScheduleFrequencyInterval
            The number of type periods to occur between each execution of the backup job.

        .PARAMETER BackupScheduleFrequencySubdayType
            Specifies the units for the sub-day FrequencyInterval.
            Allowed values are "Time", "Seconds", "Minutes", "Hours"

        .PARAMETER BackupScheduleFrequencySubdayInterval
            The number of sub-day type periods to occur between each execution of the backup job.

        .PARAMETER BackupScheduleFrequencyRelativeInterval
            A job's occurrence of FrequencyInterval in each month, if FrequencyInterval is 32 (monthlyrelative).

        .PARAMETER BackupScheduleFrequencyRecurrenceFactor
            The number of weeks or months between the scheduled execution of a job. FrequencyRecurrenceFactor is used only if FrequencyType is 8, "Weekly", 16, "Monthly", 32 or "MonthlyRelative".

        .PARAMETER BackupScheduleStartDate
            The date on which execution of a job can begin.

        .PARAMETER BackupScheduleEndDate
            The date on which execution of a job can stop.

        .PARAMETER BackupScheduleStartTime
            The time on any day to begin execution of a job. Format HHMMSS / 24 hour clock.
            Example: '010000' for 01:00:00 AM.
            Example: '140000' for 02:00:00 PM.

        .PARAMETER BackupScheduleEndTime
            The time on any day to end execution of a job. Format HHMMSS / 24 hour clock.
            Example: '010000' for 01:00:00 AM.
            Example: '140000' for 02:00:00 PM.

        .PARAMETER BackupThreshold
            Is the length of time, in minutes, after the last backup before a threshold alert error is raised.
            The default is 60.

        .PARAMETER CompressBackup
            Do the backups need to be compressed. By default the backups are not compressed.

        .PARAMETER CopyDestinationFolder
            The path to copy the transaction log backup files to. This is the root directory.
            A directory with the name of the database will be created in this path.

        .PARAMETER CopyJob
            Name of the copy job that will be created in the SQL Server agent.
            The parameter works as a prefix where the name of the database will be added to the copy job name.
            The default is "LSBackup_[databasename]"

        .PARAMETER CopyRetention
            The copy retention period in minutes. Default is 4320 / 72 hours

        .PARAMETER CopySchedule
            Name of the backup schedule created for the copy job.
            The parameter works as a prefix where the name of the database will be added to the copy job schedule name.
            Default is "LSCopy_[DestinationServerName]_[DatabaseName]"

        .PARAMETER CopyScheduleDisabled
            Parameter to set the copy schedule to disabled upon creation.
            By default the schedule is enabled.

        .PARAMETER CopyScheduleFrequencyType
            A value indicating when a job is to be executed.
            Allowed values are "Daily", "AgentStart", "IdleComputer"

        .PARAMETER CopyScheduleFrequencyInterval
            The number of type periods to occur between each execution of the copy job.

        .PARAMETER CopyScheduleFrequencySubdayType
            Specifies the units for the subday FrequencyInterval.
            Allowed values are "Time", "Seconds", "Minutes", "Hours"

        .PARAMETER CopyScheduleFrequencySubdayInterval
            The number of subday type periods to occur between each execution of the copy job.

        .PARAMETER CopyScheduleFrequencyRelativeInterval
            A job's occurrence of FrequencyInterval in each month, if FrequencyInterval is 32 (monthlyrelative).

        .PARAMETER CopyScheduleFrequencyRecurrenceFactor
            The number of weeks or months between the scheduled execution of a job. FrequencyRecurrenceFactor is used only if FrequencyType is 8, "Weekly", 16, "Monthly", 32 or "MonthlyRelative".

        .PARAMETER CopyScheduleStartDate
            The date on which execution of a job can begin.

        .PARAMETER CopyScheduleEndDate
            The date on which execution of a job can stop.

        .PARAMETER CopyScheduleStartTime
            The time on any day to begin execution of a job. Format HHMMSS / 24 hour clock.
            Example: '010000' for 01:00:00 AM.
            Example: '140000' for 02:00:00 PM.

        .PARAMETER CopyScheduleEndTime
            The time on any day to end execution of a job. Format HHMMSS / 24 hour clock.
            Example: '010000' for 01:00:00 AM.
            Example: '140000' for 02:00:00 PM.

        .PARAMETER DisconnectUsers
            If this parameter is set in combinations of standby the users will be disconnected during restore.

        .PARAMETER FullBackupPath
            Path to an existing full backup. Use this when an existing backup needs to used to initialize the database on the secondary instance.

        .PARAMETER GenerateFullBackup
            If the database is not initialized on the secondary instance it can be done by creating a new full backup and
            restore it for you.

        .PARAMETER HistoryRetention
            Is the length of time in minutes in which the history is retained.
            The default value is 14420

        .PARAMETER NoRecovery
            If this parameter is set the database will be in recovery mode. The database will not be readable.
            This setting is default.

        .PARAMETER NoInitialization
            If this parameter is set the secondary database will not be initialized.
            The database needs to be on the secondary instance in recovery mode.

        .PARAMETER PrimaryMonitorServer
            Is the name of the monitor server for the primary server.
            The default is the name of the primary sql server.

        .PARAMETER PrimaryMonitorCredential
            Allows you to login to enter a secure credential. Only needs to be used when the PrimaryMonitorServerSecurityMode is 0 or "sqlserver"
            To use: $scred = Get-Credential, then pass $scred object to the -PrimaryMonitorCredential parameter.

        .PARAMETER PrimaryMonitorServerSecurityMode
            The security mode used to connect to the monitor server for the primary server. Allowed values are 0, "sqlserver", 1, "windows"
            The default is 1 or Windows.

        .PARAMETER PrimaryThresholdAlertEnabled
            Enables the Threshold alert for the primary database

        .PARAMETER RestoreDataFolder
            Folder to be used to restore the database data files. Only used when parameter GenerateFullBackup or UseExistingFullBackup are set.
            If the parameter is not set the default data folder of the secondary instance will be used including the name of the database.
            If the folder is set but doesn't exist the default data folder of the secondary instance will be used including the name of the database.

        .PARAMETER RestoreLogFolder
            Folder to be used to restore the database log files. Only used when parameter GenerateFullBackup or UseExistingFullBackup are set.
            If the parameter is not set the default transaction log folder of the secondary instance will be used.
            If the folder is set but doesn't exist the default transaction log folder of the secondary instance will be used.

        .PARAMETER RestoreDelay
            In case a delay needs to be set for the restore.
            The default is 0.

        .PARAMETER RestoreAlertThreshold
            The amount of minutes after which an alert will be raised is no restore has taken place.
            The default is 45 minutes.

        .PARAMETER RestoreJob
            Name of the restore job that will be created in the SQL Server agent.
            The parameter works as a prefix where the name of the database will be added to the restore job name.
            The default is "LSRestore_[databasename]"

        .PARAMETER RestoreRetention
            The backup retention period in minutes. Default is 4320 / 72 hours

        .PARAMETER RestoreSchedule
            Name of the backup schedule created for the restore job.
            The parameter works as a prefix where the name of the database will be added to the restore job schedule name.
            Default is "LSRestore_[DestinationServerName]_[DatabaseName]"

        .PARAMETER RestoreScheduleDisabled
            Parameter to set the restore schedule to disabled upon creation.
            By default the schedule is enabled.

        .PARAMETER RestoreScheduleFrequencyType
            A value indicating when a job is to be executed.
            Allowed values are "Daily", "AgentStart", "IdleComputer"

        .PARAMETER RestoreScheduleFrequencyInterval
            The number of type periods to occur between each execution of the restore job.

        .PARAMETER RestoreScheduleFrequencySubdayType
            Specifies the units for the subday FrequencyInterval.
            Allowed values are "Time", "Seconds", "Minutes", "Hours"

        .PARAMETER RestoreScheduleFrequencySubdayInterval
            The number of subday type periods to occur between each execution of the restore job.

        .PARAMETER RestoreScheduleFrequencyRelativeInterval
            A job's occurrence of FrequencyInterval in each month, if FrequencyInterval is 32 (monthlyrelative).

        .PARAMETER RestoreScheduleFrequencyRecurrenceFactor
            The number of weeks or months between the scheduled execution of a job. FrequencyRecurrenceFactor is used only if FrequencyType is 8, "Weekly", 16, "Monthly", 32 or "MonthlyRelative".

        .PARAMETER RestoreScheduleStartDate
            The date on which execution of a job can begin.

        .PARAMETER RestoreScheduleEndDate
            The date on which execution of a job can stop.

        .PARAMETER RestoreScheduleStartTime
            The time on any day to begin execution of a job. Format HHMMSS / 24 hour clock.
            Example: '010000' for 01:00:00 AM.
            Example: '140000' for 02:00:00 PM.

        .PARAMETER RestoreScheduleEndTime
            The time on any day to end execution of a job. Format HHMMSS / 24 hour clock.
            Example: '010000' for 01:00:00 AM.
            Example: '140000' for 02:00:00 PM.

        .PARAMETER RestoreThreshold
            The number of minutes allowed to elapse between restore operations before an alert is generated.
            The default value = 0

        .PARAMETER SecondaryDatabasePrefix
            The secondary database can be renamed to include a prefix.

        .PARAMETER SecondaryDatabaseSuffix
            The secondary database can be renamed to include a suffix.

        .PARAMETER SecondaryMonitorServer
            Is the name of the monitor server for the secondary server.
            The default is the name of the secondary sql server.

        .PARAMETER SecondaryMonitorCredential
            Allows you to login to enter a secure credential. Only needs to be used when the SecondaryMonitorServerSecurityMode is 0 or "sqlserver"
            To use: $scred = Get-Credential, then pass $scred object to the -SecondaryMonitorCredential parameter.

        .PARAMETER SecondaryMonitorServerSecurityMode
            The security mode used to connect to the monitor server for the secondary server. Allowed values are 0, "sqlserver", 1, "windows"
            The default is 1 or Windows.

        .PARAMETER SecondaryThresholdAlertEnabled
            Enables the Threshold alert for the secondary database

        .PARAMETER Standby
            If this parameter is set the database will be set to standby mode making the database readable.
            If not set the database will be in recovery mode.

        .PARAMETER StandbyDirectory
            Directory to place the standby file(s) in

        .PARAMETER UseExistingFullBackup
            If the database is not initialized on the secondary instance it can be done by selecting an existing full backup
            and restore it for you.

        .PARAMETER UseBackupFolder
            This enables the user to specify a specific backup folder containing one or more backup files to initialize the database on the secondary instance.

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            Use this switch to disable any kind of verbose messages

        .PARAMETER Force
            The force parameter will ignore some errors in the parameters and assume defaults.
            It will also remove the any present schedules with the same name for the specific job.

        .NOTES
            Tags: LogShipping
            Author: Sander Stad (@sqlstad, sqlstad.nl)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Invoke-DbaLogShipping

        .EXAMPLE
            $params = @{
                SourceSqlInstance = 'sql1'
                DestinationSqlInstance = 'sql2'
                Database = 'db1'
                BackupNetworkPath= '\\sql1\logshipping'
                BackupLocalPath= 'D:\Data\logshipping'
                BackupScheduleFrequencyType = 'daily'
                BackupScheduleFrequencyInterval = 1
                CompressBackup = $true
                CopyScheduleFrequencyType = 'daily'
                CopyScheduleFrequencyInterval = 1
                GenerateFullBackup = $true
                RestoreScheduleFrequencyType = 'daily'
                RestoreScheduleFrequencyInterval = 1
                SecondaryDatabaseSuffix = 'DR'
                CopyDestinationFolder = '\\sql2\logshippingdest'
                Force = $true
            }

            Invoke-DbaLogShipping @params

            Sets up log shipping for database "db1" with the backup path to a network share allowing local backups.
            It creates daily schedules for the backup, copy and restore job with all the defaults to be executed every 15 minutes daily.
            The secondary database will be called "db1_LS".

        .EXAMPLE
            $params = @{
                SourceSqlInstance = 'sql1'
                DestinationSqlInstance = 'sql2'
                Database = 'db1'
                BackupNetworkPath= '\\sql1\logshipping'
                GenerateFullBackup = $true
                Force = $true
            }

            Invoke-DbaLogShipping @params

            Sets up log shipping with all defaults except that a backup file is generated.
            The script will show a message that the copy destination has not been supplied and asks if you want to use the default which would be the backup directory of the secondary server with the folder "logshipping" i.e. "D:\SQLBackup\Logshiping".
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]

    param(
        [parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [Alias("SourceServerInstance", "SourceSqlServerSqlServer", "Source")]
        [object]$SourceSqlInstance,

        [parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [Alias("DestinationServerInstance", "DestinationSqlServer", "Destination")]
        [object]$DestinationSqlInstance,

        [Parameter(Mandatory = $false)]
        [System.Management.Automation.PSCredential]
        $SourceSqlCredential,

        [Parameter(Mandatory = $false)]
        [System.Management.Automation.PSCredential]
        $SourceCredential,

        [Parameter(Mandatory = $false)]
        [System.Management.Automation.PSCredential]
        $DestinationSqlCredential,

        [Parameter(Mandatory = $false)]
        [System.Management.Automation.PSCredential]
        $DestinationCredential,

        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [object[]]$Database,

        [parameter(Mandatory = $true)]
        [string]$BackupNetworkPath,

        [parameter(Mandatory = $false)]
        [string]$BackupLocalPath,

        [parameter(Mandatory = $false)]
        [string]$BackupJob,

        [parameter(Mandatory = $false)]
        [int]$BackupRetention,

        [parameter(Mandatory = $false)]
        [string]$BackupSchedule,

        [parameter(Mandatory = $false)]
        [switch]$BackupScheduleDisabled,

        [parameter(Mandatory = $false)]
        [ValidateSet("Daily", "Weekly", "AgentStart", "IdleComputer")]
        [object]$BackupScheduleFrequencyType,

        [parameter(Mandatory = $false)]
        [object[]]$BackupScheduleFrequencyInterval,

        [parameter(Mandatory = $false)]
        [ValidateSet('Time', 'Seconds', 'Minutes', 'Hours')]
        [object]$BackupScheduleFrequencySubdayType,

        [parameter(Mandatory = $false)]
        [int]$BackupScheduleFrequencySubdayInterval,

        [Parameter(Mandatory = $false)]
        [ValidateSet('Unused', 'First', 'Second', 'Third', 'Fourth', 'Last')]
        [object]$BackupScheduleFrequencyRelativeInterval,

        [Parameter(Mandatory = $false)]
        [int]$BackupScheduleFrequencyRecurrenceFactor,

        [parameter(Mandatory = $false)]
        [string]$BackupScheduleStartDate,

        [parameter(Mandatory = $false)]
        [string]$BackupScheduleEndDate,

        [parameter(Mandatory = $false)]
        [string]$BackupScheduleStartTime,

        [parameter(Mandatory = $false)]
        [string]$BackupScheduleEndTime,

        [parameter(Mandatory = $false)]
        [int]$BackupThreshold,

        [parameter(Mandatory = $false)]
        [switch]$CompressBackup,

        [parameter(Mandatory = $false)]
        [string]$CopyDestinationFolder,

        [parameter(Mandatory = $false)]
        [string]$CopyJob,

        [parameter(Mandatory = $false)]
        [int]$CopyRetention,

        [parameter(Mandatory = $false)]
        [string]$CopySchedule,

        [parameter(Mandatory = $false)]
        [switch]$CopyScheduleDisabled,

        [parameter(Mandatory = $false)]
        [ValidateSet("Daily", "Weekly", "AgentStart", "IdleComputer")]
        [object]$CopyScheduleFrequencyType,

        [parameter(Mandatory = $false)]
        [object[]]$CopyScheduleFrequencyInterval,

        [parameter(Mandatory = $false)]
        [ValidateSet('Time', 'Seconds', 'Minutes', 'Hours')]
        [object]$CopyScheduleFrequencySubdayType,

        [parameter(Mandatory = $false)]
        [int]$CopyScheduleFrequencySubdayInterval,

        [Parameter(Mandatory = $false)]
        [ValidateSet('Unused', 'First', 'Second', 'Third', 'Fourth', 'Last')]
        [object]$CopyScheduleFrequencyRelativeInterval,

        [Parameter(Mandatory = $false)]
        [int]$CopyScheduleFrequencyRecurrenceFactor,

        [parameter(Mandatory = $false)]
        [string]$CopyScheduleStartDate,

        [parameter(Mandatory = $false)]
        [string]$CopyScheduleEndDate,

        [parameter(Mandatory = $false)]
        [string]$CopyScheduleStartTime,

        [parameter(Mandatory = $false)]
        [string]$CopyScheduleEndTime,

        [parameter(Mandatory = $false)]
        [switch]$DisconnectUsers,

        [parameter(Mandatory = $false)]
        [string]$FullBackupPath,

        [parameter(Mandatory = $false)]
        [switch]$GenerateFullBackup,

        [parameter(Mandatory = $false)]
        [int]$HistoryRetention,

        [parameter(Mandatory = $false)]
        [switch]$NoRecovery,

        [parameter(Mandatory = $false)]
        [switch]$NoInitialization,

        [Parameter(Mandatory = $false)]
        [string]$PrimaryMonitorServer,

        [Parameter(Mandatory = $false)]
        [System.Management.Automation.PSCredential]
        $PrimaryMonitorCredential,

        [Parameter(Mandatory = $false)]
        [ValidateSet(0, "sqlserver", 1, "windows")]
        [object]$PrimaryMonitorServerSecurityMode,

        [Parameter(Mandatory = $false)]
        [switch]$PrimaryThresholdAlertEnabled,

        [parameter(Mandatory = $false)]
        [string]$RestoreDataFolder,

        [parameter(Mandatory = $false)]
        [string]$RestoreLogFolder,

        [parameter(Mandatory = $false)]
        [int]$RestoreDelay,

        [parameter(Mandatory = $false)]
        [int]$RestoreAlertThreshold,

        [parameter(Mandatory = $false)]
        [string]$RestoreJob,

        [parameter(Mandatory = $false)]
        [int]$RestoreRetention,

        [parameter(Mandatory = $false)]
        [string]$RestoreSchedule,

        [parameter(Mandatory = $false)]
        [switch]$RestoreScheduleDisabled,

        [parameter(Mandatory = $false)]
        [ValidateSet("Daily", "Weekly", "AgentStart", "IdleComputer")]
        [object]$RestoreScheduleFrequencyType,

        [parameter(Mandatory = $false)]
        [object[]]$RestoreScheduleFrequencyInterval,

        [parameter(Mandatory = $false)]
        [ValidateSet('Time', 'Seconds', 'Minutes', 'Hours')]
        [object]$RestoreScheduleFrequencySubdayType,

        [parameter(Mandatory = $false)]
        [int]$RestoreScheduleFrequencySubdayInterval,

        [Parameter(Mandatory = $false)]
        [ValidateSet('Unused', 'First', 'Second', 'Third', 'Fourth', 'Last')]
        [object]$RestoreScheduleFrequencyRelativeInterval,

        [Parameter(Mandatory = $false)]
        [int]$RestoreScheduleFrequencyRecurrenceFactor,

        [parameter(Mandatory = $false)]
        [string]$RestoreScheduleStartDate,

        [parameter(Mandatory = $false)]
        [string]$RestoreScheduleEndDate,

        [parameter(Mandatory = $false)]
        [string]$RestoreScheduleStartTime,

        [parameter(Mandatory = $false)]
        [string]$RestoreScheduleEndTime,

        [parameter(Mandatory = $false)]
        [int]$RestoreThreshold,

        [parameter(Mandatory = $false)]
        [string]$SecondaryDatabasePrefix,

        [parameter(Mandatory = $false)]
        [string]$SecondaryDatabaseSuffix,

        [Parameter(Mandatory = $false)]
        [string]$SecondaryMonitorServer,

        [Parameter(Mandatory = $false)]
        [System.Management.Automation.PSCredential]
        $SecondaryMonitorCredential,

        [Parameter(Mandatory = $false)]
        [ValidateSet(0, "sqlserver", 1, "windows")]
        [object]$SecondaryMonitorServerSecurityMode,

        [Parameter(Mandatory = $false)]
        [switch]$SecondaryThresholdAlertEnabled,

        [parameter(Mandatory = $false)]
        [switch]$Standby,

        [parameter(Mandatory = $false)]
        [string]$StandbyDirectory,

        [parameter(Mandatory = $false)]
        [switch]$UseExistingFullBackup,

        [parameter(Mandatory = $false)]
        [string]$UseBackupFolder,

        [switch]$Force,

        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Write-Message -Message "Started log shipping for $SourceSqlInstance to $DestinationSqlInstance" -Level Output

        # Try connecting to the instance
        Write-Message -Message "Connecting to source Sql Server $SourceSqlInstance.." -Level Output
        try {
            $SourceServer = Connect-SqlInstance -SqlInstance $SourceSqlInstance -SqlCredential $SourceSqlCredential
        }
        catch {
            Stop-Function -Message "Could not connect to Sql Server instance $SourceSqlInstance" -ErrorRecord $_ -Target $SourceSqlInstance
            return
        }

        # Try connecting to the instance
        Write-Message -Message "Connecting to destination Sql Server $DestinationSqlInstance.." -Level Output
        try {
            $DestinationServer = Connect-SqlInstance -SqlInstance $DestinationSqlInstance -SqlCredential $DestinationSqlCredential
        }
        catch {
            Stop-Function -Message "Could not connect to Sql Server instance $DestinationSqlInstance" -ErrorRecord $_ -Target $DestinationSqlInstance
            return
        }

        # Check the instance if it is a named instance
        $SourceServerName, $SourceInstanceName = $SourceSqlInstance.Split("\")
        $DestinationServerName, $DestinationInstanceName = $DestinationSqlInstance.Split("\")

        if ($SourceInstanceName -eq $null) {
            $SourceInstanceName = "MSSQLSERVER"
        }

        if ($DestinationInstanceName -eq $null) {
            $DestinationInstanceName = "MSSQLSERVER"
        }

        $IsSourceLocal = $false
        $IsDestinationLocal = $false

        # Check if it's local or remote
        if ($SourceServerName -in ".", "localhost", $env:ServerName, "127.0.0.1") {
            $IsSourceLocal = $true
        }
        if ($DestinationServerName -in ".", "localhost", $env:ServerName, "127.0.0.1") {
            $IsDestinationLocal = $true
        }

        # Set up regex strings for several checks
        $RegexDate = '(?<!\d)(?:(?:(?:1[6-9]|[2-9]\d)?\d{2})(?:(?:(?:0[13578]|1[02])31)|(?:(?:0[1,3-9]|1[0-2])(?:29|30)))|(?:(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))0229)|(?:(?:1[6-9]|[2-9]\d)?\d{2})(?:(?:0?[1-9])|(?:1[0-2]))(?:0?[1-9]|1\d|2[0-8]))(?!\d)'
        $RegexTime = '^(?:(?:([01]?\d|2[0-3]))?([0-5]?\d))?([0-5]?\d)$'
        $RegexUnc = '^\\(?:\\[^<>:`"/\\|?*]+)+$'

        # Check the instance names and the database settings
        if (($SourceSqlInstance -eq $DestinationSqlInstance) -and (-not $SecondaryDatabasePrefix -or $SecondaryDatabaseSuffix)) {
            Stop-Function -Message "The destination database is the same as the source`nPlease enter a prefix or suffix using -SecondaryDatabasePrefix or -SecondaryDatabaseSuffix." -Target $SourceSqlInstance
            return
        }

        # Check the connection timeout
        if ($SourceServer.ConnectionContext.StatementTimeout -ne 0) {
            $SourceServer.ConnectionContext.StatementTimeout = 0
            Write-Message -Message "Connection timeout of $SourceServer is set to 0" -Level Verbose
        }

        if ($DestinationServer.ConnectionContext.StatementTimeout -ne 0) {
            $DestinationServer.ConnectionContext.StatementTimeout = 0
            Write-Message -Message "Connection timeout of $DestinationServer is set to 0" -Level Verbose
        }

        # Check the backup network path
        Write-Message -Message "Testing backup network path $BackupNetworkPath" -Level Verbose
        if ((Test-DbaSqlPath -Path $BackupNetworkPath -SqlInstance $SourceSqlInstance -SqlCredential $SourceCredential) -ne $true) {
            Stop-Function -Message "Backup network path $BackupNetworkPath is not valid or can't be reached." -Target $SourceSqlInstance
            return
        }
        elseif ($BackupNetworkPath -notmatch $RegexUnc) {
            Stop-Function -Message "Backup network path $BackupNetworkPath has to be in the form of \\server\share." -Target $SourceSqlInstance
            return
        }

        # Check the copy destination
        if (-not $CopyDestinationFolder) {
            # Make a default copy destination by retrieving the backup folder and adding a directory
            $CopyDestinationFolder = "$($DestinationServer.Settings.BackupDirectory)\Logshipping"

            # Check to see if the path already exists
            Write-Message -Message "Testing copy destination path $CopyDestinationFolder" -Level Verbose
            if (Test-DbaSqlPath -Path $CopyDestinationFolder -SqlInstance $DestinationSqlInstance -SqlCredential $DestinationCredential) {
                Write-Message -Message "Copy destination $CopyDestinationFolder already exists" -Level Verbose
            }
            else {
                # Check if force is being used
                if (-not $Force) {
                    # Set up the confirm part
                    $message = "The copy destination is missing. Do you want to use the default $($CopyDestinationFolder)?"
                    $choiceYes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Answer Yes."
                    $choiceNo = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Answer No."
                    $options = [System.Management.Automation.Host.ChoiceDescription[]]($choiceYes, $choiceNo)
                    $result = $host.ui.PromptForChoice($title, $message, $options, 0)

                    # Check the result from the confirm
                    switch ($result) {
                        # If yes
                        0 {
                            # Try to create the new directory
                            try {
                                # If the destination server is remote and the credential is set
                                if (-not $IsDestinationLocal -and $DestinationCredential) {
                                    Invoke-Command2 -ComputerName $DestinationServerName -Credential $DestinationCredential -ScriptBlock {
                                        Write-Message -Message "Creating copy destination folder $CopyDestinationFolder" -Level Verbose
                                        New-Item -Path $CopyDestinationFolder -ItemType Directory -Credential $DestinationCredential -Force:$Force | Out-Null
                                    }
                                }
                                # If the server is local and the credential is set
                                elseif ($DestinationCredential) {
                                    Invoke-Command2 -Credential $DestinationCredential -ScriptBlock {
                                        Write-Message -Message "Creating copy destination folder $CopyDestinationFolder" -Level Verbose
                                        New-Item -Path $CopyDestinationFolder -ItemType Directory -Credential $DestinationCredential -Force:$Force | Out-Null
                                    }
                                }
                                # If the server is local and the credential is not set
                                else {
                                    Write-Message -Message "Creating copy destination folder $CopyDestinationFolder" -Level Verbose
                                    New-Item -Path $CopyDestinationFolder -Force:$Force -ItemType Directory | Out-Null
                                }
                                Write-Message -Message "Copy destination $CopyDestinationFolder created." -Level Verbose
                            }
                            catch {
                                Stop-Function -Message "Something went wrong creating the copy destination folder $CopyDestinationFolder. `n$_" -Target $DestinationSqlInstance -ErrorRecord $_
                                return
                            }
                        }
                        1 {
                            Stop-Function -Message "Copy destination is a mandatory parameter. Please make sure the value is entered." -Target $DestinationSqlInstance
                            return
                        }
                    } # switch
                } # if not force
                else {
                    # Try to create the copy destination on the local server
                    try {
                        Write-Message -Message "Creating copy destination folder $CopyDestinationFolder" -Level Verbose
                        New-Item $CopyDestinationFolder -ItemType Directory -Credential $DestinationCredential -Force:$Force | Out-Null
                        Write-Message -Message "Copy destination $CopyDestinationFolder created." -Level Verbose
                    }
                    catch {
                        Stop-Function -Message "Something went wrong creating the copy destination folder $CopyDestinationFolder. `n$_" -Target $DestinationSqlInstance -ErrorRecord $_
                        return
                    }
                } # else not force
            } # if test path copy destination
        } # if not copy destination

        Write-Message -Message "Testing copy destination path $CopyDestinationFolder" -Level Verbose
        if ((Test-DbaSqlPath -Path $CopyDestinationFolder -SqlInstance $DestinationSqlInstance -SqlCredential $DestinationCredential) -ne $true) {
            Stop-Function -Message "Copy destination folder $CopyDestinationFolder is not valid or can't be reached." -Target $DestinationSqlInstance
            return
        }
        elseif ($CopyDestinationFolder.StartsWith("\\") -and $CopyDestinationFolder -notmatch $RegexUnc) {
            Stop-Function -Message "Copy destination folder $CopyDestinationFolder has to be in the form of \\server\share." -Target $DestinationSqlInstance
            return
        }

        # Check the backup compression
        if ($SourceServer.Version.Major -gt 9) {
            if ($CompressBackup) {
                Write-Message -Message "Setting backup compression to 1." -Level Verbose
                [bool]$BackupCompression = 1
            }
            else {
                $backupServerSetting = (Get-DbaSpConfigure -SqlInstance $SourceSqlInstance -ConfigName DefaultBackupCompression).ConfiguredValue
                Write-Message -Message "Setting backup compression to default server setting $backupServerSetting." -Level Verbose
                [bool]$BackupCompression = $backupServerSetting
            }
        }
        else {
            Write-Message -Message "Source server $SourceServer does not support backup compression" -Level Verbose
        }

        # Check the database parameter
        if ($Database) {
            foreach ($db in $Database) {
                if ($db -notin $SourceServer.Databases.Name) {
                    Stop-Function -Message "Database $db cannot be found on instance $SourceSqlInstance" -Target $SourceSqlInstance
                }

                $DatabaseCollection = $SourceServer.Databases | Where-Object { $_.Name -in $Database }
            }
        }
        else {
            Stop-Function -Message "Please supply a database to set up log shipping for" -Target $SourceSqlInstance -Continue
        }

        # Set the database mode
        if ($Standby) {
            $DatabaseStatus = 1
            Write-Message -Message "Destination database status set to STANDBY" -Level Verbose
        }
        else {
            $DatabaseStatus = 0
            Write-Message -Message "Destination database status set to NO RECOVERY" -Level Verbose
        }

        # Setting defaults
        if (-not $BackupRetention) {
            $BackupRetention = 4320
            Write-Message -Message "Backup retention set to $BackupRetention" -Level Verbose
        }
        if (-not $BackupThreshold) {
            $BackupThreshold = 60
            Write-Message -Message "Backup Threshold set to $BackupThreshold" -Level Verbose
        }
        if (-not $CopyRetention) {
            $CopyRetention = 4320
            Write-Message -Message "Copy retention set to $CopyRetention" -Level Verbose
        }
        if (-not $HistoryRetention) {
            $HistoryRetention = 14420
            Write-Message -Message "History retention set to $HistoryRetention" -Level Verbose
        }
        if (-not $RestoreAlertThreshold) {
            $RestoreAlertThreshold = 45
            Write-Message -Message "Restore alert Threshold set to $RestoreAlertThreshold" -Level Verbose
        }
        if (-not $RestoreDelay) {
            $RestoreDelay = 0
            Write-Message -Message "Restore delay set to $RestoreDelay" -Level Verbose
        }
        if (-not $RestoreRetention) {
            $RestoreRetention = 4320
            Write-Message -Message "Restore retention set to $RestoreRetention" -Level Verbose
        }
        if (-not $RestoreThreshold) {
            $RestoreThreshold = 0
            Write-Message -Message "Restore Threshold set to $RestoreThreshold" -Level Verbose
        }
        if (-not $PrimaryMonitorServerSecurityMode) {
            $PrimaryMonitorServerSecurityMode = 1
            Write-Message -Message "Primary monitor server security mode set to $PrimaryMonitorServerSecurityMode" -Level Verbose
        }
        if (-not $SecondaryMonitorServerSecurityMode) {
            $SecondaryMonitorServerSecurityMode = 1
            Write-Message -Message "Secondary monitor server security mode set to $SecondaryMonitorServerSecurityMode" -Level Verbose
        }
        if (-not $BackupScheduleFrequencyType) {
            $BackupScheduleFrequencyType = "Daily"
            Write-Message -Message "Backup frequency type set to $BackupScheduleFrequencyType" -Level Verbose
        }
        if (-not $BackupScheduleFrequencyInterval) {
            $BackupScheduleFrequencyInterval = "EveryDay"
            Write-Message -Message "Backup frequency interval set to $BackupScheduleFrequencyInterval" -Level Verbose
        }
        if (-not $BackupScheduleFrequencySubdayType) {
            $BackupScheduleFrequencySubdayType = "Minutes"
            Write-Message -Message "Backup frequency subday type set to $BackupScheduleFrequencySubdayType" -Level Verbose
        }
        if (-not $BackupScheduleFrequencySubdayInterval) {
            $BackupScheduleFrequencySubdayInterval = 15
            Write-Message -Message "Backup frequency subday interval set to $BackupScheduleFrequencySubdayInterval" -Level Verbose
        }
        if (-not $BackupScheduleFrequencyRelativeInterval) {
            $BackupScheduleFrequencyRelativeInterval = "Unused"
            Write-Message -Message "Backup frequency relative interval set to $BackupScheduleFrequencyRelativeInterval" -Level Verbose
        }
        if (-not $BackupScheduleFrequencyRecurrenceFactor) {
            $BackupScheduleFrequencyRecurrenceFactor = 0
            Write-Message -Message "Backup frequency recurrence factor set to $BackupScheduleFrequencyRecurrenceFactor" -Level Verbose
        }
        if (-not $CopyScheduleFrequencyType) {
            $CopyScheduleFrequencyType = "Daily"
            Write-Message -Message "Copy frequency type set to $CopyScheduleFrequencyType" -Level Verbose
        }
        if (-not $CopyScheduleFrequencyInterval) {
            $CopyScheduleFrequencyInterval = "EveryDay"
            Write-Message -Message "Copy frequency interval set to $CopyScheduleFrequencyInterval" -Level Verbose
        }
        if (-not $CopyScheduleFrequencySubdayType) {
            $CopyScheduleFrequencySubdayType = "Minutes"
            Write-Message -Message "Copy frequency subday type set to $CopyScheduleFrequencySubdayType" -Level Verbose
        }
        if (-not $CopyScheduleFrequencySubdayInterval) {
            $CopyScheduleFrequencySubdayInterval = 15
            Write-Message -Message "Copy frequency subday interval set to $CopyScheduleFrequencySubdayInterval" -Level Verbose
        }
        if (-not $CopyScheduleFrequencyRelativeInterval) {
            $CopyScheduleFrequencyRelativeInterval = "Unused"
            Write-Message -Message "Copy frequency relative interval set to $CopyScheduleFrequencyRelativeInterval" -Level Verbose
        }
        if (-not $CopyScheduleFrequencyRecurrenceFactor) {
            $CopyScheduleFrequencyRecurrenceFactor = 0
            Write-Message -Message "Copy frequency recurrence factor set to $CopyScheduleFrequencyRecurrenceFactor" -Level Verbose
        }
        if (-not $RestoreScheduleFrequencyType) {
            $RestoreScheduleFrequencyType = "Daily"
            Write-Message -Message "Restore frequency type set to $RestoreScheduleFrequencyType" -Level Verbose
        }
        if (-not $RestoreScheduleFrequencyInterval) {
            $RestoreScheduleFrequencyInterval = "EveryDay"
            Write-Message -Message "Restore frequency interval set to $RestoreScheduleFrequencyInterval" -Level Verbose
        }
        if (-not $RestoreScheduleFrequencySubdayType) {
            $RestoreScheduleFrequencySubdayType = "Minutes"
            Write-Message -Message "Restore frequency subday type set to $RestoreScheduleFrequencySubdayType" -Level Verbose
        }
        if (-not $RestoreScheduleFrequencySubdayInterval) {
            $RestoreScheduleFrequencySubdayInterval = 15
            Write-Message -Message "Restore frequency subday interval set to $RestoreScheduleFrequencySubdayInterval" -Level Verbose
        }
        if (-not $RestoreScheduleFrequencyRelativeInterval) {
            $RestoreScheduleFrequencyRelativeInterval = "Unused"
            Write-Message -Message "Restore frequency relative interval set to $RestoreScheduleFrequencyRelativeInterval" -Level Verbose
        }
        if (-not $RestoreScheduleFrequencyRecurrenceFactor) {
            $RestoreScheduleFrequencyRecurrenceFactor = 0
            Write-Message -Message "Restore frequency recurrence factor set to $RestoreScheduleFrequencyRecurrenceFactor" -Level Verbose
        }
        if (-not ($SecondaryDatabasePrefix -or $SecondaryDatabaseSuffix) -and ($SourceServer.Name -eq $DestinationServer.Name) -and ($SourceServer.InstanceName -eq $DestinationServer.InstanceName)) {
            if ($Force) {
                $SecondaryDatabaseSuffix = "_LS"
            }
            else {
                Stop-Function -Message "Destination database is the same as source database.`nPlease check the secondary server, database prefix or suffix or use -Force to set the secondary databse using a suffix." -Target $SourceSqlInstance
                return
            }
        }

        # Checking for contradicting variables
        if ($NoInitialization -and ($GenerateFullBackup -or $UseExistingFullBackup)) {
            Stop-Function -Message "Cannot use -NoInitialization with -GenerateFullBackup or -UseExistingFullBackup" -Target $DestinationSqlInstance
            return
        }

        if ($UseBackupFolder -and ($GenerateFullBackup -or $NoInitialization -or $UseExistingFullBackup)) {
            Stop-Function -Message "Cannot use -UseBackupFolder with -GenerateFullBackup, -NoInitialization or -UseExistingFullBackup" -Target $DestinationSqlInstance
            return
        }

        # Check the subday interval
        if (($BackupScheduleFrequencySubdayType -in 2, "Seconds", 4, "Minutes") -and (-not ($BackupScheduleFrequencySubdayInterval -ge 1 -or $BackupScheduleFrequencySubdayInterval -le 59))) {
            Stop-Function -Message "Backup subday interval $BackupScheduleFrequencySubdayInterval must be between 1 and 59 when subday type is 2, 'Seconds', 4 or 'Minutes'" -Target $SourceSqlInstance
            return
        }
        elseif (($BackupScheduleFrequencySubdayType -in 8, "Hours") -and (-not ($BackupScheduleFrequencySubdayInterval -ge 1 -and $BackupScheduleFrequencySubdayInterval -le 23))) {
            Stop-Function -Message "Backup Subday interval $BackupScheduleFrequencySubdayInterval must be between 1 and 23 when subday type is 8 or 'Hours" -Target $SourceSqlInstance
            return
        }

        # Check the subday interval
        if (($CopyScheduleFrequencySubdayType -in 2, "Seconds", 4, "Minutes") -and (-not ($CopyScheduleFrequencySubdayInterval -ge 1 -or $CopyScheduleFrequencySubdayInterval -le 59))) {
            Stop-Function -Message "Copy subday interval $CopyScheduleFrequencySubdayInterval must be between 1 and 59 when subday type is 2, 'Seconds', 4 or 'Minutes'" -Target $DestinationSqlInstance
            return
        }
        elseif (($CopyScheduleFrequencySubdayType -in 8, "Hours") -and (-not ($CopyScheduleFrequencySubdayInterval -ge 1 -and $CopyScheduleFrequencySubdayInterval -le 23))) {
            Stop-Function -Message "Copy subday interval $CopyScheduleFrequencySubdayInterval must be between 1 and 23 when subday type is 8 or 'Hours'" -Target $DestinationSqlInstance
            return
        }

        # Check the subday interval
        if (($RestoreScheduleFrequencySubdayType -in 2, "Seconds", 4, "Minutes") -and (-not ($RestoreScheduleFrequencySubdayInterval -ge 1 -or $RestoreScheduleFrequencySubdayInterval -le 59))) {
            Stop-Function -Message "Restore subday interval $RestoreScheduleFrequencySubdayInterval must be between 1 and 59 when subday type is 2, 'Seconds', 4 or 'Minutes'" -Target $DestinationSqlInstance
            return
        }
        elseif (($RestoreScheduleFrequencySubdayType -in 8, "Hours") -and (-not ($RestoreScheduleFrequencySubdayInterval -ge 1 -and $RestoreScheduleFrequencySubdayInterval -le 23))) {
            Stop-Function -Message "Restore subday interval $RestoreScheduleFrequencySubdayInterval must be between 1 and 23 when subday type is 8 or 'Hours" -Target $DestinationSqlInstance
            return
        }

        # Check the backup start date
        if (-not $BackupScheduleStartDate) {
            $BackupScheduleStartDate = (Get-Date -format "yyyyMMdd")
            Write-Message -Message "Backup start date set to $BackupScheduleStartDate" -Level Verbose
        }
        else {
            if ($BackupScheduleStartDate -notmatch $RegexDate) {
                Stop-Function -Message "Backup start date $BackupScheduleStartDate needs to be a valid date with format yyyyMMdd" -Target $SourceSqlInstance
                return
            }
        }

        # Check the back start time
        if (-not $BackupScheduleStartTime) {
            $BackupScheduleStartTime = '000000'
            Write-Message -Message "Backup start time set to $BackupScheduleStartTime" -Level Verbose
        }
        elseif ($BackupScheduleStartTime -notmatch $RegexTime) {
            Stop-Function -Message  "Backup start time $BackupScheduleStartTime needs to match between '000000' and '235959'" -Target $SourceSqlInstance
            return
        }

        # Check the back end time
        if (-not $BackupScheduleEndTime) {
            $BackupScheduleEndTime = '235959'
            Write-Message -Message "Backup end time set to $BackupScheduleEndTime" -Level Verbose
        }
        elseif ($BackupScheduleStartTime -notmatch $RegexTime) {
            Stop-Function -Message  "Backup end time $BackupScheduleStartTime needs to match between '000000' and '235959'" -Target $SourceSqlInstance
            return
        }

        # Check the backup end date
        if (-not $BackupScheduleEndDate) {
            $BackupScheduleEndDate = '99991231'
        }
        elseif ($BackupScheduleEndDate -notmatch $RegexDate) {
            Stop-Function -Message "Backup end date $BackupScheduleEndDate needs to be a valid date with format yyyyMMdd" -Target $SourceSqlInstance
            return
        }

        # Check the copy start date
        if (-not $CopyScheduleStartDate) {
            $CopyScheduleStartDate = (Get-Date -format "yyyyMMdd")
            Write-Message -Message "Copy start date set to $CopyScheduleStartDate" -Level Verbose
        }
        else {
            if ($CopyScheduleStartDate -notmatch $RegexDate) {
                Stop-Function -Message "Copy start date $CopyScheduleStartDate needs to be a valid date with format yyyyMMdd" -Target $SourceSqlInstance
                return
            }
        }

        # Check the copy end date
        if (-not $CopyScheduleEndDate) {
            $CopyScheduleEndDate = '99991231'
        }
        elseif ($CopyScheduleEndDate -notmatch $RegexDate) {
            Stop-Function -Message "Copy end date $CopyScheduleEndDate needs to be a valid date with format yyyyMMdd" -Target $SourceSqlInstance
            return
        }

        # Check the copy start time
        if (-not $CopyScheduleStartTime) {
            $CopyScheduleStartTime = '000000'
            Write-Message -Message "Copy start time set to $CopyScheduleStartTime" -Level Verbose
        }
        elseif ($CopyScheduleStartTime -notmatch $RegexTime) {
            Stop-Function -Message  "Copy start time $CopyScheduleStartTime needs to match between '000000' and '235959'" -Target $SourceSqlInstance
            return
        }

        # Check the copy end time
        if (-not $CopyScheduleEndTime) {
            $CopyScheduleEndTime = '235959'
            Write-Message -Message "Copy end time set to $CopyScheduleEndTime" -Level Verbose
        }
        elseif ($CopyScheduleEndTime -notmatch $RegexTime) {
            Stop-Function -Message  "Copy end time $CopyScheduleEndTime needs to match between '000000' and '235959'" -Target $SourceSqlInstance
            return
        }

        # Check the restore start date
        if (-not $RestoreScheduleStartDate) {
            $RestoreScheduleStartDate = (Get-Date -format "yyyyMMdd")
            Write-Message -Message "Restore start date set to $RestoreScheduleStartDate" -Level Verbose
        }
        else {
            if ($RestoreScheduleStartDate -notmatch $RegexDate) {
                Stop-Function -Message "Restore start date $RestoreScheduleStartDate needs to be a valid date with format yyyyMMdd" -Target $SourceSqlInstance
                return
            }
        }

        # Check the restore end date
        if (-not $RestoreScheduleEndDate) {
            $RestoreScheduleEndDate = '99991231'
        }
        elseif ($RestoreScheduleEndDate -notmatch $RegexDate) {
            Stop-Function -Message "Restore end date $RestoreScheduleEndDate needs to be a valid date with format yyyyMMdd" -Target $SourceSqlInstance
            return
        }

        # Check the restore start time
        if (-not $RestoreScheduleStartTime) {
            $RestoreScheduleStartTime = '000000'
            Write-Message -Message "Restore start time set to $RestoreScheduleStartTime" -Level Verbose
        }
        elseif ($RestoreScheduleStartTime -notmatch $RegexTime) {
            Stop-Function -Message  "Restore start time $RestoreScheduleStartTime needs to match between '000000' and '235959'" -Target $SourceSqlInstance
            return
        }

        # Check the restore end time
        if (-not $RestoreScheduleEndTime) {
            $RestoreScheduleEndTime = '235959'
            Write-Message -Message "Restore end time set to $RestoreScheduleEndTime" -Level Verbose
        }
        elseif ($RestoreScheduleEndTime -notmatch $RegexTime) {
            Stop-Function -Message  "Restore end time $RestoreScheduleEndTime needs to match between '000000' and '235959'" -Target $SourceSqlInstance
            return
        }

        # Check if standby is being used
        if ($Standby) {

            # Check the stand-by directory
            if ($StandbyDirectory) {
                # Check if the path is reachable for the destination server
                if ((Test-DbaSqlPath -Path $StandbyDirectory -SqlInstance $DestinationSqlInstance -SqlCredential $DestinationCredential) -ne $true) {
                    Stop-Function -Message "The directory $StandbyDirectory cannot be reached by the destination instance. Please check the permission and credentials." -Target $DestinationSqlInstance
                    return
                }
            }
            elseif (-not $StandbyDirectory -and $Force) {
                $StandbyDirectory = $DestinationSqlInstance.BackupDirectory
                Write-Message -Message "Stand-by directory was not set. Setting it to $StandbyDirectory" -Level Verbose
            }
            else {
                Stop-Function -Message "Please set the parameter -StandbyDirectory when using -Standby" -Target $SourceSqlInstance
                return
            }
        }
    } # begin

    process {

        if (Test-FunctionInterrupt) { return }

        # Loop through each of the databases
        foreach ($db in $DatabaseCollection) {

            # Check the status of the database
            if ($db.RecoveryModel -ne 'Full') {
                Stop-Function -Message  "Database $db is not in FULL recovery mode" -Target $SourceSqlInstance -Continue
            }

            # Set the intital destination database
            $SecondaryDatabase = $db.Name

            # Set the database prefix
            if ($SecondaryDatabasePrefix) {
                $SecondaryDatabase = "$SecondaryDatabasePrefix$($db.Name)"
            }

            # Set the database suffix
            if ($SecondaryDatabaseSuffix) {
                $SecondaryDatabase += $SecondaryDatabaseSuffix
            }

            # Check is the database is already initialized an check if the database exists on the secondary instance
            if ($NoInitialization -and ($DestinationServer.Databases.Name -notcontains $SecondaryDatabase)) {
                Stop-Function -Message "Database $SecondaryDatabase needs to be initialized before log shipping setting can continue." -Target $SourceSqlInstance -Continue
            }

            # Check the local backup path
            if ($BackupLocalPath) {
                if ($BackupLocalPath.EndsWith("\")) {
                    $DatabaseBackupLocalPath = "$BackupLocalPath$($db.Name)"
                }
                else {
                    $DatabaseBackupLocalPath = "$BackupLocalPath\$($db.Name)"
                }
            }
            else {
                $BackupLocalPath = $BackupNetworkPath

                if ($BackupLocalPath.EndsWith("\")) {
                    $DatabaseBackupLocalPath = "$BackupLocalPath$($db.Name)"
                }
                else {
                    $DatabaseBackupLocalPath = "$BackupLocalPath\$($db.Name)"
                }
            }
            Write-Message -Message "Backup local path set to $DatabaseBackupLocalPath." -Level Verbose

            # Setting the backup network path for the database
            if ($BackupNetworkPath.EndsWith("\")) {
                $DatabaseBackupNetworkPath = "$BackupNetworkPath$($db.Name)"
            }
            else {
                $DatabaseBackupNetworkPath = "$BackupNetworkPath\$($db.Name)"
            }
            Write-Message -Message "Backup network path set to $DatabaseBackupNetworkPath." -Level Verbose


            # Checking if the database network path exists
            Write-Message -Message "Testing database backup network path $DatabaseBackupNetworkPath" -Level Verbose
            if ((Test-DbaSqlPath -Path $DatabaseBackupNetworkPath -SqlInstance $SourceSqlInstance -SqlCredential $SourceCredential) -ne $true) {
                # To to create the backup directory for the database
                try {
                    Write-Message -Message "Database backup network path $DatabaseBackupNetworkPath not found. Trying to create it.." -Level Verbose

                    Invoke-Command2 -Credential $SourceCredential -ScriptBlock {
                        Write-Message -Message "Creating backup folder $DatabaseBackupNetworkPath" -Level Verbose
                        New-Item -Path $DatabaseBackupNetworkPath -ItemType Directory -Credential $SourceCredential -Force:$Force | Out-Null
                    }
                }
                catch {
                    Stop-Function -Message "Something went wrong creating the directory" -ErrorRecord $_ -Target $SourceSqlInstance -Continue
                }
            }

            # Check if the backup job name is set
            if ($BackupJob) {
                $DatabaseBackupJob = "$BackupJob_$($db.Name)"
            }
            else {
                $DatabaseBackupJob = "LSBackup_$($db.Name)"
            }
            Write-Message -Message "Backup job name set to $DatabaseBackupJob" -Level Verbose

            # Check if the backup job schedule name is set
            if ($BackupSchedule) {
                $DatabaseBackupSchedule = "$BackupSchedule_$($db.Name)"
            }
            else {
                $DatabaseBackupSchedule = "LSBackupSchedule_$($db.Name)"
            }
            Write-Message -Message "Backup job schedule name set to $DatabaseBackupSchedule" -Level Verbose

            # Check if secondary database is present on secondary instance
            if (-not $Force -and -not $NoInitialization -and ($DestinationServer.Databases[$SecondaryDatabase].Status -ne 'Restoring') -and ($DestinationServer.Databases.Name -contains $SecondaryDatabase)) {
                Stop-Function -Message "Secondary database already exists on instance $DestinationSqlInstance." -ErrorRecord $_ -Target $DestinationSqlInstance -Continue
            }

            # Check if the secondary database needs tobe initialized
            if (-not $NoInitialization) {
                # Check if the secondary database exists on the secondary instance
                if ($DestiationServer.Databases.Name -notcontains $SecondaryDatabase) {
                    # Check if force is being used and no option to generate the full backup is set
                    if ($Force -and -not ($GenerateFullBackup -or $UseExistingFullBackup)) {
                        # Set the option to generate a full backup
                        Write-Message -Message "Set option to initialize secondary database with full backup" -Level Verbose
                        $GenerateFullBackup = $true
                    }
                    elseif (-not $Force -and -not $GenerateFullBackup -and -not $UseExistingFullBackup -and -not $UseBackupFolder) {
                        # Set up the confirm part
                        $message = "The database $SecondaryDatabase does not exist on instance $DestinationSqlInstance. `nDo you want to initialize it by generating a full backup?"
                        $choiceYes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Answer Yes."
                        $choiceNo = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Answer No."
                        $options = [System.Management.Automation.Host.ChoiceDescription[]]($choiceYes, $choiceNo)
                        $result = $host.ui.PromptForChoice($title, $message, $options, 0)

                        # Check the result from the confirm
                        switch ($result) {
                            # If yes
                            0 {
                                # Set the option to generate a full backup
                                Write-Message -Message "Set option to initialize secondary database with full backup." -Level Verbose
                                $GenerateFullBackup = $true
                            }
                            1 {
                                Stop-Function -Message "The database is not initialized on the secondary instance. `nPlease initialize the database on the secondary instance, use -GenerateFullbackup or use -Force." -Target $DestinationSqlInstance
                                return
                            }
                        } # switch
                    }
                }
            }


            # Check the parameters for initialization of the secondary database
            if (-not $NoInitialization -and ($GenerateFullBackup -or $UseExistingFullBackup -or $UseBackupFolder)) {
                # Check if the restore data and log folder are set
                if (-not $RestoreDataFolder -or -not $RestoreLogFolder) {
                    Write-Message -Message "Restore data folder or restore log folder are not set. Using server defaults" -Level Verbose

                    # Get the default data folder
                    if (-not $RestoreDataFolder) {
                        $DatabaseRestoreDataFolder = $DestinationServer.DefaultFile
                    }
                    else {
                        # Set the restore data folder
                        if ($RestoreDataFolder.EndsWith("\")) {
                            $DatabaseRestoreDataFolder = "$RestoreDataFolder$($db.Name)"
                        }
                        else {
                            $DatabaseRestoreDataFolder = "$RestoreDataFolder\$($db.Name)"
                        }
                    }

                    Write-Message -Message "Restore data folder set to $DatabaseRestoreDataFolder" -Level Verbose

                    # Get the default log folder
                    if (-not $RestoreLogFolder) {
                        $DatabaseRestoreLogFolder = $DestinationServer.DefaultLog
                    }

                    Write-Message -Message "Restore log folder set to $DatabaseRestoreLogFolder" -Level Verbose

                    # Check if the restore data folder exists
                    Write-Message -Message "Testing database restore data path $DatabaseRestoreDataFolder" -Level Verbose
                    if ((Test-DbaSqlPath  -Path $DatabaseRestoreDataFolder -SqlInstance $DestinationSqlInstance -SqlCredential $DestinationCredential) -ne $true) {
                        if ($PSCmdlet.ShouldProcess($DestinationServerName, "Creating database restore data folder $DatabaseRestoreDataFolder on $DestinationServerName")) {
                            # Try creating the data folder
                            try {
                                Invoke-Command2 -Credential $DestinationCredential -ScriptBlock {
                                    Write-Message -Message "Creating data folder $DatabaseRestoreDataFolder" -Level Verbose
                                    New-Item -Path $DatabaseRestoreDataFolder -ItemType Directory -Credential $DestinationCredential -Force:$Force | Out-Null
                                }
                            }
                            catch {
                                Stop-Function -Message "Something went wrong creating the restore data directory" -ErrorRecord $_ -Target $SourceSqlInstance -Continue
                            }
                        }
                    }

                    # Check if the restore log folder exists
                    Write-Message -Message "Testing database restore log path $DatabaseRestoreLogFolder" -Level Verbose
                    if ((Test-DbaSqlPath  -Path $DatabaseRestoreLogFolder -SqlInstance $DestinationSqlInstance -SqlCredential $DestinationCredential) -ne $true) {
                        if ($PSCmdlet.ShouldProcess($DestinationServerName, "Creating database restore log folder $DatabaseRestoreLogFolder on $DestinationServerName")) {
                            # Try creating the log folder
                            try {
                                Write-Message -Message "Restore log folder $DatabaseRestoreLogFolder not found. Trying to create it.." -Level Verbose

                                Invoke-Command2 -Credential $DestinationCredential -ScriptBlock {
                                    Write-Message -Message "Restore log folder $DatabaseRestoreLogFolder not found. Trying to create it.." -Level Verbose
                                    New-Item -Path $DatabaseRestoreLogFolder -ItemType Directory -Credential $DestinationCredential -Force:$Force | Out-Null
                                }
                            }
                            catch {
                                Stop-Function -Message "Something went wrong creating the restore log directory" -ErrorRecord $_ -Target $SourceSqlInstance -Continue
                            }
                        }
                    }
                }

                # Chech if the full backup patk can be reached
                if ($FullBackupPath) {
                    Write-Message -Message "Testing full backup path $FullBackupPath" -Level Verbose
                    if ((Test-DbaSqlPath -Path $FullBackupPath -SqlInstance $DestinationSqlInstance -SqlCredential $DestinationCredential) -ne $true) {
                        Stop-Function -Message ("The path to the full backup could not be reached. Check the path and/or the crdential") -ErrorRecord $_ -Target $DestinationSqlInstance -Continue
                    }
                }
                elseif ($UseBackupFolder.Length -ge 1) {
                    Write-Message -Message "Testing backup folder $UseBackupFolder" -Level Verbose
                    if ((Test-DbaSqlPath -Path $UseBackupFolder -SqlInstance $DestinationSqlInstance -SqlCredential $DestinationCredential) -ne $true) {
                        Stop-Function -Message ("The path to the backup folder could not be reached. Check the path and/or the crdential") -ErrorRecord $_ -Target $DestinationSqlInstance -Continue
                    }

                    $BackupPath = $UseBackupFolder
                }
                elseif ($UseExistingFullBackup) {
                    Write-Message -Message "No path to the full backup is set. Trying to retrieve the last full backup for $db from $SourceSqlInstance" -Level Verbose

                    # Get the last full backup
                    $LastBackup = Get-DbaBackupHistory -SqlServer $SourceSqlInstance -Databases $($db.Name) -LastFull -Credential $SourceSqlCredential

                    # Check if there was a last backup
                    if ($LastBackup -ne $null) {
                        # Test the path to the backup
                        Write-Message -Message "Testing last backup path $(($LastBackup[-1]).Path[-1])" -Level Verbose
                        if ((Test-DbaSqlPath -Path ($LastBackup[-1]).Path[-1] -SqlInstance $SourceSqlInstance -SqlCredential $SourceCredential) -ne $true) {
                            Stop-Function -Message "The full backup could not be found on $($LastBackup.Path). Check path and/or credentials" -ErrorRecord $_ -Target $DestinationSqlInstance -Continue
                        }
                        # Check if the source for the last full backup is remote and the backup is on a shared location
                        elseif (($LastBackup.Computername -ne $SourceServerName) -and (($LastBackup[-1]).Path[-1].StartsWith('\\') -eq $false)) {
                            Stop-Function -Message "The last full backup is not located on shared location. `n$($_.Exception.Message)" -ErrorRecord $_ -Target $DestinationSqlInstance -Continue
                        }
                        else {
                            #$FullBackupPath = $LastBackup.Path
                            $BackupPath = $LastBackup.Path
                            Write-Message -Message "Full backup found for $db. Path $BackupPath" -Level Verbose
                        }
                    }
                    else {
                        Write-Message -Message "No Full backup found for $db." -Level Output
                    }
                }
            }

            # Set the copy destination folder to include the database name
            if ($CopyDestinationFolder.EndsWith("\")) {
                $DatabaseCopyDestinationFolder = "$CopyDestinationFolder$($db.Name)"
            }
            else {
                $DatabaseCopyDestinationFolder = "$CopyDestinationFolder\$($db.Name)"
            }
            Write-Message -Message "Copy destination folder set to $DatabaseCopyDestinationFolder." -Level Verbose

            # Check if the copy job name is set
            if ($CopyJob) {
                $DatabaseCopyJob = "$CopyJob_$SourceServerName_$($db.Name)"
            }
            else {
                $DatabaseCopyJob = "LSCopy_$SourceServerName_$($db.Name)"
            }
            Write-Message -Message "Copy job name set to $DatabaseCopyJob" -Level Verbose

            # Check if the copy job schedule name is set
            if ($CopySchedule) {
                $DatabaseCopySchedule = "$CopySchedule_$($db.Name)"
            }
            else {
                $DatabaseCopySchedule = "LSCopySchedule_$($db.Name)"
                Write-Message -Message "Copy job schedule name set to $DatabaseCopySchedule" -Level Verbose
            }

            # Check if the copy destination folder exists
            Write-Message -Message "Testing database copy destination path $DatabaseCopyDestinationFolder" -Level Verbose
            if ((Test-DbaSqlPath -Path $DatabaseCopyDestinationFolder -SqlInstance $DestinationSqlInstance -SqlCredential $DestinationCredential) -ne $true) {
                if ($PSCmdlet.ShouldProcess($DestinationServerName, "Creating copy destination folder on $DestinationServerName")) {
                    try {
                        Invoke-Command2 -Credential $DestinationCredential -ScriptBlock {
                            Write-Message -Message "Copy destination folder $DatabaseCopyDestinationFolder not found. Trying to create it.. ." -Level Verbose
                            New-Item -Path $DatabaseCopyDestinationFolder -ItemType Directory -Credential $DestinationCredential -Force:$Force | Out-Null
                        }
                    }
                    catch {
                        Stop-Function -Message "Something went wrong creating the database copy destination folder. `n$($_.Exception.Message)" -ErrorRecord $_ -Target $DestinationServerName -Continue
                    }
                }
            }

            # Check if the restore job name is set
            if ($RestoreJob) {
                $DatabaseRestoreJob = "$RestoreJob_$SourceServerName_$($db.Name)"
            }
            else {
                $DatabaseRestoreJob = "LSRestore_$DestinationServerName_$($db.Name)"
            }
            Write-Message -Message "Restore job name set to $DatabaseRestoreJob" -Level Verbose

            # Check if the restore job schedule name is set
            if ($RestoreSchedule) {
                $DatabaseRestoreSchedule = "$RestoreSchedule_$($db.Name)"
            }
            else {
                $DatabaseRestoreSchedule = "LSRestoreSchedule_$($db.Name)"
            }
            Write-Message -Message "Restore job schedule name set to $DatabaseRestoreSchedule" -Level Verbose

            # If the database needs to be backed up first
            if ($GenerateFullBackup) {
                if ($PSCmdlet.ShouldProcess($SourceSqlInstance, "Backing up database $db")) {

                    Write-Message -Message "Generating full backup." -Level Output
                    Write-Message -Message "Backing up database $db to $DatabaseBackupNetworkPath" -Level Output

                    try {
                        $Timestamp = Get-Date -format "yyyyMMddHHmmss"

                        $LastBackup = Backup-DbaDatabase -SqlInstance $SourceSqlInstance `
                            -SqlCredential $SourceSqlCredential `
                            -BackupDirectory $DatabaseBackupNetworkPath `
                            -BackupFileName "FullBackup_$($db.Name)_PreLogShipping_$Timestamp.bak" `
                            -Databases $($db.Name) `
                            -Type Full

                        Write-Message -Message "Backup completed." -Level Output

                        # Get the last full backup path
                        #$FullBackupPath = $LastBackup.BackupPath
                        $BackupPath = $LastBackup.BackupPath

                        Write-Message -Message "Backup is located at $BackupPath" -Level Verbose
                    }
                    catch {
                        Stop-Function -Message "Something went wrong generating the full backup" -ErrorRecord $_ -Target $DestinationServerName -Continue
                    }
                }
            }

            # Check of the MonitorServerSecurityMode value is of type string and set the integer value
            if ($PrimaryMonitorServerSecurityMode -notin 0, 1) {
                $PrimaryMonitorServerSecurityMode = switch ($PrimaryMonitorServerSecurityMode) {
                    "SQLSERVER" { 0 } "WINDOWS" { 1 } default { 1 }
                }
            }

            # Check the primary monitor server
            if ($Force -and (-not$PrimaryMonitorServer -or [string]$PrimaryMonitorServer -eq '' -or $PrimaryMonitorServer -eq $null)) {
                Write-Message -Message "Setting monitor server for primary server to $SourceSqlInstance." -Level Output
                $PrimaryMonitorServer = $SourceSqlInstance
            }

            # Check the PrimaryMonitorServerSecurityMode if it's SQL Server authentication
            if ($PrimaryMonitorServerSecurityMode -eq 0) {
                if ($PrimaryMonitorServerLogin) {
                    Stop-Function -Message "The PrimaryMonitorServerLogin cannot be empty when using SQL Server authentication." -Target $SourceSqlInstance -Continue
                }

                if ($PrimaryMonitorServerPassword) {
                    Stop-Function -Message "The PrimaryMonitorServerPassword cannot be empty when using SQL Server authentication." -Target $ -Continue
                }
            }

            # Check of the SecondaryMonitorServerSecurityMode value is of type string and set the integer value
            if ($SecondaryMonitorServerSecurityMode -notin 0, 1) {
                $SecondaryMonitorServerSecurityMode = switch ($SecondaryMonitorServerSecurityMode) {
                    "SQLSERVER" { 0 } "WINDOWS" { 1 } default { 1 }
                }
            }

            # Check the secondary monitor server
            if ($Force -and (-not $SecondaryMonitorServer -or [string]$SecondaryMonitorServer -eq '' -or $SecondaryMonitorServer -eq $null)) {
                Write-Message -Message "Setting secondary monitor server for $DestinationSqlInstance to $SourceSqlInstance." -Level Verbose
                $SecondaryMonitorServer = $SourceSqlInstance
            }

            # Check the MonitorServerSecurityMode if it's SQL Server authentication
            if ($SecondaryMonitorServerSecurityMode -eq 0) {
                if ($SecondaryMonitorServerLogin) {
                    Stop-Function -Message "The SecondaryMonitorServerLogin cannot be empty when using SQL Server authentication." -Target $SourceSqlInstance -Continue
                }

                if ($SecondaryMonitorServerPassword) {
                    Stop-Function -Message "The SecondaryMonitorServerPassword cannot be empty when using SQL Server authentication." -Target $SourceSqlInstance -Continue
                }
            }

            # Now that all the checks have been done we can start with the fun stuff !

            # Restore the full backup
            if ($PSCmdlet.ShouldProcess($DestinationSqlInstance, "Restoring database $db to $SecondaryDatabase on $DestinationSqlInstance")) {
                if ($GenerateFullBackup -or $UseExistingFullBackup -or $UseBackupFolder) {
                    try {
                        Write-Message -Message "Start database restore" -Level Output
                        if ($NoRecovery -or (-not $Standby)) {
                            if ($Force) {
                                Restore-DbaDatabase -SqlServer $DestinationSqlInstance `
                                    -SqlCredential $DestinationSqlCredential `
                                    -Path $BackupPath `
                                    -DestinationFilePrefix $SecondaryDatabasePrefix `
                                    -DestinationFileSuffix $SecondaryDatabaseSuffix `
                                    -DestinationDataDirectory $DatabaseRestoreDataFolder `
                                    -DestinationLogDirectory $DatabaseRestoreLogFolder `
                                    -DatabaseName $SecondaryDatabase `
                                    -DirectoryRecurse `
                                    -NoRecovery `
                                    -WithReplace | Out-Null
                            }
                            else {
                                Restore-DbaDatabase -SqlServer $DestinationSqlInstance `
                                    -SqlCredential $DestinationSqlCredential `
                                    -Path $BackupPath `
                                    -DestinationFilePrefix $SecondaryDatabasePrefix `
                                    -DestinationFileSuffix $SecondaryDatabaseSuffix `
                                    -DestinationDataDirectory $DatabaseRestoreDataFolder `
                                    -DestinationLogDirectory $DatabaseRestoreLogFolder `
                                    -DatabaseName $SecondaryDatabase `
                                    -DirectoryRecurse `
                                    -NoRecovery | Out-Null
                            }
                        }

                        # If the database needs to be in standby
                        if ($Standby) {
                            # Setup the path to the standby file
                            $StandbyDirectory = "$DatabaseCopyDestinationFolder"

                            # Check if credentials need to be used
                            if ($DestinationSqlCredential) {
                                Restore-DbaDatabase -ServerInstance $DestinationSqlInstance `
                                    -SqlCredential $DestinationSqlCredential `
                                    -Path $BackupPath `
                                    -DestinationFilePrefix $SecondaryDatabasePrefix `
                                    -DestinationFileSuffix $SecondaryDatabaseSuffix `
                                    -DestinationDataDirectory $DatabaseRestoreDataFolder `
                                    -DestinationLogDirectory $DatabaseRestoreLogFolder `
                                    -DatabaseName $SecondaryDatabase `
                                    -DirectoryRecurse `
                                    -StandbyDirectory $StandbyDirectory
                            }
                            else {
                                Restore-DbaDatabase -ServerInstance $DestinationSqlInstance `
                                    -Path $BackupPath `
                                    -DestinationFilePrefix $SecondaryDatabasePrefix `
                                    -DestinationFileSuffix $SecondaryDatabaseSuffix `
                                    -DestinationDataDirectory $DatabaseRestoreDataFolder `
                                    -DestinationLogDirectory $DatabaseRestoreLogFolder `
                                    -DatabaseName $SecondaryDatabase `
                                    -DirectoryRecurse `
                                    -StandbyDirectory $StandbyDirectory
                            }
                        }
                    }
                    catch {
                        Stop-Function -Message "Something went wrong restoring the secondary database" -ErrorRecord $_ -Target $SourceSqlInstance -Continue
                    }

                    Write-Message -Message "Restore completed." -Level Output
                }
            }

            #region Set up log shipping on the primary instance
            # Set up log shipping on the primary instance
            if ($PSCmdlet.ShouldProcess($SourceSqlInstance, "Configuring logshipping for primary database $db on $SourceSqlInstance")) {
                try {

                    Write-Message -Message "Configuring logshipping for primary database" -Level Output

                    New-DbaLogShippingPrimaryDatabase -SqlInstance $SourceSqlInstance `
                        -SqlCredential $SourceSqlCredential `
                        -Database $($db.Name) `
                        -BackupDirectory $DatabaseBackupLocalPath `
                        -BackupJob $DatabaseBackupJob `
                        -BackupRetention $BackupRetention `
                        -BackupShare $DatabaseBackupNetworkPath `
                        -BackupThreshold $BackupThreshold `
                        -CompressBackup:$BackupCompression `
                        -HistoryRetention $HistoryRetention `
                        -MonitorServer $PrimaryMonitorServer `
                        -MonitorServerSecurityMode $PrimaryMonitorServerSecurityMode `
                        -MonitorCredential $PrimaryMonitorCredential `
                        -ThresholdAlertEnabled:$PrimaryThresholdAlertEnabled `
                        -Force:$Force

                    # Check if the backup job needs to be enabled or disabled
                    if ($BackupScheduleDisabled) {
                        Set-DbaAgentJob -SqlInstance $SourceSqlInstance -SqlCredential $SourceSqlCredential -Job $DatabaseBackupJob -Disabled
                        Write-Message -Message "Disabling backup job $DatabaseBackupJob" -Level Output
                    }
                    else {
                        Set-DbaAgentJob -SqlInstance $SourceSqlInstance -SqlCredential $SourceSqlCredential -Job $DatabaseBackupJob -Enabled
                        Write-Message -Message "Enabling backup job $DatabaseBackupJob" -Level Output
                    }

                    Write-Message -Message "Create backup job schedule $DatabaseBackupSchedule" -Level Output

                    $BackupJobSchedule = New-DbaAgentSchedule -SqlInstance $SourceSqlInstance `
                        -SqlCredential $SourceSqlCredential `
                        -Job $DatabaseBackupJob `
                        -Schedule $DatabaseBackupSchedule `
                        -FrequencyType $BackupScheduleFrequencyType `
                        -FrequencyInterval $BackupScheduleFrequencyInterval `
                        -FrequencySubdayType $BackupScheduleFrequencySubdayType `
                        -FrequencySubdayInterval $BackupScheduleFrequencySubdayInterval `
                        -FrequencyRelativeInterval $BackupScheduleFrequencyRelativeInterval `
                        -FrequencyRecurrenceFactor $BackupScheduleFrequencyRecurrenceFactor `
                        -StartDate $BackupScheduleStartDate `
                        -EndDate $BackupScheduleEndDate `
                        -StartTime $BackupScheduleStartTime `
                        -EndTime $BackupScheduleEndTime `
                        -Force:$Force

                    Write-Message -Message "Configuring logshipping from primary to secondary database." -Level Output

                    New-DbaLogShippingPrimarySecondary -SqlInstance $SourceSqlInstance `
                        -SqlCredential $SourceSqlCredential `
                        -PrimaryDatabase $($db.Name) `
                        -SecondaryDatabase $SecondaryDatabase `
                        -SecondaryServer $DestinationSqlInstance `
                        -SecondarySqlCredential $DestinationSqlCredential
                }
                catch {
                    Stop-Function -Message "Something went wrong setting up log shipping for primary instance" -ErrorRecord $_ -Target $SourceSqlInstance -Continue
                }
            }
            #endregion Set up log shipping on the primary instance

            #region Set up log shipping on the secondary instance
            # Set up log shipping on the secondary instance
            if ($PSCmdlet.ShouldProcess($DestinationSqlInstance, "Configuring logshipping for secondary database $SecondaryDatabase on $DestinationSqlInstance")) {
                try {

                    Write-Message -Message "Configuring logshipping from secondary database $SecondaryDatabase to primary database $db." -Level Output

                    New-DbaLogShippingSecondaryPrimary -SqlInstance $DestinationSqlInstance `
                        -SqlCredential $DestinationSqlCredential `
                        -BackupSourceDirectory $DatabaseBackupNetworkPath `
                        -BackupDestinationDirectory $DatabaseCopyDestinationFolder `
                        -CopyJob $DatabaseCopyJob `
                        -FileRetentionPeriod $BackupRetention `
                        -MonitorServer $SecondaryMonitorServer `
                        -MonitorServerSecurityMode $SecondaryMonitorServerSecurityMode `
                        -MonitorCredential $SecondaryMonitorCredential `
                        -PrimaryServer $SourceSqlInstance `
                        -PrimaryDatabase $($db.Name) `
                        -RestoreJob $DatabaseRestoreJob `
                        -Force:$Force

                    Write-Message -Message "Create copy job schedule $DatabaseCopySchedule" -Level Output

                    $CopyJobSchedule = New-DbaAgentSchedule -SqlInstance $DestinationSqlInstance `
                        -SqlCredential $DestinationSqlCredential `
                        -Job $DatabaseCopyJob `
                        -Schedule $DatabaseCopySchedule `
                        -FrequencyType $CopyScheduleFrequencyType `
                        -FrequencyInterval $CopyScheduleFrequencyInterval `
                        -FrequencySubdayType $CopyScheduleFrequencySubdayType `
                        -FrequencySubdayInterval $CopyScheduleFrequencySubdayInterval `
                        -FrequencyRelativeInterval $CopyScheduleFrequencyRelativeInterval `
                        -FrequencyRecurrenceFactor $CopyScheduleFrequencyRecurrenceFactor `
                        -StartDate $CopyScheduleStartDate `
                        -EndDate $CopyScheduleEndDate `
                        -StartTime $CopyScheduleStartTime `
                        -EndTime $CopyScheduleEndTime `
                        -Force:$Force

                    Write-Message -Message "Create restore job schedule $DatabaseRestoreSchedule" -Level Output

                    $RestoreJobSchedule = New-DbaAgentSchedule -SqlInstance $DestinationSqlInstance `
                        -SqlCredential $DestinationSqlCredential `
                        -Job $DatabaseRestoreJob `
                        -Schedule $DatabaseRestoreSchedule `
                        -FrequencyType $RestoreScheduleFrequencyType `
                        -FrequencyInterval $RestoreScheduleFrequencyInterval `
                        -FrequencySubdayType $RestoreScheduleFrequencySubdayType `
                        -FrequencySubdayInterval $RestoreScheduleFrequencySubdayInterval `
                        -FrequencyRelativeInterval $RestoreScheduleFrequencyRelativeInterval `
                        -FrequencyRecurrenceFactor $RestoreScheduleFrequencyRecurrenceFactor `
                        -StartDate $RestoreScheduleStartDate `
                        -EndDate $RestoreScheduleEndDate `
                        -StartTime $RestoreScheduleStartTime `
                        -EndTime $RestoreScheduleEndTime `
                        -Force:$Force

                    Write-Message -Message "Configuring logshipping for secondary database." -Level Output

                    New-DbaLogShippingSecondaryDatabase -SqlInstance $DestinationSqlInstance `
                        -SqlCredential $DestinationSqlCredential `
                        -SecondaryDatabase $SecondaryDatabase `
                        -PrimaryServer $SourceSqlInstance `
                        -PrimaryDatabase $($db.Name) `
                        -RestoreDelay $RestoreDelay `
                        -RestoreMode $DatabaseStatus `
                        -DisconnectUsers:$DisconnectUsers `
                        -RestoreThreshold $RestoreThreshold `
                        -ThresholdAlertEnabled:$SecondaryThresholdAlertEnabled `
                        -HistoryRetention $HistoryRetention

                    # Check if the copy job needs to be enabled or disabled
                    if ($CopyScheduleDisabled) {
                        Set-DbaAgentJob -SqlInstance $DestinationSqlInstance -SqlCredential $DestinationSqlCredential -Job $DatabaseCopyJob -Disabled
                    }
                    else {
                        Set-DbaAgentJob -SqlInstance $DestinationSqlInstance -SqlCredential $DestinationSqlCredential -Job $DatabaseCopyJob -Enabled
                    }

                    # Check if the restore job needs to be enabled or disabled
                    if ($RestoreScheduleDisabled) {
                        Set-DbaAgentJob -SqlInstance $DestinationSqlInstance -SqlCredential $DestinationSqlCredential -Job $DatabaseRestoreJob -Disabled
                    }
                    else {
                        Set-DbaAgentJob -SqlInstance $DestinationSqlInstance -SqlCredential $DestinationSqlCredential -Job $DatabaseRestoreJob -Enabled
                    }

                }
                catch {
                    Stop-Function -Message "Something went wrong setting up log shipping for secondary instance.`n$($_.Exception.Message)" -ErrorRecord $_ -Target $DestinationSqlInstance -Continue
                }
            }
            #endregion Set up log shipping on the secondary instance

            Write-Message -Message "Completed configuring log shipping for database $db" -Level Output

        } # for each database
    } # end process

    end {
        Write-Message -Message "Finished setting up log shipping." -Level Verbose
    }
}
function Invoke-DbaLogShippingRecovery {
    <#
        .SYNOPSIS
            Invoke-DbaLogShippingRecovery recovers log shipped databases to a normal state to act upon a migration or disaster.

        .DESCRIPTION
            By default all the databases for a particular instance are recovered.
            If the database is in the right state, either standby or recovering, the process will try to recover the database.

            At first the function will check if the backup source directory can still be reached.
            If so it will look up the last transaction log backup for the database. If that backup file is not the last copied file the log shipping copy job will be started.
            If the directory cannot be reached for the function will continue to the restoring process.
            After the copy job check is performed the job is disabled to prevent the job to run.

            For the restore the log shipping status is checked in the msdb database.
            If the last restored file is not the same as the last file name found, the log shipping restore job will be executed.
            After the restore job check is performed the job is disabled to prevent the job to run

            The last part is to set the database online by restoring the databases with recovery

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to

        .PARAMETER Database
            Database to perform the restore for. This value can also be piped enabling multiple databases to be recovered.
            If this value is not supplied all databases will be recovered.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER NoRecovery
            Allows you to choose to not restore the database to a functional state (Normal) in the final steps of the process.
            By default the database is restored to a functional state (Normal).

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER Force
            Use this parameter to force the function to continue and perform any adjusting actions to successfully execute

        .PARAMETER Delay
            Set the delay in seconds to wait for the copy and/or restore jobs.
            By default the delay is 5 seconds

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .NOTES
            Tags: LogShipping
            Author: Sander Stad (@sqlstad, sqlstad.nl)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Invoke-DbaLogShippingRecovery

        .EXAMPLE
            Invoke-DbaLogShippingRecovery -SqlServer server1

            Recovers all the databases on the instance that are enabled for log shipping

        .EXAMPLE
            Invoke-DbaLogShippingRecovery -SqlServer server1 -SqlCredential $cred -Verbose

            Recovers all the databases on the instance that are enabled for log shipping using a credential

        .EXAMPLE
            Invoke-DbaLogShippingRecovery -SqlServer server1 -database db_logship -Verbose

            Recovers the database "db_logship" to a normal status

        .EXAMPLE
            db1, db2, db3, db4 | Invoke-DbaLogShippingRecovery -SqlServer server1 -Verbose

            Recovers the database db1, db2, db3, db4 to a normal status

        .EXAMPLE
            Invoke-DbaLogShippingRecovery -SqlServer server1 -WhatIf

            Shows what would happen if the command were executed.
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param
    (
        [Parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [object]$SqlInstance,
        [Parameter(ValueFromPipeline = $true)]
        [object[]]$Database,
        [PSCredential]$SqlCredential,
        [switch]$NoRecovery,
        [Alias('Silent')]
        [switch]$EnableException,
        [switch]$Force,
        [int]$Delay = 5
    )

    begin {
        if (!$sqlinstance -and $database.Count -lt 1) {
            # You can prolly do this with
            Stop-Function -Message "You must pipe an SMO database object or specify SqlInstance"
            return
        }

        if ($sqlinstance) {
            # Check the instance if it is a named instance
            $servername, $instancename = $sqlinstance.Split("\")

            if ($null -eq $instancename) {
                $instancename = "MSSQLSERVER"
            }

            Write-Message -Message "Connecting to Sql Server" -Level Output
            try {
                $server = Connect-SqlInstance -SqlInstance $sqlinstance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance
            }

            if ($Force -and (!$database -or $database.Count -lt 1)) {
                $database = $server.databases
            }
            elseif (-not $Force -and (!$database -or $database.Count -lt 1)) {
                Stop-Function -Message "Please enter one or more databases to recover from log shipping" -Target $instance
            }
            else {
                $databases = $server.databases | Where-Object Name -in $database
            }
        }
    }

    process {
        # Try to get the agent service details
        try {
            # Start the service
            $agentservice = Get-DbaSqlService -ComputerName $servername | Where-Object {($_.ComputerName -eq $servername) -and ($_.DisplayName -eq "SQL Server Agent ($instancename)")}
        }
        catch {
            # Stop the function when the service was unable to start
            Stop-Function -Message "Unable to start SQL Server Agent Service" -ErrorRecord $_ -Target $sqlinstance
            return
        }

        # Check if the service is running
        if ($agentservice.State -ne 'Running') {

            if ($Force) {
                try {
                    Start-DbaSqlService -ComputerName $servername -InstanceName $instancename -Type Agent -Credential $SqlCredential
                }
                catch {
                    # Stop the function when the service was unable to start
                    Stop-Function -Message "Unable to start SQL Server Agent Service" -ErrorRecord $_ -Target $sqlinstance
                    return
                }
            }
            # If the force switch and the silent switch are not set
            elseif (!$Force -and !$EnableException) {
                # Set up the parts for the user choice
                $Title = "SQL Server Agent is not running"
                $Info = "Do you want to start the SQL Server Agent service?"

                $Options = [System.Management.Automation.Host.ChoiceDescription[]] @("&Start", "&Quit")
                [int]$Defaultchoice = 0
                $choice = $host.UI.PromptForChoice($Title, $Info, $Options, $Defaultchoice)

                # Check the given option
                if ($choice -eq 0) {
                    try {
                        # Start the service
                        Start-DbaSqlService -ComputerName $servername -InstanceName $instancename -Type Agent -Credential $SqlCredential
                    }
                    catch {
                        # Stop the function when the service was unable to start
                        Stop-Function -Message "Unable to start SQL Server Agent Service" -ErrorRecord $_ -Target $sqlinstance
                        return
                    }
                }
                else {
                    Stop-Function -Message "The SQL Server Agent service needs to be started to be able to recover the databases" -ErrorRecord $_ -Target $sqlinstance
                    return
                }
            }
            # If the force switch it not set and the silent switch is set
            elseif (!$Force -and $EnableException) {
                Stop-Function -Message "The SQL Server Agent service needs to be started to be able to recover the databases" -ErrorRecord $_ -Target $sqlinstance
                return
            }
            # If nothing else matches and the agent service is not started
            else {
                Stop-Function -Message "The SQL Server Agent service needs to be started to be able to recover the databases" -ErrorRecord $_ -Target $sqlinstance
                return
            }

        }

        Write-Message -Message "Started Log Shipping Recovery" -Level Output

        # Loop through all the databases
        foreach ($db in $databases) {
            # Query for retrieving the log shipping information
            $query = "SELECT lss.primary_server, lss.primary_database, lsd.secondary_database, lss.backup_source_directory,
            lss.backup_destination_directory, lss.last_copied_file, lss.last_copied_date,
            lsd.last_restored_file, sj1.name AS 'copyjob', sj2.name AS 'restorejob'
        FROM msdb.dbo.log_shipping_secondary AS lss
            INNER JOIN msdb.dbo.log_shipping_secondary_databases AS lsd ON lsd.secondary_id = lss.secondary_id
            INNER JOIN msdb.dbo.sysjobs AS sj1 ON sj1.job_id = lss.copy_job_id
            INNER JOIN msdb.dbo.sysjobs AS sj2 ON sj2.job_id = lss.restore_job_id
        WHERE lsd.secondary_database = '$($db.Name)'"

            # Retrieve the log shipping information from the secondary instance
            try {
                Write-Message -Message "Retrieving log shipping information from the secondary instance" -Level Verbose
                $logshipping_details = $server.Query($query)
            }
            catch {
                Stop-Function -Message "Error retrieving the log shipping details: $($_.Exception.Message)" -ErrorRecord $_ -Target $sqlinstance
                return
            }

            # Check if there are any databases to recover
            if ($null -eq $logshipping_details) {
                Stop-Function -Message "The database $db is not configured as a secondary database for log shipping." -Continue
            }
            else {
                # Loop through each of the log shipped databases
                foreach ($ls in $logshipping_details) {
                    $secondarydb = $ls.secondary_database

                    # Check if the database is in the right state
                    if ($server.Databases[$secondarydb].Status -notin ('Normal, Standby', 'Standby', 'Restoring')) {
                        Stop-Function -Message "The database $db doesn't have the right status to be recovered" -Continue
                    }
                    else {
                        Write-Message -Message "Started Recovery for $secondarydb" -Level Verbose

                        # Get the last file from the backup source directory
                        <# !!!! set credentials !!! #>
                        $latestBackupSource = Get-ChildItem -Path $ls.backup_source_directory -filter ("*" + $ls.primary_database + "*") | Where-Object { ($_.Extension -eq '.trn') } | Sort-Object LastWriteTime -Descending | Select-Object -First 1

                        # Get al the backup files from the destination directory
                        <# !!!! set credentials !!! #>
                        $latestBackupDest = Get-ChildItem -Path $ls.backup_destination_directory -filter ("*" + $ls.primary_database + "*") | Where-Object { ($_.Extension -eq '.trn') } | Sort-Object LastWriteTime -Descending | Select-Object -First 1

                        # Check if source and destination directory are in sync
                        if ($latestBackupSource.Name -ne $latestBackupDest.Name) {
                            # Check if the backup source directory can be reached
                            if (Test-DbaSqlPath -SqlInstance $SqlInstance -Path $ls.backup_source_directory -SqlCredential $SqlCredential) {

                                # Check if the latest file is also the latest copied file
                                if ($latestBackupSource.Name -ne ([string]$ls.last_copied_file).Split('\')[-1]) {
                                    Write-Message -Message "Backup destination is not up-to-date" -Level Verbose

                                    # Start the job to get the latest files
                                    if ($PSCmdlet.ShouldProcess($sqlinstance, ("Starting copy job $($ls.copyjob)"))) {
                                        Write-Message -Message "Starting copy job $($ls.copyjob)" -Level Verbose
                                        try {
                                            $server.JobServer.Jobs[$ls.copyjob].Start()
                                        }
                                        catch {
                                            Stop-Function -Message "Something went wrong starting the restore job.`n$($_)" -ErrorRecord $_ -Target $sqlinstance
                                        }

                                        Write-Message -Message "Copying files to $($ls.backup_destination_directory)" -Level Verbose

                                        # Check if the file has been copied
                                        $query = "SELECT last_copied_file FROM msdb.dbo.log_shipping_secondary WHERE primary_database = '$($ls.primary_database)' AND last_copied_file IS NOT NULL "
                                        $latestcopy = $server.Query($query)

                                        Write-Message -Message "Waiting for the copy action to complete.." -Level Verbose

                                        while (($latestBackupSource.Name -ne ([string]$latestcopy.last_copied_file).Split('\')[-1])) {
                                            # Sleep for while to let the files be copied
                                            Start-Sleep -Seconds $Delay

                                            # Again get the latest file to check if the process can continue
                                            $latestcopy = $server.Query($query)
                                        }

                                        # Again get the latest file to check if the process can continue
                                        $latestcopy = $server.Query($query)

                                        # Check the lat outcome of the job
                                        if ($server.JobServer.Jobs[$ls.copyjob].LastRunOutcome -eq 'Failed') {
                                            Stop-Function -Message "The copy job for database $db failed. Please check the error log." -Continue
                                        }

                                        Write-Message -Message "Copying of backup files finished" -Level Verbose
                                    } # if should process
                                } # if latest file name
                            } # if backup directory test
                            else {
                                Stop-Function -Message "Couldn't reach the backup source directory. Continuing..." -Continue
                            }
                        } # check latest backup file is already in directory


                        # Disable the log shipping copy job on the secondary instance
                        if ($PSCmdlet.ShouldProcess($sqlinstance, "Disabling copy job $($ls.copyjob)")) {
                            try {
                                Write-Message -Message "Disabling copy job $($ls.copyjob)" -Level Verbose
                                $server.JobServer.Jobs[$ls.copyjob].IsEnabled = $false
                                $server.JobServer.Jobs[$ls.copyjob].Alter()
                            }
                            catch {
                                Stop-Function -Message "Something went wrong disabling the copy job.`n$($_)" -ErrorRecord $_ -Target $sqlinstance
                            }
                        }

                        # Check if the file has been copied
                        $query = "SELECT last_restored_file FROM msdb.dbo.log_shipping_secondary_databases WHERE secondary_database = '$secondarydb' AND last_restored_file IS NOT NULL"
                        $latestrestore = $server.Query($query)

                        # Check if the last copied file is newer than the last restored file
                        if ((([string]$latestcopy.last_copied_file).Split('\')[-1] -ne ([string]$latestrestore.last_restored_file).Split('\')[-1]) -or ($null -eq ([string]$latestcopy.last_copied_file).Split('\')[-1])) {
                            Write-Message -Message "Restore is not up-to-date" -Level Verbose

                            # Start the restore job
                            if ($PSCmdlet.ShouldProcess($sqlinstance, ("Starting restore job " + $ls.restorejob))) {
                                Write-Message -Message "Starting restore job $($ls.restorejob)" -Level Verbose
                                try {
                                    $server.JobServer.Jobs[$ls.restorejob].Start()
                                }
                                catch {
                                    Stop-Function -Message "Something went wrong starting the restore job.`n$($_)" -ErrorRecord $_ -Target $sqlinstance
                                }

                                Write-Message -Message "Waiting for the restore action to complete.." -Level Verbose

                                while ($latestBackupSource.Name -ne [string]($latestrestore.last_restored_file).Split('\')[-1]) {
                                    # Sleep for while to let the files be copied
                                    Start-Sleep -Seconds $Delay

                                    # Again get the latest file to check if the process can continue
                                    $latestrestore = $server.Query($query)
                                }

                                # Again get the latest file to check if the process can continue
                                $latestrestore = $server.Query($query)

                                # Check the lat outcome of the job
                                if ($server.JobServer.Jobs[$ls.restorejob].LastRunOutcome -eq 'Failed') {
                                    Stop-Function -Message "The restore job for database $db failed. Please check the error log." -Continue
                                }
                            }
                        }

                        # Disable the log shipping restore job on the secondary instance
                        if ($PSCmdlet.ShouldProcess($sqlinstance, "Disabling restore job $($ls.restorejob)")) {
                            try {
                                Write-Message -Message ("Disabling restore job " + $ls.restorejob) -Level Verbose
                                $server.JobServer.Jobs[$ls.restorejob].IsEnabled = $false
                                $server.JobServer.Jobs[$ls.restorejob].Alter()
                            }
                            catch {
                                Stop-Function -Message "Something went wrong disabling the restore job.`n$($_)" -ErrorRecord $_ -Target $sqlinstance
                            }

                        }

                        # Check for the last time if everything is up-to-date
                        if ($latestBackupSource.Name -eq [string]($latestrestore.last_restored_file).Split('\')[-1]) {
                            # Check if the database needs to recovered to its normal state
                            if ($NoRecovery -eq $false) {
                                if ($PSCmdlet.ShouldProcess($secondarydb, "Restoring database with recovery")) {
                                    Write-Message -Message "Restoring the database to it's normal state" -Level Verbose
                                    $query = "RESTORE DATABASE [$secondarydb] WITH RECOVERY"
                                    $server.Query($query)
                                }
                            }
                            else {
                                Write-Message -Message "Skipping restore with recovery" -Level Output
                            }
                        }

                        Write-Message -Message ("Finished Recovery for $secondarydb") -Level Output

                        # Reset the log ship details
                        $logshipping_details = $null

                    } # database in restorable mode
                } # foreach ls details
            } # ls details are not null
        } # foreach database
    } # process
}
function Invoke-DbaPfRelog {
    <#
        .SYNOPSIS
            Pipeline-compatible wrapper for the relog command which is available on modern Windows platforms.

        .DESCRIPTION
            Pipeline-compatible wrapper for the relog command. Relog is useful for converting Windows Perfmon.

            Extracts performance counters from performance counter logs into other formats,
            such as text-TSV (for tab-delimited text), text-CSV (for comma-delimited text), binary-BIN, or SQL.

            relog "C:\PerfLogs\Admin\System Correlation\WORKSTATIONX_20180112-000001\DataCollector01.blg" -o C:\temp\foo.csv -f tsv

            If you find any input hangs, please send us the output so we can accommodate for it then use -Raw for an immediate solution.

        .PARAMETER Path
            Specifies the pathname of an existing performance counter log or performance counter path. You can specify multiple input files.

        .PARAMETER Destination
            Specifies the pathname of the output file or SQL database where the counters will be written. Defaults to the same directory as the source.

        .PARAMETER Type
            The output format. Defaults to tsv. Options include tsv, csv, bin, and sql.

            For a SQL database, the output file specifies the DSN!counter_log. You can specify the database location by using the ODBC manager to configure the DSN (Database System Name).

            For more information, read here: https://technet.microsoft.com/en-us/library/bb490958.aspx

        .PARAMETER Append
            If this switch is enabled, output will be appended to the specified file instead of overwriting. This option does not apply to SQL format where the default is always to append.

        .PARAMETER AllowClobber
            If this switch is enabled, the destination file will be overwritten if it exists.

        .PARAMETER PerformanceCounter
            Specifies the performance counter path to log.

        .PARAMETER PerformanceCounterPath
            Specifies the pathname of the text file that lists the performance counters to be included in a relog file. Use this option to list counter paths in an input file, one per line. Default setting is all counters in the original log file are relogged.

        .PARAMETER Interval
            Specifies sample intervals in "n" records. Includes every nth data point in the relog file. Default is every data point.

        .PARAMETER BeginTime
            This is is Get-Date object and we format it for you.

        .PARAMETER EndTime
            Specifies end time for copying last record from the input file. This is is Get-Date object and we format it for you.

        .PARAMETER ConfigPath
            Specifies the pathname of the settings file that contains command-line parameters.

        .PARAMETER Summary
            If this switch is enabled, the performance counters and time ranges of log files specified in the input file will be displayed.

        .PARAMETER Multithread
            If this switch is enabled, processing will be done in parallel. This may speed up large batches or large files.

        .PARAMETER AllTime
            If this switch is enabled and a datacollector or datacollectorset is passed in via the pipeline, collects all logs, not just the latest.

        .PARAMETER Raw
            If this switch is enabled, the results of the DOS command instead of Get-ChildItem will be displayed. This does not run in parallel.

        .PARAMETER InputObject
            Accepts the output of Get-DbaPfDataCollector and Get-DbaPfDataCollectorSet as input via the pipeline.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Performance, DataCollector, PerfCounter, Relog
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Invoke-DbaPfRelog

        .EXAMPLE
            Invoke-DbaPfRelog -Path C:\temp\perfmon.blg

            Creates C:\temp\perfmon.tsv from C:\temp\perfmon.blg.

        .EXAMPLE
            Invoke-DbaPfRelog -Path C:\temp\perfmon.blg -Destination C:\temp\a\b\c

            Creates the temp, a, and b directories if needed, then generates c.tsv (tab separated) from C:\temp\perfmon.blg.

            Returns the newly created file as a file object.

        .EXAMPLE
            Get-DbaPfDataCollectorSet -ComputerName sql2016 | Get-DbaPfDataCollector | Invoke-DbaPfRelog -Destination C:\temp\perf

            Creates C:\temp\perf if needed, then generates computername-datacollectorname.tsv (tab separated) from the latest logs of all data collector sets on sql2016. This destination format was chosen to avoid naming conflicts with piped input.

        .EXAMPLE
            Invoke-DbaPfRelog -Path C:\temp\perfmon.blg -Destination C:\temp\a\b\c -Raw

            Creates the temp, a, and b directories if needed, then generates c.tsv (tab separated) from C:\temp\perfmon.blg then outputs the raw results of the relog command.

            [Invoke-DbaPfRelog][21:21:35] relog "C:\temp\perfmon.blg" -f csv -o C:\temp\a\b\c

            Input
            ----------------
            File(s):
                C:\temp\perfmon.blg (Binary)

            Begin:    1/13/2018 5:13:23
            End:      1/13/2018 14:29:55
            Samples:  2227

            100.00%

            Output
            ----------------
            File:     C:\temp\a\b\c.csv

            Begin:    1/13/2018 5:13:23
            End:      1/13/2018 14:29:55
            Samples:  2227

            The command completed successfully.

        .EXAMPLE
            Invoke-DbaPfRelog -Path 'C:\temp\perflog with spaces.blg' -Destination C:\temp\a\b\c -Type csv -BeginTime ((Get-Date).AddDays(-30)) -EndTime ((Get-Date).AddDays(-1))

            Creates the temp, a, and b directories if needed, then generates c.csv (comma separated) from C:\temp\perflog with spaces.blg', starts 30 days ago and ends one day ago.

        .EXAMPLE
            $servers | Get-DbaPfDataCollectorSet | Get-DbaPfDataCollector | Invoke-DbaPfRelog -Multithread -AllowClobber

            Relogs latest data files from all collectors within the servers listed in $servers.

        .EXAMPLE
            Get-DbaPfDataCollector -Collector DataCollector01 | Invoke-DbaPfRelog -AllowClobber -AllTime

            Relogs all the log files from the DataCollector01 on the local computer and allows overwrite.
    #>
    [CmdletBinding()]
    param (
        [parameter(ValueFromPipelineByPropertyName)]
        [Alias("FullName")]
        [string[]]$Path,
        [string]$Destination,
        [ValidateSet("tsv", "csv", "bin", "sql")]
        [string]$Type = "tsv",
        [switch]$Append,
        [switch]$AllowClobber,
        [string[]]$PerformanceCounter,
        [string]$PerformanceCounterPath,
        [int]$Interval,
        [datetime]$BeginTime,
        [datetime]$EndTime,
        [string]$ConfigPath,
        [switch]$Summary,
        [parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [switch]$Multithread,
        [switch]$AllTime,
        [switch]$Raw,
        [switch]$EnableException
    )
    begin {
        if (Test-Bound -ParameterName BeginTime) {
            $script:beginstring = ($BeginTime -f 'M/d/yyyy hh:mm:ss' | Out-String).Trim()
        }
        if (Test-Bound -ParameterName EndTime) {
            $script:endstring = ($EndTime -f 'M/d/yyyy hh:mm:ss' | Out-String).Trim()
        }

        $allpaths = @()
        $allpaths += $Path

        # to support multithreading
        if (Test-Bound -ParameterName Destination) {
            $script:destinationset = $true
            $originaldestination = $Destination
        }
        else {
            $script:destinationset = $false
        }
    }
    process {
        if ($Append -and $Type -ne "bin") {
            Stop-Function -Message "Append can only be used with -Type bin." -Target $Path
            return
        }

        if ($InputObject) {
            foreach ($object in $InputObject) {
                # DataCollectorSet
                if ($object.OutputLocation -and $object.RemoteOutputLocation) {
                    $instance = [dbainstance]$object.ComputerName

                    if (-not $AllTime) {
                        if ($instance.IsLocalHost) {
                            $allpaths += (Get-ChildItem -Recurse -Path $object.LatestOutputLocation -Include *.blg -ErrorAction SilentlyContinue).FullName
                        }
                        else {
                            $allpaths += (Get-ChildItem -Recurse -Path $object.RemoteLatestOutputLocation -Include *.blg -ErrorAction SilentlyContinue).FullName
                        }
                    }
                    else {
                        if ($instance.IsLocalHost) {
                            $allpaths += (Get-ChildItem -Recurse -Path $object.OutputLocation -Include *.blg -ErrorAction SilentlyContinue).FullName
                        }
                        else {
                            $allpaths += (Get-ChildItem -Recurse -Path $object.RemoteOutputLocation -Include *.blg -ErrorAction SilentlyContinue).FullName
                        }
                    }


                    $script:perfmonobject = $true
                }
                # DataCollector
                if ($object.LatestOutputLocation -and $object.RemoteLatestOutputLocation) {
                    $instance = [dbainstance]$object.ComputerName

                    if (-not $AllTime) {
                        if ($instance.IsLocalHost) {
                            $allpaths += (Get-ChildItem -Recurse -Path $object.LatestOutputLocation -Include *.blg -ErrorAction SilentlyContinue).FullName
                        }
                        else {
                            $allpaths += (Get-ChildItem -Recurse -Path $object.RemoteLatestOutputLocation -Include *.blg -ErrorAction SilentlyContinue).FullName
                        }
                    }
                    else {
                        if ($instance.IsLocalHost) {
                            $allpaths += (Get-ChildItem -Recurse -Path (Split-Path $object.LatestOutputLocation) -Include *.blg -ErrorAction SilentlyContinue).FullName
                        }
                        else {
                            $allpaths += (Get-ChildItem -Recurse -Path (Split-Path $object.RemoteLatestOutputLocation) -Include *.blg -ErrorAction SilentlyContinue).FullName
                        }
                    }
                    $script:perfmonobject = $true
                }
            }
        }
    }

    # Gotta collect all the paths first then process them otherwise there may be duplicates
    end {
        $allpaths = $allpaths | Where-Object { $_ -match '.blg' } | Select-Object -Unique

        if (-not $allpaths) {
            Stop-Function -Message "Could not find matching .blg files" -Target $file -Continue
            return
        }

        $scriptblock = {
            if ($args) {
                $file = $args
            }
            else {
                $file = $psitem
            }
            $item = Get-ChildItem -Path $file -ErrorAction SilentlyContinue

            if ($null -eq $item) {
                Stop-Function -Message "$file does not exist." -Target $file -Continue
                return
            }

            if (-not $script:destinationset -and $file -match "C\:\\.*Admin.*") {
                $null = Test-ElevationRequirement -ComputerName $env:COMPUTERNAME -Continue
            }

            if ($script:destinationset -eq $false -and -not $Append) {
                $Destination = Join-Path (Split-Path $file) $item.BaseName
            }

            if ($Destination -and $Destination -notmatch "\." -and -not $Append -and $script:perfmonobject) {
                # if destination is set, then it needs a different name
                if ($script:destinationset -eq $true) {
                    if ($file -match "\:") {
                        $computer = $env:COMPUTERNAME
                    }
                    else {
                        $computer = $file.Split("\")[2]
                    }
                    # Avoid naming conflicts
                    $timestamp = Get-Date -format yyyyMMddHHmmfff
                    $Destination = Join-Path $originaldestination "$computer - $($item.BaseName) - $timestamp"
                }
            }

            $params = @("`"$file`"")

            if ($Append) {
                $params += "-a"
            }

            if ($PerformanceCounter) {
                $parsedcounters = $PerformanceCounter -join " "
                $params += "-c `"$parsedcounters`""
            }

            if ($PerformanceCounterPath) {
                $params += "-cf `"$PerformanceCounterPath`""
            }

            $params += "-f $Type"

            if ($Interval) {
                $params += "-t $Interval"
            }

            if ($Destination) {
                $params += "-o `"$Destination`""
            }

            if ($script:beginstring) {
                $params += "-b $script:beginstring"
            }

            if ($script:endstring) {
                $params += "-e $script:endstring"
            }

            if ($ConfigPath) {
                $params += "-config $ConfigPath"
            }

            if ($Summary) {
                $params += "-q"
            }


            if (-not ($Destination.StartsWith("DSN"))) {
                $outputisfile = $true
            }
            else {
                $outputisfile = $false
            }

            if ($outputisfile) {
                if ($Destination) {
                    $dir = Split-Path $Destination
                    if (-not (Test-Path -Path $dir)) {
                        try {
                            $null = New-Item -ItemType Directory -Path $dir -ErrorAction Stop
                        }
                        catch {
                            Stop-Function -Message "Failure" -ErrorRecord $_ -Target $Destination -Continue
                        }
                    }

                    if ((Test-Path $Destination) -and -not $Append -and ((Get-Item $Destination) -isnot [System.IO.DirectoryInfo])) {
                        if ($AllowClobber) {
                            try {
                                Remove-Item -Path "$Destination" -ErrorAction Stop
                            }
                            catch {
                                Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
                            }
                        }
                        else {
                            if ($Type -eq "bin") {
                                Stop-Function -Message "$Destination exists. Use -AllowClobber to overwrite or -Append to append." -Continue
                            }
                            else {
                                Stop-Function -Message "$Destination exists. Use -AllowClobber to overwrite." -Continue
                            }
                        }
                    }

                    if ((Test-Path "$Destination.$type") -and -not $Append) {
                        if ($AllowClobber) {
                            try {
                                Remove-Item -Path "$Destination.$type" -ErrorAction Stop
                            }
                            catch {
                                Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
                            }
                        }
                        else {
                            if ($Type -eq "bin") {
                                Stop-Function -Message "$("$Destination.$type") exists. Use -AllowClobber to overwrite or -Append to append." -Continue
                            }
                            else {
                                Stop-Function -Message "$("$Destination.$type") exists. Use -AllowClobber to overwrite." -Continue
                            }
                        }
                    }
                }
            }

            $arguments = ($params -join " ")

            try {
                if ($Raw) {
                    Write-Message -Level Output -Message "relog $arguments"
                    cmd /c "relog $arguments"
                }
                else {
                    Write-Message -Level Verbose -Message "relog $arguments"
                    $scriptblock = {
                        $output = (cmd /c "relog $arguments" | Out-String).Trim()

                        if ($output -notmatch "Success") {
                            Stop-Function -Continue -Message $output.Trim("Input")
                        }
                        else {
                            Write-Message -Level Verbose -Message "$output"
                            $array = $output -Split [environment]::NewLine
                            $files = $array | Select-String "File:"

                            foreach ($rawfile in $files) {
                                $rawfile = $rawfile.ToString().Replace("File:", "").Trim()
                                $gcierror = $null
                                Get-ChildItem $rawfile -ErrorAction SilentlyContinue -ErrorVariable gcierror | Add-Member -MemberType NoteProperty -Name RelogFile -Value $true -PassThru -ErrorAction Ignore
                                if ($gcierror) {
                                    Write-Message -Level Verbose -Message "$gcierror"
                                }
                            }
                        }
                    }
                    Invoke-Command -ScriptBlock $scriptblock
                }
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_ -Target $path
            }
        }

        if ($Multithread) {
            $allpaths | Invoke-Parallel -ImportVariables -ImportModules -ScriptBlock $scriptblock -ErrorAction SilentlyContinue -ErrorVariable parallelerror
            if ($parallelerror) {
                Write-Message -Level Verbose -Message "$parallelerror"
            }
        }
        else {
            foreach ($file in $allpaths) { Invoke-Command -ScriptBlock $scriptblock -ArgumentList $file }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Invoke-DbaSqlQuery {
    <#
        .SYNOPSIS
            A command to run explicit T-SQL commands or files.

        .DESCRIPTION
            This function is a wrapper command around Invoke-DbaSqlAsync, which in turn is based on Invoke-SqlCmd2.
            It was designed to be more convenient to use in a pipeline and to behave in a way consistent with the rest of our functions.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Credential object used to connect to the SQL Server Instance as a different user. This can be a Windows or SQL Server account. Windows users are determined by the existence of a backslash, so if you are intending to use an alternative Windows connection instead of a SQL login, ensure it contains a backslash.

        .PARAMETER Database
            The database to select before running the query. This list is auto-populated from the server.

        .PARAMETER Query
            Specifies one or more queries to be run. The queries can be Transact-SQL, XQuery statements, or sqlcmd commands. Multiple queries in a single batch may be separated by a semicolon or a GO

            Escape any double quotation marks included in the string.

            Consider using bracketed identifiers such as [MyTable] instead of quoted identifiers such as "MyTable".

        .PARAMETER QueryTimeout
            Specifies the number of seconds before the queries time out.

        .PARAMETER File
            Specifies the path to one or several files to be used as the query input.

        .PARAMETER SqlObject
            Specify on or multiple SQL objects. Those will be converted to script and their scripts run on the target system(s).

        .PARAMETER As
            Specifies output type. Valid options for this parameter are 'DataSet', 'DataTable', 'DataRow', 'PSObject', and 'SingleValue'

            PSObject output introduces overhead but adds flexibility for working with results: http://powershell.org/wp/forums/topic/dealing-with-dbnull/

        .PARAMETER SqlParameters
            Specifies a hashtable of parameters for parameterized SQL queries.  http://blog.codinghorror.com/give-me-parameterized-sql-or-give-me-death/

        .PARAMETER AppendServerInstance
            If this switch is enabled, the SQL Server instance will be appended to PSObject and DataRow output.

        .PARAMETER MessagesToOutput
            Use this switch to have on the output stream messages too (e.g. PRINT statements). Output will hold the resultset too. See examples for detail

        .PARAMETER InputObject
            A collection of databases (such as returned by Get-DbaDatabase)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database, Query
            Author: Fred Winmann (@FredWeinmann)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Invoke-DbaSqlQuery

        .EXAMPLE
            Invoke-DbaSqlQuery -SqlInstance server\instance -Query 'SELECT foo FROM bar'

            Runs the sql query 'SELECT foo FROM bar' against the instance 'server\instance'

        .EXAMPLE
            Get-DbaRegisteredServer -SqlInstance [SERVERNAME] -Group [GROUPNAME] | Invoke-DbaSqlQuery -Query 'SELECT foo FROM bar'

            Runs the sql query 'SELECT foo FROM bar' against all instances in the group [GROUPNAME] on the CMS [SERVERNAME]

        .EXAMPLE
            "server1", "server1\nordwind", "server2" | Invoke-DbaSqlQuery -File "C:\scripts\sql\rebuild.sql"

            Runs the sql commands stored in rebuild.sql against the instances "server1", "server1\nordwind" and "server2"

        .EXAMPLE
            Get-DbaDatabase -SqlInstance "server1", "server1\nordwind", "server2" | Invoke-DbaSqlQuery -File "C:\scripts\sql\rebuild.sql"

            Runs the sql commands stored in rebuild.sql against all accessible databases of the instances "server1", "server1\nordwind" and "server2"
    #>
    [CmdletBinding(DefaultParameterSetName = "Query")]
    Param (
        [parameter(ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance[]]
        $SqlInstance,

        [Alias("Credential")]
        [PsCredential]
        $SqlCredential,

        [object]$Database,

        [Parameter(Mandatory = $true, Position = 0, ParameterSetName = "Query")]
        [string]
        $Query,

        [Int32]
        $QueryTimeout = 600,

        [Parameter(Mandatory = $true, ParameterSetName = "File")]
        [object[]]
        $File,

        [Parameter(Mandatory = $true, ParameterSetName = "SMO")]
        [Microsoft.SqlServer.Management.Smo.SqlSmoObject[]]
        $SqlObject,

        [ValidateSet("DataSet", "DataTable", "DataRow", "PSObject", "SingleValue")]
        [string]
        $As = "DataRow",

        [System.Collections.IDictionary]
        $SqlParameters,

        [switch]
        $AppendServerInstance,

        [switch]
        $MessagesToOutput,

        [parameter(ValueFromPipeline = $true)]
        [Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,

        [Alias('Silent')]
        [switch]
        $EnableException

    )

    begin {
        Write-Message -Level Debug -Message "Bound parameters: $($PSBoundParameters.Keys -join ", ")"

        $splatInvokeDbaSqlAsync = @{
            As      = $As
        }

        if (Test-Bound -ParameterName "SqlParameters") {
            $splatInvokeDbaSqlAsync["SqlParameters"] = $SqlParameters
        }
        if (Test-Bound -ParameterName "AppendServerInstance") {
            $splatInvokeDbaSqlAsync["AppendServerInstance"] = $AppendServerInstance
        }
        if (Test-Bound -ParameterName "Query") {
            $splatInvokeDbaSqlAsync["Query"] = $Query
        }
        if (Test-Bound -ParameterName "QueryTimeout") {
            $splatInvokeDbaSqlAsync["QueryTimeout"] = $QueryTimeout
        }
        if (Test-Bound -ParameterName "MessagesToOutput") {
            $splatInvokeDbaSqlAsync["MessagesToOutput"] = $MessagesToOutput
        }
        if (Test-Bound -ParameterName "Verbose") {
            $splatInvokeDbaSqlAsync["Verbose"] = $Verbose
        }


        if (Test-Bound -ParameterName "File") {
            $files = @()
            $temporaryFiles = @()
            $temporaryFilesCount = 0
            $temporaryFilesPrefix = (97 .. 122 | Get-Random -Count 10 | ForEach-Object { [char]$_ }) -join ''

            foreach ($item in $File) {
                if ($null -eq $item) { continue }

                $type = $item.GetType().FullName

                switch ($type) {
                    "System.IO.DirectoryInfo" {
                        if (-not $item.Exists) {
                            Stop-Function -Message "Directory not found!" -Category ObjectNotFound
                            return
                        }
                        $files += ($item.GetFiles() | Where-Object Extension -EQ ".sql").FullName

                    }
                    "System.IO.FileInfo" {
                        if (-not $item.Exists) {
                            Stop-Function -Message "Directory not found!" -Category ObjectNotFound
                            return
                        }

                        $files += $item.FullName
                    }
                    "System.String" {
                        $uri = [uri]$item

                        switch -regex ($uri.Scheme) {
                            "http" {
                                $tempfile = "$env:TEMP\$temporaryFilesPrefix-$temporaryFilesCount.sql"
                                try {
                                    Invoke-WebRequest -Uri $item -OutFile $tempfile -ErrorAction Stop
                                    $files += $tempfile
                                    $temporaryFilesCount++
                                    $temporaryFiles += $tempfile
                                }
                                catch {
                                    Stop-Function -Message "Failed to download file $item" -ErrorRecord $_
                                    return
                                }
                            }
                            default {
                                try {
                                    $paths = Resolve-Path $item | Select-Object -ExpandProperty Path | Get-Item -ErrorAction Stop
                                }
                                catch {
                                    Stop-Function -Message "Failed to resolve path: $item" -ErrorRecord $_
                                    return
                                }

                                foreach ($path in $paths) {
                                    if (-not $path.PSIsContainer) {
                                        if (([uri]$path.FullName).Scheme -ne 'file') {
                                            Stop-Function -Message "Could not resolve path $path as filesystem object"
                                            return
                                        }
                                        $files += $path.FullName
                                    }
                                }
                            }
                        }
                    }
                    default {
                        Stop-Function -Message "Unkown input type: $type" -Category InvalidArgument
                        return
                    }
                }
            }
        }

        if (Test-Bound -ParameterName "SqlObject") {
            $files = @()
            $temporaryFiles = @()
            $temporaryFilesCount = 0
            $temporaryFilesPrefix = (97 .. 122 | Get-Random -Count 10 | ForEach-Object { [char]$_ }) -join ''

            foreach ($object in $SqlObject) {
                try { $code = Export-DbaScript -InputObject $object -Passthru -EnableException }
                catch {
                    Stop-Function -Message "Failed to generate script for object $object" -ErrorRecord $_
                    return
                }

                try {
                    $newfile = "$env:TEMP\$temporaryFilesPrefix-$temporaryFilesCount.sql"
                    Set-Content -Value $code -Path $newfile -Force -ErrorAction Stop -Encoding UTF8
                    $files += $newfile
                    $temporaryFilesCount++
                    $temporaryFiles += $newfile
                }
                catch {
                    Stop-Function -Message "Failed to write sql script to temp" -ErrorRecord $_
                    return
                }
            }
        }
    }

    process {
        if (Test-FunctionInterrupt) { return }
        if (Test-Bound -ParameterName "Database", "InputObject" -And) {
            Stop-Function -Category InvalidArgument -Message "You can't use -Database with piped databases"
            return
        }
        if (Test-Bound -ParameterName "SqlInstance", "InputObject" -And) {
            Stop-Function -Category InvalidArgument -Message "You can't use -SqlInstance with piped databases"
            return
        }

        foreach ($db in $InputObject) {
            if (!$db.IsAccessible) {
                Write-Message -Level Warning -Message "Database $db is not accessible. Skipping."
                continue
            }
            $server = $db.Parent
            $conncontext = $server.ConnectionContext
            if ($conncontext.DatabaseName -ne $db.Name) {
                $conncontext = $server.ConnectionContext.Copy()
                $conncontext.DatabaseName = $db.Name
            }
            try {
                if ($File -or $SqlObject) {
                    foreach ($item in $files) {
                        if ($null -eq $item) {continue}
                        $filePath = $(Resolve-Path -LiteralPath $item).ProviderPath
                        $QueryfromFile = [System.IO.File]::ReadAllText("$filePath")
                        Invoke-DbaSqlAsync -SQLConnection $conncontext @splatInvokeDbaSqlAsync -Query $QueryfromFile
                    }
                }
                else { Invoke-DbaSqlAsync -SQLConnection $conncontext @splatInvokeDbaSqlAsync }
            }
            catch {
                Stop-Function -Message "[$db] Failed during execution" -ErrorRecord $_ -Target $server -Continue
            }
        }
        foreach ($instance in $SqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_ -Target $instance -Continue
            }
            $conncontext = $server.ConnectionContext
            try {
                if ($Database -and $conncontext.DatabaseName -ne $Database) {
                    $conncontext = $server.ConnectionContext.Copy()
                    $conncontext.DatabaseName = $Database
                }
                if ($File -or $SqlObject) {
                    foreach ($item in $files) {
                        if ($null -eq $item) {continue}
                        $filePath = $(Resolve-Path -LiteralPath $item).ProviderPath
                        $QueryfromFile = [System.IO.File]::ReadAllText("$filePath")
                        Invoke-DbaSqlAsync -SQLConnection $conncontext @splatInvokeDbaSqlAsync -Query $QueryfromFile
                    }
                }
                else {
                    Invoke-DbaSqlAsync -SQLConnection $conncontext @splatInvokeDbaSqlAsync
                }
            }
            catch {
                Stop-Function -Message "[$instance] Failed during execution" -ErrorRecord $_ -Target $instance -Continue
            }
        }
    }

    end {
        # Execute end even when interrupting, as only used for cleanup

        if ($temporaryFiles) {
            # Clean up temporary files that were downloaded
            foreach ($item in $temporaryFiles) {
                Remove-Item -Path $item -ErrorAction Ignore
            }
        }
        Test-DbaDeprecation -DeprecatedOn '1.0.0' -Alias Invoke-DbaSqlCmd
    }
}
function Invoke-DbaWhoIsActive {
    <#
        .SYNOPSIS
            Outputs results of Adam Machanic's sp_WhoIsActive DataTable

        .DESCRIPTION
            Output results of Adam Machanic's sp_WhoIsActive

            This command was built with Adam's permission. To read more about sp_WhoIsActive, please visit:

            Updates: http://sqlblog.com/blogs/adam_machanic/archive/tags/who+is+active/default.aspx

            Also, consider donating to Adam if you find this stored procedure helpful: http://tinyurl.com/WhoIsActiveDonate

        .PARAMETER SqlInstance
            The SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER Database
            The database where sp_WhoIsActive is installed. Defaults to master. If the sp_WhoIsActive is not installed, the command will warn and exit.

        .PARAMETER Filter
            FiltersBoth inclusive and exclusive
            Set either filter to '' to disable
            Session is a session ID, and either 0 or '' can be used to indicate "all" sessions
            All other filter types support % or _ as wildcards

        .PARAMETER FilterType
            Valid filter types are: session, program, database, login, and host

        .PARAMETER NotFilter
            FiltersBoth inclusive and exclusive
            Set either filter to '' to disable
            Session is a session ID, and either 0 or '' can be used to indicate "all" sessions
            All other filter types support % or _ as wildcards

        .PARAMETER NotFilterType
            Valid filter types are: session, program, database, login, and host

        .PARAMETER ShowOwnSpid
            Retrieve data about the calling session?

        .PARAMETER ShowSystemSpids
            Retrieve data about system sessions?

        .PARAMETER ShowSleepingSpids
            Controls how sleeping SPIDs are handled, based on the idea of levels of interest
            0 does not pull any sleeping SPIDs
            1 pulls only those sleeping SPIDs that also have an open transaction
            2 pulls all sleeping SPIDs

        .PARAMETER GetFullInnerText
            If 1, gets the full stored procedure or running batch, when available
            If 0, gets only the actual statement that is currently running in the batch or procedure

        .PARAMETER GetPlans
            Get associated query plans for running tasks, if available
            If 1, gets the plan based on the request's statement offset
            If 2, gets the entire plan based on the request's plan_handle

        .PARAMETER GetOuterCommand
            Get the associated outer ad hoc query or stored procedure call, if available

        .PARAMETER GetTransactionInfo
            Enables pulling transaction log write info and transaction duration

        .PARAMETER GetTaskInfo
            Get information on active tasks, based on three interest levels
            Level 0 does not pull any task-related information
            Level 1 is a lightweight mode that pulls the top non-CXPACKET wait, giving preference to blockers
            Level 2 pulls all available task-based metrics, including:
            number of active tasks, current wait stats, physical I/O, context switches, and blocker information

        .PARAMETER GetLocks
            Gets associated locks for each request, aggregated in an XML format

        .PARAMETER GetAverageTime
            Get average time for past runs of an active query
            (based on the combination of plan handle, sql handle, and offset)

        .PARAMETER GetAdditonalInfo
            Get additional non-performance-related information about the session or request
            text_size, language, date_format, date_first, quoted_identifier, arithabort, ansi_null_dflt_on,
            ansi_defaults, ansi_warnings, ansi_padding, ansi_nulls, concat_null_yields_null,
            transaction_isolation_level, lock_timeout, deadlock_priority, row_count, command_type

            If a SQL Agent job is running, an subnode called agent_info will be populated with some or all of
            the following: job_id, job_name, step_id, step_name, msdb_query_error (in the event of an error)

            If @get_task_info is set to 2 and a lock wait is detected, a subnode called block_info will be
            populated with some or all of the following: lock_type, database_name, object_id, file_id, hobt_id,
            applock_hash, metadata_resource, metadata_class_id, object_name, schema_name

        .PARAMETER FindBlockLeaders
            Walk the blocking chain and count the number of
            total SPIDs blocked all the way down by a given session
            Also enables task_info Level 1, if @get_task_info is set to 0

        .PARAMETER DeltaInterval
            Pull deltas on various metrics
            Interval in seconds to wait before doing the second data pull

        .PARAMETER OutputColumnList
            List of desired output columns, in desired order
            Note that the final output will be the intersection of all enabled features and all
            columns in the list. Therefore, only columns associated with enabled features will
            actually appear in the output. Likewise, removing columns from this list may effectively
            disable features, even if they are turned on

            Each element in this list must be one of the valid output column names. Names must be
            delimited by square brackets. White space, formatting, and additional characters are
            allowed, as long as the list contains exact matches of delimited valid column names.

        .PARAMETER SortOrder
            Column(s) by which to sort output, optionally with sort directions.
            Valid column choices:
            session_id, physical_io, reads, physical_reads, writes, tempdb_allocations,
            tempdb_current, CPU, context_switches, used_memory, physical_io_delta,
            reads_delta, physical_reads_delta, writes_delta, tempdb_allocations_delta,
            tempdb_current_delta, CPU_delta, context_switches_delta, used_memory_delta,
            tasks, tran_start_time, open_tran_count, blocking_session_id, blocked_session_count,
            percent_complete, host_name, login_name, database_name, start_time, login_time

            Note that column names in the list must be bracket-delimited. Commas and/or white
            space are not required.

        .PARAMETER FormatOutput
            Formats some of the output columns in a more "human readable" form
            0 disables output format
            1 formats the output for variable-width fonts
            2 formats the output for fixed-width fonts

        .PARAMETER DestinationTable
            If set to a non-blank value, the script will attempt to insert into the specified destination table. Please note that the script will not verify that the table exists, or that it has the correct schema, before doing the insert. Table can be specified in one, two, or three-part format

        .PARAMETER ReturnSchema
            If set to 1, no data collection will happen and no result set will be returned; instead,
            a CREATE TABLE statement will be returned via the @schema parameter, which will match
            the schema of the result set that would be returned by using the same collection of the
            rest of the parameters. The CREATE TABLE statement will have a placeholder token of
            <table_name> in place of an actual table name.

        .PARAMETER Schema
            If set to 1, no data collection will happen and no result set will be returned; instead,
            a CREATE TABLE statement will be returned via the @schema parameter, which will match
            the schema of the result set that would be returned by using the same collection of the
            rest of the parameters. The CREATE TABLE statement will have a placeholder token of
            <table_name> in place of an actual table name.

        .PARAMETER Help
            Help! What do I do?

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: AdamMechanic, WhoIsActive, SpWhoIsActive
            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Invoke-DbaWhoIsActive

        .EXAMPLE
            Invoke-DbaWhoIsActive -SqlInstance sqlserver2014a

            Execute sp_whoisactive on sqlserver2014a. This command expects sp_WhoIsActive to be in the master database. Logs into the SQL Server with Windows credentials.

        .EXAMPLE
            Invoke-DbaWhoIsActive -SqlInstance sqlserver2014a -SqlCredential $credential -Database dbatools

            Execute sp_whoisactive on sqlserver2014a. This command expects sp_WhoIsActive to be in the dbatools database. Logs into the SQL Server with SQL Authentication.

        .EXAMPLE
            Invoke-DbaWhoIsActive -SqlInstance sqlserver2014a -GetAverageTime

            Similar to running sp_WhoIsActive @get_avg_time

        .EXAMPLE
            Invoke-DbaWhoIsActive -SqlInstance sqlserver2014a -GetOuterCommand -FindBlockLeaders

            Similar to running sp_WhoIsActive @get_outer_command = 1, @find_block_leaders = 1
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias('ServerInstance', 'SqlServer')]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]
        $SqlCredential,
        [object]$Database,
        [Alias('As')]
        [ValidateLength(0, 128)]
        [string]$Filter,
        [ValidateSet('Session', 'Program', 'Database', 'Login', 'Host')]
        [string]$FilterType = 'Session',
        [ValidateLength(0, 128)]
        [string]$NotFilter,
        [ValidateSet('Session', 'Program', 'Database', 'Login', 'Host')]
        [string]$NotFilterType = 'Session',
        [switch]$ShowOwnSpid,
        [switch]$ShowSystemSpids,
        [ValidateRange(0, 255)]
        [int]$ShowSleepingSpids,
        [switch]$GetFullInnerText,
        [ValidateRange(0, 255)]
        [int]$GetPlans,
        [switch]$GetOuterCommand,
        [switch]$GetTransactionInfo,
        [ValidateRange(0, 2)]
        [int]$GetTaskInfo,
        [switch]$GetLocks,
        [switch]$GetAverageTime,
        [switch]$GetAdditonalInfo,
        [switch]$FindBlockLeaders,
        [ValidateRange(0, 255)]
        [int]$DeltaInterval,
        [ValidateLength(0, 8000)]
        [string]$OutputColumnList = '[dd%][session_id][sql_text][sql_command][login_name][wait_info][tasks][tran_log%][cpu%][temp%][block%][reads%][writes%][context%][physical%][query_plan][locks][%]',
        [ValidateLength(0, 500)]
        [string]$SortOrder = '[start_time] ASC',
        [ValidateRange(0, 255)]
        [int]$FormatOutput = 1,
        [ValidateLength(0, 4000)]
        [string]$DestinationTable = '',
        [switch]$ReturnSchema,
        [string]$Schema,
        [switch]$Help,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $passedparams = $psboundparameters.Keys | Where-Object { 'Silent', 'SqlServer', 'SqlCredential', 'OutputAs', 'ServerInstance', 'SqlInstance', 'Database' -notcontains $_ }
        $localparams = $psboundparameters
    }

    process {

        foreach ($instance in $sqlinstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($server.VersionMajor -lt 9) {
                throw "sp_WhoIsActive is only supported in SQL Server 2005 and above"
            }

            $paramdictionary = @{
                Filter             = '@filter'
                FilterType         = '@filter_type'
                NotFilter          = 'not_filter'
                NotFilterType      = '@not_filter_type'
                ShowOwnSpid        = '@show_own_spid'
                ShowSystemSpids    = '@show_system_spids'
                ShowSleepingSpids  = '@show_sleeping_spids'
                GetFullInnerText   = '@get_full_inner_text'
                GetPlans           = '@get_plans'
                GetOuterCommand    = '@get_outer_command'
                GetTransactionInfo = '@get_transaction_info'
                GetTaskInfo        = '@get_task_info'
                GetLocks           = '@get_locks '
                GetAverageTime     = '@get_avg_time'
                GetAdditonalInfo   = '@get_additional_info'
                FindBlockLeaders   = '@find_block_leaders'
                DeltaInterval      = '@delta_interval'
                OutputColumnList   = '@output_column_list'
                SortOrder          = '@sort_order'
                FormatOutput       = '@format_output '
                DestinationTable   = '@destination_table '
                ReturnSchema       = '@return_schema'
                Schema             = '@schema'
                Help               = '@help'
            }

            Write-Message -Level Verbose -Message "Collecting sp_whoisactive data from server: $instance"

            try {
                $sqlconnection = New-Object System.Data.SqlClient.SqlConnection
                $sqlconnection.ConnectionString = $server.ConnectionContext.ConnectionString
                $sqlconnection.Open()

                if ($Database) {
                    # database is being returned as something weird. change it to string without using a method then trim.
                    $Database = "$Database"
                    $Database = $Database.Trim()
                    $sqlconnection.ChangeDatabase($Database)
                }

                $sqlcommand = New-Object System.Data.SqlClient.SqlCommand
                $sqlcommand.CommandType = "StoredProcedure"
                $sqlcommand.CommandText = "dbo.sp_WhoIsActive"
                $sqlcommand.Connection = $sqlconnection

                foreach ($param in $passedparams) {
                    Write-Message -Level Verbose -Message "Check parameter '$param'"

                    $sqlparam = $paramdictionary[$param]

                    if ($sqlparam) {

                        $value = $localparams[$param]

                        switch ($value) {
                            $true { $value = 1 }
                            $false { $value = 0 }
                        }
                        Write-Message -Level Verbose -Message "Adding parameter '$sqlparam' with value '$value'"
                        [Void]$sqlcommand.Parameters.AddWithValue($sqlparam, $value)
                    }
                }

                $datatable = New-Object system.Data.DataSet
                $dataadapter = New-Object system.Data.SqlClient.SqlDataAdapter($sqlcommand)
                $dataadapter.fill($datatable) | Out-Null
                $datatable.Tables.Rows
            }
            catch {
                if ($_.Exception.InnerException -Like "*Could not find*") {
                    Stop-Function -Message "sp_whoisactive not found, please install using Install-DbaWhoIsActive." -Continue
                }
                else {
                    Stop-Function -Message "Invalid query." -Continue
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Show-SqlWhoIsActive -CustomMessage "Show-SqlWhoIsActive is no longer supported. Use Invoke-DbaWhoIsActive | Out-GridView for similar results."
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Invoke-DbaXeReplay {
    <#
        .SYNOPSIS
            This command replays events from Read-DbaXEFile on one or more target servers

        .DESCRIPTION
            This command replays events from Read-DbaXEFile. It is simplistic in its approach.

            - Writes all queries to a temp sql file
            - Executes temp file using . $sqlcmd so that batches are executed properly
            - Deletes temp file

        .PARAMETER SqlInstance
            Target SQL Server(s)

        .PARAMETER SqlCredential
            Used to provide alternative credentials.

        .PARAMETER Database
            The initial starting database.

        .PARAMETER Event
            Each Response can be limited to processing specific events, while ignoring all the other ones. When this attribute is omitted, all events are processed.

        .PARAMETER Raw
            By dafault, the results of . $sqlcmd are collected, cleaned up and displayed. If you'd like to see all results immeidately, use Raw.

        .PARAMETER InputObject
            Accepts the object output of Read-DbaXESession.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            Read-DbaXEFile -Path C:\temp\sample.xel | Invoke-DbaXeReplay -SqlInstance sql2017

            Runs all batch_text for sql_batch_completed against tempdb on sql2017.

        .EXAMPLE
            Read-DbaXEFile -Path C:\temp\sample.xel | Invoke-DbaXeReplay -SqlInstance sql2017 -Database planning -Event sql_batch_completed

            Sets the *initial* database to planning then runs only sql_batch_completed against sql2017.

        .EXAMPLE
            Read-DbaXEFile -Path C:\temp\sample.xel | Invoke-DbaXeReplay -SqlInstance sql2017, sql2016

            Runs all batch_text for sql_batch_completed against tempdb on sql2017 and sql2016.

    #>
    Param (
        [Parameter(Mandatory)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance[]]$SqlInstance,
        [Alias("Credential")]
        [PsCredential]$SqlCredential,
        [string[]]$Database,
        [string[]]$Event = @('sql_batch_completed', 'rcp_completed'),
        [Parameter(Mandatory, ValueFromPipeline)]
        [object]$InputObject,
        [switch]$Raw,
        [switch]$EnableException
    )

    begin {
        $querycolumns = 'statement', 'batch_text'
        $timestamp = (Get-Date -Format yyyyMMddHHmm)
        $temp = ([System.IO.Path]::GetTempPath()).TrimEnd("\")
        $filename = "$temp\dbatools-replay-$timestamp.sql"
        Set-Content $filename -Value $null

        $sqlcmd = "$script:PSModuleRoot\bin\sqlcmd\sqlcmd.exe"
    }
    process {
        if (Test-FunctionInterrupt) { return }
        if ($InputObject.Name -notin $Event) {
            continue
        }

        if ($InputObject.statement) {
            if ($InputObject.statement -notmatch "ALTER EVENT SESSION") {
                Add-Content -Path $filename -Value $InputObject.statement
                Add-Content -Path $filename -Value "GO"
            }
        }
        else {
            if ($InputObject.batch_text -notmatch "ALTER EVENT SESSION") {
                Add-Content -Path $filename -Value $InputObject.batch_text
                Add-Content -Path $filename -Value "GO"
            }
        }
    }
    end {
        if (Test-FunctionInterrupt) { return }
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level VeryVerbose -Message "Connecting to $instance." -Target $instance
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_ -Target $instance -Continue
            }


            if ($Raw) {
                if (Test-Bound -ParameterName SqlCredential) {
                    . $sqlcmd -S $instance -i $filename -U $SqlCredential.Username -P $SqlCredential.GetNetworkCredential().Password
                    continue
                }
                else {
                    . $sqlcmd -S $instance -i $filename
                    continue
                }
            }

            if (Test-Bound -ParameterName SqlCredential) {
                $output = . $sqlcmd -S $instance -i $filename -U $SqlCredential.Username -P $SqlCredential.GetNetworkCredential().Password
            }
            else {
                $output = . $sqlcmd -S $instance -i $filename
            }

            foreach ($line in $output) {
                $newline = $line.Trim()
                if ($newline -and $newline -notmatch "------------------------------------------------------------------------------------") {
                    "$newline"
                }
            }
        }
        Remove-Item -Path $filename -ErrorAction Ignore
    }
}
function Invoke-Sqlcmd2 {
    <#
        .SYNOPSIS
            Runs a T-SQL script.

        .DESCRIPTION
            Runs a T-SQL script. Invoke-Sqlcmd2 runs the whole script and only captures the first selected result set, such as the output of PRINT statements when -verbose parameter is specified.
            Parameterized queries are supported.

            Help details below borrowed from Invoke-Sqlcmd

        .PARAMETER ServerInstance
            Specifies the SQL Server instance(s) to execute the query against.

        .PARAMETER Database
            Specifies the name of the database to execute the query against. If specified, this database will be used in the ConnectionString when establishing the connection to SQL Server.

            If a SQLConnection is provided, the default database for that connection is overridden with this database.

        .PARAMETER Query
            Specifies one or more queries to be run. The queries can be Transact-SQL, XQuery statements, or sqlcmd commands. Multiple queries in a single batch may be separated by a semicolon.

            Do not specify the sqlcmd GO separator (or, use the ParseGo parameter). Escape any double quotation marks included in the string.

            Consider using bracketed identifiers such as [MyTable] instead of quoted identifiers such as "MyTable".

        .PARAMETER InputFile
            Specifies the full path to a file to be used as the query input to Invoke-Sqlcmd2. The file can contain Transact-SQL statements, XQuery statements, sqlcmd commands and scripting variables.

        .PARAMETER Credential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

            SECURITY NOTE: If you use the -Debug switch, the connectionstring including plain text password will be sent to the debug stream.

        .PARAMETER Encrypt
            If this switch is enabled, the connection to SQL Server will be made using SSL.

            This requires that the SQL Server has been set up to accept SSL requests. For information regarding setting up SSL on SQL Server, see https://technet.microsoft.com/en-us/library/ms189067(v=sql.105).aspx

        .PARAMETER QueryTimeout
            Specifies the number of seconds before the queries time out.

        .PARAMETER ConnectionTimeout
            Specifies the number of seconds before Invoke-Sqlcmd2 times out if it cannot successfully connect to an instance of the Database Engine. The timeout value must be an integer between 0 and 65534. If 0 is specified, connection attempts do not time out.

        .PARAMETER As
            Specifies output type. Valid options for this parameter are 'DataSet', 'DataTable', 'DataRow', 'PSObject', and 'SingleValue'

            PSObject output introduces overhead but adds flexibility for working with results: http://powershell.org/wp/forums/topic/dealing-with-dbnull/

        .PARAMETER SqlParameters
            Specifies a hashtable of parameters for parameterized SQL queries.  http://blog.codinghorror.com/give-me-parameterized-sql-or-give-me-death/

            Example:

        .PARAMETER AppendServerInstance
            If this switch is enabled, the SQL Server instance will be appended to PSObject and DataRow output.

        .PARAMETER ParseGo
            If this switch is enabled, "GO" statements will be handled automatically.
            Every "GO" will effectively run in a separate query, like if you issued multiple Invoke-SqlCmd2 commands.
            "GO"s will be recognized if they are on a single line, as this covers
            the 95% of the cases "GO" parsing is needed
            Note:
                Queries will always target that database, e.g. if you have this Query:
                    USE DATABASE [dbname]
                    GO
                    SELECT * from sys.tables
                and you call it via
                    Invoke-SqlCmd2 -ServerInstance instance -Database msdb -Query ...
                you'll get back tables from msdb, not dbname.


        .PARAMETER SQLConnection
            Specifies an existing SQLConnection object to use in connecting to SQL Server. If the connection is closed, an attempt will be made to open it.

        .INPUTS
            None
                You cannot pipe objects to Invoke-Sqlcmd2

        .OUTPUTS
        As PSObject:     System.Management.Automation.PSCustomObject
        As DataRow:      System.Data.DataRow
        As DataTable:    System.Data.DataTable
        As DataSet:      System.Data.DataTableCollectionSystem.Data.DataSet
        As SingleValue:  Dependent on data type in first column.

        .EXAMPLE
            Invoke-Sqlcmd2 -ServerInstance "MyComputer\MyInstance" -Query "SELECT login_time AS 'StartTime' FROM sysprocesses WHERE spid = 1"

            Connects to a named instance of the Database Engine on a computer and runs a basic T-SQL query.

            StartTime
            -----------
            2010-08-12 21:21:03.593

        .EXAMPLE
            Invoke-Sqlcmd2 -ServerInstance "MyComputer\MyInstance" -InputFile "C:\MyFolder\tsqlscript.sql" | Out-File -filePath "C:\MyFolder\tsqlscript.rpt"

            Reads a file containing T-SQL statements, runs the file, and writes the output to another file.

        .EXAMPLE
            Invoke-Sqlcmd2  -ServerInstance "MyComputer\MyInstance" -Query "PRINT 'hello world'" -Verbose

            Uses the PowerShell -Verbose parameter to return the message output of the PRINT command.
            VERBOSE: hello world

        .EXAMPLE
            Invoke-Sqlcmd2 -ServerInstance MyServer\MyInstance -Query "SELECT ServerName, VCNumCPU FROM tblServerInfo" -as PSObject | ?{$_.VCNumCPU -gt 8}
            Invoke-Sqlcmd2 -ServerInstance MyServer\MyInstance -Query "SELECT ServerName, VCNumCPU FROM tblServerInfo" -as PSObject | ?{$_.VCNumCPU}

            This example uses the PSObject output type to allow more flexibility when working with results.

            If we used DataRow rather than PSObject, we would see the following behavior:
                Each row where VCNumCPU does not exist would produce an error in the first example
                Results would include rows where VCNumCPU has DBNull value in the second example

        .EXAMPLE
            'Instance1', 'Server1/Instance1', 'Server2' | Invoke-Sqlcmd2 -query "Sp_databases" -as psobject -AppendServerInstance

            This example lists databases for each instance.  It includes a column for the ServerInstance in question.
                DATABASE_NAME          DATABASE_SIZE REMARKS        ServerInstance
                -------------          ------------- -------        --------------
                REDACTED                       88320                Instance1
                master                         17920                Instance1
                ...
                msdb                          618112                Server1/Instance1
                tempdb                        563200                Server1/Instance1
                ...
                OperationsManager           20480000                Server2

        .EXAMPLE
            #Construct a query using SQL parameters
                $Query = "SELECT ServerName, VCServerClass, VCServerContact FROM tblServerInfo WHERE VCServerContact LIKE @VCServerContact AND VCServerClass LIKE @VCServerClass"

            #Run the query, specifying values for SQL parameters
                Invoke-Sqlcmd2 -ServerInstance SomeServer\NamedInstance -Database ServerDB -query $query -SqlParameters @{ VCServerContact="%cookiemonster%"; VCServerClass="Prod" }

                ServerName    VCServerClass VCServerContact
                ----------    ------------- ---------------
                SomeServer1   Prod          cookiemonster, blah
                SomeServer2   Prod          cookiemonster
                SomeServer3   Prod          blah, cookiemonster

        .EXAMPLE
            Invoke-Sqlcmd2 -SQLConnection $Conn -Query "SELECT login_time AS 'StartTime' FROM sysprocesses WHERE spid = 1"

            Uses an existing SQLConnection and runs a basic T-SQL query against it

            StartTime
            -----------
            2010-08-12 21:21:03.593

        .EXAMPLE
            Invoke-SqlCmd -SQLConnection $Conn -Query "SELECT ServerName FROM tblServerInfo WHERE ServerName LIKE @ServerName" -SqlParameters @{"ServerName = "c-is-hyperv-1"}

            Executes a parameterized query against the existing SQLConnection, with a collection of one parameter to be passed to the query when executed.

        .NOTES
            Changelog moved to CHANGELOG.md:

            https://github.com/sqlcollaborative/Invoke-SqlCmd2/blob/master/CHANGELOG.md

        .LINK
            https://github.com/sqlcollaborative/Invoke-SqlCmd2

        .LINK
            https://github.com/RamblingCookieMonster/PowerShell

        .FUNCTIONALITY
            SQL
    #>

    [CmdletBinding(DefaultParameterSetName = 'Ins-Que')]
    [OutputType([System.Management.Automation.PSCustomObject], [System.Data.DataRow], [System.Data.DataTable], [System.Data.DataTableCollection], [System.Data.DataSet])]
    param (
        [Parameter(ParameterSetName = 'Ins-Que',
            Position = 0,
            Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false,
            HelpMessage = 'SQL Server Instance required...')]
        [Parameter(ParameterSetName = 'Ins-Fil',
            Position = 0,
            Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false,
            HelpMessage = 'SQL Server Instance required...')]
        [Alias('Instance', 'Instances', 'ComputerName', 'Server', 'Servers', 'SqlInstance')]
        [ValidateNotNullOrEmpty()]
        [string[]]$ServerInstance,
        [Parameter(Position = 1,
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false)]
        [string]$Database,
        [Parameter(ParameterSetName = 'Ins-Que',
            Position = 2,
            Mandatory = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false)]
        [Parameter(ParameterSetName = 'Con-Que',
            Position = 2,
            Mandatory = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false)]
        [string]$Query,
        [Parameter(ParameterSetName = 'Ins-Fil',
            Position = 2,
            Mandatory = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false)]
        [Parameter(ParameterSetName = 'Con-Fil',
            Position = 2,
            Mandatory = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false)]
        [ValidateScript( { Test-Path -LiteralPath $_ })]
        [string]$InputFile,
        [Parameter(ParameterSetName = 'Ins-Que',
            Position = 3,
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false)]
        [Parameter(ParameterSetName = 'Ins-Fil',
            Position = 3,
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false)]
        [Alias('SqlCredential')]
        [System.Management.Automation.PSCredential]$Credential,
        [Parameter(ParameterSetName = 'Ins-Que',
            Position = 4,
            Mandatory = $false,
            ValueFromRemainingArguments = $false)]
        [Parameter(ParameterSetName = 'Ins-Fil',
            Position = 4,
            Mandatory = $false,
            ValueFromRemainingArguments = $false)]
        [switch]$Encrypt,
        [Parameter(Position = 5,
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false)]
        [Int32]$QueryTimeout = 600,
        [Parameter(ParameterSetName = 'Ins-Fil',
            Position = 6,
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false)]
        [Parameter(ParameterSetName = 'Ins-Que',
            Position = 6,
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false)]
        [Int32]$ConnectionTimeout = 15,
        [Parameter(Position = 7,
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false)]
        [ValidateSet("DataSet", "DataTable", "DataRow", "PSObject", "SingleValue")]
        [string]$As = "DataRow",
        [Parameter(Position = 8,
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false)]
        [System.Collections.IDictionary]$SqlParameters,
        [Parameter(Position = 9,
            Mandatory = $false)]
        [switch]$AppendServerInstance,
        [Parameter(Position = 10,
            Mandatory = $false)]
        [switch]$ParseGO,
        [Parameter(ParameterSetName = 'Con-Que',
            Position = 11,
            Mandatory = $false,
            ValueFromPipeline = $false,
            ValueFromPipelineByPropertyName = $false,
            ValueFromRemainingArguments = $false)]
        [Parameter(ParameterSetName = 'Con-Fil',
            Position = 11,
            Mandatory = $false,
            ValueFromPipeline = $false,
            ValueFromPipelineByPropertyName = $false,
            ValueFromRemainingArguments = $false)]
        [Alias('Connection', 'Conn')]
        [ValidateNotNullOrEmpty()]
        [System.Data.SqlClient.SQLConnection]$SQLConnection
    )

    begin {
        if ($InputFile) {
            $filePath = $(Resolve-Path -LiteralPath $InputFile).ProviderPath
            $Query = [System.IO.File]::ReadAllText("$filePath")
        }

        Write-Verbose "Running Invoke-Sqlcmd2 with ParameterSet '$($PSCmdlet.ParameterSetName)'.  Performing query '$Query'."

        if ($As -eq "PSObject") {
            #This code scrubs DBNulls.  Props to Dave Wyatt
            $cSharp = @'
                using System;
                using System.Data;
                using System.Management.Automation;

                public class DBNullScrubber
                {
                    public static PSObject DataRowToPSObject(DataRow row)
                    {
                        PSObject psObject = new PSObject();

                        if (row != null && (row.RowState & DataRowState.Detached) != DataRowState.Detached)
                        {
                            foreach (DataColumn column in row.Table.Columns)
                            {
                                Object value = null;
                                if (!row.IsNull(column))
                                {
                                    value = row[column];
                                }

                                psObject.Properties.Add(new PSNoteProperty(column.ColumnName, value));
                            }
                        }

                        return psObject;
                    }
                }
'@

            try {
                Add-Type -TypeDefinition $cSharp -ReferencedAssemblies 'System.Data', 'System.Xml' -ErrorAction stop
            }
            catch {
                if (-not $_.ToString() -like "*The type name 'DBNullScrubber' already exists*") {
                    Write-Warning "Could not load DBNullScrubber.  Defaulting to DataRow output: $_."
                    $As = "Datarow"
                }
            }
        }

        #Handle existing connections
        if ($PSBoundParameters.ContainsKey('SQLConnection')) {
            if ($SQLConnection.State -notlike "Open") {
                try {
                    Write-Verbose "Opening connection from '$($SQLConnection.State)' state."
                    $SQLConnection.Open()
                }
                catch {
                    throw $_
                }
            }

            if ($Database -and $SQLConnection.Database -notlike $Database) {
                try {
                    Write-Verbose "Changing SQLConnection database from '$($SQLConnection.Database)' to $Database."
                    $SQLConnection.ChangeDatabase($Database)
                }
                catch {
                    throw "Could not change Connection database '$($SQLConnection.Database)' to $Database`: $_"
                }
            }

            if ($SQLConnection.state -like "Open") {
                $ServerInstance = @($SQLConnection.DataSource)
            }
            else {
                throw "SQLConnection is not open"
            }
        }
        $GoSplitterRegex = [regex]'(?smi)^[\s]*GO[\s]*$'

    }
    process {
        foreach ($SQLInstance in $ServerInstance) {
            Write-Verbose "Querying ServerInstance '$SQLInstance'"

            if ($PSBoundParameters.Keys -contains "SQLConnection") {
                $Conn = $SQLConnection
            }
            else {
                $CSBuilder = New-Object -TypeName System.Data.SqlClient.SqlConnectionStringBuilder
                $CSBuilder["Server"] = $SQLInstance
                $CSBuilder["Database"] = $Database
                $CSBuilder["Connection Timeout"] = $ConnectionTimeout

                if ($Encrypt) {
                    $CSBuilder["Encrypt"] = $true
                }

                if ($Credential) {
                    $CSBuilder["Trusted_Connection"] = $false
                    $CSBuilder["User ID"] = $Credential.UserName
                    $CSBuilder["Password"] = $Credential.GetNetworkCredential().Password
                }
                else {
                    $CSBuilder["Integrated Security"] = $true
                }
                if ($ApplicationName) {
                    $CSBuilder["Application Name"] = $ApplicationName
                }
                else {
                    $ScriptName = (Get-PSCallStack)[-1].Command.ToString()
                    if ($ScriptName -ne "<ScriptBlock>") {
                        $CSBuilder["Application Name"] = $ScriptName
                    }
                }
                $conn = New-Object -TypeName System.Data.SqlClient.SQLConnection

                $ConnectionString = $CSBuilder.ToString()
                $conn.ConnectionString = $ConnectionString
                Write-Debug "ConnectionString $ConnectionString"

                try {
                    $conn.Open()
                }
                catch {
                    Write-Error $_
                    continue
                }
            }

            #Following EventHandler is used for PRINT and RAISERROR T-SQL statements. Executed when -Verbose parameter specified by caller
            if ($PSBoundParameters.Verbose) {
                $conn.FireInfoMessageEventOnUserErrors = $false # Shiyang, $true will change the SQL exception to information
                $handler = [System.Data.SqlClient.SqlInfoMessageEventHandler] { Write-Verbose "$($_)" }
                $conn.add_InfoMessage($handler)
            }
            if ($ParseGO) {
                Write-Verbose "Stripping GOs from source"
                $Pieces = $GoSplitterRegex.Split($Query)
            }
            else {
                $Pieces = , $Query
            }
            # Only execute non-empty statements
            $Pieces = $Pieces | Where-Object { $_.Trim().Length -gt 0 }
            foreach ($piece in $Pieces) {
                $cmd = New-Object system.Data.SqlClient.SqlCommand($piece, $conn)
                $cmd.CommandTimeout = $QueryTimeout

                if ($null -ne $SqlParameters) {
                    $SqlParameters.GetEnumerator() |
                        ForEach-Object {
                        if ($null -ne $_.Value) {
                            $cmd.Parameters.AddWithValue($_.Key, $_.Value)
                        }
                        else {
                            $cmd.Parameters.AddWithValue($_.Key, [DBNull]::Value)
                        }
                    } > $null
                }

                $ds = New-Object system.Data.DataSet
                $da = New-Object system.Data.SqlClient.SqlDataAdapter($cmd)

                try {
                    [void]$da.fill($ds)
                }
                catch [System.Data.SqlClient.SqlException] {
                    # For SQL exception

                    $Err = $_

                    Write-Verbose "Capture SQL Error"

                    if ($PSBoundParameters.Verbose) {
                        Write-Verbose "SQL Error:  $Err"
                    } #Shiyang, add the verbose output of exception

                    switch ($ErrorActionPreference.tostring()) {
                        { 'SilentlyContinue', 'Ignore' -contains $_ } {

                        }
                        'Stop' {
                            throw $Err
                        }
                        'Continue' {
                            throw $Err
                        }
                        Default {
                            Throw $Err
                        }
                    }
                }
                catch {
                    # For other exception
                    Write-Verbose "Capture Other Error"

                    $Err = $_

                    if ($PSBoundParameters.Verbose) {
                        Write-Verbose "Other Error:  $Err"
                    }

                    switch ($ErrorActionPreference.tostring()) {
                        { 'SilentlyContinue', 'Ignore' -contains $_ } {

                        }
                        'Stop' {
                            throw $Err
                        }
                        'Continue' {
                            throw $Err
                        }
                        Default {
                            throw $Err
                        }
                    }
                }
                finally {
                    #Close the connection
                    if (-not $PSBoundParameters.ContainsKey('SQLConnection')) {
                        $conn.Close()
                    }
                }

                if ($AppendServerInstance) {
                    #Basics from Chad Miller
                    $Column = New-Object Data.DataColumn
                    $Column.ColumnName = "ServerInstance"

                    if ($ds.Tables.Count -ne 0) {
                        $ds.Tables[0].Columns.Add($Column)
                        Foreach ($row in $ds.Tables[0]) {
                            $row.ServerInstance = $SQLInstance
                        }
                    }
                }

                switch ($As) {
                    'DataSet' {
                        $ds
                    }
                    'DataTable' {
                        $ds.Tables
                    }
                    'DataRow' {
                        if ($ds.Tables.Count -ne 0) {
                            $ds.Tables[0]
                        }
                    }
                    'PSObject' {
                        if ($ds.Tables.Count -ne 0) {
                            #Scrub DBNulls - Provides convenient results you can use comparisons with
                            #Introduces overhead (e.g. ~2000 rows w/ ~80 columns went from .15 Seconds to .65 Seconds - depending on your data could be much more!)
                            foreach ($row in $ds.Tables[0].Rows) {

                                [DBNullScrubber]::DataRowToPSObject($row)
                            }
                        }
                    }
                    'SingleValue' {
                        if ($ds.Tables.Count -ne 0) {
                            $ds.Tables[0] | Select-Object -ExpandProperty $ds.Tables[0].Columns[0].ColumnName
                        }
                    }
                }
            }
        }
    }
} #Invoke-Sqlcmd2
function Measure-DbaBackupThroughput {
    <#
        .SYNOPSIS
            Determines how quickly SQL Server is backing up databases to media.

        .DESCRIPTION
            Returns backup history details for one or more databases on a SQL Server.

            Output looks like this:
            SqlInstance     : sql2016
            Database        : SharePoint_Config
            AvgThroughputMB : 1.07
            AvgSizeMB       : 24.17
            AvgDuration     : 00:00:01.1000000
            MinThroughputMB : 0.02
            MaxThroughputMB : 2.26
            MinBackupDate   : 8/6/2015 10:22:01 PM
            MaxBackupDate   : 6/19/2016 12:57:45 PM
            BackupCount     : 10

        .PARAMETER SqlInstance
            The SQL Server instance.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude. Options for this list are auto-populated from the server.

        .PARAMETER Type
            By default, this command measures the speed of Full backups. Valid options are "Full", "Log" and "Differential".

        .PARAMETER Since
             All backups taken on or after the point in time represented by this datetime object will be processed.

        .PARAMETER Last
            If this switch is enabled, only the last backup will be measured.

        .PARAMETER DeviceType
            Specifies one or more DeviceTypes to use in filtering backup sets. Valid values are "Disk", "Permanent Disk Device", "Tape", "Permanent Tape Device", "Pipe", "Permanent Pipe Device" and "Virtual Device", as well as custom integers for your own DeviceTypes.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Backup, Database
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Measure-DbaBackupThroughput

        .EXAMPLE
            Measure-DbaBackupThroughput -SqlInstance sql2016

            Parses every backup in msdb's backuphistory for stats on all databases.

        .EXAMPLE
            Measure-DbaBackupThroughput -SqlInstance sql2016 -Database AdventureWorks2014

            Parses every backup in msdb's backuphistory for stats on AdventureWorks2014.

        .EXAMPLE
            Measure-DbaBackupThroughput -SqlInstance sql2005 -Last

            Processes the last full, diff and log backups every backup for all databases on sql2005.

        .EXAMPLE
            Measure-DbaBackupThroughput -SqlInstance sql2005 -Last -Type Log

            Processes the last log backups every backup for all databases on sql2005.

        .EXAMPLE
            Measure-DbaBackupThroughput -SqlInstance sql2016 -Since (Get-Date).AddDays(-7)

            Gets backup calculations for the last week.

        .EXAMPLE
            Measure-DbaBackupThroughput -SqlInstance sql2016 -Since (Get-Date).AddDays(-365) -Database bigoldb

            Gets backup calculations, limited to the last year and only the bigoldb database

    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "Instance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [datetime]$Since,
        [switch]$Last,
        [ValidateSet("Full", "Log", "Differential", "File", "Differential File", "Partial Full", "Partial Differential")]
        [string]$Type = "Full",
        [string[]]$DeviceType,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($Database) {
                $DatabaseCollection = $server.Databases | Where-Object Name -in $Database
            }
            else {
                $DatabaseCollection = $server.Databases
            }

            if ($ExcludeDatabase) {
                $DatabaseCollection = $DatabaseCollection | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $DatabaseCollection) {
                Write-Message -Level VeryVerbose -Message "Retrieving history for $db."
                $allhistory = @()

                # Splatting didn't work
                if ($since) {
                    $histories = Get-DbaBackupHistory -SqlInstance $server -Database $db.name -Since $since -DeviceType $DeviceType -Type $Type
                }
                else {
                    $histories = Get-DbaBackupHistory -SqlInstance $server -Database $db.name -Last:$last -DeviceType $DeviceType -Type $Type
                }

                foreach ($history in $histories) {
                    $timetaken = New-TimeSpan -Start $history.Start -End $history.End

                    if ($timetaken.TotalMilliseconds -eq 0) {
                        $throughput = $history.TotalSize.Megabyte
                    }
                    else {
                        $throughput = $history.TotalSize.Megabyte / $timetaken.TotalSeconds
                    }

                    Add-Member -Force -InputObject $history -MemberType Noteproperty -Name MBps -value $throughput

                    $allhistory += $history | Select-Object ComputerName, InstanceName, SqlInstance, Database, MBps, TotalSize, Start, End
                }

                Write-Message -Level VeryVerbose -Message "Calculating averages for $db."
                foreach ($db in ($allhistory | Sort-Object Database | Group-Object Database)) {

                    $measuremb = $db.Group.MBps | Measure-Object -Average -Minimum -Maximum
                    $measurestart = $db.Group.Start | Measure-Object -Minimum
                    $measureend = $db.Group.End | Measure-Object -Maximum
                    $measuresize = $db.Group.TotalSize.Megabyte | Measure-Object -Average
                    $avgduration = $db.Group | ForEach-Object { New-TimeSpan -Start $_.Start -End $_.End } | Measure-Object -Average TotalSeconds

                    [pscustomobject]@{
                        ComputerName    = $db.Group.ComputerName | Select-Object -First 1
                        InstanceName    = $db.Group.InstanceName | Select-Object -First 1
                        SqlInstance     = $db.Group.SqlInstance | Select-Object -First 1
                        Database        = $db.Name
                        AvgThroughputMB = [System.Math]::Round($measuremb.Average, 2)
                        AvgSizeMB       = [System.Math]::Round($measuresize.Average, 2)
                        AvgDuration     = [dbatimespan](New-TimeSpan -Seconds $avgduration.Average)
                        MinThroughputMB = [System.Math]::Round($measuremb.Minimum, 2)
                        MaxThroughputMB = [System.Math]::Round($measuremb.Maximum, 2)
                        MinBackupDate   = [dbadatetime]$measurestart.Minimum
                        MaxBackupDate   = [dbadatetime]$measureend.Maximum
                        BackupCount     = $db.Count
                    } | Select-DefaultView -ExcludeProperty ComputerName, InstanceName
                }
            }
        }
    }
}
function Measure-DbaDiskSpaceRequirement {
    <#
        .SYNOPSIS
            Calculate the space needed to copy and possibly replace a database from one SQL server to another.

        .DESCRIPTION
            Returns a file list from source and destination where source file may overwrite destination. Complex scenarios where a new file may exist is taken into account.
            This command will accept a hash object in pipeline with the following keys: Source, SourceDatabase, Destination. Using this command will provide a way to prepare before a complex migration with multiple databases from different sources and destinations.

        .PARAMETER Source
            Source SQL Server.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database to copy. It MUST exist.

        .PARAMETER Destination
            Destination SQL Server instance.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER DestinationDatabase
            The database name at destination.
            May or may not be present, if unspecified it will default to the database name provided in SourceDatabase.

        .PARAMETER Credential
            The credentials to use to connect via CIM/WMI/PowerShell remoting.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database, DiskSpace, Migration
            Author: Pollus Brodeur (@pollusb)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Measure-DbaDiskSpaceRequirement

        .EXAMPLE
            Measure-DbaDiskSpaceRequirement -Source INSTANCE1 -Database DB1 -Destination INSTANCE2

            Calculate space needed for a simple migration with one database with the same name at destination.

        .EXAMPLE
            @([PSCustomObject]@{Source='SQL1';Destination='SQL2';Database='DB1'},
              [PSCustomObject]@{Source='SQL1';Destination='SQL2';Database='DB2'}
            ) | Measure-DbaDiskSpaceRequirement

            Using a PSCustomObject with 2 databases to migrate on SQL2.

        .EXAMPLE
            Import-Csv -Path .\migration.csv -Delimiter "`t" | Measure-DbaDiskSpaceRequirement | Format-Table -AutoSize

            Using a CSV file. You will need to use this header line "Source<tab>Destination<tab>Database<tab>DestinationDatabase".

        .EXAMPLE
            Invoke-DbaSqlCmd -SqlInstance DBA -Database Migrations -Query 'select Source, Destination, Database from dbo.Migrations' `
                | Measure-DbaDiskSpaceRequirement

            Using a SQL table. We are DBA after all!
    #>
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [DbaInstanceParameter]$Source,
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [string]$Database,
        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)]
        [PSCredential]$SourceSqlCredential,
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [DbaInstanceParameter]$Destination,
        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)]
        [string]$DestinationDatabase,
        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)]
        [PSCredential]$DestinationSqlCredential,
        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)]
        [PSCredential]$Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        $local:cacheMP = @{}
        $local:cacheDP = @{}
        function Get-MountPoint {
            [CmdletBinding()]
            param(
                [Parameter(Mandatory = $true)]
                $computerName,
                [PSCredential]$credential
            )
            Get-DbaCmObject -Class Win32_MountPoint -ComputerName $computerName -Credential $credential |
                Select-Object @{n='Mountpoint';e={$_.Directory.split('=')[1].Replace('"','').Replace('\\','\')}}
        }
        function Get-MountPointFromPath {
            [CmdletBinding()]
            param(
                [Parameter(Mandatory = $true)]
                $path,
                [Parameter(Mandatory = $true)]
                $computerName,
                [PSCredential]$credential
            )
            if (!$cacheMP[$computerName]) {
                try {
                    $cacheMP.Add($computerName, (Get-MountPoint -computerName $computerName -credential $credential))
                    Write-Message -Level Verbose -Message "cacheMP[$computerName] is now cached"
                }
                catch {
                    # This way, I won't be asking again for this computer.
                    $cacheMP.Add($computerName, '?')
                    Stop-Function -Message "Can't connect to $computerName. cacheMP[$computerName] = ?" -ErrorRecord $_ -Target $computerName -Continue
                }
            }
            if ($cacheMP[$computerName] -eq '?') {
                return '?'
            }
            foreach ($m in ($cacheMP[$computerName] | Sort-Object -Property Mountpoint -Descending)) {
                if ($path -like "$($m.Mountpoint)*") {
                    return $m.Mountpoint
                }
            }
            Write-Message -Level Warning -Message "Path $path can't be found in any MountPoints of $computerName"
        }
        function Get-MountPointFromDefaultPath {
            [CmdletBinding()]
            param(
                [Parameter(Mandatory = $true)]
                [ValidateSet('Log', 'Data')]
                $DefaultPathType,
                [Parameter(Mandatory = $true)]
                $SqlInstance,
                [PSCredential]$SqlCredential,
                # Could probably use the computer defined in SqlInstance but info was already available from the caller
                $computerName,
                [PSCredential]$Credential
            )
            if (!$cacheDP[$SqlInstance]) {
                try {
                    $cacheDP.Add($SqlInstance, (Get-DbaDefaultPath -SqlInstance $SqlInstance -SqlCredential $SqlCredential -EnableException))
                    Write-Message -Level Verbose -Message "cacheDP[$SqlInstance] is now cached"
                }
                catch {
                    Stop-Function -Message "Can't connect to $SqlInstance" -Continue
                    $cacheDP.Add($SqlInstance, '?')
                    return '?'
                }
            }
            if ($cacheDP[$SqlInstance] -eq '?') {
                return '?'
            }
            if (!$computerName) {
                $computerName = $cacheDP[$SqlInstance].ComputerName
            }
            if (!$cacheMP[$computerName]) {
                try {
                    $cacheMP.Add($computerName, (Get-MountPoint -computerName $computerName -Credential $Credential))
                }
                catch {
                    Stop-Function -Message "Can't connect to $computerName." -Continue
                    $cacheMP.Add($computerName, '?')
                    return '?'
                }
            }
            if ($DefaultPathType -eq 'Log') {
                $path = $cacheDP[$SqlInstance].Log
            }
            else {
                $path = $cacheDP[$SqlInstance].Data
            }
            foreach ($m in ($cacheMP[$computerName] | Sort-Object -Property Mountpoint -Descending)) {
                if ($path -like "$($m.Mountpoint)*") {
                    return $m.Mountpoint
                }
            }
        }
    }
    process {
        Write-Message -Level Verbose -Message "Connecting to SQL Servers."
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source."
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
        }

        try {
            Write-Message -Level Verbose -Message "Connecting to $Destination."
            $destServer = Connect-SqlInstance -SqlInstance $Destination -SqlCredential $DestinationSqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Destination
        }

        if (Test-Bound 'DestinationDatabase' -not) {
            $DestinationDatabase = $Database
        }
        Write-Message -Level Verbose -Message "$Source.[$Database] -> $Destination.[$DestinationDatabase]"

        $sourceDb = Get-DbaDatabase -SqlInstance $sourceServer -Database $Database -SqlCredential $SourceSqlCredential
        if (Test-Bound 'Database' -not) {
            Stop-Function -Message "Database [$Database] MUST exist on Source Instance $Source." -ErrorRecord $_
        }
        $sourceFiles = @($sourceDb.FileGroups.Files | Select-Object Name, FileName, Size, @{n='Type'; e= {'Data'}})
        $sourceFiles += @($sourceDb.LogFiles        | Select-Object Name, FileName, Size, @{n='Type'; e= {'Log'}})

        if ($destDb = Get-DbaDatabase -SqlInstance $destServer -Database $DestinationDatabase -SqlCredential $DestinationSqlCredential) {
            $destFiles = @($destDb.FileGroups.Files | Select-Object Name, FileName, Size, @{n='Type'; e= {'Data'}})
            $destFiles += @($destDb.LogFiles        | Select-Object Name, FileName, Size, @{n='Type'; e= {'Log'}})
            $computerName = $destDb.ComputerName
        }
        else {
            Write-Message -Level Verbose -Message "Database [$DestinationDatabase] does not exist on Destination Instance $Destination."
            $computerName = $destServer.ComputerName
        }

        foreach ($sourceFile in $sourceFiles) {
            foreach ($destFile in $destFiles) {
                if ($found = ($sourceFile.Name -eq $destFile.Name)) {
                    # Files found on both sides
                    [PSCustomObject]@{
                            SourceComputerName      = $sourceServer.ComputerName
                            SourceInstance          = $sourceServer.ServiceName
                            SourceSqlInstance       = $sourceServer.DomainInstanceName
                            DestinationComputerName = $destServer.ComputerName
                            DestinationInstance     = $destServer.ServiceName
                            DestinationSqlInstance  = $destServer.DomainInstanceName
                            SourceDatabase          = $sourceDb.Name
                            SourceLogicalName       = $sourceFile.Name
                            SourceFileName          = $sourceFile.FileName
                            SourceFileSize          = [DbaSize]($sourceFile.Size * 1000)
                            DestinationDatabase     = $destDb.Name
                            DestinationLogicalName  = $destFile.Name
                            DestinationFileName     = $destFile.FileName
                            DestinationFileSize     = [DbaSize]($destFile.Size * 1000) * -1
                            DifferenceSize          = [DbaSize]( ($sourceFile.Size * 1000) - ($destFile.Size * 1000) )
                            MountPoint              = Get-MountPointFromPath -Path $destFile.Filename -ComputerName $computerName -Credential $Credential
                            FileLocation            = 'Source and Destination'
                        } | Select-DefaultView -ExcludeProperty SourceComputerName, SourceInstance, DestinationInstance, DestinationLogicalName
                    break
                }
            }
            if (!$found) {
                # Files on source but not on destination
                [PSCustomObject]@{
                        SourceComputerName      = $sourceServer.ComputerName
                        SourceInstance          = $sourceServer.ServiceName
                        SourceSqlInstance       = $sourceServer.DomainInstanceName
                        DestinationComputerName = $destServer.ComputerName
                        DestinationInstance     = $destServer.ServiceName
                        DestinationSqlInstance  = $destServer.DomainInstanceName
                        SourceDatabase          = $sourceDb.Name
                        SourceLogicalName       = $sourceFile.Name
                        SourceFileName          = $sourceFile.FileName
                        SourceFileSize          = [DbaSize]($sourceFile.Size * 1000)
                        DestinationDatabase     = $DestinationDatabase
                        DestinationLogicalName  = $null
                        DestinationFileName     = $null
                        DestinationFileSize     = [DbaSize]0
                        DifferenceSize          = [DbaSize]($sourceFile.Size * 1000)
                        MountPoint              = Get-MountPointFromDefaultPath -DefaultPathType $sourceFile.Type -SqlInstance $Destination `
                                                  -SqlCredential $DestinationSqlCredential -computerName $computerName -credential $Credential
                        FileLocation            = 'Only on Source'
                    } | Select-DefaultView -ExcludeProperty SourceComputerName, SourceInstance, DestinationInstance, DestinationLogicalName
            }
        }
        if ($destDb) {
            # Files on destination but not on source (strange scenario but possible)
            $destFilesNotSource = Compare-Object -ReferenceObject $destFiles -DifferenceObject $sourceFiles -Property Name -PassThru
            foreach ($destFileNotSource in $destFilesNotSource) {
                [PSCustomObject]@{
                        SourceComputerName      = $sourceServer.ComputerName
                        SourceInstance          = $sourceServer.ServiceName
                        SourceSqlInstance       = $sourceServer.DomainInstanceName
                        DestinationComputerName = $destServer.ComputerName
                        DestinationInstance     = $destServer.ServiceName
                        DestinationSqlInstance  = $destServer.DomainInstanceName
                        SourceDatabaseName      = $Database
                        SourceLogicalName       = $null
                        SourceFileName          = $null
                        SourceFileSize          = [DbaSize]0
                        DestinationDatabaseName = $destDb.Name
                        DestinationLogicalName  = $destFileNotSource.Name
                        DestinationFileName     = $destFile.FileName
                        DestinationFileSize     = [DbaSize]($destFileNotSource.Size * 1000) * -1
                        DifferenceSize          = [DbaSize]($destFileNotSource.Size * 1000) * -1
                        MountPoint              = Get-MountPointFromPath -Path $destFileNotSource.Filename -ComputerName $computerName -Credential $Credential
                        FileLocation            = 'Only on Destination'
                    } | Select-DefaultView -ExcludeProperty SourceComputerName, SourceInstance, DestinationInstance, DestinationLogicalName
            }
        }
        $DestinationDatabase = $null
    }
}
function Mount-DbaDatabase {
    <#
        .SYNOPSIS
            Attach a SQL Server Database - aliased to Attach-DbaDatabase

        .DESCRIPTION
            This command will attach a SQL Server database.

        .PARAMETER SqlInstance
            The SQL Server instance.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to attach.

        .PARAMETER FileStructure
            A StringCollection object value that contains a list database files. If FileStructure is not specified, BackupHistory will be used to guess the structure.

        .PARAMETER DatabaseOwner
            Sets the database owner for the database. The sa account (or equivalent) will be used if DatabaseOwner is not specified.

        .PARAMETER AttachOption
            An AttachOptions object value that contains the attachment options. Valid options are "None", "RebuildLog", "EnableBroker", "NewBroker" and "ErrorBrokerConversations".

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Mount-DbaDatabase

        .EXAMPLE
            $fileStructure = New-Object System.Collections.Specialized.StringCollection
            $fileStructure.Add("E:\archive\example.mdf")
            $filestructure.Add("E:\archive\example.ldf")
            $filestructure.Add("E:\archive\example.ndf")
            Mount-DbaDatabase -SqlInstance sql2016 -Database example -FileStructure $fileStructure

            Attaches a database named "example" to sql2016 with the files "E:\archive\example.mdf", "E:\archive\example.ldf" and "E:\archive\example.ndf". The database owner will be set to sa and the attach option is None.

        .EXAMPLE
            Mount-DbaDatabase -SqlInstance sql2016 -Database example

            Since the FileStructure was not provided, this command will attempt to determine it based on backup history. If found, a database named example will be attached to sql2016.

        .EXAMPLE
            Mount-DbaDatabase -SqlInstance sql2016 -Database example -WhatIf

            Shows what would happen if the command were executed (without actually performing the command)
    #>
    [CmdletBinding(SupportsShouldProcess)]
    Param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]
        $SqlCredential,
        [parameter(Mandatory)]
        [string[]]$Database,
        [System.Collections.Specialized.StringCollection]$FileStructure,
        [string]$DatabaseOwner,
        [ValidateSet('None', 'RebuildLog', 'EnableBroker', 'NewBroker', 'ErrorBrokerConversations')]
        [string]$AttachOption = "None",
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if (-not $server.Logins.Item($DatabaseOwner)) {
                try {
                    $DatabaseOwner = ($server.Logins | Where-Object { $_.id -eq 1 }).Name
                }
                catch {
                    $DatabaseOwner = "sa"
                }
            }

            foreach ($db in $database) {

                if ($server.Databases[$db]) {
                    Stop-Function -Message "$db is already attached to $server." -Target $db -Continue
                }

                if ($server.Databases[$db].IsSystemObject) {
                    Stop-Function -Message "$db is a system database and cannot be attached using this method." -Target $db -Continue
                }

                if (-Not (Test-Bound -Parameter FileStructure)) {
                    $backuphistory = Get-DbaBackupHistory -SqlInstance $server -Database $db -Type Full | Sort-Object End -Descending | Select-Object -First 1

                    if (-not $backuphistory) {
                        $message = "Could not enumerate backup history to automatically build FileStructure. Rerun the command and provide the filestructure parameter."
                        Stop-Function -Message $message -Target $db -Continue
                    }

                    $backupfile = $backuphistory.Path[0]
                    $filepaths = (Read-DbaBackupHeader -SqlInstance $server -FileList -Path $backupfile).PhysicalName

                    $FileStructure = New-Object System.Collections.Specialized.StringCollection
                    foreach ($file in $filepaths) {
                        $exists = Test-DbaSqlpath -SqlInstance $server -Path $file
                        if (-not $exists) {
                            $message = "Could not find the files to build the FileStructure. Rerun the command and provide the FileStructure parameter."
                            Stop-Function -Message $message -Target $file -Continue
                        }

                        $null = $FileStructure.Add($file)
                    }
                }

                If ($Pscmdlet.ShouldProcess($server, "Attaching $Database with $DatabaseOwner as database owner and $AttachOption as attachoption")) {
                    try {
                        $server.AttachDatabase($db, $FileStructure, $DatabaseOwner, [Microsoft.SqlServer.Management.Smo.AttachOptions]::$AttachOption)

                        [pscustomobject]@{
                            ComputerName  = $server.ComputerName
                            InstanceName  = $server.ServiceName
                            SqlInstance   = $server.DomainInstanceName
                            Database      = $db
                            AttachResult  = "Success"
                            AttachOption  = $AttachOption
                            FileStructure = $FileStructure
                        }
                    }
                    catch {
                        Stop-Function -Message "Failure" -ErrorRecord $_ -Target $server
                    }
                }
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Move-DbaRegisteredServer {
    <#
        .SYNOPSIS
            Moves registered servers around SQL Server Central Management Server (CMS)

        .DESCRIPTION
            Moves registered servers around SQL Server Central Management Server (CMS)

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Name
            Specifies one or more reg servers to move. Name is the visible name in SSMS CMS interface (labeled Registered Server Name)

        .PARAMETER ServerName
            Specifies one or more reg servers to move. Server Name is the actual instance name (labeled Server Name)

        .PARAMETER NewGroup
            The new group. If no new group is specified, the default root will used

        .PARAMETER InputObject
            Allows results from Get-DbaRegisteredServer to be piped in

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.

            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.

            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Chrissy LeMaire (@cl)
            Tags: RegisteredServer, CMS

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Move-DbaRegisteredServer

        .EXAMPLE
            Move-DbaRegisteredServer -SqlInstance sql2012 -Name 'Web SQL Cluster' -NewGroup HR\Prod

            Moves the registered server on sql2012 titled 'Web SQL Cluster' to the Prod group within the HR group

        .EXAMPLE
            Move-DbaRegisteredServer -SqlInstance sql2012 -Group HR\Development -NewGroup HR\Prod

            Moves all servers from the HR and sub-group Development to HR Prod

        .EXAMPLE
            Get-DbaRegisteredServer -SqlInstance sql2017 -Name 'Web SQL Cluster' | Move-DbaRegisteredServer -NewGroup Web

            Moves the registered server 'Web SQL Cluster' on sql2017 to the Web group, also on sql2017
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$Name,
        [string[]]$ServerName,
        [string]$NewGroup,
        [parameter(ValueFromPipeline)]
        [Microsoft.SqlServer.Management.RegisteredServers.RegisteredServer[]]$InputObject,
        [switch]$EnableException
    )

    begin {
        if ((Test-Bound -ParameterName SqlInstance) -and (Test-Bound -Not -ParameterName Name) -and (Test-Bound -Not -ParameterName ServerName)) {
            Stop-Function -Message "Name or ServerName must be specified when using -SqlInstance"
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $SqlInstance) {
            $InputObject += Get-DbaRegisteredServer -SqlInstance $instance -SqlCredential $SqlCredential -Name $Name -ServerName $ServerName

        }

        foreach ($regserver in $InputObject) {
            $parentserver = Get-RegServerParent -InputObject $regserver

            if ($null -eq $parentserver) {
                Stop-Function -Message "Something went wrong and it's hard to explain, sorry. This basically shouldn't happen." -Continue
            }

            $server = $parentserver.ServerConnection.SqlConnectionObject

            if ((Test-Bound -ParameterName NewGroup)) {
                $group = Get-DbaRegisteredServerGroup -SqlInstance $server -Group $NewGroup

                if (-not $group) {
                    Stop-Function -Message "$NewGroup not found on $server" -Continue
                }
            }
            else {
                $group = Get-DbaRegisteredServerGroup -SqlInstance $server -Id 1
            }

            if ($Pscmdlet.ShouldProcess($regserver.SqlInstance, "Moving $($regserver.Name) to $group")) {
                try {
                    $null = $parentserver.ServerConnection.ExecuteNonQuery($regserver.ScriptMove($group).GetScript())
                    Get-DbaRegisteredServer -SqlInstance $server -Name $regserver.Name -ServerName $regserver.ServerName
                    $parentserver.ServerConnection.Disconnect()
                }
                catch {
                    Stop-Function -Message "Failed to move $($regserver.Name) to $NewGroup on $($regserver.SqlInstance)" -ErrorRecord $_ -Continue
                }
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Move-DbaRegisteredServerGroup {
    <#
        .SYNOPSIS
             Moves registered server groups around SQL Server Central Management Server (CMS).

        .DESCRIPTION
            Moves registered server groups around SQL Server Central Management Server (CMS).

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Group
            Specifies one or more groups to include from SQL Server Central Management Server.

        .PARAMETER InputObject
            Allows results from Get-DbaRegisteredServerGroup to be piped in

        .PARAMETER Id
            Get group by Id(s)

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER NewGroup
            The new location.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.

            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.

            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Chrissy LeMaire (@cl)
            Tags: RegisteredServer, CMS

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Move-DbaRegisteredServerGroup

        .EXAMPLE
            Move-DbaRegisteredServerGroup -SqlInstance sql2012 -Group HR\Development -NewGroup AD\Prod

            Moves the Development group within HR to the Prod group within AD

        .EXAMPLE
            Get-DbaRegisteredServerGroup -SqlInstance sql2017 -Group HR\Development| Move-DbaRegisteredServer -NewGroup Web

            Moves the Development group within HR to the Web group
    #>
    [CmdletBinding(SupportsShouldProcess)]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$Group,
        [parameter(Mandatory)]
        [string]$NewGroup,
        [parameter(ValueFromPipeline)]
        [Microsoft.SqlServer.Management.RegisteredServers.ServerGroup[]]$InputObject,
        [switch]$EnableException
    )
    begin {
        if ((Test-Bound -ParameterName SqlInstance) -and (Test-Bound -Not -ParameterName Group)) {
            Stop-Function -Message "Group must be specified when using -SqlInstance"
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance to search for $group"
            $InputObject += Get-DbaRegisteredServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Group $Group
        }

        foreach ($regservergroup in $InputObject) {
            $parentserver = Get-RegServerParent -InputObject $regservergroup

            if ($null -eq $parentserver) {
                Stop-Function -Message "Something went wrong and it's hard to explain, sorry. This basically shouldn't happen." -Continue
            }

            $server = $parentserver.ServerConnection.SqlConnectionObject

            if ($NewGroup -eq 'Default') {
                $groupobject = Get-DbaRegisteredServerGroup -SqlInstance $server -Id 1
            }
            else {
                $groupobject = Get-DbaRegisteredServerGroup -SqlInstance $server -Group $NewGroup
            }

            Write-Message -Level Verbose -Message "Found $($groupobject.Name) on $($parentserver.ServerConnection.ServerName)"

            if (-not $groupobject) {
                Stop-Function -Message "Group '$NewGroup' not found on $server" -Continue
            }

            if ($Pscmdlet.ShouldProcess($regservergroup.SqlInstance, "Moving $($regservergroup.Name) to $($groupobject.Name)")) {
                try {
                    Write-Message -Level Verbose -Message "Parsing $groupobject"
                    $newname = Get-RegServerGroupReverseParse $groupobject
                    $newname = "$newname\$($regservergroup.Name)"
                    Write-Message -Level Verbose -Message "Executing $($regservergroup.ScriptMove($groupobject).GetScript())"
                    $null = $parentserver.ServerConnection.ExecuteNonQuery($regservergroup.ScriptMove($groupobject).GetScript())
                    Write-Message -Level Verbose -Message "Connecting to $instance to search for $newname"
                    Get-DbaRegisteredServerGroup -SqlInstance $server -Group $newname
                    $parentserver.ServerConnection.Disconnect()
                }
                catch {
                    Stop-Function -Message "Failed to move $($regserver.Name) to $NewGroup on $($regserver.SqlInstance)" -ErrorRecord $_ -Continue
                }
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function New-DbaAgentJob {
    <#
.SYNOPSIS
New-DbaAgentJob creates a new job

.DESCRIPTION
New-DbaAgentJob makes is possible to create a job in the SQL Server Agent.
It returns an array of the job(s) created

.PARAMETER SqlInstance
SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Job
The name of the job. The name must be unique and cannot contain the percent (%) character.

.PARAMETER Schedule
Schedule to attach to job. This can be more than one schedule.

.PARAMETER ScheduleId
Schedule ID to attach to job. This can be more than one schedule ID.

.PARAMETER Disabled
Sets the status of the job to disabled. By default a job is enabled.

.PARAMETER Description
The description of the job.

.PARAMETER StartStepId
The identification number of the first step to execute for the job.

.PARAMETER Category
The category of the job.

.PARAMETER OwnerLogin
The name of the login that owns the job.

.PARAMETER EventLogLevel
Specifies when to place an entry in the Microsoft Windows application log for this job.
Allowed values 0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always"
The text value van either be lowercase, uppercase or something in between as long as the text is correct.

.PARAMETER EmailLevel
Specifies when to send an e-mail upon the completion of this job.
Allowed values 0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always"
The text value van either be lowercase, uppercase or something in between as long as the text is correct.

.PARAMETER NetsendLevel
Specifies when to send a network message upon the completion of this job.
Allowed values 0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always"
The text value van either be lowercase, uppercase or something in between as long as the text is correct.

.PARAMETER PageLevel
Specifies when to send a page upon the completion of this job.
Allowed values 0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always"
The text value van either be lowercase, uppercase or something in between as long as the text is correct.

.PARAMETER EmailOperator
The e-mail name of the operator to whom the e-mail is sent when EmailLevel is reached.

.PARAMETER NetsendOperator
The name of the operator to whom the network message is sent.

.PARAMETER PageOperator
The name of the operator to whom a page is sent.

.PARAMETER DeleteLevel
Specifies when to delete the job.
Allowed values 0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always"
The text value van either be lowercase, uppercase or something in between as long as the text is correct.

.PARAMETER Force
The force parameter will ignore some errors in the parameters and assume defaults.

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Author: Sander Stad (@sqlstad, sqlstad.nl)
Tags: Agent, Job, JobStep

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/New-DbaAgentJob

.EXAMPLE
New-DbaAgentJob -SqlInstance sql1 -Job 'Job One' -Description 'Just another job'
Creates a job with the name "Job1" and a small description

.EXAMPLE
New-DbaAgentJob -SqlInstance sql1 -Job 'Job One' -Disabled
Creates the job but sets it to disabled

.EXAMPLE
New-DbaAgentJob -SqlInstance sql1 -Job 'Job One' -EventLogLevel OnSuccess
Creates the job and sets the notification to write to the Windows Application event log on success

.EXAMPLE
New-DbaAgentJob -SqlInstance SSTAD-PC -Job 'Job One' -EmailLevel OnFailure -EmailOperator dba
Creates the job and sets the notification to send an e-mail to the e-mail operator

.EXAMPLE
New-DbaAgentJob -SqlInstance sql1 -Job 'Job One' -Description 'Just another job' -Whatif
Doesn't create the job but shows what would happen.

.EXAMPLE
New-DbaAgentJob -SqlInstance sql1, sql2, sql3 -Job 'Job One'
Creates a job with the name "Job One" on multiple servers

.EXAMPLE
"sql1", "sql2", "sql3" | New-DbaAgentJob -Job 'Job One'
Creates a job with the name "Job One" on multiple servers using the pipe line
#>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$Job,
        [object[]]$Schedule,
        [int[]]$ScheduleId,
        [switch]$Disabled,
        [string]$Description,
        [int]$StartStepId,
        [string]$Category,
        [string]$OwnerLogin,
        [ValidateSet(0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always")]
        [object]$EventLogLevel,
        [ValidateSet(0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always")]
        [object]$EmailLevel,
        [ValidateSet(0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always")]
        [Parameter()]
        [ValidateSet(0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always")]
        [object]$PageLevel,
        [string]$EmailOperator,
        [string]$NetsendOperator,
        [string]$PageOperator,
        [ValidateSet(0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always")]
        [object]$DeleteLevel,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {

        # Check of the event log level is of type string and set the integer value
        if ($EventLogLevel -notin 1, 2, 3) {
            $EventLogLevel = switch ($EventLogLevel) {
                "Never" { 0 } "OnSuccess" { 1 } "OnFailure" { 2 } "Always" { 3 }
                default { 0 }
            }
        }

        # Check of the email level is of type string and set the integer value
        if ($EmailLevel -notin 1, 2, 3) {
            $EmailLevel = switch ($EmailLevel) {
                "Never" { 0 } "OnSuccess" { 1 } "OnFailure" { 2 } "Always" { 3 }
                default { 0 }
            }
        }

        # Check of the net send level is of type string and set the integer value
        if ($NetsendLevel -notin 1, 2, 3) {
            $NetsendLevel = switch ($NetsendLevel) {
                "Never" { 0 } "OnSuccess" { 1 } "OnFailure" { 2 } "Always" { 3 }
                default { 0 }
            }
        }

        # Check of the page level is of type string and set the integer value
        if ($PageLevel -notin 1, 2, 3) {
            $PageLevel = switch ($PageLevel) {
                "Never" { 0 } "OnSuccess" { 1 } "OnFailure" { 2 } "Always" { 3 }
                default { 0 }
            }
        }

        # Check of the delete level is of type string and set the integer value
        if ($DeleteLevel -notin 1, 2, 3) {
            $DeleteLevel = switch ($DeleteLevel) {
                "Never" { 0 } "OnSuccess" { 1 } "OnFailure" { 2 } "Always" { 3 }
                default { 0 }
            }
        }

        # Check the e-mail operator name
        if (($EmailLevel -ge 1) -and (-not $EmailOperator)) {
            Stop-Function -Message "Please set the e-mail operator when the e-mail level parameter is set." -Target $sqlinstance
            return
        }

        # Check the e-mail operator name
        if (($NetsendLevel -ge 1) -and (-not $NetsendOperator)) {
            Stop-Function -Message "Please set the netsend operator when the netsend level parameter is set." -Target $sqlinstance
            return
        }

        # Check the e-mail operator name
        if (($PageLevel -ge 1) -and (-not $PageOperator)) {
            Stop-Function -Message "Please set the page operator when the page level parameter is set." -Target $sqlinstance
            return
        }
    }

    process {

        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $sqlinstance) {
            # Try connecting to the instance
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            # Check if the job already exists
            if (-not $Force -and ($server.JobServer.Jobs.Name -contains $Job)) {
                Stop-Function -Message "Job $Job already exists on $instance" -Target $instance -Continue
            }
            elseif ($Force -and ($server.JobServer.Jobs.Name -contains $Job)) {
                Write-Message -Message "Job $Job already exists on $instance. Removing.." -Level Verbose

                if ($PSCmdlet.ShouldProcess($instance, "Removing the job $Job on $instance")) {
                    try {
                        Remove-DbaAgentJob -SqlInstance $instance -Job $Job -EnableException
                    }
                    catch {
                        Stop-Function -Message "Couldn't remove job $Job from $instance" -Target $instance -Continue -ErrorRecord $_
                    }
                }

            }

            if ($PSCmdlet.ShouldProcess($instance, "Creating the job on $instance")) {
                # Create the job object
                try {
                    $currentjob = New-Object Microsoft.SqlServer.Management.Smo.Agent.Job($server.JobServer, $Job)
                }
                catch {
                    Stop-Function -Message "Something went wrong creating the job. `n" -Target $Job -Continue -ErrorRecord $_
                }

                #region job options
                # Settings the options for the job
                if ($Disabled) {
                    Write-Message -Message "Setting job to disabled" -Level Verbose
                    $currentjob.IsEnabled = $false
                }
                else {
                    Write-Message -Message "Setting job to enabled" -Level Verbose
                    $currentjob.IsEnabled = $true
                }

                if ($Description.Length -ge 1) {
                    Write-Message -Message "Setting job description" -Level Verbose
                    $currentjob.Description = $Description
                }

                if ($StartStepId -ge 1) {
                    Write-Message -Message "Setting job start step id" -Level Verbose
                    $currentjob.StartStepID = $StartStepId
                }

                if ($Category.Length -ge 1) {
                    # Check if the job category exists
                    if ($Category -notin $server.JobServer.JobCategories.Name) {
                        if ($Force) {
                            if ($PSCmdlet.ShouldProcess($instance, "Creating job category on $instance")) {
                                try {
                                    # Create the category
                                    New-DbaAgentJobCategory -SqlInstance $instance -Category $Category
                                }
                                catch {
                                    Stop-Function -Message "Couldn't create job category $Category from $instance" -Target $instance -Continue -ErrorRecord $_
                                }
                            }
                        }
                        else {
                            Stop-Function -Message "Job category $Category doesn't exist on $instance. Use -Force to create it." -Target $instance
                            return
                        }
                    }
                    else {
                        Write-Message -Message "Setting job category" -Level Verbose
                        $currentjob.Category = $Category
                    }
                }

                if ($OwnerLogin.Length -ge 1) {
                    # Check if the login name is present on the instance
                    if ($server.Logins.Name -contains $OwnerLogin) {
                        Write-Message -Message "Setting job owner login name to $OwnerLogin" -Level Verbose
                        $currentjob.OwnerLoginName = $OwnerLogin
                    }
                    else {
                        Stop-Function -Message "The owner $OwnerLogin does not exist on instance $instance" -Target $Job -Continue
                    }
                }

                if ($EventLogLevel -ge 0) {
                    Write-Message -Message "Setting job event log level" -Level Verbose
                    $currentjob.EventLogLevel = $EventLogLevel
                }

                if ($EmailOperator) {
                    if ($EmailLevel -ge 1) {
                        # Check if the operator name is present
                        if ($server.JobServer.Operators.Name -contains $EmailOperator) {
                            Write-Message -Message "Setting job e-mail level" -Level Verbose
                            $currentjob.EmailLevel = $EmailLevel

                            Write-Message -Message "Setting job e-mail operator" -Level Verbose
                            $currentjob.OperatorToEmail = $EmailOperator
                        }
                        else {
                            Stop-Function -Message "The e-mail operator name $EmailOperator does not exist on instance $instance. Exiting.." -Target $Job -Continue
                        }
                    }
                    else {
                        Stop-Function -Message "Invalid combination of e-mail operator name $EmailOperator and email level $EmailLevel. Not setting the notification." -Target $Job -Continue
                    }
                }

                if ($NetsendOperator) {
                    if ($NetsendLevel -ge 1) {
                        # Check if the operator name is present
                        if ($server.JobServer.Operators.Name -contains $NetsendOperator) {
                            Write-Message -Message "Setting job netsend level" -Level Verbose
                            $currentjob.NetSendLevel = $NetsendLevel

                            Write-Message -Message "Setting job netsend operator" -Level Verbose
                            $currentjob.OperatorToNetSend = $NetsendOperator
                        }
                        else {
                            Stop-Function -Message "The netsend operator name $NetsendOperator does not exist on instance $instance. Exiting.." -Target $Job -Continue
                        }
                    }
                    else {
                        Write-Message -Message "Invalid combination of netsend operator name $NetsendOperator and netsend level $NetsendLevel. Not setting the notification."
                    }
                }

                if ($PageOperator) {
                    if ($PageLevel -ge 1) {
                        # Check if the operator name is present
                        if ($server.JobServer.Operators.Name -contains $PageOperator) {
                            Write-Message -Message "Setting job pager level" -Level Verbose
                            $currentjob.PageLevel = $PageLevel

                            Write-Message -Message "Setting job pager operator" -Level Verbose
                            $currentjob.OperatorToPage = $PageOperator
                        }
                        else {
                            Stop-Function -Message "The page operator name $PageOperator does not exist on instance $instance. Exiting.." -Target $Job -Continue
                        }
                    }
                    else {
                        Write-Message -Message "Invalid combination of page operator name $PageOperator and page level $PageLevel. Not setting the notification." -Level Warning
                    }
                }

                if ($DeleteLevel -ge 0) {
                    Write-Message -Message "Setting job delete level" -Level Verbose
                    $currentjob.DeleteLevel = $DeleteLevel
                }
                #endregion job options

                try {
                    Write-Message -Message "Creating the job" -Level Verbose

                    # Create the job
                    $currentjob.Create()

                    Write-Message -Message "Job created with UID $($currentjob.JobID)" -Level Verbose

                    # Make sure the target is set for the job
                    Write-Message -Message "Applying the target (local) to job $Job" -Level Verbose
                    $currentjob.ApplyToTargetServer("(local)")

                    # If a schedule needs to be attached
                    if ($Schedule) {
                        Set-DbaAgentJob -SqlInstance $instance -Job $currentjob -Schedule $Schedule -SqlCredential $SqlCredential
                    }

                    if ($ScheduleId) {
                        Set-DbaAgentJob -SqlInstance $instance -Job $currentjob -ScheduleId $ScheduleId -SqlCredential $SqlCredential
                    }
                }
                catch {
                    Stop-Function -Message "Something went wrong creating the job" -Target $currentjob -ErrorRecord $_ -Continue
                }
            }

            # Return the job
            return $currentjob
        }
    }

    end {
        if (Test-FunctionInterrupt) { return }
        Write-Message -Message "Finished creating job(s)." -Level Verbose
    }

}
function New-DbaAgentJobCategory {
    <#
.SYNOPSIS
New-DbaAgentJobCategory creates a new job category.

.DESCRIPTION
New-DbaAgentJobCategory makes it possible to create a job category that can be used with jobs.
It returns an array of the job(s) created .

.PARAMETER SqlInstance
SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Category
The name of the category

.PARAMETER CategoryType
The type of category. This can be "LocalJob", "MultiServerJob" or "None".
The default is "LocalJob" and will automatically be set when no option is chosen.

.PARAMETER Force
The force parameter will ignore some errors in the parameters and assume defaults.

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Author: Sander Stad (@sqlstad, sqlstad.nl)
Tags: Agent, Job, JobCategory

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/New-DbaAgentJobCategory

.EXAMPLE
New-DbaAgentJobCategory -SqlInstance sql1 -Category 'Category 1'

Creates a new job category with the name 'Category 1'.

.EXAMPLE
New-DbaAgentJobCategory -SqlInstance sql1 -Category 'Category 2' -CategoryType MultiServerJob

Creates a new job category with the name 'Category 2' and assign the category type for a multi server job.

#>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string[]]$Category,
        [ValidateSet("LocalJob", "MultiServerJob", "None")]
        [string]$CategoryType,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        # Check the category type
        if (-not $CategoryType) {
            # Setting category type to default
            Write-Message -Message "Setting the category type to 'LocalJob'" -Level Verbose
            $CategoryType = "LocalJob"
        }
    }

    process {

        foreach ($instance in $sqlinstance) {
            # Try connecting to the instance
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            foreach ($cat in $Category) {
                # Check if the category already exists
                if ($cat -in $server.JobServer.JobCategories.Name) {
                    Stop-Function -Message "Job category $cat already exists on $instance" -Target $instance -Continue
                }
                else {
                    if ($PSCmdlet.ShouldProcess($instance, "Adding the job category $cat")) {
                        try {
                            $jobcategory = New-Object Microsoft.SqlServer.Management.Smo.Agent.JobCategory($server.JobServer, $cat)
                            $jobcategory.CategoryType = $CategoryType

                            $jobcategory.Create()

                            $server.JobServer.Refresh()
                        }
                        catch {
                            Stop-Function -Message "Something went wrong creating the job category $cat on $instance" -Target $cat -Continue -ErrorRecord $_
                        }

                    } # if should process

                } # end else category exists

                # Return the job category
                Get-DbaAgentJobCategory -SqlInstance $instance -Category $cat

            } # for each category

        } # for each instance
    }

    end {
        if (Test-FunctionInterrupt) { return }
        Write-Message -Message "Finished creating job category." -Level Verbose
    }

}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function New-DbaAgentJobStep {
    <#
.SYNOPSIS
New-DbaAgentJobStep creates a new job step for a job

.DESCRIPTION
New-DbaAgentJobStep creates a new job in the SQL Server Agent for a specific job

.PARAMETER SqlInstance
SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Job
The name of the job to which to add the step.

.PARAMETER StepId
The sequence identification number for the job step. Step identification numbers start at 1 and increment without gaps.

.PARAMETER StepName
The name of the step.

.PARAMETER SubSystem
The subsystem used by the SQL Server Agent service to execute command.
Allowed values 'ActiveScripting','AnalysisCommand','AnalysisQuery','CmdExec','Distribution','LogReader','Merge','PowerShell','QueueReader','Snapshot','Ssis','TransactSql'
The default is 'TransactSql'

.PARAMETER Command
The commands to be executed by SQLServerAgent service through subsystem.

.PARAMETER CmdExecSuccessCode
The value returned by a CmdExec subsystem command to indicate that command executed successfully.

.PARAMETER OnSuccessAction
The action to perform if the step succeeds.
Allowed values  "QuitWithSuccess" (default), "QuitWithFailure", "GoToNextStep", "GoToStep".
The text value van either be lowercase, uppercase or something in between as long as the text is correct.

.PARAMETER OnSuccessStepId
The ID of the step in this job to execute if the step succeeds and OnSuccessAction is "GoToStep".

.PARAMETER OnFailAction
The action to perform if the step fails.
Allowed values  "QuitWithSuccess" (default), "QuitWithFailure", "GoToNextStep", "GoToStep".
The text value van either be lowercase, uppercase or something in between as long as the text is correct.

.PARAMETER OnFailStepId
The ID of the step in this job to execute if the step fails and OnFailAction is "GoToNextStep".

.PARAMETER Database
The name of the database in which to execute a Transact-SQL step. The default is 'master'.

.PARAMETER DatabaseUser
The name of the user account to use when executing a Transact-SQL step.

.PARAMETER RetryAttempts
The number of retry attempts to use if this step fails. The default is 0.

.PARAMETER RetryInterval
The amount of time in minutes between retry attempts. The default is 0.

.PARAMETER OutputFileName
The name of the file in which the output of this step is saved.

.PARAMETER Flag
Sets the flag(s) for the job step.

Flag                                    Description
----------------------------------------------------------------------------
AppendAllCmdExecOutputToJobHistory      Job history, including command output, is appended to the job history file.
AppendToJobHistory                      Job history is appended to the job history file.
AppendToLogFile                         Job history is appended to the SQL Server log file.
AppendToTableLog                        Job history is appended to a log table.
LogToTableWithOverwrite                 Job history is written to a log table, overwriting previous contents.
None                                    Job history is not appended to a file.
ProvideStopProcessEvent                 Job processing is stopped.

.PARAMETER ProxyName
The name of the proxy that the job step runs as.

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER Force
The force parameter will ignore some errors in the parameters and assume defaults.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Agent, Job, JobStep
Author: Sander Stad (@sqlstad, sqlstad.nl)

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/New-DbaAgentJobStep

.EXAMPLE
New-DbaAgentJobStep -SqlInstance sql1 -Job Job1 -StepName Step1
Create a step in "Job1" with the name Step1 with the default subsystem TransactSql.

.EXAMPLE
New-DbaAgentJobStep -SqlInstance sql1 -Job Job1 -StepName Step1 -Database msdb
Create a step in "Job1" with the name Step1 where the database will the msdb

.EXAMPLE
New-DbaAgentJobStep -SqlInstance sql1, sql2, sql3 -Job Job1 -StepName Step1 -Database msdb
Create a step in "Job1" with the name Step1 where the database will the "msdb" for multiple servers

.EXAMPLE
New-DbaAgentJobStep -SqlInstance sql1, sql2, sql3 -Job Job1, Job2, 'Job Three' -StepName Step1 -Database msdb
Create a step in "Job1" with the name Step1 where the database will the "msdb" for multiple servers for multiple jobs

.EXAMPLE
sql1, sql2, sql3 | New-DbaAgentJobStep -Job Job1 -StepName Step1 -Database msdb
Create a step in "Job1" with the name Step1 where the database will the "msdb" for multiple servers using pipeline
#>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [object[]]$Job,
        [int]$StepId,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$StepName,
        [ValidateSet('ActiveScripting', 'AnalysisCommand', 'AnalysisQuery', 'CmdExec', 'Distribution', 'LogReader', 'Merge', 'PowerShell', 'QueueReader', 'Snapshot', 'Ssis', 'TransactSql')]
        [string]$Subsystem = 'TransactSql',
        [string]$Command,
        [int]$CmdExecSuccessCode,
        [ValidateSet('QuitWithSuccess', 'QuitWithFailure', 'GoToNextStep', 'GoToStep')]
        [string]$OnSuccessAction = 'QuitWithSuccess',
        [int]$OnSuccessStepId = 0,
        [ValidateSet('QuitWithSuccess', 'QuitWithFailure', 'GoToNextStep', 'GoToStep')]
        [string]$OnFailAction = 'QuitWithFailure',
        [int]$OnFailStepId,
        [object]$Database,
        [string]$DatabaseUser,
        [int]$RetryAttempts,
        [int]$RetryInterval,
        [string]$OutputFileName,
        [ValidateSet('AppendAllCmdExecOutputToJobHistory', 'AppendToJobHistory', 'AppendToLogFile', 'LogToTableWithOverwrite', 'None', 'ProvideStopProcessEvent')]
        [string[]]$Flag,
        [string]$ProxyName,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        # Check the parameter on success step id
        if (($OnSuccessAction -in 'GoToStep', 'GoToNextStep') -and ($OnSuccessStepId -ge 1)) {
            Stop-Function -Message "Parameter OnSuccessStepId can only be used with OnSuccessAction 'GoToStep'." -Target $SqlInstance
            return
        }

        # Check the parameter on success step id
        if (($OnFailAction -in 'GoToStep', 'GoToNextStep') -and ($OnFailStepId -ge 1)) {
            Stop-Function -Message "Parameter OnFailStepId can only be used with OnFailAction 'GoToStep'." -Target $SqlInstance
            return
        }
    }

    process {

        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $sqlinstance) {
            # Try connecting to the instance
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $Server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            foreach ($j in $Job) {

                # Check if the job exists
                if ($Server.JobServer.Jobs.Name -notcontains $j) {
                    Write-Message -Message "Job $j doesn't exists on $instance" -Warning
                }
                else {
                    # Create the job step object
                    try {
                        # Get the job
                        $currentjob = $Server.JobServer.Jobs[$j]

                        # Create the job step
                        $JobStep = New-Object Microsoft.SqlServer.Management.Smo.Agent.JobStep

                        # Set the job where the job steps belongs to
                        $JobStep.Parent = $currentjob
                    }
                    catch {
                        Stop-Function -Message "Something went wrong creating the job step" -Target $instance -ErrorRecord $_ -Continue
                    }

                    #region job step options
                    # Setting the options for the job step
                    if ($StepName) {
                        # Check if the step already exists
                        if ($Server.JobServer.Jobs[$j].JobSteps.Name -notcontains $StepName) {
                            $JobStep.Name = $StepName
                        }
                        elseif (($Server.JobServer.Jobs[$j].JobSteps.Name -contains $StepName) -and $Force) {
                            Write-Message -Message "Step $StepName already exists for job. Force is used. Removing existing step" -Level Verbose

                            # Remove the job step based on the name
                            Remove-DbaAgentJobStep -SqlInstance $instance -Job $currentjob -StepName $StepName

                            # Set the name job step object
                            $JobStep.Name = $StepName
                        }
                        else {
                            Stop-Function -Message "The step name $StepName already exists for job $j" -Target $instance -Continue
                        }
                    }

                    # If the step id need to be set
                    if ($StepId) {
                        # Check if the used step id is already in place
                        if ($Job.JobSteps.ID -notcontains $StepId) {
                            Write-Message -Message "Setting job step step id to $StepId" -Level Verbose
                            $JobStep.ID = $StepId
                        }
                        elseif ($Job.JobSteps.ID -contains $StepId) {
                            if($Force){
                                Write-Message -Message "Step ID $StepId already exists for job. Force is used. Removing existing step" -Level Verbose

                                # Remove the existing job step
                                $StepName = ($Server.JobServer.Jobs['Job2'].JobSteps | Where-Object {$_.ID -eq 1}).Name
                                Remove-DbaAgentJobStep -SqlInstance $instance -Job $currentjob -StepName $StepName
                            }

                            # Set the ID job step object
                            $JobStep.ID = $StepId
                        }
                        else {
                            Stop-Function -Message "The step id $StepId already exists for job $j" -Target $instance -Continue
                        }
                    }
                    else {
                        # Get the job step count
                        $JobStep.ID = $Job.JobSteps.Count + 1
                    }

                    if ($Subsystem) {
                        Write-Message -Message "Setting job step subsystem to $Subsystem" -Level Verbose
                        $JobStep.Subsystem = $Subsystem
                    }

                    if ($Command) {
                        Write-Message -Message "Setting job step command to $Command" -Level Verbose
                        $JobStep.Command = $Command
                    }

                    if ($CmdExecSuccessCode) {
                        Write-Message -Message "Setting job step command exec success code to $CmdExecSuccessCode" -Level Verbose
                        $JobStep.CommandExecutionSuccessCode = $CmdExecSuccessCode
                    }

                    if ($OnSuccessAction) {
                        Write-Message -Message "Setting job step success action to $OnSuccessAction" -Level Verbose
                        $JobStep.OnSuccessAction = $OnSuccessAction
                    }

                    if ($OnSuccessStepId) {
                        Write-Message -Message "Setting job step success step id to $OnSuccessStepId" -Level Verbose
                        $JobStep.OnSuccessStep = $OnSuccessStepId
                    }

                    if ($OnFailAction) {
                        Write-Message -Message "Setting job step fail action to $OnFailAction" -Level Verbose
                        $JobStep.OnFailAction = $OnFailAction
                    }

                    if ($OnFailStepId) {
                        Write-Message -Message "Setting job step fail step id to $OnFailStepId" -Level Verbose
                        $JobStep.OnFailStep = $OnFailStepId
                    }

                    if ($Database) {
                        # Check if the database is present on the server
                        if ($Server.Databases.Name -contains $Database) {
                            Write-Message -Message "Setting job step database name to $Database" -Level Verbose
                            $JobStep.DatabaseName = $Database
                        }
                        else {
                            Stop-Function -Message "The database is not present on instance $instance." -Target $instance -Continue
                        }
                    }

                    if ($DatabaseUser -and $DatabaseName) {
                        # Check if the username is present in the database
                        if ($Server.Databases[$DatabaseName].Users.Name -contains $DatabaseUser) {

                            Write-Message -Message "Setting job step database username to $DatabaseUser" -Level Verbose
                            $JobStep.DatabaseUserName = $DatabaseUser
                        }
                        else {
                            Stop-Function -Message "The database user is not present in the database $DatabaseName on instance $instance." -Target $instance -Continue
                        }
                    }

                    if ($RetryAttempts) {
                        Write-Message -Message "Setting job step retry attempts to $RetryAttempts" -Level Verbose
                        $JobStep.RetryAttempts = $RetryAttempts
                    }

                    if ($RetryInterval) {
                        Write-Message -Message "Setting job step retry interval to $RetryInterval" -Level Verbose
                        $JobStep.RetryInterval = $RetryInterval
                    }

                    if ($OutputFileName) {
                        Write-Message -Message "Setting job step output file name to $OutputFileName" -Level Verbose
                        $JobStep.OutputFileName = $OutputFileName
                    }

                    if ($ProxyName) {
                        # Check if the proxy exists
                        if ($Server.JobServer.ProxyAccounts.Name -contains $ProxyName) {
                            Write-Message -Message "Setting job step proxy name to $ProxyName" -Level Verbose
                            $JobStep.ProxyName = $ProxyName
                        }
                        else {
                            Stop-Function -Message "The proxy name $ProxyName doesn't exist on instance $instance." -Target $instance -Continue
                        }
                    }

                    if ($Flag.Count -ge 1) {
                        Write-Message -Message "Setting job step flag(s) to $($Flags -join ',')" -Level Verbose
                        $JobStep.JobStepFlags = $Flag
                    }
                    #endregion job step options

                    # Execute
                    if ($PSCmdlet.ShouldProcess($instance, "Creating the job step $StepName")) {
                        try {
                            Write-Message -Message "Creating the job step" -Level Verbose

                            # Create the job step
                            $JobStep.Create()
                            $currentjob.Alter()
                        }
                        catch {
                            Stop-Function -Message "Something went wrong creating the job step" -Target $instance -ErrorRecord $_ -Continue
                        }
                    }

                    # Return the job step
                    $JobStep
                }
            } # foreach object job
        } # foreach object instance
    } # process

    end {
        if (Test-FunctionInterrupt) { return }
        Write-Message -Message "Finished creating job step(s)" -Level Verbose
    }
}

function New-DbaAgentProxy {
    <#
        .SYNOPSIS
        Adds one or more proxies to SQL Server Agent

        .DESCRIPTION
        Adds one or more proxies to SQL Server Agent

        .PARAMETER SqlInstance
        The SQL Server instance holding the databases to be removed.You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SqlCredential
        Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Name
        The name of the proxy or proxies you want to create

        .PARAMETER Credential
        The associated SQL Server Credential. The credential must be created prior to creating the Proxy.

        .PARAMETER SubSystem
        The associated subsystem or subsystems. Defaults to CmdExec.

        Valid options include:
        ActiveScripting
        AnalysisCommand
        AnalysisQuery
        CmdExec
        Distribution
        LogReader
        Merge
        PowerShell
        QueueReader
        Snapshot
        Ssis
        TransactSql

        .PARAMETER Description
        A description of the proxy

        .PARAMETER Login
        The SQL Server login or logins (known as proxy principals) to assign to the proxy

        .PARAMETER ServerRole
        The SQL Server role or roles (known as proxy principals) to assign to the proxy

        .PARAMETER MsdbRole
        The msdb role or roles (known as proxy principals) to assign to the proxy

        .PARAMETER Disabled
        Create the proxy as disabled

        .PARAMETER Force
        Drop and recreate the proxy if it already exists

        .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
        Tags: Agent, Proxy
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

        .LINK
        https://dbatools.io/New-DbaAgentProxy

        .EXAMPLE
        New-DbaAgentProxy -SqlInstance sql2016 -Name STIG -Credential 'PowerShell Proxy'

        Creates an Agent Proxy on sql2016 with the name STIG with the 'PowerShell Proxy' credential.
        The proxy is automatically added to the CmdExec subsystem.

        .EXAMPLE
        New-DbaAgentProxy -SqlInstance localhost\sql2016 -Name STIG -Credential 'PowerShell Proxy' -Description "Used for auditing purposes" -Login ad\sqlstig -SubSystem CmdExec, PowerShell -ServerRole securtyadmin -MsdbRole ServerGroupAdministratorRole

        Creates an Agent Proxy on sql2016 with the name STIG with the 'PowerShell Proxy' credential and the following principals:

        Login: ad\sqlstig
        ServerRole: securtyadmin
        MsdbRole: ServerGroupAdministratorRole

        By default, only sysadmins have access to create job steps with proxies. This will allow 3 additional principals access:
        The proxy is then added to the CmdExec and PowerShell subsystems

    #>
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
    param (
        [parameter(Mandatory, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory)]
        [string[]]$Name,
        [parameter(Mandatory)]
        [string[]]$Credential,
        [ValidateSet("ActiveScripting", "AnalysisCommand", "AnalysisQuery", "CmdExec", "Distribution", "LogReader", "Merge", "PowerShell", "QueueReader", "Snapshot", "Ssis", "TransactSql")]
        [string[]]$SubSystem = "CmdExec",
        [string]$Description,
        [string[]]$Login,
        [string[]]$ServerRole,
        [string[]]$MsdbRole,
        [switch]$Disabled,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            try {
                $jobServer = $server.JobServer
            }
            catch {
                Stop-Function -Message "Failure. Is SQL Agent started?" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            foreach ($proxyname in $name) {

                if ($jobServer.ProxyAccounts[$proxyName]) {
                    if ($force) {
                        if ($Pscmdlet.ShouldProcess($instance, "Dropping $proxyname")) {
                            $jobServer.ProxyAccounts[$proxyName].Drop()
                            $jobServer.ProxyAccounts.Refresh()
                        }
                    }
                    else {
                        Write-Message -Level Warning -Message "Proxy account $proxy already exists on $instance. Use -Force to drop and recreate."
                        continue
                    }
                }

                if (-not $server.Credentials[$Credential]) {
                    Write-Message -Level Warning -Message "Credential '$Credential' does not exist on $instance"
                    continue
                }

                if ($Pscmdlet.ShouldProcess($instance, "Adding $proxyname with the $Credential credential")) {
                    # the new-object is stubborn and $true/$false has to be forced in
                    $enabled = switch ($disabled) {
                        $false {
                            $proxy = New-Object Microsoft.SqlServer.Management.Smo.Agent.ProxyAccount -ArgumentList $jobServer, $ProxyName, $Credential, $true, $Description
                        }
                        $true {
                            $proxy = New-Object Microsoft.SqlServer.Management.Smo.Agent.ProxyAccount -ArgumentList $jobServer, $ProxyName, $Credential, $false, $Description
                        }
                    }

                    try {
                        $proxy.Create()
                    }
                    catch {
                        Stop-Function -Message "Could not create proxy account" -ErrorRecord $_ -Target $instance -Continue
                    }
                }

                foreach ($loginname in $login) {
                    if ($server.Logins[$loginname]) {
                        if ($Pscmdlet.ShouldProcess($instance, "Adding login $loginname to proxy")) {
                            $proxy.AddLogin($loginname)
                        }
                    }
                    else {
                        Write-Message -Level Warning -Message "Login '$loginname' does not exist on $instance"
                    }
                }

                foreach ($role in $ServerRole) {
                    if ($server.Roles[$role]) {
                        if ($Pscmdlet.ShouldProcess($instance, "Adding server role $role to proxy")) {
                            $proxy.AddServerRole($role)
                        }
                    }
                    else {
                        Write-Message -Level Warning -Message "Server Role '$role' does not exist on $instance"
                    }
                }

                foreach ($role in $MsdbRole) {
                    if ($server.Databases['msdb'].Roles[$role]) {
                        if ($Pscmdlet.ShouldProcess($instance, "Adding msdb role $role to proxy")) {
                            $proxy.AddMsdbRole($role)
                        }
                    }
                    else {
                        Write-Message -Level Warning -Message "msdb role '$role' does not exist on $instance"
                    }
                }

                foreach ($system in $SubSystem) {
                    if ($Pscmdlet.ShouldProcess($instance, "Adding subsystem $system to proxy")) {
                        $proxy.AddSubSystem($system)
                    }
                }

                if ($Pscmdlet.ShouldProcess("console", "Outputting Proxy object")) {
                    $proxy.Alter()
                    $proxy.Refresh()
                    Add-Member -Force -InputObject $proxy -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                    Add-Member -Force -InputObject $proxy -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                    Add-Member -Force -InputObject $proxy -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                    Add-Member -Force -InputObject $proxy -MemberType NoteProperty -Name Logins -value $proxy.EnumLogins()
                    Add-Member -Force -InputObject $proxy -MemberType NoteProperty -Name ServerRoles -value $proxy.EnumServerRoles()
                    Add-Member -Force -InputObject $proxy -MemberType NoteProperty -Name MsdbRoles -value $proxy.EnumMsdbRoles()
                    Add-Member -Force -InputObject $proxy -MemberType NoteProperty -Name Subsystems -value $proxy.EnumSubSystems()

                    Select-DefaultView -InputObject $proxy -Property ComputerName, InstanceName, SqlInstance, ID, Name, CredentialName, CredentialIdentity, Description, Logins, ServerRoles, MsdbRoles, SubSystems, IsEnabled
                }
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function New-DbaAgentSchedule {
    <#
        .SYNOPSIS
            New-DbaAgentSchedule creates a new schedule in the msdb database.

        .DESCRIPTION
            New-DbaAgentSchedule will help create a new schedule for a job.
            If the job parameter is not supplied the schedule will not be attached to a job.

        .PARAMETER SqlInstance
            SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Job
            The name of the job that has the schedule.

        .PARAMETER Schedule
            The name of the schedule.

        .PARAMETER Disabled
            Set the schedule to disabled. Default is enabled

        .PARAMETER FrequencyType
            A value indicating when a job is to be executed.

            Allowed values: Once, Daily, Weekly, Monthly, MonthlyRelative, AgentStart or IdleComputer

            If force is used the default will be "Once".

        .PARAMETER FrequencyInterval
            The days that a job is executed

            Allowed values: Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Weekdays, Weekend or EveryDay.
            The other allowed values are the numbers 1 to 31 for each day of the month.

            If "Weekdays", "Weekend" or "EveryDay" is used it over writes any other value that has been passed before.

            If force is used the default will be 1.

        .PARAMETER FrequencySubdayType
            Specifies the units for the subday FrequencyInterval.

            Allowed values: Time, Seconds, Minutes, or Hours

        .PARAMETER FrequencySubdayInterval
            The number of subday type periods to occur between each execution of a job.

        .PARAMETER FrequencyRelativeInterval
            A job's occurrence of FrequencyInterval in each month, if FrequencyInterval is 32 (monthlyrelative).

            Allowed values: First, Second, Third, Fourth or Last

        .PARAMETER FrequencyRecurrenceFactor
            The number of weeks or months between the scheduled execution of a job.

            FrequencyRecurrenceFactor is used only if FrequencyType is "Weekly", "Monthly" or "MonthlyRelative".

        .PARAMETER StartDate
            The date on which execution of a job can begin.

            If force is used the start date will be the current day

        .PARAMETER EndDate
            The date on which execution of a job can stop.

            If force is used the end date will be '9999-12-31'

        .PARAMETER StartTime
            The time on any day to begin execution of a job. Format HHMMSS / 24 hour clock.
            Example: '010000' for 01:00:00 AM.
            Example: '140000' for 02:00:00 PM.

            If force is used the start time will be '00:00:00'

        .PARAMETER EndTime
            The time on any day to end execution of a job. Format HHMMSS / 24 hour clock.
            Example: '010000' for 01:00:00 AM.
            Example: '140000' for 02:00:00 PM.

            If force is used the start time will be '23:59:59'

        .PARAMETER Owner
            The name of the server principal that owns the schedule. If no value is given the schedule is owned by the creator.

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER Force
            The force parameter will ignore some errors in the parameters and assume defaults.
            It will also remove the any present schedules with the same name for the specific job.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Agent, Job, JobStep
            Author: Sander Stad (@sqlstad, sqlstad.nl)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/New-DbaAgentSchedule

        .EXAMPLE
            New-DbaAgentSchedule -SqlInstance localhost\SQL2016 -Schedule daily -FrequencyType Daily -FrequencyInterval Everyday -Force

            Creates a schedule with a daily frequency every day. It assumes default values for the start date, start time, end date and end time due to -Force.

        .EXAMPLE
            New-DbaAgentSchedule -SqlInstance sstad-pc -Schedule MonthlyTest -FrequencyType Monthly -FrequencyInterval 10 -FrequencyRecurrenceFactor 1 -Force

            Create a schedule with a monhtly frequency occuring every 10th of the month. It assumes default values for the start date, start time, end date and end time due to -Force.

    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [System.Management.Automation.PSCredential]
        $SqlCredential,
        [object[]]$Job,
        [object]$Schedule,
        [switch]$Disabled,
        [ValidateSet('Once', 'Daily', 'Weekly', 'Monthly', 'MonthlyRelative', 'AgentStart', 'IdleComputer')]
        [object]$FrequencyType,
        [ValidateSet('EveryDay', 'Weekdays', 'Weekend', 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31)]
        [object[]]$FrequencyInterval,
        [ValidateSet('Time', 'Seconds', 'Minutes', 'Hours')]
        [object]$FrequencySubdayType,
        [int]$FrequencySubdayInterval,
        [ValidateSet('Unused', 'First', 'Second', 'Third', 'Fourth', 'Last')]
        [object]$FrequencyRelativeInterval,
        [int]$FrequencyRecurrenceFactor,
        [string]$StartDate,
        [string]$EndDate,
        [string]$StartTime,
        [string]$EndTime,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        # if a Schedule is not provided there is no much point
        if (!$Schedule) {
            Stop-Function -Message "A schedule was not provided! Please provide a schedule name."
            return
        }

        [int]$Interval = 0

        # Translate FrequencyType value from string to the integer value
        if (!$FrequencyType -or $FrequencyType) {
            [int]$FrequencyType =
            switch ($FrequencyType) {
                "Once" { 1 }
                "Daily" { 4 }
                "Weekly" { 8 }
                "Monthly" { 16 }
                "MonthlyRelative" { 32 }
                "AgentStart" { 64 }
                "IdleComputer" { 128 }
                default { 1 }
            }
        }

        # Translate FrequencySubdayType value from string to the integer value
        if (!$FrequencySubdayType -or $FrequencySubdayType) {
            [int]$FrequencySubdayType =
            switch ($FrequencySubdayType) {
                "Time" { 1 }
                "Seconds" { 2 }
                "Minutes" { 4 }
                "Hours" { 8 }
                default { 1 }
            }
        }

        # Check of the relative FrequencyInterval value is of type string and set the integer value
        [int]$FrequencyRelativeInterval =
        switch ($FrequencyRelativeInterval) {
            "First" { 1 }
            "Second" { 2 }
            "Third" { 4 }
            "Fourth" { 8 }
            "Last" { 16 }
            "Unused" { 0 }
            default {0}
        }

        # Check if the interval is valid
        if (($FrequencyType -in 4, "Daily") -and (($FrequencyInterval -lt 1 -or $FrequencyInterval -ge 365) -and -not $FrequencyInterval -eq "EveryDay")) {
            Stop-Function -Message "The frequency interval $FrequencyInterval requires a frequency interval to be between 1 and 365." -Target $SqlInstance
            return
        }

        # Check if the recurrence factor is set for weekly or monthly interval
        if (($FrequencyType -in (16, 8)) -and $FrequencyRecurrenceFactor -lt 1) {
            if ($Force) {
                $FrequencyRecurrenceFactor = 1
                Write-Message -Message "Recurrence factor not set for weekly or monthly interval. Setting it to $FrequencyRecurrenceFactor." -Level Verbose
            }
            else {
                Stop-Function -Message "The recurrence factor $FrequencyRecurrenceFactor (parameter FrequencyRecurrenceFactor) needs to be at least one when using a weekly or monthly interval." -Target $SqlInstance
                return
            }
        }

        # Check the subday interval
        if (($FrequencySubdayType -in 2, "Seconds", 4, "Minutes") -and (-not ($FrequencySubdayInterval -ge 1 -or $FrequencySubdayInterval -le 59))) {
            Stop-Function -Message "Subday interval $FrequencySubdayInterval must be between 1 and 59 when subday type is 'Seconds' or 'Minutes'" -Target $SqlInstance
            return
        }
        elseif (($FrequencySubdayType -eq 8, "Hours") -and (-not ($FrequencySubdayInterval -ge 1 -and $FrequencySubdayInterval -le 23))) {
            Stop-Function -Message "Subday interval $FrequencySubdayInterval must be between 1 and 23 when subday type is 'Hours'" -Target $SqlInstance
            return
        }

        # If the FrequencyInterval is set for the daily FrequencyType
        if ($FrequencyType -in 4, 'Daily') {
            # Create the interval to hold the value(s)
            [int]$Interval = 0

            # Create the interval to hold the value(s)
            switch ($FrequencyInterval) {
                "EveryDay" { $Interval = 1}
                default {$Interval = 1 }
            }

        }

        # If the FrequencyInterval is set for the weekly FrequencyType
        if ($FrequencyType -in 8, 'Weekly') {
            # Create the interval to hold the value(s)
            [int]$Interval = 0

            # Loop through the array
            foreach ($Item in $FrequencyInterval) {

                switch ($Item) {
                    "Sunday" { $Interval += 1 }
                    "Monday" { $Interval += 2 }
                    "Tuesday" { $Interval += 4 }
                    "Wednesday" { $Interval += 8 }
                    "Thursday" { $Interval += 16 }
                    "Friday" { $Interval += 32 }
                    "Saturday" { $Interval += 64 }
                    "Weekdays" { $Interval = 62 }
                    "Weekend" { $Interval = 65 }
                    "EveryDay" {$Interval = 127 }
                    1 { $Interval += 1 }
                    2 { $Interval += 2 }
                    4 { $Interval += 4 }
                    8 { $Interval += 8 }
                    16 { $Interval += 16 }
                    32 { $Interval += 32 }
                    64 { $Interval += 64 }
                    62 { $Interval = 62 }
                    65 { $Interval = 65 }
                    127 {$Interval = 127 }
                    default { $Interval = 0 }
                }
            }
        }

        # If the FrequencyInterval is set for the monthly FrequencyInterval
        if ($FrequencyType -in 16, 'Monthly') {
            # Create the interval to hold the value(s)
            [int]$Interval = 0

            # Loop through the array
            foreach ($Item in $FrequencyInterval) {
                $FrequencyInterval
                switch ($Item) {
                    {[int]$_ -ge 1 -and [int]$_ -le 31} { $Interval = [int]$Item }
                }
            }


        }

        # If the FrequencyInterval is set for the relative monthly FrequencyInterval
        if ($FrequencyType -eq 32) {
            # Create the interval to hold the value(s)
            [int]$Interval = 0

            # Loop through the array
            foreach ($Item in $FrequencyInterval) {
                switch ($Item) {
                    "Sunday" { $Interval += 1 }
                    "Monday" { $Interval += 2 }
                    "Tuesday" { $Interval += 3 }
                    "Wednesday" { $Interval += 4 }
                    "Thursday" { $Interval += 5 }
                    "Friday" { $Interval += 6 }
                    "Saturday" { $Interval += 7 }
                    "Day" { $Interval += 8 }
                    "Weekday" { $Interval += 9 }
                    "WeekendDay" { $Interval += 10 }
                    1 { $Interval += 1 }
                    2 { $Interval += 2 }
                    3 { $Interval += 3 }
                    4 { $Interval += 4 }
                    5 { $Interval += 5 }
                    6 { $Interval += 6 }
                    7 { $Interval += 7 }
                    8 { $Interval += 8 }
                    9 { $Interval += 9 }
                    10 { $Interval += 10 }
                }
            }
        }

        # Check if the interval is valid for the frequency
        if ($FrequencyType -eq 0) {
            if ($Force) {
                Write-Message -Message "Parameter FrequencyType must be set to at least [Once]. Setting it to 'Once'." -Level Warning
                $FrequencyType = 1
            }
            else {
                Stop-Function -Message "Parameter FrequencyType must be set to at least [Once]" -Target $SqlInstance
                return
            }
        }

        # Check if the interval is valid for the frequency
        if (($FrequencyType -in 4, 8, 32) -and ($Interval -lt 1)) {
            if ($Force) {
                Write-Message -Message "Parameter FrequencyInterval must be provided for a recurring schedule. Setting it to first day of the week." -Level Warning
                $Interval = 1
            }
            else {
                Stop-Function -Message "Parameter FrequencyInterval must be provided for a recurring schedule." -Target $SqlInstance
                return
            }
        }

        # Setup the regex
        $RegexDate = '(?<!\d)(?:(?:(?:1[6-9]|[2-9]\d)?\d{2})(?:(?:(?:0[13578]|1[02])31)|(?:(?:0[1,3-9]|1[0-2])(?:29|30)))|(?:(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))0229)|(?:(?:1[6-9]|[2-9]\d)?\d{2})(?:(?:0?[1-9])|(?:1[0-2]))(?:0?[1-9]|1\d|2[0-8]))(?!\d)'
        $RegexTime = '^(?:(?:([01]?\d|2[0-3]))?([0-5]?\d))?([0-5]?\d)$'

        # Check the start date
        if (-not $StartDate -and $Force) {
            $StartDate = Get-Date -Format 'yyyyMMdd'
            Write-Message -Message "Start date was not set. Force is being used. Setting it to $StartDate" -Level Verbose
        }
        elseif (-not $StartDate) {
            Stop-Function -Message "Please enter a start date or use -Force to use defaults." -Target $SqlInstance
            return
        }
        elseif ($StartDate -notmatch $RegexDate) {
            Stop-Function -Message "Start date $StartDate needs to be a valid date with format yyyyMMdd" -Target $SqlInstance
            return
        }

        # Check the end date
        if (-not $EndDate -and $Force) {
            $EndDate = '99991231'
            Write-Message -Message "End date was not set. Force is being used. Setting it to $EndDate" -Level Verbose
        }
        elseif (-not $EndDate) {
            Stop-Function -Message "Please enter an end date or use -Force to use defaults." -Target $SqlInstance
            return
        }

        elseif ($EndDate -notmatch $RegexDate) {
            Stop-Function -Message "End date $EndDate needs to be a valid date with format yyyyMMdd" -Target $SqlInstance
            return
        }
        elseif ($EndDate -lt $StartDate) {
            Stop-Function -Message "End date $EndDate cannot be before start date $StartDate" -Target $SqlInstance
            return
        }

        # Check the start time
        if (-not $StartTime -and $Force) {
            $StartTime = '000000'
            Write-Message -Message "Start time was not set. Force is being used. Setting it to $StartTime" -Level Verbose
        }
        elseif (-not $StartTime) {
            Stop-Function -Message "Please enter a start time or use -Force to use defaults." -Target $SqlInstance
            return
        }
        elseif ($StartTime -notmatch $RegexTime) {
            Stop-Function -Message "Start time $StartTime needs to match between '000000' and '235959'" -Target $SqlInstance
            return
        }

        # Check the end time
        if (-not $EndTime -and $Force) {
            $EndTime = '235959'
            Write-Message -Message "End time was not set. Force is being used. Setting it to $EndTime" -Level Verbose
        }
        elseif (-not $EndTime) {
            Stop-Function -Message "Please enter an end time or use -Force to use defaults." -Target $SqlInstance
            return
        }
        elseif ($EndTime -notmatch $RegexTime) {
            Stop-Function -Message "End time $EndTime needs to match between '000000' and '235959'" -Target $SqlInstance
            return
        }
    }

    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $sqlinstance) {
            # Try connecting to the instance
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            # Check if the jobs parameter is set
            if ($Job) {
                # Loop through each of the jobs
                foreach ($j in $Job) {

                    # Check if the job exists
                    if ($Server.JobServer.Jobs.Name -notcontains $j) {
                        Write-Message -Message "Job $j doesn't exists on $instance" -Level Warning
                    }
                    else {
                        # Create the job schedule object
                        try {
                            # Get the job
                            $smoJob = $Server.JobServer.Jobs[$j]

                            # Check if schedule already exists with the same name
                            if ($Server.JobServer.JobSchedules.Name -contains $Schedule) {
                                # Check if force is set which will remove the other schedule
                                if ($Force) {
                                    if ($PSCmdlet.ShouldProcess($instance, "Removing the schedule $Schedule on $instance")) {
                                        # Removing schedule
                                        Remove-DbaAgentSchedule -SqlInstance $instance -SqlCredential $SqlCredential -Schedule $Schedule -Force:$Force
                                    }
                                }
                                else {
                                    Stop-Function -Message "Schedule $Schedule already exists for job $j on instance $instance" -Target $instance -ErrorRecord $_ -Continue
                                }
                            }

                            # Create the job schedule
                            $JobSchedule = New-Object Microsoft.SqlServer.Management.Smo.Agent.JobSchedule($smoJob, $Schedule)

                        }
                        catch {
                            Stop-Function -Message "Something went wrong creating the job schedule $Schedule for job $j." -Target $instance -ErrorRecord $_ -Continue
                        }

                        #region job schedule options
                        if ($Disabled) {
                            Write-Message -Message "Setting job schedule to disabled" -Level Verbose
                            $JobSchedule.IsEnabled = $false
                        }
                        else {
                            Write-Message -Message "Setting job schedule to enabled" -Level Verbose
                            $JobSchedule.IsEnabled = $true
                        }

                        if ($Interval -ge 0) {
                            Write-Message -Message "Setting job schedule frequency interval to $Interval" -Level Verbose
                            $JobSchedule.FrequencyInterval = $Interval
                        }

                        if ($FrequencyType -ge 1) {
                            Write-Message -Message "Setting job schedule frequency to $FrequencyType" -Level Verbose
                            $JobSchedule.FrequencyTypes = $FrequencyType
                        }

                        if ($FrequencySubdayType -ge 1) {
                            Write-Message -Message "Setting job schedule frequency subday type to $FrequencySubdayType" -Level Verbose
                            $JobSchedule.FrequencySubDayTypes = $FrequencySubdayType
                        }

                        if ($FrequencySubdayInterval -ge 1) {
                            Write-Message -Message "Setting job schedule frequency subday interval to $FrequencySubdayInterval" -Level Verbose
                            $JobSchedule.FrequencySubDayInterval = $FrequencySubdayInterval
                        }

                        if (($FrequencyRelativeInterval -ge 1) -and ($FrequencyType -eq 32)) {
                            Write-Message -Message "Setting job schedule frequency relative interval to $FrequencyRelativeInterval" -Level Verbose
                            $JobSchedule.FrequencyRelativeIntervals = $FrequencyRelativeInterval
                        }

                        if (($FrequencyRecurrenceFactor -ge 1) -and ($FrequencyType -in 8, 16, 32)) {
                            Write-Message -Message "Setting job schedule frequency recurrence factor to $FrequencyRecurrenceFactor" -Level Verbose
                            $JobSchedule.FrequencyRecurrenceFactor = $FrequencyRecurrenceFactor
                        }

                        if ($StartDate) {
                            $StartDate = $StartDate.Insert(6, '-').Insert(4, '-')
                            Write-Message -Message "Setting job schedule start date to $StartDate" -Level Verbose
                            $JobSchedule.ActiveStartDate = $StartDate
                        }

                        if ($EndDate) {
                            $EndDate = $EndDate.Insert(6, '-').Insert(4, '-')
                            Write-Message -Message "Setting job schedule end date to $EndDate" -Level Verbose
                            $JobSchedule.ActiveEndDate = $EndDate
                        }

                        if ($StartTime) {
                            $StartTime = $StartTime.Insert(4, ':').Insert(2, ':')
                            Write-Message -Message "Setting job schedule start time to $StartTime" -Level Verbose
                            $JobSchedule.ActiveStartTimeOfDay = $StartTime
                        }

                        if ($EndTime) {
                            $EndTime = $EndTime.Insert(4, ':').Insert(2, ':')
                            Write-Message -Message "Setting job schedule end time to $EndTime" -Level Verbose
                            $JobSchedule.ActiveEndTimeOfDay = $EndTime
                        }
                        #endregion job schedule options

                        # Create the schedule
                        if ($PSCmdlet.ShouldProcess($SqlInstance, "Adding the schedule $Schedule to job $j on $instance")) {
                            try {
                                Write-Message -Message "Adding the schedule $Schedule to job $j" -Level Verbose
                                #$JobSchedule
                                $JobSchedule.Create()

                                Write-Message -Message "Job schedule created with UID $($JobSchedule.ScheduleUid)" -Level Verbose
                            }
                            catch {
                                Stop-Function -Message "Something went wrong adding the schedule" -Target $instance -ErrorRecord $_ -Continue

                            }

                            # Output the job schedule
                            return $JobSchedule
                        }
                    }
                } # foreach object job
            } # end if job
            else {
                # Create the schedule
                $JobSchedule = New-Object Microsoft.SqlServer.Management.Smo.Agent.JobSchedule($Server.JobServer, $Schedule)

                #region job schedule options
                if ($Disabled) {
                    Write-Message -Message "Setting job schedule to disabled" -Level Verbose
                    $JobSchedule.IsEnabled = $false
                }
                else {
                    Write-Message -Message "Setting job schedule to enabled" -Level Verbose
                    $JobSchedule.IsEnabled = $true
                }

                if ($Interval -ge 1) {
                    Write-Message -Message "Setting job schedule frequency interval to $Interval" -Level Verbose
                    $JobSchedule.FrequencyInterval = $Interval
                }

                if ($FrequencyType -ge 1) {
                    Write-Message -Message "Setting job schedule frequency to $FrequencyType" -Level Verbose
                    $JobSchedule.FrequencyTypes = $FrequencyType
                }

                if ($FrequencySubdayType -ge 1) {
                    Write-Message -Message "Setting job schedule frequency subday type to $FrequencySubdayType" -Level Verbose
                    $JobSchedule.FrequencySubDayTypes = $FrequencySubdayType
                }

                if ($FrequencySubdayInterval -ge 1) {
                    Write-Message -Message "Setting job schedule frequency subday interval to $FrequencySubdayInterval" -Level Verbose
                    $JobSchedule.FrequencySubDayInterval = $FrequencySubdayInterval
                }

                if (($FrequencyRelativeInterval -ge 1) -and ($FrequencyType -eq 32)) {
                    Write-Message -Message "Setting job schedule frequency relative interval to $FrequencyRelativeInterval" -Level Verbose
                    $JobSchedule.FrequencyRelativeIntervals = $FrequencyRelativeInterval
                }

                if (($FrequencyRecurrenceFactor -ge 1) -and ($FrequencyType -in 8, 16, 32)) {
                    Write-Message -Message "Setting job schedule frequency recurrence factor to $FrequencyRecurrenceFactor" -Level Verbose
                    $JobSchedule.FrequencyRecurrenceFactor = $FrequencyRecurrenceFactor
                }

                if ($StartDate) {
                    $StartDate = $StartDate.Insert(6, '-').Insert(4, '-')
                    Write-Message -Message "Setting job schedule start date to $StartDate" -Level Verbose
                    $JobSchedule.ActiveStartDate = $StartDate
                }

                if ($EndDate) {
                    $EndDate = $EndDate.Insert(6, '-').Insert(4, '-')
                    Write-Message -Message "Setting job schedule end date to $EndDate" -Level Verbose
                    $JobSchedule.ActiveEndDate = $EndDate
                }

                if ($StartTime) {
                    $StartTime = $StartTime.Insert(4, ':').Insert(2, ':')
                    Write-Message -Message "Setting job schedule start time to $StartTime" -Level Verbose
                    $JobSchedule.ActiveStartTimeOfDay = $StartTime
                }

                if ($EndTime) {
                    $EndTime = $EndTime.Insert(4, ':').Insert(2, ':')
                    Write-Message -Message "Setting job schedule end time to $EndTime" -Level Verbose
                    $JobSchedule.ActiveEndTimeOfDay = $EndTime
                }

                # Create the schedule
                if ($PSCmdlet.ShouldProcess($SqlInstance, "Adding the schedule $schedule on $instance")) {
                    try {
                        Write-Message -Message "Adding the schedule $JobSchedule on instance $instance" -Level Verbose

                        $JobSchedule.Create()

                        Write-Message -Message "Job schedule created with UID $($JobSchedule.ScheduleUid)" -Level Verbose
                    }
                    catch {
                        Stop-Function -Message "Something went wrong adding the schedule." -Target $instance -ErrorRecord $_ -Continue
                    }

                    # Output the job schedule
                    return $JobSchedule
                }
            }
        } # foreach object instance
    } #process

    end {
        if (Test-FunctionInterrupt) { return }
        Write-Message -Message "Finished creating job schedule(s)." -Level Verbose
    }
}
function New-DbaClientAlias {
    <#
    .SYNOPSIS
    Creates/updates a sql alias for the specified server - mimics cliconfg.exe

    .DESCRIPTION
    Creates/updates a SQL Server alias by altering HKLM:\SOFTWARE\Microsoft\MSSQLServer\Client

    .PARAMETER ComputerName
    The target computer where the alias will be created

    .PARAMETER Credential
    Allows you to login to remote computers using alternative credentials

    .PARAMETER ServerName
    The target SQL Server

    .PARAMETER Alias
    The alias to be created

    .PARAMETER Protocol
    The protocol for the connection, either TCPIP or NetBIOS. Defaults to TCPIP.

    .PARAMETER EnableException
    By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
    Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
    Tags: Alias

    Website: https://dbatools.io
    Copyright: (C) Chrissy LeMaire, [email protected]
    License: MIT https://opensource.org/licenses/MIT

    .LINK
    https://dbatools.io/New-DbaClientAlias

    .EXAMPLE
    New-DbaClientAlias -ServerName sqlcluster\sharepoint -Alias sp
    Creates a new TCP alias on the local workstation called sp, which points sqlcluster\sharepoint


    .EXAMPLE
    New-DbaClientAlias -ServerName 'sqlcluster,14443' -Alias spinstance
    Creates a new TCP alias on the local workstation called spinstance, which points to sqlcluster, port 14443.

    .EXAMPLE
    New-DbaClientAlias -ServerName sqlcluster\sharepoint -Alias sp -Protocol NamedPipes
    Creates a new NamedPipes alias on the local workstation called sp, which points sqlcluster\sharepoint

#>
    [CmdletBinding()]
    Param (
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [parameter(Mandatory, ValueFromPipeline)]
        [DbaInstanceParameter[]]$ServerName,
        [parameter(Mandatory)]
        [string]$Alias,
        [ValidateSet("TCPIP", "NamedPipes")]
        [string]$Protocol = "TCPIP",
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        # This is a script block so cannot use messaging system
        $scriptblock = {
            $basekeys = "HKLM:\SOFTWARE\WOW6432Node\Microsoft\MSSQLServer", "HKLM:\SOFTWARE\Microsoft\MSSQLServer"
            $ServerName = $args[0]
            $Alias = $args[1]
            $serverstring = $args[2]

            if ($env:PROCESSOR_ARCHITECTURE -like "*64*") { $64bit = $true }

            foreach ($basekey in $basekeys) {
                if ($64bit -ne $true -and $basekey -like "*WOW64*") { continue }

                if ((Test-Path $basekey) -eq $false) {
                    throw "Base key ($basekey) does not exist. Quitting."
                }

                $client = "$basekey\Client"

                if ((Test-Path $client) -eq $false) {
                    # "Creating $client key"
                    $null = New-Item -Path $client -Force
                }

                $connect = "$client\ConnectTo"

                if ((Test-Path $connect) -eq $false) {
                    # "Creating $connect key"
                    $null = New-Item -Path $connect -Force
                }

                if ($basekey -like "*WOW64*") {
                    $architecture = "32-bit"
                }
                else {
                    $architecture = "64-bit"
                }

                # Write-Verbose "Creating/updating alias for $ComputerName for $architecture"
                $null = New-ItemProperty -Path $connect -Name $Alias -Value $serverstring -PropertyType String -Force
            }
        }
    }

    process {
        if ($protocol -eq "TCPIP") {
            $serverstring = "DBMSSOCN,$ServerName"
        }
        else {
            $serverstring = "DBNMPNTW,\\$ServerName\pipe\sql\query"
        }

        foreach ($computer in $ComputerName.ComputerName) {

            $null = Test-ElevationRequirement -ComputerName $computer -Continue

            if ($PScmdlet.ShouldProcess($computer, "Adding $alias")) {
                try {
                    Invoke-Command2 -ComputerName $computer -Credential $Credential -ScriptBlock $scriptblock -ErrorAction Stop -ArgumentList $ServerName, $Alias, $serverstring
                }
                catch {
                    Stop-Function -Message "Failure" -ErrorRecord $_ -Target $computer -Continue
                }
            }
        }

        Get-DbaClientAlias -ComputerName $computer -Credential $Credential | Where-Object AliasName -eq $Alias
    }
}
function New-DbaCmConnection {
    <#
        .SYNOPSIS
            Generates a connection object for use in remote computer management.

        .DESCRIPTION
            Generates a connection object for use in remote computer management.
            Those objects are used for the purpose of cim/wmi queries, caching which protocol worked, optimizing performance and minimizing authentication errors.

            New-DbaCmConnection will create a NEW object and overwrite any existing ones for the specified computer.
            Furthermore, information stored in the input beyond the computername will be discarded in favor of the new settings.

            Unless the connection cache has been disabled, all connections will automatically be registered in the cache, so no further action is necessary.
            The output is primarily for information purposes, however it may be used to pass objects and circumvent the cache with those.

            NOTE: Generally, this function need not be used, as a first connection to a computer using any connecting function such as "Get-DbaCmObject" will automatically register a new default connection for it.

            This function exists to be able to preconfigure connections.

        .PARAMETER ComputerName
            The computer to build the connection object for.

        .PARAMETER Credential
            The credential to register.

        .PARAMETER UseWindowsCredentials
            Whether using the default windows credentials is legit.
            Not setting this will not exclude using windows credentials, but only not pre-confirm them as working.

        .PARAMETER OverrideExplicitCredential
            Setting this will enable the credential override.
            The override will cause the system to ignore explicitly specified credentials, so long as known, good credentials are available.

        .PARAMETER DisabledConnectionTypes
            Exlicitly disable connection types.
            These types will then not be used for connecting to the computer.

        .PARAMETER DisableBadCredentialCache
            Will prevent the caching of credentials if set to true.

        .PARAMETER DisableCimPersistence
            Will prevent Cim-Sessions to be reused.

        .PARAMETER DisableCredentialAutoRegister
            Will prevent working credentials from being automatically cached

        .PARAMETER EnableCredentialFailover
            Will enable automatic failing over to known to work credentials, when using bad credentials.
            By default, passing bad credentials will cause the Computer Management functions to interrupt with a warning (Or exception if in silent mode).

        .PARAMETER WindowsCredentialsAreBad
            Will prevent the windows credentials of the currently logged on user from being used for the remote connection.

        .PARAMETER CimWinRMOptions
            Specify a set of options to use when connecting to the target computer using CIM over WinRM.
            Use 'New-CimSessionOption' to create such an object.

        .PARAMETER CimDCOMOptions
            Specify a set of options to use when connecting to the target computer using CIM over DCOM.
            Use 'New-CimSessionOption' to create such an object.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ComputerManagement, CIM
            Author: Fred Winmann (@FredWeinmann)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/New-DbaCmConnection

        .EXAMPLE
            New-DbaCmConnection -ComputerName sql2014 -UseWindowsCredentials -OverrideExplicitCredential -DisabledConnectionTypes CimRM

            Returns a new configuration object for connecting to the computer sql2014.
            - The current user credentials are set as valid
            - The connection is configured to ignore explicit credentials (so all connections use the windows credentials)
            - The connections will not try using CIM over WinRM

            Unless caching is globally disabled, this is automatically stored in the connection cache and will be applied automatically.
            In that (the default) case, the output is for information purposes only and need not be used.

        .EXAMPLE
            Get-Content computers.txt | New-DbaCmConnection -Credential $cred -CimWinRMOptions $options -DisableBadCredentialCache -OverrideExplicitCredential

            Gathers a list of computers from a text file, then creates and registers connections for each of them, setting them to ...
            - use the credentials stored in $cred
            - use the options stored in $options when connecting using CIM over WinRM
            - not store credentials that are known to not work
            - to ignore explicitly specified credentials

            Essentially, this configures all connections to those computers to prefer failure with the specified credentials over using alternative credentials.
    #>
    [CmdletBinding(DefaultParameterSetName = 'Credential')]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Sqlcollaborative.Dbatools.Parameter.DbaCmConnectionParameter[]]
        $ComputerName = $env:COMPUTERNAME,
        [Parameter(ParameterSetName = "Credential")]
        [PSCredential]
        $Credential,
        [Parameter(ParameterSetName = "Windows")]
        [switch]
        $UseWindowsCredentials,
        [switch]
        $OverrideExplicitCredential,
        [Sqlcollaborative.Dbatools.Connection.ManagementConnectionType]
        $DisabledConnectionTypes = 'None',
        [switch]
        $DisableBadCredentialCache,
        [switch]
        $DisableCimPersistence,
        [switch]
        $DisableCredentialAutoRegister,
        [switch]
        $EnableCredentialFailover,
        [Parameter(ParameterSetName = "Credential")]
        [switch]
        $WindowsCredentialsAreBad,
        [Microsoft.Management.Infrastructure.Options.WSManSessionOptions]
        $CimWinRMOptions,
        [Microsoft.Management.Infrastructure.Options.DComSessionOptions]
        $CimDCOMOptions,
        [switch]
        [Alias('Silent')]$EnableException
    )

    begin {
        Write-Message -Level InternalComment -Message "Starting execution"
        Write-Message -Level Verbose -Message "Bound parameters: $($PSBoundParameters.Keys -join ", ")"

        $disable_cache = Get-DbaConfigValue -Name 'ComputerManagement.Cache.Disable.All' -Fallback $false
    }
    process {
        foreach ($connectionObject in $ComputerName) {
            if (-not $connectionObject.Success) { Stop-Function -Message "Failed to interpret computername input: $($connectionObject.InputObject)" -Category InvalidArgument -Target $connectionObject.InputObject -Continue }
            Write-Message -Level VeryVerbose -Message "Processing computer: $($connectionObject.Connection.ComputerName)" -Target $connectionObject.Connection

            $connection = New-Object -TypeName Sqlcollaborative.Dbatools.Connection.ManagementConnection -ArgumentList $connectionObject.Connection.ComputerName
            if (Test-Bound "Credential") { $connection.Credentials = $Credential }
            if (Test-Bound "UseWindowsCredentials") {
                $connection.Credentials = $null
                $connection.UseWindowsCredentials = $UseWindowsCredentials
            }
            if (Test-Bound "OverrideExplicitCredential") { $connection.OverrideExplicitCredential = $OverrideExplicitCredential }
            if (Test-Bound "DisabledConnectionTypes") { $connection.DisabledConnectionTypes = $DisabledConnectionTypes }
            if (Test-Bound "DisableBadCredentialCache") { $connection.DisableBadCredentialCache = $DisableBadCredentialCache }
            if (Test-Bound "DisableCimPersistence") { $connection.DisableCimPersistence = $DisableCimPersistence }
            if (Test-Bound "DisableCredentialAutoRegister") { $connection.DisableCredentialAutoRegister = $DisableCredentialAutoRegister }
            if (Test-Bound "EnableCredentialFailover") { $connection.DisableCredentialAutoRegister = $EnableCredentialFailover }
            if (Test-Bound "WindowsCredentialsAreBad") { $connection.WindowsCredentialsAreBad = $WindowsCredentialsAreBad }
            if (Test-Bound "CimWinRMOptions") { $connection.CimWinRMOptions = $CimWinRMOptions }
            if (Test-Bound "CimDCOMOptions") { $connection.CimDCOMOptions = $CimDCOMOptions }

            if (-not $disable_cache) {
                Write-Message -Level Verbose -Message "Writing connection to cache"
                [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::Connections[$connectionObject.Connection.ComputerName] = $connection
            }
            else { Write-Message -Level Verbose -Message "Skipping writing to cache, since the cache has been disabled!" }
            $connection
        }
    }
    end {
        Write-Message -Level InternalComment -Message "Stopping execution"
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function New-DbaComputerCertificate {
    <#
        .SYNOPSIS
            Creates a new computer certificate useful for Forcing Encryption

        .DESCRIPTION
            Creates a new computer certificate - self-signed or signed by an Active Directory CA, using the Web Server certificate.

            By default, a key with a length of 1024 and a friendly name of the machines FQDN is generated.

            This command was originally intended to help automate the process so that SSL certificates can be available for enforcing encryption on connections.

            It makes a lot of assumptions - namely, that your account is allowed to auto-enroll and that you have permission to do everything it needs to do ;)

            References:
            http://sqlmag.com/sql-server/7-steps-ssl-encryption
            https://azurebi.jppp.org/2016/01/23/using-lets-encrypt-certificates-for-secure-sql-server-connections/
            https://blogs.msdn.microsoft.com/sqlserverfaq/2016/09/26/creating-and-registering-ssl-certificates/

            The certificate is generated using AD's webserver SSL template on the client machine and pushed to the remote machine.

        .PARAMETER ComputerName
            The target SQL Server - defaults to localhost. If target is a cluster, you must also specify ClusterInstanceName (see below)

        .PARAMETER Credential
            Allows you to login to $ComputerName using alternative credentials.

        .PARAMETER CaServer
            Optional - the CA Server where the request will be sent to

        .PARAMETER CaName
            The properly formatted CA name of the corresponding CaServer

        .PARAMETER ClusterInstanceName
            When creating certs for a cluster, use this parameter to create the certificate for the cluster node name. Use ComputerName for each of the nodes.

        .PARAMETER Password
            Password to encrypt/decrypt private key for export to remote machine

        .PARAMETER FriendlyName
            The FriendlyName listed in the certificate. This defaults to the FQDN of the $ComputerName

        .PARAMETER CertificateTemplate
            The domain's Certificate Template - WebServer by default.

        .PARAMETER KeyLength
            The length of the key - defaults to 1024

        .PARAMETER Store
            Certificate store - defaults to LocalMachine

        .PARAMETER Folder
            Certificate folder - defaults to My (Personal)

        .PARAMETER Dns
            Specify the Dns entries listed in SAN. By default, it will be ComputerName + FQDN, or in the case of clusters, clustername + cluster FQDN.

        .PARAMETER SelfSigned
            Creates a self-signed certificate. All other parameters can still apply except CaServer and CaName because the command does not go and get the certificate signed.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .NOTES
            Tags: Certificate

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            New-DbaComputerCertificate

            Creates a computer certificate signed by the local domain CA for the local machine with the keylength of 1024.

        .EXAMPLE
            New-DbaComputerCertificate -ComputerName Server1

            Creates a computer certificate signed by the local domain CA _on the local machine_ for server1 with the keylength of 1024.

            The certificate is then copied to the new machine over WinRM and imported.

        .EXAMPLE
            New-DbaComputerCertificate -ComputerName sqla, sqlb -ClusterInstanceName sqlcluster -KeyLength 4096

            Creates a computer certificate for sqlcluster, signed by the local domain CA, with the keylength of 4096.

            The certificate is then copied to sqla _and_ sqlb over WinRM and imported.

        .EXAMPLE
            New-DbaComputerCertificate -ComputerName Server1 -WhatIf

            Shows what would happen if the command were run

        .EXAMPLE
            New-DbaComputerCertificate -SelfSigned

            Creates a self-signed certificate
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param (
        [parameter(ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer", "SqlInstance")]
        [DbaInstance[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [string]$CaServer,
        [string]$CaName,
        [string]$ClusterInstanceName,
        [securestring]$Password,
        [string]$FriendlyName = "SQL Server",
        [string]$CertificateTemplate = "WebServer",
        [int]$KeyLength = 1024,
        [string]$Store = "LocalMachine",
        [string]$Folder = "My",
        [string[]]$Dns,
        [switch]$SelfSigned,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        $englishCodes = 9, 1033, 2057, 3081, 4105, 5129, 6153, 7177, 8201, 9225
        if ($englishCodes -notcontains (Get-DbaCmObject Win32_OperatingSystem).OSLanguage) {
            Stop-Function -Message "Currently, this command is only supported in English OS locales. OS Locale detected: $([System.Globalization.CultureInfo]::GetCultureInfo([int](Get-DbaCmObject Win32_OperatingSystem).OSLanguage).DisplayName)`nWe apologize for the inconvenience and look into providing universal language support in future releases."
            return
        }

        if (-not (Test-ElevationRequirement -ComputerName $env:COMPUTERNAME)) {
            return
        }

        function GetHexLength {
            [cmdletbinding()]
            param(
                [int]$strLen
            )
            $hex = [String]::Format("{0:X2}", $strLen)

            if ($strLen -gt 127) { [String]::Format("{0:X2}", 128 + ($hex.Length / 2)) + $hex }
            else { $hex }
        }

        function Get-SanExt {
            [cmdletbinding()]
            param(
                [string[]]$hostName
            )
            # thanks to Lincoln of
            # https://social.technet.microsoft.com/Forums/windows/en-US/f568edfa-7f93-46a4-aab9-a06151592dd9/converting-ascii-to-asn1-der

            $temp = ''
            foreach ($fqdn in $hostName) {
                # convert each character of fqdn to hex
                $hexString = ($fqdn.ToCharArray() | ForEach-Object { [String]::Format("{0:X2}", [int]$_) }) -join ''

                # length of hex fqdn, in hex
                $hexLength = GetHexLength ($hexString.Length / 2)

                # concatenate special code 82, hex length, hex string
                $temp += "82${hexLength}${hexString}"
            }
            # calculate total length of concatenated string, in hex
            $totalHexLength = GetHexLength ($temp.Length / 2)
            # concatenate special code 30, hex length, hex string
            $temp = "30${totalHexLength}${temp}"
            # convert to binary
            $bytes = $(
                for ($i = 0; $i -lt $temp.Length; $i += 2) {
                    [byte]"0x$($temp.SubString($i, 2))"
                }
            )
            # convert to base 64
            $base64 = [Convert]::ToBase64String($bytes)
            # output in proper format
            for ($i = 0; $i -lt $base64.Length; $i += 64) {
                $line = $base64.SubString($i, [Math]::Min(64, $base64.Length - $i))
                if ($i -eq 0) { "2.5.29.17=$line" }
                else { "_continue_=$line" }
            }
        }

        if ((!$CaServer -or !$CaName) -and !$SelfSigned) {
            try {
                Write-Message -Level Verbose -Message "No CaServer or CaName specified. Performing lookup."
                # hat tip Vadims Podans
                $domain = ([System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()).Name
                $domain = "DC=" + $domain -replace '\.', ", DC="
                $pks = [ADSI]"LDAP://CN=Enrollment Services, CN=Public Key Services, CN=Services, CN=Configuration, $domain"
                $cas = $pks.psBase.Children

                $allCas = @()
                foreach ($ca in $cas) {
                    $allCas += [pscustomobject]@{
                        CA       = $ca | ForEach-Object { $_.Name }
                        Computer = $ca | ForEach-Object { $_.DNSHostName }
                    }
                }
            }
            catch {
                Stop-Function -Message "Cannot access Active Directory or find the Certificate Authority" -ErrorRecord $_
                return
            }

            if (!$CaServer) {
                $CaServer = ($allCas | Select-Object -First 1).Computer
                Write-Message -Level Verbose -Message "Root Server: $CaServer"
            }

            if (!$CaName) {
                $CaName = ($allCas | Select-Object -First 1).CA
                Write-Message -Level Verbose -Message "Root CA name: $CaName"
            }
        }

        $tempDir = ([System.IO.Path]::GetTempPath()).TrimEnd("\")
        $certTemplate = "CertificateTemplate:$CertificateTemplate"
    }

    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($computer in $ComputerName) {

            if (!$secondaryNode) {

                if ($ClusterInstanceName) {
                    if ($ClusterInstanceName -notmatch "\.") {
                        $fqdn = "$ClusterInstanceName.$env:USERDNSDOMAIN"
                    }
                    else {
                        $fqdn = $ClusterInstanceName
                    }
                }
                else {
                    $resolved = Resolve-DbaNetworkName -ComputerName $computer.ComputerName -WarningAction SilentlyContinue

                    if (!$resolved) {
                        $fqdn = "$ComputerName.$env:USERDNSDOMAIN"
                        Write-Message -Level Warning -Message "Server name cannot be resolved. Guessing it's $fqdn"
                    }
                    else {
                        $fqdn = $resolved.fqdn
                    }
                }

                $certDir = "$tempDir\$fqdn"
                $certCfg = "$certDir\request.inf"
                $certCsr = "$certDir\$fqdn.csr"
                $certCrt = "$certDir\$fqdn.crt"
                $certPfx = "$certDir\$fqdn.pfx"
                $tempPfx = "$certDir\temp-$fqdn.pfx"

                if (Test-Path($certDir)) {
                    Write-Message -Level Output -Message "Deleting files from $certDir"
                    $null = Remove-Item "$certDir\*.*"
                }
                else {
                    Write-Message -Level Output -Message "Creating $certDir"
                    $null = New-Item -Path $certDir -ItemType Directory -Force
                }

                # Make sure output is compat with clusters
                $shortName = $fqdn.Split(".")[0]

                if (!$dns) {
                    $dns = $shortName, $fqdn
                }

                $san = Get-SanExt $dns
                # Write config file
                Set-Content $certCfg "[Version]"
                Add-Content $certCfg 'Signature="$Windows NT$"'
                Add-Content $certCfg "[NewRequest]"
                Add-Content $certCfg "Subject = ""CN=$fqdn"""
                Add-Content $certCfg "KeySpec = 1"
                Add-Content $certCfg "KeyLength = $KeyLength"
                Add-Content $certCfg "Exportable = TRUE"
                Add-Content $certCfg "MachineKeySet = TRUE"
                Add-Content $certCfg "FriendlyName=""$FriendlyName"""
                Add-Content $certCfg "SMIME = False"
                Add-Content $certCfg "PrivateKeyArchive = FALSE"
                Add-Content $certCfg "UserProtected = FALSE"
                Add-Content $certCfg "UseExistingKeySet = FALSE"
                Add-Content $certCfg "ProviderName = ""Microsoft RSA SChannel Cryptographic Provider"""
                Add-Content $certCfg "ProviderType = 12"
                if ($SelfSigned) {
                    Add-Content $certCfg "RequestType = Cert"
                }
                else {
                    Add-Content $certCfg "RequestType = PKCS10"
                }
                Add-Content $certCfg "KeyUsage = 0xa0"
                Add-Content $certCfg "[EnhancedKeyUsageExtension]"
                Add-Content $certCfg "OID=1.3.6.1.5.5.7.3.1"
                Add-Content $certCfg "[Extensions]"
                Add-Content $certCfg $san
                Add-Content $certCfg "Critical=2.5.29.17"


                if ($PScmdlet.ShouldProcess("local", "Creating certificate for $computer")) {
                    Write-Message -Level Output -Message "Running: certreq -new $certCfg $certCsr"
                    $create = certreq -new $certCfg $certCsr
                }

                if ($SelfSigned) {
                    $serial = (($create -Split "Serial Number:" -Split "Subject")[2]).Trim() # D:
                    $storedCert = Get-ChildItem Cert:\LocalMachine\My -Recurse | Where-Object SerialNumber -eq $serial

                    if ($computer.IsLocalHost) {
                        $storedCert | Select-Object * | Select-DefaultView -Property FriendlyName, DnsNameList, Thumbprint, NotBefore, NotAfter, Subject, Issuer
                    }
                }
                else {
                    if ($PScmdlet.ShouldProcess("local", "Submitting certificate request for $computer to $CaServer\$CaName")) {
                        Write-Message -Level Output -Message "certreq -submit -config `"$CaServer\$CaName`" -attrib $certTemplate $certCsr $certCrt $certPfx"
                        $submit = certreq -submit -config ""$CaServer\$CaName"" -attrib $certTemplate $certCsr $certCrt $certPfx
                    }

                    if ($submit -match "ssued") {
                        Write-Message -Level Output -Message "certreq -accept -machine $certCrt"
                        $null = certreq -accept -machine $certCrt
                        $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
                        $cert.Import($certCrt, $null, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::DefaultKeySet)
                        $storedCert = Get-ChildItem "Cert:\$store\$folder" -Recurse | Where-Object { $_.Thumbprint -eq $cert.Thumbprint }
                    }
                    elseif ($submit) {
                        Write-Message -Level Warning -Message "Something went wrong"
                        Write-Message -Level Warning -Message "$create"
                        Write-Message -Level Warning -Message "$submit"
                        Stop-Function -Message "Failure when attempting to create the cert on $computer. Exception: $_" -ErrorRecord $_ -Target $computer -Continue
                    }

                    if ($Computer.IsLocalHost) {
                        $storedCert | Select-Object * | Select-DefaultView -Property FriendlyName, DnsNameList, Thumbprint, NotBefore, NotAfter, Subject, Issuer
                    }
                }
            }

            if (!$Computer.IsLocalHost) {

                if (!$secondaryNode) {
                    if ($PScmdlet.ShouldProcess("local", "Generating pfx and reading from disk")) {
                        Write-Message -Level Output -Message "Exporting PFX with password to $tempPfx"
                        $certdata = $storedCert.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::PFX, $password)
                    }

                    if ($PScmdlet.ShouldProcess("local", "Removing cert from disk but keeping it in memory")) {
                        $storedCert | Remove-Item
                    }

                    if ($ClusterInstanceName) { $secondaryNode = $true }
                }

                $scriptblock = {
                    $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
                    $cert.Import($args[0], $args[1], "Exportable,PersistKeySet")

                    $certstore = New-Object System.Security.Cryptography.X509Certificates.X509Store($args[3], $args[2])
                    $certstore.Open('ReadWrite')
                    $certstore.Add($cert)
                    $certstore.Close()
                    Get-ChildItem "Cert:\$($args[2])\$($args[3])" -Recurse | Where-Object { $_.Thumbprint -eq $cert.Thumbprint }
                }

                if ($PScmdlet.ShouldProcess("local", "Connecting to $computer to import new cert")) {
                    try {
                        Write-Message -Level Output -Message "Connecting to $computer"
                        Invoke-Command2 -ComputerName $computer -Credential $Credential -ArgumentList $certdata, $Password, $Store, $Folder -ScriptBlock $scriptblock -ErrorAction Stop |
                            Select-DefaultView -Property DnsNameList, Thumbprint, NotBefore, NotAfter, Subject, Issuer
                    }
                    catch {
                        Stop-Function -Message "Issue importing new cert on $computer" -ErrorRecord $_ -Target $computer -Continue
                    }
                }
            }
            if ($PScmdlet.ShouldProcess("local", "Removing all files from $certDir")) {
                try {
                    Remove-Item -Force -Recurse $certDir -ErrorAction SilentlyContinue
                }
                catch {
                    Stop-Function "Isue removing files from $certDir" -Target $certDir -ErrorRecord $_
                }
            }
        }
    }
}
function New-DbaCredential {
    <#
.SYNOPSIS
Creates a new SQL Server credential

.DESCRIPTION
Creates a new credential

.PARAMETER SqlInstance
The target SQL Server(s)

.PARAMETER SqlCredential
Allows you to login to SQL Server using alternative credentials

.PARAMETER Name
The Credential name

.PARAMETER Identity
The Credential Identity

.PARAMETER Password
Secure string used to authenticate the Credential Identity

.PARAMETER MappedClassType
Sets the class associated with the credential.

.PARAMETER ProviderName
Sets the name of the provider

.PARAMETER Force
If credential exists, drop and recreate

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Certificate

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
New-DbaCredential -SqlInstance Server1

You will be prompted to securely enter your password, then a credential will be created in the master database on server1 if it does not exist.

.EXAMPLE
New-DbaCredential -SqlInstance Server1 -Database db1 -Confirm:$false

Suppresses all prompts to install but prompts to securely enter your password and creates a credential in the 'db1' database

.EXAMPLE
New-DbaCredential -SqlInstance Server1 -Name AzureBackupBlobStore -Identity '<Azure Storage Account Name>' -Password (ConvertTo-SecureString '<Azure Storage Account Access Key>' -AsPlainText -Force)

Create credential on SQL Server 2012 CU2, SQL Server 2014 for use with BACKUP TO URL.
CredentialIdentity needs to be supplied with the Azure Storage Account Name.
Password needs to be one of the Access Keys for the account.

.EXAMPLE
New-DbaCredential -SqlInstance Server1 -Name 'https://<Azure Storage Account Name>.blob.core.windows.net/<Blob Store Container Name>' -Identity 'SHARED ACCESS SIGNATURE' -Password (ConvertTo-SecureString '<Shared Access Token>' -AsPlainText -Force)

Create Credential on SQL Server 2016 or higher for use with BACKUP TO URL.
Name has to be the full URL for the blob store container that will be the backup target.
Password needs to be passed the Shared Access Token (SAS Key).

#>
    [CmdletBinding(SupportsShouldProcess = $true)] #, ConfirmImpact = "High"
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Name = $Identity,
        [parameter(Mandatory)]
        [Alias("CredentialIdentity")]
        [string[]]$Identity,
        [Security.SecureString]$Password,
        [ValidateSet('CryptographicProvider', 'None')]
        [string]$MappedClassType = "None",
        [string]$ProviderName,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $mappedclass = switch ($MappedClassType) {
            "CryptographicProvider" { 1 }
            "None" { 0 }
        }
    }

    process {
        if (!$Password) {
            Read-Host -AsSecureString -Prompt "Enter the credential password"
        }

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            foreach ($cred in $Identity) {
                $currentcred = $server.Credentials[$name]

                if ($currentcred) {
                    if ($force) {
                        Write-Message -Level Verbose -Message "Dropping credential $name"
                        $currentcred.Drop()
                    }
                    else {
                        Stop-Function -Message "Credential exists and Force was not specified" -Target $name -Continue
                    }
                }


                if ($Pscmdlet.ShouldProcess($SqlInstance, "Creating credential for database '$cred' on $instance")) {
                    try {
                        $credential = New-Object Microsoft.SqlServer.Management.Smo.Credential -ArgumentList $server, $name
                        $credential.MappedClassType = $mappedclass
                        $credential.ProviderName = $ProviderName
                        $credential.Create($Identity, $Password)

                        Add-Member -Force -InputObject $credential -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                        Add-Member -Force -InputObject $credential -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                        Add-Member -Force -InputObject $credential -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName

                        Select-DefaultView -InputObject $credential -Property ComputerName, InstanceName, SqlInstance, Name, Identity, CreateDate, MappedClassType, ProviderName
                    }
                    catch {
                        Stop-Function -Message "Failed to create credential in $cred on $instance. Exception: $($_.Exception.InnerException)" -Target $credential -InnerErrorRecord $_ -Continue
                    }
                }
            }
        }
    }
}
function New-DbaDatabaseMasterKey {
    <#
.SYNOPSIS
Creates a new database master key

.DESCRIPTION
Creates a new database master key. If no database is specified, the master key will be created in master.

.PARAMETER SqlInstance
The SQL Server to create the certificates on.

.PARAMETER SqlCredential
Allows you to login to SQL Server using alternative credentials.

.PARAMETER Database
The database where the master key will be created. Defaults to master.

.PARAMETER Password
Secure string used to create the key.

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Certificate

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
New-DbaDatabaseMasterKey -SqlInstance Server1

You will be prompted to securely enter your password, then a master key will be created in the master database on server1 if it does not exist.

.EXAMPLE
New-DbaDatabaseMasterKey -SqlInstance Server1 -Database db1 -Confirm:$false

Suppresses all prompts to install but prompts to securely enter your password and creates a master key in the 'db1' database


#>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High")]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Database = "master",
        [parameter(Mandatory)]
        [Security.SecureString]$Password = (Read-Host "Password" -AsSecureString),
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            foreach ($db in $Database) {
                $smodb = $server.Databases[$db]

                if ($null -eq $smodb) {
                    Stop-Function -Message "Database '$db' does not exist on $instance" -Target $smodb -Continue
                }

                if ($null -ne $smodb.MasterKey) {
                    Stop-Function -Message "Master key already exists in the $db database on $instance" -Target $smodb -Continue
                }

                if ($Pscmdlet.ShouldProcess($SqlInstance, "Creating master key for database '$db' on $instance")) {
                    try {
                        $masterkey = New-Object Microsoft.SqlServer.Management.Smo.MasterKey $smodb
                        $masterkey.Create(([System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($password))))

                        Add-Member -Force -InputObject $masterkey -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                        Add-Member -Force -InputObject $masterkey -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                        Add-Member -Force -InputObject $masterkey -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                        Add-Member -Force -InputObject $masterkey -MemberType NoteProperty -Name Database -value $smodb

                        Select-DefaultView -InputObject $masterkey -Property ComputerName, InstanceName, SqlInstance, Database, CreateDate, DateLastModified, IsEncryptedByServer
                    }
                    catch {
                        Stop-Function -Message "Failed to create master key in $db on $instance. Exception: $($_.Exception.InnerException)" -Target $masterkey -InnerErrorRecord $_ -Continue
                    }
                }
            }
        }
    }
}
function New-DbaDbCertificate {
    <#
.SYNOPSIS
Creates a new database certificate

.DESCRIPTION
Creates a new database certificate. If no database is specified, the certificate will be created in master.

.PARAMETER SqlInstance
The SQL Server to create the certificates on.

.PARAMETER SqlCredential
Allows you to login to SQL Server using alternative credentials.

.PARAMETER Database
The database where the certificate will be created. Defaults to master.

.PARAMETER Name
Optional name to create the certificate. Defaults to database name.

.PARAMETER Subject
Optional subject to create the certificate.

.PARAMETER StartDate
Optional secure string used to create the certificate.

.PARAMETER ExpirationDate
Optional secure string used to create the certificate.

.PARAMETER ActiveForServiceBrokerDialog
Optional secure string used to create the certificate.

.PARAMETER Password
Optional password - if no password is supplied, the password will be protected by the master key

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Certificate

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
New-DbaDbCertificate -SqlInstance Server1

You will be prompted to securely enter your password, then a certificate will be created in the master database on server1 if it does not exist.

.EXAMPLE
New-DbaDbCertificate -SqlInstance Server1 -Database db1 -Confirm:$false

Suppresses all prompts to install but prompts to securely enter your password and creates a certificate in the 'db1' database
#>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$Name,
        [object[]]$Database = "master",
        [string[]]$Subject,
        [datetime]$StartDate = (Get-Date),
        [datetime]$ExpirationDate = $StartDate.AddYears(5),
        [switch]$ActiveForServiceBrokerDialog,
        [Security.SecureString]$Password,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias New-DbaDatabaseCertificate
    }
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            foreach ($db in $Database) {

                $currentdb = $server.Databases[$db] | Where-Object IsAccessible

                if ($null -eq $currentdb) {
                    Stop-Function -Message "Database '$db' does not exist on $instance" -Target $server -Continue
                }

                if ($null -eq $name) {
                    Write-Message -Level Verbose -Message "Name is NULL, setting it to '$db'"
                    $name = $db
                }
                if ($null -eq $subject) {
                    Write-Message -Level Verbose -Message "Subject is NULL, setting it to '$db Database Certificate'"
                    $subject = "$db Database Certificate"
                }

                foreach ($cert in $name) {
                    if ($null -ne $currentdb.Certificates[$cert]) {
                        Stop-Function -Message "Certificate '$cert' already exists in the $db database on $instance" -Target $currentdb -Continue
                    }

                    if ($Pscmdlet.ShouldProcess($SqlInstance, "Creating certificate for database '$db' on $instance")) {
                        try {
                            $smocert = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Certificate $currentdb, $cert

                            $smocert.StartDate = $StartDate
                            $smocert.Subject = $Subject
                            $smocert.ExpirationDate = $ExpirationDate
                            $smocert.ActiveForServiceBrokerDialog = $ActiveForServiceBrokerDialog

                            if ($password.Length -gt 0) {
                                $smocert.Create(([System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($password))))
                            }
                            else {
                                $smocert.Create()
                            }

                            Add-Member -Force -InputObject $smocert -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                            Add-Member -Force -InputObject $smocert -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                            Add-Member -Force -InputObject $smocert -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                            Add-Member -Force -InputObject $smocert -MemberType NoteProperty -Name Database -value $currentdb.Name

                            Select-DefaultView -InputObject $smocert -Property ComputerName, InstanceName, SqlInstance, Database, Name, Subject, StartDate, ActiveForServiceBrokerDialog, ExpirationDate, Issuer, LastBackupDate, Owner, PrivateKeyEncryptionType, Serial
                        }
                        catch {
                            if ($_.Exception.InnerException) {
                                $exception = $_.Exception.InnerException.ToString() -Split "System.Data.SqlClient.SqlException: "
                                $exception = ($exception[1] -Split "at Microsoft.SqlServer.Management.Common.ConnectionManager")[0]
                            }
                            else {
                                $exception = $_.Exception
                            }

                            Stop-Function -Message "Failed to create certificate in $db on $instance. Exception: $exception" -Target $smocert -InnerErrorRecord $_ -Continue
                        }
                    }
                }
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function New-DbaDbSnapshot {
    <#
    .SYNOPSIS
        Creates database snapshots

    .DESCRIPTION
        Creates database snapshots without hassles

    .PARAMETER SqlInstance
        The SQL Server that you're connecting to.

    .PARAMETER SqlCredential
        Credential object used to connect to the SQL Server as a different user

    .PARAMETER AllDatabases
        Creates snapshot for all eligible databases

    .PARAMETER Database
        The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

    .PARAMETER ExcludeDatabase
        The database(s) to exclude - this list is auto-populated from the server

    .PARAMETER WhatIf
        Shows what would happen if the command were to run

    .PARAMETER Confirm
        Prompts for confirmation of every step.

    .PARAMETER Name
        The specific snapshot name you want to create. Works only if you target a single database. If you need to create multiple snapshot,
        you must use the NameSuffix parameter

    .PARAMETER NameSuffix
        When you pass a simple string, it'll be appended to use it to build the name of the snapshot. By default snapshots are created with yyyyMMdd_HHmmss suffix
        You can also pass a standard placeholder, in which case it'll be interpolated (e.g. '{0}' gets replaced with the database name)

    .PARAMETER Path
        Snapshot files will be created here (by default the filestructure will be created in the same folder as the base db)

    .PARAMETER InputObject
       Allows Piping from Get-DbaDatabase

    .PARAMETER Force
        Databases with Filestream FG can be snapshotted, but the Filestream FG is marked offline
        in the snapshot. To create a "partial" snapshot, you need to pass -Force explicitely

        NB: You can't then restore the Database from the newly-created snapshot.
        For details, check https://msdn.microsoft.com/en-us/library/bb895334.aspx

    .PARAMETER EnableException
                By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
                This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
                Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Tags: Snapshot, Restore, Database
        Author: niphlod

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
         https://dbatools.io/New-DbaDbSnapshot

    .EXAMPLE
        New-DbaDbSnapshot -SqlInstance sqlserver2014a -Database HR, Accounting

        Creates snapshot for HR and Accounting, returning a custom object displaying Server, Database, DatabaseCreated, SnapshotOf, SizeMB, DatabaseCreated, PrimaryFilePath, Status, Notes

    .EXAMPLE
        New-DbaDbSnapshot -SqlInstance sqlserver2014a -Database HR -Name HR_snap

        Creates snapshot named "HR_snap" for HR

    .EXAMPLE
        New-DbaDbSnapshot -SqlInstance sqlserver2014a -Database HR -NameSuffix 'fool_{0}_snap'

        Creates snapshot named "fool_HR_snap" for HR

    .EXAMPLE
        New-DbaDbSnapshot -SqlInstance sqlserver2014a -Database HR, Accounting -Path F:\snapshotpath

        Creates snapshots for HR and Accounting databases, storing files under the F:\snapshotpath\ dir

    .EXAMPLE
        Get-DbaDatabase -SqlInstance sql2016 -Database df | New-DbaDbSnapshot

        Creates a snapshot for the database df on sql2016
#>

    [CmdletBinding(SupportsShouldProcess)]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$AllDatabases,
        [string]$Name,
        [string]$NameSuffix,
        [string]$Path,
        [switch]$Force,
        [parameter(ValueFromPipeline)]
        [Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {

        $NoSupportForSnap = @('model', 'master', 'tempdb')
        # Evaluate the default suffix here for naming consistency
        $DefaultSuffix = (Get-Date -Format "yyyyMMdd_HHmmss")
        if ($NameSuffix.Length -gt 0) {
            #Validate if Name can be interpolated
            try {
                $null = $NameSuffix -f 'some_string'
            }
            catch {
                Stop-Function -Message "NameSuffix parameter must be a template only containing one parameter {0}" -ErrorRecord $_
            }
        }

        function Resolve-SnapshotError($server) {
            $errhelp = ''
            $CurrentEdition = $server.Edition.toLower()
            $CurrentVersion = $server.Version.Major * 1000000 + $server.Version.Minor * 10000 + $server.Version.Build
            if ($server.Version.Major -lt 9) {
                $errhelp = 'Not supported before 2005'
            }
            if ($CurrentVersion -lt 12002000 -and $errhelp.Length -eq 0) {
                if ($CurrentEdition -notmatch '.*enterprise.*|.*developer.*|.*datacenter.*') {
                    $errhelp = 'Supported only for Enterprise, Developer or Datacenter editions'
                }
            }
            $message = ""
            if ($errhelp.Length -gt 0) {
                $message += "Please make sure your version supports snapshots : ($errhelp)"
            }
            else {
                $message += "This module can't tell you why the snapshot creation failed. Feel free to report back to dbatools what happened"
            }
            Write-Message -Level Warning -Message $message
        }
    }
    process {
        if (-not $InputObject -and -not $Database -and $AllDatabases -eq $false) {
            Stop-Function -Message "You must specify a -AllDatabases or -Database to continue" -EnableException $EnableException
            return
        }

        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            #Checks for path existence, left the length test because test-bound wasn't working for some reason
            if ($Path.Length -gt 0) {
                if (!(Test-DbaSqlPath -SqlInstance $instance -Path $Path)) {
                    Stop-Function -Message "$instance cannot access the directory $Path" -ErrorRecord $_ -Target $instance -Continue -EnableException $EnableException
                }
            }

            if ($AllDatabases) {
                $dbs = $server.Databases
            }

            if ($Database) {
                $dbs = $server.Databases | Where-Object { $Database -contains $_.Name }
            }

            if ($ExcludeDatabase) {
                $dbs = $server.Databases | Where-Object { $ExcludeDatabase -notcontains $_.Name }
            }

            ## double check for gotchas
            foreach ($db in $dbs) {
                if ($db.IsDatabaseSnapshot) {
                    Write-Message -Level Warning -Message "$($db.name) is a snapshot, skipping"
                }
                elseif ($db.name -in $NoSupportForSnap) {
                    Write-Message -Level Warning -Message "$($db.name) snapshots are prohibited"
                }
                elseif ($db.IsAccessible -ne $true) {
                    Write-Message -Level Verbose -Message "$($db.name) is not accessible, skipping"
                }
                else {
                    $InputObject += $db
                }
            }

            if ($InputObject.Length -gt 1 -and $Name) {
                Stop-Function -Message "You passed the Name parameter that is fixed but selected multiple databases to snapshot: use the NameSuffix parameter" -Continue -EnableException $EnableException
            }
        }

        foreach ($db in $InputObject) {
            $server = $db.Parent

            # In case stuff is piped in
            if ($server.VersionMajor -lt 9) {
                Stop-Function -Message "SQL Server version 9 required - $server not supported" -Continue
            }

            if ($NameSuffix.Length -gt 0) {
                $SnapName = $NameSuffix -f $db.Name
                if ($SnapName -eq $NameSuffix) {
                    #no interpolation, just append
                    $SnapName = '{0}{1}' -f $db.Name, $NameSuffix
                }
            }
            elseif ($Name.Length -gt 0) {
                $SnapName = $Name
            }
            else {
                $SnapName = "{0}_{1}" -f $db.Name, $DefaultSuffix
            }
            if ($SnapName -in $server.Databases.Name) {
                Write-Message -Level Warning -Message "A database named $Snapname already exists, skipping"
                continue
            }
            $all_FSD = $db.FileGroups | Where-Object FileGroupType -eq 'FileStreamDataFileGroup'
            $all_MMO = $db.FileGroups | Where-Object FileGroupType -eq 'MemoryOptimizedDataFileGroup'
            $has_FSD = $all_FSD.Count -gt 0
            $has_MMO = $all_MMO.Count -gt 0
            if ($has_MMO) {
                Write-Message -Level Warning -Message "MEMORY_OPTIMIZED_DATA detected, snapshots are not possible"
                continue
            }
            if ($has_FSD -and $Force -eq $false) {
                Write-Message -Level Warning -Message "Filestream detected, skipping. You need to specify -Force. See Get-Help for details"
                continue
            }
            $snaptype = "db snapshot"
            if ($has_FSD) {
                $snaptype = "partial db snapshot"
            }
            If ($Pscmdlet.ShouldProcess($server, "Create $snaptype $SnapName of $($db.Name)")) {
                $CustomFileStructure = @{ }
                $counter = 0
                foreach ($fg in $db.FileGroups) {
                    $CustomFileStructure[$fg.Name] = @()
                    if ($fg.FileGroupType -eq 'FileStreamDataFileGroup') {
                        Continue
                    }
                    foreach ($file in $fg.Files) {
                        $counter += 1
                        $basename = [IO.Path]::GetFileNameWithoutExtension($file.FileName)
                        $basepath = Split-Path $file.FileName -Parent
                        # change path if specified
                        if ($Path.Length -gt 0) {
                            $basepath = $Path
                        }
                        # we need to avoid cases where basename is the same for multiple FG
                        $fname = [IO.Path]::Combine($basepath, ("{0}_{1}_{2:0000}_{3:000}" -f $basename, $DefaultSuffix, (Get-Date).MilliSecond, $counter))
                        # fixed extension is hardcoded as "ss", which seems a "de-facto" standard
                        $fname = [IO.Path]::ChangeExtension($fname, "ss")
                        $CustomFileStructure[$fg.Name] += @{ 'name' = $file.name; 'filename' = $fname }
                    }
                }

                $SnapDB = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Database -ArgumentList $server, $Snapname
                $SnapDB.DatabaseSnapshotBaseName = $db.Name

                foreach ($fg in $CustomFileStructure.Keys) {
                    $SnapFG = New-Object -TypeName Microsoft.SqlServer.Management.Smo.FileGroup $SnapDB, $fg
                    $SnapDB.FileGroups.Add($SnapFG)
                    foreach ($file in $CustomFileStructure[$fg]) {
                        $SnapFile = New-Object -TypeName Microsoft.SqlServer.Management.Smo.DataFile $SnapFG, $file['name'], $file['filename']
                        $SnapDB.FileGroups[$fg].Files.Add($SnapFile)
                    }
                }

                # we're ready to issue a Create, but SMO is a little uncooperative here
                # there are cases we can manage and others we can't, and we need all the
                # info we can get both from testers and from users

                $sql = $SnapDB.Script()

                try {
                    $SnapDB.Create()
                    $server.Databases.Refresh()
                    Get-DbaDbSnapshot -SqlInstance $server -Snapshot $Snapname
                }
                catch {
                    try {
                        $server.Databases.Refresh()
                        if ($SnapName -notin $server.Databases.Name) {
                            # previous creation failed completely, snapshot is not there already
                            $null = $server.Query($sql[0])
                            $server.Databases.Refresh()
                            $SnapDB = Get-DbaDbSnapshot -SqlInstance $server -Snapshot $Snapname
                        }
                        else {
                            $SnapDB = Get-DbaDbSnapshot -SqlInstance $server -Snapshot $Snapname
                        }

                        $Notes = @()
                        if ($db.ReadOnly -eq $true) {
                            $Notes += 'SMO is probably trying to set a property on a read-only snapshot, run with -Debug to find out and report back'
                        }
                        if ($has_FSD) {
                            $Status = 'Partial'
                            $Notes += 'Filestream groups are not viable for snapshot'
                        }
                        $Notes = $Notes -Join ';'

                        $hints = @("Executing these commands led to a partial failure")
                        foreach ($stmt in $sql) {
                            $hints += $stmt
                        }

                        Write-Message -Level Debug -Message ($hints -Join "`n")

                        $SnapDB
                    }
                    catch {
                        # Resolve-SnapshotError $server
                        $hints = @("Executing these commands led to a failure")
                        foreach ($stmt in $sql) {
                            $hints += $stmt
                        }
                        Write-Message -Level Debug -Message ($hints -Join "`n")

                        Stop-Function -Message "Failure" -ErrorRecord $_ -Target $SnapDB -Continue
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias New-DbaDatabaseSnapshot
    }
}
function New-DbaDbUser {
    <#
        .SYNOPSIS
            Creates a new user for the specified database.

        .DESCRIPTION
            Creates a new user for a specified database with provided specifications.

        .PARAMETER SqlInstance
            The target SQL Server instance. Defaults to the default instance on localhost.

        .PARAMETER SqlCredential
            Allows you to login to servers using SQL Logins instead of Windows Authentication (AKA Integrated or Trusted). To use:

            $scred = Get-Credential, then pass $scred object to the -SqlCredential parameter.

            Windows Authentication will be used if SqlCredential is not specified. SQL Server does not accept Windows credentials being passed as credentials.

            To connect to SQL Server as a different Windows user, run PowerShell as that user.

        .PARAMETER Database
            Specifies the database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies the database(s) to exclude from processing. Options for this list are auto-populated from the server. By default, system databases are excluded.

        .PARAMETER IncludeSystem
            If this switch is enabled, the user will be added to system databases.

        .PARAMETER Login
            When specified, the user will be associated to this SQL login and have the same name as the Login.

        .PARAMETER Username
            When specified, the user will have this name.

        .PARAMETER Force
            If user exists, drop and recreate.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database, User
            Author: Frank Henninger (@osiris687)
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/New-DbaDbUser

        .EXAMPLE
        New-DbaDbUser -SqlInstance sqlserver2014 -Database DB1 -Login user1

        Creates a new sql user with login named user1 in the specified database.

        .EXAMPLE
        New-DbaDbUser -SqlInstance sqlserver2014 -Database DB1 -Username user1

        Creates a new sql user without login named user1 in the specified database.

        .EXAMPLE
        New-DbaDbUser -SqlInstance sqlserver2014 -Database DB1 -Login Login1 -Username user1

        Creates a new sql user named user1 mapped to Login1 in the specified database.

        .EXAMPLE
        Get-DbaDatabaseUser -SqlInstance sqlserver1 -Database DB1 | New-DbaDbUser -SqlInstance sqlserver2 -Database DB1

        Copies users from sqlserver1.DB1 to sqlserver2.DB1. Does not copy permissions!
    #>
    [CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = "NoLogin")]
    param(
        [parameter(Mandatory, Position = 1)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$IncludeSystem,
        [parameter(ParameterSetName = "Login")]
        [string[]]$Login,
        [parameter(ParameterSetName = "NoLogin")]
        [parameter(ParameterSetName = "Login")]
        [string[]]$Username,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        function Test-SqlLoginInDatabase {
            param(
                [Microsoft.SqlServer.Management.Smo.Login]$Login,
                [Microsoft.SqlServer.Management.Smo.Database]$Database
            )

            # Does user exist with same login?
            if ( $existingUser = ( $Database.Users | Where-Object Login -eq $smoLogin ) ) {
                if (Test-Bound 'Force') {
                    if ($Pscmdlet.ShouldProcess($existingUser, "Dropping existing user $($existingUser.Name) because -Force was used")) {
                        try {
                            $existingUser.Drop()
                        }
                        catch {
                            Stop-Function -Message "Could not remove existing user $($existingUser.Name), skipping." -Target $existingUser -ErrorRecord $_ -Exception $_.Exception.InnerException.InnerException.InnerException -Continue
                        }
                    }
                }
                else {
                    Stop-Function -Message "User $($existingUser.Name) already exists and -Force was not specified" -Target $existingUser -Continue
                }
            }
        }

        function Test-SqlUserInDatabase {
            param(
                [string[]]$Username,
                [Microsoft.SqlServer.Management.Smo.Database]$Database
            )

            # Does user exist with same login?
            if ( $existingUser = ( $Database.Users | Where-Object Name -eq $Username ) ) {
                if (Test-Bound 'Force') {
                    if ($Pscmdlet.ShouldProcess($existingUser, "Dropping existing user $($existingUser.Name) because -Force was used")) {
                        try {
                            $existingUser.Drop()
                        }
                        catch {
                            Stop-Function -Message "Could not remove existing user $($existingUser.Name), skipping." -Target $existingUser -ErrorRecord $_ -Exception $_.Exception.InnerException.InnerException.InnerException -Continue
                        }
                    }
                }
                else {
                    Stop-Function -Message "User $($existingUser.Name) already exists and -Force was not specified" -Target $existingUser -Continue
                }
            }
        }
    }

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $databases = $server.Databases | Where-Object IsAccessible -eq $true

            if ($Database) {
                $databases = $databases | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }
            if (Test-Bound 'IncludeSystem' -Not) {
                $databases = $databases | Where-Object IsSystemObject -NE $true
            }

            foreach ($db in $databases) {
                Write-Message -Level Verbose -Message "Add users to Database $db on target $server"

                switch -Wildcard ($PSCmdlet.ParameterSetName) {
                    "Login*" {
                        # Creates a user with Login
                        Write-Message -Level VeryVerbose -Message "Using UserType: SqlLogin"

                        if ($PSBoundParameters.Keys -notcontains 'Login') {
                            Stop-Function -Message "Parameter -Login is required " -Target $instance
                        }
                        if ($Login.GetType().Name -eq 'Login') {
                            $smoLogin = $Login
                        }
                        else {
                            #get the login associated with the given name.
                            $smoLogin = $server.Logins | Where-Object Name -eq $Login
                            if ($smoLogin -eq $null) {
                                Stop-Function -Message "Invalid Login: $Login is not found on $Server" -Target $instance;
                                return
                            }
                        }

                        Test-SqlLoginInDatabase -Database $db -Login $smoLogin

                        if ( $PSCmdlet.ParameterSetName -eq "LoginWithNewUsername" ) {
                            $Name = $Username
                            Write-Message -Level Verbose -Message "Using UserName: $Username"
                        }
                        else {
                            $Name = $smoLogin.Name
                            Write-Message -Level Verbose -Message "Using LoginName: $Name"
                        }

                        $Login = $smoLogin
                        $UserType = [Microsoft.SqlServer.Management.Smo.UserType]::SqlLogin
                    }

                    "NoLogin" {
                        # Creates a user without login
                        Write-Message -Level Verbose -Message "Using UserType: NoLogin"
                        $UserType = [Microsoft.SqlServer.Management.Smo.UserType]::NoLogin
                        $Name = $Username
                    }
                } #switch

                # Does user exist with same name?
                Test-SqlUserInDatabase -Database $db -Username $Name

                if ($Pscmdlet.ShouldProcess($db, "Creating user $Name")) {
                    try {
                        $smoUser = New-Object Microsoft.SqlServer.Management.Smo.User
                        $smoUser.Parent = $db
                        $smoUser.Name = $Name

                        if ( $PSBoundParameters.Keys -contains 'Login' -and $Login.GetType().Name -eq 'Login' ) {
                            $smoUser.Login = Login
                        }
                        $smoUser.UserType = $UserType

                        $smoUser.Create()
                    }
                    catch {
                        Stop-Function -Message "Failed to add user $Name in $db to $instance"  -Category InvalidOperation -ErrorRecord $_ -Target $instance -Continue
                    }
                    $smoUser.Refresh()

                    if ( $PSBoundParameters.Keys -contains 'Username' -and $smoUser.Name -ne $Username ) {
                        $smoUser.Rename($Username)
                    }

                    Write-Message -Level Verbose -Message "Successfully added $smoUser in $db to $instance."
                }

                #Display Results
                Get-DbaDatabaseUser -SqlInstance $server.Name -Database $db.Name | Where-Object name -eq $smoUser.Name
            } #foreach ($db in $databases)
        } #foreach ($instance in $SqlInstance)
    }
}
function New-DbaLogin {
    <#
    .SYNOPSIS
    Creates a new SQL Server login

    .DESCRIPTION
    Creates a new SQL Server login with provided specifications

    .PARAMETER SqlInstance
    The target SQL Server(s)

    .PARAMETER SqlCredential
    Allows you to login to SQL Server using alternative credentials

    .PARAMETER Login
    The Login name(s)

    .PARAMETER Password
    Secure string used to authenticate the Login

    .PARAMETER HashedPassword
    Hashed password string used to authenticate the Login

    .PARAMETER InputObject
    Takes the parameters required from a Login object that has been piped into the command

    .PARAMETER LoginRenameHashtable
    Pass a hash table into this parameter to change login names when piping objects into the procedure

    .PARAMETER MapToCertificate
    Map the login to a certificate

    .PARAMETER MapToAsymmetricKey
    Map the login to an asymmetric key

    .PARAMETER MapToCredential
    Map the login to a credential

    .PARAMETER Sid
    Provide an explicit Sid that should be used when creating the account. Can be [byte[]] or hex [string] ('0xFFFF...')

    .PARAMETER DefaultDatabase
    Default database for the login

    .PARAMETER Language
    Login's default language

    .PARAMETER PasswordExpiration
    Enforces password expiration policy. Requires PasswordPolicy to be enabled. Can be $true or $false(default)

    .PARAMETER PasswordPolicy
    Enforces password complexity policy. Can be $true or $false(default)

    .PARAMETER Disabled
    Create the login in a disabled state

    .PARAMETER NewSid
    Ignore sids from the piped login object to generate new sids on the server. Useful when copying login onto the same server

    .PARAMETER Force
    If login exists, drop and recreate

    .PARAMETER WhatIf
    Shows what would happen if the command were to run. No actions are actually performed

    .PARAMETER Confirm
    Prompts you for confirmation before executing any changing operations within the command

    .PARAMETER EnableException
    By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
    Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
    Tags: Login
    Author: Kirill Kravtsov (@nvarscar)
    dbatools PowerShell module (https://dbatools.io, [email protected])
    Copyright (C) 2016 Chrissy LeMaire
    License: MIT https://opensource.org/licenses/MIT

    .LINK
    https://dbatools.io/New-DbaLogin

    .EXAMPLE
    New-DbaLogin -SqlInstance Server1,Server2 -Login Newlogin

    You will be prompted to securely enter the password for a login [Newlogin]. The login would be created on servers Server1 and Server2 with default parameters.

    .EXAMPLE
    $securePassword = Read-Host "Input password" -AsSecureString
    New-DbaLogin -SqlInstance Server1\sql1 -Login Newlogin -Password $securePassword -PasswordPolicy -PasswordExpiration

    Creates a login on Server1\sql1 with a predefined password. The login will have password and expiration policies enforced onto it.

    .EXAMPLE
    Get-DbaLogin -SqlInstance sql1 -Login Oldlogin | New-DbaLogin -SqlInstance sql1 -LoginRenameHashtable @{Oldlogin = 'Newlogin'} -Force -NewSid -Disabled:$false

    Copies a login [Oldlogin] to the same instance sql1 with the same parameters (including password). New login will have a new sid, a new name [Newlogin] and will not be disabled. Existing login [Newlogin] will be removed prior to creation.

    .EXAMPLE
    Get-DbaLogin -SqlInstance sql1 -Login Login1,Login2 | New-DbaLogin -SqlInstance sql2 -PasswordPolicy -PasswordExpiration -DefaultDatabase tempdb -Disabled

    Copies logins [Login1] and [Login2] from instance sql1 to instance sql2, but enforces password and expiration policies for the new logins. New logins will also have a default database set to [tempdb] and will be created in a disabled state.
#>
    [CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = "Password")]
    param (
        [parameter(Mandatory, Position = 1)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Name", "LoginName")]
        [parameter(ParameterSetName = "Password", Position = 2)]
        [parameter(ParameterSetName = "PasswordHash")]
        [parameter(ParameterSetName = "MapToCertificate")]
        [parameter(ParameterSetName = "MapToAsymmetricKey")]
        [string[]]$Login,
        [parameter(ValueFromPipeline = $true)]
        [parameter(ParameterSetName = "Password")]
        [parameter(ParameterSetName = "PasswordHash")]
        [parameter(ParameterSetName = "MapToCertificate")]
        [parameter(ParameterSetName = "MapToAsymmetricKey")]
        [object[]]$InputObject,
        [Alias("Rename")]
        [hashtable]$LoginRenameHashtable,
        [parameter(ParameterSetName = "Password", Position = 3)]
        [Security.SecureString]$Password,
        [Alias("Hash", "PasswordHash")]
        [parameter(ParameterSetName = "PasswordHash")]
        [string]$HashedPassword,
        [parameter(ParameterSetName = "MapToCertificate")]
        [string]$MapToCertificate,
        [parameter(ParameterSetName = "MapToAsymmetricKey")]
        [string]$MapToAsymmetricKey,
        [string]$MapToCredential,
        [object]$Sid,
        [Alias("DefaulDB")]
        [parameter(ParameterSetName = "Password")]
        [parameter(ParameterSetName = "PasswordHash")]
        [string]$DefaultDatabase,
        [parameter(ParameterSetName = "Password")]
        [parameter(ParameterSetName = "PasswordHash")]
        [string]$Language,
        [Alias("Expiration", "CheckExpiration")]
        [parameter(ParameterSetName = "Password")]
        [parameter(ParameterSetName = "PasswordHash")]
        [switch]$PasswordExpiration,
        [Alias("Policy", "CheckPolicy")]
        [parameter(ParameterSetName = "Password")]
        [parameter(ParameterSetName = "PasswordHash")]
        [switch]$PasswordPolicy,
        [Alias("Disable")]
        [switch]$Disabled,
        [switch]$NewSid,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        if ($Sid) {
            if ($Sid.GetType().Name -ne 'Byte[]') {
                foreach ($symbol in $Sid.TrimStart("0x").ToCharArray()) {
                    if ($symbol -notin "0123456789ABCDEF".ToCharArray()) {
                        Stop-Function -Message "Sid has invalid character '$symbol', cannot proceed." -Category InvalidArgument -EnableException $EnableException
                        return
                    }
                }
                $Sid = Convert-HexStringToByte $Sid
            }
        }

        if ($HashedPassword) {
            if ($HashedPassword.GetType().Name -eq 'Byte[]') {
                $HashedPassword = Convert-ByteToHexString $HashedPassword
            }
        }
    }

    process {
        #At least one of those should be specified
        if (!($Login -or $InputObject)) {
            Stop-Function -Message "No logins have been specified." -Category InvalidArgument -EnableException $EnableException
            Return
        }

        $loginCollection = @()
        if ($InputObject) {
            $loginCollection += $InputObject
            if ($Login) {
                Stop-Function -Message "Parameter -Login is not supported when processing objects from -InputObject. If you need to rename the logins, please use -LoginRenameHashtable." -Category InvalidArgument -EnableException $EnableException
                Return
            }
        }
        else {
            $loginCollection += $Login
            $Login | ForEach-Object {
                if ($_.IndexOf('\') -eq -1 -and $PsCmdlet.ParameterSetName -like "Password*" -and !($Password -or $HashedPassword)) {
                    $passwordNotSpecified = $true
                }
            }
        }
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            foreach ($loginItem in $loginCollection) {
                #check if $loginItem is an SMO Login object
                if ($loginItem.GetType().Name -eq 'Login') {
                    #Get all the necessary fields
                    $loginName = $loginItem.Name
                    $loginType = $loginItem.LoginType
                    $currentSid = $loginItem.Sid
                    $currentDefaultDatabase = $loginItem.DefaultDatabase
                    $currentLanguage = $loginItem.Language
                    $currentPasswordExpiration = $loginItem.PasswordExpiration
                    $currentPasswordPolicyEnforced = $loginItem.PasswordPolicyEnforced
                    $currentDisabled = $loginItem.IsDisabled

                    #Get previous password
                    if ($loginType -eq 'SqlLogin' -and !($Password -or $HashedPassword)) {
                        $sourceServer = $loginItem.Parent
                        switch ($sourceServer.versionMajor) {
                            0 { $sql = "SELECT CONVERT(VARBINARY(256),password) as hashedpass FROM master.dbo.syslogins WHERE loginname='$loginName'" }
                            8 { $sql = "SELECT CONVERT(VARBINARY(256),password) as hashedpass FROM dbo.syslogins WHERE name='$loginName'" }
                            9 { $sql = "SELECT CONVERT(VARBINARY(256),password_hash) as hashedpass FROM sys.sql_logins where name='$loginName'" }
                            default {
                                $sql = "SELECT CAST(CONVERT(VARCHAR(256), CAST(LOGINPROPERTY(name,'PasswordHash')
                                    AS VARBINARY(256)), 1) AS NVARCHAR(max)) AS hashedpass
                                    FROM sys.server_principals
                                    WHERE principal_id = $($loginItem.id)"
                            }
                        }

                        try {
                            $hashedPass = $sourceServer.ConnectionContext.ExecuteScalar($sql)
                        }
                        catch {
                            $hashedPassDt = $sourceServer.Databases['master'].ExecuteWithResults($sql)
                            $hashedPass = $hashedPassDt.Tables[0].Rows[0].Item(0)
                        }

                        if ($hashedPass.GetType().Name -ne "String") {
                            $hashedPass = Convert-ByteToHexString $hashedPass
                        }
                        $currentHashedPassword = $hashedPass
                    }

                    #Get cryptography and attached credentials
                    if ($loginType -eq 'AsymmetricKey') {
                        $currentAsymmetricKey = $loginItem.AsymmetricKey
                    }
                    if ($loginType -eq 'Certificate') {
                        $currentCertificate = $loginItem.Certificate
                    }
                    #This method or property is accessible only while working with SQL Server 2008 or later.
                    if ($sourceServer.versionMajor -gt 9) {
                        if ($loginItem.EnumCredentials()) {
                            $currentCredential = $loginItem.EnumCredentials()
                        }
                    }
                }
                else {
                    $loginName = $loginItem
                    $currentSid = $currentDefaultDatabase = $currentLanguage = $currentPasswordExpiration = $currentAsymmetricKey = $currentCertificate = $currentCredential = $currentDisabled = $currentPasswordPolicyEnforced = $null

                    if ($PsCmdlet.ParameterSetName -eq "MapToCertificate") { $loginType = 'Certificate' }
                    elseif ($PsCmdlet.ParameterSetName -eq "MapToAsymmetricKey") { $loginType = 'AsymmetricKey' }
                    elseif ($loginItem.IndexOf('\') -eq -1) {    $loginType = 'SqlLogin' }
                    else { $loginType = 'WindowsUser' }
                }

                if (($server.LoginMode -ne [Microsoft.SqlServer.Management.Smo.ServerLoginMode]::Mixed) -and ($loginType -eq 'SqlLogin')) {
                    Write-Message -Level Warning -Message "$instance does not have Mixed Mode enabled. [$loginName] is an SQL Login. Enable mixed mode authentication after the migration completes to use this type of login."
                }

                if ($Sid) {
                    $currentSid = $Sid
                }
                if ($DefaultDatabase) {
                    $currentDefaultDatabase = $DefaultDatabase
                }
                if ($Language) {
                    $currentLanguage = $Language
                }
                if ($PSBoundParameters.Keys -contains 'PasswordExpiration') {
                    $currentPasswordExpiration = $PasswordExpiration
                }
                if ($PSBoundParameters.Keys -contains 'PasswordPolicy') {
                    $currentPasswordPolicyEnforced = $PasswordPolicy
                }
                if ($PSBoundParameters.Keys -contains 'MapToAsymmetricKey') {
                    $currentAsymmetricKey = $MapToAsymmetricKey
                }
                if ($PSBoundParameters.Keys -contains 'MapToCertificate') {
                    $currentCertificate = $MapToCertificate
                }
                if ($PSBoundParameters.Keys -contains 'MapToCredential') {
                    $currentCredential = $MapToCredential
                }
                if ($PSBoundParameters.Keys -contains 'Disabled') {
                    $currentDisabled = $Disabled
                }

                #Apply renaming if necessary
                if ($LoginRenameHashtable.Keys -contains $loginName) {
                    $loginName = $LoginRenameHashtable[$loginName]
                }

                #Requesting password if required
                if ($loginItem.GetType().Name -ne 'Login' -and $loginType -eq 'SqlLogin' -and !($Password -or $HashedPassword)) {
                    $Password = Read-Host -AsSecureString -Prompt "Enter a new password for the SQL Server login(s)"
                }

                #verify if login exists on the server
                if ($existingLogin = $server.Logins[$loginName]) {
                    if ($force) {
                        if ($Pscmdlet.ShouldProcess($existingLogin, "Dropping existing login $loginName on $instance because -Force was used")) {
                            try {
                                $existingLogin.Drop()
                            }
                            catch {
                                Stop-Function -Message "Could not remove existing login $loginName on $instance, skipping." -Target $loginName -Continue
                            }
                        }
                    }
                    else {
                        Stop-Function -Message "Login $loginName already exists on $instance and -Force was not specified" -Target $loginName -Continue
                    }
                }


                if ($Pscmdlet.ShouldProcess($SqlInstance, "Creating login $loginName on $instance")) {
                    try {
                        $newLogin = New-Object Microsoft.SqlServer.Management.Smo.Login($server, $loginName)
                        $newLogin.LoginType = $loginType

                        $withParams = ""

                        if ($loginType -eq 'SqlLogin' -and $currentSid -and !$NewSid) {
                            Write-Message -Level Verbose -Message "Setting $loginName SID"
                            $withParams += ", SID = " + (Convert-ByteToHexString $currentSid)
                            $newLogin.Set_Sid($currentSid)
                        }

                        if ($loginType -in ("WindowsUser", "WindowsGroup", "SqlLogin")) {
                            if ($currentDefaultDatabase) {
                                Write-Message -Level Verbose -Message "Setting $loginName default database to $currentDefaultDatabase"
                                $withParams += ", DEFAULT_DATABASE = [$currentDefaultDatabase]"
                                $newLogin.DefaultDatabase = $currentDefaultDatabase
                            }

                            if ($currentLanguage) {
                                Write-Message -Level Verbose -Message "Setting $loginName language to $currentLanguage"
                                $withParams += ", DEFAULT_LANGUAGE = [$currentLanguage]"
                                $newLogin.Language = $currentLanguage
                            }

                            #CHECK_EXPIRATION: default - OFF
                            if ($currentPasswordExpiration) {
                                $withParams += ", CHECK_EXPIRATION = ON"
                                $newLogin.PasswordExpirationEnabled = $true
                            }
                            else {
                                $withParams += ", CHECK_EXPIRATION = OFF"
                                $newLogin.PasswordExpirationEnabled = $false
                            }

                            #CHECK_POLICY: default - ON
                            if ($currentPasswordPolicyEnforced) {
                                $withParams += ", CHECK_POLICY = ON"
                                $newLogin.PasswordPolicyEnforced = $true
                            }
                            else {
                                $withParams += ", CHECK_POLICY = OFF"
                                $newLogin.PasswordPolicyEnforced = $false
                            }

                            #Generate hashed password if necessary
                            if ($Password) {
                                $currentHashedPassword = Get-PasswordHash $Password $server.versionMajor
                            }
                            elseif ($HashedPassword) {
                                $currentHashedPassword = $HashedPassword
                            }
                        }
                        elseif ($loginType -eq 'AsymmetricKey') {
                            $newLogin.AsymmetricKey = $currentAsymmetricKey
                        }
                        elseif ($loginType -eq 'Certificate') {
                            $newLogin.Certificate = $currentCertificate
                        }

                        #Add credential
                        if ($currentCredential) {
                            $withParams += ", CREDENTIAL = [$currentCredential]"
                        }

                        Write-Message -Level Verbose -Message "Adding as login type $loginType"

                        # Attempt to add login using SMO, then T-SQL
                        try {
                            if ($loginType -in ("WindowsUser", "WindowsGroup", "AsymmetricKey", "Certificate")) {
                                if ($withParams) { $withParams = " WITH " + $withParams.TrimStart(',') }
                                $newLogin.Create()
                            }
                            elseif ($loginType -eq "SqlLogin") {
                                $newLogin.Create($currentHashedPassword, [Microsoft.SqlServer.Management.Smo.LoginCreateOptions]::IsHashed)
                            }
                            $newLogin.Refresh()

                            #Adding credential
                            if ($currentCredential) {
                                try {
                                    $newLogin.AddCredential($currentCredential)
                                }
                                catch {
                                    $newLogin.Drop()
                                    Stop-Function -Message "Failed to add $loginName to $instance." -Category InvalidOperation -ErrorRecord $_ -Target $instance -Continue
                                }
                            }
                            Write-Message -Level Verbose -Message "Successfully added $loginName to $instance."
                        }
                        catch {
                            Write-Message -Level Verbose -Message "Failed to create $loginName on $instance using SMO, trying T-SQL."
                            try {
                                if ($loginType -eq 'AsymmetricKey') { $sql = "CREATE LOGIN [$loginName] FROM ASYMMETRIC KEY [$currentAsymmetricKey]" }
                                elseif ($loginType -eq 'Certificate') { $sql = "CREATE LOGIN [$loginName] FROM CERTIFICATE [$currentCertificate]" }
                                elseif ($loginType -eq "SqlLogin") { $sql = "CREATE LOGIN [$loginName] WITH PASSWORD = $currentHashedPassword HASHED" + $withParams }
                                else { $sql = "CREATE LOGIN [$loginName] FROM WINDOWS" + $withParams }

                                $null = $server.Query($sql)
                                $newLogin = $server.logins[$loginName]
                                Write-Message -Level Verbose -Message "Successfully added $loginName to $instance."
                            }
                            catch {
                                Stop-Function -Message "Failed to add $loginName to $instance." -Category InvalidOperation -ErrorRecord $_ -Target $instance -Continue
                            }
                        }

                        #Process the Disabled property
                        if ($currentDisabled) {
                            try {
                                $newLogin.Disable()
                                Write-Message -Level Verbose -Message "Login $loginName has been disabled on $instance."
                            }
                            catch {
                                Write-Message -Level Verbose -Message "Failed to disable $loginName on $instance using SMO, trying T-SQL."
                                try {
                                    $sql = "ALTER LOGIN [$loginName] DISABLE"
                                    $null = $server.Query($sql)
                                    Write-Message -Level Verbose -Message "Login $loginName has been disabled on $instance."
                                }
                                catch {
                                    Stop-Function -Message "Failed to disable $loginName on $instance." -Category InvalidOperation -ErrorRecord $_ -Target $instance -Continue
                                }
                            }
                        }
                        #Display results
                        Get-DbaLogin -SqlInstance $server -Login $loginName
                    }
                    catch {
                        Stop-Function -Message "Failed to create login $loginName on $instance." -Target $credential -InnerErrorRecord $_ -Continue
                    }
                }
            }
        }
    }
}
function New-DbaPublishProfile {
    <#
        .SYNOPSIS
            Creates a new Publish Profile.

        .DESCRIPTION
            The New-PublishProfile command generates a standard publish profile xml file that can be used by the DacFx (this and everything else) to control the deployment of your dacpac
            This generates a standard template XML which is enough to dpeloy a dacpac but it is highly recommended that you add additional options to the publish profile.
            If you use Visual Studio you can open a publish.xml file and use the ui to edit the file -
            To create a new file, right click on an SSDT project, choose "Publish" then "Load Profile" and load your profile or create a new one.
            Once you have loaded it in Visual Studio, clicking advanced shows you the list of options available to you.
            For a full list of options that you can add to the profile, google "sqlpackage.exe command line switches" or (https://msdn.microsoft.com/en-us/library/hh550080(v=vs.103).aspx)

        .PARAMETER SqlInstance
        SQL Server name or SMO object representing the SQL Server to connect to and publish to. Alternatively, you can provide a ConnectionString.

        .PARAMETER SqlCredential
        Allows you to login to servers using alternative logins instead Integrated, accepts Credential object created by Get-Credential

        .PARAMETER Database
            The database name you are targeting

        .PARAMETER ConnectionString
            The connection string to the database you are upgrading.

            Alternatively, you can provide a SqlInstance (and optionally SqlCredential) and the script will connect and generate the connectionstring.

        .PARAMETER Path
            The directory where you would like to save the profile xml file(s).

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Dacpac
            Author: Richie lee (@bzzzt_io)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
        .LINK
            https://dbatools.io/New-DbaPublishProfile

        .EXAMPLE
        New-DbaPublishProfile -SqlInstance sql2017 -SqlCredential (Get-Credential) -Database WorldWideImporters -Path C:\temp

        In this example, a prompt will appear for alternative credentials, tghen a connection will be made to sql2017. Using that connection,
        the ConnectionString will be extracted and used within the Publish Profile XML file which will be created at C:\temp\sql2017-WorldWideImporters-publish.xml

        .EXAMPLE
        New-DbaPublishProfile -Database WorldWideImporters -Path C:\temp -ConnectionString "SERVER=(localdb)\MSSQLLocalDB;Integrated Security=True;Database=master"

        In this example, no connections are made, and a Publish Profile XML would be created at C:\temp\localdb-MSSQLLocalDB-WorldWideImporters-publish.xml
    #>
    [CmdletBinding()]
    param (
        [parameter(ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Parameter(Mandatory)]
        [string[]]$Database,
        [string]$Path = "$home\Documents",
        [string[]]$ConnectionString,
        [switch]$EnableException
    )
    begin {
        if ((Test-Bound -Not -ParameterName SqlInstance) -and (Test-Bound -Not -ParameterName ConnectionString)) {
            Stop-Function -Message "You must specify either SqlInstance or ConnectionString"
        }

        if (-not (Test-Path $Path)) {
            Stop-Function -Message "$Path doesn't exist or access denied"
        }

        if ((Get-Item $path) -isnot [System.IO.DirectoryInfo]) {
            Stop-Function -Message "Path must be a directory"
        }

        function Get-Template ($db, $connstring) {
            "<?xml version=""1.0"" ?>
            <Project ToolsVersion=""14.0"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
              <PropertyGroup>
                <TargetDatabaseName>{0}</TargetDatabaseName>
                <TargetConnectionString>{1}</TargetConnectionString>
                <ProfileVersionNumber>1</ProfileVersionNumber>
              </PropertyGroup>
            </Project>" -f $db[0], $connstring
        }

        function Get-ServerName ($connstring) {
            $builder = New-Object System.Data.Common.DbConnectionStringBuilder
            $builder.set_ConnectionString($connstring)
            $instance = $builder['data source']

            if (-not $instance) {
                $instance = $builder['server']
            }

            return $instance.ToString().Replace('\', '--')
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $sqlinstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $ConnectionString += $server.ConnectionContext.ConnectionString.Replace(';Application Name="dbatools PowerShell module - dbatools.io"', '')

        }

        foreach ($connstring in $ConnectionString) {
            foreach ($db in $Database) {
                $profileTemplate = Get-Template $db, $connstring
                $instancename = Get-ServerName $connstring

                try {
                    $server = [DbaInstance]($instancename.ToString().Replace('--', '\'))
                    $PublishProfile = Join-Path $Path "$($instancename.Replace('--','-'))-$db-publish.xml" -ErrorAction Stop
                    Write-Message -Level Verbose -Message "Writing to $PublishProfile"
                    $profileTemplate | Out-File $PublishProfile -ErrorAction Stop
                    [pscustomobject]@{
                        ComputerName     = $server.ComputerName
                        InstanceName     = $server.InstanceName
                        SqlInstance      = $server.FullName
                        Database         = $db
                        FileName         = $PublishProfile
                        ConnectionString = $connstring
                        ProfileTemplate  = $profileTemplate
                    } | Select-DefaultView -ExcludeProperty ComputerName, InstanceName, ProfileTemplate
                }
                catch {
                    Stop-Function -ErrorRecord $_ -Message "Failure" -Target $instancename -Continue
                }
            }
        }
    }
}
function New-DbaScriptingOption {
    <#
    .SYNOPSIS
    Creates a new Microsoft.SqlServer.Management.Smo.ScriptingOptions object

    .DESCRIPTION
    Creates a new Microsoft.SqlServer.Management.Smo.ScriptingOptions object. Basically saves you the time from remembering the SMO assembly name ;)

    See https://msdn.microsoft.com/en-us/library/microsoft.sqlserver.management.smo.scriptingoptions.aspx for more information

    .NOTES
    Tags: Migration, Backup, DR

    Website: https://dbatools.io
    Copyright: (C) Chrissy LeMaire, [email protected]
    License: MIT https://opensource.org/licenses/MIT

    .LINK
    https://dbatools.io/New-DbaScriptingOption
    https://msdn.microsoft.com/en-us/library/microsoft.sqlserver.management.smo.scriptingoptions.aspx

    .EXAMPLE
    $options = New-DbaScriptingOption
    $options.ScriptDrops = $false
    $options.WithDependencies = $true
    Get-DbaAgentJob -SqlInstance sql2016 | Export-DbaScript -ScriptingOptionObject $options

    Exports Agent Jobs with the Scripting Options ScriptDrops set to $false and WithDependencies set to true

    #>
    New-Object Microsoft.SqlServer.Management.Smo.ScriptingOptions
}
function New-DbaServiceMasterKey {
    <#
.SYNOPSIS
Creates a new service master key

.DESCRIPTION
Creates a new service master key in the master database

.PARAMETER SqlInstance
The target SQL Server instances

.PARAMETER SqlCredential
Allows you to login to SQL Server using alternative credentials.

.PARAMETER Password
Secure string used to create the key.

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Certificate

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
New-DbaServiceMasterKey -SqlInstance Server1

You will be prompted to securely enter your Service Key Password twice, then a master key will be created in the master database on server1 if it does not exist.

#>
    [CmdletBinding(SupportsShouldProcess)]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Security.SecureString]$Password,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            if (Test-Bound -ParameterName Password -Not) {
                $password = Read-Host -AsSecureString -Prompt "You must enter Service Key password for $instance"
                $password2 = Read-Host -AsSecureString -Prompt "Type the password again"

                if (([System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($password))) -ne ([System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($password2)))) {
                    Stop-Function -Message "Passwords do not match" -Continue
                }
            }
            New-DbaDatabaseMasterKey -SqlInstance $instance -Database master -Password $password
        }
    }
}
function New-DbaSqlConnectionString {
    <#
        .SYNOPSIS
            Builds or extracts a SQL Server Connection String

        .DESCRIPTION
            Builds or extracts a SQL Server Connection String

            See https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.connectionstring.aspx
            and https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnectionstringbuilder.aspx
            and https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.aspx

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to.

        .PARAMETER Credential
            Credential object used to connect to the SQL Server as a different user be it Windows or SQL Server. Windows users are determined by the existence of a backslash, so if you are intending to use an alternative Windows connection instead of a SQL login, ensure it contains a backslash.

        .PARAMETER AccessToken
            Gets or sets the access token for the connection.

        .PARAMETER AppendConnectionString
            Appends to the current connection string. Note that you cannot pass authentication information using this method. Use -SqlInstance and, optionally, -SqlCredential to set authentication information.

        .PARAMETER ApplicationIntent
            Declares the application workload type when connecting to a server. Possible values are ReadOnly and ReadWrite.

        .PARAMETER BatchSeparator
            By default, this is "GO"

        .PARAMETER ClientName
            By default, this command sets the client to "dbatools PowerShell module - dbatools.io - custom connection" if you're doing anything that requires profiling, you can look for this client name. Using -ClientName allows you to set your own custom client.

        .PARAMETER ConnectTimeout
            The length of time (in seconds) to wait for a connection to the server before terminating the attempt and generating an error.

            Valid values are greater than or equal to 0 and less than or equal to 2147483647.

            When opening a connection to a Azure SQL Database, set the connection timeout to 30 seconds.

        .PARAMETER EncryptConnection
            When true, SQL Server uses SSL encryption for all data sent between the client and server if the server has a certificate installed. Recognized values are true, false, yes, and no. For more information, see Connection String Syntax.

            Beginning in .NET Framework 4.5, when TrustServerCertificate is false and Encrypt is true, the server name (or IP address) in a SQL Server SSL certificate must exactly match the server name (or IP address) specified in the connection string. Otherwise, the connection attempt will fail. For information about support for certificates whose subject starts with a wildcard character (*), see Accepted wildcards used by server certificates for server authentication.

        .PARAMETER FailoverPartner
            The name of the failover partner server where database mirroring is configured.

            If the value of this key is "", then Initial Catalog must be present, and its value must not be "".

            The server name can be 128 characters or less.

            If you specify a failover partner but the failover partner server is not configured for database mirroring and the primary server (specified with the Server keyword) is not available, then the connection will fail.

            If you specify a failover partner and the primary server is not configured for database mirroring, the connection to the primary server (specified with the Server keyword) will succeed if the primary server is available.

        .PARAMETER IsActiveDirectoryUniversalAuth
            Azure related

        .PARAMETER LockTimeout
            Sets the time in seconds required for the connection to time out when the current transaction is locked.

        .PARAMETER MaxPoolSize
            Sets the maximum number of connections allowed in the connection pool for this specific connection string.

        .PARAMETER MinPoolSize
            Sets the minimum number of connections allowed in the connection pool for this specific connection string.

        .PARAMETER MultipleActiveResultSets
            When used, an application can maintain multiple active result sets (MARS). When false, an application must process or cancel all result sets from one batch before it can execute any other batch on that connection.

        .PARAMETER MultiSubnetFailover
            If your application is connecting to an AlwaysOn availability group (AG) on different subnets, setting MultiSubnetFailover provides faster detection of and connection to the (currently) active server. For more information about SqlClient support for Always On Availability Groups

        .PARAMETER NetworkProtocol
            Connect explicitly using 'TcpIp','NamedPipes','Multiprotocol','AppleTalk','BanyanVines','Via','SharedMemory' and 'NWLinkIpxSpx'

        .PARAMETER NonPooledConnection
            Request a non-pooled connection

        .PARAMETER PacketSize
            Sets the size in bytes of the network packets used to communicate with an instance of SQL Server. Must match at server.

        .PARAMETER PooledConnectionLifetime
            When a connection is returned to the pool, its creation time is compared with the current time, and the connection is destroyed if that time span (in seconds) exceeds the value specified by Connection Lifetime. This is useful in clustered configurations to force load balancing between a running server and a server just brought online.

            A value of zero (0) causes pooled connections to have the maximum connection timeout.

        .PARAMETER SqlExecutionModes
            The SqlExecutionModes enumeration contains values that are used to specify whether the commands sent to the referenced connection to the server are executed immediately or saved in a buffer.

            Valid values include CaptureSql, ExecuteAndCaptureSql and ExecuteSql.

        .PARAMETER StatementTimeout
            Sets the number of seconds a statement is given to run before failing with a time-out error.

        .PARAMETER TrustServerCertificate
            Sets a value that indicates whether the channel will be encrypted while bypassing walking the certificate chain to validate trust.

        .PARAMETER WorkstationId
            Sets the name of the workstation connecting to SQL Server.

        .NOTES
            Tags: Connection, Connect, ConnectionString
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/New-DbaSqlConnectionString

        .EXAMPLE
            New-DbaSqlConnectionString -SqlInstance sql2014

            Creates a connection string that connects using Windows Authentication

        .EXAMPLE
            Connect-DbaInstance -SqlInstance sql2016 | New-DbaSqlConnectionString

            Builds a connected SMO object using Connect-DbaInstance then extracts and displays the connection string

        .EXAMPLE
            $wincred = Get-Credential ad\sqladmin
            New-DbaSqlConnectionString -SqlInstance sql2014 -Credential $wincred

            Creates a connection string that connects using alternative Windows credentials

        .EXAMPLE
            $sqlcred = Get-Credential sqladmin
            $server = New-DbaSqlConnectionString -SqlInstance sql2014 -Credential $sqlcred

            Login to sql2014 as SQL login sqladmin.

        .EXAMPLE
            $server = New-DbaSqlConnectionString -SqlInstance sql2014 -ClientName "mah connection"

            Creates a connection string that connects using Windows Authentication and uses the client name "mah connection". So when you open up profiler or use extended events, you can search for "mah connection".

        .EXAMPLE
            $server = New-DbaSqlConnectionString -SqlInstance sql2014 -AppendConnectionString "Packet Size=4096;AttachDbFilename=C:\MyFolder\MyDataFile.mdf;User Instance=true;"

            Creates a connection string that connects to sql2014 using Windows Authentication, then it sets the packet size (this can also be done via -PacketSize) and other connection attributes.

        .EXAMPLE
            $server = New-DbaSqlConnectionString -SqlInstance sql2014 -NetworkProtocol TcpIp -MultiSubnetFailover

            Creates a connection string with Windows Authentication that uses TCPIP and has MultiSubnetFailover enabled.

        .EXAMPLE
            $connstring = New-DbaSqlConnectionString sql2016 -ApplicationIntent ReadOnly

            Creates a connection string with ReadOnly ApplicantionIntent.
    #>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("SqlCredential")]
        [PSCredential]$Credential,
        [string]$AccessToken,
        [ValidateSet('ReadOnly', 'ReadWrite')]
        [string]$ApplicationIntent,
        [string]$BatchSeparator,
        [string]$ClientName = "custom connection",
        [int]$ConnectTimeout,
        [switch]$EncryptConnection,
        [string]$FailoverPartner,
        [switch]$IsActiveDirectoryUniversalAuth,
        [int]$LockTimeout,
        [int]$MaxPoolSize,
        [int]$MinPoolSize,
        [switch]$MultipleActiveResultSets,
        [switch]$MultiSubnetFailover,
        [ValidateSet('TcpIp', 'NamedPipes', 'Multiprotocol', 'AppleTalk', 'BanyanVines', 'Via', 'SharedMemory', 'NWLinkIpxSpx')]
        [string]$NetworkProtocol,
        [switch]$NonPooledConnection,
        [int]$PacketSize,
        [int]$PooledConnectionLifetime,
        [ValidateSet('CaptureSql', 'ExecuteAndCaptureSql', 'ExecuteSql')]
        [string]$SqlExecutionModes,
        [int]$StatementTimeout,
        [switch]$TrustServerCertificate,
        [string]$WorkstationId,
        [string]$AppendConnectionString
    )

    process {
        foreach ($instance in $sqlinstance) {

            if ($instance.GetType() -eq [Microsoft.SqlServer.Management.Smo.Server]) {
                return $instance.ConnectionContext.ConnectionString
            }
            else {
                $guid = [System.Guid]::NewGuid()
                $server = New-Object Microsoft.SqlServer.Management.Smo.Server $guid

                if ($AppendConnectionString) {
                    $connstring = $server.ConnectionContext.ConnectionString
                    $server.ConnectionContext.ConnectionString = "$connstring;$appendconnectionstring"
                    $server.ConnectionContext.ConnectionString
                }
                else {

                    $server.ConnectionContext.ApplicationName = $clientname

                    if ($AccessToken) { $server.ConnectionContext.AccessToken = $AccessToken }
                    if ($BatchSeparator) { $server.ConnectionContext.BatchSeparator = $BatchSeparator }
                    if ($ConnectTimeout) { $server.ConnectionContext.ConnectTimeout = $ConnectTimeout }
                    if ($Database) { $server.ConnectionContext.DatabaseName = $Database }
                    if ($EncryptConnection) { $server.ConnectionContext.EncryptConnection = $true }
                    if ($IsActiveDirectoryUniversalAuth) { $server.ConnectionContext.IsActiveDirectoryUniversalAuth = $true }
                    if ($LockTimeout) { $server.ConnectionContext.LockTimeout = $LockTimeout }
                    if ($MaxPoolSize) { $server.ConnectionContext.MaxPoolSize = $MaxPoolSize }
                    if ($MinPoolSize) { $server.ConnectionContext.MinPoolSize = $MinPoolSize }
                    if ($MultipleActiveResultSets) { $server.ConnectionContext.MultipleActiveResultSets = $true }
                    if ($NetworkProtocol) { $server.ConnectionContext.NetworkProtocol = $NetworkProtocol }
                    if ($NonPooledConnection) { $server.ConnectionContext.NonPooledConnection = $true }
                    if ($PacketSize) { $server.ConnectionContext.PacketSize = $PacketSize }
                    if ($PooledConnectionLifetime) { $server.ConnectionContext.PooledConnectionLifetime = $PooledConnectionLifetime }
                    if ($StatementTimeout) { $server.ConnectionContext.StatementTimeout = $StatementTimeout }
                    if ($SqlExecutionModes) { $server.ConnectionContext.SqlExecutionModes = $SqlExecutionModes }
                    if ($TrustServerCertificate) { $server.ConnectionContext.TrustServerCertificate = $true }
                    if ($WorkstationId) { $server.ConnectionContext.WorkstationId = $WorkstationId }

                    $connstring = $server.ConnectionContext.ConnectionString
                    if ($MultiSubnetFailover) { $connstring = "$connstring;MultiSubnetFailover=True" }
                    if ($FailoverPartner) { $connstring = "$connstring;Failover Partner=$FailoverPartner" }
                    if ($ApplicationIntent) { $connstring = "$connstring;ApplicationIntent=$ApplicationIntent;" }

                    if ($connstring -ne $server.ConnectionContext.ConnectionString) {
                        $server.ConnectionContext.ConnectionString = $connstring
                    }
                    if ($null -ne $Credential.username) {
                        $username = ($Credential.username).TrimStart("\")

                        if ($username -like "*\*") {
                            $username = $username.Split("\")[1]
                            $authtype = "Windows Authentication with Credential"
                            $server.ConnectionContext.LoginSecure = $true
                            $server.ConnectionContext.ConnectAsUser = $true
                            $server.ConnectionContext.ConnectAsUserName = $username
                            $server.ConnectionContext.ConnectAsUserPassword = ($Credential).GetNetworkCredential().Password
                        }
                        else {
                            $authtype = "SQL Authentication"
                            $server.ConnectionContext.LoginSecure = $false
                            $server.ConnectionContext.set_Login($username)
                            $server.ConnectionContext.set_SecurePassword($Credential.Password)
                        }
                    }

                    ($server.ConnectionContext.ConnectionString).Replace($guid, $SqlInstance)
                }
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function New-DbaSqlConnectionStringBuilder {
    <#
        .SYNOPSIS
            Returns a System.Data.SqlClient.SqlConnectionStringBuilder with the string specified

        .DESCRIPTION
            Creates a System.Data.SqlClient.SqlConnectionStringBuilder from a connection string.

        .PARAMETER ConnectionString
            A Connection String

        .PARAMETER ApplicationName
            The application name to tell SQL Server the connection is associated with.

        .PARAMETER DataSource
            The Sql Server to connect to.

        .PARAMETER InitialCatalog
            The initial database on the server to connect to.

        .PARAMETER IntegratedSecurity
            Set to true to use windows authentication.

        .PARAMETER UserName
            Sql User Name to connect with.

        .PARAMETER Password
            Password to use to connect with.

        .PARAMETER MultipleActiveResultSets
            Enable Multiple Active Result Sets.

        .PARAMETER ColumnEncryptionSetting
            Enable Always Encrypted.

        .PARAMETER WorkstationID
            Set the Workstation Id that is associated with the connection.

        .NOTES
            Author: zippy1981
            Tags: SqlBuild, ConnectionString, Connection

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2017 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/New-DbaSqlConnectionStringBuilder

        .EXAMPLE
            New-DbaSqlConnectionStringBuilder

            Returns an empty ConnectionStringBuilder

        .EXAMPLE
            "Data Source=localhost,1433;Initial Catalog=AlwaysEncryptedSample;UID=sa;PWD=alwaysB3Encrypt1ng;Application Name=Always Encrypted Sample MVC App;Column Encryption Setting=enabled" | New-DbaSqlConnectionStringBuilder

            Returns a connection string builder that can be used to connect to the local sql server instance on the default port.
    #>
    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "")]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingUserNameAndPassWordParams", "")]
    param (
        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [string[]]$ConnectionString = "",
        [Parameter(Mandatory = $false)]
        [string]$ApplicationName = "dbatools Powershell Module",
        [Parameter(Mandatory = $false)]
        [string]$DataSource = $null,
        [Parameter(Mandatory = $false)]
        [string]$InitialCatalog = $null,
        [Parameter(Mandatory = $false)]
        [Nullable[bool]]$IntegratedSecurity = $null,
        [Parameter(Mandatory = $false)]
        [string]$UserName = $null,
        # No point in securestring here, the memory is never stored securely in memory.
        [Parameter(Mandatory = $false)]
        [string]$Password = $null,
        [Alias('MARS')]
        [Parameter(Mandatory = $false)]
        [switch]$MultipleActiveResultSets,
        [Alias('AlwaysEncrypted')]
        [Parameter(Mandatory = $false)]
        [Data.SqlClient.SqlConnectionColumnEncryptionSetting]$ColumnEncryptionSetting =
        [Data.SqlClient.SqlConnectionColumnEncryptionSetting]::Enabled,
        [Parameter(Mandatory = $false)]
        [string]$WorkstationId = $env:COMPUTERNAME
    )
    process {
        foreach ($cs in $ConnectionString) {
            $builder = New-Object Data.SqlClient.SqlConnectionStringBuilder $cs
            if ($builder.ApplicationName -eq ".Net SqlClient Data Provider") {
                $builder['Application Name'] = $ApplicationName
            }
            if (![string]::IsNullOrWhiteSpace($DataSource)) {
                $builder['Data Source'] = $DataSource
            }
            if (![string]::IsNullOrWhiteSpace($InitialCatalog)) {
                $builder['Initial Catalog'] = $InitialCatalog
            }
            if (![string]::IsNullOrWhiteSpace($IntegratedSecurity)) {
                $builder['Integrated Security'] = $IntegratedSecurity
            }
            if (![string]::IsNullOrWhiteSpace($UserName)) {
                $builder["User ID"] = $UserName
            }
            if (![string]::IsNullOrWhiteSpace($Password)) {
                $builder['Password'] = $Password
            }
            if (![string]::IsNullOrWhiteSpace($WorkstationId)) {
                $builder['Workstation ID'] = $WorkstationId
            }
            if ($MultipleActiveResultSets -eq $true) {
                $builder['MultipleActiveResultSets'] = $true
            }
            if ($ColumnEncryptionSetting -eq [Data.SqlClient.SqlConnectionColumnEncryptionSetting]::Enabled) {
                $builder['Column Encryption Setting'] = [Data.SqlClient.SqlConnectionColumnEncryptionSetting]::Enabled
            }
            $builder
        }
    }
}
function New-DbaSqlDirectory {
    <#
        .SYNOPSIS
            Creates new path as specified by the path variable

        .DESCRIPTION
            Uses master.dbo.xp_create_subdir to create the path
            Returns $true if the path can be created, $false otherwise

        .PARAMETER SqlInstance
            The SQL Server you want to run the test on.

        .PARAMETER Path
            The Path to tests. Can be a file or directory.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Path, Directory, Folder
            Author: Stuart Moore

            Requires: Admin access to server (not SQL Services),
            Remoting must be enabled and accessible if $SqlInstance is not local

            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/New-DbaSqlDirectory

        .EXAMPLE
            New-DbaSqlDirectory -SqlInstance sqlcluster -Path L:\MSAS12.MSSQLSERVER\OLAP

            If the SQL Server instance sqlcluster can create the path L:\MSAS12.MSSQLSERVER\OLAP it will do and return $true, if not it will return $false.

        .EXAMPLE
            $credential = Get-Credential
            New-DbaSqlDirectory -SqlInstance sqlcluster -SqlCredential $credential -Path L:\MSAS12.MSSQLSERVER\OLAP

            If the SQL Server instance sqlcluster can create the path L:\MSAS12.MSSQLSERVER\OLAP it will do and return $true, if not it will return $false. Uses a SqlCredential to connect
    #>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Parameter(Mandatory = $true)]
        [string]$Path,
        [PSCredential]$SqlCredential,
        [switch]$EnableException
    )
    
    foreach ($instance in $SqlInstance) {
        try {
            Write-Message -Level Verbose -Message "Connecting to $instance."
            $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
        }
        
        $Path = $Path.Replace("'", "''")
        
        $exists = Test-DbaSqlPath -SqlInstance $sqlinstance -SqlCredential $SqlCredential -Path $Path
        
        if ($exists) {
            Stop-Function -Message "$Path already exists" -Target $server -Continue
        }
        
        $sql = "EXEC master.dbo.xp_create_subdir'$path'"
        Write-Message -Level Debug -Message $sql
        
        try {
            $query = $server.Query($sql)
            $Created = $true
        }
        catch {
            $Created = $false
            Stop-Function -Message "Failure" -ErrorRecord $_
        }
        
        [pscustomobject]@{
            Server  = $SqlInstance
            Path    = $Path
            Created = $Created
        }
    }
}
function New-DbaSsisCatalog {
    <#
        .SYNOPSIS
            Enables the SSIS Catalog on a SQL Server 2012+

        .DESCRIPTION
            After installing the SQL Server Engine and SSIS you still have to enable the SSIS Catalog. This function will enable the catalog and gives the option of supplying the password.

        .PARAMETER SqlInstance
            SQL Server you wish to run the function on.

        .PARAMETER SqlCredential
            Credentials used to connect to the SQL Server

        .PARAMETER Password
            Required password that will be used for the security key in SSISDB.

        .PARAMETER SsisCatalog
            SSIS catalog name. By default, this is SSISDB.

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: SSIS, SSISDB, Catalog
            Author: Stephen Bennett, https://sqlnotesfromtheunderground.wordpress.com/
            Tags:
            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/New-DbaSsisCatalog

        .EXAMPLE
            $password = ConvertTo-SecureString MyVisiblePassWord -AsPlainText -Force
            New-DbaSsisCatalog -SqlInstance sql2016 -Password $password

            Creates the SSIS Catalog on server DEV01 with the specified password.

        .EXAMPLE
            $password = Read-Host -AsSecureString -Prompt "Enter password"
            New-DbaSsisCatalog -SqlInstance DEV01 -Password $password

            Creates the SSIS Catalog on server DEV01 with the specified password.
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory = $true)]
        [Security.SecureString]$Password,
        [string]$SsisCatalog = "SSISDB",
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            ## check if SSIS and Engine running on box
            $services = Get-DbaSqlService -ComputerName $server.ComputerName

            $ssisservice = $Services | Where-Object { $_.ServiceType -eq "SSIS" -and $_.State -eq "Running" }

            if (-not $ssisservice) {
                Stop-Function -Message "SSIS is not running on $instance" -Continue -Target $instance
            }

            #if SQL 2012 or higher only validate databases with ContainmentType = NONE
            $clrenabled = Get-DbaSpConfigure -SqlInstance $server -Config IsSqlClrEnabled

            if (!$clrenabled.RunningValue) {
                Stop-Function -Message 'CLR Integration must be enabled.  You can enable it by running Set-DbaSpConfigure -SqlInstance sql2012 -Config IsSqlClrEnabled -Value $true' -Continue -Target $instance
            }

            try {
                $ssis = New-Object Microsoft.SqlServer.Management.IntegrationServices.IntegrationServices $server
            }
            catch {
                Stop-Function -Message "Can't load server" -Target $instance -ErrorRecord $_
                return
            }

            if ($ssis.Catalogs[$SsisCatalog]) {
                Stop-Function -Message "SSIS Catalog already exists" -Continue -Target $ssis.Catalogs[$SsisCatalog]
            }
            else {
                if ($Pscmdlet.ShouldProcess($server, "Creating SSIS catalog: $SsisCatalog")) {
                    try {
                        $ssisdb = New-Object Microsoft.SqlServer.Management.IntegrationServices.Catalog ($ssis, $SsisCatalog, $(([System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($password)))))
                        $ssisdb.Create()

                        [pscustomobject]@{
                            ComputerName = $server.ComputerName
                            InstanceName = $server.ServiceName
                            SqlInstance  = $server.DomainInstanceName
                            SsisCatalog  = $SsisCatalog
                            Created      = $true
                        }
                    }
                    catch {
                        Stop-Function -Message "Failed to create SSIS Catalog: $_" -Target $_ -Continue
                    }
                }
            }
        }
    }
}
function New-DbatoolsSupportPackage {
    <#
    .SYNOPSIS
    Creates a package of troubleshooting information that can be used by dbatools to help debug issues.

    .DESCRIPTION
    This function creates an extensive debugging package that can help with reproducing and fixing issues.

    The file will be created on the desktop by default and will contain quite a bit of information:
        - OS Information
        - Hardware Information (CPU, Ram, things like that)
        - .NET Information
        - PowerShell Information
        - Your input history
        - The In-Memory message log
        - The In-Memory error log
        - Screenshot of the console buffer (Basically, everything written in your current console, even if you have to scroll upwards to see it.

    .PARAMETER Path
    The folder where to place the output xml in.

    .PARAMETER Variables
    Name of additional variables to attach.
    This allows you to add the content of variables to the support package, if you believe them to be relevant to the case.

    .PARAMETER EnableException
    By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
    Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
    Author: Fred Weinmann (@FredWeinmann)
    Tags: Debug

    Website: https://dbatools.io
    Copyright: (C) Chrissy LeMaire, [email protected]
    License: MIT https://opensource.org/licenses/MIT

    .LINK
    https://dbatools.io/New-DbatoolsSupportPackage

    .EXAMPLE
    New-DbatoolsSupportPackage

    Creates a large support pack in order to help us troubleshoot stuff.
    #>
    [CmdletBinding()]
    param (
        [string]
        $Path = "$($env:USERPROFILE)\Desktop",

        [string[]]
        $Variables,

        [switch]
        [Alias('Silent')]$EnableException
    )

    BEGIN {
        Write-Message -Level InternalComment -Message "Starting"
        Write-Message -Level Verbose -Message "Bound parameters: $($PSBoundParameters.Keys -join ", ")"

        #region Helper functions
        function Get-ShellBuffer {
            [CmdletBinding()]
            Param ()

            try {
                # Define limits
                $rec = New-Object System.Management.Automation.Host.Rectangle
                $rec.Left = 0
                $rec.Right = $host.ui.rawui.BufferSize.Width - 1
                $rec.Top = 0
                $rec.Bottom = $host.ui.rawui.BufferSize.Height - 1

                # Load buffer
                $buffer = $host.ui.rawui.GetBufferContents($rec)

                # Convert Buffer to list of strings
                $int = 0
                $lines = @()
                while ($int -le $rec.Bottom) {
                    $n = 0
                    $line = ""
                    while ($n -le $rec.Right) {
                        $line += $buffer[$int, $n].Character
                        $n++
                    }
                    $line = $line.TrimEnd()
                    $lines += $line
                    $int++
                }

                # Measure empty lines at the beginning
                $int = 0
                $temp = $lines[$int]
                while ($temp -eq "") { $int++; $temp = $lines[$int] }

                # Measure empty lines at the end
                $z = $rec.Bottom
                $temp = $lines[$z]
                while ($temp -eq "") { $z--; $temp = $lines[$z] }

                # Skip the line launching this very function
                $z--

                # Measure empty lines at the end (continued)
                $temp = $lines[$z]
                while ($temp -eq "") { $z--; $temp = $lines[$z] }

                # Cut results to the limit and return them
                return $lines[$int .. $z]
            }
            catch { }
        }
        #endregion Helper functions
    }
    PROCESS {
        $filePathXml = "$($Path.Trim('\'))\dbatools_support_pack_$(Get-Date -Format "yyyy_MM_dd-HH_mm_ss").xml"
        $filePathZip = $filePathXml -replace "\.xml$", ".zip"

        Write-Message -Level Critical -Message @"
Gathering information...
Will write the final output to: $filePathZip

Please submit this file to the team, to help with troubleshooting whatever issue you encountered.
Be aware that this package contains a lot of information including your input history in the console.
Please make sure no sensitive data (such as passwords) can be caught this way.

Ideally start a new console, perform the minimal steps required to reproduce the issue, then run this command.
This will make it easier for us to troubleshoot and you won't be sending us the keys to your castle.
"@

        $hash = @{ }
        Write-Message -Level Output -Message "Collecting dbatools logged messages (Get-DbatoolsLog)"
        $hash["Messages"] = Get-DbatoolsLog
        Write-Message -Level Output -Message "Collecting dbatools logged errors (Get-DbatoolsLog -Errors)"
        $hash["Errors"] = Get-DbatoolsLog -Errors
        Write-Message -Level Output -Message "Collecting copy of console buffer (what you can see on your console)"
        $hash["ConsoleBuffer"] = Get-ShellBuffer
        Write-Message -Level Output -Message "Collecting Operating System information (Win32_OperatingSystem)"
        $hash["OperatingSystem"] = Get-DbaCmObject -ClassName Win32_OperatingSystem
        Write-Message -Level Output -Message "Collecting CPU information (Win32_Processor)"
        $hash["CPU"] = Get-DbaCmObject -ClassName Win32_Processor
        Write-Message -Level Output -Message "Collecting Ram information (Win32_PhysicalMemory)"
        $hash["Ram"] = Get-DbaCmObject -ClassName Win32_PhysicalMemory
        Write-Message -Level Output -Message "Collecting PowerShell & .NET Version (`$PSVersionTable)"
        $hash["PSVersion"] = $PSVersionTable
        Write-Message -Level Output -Message "Collecting Input history (Get-History)"
        $hash["History"] = Get-History
        Write-Message -Level Output -Message "Collecting list of loaded modules (Get-Module)"
        $hash["Modules"] = Get-Module
        Write-Message -Level Output -Message "Collecting list of loaded snapins (Get-PSSnapin)"
        $hash["SnapIns"] = Get-PSSnapin
        Write-Message -Level Output -Message "Collecting list of loaded assemblies (Name, Version, and Location)"
        $hash["Assemblies"] = [appdomain]::CurrentDomain.GetAssemblies() | Select-Object CodeBase, FullName, Location, ImageRuntimeVersion, GlobalAssemblyCache, IsDynamic

        if (Test-Bound "Variables") {
            Write-Message -Level Output -Message "Adding variables specified for export: $($Variables -join ", ")"
            $hash["Variables"] = $Variables | Get-Variable -ErrorAction Ignore
        }

        $data = [pscustomobject]$hash

        try { $data | Export-Clixml -Path $filePathXml -ErrorAction Stop }
        catch {
            Stop-Function -Message "Failed to export dump to file!" -ErrorRecord $_ -Target $filePathXml
            return
        }

        try { Compress-Archive -Path $filePathXml -DestinationPath $filePathZip -ErrorAction Stop }
        catch {
            Stop-Function -Message "Failed to pack dump-file into a zip archive. Please do so manually before submitting the results as the unpacked xml file will be rather large." -ErrorRecord $_ -Target $filePathZip
            return
        }

        Remove-Item -Path $filePathXml -ErrorAction Ignore
    }
    END {
        Write-Message -Level InternalComment -Message "Ending"
    }
}
function New-DbaXESession {
    <#
        .SYNOPSIS
            Creates a new XESession object - for the dogged.

        .DESCRIPTION
            Creates a new XESession object - for the dogged (very manual, Import-DbaXESession is recommended). See the following for more info:

            https://docs.microsoft.com/en-us/sql/relational-databases/extended-events/use-the-powershell-provider-for-extended-events

        .PARAMETER SqlInstance
            Target SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Name
            The Name of the session to be created.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/New-DbaXESession

        .EXAMPLE
            $session = New-DbaXESession -SqlInstance sql2017 -Name XeSession_Test
            $event = $session.AddEvent("sqlserver.file_written")
            $event.AddAction("package0.callstack")
            $session.Create()

            Returns a new XE Session object from sql2017 then adds an event, an action then creates it.

    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory)]
        [string]$Name,
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 11
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $SqlConn = $server.ConnectionContext.SqlConnectionObject
            $SqlStoreConnection = New-Object Microsoft.SqlServer.Management.Sdk.Sfc.SqlStoreConnection $SqlConn
            $store = New-Object  Microsoft.SqlServer.Management.XEvent.XEStore $SqlStoreConnection

            $store.CreateSession($Name)
        }
    }
}
function New-DbaXESmartCsvWriter {
    <#
        .SYNOPSIS
            This Response type is used to write Extended Events to a CSV file.

        .DESCRIPTION
            This Response type is used to write Extended Events to a CSV file.

        .PARAMETER OutputFile
            Specifies the path to the output CSV file.

        .PARAMETER Overwrite
            Specifies whether any existiting file should be overwritten or not.

        .PARAMETER OutputColumn
            Specifies the list of columns to output from the events. XESmartTarget will capture in memory and write to the target table only the columns (fields or targets) that are present in this list.

            Fields and actions are matched in a case-sensitive manner.

            Expression columns are supported. Specify a column with ColumnName AS Expression to add an expression column (Example: Total AS Reads + Writes)

        .PARAMETER Event
            Specifies a list of events to be processed (with others being ignored. By default, all events are processed.

        .PARAMETER Filter
            Specifies a filter expression in the same form as you would use in the WHERE clause of a SQL query.

            Example: duration > 10000 AND cpu_time > 10000

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
            SmartTarget: by Gianluca Sartori (@spaghettidba)

        .LINK
            https://dbatools.io/New-DbaXESmartCsvWriter
            https://github.com/spaghettidba/XESmartTarget/wiki

        .EXAMPLE
            $columns = "cpu_time", "duration", "physical_reads", "logical_reads", "writes", "row_count"
            $response = New-DbaXESmartCsvWriter -OutputFile c:\temp\workload.csv -OutputColumn $columns -OverWrite -Event "sql_batch_completed"
            Start-DbaXESmartTarget -SqlInstance localhost\sql2017 -Session "Profiler Standard" -Responder $response

            Writes Extended Events to the file "C:\temp\workload.csv".
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory)]
        [string]$OutputFile,
        [switch]$Overwrite,
        [string[]]$Event,
        [string[]]$OutputColumn,
        [string]$Filter,
        [switch]$EnableException
    )

    begin {
        try {
            Add-Type -Path "$script:PSModuleRoot\bin\XESmartTarget\XESmartTarget.Core.dll" -ErrorAction Stop
        }
        catch {
            Stop-Function -Message "Could not load XESmartTarget.Core.dll" -ErrorRecord $_ -Target "XESmartTarget"
            return
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }

        try {
            $writer = New-Object -TypeName XESmartTarget.Core.Responses.CsvAppenderResponse
            $writer.OutputFile = $OutputFile
            $writer.OverWrite = $Overwrite
            if (Test-Bound -ParameterName "Event") {
                $writer.Events = $Event
            }
            if (Test-Bound -ParameterName "OutputColumn") {
                $writer.OutputColumns = $OutputColumn
            }
            if (Test-Bound -ParameterName "Filter") {
                $writer.Filter = $Filter
            }
            $writer
        }
        catch {
            Stop-Function -Message "Failure" -ErrorRecord $_ -Target "XESmartTarget" -Continue
        }
    }
}
function New-DbaXESmartEmail {
    <#
        .SYNOPSIS
            This Response type can be used to send an email each time an event is captured.

        .DESCRIPTION
            This Response type can be used to send an email each time an event is captured.

        .PARAMETER SmtpServer
            Address of the SMTP server for outgoing mail.

        .PARAMETER Sender
            Sender's email address.

        .PARAMETER To
            Address of the To recipient(s).

        .PARAMETER Cc
            Address of the Cc recipient(s).

        .PARAMETER Bcc
            Address of the Bcc recipient(s).

        .PARAMETER Credential
            Credential object containing username and password used to authenticate on the SMTP server. When blank, no authentication is performed.

        .PARAMETER Subject
            Subject of the mail message. Accepts placeholders in the text.

            Placeholders are in the form {PropertyName}, where PropertyName is one of the fields or actions available in the Event object.

            For instance, a valid Subject in a configuration file looks like this: "An event of name {Name} occurred at {collection_time}"

        .PARAMETER Body
            Body of the mail message. The body can be static text or any property taken from the underlying event. See Subject for a description of how placeholders work.

        .PARAMETER Attachment
            Data to attach to the email message. At this time, it can be any of the fields/actions of the underlying event. The data from the field/action is attached to the message as an ASCII stream. A single attachment is supported.

        .PARAMETER AttachmentFileName
            File name to assign to the attachment.

        .PARAMETER PlainText
            If this switch is enabled, the email will be sent in plain text. By default, HTML formatting is used.

        .PARAMETER Event
            Each Response can be limited to processing specific events, while ignoring all the other ones. When this attribute is omitted, all events are processed.

        .PARAMETER Filter
            You can specify a filter expression by using this attribute. The filter expression is in the same form that you would use in a SQL query. For example, a valid example looks like this: duration > 10000 AND cpu_time > 10000

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
            SmartTarget: by Gianluca Sartori (@spaghettidba)

        .LINK
            https://dbatools.io/New-DbaXESmartEmail
            https://github.com/spaghettidba/XESmartTarget/wiki

        .EXAMPLE
            $params = @{
                SmtpServer = "smtp.ad.local"
                To = "[email protected]"
                Sender = "[email protected]"
                Subject = "Query executed"
                Body = "Query executed at {collection_time}"
                Attachment = "batch_text"
                AttachmentFileName = "query.sql"
            }
            $emailresponse = New-DbaXESmartEmail @params
            Start-DbaXESmartTarget -SqlInstance sql2017 -Session querytracker -Responder $emailresponse

            Sends an email each time a querytracker event is captured.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory)]
        [string]$SmtpServer,
        [parameter(Mandatory)]
        [string]$Sender,
        [parameter(Mandatory)]
        [string[]]$To,
        [string[]]$Cc,
        [string[]]$Bcc,
        [pscredential]$Credential,
        [parameter(Mandatory)]
        [string]$Subject,
        [parameter(Mandatory)]
        [string]$Body,
        [string]$Attachment,
        [string]$AttachmentFileName,
        [string]$PlainText,
        [string[]]$Event,
        [string]$Filter,
        [switch]$EnableException
    )
    begin {
        try {
            Add-Type -Path "$script:PSModuleRoot\bin\XESmartTarget\XESmartTarget.Core.dll" -ErrorAction Stop
        }
        catch {
            Stop-Function -Message "Could not load XESmartTarget.Core.dll." -ErrorRecord $_ -Target "XESmartTarget"
            return
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }

        try {
            $email = New-Object -TypeName XESmartTarget.Core.Responses.EmailResponse
            $email.SmtpServer = $SmtpServer
            $email.Sender = $Sender
            $email.To = $To
            $email.Cc = $Cc
            $email.Bcc = $Bcc
            $email.Subject = $Subject
            $email.Body = $Body
            $email.Attachment = $Attachment
            $email.AttachmentFileName = $AttachmentFileName
            $email.HTMLFormat = ($PlainText -eq $false)
            if (Test-Bound -ParameterName "Event") {
                $email.Events = $Event
            }
            if (Test-Bound -ParameterName "Filter") {
                $email.Filter = $Filter
            }

            if ($Credential) {
                $email.UserName = $Credential.UserName
                $email.Password = $Credential.GetNetworkCredential().Password
            }

            $email
        }
        catch {
            Stop-Function -Message "Failure" -ErrorRecord $_ -Target "XESmartTarget" -Continue
        }
    }
}
function New-DbaXESmartQueryExec {
    <#
        .SYNOPSIS
            This Response type executes a T-SQL command against a target database whenever an event is recorded.

        .DESCRIPTION
            This Response type executes a T-SQL command against a target database whenever an event is recorded.

        .PARAMETER SqlInstance
            Target SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies the name of the database that contains the target table.

        .PARAMETER Query
            The T-SQL command to execute. This string can contain placeholders for properties taken from the events.

            Placeholders are in the form {PropertyName}, where PropertyName is one of the fields or actions available in the Event object.

        .PARAMETER Event
            Each Response can be limited to processing specific events, while ignoring all the other ones. When this attribute is omitted, all events are processed.

        .PARAMETER Filter
            You can specify a filter expression by using this attribute. The filter expression is in the same form that you would use in a SQL query. For example, a valid example looks like this: duration > 10000 AND cpu_time > 10000

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/New-DbaXESmartQueryExec
            https://github.com/spaghettidba/XESmartTarget/wiki

        .EXAMPLE
            $response = New-DbaXESmartQueryExec -SqlInstance sql2017 -Database dbadb -Query "update table set whatever = 1"
            Start-DbaXESmartTarget -SqlInstance sql2017 -Session deadlock_tracker -Responder $response

            Executes a T-SQL command against dbadb on sql2017 whenever a deadlock event is recorded.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string]$Database,
        [string]$Query,
        [switch]$EnableException,
        [string[]]$Event,
        [string]$Filter
    )
    begin {
        try {
            Add-Type -Path "$script:PSModuleRoot\XESmartTarget\XESmartTarget.Core.dll" -ErrorAction Stop
        }
        catch {
            Stop-Function -Message "Could not load XESmartTarget.Core.dll" -ErrorRecord $_ -Target "XESmartTarget"
            return
        }
    }
    process {
        if (Test-FunctionInterrupt) {
            return
        }

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 11
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $execute = New-Object -TypeName XESmartTarget.Core.Responses.ExecuteTSQLResponse
            $execute.ServerName = $server.Name
            $execute.DatabaseName = $Database
            $execute.TSQL = $Query

            if ($SqlCredential) {
                $execute.UserName = $SqlCredential.UserName
                $execute.Password = $SqlCredential.GetNetworkCredential().Password
            }

            if (Test-Bound -ParameterName "Event") {
                $execute.Events = $Event
            }
            if (Test-Bound -ParameterName "Filter") {
                $execute.Filter = $Filter
            }

            $execute
        }
    }
}
function New-DbaXESmartReplay {
    <#
        .SYNOPSIS
            This Response type can be used to replay execution related events to a target SQL Server instance.

        .DESCRIPTION
            This Response type can be used to replay execution related events to a target SQL Server instance. The events that you can replay are of the type sql_batch_completed and rpc_completed: all other events are ignored.

        .PARAMETER SqlInstance
            Target SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Name of the initial catalog to connect to. Statements will be replayed by changing database to the same database where the event was originally captured, so this property only controls the initial database to connect to.

        .PARAMETER Event
            Each Response can be limited to processing specific events, while ignoring all the other ones. When this attribute is omitted, all events are processed.

        .PARAMETER Filter
            Specifies a filter expression in the same form as you would use in the WHERE clause of a SQL query.

            Example: duration > 10000 AND cpu_time > 10000

        .PARAMETER DelaySeconds
            Specifies the duration of the delay in seconds.

        .PARAMETER ReplayIntervalSeconds
            Specifies the duration of the replay interval in seconds.

        .PARAMETER StopOnError
            If this switch is enabled, the replay will be stopped when the first error is encountered. By default, error messages are piped to the log and console output, and replay proceeds.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
            SmartTarget: by Gianluca Sartori (@spaghettidba)

        .LINK
            https://dbatools.io/New-DbaXESmartReplay
            https://github.com/spaghettidba/XESmartTarget/wiki

        .EXAMPLE
            $response = New-DbaXESmartReplay -SqlInstance sql2017 -Database planning
            Start-DbaXESmartTarget -SqlInstance sql2016 -Session loadrelay -Responder $response

            Replays events from sql2016 on sql2017 in the planning database. Returns a PowerShell job object.

            To see a list of all SmartTarget job objects, use Get-DbaXESmartTarget.

        .EXAMPLE
            $response = New-DbaXESmartReplay -SqlInstance sql2017 -Database planning
            Start-DbaXESmartTarget -SqlInstance sql2017 -Session 'Profiler Standard' -Responder $response -NotAsJob

            Replays events from the 'Profiler Standard' session on sql2016 to sql2017's planning database. Does not run as a job so you can see the raw output.

    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string]$Database,
        [string[]]$Event = "sql_batch_completed",
        [string]$Filter,
        [int]$DelaySeconds,
        [switch]$StopOnError,
        [int]$ReplayIntervalSeconds,
        [switch]$EnableException
    )
    begin {
        try {
            Add-Type -Path "$script:PSModuleRoot\bin\XESmartTarget\XESmartTarget.Core.dll" -ErrorAction Stop
        }
        catch {
            Stop-Function -Message "Could not load XESmartTarget.Core.dll" -ErrorRecord $_ -Target "XESmartTarget"
            return
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 11
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            try {
                $replay = New-Object -TypeName XESmartTarget.Core.Responses.ReplayResponse
                $replay.ServerName = $instance
                $replay.DatabaseName = $Database
                $replay.Events = $Event
                $replay.StopOnError = $StopOnError
                $replay.Filter = $Filter
                $replay.DelaySeconds = $DelaySeconds
                $replay.ReplayIntervalSeconds = $ReplayIntervalSeconds

                if ($SqlCredential) {
                    $replay.UserName = $SqlCredential.UserName
                    $replay.Password = $SqlCredential.GetNetworkCredential().Password
                }

                $replay
            }
            catch {
                $message = $_.Exception.InnerException.InnerException | Out-String
                Stop-Function -Message $message -Target "XESmartTarget" -Continue
            }
        }
    }
}
function New-DbaXESmartTableWriter {
    <#
        .SYNOPSIS
            This Response type is used to write Extended Events to a database table.

        .DESCRIPTION
            This Response type is used to write Extended Events to a database table. The events are temporarily stored in memory before being written to the database at regular intervals.

            The target table can be created manually upfront or you can let the TableAppenderResponse create a target table based on the fields and actions available in the events captured.

            The columns of the target table and the fields/actions of the events are mapped by name (case-sensitive).

       .PARAMETER SqlInstance
            Target SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies the name of the database that contains the target table.

        .PARAMETER Table
            Specifies the name of the target table.

        .PARAMETER AutoCreateTargetTable
            If this switch is enabled, XESmartTarget will infer the definition of the target table from the columns captured in the Extended Events session.

            If the target table already exists, it will not be recreated.

        .PARAMETER UploadIntervalSeconds
            Specifies the number of seconds XESmartTarget will keep the events in memory before dumping them to the target table. The default is 10 seconds.

        .PARAMETER OutputColumn
            Specifies the list of columns to output from the events. XESmartTarget will capture in memory and write to the target table only the columns (fields or targets) that are present in this list.

            Fields and actions are matched in a case-sensitive manner.

            Expression columns are supported. Specify a column with ColumnName AS Expression to add an expression column (Example: Total AS Reads + Writes)

        .PARAMETER Event
            Specifies a list of events to be processed (with others being ignored. By default, all events are processed.

        .PARAMETER Filter
            Specifies a filter expression in the same form as you would use in the WHERE clause of a SQL query.

            Example: duration > 10000 AND cpu_time > 10000

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
            SmartTarget: by Gianluca Sartori (@spaghettidba)

        .LINK
            https://dbatools.io/New-DbaXESmartTableWriter
            https://github.com/spaghettidba/XESmartTarget/wiki

        .EXAMPLE
            $columns = "cpu_time", "duration", "physical_reads", "logical_reads", "writes", "row_count", "batch_text"
            $response = New-DbaXESmartTableWriter -SqlInstance sql2017 -Database dbadb -Table deadlocktracker -OutputColumn $columns -Filter "duration > 10000"
            Start-DbaXESmartTarget -SqlInstance sql2017 -Session deadlock_tracker -Responder $response

            Writes Extended Events to the deadlocktracker table in dbadb on sql2017.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory)]
        [string]$Database,
        [parameter(Mandatory)]
        [string]$Table,
        [switch]$AutoCreateTargetTable,
        [int]$UploadIntervalSeconds = 10,
        [string[]]$Event,
        [string[]]$OutputColumn,
        [string]$Filter,
        [switch]$EnableException
    )
    begin {
        try {
            Add-Type -Path "$script:PSModuleRoot\bin\XESmartTarget\XESmartTarget.Core.dll" -ErrorAction Stop
        }
        catch {
            Stop-Function -Message "Could not load XESmartTarget.Core.dll" -ErrorRecord $_ -Target "XESmartTarget"
            return
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 11
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            try {
                $writer = New-Object -TypeName XESmartTarget.Core.Responses.TableAppenderResponse
                $writer.ServerName = $server.Name
                $writer.DatabaseName = $Database
                $writer.TableName = $Table
                $writer.AutoCreateTargetTable = $AutoCreateTargetTable
                $writer.UploadIntervalSeconds = $UploadIntervalSeconds
                if (Test-Bound -ParameterName "Event") {
                    $writer.Events = $Event
                }
                if (Test-Bound -ParameterName "OutputColumn") {
                    $writer.OutputColumns = $OutputColumn
                }
                if (Test-Bound -ParameterName "Filter") {
                    $writer.Filter = $Filter
                }
                $writer
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_ -Target "XESmartTarget" -Continue
            }
        }
    }
}
function Publish-DbaDacpac {
    <#
        .SYNOPSIS
            The Publish-Database command takes a dacpac which is the output from an SSDT project and publishes it to a database. Changing the schema to match the dacpac and also to run any scripts in the dacpac (pre/post deploy scripts).

        .DESCRIPTION
            Deploying a dacpac uses the DacFx which historically needed to be installed on a machine prior to use. In 2016 the DacFx was supplied by Microsoft as a nuget package and this uses that nuget package.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Path
            Specifies the filesystem path to the DACPAC

        .PARAMETER PublishXml
            Specifies the publish profile which will include options and sqlCmdVariables.

        .PARAMETER Database
            Specifies the name of the database being published.

        .PARAMETER ConnectionString
            Specifies the connection string to the database you are upgrading. This is not required if SqlInstance is specified.

        .PARAMETER GenerateDeploymentScript
            If this switch is enabled, the publish script will be generated.

        .PARAMETER GenerateDeploymentReport
            If this switch is enabled, the publish XML report  will be generated.

        .PARAMETER OutputPath
            Specifies the filesystem path (directory) where output files will be generated.

        .PARAMETER ScriptOnly
            If this switch is enabled, only the change scripts will be generated.

        .PARAMETER IncludeSqlCmdVars
            If this switch is enabled, SqlCmdVars in publish.xml will have their values overwritten.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER DacFxPath
            Path to the dac dll. If this is ommited, then the version of dac dll which is packaged with dbatools is used.

        .NOTES
            Tags: Migration, Database, Dacpac
            Author: Richie lee (@bzzzt_io)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Publish-DbaDacpac

        .EXAMPLE
            Publish-DbaDacpac -SqlInstance sql2017 -Database WideWorldImporters -Path C:\temp\sql2016-WideWorldImporters.dacpac -PublishXml C:\temp\sql2016-WideWorldImporters-publish.xml

            Updates WideWorldImporters on sql2017 from the sql2016-WideWorldImporters.dacpac using the sql2016-WideWorldImporters-publish.xml publish profile

        .EXAMPLE
            New-DbaPublishProfile -SqlInstance sql2016 -Database db2 -Path C:\temp
            Export-DbaDacpac -SqlInstance sql2016 -Database db2 | Publish-DbaDacpac -PublishXml C:\temp\sql2016-db2-publish.xml -Database db1, db2 -SqlInstance sql2017

            Creates a publish profile at C:\temp\sql2016-db2-publish.xml, exports the .dacpac to $home\Documents\sql2016-db2.dacpac
            then publishes it to the sql2017 server database db2
        
        .EXAMPLE
        $loc = "C:\Users\bob\source\repos\Microsoft.Data.Tools.Msbuild\lib\net46\Microsoft.SqlServer.Dac.dll"
        Publish-DbaDacpac -SqlInstance "local" -Database WideWorldImporters -Path C:\temp\WideWorldImporters.dacpac -PublishXml C:\temp\WideWorldImporters.publish.xml -DacFxPath $loc
  #>
    [CmdletBinding()]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [string]$Path,
        [Parameter(Mandatory)]
        [string]$PublishXml,
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [string[]]$Database,
        [string[]]$ConnectionString,
        [switch]$GenerateDeploymentScript,
        [switch]$GenerateDeploymentReport,
        [Switch]$ScriptOnly,
        [string]$OutputPath = "$home\Documents",
        [switch]$IncludeSqlCmdVars,
        [switch]$EnableException,
        [String]$DacFxPath
    )

    begin {
        if ((Test-Bound -Not -ParameterName SqlInstance) -and (Test-Bound -Not -ParameterName ConnectionString)) {
            Stop-Function -Message "You must specify either SqlInstance or ConnectionString."
        }
        if ((Test-Bound -ParameterName GenerateDeploymentScript) -or (Test-Bound -ParameterName GenerateDeploymentReport)) {
            $defaultcolumns = 'ComputerName', 'InstanceName', 'SqlInstance', 'Database', 'Dacpac', 'PublishXml', 'Result', 'DatabaseScriptPath', 'MasterDbScriptPath', 'DeploymentReport', 'DeployOptions', 'SqlCmdVariableValues'
        }
        else {
            $defaultcolumns = 'ComputerName', 'InstanceName', 'SqlInstance', 'Database', 'Dacpac', 'PublishXml', 'Result', 'DeployOptions', 'SqlCmdVariableValues'
        }
        if ((Test-Bound -ParameterName ScriptOnly) -and (Test-Bound -Not -ParameterName GenerateDeploymentScript) -and (Test-Bound -Not -ParameterName GenerateDeploymentScript)) {
            Stop-Function -Message "You must at least one of GenerateDeploymentScript or GenerateDeploymentReport when using ScriptOnly"
        }

        function Get-ServerName ($connstring) {
            $builder = New-Object System.Data.Common.DbConnectionStringBuilder
            $builder.set_ConnectionString($connstring)
            $instance = $builder['data source']

            if (-not $instance) {
                $instance = $builder['server']
            }

            return $instance.ToString().Replace('\', '-').Replace('(','').Replace(')','')
        }
        if (Test-Bound -Not -ParameterName 'DacfxPath'){
            $dacfxPath = "$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.Dac.dll"
        }

        if ((Test-Path $dacfxPath) -eq $false) {
            Stop-Function -Message 'No usable version of Dac Fx found.' -EnableException $EnableException
        }
        else {
            try {
                Add-Type -Path $dacfxPath
                Write-Message -Level Verbose -Message "Dac Fx loaded."
            }
            catch {
                Stop-Function -Message 'No usable version of Dac Fx found.' -EnableException $EnableException -ErrorRecord $_
            }
        }
    }

    process {
        if (Test-FunctionInterrupt) { return }

        if (-not (Test-Path -Path $Path)) {
            Stop-Function -Message "$Path not found!"
        }

        if (-not (Test-Path -Path $PublishXml)) {
            Stop-Function -Message "$PublishXml not found!"
        }

        foreach ($instance in $sqlinstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure." -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $ConnectionString += $server.ConnectionContext.ConnectionString.Replace('"', "'")
        }

        try {
            $dacPackage = [Microsoft.SqlServer.Dac.DacPackage]::Load($Path)
        }
        catch {
            Stop-Function -Message "Could not load package." -ErrorRecord $_
        }

        try {
            $dacProfile = [Microsoft.SqlServer.Dac.DacProfile]::Load($PublishXml)
        }
        catch {
            Stop-Function -Message "Could not load profile." -ErrorRecord $_
        }

        if ($IncludeSqlCmdVars) {
            Get-SqlCmdVars -SqlCommandVariableValues $dacProfile.DeployOptions.SqlCommandVariableValues
        }

        foreach ($connstring in $ConnectionString) {
            $cleaninstance = Get-ServerName $connstring
            $instance = $cleaninstance.ToString().Replace('--', '\')

            foreach ($dbname in $database) {
                if ($GenerateDeploymentScript -or $GenerateDeploymentReport) {
                    $timeStamp = (Get-Date).ToString("yyMMdd_HHmmss_f")
                    $DatabaseScriptPath = Join-Path $OutputPath "$cleaninstance-$dbname`_DeployScript_$timeStamp.sql"
                    $MasterDbScriptPath = Join-Path $OutputPath "$cleaninstance-$dbname`_Master.DeployScript_$timeStamp.sql"
                    $DeploymentReport = Join-Path $OutputPath "$cleaninstance-$dbname`_Result.DeploymentReport_$timeStamp.xml"
                }

                if ($connstring -notmatch 'Database=') {
                    $connstring = "$connstring;Database=$dbname"
                }

                try {
                    $dacServices = New-Object Microsoft.SqlServer.Dac.DacServices $connstring
                }
                catch {
                    Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $server -Continue
                }

                $options = @{
                    GenerateDeploymentScript = $GenerateDeploymentScript
                    GenerateDeploymentReport = $GenerateDeploymentReport
                    DatabaseScriptPath       = $DatabaseScriptPath
                    MasterDbScriptPath       = $MasterDbScriptPath
                    DeployOptions            = $dacProfile.DeployOptions
                }

                try {
                    $global:output = @()
                    Register-ObjectEvent -InputObject $dacServices -EventName "Message" -SourceIdentifier "msg" -Action { $global:output += $EventArgs.Message.Message } | Out-Null
                    if ($ScriptOnly) {
                        Write-Message -Level Verbose -Message "Generating script."
                        $result = $dacServices.Script($dacPackage, $dbname, $options)
                    }
                    else {
                        Write-Message -Level Verbose -Message "Executing Deployment."
                        $result = $dacServices.Publish($dacPackage, $dbname, $options)
                    }
                }
                catch [Microsoft.SqlServer.Dac.DacServicesException] {
                        Stop-Function -Message "Deployment failed" -ErrorRecord $_ -EnableException $true
                }
                finally {
                    Unregister-Event -SourceIdentifier "msg"
                    if ($GenerateDeploymentReport) {
                        $result.DeploymentReport | Out-File $DeploymentReport
                        Write-Message -Level Verbose -Message "Deployment Report - $DeploymentReport."
                    }
                    if ($GenerateDeploymentScript) {
                        Write-Message -Level Verbose -Message "Database change script - $DatabaseScriptPath."
                        if ((Test-Path $MasterDbScriptPath)) {
                            Write-Message -Level Verbose -Message "Master database change script - $($result.MasterDbScript)."
                        }
                    }
                    $resultoutput = ($global:output -join "`r`n" | Out-String).Trim()
                    if ($resultoutput -match "Failed" -and ($GenerateDeploymentReport -or $GenerateDeploymentScript)) {
                        Write-Message -Level Warning -Message "Seems like the attempt to publish/script may have failed. If scripts have not generated load dacpac into Visual Studio to check SQL is valid."
                    }
                    $server = [dbainstance]$instance
                    $deployOptions = $dacProfile.DeployOptions | Select-Object -Property * -ExcludeProperty "SqlCommandVariableValues"
                    [pscustomobject]@{
                        ComputerName         = $server.ComputerName
                        InstanceName         = $server.InstanceName
                        SqlInstance          = $server.FullName
                        Database             = $dbname
                        Result               = $resultoutput
                        Dacpac               = $Path
                        PublishXml           = $PublishXml
                        ConnectionString     = $connstring
                        DatabaseScriptPath   = $DatabaseScriptPath
                        MasterDbScriptPath   = $MasterDbScriptPath
                        DeploymentReport     = $DeploymentReport
                        DeployOptions        = $deployOptions
                        SqlCmdVariableValues = $dacProfile.DeployOptions.SqlCommandVariableValues.Keys

                    } | Select-DefaultView -Property $defaultcolumns
                }
            }
        }
    }
}
function Read-DbaAuditFile {
    <#
        .SYNOPSIS
            Read Audit details from a sqlaudit file.

        .DESCRIPTION
            Read Audit details from a sqlaudit file.

        .PARAMETER Path
            The path to the sqlaudit file. This is relative to the computer executing the command. UNC paths are supported.

        .PARAMETER Exact
            If this switch is enabled, only an exact search will be used for the Path. By default, this command will add a wildcard to the Path because Eventing uses the file name as a template and adds characters.

        .PARAMETER Raw
            If this switch is enabled, the Microsoft.SqlServer.XEvent.Linq.PublishedEvent enumeration object will be returned.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, Audit
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Read-DbaAuditFile

        .EXAMPLE
            Read-DbaAuditFile -Path C:\temp\logins.sqlaudit

            Returns events from C:\temp\logins.sqlaudit.

        .EXAMPLE
            Get-ChildItem C:\temp\audit\*.sqlaudit | Read-DbaAuditFile

            Returns events from all .sqlaudit files in C:\temp\audit.

        .EXAMPLE
            Get-DbaServerAudit -SqlInstance sql2014 -Audit LoginTracker | Read-DbaAuditFile

            Reads remote Audit details by accessing the file over the admin UNC share.

    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias('FullName')]
        [object[]]$Path,
        [switch]$Exact,
        [switch]$Raw,
        [switch]$EnableException
    )
    process {
        foreach ($file in $path) {
            # in order to ensure CSV gets all fields, all columns will be
            # collected and output in the first (all all subsequent) object
            $columns = @("name", "timestamp")

            if ($file -is [System.String]) {
                $currentfile = $file
                $manualadd = $true
            }
            elseif ($file -is [System.IO.FileInfo]) {
                $currentfile = $file.FullName
                $manualadd = $true
            }
            else {
                if ($file -isnot [Microsoft.SqlServer.Management.Smo.Audit]) {
                    Stop-Function -Message "Unsupported file type."
                    return
                }

                if ($file.FullName.Length -eq 0) {
                    Stop-Function -Message "This Audit does not have an associated file."
                    return
                }

                $instance = [dbainstance]$file.ComputerName

                if ($instance.IsLocalHost) {
                    $currentfile = $file.FullName
                }
                else {
                    $currentfile = $file.RemoteFullName
                }
            }

            if (-not $Exact) {
                $currentfile = $currentfile.Replace('.sqlaudit', '*.sqlaudit')

                if ($currentfile -notmatch "sqlaudit") {
                    $currentfile = "$currentfile*.sqlaudit"
                }
            }

            $accessible = Test-Path -Path $currentfile
            $whoami = whoami

            if (-not $accessible) {
                if ($file.Status -eq "Stopped") { continue }
                Stop-Function -Continue -Message "$currentfile cannot be accessed from $($env:COMPUTERNAME). Does $whoami have access?"
            }

            if ($raw) {
                return New-Object Microsoft.SqlServer.XEvent.Linq.QueryableXEventData($currentfile)
            }

            $enum = New-Object Microsoft.SqlServer.XEvent.Linq.QueryableXEventData($currentfile)
            $newcolumns = ($enum.Fields.Name | Select-Object -Unique)

            $actions = ($enum.Actions.Name | Select-Object -Unique)
            foreach ($action in $actions) {
                $newcolumns += ($action -Split '\.')[-1]
            }

            $newcolumns = $newcolumns | Sort-Object
            $columns = ($columns += $newcolumns) | Select-Object -Unique

            # Make it selectable, otherwise it's a weird enumeration
            foreach ($event in (New-Object Microsoft.SqlServer.XEvent.Linq.QueryableXEventData($currentfile))) {
                $hash = [ordered]@{ }

                foreach ($column in $columns) {
                    $null = $hash.Add($column, $event.$column)
                }

                foreach ($action in $event.Actions) {
                    $hash[$action.Name] = $action.Value
                }

                foreach ($field in $event.Fields) {
                    $hash[$field.Name] = $field.Value
                }

                [pscustomobject]$hash
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#

function Read-DbaBackupHeader {
    <#
        .SYNOPSIS
            Reads and displays detailed information about a SQL Server backup.

        .DESCRIPTION
            Reads full, differential and transaction log backups. An online SQL Server is required to parse the backup files and the path specified must be relative to that SQL Server.

        .PARAMETER SqlInstance
            The SQL Server instance to use for parsing the backup files.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Path
            Path to SQL Server backup file. This can be a full, differential or log backup file. Accepts valid filesystem paths and URLs.

        .PARAMETER Simple
            If this switch is enabled, fewer columns are returned, giving an easy overview.

        .PARAMETER FileList
            If this switch is enabled, detailed information about the files within the backup is returned.

        .PARAMETER AzureCredential
            Name of the SQL Server credential that should be used for Azure storage access.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message. This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: DisasterRecovery, Backup, Restore
            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Read-DbaBackupHeader

        .EXAMPLE
            Read-DbaBackupHeader -SqlInstance sql2016 -Path S:\backups\mydb\mydb.bak

            Logs into sql2016 using Windows authentication and reads the local file on sql2016, S:\backups\mydb\mydb.bak.

            If you are running this command on a workstation and connecting remotely, remember that sql2016 cannot access files on your own workstation.

        .EXAMPLE
            Read-DbaBackupHeader -SqlInstance sql2016 -Path \\nas\sql\backups\mydb\mydb.bak, \\nas\sql\backups\otherdb\otherdb.bak

            Logs into sql2016 and reads two backup files - mydb.bak and otherdb.bak. The SQL Server service account must have rights to read this file.

        .EXAMPLE
            Read-DbaBackupHeader -SqlInstance . -Path C:\temp\myfile.bak -Simple

            Logs into the local workstation (or computer) and shows simplified output about C:\temp\myfile.bak. The SQL Server service account must have rights to read this file.

        .EXAMPLE
            $backupinfo = Read-DbaBackupHeader -SqlInstance . -Path C:\temp\myfile.bak
            $backupinfo.FileList

            Displays detailed information about each of the datafiles contained in the backupset.

        .EXAMPLE
            Read-DbaBackupHeader -SqlInstance . -Path C:\temp\myfile.bak -FileList

            Also returns detailed information about each of the datafiles contained in the backupset.

        .EXAMPLE
            "C:\temp\myfile.bak", "\backupserver\backups\myotherfile.bak" | Read-DbaBackupHeader -SqlInstance sql2016

            Similar to running Read-DbaBackupHeader -SqlInstance sql2016 -Path "C:\temp\myfile.bak", "\backupserver\backups\myotherfile.bak"

        .EXAMPLE
            Get-ChildItem \\nas\sql\*.bak | Read-DbaBackupHeader -SqlInstance sql2016

            Gets a list of all .bak files on the \\nas\sql share and reads the headers using the server named "sql2016". This means that the server, sql2016, must have read access to the \\nas\sql share.

        .EXAMPLE
            Read-DbaBackupHeader -Path https://dbatoolsaz.blob.core.windows.net/azbackups/restoretime/restoretime_201705131850.bak
            -AzureCredential AzureBackupUser

            Gets the backup header information from the SQL Server backup file stored at https://dbatoolsaz.blob.core.windows.net/azbackups/restoretime/restoretime_201705131850.bak on Azure
    #>
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", '')]
    <# AzureCredential is utilized in this command is not a formal Credential object. #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance]$SqlInstance,
        [PsCredential]$SqlCredential,
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [object[]]$Path,
        [switch]$Simple,
        [switch]$FileList,
        [string]$AzureCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        foreach($p in $path) {
            if ([System.IO.Path]::GetExtension($p).Length -eq 0) {
                Stop-Function -Message "Path ($p) should be a file, not a folder" -Category InvalidArgument
                return
            }
        }
        Write-Message -Level InternalComment -Message "Starting reading headers"
        try {
            $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            return
        }
        $getHeaderScript = {
            Param (
                $SqlInstance,
                $Path,
                $DeviceType,
                $AzureCredential
            )
            #Copy existing connection to create an independent TSQL session
            $server = New-Object Microsoft.SqlServer.Management.Smo.Server $SqlInstance.ConnectionContext.Copy()
            $restore = New-Object Microsoft.SqlServer.Management.Smo.Restore

            if ($DeviceType -eq 'URL') {
                $restore.CredentialName = $AzureCredential
            }

            $device = New-Object Microsoft.SqlServer.Management.Smo.BackupDeviceItem $Path, $DeviceType
            $restore.Devices.Add($device)
            $dataTable = $restore.ReadBackupHeader($server)

            $null = $dataTable.Columns.Add("FileList", [object])

            $mb = $dataTable.Columns.Add("BackupSizeMB", [int])
            $mb.Expression = "BackupSize / 1024 / 1024"
            $gb = $dataTable.Columns.Add("BackupSizeGB")
            $gb.Expression = "BackupSizeMB / 1024"

            if ($null -eq $dataTable.Columns['CompressedBackupSize']) {
                $formula = "0"
            }
            else {
                $formula = "CompressedBackupSize / 1024 / 1024"
            }

            $cmb = $dataTable.Columns.Add("CompressedBackupSizeMB", [int])
            $cmb.Expression = $formula
            $cgb = $dataTable.Columns.Add("CompressedBackupSizeGB")
            $cgb.Expression = "CompressedBackupSizeMB / 1024"

            $null = $dataTable.Columns.Add("SqlVersion")

            $null = $dataTable.Columns.Add("BackupPath")

            foreach ($row in $dataTable) {
                $row.BackupPath = $Path
                $restore.FileNumber = $row.Position
                <# Select-Object does a quick and dirty conversion from datatable to PS object #>
                $row.FileList = $restore.ReadFileList($server) | Select-Object *
            }
            $dataTable
        }
    }

    process {
        if (Test-FunctionInterrupt) { return }

        #Extract fullnames from the file system objects
        $pathStrings = @()
        foreach ($pathItem in $Path) {
            if ($null -ne $pathItem.FullName) {
                $pathStrings += $pathItem.FullName
            }
            else {
                $pathStrings += $pathItem
            }
        }
        #Group by filename
        $pathGroup = $pathStrings | Group-Object -NoElement | Select-Object -ExpandProperty Name

        $pathCount = ($pathGroup | Measure-Object).Count
        Write-Message -Level Verbose -Message "$pathCount unique files to scan."
        Write-Message -Level Verbose -Message "Checking accessibility for all the files."

        $testPath = Test-DbaSqlPath -SqlInstance $server -Path $pathGroup

        #Setup initial session state
        $InitialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
        #Create Runspace pool, min - 1, max - 10 sessions: there is internal SQL Server queue for the restore operations. 10 threads seem to perform best
        $runspacePool = [runspacefactory]::CreateRunspacePool(1, 10, $InitialSessionState, $Host)
        $runspacePool.Open()

        $threads = @()

        foreach ($file in $pathGroup) {
            if ($file -like 'http*') {
                $deviceType = 'URL'
            }
            else {
                $deviceType = 'FILE'
            }
            if ($pathCount -eq 1) {
                $fileExists = $testPath
            }
            else {
                $fileExists = ($testPath | Where-Object FilePath -eq $file).FileExists
            }
            if ($fileExists -or $deviceType -eq 'URL') {
                #Create parameters hashtable
                $argsRunPool = @{
                    SqlInstance     = $server
                    Path            = $file
                    AzureCredential = $AzureCredential
                    DeviceType      = $deviceType
                }
                Write-Message -Level Verbose -Message "Scanning file $file."
                #Create new runspace thread
                $thread = [powershell]::Create()
                $thread.RunspacePool = $runspacePool
                $thread.AddScript($getHeaderScript) | Out-Null
                $thread.AddParameters($argsRunPool) | Out-Null
                #Start the thread
                $handle = $thread.BeginInvoke()
                $threads += [pscustomobject]@{
                    handle      = $handle
                    thread      = $thread
                    file        = $file
                    deviceType  = $deviceType
                    isRetrieved = $false
                    started     = Get-Date
                }
            }
            else {
                Write-Message -Level Warning -Message "File $file does not exist or access denied. The SQL Server service account may not have access to the source directory."
            }
        }
        #receive runspaces
        while ($threads | Where-Object { $_.isRetrieved -eq $false }) {
            $totalThreads = ($threads | Measure-Object).Count
            $totalRetrievedThreads = ($threads | Where-Object { $_.isRetrieved -eq $true } | Measure-Object).Count
            Write-Progress -Id 1 -Activity Updating -Status 'Progress' -CurrentOperation "Scanning Restore headers: $totalRetrievedThreads/$totalThreads" -PercentComplete ($totalRetrievedThreads / $totalThreads * 100)
            foreach ($thread in ($threads | Where-Object { $_.isRetrieved -eq $false })) {
                if ($thread.Handle.IsCompleted) {
                    $dataTable = $thread.thread.EndInvoke($thread.handle)
                    $thread.isRetrieved = $true
                    #Check if thread had any errors
                    if ($thread.thread.HadErrors) {
                        if ($thread.deviceType -eq 'FILE') {
                            Stop-Function -Message "Problem found with $($thread.file)." -Target $thread.file -ErrorRecord $thread.thread.Streams.Error -Continue
                        }
                        else {
                            Stop-Function -Message "Unable to read $($thread.file), check credential $AzureCredential and network connectivity." -Target $thread.file -ErrorRecord $thread.thread.Streams.Error -Continue
                        }
                    }
                    #Process the result of this thread

                    $dbVersion = $dataTable[0].DatabaseVersion
                    $SqlVersion = (Convert-DbVersionToSqlVersion $dbVersion)
                    foreach ($row in $dataTable) {
                        $row.SqlVersion = $SqlVersion
                        if ($row.BackupName -eq "*** INCOMPLETE ***") {
                            Stop-Function -Message "$($thread.file) appears to be from a new version of SQL Server than $SqlInstance, skipping" -Target $thread.file -Continue
                        }
                    }
                    if ($Simple) {
                        $dataTable | Select-Object DatabaseName, BackupFinishDate, RecoveryModel, BackupSizeMB, CompressedBackupSizeMB, DatabaseCreationDate, UserName, ServerName, SqlVersion, BackupPath
                    }
                    elseif ($FileList) {
                        $dataTable.filelist
                    }
                    else {
                        $dataTable
                    }

                    $thread.thread.Dispose()
                }
            }
            Start-Sleep -Milliseconds 500
        }
        #Close the runspace pool
        $runspacePool.Close()
    }
}
function Read-DbaTraceFile {
    <#
        .SYNOPSIS
        Reads SQL Server trace files

        .DESCRIPTION
        Using the fn_trace_gettable function, a trace file is read and returned as a PowerShell object

        This function returns the whole of the trace file. The information is presented in the format that the trace subsystem uses.

        .PARAMETER SqlInstance
        The target SQL Server instance

        .PARAMETER SqlCredential
        Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Path
        Path to the trace file. This path is relative to the SQL Server instance.

        .PARAMETER Database
        Search for results only with specific DatabaseName. Uses IN for comparisons.

        .PARAMETER Login
        Search for results only with specific Logins. Uses IN for comparisons.

        .PARAMETER Spid
        Search for results only with specific Spids. Uses IN for comparisons.

        .PARAMETER EventClass
        Search for results only with specific EventClasses. Uses IN for comparisons.

        .PARAMETER ObjectType
        Search for results only with specific ObjectTypes. Uses IN for comparisons.

        .PARAMETER Error
        Search for results only with specific Errors. Uses IN for comparisons.

        .PARAMETER EventSequence
        Search for results only with specific EventSequences. Uses IN for comparisons.

        .PARAMETER TextData
        Search for results only with specific TextData. Uses LIKE for comparisons.

        .PARAMETER ApplicationName
        Search for results only with specific ApplicationNames. Uses LIKE for comparisons.

        .PARAMETER ObjectName
        Search for results only with specific ObjectNames. Uses LIKE for comparisons.

        .PARAMETER Where
        Custom where clause - use without the word "WHERE". Here are the available columns:

        TextData
        BinaryData
        DatabaseID
        TransactionID
        LineNumber
        NTUserName
        NTDomainName
        HostName
        ClientProcessID
        ApplicationName
        LoginName
        SPID
        Duration
        StartTime
        EndTime
        Reads
        Writes
        CPU
        Permissions
        Severity
        EventSubClass
        ObjectID
        Success
        IndexID
        IntegerData
        ServerName
        EventClass
        ObjectType
        NestLevel
        State
        Error
        Mode
        Handle
        ObjectName
        DatabaseName
        FileName
        OwnerName
        RoleName
        TargetUserName
        DBUserName
        LoginSid
        TargetLoginName
        TargetLoginSid
        ColumnPermissions
        LinkedServerName
        ProviderName
        MethodName
        RowCounts
        RequestID
        XactSequence
        EventSequence
        BigintData1
        BigintData2
        GUID
        IntegerData2
        ObjectID2
        Type
        OwnerID
        ParentName
        IsSystem
        Offset
        SourceDatabaseID
        SqlHandle
        SessionLoginName
        PlanHandle
        GroupID

        .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
        Tags: Security, Trace
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
        Read-DbaTraceFile -SqlInstance sql2016 -Database master, tempdb -Path C:\traces\big.trc

        Reads the tracefile C:\traces\big.trc, stored on the sql2016 sql server. Filters only results that have master or tempdb as the DatabaseName.

        .EXAMPLE
        Read-DbaTraceFile -SqlInstance sql2016 -Database master, tempdb -Path C:\traces\big.trc -TextData 'EXEC SP_PROCOPTION'

        Reads the tracefile C:\traces\big.trc, stored on the sql2016 sql server.
        Filters only results that have master or tempdb as the DatabaseName and that have 'EXEC SP_PROCOPTION' somewhere in the text.

        .EXAMPLE
        Read-DbaTraceFile -SqlInstance sql2016 -Path C:\traces\big.trc -Where "LinkedServerName = 'myls' and StartTime > '5/30/2017 4:27:52 PM'"

        Reads the tracefile C:\traces\big.trc, stored on the sql2016 sql server.
        Filters only results where LinkServerName = myls and StartTime is greater than '5/30/2017 4:27:52 PM'.

        .EXAMPLE
        Get-DbaTrace -SqlInstance sql2014 | Read-DbaTraceFile

        Reads every trace file on sql2014

#>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory, ValueFromPipelineByPropertyName)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [parameter(ValueFromPipelineByPropertyName)]
        [PSCredential]$SqlCredential,
        [parameter(ValueFromPipelineByPropertyName)]
        [string[]]$Path,
        [string[]]$Database,
        [string[]]$Login,
        [int[]]$Spid,
        [string[]]$EventClass,
        [string[]]$ObjectType,
        [int[]]$Error,
        [int[]]$EventSequence,
        [string[]]$TextData,
        [string[]]$ApplicationName,
        [string[]]$ObjectName,
        [string]$Where,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        if ($where) {
            $Where = "where $where"
        }
        elseif ($Database -or $Login -or $Spid -or $ApplicationName -or $EventClass -or $ObjectName -or $ObjectType -or $EventSequence -or $Error) {

            $tempwhere = @()

            if ($Database) {
                $where = $database -join "','"
                $tempwhere += "databasename in ('$where')"
            }

            if ($Login) {
                $where = $Login -join "','"
                $tempwhere += "LoginName in ('$where')"
            }

            if ($Spid) {
                $where = $Spid -join ","
                $tempwhere += "Spid in ($where)"
            }

            if ($EventClass) {
                $where = $EventClass -join ","
                $tempwhere += "EventClass in ($where)"
            }

            if ($ObjectType) {
                $where = $ObjectType -join ","
                $tempwhere += "ObjectType in ($where)"
            }

            if ($Error) {
                $where = $Error -join ","
                $tempwhere += "Error in ($where)"
            }

            if ($EventSequence) {
                $where = $EventSequence -join ","
                $tempwhere += "EventSequence in ($where)"
            }

            if ($TextData) {
                $where = $TextData -join "%','%"
                $tempwhere += "TextData like ('%$where%')"
            }

            if ($ApplicationName) {
                $where = $ApplicationName -join "%','%"
                $tempwhere += "ApplicationName like ('%$where%')"
            }

            if ($ObjectName) {
                $where = $ObjectName -join "%','%"
                $tempwhere += "ObjectName like ('%$where%')"
            }

            $tempwhere = $tempwhere -join " and "
            $Where = "where $tempwhere"
        }
    }
    process {
        foreach ($instance in $sqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                return
            }

            if (Test-Bound -Parameter Path) {
                $currentpath = $path
            }
            else {
                $currentpath = $server.ConnectionContext.ExecuteScalar("Select path from sys.traces where is_default = 1")
            }

            foreach ($file in $currentpath) {
                Write-Message -Level Verbose -Message "Parsing $file"

                $exists = Test-DbaSqlPath -SqlInstance $server -Path $file

                if (!$exists) {
                    Write-Message -Level Warning -Message "Path does not exist" -Target $file
                    Continue
                }

                $sql = "select SERVERPROPERTY('MachineName') AS ComputerName,
                ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
                SERVERPROPERTY('ServerName') AS SqlInstance,
                 * FROM [fn_trace_gettable]('$file', DEFAULT) $Where"

                try {
                    $server.Query($sql)
                }
                catch {
                    Stop-Function -Message "Error returned from SQL Server: $_" -Target $server -InnerErrorRecord $_
                }
            }
        }
    }
}
function Read-DbaTransactionLog {
    <#
.SYNOPSIS
Reads the live Transaction log from specified SQL Server Database

.DESCRIPTION
Using the fn_dblog function, the live transaction log is read and returned as a PowerShell object

This function returns the whole of the log. The information is presented in the format that the logging subsystem uses.

A soft limit of 0.5GB of log as been implemented. This is based on testing. This limit can be overridden
at the users request, but please be aware that this may have an impact on your target databases and on the
system running this function

.PARAMETER SqlInstance
A SQL Server instance to connect to

.PARAMETER SqlCredential
A credential to use to connect to the SQL Instance rather than using Windows Authentication

.PARAMETER Database
Database to read the transaction log of

.PARAMETER IgnoreLimit
Switch to indicate that you wish to bypass the recommended limits of the function

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Database, Log, LogFile
Author: Stuart Moore (@napalmgram), stuart-moore.com

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
$Log = Read-DbaTransactionLog -SqlInstance sql2016 -Database MyDatabase

Will read the contents of the transaction log of MyDatabase on SQL Server Instance sql2016 into the local PowerShell object $Log

.EXAMPLE
$Log = Read-DbaTransactionLog -SqlInstance sql2016 -Database MyDatabase -IgnoreLimit

Will read the contents of the transaction log of MyDatabase on SQL Server Instance sql2016 into the local PowerShell object $Log, ignoring the recommnedation of not returning more that 0.5GB of log

#>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    Param (
        [parameter(Position = 0, Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory = $true)]
        [object]$Database,
        [Switch]$IgnoreLimit,
        [Alias('Silent')]
        [switch]$EnableException
    )

    try {
        $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
    }
    catch {
        Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
        return
    }

    if (-not $server.databases[$Database]) {
        Stop-Function -Message "$Database does not exist"
        return
    }

    if ($server.databases[$Database].Status -ne 'Normal') {
        Stop-Function -Message "$Database is not in a normal State, command will not run."
        return
    }

    if ($IgnoreLimit) {
        Write-Message -Level Verbose -Message "Please be aware that ignoring the recommended limits may impact on the performance of the SQL Server database and the calling system"
    }
    else {
        #Warn if more than 0.5GB of live log. Dodgy conversion as SMO returns the value in an unhelpful format :(
        $SqlSizeCheck = "select
                                sum(FileProperty(sf.name,'spaceused')*8/1024) as 'SizeMb'
                                from sys.sysfiles sf
                                where CONVERT(INT,sf.status & 0x40) / 64=1"
        $TransLogSize = $server.Query($SqlSizeCheck, $Database)
        if ($TransLogSize.SizeMb -ge 500) {
            Stop-Function -Message "$Database has more than 0.5 Gb of live log data, returning this may have an impact on the database and the calling system. If you wish to proceed please rerun with the -IgnoreLimit switch"
            return
        }
    }

    $sql = "select * from fn_dblog(NULL,NULL)"
    Write-Message -Level Debug -Message $sql
    Write-Message -Level Verbose -Message "Starting Log retrieval"
    $server.Query($sql, $Database)

}
function Read-DbaXEFile {
    <#
        .SYNOPSIS
            Read XEvents from a xel or xem file.

        .DESCRIPTION
            Read XEvents from a xel or xem file.

        .PARAMETER Path
            The path to the xel or xem file. This is relative to the computer executing the command. UNC paths are supported.

        .PARAMETER Exact
            If this switch is enabled, only an exact search will be used for the Path. By default, this command will add a wildcard to the Path because Eventing uses the file name as a template and adds characters.

        .PARAMETER Raw
            If this switch is enabled, the Microsoft.SqlServer.XEvent.Linq.PublishedEvent enumeration object will be returned.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Read-DbaXEFile

        .EXAMPLE
            Read-DbaXEFile -Path C:\temp\deadocks.xel

            Returns events from C:\temp\deadocks.xel.

        .EXAMPLE
            Get-ChildItem C:\temp\xe\*.xel | Read-DbaXEFile

            Returns events from all .xel files in C:\temp\xe.

        .EXAMPLE
            Get-DbaXESession -SqlInstance sql2014 -Session deadlocks | Read-DbaXEFile

            Reads remote XEvents by accessing the file over the admin UNC share.

    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias('FullName')]
        [object[]]$Path,
        [switch]$Exact,
        [switch]$Raw,
        [switch][Alias('Silent')]
        $EnableException
    )
    process {
        foreach ($file in $path) {
            # in order to ensure CSV gets all fields, all columns will be
            # collected and output in the first (all all subsequent) object
            $columns = @("name", "timestamp")

            if ($file -is [System.String]) {
                $currentfile = $file
                $manualadd = $true
            }
            elseif ($file -is [System.IO.FileInfo]) {
                $currentfile = $file.FullName
                $manualadd = $true
            }
            else {
                if ($file -isnot [Microsoft.SqlServer.Management.XEvent.Session]) {
                    Stop-Function -Message "Unsupported file type."
                    return
                }

                if ($file.TargetFile.Length -eq 0) {
                    Stop-Function -Message "This session does not have an associated Target File."
                    return
                }

                $instance = [dbainstance]$file.ComputerName

                if ($instance.IsLocalHost) {
                    $currentfile = $file.TargetFile
                }
                else {
                    $currentfile = $file.RemoteTargetFile
                }
            }

            if (-not $Exact) {
                $currentfile = $currentfile.Replace('.xel', '*.xel')
                $currentfile = $currentfile.Replace('.xem', '*.xem')

                if ($currentfile -notmatch "xel" -and $currentfile -notmatch "xem") {
                    $currentfile =  "$currentfile*.xel"
                }
            }

            $accessible = Test-Path -Path $currentfile
            $whoami = whoami

            if (-not $accessible) {
                if ($file.Status -eq "Stopped") { continue }
                Stop-Function -Continue -Message "$currentfile cannot be accessed from $($env:COMPUTERNAME). Does $whoami have access?"
            }

            if ($raw) {
                return New-Object Microsoft.SqlServer.XEvent.Linq.QueryableXEventData($currentfile)
            }

            $enum = New-Object Microsoft.SqlServer.XEvent.Linq.QueryableXEventData($currentfile)
            $newcolumns = ($enum.Fields.Name | Select-Object -Unique)

            $actions = ($enum.Actions.Name | Select-Object -Unique)
            foreach ($action in $actions) {
                $newcolumns += ($action -Split '\.')[-1]
            }

            $newcolumns = $newcolumns | Sort-Object
            $columns = ($columns += $newcolumns) | Select-Object -Unique

            # Make it selectable, otherwise it's a weird enumeration
            foreach ($event in (New-Object Microsoft.SqlServer.XEvent.Linq.QueryableXEventData($currentfile))) {
                $hash = [ordered]@{ }

                foreach ($column in $columns) {
                    $null = $hash.Add($column, $event.$column)
                }

                foreach ($action in $event.Actions) {
                    $hash[$action.Name] = $action.Value
                }

                foreach ($field in $event.Fields) {
                    $hash[$field.Name] = $field.Value
                }

                [pscustomobject]$hash
            }
        }
    }
}
function Register-DbaConfig {
    <#
        .SYNOPSIS
            Registers an existing configuration object in registry.

        .DESCRIPTION
            Registers an existing configuration object in registry.
            This allows simple persisting of settings across powershell consoles.
            It also can be used to generate a registry template, which can then be used to create policies.

        .PARAMETER Config
            The configuration object to write to registry.
            Can be retrieved using Get-DbaConfig.

        .PARAMETER FullName
            The full name of the setting to be written to registry.

        .PARAMETER Module
            The name of the module, whose settings should be written to registry.

        .PARAMETER Name
            Default: "*"
            Used in conjunction with the -Module parameter to restrict the number of configuration items written to registry.

        .PARAMETER Scope
            Default: UserDefault
            Who will be affected by this export how? Current user or all? Default setting or enforced?
            Legal values: UserDefault, UserMandatory, SystemDefault, SystemMandatory

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Config, Module
            Author: Friedrich Weinmann

        .EXAMPLE
            PS C:\> Get-DbaConfig message.* | Register-DbaConfig

            Retrieves all configuration items that that start with message. and registers them in registry for the current user.

        .EXAMPLE
            PS C:\> Register-DbaConfig -FullName "developer.mode.enable" -Scope SystemDefault

            Retrieves the configuration item "developer.mode.enable" and registers it in registry as the default setting for all users on this machine.

        .EXAMPLE
            PS C:\> Register-DbaConfig -Module message -Scope SystemMandatory

            Retrieves all configuration items of the module MyModule, then registers them in registry to enforce them for all users on the current system.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    Param (
        [Parameter(ParameterSetName = "Default", Position = 0, ValueFromPipeline = $true)]
        [Sqlcollaborative.Dbatools.Configuration.Config[]]
        $Config,

        [Parameter(ParameterSetName = "Default", Position = 0, ValueFromPipeline = $true)]
        [string[]]
        $FullName,

        [Parameter(Mandatory = $true, ParameterSetName = "Name", Position = 0)]
        [string]
        $Module,

        [Parameter(ParameterSetName = "Name", Position = 1)]
        [string]
        $Name = "*",

        [Sqlcollaborative.Dbatools.Configuration.ConfigScope]
        $Scope = "UserDefault",

        [switch]
        $EnableException
    )

    begin {
        $parSet = $PSCmdlet.ParameterSetName

        function Write-Config {
            [CmdletBinding()]
            Param (
                [Sqlcollaborative.Dbatools.Configuration.Config]
                $Config,

                [Sqlcollaborative.Dbatools.Configuration.ConfigScope]
                $Scope,

                [bool]
                $EnableException,

                [string]
                $FunctionName = (Get-PSCallStack)[0].Command
            )

            if (-not $Config -or ($Config.RegistryData -eq "<type not supported>")) {
                Stop-Function -Message "Invalid Input, cannot export $($Config.FullName), type not supported" -EnableException $EnableException -Category InvalidArgument -Target $Config -FunctionName $FunctionName #-ModuleName "PSFramework" -Tag "config", "fail"
                return
            }

            try {
                Write-Message -Level Verbose -Message "Registering $($Config.FullName) for $Scope" -Target $Config -FunctionName $FunctionName #-ModuleName "PSFramework" -Tag "Config"
                #region User Default
                if (1 -band $Scope) {
                    Ensure-RegistryPath -Path "HKCU:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\Config\Default" -ErrorAction Stop
                    Set-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\Config\Default" -Name $Config.FullName -Value $Config.RegistryData -ErrorAction Stop
                }
                #endregion User Default

                #region User Mandatory
                if (2 -band $Scope) {
                    Ensure-RegistryPath -Path "HKCU:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\Config\Enforced" -ErrorAction Stop
                    Set-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\Config\Enforced" -Name $Config.FullName -Value $Config.RegistryData -ErrorAction Stop
                }
                #endregion User Mandatory

                #region System Default
                if (4 -band $Scope) {
                    Ensure-RegistryPath -Path "HKLM:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\Config\Default" -ErrorAction Stop
                    Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\Config\Default" -Name $Config.FullName -Value $Config.RegistryData -ErrorAction Stop
                }
                #endregion System Default

                #region System Mandatory
                if (8 -band $Scope) {
                    Ensure-RegistryPath -Path "HKLM:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\Config\Enforced" -ErrorAction Stop
                    Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\Config\Enforced" -Name $Config.FullName -Value $Config.RegistryData -ErrorAction Stop
                }
                #endregion System Mandatory
            }
            catch {
                Stop-Function -Message "Failed to export $($Config.FullName), to scope $Scope" -EnableException $EnableException -Target $Config -ErrorRecord $_ -FunctionName $FunctionName #-ModuleName "PSFramework" -Tag "config", "fail"
                return
            }
        }

        function Ensure-RegistryPath {
            [CmdletBinding()]
            Param (
                [string]
                $Path
            )

            if (-not (Test-Path $Path)) {
                $null = New-Item $Path -Force -ErrorAction Stop
            }
        }
    }
    process {
        switch ($parSet) {
            "Default" {
                foreach ($item in $Config) {
                    Write-Config -Config $item -Scope $Scope -EnableException $EnableException
                }

                foreach ($item in $FullName) {
                    if ([Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations.ContainsKey($item.ToLower())) {
                        Write-Config -Config ([Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations[$item.ToLower()]) -Scope $Scope -EnableException $EnableException
                    }
                }
            }
            "Name" {
                foreach ($item in ([Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations.Values | Where-Object Module -EQ $Module | Where-Object Name -Like $Name)) {
                    Write-Config -Config $item -Scope $Scope -EnableException $EnableException
                }
            }
        }
    }
    end {

    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Remove-DbaAgentJob {
    <#
        .SYNOPSIS
            Remove-DbaAgentJob removes a job.

        .DESCRIPTION
            Remove-DbaAgentJob removes a a job in the SQL Server Agent.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Job
            The name of the job. Can be null if the the job id is being used.

        .PARAMETER KeepHistory
            Specifies to keep the history for the job. By default history is deleted.

        .PARAMETER KeepUnusedSchedule
            Specifies to keep the schedules attached to this job if they are not attached to any other job.
            By default the unused schedule is deleted.

        .PARAMETER Mode
            Default: Strict
            How strict does the command take lesser issues?
            Strict: Interrupt if the job specified doesn't exist.
            Lazy:   Silently skip over jobs that don't exist.

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Sander Stad (@sqlstad, sqlstad.nl)
            Tags: Agent, Job

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Remove-DbaAgentJob

        .EXAMPLE
            Remove-DbaAgentJob -SqlInstance sql1 -Job Job1

            Removes the job from the instance with the name Job1

        .EXAMPLE
            Remove-DbaAgentJob -SqlInstance sql1 -Job Job1 -KeepHistory

            Removes the job but keeps the history

        .EXAMPLE
            Remove-DbaAgentJob -SqlInstance sql1 -Job Job1 -KeepUnusedSchedule

            Removes the job but keeps the unused schedules

        .EXAMPLE
            Remove-DbaAgentJob -SqlInstance sql1, sql2, sql3 -Job Job1

            Removes the job from multiple servers

        .EXAMPLE
            sql1, sql2, sql3 | Remove-DbaAgentJob -Job Job1

            Removes the job from multiple servers using pipe line

    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param(
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Parameter(Mandatory = $false)]
        [PSCredential]$SqlCredential,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [object[]]$Job,
        [Parameter(Mandatory = $false)]
        [switch]$KeepHistory,
        [Parameter(Mandatory = $false)]
        [switch]$KeepUnusedSchedule,
        [DbaMode]$Mode = (Get-DbaConfigValue -Name 'message.mode.default' -Fallback "Strict"),
        [Parameter(Mandatory = $false)]
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            foreach ($j in $Job) {
                Write-Message -Level Verbose -Message "Processing job $j"

                if ($Server.JobServer.Jobs.Name -notcontains $j) {
                    switch ($Mode) {
                        'Lazy' {
                            Write-Message -Level Verbose -Message "Job $j doesn't exists on $instance." -Target $instance
                        }
                        'Strict' {
                            Stop-Function -Message "Job $j doesn't exist on $instance." -Continue -ContinueLabel main -Target $instance -Category InvalidData
                        }
                    }
                }
                else {
                    if ($PSCmdlet.ShouldProcess($instance, "Removing the job $j")) {
                        try {
                            $currentJob = $Server.JobServer.Jobs[$j]
                            $dropHistory = 1
                            $dropSchedule = 1
                            if (Test-Bound -ParameterName KeepHistory) {
                                Write-Message -Level SomewhatVerbose -Message "Job history will be kept"
                                $dropHistory = 0
                            }
                            if (Test-Bound -ParameterName KeepUnusedSchedule) {
                                Write-Message -Level SomewhatVerbose -Message "Unused job schedules will be kept"
                                $dropSchedule = 0
                            }
                            Write-Message -Level SomewhatVerbose -Message "Removing job"
                            $dropJobQuery = ("EXEC dbo.sp_delete_job @job_name = '{0}', @delete_history = {1}, @delete_unused_schedule = {2}" -f $currentJob.Name, $dropHistory, $dropSchedule)
                            $server.Databases['msdb'].ExecuteNonQuery($dropJobQuery)
                        }
                        catch {
                            Stop-Function -Message  "Something went wrong removing the job" -Target $instance -ErrorRecord $_ -Continue
                        }
                    }
                }
            }
        }
    }
    end {
        Write-Message -Message "Finished removing jobs(s)." -Level Verbose
    }
}
function Remove-DbaAgentJobCategory {

    <#
.SYNOPSIS
Remove-DbaAgentJobCategory removes a job category.

.DESCRIPTION
Remove-DbaAgentJobCategory makes it possible to remove a job category.
Be assured that the category you want to remove is not used with other jobs. If another job uses this category it will be get the category [Uncategorized (Local)].

.PARAMETER SqlInstance
SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Category
The name of the category

.PARAMETER Force
The force parameter will ignore some errors in the parameters and assume defaults.

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Author: Sander Stad (@sqlstad, sqlstad.nl)
Tags: Agent, Job, JobCategory

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Remove-DbaAgentJobCategory

.EXAMPLE
Remove-DbaAgentJobCategory -SqlInstance sql1 -Category 'Category 1'

Remove the job category Category 1 from the instance.

.EXAMPLE
Remove-DbaAgentJobCategory -SqlInstance sql1 -Category Category1, Category2, Category3

Remove multiple job categories from the instance.

.EXAMPLE
Remove-DbaAgentJobCategory -SqlInstance sql1, sql2, sql3 -Category Category1, Category2, Category3

Remove multiple job categories from the multiple instances.

#>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Parameter(Mandatory = $false)]
        [ValidateNotNullOrEmpty()]
        [string[]]$Category,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {

        foreach ($instance in $sqlinstance) {
            # Try connecting to the instance
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            # Loop through each of the categories
            foreach ($cat in $Category) {

                # Check if the job category exists
                if ($cat -notin $server.JobServer.JobCategories.Name) {
                    Stop-Function -Message "Job category $cat doesn't exist on $instance" -Target $instance -Continue
                }

                # Remove the category
                if ($PSCmdlet.ShouldProcess($instance, "Changing the job category $Category")) {
                    try {
                        # Get the category
                        $currentCategory = $server.JobServer.JobCategories[$cat]

                        Write-Message -Message "Removing job category $cat" -Level Verbose

                        $currentCategory.Drop()
                    }
                    catch {
                        Stop-Function -Message "Something went wrong removing the job category $cat on $instance" -Target $cat -Continue -ErrorRecord $_
                    }

                } #if should process

            } # for each category

        } # for each instance

    } # end process

    end {
        if (Test-FunctionInterrupt) { return }
        Write-Message -Message "Finished removing job category." -Level Verbose
    }

}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Remove-DbaAgentJobStep {
    <#
        .SYNOPSIS
            Removes a step from the specified SQL Agent job.

        .DESCRIPTION
            Removes a job step from a SQL Server Agent job.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Job
            The name of the job.

        .PARAMETER StepName
            The name of the job step.

        .PARAMETER Mode
            Default: Strict
            How strict does the command take lesser issues?
            Strict: Interrupt if the configuration already has the same value as the one specified.
            Lazy:   Silently skip over instances that already have this configuration at the specified value.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Sander Stad (@sqlstad, sqlstad.nl)
            Tags: Agent, Job, JobStep

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Remove-DbaAgentJobStep

        .EXAMPLE
            Remove-DbaAgentJobStep -SqlInstance sql1 -Job Job1 -StepName Step1

            Remove 'Step1' from job 'Job1' on sql1.

        .EXAMPLE
            Remove-DbaAgentJobStep -SqlInstance sql1 -Job Job1, Job2, Job3 -StepName Step1

            Remove the job step from multiple jobs.

        .EXAMPLE
            Remove-DbaAgentJobStep -SqlInstance sql1, sql2, sql3 -Job Job1 -StepName Step1

            Remove the job step from the job on multiple servers.

        .EXAMPLE
            sql1, sql2, sql3 | Remove-DbaAgentJobStep -Job Job1 -StepName Step1

            Remove the job step from the job on multiple servers using pipeline.
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Parameter(Mandatory = $false)]
        [PSCredential]$SqlCredential,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [object[]]$Job,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$StepName,
        [DbaMode]$Mode = (Get-DbaConfigValue -Name 'message.mode.default' -Fallback "Strict"),
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            foreach ($j in $Job) {
                Write-Message -Level Verbose -Message "Processing job $j"
                # Check if the job exists
                if ($Server.JobServer.Jobs.Name -notcontains $j) {
                    switch ($Mode) {
                        'Lazy' {
                            Write-Message -Level Verbose -Message "Job $j doesn't exists on $instance." -Target $instance
                        }
                        'Strict' {
                            Stop-Function -Message "Job $j doesnn't exist on $instance." -Continue -ContinueLabel main -Target $instance -Category InvalidData
                        }
                    }
                }
                else {
                    # Check if the job step exists
                    if ($Server.JobServer.Jobs[$j].JobSteps.Name -notcontains $StepName) {
                        switch ($Mode) {
                            'Lazy' {
                                Write-Message -Level Verbose -Message "Step $StepName doesn't exist for $job on $instance." -Target $instance
                            }
                            'Strict' {
                                Stop-Function -Message "Step $StepName doesn't exist for $job on $instance." -Continue -ContinueLabel main -Target $instance -Category InvalidData
                            }
                        }
                    }
                    else {
                        # Execute
                        if ($PSCmdlet.ShouldProcess($instance, "Removing the job step $StepName for job $j")) {
                            try {
                                $JobStep = $Server.JobServer.Jobs[$j].JobSteps[$StepName]
                                Write-Message -Level SomewhatVerbose -Message "Removing the job step $StepName for job $j."
                                $JobStep.Drop()
                            }
                            catch {
                                Stop-Function -Message "Something went wrong removing the job step" -Target $JobStep -Continue -ErrorRecord $_
                                Write-Message -Level Verbose -Message "Could not remove the job step $StepName from $j"
                            }
                        }
                    }
                }
            }
        }
    }
    end {
        Write-Message -Message "Finished removing the jobs step(s)" -Level Verbose
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Remove-DbaAgentSchedule {
    <#
.SYNOPSIS
Remove-DbaAgentJobSchedule removes a job schedule.

.DESCRIPTION
Remove-DbaAgentJobSchedule removes a a job in the SQL Server Agent.

.PARAMETER SqlInstance
SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Schedule
The name of the job schedule.

.PARAMETER InputObject
A collection of schedule (such as returned by Get-DbaAgentSchedule), to be removed.

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.PARAMETER Force
The force parameter will ignore some errors in the parameters and assume defaults.
It will also remove the any present schedules with the same name for the specific job.

.NOTES
Author: Sander Stad (@sqlstad, sqlstad.nl)
Tags: Agent, Job, Schedule

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Remove-DbaAgentJobSchedule

.EXAMPLE
Remove-DbaAgentSchedule -SqlInstance sql1 -Schedule weekly
Remove the schedule weekly

.EXAMPLE
Remove-DbaAgentSchedule -SqlInstance sql1 -Schedule weekly -Force
Remove the schedule weekly from the job even if the schedule is being used by another job.

.EXAMPLE
Remove-DbaAgentSchedule -SqlInstance sql1 -Schedule daily, weekly
Remove multiple schedule

.EXAMPLE
Remove-DbaAgentSchedule -SqlInstance sql1, sql2, sql3 -Schedule daily, weekly
Remove the schedule on multiple servers for multiple schedules

.EXAMPLE
sql1, sql2, sql3 | Remove-DbaAgentSchedule -Schedule daily, weekly
Remove the schedule on multiple servers using pipe line

.EXAMPLE
Get-DbaAgentSchedule -SqlInstance sql1 -Schedule sched1, sched2, sched3 | Remove-DbaAgentSchedule

Remove the schedules using a pipeline

#>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]

    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "instance")]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [System.Management.Automation.PSCredential]
        $SqlCredential,
        [Parameter(Mandatory = $true, ParameterSetName = "instance")]
        [ValidateNotNullOrEmpty()]
        [Alias("Schedules")]
        [object[]]$Schedule,
        [Parameter(ValueFromPipeline, Mandatory, ParameterSetName = "schedules")]
        [Microsoft.SqlServer.Management.Smo.Agent.ScheduleBase[]]$InputObject,
        [Alias('Silent')]
        [switch]$EnableException,
        [switch]$Force
    )

    process {

        foreach ($instance in $sqlinstance) {
            # Try connecting to the instance
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $InputObject += $server.JobServer.SharedSchedules | Where-Object { $_.Name -in $Schedule }

        } # foreach object instance

        foreach ($s in $InputObject) {

            if ($Server.JobServer.SharedSchedules.Name -contains $s.Name) {
                # Get job count
                $jobCount = $Server.JobServer.SharedSchedules[$s].JobCount

                # Check if the schedule is shared among other jobs
                if ($jobCount -ge 1 -and -not $Force) {
                    Stop-Function -Message "The schedule $s is shared connected to one or more jobs. If removal is neccesary use -Force." -Target $instance -Continue
                }

                # Remove the job schedule
                if ($PSCmdlet.ShouldProcess($instance, "Removing schedule $s on $instance")) {
                    # Loop through each of the schedules and drop them
                    Write-Message -Message "Removing schedule $s on $instance" -Level Verbose

                    #Check if jobs use the schedule
                    if ($jobCount -ge 1) {
                        # Get the job object
                        $smoSchedules = $server.JobServer.SharedSchedules | Where-Object {($_.Name -eq $s.Name)}

                        Write-Message -Message "Schedule $sched is used in one or more jobs. Removing it for each job." -Level Verbose

                        # Loop through each if the schedules
                        foreach ($smoSchedule in $smoSchedules) {

                            # Get the job ids
                            $jobGuids = $Server.JobServer.SharedSchedules[$smoSchedule].EnumJobReferences()

                            if (($jobCount -gt 1 -and $Force) -or $jobCount -eq 1) {

                                # Loop though each of the jobs
                                foreach ($guid in $jobGuids) {
                                    # Get the job object
                                    $smoJob = $Server.JobServer.GetJobByID($guid)

                                    # Get the job schedule
                                    $jobSchedules = $Server.JobServer.Jobs[$smoJob].JobSchedules | Where-Object {$_.Name -eq $smoSchedule}

                                    foreach ($jobSchedule in $jobSchedules) {
                                        try {
                                            Write-Message -Message "Removing the schedule $jobSchedule for job $smoJob" -Level Verbose

                                            $jobSchedule.Drop()
                                        }
                                        catch {
                                            Stop-Function -Message  "Something went wrong removing the job schedule" -Target $instance -ErrorRecord $_ -Continue
                                        }
                                    }
                                } # foreach guid
                            } # if jobcount

                        } # foreach smoschedule
                    } # if jobcount ge 1

                    Write-Message -Message "Removing schedules that are not being used by other jobs." -Level Verbose

                    # Get the schedules
                    $smoSchedules = $server.JobServer.SharedSchedules | Where-Object {($_.Name -eq $s.Name) -and ($_.JobCount -eq 0)}

                    # Remove the schedules that have no jobs
                    foreach ($smoSchedule in $smoSchedules) {
                        try {
                            $smoSchedule.Drop()
                        }
                        catch {
                            Stop-Function -Message  "Something went wrong removing the schedule" -Target $instance -ErrorRecord $_ -Continue
                        }
                    } # foreach schedule
                } # should process
            } # if contains schedule
            else {
                Stop-Function -Message "Schedule $s is not present on instance $instance" -Target $instance -Continue
            }
        } #foreach object schedule

    } # process

    end {
        Write-Message -Message "Finished removing jobs schedule(s)." -Level Verbose
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Remove-DbaBackup {
    <#
        .SYNOPSIS
            Removes SQL Server backups from disk.

        .DESCRIPTION
            Provides all of the same functionality for removing SQL backups from disk as a standard maintenance plan would.

            As an addition you have the ability to check the Archive bit on files before deletion. This will allow you to ensure backups have been archived to your archive location before removal.

            Also included is the ability to remove empty folders as part of this cleanup activity.

        .PARAMETER Path
            Specifies the name of the base level folder to search for backup files. Deletion of backup files will be recursive from this location.

        .PARAMETER BackupFileExtension
            Specifies the filename extension of the backup files you wish to remove (typically 'bak', 'trn' or 'log'). Do not include the period.

        .PARAMETER RetentionPeriod
            Specifies the retention period for backup files. Correct format is ##U.

            ## is the retention value and must be an integer value
            U signifies the units where the valid units are:
            h = hours
            d = days
            w = weeks
            m = months

            Formatting Examples:
            '48h' = 48 hours
            '7d' = 7 days
            '4w' = 4 weeks
            '1m' = 1 month

        .PARAMETER CheckArchiveBit
            If this switch is enabled, the filesystem Archive bit is checked before deletion. If this bit is set (which translates to "it has not been backed up to another location yet", the file won't be deleted.

        .PARAMETER RemoveEmptyBackupFolder
            If this switch is enabled, empty folders will be removed after the cleanup process is complete.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

       .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.i

        .NOTES
            Tags: Storage, DisasterRecovery, Backup
            Author: Chris Sommer, @cjsommer, www.cjsommer.com

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Remove-DbaBackup

        .EXAMPLE
            Remove-DbaBackup -Path 'C:\MSSQL\SQL Backup\' -BackupFileExtension trn -RetentionPeriod 48h

            '*.trn' files in 'C:\MSSQL\SQL Backup\' and all subdirectories that are more than 48 hours old will be removed.

        .EXAMPLE
            Remove-DbaBackup -Path 'C:\MSSQL\SQL Backup\' -BackupFileExtension trn -RetentionPeriod 48h -WhatIf

            Same as example #1, but doesn't actually remove any files. The function will instead show you what would be done.
            This is useful when first experimenting with using the function.

        .EXAMPLE
            Remove-DbaBackup -Path 'C:\MSSQL\Backup\' -BackupFileExtension bak -RetentionPeriod 7d -CheckArchiveBit

            '*.bak' files in 'C:\MSSQL\Backup\' and all subdirectories that are more than 7 days old will be removed, but only if the files have been backed up to another location as verified by checking the Archive bit.

        .EXAMPLE
            Remove-DbaBackup -Path 'C:\MSSQL\Backup\' -BackupFileExtension bak -RetentionPeriod 1w -RemoveEmptyBackupFolder

            '*.bak' files in 'C:\MSSQL\Backup\' and all subdirectories that are more than 1 week old will be removed. Any folders left empty will be removed as well.
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true, HelpMessage = "Full path to the root level backup folder (ex. 'C:\SQL\Backups'")]
        [Alias("BackupFolder")]
        [string]$Path,
        [parameter(Mandatory = $true, HelpMessage = "Backup File extension to remove (ex. bak, trn, dif)")]
        [string]$BackupFileExtension ,
        [parameter(Mandatory = $true, HelpMessage = "Backup retention period. (ex. 24h, 7d, 4w, 6m)")]
        [string]$RetentionPeriod ,
        [parameter(Mandatory = $false)]
        [switch]$CheckArchiveBit = $false ,
        [parameter(Mandatory = $false)]
        [switch]$RemoveEmptyBackupFolder = $false,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        # Ensure BackupFileExtension does not begin with a .
        if ($BackupFileExtension -match "^[.]") {
            Write-Message -Level Warning -Message "Parameter -BackupFileExtension begins with a period '$BackupFileExtension'. A period is automatically prepended to -BackupFileExtension and need not be passed in."
        }
    }
    process {
        # Process stuff
        Write-Message -Message "Removing backups from $Path" -Level Verbose
        Find-DbaBackup -Path $Path -BackupFileExtension $BackupFileExtension -RetentionPeriod $RetentionPeriod -CheckArchiveBit:$CheckArchiveBit -EnableException |
            Foreach-Object {
            $file = $_
            if ($PSCmdlet.ShouldProcess($file.Directory.FullName, "Removing backup file $($file.Name)")) {
                try {
                    $file | Remove-Item -Force -EA Stop
                }
                catch {
                    Write-Message -Message "Failed to remove $file." -Level Warning -ErrorRecord $_
                }
            }
        }
        Write-Message -Message "File Cleaning ended." -Level Verbose
        # Cleanup empty backup folders.
        if ($RemoveEmptyBackupFolder) {
            Write-Message -Message "Removing empty folders." -Level Verbose
            (Get-ChildItem -Directory -Path $Path -Recurse -ErrorAction SilentlyContinue -ErrorVariable EnumErrors).FullName |
                Sort-Object -Descending |
                Foreach-Object {
                $OrigPath = $_
                try {
                    $Contents = @(Get-ChildItem -Force $OrigPath -ErrorAction Stop)
                }
                catch {
                    Write-Message -Message "Can't enumerate $OrigPath." -Level Warning -ErrorRecord $_
                }
                if ($Contents.Count -eq 0) {
                    return $_
                }
            } |
                Foreach-Object {
                $FolderPath = $_
                if ($PSCmdlet.ShouldProcess($Path, "Removing empty folder .$($FolderPath.Replace($Path, ''))")) {
                    try {
                        $FolderPath | Remove-Item -ErrorAction Stop
                    }
                    catch {
                        Write-Message -Message "Failed to remove $FolderPath." -Level Warning -ErrorRecord $_
                    }
                }
            }
            if ($EnumErrors) {
                Write-Message "Errors encountered enumerating folders." -Level Warning -ErrorRecord $EnumErrors
            }
            Write-Message -Message "Removed empty folders." -Level Verbose
        }
    }
}
function Remove-DbaClientAlias {
    <#
    .SYNOPSIS
    Removes a sql alias for the specified server - mimics cliconfg.exe

    .DESCRIPTION
    Removes a SQL Server alias by altering HKLM:\SOFTWARE\Microsoft\MSSQLServer\Client

    .PARAMETER ComputerName
    The target computer where the alias will be created

    .PARAMETER Credential
    Allows you to login to remote computers using alternative credentials

    .PARAMETER Alias
    The alias to be deleted

    .PARAMETER EnableException
    By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
    Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
    Tags: Alias

    Website: https://dbatools.io
    Copyright: (C) Chrissy LeMaire, [email protected]
    License: MIT https://opensource.org/licenses/MIT

    .LINK
    https://dbatools.io/Remove-DbaClientAlias

    .EXAMPLE
    Remove-DbaClientAlias -ComputerName workstationx -Alias sqlps
    Removes the sqlps SQL client alias on workstationx

    .EXAMPLE
    Get-DbaClientAlias | Remove-DbaClientAlias
    Removes all SQL Server client aliases on the local computer

#>
    [CmdletBinding()]
    Param (
        [parameter(ValueFromPipelineByPropertyName)]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [Alias('AliasName')]
        [string]$Alias,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {

        foreach ($computer in $ComputerName) {
            $null = Test-ElevationRequirement -ComputerName $computer -Continue

            $scriptblock = {
                $Alias = $args[0]
                function Get-ItemPropertyValue {
                    Param (
                        [parameter()]
                        [String]$Path,
                        [parameter()]
                        [String]$Name
                    )
                    Get-ItemProperty -LiteralPath $Path -Name $Name
                }

                $basekeys = "HKLM:\SOFTWARE\WOW6432Node\Microsoft\MSSQLServer", "HKLM:\SOFTWARE\Microsoft\MSSQLServer"

                foreach ($basekey in $basekeys) {

                    if ((Test-Path $basekey) -eq $false) {
                        Write-Warning "Base key ($basekey) does not exist. Quitting."
                        continue
                    }

                    $client = "$basekey\Client"

                    if ((Test-Path $client) -eq $false) {
                        continue
                    }

                    $connect = "$client\ConnectTo"

                    if ((Test-Path $connect) -eq $false) {
                        continue
                    }

                    if ($basekey -like "*WOW64*") {
                        $architecture = "32-bit"
                    }
                    else {
                        $architecture = "64-bit"
                    }


                    $all = Get-Item -Path $connect
                    foreach ($entry in $all) {

                        foreach ($en in $entry) {
                            $e = $entry.ToString().Replace('HKEY_LOCAL_MACHINE', 'HKLM:\')
                            if ($en.Property -contains $Alias) {
                                Remove-ItemProperty -Path $e -Name $Alias
                            }
                            else {
                                $en
                            }
                        }
                    }
                }
            }

            if ($PScmdlet.ShouldProcess($computer, "Getting aliases")) {
                try {
                    $null = Invoke-Command2 -ComputerName $computer -Credential $Credential -ScriptBlock $scriptblock -ErrorAction Stop -Verbose:$false -ArgumentList $Alias
                    Get-DbaClientAlias -ComputerName $computer

                }
                catch {
                    Stop-Function -Message "Failure" -ErrorRecord $_ -Target $computer -Continue
                }
            }
        }
    }
}
function Remove-DbaCmConnection {
    <#
        .SYNOPSIS
            Removes connection objects from the connection cache used for remote computer management.

        .DESCRIPTION
            Removes connection objects from the connection cache used for remote computer management.

        .PARAMETER ComputerName
            The computer whose connection to remove.
            Accepts both text as well as the output of Get-DbaCmConnection.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ComputerManagement, CIM
            Author: Fred Winmann (@FredWeinmann)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Remove-DbaCmConnection

        .EXAMPLE
            Remove-DbaCmConnection -ComputerName sql2014

            Removes the cached connection to the server sql2014 from the cache.

        .EXAMPLE
            Get-DbaCmConnection | Remove-DbaCmConnection

            Clears the entire connection cache.
    #>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true, Mandatory = $true)]
        [Sqlcollaborative.Dbatools.Parameter.DbaCmConnectionParameter[]]
        $ComputerName,

        [switch]
        [Alias('Silent')]$EnableException
    )

    BEGIN {
        Write-Message -Level InternalComment -Message "Starting"
        Write-Message -Level Verbose -Message "Bound parameters: $($PSBoundParameters.Keys -join ", ")"
    }
    PROCESS {
        foreach ($connectionObject in $ComputerName) {
            if (-not $connectionObject.Success) { Stop-Function -Message "Failed to interpret computername input: $($connectionObject.InputObject)" -Category InvalidArgument -Target $connectionObject.InputObject -Continue }
            Write-Message -Level VeryVerbose -Message "Removing from connection cache: $($connectionObject.Connection.ComputerName)" -Target $connectionObject.Connection.ComputerName
            if ([Sqlcollaborative.Dbatools.Connection.ConnectionHost]::Connections.ContainsKey($connectionObject.Connection.ComputerName)) {
                $null = [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::Connections.Remove($connectionObject.Connection.ComputerName)
                Write-Message -Level Verbose -Message "Successfully removed $($connectionObject.Connection.ComputerName)" -Target $connectionObject.Connection.ComputerName
            }
            else {
                Write-Message -Level Verbose -Message "Not found: $($connectionObject.Connection.ComputerName)" -Target $connectionObject.Connection.ComputerName
            }
        }
    }
    END {
        Write-Message -Level InternalComment -Message "Ending"
    }
}
function Remove-DbaComputerCertificate {
    <#
    .SYNOPSIS
        Removes a computer certificate - useful for removing easily certs from remote computers

    .DESCRIPTION
        Removes a computer certificate from a local or remote compuer

    .PARAMETER ComputerName
        The target computer - defaults to localhost

    .PARAMETER Credential
        Allows you to login to $ComputerName using alternative credentials

    .PARAMETER Thumbprint
        The thumbprint of the certificate object

    .PARAMETER Store
        Certificate store - defaults to LocalMachine (otherwise exceptions can be thrown on remote connections)

    .PARAMETER Folder
        Certificate folder

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .PARAMETER WhatIf
        Shows what would happen if the command were to run. No actions are actually performed.

    .PARAMETER Confirm
        Prompts you for confirmation before executing any changing operations within the command.

    .EXAMPLE
        Remove-DbaComputerCertificate -ComputerName Server1 -Thumbprint C2BBE81A94FEE7A26FFF86C2DFDAF6BFD28C6C94

        Removes certificate with thumbprint C2BBE81A94FEE7A26FFF86C2DFDAF6BFD28C6C94 in the LocalMachine store on Server1

    .EXAMPLE
        Get-DbaComputerCertificate | Where-Object Thumbprint -eq E0A071E387396723C45E92D42B2D497C6A182340 | Remove-DbaComputerCertificate

        Removes certificate using the pipeline

    .EXAMPLE
        Remove-DbaComputerCertificate -ComputerName Server1 -Thumbprint C2BBE81A94FEE7A26FFF86C2DFDAF6BFD28C6C94 -Store User -Folder My

        Removes certificate with thumbprint C2BBE81A94FEE7A26FFF86C2DFDAF6BFD28C6C94 in the User\My (Personal) store on Server1

    .NOTES
        Tags: Certificate

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT
#>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High")]
    param (
        [Alias("ServerInstance", "SqlServer", "SqlInstance")]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [parameter(ValueFromPipelineByPropertyName, Mandatory)]
        [string[]]$Thumbprint,
        [string]$Store = "LocalMachine",
        [string]$Folder = "My",
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        #region Scriptblock for remoting
        $scriptblock = {
            param (
                $Thumbprint,

                $Store,

                $Folder
            )
            Write-Verbose "Searching Cert:\$Store\$Folder for thumbprint: $thumbprint"
            $cert = Get-ChildItem "Cert:\$store\$folder" -Recurse | Where-Object { $_.Thumbprint -eq $Thumbprint }

            if ($cert) {
                $null = $cert | Remove-Item
                $status = "Removed"
            }
            else {
                $status = "Certificate not found in Cert:\$Store\$Folder"
            }

            [pscustomobject]@{
                ComputerName = $env:COMPUTERNAME
                Store        = $Store
                Folder       = $Folder
                Thumbprint   = $thumbprint
                Status       = $status
            }
        }
        #endregion Scriptblock for remoting
    }

    process {
        foreach ($computer in $computername) {
            foreach ($thumb in $Thumbprint) {
                if ($PScmdlet.ShouldProcess("local", "Connecting to $computer to remove cert from Cert:\$Store\$Folder")) {
                    try {
                        Invoke-Command2 -ComputerName $computer -Credential $Credential -ArgumentList $thumb, $Store, $Folder -ScriptBlock $scriptblock -ErrorAction Stop
                    }
                    catch {
                        Stop-Function -Message $_ -ErrorRecord $_ -Target $computer -Continue
                    }
                }
            }
        }
    }
}
function Remove-DbaDatabase {
    <#
.SYNOPSIS
Drops a database, hopefully even the really stuck ones.

.DESCRIPTION
Tries a bunch of different ways to remove a database or two or more.

.PARAMETER SqlInstance
The SQL Server instance holding the databases to be removed.You must have sysadmin access and server version must be SQL Server version 2000 or higher.

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Database
The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

.PARAMETER InputObject
A collection of databases (such as returned by Get-DbaDatabase), to be removed.

.PARAMETER IncludeSystemDb
Use this switch to disable any kind of verbose messages

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Delete, Databases

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Remove-DbaDatabase

.EXAMPLE
Remove-DbaDatabase -SqlInstance sql2016 -Database containeddb

Prompts then removes the database containeddb on SQL Server sql2016

.EXAMPLE
Remove-DbaDatabase -SqlInstance sql2016 -Database containeddb, mydb

Prompts then removes the databases containeddb and mydb on SQL Server sql2016

.EXAMPLE
Remove-DbaDatabase -SqlInstance sql2016 -Database containeddb -Confirm:$false

Does not prompt and swiftly removes containeddb on SQL Server sql2016

.EXAMPLE
Get-DbaDatabase -SqlInstance server\instance -ExcludeAllSystemDb | Remove-DbaDatabase

Removes all the user databases from server\instance

.EXAMPLE
Get-DbaDatabase -SqlInstance server\instance -ExcludeAllSystemDb | Remove-DbaDatabase -Confirm:$false

Removes all the user databases from server\instance without any confirmation
#>
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High', DefaultParameterSetName = "Default")]
    Param (
        [parameter(Mandatory, ParameterSetName = "instance")]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [parameter(Mandatory = $false)]
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [parameter(Mandatory, ParameterSetName = "instance")]
        [Alias("Databases")]
        [object[]]$Database,
        [Parameter(ValueFromPipeline, Mandatory, ParameterSetName = "databases")]
        [Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
        [switch]$IncludeSystemDb,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            $InputObject += $server.Databases | Where-Object { $_.Name -in $Database }
        }

        $system_dbs = @( "master", "model", "tempdb", "resource", "msdb" )

        if (-not($IncludeSystemDb)) {
            $InputObject = $InputObject | Where-Object { $_.Name -notin $system_dbs}
        }

        foreach ($db in $InputObject) {
            try {
                $server = $db.Parent
                if ($Pscmdlet.ShouldProcess("$db on $server", "KillDatabase")) {
                    $server.KillDatabase($db.name)
                    $server.Refresh()

                    [pscustomobject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Database     = $db.name
                        Status       = "Dropped"
                    }
                }
            }
            catch {
                try {
                    if ($Pscmdlet.ShouldProcess("$db on $server", "alter db set single_user with rollback immediate then drop")) {
                        $null = $server.Query("if exists (select * from sys.databases where name = '$($db.name)' and state = 0) alter database $db set single_user with rollback immediate; drop database $db")

                        [pscustomobject]@{
                            ComputerName = $server.ComputerName
                            InstanceName = $server.ServiceName
                            SqlInstance  = $server.DomainInstanceName
                            Database     = $db.name
                            Status       = "Dropped"
                        }
                    }
                }
                catch {
                    try {
                        if ($Pscmdlet.ShouldProcess("$db on $server", "SMO drop")) {
                            $server.databases[$dbname].Drop()
                            $server.Refresh()

                            [pscustomobject]@{
                                ComputerName = $server.ComputerName
                                InstanceName = $server.ServiceName
                                SqlInstance  = $server.DomainInstanceName
                                Database     = $db.name
                                Status       = "Dropped"
                            }
                        }
                    }
                    catch {
                        Write-Message -Level Verbose -Message "Could not drop database $db on $server"

                        [pscustomobject]@{
                            ComputerName = $server.ComputerName
                            InstanceName = $server.ServiceName
                            SqlInstance  = $server.DomainInstanceName
                            Database     = $db.name
                            Status       = (Get-ErrorMessage -Record $_)
                        }
                    }
                }
            }
        }
    }
}
function Remove-DbaDatabaseMasterKey {
    <#
    .SYNOPSIS
        Deletes specified database master key

    .DESCRIPTION
        Deletes specified database master key.

    .PARAMETER SqlInstance
        The target SQL Server instance.

    .PARAMETER SqlCredential
        Allows you to login to SQL Server using alternative credentials.

    .PARAMETER Database
        The database where the master key will be removed.

    .PARAMETER ExcludeDatabase
        List of databases to exclude from clearing all master keys

    .PARAMETER All
        Purge the master keys from all databases on an instance.

    .PARAMETER MasterKeyCollection
        Internal parameter to support pipeline input

    .PARAMETER Mode
        Controls how the function handles cases where it can't do anything due to missing database or key:
        Strict: Write a warning (default)
        Lazy:   Write a verbose message
        Report: Create a report object as part of the output
        The default action can be adjusted by using Set-DbaConfig to change the 'message.mode.default' configuration

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .PARAMETER WhatIf
        Shows what would happen if the command were to run. No actions are actually performed.

    .PARAMETER Confirm
        Prompts you for confirmation before executing any changing operations within the command.

    .EXAMPLE
        Remove-DbaDatabaseMasterKey -SqlInstance Server1

        The master key in the master database on server1 will be removed if it exists.

    .EXAMPLE
        Remove-DbaDatabaseMasterKey -SqlInstance Server1 -Database db1 -Confirm:$false

        Suppresses all prompts to remove the master key in the 'db1' database and drops the key.


    .NOTES
        Tags: Certificate

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT
#>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true, ConfirmImpact = "High")]
    param (
        [parameter(Mandatory, ParameterSetName = "instanceExplicit")]
        [parameter(Mandatory, ParameterSetName = "instanceAll")]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]
        $SqlInstance,

        [System.Management.Automation.PSCredential]
        $SqlCredential,

        [parameter(Mandatory, ParameterSetName = "instanceExplicit")]
        [object[]]
        $Database,

        [parameter(ParameterSetName = "instanceAll")]
        [object[]]
        $ExcludeDatabase,

        [parameter(Mandatory, ParameterSetName = "instanceAll")]
        [switch]
        $All,

        [parameter(ValueFromPipeline, ParameterSetName = "collection")]
        [Microsoft.SqlServer.Management.Smo.MasterKey[]]
        $MasterKeyCollection,

        [DbaMode]
        $Mode = (Get-DbaConfigValue -Name 'message.mode.default' -Fallback "Strict"),

        [switch]
        [Alias('Silent')]$EnableException
    )

    begin {
        function Drop-Masterkey {
            [CmdletBinding()]
            Param (
                $masterkey,

                $mode = $Mode,

                $EnableException = $EnableException
            )
            $server = $masterkey.Parent.Parent
            $instance = $server.DomainInstanceName
            $cert = $masterkey.Name
            $db = $masterkey.Parent

            if ($Pscmdlet.ShouldProcess($instance, "Dropping the master key for database '$db'")) {
                try {
                    $masterkey.Drop()
                    Write-Message -Level Verbose -Message "Successfully removed master key from the $db database on $instance"

                    [pscustomobject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Database     = $db.Name
                        Status       = "Success"
                    }
                }
                catch {
                    [pscustomobject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Database     = $db.Name
                        Status       = "Failure"
                    }
                    Stop-Function -Message "Failed to drop master key from $db on $instance." -Target $db -InnerErrorRecord $_ -Continue
                }
            }
        }
    }
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($All) {
                $Database = ($server.Databases | Where-Object Name -NotIn $ExcludeDatabase).Name
            }

            :Database foreach ($db in $Database) {
                $smodb = $server.Databases[$db]
                $masterkey = $smodb.MasterKey

                #region Case: Database Unknown
                if ($null -eq $smodb) {
                    switch ($Mode) {
                        [DbaMode]::Strict { Stop-Function -Message "Database '$db' does not exist on $instance" -Target $smodb -Continue -ContinueLabel database }
                        [DbaMode]::Lazy {
                            Write-Message -Level (Get-DbaConfigValue -Name 'message.mode.lazymessagelevel' -Fallback 4) -Message "Database '$db' does not exist on $instance" -Target $smodb
                            continue database
                        }
                        [DbaMode]::Report {
                            [pscustomobject]@{
                                ComputerName = $server.ComputerName
                                InstanceName = $server.ServiceName
                                SqlInstance  = $server.DomainInstanceName
                                Database     = $db
                                Status       = "Unknown Database"
                            }
                            continue Database
                        }
                    }
                }
                #endregion Case: Database Unknown

                #region Case: No Master Key
                if ($null -eq $masterkey) {
                    switch ($Mode.ToString()) {
                        "Strict" { Stop-Function -Message "No master key exists in the $db database on $instance" -Target $smodb -Continue -ContinueLabel database }
                        "Lazy" {
                            Write-Message -Level (Get-DbaConfigValue -Name 'message.mode.lazymessagelevel' -Fallback 4) -Message "No master key exists in the $db database on $instance" -Target $smodb
                            continue database
                        }
                        "Report" {
                            [pscustomobject]@{
                                ComputerName = $server.ComputerName
                                InstanceName = $server.ServiceName
                                SqlInstance  = $server.DomainInstanceName
                                Database     = $smodb.Name
                                Status       = "No Masterkey"
                            }
                            continue Database
                        }
                    }
                }
                #endregion Case: No Master Key

                Write-Message -Level Verbose -Message "Removing master key from $db"
                Drop-Masterkey -masterkey $masterkey
            }
        }

        foreach ($key in $MasterKeyCollection) {
            Write-Message -Level Verbose -Message "Removing master key: $key"
            Drop-Masterkey -masterkey $key
        }
    }
}
function Remove-DbaDatabaseSafely {
    <#
        .SYNOPSIS
            Safely removes a SQL Database and creates an Agent Job to restore it.

        .DESCRIPTION
            Performs a DBCC CHECKDB on the database, backs up the database with Checksum and verify only to a final (golden) backup location, creates an Agent Job to restore from that backup, drops the database, runs the agent job to restore the database, performs a DBCC CHECKDB and drops the database.

            With huge thanks to Grant Fritchey and his verify your backups video. Take a look, it's only 3 minutes long. http://sqlps.io/backuprant

        .PARAMETER SqlInstance
            The SQL Server instance holding the databases to be removed. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            If specified, Agent jobs will be created on this server. By default, the jobs will be created on the server specified by SqlInstance. You must have sysadmin access and the server must be SQL Server 2000 or higher. The SQL Agent service will be started if it is not already running.

        .PARAMETER DestinationCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies one or more databases to remove.

        .PARAMETER NoDbccCheckDb
            If this switch is enabled, the initial DBCC CHECK DB will be skipped. This will make the process quicker but will also allow you to create an Agent job that restores a database backup containing a corrupt database.

            A second DBCC CHECKDB is performed on the restored database so you will still be notified BUT USE THIS WITH CARE.

        .PARAMETER BackupFolder
            Specifies the path to a folder where the final backups of the removed databases will be stored. If you are using separate source and destination servers, you must specify a UNC path such as  \\SERVER1\BACKUPSHARE\

        .PARAMETER JobOwner
            Specifies the name of the account which will own the Agent jobs. By default, sa is used.

        .PARAMETER UseDefaultFilePaths
            If this switch is enabled, the default file paths for the data and log files on the instance where the database is restored will be used. By default, the original file paths will be used.

        .PARAMETER CategoryName
            Specifies the Category Name for the Agent job that is created for restoring the database(s). By default, the name is "Rationalisation".

        .PARAMETER BackupCompression
            If this switch is enabled, compression will be used for the backup regardless of the SQL Server instance setting. By default, the SQL Server instance setting for backup compression is used.

        .PARAMETER AllDatabases
            If this switch is enabled, all user databases on the server will be removed. This is useful when decommissioning a server. You should use a DestinationServer with this switch.

        .PARAMETER ReuseSourceFolderStructure
            If this switch is enabled, the source folder structure will be used when restoring instead of using the destination instance default folder structure.

        .PARAMETER Force
            If this switch is enabled, all actions will be performed even if DBCC errors are detected. An Agent job will be created with 'DBCCERROR' in the name and the backup file will have 'DBCC' in its name.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database, Remove
            Author: Rob Sewell @SQLDBAWithBeard, sqldbawithabeard.com

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Remove-DbaDatabaseSafely

        .EXAMPLE
            Remove-DbaDatabaseSafely -SqlInstance 'Fade2Black' -Database RideTheLightning -BackupFolder 'C:\MSSQL\Backup\Rationalised - DO NOT DELETE'

            Performs a DBCC CHECKDB on database RideTheLightning on server Fade2Black. If there are no errors, the database is backup to the folder C:\MSSQL\Backup\Rationalised - DO NOT DELETE. Then, an Agent job to restore the database from that backup is created. The database is then dropped, the Agent job to restore it run, a DBCC CHECKDB run against the restored database, and then it is dropped again.

            Any DBCC errors will be written to your documents folder

        .EXAMPLE
            $Database = 'DemoNCIndex','RemoveTestDatabase'
            Remove-DbaDatabaseSafely -SqlInstance 'Fade2Black' -Database $Database -BackupFolder 'C:\MSSQL\Backup\Rationalised - DO NOT DELETE'

            Performs a DBCC CHECKDB on two databases, 'DemoNCIndex' and 'RemoveTestDatabase' on server Fade2Black. Then, an Agent job to restore each database from those backups is created. The databases are then dropped, the Agent jobs to restore them run, a DBCC CHECKDB run against the restored databases, and then they are dropped again.

            Any DBCC errors will be written to your documents folder

        .EXAMPLE
            Remove-DbaDatabaseSafely -SqlInstance 'Fade2Black' -DestinationServer JusticeForAll -Database RideTheLightning -BackupFolder '\\BACKUPSERVER\BACKUPSHARE\MSSQL\Rationalised - DO NOT DELETE'

            Performs a DBCC CHECKDB on database RideTheLightning on server Fade2Black. If there are no errors, the database is backup to the folder \\BACKUPSERVER\BACKUPSHARE\MSSQL\Rationalised - DO NOT DELETE . Then, an Agent job is created on server JusticeForAll to restore the database from that backup is created. The database is then dropped on Fade2Black, the Agent job to restore it on JusticeForAll is run, a DBCC CHECKDB run against the restored database, and then it is dropped from JusticeForAll.

            Any DBCC errors will be written to your documents folder

        .EXAMPLE
            Remove-DbaDatabaseSafely -SqlInstance IronMaiden -Database $Database -DestinationServer TheWildHearts -BackupFolder Z:\Backups -NoDbccCheckDb -UseDefaultFilePaths -JobOwner 'THEBEARD\Rob'

            For the databases $Database on the server IronMaiden a DBCC CHECKDB will not be performed before backing up the databases to the folder Z:\Backups. Then, an Agent job is created on server TheWildHearts with a Job Owner of THEBEARD\Rob to restore each database from that backup using the instance's default file paths. The database(s) is(are) then dropped on IronMaiden, the Agent job(s) run, a DBCC CHECKDB run on the restored database(s), and then the database(s) is(are) dropped.

        .EXAMPLE
            Remove-DbaDatabaseSafely -SqlInstance IronMaiden -Database $Database -DestinationServer TheWildHearts -BackupFolder Z:\Backups -UseDefaultFilePaths -ContinueAfterDbccError

            The databases $Database on the server IronMaiden will be backed up the to the folder Z:\Backups. Then, an Agent job is created on server TheWildHearts with a Job Owner of THEBEARD\Rob to restore each database from that backup using the instance's default file paths. The database(s) is(are) then dropped on IronMaiden, the Agent job(s) run, a DBCC CHECKDB run on the restored database(s), and then the database(s) is(are) dropped.

            If there is a DBCC Error, the function  will continue to perform rest of the actions and will create an Agent job with 'DBCCERROR' in the name and a Backup file with 'DBCCError' in the name.
    #>
    [CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = "Default")]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [parameter(Mandatory = $false)]
        [DbaInstanceParameter]$Destination = $sqlinstance,
        [PSCredential]
        $DestinationCredential,
        [parameter(Mandatory = $false)]
        [Alias("NoCheck")]
        [switch]$NoDbccCheckDb,
        [parameter(Mandatory = $true)]
        [string]$BackupFolder,
        [parameter(Mandatory = $false)]
        [string]$CategoryName = 'Rationalisation',
        [parameter(Mandatory = $false)]
        [string]$JobOwner,
        [parameter(Mandatory = $false)]
        [switch]$AllDatabases,
        [ValidateSet("Default", "On", "Of")]
        [string]$BackupCompression = 'Default',
        [switch]$ReuseSourceFolderStructure,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        if (!$AllDatabases -and !$Database) {
            Stop-Function -Message "You must specify at least one database. Use -Database or -AllDatabases." -ErrorRecord $_
            return
        }

        $sourceserver = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $sqlCredential -ParameterConnection

        if (-not $destination) {
            $destination = $sqlinstance
            $DestinationCredential = $SqlCredential
        }

        if ($sqlinstance -ne $destination) {

            $destserver = Connect-SqlInstance -SqlInstance $destination -SqlCredential $DestinationCredential

            $sourcenb = $instance.ComputerName
            $destnb = $instance.ComputerName

            if ($BackupFolder.StartsWith("\\") -eq $false -and $sourcenb -ne $destnb) {
                Stop-Function -Message "Backup folder must be a network share if the source and destination servers are not the same." -ErrorRecord $_ -Target $backupFolder
                return
            }
        }
        else {
            $destserver = $sourceserver
        }

        $source = $sourceserver.DomainInstanceName
        $destination = $destserver.DomainInstanceName

        if (!$jobowner) {
            $jobowner = Get-SqlSaLogin $destserver
        }

        if ($alldatabases -or !$Database) {
            $database = ($sourceserver.databases | Where-Object { $_.IsSystemObject -eq $false -and ($_.Status -match 'Offline') -eq $false }).Name
        }

        if (!(Test-DbaSqlPath -SqlInstance $destserver -Path $backupFolder)) {
            $serviceaccount = $destserver.ServiceAccount
            Stop-Function -Message "Can't access $backupFolder Please check if $serviceaccount has permissions." -ErrorRecord $_ -Target $backupFolder
        }

        $jobname = "Rationalised Final Database Restore for $dbname"
        $jobStepName = "Restore the $dbname database from Final Backup"

        if (!($destserver.Logins | Where-Object { $_.Name -eq $jobowner })) {
            Stop-Function -Message "$destination does not contain the login $jobowner - Please fix and try again - Aborting." -ErrorRecord $_ -Target $jobowner
        }

        function Start-SqlAgent {
            <#
                .SYNOPSIS
            #>
            [CmdletBinding(SupportsShouldProcess = $true)]
            param ()
            if ($destserver.VersionMajor -eq 8) {
                $serviceName = 'MSSQLSERVER'
            }
            else {
                $instance = $destserver.InstanceName
                if ($instance.length -eq 0) { $instance = "MSSQLSERVER" }
                $serviceName = "SQL Server Agent ($instance)"
            }

            if ($Pscmdlet.ShouldProcess($destination, "Starting Sql Agent")) {
                try {
                    $ipaddr = Resolve-SqlIpAddress $destserver
                    $agentservice = Get-Service -ComputerName $ipaddr -DisplayName $serviceName

                    if ($agentservice.Status -ne 'Running') {
                        $agentservice.Start()
                        $timeout = New-Timespan -seconds 60
                        $sw = [diagnostics.stopwatch]::StartNew()
                        $agentstatus = (Get-Service -ComputerName $ipaddr -DisplayName $serviceName).Status
                        while ($AgentStatus -ne 'Running' -and $sw.elapsed -lt $timeout) {
                            $agentStatus = (Get-Service -ComputerName $ipaddr -DisplayName $serviceName).Status
                        }
                    }
                }

                catch {
                    throw $_
                }

                if ($agentservice.Status -ne 'Running') {
                    throw "Cannot start Agent Service on $destination - Aborting."
                }
            }
        }

        function Start-DbccCheck {
            <#
            .SYNOPSIS

            #>

            [CmdletBinding(SupportsShouldProcess = $true)]
            param (
                [object]$server,
                [string]$dbname
            )

            $servername = $server.name
            $db = $server.databases[$dbname]

            if ($Pscmdlet.ShouldProcess($sourceserver, "Running dbcc check on $dbname on $servername")) {
                try {
                    $null = $db.CheckTables('None')
                    Write-Message -Level Verbose -Message "DBCC CHECKDB finished successfully for $dbname on $servername."
                }

                catch {
                    Write-Message -Level Warning -Message "DBCC CHECKDB failed."
                    Stop-Function -Message "Error occured: $_" -Target $agentservice -ErrorRecord $_ -Continue

                    if ($force) {
                        return $true
                    }
                    else {
                        return $false
                    }
                }
            }
        }

        function New-SqlAgentJobCategory {
            <#
                .SYNOPSIS

            #>
            [CmdletBinding(SupportsShouldProcess = $true)]
            param ([string]$categoryname,
                [object]$jobServer)

            if (!$jobServer.JobCategories[$categoryname]) {
                if ($Pscmdlet.ShouldProcess($sourceserver, "Running dbcc check on $dbname on $sourceserver")) {
                    try {
                        Write-Message -Level Verbose -Message "Creating Agent Job Category $categoryname."
                        $category = New-Object Microsoft.SqlServer.Management.Smo.Agent.JobCategory
                        $category.Parent = $jobServer
                        $category.Name = $categoryname
                        $category.Create()
                        Write-Message -Level Verbose -Message "Created Agent Job Category $categoryname."
                    }
                    catch {
                        Stop-Function -Message "FAILED : To Create Agent Job Category - $categoryname - Aborting." -Target $categoryname -ErrorRecord $_
                        return
                    }
                }
            }
        }

        function Restore-Database {
            <#
                .SYNOPSIS
                    Internal function. Restores .bak file to Sql database. Creates db if it doesn't exist. $filestructure is
                a custom object that contains logical and physical file locations.
            #>

            param (
                [Parameter(Mandatory = $true)]
                [Alias('ServerInstance', 'SqlInstance', 'SqlServer')]
                [object]$server,
                [Parameter(Mandatory = $true)]
                [ValidateNotNullOrEmpty()]
                [string]$dbname,
                [Parameter(Mandatory = $true)]
                [string]$backupfile,
                [string]$filetype = 'Database',
                [Parameter(Mandatory = $true)]
                [object]$filestructure,
                [switch]$norecovery,
                [PSCredential]$sqlCredential,
                [switch]$TSql = $false
            )

            $server = Connect-SqlInstance -SqlInstance $server -SqlCredential $sqlCredential
            $servername = $server.name
            $server.ConnectionContext.StatementTimeout = 0
            $restore = New-Object 'Microsoft.SqlServer.Management.Smo.Restore'
            $restore.ReplaceDatabase = $true

            foreach ($file in $filestructure.values) {
                $movefile = New-Object 'Microsoft.SqlServer.Management.Smo.RelocateFile'
                $movefile.LogicalFileName = $file.logical
                $movefile.PhysicalFileName = $file.physical
                $null = $restore.RelocateFiles.Add($movefile)
            }

            try {
                if ($TSql) {
                    $restore.PercentCompleteNotification = 1
                    $restore.add_Complete($complete)
                    $restore.ReplaceDatabase = $true
                    $restore.Database = $dbname
                    $restore.Action = $filetype
                    $restore.NoRecovery = $norecovery
                    $device = New-Object -TypeName Microsoft.SqlServer.Management.Smo.BackupDeviceItem
                    $device.name = $backupfile
                    $device.devicetype = 'File'
                    $restore.Devices.Add($device)
                    $restorescript = $restore.script($server)
                    return $restorescript
                }
                else {
                    $percent = [Microsoft.SqlServer.Management.Smo.PercentCompleteEventHandler] {
                        Write-Progress -id 1 -activity "Restoring $dbname to $servername" -percentcomplete $_.Percent -status ([System.String]::Format("Progress: {0} %", $_.Percent))
                    }
                    $restore.add_PercentComplete($percent)
                    $restore.PercentCompleteNotification = 1
                    $restore.add_Complete($complete)
                    $restore.ReplaceDatabase = $true
                    $restore.Database = $dbname
                    $restore.Action = $filetype
                    $restore.NoRecovery = $norecovery
                    $device = New-Object -TypeName Microsoft.SqlServer.Management.Smo.BackupDeviceItem
                    $device.name = $backupfile
                    $device.devicetype = 'File'
                    $restore.Devices.Add($device)

                    Write-Progress -id 1 -activity "Restoring $dbname to $servername" -percentcomplete 0 -status ([System.String]::Format("Progress: {0} %", 0))
                    $restore.sqlrestore($server)
                    Write-Progress -id 1 -activity "Restoring $dbname to $servername" -status 'Complete' -Completed

                    return $true
                }
            }
            catch {
                Stop-Function -Message "Restore failed" -ErrorRecord $_ -Target $dbname
                return $false
            }
        }

    }
    process {
        if (Test-FunctionInterrupt) {
            return
        }
        try {
            Start-SqlAgent
        }
        catch {
            Stop-Function -Message "Failure starting SQL Agent" -ErrorRecord $_
            return
        }

        $start = Get-Date
        Write-Message -Level Verbose -Message "Starting Rationalisation Script at $start."

        foreach ($dbname in $Database) {

            $db = $sourceserver.databases[$dbname]

            # The db check is needed when the number of databases exceeds 255, then it's no longer auto-populated
            if (!$db) {
                Stop-Function -Message "$dbname does not exist on $source. Aborting routine for this database." -Continue
            }

            $lastFullBckDuration = (Get-DbaBackupHistory -SqlInstance $sourceserver -Database $dbname -LastFull).Duration

            if (-NOT ([string]::IsNullOrEmpty($lastFullBckDuration))) {
                $lastFullBckDurationSec = $lastFullBckDuration.TotalSeconds
                $lastFullBckDurationMin = [Math]::Round($lastFullBckDuration.TotalMinutes, 2)

                Write-Message -Level Verbose -Message "From the backup history the last full backup took $lastFullBckDurationSec seconds ($lastFullBckDurationMin minutes)"
                if ($lastFullBckDurationSec -gt 600) {
                    Write-Message -Level Verbose -Message "Last full backup took more than 10 minutes. Do you want to continue?"

                    # Set up the parts for the user choice
                    $Title = "Backup duration"
                    $Info = "Last full backup took more than $lastFullBckDurationMin minutes. Do you want to continue?"

                    $Options = [System.Management.Automation.Host.ChoiceDescription[]] @("&Yes", "&No (Skip)")
                    [int]$Defaultchoice = 0
                    $choice = $host.UI.PromptForChoice($Title, $Info, $Options, $Defaultchoice)
                    # Check the given option
                    if ($choice -eq 1) {
                        Stop-Function -Message "You have chosen skipping the database $dbname because of last known backup time ($lastFullBckDurationMin minutes)." -ErrorRecord $_ -Target $dbname -Continue
                        Continue
                    }
                }
            }
            else {
                Write-Message -Level Verbose -Message "Couldn't find last full backup time for database $dbname using Get-DbaBackupHistory."
            }

            $jobname = "Rationalised Database Restore Script for $dbname"
            $jobStepName = "Restore the $dbname database from Final Backup"
            $jobServer = $destserver.JobServer

            if ($jobServer.Jobs[$jobname].count -gt 0) {
                if ($force -eq $false) {
                    Stop-Function -Message "FAILED: The Job $jobname already exists. Have you done this before? Rename the existing job and try again or use -Force to drop and recreate." -Continue
                }
                else {
                    if ($Pscmdlet.ShouldProcess($dbname, "Dropping $jobname on $source")) {
                        Write-Message -Level Verbose -Message "Dropping $jobname on $source."
                        $jobServer.Jobs[$jobname].Drop()
                        $jobServer.Jobs.Refresh()
                    }
                }
            }


            Write-Message -Level Verbose -Message "Starting Rationalisation of $dbname."
            ## if we want to Dbcc before to abort if we have a corrupt database to start with
            if ($NoDbccCheckDb -eq $false) {
                if ($Pscmdlet.ShouldProcess($dbname, "Running dbcc check on $dbname on $source")) {
                    Write-Message -Level Verbose -Message "Starting DBCC CHECKDB for $dbname on $source."
                    $dbccgood = Start-DbccCheck -Server $sourceserver -DBName $dbname

                    if ($dbccgood -eq $false) {
                        if ($force -eq $false) {
                            Write-Message -Level Verbose -Message "DBCC failed for $dbname (you should check that).  Aborting routine for this database."
                            continue
                        }
                        else {
                            Write-Message -Level Verbose -Message "DBCC failed, but Force specified. Continuing."
                        }
                    }
                }
            }

            if ($Pscmdlet.ShouldProcess($source, "Backing up $dbname")) {
                Write-Message -Level Verbose -Message "Starting Backup for $dbname on $source."
                ## Take a Backup
                try {
                    $timenow = [DateTime]::Now.ToString('yyyyMMdd_HHmmss')
                    $backup = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Backup
                    $backup.Action = [Microsoft.SqlServer.Management.SMO.BackupActionType]::Database
                    $backup.BackupSetDescription = "Final Full Backup of $dbname Prior to Dropping"
                    $backup.Database = $dbname
                    $backup.Checksum = $True
                    if ($sourceserver.versionMajor -gt 9) {
                        $backup.CompressionOption = $BackupCompression
                    }
                    if ($force -and $dbccgood -eq $false) {

                        $filename = "$backupFolder\$($dbname)_DBCCERROR_$timenow.bak"
                    }
                    else {
                        $filename = "$backupFolder\$($dbname)_Final_Before_Drop_$timenow.bak"
                    }

                    $devicetype = [Microsoft.SqlServer.Management.Smo.DeviceType]::File
                    $backupDevice = New-Object -TypeName Microsoft.SqlServer.Management.Smo.BackupDeviceItem($filename, $devicetype)

                    $backup.Devices.Add($backupDevice)
                    #Progress
                    $percent = [Microsoft.SqlServer.Management.Smo.PercentCompleteEventHandler] {
                        Write-Progress -id 1 -activity "Backing up database $dbname on $source to $filename" -percentcomplete $_.Percent -status ([System.String]::Format("Progress: {0} %", $_.Percent))
                    }
                    $backup.add_PercentComplete($percent)
                    $backup.add_Complete($complete)
                    Write-Progress -id 1 -activity "Backing up database $dbname on $source to $filename" -percentcomplete 0 -status ([System.String]::Format("Progress: {0} %", 0))
                    $backup.SqlBackup($sourceserver)
                    $null = $backup.Devices.Remove($backupDevice)
                    Write-Progress -id 1 -activity "Backing up database $dbname  on $source to $filename" -status "Complete" -Completed
                    Write-Message -Level Verbose -Message "Backup Completed for $dbname on $source."

                    Write-Message -Level Verbose -Message "Running Restore Verify only on Backup of $dbname on $source."
                    try {
                        $restoreverify = New-Object 'Microsoft.SqlServer.Management.Smo.Restore'
                        $restoreverify.Database = $dbname
                        $restoreverify.Devices.AddDevice($filename, $devicetype)
                        $result = $restoreverify.SqlVerify($sourceserver)

                        if ($result -eq $false) {
                            Write-Message -Level Warning -Message "FAILED : Restore Verify Only failed for $filename on $server - aborting routine for this database."
                            continue
                        }

                        Write-Message -Level Verbose -Message "Restore Verify Only for $filename succeeded."
                    }
                    catch {
                        Stop-Function -Message "FAILED : Restore Verify Only failed for $filename on $server - aborting routine for this database. Exception: $_" -Target $filename -ErrorRecord $_ -Continue
                    }
                }
                catch {
                    Stop-Function -Message "FAILED : Restore Verify Only failed for $filename on $server - aborting routine for this database. Exception: $_" -Target $filename -ErrorRecord $_ -Continue
                }
            }

            if ($Pscmdlet.ShouldProcess($destination, "Creating Automated Restore Job from Golden Backup for $dbname on $destination")) {
                Write-Message -Level Verbose -Message "Creating Automated Restore Job from Golden Backup for $dbname on $destination."
                try {
                    if ($force -eq $true -and $dbccgood -eq $false) {
                        $jobName = $jobname -replace "Rationalised", "DBCC ERROR"
                    }

                    ## Create an agent job to restore the database
                    $job = New-Object Microsoft.SqlServer.Management.Smo.Agent.Job $jobServer, $jobname
                    $job.Name = $jobname
                    $job.OwnerLoginName = $jobowner
                    $job.Description = "This job will restore the $dbname database using the final backup located at $filename."

                    ## Create a Job Category
                    if (!$jobServer.JobCategories[$categoryname]) {
                        New-SqlAgentJobCategory -JobServer $jobServer -categoryname $categoryname
                    }

                    $job.Category = $categoryname
                    try {
                        if ($Pscmdlet.ShouldProcess($destination, "Creating Agent Job on $destination")) {
                            Write-Message -Level Verbose -Message "Created Agent Job $jobname on $destination."
                            $job.Create()
                        }
                    }
                    catch {
                        Stop-Function -Message "FAILED : To Create Agent Job $jobname on $destination - aborting routine for this database." -Target $categoryname -ErrorRecord $_ -Continue
                    }

                    ## Create Job Step
                    ## Aaron's Suggestion: In the restore script, add a comment block that tells the last known size of each file in the database.
                    ## Suggestion check for disk space before restore
                    ## Create Restore Script
                    try {
                        $restore = New-Object Microsoft.SqlServer.Management.Smo.Restore
                        $device = New-Object -TypeName Microsoft.SqlServer.Management.Smo.BackupDeviceItem $filename, 'FILE'
                        $restore.Devices.Add($device)
                        try {
                            $filelist = $restore.ReadFileList($destserver)
                        }

                        catch {
                            throw 'File list could not be determined. This is likely due to connectivity issues or tiemouts with the Sql Server, the database version is incorrect, or the Sql Server service account does not have access to the file share. Script terminating.'
                        }

                        $filestructure = Get-OfflineSqlFileStructure $destserver $dbname $filelist $ReuseSourceFolderStructure

                        $jobStepCommand = Restore-Database $destserver $dbname $filename "Database" $filestructure -TSql -ErrorAction Stop
                        $jobStep = new-object Microsoft.SqlServer.Management.Smo.Agent.JobStep $job, $jobStepName
                        $jobStep.SubSystem = 'TransactSql' # 'PowerShell'
                        $jobStep.DatabaseName = 'master'
                        $jobStep.Command = $jobStepCommand
                        $jobStep.OnSuccessAction = 'QuitWithSuccess'
                        $jobStep.OnFailAction = 'QuitWithFailure'
                        if ($Pscmdlet.ShouldProcess($destination, "Creating Agent JobStep on $destination")) {
                            $null = $jobStep.Create()
                        }
                        $jobStartStepid = $jobStep.ID
                        Write-Message -Level Verbose -Message "Created Agent JobStep $jobStepName on $destination."
                    }
                    catch {
                        Stop-Function -Message "FAILED : To Create Agent JobStep $jobStepName on $destination - Aborting." -Target $jobStepName -ErrorRecord $_ -Continue
                    }
                    if ($Pscmdlet.ShouldProcess($destination, "Applying Agent Job $jobname to $destination")) {
                        $job.ApplyToTargetServer($destination)
                        $job.StartStepID = $jobStartStepid
                        $job.Alter()
                    }
                }
                catch {
                    Stop-Function -Message "FAILED : To Create Agent Job $jobname on $destination - aborting routine for $dbname. Exception: $_" -Target $jobname -ErrorRecord $_ -Continue
                }
            }

            if ($Pscmdlet.ShouldProcess($destination, "Dropping Database $dbname on $sourceserver")) {
                ## Drop the database
                try {
                    $null = Remove-DbaDatabase -SqlInstance $sourceserver -Database $dbname -Confirm:$false
                    Write-Message -Level Verbose -Message "Dropped $dbname Database on $source prior to running the Agent Job"
                }
                catch {
                    Stop-Function -Message "FAILED : To Drop database $dbname on $server - aborting routine for $dbname. Exception: $_" -Continue
                }
            }

            if ($Pscmdlet.ShouldProcess($destination, "Running Agent Job on $destination to restore $dbname")) {
                ## Run the restore job to restore it
                Write-Message -Level Verbose -Message "Starting $jobname on $destination."
                try {
                    $job = $destserver.JobServer.Jobs[$jobname]
                    $job.Start()
                    $job.Refresh()
                    $status = $job.CurrentRunStatus

                    while ($status -ne 'Idle') {
                        Write-Message -Level Verbose -Message "Restore Job for $dbname on $destination is $status."
                        Start-Sleep -Seconds 15
                        $job.Refresh()
                        $status = $job.CurrentRunStatus
                    }

                    Write-Message -Level Verbose -Message "Restore Job $jobname has completed on $destination."
                    Write-Message -Level Verbose -Message "Sleeping for a few seconds to ensure the next step (DBCC) succeeds."
                    Start-Sleep -Seconds 10 ## This is required to ensure the next DBCC Check succeeds
                }
                catch {
                    Stop-Function -Message "FAILED : Restore Job $jobname failed on $destination - aborting routine for $dbname. Exception: $_" -Continue
                }

                if ($job.LastRunOutcome -ne 'Succeeded') {
                    # LOL, love the plug.
                    Write-Message -Level Warning -Message "FAILED : Restore Job $jobname failed on $destination - aborting routine for $dbname."
                    Write-Message -Level Warning -Message "Check the Agent Job History on $destination - if you have SSMS2016 July release or later."
                    Write-Message -Level Warning -Message "Get-SqlAgentJobHistory -JobName '$jobname' -ServerInstance $destination -OutcomesType Failed."
                    continue
                }
            }

            $refreshRetries = 1

            while ($null -eq ($destserver.databases[$dbname]) -and $refreshRetries -lt 6) {
                Write-Message -Level verbose -Message "Database $dbname not found! Refreshing collection."

                #refresh database list, otherwise the next step (DBCC) can fail
                $destserver.Databases.Refresh()

                Start-Sleep -Seconds 1

                $refreshRetries += 1
            }


            ## Run a Dbcc No choice here
            if ($Pscmdlet.ShouldProcess($dbname, "Running Dbcc CHECKDB on $dbname on $destination")) {
                Write-Message -Level Verbose -Message "Starting Dbcc CHECKDB for $dbname on $destination."
                $null = Start-DbccCheck -Server $destserver -DbName $dbname
            }

            if ($Pscmdlet.ShouldProcess($dbname, "Dropping Database $dbname on $destination")) {
                ## Drop the database
                try {
                    $null = Remove-DbaDatabase -SqlInstance $sourceserver -Database $dbname -Confirm:$false
                    Write-Message -Level Verbose -Message "Dropped $dbname database on $destination."
                }
                catch {
                    Stop-Function -Message "FAILED : To Drop database $dbname on $destination - Aborting. Exception: $_" -Target $dbname -ErrorRecord $_ -Continue
                }
            }
            Write-Message -Level Verbose -Message "Rationalisation Finished for $dbname."

            [PSCustomObject]@{
                SqlInstance     = $source
                DatabaseName    = $dbname
                JobName         = $jobname
                TestingInstance = $destination
                BackupFolder    = $backupFolder
            }
        }
    }

    end {
        if (Test-FunctionInterrupt) {
            return
        }
        if ($Pscmdlet.ShouldProcess("console", "Showing final message")) {
            $End = Get-Date
            Write-Message -Level Verbose -Message "Finished at $End."
            $Duration = $End - $start
            Write-Message -Level Verbose -Message "Script Duration: $Duration."
        }

        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Remove-SqlDatabaseSafely
    }
}
function Remove-DbaDbCertificate {
    <#
.SYNOPSIS
Deletes specified database certificate

.DESCRIPTION
Deletes specified database certificate

.PARAMETER SqlInstance
The SQL Server to create the certificates on.

.PARAMETER SqlCredential
Allows you to login to SQL Server using alternative credentials.

.PARAMETER Database
The database where the certificate will be removed.

.PARAMETER Certificate
The certificate that will be removed

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.PARAMETER InputObject
Piped certificate objects

.NOTES
Tags: Certificate
Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
Remove-DbaDbCertificate -SqlInstance Server1

The certificate in the master database on server1 will be removed if it exists.

.EXAMPLE
Remove-DbaDbCertificate -SqlInstance Server1 -Database db1 -Confirm:$false

Suppresses all prompts to remove the certificate in the 'db1' database and drops the key.


#>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true, ConfirmImpact = "High")]
    param (
        [parameter(Mandatory, ParameterSetName = "instance")]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory, ParameterSetName = "instance")]
        [object[]]$Database,
        [parameter(Mandatory, ParameterSetName = "instance")]
        [object[]]$Certificate,
        [parameter(ValueFromPipeline, ParameterSetName = "collection")]
        [Microsoft.SqlServer.Management.Smo.Certificate[]]$InputObject,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {

        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Remove-DbaDatabaseCertificate

        function drop-cert ($smocert) {
            $server = $smocert.Parent.Parent
            $instance = $server.DomainInstanceName
            $cert = $smocert.Name
            $db = $smocert.Parent.Name

            $output = [pscustomobject]@{
                ComputerName = $server.ComputerName
                InstanceName = $server.ServiceName
                SqlInstance  = $instance
                Database     = $db
                Certificate  = $cert
                Status       = $null
            }

            if ($Pscmdlet.ShouldProcess($instance, "Dropping the certificate named $cert for database '$db' on $server")) {
                try {
                    $smocert.Drop()
                    Write-Message -Level Verbose -Message "Successfully removed certificate named $cert from the $db database on $server"
                    $output.status = "Success"
                }
                catch {
                    $output.Status = "Failure"
                    Stop-Function -Message "Failed to drop certificate named $cert from $db on $server." -Target $smocert -InnerErrorRecord $_ -Continue
                }
                $output
            }
        }
    }
    process {

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            foreach ($db in $Database) {
                $currentdb = $server.Databases[$db]

                if ($null -eq $currentdb) {
                    Stop-Function -Message "Database '$db' does not exist on $server" -Target $currentdb -Continue
                }

                if (-not $currentdb.IsAccessible) {
                    Stop-Function -Message "Database '$db' is not accessible" -Target $currentdb -Continue
                }

                foreach ($cert in $certificate) {
                    $smocert = $currentdb.Certificates[$cert]

                    if ($null -eq $smocert) {
                        Stop-Function -Message "No certificate named $cert exists in the $db database on $server" -Target $currentdb.Certificates -Continue
                    }

                    Drop-Cert -smocert $smocert
                }
            }
        }

        foreach ($smocert in $InputObject) {
            Drop-Cert -smocert $smocert
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Remove-DbaDbSnapshot {
    <#
    .SYNOPSIS
        Removes database snapshots

    .DESCRIPTION
        Removes (drops) database snapshots from the server

    .PARAMETER SqlInstance
        The SQL Server that you're connecting to

    .PARAMETER SqlCredential
        Credential object used to connect to the SQL Server as a different user

    .PARAMETER Database
        Removes snapshots for only this specific base db

    .PARAMETER ExcludeDatabase
        Removes snapshots excluding this specific base dbs

    .PARAMETER Snapshot
        Restores databases from snapshot with this name only

    .PARAMETER AllSnapshots
        Specifies that you want to remove all snapshots from the server

    .PARAMETER Force
        Will forcibly kill all running queries that prevent the drop process.

    .PARAMETER WhatIf
        Shows what would happen if the command were to run

    .PARAMETER Confirm
        Prompts for confirmation of every step.

    .PARAMETER InputObject
        Enables input from Get-DbaDbSnapshot

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Tags: Snapshot, Database
        Author: niphlod

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
         https://dbatools.io/Remove-DbaDbSnapshot

    .EXAMPLE
        Remove-DbaDbSnapshot -SqlInstance sql2014 -Snapshot HR_snap_20161201, HR_snap_20161101

        Removes database snapshots named HR_snap_20161201 and HR_snap_20161101

    .EXAMPLE
        Remove-DbaDbSnapshot -SqlInstance sql2014 -Database HR, Accounting

        Removes all database snapshots having HR and Accounting as base dbs

    .EXAMPLE
        Get-DbaDbSnapshot -SqlInstance sql2014 -Database HR, Accounting | Remove-DbaDbSnapshot

        Removes all database snapshots having HR and Accounting as base dbs

    .EXAMPLE
        Remove-DbaDbSnapshot -SqlInstance sql2014 -Snapshot HR_snapshot, Accounting_snapshot

        Removes HR_snapshot and Accounting_snapshot

    .EXAMPLE
        Get-DbaDbSnapshot -SqlInstance sql2016 | Where SnapshotOf -like '*dumpsterfire*' | Remove-DbaDbSnapshot

        Removes all snapshots associated with databases that have dumpsterfire in the name

    .EXAMPLE
        Get-DbaDbSnapshot -SqlInstance sql2016 | Out-GridView -Passthru | Remove-DbaDbSnapshot

        Allows the selection of snapshots on sql2016 to remove

    .EXAMPLE
        Remove-DbaDbSnapshot -SqlInstance sql2014 -AllSnapshots

        Removes all database snapshots from sql2014

    .EXAMPLE
        Remove-DbaDbSnapshot -SqlInstance sql2014 -AllSnapshots -Confirm

        Removes all database snapshots from sql2014 and prompts for each database
#>
    [CmdletBinding(SupportsShouldProcess)]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [string[]]$Database,
        [string[]]$ExcludeDatabase,
        [string[]]$Snapshot,
        [parameter(ValueFromPipeline)]
        [Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
        [switch]$AllSnapshots,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        $defaultprops = 'ComputerName', 'InstanceName', 'SqlInstance', 'Database as Name', 'Status'
    }
    process {
        if (!$Snapshot -and !$Database -and !$AllSnapshots -and $null -eq $InputObject -and !$ExcludeDatabase) {
            Stop-Function -Message "You must pipe in a snapshot or specify -Snapshot, -Database, -ExcludeDatabase or -AllSnapshots"
            return
        }

        # if piped value either doesn't exist or is not the proper type
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $InputObject += Get-DbaDbSnapshot -SqlInstance $server -Database $Database -ExcludeDatabase $ExcludeDatabase -Snapshot $Snapshot
        }

        foreach ($db in $InputObject) {
            $server = $db.Parent

            if (-not $db.DatabaseSnapshotBaseName) {
                Stop-Function -Message "$db on $server is not a database snapshot" -Continue
            }

            if ($Force) {
                $db | Remove-DbaDatabase -Confirm:$false | Select-DefaultView -Property $defaultprops
            }
            else {
                try {
                    if ($Pscmdlet.ShouldProcess("$db on $server", "Drop snapshot")) {
                        $db.Drop()
                        $server.Refresh()

                        [pscustomobject]@{
                            ComputerName   = $server.ComputerName
                            InstanceName   = $server.ServiceName
                            SqlInstance    = $server.DomainInstanceName
                            Database       = $db.name
                            Status         = "Dropped"
                        } | Select-DefaultView -Property $defaultprops
                    }
                }
                catch {
                    Write-Message -Level Verbose -Message "Could not drop database $db on $server"

                    [pscustomobject]@{
                        ComputerName   = $server.ComputerName
                        InstanceName   = $server.ServiceName
                        SqlInstance    = $server.DomainInstanceName
                        Database       = $db.name
                        Status         = (Get-ErrorMessage -Record $_)
                    } | Select-DefaultView -Property $defaultprops
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Remove-DbaDatabaseSnapshot
    }
}
function Remove-DbaDbUser {
    <#
    .SYNOPSIS
    Drop database user

    .DESCRIPTION
    If user is the owner of a schema with the same name and if if the schema does not have any underlying objects the schema will be
    dropped.  If user owns more than one schema, the owner of the schemas that does not have the same name as the user, will be
    changed to 'dbo'. If schemas have underlying objects, you must specify the -Force parameter so the user can be dropped.

    .PARAMETER SqlInstance
    The SQL Instances that you're connecting to.

    .PARAMETER SqlCredential
    Credential object used to connect to the SQL Server as a different user.

    .PARAMETER Database
    Specifies the database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

    .PARAMETER ExcludeDatabase
    Specifies the database(s) to exclude from processing. Options for this list are auto-populated from the server.

    .PARAMETER User
    Specifies the list of users to remove.

    .PARAMETER InputObject
    Support piping from Get-DbaDatabaseUser.

    .PARAMETER Force
    If enabled this will force the change of the owner to 'dbo' for any schema which owner is the User.

    .PARAMETER WhatIf
    Shows what would happen if the command were to run. No actions are actually performed.

    .PARAMETER Confirm
    If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

    .PARAMETER EnableException
    By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
    Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
    Tags: Database, User, Login, Security
    Author: Doug Meyers (@dgmyrs)

    Website: https://dbatools.io
    Copyright: (C) Chrissy LeMaire, [email protected]
    License: MIT https://opensource.org/licenses/MIT

    .LINK
    https://dbatools.io/Remove-DbaDbUser

    .EXAMPLE
    Remove-DbaDbUser -SqlInstance sqlserver2014 -User user1

    Drops user1 from all databases it exists in on server 'sqlserver2014'.

    .EXAMPLE
    Remove-DbaDbUser -SqlInstance sqlserver2014 -Database database1 -User user1

    Drops user1 from the database1 database on server 'sqlserver2014'.

    .EXAMPLE
    Remove-DbaDbUser -SqlInstance sqlserver2014 -ExcludeDatabase model -User user1

    Drops user1 from all databases it exists in on server 'sqlserver2014' except for the model database.

    .EXAMPLE
    Get-DbaDatabaseUser sqlserver2014 | Where-Object Name -In "user1" | Remove-DbaDbUser

    Drops user1 from all databases it exists in on server 'sqlserver2014'.

#>

    [CmdletBinding(DefaultParameterSetName = 'User', SupportsShouldProcess = $true)]
    Param (
        [parameter(Position = 1, Mandatory, ValueFromPipeline, ParameterSetName = 'User')]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,

        [parameter(ParameterSetName = 'User')]
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,

        [parameter(ParameterSetName = 'User')]
        [Alias("Databases")]
        [object[]]$Database,

        [parameter(ParameterSetName = 'User')]
        [object[]]$ExcludeDatabase,

        [parameter(Mandatory, ParameterSetName = 'User')]
        [object[]]$User,

        [parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Object')]
        [Microsoft.SqlServer.Management.Smo.User[]]$InputObject,

        [parameter(ParameterSetName = 'User')]
        [parameter(ParameterSetName = 'Object')]
        [switch]$Force,

        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        function Remove-DbUser {
            [CmdletBinding()]
            param ([Microsoft.SqlServer.Management.Smo.User[]]$users)

            foreach ($user in $users) {
                $db = $user.Parent
                $server = $db.Parent
                $ownedObjects = $false
                Write-Message -Level Verbose -Message "Removing User $user from Database $db on target $server"

                # Drop Schemas owned by the user before droping the user
                $schemaUrns = $user.EnumOwnedObjects() | Where-Object Type -EQ Schema
                if ($schemaUrns) {
                    Write-Message -Level Verbose -Message "User $user owns $($schemaUrns.Count) schema(s)."

                    # Need to gather up the schema changes so they can be done in a non-desctructive order
                    $alterSchemas = @()
                    $dropSchemas = @()

                    foreach ($schemaUrn in $schemaUrns) {
                        $schema = $server.GetSmoObject($schemaUrn)

                        # Drop any schema that is the same name as the user
                        if ($schema.Name -EQ $user.Name) {
                            # Check for owned objects early so we can exit before any changes are made
                            $ownedUrns = $schema.EnumOwnedObjects()
                            if (-Not $ownedUrns) {
                                $dropSchemas += $schema
                            }
                            else {
                                Write-Message -Level Warning -Message "User owns objects in the database and will not be removed."
                                foreach ($ownedUrn in $ownedUrns) {
                                    $obj = $server.GetSmoObject($ownedUrn)
                                    Write-Message -Level Warning -Message "User $user owns $($obj.GetType().Name) $obj"
                                }
                                $ownedObjects = $true
                            }
                        }

                        # Change the owner of any schema not the same name as the user
                        if ($schema.Name -NE $user.Name) {
                            # Check for owned objects early so we can exit before any changes are made
                            $ownedUrns = $schema.EnumOwnedObjects()
                            if (($ownedUrns -And $Force) -Or (-Not $ownedUrns)) {
                                $alterSchemas += $schema
                            }
                            else {
                                Write-Message -Level Warning -Message "User $user owns the Schema $schema, which owns $($ownedUrns.Count) Object(s).  If you want to change the schemas' owner to [dbo] and drop the user anyway, use -Force parameter.  User $user will not be removed."
                                $ownedObjects = $true
                            }
                        }
                    }
                }

                if (-Not $ownedObjects) {
                    try {
                        # Alter Schemas
                        foreach ($schema in $alterSchemas) {
                            Write-Message -Level Verbose -Message "Owner of Schema $schema will be changed to [dbo]."
                            if ($PSCmdlet.ShouldProcess($server, "Change the owner of Schema $schema to [dbo].")) {
                                $schema.Owner = "dbo"
                                $schema.Alter()
                            }
                        }

                        # Drop Schemas
                        foreach ($schema in $dropSchemas) {
                            if ($PSCmdlet.ShouldProcess($server, "Drop Schema $schema from Database $db.")) {
                                $schema.Drop()
                            }
                        }

                        # Finally, Drop user
                        if ($PSCmdlet.ShouldProcess($server, "Drop User $user from Database $db.")) {
                            $user.Drop()
                        }

                        $status = "Dropped"

                    }
                    catch {
                        Write-Error -Message "Could not drop $user from Database $db on target $server"
                        $status = "Not Dropped"
                    }

                    [pscustomobject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Database     = $db.name
                        User         = $user
                        Status       = $status
                    }
                }
            }
        }
    }

    process {
        if ($InputObject) {
            Remove-DbUser $InputObject
        }
        else {
            foreach ($instance in $SqlInstance) {
                try {
                    Write-Message -Level Verbose -Message "Connecting to $instance"
                    $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
                }
                catch {
                    Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                }

                $databases = $server.Databases | Where-Object IsAccessible

                if ($Database) {
                    $databases = $databases | Where-Object Name -In $Database
                }
                if ($ExcludeDatabase) {
                    $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
                }

                foreach ($db in $databases) {
                    Write-Message -Level Verbose -Message "Get users in Database $db on target $server"
                    $users = Get-DbaDatabaseUser -SqlInstance $server -Database $db.Name
                    $users = $users | Where-Object Name -In $User
                    Remove-DbUser $users
                }
            }
        }
    }

}
function Remove-DbaLogin {
    <#
.SYNOPSIS
Drops a Login

.DESCRIPTION
Tries a bunch of different ways to remove a Login or two or more.

.PARAMETER SqlInstance
The SQL Server instance holding the Logins to be removed.You must have sysadmin access and server version must be SQL Server version 2000 or higher.

.PARAMETER SqlCredential
Allows you to login to servers using alternative credentials.

.PARAMETER Login
The Login(s) to process - this list is auto-populated from the server. If unspecified, all Logins will be processed.

.PARAMETER InputObject
A collection of Logins (such as returned by Get-DbaLogin), to be removed.

.PARAMETER Force
Kills any sessions associated with the login prior to drop

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Delete, Logins

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Remove-DbaLogin

.EXAMPLE
Remove-DbaLogin -SqlInstance sql2016 -Login mylogin

Prompts then removes the Login mylogin on SQL Server sql2016

.EXAMPLE
Remove-DbaLogin -SqlInstance sql2016 -Login mylogin, yourlogin

Prompts then removes the Logins mylogin and yourlogin on SQL Server sql2016

.EXAMPLE
Remove-DbaLogin -SqlInstance sql2016 -Login mylogin -Confirm:$false

Does not prompt and swiftly removes mylogin on SQL Server sql2016

.EXAMPLE
Get-DbaLogin -SqlInstance server\instance -Login yourlogin | Remove-DbaLogin

removes mylogin on SQL Server server\instance

#>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High', DefaultParameterSetName = "Default")]
    Param (
        [parameter(Mandatory, ParameterSetName = "instance")]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [parameter(Mandatory = $false)]
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [parameter(Mandatory, ParameterSetName = "instance")]
        [string[]]$Login,
        [Parameter(ValueFromPipeline, Mandatory, ParameterSetName = "Logins")]
        [Microsoft.SqlServer.Management.Smo.Login[]]$InputObject,
        [switch]$Force,
        [switch]$EnableException
    )
    
    process {
        
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            $InputObject += $server.Logins | Where-Object { $_.Name -in $Login }
        }
        
        foreach ($currentlogin in $InputObject) {
            try {
                $server = $currentlogin.Parent
                if ($Pscmdlet.ShouldProcess("$currentlogin on $server", "KillLogin")) {
                    if ($force) {
                        $null = Stop-DbaProcess -SqlInstance $server -Login $currentlogin.name
                    }
                    
                    $currentlogin.Drop()
                    
                    [pscustomobject]@{
                        ComputerName  = $server.ComputerName
                        InstanceName  = $server.ServiceName
                        SqlInstance   = $server.DomainInstanceName
                        Login         = $currentlogin.name
                        Status        = "Dropped"
                    }
                }
            }
            catch {
                [pscustomobject]@{
                    ComputerName  = $server.ComputerName
                    InstanceName  = $server.ServiceName
                    SqlInstance   = $server.DomainInstanceName
                    Login         = $currentlogin.name
                    Status        = $_
                }
                Stop-Function -Message "Could not drop Login $currentlogin on $server" -ErrorRecord $_ -Target $currentlogin -Continue
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#

function Remove-DbaNetworkCertificate {
    <#
    .SYNOPSIS
        Removes the network certificate for SQL Server instance

    .DESCRIPTION
        Removes the network certificate for SQL Server instance. This setting is found in Configuration Manager.

    .PARAMETER SqlInstance
        The target SQL Server - defaults to localhost. If target is a cluster, you must also specify InstanceClusterName (see below)

    .PARAMETER Credential
        Allows you to login to the computer (not sql instance) using alternative credentials.

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .PARAMETER WhatIf
        Shows what would happen if the command were to run. No actions are actually performed.

    .PARAMETER Confirm
        Prompts you for confirmation before executing any changing operations within the command.

    .EXAMPLE
        Remove-DbaNetworkCertificate

        Removes the Network Certificate for the default instance (MSSQLSERVER) on localhost

    .EXAMPLE
        Remove-DbaNetworkCertificate -SqlInstance sql1\SQL2008R2SP2

        Removes the Network Certificate for the SQL2008R2SP2 instance on sql1

    .EXAMPLE
        Remove-DbaNetworkCertificate -SqlInstance localhost\SQL2008R2SP2 -WhatIf

        Shows what would happen if the command were run

    .NOTES
        Tags: Certificate

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT
#>
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Low", DefaultParameterSetName = 'Default')]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer", "ComputerName")]
        [DbaInstanceParameter[]]
        $SqlInstance = $env:COMPUTERNAME,

        [PSCredential]

        $Credential,

        [switch]
        [Alias('Silent')]$EnableException
    )
    process {
        foreach ($instance in $sqlinstance) {
            Write-Message -Level VeryVerbose -Message "Processing $instance" -Target $instance
            $null = Test-ElevationRequirement -ComputerName $instance -Continue

            Write-Message -Level Verbose -Message "Resolving hostname"
            $resolved = $null
            $resolved = Resolve-DbaNetworkName -ComputerName $instance -Turbo

            if ($null -eq $resolved) {
                Stop-Function -Message "Can't resolve $instance" -Target $instance -Continue -Category InvalidArgument
            }

            Write-Message -Level Output -Message "Connecting to SQL WMI on $($instance.ComputerName)"
            try {
                $sqlwmi = Invoke-ManagedComputerCommand -ComputerName $resolved.FQDN -ScriptBlock { $wmi.Services } -Credential $Credential -ErrorAction Stop | Where-Object DisplayName -eq "SQL Server ($($instance.InstanceName))"
            }
            catch {
                Stop-Function -Message "Failed to access $instance" -Target $instance -Continue -ErrorRecord $_
            }

            $regroot = ($sqlwmi.AdvancedProperties | Where-Object Name -eq REGROOT).Value
            $vsname = ($sqlwmi.AdvancedProperties | Where-Object Name -eq VSNAME).Value
            $instancename = $sqlwmi.DisplayName.Replace('SQL Server (', '').Replace(')', '') # Don't clown, I don't know regex :(
            $serviceaccount = $sqlwmi.ServiceAccount

            if ([System.String]::IsNullOrEmpty($regroot)) {
                $regroot = $sqlwmi.AdvancedProperties | Where-Object { $_ -match 'REGROOT' }
                $vsname = $sqlwmi.AdvancedProperties | Where-Object { $_ -match 'VSNAME' }

                if (![System.String]::IsNullOrEmpty($regroot)) {
                    $regroot = ($regroot -Split 'Value\=')[1]
                    $vsname = ($vsname -Split 'Value\=')[1]
                }
                else {
                    Stop-Function -Message "Can't find instance $vsname on $instance" -Continue -Category ObjectNotFound -Target $instance
                }
            }

            if ([System.String]::IsNullOrEmpty($vsname)) { $vsname = $instance }

            Write-Message -Level Output -Message "Regroot: $regroot" -Target $instance
            Write-Message -Level Output -Message "ServiceAcct: $serviceaccount" -Target $instance
            Write-Message -Level Output -Message "InstanceName: $instancename" -Target $instance
            Write-Message -Level Output -Message "VSNAME: $vsname" -Target $instance

            $scriptblock = {
                $regroot = $args[0]
                $serviceaccount = $args[1]
                $instancename = $args[2]
                $vsname = $args[3]

                $regpath = "Registry::HKEY_LOCAL_MACHINE\$($args[0])\MSSQLServer\SuperSocketNetLib"
                $cert = (Get-ItemProperty -Path $regpath -Name Certificate).Certificate
                Set-ItemProperty -Path $regpath -Name Certificate -Value $null

                [pscustomobject]@{
                    ComputerName      = $env:COMPUTERNAME
                    InstanceName      = $instancename
                    SqlInstance       = $vsname
                    ServiceAccount    = $serviceaccount
                    RemovedThumbprint = $cert.Thumbprint
                }
            }

            if ($PScmdlet.ShouldProcess("local", "Connecting to $ComputerName to remove the cert")) {
                try {
                    Invoke-Command2 -ComputerName $resolved.fqdn -Credential $Credential -ArgumentList $regroot, $serviceaccount, $instancename, $vsname -ScriptBlock $scriptblock -ErrorAction Stop
                }
                catch {
                    Stop-Function -Message "Failed to connect to $($resolved.fqdn) using PowerShell remoting!" -ErrorRecord $_ -Target $instance -Continue
                }
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Remove-DbaOrphanUser {
    <#
        .SYNOPSIS
            Drop orphan users with no existing login to map

        .DESCRIPTION
            An orphan user is defined by a user that does not have their matching login. (Login property = "").

            If user is the owner of the schema with the same name and if if the schema does not have any underlying objects the schema will be dropped.

            If user owns more than one schema, the owner of the schemas that does not have the same name as the user, will be changed to 'dbo'. If schemas have underlying objects, you must specify the -Force parameter so the user can be dropped.

            If exists a login to map the drop will not be performed unless you specify the -Force parameter (only when calling from Repair-DbaOrphanUser.

        .PARAMETER SqlInstance
            The SQL Server Instance to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies the database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies the database(s) to exclude from processing. Options for this list are auto-populated from the server

        .PARAMETER User
            Specifies the list of users to remove.

        .PARAMETER Force
            If this switch is enabled:
                If exists any schema which owner is the User, this will force the change of the owner to 'dbo'.
                If exists a login to map the drop will not be performed unless you specify this parameter.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Orphan, Database, Security, Login
            Author: Claudio Silva (@ClaudioESSilva)
            Editor: Simone Bizzotto (@niphlod)
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Remove-DbaOrphanUser

        .EXAMPLE
            Remove-DbaOrphanUser -SqlInstance sql2005

            Finds and drops all orphan users without matching Logins in all databases present on server 'sql2005'.

        .EXAMPLE
            Remove-DbaOrphanUser -SqlInstance sqlserver2014a -SqlCredential $cred

            Finds and drops all orphan users without matching Logins in all databases present on server 'sqlserver2014a'. SQL Server authentication will be used in connecting to the server.

        .EXAMPLE
            Remove-DbaOrphanUser -SqlInstance sqlserver2014a -Database db1, db2 -Force

            Finds and drops orphan users even if they have a matching Login on both db1 and db2 databases.

        .EXAMPLE
            Remove-DbaOrphanUser -SqlInstance sqlserver2014a -ExcludeDatabase db1, db2 -Force

            Finds and drops orphan users even if they have a matching Login from all databases except db1 and db2.

        .EXAMPLE
            Remove-DbaOrphanUser -SqlInstance sqlserver2014a -User OrphanUser

            Removes user OrphanUser from all databases only if there is no matching login.

        .EXAMPLE
            Remove-DbaOrphanUser -SqlInstance sqlserver2014a -User OrphanUser -Force

            Removes user OrphanUser from all databases even if they have a matching Login. Any schema that the user owns will change ownership to dbo.

    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [object[]]$User,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {

        foreach ($Instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $Instance."
            try {
                $server = Connect-SqlInstance -SqlInstance $Instance -SqlCredential $SqlCredential
            }
            catch {
                Write-Message -Level Warning -Message "Can't connect to $Instance or access denied. Skipping."
                continue
            }

            $DatabaseCollection = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                $DatabaseCollection = $DatabaseCollection | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $DatabaseCollection = $DatabaseCollection | Where-Object Name -NotIn $ExcludeDatabase
            }

            $CallStack = Get-PSCallStack | Select-Object -Property *
            if ($CallStack.Count -eq 1) {
                $StackSource = $CallStack[0].Command
            }
            else {
                #-2 because index base is 0 and we want the one before the last (the last is the actual command)
                $StackSource = $CallStack[($CallStack.Count - 2)].Command
            }

            if ($DatabaseCollection) {
                foreach ($db in $DatabaseCollection) {
                    try {
                        #if SQL 2012 or higher only validate databases with ContainmentType = NONE
                        if ($server.versionMajor -gt 10) {
                            if ($db.ContainmentType -ne [Microsoft.SqlServer.Management.Smo.ContainmentType]::None) {
                                Write-Message -Level Warning -Message "Database '$db' is a contained database. Contained databases can't have orphaned users. Skipping validation."
                                Continue
                            }
                        }

                        if ($StackSource -eq "Repair-DbaOrphanUser") {
                            Write-Message -Level Verbose -Message "Call origin: Repair-DbaOrphanUser."
                            #Will use collection from parameter ($User)
                        }
                        else {
                            Write-Message -Level Verbose -Message "Validating users on database $db."

                            if ($User.Count -eq 0) {
                                #the third validation will remove from list sql users without login. The rule here is Sid with length higher than 16
                                $User = $db.Users | Where-Object { $_.Login -eq "" -and ($_.ID -gt 4) -and (($_.Sid.Length -gt 16 -and $_.LoginType -eq [Microsoft.SqlServer.Management.Smo.LoginType]::SqlLogin) -eq $false) }
                            }
                            else {

                                #the fourth validation will remove from list sql users without login. The rule here is Sid with length higher than 16
                                $User = $db.Users | Where-Object { $_.Login -eq "" -and ($_.ID -gt 4) -and ($User -contains $_.Name) -and (($_.Sid.Length -gt 16 -and $_.LoginType -eq [Microsoft.SqlServer.Management.Smo.LoginType]::SqlLogin) -eq $false) }

                            }
                        }

                        if ($User.Count -gt 0) {
                            Write-Message -Level Verbose -Message "Orphan users found."
                            foreach ($dbuser in $User) {
                                $SkipUser = $false

                                $ExistLogin = $null

                                if ($StackSource -ne "Repair-DbaOrphanUser") {
                                    #Need to validate Existing Login because the call does not came from Repair-DbaOrphanUser
                                    $ExistLogin = $server.logins | Where-Object {
                                        $_.Isdisabled -eq $False -and
                                        $_.IsSystemObject -eq $False -and
                                        $_.IsLocked -eq $False -and
                                        $_.Name -eq $dbuser.Name
                                    }
                                }

                                #Schemas only appears on SQL Server 2005 (v9.0)
                                if ($server.versionMajor -gt 8) {

                                    #reset variables
                                    $AlterSchemaOwner = ""
                                    $DropSchema = ""

                                    #Validate if user owns any schema
                                    $Schemas = @()
                                    $Schemas = $db.Schemas | Where-Object Owner -eq $dbuser.Name

                                    if (@($Schemas).Count -gt 0) {
                                        Write-Message -Level Verbose -Message "User $dbuser owns one or more schemas."

                                        foreach ($sch in $Schemas) {
                                            <#
                                                On sql server 2008 or lower the EnumObjects method does not accept empty parameter.
                                                0x1FFFFFFF is the way we can say we want everything known by those versions

                                                When it is an higher version we can use empty to get all
                                            #>
                                            if ($server.versionMajor -lt 11) {
                                                $NumberObjects = ($db.EnumObjects(0x1FFFFFFF) | Where-Object { $_.Schema -eq $sch.Name } | Measure-Object).Count
                                            }
                                            else {
                                                $NumberObjects = ($db.EnumObjects() | Where-Object { $_.Schema -eq $sch.Name } | Measure-Object).Count
                                            }

                                            if ($NumberObjects -gt 0) {
                                                if ($Force) {
                                                    Write-Message -Level Verbose -Message "Parameter -Force was used! The schema '$($sch.Name)' have $NumberObjects underlying objects. We will change schema owner to 'dbo' and drop the user."

                                                    if ($Pscmdlet.ShouldProcess($db.Name, "Changing schema '$($sch.Name)' owner to 'dbo'. -Force used.")) {
                                                        $AlterSchemaOwner += "ALTER AUTHORIZATION ON SCHEMA::[$($sch.Name)] TO [dbo]`r`n"

                                                        [pscustomobject]@{
                                                            ComputerName      = $server.ComputerName
                                                            InstanceName      = $server.ServiceName
                                                            SqlInstance       = $server.DomainInstanceName
                                                            DatabaseName      = $db.Name
                                                            SchemaName        = $sch.Name
                                                            Action            = "ALTER OWNER"
                                                            SchemaOwnerBefore = $sch.Owner
                                                            SchemaOwnerAfter  = "dbo"
                                                        }
                                                    }
                                                }
                                                else {
                                                    Write-Message -Level Warning -Message "Schema '$($sch.Name)' owned by user $($dbuser.Name) have $NumberObjects underlying objects. If you want to change the schemas' owner to 'dbo' and drop the user anyway, use -Force parameter. Skipping user '$dbuser'."
                                                    $SkipUser = $true
                                                    break
                                                }
                                            }
                                            else {
                                                if ($sch.Name -eq $dbuser.Name) {
                                                    Write-Message -Level Verbose -Message "The schema '$($sch.Name)' have the same name as user $dbuser. Schema will be dropped."

                                                    if ($Pscmdlet.ShouldProcess($db.Name, "Dropping schema '$($sch.Name)'.")) {
                                                        $DropSchema += "DROP SCHEMA [$($sch.Name)]"

                                                        [pscustomobject]@{
                                                            ComputerName      = $server.ComputerName
                                                            InstanceName      = $server.ServiceName
                                                            SqlInstance       = $server.DomainInstanceName
                                                            DatabaseName      = $db.Name
                                                            SchemaName        = $sch.Name
                                                            Action            = "DROP"
                                                            SchemaOwnerBefore = $sch.Owner
                                                            SchemaOwnerAfter  = "N/A"
                                                        }
                                                    }
                                                }
                                                else {
                                                    Write-Message -Level Warning -Message "Schema '$($sch.Name)' does not have any underlying object. Ownership will be changed to 'dbo' so the user can be dropped. Remember to re-check permissions on this schema!"

                                                    if ($Pscmdlet.ShouldProcess($db.Name, "Changing schema '$($sch.Name)' owner to 'dbo'.")) {
                                                        $AlterSchemaOwner += "ALTER AUTHORIZATION ON SCHEMA::[$($sch.Name)] TO [dbo]`r`n"

                                                        [pscustomobject]@{
                                                            ComputerName      = $server.ComputerName
                                                            InstanceName      = $server.ServiceName
                                                            SqlInstance       = $server.DomainInstanceName
                                                            DatabaseName      = $db.Name
                                                            SchemaName        = $sch.Name
                                                            Action            = "ALTER OWNER"
                                                            SchemaOwnerBefore = $sch.Owner
                                                            SchemaOwnerAfter  = "dbo"
                                                        }
                                                    }
                                                }
                                            }
                                        }

                                    }
                                    else {
                                        Write-Message -Level Verbose -Message "User $dbuser does not own any schema. Will be dropped."
                                    }

                                    $query = "$AlterSchemaOwner `r`n$DropSchema `r`nDROP USER " + $dbuser

                                    Write-Message -Level Debug -Message $query
                                }
                                else {
                                    $query = "EXEC master.dbo.sp_droplogin @loginame = N'$($dbuser.name)'"
                                }

                                if ($ExistLogin) {
                                    if (-not $SkipUser) {
                                        if ($Force) {
                                            if ($Pscmdlet.ShouldProcess($db.Name, "Dropping user $dbuser using -Force")) {
                                                $server.Databases[$db.Name].ExecuteNonQuery($query) | Out-Null
                                                Write-Message -Level Verbose -Message "User $dbuser was dropped from $($db.Name). -Force parameter was used!"
                                            }
                                        }
                                        else {
                                            Write-Message -Level Warning -Message "Orphan user $($dbuser.Name) has a matching login. The user will not be dropped. If you want to drop anyway, use -Force parameter."
                                            Continue
                                        }
                                    }
                                }
                                else {
                                    if (-not $SkipUser) {
                                        if ($Pscmdlet.ShouldProcess($db.Name, "Dropping user $dbuser")) {
                                            $server.Databases[$db.Name].ExecuteNonQuery($query) | Out-Null
                                            Write-Message -Level Verbose -Message "User $dbuser was dropped from $($db.Name)."
                                        }
                                    }
                                }
                            }
                        }
                        else {
                            Write-Message -Level Verbose -Message "No orphan users found on database $db."
                        }
                        #reset collection
                        $User = $null
                    }
                    catch {
                        Stop-Function -Message "Failure" -ErrorRecord $_ -Target $db -Continue
                    }
                }
            }
            else {
                Write-Message -Level Verbose -Message "There are no databases to analyse."
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Remove-SqlOrphanUser
    }
}
function Remove-DbaPfDataCollectorCounter {
    <#
        .SYNOPSIS
            Removes a Performance Data Collector Counter.

        .DESCRIPTION
            Removes a Performance Data Collector Counter.

        .PARAMETER ComputerName
            The target computer. Defaults to localhost.

        .PARAMETER Credential
            Allows you to login to $ComputerName using alternative credentials. To use:

            $cred = Get-Credential, then pass $cred object to the -Credential parameter.

        .PARAMETER CollectorSet
            The name of the Collector Set to search.

        .PARAMETER Collector
            The name of the Collector to remove.
    
        .PARAMETER Counter
            The name of the Counter - in the form of '\Processor(_Total)\% Processor Time'.
    
        .PARAMETER InputObject
            Accepts the object output by Get-DbaPfDataCollectorSet via the pipeline.
    
        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
        
        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
    
        .NOTES
            Tags: PerfMon
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
    
        .LINK
            https://dbatools.io/Remove-DbaPfDataCollectorCounter

        .EXAMPLE
            Remove-DbaPfDataCollectorCounter -ComputerName sql2017 -CollectorSet 'System Correlation' -Collector DataCollector01  -Counter '\LogicalDisk(*)\Avg. Disk Queue Length'
    
            Prompts for confirmation then removes the '\LogicalDisk(*)\Avg. Disk Queue Length' counter within the DataCollector01 collector within the System Correlation collector set on sql2017.
    
        .EXAMPLE
            Get-DbaPfDataCollectorCounter | Out-GridView -PassThru | Remove-DbaPfDataCollectorCounter -Confirm:$false
    
            Allows you to select which counters you'd like on localhost and does not prompt for confirmation.

    #>
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "High")]
    param (
        [DbaInstance[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias("DataCollectorSet")]
        [string[]]$CollectorSet,
        [Alias("DataCollector")]
        [string[]]$Collector,
        [parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [Alias("Name")]
        [object[]]$Counter,
        [parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [switch]$EnableException
    )
    begin {
        $setscript = {
            $setname = $args[0]; $removexml = $args[1]
            $CollectorSet = New-Object -ComObject Pla.DataCollectorSet
            $CollectorSet.SetXml($removexml)
            $CollectorSet.Commit($setname, $null, 0x0003) #add or modify.
            $CollectorSet.Query($setname, $Null)
        }
    }
    process {
        if ($InputObject.Credential -and (Test-Bound -ParameterName Credential -Not)) {
            $Credential = $InputObject.Credential
        }
        
        if (-not $InputObject -or ($InputObject -and (Test-Bound -ParameterName ComputerName))) {
            foreach ($computer in $ComputerName) {
                $InputObject += Get-DbaPfDataCollectorCounter -ComputerName $computer -Credential $Credential -CollectorSet $CollectorSet -Collector $Collector -Counter $Counter
            }
        }
        
        if ($InputObject) {
            if (-not $InputObject.CounterObject) {
                Stop-Function -Message "InputObject is not of the right type. Please use Get-DbaPfDataCollectorCounter."
                return
            }
        }
        
        foreach ($object in $InputObject) {
            $computer = $InputObject.ComputerName
            $null = Test-ElevationRequirement -ComputerName $computer -Continue
            $setname = $InputObject.DataCollectorSet
            $collectorname = $InputObject.DataCollector
            
            $xml = [xml]($InputObject.DataCollectorSetXml)
            
            foreach ($countername in $counter) {
                $node = $xml.SelectSingleNode("//Name[.='$collectorname']").SelectSingleNode("//Counter[.='$countername']")
                $null = $node.ParentNode.RemoveChild($node)
                $node = $xml.SelectSingleNode("//Name[.='$collectorname']").SelectSingleNode("//CounterDisplayName[.='$countername']")
                $null = $node.ParentNode.RemoveChild($node)
            }
            
            $plainxml = $xml.OuterXml
            
            if ($Pscmdlet.ShouldProcess("$computer", "Remove $countername from $collectorname with the $setname collection set")) {
                try {
                    $results = Invoke-Command2 -ComputerName $computer -Credential $Credential -ScriptBlock $setscript -ArgumentList $setname, $plainxml -ErrorAction Stop -Raw
                    Write-Message -Level Verbose -Message " $results"
                    [pscustomobject]@{
                        ComputerName     = $computer
                        DataCollectorSet = $setname
                        DataCollector    = $collectorname
                        Name             = $counterName
                        Status           = "Removed"
                    }
                }
                catch {
                    Stop-Function -Message "Failure importing $Countername to $computer." -ErrorRecord $_ -Target $computer -Continue
                }
            }
        }
    }
}
function Remove-DbaPfDataCollectorSet {
    <#
        .SYNOPSIS
            Removes a Performance Monitor Data Collector Set

        .DESCRIPTION
            Removes a Performance Monitor Data Collector Set. When removing data collector sets from the local instance, Run As Admin is required.

        .PARAMETER ComputerName
            The target computer. Defaults to localhost.

        .PARAMETER Credential
            Allows you to login to $ComputerName using alternative credentials. To use:

            $cred = Get-Credential, then pass $cred object to the -Credential parameter.

        .PARAMETER CollectorSet
            The name of the Collector Set to remove.

        .PARAMETER InputObject
            Accepts the object output by Get-DbaPfDataCollectorSet via the pipeline.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: PerfMon
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Remove-DbaPfDataCollectorSet

        .EXAMPLE
            Remove-DbaPfDataCollectorSet

            Prompts for confirmation then removes all ready Collectors on localhost.

        .EXAMPLE
            Remove-DbaPfDataCollectorSet -ComputerName sql2017 -Confirm:$false

            Attempts to remove all ready Collectors on localhost and does not prompt to confirm.

        .EXAMPLE
            Remove-DbaPfDataCollectorSet -ComputerName sql2017, sql2016 -Credential (Get-Credential) -CollectorSet 'System Correlation'

            Prompts for confirmation then removes the 'System Correlation' Collector on sql2017 and sql2016 using alternative credentials.

        .EXAMPLE
            Get-DbaPfDataCollectorSet -CollectorSet 'System Correlation' | Remove-DbaPfDataCollectorSet

            Removes the 'System Correlation' Collector.

        .EXAMPLE
            Get-DbaPfDataCollectorSet -CollectorSet 'System Correlation' | Stop-DbaPfDataCollectorSet | Remove-DbaPfDataCollectorSet

            Stops and removes the 'System Correlation' Collector.
    #>
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "High")]
    param (
        [DbaInstance[]]$ComputerName=$env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias("DataCollectorSet")]
        [string[]]$CollectorSet,
        [parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [switch]$EnableException
    )
    begin {
        $setscript = {
            $setname = $args
            $collectorset = New-Object -ComObject Pla.DataCollectorSet
            $collectorset.Query($setname, $null)
            if ($collectorset.name -eq $setname) {
                $null = $collectorset.Delete()
            }
            else {
                Write-Warning "Data Collector Set $setname does not exist on $env:COMPUTERNAME."
            }
        }
    }
    process {
        if (-not $InputObject -or ($InputObject -and (Test-Bound -ParameterName ComputerName))) {
            foreach ($computer in $ComputerName) {
                $InputObject += Get-DbaPfDataCollectorSet -ComputerName $computer -Credential $Credential -CollectorSet $CollectorSet
            }
        }

        if ($InputObject) {
            if (-not $InputObject.DataCollectorSetObject) {
                Stop-Function -Message "InputObject is not of the right type. Please use Get-DbaPfDataCollectorSet."
                return
            }
        }

        # Check to see if its running first
        foreach ($set in $InputObject) {
            $setname = $set.Name
            $computer = $set.ComputerName
            $status = $set.State

            $null = Test-ElevationRequirement -ComputerName $computer -Continue

            Write-Message -Level Verbose -Message "$setname on $ComputerName is $status."

            if ($status -eq "Running") {
                Stop-Function -Message "$setname on $computer is running. Use Stop-DbaPfDataCollectorSet to stop first." -Continue
            }

            if ($Pscmdlet.ShouldProcess("$computer", "Removing collector set $setname")) {
                Write-Message -Level Verbose -Message "Connecting to $computer using Invoke-Command."
                try {
                    Invoke-Command2 -ComputerName $computer -Credential $Credential -ScriptBlock $setscript -ArgumentList $setname -ErrorAction Stop
                    [pscustomobject]@{
                        ComputerName = $computer
                        Name         = $setname
                        Status       = "Removed"
                    }
                }
                catch {
                    Stop-Function -Message "Failure Removing $setname on $computer." -ErrorRecord $_ -Target $computer -Continue
                }
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Remove-DbaRegisteredServer {
    <#
        .SYNOPSIS
            Removes registered servers found in SQL Server Central Management Server (CMS).

        .DESCRIPTION
            Removes registered servers found in SQL Server Central Management Server (CMS).

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Name
            Specifies one or more names to include. Name is the visible name in SSMS CMS interface (labeled Registered Server Name)

        .PARAMETER ServerName
            Specifies one or more server names to include. Server Name is the actual instance name (labeled Server Name)

        .PARAMETER Group
            Specifies one or more groups to include from SQL Server Central Management Server.

        .PARAMETER InputObject
            Allows results from Get-DbaRegisteredServer to be piped in

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.

            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.

            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Chrissy LeMaire (@cl)
            Tags: RegisteredServer, CMS

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Remove-DbaRegisteredServer

        .EXAMPLE
            Remove-DbaRegisteredServer -SqlInstance sql2012 -Group HR, Accounting

            Removes all servers from the HR and Accounting groups on sql2012

        .EXAMPLE
            Remove-DbaRegisteredServer -SqlInstance sql2012 -Group HR\Development

            Removes all servers from the HR and sub-group Development from the CMS on sql2012.

        .EXAMPLE
            Remove-DbaRegisteredServer -SqlInstance sql2012 -Confirm:$false

            Removes all registered servers on sql2012 and turns off all prompting
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$Name,
        [string[]]$ServerName,
        [string[]]$Group,
        [parameter(ValueFromPipeline)]
        [Microsoft.SqlServer.Management.RegisteredServers.RegisteredServer[]]$InputObject,
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            $InputObject += Get-DbaRegisteredServer -SqlInstance $instance -SqlCredential $SqlCredential -Group $Group -ExcludeGroup $ExcludeGroup -Name $Name -ServerName $ServerName
        }

        foreach ($regserver in $InputObject) {
            $server = $regserver.Parent
            
            if ($Pscmdlet.ShouldProcess($regserver.Parent, "Removing $regserver")) {
                $null = $regserver.Drop()
                Disconnect-RegServer -Server $server
                
                try {
                    [pscustomobject]@{
                        ComputerName = $regserver.ComputerName
                        InstanceName = $regserver.InstanceName
                        SqlInstance  = $regserver.SqlInstance
                        Name         = $regserver.Name
                        ServerName   = $regserver.ServerName
                        Status       = "Dropped"
                    }
                }
                catch {
                    Stop-Function -Message "Failed to drop $regserver on $server" -ErrorRecord $_ -Continue
                }
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Remove-DbaRegisteredServerGroup {
    <#
        .SYNOPSIS
            Gets list of Server Groups objects stored in SQL Server Central Management Server (CMS).

        .DESCRIPTION
            Returns an array of Server Groups found in the CMS.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Name
            Specifies one or more groups to include from SQL Server Central Management Server.

        .PARAMETER InputObject
            Allows results from Get-DbaRegisteredServerGroup to be piped in

        .PARAMETER Id
            Get group by Id(s)

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.

            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.

            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Chrissy LeMaire (@cl)
            Tags: RegisteredServer, CMS

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Remove-DbaRegisteredServerGroup

        .EXAMPLE
            Remove-DbaRegisteredServerGroup -SqlInstance sql2012 -Group HR, Accounting

            Removes the HR and Accounting groups on sql2012

        .EXAMPLE
            Remove-DbaRegisteredServerGroup -SqlInstance sql2012 -Group HR\Development -Confirm:$false

            Removes the Development subgroup within the HR group on sql2012 and turns off all prompting

    #>
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Group")]
        [string[]]$Name,
        [parameter(ValueFromPipeline)]
        [Microsoft.SqlServer.Management.RegisteredServers.ServerGroup[]]$InputObject,
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            $InputObject += Get-DbaRegisteredServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Group $Name
        }

        foreach ($regservergroup in $InputObject) {
            $parentserver = Get-RegServerParent -InputObject $regservergroup

            if ($null -eq $parentserver) {
                Stop-Function -Message "Something went wrong and it's hard to explain, sorry. This basically shouldn't happen." -Continue
            }

            if ($Pscmdlet.ShouldProcess($parentserver.DomainInstanceName, "Removing $($regservergroup.Name) CMS Group")) {
                $null = $parentserver.ServerConnection.ExecuteNonQuery($regservergroup.ScriptDrop().GetScript())
                $parentserver.ServerConnection.Disconnect()
                try {
                    [pscustomobject]@{
                        ComputerName            = $parentserver.ComputerName
                        InstanceName            = $parentserver.InstanceName
                        SqlInstance             = $parentserver.SqlInstance
                        Name                    = $regservergroup.Name
                        Status                  = "Dropped"
                    }
                }
                catch {
                    Stop-Function -Message "Failed to drop $regservergroup on $parentserver" -ErrorRecord $_ -Continue
                }
            }
        }
    }
}
#ValidationTags#FlowControl,Pipeline#
function Remove-DbaSpn {
    <#
.SYNOPSIS
Removes an SPN for a given service account in active directory and also removes delegation to the same SPN, if found

.DESCRIPTION
This function will connect to Active Directory and search for an account. If the account is found, it will attempt to remove the specified SPN. Once the SPN is removed, the function will also remove delegation to that service.

In order to run this function, the credential you provide must have write access to Active Directory.

Note: This function supports -WhatIf

.PARAMETER SPN
The SPN you want to remove

.PARAMETER ServiceAccount
The account you want the SPN remove from

.PARAMETER Credential
The credential you want to use to connect to Active Directory to make the changes

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.PARAMETER Confirm
Turns confirmations before changes on or off

.PARAMETER WhatIf
Shows what would happen if the command was executed

.NOTES
Tags: SPN
Author: Drew Furgiuele (@pittfurg), http://www.port1433.com

dbatools PowerShell module (https://dbatools.io)
Copyright (C) 2016 Chrissy LeMaire
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Remove-DbaSpn

.EXAMPLE
Remove-DbaSpn -SPN MSSQLSvc\SQLSERVERA.domain.something -ServiceAccount domain\account

Connects to Active Directory and removes a provided SPN from the given account (and also the relative delegation)

.EXAMPLE
Remove-DbaSpn -SPN MSSQLSvc\SQLSERVERA.domain.something -ServiceAccount domain\account -EnableException

Connects to Active Directory and removes a provided SPN from the given account, suppressing all error messages and throw exceptions that can be caught instead

.EXAMPLE
Remove-DbaSpn -SPN MSSQLSvc\SQLSERVERA.domain.something -ServiceAccount domain\account -Credential (Get-Credential)

Connects to Active Directory and removes a provided SPN to the given account. Uses alternative account to connect to AD.

.EXAMPLE
Test-DbaSpn -ComputerName sql2005 | Where { $_.isSet -eq $true } | Remove-DbaSpn -WhatIf

Shows what would happen trying to remove all set SPNs for sql2005 and the relative delegations

.EXAMPLE
Test-DbaSpn -ComputerName sql2005 | Where { $_.isSet -eq $true } | Remove-DbaSpn

Removes all set SPNs for sql2005 and the relative delegations


#>
    [cmdletbinding(SupportsShouldProcess = $true, DefaultParameterSetName = "Default")]
    param (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName)]
        [Alias("RequiredSPN")]
        [string]$SPN,
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName)]
        [Alias("InstanceServiceAccount", "AccountName")]
        [string]$ServiceAccount,
        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName)]
        [PSCredential]$Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        Write-Message -Message "Looking for account $ServiceAccount..." -Level Verbose
        $searchfor = 'User'
        if ($ServiceAccount.EndsWith('$')) {
            $searchfor = 'Computer'
        }
        try {
            $Result = Get-DbaADObject -ADObject $ServiceAccount -Type $searchfor -Credential $Credential -EnableException
        }
        catch {
            Stop-Function -Message "AD lookup failure. This may be because the domain cannot be resolved for the SQL Server service account ($ServiceAccount). $($_.Exception.Message)" -EnableException $EnableException -InnerErrorRecord $_ -Target $ServiceAccount
        }
        if ($Result.Count -gt 0) {
            try {
                $adentry = $Result.GetUnderlyingObject()
            }
            catch {
                Stop-Function -Message "The SQL Service account ($ServiceAccount) has been found, but you don't have enough permission to inspect its properties $($_.Exception.Message)" -EnableException $EnableException -InnerErrorRecord $_ -Target $ServiceAccount
            }
        }
        else {
            Stop-Function -Message "The SQL Service account ($ServiceAccount) has not been found" -EnableException $EnableException -Target $ServiceAccount
        }

        # Cool! Remove an SPN
        $delegate = $true
        $spnadobject = $adentry.Properties['servicePrincipalName']

        if ($spnadobject -notcontains $spn) {
            Write-Message -Level Warning -Message "SPN $SPN not found"
            $status = "SPN not found"
            $set = $false
        }

        if ($PSCmdlet.ShouldProcess("$spn", "Removing SPN for service account")) {
            try {
                if ($spnadobject -contains $spn) {
                    $null = $spnadobject.Remove($spn)
                    $adentry.CommitChanges()
                    Write-Message -Message "Remove SPN $spn for $serviceaccount" -Level Verbose
                    $set = $false
                    $status = "Successfully removed SPN"
                }
            }
            catch {
                Write-Message -Message "Could not remove SPN. $($_.Exception.Message)" -Level Warning -EnableException $EnableException.ToBool() -ErrorRecord $_ -Target $ServiceAccountWrite
                $set = $true
                $status = "Failed to remove SPN"
                $delegate = $false
            }

            [pscustomobject]@{
                Name           = $spn
                ServiceAccount = $ServiceAccount
                Property       = "servicePrincipalName"
                IsSet          = $set
                Notes          = $status
            }
        }
        # if we removed the SPN, we should clean up also the delegation
        if ($PSCmdlet.ShouldProcess("$spn", "Removing delegation for service account for SPN")) {
            # if we didn't remove the SPN we shouldn't do anything
            if ($delegate) {
                # even if we removed the SPN, delegation could have been not set at all. We should not raise an error
                if ($adentry.Properties['msDS-AllowedToDelegateTo'] -notcontains $spn) {
                    [pscustomobject]@{
                        Name           = $spn
                        ServiceAccount = $ServiceAccount
                        Property       = "msDS-AllowedToDelegateTo"
                        IsSet          = $false
                        Notes          = "Delegation not found"
                    }
                }
                else {
                    # we indeed need the cleanup
                    try {
                        $null = $adentry.Properties['msDS-AllowedToDelegateTo'].Remove($spn)
                        $adentry.CommitChanges()
                        Write-Message -Message "Removed kerberos delegation $spn for $ServiceAccount" -Level Verbose
                        $set = $false
                        $status = "Successfully removed delegation"
                    }
                    catch {
                        Write-Message -Message "Could not remove delegation. $($_.Exception.Message)" -Level Warning -EnableException $EnableException.ToBool() -ErrorRecord $_ -Target $ServiceAccount
                        $set = $true
                        $status = "Failed to remove delegation"
                    }

                    [pscustomobject]@{
                        Name           = $spn
                        ServiceAccount = $ServiceAccount
                        Property       = "msDS-AllowedToDelegateTo"
                        IsSet          = $set
                        Notes          = $status
                    }
                }
            }

        }
    }
}
function Remove-DbaTrace {
     <#
        .SYNOPSIS
        Stops and closes the specified trace and deletes its definition from the server.

        .DESCRIPTION
        Stops and closes the specified trace and deletes its definition from the server.

        .PARAMETER SqlInstance
        The target SQL Server instance

        .PARAMETER SqlCredential
        Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Id
        A list of trace ids

        .PARAMETER InputObject
        Internal parameter for piping

        .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
        Tags: Security, Trace
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

       .EXAMPLE
        Remove-DbaTrace -SqlInstance sql2008

        Stops and removes all traces on sql2008

        .EXAMPLE
        Remove-DbaTrace -SqlInstance sql2008 -Id 1

        Stops and removes all trace with ID 1 on sql2008

        .EXAMPLE
        Get-DbaTrace -SqlInstance sql2008 | Out-GridView -PassThru | Remove-DbaTrace

        Stops and removes selected traces on sql2008

#>
    [CmdletBinding()]
    Param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [int[]]$Id,
        [parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [switch]$EnableException
    )
    process {
        if (-not $InputObject -and $SqlInstance) {
            $InputObject = Get-DbaTrace -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Id $Id
        }

        foreach ($trace in $InputObject) {
            if (-not $trace.id -and -not $trace.Parent) {
                Stop-Function -Message "Input is of the wrong type. Use Get-DbaTrace." -Continue
                return
            }

            $server = $trace.Parent
            $traceid = $trace.id
            $default = Get-DbaTrace -SqlInstance $server -Default

            if ($default.id -eq $traceid) {
                Stop-Function -Message "The default trace on $server cannot be stopped. Use Set-DbaSpConfigure to turn it off." -Continue
            }

            $stopsql = "sp_trace_setstatus $traceid, 0"
            $removesql = "sp_trace_setstatus $traceid, 2"

            try {
                $server.Query($stopsql)
                if (Get-DbaTrace -SqlInstance $server -Id $traceid) {
                    $server.Query($removesql)
                }
                [pscustomobject]@{
                    ComputerName      = $server.ComputerName
                    InstanceName      = $server.ServiceName
                    SqlInstance       = $server.DomainInstanceName
                    Id                = $traceid
                    Status            = "Stopped, closed and deleted"
                }
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_ -Target $server -Continue
                return
            }
        }
    }
}
function Remove-DbaXESession {
    <#
        .SYNOPSIS
            Removes Extended Events sessions.

        .DESCRIPTION
            This script removes Extended Events sessions on a SQL Server instance.

        .PARAMETER SqlInstance
            Target SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Session
            Specifies a list of Extended Events sessions to remove.

        .PARAMETER AllSessions
            If this switch is enabled, all Extended Events sessions will be removed except the packaged sessions AlwaysOn_health, system_health, telemetry_xevents.

        .PARAMETER InputObject
            Accepts a collection of XEsession objects as output by Get-DbaXESession.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Remove-DbaXESession

        .EXAMPLE
            Remove-DbaXESession -SqlInstance sql2012 -AllSessions

            Removes all Extended Event Session on the sqlserver2014 instance.

        .EXAMPLE
            Remove-DbaXESession -SqlInstance sql2012 -Session xesession1,xesession2

            Removes the xesession1 and xesession2 Extended Event sessions.

        .EXAMPLE
            Get-DbaXESession -SqlInstance sql2017 | Remove-DbaXESession -Confirm:$false

            Removes all sessions from sql2017, bypassing prompts.

        .EXAMPLE
            Get-DbaXESession -SqlInstance sql2012 -Session xesession1 | Remove-DbaXESession

            Removes the sessions returned from the Get-DbaXESession function.
    #>
    [CmdletBinding(DefaultParameterSetName = 'Session', SupportsShouldProcess, ConfirmImpact = 'High')]
    param (
        [parameter(Position = 1, Mandatory, ParameterSetName = 'Session')]
        [parameter(Position = 1, Mandatory, ParameterSetName = 'All')]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [parameter(ParameterSetName = 'Session')]
        [parameter(ParameterSetName = 'All')]
        [PSCredential]$SqlCredential,
        [parameter(Mandatory, ParameterSetName = 'Session')]
        [Alias("Sessions")]
        [object[]]$Session,
        [parameter(Mandatory, ParameterSetName = 'All')]
        [switch]$AllSessions,
        [parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Object')]
        [Microsoft.SqlServer.Management.XEvent.Session[]]$InputObject,
        [switch]$EnableException
    )

    begin {
        # Remove each XESession
        function Remove-XESessions {
            [CmdletBinding()]
            param ([Microsoft.SqlServer.Management.XEvent.Session[]]$xeSessions)

            foreach ($xe in $xeSessions) {
                $instance = $xe.Parent.Name
                $session = $xe.Name

                if ($Pscmdlet.ShouldProcess("$instance", "Removing XEvent Session $session")) {
                    try {
                        $xe.Drop()
                        [pscustomobject]@{
                            ComputerName = $xe.Parent.ComputerName
                            InstanceName = $xe.Parent.ServiceName
                            SqlInstance  = $xe.Parent.DomainInstanceName
                            Session      = $session
                            Status       = "Removed"
                        }
                    }
                    catch {
                        Stop-Function -Message "Could not remove XEvent Session on $instance" -Target $session -ErrorRecord $_ -Continue
                    }
                }
            }
        }
    }

    process {
        if ($InputObject) {
            # avoid the collection issue
            $sessions = Get-DbaXESession -SqlInstance $InputObject.Parent -Session $InputObject.Name
            foreach ($item in $sessions) {
                Remove-XESessions $item
            }
        }
        else {
            foreach ($instance in $SqlInstance) {
                $xeSessions = Get-DbaXESession -SqlInstance $instance -SqlCredential $SqlCredential

                # Filter xeSessions based on parameters
                if ($Session) {
                    $xeSessions = $xeSessions | Where-Object { $_.Name -in $Session }
                }
                elseif ($AllSessions) {
                    $systemSessions = @('AlwaysOn_health', 'system_health', 'telemetry_xevents')
                    $xeSessions = $xeSessions | Where-Object { $_.Name -notin $systemSessions }
                }

                Remove-XESessions $xeSessions
            }
        }
    }
}
function Remove-DbaXESmartTarget {
    <#
        .SYNOPSIS
           Removes an XESmartTarget PowerShell Job.

        .DESCRIPTION
           Removes an XESmartTarget PowerShell Job.

        .PARAMETER InputObject
           Specifies one or more XESmartTarget job objects as output by Get-DbaXESmartTarget.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
            SmartTarget: by Gianluca Sartori (@spaghettidba)

        .LINK
            https://dbatools.io/Remove-DbaXESmartTarget
            https://github.com/spaghettidba/XESmartTarget/wiki

        .EXAMPLE
            Get-DbaXESmartTarget | Remove-DbaXESmartTarget

            Removes all XESmartTarget jobs.

        .EXAMPLE
            Get-DbaXESmartTarget | Where-Object Id -eq 2 | Remove-DbaXESmartTarget

            Removes a specific XESmartTarget job.
    #>
    [CmdletBinding(SupportsShouldProcess)]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [object[]]$InputObject,
        [switch]$EnableException
    )
    process {
        if ($Pscmdlet.ShouldProcess("localhost", "Removing job $id")) {
            try {
                $id = $InputObject.Id
                Write-Message -Level Output -Message "Removing job $id, this may take a couple minutes."
                Get-Job -ID $InputObject.Id | Remove-Job -Force
                Write-Message -Level Output -Message "Successfully removed $id."
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_
            }
        }
    }
}
function Rename-DbaDatabase {
    <#
        .SYNOPSIS
            Changes database name, logical file names, file group names and physical file names (optionally handling the move). BETA VERSION.

        .DESCRIPTION
            Can change every database metadata that can be renamed.
            The ultimate goal is choosing to have a default template to enforce in your environment
            so your naming convention for every bit can be put in place in no time.
            The process is as follows (it follows the hierarchy of the entities):
                - database name is changed (optionally, forcing users out)
                - filegroup name(s) are changed accordingly
                - logical name(s) are changed accordingly
                - physical file(s) are changed accordingly
                    - if Move is specified, the database will be taken offline and the move will initiate, then it will be taken online
                    - if Move is not specified, the database remains online (unless SetOffline), and you are in charge of moving files
            If any of the above fails, the process stops.
            Please take a backup of your databases BEFORE using this, and remember to backup AFTER (also a FULL backup of master)

            It returns an object for each database with all the renames done, plus hidden properties showing a "human" representation of them.

            It's better you store the resulting object in a variable so you can inspect it in case of issues, e.g. "$result = Rename-DbaDatabase ....."

            To get a grasp without worrying of what would happen under the hood, use "Rename-DbaDatabase .... -Preview | Select-Object *"

        .PARAMETER SqlInstance
            Target any number of instances, in order to return their build state.

        .PARAMETER SqlCredential
            When connecting to an instance, use the credentials specified.

        .PARAMETER Database
            Targets only specified databases

        .PARAMETER ExcludeDatabase
            Excludes only specified databases

        .PARAMETER AllDatabases
            If you want to apply the naming convention system wide, you need to pass this parameter

        .PARAMETER DatabaseName
            Pass a template to rename the database name. Valid placeholders are:
                - <DBN> current database name
                - <DATE> date (yyyyMMdd)

        .PARAMETER FileGroupName
            Pass a template to rename file group name. Valid placeholders are:
                - <FGN> current filegroup name
                - <DBN> current database name
                - <DATE> date (yyyyMMdd)
            If distinct names cannot be generated, a counter will be appended (0001, 0002, 0003, etc)

        .PARAMETER LogicalName
            Pass a template to rename logical name. Valid placeholders are:
                - <FT> file type (ROWS, LOG)
                - <LGN> current logical name
                - <FGN> current filegroup name
                - <DBN> current database name
                - <DATE> date (yyyyMMdd)
            If distinct names cannot be generated, a counter will be appended (0001, 0002, 0003, etc)

        .PARAMETER FileName
            Pass a template to rename file name. Valid placeholders are:
                - <FNN> current file name (the basename, without directory nor extension)
                - <FT> file type (ROWS, LOG, MMO, FS)
                - <LGN> current logical name
                - <FGN> current filegroup name
                - <DBN> current database name
                - <DATE> date (yyyyMMdd)
            If distinct names cannot be generated, a counter will be appended (0001, 0002, 0003, etc)

        .PARAMETER ReplaceBefore
            If you pass this switch, all upper level "current names" will be inspected and replaced BEFORE doing the
            rename according to the template in the current level (remember the hierarchy):
            Let's say you have a database named "dbatools_HR", composed by 3 files
                - dbatools_HR_Data.mdf
                - dbatools_HR_Index.ndf
                - dbatools_HR_log.ldf
            Rename-DbaDatabase .... -Database "dbatools_HR" -DatabaseName "dbatools_HRARCHIVE" -FileName '<DBN><FNN>'
            would end up with this logic:
            - database --> no placeholders specified
                - dbatools_HR to dbatools_HRARCHIVE
                    - filenames placeholders specified
                        <DBN><FNN> --> current database name + current filename"
                            - dbatools_HR_Data.mdf to dbatools_HRARCHIVEdbatools_HR_Data.mdf
                            - dbatools_HR_Index.mdf to dbatools_HRARCHIVEdbatools_HR_Data.mdf
                            - dbatools_HR_log.ldf to dbatools_HRARCHIVEdbatools_HR_log.ldf
            Passing this switch, instead, e.g.
            Rename-DbaDatabase .... -Database "dbatools_HR" -DatabaseName "dbatools_HRARCHIVE" -FileName '<DBN><FNN>' -ReplaceBefore
            end up with this logic instead:
            - database --> no placeholders specified
                - dbatools_HR to dbatools_HRARCHIVE
                    - filenames placeholders specified,
                        <DBN><FNN>, plus -ReplaceBefore --> current database name + replace OLD "upper level" names inside the current filename
                        - dbatools_HR_Data.mdf to dbatools_HRARCHIVE_Data.mdf
                        - dbatools_HR_Index.mdf to dbatools_HRARCHIVE_Data.mdf
                        - dbatools_HR_log.ldf to dbatools_HRARCHIVE_log.ldf

        .PARAMETER Force
            Kills any open session to be able to do renames.

        .PARAMETER SetOffline
            Kills any open session and sets the database offline to be able to move files

        .PARAMETER Move
            If you want this function to move files, else you're the one in charge of it.
            This enables the same functionality as SetOffline, killing open transactions and putting the database
            offline, then do the actual rename and setting it online again afterwards

        .PARAMETER Preview
            Shows the renames without performing any operation (recommended to find your way around this function parameters ;-) )

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER InputObject
            Accepts piped database objects

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database, Rename
            Author: niphlod

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Rename-DbaDatabase

        .EXAMPLE
            Rename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -DatabaseName HR2 | select *

            Shows the detailed resultset you'll get renaming the HR database to HR2 without doing anything

        .EXAMPLE
            Rename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -DatabaseName HR2

            Renames the HR database to HR2

        .EXAMPLE
            Get-DbaDatabase -SqlInstance sqlserver2014a -Database HR | Rename-DbaDatabase -DatabaseName HR2

            Same as before, but with a piped database (renames the HR database to HR2)

        .EXAMPLE
            Rename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -DatabaseName "dbatools_<DBN>"

            Renames the HR database to dbatools_HR

        .EXAMPLE
            Rename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -DatabaseName "dbatools_<DBN>_<DATE>"

            Renames the HR database to dbatools_HR_20170807 (if today is 07th Aug 2017)

        .EXAMPLE
            Rename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -FileGroupName "dbatools_<FGN>"

            Renames every FileGroup within HR to "dbatools_[the original FileGroup name]"

        .EXAMPLE
            Rename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -DatabaseName "dbatools_<DBN>" -FileGroupName "<DBN>_<FGN>"

            Renames the HR database to "dbatools_HR", then renames every FileGroup within to "dbatools_HR_[the original FileGroup name]"
            Note the "default recursive behaviour" here: for all intents and purposes the result of the former can be obtained with two distinct calls:
            Rename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -FileGroupName "dbatools_<DBN>_<FGN>"
            Rename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -DatabaseName "dbatools_<DBN>"

        .EXAMPLE
            Rename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -DatabaseName "dbatools_<DBN>" -FileName "<DBN>_<FGN>_<FNN>"

            Renames the HR database to "dbatools_HR" and then all filenames as "dbatools_HR_[Name of the FileGroup]_[original_filename]"
            The db stays online (watch out!). You can then proceed manually to move/copy files by hand, set the db offline and then online again to finish the rename process

        .EXAMPLE
            Rename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -DatabaseName "dbatools_<DBN>" -FileName "<DBN>_<FGN>_<FNN>" -SetOffline

            Renames the HR database to "dbatools_HR" and then all filenames as "dbatools_HR_[Name of the FileGroup]_[original_filename]"
            The db is then set offline (watch out!). You can then proceed manually to move/copy files by hand and then set it online again to finish the rename process

        .EXAMPLE
            Rename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -DatabaseName "dbatools_<DBN>" -FileName "<DBN>_<FGN>_<FNN>" -Move

            Renames the HR database to "dbatools_HR" and then all filenames as "dbatools_HR_[Name of the FileGroup]_[original_filename]"
            The db is then set offline (watch out!). The function tries to do a simple rename and then sets the db online again to finish the rename process
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    Param (
        [parameter(Mandatory, ParameterSetName = "Server")]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [parameter(ParameterSetName = "Server")]
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$AllDatabases,
        [string]$DatabaseName,
        [string]$FileGroupName,
        [string]$LogicalName,
        [string]$FileName,
        [switch]$ReplaceBefore,
        [switch]$Force,
        [switch]$Move,
        [switch]$SetOffline,
        [switch]$Preview,
        [parameter(Mandatory, ValueFromPipeline, ParameterSetName = "Pipe")]
        [Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $CurrentDate = Get-Date -Format 'yyyyMMdd'

        function Get-DbaNameStructure($database) {
            $obj = @()
            # db name
            $obj += "- Database : $database"
            # FileGroups
            foreach ($fg in $database.FileGroups) {
                $obj += "  - FileGroup: $($fg.Name)"
                # LogicalNames
                foreach ($ln in $fg.Files) {
                    $obj += "    - Logical: $($ln.Name)"
                    $obj += "      - FileName: $($ln.FileName)"
                }
            }
            $obj += "  - Logfiles"
            foreach ($log in $database.LogFiles) {
                $obj += "    - Logical: $($log.Name)"
                $obj += "      - FileName: $($log.FileName)"
            }
            return $obj -Join "`n"
        }


        function Get-DbaKeyByValue($hashtable, $Value) {
            ($hashtable.GetEnumerator() | Where-Object Value -eq $Value).Name
        }

        if ((Test-Bound -ParameterName SetOffline) -and (-not(Test-Bound -ParameterName FileName))) {
            Stop-Function -Category InvalidArgument -Message "-SetOffline is only useful when -FileName is passed. Quitting."
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }
        if (!$Database -and !$AllDatabases -and !$InputObject -and !$ExcludeDatabase) {
            Stop-Function -Message "You must specify a -AllDatabases or -Database/ExcludeDatabase to continue"
            return
        }
        if (!$DatabaseName -and !$FileGroupName -and !$LogicalName -and !$FileName) {
            Stop-Function -Message "You must specify at least one of -DatabaseName,-FileGroupName,-LogicalName or -Filename to continue"
            return
        }
        $dbs = @()
        if ($InputObject) {
            if ($InputObject.Name) {
                # comes from Get-DbaDatabase
                $dbs += $InputObject
            }
        }
        else {
            foreach ($instance in $SqlInstance) {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                try {
                    $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlCredential
                }
                catch {
                    Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                }
                $all_dbs = $server.Databases | Where-Object IsAccessible
                $dbs += $all_dbs | Where-Object { @('master', 'model', 'msdb', 'tempdb', 'distribution') -notcontains $_.Name }
                if ($Database) {
                    $dbs = $dbs | Where-Object { $Database -contains $_.Name }
                }
                if ($ExcludeDatabase) {
                    $dbs = $dbs | Where-Object { $ExcludeDatabase -notcontains $_.Name }
                }
            }
        }

        # holds all dbs per instance to avoid naming clashes
        $InstanceDbs = @{}

        # holds all db file enumerations (used for -Move only)
        $InstanceFiles = @{}

        #region db loop
        foreach ($db in $dbs) {
            # used to stop futher operations on database
            $failed = $false

            # pending renames initialized at db level
            $Pending_Renames = @()

            $Entities_Before = @{}

            $server = $db.Parent
            if ($db.Name -in @('master', 'model', 'msdb', 'tempdb', 'distribution')) {
                Write-Message -Level Warning -Message "Database $($db.Name) is a system one, skipping..."
                continue
            }
            if (!$db.IsAccessible) {
                Write-Message -Level Warning -Message "Database $($db.Name) is not accessible, skipping..."
                continue
            }
            if ($db.IsMirroringEnabled -eq $true -or $db.AvailabilityGroupName.Length -gt 0) {
                Write-Message -Level Warning -Message "Database $($db.Name) is either mirrored or in an AG, skipping..."
                continue
            }
            $Server_Id = $server.DomainInstanceName
            if ( !$InstanceDbs.ContainsKey($Server_Id) ) {
                $InstanceDbs[$Server_Id] = @{}
                foreach ($dn in $server.Databases.Name) {
                    $InstanceDbs[$Server_Id][$dn] = 1
                }
            }

            $Entities_Before['DBN'] = @{}
            $Entities_Before['FGN'] = @{}
            $Entities_Before['LGN'] = @{}
            $Entities_Before['FNN'] = @{}
            $Entities_Before['DBN'][$db.Name] = $db.Name
            #region databasename
            if ($DatabaseName) {
                $Orig_DBName = $db.Name
                # fixed replacements
                $NewDBName = $DatabaseName.Replace('<DBN>', $Orig_DBName).Replace('<DATE>', $CurrentDate)
                if ($Orig_DBName -eq $NewDBName) {
                    Write-Message -Level VeryVerbose -Message "Database name unchanged, skipping"
                }
                else {
                    if ($InstanceDbs[$Server_Id].ContainsKey($NewDBName)) {
                        Write-Message -Level Warning -Message "Database $NewDBName exists already, skipping this rename"
                        $failed = $true
                    }
                    else {
                        if ($PSCmdlet.ShouldProcess($db, "Renaming Database $db to $NewDBName")) {
                            if ($Force) {
                                $server.KillAllProcesses($Orig_DBName)
                            }
                            try {
                                if (!$Preview) {
                                    $db.Rename($NewDBName)
                                }
                                $InstanceDbs[$Server_Id].Remove($Orig_DBName)
                                $InstanceDbs[$Server_Id][$NewDBName] = 1
                                $Entities_Before['DBN'][$Orig_DBName] = $NewDBName
                                #$db.Refresh()
                            }
                            catch {
                                Stop-Function -Message "Failed to rename Database : $($_.Exception.InnerException.InnerException.InnerException)" -ErrorRecord $_ -Target $server.DomainInstanceName -OverrideExceptionMessage
                                # stop any further renames
                                $failed = $true
                            }
                        }
                    }
                }
            }
            #endregion databasename
            #region filegroupname
            if ($ReplaceBefore) {
                #backfill PRIMARY
                $Entities_Before['FGN']['PRIMARY'] = 'PRIMARY'
                foreach ($fg in $db.FileGroups.Name) {
                    $Entities_Before['FGN'][$fg] = $fg
                }
            }

            if (!$failed -and $FileGroupName) {
                $Editable_FGs = $db.FileGroups | Where-Object Name -ne 'PRIMARY'
                $New_FGNames = @{}
                foreach ($fg in $db.FileGroups.Name) {
                    $New_FGNames[$fg] = 1
                }
                $FGCounter = 0
                foreach ($fg in $Editable_FGs) {
                    $Orig_FGName = $fg.Name
                    $Orig_Placeholder = $Orig_FGName
                    if ($ReplaceBefore) {
                        # at Filegroup level, we need to worry about database name
                        $Orig_Placeholder = $Orig_Placeholder.Replace($Entities_Before['DBN'][$Orig_DBName], '')
                    }
                    $NewFGName = $FileGroupName.Replace('<DBN>', $Entities_Before['DBN'][$db.Name]).Replace('<DATE>', $CurrentDate).Replace('<FGN>', $Orig_Placeholder)
                    $FinalFGName = $NewFGName
                    while ($fg.Name -ne $FinalFGName) {
                        if ($FinalFGName -in $New_FGNames.Keys) {
                            $FGCounter += 1
                            $FinalFGName = "$NewFGName$($FGCounter.ToString('000'))"
                        }
                        else {
                            break
                        }
                    }
                    if ($fg.Name -eq $FinalFGName) {
                        Write-Message -Level VeryVerbose -Message "No rename necessary for FileGroup $($fg.Name) (on $db)"
                        continue
                    }
                    if ($PSCmdlet.ShouldProcess($db, "Renaming FileGroup $($fg.Name) to $FinalFGName")) {
                        try {
                            if (!$Preview) {
                                $fg.Rename($FinalFGName)
                            }
                            $New_FGNames.Remove($Orig_FGName)
                            $New_FGNames[$FinalFGName] = 1
                            $Entities_Before['FGN'][$Orig_FGName] = $FinalFGName
                        }
                        catch {
                            Stop-Function -Message "Failed to rename FileGroup : $($_.Exception.InnerException.InnerException.InnerException)" -ErrorRecord $_ -Target $server.DomainInstanceName -OverrideExceptionMessage
                            # stop any further renames
                            $failed = $true
                            break
                        }
                    }
                }
                #$db.FileGroups.Refresh()
            }

            #endregion filegroupname
            #region logicalname
            if ($ReplaceBefore) {
                foreach ($fn in $db.FileGroups.Files.Name) {
                    $Entities_Before['LGN'][$fn] = $fn
                }
                foreach ($fn in $db.Logfiles.Name) {
                    $Entities_Before['LGN'][$fn] = $fn
                }
            }
            if (!$failed -and $LogicalName) {
                $New_LogicalNames = @{}
                foreach ($fn in $db.FileGroups.Files.Name) {
                    $New_LogicalNames[$fn] = 1
                }
                foreach ($fn in $db.Logfiles.Name) {
                    $New_LogicalNames[$fn] = 1
                }
                $LNCounter = 0
                foreach ($fg in $db.FileGroups) {
                    $logicalfiles = @($fg.Files)
                    for ($i = 0; $i -lt $logicalfiles.Count; $i++) {
                        $logical = $logicalfiles[$i]
                        $FileType = switch ($fg.FileGroupType) {
                            'RowsFileGroup' { 'ROWS' }
                            'MemoryOptimizedDataFileGroup' { 'MMO' }
                            'FileStreamDataFileGroup' { 'FS' }
                            default { 'STD' }
                        }
                        $Orig_LGName = $logical.Name
                        $Orig_Placeholder = $Orig_LGName
                        if ($ReplaceBefore) {
                            # at Logical Name level, we need to worry about database name and filegroup name
                            $Orig_Placeholder = $Orig_Placeholder.Replace((Get-DbaKeyByValue -HashTable $Entities_Before['DBN'] -Value $db.Name), '').Replace(
                                (Get-DbaKeyByValue -HashTable $Entities_Before['FGN'] -Value $fg.Name), '')
                        }
                        $NewLGName = $LogicalName.Replace('<DBN>', $db.Name).Replace('<DATE>', $CurrentDate).Replace('<FGN>', $fg.Name).Replace(
                            '<FT>', $FileType).Replace('<LGN>', $Orig_Placeholder)
                        $FinalLGName = $NewLGName
                        while ($logical.Name -ne $FinalLGName) {
                            if ($FinalLGName -in $New_LogicalNames.Keys) {
                                $LNCounter += 1
                                $FinalLGName = "$NewLGName$($LNCounter.ToString('000'))"
                            }
                            else {
                                break
                            }
                        }
                        if ($logical.Name -eq $FinalLGName) {
                            Write-Message -Level VeryVerbose -Message "No rename necessary for LogicalFile $($logical.Name) (on FileGroup $($fg.Name) (on $db))"
                            continue
                        }
                        if ($PSCmdlet.ShouldProcess($db, "Renaming LogicalFile $($logical.Name) to $FinalLGName (on FileGroup $($fg.Name))")) {
                            try {
                                if (!$Preview) {
                                    $logical.Rename($FinalLGName)
                                }
                                $New_LogicalNames.Remove($Orig_LGName)
                                $New_LogicalNames[$FinalLGName] = 1
                                $Entities_Before['LGN'][$Orig_LGName] = $FinalLGName
                            }
                            catch {
                                Stop-Function -Message "Failed to Rename Logical File : $($_.Exception.InnerException.InnerException.InnerException)" -ErrorRecord $_ -Target $server.DomainInstanceName -OverrideExceptionMessage
                                # stop any further renames
                                $failed = $true
                                break
                            }
                        }
                    }
                }
                #$fg.Files.Refresh()
                if (!$failed) {
                    $logfiles = @($db.LogFiles)
                    for ($i = 0; $i -lt $logfiles.Count; $i++) {
                        $logicallog = $logfiles[$i]
                        $Orig_LGName = $logicallog.Name
                        $Orig_Placeholder = $Orig_LGName
                        if ($ReplaceBefore) {
                            # at Logical Name level, we need to worry about database name and filegroup name, but for logfiles filegroup is not there
                            $Orig_Placeholder = $Orig_Placeholder.Replace((Get-DbaKeyByValue -HashTable $Entities_Before['DBN'] -Value $db.Name), '').Replace(
                                (Get-DbaKeyByValue -HashTable $Entities_Before['FGN'] -Value $fg.Name), '')
                        }
                        $NewLGName = $LogicalName.Replace('<DBN>', $db.Name).Replace('<DATE>', $CurrentDate).Replace('<FGN>', '').Replace(
                            '<FT>', 'LOG').Replace('<LGN>', $Orig_Placeholder)
                        $FinalLGName = $NewLGName
                        if ($FinalLGName.Length -eq 0) {
                            #someone passed in -LogicalName '<FGN>'.... but we don't have FGN here
                            $FinalLGName = $Orig_LGName
                        }
                        while ($logicallog.Name -ne $FinalLGName) {
                            if ($FinalLGName -in $New_LogicalNames.Keys) {
                                $LNCounter += 1
                                $FinalLGName = "$NewLGName$($LNCounter.ToString('000'))"
                            }
                            else {
                                break
                            }
                        }
                        if ($logicallog.Name -eq $FinalLGName) {
                            Write-Message -Level VeryVerbose -Message "No Rename necessary for LogicalFile log $($logicallog.Name) (LOG on (on $db))"
                            continue
                        }
                        if ($PSCmdlet.ShouldProcess($db, "Renaming LogicalFile log $($logicallog.Name) to $FinalLGName (LOG)")) {
                            try {
                                if (!$Preview) {
                                    $logicallog.Rename($FinalLGName)
                                }
                                $New_LogicalNames.Remove($Orig_LGName)
                                $New_LogicalNames[$FinalLGName] = 1
                                $Entities_Before['LGN'][$Orig_LGName] = $FinalLGName
                            }
                            catch {
                                Stop-Function -Message "Failed to Rename Logical File : $($_.Exception.InnerException.InnerException.InnerException)" -ErrorRecord $_ -Target $server.DomainInstanceName -OverrideExceptionMessage
                                # stop any further renames
                                $failed = $true
                                break
                            }
                        }
                    }
                    #$db.Logfiles.Refresh()
                }
            }
            #endregion logicalname
            #region filename
            if ($ReplaceBefore) {
                foreach ($fn in $db.FileGroups.Files.FileName) {
                    $Entities_Before['FNN'][$fn] = $fn
                }
                foreach ($fn in $db.Logfiles.FileName) {
                    $Entities_Before['FNN'][$fn] = $fn
                }
            }
            if (!$failed -and $FileName) {

                $New_FileNames = @{}
                foreach ($fn in $db.FileGroups.Files.FileName) {
                    $New_FileNames[$fn] = 1
                }
                foreach ($fn in $db.Logfiles.FileName) {
                    $New_FileNames[$fn] = 1
                }
                # we need to inspect what files are in the same directory
                # to avoid failing the process because the move won't work
                # here we have a dict keyed by instance and then keyed by path
                if ( !$InstanceFiles.ContainsKey($Server_Id) ) {
                    $InstanceFiles[$Server_Id] = @{}
                }
                foreach ($fn in $New_FileNames.Keys) {
                    $dirname = [IO.Path]::GetDirectoryName($fn)
                    if ( !$InstanceFiles[$Server_Id].ContainsKey($dirname) ) {
                        $InstanceFiles[$Server_Id][$dirname] = @{}
                        try {
                            $dirfiles = Get-DbaFile -SqlInstance $server -Path $dirname -EnableException
                        }
                        catch {
                            Write-Message -Level Warning -Message "Failed to enumerate existing files at $dirname, move could go wrong"
                        }
                        foreach ($f in $dirfiles) {
                            $InstanceFiles[$Server_Id][$dirname][$f.Filename] = 1
                        }
                    }
                }
                $FNCounter = 0
                foreach ($fg in $db.FileGroups) {
                    $FG_Files = @($fg.Files)
                    foreach ($logical in $FG_Files) {
                        $FileType = switch ($fg.FileGroupType) {
                            'RowsFileGroup' { 'ROWS' }
                            'MemoryOptimizedDataFileGroup' { 'MMO' }
                            'FileStreamDataFileGroup' { 'FS' }
                            default { 'STD' }
                        }
                        $FNName = $logical.FileName
                        $FNNameDir = [IO.Path]::GetDirectoryName($FNName)
                        $Orig_FNNameLeaf = [IO.Path]::GetFileNameWithoutExtension($logical.FileName)
                        $Orig_Placeholder = $Orig_FNNameLeaf
                        if ($ReplaceBefore) {
                            # at Filename level, we need to worry about database name, filegroup name and logical file name
                            $Orig_Placeholder = $Orig_Placeholder.Replace((Get-DbaKeyByValue -HashTable $Entities_Before['DBN'] -Value $db.Name), '').Replace(
                                (Get-DbaKeyByValue -HashTable $Entities_Before['FGN'] -Value $fg.Name), '').Replace(
                                (Get-DbaKeyByValue -HashTable $Entities_Before['LGN'] -Value $logical.Name), '')
                        }
                        $NewFNName = $FileName.Replace('<DBN>', $db.Name).Replace('<DATE>', $CurrentDate).Replace('<FGN>', $fg.Name).Replace(
                            '<FT>', $FileType).Replace('<LGN>', $logical.Name).Replace('<FNN>', $Orig_Placeholder)
                        $FinalFNName = [IO.Path]::Combine($FNNameDir, "$NewFNName$([IO.Path]::GetExtension($FNName))")

                        while ($logical.FileName -ne $FinalFNName) {
                            if ($InstanceFiles[$Server_Id][$FNNameDir].ContainsKey($FinalFNName)) {
                                $FNCounter += 1
                                $FinalFNName = [IO.Path]::Combine($FNNameDir, "$NewFNName$($FNCounter.ToString('000'))$([IO.Path]::GetExtension($FNName))"
                                )
                            }
                            else {
                                break
                            }
                        }
                        if ($logical.FileName -eq $FinalFNName) {
                            Write-Message -Level VeryVerbose -Message "No rename necessary (on FileGroup $($fg.Name) (on $db))"
                            continue
                        }
                        if ($PSCmdlet.ShouldProcess($db, "Renaming FileName $($logical.FileName) to $FinalFNName (on FileGroup $($fg.Name))")) {
                            try {
                                if (!$Preview) {
                                    $logical.FileName = $FinalFNName
                                    $db.Alter()
                                }
                                $InstanceFiles[$Server_Id][$FNNameDir].Remove($FNName)
                                $InstanceFiles[$Server_Id][$FNNameDir][$FinalFNName] = 1
                                $Entities_Before['FNN'][$FNName] = $FinalFNName
                                $Pending_Renames += [pscustomobject]@{
                                    Source      = $FNName
                                    Destination = $FinalFNName
                                }
                            }
                            catch {
                                Stop-Function -Message "Failed to Rename FileName : $($_.Exception.InnerException.InnerException.InnerException)" -ErrorRecord $_ -Target $server.DomainInstanceName -OverrideExceptionMessage
                                # stop any further renames
                                $failed = $true
                                break
                            }
                        }
                    }
                    if (!$failed) {
                        $FG_Files = @($db.Logfiles)
                        foreach ($logical in $FG_Files) {
                            $FNName = $logical.FileName
                            $FNNameDir = [IO.Path]::GetDirectoryName($FNName)
                            $Orig_FNNameLeaf = [IO.Path]::GetFileNameWithoutExtension($logical.FileName)
                            $Orig_Placeholder = $Orig_FNNameLeaf
                            if ($ReplaceBefore) {
                                # at Filename level, we need to worry about database name, filegroup name and logical file name
                                $Orig_Placeholder = $Orig_Placeholder.Replace((Get-DbaKeyByValue -HashTable $Entities_Before['DBN'] -Value $db.Name), '').Replace(
                                    (Get-DbaKeyByValue -HashTable $Entities_Before['FGN'] -Value $fg.Name), '').Replace(
                                    (Get-DbaKeyByValue -HashTable $Entities_Before['LGN'] -Value $logical.Name), '')
                            }
                            $NewFNName = $FileName.Replace('<DBN>', $db.Name).Replace('<DATE>', $CurrentDate).Replace('<FGN>', '').Replace(
                                '<FT>', 'LOG').Replace('<LGN>', $logical.Name).Replace('<FNN>', $Orig_Placeholder)
                            $FinalFNName = [IO.Path]::Combine($FNNameDir, "$NewFNName$([IO.Path]::GetExtension($FNName))")
                            while ($logical.FileName -ne $FinalFNName) {
                                if ($InstanceFiles[$Server_Id][$FNNameDir].ContainsKey($FinalFNName)) {
                                    $FNCounter += 1
                                    $FinalFNName = [IO.Path]::Combine($FNNameDir, "$NewFNName$($FNCounter.ToString('000'))$([IO.Path]::GetExtension($FNName))")
                                }
                                else {
                                    break
                                }
                            }
                            if ($logical.FileName -eq $FinalFNName) {
                                Write-Message -Level VeryVerbose -Message "No rename necessary for $($logical.FileName) (LOG on (on $db))"
                                continue
                            }

                            if ($PSCmdlet.ShouldProcess($db, "Renaming FileName $($logical.FileName) to $FinalFNName (LOG)")) {
                                try {
                                    if (!$Preview) {
                                        $logical.FileName = $FinalFNName
                                        $db.Alter()
                                    }
                                    $InstanceFiles[$Server_Id][$FNNameDir].Remove($FNName)
                                    $InstanceFiles[$Server_Id][$FNNameDir][$FinalFNName] = 1
                                    $Entities_Before['FNN'][$FNName] = $FinalFNName
                                    $Pending_Renames += [pscustomobject]@{
                                        Source      = $FNName
                                        Destination = $FinalFNName
                                    }
                                }
                                catch {
                                    Stop-Function -Message "Failed to Rename FileName : $($_.Exception.InnerException.InnerException.InnerException)" -ErrorRecord $_ -Target $server.DomainInstanceName -OverrideExceptionMessage
                                    # stop any further renames
                                    $failed = $true
                                    break
                                }
                            }
                        }
                    }

                }
                #endregion filename
                #region move
                $ComputerName = $null
                $Final_Renames = New-Object System.Collections.ArrayList
                if ([DbaValidate]::IsLocalhost($server.ComputerName)) {
                    # locally ran so we can just use rename-item
                    $ComputerName = $server.ComputerName
                }
                else {
                    # let's start checking if we can access .ComputerName
                    $testPS = $false
                    if ($SqlCredential) {
                        # why does Test-PSRemoting require a Credential param ? this is ugly...
                        $testPS = Test-PSRemoting -ComputerName $server.ComputerName -Credential $SqlCredential -ErrorAction Stop
                    }
                    else {
                        $testPS = Test-PSRemoting -ComputerName $server.ComputerName -ErrorAction Stop
                    }
                    if (!($testPS)) {
                        # let's try to resolve it to a more qualified name, without "cutting" knowledge about the domain (only $server.Name possibly holds the complete info)
                        $Resolved = (Resolve-DbaNetworkName -ComputerName $server.Name).FullComputerName
                        if ($SqlCredential) {
                            $testPS = Test-PSRemoting -ComputerName $Resolved -Credential $SqlCredential -ErrorAction Stop
                        }
                        else {
                            $testPS = Test-PSRemoting -ComputerName $Resolved -ErrorAction Stop
                        }
                        if ($testPS) {
                            $ComputerName = $Resolved
                        }
                    }
                    else {
                        $ComputerName = $server.ComputerName
                    }
                }
                foreach ($op in $pending_renames) {
                    if ([DbaValidate]::IsLocalhost($server.ComputerName)) {
                        $null = $Final_Renames.Add([pscustomobject]@{
                                Source       = $op.Source
                                Destination  = $op.Destination
                                ComputerName = $ComputerName
                            })
                    }
                    else {
                        if ($null -eq $ComputerName) {
                            # if we don't have remote access ($ComputerName is null) we can fallback to admin shares if they're available
                            if (Test-Path (Join-AdminUnc -ServerName $server.ComputerName -filepath $op.Source)) {
                                $null = $Final_Renames.Add([pscustomobject]@{
                                        Source       = Join-AdminUnc -ServerName $server.ComputerName -filepath $op.Source
                                        Destination  = Join-AdminUnc -ServerName $server.ComputerName -filepath $op.Destination
                                        ComputerName = $server.ComputerName
                                    })
                            }
                            else {
                                # flag the impossible rename ($ComputerName is $null)
                                $null = $Final_Renames.Add([pscustomobject]@{
                                        Source       = $op.Source
                                        Destination  = $op.Destination
                                        ComputerName = $ComputerName
                                    })
                            }
                        }
                        else {
                            # we can do renames in a remote pssession
                            $null = $Final_Renames.Add([pscustomobject]@{
                                    Source       = $op.Source
                                    Destination  = $op.Destination
                                    ComputerName = $ComputerName
                                })
                        }
                    }
                }
                $Status = 'FULL'
                if (!$failed -and ($SetOffline -or $Move) -and $Final_Renames) {
                    if (!$Move) {
                        Write-Message -Level VeryVerbose -Message "Setting the database offline. You are in charge of moving the files to the new location"
                        # because renames still need to be dealt with
                        $Status = 'PARTIAL'
                    }
                    else {
                        if ($PSCmdlet.ShouldProcess($db, "File Rename required, setting db offline")) {
                            $SetState = Set-DbaDatabaseState -SqlInstance $server -Database $db.Name -Offline -Force
                            if ($SetState.Status -ne 'OFFLINE') {
                                Write-Message -Level Warning -Message "Setting db offline failed, You are in charge of moving the files to the new location"
                                # because it was impossible to set the database offline
                                $Status = 'PARTIAL'
                            }
                            else {
                                try {
                                    while ($Final_Renames.Count -gt 0) {
                                        $op = $Final_Renames.Item(0)
                                        if ($null -eq $op.ComputerName) {
                                            Stop-Function -Message "No access to physical files for renames"
                                        }
                                        else {
                                            Write-Message -Level VeryVerbose -Message "Moving file $($op.Source) to $($op.Destination)"
                                            if (!$Preview) {
                                                $scriptblock = {
                                                    $op = $args[0]
                                                    Rename-Item -Path $op.Source -NewName $op.Destination
                                                }
                                                Invoke-Command2 -ComputerName $op.ComputerName -Credential $sqlCredential -ScriptBlock $scriptblock -ArgumentList $op
                                            }
                                        }
                                        $null = $Final_Renames.RemoveAt(0)
                                    }
                                }
                                catch {
                                    $failed = $true
                                    # because a rename operation failed
                                    $Status = 'PARTIAL'
                                    Stop-Function -Message "Failed to rename $($op.Source) to $($op.Destination), you are in charge of moving the files to the new location" -ErrorRecord $_ -Target $instance -Exception $_.Exception -Continue
                                }
                                if (!$failed) {
                                    if ($PSCmdlet.ShouldProcess($db, "Setting database online")) {
                                        $SetState = Set-DbaDatabaseState -SqlInstance $server -Database $db.Name -Online -Force
                                        if ($SetState.Status -ne 'ONLINE') {
                                            Write-Message -Level Warning -Message "Setting db online failed"
                                            # because renames were done, but the database didn't wake up
                                            $Status = 'PARTIAL'
                                        }
                                        else {
                                            $Status = 'FULL'
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                else {
                    # because of a previous error with renames to do
                    $Status = 'PARTIAL'
                }
            }
            else {
                if (!$failed) {
                    # because no previous error and not filename
                    $Status = 'FULL'
                }
                else {
                    # because previous errors and not filename
                    $Status = 'PARTIAL'
                }
            }
            #endregion move
            # remove entities that match for the output
            foreach ($k in $Entities_Before.Keys) {
                $ToRemove = $Entities_Before[$k].GetEnumerator() | Where-Object { $_.Name -eq $_.Value } | Select-Object -ExpandProperty Name
                foreach ($el in $ToRemove) {
                    $Entities_Before[$k].Remove($el)
                }
            }
            [pscustomobject]@{
                ComputerName       = $server.ComputerName
                InstanceName       = $server.ServiceName
                SqlInstance        = $server.DomainInstanceName
                Database           = $db
                DBN                = $Entities_Before['DBN']
                DatabaseRenames    = ($Entities_Before['DBN'].GetEnumerator() | Foreach-Object { "$($_.Name) --> $($_.Value)" }) -Join "`n"
                FGN                = $Entities_Before['FGN']
                FileGroupsRenames  = ($Entities_Before['FGN'].GetEnumerator() | Foreach-Object { "$($_.Name) --> $($_.Value)" }) -Join "`n"
                LGN                = $Entities_Before['LGN']
                LogicalNameRenames = ($Entities_Before['LGN'].GetEnumerator() | Foreach-Object { "$($_.Name) --> $($_.Value)"  }) -Join "`n"
                FNN                = $Entities_Before['FNN']
                FileNameRenames    = ($Entities_Before['FNN'].GetEnumerator() | Foreach-Object { "$($_.Name) --> $($_.Value)"  }) -Join "`n"
                PendingRenames     = $Final_Renames
                Status             = $Status
            } | Select-DefaultView -ExcludeProperty DatabaseRenames, FileGroupsRenames, LogicalNameRenames, FileNameRenames
        }
        #endregion db loop
    }
}
function Rename-DbaLogin {
    <#
.SYNOPSIS
Rename-DbaLogin will rename login and database mapping for a specified login.

.DESCRIPTION
There are times where you might want to rename a login that was copied down, or if the name is not descriptive for what it does.

It can be a pain to update all of the mappings for a specific user, this does it for you.

.PARAMETER SqlInstance
Source SQL Server.You must have sysadmin access and server version must be SQL Server version 2000 or greater.

.PARAMETER Destination
Destination Sql Server. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Login
The current Login on the server - this list is auto-populated from the server.

.PARAMETER NewLogin
The new Login that you wish to use. If it is a windows user login, then the SID must match.

.PARAMETER Confirm
Prompts to confirm actions

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.


.NOTES
Tags: Login
Author: Mitchell Hamann (@SirCaptainMitch)

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Rename-DbaLogin

.EXAMPLE
Rename-DbaLogin -SqlInstance localhost -Login DbaToolsUser -NewLogin captain

SQL Login Example

.EXAMPLE
Rename-DbaLogin -SqlInstance localhost -Login domain\oldname -NewLogin domain\newname

Change the windowsuser login name.

.EXAMPLE
Rename-DbaLogin -SqlInstance localhost -Login dbatoolsuser -NewLogin captain -WhatIf

WhatIf Example
#>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory = $true)]
        [string]$Login,
        [parameter(Mandatory = $true)]
        [string]$NewLogin,
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $Databases = $server.Databases | Where-Object IsAccessible
            $currentLogin = $server.Logins[$Login]

            if ($Pscmdlet.ShouldProcess($SqlInstance, "Changing Login name from  [$Login] to [$NewLogin]")) {
                try {
                    $dbenums = $currentLogin.EnumDatabaseMappings()
                    $currentLogin.rename($NewLogin)
                    [pscustomobject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Database     = $null
                        OldLogin     = $Login
                        NewLogin     = $NewLogin
                        Status       = "Successful"
                    }
                }
                catch {
                    $dbenums = $null
                    [pscustomobject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Database     = $null
                        OldLogin     = $Login
                        NewLogin     = $NewLogin
                        Status       = "Failure"
                    }
                    Stop-Function -Message "Failure" -ErrorRecord $_ -Target $login
                }
            }

            foreach ($db in $dbenums) {
                $db = $databases[$db.DBName]
                $user = $db.Users[$Login]
                Write-Message -Level Verbose -Message "Starting update for $db"

                if ($Pscmdlet.ShouldProcess($SqlInstance, "Changing database $db user $user from [$Login] to [$NewLogin]")) {
                    try {
                        $oldname = $user.name
                        $user.Rename($NewLogin)
                        [pscustomobject]@{
                            ComputerName = $server.ComputerName
                            InstanceName = $server.ServiceName
                            SqlInstance  = $server.DomainInstanceName
                            Database     = $db.name
                            OldUser      = $oldname
                            NewUser      = $NewLogin
                            Status       = "Successful"
                        }

                    }
                    catch {
                        Write-Message -Level Warning -Message "Rolling back update to login: $Login"
                        $currentLogin.rename($Login)

                        [pscustomobject]@{
                            ComputerName = $server.ComputerName
                            InstanceName = $server.ServiceName
                            SqlInstance  = $server.DomainInstanceName
                            Database     = $db.name
                            OldUser      = $NewLogin
                            NewUser      = $oldname
                            Status       = "Failure to rename. Rolled back change."
                        }
                        Stop-Function -Message "Failure" -ErrorRecord $_ -Target $NewLogin
                    }
                }
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Repair-DbaOrphanUser {
    <#
        .SYNOPSIS
            Finds orphan users with existing login and remaps them.

        .DESCRIPTION
            An orphan user is defined by a user that does not have a matching login (Login property = "").

            If the matching login exists it must be:
                Enabled
                Not a system object
                Not locked
                Have the same name that user

            You can drop users that does not have their matching login by specifying the parameter -RemoveNotExisting.

        .PARAMETER SqlInstance
            The SQL Server Instance to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies the database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies the database(s) to exclude from processing. Options for this list are auto-populated from the server

        .PARAMETER Users
            Specifies the list of usernames to repair.

        .PARAMETER Force
        Forces alter schema to dbo owner so users can be dropped.

        .PARAMETER RemoveNotExisting
            If this switch is enabled, all users that do not have a matching login will be dropped from the database.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .EXAMPLE
            Repair-DbaOrphanUser -SqlInstance sql2005

            Finds and repairs all orphan users of all databases present on server 'sql2005'

        .EXAMPLE
            Repair-DbaOrphanUser -SqlInstance sqlserver2014a -SqlCredential $cred

            Finds and repair all orphan users in all databases present on server 'sqlserver2014a'. SQL credentials are used to authenticate to the server.

        .EXAMPLE
            Repair-DbaOrphanUser -SqlInstance sqlserver2014a -Database db1, db2

            Finds and repairs all orphan users in both db1 and db2 databases.

        .EXAMPLE
            Repair-DbaOrphanUser -SqlInstance sqlserver2014a -Database db1 -Users OrphanUser

            Finds and repairs user 'OrphanUser' in 'db1' database.

        .EXAMPLE
            Repair-DbaOrphanUser -SqlInstance sqlserver2014a -Users OrphanUser

            Finds and repairs user 'OrphanUser' on all databases

        .EXAMPLE
            Repair-DbaOrphanUser -SqlInstance sqlserver2014a -RemoveNotExisting

            Finds all orphan users of all databases present on server 'sqlserver2014a'. Removes all users that do not have  matching Logins.

        .NOTES
            Tags: Orphan
            Author: Claudio Silva (@ClaudioESSilva)
            Editor: Simone Bizzotto (@niphlod)
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Repair-DbaOrphanUser
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [object[]]$Users,
        [switch]$RemoveNotExisting,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {

        foreach ($instance in $SqlInstance) {

            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Write-Message -Level Warning -Message "Failed to connect to: $SqlInstance."
                continue
            }

            $DatabaseCollection = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                $DatabaseCollection = $DatabaseCollection | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $DatabaseCollection = $DatabaseCollection | Where-Object Name -NotIn $ExcludeDatabase
            }

            if ($DatabaseCollection.Count -gt 0) {
                foreach ($db in $DatabaseCollection) {
                    try {
                        #if SQL 2012 or higher only validate databases with ContainmentType = NONE
                        if ($server.versionMajor -gt 10) {
                            if ($db.ContainmentType -ne [Microsoft.SqlServer.Management.Smo.ContainmentType]::None) {
                                Write-Message -Level Warning -Message "Database '$db' is a contained database. Contained databases can't have orphaned users. Skipping validation."
                                Continue
                            }
                        }

                        Write-Message -Level Verbose -Message "Validating users on database '$db'."

                        if ($Users.Count -eq 0) {
                            #the third validation will remove from list sql users without login. The rule here is Sid with length higher than 16
                            $UsersToWork = $db.Users | Where-Object { $_.Login -eq "" -and ($_.ID -gt 4) -and ($_.Sid.Length -gt 16 -and $_.LoginType -eq [Microsoft.SqlServer.Management.Smo.LoginType]::SqlLogin) -eq $false }
                        }
                        else {

                            #the fourth validation will remove from list sql users without login. The rule here is Sid with length higher than 16
                            $UsersToWork = $db.Users | Where-Object { $_.Login -eq "" -and ($_.ID -gt 4) -and ($Users -contains $_.Name) -and (($_.Sid.Length -gt 16 -and $_.LoginType -eq [Microsoft.SqlServer.Management.Smo.LoginType]::SqlLogin) -eq $false) }

                        }

                        if ($UsersToWork.Count -gt 0) {
                            Write-Message -Level Verbose -Message "Orphan users found"
                            $UsersToRemove = @()
                            foreach ($User in $UsersToWork) {
                                $ExistLogin = $server.logins | Where-Object {
                                    $_.Isdisabled -eq $False -and
                                    $_.IsSystemObject -eq $False -and
                                    $_.IsLocked -eq $False -and
                                    $_.Name -eq $User.Name
                                }

                                if ($ExistLogin) {
                                    if ($server.versionMajor -gt 8) {
                                        $query = "ALTER USER " + $User + " WITH LOGIN = " + $User
                                    }
                                    else {
                                        $query = "exec sp_change_users_login 'update_one', '$User'"
                                    }

                                    if ($Pscmdlet.ShouldProcess($db.Name, "Mapping user '$($User.Name)'")) {
                                        $server.Databases[$db.Name].ExecuteNonQuery($query) | Out-Null
                                        Write-Message -Level Verbose -Message "User '$($User.Name)' mapped with their login."

                                        [PSCustomObject]@{
                                            ComputerName = $server.ComputerName
                                            InstanceName = $server.ServiceName
                                            SqlInstance  = $server.DomainInstanceName
                                            DatabaseName = $db.Name
                                            User         = $User.Name
                                            Status       = "Success"
                                        }
                                    }
                                }
                                else {
                                    if ($RemoveNotExisting) {
                                        #add user to collection
                                        $UsersToRemove += $User
                                    }
                                    else {
                                        Write-Message -Level Verbose -Message "Orphan user $($User.Name) does not have matching login."
                                        [PSCustomObject]@{
                                            ComputerName = $server.ComputerName
                                            InstanceName = $server.ServiceName
                                            SqlInstance  = $server.DomainInstanceName
                                            DatabaseName = $db.Name
                                            User         = $User.Name
                                            Status       = "No matching login"
                                        }
                                    }
                                }
                            }

                            #With the collection complete invoke remove.
                            if ($RemoveNotExisting) {
                                if ($Force) {
                                    if ($Pscmdlet.ShouldProcess($db.Name, "Remove-DbaOrphanUser")) {
                                        Write-Message -Level Verbose -Message "Calling 'Remove-DbaOrphanUser' with -Force."
                                        Remove-DbaOrphanUser -SqlInstance $server -Database $db.Name -User $UsersToRemove -Force
                                    }
                                }
                                else {
                                    if ($Pscmdlet.ShouldProcess($db.Name, "Remove-DbaOrphanUser")) {
                                        Write-Message -Level Verbose -Message "Calling 'Remove-DbaOrphanUser'."
                                        Remove-DbaOrphanUser -SqlInstance $server -Database $db.Name -User $UsersToRemove
                                    }
                                }
                            }
                        }
                        else {
                            Write-Message -Level Verbose -Message "No orphan users found on database '$db'."
                        }
                        #reset collection
                        $UsersToWork = $null
                    }
                    catch {
                        Stop-Function -Message $_ -Continue
                    }
                }
            }
            else {
                Write-Message -Level Verbose -Message "There are no databases to analyse."
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Repair-SqlOrphanUser
    }
}
function Repair-DbaServerName {
    <#
        .SYNOPSIS
            Renames @@SERVERNAME to match with the Windows name.

        .DESCRIPTION
            When a SQL Server's host OS is renamed, the SQL Server should be as well. This helps with Availability Groups and Kerberos.

            This command renames @@SERVERNAME to match with the Windows name. The new name is automatically determined. It does not matter if you use an alias to connect to the SQL instance.

            If the automatically determined new name matches the old name, the command will not run.

            https://www.mssqltips.com/sqlservertip/2525/steps-to-change-the-server-name-for-a-sql-server-machine/

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER AutoFix
            If this switch is enabled, the repair will be performed automatically.

        .PARAMETER Force
            If this switch is enabled, most confirmation prompts will be skipped.
        
        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.


        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .NOTES
            Tags: SPN
            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Repair-DbaServerName

        .EXAMPLE
            Repair-DbaServerName -SqlInstance sql2014

            Checks to see if the server name is updatable and changes the name with a number of prompts.

        .EXAMPLE
            Repair-DbaServerName -SqlInstance sql2014 -AutoFix

            Checks to see if the server name is updatable and automatically performs the change. Replication or mirroring will be broken if necessary.

        .EXAMPLE
            Repair-DbaServerName -SqlInstance sql2014 -AutoFix -Force

            Checks to see if the server name is updatable and automatically performs the change, bypassing most prompts and confirmations. Replication or mirroring will be broken if necessary.
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High")]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [switch]$AutoFix,
        [switch]$Force,
        [switch][Alias('Silent')]
        $EnableException
    )

    begin {
        if ($Force -eq $true) {
            $ConfirmPreference = "None"
        }
    }

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($server.isClustered) {
                Write-Message -Level Warning -Message "$instance is a cluster. Microsoft does not support renaming clusters."
                continue
            }


            # Check to see if we can easily proceed

            $nametest = Test-DbaServerName $server -EnableException | Select-Object *
            $oldserverinstancename = $nametest.ServerName
            $SqlInstancename = $nametest.SqlInstance

            if ($nametest.RenameRequired -eq $false) {
                Stop-Function -Continue -Message "Good news! $oldserverinstancename's @@SERVERNAME does not need to be changed. If you'd like to rename it, first rename the Windows server."
            }

            if (-not $nametest.updatable) {
                Write-Message -Level Output -Message "Test-DbaServerName reports that the rename cannot proceed with a rename in this $instance's current state."

                foreach ($nametesterror in $nametest.Blockers) {
                    if ($nametesterror -like '*replication*') {

                        if (-not $AutoFix) {
                            Stop-Function -Message "Cannot proceed because some databases are involved in replication. You can run exec sp_dropdistributor @no_checks = 1 but that may be pretty dangerous. Alternatively, you can run -AutoFix to automatically fix this issue. AutoFix will also break all database mirrors."
                            return
                        }
                        else {
                            if ($Pscmdlet.ShouldProcess("console", "Prompt will appear for confirmation to break replication.")) {
                                $title = "You have chosen to AutoFix the blocker: replication."
                                $message = "We can run sp_dropdistributor which will pretty much destroy replication on this server. Do you wish to continue? (Y/N)"
                                $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Will continue"
                                $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Will exit"
                                $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
                                $result = $host.ui.PromptForChoice($title, $message, $options, 1)

                                if ($result -eq 1) {
                                    Stop-Function -Message "Failure" -Target $server -ErrorRecord $_ -Continue
                                }
                                else {
                                    Write-Message -Level Output -Message "`nPerforming sp_dropdistributor @no_checks = 1."
                                    $sql = "sp_dropdistributor @no_checks = 1"
                                    Write-Message -Level Debug -Message $sql
                                    try {
                                        $null = $server.Query($sql)
                                    }
                                    catch {
                                        Stop-Function -Message "Failure" -Target $server -ErrorRecord $_ -Continue
                                    }
                                }
                            }
                        }
                    }
                    elseif ($Error -like '*mirror*') {
                        if ($AutoFix -eq $false) {
                            Stop-Function -Message "Cannot proceed because some databases are being mirrored. Stop mirroring to proceed. Alternatively, you can run -AutoFix to automatically fix this issue. AutoFix will also stop replication." -Continue
                        }
                        else {
                            if ($Pscmdlet.ShouldProcess("console", "Prompt will appear for confirmation to break replication.")) {
                                $title = "You have chosen to AutoFix the blocker: mirroring."
                                $message = "We can run sp_dropdistributor which will pretty much destroy replication on this server. Do you wish to continue? (Y/N)"
                                $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Will continue"
                                $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Will exit"
                                $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
                                $result = $host.ui.PromptForChoice($title, $message, $options, 1)

                                if ($result -eq 1) {
                                    Write-Message -Level Output -Message "Okay, moving on."
                                }
                                else {
                                    Write-Message -Level Verbose -Message "Removing Mirroring"

                                    foreach ($database in $server.Databases) {
                                        if ($database.IsMirroringEnabled) {
                                            $dbname = $database.name

                                            try {
                                                Write-Message -Level Verbose -Message "Breaking mirror for $dbname."
                                                $database.ChangeMirroringState([Microsoft.SqlServer.Management.Smo.MirroringOption]::Off)
                                                $database.Alter()
                                                $database.Refresh()
                                            }
                                            catch {
                                                Stop-Function -Message "Failure" -Target $server -ErrorRecord $_
                                                return
                                                #throw "Could not break mirror for $dbname. Skipping."
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            # ^ That's embarrassing

            $instancename = $server.InstanceName

            if (-not $instancename) {
                $instancename = "MSSQLSERVER"
            }

            try {
                $allsqlservices = Get-Service -ComputerName $instance.ComputerName -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -like "SQL*$instancename*" -and $_.Status -eq "Running" }
            }
            catch {
                Write-Message -Level Warning -Message "Can't contact $instance using Get-Service. This means the script will not be able to automatically restart SQL services."
            }

            if ($nametest.Warnings.length -gt 0) {
                $reportingservice = Get-Service -ComputerName $instance.ComputerName -DisplayName "SQL Server Reporting Services ($instancename)" -ErrorAction SilentlyContinue

                if ($reportingservice.Status -eq "Running") {
                    if ($Pscmdlet.ShouldProcess($server.name, "Reporting Services is running for this instance. Would you like to automatically stop this service?")) {
                        $reportingservice | Stop-Service
                        Write-Message -Level Warning -Message "You must reconfigure Reporting Services using Reporting Services Configuration Manager or PowerShell once the server has been successfully renamed."
                    }
                }
            }

            if ($Pscmdlet.ShouldProcess($server.name, "Performing sp_dropserver to remove the old server name, $oldserverinstancename, then sp_addserver to add $SqlInstancename")) {
                $sql = "sp_dropserver '$oldserverinstancename'"
                Write-Message -Level Debug -Message $sql
                try {
                    $null = $server.Query($sql)
                }
                catch {
                    Stop-Function -Message "Failure" -Target $server -ErrorRecord $_
                    return
                }

                $sql = "sp_addserver '$SqlInstancename', local"
                Write-Message -Level Debug -Message $sql

                try {
                    $null = $server.Query($sql)
                }
                catch {
                    Stop-Function -Message "Failure" -Target $server -ErrorRecord $_
                    return
                }
                $renamed = $true
            }

            if ($null -eq $allsqlservices) {
                Write-Message -Level Warning -Message "Could not contact $($instance.ComputerName) using Get-Service. You must manually restart the SQL Server instance."
                $needsrestart = $true
            }
            else {
                if ($Pscmdlet.ShouldProcess($instance.ComputerName, "Rename complete! The SQL Service must be restarted to commit the changes. Would you like to restart the $instancename instance now?")) {
                    try {
                        Write-Message -Level Verbose -Message "Stopping SQL Services for the $instancename instance"
                        $allsqlservices | Stop-Service -Force -WarningAction SilentlyContinue # because it reports the wrong name
                        Write-Message -Level Verbose -Message "Starting SQL Services for the $instancename instance."
                        $allsqlservices | Where-Object { $_.DisplayName -notlike "*reporting*" } | Start-Service -WarningAction SilentlyContinue # because it reports the wrong name
                    }
                    catch {
                        Stop-Function -Message "Failure" -Target $server -ErrorRecord $_ -Continue
                    }
                }
            }

            if ($renamed -eq $true) {
                Write-Message -Level Verbose -Message "$instance successfully renamed from $oldserverinstancename to $SqlInstancename."
                Test-DbaServerName -SqlInstance $server
            }

            if ($needsrestart -eq $true) {
                Write-Message -Level Warning -Message "SQL Service restart for $SqlInstancename still required."
            }
        }
    }
}
function Reset-DbaAdmin {
    <#
        .SYNOPSIS
            This function allows administrators to regain access to SQL Servers in the event that passwords or access was lost.

            Supports SQL Server 2005 and above. Windows administrator access is required.

        .DESCRIPTION
            This function allows administrators to regain access to local or remote SQL Servers by either resetting the sa password, adding the sysadmin role to existing login, or adding a new login (SQL or Windows) and granting it sysadmin privileges.

            This is accomplished by stopping the SQL services or SQL Clustered Resource Group, then restarting SQL via the command-line using the /mReset-DbaAdmin parameter which starts the server in Single-User mode and only allows this script to connect.

            Once the service is restarted, the following tasks are performed:
            - Login is added if it doesn't exist
            - If login is a Windows User, an attempt is made to ensure it exists
            - If login is a SQL Login, password policy will be set to OFF when creating the login, and SQL Server authentication will be set to Mixed Mode.
            - Login will be enabled and unlocked
            - Login will be added to sysadmin role

            If failures occur at any point, a best attempt is made to restart the SQL Server.

            In order to make this script as portable as possible, System.Data.SqlClient and Get-WmiObject are used (as opposed to requiring the Failover Cluster Admin tools or SMO).

            If using this function against a remote SQL Server, ensure WinRM is configured and accessible. If this is not possible, run the script locally.

            Tested on Windows XP, 7, 8.1, Server 2012 and Windows Server Technical Preview 2.
            Tested on SQL Server 2005 SP4 through 2016 CTP2.

        .PARAMETER SqlInstance
            The SQL Server instance. SQL Server must be 2005 and above, and can be a clustered or stand-alone instance.

        .PARAMETER Login
            By default, the Login parameter is "sa" but any other SQL or Windows account can be specified. If a login does not currently exist, it will be added.

            When adding a Windows login to remote servers, ensure the SQL Server can add the login (ie, don't add WORKSTATION\Admin to remoteserver\instance. Domain users and Groups are valid input.

        .PARAMETER SecurePassword
            By default, if a SQL Login is detected, you will be prompted for a password. Use this to securely bypass the prompt.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER Force
            If this switch is enabled, the Login(s) will be dropped and recreated on Destination. Logins that own Agent jobs cannot be dropped at this time.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .EXAMPLE
            Reset-DbaAdmin -SqlInstance sqlcluster

            Prompts for password, then resets the "sa" account password on sqlcluster.

        .EXAMPLE
            Reset-DbaAdmin -SqlInstance sqlserver\sqlexpress -Login ad\administrator

            Prompts user to confirm that they understand the SQL Service will be restarted.

            Adds the domain account "ad\administrator" as a sysadmin to the SQL instance.
            If the account already exists, it will be added to the sysadmin role.

        .EXAMPLE
            Reset-DbaAdmin -SqlInstance sqlserver\sqlexpress -Login sqladmin -Force

            Skips restart confirmation, prompts for password, then adds a SQL Login "sqladmin" with sysadmin privileges.
            If the account already exists, it will be added to the sysadmin role and the password will be reset.

        .NOTES
            Tags: WSMan
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: Admin access to server (not SQL Services),
            Remoting must be enabled and accessible if $SqlInstance is not local

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire

        .LINK
            https://dbatools.io/Reset-DbaAdmin
#>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High")]
    param (
        [Parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]
        $SqlInstance,
        [string]$Login = "sa",
        [SecureString]$SecurePassword,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Reset-SqlAdmin

        #region Utility functions
        function ConvertTo-PlainText {
            <#
                .SYNOPSIS
                    Internal function.
             #>
            [CmdletBinding()]
            param (
                [Parameter(Mandatory = $true)]
                [Security.SecureString]
                $Password
            )

            $marshal = [Runtime.InteropServices.Marshal]
            $plaintext = $marshal::PtrToStringAuto($marshal::SecureStringToBSTR($Password))
            return $plaintext
        }

        function Invoke-ResetSqlCmd {
            <#
                .SYNOPSIS
                    Internal function. Executes a SQL statement against specified computer, and uses "Reset-DbaAdmin" as the Application Name.
            #>
            [OutputType([System.Boolean])]
            [CmdletBinding()]
            param (
                [Parameter(Mandatory = $true)]
                [Alias("ServerInstance", "SqlServer")]
                [DbaInstanceParameter]
                $SqlInstance,
                [string]$sql
            )
            try {
                $connstring = "Data Source=$SqlInstance;Integrated Security=True;Connect Timeout=2;Application Name=Reset-DbaAdmin"
                $conn = New-Object System.Data.SqlClient.SqlConnection $connstring
                $conn.Open()
                $cmd = New-Object system.data.sqlclient.sqlcommand($null, $conn)
                $cmd.CommandText = $sql
                $cmd.ExecuteNonQuery() | Out-Null
                $cmd.Dispose()
                $conn.Close()
                $conn.Dispose()
                return $true
            }
            catch {
                return $false
            }
        }
        #endregion Utility functions
    }

    process {
        if ($Force) {
            $ConfirmPreference = "none"
        }

        $baseaddress = $SqlInstance.ComputerName

        # Before we continue, we need confirmation.
        if ($pscmdlet.ShouldProcess($baseaddress, "Reset-DbaAdmin (SQL Server instance $SqlInstance will restart)")) {
            # Get hostname

            if ($baseaddress -eq "." -or $baseaddress -eq $env:COMPUTERNAME -or $baseaddress -eq "localhost") {
                $ipaddr = "."
                $hostname = $env:COMPUTERNAME
                $baseaddress = $env:COMPUTERNAME
            }

            # If server is not local, get IP address and NetBios name in case CNAME records were referenced in the SQL hostname
            if ($baseaddress -ne $env:COMPUTERNAME) {
                # Test for WinRM #Test-WinRM neh
                winrm id -r:$baseaddress 2>$null | Out-Null
                if ($LastExitCode -ne 0) {
                    throw "Remote PowerShell access not enabled on on $source or access denied. Quitting."
                }

                # Test Connection first using Test-Connection which requires ICMP access then failback to tcp if pings are blocked
                Write-Message -Level Verbose -Message "Testing connection to $baseaddress"
                $testconnect = Test-Connection -ComputerName $baseaddress -Count 1 -Quiet

                if ($testconnect -eq $false) {
                    Write-Message -Level Verbose -Message "First attempt using ICMP failed. Trying to connect using sockets. This may take up to 20 seconds."
                    $tcp = New-Object System.Net.Sockets.TcpClient
                    try {
                        $tcp.Connect($hostname, 135)
                        $tcp.Close()
                        $tcp.Dispose()
                    }
                    catch {
                        throw "Can't connect to $baseaddress either via ping or tcp (WMI port 135)"
                    }
                }
                Write-Message -Level Verbose -Message "Resolving IP address."
                try {
                    $hostentry = [System.Net.Dns]::GetHostEntry($baseaddress)
                    $ipaddr = ($hostentry.AddressList | Where-Object { $_ -notlike '169.*' } | Select-Object -First 1).IPAddressToString
                }
                catch {
                    throw "Could not resolve SqlServer IP or NetBIOS name"
                }

                Write-Message -Level Verbose -Message "Resolving NetBIOS name."
                try {
                    $hostname = (Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter IPEnabled=TRUE -ComputerName $ipaddr).PSComputerName
                    if ($null -eq $hostname) {
                        $hostname = (nbtstat -A $ipaddr | Where-Object { $_ -match '\<00\>  UNIQUE' } | ForEach-Object { $_.SubString(4, 14) }).Trim()
                    }
                }
                catch {
                    throw "Could not access remote WMI object. Check permissions and firewall."
                }
            }

            # Setup remote session if server is not local
            if ($hostname -ne $env:COMPUTERNAME) {
                try {
                    $session = New-PSSession -ComputerName $hostname
                }
                catch {
                    throw "Can't access $hostname using PSSession. Check your firewall settings and ensure Remoting is enabled or run the script locally."
                }
            }

            Write-Message -Level Verbose -Message "Detecting login type."
            # Is login a Windows login? If so, does it exist?
            if ($login -match "\\") {
                Write-Message -Level Verbose -Message "Windows login detected. Checking to ensure account is valid."
                $windowslogin = $true
                try {
                    if ($hostname -eq $env:COMPUTERNAME) {
                        $account = New-Object System.Security.Principal.NTAccount($args)
                        $sid = $account.Translate([System.Security.Principal.SecurityIdentifier])
                    }
                    else {
                        Invoke-Command -ErrorAction Stop -Session $session -ArgumentList $login -ScriptBlock {
                            $account = New-Object System.Security.Principal.NTAccount($args)
                            $sid = $account.Translate([System.Security.Principal.SecurityIdentifier])
                        }
                    }
                }
                catch {
                    Write-Message -Level Warning -Message "Cannot resolve Windows User or Group $login. Trying anyway."
                }
            }

            # If it's not a Windows login, it's a SQL login, so it needs a password.
            if ($windowslogin -ne $true -and (Test-Bound -Not -ParameterName SecurePassword)) {
                Write-Message -Level Verbose -Message "SQL login detected"
                do {
                    $Password = Read-Host -AsSecureString "Please enter a new password for $login"
                }
                while ($Password.Length -eq 0)
            }
            
            If ((Test-Bound -ParameterName SecurePassword)) {
                $Password = $SecurePassword
            }
            
            # Get instance and service display name, then get services
            $instance = $null
            $instance = $SqlInstance.InstanceName
            if (-not $instance) {
                $instance = "MSSQLSERVER"
            }
            $displayName = "SQL Server ($instance)"

            try {
                if ($hostname -eq $env:COMPUTERNAME) {
                    $instanceservices = Get-Service -ErrorAction Stop | Where-Object { $_.DisplayName -like "*($instance)*" -and $_.Status -eq "Running" }
                    $sqlservice = Get-Service -ErrorAction Stop | Where-Object DisplayName -eq "SQL Server ($instance)"
                }
                else {
                    $instanceservices = Get-Service -ComputerName $ipaddr -ErrorAction Stop | Where-Object { $_.DisplayName -like "*($instance)*" -and $_.Status -eq "Running" }
                    $sqlservice = Get-Service -ComputerName $ipaddr -ErrorAction Stop | Where-Object DisplayName -eq "SQL Server ($instance)"
                }
            }
            catch {
                Stop-Function -Message "Cannot connect to WMI on $hostname or SQL Service does not exist. Check permissions, firewall and SQL Server running status." -ErrorRecord $_ -Target $SqlInstance
                return
            }

            if (-not $instanceservices) {
                Stop-Function -Message "Couldn't find SQL Server instance. Check the spelling, ensure the service is running and try again." -Target $SqlInstance
                return
            }

            Write-Message -Level Verbose -Message "Attempting to stop SQL Services."

            # Check to see if service is clustered. Clusters don't support -m (since the cluster service
            # itself connects immediately) or -f, so they are handled differently.
            try {
                $checkcluster = Get-Service -ComputerName $ipaddr -ErrorAction Stop | Where-Object { $_.Name -eq "ClusSvc" -and $_.Status -eq "Running" }
            }
            catch {
                Stop-Function -Message "Can't check services." -Target $SqlInstance -ErrorRecord $_
                return
            }

            if ($null -ne $checkcluster) {
                $clusterResource = Get-DbaCmObject -ClassName "MSCluster_Resource" -Namespace "root\mscluster" -ComputerName $hostname |
                    Where-Object { $_.Name.StartsWith("SQL Server") -and $_.OwnerGroup -eq "SQL Server ($instance)" }
            }

            # Take SQL Server offline so that it can be started in single-user mode
            if ($clusterResource.count -gt 0) {
                $isclustered = $true
                try {
                    $clusterResource | Where-Object { $_.Name -eq "SQL Server" } | ForEach-Object { $_.TakeOffline(60) }
                }
                catch {
                    $clusterResource | Where-Object { $_.Name -eq "SQL Server" } | ForEach-Object { $_.BringOnline(60) }
                    $clusterResource | Where-Object { $_.Name -ne "SQL Server" } | ForEach-Object { $_.BringOnline(60) }
                    Stop-Function -Message "Could not stop the SQL Service. Restarted SQL Service and quit." -ErrorRecord $_ -Target $SqlInstance
                    return
                }
            }
            else {
                try {
                    Stop-Service -InputObject $sqlservice -Force -ErrorAction Stop
                    Write-Message -Level Verbose -Message "Successfully stopped SQL service."
                }
                catch {
                    Start-Service -InputObject $instanceservices -ErrorAction Stop
                    Stop-Function -Message "Could not stop the SQL Service. Restarted SQL service and quit." -ErrorRecord $_ -Target $SqlInstance
                    return
                }
            }

            # /mReset-DbaAdmin Starts an instance of SQL Server in single-user mode and only allows this script to connect.
            Write-Message -Level Verbose -Message "Starting SQL Service from command line."
            try {
                if ($hostname -eq $env:COMPUTERNAME) {
                    $netstart = net start ""$displayname"" /mReset-DbaAdmin 2>&1
                    if ("$netstart" -notmatch "success") {
                        throw
                    }
                }
                else {
                    $netstart = Invoke-Command -ErrorAction Stop -Session $session -ArgumentList $displayname -ScriptBlock { net start ""$args"" /mReset-DbaAdmin } 2>&1
                    foreach ($line in $netstart) {
                        if ($line.length -gt 0) { Write-Message -Level Verbose -Message $line }
                    }
                }
            }
            catch {
                Stop-Service -InputObject $sqlservice -Force -ErrorAction SilentlyContinue

                if ($isclustered) {
                    $clusterResource | Where-Object Name -eq "SQL Server" | ForEach-Object { $_.BringOnline(60) }
                    $clusterResource | Where-Object Name -ne "SQL Server" | ForEach-Object { $_.BringOnline(60) }
                }
                else {
                    Start-Service -InputObject $instanceservices -ErrorAction SilentlyContinue
                }
                Stop-Function -Message "Couldn't execute net start command. Restarted services and quit." -ErrorRecord $_
                return
            }

            Write-Message -Level Verbose -Message "Reconnecting to SQL instance."
            try {
                $null = Invoke-ResetSqlCmd -SqlInstance $sqlinstance -Sql "SELECT 1" -ErrorAction Stop
            }
            catch {
                try {
                    Start-Sleep 3
                    $null = Invoke-ResetSqlCmd -SqlInstance $sqlinstance -Sql "SELECT 1" -ErrorAction Stop
                }
                catch {
                    Stop-Service Input-Object $sqlservice -Force -ErrorAction SilentlyContinue
                    if ($isclustered) {
                        $clusterResource | Where-Object { $_.Name -eq "SQL Server" } | ForEach-Object { $_.BringOnline(60) }
                        $clusterResource | Where-Object { $_.Name -ne "SQL Server" } | ForEach-Object { $_.BringOnline(60) }
                    }
                    else {
                        Start-Service -InputObject $instanceservices -ErrorAction SilentlyContinue
                    }
                    Stop-Function -Message "Could not stop the SQL Service. Restarted SQL Service and quit." -ErrorRecord $_
                }
            }

            # Get login. If it doesn't exist, create it.
            Write-Message -Level Verbose -Message "Adding login $login if it doesn't exist."
            if ($windowslogin -eq $true) {
                $sql = "IF NOT EXISTS (SELECT name FROM master.sys.server_principals WHERE name = '$login')
                    BEGIN CREATE LOGIN [$login] FROM WINDOWS END"
                if ($(Invoke-ResetSqlCmd -SqlInstance $sqlinstance -Sql $sql) -eq $false) {
                    Write-Message -Level Warning -Message "Couldn't create login."
                }

            }
            elseif ($login -ne "sa") {
                # Create new sql user
                $sql = "IF NOT EXISTS (SELECT name FROM master.sys.server_principals WHERE name = '$login')
                    BEGIN CREATE LOGIN [$login] WITH PASSWORD = '$(ConvertTo-PlainText $Password)', CHECK_POLICY = OFF, CHECK_EXPIRATION = OFF END"
                if ($(Invoke-ResetSqlCmd -SqlInstance $sqlinstance -Sql $sql) -eq $false) {
                    Write-Message -Level Warning -Message "Couldn't create login."
                }
            }

            # If $login is a SQL Login, Mixed mode authentication is required.
            if ($windowslogin -ne $true) {
                Write-Message -Level Verbose -Message "Enabling mixed mode authentication."
                Write-Message -Level Verbose -Message "Ensuring account is unlocked."
                $sql = "EXEC xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'Software\Microsoft\MSSQLServer\MSSQLServer', N'LoginMode', REG_DWORD, 2"
                if ($(Invoke-ResetSqlCmd -SqlInstance $sqlinstance -Sql $sql) -eq $false) {
                    Write-Message -Level Warning -Message "Couldn't set to Mixed Mode."
                }

                $sql = "ALTER LOGIN [$login] WITH CHECK_POLICY = OFF
                    ALTER LOGIN [$login] WITH PASSWORD = '$(ConvertTo-PlainText $Password)' UNLOCK"
                if ($(Invoke-ResetSqlCmd -SqlInstance $sqlinstance -Sql $sql) -eq $false) {
                    Write-Message -Level Warning -Message "Couldn't unlock account."
                }
            }

            Write-Message -Level Verbose -Message "Ensuring login is enabled."
            $sql = "ALTER LOGIN [$login] ENABLE"
            if ($(Invoke-ResetSqlCmd -SqlInstance $sqlinstance -Sql $sql) -eq $false) {
                Write-Message -Level Warning -Message "Couldn't enable login."
            }

            if ($login -ne "sa") {
                Write-Message -Level Verbose -Message "Ensuring login exists within sysadmin role."
                $sql = "EXEC sp_addsrvrolemember '$login', 'sysadmin'"
                if ($(Invoke-ResetSqlCmd -SqlInstance $sqlinstance -Sql $sql) -eq $false) {
                    Write-Message -Level Warning -Message "Couldn't add to sysadmin role."
                }
            }

            Write-Message -Level Verbose -Message "Finished with login tasks."
            Write-Message -Level Verbose -Message "Restarting SQL Server."
            Stop-Service -InputObject $sqlservice -Force -ErrorAction SilentlyContinue
            if ($isclustered -eq $true) {
                $clusterResource | Where-Object Name -eq "SQL Server" | ForEach-Object { $_.BringOnline(60) }
                $clusterResource | Where-Object Name -ne "SQL Server" | ForEach-Object { $_.BringOnline(60) }
            }
            else {
                Start-Service -InputObject $instanceservices -ErrorAction SilentlyContinue
            }
        }
    }
    end {
        Write-Message -Level Verbose -Message "Script complete!"
    }
}
function Resolve-DbaNetworkName {
    <#
        .SYNOPSIS
            Returns information about the network connection of the target computer including NetBIOS name, IP Address, domain name and fully qualified domain name (FQDN).

        .DESCRIPTION
            Retrieves the IPAddress, ComputerName from one computer.
            The object can be used to take action against its name or IPAddress.

            First ICMP is used to test the connection, and get the connected IPAddress.

            Multiple protocols (e.g. WMI, CIM, etc) are attempted before giving up.

            Important: Remember that FQDN doesn't always match "ComputerName dot Domain" as AD intends.
                There are network setup (google "disjoint domain") where AD and DNS do not match.
                "Full computer name" (as reported by sysdm.cpl) is the only match between the two,
                and it matches the "DNSHostName"  property of the computer object stored in AD.
                This means that the notation of FQDN that matches "ComputerName dot Domain" is incorrect
                in those scenarios.
                In other words, the "suffix" of the FQDN CAN be different from the AD Domain.

                This cmdlet has been providing good results since its inception but for lack of useful
                names some doubts may arise.
                Let this clear the doubts:
                - InputName: whatever has been passed in
                - ComputerName: hostname only
                - IPAddress: IP Address
                - DNSHostName: hostname only, coming strictly from DNS (as reported from the calling computer)
                - DNSDomain: domain only, coming strictly from DNS (as reported from the calling computer)
                - Domain: domain only, coming strictly from AD (i.e. the domain the ComputerName is joined to)
                - DNSHostEntry: Fully name as returned by DNS [System.Net.Dns]::GetHostEntry
                - FQDN: "legacy" notation of ComputerName "dot" Domain (coming from AD)
                - FullComputerName: Full name as configured from within the Computer (i.e. the only secure match between AD and DNS)

            So, if you need to use something, go with FullComputerName, always, as it is the most correct in every scenario.

        .PARAMETER ComputerName
            The Server that you're connecting to.
            This can be the name of a computer, a SMO object, an IP address or a SQL Instance.

        .PARAMETER Credential
            Credential object used to connect to the SQL Server as a different user

        .PARAMETER Turbo
            Resolves without accessing the server itself. Faster but may be less accurate because it relies on DNS only,
            so it may fail spectacularly for disjoin-domain setups. Also, everyone has its own DNS (i.e. results may vary
            changing the computer where the function runs)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Network, Resolve
            Author: Klaas Vandenberghe ( @PowerDBAKlaas )
            Editor: niphlod

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Resolve-DbaNetworkName

        .EXAMPLE
            Resolve-DbaNetworkName -ComputerName ServerA

            Returns a custom object displaying InputName, ComputerName, IPAddress, DNSHostName, DNSDomain, Domain, DNSHostEntry, FQDN, DNSHostEntry for ServerA

        .EXAMPLE
            Resolve-DbaNetworkName -SqlInstance sql2016\sqlexpress

            Returns a custom object displaying InputName, ComputerName, IPAddress, DNSHostName, DNSDomain, Domain, DNSHostEntry, FQDN, DNSHostEntry  for the SQL instance sql2016\sqlexpress

        .EXAMPLE
            Resolve-DbaNetworkName -SqlInstance sql2016\sqlexpress, sql2014

            Returns a custom object displaying InputName, ComputerName, IPAddress, DNSHostName, DNSDomain, Domain, DNSHostEntry, FQDN, DNSHostEntry  for the SQL instance sql2016\sqlexpress and sql2014

        .EXAMPLE
            Get-DbaRegisteredServer -SqlInstance sql2014 | Resolve-DbaNetworkName

            Returns a custom object displaying InputName, ComputerName, IPAddress, DNSHostName, Domain, FQDN for all SQL Servers returned by Get-DbaRegisteredServer
    #>
    [CmdletBinding()]
    param (
        [parameter(ValueFromPipeline)]
        [Alias('cn', 'host', 'ServerInstance', 'Server', 'SqlInstance')]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential] $Credential,
        [Alias('FastParrot')]
        [switch]$Turbo,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($Computer in $ComputerName) {
            $conn = $ipaddress = $null

            $OGComputer = $Computer

            if ($Computer.IsLocalhost) {
                $Computer = $env:COMPUTERNAME
            }
            else {
                $Computer = $Computer.ComputerName
            }

            if ($Turbo) {
                try {
                    Write-Message -Level VeryVerbose -Message "Resolving $Computer using .NET.Dns GetHostEntry"
                    $ipaddress = ([System.Net.Dns]::GetHostEntry($Computer)).AddressList[0].IPAddressToString
                    Write-Message -Level VeryVerbose -Message "Resolving $ipaddress using .NET.Dns GetHostByAddress"
                    $fqdn = [System.Net.Dns]::GetHostByAddress($ipaddress).HostName
                }
                catch {
                    try {
                        Write-Message -Level VeryVerbose -Message "Resolving $Computer and IP using .NET.Dns GetHostEntry"
                        $resolved = [System.Net.Dns]::GetHostEntry($Computer)
                        $ipaddress = $resolved.AddressList[0].IPAddressToString
                        $fqdn = $resolved.HostName
                    }
                    catch {
                        Stop-Function -Message "DNS name not found" -Continue -InnerErrorRecord $_
                    }
                }

                if ($fqdn -notmatch "\.") {
                    if ($computer.ComputerName -match "\.") {
                        $dnsdomain = $computer.ComputerName.Substring($computer.ComputerName.IndexOf(".") + 1)
                        $fqdn = "$resolved.$dnsdomain"
                    }
                    else {
                        $dnsdomain = "$env:USERDNSDOMAIN".ToLower()
                        if ($dnsdomain -match "\.") {
                            $fqdn = "$fqdn.$dnsdomain"
                        }
                    }
                }

                $hostname = $fqdn.Split(".")[0]

                [PSCustomObject]@{
                    InputName        = $OGComputer
                    ComputerName     = $hostname.ToUpper()
                    IPAddress        = $ipaddress
                    DNSHostname      = $hostname
                    DNSDomain        = $fqdn.Replace("$hostname.", "")
                    Domain           = $fqdn.Replace("$hostname.", "")
                    DNSHostEntry     = $fqdn
                    FQDN             = $fqdn
                    FullComputerName = $fqdn
                }

            }
            else {

                Write-Message -Level Verbose -Message "Connecting to $Computer"

                try {
                    $ipaddress = ((Test-Connection -ComputerName $Computer -Count 1 -ErrorAction Stop).Ipv4Address).IPAddressToString
                }
                catch {
                    try {
                        if ($env:USERDNSDOMAIN) {
                            $ipaddress = ((Test-Connection -ComputerName "$Computer.$env:USERDNSDOMAIN" -Count 1 -ErrorAction SilentlyContinue).Ipv4Address).IPAddressToString
                            $Computer = "$Computer.$env:USERDNSDOMAIN"
                        }
                    }
                    catch {
                        $Computer = $OGComputer
                        $ipaddress = ([System.Net.Dns]::GetHostEntry($Computer)).AddressList[0].IPAddressToString
                    }
                }

                if ($ipaddress) {
                    Write-Message -Level VeryVerbose -Message "IP Address from $Computer is $ipaddress"
                }
                else {
                    Write-Message -Level VeryVerbose -Message "No IP Address returned from $Computer"
                    Write-Message -Level VeryVerbose -Message "Using .NET.Dns to resolve IP Address"
                    return (Resolve-DbaNetworkName -ComputerName $Computer -Turbo)
                }

                if ($PSVersionTable.PSVersion.Major -gt 2) {
                    Write-Message -Level System -Message "Your PowerShell Version is $($PSVersionTable.PSVersion.Major)"
                    try {
                        try {
                            # if an alias (CNAME) is passed we should try to connect to the A name via CIM or WinRM
                            $ComputerNameIP = ([System.Net.Dns]::GetHostEntry($Computer)).AddressList[0].IPAddressToString
                            $RemoteComputer = [System.Net.Dns]::GetHostByAddress($ComputerNameIP).HostName
                        }
                        catch {
                            $RemoteComputer = $Computer
                        }
                        Write-Message -Level VeryVerbose -Message "Getting computer information from $RemoteComputer"
                        $ScBlock = {
                            $IPGProps = [System.Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties()
                            return [pscustomobject]@{
                                'DNSDomain' = $IPGProps.DomainName
                            }
                        }
                        if (Test-Bound "Credential") {
                            $conn = Get-DbaCmObject -ClassName win32_ComputerSystem -Computer $RemoteComputer -Credential $Credential -EnableException
                            $DNSSuffix = Invoke-Command2 -Computer $RemoteComputer -ScriptBlock $ScBlock -Credential $Credential -ErrorAction Stop
                        }
                        else {
                            $conn = Get-DbaCmObject -ClassName win32_ComputerSystem -Computer $RemoteComputer -EnableException
                            $DNSSuffix = Invoke-Command2 -Computer $RemoteComputer -ScriptBlock $ScBlock -ErrorAction Stop
                        }
                    }
                    catch {
                        Write-Message -Level Verbose -Message "Unable to get computer information from $Computer"
                    }

                    if (!$conn) {
                        Write-Message -Level Verbose -Message "No WMI/CIM from $Computer. Getting HostName via .NET.Dns"
                        try {
                            $fqdn = ([System.Net.Dns]::GetHostEntry($Computer)).HostName
                            $hostname = $fqdn.Split(".")[0]
                            $suffix = $fqdn.Replace("$hostname.", "")
                            if ($hostname -eq $fqdn) {
                                $suffix = ""
                            }
                            $conn = [PSCustomObject]@{
                                Name        = $Computer
                                DNSHostname = $hostname
                                Domain      = $suffix
                            }
                            $DNSSuffix = [PSCustomObject]@{
                                DNSDomain = $suffix
                            }
                        }
                        catch {
                            Stop-Function -Message "No .NET.Dns information from $Computer" -InnerErrorRecord $_ -Continue
                        }
                    }
                }
                if ($DNSSuffix.DNSDomain.Length -eq 0) {
                    $FullComputerName = $conn.DNSHostname
                }
                else {
                    $FullComputerName = $conn.DNSHostname + "." + $DNSSuffix.DNSDomain
                }
                try {
                    Write-Message -Level VeryVerbose -Message "Resolving $FullComputerName using .NET.Dns GetHostEntry"
                    $hostentry = ([System.Net.Dns]::GetHostEntry($FullComputerName)).HostName
                }
                catch {
                    Stop-Function -Message ".NET.Dns GetHostEntry failed for $FullComputerName" -InnerErrorRecord $_
                }

                $fqdn = "$($conn.DNSHostname).$($conn.Domain)"
                if ($fqdn -eq ".") {
                    Write-Message -Level VeryVerbose -Message "No full FQDN found. Setting to null"
                    $fqdn = $null
                }
                if ($FullComputerName -eq ".") {
                    Write-Message -Level VeryVerbose -Message "No DNS FQDN found. Setting to null"
                    $FullComputerName = $null
                }

                if ($FullComputerName -ne "." -and $FullComputerName -notmatch "\." -and $conn.Domain -match "\.") {
                    $d = $conn.Domain
                    $FullComputerName = "$FullComputerName.$d"
                }

                [PSCustomObject]@{
                    InputName        = $OGComputer
                    ComputerName     = $conn.Name
                    IPAddress        = $ipaddress
                    DNSHostName      = $conn.DNSHostname
                    DNSDomain        = $DNSSuffix.DNSDomain
                    Domain           = $conn.Domain
                    DNSHostEntry     = $hostentry
                    FQDN             = $fqdn.TrimEnd(".")
                    FullComputerName = $FullComputerName
                }
            }
        }
    }
}
function Restart-DbaSqlService {
    <#
        .SYNOPSIS
            Restarts SQL Server services on a computer.

        .DESCRIPTION
            Restarts the SQL Server related services on one or more computers. Will follow SQL Server service dependencies.

            Requires Local Admin rights on destination computer(s).

        .PARAMETER ComputerName
            The SQL Server (or server in general) that you're connecting to. This command handles named instances.

        .PARAMETER InstanceName
            Only affects services that belong to the specific instances.

        .PARAMETER Credential
            Credential object used to connect to the computer as a different user.

        .PARAMETER Type
            Use -Type to collect only services of the desired SqlServiceType.
            Can be one of the following: "Agent","Browser","Engine","FullText","SSAS","SSIS","SSRS"

        .PARAMETER Timeout
            How long to wait for the start/stop request completion before moving on. Specify 0 to wait indefinitely.

        .PARAMETER InputObject
            A collection of services from Get-DbaSqlService

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER WhatIf
            Shows what would happen if the cmdlet runs. The cmdlet is not run.

        .PARAMETER Confirm
            Prompts you for confirmation before running the cmdlet.

        .PARAMETER Force
            Will stop dependent SQL Server agents when stopping Engine services.

        .NOTES
            Tags: Service, SqlServer, Instance, Connect
            Author: Kirill Kravtsov( @nvarscar )

            Requires Local Admin rights on destination computer(s).

            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2017 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Restart-DbaSqlService

        .EXAMPLE
            Restart-DbaSqlService -ComputerName sqlserver2014a

            Restarts the SQL Server related services on computer sqlserver2014a.

        .EXAMPLE
            'sql1','sql2','sql3'| Get-DbaSqlService | Restart-DbaSqlService

            Gets the SQL Server related services on computers sql1, sql2 and sql3 and restarts them.

        .EXAMPLE
            Restart-DbaSqlService -ComputerName sql1,sql2 -Instance MSSQLSERVER

            Restarts the SQL Server services related to the default instance MSSQLSERVER on computers sql1 and sql2.

        .EXAMPLE
            Restart-DbaSqlService -ComputerName $MyServers -Type SSRS

            Restarts the SQL Server related services of type "SSRS" (Reporting Services) on computers in the variable MyServers.

        .EXAMPLE
            Restart-DbaSqlService -ComputerName sql1 -Type Engine -Force

            Restarts SQL Server database engine services on sql1 forcing dependent SQL Server Agent services to restart as well.
    #>
    [CmdletBinding(DefaultParameterSetName = "Server", SupportsShouldProcess = $true)]
    Param (
        [Parameter(ParameterSetName = "Server", Position = 1)]
        [Alias("cn", "host", "Server")]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [Alias("Instance")]
        [string[]]$InstanceName,
        [ValidateSet("Agent", "Browser", "Engine", "FullText", "SSAS", "SSIS", "SSRS")]
        [string[]]$Type,
        [parameter(ValueFromPipeline = $true, Mandatory = $true, ParameterSetName = "Service")]
        [Alias("ServiceCollection")]
        [object[]]$InputObject,
        [int]$Timeout = 30,
        [PSCredential]$Credential,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        $processArray = @()
        if ($PsCmdlet.ParameterSetName -eq "Server") {
            $serviceParams = @{ ComputerName = $ComputerName }
            if ($InstanceName) { $serviceParams.InstanceName = $InstanceName }
            if ($Type) { $serviceParams.Type = $Type }
            if ($Credential) { $serviceParams.Credential = $Credential }
            if ($EnableException) { $serviceParams.Silent = $EnableException }
            $InputObject = Get-DbaSqlService @serviceParams
        }
    }
    process {
        #Get all the objects from the pipeline before proceeding
        $processArray += $InputObject
    }
    end {
        $processArray = [array]($processArray | Where-Object { (!$InstanceName -or $_.InstanceName -in $InstanceName) -and (!$Type -or $_.ServiceType -in $Type) })
        foreach ($service in $processArray) {
            if ($Force -and $service.ServiceType -eq 'Engine' -and !($processArray | Where-Object { $_.ServiceType -eq 'Agent' -and $_.InstanceName -eq $service.InstanceName -and $_.ComputerName -eq $service.ComputerName })) {
                Write-Message -Level Verbose -Message "Adding Agent service to the list for service $($service.ServiceName) on $($service.ComputerName), since -Force has been specified"
                #Construct parameters to call Get-DbaSqlService
                $serviceParams = @{
                    ComputerName = $service.ComputerName
                    InstanceName = $service.InstanceName
                    Type         = 'Agent'
                }
                if ($Credential) { $serviceParams.Credential = $Credential }
                if ($EnableException) { $serviceParams.Silent = $EnableException }
                $processArray += @(Get-DbaSqlService @serviceParams)
            }
        }
        if ($processArray) {
            $services = Update-ServiceStatus -InputObject $processArray -Action 'stop' -Timeout $Timeout -EnableException $EnableException
            foreach ($service in ($services | Where-Object { $_.Status -eq 'Failed'})) {
                $service
            }
            $services = $services | Where-Object { $_.Status -eq 'Successful'}
            if ($services) {
                Update-ServiceStatus -InputObject $services -Action 'restart' -Timeout $Timeout -EnableException $EnableException
            }
        }
        else { Stop-Function -EnableException $EnableException -Message "No SQL Server services found with current parameters." }
    }
}
function Restore-DbaBackupFromDirectory {
    <#
        .SYNOPSIS
            Restores SQL Server databases from the backup directory structure created by Ola Hallengren's database maintenance scripts. Different structures coming soon.

        .DESCRIPTION
            Many SQL Server database administrators use Ola Hallengren's SQL Server Maintenance Solution which can be found at http://ola.hallengren.com

            Hallengren uses a predictable backup structure which made it relatively easy to create a script that can restore an entire SQL Server database instance, down to the master database (next version), to a new server. This script is intended to be used in the event that the originating SQL Server becomes unavailable, thus rendering my other SQL restore script (http://goo.gl/QmfQ6s) ineffective.

        .PARAMETER SqlInstance
            The SQL Server instance to which you will be restoring the database.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Path
            Specifies the full path to the directory that contains the database backups. The SQL Server service must have read access to this path.

        .PARAMETER ReuseSourceFolderStructure
            If this switch is enabled, the folder structure used on the instance where the backup was made will be recreated. By default, the database files will be restored to the default data and log directories for the instance you're restoring onto.

        .PARAMETER NoRecovery
            If this switch is enabled, the database is left in the No Recovery state to enable further backups to be added.

        .PARAMETER Force
            If this switch is enabled, any existing database matching the name of a database being restored will be overwritten.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .NOTES
            Tags: DisasterRecovery, Backup, Restore
            Requires: sysadmin access on destination SQL Server.

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Restore-SqlBackupFromDirectory

        .EXAMPLE
            Restore-SqlBackupFromDirectory -SqlInstance sqlcluster -Path \\fileserver\share\sqlbackups\SQLSERVER2014A

            All user databases contained within \\fileserver\share\sqlbackups\SQLSERVERA will be restored to sqlcluster, down the most recent full/differential/logs.

    #>
    #Requires -Version 3.0
    [CmdletBinding()]
    Param (
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [parameter(Mandatory = $true)]
        [string]$Path,
        [switch]$NoRecovery,
        [Alias("ReuseFolderStructure")]
        [switch]$ReuseSourceFolderStructure,
        [PSCredential]$SqlCredential,
        [switch]$Force
    )

    Write-Warning "This command is no longer supported. Please use Get-ChildItem | Restore-DbaDatabase instead"
}
function Restore-DbaDatabase {
    <#
    .SYNOPSIS
        Restores a SQL Server Database from a set of backupfiles

    .DESCRIPTION
        Upon being passed a list of potential backups files this command will scan the files, select those that contain SQL Server
        backup sets. It will then filter those files down to a set that can perform the requested restore, checking that we have a
        full restore chain to the point in time requested by the caller.

        The function defaults to working on a remote instance. This means that all paths passed in must be relative to the remote instance.
        XpDirTree will be used to perform the file scans


        Various means can be used to pass in a list of files to be considered. The default is to non recursively scan the folder
        passed in.

    .PARAMETER SqlInstance
        The SQL Server instance to restore to.

    .PARAMETER SqlCredential
        Allows you to login to servers using SQL Logins as opposed to Windows Auth/Integrated/Trusted.

    .PARAMETER Path
        Path to SQL Server backup files.

        Paths passed in as strings will be scanned using the desired method, default is a non recursive folder scan
        Accepts multiple paths separated by ','

        Or it can consist of FileInfo objects, such as the output of Get-ChildItem or Get-Item. This allows you to work with
        your own filestructures as needed

    .PARAMETER DatabaseName
        Name to restore the database under.
        Only works with a single database restore. If multiple database are found in the provided paths then we will exit

    .PARAMETER DestinationDataDirectory
        Path to restore the SQL Server backups to on the target instance.
        If only this parameter is specified, then all database files (data and log) will be restored to this location

    .PARAMETER DestinationLogDirectory
        Path to restore the database log files to.
        This parameter can only be specified alongside DestinationDataDirectory.

    .PARAMETER DestinationFileStreamDirectory
        Path to restore FileStream data to
        This parameter can only be specified alongside DestinationDataDirectory

    .PARAMETER RestoreTime
        Specify a DateTime object to which you want the database restored to. Default is to the latest point  available in the specified backups

    .PARAMETER NoRecovery
        Indicates if the databases should be recovered after last restore. Default is to recover

    .PARAMETER WithReplace
        Switch indicated is the restore is allowed to replace an existing database.

    .PARAMETER XpDirTree
        Switch that indicated file scanning should be performed by the SQL Server instance using xp_dirtree
        This will scan recursively from the passed in path
        You must have sysadmin role membership on the instance for this to work.

    .PARAMETER OutputScriptOnly
        Switch indicates that ONLY T-SQL scripts should be generated, no restore takes place

    .PARAMETER VerifyOnly
        Switch indicate that restore should be verified

    .PARAMETER MaintenanceSolutionBackup
        Switch to indicate the backup files are in a folder structure as created by Ola Hallengreen's maintenance scripts.
        This swith enables a faster check for suitable backups. Other options require all files to be read first to ensure we have an anchoring full backup. Because we can rely on specific locations for backups performed with OlaHallengren's backup solution, we can rely on file locations.

    .PARAMETER FileMapping
        A hashtable that can be used to move specific files to a location.
        $FileMapping = @{'DataFile1'='c:\restoredfiles\Datafile1.mdf';'DataFile3'='d:\DataFile3.mdf'}
        And files not specified in the mapping will be restored to their original location
        This Parameter is exclusive with DestinationDataDirectory

    .PARAMETER IgnoreLogBackup
        This switch tells the function to ignore transaction log backups. The process will restore to the latest full or differential backup point only

    .PARAMETER useDestinationDefaultDirectories
        Switch that tells the restore to use the default Data and Log locations on the target server. If they don't exist, the function will try to create them

    .PARAMETER ReuseSourceFolderStructure
        By default, databases will be migrated to the destination Sql Server's default data and log directories. You can override this by specifying -ReuseSourceFolderStructure.
        The same structure on the SOURCE will be kept exactly, so consider this if you're migrating between different versions and use part of Microsoft's default Sql structure (MSSql12.INSTANCE, etc)

        *Note, to reuse destination folder structure, specify -WithReplace

    .PARAMETER DestinationFilePrefix
        This value will be prefixed to ALL restored files (log and data). This is just a simple string prefix. If you want to perform more complex rename operations then please use the FileMapping parameter

        This will apply to all file move options, except for FileMapping

    .PARAMETER DestinationFileSuffix
        This value will be suffixed to ALL restored files (log and data). This is just a simple string suffix. If you want to perform more complex rename operations then please use the FileMapping parameter

        This will apply to all file move options, except for FileMapping

    .PARAMETER RestoredDatabaseNamePrefix
        A string which will be prefixed to the start of the restore Database's Name
        Useful if restoring a copy to the same sql server for testing.

    .PARAMETER TrustDbBackupHistory
        This switch can be used when piping the output of Get-DbaBackupHistory or Backup-DbaDatabase into this command.
        It allows the user to say that they trust that the output from those commands is correct, and skips the file header read portion of the process. This means a faster process, but at the risk of not knowing till halfway through the restore that something is wrong with a file.

    .PARAMETER MaxTransferSize
        Parameter to set the unit of transfer. Values must be a multiple by 64kb

    .PARAMETER Blocksize
        Specifies the block size to use. Must be one of 0.5kb,1kb,2kb,4kb,8kb,16kb,32kb or 64kb
        Can be specified in bytes
        Refer to https://msdn.microsoft.com/en-us/library/ms178615.aspx for more detail

    .PARAMETER BufferCount
        Number of I/O buffers to use to perform the operation.
        Refer to https://msdn.microsoft.com/en-us/library/ms178615.aspx for more detail

    .PARAMETER XpNoRecurse
        If specified, prevents the XpDirTree process from recursing (its default behaviour)

    .PARAMETER DirectoryRecurse
        If specified the specified directory will be recursed into

    .PARAMETER Continue
        If specified we will to attempt to recover more transaction log backups onto  database(s) in Recovering or Standby states

    .PARAMETER StandbyDirectory
        If a directory is specified the database(s) will be restored into a standby state, with the standby file placed into this directory (which must exist, and be writable by the target Sql Server instance)

    .PARAMETER AzureCredential
        The name of the SQL Server credential to be used if restoring from an Azure hosted backup

    .PARAMETER ReplaceDbNameInFile
        If switch set and occurence of the original database's name in a data or log file will be replace with the name specified in the Databasename parameter

    .PARAMETER Recover
        If set will perform recovery on the indicated database

    .PARAMETER AllowContinue
        By default, Restore-DbaDatabase will stop restoring any databases if it comes across an error.
        Use this switch to enable it to restore all databases without issues.

    .PARAMETER GetBackupInformation
        Passing a string value into this parameter will cause a global variable to be created holding the output of Get-DbaBackupInformation

    .PARAMETER SelectBackupInformation
        Passing a string value into this parameter will cause a global variable to be created holding the output of Select-DbaBackupInformation

    .PARAMETER FormatBackupInformation
        Passing a string value into this parameter will cause a global variable to be created holding the output of Format-DbaBackupInformation

    .PARAMETER TestBackupInformation
        Passing a string value into this parameter will cause a global variable to be created holding the output of Test-DbaBackupInformation

    .PARAMETER StopAfterGetBackupInformation
        Switch which will cause the function to exit after returning GetBackupInformation

    .PARAMETER StopAfterSelectBackupInformation
        Switch which will cause the function to exit after returning SelectBackupInformation

    .PARAMETER StopAfterFormatBackupInformation
         Switch which will cause the function to exit after returning FormatBackupInformation

    .PARAMETER StopAfterTestBackupInformation
         Switch which will cause the function to exit after returning TestBackupInformation

    .PARAMETER StatementTimeOut
        Timeout in minutes. Defaults to infinity (restores can take a while.)

    .PARAMETER KeepCDC
        Indicates whether CDC information should be restored as part of the database

    .PARAMETER PageRestore
        Passes in an object from Get-DbaSuspectPages containing suspect pages from a single database.
        Setting this Parameter will cause an Online Page restore if the target Instance is Enterprise Edition, or offline if not.
        This will involve taking a tail log backup, so you must check your restore chain once it has completed

    .PARAMETER PageRestoreTailFolder
        This parameter passes in a location for the tail log backup required for page level restore

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .PARAMETER Confirm
        Prompts to confirm certain actions

    .PARAMETER WhatIf
        Shows what would happen if the command would execute, but does not actually perform the command

    .EXAMPLE
        Restore-DbaDatabase -SqlInstance server1\instance1 -Path \\server2\backups

        Scans all the backup files in \\server2\backups, filters them and restores the database to server1\instance1

    .EXAMPLE
        Restore-DbaDatabase -SqlInstance server1\instance1 -Path \\server2\backups -MaintenanceSolutionBackup -DestinationDataDirectory c:\restores

        Scans all the backup files in \\server2\backups$ stored in an Ola Hallengren style folder structure,
        filters them and restores the database to the c:\restores folder on server1\instance1

    .EXAMPLE
        Get-ChildItem c:\SQLbackups1\, \\server\sqlbackups2 | Restore-DbaDatabase -SqlInstance server1\instance1

        Takes the provided files from multiple directories and restores them on  server1\instance1

    .EXAMPLE
        $RestoreTime = Get-Date('11:19 23/12/2016')
        Restore-DbaDatabase -SqlInstance server1\instance1 -Path \\server2\backups -MaintenanceSolutionBackup -DestinationDataDirectory c:\restores -RestoreTime $RestoreTime

        Scans all the backup files in \\server2\backups stored in an Ola Hallengren style folder structure,
        filters them and restores the database to the c:\restores folder on server1\instance1 up to 11:19 23/12/2016

    .EXAMPLE
        Restore-DbaDatabase -SqlInstance server1\instance1 -Path \\server2\backups -DestinationDataDirectory c:\restores -OutputScriptOnly | Select-Object -ExpandProperty Tsql | Out-File -Filepath c:\scripts\restore.sql

        Scans all the backup files in \\server2\backups stored in an Ola Hallengren style folder structure,
        filters them and generate the T-SQL Scripts to restore the database to the latest point in time,
        and then stores the output in a file for later retrieval

    .EXAMPLE
        Restore-DbaDatabase -SqlInstance server1\instance1 -Path c:\backups -DestinationDataDirectory c:\DataFiles -DestinationLogDirectory c:\LogFile

        Scans all the files in c:\backups and then restores them onto the SQL Server Instance server1\instance1, placing data files
        c:\DataFiles and all the log files into c:\LogFiles

    .EXAMPLE
        Restore-DbaDatabase -SqlInstance server1\instance1 -Path http://demo.blob.core.windows.net/backups/dbbackup.bak -AzureCredential MyAzureCredential

        Will restore the backup held at  http://demo.blob.core.windows.net/backups/dbbackup.bak to server1\instance1. The connection to Azure will be made using the
        credential MyAzureCredential held on instance Server1\instance1

    .EXAMPLE
        $File = Get-ChildItem c:\backups, \\server1\backups -recurse
        $File | Restore-DbaDatabase -SqlInstance Server1\Instance -useDestinationDefaultDirectories

        This will take all of the files found under the folders c:\backups and \\server1\backups, and pipeline them into
        Restore-DbaDatabase. Restore-DbaDatabase will then scan all of the files, and restore all of the databases included
        to the latest point in time covered by their backups. All data and log files will be moved to the default SQL Server
        folder for those file types as defined on the target instance.

    .EXAMPLE
        $files = Get-ChildItem C:\dbatools\db1

        #Restore database to a point in time
        $files | Restore-DbaDatabase -SqlInstance server\instance1 `
                    -DestinationFilePrefix prefix -DatabaseName Restored  `
                    -RestoreTime (get-date "14:58:30 22/05/2017") `
                    -NoRecovery -WithReplace -StandbyDirectory C:\dbatools\standby

        #It's in standby so we can peek at it
        Invoke-Sqlcmd2 -ServerInstance server\instance1 -Query "select top 1 * from Restored.dbo.steps order by dt desc"

        #Not quite there so let's roll on a bit:
        $files | Restore-DbaDatabase -SqlInstance server\instance1 `
                    -DestinationFilePrefix prefix -DatabaseName Restored `
                    -continue -WithReplace -RestoreTime (get-date "15:09:30 22/05/2017") `
                    -StandbyDirectory C:\dbatools\standby

        Invoke-Sqlcmd2 -ServerInstance server\instance1 -Query "select top 1 * from restored.dbo.steps order by dt desc"

        Restore-DbaDatabase -SqlInstance server\instance1 `
                    -DestinationFilePrefix prefix -DatabaseName Restored `
                    -continue -WithReplace

        In this example we step through the backup files held in c:\dbatools\db1 folder.
        First we restore the database to a point in time in standby mode. This means we can check some details in the databases
        We then roll it on a further 9 minutes to perform some more checks
        And finally we continue by rolling it all the way forward to the latest point in the backup.
        At each step, only the log files needed to roll the database forward are restored.

    .EXAMPLE
        Restore-DbaDatabase -SqlInstance server\instance1 -Path c:\backups -DatabaseName example1 -WithNoRecovery
        Restore-DbaDatabase -SqlInstance server\instance1 -Recover -DatabaseName example1

    .EXAMPLE
        $SuspectPage = Get-DbaSuspectPage -SqlInstance server\instance1 -Database ProdFinance
        Get-DbaBackupHistory - SqlInstance server\instance1 -Database -ProdFinance -Last | Restore-DbaDatabase -PageRestore $SuspectPage -PageRestoreTailFolder c:\temp -TrustDbBackupHistory -AllowContinues

        Gets a list of Suspect Pages using Get-DbaSuspectPage. The uses Get-DbaBackupHistory and Restore-DbaDatabase to perform a restore of the suspect pages and bring them up to date
        If server\instance1 is Enterprise edition this will be done online, if not it will be performed offline
        AllowContinue is required to make sure we cope with existing files

    .NOTES
        Tags: DisasterRecovery, Backup, Restore
        Author: Stuart Moore (@napalmgram), stuart-moore.com

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT
#>
    [CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = "Restore")]
    param (
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "Restore")]
        [parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "RestorePage")]
        [object[]]$Path,
        [parameter(ValueFromPipeline = $true)]
        [Alias("Name")]
        [object[]]$DatabaseName,
        [parameter(ParameterSetName = "Restore")]
        [String]$DestinationDataDirectory,
        [parameter(ParameterSetName = "Restore")]
        [String]$DestinationLogDirectory,
        [parameter(ParameterSetName = "Restore")]
        [String]$DestinationFileStreamDirectory,
        [parameter(ParameterSetName = "Restore")]
        [DateTime]$RestoreTime = (Get-Date).AddYears(1),
        [parameter(ParameterSetName = "Restore")]
        [switch]$NoRecovery,
        [parameter(ParameterSetName = "Restore")]
        [switch]$WithReplace,
        [parameter(ParameterSetName = "Restore")]
        [Switch]$XpDirTree,
        [switch]$OutputScriptOnly,
        [parameter(ParameterSetName = "Restore")]
        [switch]$VerifyOnly,
        [parameter(ParameterSetName = "Restore")]
        [switch]$MaintenanceSolutionBackup,
        [parameter(ParameterSetName = "Restore")]
        [hashtable]$FileMapping,
        [parameter(ParameterSetName = "Restore")]
        [switch]$IgnoreLogBackup,
        [parameter(ParameterSetName = "Restore")]
        [switch]$useDestinationDefaultDirectories,
        [parameter(ParameterSetName = "Restore")]
        [switch]$ReuseSourceFolderStructure,
        [parameter(ParameterSetName = "Restore")]
        [string]$DestinationFilePrefix = '',
        [parameter(ParameterSetName = "Restore")]
        [Alias("RestoredDatababaseNamePrefix")]
        [string]$RestoredDatabaseNamePrefix,
        [parameter(ParameterSetName = "Restore")]
        [parameter(ParameterSetName = "RestorePage")]
        [switch]$TrustDbBackupHistory,
        [parameter(ParameterSetName = "Restore")]
        [parameter(ParameterSetName = "RestorePage")]
        [int]$MaxTransferSize,
        [parameter(ParameterSetName = "Restore")]
        [parameter(ParameterSetName = "RestorePage")]
        [int]$BlockSize,
        [parameter(ParameterSetName = "Restore")]
        [parameter(ParameterSetName = "RestorePage")]
        [int]$BufferCount,
        [parameter(ParameterSetName = "Restore")]
        [switch]$DirectoryRecurse,
        [switch]$EnableException ,
        [parameter(ParameterSetName = "Restore")]
        [string]$StandbyDirectory,
        [parameter(ParameterSetName = "Restore")]
        [switch]$Continue,
        [string]$AzureCredential,
        [parameter(ParameterSetName = "Restore")]
        [switch]$ReplaceDbNameInFile,
        [parameter(ParameterSetName = "Restore")]
        [string]$DestinationFileSuffix,
        [parameter(ParameterSetName = "Recovery")]
        [switch]$Recover,
        [parameter(ParameterSetName = "Restore")]
        [switch]$KeepCDC,
        [switch]$AllowContinue,
        [string]$GetBackupInformation,
        [switch]$StopAfterGetBackupInformation,
        [string]$SelectBackupInformation,
        [switch]$StopAfterSelectBackupInformation,
        [string]$FormatBackupInformation,
        [switch]$StopAfterFormatBackupInformation,
        [string]$TestBackupInformation,
        [switch]$StopAfterTestBackupInformation,
        [parameter(Mandatory = $true, ParameterSetName = "RestorePage")]
        [object]$PageRestore,
        [parameter(Mandatory = $true, ParameterSetName = "RestorePage")]
        [string]$PageRestoreTailFolder,
        [int]$StatementTimeout = 0

    )
    begin {
        Write-Message -Level InternalComment -Message "Starting"
        Write-Message -Level Debug -Message "Parameters bound: $($PSBoundParameters.Keys -join ", ")"
        #[string]$DatabaseName = 'testparam'
        #region Validation
        try {
            $RestoreInstance = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            return
        }
        if ($PSCmdlet.ParameterSetName -eq "Restore") {
            $useDestinationDefaultDirectories = $true
            $paramCount = 0

            if (!(Test-Bound "AllowContinue") -and $true -ne $AllowContinue) {
                $AllowContinue = $false
            }
            if (Test-Bound "FileMapping") {
                $paramCount += 1
            }
            if (Test-Bound "ReuseSourceFolderStructure") {
                $paramCount += 1
            }
            if (Test-Bound "DestinationDataDirectory") {
                $paramCount += 1
            }
            if ($paramCount -gt 1) {
                Stop-Function -Category InvalidArgument -Message "You've specified incompatible Location parameters. Please only specify one of FileMapping, ReuseSourceFolderStructure or DestinationDataDirectory"
                return
            }
            if (($ReplaceDbNameInFile) -and !(Test-Bound "DatabaseName")) {
                Stop-Function -Category InvalidArgument -Message "To use ReplaceDbNameInFile you must specify DatabaseName"
                return
            }

            if ((Test-Bound "DestinationLogDirectory") -and (Test-Bound "ReuseSourceFolderStructure")) {
                Stop-Function -Category InvalidArgument -Message "The parameters DestinationLogDirectory and UseDestinationDefaultDirectories are mutually exclusive"
                return
            }
            if ((Test-Bound "DestinationLogDirectory") -and -not (Test-Bound "DestinationDataDirectory")) {
                Stop-Function -Category InvalidArgument -Message "The parameter DestinationLogDirectory can only be specified together with DestinationDataDirectory"
                return
            }
            if ((Test-Bound "DestinationFileStreamDirectory") -and (Test-Bound "ReuseSourceFolderStructure")) {
                Stop-Function -Category InvalidArgument -Message "The parameters DestinationFileStreamDirectory and UseDestinationDefaultDirectories are mutually exclusive"
                return
            }
            if ((Test-Bound "DestinationFileStreamDirectory") -and -not (Test-Bound "DestinationDataDirectory")) {
                Stop-Function -Category InvalidArgument -Message "The parameter DestinationFileStreamDirectory can only be specified together with DestinationDataDirectory"
                return
            }
            if (($null -ne $FileMapping) -or $ReuseSourceFolderStructure -or ($DestinationDataDirectory -ne '')) {
                $useDestinationDefaultDirectories = $false
            }
            if (($MaxTransferSize % 64kb) -ne 0 -or $MaxTransferSize -gt 4mb) {
                Stop-Function -Category InvalidArgument -Message "MaxTransferSize value must be a multiple of 64kb and no greater than 4MB"
                return
            }
            if ($BlockSize) {
                if ($BlockSize -notin (0.5kb, 1kb, 2kb, 4kb, 8kb, 16kb, 32kb, 64kb)) {
                    Stop-Function -Category InvalidArgument -Message "Block size must be one of 0.5kb,1kb,2kb,4kb,8kb,16kb,32kb,64kb"
                    return
                }
            }
            if ('' -ne $StandbyDirectory) {
                if (!(Test-DbaSqlPath -Path $StandbyDirectory -SqlInstance $RestoreInstance)) {
                    Stop-Function -Message "$SqlSever cannot see the specified Standby Directory $StandbyDirectory" -Target $SqlInstance
                    return
                }
            }
            if ($KeepCDC -and ($NoRecovery -or ('' -ne $StandbyDirectory))) {
                Stop-Function -Category InvalidArgument -Message "KeepCDC cannot be specified with Norecovery or Standby as it needs recovery to work"
                return
            }
            if ($Continue) {
                $ContinuePoints = Get-RestoreContinuableDatabase -SqlInstance $RestoreInstance
                #$WithReplace = $true
                #$ContinuePoints
            }
            if (!($PSBoundParameters.ContainsKey("DataBasename"))) {
                $PipeDatabaseName = $true
            }

        }



        # changing statement timeout to $StatementTimeout
        if ($StatementTimeout -eq 0) {
            Write-Message -Level Verbose -Message "Changing statement timeout to infinity"
        }
        else {
            Write-Message -Level Verbose -Message "Changing statement timeout to ($StatementTimeout) minutes"
        }
        $RestoreInstance.ConnectionContext.StatementTimeout = ($StatementTimeout * 60)
        #endregion Validation

        $isLocal = [dbavalidate]::IsLocalHost($SqlInstance.ComputerName)

        if ($useDestinationDefaultDirectories) {
            $DefaultPath = (Get-DbaDefaultPath -SqlInstance $RestoreInstance)
            $DestinationDataDirectory = $DefaultPath.Data
            $DestinationLogDirectory = $DefaultPath.Log
        }

        $BackupHistory = @()
        #$useDestinationDefaultDirectories = $true
    }
    process {
        if (Test-FunctionInterrupt) { return }
        if ($PSCmdlet.ParameterSetName -like "Restore*") {
            if ($PipeDatabaseName -eq $true) {$DatabaseName = ''}
            Write-Message -message "ParameterSet  = Restore" -Level Verbose
            if ($TrustDbBackupHistory -or $path[0].GetType().ToString() -eq 'Sqlcollaborative.Dbatools.Database.BackupHistory') {
                foreach ($f in $path) {
                    Write-Message -Level Verbose -Message "Trust Database Backup History Set"
                    if ("BackupPath" -notin $f.PSobject.Properties.name) {
                        Write-Message -Level Verbose -Message "adding BackupPath - $($_.Fullname)"
                        $f = $f | Select-Object *, @{ Name = "BackupPath"; Expression = { $_.FullName } }
                    }
                    if ("DatabaseName" -notin $f.PSobject.Properties.name) {
                        $f = $f | Select-Object *, @{ Name = "DatabaseName"; Expression = { $_.Database } }
                    }
                    if ("Database" -notin $f.PSobject.Properties.name) {
                        $f = $f | Select-Object *, @{ Name = "Database"; Expression = { $_.DatabaseName } }
                    }
                    if ("Type" -notin $f.PSobject.Properties.name) {
                        #$f = $f | Select-Object *,  @{Name="Type";Expression={"Full"}}
                    }
                    if ("BackupSetGUID" -notin $f.PSobject.Properties.name) {
                        #This line until Get-DbaBackupHistory gets fixed
                        #$f = $f | Select-Object *, @{ Name = "BackupSetGUID"; Expression = { $_.BackupSetupID } }
                        #This one once it's sorted:
                        $f = $f | Select-Object *, @{Name = "BackupSetGUID"; Expression = {$_.BackupSetID}}
                    }
                    if ($f.BackupPath -like 'http*' -and '' -eq $AzureCredential) {
                        Stop-Function -Message "At least one Azure backup passed in, and no Credential supplied. Stopping"
                        return
                    }

                    $BackupHistory += $F | Select-Object *, @{ Name = "ServerName"; Expression = { $_.SqlInstance } }, @{ Name = "BackupStartDate"; Expression = { $_.Start -as [DateTime] } }

                }
            }
            else {
                $files = @()
                foreach ($f in $Path) {
                    if ($f -is [System.IO.FileSystemInfo]) {
                        $files += $f.fullname
                    }
                    else {
                        $files += $f
                    }
                }
                Write-Message -Level Verbose -Message "Unverified input, full scans - $($files -join ';')"
                $BackupHistory += Get-DbaBackupInformation -SqlInstance $RestoreInstance -SqlCredential $SqlCredential -Path $files -DirectoryRecurse:$DirectoryRecurse -MaintenanceSolution:$MaintenanceSolutionBackup -IgnoreLogBackup:$IgnoreLogBackup -AzureCredential $AzureCredential
            }
            if ($PSCmdlet.ParameterSetName -eq "RestorePage") {
                if (-not (Test-DbaSqlPath -SqlInstance $RestoreInstance -Path $PageRestoreTailFolder)) {
                    Stop-Function -Message "Instance $RestoreInstance cannot read $PageRestoreTailFolder, cannot proceed" -Target $PageRestoreTailFolder
                    return
                }
                $WithReplace = $true
            }
        }
        elseif ($PSCmdlet.ParameterSetName -eq "Recovery") {
            Write-Message -Message "$($Database.count) databases to recover" -level Verbose
            ForEach ($DataBase in $DatabaseName) {
                if ($database -is [object]) {
                    #We've got an object, try the normal options Database, DatabaseName, Name
                    if ("Database" -in $Database.PSobject.Properties.name) {
                        [string]$DataBase = $database.Database
                    }
                    elseif ("DatabaseName" -in $Database.PSobject.Properties.name) {
                        [string]$DataBase = $database.DatabaseName
                    }
                    elseif ("Name" -in $Database.PSobject.Properties.name) {
                        [string]$DataBase = $database.name
                    }
                }
                Write-Verbose "existence - $($RestoreInstance.Databases[$DataBase].State)"
                if ($RestoreInstance.Databases[$DataBase].State -ne 'Existing') {
                    Write-Message -Message "$Database does not exist on $RestoreInstance" -level Warning
                    Continue

                }
                if ($RestoreInstance.Databases[$Database].Status -ne "Restoring") {
                    Write-Message -Message "$Database on $RestoreInstance is not in a Restoring State" -Level Warning
                    Continue

                }
                $RestoreComplete = $true
                $RecoverSql = "RESTORE DATABASE $Database WITH RECOVERY"
                Write-Message -Message "Recovery Sql Query - $RecoverSql" -level verbose
                Try {
                    $RestoreInstance.query($RecoverSql)
                }
                Catch {
                    $RestoreComplete = $False
                    $ExitError = $_.Exception.InnerException
                    Write-Message -Level Warning -Message "Failed to recover $Database on $RestoreInstance, `n $ExitError"
                }
                Finally {
                    [PSCustomObject]@{
                        SqlInstance     = $SqlInstance
                        DatabaseName    = $Database
                        RestoreComplete = $RestoreComplete
                        Scripts         = $RecoverSql
                    }
                }
            }
        }
    }
    end {
        if (Test-FunctionInterrupt) { return }
        if ($PSCmdlet.ParameterSetName -like "Restore*") {
            if ($BackupHistory.Count -eq 0) {
                Write-Message -Level Warning -Message "No backups passed through. `n This could mean the SQL instance cannot see the referenced files, the file's headers could not be read or some other issue"
                return
            }
            Write-Message -message "Processing DatabaseName - $DatabaseName" -Level Verbose
            $FilteredBackupHistory = @()
            if (Test-Bound -ParameterName GetBackupInformation) {
                Write-Message -Message "Setting $GetBackupInformation to BackupHistory" -Level Verbose
                Set-Variable -Name $GetBackupInformation -Value $BackupHistory -Scope Global
            }
            if ($StopAfterGetBackupInformation) {
                return
            }
            
            $FilteredBackupHistory = $BackupHistory | Select-DbaBackupInformation -RestoreTime $RestoreTime -IgnoreLogs:$IgnoreLogBackups -ContinuePoints $ContinuePoints
            
            if (Test-Bound -ParameterName SelectBackupInformation) {
                Write-Message -Message "Setting $SelectBackupInformation to FilteredBackupHistory" -Level Verbose
                Set-Variable -Name $SelectBackupInformation -Value $FilteredBackupHistory -Scope Global
                
            }
            if ($StopAfterSelectBackupInformation) {
                return
            }
            
            $null = $FilteredBackupHistory | Format-DbaBackupInformation -DataFileDirectory $DestinationDataDirectory -LogFileDirectory $DestinationLogDirectory -DestinationFileStreamDirectory $DestinationFileStreamDirectory -DatabaseFileSuffix $DestinationFileSuffix -DatabaseFilePrefix $DestinationFilePrefix -DatabaseNamePrefix $RestoredDatabaseNamePrefix -ReplaceDatabaseName $DatabaseName -Continue:$Continue -ReplaceDbNameInFile:$ReplaceDbNameInFile -FileMapping $FileMapping
            
            if (Test-Bound -ParameterName FormatBackupInformation) {
                Set-Variable -Name $FormatBackupInformation -Value $FilteredBackupHistory -Scope Global
            }
            if ($StopAfterFormatBackupInformation) {
                return
            }
            
            try {
                Write-Message -Level Verbose -Message "VerifyOnly = $VerifyOnly"
                $null = $FilteredBackupHistory | Test-DbaBackupInformation -SqlInstance $RestoreInstance -WithReplace:$WithReplace -Continue:$Continue -VerifyOnly:$VerifyOnly -EnableException:$true
            }
            catch {
                Stop-Function -ErrorRecord $_ -Message "Failure" -Continue
            }
            
            if (Test-Bound -ParameterName TestBackupInformation) {
                Set-Variable -Name $TestBackupInformation -Value $FilteredBackupHistory -Scope Global
            }
            if ($StopAfterTestBackupInformation) {
                return
            }
            $DbVerfied = ($FilteredBackupHistory | Where-Object { $_.IsVerified -eq $True } | Select-Object -Property Database -Unique).Database -join ','
            Write-Message -Message "$DbVerfied passed testing" -Level Verbose
            
            if (($FilteredBackupHistory | Where-Object { $_.IsVerified -eq $True }).count -lt $FilteredBackupHistory.count) {
                $DbUnVerified = ($FilteredBackupHistory | Where-Object { $_.IsVerified -eq $False } | Select-Object -Property Database -Unique).Database -join ','
                if ($AllowContinue) {
                    Write-Message -Message "$DbUnverified failed testing, AllowContinue set" -Level Verbose
                }
                else {
                    Stop-Function -Message "Database $DbUnverified failed testing, AllowContinue not set, exiting"
                    return
                }
            }
            
            If ($PSCmdlet.ParameterSetName -eq "RestorePage") {
                if (($FilteredBackupHistory.Database | select-Object -unique | Measure-Object).count -ne 1) {
                    Stop-Function -Message "Must only 1 database passed in for Page Restore. Sorry"
                    return
                }
                else {
                    $WithReplace = $false
                    $PageDb = ($FilteredBackupHistory.Database | select-Object -unique).Database
                }
            }
            Write-Message -Message "Passing in to restore" -Level Verbose
            if ($PSCmdlet.ParameterSetName -eq "RestorePage" -and $RestoreInstance.Edition -notlike '*Enterprise*') {
                Write-Message -Message "Taking Tail log backup for page restore for non-Enterprise" -Level Verbose
                $TailBackup = Backup-DbaDatabase -SqlInstance $RestoreInstance -Database $DatabaseName -Type Log -BackupDirectory $PageRestoreTailFolder -Norecovery -CopyOnly
            }
            try {
                $FilteredBackupHistory | Where-Object { $_.IsVerified -eq $true } | Invoke-DbaAdvancedRestore -SqlInstance $RestoreInstance -WithReplace:$WithReplace -RestoreTime $RestoreTime -StandbyDirectory $StandbyDirectory -NoRecovery:$NoRecovery -Continue:$Continue -OutputScriptOnly:$OutputScriptOnly -BlockSize $BlockSize -MaxTransferSize $MaxTransferSize -Buffercount $Buffercount -KeepCDC:$KeepCDC -VerifyOnly:$VerifyOnly -PageRestore $PageRestore -EnableException -AzureCredential $AzureCredential
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_ -Continue -Target $RestoreInstance
            }
            if ($PSCmdlet.ParameterSetName -eq "RestorePage" ) {
                if ($RestoreInstace.Edition -like '*Enterprise*') {
                    Write-Message -Message "Taking Tail log backup for page restore for Enterprise" -Level Verbose
                    $TailBackup = Backup-DbaDatabase -SqlInstance $RestoreInstance -Database $DatabaseName -Type Log -BackupDirectory $PageRestoreTailFolder -Norecovery -CopyOnly
                }
                Write-Message -Message "Restoring Tail log backup for page restore" -Level Verbose
                $TailBackup | Restore-DbaDatabase -SqlInstance $RestoreInstance -TrustDbBackupHistory -NoRecovery -OutputScriptOnly:$OutputScriptOnly -BlockSize $BlockSize -MaxTransferSize $MaxTransferSize -Buffercount $Buffercount -Continue
                Restore-DbaDatabase -SqlInstance $RestoreInstance -Recover -Database $DatabaseName -OutputScriptOnly:$OutputScriptOnly
            }

        }
    }
}
function Restore-DbaDbCertificate {
    <#
.SYNOPSIS
Imports certificates from .cer files using SMO.

.DESCRIPTION
Imports certificates from.cer files using SMO.

.PARAMETER SqlInstance
The SQL Server to create the certificates on.

.PARAMETER Path
The Path the contains the certificate and private key files. The path can be a directory or a specific certificate.

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Password
Secure string used to decrypt the private key.

.PARAMETER Database
The database where the certificate imports into. Defaults to master.

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Migration, Certificate
Author: Jess Pomfret (@jpomfret)

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
Restore-DbaDbCertificate -SqlInstance Server1 -Path \\Server1\Certificates -password (ConvertTo-SecureString -force -AsPlainText GoodPass1234!!)
Imports all the certificates in the specified path.

#>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [object[]]$Path,
        [object]$Database = "master",
        [Security.SecureString]$Password = (Read-Host "Password" -AsSecureString),
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Retore-DbaDatabaseCertificate

        function new-smocert ($directory, $certname) {
            if ($Pscmdlet.ShouldProcess("$cert on $SqlInstance", "Importing Certificate")) {
                $smocert = New-Object Microsoft.SqlServer.Management.Smo.Certificate
                $smocert.Name = $certname
                $smocert.Parent = $server.Databases[$Database]
                Write-Message -Level Verbose -Message "Creating Certificate: $certname"
                try {
                    $fullcertname = "$directory\$certname.cer"
                    $privatekey = "$directory\$certname.pvk"
                    Write-Message -Level Verbose -Message "Full certificate path: $fullcertname"
                    Write-Message -Level Verbose -Message "Private key: $privatekey"
                    $fromfile = 1
                    $smocert.Create($fullcertname, $fromfile, $privatekey, [System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($password)), [System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($password)))
                    $smocert
                }
                catch {
                    Write-Message -Level Warning -Message $_ -ErrorRecord $_ -Target $instance
                }
            }
        }

        try {
            Write-Message -Level Verbose -Message "Connecting to $SqlInstance"
            $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $sqlcredential
        }
        catch {
            Stop-Function -Message "Failed to connect to: $SqlInstance" -Target $SqlInstance -InnerErrorRecord $_
            return
        }
    }

    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($fullname in $path) {

            if (-not $SqlInstance.IsLocalHost -and -not $fullname.StartsWith('\')) {
                Stop-Function -Message "Path ($fullname) must be a UNC share when SQL instance is not local." -Continue -Target $fullname
            }

            if (-not (Test-DbaSqlPath -SqlInstance $server -Path $fullname)) {
                Stop-Function -Message "$SqlInstance cannot access $fullname" -Continue -Target $fullname
            }

            $directory = Split-Path $fullname
            $filename = Split-Path $fullname -Leaf
            $basename = [io.path]::GetFileNameWithoutExtension($filename)
            $cert = new-smocert -directory $directory -certname $basename
            Get-DbaDbCertificate -SqlInstance $server -Database $Database -Certificate $cert.Name
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Restore-DbaDbSnapshot {
    <#
    .SYNOPSIS
        Restores databases from snapshots

    .DESCRIPTION
        Restores the database from the snapshot, discarding every modification made to the database
        NB: Restoring to a snapshot will result in every other snapshot of the same database to be dropped
        It also fixes some long-standing bugs in SQL Server when restoring from snapshots

    .PARAMETER SqlInstance
        The SQL Server that you're connecting to

    .PARAMETER SqlCredential
        Credential object used to connect to the SQL Server as a different user

    .PARAMETER Database
        Restores from the last snapshot databases with this names only. You can pass either Databases or Snapshots

    .PARAMETER ExcludeDatabase
        The database(s) to exclude - this list is auto-populated from the server

    .PARAMETER Snapshot
        Restores databases from snapshots with this names only. You can pass either Databases or Snapshots

    .PARAMETER InputObject
        Allows piping from other Snapshot commands

    .PARAMETER Force
        If restoring from a snapshot involves dropping any other shapshot, you need to explicitly
        use -Force to let this command delete the ones not involved in the restore process.
        Also, -Force will forcibly kill all running queries that prevent the restore process.

    .PARAMETER WhatIf
        Shows what would happen if the command were to run

    .PARAMETER Confirm
        Prompts for confirmation of every step.

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Tags: Snapshot, Backup, Restore, Database
        Author: niphlod

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Restore-DbaDbSnapshot

    .EXAMPLE
        Restore-DbaDbSnapshot -SqlInstance sql2014 -Database HR, Accounting

        Restores HR and Accounting databases using the latest snapshot available

    .EXAMPLE
        Restore-DbaDbSnapshot -SqlInstance sql2014 -Database HR -Force

        Restores HR database from latest snapshot and kills any active connections in the database on sql2014.

    .EXAMPLE
        Get-DbaDbSnapshot -SqlInstance sql2016 -Database HR | Restore-DbaDbSnapshot -Force

        Restores HR database from latest snapshot and kills any active connections in the database on sql2016.

    .EXAMPLE
        Get-DbaDbSnapshot -SqlInstance sql2016 | Out-GridView -Passthru | Restore-DbaDbSnapshot

        Allows the selection of snapshots on sql2016 to restore

    .EXAMPLE
        Restore-DbaDbSnapshot -SqlInstance sql2014 -Snapshot HR_snap_20161201, Accounting_snap_20161101

        Restores databases from snapshots named HR_snap_20161201 and Accounting_snap_20161101
    #>
    [CmdletBinding(SupportsShouldProcess)]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [object[]]$Snapshot,
        [Parameter(ValueFromPipeline)]
        [Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        if (-not $Snapshot -and -not $Database -and -not $ExcludeDatabase -and -not $InputObject) {
            Stop-Function -Message "You must specify either -Snapshot (to restore from) or -Database/-ExcludeDatabase (to restore to) or pipe in a snapshot"
            return
        }

        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $InputObject += Get-DbaDbSnapshot -SqlInstance $server -Database $Database -ExcludeDatabase $ExcludeDatabase -Snapshot $Snapshot | Sort-Object CreateDate -Descending

            if ($Snapshot) {
                # Restore databases from these snapshots
                Write-Message -Level Verbose -Message "Selected only snapshots"
                $dbs = $InputObject | Where-Object { $Snapshot -contains $_.Name }
                $baseDatabases = $dbs | Select-Object -ExpandProperty DatabaseSnapshotBaseName | Get-Unique
                if ($baseDatabases.Count -ne $Snapshot.Count -and $dbs.Count -ne 0) {
                    Stop-Function -Message "Failure. Multiple snapshots selected for the same database" -Continue
                }
            }
        }

        foreach ($snap in $InputObject) {
            # In the event someone passed -Database and it got all the snaps, most of which were dropped by the first
            if ($snap.Parent) {
                $server = $snap.Parent

                if (-not $snap.IsDatabaseSnapshot) {
                    Stop-Function -Continue -Message "$snap on $server is not a valid snapshot"
                }

                if (-not ($snap.IsAccessible)) {
                    Stop-Function -Message "Database $snap is not accessible on $($snap.Parent)." -Continue
                }

                $othersnaps = $server.Databases | Where-Object { $_.DatabaseSnapshotBaseName -eq $snap.DatabaseSnapshotBaseName -and $_.Name -ne $snap.Name }

                $db = $server.Databases | Where-Object Name -eq $snap.DatabaseSnapshotBaseName
                $loginfo = $db.LogFiles | Select-Object Id, Size, Growth, GrowthType

                if (($snap | Where-Object FileGroupType -eq 'FileStreamDataFileGroup')) {
                    Stop-Function -Message "Database $snap on $server has FileStream group(s). You cannot restore from snapshots" -Continue
                }

                if ($othersnaps -and -not $force) {
                    Stop-Function -Message "The restore process for $db from $snap needs to drop other snapshots on $db. Use -Force if you want to drop these snapshots" -Continue
                }

                if ($Pscmdlet.ShouldProcess($server, "Remove other db snapshots for $db")) {
                    try {
                        $null = $othersnaps | Remove-DbaDatabase -Confirm:$false -EnableException
                    }
                    catch {
                        Stop-Function -Message "Failed to remove other snapshots for $db on $server" -ErrorRecord $_ -Continue
                    }
                }

                # Need a proper restore now
                if ($Pscmdlet.ShouldProcess($server, "Restore db $db from $snap")) {
                    try {
                        if ($Force) {
                            $null = Stop-DbaProcess -SqlInstance $server -Database $db.Name, $snap.Name -WarningAction SilentlyContinue
                        }

                        $null = $server.Query("USE master; RESTORE DATABASE [$($db.Name)] FROM DATABASE_SNAPSHOT='$($snap.Name)'")
                    }
                    catch {
                        Stop-Function -Message "Failiure attempting to restore $db on $server" -ErrorRecord $_ -Continue
                    }
                }

                # Comparing sizes before and after, need to refresh to see if size
                foreach ($log in $db.LogFiles) {
                    $log.Refresh()
                }

                foreach ($log in $db.LogFiles) {
                    $matching = $loginfo | Where-Object ID -eq $log.ID
                    $changeflag = 0
                    foreach ($prop in @('Size', 'Growth', 'Growth', 'GrowthType')) {
                        if ($matching.$prop -ne $log.$prop) {
                            $changeflag = 1
                            $log.$prop = $matching.$prop
                        }
                    }
                    if ($changeflag -ne 0) {
                        Write-Message -Level Verbose -Message "Restoring original settings for log file"
                        $log.Alter()
                    }
                }

                Write-Message -Level Verbose -Message "Restored. Remember to take a backup now, and also to remove the snapshot if not needed."
                Get-DbaDatabase -SqlInstance $server -Database $db.Name
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Restore-DbaFromDatabaseSnapshot
    }
}
function Save-DbaDiagnosticQueryScript {
    <#
.SYNOPSIS
Save-DbaDiagnosticQueryScript downloads the most recent version of all Glenn Berry DMV scripts

.DESCRIPTION
The dbatools module will have the diagnostic queries pre-installed. Use this only to update to a more recent version or specific versions.

This function is mainly used by Invoke-DbaDiagnosticQuery, but can also be used independently to download the Glenn Berry DMV scripts.

Use this function to pre-download the scripts from a device with an Internet connection.

The function Invoke-DbaDiagnosticQuery will try to download these scripts automatically, but it obviously needs an internet connection to do that.

.PARAMETER Path
Specifies the path to the output

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Author: André Kamman (@AndreKamman), http://clouddba.io
Tags: Diagnostic, DMV, Troubleshooting

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
Save-DbaDiagnosticQueryScript -Path c:\temp

Downloads the most recent version of all Glenn Berry DMV scripts to the specified location.
If Path is not specified, the "My Documents" location will be used.

#>
    [CmdletBinding()]
    param (
        [System.IO.FileInfo]$Path = [Environment]::GetFolderPath("mydocuments"),
        [Alias('Silent')]
        [switch]$EnableException
    )
    function Get-WebData {
        param ($uri)
        try {
            $data = (Invoke-WebRequest -uri $uri -ErrorAction Stop)
            return $data
        }
        catch {
            Stop-Function -Message "Invoke-WebRequest failed: $_" -Target $data -ErrorRecord $_
            return
        }
    }

    if (-not (Test-Path $Path)) {
        Stop-Function -Message "Path does not exist or access denied" -Target $path
        return
    }

    Add-Type -AssemblyName System.Web
    $glenberryrss = "http://www.sqlskills.com/blogs/glenn/feed/"
    $glenberrysql = @()

    Write-Message -Level Output -Message "Downloading RSS Feed"
    $rss = [xml](get-webdata -uri $glenberryrss)
    $Feed = $rss.rss.Channel

    $glenberrysql = @()
    $RssPostFilter = "SQL Server Diagnostic Information Queries for*"
    $DropboxLinkFilter = "*dropbox.com*"
    $LinkTitleFilter = "*Diagnostic*"

    foreach ($post in $Feed.item) {
        if ($post.title -like $RssPostFilter) {
            # We found the first post that matches it, lets go visit and scrape.
            $page = Get-WebData -uri $post.link
            $glenberrysql += ($page.Links | Where-Object { $_.href -like $DropboxLinkFilter -and $_.innerText -like $LinkTitleFilter } | ForEach-Object {
                    [pscustomobject]@{
                        URL        = $_.href
                        SQLVersion = $_.innerText -replace " Diagnostic Information Queries", "" -replace "SQL Server ", "" -replace ' ', ''
                        FileYear   = ($post.title -split " ")[-1]
                        FileMonth  = "{0:00}" -f [int]([CultureInfo]::InvariantCulture.DateTimeFormat.MonthNames.IndexOf(($post.title -split " ")[-2]))
                    }
                })
            break
        }
    }
    Write-Message -Level Output -Message "Found $($glenberrysql.Count) documents to download"
    foreach ($doc in $glenberrysql) {
        try {
            Write-Message -Level Output -Message "Downloading $($doc.URL)"
            $filename = "{0}\SQLServerDiagnosticQueries_{1}_{2}.sql" -f $Path, $doc.SQLVersion, "$($doc.FileYear)$($doc.FileMonth)"
            Invoke-WebRequest -Uri $doc.URL -OutFile $filename -ErrorAction Stop
        }
        catch {
            Stop-Function -Message "Requesting and writing file failed: $_" -Target $filename -ErrorRecord $_
            return
        }
    }
}
function Select-DbaBackupInformation {
    <#
        .SYNOPSIS
            Select a subset of backups from a dbatools backup history object

        .DESCRIPTION
        Select-DbaBackupInformation filters out a subset of backups from the dbatools backup history object with parameters supplied.

        .PARAMETER BackupHistory
            A dbatools.BackupHistory object containing backup history records

        .PARAMETER RestoreTime
            The point in time you want to restore to

        .PARAMETER IgnoreLogs
            This switch will cause Log Backups to be ignored. So will restore to the last Full or Diff backup only

        .PARAMETER IgnoreDiffs
            This switch will cause Differential backups to be ignored. Unless IgnoreLogs is specified, restore to point in time will still occur, just using all available log backups

        .PARAMETER DatabaseName
            A string array of Database Names that you want to filter to

        .PARAMETER ServerName
            A string array of Server Names that you want to filter

        .PARAMETER ContinuePoints
            The Output of Get-RestoreContinuableDatabase while provides 'Database',redo_start_lsn,'FirstRecoveryForkID' values. Used to filter backups to continue a restore on a database
            Sets IgnoreDiffs, and also filters databases to only those within the ContinuePoints object, or the ContinuePoints object AND DatabaseName if both specified

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Backup, Restore
            Author:Stuart Moore (@napalmgram stuart-moore.com )

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Select-DbaBackupInformation

        .EXAMPLE
            $Backups = Get-DbaBackupInformation -SqlInstance Server1 -Path \\server1\backups$
            $FilteredBackups = $Backups | Select-DbaBackupInformation -RestoreTime (Get-Date).AddHours(-1)

            Returns all backups needed to restore all the backups in \\server1\backups$ to 1 hour ago

        .EXAMPLE
            $Backups = Get-DbaBackupInformation -SqlInstance Server1 -Path \\server1\backups$
            $FilteredBackups = $Backups | Select-DbaBackupInformation -RestoreTime (Get-Date).AddHours(-1) -DatabaseName ProdFinance

            Returns all the backups needed to restore Database ProdFinance to an hour ago

        .EXAMPLE
            $Backups = Get-DbaBackupInformation -SqlInstance Server1 -Path \\server1\backups$
            $FilteredBackups = $Backups | Select-DbaBackupInformation -RestoreTime (Get-Date).AddHours(-1) -IgnoreLogs

            Returns all the backups in \\server1\backups$ to restore to as close prior to 1 hour ago as can be managed with only full and differential backups

        .EXAMPLE
            $Backups = Get-DbaBackupInformation -SqlInstance Server1 -Path \\server1\backups$
            $FilteredBackups = $Backups | Select-DbaBackupInformation -RestoreTime (Get-Date).AddHours(-1) -IgnoreDiffs

            Returns all the backups in \\server1\backups$ to restore to 1 hour ago using only Full and Diff backups.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [object]$BackupHistory,
        [DateTime]$RestoreTime = (get-date).addmonths(1),
        [switch]$IgnoreLogs,
        [switch]$IgnoreDiffs,
        [string[]]$DatabaseName,
        [string[]]$ServerName,
        [object]$ContinuePoints,
        [switch]$EnableException
    )
    begin {
        $InternalHistory = @()

        if ((Test-Bound -ParameterName ContinuePoints) -and $null -ne $ContinuePoints) {
            Write-Message -Message "ContinuePoints provided so setting up for a continue" -Level Verbose

            $IgnoreDiffs = $true
            $IgnoreFull = $true
            if (Test-Bound -ParameterName DatabaseName) {
                $DatabaseName = $DatabaseName | Where-Object {$_ -in ($ContinuePoints | Select-Object -Property Database).Database}

                $DroppedDatabases = $DatabaseName | Where-Object {$_ -notin ($ContinuePoints | Select-Object -Property Database).Database}
                if ($null -ne $DroppedDatabases) {
                    Write-Message -Message "$($DroppedDatabases.join(',')) filtered out as not in ContinuePoints" -Level Verbose
                }
            }
            else {
                $DatabaseName = ($ContinuePoints | Select-Object -Property Database).Database
            }
        }
    }
    process {
        $internalHistory += $BackupHistory
    }

    end {
        ForEach ($History in $InternalHistory) {
            if ("RestoreTime" -notin $History.PSobject.Properties.name) {
                $History | Add-Member -Name 'RestoreTime' -Type NoteProperty -Value $RestoreTime
            }
        }
        if ((Test-Bound -ParameterName DatabaseName) -and '' -ne $DatabaseName) {
            Write-Message -Message "Filtering by DatabaseName" -Level Verbose
            $InternalHistory = $InternalHistory | Where-Object {$_.Database -in $DatabaseName}
        }
        if (Test-Bound -ParameterName ServerName) {
            Write-Message -Message "Filtering by ServerName" -Level Verbose
            $InternalHistory = $InternalHistory | Where-Object {$_.InstanceName -in $servername}
        }

        $Databases = ($InternalHistory | Select-Object -Property Database -unique).Database
        ForEach ($Database in $Databases) {
            Write-Message -Message "Processing Db $Database" -Level Verbose
            $DatabaseHistory = $InternalHistory | Where-Object {$_.Database -eq $Database}

            $dbHistory = @()
            #Find the Last Full Backup before RestoreTime
            if ($true -ne $IgnoreFull) {
                $Full = $DatabaseHistory | Where-Object {$_.Type -in ('Full', 'Database') -and $_.Start -le $RestoreTime} | Sort-Object -Property LastLsn -Descending | Select-Object -First 1
                $full.Fullname = ($DatabaseHistory | Where-Object {$_.Type -in ('Full', 'Database') -and $_.BackupSetID -eq $Full.BackupSetID}).Fullname
                $dbHistory += $full
            }
            #Find the Last diff between Full and RestoreTime
            if ($true -ne $IgnoreDiffs) {
                $Diff = $DatabaseHistory | Where-Object {$_.Type -in ('Differential', 'Database Differential') -and $_.Start -le $RestoreTime -and $_.DatabaseBackupLSN -eq $Full.CheckpointLSN} | Sort-Object -Property LastLsn -Descending | Select-Object -First 1
                if ($null -ne $Diff) {
                    $Diff.FullName = ($DatabaseHistory | Where-Object {$_.Type -in ('Differential', 'Database Differential') -and $_.BackupSetID -eq $diff.BackupSetID}).Fullname
                    $dbhistory += $Diff
                }
            }
            #Get All t-logs up to restore time
            if ($IgnoreFull -eq $true) {
                [bigint]$LogBaseLsn = ($ContinuePoints | Where-Object {$_.Database -eq $Database}).redo_start_lsn
                $FirstRecoveryForkID = ($ContinuePoints | Where-Object {$_.Database -eq $Database}).FirstRecoveryForkID
                Write-Message -Message "Continuing, setting fake LastLsn - $LogBaseLSN" -Level Verbose
            }
            else {
                Write-Message -Message "Setting LogBaseLSN" -Level Verbose
                [bigint]$LogBaseLsn = ($dbHistory | Sort-Object -Property LastLsn -Descending | select-object -First 1).lastLsn.ToString()
                $FirstRecoveryForkID = $Full.FirstRecoveryForkID
            }

            if ($true -ne $IgnoreLogs) {
                $FilteredLogs = $DatabaseHistory | Where-Object {$_.Type -in ('Log', 'Transaction Log') -and $_.Start -le $RestoreTime -and $_.LastLSN.ToString() -ge $LogBaseLsn -and $_.FirstLSN -ne $_.LastLSN}  | Sort-Object -Property LastLsn, FirstLsn
                $GroupedLogs = $FilteredLogs | Group-Object -Property LastLSN, FirstLSN
                ForEach ($Group in $GroupedLogs) {
                    $Log = $DatabaseHistory | Where-Object {$_.BackupSetID -eq $Group.group[0].BackupSetID} | select-object -First 1
                    $Log.FullName = ($DatabaseHistory | Where-Object {$_.BackupSetID -eq $Group.group[0].BackupSetID}).Fullname
                    $dbhistory += $Log
                    #$dbhistory += $DatabaseHistory | Where-Object {$_.BackupSetID -eq $Group.group[0].BackupSetID}
                }
                # Get Last T-log
                $dbHistory += $DatabaseHistory | Where-Object {$_.Type -in ('Log', 'Transaction Log') -and $_.End -ge $RestoreTime -and $_.DatabaseBackupLSN -eq $Full.CheckpointLSN} | Sort-Object -Property LastLsn, FirstLsn  | Select-Object -First 1
            }

            $dbHistory  #| Group-Object -Property BackupSetId # -Unique
        }
    }
}
function Select-DbaObject {
<#
    .SYNOPSIS
        Wrapper around Select-Object, extends property parameter.
    
    .DESCRIPTION
        Wrapper around Select-Object, extends property parameter.
        
        This function allows specifying in-line transformation of the properties specified without needing to use complex hashtables.
        Without removing the ability to specify just those hashtables.
        
        See the description of the Property parameter for an exhaustive list of legal notations.
    
    .PARAMETER InputObject
        The object(s) to select from.
    
    .PARAMETER Property
        The properties to select.
        - Supports hashtables, which will be passed through to Select-Object.
        - Supports renaming as it is possible in SQL: "Length AS Size" will select the Length property but rename it to size.
        - Supports casting to a specified type: "Address to IPAddress" or "Length to int". Uses PowerShell type-conversion.
        - Supports parsing numbers to sizes: "Length size GB:2" Converts numeric input (presumed to be bytes) to gigabyte with two decimals.
        Also supports toggling on Unit descriptors by adding another element: "Length size GB:2:1"
        - Supports selecting properties from objects in other variables: "ComputerName from VarName" (Will insert the property 'ComputerName' from variable $VarName)
        - Supports filtering when selecting from outside objects: "ComputerName from VarName where ObjectId = Id" (Will insert the property 'ComputerName' from the object in variable $VarName, whose ObjectId property is equal to the inputs Id property)
        
        Important:
        When using this command from another module (not script-files, those are fine), you do not have access to the variables in the calling module.
        In order to select from other variables using the 'from' call, you need to declare the variable global.
    
    .PARAMETER ExcludeProperty
        Properties to not list.
    
    .PARAMETER ExpandProperty
        Properties to expand.
    
    .PARAMETER Unique
        Do not list multiples of the same value.
    
    .PARAMETER Last
        Select the last n items.
    
    .PARAMETER First
        Select the first n items.
    
    .PARAMETER Skip
        Skip the first (or last if used with -Last) n items.
    
    .PARAMETER SkipLast
        Skip the last n items.
    
    .PARAMETER Wait
        Indicates that the cmdlet turns off optimization.Windows PowerShell runs commands in the order that they appear in the command pipeline and lets them generate all objects. By default, if you include a Select-Object command with the First or Index parameters in a command pipeline, Windows PowerShell stops the command that generates the objects as soon as the selected number of objects is generated.
    
    .PARAMETER Index
        Specifies an array of objects based on their index values. Enter the indexes in a comma-separated list.
    
    .PARAMETER ShowProperty
        Only the specified properties will be shown by default.
        Supersedes ShowExcludeProperty.
    
    .PARAMETER ShowExcludeProperty
        Hides the specified properties from the default display style of the output object.
        Is ignored if used together with ShowProperty.
    
    .PARAMETER TypeName
        Adds a typename to the selected object.
        Will automatically prefix the module.
    
    .EXAMPLE
        PS C:\> Get-ChildItem | Select-DbaObject Name, "Length as Size"
        
        Selects the properties Name and Length, renaming Length to Size in the process.
    
    .EXAMPLE
        PS C:\> Import-Csv .\file.csv | Select-DbaObject Name, "Length as Size to DbaSize"
        
        Selects the properties Name and Length, renaming Length to Size and converting it to [DbaSize] (a userfriendly representation of size numbers)
    
    .EXAMPLE
        PS C:\> $obj = [PSCustomObject]@{ Name = "Foo" }
        PS C:\> Get-ChildItem | Select-DbaObject FullName, Length, "Name from obj"
        
        Selects the properties FullName and Length from the input and the Name property from the object stored in $obj
    
    .EXAMPLE
        PS C:\> $list = @()
        PS C:\> $list += [PSCustomObject]@{ Type = "Foo"; ID = 1 }
        PS C:\> $list += [PSCustomObject]@{ Type = "Bar"; ID = 2 }
        PS C:\> $obj | Select-DbaObject Name, "ID from list WHERE Type = Name"
        
        This allows you to LEFT JOIN contents of another variable.
        Note that it can only do simple property-matching at this point.
        
        It will select Name from the objects stored in $obj, and for each of those the ID Property on any object in $list that has a Type property of equal value as Name on the input.
#>
    [CmdletBinding(DefaultParameterSetName = 'DefaultParameter', RemotingCapability = 'None')]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [psobject]
        $InputObject,
        
        [Parameter(ParameterSetName = 'DefaultParameter', Position = 0)]
        [Parameter(ParameterSetName = 'SkipLastParameter', Position = 0)]
        [SqlCollaborative.Dbatools.Parameter.DbaSelectParameter[]]
        $Property,
        
        [Parameter(ParameterSetName = 'SkipLastParameter')]
        [Parameter(ParameterSetName = 'DefaultParameter')]
        [string[]]
        $ExcludeProperty,
        
        [Parameter(ParameterSetName = 'DefaultParameter')]
        [Parameter(ParameterSetName = 'SkipLastParameter')]
        [string]
        $ExpandProperty,
        
        [switch]
        $Unique,
        
        [Parameter(ParameterSetName = 'DefaultParameter')]
        [ValidateRange(0, 2147483647)]
        [int]
        $Last,
        
        [Parameter(ParameterSetName = 'DefaultParameter')]
        [ValidateRange(0, 2147483647)]
        [int]
        $First,
        
        [Parameter(ParameterSetName = 'DefaultParameter')]
        [ValidateRange(0, 2147483647)]
        [int]
        $Skip,
        
        [Parameter(ParameterSetName = 'SkipLastParameter')]
        [ValidateRange(0, 2147483647)]
        [int]
        $SkipLast,
        
        [Parameter(ParameterSetName = 'IndexParameter')]
        [Parameter(ParameterSetName = 'DefaultParameter')]
        [switch]
        $Wait,
        
        [Parameter(ParameterSetName = 'IndexParameter')]
        [ValidateRange(0, 2147483647)]
        [int[]]
        $Index,
        
        [string[]]
        $ShowProperty,
        
        [string[]]
        $ShowExcludeProperty,
        
        [string]
        $TypeName
    )
    
    begin {
        try {
            $outBuffer = $null
            if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) {
                $PSBoundParameters['OutBuffer'] = 1
            }
            
            $clonedParameters = @{ }
            foreach ($key in $PSBoundParameters.Keys) {
                if (($key -ne "Property") -and ($key -ne "ShowExcludeProperty") -and ($key -ne "ShowProperty") -and ($key -ne "TypeName")) {
                    $clonedParameters[$key] = $PSBoundParameters[$key]
                }
            }
            if (Test-Bound -ParameterName 'Property') {
                $clonedParameters['Property'] = $Property.Value
            }
            
            if (-not ($ShowExcludeProperty -or $ShowProperty -or $TypeName)) {
                $__noAdjustment = $true
            }
            else {
                $__noAdjustment = $false
                if ($ShowProperty) {
                    $__defaultset = New-Object System.Management.Automation.PSPropertySet('DefaultDisplayPropertySet', $ShowProperty)
                    $__standardmembers = [System.Management.Automation.PSMemberInfo[]]@($__defaultset)
                }
                if ($TypeName) {
                    $__callerModule = (Get-PSCallStack)[1].InvocationInfo.MyCommand.ModuleName
                    $__typeName = $TypeName
                    if ($__callerModule) { $__typeName = "$($__callerModule).$($TypeName)" }
                }
            }
            
            $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Microsoft.PowerShell.Utility\Select-Object', [System.Management.Automation.CommandTypes]::Cmdlet)
            $scriptCmd = { & $wrappedCmd @clonedParameters }
            $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
            # If no adjustment is necessary, run as integrated command (better performance)
            if ($__noAdjustment) { $steppablePipeline.Begin($PSCmdlet) }
            # If Adjustments are necessary, run as addon, allowing us to capture output
            else { $steppablePipeline.Begin($true) }
        }
        catch {
            throw
        }
    }
    
    process {
        try {
            if ($__noAdjustment) {
                $steppablePipeline.Process($InputObject)
            }
            else {
                $__item = $steppablePipeline.Process($InputObject)[0]
                if ($ShowProperty) {
                    $__item | Add-Member -Force -MemberType MemberSet -Name PSStandardMembers -Value $__standardmembers -ErrorAction SilentlyContinue
                }
                elseif ($ShowExcludeProperty) {
                    $__propertiesToShow = @()
                    foreach ($prop in $__item.PSObject.Properties.Name) {
                        if ($prop -notin $ShowExcludeProperty) {
                            $__propertiesToShow += $prop
                        }
                    }
                    $__defaultset = New-Object System.Management.Automation.PSPropertySet('DefaultDisplayPropertySet', $__propertiesToShow)
                    $__standardmembers = [System.Management.Automation.PSMemberInfo[]]@($__defaultset)
                    $__item | Add-Member -Force -MemberType MemberSet -Name PSStandardMembers -Value $__standardmembers -ErrorAction SilentlyContinue
                }
                if ($TypeName) {
                    $__item.PSObject.TypeNames.Insert(0, $__typeName)
                }
                $__item
            }
        }
        catch {
            throw
        }
    }
    
    end {
        try {
            $steppablePipeline.End()
        }
        catch {
            throw
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Set-DbaAgentAlert {
    <#
.SYNOPSIS
Set-DbaAgentAlert updates a the status of a SQL Agent Alert.

.DESCRIPTION
Set-DbaAgentAlert updates an alert in the SQL Server Agent with parameters supplied.

.PARAMETER SqlInstance
SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Alert
The name of the alert.

.PARAMETER NewName
The new name for the alert.

.PARAMETER Enabled
Enabled the alert.

.PARAMETER Disabled
Disabled the alert.

.PARAMETER Force
The force parameter will ignore some errors in the parameters and assume defaults.

.PARAMETER InputObject
Enables piping alert objects

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Author: Garry Bargsley (@gbargsley, garrybargsley.com)
Tags: Agent, Alert

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Set-DbaAgentAlert

.EXAMPLE
Set-DbaAgentAlert -SqlInstance sql1 -Alert 'Severity 025: Fatal Error' -Disabled
Changes the alert to disabled.

.EXAMPLE
Set-DbaAgentAlert -SqlInstance sql1 -Alert 'Severity 025: Fatal Error', 'Error Number 825', 'Error Number 824' -Enabled
Changes multiple alerts to enabled.

.EXAMPLE
Set-DbaAgentAlert -SqlInstance sql1, sql2, sql3 -Alert 'Severity 025: Fatal Error', 'Error Number 825', 'Error Number 824' -Enabled
Changes multiple alerts to enabled on multiple servers.

.EXAMPLE
Set-DbaAgentAlert -SqlInstance sql1 -Alert 'Severity 025: Fatal Error' -Disabled -Whatif
Doesn't Change the alert but shows what would happen.

#>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Alert,
        [string]$NewName,
        [switch]$Enabled,
        [switch]$Disabled,
        [switch]$Force,
        [parameter(ValueFromPipeline = $true)]
        [Microsoft.SqlServer.Management.Smo.Agent.Alert[]]$InputObject,
        [switch][Alias('Silent')]
        $EnableException
    )

    begin {
}
        process {

            if (Test-FunctionInterrupt) { return }

            if ((-not $InputObject) -and (-not $Alert)) {
                Stop-Function -Message "You must specify an alert name or pipe in results from another command" -Target $sqlinstance
                return
            }

            foreach ($instance in $sqlinstance) {
                # Try connecting to the instance
                Write-Message -Message "Connecting to $instance" -Level Verbose
                try {
                    $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
                }
                catch {
                    Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                }
            foreach ($a in $Alert) {
                    # Check if the alert exists
                    if ($server.JobServer.Alerts.Name -notcontains $a) {
                        Stop-Function -Message "Alert $a doesn't exists on $instance" -Target $instance
                    }
                    else {
                        # Get the alert
                        try {
                            $InputObject += $server.JobServer.Alerts[$a]

                            # Refresh the object
                            $InputObject.Refresh()
                        }
                        catch {
                            Stop-Function -Message "Something went wrong retrieving the alert" -Target $a -ErrorRecord $_ -Continue
                        }
                    }
                }
            }

            foreach ($currentalert in $InputObject) {
                $server = $currentalert.Parent.Parent

                #region alert options
                # Settings the options for the alert
                if ($NewName) {
                    Write-Message -Message "Setting alert name to $NewName" -Level Verbose
                    $currentalert.Rename($NewName)
                }

                if ($Enabled) {
                    Write-Message -Message "Setting alert to enabled" -Level Verbose
                    $currentalert.IsEnabled = $true
                }

                if ($Disabled) {
                    Write-Message -Message "Setting alert to disabled" -Level Verbose
                    $currentalert.IsEnabled = $false
                }

                #endregion alert options

                # Execute
                if ($PSCmdlet.ShouldProcess($SqlInstance, "Changing the alert $a")) {
                    try {
                        Write-Message -Message "Changing the alert" -Level Verbose

                        # Change the alert
                        $currentalert.Alter()
                    }
                    catch {
                        Stop-Function -Message "Something went wrong changing the alert" -ErrorRecord $_ -Target $instance -Continue
                    }
                    Get-DbaAgentAlert -SqlInstance $server | Where-Object Name -eq $currentalert.name
                }
            }
        }
}
        {
            Write-Message -Message "Finished changing alert(s)" -Level Verbose
        }
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Set-DbaAgentJob {
    <#
.SYNOPSIS
Set-DbaAgentJob updates a job.

.DESCRIPTION
Set-DbaAgentJob updates a job in the SQL Server Agent with parameters supplied.

.PARAMETER SqlInstance
SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Job
The name of the job.

.PARAMETER Schedule
Schedule to attach to job. This can be more than one schedule.

.PARAMETER ScheduleId
Schedule ID to attach to job. This can be more than one schedule ID.

.PARAMETER NewName
The new name for the job.

.PARAMETER Enabled
Enabled the job.

.PARAMETER Disabled
Disabled the job

.PARAMETER Description
The description of the job.

.PARAMETER StartStepId
The identification number of the first step to execute for the job.

.PARAMETER Category
The category of the job.

.PARAMETER OwnerLogin
The name of the login that owns the job.

.PARAMETER EventlogLevel
Specifies when to place an entry in the Microsoft Windows application log for this job.
Allowed values 0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always"
The text value van either be lowercase, uppercase or something in between as long as the text is correct.

.PARAMETER EmailLevel
Specifies when to send an e-mail upon the completion of this job.
Allowed values 0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always"
The text value van either be lowercase, uppercase or something in between as long as the text is correct.

.PARAMETER NetsendLevel
Specifies when to send a network message upon the completion of this job.
Allowed values 0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always"
The text value van either be lowercase, uppercase or something in between as long as the text is correct.

.PARAMETER PageLevel
Specifies when to send a page upon the completion of this job.
Allowed values 0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always"
The text value van either be lowercase, uppercase or something in between as long as the text is correct.

.PARAMETER EmailOperator
The e-mail name of the operator to whom the e-mail is sent when EmailLevel is reached.

.PARAMETER NetsendOperator
The name of the operator to whom the network message is sent.

.PARAMETER PageOperator
The name of the operator to whom a page is sent.

.PARAMETER DeleteLevel
Specifies when to delete the job.
Allowed values 0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always"
The text value van either be lowercase, uppercase or something in between as long as the text is correct.

.PARAMETER Force
The force parameter will ignore some errors in the parameters and assume defaults.

.PARAMETER InputObject
Enables piping job objects

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Author: Sander Stad (@sqlstad, sqlstad.nl)
Tags: Agent, Job

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Set-DbaAgentJob

.EXAMPLE
Set-DbaAgentJob sql1 -Job Job1 -Disabled
Changes the job to disabled

.EXAMPLE
Set-DbaAgentJob sql1 -Job Job1 -OwnerLogin user1
Changes the owner of the job

.EXAMPLE
Set-DbaAgentJob -SqlInstance sql1 -Job Job1 -EventLogLevel OnSuccess
Changes the job and sets the notification to write to the Windows Application event log on success

.EXAMPLE
Set-DbaAgentJob -SqlInstance sql1 -Job Job1 -EmailLevel OnFailure -EmailOperator dba
Changes the job and sets the notification to send an e-mail to the e-mail operator

.EXAMPLE
Set-DbaAgentJob -SqlInstance sql1 -Job Job1, Job2, Job3 -Enabled
Changes multiple jobs to enabled

.EXAMPLE
Set-DbaAgentJob -SqlInstance sql1, sql2, sql3 -Job Job1, Job2, Job3 -Enabled
Changes multiple jobs to enabled on multiple servers

.EXAMPLE
Set-DbaAgentJob -SqlInstance sql1 -Job Job1 -Description 'Just another job' -Whatif
Doesn't Change the job but shows what would happen.

.EXAMPLE
Set-DbaAgentJob -SqlInstance sql1, sql2, sql3 -Job 'Job One' -Description 'Job One'
Changes a job with the name "Job1" on multiple servers to have another description

.EXAMPLE
sql1, sql2, sql3 | Set-DbaAgentJob -Job Job1 -Description 'Job One'
Changes a job with the name "Job1" on multiple servers to have another description using pipe line

#>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Job,
        [object[]]$Schedule,
        [int[]]$ScheduleId,
        [string]$NewName,
        [switch]$Enabled,
        [switch]$Disabled,
        [string]$Description,
        [int]$StartStepId,
        [string]$Category,
        [string]$OwnerLogin,
        [ValidateSet(0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always")]
        [object]$EventLogLevel,
        [ValidateSet(0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always")]
        [object]$EmailLevel,
        [ValidateSet(0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always")]
        [object]$NetsendLevel,
        [ValidateSet(0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always")]
        [object]$PageLevel,
        [string]$EmailOperator,
        [string]$NetsendOperator,
        [string]$PageOperator,
        [ValidateSet(0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always")]
        [object]$DeleteLevel,
        [switch]$Force,
        [parameter(ValueFromPipeline = $true)]
        [Microsoft.SqlServer.Management.Smo.Agent.Job[]]$InputObject,
        [switch][Alias('Silent')]
        $EnableException
    )

    begin {
        # Check of the event log level is of type string and set the integer value
        if (($EventLogLevel -notin 0, 1, 2, 3) -and ($null -ne $EventLogLevel)) {
            $EventLogLevel = switch ($EventLogLevel) { "Never" { 0 } "OnSuccess" { 1 } "OnFailure" { 2 } "Always" { 3 } }
        }

        # Check of the email level is of type string and set the integer value
        if (($EmailLevel -notin 0, 1, 2, 3) -and ($null -ne $EmailLevel)) {
            $EmailLevel = switch ($EmailLevel) { "Never" { 0 } "OnSuccess" { 1 } "OnFailure" { 2 } "Always" { 3 } }
        }

        # Check of the net send level is of type string and set the integer value
        if (($NetsendLevel -notin 0, 1, 2, 3) -and ($null -ne $NetsendLevel)) {
            $NetsendLevel = switch ($NetsendLevel) { "Never" { 0 } "OnSuccess" { 1 } "OnFailure" { 2 } "Always" { 3 } }
        }

        # Check of the page level is of type string and set the integer value
        if (($PageLevel -notin 0, 1, 2, 3) -and ($null -ne $PageLevel)) {
            $PageLevel = switch ($PageLevel) { "Never" { 0 } "OnSuccess" { 1 } "OnFailure" { 2 } "Always" { 3 } }
        }

        # Check of the delete level is of type string and set the integer value
        if (($DeleteLevel -notin 0, 1, 2, 3) -and ($null -ne $DeleteLevel)) {
            $DeleteLevel = switch ($DeleteLevel) { "Never" { 0 } "OnSuccess" { 1 } "OnFailure" { 2 } "Always" { 3 } }
        }

        # Check the e-mail operator name
        if (($EmailLevel -ge 1) -and (-not $EmailOperator)) {
            Stop-Function -Message "Please set the e-mail operator when the e-mail level parameter is set." -Target $sqlinstance
            return
        }

        # Check the e-mail operator name
        if (($NetsendLevel -ge 1) -and (-not $NetsendOperator)) {
            Stop-Function -Message "Please set the netsend operator when the netsend level parameter is set." -Target $sqlinstance
            return
        }

        # Check the e-mail operator name
        if (($PageLevel -ge 1) -and (-not $PageOperator)) {
            Stop-Function -Message "Please set the page operator when the page level parameter is set." -Target $sqlinstance
            return
        }
    }

    process {

        if (Test-FunctionInterrupt) { return }

        if ((-not $InputObject) -and (-not $Job)) {
            Stop-Function -Message "You must specify a job name or pipe in results from another command" -Target $sqlinstance
            return
        }

        foreach ($instance in $sqlinstance) {
            # Try connecting to the instance
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            foreach ($j in $Job) {

                # Check if the job exists
                if ($server.JobServer.Jobs.Name -notcontains $j) {
                    Stop-Function -Message "Job $j doesn't exists on $instance" -Target $instance
                }
                else {
                    # Get the job
                    try {
                        $InputObject += $server.JobServer.Jobs[$j]

                        # Refresh the object
                        $InputObject.Refresh()
                    }
                    catch {
                        Stop-Function -Message "Something went wrong retrieving the job" -Target $j -ErrorRecord $_ -Continue
                    }
                }
            }
        }

        foreach ($currentjob in $InputObject) {
            $server = $currentjob.Parent.Parent

            #region job options
            # Settings the options for the job
            if ($NewName) {
                Write-Message -Message "Setting job name to $NewName" -Level Verbose
                $currentjob.Rename($NewName)
            }

            if ($Schedule) {
                # Loop through each of the schedules
                foreach ($s in $Schedule) {
                    if ($server.JobServer.SharedSchedules.Name -contains $s) {
                        # Get the schedule ID
                        $sID = $server.JobServer.SharedSchedules[$s].ID

                        # Add schedule to job
                        Write-Message -Message "Adding schedule id $sID to job" -Level Verbose
                        $currentjob.AddSharedSchedule($sID)
                    }
                    else {
                        Stop-Function -Message "Schedule $s cannot be found on instance $instance" -Target $s -Continue
                    }

                }
            }

            if ($ScheduleId) {
                # Loop through each of the schedules IDs
                foreach ($sID in $ScheduleId) {
                    # Check if the schedule is
                    if ($server.JobServer.SharedSchedules.ID -contains $sID) {
                        # Add schedule to job
                        Write-Message -Message "Adding schedule id $sID to job" -Level Verbose
                        $currentjob.AddSharedSchedule($sID)

                    }
                    else {
                        Stop-Function -Message "Schedule ID $sID cannot be found on instance $instance" -Target $sID -Continue
                    }
                }
            }

            if ($Enabled) {
                Write-Message -Message "Setting job to enabled" -Level Verbose
                $currentjob.IsEnabled = $true
            }

            if ($Disabled) {
                Write-Message -Message "Setting job to disabled" -Level Verbose
                $currentjob.IsEnabled = $false
            }

            if ($Description) {
                Write-Message -Message "Setting job description to $Description" -Level Verbose
                $currentjob.Description = $Description
            }

            if ($Category) {
                # Check if the job category exists
                if ($Category -notin $server.JobServer.JobCategories.Name) {
                    if ($Force) {
                        if ($PSCmdlet.ShouldProcess($instance, "Creating job category on $instance")) {
                            try {
                                # Create the category
                                New-DbaAgentJobCategory -SqlInstance $instance -Category $Category

                                Write-Message -Message "Setting job category to $Category" -Level Verbose
                                $currentjob.Category = $Category
                            }
                            catch {
                                Stop-Function -Message "Couldn't create job category $Category from $instance" -Target $instance -ErrorRecord $_
                            }
                        }
                    }
                    else {
                        Stop-Function -Message "Job category $Category doesn't exist on $instance. Use -Force to create it." -Target $instance
                        return
                    }
                }
                else {
                    Write-Message -Message "Setting job category to $Category" -Level Verbose
                    $currentjob.Category = $Category
                }
            }

            if ($StartStepId) {
                # Get the job steps
                $currentjobSteps = $currentjob.JobSteps

                # Check if there are any job steps
                if ($currentjobSteps.Count -ge 1) {
                    # Check if the start step id value is one of the job steps in the job
                    if ($currentjobSteps.ID -contains $StartStepId) {
                        Write-Message -Message "Setting job start step id to $StartStepId" -Level Verbose
                        $currentjob.StartStepID = $StartStepId
                    }
                    else {
                        Write-Message -Message "The step id is not present in job $j on instance $instance" -Warning
                    }

                }
                else {
                    Stop-Function -Message "There are no job steps present for job $j on instance $instance" -Target $instance -Continue
                }

            }

            if ($OwnerLogin) {
                # Check if the login name is present on the instance
                if ($server.Logins.Name -contains $OwnerLogin) {
                    Write-Message -Message "Setting job owner login name to $OwnerLogin" -Level Verbose
                    $currentjob.OwnerLoginName = $OwnerLogin
                }
                else {
                    Stop-Function -Message "The given owner log in name $OwnerLogin does not exist on instance $instance" -Target $instance -Continue
                }
            }

            if ($EventLogLevel) {
                Write-Message -Message "Setting job event log level to $EventlogLevel" -Level Verbose
                $currentjob.EventLogLevel = $EventLogLevel
            }

            if ($EmailLevel) {
                # Check if the notifiction needs to be removed
                if ($EmailLevel -eq 0) {
                    # Remove the operator
                    $currentjob.OperatorToEmail = $null

                    # Remove the notification
                    $currentjob.EmailLevel = $EmailLevel
                }
                else {
                    # Check if either the operator e-mail parameter is set or the operator is set in the job
                    if ($EmailOperator -or $currentjob.OperatorToEmail) {
                        Write-Message -Message "Setting job e-mail level to $EmailLevel" -Level Verbose
                        $currentjob.EmailLevel = $EmailLevel
                    }
                    else {
                        Stop-Function -Message "Cannot set e-mail level $EmailLevel without a valid e-mail operator name" -Target $instance -Continue
                    }
                }
            }

            if ($NetsendLevel) {
                # Check if the notifiction needs to be removed
                if ($NetsendLevel -eq 0) {
                    # Remove the operator
                    $currentjob.OperatorToNetSend = $null

                    # Remove the notification
                    $currentjob.NetSendLevel = $NetsendLevel
                }
                else {
                    # Check if either the operator netsend parameter is set or the operator is set in the job
                    if ($NetsendOperator -or $currentjob.OperatorToNetSend) {
                        Write-Message -Message "Setting job netsend level to $NetsendLevel" -Level Verbose
                        $currentjob.NetSendLevel = $NetsendLevel
                    }
                    else {
                        Stop-Function -Message "Cannot set netsend level $NetsendLevel without a valid netsend operator name" -Target $instance -Continue
                    }
                }
            }

            if ($PageLevel) {
                # Check if the notifiction needs to be removed
                if ($PageLevel -eq 0) {
                    # Remove the operator
                    $currentjob.OperatorToPage = $null

                    # Remove the notification
                    $currentjob.PageLevel = $PageLevel
                }
                else {
                    # Check if either the operator pager parameter is set or the operator is set in the job
                    if ($PageOperator -or $currentjob.OperatorToPage) {
                        Write-Message -Message "Setting job pager level to $PageLevel" -Level Verbose
                        $currentjob.PageLevel = $PageLevel
                    }
                    else {
                        Stop-Function -Message "Cannot set page level $PageLevel without a valid netsend operator name" -Target $instance -Continue
                    }
                }
            }

            # Check the current setting of the job's email level
            if ($EmailOperator) {
                # Check if the operator name is present
                if ($server.JobServer.Operators.Name -contains $EmailOperator) {
                    Write-Message -Message "Setting job e-mail operator to $EmailOperator" -Level Verbose
                    $currentjob.OperatorToEmail = $EmailOperator
                }
                else {
                    Stop-Function -Message "The e-mail operator name $EmailOperator does not exist on instance $instance. Exiting.." -Target $j -Continue
                }
            }

            if ($NetsendOperator) {
                # Check if the operator name is present
                if ($server.JobServer.Operators.Name -contains $NetsendOperator) {
                    Write-Message -Message "Setting job netsend operator to $NetsendOperator" -Level Verbose
                    $currentjob.OperatorToNetSend = $NetsendOperator
                }
                else {
                    Stop-Function -Message "The netsend operator name $NetsendOperator does not exist on instance $instance. Exiting.." -Target $j -Continue
                }
            }

            if ($PageOperator) {
                # Check if the operator name is present
                if ($server.JobServer.Operators.Name -contains $PageOperator) {
                    Write-Message -Message "Setting job pager operator to $PageOperator" -Level Verbose
                    $currentjob.OperatorToPage = $PageOperator
                }
                else {
                    Stop-Function -Message "The page operator name $PageOperator does not exist on instance $instance. Exiting.." -Target $instance -Continue
                }
            }

            if ($DeleteLevel) {
                Write-Message -Message "Setting job delete level to $DeleteLevel" -Level Verbose
                $currentjob.DeleteLevel = $DeleteLevel
            }
            #endregion job options

            # Execute
            if ($PSCmdlet.ShouldProcess($SqlInstance, "Changing the job $j")) {
                try {
                    Write-Message -Message "Changing the job" -Level Verbose

                    # Change the job
                    $currentjob.Alter()
                }
                catch {
                    Stop-Function -Message "Something went wrong changing the job" -ErrorRecord $_ -Target $instance -Continue
                }
                Get-DbaAgentJob -SqlInstance $server | Where-Object Name -eq $currentjob.name
            }
        }
    }

    end {
        Write-Message -Message "Finished changing job(s)" -Level Verbose
    }
}
function Set-DbaAgentJobCategory {
    <#
.SYNOPSIS
Set-DbaAgentJobCategory changes a job category.

.DESCRIPTION
Set-DbaAgentJobCategory makes it possible to change a job category.

.PARAMETER SqlInstance
SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Category
The name of the category

.PARAMETER NewName
New name of the job category

.PARAMETER Force
The force parameter will ignore some errors in the parameters and assume defaults.

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Author: Sander Stad (@sqlstad, sqlstad.nl)
Tags: Agent, Job, JobCategory

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Set-DbaAgentJobCategory

.EXAMPLE
New-DbaAgentJobCategory -SqlInstance sql1 -Category 'Category 1' -NewName 'Category 2'

Change the name of the category from 'Category 1' to 'Category 2'.

.EXAMPLE
Set-DbaAgentJobCategory -SqlInstance sql1, sql2 -Category Category1, Category2 -NewName cat1, cat2

Rename multiple jobs in one go on multiple servers.

#>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Parameter(Mandatory = $false)]
        [ValidateNotNullOrEmpty()]
        [string[]]$Category,
        [string[]]$NewName,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        # Create array list to hold the results
        $collection = New-Object System.Collections.ArrayList

        # Check if multiple categories are being changed
        if ($Category.Count -gt 1 -and $NewName.Count -eq 1) {
            Stop-Function -Message "You cannot rename multiple jobs to the same name" -Target $instance
        }
    }

    process {

        foreach ($instance in $sqlinstance) {
            # Try connecting to the instance
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            # Loop through each of the categories
            foreach ($cat in $Category) {
                # Check if the category exists
                if ($cat -notin $server.JobServer.JobCategories.Name) {
                    Stop-Function -Message "Job category $cat doesn't exist on $instance" -Target $instance -Continue
                }

                # Check if the category already exists
                if ($NewName -and ($NewName -in $server.JobServer.JobCategories.Name)) {
                    Stop-Function -Message "Job category $NewName already exists on $instance" -Target $instance -Continue
                }

                if ($PSCmdlet.ShouldProcess($instance, "Changing the job category $Category")) {
                    try {
                        # Get the job category object
                        $currentCategory = $server.JobServer.JobCategories[$cat]

                        Write-Message -Message "Changing job category $cat" -Level Verbose

                        # Get and set the original and new values
                        $originalCategoryName = $currentCategory.Name
                        $newCategoryName = $null

                        # Check if the job category needs to be renamed
                        if ($NewName) {
                            $currentCategory.Rename($NewName[$Category.IndexOf($cat)])
                            $newCategoryName = $currentCategory.Name
                        }

                        # Set up the custom object
                        $null = $collection.Add([PSCustomObject]@{
                                ComputerName    = $server.ComputerName
                                InstanceName    = $server.ServiceName
                                SqlInstance     = $server.DomainInstanceName
                                CategoryName    = $originalCategoryName
                                NewCategoryName = $newCategoryName
                            })

                    }
                    catch {
                        Stop-Function -Message "Something went wrong changing the job category $cat on $instance" -Target $cat -Continue -ErrorRecord $_
                    }

                } # if should process

            } # for each category

        } # foreach instance

        # Return result
        return $collection

    } # end process

    end {
        if (Test-FunctionInterrupt) { return }
        Write-Message -Message "Finished changing job category." -Level Verbose
    }

}
function Set-DbaAgentJobOutputFile {
    <#
        .Synopsis
            Set the output file for a step within an Agent job.

        .DESCRIPTION
            Sets the Output File for a step of an agent job with the Job Names and steps provided dynamically if required

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to.

        .PARAMETER SQLCredential
            Credential object used to connect to the SQL Server as a different user be it Windows or SQL Server. Windows users are determined by the existence of a backslash, so if you are intending to use an alternative Windows connection instead of a SQL login, ensure it contains a backslash.

        .PARAMETER Job
            The job to process - this list is auto-populated from the server.

        .PARAMETER Step
            The Agent Job Step to provide Output File Path for. Also available dynamically

        .PARAMETER OutputFile
            The Full Path to the New Output file

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Agent, Job, SqlAgent
            Author: Rob Sewell (https://sqldbawithabeard.com)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

            # todo - allow piping and add -All

        .EXAMPLE
            Set-DbaAgentJobOutputFile -SqlInstance SERVERNAME -JobName 'The Agent Job' -OutPutFile E:\Logs\AgentJobStepOutput.txt

            Sets the Job step for The Agent job on SERVERNAME to E:\Logs\AgentJobStepOutput.txt
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [Parameter(Mandatory = $true, HelpMessage = 'The SQL Server Instance',
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false,
            Position = 0)]
        [ValidateNotNull()]
        [ValidateNotNullOrEmpty()]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Parameter(Mandatory = $false, HelpMessage = 'SQL Credential',
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false)]
        [PSCredential]$SqlCredential,
        [object[]]$Job,
        [Parameter(Mandatory = $false, HelpMessage = 'The Job Step name',
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNull()]
        [ValidateNotNullOrEmpty()]
        [object[]]$Step,
        [Parameter(Mandatory = $true, HelpMessage = 'The Full Output File Path',
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false)]
        [ValidateNotNull()]
        [ValidateNotNullOrEmpty()]
        [string]$OutputFile,
        [Alias('Silent')]
        [switch]$EnableException
    )

    foreach ($instance in $sqlinstance) {
        try {
            $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
        }
        catch {
            Write-Message -Level Warning -Message "Failed to connect to: $instance"
            continue
        }

        if (!$Job) {
            # This is because jobname isn't yet required
            Write-Message -Level Warning -Message "You must specify a job using the -Job parameter."
            return
        }

        foreach ($name in $Job) {
            $currentJob = $server.JobServer.Jobs[$name]

            if ($Step) {
                $steps = $currentJob.JobSteps | Where-Object Name -in $Step

                if (!$steps) {
                    Write-Message -Level Warning -Message "$Step didn't return any steps"
                    return
                }
            }
            else {
                if (($currentJob.JobSteps).Count -gt 1) {
                    Write-Message -Level Output -Message "Which Job Step do you wish to add output file to?"
                    $steps = $currentJob.JobSteps | Out-GridView -Title "Choose the Job Steps to add an output file to" -PassThru -Verbose
                }
                else {
                    $steps = $currentJob.JobSteps
                }
            }

            if (!$steps) {
                $steps = $currentJob.JobSteps
            }

            foreach ($jobstep in $steps) {
                $currentoutputfile = $jobstep.OutputFileName

                Write-Message -Level Verbose -Message "Current Output File for $currentJob is $currentoutputfile"
                Write-Message -Level Verbose -Message "Adding $OutputFile to $jobstep for $currentJob"

                try {
                    if ($Pscmdlet.ShouldProcess($jobstep, "Changing Output File from $currentoutputfile to $OutputFile")) {
                        $jobstep.OutputFileName = $OutputFile
                        $jobstep.Alter()
                        $jobstep.Refresh()

                        [pscustomobject]@{
                            ComputerName   = $server.ComputerName
                            InstanceName   = $server.ServiceName
                            SqlInstance    = $server.DomainInstanceName
                            Job            = $currentJob.Name
                            JobStep        = $jobstep.Name
                            OutputFileName = $currentoutputfile
                        }
                    }
                }
                catch {
                    Stop-Function -Message "Failed to add $OutputFile to $jobstep for $currentJob" -InnerErrorRecord $_ -Target $currentJob
                }
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Set-DbaAgentJobStep {
    <#
.SYNOPSIS
Set-DbaAgentJobStep updates a job step.

.DESCRIPTION
Set-DbaAgentJobStep updates a job step in the SQL Server Agent with parameters supplied.

.PARAMETER SqlInstance
SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Job
The name of the job. Can be null if the the job id is being used.

.PARAMETER StepName
The name of the step.

.PARAMETER NewName
The new name for the step in case it needs to be renamed.

.PARAMETER SubSystem
The subsystem used by the SQL Server Agent service to execute command.
Allowed values 'ActiveScripting','AnalysisCommand','AnalysisQuery','CmdExec','Distribution','LogReader','Merge','PowerShell','QueueReader','Snapshot','Ssis','TransactSql'

.PARAMETER Command
The commands to be executed by SQLServerAgent service through subsystem.

.PARAMETER CmdExecSuccessCode
The value returned by a CmdExec subsystem command to indicate that command executed successfully.

.PARAMETER OnSuccessAction
The action to perform if the step succeeds.
Allowed values  "QuitWithSuccess" (default), "QuitWithFailure", "GoToNextStep", "GoToStep".
The text value van either be lowercase, uppercase or something in between as long as the text is correct.

.PARAMETER OnSuccessStepId
The ID of the step in this job to execute if the step succeeds and OnSuccessAction is "GoToNextStep".

.PARAMETER OnFailAction
The action to perform if the step fails.
Allowed values  "QuitWithSuccess" (default), "QuitWithFailure", "GoToNextStep", "GoToStep".
The text value van either be lowercase, uppercase or something in between as long as the text is correct.

.PARAMETER OnFailStepId
The ID of the step in this job to execute if the step fails and OnFailAction is "GoToNextStep".

.PARAMETER Database
The name of the database in which to execute a Transact-SQL step. The default is 'master'.

.PARAMETER DatabaseUser
The name of the user account to use when executing a Transact-SQL step. The default is 'sa'.

.PARAMETER RetryAttempts
The number of retry attempts to use if this step fails. The default is 0.

.PARAMETER RetryInterval
The amount of time in minutes between retry attempts. The default is 0.

.PARAMETER OutputFileName
The name of the file in which the output of this step is saved.

.PARAMETER Flag
Sets the flag(s) for the job step.

Flag                                    Description
----------------------------------------------------------------------------
AppendAllCmdExecOutputToJobHistory      Job history, including command output, is appended to the job history file.
AppendToJobHistory                      Job history is appended to the job history file.
AppendToLogFile                         Job history is appended to the SQL Server log file.
AppendToTableLog                        Job history is appended to a log table.
LogToTableWithOverwrite                 Job history is written to a log table, overwriting previous contents.
None                                    Job history is not appended to a file.
ProvideStopProcessEvent                 Job processing is stopped.

.PARAMETER ProxyName
The name of the proxy that the job step runs as.

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.PARAMETER Force
The force parameter will ignore some errors in the parameters and assume defaults.

.NOTES
Author: Sander Stad (@sqlstad, sqlstad.nl)
Tags: Agent, Job, JobStep

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Set-DbaAgentJobStep

.EXAMPLE
Set-DbaAgentJobStep -SqlInstance sql1 -Job Job1 -StepName Step1 -NewName Step2
Changes the name of the step in "Job1" with the name Step1 to Step2

.EXAMPLE
Set-DbaAgentJobStep -SqlInstance sql1 -Job Job1 -StepName Step1 -Database msdb
Changes the database of the step in "Job1" with the name Step1 to msdb

.EXAMPLE
Set-DbaAgentJobStep -SqlInstance sql1 -Job Job1, Job2 -StepName Step1 -Database msdb
Changes job steps in multiple jobs with the name Step1 to msdb

.EXAMPLE
Set-DbaAgentJobStep -SqlInstance sql1, sql2, sql3 -Job Job1, Job2 -StepName Step1 -Database msdb
Changes job steps in multiple jobs on multiple servers with the name Step1 to msdb

.EXAMPLE
Set-DbaAgentJobStep -SqlInstance sql1, sql2, sql3 -Job Job1 -StepName Step1 -Database msdb
Changes the database of the step in "Job1" with the name Step1 to msdb for multiple servers

.EXAMPLE
sql1, sql2, sql3 | Set-DbaAgentJobStep -Job Job1 -StepName Step1 -Database msdb
Changes the database of the step in "Job1" with the name Step1 to msdb for multiple servers using pipeline

#>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Parameter(Mandatory = $false)]
        [PSCredential]$SqlCredential,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [object[]]$Job,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$StepName,
        [Parameter(Mandatory = $false)]
        [string]$NewName,
        [Parameter(Mandatory = $false)]
        [ValidateSet('ActiveScripting', 'AnalysisCommand', 'AnalysisQuery', 'CmdExec', 'Distribution', 'LogReader', 'Merge', 'PowerShell', 'QueueReader', 'Snapshot', 'Ssis', 'TransactSql')]
        [string]$Subsystem,
        [Parameter(Mandatory = $false)]
        [string]$Command,
        [Parameter(Mandatory = $false)]
        [int]$CmdExecSuccessCode,
        [Parameter(Mandatory = $false)]
        [ValidateSet('QuitWithSuccess', 'QuitWithFailure', 'GoToNextStep', 'GoToStep')]
        [string]$OnSuccessAction,
        [Parameter(Mandatory = $false)]
        [int]$OnSuccessStepId,
        [Parameter(Mandatory = $false)]
        [ValidateSet('QuitWithSuccess', 'QuitWithFailure', 'GoToNextStep', 'GoToStep')]
        [string]$OnFailAction,
        [Parameter(Mandatory = $false)]
        [int]$OnFailStepId,
        [Parameter(Mandatory = $false)]
        [string]$Database,
        [Parameter(Mandatory = $false)]
        [string]$DatabaseUser,
        [Parameter(Mandatory = $false)]
        [int]$RetryAttempts,
        [Parameter(Mandatory = $false)]
        [int]$RetryInterval,
        [Parameter(Mandatory = $false)]
        [string]$OutputFileName,
        [Parameter(Mandatory = $false)]
        [ValidateSet('AppendAllCmdExecOutputToJobHistory', 'AppendToJobHistory', 'AppendToLogFile', 'LogToTableWithOverwrite', 'None', 'ProvideStopProcessEvent')]
        [string[]]$Flag,
        [Parameter(Mandatory = $false)]
        [string]$ProxyName,
        [Parameter(Mandatory = $false)]
        [Alias('Silent')]
        [switch]$EnableException,
        [Parameter(Mandatory = $false)]
        [switch]$Force
    )

    begin {
        # Check the parameter on success step id
        if (($OnSuccessAction -ne 'GoToStep') -and ($OnSuccessStepId -ge 1)) {
            Stop-Function -Message "Parameter OnSuccessStepId can only be used with OnSuccessAction 'GoToStep'." -Target $SqlInstance
            return
        }

        # Check the parameter on success step id
        if (($OnFailAction -ne 'GoToStep') -and ($OnFailStepId -ge 1)) {
            Stop-Function -Message "Parameter OnFailStepId can only be used with OnFailAction 'GoToStep'." -Target $SqlInstance
            return
        }
    }

    process {

        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $sqlinstance) {

            # Try connecting to the instance
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $Server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            foreach ($j in $Job) {

                # Check if the job exists
                if ($Server.JobServer.Jobs.Name -notcontains $j) {
                    Stop-Function -Message "Job $j doesn't exists on $instance" -Target $instance -Continue
                }
                else {
                    # Check if the job step exists
                    if ($Server.JobServer.Jobs[$j].JobSteps.Name -notcontains $StepName) {
                        Stop-Function -Message "Step $StepName doesn't exists for job $j" -Target $instance -Continue
                    }
                    else {

                        # Get the job step
                        $JobStep = $Server.JobServer.Jobs[$j].JobSteps[$StepName]

                        Write-Message -Message "Modifying job $j on $instance" -Level Verbose

                        #region job step options
                        # Setting the options for the job step
                        if ($NewName) {
                            Write-Message -Message "Setting job step name to $NewName" -Level Verbose
                            $JobStep.Rename($NewName)
                        }

                        if ($Subsystem) {
                            Write-Message -Message "Setting job step subsystem to $Subsystem" -Level Verbose
                            $JobStep.Subsystem = $Subsystem
                        }

                        if ($Command) {
                            Write-Message -Message "Setting job step command to $Command" -Level Verbose
                            $JobStep.Command = $Command
                        }

                        if ($CmdExecSuccessCode) {
                            Write-Message -Message "Setting job step command exec success code to $CmdExecSuccessCode" -Level Verbose
                            $JobStep.CommandExecutionSuccessCode = $CmdExecSuccessCode
                        }

                        if ($OnSuccessAction) {
                            Write-Message -Message "Setting job step success action to $OnSuccessAction" -Level Verbose
                            $JobStep.OnSuccessAction = $OnSuccessAction
                        }

                        if ($OnSuccessStepId) {
                            Write-Message -Message "Setting job step success step id to $OnSuccessStepId" -Level Verbose
                            $JobStep.OnSuccessStep = $OnSuccessStepId
                        }

                        if ($OnFailAction) {
                            Write-Message -Message "Setting job step fail action to $OnFailAction" -Level Verbose
                            $JobStep.OnFailAction = $OnFailAction
                        }

                        if ($OnFailStepId) {
                            Write-Message -Message "Setting job step fail step id to $OnFailStepId" -Level Verbose
                            $JobStep.OnFailStep = $OnFailStepId
                        }

                        if ($Database) {
                            # Check if the database is present on the server
                            if ($Server.Databases.Name -contains $Database) {
                                Write-Message -Message "Setting job step database name to $Database" -Level Verbose
                                $JobStep.DatabaseName = $Database
                            }
                            else {
                                Stop-Function -Message "The database is not present on instance $instance." -Target $instance -Continue
                            }
                        }

                        if (($DatabaseUser) -and ($Database)) {
                            # Check if the username is present in the database
                            if ($Server.Databases[$Database].Users.Name -contains $DatabaseUser) {
                                Write-Message -Message "Setting job step database username to $DatabaseUser" -Level Verbose
                                $JobStep.DatabaseUserName = $DatabaseUser
                            }
                            else {
                                Stop-Function -Message "The database user is not present in the database $Database on instance $instance." -Target $instance -Continue
                            }
                        }

                        if ($RetryAttempts) {
                            Write-Message -Message "Setting job step retry attempts to $RetryAttempts" -Level Verbose
                            $JobStep.RetryAttempts = $RetryAttempts
                        }

                        if ($RetryInterval) {
                            Write-Message -Message "Setting job step retry interval to $RetryInterval" -Level Verbose
                            $JobStep.RetryInterval = $RetryInterval
                        }

                        if ($OutputFileName) {
                            Write-Message -Message "Setting job step output file name to $OutputFileName" -Level Verbose
                            $JobStep.OutputFileName = $OutputFileName
                        }

                        if ($ProxyName) {
                            # Check if the proxy exists
                            if ($Server.JobServer.ProxyAccounts.Name -contains $ProxyName) {
                                Write-Message -Message "Setting job step proxy name to $ProxyName" -Level Verbose
                                $JobStep.ProxyName = $ProxyName
                            }
                            else {
                                Stop-Function -Message "The proxy name $ProxyName doesn't exist on instance $instance." -Target $instance -Continue
                            }
                        }

                        if ($Flag.Count -ge 1) {
                            Write-Message -Message "Setting job step flag(s) to $($Flags -join ',')" -Level Verbose
                            $JobStep.JobStepFlags = $Flag
                        }
                        #region job step options

                        # Execute
                        if ($PSCmdlet.ShouldProcess($instance, "Changing the job step $StepName for job $j")) {
                            try {
                                Write-Message -Message "Changing the job step $StepName for job $j" -Level Verbose

                                # Change the job step
                                $JobStep.Alter()
                            }
                            catch {
                                Stop-Function -Message "Something went wrong changing the job step" -ErrorRecord $_ -Target $instance -Continue
                            }
                        }
                    }
                }

            } # foreach object job
        } # foreach object intance
    } # process

    end {
        if (Test-FunctionInterrupt) { return }
        Write-Message -Message "Finished changing job step(s)" -Level Verbose
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Set-DbaAgentSchedule {
    <#
.SYNOPSIS
Set-DbaAgentSchedule updates a schedule in the msdb database.

.DESCRIPTION
Set-DbaAgentSchedule will help update a schedule for a job. It does not attach the schedule to a job.

.PARAMETER SqlInstance
SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Job
The name of the job that has the schedule.

.PARAMETER ScheduleName
The name of the schedule.

.PARAMETER NewName
The new name for the schedule.

.PARAMETER Enabled
Set the schedule to enabled.

.PARAMETER Disabled
Set the schedule to disabled.

.PARAMETER FrequencyType
A value indicating when a job is to be executed.
Allowed values are 1, "Once", 4, "Daily", 8, "Weekly", 16, "Monthly", 32, "MonthlyRelative", 64, "AgentStart", 128 or "IdleComputer"

.PARAMETER FrequencyInterval
The days that a job is executed
Allowed values are 1, "Sunday", 2, "Monday", 4, "Tuesday", 8, "Wednesday", 16, "Thursday", 32, "Friday", 64, "Saturday", 62, "Weekdays", 65, "Weekend", 127, "EveryDay".
If 62, "Weekdays", 65, "Weekend", 127, "EveryDay" is used it overwwrites any other value that has been passed before.

.PARAMETER FrequencySubdayType
Specifies the units for the subday FrequencyInterval.
Allowed values are 1, "Time", 2, "Seconds", 4, "Minutes", 8 or "Hours"

.PARAMETER FrequencySubdayInterval
The number of subday type periods to occur between each execution of a job.

.PARAMETER FrequencySubdayInterval
The number of subday type periods to occur between each execution of a job.

.PARAMETER FrequencyRelativeInterval
A job's occurrence of FrequencyInterval in each month, if FrequencyInterval is 32 (monthlyrelative).

.PARAMETER FrequencyRecurrenceFactor
The number of weeks or months between the scheduled execution of a job. FrequencyRecurrenceFactor is used only if FrequencyType is 8, "Weekly", 16, "Monthly", 32 or "MonthlyRelative".

.PARAMETER StartDate
The date on which execution of a job can begin.

.PARAMETER EndDate
The date on which execution of a job can stop.

.PARAMETER StartTime
The time on any day to begin execution of a job. Format HHMMSS / 24 hour clock.
Example: '010000' for 01:00:00 AM.
Example: '140000' for 02:00:00 PM.

.PARAMETER EndTime
The time on any day to end execution of a job. Format HHMMSS / 24 hour clock.
Example: '010000' for 01:00:00 AM.
Example: '140000' for 02:00:00 PM.

.PARAMETER Owner
The name of the server principal that owns the schedule. If no value is given the schedule is owned by the creator.

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.PARAMETER Force
The force parameter will ignore some errors in the parameters and assume defaults.
It will also remove the any present schedules with the same name for the specific job.

.NOTES
Author: Sander Stad (@sqlstad, sqlstad.nl)
Tags: Agent, Job, JobStep

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Set-DbaAgentSchedule

.EXAMPLE
Set-DbaAgentSchedule -SqlInstance sql1 -Job Job1 -ScheduleName daily -Enabled
Changes the schedule for Job1 with the name 'daily' to enabled

.EXAMPLE
Set-DbaAgentSchedule -SqlInstance sql1 -Job Job1 -ScheduleName daily -NewName weekly -FrequencyType Weekly -FrequencyInterval Monday, Wednesday, Friday
Changes the schedule for Job1 with the name daily to have a new name weekly

.EXAMPLE
Set-DbaAgentSchedule -SqlInstance sql1 -Job Job1, Job2, Job3 -ScheduleName daily -StartTime '230000'
Changes the start time of the schedule for Job1 to 11 PM for multiple jobs

.EXAMPLE
Set-DbaAgentSchedule -SqlInstance sql1, sql2, sql3 -Job Job1 -ScheduleName daily -Enabled
Changes the schedule for Job1 with the name daily to enabled on multiple servers

.EXAMPLE
sql1, sql2, sql3 | Set-DbaAgentSchedule -Job Job1 -ScheduleName 'daily' -Enabled
Changes the schedule for Job1 with the name 'daily' to enabled on multiple servers using pipe line

#>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]

    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Parameter(Mandatory = $false)]
        [PSCredential]$SqlCredential,
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [ValidateNotNullOrEmpty()]
        [object[]]$Job,
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$ScheduleName,
        [Parameter(Mandatory = $false)]
        [string]$NewName,
        [Parameter(Mandatory = $false)]
        [switch]$Enabled,
        [Parameter(Mandatory = $false)]
        [switch]$Disabled,
        [ValidateSet(1, "Once", 4, "Daily", 8, "Weekly", 16, "Monthly", 32, "MonthlyRelative", 64, "AgentStart", 128, "IdleComputer")]
        [object]$FrequencyType,
        [Parameter(Mandatory = $false)]
        [object[]]$FrequencyInterval,
        [Parameter(Mandatory = $false)]
        [ValidateSet(1, "Time", 2, "Seconds", 4, "Minutes", 8, "Hours")]
        [object]$FrequencySubdayType,
        [Parameter(Mandatory = $false)]
        [int]$FrequencySubdayInterval,
        [Parameter(Mandatory = $false)]
        [ValidateSet('Unused', 'First', 'Second', 'Third', 'Fourth', 'Last')]
        [object]$FrequencyRelativeInterval,
        [Parameter(Mandatory = $false)]
        [int]$FrequencyRecurrenceFactor,
        [Parameter(Mandatory = $false)]
        [string]$StartDate,
        [Parameter(Mandatory = $false)]
        [string]$EndDate,
        [Parameter(Mandatory = $false)]
        [string]$StartTime,
        [Parameter(Mandatory = $false)]
        [string]$EndTime,
        [Parameter(Mandatory = $false)]
        [Alias('Silent')]
        [switch]$EnableException,
        [Parameter(Mandatory = $false)]
        [switch]$Force
    )

    begin {

        # Check of the FrequencyType value is of type string and set the integer value
        if ($FrequencyType -notin 0, 1, 4, 8, 16, 32, 64, 128) {
            [int]$FrequencyType = switch ($FrequencyType) { "Once" { 1 } "Daily" { 4 } "Weekly" { 8 } "Monthly" { 16 } "MonthlyRelative" { 32 } "AgentStart" { 64 } "IdleComputer" { 128 } }
        }

        # Check of the FrequencySubdayType value is of type string and set the integer value
        if ($FrequencySubdayType -notin 0, 1, 2, 4, 8) {
            [int]$FrequencySubdayType = switch ($FrequencySubdayType) { "Time" { 1 } "Seconds" { 2 } "Minutes" { 4 } "Hours" { 8 } default {0} }
        }

        # Check if the interval is valid
        if (($FrequencyType -eq 4) -and ($FrequencyInterval -lt 1 -or $FrequencyInterval -ge 365)) {
            Stop-Function -Message "The interval $FrequencyInterval needs to be higher than 1 and lower than 365 when using a daily frequency the interval." -Target $SqlInstance
            return
        }

        # Check if the recurrence factor is set for weekly or monthly interval
        if (($FrequencyType -in 8, 16) -and $FrequencyRecurrenceFactor -lt 1) {
            if ($Force) {
                $FrequencyRecurrenceFactor = 1
                Write-Message -Message "Recurrence factor not set for weekly or monthly interval. Setting it to $FrequencyRecurrenceFactor." -Level Verbose
            }
            else {
                Stop-Function -Message "The recurrence factor $FrequencyRecurrenceFactor needs to be at least on when using a weekly or monthly interval." -Target $SqlInstance
                return
            }
        }

        # Check the subday interval
        if (($FrequencySubdayType -in 2, 4) -and (-not ($FrequencySubdayInterval -ge 1 -or $FrequencySubdayInterval -le 59))) {
            Stop-Function -Message "Subday interval $FrequencySubdayInterval must be between 1 and 59 when subday type is 2, 'Seconds', 4 or 'Minutes'" -Target $SqlInstance
            return
        }
        elseif (($FrequencySubdayType -eq 8) -and (-not ($FrequencySubdayInterval -ge 1 -and $FrequencySubdayInterval -le 23))) {
            Stop-Function -Message "Subday interval $FrequencySubdayInterval must be between 1 and 23 when subday type is 8 or 'Hours" -Target $SqlInstance
            return
        }

        # Check of the FrequencyInterval value is of type string and set the integer value
        if (($null -ne $FrequencyType)) {
            # Create the interval to hold the value(s)
            [int]$Interval = 0

            # If the FrequencyInterval is set for the weekly FrequencyType
            if ($FrequencyType -eq 8) {
                # Loop through the array
                foreach ($Item in $FrequencyInterval) {
                    switch ($Item) {
                        "Sunday" { $Interval += 1 }
                        "Monday" { $Interval += 2 }
                        "Tuesday" { $Interval += 4 }
                        "Wednesday" { $Interval += 8 }
                        "Thursday" { $Interval += 16 }
                        "Friday" { $Interval += 32 }
                        "Saturday" { $Interval += 64 }
                        "Weekdays" { $Interval = 62 }
                        "Weekend" { $Interval = 65 }
                        "EveryDay" {$Interval = 127 }
                        1 { $Interval += 1 }
                        2 { $Interval += 2 }
                        4 { $Interval += 4 }
                        8 { $Interval += 8 }
                        16 { $Interval += 16 }
                        31 { $Interval += 32 }
                        64 { $Interval += 64 }
                        62 { $Interval = 62 }
                        65 { $Interval = 65 }
                        127 {$Interval = 127 }
                    }
                }
            }

            # If the FrequencyInterval is set for the relative monthly FrequencyInterval
            if ($FrequencyType -eq 32) {
                # Loop through the array
                foreach ($Item in $FrequencyInterval) {
                    switch ($Item) {
                        "Sunday" { $Interval += 1 }
                        "Monday" { $Interval += 2 }
                        "Tuesday" { $Interval += 3 }
                        "Wednesday" { $Interval += 4 }
                        "Thursday" { $Interval += 5 }
                        "Friday" { $Interval += 6 }
                        "Saturday" { $Interval += 7 }
                        "Day" { $Interval += 8 }
                        "Weekday" { $Interval += 9 }
                        "WeekendDay" { $Interval += 10 }
                        1 { $Interval += 1 }
                        2 { $Interval += 2 }
                        3 { $Interval += 3 }
                        4 { $Interval += 4 }
                        5 { $Interval += 5 }
                        6 { $Interval += 6 }
                        7 { $Interval += 7 }
                        8 { $Interval += 8 }
                        9 { $Interval += 9 }
                        10 { $Interval += 10 }
                    }
                }
            }
        }

        # Check of the relative FrequencyInterval value is of type string and set the integer value
        if (($FrequencyRelativeInterval -notin 1, 2, 4, 8, 16) -and $null -ne $FrequencyRelativeInterval) {
            [int]$FrequencyRelativeInterval = switch ($FrequencyRelativeInterval) { "First" { 1 } "Second" { 2 } "Third" { 4 } "Fourth" { 8 } "Last" { 16 } "Unused" { 0 } default { 0 }}
        }

        # Check if the interval is valid
        if (($FrequencyType -eq 4) -and ($FrequencyInterval -lt 1 -or $FrequencyInterval -ge 365)) {
            Stop-Function -Message "The interval $FrequencyInterval needs to be higher than 1 and lower than 365 when using a daily frequency the interval." -Target $SqlInstance
            return
        }

        # Setup the regex
        $RegexDate = '(?<!\d)(?:(?:(?:1[6-9]|[2-9]\d)?\d{2})(?:(?:(?:0[13578]|1[02])31)|(?:(?:0[1,3-9]|1[0-2])(?:29|30)))|(?:(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))0229)|(?:(?:1[6-9]|[2-9]\d)?\d{2})(?:(?:0?[1-9])|(?:1[0-2]))(?:0?[1-9]|1\d|2[0-8]))(?!\d)'
        $RegexTime = '^(?:(?:([01]?\d|2[0-3]))?([0-5]?\d))?([0-5]?\d)$'

        # Check the start date
        if ($StartDate -and ($StartDate -notmatch $RegexDate)) {
            Stop-Function -Message "Start date $StartDate needs to be a valid date with format yyyyMMdd" -Target $SqlInstance
            return
        }

        # Check the end date
        if ($EndDate -and ($EndDate -notmatch $RegexDate)) {
            Stop-Function -Message "End date $EndDate needs to be a valid date with format yyyyMMdd" -Target $SqlInstance
            return
        }
        elseif ($EndDate -lt $StartDate) {
            Stop-Function -Message "End date $EndDate cannot be before start date $StartDate" -Target $SqlInstance
            return
        }

        # Check the start time
        if ($StartTime -and ($StartTime -notmatch $RegexTime)) {
            Stop-Function -Message "Start time $StartTime needs to match between '000000' and '235959'" -Target $SqlInstance
            return
        }

        # Check the end time
        if ($EndTime -and ($EndTime -notmatch $RegexTime)) {
            Stop-Function -Message "End time $EndTime needs to match between '000000' and '235959'" -Target $SqlInstance
            return
        }
    }

    process {

        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $sqlinstance) {

            foreach ($j in $Job) {

                # Try connecting to the instance
                Write-Message -Message "Connecting to $instance" -Level Verbose
                try {
                    $Server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
                }
                catch {
                    Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                }

                # Check if the job exists
                if ($Server.JobServer.Jobs.Name -notcontains $j) {
                    Write-Message -Message "Job $j doesn't exists on $instance" -Level Warning
                }
                else {
                    # Check if the job schedule exists
                    if ($Server.JobServer.Jobs[$j].JobSchedules.Name -notcontains $ScheduleName) {
                        Stop-Function -Message "Schedule $ScheduleName doesn't exists for job $j on $instance" -Target $instance -Continue
                    }
                    else {
                        # Get the job schedule
                        # If for some reason the there are multiple schedules with the same name, the first on is chosen
                        $JobSchedule = $Server.JobServer.Jobs[$j].JobSchedules[$ScheduleName][0]

                        #region job step options
                        # Setting the options for the job schedule
                        if ($NewName) {
                            Write-Message -Message "Setting job schedule name to $NewName for schedule $ScheduleName" -Level Verbose
                            $JobSchedule.Rename($NewName)
                        }

                        if ($Enabled) {
                            Write-Message -Message "Setting job schedule to enabled for schedule $ScheduleName" -Level Verbose
                            $JobSchedule.IsEnabled = $true
                        }

                        if ($Disabled) {
                            Write-Message -Message "Setting job schedule to disabled for schedule $ScheduleName" -Level Verbose
                            $JobSchedule.IsEnabled = $false
                        }

                        if ($FrequencyType -ge 1) {
                            Write-Message -Message "Setting job schedule frequency to $FrequencyType for schedule $ScheduleName" -Level Verbose
                            $JobSchedule.FrequencyTypes = $FrequencyType
                        }

                        if ($Interval -ge 1) {
                            Write-Message -Message "Setting job schedule frequency interval to $Interval for schedule $ScheduleName" -Level Verbose
                            $JobSchedule.FrequencyInterval = $Interval
                        }

                        if ($FrequencySubdayType -ge 1) {
                            Write-Message -Message "Setting job schedule frequency subday type to $FrequencySubdayType for schedule $ScheduleName" -Level Verbose
                            $JobSchedule.FrequencySubDayTypes = $FrequencySubdayType
                        }

                        if ($FrequencySubdayInterval -ge 1) {
                            Write-Message -Message "Setting job schedule frequency subday interval to $FrequencySubdayInterval for schedule $ScheduleName" -Level Verbose
                            $JobSchedule.FrequencySubDayInterval = $FrequencySubdayInterval
                        }

                        if (($FrequencyRelativeInterval -ge 1) -and ($FrequencyType -eq 32)) {
                            Write-Message -Message "Setting job schedule frequency relative interval to $FrequencyRelativeInterval for schedule $ScheduleName" -Level Verbose
                            $JobSchedule.FrequencyRelativeIntervals = $FrequencyRelativeInterval
                        }

                        if (($FrequencyRecurrenceFactor -ge 1) -and ($FrequencyType -in 8, 16, 32)) {
                            Write-Message -Message "Setting job schedule frequency recurrence factor to $FrequencyRecurrenceFactor for schedule $ScheduleName" -Level Verbose
                            $JobSchedule.FrequencyRecurrenceFactor = $FrequencyRecurrenceFactor
                        }

                        if ($StartDate) {
                            $StartDate = $StartDate.Insert(6, '-').Insert(4, '-')
                            Write-Message -Message "Setting job schedule start date to $StartDate for schedule $ScheduleName" -Level Verbose
                            $JobSchedule.StartDate = $StartDate
                        }

                        if ($EndDate) {
                            $EndDate = $EndDate.Insert(6, '-').Insert(4, '-')
                            Write-Message -Message "Setting job schedule end date to $EndDate for schedule $ScheduleName" -Level Verbose
                            $JobSchedule.EndDate = $EndDate
                        }

                        if ($StartTime) {
                            $StartTime = $StartTime.Insert(4, ':').Insert(2, ':')
                            Write-Message -Message "Setting job schedule start time to $StartTime for schedule $ScheduleName" -Level Verbose
                            $JobSchedule.ActiveStartTimeOfDay = $StartTime
                        }

                        if ($EndTime) {
                            $EndTime = $EndTime.Insert(4, ':').Insert(2, ':')
                            Write-Message -Message "Setting job schedule end time to $EndTime for schedule $ScheduleName" -Level Verbose
                            $JobSchedule.ActiveStartTimeOfDay = $EndTime
                        }
                        #endregion job step options

                        # Execute the query
                        if ($PSCmdlet.ShouldProcess($instance, "Changing the schedule $ScheduleName for job $j on $instance")) {
                            try {
                                # Excute the query and save the result
                                Write-Message -Message "Changing the schedule $ScheduleName for job $j" -Level Verbose

                                $JobSchedule.Alter()

                            }
                            catch {
                                Stop-Function -Message "Something went wrong changing the schedule" -Target $instance -ErrorRecord $_ -Continue
                                return
                            }
                        }
                    }
                }
            } # foreach object job
        } # foreach object instance
    } # process

    end {
        if (Test-FunctionInterrupt) { return }
        Write-Message -Message "Finished changing the job schedule(s)" -Level Verbose
    }
}
function Set-DbaCmConnection {
    <#
        .SYNOPSIS
            Configures a connection object for use in remote computer management.

        .DESCRIPTION
            Configures a connection object for use in remote computer management.
            This function will either create new records for computers that have no connection registered so far, or it will configure existing connections if already present.

            As such it can be handy in making bulk-edits on connections or manually adjusting some settings.

        .PARAMETER ComputerName
            The computer to build the connection object for.

        .PARAMETER Credential
            The credential to register.

        .PARAMETER UseWindowsCredentials
            Whether using the default windows credentials is legit.
            Not setting this will not exclude using windows credentials, but only not pre-confirm them as working.

        .PARAMETER OverrideExplicitCredential
            Setting this will enable the credential override.
            The override will cause the system to ignore explicitly specified credentials, so long as known, good credentials are available.

        .PARAMETER OverrideConnectionPolicy
            Setting this will configure the connection policy override.
            By default, global configurations enforce, which connection type is available at all and which is disabled.

        .PARAMETER DisabledConnectionTypes
            Exlicitly disable connection types.
            These types will then not be used for connecting to the computer.

        .PARAMETER DisableBadCredentialCache
            Will prevent the caching of credentials if set to true.

        .PARAMETER DisableCimPersistence
            Will prevent Cim-Sessions to be reused.

        .PARAMETER DisableCredentialAutoRegister
            Will prevent working credentials from being automatically cached

        .PARAMETER EnableCredentialFailover
            Will enable automatic failing over to known to work credentials, when using bad credentials.
            By default, passing bad credentials will cause the Computer Management functions to interrupt with a warning (Or exception if in silent mode).

        .PARAMETER WindowsCredentialsAreBad
            Will prevent the windows credentials of the currently logged on user from being used for the remote connection.

        .PARAMETER CimWinRMOptions
            Specify a set of options to use when connecting to the target computer using CIM over WinRM.
            Use 'New-CimSessionOption' to create such an object.

        .PARAMETER CimDCOMOptions
            Specify a set of options to use when connecting to the target computer using CIM over DCOM.
            Use 'New-CimSessionOption' to create such an object.

        .PARAMETER AddBadCredential
            Adds credentials to the bad credential cache.
            These credentials will not be used when connecting to the target remote computer.

        .PARAMETER RemoveBadCredential
            Removes credentials from the bad credential cache.

        .PARAMETER ClearBadCredential
            Clears the cache of credentials that didn't worked.
            Will be applied before adding entries to the credential cache.

        .PARAMETER ClearCredential
            Clears the cache of credentials that worked.
            Will be applied before adding entries to the credential cache.

        .PARAMETER ResetCredential
            Resets all credential-related caches:
            - Clears bad credential cache
            - Removes last working credential
            - Un-Confirms the windows credentials as working
            - Un-Confirms the windows credentials as not working

            Automatically implies the parameters -ClearCredential and -ClearBadCredential. Using them together is redundant.
            Will be applied before adding entries to the credential cache.

        .PARAMETER ResetConnectionStatus
            Restores all connection stati to default, as if no connection protocol had ever been tested.

        .PARAMETER ResetConfiguration
            Restores the configuration back to system default.
            Configuration elements are the basic behavior controlling settings, such as whether to cache bad credentials, etc.
            These can be configured globally using the dbatools configuration system and overridden locally on a per-connection basis.
            For a list of all available settings, use "Get-DbaConfig -Module ComputerManagement".

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Fred Winmann (@FredWeinmann)
            Tags: ComputerManagement, CIM

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/set-DbaCmConnection

        .EXAMPLE
            Get-DbaCmConnection sql2014 | Set-DbaCmConnection -ClearBadCredential -UseWindowsCredentials

            Retrieves the already existing connection to sql2014, removes the list of not working credentials and configures it to default to the credentials of the logged on user.

        .EXAMPLE
            Get-DbaCmConnection | Set-DbaCmConnection -RemoveBadCredential $cred
            Removes the credentials stored in $cred from all connections' list of "known to not work" credentials.
            Handy to update changes in privilege.

        .EXAMPLE
            Get-DbaCmConnection | Export-Clixml .\connections.xml
            Import-Clixml .\connections.xml | Set-DbaCmConnection -ResetConfiguration

            At first, the current cached connections are stored in an xml file. At a later time - possibly in the profile when starting the console again - those connections are imported again and applied again to the connection cache.

            In this example, the configuration settings will also be reset, since after reimport those will be set to explicit, rather than deriving them from the global settings.
            In many cases, using the default settings is desirable. For specific settings, use New-DbaCmConnection as part of the profile in order to explicitly configure a connection.
    #>
    [CmdletBinding(DefaultParameterSetName = 'Credential')]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Sqlcollaborative.Dbatools.Parameter.DbaCmConnectionParameter[]]
        $ComputerName = $env:COMPUTERNAME,

        [Parameter(ParameterSetName = "Credential")]
        [PSCredential]
        $Credential,

        [Parameter(ParameterSetName = "Windows")]
        [switch]
        $UseWindowsCredentials,

        [switch]
        $OverrideExplicitCredential,

        [switch]
        $OverrideConnectionPolicy,

        [Sqlcollaborative.Dbatools.Connection.ManagementConnectionType]
        $DisabledConnectionTypes = 'None',

        [switch]
        $DisableBadCredentialCache,

        [switch]
        $DisableCimPersistence,

        [switch]
        $DisableCredentialAutoRegister,

        [switch]
        $EnableCredentialFailover,

        [Parameter(ParameterSetName = "Credential")]
        [switch]
        $WindowsCredentialsAreBad,

        [Microsoft.Management.Infrastructure.Options.WSManSessionOptions]
        $CimWinRMOptions,

        [Microsoft.Management.Infrastructure.Options.DComSessionOptions]
        $CimDCOMOptions,

        [System.Management.Automation.PSCredential[]]
        $AddBadCredential,

        [System.Management.Automation.PSCredential[]]
        $RemoveBadCredential,

        [switch]
        $ClearBadCredential,

        [switch]
        $ClearCredential,

        [switch]
        $ResetCredential,

        [switch]
        $ResetConnectionStatus,

        [switch]
        $ResetConfiguration,

        [switch]
        [Alias('Silent')]$EnableException
    )

    BEGIN {
        Write-Message -Level InternalComment -Message "Starting execution"
        Write-Message -Level Verbose -Message "Bound parameters: $($PSBoundParameters.Keys -join ", ")"

        $disable_cache = Get-DbaConfigValue -Name 'ComputerManagement.Cache.Disable.All' -Fallback $false
    }
    PROCESS {
        foreach ($connectionObject in $ComputerName) {
            if (-not $connectionObject.Success) { Stop-Function -Message "Failed to interpret computername input: $($connectionObject.InputObject)" -Category InvalidArgument -Target $connectionObject.InputObject -Continue }
            Write-Message -Level VeryVerbose -Message "Processing computer: $($connectionObject.Connection.ComputerName)"

            $connection = $connectionObject.Connection

            if ($ResetConfiguration) {
                Write-Message -Level Verbose -Message "Resetting the configuration to system default"

                $connection.RestoreDefaultConfiguration()
            }

            if ($ResetConnectionStatus) {
                Write-Message -Level Verbose -Message "Resetting the connection status"

                $connection.CimRM = 'Unknown'
                $connection.CimDCOM = 'Unknown'
                $connection.Wmi = 'Unknown'
                $connection.PowerShellRemoting = 'Unknown'

                $connection.LastCimRM = New-Object System.DateTime(0)
                $connection.LastCimDCOM = New-Object System.DateTime(0)
                $connection.LastWmi = New-Object System.DateTime(0)
                $connection.LastPowerShellRemoting = New-Object System.DateTime(0)
            }

            if ($ResetCredential) {
                Write-Message -Level Verbose -Message "Resetting credentials"

                $connection.KnownBadCredentials.Clear()
                $connection.Credentials = $null
                $connection.UseWindowsCredentials = $false
                $connection.WindowsCredentialsAreBad = $false
            }
            else {
                if ($ClearBadCredential) {
                    Write-Message -Level Verbose -Message "Clearing bad credentials"

                    $connection.KnownBadCredentials.Clear()
                    $connection.WindowsCredentialsAreBad = $false
                }

                if ($ClearCredential) {
                    Write-Message -Level Verbose -Message "Clearing credentials"

                    $connection.Credentials = $null
                    $connection.UseWindowsCredentials = $false
                }
            }

            foreach ($badCred in $RemoveBadCredential) {
                $connection.RemoveBadCredential($badCred)
            }

            foreach ($badCred in $AddBadCredential) {
                $connection.AddBadCredential($badCred)
            }

            if (Test-Bound "Credential") { $connection.Credentials = $Credential }
            if ($UseWindowsCredentials) {
                $connection.Credentials = $null
                $connection.UseWindowsCredentials = $UseWindowsCredentials
            }
            if (Test-Bound "OverrideExplicitCredential") { $connection.OverrideExplicitCredential = $OverrideExplicitCredential }
            if (Test-Bound "DisabledConnectionTypes") { $connection.DisabledConnectionTypes = $DisabledConnectionTypes }
            if (Test-Bound "DisableBadCredentialCache") { $connection.DisableBadCredentialCache = $DisableBadCredentialCache }
            if (Test-Bound "DisableCimPersistence") { $connection.DisableCimPersistence = $DisableCimPersistence }
            if (Test-Bound "DisableCredentialAutoRegister") { $connection.DisableCredentialAutoRegister = $DisableCredentialAutoRegister }
            if (Test-Bound "EnableCredentialFailover") { $connection.DisableCredentialAutoRegister = $EnableCredentialFailover }
            if (Test-Bound "WindowsCredentialsAreBad") { $connection.WindowsCredentialsAreBad = $WindowsCredentialsAreBad }
            if (Test-Bound "CimWinRMOptions") { $connection.CimWinRMOptions = $CimWinRMOptions }
            if (Test-Bound "CimDCOMOptions") { $connection.CimDCOMOptions = $CimDCOMOptions }
            if (Test-Bound "OverrideConnectionPolicy") { $connection.OverrideConnectionPolicy = $OverrideConnectionPolicy }

            if (-not $disable_cache) {
                Write-Message -Level Verbose -Message "Writing connection to cache"
                [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::Connections[$connectionObject.Connection.ComputerName] = $connection
            }
            else { Write-Message -Level Verbose -Message "Skipping writing to cache, since the cache has been disabled!" }
            $connection
        }
    }
    END {
        Write-Message -Level InternalComment -Message "Stopping execution"
    }
}
function Set-DbaConfig {
    <#
        .SYNOPSIS
            Sets configuration entries.

        .DESCRIPTION
            This function creates or changes configuration values.
            These are used in dbatools to provide dynamic configuration information outside the PowerShell variable system.

        .PARAMETER FullName
            The full name of a configuration element. Must be namespaced <Module>.<Name>.
            The name can have any number of sub-segments, in order to better group configurations thematically.

        .PARAMETER Name
            Name of the configuration entry. If an entry of exactly this non-casesensitive name already exists, its value will be overwritten.
            Duplicate names across different modules are possible and will be treated separately.
            If a name contains namespace notation and no module is set, the first namespace element will be used as module instead of name. Example:
            -Name "Nordwind.Server"
            Is Equivalent to
            -Name "Server" -Module "Nordwind"

        .PARAMETER Module
            This allows grouping configuration elements into groups based on the module/component they server.
            If this parameter is not set, the configuration element is stored under its name only, which increases the likelyhood of name conflicts in large environments.

        .PARAMETER Value
            The value to assign to the named configuration element.

        .PARAMETER Description
            Using this, the configuration setting is given a description, making it easier for a user to comprehend, what a specific setting is for.

        .PARAMETER Validation
            The name of the validation script used for input validation.
            These can be used to validate make sure that input is of the proper data type.
            New validation scripts can be registered using Register-DbaConfigValidation

        .PARAMETER Handler
            A scriptblock that is executed when a value is being set.
            Is only executed if the validation was successful (assuming there was a validation, of course)

        .PARAMETER Hidden
            Setting this parameter hides the configuration from casual discovery. Configurations with this set will only be returned by Get-Config, if the parameter "-Force" is used.
            This should be set for all system settings a user should have no business changing (e.g. for Infrastructure related settings such as mail server).

        .PARAMETER Default
            Setting this parameter causes the system to treat this configuration as a default setting. If the configuration already exists, no changes will be performed.
            Useful in scenarios where for some reason it is not practical to automatically set defaults before loading userprofiles.

        .PARAMETER Initialize
            Use this when setting configurations as part of module import.
            When initializing a configuration, it will only do a thing if the configuration hasn't already been initialized (So if you load the module multiple times or in multiple runspaces, it won't make a difference)
            Also, if there already was a non-initialized setting set for a given configuration, it will then try to set the old value again.
            This value will be processed by handlers, if any are set.

        .PARAMETER DisableValidation
            This parameters disables the input validation - if any - when processing a setting.
            Normally this shouldn't be circumvented, but just in case, it can be disabled.

        .PARAMETER DisableHandler
            Internal Use Only.
            This parameter disables the configuration handlers.
            Configuration handlers are designed to automatically validate and process input set to a config value, in addition to writing the value.
            In many cases, this is used to improve performance, by forking the value location also to a static C#-field, which is then used, rather than searching a Hashtable.
            Sometimes it may only be used to introduce input validation.
            During module import, some handlers are registered and many values written to configuration.
            However, some of those values actually are already set as default values within the library. Processing a handler will cost a few ms.
            Add up a couple dozen such events and the delay is very notable.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Config, Module
            Author: Friedrich Weinmann

        .EXAMPLE
            PS C:\> Set-DbaConfig -Name 'User' -Value "Friedrich" -Description "The user under which the show must go on."

            Creates a configuration entry named "User" with the value "Friedrich"

        .EXAMPLE
            PS C:\> Set-DbaConfig -Name 'mymodule.User' -Value "Friedrich" -Description "The user under which the show must go on." -Handler $scriptBlock -Initialize -Validation String

            Creates a configuration entry ...
            - Named "mymodule.user"
            - With the value "Friedrich"
            - It adds a description as noted
            - It registers the scriptblock stored in $scriptBlock as handler
            - It initializes the script. This block only executes the first time a it is run like this. Subsequent calls will be ignored.
            - It registers the basic string input type validator
            This is the default example for modules using the configuration system.
            Note: While the -Handler parameter is optional, it is important to add it at the initial initialize call, if you are planning to add it.
            Only then will the system validate previous settings (such as what a user might have placed in his user profile)

        .EXAMPLE
            PS C:\> Set-DbaConfig 'ConfigLink' 'https://www.example.com/config.xml' 'Company' -Hidden

            Creates a configuration entry named "ConfigLink" in the "Company" module with the value 'https://www.example.com/config.xml'.
            This entry is hidden from casual discovery using Get-Config.

        .EXAMPLE
            PS C:\> Set-DbaConfig 'Network.Firewall' '10.0.0.2' -Default

            Creates a configuration entry named "Firewall" in the "Network" module with the value '10.0.0.2'
            This is only set, if the setting does not exist yet. If it does, this command will apply no changes.
    #>
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
    [CmdletBinding(DefaultParameterSetName = "FullName")]
    Param (
        [Parameter(ParameterSetName = "FullName", Position = 0, Mandatory = $true)]
        [string]
        $FullName,

        [Parameter(ParameterSetName = "Module", Position = 1, Mandatory = $true)]
        [string]
        $Name,

        [Parameter(ParameterSetName = "Module", Position = 0)]
        [string]
        $Module,

        [Parameter(ParameterSetName = "FullName", Position = 1)]
        [Parameter(ParameterSetName = "Module", Position = 2)]
        [AllowNull()]
        [AllowEmptyCollection()]
        [AllowEmptyString()]
        $Value,

        [string]
        $Description,

        [string]
        $Validation,

        [System.Management.Automation.ScriptBlock]
        $Handler,

        [switch]
        $Hidden,

        [switch]
        $Default,

        [switch]
        $Initialize,

        [switch]
        $DisableValidation,

        [switch]
        $DisableHandler,

        [switch]
        $EnableException
    )

    #region Prepare Names
    if ($PSCmdlet.ParameterSetName -eq "FullName") {
        if (-not $FullName.Trim(".").Contains(".")) {
            Stop-Function -Message "Invalid Name: $FullName ! At least one '.' is required, to separate module from name" -EnableException $EnableException -Category InvalidArgument
            return
        }

        $Module = $FullName.Split(".")[0].ToLower().Trim(".")
        $Name = $FullName.Substring(($Module.Length + 1)).ToLower().Trim(".")
        $internalFullName = $FullName.ToLower().Trim(".")
    }
    else {
        $Name = $Name.ToLower().Trim(".")
        if ($Module) { $Module = $Module.ToLower().Trim(".") }

        if ((Test-Bound -ParameterName "Module" -Not) -and ($Name -match ".+\..+")) {
            $r = $Name | select-string "^(.+?)\..+" -AllMatches
            $Module = $r.Matches[0].Groups[1].Value
            $Name = $Name.Substring($Module.Length + 1)
        }
        elseif ((Test-Bound -ParameterName "Module" -Not) -and ($Name -notmatch ".+\..+")) {
            Stop-Function -Message "Invalid Name: $Name ! At least one '.' is required when not explicitly specifying a module name, to separate module from name" -EnableException $EnableException -Category InvalidArgument
            return
        }

        If ($Module) { $internalFullName = $Module, $Name -join "." }
        else { $internalFullName = $Name }
    }
    #endregion Prepare Names

    #region Prepare runtime and kill execution as needed
    if ([Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations.ContainsKey($internalFullName)) {
        $itExists = $true
        $itIsInitialized = [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations[$internalFullName].Initialized
        $itIsEnforced = [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations[$internalFullName].PolicyEnforced
    }
    else {
        $itExists = $false
        $itIsInitialized = $false
        $itIsEnforced = $false
    }

    if ($itExists -and $Default) { return }
    if ($itIsInitialized -and $Initialize) { return }
    if ($itIsEnforced -and (-not $Initialize)) {
        Stop-Function -Message "Could not update configuration due to policy settings: $internalFullName" -EnableException $EnableException -Category PermissionDenied
        return
    }

    if (Test-Bound -ParameterName "Validation") {
        if (-not ([Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Validation.Keys -contains $Validation.ToLower())) {
            Stop-Function -Message "Invalid validation name: $Validation. Supported validations: $([Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Validation.Keys -join ", ")" -Category InvalidArgument -Target $Name
            return
        }
    }
    #endregion Prepare runtime and kill execution as needed

    #region Initializing a configuration
    if ($Initialize) {
        if ($itExists) {
            $oldValue = [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations[$internalFullName].Value
            $cfg = [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations[$internalFullName]
        }
        else { $cfg = New-Object Sqlcollaborative.Dbatools.Configuration.Config }
        $cfg.Name = $Name
        $cfg.Module = $Module
        $cfg.Description = $Description
        $cfg.Value = $Value
        $cfg.Handler = $Handler
        if ($Validation) { $cfg.Validation = [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Validation[$Validation.ToLower()] }
        $cfg.Hidden = $Hidden
        $cfg.Initialized = $true
        [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations[$internalFullName] = $cfg

        if ($itExists) { Set-DbaConfig -Name $internalFullName -Value $oldValue }
    }
    #endregion Initializing a configuration

    #region Regular configuration update
    else {
        if (-not $itExists) {
            $cfg = New-Object Sqlcollaborative.Dbatools.Configuration.Config
            $cfg.Name = $Name
            $cfg.Module = $Module
            $cfg.Description = $Description
            $cfg.Handler = $Handler
            if ($Validation) { $cfg.Validation = [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Validation[$Validation.ToLower()] }
            $cfg.Hidden = $Hidden
            [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations[$internalFullName] = $cfg

            Set-DbaConfig -Name $internalFullName -Value $Value
            return
        }

        else {
            [Sqlcollaborative.Dbatools.Configuration.Config]$cfg = [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations[$internalFullName]
            if ((-not $DisableValidation) -and ($cfg.Validation) -and (Test-Bound -ParameterName "Value")) {
                $testResult = [scriptblock]::Create($cfg.Validation.ToString()).Invoke($Value)
                if (-not $TestResult.Success) {
                    Stop-Function -Message "Could not update configuration $internalFullName | Failed validation: $($testResult.Message)" -EnableException $EnableException -Category InvalidResult -Target $internalFullName
                    return
                }
                $Value = $testResult.Value
            }

            if (Test-Bound -ParameterName "Hidden") { [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations[$internalFullName].Hidden = $Hidden }
            if (Test-Bound -ParameterName "Value") { [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations[$internalFullName].Value = $Value }
            if (Test-Bound -ParameterName "Description") { [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations[$internalFullName].Description = $Description }
            if (Test-Bound -ParameterName "Handler") { [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations[$internalFullName].Handler = $Handler }
            if (Test-Bound -ParameterName "Validation") { [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations[$internalFullName].Validation = [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Validation[$Validation.ToLower()] }

            if ((-not $DisableHandler) -and ($cfg.Handler) -and (Test-Bound -ParameterName "Value")) {
                try { [scriptblock]::Create($cfg.Handler.ToString()).Invoke($Value) }
                catch {
                    Stop-Function -Message "Could not update configuration $internalFullName | Failed handling $_" -EnableException $EnableException -Category InvalidResult -Target $internalFullName
                    return
                }
            }
        }
    }
    #endregion Regular configuration update
}
function Set-DbaDatabaseOwner {
    <#
        .SYNOPSIS
            Sets database owners with a desired login if databases do not match that owner.

        .DESCRIPTION
            This function will alter database ownership to match a specified login if their current owner does not match the target login. By default, the target login will be 'sa', but the function will allow the user to specify a different login for  ownership. The user can also apply this to all databases or only to a select list of databases (passed as either a comma separated list or a string array).

            Best Practice reference: http://weblogs.sqlteam.com/dang/archive/2008/01/13/Database-Owner-Troubles.aspx

        .PARAMETER SqlInstance
            Specifies the SQL Server instance(s) to scan.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies the database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies the database(s) to exclude from processing. Options for this list are auto-populated from the server.

        .PARAMETER TargetLogin
            Specifies the login that you wish check for ownership. This defaults to 'sa' or the sysadmin name if sa was renamed. This must be a valid security principal which exists on the target server.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database, Owner, DbOwner
            Author: Michael Fal (@Mike_Fal), http://mikefal.net

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Set-DbaDatabaseOwner

        .EXAMPLE
            Set-DbaDatabaseOwner -SqlInstance localhost

            Sets database owner to 'sa' on all databases where the owner does not match 'sa'.

        .EXAMPLE
            Set-DbaDatabaseOwner -SqlInstance localhost -TargetLogin DOMAIN\account

            Sets the database owner to DOMAIN\account on all databases where the owner does not match DOMAIN\account.

        .EXAMPLE
            Set-DbaDatabaseOwner -SqlInstance sqlserver -Database db1, db2

            Sets database owner to 'sa' on the db1 and db2 databases if their current owner does not match 'sa'.
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Alias("Login")]
        [string]$TargetLogin,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance."
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure." -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            # dynamic sa name for orgs who have changed their sa name
            if (!$TargetLogin) {
                $TargetLogin = ($server.logins | Where-Object { $_.id -eq 1 }).Name
            }

            #Validate login
            if (($server.Logins.Name) -notcontains $TargetLogin) {
                Stop-Function -Message "$TargetLogin is not a valid login on $instance. Moving on." -Continue -EnableException $EnableException
            }

            #Owner cannot be a group
            $TargetLoginObject = $server.Logins | where-object {$PSItem.Name -eq $TargetLogin }| Select-Object -property  Name, LoginType
            if ($TargetLoginObject.LoginType -eq 'WindowsGroup') {
                Stop-Function -Message "$TargetLogin is a group, therefore can't be set as owner. Moving on." -Continue -EnableException $EnableException
            }

            #Get database list. If value for -Database is passed, massage to make it a string array.
            #Otherwise, use all databases on the instance where owner not equal to -TargetLogin
            #use where owner and target login do not match
            #exclude system dbs
            $dbs = $server.Databases | Where-Object { $_.IsAccessible -and $_.Owner -ne $TargetLogin -and @('master', 'model', 'msdb', 'tempdb', 'distribution') -notcontains $_.Name}

            #filter collection based on -Databases/-Exclude parameters
            if ($Database) {
                $dbs = $dbs | Where-Object { $Database -contains $_.Name }
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object { $ExcludeDatabase -notcontains $_.Name }
            }

            Write-Message -Level Verbose -Message "Updating $($dbs.Count) database(s)."
            foreach ($db in $dbs) {
                $dbname = $db.name
                if ($PSCmdlet.ShouldProcess($instance, "Setting database owner for $dbname to $TargetLogin")) {
                    try {
                        Write-Message -Level Verbose -Message "Setting database owner for $dbname to $TargetLogin on $instance."
                        # Set database owner to $TargetLogin (default 'sa')
                        # Ownership validations checks

                        #Database is online and accessible
                        if ($db.Status -notmatch 'Normal') {
                            Write-Message -Level Warning -Message "$dbname on $instance is in a  $($db.Status) state and can not be altered. It will be skipped."
                        }
                        #Database is updatable, not read-only
                        elseif ($db.IsUpdateable -eq $false) {
                            Write-Message -Level Warning -Message "$dbname on $instance is not in an updateable state and can not be altered. It will be skipped."
                        }
                        #Is the login mapped as a user? Logins already mapped in the database can not be the owner
                        elseif ($db.Users.name -contains $TargetLogin) {
                            Write-Message -Level Warning -Message "$dbname on $instance has $TargetLogin as a mapped user. Mapped users can not be database owners."
                        }
                        else {
                            $db.SetOwner($TargetLogin)
                            [PSCustomObject]@{
                                ComputerName = $server.ComputerName
                                InstanceName = $server.ServiceName
                                SqlInstance  = $server.DomainInstanceName
                                Database     = $db
                                Owner        = $TargetLogin
                            }
                        }
                    }
                    catch {
                        Stop-Function -Message "Failure updating owner." -ErrorRecord $_ -Target $instance -Continue
                    }
                }
            }
        }
    }
}

#ValidationTags#Messaging,FlowControl,Pipeline#
function Set-DbaDatabaseState {
    <#
        .SYNOPSIS
            Sets various options for databases, hereby called "states"

        .DESCRIPTION
            Sets some common "states" on databases:
            - "RW" options (ReadOnly, ReadWrite)
            - "Status" options (Online, Offline, Emergency, plus a special "Detached")
            - "Access" options (SingleUser, RestrictedUser, MultiUser)

            Returns an object with SqlInstance, Database, RW, Status, Access, Notes

            Notes gets filled when something went wrong setting the state

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to

        .PARAMETER SqlCredential
            Credential object used to connect to the SQL Server as a different user

        .PARAMETER Database
            The database(s) to process - this list is auto-populated from the server. if unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto-populated from the server

        .PARAMETER AllDatabases
            This is a parameter that was included for safety, so you don't accidentally set options on all databases without specifying

        .PARAMETER ReadOnly
            RW Option : Sets the database as READ_ONLY

        .PARAMETER ReadWrite
            RW Option : Sets the database as READ_WRITE

        .PARAMETER Online
            Status Option : Sets the database as ONLINE

        .PARAMETER Offline
            Status Option : Sets the database as OFFLINE

        .PARAMETER Emergency
            Status Option : Sets the database as EMERGENCY

        .PARAMETER Detached
            Status Option : Detaches the database

        .PARAMETER SingleUser
            Access Option : Sets the database as SINGLE_USER

        .PARAMETER RestrictedUser
            Access Option : Sets the database as RESTRICTED_USER

        .PARAMETER MultiUser
            Access Option : Sets the database as MULTI_USER

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER Force
            For most options, this translates to instantly rolling back any open transactions
            that may be stopping the process.
            For -Detached it is required to break mirroring and Availability Groups

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER InputObject
            Accepts piped database objects

        .NOTES
            Tags: Database, State
            Author: niphlod
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Set-DbaDatabaseState

        .EXAMPLE
            Set-DbaDatabaseState -SqlInstance sqlserver2014a -Database HR -Offline

            Sets the HR database as OFFLINE

        .EXAMPLE
            Set-DbaDatabaseState -SqlInstance sqlserver2014a -AllDatabases -Exclude HR -Readonly -Force

            Sets all databases of the sqlserver2014a instance, except for HR, as READ_ONLY

        .EXAMPLE
            Get-DbaDatabaseState -SqlInstance sql2016 | Where-Object Status -eq 'Offline' | Set-DbaDatabaseState -Online

            Finds all offline databases and sets them to online

        .EXAMPLE
            Set-DbaDatabaseState -SqlInstance sqlserver2014a -Database HR -SingleUser

            Sets the HR database as SINGLE_USER

        .EXAMPLE
            Set-DbaDatabaseState -SqlInstance sqlserver2014a -Database HR -SingleUser -Force

            Sets the HR database as SINGLE_USER, dropping all other connections (and rolling back open transactions)

        .EXAMPLE
            Get-DbaDatabase -SqlInstance sqlserver2014a -Database HR | Set-DbaDatabaseState -SingleUser -Force

            Gets the databases from Get-DbaDatabase, and sets them as SINGLE_USER, dropping all other connections (and rolling back open transactions)
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    Param (
        [parameter(Mandatory = $true, ValueFromPipelineByPropertyName, ParameterSetName = "Server")]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$AllDatabases,
        [switch]$ReadOnly,
        [switch]$ReadWrite,
        [switch]$Online,
        [switch]$Offline,
        [switch]$Emergency,
        [switch]$Detached,
        [switch]$SingleUser,
        [switch]$RestrictedUser,
        [switch]$MultiUser,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException,
        [parameter(Mandatory = $true, ValueFromPipeline, ParameterSetName = "Database")]
        [PsCustomObject[]]$InputObject
    )

    begin {
        function Get-WrongCombo($optset, $allparams) {
            $x = 0
            foreach ($opt in $optset) {
                if ($allparams.ContainsKey($opt)) { $x += 1 }
            }
            if ($x -gt 1) {
                $msg = $optset -Join ',-'
                $msg = "You can only specify one of: -" + $msg
                throw $msg
            }
        }

        function Edit-DatabaseState($sqlinstance, $dbname, $opt, $immediate = $false) {
            $warn = $null
            $sql = "ALTER DATABASE [$dbname] SET $opt"
            if ($immediate) {
                $sql += " WITH ROLLBACK IMMEDIATE"
            }
            else {
                $sql += " WITH NO_WAIT"
            }
            try {
                Write-Message -Level System -Message $sql
                if ($immediate) {
                    # this can be helpful only for SINGLE_USER databases
                    # but since $immediate is called, it does no more harm
                    # than the immediate rollback
                    $sqlinstance.KillAllProcesses($dbname)
                }
                $null = $sqlinstance.Query($sql)
            }
            catch {
                $warn = "Failed to set '$dbname' to $opt"
                Write-Message -Level Warning -Message $warn
            }
            return $warn
        }

        $StatusHash = @{
            'Offline'       = 'OFFLINE'
            'Normal'        = 'ONLINE'
            'EmergencyMode' = 'EMERGENCY'
        }

        function Get-DbState($databaseName, $dbStatuses) {
            $base = $dbStatuses | Where-Object DatabaseName -ceq $databaseName
            foreach ($status in $StatusHash.Keys) {
                if ($base.Status -match $status) {
                    $base.Status = $StatusHash[$status]
                    break
                }
            }
            return $base
        }

        $RWExclusive = @('ReadOnly', 'ReadWrite')
        $StatusExclusive = @('Online', 'Offline', 'Emergency', 'Detached')
        $AccessExclusive = @('SingleUser', 'RestrictedUser', 'MultiUser')
        $allparams = $PSBoundParameters
        try {
            Get-WrongCombo -optset $RWExclusive -allparams $allparams
        }
        catch {
            Stop-Function -Message $_
            return
        }
        try {
            Get-WrongCombo -optset $StatusExclusive -allparams $allparams
        }
        catch {
            Stop-Function -Message $_
            return
        }
        try {
            Get-WrongCombo -optset $AccessExclusive -allparams $allparams
        }
        catch {
            Stop-Function -Message $_
            return
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }
        $dbs = @()
        if (!$Database -and !$AllDatabases -and !$InputObject -and !$ExcludeDatabase) {
            Stop-Function -Message "You must specify a -AllDatabases or -Database to continue"
            return
        }

        if ($InputObject) {
            if ($InputObject.Database) {
                # comes from Get-DbaDatabaseState
                $dbs += $InputObject.Database
            }
            elseif ($InputObject.Name) {
                # comes from Get-DbaDatabase
                $dbs += $InputObject
            }
        }
        else {
            foreach ($instance in $SqlInstance) {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                try {
                    $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
                }
                catch {
                    Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                }
                $all_dbs = $server.Databases
                $dbs += $all_dbs | Where-Object { @('master', 'model', 'msdb', 'tempdb', 'distribution') -notcontains $_.Name }

                if ($database) {
                    $dbs = $dbs | Where-Object { $database -contains $_.Name }
                }
                if ($ExcludeDatabase) {
                    $dbs = $dbs | Where-Object { $ExcludeDatabase -notcontains $_.Name }
                }
            }
        }

        # need to pick up here
        foreach ($db in $dbs) {
            if ($db.Name -in @('master', 'model', 'msdb', 'tempdb', 'distribution')) {
                Write-Message -Level Warning -Message "Database $db is a system one, skipping"
                Continue
            }
            $dbStatuses = @{}
            $server = $db.Parent
            if ($server -notin $dbStatuses.Keys) {
                $dbStatuses[$server] = Get-DbaDatabaseState -SqlInstance $server
            }

            # normalizing properties returned by SMO to something more "fixed"
            $db_status = Get-DbState -DatabaseName $db.Name -dbStatuses $dbStatuses[$server]


            $warn = @()

            if ($db.DatabaseSnapshotBaseName.Length -gt 0) {
                Write-Message -Level Warning -Message "Database $db is a snapshot, skipping"
                Continue
            }

            if ($ReadOnly -eq $true) {
                if ($db_status.RW -eq 'READ_ONLY') {
                    Write-Message -Level VeryVerbose -Message "Database $db is already READ_ONLY"
                }
                else {
                    if ($Pscmdlet.ShouldProcess($server, "Set $db to READ_ONLY")) {
                        Write-Message -Level VeryVerbose -Message "Setting database $db to READ_ONLY"
                        $partial = Edit-DatabaseState -sqlinstance $server -dbname $db.Name -opt "READ_ONLY" -immediate $Force
                        $warn += $partial
                        if (!$partial) {
                            $db_status.RW = 'READ_ONLY'
                        }
                    }
                }
            }

            if ($ReadWrite -eq $true) {
                if ($db_status.RW -eq 'READ_WRITE') {
                    Write-Message -Level VeryVerbose -Message "Database $db is already READ_WRITE"
                }
                else {
                    if ($Pscmdlet.ShouldProcess($server, "Set $db to READ_WRITE")) {
                        Write-Message -Level VeryVerbose -Message "Setting database $db to READ_WRITE"
                        $partial = Edit-DatabaseState -sqlinstance $server -dbname $db.Name -opt "READ_WRITE" -immediate $Force
                        $warn += $partial
                        if (!$partial) {
                            $db_status.RW = 'READ_WRITE'
                        }
                    }
                }
            }

            if ($Online -eq $true) {
                if ($db_status.Status -eq 'ONLINE') {
                    Write-Message -Level VeryVerbose -Message "Database $db is already ONLINE"
                }
                else {
                    if ($Pscmdlet.ShouldProcess($server, "Set $db to ONLINE")) {
                        Write-Message -Level VeryVerbose -Message "Setting database $db to ONLINE"
                        $partial = Edit-DatabaseState -sqlinstance $server -dbname $db.Name -opt "ONLINE" -immediate $Force
                        $warn += $partial
                        if (!$partial) {
                            $db_status.Status = 'ONLINE'
                        }
                    }
                }
            }

            if ($Offline -eq $true) {
                if ($db_status.Status -eq 'OFFLINE') {
                    Write-Message -Level VeryVerbose -Message "Database $db is already OFFLINE"
                }
                else {
                    if ($Pscmdlet.ShouldProcess($server, "Set $db to OFFLINE")) {
                        Write-Message -Level VeryVerbose -Message "Setting database $db to OFFLINE"
                        $partial = Edit-DatabaseState -sqlinstance $server -dbname $db.Name -opt "OFFLINE" -immediate $Force
                        $warn += $partial
                        if (!$partial) {
                            $db_status.Status = 'OFFLINE'
                        }
                    }
                }
            }

            if ($Emergency -eq $true) {
                if ($db_status.Status -eq 'EMERGENCY') {
                    Write-Message -Level VeryVerbose -Message "Database $db is already EMERGENCY"
                }
                else {
                    if ($Pscmdlet.ShouldProcess($server, "Set $db to EMERGENCY")) {
                        Write-Message -Level VeryVerbose -Message "Setting database $db to EMERGENCY"
                        $partial = Edit-DatabaseState -sqlinstance $server -dbname $db.Name -opt "EMERGENCY" -immediate $Force
                        if (!$partial) {
                            $db_status.Status = 'EMERGENCY'
                        }
                    }
                }
            }

            if ($SingleUser -eq $true) {
                if ($db_status.Access -eq 'SINGLE_USER') {
                    Write-Message -Level VeryVerbose -Message "Database $db is already SINGLE_USER"
                }
                else {
                    if ($Pscmdlet.ShouldProcess($server, "Set $db to SINGLE_USER")) {
                        Write-Message -Level VeryVerbose -Message "Setting $db to SINGLE_USER"
                        $partial = Edit-DatabaseState -sqlinstance $server -dbname $db.Name -opt "SINGLE_USER" -immediate $Force
                        if (!$partial) {
                            $db_status.Access = 'SINGLE_USER'
                        }
                    }
                }
            }

            if ($RestrictedUser -eq $true) {
                if ($db_status.Access -eq 'RESTRICTED_USER') {
                    Write-Message -Level VeryVerbose -Message "Database $db is already RESTRICTED_USER"
                }
                else {
                    if ($Pscmdlet.ShouldProcess($server, "Set $db to RESTRICTED_USER")) {
                        Write-Message -Level VeryVerbose -Message "Setting $db to RESTRICTED_USER"
                        $partial = Edit-DatabaseState -sqlinstance $server -dbname $db.Name -opt "RESTRICTED_USER" -immediate $Force
                        if (!$partial) {
                            $db_status.Access = 'RESTRICTED_USER'
                        }
                    }
                }
            }

            if ($MultiUser -eq $true) {
                if ($db_status.Access -eq 'MULTI_USER') {
                    Write-Message -Level VeryVerbose -Message "Database $db is already MULTI_USER"
                }
                else {
                    if ($Pscmdlet.ShouldProcess($server, "Set $db to MULTI_USER")) {
                        Write-Message -Level VeryVerbose -Message "Setting $db to MULTI_USER"
                        $partial = Edit-DatabaseState -sqlinstance $server -dbname $db.Name -opt "MULTI_USER" -immediate $Force
                        if (!$partial) {
                            $db_status.Access = 'MULTI_USER'
                        }
                    }
                }
            }

            if ($Detached -eq $true) {
                # Refresh info about database state here (before detaching)
                $db.Refresh()
                # we need to see what snaps are on the server, as base databases cannot be dropped
                $snaps = $server.Databases | Where-Object { $_.DatabaseSnapshotBaseName.Length -gt 0 }
                $snaps = $snaps.DatabaseSnapshotBaseName | Get-Unique
                if ($db.Name -in $snaps) {
                    Write-Message -Level Warning -Message "Database $db has snapshots, you need to drop them before detaching, skipping..."
                    Continue
                }
                if ($db.IsMirroringEnabled -eq $true -or $db.AvailabilityGroupName.Length -gt 0) {
                    if ($Force -eq $false) {
                        Write-Message -Level Warning -Message "Needs -Force to detach $db, skipping"
                        Continue
                    }
                }

                if ($db.IsMirroringEnabled) {
                    if ($Pscmdlet.ShouldProcess($server, "Break mirroring for $db")) {
                        try {
                            $db.ChangeMirroringState([Microsoft.SqlServer.Management.Smo.MirroringOption]::Off)
                            $db.Alter()
                            $db.Refresh()
                            Write-Message -Level VeryVerbose -Message "Broke mirroring for $db"
                        }
                        catch {
                            Stop-Function -Message "Could not break mirror for $db. Skipping." -ErrorRecord $_ -Target $server -Continue
                        }
                    }
                }

                if ($db.AvailabilityGroupName) {
                    $agname = $db.AvailabilityGroupName
                    if ($Pscmdlet.ShouldProcess($server, "Removing $db from AG [$agname]")) {
                        try {
                            $server.AvailabilityGroups[$db.AvailabilityGroupName].AvailabilityDatabases[$db.Name].Drop()
                            Write-Message -Level VeryVerbose -Message "Successfully removed $db from AG [$agname] on $server"
                        }
                        catch {
                            Stop-Function -Message "Could not remove $db from AG [$agname] on $server" -ErrorRecord $_ -Target $server -Continue
                        }
                    }
                }

                # DBA 101 should encourage detaching just OFFLINE databases
                # we can do that here
                if ($Pscmdlet.ShouldProcess($server, "Detaching $db")) {
                    if ($db_status.Status -ne 'OFFLINE') {
                        $null = Edit-DatabaseState -sqlinstance $server -dbname $db.Name -opt "OFFLINE" -immediate $true
                    }
                    try {
                        $sql = "EXEC master.dbo.sp_detach_db N'$($db.Name)'"
                        Write-Message -Level System -Message $sql
                        $null = $server.Query($sql)
                        $db_status.Status = 'DETACHED'
                    }
                    catch {
                        Stop-Function -Message "Failed to detach $db" -ErrorRecord $_ -Target $server -Continue
                        $warn += "Failed to detach"
                    }

                }

            }
            if ($warn) {
                $warn = $warn | Get-Unique
                $warn = $warn -Join ';'
            }
            else {
                $warn = $null
            }
            if ($Detached -eq $true) {
                [PSCustomObject]@{
                    ComputerName = $server.ComputerName
                    InstanceName = $server.ServiceName
                    SqlInstance  = $server.DomainInstanceName
                    DatabaseName = $db.Name
                    RW           = $db_status.RW
                    Status       = $db_status.Status
                    Access       = $db_status.Access
                    Notes        = $warn
                    Database     = $db
                } | Select-DefaultView -ExcludeProperty Database
            }
            else {
                $db.Refresh()
                if ($null -eq $warn) {
                    # we avoid reenumerating properties
                    $newstate = $db_status
                }
                else {
                    $newstate = Get-DbState -databaseName $db.Name -dbStatuses $stateCache[$server]
                }

                [PSCustomObject]@{
                    ComputerName = $server.ComputerName
                    InstanceName = $server.ServiceName
                    SqlInstance  = $server.DomainInstanceName
                    DatabaseName = $db.Name
                    RW           = $newstate.RW
                    Status       = $newstate.Status
                    Access       = $newstate.Access
                    Notes        = $warn
                    Database     = $db
                } | Select-DefaultView -ExcludeProperty Database
            }
        }

    }

    end {

    }
}
function Set-DbaDbCompression {
    <#
        .SYNOPSIS
            Sets tables and indexes with preferred compression setting.

        .DESCRIPTION
            This function sets the appropriate compression recommendation, determined either by using the Tiger Team's query or set to the CompressionType parameter.

            Remember Uptime is critical for the Tiger Team query, the longer uptime, the more accurate the analysis is.
            You would probably be best if you utilized Get-DbaUptime first, before running this command.

            Set-DbaDbCompression script derived from GitHub and the tigertoolbox
            (https://github.com/Microsoft/tigertoolbox/tree/master/Evaluate-Compression-Gains)

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process - this list is auto populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto populated from the server.

        .PARAMETER CompressionType
            Control the compression type applied. Default is 'Recommended' which uses the Tiger Team query to use the most appropriate setting per object. Other option is to compress all objects to either Row or Page.

        .PARAMETER MaxRunTime
            Will continue to alter tables and indexes for the given amount of minutes.

        .PARAMETER PercentCompression
            Will only work on the tables/indexes that have the calculated savings at and higher for the given number provided.

        .PARAMETER InputObject
            Takes the output of Test-DbaDbCompression as an object and applied compression based on those recommendations.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Jason Squires (@js_0505, [email protected])
            Tags: Compression, Table, Database
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Set-DbaDbCompression

        .EXAMPLE
            Set-DbaDbCompression -SqlInstance localhost -MaxRunTime 60 -PercentCompression 25

            Set the compression run time to 60 minutes and will start the compression of tables/indexes that have a difference of 25% or higher between current and recommended.

        .EXAMPLE
            Set-DbaDbCompression -SqlInstance ServerA -Database DBName -CompressionType Page

            Utilizes Page compression for all objects in DBName on ServerA with no time limit.

        .EXAMPLE
            Set-DbaDbCompression -SqlInstance ServerA -Database DBName -PercentCompression 25 | Out-GridView

            Will compress tables/indexes within the specified database that would show any % improvement with compression and with no time limit. The results will be piped into a nicely formated GridView.

        .EXAMPLE
            $testCompression = Test-DbaDbCompression -SqlInstance ServerA -Database DBName
            Set-DbaDbCompression -SqlInstance ServerA -Database DBName -InputObject $testCompression

            Gets the compression suggestions from Test-DbaDbCompression into a variable, this can then be reviewed and passed into Set-DbaDbCompression.

        .EXAMPLE
            $cred = Get-Credential sqladmin
            Set-DbaDbCompression -SqlInstance ServerA -ExcludeDatabase Database -SqlCredential $cred -MaxRunTime 60 -PercentCompression 25

            Set the compression run time to 60 minutes and will start the compression of tables/indexes for all databases except the specified excluded database. Only objects that have a difference of 25% or higher between current and recommended will be compressed.

        .EXAMPLE
            $servers = 'Server1','Server2'
            foreach ($svr in $servers)
            {
                Set-DbaDbCompression -SqlInstance $svr -MaxRunTime 60 -PercentCompression 25 | Export-Csv -Path C:\temp\CompressionAnalysisPAC.csv -Append
            }

            Set the compression run time to 60 minutes and will start the compression of tables/indexes across all listed servers that have a difference of 25% or higher between current and recommended. Output of command is exported to a csv.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess)]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [ValidateSet("Recommended", "Page", "Row", "None")]$CompressionType = "Recommended",
        [int]$MaxRunTime = 0,
        [int]$PercentCompression = 0,
        $InputObject,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        $starttime = Get-Date
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level VeryVerbose -Message "Connecting to $instance" -Target $instance
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failed to process Instance $instance" -ErrorRecord $_ -Target $instance -Continue
            }

            $Server.ConnectionContext.StatementTimeout = 0

            #The reason why we do this is because of SQL 2016 and they now allow for compression on standard edition.
            if ($server.EngineEdition -notmatch 'Enterprise' -and $server.VersionMajor -lt '13') {
                Stop-Function -Message "Only SQL Server Enterprise Edition supports compression on $server" -Target $server -Continue
            }
            try {
                $dbs = $server.Databases | Where-Object { $_.IsAccessible -and $_.IsSystemObject -eq 0}
                if ($Database) {
                    $dbs = $dbs | Where-Object { $_.Name -in $Database }
                }
                if ($ExcludeDatabase) {
                    $dbs = $dbs | Where-Object { $_.Name -NotIn $ExcludeDatabase }
                }
            }
            catch {
                Stop-Function -Message "Unable to gather list of databases for $instance" -Target $instance -ErrorRecord $_ -Continue
            }

            foreach ($db in $dbs) {
                try {
                    Write-Message -Level Verbose -Message "Querying $instance - $db"
                    if ($db.status -ne 'Normal' -or $db.IsAccessible -eq $false) {
                        Write-Message -Level Warning -Message "$db is not accessible" -Target $db
                        continue
                    }
                    if ($db.CompatibilityLevel -lt 'Version100') {
                        Stop-Function -Message "$db has a compatibility level lower than Version100 and will be skipped." -Target $db -Continue
                    }
                    if ($CompressionType -eq "Recommended") {
                        if (Test-Bound "InputObject") {
                            Write-Message -Level Verbose -Message "Using passed in compression suggestions"
                            $compressionSuggestion = $InputObject | Where-Object {$_.Database -eq $db.name}
                        }
                        else {
                            Write-Message -Level Verbose -Message "Testing database for compression suggestions for $instance.$db"
                            $compressionSuggestion = Test-DbaDbCompression -SqlInstance $server -Database $db.Name
                        }
                    }
                }
                catch {
                    Stop-Function -Message "Unable to query $instance - $db" -Target $db -ErrorRecord $_ -Continue
                }

                try {
                    if ($CompressionType -eq "Recommended") {
                        if ($Pscmdlet.ShouldProcess($db, "Applying suggested compression using results from Test-DbaDbCompression")) {
                            Write-Message -Level Verbose -Message "Applying suggested compression settings using Test-DbaDbCompression"
                            $results += $compressionSuggestion | Select-Object *, @{l = 'AlreadyProcesssed'; e = {"False"}}
                            foreach ($obj in ($results | Where-Object {$_.CompressionTypeRecommendation -ne 'NO_GAIN' -and $_.PercentCompression -ge $PercentCompression} | Sort-Object PercentCompression -Descending)) {
                                if ($MaxRunTime -ne 0 -and ($(get-date) - $starttime).TotalMinutes -ge $MaxRunTime) {
                                    Write-Message -Level Verbose -Message "Reached max run time of $MaxRunTime"
                                    break
                                }
                                if ($obj.indexId -le 1) {
                                    ##heaps and clustered indexes
                                    Write-Message -Level Verbose -Message "Applying $($obj.CompressionTypeRecommendation) compression to $($obj.Database).$($obj.Schema).$($obj.TableName)"
                                    $($server.Databases[$obj.Database].Tables[$obj.TableName, $obj.Schema].PhysicalPartitions | Where-Object {$_.PartitionNumber -eq $obj.Partition}).DataCompression = $($obj.CompressionTypeRecommendation)
                                    $server.Databases[$obj.Database].Tables[$obj.TableName, $($obj.Schema)].Rebuild()
                                    $obj.AlreadyProcesssed = "True"
                                }
                                else {
                                    ##nonclustered indexes
                                    Write-Message -Level Verbose -Message "Applying $($obj.CompressionTypeRecommendation) compression to $($obj.Database).$($obj.Schema).$($obj.TableName).$($obj.IndexName)"
                                    $($server.Databases[$obj.Database].Tables[$obj.TableName, $obj.Schema].Indexes[$obj.IndexName].PhysicalPartitions | Where-Object {$_.PartitionNumber -eq $obj.Partition}).DataCompression = $($obj.CompressionTypeRecommendation)
                                    $server.Databases[$obj.Database].Tables[$obj.TableName, $obj.Schema].Indexes[$obj.IndexName].Rebuild()
                                    $obj.AlreadyProcesssed = "True"
                                }
                                $obj
                            }
                        }
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($db, "Applying $CompressionType compression")) {
                            Write-Message -Level Verbose -Message "Applying $CompressionType compression to all objects in $($db.name)"
                            foreach ($obj in $server.Databases[$($db.name)].Tables | Where-Object {!$_.IsMemoryOptimized}) {
                                if ($MaxRunTime -ne 0 -and ($(get-date) - $starttime).TotalMinutes -ge $MaxRunTime) {
                                    Write-Message -Level Verbose -Message "Reached max run time of $MaxRunTime"
                                    break
                                }
                                foreach ($p in $($obj.PhysicalPartitions | Where-Object {$_.DataCompression -ne $CompressionType})) {
                                    Write-Message -Level Verbose -Message "Compressing table $($obj.Schema).$($obj.Name)"
                                    $($obj.PhysicalPartitions | Where-Object {$_.PartitionNumber -eq $P.PartitionNumber}).DataCompression = $CompressionType
                                    $obj.Rebuild()
                                    [pscustomobject]@{
                                        ComputerName                  = $server.ComputerName
                                        InstanceName                  = $server.ServiceName
                                        SqlInstance                   = $server.DomainInstanceName
                                        Database                      = $db.Name
                                        Schema                        = $obj.Schema
                                        TableName                     = $obj.Name
                                        IndexName                     = $null
                                        Partition                     = $p.PartitionNumber
                                        IndexID                       = 0
                                        IndexType                     = Switch ($obj.HasHeapIndex) {$false {"ClusteredIndex"} $true {"Heap"}}
                                        PercentScan                   = $null
                                        PercentUpdate                 = $null
                                        RowEstimatePercentOriginal    = $null
                                        PageEstimatePercentOriginal   = $null
                                        CompressionTypeRecommendation = $CompressionType.ToUpper()
                                        SizeCurrent                   = $null
                                        SizeRequested                 = $null
                                        PercentCompression            = $null
                                        AlreadyProcesssed             = "True"
                                    }
                                }

                                foreach ($index in $($obj.Indexes | Where-Object {!$_.IsMemoryOptimized})) {
                                    if ($MaxRunTime -ne 0 -and ($(get-date) - $starttime).TotalMinutes -ge $MaxRunTime) {
                                        Write-Message -Level Verbose -Message "Reached max run time of $MaxRunTime"
                                        break
                                    }
                                    foreach ($p in $($index.PhysicalPartitions | Where-Object {$_.DataCompression -ne $CompressionType})) {
                                        Write-Message -Level Verbose -Message "Compressing $($Index.IndexType) $($Index.Name) Partition $($p.PartitionNumber)"

                                        ## There is a bug in SMO where setting compression to None at the index level doesn't work
                                        ## Once this UserVoice item is fixed the workaround can be removed
                                        ## https://feedback.azure.com/forums/908035-sql-server/suggestions/34080112-data-compression-smo-bug
                                        if ($CompressionType -eq "None") {
                                            $query = "ALTER INDEX [$($index.Name)] ON $($index.Parent) REBUILD PARTITION = ALL WITH (DATA_COMPRESSION = $CompressionType)"
                                            $Server.Query($query, $db.Name)
                                        }
                                        else {
                                            $($Index.PhysicalPartitions | Where-Object {$_.PartitionNumber -eq $P.PartitionNumber}).DataCompression = $CompressionType
                                            $index.Rebuild()
                                        }

                                        [pscustomobject]@{
                                            ComputerName                  = $server.ComputerName
                                            InstanceName                  = $server.ServiceName
                                            SqlInstance                   = $server.DomainInstanceName
                                            Database                      = $db.Name
                                            Schema                        = $obj.Schema
                                            TableName                     = $obj.Name
                                            IndexName                     = $index.Name
                                            Partition                     = $p.PartitionNumber
                                            IndexID                       = $index.Id
                                            IndexType                     = $index.IndexType
                                            PercentScan                   = $null
                                            PercentUpdate                 = $null
                                            RowEstimatePercentOriginal    = $null
                                            PageEstimatePercentOriginal   = $null
                                            CompressionTypeRecommendation = $CompressionType.ToUpper()
                                            SizeCurrent                   = $null
                                            SizeRequested                 = $null
                                            PercentCompression            = $null
                                            AlreadyProcesssed             = "True"
                                        }
                                    }
                                }
                            }
                            foreach ($index in $($server.Databases[$($db.name)].Views | Where-Object {$_.Indexes}).Indexes) {
                                foreach ($p in $($index.PhysicalPartitions | Where-Object {$_.DataCompression -ne $CompressionType})) {
                                    Write-Message -Level Verbose -Message "Compressing $($index.IndexType) $($index.Name) Partition $($p.PartitionNumber)"

                                    ## There is a bug in SMO where setting compression to None at the index level doesn't work
                                    ## Once this UserVoice item is fixed the workaround can be removed
                                    ## https://feedback.azure.com/forums/908035-sql-server/suggestions/34080112-data-compression-smo-bug
                                    if ($CompressionType -eq "None") {
                                        $query = "ALTER INDEX [$($index.Name)] ON $($index.Parent) REBUILD PARTITION = ALL WITH (DATA_COMPRESSION = $CompressionType)"
                                        $query
                                        $Server.Query($query, $db.Name)
                                    }
                                    else {
                                        $($index.PhysicalPartitions | Where-Object {$_.PartitionNumber -eq $P.PartitionNumber}).DataCompression = $CompressionType
                                        $index.Rebuild()
                                    }

                                    [pscustomobject]@{
                                        ComputerName                  = $server.ComputerName
                                        InstanceName                  = $server.ServiceName
                                        SqlInstance                   = $server.DomainInstanceName
                                        Database                      = $db.Name
                                        Schema                        = $obj.Schema
                                        TableName                     = $obj.Name
                                        IndexName                     = $index.Name
                                        Partition                     = $p.PartitionNumber
                                        IndexID                       = $index.Id
                                        IndexType                     = $index.IndexType
                                        PercentScan                   = $null
                                        PercentUpdate                 = $null
                                        RowEstimatePercentOriginal    = $null
                                        PageEstimatePercentOriginal   = $null
                                        CompressionTypeRecommendation = $CompressionType.ToUpper()
                                        SizeCurrent                   = $null
                                        SizeRequested                 = $null
                                        PercentCompression            = $null
                                        AlreadyProcesssed             = "True"
                                    }
                                }
                            }
                        }
                    }
                }
                catch {
                    Stop-Function -Message "Compression failed for $instance - $db" -Target $db -ErrorRecord $_ -Continue
                }
            }
        }
    }
}
#ValidationTags#CodeStyle,Messaging,FlowControl,Pipeline#
function Set-DbaDbQueryStoreOptions {
    <#
        .SYNOPSIS
            Configure Query Store settings for a specific or multiple databases.

        .DESCRIPTION
            Configure Query Store settings for a specific or multiple databases.

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to.

        .PARAMETER SqlCredential
            SqlCredential object used to connect to the SQL Server as a different user.

        .PARAMETER Database
            The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto-populated from the server

        .PARAMETER AllDatabases
            Run command against all user databases

        .PARAMETER State
            Set the state of the Query Store. Valid options are "ReadWrite", "ReadOnly" and "Off".

        .PARAMETER FlushInterval
            Set the flush to disk interval of the Query Store in seconds.

        .PARAMETER CollectionInterval
            Set the runtime statistics collection interval of the Query Store in minutes.

        .PARAMETER MaxSize
            Set the maximum size of the Query Store in MB.

        .PARAMETER CaptureMode
            Set the query capture mode of the Query Store. Valid options are "Auto" and "All".

        .PARAMETER CleanupMode
            Set the query cleanup mode policy. Valid options are "Auto" and "Off".

        .PARAMETER StaleQueryThreshold
            Set the stale query threshold in days.

        .PARAMETER WhatIf
            Shows what would happen if the command were to run

        .PARAMETER Confirm
            Prompts for confirmation of every step. For example:

            Are you sure you want to perform this action?
            Performing the operation "Changing Desired State" on target "pubs on SQL2016\VNEXT".
            [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: QueryStore
            Author: Enrico van de Laar ( @evdlaar )

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Set-DbaQueryStoreOptions

        .EXAMPLE
            Set-DbaDbQueryStoreOptions -SqlInstance ServerA\SQL -State ReadWrite -FlushInterval 600 -CollectionInterval 10 -MaxSize 100 -CaptureMode All -CleanupMode Auto -StaleQueryThreshold 100 -AllDatabases

            Configure the Query Store settings for all user databases in the ServerA\SQL Instance.

        .EXAMPLE
            Set-DbaDbQueryStoreOptions -SqlInstance ServerA\SQL -FlushInterval 600

            Only configure the FlushInterval setting for all Query Store databases in the ServerA\SQL Instance.

        .EXAMPLE
            Set-DbaDbQueryStoreOptions -SqlInstance ServerA\SQL -Database AdventureWorks -State ReadWrite -FlushInterval 600 -CollectionInterval 10 -MaxSize 100 -CaptureMode all -CleanupMode Auto -StaleQueryThreshold 100

            Configure the Query Store settings for the AdventureWorks database in the ServerA\SQL Instance.

        .EXAMPLE
            Set-DbaDbQueryStoreOptions -SqlInstance ServerA\SQL -Exclude AdventureWorks -State ReadWrite -FlushInterval 600 -CollectionInterval 10 -MaxSize 100 -CaptureMode all -CleanupMode Auto -StaleQueryThreshold 100

            Configure the Query Store settings for all user databases except the AdventureWorks database in the ServerA\SQL Instance.
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$AllDatabases,
        [ValidateSet('ReadWrite', 'ReadOnly', 'Off')]
        [string[]]$State,
        [int64]$FlushInterval,
        [int64]$CollectionInterval,
        [int64]$MaxSize,
        [ValidateSet('Auto', 'All')]
        [string[]]$CaptureMode,
        [ValidateSet('Auto', 'Off')]
        [string[]]$CleanupMode,
        [int64]$StaleQueryThreshold,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        $ExcludeDatabase += 'master', 'tempdb'
    }

    process {
        if (!$Database -and !$ExcludeDatabase -and !$AllDatabases) {
            Stop-Function -Message "You must specify a database(s) to execute against using either -Database, -ExcludeDatabase or -AllDatabases"
            return
        }

        if (!$State -and !$FlushInterval -and !$CollectionInterval -and !$MaxSize -and !$CaptureMode -and !$CleanupMode -and !$StaleQueryThreshold) {
            Stop-Function -Message "You must specify something to change."
            return
        }

        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 13

            }
            catch {
                Stop-Function -Message "Can't connect to $instance. Moving on." -Category InvalidOperation -InnerErrorRecord $_ -Target $instance -Continue
            }

            # We have to exclude all the system databases since they cannot have the Query Store feature enabled
            $dbs = Get-DbaDatabase -SqlInstance $server -ExcludeDatabase $ExcludeDatabase -Database $Database | Where-Object IsAccessible

            foreach ($db in $dbs) {
                Write-Message -Level Verbose -Message "Processing $($db.name) on $instance"

                if ($db.IsAccessible -eq $false) {
                    Write-Message -Level Warning -Message "The database $db on server $instance is not accessible. Skipping database."
                    continue
                }

                if ($State) {
                    if ($Pscmdlet.ShouldProcess("$db on $instance", "Changing DesiredState to $state")) {
                        $db.QueryStoreOptions.DesiredState = $State
                        $db.QueryStoreOptions.Alter()
                        $db.QueryStoreOptions.Refresh()
                    }
                }

                if ($db.QueryStoreOptions.DesiredState -eq "Off" -and (Test-Bound -Parameter State -Not)) {
                    Write-Message -Level Warning -Message "State is set to Off; cannot change values. Please update State to ReadOnly or ReadWrite."
                    continue
                }

                if ($FlushInterval) {
                    if ($Pscmdlet.ShouldProcess("$db on $instance", "Changing DataFlushIntervalInSeconds to $FlushInterval")) {
                        $db.QueryStoreOptions.DataFlushIntervalInSeconds = $FlushInterval
                    }
                }

                if ($CollectionInterval) {
                    if ($Pscmdlet.ShouldProcess("$db on $instance", "Changing StatisticsCollectionIntervalInMinutes to $CollectionInterval")) {
                        $db.QueryStoreOptions.StatisticsCollectionIntervalInMinutes = $CollectionInterval
                    }
                }

                if ($MaxSize) {
                    if ($Pscmdlet.ShouldProcess("$db on $instance", "Changing MaxStorageSizeInMB to $MaxSize")) {
                        $db.QueryStoreOptions.MaxStorageSizeInMB = $MaxSize
                    }
                }

                if ($CaptureMode) {
                    if ($Pscmdlet.ShouldProcess("$db on $instance", "Changing QueryCaptureMode to $CaptureMode")) {
                        $db.QueryStoreOptions.QueryCaptureMode = $CaptureMode
                    }
                }

                if ($CleanupMode) {
                    if ($Pscmdlet.ShouldProcess("$db on $instance", "Changing SizeBasedCleanupMode to $CleanupMode")) {
                        $db.QueryStoreOptions.SizeBasedCleanupMode = $CleanupMode
                    }
                }

                if ($StaleQueryThreshold) {
                    if ($Pscmdlet.ShouldProcess("$db on $instance", "Changing StaleQueryThresholdInDays to $StaleQueryThreshold")) {
                        $db.QueryStoreOptions.StaleQueryThresholdInDays = $StaleQueryThreshold
                    }
                }

                # Alter the Query Store Configuration
                if ($Pscmdlet.ShouldProcess("$db on $instance", "Altering Query Store configuration on database")) {
                    try {
                        $db.QueryStoreOptions.Alter()
                        $db.Alter()
                        $db.Refresh()
                    }
                    catch {
                        Stop-Function -Message "Could not modify configuration." -Category InvalidOperation -InnerErrorRecord $_ -Target $db -Continue
                    }
                }

                if ($Pscmdlet.ShouldProcess("$db on $instance", "Getting results from Get-DbaDbQueryStoreOptions")) {
                    # Display resulting changes
                    Get-DbaDbQueryStoreOptions -SqlInstance $server -Database $db.name -Verbose:$false
                }
            }
        }
    }
}

function Set-DbaDbRecoveryModel {
    <#
        .SYNOPSIS
            Set-DbaDbRecoveryModel sets the Recovery Model.

        .DESCRIPTION
            Set-DbaDbRecoveryModel sets the Recovery Model for user databases.

        .PARAMETER SqlInstance
            The target SQL Server instance or instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process - this list is auto-populated from the server. if unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto-populated from the server

        .PARAMETER AllDatabases
            This is a parameter that was included for safety, so you don't accidentally set options on all databases without specifying

        .PARAMETER RecoveryModel
            Recovery Model to be set. Valid options are 'Simple', 'Full', 'BulkLogged'

            Details about the recovery models can be found here:
            https://docs.microsoft.com/en-us/sql/relational-databases/backup-restore/recovery-models-sql-server

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            Prompts for confirmation. For example:

            Are you sure you want to perform this action?
            Performing the operation "ALTER DATABASE [model] SET RECOVERY Full" on target "[model] on WERES14224".
            [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER InputObject
        A collection of databases (such as returned by Get-DbaDatabase)

        .NOTES
            Tags: Recovery, RecoveryModel, Simple, Full, Bulk, BulkLogged
            Author: Viorel Ciucu (@viorelciucu), https://www.cviorel.com

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Set-DbaDbRecoveryModel

        .EXAMPLE
            Set-DbaDbRecoveryModel -SqlInstance sql2014 -RecoveryModel BulkLogged -Database model -Confirm:$true -Verbose

            Sets the Recovery Model to BulkLogged for database [model] on SQL Server instance sql2014. User is requested to confirm the action.

        .EXAMPLE
            Get-DbaDatabase -SqlInstance sql2014 -Database TestDB | Set-DbaDbRecoveryModel -RecoveryModel Simple  -Confirm:$false

            Sets the Recovery Model to Simple for database [TestDB] on SQL Server instance sql2014. Confirmation is not required.

        .EXAMPLE
            Set-DbaDbRecoveryModel -SqlInstance sql2014 -RecoveryModel Simple -Database TestDB -Confirm:$false

            Sets the Recovery Model to Simple for database [TestDB] on SQL Server instance sql2014. Confirmation is not required.

        .EXAMPLE
            Set-DbaDbRecoveryModel -SqlInstance sql2014 -RecoveryModel Simple -AllDatabases -Confirm:$false

            Sets the Recovery Model to Simple for ALL uses databases MODEL database on SQL Server instance sql2014. Runs without asking for confirmation.

        .EXAMPLE
            Set-DbaDbRecoveryModel -SqlInstance sql2014 -RecoveryModel BulkLogged -Database TestDB1, TestDB2 -Confirm:$false -Verbose

            Sets the Recovery Model to BulkLogged for [TestDB1] and [TestDB2] databases on SQL Server instance sql2014. Runs without asking for confirmation.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess, ConfirmImpact = 'High')]
    param (
        [parameter(Mandatory, ParameterSetName = "Instance")]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory)]
        [ValidateSet('Simple', 'Full', 'BulkLogged')]
        [string]$RecoveryModel,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$AllDatabases,
        [switch]$EnableException,
        [parameter(Mandatory, ValueFromPipeline, ParameterSetName = "Pipeline")]
        [Microsoft.SqlServer.Management.Smo.Database[]]$InputObject
    )
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if (!$Database -and !$AllDatabases -and !$ExcludeDatabase) {
                Stop-Function -Message "You must specify -AllDatabases or -Database to continue"
                return
            }

            # We need to be able to change the RecoveryModel for model database
            $systemdbs = @("tempdb")
            $databases = $server.Databases | Where-Object { $systemdbs -notcontains $_.Name -and $_.IsAccessible }

            # filter collection based on -Database/-Exclude parameters
            if ($Database) {
                $databases = $databases | Where-Object Name -In $Database
            }

            if ($ExcludeDatabase) {
                $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }

            if (!$databases) {
                Stop-Function -Message "The database(s) you specified do not exist on the instance $instance."
                return
            }

            $InputObject += $databases
        }

        foreach ($db in $InputObject) {
            if ($db.RecoveryModel -eq $RecoveryModel) {
                Stop-Function -Message "Recovery Model for database $db is already set to $RecoveryModel" -Category ConnectionError -Target $instance -Continue
            }
            else {
                $db.RecoveryModel = $RecoveryModel;
                if ($Pscmdlet.ShouldProcess("$db on $instance", "ALTER DATABASE $db SET RECOVERY $RecoveryModel")) {
                    $db.Alter()
                    Write-Message -Level Verbose -Message "Recovery Model set to $RecoveryModel for database $db"
                }
            }
            Get-DbaDbRecoveryModel -SqlInstance $db.Parent -Database $db.name
        }
    }
}
function Set-DbaErrorLogConfig {
    <#
        .SYNOPSIS
            Set the configuration for the ErrorLog on a given SQL Server instance

        .DESCRIPTION
            Sets the number of log files configured on all versions, and size in KB in SQL Server 2012+ and above.

            To set the Path to the ErrorLog, use Set-DbaStartupParameter -ErrorLog. Note that this command requires
            remote, administrative access to the Windows/WMI server, similar to SQL Configuration Manager.

        .PARAMETER SqlInstance
            The target SQL Server instance(s)

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER LogCount
            Integer value between 6 and 99 for setting the number of error log files to keep for SQL Server instance.

        .PARAMETER LogSize
            Integer value for the size in KB that you want the error log file to grow. This is feature only in SQL Server 2012 and higher. When the file reaches that limit SQL Server will roll the error log over.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Instance, ErrorLog
            Author: Shawn Melton (@wsmelton)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Set-DbaErrorLogConfig

       .EXAMPLE
            Set-DbaErrorLogConfig -SqlInstance sql2017,sql2014 -LogCount 25

            Sets the number of error log files to 25 on sql2017 and sql2014

        .EXAMPLE
            Set-DbaErrorLogConfig -SqlInstance sql2014 -LogSize 102400

            Sets the size of the error log file, before it rolls over, to 102400 KB (100 MB) on sql2014

        .EXAMPLE
            Set-DbaErrorLogConfig -SqlInstance sql2012 -LogCount 25 -LogSize 500

            Sets the number of error log files to 25 and size before it will roll over to 500 KB on sql2012
    #>
    [cmdletbinding(SupportsShouldProcess)]
    param(
        [Parameter(ValueFromPipelineByPropertyName, Mandatory)]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [ValidateRange(6, 99)]
        [int]$LogCount,
        [int]$LogSize,
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $currentNumLogs = $server.NumberOfLogFiles
            $currentLogSize = $server.ErrorLogSizeKb

            $collection = [PSCustomObject]@{
                ComputerName              = $server.ComputerName
                InstanceName              = $server.ServiceName
                SqlInstance               = $server.DomainInstanceName
                LogCount                  = $currentNumLogs
                LogSize                   = [dbasize]($currentLogSize * 1024)
            }
            if (Test-Bound -ParameterName 'LogSize') {
                if ($server.VersionMajor -lt 11) {
                    Stop-Function -Message "Size is cannot be set on $instance. SQL Server 2008 R2 and below not supported." -Continue
                }
                if ($LogSize -eq $currentLogSize) {
                    Write-Message -Level Warning -Message "The provided value for LogSize is already set to $LogSize KB on $instance"
                }
                else {
                    if ($PSCmdlet.ShouldProcess($server, "Updating log size from [$currentLogSize] to [$LogSize]")) {
                        try {
                            $server.ErrorLogSizeKb = $LogSize
                            $server.Alter()
                        }
                        catch {
                            Stop-Function -Message "Issue setting number of log files on $instance" -Target $instance -ErrorRecord $_ -Exception $_.Exception.InnerException.InnerException.InnerException -Continue
                        }
                    }
                    if ($PSCmdlet.ShouldProcess($server, "Output final results of setting error log size")) {
                        $server.Refresh()
                        $collection.LogSize = [dbasize]($server.ErrorLogSizeKb * 1024)
                    }
                }
            }

            if (Test-Bound -ParameterName 'LogCount') {
                if ($LogCount -eq $currentNumLogs) {
                    Write-Message -Level Warning -Message "The provided value for LogCount is already set to $LogCount on $instance"
                }
                else {
                    if ($PSCmdlet.ShouldProcess($server, "Setting number of logs from [$currentNumLogs] to [$LogCount]")) {
                        try {
                            $server.NumberOfLogFiles = $LogCount
                            $server.Alter()
                        }
                        catch {
                            Stop-Function -Message "Issue setting number of log files on $instance" -Target $instance -ErrorRecord $_ -Exception $_.Exception.InnerException.InnerException.InnerException -Continue
                        }
                    }
                    if ($PSCmdlet.ShouldProcess($server, "Output final results of setting number of log files")) {
                        $server.Refresh()
                        $collection.LogCount = $server.NumberOfLogFiles
                    }
                }
            }
            $collection
        }
    }
}
function Set-DbaJobOwner {
    <#
        .SYNOPSIS
            Sets SQL Agent job owners with a desired login if jobs do not match that owner.

        .DESCRIPTION
            This function alters SQL Agent Job ownership to match a specified login if their current owner does not match the target login. By default, the target login will be 'sa', but the the user may specify a different login for ownership. This be applied to all jobs or only to a select collection of jobs.

            Best practice reference: http://sqlmag.com/blog/sql-server-tip-assign-ownership-jobs-sysadmin-account

        .NOTES
            Tags: Agent, Job
            Author: Michael Fal (@Mike_Fal), http://mikefal.net

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .PARAMETER SqlInstance
            Specifies the SQL Server instance(s) to scan.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Job
            Specifies the job(s) to process. Options for this list are auto-populated from the server. If unspecified, all jobs will be processed.

        .PARAMETER ExcludeJob
            Specifies the job(s) to exclude from processing. Options for this list are auto-populated from the server.

        .PARAMETER Login
            Specifies the login that you wish check for ownership. This defaults to 'sa' or the sysadmin name if sa was renamed. This must be a valid security principal which exists on the target server.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .LINK
            https://dbatools.io/Set-DbaJobOwner

        .EXAMPLE
            Set-DbaJobOwner -SqlInstance localhost

            Sets SQL Agent Job owner to sa on all jobs where the owner does not match sa.

        .EXAMPLE
            Set-DbaJobOwner -SqlInstance localhost -Login DOMAIN\account

            Sets SQL Agent Job owner to sa on all jobs where the owner does not match 'DOMAIN\account'. Note
            that Login must be a valid security principal that exists on the target server.

        .EXAMPLE
            Set-DbaJobOwner -SqlInstance localhost -Job job1, job2

            Sets SQL Agent Job owner to 'sa' on the job1 and job2 jobs if their current owner does not match 'sa'.

        .EXAMPLE
            'sqlserver','sql2016' | Set-DbaJobOwner

            Sets SQL Agent Job owner to sa on all jobs where the owner does not match sa on both sqlserver and sql2016.
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Jobs")]
        [object[]]$Job,
        [object[]]$ExcludeJob,
        [Alias("TargetLogin")]
        [string]$Login,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($servername in $SqlInstance) {
            #connect to the instance
            Write-Message -Level Verbose -Message "Connecting to $servername."
            $server = Connect-SqlInstance $servername -SqlCredential $SqlCredential

            # dynamic sa name for orgs who have changed their sa name
            if (!$Login) {
                $Login = ($server.logins | Where-Object { $_.id -eq 1 }).Name
            }

            #Validate login
            if (($server.Logins.Name) -notcontains $Login) {
                if ($SqlInstance.count -eq 1) {
                    throw -Message "Invalid login: $Login."
                }
                else {
                    Write-Message -Level Warning -Message "$Login is not a valid login on $servername. Moving on."
                    Continue
                }
            }

            if ($server.logins[$Login].LoginType -eq 'WindowsGroup') {
                throw "$Login is a Windows Group and can not be a job owner."
            }

            #Get database list. If value for -Job is passed, massage to make it a string array.
            #Otherwise, use all jobs on the instance where owner not equal to -TargetLogin
            Write-Message -Level Verbose -Message "Gathering jobs to update."

            if ($Job) {
                $jobcollection = $server.JobServer.Jobs | Where-Object { $_.OwnerLoginName -ne $Login -and $Job -contains $_.Name }
            }
            else {
                $jobcollection = $server.JobServer.Jobs | Where-Object { $_.OwnerLoginName -ne $Login }
            }

            if ($ExcludeJob) {
                $jobcollection = $jobcollection | Where-Object { $ExcludeJob -notcontains $_.Name }
            }

            Write-Message -Level Verbose -Message "Updating $($jobcollection.Count) job(s)."
            foreach ($j in $jobcollection) {
                $jobname = $j.name

                if ($PSCmdlet.ShouldProcess($servername, "Setting job owner for $jobname to $Login")) {
                    try {
                        Write-Message -Level Verbose -Message "Setting job owner for $jobname to $Login on $servername."
                        #Set job owner to $TargetLogin (default 'sa')
                        $j.OwnerLoginName = $Login
                        $j.Alter()
                    }
                    catch {
                        Stop-Function -Message "Issue setting job owner on $jobName." -Target $jobName -InnerErrorRecord $_ -Category InvalidOperation
                    }
                }
            }
        }
    }
}
function Set-DbaLogin {

    <#
    .SYNOPSIS
    Set-DbaLogin makes it possible to make changes to one or more logins.

    .DESCRIPTION
    Set-DbaLogin will enable you to change the password, unlock, rename, disable or enable, deny or grant login privileges to the login.
    It's also possible to add or remove server roles from the login.

    .PARAMETER SqlInstance
    SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

    .PARAMETER SqlCredential
    Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

    .PARAMETER Login
    The login that needs to be changed

    .PARAMETER Password
    The new password for the login This can be either a credential or a secure string.

    .PARAMETER Unlock
    Switch to unlock an account. This will only be used in conjunction with the -Password parameter.
    The default is false.

    .PARAMETER MustChange
    Does the user need to change his/her password. This will only be used in conjunction with the -Password parameter.
    The default is false.

    .PARAMETER NewName
    The new name for the login.

    .PARAMETER Disable
    Disable the login

    .PARAMETER Enable
    Enable the login

    .PARAMETER DenyLogin
    Deny access to SQL Server

    .PARAMETER GrantLogin
    Grant access to SQL Server

    .PARAMETER PasswordPolicyEnforced
    Should the password policy be enforced.

    .PARAMETER AddRole
    Add one or more server roles to the login
    The following roles can be used "bulkadmin", "dbcreator", "diskadmin", "processadmin", "public", "securityadmin", "serveradmin", "setupadmin", "sysadmin".

    .PARAMETER RemoveRole
    Remove one or more server roles to the login
    The following roles can be used "bulkadmin", "dbcreator", "diskadmin", "processadmin", "public", "securityadmin", "serveradmin", "setupadmin", "sysadmin".

    .PARAMETER EnableException
    By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
    Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
    Original Author: Sander Stad (@sqlstad, sqlstad.nl)
    Tags: Login

    Website: https://dbatools.io
    Copyright: (C) Chrissy LeMaire, [email protected]
    License: MIT https://opensource.org/licenses/MIT

    .LINK
    https://dbatools.io/Set-DbaLogin

    .EXAMPLE
    $password = ConvertTo-SecureString "PlainTextPassword" -AsPlainText -Force
    $cred = New-Object System.Management.Automation.PSCredential ("username", $password)
    Set-DbaLogin -SqlInstance sql1 -Login login1 -Password $cred -Unlock -MustChange

    Set the new password for login1 using a credential, unlock the account and set the option
    that the usermust change password at next logon.

    .EXAMPLE
    Set-DbaLogin -SqlInstance sql1 -Login login1 -Enable

    Enable the login

    .EXAMPLE
    Set-DbaLogin -SqlInstance sql1 -Login login1, login2, login3, login4 -Enable

    Enable multiple logins

    .EXAMPLE
    Set-DbaLogin -SqlInstance sql1, sql2, sql3 -Login login1, login2, login3, login4 -Enable

    Enable multiple logins on multiple instances

    .EXAMPLE
    Set-DbaLogin -SqlInstance sql1 -Login login1 -Disable

    Disable the login

    .EXAMPLE
    Set-DbaLogin -SqlInstance sql1 -Login login1 -DenyLogin

    Deny the login to connect to the instance

    .EXAMPLE
    Set-DbaLogin -SqlInstance sql1 -Login login1 -GrantLogin

    Grant the login to connect to the instance

    .EXAMPLE
    Set-DbaLogin -SqlInstance sql1 -Login login1 -PasswordPolicyEnforced

    Enforces the password policy on a login

    .EXAMPLE
    Set-DbaLogin -SqlInstance sql1 -Login login1 -PasswordPolicyEnforced:$false

    Disables enforcement of the password policy on a login

    .EXAMPLE
    Set-DbaLogin -SqlInstance sql1 -Login test -AddRole serveradmin

    Add the server role "serveradmin" to the login

    .EXAMPLE
    Set-DbaLogin -SqlInstance sql1 -Login test -RemoveRole bulkadmin

    Remove the server role "bulkadmin" to the login

#>

    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory = $true)]
        [string[]]$Login,
        [SecureString]$Password,
        [switch]$Unlock,
        [switch]$MustChange,
        [string]$NewName,
        [switch]$Disable,
        [switch]$Enable,
        [switch]$DenyLogin,
        [switch]$GrantLogin,
        [switch]$PasswordPolicyEnforced,
        [ValidateSet("bulkadmin", "dbcreator", "diskadmin", "processadmin", "public", "securityadmin", "serveradmin", "setupadmin", "sysadmin")]
        [string[]]$AddRole,
        [ValidateSet("bulkadmin", "dbcreator", "diskadmin", "processadmin", "public", "securityadmin", "serveradmin", "setupadmin", "sysadmin")]
        [string[]]$RemoveRole,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {

        # Check the parameters
        if ($Login -eq $NewName) {
            Stop-Function -Message "Login name is the same as the value in -NewName" -Target $Login -Continue
        }

        if ($Disable -and $Enable) {
            Stop-Function -Message "You cannot use both -Enable and -Disable together" -Target $Login -Continue
        }

        if ($GrantLogin -and $DenyLogin) {
            Stop-Function -Message "You cannot use both -GrantLogin and -DenyLogin together" -Target $Login -Continue
        }

        # Check the password
        if ($Password) {
            switch ($Password.GetType().Name) {
                "PSCredential" { $newPassword = $Password.Password}
                "SecureString" { $newPassword = $Password}
            }
        }
        else {
        }

    }

    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $sqlinstance) {
            # Try connecting to the instance
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            # Get all the logins
            $allLogins = $server.Logins | Where-Object {($_.IsSystemObject -eq $false) -and ($_.Name -notlike '##*')}
            $logins = $server.Logins | Where-Object {$Login -contains $_.Name}

            # Loop through all the logins
            foreach ($l in $logins) {

                # Create the notes
                $notes = @()

                # Change the name
                if ($NewName) {
                    # Check if the new name doesn't already exist
                    if ($allLogins.Name -notcontains $NewName) {
                        try {
                            $l.Rename($NewName)
                        }
                        catch {
                            $notes += "Couldn't rename login"
                            Stop-Function -Message "Something went wrong changing the name for $l" -Target $l -ErrorRecord $_ -Continue
                        }
                    }
                    else {
                        $notes += "New login name already exists"
                        Write-Message -Message "New login name $NewName already exists on $instance" -Level Verbose
                    }
                }

                # Change the password
                if ($Password) {
                    try {
                        $l.ChangePassword($newPassword, $Unlock, $MustChange)
                        $passwordChanged = $true
                    }
                    catch {
                        $notes += "Couldn't change password"
                        $passwordChanged = $false
                        Stop-Function -Message "Something went wrong changing the password for $l" -Target $l -ErrorRecord $_ -Continue
                    }
                }

                # Disable the login
                if ($Disable) {
                    if ($l.IsDisabled) {
                        Write-Message -Message "Login $l is already disabled" -Level Verbose
                    }
                    else {
                        try {
                            $l.Disable()
                        }
                        catch {
                            $notes += "Couldn't disable login"
                            Stop-Function -Message "Something went wrong disabling $l" -Target $l -ErrorRecord $_ -Continue
                        }
                    }
                }

                # Enable the login
                if ($Enable) {
                    if (-not $l.IsDisabled) {
                        Write-Message -Message "Login $l is already enabled" -Level Verbose
                    }
                    else {
                        try {
                            $l.Enable()
                        }
                        catch {
                            $notes += "Couldn't enable login"
                            Stop-Function -Message "Something went wrong enabling $l" -Target $l -ErrorRecord $_ -Continue
                        }
                    }
                }

                # Deny access
                if ($DenyLogin) {
                    if ($l.DenyWindowsLogin) {
                        Write-Message -Message "Login $l already has login access denied" -Level Verbose
                    }
                    else {
                        $l.DenyWindowsLogin = $true
                    }
                }

                # Grant access
                if ($GrantLogin) {
                    if (-not $l.DenyWindowsLogin) {
                        Write-Message -Message "Login $l already has login access granted" -Level Verbose
                    }
                    else {
                        $l.DenyWindowsLogin = $false
                    }
                }

                # Enforce password policy
                if (Test-Bound PasswordPolicyEnforced) {
                    if ($l.PasswordPolicyEnforced -eq $PasswordPolicyEnforced) {
                        Write-Message -Message ("Login $l password policy is already set to " + $l.PasswordPolicyEnforced) -Level Verbose
                    } else {
                        $l.PasswordPolicyEnforced = $PasswordPolicyEnforced
                    }
                }

                # Add server roles to login
                if ($AddRole) {
                    # Loop through each of the roles
                    foreach ($role in $AddRole) {
                        try {
                            $l.AddToRole($role)
                        }
                        catch {
                            $notes += "Couldn't add role $role"
                            Stop-Function -Message "Something went wrong adding role $role to $l" -Target $l -ErrorRecord $_ -Continue
                        }
                    }
                }

                # Remove server roles from login
                if ($RemoveRole) {
                    # Loop through each of the roles
                    foreach ($role in $RemoveRole) {
                        try {
                            $server.Roles[$role].DropMember($l.Name)
                        }
                        catch {
                            $notes += "Couldn't remove role $role"
                            Stop-Function -Message "Something went wrong removing role $role to $l" -Target $l -ErrorRecord $_ -Continue
                        }
                    }
                }

                # Alter the login to make the changes
                $l.Alter()

                # Retrieve the server roles for the login
                $roles = Get-DbaRoleMember -SqlInstance $instance -IncludeServerLevel | Where-Object {$null -eq $_.Database -and $_.Member -eq $l.Name}

                # Check if there were any notes to include in the results
                if ($notes) {
                    $notes = $notes | Get-Unique
                    $notes = $notes -Join ';'
                }
                else {
                    $notes = $null
                }

                # Return the results
                [PSCustomObject]@{
                    ComputerName           = $server.ComputerName
                    InstanceName           = $server.ServiceName
                    SqlInstance            = $server.DomainInstanceName
                    LoginName              = $l.Name
                    DenyLogin              = $l.DenyWindowsLogin
                    IsDisabled             = $l.IsDisabled
                    IsLocked               = $l.IsLocked
                    PasswordPolicyEnforced = $l.PasswordPolicyEnforced
                    MustChangePassword     = $l.MustChangePassword
                    PasswordChanged        = $passwordChanged
                    ServerRole             = $roles.Role -join ","
                    Notes                  = $notes
                } | Select-DefaultView -ExcludeProperty Login

            } # end for each login

        } # end for each instance

    } # end process

    end {
        if (Test-FunctionInterrupt) { return }
        Write-Message -Message "Finished changing login(s)" -Level Verbose
    }


}
function Set-DbaMaxDop {
    <#
        .SYNOPSIS
            Sets SQL Server maximum degree of parallelism (Max DOP), then displays information relating to SQL Server Max DOP configuration settings. Works on SQL Server 2005 and higher.

        .DESCRIPTION
            Uses the Test-DbaMaxDop command to get the recommended value if -MaxDop parameter is not specified.

            These are just general recommendations for SQL Server and are a good starting point for setting the "max degree of parallelism" option.

            You can set MaxDop database scoped configurations if the server is version 2016 or higher

        .PARAMETER SqlInstance
            The SQL Server instance to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies one or more databases to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies one or more databases to exclude from processing. Options for this list are auto-populated from the server

        .PARAMETER MaxDop
            Specifies the Max DOP value to set.

        .PARAMETER AllDatabases
            If this switch is enabled, Max DOP will be set on all databases. This switch is only useful on SQL Server 2016 and higher.

        .PARAMETER Collection
            If Test-SQLMaxDop has been executed prior to this function, the results may be passed in via this parameter.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER WhatIf
            Shows what would happen if the cmdlet runs. The cmdlet is not run.

        .PARAMETER Confirm
            Prompts you for confirmation before running the cmdlet.

        .NOTES
            Tags: MaxDop, SpConfigure
            Author: Claudio Silva (@claudioessilva)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Set-DbaMaxDop

        .EXAMPLE
            Set-DbaMaxDop -SqlInstance sql2008, sql2012

            Sets Max DOP to the recommended value for servers sql2008 and sql2012.

        .EXAMPLE
            Set-DbaMaxDop -SqlInstance sql2014 -MaxDop 4

            Sets Max DOP to 4 for server sql2014.

        .EXAMPLE
            Test-DbaMaxDop -SqlInstance sql2008 | Set-DbaMaxDop

            Gets the recommended Max DOP from Test-DbaMaxDop and applies it to to sql2008.

        .EXAMPLE
            Set-DbaMaxDop -SqlInstance sql2016 -Database db1

            Set recommended Max DOP for database db1 on server sql2016.

        .EXAMPLE
            Set-DbaMaxDop -SqlInstance sql2016 -AllDatabases

            Set recommended Max DOP for all databases on server sql2016.

    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [int]$MaxDop = -1,
        [Parameter(ValueFromPipeline = $True)]
        [object]$Collection,
        [Alias("All")]
        [switch]$AllDatabases,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $processed = New-Object System.Collections.ArrayList
        $results = @()
    }
    process {
        if ((Test-Bound -Parameter Database) -and (Test-Bound -Parameter AllDatabases) -and (Test-Bound -Parameter ExcludeDatabase)) {
            Stop-Function -Category InvalidArgument -Message "-Database, -AllDatabases and -ExcludeDatabase are mutually exclusive. Please choose only one. Quitting."
            return
        }

        $dbscopedconfiguration = $false

        if ($MaxDop -eq -1) {
            $UseRecommended = $true
        }

        if ((Test-Bound -Not -Parameter Collection)) {
            $collection = Test-DbaMaxDop -SqlInstance $sqlinstance -SqlCredential $SqlCredential -Verbose:$false
        }
        elseif ($null -eq $collection.SqlInstance) {
            $collection = Test-DbaMaxDop -SqlInstance $sqlinstance -SqlCredential $SqlCredential -Verbose:$false
        }

        $collection | Add-Member -Force -NotePropertyName OldInstanceMaxDopValue -NotePropertyValue 0
        $collection | Add-Member -Force -NotePropertyName OldDatabaseMaxDopValue -NotePropertyValue 0

        #If we have servers 2016 or higher we will have a row per database plus the instance level, getting unique we only run one time per instance
        $servers = $collection | Select-Object SqlInstance -Unique

        foreach ($server in $servers) {
            $servername = $server.SqlInstance

            Write-Message -Level Verbose -Message "Connecting to $servername"
            try {
                $server = Connect-SqlInstance -SqlInstance $servername -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $servername -Continue
            }

            if (!(Test-SqlSa -SqlInstance $server)) {
                Stop-Function -Message "Not a sysadmin on $server. Skipping." -Category PermissionDenied -ErrorRecord $_ -Target $currentServer -Continue
            }

            if ($server.versionMajor -ge 13) {
                Write-Message -Level Verbose -Message "Server '$servername' supports Max DOP configuration per database."

                if ((Test-Bound -Not -Parameter Database) -and (Test-Bound -Not -Parameter ExcludeDatabase)) {
                    #Set at instance level
                    $collection = $collection | Where-Object { $_.DatabaseMaxDop -eq "N/A" }
                }
                else {
                    $dbscopedconfiguration = $true

                    if ((Test-Bound -Not -Parameter AllDatabases) -and (Test-Bound -Parameter Database)) {
                        $collection = $collection | Where-Object { $_.Database -in $Database }
                    }
                    elseif ((Test-Bound -Not -Parameter AllDatabases) -and (Test-Bound -Parameter ExcludeDatabase)) {
                        $collection = $collection | Where-Object { $_.Database -notin $ExcludeDatabase }
                    }
                    else {
                        if (Test-Bound -Parameter AllDatabases) {
                            $collection = $collection | Where-Object { $_.DatabaseMaxDop -ne "N/A" }
                        }
                        else {
                            $collection = $collection | Where-Object { $_.DatabaseMaxDop -eq "N/A" }
                            $dbscopedconfiguration = $false
                        }
                    }
                }
            }
            else {
                if ((Test-Bound -Parameter database) -or (Test-Bound -Parameter AllDatabases)) {
                    Write-Message -Level Warning -Message "Server '$servername' (v$($server.versionMajor)) does not support Max DOP configuration at the database level. Remember that this option is only available from SQL Server 2016 (v13). Run the command again without using database related parameters. Skipping."
                    Continue
                }
            }

            foreach ($row in $collection | Where-Object { $_.SqlInstance -eq $servername }) {
                if ($UseRecommended -and ($row.RecommendedMaxDop -eq $row.CurrentInstanceMaxDop) -and !($dbscopedconfiguration)) {
                    Write-Message -Level Verbose -Message "$servername is configured properly. No change required."
                    Continue
                }

                if ($UseRecommended -and ($row.RecommendedMaxDop -eq $row.DatabaseMaxDop) -and $dbscopedconfiguration) {
                    Write-Message -Level Verbose -Message "Database $($row.Database) on $servername is configured properly. No change required."
                    Continue
                }

                $row.OldInstanceMaxDopValue = $row.CurrentInstanceMaxDop

                try {
                    if ($UseRecommended) {
                        if ($dbscopedconfiguration) {
                            $row.OldDatabaseMaxDopValue = $row.DatabaseMaxDop

                            if ($resetDatabases) {
                                Write-Message -Level Verbose -Message "Changing $($row.Database) database max DOP to $($row.DatabaseMaxDop)."
                                $server.Databases["$($row.Database)"].MaxDop = $row.DatabaseMaxDop
                            }
                            else {
                                Write-Message -Level Verbose -Message "Changing $($row.Database) database max DOP from $($row.DatabaseMaxDop) to $($row.RecommendedMaxDop)."
                                $server.Databases["$($row.Database)"].MaxDop = $row.RecommendedMaxDop
                                $row.DatabaseMaxDop = $row.RecommendedMaxDop
                            }

                        }
                        else {
                            Write-Message -Level Verbose -Message "Changing $server SQL Server max DOP from $($row.CurrentInstanceMaxDop) to $($row.RecommendedMaxDop)."
                            $server.Configuration.MaxDegreeOfParallelism.ConfigValue = $row.RecommendedMaxDop
                            $row.CurrentInstanceMaxDop = $row.RecommendedMaxDop
                        }
                    }
                    else {
                        if ($dbscopedconfiguration) {
                            $row.OldDatabaseMaxDopValue = $row.DatabaseMaxDop

                            Write-Message -Level Verbose -Message "Changing $($row.Database) database max DOP from $($row.DatabaseMaxDop) to $MaxDop."
                            $server.Databases["$($row.Database)"].MaxDop = $MaxDop
                            $row.DatabaseMaxDop = $MaxDop
                        }
                        else {
                            Write-Message -Level Verbose -Message "Changing $servername SQL Server max DOP from $($row.CurrentInstanceMaxDop) to $MaxDop."
                            $server.Configuration.MaxDegreeOfParallelism.ConfigValue = $MaxDop
                            $row.CurrentInstanceMaxDop = $MaxDop
                        }
                    }

                    if ($dbscopedconfiguration) {
                        if ($Pscmdlet.ShouldProcess($row.Database, "Setting max dop on database")) {
                            $server.Databases["$($row.Database)"].Alter()
                        }
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($servername, "Setting max dop on instance")) {
                            $server.Configuration.Alter()
                        }
                    }

                    $results += [pscustomobject]@{
                        ComputerName           = $server.ComputerName
                        InstanceName           = $server.ServiceName
                        SqlInstance            = $server.DomainInstanceName
                        InstanceVersion        = $row.InstanceVersion
                        Database               = $row.Database
                        DatabaseMaxDop         = $row.DatabaseMaxDop
                        CurrentInstanceMaxDop  = $row.CurrentInstanceMaxDop
                        RecommendedMaxDop      = $row.RecommendedMaxDop
                        OldDatabaseMaxDopValue = $row.OldDatabaseMaxDopValue
                        OldInstanceMaxDopValue = $row.OldInstanceMaxDopValue
                    }
                }
                catch {
                    Stop-Function -Message "Could not modify Max Degree of Parallelism for $server."  -ErrorRecord $_ -Target $server -Continue
                }
            }

            if ($dbscopedconfiguration) {
                Select-DefaultView -InputObject $results -Property InstanceName, Database, OldDatabaseMaxDopValue, @{ name = "CurrentDatabaseMaxDopValue"; expression = { $_.DatabaseMaxDop } }
            }
            else {
                Select-DefaultView -InputObject $results -Property InstanceName, OldInstanceMaxDopValue, CurrentInstanceMaxDop
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#

function Set-DbaMaxMemory {
    <#
        .SYNOPSIS
            Sets SQL Server 'Max Server Memory' configuration setting to a new value then displays information this setting.

        .DESCRIPTION
            Sets SQL Server max memory then displays information relating to SQL Server Max Memory configuration settings.

            Inspired by Jonathan Kehayias's post about SQL Server Max memory (http://bit.ly/sqlmemcalc), this uses a formula to
            determine the default optimum RAM to use, then sets the SQL max value to that number.

            Jonathan notes that the formula used provides a *general recommendation* that doesn't account for everything that may
            be going on in your specific environment.

        .PARAMETER SqlInstance
            Allows you to specify a comma separated list of servers to query.

        .PARAMETER MaxMB
            Specifies the max megabytes

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER WhatIf
            Shows what would happen if the cmdlet runs. The cmdlet is not run.

        .PARAMETER Confirm
            Prompts you for confirmation before running the cmdlet.

        .NOTES
            Tags: MaxMemory, Memory
            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Set-DbaMaxMemory

        .EXAMPLE
            Set-DbaMaxMemory sqlserver1

            Set max memory to the recommended MB on just one server named "sqlserver1"

        .EXAMPLE
            Set-DbaMaxMemory -SqlInstance sqlserver1 -MaxMB 2048

            Explicitly max memory to 2048 MB on just one server, "sqlserver1"

        .EXAMPLE
            Get-DbaRegisteredServer -SqlInstance sqlserver | Test-DbaMaxMemory | Where-Object { $_.SqlMaxMB -gt $_.TotalMB } | Set-DbaMaxMemory

            Find all servers in SQL Server Central Management server that have Max SQL memory set to higher than the total memory
            of the server (think 2147483647), then pipe those to Set-DbaMaxMemory and use the default recommendation.
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param (
        [Parameter(Position = 0)]
        [Alias("ServerInstance", "SqlServer", "SqlServers", "ComputerName")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Parameter(Position = 1)]
        [int]$MaxMB,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        if ((Test-Bound -Not -Parameter SqlInstance) -and (Test-Bound -Not -Parameter Collection)) {
            Stop-Function -Category InvalidArgument -Message "You must specify a server list source using -SqlInstance or you can pipe results from Test-DbaMaxMemory"
            return
        }

        if ($MaxMB -eq 0) {
            $UseRecommended = $true
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if (!(Test-SqlSa -SqlInstance $server)) {
                Stop-Function -Message "Not a sysadmin on $server. Skipping." -Category PermissionDenied -ErrorRecord $_ -Target $server -Continue
            }

            try {
                $currentServer = Test-DbaMaxMemory -SqlInstance $server
                Add-Member -Force -InputObject $currentServer -NotePropertyName OldMaxValue -NotePropertyValue 0
                $currentServer.OldMaxValue = $currentServer.SqlMaxMB
            }
            catch {
                Stop-Function -Message "Issue collecting memory information on $server" -Target $server -ErrorRecord $_ -InnerException $_.Exception -Continue
            }

            try {
                if ($UseRecommended) {
                    Write-Message -Level Verbose -Message "Change $server SQL Server Max Memory from $($currentServer.SqlMaxMB) to $($currentServer.RecommendedMB) MB"

                    if ($currentServer.RecommendedMB -eq 0 -or $null -eq $currentServer.RecommendedMB) {
                        $maxMem = (Test-DbaMaxMemory -SqlInstance $server).RecommendedMB
                        Write-Message -Level VeryVerbose -Message "Max memory recommended: $maxMem"
                        $server.Configuration.MaxServerMemory.ConfigValue = $maxMem
                    }
                    else {
                        $server.Configuration.MaxServerMemory.ConfigValue = $currentServer.RecommendedMB
                    }
                }
                else {
                    Write-Message -Level Verbose -Message "Change $server SQL Server Max Memory from $($currentServer.SqlMaxMB) to $MaxMB MB"
                    $server.Configuration.MaxServerMemory.ConfigValue = $MaxMB
                }
                if ($PSCmdlet.ShouldProcess($server, "Change Max Memory from $($currentServer.OldMaxValue) to $($server.Configuration.MaxServerMemory.ConfigValue)")) {
                    try {
                        $server.Configuration.Alter()
                        $currentServer.SqlMaxMB = $server.Configuration.MaxServerMemory.ConfigValue
                    }
                    catch {
                        Stop-Function -Message "Failed to apply configuration change for $server" -ErrorRecord $_ -Target $server -Continue
                    }
                }
            }
            catch {
                Stop-Function -Message "Could not modify Max Server Memory for $server" -ErrorRecord $_ -Target $server -Continue
            }

            Add-Member -InputObject $currentServer -Force -MemberType NoteProperty -Name CurrentMaxValue -Value $currentServer.SqlMaxMB
            Select-DefaultView -InputObject $currentServer -Property ComputerName, InstanceName, SqlInstance, TotalMB, OldMaxValue, CurrentMaxValue
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#

function Set-DbaNetworkCertificate {
    <#
    .SYNOPSIS
        Sets the network certificate for SQL Server instance

    .DESCRIPTION
        Sets the network certificate for SQL Server instance. This setting is found in Configuration Manager.

        This command also grants read permissions for the service account on the certificate's private key.

        References:
        http://sqlmag.com/sql-server/7-steps-ssl-encryption
        https://azurebi.jppp.org/2016/01/23/using-lets-encrypt-certificates-for-secure-sql-server-connections/
        https://blogs.msdn.microsoft.com/sqlserverfaq/2016/09/26/creating-and-registering-ssl-certificates/

    .PARAMETER SqlInstance
        The target SQL Server - defaults to localhost.

    .PARAMETER Credential
        Allows you to login to the computer (not sql instance) using alternative credentials.

    .PARAMETER Certificate
        The target certificate object

    .PARAMETER Thumbprint
        The thumbprint of the target certificate

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .PARAMETER WhatIf
        Shows what would happen if the command were to run. No actions are actually performed.

    .PARAMETER Confirm
        Prompts you for confirmation before executing any changing operations within the command.

    .EXAMPLE
        New-DbaComputerCertificate | Set-DbaNetworkCertificate -SqlInstance localhost\SQL2008R2SP2

        Creates and imports a new certificate signed by an Active Directory CA on localhost then sets the network certificate for the SQL2008R2SP2 to that newly created certificate.

    .EXAMPLE
        Set-DbaNetworkCertificate -SqlInstance sql1\SQL2008R2SP2 -Thumbprint 1223FB1ACBCA44D3EE9640F81B6BA14A92F3D6E2

        Sets the network certificate for the SQL2008R2SP2 instance to the certificate with the thumbprint of 1223FB1ACBCA44D3EE9640F81B6BA14A92F3D6E2 in LocalMachine\My on sql1

    .NOTES
        Tags: Certificate

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT
#>
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Low", DefaultParameterSetName = 'Default')]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer", "ComputerName")]
        [DbaInstanceParameter[]]
        $SqlInstance = $env:COMPUTERNAME,

        [PSCredential]

        $Credential,

        [parameter(Mandatory, ParameterSetName = "Certificate", ValueFromPipeline)]
        [System.Security.Cryptography.X509Certificates.X509Certificate2]
        $Certificate,

        [parameter(Mandatory, ParameterSetName = "Thumbprint")]
        [string]
        $Thumbprint,

        [switch]
        [Alias('Silent')]$EnableException
    )

    process {
        if (Test-FunctionInterrupt) { return }
        $Certificate
        if (!$Certificate -and !$Thumbprint) {
            Stop-Function -Message "You must specify a certificate or thumbprint"
            return
        }

        if (!$Thumbprint) {
            Write-Message -Level SomewhatVerbose -Message "Getting thumbprint"
            $Thumbprint = $Certificate.Thumbprint
        }

        foreach ($instance in $sqlinstance) {
            Write-Message -Level VeryVerbose -Message "Processing $instance" -Target $instance
            $null = Test-ElevationRequirement -ComputerName $instance -Continue

            Write-Message -Level Verbose -Message "Resolving hostname"
            $resolved = $null
            $resolved = Resolve-DbaNetworkName -ComputerName $instance -Turbo

            if ($null -eq $resolved) {
                Stop-Function -Message "Can't resolve $instance" -Target $instance -Continue -Category InvalidArgument
            }

            $computername = $instance.ComputerName
            $instancename = $instance.instancename
            Write-Message -Level Output -Message "Connecting to SQL WMI on $computername"

            try {
                $sqlwmi = Invoke-ManagedComputerCommand -ComputerName $resolved.FQDN -ScriptBlock { $wmi.Services } -Credential $Credential -ErrorAction Stop | Where-Object DisplayName -eq "SQL Server ($instancename)"
            }
            catch {
                Stop-Function -Message "Failed to access $instance" -Target $instance -Continue -ErrorRecord $_
            }

            if (-not $sqlwmi) {
                Stop-Function -Message "Cannot find $instancename on $computerName" -Continue -Category ObjectNotFound -Target $instance
            }

            $regroot = ($sqlwmi.AdvancedProperties | Where-Object Name -eq REGROOT).Value
            $vsname = ($sqlwmi.AdvancedProperties | Where-Object Name -eq VSNAME).Value
            $instancename = $sqlwmi.DisplayName.Replace('SQL Server (', '').Replace(')', '') # Don't clown, I don't know regex :(
            $serviceaccount = $sqlwmi.ServiceAccount

            if ([System.String]::IsNullOrEmpty($regroot)) {
                $regroot = $sqlwmi.AdvancedProperties | Where-Object { $_ -match 'REGROOT' }
                $vsname = $sqlwmi.AdvancedProperties | Where-Object { $_ -match 'VSNAME' }

                if (![System.String]::IsNullOrEmpty($regroot)) {
                    $regroot = ($regroot -Split 'Value\=')[1]
                    $vsname = ($vsname -Split 'Value\=')[1]
                }
                else {
                    Stop-Function -Message "Can't find instance $vsname on $instance" -Continue -Category ObjectNotFound -Target $instance
                }
            }

            if ([System.String]::IsNullOrEmpty($vsname)) { $vsname = $instance }

            Write-Message -Level Output -Message "Regroot: $regroot" -Target $instance
            Write-Message -Level Output -Message "ServiceAcct: $serviceaccount" -Target $instance
            Write-Message -Level Output -Message "InstanceName: $instancename" -Target $instance
            Write-Message -Level Output -Message "VSNAME: $vsname" -Target $instance

            $scriptblock = {
                $regroot = $args[0]
                $serviceaccount = $args[1]
                $instancename = $args[2]
                $vsname = $args[3]
                $Thumbprint = $args[4]

                $regpath = "Registry::HKEY_LOCAL_MACHINE\$regroot\MSSQLServer\SuperSocketNetLib"

                $oldthumbprint = (Get-ItemProperty -Path $regpath -Name Certificate).Certificate

                $cert = Get-ChildItem Cert:\LocalMachine -Recurse -ErrorAction Stop | Where-Object { $_.Thumbprint -eq $Thumbprint }

                if ($null -eq $cert) {
                    Write-Warning "Certificate does not exist on $env:COMPUTERNAME"
                    return
                }

                $permission = $serviceaccount, "Read", "Allow"
                $accessRule = New-Object -TypeName System.Security.AccessControl.FileSystemAccessRule -ArgumentList $permission

                $keyPath = $env:ProgramData + "\Microsoft\Crypto\RSA\MachineKeys\"
                $keyName = $cert.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName
                $keyFullPath = $keyPath + $keyName

                $acl = Get-Acl -Path $keyFullPath
                $null = $acl.AddAccessRule($accessRule)
                Set-Acl -Path $keyFullPath -AclObject $acl

                if ($acl) {
                    Set-ItemProperty -Path $regpath -Name Certificate -Value $Thumbprint.ToString().ToLower() # to make it compat with SQL config
                }
                else {
                    Write-Warning "Read-only permissions could not be granted to certificate"
                    return
                }

                if (![System.String]::IsNullOrEmpty($oldthumbprint)) {
                    $notes = "Granted $serviceaccount read access to certificate private key. Replaced thumbprint: $oldthumbprint."
                }
                else {
                    $notes = "Granted $serviceaccount read access to certificate private key"
                }

                $newthumbprint = (Get-ItemProperty -Path $regpath -Name Certificate).Certificate

                [pscustomobject]@{
                    ComputerName          = $env:COMPUTERNAME
                    InstanceName          = $instancename
                    SqlInstance           = $vsname
                    ServiceAccount        = $serviceaccount
                    CertificateThumbprint = $newthumbprint
                    Notes                 = $notes
                }
            }

            if ($PScmdlet.ShouldProcess("local", "Connecting to $instanceName to import new cert")) {
                try {
                    Invoke-Command2 -Raw -ComputerName $resolved.fqdn -Credential $Credential -ArgumentList $regroot, $serviceaccount, $instancename, $vsname, $Thumbprint -ScriptBlock $scriptblock -ErrorAction Stop
                }
                catch {
                    Stop-Function -Message "Failed to connect to $($resolved.fqdn) using PowerShell remoting!" -ErrorRecord $_ -Target $instance -Continue
                }
            }
        }
    }
}
function Set-DbaPowerPlan {
    <#
        .SYNOPSIS
            Sets the SQL Server OS's Power Plan.

        .DESCRIPTION
            Sets the SQL Server OS's Power Plan. Defaults to High Performance which is best practice.

            If your organization uses a custom power plan that is considered best practice, specify -CustomPowerPlan.

            References:
            https://support.microsoft.com/en-us/kb/2207548
            http://www.sqlskills.com/blogs/glenn/windows-power-plan-effects-on-newer-intel-processors/

        .PARAMETER ComputerName
            The server(s) to set the Power Plan on.

        .PARAMETER PowerPlan
            Specifies the Power Plan that you wish to use. Valid options for this match the Windows default Power Plans of "Power Saver", "Balanced", and "High Performance".

        .PARAMETER CustomPowerPlan
            Specifies the name of a custom Power Plan to use.


        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .NOTES
            Tags: PowerPlan, OS, Configure
            Requires: WMI access to servers

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Set-DbaPowerPlan

        .EXAMPLE
            Set-DbaPowerPlan -ComputerName sqlserver2014a

            Sets the Power Plan to High Performance. Skips it if its already set.

        .EXAMPLE
            Set-DbaPowerPlan -ComputerName sqlcluster -CustomPowerPlan 'Maximum Performance'

            Sets the Power Plan to the custom power plan called "Maximum Performance". Skips it if its already set.

    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer", "SqlInstance")]
        [object[]]$ComputerName,
        [ValidateSet('High Performance', 'Balanced', 'Power saver')]
        [string]$PowerPlan = 'High Performance',
        [string]$CustomPowerPlan,
        [switch][Alias('Silent')]
        $EnableException
    )

    begin {
        if ($CustomPowerPlan.Length -gt 0) {
            $PowerPlan = $CustomPowerPlan
        }

        function Set-DbaPowerPlanInternal {
            param($server)

            try {
                Write-Message -Level Verbose -Message "Testing connection to $server and resolving IP address."
                $ipaddr = (Test-Connection $server -Count 1 -ErrorAction SilentlyContinue).Ipv4Address | Select-Object -First 1

            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $server
                return
            }

            try {
                Write-Message -Level Verbose -Message "Getting Power Plan information from $server."
                $query = "Select ElementName from Win32_PowerPlan WHERE IsActive = 'true'"
                $currentplan = Get-WmiObject -Namespace Root\CIMV2\Power -ComputerName $ipaddr -Query $query -ErrorAction SilentlyContinue
                $currentplan = $currentplan.ElementName
            }
            catch {
                Stop-Function -Message "Can't connect to WMI on $server." -Category ConnectionError -ErrorRecord $_ -Target $server
                return
            }

            if ($null -eq $currentplan) {
                # the try/catch above isn't working, so make it silent and handle it here.
                Stop-Function -Message "Cannot get Power Plan for $server." -Category ConnectionError -ErrorRecord $_ -Target $server
                return
            }

            $planinfo = [PSCustomObject]@{
                Server            = $server
                PreviousPowerPlan = $currentplan
                ActivePowerPlan   = $PowerPlan
            }

            if ($PowerPlan -ne $currentplan) {
                if ($Pscmdlet.ShouldProcess($server, "Changing Power Plan from $CurrentPlan to $PowerPlan")) {
                    try {
                        Write-Message -Level Verbose -Message "Setting Power Plan to $PowerPlan."
                        $null = (Get-WmiObject -Name root\cimv2\power -ComputerName $ipaddr -Class Win32_PowerPlan -Filter "ElementName='$PowerPlan'").Activate()
                    }
                    catch {
                        Stop-Function -Message "Couldn't set Power Plan on $server." -Category ConnectionError -ErrorRecord $_ -Target $server
                        return
                    }
                }
            }
            else {
                if ($Pscmdlet.ShouldProcess($server, "Stating power plan is already set to $PowerPlan, won't change.")) {
                    Write-Message -Level Verbose -Message "PowerPlan on $server is already set to $PowerPlan. Skipping."
                }
            }

            return $planinfo
        }


        $collection = New-Object System.Collections.ArrayList
        $processed = New-Object System.Collections.ArrayList
    }

    process {
        foreach ($server in $ComputerName) {
            if ($server -match 'Server\=') {
                Write-Message -Level Verbose -Message "Matched that value was piped from Test-DbaPowerPlan."
                # I couldn't properly unwrap the output from  Test-DbaPowerPlan so here goes.
                $lol = $server.Split("\;")[0]
                $lol = $lol.TrimEnd("\}")
                $lol = $lol.TrimStart("\@\{Server")
                # There was some kind of parsing bug here, don't clown
                $server = $lol.TrimStart("\=")
            }

            if ($server -match '\\') {
                $server = $server.Split('\\')[0]
            }

            if ($server -notin $processed) {
                $null = $processed.Add($server)
                Write-Message -Level Verbose -Message "Connecting to $server."
            }
            else {
                continue
            }

            $data = Set-DbaPowerPlanInternal $server

            if ($data.Count -gt 1) {
                $data.GetEnumerator() | ForEach-Object { $null = $collection.Add($_) }
            }
            else {
                $null = $collection.Add($data)
            }
        }
    }

    end {
        If ($Pscmdlet.ShouldProcess("console", "Showing results")) {
            return $collection
        }
    }
}
function Set-DbaPrivilege {
    <#
      .SYNOPSIS
      Adds the SQL Service account to local privileges on one or more computers.

      .DESCRIPTION
      Adds the SQL Service account to local privileges 'Lock Pages in Memory', 'Instant File Initialization', 'Logon as Batch' on one or more computers.

      Requires Local Admin rights on destination computer(s).

      .PARAMETER ComputerName
      The SQL Server (or server in general) that you're connecting to. This command handles named instances.

      .PARAMETER Credential
      Credential object used to connect to the computer as a different user.

      .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

      .PARAMETER Type
      Use this to choose the privilege(s) to which you want to add the SQL Service account.
      Accepts 'IFI', 'LPIM' and/or 'BatchLogon' for local privileges 'Instant File Initialization', 'Lock Pages in Memory' and 'Logon as Batch'.

      .NOTES
      Author: Klaas Vandenberghe ( @PowerDBAKlaas )
      Tags: Privilege
      Website: https://dbatools.io
      Copyright: (C) Chrissy LeMaire, [email protected]
      License: MIT https://opensource.org/licenses/MIT

    .LINK
      https://dbatools.io/Set-DbaPrivilege

      .EXAMPLE
      Set-DbaPrivilege -ComputerName sqlserver2014a -Type LPIM,IFI

      Adds the SQL Service account(s) on computer sqlserver2014a to the local privileges 'SeManageVolumePrivilege' and 'SeLockMemoryPrivilege'.

      .EXAMPLE
      'sql1','sql2','sql3' | Set-DbaPrivilege -Type IFI

      Adds the SQL Service account(s) on computers sql1, sql2 and sql3 to the local privilege 'SeManageVolumePrivilege'.

  #>
    [CmdletBinding()]
    Param (
        [parameter(ValueFromPipeline)]
        [Alias("cn", "host", "Server")]
        [dbainstanceparameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Parameter(Mandatory = $true)]
        [ValidateSet('IFI', 'LPIM', 'BatchLogon')]
        [string[]]$Type,
        [switch][Alias('Silent')]
        $EnableException
    )
    
    begin {
        $ResolveAccountToSID = @"
function Convert-UserNameToSID ([string] `$Acc ) {
`$objUser = New-Object System.Security.Principal.NTAccount(`"`$Acc`")
`$strSID = `$objUser.Translate([System.Security.Principal.SecurityIdentifier])
`$strSID.Value
}
"@
        $ComputerName = $ComputerName.ComputerName | Select-Object -Unique
    }
    process {
        foreach ($computer in $ComputerName) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $computer"
                $null = Test-ElevationRequirement -ComputerName $Computer -Continue
                if (Test-PSRemoting -ComputerName $Computer) {
                    Write-Message -Level Verbose -Message "Exporting Privileges on $Computer"
                    Invoke-Command2 -Raw -ComputerName $computer -Credential $Credential -ScriptBlock {
                        $temp = ([System.IO.Path]::GetTempPath()).TrimEnd(""); secedit /export /cfg $temp\secpolByDbatools.cfg > $NULL;
                    }
                    Write-Message -Level Verbose -Message "Getting SQL Service Accounts on $computer"
                    $SQLServiceAccounts = (Get-DbaSqlService -ComputerName $computer -Type Engine).StartName
                    if ($SQLServiceAccounts.count -ge 1) {
                        Write-Message -Level Verbose -Message "Setting Privileges on $Computer"
                        Invoke-Command2 -Raw -ComputerName $computer -Credential $Credential -Verbose -ArgumentList $ResolveAccountToSID, $SQLServiceAccounts, $BatchLogon, $IFI, $LPIM -ScriptBlock {
                            [CmdletBinding()]
                            Param ($ResolveAccountToSID,
                                $SQLServiceAccounts,
                                $BatchLogon,
                                $IFI,
                                $LPIM)
                            . ([ScriptBlock]::Create($ResolveAccountToSID))
                            $temp = ([System.IO.Path]::GetTempPath()).TrimEnd("");
                            $tempfile = "$temp\secpolByDbatools.cfg"
                            if ('BatchLogon' -in $Type) {
                                $BLline = Get-Content $tempfile | Where-Object { $_ -match "SeBatchLogonRight" }
                                ForEach ($acc in $SQLServiceAccounts) {
                                    $SID = Convert-UserNameToSID -Acc $acc;
                                    if ($BLline -notmatch $SID) {
                                        (Get-Content $tempfile) -replace "SeBatchLogonRight = ", "SeBatchLogonRight = *$SID," |
                                        Set-Content $tempfile
                                        Write-Verbose "Added $acc to Batch Logon Privileges on $env:ComputerName"
                                    }
                                    else {
                                        Write-Warning "$acc already has Batch Logon Privilege on $env:ComputerName"
                                    }
                                }
                            }
                            if ('IFI' -in $Type) {
                                $IFIline = Get-Content $tempfile | Where-Object { $_ -match "SeManageVolumePrivilege" }
                                ForEach ($acc in $SQLServiceAccounts) {
                                    $SID = Convert-UserNameToSID -Acc $acc;
                                    if ($IFIline -notmatch $SID) {
                                        (Get-Content $tempfile) -replace "SeManageVolumePrivilege = ", "SeManageVolumePrivilege = *$SID," |
                                        Set-Content $tempfile
                                        Write-Verbose "Added $acc to Instant File Initialization Privileges on $env:ComputerName"
                                    }
                                    else {
                                        Write-Warning "$acc already has Instant File Initialization Privilege on $env:ComputerName"
                                    }
                                }
                            }
                            if ('LPIM' -in $Type) {
                                $LPIMline = Get-Content $tempfile | Where-Object { $_ -match "SeLockMemoryPrivilege" }
                                ForEach ($acc in $SQLServiceAccounts) {
                                    $SID = Convert-UserNameToSID -Acc $acc;
                                    if ($LPIMline -notmatch $SID) {
                                        (Get-Content $tempfile) -replace "SeLockMemoryPrivilege = ", "SeLockMemoryPrivilege = *$SID," |
                                        Set-Content $tempfile
                                        Write-Verbose "Added $acc to Lock Pages in Memory Privileges on $env:ComputerName"
                                    }
                                    else {
                                        Write-Warning "$acc already has Lock Pages in Memory Privilege on $env:ComputerName"
                                    }
                                }
                            }
                            $null = secedit /configure /cfg $tempfile /db secedit.sdb /areas USER_RIGHTS /overwrite /quiet
                        } -ErrorAction SilentlyContinue
                        Write-Message -Level Verbose -Message "Removing secpol file on $computer"
                        Invoke-Command2 -Raw -ComputerName $computer -Credential $Credential -ScriptBlock { $temp = ([System.IO.Path]::GetTempPath()).TrimEnd(""); Remove-Item $temp\secpolByDbatools.cfg -Force > $NULL }
                    }
                    else {
                        Write-Message -Level Warning -Message "No SQL Service Accounts found on $Computer"
                    }
                }
                else {
                    Write-Message -Level Warning -Message "Failed to connect to $Computer"
                }
            }
            catch {
                Stop-Function -Continue -Message "Failure" -ErrorRecord $_ -Target $computer -Continue
            }
        }
    }
}
function Set-DbaSpConfigure {
    <#
        .SYNOPSIS
            Changes the server level system configuration (sys.configuration/sp_configure) value for a given configuration

        .DESCRIPTION
            This function changes the configured value for sp_configure settings. If the setting is dynamic this setting will be used, otherwise the user will be warned that a restart of SQL is required.
            This is designed to be safe and will not allow for configurations to be set outside of the defined configuration min and max values.
            While it is possible to set below the min, or above the max this can cause serious problems with SQL Server (including startup failures), and so is not permitted.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a
            collection and receive pipeline input

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Name
            The name of the configuration to be set -- Configs is auto-populated for tabbing convenience.

        .PARAMETER Value
            The new value for the configuration

        .PARAMETER InputObject
            Piped objectgs from Get-DbaSpConfigure
    
        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .NOTES
            Tags: SpConfigure
            Author: Nic Cain, https://sirsql.net/

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Set-DbaSpConfigure

        .EXAMPLE
            Set-DbaSpConfigure -SqlInstance localhost -Name ScanForStartupProcedures -Value 1

            Adjusts the Scan for startup stored procedures configuration value to 1 and notifies the user that this requires a SQL restart to take effect

        .EXAMPLE
            Get-DbaSpConfigure -SqlInstance sql2017, sql2014 -Name XPCmdShellEnabled, IsSqlClrEnabled | Set-DbaSpConfigure -Value $false
            Sets the values for XPCmdShellEnabled and IsSqlClrEnabled on sql2017 and sql2014 to False
    
        .EXAMPLE
            Set-DbaSpConfigure -SqlInstance localhost -Name XPCmdShellEnabled -Value 1

            Adjusts the xp_cmdshell configuration value to 1.

        .EXAMPLE
            Set-DbaSpConfigure -SqlInstance localhost -Name XPCmdShellEnabled -Value 1 -WhatIf

            Returns information on the action that would be performed. No actual change will be made.
        #>
    [CmdletBinding(SupportsShouldProcess)]
    Param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [System.Management.Automation.PSCredential]$SqlCredential,
        [Alias("NewValue", "NewConfig")]
        [int]$Value,
        [Alias("Config", "ConfigName")]
        [string[]]$Name,
        [parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [switch][Alias('Silent')]
        $EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            $InputObject += Get-DbaSpConfigure -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Name $Name
        }
        
        foreach ($configobject in $InputObject) {
            $server = $InputObject.Parent
            $currentRunValue = $configobject.RunningValue
            $minValue = $configobject.MinValue
            $maxValue = $configobject.MaxValue
            $isDynamic = $configobject.IsDynamic
            $configuration = $configobject.Name
            
            #Let us not waste energy setting the value to itself
            if ($currentRunValue -eq $value) {
                Stop-Function -Message "Value to set is the same as the existing value. No work being performed." -Continue -Target $server -Category InvalidData
            }
            
            #Going outside the min/max boundary can be done, but it can break SQL, so I don't think allowing that is wise at this juncture
            if ($value -lt $minValue -or $value -gt $maxValue) {
                Stop-Function -Message "Value out of range for $configuration ($minValue <-> $maxValue)" -Continue -Category InvalidArgument
            }
            
            If ($Pscmdlet.ShouldProcess($SqlInstance, "Adjusting server configuration $configuration from $currentRunValue to $value.")) {
                try {
                    $server.Configuration.$configuration.ConfigValue = $value
                    $server.Configuration.Alter()
                    
                    [pscustomobject]@{
                        ComputerName           = $server.ComputerName
                        InstanceName           = $server.ServiceName
                        SqlInstance            = $server.DomainInstanceName
                        ConfigName             = $configuration
                        OldValue               = $currentRunValue
                        NewValue               = $value
                    }
                    
                    #If it's a dynamic setting we're all clear, otherwise let the user know that SQL needs to be restarted for the change to take
                    if ($isDynamic -eq $false) {
                        Write-Message -Level Warning -Message "Configuration setting $configuration has been set, but restart of SQL Server is required for the new value `"$value`" to be used (old value: `"$currentRunValue`")" -Target $Instance
                    }
                }
                catch {
                    Stop-Function -Message "Unable to change config setting" -Target $Instance -ErrorRecord $_ -Continue -ContinueLabel main
                }
            }
        }
    }
}
#ValidationTags#FlowControl,Pipeline#
function Set-DbaSpn {
    <#
.SYNOPSIS
Sets an SPN for a given service account in active directory (and also enables delegation to the same SPN by default)

.DESCRIPTION
This function will connect to Active Directory and search for an account. If the account is found, it will attempt to add an SPN. Once the SPN
is added, the function will also set delegation to that service, unless -NoDelegation is specified. In order to run this function, the credential you provide must have write
access to Active Directory.

Note: This function supports -WhatIf

.PARAMETER SPN
The SPN you want to add

.PARAMETER ServiceAccount
The account you want the SPN added to

.PARAMETER Credential
The credential you want to use to connect to Active Directory to make the changes

.PARAMETER NoDelegation
Skips setting the delegation

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.PARAMETER Confirm
Turns confirmations before changes on or off

.PARAMETER WhatIf
Shows what would happen if the command was executed

.NOTES
Tags: SPN
Author: Drew Furgiuele (@pittfurg), http://www.port1433.com

dbatools PowerShell module (https://dbatools.io)
Copyright (C) 2016 Chrissy LeMaire
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Set-DbaSpn

.EXAMPLE
Set-DbaSpn -SPN MSSQLSvc\SQLSERVERA.domain.something -ServiceAccount domain\account

Connects to Active Directory and adds a provided SPN to the given account.

Set-DbaSpn -SPN MSSQLSvc\SQLSERVERA.domain.something -ServiceAccount domain\account -EnableException

Connects to Active Directory and adds a provided SPN to the given account, suppressing all error messages and throw exceptions that can be caught instead

.EXAMPLE
Set-DbaSpn -SPN MSSQLSvc\SQLSERVERA.domain.something -ServiceAccount domain\account -Credential (Get-Credential)

Connects to Active Directory and adds a provided SPN to the given account. Uses alternative account to connect to AD.

.EXAMPLE
Set-DbaSpn -SPN MSSQLSvc\SQLSERVERA.domain.something -ServiceAccount domain\account -NoDelegation

Connects to Active Directory and adds a provided SPN to the given account, without the delegation.

.EXAMPLE
Test-DbaSpn -ComputerName sql2016 | Where { $_.isSet -eq $false } | Set-DbaSpn

Sets all missing SPNs for sql2016

.EXAMPLE
Test-DbaSpn -ComputerName sql2016 | Where { $_.isSet -eq $false } | Set-DbaSpn -WhatIf

Displays what would happen trying to set all missing SPNs for sql2016

#>
    [cmdletbinding(SupportsShouldProcess = $true, DefaultParameterSetName = "Default")]
    param (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName)]
        [Alias("RequiredSPN")]
        [string]$SPN,
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName)]
        [Alias("InstanceServiceAccount", "AccountName")]
        [string]$ServiceAccount,
        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName)]
        [PSCredential]$Credential,
        [switch]$NoDelegation,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        #did we find the server account?
        Write-Message -Message "Looking for account $ServiceAccount..." -Level Verbose
        $searchfor = 'User'
        if ($ServiceAccount.EndsWith('$')) {
            $searchfor = 'Computer'
        }
        try {
            $Result = Get-DbaADObject -ADObject $ServiceAccount -Type $searchfor -Credential $Credential -EnableException
        }
        catch {
            Stop-Function -Message "AD lookup failure. This may be because the domain cannot be resolved for the SQL Server service account ($ServiceAccount). $($_.Exception.Message)" -EnableException $EnableException -InnerErrorRecord $_ -Target $ServiceAccount
        }
        if ($Result.Count -gt 0) {
            try {
                $adentry = $Result.GetUnderlyingObject()
            }
            catch {
                Stop-Function -Message "The SQL Service account ($ServiceAccount) has been found, but you don't have enough permission to inspect its properties $($_.Exception.Message)" -EnableException $EnableException -InnerErrorRecord $_ -Target $ServiceAccount
            }
        }
        else {
            Stop-Function -Message "The SQL Service account ($ServiceAccount) has not been found" -EnableException $EnableException -Target $ServiceAccount
        }
        # Cool! Add an SPN
        $delegate = $true
        if ($PSCmdlet.ShouldProcess("$spn", "Adding SPN to service account")) {
            try {
                $null = $adentry.Properties['serviceprincipalname'].Add($spn)
                $status = "Successfully added SPN"
                $adentry.CommitChanges()
                Write-Message -Message "Added SPN $spn to $ServiceAccount" -Level Verbose
                $set = $true
            }
            catch {
                Write-Message -Message "Could not add SPN. $($_.Exception.Message)" -Level Warning -EnableException $EnableException.ToBool() -ErrorRecord $_ -Target $ServiceAccount
                $set = $false
                $status = "Failed to add SPN"
                $delegate = $false
            }

            [pscustomobject]@{
                Name           = $spn
                ServiceAccount = $ServiceAccount
                Property       = "servicePrincipalName"
                IsSet          = $set
                Notes          = $status
            }
        }

        #if we have the SPN set, we can add the delegation
        if ($delegate) {
            # but only if $NoDelegation is not passed
            if (!$NoDelegation) {
                if ($PSCmdlet.ShouldProcess("$spn", "Adding constrained delegation to service account for SPN")) {
                    try {
                        $null = $adentry.Properties['msDS-AllowedToDelegateTo'].Add($spn)
                        $adentry.CommitChanges()
                        Write-Message -Message "Added kerberos delegation to $spn for $ServiceAccount" -Level Verbose
                        $set = $true
                        $status = "Successfully added constrained delegation"
                    }
                    catch {
                        Write-Message -Message "Could not add delegation. $($_.Exception.Message)" -Level Warning -EnableException $EnableException.ToBool() -ErrorRecord $_ -Target $ServiceAccount
                        $set = $false
                        $status = "Failed to add constrained delegation"
                    }

                    [pscustomobject]@{
                        Name           = $spn
                        ServiceAccount = $ServiceAccount
                        Property       = "msDS-AllowedToDelegateTo"
                        IsSet          = $set
                        Notes          = $status
                    }
                }
            }
            else {
                Write-Message -Message "Skipping delegation as instructed" -Level Verbose
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Set-DbaStartupParameter {
    <#
        .SYNOPSIS
            Sets the Startup Parameters for a SQL Server instance

        .DESCRIPTION
            Modifies the startup parameters for a specified SQL Server Instance

            For full details of what each parameter does, please refer to this MSDN article - https://msdn.microsoft.com/en-us/library/ms190737(v=sql.105).aspx

        .PARAMETER SqlInstance
            The SQL Server instance to be modified

            If the Sql Instance is offline path parameters will be ignored as we cannot test the instance's access to the path. If you want to force this to work then please use the Force switch

        .PARAMETER SqlCredential
            Windows or Sql Login Credential with permission to log into the SQL instance

        .PARAMETER Credential
            Windows Credential with permission to log on to the server running the SQL instance

        .PARAMETER MasterData
            Path to the data file for the Master database

            Will be ignored if SqlInstance is offline or the Offline switch is set. To override this behaviour use the Force switch. This is to ensure you understand the risk as we cannot validate the path if the instance is offline

        .PARAMETER MasterLog
            Path to the log file for the Master database

            Will be ignored if SqlInstance is offline or the Offline switch is set. To override this behaviour use the Force switch. This is to ensure you understand the risk as we cannot validate the path if the instance is offline

        .PARAMETER ErrorLog
            Path to the SQL Server error log file

            Will be ignored if SqlInstance is offline or the Offline switch is set. To override this behaviour use the Force switch. This is to ensure you understand the risk as we cannot validate the path if the instance is offline

        .PARAMETER TraceFlags
            A comma separated list of TraceFlags to be applied at SQL Server startup
            By default these will be appended to any existing trace flags set

        .PARAMETER CommandPromptStart
            Shortens startup time when starting SQL Server from the command prompt. Typically, the SQL Server Database Engine starts as a service by calling the Service Control Manager.
            Because the SQL Server Database Engine does not start as a service when starting from the command prompt

        .PARAMETER MinimalStart
            Starts an instance of SQL Server with minimal configuration. This is useful if the setting of a configuration value (for example, over-committing memory) has
            prevented the server from starting. Starting SQL Server in minimal configuration mode places SQL Server in single-user mode

        .PARAMETER MemoryToReserve
            Specifies an integer number of megabytes (MB) of memory that SQL Server will leave available for memory allocations within the SQL Server process,
            but outside the SQL Server memory pool. The memory outside of the memory pool is the area used by SQL Server for loading items such as extended procedure .dll files,
            the OLE DB providers referenced by distributed queries, and automation objects referenced in Transact-SQL statements. The default is 256 MB.

        .PARAMETER SingleUser
            Start Sql Server in single user mode

        .PARAMETER NoLoggingToWinEvents
            Don't use Windows Application events log

        .PARAMETER StartAsNamedInstance
            Allows you to start a named instance of SQL Server

        .PARAMETER DisableMonitoring
            Disables the following monitoring features:

            SQL Server performance monitor counters
            Keeping CPU time and cache-hit ratio statistics
            Collecting information for the DBCC SQLPERF command
            Collecting information for some dynamic management views
            Many extended-events event points

            ** Warning *\* When you use the -x startup option, the information that is available for you to diagnose performance and functional problems with SQL Server is greatly reduced.

        .PARAMETER SingleUserDetails
            The username for single user

        .PARAMETER IncreasedExtents
            Increases the number of extents that are allocated for each file in a filegroup.

        .PARAMETER TraceFlagsOverride
            Overrides the default behaviour and replaces any existing trace flags. If not trace flags specified will just remove existing ones

        .PARAMETER StartUpConfig
            Pass in a previously saved SQL Instance startup config
            using this parameter will set TraceFlagsOverride to true, so existing Trace Flags will be overridden

        .PARAMETER Offline
            Setting this switch will try perform the requested actions without connect to the SQL Server Instance, this will speed things up if you know the Instance is offline.

            When working offline, path inputs (MasterData, MasterLog and ErrorLog) will be ignored, unless Force is specified

        .PARAMETER Force
            By default we test the values passed in via MasterData, MasterLog, ErrorLog

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Service, Startup, Parameter, Configure
            Author: Stuart Moore (@napalmgram), stuart-moore.com

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            Set-DbaStartupParameter -SqlInstance server1\instance1 -SingleUser

            Will configure the SQL Instance server1\instance1 to startup up in Single User mode at next startup

        .EXAMPLE
            Set-DbaStartupParameter -SqlInstance sql2016 -IncreasedExtents

            Will configure the SQL Instance sql2016 to IncreasedExtents = True (-E)

        .EXAMPLE
            Set-DbaStartupParameter -SqlInstance sql2016  -IncreasedExtents:$false -WhatIf

            Shows what would happen if you attempted to configure the SQL Instance sql2016 to IncreasedExtents = False (no -E)

        .EXAMPLE
            Set-DbaStartupParameter -SqlInstance server1\instance1 -SingleUser -TraceFlags 8032,8048

            This will append Trace Flags 8032 and 8048 to the startup parameters

        .EXAMPLE
            Set-DbaStartupParameter -SqlInstance sql2016 -SingleUser:$false -TraceFlagsOverride

            This will remove all trace flags and set SinguleUser to false

        .EXAMPLE
            Set-DbaStartupParameter -SqlInstance server1\instance1 -SingleUser -TraceFlags 8032,8048 -TraceFlagsOverride

            This will set Trace Flags 8032 and 8048 to the startup parameters, removing any existing Trace Flags

        .EXAMPLE
            Set-DbaStartupParameter -SqlInstance sql2016 -SingleUser:$false -TraceFlagsOverride -Offline

            This will remove all trace flags and set SinguleUser to false from an offline instance

        .EXAMPLE
            Set-DbaStartupParameter -SqlInstance sql2016 -ErrorLog c:\Sql\ -Offline

            This will attempt to change the ErrorLog path to c:\sql\. However, with the offline switch this will not happen. To force it, use the -Force switch like so:

            Set-DbaStartupParameter -SqlInstance sql2016 -ErrorLog c:\Sql\ -Offline -Force

        .EXAMPLE
            $StartupConfig = Get-DbaStartupParameter -SqlInstance server1\instance1
            Set-DbaStartupParameter -SqlInstance server1\instance1 -SingleUser -NoLoggingToWinEvents
            #Restart your SQL instance with the tool of choice
            #Do Some work
            Set-DbaStartupParameter -SqlInstance server1\instance1 -StartUpConfig $StartUpConfig
            #Restart your SQL instance with the tool of choice and you're back to normal

            In this example we take a copy of the existing startup configuration of server1\instance1

            We then change the startup parameters ahead of some work

            After the work has been completed, we can push the original startup parameters back to server1\instance1 and resume normal operation
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High")]
    param ([parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [PSCredential]$Credential,
        [string]$MasterData,
        [string]$MasterLog,
        [string]$ErrorLog,
        [string[]]$TraceFlags,
        [switch]$CommandPromptStart,
        [switch]$MinimalStart,
        [int]$MemoryToReserve,
        [switch]$SingleUser,
        [string]$SingleUserDetails,
        [switch]$NoLoggingToWinEvents,
        [switch]$StartAsNamedInstance,
        [switch]$DisableMonitoring,
        [switch]$IncreasedExtents,
        [switch]$TraceFlagsOverride,
        [object]$StartUpConfig,
        [switch]$Offline,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {

        if (-not $Offline) {
            try {
                Write-Message -Level VeryVerbose -Message "Connecting to $SqlInstance" -Target $SqlInstance
                $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
            }
            catch {
                Write-Message -Level Warning -Message "Failed to connect to $SqlInstance, will try to work with just WMI. Path options will be ignored unless Force was indicated"
                $Server = $SqlInstance
                $Offline = $true
            }
        }
        else {
            Write-Message -Level Verbose -Message "Offline switch set, proceeding with just WMI"
            $Server = $SqlInstance
        }

        #Get Current parameters:
        $currentstartup = Get-DbaStartupParameter -SqlInstance $server -Credential $Credential
        $originalparamstring = $currentstartup.ParameterString

        Write-Message -Level Output -Message "Original startup parameter string: $originalparamstring"

        if ('startUpconfig' -in $PsBoundParameters.keys) {
            Write-Message -Level VeryVerbose -Message "StartupObject passed in"
            $newstartup = $StartUpConfig
            $TraceFlagsOverride = $true
        }
        else {
            Write-Message -Level VeryVerbose -Message "Parameters passed in"
            $newstartup = $currentstartup.PSObject.copy()
            foreach ($param in ($PsBoundParameters.keys | Where-Object { $_ -in ($newstartup.PSObject.Properties.name) })) {
                if ($PsBoundParameters.item($param) -ne $newstartup.$param) {
                    $newstartup.$param = $PsBoundParameters.item($param)
                }
            }
        }

        if (!($currentstartup.SingleUser)) {

            if ($newstartup.Masterdata.length -gt 0) {
                if ($Offline -and -not $Force) {
                    Write-Message -Level Warning -Message "Working offline, skipping untested MasterData path"
                    $ParameterString += "-d$($CurrentStartup.MasterData);"

                }
                else {
                    if ($Force) {
                        $ParameterString += "-d$($newstartup.MasterData);"
                    }
                    elseif (Test-DbaSqlPath -SqlInstance $server -SqlCredential $SqlCredential -Path (Split-Path $newstartup.MasterData -Parent)) {
                        $ParameterString += "-d$($newstartup.MasterData);"
                    }
                    else {
                        Stop-Function -Message "Specified folder for Master Data file is not reachable by instance $SqlInstance"
                        return
                    }
                }
            }
            else {
                Stop-Function -Message "MasterData value must be provided"
                return
            }

            if ($newstartup.ErrorLog.length -gt 0) {
                if ($Offline -and -not $Force) {
                    Write-Message -Level Warning -Message "Working offline, skipping untested ErrorLog path"
                    $ParameterString += "-e$($CurrentStartup.ErrorLog);"
                }
                else {
                    if ($Force) {
                        $ParameterString += "-e$($newstartup.ErrorLog);"
                    }
                    elseif (Test-DbaSqlPath -SqlInstance $server -SqlCredential $SqlCredential -Path (Split-Path $newstartup.ErrorLog -Parent)) {
                        $ParameterString += "-e$($newstartup.ErrorLog);"
                    }
                    else {
                        Stop-Function -Message "Specified folder for ErrorLog  file is not reachable by $SqlInstance"
                        return
                    }
                }
            }
            else {
                Stop-Function -Message "ErrorLog value must be provided"
                return
            }

            if ($newstartup.MasterLog.Length -gt 0) {
                if ($offline -and -not $Force) {
                    Write-Message -Level Warning -Message "Working offline, skipping untested MasterLog path"
                    $ParameterString += "-l$($CurrentStartup.MasterLog);"
                }
                else {
                    if ($Force) {
                        $ParameterString += "-l$($newstartup.MasterLog);"
                    }
                    elseif (Test-DbaSqlPath -SqlInstance $server -SqlCredential $SqlCredential -Path (Split-Path $newstartup.MasterLog -Parent)) {
                        $ParameterString += "-l$($newstartup.MasterLog);"
                    }
                    else {
                        Stop-Function -Message "Specified folder for Master Log  file is not reachable by $SqlInstance"
                        return
                    }
                }
            }
            else {
                Stop-Function -Message "MasterLog value must be provided."
                return
            }
        }
        else {

            Write-Message -Level Verbose -Message "Sql instance is presently configured for single user, skipping path validation"
            if ($newstartup.MasterData.Length -gt 0) {
                $ParameterString += "-d$($newstartup.MasterData);"
            }
            else {
                Stop-Function -Message "Must have a value for MasterData"
                return
            }
            if ($newstartup.ErrorLog.Length -gt 0) {
                $ParameterString += "-e$($newstartup.ErrorLog);"
            }
            else {
                Stop-Function -Message "Must have a value for Errorlog"
                return
            }
            if ($newstartup.MasterLog.Length -gt 0) {
                $ParameterString += "-l$($newstartup.MasterLog);"
            }
            else {
                Stop-Function -Message "Must have a value for MsterLog"
                return
            }
        }

        if ($newstartup.CommandPromptStart) {
            $ParameterString += "-c;"
        }
        if ($newstartup.MinimalStart) {
            $ParameterString += "-f;"
        }
        if ($newstartup.MemoryToReserve -notin ($null, 0)) {
            $ParameterString += "-g$($newstartup.MemoryToReserve)"
        }
        if ($newstartup.SingleUser) {
            if ($SingleUserDetails.length -gt 0) {
                if ($SingleUserDetails -match ' ') {
                    $SingleUserDetails = """$SingleUserDetails"""
                }
                $ParameterString += "-m$SingleUserDetails;"
            }
            else {
                $ParameterString += "-m;"
            }
        }
        if ($newstartup.NoLoggingToWinEvents) {
            $ParameterString += "-n;"
        }
        If ($newstartup.StartAsNamedInstance) {
            $ParameterString += "-s;"
        }
        if ($newstartup.DisableMonitoring) {
            $ParameterString += "-x;"
        }
        if ($newstartup.IncreasedExtents) {
            $ParameterString += "-E;"
        }
        if ($newstartup.TraceFlags -eq 'None') {
            $newstartup.TraceFlags = ''
        }
        if ($TraceFlagsOverride -and 'TraceFlags' -in $PsBoundParameters.keys) {
            if ($null -ne $TraceFlags -and '' -ne $TraceFlags) {
                $newstartup.TraceFlags = $TraceFlags -join ','
                $ParameterString += (($TraceFlags.split(',') | ForEach-Object { "-T$_" }) -join ';') + ";"
            }
        }
        else {
            if ('TraceFlags' -in $PsBoundParameters.keys) {
                if ($null -eq $TraceFlags) { $TraceFlags = '' }
                $oldflags = @($currentstartup.TraceFlags) -split ',' | Where-Object { $_ -ne 'None' }
                $newflags = $TraceFlags
                $newstartup.TraceFlags = (@($oldFlags) + @($newflags) | Sort-Object -Unique) -join ','
            }
            elseif ($TraceFlagsOverride) {
                $newstartup.TraceFlags = ''
            }
            else {
                $newstartup.TraceFlags = if ($currentstartup.TraceFlags -eq 'None') { }
                else { $currentstartup.TraceFlags -join ',' }
            }
            If ($newstartup.TraceFlags.Length -ne 0) {
                $ParameterString += (($newstartup.TraceFlags.split(',') | ForEach-Object { "-T$_" }) -join ';') + ";"
            }
        }

        $instance = $SqlInstance.ComputerName
        $instancename = $SqlInstance.InstanceName
        Write-Message -Level Verbose -Message "Connecting to $instancename on $instance"

        if ($instancename.Length -eq 0) { $instancename = "MSSQLSERVER" }

        $displayname = "SQL Server ($instancename)"

        if ($originalparamstring -eq "$ParameterString" -or "$originalparamstring;" -eq "$ParameterString") {
            Stop-Function -Message "New parameter string would be the same as the old parameter string. Nothing to do." -Target $ParameterString
            return
        }

        $Scriptblock = {
            $instance = $args[0]
            $displayname = $args[1]
            $ParameterString = $args[2]

            $wmisvc = $wmi.Services | Where-Object { $_.DisplayName -eq $displayname }
            $wmisvc.StartupParameters = $ParameterString
            $wmisvc.Alter()
            $wmisvc.Refresh()
            if ($wmisvc.StartupParameters -eq $ParameterString) {
                $true
            }
            else {
                $false
            }
        }

        if ($pscmdlet.ShouldProcess("Setting Sql Server start parameters on $SqlInstance to $ParameterString")) {
            try {
                if ($Credential) {
                    $response = Invoke-ManagedComputerCommand -ComputerName $instance -Credential $Credential -ScriptBlock $Scriptblock -ArgumentList $instance, $displayname, $ParameterString -EnableException
                    $output = Get-DbaStartupParameter -SqlInstance $server -Credential $Credential -EnableException
                    Add-Member -Force -InputObject $output -MemberType NoteProperty -Name OriginalStartupParameters -Value $originalparamstring
                }
                else {
                    $response = Invoke-ManagedComputerCommand -ComputerName $instance -ScriptBlock $Scriptblock -ArgumentList $instance, $displayname, $ParameterString -EnableException
                    $output = Get-DbaStartupParameter -SqlInstance $server -EnableException
                    Add-Member -Force -InputObject $output -MemberType NoteProperty -Name OriginalStartupParameters -Value $originalparamstring
                }

                $output

                Write-Message -Level Output -Message "Startup parameters changed on $SqlInstance. You must restart SQL Server for changes to take effect."
            }
            catch {
                Stop-Function -Message "Startup parameters failed to change on $SqlInstance. " -Target $SqlInstance -ErrorRecord $_
                return
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Set-DbaTcpPort {
    <#
        .SYNOPSIS
            Changes the TCP port used by the specified SQL Server.

        .DESCRIPTION
            This function changes the TCP port used by the specified SQL Server.

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to.

        .PARAMETER SqlCredential
            Credential object used to connect to the SQL Server instance as a different user

        .PARAMETER Credential
            Credential object used to connect to the Windows server itself as a different user

        .PARAMETER Port
            TCPPort that SQLService should listen on.

        .PARAMETER IpAddress
            Wich IpAddress should the portchange , if omitted allip (0.0.0.0) will be changed with the new portnumber.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .NOTES
            Tags: Service, Port, TCP, Configure
            Author: [email protected], @H0s0n77

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Set-DbaTcpPort

        .EXAMPLE
            Set-DbaTcpPort -SqlInstance SqlInstance2014a -Port 1433

            Sets the port number 1433 for allips on the default instance on SqlInstance2014a

        .EXAMPLE
            Set-DbaTcpPort -SqlInstance winserver\sqlexpress -IpAddress 192.168.1.22 -Port 1433

            Sets the port number 1433 for IP 192.168.1.22 on the sqlexpress instance on winserver

        .EXAMPLE
            Set-DbaTcpPort -SqlInstance 'SQLDB2014A' ,'SQLDB2016B' -port 1337

            Sets the port number 1337 for ALLIP's on SqlInstance SQLDB2014A and SQLDB2016B
    #>
    [CmdletBinding(ConfirmImpact = "High")]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [PSCredential]$Credential,
        [parameter(Mandatory = $true)]
        [ValidateRange(1, 65535)]
        [int]$Port,
        [IpAddress[]]$IpAddress,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {

        if ($IpAddress.Length -eq 0) {
            $IpAddress = '0.0.0.0'
        }
        else {
            if ($SqlInstance.count -gt 1) {
                Stop-Function -Message "-IpAddress switch cannot be used with a collection of serveraddresses" -Target $SqlInstance
                return
            }
        }
    }

    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $SqlInstance) {

            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $wmiinstancename = $server.ServiceName


            if ($server.IsClustered) {
                Write-Message -Level Verbose -Message "Instance is clustered fetching nodes..."
                $clusternodes = (Get-DbaClusterNode -SqlInstance $server).ComputerName -join ", "

                Write-Message -Level Output -Message "$instance is a clustered instance, portchanges will be reflected on all nodes ($clusternodes) after a failover"
            }

            $scriptblock = {
                $instance = $args[0]
                $wmiinstancename = $args[1]
                $port = $args[2]
                $IpAddress = $args[3]
                $sqlinstanceName = $args[4]

                $wmi = New-Object Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer $instance
                $wmiinstance = $wmi.ServerInstances | Where-Object { $_.Name -eq $wmiinstancename }
                $tcp = $wmiinstance.ServerProtocols | Where-Object { $_.DisplayName -eq 'TCP/IP' }
                $IpAddress = $tcp.IpAddresses | where-object { $_.IpAddress -eq $IpAddress }
                $tcpport = $IpAddress.IpAddressProperties | Where-Object { $_.Name -eq 'TcpPort' }

                $oldport = $tcpport.Value
                try {
                    $tcpport.value = $port
                    $tcp.Alter()
                    [pscustomobject]@{
                        ComputerName  = $env:COMPUTERNAME
                        InstanceName  = $wmiinstancename
                        SqlInstance   = $sqlinstanceName
                        OldPortNumber = $oldport
                        PortNumber    = $Port
                        Status        = "Success"
                    }
                }
                catch {
                    [pscustomobject]@{
                        ComputerName  = $env:COMPUTERNAME
                        InstanceName  = $wmiinstancename
                        SqlInstance   = $sqlinstanceName
                        OldPortNumber = $oldport
                        PortNumber    = $Port
                        Status        = "Failed: $_"
                    }
                }
            }

            try {
                $computerName = $instance.ComputerName
                $resolved = Resolve-DbaNetworkName -ComputerName $computerName

                Write-Message -Level Verbose -Message "Writing TCPPort $port for $instance to $($resolved.FQDN)..."
                Invoke-ManagedComputerCommand -ComputerName $resolved.FQDN -ScriptBlock $scriptblock -ArgumentList $Server.ComputerName, $wmiinstancename, $port, $IpAddress, $server.DomainInstanceName -Credential $Credential

            }
            catch {
                Invoke-ManagedComputerCommand -ComputerName $instance.ComputerName -ScriptBlock $scriptblock -ArgumentList $Server.ComputerName, $wmiinstancename, $port, $IpAddress, $server.DomainInstanceName -Credential $Credential
            }
        }
    }
}
function Set-DbaTempDbConfiguration {
    <#
        .SYNOPSIS
            Sets tempdb data and log files according to best practices.

        .DESCRIPTION
            Calculates tempdb size and file configurations based on passed parameters, calculated values, and Microsoft best practices. User must declare SQL Server to be configured and total data file size as mandatory values. Function then calculates the number of data files based on logical cores on the target host and create evenly sized data files based on the total data size declared by the user, with a log file 25% of the total data file size.

            Other parameters can adjust the settings as the user desires (such as different file paths, number of data files, and log file size). No functions that shrink or delete data files are performed. If you wish to do this, you will need to resize tempdb so that it is "smaller" than what the function will size it to before running the function.

        .PARAMETER SqlInstance
            The SQL Server Instance to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER DataFileCount
            Specifies the number of data files to create. If this number is not specified, the number of logical cores of the host will be used.

        .PARAMETER DataFileSizeMB
            Specifies the total data file size in megabytes. This is distributed across the total number of data files.

        .PARAMETER LogFileSizeMB
            Specifies the log file size in megabytes. If not specified, this will be set to 25% of total data file size.

        .PARAMETER DataFileGrowthMB
            Specifies the growth amount for the data file(s) in megabytes. The default is 512 MB.

        .PARAMETER LogFileGrowthMB
            Specifies the growth amount for the log file in megabytes. The default is 512 MB.

        .PARAMETER DataPath
            Specifies the filesystem path in which to create the tempdb data files. If not specified, current tempdb location will be used.

        .PARAMETER LogPath
            Specifies the filesystem path in which to create the tempdb log file. If not specified, current tempdb location will be used.

        .PARAMETER OutputScriptOnly
            If this switch is enabled, only the T-SQL script to change the tempdb configuration is created and output.

        .PARAMETER OutFile
            Specifies the filesystem path into which the generated T-SQL script will be saved.

        .PARAMETER DisableGrowth
            If this switch is enabled, the tempdb files will be configured to not grow. This overrides -DataFileGrowthMB and -LogFileGrowthMB.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Tempdb, Space, Configure, Configuration
            Author: Michael Fal (@Mike_Fal), http://mikefal.net

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Set-DbaTempDbConfiguration

        .EXAMPLE
            Set-DbaTempDbConfiguration -SqlInstance localhost -DataFileSizeMB 1000

            Creates tempdb with a number of data files equal to the logical cores where each file is equal to 1000MB divided by the number of logical cores, with a log file of 250MB.

        .EXAMPLE
            Set-DbaTempDbConfiguration -SqlInstance localhost -DataFileSizeMB 1000 -DataFileCount 8

            Creates tempdb with 8 data files, each one sized at 125MB, with a log file of 250MB.

        .EXAMPLE
            Set-DbaTempDbConfiguration -SqlInstance localhost -DataFileSizeMB 1000 -OutputScriptOnly

            Provides a SQL script output to configure tempdb according to the passed parameters.

        .EXAMPLE
            Set-DbaTempDbConfiguration -SqlInstance localhost -DataFileSizeMB 1000 -DisableGrowth

            Disables the growth for the data and log files.

        .EXAMPLE
            Set-DbaTempDbConfiguration -SqlInstance localhost -DataFileSizeMB 1000 -OutputScriptOnly

            Returns the T-SQL script representing tempdb configuration.
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [int]$DataFileCount,
        [Parameter(Mandatory = $true)]
        [int]$DataFileSizeMB,
        [int]$LogFileSizeMB,
        [int]$DataFileGrowthMB = 512,
        [int]$LogFileGrowthMB = 512,
        [string]$DataPath,
        [string]$LogPath,
        [string]$OutFile,
        [switch]$OutputScriptOnly,
        [switch]$DisableGrowth,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        $sql = @()
        Write-Message -Level Verbose -Message "Connecting to $SqlInstance"
        $server = Connect-SqlInstance $sqlinstance -SqlCredential $SqlCredential

        if ($server.VersionMajor -lt 9) {
            Stop-Function -Message "SQL Server 2000 is not supported"
            return
        }
    }

    process {

        if (Test-FunctionInterrupt) {
            return
        }

        $cores = $server.Processors
        if ($cores -gt 8) {
            $cores = 8
        }

        #Set DataFileCount if not specified. If specified, check against best practices.
        if (-not $DataFileCount) {
            $DataFileCount = $cores
            Write-Message -Message "Data file count set to number of cores: $DataFileCount" -Level Verbose
        }
        else {
            if ($DataFileCount -gt $cores) {
                Write-Message -Message "Data File Count of $DataFileCount exceeds the Logical Core Count of $cores. This is outside of best practices." -Level Warning
            }
            Write-Message -Message "Data file count set explicitly: $DataFileCount" -Level Verbose
        }

        $DataFilesizeSingleMB = $([Math]::Floor($DataFileSizeMB / $DataFileCount))
        Write-Message -Message "Single data file size (MB): $DataFilesizeSingleMB." -Level Verbose

        if ($DataPath) {
            if ((Test-DbaSqlPath -SqlInstance $server -Path $DataPath) -eq $false) {
                Stop-Function -Message "$datapath is an invalid path."
                return
            }
        }
        else {
            $Filepath = $server.Databases['tempdb'].ExecuteWithResults('SELECT physical_name as FileName FROM sys.database_files WHERE file_id = 1').Tables[0].Rows[0].FileName
            $DataPath = Split-Path $Filepath
        }

        Write-Message -Message "Using data path: $datapath." -Level Verbose

        if ($LogPath) {
            if ((Test-DbaSqlPath -SqlInstance $server -Path $LogPath) -eq $false) {
                Stop-Function -Message "$LogPath is an invalid path."
                return
            }
        }
        else {
            $Filepath = $server.Databases['tempdb'].ExecuteWithResults('SELECT physical_name as FileName FROM sys.database_files WHERE file_id = 2').Tables[0].Rows[0].FileName
            $LogPath = Split-Path $Filepath
        }
        Write-Message -Message "Using log path: $LogPath." -Level Verbose

        # Check if the file growth needs to be disabled
        if ($DisableGrowth) {
            $DataFileGrowthMB = 0
            $LogFileGrowthMB = 0
        }

        # Check current tempdb. Throw an error if current tempdb is larger than config.
        $CurrentFileCount = $server.Databases['tempdb'].ExecuteWithResults('SELECT count(1) as FileCount FROM sys.database_files WHERE type=0').Tables[0].Rows[0].FileCount
        $TooBigCount = $server.Databases['tempdb'].ExecuteWithResults("SELECT TOP 1 (size/128) as Size FROM sys.database_files WHERE size/128 > $DataFilesizeSingleMB AND type = 0").Tables[0].Rows[0].Size

        if ($CurrentFileCount -gt $DataFileCount) {
            Stop-Function -Message "Current tempdb not suitable to be reconfigured. The current tempdb has a greater number of files ($CurrentFileCount) than the calculated configuration ($DataFileCount)."
            return
        }

        if ($TooBigCount) {
            Stop-Function -Message "Current tempdb not suitable to be reconfigured. The current tempdb ($TooBigCount MB) is larger than the calculated individual file configuration ($DataFilesizeSingleMB MB)."
            return
        }

        $EqualCount = $server.Databases['tempdb'].ExecuteWithResults("SELECT count(1) as FileCount FROM sys.database_files WHERE size/128 = $DataFilesizeSingleMB AND type = 0").Tables[0].Rows[0].FileCount

        if ($EqualCount -gt 0) {
            Stop-Function -Message "Current tempdb not suitable to be reconfigured. The current tempdb is the same size as the specified DataFileSizeMB."
            return
        }

        Write-Message -Message "tempdb configuration validated." -Level Verbose

        $DataFiles = $server.Databases['tempdb'].ExecuteWithResults("select f.name as Name, f.physical_name as FileName from sys.filegroups fg join sys.database_files f on fg.data_space_id = f.data_space_id where fg.name = 'PRIMARY' and f.type_desc = 'ROWS'").Tables[0];

        #Checks passed, process reconfiguration
        for ($i = 0; $i -lt $DataFileCount; $i++) {
            $File = $DataFiles.Rows[$i]
            if ($File) {
                $Filename = Split-Path $File.FileName -Leaf
                $LogicalName = $File.Name
                $NewPath = "$datapath\$Filename"
                $sql += "ALTER DATABASE tempdb MODIFY FILE(name=$LogicalName,filename='$NewPath',size=$DataFilesizeSingleMB MB,filegrowth=$DataFileGrowthMB);"
            }
            else {
                $NewName = "tempdev$i.ndf"
                $NewPath = "$datapath\$NewName"
                $sql += "ALTER DATABASE tempdb ADD FILE(name=tempdev$i,filename='$NewPath',size=$DataFilesizeSingleMB MB,filegrowth=$DataFileGrowthMB);"
            }
        }

        if (-not $LogFileSizeMB) {
            $LogFileSizeMB = [Math]::Floor($DataFileSizeMB / 4)
        }

        $logfile = $server.Databases['tempdb'].ExecuteWithResults("SELECT name, physical_name as FileName FROM sys.database_files WHERE file_id = 2").Tables[0].Rows[0];
        $Filename = Split-Path $logfile.FileName -Leaf
        $LogicalName = $logfile.Name
        $NewPath = "$LogPath\$Filename"
        $sql += "ALTER DATABASE tempdb MODIFY FILE(name=$LogicalName,filename='$NewPath',size=$LogFileSizeMB MB,filegrowth=$LogFileGrowthMB);"

        Write-Message -Message "SQL Statement to resize tempdb." -Level Verbose
        Write-Message -Message ($sql -join "`n`n") -Level Verbose

        if ($OutputScriptOnly) {
            return $sql
        }
        elseif ($OutFile) {
            $sql | Set-Content -Path $OutFile
        }
        else {
            if ($Pscmdlet.ShouldProcess($SqlInstance, "Executing query and informing that a restart is required.")) {
                try {
                    $server.Databases['master'].ExecuteNonQuery($sql)
                    Write-Message -Level Verbose -Message "tempdb successfully reconfigured."

                    [PSCustomObject]@{
                        ComputerName         = $server.ComputerName
                        InstanceName         = $server.ServiceName
                        SqlInstance          = $server.DomainInstanceName
                        DataFileCount        = $DataFileCount
                        DataFileSizeMB       = $DataFileSizeMB
                        SingleDataFileSizeMB = $DataFilesizeSingleMB
                        LogSizeMB            = $LogFileSizeMB
                        DataPath             = $DataPath
                        LogPath              = $LogPath
                        DataFileGrowthMB     = $DataFileGrowthMB
                        LogFileGrowthMB      = $LogFileGrowthMB
                    }

                    Write-Message -Level Output -Message "tempdb reconfigured. You must restart the SQL Service for settings to take effect."
                }
                catch {
                    Stop-Function -Message "Unable to reconfigure tempdb. Exception: $_" -Target $sql -InnerErrorRecord $_
                    return
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Set-SqlTempDbConfiguration
    }
}
function Show-DbaDatabaseList {
    <#
        .SYNOPSIS
            Shows a list of databases in a GUI.

        .DESCRIPTION
            Shows a list of databases in a GUI. Returns a string holding the name of the selected database. Hitting cancel returns null.

        .PARAMETER SqlInstance
            The SQL Server Instance to connect to..

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Title
            Title of the window being displayed. Default is "Select Database".

        .PARAMETER Header
            Header text displayed above the database listing. Default is "Select the database:".

        .PARAMETER DefaultDb
            Specify a database to have selected when the window appears.

        .NOTES
            Tags: Database
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Show-DbaDatabaseList

        .EXAMPLE
            Show-DbaDatabaseList -SqlInstance sqlserver2014a

            Shows a GUI list of databases using Windows Authentication to connect to the SQL Server. Returns a string of the selected database.

        .EXAMPLE
            Show-DbaDatabaseList -Source sqlserver2014a -SqlCredential $cred

            Shows a GUI list of databases using SQL credentials to connect to the SQL Server. Returns a string of the selected database.
    #>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string]$Title = "Select Database",
        [string]$Header = "Select the database:",
        [string]$DefaultDb
    )

    begin {
        try {
            Add-Type -AssemblyName PresentationFramework
        }
        catch {
            throw "Windows Presentation Framework required but not installed"
        }

        function Add-TreeItem {
            Param (
                [string]$name,
                [object]$parent,
                [string]$tag
            )

            $childitem = New-Object System.Windows.Controls.TreeViewItem
            $textblock = New-Object System.Windows.Controls.TextBlock
            $textblock.Margin = "5,0"
            $stackpanel = New-Object System.Windows.Controls.StackPanel
            $stackpanel.Orientation = "Horizontal"
            $image = New-Object System.Windows.Controls.Image
            $image.Height = 20
            $image.Width = 20
            $image.Stretch = "Fill"
            $image.Source = $dbicon
            $textblock.Text = $name
            $childitem.Tag = $name

            if ($name -eq $DefaultDb) {
                $childitem.IsSelected = $true
                $script:selected = $name
            }

            [void]$stackpanel.Children.Add($image)
            [void]$stackpanel.Children.Add($textblock)

            $childitem.Header = $stackpanel
            [void]$parent.Items.Add($childitem)
        }

        function Convert-b64toimg {
            param ($base64)

            $bitmap = New-Object System.Windows.Media.Imaging.BitmapImage
            $bitmap.BeginInit()
            $bitmap.StreamSource = [System.IO.MemoryStream][System.Convert]::FromBase64String($base64)
            $bitmap.EndInit()
            $bitmap.Freeze()
            return $bitmap
        }

        $dbicon = Convert-b64toimg "iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAAFRSURBVDhPY/j//z9VMVZBSjCCgQZunFn6/8zenv+7llf83zA75/+6WTn/N80v+L93ddP/M/tnY2jAayDIoNvn5/5/cX/t/89vdv7/9fUQGIPYj2+t/H/xyJT/O1ZUoWjCaeCOxcX///48ShSeWhMC14jXwC9Xs/5/fzHr/6/PW+GaQS78/WH9/y+Pe8DyT3fYEmcgKJw+HHECawJp/vZ60f8v95v/fzgd8P/tVtn/L1cw/n+0iOH/7TlMxBkIigBiDewr9iVsICg2qWrg6qnpA2dgW5YrYQOX9icPAQPfU9PA2S2RRLuwMtaGOAOf73X+//FyGl4DL03jIM5AEFjdH/x//+Lo/1cOlP9/dnMq2MA3x/z/312l/P/4JNH/axoU/0/INUHRhNdAEDi+pQ1cZIFcDEpvoPCaVOTwf1Gjy/9ds5MxNGAYSC2MVZB8/J8BAGcHwqQBNWHRAAAAAElFTkSuQmCC"
        $foldericon = Convert-b64toimg "iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAAHaSURBVDhPY/j//z9VMVZBSjBWQUowVkFKMApnzZL+/+gYWZ4YDGeANL95sun/j3fbwPjbm5X/Pz+cRLKhcAayq2B45YKe/8vndoHx4lltYLxgajMKhumHYRQDf37Yh4J/fNry//fb1f9/v1n6/8/Tqf//3O/6/+dO9f9fV4v+fzmV/v/L0aj/lflJQO1YDAS5AmwI1MvfPyAZ9KgbYtDlvP/fzyT9/3w45P+HPT7/z8+UwG0gyDvIBmIYBnQVyDCQq0CGPV9p8v94P/f/rKQwoHYsBs4HhgfIQJjLfr+YjdOwt5tt/z9eov1/fxf3/+ggD6B2HAaCXQYKM6hhv+81oYQXzLCXq03/P5qn/H9LE/9/LycroHYsBs7oq4EYCDIM6FVshr3Z4gg2DOS6O9Nk/q+sFvlvZawD1I7FwKldleC0h2zY9wuZEMP2+aMYdn+W/P/rE0T/zy+T+q+jJg/UjsXASe1l/z/cX/T/1dn8/492ePy/vc7s/82VOv8vLVT9f3yGwv89ffL/1zXL/l9dJwF2GciwaYVy/xVlxIDasRjY31Lyv7Uy+39ZTvz/1JiA/8Hejv8dLA3+62sqgTWJC/HixDAzQBjOoBbGKkgJxipICcYqSD7+zwAAkIiWzSGuSg0AAAAASUVORK5CYII="
        $dbatoolsicon = Convert-b64toimg "iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAAO9SURBVEhL3VVdTFNXHO9MzPTF+OzDeBixFdTMINIWsAUK3AIVkFvAIQVFRLYZKR8Wi1IEKV9DYB8PGFAyEx8QScySabYY5+I2JvK18iWISKGk0JGhLzA3+e2c29uHtpcvH/0lv9yennN+v3vO/3fOFb2fCAg4vXWPNOmMRJ745TtTSskqeElviGXJ0XtkWvjJkyGLPoFAVQZoe/NkX/n6Mh/ysu4Qy7WZdJAutxRW6zT6LcNQaE4LiGgREH4cibpCMNqzCIk9hbScEoSSZ0zKOa7fRxG/k5d1h8ukvO4a5ubmMT1jw5E0vZcBZWzqOTS3dcB8tRXZeRX4/v5DZH5uIu0Wrn8NEzaNDjgYoUPd120oMjViX2iql8H6ZFd8DzE7eFl3iOWpuyQydlh44kbJroilSd8RuQ+cqh7wC9Z+JJaxY8KTN0gp+5Yk9DaREzYhb5FOBwZFZ6LlZifKa5ux//AxYTHCvSEp8A9O5n77B6dwqXS119guZ+GrGq9jfn4eM7ZZxB/PdxN2UfOpHq3kRWq/uoE8Yx3u/fQLzhSYUdN0g+tfN126z0oxNj6BJz0Dq0b4E2UawuJzuPhKyZmKYr/AocgMrk37VzWRBLGRdE/psuXqk9wkT/GNUCJLWqS3By/rDh9FxjaSrnahiZ7cq8wCUzKImLIJqC+Ngbk4gmjjIKKKB6Aq7l+OLBmfVF0YnlQZR1p4eSd2y5IiyEr+oyJ0CwIi0gUNKAOPmnG04Q0utf+DHweWkFjjQOyVWajLpsCUPkeUcRgqAzE09Dfz8k64aqI9YcDziUk87bMgOCZL0CQ0ux2J9UtIbXyFwall/PD0NeLKrU6DkhGymj8RXtRDjU7x8k64TKpJQmi6bLOzSEgv8DYhNWMujiK+9jU0VQs4Vm/H2MwSOh4vcP+rii2cQVh+F+IqbRJe3glyReuoSFBUJtpu3eWulv2h3ueE1iOu0g5N9QL3jLk8jerbdrz59y1yGoYQUdSLsII/CLscIsD9UPrLUz4myXhBhWjCPMVdPBBnhMbsIAZzSDDbcOvRIhyLy6i4+Qyq82QFxECR9xjK/K5OXtodNHo+CsW2tagunbxADbK+sXP16Bv/G7lNQ8hpHEX21UGoDb/j8NmfoSzoNvCymwdTPvMotsKGB32LaL1H0mS0oOHOFLpH/0L3iAOF3/YSk4dgTBMh/JTNgdVbtzNl1il12UuSpHE+SRayTb0IL3yCMP2vUJKtUuh/szNNK8Jfxw3BZNpiMoGjiKPJm54Ffw8gEv0PQRYX7wDAUKEAAAAASUVORK5CYII="

        $sourceserver = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
    }

    process {
        # Create XAML form in Visual Studio, ensuring the ListView looks chromeless
        [xml]$xaml = "<Window
        xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
        xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
        Title='$Title' SizeToContent='WidthAndHeight' Background='#F0F0F0'
        WindowStartupLocation='CenterScreen' MaxHeight='600'>
    <Grid>
        <TreeView Name='treeview' Height='Auto' Width='Auto' Background='#FFFFFF' BorderBrush='#FFFFFF' Foreground='#FFFFFF' Margin='11,36,11,79'/>
        <Label x:Name='label' Content='$header' HorizontalAlignment='Left' Margin='15,4,10,0' VerticalAlignment='Top'/>
        <StackPanel HorizontalAlignment='Right' Orientation='Horizontal' VerticalAlignment='Bottom' Margin='0,50,10,30'>
        <Button Name='okbutton' Content='OK'  Margin='0,0,0,0' Width='75'/>
        <Label Width='10'/>
        <Button Name='cancelbutton' Content='Cancel' Margin='0,0,0,0' Width='75'/>
    </StackPanel>
</Grid>
</Window>"
        #second pushes it down
        # Turn XAML into PowerShell objects
        $window = [Windows.Markup.XamlReader]::Load((New-Object System.Xml.XmlNodeReader $xaml))
        $window.icon = $dbatoolsicon

        $xaml.SelectNodes("//*[@Name]") | ForEach-Object { Set-Variable -Name ($_.Name) -Value $window.FindName($_.Name) -Scope Script }

        $childitem = New-Object System.Windows.Controls.TreeViewItem
        $textblock = New-Object System.Windows.Controls.TextBlock
        $textblock.Margin = "5,0"
        $stackpanel = New-Object System.Windows.Controls.StackPanel
        $stackpanel.Orientation = "Horizontal"
        $image = New-Object System.Windows.Controls.Image
        $image.Height = 20
        $image.Width = 20
        $image.Stretch = "Fill"
        $image.Source = $foldericon
        $textblock.Text = "Databases"
        $childitem.Tag = "Databases"
        $childitem.isExpanded = $true
        [void]$stackpanel.Children.Add($image)
        [void]$stackpanel.Children.Add($textblock)
        $childitem.Header = $stackpanel
        $databaseParent = $treeview.Items.Add($childitem)

        try {
            $databases = $sourceserver.databases.name
        }
        catch {
            return
        }

        foreach ($database in $databases) {
            Add-TreeItem -Name $database -Parent $childitem -Tag $nameSpace
        }

        $okbutton.Add_Click( {
                $window.Close()
                $script:okay = $true
            })

        $cancelbutton.Add_Click( {
                $script:selected = $null
                $window.Close()
            })

        $window.Add_SourceInitialized( {
                [System.Windows.RoutedEventHandler]$Event = {
                    if ($_.OriginalSource -is [System.Windows.Controls.TreeViewItem]) {
                        $script:selected = $_.OriginalSource.Tag
                    }
                }
                $treeview.AddHandler([System.Windows.Controls.TreeViewItem]::SelectedEvent, $Event)
            })

        $null = $window.ShowDialog()
    }

    end {
        if ($script:selected.length -gt 0 -and $script:okay -eq $true) {
            return $script:selected
        }

        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Show-SqlDatabaseList
    }
}
function Show-DbaServerFileSystem {
    <#
        .SYNOPSIS
            Shows file system on remote SQL Server in a local GUI and returns the selected directory name

        .DESCRIPTION
            Similar to the remote file system popup you see when browsing a remote SQL Server in SQL Server Management Studio, this function allows you to traverse the remote SQL Server's file structure.

            Show-DbaServerFileSystem uses SQL Management Objects to browse the directories and what you see is limited to the permissions of the account running the command.

        .PARAMETER SqlInstance
            The SQL Server whose filesystem you want to view.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .NOTES
            Tags: Storage
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Show-DbaServerFileSystem

        .EXAMPLE
            Show-DbaServerFileSystem -SqlInstance sqlserver2014a

            Shows a list of databases using Windows Authentication to connect to the SQL Server. Returns a string of the selected path.

        .EXAMPLE
            Show-DbaServerFileSystem -Source sqlserver2014a -SqlCredential $cred

            Shows a list of databases using SQL credentials to connect to the SQL Server. Returns a string of the selected path.

    #>
    [CmdletBinding(SupportsShouldProcess)]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [object]$SqlCredential
    )

    begin {
        try {
            Add-Type -AssemblyName PresentationFramework
        }
        catch {
            throw "Windows Presentation Framework required but not installed."
        }

        function Add-TreeItem {
            param (
                [string]$name,
                [object]$parent,
                [string]$tag
            )

            $childitem = New-Object System.Windows.Controls.TreeViewItem

            $textblock = New-Object System.Windows.Controls.TextBlock
            $textblock.Margin = "5,0"

            $stackpanel = New-Object System.Windows.Controls.StackPanel
            $stackpanel.Orientation = "Horizontal"

            $image = New-Object System.Windows.Controls.Image
            $image.Height = 20
            $image.Width = 20
            $image.Stretch = "Fill"

            if ($name.length -eq 1) {
                $image.Source = $diskicon
                $textblock.Text = "$name`:"
                $childitem.Tag = "$name`:"

            }
            else {
                $image.Source = $foldericon
                $textblock.Text = $name
                $childitem.Tag = "$tag\$name"
            }

            [void]$stackpanel.Children.Add($image)
            [void]$stackpanel.Children.Add($textblock)

            $childitem.Header = $stackpanel

            [void]$childitem.Items.Add("*")
            [void]$parent.Items.Add($childitem)
        }

        function Get-SubDirectory {
            Param (
                [string]$nameSpace,
                [object]$treeviewItem
            )

            $textbox.Text = $nameSpace
            try {
                $dirs = $sourceserver.EnumDirectories($nameSpace)
            }
            catch {
                return
            }
            $subdirs = $dirs.Name

            foreach ($subdir in $subdirs) {
                if (!$subdir.StartsWith("$") -and $subdir -ne 'System Volume Information') {
                    Add-TreeItem -Name $subdir -Parent $treeviewItem -Tag $nameSpace
                }
            }
        }

        function Convert-b64toimg {
            param ($base64)

            $bitmap = New-Object System.Windows.Media.Imaging.BitmapImage
            $bitmap.BeginInit()
            $bitmap.StreamSource = [System.IO.MemoryStream][System.Convert]::FromBase64String($base64)
            $bitmap.EndInit()
            $bitmap.Freeze()
            return $bitmap
        }

        $diskicon = Convert-b64toimg "iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAACxEAAAsRAX9kX5EAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAAJtSURBVEhLtZJLa1NBGIa78ze4EZeu3bjS36BduVOsVCGmUqo1QlMaTV3E0oVugm0obdUQTZtYEnNvboTczjlN0ubaWE2aWGhuVQQXKbzOBM+BmokinA48nI+XmefjfDNDAE4dZig3zFBumKHcMEO5YYZywwwppVL5QrG4+217OweO30IiySPJCT1ozQsp7GTzoHvoXpZDpC/4Ut2/nc7sRIhYqO3Xuq1GA512C53WSY46bbSaTVQr1S5pLNAz9OyfPopUlMuf9KFAWO9yeit2uwtWiw1Ohwd+XwBBfxjBAIF+f9dkLzZ9QTg/umGzuuGwe+F0uivBQEhPXcwmJtM6HOSA2+VDOBRBaisNno4nwSOR4PqIx5LgyRhzuQK4NIdYPE7ORXsO6hK9FKkYHb0Po3ENGXIHzVabRP9ex13gsHkI7qcdobwTyUgapncWUBdZ/U3Gxx/j9aoJqVQGpd0KCsWvhPpAavXv8Ls5KCfGcMN7EcOay9CpX8D8/gOoS/RSTjQxLK6QlyRgt1xFvlAn1AZSq/yAZzOCW7pruHpwBlc056C+8xxr5o3BTRSKid6fZHM5VKoH2PvcIjQH0mwcwx/gcFN1HcOxs7ikPI+ZsTnyWHygLtFLkQq1ehZTUxpYrRvI58sQhAIhP5Bsbg9+Txzzcy+hddzDkwUVnk3PY1arA3WJXopUmEwWjIzcheqRGsa3ZjK65b+y8GoJy0tvyEWvY9W+CJvXhqczup6DukQvRSqi0QQMhhVMTk5DqXzYm+v/oFA8IJPQkhdqBnWJXopUnCbMUG6YodwwQ7lhhnLDDOWGGcoNM5QXDP0CA9dqCMSSjzkAAAAASUVORK5CYII="
        $foldericon = Convert-b64toimg "iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAACxEAAAsRAX9kX5EAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAAU0SURBVEhLtZXpU5NXFIf9q/qto9bRuhc3xqUiUK3KoLYq6ihu1VIU1DpjRZ3BHVR8i6hUkDUJASNL2EJIWLKvBLJAAmHRpwfb6ai10Q/2w29u3pP3/p57zrn3vnOA/13/CkwMlzLuv0rMfYaoM59R+1ki1lwiFtHgGcIDuYT6fiZoPsWI6SSBnmyGeo4yZDxN2KEQHXr54H3Pdx5mNe6/QnzkGtMxNVNjeqK+AkLWQ/iMWdg7sxnz3uZ1vIrJSBnx0O9MhBQmggqxkRJC9hsEzFc+Dol5cpkcFaNXHbyasTMVNzARqWXEeZv+lh+xG/OIRWrgtU5ebxG1yu9meb+JEct57C+2fwLEe04g95mOPxJV8JphCcPUuBOHfg/6qm/pa8klNvyA6fFqZqbqZaxiZqKWQF8uVm3qp0LuMTNZyfREuUx+JuEo8agLe1smHTXpGLXSB3O+lKqMV5N1UtZypqJ/SCwHS2P6J0Iis5BqgTyR3tyVcIj4mAtL807aq1IxqLJwdR6TJhfL/xUCK3kjn/Ekg9rvPg6JevL/hlQJ5KmU4qGEg9IXCwNN22ir3ES3ap9AjjLqvU48/FBgN0SFeAzZDDSk3nnf852HWY25zxKPFEut/4JMRmcX5mM8bKJPm0bLs/V01e/B2X6IsPMyscAtRt2XRZdwdx1kQPsJ5Rp1nRGIZCKQqZhs09FiCTuJhQyYNFtoLl9HZ20mjtb9BK0XJJsrhG3nZJvn4Wr/AUfbEc+I/fFsI//xfAcwq4gjR0pTxPRkhQAeMh66zavpHkYDWoz1m3j5ZDWd1duxN+9muO80IVuejCcImI7h0W/F2boLtyFfrBJAwtZTjAdvMTn+VFZ/j7HADSbGahh2FGGoWYeuLImOynRsL3bg7zksgGP4DPvwdu2W52wp4wH6NJvFKgEkaP2J6LA0dKxMxruM+q9LFnfxmH+hu3oVTY+S0FekyFbdhrdzr1wnWXg6MnC1phBwVuOxqelvSBOrBJCRgWOM+a9JmUqI+AoJea4SlMY6pamdlStoVJbTVr6BQemPq22ngDJwNm/B3rSKgFuPzz0gmXwEEug7TMTzm5TpDkHXZYbtFxm2XsTenkX7s2VoS76m9cla+lUbcbxMlQxmAclYNEsIuzSE3N0CSRWrBBC/6RBB50XC3kICtl/xD+bh78/DIveWvnwJDQ8W0iJ96atNxtYoIN0GrA1JWOoX4Ler8Dq7MKu3iFUCiK/ngKw8Xy7EAnwDZ6UXOXjkGh/QZdL2dDHq+1/RXLoSc80aMV+LTbsKm3opHvU8buoqKdC1YFKliFUCiKdrr6w8B7/lAu7eUzi7j+PoPCIH8XtaHi9CXTyPZmUJpqqVDKq/wapZgUO9mKB2LqmlVSQ9bKG3fqNYJYA4O3bhMZ2QDHJxdGVj0x/E2rafXnW6QBaiKZ7LS2URpucrGFQtF8hyHJqlDGkXcLz0OnuVUno/loldvwO37He3MQebXB3W1n1yMe7BKBNnIeqiL9GVLHwDsaiWYdMsw6pehl1A7bXraK7birlhh1glgLi6T2Br24tZmyqr34BJvZn+xu1y2r+l9fF86m5+gbZ4PsbnazDXJ9OvXv9mNNUl45LSDck3x91bKFYJIF7TJWVQl6mYNGn6fl0WpoYMeupSaK9Yje7RcjmMqbSWZ9gNz9crxvotSq86TTHWpiiGmk2KUZWuOAyXlBG3Snnb8x3A2wp6m3b6rE+wtp+nq2oDL0oX01CaQndjAVZjbdGH5vyXPhj83Ppg8POKOX8Cx4yjZbQFLr4AAAAASUVORK5CYII="
        $dbatoolsicon = Convert-b64toimg "iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAAO9SURBVEhL3VVdTFNXHO9MzPTF+OzDeBixFdTMINIWsAUK3AIVkFvAIQVFRLYZKR8Wi1IEKV9DYB8PGFAyEx8QScySabYY5+I2JvK18iWISKGk0JGhLzA3+e2c29uHtpcvH/0lv9yennN+v3vO/3fOFb2fCAg4vXWPNOmMRJ745TtTSskqeElviGXJ0XtkWvjJkyGLPoFAVQZoe/NkX/n6Mh/ysu4Qy7WZdJAutxRW6zT6LcNQaE4LiGgREH4cibpCMNqzCIk9hbScEoSSZ0zKOa7fRxG/k5d1h8ukvO4a5ubmMT1jw5E0vZcBZWzqOTS3dcB8tRXZeRX4/v5DZH5uIu0Wrn8NEzaNDjgYoUPd120oMjViX2iql8H6ZFd8DzE7eFl3iOWpuyQydlh44kbJroilSd8RuQ+cqh7wC9Z+JJaxY8KTN0gp+5Yk9DaREzYhb5FOBwZFZ6LlZifKa5ux//AxYTHCvSEp8A9O5n77B6dwqXS119guZ+GrGq9jfn4eM7ZZxB/PdxN2UfOpHq3kRWq/uoE8Yx3u/fQLzhSYUdN0g+tfN126z0oxNj6BJz0Dq0b4E2UawuJzuPhKyZmKYr/AocgMrk37VzWRBLGRdE/psuXqk9wkT/GNUCJLWqS3By/rDh9FxjaSrnahiZ7cq8wCUzKImLIJqC+Ngbk4gmjjIKKKB6Aq7l+OLBmfVF0YnlQZR1p4eSd2y5IiyEr+oyJ0CwIi0gUNKAOPmnG04Q0utf+DHweWkFjjQOyVWajLpsCUPkeUcRgqAzE09Dfz8k64aqI9YcDziUk87bMgOCZL0CQ0ux2J9UtIbXyFwall/PD0NeLKrU6DkhGymj8RXtRDjU7x8k64TKpJQmi6bLOzSEgv8DYhNWMujiK+9jU0VQs4Vm/H2MwSOh4vcP+rii2cQVh+F+IqbRJe3glyReuoSFBUJtpu3eWulv2h3ueE1iOu0g5N9QL3jLk8jerbdrz59y1yGoYQUdSLsII/CLscIsD9UPrLUz4myXhBhWjCPMVdPBBnhMbsIAZzSDDbcOvRIhyLy6i4+Qyq82QFxECR9xjK/K5OXtodNHo+CsW2tagunbxADbK+sXP16Bv/G7lNQ8hpHEX21UGoDb/j8NmfoSzoNvCymwdTPvMotsKGB32LaL1H0mS0oOHOFLpH/0L3iAOF3/YSk4dgTBMh/JTNgdVbtzNl1il12UuSpHE+SRayTb0IL3yCMP2vUJKtUuh/szNNK8Jfxw3BZNpiMoGjiKPJm54Ffw8gEv0PQRYX7wDAUKEAAAAASUVORK5CYII="

        $sourceserver = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SourceSqlCredential
    }

    process {
        # Create XAML form in Visual Studio, ensuring the ListView looks chromeless
        [xml]$xaml = '<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Locate Folder" Height="620" Width="440" Background="#F0F0F0"
        WindowStartupLocation="CenterScreen">
    <Grid>
        <TreeView Name="treeview" Height="462" Width="391" Background="#FFFFFF" BorderBrush="#FFFFFF" Foreground="#FFFFFF" Margin="11,36,11,79"/>
        <Label x:Name="label" Content="Select the folder:" HorizontalAlignment="Left" Margin="15,4,0,0" VerticalAlignment="Top"/>
        <Label x:Name="path" Content="Selected Path" HorizontalAlignment="Left" Margin="15,502,0,0" VerticalAlignment="Top"/>
        <TextBox Name="textbox" HorizontalAlignment="Left" Height="Auto" Margin="111,504,0,0" TextWrapping="NoWrap" Text="C:\" VerticalAlignment="Top" Width="292"/>
        <Button Name="okbutton" Content="OK" HorizontalAlignment="Left" Margin="241,540,0,0" VerticalAlignment="Top" Width="75"/>
        <Button Name="cancelbutton" Content="Cancel" HorizontalAlignment="Left" Margin="328.766,540,0,0" VerticalAlignment="Top" Width="75"/>
    </Grid>
</Window>
'
        # Turn XAML into PowerShell objects
        $window = [Windows.Markup.XamlReader]::Load((New-Object System.Xml.XmlNodeReader $xaml))
        $window.icon = $dbatoolsicon

        $xaml.SelectNodes("//*[@Name]") | ForEach-Object { Set-Variable -Name ($_.Name) -Value $window.FindName($_.Name) -Scope Script }

        try {
            $drives = ($sourceserver.EnumAvailableMedia()).Name
        }
        catch {
            throw "No access to remote SQL Server files."
        }

        foreach ($drive in $drives) {
            $drive = $drive.Replace(":", "")
            Add-TreeItem -Name $drive -Parent $treeview -Tag $drive
        }

        $window.Add_SourceInitialized( {
                [System.Windows.RoutedEventHandler]$Event = {
                    if ($_.OriginalSource -is [System.Windows.Controls.TreeViewItem]) {
                        $treeviewItem = $_.OriginalSource
                        $treeviewItem.items.clear()
                        Get-SubDirectory -NameSpace $treeviewItem.Tag -TreeViewItem $treeviewItem
                    }
                }
                $treeview.AddHandler([System.Windows.Controls.TreeViewItem]::ExpandedEvent, $Event)
                $treeview.AddHandler([System.Windows.Controls.TreeViewItem]::SelectedEvent, $Event)
            })

        $okbutton.Add_Click( {
                $window.Close()
            })

        $cancelbutton.Add_Click( {
                $textbox.Text = $null
                $window.Close()
            })

        $null = $window.ShowDialog()
    }

    end {

        if ($textbox.Text.length -gt 0) {
            $drive = $textbox.Text + '\'
            return $drive
        }

        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Show-SqlServerFileSystem
    }
}
function Start-DbaAgentJob {
    <#
        .SYNOPSIS
            Starts a running SQL Server Agent Job.

        .DESCRIPTION
            This command starts a job then returns connected SMO object for SQL Agent Job information for each instance(s) of SQL Server.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Job
            The job(s) to process - this list is auto-populated from the server. If unspecified, all jobs will be processed.

        .PARAMETER ExcludeJob
            The job(s) to exclude - this list is auto-populated from the server.

        .PARAMETER AllJobs
            Retrieve all the jobs

        .PARAMETER Wait
            Wait for output until the job has started

        .PARAMETER WaitPeriod
            Wait period in seconds to use when -Wait is used

        .PARAMETER SleepPeriod
            Period in milliseconds to wait after a job has started

        .PARAMETER InputObject
            Internal parameter that enables piping

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Job, Agent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Start-DbaAgentJob

        .EXAMPLE
            Start-DbaAgentJob -SqlInstance localhost

            Starts all running SQL Agent Jobs on the local SQL Server instance

        .EXAMPLE
            Get-DbaAgentJob -SqlInstance sql2016 -Job cdc.DBWithCDC_capture | Start-DbaAgentJob

            Starts the cdc.DBWithCDC_capture SQL Agent Job on sql2016

        .EXAMPLE
            Start-DbaAgentJob -SqlInstance sql2016 -Job cdc.DBWithCDC_capture

            Starts the cdc.DBWithCDC_capture SQL Agent Job on sql2016

        .EXAMPLE
            $servers | Find-DbaAgentJob -IsFailed | Start-DbaAgentJob

            Restarts all failed jobs on all servers in the $servers collection

        .EXAMPLE
            Start-DbaAgentJob -SqlInstance sql2016 -AllJobs

            Start all the jobs

    #>
    [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = "Default")]
    param (
        [parameter(Mandatory, ParameterSetName = "Instance")]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$Job,
        [string[]]$ExcludeJob,
        [parameter(Mandatory, ValueFromPipeline, ParameterSetName = "Object")]
        [Microsoft.SqlServer.Management.Smo.Agent.Job[]]$InputObject,
        [switch]$AllJobs,
        [switch]$Wait,
        [int]$WaitPeriod = 3,
        [int]$SleepPeriod = 300,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        if ((Test-Bound -not -ParameterName AllJobs) -and (Test-Bound -not -ParameterName Job) -and (Test-Bound -not -ParameterName InputObject)) {
            Stop-Function -Message "Please use one of the job parameters, either -Job or -AllJobs. Or pipe in a list of jobs." -Target $instance
            return
        }
        # Loop through each of the instances
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            
            # Check if all the jobs need to included
            if ($AllJobs) {
                $InputObject += $server.JobServer.Jobs
            }
            
            # If a specific job needs to be added
            if (-not $AllJobs -and $Job) {
                $InputObject = $server.JobServer.Jobs | Where-Object Name -In $Job
            }
            
            # If a job needs to be excluded
            if ($ExcludeJob) {
                $InputObject = $InputObject | Where-Object Name -NotIn $ExcludeJob
            }
        }
        
        # Loop through each of the jobs
        foreach ($currentjob in $InputObject) {
            $server = $currentjob.Parent.Parent
            $status = $currentjob.CurrentRunStatus
            
            if ($status -ne 'Idle') {
                Stop-Function -Message "$currentjob on $server is not idle ($status)" -Target $currentjob -Continue
            }
            
            If ($Pscmdlet.ShouldProcess($server, "Starting job $currentjob")) {
                # Start the job
                $lastrun = $currentjob.LastRunDate
                Write-Message -Level Verbose -Message "Last run date was $lastrun"
                $null = $currentjob.Start()
                
                # Wait and refresh so that it has a chance to change status
                Start-Sleep -Milliseconds $SleepPeriod
                $currentjob.Refresh()
                
                $i = 0
                # Check if the status is Idle
                while (($currentjob.CurrentRunStatus -eq 'Idle' -and $i++ -lt 60)) {
                    Write-Message -Level Verbose -Message "Job $($currentjob.Name) status is $($currentjob.CurrentRunStatus)"
                    Write-Message -Level Verbose -Message "Job $($currentjob.Name) last run date is $($currentjob.LastRunDate)"
                    
                    Write-Message -Level Verbose -Message "Sleeping for $SleepPeriod ms and refreshing"
                    Start-Sleep -Milliseconds $SleepPeriod
                    $currentjob.Refresh()
                    
                    # If it failed fast, speed up output
                    if ($lastrun -ne $currentjob.LastRunDate) {
                        $i = 600
                    }
                }
                
                # Wait for the job
                if (Test-Bound -ParameterName Wait) {
                    while ($currentjob.CurrentRunStatus -ne 'Idle') {
                        Write-Message -Level Output -Message "$currentjob is $($currentjob.CurrentRunStatus)"
                        Start-Sleep -Seconds $WaitPeriod
                        $currentjob.Refresh()
                    }
                    Get-DbaAgentJob -SqlInstance $server -Job $currentjob.Name
                }
                else {
                    Get-DbaAgentJob -SqlInstance $server -Job $currentjob.Name
                }
            }
        }
    }
}
function Start-DbaMigration {
    <#
        .SYNOPSIS
            Migrates SQL Server *ALL* databases, logins, database mail profiles/accounts, credentials, SQL Agent objects, linked servers,
            Central Management Server objects, server configuration settings (sp_configure), user objects in systems databases,
            system triggers and backup devices from one SQL Server to another.

            For more granular control, please use one of the -No parameters and use the other functions available within the dbatools module.

        .DESCRIPTION
            Start-DbaMigration consolidates most of the migration tools in dbatools into one command.  This is useful when you're looking to migrate entire instances. It less flexible than using the underlying functions. Think of it as an easy button. It migrates:

            All user databases to exclude support databases such as ReportServerTempDB (Use -IncludeSupportDbs for this). Use -NoDatabases to skip.
            All logins. Use -NoLogins to skip.
            All database mail objects. Use -NoDatabaseMail
            All credentials. Use -NoCredentials to skip.
            All objects within the Job Server (SQL Agent). Use -NoAgentServer to skip.
            All linked servers. Use -NoLinkedServers to skip.
            All groups and servers within Central Management Server. Use -NoCentralManagementServer to skip.
            All SQL Server configuration objects (everything in sp_configure). Use -NoSpConfigure to skip.
            All user objects in system databases. Use -NoSysDbUserObjects to skip.
            All system triggers. Use -NoSystemTriggers to skip.
            All system backup devices. Use -NoBackupDevices to skip.
            All Audits. Use -NoAudits to skip.
            All Endpoints. Use -NoEndpoints to skip.
            All Extended Events. Use -NoExtendedEvents to skip.
            All Policy Management objects. Use -NoPolicyManagement to skip.
            All Resource Governor objects. Use -NoResourceGovernor to skip.
            All Server Audit Specifications. Use -NoServerAuditSpecifications to skip.
            All Custom Errors (User Defined Messages). Use -NoCustomErrors to skip.
            Copies All Data Collector collection sets. Does not configure the server. Use -NoDataCollector to skip.

            This script provides the ability to migrate databases using detach/copy/attach or backup/restore. SQL Server logins, including passwords, SID and database/server roles can also be migrated. In addition, job server objects can be migrated and server configuration settings can be exported or migrated. This script works with named instances, clusters and SQL Express.

            By default, databases will be migrated to the destination SQL Server's default data and log directories. You can override this by specifying -ReuseSourceFolderStructure. Filestreams and filegroups are also migrated. Safety is emphasized.

        .PARAMETER Source
            Source SQL Server.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You may specify multiple servers.

            Note that when using -BackupRestore with multiple servers, the backup will only be performed once and backups will be deleted at the end (if you didn't specify -NoBackupCleanup).

            When using -DetachAttach with multiple servers, -Reattach must be specified.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER BackupRestore
            If this switch is enabled, the Copy-Only backup and restore method is used to perform database migrations. You must specify -NetworkShare with a valid UNC format as well (\\server\share).

        .PARAMETER NetworkShare
            Specifies the network location for the backup files. The SQL Server service accounts on both Source and Destination must have read/write permission to access this location.

        .PARAMETER WithReplace
            If this switch is enabled, databases are restored from backup using WITH REPLACE. This is useful if you want to stage some complex file paths.

        .PARAMETER ReuseSourceFolderStructure
            If this switch is enabled, the data and log directory structures on Source will be kept on Destination. Otherwise, databases will be migrated to Destination's default data and log directories.

            Consider this if you're migrating between different versions and use part of Microsoft's default SQL structure (MSSQL12.INSTANCE, etc.).

        .PARAMETER DetachAttach
            If this switch is enabled, the the detach/copy/attach method is used to perform database migrations. No files are deleted on Source. If the destination attachment fails, the source database will be reattached. File copies are performed over administrative shares (\\server\x$\mssql) using BITS. If a database is being mirrored, the mirror will be broken prior to migration.

        .PARAMETER Reattach
            If this switch is enabled, all databases are reattached to Source after a DetachAttach migration is complete.

            .PARAMETER NoRecovery
            If this switch is enabled, databases will be left in the No Recovery state to enable further backups to be added.

        .PARAMETER IncludeSupportDbs
            If this switch is enabled, the ReportServer, ReportServerTempDb, SSIDb, and distribution databases will be migrated if they exist. A logfile named $SOURCE-$DESTINATION-$date-Sqls.csv will be written to the current directory. Requires -BackupRestore or -DetachAttach.

        .PARAMETER SetSourceReadOnly
            If this switch is enabled, all migrated databases will be set to ReadOnly on the source instance prior to detach/attach & backup/restore. If -Reattach is specified, the database is set to read-only after reattaching.

        .PARAMETER NoDatabases
            If this switch is enabled, databases will not be migrated.

        .PARAMETER NoLogins
            If this switch is enabled, Logins will not be migrated.

        .PARAMETER NoAgentServer
            If this switch is enabled, SQL Agent jobs will not be migrated.

        .PARAMETER NoCredentials
            If this switch is enabled, Credentials will not be migrated.

        .PARAMETER NoLinkedServers
            If this switch is enabled, Linked Servers will not be migrated.

        .PARAMETER NoSpConfigure
            If this switch is enabled, options configured via sp_configure will not be migrated.

        .PARAMETER NoCentralManagementServer
            If this switch is enabled, Central Management Server will not be migrated.

        .PARAMETER NoDatabaseMail
            If this switch is enabled, Database Mail will not be migrated.

        .PARAMETER NoSysDbUserObjects
            If this switch is enabled, user objects found in the master, msdb and model databases will not be migrated.

        .PARAMETER NoSystemTriggers
            If this switch is enabled, System Triggers will not be migrated.

        .PARAMETER NoBackupDevices
            If this switch is enabled, Backup Devices will not be migrated.

        .PARAMETER NoAudits
            If this switch is enabled, Audits will not be migrated.

        .PARAMETER NoEndpoints
            If this switch is enabled, Endpoints will not be migrated.

        .PARAMETER NoExtendedEvents
            If this switch is enabled, Extended Events will not be migrated.

        .PARAMETER NoPolicyManagement
            If this switch is enabled, Policy-Based Management will not be migrated.

        .PARAMETER NoResourceGovernor
            If this switch is enabled, Resource Governor will not be migrated.

        .PARAMETER NoServerAuditSpecifications
            If this switch is enabled, the Server Audit Specification will not be migrated.

        .PARAMETER NoCustomErrors
            If this switch is enabled, Custom Errors (User Defined Messages) will not be migrated.

        .PARAMETER NoDataCollector
            If this switch is enabled, the Data Collector will not be migrated.

        .PARAMETER NoSaRename
            If this switch is enabled, the sa account will not be renamed on the destination instance to match the source.

        .PARAMETER DisableJobsOnDestination
            If this switch is enabled, migrated SQL Agent jobs will be disabled on the destination instance.

        .PARAMETER DisableJobsOnSource
            If this switch is enabled, SQL Agent jobs will be disabled on the source instance.

        .PARAMETER UseLastBackups
            Use the last full, diff and logs instead of performing backups. Note that the backups must exist in a location accessible by all destination servers, such a network share.

        .PARAMETER Force
            If migrating users, forces drop and recreate of SQL and Windows logins.
            If migrating databases, deletes existing databases with matching names.
            If using -DetachAttach, -Force will break mirrors and drop dbs from Availability Groups.

            For other migration objects, it will just drop existing items and readd, if -force is supported within the underlying function.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration
            Author: Chrissy LeMaire
            Limitations:     Doesn't cover what it doesn't cover (certificates, etc)
                            SQL Server 2000 login migrations have some limitations (server perms aren't migrated)
                            SQL Server 2000 databases cannot be directly migrated to SQL Server 2012 and above.
                            Logins within SQL Server 2012 and above logins cannot be migrated to SQL Server 2008 R2 and below.
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Start-DbaMigration

        .EXAMPLE
            Start-DbaMigration -Source sqlserver\instance -Destination sqlcluster -DetachAttach

            All databases, logins, job objects and sp_configure options will be migrated from sqlserver\instance to sqlcluster. Databases will be migrated using the detach/copy files/attach method. Dbowner will be updated. User passwords, SIDs, database roles and server roles will be migrated along with the login.

        .EXAMPLE
            Start-DbaMigration -Verbose -Source sqlcluster -Destination sql2016 -SourceSqlCredential $scred -ReuseSourceFolderStructure -DestinationSqlCredential $cred -Force -NetworkShare \\fileserver\share\sqlbackups\Migration -BackupRestore

            Migrate databases uses backup/restore. Also migrate logins, database mail, credentials, SQL Agent, Central Management Server, SQL global configuration.

        .EXAMPLE
            Start-DbaMigration -Verbose -Source sqlcluster -Destination sql2016 -NoDatabases -NoLogins

            Migrates everything but logins and databases.

        .EXAMPLE
            Start-DbaMigration -Verbose -Source sqlcluster -Destination sql2016 -DetachAttach -Reattach -SetSourceReadonly

            Migrate databases using detach/copy/attach. Reattach at source and set source databases read-only. Also migrates everything else.

    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    Param (
        [parameter(Position = 1, Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [parameter(Position = 2, Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [parameter(Position = 3, Mandatory = $true, ParameterSetName = "DbAttachDetach")]
        [switch]$DetachAttach,
        [parameter(Position = 4, ParameterSetName = "DbAttachDetach")]
        [switch]$Reattach,
        [parameter(Position = 5, Mandatory = $true, ParameterSetName = "DbBackup")]
        [switch]$BackupRestore,
        [parameter(Position = 6, ParameterSetName = "DbBackup",
                   HelpMessage = "Specify a valid network share in the format \\server\share that can be accessed by your account and both Sql Server service accounts.")]
        [string]$NetworkShare,
        [parameter(Position = 7, ParameterSetName = "DbBackup")]
        [switch]$WithReplace,
        [parameter(Position = 8, ParameterSetName = "DbBackup")]
        [switch]$NoRecovery,
        [parameter(Position = 9, ParameterSetName = "DbBackup")]
        [parameter(Position = 10, ParameterSetName = "DbAttachDetach")]
        [switch]$SetSourceReadOnly,
        [Alias("ReuseFolderStructure")]
        [parameter(Position = 11, ParameterSetName = "DbBackup")]
        [parameter(Position = 12, ParameterSetName = "DbAttachDetach")]
        [switch]$ReuseSourceFolderStructure,
        [parameter(Position = 13, ParameterSetName = "DbBackup")]
        [parameter(Position = 14, ParameterSetName = "DbAttachDetach")]
        [switch]$IncludeSupportDbs,
        [parameter(Position = 15)]
        [PSCredential]$SourceSqlCredential,
        [parameter(Position = 16)]
        [PSCredential]$DestinationSqlCredential,
        [Alias("SkipDatabases")]
        [switch]$NoDatabases,
        [switch]$NoLogins,
        [Alias("SkipJobServer", "NoJobServer")]
        [switch]$NoAgentServer,
        [Alias("SkipCredentials")]
        [switch]$NoCredentials,
        [Alias("SkipLinkedServers")]
        [switch]$NoLinkedServers,
        [Alias("SkipSpConfigure")]
        [switch]$NoSpConfigure,
        [Alias("SkipCentralManagementServer")]
        [switch]$NoCentralManagementServer,
        [Alias("SkipDatabaseMail")]
        [switch]$NoDatabaseMail,
        [Alias("SkipSysDbUserObjects")]
        [switch]$NoSysDbUserObjects,
        [Alias("SkipSystemTriggers")]
        [switch]$NoSystemTriggers,
        [Alias("SkipBackupDevices")]
        [switch]$NoBackupDevices,
        [switch]$NoAudits,
        [switch]$NoEndpoints,
        [switch]$NoExtendedEvents,
        [switch]$NoPolicyManagement,
        [switch]$NoResourceGovernor,
        [switch]$NoServerAuditSpecifications,
        [switch]$NoCustomErrors,
        [switch]$NoDataCollector,
        [switch]$DisableJobsOnDestination,
        [switch]$DisableJobsOnSource,
        [switch]$NoSaRename,
        [switch]$UseLastBackups,
        [switch]$Force,
        [switch]$EnableException
    )

    begin {
        $elapsed = [System.Diagnostics.Stopwatch]::StartNew()
        $started = Get-Date
        $sourceserver = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential

        if ($BackupRestore -eq $false -and $DetachAttach -eq $false -and $NoDatabases -eq $false) {
            Stop-Function -Message "You must specify a database migration method (-BackupRestore or -DetachAttach) or -NoDatabases"
            return
        }
        if (-not $NoDatabases) {
            if (-not $DetachAttach -and !$BackupRestore) {
                Stop-Function -Message "You must specify a migration method using -BackupRestore or -DetachAttach."
                return
            }
        }
        if ($BackupRestore -and (-not $NetworkShare -and -not $UseLastBackups)) {
            Stop-Function -Message "When using -BackupRestore, you must specify -NetworkShare or -UseLastBackups"
            return
        }
        if ($NetworkShare -and $UseLastBackups) {
            Stop-Function -Message "-NetworkShare cannot be used with -UseLastBackups because the backup path is determined by the paths in the last backups"
            return
        }
        if ($DetachAttach -and -not $Reattach -and $Destination.Count -gt 1) {
            Stop-Function -Message "When using -DetachAttach with multiple servers, you must specify -Reattach to reattach database at source"
            return
        }
    }

    process {
        if (Test-FunctionInterrupt) { return }
        
        # testing twice for whatif reasons
        if ($BackupRestore -and (-not $NetworkShare -and -not $UseLastBackups)) {
            Stop-Function -Message "When using -BackupRestore, you must specify -NetworkShare or -UseLastBackups"
            return
        }
        if ($NetworkShare -and $UseLastBackups) {
            Stop-Function -Message "-NetworkShare cannot be used with -UseLastBackups because the backup path is determined by the paths in the last backups"
            return
        }
        if ($DetachAttach -and -not $Reattach -and $Destination.Count -gt 1) {
            Stop-Function -Message "When using -DetachAttach with multiple servers, you must specify -Reattach to reattach database at source"
            return
        }
        if (-not $NoSpConfigure) {
            Write-Message -Level Verbose -Message "Migrating SQL Server Configuration"
            Copy-DbaSpConfigure -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential
        }

        if (-not $NoCustomErrors) {
            Write-Message -Level Verbose -Message "Migrating custom errors (user defined messages)"
            Copy-DbaCustomError -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
        }

        if (-not $NoCredentials) {
            Write-Message -Level Verbose -Message "Migrating SQL credentials"
            Copy-DbaCredential -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
        }

        if (-not $NoDatabaseMail) {
            Write-Message -Level Verbose -Message "Migrating database mail"
            Copy-DbaDatabaseMail -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
        }

        if (-not $NoCentralManagementServer) {
            Write-Message -Level Verbose -Message "Migrating Central Management Server"
            Copy-DbaCentralManagementServer -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
        }

        if (-not $NoBackupDevices) {
            Write-Message -Level Verbose -Message "Migrating Backup Devices"
            Copy-DbaBackupDevice -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
        }

        if (-not $NoLinkedServers) {
            Write-Message -Level Verbose -Message "Migrating linked servers"
            Copy-DbaLinkedServer -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
        }

        if (-not $NoSystemTriggers) {
            Write-Message -Level Verbose -Message "Migrating System Triggers"
            Copy-DbaServerTrigger -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
        }

        if (-not $NoDatabases) {
            # Do it
            Write-Message -Level Verbose -Message "Migrating databases"
            if ($BackupRestore) {
                if ($UseLastBackups) {
                    Copy-DbaDatabase -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -AllDatabases -SetSourceReadOnly:$SetSourceReadOnly -ReuseSourceFolderStructure:$ReuseSourceFolderStructure -BackupRestore -Force:$Force -NoRecovery:$NoRecovery -WithReplace:$WithReplace -IncludeSupportDbs:$IncludeSupportDbs -UseLastBackups:$UseLastBackups
                }
                else {
                    Copy-DbaDatabase -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -AllDatabases -SetSourceReadOnly:$SetSourceReadOnly -ReuseSourceFolderStructure:$ReuseSourceFolderStructure -BackupRestore -NetworkShare $NetworkShare -Force:$Force -NoRecovery:$NoRecovery -WithReplace:$WithReplace -IncludeSupportDbs:$IncludeSupportDbs
                }
            }
            else {
                Copy-DbaDatabase -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -AllDatabases -SetSourceReadOnly:$SetSourceReadOnly -ReuseSourceFolderStructure:$ReuseSourceFolderStructure -DetachAttach:$DetachAttach -Reattach:$Reattach -Force:$Force -IncludeSupportDbs:$IncludeSupportDbs
            }
        }

        if (-not $NoLogins) {
            Write-Message -Level Verbose -Message "Migrating logins"
            $syncit = $NoSaRename -eq $false
            Copy-DbaLogin -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force -SyncSaName:$syncit
        }

        if (-not $NoLogins -and -not $NoDatabases -and -not $NoRecovery) {
            Write-Message -Level Verbose -Message "Updating database owners to match newly migrated logins"
            foreach ($dest in $Destination) {
                $null = Update-SqlDbOwner -Source $sourceserver -Destination $dest -DestinationSqlCredential $DestinationSqlCredential
            }
        }

        if (-not $NoDataCollector) {
            Write-Message -Level Verbose -Message "Migrating Data Collector collection sets"
            Copy-DbaSqlDataCollector -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
        }

        if (-not $NoAudits) {
            Write-Message -Level Verbose -Message "Migrating Audits"
            Copy-DbaServerAudit -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
        }

        if (-not $NoServerAuditSpecifications) {
            Write-Message -Level Verbose -Message "Migrating Server Audit Specifications"
            Copy-DbaServerAuditSpecification -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
        }

        if (-not $NoEndpoints) {
            Write-Message -Level Verbose -Message "Migrating Endpoints"
            Copy-DbaEndpoint -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
        }

        if (-not $NoPolicyManagement) {
            Write-Message -Level Verbose -Message "Migrating Policy Management"
            Copy-DbaSqlPolicyManagement -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
        }

        if (-not $NoResourceGovernor) {
            Write-Message -Level Verbose -Message "Migrating Resource Governor"
            Copy-DbaResourceGovernor -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
        }
        
        if (-not $NoSysDbUserObjects) {
            Write-Message -Level Verbose -Message "Migrating user objects in system databases (this can take a second)."
            If ($Pscmdlet.ShouldProcess($destination, "Copying user objects.")) {
                Copy-DbaSysDbUserObject -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$force
            }
        }
        
        if (-not $NoExtendedEvents) {
            Write-Message -Level Verbose -Message "Migrating Extended Events"
            Copy-DbaExtendedEvent -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
        }

        if (-not $NoAgentServer) {
            Write-Message -Level Verbose -Message "Migrating job server"
            Copy-DbaSqlServerAgent -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -DisableJobsOnDestination:$DisableJobsOnDestination -DisableJobsOnSource:$DisableJobsOnSource -Force:$Force
        }
    }

    end {
        if (Test-FunctionInterrupt) { return }
        $totaltime = ($elapsed.Elapsed.toString().Split(".")[0])
        Write-Message -Level Verbose -Message "SQL Server migration complete."
        Write-Message -Level Verbose -Message "Migration started: $started"
        Write-Message -Level Verbose -Message "Migration completed: $(Get-Date)"
        Write-Message -Level Verbose -Message "Total Elapsed time: $totaltime"
    }
}
function Start-DbaPfDataCollectorSet {
    <#
        .SYNOPSIS
            Starts Performance Monitor Data Collector Set.

        .DESCRIPTION
            Starts Performance Monitor Data Collector Set.

        .PARAMETER ComputerName
            The target computer. Defaults to localhost.

        .PARAMETER Credential
            Allows you to login to $ComputerName using alternative credentials. To use:

            $cred = Get-Credential, then pass $cred object to the -Credential parameter.

        .PARAMETER CollectorSet
            The name of the Collector Set to start.
    
        .PARAMETER NoWait
            If this switch is enabled, the collector is started and the results are returned immediately.
    
        .PARAMETER InputObject
            Accepts the object output by Get-DbaPfDataCollectorSet via the pipeline.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
    
        .NOTES
            Tags: PerfMon
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
    
        .LINK
            https://dbatools.io/Start-DbaPfDataCollectorSet

        .EXAMPLE
            Start-DbaPfDataCollectorSet
    
            Attempts to start all ready Collectors on localhost.

        .EXAMPLE
            Start-DbaPfDataCollectorSet -ComputerName sql2017
    
            Attempts to start all ready Collectors on localhost.
    
        .EXAMPLE
            Start-DbaPfDataCollectorSet -ComputerName sql2017, sql2016 -Credential (Get-Credential) -CollectorSet 'System Correlation'
    
            Starts the 'System Correlation' Collector on sql2017 and sql2016 using alternative credentials.
    
        .EXAMPLE
            Get-DbaPfDataCollectorSet -CollectorSet 'System Correlation' | Start-DbaPfDataCollectorSet
    
            Starts the 'System Correlation' Collector.
    #>
    [CmdletBinding()]
    param (
        [DbaInstance[]]$ComputerName=$env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias("DataCollectorSet")]
        [string[]]$CollectorSet,
        [parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [switch]$NoWait,
        [switch]$EnableException
    )
    begin {
        $wait = $NoWait -eq $false
        
        $setscript = {
            $setname = $args[0]; $wait = $args[1]
            $collectorset = New-Object -ComObject Pla.DataCollectorSet
            $collectorset.Query($setname, $null)
            $null = $collectorset.Start($wait)
        }
    }
    process {
        if (-not $InputObject -or ($InputObject -and (Test-Bound -ParameterName ComputerName))) {
            foreach ($computer in $ComputerName) {
                $InputObject += Get-DbaPfDataCollectorSet -ComputerName $computer -Credential $Credential -CollectorSet $CollectorSet
            }
        }
        
        if ($InputObject) {
            if (-not $InputObject.DataCollectorSetObject) {
                Stop-Function -Message "InputObject is not of the right type. Please use Get-DbaPfDataCollectorSet."
                return
            }
        }
        
        # Check to see if its running first
        foreach ($set in $InputObject) {
            $setname = $set.Name
            $computer = $set.ComputerName
            $status = $set.State
            Write-Message -Level Verbose -Message "$setname on $ComputerName is $status."
            if ($status -eq "Running") {
                Stop-Function -Message "$setname on $computer is already running." -Continue
            }
            if ($status -eq "Disabled") {
                Stop-Function -Message "$setname on $computer is disabled." -Continue
            }
            Write-Message -Level Verbose -Message "Connecting to $computer using Invoke-Command."
            try {
                Invoke-Command2 -ComputerName $computer -Credential $Credential -ScriptBlock $setscript -ArgumentList $setname, $wait -ErrorAction Stop
            }
            catch {
                Stop-Function -Message "Failure starting $setname on $computer." -ErrorRecord $_ -Target $computer -Continue
            }
            
            Get-DbaPfDataCollectorSet -ComputerName $computer -Credential $Credential -CollectorSet $setname
        }
    }
}
function Start-DbaPowerBi {
    <#
        .SYNOPSIS
            Launches the PowerBi dashboard for dbatools

        .DESCRIPTION
            Launches the PowerBi dashboard for dbatools

        .PARAMETER Path
            The location of the pbix file. "$script:ModuleRoot\bin\pbix\dbatools.pbix" by default.

        .PARAMETER InputObject
            Enables piping.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .EXAMPLE
            Start-DbaPowerBi

            Launches PowerBi from "$script:ModuleRoot\bin\pbix\dbatools.pbix" using "C:\windows\Temp\dbatools\" (generated by Update-DbaPowerBiDataSource) as the datasource.

        .EXAMPLE
            Start-DbaPowerBi -Path \\nas\projects\dbatools.pbix

            Launches \\nas\projects\dbatools.pbix

    #>
    [CmdletBinding()]
    param (
        [string]$Path = "$script:ModuleRoot\bin\pbix\dbatools.pbix",
        [parameter(ValueFromPipeline)]
        [pscustomobject]$InputObject,
        [switch]$EnableException
    )

    process {
        if (-not (Test-Path -Path $Path)) {
            Stop-Function -Message "$Path does not exist"
            return
        }

        $association = Get-ItemProperty "Registry::HKEY_Classes_root\.pbix" -ErrorAction SilentlyContinue

        if (-not $association) {
            Stop-Function -Message ".pbix not associated with any program. Please (re)install Power BI"
            return
        }

        if ($Path -match "Program Files") {
            $newpath = "$script:localapp\dbatools.pbix"
            #if ((Test-Path -Path $newpath)) { # Would be nice if we could tell if it needed to be replaced or not
            #I suppose we could use dbatools versioning and wintemp?
            Copy-Item -Path $Path -Destination $newpath -Force -ErrorAction SilentlyContinue
            $Path = $newpath
        }

        try {
            Invoke-Item -Path $path
        }
        catch {
            Stop-Function -Message "Failure" -ErrorRecord $_
            return
        }
    }
}
function Start-DbaSqlService {
    <#
        .SYNOPSIS
            Starts SQL Server services on a computer.

        .DESCRIPTION
            Starts the SQL Server related services on one or more computers. Will follow SQL Server service dependencies.

            Requires Local Admin rights on destination computer(s).

        .PARAMETER ComputerName
            The SQL Server (or server in general) that you're connecting to. This command handles named instances.

        .PARAMETER InstanceName
            Only affects services that belong to the specific instances.

        .PARAMETER Credential
            Credential object used to connect to the computer as a different user.

        .PARAMETER Type
            Use -Type to collect only services of the desired SqlServiceType.
            Can be one of the following: "Agent","Browser","Engine","FullText","SSAS","SSIS","SSRS"

        .PARAMETER Timeout
            How long to wait for the start/stop request completion before moving on. Specify 0 to wait indefinitely.

        .PARAMETER InputObject
            A collection of services from Get-DbaSqlService

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER WhatIf
            Shows what would happen if the cmdlet runs. The cmdlet is not run.

        .PARAMETER Confirm
            Prompts you for confirmation before running the cmdlet.

        .NOTES
            Tags: Service, SqlServer, Instance, Connect
            Author: Kirill Kravtsov( @nvarscar )

            Requires Local Admin rights on destination computer(s).

            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2017 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Start-DbaSqlService

        .EXAMPLE
            Start-DbaSqlService -ComputerName sqlserver2014a

            Starts the SQL Server related services on computer sqlserver2014a.

        .EXAMPLE
            'sql1','sql2','sql3'| Get-DbaSqlService | Start-DbaSqlService

            Gets the SQL Server related services on computers sql1, sql2 and sql3 and starts them.

        .EXAMPLE
            Start-DbaSqlService -ComputerName sql1,sql2 -Instance MSSQLSERVER

            Starts the SQL Server services related to the default instance MSSQLSERVER on computers sql1 and sql2.

        .EXAMPLE
            Start-DbaSqlService -ComputerName $MyServers -Type SSRS

            Starts the SQL Server related services of type "SSRS" (Reporting Services) on computers in the variable MyServers.
    #>
    [CmdletBinding(DefaultParameterSetName = "Server", SupportsShouldProcess = $true)]
    Param (
        [Parameter(ParameterSetName = "Server", Position = 1)]
        [Alias("cn", "host", "Server")]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [Alias("Instance")]
        [string[]]$InstanceName,
        [ValidateSet("Agent", "Browser", "Engine", "FullText", "SSAS", "SSIS", "SSRS")]
        [string[]]$Type,
        [parameter(ValueFromPipeline = $true, Mandatory = $true, ParameterSetName = "Service")]
        [Alias("ServiceCollection")]
        [object[]]$InputObject,
        [int]$Timeout = 30,
        [PSCredential]$Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        $processArray = @()
        if ($PsCmdlet.ParameterSetName -eq "Server") {
            $serviceParams = @{ ComputerName = $ComputerName }
            if ($InstanceName) { $serviceParams.InstanceName = $InstanceName }
            if ($Type) { $serviceParams.Type = $Type }
            if ($Credential) { $serviceParams.Credential = $Credential }
            if ($EnableException) { $serviceParams.Silent = $EnableException }
            $InputObject = Get-DbaSqlService @serviceParams
        }
    }
    process {
        #Get all the objects from the pipeline before proceeding
        $processArray += $InputObject
    }
    end {
        $processArray = $processArray | Where-Object { (!$InstanceName -or $_.InstanceName -in $InstanceName) -and (!$Type -or $_.ServiceType -in $Type) }
        if ($processArray) {
            Update-ServiceStatus -InputObject $processArray -Action 'start' -Timeout $Timeout -EnableException $EnableException
        }
        else { Stop-Function -EnableException $EnableException -Message "No SQL Server services found with current parameters." -Category ObjectNotFound }
    }
}
function Start-DbaTrace {
     <#
        .SYNOPSIS
        Starts SQL Server traces

        .DESCRIPTION
        Starts SQL Server traces

        .PARAMETER SqlInstance
        The target SQL Server instance

        .PARAMETER SqlCredential
        Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Id
        A list of trace ids

        .PARAMETER InputObject
        Internal parameter for piping

        .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
        Tags: Security, Trace
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

       .EXAMPLE
        Start-DbaTrace -SqlInstance sql2008

        Starts all traces on sql2008

        .EXAMPLE
        Start-DbaTrace -SqlInstance sql2008 -Id 1

        Starts all trace with ID 1 on sql2008

        .EXAMPLE
        Get-DbaTrace -SqlInstance sql2008 | Out-GridView -PassThru | Start-DbaTrace

        Starts selected traces on sql2008

#>
    [CmdletBinding()]
    Param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [int[]]$Id,
        [parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [switch]$EnableException
    )
    process {
        if (-not $InputObject -and $SqlInstance) {
            $InputObject = Get-DbaTrace -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Id $Id
        }

        foreach ($trace in $InputObject) {
            if (-not $trace.id -and -not $trace.Parent) {
                Stop-Function -Message "Input is of the wrong type. Use Get-DbaTrace." -Continue
                return
            }

            $server = $trace.Parent
            $traceid = $trace.id
            $default = Get-DbaTrace -SqlInstance $server -Default

            if ($default.id -eq $traceid) {
                Stop-Function -Message "The default trace on $server cannot be started. Use Set-DbaSpConfigure to turn it on." -Continue
            }

            $sql = "sp_trace_setstatus $traceid, 1"

            try {
                $server.Query($sql)
                Get-DbaTrace -SqlInstance $server -Id $traceid
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_ -Target $server -Continue
                return
            }
        }
    }
}
function Start-DbaXESession {
    <#
        .SYNOPSIS
            Starts Extended Events sessions.

        .DESCRIPTION
            This script starts Extended Events sessions on a SQL Server instance.

        .PARAMETER SqlInstance
            Target SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Session
            Only start specific Extended Events sessions.

        .PARAMETER AllSessions
            Start all Extended Events sessions on an instance, ignoring the packaged sessions: AlwaysOn_health, system_health, telemetry_xevents.

        .PARAMETER InputObject
            Internal parameter to support piping from Get-DbaXESession

        .PARAMETER StopAt
            Specifies a datetime at which the session will be stopped. This is done via a self-deleting schedule.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Author: Doug Meyers
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Start-DbaXESession

        .EXAMPLE
            Start-DbaXESession -SqlInstance sqlserver2012 -AllSessions

            Starts all Extended Event Session on the sqlserver2014 instance.

        .EXAMPLE
            Start-DbaXESession -SqlInstance sqlserver2012 -Session xesession1,xesession2

            Starts the xesession1 and xesession2 Extended Event sessions.

        .EXAMPLE
            Start-DbaXESession -SqlInstance sqlserver2012 -Session xesession1,xesession2 -StopAt (Get-Date).AddMinutes(30)

            Starts the xesession1 and xesession2 Extended Event sessions and stops them in 30 minutes.

        .EXAMPLE
            Get-DbaXESession -SqlInstance sqlserver2012 -Session xesession1 | Start-DbaXESession

            Starts the sessions returned from the Get-DbaXESession function.

    #>
    [CmdletBinding(DefaultParameterSetName = 'Session')]
    param (
        [parameter(Position = 1, Mandatory, ParameterSetName = 'Session')]
        [parameter(Position = 1, Mandatory, ParameterSetName = 'All')]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [parameter(ParameterSetName = 'Session')]
        [parameter(ParameterSetName = 'All')]
        [PSCredential]$SqlCredential,
        [parameter(Mandatory, ParameterSetName = 'Session')]
        [Alias("Sessions")]
        [object[]]$Session,
        [datetime]$StopAt,
        [parameter(Mandatory, ParameterSetName = 'All')]
        [switch]$AllSessions,
        [parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Object')]
        [Microsoft.SqlServer.Management.XEvent.Session[]]$InputObject,
        [switch]$EnableException
    )

    begin {
        # Start each XESession
        function Start-XESessions {
            [CmdletBinding()]
            param ([Microsoft.SqlServer.Management.XEvent.Session[]]$xeSessions)

            foreach ($xe in $xeSessions) {
                $instance = $xe.Parent.Name
                $session = $xe.Name
                if (-Not $xe.isRunning) {
                    Write-Message -Level Verbose -Message "Starting XEvent Session $session on $instance."
                    try {
                        $xe.Start()
                    }
                    catch {
                        Stop-Function -Message "Could not start XEvent Session on $instance." -Target $session -ErrorRecord $_ -Continue
                    }
                }
                else {
                    Write-Message -Level Warning -Message "$session on $instance is already running."
                }
                Get-DbaXESession -SqlInstance $xe.Parent -Session $session
            }
        }

        function New-StopJob {
            [CmdletBinding()]
            param (
                [Microsoft.SqlServer.Management.XEvent.Session[]]$xeSessions,
                [datetime]$StopAt
            )

            foreach ($xe in $xeSessions) {
                $server = $xe.Parent
                $session = $xe.Name
                $name = "XE Session Stop - $session"

                # Setup the schedule time
                $time = ($StopAt).ToString("HHmmss")

                # Create the schedule
                $schedule = New-DbaAgentSchedule -SqlInstance $server -Schedule $name -FrequencyType Once -StartTime ($StopAt).ToString("HHmmss") -Force

                # Create the job and attach the schedule
                $job = New-DbaAgentJob -SqlInstance $server -Job $name -Schedule $schedule -DeleteLevel Always -Force

                # Create the job step
                $sql = "ALTER EVENT SESSION [$session] ON SERVER STATE = stop;"
                $jobstep = New-DbaAgentJobStep -SqlInstance $server -Job $job -StepName 'T-SQL Stop' -Subsystem TransactSql -Command $sql -Force
            }
        }
    }
    process {
        if ($InputObject) {
            Start-XESessions $InputObject
        }
        else {
            foreach ($instance in $SqlInstance) {
                $xeSessions = Get-DbaXESession -SqlInstance $instance -SqlCredential $SqlCredential

                # Filter xeSessions based on parameters
                if ($Session) {
                    $xeSessions = $xeSessions | Where-Object { $_.Name -in $Session }
                }
                elseif ($AllSessions) {
                    $systemSessions = @('AlwaysOn_health', 'system_health', 'telemetry_xevents')
                    $xeSessions = $xeSessions | Where-Object { $_.Name -notin $systemSessions }
                }

                Start-XESessions $xeSessions

                if ($StopAt) {
                    New-StopJob -xeSessions $xeSessions -StopAt $stopat
                }
            }
        }
    }
}
function Start-DbaXESmartTarget {
    <#
        .SYNOPSIS
            XESmartTarget runs as a client application for an Extended Events session running on a SQL Server instance.

        .DESCRIPTION
            XESmartTarget offers the ability to set up complex actions in response to Extended Events captured in sessions, without writing a single line of code.

            See more at https://github.com/spaghettidba/XESmartTarget/wiki

        .PARAMETER SqlInstance
            Target SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Session
            Name of the Extended Events session to attach to.

            You can monitor a single session with an instance of XESmartTarget. In case you need to perform action on multiple sessions, run an additional instance of XESmartTarget, with its own configuration file.

        .PARAMETER Database
            Specifies the name of the database that contains the target table.

        .PARAMETER FailOnProcessingError
            If this switch is enabled, the a processing error will trigger a failure.

        .PARAMETER Responder
            The list of responses can include zero or more Response objects, each to be configured by specifying values for their public members.

        .PARAMETER Template
            Path to the dbatools built-in templates

        .PARAMETER NotAsJob
            If this switch is enabled, output will be sent to screen indefinitely. BY default, a job will be run in the background.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
            SmartTarget: by Gianluca Sartori (@spaghettidba)

        .LINK
            https://dbatools.io/Start-DbaXESmartTarget
            https://github.com/spaghettidba/XESmartTarget/wiki

        .EXAMPLE
            $response = New-DbaXESmartQueryExec -SqlInstance sql2017 -Database dbadb -Query "update table set whatever = 1"
            Start-DbaXESmartTarget -SqlInstance sql2017 -Session deadlock_tracker -Responder $response

            Executes a T-SQL command against dbadb on sql2017 whenever a deadlock event is recorded.

        .EXAMPLE
            $response = New-DbaXESmartQueryExec -SqlInstance sql2017 -Database dbadb -Query "update table set whatever = 1"
            $params = @{
                SmtpServer = "smtp.ad.local"
                To = "[email protected]"
                Sender = "[email protected]"
                Subject = "Query executed"
                Body = "Query executed at {collection_time}"
                Attachment = "batch_text"
                AttachmentFileName = "query.sql"
            }
            $emailresponse = New-DbaXESmartEmail @params
            Start-DbaXESmartTarget -SqlInstance sql2017 -Session querytracker -Responder $response, $emailresponse

            Executes a T-SQL command against dbadb on sql2017 and sends an email whenever a querytracker event is recorded.

        .EXAMPLE
            $columns = "cpu_time", "duration", "physical_reads", "logical_reads", "writes", "row_count", "batch_text"
            $response = New-DbaXESmartTableWriter -SqlInstance sql2017 -Database dbadb -Table deadlocktracker -OutputColumns $columns -Filter "duration > 10000"
            Start-DbaXESmartTarget -SqlInstance sql2017 -Session deadlock_tracker -Responder $response

            Writes Extended Events to the deadlocktracker table in dbadb on sql2017.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string]$Database,
        [parameter(Mandatory)]
        [string]$Session,
        [switch]$FailOnProcessingError,
        [object[]]$Responder,
        [string[]]$Template,
        [switch]$NotAsJob,
        [switch]$EnableException
    )
    begin {
        function Start-SmartFunction {
            [CmdletBinding()]
            param (
                [parameter(Mandatory, ValueFromPipeline)]
                [Alias("ServerInstance", "SqlServer")]
                [DbaInstanceParameter[]]$SqlInstance,
                [PSCredential]$SqlCredential,
                [string]$Database,
                [parameter(Mandatory)]
                [string]$Session,
                [switch]$FailOnProcessingError,
                [object[]]$Responder,
                [string[]]$Template,
                [switch]$NotAsJob,
                [switch]$EnableException
            )
            begin {
                try {
                    Add-Type -Path "$script:PSModuleRoot\bin\XESmartTarget\XESmartTarget.Core.dll" -ErrorAction Stop
                }
                catch {
                    Stop-Function -Message "Could not load XESmartTarget.Core.dll" -ErrorRecord $_ -Target "XESmartTarget"
                    return
                }
            }
            process {
                if (Test-FunctionInterrupt) { return }

                foreach ($instance in $SqlInstance) {
                    try {
                        Write-Message -Level Verbose -Message "Connecting to $instance."
                        $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 11
                    }
                    catch {
                        Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                    }

                    $target = New-Object -TypeName XESmartTarget.Core.Target
                    $target.ServerName = $instance
                    $target.SessionName = $Session
                    $target.FailOnProcessingError = $FailOnProcessingError

                    if ($SqlCredential) {
                        $target.UserName = $SqlCredential.UserName
                        $target.Password = $SqlCredential.GetNetworkCredential().Password
                    }

                    foreach ($response in $Responder) {
                        $target.Responses.Add($response)
                    }

                    try {
                        $target.Start()
                    }
                    catch {
                        $message = $_.Exception.InnerException.InnerException | Out-String

                        if ($message) {
                            Stop-Function -Message $message -Target "XESmartTarget" -Continue
                        }
                        else {
                            Stop-Function -Message "Failure" -Target "XESmartTarget" -ErrorRecord $_ -Continue
                        }
                    }
                }
            }
        }
    }
    process {
        foreach ($instance in $SqlInstance) {
            if (-not ($xesession = Get-DbaXESession -SqlInstance $instance -SqlCredential $SqlCredential -Session $Session)) {
                Stop-Function -Message "Session $Session does not exist on $instance."
                return
            }
            if ($xesession.Status -ne "Running") {
                Stop-Function -Message "Session $Session on $instance is not running."
                return
            }
        }

        if ($NotAsJob) {
            Start-SmartFunction @PSBoundParameters
        }
        else {
            $date = (Get-Date -UFormat "%H%M%S") #"%m%d%Y%H%M%S"
            Start-Job -Name "XESmartTarget-$session-$date" -ArgumentList $PSBoundParameters, $script:PSModuleRoot -ScriptBlock {
                param (
                    $Parameters,
                    $ModulePath
                )
                Import-Module "$ModulePath\dbatools.psd1"
                Add-Type -Path "$ModulePath\bin\XESmartTarget\XESmartTarget.Core.dll" -ErrorAction Stop
                $params = @{
                    SqlInstance    = $Parameters.SqlInstance.InputObject
                    Database       = $Parameters.Database
                    Session        = $Parameters.Session
                    Responder      = @()
                }
                if ($Parameters.SqlCredential) {
                    $params["SqlCredential"] = $Parameters.SqlCredential
                }
                foreach ($responder in $Parameters.Responder) {
                    $typename = $responder.PSObject.TypeNames[0] -replace "^Deserialized\.", ""
                    $newResponder = New-Object -TypeName $typename
                    foreach ($property in $responder.PSObject.Properties) {
                        if ($property.Value) {
                            $name = $property.Name
                            $newResponder.$name = $property.Value
                        }
                    }
                    $params["Responder"] += $newResponder
                }

                Start-DbaXESmartTarget @params -NotAsJob -FailOnProcessingError
            } | Select-Object -Property ID, Name, State
        }
    }
}
function Stop-DbaAgentJob {
    <#
        .SYNOPSIS
            Stops a running SQL Server Agent Job.

        .DESCRIPTION
            This command stops a job then returns connected SMO object for SQL Agent Job information for each instance(s) of SQL Server.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Job
            The job(s) to process - this list is auto-populated from the server. If unspecified, all jobs will be processed.

        .PARAMETER ExcludeJob
            The job(s) to exclude - this list is auto-populated from the server.

        .PARAMETER Wait
            Wait for output until the job has completely stopped

        .PARAMETER InputObject
            Internal parameter that enables piping

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Job, Agent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Stop-DbaAgentJob

        .EXAMPLE
            Stop-DbaAgentJob -SqlInstance localhost

            Stops all running SQL Agent Jobs on the local SQL Server instance

        .EXAMPLE
            Get-DbaAgentJob -SqlInstance sql2016 -Job cdc.DBWithCDC_capture | Stop-DbaAgentJob

            Stops the cdc.DBWithCDC_capture SQL Agent Job on sql2016

        .EXAMPLE
            Stop-DbaAgentJob -SqlInstance sql2016 -Job cdc.DBWithCDC_capture

            Stops the cdc.DBWithCDC_capture SQL Agent Job on sql2016

    #>
    [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = "Default")]
    param (
        [parameter(Mandatory, ParameterSetName = "Instance")]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$Job,
        [string[]]$ExcludeJob,
        [parameter(Mandatory, ValueFromPipeline, ParameterSetName = "Object")]
        [Microsoft.SqlServer.Management.Smo.Agent.Job[]]$InputObject,
        [switch]$Wait,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Verbose "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $InputObject += $server.JobServer.Jobs

            if ($Job) {
                $InputObject = $InputObject | Where-Object Name -In $Job
            }
            if ($ExcludeJob) {
                $InputObject = $InputObject | Where-Object Name -NotIn $ExcludeJob
            }
        }

        foreach ($currentjob in $InputObject) {

            $server = $currentjob.Parent.Parent
            $status = $currentjob.CurrentRunStatus

            if ($status -eq 'Idle') {
                Stop-Function -Message "$currentjob on $server is idle ($status)" -Target $currentjob -Continue
            }

            If ($Pscmdlet.ShouldProcess($server, "Stopping job $currentjob")) {
                $null = $currentjob.Stop()
                Start-Sleep -Milliseconds 300
                $currentjob.Refresh()

                $waits = 0
                while ($currentjob.CurrentRunStatus -ne 'Idle' -and $waits++ -lt 10) {
                    Start-Sleep -Milliseconds 100
                    $currentjob.Refresh()
                }

                if ($wait) {
                    while ($currentjob.CurrentRunStatus -ne 'Idle') {
                        Write-Message -Level Output -Message "$currentjob is $($currentjob.CurrentRunStatus)"
                        Start-Sleep -Seconds 3
                        $currentjob.Refresh()
                    }
                    $currentjob
                }
                else {
                    $currentjob
                }
            }
        }
    }
}
function Stop-DbaPfDataCollectorSet {
    <#
        .SYNOPSIS
            Stops Performance Monitor Data Collector Set.

        .DESCRIPTION
            Stops Performance Monitor Data Collector Set.

        .PARAMETER ComputerName
            The target computer. Defaults to localhost.

        .PARAMETER Credential
            Allows you to login to $ComputerName using alternative credentials. To use:

            $cred = Get-Credential, then pass $cred object to the -Credential parameter.

        .PARAMETER CollectorSet
            The name of the Collector Set to stop.
    
        .PARAMETER NoWait
            If this switch is enabled, the collector is stopped and the results are returned immediately.
    
        .PARAMETER InputObject
            Accepts the object output by Get-DbaPfDataCollectorSet via the pipeline.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
    
        .NOTES
            Tags: PerfMon
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
    
        .LINK
            https://dbatools.io/Stop-DbaPfDataCollectorSet

        .EXAMPLE
            Stop-DbaPfDataCollectorSet
    
            Attempts to stop all ready Collectors on localhost.

        .EXAMPLE
            Stop-DbaPfDataCollectorSet -ComputerName sql2017
    
            Attempts to stop all ready Collectors on localhost.
    
        .EXAMPLE
            Stop-DbaPfDataCollectorSet -ComputerName sql2017, sql2016 -Credential (Get-Credential) -CollectorSet 'System Correlation'
    
            Stops the 'System Correlation' Collector on sql2017 and sql2016 using alternative credentials.
    
        .EXAMPLE
            Get-DbaPfDataCollectorSet -CollectorSet 'System Correlation' | Stop-DbaPfDataCollectorSet
    
            Stops the 'System Correlation' Collector.
    #>
    [CmdletBinding()]
    param (
        [DbaInstance[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias("DataCollectorSet")]
        [string[]]$CollectorSet,
        [parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [switch]$NoWait,
        [switch]$EnableException
    )
    begin {
        $sets = @()
        $wait = $NoWait -eq $false
        
        $setscript = {
            $setname = $args[0]; $wait = $args[1]
            $collectorset = New-Object -ComObject Pla.DataCollectorSet
            $collectorset.Query($setname, $null)
            $null = $collectorset.Stop($wait)
        }
    }
    process {
        if (-not $InputObject -or ($InputObject -and (Test-Bound -ParameterName ComputerName))) {
            foreach ($computer in $ComputerName) {
                $InputObject += Get-DbaPfDataCollectorSet -ComputerName $computer -Credential $Credential -CollectorSet $CollectorSet
            }
        }
        
        if ($InputObject) {
            if (-not $InputObject.DataCollectorSetObject) {
                Stop-Function -Message "InputObject is not of the right type. Please use Get-DbaPfDataCollectorSet."
                return
            }
        }
        
        # Check to see if its running first
        foreach ($set in $InputObject) {
            $setname = $set.Name
            $computer = $set.ComputerName
            $status = $set.State
            
            Write-Message -Level Verbose -Message "$setname on $ComputerName is $status."
            if ($status -ne "Running") {
                Stop-Function -Message "$setname on $computer is already stopped." -Continue
            }
            Write-Message -Level Verbose -Message "Connecting to $computer using Invoke-Command."
            try {
                Invoke-Command2 -ComputerName $computer -Credential $Credential -ScriptBlock $setscript -ArgumentList $setname, $wait -ErrorAction Stop
            }
            catch {
                Stop-Function -Message "Failure stopping $setname on $computer." -ErrorRecord $_ -Target $computer -Continue
            }
            
            Get-DbaPfDataCollectorSet -ComputerName $computer -Credential $Credential -CollectorSet $setname
        }
    }
}
function Stop-DbaProcess {
    <#
        .SYNOPSIS
            This command finds and kills SQL Server processes.

        .DESCRIPTION
            This command kills all spids associated with a spid, login, host, program or database.

            If you are attempting to kill your own login sessions, the process performing the kills will be skipped.

        .PARAMETER SqlInstance
            The SQL Server instance.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Spid
            Specifies one or more spids to be killed. Options for this parameter are auto-populated from the server.

        .PARAMETER Login
            Specifies one or more login names whose processes will be killed. Options for this parameter are auto-populated from the server and only login names that have active processes are offered.

        .PARAMETER Hostname
            Specifies one or more client hostnames whose processes will be killed. Options for this parameter are auto-populated from the server and only hostnames that have active processes are offered.

        .PARAMETER Program
            Specifies one or more client programs whose processes will be killed. Options for this parameter are auto-populated from the server and only programs that have active processes are offered.

        .PARAMETER Database
            Specifies one or more databases whose processes will be killed. Options for this parameter are auto-populated from the server and only databases that have active processes are offered.

            This parameter is auto-populated from -SqlInstance and allows only database names that have active processes. You can specify one or more Databases whose processes will be killed.

        .PARAMETER ExcludeSpid
            Specifies one or more spids which will not be killed. Options for this parameter are auto-populated from the server.

            Exclude is the last filter to run, so even if a spid matches (for example) Hosts, if it's listed in Exclude it wil be excluded.

        .PARAMETER InputObject
            This is the process object passed by Get-DbaProcess if using a pipeline.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Processes
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Stop-DbaProcess

        .EXAMPLE
            Stop-DbaProcess -SqlInstance sqlserver2014a -Login base\ctrlb, sa

            Finds all processes for base\ctrlb and sa on sqlserver2014a, then kills them. Uses Windows Authentication to login to sqlserver2014a.

        .EXAMPLE
            Stop-DbaProcess -SqlInstance sqlserver2014a -SqlCredential $credential -Spids 56, 77

            Finds processes for spid 56 and 57, then kills them. Uses alternative (SQL or Windows) credentials to login to sqlserver2014a.

        .EXAMPLE
            Stop-DbaProcess -SqlInstance sqlserver2014a -Programs 'Microsoft SQL Server Management Studio'

            Finds processes that were created in Microsoft SQL Server Management Studio, then kills them.

        .EXAMPLE
            Stop-DbaProcess -SqlInstance sqlserver2014a -Hosts workstationx, server100

            Finds processes that were initiated by hosts (computers/clients) workstationx and server 1000, then kills them.

        .EXAMPLE
            Stop-DbaProcess -SqlInstance sqlserver2014  -Database tempdb -WhatIf

            Shows what would happen if the command were executed.

        .EXAMPLE
            Get-DbaProcess -SqlInstance sql2016 -Programs 'dbatools PowerShell module - dbatools.io' | Stop-DbaProcess

            Finds processes that were created with dbatools, then kills them.

    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess)]
    Param (
        [parameter(Mandatory, ParameterSetName = "Server")]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [int[]]$Spid,
        [int[]]$ExcludeSpid,
        [string[]]$Database,
        [string[]]$Login,
        [string[]]$Hostname,
        [string[]]$Program,
        [parameter(ValueFromPipeline = $true, Mandatory = $true, ParameterSetName = "Process")]
        [object[]]$InputObject,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        if (Test-FunctionInterrupt) { return }

        if (!$InputObject) {
            $InputObject = Get-DbaProcess @PSBoundParameters
        }

        foreach ($session in $InputObject) {
            $sourceserver = $session.Parent

            if (!$sourceserver) {
                Stop-Function -Message "Only process objects can be passed through the pipeline." -Category InvalidData -Target $session
                return
            }

            $currentspid = $session.spid

            if ($sourceserver.ConnectionContext.ProcessID -eq $currentspid) {
                Write-Message -Level Warning -Message "Skipping spid $currentspid because you cannot use KILL to kill your own process." -Target $session
                Continue
            }

            if ($Pscmdlet.ShouldProcess($sourceserver, "Killing spid $currentspid")) {
                try {
                    $sourceserver.KillProcess($currentspid)
                    [pscustomobject]@{
                        SqlInstance = $sourceserver.name
                        Spid        = $session.Spid
                        Login       = $session.Login
                        Host        = $session.Host
                        Database    = $session.Database
                        Program     = $session.Program
                        Status      = 'Killed'
                    }
                }
                catch {
                    Stop-Function -Message "Couldn't kill spid $currentspid." -Target $session -ErrorRecord $_ -Continue
                }
            }
        }
    }
}
function Stop-DbaSqlService {
    <#
        .SYNOPSIS
            Stops SQL Server services on a computer.

        .DESCRIPTION
            Stops the SQL Server related services on one or more computers. Will follow SQL Server service dependencies.

            Requires Local Admin rights on destination computer(s).

        .PARAMETER ComputerName
            The SQL Server (or server in general) that you're connecting to. This command handles named instances.

        .PARAMETER InstanceName
            Only affects services that belong to the specific instances.

        .PARAMETER Credential
            Credential object used to connect to the computer as a different user.

        .PARAMETER Type
            Use -Type to collect only services of the desired SqlServiceType.
            Can be one of the following: "Agent","Browser","Engine","FullText","SSAS","SSIS","SSRS"

        .PARAMETER Timeout
            How long to wait for the start/stop request completion before moving on. Specify 0 to wait indefinitely.

        .PARAMETER InputObject
            A collection of services from Get-DbaSqlService

        .PARAMETER Force
            Use this switch to stop dependent services before proceeding with the specified service

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER WhatIf
            Shows what would happen if the cmdlet runs. The cmdlet is not run.

        .PARAMETER Confirm
            Prompts you for confirmation before running the cmdlet.

        .PARAMETER Force
            Will stop dependent SQL Server agents when stopping Engine services.

        .NOTES
            Tags: Service, SqlServer, Instance, Connect
            Author: Kirill Kravtsov( @nvarscar )

            Requires Local Admin rights on destination computer(s).

            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2017 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Stop-DbaSqlService

        .EXAMPLE
            Stop-DbaSqlService -ComputerName sqlserver2014a

            Stops the SQL Server related services on computer sqlserver2014a.

        .EXAMPLE
            'sql1','sql2','sql3'| Get-DbaSqlService | Stop-DbaSqlService

            Gets the SQL Server related services on computers sql1, sql2 and sql3 and stops them.

        .EXAMPLE
            Stop-DbaSqlService -ComputerName sql1,sql2 -Instance MSSQLSERVER

            Stops the SQL Server services related to the default instance MSSQLSERVER on computers sql1 and sql2.

        .EXAMPLE
            Stop-DbaSqlService -ComputerName $MyServers -Type SSRS

            Stops the SQL Server related services of type "SSRS" (Reporting Services) on computers in the variable MyServers.

        .EXAMPLE
            Stop-DbaSqlService -ComputerName sql1 -Type Engine -Force

            Stops SQL Server database engine services on sql1 forcing dependent SQL Server Agent services to stop as well.
    #>
    [CmdletBinding(DefaultParameterSetName = "Server", SupportsShouldProcess = $true)]
    Param (
        [Parameter(ParameterSetName = "Server", Position = 1)]
        [Alias("cn", "host", "Server")]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [Alias("Instance")]
        [string[]]$InstanceName,
        [ValidateSet("Agent", "Browser", "Engine", "FullText", "SSAS", "SSIS", "SSRS")]
        [string[]]$Type,
        [parameter(ValueFromPipeline = $true, Mandatory = $true, ParameterSetName = "Service")]
        [Alias("ServiceCollection")]
        [object[]]$InputObject,
        [int]$Timeout = 30,
        [PSCredential]$Credential,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        $processArray = @()
        if ($PsCmdlet.ParameterSetName -eq "Server") {
            $serviceParams = @{ ComputerName = $ComputerName }
            if ($InstanceName) { $serviceParams.InstanceName = $InstanceName }
            if ($Type) { $serviceParams.Type = $Type }
            if ($Credential) { $serviceParams.Credential = $Credential }
            if ($EnableException) { $serviceParams.Silent = $EnableException }
            $InputObject = Get-DbaSqlService @serviceParams
        }
    }
    process {
        #Get all the objects from the pipeline before proceeding
        $processArray += $InputObject
    }
    end {
        $processArray = [array]($processArray | Where-Object { (!$InstanceName -or $_.InstanceName -in $InstanceName) -and (!$Type -or $_.ServiceType -in $Type) })
        foreach ($service in $processArray) {
            if ($Force -and $service.ServiceType -eq 'Engine' -and !($processArray | Where-Object { $_.ServiceType -eq 'Agent' -and $_.InstanceName -eq $service.InstanceName -and $_.ComputerName -eq $service.ComputerName })) {
                #Construct parameters to call Get-DbaSqlService
                $serviceParams = @{
                    ComputerName = $service.ComputerName
                    InstanceName = $service.InstanceName
                    Type         = 'Agent'
                }
                if ($Credential) { $serviceParams.Credential = $Credential }
                if ($EnableException) { $serviceParams.Silent = $EnableException }
                $processArray += @(Get-DbaSqlService @serviceParams)
            }
        }
        if ($processArray) {
            Update-ServiceStatus -InputObject $processArray -Action 'stop' -Timeout $Timeout -EnableException $EnableException
        }
        else { Stop-Function -EnableException $EnableException -Message "No SQL Server services found with current parameters." -Category ObjectNotFound }
    }
}
function Stop-DbaTrace {
     <#
        .SYNOPSIS
        Stops SQL Server traces

        .DESCRIPTION
        Stops SQL Server traces

        .PARAMETER SqlInstance
        The target SQL Server instance

        .PARAMETER SqlCredential
        Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Id
        A list of trace ids

        .PARAMETER InputObject
        Internal parameter for piping

        .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
        Tags: Security, Trace
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

       .EXAMPLE
        Stop-DbaTrace -SqlInstance sql2008

        Stops all traces on sql2008

        .EXAMPLE
        Stop-DbaTrace -SqlInstance sql2008 -Id 1

        Stops all trace with ID 1 on sql2008

        .EXAMPLE
        Get-DbaTrace -SqlInstance sql2008 | Out-GridView -PassThru | Stop-DbaTrace

        Stops selected traces on sql2008

#>
    [CmdletBinding()]
    Param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [int[]]$Id,
        [parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [switch]$EnableException
    )
    process {
        if (-not $InputObject -and $SqlInstance) {
            $InputObject = Get-DbaTrace -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Id $Id
        }

        foreach ($trace in $InputObject) {
            if (-not $trace.id -and -not $trace.Parent) {
                Stop-Function -Message "Input is of the wrong type. Use Get-DbaTrace." -Continue
                return
            }

            $server = $trace.Parent
            $traceid = $trace.id
            $default = Get-DbaTrace -SqlInstance $server -Default

            if ($default.id -eq $traceid) {
                Stop-Function -Message "The default trace on $server cannot be stopped. Use Set-DbaSpConfigure to turn it off." -Continue
            }

            $sql = "sp_trace_setstatus $traceid, 0"

            try {
                $server.Query($sql)
                $output = Get-DbaTrace -SqlInstance $server -Id $traceid
                if (-not $output) {
                    $output = [PSCustomObject]@{
                        ComputerName            = $server.ComputerName
                        InstanceName            = $server.ServiceName
                        SqlInstance             = $server.DomainInstanceName
                        Id                      = $traceid
                        Status                  = $null
                        IsRunning               = $false
                        Path                    = $null
                        MaxSize                 = $null
                        StopTime                = $null
                        MaxFiles                = $null
                        IsRowset                = $null
                        IsRollover              = $null
                        IsShutdown              = $null
                        IsDefault               = $null
                        BufferCount             = $null
                        BufferSize              = $null
                        FilePosition            = $null
                        ReaderSpid              = $null
                        StartTime               = $null
                        LastEventTime           = $null
                        EventCount              = $null
                        DroppedEventCount       = $null
                        Parent                  = $server
                    } | Select-DefaultView -Property 'ComputerName', 'InstanceName', 'SqlInstance', 'Id', 'IsRunning'
                }
                $output
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_ -Target $server -Continue
                return
            }
        }
    }
}
function Stop-DbaXESession {
    <#
        .SYNOPSIS
            Stops Extended Events sessions.

        .DESCRIPTION
            This script stops Extended Events sessions on a SQL Server instance.

        .PARAMETER SqlInstance
            Target SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Session
            Specifies individual Extended Events sessions to stop.

        .PARAMETER AllSessions
            If this switch is enabled, all Extended Events sessions will be stopped except the packaged sessions AlwaysOn_health, system_health, telemetry_xevents.

        .PARAMETER InputObject
            Accepts the object output by Get-DbaXESession as the list of sessions to be stopped.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Author: Doug Meyers
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Stop-DbaXESession

        .EXAMPLE
            Stop-DbaXESession -SqlInstance sqlserver2012 -AllSessions

            Stops all Extended Event Session on the sqlserver2014 instance.

        .EXAMPLE
            Stop-DbaXESession -SqlInstance sqlserver2012 -Session xesession1,xesession2

            Stops the xesession1 and xesession2 Extended Event sessions.

        .EXAMPLE
            Get-DbaXESession -SqlInstance sqlserver2012 -Session xesession1 | Stop-DbaXESession

            Stops the sessions returned from the Get-DbaXESession function.
    #>
    [CmdletBinding(DefaultParameterSetName = 'Session')]
    param (
        [parameter(Position = 1, Mandatory, ParameterSetName = 'Session')]
        [parameter(Position = 1, Mandatory, ParameterSetName = 'All')]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,

        [parameter(ParameterSetName = 'Session')]
        [parameter(ParameterSetName = 'All')]
        [PSCredential]$SqlCredential,

        [parameter(Mandatory, ParameterSetName = 'Session')]
        [Alias("Sessions")]
        [object[]]$Session,

        [parameter(Mandatory, ParameterSetName = 'All')]
        [switch]$AllSessions,

        [parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Object')]
        [Microsoft.SqlServer.Management.XEvent.Session[]]$InputObject,
        [switch]$EnableException
    )

    begin {
        # Stop each XESession
        function Stop-XESessions {
            [CmdletBinding()]
            param ([Microsoft.SqlServer.Management.XEvent.Session[]]$xeSessions)

            foreach ($xe in $xeSessions) {
                $instance = $xe.Parent.Name
                $session = $xe.Name
                if ($xe.isRunning) {
                    Write-Message -Level Verbose -Message "Stopping XEvent Session $session on $instance."
                    try {
                        $xe.Stop()
                    }
                    catch {
                        Stop-Function -Message "Could not stop XEvent Session on $instance" -Target $session -ErrorRecord $_ -Continue
                    }
                }
                else {
                    Write-Message -Level Warning -Message "$session on $instance is already stopped"
                }
                Get-DbaXESession -SqlInstance $xe.Parent -Session $session
            }
        }
    }

    process {
        if ($InputObject) {
            Stop-XESessions $InputObject
        }
        else {
            foreach ($instance in $SqlInstance) {
                $xeSessions = Get-DbaXESession -SqlInstance $instance -SqlCredential $SqlCredential

                # Filter xesessions based on parameters
                if ($Session) {
                    $xeSessions = $xeSessions | Where-Object { $_.Name -in $Session }
                }
                elseif ($AllSessions) {
                    $systemSessions = @('AlwaysOn_health', 'system_health', 'telemetry_xevents')
                    $xeSessions = $xeSessions | Where-Object { $_.Name -notin $systemSessions }
                }

                Stop-XESessions $xeSessions
            }
        }
    }
}
function Stop-DbaXESmartTarget {
    <#
        .SYNOPSIS
            Stops an XESmartTarget PowerShell Job. Useful if you want to run a target, but not right now.

        .DESCRIPTION
            Stops an XESmartTarget PowerShell Job. Useful if you want to run a target, but not right now.

        .PARAMETER InputObject
            The XESmartTarget job object.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
            SmartTarget: by Gianluca Sartori (@spaghettidba)

        .LINK
            https://dbatools.io/Stop-DbaXESmartTarget
            https://github.com/spaghettidba/XESmartTarget/wiki

        .EXAMPLE
            Get-DbaXESmartTarget | Stop-DbaXESmartTarget

            Stops all XESmartTarget jobs.

        .EXAMPLE
            Get-DbaXESmartTarget | Where-Object Id -eq 2 | Stop-DbaXESmartTarget

            Stops a specific XESmartTarget job.
    #>
    [CmdletBinding(SupportsShouldProcess)]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [object[]]$InputObject,
        [switch]$EnableException
    )
    process {
        if ($Pscmdlet.ShouldProcess("localhost", "Stopping job $id")) {
            try {
                $id = $InputObject.Id
                Write-Message -Level Output -Message "Stopping job $id, this may take a couple minutes."
                Get-Job -ID $InputObject.Id | Stop-Job
                Write-Message -Level Output -Message "Successfully Stopped $id. If you need to remove the job for good, use Remove-DbaXESmartTarget."
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_
            }
        }
    }
}
function Sync-DbaLoginPermission {
    <#
        .SYNOPSIS
            Copies SQL login permissions from one server to another.

        .DESCRIPTION
            Syncs only SQL Server login permissions, roles, etc. Does not add or drop logins. If a matching login does not exist on the destination, the login will be skipped. Credential removal is not currently supported for this operation.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Login
            The login(s) to process. Options for this list are auto-populated from the server. If unspecified, all logins will be processed.

        .PARAMETER ExcludeLogin
            The login(s) to exclude. Options for this list are auto-populated from the server.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration, Login
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers
            Limitations: Does not support Application Roles yet

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Sync-DbaLoginPermission

        .EXAMPLE
            Sync-DbaLoginPermission -Source sqlserver2014a -Destination sqlcluster

            Syncs only SQL Server login permissions, roles, etc. Does not add or drop logins or users. To copy logins and their permissions, use Copy-SqlLogin.

        .EXAMPLE
            Sync-DbaLoginPermission -Source sqlserver2014a -Destination sqlcluster -Exclude realcajun -SourceSqlCredential $scred -DestinationSqlCredential $dcred

            Copies all login permissions except for realcajun using SQL Authentication to connect to each server. If a login already exists on the destination, the permissions will not be migrated.

        .EXAMPLE
            Sync-DbaLoginPermission -Source sqlserver2014a -Destination sqlcluster -Login realcajun, netnerds

            Copies permissions ONLY for logins netnerds and realcajun.
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]
        $SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Destination,
        [PSCredential]
        $DestinationSqlCredential,
        [object[]]$Login,
        [object[]]$ExcludeLogin,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        function Sync-Only {
            [CmdletBinding()]
            param (
                [Parameter(Mandatory = $true)]
                [ValidateNotNullOrEmpty()]
                [object]$sourceServer,
                [object]$destServer,
                [array]$Logins,
                [array]$Exclude
            )

            try {
                $sa = ($destServer.Logins | Where-Object { $_.id -eq 1 }).Name
            }
            catch {
                $sa = "sa"
            }

            foreach ($sourceLogin in $sourceServer.Logins) {

                $username = $sourceLogin.Name
                $currentLogin = $sourceServer.ConnectionContext.TrueLogin

                if (!$Login -and $currentLogin -eq $username) {
                    Write-Message -Level Warning -Message "Sync does not modify the permissions of the current user. Skipping."
                    continue
                }

                if ($null -ne $Logins -and $Logins -notcontains $username) {
                    continue
                }

                if ($Exclude -contains $username -or $username.StartsWith("##") -or $username -eq $sa) {
                    continue
                }

                $serverName = Resolve-NetBiosName $sourceServer
                $userBase = ($username.Split("\")[0]).ToLower()
                if ($serverName -eq $userBase -or $username.StartsWith("NT ")) {
                    continue
                }
                if ($null -eq ($destLogin = $destServer.Logins.Item($username))) {
                    continue
                }

                Update-SqlPermissions -SourceServer $sourceServer -SourceLogin $sourceLogin -DestServer $destServer -DestLogin $destLogin
            }
        }
    }
    process {

        if ($source -eq $destination) {
            Stop-Function -Message "Source and Destination SQL Servers are the same. Quitting."
            return
        }

        Write-Message -Level Verbose -Message "Connecting to SQL Servers."
        $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 8
        $destServer = Connect-SqlInstance -SqlInstance $Destination -SqlCredential $DestinationSqlCredential -MinimumVersion 8

        $source = $sourceServer.DomainInstanceName
        $destination = $destServer.DomainInstanceName

        if (!$Login) {
            $login = $sourceServer.Logins.Name
        }

        Sync-Only -SourceServer $sourceServer -DestServer $destServer -Logins $login -Exclude $ExcludeLogin
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Sync-SqlLoginPermissions
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Sync-SqlLoginPermission
    }
}
function Test-DbaBackupInformation {
    <#
        .SYNOPSIS
            Tests a dbatools backup history object is correct for restoring

        .DESCRIPTION
            Normally takes in a backup history object from Format-DbaBackupInformation

            This is then parse to check that it's valid for restore. Tests performed include:
                Checking unbroken LSN chain
                if the target database exists and WithReplace has been provided
                if any files already exist, but owned by other databases
                Creates any new folders required
                That the backup files exists at the location specified, and can be seen by the Sql Instance

            if no errors are found then the objects for that database will me marked as Verified.

        .PARAMETER BackupHistory
            dbatools BackupHistory object. Normally this will have been process with Select- and then Format-DbaBackupInformation

        .PARAMETER SqlInstance
            The Sql Server instance that wil be performing the restore

        .PARAMETER SqlCredential
            A Sql Credential to connect to $SqlInstance

        .PARAMETER WithReplace
            By default we won't overwrite an existing database, this switch tells us you want to

        .PARAMETER Continue
            Switch to indicate a continuing restore

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER VerifyOnly
            This switch indicates that you only wish to verify a restore, so runs a smaller number of tests as you won't be writing anything to the restore server

        .PARAMETER WhatIf
            Shows what would happen if the cmdlet runs. The cmdlet is not run.

        .PARAMETER Confirm
            Prompts you for confirmation before running the cmdlet.

        .NOTES
            Tags: Backup, Restore, DisasterRecovery
            Author: Stuart Moore (@napalmgram stuart-moore.com )

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaBackupInformation

        .EXAMPLE
            $BackupHistory | Test-DbaBackupInformation -SqlInstance MyInstance

            $PassedDbs = $BackupHistory | Where-Object {$_.IsVerified -eq $True}
            $FailedDbs = $BackupHistory | Where-Object {$_.IsVerified -ne $True}

            Pass in a BackupHistory object to be tested against MyInstance.

            Those records that pass are marked as verified. We can then use the IsVerified property to divide the failures and successes
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [object[]]$BackupHistory,
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [switch]$WithReplace,
        [switch]$Continue,
        [switch]$VerifyOnly,
        [switch]$EnableException
    )

    begin {
        try {
            $RestoreInstance = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            return
        }
        $InternalHistory = @()
    }
    process {
        foreach ($bh in $BackupHistory) {
            $InternalHistory += $bh
        }
    }
    end {
        $RegisteredFileCheck = Get-DbaDbPhysicalFile -SqlInstance $RestoreInstance

        $Databases = $InternalHistory.Database | Select-Object -Unique
        foreach ($Database in $Databases) {
            $VerificationErrors = 0
            Write-Message -Message "Testing restore for $Database" -Level Verbose
            #Test we're only restoring backups from one database, or hilarity will ensure
            $DbHistory = $InternalHistory | Where-Object {$_.Database -eq $Database}
            if (( $DbHistory | Select-Object -Property OriginalDatabase -Unique ).Count -gt 1) {
                Write-Message -Message "Trying to restore $Database from multiple sources databases" -Level Warning
                $VerificationErrors++
            }
            #Test Db Existance on destination
            $DbCheck = Get-DbaDatabase -SqlInstance $RestoreInstance -Database $Database
            # Only do file and db tests if we're not verifing
            Write-Message -Level Verbose -Message "VerifyOnly = $VerifyOnly"
            If ($VerifyOnly -ne $true) {
                if ($null -ne $DbCheck -and ($WithReplace -ne $true -and $Continue -ne $true)) {
                    Stop-Function -Message "Database $Database exists, so WithReplace must be specified" -Target $database
                    $VerificationErrors++
                }

                $DBFileCheck = ($RegisteredFileCheck | Where-Object Name -eq $Database).PhysicalName
                $OtherFileCheck = ($RegisteredFileCheck | Where-Object Name -ne $Database).PhysicalName
                $DBHistoryPhysicalPaths = ($DbHistory | Select-Object -ExpandProperty filelist | Select-Object PhysicalName -Unique).PhysicalName
                $DBHistoryPhysicalPathsTest = Test-DbaSqlPath -SqlInstance $RestoreInstance -Path $DBHistoryPhysicalPaths
                $DBHistoryPhysicalPathsExists = ($DBHistoryPhysicalPathsTest | Where-Object FileExists -eq $True).FilePath
                foreach ($path in $DBHistoryPhysicalPaths) {
                    if (($DBHistoryPhysicalPathsTest | Where-Object FilePath -eq $path).FileExists) {
                        if ($path -in $DBFileCheck) {
                            #If the Files are owned by the db we're restoring check for Continue or WithReplace. If not, then report error otherwise just carry on
                            if  ($WithReplace -ne $True -and $Continue -ne $True) {
                                Write-Message -Message "File $path already exists on $SqlInstance and WithReplace not specified, cannot restore" -Level Warning
                                $VerificationErrors++
                            }
                        }
                        elseif ($path -in $OtherFileCheck) {
                            Write-Message -Message "File $path already exists on $SqlInstance and owned by another database, cannot restore" -Level Warning
                            $VerificationErrors++
                        }
                        elseif ($path -in $DBHistoryPhysicalPathsExists) {
                                Write-Message -Message "File $path already exists on $($SqlInstance.ComputerName), not owned by any database in $SqlInstance, will not overwrite." -Level Warning
                                $VerificationErrors++
                        }
                    }
                    else {
                        $ParentPath = Split-Path $path -Parent
                        if (!(Test-DbaSqlPath -SqlInstance $RestoreInstance -Path $ParentPath) ) {
                            $ConfirmMessage = "`n Creating Folder $ParentPath on $SqlInstance `n"
                            if ($Pscmdlet.ShouldProcess("$Path on $SqlInstance `n `n", $ConfirmMessage)) {
                                if (New-DbaSqlDirectory -SqlInstance $RestoreInstance -Path $ParentPath) {
                                    Write-Message -Message "Created Folder $ParentPath on $SqlInstance" -Level Verbose
                                }
                                else {
                                    Write-Message -Message "Failed to create $ParentPath on $SqlInstance" -Level Warning
                                    $VerificationErrors++
                                }
                            }
                        }
                    }
                }
                #Easier to do FileStream checks out of the loop:
                if ('s' -in ($DbHistory | Select-Object -ExpandProperty filelist | Select-Object FileType -Unique).FileType) {
                    if ((Get-DbaSpConfigure -SqlInstance $RestoreInstance -ConfigName FilestreamAccessLevel).RunningValue -eq 0) {
                        Write-Message -Level Warning -Message "Database $Database contains FileStream data, and FileStream is not enable on the destination server"
                        $VerificationErrors++
                    }

                    $ExistingFS = Get-DbaFileStreamFolder -SqlInstance $SqlInstance
                    foreach ($FileStreamFolder in ($DbHistory | Select-Object -ExpandProperty filelist | Where-Object {$_.FileType -eq 's'} | Select-Object PhysicalName -unique).PhysicalName) {
                        if ($null -ne $ExistingFS) {
                            if ($null -ne ($ExistingFs | Where-Object {$_.Database -eq $Database}) -and $Withreplace -ne $True) {
                                Write-Message -Level Warning -Message "Folder $FileStreamFolder already in use for Filestream data on $SqlInstance and WithReplace not specified, cannot restore"
                                $VerificationErrors++
                            }
                            $OtherOwners = $ExistingFs | Where-Object {$_.FileStreamFolder -eq $FileStreamFolder -and $_.Database -ne $Database}
                            if ($null -ne $OtherOwners) {
                                Write-Message -Level Warning -Message "Folder $FileStreamFolder already in use for Filestream data by $($OtherOwners.Database) on $SqlInstance, cannot restore"
                                $VerificationErrors++
                            }
                        }
                    }
                }

            }

            #Test all backups readable
            $allpaths = $DbHistory | Select-Object -ExpandProperty FullName
            $allpaths_validity = Test-DbaSqlPath -SqlInstance $RestoreInstance -Path $allpaths
            foreach ($path in $allpaths_validity) {
                if ($path.FileExists -eq $false) {
                    Write-Message -Message "Backup File $($path.FilePath) cannot be read" -Level Warning
                    $VerificationErrors++
                }
            }
            #Test for LSN chain
            if ($true -ne $Continue) {
                if (!($DbHistory | Test-DbaLsnChain)) {
                    Write-Message -Message "LSN Check failed" -Level Verbose
                    $VerificationErrors++
                }
            }
            if ($VerificationErrors -eq 0) {
                Write-Message -Message "Marking $Database as verified" -Level Verbose
                $InternalHistory | Where-Object {$_.Database -eq $Database} | foreach-Object {$_.IsVerified = $True}
            }
            else {
                Write-Message -Message "Verification errors  = $VerificationErrors - Has not Passed" -Level Verbose
            }
        }
        $InternalHistory
    }
}
function Test-DbaCmConnection {
    <#
        .SYNOPSIS
            Tests over which paths a computer can be managed.

        .DESCRIPTION
            Tests over which paths a computer can be managed.

            This function tries out the connectivity for:
                - Cim over WinRM
                - Cim over DCOM
                - Wmi
                - PowerShellRemoting
            Results will be written to the connectivity cache and will cause Get-DbaCmObject and Invoke-DbaCmMethod to connect using the way most likely to succeed. This way, it is likely the other commands will take less time to execute. These others too cache their results, in order to dynamically update connection statistics.

            This function ignores global configuration settings limiting which protocols may be used.

        .PARAMETER ComputerName
            The computer to test against.

        .PARAMETER Credential
            The credentials to use when running the test. Bad credentials are automatically cached as non-working. This behavior can be disabled by the 'Cache.Management.Disable.BadCredentialList' configuration.

        .PARAMETER Type
            The connection protocol types to test.
            By default, all types are tested.

            Note that this function will ignore global configurations limiting the types of connections available and test all connections specified here instead.

            Available connection protocol types: "CimRM", "CimDCOM", "Wmi", "PowerShellRemoting"

        .PARAMETER Force
            If this switch is enabled, the Alert will be dropped and recreated on Destination.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ComputerManagement, CIM
            Author: Fred Winmann (@FredWeinmann)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

            **This function should not be called from within dbatools. It is meant as a tool for users only.**

        .LINK
            https://dbatools.io/Test-DbaCmConnection

        .EXAMPLE
            Test-DbaCmConnection -ComputerName sql2014

            Performs a full-spectrum connection test against the computer sql2014. The results will be reported and registered. Future calls from Get-DbaCmObject will recognize the results and optimize the query.

        .EXAMPLE
            Test-DbaCmConnection -ComputerName sql2014 -Credential $null -Type CimDCOM, CimRM

            This test will run a connectivity test of CIM over DCOM and CIM over WinRM against the computer sql2014 using Windows Authentication.

            The results will be reported and registered. Future calls from Get-DbaCmObject will recognize the results and optimize the query.
        #>
    [CmdletBinding()]
    Param (
        [Parameter(ValueFromPipeline = $true)]
        [Sqlcollaborative.Dbatools.Parameter.DbaCmConnectionParameter[]]
        $ComputerName = $env:COMPUTERNAME,

        [System.Management.Automation.PSCredential]
        $Credential,

        [Sqlcollaborative.Dbatools.Connection.ManagementConnectionType[]]
        $Type = @("CimRM", "CimDCOM", "Wmi", "PowerShellRemoting"),

        [switch]
        $Force,

        [switch]
        [Alias('Silent')]$EnableException
    )

    Begin {
        #region Configuration Values
        $disable_cache = Get-DbaConfigValue -Name "ComputerManagement.Cache.Disable.All" -Fallback $false
        $disable_badcredentialcache = Get-DbaConfigValue -Name "ComputerManagement.Cache.Disable.BadCredentialList" -Fallback $false
        #endregion Configuration Values

        #region Helper Functions
        function Test-ConnectionCimRM {
            [CmdletBinding()]
            Param (
                [Sqlcollaborative.Dbatools.Parameter.DbaCmConnectionParameter]
                $ComputerName,

                [System.Management.Automation.PSCredential]
                $Credential
            )

            try {
                $os = $ComputerName.Connection.GetCimRMInstance($Credential, "Win32_OperatingSystem", "root\cimv2")

                New-Object PSObject -Property @{
                    Success       = "Success"
                    Timestamp     = Get-Date
                    Authenticated = $true
                }
            }
            catch {
                if (($_.Exception.InnerException -eq 0x8007052e) -or ($_.Exception.InnerException -eq 0x80070005)) {
                    New-Object PSObject -Property @{
                        Success       = "Error"
                        Timestamp     = Get-Date
                        Authenticated = $false
                    }
                }
                else {
                    New-Object PSObject -Property @{
                        Success       = "Error"
                        Timestamp     = Get-Date
                        Authenticated = $true
                    }
                }
            }
        }

        function Test-ConnectionCimDCOM {
            [CmdletBinding()]
            Param (
                [Sqlcollaborative.Dbatools.Parameter.DbaCmConnectionParameter]
                $ComputerName,

                [System.Management.Automation.PSCredential]
                $Credential
            )

            try {
                $os = $ComputerName.Connection.GetCimDComInstance($Credential, "Win32_OperatingSystem", "root\cimv2")

                New-Object PSObject -Property @{
                    Success       = "Success"
                    Timestamp     = Get-Date
                    Authenticated = $true
                }
            }
            catch {
                if (($_.Exception.InnerException -eq 0x8007052e) -or ($_.Exception.InnerException -eq 0x80070005)) {
                    New-Object PSObject -Property @{
                        Success       = "Error"
                        Timestamp     = Get-Date
                        Authenticated = $false
                    }
                }
                else {
                    New-Object PSObject -Property @{
                        Success       = "Error"
                        Timestamp     = Get-Date
                        Authenticated = $true
                    }
                }
            }
        }

        function Test-ConnectionWmi {
            [CmdletBinding()]
            Param (
                [string]
                $ComputerName,

                [System.Management.Automation.PSCredential]
                $Credential
            )

            try {
                $os = Get-WmiObject -ComputerName $ComputerName -Credential $Credential -Class Win32_OperatingSystem -ErrorAction Stop
                New-Object PSObject -Property @{
                    Success       = "Success"
                    Timestamp     = Get-Date
                    Authenticated = $true
                }
            }
            catch [System.UnauthorizedAccessException] {
                New-Object PSObject -Property @{
                    Success       = "Error"
                    Timestamp     = Get-Date
                    Authenticated = $false
                }
            }
            catch {
                New-Object PSObject -Property @{
                    Success       = "Error"
                    Timestamp     = Get-Date
                    Authenticated = $true
                }
            }
        }

        function Test-ConnectionPowerShellRemoting {
            [CmdletBinding()]
            Param (
                [string]
                $ComputerName,

                [System.Management.Automation.PSCredential]
                $Credential
            )

            try {
                $parameters = @{
                    ScriptBlock  = { Get-WmiObject -Class Win32_OperatingSystem -ErrorAction Stop }
                    ComputerName = $ComputerName
                    ErrorAction  = 'Stop'
                }
                if ($Credential) { $parameters["Credential"] = $Credential }
                $os = Invoke-Command @parameters

                New-Object PSObject -Property @{
                    Success       = "Success"
                    Timestamp     = Get-Date
                    Authenticated = $true
                }
            }
            catch {
                # Will always consider authenticated, since any call with credentials to a server that doesn't exist will also carry invalid credentials error.
                # There simply is no way to differentiate between actual authentication errors and server not reached
                New-Object PSObject -Property @{
                    Success       = "Error"
                    Timestamp     = Get-Date
                    Authenticated = $true
                }
            }
        }
        #endregion Helper Functions
    }
    Process {
        foreach ($ConnectionObject in $ComputerName) {
            if (-not $ConnectionObject.Success) { Stop-Function -Message "Failed to interpret input: $($ConnectionObject.Input)" -Category InvalidArgument -Target $ConnectionObject.Input -Continue}

            $Computer = $ConnectionObject.Connection.ComputerName.ToLower()
            Write-Message -Level VeryVerbose -Message "[$Computer] Testing management connection"

            #region Setup connection object
            $con = $ConnectionObject.Connection
            #endregion Setup connection object

            #region Handle credentials
            $BadCredentialsFound = $false
            if ($con.DisableBadCredentialCache) { $con.KnownBadCredentials.Clear() }
            elseif ($con.IsBadCredential($Credential) -and (-not $Force)) {
                Stop-Function -Message "[$Computer] The credentials supplied are on the list of known bad credentials, skipping. Use -Force to override this." -Continue -Category InvalidArgument -Target $Computer
            }
            elseif ($con.IsBadCredential($Credential) -and $Force) {
                $con.RemoveBadCredential($Credential)
            }
            #endregion Handle credentials

            #region Connectivity Tests
            :types foreach ($ConnectionType in $Type) {
                switch ($ConnectionType) {
                    #region CimRM
                    "CimRM" {
                        Write-Message -Level Verbose -Message "[$Computer] Testing management access using CIM over WinRM"
                        $res = Test-ConnectionCimRM -ComputerName $con -Credential $Credential
                        $con.LastCimRM = $res.Timestamp
                        $con.CimRM = $res.Success
                        Write-Message -Level VeryVerbose -Message "[$Computer] CIM over WinRM Results | Success: $($res.Success), Authentication: $($res.Authenticated)"

                        if (-not $res.Authenticated) {
                            Write-Message -Level Important -Message "[$Computer] The credentials supplied proved to be invalid. Skipping further tests"
                            $con.AddBadCredential($Credential)
                            break types
                        }
                    }
                    #endregion CimRM

                    #region CimDCOM
                    "CimDCOM" {
                        Write-Message -Level Verbose -Message "[$Computer] Testing management access using CIM over DCOM."
                        $res = Test-ConnectionCimDCOM -ComputerName $con -Credential $Credential
                        $con.LastCimDCOM = $res.Timestamp
                        $con.CimDCOM = $res.Success
                        Write-Message -Level VeryVerbose -Message "[$Computer] CIM over DCOM Results | Success: $($res.Success), Authentication: $($res.Authenticated)"

                        if (-not $res.Authenticated) {
                            Write-Message -Level Important -Message "[$Computer] The credentials supplied proved to be invalid. Skipping further tests."
                            $con.AddBadCredential($Credential)
                            break types
                        }
                    }
                    #endregion CimDCOM

                    #region Wmi
                    "Wmi" {
                        Write-Message -Level Verbose -Message "[$Computer] Testing management access using WMI."
                        $res = Test-ConnectionWmi -ComputerName $Computer -Credential $Credential
                        $con.LastWmi = $res.Timestamp
                        $con.Wmi = $res.Success
                        Write-Message -Level VeryVerbose -Message "[$Computer] WMI Results | Success: $($res.Success), Authentication: $($res.Authenticated)"

                        if (-not $res.Authenticated) {
                            Write-Message -Level Important -Message "[$Computer] The credentials supplied proved to be invalid. Skipping further tests"
                            $con.AddBadCredential($Credential)
                            break types
                        }
                    }
                    #endregion Wmi

                    #region PowerShell Remoting
                    "PowerShellRemoting" {
                        Write-Message -Level Verbose -Message "[$Computer] Testing management access using PowerShell Remoting."
                        $res = Test-ConnectionPowerShellRemoting -ComputerName $Computer -Credential $Credential
                        $con.LastPowerShellRemoting = $res.Timestamp
                        $con.PowerShellRemoting = $res.Success
                        Write-Message -Level VeryVerbose -Message "[$Computer] PowerShell Remoting Results | Success: $($res.Success)"
                    }
                    #endregion PowerShell Remoting
                }
            }
            #endregion Connectivity Tests

            if (-not $disable_cache) { [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::Connections[$Computer] = $con }
            $con
        }
    }
    End {

    }
}

#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#

function Test-DbaConnection {
    <#
        .SYNOPSIS
            Tests the connection to a single instance.

        .DESCRIPTION
            Tests the ability to connect to an SQL Server instance outputting information about the server and instance.

        .PARAMETER SqlInstance
            The SQL Server Instance to test connection

        .PARAMETER Credential
            Credential object used to connect to the Computer as a different user

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .EXAMPLE
            Test-DbaConnection SQL2016

            ComputerName         : SQL2016
            InstanceName         : MSSQLSERVER
            SqlInstance          : sql2016
            SqlVersion           : 13.0.4001
            ConnectingAsUser     : BASE\ctrlb
            ConnectSuccess       : True
            AuthType             : Windows Authentication
            AuthScheme           : KERBEROS
            TcpPort              : 1433
            IPAddress            : 10.2.1.5
            NetBiosName          : sql2016.base.local
            IsPingable           : True
            PSRemotingAccessible : True
            DomainName           : base.local
            LocalWindows         : 10.0.15063.0
            LocalPowerShell      : 5.1.15063.502
            LocalCLR             : 4.0.30319.42000
            LocalSMOVersion      : 13.0.0.0
            LocalDomainUser      : True
            LocalRunAsAdmin      : False

        .NOTES
            Tags: CIM, Test, Connection
            Author: Chrissy LeMaire

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
    #>
    [CmdletBinding()]
    param (
        [parameter(ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$Credential,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            # Get local environment
            Write-Message -Level Verbose -Message "Getting local environment information"
            $localInfo = [pscustomobject]@{
                Windows    = [environment]::OSVersion.Version.ToString()
                PowerShell = $PSVersionTable.PSversion.ToString()
                CLR        = $PSVersionTable.CLRVersion.ToString()
                SMO        = ((([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.Fullname -like "Microsoft.SqlServer.SMO,*" }).FullName -Split ", ")[1]).TrimStart("Version=")
                DomainUser = $env:computername -ne $env:USERDOMAIN
                RunAsAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
            }

            try {
                <# gather following properties #>
                <#
                        InputName        :
                        ComputerName     :
                        IPAddress        :
                        DNSHostName      :
                        DNSDomain        :
                        Domain           :
                        DNSHostEntry     :
                        FQDN             :
                        FullComputerName :
                     #>
                $resolved = Resolve-DbaNetworkName -ComputerName $instance.ComputerName -Credential $Credential
            }
            catch {
                Stop-Function -Message "Unable to resolve server information" -Category ConnectionError -Target $instance -ErrorRecord $_ -Continue
            }

            # Test for WinRM #Test-WinRM neh
            Write-Message -Level Verbose -Message "Checking remote acccess"
            try {
                $null = Invoke-Command2 -ComputerName $instance.ComputerName -Credential $Credential -ScriptBlock { Get-ChildItem } -ErrorAction Stop
                $remoting = $true
            }
            catch {
                $remoting = $_
            }

            # Test Connection first using Test-Connection which requires ICMP access then failback to tcp if pings are blocked
            Write-Message -Level Verbose -Message "Testing ping to $($instance.ComputerName)"
            $pingable = Test-Connection -ComputerName $instance.ComputerName -Count 1 -Quiet

            # SQL Server connection
            if ($instance.InstanceName -ne "MSSQLSERVER") {
                $sqlport = "N/A"
            }
            else {
                Write-Message -Level Verbose -Message "Testing raw socket connection to default SQL port"
                $tcp = New-Object System.Net.Sockets.TcpClient
                try {
                    $tcp.Connect($baseaddress, 1433)
                    $tcp.Close()
                    $tcp.Dispose()
                    $sqlport = $true
                }
                catch {
                    $sqlport = $false
                }
            }

            try {
                $server = Connect-SqlInstance -SqlInstance $instance.FullSmoName -SqlCredential $SqlCredential
                $connectSuccess = $true
            }
            catch {
                $connectSuccess = $false
                Stop-Function -Message "Issue connection to SQL Server on $instance" -Category ConnectionError -Target $instance -ErrorRecord $_ -Continue
            }

            $username = $server.ConnectionContext.TrueLogin
            if ($username -like "*\*") {
                $authType = "Windows Authentication"
            }
            else {
                $authType = "SQL Authentication"
            }

            # TCP Port
            try {
                $tcpport = (Get-DbaTcpPort -SqlInstance $server -EnableException).Port
            }
            catch {
                $tcpport = $_
            }

            # Auth Scheme
            try {
                $authscheme = (Test-DbaConnectionAuthScheme -SqlInstance $server -WarningVariable authwarning -WarningAction SilentlyContinue).AuthScheme
            }
            catch {
                $authscheme = $_
            }

            if ($authwarning) {
                $authscheme = "N/A"
            }

            [pscustomobject]@{
                ComputerName         = $resolved.ComputerName
                InstanceName         = $instance.InstanceName
                SqlInstance          = $instance.FullSmoName
                SqlVersion           = $server.Version
                ConnectingAsUser     = $username
                ConnectSuccess       = $connectSuccess
                AuthType             = $authType
                AuthScheme           = $authscheme
                TcpPort              = $tcpport
                IPAddress            = $resolved.IPAddress
                NetBiosName          = $resolved.FullComputerName
                IsPingable           = $pingable
                PSRemotingAccessible = $remoting
                DomainName           = $resolved.Domain
                LocalWindows         = $localInfo.Windows
                LocalPowerShell      = $localInfo.PowerShell
                LocalCLR             = $localInfo.CLR
                LocalSMOVersion      = $localInfo.SMO
                LocalDomainUser      = $localInfo.DomainUser
                LocalRunAsAdmin      = $localInfo.RunAsAdmin
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Test-SqlConnection
    }
}
function Test-DbaConnectionAuthScheme {
    <#
        .SYNOPSIS
            Returns the transport protocol and authentication scheme of the connection. This is useful to determine if your connection is using Kerberos.

        .DESCRIPTION
            By default, this command will return the ConnectName, ServerName, Transport and AuthScheme of the current connection.

            ConnectName is the name you used to connect. ServerName is the name that the SQL Server reports as its @@SERVERNAME which is used to register its SPN. If you were expecting a Kerberos connection and got NTLM instead, ensure ConnectName and ServerName match.

            If -Kerberos or -Ntlm is specified, the $true/$false results of the test will be returned. Returns $true or $false by default for one server. Returns Server name and Results for more than one server.

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to. Server(s) must be SQL Server 2005 or higher.

        .PARAMETER Kerberos
            If this switch is enabled, checks will be made for Kerberos authentication.

        .PARAMETER Ntlm
            If this switch is enabled, checks will be made for NTLM authentication.

        .PARAMETER Detailed
            Output all properties, will be deprecated in 1.0.0 release.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

            .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: SPN, Kerberos
            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaConnectionAuthScheme

        .EXAMPLE
            Test-DbaConnectionAuthScheme -SqlInstance sqlserver2014a, sql2016

            Returns ConnectName, ServerName, Transport and AuthScheme for sqlserver2014a and sql2016.

        .EXAMPLE
            Test-DbaConnectionAuthScheme -SqlInstance sqlserver2014a -Kerberos

            Returns $true or $false depending on if the connection is Kerberos or not.

        .EXAMPLE
            Test-DbaConnectionAuthScheme -SqlInstance sqlserver2014a | Select-Object *

            Returns the results of "SELECT * from sys.dm_exec_connections WHERE session_id = @@SPID"

    #>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential", "Cred")]
        [PSCredential]$SqlCredential,
        [switch]$Kerberos,
        [switch]$Ntlm,
        [switch]$Detailed,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter Detailed

        $sql = "SELECT  SERVERPROPERTY('MachineName') AS ComputerName,
                            ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
                            SERVERPROPERTY('ServerName') AS SqlInstance,
                            session_id as SessionId, most_recent_session_id as MostRecentSessionId, connect_time as ConnectTime,
                            net_transport as Transport, protocol_type as ProtocolType, protocol_version as ProtocolVersion,
                            endpoint_id as EndpointId, encrypt_option as EncryptOption, auth_scheme as AuthScheme, node_affinity as NodeAffinity,
                            num_reads as NumReads, num_writes as NumWrites, last_read as LastRead, last_write as LastWrite,
                            net_packet_size as PacketSize, client_net_address as ClientNetworkAddress, client_tcp_port as ClientTcpPort,
                            local_net_address as ServerNetworkAddress, local_tcp_port as ServerTcpPort, connection_id as ConnectionId,
                            parent_connection_id as ParentConnectionId, most_recent_sql_handle as MostRecentSqlHandle
                            FROM sys.dm_exec_connections WHERE session_id = @@SPID"
    }

    process {
        foreach ($instance in $SqlInstance) {

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            Write-Message -Level Verbose -Message "Getting results for the following query: $sql."
            try {
                $results = $server.Query($sql)
            }
            catch {
                Stop-Function -Message "Failure" -Target $server -Exception $_ -Continue
            }

            # sorry, standards!
            if ($Kerberos -or $Ntlm) {
                if ($Ntlm) {
                    $auth = 'NTLM'
                }
                else {
                    $auth = 'Kerberos'
                }
                [PSCustomObject]@{
                    ComputerName = $results.ComputerName
                    InstanceName = $results.InstanceName
                    SqlInstance  = $results.SqlInstance
                    Result       = ($server.AuthScheme -eq $auth)
                } | Select-DefaultView -Property SqlInstance, Result
            }
            else {
                Select-DefaultView -InputObject $results -Property ComputerName, InstanceName, SqlInstance, Transport, AuthScheme
            }
        }
    }
}
function Test-DbaDatabaseCollation {
    <#
        .SYNOPSIS
            Compares Database Collations to Server Collation

        .DESCRIPTION
            Compares Database Collations to Server Collation

        .PARAMETER SqlInstance
            The target SQL Server instance or instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies the database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies the database(s) to exclude from processing. Options for this list are auto-populated from the server.

        .PARAMETER Detailed
            Output all properties, will be deprecated in 1.0.0 release.

        .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database, Collation
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaDatabaseCollation

        .EXAMPLE
            Test-DbaDatabaseCollation -SqlInstance sqlserver2014a

            Returns server name, database name and true/false if the collations match for all databases on sqlserver2014a.

        .EXAMPLE
            Test-DbaDatabaseCollation -SqlInstance sqlserver2014a -Database db1, db2

            Returns inforamtion for the db1 and db2 databases on sqlserver2014a.

        .EXAMPLE
            Test-DbaDatabaseCollation -SqlInstance sqlserver2014a, sql2016 -Exclude db1

            Returns information for database and server collations for all databases except db1 on sqlserver2014a and sql2016.

        .EXAMPLE
            Get-DbaRegisteredServer -SqlInstance sql2016 | Test-DbaDatabaseCollation

            Returns db/server collation information for every database on every server listed in the Central Management Server on sql2016.
    #>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$Detailed,
        [switch]$EnableException
    )
    begin {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Parameter "Detailed"
    }
    process {
        foreach ($instance in $sqlinstance) {
            # Try connecting to the instance
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $dbs = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                $dbs = $dbs | Where-Object { $Database -contains $_.Name }
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $dbs) {
                Write-Message -Level Verbose -Message "Processing $($db.name) on $servername."
                [PSCustomObject]@{
                    ComputerName      = $server.ComputerName
                    InstanceName      = $server.ServiceName
                    SqlInstance       = $server.DomainInstanceName
                    Database          = $db.name
                    ServerCollation   = $server.collation
                    DatabaseCollation = $db.collation
                    IsEqual           = $db.collation -eq $server.collation
                }
            }
        }
    }
}
function Test-DbaDatabaseCompatibility {
    <#
        .SYNOPSIS
            Compares Database Compatibility level to Server Compatibility

        .DESCRIPTION
            Compares Database Compatibility level to Server Compatibility

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to.

        .PARAMETER Credential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies the database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies the database(s) to exclude from processing. Options for this list are auto-populated from the server.

        .PARAMETER Detailed
            Will be deprecated in 1.0.0 release.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database, Compatibility
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaDatabaseCompatibility

        .EXAMPLE
            Test-DbaDatabaseCompatibility -SqlInstance sqlserver2014a

            Returns server name, database name and true/false if the compatibility level match for all databases on sqlserver2014a.

        .EXAMPLE
            Test-DbaDatabaseCompatibility -SqlInstance sqlserver2014a -Database db1, db2

            Returns detailed information for database and server compatibility level for the db1 and db2 databases on sqlserver2014a.

        .EXAMPLE
            Test-DbaDatabaseCompatibility -SqlInstance sqlserver2014a, sql2016 -Exclude db1

            Returns detailed information for database and server compatibility level for all databases except db1 on sqlserver2014a and sql2016.

        .EXAMPLE
            Get-DbaRegisteredServer -SqlInstance sql2014 | Test-DbaDatabaseCompatibility

            Returns db/server compatibility information for every database on every server listed in the Central Management Server on sql2016.
    #>
    [CmdletBinding()]
    [OutputType("System.Collections.ArrayList")]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$Credential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$Detailed,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Parameter "Detailed"
    }

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance."
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $serverversion = "Version$($server.VersionMajor)0"
            $dbs = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                $dbs = $dbs | Where-Object { $Database -contains $_.Name }
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $dbs) {
                Write-Message -Level Verbose -Message "Processing $($db.name) on $instance."
                [PSCustomObject]@{
                    ComputerName          = $server.ComputerName
                    InstanceName          = $server.ServiceName
                    SqlInstance           = $server.DomainInstanceName
                    ServerLevel           = $serverversion
                    Database              = $db.name
                    DatabaseCompatibility = $db.CompatibilityLevel
                    IsEqual               = $db.CompatibilityLevel -eq $serverversion
                }
            }
        }
    }
}
function Test-DbaDatabaseOwner {
    <#
        .SYNOPSIS
            Checks database owners against a login to validate which databases do not match that owner.

        .DESCRIPTION
            This function will check all databases on an instance against a SQL login to validate if that
            login owns those databases or not. By default, the function will check against 'sa' for
            ownership, but the user can pass a specific login if they use something else.

            Best Practice reference: http://weblogs.sqlteam.com/dang/archive/2008/01/13/Database-Owner-Troubles.aspx

        .PARAMETER SqlInstance
            Specifies the SQL Server instance(s) to scan.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies the database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies the database(s) to exclude from processing. Options for this list are auto-populated from the server.

        .PARAMETER TargetLogin
            Specifies the login that you wish check for ownership. This defaults to 'sa' or the sysadmin name if sa was renamed. This must be a valid security principal which exists on the target server.

        .PARAMETER Detailed
            Will be deprecated in 1.0.0 release.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database, Owner, DbOwner
            Author: Michael Fal (@Mike_Fal), http://mikefal.net

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaDatabaseOwner

        .EXAMPLE
            Test-DbaDatabaseOwner -SqlInstance localhost

            Returns all databases where the owner does not match 'sa'.

        .EXAMPLE
            Test-DbaDatabaseOwner -SqlInstance localhost -TargetLogin 'DOMAIN\account'

            Returns all databases where the owner does not match 'DOMAIN\account'.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [string]$TargetLogin ,
        [Switch]$Detailed,
        [Alias('Silent')]
        [Switch]$EnableException
    )

    begin {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Parameter "Detailed"
    }
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            # dynamic sa name for orgs who have changed their sa name
            if (Test-Bound -ParameterName TargetLogin -Not) {
                $TargetLogin = ($server.logins | Where-Object { $_.id -eq 1 }).Name
            }

            #Validate login
            if (($server.Logins.Name) -notmatch [Regex]::Escape($TargetLogin)) {
                Write-Message -Level Verbose -Message "$TargetLogin is not a login on $instance" -Target $instance
            }
        }
        #use online/available dbs
        $dbs = $server.Databases | Where-Object IsAccessible

        #filter database collection based on parameters
        if ($Database) {
            $dbs = $dbs | Where-Object { $Database -contains $_.Name }
        }

        if ($ExcludeDatabase) {
            $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
        }

        #for each database, create custom object for return set.
        foreach ($db in $dbs) {

            if ($db.IsAccessible -eq $false) {
                Stop-Function -Message "The database $db is not accessible. Skipping database." -Continue -Target $db
            }

            Write-Message -Level Verbose -Message "Checking $db"
            [pscustomobject]@{
                ComputerName = $server.ComputerName
                InstanceName = $server.ServiceName
                SqlInstance  = $server.DomainInstanceName
                Server       = $server.DomainInstanceName
                Database     = $db.Name
                DBState      = $db.Status
                CurrentOwner = $db.Owner
                TargetOwner  = $TargetLogin
                OwnerMatch   = ($db.owner -eq $TargetLogin)
            } | Select-DefaultView -ExcludeProperty Server
        }
    }
}
function Test-DbaDbCompression {
    <#
    .SYNOPSIS
        Returns tables and indexes with preferred compression setting.
     .DESCRIPTION
        This function returns the results of a full table/index compression analysis.
        This function returns the best option to date for either NONE, Page, or Row Compression.
        Remember Uptime is critical, the longer uptime, the more accurate the analysis is.
        You would probably be best if you utilized Get-DbaUptime first, before running this command.

        Test-DbaCompression script derived from GitHub and the tigertoolbox
        (https://github.com/Microsoft/tigertoolbox/tree/master/Evaluate-Compression-Gains)
        In the output, you will find the following information:
        Column Percent_Update shows the percentage of update operations on a specific table, index, or partition,
        relative to total operations on that object. The lower the percentage of Updates
        (that is, the table, index, or partition is infrequently updated), the better candidate it is for page compression.
        Column Percent_Scan shows the percentage of scan operations on a table, index, or partition, relative to total
        operations on that object. The higher the value of Scan (that is, the table, index, or partition is mostly scanned),
        the better candidate it is for page compression.
        Column Compression_Type_Recommendation can have four possible outputs indicating where there is most gain,
        if any: 'PAGE', 'ROW', 'NO_GAIN' or '?'. When the output is '?' this approach could not give a recommendation,
        so as a rule of thumb I would lean to ROW if the object suffers mainly UPDATES, or PAGE if mainly INSERTS,
        but this is where knowing your workload is essential. When the output is 'NO_GAIN' well, that means that according
        to sp_estimate_data_compression_savings no space gains will be attained when compressing, as in the above output example,
        where compressing would grow the affected object.

        Note: Note that this script will execute on the context of the current database.
        Also be aware that this may take awhile to execute on large objects, because if the IS locks taken by the
        sp_estimate_data_compression_savings cannot be honored, the SP will be blocked.

    .PARAMETER SqlInstance
        SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

    .PARAMETER SqlCredential
        Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

    .PARAMETER Database
        The database(s) to process - this list is autopopulated from the server. If unspecified, all databases will be processed.

    .PARAMETER ExcludeDatabase
        The database(s) to exclude - this list is autopopulated from the server

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Author: Jason Squires (@js_0505, [email protected])
        Tags: Compression, Table, Database
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Test-DbaCompression

    .EXAMPLE
        Test-DbaCompression -SqlInstance localhost

        Returns all user database files and free space information for the local host

    .EXAMPLE
        Test-DbaCompression -SqlInstance ServerA -Database DBName | Out-GridView
        Returns results of all potential compression options for a single database
        with the recommendation of either Page or Row into and nicely formatted GridView

    .EXAMPLE
        Test-DbaCompression -SqlInstance ServerA
        Returns results of all potential compression options for all databases
        with the recommendation of either Page or Row
    .EXAMPLE
        $cred = Get-Credential sqladmin
        Test-DbaCompression -SqlInstance ServerA -ExcludeDatabase Database -SqlCredential $cred
        Returns results of all potential compression options for all databases
        with the recommendation of either Page or Row

    .EXAMPLE
        $servers = 'Server1','Server2'
        foreach ($svr in $servers)
        {
            Test-DbaCompression -SqlInstance $svr | Export-Csv -Path C:\temp\CompressionAnalysisPAC.csv -Append
        }

        This produces a full analysis of all your servers listed and is pushed to a csv for you to
        analyze.
#>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Write-Message -Level System -Message "Bound parameters: $($PSBoundParameters.Keys -join ", ")"
        $sql = "SET NOCOUNT ON;

IF OBJECT_ID('tempdb..##testdbacompression', 'U') IS NOT NULL
    DROP TABLE ##testdbacompression

IF OBJECT_ID('tempdb..##tmpEstimateRow', 'U') IS NOT NULL
    DROP TABLE ##tmpEstimateRow

IF OBJECT_ID('tempdb..##tmpEstimatePage', 'U') IS NOT NULL
    DROP TABLE ##tmpEstimatePage

CREATE TABLE ##testdbacompression (
    [Schema] SYSNAME
    ,[TableName] SYSNAME
    ,[IndexName] SYSNAME NULL
    ,[Partition] INT
    ,[IndexID] INT
    ,[IndexType] VARCHAR(25)
    ,[PercentScan] SMALLINT
    ,[PercentUpdate] SMALLINT
    ,[RowEstimatePercentOriginal] BIGINT
    ,[PageEstimatePercentOriginal] BIGINT
    ,[CompressionTypeRecommendation] VARCHAR(7)
    ,SizeCurrent BIGINT
    ,SizeRequested BIGINT
    ,PercentCompression NUMERIC(10, 2)
    );

CREATE TABLE ##tmpEstimateRow (
    objname SYSNAME
    ,schname SYSNAME
    ,indid INT
    ,partnr INT
    ,SizeCurrent BIGINT
    ,SizeRequested BIGINT
    ,SampleCurrent BIGINT
    ,SampleRequested BIGINT
    );

CREATE TABLE ##tmpEstimatePage (
    objname SYSNAME
    ,schname SYSNAME
    ,indid INT
    ,partnr INT
    ,SizeCurrent BIGINT
    ,SizeRequested BIGINT
    ,SampleCurrent BIGINT
    ,SampleRequested BIGINT
    );

INSERT INTO ##testdbacompression (
    [Schema]
    ,[TableName]
    ,[IndexName]
    ,[Partition]
    ,[IndexID]
    ,[IndexType]
    ,[PercentScan]
    ,[PercentUpdate]
    )
    SELECT s.NAME AS [Schema]
    ,t.NAME AS [TableName]
    ,x.NAME AS [IndexName]
    ,p.partition_number AS [Partition]
    ,x.Index_ID AS [IndexID]
    ,x.type_desc AS [IndexType]
    ,NULL AS [PercentScan]
    ,NULL AS [PercentUpdate]
FROM sys.tables t
INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
INNER JOIN sys.indexes x ON x.object_id = t.object_id
INNER JOIN sys.partitions p ON x.object_id = p.object_id
    AND x.Index_ID = p.Index_ID
WHERE objectproperty(t.object_id, 'IsUserTable') = 1
    AND p.data_compression_desc = 'NONE'
    AND p.rows > 0
ORDER BY [TableName] ASC;

DECLARE @sqlVersion int
SELECT @sqlVersion = substring(CONVERT(VARCHAR,SERVERPROPERTY('ProductVersion')),0,CHARINDEX('.',(CONVERT(VARCHAR,SERVERPROPERTY('ProductVersion')))))
IF @sqlVersion >= '12'
    BEGIN
        -- remove memory optimized tables
        DELETE tdc
        FROM ##testdbacompression tdc
        INNER JOIN sys.tables t
            ON SCHEMA_NAME(t.schema_id) = tdc.[Schema]
            AND t.name = tdc.TableName
        WHERE t.is_memory_optimized = 1
    END
IF @sqlVersion >= '13'
    BEGIN
        -- remove tables with encrypted columns
        DELETE tdc
        FROM ##testdbacompression tdc
        INNER JOIN sys.tables t
            ON SCHEMA_NAME(t.schema_id) = tdc.[Schema]
            AND t.name = tdc.TableName
        INNER JOIN sys.columns c
            ON t.object_id = c.object_id
        WHERE encryption_type IS NOT NULL
    END
IF @sqlVersion >= '14'
    BEGIN
        -- remove graph (node/edge) tables
        DELETE tdc
        FROM ##testdbacompression tdc
        INNER JOIN sys.tables t
            ON tdc.[Schema] = SCHEMA_NAME(t.schema_id)
            AND tdc.TableName = t.name
        WHERE (is_node = 1 OR is_edge = 1)
    END

DECLARE @schema SYSNAME
    ,@tbname SYSNAME
    ,@ixid INT

DECLARE cur CURSOR FAST_FORWARD
FOR
SELECT [Schema]
    ,[TableName]
    ,[IndexID]
FROM ##testdbacompression

OPEN cur

FETCH NEXT
FROM cur
INTO @schema
    ,@tbname
    ,@ixid

WHILE @@FETCH_STATUS = 0
BEGIN
    DECLARE @sqlcmd NVARCHAR(500)

    SET @sqlcmd = 'EXEC sp_estimate_data_compression_savings ''' + @schema + ''', ''' + @tbname + ''', ''' + cast(@ixid AS VARCHAR) + ''', NULL, ''ROW''';

    INSERT INTO ##tmpEstimateRow (
        objname
        ,schname
        ,indid
        ,partnr
        ,SizeCurrent
        ,SizeRequested
        ,SampleCurrent
        ,SampleRequested
        )
    EXECUTE sp_executesql @sqlcmd

    SET @sqlcmd = 'EXEC sp_estimate_data_compression_savings ''' + @schema + ''', ''' + @tbname + ''', ''' + cast(@ixid AS VARCHAR) + ''', NULL, ''PAGE''';

    INSERT INTO ##tmpEstimatePage (
        objname
        ,schname
        ,indid
        ,partnr
        ,SizeCurrent
        ,SizeRequested
        ,SampleCurrent
        ,SampleRequested
        )
    EXECUTE sp_executesql @sqlcmd

    FETCH NEXT
    FROM cur
    INTO @schema
        ,@tbname
        ,@ixid
END

CLOSE cur

DEALLOCATE cur;

--Update usage and partition_number - If database was restore the sys.dm_db_index_operational_stats will be empty until tables have accesses. Executing the sp_estimate_data_compression_savings first will make those entries appear
UPDATE ##testdbacompression
SET  [PercentScan] = i.range_scan_count * 100.0 / NULLIF((i.range_scan_count + i.leaf_insert_count + i.leaf_delete_count + i.leaf_update_count + i.leaf_page_merge_count + i.singleton_lookup_count), 0)
    ,[PercentUpdate] = i.leaf_update_count * 100.0 / NULLIF((i.range_scan_count + i.leaf_insert_count + i.leaf_delete_count + i.leaf_update_count + i.leaf_page_merge_count + i.singleton_lookup_count), 0)
FROM sys.dm_db_index_operational_stats(db_id(), NULL, NULL, NULL) i
INNER JOIN ##testdbacompression tmp ON OBJECT_ID(tmp.TableName) = i.[object_id]
    AND tmp.IndexID = i.index_id;

WITH tmp_cte (
    objname
    ,schname
    ,indid
    ,pct_of_orig_row
    ,pct_of_orig_page
    ,SizeCurrent
    ,SizeRequested
    )
AS (
    SELECT tr.objname
        ,tr.schname
        ,tr.indid
        ,(tr.SampleRequested * 100) / CASE
            WHEN tr.SampleCurrent = 0
                THEN 1
            ELSE tr.SampleCurrent
            END AS pct_of_orig_row
        ,(tp.SampleRequested * 100) / CASE
            WHEN tp.SampleCurrent = 0
                THEN 1
            ELSE tp.SampleCurrent
            END AS pct_of_orig_page
        ,tr.SizeCurrent
        ,tr.SizeRequested
    FROM ##tmpestimaterow tr
    INNER JOIN ##tmpestimatepage tp ON tr.objname = tp.objname
        AND tr.schname = tp.schname
        AND tr.indid = tp.indid
        AND tr.partnr = tp.partnr
    )
UPDATE ##testdbacompression
SET [RowEstimatePercentOriginal] = tcte.pct_of_orig_row
    ,[PageEstimatePercentOriginal] = tcte.pct_of_orig_page
    ,SizeCurrent = tcte.SizeCurrent
    ,SizeRequested = tcte.SizeRequested
    ,PercentCompression = 100 - (cast(tcte.[SizeRequested] AS NUMERIC(21, 2)) * 100 / (tcte.[SizeCurrent] - ABS(SIGN(tcte.[SizeCurrent])) + 1))
FROM tmp_cte tcte
    ,##testdbacompression tcomp
WHERE tcte.objname = tcomp.TableName
    AND tcte.schname = tcomp.[schema]
    AND tcte.indid = tcomp.IndexID;

WITH tmp_cte2 (
    TableName
    ,[schema]
    ,IndexID
    ,[CompressionTypeRecommendation]
    )
AS (
    SELECT TableName
        ,[schema]
        ,IndexID
        ,CASE
            WHEN [RowEstimatePercentOriginal] >= 100
                AND [PageEstimatePercentOriginal] >= 100
                THEN 'NO_GAIN'
            WHEN [PercentUpdate] >= 10
                THEN 'ROW'
            WHEN [PercentScan] <= 1
                AND [PercentUpdate] <= 1
                AND [RowEstimatePercentOriginal] < [PageEstimatePercentOriginal]
                THEN 'ROW'
            WHEN [PercentScan] <= 1
                AND [PercentUpdate] <= 1
                AND [RowEstimatePercentOriginal] > [PageEstimatePercentOriginal]
                THEN 'PAGE'
            WHEN [PercentScan] >= 60
                AND [PercentUpdate] <= 5
                THEN 'PAGE'
            WHEN [PercentScan] <= 35
                AND [PercentUpdate] <= 5
                THEN '?'
            ELSE 'ROW'
            END
    FROM ##testdbacompression
    )
UPDATE ##testdbacompression
SET [CompressionTypeRecommendation] = tcte2.[CompressionTypeRecommendation]
FROM tmp_cte2 tcte2
    ,##testdbacompression tcomp2
WHERE tcte2.TableName = tcomp2.TableName
    AND tcte2.[schema] = tcomp2.[schema]
    AND tcte2.IndexID = tcomp2.IndexID;

SET NOCOUNT ON;

SELECT DBName = DB_Name()
    ,[Schema]
    ,[TableName]
    ,[IndexName]
    ,[Partition]
    ,[IndexID]
    ,[IndexType]
    ,[PercentScan]
    ,[PercentUpdate]
    ,[RowEstimatePercentOriginal]
    ,[PageEstimatePercentOriginal]
    ,[CompressionTypeRecommendation]
    ,SizeCurrentKB = [SizeCurrent]
    ,SizeRequestedKB = [SizeRequested]
    ,PercentCompression
FROM ##testdbacompression;

IF OBJECT_ID('tempdb..##setdbacompression', 'U') IS NOT NULL
    DROP TABLE ##testdbacompression

IF OBJECT_ID('tempdb..##tmpEstimateRow', 'U') IS NOT NULL
    DROP TABLE ##tmpEstimateRow

IF OBJECT_ID('tempdb..##tmpEstimatePage', 'U') IS NOT NULL
    DROP TABLE ##tmpEstimatePage;

"
    }

    process {

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level VeryVerbose -Message "Connecting to $instance" -Target $instance
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SourceSqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failed to process Instance $Instance" -ErrorRecord $_ -Target $instance -Continue
            }

            $Server.ConnectionContext.StatementTimeout = 0

            [long]$instanceVersionNumber = $($server.VersionString).Replace(".", "")


            #If SQL Server 2016 SP1 (13.0.4001.0) or higher every version supports compression.
            if ($Server.EngineEdition -ne "EnterpriseOrDeveloper" -and $instanceVersionNumber -lt 13040010) {
                Stop-Function -Message "Compresison before SQLServer 2016 SP1 (13.0.4001.0) is only supported by enterprise, developer or evaluation edition. $Server has version $($server.VersionString) and edition is $($Server.EngineEdition)." -Target $db -Continue
            }
            #If IncludeSystemDBs is true, include systemdbs
            #look at all databases, online/offline/accessible/inaccessible and tell user if a db can't be queried.
            try {
                $dbs = $server.Databases | Where-Object IsAccessible

                if ($Database) {
                    $dbs = $dbs | Where-Object { $Database -contains $_.Name -and $_.IsSystemObject -eq 0 }
                }

                else {
                    $dbs = $dbs | Where-Object { $_.IsSystemObject -eq 0 }
                }

                if (Test-Bound "ExcludeDatabase") {
                    $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
                }
            }
            catch {
                Stop-Function -Message "Unable to gather list of databases for $instance" -Target $instance -ErrorRecord $_ -Continue
            }

            foreach ($db in $dbs) {
                try {
                    $dbCompatibilityLevel = [int]($db.CompatibilityLevel.ToString().Replace('Version', ''))

                    Write-Message -Level Verbose -Message "Querying $instance - $db"
                    if ($db.status -ne 'Normal' -or $db.IsAccessible -eq $false) {
                        Write-Message -Level Warning -Message "$db is not accessible." -Target $db
                        Continue
                    }

                    if ($dbCompatibilityLevel -lt 100) {
                        Stop-Function -Message "$db has a compatibility level lower than Version100 and will be skipped." -Target $db -Continue
                        Continue
                    }
                    #Execute query against individual database and add to output
                    foreach ($row in ($server.Query($sql, $db.Name))) {
                        [pscustomobject]@{
                            ComputerName                  = $server.ComputerName
                            InstanceName                  = $server.ServiceName
                            SqlInstance                   = $server.DomainInstanceName
                            Database                      = $row.DBName
                            Schema                        = $row.Schema
                            TableName                     = $row.TableName
                            IndexName                     = $row.IndexName
                            Partition                     = $row.Partition
                            IndexID                       = $row.IndexID
                            IndexType                     = $row.IndexType
                            PercentScan                   = $row.PercentScan
                            PercentUpdate                 = $row.PercentUpdate
                            RowEstimatePercentOriginal    = $row.RowEstimatePercentOriginal
                            PageEstimatePercentOriginal   = $row.PageEstimatePercentOriginal
                            CompressionTypeRecommendation = $row.CompressionTypeRecommendation
                            SizeCurrent                   = [dbasize]($row.SizeCurrentKB * 1024)
                            SizeRequested                 = [dbasize]($row.SizeRequestedKB * 1024)
                            PercentCompression            = $row.PercentCompression
                        }
                    }
                }
                catch {
                    Stop-Function -Message "Unable to query $instance - $db" -Target $db -ErrorRecord $_ -Continue
                }
            }
        }
    }
}
#ValidationTags#CodeStyle,Messaging,FlowControl,Pipeline#
function Test-DbaDbVirtualLogFile {
    <#
        .SYNOPSIS
            Returns calculations on the database virtual log files for database on a SQL instance.

        .DESCRIPTION
            Having a transaction log file with too many virtual log files (VLFs) can hurt database performance.

            Too many VLFs can cause transaction log backups to slow down and can also slow down database recovery and, in extreme cases, even affect insert/update/delete performance.

            References:
                http://www.sqlskills.com/blogs/kimberly/transaction-log-vlfs-too-many-or-too-few/
                http://blogs.msdn.com/b/saponsqlserver/archive/2012/02/22/too-many-virtual-log-files-vlfs-can-cause-slow-database-recovery.aspx

            If you've got a high number of VLFs, you can use Expand-SqlTLogResponsibly to reduce the number.

        .PARAMETER SqlInstance
            Specifies the SQL Server instance(s) to scan.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies the database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies the database(s) to exclude from processing. Options for this list are auto-populated from the server.

        .PARAMETER IncludeSystemDBs
            If this switch is enabled, system database information will be displayed.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: VLF, Database

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaDbVirtualLogFile

        .EXAMPLE
            Test-DbaDbVirtualLogFile -SqlInstance sqlcluster

            Returns all user database virtual log file counts for the sqlcluster instance.

        .EXAMPLE
            Test-DbaDbVirtualLogFile -SqlInstance sqlserver | Where-Object {$_.Count -ge 50}

            Returns user databases that have 50 or more VLFs.

        .EXAMPLE
            @('sqlserver','sqlcluster') | Test-DbaDbVirtualLogFile

            Returns all VLF information for the sqlserver and sqlcluster SQL Server instances. Processes data via the pipeline.

        .EXAMPLE
            Test-DbaDbVirtualLogFile -SqlInstance sqlcluster -Database db1, db2

            Returns VLF counts for the db1 and db2 databases on sqlcluster.
    #>
    [CmdletBinding()]
    [OutputType([System.Collections.ArrayList])]
    param ([parameter(ValueFromPipeline, Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$IncludeSystemDBs,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $dbs = $server.Databases
            if ($Database) {
                $dbs = $dbs | Where-Object Name -in $Database
            }
            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            if (!$IncludeSystemDBs) {
                $dbs = $dbs | Where-Object IsSystemObject -eq $false
            }

            foreach ($db in $dbs) {
                try {
                    $data = Get-DbaDbVirtualLogFile -SqlInstance $server -Database $db.Name
                    $logFile = Get-DbaDatabaseFile -SqlInstance $server -Database $db.Name | Where-Object Type -eq 1

                    $active = $data | Where-Object Status -eq 2
                    $inactive = $data | Where-Object Status -eq 0

                    [PSCustomObject]@{
                        ComputerName      = $server.ComputerName
                        InstanceName      = $server.ServiceName
                        SqlInstance       = $server.DomainInstanceName
                        Database          = $db.name
                        Total             = $data.Count
                        TotalCount        = $data.Count
                        Inactive          = if ($inactive -and $null -eq $inactive.Count) {1} else {$inactive.Count}
                        Active            = if ($active -and $null -eq $active.Count) {1} else {$active.Count}
                        LogFileName       = $logFile.LogicalName -join ","
                        LogFileGrowth     = $logFile.Growth -join ","
                        LogFileGrowthType = $logFile.GrowthType -join ","
                    } | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, Database, Total
                }
                catch {
                    Stop-Function -Message "Unable to query $($db.name) on $instance." -ErrorRecord $_ -Target $db -Continue
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Test-DbaVirtualLogFile
    }
}
function Test-DbaDiskAlignment {
    <#
        .SYNOPSIS
            Verifies that your non-dynamic disks are aligned according to physical constraints.

        .DESCRIPTION
            Returns $true or $false by default for one server. Returns Server name and IsBestPractice for more than one server.

            Please refer to your storage vendor best practices before following any advice below.

            By default issues with disk alignment should be resolved by a new installation of Windows Server 2008, Windows Vista, or later operating systems, but verifying disk alignment continues to be recommended as a best practice.
            While some versions of Windows use different starting alignments, if you are starting anew 1MB is generally the best practice offset for current operating systems (because it ensures that the partition offset % common stripe unit sizes == 0 )

            Caveats:
            * Dynamic drives (or those provisioned via third party software) may or may not have accurate results when polled by any of the built in tools, see your vendor for details.
            * Windows does not have a reliable way to determine stripe unit Sizes. These values are obtained from vendor disk management software or from your SAN administrator.
            * System drives in versions previous to Windows Server 2008 cannot be aligned, but it is generally not recommended to place SQL Server databases on system drives.

        .PARAMETER ComputerName
            The server(s) to check disk configuration on.

        .PARAMETER Detailed
            Output all properties, will be deprecated in 1.0.0 release.

        .PARAMETER Credential
            Specifies an alternate Windows account to use when enumerating drives on the server. May require Administrator privileges. To use:

            $cred = Get-Credential, then pass $cred object to the -Credential parameter.

        .PARAMETER SQLCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER NoSqlCheck
            If this switch is enabled, the disk(s) will not be checked for SQL Server data or log files.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .EXAMPLE
            Test-DbaDiskAlignment -ComputerName sqlserver2014a

            Tests the disk alignment of a single server named sqlserver2014a

        .EXAMPLE
            Test-DbaDiskAlignment -ComputerName sqlserver2014a, sqlserver2014b, sqlserver2014c

            Tests the disk alignment of multiple servers

        .NOTES
            Tags: Storage
            The preferred way to determine if your disks are aligned (or not) is to calculate:
            1. Partition offset - stripe unit size
            2. Stripe unit size - File allocation unit size

            References:
            Disk Partition Alignment Best Practices for SQL Server - https://technet.microsoft.com/en-us/library/dd758814(v=sql.100).aspx
            A great article and behind most of this code.

            Getting Partition Offset information with Powershell - http://sqlblog.com/blogs/jonathan_kehayias/archive/2010/03/01/getting-partition-Offset-information-with-powershell.aspx
            Thanks to Jonathan Kehayias!

            Decree: Set your partition Offset and block Size and make SQL Server faster - http://www.midnightdba.com/Jen/2014/04/decree-set-your-partition-Offset-and-block-Size-make-sql-server-faster/
            Thanks to Jen McCown!

            Disk Performance Hands On - http://www.kendalvandyke.com/2009/02/disk-performance-hands-on-series-recap.html
            Thanks to Kendal Van Dyke!

            Get WMI Disk Information - http://powershell.com/cs/media/p/7937.aspx
            Thanks to jbruns2010!

            Author: Constantine Kokkinos (https://constantinekokkinos.com, @mobileck)

            dbatools PowerShell module (https://dbatools.io, [email protected],)
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaDiskAlignment
    #>
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer", "SqlInstance")]
        [object[]]$ComputerName,
        [switch]$Detailed,
        [System.Management.Automation.PSCredential]$Credential,
        [System.Management.Automation.PSCredential]$SqlCredential,
        [switch]$NoSqlCheck,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Parameter 'Detailed'

        $sessionoption = New-CimSessionOption -Protocol DCom

        function Get-DiskAlignment {
            [CmdletBinding()]
            param (
                $CimSession,
                [string]$FunctionName = (Get-PSCallStack)[0].Command,
                [bool]$NoSqlCheck,
                [string]$ComputerName,
                [System.Management.Automation.PSCredential]$SqlCredential,
                [bool]$EnableException = $EnableException
            )

            $SqlInstances = @()
            $offsets = @()

            #region Retrieving partition/disk Information
            try {
                Write-Message -Level Verbose -Message "Gathering information about first partition on each disk for $ComputerName." -FunctionName $FunctionName

                try {
                    $partitions = Get-CimInstance -CimSession $CimSession -ClassName Win32_DiskPartition -Namespace "root\cimv2" -ErrorAction Stop
                }
                catch {
                    if ($_.Exception -match "namespace") {
                        Stop-Function -Message "Can't get disk alignment info for $ComputerName. Unsupported operating system." -InnerErrorRecord $_ -Target $ComputerName -FunctionName $FunctionName
                        return
                    }
                    else {
                        Stop-Function -Message "Can't get disk alignment info for $ComputerName. Check logs for more details." -InnerErrorRecord $_ -Target $ComputerName -FunctionName $FunctionName
                        return
                    }
                }


                $disks = @()
                $disks += $($partitions | ForEach-Object {
                        Get-CimInstance -CimSession $CimSession -Query "ASSOCIATORS OF {Win32_DiskPartition.DeviceID=""$($_.DeviceID.Replace("\", "\\"))""} WHERE AssocClass = Win32_LogicalDiskToPartition" |
                            Add-Member -Force -MemberType noteproperty -Name BlockSize -Value $_.BlockSize -PassThru |
                            Add-Member -Force -MemberType noteproperty -Name BootPartition -Value $_.BootPartition -PassThru |
                            Add-Member -Force -MemberType noteproperty -Name DiskIndex -Value $_.DiskIndex -PassThru |
                            Add-Member -Force -MemberType noteproperty -Name Index -Value $_.Index -PassThru |
                            Add-Member -Force -MemberType noteproperty -Name NumberOfBlocks -Value $_.NumberOfBlocks -PassThru |
                            Add-Member -Force -MemberType noteproperty -Name StartingOffset -Value $_.StartingOffset -PassThru |
                            Add-Member -Force -MemberType noteproperty -Name Type -Value $_.Type -PassThru
                    } |
                        Select-Object BlockSize, BootPartition, Description, DiskIndex, Index, Name, NumberOfBlocks, Size, StartingOffset, Type
                )
                Write-Message -Level Verbose -Message "Gathered CIM information." -FunctionName $FunctionName
            }
            catch {
                Stop-Function -Message "Can't connect to CIM on $ComputerName." -FunctionName $FunctionName -InnerErrorRecord $_
                return
            }
            #endregion Retrieving partition Information

            #region Retrieving Instances
            if (-not $NoSqlCheck) {
                Write-Message -Level Verbose -Message "Checking for SQL Services." -FunctionName $FunctionName
                $sqlservices = Get-CimInstance -ClassName Win32_Service -CimSession $CimSession | Where-Object DisplayName -like 'SQL Server (*'
                foreach ($service in $sqlservices) {
                    $instance = $service.DisplayName.Replace('SQL Server (', '')
                    $instance = $instance.TrimEnd(')')

                    $instancename = $instance.Replace("MSSQLSERVER", "Default")
                    Write-Message -Level Verbose -Message "Found instance $instancename" -FunctionName $FunctionName
                    if ($instance -eq 'MSSQLSERVER') {
                        $SqlInstances += $ComputerName
                    }
                    else {
                        $SqlInstances += "$ComputerName\$instance"
                    }
                }
                $sqlcount = $SqlInstances.Count
                Write-Message -Level Verbose -Message "$sqlcount instance(s) found." -FunctionName $FunctionName
            }
            #endregion Retrieving Instances

            #region Offsets
            foreach ($disk in $disks) {
                if (!$disk.name.StartsWith("\\")) {
                    $diskname = $disk.Name
                    if ($NoSqlCheck -eq $false) {
                        $sqldisk = $false

                        foreach ($SqlInstance in $SqlInstances) {
                            Write-Message -Level Verbose -Message "Connecting to SQL instance ($SqlInstance)." -FunctionName $FunctionName
                            try {
                                if ($null -ne $SqlCredential) {
                                    $smoserver = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
                                }
                                else {
                                    $smoserver = Connect-SqlInstance -SqlInstance $SqlInstance # win auth
                                }
                                $sql = "Select count(*) as Count from sys.master_files where physical_name like '$diskname%'"
                                Write-Message -Level Verbose -Message "Query is: $sql" -FunctionName $FunctionName
                                Write-Message -Level Verbose -Message "SQL Server is: $SqlInstance." -FunctionName $FunctionName
                                $sqlcount = $smoserver.Databases['master'].ExecuteWithResults($sql).Tables[0].Count
                                if ($sqlcount -gt 0) {
                                    $sqldisk = $true
                                    break
                                }
                            }
                            catch {
                                Stop-Function -Message "Can't connect to $ComputerName ($SqlInstance)." -FunctionName $FunctionName -InnerErrorRecord $_
                                return
                            }
                        }
                    }

                    if ($NoSqlCheck -eq $false) {
                        if ($sqldisk -eq $true) {
                            $offsets += $disk
                        }
                    }
                    else {
                        $offsets += $disk
                    }
                }
            }
            #endregion Offsets

            #region Processing results
            Write-Message -Level Verbose -Message "Checking $($offsets.count) partitions." -FunctionName $FunctionName

            $allpartitions = @()
            foreach ($partition in $offsets) {
                # Unfortunately "Windows does not have a reliable way to determine stripe unit Sizes. These values are obtained from vendor disk management software or from your SAN administrator."
                # And this is the #1 most impactful issue with disk alignment :D
                # What we can do is test common stripe unit Sizes against the Offset we have and give advice if the Offset they chose would work in those scenarios
                $offset = $partition.StartingOffset / 1kb
                $type = $partition.Type
                $stripe_units = @(64, 128, 256, 512, 1024) # still wish I had a better way to verify this or someone to pat my back and say its alright.

                # testing dynamic disks, everyone states that info from dynamic disks is not to be trusted, so throw a warning.
                Write-Message -Level Verbose -Message "Testing for dynamic disks." -FunctionName $FunctionName
                if ($type -eq "Logical Disk Manager") {
                    $IsDynamicDisk = $true
                    Write-Message -Level Warning -Message "Disk is dynamic, all Offset calculations should be suspect, please refer to your vendor to determine actual Offset calculations." -FunctionName $FunctionName
                }
                else {
                    $IsDynamicDisk = $false
                }

                Write-Message -Level Verbose -Message "Checking for best practices offsets." -FunctionName $FunctionName

                if ($offset -ne 64 -and $offset -ne 128 -and $offset -ne 256 -and $offset -ne 512 -and $offset -ne 1024) {
                    $IsOffsetBestPractice = $false
                }
                else {
                    $IsOffsetBestPractice = $true
                }

                # as we cant tell the actual size of the file strip unit, just check all the sizes I know about
                foreach ($size in $stripe_units) {
                    if ($offset % $size -eq 0) {
                        # for proper alignment we really only need to know that your offset divided by your stripe unit size has a remainder of 0
                        $OffsetModuloKB = "$($offset % $size)"
                        $isBestPractice = $true
                    }
                    else {
                        $OffsetModuloKB = "$($offset % $size)"
                        $isBestPractice = $false
                    }

                    $output = [PSCustomObject]@{
                        Server                    = $ComputerName
                        Name                      = "$($partition.Name)"
                        PartitonSizeInMB          = $($partition.Size / 1MB)
                        PartitionType             = $partition.Type
                        TestingStripeSizeKB       = $size
                        OffsetModuluCalculationKB = $OffsetModuloKB
                        StartingOffsetKB          = $offset
                        IsOffsetBestPractice      = $IsOffsetBestPractice
                        IsBestPractice            = $isBestPractice
                        NumberOfBlocks            = $partition.NumberOfBlocks
                        BootPartition             = $partition.BootPartition
                        PartitionBlockSize        = $partition.BlockSize
                        IsDynamicDisk             = $IsDynamicDisk
                    }
                    $allpartitions += $output
                }
            }
            #endregion Processing results
            return $allpartitions
        }
    }

    process {
        foreach ($computer in $ComputerName) {
            Write-Message -Level VeryVerbose -Message "Processing: $computer."

            $computer = Resolve-DbaNetworkName -ComputerName $computer -Credential $credential
            $Computer = $computer.ComputerName

            if (!$Computer) {
                Stop-Function -Message "Couldn't resolve hostname. Skipping." -Continue
            }

            #region Connecting to server via Cim
            Write-Message -Level Verbose -Message "Creating CimSession on $computer over WSMan"

            if (!$Credential) {
                $cimsession = New-CimSession -ComputerName $Computer -ErrorAction Ignore
            }
            else {
                $cimsession = New-CimSession -ComputerName $Computer -ErrorAction Ignore -Credential $Credential
            }

            if ($null -eq $cimsession.id) {
                Write-Message -Level Verbose -Message "Creating CimSession on $computer over WSMan failed. Creating CimSession on $computer over DCOM."

                if (!$Credential) {
                    $cimsession = New-CimSession -ComputerName $Computer -SessionOption $sessionoption -ErrorAction Ignore -Credential $Credential
                }
                else {
                    $cimsession = New-CimSession -ComputerName $Computer -SessionOption $sessionoption -ErrorAction Ignore
                }
            }

            if ($null -eq $cimsession.id) {
                Stop-Function -Message "Can't create CimSession on $computer." -Target $Computer -Continue
            }
            #endregion Connecting to server via Cim

            Write-Message -Level Verbose -Message "Getting Power Plan information from $Computer."


            try {
                $data = Get-DiskAlignment -CimSession $cimsession -NoSqlCheck $NoSqlCheck -ComputerName $Computer -ErrorAction Stop
            }
            catch {
                Stop-Function -Message "Failed to process $($Computer): $($_.Exception.Message)" -Continue -InnerErrorRecord $_ -Target $Computer
            }

            if ($null -eq $data.Server) {
                Stop-Function -Message "CIM query to $Computer failed." -Continue -Target $computer
            }

            if ($data.Count -gt 1) {
                $data.GetEnumerator()
            }
            else {
                $data
            }
        }
    }
}
function Test-DbaDiskAllocation {
    <#
        .SYNOPSIS
            Checks all disks on a computer to see if they are formatted with allocation units of 64KB.

        .DESCRIPTION
            Checks all disks on a computer for disk allocation units that match best practice recommendations. If one server is checked, only $true or $false is returned. If multiple servers are checked, each server's name and an IsBestPractice field are returned.

            Specify -Detailed for details.

            References:
            https://technet.microsoft.com/en-us/library/dd758814(v=sql.100).aspx - "The performance question here is usually not one of correlation per the formula, but whether the cluster size has been explicitly defined at 64 KB, which is a best practice for SQL Server."

            http://tk.azurewebsites.net/2012/08/

        .PARAMETER ComputerName
            The server(s) to check disk configuration on.

        .PARAMETER NoSqlCheck
            If this switch is enabled, the disk(s) will not be checked for SQL Server data or log files.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Detailed
            Output all properties, will be deprecated in 1.0.0 release.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: CIM, Storage
            Requires: Windows sysadmin access on SQL Servers

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaDiskAllocation

        .EXAMPLE
            Test-DbaDiskAllocation -ComputerName sqlserver2014a

            Scans all disks on server sqlserver2014a for best practice allocation unit size.

        .EXAMPLE
            Test-DbaDiskAllocation -ComputerName sqlserver2014 | Select-Output *

            Scans all disks on server sqlserver2014a for allocation unit size and returns detailed results for each.

        .EXAMPLE
            Test-DbaDiskAllocation -ComputerName sqlserver2014a -NoSqlCheck

            Scans all disks not hosting SQL Server data or log files on server sqlserver2014a for best practice allocation unit size.
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    [OutputType("System.Collections.ArrayList", "System.Boolean")]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer", "SqlInstance")]
        [object[]]$ComputerName,
        [switch]$NoSqlCheck,
        [object]$SqlCredential,
        [switch]$Detailed,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter Detailed

        $sessionoptions = New-CimSessionOption -Protocol DCOM

        function Get-AllDiskAllocation {
            $alldisks = @()
            $SqlInstances = @()

            try {
                Write-Message -Level Verbose -Message "Getting disk information from $computer."

                # $query = "Select Label, BlockSize, Name from Win32_Volume WHERE FileSystem='NTFS'"
                # $disks = Get-WmiObject -ComputerName $ipaddr -Query $query | Sort-Object -Property Name
                $disks = Get-CimInstance -CimSession $CIMsession -ClassName win32_volume -Filter "FileSystem='NTFS'" -ErrorAction Stop | Sort-Object -Property Name
            }
            catch {
                Stop-Function -Message "Can't connect to WMI on $computer."
                return
            }

            if ($NoSqlCheck -eq $false) {
                Write-Message -Level Verbose -Message "Checking for SQL Services"
                $sqlservices = Get-Service -ComputerName $ipaddr | Where-Object { $_.DisplayName -like 'SQL Server (*' }
                foreach ($service in $sqlservices) {
                    $instance = $service.DisplayName.Replace('SQL Server (', '')
                    $instance = $instance.TrimEnd(')')

                    $instancename = $instance.Replace("MSSQLSERVER", "Default")
                    Write-Message -Level Verbose -Message "Found instance $instancename."

                    if ($instance -eq 'MSSQLSERVER') {
                        $SqlInstances += $ipaddr
                    }
                    else {
                        $SqlInstances += "$ipaddr\$instance"
                    }
                }
                $sqlcount = $SqlInstances.Count

                Write-Message -Level Verbose -Message "$sqlcount instance(s) found."
            }

            foreach ($disk in $disks) {
                if (!$disk.name.StartsWith("\\")) {
                    $diskname = $disk.Name

                    if ($NoSqlCheck -eq $false) {
                        $sqldisk = $false

                        foreach ($SqlInstance in $SqlInstances) {
                            Write-Message -Level Verbose -Message "Connecting to SQL instance ($SqlInstance)."
                            try {
                                $smoserver = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
                                $sql = "Select count(*) as Count from sys.master_files where physical_name like '$diskname%'"
                                $sqlcount = $smoserver.Databases['master'].ExecuteWithResults($sql).Tables[0].Count
                                if ($sqlcount -gt 0) {
                                    $sqldisk = $true
                                    break
                                }
                            }
                            catch {
                                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                                continue
                            }
                        }
                    }

                    if ($disk.BlockSize -eq 65536) {
                        $IsBestPractice = $true
                    }
                    else {
                        $IsBestPractice = $false
                    }

                    $windowsdrive = "$env:SystemDrive\"

                    if ($diskname -eq $windowsdrive) {
                        $IsBestPractice = $false
                    }

                    if ($NoSqlCheck -eq $false) {
                        $alldisks += [PSCustomObject]@{
                            Server         = $computer
                            Name           = $diskname
                            Label          = $disk.Label
                            BlockSize      = $disk.BlockSize
                            IsSqlDisk      = $sqldisk
                            IsBestPractice = $IsBestPractice
                        }
                    }
                    else {
                        $alldisks += [PSCustomObject]@{
                            Server         = $computer
                            Name           = $diskname
                            Label          = $disk.Label
                            BlockSize      = $disk.BlockSize
                            IsBestPractice = $IsBestPractice
                        }
                    }
                }
            }
            return $alldisks
        }
    }

    process {
        foreach ($computer in $ComputerName) {

            $computer = Resolve-DbaNetworkName -ComputerName $computer -Credential $credential
            $ipaddr = $computer.IpAddress
            $Computer = $computer.ComputerName

            if (!$Computer) {
                Stop-Function -Message "Couldn't resolve hostname. Skipping." -Continue
            }

            Write-Message -Level Verbose -Message "Creating CimSession on $computer over WSMan."

            if (!$Credential) {
                $cimsession = New-CimSession -ComputerName $Computer -ErrorAction SilentlyContinue
            }
            else {
                $cimsession = New-CimSession -ComputerName $Computer -ErrorAction SilentlyContinue -Credential $Credential
            }

            if ($null -eq $cimsession.id) {
                Write-Message -Level Verbose -Message "Creating CimSession on $computer over WSMan failed. Creating CimSession on $computer over DCOM."

                if (!$Credential) {
                    $cimsession = New-CimSession -ComputerName $Computer -SessionOption $sessionoption -ErrorAction SilentlyContinue -Credential $Credential
                }
                else {
                    $cimsession = New-CimSession -ComputerName $Computer -SessionOption $sessionoption -ErrorAction SilentlyContinue
                }
            }

            if ($null -eq $cimsession.id) {
                Stop-Function -Message "Can't create CimSession on $computer" -Target $Computer
            }

            Write-Message -Level Verbose -Message "Getting Power Plan information from $Computer"

            $data = Get-AllDiskAllocation $computer

            if ($data.Count -gt 1) {
                $data.GetEnumerator()
            }
            else {
                $data
            }
        }
    }
}
function Test-DbaDiskSpeed {
    <#
    .SYNOPSIS
        Tests how disks are performing.

    .DESCRIPTION
        Tests how disks are performing.

        This command uses a query from Rich Benner which was adapted from David Pless's article:
        https://blogs.msdn.microsoft.com/dpless/2010/12/01/leveraging-sys-dm_io_virtual_file_stats/
        https://github.com/RichBenner/PersonalCode/blob/master/Disk_Speed_Check.sql

    .PARAMETER SqlInstance
        Allows you to specify a comma separated list of servers to query.

    .PARAMETER SqlCredential
       Allows you to login to the SQL Server using alternative credentials.

    .PARAMETER Database
        The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

    .PARAMETER ExcludeDatabase
        The database(s) to exclude - this list is auto-populated from the server

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Author: Chrissy LeMaire
        Tags: Performance

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Test-DbaDiskSpeed

    .EXAMPLE
        Test-DbaDiskSpeed -SqlInstance sql2008, sqlserver2012
        Tests how disks are performing on sql2008 and sqlserver2012.

    .EXAMPLE
        Test-DbaDiskSpeed -SqlInstance sql2008 -Database tempdb
        Tests how disks storing tempdb files on sql2008 are performing.
    #>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$EnableException
    )

    begin {

        $sql = "SELECT  SERVERPROPERTY('MachineName') AS ComputerName,
        ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
        SERVERPROPERTY('ServerName') AS SqlInstance, db_name(a.database_id) AS [Database]
        , CAST(((a.size_on_disk_bytes/1024)/1024.0)/1024 AS DECIMAL(10,2)) AS [SizeGB]
        , RIGHT(b.physical_name, CHARINDEX('\', REVERSE(b.physical_name)) -1) AS [FileName]
        , a.file_id AS [FileID]
        , CASE WHEN a.file_id = 2 THEN 'Log' ELSE 'Data' END AS [FileType]
        , UPPER(SUBSTRING(b.physical_name, 1, 2)) AS [DiskLocation]
        , a.num_of_reads AS [Reads]
        , CASE WHEN a.num_of_reads < 1 THEN NULL ELSE CAST(a.io_stall_read_ms/(a.num_of_reads) AS INT) END AS [AverageReadStall]
        , CASE
            WHEN CASE WHEN a.num_of_reads < 1 THEN NULL ELSE CAST(a.io_stall_read_ms/(a.num_of_reads) AS INT) END < 10 THEN 'Very Good'
            WHEN CASE WHEN a.num_of_reads < 1 THEN NULL ELSE CAST(a.io_stall_read_ms/(a.num_of_reads) AS INT) END < 20 THEN 'OK'
            WHEN CASE WHEN a.num_of_reads < 1 THEN NULL ELSE CAST(a.io_stall_read_ms/(a.num_of_reads) AS INT) END < 50 THEN 'Slow, Needs Attention'
            WHEN CASE WHEN a.num_of_reads < 1 THEN NULL ELSE CAST(a.io_stall_read_ms/(a.num_of_reads) AS INT) END >= 50 THEN 'Serious I/O Bottleneck'
            END AS [ReadPerformance]
        , a.num_of_writes AS [Writes]
        , CASE WHEN a.num_of_writes < 1 THEN NULL ELSE CAST(a.io_stall_write_ms/a.num_of_writes AS INT) END AS [AverageWriteStall]
        , CASE
            WHEN CASE WHEN a.num_of_writes < 1 THEN NULL ELSE CAST(a.io_stall_write_ms/(a.num_of_writes) AS INT) END < 10 THEN 'Very Good'
            WHEN CASE WHEN a.num_of_writes < 1 THEN NULL ELSE CAST(a.io_stall_write_ms/(a.num_of_writes) AS INT) END < 20 THEN 'OK'
            WHEN CASE WHEN a.num_of_writes < 1 THEN NULL ELSE CAST(a.io_stall_write_ms/(a.num_of_writes) AS INT) END < 50 THEN 'Slow, Needs Attention'
            WHEN CASE WHEN a.num_of_writes < 1 THEN NULL ELSE CAST(a.io_stall_write_ms/(a.num_of_writes) AS INT) END >= 50 THEN 'Serious I/O Bottleneck'
            END AS [WritePerformance]
        FROM sys.dm_io_virtual_file_stats (NULL, NULL) a
        JOIN sys.master_files b
            ON a.file_id = b.file_id
            AND a.database_id = b.database_id"

        if ($Database -or $ExcludeDatabase) {
            if ($database) {
                $where = " where db_name(a.database_id) in ('$($Database -join "'")') "
            }
            if ($ExcludeDatabase) {
                $where = " where db_name(a.database_id) not in ('$($ExcludeDatabase -join "'")') "
            }
            $sql += $where
        }

        $sql += " ORDER BY (a.num_of_reads + a.num_of_writes) DESC"
    }

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            Write-Message -Level Debug -Message "Executing $sql"
            $server.Query("$sql")
        }
    }
}
function Test-DbaIdentityUsage {
    <#
        .SYNOPSIS
            Displays information relating to IDENTITY seed usage.  Works on SQL Server 2008 and above.

        .DESCRIPTION
            IDENTITY seeds have max values based off of their data type.  This module will locate identity columns and report the seed usage.

        .PARAMETER SqlInstance
            Allows you to specify a comma separated list of servers to query.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto-populated from the server

        .PARAMETER Threshold
            Allows you to specify a minimum % of the seed range being utilized.  This can be used to ignore seeds that have only utilized a small fraction of the range.

        .PARAMETER ExcludeSystemDb
            Allows you to suppress output on system databases

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Brandon Abshire, netnerds.net
            Tags: Identity, Table, Column

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaIdentityUsage

        .EXAMPLE
            Test-DbaIdentityUsage -SqlInstance sql2008, sqlserver2012

            Check identity seeds for servers sql2008 and sqlserver2012.

        .EXAMPLE
            Test-DbaIdentityUsage -SqlInstance sql2008 -Database TestDB

            Check identity seeds on server sql2008 for only the TestDB database

        .EXAMPLE
            Test-DbaIdentityUsage -SqlInstance sql2008 -Database TestDB -Threshold 20

            Check identity seeds on server sql2008 for only the TestDB database, limiting results to 20% utilization of seed range or higher
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [parameter(Position = 1, Mandatory = $false)]
        [int]$Threshold = 0,
        [parameter(Position = 2, Mandatory = $false)]
        [Alias("NoSystemDb")]
        [switch]$ExcludeSystemDb,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter NoSystemDb

        $sql = ";WITH CT_DT AS
        (
            SELECT 'tinyint' AS DataType, 0 AS MinValue ,255 AS MaxValue UNION
            SELECT 'smallint' AS DataType, -32768 AS MinValue ,32767 AS MaxValue UNION
            SELECT 'int' AS DataType, -2147483648 AS MinValue ,2147483647 AS MaxValue UNION
            SELECT 'bigint' AS DataType, -9223372036854775808 AS MinValue ,9223372036854775807 AS MaxValue
        ), CTE_1
        AS
        (
          SELECT SCHEMA_NAME(o.schema_id) AS SchemaName,
                 OBJECT_NAME(a.Object_id) as TableName,
                 a.Name as ColumnName,
                 seed_value AS SeedValue,
                 CONVERT(bigint, increment_value) as IncrementValue,

                 CONVERT(bigint, ISNULL(a.last_value, seed_value)) AS LastValue,

                 (CASE
                        WHEN CONVERT(bigint, increment_value) < 0 THEN
                            (CONVERT(bigint, seed_value)
                            - CONVERT(bigint, ISNULL(last_value, seed_value))
                            + (CASE WHEN CONVERT(bigint, seed_value) <> 0 THEN ABS(CONVERT(bigint, increment_value)) ELSE 0 END))
                        ELSE
                            (CONVERT(bigint, ISNULL(last_value, seed_value))
                            - CONVERT(bigint, seed_value)
                            + (CASE WHEN CONVERT(bigint, seed_value) <> 0 THEN ABS(CONVERT(bigint, increment_value)) ELSE 0 END))
                    END) / ABS(CONVERT(bigint, increment_value))  AS NumberOfUses,

                  CAST (
                        (CASE
                            WHEN CONVERT(Numeric(20, 0), increment_value) < 0 THEN
                                ABS(CONVERT(Numeric(20, 0),dt.MinValue)
                                - CONVERT(Numeric(20, 0), seed_value)
                                - (CASE WHEN CONVERT(Numeric(20, 0), seed_value) <> 0 THEN ABS(CONVERT(Numeric(20, 0), increment_value)) ELSE 0 END))
                            ELSE
                                CONVERT(Numeric(20, 0),dt.MaxValue)
                                - CONVERT(Numeric(20, 0), seed_value)
                                + (CASE WHEN CONVERT(Numeric(20, 0), seed_value) <> 0 THEN ABS(CONVERT(Numeric(20, 0), increment_value)) ELSE 0 END)
                        END) / ABS(CONVERT(Numeric(20, 0), increment_value))
                    AS Numeric(20, 0)) AS MaxNumberRows

            FROM sys.identity_columns a
                INNER JOIN sys.objects o
                   ON a.object_id = o.object_id
                INNER JOIN sys.types As b
                     ON a.system_type_id = b.system_type_id
                INNER JOIN CT_DT dt
                     ON b.name = dt.DataType
          WHERE a.seed_value is not null
        ),
        CTE_2
        AS
        (
        SELECT SchemaName, TableName, ColumnName, CONVERT(BIGINT, SeedValue) AS SeedValue, CONVERT(BIGINT, IncrementValue) AS IncrementValue, LastValue, ABS(CONVERT(NUMERIC(20,0),MaxNumberRows)) AS MaxNumberRows, NumberOfUses,
               CONVERT(Numeric(18,2), ((CONVERT(Float, NumberOfUses) / ABS(CONVERT(Numeric(20, 0),MaxNumberRows)) * 100))) AS [PercentUsed]
          FROM CTE_1
        )
        SELECT DB_NAME() as DatabaseName, SchemaName, TableName, ColumnName, SeedValue, IncrementValue, LastValue, MaxNumberRows, NumberOfUses, [PercentUsed]
          FROM CTE_2"

        if ($Threshold -gt 0) {
            $sql += " WHERE [PercentUsed] >= " + $Threshold + " ORDER BY [PercentUsed] DESC"
        }
        else {
            $sql += " ORDER BY [PercentUsed] DESC"
        }
    }

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $dbs = $server.Databases

            if ($Database) {
                $dbs = $dbs | Where-Object Name -In $Database
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            if ($ExcludeSystemDb) {
                $dbs = $dbs | Where-Object IsSystemObject -EQ $false
            }

            foreach ($db in $dbs) {
                Write-Message -Level Verbose -Message "Processing $db on $instance"

                if ($db.IsAccessible -eq $false) {
                    Stop-Function -Message "The database $db is not accessible. Skipping." -Continue
                }

                try {
                    $results = $db.Query($sql)
                }
                catch {
                    Stop-Function -Message "Error capturing data on $db" -Target $instance -ErrorRecord $_ -Exception $_.Exception -Continue
                }

                foreach ($row in $results) {
                    if ($row.PercentUsed -eq [System.DBNull]::Value) {
                        continue
                    }

                    if ($row.PercentUsed -ge $threshold) {
                        [PSCustomObject]@{
                            ComputerName   = $server.ComputerName
                            InstanceName   = $server.ServiceName
                            SqlInstance    = $server.DomainInstanceName
                            Database       = $row.DatabaseName
                            Schema         = $row.SchemaName
                            Table          = $row.TableName
                            Column         = $row.ColumnName
                            SeedValue      = $row.SeedValue
                            IncrementValue = $row.IncrementValue
                            LastValue      = $row.LastValue
                            MaxNumberRows  = $row.MaxNumberRows
                            NumberOfUses   = $row.NumberOfUses
                            PercentUsed    = $row.PercentUsed
                        } | Select-DefaultView -Exclude MaxNumberRows, NumberOfUses
                    }
                }
            }
        }
    }
}

function Test-DbaJobOwner {
    <#
        .SYNOPSIS
            Checks SQL Agent Job owners against a login to validate which jobs do not match that owner.

        .DESCRIPTION
            This function checks all SQL Agent Jobs on an instance against a SQL login to validate if that login owns those SQL Agent Jobs or not.

            By default, the function checks against 'sa' for ownership, but the user can pass a specific login if they use something else.

            Only SQL Agent Jobs that do not match this ownership will be displayed.

            Best practice reference: http://sqlmag.com/blog/sql-server-tip-assign-ownership-jobs-sysadmin-account

        .PARAMETER SqlInstance
            Specifies the SQL Server instance(s) to scan.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Job
            Specifies the job(s) to process. Options for this list are auto-populated from the server. If unspecified, all jobs will be processed.

        .PARAMETER ExcludeJob
            Specifies the job(s) to exclude from processing. Options for this list are auto-populated from the server.

        .PARAMETER Login
            Specifies the login that you wish check for ownership. This defaults to 'sa' or the sysadmin name if sa was renamed. This must be a valid security principal which exists on the target server.

        .PARAMETER Detailed
            Output all properties, will be deprecated in 1.0.0 release.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Agent, Job, Owner
            Author: Michael Fal (@Mike_Fal), http://mikefal.net

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaJobOwner

        .EXAMPLE
            Test-DbaJobOwner -SqlInstance localhost

            Returns all SQL Agent Jobs where the owner does not match 'sa'.

        .EXAMPLE
            Test-DbaJobOwner -SqlInstance localhost -ExcludeJob 'syspolicy_purge_history'

            Returns SQL Agent Jobs except for the syspolicy_purge_history job

        .EXAMPLE
            Test-DbaJobOwner -SqlInstance localhost -Login DOMAIN\account

            Returns all SQL Agent Jobs where the owner does not match DOMAIN\account. Note
            that Login must be a valid security principal that exists on the target server.
    #>
    [CmdletBinding()]
    [OutputType('System.Object[]')]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Jobs")]
        [object[]]$Job,
        [object[]]$ExcludeJob,
        [Alias("TargetLogin")]
        [string]$Login,
        [switch]$Detailed,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter Detailed
        #connect to the instance and set return array empty
        $return = @()
    }
    process {
        foreach ($servername in $SqlInstance) {
            #connect to the instance
            Write-Message -Level Verbose -Message "Connecting to $servername."
            $server = Connect-SqlInstance $servername -SqlCredential $SqlCredential

            #Validate login
            if ($Login -and ($server.Logins.Name) -notcontains $Login) {
                if ($SqlInstance.count -eq 1) {
                    Stop-Function -Message "Invalid login: $Login."
                    return
                }
                else {
                    Write-Message -Level Warning -Message "$Login is not a valid login on $servername. Moving on."
                    continue
                }
            }
            if ($Login -and $server.Logins[$Login].LoginType -eq 'WindowsGroup') {
                Stop-Function -Message "$Login is a Windows Group and can not be a job owner."
                return
            }

            #Sets the Default Login to sa if the Login Paramater is not set.
            if(!($PSBoundParameters.ContainsKey('Login'))){
                $Login = "sa"
            }
            #sql2000 id property is empty -force target login to 'sa' login
            if ($Login -and ( ($server.VersionMajor -lt 9) -and ([string]::IsNullOrEmpty($Login)) )) {
                $Login = "sa"
            }
            # dynamic sa name for orgs who have changed their sa name
            if ($Login -eq "sa") {
                $Login = ($server.Logins | Where-Object { $_.id -eq 1 }).Name
            }

            #Get database list. If value for -Job is passed, massage to make it a string array.
            #Otherwise, use all jobs on the instance where owner not equal to -TargetLogin
            Write-Message -Level Verbose -Message "Gathering jobs to check."
            if ($Job) {
                $jobCollection = $server.JobServer.Jobs | Where-Object { $Job -contains $_.Name }
            }
            elseif ($ExcludeJob) {
                $jobCollection = $server.JobServer.Jobs | Where-Object { $ExcludeJob -notcontains $_.Name }
            }
            else {
                $jobCollection = $server.JobServer.Jobs
            }

            #for each database, create custom object for return set.
            foreach ($j in $jobCollection) {
                Write-Message -Level Verbose -Message "Checking $j"
                $row = [ordered]@{
                    Server       = $server.Name
                    Job          = $j.Name
                    JobType      = if ($j.CategoryID -eq 1){ "Remote" } else { $j.JobType }
                    CurrentOwner = $j.OwnerLoginName
                    TargetOwner  = $Login
                    OwnerMatch   = if ($j.CategoryID -eq 1){ $true } else { $j.OwnerLoginName -eq $Login }

                }
                #add each custom object to the return array
                $return += New-Object PSObject -Property $row
            }
            if($Job){
                $results = $return
            }
            else{
                $results = $return | Where-Object {$_.OwnerMatch -eq $False}
            }
        }
    }
    end {
        #return results
            Select-DefaultView -InputObject $results -Property Server, Job, JobType, CurrentOwner, TargetOwner, OwnerMatch
    }

}
function Test-DbaLastBackup {
    <#
        .SYNOPSIS
            Quickly and easily tests the last set of full backups for a server.

        .DESCRIPTION
            Restores all or some of the latest backups and performs a DBCC CHECKDB.

            1. Gathers information about the last full backups
            2. Restores the backups to the Destination with a new name. If no Destination is specified, the originating SqlServer wil be used.
            3. The database is restored as "dbatools-testrestore-$databaseName" by default, but you can change dbatools-testrestore to whatever you would like using -Prefix
            4. The internal file names are also renamed to prevent conflicts with original database
            5. A DBCC CHECKDB is then performed
            6. And the test database is finally dropped

        .PARAMETER SqlInstance
            The SQL Server to connect to. Unlike many of the other commands, you cannot specify more than one server.

        .PARAMETER Destination
            The destination server to use to test the restore. By default, the Destination will be set to the source server

            If a different Destination server is specified, you must ensure that the database backups are on a shared location

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER DestinationCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database backups to test. If -Database is not provided, all database backups will be tested.

        .PARAMETER ExcludeDatabase
            Exclude specific Database backups to test.

        .PARAMETER DataDirectory
            Specifies an alternative directory for mdfs, ndfs and so on. The command uses the SQL Server's default data directory for all restores.

        .PARAMETER LogDirectory
            Specifies an alternative directory for ldfs. The command uses the SQL Server's default log directory for all restores.

        .PARAMETER VerifyOnly
            If this switch is enabled, VERIFYONLY will be performed. An actual restore will not be executed.

        .PARAMETER NoCheck
            If this switch is enabled, DBCC CHECKDB will be skipped

        .PARAMETER NoDrop
            If this switch is enabled, the newly-created test database will not be dropped.

        .PARAMETER CopyFile
            If this switch is enabled, the backup file will be copied to the destination default backup location unless CopyPath is specified.

        .PARAMETER CopyPath
            Specifies a path relative to the SQL Server to copy backups when CopyFile is specified. If not specified will use destination default backup location. If destination SQL Server is not local, admin UNC paths will be utilized for the copy.

        .PARAMETER MaxMB
            Databases larger than this value will not be restored.

        .PARAMETER AzureCredential
            The name of the SQL Server credential on the destination instance that holds the key to the azure storage account.

        .PARAMETER IncludeCopyOnly
            If this switch is enabled, copy only backups will not be counted as a last backup.

        .PARAMETER IgnoreLogBackup
            If this switch is enabled, transaction log backups will be ignored. The restore will stop at the latest full or differential backup point.

        .PARAMETER Prefix
            The database is restored as "dbatools-testrestore-$databaseName" by default. You can change dbatools-testrestore to whatever you would like using this parameter.

       .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: DisasterRecovery, Backup, Restore

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaLastBackup

        .EXAMPLE
            Test-DbaLastBackup -SqlInstance sql2016

            Determines the last full backup for ALL databases, attempts to restore all databases (with a different name and file structure), then performs a DBCC CHECKDB.

            Once the test is complete, the test restore will be dropped.

        .EXAMPLE
            Test-DbaLastBackup -SqlInstance sql2016 -Database master

            Determines the last full backup for master, attempts to restore it, then performs a DBCC CHECKDB.

        .EXAMPLE
            Test-DbaLastBackup -SqlInstance sql2016 -Database model, master -VerifyOnly

        .EXAMPLE
            Test-DbaLastBackup -SqlInstance sql2016 -NoCheck -NoDrop

            Skips the DBCC CHECKDB check. This can help speed up the tests but makes it less tested. The test restores will remain on the server.

        .EXAMPLE
            Test-DbaLastBackup -SqlInstance sql2016 -DataDirectory E:\bigdrive -LogDirectory L:\bigdrive -MaxMB 10240

            Restores data and log files to alternative locations and only restores databases that are smaller than 10 GB.

        .EXAMPLE
            Test-DbaLastBackup -SqlInstance sql2014 -Destination sql2016 -CopyFile

            Copies the backup files for sql2014 databases to sql2016 default backup locations and then attempts restore from there.

        .EXAMPLE
            Test-DbaLastBackup -SqlInstance sql2014 -Destination sql2016 -CopyFile -CopyPath "\\BackupShare\TestRestore\"

            Copies the backup files for sql2014 databases to sql2016 default backup locations and then attempts restore from there.
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer", "Source")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [DbaInstanceParameter]$Destination,
        [object]$DestinationCredential,
        [string]$DataDirectory,
        [string]$LogDirectory,
        [string]$Prefix = "dbatools-testrestore-",
        [switch]$VerifyOnly,
        [switch]$NoCheck,
        [switch]$NoDrop,
        [switch]$CopyFile,
        [string]$CopyPath,
        [int]$MaxMB,
        [switch]$IncludeCopyOnly,
        [switch]$IgnoreLogBackup,
        [string]$AzureCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $sqlinstance) {

            if (-not $destination -or $nodestination) {
                $nodestination = $true
                $destination = $instance
                $DestinationCredential = $SqlCredential
            }

            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $sourceserver = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            try {
                Write-Message -Level Verbose -Message "Connecting to $destination."
                $destserver = Connect-SqlInstance -SqlInstance $destination -SqlCredential $DestinationCredential
            }
            catch {
                Stop-Function -Message "Failed to connect to: $destination." -Target $destination -Continue
            }

            if ($destserver.VersionMajor -lt $sourceserver.VersionMajor) {
                Stop-Function -Message "$Destination is a lower version than $instance. Backups would be incompatible." -Continue
            }

            if ($destserver.VersionMajor -eq $sourceserver.VersionMajor -and $destserver.VersionMinor -lt $sourceserver.VersionMinor) {
                Stop-Function -Message "$Destination is a lower version than $instance. Backups would be incompatible." -Continue
            }

            if ($CopyPath) {
                $testpath = Test-DbaSqlPath -SqlInstance $destserver -Path $CopyPath
                if (!$testpath) {
                    Stop-Function -Message "$destserver cannot access $CopyPath." -Continue
                }
            }
            else {
                # If not CopyPath is specified, use the destination server default backup directory
                $copyPath = $destserver.BackupDirectory
            }

            if ($instance -ne $destination -and !$CopyFile) {
                $sourcerealname = $sourceserver.ComputerNetBiosName
                $destrealname = $destserver.ComputerNetBiosName

                if ($BackupFolder) {
                    if ($BackupFolder.StartsWith("\\") -eq $false -and $sourcerealname -ne $destrealname) {
                        Stop-Function -Message "Backup folder must be a network share if the source and destination servers are not the same." -Continue
                    }
                }
            }

            $source = $sourceserver.DomainInstanceName
            $destination = $destserver.DomainInstanceName

            if ($datadirectory) {
                if (!(Test-DbaSqlPath -SqlInstance $destserver -Path $datadirectory)) {
                    $serviceaccount = $destserver.ServiceAccount
                    Stop-Function -Message "Can't access $datadirectory Please check if $serviceaccount has permissions." -Continue
                }
            }
            else {
                $datadirectory = Get-SqlDefaultPaths -SqlInstance $destserver -FileType mdf
            }

            if ($logdirectory) {
                if (!(Test-DbaSqlPath -SqlInstance $destserver -Path $logdirectory)) {
                    $serviceaccount = $destserver.ServiceAccount
                    Stop-Function -Message "$Destination can't access its local directory $logdirectory. Please check if $serviceaccount has permissions." -Continue
                }
            }
            else {
                $logdirectory = Get-SqlDefaultPaths -SqlInstance $destserver -FileType ldf
            }

            if ((Test-Bound "AzureCredential") -and (Test-Bound "CopyFile")) {
                Stop-Function -Message "Cannot use copyfile with Azure backups, set to false." -continue
                $CopyFile = $false
            }

            if (!$Database) {
                $database = $sourceserver.databases.Name | Where-Object Name -ne 'tempdb'
            }

            if ($ExcludeDatabase) {
                $database = $database | Where-Object { $_ -notin $ExcludeDatabase }
            }

            if ($Database -or $ExcludeDatabase) {
                $dblist = $database

                Write-Message -Level Verbose -Message "Getting recent backup history for $instance."

                foreach ($dbname in $dblist) {
                    if ($dbname -eq 'tempdb') {
                        Write-Message -Level Verbose -Message "Skipping tempdb."
                        continue
                    }

                    Write-Message -Level Verbose -Message "Processing $dbname."

                    $copysuccess = $true
                    $db = $sourceserver.databases[$dbname]

                    # The db check is needed when the number of databases exceeds 255, then it's no longer auto-populated
                    if (!$db) {
                        Stop-Function -Message "$dbname does not exist on $source." -Continue
                    }

                    if (Test-Bound "IgnoreLogBackup") {
                        Write-Message -Level Verbose -Message "Skipping Log backups as requested."
                        $lastbackup = @()
                        $lastbackup += $full = Get-DbaBackupHistory -SqlInstance $sourceserver -Database $dbname -IncludeCopyOnly:$IncludeCopyOnly -LastFull #-raw
                        $diff = Get-DbaBackupHistory -SqlInstance $sourceserver -Database $dbname -IncludeCopyOnly:$IncludeCopyOnly -LastDiff # -raw
                        if ($full.start -le $diff.start) {
                            $lastbackup += $diff
                        }
                    }
                    else {
                        $lastbackup = Get-DbaBackupHistory -SqlInstance $sourceserver -Database $dbname -IncludeCopyOnly:$IncludeCopyOnly -Last #-raw
                    }

                    if ($null -eq $lastbackup) {
                        Write-Message -Level Verbose -Message "No backups exist for this database."
                        $lastbackup = @{ Path = "No backups exist for this database" }
                        $fileexists = $false
                        $success = $restoreresult = $dbccresult = "Skipped"
                        continue
                    }

                    if ($CopyFile) {
                        try {
                            Write-Message -Level Verbose -Message "Gathering information for file copy."
                            $removearray = @()

                            foreach ($backup in $lastbackup) {
                                foreach ($file in $backup) {
                                    $filename = Split-Path -Path $file.FullName -Leaf
                                    Write-Message -Level Verbose -Message "Processing $filename."

                                    $sourcefile = Join-AdminUnc -servername $instance.ComputerName -filepath $file.Path

                                    if ($instance.IsLocalHost) {
                                        $remotedestdirectory = Join-AdminUnc -servername $instance.ComputerName -filepath $copyPath
                                    }
                                    else {
                                        $remotedestdirectory = $copyPath
                                    }

                                    $remotedestfile = "$remotedestdirectory\$filename"
                                    $localdestfile = "$copyPath\$filename"
                                    Write-Message -Level Verbose -Message "Destination directory is $destdirectory."
                                    Write-Message -Level Verbose -Message "Destination filename is $remotedestfile."

                                    try {
                                        Write-Message -Level Verbose -Message "Copying $sourcefile to $remotedestfile."
                                        Copy-Item -Path $sourcefile -Destination $remotedestfile -ErrorAction Stop
                                        $backup.Path = $localdestfile
                                        $backup.FullName = $localdestfile
                                        $removearray += $remotedestfile
                                    }
                                    catch {
                                        $backup.Path = $sourcefile
                                        $backup.FullName = $sourcefile
                                    }
                                }
                            }
                            $copysuccess = $true
                        }
                        catch {
                            Write-Message -Level Warning -Message "Failed to copy backups for $dbname on $instance to $destdirectory - $_."
                            $copysuccess = $false
                        }
                    }
                    if (!$copysuccess) {
                        Write-Message -Level Verbose -Message "Failed to copy backups."
                        $lastbackup = @{ Path = "Failed to copy backups" }
                        $fileexists = $false
                        $success = $restoreresult = $dbccresult = "Skipped"
                    }
                    elseif (!($lastbackup | Where-Object { $_.type -eq 'Full' })) {
                        Write-Message -Level Verbose -Message "No full backup returned from lastbackup."
                        $lastbackup = @{ Path = "Not found" }
                        $fileexists = $false
                        $success = $restoreresult = $dbccresult = "Skipped"
                    }
                    elseif ($source -ne $destination -and $lastbackup[0].Path.StartsWith('\\') -eq $false -and !$CopyFile) {
                        Write-Message -Level Verbose -Message "Path not UNC and source does not match destination. Use -CopyFile to move the backup file."
                        $fileexists = $dbccresult = "Skipped"
                        $success = $restoreresult = "Restore not located on shared location"
                    }
                    elseif (($lastbackup[0].Path | ForEach-Object { Test-DbaSqlPath -SqlInstance $destserver -Path $_ }) -eq $false) {
                        Write-Message -Level Verbose -Message "SQL Server cannot find backup."
                        $fileexists = $false
                        $success = $restoreresult = $dbccresult = "Skipped"
                    }
                    if ($restoreresult -ne "Skipped" -or $lastbackup[0].Path -like 'http*') {
                        Write-Message -Level Verbose -Message "Looking good!"

                        $fileexists = $true
                        $ogdbname = $dbname
                        $restorelist = Read-DbaBackupHeader -SqlInstance $destserver -Path $lastbackup[0].Path -AzureCredential $AzureCredential
                        $mb = $restorelist.BackupSizeMB

                        if ($MaxMB -gt 0 -and $MaxMB -lt $mb) {
                            $success = "The backup size for $dbname ($mb MB) exceeds the specified maximum size ($MaxMB MB)."
                            $dbccresult = "Skipped"
                        }
                        else {
                            $dbccElapsed = $restoreElapsed = $startRestore = $endRestore = $startDbcc = $endDbcc = $null

                            $dbname = "$prefix$dbname"
                            $destdb = $destserver.databases[$dbname]

                            if ($destdb) {
                                Stop-Function -Message "$dbname already exists on $destination - skipping." -Continue
                            }

                            if ($Pscmdlet.ShouldProcess($destination, "Restoring $ogdbname as $dbname.")) {
                                Write-Message -Level Verbose -Message "Performing restore."
                                $startRestore = Get-Date
                                if ($verifyonly) {
                                    $restoreresult = $lastbackup | Restore-DbaDatabase -SqlInstance $destserver -RestoredDatabaseNamePrefix $prefix -DestinationFilePrefix $Prefix -DestinationDataDirectory $datadirectory -DestinationLogDirectory $logdirectory -VerifyOnly:$VerifyOnly -IgnoreLogBackup:$IgnoreLogBackup -AzureCredential $AzureCredential -TrustDbBackupHistory
                                }
                                else {
                                    $restoreresult = $lastbackup | Restore-DbaDatabase -SqlInstance $destserver -RestoredDatabaseNamePrefix $prefix -DestinationFilePrefix $Prefix -DestinationDataDirectory $datadirectory -DestinationLogDirectory $logdirectory -IgnoreLogBackup:$IgnoreLogBackup -AzureCredential $AzureCredential -TrustDbBackupHistory
                                    Write-verbose " Restore-DbaDatabase -SqlInstance $destserver -RestoredDatabaseNamePrefix $prefix -DestinationFilePrefix $Prefix -DestinationDataDirectory $datadirectory -DestinationLogDirectory $logdirectory -IgnoreLogBackup:$IgnoreLogBackup -AzureCredential $AzureCredential -TrustDbBackupHistory"

                                }

                                $endRestore = Get-Date
                                $restorets = New-TimeSpan -Start $startRestore -End $endRestore
                                $ts = [timespan]::fromseconds($restorets.TotalSeconds)
                                $restoreElapsed = "{0:HH:mm:ss}" -f ([datetime]$ts.Ticks)

                                if ($restoreresult.RestoreComplete -eq $true) {
                                    $success = "Success"
                                }
                                else {
                                    $success = "Failure"
                                }
                            }

                            $destserver = Connect-SqlInstance -SqlInstance $destination -SqlCredential $DestinationCredential

                            if (!$NoCheck -and !$VerifyOnly) {
                                # shouldprocess is taken care of in Start-DbccCheck
                                if ($ogdbname -eq "master") {
                                    $dbccresult = "DBCC CHECKDB skipped for restored master ($dbname) database."
                                }
                                else {
                                    if ($success -eq "Success") {
                                        Write-Message -Level Verbose -Message "Starting DBCC."

                                        $startDbcc = Get-Date
                                        $dbccresult = Start-DbccCheck -Server $destserver -DbName $dbname 3>$null
                                        $endDbcc = Get-Date

                                        $dbccts = New-TimeSpan -Start $startDbcc -End $endDbcc
                                        $ts = [timespan]::fromseconds($dbccts.TotalSeconds)
                                        $dbccElapsed = "{0:HH:mm:ss}" -f ([datetime]$ts.Ticks)
                                    }
                                    else {
                                        $dbccresult = "Skipped"
                                    }
                                }
                            }

                            if ($VerifyOnly) {
                                $dbccresult = "Skipped"
                            }

                            if (!$NoDrop -and $null -ne $destserver.databases[$dbname]) {
                                if ($Pscmdlet.ShouldProcess($dbname, "Dropping Database $dbname on $destination")) {
                                    Write-Message -Level Verbose -Message "Dropping database."

                                    ## Drop the database
                                    try {
                                        $removeresult = Remove-DbaDatabase -SqlInstance $destserver -Database $dbname -Confirm:$false
                                        Write-Message -Level Verbose -Message "Dropped $dbname Database on $destination."
                                    }
                                    catch {
                                        $destserver.Databases.Refresh()
                                        if ($destserver.databases[$dbname]) {
                                            Write-Message -Level Warning -Message "Failed to Drop database $dbname on $destination."
                                        }
                                    }
                                }
                            }

                            #Cleanup BackupFiles if -CopyFile and backup was moved to destination
                            if ($CopyFile) {
                                Write-Message -Level Verbose -Message "Removing copied backup file from $destination."
                                try {
                                    $removearray | Remove-item -ErrorAction Stop
                                }
                                catch {
                                    Write-Message -Level Warning -Message $_ -ErrorRecord $_ -Target $instance
                                }
                            }

                            $destserver.Databases.Refresh()
                            if ($destserver.Databases[$dbname] -and !$NoDrop) {
                                Write-Message -Level Warning -Message "$dbname was not dropped."
                            }
                        }
                    }

                    if ($Pscmdlet.ShouldProcess("console", "Showing results")) {
                        [pscustomobject]@{
                            SourceServer   = $source
                            TestServer     = $destination
                            Database       = $db.name
                            FileExists     = $fileexists
                            Size           = [dbasize](($lastbackup.TotalSize | Measure-Object -Sum).Sum)
                            RestoreResult  = $success
                            DbccResult     = $dbccresult
                            RestoreStart   = [dbadatetime]$startRestore
                            RestoreEnd     = [dbadatetime]$endRestore
                            RestoreElapsed = $restoreElapsed
                            DbccStart      = [dbadatetime]$startDbcc
                            DbccEnd        = [dbadatetime]$endDbcc
                            DbccElapsed    = $dbccElapsed
                            BackupDate     = $lastbackup.Start
                            BackupFiles    = $lastbackup.FullName
                        }
                    }
                }
            }
        }
    }
}
function Test-DbaLinkedServerConnection {
    <#
        .SYNOPSIS
            Test all linked servers from the sql servers passed

        .DESCRIPTION
            Test each linked server on the instance

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to.

        .PARAMETER SqlCredential
            Credential object used to connect to the SQL Server as a different user

        .PARAMETER EnableException
                By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.

                This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
                Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: LinkedServer
            Author: Thomas LaRock ( https://thomaslarock.com )

            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2017 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaLinkedServerConnection

        .EXAMPLE
            Test-DbaLinkedServerConnection -SqlInstance DEV01

            Test all Linked Servers for the SQL Server instance DEV01

        .EXAMPLE
            Test-DbaLinkedServerConnection -SqlInstance sql2016 | Out-File C:\temp\results.txt

            Test all Linked Servers for the SQL Server instance sql2016 and output results to file

        .EXAMPLE
            Test-DbaLinkedServerConnection -SqlInstance sql2016, sql2014, sql2012

            Test all Linked Servers for the SQL Server instances sql2016, sql2014 and sql2012

        .EXAMPLE
            $servers = "sql2016","sql2014","sql2012"
            $servers | Test-DbaLinkedServerConnection -SqlCredential (Get-Credential sqladmin)

            Test all Linked Servers for the SQL Server instances sql2016, sql2014 and sql2012 using SQL login credentials

        .EXAMPLE
            $servers | Get-DbaLinkedServer | Test-DbaLinkedServerConnection

            Test all Linked Servers for the SQL Server instances sql2016, sql2014 and sql2012
    #>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            if ($instance.LinkedLive) {
                $linkedServerCollection = $instance.LinkedServer
            }
            else {
                try {
                    Write-Message -Level Verbose -Message "Connecting to $instance"
                    $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
                }
                catch {
                    Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                }
            }

            $linkedServerCollection = $server.LinkedServers

            foreach ($ls in $linkedServerCollection) {
                Write-Message -Level Verbose -Message "Testing linked server $($ls.name) on server $($ls.parent.name)"
                try {
                    $null = $ls.TestConnection()
                    $result = "Success"
                    $connectivity = $true
                }
                catch {
                    $result = $_.Exception.InnerException.InnerException.Message
                    $connectivity = $false
                }

                New-Object Sqlcollaborative.Dbatools.Validation.LinkedServerResult($ls.parent.ComputerName, $ls.parent.ServiceName, $ls.parent.DomainInstanceName, $ls.Name, $ls.DataSource, $connectivity, $result)
            }
        }
    }
}
function Test-DbaLoginPassword {
    <#
        .SYNOPSIS
            Test-DbaLoginPassword finds any logins on SQL instance that are SQL Logins and have a password that is either null or same as the login

        .DESCRIPTION
            The purpose of this function is to find SQL Server logins that have no password or the same password as login. You can add your own password to check for or add them to a csv file.
            By default it will test for empty password and the same password as username.

        .PARAMETER SqlInstance
            The SQL Server instance you're checking logins on. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SqlCredential
            Allows you to login to servers using SQL Logins instead of Windows Authentication (AKA Integrated or Trusted). To use:

            $scred = Get-Credential, then pass $scred object to the -SqlCredential parameter.

            Windows Authentication will be used if SqlCredential is not specified. SQL Server does not accept Windows credentials being passed as credentials.

            To connect as a different Windows user, run PowerShell as that user.

        .PARAMETER Dictionary
            Specifies a list of passwords to include in the test for weak passwords.

        .PARAMETER Login
            The login(s) to process.
    
        .PARAMETER InputObject
            Allows piping from Get-DbaLogin.
    
        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Peter Samuelsson
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaLoginPassword

        .EXAMPLE
            Test-DbaLoginPassword -SqlInstance Dev01

            Test all SQL logins that the password is null or same as username on SQL server instance Dev01

        .EXAMPLE
            Test-DbaLoginPassword -SqlInstance Dev01 -Login sqladmin

            Test the 'sqladmin' SQL login that the password is null or same as username on SQL server instance Dev01

        .EXAMPLE
            Test-DbaLoginPassword -SqlInstance Dev01 -Dictionary Test1,test2

            Test all SQL logins that the password is null, same as username or Test1,Test2 on SQL server instance Dev0

        .EXAMPLE
            Get-DbaLogin -SqlInstance "sql2017","sql2016" | Test-DbaLoginPassword

            Test all logins on sql2017 and sql2016

        .EXAMPLE
            $servers | Get-DbaLogin | Out-GridView -Passthru | Test-DbaLoginPassword

            Test selected logins on all servers in the $servers variable
    #>
    [CmdletBinding()]
    Param (
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [String[]]$Login,
        [String[]]$Dictionary,
        [Parameter(ValueFromPipeline)]
        [Microsoft.SqlServer.Management.Smo.Login[]]$InputObject,
        [switch]$EnableException
    )

    begin {
        $CheckPasses = "''", "'@@Name'"
        if ($Dictionary) {
            $Dictionary | ForEach-Object { $CheckPasses += "'" + $psitem + "'" }
        }

        foreach ($CheckPass in $CheckPasses) {
            if ($CheckPasses.IndexOf($CheckPass) -eq 0) {
                $checks = "SELECT " + $CheckPass
            }
            else {
                $checks += "
        UNION SELECT " + $CheckPass
            }
        }

        $sql = "DECLARE @WeakPwdList TABLE(WeakPwd NVARCHAR(255))
            --Define weak password list
            --Use @@Name if users password contain their name
            INSERT INTO @WeakPwdList(WeakPwd)
            $checks

            SELECT SERVERPROPERTY('MachineName') AS [ComputerName],
                ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
                SERVERPROPERTY('ServerName') AS [SqlInstance],
                SysLogins.name as SqlLogin,
                WeakPassword = 'True',
                REPLACE(WeakPassword.WeakPwd,'@@Name',SysLogins.name) As [Password],
                SysLogins.is_disabled as Disabled,
                SysLogins.create_date as CreatedDate,
                SysLogins.modify_date as ModifiedDate,
                SysLogins.default_database_name as DefaultDatabase
            FROM sys.sql_logins SysLogins
            INNER JOIN @WeakPwdList WeakPassword ON (PWDCOMPARE(WeakPassword.WeakPwd, password_hash) = 1
                OR PWDCOMPARE(REPLACE(WeakPassword.WeakPwd,'@@Name',SysLogins.name),password_hash) = 1)"
    }
    process {
        foreach ($instance in $SqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential -MinimumVersion 10
                Write-Message -Message "Connected to: $instance." -Level Verbose
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            $InputObject += Get-DbaLogin -SqlInstance $server -Login $Login
        }

        $logins += $InputObject
    }
    end {
        $servers = $logins | Select-Object -Unique -ExpandProperty Parent
        $names = $logins | Select-Object -Unique -ExpandProperty Name

        foreach ($serverinstance in $servers) {
            Write-Message -Level Debug -Message "Executing $sql"
            Write-Message -Level Verbose -Message "Testing: same username as Password"
            Write-Message -Level Verbose -Message "Testing: the following Passwords $CheckPasses"
            try {
                $serverinstance.Query("$sql") | Where-Object SqlLogin -in $names
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_ -Target $serverinstance -Continue
            }
        }
    }
}
function Test-DbaLogShippingStatus {
    <#
        .SYNOPSIS
            Test-DbaLogShippingStatus returns the status of your log shipping databases

        .DESCRIPTION
            Most of the time your log shipping "just works".
            Checking your log shipping status can be done really easy with this function.

            Make sure you're connecting to the monitoring instance of your log shipping infrastructure.

            The function will return the status for a database. This can be one or more messages in a comma separated list.
            If everything is OK with the database than you should only see the message "All OK".

        .PARAMETER SqlInstance
            SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Allows you to filter the results to only return the databases you're interested in. This can be one or more values separated by commas.
            This is not a wildcard and should be the exact database name. See examples for more info.

        .PARAMETER ExcludeDatabase
            Allows you to filter the results to only return the databases you're not interested in. This can be one or more values separated by commas.
            This is not a wildcard and should be the exact database name.

        .PARAMETER Primary
            Allows to filter the results to only return values that apply to the primary instance.

        .PARAMETER Secondary
            Allows to filter the results to only return values that apply to the secondary instance.

        .PARAMETER Simple
            By default all the information will be returned.
            If this parameter is used you get an overview with the SQL Instance, Database, Instance Type and the status

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: LogShipping
            Author: Sander Stad (@sqlstad, sqlstad.nl)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaLogShippingStatus

        .EXAMPLE
            Test-DbaLogShippingStatus -SqlInstance sql1

            Retrieves the log ship information from sql1 and displays all the information present including the status.

        .EXAMPLE
            Test-DbaLogShippingStatus -SqlInstance sql1 -Database AdventureWorks2014

            Retrieves the log ship information for just the database AdventureWorks.

        .EXAMPLE
            Test-DbaLogShippingStatus -SqlInstance sql1 -Primary

            Retrieves the log ship information and only returns the information for the databases on the primary instance.

        .EXAMPLE
            Test-DbaLogShippingStatus -SqlInstance sql1 -Secondary

            Retrieves the log ship information and only returns the information for the databases on the secondary instance.

        .EXAMPLE
            Test-DbaLogShippingStatus -SqlInstance sql1 -Simple

            Retrieves the log ship information and only returns the columns SQL Instance, Database, Instance Type and Status
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$Database,
        [string[]]$ExcludeDatabase,
        [switch]$Simple,
        [switch]$Primary,
        [switch]$Secondary,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {

        # Create array list to hold the results
        $collection = New-Object System.Collections.ArrayList

        # Setup the query
        [string[]]$query = "
IF ( OBJECT_ID('tempdb..#logshippingstatus') ) IS NOT NULL
BEGIN
DROP TABLE #logshippingstatus;
END;

CREATE TABLE #logshippingstatus
(
    Status BIT ,
    IsPrimary BIT ,
    Server VARCHAR(100) ,
    DatabaseName VARCHAR(100) ,
    TimeSinceLastBackup INT ,
    LastBackupFile VARCHAR(255) ,
    BackupThresshold INT ,
    IsBackupAlertEnabled BIT ,
    TimeSinceLastCopy INT ,
    LastCopiedFile VARCHAR(255) ,
    TimeSinceLastRestore INT ,
    LastRestoredFile VARCHAR(255) ,
    LastRestoredLatency INT ,
    RestoreThresshold INT ,
    IsRestoreAlertEnabled BIT
);

INSERT INTO #logshippingstatus
(   Status ,
    IsPrimary ,
    Server ,
    DatabaseName ,
    TimeSinceLastBackup ,
    LastBackupFile ,
    BackupThresshold ,
    IsBackupAlertEnabled ,
    TimeSinceLastCopy ,
    LastCopiedFile ,
    TimeSinceLastRestore ,
    LastRestoredFile ,
    LastRestoredLatency ,
    RestoreThresshold ,
    IsRestoreAlertEnabled
)
EXEC master.sys.sp_help_log_shipping_monitor"

        $select = "SELECT * FROM #logshippingstatus"

        if ($Database -or $ExcludeDatabase) {

            if ($database) {
                $where += "DatabaseName IN ('$($Database -join ''',''')')"
            }
            elseif ($ExcludeDatabase) {
                $where += "DatabaseName NOT IN ('$($ExcludeDatabase -join ''',''')')"
            }

            $select = "$select WHERE $where"
        }

        $query += $select
        $query += "DROP TABLE #logshippingstatus"
        $sql = $query -join ";`n"
        Write-Message -level Debug -Message $sql
    }

    process {
        foreach ($instance in $sqlinstance) {
            # Try connecting to the instance
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($server.EngineEdition -match "Express") {
                Write-Message -Level Warning -Message "$instance is Express Edition which does not support Log Shipping"
                continue
            }

            # Check the variables
            if ($Primary -and $Secondary) {
                Stop-Function -Message "Invalid parameter combination. Please enter either -Primary or -Secondary" -Target $instance -Continue
            }

            # Get the log shipped databases
            $results = $server.Query($sql)

            # Check if any rows were returned
            if ($results.Count -lt 1) {
                Stop-Function -Message "No information available about any log shipped databases for $instance. Please check the instance name." -Target $instance -Continue
            }

            # Filter the results
            if ($Primary) {
                $results = $results | Where-Object { $_.IsPrimary -eq $true }
            }

            if ($Secondary) {
                $results = $results | Where-Object { $_.IsPrimary -eq $false }
            }

            # Loop through each of the results
            foreach ($result in $results) {

                # Setup a variable to hold the errors
                $statusDetails = @()

                # Check if there are any results that need to be returned
                if ($result.Status -notin 0, 1) {
                    $statusDetails += "N/A"
                }
                else {
                    # Check the status of the row is true which indicates that something is wrong
                    if ($result.Status) {
                        # Check if the row is part of the primary or secondary instance
                        if ($result.IsPrimary) {
                            # Check the backup
                            if (-not $result.TimeSinceLastBackup) {
                                $statusDetails += "The backup has never been executed."
                            }
                            elseif ($result.TimeSinceLastBackup -ge $result.BackupThresshold) {
                                $statusDetails += "The backup has not been executed in the last $($result.BackupThresshold) minutes"
                            }
                        }
                        elseif (-not $result.IsPrimary) {
                            # Check the restore
                            if ($null -eq $result.TimeSinceLastRestore) {
                                $statusDetails += "The restore has never been executed."
                            }
                            elseif ($result.TimeSinceLastRestore -ge $result.RestoreThresshold) {
                                $statusDetails += "The restore has not been executed in the last $($result.RestoreThresshold) minutes"
                            }
                        }
                    }
                    else {
                        $statusDetails += "All OK"
                    }


                    # Check the time for the backup, copy and restore
                    if ($result.TimeSinceLastBackup -eq [DBNull]::Value) {
                        $lastBackup = "N/A"
                    }
                    else {
                        $lastBackup = (Get-Date).AddMinutes( - $result.TimeSinceLastBackup)
                    }

                    if ($result.TimeSinceLastCopy -eq [DBNull]::Value) {
                        $lastCopy = "N/A"
                    }
                    else {
                        $lastCopy = (Get-Date).AddMinutes( - $result.TimeSinceLastCopy)
                    }

                    if ($result.TimeSinceLastRestore -eq [DBNull]::Value) {
                        $lastRestore = "N/A"
                    }
                    else {
                        $lastRestore = (Get-Date).AddMinutes( - $result.TimeSinceLastRestore)
                    }
                }

                # Set up the custom object
                $null = $collection.Add([PSCustomObject]@{
                        ComputerName          = $server.ComputerName
                        InstanceName          = $server.ServiceName
                        SqlInstance           = $server.DomainInstanceName
                        Database              = $result.DatabaseName
                        InstanceType          = switch ($result.IsPrimary) { $true { "Primary Instance" } $false { "Secondary Instance" } }
                        TimeSinceLastBackup   = $lastBackup
                        LastBackupFile        = $result.LastBackupFile
                        BackupThresshold      = $result.BackupThresshold
                        IsBackupAlertEnabled  = $result.IsBackupAlertEnabled
                        TimeSinceLastCopy     = $lastCopy
                        LastCopiedFile        = $result.LastCopiedFile
                        TimeSinceLastRestore  = $lastRestore
                        LastRestoredFile      = $result.LastRestoredFile
                        LastRestoredLatency   = $result.LastRestoredLatency
                        RestoreThresshold     = $result.RestoreThresshold
                        IsRestoreAlertEnabled = $result.IsRestoreAlertEnabled
                        Status                = $statusDetails -join ","
                    })

            }

            if ($Simple) {
                return $collection | Select-Object SqlInstance, Database, InstanceType, Status
            }
            else {
                return $collection
            }
        }
    }
}
function Test-DbaMaxDop {
    <#
        .SYNOPSIS
            Displays information relating to SQL Server Max Degree of Parallelism setting. Works on SQL Server 2005-2016.

        .DESCRIPTION
            Inspired by Sakthivel Chidambaram's post about SQL Server MAXDOP Calculator (https://blogs.msdn.microsoft.com/sqlsakthi/p/maxdop-calculator-SqlInstance/),
            this script displays a SQL Server's: max dop configured, and the calculated recommendation.

            For SQL Server 2016 shows:
                - Instance max dop configured and the calculated recommendation
                - max dop configured per database (new feature)

            More info:
                https://support.microsoft.com/en-us/kb/2806535
                https://blogs.msdn.microsoft.com/sqlsakthi/2012/05/23/wow-we-have-maxdop-calculator-for-sql-server-it-makes-my-job-easier/

            These are just general recommendations for SQL Server and are a good starting point for setting the "max degree of parallelism" option.

        .PARAMETER SqlInstance
            The SQL Server instance(s) to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Detailed
            Output all properties, will be deprecated in 1.0.0 release.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: MaxDop, SpConfigure
            Author  : Claudio Silva (@claudioessilva)
            Requires: sysadmin access on SQL Servers

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaMaxDop

        .EXAMPLE
            Test-DbaMaxDop -SqlInstance sql2008, sqlserver2012

            Get Max DOP setting for servers sql2008 and sqlserver2012 and also the recommended one.

        .EXAMPLE
            Test-DbaMaxDop -SqlInstance sql2014 | Select-Object *

            Shows Max DOP setting for server sql2014 with the recommended value. Piping the output to Select-Object * will also show the 'NUMANodes' and 'NumberOfCores' of each instance

        .EXAMPLE
            Test-DbaMaxDop -SqlInstance sqlserver2016 | Select-Object *

            Get Max DOP setting for servers sql2016 with the recommended value. Piping the output to Select-Object * will also show the 'NUMANodes' and 'NumberOfCores' of each instance. Because it is an 2016 instance will be shown 'InstanceVersion', 'Database' and 'DatabaseMaxDop' columns.
    #>
    [CmdletBinding()]
    [OutputType([System.Collections.ArrayList])]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [switch]$Detailed,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter Detailed

        $notesDopLT = "Before changing MaxDop, consider that the lower value may have been intentionally set."
        $notesDopGT = "Before changing MaxDop, consider that the higher value may have been intentionally set."
        $notesDopZero = "This is the default setting. Consider using the recommended value instead."
        $notesDopOne = "Some applications like SharePoint, Dynamics NAV, SAP, BizTalk has the need to use MAXDOP = 1. Please confirm that your instance is not supporting one of these applications prior to changing the MaxDop."
        $notesAsRecommended = "Configuration is as recommended."
    }

    process {
        $hasScopedConfig = $false

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            #Get current configured value
            $maxDop = $server.Configuration.MaxDegreeOfParallelism.ConfigValue

            try {
                #represents the Number of NUMA nodes
                $sql = "SELECT COUNT(DISTINCT memory_node_id) AS NUMA_Nodes FROM sys.dm_os_memory_clerks WHERE memory_node_id!=64"
                $numaNodes = $server.ConnectionContext.ExecuteScalar($sql)
            }
            catch {
                Stop-Function -Message "Failed to get Numa node count." -ErrorRecord $_ -Target $server -Continue
            }

            try {
                #represents the Number of Processor Cores
                $sql = "SELECT COUNT(scheduler_id) FROM sys.dm_os_schedulers WHERE status = 'VISIBLE ONLINE'"
                $numberOfCores = $server.ConnectionContext.ExecuteScalar($sql)
            }
            catch {
                Stop-Function -Message "Failed to get number of cores." -ErrorRecord $_ -Target $server -Continue
            }

            #Calculate Recommended Max Dop to instance
            #Server with single NUMA node
            if ($numaNodes -eq 1) {
                if ($numberOfCores -lt 8) {
                    #Less than 8 logical processors - Keep MAXDOP at or below # of logical processors
                    $recommendedMaxDop = $numberOfCores
                }
                else {
                    #Equal or greater than 8 logical processors - Keep MAXDOP at 8
                    $recommendedMaxDop = 8
                }
            }
            else {
                #Server with multiple NUMA nodes
                if (($numberOfCores / $numaNodes) -lt 8) {
                    # Less than 8 logical processors per NUMA node - Keep MAXDOP at or below # of logical processors per NUMA node
                    $recommendedMaxDop = [int]($numberOfCores / $numaNodes)
                }
                else {
                    # Greater than 8 logical processors per NUMA node - Keep MAXDOP at 8
                    $recommendedMaxDop = 8
                }
            }

            #Setting notes for instance max dop value
            $notes = $null
            if ($maxDop -eq 1) {
                $notes = $notesDopOne
            }
            else {
                if ($maxDop -ne 0 -and $maxDop -lt $recommendedMaxDop) {
                    $notes = $notesDopLT
                }
                else {
                    if ($maxDop -ne 0 -and $maxDop -gt $recommendedMaxDop) {
                        $notes = $notesDopGT
                    }
                    else {
                        if ($maxDop -eq 0) {
                            $notes = $notesDopZero
                        }
                        else {
                            $notes = $notesAsRecommended
                        }
                    }
                }
            }

            [pscustomobject]@{
                ComputerName          = $server.ComputerName
                InstanceName          = $server.ServiceName
                SqlInstance           = $server.DomainInstanceName
                InstanceVersion       = $server.Version
                Database              = "N/A"
                DatabaseMaxDop        = "N/A"
                CurrentInstanceMaxDop = $maxDop
                RecommendedMaxDop     = $recommendedMaxDop
                NUMANodes             = $numaNodes
                NumberOfCores         = $numberOfCores
                Notes                 = $notes
            } | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, Database, DatabaseMaxDop, CurrentInstanceMaxDop, RecommendedMaxDop, Notes

            # On SQL Server 2016 and higher, MaxDop can be set on a per-database level
            if ($server.VersionMajor -ge 13) {
                $hasScopedConfig = $true
                Write-Message -Level Verbose -Message "SQL Server 2016 or higher detected, checking each database's MaxDop."

                $databases = $server.Databases | where-object {$_.IsSystemObject -eq $false}

                foreach ($database in $databases) {
                    if ($database.IsAccessible -eq $false) {
                        Write-Message -Level Verbose -Message "Database $database is not accessible."
                        continue
                    }
                    Write-Message -Level Verbose -Message "Checking database '$($database.Name)'."

                    $dbmaxdop = $database.MaxDop

                    [pscustomobject]@{
                        ComputerName          = $server.ComputerName
                        InstanceName          = $server.ServiceName
                        SqlInstance           = $server.DomainInstanceName
                        InstanceVersion       = $server.Version
                        Database              = $database.Name
                        DatabaseMaxDop        = $dbmaxdop
                        CurrentInstanceMaxDop = $maxDop
                        RecommendedMaxDop     = $recommendedMaxDop
                        NUMANodes             = $numaNodes
                        NumberOfCores         = $numberOfCores
                        Notes                 = if ($dbmaxdop -eq 0) { "Will use CurrentInstanceMaxDop value" } else { "$notes" }
                    }  | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, Database, DatabaseMaxDop, CurrentInstanceMaxDop, RecommendedMaxDop, Notes
                }
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Test-DbaMaxMemory {
    <#
        .SYNOPSIS
            Calculates the recommended value for SQL Server 'Max Server Memory' configuration setting. Works on SQL Server 2000-2014.

        .DESCRIPTION
            Inspired by Jonathan Kehayias's post about SQL Server Max memory (http://bit.ly/sqlmemcalc), this script displays a SQL Server's: total memory, currently configured SQL max memory, and the calculated recommendation.

            Jonathan notes that the formula used provides a *general recommendation* that doesn't account for everything that may be going on in your specific environment.

        .PARAMETER SqlInstance
            Allows you to specify a comma separated list of servers to query.

        .PARAMETER SqlCredential
            Windows or Sql Login Credential with permission to log into the SQL instance

        .PARAMETER Credential
            Windows Credential with permission to log on to the server running the SQL instance

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: MaxMemory, Memory
            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaMaxMemory

        .EXAMPLE
            Test-DbaMaxMemory -SqlInstance sqlcluster,sqlserver2012

            Calculate the 'Max Server Memory' settings for all servers within the SQL Server Central Management Server "sqlcluster"

        .EXAMPLE
            Test-DbaMaxMemory -SqlInstance sqlcluster | Where-Object { $_.SqlMaxMB -gt $_.TotalMB } | Set-DbaMaxMemory

            Find all servers in CMS that have Max SQL memory set to higher than the total memory of the server (think 2147483647) and set it to recommended value.
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [PSCredential]$Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level VeryVerbose -Message "Processing $instance" -Target $instance

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            Write-Message -Level Verbose -Target $instance -Message "Retrieving maximum memory statistics from $instance"
            $serverMemory = Get-DbaMaxMemory -SqlInstance $server
            try {
                Write-Message -Level Verbose -Target $instance -Message "Retrieving number of instances from $($instance.ComputerName)"
                if ($Credential) {
                    $serverService = Get-DbaSqlService -ComputerName $instance -Credential $Credential -EnableException
                }
                else {
                    $serverService = Get-DbaSqlService -ComputerName $instance -EnableException
                }
                $instanceCount = ($serverService | Where-Object State -Like Running | Where-Object InstanceName | Group-Object InstanceName | Measure-Object Count).Count
            }
            catch {
                Write-Message -Level Warning -Message "Couldn't get accurate SQL Server instance count on $instance. Defaulting to 1." -Target $instance -ErrorRecord $_
                $instanceCount = 1
            }

            if ($null -eq $serverMemory) {
                continue
            }
            $reserve = 1

            $maxMemory = $serverMemory.SqlMaxMB
            $totalMemory = $serverMemory.TotalMB

            if ($totalMemory -ge 4096) {
                $currentCount = $totalMemory
                while ($currentCount / 4096 -gt 0) {
                    if ($currentCount -gt 16384) {
                        $reserve += 1
                        $currentCount += -8192
                    }
                    else {
                        $reserve += 1
                        $currentCount += -4096
                    }
                }
                $recommendedMax = [int]($totalMemory - ($reserve * 1024))
            }
            else {
                $recommendedMax = $totalMemory * .5
            }

            $recommendedMax = $recommendedMax / $instanceCount

            [pscustomobject]@{
                ComputerName  = $server.ComputerName
                InstanceName  = $server.ServiceName
                SqlInstance   = $server.DomainInstanceName
                InstanceCount = $instanceCount
                TotalMB       = [int]$totalMemory
                SqlMaxMB      = [int]$maxMemory
                RecommendedMB = [int]$recommendedMax
            } | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, InstanceCount, TotalMB, SqlMaxMB, RecommendedMB
        }
    }
}
function Test-DbaMigrationConstraint {
    <#
        .SYNOPSIS
            Show if you can migrate the database(s) between the servers.

        .DESCRIPTION
            When you want to migrate from a higher edition to a lower one there are some features that can't be used.
            This function will validate if you have any of this features in use and will report to you.
            The validation will be made ONLY on on SQL Server 2008 or higher using the 'sys.dm_db_persisted_sku_features' dmv.

            This function only validate SQL Server 2008 versions or higher.
            The editions supported by this function are:
                - Enterprise
                - Developer
                - Evaluation
                - Standard
                - Express

            Take into account the new features introduced on SQL Server 2016 SP1 for all versions. More information at https://blogs.msdn.microsoft.com/sqlreleaseservices/sql-server-2016-service-pack-1-sp1-released/

            The -Database parameter is auto-populated for command-line completion.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude. Options for this list are auto-populated from the server.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration

            Author: Claudio Silva (@ClaudioESSilva)
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaMigrationConstraint

        .EXAMPLE
            Test-DbaMigrationConstraint -Source sqlserver2014a -Destination sqlcluster

            All databases on sqlserver2014a will be verified for features in use that can't be supported on sqlcluster.

        .EXAMPLE
            Test-DbaMigrationConstraint -Source sqlserver2014a -Destination sqlcluster -SqlCredential $cred

            All databases will be verified for features in use that can't be supported on the destination server. SQL credentials are used to authenticate against sqlserver2014 and Windows Authentication is used for sqlcluster.

        .EXAMPLE
            Test-DbaMigrationConstraint -Source sqlserver2014a -Destination sqlcluster -Database db1

            Only db1 database will be verified for features in use that can't be supported on the destination server.
    #>
    [CmdletBinding(DefaultParameterSetName = "DbMigration")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $True)]
        [DbaInstance]$Source,
        [PSCredential]$SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstance]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        <#
            1804890536 = Enterprise
            1872460670 = Enterprise Edition: Core-based Licensing
            610778273 = Enterprise Evaluation
            284895786 = Business Intelligence
            -2117995310 = Developer
            -1592396055 = Express
            -133711905= Express with Advanced Services
            -1534726760 = Standard
            1293598313 = Web
            1674378470 = SQL Database
        #>

        $editions = @{
            "Enterprise" = 10;
            "Developer"  = 10;
            "Evaluation" = 10;
            "Standard"   = 5;
            "Express"    = 1
        }
        $notesCanMigrate = "Database can be migrated."
        $notesCannotMigrate = "Database cannot be migrated."
    }
    process {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source."
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source -Continue
        }

        try {
            Write-Message -Level Verbose -Message "Connecting to $Destination."
            $destServer = Connect-SqlInstance -SqlInstance $Destination -SqlCredential $DestinationSqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Destination -Continue
        }

        if (-Not $Database) {
            $Database = $sourceServer.Databases | Where-Object IsSystemObject -eq 0 | Select-Object Name, Status
        }

        if ($ExcludeDatabase) {
            $Database = $sourceServer.Databases | Where-Object Name -NotIn $ExcludeDatabase
        }

        if ($Database.Count -gt 0) {
            if ($Database -in @("master", "msdb", "tempdb")) {
                Stop-Function -Message "Migrating system databases is not currently supported."
                return
            }

            if ($sourceServer.VersionMajor -lt 9 -and $destServer.VersionMajor -gt 10) {
                Stop-Function -Message "Sql Server 2000 databases cannot be migrated to SQL Server version 2012 and above. Quitting."
                return
            }

            if ($sourceServer.Collation -ne $destServer.Collation) {
                Write-Message -Level Warning -Message "Collation on $Source, $($sourceServer.collation) differs from the $Destination, $($destServer.collation)."
            }

            if ($sourceServer.VersionMajor -gt $destServer.VersionMajor) {
                #indicate they must use 'Generate Scripts' and 'Export Data' options?
                Stop-Function -Message "You can't migrate databases from a higher version to a lower one. Quitting."
                return
            }

            if ($sourceServer.VersionMajor -lt 10) {
                Stop-Function -Message "This function does not support versions lower than SQL Server 2008 (v10)"
                return
            }

            #if editions differs, from higher to lower one, verify the sys.dm_db_persisted_sku_features - only available from SQL 2008 +
            if (($sourceServer.VersionMajor -ge 10 -and $destServer.VersionMajor -ge 10)) {
                foreach ($db in $Database) {
                    if ([string]::IsNullOrEmpty($db.Status)) {
                        $dbstatus = ($sourceServer.Databases | Where-Object Name -eq $db).Status.ToString()
                        $dbName = $db
                    }
                    else {
                        $dbstatus = $db.Status.ToString()
                        $dbName = $db.Name
                    }

                    Write-Message -Level Verbose -Message "Checking database '$dbName'."

                    if ($dbstatus.Contains("Offline") -eq $false -or $db.IsAccessible -eq $true) {

                        [long]$destVersionNumber = $($destServer.VersionString).Replace(".", "")
                        [string]$sourceVersion = "$($sourceServer.Edition) $($sourceServer.ProductLevel) ($($sourceServer.Version))"
                        [string]$destVersion = "$($destServer.Edition) $($destServer.ProductLevel) ($($destServer.Version))"
                        [string]$dbFeatures = ""

                        #Check if database has any FILESTREAM filegroup
                        Write-Message -Level Verbose -Message "Checking if FileStream is in use for database '$dbName'."
                        if ($sourceServer.Databases[$dbName].FileGroups | Where-Object FileGroupType -eq 'FileStreamDataFileGroup') {
                            Write-Message -Level Verbose -Message "Found FileStream filegroup and files."
                            $fileStreamSource = Get-DbaSpConfigure -SqlInstance $sourceServer -ConfigName FilestreamAccessLevel
                            $fileStreamDestination = Get-DbaSpConfigure -SqlInstance $destServer -ConfigName FilestreamAccessLevel

                            if ($fileStreamSource.RunningValue -ne $fileStreamDestination.RunningValue) {
                                [pscustomobject]@{
                                    SourceInstance      = $sourceServer.Name
                                    DestinationInstance = $destServer.Name
                                    SourceVersion       = $sourceVersion
                                    DestinationVersion  = $destVersion
                                    Database            = $dbName
                                    FeaturesInUse       = $dbFeatures
                                    IsMigratable        = $false
                                    Notes               = "$notesCannotMigrate. Destination server dones not have the 'FilestreamAccessLevel' configuration (RunningValue: $($fileStreamDestination.RunningValue)) equal to source server (RunningValue: $($fileStreamSource.RunningValue))."
                                }
                                Continue
                            }
                        }

                        try {
                            $sql = "SELECT feature_name FROM sys.dm_db_persisted_sku_features"

                            $skuFeatures = $sourceServer.Query($sql, $dbName)

                            Write-Message -Level Verbose -Message "Checking features in use..."

                            if (@($skuFeatures).Count -gt 0) {
                                foreach ($row in $skuFeatures) {
                                    $dbFeatures += ",$($row["feature_name"])"
                                }

                                $dbFeatures = $dbFeatures.TrimStart(",")
                            }
                        }
                        catch {
                            Stop-Function -Message "Issue collecting sku features." -ErrorRecord $_ -Target $sourceServer -Continue
                        }

                        #If SQL Server 2016 SP1 (13.0.4001.0) or higher
                        if ($destVersionNumber -ge 13040010) {
                            <#
                                Need to verify if Edition = EXPRESS and database uses 'Change Data Capture' (CDC)
                                This means that database cannot be migrated because Express edition doesn't have SQL Server Agent
                            #>
                            if ($editions.Item($destServer.Edition.ToString().Split(" ")[0]) -eq 1 -and $dbFeatures.Contains("ChangeCapture")) {
                                [pscustomobject]@{
                                    SourceInstance      = $sourceServer.Name
                                    DestinationInstance = $destServer.Name
                                    SourceVersion       = $sourceVersion
                                    DestinationVersion  = $destVersion
                                    Database            = $dbName
                                    FeaturesInUse       = $dbFeatures
                                    IsMigratable        = $false
                                    Notes               = "$notesCannotMigrate. Destination server edition is EXPRESS which does not support 'ChangeCapture' feature that is in use."
                                }
                            }
                            else {
                                [pscustomobject]@{
                                    SourceInstance      = $sourceServer.Name
                                    DestinationInstance = $destServer.Name
                                    SourceVersion       = $sourceVersion
                                    DestinationVersion  = $destVersion
                                    Database            = $dbName
                                    FeaturesInUse       = $dbFeatures
                                    IsMigratable        = $true
                                    Notes               = $notesCanMigrate
                                }
                            }
                        }
                        #Version is lower than SQL Server 2016 SP1
                        else {
                            Write-Message -Level Verbose -Message "Source Server Edition: $($sourceServer.Edition) (Weight: $($editions.Item($sourceServer.Edition.ToString().Split(" ")[0])))"
                            Write-Message -Level Verbose -Message "Destination Server Edition: $($destServer.Edition) (Weight: $($editions.Item($destServer.Edition.ToString().Split(" ")[0])))"

                            #Check for editions. If destination edition is lower than source edition and exists features in use
                            if (($editions.Item($destServer.Edition.ToString().Split(" ")[0]) -lt $editions.Item($sourceServer.Edition.ToString().Split(" ")[0])) -and (!([string]::IsNullOrEmpty($dbFeatures)))) {
                                [pscustomobject]@{
                                    SourceInstance      = $sourceServer.Name
                                    DestinationInstance = $destServer.Name
                                    SourceVersion       = $sourceVersion
                                    DestinationVersion  = $destVersion
                                    Database            = $dbName
                                    FeaturesInUse       = $dbFeatures
                                    IsMigratable        = $false
                                    Notes               = "$notesCannotMigrate There are features in use not available on destination instance."
                                }
                            }
                            #
                            else {
                                [pscustomobject]@{
                                    SourceInstance      = $sourceServer.Name
                                    DestinationInstance = $destServer.Name
                                    SourceVersion       = $sourceVersion
                                    DestinationVersion  = $destVersion
                                    Database            = $dbName
                                    FeaturesInUse       = $dbFeatures
                                    IsMigratable        = $true
                                    Notes               = $notesCanMigrate
                                }
                            }
                        }
                    }
                    else {
                        Write-Message -Level Warning -Message "Database '$dbName' is offline or not accessible. Bring database online and re-run the command."
                    }
                }
            }
            else {
                #SQL Server 2005 or under
                Write-Message -Level Warning -Message "This validation will not be made on versions lower than SQL Server 2008 (v10)."
                Write-Message -Level Verbose -Message "Source server version: $($sourceServer.VersionMajor)."
                Write-Message -Level Verbose -Message "Destination server version: $($destServer.VersionMajor)."
            }
        }
        else {
            Write-Message -Level Output -Message "There are no databases to validate."
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Test-SqlMigrationConstraint
    }
}
function Test-DbaNetworkLatency {
    <#
        .SYNOPSIS
            Tests how long a query takes to return from SQL Server

        .DESCRIPTION
            This function is intended to help measure SQL Server network latency by establishing a connection and executing a simple query. This is a better than a simple ping because it actually creates the connection to the SQL Server and measures the time required for only the entire routine, but the duration of the query as well how long it takes for the results to be returned.

            By default, this command will execute "SELECT TOP 100 * FROM INFORMATION_SCHEMA.TABLES" three times.

            It will then output how long the entire connection and command took, as well as how long *only* the execution of the command took.

            This allows you to see if the issue is with the connection or the SQL Server itself.

        .PARAMETER SqlInstance
            The SQL Server you want to run the test on.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Query
            Specifies the query to be executed. By default, "SELECT TOP 100 * FROM INFORMATION_SCHEMA.TABLES" will be executed on master. To execute in other databases, use fully qualified object names.

        .PARAMETER Count
            Specifies how many times the query should be executed. By default, the query is executed three times.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Performance, Network
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaNetworkLatency

        .EXAMPLE
            Test-DbaNetworkLatency -SqlInstance sqlserver2014a, sqlcluster

            Tests the roundtrip return of "SELECT TOP 100 * FROM INFORMATION_SCHEMA.TABLES" on sqlserver2014a and sqlcluster using Windows credentials.

        .EXAMPLE
            Test-DbaNetworkLatency -SqlInstance sqlserver2014a -SqlCredential $cred

            Tests the execution results return of "SELECT TOP 100 * FROM INFORMATION_SCHEMA.TABLES" on sqlserver2014a using SQL credentials.

        .EXAMPLE
            Test-DbaNetworkLatency -SqlInstance sqlserver2014a, sqlcluster, sqlserver -Query "select top 10 * from otherdb.dbo.table" -Count 10

            Tests the execution results return of "select top 10 * from otherdb.dbo.table" 10 times on sqlserver2014a, sqlcluster, and sqlserver using Windows credentials.

    #>
    [CmdletBinding()]
    [OutputType([System.Object[]])]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string]$Query = "select top 100 * from INFORMATION_SCHEMA.TABLES",
        [int]$Count = 3,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            try {
                $start = [System.Diagnostics.Stopwatch]::StartNew()
                $currentCount = 0
                try {
                    Write-Message -Level Verbose -Message "Connecting to $instance."
                    $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
                }
                catch {
                    Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                }

                do {
                    if (++$currentCount -eq 1) {
                        $first = [System.Diagnostics.Stopwatch]::StartNew()
                    }
                    $null = $server.Query($query)
                    if ($currentCount -eq $count) {
                        $last = $first.Elapsed
                    }
                }
                while ($currentCount -lt $count)

                $end = $start.Elapsed
                $totalTime = $end.TotalMilliseconds
                $average = $totalTime / $count

                $totalWarm = $last.TotalMilliseconds
                if ($Count -eq 1) {
                    $averageWarm = $totalWarm
                }
                else {
                    $averageWarm = $totalWarm / $count
                }

                [PSCustomObject]@{
                    ComputerName     = $server.ComputerName
                    InstanceName     = $server.ServiceName
                    SqlInstance      = $server.DomainInstanceName
                    Count            = $count
                    Total            = [prettytimespan]::FromMilliseconds($totalTime)
                    Avg              = [prettytimespan]::FromMilliseconds($average)
                    ExecuteOnlyTotal = [prettytimespan]::FromMilliseconds($totalWarm)
                    ExecuteOnlyAvg   = [prettytimespan]::FromMilliseconds($averageWarm)
                    NetworkOnlyTotal = [prettytimespan]::FromMilliseconds($totalTime - $totalWarm)
                } | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, 'Count as ExecutionCount', Total, 'Avg as Average', ExecuteOnlyTotal, 'ExecuteOnlyAvg as ExecuteOnlyAverage', NetworkOnlyTotal #backwards compat
            }
            catch {
                Stop-Function -Message "Error occurred testing dba network latency: $_" -ErrorRecord $_ -Continue -Target $instance
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Test-SqlNetworkLatency
    }
}
function Test-DbaOptimizeForAdHoc {
    <#
        .SYNOPSIS
            Displays information relating to SQL Server Optimize for AdHoc Workloads setting.  Works on SQL Server 2008-2016.

        .DESCRIPTION
            When this option is set, plan cache size is further reduced for single-use ad hoc OLTP workload.

            More info: https://msdn.microsoft.com/en-us/library/cc645587.aspx
            http://www.sqlservercentral.com/blogs/glennberry/2011/02/25/some-suggested-sql-server-2008-r2-instance-configuration-settings/

            These are just general recommendations for SQL Server and are a good starting point for setting the "optimize for adhoc workloads" option.

        .PARAMETER SqlInstance
            A collection of one or more SQL Server instance names to query.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

    .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message. This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting. Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Configure, SPConfigure
            Author: Brandon Abshire, netnerds.net

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaOptimizeForAdHoc

        .EXAMPLE
            Test-DbaOptimizeForAdHoc -SqlInstance sql2008, sqlserver2012

            Validates whether Optimize for AdHoc Workloads setting is enabled for servers sql2008 and sqlserver2012.
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $notesAdHocZero = "Recommended configuration is 1 (enabled)."
        $notesAsRecommended = "Configuration is already set as recommended."
        $recommendedValue = 1
    }
    process {

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            #Get current configured value
            $optimizeAdHoc = $server.Configuration.OptimizeAdhocWorkloads.ConfigValue

            #Setting notes for optimize adhoc value
            if ($optimizeAdHoc -eq $recommendedValue) {
                $notes = $notesAsRecommended
            }
            else {
                $notes = $notesAdHocZero
            }

            [pscustomobject]@{
                ComputerName             = $server.ComputerName
                InstanceName             = $server.ServiceName
                SqlInstance              = $server.DomainInstanceName
                CurrentOptimizeAdHoc     = $optimizeAdHoc
                RecommendedOptimizeAdHoc = $recommendedValue
                Notes                    = $notes
            }
        }
    }
}
function Test-DbaPowerPlan {
    <#
        .SYNOPSIS
            Checks the Power Plan settings for compliance with best practices, which recommend High Performance for SQL Server.

        .DESCRIPTION
            Checks the Power Plan settings on a computer against best practices recommendations. If one server is checked, only $true or $false is returned. If multiple servers are checked, each server's name and an isBestPractice field are returned.

            References:
            https://support.microsoft.com/en-us/kb/2207548
            http://www.sqlskills.com/blogs/glenn/windows-power-plan-effects-on-newer-intel-processors/

        .PARAMETER ComputerName
            The server(s) to check Power Plan settings on.

        .PARAMETER Credential
            Specifies a PSCredential object to use in authenticating to the server(s), instead of the current user account.

        .PARAMETER CustomPowerPlan
            If your organization uses a custom power plan that's considered best practice, specify it here.

        .PARAMETER Detailed
             Output all properties, will be deprecated in 1.0.0 release.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: PowerPlan
            Requires: WMI access to servers

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaPowerPlan

        .EXAMPLE
            Test-DbaPowerPlan -ComputerName sqlserver2014a

            Checks the Power Plan settings for sqlserver2014a and indicates whether or not it complies with best practices.

        .EXAMPLE
            Test-DbaPowerPlan -ComputerName sqlserver2014a -CustomPowerPlan 'Maximum Performance'

            Checks the Power Plan settings for sqlserver2014a and indicates whether or not it is set to the custom plan "Maximum Performance".
    #>
    param (
        [parameter(ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer", "SqlInstance")]
        [DbaInstance[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [string]$CustomPowerPlan,
        [switch]$Detailed,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter Detailed

        $bpPowerPlan = [PSCustomObject]@{
            InstanceID  = '8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c'
            ElementName = $null
        }

        $sessionOption = New-CimSessionOption -Protocol DCom
    }

    process {
        foreach ($computer in $ComputerName) {
            $server = Resolve-DbaNetworkName -ComputerName $computer -Credential $credential

            $computerResolved = $server.FullComputerName

            if (!$computerResolved) {
                Stop-Function -Message "Couldn't resolve hostname. Skipping." -Continue
            }

            Write-Message -Level Verbose -Message "Creating CimSession on $computer over WSMan."

            if (!$Credential) {
                $cimSession = New-CimSession -ComputerName $computerResolved -ErrorAction SilentlyContinue
            }
            else {
                $cimSession = New-CimSession -ComputerName $computerResolved -ErrorAction SilentlyContinue -Credential $Credential
            }

            if ($null -eq $cimSession.id) {
                Write-Message -Level Verbose -Message "Creating CimSession on $computer over WSMan failed. Creating CimSession on $computer over DCOM."

                if (!$Credential) {
                    $cimSession = New-CimSession -ComputerName $computerResolved -SessionOption $sessionOption -ErrorAction SilentlyContinue -Credential $Credential
                }
                else {
                    $cimSession = New-CimSession -ComputerName $computerResolved -SessionOption $sessionOption -ErrorAction SilentlyContinue
                }
            }

            if ($null -eq $cimSession.id) {
                Stop-Function -Message "Can't create CimSession on $computer." -Target $computer
            }

            Write-Message -Level Verbose -Message "Getting Power Plan information from $computer."

            try {
                $powerPlans = Get-CimInstance -CimSession $cimSession -ClassName Win32_PowerPlan -Namespace "root\cimv2\power" -ErrorAction Stop | Select-Object ElementName, InstanceID, IsActive
            }
            catch {
                if ($_.Exception -match "namespace") {
                    Stop-Function -Message "Can't get Power Plan Info for $computer. Unsupported operating system." -Continue -ErrorRecord $_ -Target $computer
                }
                else {
                    Stop-Function -Message "Can't get Power Plan Info for $computer. Check logs for more details." -Continue -ErrorRecord $_ -Target $computer
                }
            }

            $powerPlan = $powerPlans | Where-Object IsActive -eq 'True' | Select-Object ElementName, InstanceID
            $powerPlan.InstanceID = $powerPlan.InstanceID.Split('{')[1].Split('}')[0]

            if ($CustomPowerPlan.Length -gt 0) {
                $bpPowerPlan.ElementName = $CustomPowerPlan
                $bpPowerPlan.InstanceID = $($powerPlans | Where-Object { $_.ElementName -eq $CustomPowerPlan }).InstanceID
            }
            else {
                $bpPowerPlan.ElementName = $($powerPlans | Where-Object { $_.InstanceID.Split('{')[1].Split('}')[0] -eq $bpPowerPlan.InstanceID }).ElementName
                if ($null -eq $bpPowerplan.ElementName) {
                    $bpPowerPlan.ElementName = "You do not have the high performance plan installed on this machine."
                }
            }

            Write-Message -Level Verbose -Message "Recommended GUID is $($bpPowerPlan.InstanceID) and you have $($powerPlan.InstanceID)."

            if ($null -eq $powerPlan.InstanceID) {
                $powerPlan.ElementName = "Unknown"
            }

            if ($powerPlan.InstanceID -eq $bpPowerPlan.InstanceID) {
                $isBestPractice = $true
            }
            else {
                $isBestPractice = $false
            }

            [PSCustomObject]@{
                ComputerName         = $computer
                ActivePowerPlan      = $powerPlan.ElementName
                RecommendedPowerPlan = $bpPowerPlan.ElementName
                isBestPractice       = $isBestPractice
            }
        }
    }
}
function Test-DbaRecoveryModel {
    <#
        .SYNOPSIS
            Find if database is really a specific recovery model or not.

        .DESCRIPTION
            When you switch a database into FULL recovery model, it will behave like a SIMPLE recovery model until a full backup is taken in order to begin a log backup chain.

            However, you may also desire to validate if a database is SIMPLE or BULK LOGGED on an instance.

            Inspired by Paul Randal's post (http://www.sqlskills.com/blogs/paul/new-script-is-that-database-really-in-the-full-recovery-mode/)

        .PARAMETER SqlInstance
            The SQL Server instance to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies the database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies the database(s) to exclude from processing. Options for this list are auto-populated from the server.

        .PARAMETER RecoveryModel
            Specifies the type of recovery model you wish to test. By default it will test for FULL Recovery Model.

        .PARAMETER Detailed
            Output all properties, will be deprecated in 1.0.0 release.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: DisasterRecovery, Backup
            Author: Claudio Silva (@ClaudioESSilva)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: GNU GPL v3 https://opensource.org/licenses/GPL-3.0

        .LINK
            https://dbatools.io/Test-DbaRecoveryModel

        .EXAMPLE
            Test-DbaRecoveryModel -SqlInstance sql2005

            Shows all databases where the configured recovery model is FULL and indicates whether or not they are really in FULL recovery model.

        .EXAMPLE
            Test-DbaRecoveryModel -SqlInstance . | Where-Object {$_.ActualRecoveryModel -ne "FULL"}

            Only shows the databases that are functionally in 'simple' mode.

        .EXAMPLE
            Test-DbaRecoveryModel -SqlInstance sql2008 -RecoveryModel Bulk_Logged | Sort-Object Server  -Descending

            Shows all databases where the configured recovery model is BULK_LOGGED and sort them by server name descending

        .EXAMPLE
            Test-DbaRecoveryModel -SqlInstance localhost | Select-Object -Property *

            Shows all of the properties for the databases that have Full Recovery Model
    #>
    [CmdletBinding()]
    [OutputType("System.Collections.ArrayList")]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [PSCredential]$SqlCredential,
        [validateSet("Full","Simple","Bulk_Logged")]
        [object]$RecoveryModel,
        [switch]$Detailed,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter Detailed
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Alias Test-DbaFullRecoveryModel

        if(Test-Bound -ParameterName RecoveryModel -Not){
            $RecoveryModel = "Full"
        }

        switch($RecoveryModel){
            "Full"          {$recoveryCode = 1}
            "Bulk_Logged"   {$recoveryCode = 2}
            "Simple"        {$recoveryCode = 3}
        }

        $sqlRecoveryModel = "SELECT  SERVERPROPERTY('MachineName') AS ComputerName,
                ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
                SERVERPROPERTY('ServerName') AS SqlInstance
                        , d.[name] AS [Database]
                        , d.recovery_model AS RecoveryModel
                        , d.recovery_model_desc AS RecoveryModelDesc
                        , CASE
                            WHEN d.recovery_model = 1 AND drs.last_log_backup_lsn IS NOT NULL THEN 1
                            ELSE 0
                           END AS IsReallyInFullRecoveryModel
                  FROM sys.databases AS D
                    INNER JOIN sys.database_recovery_status AS drs
                       ON D.database_id = drs.database_id
                  WHERE d.recovery_model = $recoveryCode"

        if ($Database) {
            $dblist = $Database -join "','"
            $databasefilter += "AND d.[name] in ('$dblist')"
        }
        if ($ExcludeDatabase) {
            $dblist = $ExcludeDatabase -join "','"
            $databasefilter += "AND d.[name] NOT IN ('$dblist')"
        }

        $sql = "$sqlRecoveryModel $databasefilter"

        Write-Message -Level Debug -Message $sql
    }
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            try {
                $results = $server.Query($sql)

                if (-not $results) {
                    Write-Message -Level Verbose -Message "Server '$instance' does not have any databases in the $RecoveryModel recovery model."
                }

                foreach ($row in $results) {
                    if (!([bool]$row.IsReallyInFullRecoveryModel) -and $RecoveryModel -eq 'Full') {
                        $ActualRecoveryModel = "SIMPLE"
                    }
                    else{
                        $ActualRecoveryModel = "$($RecoveryModel.ToString().ToUpper())"
                    }

                    [PSCustomObject]@{
                        ComputerName   = $row.ComputerName
                        InstanceName   = $row.InstanceName
                        SqlInstance    = $row.SqlInstance
                        Database       = $row.Database
                        ConfiguredRecoveryModel = $row.RecoveryModelDesc
                        ActualRecoveryModel = $ActualRecoveryModel
                    } | Select-DefaultView -Property ComputerName,InstanceName,SqlInstance,Database,ConfiguredRecoveryModel,ActualRecoveryModel
                }
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
        }
    }
}
function Test-DbaServerName {
    <#
        .SYNOPSIS
            Tests to see if it's possible to easily rename the server at the SQL Server instance level, or if it even needs to be changed.

        .DESCRIPTION
            When a SQL Server's host OS is renamed, the SQL Server should be as well. This helps with Availability Groups and Kerberos.

            This command helps determine if your OS and SQL Server names match, and whether a rename is required.

            It then checks conditions that would prevent a rename, such as database mirroring and replication.

            https://www.mssqltips.com/sqlservertip/2525/steps-to-change-the-server-name-for-a-sql-server-machine/

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Detailed
            Output all properties, will be deprecated in 1.0.0 release.

        .PARAMETER ExcludeSsrs
            If this switch is enabled, checking for SQL Server Reporting Services will be skipped.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: SPN, ServerName

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaServerName

        .EXAMPLE
            Test-DbaServerName -SqlInstance sqlserver2014a

            Returns ServerInstanceName, SqlServerName, IsEqual and RenameRequired for sqlserver2014a.

        .EXAMPLE
            Test-DbaServerName -SqlInstance sqlserver2014a, sql2016

            Returns ServerInstanceName, SqlServerName, IsEqual and RenameRequired for sqlserver2014a and sql2016.

        .EXAMPLE
            Test-DbaServerName -SqlInstance sqlserver2014a, sql2016 -ExcludeSsrs

            Returns ServerInstanceName, SqlServerName, IsEqual and RenameRequired for sqlserver2014a and sql2016, but skips validating if SSRS is installed on both instances.

        .EXAMPLE
            Test-DbaServerName -SqlInstance sqlserver2014a, sql2016 | Select-Object *

            Returns ServerInstanceName, SqlServerName, IsEqual and RenameRequired for sqlserver2014a and sql2016.

            If a Rename is required, it will also show Updatable, and Reasons if the servername is not updatable.
    #>
    [CmdletBinding()]
    [OutputType([System.Collections.ArrayList])]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [switch]$Detailed,
        [Alias("NoWarning")]
        [switch]$ExcludeSsrs,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter Detailed
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter NoWarning
    }
    process {

        foreach ($instance in $SqlInstance) {
            Write-Verbose "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($server.IsClustered) {
                Write-Message -Level Warning -Message "$instance is a cluster. Renaming clusters is not supported by Microsoft."
            }

            $sqlInstanceName = $server.Query("SELECT @@servername AS ServerName").ServerName
            $instance = $server.InstanceName

            if ($instance.Length -eq 0) {
                $serverInstanceName = $server.NetName
                $instance = "MSSQLSERVER"
            }
            else {
                $netname = $server.NetName
                $serverInstanceName = "$netname\$instance"
            }

            $serverInfo = [PSCustomObject]@{
                ComputerName = $server.NetName
                ServerName   = $sqlInstanceName
                InstanceName = $server.ServiceName
                SqlInstance  = $server.DomainInstanceName
                RenameRequired = $serverInstanceName -ne $sqlInstanceName
                Updatable    = "N/A"
                Warnings     = $null
                Blockers     = $null
            }

            $reasons = @()
            $ssrsService = "SQL Server Reporting Services ($instance)"

            Write-Message -Level Verbose -Message "Checking for $serverName on $netBiosName"
            $rs = $null
            if ($SkipSsrs -eq $false -or $NoWarning -eq $false) {
                try {
                    $rs = Get-DbaSqlService -ComputerName $instance.ComputerName -InstanceName $server.ServiceName -Type SSRS -EnableException -WarningAction Stop
                }
                catch {
                    Write-Message -Level Warning -Message "Unable to pull information on $ssrsService." -ErrorRecord $_ -Target $instance
                }
            }

            if ($null -ne $rs -or $rs.Count -gt 0) {
                if ($rs.State -eq 'Running') {
                    $rstext = "$ssrsService must be stopped and updated."
                }
                else {
                    $rstext = "$ssrsService exists. When it is started again, it must be updated."
                }
                $serverInfo.Warnings = $rstext
            }
            else {
                $serverInfo.Warnings = "N/A"
            }

            # check for mirroring
            $mirroredDb = $server.Databases | Where-Object { $_.IsMirroringEnabled -eq $true }

            Write-Message -Level Debug -Message "Found the following mirrored dbs: $($mirroredDb.Name)"

            if ($mirroredDb.Length -gt 0) {
                $dbs = $mirroredDb.Name -join ", "
                $reasons += "Databases are being mirrored: $dbs"
            }

            # check for replication
            $sql = "SELECT name FROM sys.databases WHERE is_published = 1 OR is_subscribed = 1 OR is_distributor = 1"
            Write-Message -Level Debug -Message "SQL Statement: $sql"
            $replicatedDb = $server.Query($sql)

            if ($replicatedDb.Count -gt 0) {
                $dbs = $replicatedDb.Name -join ", "
                $reasons += "Database(s) are involved in replication: $dbs"
            }

            # check for even more replication
            $sql = "SELECT srl.remote_name as RemoteLoginName FROM sys.remote_logins srl JOIN sys.sysservers sss ON srl.server_id = sss.srvid"
            Write-Message -Level Debug -Message "SQL Statement: $sql"
            $results = $server.Query($sql)

            if ($results.RemoteLoginName.Count -gt 0) {
                $remoteLogins = $results.RemoteLoginName -join ", "
                $reasons += "Remote logins still exist: $remoteLogins"
            }

            if ($reasons.Length -gt 0) {
                $serverInfo.Updatable = $false
                $serverInfo.Blockers = $reasons
            }
            else {
                $serverInfo.Updatable = $true
                $serverInfo.Blockers = "N/A"
            }

            $serverInfo | Select-DefaultView -ExcludeProperty InstanceName, SqlInstance
        }
    }
}
#ValidationTags#FlowControl,Pipeline#
function Test-DbaSpn {
    <#
        .SYNOPSIS
            Test-DbaSpn will determine what SPNs *should* be set for a given server (and any instances of SQL running on it) and return
            whether the SPNs are set or not.

        .DESCRIPTION
            This function is designed to take in a server name(s) and attempt to determine required SPNs. It was initially written to mimic the (previously)
            broken functionality of the Microsoft Kerberos Configuration manager and SQL Server 2016. The functon will connect to a remote server and,
            through WMI, discover all running intances of SQL Server. For any instances with TCP/IP enabled, the script will determine which port(s)
            the instances are listening on and generate the required SPNs. For named instances NOT using dynamic ports, the script will generate a port-
            based SPN for those instances as well.  At a minimum, the script will test a base, port-less SPN for each instance discovered.

            Once the required SPNs are generated, the script will connect to Active Directory and search for any of the SPNs (if any) that are already
            set.

            The function will return a custom object(s) that contains the server name checked, the instance name discovered, the account the service is
            running under, and what the "required" SPN should be. It will also return a boolean property indicating if the SPN is set in Active Directory
            or not.

        .PARAMETER ComputerName
            The computer you want to discover any SQL Server instances on. This parameter is required.

        .PARAMETER Credential
            The credential you want to use to connect to the remote server and active directory.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: SPN
            Author: Drew Furgiuele (@pittfurg), http://www.port1433.com
            Editor: niphlod

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaSpn

        .EXAMPLE
            Test-DbaSpn -ComputerName SQLSERVERA -Credential (Get-Credential)

            Connects to a computer (SQLSERVERA) and queries WMI for all SQL instances and return "required" SPNs. It will then take each SPN it generates
            and query Active Directory to make sure the SPNs are set.

        .EXAMPLE
            Test-DbaSpn -ComputerName SQLSERVERA,SQLSERVERB -Credential (Get-Credential)

            Connects to multiple computers (SQLSERVERA, SQLSERVERB) and queries WMI for all SQL instances and return "required" SPNs.
            It will then take each SPN it generates and query Active Directory to make sure the SPNs are set.

        .EXAMPLE
            Test-DbaSpn -ComputerName SQLSERVERC -Credential (Get-Credential)

            Connects to a computer (SQLSERVERC) on a specified and queries WMI for all SQL instances and return "required" SPNs.
            It will then take each SPN it generates and query Active Directory to make sure the SPNs are set. Note that the credential you pass must have be a valid login with appropriate rights on the domain
    #>
    [cmdletbinding()]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [DbaInstance[]]$ComputerName,
        [PSCredential]$Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        # spare the cmdlet to search for the same account over and over
        $resultCache = @{}
    }
    process {
        foreach ($computer in $ComputerName) {
            try {
                $resolved = Resolve-DbaNetworkName -ComputerName $computer.ComputerName -Credential $Credential -ErrorAction Stop
            }
            catch {
                $resolved = Resolve-DbaNetworkName -ComputerName $computer.ComputerName -Turbo
            }

            if ($null -eq $resolved.IPAddress) {
                Write-Message -Level Warning -Message "Cannot resolve IP address, moving on."
                continue
            }

            $hostEntry = $resolved.FullComputerName

            Write-Message -Message "Resolved ComputerName to FQDN: $hostEntry" -Level Verbose

            $Scriptblock = {

                function Convert-SqlVersion {
                    [cmdletbinding()]
                    param (
                        [version]$version
                    )

                    switch ($version.Major) {
                        9 { "SQL Server 2005" }
                        10 {
                            if ($version.Minor -eq 0) {
                                "SQL Server 2008"
                            }
                            else {
                                "SQL Server 2008 R2"
                            }
                        }
                        11 { "SQL Server 2012" }
                        12 { "SQL Server 2014" }
                        13 { "SQL Server 2016" }
                        14 { "SQL Server 2017" }
                        default { $version }
                    }
                }

                $spns = @()
                $servereName = $args[0]
                $hostEntry = $args[1]
                $instanceName = $args[2]
                $instanceCount = $wmi.ServerInstances.Count

                <# DO NOT use Write-Message as this is inside of a script block #>
                Write-Verbose "Found $instanceCount instances"

                foreach ($instance in $wmi.ServerInstances) {
                    $spn = [pscustomobject] @{
                        ComputerName           = $servereName
                        InstanceName           = $instanceName
                        #SKUNAME
                        SqlProduct             = $null
                        InstanceServiceAccount = $null
                        RequiredSPN            = $null
                        IsSet                  = $false
                        Cluster                = $false
                        TcpEnabled             = $false
                        Port                   = $null
                        DynamicPort            = $false
                        Warning                = "None"
                        Error                  = "None"
                        # for piping
                        Credential             = $Credential
                    }

                    $spn.InstanceName = $instance.Name
                    $instanceName = $spn.InstanceName

                    <# DO NOT use Write-Message as this is inside of a script block #>
                    Write-Verbose "Parsing $instanceName"

                    $services = $wmi.Services | Where-Object DisplayName -EQ "SQL Server ($instanceName)"
                    $spn.InstanceServiceAccount = $services.ServiceAccount
                    $spn.Cluster = ($services.advancedproperties | Where-Object Name -EQ 'Clustered').Value

                    if ($spn.Cluster) {
                        $hostEntry = ($services.advancedproperties | Where-Object Name -EQ 'VSNAME').Value.ToLower()
                        <# DO NOT use Write-Message as this is inside of a script block #>
                        Write-Verbose "Found cluster $hostEntry"
                        $hostEntry = ([System.Net.Dns]::GetHostEntry($hostEntry)).HostName
                        $spn.ComputerName = $hostEntry
                    }

                    $rawVersion = [version]($services.AdvancedProperties | Where-Object Name -EQ 'VERSION').Value

                    $version = Convert-SqlVersion $rawVersion
                    $skuName = ($services.AdvancedProperties | Where-Object Name -EQ 'SKUNAME').Value

                    $spn.SqlProduct = "$version $skuName"

                    #is tcp enabled on this instance? If not, we don't need an spn, son
                    if ((($instance.ServerProtocols | Where-Object { $_.Displayname -eq "TCP/IP" }).ProtocolProperties | Where-Object { $_.Name -eq "Enabled" }).Value -eq $true) {
                        <# DO NOT use Write-Message as this is inside of a script block #>
                        Write-Verbose "TCP is enabled, gathering SPN requirements"
                        $spn.TcpEnabled = $true
                        #Each instance has a default SPN of MSSQLSvc\<fqdn> or MSSSQLSvc\<fqdn>:Instance
                        if ($instance.Name -eq "MSSQLSERVER") {
                            $spn.RequiredSPN = "MSSQLSvc/$hostEntry"
                        }
                        else {
                            $spn.RequiredSPN = "MSSQLSvc/" + $hostEntry + ":" + $instance.Name
                        }
                    }

                    $spns += $spn
                }
                # Now, for each spn, do we need a port set? Only if TCP is enabled and NOT DYNAMIC!
                foreach ($spn in $spns) {
                    $ports = @()

                    $ips = (($wmi.ServerInstances | Where-Object { $_.Name -eq $spn.InstanceName }).ServerProtocols | Where-Object { $_.DisplayName -eq "TCP/IP" -and $_.IsEnabled -eq "True" }).IpAddresses
                    $ipAllPort = $null
                    foreach ($ip in $ips) {
                        if ($ip.Name -eq "IPAll") {
                            $ipAllPort = ($ip.IPAddressProperties | Where-Object { $_.Name -eq "TCPPort" }).Value
                            if (($ip.IpAddressProperties | Where-Object { $_.Name -eq "TcpDynamicPorts" }).Value -ne "") {
                                $ipAllPort = ($ip.IPAddressProperties | Where-Object { $_.Name -eq "TcpDynamicPorts" }).Value + "d"
                            }
                        }
                        else {
                            $enabled = ($ip.IPAddressProperties | Where-Object { $_.Name -eq "Enabled" }).Value
                            $active = ($ip.IPAddressProperties | Where-Object { $_.Name -eq "Active" }).Value
                            $tcpDynamicPorts = ($ip.IPAddressProperties | Where-Object { $_.Name -eq "TcpDynamicPorts" }).Value
                            if ($enabled -and $active -and $tcpDynamicPorts -eq "") {
                                $ports += ($ip.IPAddressProperties | Where-Object { $_.Name -eq "TCPPort" }).Value
                            }
                            elseif ($enabled -and $active -and $tcpDynamicPorts -ne "") {
                                $ports += $ipAllPort + "d"
                            }
                        }
                    }
                    if ($ipAllPort -ne "") {
                        #IPAll overrides any set ports. Not sure why that's the way it is?
                        $ports = $ipAllPort
                    }

                    $ports = $ports | Select-Object -Unique
                    foreach ($port in $ports) {
                        $newspn = $spn.PSObject.Copy()
                        if ($port -like "*d") {
                            $newspn.Port = ($port.replace("d", ""))
                            $newspn.RequiredSPN = $newspn.RequiredSPN.Replace($newSPN.InstanceName, $newspn.Port)
                            $newspn.DynamicPort = $true
                            $newspn.Warning = "Dynamic port is enabled"
                        }
                        else {
                            #If this is a named instance, replace the instance name with a port number (for non-dynamic ported named instances)
                            $newspn.Port = $port
                            $newspn.DynamicPort = $false

                            if ($newspn.InstanceName -eq "MSSQLSERVER") {
                                $newspn.RequiredSPN = $newspn.RequiredSPN + ":" + $port
                            }
                            else {
                                $newspn.RequiredSPN = $newspn.RequiredSPN.Replace($newSPN.InstanceName, $newspn.Port)
                            }
                        }
                        $spns += $newspn
                    }
                }
                $spns
            }

            Write-Message -Message "Connecting to SQL WMI on remote computer " -Level Verbose

            try {
                $spns = Invoke-ManagedComputerCommand -ComputerName $hostEntry -ScriptBlock $Scriptblock -ArgumentList $resolved.FullComputerName, $hostEntry, $computer.InstanceName -Credential $Credential -ErrorAction Stop
            }
            catch {
                Stop-Function -Message "Couldn't connect to $computer" -ErrorRecord $_ -Continue
            }

            #Now query AD for each required SPN
            foreach ($spn in $spns) {
                $searchfor = 'User'
                if ($spn.InstanceServiceAccount -eq 'LocalSystem' -or $spn.InstanceServiceAccount -like 'NT SERVICE\*') {
                    Write-Message -Level Verbose -Message "Virtual account detected, changing target registration to computername"
                    $spn.InstanceServiceAccount = "$($resolved.Domain)\$($resolved.ComputerName)$"
                    $searchfor = 'Computer'
                }
                elseif ($spn.InstanceServiceAccount -like '*\*$') {
                    Write-Message -Level Verbose -Message "Managed Service Account detected"
                    $searchfor = 'Computer'
                }

                $serviceAccount = $spn.InstanceServiceAccount
                # spare the cmdlet to search for the same account over and over
                if ($spn.InstanceServiceAccount -notin $resultCache.Keys) {
                    Write-Message -Message "Searching for $serviceAccount" -Level Verbose
                    try {
                        $result = Get-DbaADObject -ADObject $serviceAccount -Type $searchfor -Credential $Credential -EnableException
                        $resultCache[$spn.InstanceServiceAccount] = $result
                    }
                    catch {
                        if (![System.String]::IsNullOrEmpty($spn.InstanceServiceAccount)) {
                            Write-Message -Message "AD lookup failure. This may be because the domain cannot be resolved for the SQL Server service account ($serviceAccount)." -Level Warning
                        }
                    }
                }
                else {
                    $result = $resultCache[$spn.InstanceServiceAccount]
                }
                if ($result.Count -gt 0) {
                    try {
                        $results = $result.GetUnderlyingObject()
                        if ($results.Properties.servicePrincipalName -contains $spn.RequiredSPN) {
                            $spn.IsSet = $true
                        }
                    }
                    catch {
                        Write-Message -Message "The SQL Service account ($serviceAccount) has been found, but you don't have enough permission to inspect its SPNs" -Level Warning
                        continue
                    }
                }
                else {
                    Write-Message -Level Warning -Message "SQL Service account not found. Results may not be accurate."
                    $spn
                    continue
                }
                if (!$spn.IsSet -and $spn.TcpEnabled) {
                    $spn.Error = "SPN missing"
                }

                $spn | Select-DefaultView -ExcludeProperty Credential, DomainName
            }
        }
    }
}
function Test-DbaSqlBuild {
    <#
    .SYNOPSIS
        Returns SQL Server Build "compliance" level on a build

    .DESCRIPTION
        It answers the question "is this build up to date ?"
        Returns info about the specific build of a SQL instance, including the SP, the CU and the reference KB, End Of Support, wherever possible.
        It adds a Compliance property as true/false, and adds details about the "targeted compliance"

    .PARAMETER Build
        Instead of connecting to a real instance, pass a string identifying the build to get the info back.

    .PARAMETER MinimumBuild
        This is the build version to test "compliance" against. Anything below this is flagged as not compliant

    .PARAMETER MaxBehind
        Instead of using a specific MinimumBuild here you can pass "how many service packs and cu back" is the targeted compliance level
        You can use xxSP or xxCU or both, where xx is a number. See Examples for more informations

    .PARAMETER Latest
        Shortcut for specifying the very most up-to-date build available.

    .PARAMETER SqlInstance
        Target any number of instances, in order to return their compliance state.

    .PARAMETER SqlCredential
        When connecting to an instance, use the credentials specified.

    .PARAMETER Update
        Looks online for the most up to date reference, replacing the local one.

    .PARAMETER Quiet
        Makes the function just return $true/$false. It's useful if you use Test-DbaSqlBuild in your own scripts, like
            if (Test-DbaSqlBuild -Build "12.0.5540" -MaxBehind "0CU" -Quiet) {
                Do-Something
            }

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .EXAMPLE
        Test-DbaSqlBuild -Build "12.0.5540" -MinimumBuild "12.0.5557"

        Returns information about a build identified by "12.0.5540" (which is SQL 2014 with SP2 and CU4), which is not compliant as the minimum required
        build is "12.0.5557" (which is SQL 2014 with SP2 and CU8)

    .EXAMPLE
        Test-DbaSqlBuild -Build "12.0.5540" -MaxBehind "1SP"

        Returns information about a build identified by "12.0.5540", making sure it is AT MOST 1 Service Pack "behind". For that version,
        that identifies an SP2, means accepting as the lowest compliance version as "12.0.4110", that identifies 2014 with SP1

    .EXAMPLE
        Test-DbaSqlBuild -Build "12.0.5540" -MaxBehind "1SP 1CU"

        Returns information about a build identified by "12.0.5540", making sure it is AT MOST 1 Service Pack "behind", plus 1 CU "behind". For that version,
        that identifies an SP2 and CU, rolling back 1 SP brings you to "12.0.4110", but given the latest CU for SP1 is CU13, the target "compliant" build
        will be "12.0.4511", which is 2014 with SP1 and CU12

    .EXAMPLE
        Test-DbaSqlBuild -Build "12.0.5540" -MaxBehind "0CU"

        Returns information about a build identified by "12.0.5540", making sure it is the latest CU release.

    .EXAMPLE
        Test-DbaSqlBuild -Build "12.0.5540" -Latest

        Same as previous, returns information about a build identified by "12.0.5540", making sure it is the latest build available.

    .EXAMPLE
        Test-DbaSqlBuild -Build "12.00.4502" -MinimumBuild "12.0.4511" -Update

        Same as before, but tries to fetch the most up to date index online. When the online version is newer, the local one gets overwritten

    .EXAMPLE
        Test-DbaSqlBuild -Build "12.0.4502","10.50.4260" -MinimumBuild "12.0.4511"

        Returns information builds identified by these versions strings

    .EXAMPLE
        Get-DbaRegisteredServer -SqlInstance sqlserver2014a | Test-DbaSqlBuild -MinimumBuild "12.0.4511"

        Integrate with other commandlets to have builds checked for all your registered servers on sqlserver2014a

    .NOTES
        Author: niphlod
        Editor: Fred
        Tags: SqlBuild

        dbatools PowerShell module (https://dbatools.io, [email protected])
        Copyright (C) 2016 Chrissy LeMaire
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Test-DbaSqlBuild
#>
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
    [CmdletBinding()]
    param (
        [version[]]
        $Build,

        [version]
        $MinimumBuild,

        [string]
        $MaxBehind,

        [switch]
        $Latest,

        [parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]
        $SqlInstance,

        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,

        [switch]
        $Update,

        [switch]
        $Quiet,

        [switch]
        [Alias('Silent')]$EnableException
    )

    begin {
        #region Helper functions
        function Get-DbaSqlBuildReferenceIndex {
            [CmdletBinding()]

            $DbatoolsData = Get-DbaConfigValue -Name 'Path.DbatoolsData'
            $writable_idxfile = Join-Path $DbatoolsData "dbatools-buildref-index.json"
            $result = Get-Content $writable_idxfile -Raw | ConvertFrom-Json
            $result.Data | Select-Object @{ Name = "VersionObject"; Expression = { [version]$_.Version } }, *
        }

        $ComplianceSpec = @()
        $ComplianceSpecExclusiveParams = @('MinimumBuild', 'MaxBehind', 'Latest')
        foreach ($exclParam in $ComplianceSpecExclusiveParams) {
            if (Test-Bound -Parameter $exclParam) { $ComplianceSpec += $exclParam }
        }
        if ($ComplianceSpec.Length -gt 1) {
            Stop-Function -Category InvalidArgument -Message "-MinimumBuild, -MaxBehind and -Latest are mutually exclusive. Please choose only one. Quitting."
            return
        }
        if ($ComplianceSpec.Length -eq 0) {
            Stop-Function -Category InvalidArgument -Message "You need to choose one from -MinimumBuild, -MaxBehind and -Latest. Quitting."
            return
        }
        try {
            # Empty call just to make sure the buildref is updated and on the right path
            Get-DbaSqlBuildReference -Update:$Update -EnableException:$true
            $IdxRef = Get-DbaSqlBuildReferenceIndex
        }
        catch {
            Stop-Function -Message "Error loading SQL build reference" -ErrorRecord $_
            return
        }
        if ($MaxBehind) {
            $MaxBehindValidator = [regex]'^(?<howmany>[\d]+)(?<what>SP|CU)$'
            $pieces = $MaxBehind.Split(' ')	| Where-Object { $_ }
            try {
                $ParsedMaxBehind = @{}
                foreach ($piece in $pieces) {
                    $pieceMatch = $MaxBehindValidator.Match($piece)
                    if ($pieceMatch.Success -ne $true) {
                        Stop-Function -Message "MaxBehind has an invalid syntax ('$piece' could not be parsed correctly)" -ErrorRecord $_
                        return
                    }
                    else {
                        $howmany = [int]$pieceMatch.Groups['howmany'].Value
                        $what = $pieceMatch.Groups['what'].Value
                        if ($ParsedMaxBehind.ContainsKey($what)) {
                            Stop-Function -Message "The specifier $what has been already passed" -ErrorRecord $_
                            return
                        }
                        else {
                            $ParsedMaxBehind[$what] = $howmany
                        }
                    }
                }
                if (-not $ParsedMaxBehind.ContainsKey('SP')) {
                    $ParsedMaxBehind['SP'] = 0
                }
            }
            catch {
                Stop-Function -Message "Error parsing MaxBehind" -ErrorRecord $_
                return
            }
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }
        $hiddenProps = @()
        if (-not $SqlInstance) {
            $hiddenProps += 'SqlInstance'
        }
        if ($MinimumBuild) {
            $hiddenProps += 'MaxBehind', 'SPTarget', 'CUTarget', 'BuildTarget'
        }
        elseif ($MaxBehind -or $Latest) {
            $hiddenProps += 'MinimumBuild'
        }
        $BuildVersions = Get-DbaSqlBuildReference -Build $Build -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Update:$Update -EnableException:$EnableException
        foreach ($BuildVersion in $BuildVersions) {
            $inputbuild = $BuildVersion.Build
            $compliant = $false
            $targetSPName = $null
            $targetCUName = $null
            if ($BuildVersion.MatchType -eq 'Approximate') {
                Stop-Function -Message "$($BuildVersion.Build) is not recognized as a correct version" -ErrorRecord $_ -Continue
            }
            if ($MinimumBuild) {
                Write-Message -Level Debug -Message "Comparing $MinimumBuild to $inputbuild"
                if ($inputbuild -ge $MinimumBuild) {
                    $compliant = $true
                }
            }
            elseif ($MaxBehind -or $Latest) {
                $IdxVersion = $IdxRef | Where-Object Version -like "$($inputbuild.Major).$($inputbuild.Minor).*"
                $lastsp = ''
                $SPsAndCUs = @()
                foreach ($el in $IdxVersion) {
                    if ($null -ne $el.SP) {
                        $lastsp = $el.SP | Where-Object { $_ -ne 'LATEST' }
                        $SPsAndCUs += @{
                            VersionObject = $el.VersionObject
                            SP            = $lastsp
                        }
                    }
                    if ($null -ne $el.CU) {
                        $SPsAndCUs += @{
                            VersionObject = $el.VersionObject
                            SP            = $lastsp
                            CU            = $el.CU
                        }
                    }
                }
                $targetedBuild = $SPsAndCUs[0]
                if ($Latest) {
                    $targetedBuild = $IdxVersion[$IdxVersion.Length - 1]
                }
                else {
                    if ($ParsedMaxBehind.ContainsKey('SP')) {
                        $AllSPs = $SPsAndCUs.SP | Select-Object -Unique
                        $targetSP = $AllSPs.Length - $ParsedMaxBehind['SP'] - 1
                        if ($targetSP -lt 0) {
                            $targetSP = 0
                        }
                        $targetSPName = $AllSPs[$targetSP]
                        Write-Message -Level Debug -Message "Target SP is $targetSPName - $targetSP on $($AllSPs.Length)"
                        $targetedBuild = $SPsAndCUs | Where-Object SP -eq $targetSPName | Select-Object -First 1
                    }
                    if ($ParsedMaxBehind.ContainsKey('CU')) {
                        $AllCUs = ($SPsAndCUs | Where-Object VersionObject -gt $targetedBuild.VersionObject).CU | Select-Object -Unique
                        if ($AllCUs.Length -gt 0) {
                            #CU after the targeted build available
                            $targetCU = $AllCUs.Length - $ParsedMaxBehind['CU'] - 1
                            if ($targetCU -lt 0) {
                                $targetCU = 0
                            }
                            $targetCUName = $AllCUs[$targetCU]
                            Write-Message -Level Debug -Message "Target CU is $targetCUName - $targetCU on $($AllCUs.Length)"
                            $targetedBuild = $SPsAndCUs | Where-Object VersionObject -gt $targetedBuild.VersionObject | Where-Object CU -eq $targetCUName | Select-Object -First 1
                        }
                    }
                }
                if ($inputbuild -ge $targetedBuild.VersionObject) {
                    $compliant = $true
                }
            }
            Add-Member -InputObject $BuildVersion -MemberType NoteProperty -Name Compliant -Value $compliant
            Add-Member -InputObject $BuildVersion -MemberType NoteProperty -Name MinimumBuild -Value $MinimumBuild
            Add-Member -InputObject $BuildVersion -MemberType NoteProperty -Name MaxBehind -Value $MaxBehind
            Add-Member -InputObject $BuildVersion -MemberType NoteProperty -Name SPTarget -Value $targetSPName
            Add-Member -InputObject $BuildVersion -MemberType NoteProperty -Name CUTarget -Value $targetCUName
            Add-Member -InputObject $BuildVersion -MemberType NoteProperty -Name BuildTarget -Value $targetedBuild.VersionObject
            if ($Quiet) {
                $BuildVersion.Compliant
            }
            else {
                $BuildVersion | Select-Object * | Select-DefaultView -ExcludeProperty $hiddenProps
            }
        }
    }
}
function Test-DbaSqlManagementObject {
    <#
        .SYNOPSIS
            Tests to see if the SMO version specified exists on the computer.

        .DESCRIPTION
            The Test-DbaSqlManagementObject returns True if the Version is on the computer, and False if it does not exist.

        .PARAMETER ComputerName
            The name of the target you would like to check

        .PARAMETER Credential
            This command uses Windows credentials. This parameter allows you to connect remotely as a different user.

        .PARAMETER VersionNumber
            This is the specific version number you are looking for and the return will be True.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: SMO
            Author: Ben Miller (@DBAduck - http://dbaduck.com)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaSqlManagementObject

        .EXAMPLE
            Test-DbaSqlManagementObject -VersionNumber 13

            Returns True if the version exists, if it does not exist it will return False
    #>
    [CmdletBinding()]
    param (
        [parameter(ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer", "SqlInstance")]
        [DbaInstance[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Parameter(Mandatory)]
        [int[]]$VersionNumber,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $scriptblock = {
            foreach ($number in $args) {
                $smoList = (Get-ChildItem -Path "$($env:SystemRoot)\assembly\GAC_MSIL\Microsoft.SqlServer.Smo" -Filter "$number.*" | Sort-Object Name -Descending).Name

                if ($smoList) {
                    [pscustomobject]@{
                        ComputerName = $env:COMPUTERNAME
                        Version      = $number
                        Exists       = $true
                    }
                }
                else {
                    [pscustomobject]@{
                        ComputerName = $env:COMPUTERNAME
                        Version      = $number
                        Exists       = $false
                    }
                }
            }
        }
    }
    process {
        foreach ($computer in $ComputerName.ComputerName) {
            try {
                Invoke-Command2 -ComputerName $computer -ScriptBlock $scriptblock -Credential $Credential -ArgumentList $VersionNumber -ErrorAction Stop
            }
            catch {
                Stop-Function -Continue -Message "Failure" -ErrorRecord $_ -Target $computer -Continue
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Test-DbaSqlPath {
    <#
        .SYNOPSIS
            Tests if file or directory exists from the perspective of the SQL Server service account.

        .DESCRIPTION
            Uses master.dbo.xp_fileexist to determine if a file or directory exists.

        .PARAMETER SqlInstance
            The SQL Server you want to run the test on.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Path
            The Path to test. This can be a file or directory

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.


        .NOTES
            Tags: Path, ServiceAccount
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: Admin access to server (not SQL Services),
            Remoting must be enabled and accessible if $SqlInstance is not local

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaSqlPath

        .EXAMPLE
            Test-DbaSqlPath -SqlInstance sqlcluster -Path L:\MSAS12.MSSQLSERVER\OLAP

            Tests whether the service account running the "sqlcluster" SQL Server instance can access L:\MSAS12.MSSQLSERVER\OLAP. Logs into sqlcluster using Windows credentials.

        .EXAMPLE
            $credential = Get-Credential
            Test-DbaSqlPath -SqlInstance sqlcluster -SqlCredential $credential -Path L:\MSAS12.MSSQLSERVER\OLAP

            Tests whether the service account running the "sqlcluster" SQL Server instance can access L:\MSAS12.MSSQLSERVER\OLAP. Logs into sqlcluster using SQL authentication.

    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Parameter(Mandatory = $true)]
        [object]$Path,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level VeryVerbose -Message "Connecting to $instance." -Target $instance
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_ -Target $instance -Continue
            }
            $counter = [pscustomobject] @{ Value = 0 }
            $groupSize = 100
            $RawPath = $Path
            $Path = [string[]]$Path
            $groups = $Path | Group-Object -Property { [math]::Floor($counter.Value++ / $groupSize) }
            foreach ($g in $groups) {
                $PathsBatch = $g.Group
                $query = @()
                foreach ($p in $PathsBatch) {
                    $query += "EXEC master.dbo.xp_fileexist '$p'"
                }
                $sql = $query -join ';'
                $batchresult = $server.ConnectionContext.ExecuteWithResults($sql)
                if ($Path.Count -eq 1 -and $SqlInstance.Count -eq 1 -and (-not($RawPath -is [array]))) {
                    if ($batchresult.Tables.rows[0] -eq $true -or $batchresult.Tables.rows[1] -eq $true) {
                        return $true
                    }
                    else {
                        return $false
                    }
                }
                else {
                    $i = 0
                    foreach ($r in $batchresult.tables.rows) {
                        $DoesPass = $r[0] -eq $true -or $r[1] -eq $true
                        [pscustomobject]@{
                            SqlInstance  = $server.Name
                            InstanceName = $server.ServiceName
                            ComputerName = $server.ComputerName
                            FilePath     = $PathsBatch[$i]
                            FileExists   = $DoesPass
                            IsContainer  = $r[1] -eq $true
                        }
                        $i += 1
                    }
                }
            }
        }

    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Test-SqlPath
    }
}
function Test-DbaTempDbConfiguration {
    <#
        .SYNOPSIS
            Evaluates tempdb against several rules to match best practices.

        .DESCRIPTION
            Evaluates tempdb against a set of rules to match best practices. The rules are:

            * TF 1118 enabled - Is Trace Flag 1118 enabled (See KB328551).
            * File Count - Does the count of data files in tempdb match the number of logical cores, up to 8?
            * File Growth - Are any files set to have percentage growth? Best practice is all files have an explicit growth value.
            * File Location - Is tempdb located on the C:\? Best practice says to locate it elsewhere.
            * File MaxSize Set (optional) - Do any files have a max size value? Max size could cause tempdb problems if it isn't allowed to grow.

            Other rules can be added at a future date.

        .PARAMETER SqlInstance
            The SQL Server Instance to connect to. SQL Server 2005 and higher are supported.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Detailed
            Output all properties, will be depreciated in 1.0.0 release.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: tempdb, configuration
            Author: Michael Fal (@Mike_Fal), http://mikefal.net
            Based off of Amit Bannerjee's (@banerjeeamit) Get-TempDB function (https://github.com/amitmsft/SqlOnAzureVM/blob/master/Get-TempdbFiles.ps1)

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaTempDbConfiguration

        .EXAMPLE
            Test-DbaTempDbConfiguration -SqlInstance localhost

            Checks tempdb on the localhost machine.

        .EXAMPLE
            Test-DbaTempDbConfiguration -SqlInstance localhost | Select-Object *

            Checks tempdb on the localhost machine. All rest results are shown.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [switch]$Detailed,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter Detailed

        $result = @()
    }
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            #test for TF 1118
            if ($server.VersionMajor -ge 13) {
                $notes = 'SQL Server 2016 has this functionality enabled by default'
                # DBA May have changed setting. May need to check.
                $value = [PSCustomObject]@{
                    ComputerName   = $server.ComputerName
                    InstanceName   = $server.ServiceName
                    SqlInstance    = $server.DomainInstanceName
                    Rule           = 'TF 1118 Enabled'
                    Recommended    = $true
                    CurrentSetting = $true
                }
            }
            else {
                $sql = "DBCC TRACEON (3604);DBCC TRACESTATUS(-1)"
                $tfCheck = $server.Databases['tempdb'].Query($sql)
                $notes = 'KB328551 describes how TF 1118 can benefit performance.'

                $value = [PSCustomObject]@{
                    ComputerName   = $server.ComputerName
                    InstanceName   = $server.ServiceName
                    SqlInstance    = $server.DomainInstanceName
                    Rule           = 'TF 1118 Enabled'
                    Recommended    = $true
                    CurrentSetting = ($tfCheck.TraceFlag -join ',').Contains('1118')
                }
            }

            if ($value.Recommended -ne $value.CurrentSetting -and $null -ne $value.Recommended) {
                $isBestPractice = $false
            }
            else {
                $isBestPractice = $true
            }

            Add-Member -Force -InputObject $value -MemberType NoteProperty -Name IsBestPractice -Value $isBestPractice
            Add-Member -Force -InputObject $value -MemberType NoteProperty -Name Notes -Value $notes
            $result += $value
            Write-Message -Level Verbose -Message "TF 1118 evaluated"

            #get files and log files
            $tempdbFiles = Get-DbaDatabaseFile -SqlInstance $server -Database tempdb
            [array]$dataFiles = $tempdbFiles | Where-Object Type -ne 1
            $logFiles = $tempdbFiles | Where-Object Type -eq 1
            Write-Message -Level Verbose -Message "TempDB file objects gathered"

            $value = [PSCustomObject]@{
                ComputerName   = $server.ComputerName
                InstanceName   = $server.ServiceName
                SqlInstance    = $server.DomainInstanceName
                Rule           = 'File Count'
                Recommended    = [Math]::Min(8, $server.Processors)
                CurrentSetting = $dataFiles.Count
            }

            if ($value.Recommended -ne $value.CurrentSetting -and $null -ne $value.Recommended) {
                $isBestPractice = $false
            }
            else {
                $isBestPractice = $true
            }

            Add-Member -Force -InputObject $value -MemberType NoteProperty -Name IsBestPractice -Value $isBestPractice
            Add-Member -Force -InputObject $value -MemberType NoteProperty -Name Notes -Value 'Microsoft recommends that the number of tempdb data files is equal to the number of logical cores up to 8.'
            $result += $value

            Write-Message -Level Verbose -Message "File counts evaluated."

            #test file growth
            $percData = $dataFiles | Where-Object GrowthType -ne 'KB' | Measure-Object
            $percLog = $logFiles  | Where-Object GrowthType -ne 'KB' | Measure-Object

            $totalCount = $percData.Count + $percLog.Count
            if ($totalCount -gt 0) {
                $totalCount = $true
            }
            else {
                $totalCount = $false
            }

            $value = [PSCustomObject]@{
                ComputerName   = $server.ComputerName
                InstanceName   = $server.ServiceName
                SqlInstance    = $server.DomainInstanceName
                Rule           = 'File Growth in Percent'
                Recommended    = $false
                CurrentSetting = $totalCount
            }

            if ($value.Recommended -ne $value.CurrentSetting -and $null -ne $value.Recommended) {
                $isBestPractice = $false
            }
            else {
                $isBestPractice = $true
            }

            Add-Member -Force -InputObject $value -MemberType NoteProperty -Name IsBestPractice -Value $isBestPractice
            Add-Member -Force -InputObject $value -MemberType NoteProperty -Name Notes -Value 'Set file growth to explicit values, not by percent.'
            $result += $value

            Write-Message -Level Verbose -Message "File growth settings evaluated."
            #test file Location

            $cdata = ($dataFiles | Where-Object PhysicalName -like 'C:*' | Measure-Object).Count + ($logFiles | Where-Object PhysicalName -like 'C:*' | Measure-Object).Count
            if ($cdata -gt 0) {
                $cdata = $true
            }
            else {
                $cdata = $false
            }

            $value = [PSCustomObject]@{
                ComputerName   = $server.ComputerName
                InstanceName   = $server.ServiceName
                SqlInstance    = $server.DomainInstanceName
                Rule           = 'File Location'
                Recommended    = $false
                CurrentSetting = $cdata
            }

            if ($value.Recommended -ne $value.CurrentSetting -and $null -ne $value.Recommended) {
                $isBestPractice = $false
            }
            else {
                $isBestPractice = $true
            }

            Add-Member -Force -InputObject $value -MemberType NoteProperty -Name IsBestPractice -Value $isBestPractice
            Add-Member -Force -InputObject $value -MemberType NoteProperty -Name Notes -Value "Do not place your tempdb files on C:\."
            $result += $value

            Write-Message -Level Verbose -Message "File locations evaluated."

            #Test growth limits
            $growthLimits = ($dataFiles | Where-Object MaxSize -gt 0 | Measure-Object).Count + ($logFiles | Where-Object MaxSize -gt 0 | Measure-Object).Count
            if ($growthLimits -gt 0) {
                $growthLimits = $true
            }
            else {
                $growthLimits = $false
            }

            $value = [PSCustomObject]@{
                ComputerName   = $server.ComputerName
                InstanceName   = $server.ServiceName
                SqlInstance    = $server.DomainInstanceName
                Rule           = 'File MaxSize Set'
                Recommended    = $false
                CurrentSetting = $growthLimits
            }

            if ($value.Recommended -ne $value.CurrentSetting -and $null -ne $value.Recommended) {
                $isBestPractice = $false
            }
            else {
                $isBestPractice = $true
            }

            Add-Member -Force -InputObject $value -MemberType NoteProperty -Name IsBestPractice -Value $isBestPractice
            Add-Member -Force -InputObject $value -MemberType NoteProperty -Name Notes -Value "Consider setting your tempdb files to unlimited growth."
            $result += $value

            Write-Message -Level Verbose -Message "MaxSize values evaluated."

            Select-DefaultView -InputObject $result -Property ComputerName, InstanceName, SqlInstance, Rule, Recommended, IsBestPractice
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Test-SqlTempDbConfiguration
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Test-DbaWindowsLogin {
    <#
        .SYNOPSIS
            Test-DbaWindowsLogin finds any logins on SQL instance that are AD logins with either disabled AD user accounts or ones that no longer exist

        .DESCRIPTION
            The purpose of this function is to find SQL Server logins that are used by active directory users that are either disabled or removed from the domain. It allows you to keep your logins accurate and up to date by removing accounts that are no longer needed.

        .PARAMETER SqlInstance
            The SQL Server instance you're checking logins on. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Login
            Specifies a list of logins to include in the results. Options for this list are auto-populated from the server.

        .PARAMETER ExcludeLogin
            Specifies a list of logins to exclude from the results. Options for this list are auto-populated from the server.

        .PARAMETER FilterBy
            Specifies the object types to return. By default, both Logins and Groups are returned. Valid options for this parameter are 'GroupsOnly' and 'LoginsOnly'.

        .PARAMETER IgnoreDomains
            Specifies a list of Active Directory domains to ignore. By default, all domains in the forest as well as all trusted domains are traversed.

        .PARAMETER Detailed
            Output all properties, will be depreciated in 1.0.0 release.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Login, Security
            Author: Stephen Bennett: https://sqlnotesfromtheunderground.wordpress.com/
            Author: Chrissy LeMaire (@cl), netnerds.net

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaWindowsLogin

        .EXAMPLE
            Test-DbaWindowsLogin -SqlInstance Dev01

            Tests all logins in the current Active Directory domain that are either disabled or do not exist on the SQL Server instance Dev01

        .EXAMPLE
            Test-DbaWindowsLogin -SqlInstance Dev01 -FilterBy GroupsOnly | Select-Object -Property *

            Tests all Active Directory groups that have logins on Dev01, and shows all information for those logins

        .EXAMPLE
            Test-DbaWindowsLogin -SqlInstance Dev01 -IgnoreDomains testdomain

            Tests all Domain logins excluding any that are from the testdomain

    #>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Login,
        [object[]]$ExcludeLogin,
        [ValidateSet("LoginsOnly", "GroupsOnly", "None")]
        [string]$FilterBy = "None",
        [string[]]$IgnoreDomains,
        [switch]$Detailed,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter Detailed

        if ($IgnoreDomains) {
            $IgnoreDomainsNormalized = $IgnoreDomains.ToUpper()
            Write-Message -Message ("Excluding logins for domains " + ($IgnoreDomains -join ',')) -Level Verbose
        }

        $mappingRaw = @{
            'SCRIPT'                                 = 1
            'ACCOUNTDISABLE'                         = 2
            'HOMEDIR_REQUIRED'                       = 8
            'LOCKOUT'                                = 16
            'PASSWD_NOTREQD'                         = 32
            'PASSWD_CANT_CHANGE'                     = 64
            'ENCRYPTED_TEXT_PASSWORD_ALLOWED'        = 128
            'TEMP_DUPLICATE_ACCOUNT'                 = 256
            'NORMAL_ACCOUNT'                         = 512
            'INTERDOMAIN_TRUST_ACCOUNT'              = 2048
            'WORKSTATION_TRUST_ACCOUNT'              = 4096
            'SERVER_TRUST_ACCOUNT'                   = 8192
            'DONT_EXPIRE_PASSWD'                     = 65536
            'MNS_LOGON_ACCOUNT'                      = 131072
            'SMARTCARD_REQUIRED'                     = 262144
            'TRUSTED_FOR_DELEGATION'                 = 524288
            'NOT_DELEGATED'                          = 1048576
            'USE_DES_KEY_ONLY'                       = 2097152
            'DONT_REQUIRE_PREAUTH'                   = 4194304
            'PASSWORD_EXPIRED'                       = 8388608
            'TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION' = 16777216
            'NO_AUTH_DATA_REQUIRED'                  = 33554432
            'PARTIAL_SECRETS_ACCOUNT'                = 67108864
        }
    }
    process {
        foreach ($instance in $SqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
                Write-Message -Message "Connected to: $instance." -Level Verbose
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }


            # we can only validate AD logins
            $allWindowsLoginsGroups = $server.Logins | Where-Object { $_.LoginType -in ('WindowsUser', 'WindowsGroup') }

            # we cannot validate local users
            $allWindowsLoginsGroups = $allWindowsLoginsGroups | Where-Object { $_.Name.StartsWith("NT ") -eq $false -and $_.Name.StartsWith($server.ComputerName) -eq $false -and $_.Name.StartsWith("BUILTIN") -eq $false }
            if ($Login) {
                $allWindowsLoginsGroups = $allWindowsLoginsGroups | Where-Object Name -In $Login
            }
            if ($ExcludeLogin) {
                $allWindowsLoginsGroups = $allWindowsLoginsGroups | Where-Object Name -NotIn $ExcludeLogin
            }
            switch ($FilterBy) {
                "LoginsOnly" {
                    Write-Message -Message "Search restricted to logins." -Level Verbose
                    $windowsLogins = $allWindowsLoginsGroups | Where-Object LoginType -eq 'WindowsUser'
                }
                "GroupsOnly" {
                    Write-Message -Message "Search restricted to groups." -Level Verbose
                    $windowsGroups = $allWindowsLoginsGroups | Where-Object LoginType -eq 'WindowsGroup'
                }
                "None" {
                    Write-Message -Message "Search both logins and groups." -Level Verbose
                    $windowsLogins = $allWindowsLoginsGroups | Where-Object LoginType -eq 'WindowsUser'
                    $windowsGroups = $allWindowsLoginsGroups | Where-Object LoginType -eq 'WindowsGroup'
                }
            }
            foreach ($login in $windowsLogins) {
                $adLogin = $login.Name
                $loginSid = $login.Sid -join ''
                $domain, $username = $adLogin.Split("\")
                if ($domain.ToUpper() -in $IgnoreDomainsNormalized) {
                    Write-Message -Message "Skipping Login $adLogin." -Level Verbose
                    continue
                }
                Write-Message -Message "Parsing Login $adLogin." -Level Verbose
                $exists = $false
                try {
                    $u = Get-DbaADObject -ADObject $adLogin -Type User -EnableException
                    if ($null -eq $u -and $adLogin -like '*$'){
                        Write-Message -Message "Parsing Login as computer" -Level Verbose
                        $u = Get-DbaADObject -ADObject $adLogin -Type Computer -EnableException
                        $adType = 'Computer'
                    }
                    else {
                        $adType = 'User'
                    }
                    $foundUser = $u.GetUnderlyingObject()
                    $foundSid = $foundUser.ObjectSid.Value -join ''
                    if ($foundUser) {
                        $exists = $true
                    }
                    if ($foundSid -ne $loginSid) {
                        Write-Message -Message "SID mismatch detected for $adLogin." -Level Warning
                        Write-Message -Message "SID mismatch detected for $adLogin (MSSQL: $loginSid, AD: $foundSid)." -Level Debug
                        $exists = $false
                    }
                }
                catch {
                    Write-Message -Message "AD Searcher Error for $username." -Level Warning
                }

                $uac = $foundUser.Properties.UserAccountControl

                $additionalProps = @{
                    AccountNotDelegated               = $null
                    AllowReversiblePasswordEncryption = $null
                    CannotChangePassword              = $null
                    PasswordExpired                   = $null
                    LockedOut                         = $null
                    Enabled                           = $null
                    PasswordNeverExpires              = $null
                    PasswordNotRequired               = $null
                    SmartcardLogonRequired            = $null
                    TrustedForDelegation              = $null
                }
                if ($uac) {
                    $additionalProps = @{
                        AccountNotDelegated               = [bool]($uac.Value -band $mappingRaw['NOT_DELEGATED'])
                        AllowReversiblePasswordEncryption = [bool]($uac.Value -band $mappingRaw['ENCRYPTED_TEXT_PASSWORD_ALLOWED'])
                        CannotChangePassword              = [bool]($uac.Value -band $mappingRaw['PASSWD_CANT_CHANGE'])
                        PasswordExpired                   = [bool]($uac.Value -band $mappingRaw['PASSWORD_EXPIRED'])
                        LockedOut                         = [bool]($uac.Value -band $mappingRaw['LOCKOUT'])
                        Enabled                           = !($uac.Value -band $mappingRaw['ACCOUNTDISABLE'])
                        PasswordNeverExpires              = [bool]($uac.Value -band $mappingRaw['DONT_EXPIRE_PASSWD'])
                        PasswordNotRequired               = [bool]($uac.Value -band $mappingRaw['PASSWD_NOTREQD'])
                        SmartcardLogonRequired            = [bool]($uac.Value -band $mappingRaw['SMARTCARD_REQUIRED'])
                        TrustedForDelegation              = [bool]($uac.Value -band $mappingRaw['TRUSTED_FOR_DELEGATION'])
                        UserAccountControl                = $uac.Value
                    }
                }
                $rtn = [PSCustomObject]@{
                    Server                            = $server.DomainInstanceName
                    Domain                            = $domain
                    Login                             = $username
                    Type                              = $adType
                    Found                             = $exists
                    DisabledInSQLServer               = $login.IsDisabled
                    AccountNotDelegated               = $additionalProps.AccountNotDelegated
                    AllowReversiblePasswordEncryption = $additionalProps.AllowReversiblePasswordEncryption
                    CannotChangePassword              = $additionalProps.CannotChangePassword
                    PasswordExpired                   = $additionalProps.PasswordExpired
                    LockedOut                         = $additionalProps.LockedOut
                    Enabled                           = $additionalProps.Enabled
                    PasswordNeverExpires              = $additionalProps.PasswordNeverExpires
                    PasswordNotRequired               = $additionalProps.PasswordNotRequired
                    SmartcardLogonRequired            = $additionalProps.SmartcardLogonRequired
                    TrustedForDelegation              = $additionalProps.TrustedForDelegation
                    UserAccountControl                = $additionalProps.UserAccountControl
                }

                Select-DefaultView -InputObject $rtn -ExcludeProperty AccountNotDelegated, AllowReversiblePasswordEncryption, CannotChangePassword, PasswordNeverExpires, SmartcardLogonRequired, TrustedForDelegation, UserAccountControl

            }

            foreach ($login in $windowsGroups) {
                $adLogin = $login.Name
                $loginSid = $login.Sid -join ''
                $domain, $groupName = $adLogin.Split("\")
                if ($domain.ToUpper() -in $IgnoreDomainsNormalized) {
                    Write-Message -Message "Skipping Login $adLogin." -Level Verbose
                    continue
                }
                Write-Message -Message "Parsing Login $adLogin on $server." -Level Verbose
                $exists = $false
                try {
                    $u = Get-DbaADObject -ADObject $adLogin -Type Group -EnableException
                    $foundUser = $u.GetUnderlyingObject()
                    $foundSid = $foundUser.objectSid.Value -join ''
                    if ($foundUser) {
                        $exists = $true
                    }
                    if ($foundSid -ne $loginSid) {
                        Write-Message -Message "SID mismatch detected for $adLogin." -Level Warning
                        Write-Message -Message "SID mismatch detected for $adLogin (MSSQL: $loginSid, AD: $foundSid)." -Level Debug
                        $exists = $false
                    }
                }
                catch {
                    Write-Message -Message "AD Searcher Error for $groupName on $server" -Level Warning
                }
                $rtn = [PSCustomObject]@{
                    Server                            = $server.DomainInstanceName
                    Domain                            = $domain
                    Login                             = $groupName
                    Type                              = "Group"
                    Found                             = $exists
                    DisabledInSQLServer               = $login.IsDisabled
                    AccountNotDelegated               = $null
                    AllowReversiblePasswordEncryption = $null
                    CannotChangePassword              = $null
                    PasswordExpired                   = $null
                    LockedOut                         = $null
                    Enabled                           = $null
                    PasswordNeverExpires              = $null
                    PasswordNotRequired               = $null
                    SmartcardLogonRequired            = $null
                    TrustedForDelegation              = $null
                    UserAccountControl                = $null
                }

                Select-DefaultView -InputObject $rtn -ExcludeProperty AccountNotDelegated, AllowReversiblePasswordEncryption, CannotChangePassword, PasswordNeverExpires, SmartcardLogonRequired, TrustedForDelegation, UserAccountControl

            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Test-DbaValidLogin
    }
}
function Uninstall-DbaWatchUpdate {
    <#
        .SYNOPSIS
            Removes the scheduled task created for Watch-DbaUpdate by Install-DbaWatchUpdate so that notifications no longer pop up.

        .DESCRIPTION
            Removes the scheduled task created for Watch-DbaUpdate by Install-DbaWatchUpdate so that notifications no longer pop up.

        .NOTES
            Tags: JustForFun, Module
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Uninstall-DbaWatchUpdate

        .EXAMPLE
            Uninstall-DbaWatchUpdate

            Removes the scheduled task created by Install-DbaWatchUpdate.
    #>
    process {
        if (([Environment]::OSVersion).Version.Major -lt 10) {
            Write-Warning "This command only supports Windows 10 and higher."
            return
        }

        <# Does not utilize message system because of script block #>
        $script = {
            try {
                $task = Get-ScheduledTask -TaskName "dbatools version check" -ErrorAction SilentlyContinue

                if ($null -eq $task) {
                    Write-Warning "Task doesn't exist. Skipping removal."
                }
                else {
                    Write-Output "Removing watchupdate.xml."
                    $file = "$env:LOCALAPPDATA\dbatools\watchupdate.xml"
                    Remove-Item $file -ErrorAction SilentlyContinue

                    Write-Output "Removing Scheduled Task 'dbatools version check'."
                    $task | Unregister-ScheduledTask -Confirm:$false -ErrorAction Stop

                    Write-Output "Task removed"

                    Start-Sleep -Seconds 2
                }
            }
            catch {
                Write-Warning "Task could not be deleted. Please remove 'dbatools version check' manually."
            }
        }
        # Needs admin credentials to remove the task because of the way it was setup

        $task = Get-ScheduledTask -TaskName "dbatools version check" -ErrorAction SilentlyContinue

        if ($null -eq $task) {
            Write-Warning "dbatools update watcher is not installed."
            return
        }

        if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
            Write-Warning "Removal of this scheduled task requires elevated permissions."
            Start-Process powershell -Verb runAs -ArgumentList Uninstall-DbaWatchUpdate -Wait
        }
        else {
            Invoke-Command -ScriptBlock $script
        }

        Write-Output "All done!"
    }
}
function Update-DbaPowerBiDataSource {
    <#
        .SYNOPSIS
            Converts the results of dbatools commands for our PowerBI Dashboard related commands. This command is specific to our toolset and not a general Power BI command.

        .DESCRIPTION
            Converts the results of dbatools commands for our PowerBI Dashboard related commands. This command is specific to our toolset and not a general Power BI command.

        .PARAMETER InputObject
            Enables piping

        .PARAMETER Path
            The directory to store your files. "C:\windows\temp\dbatools\" by default

        .PARAMETER Enviornment
            Tag your data with an enviornment. Defaults to "Default"

        .PARAMETER Append
            Don't delete previous default data sources.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .EXAMPLE
            Get-DbaPfDataCollectorSet -ComputerName sql2016 | Invoke-DbaPfRelog -AllowClobber | Update-DbaPowerBiDataSource | Start-DbaPowerBi

            Converts the results of the performance monitor data source and stores it in the appropriate directory then launches our Power BI dashboard

    #>
    [CmdletBinding()]
    param (
        [parameter(ValueFromPipeline, Mandatory)]
        [pscustomobject]$InputObject,
        [string]$Path = "$env:windir\temp\dbatools",
        [string]$Enviornment = "Default",
        [switch]$Append,
        [switch]$EnableException
    )
    begin {
        if ($Environment -ne "Default" -and -not $Append) {
            $null = Remove-Item "$Path\*Default*.*sv" -ErrorAction SilentlyContinue
        }
        $orginalpath = $Path
    }
    process {
        ++$i

        if ($InputObject.RelogFile) {
            $Path = "$orginalpath\perfmon"
        }
        else {
            $Path = "$orginalpath\xevents"
        }

        try {
            if (-not (Test-Path -Path $Path)) {
                $null = New-Item -ItemType Directory -Path $Path -ErrorAction Stop
            }
        }
        catch {
            Stop-Function -Message "Failure" -Exception $_
            return
        }

        $extension = $InputObject.Extension.TrimStart(".")
        $basename = "dbatools_$i"
        if ($InputObject.TagFilter) {
            $basename = "$basename`_$($InputObject.TagFilter -join "_")"
        }

        if ($Enviornment) {
            $basename = "$basename`_$Enviornment"
        }

        $filename = "$basename.$extension"

        try {
            Write-Message -Level Verbose -Message "Writing $filename to $path"
            $inputObject | Copy-Item -Destination "$path\$filename"
            Get-ChildItem "$path\$filename"
        }
        catch {
            Stop-Function -Message "Failure" -ErrorRecord $_
            return
        }
    }
    end {
        if ($InputObject -isnot [System.IO.FileInfo] -and $InputObject -isnot [System.IO.DirectoryInfo]) {
            Stop-Function -Message "Invalid input"
            return
        }
    }
}
function Update-DbaSqlServiceAccount {
    <#
        .SYNOPSIS
            Changes service account (or just its password) of the SQL Server service.

        .DESCRIPTION
            Reconfigure the service account or update the password of the specified SQL Server service. The service will be restarted in the event of changing the account.

        .PARAMETER ComputerName
            The SQL Server (or server in general) that you're connecting to. This command handles named instances.

        .PARAMETER Credential
            Windows Credential with permission to log on to the server running the SQL instance

        .PARAMETER InputObject
            A collection of services. Basically, any object that has ComputerName and ServiceName properties. Can be piped from Get-DbaSqlService.

        .PARAMETER ServiceName
            A name of the service on which the action is performed. E.g. MSSQLSERVER or SqlAgent$INSTANCENAME

        .PARAMETER ServiceCredential
            Windows Credential object under which the service will be setup to run. Cannot be used with -Username. For local service accounts use one of the following usernames with empty password:
            LOCALSERVICE
            NETWORKSERVICE
            LOCALSYSTEM

        .PARAMETER OldPassword
            An old password of the service account. Optional when run under local admin privileges.

        .PARAMETER NewPassword
            New password of the service account. The function will ask for a password if not specified. MSAs and local system accounts will ignore the password.

        .PARAMETER Username
            Username of the service account. Cannot be used with -ServiceCredential. For local service accounts use one of the following usernames omitting the -Password parameter:
            LOCALSERVICE
            NETWORKSERVICE
            LOCALSYSTEM

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Service, SqlServer, Instance, Connect
            Author: Kirill Kravtsov (@nvarscar)

            Requires Local Admin rights on destination computer(s).

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            $NewPassword = ConvertTo-SecureString 'Qwerty1234' -AsPlainText -Force
            Update-DbaSqlServiceAccount -ComputerName sql1 -ServiceName 'MSSQL$MYINSTANCE' -Password $NewPassword

            Changes the current service account's password of the service MSSQL$MYINSTANCE to 'Qwerty1234'

        .EXAMPLE
            $cred = Get-Credential
            Get-DbaSqlService sql1 -Type Engine,Agent -Instance MYINSTANCE | Update-DbaSqlServiceAccount -ServiceCredential $cred

            Requests credentials from the user and configures them as a service account for the SQL Server engine and agent services of the instance sql1\MYINSTANCE

        .EXAMPLE
            Update-DbaSqlServiceAccount -ComputerName sql1,sql2 -ServiceName 'MSSQLSERVER','SQLSERVERAGENT' -Username NETWORKSERVICE

            Configures SQL Server engine and agent services on the machines sql1 and sql2 to run under Network Service system user.

        .EXAMPLE
            Get-DbaSqlService sql1 -Type Engine -Instance MSSQLSERVER | Update-DbaSqlServiceAccount -Username 'MyDomain\sqluser1'

            Configures SQL Server engine service on the machine sql1 to run under 'MyDomain\sqluser1'. Will request user to input the account password.
    #>
    [CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = "ServiceName" )]
    param (
        [parameter(ParameterSetName = "ServiceName")]
        [Alias("cn", "host", "Server")]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [parameter(ValueFromPipeline = $true, Mandatory = $true, ParameterSetName = "InputObject")]
        [Alias("ServiceCollection")]
        [object[]]$InputObject,
        [parameter(ParameterSetName = "ServiceName", Position = 1, Mandatory = $true)]
        [Alias("Name", "Service")]
        [string[]]$ServiceName,
        [Alias("User")]
        [string]$Username,
        [PSCredential]$ServiceCredential,
        [securestring]$OldPassword = (New-Object System.Security.SecureString),
        [Alias("Password")]
        [securestring]$NewPassword = (New-Object System.Security.SecureString),
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        $svcCollection = @()
        $scriptAccountChange = {
            $service = $wmi.Services[$args[0]]
            $service.SetServiceAccount($args[1], $args[2])
            $service.Alter()
        }
        $scriptPasswordChange = {
            $service = $wmi.Services[$args[0]]
            $service.ChangePassword($args[1], $args[2])
            $service.Alter()
        }
        #Check parameters
        if ($Username) {
            $actionType = 'Account'
            if ($ServiceCredential) {
                Stop-Function -EnableException $EnableException -Message "You cannot specify both -UserName and -ServiceCredential parameters" -Category InvalidArgument
                return
            }
            #System logins should not have a domain name, whitespaces or passwords
            $trimmedUsername = (Split-Path $Username -Leaf).Trim().Replace(' ', '')
            #Request password input if password was not specified and account is not MSA or system login
            if ($NewPassword.Length -eq 0 -and $PSBoundParameters.Keys -notcontains 'NewPassword' -and $trimmedUsername -notin 'NETWORKSERVICE', 'LOCALSYSTEM', 'LOCALSERVICE' -and $Username.EndsWith('$') -eq $false -and $Username.StartsWith('NT Service\') -eq $false) {
                $NewPassword = Read-Host -Prompt "Input new password for account $UserName" -AsSecureString
                $NewPassword2 = Read-Host -Prompt "Repeat password" -AsSecureString
                if ((New-Object System.Management.Automation.PSCredential ("user", $NewPassword)).GetNetworkCredential().Password -ne `
                    (New-Object System.Management.Automation.PSCredential ("user", $NewPassword2)).GetNetworkCredential().Password) {
                    Stop-Function -Message "Passwords do not match" -Category InvalidArgument -EnableException $EnableException
                    return
                }
            }
            $currentCredential = New-Object System.Management.Automation.PSCredential ($Username, $NewPassword)
        }
        elseif ($ServiceCredential) {
            $actionType = 'Account'
            $currentCredential = $ServiceCredential
        }
        else {
            $actionType = 'Password'
        }
        if ($actionType -eq 'Account') {
            #System logins should not have a domain name, whitespaces or passwords
            $credUserName = (Split-Path $currentCredential.UserName -Leaf).Trim().Replace(' ', '')
            #Check for system logins and replace the Credential object to simplify passing localsystem-like login names
            if ($credUserName -in 'NETWORKSERVICE', 'LOCALSYSTEM', 'LOCALSERVICE') {
                $currentCredential = New-Object System.Management.Automation.PSCredential ($credUserName, (New-Object System.Security.SecureString))
            }
        }
    }
    process {
        if ($PsCmdlet.ParameterSetName -match 'ServiceName') {
            foreach ($Computer in $ComputerName.ComputerName) {
                $Server = Resolve-DbaNetworkName -ComputerName $Computer -Credential $credential
                if ($Server.ComputerName) {
                    foreach ($service in $ServiceName) {
                        $svcCollection += [psobject]@{
                            ComputerName = $server.ComputerName
                            ServiceName  = $service
                        }
                    }
                }
                else {
                    Stop-Function -EnableException $EnableException -Message "Failed to connect to $Computer" -Continue
                }
            }
        }
        elseif ($PsCmdlet.ParameterSetName -match 'InputObject') {
            foreach ($service in $InputObject) {
                $Server = Resolve-DbaNetworkName -ComputerName $service.ComputerName -Credential $credential
                if ($Server.ComputerName) {
                    $svcCollection += [psobject]@{
                        ComputerName = $Server.ComputerName
                        ServiceName  = $service.ServiceName
                    }
                }
                else {
                    Stop-Function -EnableException $EnableException -Message "Failed to connect to $($service.ComputerName)" -Continue
                }
            }
        }

    }
    end {
        foreach ($svc in $svcCollection) {
            if ($serviceObject = Get-DbaSqlService -ComputerName $svc.ComputerName -ServiceName $svc.ServiceName -Credential $Credential -EnableException:$EnableException) {
                $outMessage = $outStatus = $agent = $null
                if ($actionType -eq 'Password' -and $NewPassword.Length -eq 0) {
                    $currentPassword = Read-Host -Prompt "New password for $($serviceObject.StartName) ($($svc.ServiceName) on $($svc.ComputerName))" -AsSecureString
                    $currentPassword2 = Read-Host -Prompt "Repeat password" -AsSecureString
                    if ((New-Object System.Management.Automation.PSCredential ("user", $currentPassword)).GetNetworkCredential().Password -ne `
                        (New-Object System.Management.Automation.PSCredential ("user", $currentPassword2)).GetNetworkCredential().Password) {
                        Stop-Function -Message "Passwords do not match. This service will not be updated" -Category InvalidArgument -EnableException $EnableException -Continue
                    }
                }
                else {
                    $currentPassword = $NewPassword
                }
                if ($serviceObject.ServiceType -eq 'Engine') {
                    #Get SQL Agent running status
                    $agent = Get-DbaSqlService -ComputerName $svc.ComputerName -Type Agent -InstanceName $serviceObject.InstanceName
                }
                if ($PsCmdlet.ShouldProcess($serviceObject, "Changing account information for service $($svc.ServiceName) on $($svc.ComputerName)")) {
                    try {
                        if ($actionType -eq 'Account') {
                            Write-Message -Level Verbose -Message "Attempting an account change for service $($svc.ServiceName) on $($svc.ComputerName)"
                            $null = Invoke-ManagedComputerCommand -ComputerName $svc.ComputerName -Credential $Credential -ScriptBlock $scriptAccountChange -ArgumentList @($svc.ServiceName, $currentCredential.UserName, $currentCredential.GetNetworkCredential().Password) -EnableException:$EnableException
                            $outMessage = "The login account for the service has been successfully set."
                        }
                        elseif ($actionType -eq 'Password') {
                            Write-Message -Level Verbose -Message "Attempting a password change for service $($svc.ServiceName) on $($svc.ComputerName)"
                            $null = Invoke-ManagedComputerCommand -ComputerName $svc.ComputerName -Credential $Credential -ScriptBlock $scriptPasswordChange -ArgumentList @($svc.ServiceName, (New-Object System.Management.Automation.PSCredential ("user", $OldPassword)).GetNetworkCredential().Password, (New-Object System.Management.Automation.PSCredential ("user", $currentPassword)).GetNetworkCredential().Password) -EnableException:$EnableException
                            $outMessage = "The password has been successfully changed."
                        }
                        $outStatus = 'Successful'
                    }
                    catch {
                        $outStatus = 'Failed'
                        $outMessage = $_.Exception.Message
                        Write-Message -Level Warning -Message $_.Exception.Message -EnableException $EnableException.ToBool()
                    }
                }
                else {
                    $outStatus = 'Successful'
                    $outMessage = 'No changes made - running in -WhatIf mode.'
                }
                if ($serviceObject.ServiceType -eq 'Engine' -and $actionType -eq 'Account' -and $outStatus -eq 'Successful' -and $agent.State -eq 'Running') {
                    #Restart SQL Agent after SQL Engine has been restarted
                    if ($PsCmdlet.ShouldProcess($serviceObject, "Starting SQL Agent after Engine account change on $($svc.ComputerName)")) {
                        $res = Start-DbaSqlService -ComputerName $svc.ComputerName -Type Agent -InstanceName $serviceObject.InstanceName
                        if ($res.Status -ne 'Successful') {
                            Write-Message -Level Warning -Message "Failed to restart SQL Agent after changing credentials. $($res.Message)"
                        }
                    }
                }
                $serviceObject = Get-DbaSqlService -ComputerName $svc.ComputerName -ServiceName $svc.ServiceName -Credential $Credential -EnableException:$EnableException
                Add-Member -Force -InputObject $serviceObject -NotePropertyName Message -NotePropertyValue $outMessage
                Add-Member -Force -InputObject $serviceObject -NotePropertyName Status -NotePropertyValue $outStatus
                Select-DefaultView -InputObject $serviceObject -Property ComputerName, ServiceName, State, StartName, Status, Message
            }
            Else {
                Stop-Function -Message "The service $($svc.ServiceName) has not been found on $($svc.ComputerName)" -EnableException $EnableException -Continue
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Update-Dbatools {
    <#
        .SYNOPSIS
            Exported function. Updates dbatools. Deletes current copy and replaces it with freshest copy.

        .DESCRIPTION
            Exported function. Updates dbatools. Deletes current copy and replaces it with freshest copy.

        .PARAMETER Development
            If this switch is enabled, the current development branch will be installed. By default, the latest official release is installed.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .NOTES
            Tags: Module
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Update-DbaTools

        .EXAMPLE
            Update-Dbatools

            Updates dbatools. Deletes current copy and replaces it with freshest copy.

        .EXAMPLE
            Update-Dbatools -dev

            Updates dbatools to the current development branch. Deletes current copy and replaces it with latest from github.
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param(
        [parameter(Mandatory = $false)]
        [Alias("dev", "devbranch")]
        [switch]$Development,
        [Alias('Silent')]
        [switch]$EnableException
    )
    $MyModuleBase = [SqlCollaborative.Dbatools.dbaSystem.SystemHost]::ModuleBase
    $InstallScript = join-path -path $MyModuleBase -ChildPath "install.ps1";
    if ($Development) {
        Write-Message -Level Verbose -Message "Installing dev/beta channel via $Installscript.";
        if ($PSCmdlet.ShouldProcess("development branch", "Updating dbatools")) {
            & $InstallScript -beta;
        }
    }
    else {
        Write-Message -Level Verbose -Message "Installing release version via $Installscript."
        if ($PSCmdlet.ShouldProcess("release branch", "Updating dbatools")) {
            & $InstallScript;
        }
    }
}
function Watch-DbaDbLogin {
    <#
        .SYNOPSIS
            Tracks SQL Server logins: which host they came from, what database they're using, and what program is being used to log in.

        .DESCRIPTION
            Watch-DbaDbLogin uses SQL Server DMVs to track logins into a SQL Server table. This is helpful when you need to migrate a SQL Server and update connection strings, but have inadequate documentation on which servers/applications are logging into your SQL instance.

            Running this script every 5 minutes for a week should give you a sufficient idea about database and login usage.

        .PARAMETER SqlInstance
            The SQL Server that stores the Watch database.

        .PARAMETER SqlCms
            Specifies a Central Management Server to query for a list of servers to watch.

        .PARAMETER ServersFromFile
            Specifies a file containing a list of servers to watch. This file must contain one server name per line.

        .PARAMETER Database
            The name of the Watch database.

        .PARAMETER Table
            The name of the Watch table. By default, this is DbaTools-WatchDbLogins.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Login
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on all SQL Servers for the most accurate results

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Watch-DbaDbLogin

        .EXAMPLE
            Watch-DbaDbLogin -SqlInstance sqlserver -SqlCms SqlCms1

            A list of all database instances within the Central Management Server SqlCms1 is generated. Using this list, the script enumerates all the processes and gathers login information and saves it to the table Dblogins in the DatabaseLogins database on SQL Server sqlserver.

        .EXAMPLE
            Watch-DbaDbLogin -SqlInstance sqlcluster -Database CentralAudit -ServersFromFile .\sqlservers.txt

            A list of servers is gathered from the file sqlservers.txt in the current directory. Using this list, the script enumerates all the processes and gathers login information and saves it to the table Dblogins in the CentralAudit database on SQL Server sqlcluster.

        .EXAMPLE
            Watch-DbaDbLogin -SqlInstance sqlserver -SqlCms SqlCms1 -SqlCredential $cred

            A list of servers is generated using database instance names within the SQL2014Clusters group on the Central Management Server SqlCms1. Using this list, the script enumerates all the processes and gathers login information and saves it to the table Dblogins in the DatabaseLogins database on sqlserver.

    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    param (
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance]$SqlInstance,
        [object]$Database,
        [string]$Table = "DbaTools-WatchDbLogins",
        [PSCredential]$SqlCredential,

        # Central Management Server
        [string]$SqlCms,

        # File with one server per line
        [string]$ServersFromFile,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        if (Test-Bound 'SqlCms', 'ServersFromFile' -Not) {
            Stop-Function -Message "You must specify a server list source using -SqlCms or -ServersFromFile"
            return
        }

        Write-Message -Level Verbose -Message "Connecting to $SqlInstance"
        try {
            $serverDest = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $SqlInstance -Continue
        }

        $systemdbs = "master", "msdb", "model", "tempdb"
        $excludedPrograms = "Microsoft SQL Server Management Studio - Query", "SQL Management"

        <#
            Get servers to query from Central Management Server or File
        #>
        if ($SqlCms) {
            try {
                $servers = Get-DbaRegisteredServerName -SqlInstance $SqlCms -SqlCredential $SqlCredential -EnableException
            }
            catch {
                Stop-Function -Message "The CMS server, $SqlCms, was not accessible." -Target $SqlCms -ErrorRecord $_
                return
            }
        }
        if (Test-Bound 'ServersFromFile') {
            if (Test-Path $ServersFromFile) {
                $servers = Get-Content $ServersFromFile
            }
            else {
                Stop-Function -Message "$ServersFromFile was not found." -Target $ServersFromFile
                return
            }
        }

        <#
            Process each server
        #>
        foreach ($instance in $servers) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if (!(Test-SqlSa $server)) {
                Write-Warning "Not a sysadmin on $instance, resultset would be underwhelming. Skipping.";
                continue
            }

            $sql = "
            SELECT
                s.login_time AS [LoginTime]
                , s.login_name AS [Login]
                , ISNULL(s.host_name,N'') AS [Host]
                , ISNULL(s.program_name,N'') AS [Program]
                , ISNULL(r.database_id,N'') AS [DatabaseId]
                , ISNULL(DB_NAME(r.database_id),N'') AS [Database]
                , CAST(~s.is_user_process AS bit) AS [IsSystem]
                , CaptureTime = (SELECT GETDATE())
            FROM sys.dm_exec_sessions AS s
            LEFT OUTER JOIN sys.dm_exec_requests AS r
                ON r.session_id = s.session_id"
            Write-Message -Level Debug -Message $sql

            $procs = $server.Query($sql) | Where-Object { $_.Host -ne $instance.ComputerName -and ![string]::IsNullOrEmpty($_.Host) }
            $procs = $procs | Where-Object { $systemdbs -notcontains $_.Database -and $excludedPrograms -notcontains $_.Program }

            if ($procs.Count -gt 0) {
                $procs | Select-Object @{Label = "ComputerName"; Expression = {$server.ComputerName}}, @{Label = "InstanceName"; Expression = {$server.ServiceName}}, @{Label = "SqlInstance"; Expression = {$server.DomainInstanceName}}, LoginTime, Login, Host, Program, DatabaseId, Database, IsSystem, CaptureTime | ConvertTo-DbaDataTable | Write-DbaDataTable -SqlInstance $serverDest -Database $Database -Table $Table -AutoCreateTable

                Write-Output "Added process information for $instance to datatable."
            }
            else {
                Write-message -Level Verbose -Message "No data returned for $instance."
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Watch-SqlDbLogin
    }
}
function Watch-DbaUpdate {
    <#
        .SYNOPSIS
            Just for fun - checks the PowerShell Gallery every 1 hour for updates to dbatools. Notifies once per release.

        .DESCRIPTION
            Just for fun - checks the PowerShell Gallery every 1 hour for updates to dbatools. Notifies once max per release.

            Anyone know how to make it clickable so that it opens an URL?

        .NOTES
            Tags: JustForFun, Module
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Watch-DbaUpdate

        .EXAMPLE
            Watch-DbaUpdate

            Watches the gallery for updates to dbatools.
    #>
    [cmdletbinding()]
    param()
    process {
        if (([Environment]::OSVersion).Version.Major -lt 10) {
            Write-Warning "This command only supports Windows 10 and higher."
            return
        }

        if ($null -eq (Get-ScheduledTask -TaskName "dbatools version check" -ErrorAction SilentlyContinue)) {
            Install-DbaWatchUpdate
        }

        # leave this in for the scheduled task
        $module = Get-Module -Name dbatools

        if (-not $module) {
            Import-Module dbatools
            $module = Get-Module -Name dbatools
        }

        $galleryVersion = (Find-Module -Name dbatools -Repository PSGallery).Version
        $localVersion = $module.Version

        if ($galleryVersion -le $localVersion) { return }

        $file = "$env:LOCALAPPDATA\dbatools\watchupdate.xml"

        $new = [PSCustomObject]@{
            NotifyVersion = $galleryVersion
        }

        # now that notifications stay until they are checked, we just have to keep
        # track of the last version we notified about

        if (Test-Path $file) {
            $old = Import-Clixml -Path $file -ErrorAction SilentlyContinue

            if ($galleryVersion -gt $old.NotifyVersion) {
                Export-Clixml -InputObject $new -Path $file
                Show-Notification -GalleryVersion $galleryVersion
            }
        }
        else {
            $directory = Split-Path $file

            if (!(Test-Path $directory)) {
                $null = New-Item -ItemType Directory -Path $directory
            }

            Export-Clixml -InputObject $new -Path $file
            Show-Notification -GalleryVersion $galleryVersion
        }
    }
}
function Watch-DbaXESession {
    <#
        .SYNOPSIS
            Watch live XEvent Data as it happens

        .DESCRIPTION
            Watch live XEvent Data as it happens. This command runs until you stop the session, kill the PowerShell session, or Ctrl-C.

            Thanks to Dave Mason (@BeginTry) for some straightforward code samples https://itsalljustelectrons.blogspot.be/2017/01/SQL-Server-Extended-Event-Handling-Via-Powershell.html

        .PARAMETER SqlInstance
            Target SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Session
            Only return a specific session. Options for this parameter are auto-populated from the server.

        .PARAMETER Raw
            If this switch is enabled, the Microsoft.SqlServer.XEvent.Linq.QueryableXEventData enumeration object is returned.

        .PARAMETER InputObject
            Accepts an XESession object returned by Get-DbaXESession.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Watch-DbaXESession

        .EXAMPLE
            Watch-DbaXESession -SqlInstance sql2017 -Session system_health

            Shows events for the system_health session as it happens.

        .EXAMPLE
            Watch-DbaXESession -SqlInstance sql2017 -Session system_health | Export-Csv -NoTypeInformation -Path C:\temp\system_health.csv

            Exports live events to CSV. Ctrl-C may not not cancel out of it - fastest way is to stop the session.

        .EXAMPLE
            Get-DbaXESession -SqlInstance sql2017 -Session system_health | Start-DbaXESession | Watch-DbaXESession | Export-Csv -NoTypeInformation -Path C:\temp\system_health.csv

            Exports live events to CSV. Ctrl-C may not not cancel out of this. The fastest way to do so is to stop the session.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    param (
        [parameter(ValueFromPipeline, ParameterSetName = "instance", Mandatory)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string]$Session,
        [parameter(ValueFromPipeline, ParameterSetName = "piped", Mandatory)]
        [Microsoft.SqlServer.Management.XEvent.Session]$InputObject,
        [switch]$Raw,
        [switch][Alias('Silent')]
        $EnableException
    )
    process {
        if (-not $SqlInstance) {
            $server = $InputObject.Parent
        }
        else {
            try {
                Write-Message -Level Verbose -Message "Connecting to $SqlInstance."
                $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential -MinimumVersion 11
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $SqlInstance -Continue
            }
            $SqlConn = $server.ConnectionContext.SqlConnectionObject
            $SqlStoreConnection = New-Object Microsoft.SqlServer.Management.Sdk.Sfc.SqlStoreConnection $SqlConn
            $XEStore = New-Object  Microsoft.SqlServer.Management.XEvent.XEStore $SqlStoreConnection
            Write-Message -Level Verbose -Message "Getting XEvents Sessions on $SqlInstance."
            $InputObject = $XEStore.sessions | Where-Object Name -eq $Session | Select-Object -First 1
        }

        if ($InputObject) {
            if (-Not $InputObject.IsRunning) {
                Stop-Function -Message "$($InputObject.Name) is in a $status state."
                return
            }

            # Setup all columns for csv but do it in an order
            $columns = @("name", "timestamp")
            $newcolumns = @()

            $fields = ($InputObject.Events.EventFields.Name | Select-Object -Unique)
            foreach ($column in $fields) {
                $newcolumns += $column.TrimStart("collect_")
            }

            $actions = ($InputObject.Events.Actions.Name | Select-Object -Unique)
            foreach ($action in $actions) {
                $newcolumns += ($action -Split '\.')[-1]
            }

            $newcolumns = $newcolumns | Sort-Object
            $columns = ($columns += $newcolumns) | Select-Object -Unique

            try {
                $xevent = New-Object -TypeName Microsoft.SqlServer.XEvent.Linq.QueryableXEventData(
                    ($server.ConnectionContext.ConnectionString),
                    ($InputObject.Name),
                    [Microsoft.SqlServer.XEvent.Linq.EventStreamSourceOptions]::EventStream,
                    [Microsoft.SqlServer.XEvent.Linq.EventStreamCacheOptions]::DoNotCache
                )

                if ($raw) {
                    return $xevent
                }

                # Format output
                foreach ($event in $xevent) {
                    $hash = [ordered]@{}

                    foreach ($column in $columns) {
                        $null = $hash.Add($column, $event.$column) # this basically adds name and timestamp then nulls
                    }

                    foreach ($action in $event.Actions) {
                        $hash[$action.Name] = $action.Value
                    }

                    foreach ($field in $event.Fields) {
                        $hash[$field.Name] = $field.Value
                    }

                    [pscustomobject]($hash)
                }
            }
            catch {
                Start-Sleep 1
                $status = Get-DbaXESession -SqlInstance $server -Session $Session
                if ($status.Status -ne "Running") {
                    Stop-Function -Message "$($InputObject.Name) was stopped."
                }
                else {
                    Stop-Function -Message "Failure" -ErrorRecord $_ -Target $session
                }
            }
            finally {
                if ($xevent -is [IDisposable]) {
                    $xevent.Dispose()
                }
            }
        }
        else {
            Stop-Function -Message "Session not found." -Target $session
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Write-DbaDataTable {
    <#
        .SYNOPSIS
            Writes data to a SQL Server Table.

        .DESCRIPTION
            Writes a .NET DataTable to a SQL Server table using SQL Bulk Copy.

        .PARAMETER SqlInstance
            The SQL Server instance.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database to import the table into.

        .PARAMETER InputObject
            This is the DataTable (or datarow) to import to SQL Server.

        .PARAMETER Table
            The table name to import data into. You can specify a one, two, or three part table name. If you specify a one or two part name, you must also use -Database.

            If the table does not exist, you can use -AutoCreateTable to automatically create the table with inefficient data types.

        .PARAMETER Schema
            Defaults to dbo if no schema is specified.

        .PARAMETER BatchSize
            The BatchSize for the import defaults to 5000.

        .PARAMETER NotifyAfter
            Sets the option to show the notification after so many rows of import

        .PARAMETER AutoCreateTable
            If this switch is enabled, the table will be created if it does not already exist. The table will be created with sub-optimal data types such as nvarchar(max)

        .PARAMETER NoTableLock
            If this switch is enabled, a table lock (TABLOCK) will not be placed on the destination table. By default, this operation will lock the destination table while running.

        .PARAMETER CheckConstraints
            If this switch is enabled, the SqlBulkCopy option to process check constraints will be enabled.

            Per Microsoft "Check constraints while data is being inserted. By default, constraints are not checked."

        .PARAMETER FireTriggers
            If this switch is enabled, the SqlBulkCopy option to fire insert triggers will be enabled.

            Per Microsoft "When specified, cause the server to fire the insert triggers for the rows being inserted into the Database."

        .PARAMETER KeepIdentity
            If this switch is enabled, the SqlBulkCopy option to preserve source identity values will be enabled.

            Per Microsoft "Preserve source identity values. When not specified, identity values are assigned by the destination."

        .PARAMETER KeepNulls
            If this switch is enabled, the SqlBulkCopy option to preserve NULL values will be enabled.

            Per Microsoft "Preserve null values in the destination table regardless of the settings for default values. When not specified, null values are replaced by default values where applicable."

        .PARAMETER Truncate
            If this switch is enabled, the destination table will be truncated after prompting for confirmation.

        .PARAMETER BulkCopyTimeOut
            Value in seconds for the BulkCopy operations timeout. The default is 30 seconds.

        .PARAMETER RegularUser
           Deprecated - now all connections are regular user (don't require admin)

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER UseDynamicStringLength
            By default, all string columns will be NVARCHAR(MAX).
            If this switch is enabled, all columns will get the length specified by the column's MaxLength property (if specified)

        .NOTES
            Tags: DataTable, Insert
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Write-DbaDataTable

        .EXAMPLE
            $DataTable = Import-Csv C:\temp\customers.csv | Out-DbaDataTable
            Write-DbaDataTable -SqlInstance sql2014 -InputObject $DataTable -Table mydb.dbo.customers

            Performs a bulk insert of all the data in customers.csv into database mydb, schema dbo, table customers. A progress bar will be shown as rows are inserted. If the destination table does not exist, the import will be halted.

        .EXAMPLE
            $DataTable = Import-Csv C:\temp\customers.csv | Out-DbaDataTable
            $DataTable | Write-DbaDataTable -SqlInstance sql2014 -Table mydb.dbo.customers

            Performs a row by row insert of the data in customers.csv. This is significantly slower than a bulk insert and will not show a progress bar.

            This method is not recommended. Use -InputObject instead.

        .EXAMPLE
            $DataTable = Import-Csv C:\temp\customers.csv | Out-DbaDataTable
            Write-DbaDataTable -SqlInstance sql2014 -InputObject $DataTable -Table mydb.dbo.customers -AutoCreateTable

            Performs a bulk insert of all the data in customers.csv. If mydb.dbo.customers does not exist, it will be created with inefficient but forgiving DataTypes.

        .EXAMPLE
            $DataTable = Import-Csv C:\temp\customers.csv | Out-DbaDataTable
            Write-DbaDataTable -SqlInstance sql2014 -InputObject $DataTable -Table mydb.dbo.customers -Truncate

            Performs a bulk insert of all the data in customers.csv. Prior to importing into mydb.dbo.customers, the user is informed that the table will be truncated and asks for confirmation. The user is prompted again to perform the import.

        .EXAMPLE
            $DataTable = Import-Csv C:\temp\customers.csv | Out-DbaDataTable
            Write-DbaDataTable -SqlInstance sql2014 -InputObject $DataTable -Database mydb -Table customers -KeepNulls

            Performs a bulk insert of all the data in customers.csv into mydb.dbo.customers. Because Schema was not specified, dbo was used. NULL values in the destination table will be preserved.

        .EXAMPLE
            $passwd = ConvertTo-SecureString "P@ssw0rd" -AsPlainText -Force
            $AzureCredential = Mew-Object System.Management.Automation.PSCredential("AzureAccount"),$passwd)
            $DataTable = Import-Csv C:\temp\customers.csv | Out-DbaDataTable
            Write-DbaDataTable -SqlInstance AzureDB.database.windows.net -InputObject $DataTable -Database mydb -Table customers -KeepNulls -Credential $AzureCredential -BulkCopyTimeOut 300

            This performs the same operation as the previous example, but against a SQL Azure Database instance using the required credentials.

        .EXAMPLE
            $process = Get-Process | Out-DbaDataTable
            Write-DbaDataTable -InputObject $process -SqlInstance sql2014 -Database mydb -Table myprocesses -AutoCreateTable

            Creates a table based on the Process object with over 60 columns, converted from PowerShell data types to SQL Server data types. After the table is created a bulk insert is performed to add process information into the table.

            This is an example of the type conversion in action. All process properties are converted, including special types like TimeSpan. Script properties are resolved before the type conversion starts thanks to Out-DbaDataTable.
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High")]
    param (
        [Parameter(Position = 0, Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [ValidateNotNull()]
        [DbaInstanceParameter]$SqlInstance,
        [Parameter(Position = 1)]
        [ValidateNotNull()]
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Parameter(Position = 2)]
        [object]$Database,
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("DataTable")]
        [ValidateNotNull()]
        [object]$InputObject,
        [Parameter(Position = 3, Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$Table,
        [Parameter(Position = 4)]
        [ValidateNotNullOrEmpty()]
        [string]$Schema = 'dbo',
        [ValidateNotNull()]
        [int]$BatchSize = 50000,
        [ValidateNotNull()]
        [int]$NotifyAfter = 5000,
        [switch]$AutoCreateTable,
        [switch]$NoTableLock,
        [switch]$CheckConstraints,
        [switch]$FireTriggers,
        [switch]$KeepIdentity,
        [switch]$KeepNulls,
        [switch]$Truncate,
        [ValidateNotNull()]
        [int]$bulkCopyTimeOut = 5000,
        [switch]$RegularUser,
        [Alias('Silent')]
        [switch]$EnableException,
        [switch]$UseDynamicStringLength
    )

    begin {
        # Null variable to make sure upper-scope variables don't interfere later
        $steppablePipeline = $null

        #region Utility Functions
        function Invoke-BulkCopy {
        <#
            .SYNOPSIS
                Copies a datatable in bulk over to a table.

            .DESCRIPTION
                Copies a datatable in bulk over to a table.

            .PARAMETER DataTable
                The datatable to copy.

            .PARAMETER SqlInstance
                Needs not be specified. The SqlInstance targeted. For message purposes only.

            .PARAMETER Fqtn
                Needs not be specified. The fqtn written to. For message purposes only.

            .PARAMETER BulkCopy
                Needs not be specified. The bulk copy object used to perform the copy operation.
        #>
            [CmdletBinding()]
            param (
                $DataTable,
                [DbaInstance]$SqlInstance = $SqlInstance,
                [string]$Fqtn = $fqtn,
                $BulkCopy = $bulkCopy
            )
            Write-Message -Level Verbose -Message "Importing in bulk to $fqtn"

            $rowCount = $DataTable.Rows.Count
            if ($rowCount -eq 0) {
                $rowCount = 1
            }

            if ($Pscmdlet.ShouldProcess($SqlInstance, "Writing $rowCount rows to $Fqtn")) {
                $bulkCopy.WriteToServer($DataTable)
                if ($rowCount -is [int]) {
                    Write-Progress -id 1 -activity "Inserting $rowCount rows" -status "Complete" -Completed
                }
            }
        }

        function New-Table {
        <#
            .SYNOPSIS
                Creates a table, based upon a DataTable.

            .DESCRIPTION
                Creates a table, based upon a DataTable.

            .PARAMETER DataTable
                The DataTable to base the table structure upon.

            .PARAMETER PStoSQLTypes
                Automatically inherits from parent.

            .PARAMETER SqlInstance
                Automatically inherits from parent.

            .PARAMETER Fqtn
                Automatically inherits from parent.

            .PARAMETER Server
                Automatically inherits from parent.

            .PARAMETER DatabaseName
                Automatically inherits from parent.

            .PARAMETER EnableException
                By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
                This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
                Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

            .PARAMETER UseDynamicStringLength
                Automatically inherits from parent.
        #>
            [CmdletBinding()]
            param (
                $DataTable,
                $PStoSQLTypes = $PStoSQLTypes,
                $SqlInstance = $SqlInstance,
                $Fqtn = $fqtn,
                $Server = $server,
                $DatabaseName = $databaseName,
                [switch]$EnableException
            )

            Write-Message -Level Verbose -Message "Creating table for $fqtn"

            # Get SQL datatypes by best guess on first data row
            $sqlDataTypes = @();
            $columns = $DataTable.Columns

            if ($null -eq $columns) {
                $columns = $DataTable.Table.Columns
            }

            foreach ($column in $columns) {
                $sqlColumnName = $column.ColumnName

                try {
                    $columnValue = $DataTable.Rows[0].$sqlColumnName
                }
                catch {
                    $columnValue = $DataTable.$sqlColumnName
                }

                if ($null -eq $columnValue) {
                    $columnValue = $DataTable.$sqlColumnName
                }

            <#
                PS to SQL type conversion
                If data type exists in hash table, use the corresponding SQL type
                Else, fallback to nvarchar.
                If UseDynamicStringLength is specified, the DataColumn MaxLength is used if specified
            #>
                if ($PStoSQLTypes.Keys -contains $column.DataType) {
                    $sqlDataType = $PStoSQLTypes[$($column.DataType.toString())]
                    if ($UseDynamicStringLength -and $column.MaxLength -gt 0 -and ($column.DataType -in ("String", "System.String"))) {
                        $sqlDataType = $sqlDataType.Replace("(MAX)", "($($column.MaxLength))")
                    }
                }
                else {
                    $sqlDataType = "nvarchar(MAX)"
                }

                $sqlDataTypes += "[$sqlColumnName] $sqlDataType"
            }

            $sql = "BEGIN CREATE TABLE $fqtn ($($sqlDataTypes -join ' NULL,')) END"

            Write-Message -Level Debug -Message $sql

            if ($Pscmdlet.ShouldProcess($SqlInstance, "Creating table $Fqtn")) {
                try {
                    $null = $Server.Databases[$DatabaseName].Query($sql)
                }
                catch {
                    Stop-Function -Message "The following query failed: $sql" -ErrorRecord $_
                    return
                }
            }
        }

        #endregion Utility Functions

        #region Prepare type for bulk copy
        if (-not $Truncate) { $ConfirmPreference = "None" }

        # Getting the total rows copied is a challenge. Use SqlBulkCopyExtension.
        # http://stackoverflow.com/questions/1188384/sqlbulkcopy-row-count-when-complete

        $source = 'namespace System.Data.SqlClient {
            using Reflection;

            public static class SqlBulkCopyExtension
            {
                const String _rowsCopiedFieldName = "_rowsCopied";
                static FieldInfo _rowsCopiedField = null;

                public static int RowsCopiedCount(this SqlBulkCopy bulkCopy)
                {
                    if (_rowsCopiedField == null) _rowsCopiedField = typeof(SqlBulkCopy).GetField(_rowsCopiedFieldName, BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance);
                    return (int)_rowsCopiedField.GetValue(bulkCopy);
                }
            }
        }'

        Add-Type -ReferencedAssemblies 'System.Data.dll' -TypeDefinition $source -ErrorAction SilentlyContinue
        #endregion Prepare type for bulk copy

        #region Resolve Full Qualified Table Name
        $dotCount = ([regex]::Matches($Table, "\.")).count

        if ($dotCount -lt 2 -and $null -eq $Database) {
            Stop-Function -Message "You must specify a database or fully qualified table name."
            return
        }

        if (Test-Bound -ParameterName Database) {
            $databaseName = "$Database"
        }

        $tableName = $Table
        $schemaName = $Schema

        if ($dotCount -eq 1) {
            $schemaName = $Table.Split(".")[0]
            $tableName = $Table.Split(".")[1]
        }

        if ($dotCount -eq 2) {
            $databaseName = $Table.Split(".")[0]
            $schemaName = $Table.Split(".")[1]
            $tableName = $Table.Split(".")[2]
        }

        if ($databaseName -match "\[.*\]") {
            $databaseName = ($databaseName -replace '\[', '') -replace '\]', ''
        }

        if ($schemaName -match "\[.*\]") {
            $schemaName = ($schemaName -replace '\[', '') -replace '\]', ''
        }

        if ($tableName -match "\[.*\]") {
            $tableName = ($tableName -replace '\[', '') -replace '\]', ''
        }

        $fqtn = "[$databaseName].[$schemaName].[$tableName]"
        Write-Message -Level SomewhatVerbose -Message "FQTN processed: $fqtn"
        #endregion Resolve Full Qualified Table Name

        #region Connect to server and get database
        Write-Message -Message "Connecting to $SqlInstance." -Level Verbose -Target $SqlInstance
        try {
            $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $SqlInstance
            return
        }

        if ($server.ServerType -eq 'SqlAzureDatabase') {
            <#
                For some reasons SMO wants an initial pull when talking to Azure Sql DB
                This will throw and be caught, and then we can continue as normal.
            #>
            try {
                $null = $server.Databases
            }
            catch {
                #do nothing
            }
        }
        $databaseObject = $server.Databases[$databaseName]
        #endregion Connect to server and get database

        #region Prepare database and bulk operations
        if ($null -eq $databaseObject) {
            Stop-Function -Message "$databaseName does not exist." -Target $SqlInstance
            return
        }

        $databaseObject.Tables.Refresh()
        if ($schemaName -notin $databaseObject.Schemas.Name) {
            Stop-Function -Message "Schema does not exist."
            return
        }

        $tableExists = ($tableName -in $databaseObject.Tables.Name) -and ($databaseObject.Tables.Schema -eq $schemaName)

        if ((-not $tableExists) -and (-not $AutoCreateTable)) {
            Stop-Function -Message "Table does not exist and automatic creation of the table has not been selected. Specify the '-AutoCreateTable'-parameter to generate a suitable table."
            return
        }

        $bulkCopyOptions = 0
        $options = "TableLock", "CheckConstraints", "FireTriggers", "KeepIdentity", "KeepNulls", "Default"

        foreach ($option in $options) {
            $optionValue = Get-Variable $option -ValueOnly -ErrorAction SilentlyContinue
            if ($option -eq "TableLock" -and (!$NoTableLock)) {
                $optionValue = $true
            }
            if ($optionValue -eq $true) {
                $bulkCopyOptions += $([Data.SqlClient.SqlBulkCopyOptions]::$option).value__
            }
        }

        if ($Truncate -eq $true) {
            if ($Pscmdlet.ShouldProcess($SqlInstance, "Truncating $fqtn")) {
                try {
                    Write-Message -Level Output -Message "Truncating $fqtn."
                    $null = $server.Databases[$databaseName].Query("TRUNCATE TABLE $fqtn")
                }
                catch {
                    Write-Message -Level Warning -Message "Could not truncate $fqtn. Table may not exist or may have key constraints." -ErrorRecord $_
                }
            }
        }

        $bulkCopy = New-Object Data.SqlClient.SqlBulkCopy("$($server.ConnectionContext.ConnectionString);Database=$databaseName", $bulkCopyOptions)
        $bulkCopy.DestinationTableName = $fqtn
        $bulkCopy.BatchSize = $BatchSize
        $bulkCopy.NotifyAfter = $NotifyAfter
        $bulkCopy.BulkCopyTimeOut = $BulkCopyTimeOut

        $elapsed = [System.Diagnostics.Stopwatch]::StartNew()
        # Add RowCount output
        $bulkCopy.Add_SqlRowsCopied({
                $script:totalRows = $args[1].RowsCopied
                $percent = [int](($script:totalRows / $rowCount) * 100)
                $timeTaken = [math]::Round($elapsed.Elapsed.TotalSeconds, 1)
                Write-Progress -id 1 -activity "Inserting $rowCount rows." -PercentComplete $percent -Status ([System.String]::Format("Progress: {0} rows ({1}%) in {2} seconds", $script:totalRows, $percent, $timeTaken))
            })

        $PStoSQLTypes = @{
            #PS datatype      = SQL data type
            'System.Int32'     = 'int';
            'System.UInt32'    = 'bigint';
            'System.Int16'     = 'smallint';
            'System.UInt16'    = 'int';
            'System.Int64'     = 'bigint';
            'System.UInt64'    = 'decimal(20,0)';
            'System.Decimal'   = 'decimal(20,5)';
            'System.Single'    = 'bigint';
            'System.Double'    = 'float';
            'System.Byte'      = 'tinyint';
            'System.SByte'     = 'smallint';
            'System.TimeSpan'  = 'nvarchar(30)';
            'System.String'    = 'nvarchar(MAX)';
            'System.Char'      = 'nvarchar(1)'
            'System.DateTime'  = 'datetime2';
            'System.Boolean'   = 'bit';
            'System.Guid'      = 'uniqueidentifier';
            'Int32'            = 'int';
            'UInt32'           = 'bigint';
            'Int16'            = 'smallint';
            'UInt16'           = 'int';
            'Int64'            = 'bigint';
            'UInt64'           = 'decimal(20,0)';
            'Decimal'          = 'decimal(20,5)';
            'Single'           = 'bigint';
            'Double'           = 'float';
            'Byte'             = 'tinyint';
            'SByte'            = 'smallint';
            'TimeSpan'         = 'nvarchar(30)';
            'String'           = 'nvarchar(MAX)';
            'Char'             = 'nvarchar(1)'
            'DateTime'         = 'datetime2';
            'Boolean'          = 'bit';
            'Bool'             = 'bit';
            'Guid'             = 'uniqueidentifier';
            'int'              = 'int';
            'long'             = 'bigint';
        }

        $validTypes = @([System.Data.DataSet], [System.Data.DataTable], [System.Data.DataRow], [System.Data.DataRow[]])
        #endregion Prepare database and bulk operations

        #region ConvertTo-DbaDataTable wrapper
        try {
            $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('ConvertTo-DbaDataTable', [System.Management.Automation.CommandTypes]::Function)
            $splatCDDT = @{
                TimeSpanType   = (Get-DbaConfigValue -FullName 'commands.write-dbadatatable.timespantype' -Fallback 'TotalMilliseconds')
                SizeType       = (Get-DbaConfigValue -FullName 'commands.write-dbadatatable.sizetype' -Fallback 'Int64')
                IgnoreNull     = (Get-DbaConfigValue -FullName 'commands.write-dbadatatable.ignorenull' -Fallback $false)
                Raw            = (Get-DbaConfigValue -FullName 'commands.write-dbadatatable.raw' -Fallback $false)
            }
            $scriptCmd = { & $wrappedCmd @splatCDDT }
            $steppablePipeline = $scriptCmd.GetSteppablePipeline()
            $steppablePipeline.Begin($true)
        }
        catch {
            Stop-Function -Message "Failed to initialize "
        }
        #endregion ConvertTo-DbaDataTable wrapper
    }
    process {
        if (Test-FunctionInterrupt) { return }

        if ($null -ne $InputObject) { $inputType = $InputObject.GetType() }
        else { $inputType = $null }

        if ($inputType -eq [System.Data.DataSet]) {
            $inputData = $InputObject.Tables
            $inputType = [System.Data.DataTable[]]
        }
        else {
            $inputData = $InputObject
        }

        #region Scenario 1: Single valid table
        if ($inputType -in $validTypes) {
            if (-not $tableExists) {
                try {
                    New-Table -DataTable $InputObject -EnableException
                    $tableExists = $true
                }
                catch {
                    Stop-Function -Message "Failed to create table $fqtn" -ErrorRecord $_ -Target $SqlInstance
                    return
                }
            }

            try { Invoke-BulkCopy -DataTable $InputObject }
            catch {
                Stop-Function -Message "Failed to bulk import to $fqtn" -ErrorRecord $_ -Target $SqlInstance
            }
            return
        }
        #endregion Scenario 1: Single valid table

        foreach ($object in $inputData) {
            #region Scenario 2: Multiple valid tables
            if ($object.GetType() -in $validTypes) {
                if (-not $tableExists) {
                    try {
                        New-Table -DataTable $object -EnableException
                        $tableExists = $true
                    }
                    catch {
                        Stop-Function -Message "Failed to create table $fqtn" -ErrorRecord $_ -Target $SqlInstance
                        return
                    }
                }

                try { Invoke-BulkCopy -DataTable $object }
                catch {
                    Stop-Function -Message "Failed to bulk import to $fqtn" -ErrorRecord $_ -Target $SqlInstance -Continue
                }
                continue
            }
            #endregion Scenario 2: Multiple valid tables

            #region Scenario 3: Invalid data types
            else {
                $null = $steppablePipeline.Process($object)
                continue
            }
            #endregion Scenario 3: Invalid data types
        }
    }
    end {
        #region ConvertTo-DbaDataTable wrapper
        if ($null -ne $steppablePipeline) {
            $dataTable = $steppablePipeline.End()

            if (-not $tableExists) {
                try {
                    New-Table -DataTable $dataTable[0] -EnableException
                    $tableExists = $true
                }
                catch {
                    Stop-Function -Message "Failed to create table $fqtn" -ErrorRecord $_ -Target $SqlInstance
                    return
                }
            }

            try { Invoke-BulkCopy -DataTable $dataTable[0] }
            catch {
                Stop-Function -Message "Failed to bulk import to $fqtn" -ErrorRecord $_ -Target $SqlInstance
            }
        }
        #endregion ConvertTo-DbaDataTable wrapper

        if ($bulkCopy) {
            $bulkCopy.Close()
            $bulkCopy.Dispose()
        }
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter RegularUser
    }
}
function Connect-AsServer {
    <#
.SYNOPSIS
Internal function that creates SMO server object.

.DESCRIPTION
Internal function that creates SMO server object.

.PARAMETER AsServer
Analysis Server

.PARAMETER ParameterConnection
Shorten the timeout

.NOTES
Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
Connect-AsServer -AsServer localhost
Connects to SSAS on the local server

#>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [object]$AsServer,
        [switch]$ParameterConnection
    )

    if ($AsServer.GetType() -eq [Microsoft.AnalysisServices.Server]) {

        if ($ParameterConnection) {
            $paramserver = New-Object Microsoft.AnalysisServices.Server
            $paramserver.Connect("Data Source=$($AsServer.Name);Connect Timeout=2")
            return $paramserver
        }

        if ($AsServer.Connected -eq $false) { $AsServer.Connect("Data Source=$($AsServer.Name);Connect Timeout=3") }
        return $AsServer
    }

    $server = New-Object Microsoft.AnalysisServices.Server

    try {
        if ($ParameterConnection) {
            $server.Connect("Data Source=$AsServer;Connect Timeout=2")
        }
        else { $server.Connect("Data Source=$AsServer;Connect Timeout=3") }
    }
    catch {
        $message = $_.Exception.InnerException
        $message = $message.ToString()
        $message = ($message -Split '-->')[0]
        $message = ($message -Split 'at System.Data.SqlClient')[0]
        $message = ($message -Split 'at System.Data.ProviderBase')[0]
        throw "Can't connect to $asserver`: $message "
    }

    return $server
}
function Connect-SqlInstance {
    <#
        .SYNOPSIS
            Internal function to establish smo connections.

        .DESCRIPTION
            Internal function to establish smo connections.

            Can interpret any of the following types of information:
            - String
            - Smo Server objects
            - Smo Linked Server objects

        .PARAMETER SqlInstance
            The SQL Server instance to restore to.

        .PARAMETER SqlCredential
            Allows you to login to servers using SQL Logins as opposed to Windows Auth/Integrated/Trusted.

        .PARAMETER ParameterConnection
            This call is for dynamic parameters only and is no longer used, actually.

        .PARAMETER AzureUnsupported
            Throw if Azure is detected but not supported

        .PARAMETER RegularUser
            The connection doesn't require SA privileges.
            By default, the assumption is that SA is no longer required.

        .PARAMETER MinimumVersion
           The minimum version that the calling command will support

        .EXAMPLE
            Connect-SqlInstance -SqlInstance sql2014

            Connect to the Server sql2014 with native credentials.
    #>
    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingEmptyCatchBlock", "")]
    param (
        [Parameter(Mandatory = $true)]
        [object]$SqlInstance,
        [object]$SqlCredential,
        [switch]$ParameterConnection,
        [switch]$RegularUser = $true,
        [int]$MinimumVersion,
        [switch]$AzureUnsupported,
        [switch]$NonPooled
    )

    #region Utility functions
    function Invoke-TEPPCacheUpdate {
        [CmdletBinding()]
        param (
            [System.Management.Automation.ScriptBlock]$ScriptBlock
        )

        try {
            [ScriptBlock]::Create($scriptBlock).Invoke()
        }
        catch {
            # If the SQL Server version doesn't support the feature, we ignore it and silently continue
            if ($_.Exception.InnerException.InnerException.GetType().FullName -eq "Microsoft.SqlServer.Management.Sdk.Sfc.InvalidVersionEnumeratorException") {
                return
            }

            if ($ENV:APPVEYOR_BUILD_FOLDER -or ([Sqlcollaborative.Dbatools.Message.MEssageHost]::DeveloperMode)) { throw }
            else {
                Write-Message -Level Warning -Message "Failed TEPP Caching: $($scriptBlock.ToString() | Select-String '"(.*?)"' | ForEach-Object { $_.Matches[0].Groups[1].Value })" -ErrorRecord $_ 3>$null
            }
        }
    }
    #endregion Utility functions

    #region Ensure Credential integrity
    <#
    Usually, the parameter type should have been not object but off the PSCredential type.
    When binding null to a PSCredential type parameter on PS3-4, it'd then show a prompt, asking for username and password.

    In order to avoid that and having to refactor lots of functions (and to avoid making regular scripts harder to read), we created this workaround.
    #>
    if ($SqlCredential) {
        if ($SqlCredential.GetType() -ne [System.Management.Automation.PSCredential]) {
            throw "The credential parameter was of a non-supported type! Only specify PSCredentials such as generated from Get-Credential. Input was of type $($SqlCredential.GetType().FullName)"
        }
    }
    #endregion Ensure Credential integrity

    #region Safely convert input into instance parameters
    <#
    This is a bit ugly, but:
    In some cases functions would directly pass their own input through when the parameter on the calling function was typed as [object[]].
    This would break the base parameter class, as it'd automatically be an array and the parameterclass is not designed to handle arrays (Shouldn't have to).

    Note: Multiple servers in one call were never supported, those old functions were liable to break anyway and should be fixed soonest.
    #>
    if ($SqlInstance.GetType() -eq [Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter]) {
        [DbaInstanceParameter]$ConvertedSqlInstance = $SqlInstance
        if ($ConvertedSqlInstance.Type -like "SqlConnection") {
            [DbaInstanceParameter]$ConvertedSqlInstance = New-Object Microsoft.SqlServer.Management.Smo.Server($ConvertedSqlInstance.InputObject)
        }
    }
    else {
        [DbaInstanceParameter]$ConvertedSqlInstance = [DbaInstanceParameter]($SqlInstance | Select-Object -First 1)

        if ($SqlInstance.Count -gt 1) {
            Write-Message -Level Warning -EnableException $true -Message "More than on server was specified when calling Connect-SqlInstance from $((Get-PSCallStack)[1].Command)"
        }
    }
    #endregion Safely convert input into instance parameters

    #region Input Object was a server object
    if ($ConvertedSqlInstance.InputObject.GetType() -eq [Microsoft.SqlServer.Management.Smo.Server]) {
        $server = $ConvertedSqlInstance.InputObject
        if ($server.ConnectionContext.IsOpen -eq $false) {
            if ($NonPooled) {
                $server.ConnectionContext.Connect()
            }
            elseif ($authtype -eq "Windows Authentication with Credential") {
                # Make it connect in a natural way, hard to explain.
                $null = $server.IsMemberOfWsfcCluster
            }
            else {
                $server.ConnectionContext.SqlConnectionObject.Open()
            }
        }

        # Register the connected instance, so that the TEPP updater knows it's been connected to and starts building the cache
        [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::SetInstance($ConvertedSqlInstance.FullSmoName.ToLower(), $server.ConnectionContext.Copy(), ($server.ConnectionContext.FixedServerRoles -match "SysAdmin"))

        # Update cache for instance names
        if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["sqlinstance"] -notcontains $ConvertedSqlInstance.FullSmoName.ToLower()) {
            [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["sqlinstance"] += $ConvertedSqlInstance.FullSmoName.ToLower()
        }

        # Update lots of registered stuff
        if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::TeppSyncDisabled) {
            $FullSmoName = $ConvertedSqlInstance.FullSmoName.ToLower()
            foreach ($scriptBlock in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::TeppGatherScriptsFast)) {
                Invoke-TEPPCacheUpdate -ScriptBlock $scriptBlock
            }
        }

        if (-not $server.ComputerName) {
            $parsedcomputername = $server.NetName
            if (-not $parsedcomputername) {
                $parsedcomputername = ([dbainstance]$SqlInstance).ComputerName
            }
            Add-Member -InputObject $server -NotePropertyName ComputerName -NotePropertyValue $parsedcomputername -Force
        }
        return $server
    }
    #endregion Input Object was a server object

    #region Input Object was anything else

    $server = New-Object Microsoft.SqlServer.Management.Smo.Server $ConvertedSqlInstance.FullSmoName
    $server.ConnectionContext.ApplicationName = "dbatools PowerShell module - dbatools.io"
    if ($ConvertedSqlInstance.IsConnectionString) { $server.ConnectionContext.ConnectionString = $ConvertedSqlInstance.InputObject }

    try {
        $server.ConnectionContext.ConnectTimeout = [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::SqlConnectionTimeout

        if ($null -ne $SqlCredential.Username) {
            $username = ($SqlCredential.Username).TrimStart("\")

            if ($username -like "*\*") {
                $username = $username.Split("\")[1]
                $authtype = "Windows Authentication with Credential"
                $server.ConnectionContext.LoginSecure = $true
                $server.ConnectionContext.ConnectAsUser = $true
                $server.ConnectionContext.ConnectAsUserName = $username
                $server.ConnectionContext.ConnectAsUserPassword = ($SqlCredential).GetNetworkCredential().Password
            }
            else {
                $authtype = "SQL Authentication"
                $server.ConnectionContext.LoginSecure = $false
                $server.ConnectionContext.set_Login($username)
                $server.ConnectionContext.set_SecurePassword($SqlCredential.Password)
            }
        }
    }
    catch { }

    try {
        if ($NonPooled) {
            $server.ConnectionContext.Connect()
        }
        elseif ($authtype -eq "Windows Authentication with Credential") {
            # Make it connect in a natural way, hard to explain.
            $null = $server.IsMemberOfWsfcCluster
        }
        else {
            $server.ConnectionContext.SqlConnectionObject.Open()
        }
    }
    catch {
        $message = $_.Exception.InnerException.InnerException
        if ($message) {
            $message = $message.ToString()
            $message = ($message -Split '-->')[0]
            $message = ($message -Split 'at System.Data.SqlClient')[0]
            $message = ($message -Split 'at System.Data.ProviderBase')[0]

            if ($message -match "network path was not found") {
                $message = "Can't connect to $sqlinstance`: System.Data.SqlClient.SqlException (0x80131904): A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections."
            }

            throw "Can't connect to $ConvertedSqlInstance`: $message "
        }
        else {
            throw $_
        }
    }

    if ($MinimumVersion -and $server.VersionMajor) {
        if ($server.versionMajor -lt $MinimumVersion) {
            throw "SQL Server version $MinimumVersion required - $server not supported."
        }
    }

    if ($AzureUnsupported -and $server.DatabaseEngineType -eq "SqlAzureDatabase") {
        throw "Azure SQL Database not supported"
    }

    if (-not $RegularUser) {
        if ($server.ConnectionContext.FixedServerRoles -notmatch "SysAdmin") {
            throw "Not a sysadmin on $ConvertedSqlInstance. Quitting."
        }
    }
    #'PrimaryFilePath' seems the culprit for slow SMO on databases
    $Fields2000_Db = 'Collation', 'CompatibilityLevel', 'CreateDate', 'ID', 'IsAccessible', 'IsFullTextEnabled', 'IsSystemObject', 'IsUpdateable', 'LastBackupDate', 'LastDifferentialBackupDate', 'LastLogBackupDate', 'Name', 'Owner', 'ReadOnly', 'RecoveryModel', 'ReplicationOptions', 'Status', 'Version'
    $Fields200x_Db = $Fields2000_Db + @('BrokerEnabled', 'DatabaseSnapshotBaseName', 'IsMirroringEnabled', 'Trustworthy')
    $Fields201x_Db = $Fields200x_Db + @('ActiveConnections', 'AvailabilityDatabaseSynchronizationState', 'AvailabilityGroupName', 'ContainmentType', 'EncryptionEnabled')

    $Fields2000_Login = 'CreateDate', 'DateLastModified', 'DefaultDatabase', 'DenyWindowsLogin', 'IsSystemObject', 'Language', 'LanguageAlias', 'LoginType', 'Name', 'Sid', 'WindowsLoginAccessType'
    $Fields200x_Login = $Fields2000_Login + @('AsymmetricKey', 'Certificate', 'Credential', 'ID', 'IsDisabled', 'IsLocked', 'IsPasswordExpired', 'MustChangePassword', 'PasswordExpirationEnabled', 'PasswordPolicyEnforced')
    $Fields201x_Login = $Fields200x_Login + @('PasswordHashAlgorithm')

    try {
        if ($Server.ServerType -ne 'SqlAzureDatabase') {
            $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Trigger], 'IsSystemObject')
            $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Schema], 'IsSystemObject')
            $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.SqlAssembly], 'IsSystemObject')
            $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Table], 'IsSystemObject')
            $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.View], 'IsSystemObject')
            $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.StoredProcedure], 'IsSystemObject')
            $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.UserDefinedFunction], 'IsSystemObject')

            if ($server.VersionMajor -eq 8) {
                # 2000
                $initFieldsDb = New-Object System.Collections.Specialized.StringCollection
                [void]$initFieldsDb.AddRange($Fields2000_Db)
                $initFieldsLogin = New-Object System.Collections.Specialized.StringCollection
                [void]$initFieldsLogin.AddRange($Fields2000_Login)
                $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Database], $initFieldsDb)
                $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Login], $initFieldsLogin)
            }
            elseif ($server.VersionMajor -eq 9 -or $server.VersionMajor -eq 10) {
                # 2005 and 2008
                $initFieldsDb = New-Object System.Collections.Specialized.StringCollection
                [void]$initFieldsDb.AddRange($Fields200x_Db)
                $initFieldsLogin = New-Object System.Collections.Specialized.StringCollection
                [void]$initFieldsLogin.AddRange($Fields200x_Login)
                $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Database], $initFieldsDb)
                $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Login], $initFieldsLogin)
            }
            else {
                # 2012 and above
                $initFieldsDb = New-Object System.Collections.Specialized.StringCollection
                [void]$initFieldsDb.AddRange($Fields201x_Db)
                $initFieldsLogin = New-Object System.Collections.Specialized.StringCollection
                [void]$initFieldsLogin.AddRange($Fields201x_Login)
                $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Database], $initFieldsDb)
                $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Login], $initFieldsLogin)
            }
        }
    }
    catch {
        # perhaps a DLL issue, continue going
    }

    # Register the connected instance, so that the TEPP updater knows it's been connected to and starts building the cache
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::SetInstance($ConvertedSqlInstance.FullSmoName.ToLower(), $server.ConnectionContext.Copy(), ($server.ConnectionContext.FixedServerRoles -match "SysAdmin"))

    # Update cache for instance names
    if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["sqlinstance"] -notcontains $ConvertedSqlInstance.FullSmoName.ToLower()) {
        [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["sqlinstance"] += $ConvertedSqlInstance.FullSmoName.ToLower()
    }

    # Update lots of registered stuff
    if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::TeppSyncDisabled) {
        $FullSmoName = $ConvertedSqlInstance.FullSmoName.ToLower()
        foreach ($scriptBlock in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::TeppGatherScriptsFast)) {
            Invoke-TEPPCacheUpdate -ScriptBlock $scriptBlock
        }
    }

    if (-not $server.ComputerName) {
        $parsedcomputername = $server.NetName
        if (-not $parsedcomputername) {
            $parsedcomputername = ([dbainstance]$SqlInstance).ComputerName
        }
        Add-Member -InputObject $server -NotePropertyName ComputerName -NotePropertyValue $parsedcomputername -Force
    }
    return $server
    #endregion Input Object was anything else
}
function Convert-ByteToHexString {
    <#
    .SYNOPSIS
    Converts byte object into hex string

    .DESCRIPTION
    Converts byte object ([byte[]]@(1,100,23,54)) into the hex string (e.g. '0x01641736')
    Used when working with SMO logins and their byte parameters: sids and hashed passwords

    .PARAMETER InputObject
    Input byte[] object (e.g. [byte[]]@(18,52))

    .NOTES
    Tags: Login, Internal
    Author: Kirill Kravtsov (@nvarscar)
    dbatools PowerShell module (https://dbatools.io, [email protected])
    Copyright (C) 2016 Chrissy LeMaire
    License: MIT https://opensource.org/licenses/MIT

    .EXAMPLE
    Convert-ByteToHexString ([byte[]]@(1,100,23,54))

    Returns hex string '0x01641736'

    .EXAMPLE
    Convert-ByteToHexString 18,52

    Returns hex string '0x1234'
#>
    param ([byte[]]$InputObject)
    $outString = "0x"
    $InputObject | ForEach-Object { $outString += ("{0:X}" -f $_).PadLeft(2, "0") }
    $outString
}
function Convert-DbaMessageException {
<#
    .SYNOPSIS
        Transforms the Exception input to the message system.
    
    .DESCRIPTION
        Transforms the Exception input to the message system.
        
        If there is an exception running a transformation scriptblock, it will log the error in the transform error queue and return the original object instead.
    
    .PARAMETER Exception
        The input Exception object, that might have to be transformed (may not either)
    
    .PARAMETER FunctionName
        The function writing the message
    
    .PARAMETER ModuleName
        The module, that the function writing the message is part of
    
    .EXAMPLE
        PS C:\> Convert-DbaMessageException -Exception $Exception -FunctionName 'Get-Test' -ModuleName 'MyModule'
        
        Checks internal storage for definitions that require a Exception transform, and either returns the original object or the transformed object.
#>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        $Exception,
        
        [Parameter(Mandatory = $true)]
        [string]
        $FunctionName,
        
        [Parameter(Mandatory = $true)]
        [string]
        $ModuleName
    )
    
    if ($null -eq $Exception) { return }
    
    $typeName = $Exception.GetType().FullName.ToLower()
    
    if ([Sqlcollaborative.Dbatools.Message.MessageHost]::ExceptionTransforms.ContainsKey($typeName)) {
        $scriptBlock = [Sqlcollaborative.Dbatools.Message.MessageHost]::ExceptionTransforms[$typeName]
        try {
            $tempException = $ExecutionContext.InvokeCommand.InvokeScript($false, ([scriptblock]::Create($scriptBlock.ToString())), $null, $Exception)
            return $tempException
        }
        catch {
            [Sqlcollaborative.Dbatools.Message.MessageHost]::WriteTransformError($_, $FunctionName, $ModuleName, $Exception, "Exception", ([System.Management.Automation.Runspaces.Runspace]::DefaultRunspace.InstanceId))
            return $Exception
        }
    }
    
    if ($transform = [Sqlcollaborative.Dbatools.Message.MessageHost]::ExceptionTransformList.Get($typeName, $ModuleName, $FunctionName)) {
        try {
            $tempException = $ExecutionContext.InvokeCommand.InvokeScript($false, ([scriptblock]::Create($transform.ScriptBlock.ToString())), $null, $Exception)
            return $tempException
        }
        catch {
            [Sqlcollaborative.Dbatools.Message.MessageHost]::WriteTransformError($_, $FunctionName, $ModuleName, $Exception, "Target", ([System.Management.Automation.Runspaces.Runspace]::DefaultRunspace.InstanceId))
            return $Exception
        }
    }
    
    return $Exception
}
function Convert-DbaMessageLevel {
<#
    .SYNOPSIS
        Processes the effective message level of a message
    
    .DESCRIPTION
        Processes the effective message level of a message
        - Applies level decrements
        - Applies message level modifiers
    
    .PARAMETER OriginalLevel
        The level the message was originally written to
    
    .PARAMETER FromStopFunction
        Whether the message was passed through Stop-PSFFunction first.
        This is used to increment the automatic message level decrement counter by 1 (so it ignores the fact, that it was passed through Stop-PSFFunction).
        The automatic message level decrement functionality allows users to make nested commands' messages be less verbose.
    
    .PARAMETER Tags
        The tags that were added to the message
    
    .PARAMETER FunctionName
        The function that wrote the message.
    
    .PARAMETER ModuleName
        The module the function writing the message comes from.
    
    .EXAMPLE
        Convert-DbaMessageLevel -OriginalLevel $Level -FromStopFunction $fromStopFunction -Tags $Tag -FunctionName $FunctionName -ModuleName $ModuleName
        
        This will convert the original level of $Level based on the transformation rules for levels.
#>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Sqlcollaborative.Dbatools.Message.MessageLevel]
        $OriginalLevel,
        
        [Parameter(Mandatory = $true)]
        [bool]
        $FromStopFunction,
        
        [Parameter(Mandatory = $true)]
        [AllowNull()]
        [string[]]
        $Tags,
        
        [Parameter(Mandatory = $true)]
        [string]
        $FunctionName,
        
        [Parameter(Mandatory = $true)]
        [string]
        $ModuleName
    )
    
    $number = $OriginalLevel.value__
    
    if ([Sqlcollaborative.Dbatools.Message.MessageHost]::NestedLevelDecrement -gt 0) {
        $depth = (Get-PSCallStack).Count - 3
        if ($FromStopFunction) { $depth = $depth - 1 }
        $number = $number + $depth * ([Sqlcollaborative.Dbatools.Message.MessageHost]::NestedLevelDecrement)
    }
    
    foreach ($modifier in [Sqlcollaborative.Dbatools.Message.MessageHost]::MessageLevelModifiers.Values) {
        if ($modifier.AppliesTo($FunctionName, $ModuleName, $Tags)) {
            $number = $number + $modifier.Modifier
        }
    }
    
    # Finalize number and return
    if ($number -lt 1) { $number = 1 }
    if ($number -gt 9) { $number = 9 }
    return ([Sqlcollaborative.Dbatools.Message.MessageLevel]$number)
}
function Convert-DbaMessageTarget {
<#
    .SYNOPSIS
        Transforms the target input to the message system.
    
    .DESCRIPTION
        Transforms the target input to the message system.
        
        If there is an exception running a transformation scriptblock, it will log the error in the transform error queue and return the original object instead.
    
    .PARAMETER Target
        The input target object, that might have to be transformed (may not either)
    
    .PARAMETER FunctionName
        The function writing the message
    
    .PARAMETER ModuleName
        The module, that the function writing the message is part of
    
    .EXAMPLE
        PS C:\> Convert-DbaMessageTarget -Target $Target -FunctionName 'Get-Test' -ModuleName 'MyModule'
        
        Checks internal storage for definitions that require a target transform, and either returns the original object or the transformed object.
#>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        $Target,
        
        [Parameter(Mandatory = $true)]
        [string]
        $FunctionName,
        
        [Parameter(Mandatory = $true)]
        [string]
        $ModuleName
    )
    
    if ($null -eq $Target) { return }
    
    $typeName = $Target.GetType().FullName.ToLower()
    
    if ([Sqlcollaborative.Dbatools.Message.MessageHost]::TargetTransforms.ContainsKey($typeName)) {
        $scriptBlock = [Sqlcollaborative.Dbatools.Message.MessageHost]::TargetTransforms[$typeName]
        try {
            $tempTarget = $ExecutionContext.InvokeCommand.InvokeScript($false, ([scriptblock]::Create($scriptBlock.ToString())), $null, $Target)
            return $tempTarget
        }
        catch {
            [Sqlcollaborative.Dbatools.Message.MessageHost]::WriteTransformError($_, $FunctionName, $ModuleName, $Target, "Target", ([System.Management.Automation.Runspaces.Runspace]::DefaultRunspace.InstanceId))
            return $Target
        }
    }
    
    if ($transform = [Sqlcollaborative.Dbatools.Message.MessageHost]::TargetTransformlist.Get($typeName, $ModuleName, $FunctionName)) {
        try {
            $tempTarget = $ExecutionContext.InvokeCommand.InvokeScript($false, ([scriptblock]::Create($transform.ScriptBlock.ToString())), $null, $Target)
            return $tempTarget
        }
        catch {
            [Sqlcollaborative.Dbatools.Message.MessageHost]::WriteTransformError($_, $FunctionName, $ModuleName, $Target, "Target", ([System.Management.Automation.Runspaces.Runspace]::DefaultRunspace.InstanceId))
            return $Target
        }
    }
    
    return $Target
}
function Convert-DbVersionToSqlVersion {
    <#
.SYNOPSIS
Internal function that makes db versions human readable

.DESCRIPTION
Internal function that makes db versions human readable

.PARAMETER dbversion
Analysis Server

.EXAMPLE
Convert-DbVersionToSqlVersion -dbversion 856

Returns "SQL Server vNext CTP1"

#>
    param (
        [string]$dbversion
    )

    $dbversion = switch ($dbversion) {
        869 { "SQL Server 2017"}
        856 { "SQL Server vNext CTP1" }
        852 { "SQL Server 2016" }
        829 { "SQL Server 2016 Prerelease" }
        782 { "SQL Server 2014" }
        706 { "SQL Server 2012" }
        684 { "SQL Server 2012 CTP1" }
        661 { "SQL Server 2008 R2" }
        660 { "SQL Server 2008 R2" }
        655 { "SQL Server 2008 SP2+" }
        612 { "SQL Server 2005" }
        611 { "SQL Server 2005" }
        539 { "SQL Server 2000" }
        515 { "SQL Server 7.0" }
        408 { "SQL Server 6.5" }
        default { $dbversion }
    }

    return $dbversion
}
function Convert-HexStringToByte {
    <#
    .SYNOPSIS
    Converts hex string into byte object

    .DESCRIPTION
    Converts hex string (e.g. '0x01641736') into the byte object ([byte[]]@(1,100,23,54))
    Used when working with SMO logins and their byte parameters: sids and hashed passwords

    .PARAMETER InputObject
    Input hex string (e.g. '0x1234' or 'DBA2FF')

    .NOTES
    Tags: Login, Internal
    Author: Kirill Kravtsov (@nvarscar)
    dbatools PowerShell module (https://dbatools.io, [email protected])
    Copyright (C) 2016 Chrissy LeMaire
    License: MIT https://opensource.org/licenses/MIT

    .EXAMPLE
    Convert-HexStringToByte '0x01641736'

    Returns byte[] object [byte[]]@(1,100,23,54)

    .EXAMPLE
    Convert-HexStringToByte '1234'

    Returns byte[] object [byte[]]@(18,52)
#>
    param (
        [string]$InputObject
    )
    $hexString = $InputObject.TrimStart("0x")
    if ($hexString.Length % 2 -eq 1) { $hexString = '0' + $hexString }
    [byte[]]$outByte = $null; $outByte += 0 .. (($hexString.Length) / 2 - 1) | ForEach-Object { [Int16]::Parse($hexString.Substring($_ * 2, 2), 'HexNumber') }
    Return $outByte
}
function Disconnect-Regserver ($Server) {
    $i = 0
    do { $server = $server.Parent }
    until ($null -ne $server.ServerConnection -or $i++ -gt 20)
    if ($server.ServerConnection) {
        $server.ServerConnection.Disconnect()
    }
}
function Get-BackupAncientHistory {
    <#
        .SYNOPSIS
            Returns details of the last full backup of a SQL Server 2000 database

        .DESCRIPTION
            Backup History command to pull limited history from a SQL 2000 instance. If not using SQL 2000, please use Get-DbaBackupHistory which pulls more infomation, and has more options. This is just here to cope with 2k and copy-DbaDatabase issues

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER Credential
            Credential object used to connect to the SQL Server Instance as a different user. This can be a Windows or SQL Server account. Windows users are determined by the existence of a backslash, so if you are intending to use an alternative Windows connection instead of a SQL login, ensure it contains a backslash.

        .PARAMETER Database
            Specifies one or more database(s) to process. If unspecified, all databases will be processed.

        .NOTES
        Author: Stuart Moore (@napalmgram), stuart-moore.com

        dbatools PowerShell module (https://dbatools.io, [email protected])
        Copyright (C) 2016 Chrissy LeMaire
        License: MIT https://opensource.org/licenses/MIT

    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    Param (
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [Alias("Credential")]
        [PsCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [string]$FileNameStub,
        [Alias('Silent')]
        [switch]$EnableException
    )
    BEGIN {
        try {
            Write-Message -Level VeryVerbose -Message "Connecting to $SqlInstance." -Target $SqlInstance
            $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
        }
        catch {
            Stop-Function -Message "Failed to process Instance $SqlInstance." -InnerErrorRecord $_ -Target $SqlInstance -Continue
        }
        if ($server.SoftwareVersionMajor -gt 8) {
            Write-Message -Level Warning -Message "This is not the function you're looking for. This is for SQL 2000 only, please use Get-DbaBackupHistory instead. It's much nicer"
        }

        $databases = @()
        if ($null -ne $Database) {
            ForEach ($db in $Database) {
                $databases += [PScustomObject]@{name = $db}
            }
        }
        else {
            $databases = $server.Databases
        }
    }

    PROCESS {
        foreach ($db in $Database) {
            Write-Message -Level Verbose -Message "Processing database $db"
            $sql = "
            SELECT
            a.Server,
             a.[Database],
             a.Username,
             a.Start,
             a.[End],
             a.Duration,
             a.[Path],
             a.Type,
            NULL as TotalSize,
             a.MediaSetId,
             a.BackupSetID,
             a.Software,
              a.position,
              a.first_lsn,
              a.database_backup_lsn,
              a.checkpoint_lsn,
              a.last_lsn,
             a.first_lsn as 'FirstLSN',
              a.database_backup_lsn as 'DatabaseBackupLsn',
              a.checkpoint_lsn as 'CheckpointLsn',
              a.last_lsn as 'Lastlsn',
              a.software_major_version,
             a.DeviceType,
                NULL as is_copy_only,
            NULL as last_recovery_fork_guid
            FROM (
            SELECT
              backupset.database_name AS [Database],
              backupset.user_name AS Username,
              backupset.backup_start_date AS Start,
              backupset.server_name as [Server],
              backupset.backup_finish_date AS [End],
              DATEDIFF(SECOND, backupset.backup_start_date, backupset.backup_finish_date) AS Duration,
              mediafamily.physical_device_name AS Path,
              CASE backupset.type
             WHEN 'L' THEN 'Log'
             WHEN 'D' THEN 'Full'
             WHEN 'F' THEN 'File'
             WHEN 'I' THEN 'Differential'
             WHEN 'G' THEN 'Differential File'
             WHEN 'P' THEN 'Partial Full'
             WHEN 'Q' THEN 'Partial Differential'
             ELSE NULL
              END AS Type,
              backupset.media_set_id AS MediaSetId,
              mediafamily.media_family_id as mediafamilyid,
              backupset.backup_set_id as BackupSetID,
              CASE mediafamily.device_type
             WHEN 2 THEN 'Disk'
             WHEN 102 THEN 'Permanent Disk Device'
             WHEN 5 THEN 'Tape'
             WHEN 105 THEN 'Permanent Tape Device'
             WHEN 6 THEN 'Pipe'
             WHEN 106 THEN 'Permanent Pipe Device'
             WHEN 7 THEN 'Virtual Device'
             ELSE 'Unknown'
             END AS DeviceType,
              backupset.position,
              backupset.first_lsn,
              backupset.database_backup_lsn,
              backupset.checkpoint_lsn,
              backupset.last_lsn,
              backupset.software_major_version,
              mediaset.software_name AS Software
            FROM msdb..backupmediafamily AS mediafamily
            JOIN msdb..backupmediaset AS mediaset
              ON mediafamily.media_set_id = mediaset.media_set_id
            JOIN msdb..backupset AS backupset
              ON backupset.media_set_id = mediaset.media_set_id
            WHERE backupset.database_name = '$db'
                    ) AS a
            where  a.backupsetid in (Select max(backup_set_id) from msdb..backupset where database_name='$db')"
            Write-Message -Level Debug -Message $sql
            $results = $server.ConnectionContext.ExecuteWithResults($sql).Tables.Rows | Select-Object * -ExcludeProperty BackupSetRank, RowError, Rowstate, table, itemarray, haserrors
            Write-Message -Level SomewhatVerbose -Message "Processing as grouped output."
            $GroupedResults = $results | Group-Object -Property backupsetid
            Write-Message -Level SomewhatVerbose -Message "$($GroupedResults.Count) result-groups found."
            $groupResults = @()
            foreach ($group in $GroupedResults) {

                $fileSql = "select file_type as FileType, logical_name as LogicalName, physical_name as PhysicalName
                            from msdb.dbo.backupfile where backup_set_id='$($Group.group[0].BackupSetID)'"

                Write-Message -Level Debug -Message "FileSQL: $fileSql"

                $historyObject = New-Object Sqlcollaborative.Dbatools.Database.BackupHistory
                $historyObject.ComputerName = $server.ComputerName
                $historyObject.InstanceName = $server.ServiceName
                $historyObject.SqlInstance = $server.DomainInstanceName
                $historyObject.Database = $group.Group[0].Database
                $historyObject.UserName = $group.Group[0].UserName
                $historyObject.Start = ($group.Group.Start | Measure-Object -Minimum).Minimum
                $historyObject.End = ($group.Group.End | Measure-Object -Maximum).Maximum
                $historyObject.Duration = New-TimeSpan -Seconds ($group.Group.Duration | Measure-Object -Maximum).Maximum
                $historyObject.Path = $group.Group.Path
                $historyObject.TotalSize = $NULL
                $historyObject.Type = $group.Group[0].Type
                $historyObject.BackupSetId = $group.Group[0].BackupSetId
                $historyObject.DeviceType = $group.Group[0].DeviceType
                $historyObject.Software = $group.Group[0].Software
                $historyObject.FullName = $group.Group.Path
                $historyObject.FileList = $server.ConnectionContext.ExecuteWithResults($fileSql).Tables.Rows
                $historyObject.Position = $group.Group[0].Position
                $historyObject.FirstLsn = $group.Group[0].First_LSN
                $historyObject.DatabaseBackupLsn = $group.Group[0].database_backup_lsn
                $historyObject.CheckpointLsn = $group.Group[0].checkpoint_lsn
                $historyObject.LastLsn = $group.Group[0].Last_Lsn
                $historyObject.SoftwareVersionMajor = $group.Group[0].Software_Major_Version
                $historyObject.IsCopyOnly = if ($group.Group[0].is_copy_only -eq 1) {
                    $true
                }
                else {
                    $false
                }
                $groupResults += $historyObject
            }
            $groupResults | Sort-Object -Property LastLsn, Type
        }

    }

    END {}
}
function Get-CodePage {
    <#
        .SYNOPSIS
            Converts Microsoft's code page ID to human readable format

        .DESCRIPTION
            Converts Microsoft's code page ID to human readable format

        .PARAMETER Id
            The code page ID

        .EXAMPLE
            Get-CodePage 1252

            Returns a pscustomobject with id, alias and name
    #>
    [CmdletBinding()]
    param (
        [int]$id
    )
    process {
        $encoding = [System.Text.Encoding]::GetEncoding($id)
        $IncludeProps = 'CodePage', 'BodyName', 'EncodingName', 'HeaderName', 'WebName', 'IsSingleByte'
        Select-DefaultView -InputObject $encoding -Property $IncludeProps
    }
}
#ValidationTags#FlowControl,Pipeline#
function Get-DbaADObject {
    <#
    .SYNOPSIS
    Get-DbaADObject tries to facilitate searching AD with dbatools, which ATM can't require AD cmdlets.

    .DESCRIPTION
    As working with multiple domains, forests, ldap filters, partitions, etc is quite hard to grasp, let's try to do "the right thing" here and
    facilitate everybody's work with it. It either returns the exact matched result or None if it isn't found. You can inspect the raw object
    calling GetUnderlyingObject() on the returned object.

    .PARAMETER ADObject
    Pass in both the domain and the login name in Domain\sAMAccountName format (the one everybody is accustomed to)
    You can also pass a UserPrincipalName format (with the correct IdentityType, either with Domain\UserPrincipalName or UserPrincipalName@Domain)
    Beware: the "Domain" part of the UPN *can* be different from the real domain, see "UPN suffixes" (https://msdn.microsoft.com/en-us/library/windows/desktop/aa380525(v=vs.85).aspx)
    It's always best to pass the real domain name in (see the examples)
    For any other format, please beware that the domain part must always be specified (again, for the best result, before the slash)

    .PARAMETER Type
    You *should* always know what you are asking for. Please pass in Computer,Group or User to help speeding up the search

    .PARAMETER IdentityType
    By default objects are searched using sAMAccountName format, here you can pass different representation that need to match the passed in ADObject

    .PARAMETER Credential
    Use this credential to connect to the domain and search for the needed ADObject. If not passed, uses the current process' one.

    .PARAMETER SearchAllDomains
    Search for the object in all domains connected to the current one. If you are unsure what domain the object is coming from,
    using this switch will search through all domains in your forest and also in the ones that are trusted. This is HEAVY, but it can save
    some headaches.

    .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
    Author: Niphlod, https://github.com/niphlod
    Tags:
    dbatools PowerShell module (https://dbatools.io, [email protected])
    Copyright (C) 2016 Chrissy LeMaire
    License: MIT https://opensource.org/licenses/MIT

    .EXAMPLE
    Get-DbaADObject -ADObject "contoso\ctrlb" -Type User

    Searches in the contoso domain for a ctrlb user

    .EXAMPLE
    Get-DbaADObject -ADObject "[email protected]" -Type User -IdentityType UserPrincipalName

    Searches in the contoso domain for a ctrlb user using the UserPrincipalName format. Again, beware of the UPN suffixes in elaborate AD structures!

    .EXAMPLE
    Get-DbaADObject -ADObject "contoso\[email protected]" -Type User -IdentityType UserPrincipalName

    Searches in the contoso domain for a [email protected] user using the UserPrincipalName format. This kind of search is better than the previous one
    because it takes into account possible UPN suffixes

    .EXAMPLE
    Get-DbaADObject -ADObject "[email protected]" -Type User -IdentityType UserPrincipalName -SearchAllDomains

    As a last resort, searches in all the current forest for a [email protected] user using the UserPrincipalName format

    .EXAMPLE
    Get-DbaADObject -ADObject "contoso\sqlcollaborative" -Type Group

    Searches in the contoso domain for a sqlcollaborative group

    .EXAMPLE
    Get-DbaADObject -ADObject "contoso\SqlInstance2014$" -Type Group

    Searches in the contoso domain for a SqlInstance2014 computer (remember the ending $ for computer objects)

    .EXAMPLE
    Get-DbaADObject -ADObject "contoso\ctrlb" -Type User -EnableException

    Searches in the contoso domain for a ctrlb user, suppressing all error messages and throw exceptions that can be caught instead

#>
    [CmdletBinding()]
    param (
        [string[]]$ADObject,
        [ValidateSet("User", "Group", "Computer")]
        [string]$Type,

        [ValidateSet("DistinguishedName", "Guid", "Name", "SamAccountName", "Sid", "UserPrincipalName")]
        [string]$IdentityType = "SamAccountName",

        [PSCredential]$Credential,
        [switch]$SearchAllDomains,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        try {
            Add-Type -AssemblyName System.DirectoryServices.AccountManagement
        }
        catch {
            Stop-Function -Message "Failed to load the required module $($_.Exception.Message)" -EnableException $EnableException -InnerErrorRecord $_
            return
        }
        switch ($Type) {
            "User" {
                $searchClass = [System.DirectoryServices.AccountManagement.UserPrincipal]
            }
            "Group" {
                $searchClass = [System.DirectoryServices.AccountManagement.GroupPrincipal]
            }
            "Computer" {
                $searchClass = [System.DirectoryServices.AccountManagement.ComputerPrincipal]
            }
            default {
                $searchClass = [System.DirectoryServices.AccountManagement.Principal]
            }
        }

        function Get-DbaADObjectInternal($Domain, $IdentityType, $obj, $EnableException) {
            try {
                # can we simply resolve the passed domain ? This has the benefit of raising almost instantly if the domain is not valid
                $Context = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Domain', $Domain)
                $null = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($Context)
                if ($Credential) {
                    $ctx = New-Object System.DirectoryServices.AccountManagement.PrincipalContext('Domain', $Domain, $Credential.UserName, $Credential.GetNetworkCredential().Password)
                }
                else {
                    $ctx = New-Object System.DirectoryServices.AccountManagement.PrincipalContext('Domain', $Domain)
                }
                $found = $searchClass::FindByIdentity($ctx, $IdentityType, $obj)
                $found
            }
            catch {
                Stop-Function -Message "Errors trying to connect to the domain $Domain $($_.Exception.Message)" -EnableException $EnableException -InnerErrorRecord $_ -Target $ADObj
            }
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($ADObj in $ADObject) {
            # passing the domain as the first part before the \ wins always in defining the domain to search into
            $Splitted = $ADObj.Split("\")
            if ($Splitted.Length -ne 2) {
                # we can also take the object@domain format
                $Splitted = $ADObj.Split("@")
                if ($Splitted.Length -ne 2) {
                    Stop-Function -Message "You need to pass ADObject either DOMAIN\object or object@domain format" -Continue -EnableException $EnableException
                }
                else {
                    if ($IdentityType -ne 'UserPrincipalName') {
                        $obj, $Domain = $Splitted
                    }
                    else {
                        # if searching for a UserPrincipalName format without a specific domain passed in before the slash,
                        # we can assume there are no custom UPN suffixes in place
                        $obj, $Domain = $AdObj, $Splitted[1]
                    }
                }
            }
            else {
                $Domain, $obj = $Splitted
            }
            if ($SearchAllDomains) {
                Write-Message -Message "Searching for $obj under all domains in $IdentityType format" -Level VeryVerbose
                # if we're lucky, we can resolve the domain right away
                try {
                    Get-DbaADObjectInternal -Domain $Domain -IdentityType $IdentityType -obj $obj -EnableException $true
                }
                catch {
                    # if not, let's build up all domains
                    $ForestObject = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
                    $AllDomains = $ForestObject.Domains.Name
                    foreach ($ForestDomain in $AllDomains) {
                        Write-Message -Message "Searching for $obj under domain $ForestDomain in $IdentityType format" -Level VeryVerbose
                        $found = Get-DbaADObjectInternal -Domain $ForestDomain -IdentityType $IdentityType -obj $obj
                        if ($found) {
                            $found
                            break
                        }
                    }
                    # we are very unlucky, let's search also in all trusted domains
                    $AllTrusted = ($ForestObject.GetAllTrustRelationships().TopLevelNames | Where-Object Status -eq 'Enabled').Name
                    foreach ($ForestDomain in $AllTrusted) {
                        Write-Message -Message "Searching for $obj under domain $ForestDomain in $IdentityType format" -Level VeryVerbose
                        $found = Get-DbaADObjectInternal -Domain $ForestDomain -IdentityType $IdentityType -obj $obj
                        if ($found) {
                            $found
                            break
                        }
                    }
                }
            }
            else {
                Write-Message -Message "Searching for $obj under domain $domain in $IdentityType format" -Level VeryVerbose
                Get-DbaADObjectInternal -Domain $Domain -IdentityType $IdentityType -obj $obj
            }
        }
    }
}

function Get-DbaDbPhysicalFile {
    <#
    .SYNOPSIS
    Gets raw information about physical files linked to databases

    .DESCRIPTION
    Fastest way to fetch just the paths of the physical files for every database on the instance, also for offline databases.
    Incidentally, it also fetches the paths for MMO and FS filegroups.
    This is partly already in Get-DbaDatabaseFile, but this internal needs to stay lean and fast, as it's heavily used in top-level functions

    .PARAMETER SqlInstance
    SMO object representing the SQL Server to connect to.

    .EXAMPLE
    Get-DbaDbPhysicalFile -SqlInstance server1\instance2

    .NOTES
        Author: Simone Bizzotto

        dbatools PowerShell module (https://dbatools.io, [email protected])
        Copyright (C) 2016 Chrissy LeMaire
        License: MIT https://opensource.org/licenses/MIT
    #>
    [CmdletBinding()]
    param(
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential
    )
    try {
        Write-Message -Level Verbose -Message "Connecting to $SqlInstance"
        $Server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
    }
    catch {
        Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $SqlInstance
        return
    }
    if ($Server.versionMajor -le 8) {
        $sql = "SELECT DB_NAME(db_id) AS Name, filename AS PhysicalName FROM sysaltfiles"
    }
    else {
        $sql = "SELECT DB_NAME(database_id) AS Name, physical_name AS PhysicalName FROM sys.master_files"
    }
    Write-Message -Level Debug -Message "$sql"
    try {
        $Server.Query($sql)
    }
 catch {
        throw "Error enumerating files"
    }
}
function Get-DbaFileStreamFolder {
    <#

    .SYNOPSIS
        Returns basic information about Filestream folders from a Sql Instance

    .DESCRIPTION
        Given a SQL Instance, and an optional list of databases returns the FileStream containing folders on that Instance. Without the Database parameter, all dbs with FileStream are returned

    .PARAMETER SqlInstance
        The Sql Server instance to be queries

    .PARAMETER SqlCredential
        A Sql Credential to connect to $SqlInstance

    .PARAMETER Database
        Database to be tested, multiple databases may be specified as a comma seperated list.

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .EXAMPLE
        Get-DbaFileStreamFolder -SqlInstance server1\instance2

        Returns all FileStream folders from server1\instance2

    .EXAMPLE
        Get-DbaFileStreamFolder -SqlInstance server1\instance2 -Database Archive

        Returns any FileStream folders from the Archive database on server1\instance2

    .NOTES
    Author:Stuart Moore (@napalmgram stuart-moore.com )


    Website: https://dbatools.io
    Copyright: (C) Chrissy LeMaire, [email protected]
    License: MIT https://opensource.org/licenses/MIT
    #>
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$Database,
        [switch]$EnableException
    )

    BEGIN {
        try {
            Write-Message -Level VeryVerbose -Message "Connecting to $SqlInstance." -Target $SqlInstance
            $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
        }
        catch {
            Stop-Function -Message "Failed to process Instance $SqlInstance." -InnerErrorRecord $_ -Target $SqlInstance -Continue
        }
    }

    PROCESS {
        $sql = "select d.name as 'dbname', mf.Physical_Name from sys.master_files mf inner join sys.databases d on mf.database_id = d.database_id
        where mf.type=2"
        $databases = @()
        if ($null -ne $Database) {
            ForEach ($db in $Database) {
                $databases += "'$db'"
            }
            $sql = $sql + " and d.name in ( $($databases -join ',') )"
        }

        $results = $server.ConnectionContext.ExecuteWithResults($sql).Tables.Rows | Select-Object * -ExcludeProperty  RowError, Rowstate, table, itemarray, haserrors
        foreach ($result in $results) {
            [PsCustomObject]@{
                ServerInstance   = $SqlInstance
                Database         = $result.dbname
                FileStreamFolder = $result.Physical_Name
            }
        }


    }

    END {}
}
function Get-DbaMessageLevelModifier {
<#
    .SYNOPSIS
        Returns all registered message level modifiers with similar name.
    
    .DESCRIPTION
        Returns all registered message level modifiers with similar name.
        
        Message level modifiers are created using New-DbaMessageLevelModifier and allow dynamically modifying the actual message level written by commands.
    
    .PARAMETER Name
        Default: "*"
        A name filter - only commands that are similar to the filter will be returned.
    
    .EXAMPLE
        PS C:\> Get-DbaMessageLevelModifier
        
        Returns all message level filters
    
    .EXAMPLE
        PS C:\> Get-DbaMessageLevelModifier -Name "mymodule.*"
        
        Returns all message level filters that start with "mymodule."
#>
    [CmdletBinding()]
    param (
        [string]
        $Name = "*"
    )
    
    ([Sqlcollaborative.Dbatools.Message.MessageHost]::MessageLevelModifiers.Values) | Where-Object Name -Like $Name
}
function Get-DbaRunspace {
    <#
    .SYNOPSIS
        Returns registered runspaces.

    .DESCRIPTION
        Returns a list of runspaces that have been registered with dbatools

    .PARAMETER Name
        Default: "*"
        Only registered runspaces of similar names are returned.

    .EXAMPLE
        PS C:\> Get-DbaRunspace

        Returns all registered runspaces

    .EXAMPLE
        PS C:\> Get-DbaRunspace -Name 'mymodule.maintenance'

        Returns the runspace registered under the name 'mymodule.maintenance'
#>
    [CmdletBinding()]
    param (
        [string]
        $Name = "*"
    )

    [Sqlcollaborative.Dbatools.Runspace.RunspaceHost]::Runspaces.Values | Where-Object Name -Like $Name
}
function Get-DbaService {
    <#
    .SYNOPSIS
        Uses WMI/CIM to scan for the existance of a specific windows services.

    .DESCRIPTION
        Uses WMI/CIM to scan for the existance of a specific windows services.

        Use Get-DbaSqlService if you are interested in scanning for sql server services exclusively.

    .PARAMETER ComputerName
        The computer to target. Uses localhost by default.

    .PARAMETER Name
        The name of the service to search for.

    .PARAMETER DisplayName
        The display-name of the service to search for.

    .PARAMETER Credential
        The credentials to use when connecting to the computer.

    .PARAMETER DoNotUse
        Connection Protocols that should not be used when retrieving the information.

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .EXAMPLE
        Get-DbaService -Name LanmanServer

        Returns information on the LanmanServer service from localhost.

    .EXAMPLE
        Get-ADComputer -Filter * | Get-DbaService -Name Browser

        First retrieves all computer accounts from active directory, then scans all of those computers for the browser service.
        Note: THis may take seriously long time, you may also want to filter out computers that are offline before scanning for services.

    .EXAMPLE
        Get-DbaService -ComputerName "server1","server2","server3" -Name Lanman%

        Scans the servers server1, server2 and server3 for all services whose name starts with 'lanman'
#>
    [CmdletBinding()]
    param (
        [string[]]
        $Name,

        [string[]]
        $DisplayName,

        [Parameter(ValueFromPipeline = $true)]
        [Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter[]]
        $ComputerName = $env:COMPUTERNAME,

        [System.Management.Automation.PSCredential]
        $Credential,

        [Sqlcollaborative.Dbatools.Connection.ManagementConnectionType[]]
        $DoNotUse,

        [switch]
        [Alias('Silent')]$EnableException
    )

    begin {
        Write-Message -Level InternalComment -Message "Starting"
        Write-Message -Level System -Message "Bound parameters: $($PSBoundParameters.Keys -join ", ")"

        if (-not (Test-Bound "Name") -and -not (Test-Bound "DisplayName")) {
            $Name = "%"
        }
    }
    process {
        :main foreach ($computer in $ComputerName) {
            Write-Message -Level VeryVerbose -Message "Processing queries to $($computer.ComputerName)" -Target $computer.ComputerName
            foreach ($serviceName in $Name) {
                Write-Message -Level Verbose -Message "Searching for services with name: $serviceName" -Target $computer.ComputerName
                try {
                    if (Test-Bound "Credential") { Get-DbaCmObject -Query "SELECT * FROM Win32_Service WHERE Name LIKE '$serviceName'" -ComputerName $computer.ComputerName -Credential $Credential -EnableException -DoNotUse $DoNotUse }
                    else { Get-DbaCmObject -Query "SELECT * FROM Win32_Service WHERE Name LIKE '$serviceName'" -ComputerName $computer.ComputerName -EnableException -DoNotUse $DoNotUse }
                }
                catch {
                    if ($_.CategoryInfo.Category -eq "OpenError") {
                        Stop-Function -Message "Failed to access computer $($computer.ComputerName)" -ErrorRecord $_ -Target $computer.ComputerName -Continue -ContinueLabel main
                    }
                    else {
                        Stop-Function -Message "Failed to retrieve service" -ErrorRecord $_ -Target $computer.ComputerName -Continue
                    }
                }
            }

            foreach ($serviceDisplayName in $DisplayName) {
                Write-Message -Level Verbose -Message "Searching for services with display name: $serviceDisplayName" -Target $computer.ComputerName
                try {
                    if (Test-Bound "Credential") { Get-DbaCmObject -Query "SELECT * FROM Win32_Service WHERE DisplayName LIKE '$serviceDisplayName'" -ComputerName $computer.ComputerName -Credential $Credential -EnableException -DoNotUse $DoNotUse }
                    else { Get-DbaCmObject -Query "SELECT * FROM Win32_Service WHERE DisplayName LIKE '$serviceDisplayName'" -ComputerName $computer.ComputerName -EnableException -DoNotUse $DoNotUse }
                }
                catch {
                    if ($_.CategoryInfo.Category -eq "OpenError") {
                        Stop-Function -Message "Failed to access computer $($computer.ComputerName)" -ErrorRecord $_ -Target $computer.ComputerName -Continue -ContinueLabel main
                    }
                    else {
                        Stop-Function -Message "Failed to retrieve service" -ErrorRecord $_ -Target $computer.ComputerName -Continue
                    }
                }
            }
        }
    }
    end {
        Write-Message -Level InternalComment -Message "Ending"
    }
}
function Get-DBASQLServiceErrorMessage {
    <#
    .SYNOPSIS
    Internal function. Returns the list of error code messages for Windows service management.

#>
    param(
        [parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 1)]
        [int]$ErrorNumber
    )
    $returnCodes = @("The request was accepted.",
        "The request is not supported.",
        "The user did not have the necessary access.",
        "The service cannot be stopped because other services that are running are dependent on it.",
        "The requested control code is not valid, or it is unacceptable to the service.",
        "The requested control code cannot be sent to the service because the state of the service (Win32_BaseService.State property) is equal to 0, 1, or 2.",
        "The service has not been started.",
        "The service did not respond to the start request in a timely fashion.",
        "Unknown failure when starting the service.",
        "The directory path to the service executable file was not found.",
        "The service is already running.",
        "The database to add a new service is locked.",
        "A dependency this service relies on has been removed from the system.",
        "The service failed to find the service needed from a dependent service.",
        "The service has been disabled from the system.",
        "The service does not have the correct authentication to run on the system.",
        "This service is being removed from the system.",
        "The service has no execution thread.",
        "The service has circular dependencies when it starts.",
        "A service is running under the same name.",
        "The service name has invalid characters.",
        "Invalid parameters have been passed to the service.",
        "The account under which this service runs is either invalid or lacks the permissions to run the service.",
        "The service exists in the database of services available from the system.",
        "The service is currently paused in the system.")
    if ($ErrorNumber -in 0..($returnCodes.Length - 1)) { Return $returnCodes[$ErrorNumber] }
    else { Return "Unknown error." }
}
function Get-DecryptedObject {
            <#
            .SYNOPSIS
                Internal function.

                This function is heavily based on Antti Rantasaari's script at http://goo.gl/wpqSib
                Antti Rantasaari 2014, NetSPI
                License: BSD 3-Clause http://opensource.org/licenses/BSD-3-Clause
            #>
    param (
        [Parameter(Mandatory)]
        [Microsoft.SqlServer.Management.Smo.Server]$SqlInstance,
        [Parameter(Mandatory)]
        [ValidateSet("LinkedServer", "Credential")]
        [string]$Type,
        [switch]$EnableException
    )
    
    $server = $SqlInstance
    $sourceName = $server.Name
    
    # Query Service Master Key from the database - remove padding from the key
    # key_id 102 eq service master key, thumbprint 3 means encrypted with machinekey
    $sql = "SELECT substring(crypt_property,9,len(crypt_property)-8) as smk FROM sys.key_encryptions WHERE key_id=102 and (thumbprint=0x03 or thumbprint=0x0300000001)"
    try {
        $smkbytes = $server.Query($sql).smk
    }
    catch {
        Stop-Function -Message "Can't execute query on $sourcename" -Target $server -ErrorRecord $_
        return
    }
    
    $sourceNetBios = Resolve-NetBiosName $server
    $instance = $server.InstanceName
    $serviceInstanceId = $server.ServiceInstanceId
    
    # Get entropy from the registry - hopefully finds the right SQL server instance
    try {
        [byte[]]$entropy = Invoke-Command2 -Raw -Credential $Credential -ComputerName $sourceNetBios -argumentlist $serviceInstanceId {
            $serviceInstanceId = $args[0]
            $entropy = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$serviceInstanceId\Security\").Entropy
            return $entropy
        }
    }
    catch {
       Stop-Function -Message "Can't access registry keys on $sourceName. Do you have administrative access to the Windows registry on $sourcename? Otherwise, we're out of ideas." -Target $source
        return
    }
    
    # Decrypt the service master key
    try {
        $serviceKey = Invoke-Command2 -Raw -Credential $Credential -ComputerName $sourceNetBios -ArgumentList $smkbytes, $Entropy {
            Add-Type -AssemblyName System.Security
            Add-Type -AssemblyName System.Core
            $smkbytes = $args[0]; $Entropy = $args[1]
            $serviceKey = [System.Security.Cryptography.ProtectedData]::Unprotect($smkbytes, $Entropy, 'LocalMachine')
            return $serviceKey
        }
    }
    catch {
        Stop-Function -Message "Can't unprotect registry data on $sourcename. Do you have administrative access to the Windows registry on $sourcename? Otherwise, we're out of ideas." -Target $source
        return
    }
    
    # Choose the encryption algorithm based on the SMK length - 3DES for 2008, AES for 2012
    # Choose IV length based on the algorithm
    if (($serviceKey.Length -ne 16) -and ($serviceKey.Length -ne 32)) {
        Write-Message -Level Verbose -Message "ServiceKey found: $serviceKey.Length"
        Stop-Function -Message "Unknown key size. Do you have administrative access to the Windows registry on $sourcename? Otherwise, we're out of ideas." -Target $source
        return
    }
    
    if ($serviceKey.Length -eq 16) {
        $decryptor = New-Object System.Security.Cryptography.TripleDESCryptoServiceProvider
        $ivlen = 8
    }
    elseif ($serviceKey.Length -eq 32) {
        $decryptor = New-Object System.Security.Cryptography.AESCryptoServiceProvider
        $ivlen = 16
    }
    
            <#
                Query link server password information from the Db.
                Remove header from pwdhash, extract IV (as iv) and ciphertext (as pass)
                Ignore links with blank credentials (integrated auth ?)
            #>
    try {
        if (-not $server.IsClustered) {
            $connString = "Server=ADMIN:$sourceNetBios\$instance;Trusted_Connection=True"
        }
        else {
            $dacEnabled = $server.Configuration.RemoteDacConnectionsEnabled.ConfigValue
            
            if ($dacEnabled -eq $false) {
                If ($Pscmdlet.ShouldProcess($server.Name, "Enabling DAC on clustered instance.")) {
                    Write-Message -Level Verbose -Message "DAC must be enabled for clusters, even when accessed from active node. Enabling."
                    $server.Configuration.RemoteDacConnectionsEnabled.ConfigValue = $true
                    $server.Configuration.Alter()
                }
            }
            
            $connString = "Server=ADMIN:$sourceName;Trusted_Connection=True"
        }
    }
    catch {
        Stop-Function -Message "Failure enabling DAC on $sourcename" -Target $source -ErrorRecord $_
    }
    
    <# NOTE: This query is accessing syslnklgns table. Can only be done via the DAC connection #>
    
    $sql = switch ($Type) {
        "LinkedServer" {
            "SELECT sysservers.srvname,
                    syslnklgns.Name,
                    substring(syslnklgns.pwdhash,5,$ivlen) iv,
                    substring(syslnklgns.pwdhash,$($ivlen + 5),
                    len(syslnklgns.pwdhash)-$($ivlen + 4)) pass
                FROM master.sys.syslnklgns
                    inner join master.sys.sysservers
                    on syslnklgns.srvid=sysservers.srvid
                WHERE len(pwdhash) > 0"
        }
        "Credential" {
            "SELECT QUOTENAME(name) AS name,credential_identity,substring(imageval,5,$ivlen) iv, substring(imageval,$($ivlen + 5),len(imageval)-$($ivlen + 4)) pass from sys.Credentials cred inner join sys.sysobjvalues obj on cred.credential_id = obj.objid where valclass=28 and valnum=2"
        }
    }
    
    Write-Message -Level Debug -Message $sql
    # Get entropy from the registry
    try {
        $results = Invoke-Command2 -Raw -Credential $Credential -ComputerName $sourceNetBios -ArgumentList $connString, $sql {
            $connString = $args[0]; $sql = $args[1]
            $conn = New-Object System.Data.SqlClient.SQLConnection($connString)
            $conn.open()
            $cmd = New-Object System.Data.SqlClient.SqlCommand($sql, $conn);
            $dt = New-Object System.Data.DataTable
            $dt.Load($cmd.ExecuteReader())
            $conn.Close()
            $conn.Dispose()
            return $dt
        }
    }
    catch {
        Stop-Function -Message "Can't establish local DAC connection on $sourcename." -Target $server -ErrorRecord $_
        return
    }
    
    if ($server.IsClustered -and $dacEnabled -eq $false) {
        If ($Pscmdlet.ShouldProcess($server.Name, "Disabling DAC on clustered instance.")) {
            try {
                Write-Message -Level Verbose -Message "Setting DAC config back to 0."
                $server.Configuration.RemoteDacConnectionsEnabled.ConfigValue = $false
                $server.Configuration.Alter()
            }
            catch {
                Stop-Function -Message "Can't establish local DAC connection on $sourcename" -Target $server -ErrorRecord $_
                return
            }
        }
    }
    
    # Go through each row in results
    foreach ($result in $results) {
        # decrypt the password using the service master key and the extracted IV
        $decryptor.Padding = "None"
        $decrypt = $decryptor.Createdecryptor($serviceKey, $result.iv)
        $stream = New-Object System.IO.MemoryStream ( , $result.pass)
        $crypto = New-Object System.Security.Cryptography.CryptoStream $stream, $decrypt, "Write"
        
        $crypto.Write($result.pass, 0, $result.pass.Length)
        [byte[]]$decrypted = $stream.ToArray()
        
        # convert decrypted password to unicode
        $encode = New-Object System.Text.UnicodeEncoding
        
        # Print results - removing the weird padding (8 bytes in the front, some bytes at the end)...
        # Might cause problems but so far seems to work.. may be dependant on SQL server version...
        # If problems arise remove the next three lines..
        $i = 8; foreach ($b in $decrypted) { if ($decrypted[$i] -ne 0 -and $decrypted[$i + 1] -ne 0 -or $i -eq $decrypted.Length) { $i -= 1; break; }; $i += 1; }
        $decrypted = $decrypted[8 .. $i]
        
        if ($Type -eq "LinkedServer") {
            $name = $result.srvname
            $identity = $result.Name
        }
        else {
            $name = $result.name
            $identity = $result.credential_identity
        }
        [pscustomobject]@{
            Name = $name
            Identity = $identity
            Password = $encode.GetString($decrypted)
        }
    }
}
function Get-DirectoryRestoreFile {
    <#
.SYNOPSIS
Internal Function to get SQL Server backfiles from a specified folder

.DESCRIPTION
Takes path, checks for validity. Scans for usual backup file
#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [string]$Path,
        [switch]$Recurse,
        [Alias('Silent')]
        [switch]$EnableException
    )

    Write-Message -Level Verbose -Message "Starting"
    Write-Message -Level Verbose -Message "Checking Path"
    if ((Test-Path $Path) -ne $true) {
        Stop-Function -Message "$Path is not reachable"
        return
    }
    #Path needs to end \* to use includes, which is faster than Where-Object
    $PathCheckArray = $path.ToCharArray()
    if ($PathCheckArray[-2] -eq '\' -and $PathCheckArray[-1] -eq '*') {
        #We're good
    }
    elseif ($PathCheckArray[-2] -ne '\' -and $PathCheckArray[-1] -eq '*') {
        $Path = ($PathCheckArray[0..(($PathCheckArray.length) - 2)] -join ('')) + "\*"
    }
    elseif ($PathCheckArray[-2] -eq '\' -and $PathCheckArray[-1] -ne '*') {
        #Append a * to the end
        $Path = "$Path*"
    }
    elseif ($PathCheckArray[-2] -ne '\' -and $PathCheckArray[-1] -ne '*') {
        #Append a \* to the end
        $Path = "$Path\*"
    }
    Write-Message -Level Verbose -Message "Scanning $path"
    $Results = Get-ChildItem -path $Path -Recurse:$Recurse | Where-Object {$_.PsIsContainer -eq $false}
    return $Results
}
function Get-ErrorMessage {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [System.Management.Automation.ErrorRecord]$Record
    )
    process {
        $innermessage = $Record.Exception.InnerException.InnerException.InnerException.InnerException.InnerException.Message
        if (-not $innermessage) { $innermessage = $Record.Exception.InnerException.InnerException.InnerException.InnerException.Message }
        if (-not $innermessage) { $innermessage = $Record.Exception.InnerException.InnerException.InnerException.Message }
        if (-not $innermessage) { $innermessage = $Record.Exception.InnerException.InnerException.Message }
        if (-not $innermessage) { $innermessage = $Record.Exception.InnerException.Message }
        if (-not $innermessage) { $innermessage = $Record.Exception.Message }
        return $innermessage
    }
}
function Get-JobList {
    <#
    .SYNOPSIS
        Helper function to get SQL Agent jobs.
    .DESCRIPTION
        Helper function to get all SQL Agent jobs or provide filter
    .PARAMETER SqlInstance
        SQL Server instance
    .PARAMETER SqlCredential
        Credential to use if SqlInstance did not include it.
    .PARAMETER JobFilter
        Object of jobs to filter on, also supports wildcard patterns
    .PARAMETER StepFilter
        Object of job steps to filter on, also supports wildcard patterns
    .PARAMETER Not
        Reverse results where object returned excludes filtered content.
    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .EXAMPLE
        Get-JobList -SqlInstance sql2016

        Returns the full JobServer.Jobs object found on sql2016
    .EXAMPLE
        Get-JobList -SqlInstance sql2016 -JobFilter '*job*'

        Returns the Job object for each job name found to have "job" in the name on sql2016
    .EXAMPLE
        Get-JobList -SqlInstance sql2016 -JobFilter '*job*' -Not

        Returns any Job object that does not have "job" in the name on sql2016
    .EXAMPLE
        Get-JobList -SqlInstance YourServer -JobFilter 'JobName'

        Returns the Job object where the job name is 'JobName' on sql2016
    .EXAMPLE
        Get-JobList -SqlInstance YourServer -JobFilter 'JobName' -Not

        Returns any Job object where the job name is not 'JobName' on sql2016
    .EXAMPLE
        Get-JobList -SqlInstance YourServer -JobFilter job_3_upload, job_3_download

        Returns the Job object for where job is job_3_upload or job_3_download on sql2016
    .EXAMPLE
        Get-JobList -SqlInstance YourServer -JobFilter job_3_upload, job_3_download -Not

        Returns any Job object where job is not job_3_upload or job_3_download on sql2016
    .NOTES
        Author: Shawn Melton (@wsmelton)

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT
    #>
    [cmdletbinding()]
    param(
        [Parameter(ValueFromPipeline = $true)]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$JobFilter,
        [string[]]$StepFilter,
        [switch]$Not,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential

        $jobs = $server.JobServer.Jobs
        if ( (Test-Bound 'JobFilter') -or (Test-Bound 'StepFilter') ) {
            if ($JobFilter.Count -gt 1) {
                if ($Not) {
                    $jobs | Where-Object Name -NotIn $JobFilter
                }
                else {
                    $jobs | Where-Object Name -In $JobFilter
                }
            }
            else {
                foreach ($job in $jobs) {
                    if ($JobFilter -match '`*') {
                        if ($Not) {
                            $job | Where-Object Name -NotLike $JobFilter
                        }
                        else {
                            $job | Where-Object Name -Like $JobFilter
                        }
                    }
                    else {
                        if ($Not) {
                            $job | Where-Object Name -NE $JobFilter
                        }
                        else {
                            $job | Where-Object Name -EQ $JobFilter
                        }
                    }
                    if ($StepFilter -match '`*') {
                        if ($Not) {
                            $stepFound = $job.JobSteps | Where-Object Name -NotLike $StepFilter
                            if ($stepFound.Count -gt 0) {
                                $job
                            }
                        }
                        else {
                            $stepFound = $job.JobSteps | Where-Object Name -Like $StepFilter
                            if ($stepFound.Count -gt 0) {
                                $job
                            }
                        }
                    }
                    elseif ($StepName.Count -gt 1) {
                        if ($Not) {
                            $stepFound = $job.JobSteps | Where-Object Name -NotIn $StepName
                            if ($stepFound.Count -gt 0) {
                                $job
                            }
                        }
                        else {
                            $stepFound = $job.JobSteps | Where-Object Name -In $StepName
                            if ($stepFound.Count -gt 0) {
                                $job
                            }
                        }
                    }
                    else {
                        if ($Not) {
                            $stepFound = $job.JobSteps | Where-Object Name -NE $StepName
                            if ($stepFound.Count -gt 0) {
                                $job
                            }
                        }
                        else {
                            $stepFound = $job.JobSteps | Where-Object Name -EQ $StepName
                            if ($stepFound.Count -gt 0) {
                                $job
                            }
                        }
                    }
                }
            }
        }
        else {
            $jobs
        }
    }
}
function Get-Language {
    <#
        .SYNOPSIS
            Converts Microsoft's language ID to human readable format

        .DESCRIPTION
            Converts Microsoft's language ID to human readable format

        .PARAMETER Id
            The language ID

        .EXAMPLE
            Get-Language 1033

            Returns a pscustomobject with id, alias and name
    #>
    [CmdletBinding()]
    param (
        [int]$id
    )
    process {

        $culture = [System.Globalization.CultureInfo]::GetCultureInfo($id)

        $excludeProps = 'Parent', 'IetfLanguageTag', 'CompareInfo', 'TextInfo', 'IsNeutralCulture', 'NumberFormat', 'DateTimeFormat', 'Calendar'
        , 'OptionalCalendars', 'UseUserOverride', 'IsReadOnly'
        Select-DefaultView -InputObject $culture -ExcludeProperty $excludeProps
    }
}
function Get-OfflineSqlFileStructure {
    <#
.SYNOPSIS
Internal function. Returns dictionary object that contains file structures for SQL databases.

#>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [object]$SqlInstance,
        [Parameter(Mandatory = $true, Position = 1)]
        [string]$dbname,
        [Parameter(Mandatory = $true, Position = 2)]
        [object]$filelist,
        [Parameter(Mandatory = $false, Position = 3)]
        [bool]$ReuseSourceFolderStructure,
        [PSCredential]$SqlCredential
    )

    $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential

    $destinationfiles = @{ };
    $logfiles = $filelist | Where-Object { $_.Type -eq "L" }
    $datafiles = $filelist | Where-Object { $_.Type -ne "L" }
    $filestream = $filelist | Where-Object { $_.Type -eq "S" }

    if ($filestream) {
        $sql = "select coalesce(SERVERPROPERTY('FilestreamConfiguredLevel'),0) as fs"
        $fscheck = $server.databases['master'].ExecuteWithResults($sql)
        if ($fscheck.tables.fs -eq 0) { return $false }
    }

    # Data Files
    foreach ($file in $datafiles) {
        # Destination File Structure
        $d = @{ }
        if ($ReuseSourceFolderStructure -eq $true) {
            $d.physical = $file.PhysicalName
        }
        else {
            $directory = Get-SqlDefaultPaths $server data
            $filename = Split-Path $($file.PhysicalName) -leaf
            $d.physical = "$directory\$filename"
        }

        $d.logical = $file.LogicalName
        $destinationfiles.add($file.LogicalName, $d)
    }

    # Log Files
    foreach ($file in $logfiles) {
        $d = @{ }
        if ($ReuseSourceFolderStructure) {
            $d.physical = $file.PhysicalName
        }
        else {
            $directory = Get-SqlDefaultPaths $server log
            $filename = Split-Path $($file.PhysicalName) -leaf
            $d.physical = "$directory\$filename"
        }

        $d.logical = $file.LogicalName
        $destinationfiles.add($file.LogicalName, $d)
    }

    return $destinationfiles
}
function Get-PasswordHash {
    <#
    .SYNOPSIS
    Generates a password hash for SQL Server login

    .DESCRIPTION
    Generates a hash string based on the plaintext or securestring password and a SQL Server version. Salt is optional

    .PARAMETER Password
    Either plain text or Securestring password

    .PARAMETER SqlMajorVersion
    Major version of the SQL Server. Defines the hash algorithm.

    .PARAMETER byteSalt
    Optional. Inserts custom salt into the hash instead of randomly generating new salt

    .NOTES
    Tags: Login, Internal
    Author: Kirill Kravtsov (@nvarscar)
    dbatools PowerShell module (https://dbatools.io, [email protected])
    Copyright (C) 2016 Chrissy LeMaire
    License: MIT https://opensource.org/licenses/MIT

    .EXAMPLE
    Get-PasswordHash $securePassword 11

    Generates password hash for SQL 2012

    .EXAMPLE
    Get-PasswordHash $securePassword 9 $byte

    Generates password hash for SQL 2005 using custom salt from the $byte variable

#>
    param (
        [object]$Password,
        $SqlMajorVersion,
        [byte[]]$byteSalt
    )
    #Choose hash algorithm
    if ($SqlMajorVersion -lt 11) {
        $algorithm = 'SHA1'
        $hashVersion = '0100'
    }
    else {
        $algorithm = 'SHA512'
        $hashVersion = '0200'
    }

    #Generate salt
    if (!$byteSalt) {
        0 .. 3 | ForEach-Object { $byteSalt += Get-Random -Minimum 0 -Maximum 255 }
    }

    #Convert salt to a hex string
    [string]$stringSalt = ""
    $byteSalt | ForEach-Object { $stringSalt += ("{0:X}" -f $_).PadLeft(2, "0") }

    #Extract password
    if ($Password.GetType().Name -eq 'SecureString') {
        $cred = New-Object System.Management.Automation.PSCredential -ArgumentList 'foo', $Password
        $plainPassword = $cred.GetNetworkCredential().Password
    }
    else {
        $plainPassword = $Password
    }
    #Get byte representation of the password string
    $enc = [system.Text.Encoding]::Unicode
    $data = $enc.GetBytes($plainPassword)
    #Run hash algorithm
    $hash = [Security.Cryptography.HashAlgorithm]::Create($algorithm)
    $bytes = $hash.ComputeHash($data + $byteSalt)
    #Construct hex string
    $hashString = "0x$hashVersion$stringSalt"
    $bytes | ForEach-Object { $hashString += ("{0:X2}" -f $_).PadLeft(2, "0") }
    #Add UPPERCASE hash for SQL 2000 and lower
    if ($SqlMajorVersion -lt 9) {
        $data = $enc.GetBytes($plainPassword.ToUpper())
        $bytes = $hash.ComputeHash($data + $byteSalt)
        $bytes | ForEach-Object { $hashString += ("{0:X2}" -f $_).PadLeft(2, "0") }
    }
    return $hashString
}
function Get-RegServerGroupReverseParse ($object) {
    if ($object.Name -eq 'DatabaseEngineServerGroup') {
        $object.Name
    }
    else {
        $name = @()
        do {
            $name += $object.Name.Split("\")[0]
            $object = $object.Parent
        }
        until ($object.Name -eq 'DatabaseEngineServerGroup')
        
        [array]::Reverse($name)
        $name -join '\'
    }
}
function Get-RegServerParent {
    [cmdletbinding()]
    param (
        [object]$InputObject
    )
    process {
        $parentcount = 0
        do {
            if ($null -ne $InputObject.Parent) {
                $InputObject = $InputObject.Parent
            }
        }
        until ($null -ne $InputObject.ServerConnection -or $parentcount++ -gt 10)
        
        
        if ($parentcount -lt 10) {
            $InputObject
        }
    }
}
function Get-RestoreContinuableDatabase {
    <#
    .SYNOPSIS
    Gets a list of databases from a SQL instance that are in a state for further restores

    .DESCRIPTION
    Takes a SQL instance and checks for databases with a redo_start_lsn value, and returns the database name and that value
    -gt SQl 2005 it comes from master.sys.master_files
    -eq SQL 2000 DBCC DBINFO
#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [object]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    try {
        $Server = Connect-SqlInstance -Sqlinstance $SqlInstance -SqlCredential $SqlCredential
    }
    catch {
        Write-Message -Level Warning -Message "Cannot connect to $SqlInstance"
        break
    }
    if ($Server.VersionMajor -ge 9) {
        $sql = "select distinct db_name(database_id) as 'Database', redo_start_lsn, redo_start_fork_guid as 'FirstRecoveryForkID' from master.sys.master_files where redo_start_lsn is not NULL"
    }
    else {
        $sql = "
              CREATE TABLE #db_info
                (
                ParentObject NVARCHAR(128) COLLATE database_default ,
                Object       NVARCHAR(128) COLLATE database_default,
                Field        NVARCHAR(128) COLLATE database_default,
                Value        SQL_VARIANT
                )"
    }
    $server.ConnectionContext.ExecuteWithResults($sql).Tables.Rows
}
function Get-SaLoginName {
    <#
    .SYNOPSIS
    Gets the login matching the standard "sa" user

    .DESCRIPTION
    Gets the login matching the standard "sa" user, useful in case of renames

    .PARAMETER SqlInstance
    The SQL Server instance.

    .PARAMETER SqlCredential
    Allows you to login to servers using SQL Logins instead of Windows Authentication (AKA Integrated or Trusted).

    .EXAMPLE
    Get-SaLoginName -SqlInstance base\sql2016

    .NOTES
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT
    #>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [object]$SqlInstance,
        [PSCredential]$SqlCredential
    )

    $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
    $saname = ($server.logins | Where-Object { $_.id -eq 1 }).Name

    return $saname
}
function Get-SmoServerForDynamicParams {
    ##############################
    # THIS DOES NOT SEEM TO BE USED
    ##############################
    if ($fakeBoundParameter.length -eq 0) { return }

    $SqlInstance = $fakeBoundParameter['SqlInstance']
    $sqlcredential = $fakeBoundParameter['SqlCredential']

    if ($null -eq $SqlInstance) {
        $SqlInstance = $fakeBoundParameter['sqlinstance']
    }
    if ($null -eq $SqlInstance) {
        $SqlInstance = $fakeBoundParameter['source']
    }
    if ($null -eq $sqlcredential) {
        $sqlcredential = $fakeBoundParameter['Credential']
    }

    if ($SqlInstance) {
        Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential -ParameterConnection
    }
}
function Get-SqlCmdVars {
    <#
        .SYNOPSIS
            Retrieves the values of PowerShell parameters and updates values of SqlmdVars listed in the publish.xml.

        .DESCRIPTION
            Attempt to resolve SQLCmd variables via matching powershell variables explicitly defined in the current context.
            To try and avoid 'bad' default values getting deployed, block a deployment if we have SqlCmd variables that aren't defined in current context.
            Function has one reference and is executed when the "getSqlCmdVars" switch is included.
        .PARAMETER SqlCommandVariableValues
            Mandatory. The SqlCommandVariableValues from the DeployOptions property in the Microsoft.SqlServer.Dac.DacProfile
        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Richie lee (@bzzzt_io)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
        .LINK
            https://dbatools.io/Test-Noun

        .EXAMPLE
        Imagine content of MyDbProject.publish.xml is as follows -
        <?xml version="1.0" encoding="utf-8"?>
        <Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
        <PropertyGroup>
            <IncludeCompositeObjects>True</IncludeCompositeObjects>
            <TargetDatabaseName>MyDbProject</TargetDatabaseName>
            <DeployScriptFileName>MyDbProject.sql</DeployScriptFileName>
            <TargetConnectionString>Data Source=.;Integrated Security=True;Persist Security Info=False;Pooling=False;MultipleActiveResultSets=False;Connect Timeout=60;Encrypt=False;TrustServerCertificate=True</TargetConnectionString>
            <BlockOnPossibleDataLoss>True</BlockOnPossibleDataLoss>
            <CreateNewDatabase>False</CreateNewDatabase>
            <ProfileVersionNumber>1</ProfileVersionNumber>
        </PropertyGroup>
        <ItemGroup>
            <SqlCmdVariable Include="DeployTag">
            <Value>OldValue</Value>
            </SqlCmdVariable>
        </ItemGroup>
        </Project>
        We will need one PowerShell parameter named $DeployTag to update the value

        The following scenario will fail as no $deployTag -
        "
            $publishXml =  "C:\MyDbProject\bin\Debug\MyDbProject.publish.xml"
            $dacProfile = [Microsoft.SqlServer.Dac.DacProfile]::Load($publishXml)
            Get-SqlCmdVars $dacProfile.DeployOptions.SqlCommandVariableValues -EnableException
        "
        This scenario will pass.
        "
            $deployTag = "NewValue"
            $publishXml =  "C:\MyDbProject\bin\Debug\MyDbProject.publish.xml"
            $dacProfile = [Microsoft.SqlServer.Dac.DacProfile]::Load($publishXml)
            Get-SqlCmdVars $dacProfile.DeployOptions.SqlCommandVariableValues -EnableException
        "
    #>
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        $SqlCommandVariableValues,
        [switch]$EnableException
    )
    $missingVariables = @()
    $keys = $($SqlCommandVariableValues.Keys)
    foreach ($var in $keys) {
        if (Test-Path variable:$var) {
            $value = Get-Variable $var -ValueOnly
            $SqlCommandVariableValues[$var] = $value
        }
        else {
            $missingVariables += $var
        }
    }
    if ($missingVariables.Count -gt 0) {
        $errorMsg = 'The following SqlCmd variables are not defined in the session (but are defined in the publish profile): {0}' -f ($missingVariables -join " `n")
        Stop-Function -Message $errorMsg -EnableException $EnableException
    }
}
function Get-SqlDefaultPaths {
    <#
    .SYNOPSIS
        Internal function. Returns the default data and log paths for SQL Server. Needed because SMO's server.defaultpath is sometimes null.
#>
    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "")]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [Alias("ServerInstance", "SqlServer")]
        [object]$SqlInstance,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$filetype,
        [PSCredential]$SqlCredential
    )

    try {
        if ($SqlInstance -isnot [Microsoft.SqlServer.Management.Smo.SqlSmoObject]) {
            $Server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
        }
        else {
            $server = $SqlInstance
        }
    }
    catch {
        Write-Message -Lvel Warning -Message "Cannot connect to $SqlInstance"
        break
    }
    switch ($filetype) { "mdf" { $filetype = "data" } "ldf" { $filetype = "log" } }

    if ($filetype -eq "log") {
        # First attempt
        $filepath = $server.DefaultLog
        # Second attempt
        if ($filepath.Length -eq 0) { $filepath = $server.Information.MasterDbLogPath }
        # Third attempt
        if ($filepath.Length -eq 0) {
            $sql = "select SERVERPROPERTY('InstanceDefaultLogPath') as physical_name"
            $filepath = $server.ConnectionContext.ExecuteScalar($sql)
        }
    }
    else {
        # First attempt
        $filepath = $server.DefaultFile
        # Second attempt
        if ($filepath.Length -eq 0) { $filepath = $server.Information.MasterDbPath }
        # Third attempt
        if ($filepath.Length -eq 0) {
            $sql = "select SERVERPROPERTY('InstanceDefaultDataPath') as physical_name"
            $filepath = $server.ConnectionContext.ExecuteScalar($sql)
        }
    }

    if ($filepath.Length -eq 0) { throw "Cannot determine the required directory path" }
    $filepath = $filepath.TrimEnd("\")
    return $filepath
}
function Get-SqlDefaultSpConfigure {
    <#
        .SYNOPSIS
        Internal function. Returns the default sp_configure options for a given version of SQL Server.

        .NOTES
        Server Configuration Options BOL (links subject to change):
        SQL Server 2017 - https://technet.microsoft.com/en-us/library/ms189631(v=sql.140).aspx
        SQL Server 2016 - https://technet.microsoft.com/en-us/library/ms189631(v=sql.130).aspx
        SQL Server 2014 - http://technet.microsoft.com/en-us/library/ms189631(v=sql.120).aspx
        SQL Server 2012 - http://technet.microsoft.com/en-us/library/ms189631(v=sql.110).aspx
        SQL Server 2008 R2 - http://technet.microsoft.com/en-us/library/ms189631(v=sql.105).aspx
        SQL Server 2008 - http://technet.microsoft.com/en-us/library/ms189631(v=sql.100).aspx
        SQL Server 2005 - http://technet.microsoft.com/en-us/library/ms189631(v=sql.90).aspx
        SQL Server 2000 - http://technet.microsoft.com/en-us/library/aa196706(v=sql.80).aspx (requires PDF download)

        .EXAMPLE
        Get-SqlDefaultSpConfigure -SqlVersion 11
        Returns a list of sp_configure (sys.configurations) items for SQL 2012.

#>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [Alias("Version")]
        [object]$SqlVersion
    )

    switch ($SqlVersion) {

        #region SQL2000
        8 {
            [pscustomobject]@{
                "affinity mask"                  = 0
                "allow updates"                  = 0
                "aweenabled"                     = 0
                "c2 audit mode"                  = 0
                "cost threshold for parallelism" = 5
                "Cross DB Ownership Chaining"    = 0
                "cursor threshold"               = -1
                "default full-text language"     = 1033
                "default language"               = 0
                "fill factor (%)"                = 0
                "index create memory (KB)"       = 0
                "lightweight pooling"            = 0
                "locks"                          = 0
                "max degree of parallelism"      = 0
                "max server memory (MB)"         = 2147483647
                "max text repl size (B)"         = 65536
                "max worker threads"             = 255
                "media retention"                = 0
                "min memory per query (KB)"      = 1024
                "min server memory (MB)"         = 0
                "Using Nested Triggers"          = 1
                "network packet size (B)"        = 4096
                "open objects"                   = 0
                "priority boost"                 = 0
                "query governor cost limit"      = 0
                "query wait (s)"                 = -1
                "recovery interval (min)"        = 0
                "remoteaccess"                   = 1
                "remotelogin timeout"            = 20
                "remote proc trans"              = 0
                "remote query timeout (s)"       = 600
                "scan for startup procs"         = 0
                "set working set size"           = 0
                "show advanced options"          = 0
                "two digityear cutoff"           = 2049
                "user connections"               = 0
                "user options"                   = 0
            }
        }
        #endregion SQL2000

        #region SQL2005
        9 {
            [pscustomobject]@{
                "Ad Hoc Distributed Queries"         = 0
                "affinity I/O mask"                  = 0
                "affinity64 I/O mask"                = 0
                "affinity mask"                      = 0
                "affinity64 mask"                    = 0
                "Agent XPs"                          = 0
                "allow updates"                      = 0
                "awe enabled"                        = 0
                "blocked process threshold (s)"      = 0
                "c2 audit mode"                      = 0
                "clr enabled"                        = 0
                "common criteria compliance enabled" = 0
                "cost threshold for parallelism"     = 5
                "cross db ownership chaining"        = 0
                "cursor threshold"                   = -1
                "Database Mail XPs"                  = 0
                "default full-text language"         = 1033
                "default language"                   = 0
                "default trace enabled"              = 1
                "disallow results from triggers"     = 0
                "fill factor (%)"                    = 0
                "ft crawl bandwidth (max)"           = 100
                "ft crawl bandwidth (min)"           = 0
                "ft notify bandwidth (max)"          = 100
                "ft notify bandwidth (min)"          = 0
                "index create memory (KB)"           = 0
                "in-doubt xact resolution"           = 0
                "lightweight pooling"                = 0
                "locks"                              = 0
                "max degree of parallelism"          = 0
                "max full-text crawl range"          = 4
                "max server memory (MB)"             = 2147483647
                "max text repl size (B)"             = 65536
                "max worker threads"                 = 0
                "media retention"                    = 0
                "min memory per query (KB)"          = 1024
                "min server memory (MB)"             = 8
                "nested triggers"                    = 1
                "network packet size (B)"            = 4096
                "Ole Automation Procedures"          = 0
                "open objects"                       = 0
                "PH timeout (s)"                     = 60
                "precompute rank"                    = 0
                "priority boost"                     = 0
                "query governor cost limit"          = 0
                "query wait (s)"                     = -1
                "recovery interval (min)"            = 0
                "remote access"                      = 1
                "remote admin connections"           = 0
                "remote login timeout (s)"           = 20
                "remote proc trans"                  = 0
                "remote query timeout (s)"           = 600
                "Replication XPs"                    = 0
                "scan for startup procs"             = 0
                "server trigger recursion"           = 1
                "set working set size"               = 0
                "show advanced options"              = 0
                "SMO and DMO XPs"                    = 1
                "SQL Mail XPs"                       = 0
                "transform noise words"              = 0
                "two digit year cutoff"              = 2049
                "user connections"                   = 0
                "User Instance Timeout"              = 60
                "user instances enabled"             = 0
                "user options"                       = 0
                "Web Assistant Procedures"           = 0
                "xp_cmdshell"                        = 0
            }
        }

        #endregion SQL2005

        #region SQL2008&2008R2
        10 {
            [pscustomobject]@{
                "access check cache bucket count"    = 0
                "access check cache quota"           = 0
                "ad hoc distributed queries"         = 0
                "affinity I/O mask"                  = 0
                "affinity64 I/O mask"                = 0
                "affinity mask"                      = 0
                "affinity64 mask"                    = 0
                "Agent XPs"                          = 0
                "allow updates"                      = 0
                "awe enabled"                        = 0
                "backup compression default"         = 0
                "blocked process threshold (s)"      = 0
                "c2 audit mode"                      = 0
                "clr enabled"                        = 0
                "common criteria compliance enabled" = 0
                "cost threshold for parallelism"     = 5
                "cross db ownership chaining"        = 0
                "cursor threshold"                   = -1
                "Database Mail XPs"                  = 0
                "default full-text language"         = 1033
                "default language"                   = 0
                "default trace enabled"              = 1
                "disallow results from triggers"     = 0
                "EKM provider enabled"               = 0
                "filestream access level"            = 0
                "fill factor (%)"                    = 0
                "ft crawl bandwidth (max)"           = 100
                "ft crawl bandwidth (min)"           = 0
                "ft notify bandwidth (max)"          = 100
                "ft notify bandwidth (min)"          = 0
                "index create memory (KB)"           = 0
                "in-doubt xact resolution"           = 0
                "lightweight pooling"                = 0
                "locks"                              = 0
                "max degree of parallelism"          = 0
                "max full-text crawl range"          = 4
                "max server memory (MB)"             = 2147483647
                "max text repl size (B)"             = 65536
                "max worker threads"                 = 0
                "media retention"                    = 0
                "min memory per query (KB)"          = 1024
                "min server memory (MB)"             = 0
                "nested triggers"                    = 1
                "network packet size (B)"            = 4096
                "Ole Automation Procedures"          = 0
                "open objects"                       = 0
                "optimize for ad hoc workloads"      = 0
                "PH timeout (s)"                     = 60
                "precompute rank"                    = 0
                "priority boost"                     = 0
                "query governor cost limit"          = 0
                "query wait (s)"                     = -1
                "recovery interval (min)"            = 0
                "remote access"                      = 1
                "remote admin connections"           = 0
                "remote login timeout (s)"           = 20
                "remote proc trans"                  = 0
                "remote query timeout (s)"           = 600
                "Replication XPs"                    = 0
                "scan for startup procs"             = 0
                "server trigger recursion"           = 1
                "set working set size"               = 0
                "show advanced options"              = 0
                "SMO and DMO XPs"                    = 1
                "SQL Mail XPs"                       = 0
                "transform noise words"              = 0
                "two digit year cutoff"              = 2049
                "user connections"                   = 0
                "User Instance Timeout"              = 60
                "user instances enabled"             = 0
                "user options"                       = 0
                "xp_cmdshell"                        = 0
            }
        }
        #endregion SQL2008&2008R2

        #region SQL2012
        11 {
            [pscustomobject]@{
                "access check cache bucket count"    = 0
                "access check cache quota"           = 0
                "ad hoc distributed queries"         = 0
                "affinity I/O mask"                  = 0
                "affinity64 I/O mask"                = 0
                "affinity mask"                      = 0
                "affinity64 mask"                    = 0
                "Agent XPs"                          = 0
                "allow updates"                      = 0
                "backup compression default"         = 0
                "blocked process threshold (s)"      = 0
                "c2 audit mode"                      = 0
                "clr enabled"                        = 0
                "common criteria compliance enabled" = 0
                "contained database authentication"  = 0
                "cost threshold for parallelism"     = 5
                "cross db ownership chaining"        = 0
                "cursor threshold"                   = -1
                "Database Mail XPs"                  = 0
                "default full-text language"         = 1033
                "default language"                   = 0
                "default trace enabled"              = 1
                "disallow results from triggers"     = 0
                "EKM provider enabled"               = 0
                "filestream access level"            = 0
                "fill factor (%)"                    = 0
                "ft crawl bandwidth (max)"           = 100
                "ft crawl bandwidth (min)"           = 0
                "ft notify bandwidth (max)"          = 100
                "ft notify bandwidth (min)"          = 0
                "index create memory (KB)"           = 0
                "in-doubt xact resolution"           = 0
                "lightweight pooling"                = 0
                "locks"                              = 0
                "max degree of parallelism"          = 0
                "max full-text crawl range"          = 4
                "max server memory (MB)"             = 2147483647
                "max text repl size (B)"             = 65536
                "max worker threads"                 = 0
                "media retention"                    = 0
                "min memory per query (KB)"          = 1024
                "min server memory (MB)"             = 0
                "nested triggers"                    = 1
                "network packet size (B)"            = 4096
                "Ole Automation Procedures"          = 0
                "open objects"                       = 0
                "optimize for ad hoc workloads"      = 0
                "PH_timeou"                          = 60
                "precompute rank"                    = 0
                "priority boost"                     = 0
                "query governor cost limit"          = 0
                "query wait (s)"                     = -1
                "recovery interval (min)"            = 0
                "remote access"                      = 1
                "remote admin connections"           = 0
                "remote login timeout (s)"           = 10
                "remote proc trans"                  = 0
                "remote query timeout (s)"           = 600
                "Replication XPs"                    = 0
                "scan for startup procs"             = 0
                "server trigger recursion"           = 1
                "set working set size"               = 0
                "show advanced options"              = 0
                "SMO and DMO XPs"                    = 1
                "transform noise words"              = 0
                "two digit year cutoff"              = 2049
                "user connections"                   = 0
                "user options"                       = 0
                "xp_cmdshell"                        = 0
            }
        }
        #endregion SQL2012

        #region SQL2014
        12 {
            [pscustomobject]@{
                "access check cache bucket count"    = 0
                "access check cache quota"           = 0
                "ad hoc distributed queries"         = 0
                "affinity I/O mask"                  = 0
                "affinity64 I/O mask"                = 0
                "affinity mask"                      = 0
                "affinity64 mask"                    = 0
                "Agent XPs"                          = 0
                "allow updates"                      = 0
                "backup checksum default"            = 0
                "backup compression default"         = 0
                "blocked process threshold (s)"      = 0
                "c2 audit mode"                      = 0
                "clr enabled"                        = 0
                "common criteria compliance enabled" = 0
                "contained database authentication"  = 0
                "cost threshold for parallelism"     = 5
                "cross db ownership chaining"        = 0
                "cursor threshold"                   = -1
                "Database Mail XPs"                  = 0
                "default full-text language"         = 1033
                "default language"                   = 0
                "default trace enabled"              = 1
                "disallow results from triggers"     = 0
                "EKM provider enabled"               = 0
                "filestream access level"            = 0
                "fill factor (%)"                    = 0
                "ft crawl bandwidth (max)"           = 100
                "ft crawl bandwidth (min)"           = 0
                "ft notify bandwidth (max)"          = 100
                "ft notify bandwidth (min)"          = 0
                "index create memory (KB)"           = 0
                "in-doubt xact resolution"           = 0
                "lightweight pooling"                = 0
                "locks"                              = 0
                "max degree of parallelism"          = 0
                "max full-text crawl range"          = 4
                "max server memory (MB)"             = 2147483647
                "max text repl size (B)"             = 65536
                "max worker threads"                 = 0
                "media retention"                    = 0
                "min memory per query (KB)"          = 1024
                "min server memory (MB)"             = 0
                "nested triggers"                    = 1
                "network packet size (B)"            = 4096
                "Ole Automation Procedures"          = 0
                "open objects"                       = 0
                "optimize for ad hoc workloads"      = 0
                "PH timeout (s)"                     = 60
                "precompute rank"                    = 0
                "priority boost"                     = 0
                "query governor cost limit"          = 0
                "query wait (s)"                     = -1
                "recovery interval (min)"            = 0
                "remote access"                      = 1
                "remote admin connections"           = 0
                "remote login timeout (s)"           = 10
                "remote proc trans"                  = 0
                "remote query timeout (s)"           = 600
                "Replication XPs"                    = 0
                "scan for startup procs"             = 0
                "server trigger recursion"           = 1
                "set working set size"               = 0
                "show advanced options"              = 0
                "SMO and DMO XPs"                    = 1
                "transform noise words"              = 0
                "two digit year cutoff"              = 2049
                "user connections"                   = 0
                "user options"                       = 0
                "xp_cmdshell"                        = 0
            }
        }
        #endregion SQL2014

        #region SQL2016
        13 {
            [pscustomobject]@{
                "access check cache bucket count"        = 0
                "access check cache quota"               = 0
                "ad hoc distributed queries"             = 0
                "affinity I/O mask"                      = 0
                "affinity64 I/O mask"                    = 0
                "affinity mask"                          = 0
                "affinity64 mask"                        = 0
                "Agent XPs"                              = 0
                "allow updates"                          = 0
                "automatic soft-NUMA disabled"           = 0
                "backup checksum default"                = 0
                "backup compression default"             = 0
                "blocked process threshold (s)"          = 0
                "c2 audit mode"                          = 0
                "clr enabled"                            = 0
                "common criteria compliance enabled"     = 0
                "contained database authentication"      = 0
                "cost threshold for parallelism"         = 5
                "cross db ownership chaining"            = 0
                "cursor threshold"                       = -1
                "Database Mail XPs"                      = 0
                "default full-text language"             = 1033
                "default language"                       = 0
                "default trace enabled"                  = 1
                "disallow results from triggers"         = 0
                "EKM provider enabled"                   = 0
                "external scripts enabled"               = 0
                "filestream access level"                = 0
                "fill factor (%)"                        = 0
                "ft crawl bandwidth (max)"               = 100
                "ft crawl bandwidth (min)"               = 0
                "ft notify bandwidth (max)"              = 100
                "ft notify bandwidth (min)"              = 0
                "index create memory (KB)"               = 0
                "in-doubt xact resolution"               = 0
                "lightweight pooling"                    = 0
                "locks"                                  = 0
                "max degree of parallelism"              = 0
                "max full-text crawl range"              = 4
                "max server memory (MB)"                 = 2147483647
                "max text repl size (B)"                 = 65536
                "max worker threads"                     = 0
                "media retention"                        = 0
                "min memory per query (KB)"              = 1024
                "min server memory (MB)"                 = 0
                "nested triggers"                        = 1
                "network packet size (B)"                = 4096
                "Ole Automation Procedures"              = 0
                "open objects"                           = 0
                "optimize for ad hoc workloads"          = 0
                "PH timeout (s)"                         = 60
                "PolyBase Hadoop and Azure blob storage" = 0
                "precompute rank"                        = 0
                "priority boost"                         = 0
                "query governor cost limit"              = 0
                "query wait (s)"                         = -1
                "recovery interval (min)"                = 0
                "remote access"                          = 1
                "remote admin connections"               = 0
                "remote data archive"                    = 0
                "remote login timeout (s)"               = 10
                "remote proc trans"                      = 0
                "remote query timeout (s)"               = 0
                "Replication XPs"                        = 0
                "scan for startup procs"                 = 0
                "server trigger recursion"               = 1
                "set working set size"                   = 0
                "show advanced options"                  = 0
                "SMO and DMO XPs"                        = 1
                "transform noise words"                  = 0
                "two digit year cutoff"                  = 2049
                "user connections"                       = 0
                "user options"                           = 0
                "xp_cmdshell"                            = 0
            }
        }
        #endregion SQL2016

        #region SQL2017
        14 {
            [pscustomobject]@{
                "access check cache bucket count"    = 0
                "access check cache quota"           = 0
                "Ad Hoc Distributed Queries"         = 0
                "affinity I / O mask"                = 0
                "affinity mask"                      = 0
                "affinity64 I / O mask"              = 0
                "affinity64 mask"                    = 0
                "Agent XPs"                          = 0
                "allow polybase export"              = 0
                "allow updates"                      = 0
                "automatic soft-NUMA disabled"       = 0
                "backup checksum default"            = 0
                "backup compression default"         = 0
                "blocked process threshold (s)"      = 0
                "c2 audit mode"                      = 0
                "clr enabled"                        = 0
                "clr strict security"                = 1
                "common criteria compliance enabled" = 0
                "contained database authentication"  = 0
                "cost threshold for parallelism"     = 5
                "cross db ownership chaining"        = 0
                "cursor threshold"                   = -1
                "Database Mail XPs"                  = 0
                "default full-text language"         = 1033
                "default language"                   = 0
                "default trace enabled"              = 1
                "disallow results from triggers"     = 0
                "EKM provider enabled"               = 0
                "external scripts enabled"           = 0
                "filestream access level"            = 0
                "fill factor ( % )"                  = 0
                "ft crawl bandwidth (max)"           = 100
                "ft crawl bandwidth (min)"           = 0
                "ft notify bandwidth (max)"          = 100
                "ft notify bandwidth (min)"          = 0
                "hadoop connectivity"                = 0
                "index create memory (KB)"           = 0
                "in-doubt xact resolution"           = 0
                "lightweight pooling"                = 0
                "locks"                              = 0
                "max degree of parallelism"          = 0
                "max full-text crawl range"          = 4
                "max server memory (MB)"             = 2147483647
                "max text repl size (B)"             = 65536
                "max worker threads"                 = 0
                "media retention"                    = 0
                "min memory per query (KB)"          = 1024
                "min server memory (MB)"             = 0
                "nested triggers"                    = 1
                "network packet size (B)"            = 4096
                "Ole Automation Procedures"          = 0
                "open objects"                       = 0
                "optimize for ad hoc workloads"      = 0
                "PH timeout (s)"                     = 60
                "polybase network encryption"        = 1
                "precompute rank"                    = 0
                "priority boost"                     = 0
                "query governor cost limit"          = 0
                "query wait (s)"                     = -1
                "recovery interval (min)"            = 0
                "remote access"                      = 1
                "remote admin connections"           = 0
                "remote data archive"                = 0
                "remote login timeout (s)"           = 10
                "remote proc trans"                  = 0
                "remote query timeout (s)"           = 600
                "Replication XPs"                    = 0
                "scan for startup procs"             = 0
                "server trigger recursion"           = 1
                "set working set size"               = 0
                "show advanced options"              = 0
                "SMO and DMO XPs"                    = 1
                "transform noise words"              = 0
                "two digit year cutoff"              = 2049
                "user connections"                   = 0
                "user options"                       = 0
                "xp_cmdshell"                        = 0

            }
        }
        #endregion SQL2017


    }

}
function Get-SqlFileStructure {
    <#
    .SYNOPSIS
    Internal function. Returns custom object that contains file structures on destination paths (\\SqlInstance\m$\mssql\etc\etc\file.mdf) for
    source and destination servers.
#>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [object]$source,
        [Parameter(Mandatory = $true, Position = 1)]
        [ValidateNotNullOrEmpty()]
        [object]$destination,
        [Parameter(Mandatory = $false, Position = 2)]
        [bool]$ReuseSourceFolderStructure,
        [PSCredential]$SourceSqlCredential,
        [PSCredential]$DestinationSqlCredential
    )

    $sourceserver = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
    $source = $sourceserver.DomainInstanceName
    $destserver = Connect-SqlInstance -SqlInstance $Destination -SqlCredential $DestinationSqlCredential
    $destination = $destserver.DomainInstanceName

    $sourcenetbios = Resolve-NetBiosName $sourceserver
    $destnetbios = Resolve-NetBiosName $destserver

    $dbcollection = @{ };

    foreach ($db in $sourceserver.databases) {
        $dbstatus = $db.status.toString()
        if ($dbstatus.StartsWith("Normal") -eq $false) { continue }
        $destinationfiles = @{ }; $sourcefiles = @{ }

        # Data Files
        foreach ($filegroup in $db.filegroups) {
            foreach ($file in $filegroup.files) {
                # Destination File Structure
                $d = @{ }
                if ($ReuseSourceFolderStructure) {
                    $d.physical = $file.filename
                }
                else {
                    $directory = Get-SqlDefaultPaths $destserver data
                    $filename = Split-Path $($file.filename) -leaf
                    $d.physical = "$directory\$filename"
                }
                $d.logical = $file.name
                $d.remotefilename = Join-AdminUnc $destnetbios $d.physical
                $destinationfiles.add($file.name, $d)

                # Source File Structure
                $s = @{ }
                $s.logical = $file.name
                $s.physical = $file.filename
                $s.remotefilename = Join-AdminUnc $sourcenetbios $s.physical
                $sourcefiles.add($file.name, $s)
            }
        }

        # Add support for Full Text Catalogs in SQL Server 2005 and below
        if ($sourceserver.VersionMajor -lt 10) {
            foreach ($ftc in $db.FullTextCatalogs) {
                # Destination File Structure
                $d = @{ }
                $pre = "sysft_"
                $name = $ftc.name
                $physical = $ftc.RootPath
                $logical = "$pre$name"
                if ($ReuseSourceFolderStructure) {
                    $d.physical = $physical
                }
                else {
                    $directory = Get-SqlDefaultPaths $destserver data
                    if ($destserver.VersionMajor -lt 10) { $directory = "$directory\FTDATA" }
                    $filename = Split-Path($physical) -leaf
                    $d.physical = "$directory\$filename"
                }
                $d.logical = $logical
                $d.remotefilename = Join-AdminUnc $destnetbios $d.physical
                $destinationfiles.add($logical, $d)

                # Source File Structure
                $s = @{ }
                $pre = "sysft_"
                $name = $ftc.name
                $physical = $ftc.RootPath
                $logical = "$pre$name"

                $s.logical = $logical
                $s.physical = $physical
                $s.remotefilename = Join-AdminUnc $sourcenetbios $s.physical
                $sourcefiles.add($logical, $s)
            }
        }

        # Log Files
        foreach ($file in $db.logfiles) {
            $d = @{ }
            if ($ReuseSourceFolderStructure) {
                $d.physical = $file.filename
            }
            else {
                $directory = Get-SqlDefaultPaths $destserver log
                $filename = Split-Path $($file.filename) -leaf
                $d.physical = "$directory\$filename"
            }
            $d.logical = $file.name
            $d.remotefilename = Join-AdminUnc $destnetbios $d.physical
            $destinationfiles.add($file.name, $d)

            $s = @{ }
            $s.logical = $file.name
            $s.physical = $file.filename
            $s.remotefilename = Join-AdminUnc $sourcenetbios $s.physical
            $sourcefiles.add($file.name, $s)
        }

        $location = @{ }
        $location.add("Destination", $destinationfiles)
        $location.add("Source", $sourcefiles)
        $dbcollection.Add($($db.name), $location)
    }

    $filestructure = [pscustomobject]@{ "databases" = $dbcollection }
    return $filestructure
}
function Get-SqlSaLogin {
    <#
        .SYNOPSIS
            Internal function. Gets the name of the sa login in case someone changed it.
        .PARAMETER SqlInstance
            The SQL Server instance.
        .PARAMETER SqlCredential
            Allows you to login to servers using SQL Logins instead of Windows Authentication (AKA Integrated or Trusted).
    #>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [object]$SqlInstance,
        [PSCredential]$SqlCredential
    )
    $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
    $sa = $server.Logins | Where-Object Id -eq 1
    return $sa.Name
}
function Get-XpDirTreeRestoreFile {
    <#
    .SYNOPSIS
        Internal Function to get SQL Server backfiles from a specified folder using xp_dirtree

    .DESCRIPTION
        Takes path, checks for validity. Scans for usual backup file

    .PARAMETER Path
        The path to retrieve the restore for.

    .PARAMETER SqlInstance
        The SQL Server that you're connecting to.

    .PARAMETER SqlCredential
        Credential object used to connect to the SQL Server as a different user

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .EXAMPLE
        PS C:\> Get-XpDirTreeRestoreFile -Path '\\foo\bar\' -SqlInstance $SqlInstance

        Tests whether the instance $SqlInstance has access to the path \\foo\bar\
#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [string]$Path,
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [System.Management.Automation.PSCredential]$SqlCredential,
        [bool][Alias('Silent')]$EnableException = $false,
        [switch]$NoRecurse
    )

    Write-Message -Level InternalComment -Message "Starting"

    Write-Message -Level Verbose -Message "Connecting to $SqlInstance"
    $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential

    if (($path -like '*.bak') -or ($path -like '*trn')) {

    }
    elseif ($Path[-1] -ne "\") {
        $Path = $Path + "\"
    }

    if (!(Test-DbaSqlPath -SqlInstance $server -path $path)) {
        Stop-Function -Message "SqlInstance $SqlInstance cannot access $path" -EnableException $true
    }
    if ($server.VersionMajor -lt 9) {
        $sql = "EXEC master..xp_dirtree '$Path',1,1;"
    }
    else {
        $sql = "EXEC master.sys.xp_dirtree '$Path',1,1;"
    }
    #$queryResult = Invoke-Sqlcmd2 -ServerInstance $SqlInstance -Credential $SqlCredential -Database tempdb -Query $query
    $queryResult = $server.Query($sql)
    Write-Message -Level Debug -Message $sql
    $dirs = $queryResult | where-object file -eq 0
    $Results = @()
    $Results += $queryResult | where-object file -eq 1 | Select-Object @{ Name = "FullName"; Expression = { $path + $_."Subdirectory" } }

    if ($True -ne $NoRecurse) {
        foreach ($d in $dirs) {
            $fullpath = "$path$($d.Subdirectory)"
            Write-Message -Level Verbose -Message "Enumerating subdirectory '$fullpath'"
            $Results += Get-XpDirTreeRestoreFile -path $fullpath -SqlInstance $server
        }
    }
    return $Results
}
function Import-DbaCmdlet {
<#
    .SYNOPSIS
        Loads a cmdlet into the current context.
    
    .DESCRIPTION
        Loads a cmdlet into the current context.
        This can be used to register a cmdlet during module import, making it easy to have hybrid modules publishing both cmdlets and functions.
        Can also be used to register cmdlets written in PowerShell classes.
    
    .PARAMETER Name
        The name of the cmdlet to register.
    
    .PARAMETER Type
        The type of the class implementing the cmdlet.
    
    .PARAMETER HelpFile
        Path to the help XML containing the help for the cmdlet.
    
    .PARAMETER Module
        Module to inject the cmdlet into.
    
    .EXAMPLE
        PS C:\> Import-DbaCmdlet -Name Get-Something -Type ([GetSomethingCommand])
        
        Imports the Get-Something cmdlet into the current context.
    
    .EXAMPLE
        PS C:\> Import-DbaCmdlet -Name Get-Something -Type ([GetSomethingCommand]) -Module (Get-Module PSReadline)
        
        Imports the Get-Something cmdlet into the PSReadline module.
    
    .NOTES
        Original Author: Chris Dent
        Link: https://www.indented.co.uk/cmdlets-without-a-dll/
#>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [String]
        $Name,
        
        [Parameter(Mandatory = $true)]
        [Type]
        $Type,
        
        [string]
        $HelpFile,
        
        [System.Management.Automation.PSModuleInfo]
        $Module
    )
    
    begin {
        $scriptBlock = {
            param (
                [String]
                $Name,
                
                [Type]
                $Type,
                
                [string]
                $HelpFile
            )
            
            $sessionStateCmdletEntry = New-Object System.Management.Automation.Runspaces.SessionStateCmdletEntry(
                $Name,
                $Type,
                $HelpFile
            )
            
            # System.Management.Automation.Runspaces.LocalPipeline will let us get at ExecutionContext.
            # Note: $ExecutionContext is *not* an instance of this object.
            $pipelineType = [PowerShell].Assembly.GetType('System.Management.Automation.Runspaces.LocalPipeline')
            $method = $pipelineType.GetMethod(
                'GetExecutionContextFromTLS',
                [System.Reflection.BindingFlags]'Static,NonPublic'
            )
            
            # Invoke the method to get an instance of ExecutionContext.
            $context = $method.Invoke(
                $null,
                [System.Reflection.BindingFlags]'Static,NonPublic',
                $null,
                $null,
                (Get-Culture)
            )
            
            # Get the SessionStateInternal type
            $internalType = [PowerShell].Assembly.GetType('System.Management.Automation.SessionStateInternal')
            
            # Get a valid constructor which accepts a param of type ExecutionContext
            $constructor = $internalType.GetConstructor(
                [System.Reflection.BindingFlags]'Instance,NonPublic',
                $null,
                $context.GetType(),
                $null
            )
            
            # Get the SessionStateInternal for this execution context
            $sessionStateInternal = $constructor.Invoke($context)
            
            # Get the method which allows Cmdlets to be added to the session
            $method = $internalType.GetMethod(
                'AddSessionStateEntry',
                [System.Reflection.BindingFlags]'Instance,NonPublic',
                $null,
                $sessionStateCmdletEntry.GetType(),
                $null
            )
            # Invoke the method.
            $method.Invoke($sessionStateInternal, $sessionStateCmdletEntry)
        }
    }
    
    process {
        if (-not $Module) { $scriptBlock.Invoke($Name, $Type, $HelpFile) }
        else { $Module.Invoke($scriptBlock, @($Name, $Type, $HelpFile)) }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Invoke-Command2 {
    <#
        .SYNOPSIS
            Wrapper function that calls Invoke-Command and gracefully handles credentials.

        .DESCRIPTION
            Wrapper function that calls Invoke-Command and gracefully handles credentials.

        .PARAMETER ComputerName
            Default: $env:COMPUTERNAME
            The computer to invoke the scriptblock on.

        .PARAMETER Credential
            The credentials to use.
            Can accept $null on older PowerShell versions, since it expects type object, not PSCredential

        .PARAMETER ScriptBlock
            The code to run on the targeted system

        .PARAMETER ArgumentList
            Any arguments to pass to the scriptblock being run

        .PARAMETER Raw
            Passes through the raw return data, rather than prettifying stuff.

        .EXAMPLE
            PS C:\> Invoke-Command2 -ComputerName sql2014 -Credential $Credential -ScriptBlock { dir }

            Executes the scriptblock '{ dir }' on the computer sql2014 using the credentials stored in $Credential.
            If $Credential is null, no harm done.
    #>
    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUsePSCredentialType", "")]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "")]
    param (
        [DbaInstanceParameter]$ComputerName = $env:COMPUTERNAME,
        [object]$Credential,
        [scriptblock]$ScriptBlock,
        [object[]]$ArgumentList,
        [switch]$Raw
    )
    <# Note: Credential stays as an object type for legacy reasons. #>

    $InvokeCommandSplat = @{
        ScriptBlock = $ScriptBlock
    }
    if ($ArgumentList) {
        $InvokeCommandSplat["ArgumentList"] = $ArgumentList
    }
    if (-not $ComputerName.IsLocalHost) {
        $runspaceId = [System.Management.Automation.Runspaces.Runspace]::DefaultRunspace.InstanceId
        $sessionName = "dbatools_$runspaceId"

        # Retrieve a session from the session cache, if available (it's unique per runspace)
        if (-not ($currentSession = [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::PSSessionGet($runspaceId, $ComputerName.ComputerName) | Where-Object State -Match "Opened|Disconnected")) {
            $timeout = New-PSSessionOption -IdleTimeout (New-TimeSpan -Minutes 10).TotalMilliSeconds
            if ($Credential) {
                $InvokeCommandSplat["Session"] = (New-PSSession -ComputerName $ComputerName.ComputerName -Name $sessionName -SessionOption $timeout -Credential $Credential -ErrorAction Stop)
            }
            else {
                $InvokeCommandSplat["Session"] = (New-PSSession -ComputerName $ComputerName.ComputerName -Name $sessionName -SessionOption $timeout -ErrorAction Stop)
            }
            $currentSession = $InvokeCommandSplat["Session"]
        }
        else {
            if ($currentSession.State -eq "Disconnected") {
                $null = $currentSession | Connect-PSSession -ErrorAction Stop
            }
            $InvokeCommandSplat["Session"] = $currentSession

            # Refresh the session registration if registered, to reset countdown until purge
            [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::PSSessionSet($runspaceId, $ComputerName.ComputerName, $currentSession)
        }
    }

    if ($Raw) {
        Invoke-Command @InvokeCommandSplat
    }
    else {
        Invoke-Command @InvokeCommandSplat | Select-Object -Property * -ExcludeProperty PSComputerName, RunspaceId, PSShowComputerName
    }

    if (-not $ComputerName.IsLocalhost) {
        # Tell the system to clean up if the session expires
        [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::PSSessionSet($runspaceId, $ComputerName.ComputerName, $currentSession)

        if (-not (Get-DbaConfigValue -FullName 'PSRemoting.Sessions.Enable' -Fallback $true)) {
            $currentSession | Remove-PSSession
        }
    }
}
function Invoke-DbaDatabaseCorruption {
    <#
      .SYNOPSIS
      Utilizes the DBCC WRITEPAGE functionality  to corrupt a specific database table for testing.  In no uncertain terms, this is a non-production command.
      This will absolutely break your databases and that is its only purpose.
      Using DBCC WritePage will definitely void any support options for your database.

      .DESCRIPTION
      This command can be used to verify your tests for corruption are successful, and to demo various scenarios for corrupting page data.
      This command will take an instance and database (and optionally a table) and set the database to single user mode, corrupt either the specified table or the first table it finds, and returns it to multi-user.

      .PARAMETER SqlInstance
      The SQL Server instance holding the databases to be removed.You must have sysadmin access and Server version must be SQL Server version 2000 or higher.

      .PARAMETER SqlCredential
      Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

      .PARAMETER Database
      The single database you would like to corrupt, this command does not support multiple databases (on purpose.)

      .PARAMETER Table
      The specific table you want corrupted, if you do not choose one, the first user table (alphabetically) will be chosen for corruption.

      .PARAMETER WhatIf
      If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

      .PARAMETER Confirm
      If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

      .PARAMETER EnableException
      By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
      This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
      Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

      .NOTES
      Tags: Corruption, Testing
      Author: Constantine Kokkinos (@mobileck https://constantinekokkinos.com)
      Reference: https://www.sqlskills.com/blogs/paul/dbcc-writepage/
      Website: https://dbatools.io
      Copyright: (C) Chrissy LeMaire, [email protected]
      License: MIT https://opensource.org/licenses/MIT

      .LINK
      https://dbatools.io/Invoke-DbaDatabaseCorruption

      .EXAMPLE
      Invoke-DbaDatabaseCorruption -SqlInstance sql2016 -Database containeddb
      Prompts for confirmation then selects the first table in database containeddb and corrupts it (by putting database into single user mode, writing to garbage to its first non-iam page, and returning it to multi-user.)

      .EXAMPLE
      Invoke-DbaDatabaseCorruption -SqlInstance sql2016 -Database containeddb -Table Customers -Confirm:$false
      Does not prompt and immediately corrupts table customers in database containeddb on the sql2016 instance (by putting database into single user mode, writing to garbage to its first non-iam page, and returning it to multi-user.)
  #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [parameter(Mandatory = $false)]
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [parameter(Mandatory)]
        [string]$Database,
        [string]$Table,
        [Alias('Silent')]
        [switch]$EnableException
    )
    # For later if we want to do bit flipping.
    # function Dbcc-ReadPage {
    #   param (
    #     $SqlInstance,
    #     $Database,
    #     $TableName,
    #     $IndexID = 1
    #   )
    #   $DbccPage = "DBCC PAGE (N'$Database',N'$($TableName)',$IndexID)"
    #   Write-Message -Level Verbose -Message "$DbccPage"
    #   $pages = $SqlInstance.Query($DbccPage) | Where-Object { $_.IAMFID -ne [DBNull]::Value }
    #   return $Pages
    # }

    function Dbcc-Index {
        [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseApprovedVerbs", "")]
        [CmdletBinding()]
        param (
            $SqlInstance,
            $Database,
            $TableName,
            $IndexID = 1
        )
        $DbccInd = "DBCC IND (N'$Database',N'$($TableName)',$IndexID)"
        Write-Message -Level Verbose -Message "$DbccInd"
        $pages = $SqlInstance.Query($DbccInd) | Where-Object { $_.IAMFID -ne [DBNull]::Value }
        return $Pages
    }
    function Dbcc-WritePage {
        [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseApprovedVerbs", "")]
        [CmdletBinding()]
        param (
            $SqlInstance,
            $Database,
            $FileId = 1,
            $PageId,
            $Offset = 4000,
            $NumberOfBytesToChange = 1,
            $HexString = '0x45',
            $bypassbufferpool = 1
        )
        $DbccWritePage = "DBCC WRITEPAGE (N'$Database', $FileId, $PageId, $Offset, $NumberOfBytesToChange, $HexString, $bypassbufferpool);"
        Write-Message -Level Verbose -Message "$DbccWritePage"
        $WriteInfo = $SqlInstance.Databases[$Database].Query($DbccWritePage)
        return $WriteInfo
    }

    if ("master", "tempdb", "model", "msdb" -contains $Database) {
        Stop-Function -Message "You may not corrupt system databases."
        return
    }

    try {
        Write-Message -Level Verbose -Message "Connecting to $SqlInstance"
        $Server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential -MinimumVersion 9
    }
    catch {
        Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $SqlInstance
        return
    }

    $db = $Server.Databases | Where-Object { $_.Name -eq $Database }
    if (!$db) {
        Stop-Function -Message "The database specified does not exist."
        return
    }
    if ($Table) {
        $tb = $db.Tables | Where-Object Name -eq $Table
    }
    else {
        $tb = $db.Tables | Select-Object -First 1
    }

    if (-not $tb) {
        Stop-Function -Message "There are no accessible tables in $Database on $SqlInstance." -Target $Database
        return
    }

    $RowCount = $db.Query("select top 1 * from $($tb.name)")
    if ($RowCount.count -eq 0) {
        Stop-Function -Message "The table $tb has no rows" -Target $table
        return
    }

    if ($Pscmdlet.ShouldProcess("$db on $SqlInstance", "Corrupt $tb in $Database")) {
        $pages = Dbcc-Index -SqlInstance $Server -Database $Database -TableName $tb.Name | Select-Object -First 1
        #Dbcc-ReadPage -SqlInstance $Server -Database $Database -PageId $pages.PagePID -FileId $pages.PageFID
        Write-Message -Level Verbose -Message "Setting single-user mode."
        $null = Stop-DbaProcess -SqlInstance $Server -Database $Database
        $null = Set-DbaDatabaseState -SqlServer $Server -Database $Database -SingleUser -Force

        try {
            Write-Message -Level Verbose -Message "Stopping processes in target database."
            $null = Stop-DbaProcess -SqlInstance $Server -Database $Database
            Write-Message -Level Verbose -Message "Corrupting data."
            Dbcc-WritePage -SqlInstance $Server -Database $Database -PageId $pages.PagePID -FileId $pages.PageFID
        }
        catch {
            $Server.ConnectionContext.Disconnect()
            $Server.ConnectionContext.Connect()
            $null = Set-DbaDatabaseState -SqlServer $Server -Database $Database -MultiUser -Force
            Stop-Function -Message "Failed to write page" -Category WriteError -ErrorRecord $_ -Target $instance
            return
        }

        Write-Message -Level Verbose -Message "Setting database into multi-user mode."
        # If you do not disconnect and reconnect, multiuser fails.
        $Server.ConnectionContext.Disconnect()
        $Server.ConnectionContext.Connect()
        $null = Set-DbaDatabaseState -SqlServer $Server -Database $Database -MultiUser -Force

        [pscustomobject]@{
            ComputerName = $Server.ComputerName
            InstanceName = $Server.ServiceName
            SqlInstance  = $Server.DomainInstanceName
            Database     = $db.Name
            Table        = $tb.Name
            Status       = "Corrupted"
        }
    }
}
function Invoke-DbaDiagnosticQueryScriptParser {
    [CmdletBinding(DefaultParameterSetName = "Default")]

    Param(
        [parameter(Mandatory = $true)]
        [ValidateScript( {Test-Path $_})]
        [System.IO.FileInfo]$filename,
        [Switch]$NoQueryTextColumn,
        [Switch]$NoPlanColumn,
        [Switch]$NoColumnParsing
    )

    $out = "Parsing file {0}" -f $filename
    write-verbose -Message $out

    $ParsedScript = @()
    [string]$scriptpart = ""

    $fullscript = Get-Content -Path $filename

    $start = $false
    $querynr = 0
    $DBSpecific = $false

    if ($NoQueryTextColumn) {$QueryTextColumn = ""}  else {$QueryTextColumn = ", t.[text] AS [Complete Query Text]"}
    if ($NoPlanColumn) {$PlanTextColumn = ""} else {$PlanTextColumn = ", qp.query_plan AS [Query Plan]"}

    foreach ($line in $fullscript) {
        if ($start -eq $false) {
            if ($line -match "You have the correct major version of SQL Server for this diagnostic information script") {
                $start = $true
            }
            continue
        }

        if ($line.StartsWith("-- Database specific queries ***") -or ($line.StartsWith("-- Switch to user database **"))) {
            $DBSpecific = $true
        }

        if (!$NoColumnParsing) {
            if (($line -match "-- uncomment out these columns if not copying results to Excel") -or ($line -match "-- comment out this column if copying results to Excel")) {
                $line = $QueryTextColumn + $PlanTextColumn
            }
        }

        if ($line -match "-{2,}\s{1,}(.*) \(Query (\d*)\) \((\D*)\)") {
            $prev_querydescription = $Matches[1]
            $prev_querynr = $Matches[2]
            $prev_queryname = $Matches[3]

            if ($querynr -gt 0) {
                $properties = @{QueryNr = $querynr; QueryName = $queryname; DBSpecific = $DBSpecific; Description = $queryDescription; Text = $scriptpart}
                $newscript = New-Object -TypeName PSObject -Property $properties
                $ParsedScript += $newscript
                $scriptpart = ""
            }

            $querydescription = $prev_querydescription
            $querynr = $prev_querynr
            $queryname = $prev_queryname
        }
        else {
            if (!$line.startswith("--") -and ($line.trim() -ne "") -and ($null -ne $line) -and ($line -ne "\n")) {
                $scriptpart += $line + "`n"
            }
        }
    }

    $properties = @{QueryNr = $querynr; QueryName = $queryname; DBSpecific = $DBSpecific; Description = $queryDescription; Text = $scriptpart}
    $newscript = New-Object -TypeName PSObject -Property $properties
    $ParsedScript += $newscript
    $ParsedScript
}
function Invoke-DbaSqlAsync {
    <#
        .SYNOPSIS
            Runs a T-SQL script.

        .DESCRIPTION
            Runs a T-SQL script. It's a stripped down version of https://github.com/sqlcollaborative/Invoke-SqlCmd2 and adapted to use dbatools' facilities.
            If you're looking for a public usable function, see Invoke-DbaSqlQuery

        .PARAMETER SQLConnection
            Specifies an existing SQLConnection object to use in connecting to SQL Server.

        .PARAMETER Query
            Specifies one or more queries to be run. The queries can be Transact-SQL, XQuery statements, or sqlcmd commands. Multiple queries in a single batch may be separated by a semicolon.

            Do not specify the sqlcmd GO separator (or, use the ParseGo parameter). Escape any double quotation marks included in the string.

            Consider using bracketed identifiers such as [MyTable] instead of quoted identifiers such as "MyTable".

        .PARAMETER QueryTimeout
            Specifies the number of seconds before the queries time out.

        .PARAMETER As
            Specifies output type. Valid options for this parameter are 'DataSet', 'DataTable', 'DataRow', 'PSObject', and 'SingleValue'

            PSObject output introduces overhead but adds flexibility for working with results: http://powershell.org/wp/forums/topic/dealing-with-dbnull/

        .PARAMETER SqlParameters
            Specifies a hashtable of parameters for parameterized SQL queries.  http://blog.codinghorror.com/give-me-parameterized-sql-or-give-me-death/

            Example:

        .PARAMETER AppendServerInstance
            If this switch is enabled, the SQL Server instance will be appended to PSObject and DataRow output.


        .PARAMETER MessagesToOutput
            Use this switch to have on the output stream messages too (e.g. PRINT statements). Output will hold the resultset too. See examples for detail

        .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    #>

    param (
        [Alias('Connection', 'Conn')]
        [ValidateNotNullOrEmpty()]
        [Microsoft.SqlServer.Management.Common.ServerConnection]$SQLConnection,

        [Parameter(Mandatory = $true, Position = 0, ParameterSetName = "Query")]
        [string]
        $Query,

        [ValidateSet("DataSet", "DataTable", "DataRow", "PSObject", "SingleValue")]
        [string]
        $As = "DataRow",

        [System.Collections.IDictionary]
        $SqlParameters,

        [switch]
        $AppendServerInstance,

        [Int32]$QueryTimeout = 600,

        [switch]
        $MessagesToOutput,

        [switch]
        $EnableException
    )

    begin {
        function Resolve-SqlError {
            param($Err)
            if ($Err) {
                if ($Err.Exception.GetType().Name -eq 'SqlException') {
                    # For SQL exception
                    #$Err = $_
                    Write-Message -Level Debug -Message "Capture SQL Error"
                    if ($PSBoundParameters.Verbose) {
                        Write-Message -Level Verbose -Message "SQL Error:  $Err"
                    } #Shiyang, add the verbose output of exception
                    switch ($ErrorActionPreference.ToString()) {
                        { 'SilentlyContinue', 'Ignore' -contains $_ } {   }
                        'Stop' { throw $Err }
                        'Continue' { throw $Err }
                        Default { Throw $Err }
                    }
                }
                else {
                    # For other exception
                    Write-Message -Level Debug -Message "Capture Other Error"
                    if ($PSBoundParameters.Verbose) {
                        Write-Message -Level Verbose -Message "Other Error:  $Err"
                    }
                    switch ($ErrorActionPreference.ToString()) {
                        { 'SilentlyContinue', 'Ignore' -contains $_ } { }
                        'Stop' { throw $Err }
                        'Continue' { throw $Err }
                        Default { throw $Err }
                    }
                }
            }

        }

        if ($As -eq "PSObject") {
            #This code scrubs DBNulls.  Props to Dave Wyatt
            $cSharp = @'
                using System;
                using System.Data;
                using System.Management.Automation;

                public class DBNullScrubber
                {
                    public static PSObject DataRowToPSObject(DataRow row)
                    {
                        PSObject psObject = new PSObject();

                        if (row != null && (row.RowState & DataRowState.Detached) != DataRowState.Detached)
                        {
                            foreach (DataColumn column in row.Table.Columns)
                            {
                                Object value = null;
                                if (!row.IsNull(column))
                                {
                                    value = row[column];
                                }

                                psObject.Properties.Add(new PSNoteProperty(column.ColumnName, value));
                            }
                        }

                        return psObject;
                    }
                }
'@

            try {
                Add-Type -TypeDefinition $cSharp -ReferencedAssemblies 'System.Data', 'System.Xml' -ErrorAction stop
            }
            catch {
                if (-not $_.ToString() -like "*The type name 'DBNullScrubber' already exists*") {
                    Write-Warning "Could not load DBNullScrubber.  Defaulting to DataRow output: $_."
                    $As = "Datarow"
                }
            }
        }

        $GoSplitterRegex = [regex]'(?smi)^[\s]*GO[\s]*$'

    }
    process {
        $Conn = $SQLConnection.SqlConnectionObject


        Write-Message -Level Debug -Message "Stripping GOs from source"
        $Pieces = $GoSplitterRegex.Split($Query)

        # Only execute non-empty statements
        $Pieces = $Pieces | Where-Object { $_.Trim().Length -gt 0 }
        foreach ($piece in $Pieces) {
            $cmd = New-Object system.Data.SqlClient.SqlCommand($piece, $conn)
            $cmd.CommandTimeout = $QueryTimeout

            if ($null -ne $SqlParameters) {
                $SqlParameters.GetEnumerator() |
                    ForEach-Object {
                    if ($null -ne $_.Value) {
                        $cmd.Parameters.AddWithValue($_.Key, $_.Value)
                    }
                    else {
                        $cmd.Parameters.AddWithValue($_.Key, [DBNull]::Value)
                    }
                } > $null
            }

            $ds = New-Object system.Data.DataSet
            $da = New-Object system.Data.SqlClient.SqlDataAdapter($cmd)

            if ($MessagesToOutput) {
                $pool = [RunspaceFactory]::CreateRunspacePool(1, [int]$env:NUMBER_OF_PROCESSORS + 1)
                $pool.ApartmentState = "MTA"
                $pool.Open()
                $runspaces = @()
                $scriptblock = {
                    Param ($da, $ds, $conn, $queue )
                    $conn.FireInfoMessageEventOnUserErrors = $false
                    $handler = [System.Data.SqlClient.SqlInfoMessageEventHandler] { $queue.Enqueue($_) }
                    $conn.add_InfoMessage($handler)
                    $Err = $null
                    try {
                        [void]$da.fill($ds)
                    }
                    catch {
                        $Err = $_
                    }
                    finally {
                        $conn.remove_InfoMessage($handler)
                    }
                    return $Err
                }
                $queue = New-Object System.Collections.Concurrent.ConcurrentQueue[string]
                $runspace = [PowerShell]::Create()
                $null = $runspace.AddScript($scriptblock)
                $null = $runspace.AddArgument($da)
                $null = $runspace.AddArgument($ds)
                $null = $runspace.AddArgument($Conn)
                $null = $runspace.AddArgument($queue)
                $runspace.RunspacePool = $pool
                $runspaces += [PSCustomObject]@{ Pipe = $runspace; Status = $runspace.BeginInvoke() }
                # While streaming ...
                while ($runspaces.Status.IsCompleted -notcontains $true) {
                    $item = $null
                    if ($queue.TryDequeue([ref]$item)) {
                        "$item"
                    }
                }
                # Drain the stream as the runspace is closed, just to be safe
                if ($queue.IsEmpty -ne $true) {
                    $item = $null
                    while ($queue.TryDequeue([ref]$item)) {
                        "$item"
                    }
                }
                foreach ($runspace in $runspaces) {
                    $results = $runspace.Pipe.EndInvoke($runspace.Status)
                    $runspace.Pipe.Dispose()
                    if ($null -ne $results) {
                        Resolve-SqlError $results[0]
                    }
                }
                $pool.Close()
                $pool.Dispose()
            }
            else {
                #Following EventHandler is used for PRINT and RAISERROR T-SQL statements. Executed when -Verbose parameter specified by caller and no -MessageToOutput
                if ($PSBoundParameters.Verbose) {
                    $conn.FireInfoMessageEventOnUserErrors = $false
                    $handler = [System.Data.SqlClient.SqlInfoMessageEventHandler] { Write-Verbose -Message "$($_)" }
                    $conn.add_InfoMessage($handler)
                }
                try {
                    [void]$da.fill($ds)
                }
                catch {
                    $Err = $_
                }
                finally {
                    if ($PSBoundParameters.Verbose) {
                        $conn.remove_InfoMessage($handler)
                    }
                }
                Resolve-SqlError $Err
            }
            if ($AppendServerInstance) {
                #Basics from Chad Miller
                $Column = New-Object Data.DataColumn
                $Column.ColumnName = "ServerInstance"
                
                if ($ds.Tables.Count -ne 0) {
                    $ds.Tables[0].Columns.Add($Column)
                    Foreach ($row in $ds.Tables[0]) {
                        $row.ServerInstance = $SQLConnection.ServerInstance
                    }
                }
            }

            switch ($As) {
                'DataSet' {
                    $ds
                }
                'DataTable' {
                    $ds.Tables
                }
                'DataRow' {
                    if ($ds.Tables.Count -ne 0) {
                        $ds.Tables[0]
                    }
                }
                'PSObject' {
                    if ($ds.Tables.Count -ne 0) {
                        #Scrub DBNulls - Provides convenient results you can use comparisons with
                        #Introduces overhead (e.g. ~2000 rows w/ ~80 columns went from .15 Seconds to .65 Seconds - depending on your data could be much more!)
                        foreach ($row in $ds.Tables[0].Rows) {
                            [DBNullScrubber]::DataRowToPSObject($row)
                        }
                    }
                }
                'SingleValue' {
                    if ($ds.Tables.Count -ne 0) {
                        $ds.Tables[0] | Select-Object -ExpandProperty $ds.Tables[0].Columns[0].ColumnName
                    }
                }
            }
        } #foreach ($piece in $Pieces)

    }
}
function Invoke-ManagedComputerCommand {
    <#
        .SYNOPSIS
            Runs wmi commands against a target system.

        .DESCRIPTION
            Runs wmi commands against a target system.
            Either directly or over PowerShell remoting.

        .PARAMETER ComputerName
            The target to run against. Must be resolvable.

        .PARAMETER Credential
            Credentials to use when using PowerShell remoting.

        .PARAMETER ScriptBlock
            The scriptblock to execute.
            Use $wmi to access the smo wmi object.
            Must not include a param block!

        .PARAMETER ArgumentList
            The arguments to pass to your scriptblock.
            Access them within the scriptblock using the automatic variable $args

        .PARAMETER EnableException
            Left in for legacy reasons. This command will throw no matter what
    #>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Alias("Server")]
        [dbainstanceparameter]$ComputerName,
        [PSCredential]$Credential,
        [Parameter(Mandatory = $true)]
        [scriptblock]$ScriptBlock,
        [string[]]$ArgumentList,
        [switch][Alias('Silent')]
        $EnableException # Left in for legacy but this command needs to throw
    )
    
    $computer = $ComputerName.ComputerName
    
    $null = Test-ElevationRequirement -ComputerName $computer -EnableException $true
    
    $resolved = Resolve-DbaNetworkName -ComputerName $computer -Turbo
    $ipaddr = $resolved.IpAddress
    $ArgumentList += $ipaddr
    
    [scriptblock]$setupScriptBlock = {
        $ipaddr = $args[$args.GetUpperBound(0)]
        
        # Just in case we go remote, ensure the assembly is loaded
        [void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SqlWmiManagement')
        $wmi = New-Object Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer $ipaddr
        $null = $wmi.Initialize()
    }
    
    $prescriptblock = $setupScriptBlock.ToString()
    $postscriptblock = $ScriptBlock.ToString()
    
    $scriptblock = [ScriptBlock]::Create("$prescriptblock  $postscriptblock")
    
    try {
        Invoke-Command2 -ScriptBlock $ScriptBlock -ArgumentList $ArgumentList -Credential $Credential -ErrorAction Stop
    }
    catch {
        Write-Message -Level Verbose -Message "Local connection attempt to $computer failed. Connecting remotely."
        
        # For surely resolve stuff, and going by default with kerberos, this needs to match FullComputerName
        $hostname = $resolved.FullComputerName
        
        Invoke-Command2 -ScriptBlock $ScriptBlock -ArgumentList $ArgumentList -ComputerName $hostname -ErrorAction Stop
    }
}
function Invoke-Parallel {
    <#
    .SYNOPSIS
        Function to control parallel processing using runspaces

    .DESCRIPTION
        Function to control parallel processing using runspaces

            Note that each runspace will not have access to variables and commands loaded in your session or in other runspaces by default.
            This behaviour can be changed with parameters.

    .PARAMETER ScriptFile
        File to run against all input objects.  Must include parameter to take in the input object, or use $args.  Optionally, include parameter to take in parameter.  Example: C:\script.ps1

    .PARAMETER ScriptBlock
        Scriptblock to run against all computers.

        You may use $Using:<Variable> language in PowerShell 3 and later.

            The parameter block is added for you, allowing behaviour similar to foreach-object:
                Refer to the input object as $_.
                Refer to the parameter parameter as $parameter

    .PARAMETER InputObject
        Run script against these specified objects.

    .PARAMETER Parameter
        This object is passed to every script block.  You can use it to pass information to the script block; for example, the path to a logging folder

            Reference this object as $parameter if using the scriptblock parameterset.

    .PARAMETER ImportVariables
        If specified, get user session variables and add them to the initial session state

    .PARAMETER ImportModules
        If specified, get loaded modules and pssnapins, add them to the initial session state

    .PARAMETER Throttle
        Maximum number of threads to run at a single time.

    .PARAMETER SleepTimer
        Milliseconds to sleep after checking for completed runspaces and in a few other spots.  I would not recommend dropping below 200 or increasing above 500

    .PARAMETER RunspaceTimeout
        Maximum time in seconds a single thread can run.  If execution of your code takes longer than this, it is disposed.  Default: 0 (seconds)

        WARNING:  Using this parameter requires that maxQueue be set to throttle (it will be by default) for accurate timing.  Details here:
        http://gallery.technet.microsoft.com/Run-Parallel-Parallel-377fd430

    .PARAMETER NoCloseOnTimeout
        Do not dispose of timed out tasks or attempt to close the runspace if threads have timed out. This will prevent the script from hanging in certain situations where threads become non-responsive, at the expense of leaking memory within the PowerShell host.

    .PARAMETER MaxQueue
        Maximum number of powershell instances to add to runspace pool.  If this is higher than $throttle, $timeout will be inaccurate

        If this is equal or less than throttle, there will be a performance impact

        The default value is $throttle times 3, if $runspaceTimeout is not specified
        The default value is $throttle, if $runspaceTimeout is specified

    .PARAMETER LogFile
        Path to a file where we can log results, including run time for each thread, whether it completes, completes with errors, or times out.

    .PARAMETER AppendLog
        Append to existing log

    .PARAMETER Quiet
        Disable progress bar

    .EXAMPLE
        Each example uses Test-ForPacs.ps1 which includes the following code:
            param($computer)

            if(test-connection $computer -count 1 -quiet -BufferSize 16){
                $object = [pscustomobject] @{
                    Computer=$computer;
                    Available=1;
                    Kodak=$(
                        if((test-path "\\$computer\c$\users\public\desktop\Kodak Direct View Pacs.url") -or (test-path "\\$computer\c$\documents and settings\all users\desktop\Kodak Direct View Pacs.url") ){"1"}else{"0"}
                    )
                }
            }
            else{
                $object = [pscustomobject] @{
                    Computer=$computer;
                    Available=0;
                    Kodak="NA"
                }
            }

            $object

    .EXAMPLE
        Invoke-Parallel -scriptfile C:\public\Test-ForPacs.ps1 -inputobject $(get-content C:\pcs.txt) -runspaceTimeout 10 -throttle 10

            Pulls list of PCs from C:\pcs.txt,
            Runs Test-ForPacs against each
            If any query takes longer than 10 seconds, it is disposed
            Only run 10 threads at a time

    .EXAMPLE
        Invoke-Parallel -scriptfile C:\public\Test-ForPacs.ps1 -inputobject c-is-ts-91, c-is-ts-95

            Runs against c-is-ts-91, c-is-ts-95 (-computername)
            Runs Test-ForPacs against each

    .EXAMPLE
        $stuff = [pscustomobject] @{
            ContentFile = "windows\system32\drivers\etc\hosts"
            Logfile = "C:\temp\log.txt"
        }

        $computers | Invoke-Parallel -parameter $stuff {
            $contentFile = join-path "\\$_\c$" $parameter.contentfile
            Get-Content $contentFile |
                set-content $parameter.logfile
        }

        This example uses the parameter argument.  This parameter is a single object.  To pass multiple items into the script block, we create a custom object (using a PowerShell v3 language) with properties we want to pass in.

        Inside the script block, $parameter is used to reference this parameter object.  This example sets a content file, gets content from that file, and sets it to a predefined log file.

    .EXAMPLE
        $test = 5
        1..2 | Invoke-Parallel -ImportVariables {$_ * $test}

        Add variables from the current session to the session state.  Without -ImportVariables $Test would not be accessible

    .EXAMPLE
        $test = 5
        1..2 | Invoke-Parallel {$_ * $Using:test}

        Reference a variable from the current session with the $Using:<Variable> syntax.  Requires PowerShell 3 or later. Note that -ImportVariables parameter is no longer necessary.

    .FUNCTIONALITY
        PowerShell Language

    .NOTES
        Credit to Boe Prox for the base runspace code and $Using implementation
            http://learn-powershell.net/2012/05/10/speedy-network-information-query-using-powershell/
            http://gallery.technet.microsoft.com/scriptcenter/Speedy-Network-Information-5b1406fb#content
            https://github.com/proxb/PoshRSJob/

        Credit to T Bryce Yehl for the Quiet and NoCloseOnTimeout implementations

        Credit to Sergei Vorobev for the many ideas and contributions that have improved functionality, reliability, and ease of use

    .LINK
        https://github.com/RamblingCookieMonster/Invoke-Parallel
    #>
    [cmdletbinding(DefaultParameterSetName='ScriptBlock')]
    Param (
        [Parameter(Mandatory=$false,position=0,ParameterSetName='ScriptBlock')]
        [System.Management.Automation.ScriptBlock]$ScriptBlock,

        [Parameter(Mandatory=$false,ParameterSetName='ScriptFile')]
        [ValidateScript({Test-Path $_ -pathtype leaf})]
        $ScriptFile,

        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [Alias('CN','__Server','IPAddress','Server','ComputerName')]
        [PSObject]$InputObject,

        [PSObject]$Parameter,

        [switch]$ImportVariables,
        [switch]$ImportModules,
        [switch]$ImportFunctions,

        [int]$Throttle = 20,
        [int]$SleepTimer = 200,
        [int]$RunspaceTimeout = 0,
        [switch]$NoCloseOnTimeout = $false,
        [int]$MaxQueue,

        [validatescript({Test-Path (Split-Path $_ -parent)})]
        [switch] $AppendLog = $false,
        [string]$LogFile,

        [switch] $Quiet = $false
    )
    begin {
        #No max queue specified?  Estimate one.
        #We use the script scope to resolve an odd PowerShell 2 issue where MaxQueue isn't seen later in the function
        if( -not $PSBoundParameters.ContainsKey('MaxQueue') ) {
            if($RunspaceTimeout -ne 0){ $script:MaxQueue = $Throttle }
            else{ $script:MaxQueue = $Throttle * 3 }
        }
        else {
            $script:MaxQueue = $MaxQueue
        }
        $ProgressId = Get-Random
        Write-Verbose "Throttle: '$throttle' SleepTimer '$sleepTimer' runSpaceTimeout '$runspaceTimeout' maxQueue '$maxQueue' logFile '$logFile'"

        #If they want to import variables or modules, create a clean runspace, get loaded items, use those to exclude items
        if ($ImportVariables -or $ImportModules -or $ImportFunctions) {
            $StandardUserEnv = [powershell]::Create().addscript({

                #Get modules, snapins, functions in this clean runspace
                $Modules = Get-Module | Select-Object -ExpandProperty Name
                $Snapins = Get-PSSnapin | Select-Object -ExpandProperty Name
                $Functions = Get-ChildItem function:\ | Select-Object -ExpandProperty Name

                #Get variables in this clean runspace
                #Called last to get vars like $? into session
                $Variables = Get-Variable | Select-Object -ExpandProperty Name

                #Return a hashtable where we can access each.
                @{
                    Variables   = $Variables
                    Modules     = $Modules
                    Snapins     = $Snapins
                    Functions   = $Functions
                }
            }).invoke()[0]

            if ($ImportVariables) {
                #Exclude common parameters, bound parameters, and automatic variables
                Function _temp {[cmdletbinding(SupportsShouldProcess=$True)] param() }
                $VariablesToExclude = @( (Get-Command _temp | Select-Object -ExpandProperty parameters).Keys + $PSBoundParameters.Keys + $StandardUserEnv.Variables )
                Write-Verbose "Excluding variables $( ($VariablesToExclude | Sort-Object ) -join ", ")"

                # we don't use 'Get-Variable -Exclude', because it uses regexps.
                # One of the veriables that we pass is '$?'.
                # There could be other variables with such problems.
                # Scope 2 required if we move to a real module
                $UserVariables = @( Get-Variable | Where-Object { -not ($VariablesToExclude -contains $_.Name) } )
                Write-Verbose "Found variables to import: $( ($UserVariables | Select-Object -expandproperty Name | Sort-Object ) -join ", " | Out-String).`n"
            }
            if ($ImportModules) {
                $UserModules = @( Get-Module | Where-Object {$StandardUserEnv.Modules -notcontains $_.Name -and (Test-Path $_.Path -ErrorAction SilentlyContinue)} | Select-Object -ExpandProperty Path )
                $UserSnapins = @( Get-PSSnapin | Select-Object -ExpandProperty Name | Where-Object {$StandardUserEnv.Snapins -notcontains $_ } )
            }
            if($ImportFunctions) {
                $UserFunctions = @( Get-ChildItem function:\ | Where-Object { $StandardUserEnv.Functions -notcontains $_.Name } )
            }
        }

        #region functions
            Function Get-RunspaceData {
                [cmdletbinding()]
                param( [switch]$Wait )
                #loop through runspaces
                #if $wait is specified, keep looping until all complete
                Do {
                    #set more to false for tracking completion
                    $more = $false

                    #Progress bar if we have inputobject count (bound parameter)
                    if (-not $Quiet) {
                        Write-Progress -Id $ProgressId -Activity "Running Query" -Status "Starting threads"`
                            -CurrentOperation "$startedCount threads defined - $totalCount input objects - $script:completedCount input objects processed"`
                            -PercentComplete $( Try { $script:completedCount / $totalCount * 100 } Catch {0} )
                    }

                    #run through each runspace.
                    Foreach($runspace in $runspaces) {

                        #get the duration - inaccurate
                        $currentdate = Get-Date
                        $runtime = $currentdate - $runspace.startTime
                        $runMin = [math]::Round( $runtime.totalminutes ,2 )

                        #set up log object
                        $log = "" | Select-Object Date, Action, Runtime, Status, Details
                        $log.Action = "Removing:'$($runspace.object)'"
                        $log.Date = $currentdate
                        $log.Runtime = "$runMin minutes"

                        #If runspace completed, end invoke, dispose, recycle, counter++
                        If ($runspace.Runspace.isCompleted) {

                            $script:completedCount++

                            #check if there were errors
                            if($runspace.powershell.Streams.Error.Count -gt 0) {
                                #set the logging info and move the file to completed
                                $log.status = "CompletedWithErrors"
                                Write-Verbose ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1]
                                foreach($ErrorRecord in $runspace.powershell.Streams.Error) {
                                    Write-Error -ErrorRecord $ErrorRecord
                                }
                            }
                            else {
                                #add logging details and cleanup
                                $log.status = "Completed"
                                Write-Verbose ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1]
                            }

                            #everything is logged, clean up the runspace
                            $runspace.powershell.EndInvoke($runspace.Runspace)
                            $runspace.powershell.dispose()
                            $runspace.Runspace = $null
                            $runspace.powershell = $null
                        }
                        #If runtime exceeds max, dispose the runspace
                        ElseIf ( $runspaceTimeout -ne 0 -and $runtime.totalseconds -gt $runspaceTimeout) {
                            $script:completedCount++
                            $timedOutTasks = $true

                            #add logging details and cleanup
                            $log.status = "TimedOut"
                            Write-Verbose ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1]
                            Write-Error "Runspace timed out at $($runtime.totalseconds) seconds for the object:`n$($runspace.object | out-string)"

                            #Depending on how it hangs, we could still get stuck here as dispose calls a synchronous method on the powershell instance
                            if (!$noCloseOnTimeout) { $runspace.powershell.dispose() }
                            $runspace.Runspace = $null
                            $runspace.powershell = $null
                            $completedCount++
                        }

                        #If runspace isn't null set more to true
                        ElseIf ($runspace.Runspace -ne $null ) {
                            $log = $null
                            $more = $true
                        }

                        #log the results if a log file was indicated
                        if($logFile -and $log) {
                            ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1] | out-file $LogFile -append
                        }
                    }

                    #Clean out unused runspace jobs
                    $temphash = $runspaces.clone()
                    $temphash | Where-Object { $_.runspace -eq $Null } | ForEach-Object {
                        $Runspaces.remove($_)
                    }

                    #sleep for a bit if we will loop again
                    if($PSBoundParameters['Wait']){ Start-Sleep -milliseconds $SleepTimer }

                #Loop again only if -wait parameter and there are more runspaces to process
                } while ($more -and $PSBoundParameters['Wait'])

            #End of runspace function
            }
        #endregion functions

        #region Init

            if($PSCmdlet.ParameterSetName -eq 'ScriptFile') {
                $ScriptBlock = [scriptblock]::Create( $(Get-Content $ScriptFile | out-string) )
            }
            elseif($PSCmdlet.ParameterSetName -eq 'ScriptBlock') {
                #Start building parameter names for the param block
                [string[]]$ParamsToAdd = '$_'
                if( $PSBoundParameters.ContainsKey('Parameter') ) {
                    $ParamsToAdd += '$Parameter'
                }

                $UsingVariableData = $Null

                # This code enables $Using support through the AST.
                # This is entirely from  Boe Prox, and his https://github.com/proxb/PoshRSJob module; all credit to Boe!

                if($PSVersionTable.PSVersion.Major -gt 2) {
                    #Extract using references
                    $UsingVariables = $ScriptBlock.ast.FindAll({$args[0] -is [System.Management.Automation.Language.UsingExpressionAst]},$True)

                    If ($UsingVariables) {
                        $List = New-Object 'System.Collections.Generic.List`1[System.Management.Automation.Language.VariableExpressionAst]'
                        ForEach ($Ast in $UsingVariables) {
                            [void]$list.Add($Ast.SubExpression)
                        }

                        $UsingVar = $UsingVariables | Group-Object -Property SubExpression | ForEach-Object {$_.Group | Select-Object -First 1}

                        #Extract the name, value, and create replacements for each
                        $UsingVariableData = ForEach ($Var in $UsingVar) {
                            try {
                                $Value = Get-Variable -Name $Var.SubExpression.VariablePath.UserPath -ErrorAction Stop
                                [pscustomobject]@{
                                    Name = $Var.SubExpression.Extent.Text
                                    Value = $Value.Value
                                    NewName = ('$__using_{0}' -f $Var.SubExpression.VariablePath.UserPath)
                                    NewVarName = ('__using_{0}' -f $Var.SubExpression.VariablePath.UserPath)
                                }
                            }
                            catch {
                                Write-Error "$($Var.SubExpression.Extent.Text) is not a valid Using: variable!"
                            }
                        }
                        $ParamsToAdd += $UsingVariableData | Select-Object -ExpandProperty NewName -Unique

                        $NewParams = $UsingVariableData.NewName -join ', '
                        $Tuple = [Tuple]::Create($list, $NewParams)
                        $bindingFlags = [Reflection.BindingFlags]"Default,NonPublic,Instance"
                        $GetWithInputHandlingForInvokeCommandImpl = ($ScriptBlock.ast.gettype().GetMethod('GetWithInputHandlingForInvokeCommandImpl',$bindingFlags))

                        $StringScriptBlock = $GetWithInputHandlingForInvokeCommandImpl.Invoke($ScriptBlock.ast,@($Tuple))

                        $ScriptBlock = [scriptblock]::Create($StringScriptBlock)

                        Write-Verbose $StringScriptBlock
                    }
                }

                $ScriptBlock = $ExecutionContext.InvokeCommand.NewScriptBlock("param($($ParamsToAdd -Join ", "))`r`n" + $Scriptblock.ToString())
            }
            else {
                Throw "Must provide ScriptBlock or ScriptFile"; Break
            }

            Write-Debug "`$ScriptBlock: $($ScriptBlock | Out-String)"
            Write-Verbose "Creating runspace pool and session states"

            #If specified, add variables and modules/snapins to session state
            $sessionstate = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
            if($ImportVariables -and $UserVariables.count -gt 0) {
                foreach($Variable in $UserVariables) {
                    $sessionstate.Variables.Add((New-Object -TypeName System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList $Variable.Name, $Variable.Value, $null) )
                }
            }
            if ($ImportModules) {
                if($UserModules.count -gt 0) {
                    foreach($ModulePath in $UserModules) {
                        $sessionstate.ImportPSModule($ModulePath)
                    }
                }
                if($UserSnapins.count -gt 0) {
                    foreach($PSSnapin in $UserSnapins) {
                        [void]$sessionstate.ImportPSSnapIn($PSSnapin, [ref]$null)
                    }
                }
            }
            if($ImportFunctions -and $UserFunctions.count -gt 0) {
                foreach ($FunctionDef in $UserFunctions) {
                    $sessionstate.Commands.Add((New-Object System.Management.Automation.Runspaces.SessionStateFunctionEntry -ArgumentList $FunctionDef.Name,$FunctionDef.ScriptBlock))
                }
            }

            #Create runspace pool
            $runspacepool = [runspacefactory]::CreateRunspacePool(1, $Throttle, $sessionstate, $Host)
            $runspacepool.Open()

            Write-Verbose "Creating empty collection to hold runspace jobs"
            $Script:runspaces = New-Object System.Collections.ArrayList

            #If inputObject is bound get a total count and set bound to true
            $bound = $PSBoundParameters.keys -contains "InputObject"
            if(-not $bound) {
                [System.Collections.ArrayList]$allObjects = @()
            }

            #Set up log file if specified
            if( $LogFile -and (-not (Test-Path $LogFile) -or $AppendLog -eq $false)){
                New-Item -ItemType file -Path $logFile -Force | Out-Null
                ("" | Select-Object -Property Date, Action, Runtime, Status, Details | ConvertTo-Csv -NoTypeInformation -Delimiter ";")[0] | Out-File $LogFile
            }

            #write initial log entry
            $log = "" | Select-Object -Property Date, Action, Runtime, Status, Details
                $log.Date = Get-Date
                $log.Action = "Batch processing started"
                $log.Runtime = $null
                $log.Status = "Started"
                $log.Details = $null
                if($logFile) {
                    ($log | convertto-csv -Delimiter ";" -NoTypeInformation)[1] | Out-File $LogFile -Append
                }
            $timedOutTasks = $false
        #endregion INIT
    }
    process {
        #add piped objects to all objects or set all objects to bound input object parameter
        if($bound) {
            $allObjects = $InputObject
        }
        else {
            [void]$allObjects.add( $InputObject )
        }
    }
    end {
        #Use Try/Finally to catch Ctrl+C and clean up.
        try {
            #counts for progress
            $totalCount = $allObjects.count
            $script:completedCount = 0
            $startedCount = 0
            foreach($object in $allObjects) {
                #region add scripts to runspace pool
                    #Create the powershell instance, set verbose if needed, supply the scriptblock and parameters
                    $powershell = [powershell]::Create()

                    if ($VerbosePreference -eq 'Continue') {
                        [void]$PowerShell.AddScript({$VerbosePreference = 'Continue'})
                    }

                    [void]$PowerShell.AddScript($ScriptBlock).AddArgument($object)

                    if ($parameter) {
                        [void]$PowerShell.AddArgument($parameter)
                    }

                    # $Using support from Boe Prox
                    if ($UsingVariableData) {
                        Foreach($UsingVariable in $UsingVariableData) {
                            Write-Verbose "Adding $($UsingVariable.Name) with value: $($UsingVariable.Value)"
                            [void]$PowerShell.AddArgument($UsingVariable.Value)
                        }
                    }

                    #Add the runspace into the powershell instance
                    $powershell.RunspacePool = $runspacepool

                    #Create a temporary collection for each runspace
                    $temp = "" | Select-Object PowerShell, StartTime, object, Runspace
                    $temp.PowerShell = $powershell
                    $temp.StartTime = Get-Date
                    $temp.object = $object

                    #Save the handle output when calling BeginInvoke() that will be used later to end the runspace
                    $temp.Runspace = $powershell.BeginInvoke()
                    $startedCount++

                    #Add the temp tracking info to $runspaces collection
                    Write-Verbose ( "Adding {0} to collection at {1}" -f $temp.object, $temp.starttime.tostring() )
                    $runspaces.Add($temp) | Out-Null

                    #loop through existing runspaces one time
                    Get-RunspaceData

                    #If we have more running than max queue (used to control timeout accuracy)
                    #Script scope resolves odd PowerShell 2 issue
                    $firstRun = $true
                    while ($runspaces.count -ge $Script:MaxQueue) {
                        #give verbose output
                        if($firstRun) {
                            Write-Verbose "$($runspaces.count) items running - exceeded $Script:MaxQueue limit."
                        }
                        $firstRun = $false

                        #run get-runspace data and sleep for a short while
                        Get-RunspaceData
                        Start-Sleep -Milliseconds $sleepTimer
                    }
                #endregion add scripts to runspace pool
            }
            Write-Verbose ( "Finish processing the remaining runspace jobs: {0}" -f ( @($runspaces | Where-Object {$_.Runspace -ne $Null}).Count) )

            Get-RunspaceData -wait
            if (-not $quiet) {
                Write-Progress -Id $ProgressId -Activity "Running Query" -Status "Starting threads" -Completed
            }
        }
        finally {
            #Close the runspace pool, unless we specified no close on timeout and something timed out
            if ( ($timedOutTasks -eq $false) -or ( ($timedOutTasks -eq $true) -and ($noCloseOnTimeout -eq $false) ) ) {
                Write-Verbose "Closing the runspace pool"
                $runspacepool.close()
            }
            #collect garbage
            [gc]::Collect()
        }
    }
}
function Invoke-SmoCheck {
    <#
    .SYNOPSIS
    Checks for PowerShell SMO version vs SQL Server's SMO version.

#>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [object]$SqlInstance
    )

    if ($script:smocheck -ne $true) {
        $script:smocheck = $true
        $smo = (([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.Fullname -like "Microsoft.SqlServer.SMO,*" }).FullName -Split ", ")[1]
        $smo = ([version]$smo.TrimStart("Version=")).Major
        $serverversion = $SqlInstance.version.major

        if ($serverversion - $smo -gt 1) {
            Write-Warning "Your version of SMO is $smo, which is significantly older than $($SqlInstance.name)'s version $($SqlInstance.version.major)."
            Write-Warning "This may present an issue when migrating certain portions of SQL Server."
            Write-Warning "If you encounter issues, consider upgrading SMO."
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Invoke-SteppablePipeline
{
<#
    .SYNOPSIS
        Allows using steppable pipelines on the pipeline.
    
    .DESCRIPTION
        Allows using steppable pipelines on the pipeline.
        
    .PARAMETER InputObject
        The object(s) to process
        Should only receive input from the pipeline!
    
    .PARAMETER Pipeline
        The pipeline to execute
    
    .EXAMPLE
        PS C:\> Get-ChildItem | Invoke-SteppablePipeline -Pipeline $steppablePipeline
    
        Processes the object returned by Get-ChildItem in the pipeline defined
#>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        $InputObject,
        
        [Parameter(Mandatory = $true)]
        $Pipeline
    )
    
    process
    {
        $Pipeline.Process($InputObject)
    }
}
function Invoke-TagCommand ([string]$Tag, [string]$Keyword) {
    <#
.SYNOPSIS
    An internal command, feel free to ignore.

.EXAMPLE
    Tag-Command -Tag Restore -Keyword Restore
    Tag-Command -Tag Backup -Keyword Backup
    Tag-Command -Tag Orphan -Keyword Orphan
    Tag-Command -Tag DisasterRecovery -Keyword Attach
    Tag-Command -Tag DisasterRecovery -Keyword Detach
    Tag-Command -Tag Snapshot -Keyword Snapshot
    Tag-Command -Tag Memory -Keyword Memory
    Tag-Command -Tag DisasterRecovery -Keyword Restore
    Tag-Command -Tag DisasterRecovery -Keyword Backup
    Tag-Command -Tag Storage -Keyword disk
    Tag-Command -Tag Storage -Keyword storage
    Tag-Command -Tag Migration -Keyword "Copy-"
    Tag-Command -Tag SPN -Keyword Kerberos
    Tag-Command -Tag SPN -Keyword SPN
    Tag-Command -Tag CIM -Keyword CimSession
    Tag-Command -Tag SQLWMI -Keyword Invoke-ManagedComputerCommand
    Tag-Command -Tag WSMan -Keyword Invoke-Command

#>

    $tagsRex = ([regex]'(?m)^[\s]{0,15}Tags:(.*)$')
    $modulepath = (Get-Module -Name dbatools).Path
    $directory = Split-Path $modulepath
    $basedir = "$directory\functions\"
    Import-Module $modulepath -force
    $allfiles = Get-ChildItem $basedir
    foreach ($f in $allfiles) {
        if ($f -eq "Find-DbaCommand.ps1") { continue }

        $content = Get-Content $f.fullname
        if ($content -like "*$keyword*") {
            Write-Message -Level Warning -Message "$f needs a tag tag"
            $cmdname = $f.name.replace('.ps1', '')

            $fullhelp = get-help $cmdname -full

            $as = $fullhelp.alertset | out-string

            $tags = $tagsrex.Match($as).Groups[1].Value

            if ($tags) {
                $tags = $tags.ToString().split(',').Trim()
                Write-Message -Level Warning -Message "adding tags to existing ones"
                if ($tag -in $tags) {
                    Write-Message -Level Warning -Message "tag $tag is already present"
                    continue
                }
                $out = @()
                foreach ($line in $content) {
                    if ($line.trim().startsWith('Tags:')) {
                        $out += "$line, $tag"
                    }
                    else {
                        $out += $line
                    }
                }
                Write-Message -Level Warning -Message "replacing content into $($f.fullname)"
                $out -join "`r`n" | Set-Content $f.fullname -Encoding UTF8

            }
            else {
                Write-Message -Level Warning -Message "need to add tags"
                $out = @()
                foreach ($line in $content) {
                    if ($line.startsWith('.NOTES')) {
                        $out += '.NOTES'
                        $out += "Tags: $tag"
                    }
                    else {
                        $out += $line
                    }
                }
                Write-Message -Level Warning -Message "replacing content into $($f.fullname)"
                $out -join "`r`n" | Set-Content $f.fullname -Encoding UTF8
            }
        }
    }
}
function Join-AdminUnc {
    <#
    .SYNOPSIS
    Internal function. Parses a path to make it an admin UNC.
#>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$servername,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$filepath

    )

    if (!$filepath) { return }
    if ($filepath.StartsWith("\\")) { return $filepath }

    $servername = $servername.Split("\")[0]

    if ($filepath.length -gt 0 -and $filepath -ne [System.DbNull]::Value) {
        $newpath = Join-Path "\\$servername\" $filepath.replace(':', '$')
        return $newpath
    }
    else { return }
}
function New-DbaLogShippingPrimaryDatabase {
    <#
        .SYNOPSIS
            New-DbaLogShippingPrimaryDatabase add the primary database to log shipping

        .DESCRIPTION
            New-DbaLogShippingPrimaryDatabase will add the primary database to log shipping.
            This is executed on the primary server.

        .PARAMETER SqlInstance
            SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Database to set up log shipping for.

        .PARAMETER BackupDirectory
            Is the path to the backup folder on the primary server.

        .PARAMETER BackupJob
            Is the name of the SQL Server Agent job on the primary server that copies the backup into the backup folder.

        .PARAMETER BackupJobID
            The SQL Server Agent job ID associated with the backup job on the primary server.

        .PARAMETER BackupRetention
            Is the length of time, in minutes, to retain the log backup file in the backup directory on the primary server.

        .PARAMETER BackupShare
            Is the network path to the backup directory on the primary server.

        .PARAMETER BackupThreshold
            Is the length of time, in minutes, after the last backup before a threshold_alert error is raised.
            The default is 60.

        .PARAMETER CompressBackup
            Enables the use of backup compression

        .PARAMETER ThressAlert
            Is the length of time, in minutes, when the alert is to be raised when the backup threshold is exceeded.
            The default is 14,420.

        .PARAMETER HistoryRetention
            Is the length of time in minutes in which the history will be retained.
            The default is 14420.

        .PARAMETER MonitorServer
            Is the name of the monitor server.
            The default is the name of the primary server.

        .PARAMETER MonitorCredential
            Allows you to login to enter a secure credential.
            This is only needed in combination with MonitorServerSecurityMode having either a 0 or 'sqlserver' value.
            To use: $scred = Get-Credential, then pass $scred object to the -MonitorCredential parameter.

        .PARAMETER MonitorServerSecurityMode
            The security mode used to connect to the monitor server. Allowed values are 0, "sqlserver", 1, "windows"
            The default is 1 or Windows.

        .PARAMETER ThresholdAlertEnabled
            Specifies whether an alert will be raised when backup threshold is exceeded.
            The default is 0.

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER Force
            The force parameter will ignore some errors in the parameters and assume defaults.
            It will also remove the any present schedules with the same name for the specific job.

        .NOTES
            Author: Sander Stad (@sqlstad, sqlstad.nl)
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            New-DbaLogShippingPrimaryDatabase -SqlInstance sql1 -Database DB1 -BackupDirectory D:\data\logshipping -BackupJob LSBackup_DB1 -BackupRetention 4320 -BackupShare "\\sql1\logshipping" -BackupThreshold 60 -CompressBackup -HistoryRetention 14420 -MonitorServer sql1 -ThresholdAlertEnabled

    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]

    param (
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [object]$SqlInstance,

        [System.Management.Automation.PSCredential]
        $SqlCredential,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [object]$Database,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$BackupDirectory,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$BackupJob,

        [Parameter(Mandatory = $true)]
        [int]$BackupRetention,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$BackupShare,

        [int]$BackupThreshold = 60,

        [switch]$CompressBackup,

        [int]$ThressAlert = 14420,

        [int]$HistoryRetention = 14420,

        [string]$MonitorServer,

        [ValidateSet(0, "sqlserver", 1, "windows")]
        [object]$MonitorServerSecurityMode = 1,

        [System.Management.Automation.PSCredential]
        $MonitorCredential,

        [switch]$ThresholdAlertEnabled,

        [Alias('Silent')]
        [switch]$EnableException,

        [switch]$Force
    )

    # Try connecting to the instance
    Write-Message -Message "Connecting to $SqlInstance" -Level Verbose
    try {
        $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
    }
    catch {
        Stop-Function -Message "Could not connect to Sql Server instance" -Target $SqlInstance -Continue
    }

    # Check if the backup UNC path is correct and reachable
    if ([bool]([uri]$BackupShare).IsUnc -and $BackupShare -notmatch '^\\(?:\\[^<>:`"/\\|?*]+)+$') {
        Stop-Function -Message "The backup share path $BackupShare should be formatted in the form \\server\share." -Target $SqlInstance
        return
    }
    else {
        if (-not ((Test-Path $BackupShare -PathType Container -IsValid) -and ((Get-Item $BackupShare).PSProvider.Name -eq 'FileSystem'))) {
            Stop-Function -Message "The backup share path $BackupShare is not valid or can't be reached." -Target $SqlInstance
            return
        }
    }

    # Check the backup compression
    if ($CompressBackup -eq $true) {
        Write-Message -Message "Setting backup compression to 1." -Level Verbose
        $BackupCompression = 1
    }
    elseif ($CompressBackup -eq $false) {
        Write-Message -Message "Setting backup compression to 0." -Level Verbose
        $BackupCompression = 0
    }
    elseif (-not $CompressBackup) {
        $defaultCompression = (Get-DbaSpConfigure -SqlInstance $SqlInstance -ConfigName DefaultBackupCompression).ConfiguredValue
        Write-Message -Message "Setting backup compression to default value $defaultCompression." -Level Verbose
        $BackupCompression = $defaultCompression

    }

    # Check of the MonitorServerSecurityMode value is of type string and set the integer value
    if ($MonitorServerSecurityMode -notin 0, 1) {
        $MonitorServerSecurityMode = switch ($MonitorServerSecurityMode) {"WINDOWS" { 1 } "SQLSERVER" { 0 } }
        Write-Message -Message "Setting monitor server security mode to $MonitorServerSecurityMode." -Level Verbose
    }

    # Check the MonitorServer
    if ($Force -and -not $MonitorServer) {
        $MonitorServer = $SqlInstance
        Write-Message -Message "Setting monitor server to $MonitorServer." -Level Verbose
    }

    # Check the MonitorServerSecurityMode if it's SQL Server authentication
    if ($MonitorServerSecurityMode -eq 0 -and -not $MonitorCredential) {
        Stop-Function -Message "The MonitorServerCredential cannot be empty when using SQL Server authentication." -Target $SqlInstance
        return
    }
    elseif ($MonitorServerSecurityMode -eq 0 -and $MonitorCredential) {
        # Get the username and password from the credential
        $MonitorLogin = $MonitorCredential.UserName
        $MonitorPassword = $MonitorCredential.GetNetworkCredential().Password

        # Check if the user is in the database
        if ($server.Databases['master'].Users.Name -notcontains $MonitorLogin) {
            Stop-Function -Message "User $MonitorLogin for monitor login must be in the master database." -Target $SqlInstance
            return
        }
    }

    # Check if the database is present on the source sql server
    if ($server.Databases.Name -notcontains $Database) {
        Stop-Function -Message "Database $Database is not available on instance $SqlInstance" -Target $SqlInstance
        return
    }

    # Check the if Threshold alert needs to be enabled
    if ($ThresholdAlertEnabled) {
        [int]$ThresholdAlertEnabled = 1
        Write-Message -Message "Setting Threshold alert to $ThresholdAlertEnabled." -Level Verbose
    }
    else {
        [int]$ThresholdAlertEnabled = 0
        Write-Message -Message "Setting Threshold alert to $ThresholdAlertEnabled." -Level Verbose
    }

    # Set the log shipping primary
    $Query = "
        DECLARE @LS_BackupJobId AS uniqueidentifier;
        DECLARE @LS_PrimaryId AS uniqueidentifier;
        EXEC master.sys.sp_add_log_shipping_primary_database
            @database = N'$Database'
            ,@backup_directory = N'$BackupDirectory'
            ,@backup_share = N'$BackupShare'
            ,@backup_job_name = N'$BackupJob'
            ,@backup_retention_period = $BackupRetention"

    if ($SqlInstance.Version.Major -gt 9) {
        $Query += ",@backup_compression = $BackupCompression"
    }

    if ($MonitorServer) {
        $Query += ",@monitor_server = N'$MonitorServer'
            ,@monitor_server_security_mode = $MonitorServerSecurityMode
            ,@threshold_alert = $ThressAlert
            ,@threshold_alert_enabled = $ThresholdAlertEnabled"
    }

    $Query += ",@backup_threshold = $BackupThreshold
            ,@history_retention_period = $HistoryRetention
            ,@backup_job_id = @LS_BackupJobId OUTPUT
            ,@primary_id = @LS_PrimaryId OUTPUT "

    # Check the MonitorServerSecurityMode if it's SQL Server authentication
    if ($MonitorServer -and $MonitorServerSecurityMode -eq 0 ) {
        $Query += ",@monitor_server_login = N'$MonitorLogin'
            ,@monitor_server_password = N'$MonitorPassword' "
    }

    if ($server.Version.Major -gt 9) {
        $Query += ",@overwrite = 1;"
    }
    else {
        $Query += ";"
    }

    # Execute the query to add the log shipping primary
    if ($PSCmdlet.ShouldProcess($SqlServer, ("Configuring logshipping for primary database $Database on $SqlInstance"))) {
        try {
            Write-Message -Message "Configuring logshipping for primary database $Database." -Level Output
            Write-Message -Message "Executing query:`n$Query" -Level Verbose
            $server.Query($Query)
        }
        catch {
            Write-Message -Message "$($_.Exception.InnerException.InnerException.InnerException.InnerException.Message)" -Level Warning
            Stop-Function -Message "Error executing the query.`n$($_.Exception.Message)`n$($Query)" -ErrorRecord $_ -Target $SqlInstance -Continue
        }
    }

    Write-Message -Message "Finished adding the primary database $Database to log shipping." -Level Output

}
function New-DbaLogShippingPrimarySecondary {
    <#
        .SYNOPSIS
            New-DbaLogShippingPrimarySecondary adds an entry for a secondary database.

        .DESCRIPTION
            New-DbaLogShippingPrimarySecondary adds an entry for a secondary database.
            This is executed on the primary server.

        .PARAMETER SqlInstance
            SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER PrimaryDatabase
            Is the name of the database on the primary server.

        .PARAMETER SecondaryDatabase
            Is the name of the secondary database.

        .PARAMETER SecondaryServer
            Is the name of the secondary server.

        .PARAMETER SecondarySqlCredential
            Allows you to login to servers using SQL Logins as opposed to Windows Auth/Integrated/Trusted. To use:
            $scred = Get-Credential, then pass $scred object to the -SecondarySqlCredential parameter.

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Sander Stad (@sqlstad, sqlstad.nl)
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/New-DbaLogShippingPrimarySecondary

        .EXAMPLE
            New-DbaLogShippingPrimarySecondary -SqlInstance sql1 -PrimaryDatabase DB1 -SecondaryServer sql2 -SecondaryDatabase DB1_DR
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param (
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [object]$PrimaryDatabase,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [object]$SecondaryDatabase,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [DBAInstanceParameter]$SecondaryServer,
        [PSCredential]$SecondarySqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    # Try connecting to the instance
    Write-Message -Message "Connecting to $SqlInstance" -Level Verbose
    try {
        $ServerPrimary = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
    }
    catch {
        Stop-Function -Message "Could not connect to Sql Server instance" -Target $SqlInstance -Continue
    }

    # Try connecting to the instance
    Write-Message -Message "Connecting to $SecondaryServer" -Level Verbose
    try {
        $ServerSecondary = Connect-SqlInstance -SqlInstance $SecondaryServer -SqlCredential $SecondarySqlCredential
    }
    catch {
        Stop-Function -Message "Could not connect to Sql Server instance" -Target $SecondaryServer -Continue
    }

    # Check if the database is present on the source sql server
    if ($ServerPrimary.Databases.Name -notcontains $PrimaryDatabase) {
        Stop-Function -Message "Database $PrimaryDatabase is not available on instance $SqlInstance" -ErrorRecord $_ -Target $SqlInstance -Continue
    }

    # Check if the database is present on the destination sql server
    if ($ServerSecondary.Databases.Name -notcontains $SecondaryDatabase) {
        Stop-Function -Message "Database $SecondaryDatabase is not available on instance $SecondaryServer" -ErrorRecord $_ -Target $SecondaryServer -Continue
    }

    $Query = "SELECT primary_database FROM msdb.dbo.log_shipping_primary_databases WHERE primary_database = '$PrimaryDatabase'"

    try {
        Write-Message -Message "Executing query:`n$Query" -Level Verbose
        $Result = $ServerPrimary.Query($Query)
        if ($Result.Count -eq 0 -or $Result[0] -ne $PrimaryDatabase) {
            Stop-Function -Message "Database $PrimaryDatabase does not exist as log shipping primary.`nPlease run New-DbaLogShippingPrimaryDatabase first."  -ErrorRecord $_ -Target $SqlInstance -Continue
        }
    }
    catch {
        Stop-Function -Message "Error executing the query.`n$($_.Exception.Message)`n$Query" -ErrorRecord $_ -Target $SqlInstance -Continue
    }

    # Set the query for the log shipping primary and secondary
    $Query = "EXEC master.sys.sp_add_log_shipping_primary_secondary
        @primary_database = N'$PrimaryDatabase'
        ,@secondary_server = N'$SecondaryServer'
        ,@secondary_database = N'$SecondaryDatabase' "

    if ($ServerPrimary.Version.Major -gt 9) {
        $Query += ",@overwrite = 1;"
    }
    else {
        $Query += ";"
    }

    # Execute the query to add the log shipping primary
    if ($PSCmdlet.ShouldProcess($SqlInstance, ("Configuring logshipping connecting the primary database $PrimaryDatabase to secondary database $SecondaryDatabase on $SqlInstance"))) {
        try {
            Write-Message -Message "Configuring logshipping connecting the primary database $PrimaryDatabase to secondary database $SecondaryDatabase on $SqlInstance." -Level Output
            Write-Message -Message "Executing query:`n$Query" -Level Verbose
            $ServerPrimary.Query($Query)
        }
        catch {
            Write-Message -Message "$($_.Exception.InnerException.InnerException.InnerException.InnerException.Message)" -Level Warning
            Stop-Function -Message "Error executing the query.`n$($_.Exception.Message)`n$Query" -ErrorRecord $_ -Target $SqlInstance -Continue
        }
    }

    Write-Message -Message "Finished configuring of primary database $PrimaryDatabase to secondary database $SecondaryDatabase." -Level Output

}
function New-DbaLogShippingSecondaryDatabase {
    <#
        .SYNOPSIS
            New-DbaLogShippingSecondaryDatabase sets up a secondary databases for log shipping.

        .DESCRIPTION
            New-DbaLogShippingSecondaryDatabase sets up a secondary databases for log shipping.
            This is executed on the secondary server.

        .PARAMETER SqlInstance
            SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER BufferCount
            The total number of buffers used by the backup or restore operation.
            The default is -1.

        .PARAMETER BlockSize
            The size, in bytes, that is used as the block size for the backup device.
            The default is -1.

        .PARAMETER DisconnectUsers
            If set to 1, users are disconnected from the secondary database when a restore operation is performed.
            Te default is 0.

        .PARAMETER HistoryRetention
            Is the length of time in minutes in which the history is retained.
            The default is 14420.

        .PARAMETER MaxTransferSize
            The size, in bytes, of the maximum input or output request which is issued by SQL Server to the backup device.

        .PARAMETER PrimaryServer
            The name of the primary instance of the Microsoft SQL Server Database Engine in the log shipping configuration.

        .PARAMETER PrimaryDatabase
            Is the name of the database on the primary server.

        .PARAMETER RestoreAll
            If set to 1, the secondary server restores all available transaction log backups when the restore job runs.
            The default is 1.

        .PARAMETER RestoreDelay
            The amount of time, in minutes, that the secondary server waits before restoring a given backup file.
            The default is 0.

        .PARAMETER RestoreMode
            The restore mode for the secondary database. The default is 0.
            0 = Restore log with NORECOVERY.
            1 = Restore log with STANDBY.

        .PARAMETER RestoreThreshold
            The number of minutes allowed to elapse between restore operations before an alert is generated.

        .PARAMETER SecondaryDatabase
            Is the name of the secondary database.

        .PARAMETER ThresholdAlert
            Is the alert to be raised when the backup threshold is exceeded.
            The default is 14420.

        .PARAMETER ThresholdAlertEnabled
            Specifies whether an alert is raised when backup_threshold is exceeded.

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER Force
            The force parameter will ignore some errors in the parameters and assume defaults.
            It will also remove the any present schedules with the same name for the specific job.

        .NOTES
            Author: Sander Stad (@sqlstad, sqlstad.nl)
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            New-DbaLogShippingSecondaryDatabase -SqlInstance sql2 -SecondaryDatabase DB1_DR -PrimaryServer sql1 -PrimaryDatabase DB1 -RestoreDelay 0 -RestoreMode standby -DisconnectUsers -RestoreThreshold 45 -ThresholdAlertEnabled -HistoryRetention 14420
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]

    param (
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [int]$BufferCount = -1,
        [int]$BlockSize = -1,
        [switch]$DisconnectUsers,
        [int]$HistoryRetention = 14420,
        [int]$MaxTransferSize,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [DbaInstanceParameter]$PrimaryServer,
        [PSCredential]$PrimarySqlCredential,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [object]$PrimaryDatabase,
        [int]$RestoreAll = 1,
        [int]$RestoreDelay = 0,
        [ValidateSet(0, 'NoRecovery', 1, 'Standby')]
        [object]$RestoreMode = 0,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [int]$RestoreThreshold,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [object]$SecondaryDatabase,
        [int]$ThresholdAlert = 14420,
        [switch]$ThresholdAlertEnabled,
        [Alias('Silent')]
        [switch]$EnableException,
        [switch]$Force
    )

    # Try connecting to the instance
    Write-Message -Message "Connecting to $SqlInstance" -Level Verbose
    try {
        $ServerSecondary = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
    }
    catch {
        Stop-Function -Message "Failure" -Category ConnectionError -Target $SqlInstance -ErrorRecord $_ -Continue
    }

    # Try connecting to the instance
    Write-Message -Message "Connecting to $PrimaryServer" -Level Verbose
    try {
        $ServerPrimary = Connect-SqlInstance -SqlInstance $PrimaryServer -SqlCredential $PrimarySqlCredential
    }
    catch {
        Stop-Function -Message "Failure" -Category ConnectionError -Target $PrimaryServer -ErrorRecord $_ -Continue
    }

    # Check if the database is present on the primary sql server
    if ($ServerPrimary.Databases.Name -notcontains $PrimaryDatabase) {
        Stop-Function -Message "Database $PrimaryDatabase is not available on instance $PrimaryServer" -Target $PrimaryServer -Continue
    }

    # Check if the database is present on the primary sql server
    if ($ServerSecondary.Databases.Name -notcontains $SecondaryDatabase) {
        Stop-Function -Message "Database $SecondaryDatabase is not available on instance $ServerSecondary" -Target $SqlInstance -Continue
    }

    # Check the restore mode
    if ($RestoreMode -notin 0, 1) {
        $RestoreMode = switch ($RestoreMode) { "NoRecovery" { 0}  "Standby" { 1 } }
        Write-Message -Message "Setting restore mode to $RestoreMode." -Level Verbose
    }

    # Check the if Threshold alert needs to be enabled
    if ($ThresholdAlertEnabled) {
        [int]$ThresholdAlertEnabled = 1
        Write-Message -Message "Setting Threshold alert to $ThresholdAlertEnabled." -Level Verbose
    }
    else {
        [int]$ThresholdAlertEnabled = 0
        Write-Message -Message "Setting Threshold alert to $ThresholdAlertEnabled." -Level Verbose
    }

    # Checking the option to disconnect users
    if ($DisconnectUsers) {
        [int]$DisconnectUsers = 1
        Write-Message -Message "Setting disconnect users to $DisconnectUsers." -Level Verbose
    }
    else {
        [int]$DisconnectUsers = 0
        Write-Message -Message "Setting disconnect users to $DisconnectUsers." -Level Verbose
    }

    # Check hte combination of the restore mode with the option to disconnect users
    if ($RestoreMode -eq 0 -and $DisconnectUsers -ne 0) {
        if ($Force) {
            [int]$DisconnectUsers = 0
            Write-Message -Message "Illegal combination of database restore mode $RestoreMode and disconnect users $DisconnectUsers. Setting it to $DisconnectUsers." -Level Warning
        }
        else {
            Stop-Function -Message "Illegal combination of database restore mode $RestoreMode and disconnect users $DisconnectUsers." -Target $SqlInstance -Continue
        }
    }

    # Set up the query
    $Query = "EXEC master.sys.sp_add_log_shipping_secondary_database
        @secondary_database = '$SecondaryDatabase'
        ,@primary_server = '$PrimaryServer'
        ,@primary_database = '$PrimaryDatabase'
        ,@restore_delay = $RestoreDelay
        ,@restore_all = $RestoreAll
        ,@restore_mode = $RestoreMode
        ,@disconnect_users = $DisconnectUsers
        ,@restore_threshold = $RestoreThreshold
        ,@threshold_alert = $ThresholdAlert
        ,@threshold_alert_enabled = $ThresholdAlertEnabled
        ,@history_retention_period = $HistoryRetention "

    # Addinf extra options to the query when needed
    if ($BlockSize -ne -1) {
        $Query += ",@block_size = $BlockSize"
    }

    if ($BufferCount -ne -1) {
        $Query += ",@buffer_count = $BufferCount"
    }

    if ($MaxTransferSize -ge 1) {
        $Query += ",@max_transfer_size = $MaxTransferSize"
    }

    if ($ServerSecondary.Version.Major -gt 9) {
        $Query += ",@overwrite = 1;"
    }
    else {
        $Query += ";"
    }

    # Execute the query to add the log shipping primary
    if ($PSCmdlet.ShouldProcess($SqlServer, ("Configuring logshipping for secondary database $SecondaryDatabase on $SqlInstance"))) {
        try {
            Write-Message -Message "Configuring logshipping for secondary database $SecondaryDatabase on $SqlInstance." -Level Output
            Write-Message -Message "Executing query:`n$Query" -Level Verbose
            $ServerSecondary.Query($Query)
        }
        catch {
            Write-Message -Message "$($_.Exception.InnerException.InnerException.InnerException.InnerException.Message)" -Level Warning
            Stop-Function -Message "Error executing the query.`n$($_.Exception.Message)`n$Query"  -ErrorRecord $_ -Target $SqlInstance -Continue
        }
    }

    Write-Message -Message "Finished adding the secondary database $SecondaryDatabase to log shipping." -Level Output

}
function New-DbaLogShippingSecondaryPrimary {
    <#
        .SYNOPSIS
            New-DbaLogShippingPrimarySecondary sets up the primary information for the primary database.

        .DESCRIPTION
            New-DbaLogShippingPrimarySecondary sets up the primary information, adds local and remote monitor links,
            and creates copy and restore jobs for the specified primary database.
            This is executed on the secondary server.

        .PARAMETER SqlInstance
            SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER BackupSourceDirectory
            The directory where transaction log backup files from the primary server are stored.

        .PARAMETER BackupDestinationDirectory
            The directory on the secondary server where backup files are copied to.

        .PARAMETER CopyJob
            The name to use for the SQL Server Agent job being created to copy transaction log backups to the secondary server.

        .PARAMETER CopyJobID
            The UID associated with the copy job on the secondary server.

        .PARAMETER FileRetentionPeriod
            The length of time, in minutes, that a backup file is retained on the secondary server in the path specified by the BackupDestinationDirectory parameter before being deleted.
            The default is 14420.

        .PARAMETER MonitorServer
            Is the name of the monitor server. The default is the secondary server.

        .PARAMETER MonitorServerLogin
            Is the username of the account used to access the monitor server.

        .PARAMETER MonitorServerPassword
            Is the password of the account used to access the monitor server.

        .PARAMETER MonitorServerSecurityMode
            The security mode used to connect to the monitor server. Allowed values are 0, "sqlserver", 1, "windows"
            The default is 1 or Windows.

        .PARAMETER PrimaryServer
            The name of the primary instance of the Microsoft SQL Server Database Engine in the log shipping configuration.

        .PARAMETER PrimaryDatabase
            Is the name of the database on the primary server.

        .PARAMETER RestoreJob
            Is the name of the SQL Server Agent job on the secondary server that restores the backups to the secondary database.

        .PARAMETER RestoreJobID
            The UID associated with the restore job on the secondary server.

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER Force
            The force parameter will ignore some errors in the parameters and assume defaults.
            It will also remove the any present schedules with the same name for the specific job.

        .NOTES
            Author: Sander Stad (@sqlstad, sqlstad.nl)
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/New-DbaLogShippingPrimarySecondary

        .EXAMPLE
            New-DbaLogShippingSecondaryPrimary -SqlInstance sql2 -BackupSourceDirectory "\\sql1\logshipping\DB1" -BackupDestinationDirectory D:\Data\logshippingdestination\DB1_DR -CopyJob LSCopy_sql2_DB1_DR -FileRetentionPeriod 4320 -MonitorServer sql2 -MonitorServerSecurityMode 'Windows' -PrimaryServer sql1 -PrimaryDatabase DB1 -RestoreJob LSRestore_sql2_DB1_DR
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param (
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [object]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$BackupSourceDirectory,
        [Parameter(Mandatory = $false)]
        [string]$BackupDestinationDirectory,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$CopyJob,
        [int]$FileRetentionPeriod = 14420,
        [string]$MonitorServer,
        [PSCredential]$MonitorCredential,
        [Parameter(Mandatory = $true)]
        [ValidateSet(0, "sqlserver", 1, "windows")]
        [object]$MonitorServerSecurityMode = 1,
        [object]$PrimaryServer,
        [PSCredential]$PrimarySqlCredential,
        [object]$PrimaryDatabase,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$RestoreJob,
        [Alias('Silent')]
        [switch]$EnableException,
        [switch]$Force
    )

    # Try connecting to the instance
    Write-Message -Message "Connecting to $SqlInstance" -Level Verbose
    try {
        $ServerSecondary = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
    }
    catch {
        Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $SqlInstance -Continue
    }

    # Try connecting to the instance
    Write-Message -Message "Connecting to $PrimaryServer" -Level Verbose
    try {
        $ServerPrimary = Connect-SqlInstance -SqlInstance $PrimaryServer -SqlCredential $PrimarySqlCredential
    }
    catch {
        Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $PrimaryServer -Continue
    }

    # Check if the backup UNC path is correct and reachable
    if ([bool]([uri]$BackupDestinationDirectory).IsUnc -and $BackupDestinationDirectory -notmatch '^\\(?:\\[^<>:`"/\\|?*]+)+$') {
        Stop-Function -Message "The backup destination path should be formatted in the form \\server\share." -Target $SqlInstance
        return
    }
    else {
        if (-not ((Test-Path $BackupDestinationDirectory -PathType Container -IsValid) -and ((Get-Item $BackupDestinationDirectory).PSProvider.Name -eq 'FileSystem'))) {
            Stop-Function -Message "The backup destination path is not valid or can't be reached." -Target $SqlInstance
            return
        }
    }

    # Check the MonitorServer
    if ($Force -and -not $MonitorServer) {
        $MonitorServer = $SqlInstance
        Write-Message -Message "Setting monitor server to $MonitorServer." -Level Verbose
    }

    # Check of the MonitorServerSecurityMode value is of type string and set the integer value
    if ($MonitorServerSecurityMode -notin 0, 1) {
        $MonitorServerSecurityMode = switch ($MonitorServerSecurityMode) {"WINDOWS" { 1 } "SQLSERVER" { 0 } }
        Write-Message -Message "Setting monitor server security mode to $MonitorServerSecurityMode." -Level Verbose
    }

    # Check the MonitorServerSecurityMode if it's SQL Server authentication
    if ($MonitorServerSecurityMode -eq 0 -and -not $MonitorCredential) {
        Stop-Function -Message "The MonitorServerCredential cannot be empty when using SQL Server authentication." -Target $SqlInstance -Continue
        return
    }
    elseif ($MonitorServerSecurityMode -eq 0 -and $MonitorCredential) {
        # Get the username and password from the credential
        $MonitorLogin = $MonitorCredential.UserName
        $MonitorPassword = $MonitorCredential.GetNetworkCredential().Password

        # Check if the user is in the database
        if ($ServerSecondary.Databases['master'].Users.Name -notcontains $MonitorLogin) {
            Stop-Function -Message "User $MonitorLogin for monitor login must be in the master database." -Target $SqlInstance -Continue
            return
        }
    }

    # Check if the database is present on the primary sql server
    if ($ServerPrimary.Databases.Name -notcontains $PrimaryDatabase) {
        Stop-Function -Message "Database $PrimaryDatabase is not available on instance $PrimaryServer" -Target $PrimaryServer -Continue
        return
    }

    # Set up the query
    $Query = "
        DECLARE @LS_Secondary__CopyJobId AS uniqueidentifier
        DECLARE @LS_Secondary__RestoreJobId	AS uniqueidentifier
        DECLARE @LS_Secondary__SecondaryId AS uniqueidentifier
        EXEC master.sys.sp_add_log_shipping_secondary_primary
                @primary_server = N'$PrimaryServer'
                ,@primary_database = N'$PrimaryDatabase'
                ,@backup_source_directory = N'$BackupSourceDirectory'
                ,@backup_destination_directory = N'$BackupDestinationDirectory'
                ,@copy_job_name = N'$CopyJob'
                ,@restore_job_name = N'$RestoreJob'
                ,@file_retention_period = $FileRetentionPeriod
                ,@copy_job_id = @LS_Secondary__CopyJobId OUTPUT
                ,@restore_job_id = @LS_Secondary__RestoreJobId OUTPUT
                ,@secondary_id = @LS_Secondary__SecondaryId OUTPUT "

    if ($MonitorServer) {
        $Query += ",@monitor_server = N'$MonitorServer'
                ,@monitor_server_security_mode = $($MonitorServerSecurityMode) "
    }


    # Check the MonitorServerSecurityMode if it's SQL Server authentication
    if ($MonitorServerSecurityMode -eq 0 -and $MonitorServer) {
        $Query += ",@monitor_server_login = N'$MonitorLogin'
            ,@monitor_server_password = N'$MonitorPassword' "
    }

    if ($ServerSecondary.Version.Major -gt 9) {
        $Query += ",@overwrite = 1;"
    }
    else {
        $Query += ";"
    }

    # Execute the query to add the log shipping primary
    if ($PSCmdlet.ShouldProcess($SqlServer, ("Configuring logshipping making settings for the primary database to secondary database on $SqlInstance"))) {
        try {
            Write-Message -Message "Configuring logshipping making settings for the primary database." -Level Output
            Write-Message -Message "Executing query:`n$Query" -Level Verbose
            $ServerSecondary.Query($Query)
        }
        catch {
            Write-Message -Message "$($_.Exception.InnerException.InnerException.InnerException.InnerException.Message)" -Level Warning
            Stop-Function -Message "Error executing the query.`n$($_.Exception.Message)"  -ErrorRecord $_ -Target $SqlInstance -Continue
        }
    }

    Write-Message -Message "Finished configuring of secondary database to primary database $PrimaryDatabase." -Level Output
}
function New-DbaMessageLevelModifier {
<#
    .SYNOPSIS
        Allows modifying message levels by powerful filters.
    
    .DESCRIPTION
        Allows modifying message levels by powerful filters.
        
        This is designed to allow a developer to have more control over what is written how during the development process.
        It also allows a debug user to fine tune what he is shown.
        
        This functionality is NOT designed for default implementation within a module.
        Instead, set healthy message levels for your own messages and leave others to tend to their own levels.
        
        Note:
        Adding too many level modifiers may impact performance, use with discretion.
    
    .PARAMETER Name
        The name of the level modifier.
        Can be arbitrary, but must be unique. Not case sensitive.
    
    .PARAMETER Modifier
        The level modifier to apply.
        - Use a negative value to make a message more relevant
        - Use a positive value to make a message less relevant
        While not limited to this range, the original levels range from 1 through 9:
        - 1-3 : Written to host and debug by default
        - 4-6 : Written to verbose and debug by default
        - 7-9 : Internas, written only to debug
    
    .PARAMETER IncludeFunctionName
        Only messages from functions with one of these exact names will be considered.
    
    .PARAMETER ExcludeFunctionName
        Messages from functions with one of these exact names will be ignored.
    
    .PARAMETER IncludeModuleName
        Only messages from modules with one of these exact names will be considered.
    
    .PARAMETER ExcludeModuleName
        Messages from module with one of these exact names will be ignored.
    
    .PARAMETER IncludeTags
        Only messages that contain one of these tags will be considered.
    
    .PARAMETER ExcludeTags
        Messages that contain one of these tags will be ignored.
    
    .PARAMETER EnableException
        This parameters disables user-friendly warnings and enables the throwing of exceptions.
        This is less user friendly, but allows catching exceptions in calling scripts.
    
    .EXAMPLE
        PS C:\> New-DbaMessageLevelModifier -Name 'MyModule-Include' -Modifier -9 -IncludeModuleName MyModule
        PS C:\> New-DbaMessageLevelModifier -Name 'MyModule-Exclude' -Modifier 9 -ExcludeModuleName MyModule
        
        These settings will cause all messages from the module 'MyModule' to be highly prioritized and almost certainly written to host.
        It will also make it highly unlikely, that messages from other modules will even be considered for anything but the lowest level.
        
        This is useful when prioritizing your own module during development.
#>
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]
        $Name,
        
        [Parameter(Mandatory = $true)]
        [int]
        $Modifier,
        
        [string]
        $IncludeFunctionName,
        
        [string]
        $ExcludeFunctionName,
        
        [string]
        $IncludeModuleName,
        
        [string]
        $ExcludeModuleName,
        
        [string[]]
        $IncludeTags,
        
        [string[]]
        $ExcludeTags,
        
        [switch]
        $EnableException
    )
    
    if (Test-Bound -ParameterName IncludeFunctionName, ExcludeFunctionName, IncludeModuleName, ExcludeModuleName, IncludeTags, ExcludeTags -Not) {
        Stop-Function -Message "Must specify at least one condition in order to apply message level modifier!" -EnableException $EnableException -Category InvalidArgument
        return
    }
    
    $levelModifier = New-Object Sqlcollaborative.Dbatools.Message.MessageLevelModifier
    $levelModifier.Name = $Name.ToLower()
    $levelModifier.Modifier = $Modifier
    
    if (Test-Bound -ParameterName IncludeFunctionName) {
        $levelModifier.IncludeFunctionName = $IncludeFunctionName
    }
    
    if (Test-Bound -ParameterName ExcludeFunctionName) {
        $levelModifier.ExcludeFunctionName = $ExcludeFunctionName
    }
    
    if (Test-Bound -ParameterName IncludeModuleName) {
        $levelModifier.IncludeModuleName = $IncludeModuleName
    }
    
    if (Test-Bound -ParameterName ExcludeModuleName) {
        $levelModifier.ExcludeModuleName = $ExcludeModuleName
    }
    
    if (Test-Bound -ParameterName IncludeTags) {
        $levelModifier.IncludeTags = $IncludeTags
    }
    
    if (Test-Bound -ParameterName ExcludeTags) {
        $levelModifier.ExcludeTags = $ExcludeTags
    }
    
    [Sqlcollaborative.Dbatools.Message.MessageHost]::MessageLevelModifiers[$levelModifier.Name] = $levelModifier
    
    $levelModifier
}
function global:New-DbaTeppCompletionResult {
    <#
        .SYNOPSIS
            Generates a completion result for dbatools internal tab completion.

        .DESCRIPTION
            Generates a completion result for dbatools internal tab completion.

        .PARAMETER CompletionText
            The text to propose.

        .PARAMETER ToolTip
            The tooltip to show in tooltip-aware hosts (ISE, mostly)

        .PARAMETER ListItemText
            ???

        .PARAMETER CompletionResultType
            The type of object that is being completed.
            By default it generates one of type paramter value.

        .PARAMETER NoQuotes
            Whether to put the result in quotes or not.

        .EXAMPLE
            New-DbaTeppCompletionResult -CompletionText 'master' -ToolTip 'master'

            Returns a CompletionResult with the text and tooltip 'master'
    #>
    param (
        [Parameter(Position = 0, ValueFromPipelineByPropertyName = $true, Mandatory = $true, ValueFromPipeline = $true)]
        [ValidateNotNullOrEmpty()]
        [string]
        $CompletionText,

        [Parameter(Position = 1, ValueFromPipelineByPropertyName = $true)]
        [string]
        $ToolTip,

        [Parameter(Position = 2, ValueFromPipelineByPropertyName = $true)]
        [string]
        $ListItemText,

        [System.Management.Automation.CompletionResultType]
        $CompletionResultType = [System.Management.Automation.CompletionResultType]::ParameterValue,

        [Parameter(Mandatory = $false)]
        [switch]
        $NoQuotes = $false
    )

    process {
        $toolTipToUse = if ($ToolTip -eq '') { $CompletionText }
        else { $ToolTip }
        $listItemToUse = if ($ListItemText -eq '') { $CompletionText }
        else { $ListItemText }

        # If the caller explicitly requests that quotes
        # not be included, via the -NoQuotes parameter,
        # then skip adding quotes.

        if ($CompletionResultType -eq [System.Management.Automation.CompletionResultType]::ParameterValue -and -not $NoQuotes) {
            # Add single quotes for the caller in case they are needed.
            # We use the parser to robustly determine how it will treat
            # the argument.  If we end up with too many tokens, or if
            # the parser found something expandable in the results, we
            # know quotes are needed.

            $tokens = $null
            $null = [System.Management.Automation.Language.Parser]::ParseInput("echo $CompletionText", [ref]$tokens, [ref]$null)
            if ($tokens.Length -ne 3 -or ($tokens[1] -is [System.Management.Automation.Language.StringExpandableToken] -and $tokens[1].Kind -eq [System.Management.Automation.Language.TokenKind]::Generic)) {
                $CompletionText = "'$CompletionText'"
            }
        }
        return New-Object System.Management.Automation.CompletionResult($CompletionText, $listItemToUse, $CompletionResultType, $toolTipToUse.Trim())
    }
}

(Get-Item Function:\New-DbaTeppCompletionResult).Visibility = "Private"
function Register-DbaConfigValidation {
    <#
        .SYNOPSIS
            Registers a validation scriptblock for use with the configuration system.

        .DESCRIPTION
            Registers a validation scriptblock for use with the configuration system.

            The scriptblock must be designed according to a few guidelines:
            - It must not throw exceptions
            - It must accept a single parameter (the value to be tested)
            - It must return an object with three properties: 'Message', 'Value' and 'Success'.
            The Success property should be boolean and indicate whether the value is valid.
            The Value property contains the validated input. The scriptblock may legally convert the input (For example from string to int in case of integer validation)
            The message contains a string that will be passed along to an exception in case the input is NOT valid.

        .PARAMETER Name
            The name under which to register the validation scriptblock

        .PARAMETER ScriptBlock
            The scriptblock to register

        .EXAMPLE
            PS C:\> Register-DbaConfigValidation -Name IntPositive -ScriptBlock $scriptblock

            Registers the scriptblock stored in $scriptblock as validation with the name IntPositive
    #>
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $true)]
        [string]
        $Name,

        [Parameter(Mandatory = $true)]
        [ScriptBlock]
        $ScriptBlock
    )

    [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Validation[$Name.ToLower()] = $ScriptBlock
}
function Register-DbaMaintenanceTask {
    <#
        .SYNOPSIS
            Allows scheduling maintenance tasks, that are perfomed in the background.

        .DESCRIPTION
            Allows scheduling maintenance tasks, that are perfomed in the background.

            All scriptblocks scheduled like this will be performed on a separate runspace and have access to all internal dbatools commands.
            None of the scriptblocks will affect the main session (so you cannot manipulate variables, etc.)

        .PARAMETER Name
            The name of the task.
            Must be unique, otherwise it will update the existing task.

        .PARAMETER ScriptBlock
            The task/scriptblock that should be performed as part of the maintenance.
            It will have all of dbatools including internal commands available.

        .PARAMETER Once
            Whether the interval should be performed only once.

        .PARAMETER Interval
            The interval at which the task should be repeated.

        .PARAMETER Delay
            How far after the initial registration should the maintenance script wait before processing this.
            This can be used to delay background stuff that should not content with items that would be good to have as part of the module import.
            Some library specific items can be moved to maintenance if their processing would take too much time on original import, even if it is desirable to have them available as soon as possible.

        .PARAMETER Priority
            How important is this task?
            If multiple tasks are due at the same maintenance cycle, the more critical one will be processed first.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .EXAMPLE
            PS C:\> Register-DbaMaintenanceTask -Name 'value1' -ScriptBlock $ScriptBlock -Once
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]
        $Name,

        [Parameter(Mandatory = $true)]
        [System.Management.Automation.ScriptBlock]
        $ScriptBlock,

        [Parameter(Mandatory = $true, ParameterSetName = "Once")]
        [switch]
        $Once,

        [Parameter(Mandatory = $true, ParameterSetName = "Repeating")]
        [System.TimeSpan]
        $Interval,

        [System.TimeSpan]
        $Delay,

        [Sqlcollaborative.Dbatools.Maintenance.MaintenancePriority]
        $Priority = "Medium",

        [switch]
        [Alias('Silent')]$EnableException
    )

    #region Case: Task already registered
    if ([Sqlcollaborative.Dbatools.Maintenance.MaintenanceHost]::Tasks.ContainsKey($Name.ToLower())) {
        $task = [Sqlcollaborative.Dbatools.Maintenance.MaintenanceHost]::Tasks[$Name.ToLower()]
        if ($task.ScriptBlock -ne $ScriptBlock) { $task.ScriptBlock = $ScriptBlock }
        if (Test-Bound -ParameterName Once) { $task.Once = $Once }
        if (Test-Bound -ParameterName Interval) {
            $task.Once = $false
            $task.Interval = $Interval
        }
        if (Test-Bound -ParameterName Delay) { $task.Delay = $Delay }
        if (Test-Bound -ParameterName Priority) { $task.Priority = $Priority }
    }
    #endregion Case: Task already registered

    #region New Task
    else {
        $task = New-Object Sqlcollaborative.Dbatools.Maintenance.MaintenanceTask
        $task.Name = $Name.ToLower()
        $task.ScriptBlock = $ScriptBlock
        if (Test-Bound -ParameterName Once) { $task.Once = $true }
        if (Test-Bound -ParameterName Interval) {
            if ($Interval.Ticks -le 0) {
                Stop-Function -Message "Failed to register task: $Name - Interval cannot be 0 or less" -Category InvalidArgument
                return
            }
            else { $task.Interval = $Interval }
        }
        if (Test-Bound -ParameterName Delay) { $task.Delay = $Delay }
        $task.Priority = $Priority
        $task.Registered = Get-Date
        [Sqlcollaborative.Dbatools.Maintenance.MaintenanceHost]::Tasks[$Name.ToLower()] = $task
    }
    #endregion New Task
}
function Register-DbaMessageEvent {
<#
    .SYNOPSIS
        Registers an event to when a message is written.
    
    .DESCRIPTION
        Registers an event to when a message is written.
        These events will fire whenever the written message fulfills the specified filter criteria.
        
        This allows integrating direct alerts and reactions to messages as they occur.
        
        Warnings:
        - Adding many subscriptions can impact overall performance, even without triggering.
        - Events are executed synchronously. executing complex operations may introduce a significant delay to the command execution.
        
        It is recommended to push processing that involves outside resources to a separate runspace, then use the event to pass the object as trigger.
        The TaskEngine component may prove to be just what is needed to accomplish this.
    
    .PARAMETER Name
        The name of the subscription.
        Each subscription must have a name, subscriptions of equal name will overwrite each other.
        This is in order to avoid having runspace uses explode the number of subscriptions on each invocation.
    
    .PARAMETER ScriptBlock
        The scriptblock to execute.
        It will receive the message entry (as returned by Get-DbatoolsLog) as its sole argument.
    
    .PARAMETER MessageFilter
        Filter by message content. Understands wildcards, but not regex.
    
    .PARAMETER ModuleNameFilter
        Filter by Name of the module, from which the message comes. Understands wildcards, but not regex.
    
    .PARAMETER FunctionNameFilter
        Filter by Name of the function, from which the message comes. Understands wildcards, but not regex.
    
    .PARAMETER TargetFilter
        Filter by target object. Performs equality comparison on an object level.
    
    .PARAMETER LevelFilter
        Include only messages of the specified levels.
    
    .PARAMETER TagFilter
        Only include messages with any of the specified tags.
    
    .PARAMETER RunspaceFilter
        Only include messages which were written by the specified runspace.
        You can find out the current runspace ID by running this:
        [System.Management.Automation.Runspaces.Runspace]::DefaultRunspace.InstanceId
        You can retrieve the primary runspace - the Guid used by the runspace the user sees - by running this:
        [Sqlcollaborative.Dbatools.Utility.UtilityHost]::PrimaryRunspace
    
    .EXAMPLE
        PS C:\> Register-DbaMessageEvent -Name 'Mymodule.OffloadTrigger' -ScriptBlock $ScriptBlock -Tag 'engine' -Module 'MyModule' -Level Warning
        
        Registers an event subscription ...
        - Under the name 'Mymodule.OffloadTrigger' ...
        - To execute $ScriptBlock ...
        - Whenever a message is written with the tag 'engine' by the module 'MyModule' at the level 'Warning'
#>
    [CmdletBinding(PositionalBinding = $false)]
    param (
        [Parameter(Mandatory = $true)]
        [string]
        $Name,
        
        [Parameter(Mandatory = $true)]
        [System.Management.Automation.ScriptBlock]
        $ScriptBlock,
        
        [string]
        $MessageFilter,
        
        [string]
        $ModuleNameFilter,
        
        [string]
        $FunctionNameFilter,
        
        $TargetFilter,
        
        [Sqlcollaborative.Dbatools.Message.MessageLevel[]]
        $LevelFilter,
        
        [string[]]
        $TagFilter,
        
        [System.Guid]
        $RunspaceFilter
    )
    
    $newName = $Name.ToLower()
    $eventSubscription = New-Object Sqlcollaborative.Dbatools.Message.MessageEventSubscription
    $eventSubscription.Name = $newName
    $eventSubscription.ScriptBlock = $ScriptBlock
    
    if (Test-Bound -ParameterName MessageFilter) {
        $eventSubscription.MessageFilter = $MessageFilter
    }
    
    if (Test-Bound -ParameterName ModuleNameFilter) {
        $eventSubscription.ModuleNameFilter = $ModuleNameFilter
    }
    
    if (Test-Bound -ParameterName FunctionNameFilter) {
        $eventSubscription.FunctionNameFilter = $FunctionNameFilter
    }
    
    if (Test-Bound -ParameterName TargetFilter) {
        $eventSubscription.TargetFilter = $TargetFilter
    }
    
    if (Test-Bound -ParameterName LevelFilter) {
        $eventSubscription.LevelFilter = $LevelFilter
    }
    
    if (Test-Bound -ParameterName TagFilter) {
        $eventSubscription.TagFilter = $TagFilter
    }
    
    if (Test-Bound -ParameterName RunspaceFilter) {
        $eventSubscription.RunspaceFilter = $RunspaceFilter
    }
    
    [Sqlcollaborative.Dbatools.Message.MessageHost]::Events[$newName] = $eventSubscription
}
function Register-DbaMessageTransform {
<#
    .SYNOPSIS
        Registers a scriptblock that can transform message content.
    
    .DESCRIPTION
        Registers a scriptblock that can transform message content.
        This can be used to convert some kinds of input. Specifically:
        
        Target:
        When specifying a target, this target may require some conversion.
        For example, an object containing a live connection may need to have a static copy stored instead,
        as otherwise its export on a different runspace may cause access violations.
        
        Exceptions:
        Some exceptions may need transforming.
        For example some APIs might wrap the actual exception into a common wrapper.
        In this scenario you may want the actual exception in order to provide more specific information.
        
        In all instances, the scriptblock will be called, receiving only the relevant object as its sole input.
        
        Note: This transformation is performed synchronously on the active runspace. Complex scriptblocks may delay execution times when a matching object is passed.
    
    .PARAMETER TargetType
        The full typename of the target object to apply the scriptblock to.
        All objects of that typename will be processed through that scriptblock.
    
    .PARAMETER ExceptionType
        The full typename of the exception object to apply the scriptblock to.
        All objects of that typename will be processed through that scriptblock.
        Note: In case of error records, the type of the Exception Property is inspected. The error record as a whole will not be touched, except for having its exception exchanged.
    
    .PARAMETER ScriptBlock
        The scriptblock that performs the transformation.
    
    .PARAMETER TargetTypeFilter
        A filter for the typename of the target object to transform.
        Supports wildcards, but not regex.
        WARNING: Adding too many filter-type transforms may impact overall performance, try to avoid using them!
    
    .PARAMETER ExceptionTypeFilter
        A filter for the typename of the exception object to transform.
        Supports wildcards, but not regex.
        WARNING: Adding too many filter-type transforms may impact overall performance, try to avoid using them!
    
    .PARAMETER FunctionNameFilter
        Default: "*"
        Allows filtering by function name, in order to consider whether the function is affected.
        Supports wildcards, but not regex.
        WARNING: Adding too many filter-type transforms may impact overall performance, try to avoid using them!
    
    .PARAMETER ModuleNameFilter
        Default: "*"
        Allows filtering by module name, in order to consider whether the function is affected.
        Supports wildcards, but not regex.
        WARNING: Adding too many filter-type transforms may impact overall performance, try to avoid using them!
    
    .EXAMPLE
        PS C:\> Register-DbaMessageTransform -TargetType 'mymodule.category.classname' -ScriptBlock $ScriptBlock
        
        Whenever a target object of type 'mymodule.category.classname' is specified, invoke $ScriptBlock (with the object as sole argument) and store the result as target instead.
    
    .EXAMPLE
        PS C:\> Register-DbaMessageTransform -ExceptionType 'mymodule.category.exceptionname' -ScriptBlock $ScriptBlock
        
        Whenever an exception or error record of type 'mymodule.category.classname' is specified, invoke $ScriptBlock (with the object as sole argument) and store the result as exception instead.
        If the full error record is specified, only the updated exception will be inserted
    
    .EXAMPLE
        PS C:\> Register-DbaMessageTransform -TargetTypeFilter 'mymodule.category.*' -ScriptBlock $ScriptBlock
        
        Adds a transform for all target objects that are of a type whose full name starts with 'mymodule.category.'
        All target objects matching that typename will be run through the specified scriptblock, which in return generates the new target object.
#>
    [CmdletBinding(PositionalBinding = $false)]
    param (
        [Parameter(Mandatory = $true, ParameterSetName = "Target")]
        [string]
        $TargetType,
        
        [Parameter(Mandatory = $true, ParameterSetName = "Exception")]
        [string]
        $ExceptionType,
        
        [Parameter(Mandatory = $true)]
        [ScriptBlock]
        $ScriptBlock,
        
        [Parameter(Mandatory = $true, ParameterSetName = "TargetFilter")]
        [string]
        $TargetTypeFilter,
        
        [Parameter(Mandatory = $true, ParameterSetName = "ExceptionFilter")]
        [string]
        $ExceptionTypeFilter,
        
        [Parameter(ParameterSetName = "TargetFilter")]
        [Parameter(ParameterSetName = "ExceptionFilter")]
        $FunctionNameFilter = "*",
        
        [Parameter(ParameterSetName = "TargetFilter")]
        [Parameter(ParameterSetName = "ExceptionFilter")]
        $ModuleNameFilter = "*"
    )
    
    process {
        if ($TargetType) { [Sqlcollaborative.Dbatools.Message.MessageHost]::TargetTransforms[$TargetType.ToLower()] = $ScriptBlock }
        if ($ExceptionType) { [Sqlcollaborative.Dbatools.Message.MessageHost]::ExceptionTransforms[$ExceptionType.ToLower()] = $ScriptBlock }
        
        if ($TargetTypeFilter) {
            $condition = New-Object Sqlcollaborative.Dbatools.Message.TransformCondition($TargetTypeFilter, $ModuleNameFilter, $FunctionNameFilter, $ScriptBlock, "Target")
            [Sqlcollaborative.Dbatools.Message.MessageHost]::TargetTransformList.Add($condition)
        }
        
        if ($ExceptionTypeFilter) {
            $condition = New-Object Sqlcollaborative.Dbatools.Message.TransformCondition($ExceptionTypeFilter, $ModuleNameFilter, $FunctionNameFilter, $ScriptBlock, "Exception")
            [Sqlcollaborative.Dbatools.Message.MessageHost]::ExceptionTransformList.Add($condition)
        }
    }
}
function Register-DbaRunspace {
    <#
    .SYNOPSIS
        Registers a scriptblock to run in the background.

    .DESCRIPTION
        This function registers a scriptblock to run in separate runspace.
        This is different from most runspace solutions, in that it is designed for permanent background tasks that need to be done.
        It guarantees a single copy of the task to run within the powershell process, even when running the same module in many runspaces in parallel.

        Updating:
        If this function is called multiple times, targeting the same name, it will update the scriptblock.
        - If that scriptblock is the same as the previous scriptblock, nothing changes
        - If that scriptblock is different from the previous ones, it will be registered, but will not be executed right away!
          Only after stopping and starting the runspace will it operate under the new scriptblock.

    .PARAMETER ScriptBlock
        The scriptblock to run in a dedicated runspace

    .PARAMETER Name
        The name to register the scriptblock under.

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .EXAMPLE
        PS C:\> Register-DbaRunspace -ScriptBlock $scriptBlock -Name 'mymodule.maintenance'

        Registers the script defined in $scriptBlock under the name 'mymodule.maintenance'
        It does not start the runspace yet. If it already exists, it will overwrite the scriptblock without affecting the running script.

    .EXAMPLE
        PS C:\> Register-DbaRunspace -ScriptBlock $scriptBlock -Name 'mymodule.maintenance'
        PS C:\> Start-DbaRunspace -Name 'mymodule.maintenance'

        Registers the script defined in $scriptBlock under the name 'mymodule.maintenance'
        Then it starts the runspace, running the registered $scriptBlock
#>
    [CmdletBinding(PositionalBinding = $false)]
    param
    (
        [Parameter(Mandatory = $true)]
        [Scriptblock]
        $ScriptBlock,

        [Parameter(Mandatory = $true)]
        [String]
        $Name,

        [switch]
        [Alias('Silent')]
        $EnableException
    )

    if ([Sqlcollaborative.Dbatools.Runspace.RunspaceHost]::Runspaces.ContainsKey($Name.ToLower())) {
        Write-Message -Level Verbose -Message "Updating runspace: $($Name.ToLower())" -Target $Name.ToLower()
        [Sqlcollaborative.Dbatools.Runspace.RunspaceHost]::Runspaces[$Name.ToLower()].SetScript($ScriptBlock)
    }
    else {
        Write-Message -Level Verbose -Message "Registering runspace: $($Name.ToLower())" -Target $Name.ToLower()
        [Sqlcollaborative.Dbatools.Runspace.RunspaceHost]::Runspaces[$Name.ToLower()] = New-Object Sqlcollaborative.Dbatools.Runspace.RunspaceContainer($Name.ToLower(), $ScriptBlock)
    }
}
function Register-DbaTeppArgumentCompleter {
    <#
        .SYNOPSIS
            Registers a parameter for a prestored Tepp.

        .DESCRIPTION
            Registers a parameter for a prestored Tepp.
            This function allows easily registering a function's parameter for Tepp in the function-file, rather than in a centralized location.

        .PARAMETER Command
            Name of the command whose parameter should receive Tepp.
            Supports multiple commands at the same time in order to optimize performance.

        .PARAMETER Parameter
            Name of the parameter that should be Tepp'ed.

        .PARAMETER Name
            Name of the Tepp Completioner to use.
            Defaults to the parameter name.
            Best practice requires a Completioner to be named the same as the completed parameter, in which case this parameter needs not be specified.
            However sometimes that may not be universally possible, which is when this parameter comes in.

        .PARAMETER All
            Whether this TEPP applies to all commands in dbatools that have the specified parameter.

        .EXAMPLE
            Register-DbaTeppArgumentCompleter -Command Get-DbaBackupHistory -Parameter Database

            Registers the "Database" parameter of the Get-DbaBackupHistory to receive Database-Tepp
    #>
    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingEmptyCatchBlock", "")]
    param (
        [string[]]$Command,
        [string[]]$Parameter,
        [string]$Name,
        [switch]$All
    )

    #region ScriptBlock
    $scriptBlock = {
        param (
            $commandName,
            $parameterName,
            $wordToComplete,
            $commandAst,
            $fakeBoundParameter
        )

        if ($teppScript = [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::GetTeppScript($commandName, $parameterName)) {
            $start = Get-Date
            $teppScript.LastExecution = $start
            $teppScript.LastDuration = New-Object System.TimeSpan(-1) # Null it, just in case. It's a new start.

            try { $ExecutionContext.InvokeCommand.InvokeScript($true, ([System.Management.Automation.ScriptBlock]::Create($teppScript.ScriptBlock.ToString())), $null, @($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)) }
            catch { }

            $teppScript.LastDuration = (Get-Date) - $start
        }
    }
    #endregion ScriptBlock

    foreach ($p in $Parameter) {
        $lowername = $PSBoundParameters.Name

        if ($null -eq $lowername) {
            $lowername = $p.ToLower()
        }
        else {
            $lowername = $lowername.ToLower()
        }

        if ($All) { [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::AddTabCompletionSet("*", $p, $lowername) }
        else {
            foreach ($c in $Command) {
                [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::AddTabCompletionSet($c, $p, $lowername)
            }
        }

        if ($script:TEPP) {
            TabExpansionPlusPlus\Register-ArgumentCompleter -CommandName $Command -ParameterName $p -ScriptBlock $scriptBlock
        }
        else {
            Register-ArgumentCompleter -CommandName $Command -ParameterName $p -ScriptBlock $scriptBlock
        }
    }
}
function Register-DbaTeppInstanceCacheBuilder {
    <#
        .SYNOPSIS
            Registers a scriptblock used to build the TEPP cache from an instance connection.

        .DESCRIPTION
            Registers a scriptblock used to build the TEPP cache from an instance connection.
            Used only on import of the module.

        .PARAMETER ScriptBlock
            The ScriptBlock used to build the cache.

            The ScriptBlock may assume the following two variables to exist:
            - $FullSmoName (A string containing the full SMO name as presented by the DbaInstanceParameter class-interpreted input)
            - $server (An SMO connection object)

        .PARAMETER Slow
            This switch implies a gathering process that takes too much time to be performed synchronously.
            Basically, when retrieving the information takes more than 25ms on an average server (on top of establishing the original connection), this switch should be set.

        .EXAMPLE
            Register-DbaTeppInstanceCacheBuilder -ScriptBlock $ScriptBlock

            Registers the scriptblock stored in the aptly named variable $ScriptBlock as a fest cache building scriptblock.
            Note: The scriptblock must execute swiftly! (less than 25ms)

        .EXAMPLE
            Register-DbaTeppInstanceCacheBuilder -ScriptBlock $ScriptBlock -Slow

            Registers the scriptblock stored in the aptly named variable $ScriptBlock as a slow cache building scriptblock.
            This is suitable for cache building scriptblocks that take a while to execute.

        .NOTES
            Additional information about the function.
    #>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [System.Management.Automation.ScriptBlock]
        $ScriptBlock,

        [switch]
        $Slow
    )

    if ($Slow -and ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::TeppGatherScriptsSlow -notcontains $ScriptBlock)) {
        [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::TeppGatherScriptsSlow.Add($ScriptBlock)
    }
    elseif ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::TeppGatherScriptsFast -notcontains $ScriptBlock) {
        [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::TeppGatherScriptsFast.Add($ScriptBlock)
    }
}
function Register-DbaTeppScriptblock {
    <#
        .SYNOPSIS
            Registers a scriptblock under name, to later be available for TabExpansion.

        .DESCRIPTION
            Registers a scriptblock under name, to later be available for TabExpansion.

        .PARAMETER ScriptBlock
            The scriptblock to register.

        .PARAMETER Name
            The name under which the scriptblock should be registered.

        .EXAMPLE
            Register-DbaTeppScriptblock -ScriptBlock $scriptBlock -Name MyFirstTeppScriptBlock

            Stores the scriptblock stored in $scriptBlock under the name "MyFirstTeppScriptBlock"
    #>
    [CmdletBinding()]
    param (
        [System.Management.Automation.ScriptBlock]
        $ScriptBlock,

        [string]
        $Name
    )

    $scp = New-Object Sqlcollaborative.Dbatools.TabExpansion.ScriptContainer
    $scp.Name = $Name.ToLower()
    $scp.ScriptBlock = $ScriptBlock
    $scp.LastDuration = New-TimeSpan -Seconds -1

    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Scripts[$Name.ToLower()] = $scp
}
function Remove-DbaMessageLevelModifier {
<#
    .SYNOPSIS
        Removes a message level modifier.
    
    .DESCRIPTION
        Removes a message level modifier.
        
        Message Level Modifiers can be created by using New-DbaMessageLevelModifier.
        They are used to emphasize or deemphasize messages, in order to help with debugging.
    
    .PARAMETER Name
        Name of the message level modifier to remove.
    
    .PARAMETER Modifier
        The actual modifier to remove, as returned by Get-DbaMessageLevelModifier.
    
    .PARAMETER EnableException
        This parameters disables user-friendly warnings and enables the throwing of exceptions.
        This is less user friendly, but allows catching exceptions in calling scripts.
    
    .EXAMPLE
        PS C:\> Get-DbaMessageLevelModifier | Remove-DbaMessageLevelModifier
        
        Removes all message level modifiers, restoring everything to their default levels.
    
    .EXAMPLE
        PS C:\> Remove-DbaMessageLevelModifier -Name "mymodule.foo"
        
        Removes the message level modifier named "mymodule.foo"
#>
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [string[]]
        $Name,
        
        [Parameter(ValueFromPipeline = $true)]
        [Sqlcollaborative.Dbatools.Message.MessageLevelModifier[]]
        $Modifier,
        
        [switch]
        $EnableException
    )
    
    process {
        foreach ($item in $Name) {
            if ($item -eq "Sqlcollaborative.Dbatools.Message.MessageLevelModifier") { continue }
            
            if ([Sqlcollaborative.Dbatools.Message.MessageHost]::MessageLevelModifiers.ContainsKey($item.ToLower())) {
                [Sqlcollaborative.Dbatools.Message.MessageHost]::MessageLevelModifiers.Remove($item.ToLower())
            }
            else {
                Stop-Function -Message "No message level modifier of name $item found!" -EnableException $EnableException -Category InvalidArgument -Continue
            }
        }
        foreach ($item in $Modifier) {
            if ([Sqlcollaborative.Dbatools.Message.MessageHost]::MessageLevelModifiers.ContainsKey($item.Name)) {
                [Sqlcollaborative.Dbatools.Message.MessageHost]::MessageLevelModifiers.Remove($item.Name)
            }
            else {
                Stop-Function -Message "No message level modifier of name $($item.Name) found!" -EnableException $EnableException -Category InvalidArgument -Continue
            }
        }
    }
}
Function Remove-InvalidFileNameChars {
  param(
    [Parameter(Mandatory=$true,
      Position=0,
      ValueFromPipeline=$true,
      ValueFromPipelineByPropertyName=$true)]
    [String]$Name
  )

  $invalidChars = [IO.Path]::GetInvalidFileNameChars() -join ''
  $re = "[{0}]" -f [RegEx]::Escape($invalidChars)
  return ($Name -replace $re)
}
function Resolve-IpAddress {
    # Uses the Beard's method to resolve IPs
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlInstance", "ComputerName", "SqlServer")]
        [object]$Server
    )

    if ($Server.GetType() -eq [Microsoft.SqlServer.Management.Smo.Server]) {
        return $ipaddress = ((Test-Connection $Server.ComputerName -Count 1 -ErrorAction SilentlyContinue).Ipv4Address).IPAddressToString
    }
    else {
        return $ipaddress = ((Test-Connection $server.Split('\')[0] -Count 1 -ErrorAction SilentlyContinue).Ipv4Address).IPAddressToString
    }
}
function Resolve-NetBiosName {
    <#
.SYNOPSIS
Internal function. Takes a best guess at the NetBIOS name of a server.
 #>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [object]$SqlInstance,
        [PSCredential]$SqlCredential
    )
    $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
    $server.ComputerName
}
function Resolve-SqlIpAddress {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [object]$SqlInstance,
        [PSCredential]$SqlCredential
    )

    $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
    $servernetbios = $server.ComputerNamePhysicalNetBIOS
    $ipaddr = (Test-Connection $servernetbios -count 1).Ipv4Address
    return $ipaddr
}
function Select-DefaultView {
    <#

    This command enables us to send full on objects to the pipeline without the user seeing it

    See it in action in Get-DbaDbSnapshot and Remove-DbaDbSnapshot

    a lot of this is from boe, thanks boe!
    https://learn-powershell.net/2013/08/03/quick-hits-set-the-default-property-display-in-powershell-on-custom-objects/

    TypeName creates a new type so that we can use ps1xml to modify the output
    #>

    [CmdletBinding()]
    param (
        [parameter(ValueFromPipeline = $true)]
        [object]$InputObject,
        [string[]]$Property,
        [string[]]$ExcludeProperty,
        [string]$TypeName
    )
    process {

        if ($null -eq $InputObject) { return }

        if ($TypeName) {
            $InputObject.PSObject.TypeNames.Insert(0, "dbatools.$TypeName")
        }

        if ($ExcludeProperty) {
            if ($InputObject.GetType().Name.ToString() -eq 'DataRow') {
                $ExcludeProperty += 'Item', 'RowError', 'RowState', 'Table', 'ItemArray', 'HasErrors'
            }
            
            $props = ($InputObject | Get-Member | Where-Object MemberType -in 'Property', 'NoteProperty', 'AliasProperty' | Where-Object { $_.Name -notin $ExcludeProperty }).Name
            $defaultset = New-Object System.Management.Automation.PSPropertySet('DefaultDisplayPropertySet', [string[]]$props)
        }
        else {
            # property needs to be string
            if ("$property" -like "* as *") {
                $newproperty = @()
                foreach ($p in $property) {
                    if ($p -like "* as *") {
                        $old, $new = $p -isplit " as "
                        # Do not be tempted to not pipe here
                        $inputobject | Add-Member -Force -MemberType AliasProperty -Name $new -Value $old -ErrorAction SilentlyContinue
                        $newproperty += $new
                    }
                    else {
                        $newproperty += $p
                    }
                }
                $property = $newproperty
            }
            $defaultset = New-Object System.Management.Automation.PSPropertySet('DefaultDisplayPropertySet', [string[]]$Property)
        }

        $standardmembers = [System.Management.Automation.PSMemberInfo[]]@($defaultset)

        # Do not be tempted to not pipe here
        $inputobject | Add-Member -Force -MemberType MemberSet -Name PSStandardMembers -Value $standardmembers -ErrorAction SilentlyContinue

        $inputobject
    }
}
function Set-ServiceStartMode {
    <#
        .SYNOPSIS
        Internal function. Implements the method that changes startup mode of the SQL Server service.

        .DESCRIPTION
        Accepts objects from Get-DbaSqlService and performs a corresponding action.

        .PARAMETER InputObject
        A collection of services from Get-DbaSqlService.

        .PARAMETER Mode
        Startup mode of the service: Automatic, Manual or Disabled.

        .PARAMETER WhatIf
        Shows what would happen if the cmdlet runs. The cmdlet is not run.

        .PARAMETER Confirm
        Prompts you for confirmation before running the cmdlet.

        .NOTES
        Author: Kirill Kravtsov ( @nvarscar )

        dbatools PowerShell module (https://dbatools.io)
        Copyright (C) 2017 Chrissy LeMaire
        License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
        Get-DbaSqlService -ComputerName sql1 | Set-ServiceStartMode -Mode 'Manual'

        Sets all SQL services on sql1 to Manual startup.

        .EXAMPLE
        $services = Get-DbaSqlService -ComputerName sql1
        Set-ServiceStartMode -InputObject $services -Mode 'Automatic'

        Sets all SQL services on sql1 to Automatic startup.

#>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [string]$Mode,
        [parameter(ValueFromPipeline = $true, Mandatory = $true)]
        [object[]]$InputObject
    )
    begin {
        $callStack = Get-PSCallStack
        if ($callStack.Length -gt 1) {
            $callerName = $callStack[1].Command
        }
        else {
            $callerName = $callStack[0].Command
        }
        $ProcessArray = @()
    }
    process {
        #Get all the objects from the pipeline before proceeding
        $ProcessArray += $InputObject
    }
    end {
        $ProcessArray = $ProcessArray | Where-Object { (!$InstanceName -or $_.InstanceName -in $InstanceName) -and (!$Type -or $_.type -in $Type) }
        foreach ($service in $ProcessArray) {
            #Get WMI object
            $Wmi = Get-WmiObject Win32_Service -ComputerName $service.ComputerName -filter "name='$($service.ServiceName)'"
            if ($Pscmdlet.ShouldProcess($Wmi, "Changing the Start Mode to $Mode")) {
                $x = $Wmi.ChangeStartMode($Mode)
                if ($x.ReturnValue -ne 0) {
                    Write-Message -Level Warning -FunctionName $callerName -Message ("The attempt to $action the service $($job.ServiceName) on $($job.ComputerName) returned the following message: " + (Get-DbaSQLServiceErrorMessage $x.ReturnValue))
                }
            }
        }
    }
}
function Show-Notification {
    param(
        $GalleryVersion,
        $Title = "dbatools update",
        $Text = "Version $GalleryVersion is now available"
    )
    # ensure the dbatools 'app' exists in registry so that it doesn't immediately disappear from Action Center
    $regPath = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Notifications\Settings'
    $appId = "{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\WindowsPowerShell\v1.0\powershell.exe"

    if (!(Test-Path -Path "$regPath\$appId")) {
        Write-Verbose "Adding required registry entry at $("$regPath\$appId")"
        $null = New-Item -Path "$regPath\$appId" -Force
        $null = New-ItemProperty -Path "$regPath\$appId" -Name 'ShowInActionCenter' -Value 1 -PropertyType 'DWORD' -Force
    }
    $null = [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime]
    $template = [Windows.UI.Notifications.ToastTemplateType]::ToastImageAndText02
    [xml]$toastTemplate = ([Windows.UI.Notifications.ToastNotificationManager]::GetTemplateContent($template).GetXml())

    [xml]$toastTemplate = "
    <toast launch=`"app-defined-string`">
        <visual>
            <binding template=`"ToastGeneric`">
                <text>`"$Title`"</text>
                <text>`"$Text`"</text>
            </binding>
        </visual>
        <actions>
            <action activationType=`"background`" content=`"OK`" arguments=`"later`"/>
        </actions>
    </toast>"

    $toastXml = New-Object -TypeName Windows.Data.Xml.Dom.XmlDocument
    $toastXml.LoadXml($toastTemplate.OuterXml)

    $notify = [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier($appId)
    $notify.Show($toastXml)
}
function Start-DbaRunspace {
    <#
    .SYNOPSIS
        Starts a managed runspace

    .DESCRIPTION
        Starts a runspace that was registered to dbatools
        Simply registering does not automatically start a given runspace. Only by executing this function will it take effect.

    .PARAMETER Name
        The name of the registered runspace to launch

    .PARAMETER Runspace
        The runspace to launch. Returned by Get-DbaRunspace

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .EXAMPLE
        PS C:\> Start-DbaRunspace -Name 'mymodule.maintenance'

        Starts the runspace registered under the name 'mymodule.maintenance'
#>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [string[]]
        $Name,

        [Parameter(ValueFromPipeline = $true)]
        [Sqlcollaborative.Dbatools.Runspace.RunspaceContainer[]]
        $Runspace,

        [switch]
        [Alias('Silent')]$EnableException
    )

    process {
        foreach ($item in $Name) {
            # Ignore all output from Get-DbaRunspace - it'll be handled by the second loop
            if ($item -eq "Sqlcollaborative.Dbatools.Runspace.runspacecontainer") { continue }

            if ([Sqlcollaborative.Dbatools.Runspace.RunspaceHost]::Runspaces.ContainsKey($item.ToLower())) {
                try {
                    Write-Message -Level Verbose -Message "Starting runspace: $($item.ToLower())" -Target $item.ToLower()
                    [Sqlcollaborative.Dbatools.Runspace.RunspaceHost]::Runspaces[$item.ToLower()].Start()
                }
                catch {
                    Stop-Function -Message "Failed to start runspace: $($item.ToLower())" -EnableException $EnableException -Target $item.ToLower() -Continue
                }
            }
            else {
                Stop-Function -Message "Failed to start runspace: $($item.ToLower()) | No runspace registered under this name!" -EnableException $EnableException -Category InvalidArgument -Tag "fail", "argument", "runspace", "start" -Target $item.ToLower() -Continue
            }
        }

        foreach ($item in $Runspace) {
            try {
                Write-Message -Level Verbose -Message "Starting runspace: $($item.Name.ToLower())" -Target $item
                $item.Start()
            }
            catch {
                Stop-Function -Message "Failed to start runspace: $($item.Name.ToLower())" -EnableException $EnableException -Target $item -Continue
            }
        }
    }
}
function Start-DbccCheck {
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [object]$server,
        [string]$dbname,
        [switch]$table
    )

    $servername = $server.name

    if ($Pscmdlet.ShouldProcess($sourceserver, "Running dbcc check on $dbname on $servername")) {
        if ($server.ConnectionContext.StatementTimeout = 0 -ne 0) {
            $server.ConnectionContext.StatementTimeout = 0
        }

        try {
            if ($table) {
                $null = $server.databases[$dbname].CheckTables('None')
                Write-Verbose "Dbcc CheckTables finished successfully for $dbname on $servername"
            }
            else {
                $null = $server.Query("DBCC CHECKDB ([$dbname])")
                Write-Verbose "Dbcc CHECKDB finished successfully for $dbname on $servername"
            }
            return "Success"
        }
        catch {
            $message = $_.Exception
            if ($null -ne $_.Exception.InnerException) { $message = $_.Exception.InnerException }

            # english cleanup only sorry
            try {
                $newmessage = ($message -split "at Microsoft.SqlServer.Management.Common.ConnectionManager.ExecuteTSql")[0]
                $newmessage = ($newmessage -split "Microsoft.SqlServer.Management.Common.ExecutionFailureException:")[1]
                $newmessage = ($newmessage -replace "An exception occurred while executing a Transact-SQL statement or batch. ---> System.Data.SqlClient.SqlException:").Trim()
                $message = $newmessage
            }
            catch {
                $null
            }
            return $message.Trim()
        }
    }
}
function Stop-DbaRunspace {
    <#
    .SYNOPSIS
        Stops a managed runspace

    .DESCRIPTION
        Stops a runspace that was registered to dbatools.
        Will not cause errors if the runspace is already halted.

        Runspaces may not automatically terminate immediately when calling this function.
        Depending on the implementation of the scriptblock, this may in fact take a little time.
        If the scriptblock hasn't finished and terminated the runspace in a seemingly time, it will be killed by the system.
        This timeout is by default 30 seconds, but can be altered by using the Configuration System.
        For example, this line will increase the timeout to 60 seconds:
        Set-DbaConfig Runspace.StopTimeout 60

    .PARAMETER Name
        The name of the registered runspace to stop

    .PARAMETER Runspace
        The runspace to stop. Returned by Get-DbaRunspace

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .EXAMPLE
        PS C:\> Stop-DbaRunspace -Name 'mymodule.maintenance'

        Stops the runspace registered under the name 'mymodule.maintenance'
#>
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [string[]]
        $Name,

        [Parameter(ValueFromPipeline = $true)]
        [Sqlcollaborative.Dbatools.Runspace.RunspaceContainer[]]
        $Runspace,

        [switch]
        [Alias('Silent')]$EnableException
    )

    process {
        foreach ($item in $Name) {
            # Ignore all output from Get-DbaRunspace - it'll be handled by the second loop
            if ($item -eq "Sqlcollaborative.Dbatools.Runspace.runspacecontainer") { continue }

            if ([Sqlcollaborative.Dbatools.Runspace.RunspaceHost]::Runspaces.ContainsKey($item.ToLower())) {
                try {
                    Write-Message -Level Verbose -Message "Stopping runspace: $($item.ToLower())" -Target $item.ToLower()
                    [Sqlcollaborative.Dbatools.Runspace.RunspaceHost]::Runspaces[$item.ToLower()].Stop()
                }
                catch {
                    Stop-Function -Message "Failed to stop runspace: $($item.ToLower())" -EnableException $EnableException -Target $item.ToLower() -Continue
                }
            }
            else {
                Stop-Function -Message "Failed to stop runspace: $($item.ToLower()) | No runspace registered under this name!" -EnableException $EnableException -Category InvalidArgument -Target $item.ToLower() -Continue
            }
        }

        foreach ($item in $Runspace) {
            try {
                Write-Message -Level Verbose -Message "Stopping runspace: $($item.Name.ToLower())" -Target $item
                $item.Stop()
            }
            catch {
                Stop-Function -Message "Failed to stop runspace: $($item.Name.ToLower())" -EnableException $EnableException -Target $item -Continue
            }
        }
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#

function Stop-Function {
<#
    .SYNOPSIS
        Function that interrupts a function.
    
    .DESCRIPTION
        Function that interrupts a function.
        
        This function is a utility function used by other functions to reduce error catching overhead.
        It is designed to allow gracefully terminating a function with a warning by default and also allow opt-in into terminating errors.
        It also allows simple integration into loops.
        
        Note:
        When calling this function with the intent to terminate the calling function in non-EnableException mode too, you need to add a return below the call.
    
    .PARAMETER Message
        A message to pass along, explaining just what the error was.
    
    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
    
    .PARAMETER Category
        What category does this termination belong to?
        Mandatory so long as no inner exception is passed.
    
    .PARAMETER ErrorRecord
        An option to include an inner exception in the error record (and in the exception thrown, if one is thrown).
        Use this, whenever you call Stop-Function in a catch block.
        
        Note:
        Pass the full error record, not just the exception.
    
    .PARAMETER Tag
        Tags to add to the message written.
        This allows filtering and grouping by category of message, targeting specific messages.
    
    .PARAMETER FunctionName
        The name of the function to crash.
        This parameter is very optional, since it automatically selects the name of the calling function.
        The function name is used as part of the errorid.
        That in turn allows easily figuring out, which exception belonged to which function when checking out the $error variable.
    
    .PARAMETER File
        The file in which Stop-PSFFunction was called.
        Will be automatically set, but can be overridden when necessary.
    
    .PARAMETER Line
        The line on which Stop-PSFFunction was called.
        Will be automatically set, but can be overridden when necessary.
    
    .PARAMETER Target
        The object that was processed when the error was thrown.
        For example, if you were trying to process a Database Server object when the processing failed, add the object here.
        This object will be in the error record (which will be written, even in non-EnableException mode, just won't show it).
        If you specify such an object, it becomes simple to actually figure out, just where things failed at.
    
    .PARAMETER Exception
        Allows specifying an inner exception as input object. This will be passed on to the logging and used for messages.
        When specifying both ErrorRecord AND Exception, Exception wins, but ErrorRecord is still used for record metadata.
    
    .PARAMETER OverrideExceptionMessage
        Disables automatic appending of exception messages.
        Use in cases where you already have a speaking message interpretation and do not need the original message.
    
    .PARAMETER Continue
        This will cause the function to call continue while not running silently.
        Useful when mass-processing items where an error shouldn't break the loop.
    
    .PARAMETER SilentlyContinue
        This will cause the function to call continue while running silently.
        Useful when mass-processing items where an error shouldn't break the loop.
    
    .PARAMETER ContinueLabel
        When specifying a label in combination with "-Continue" or "-SilentlyContinue", this function will call continue with this specified label.
        Helpful when trying to continue on an upper level named loop.
    
    .EXAMPLE
        Stop-Function -Message "Foo failed bar!" -EnableException $EnableException -ErrorRecord $_
        return
        
        Depending on whether $EnableException is true or false it will:
        - Throw a bloody terminating error. Game over.
        - Write a nice warning about how Foo failed bar, then terminate the function. The return on the next line will then end the calling function.
    
    .EXAMPLE
        Stop-Function -Message "Foo failed bar!" -EnableException $EnableException -Category InvalidOperation -Target $foo -Continue
        
        Depending on whether $silent is true or false it will:
        - Throw a bloody terminating error. Game over.
        - Write a nice warning about how Foo failed bar, then call continue to process the next item in the loop.
        In both cases, the error record added to $error will have the content of $foo added, the better to figure out what went wrong.
#>
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
    [CmdletBinding(DefaultParameterSetName = 'Plain')]
    param (
        [Parameter(Mandatory = $true)]
        [string]
        $Message,

        [bool]
        [Alias('Silent')]
        $EnableException = $EnableException,

        [Parameter(ParameterSetName = 'Plain')]
        [Parameter(ParameterSetName = 'Exception')]
        [System.Management.Automation.ErrorCategory]
        $Category = ([System.Management.Automation.ErrorCategory]::NotSpecified),

        [Parameter(ParameterSetName = 'Exception')]
        [Alias('InnerErrorRecord')]
        [System.Management.Automation.ErrorRecord[]]
        $ErrorRecord,
        
        [string[]]
        $Tag,

        [string]
        $FunctionName = ((Get-PSCallStack)[0].Command),
        
        [string]
        $File,
        
        [int]
        $Line,

        [object]
        $Target,

        [System.Exception]
        $Exception,

        [switch]
        $OverrideExceptionMessage,

        [switch]
        $Continue,

        [switch]
        $SilentlyContinue,

        [string]
        $ContinueLabel
    )
    
    #region Initialize information on the calling command
    $callStack = (Get-PSCallStack)[1]
    if (-not $FunctionName) { $FunctionName = $callStack.Command }
    $ModuleName = "dbatools"
    if (-not $File) { $File = $callStack.Position.File }
    if (-not $Line) { $Line = $callStack.Position.StartLineNumber }
    #endregion Initialize information on the calling command
    
    #region Apply Transforms
    #region Target Transform
    if ($null -ne $Target) {
        $Target = Convert-DbaMessageTarget -Target $Target -FunctionName $FunctionName -ModuleName $ModuleName
    }
    #endregion Target Transform
    
    #region Exception Transforms
    if ($Exception) {
        $Exception = Convert-DbaMessageException -Exception $Exception -FunctionName $FunctionName -ModuleName $ModuleName
    }
    elseif ($ErrorRecord) {
        $int = 0
        while ($int -lt $ErrorRecord.Length) {
            $tempException = Convert-DbaMessageException -Exception $ErrorRecord[$int].Exception -FunctionName $FunctionName -ModuleName $ModuleName
            if ($tempException -ne $ErrorRecord[$int].Exception) {
                $ErrorRecord[$int] = New-Object System.Management.Automation.ErrorRecord($tempException, $ErrorRecord[$int].FullyQualifiedErrorId, $ErrorRecord[$int].CategoryInfo.Category, $ErrorRecord[$int].TargetObject)
            }
            
            $int++
        }
    }
    #endregion Exception Transforms
    #endregion Apply Transforms

    #region Message Handling
    $records = @()

    if ($ErrorRecord -or $Exception) {
        if ($ErrorRecord) {
            foreach ($record in $ErrorRecord) {
                if (-not $Exception) { $newException = New-Object System.Exception($record.Exception.Message, $record.Exception) }
                else { $newException = $Exception }
                if ($record.CategoryInfo.Category) { $Category = $record.CategoryInfo.Category }
                $records += New-Object System.Management.Automation.ErrorRecord($newException, "$($ModuleName)_$FunctionName", $Category, $Target)
            }
        }
        else {
            $records += New-Object System.Management.Automation.ErrorRecord($Exception, "$($ModuleName)_$FunctionName", $Category, $Target)
        }
        
        # Manage Debugging
        if ($EnableException) { Write-Message -Level Warning -Message $Message -EnableException $EnableException -FunctionName $FunctionName -Target $Target -ErrorRecord $records -Tag $Tag -ModuleName $ModuleName -OverrideExceptionMessage:$OverrideExceptionMessage -File $File -Line $Line 3>$null }
        else { Write-Message -Level Warning -Message $Message -EnableException $EnableException -FunctionName $FunctionName -Target $Target -ErrorRecord $records -Tag $Tag -ModuleName $ModuleName -OverrideExceptionMessage:$OverrideExceptionMessage -File $File -Line $Line }
    }
    else {
        $exception = New-Object System.Exception($Message)
        $records += New-Object System.Management.Automation.ErrorRecord($Exception, "dbatools_$FunctionName", $Category, $Target)
        
        # Manage Debugging
        if ($EnableException) { Write-Message -Level Warning -Message $Message -EnableException $EnableException -FunctionName $FunctionName -Target $Target -ErrorRecord $records -Tag $Tag -ModuleName $ModuleName -OverrideExceptionMessage:$true -File $File -Line $Line 3>$null}
        else { Write-Message -Level Warning -Message $Message -EnableException $EnableException -FunctionName $FunctionName -Target $Target -ErrorRecord $records -Tag $Tag -ModuleName $ModuleName -OverrideExceptionMessage:$true -File $File -Line $Line }
    }
    #endregion Message Handling



    #region EnableException Mode
    if ($EnableException) {
        if ($SilentlyContinue) {
            foreach ($record in $records) { Write-Error -Message $record -Category $Category -TargetObject $Target -Exception $record.Exception -ErrorId "dbatools_$FunctionName" -ErrorAction Continue }
            if ($ContinueLabel) { continue $ContinueLabel }
            else { Continue }
        }

        # Extra insurance that it'll stop
        Set-Variable -Name "__dbatools_interrupt_function_78Q9VPrM6999g6zo24Qn83m09XF56InEn4hFrA8Fwhu5xJrs6r" -Scope 1 -Value $true

        throw $records[0]
    }
    #endregion EnableException Mode

    #region Non-EnableException Mode
    else {
        # This ensures that the error is stored in the $error variable AND has its Stacktrace (simply adding the record would lack the stacktrace)
        foreach ($record in $records) {
            $null = Write-Error -Message $record -Category $Category -TargetObject $Target -Exception $record.Exception -ErrorId "dbatools_$FunctionName" -ErrorAction Continue 2>&1
        }

        if ($Continue) {
            if ($ContinueLabel) { continue $ContinueLabel }
            else { Continue }
        }
        else {
            # Make sure the function knows it should be stopping
            Set-Variable -Name "__dbatools_interrupt_function_78Q9VPrM6999g6zo24Qn83m09XF56InEn4hFrA8Fwhu5xJrs6r" -Scope 1 -Value $true

            return
        }
    }
    #endregion Non-EnableException Mode
}
function Test-Bound {
    <#
        .SYNOPSIS
            Helperfunction that tests, whether a parameter was bound.

        .DESCRIPTION
            Helperfunction that tests, whether a parameter was bound.

        .PARAMETER ParameterName
            The name(s) of the parameter that is tested for being bound.
            By default, the check is true when AT LEAST one was bound.

        .PARAMETER Not
            Reverses the result. Returns true if NOT bound and false if bound.

        .PARAMETER And
            All specified parameters must be present, rather than at least one of them.

        .PARAMETER BoundParameters
            The hashtable of bound parameters. Is automatically inherited from the calling function via default value. Needs not be bound explicitly.

        .EXAMPLE
            if (Test-Bound "Day")
            {

            }

            Snippet as part of a function. Will check whether the parameter "Day" was bound. If yes, whatever logic is in the conditional will be executed.

        .EXAMPLE
            Test-Bound -Not 'Login', 'Spid', 'ExcludeSpid', 'Host', 'Program', 'Database'

            Returns whether none of the parameters above were specified.

        .EXAMPLE
            Test-Bound -And 'Login', 'Spid', 'ExcludeSpid', 'Host', 'Program', 'Database'

            Returns whether any of the specified parameters was not bound
    #>
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $true, Position = 0)]
        [string[]]
        $ParameterName,

        [Alias('Reverse')]
        [switch]
        $Not,

        [switch]
        $And,

        [object]
        $BoundParameters = (Get-PSCallStack)[0].InvocationInfo.BoundParameters
    )

    if ($And) {
        $test = $true
    }
    else {
        $test = $false
    }

    foreach ($name in $ParameterName) {
        if ($And) {
            if (-not $BoundParameters.ContainsKey($name)) { $test = $false }
        }
        else {
            if ($BoundParameters.ContainsKey($name)) { $test = $true }
        }
    }

    return ((-not $Not) -eq $test)
}
function Test-ComputerTarget {
    <#
    .SYNOPSIS
        Validates wheher the input string can be legally used to target a computer.

    .DESCRIPTION
        Validates whether the input string can be legally used to target a computer.
        It will consider:
        - Names (NETBIOS/dns)
        - IPv4 Addresses
        - IPv6 Addresses
        It will resolve idn names into default ascii names according to the official rules, before rendering judgement.

    .PARAMETER ComputerName
        The name to verify

    .EXAMPLE
        PS C:\> Test-ComputerTarget -ComputerName 'server1'

        Will test whether 'server1' is a legal computername (hint: it is)

    .EXAMPLE
        PS C:\> "foo", "bar", "foo bar" | Test-ComputerTarget

        Will test, whether the names passed to it are legal targets.
        - The first two will pass, the last one will fail
        - Note that it will only return boolean values, so the order needs to be remembered (due to this, using it by pipeline on more than one object is not really recommended).
#>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true, Mandatory = $true)]
        [string[]]
        $ComputerName
    )

    process {
        foreach ($Computer in $ComputerName) {
            [Sqlcollaborative.Dbatools.Utility.Validation]::IsValidComputerTarget($ComputerName)
        }
    }
}
function Test-DbaDeprecation {
    <#
        .SYNOPSIS
            Tests whether a function or one of its parameters was called by a bad name.

        .DESCRIPTION
            Tests whether a function or one of its parameters was called by a bad name.
            This allows giving deprecation warnings - once per session - whenever a user uses something we are planning on removing.

            For example, when renaming a function, we give a grace period by adding an Alias for that function with its old name.
            However, we do not want to carry along this alias forever, so we give warning ahead of time using this function.
            When reaching the specified version, we then can safely remove the alias.

            Furthermore, this function is used for testing, whether such a removal was properly done.

        .PARAMETER DeprecatedOn
            The version this parameter or alias will be removed in.
            Generally, deprecated parameters and aliases should only be removed on major releases.

        .PARAMETER FunctionName
            Automatically filled with the calling function.
            The name of the function that contains either a deprecated alias or parameter.

        .PARAMETER Call
            The InvocationInfo of the calling function.
            Automatically filled.

        .PARAMETER Parameter
            The parameter that has become deprecated.
            On renamed parameters, keep a parameter-alias. This function will notice, when the alias is used.

        .PARAMETER Alias
            The alias of the command that will be deprecated.

        .PARAMETER CustomMessage
            This function will generate a default message. However, this may not always be appropriate.
            Use CustomMessage to tailor a response to the necessity of the moment.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .EXAMPLE
            PS C:\> Test-DbaDeprecation -DeprecatedOn "1.0.0.0" -Parameter 'Details'

            Will - once per session - complain if the parameter 'Details' is used.
            Will cause tests to fail, if it's still in the code after release 1.0.0.0.

        .EXAMPLE
            PS C:\> Test-DbaDeprecation -DeprecatedOn "1.0.0.0" -Alias Copy-SqlDatabase

            Will - once per session - complain if the alias 'Copy-SqlDatabase' is used.
            Will cause tests to fail, if it's still in the code after release 1.0.0.0.
    #>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Version]
        $DeprecatedOn,

        [string]
        $FunctionName = (Get-PSCallStack)[0].Command,

        [object]
        $Call = (Get-PSCallStack)[0].InvocationInfo,

        [Parameter(ParameterSetName = "Param", Mandatory = $true)]
        [string]
        $Parameter,

        [Parameter(ParameterSetName = "Alias", Mandatory = $true)]
        [string]
        $Alias,

        [string]
        $CustomMessage,

        [bool]
        [Alias('Silent')]
        $EnableException = $EnableException
    )

    switch ($PSCmdlet.ParameterSetName) {
        "Param" {
            $ast = [System.Management.Automation.Language.Parser]::ParseInput($Call.Line, [ref]$null, [ref]$null)
            $objects = $ast.FindAll( { $args[0] -is [System.Management.Automation.Language.CommandAst] }, $true)
            $sub = $objects | Where-Object Parent -Like "$($Call.InvocationName)*" | Select-Object -First 1

            if ($sub.CommandElements | Where-Object ParameterName -eq $Parameter) {
                if ($CustomMessage) { $Message = $CustomMessage }
                else { $Message = "Using the parameter $Parameter is deprecated. This parameter will be removed in version $DeprecatedOn, check in the documentation what parameter to use instead" }

                Write-Message -Message $Message -Level Warning -FunctionName $FunctionName -Once "Deprecated.Alias.$Alias"
            }
        }

        "Alias" {
            if ($Alias -eq $Call.InvocationName) {
                if ($CustomMessage) { $Message = $CustomMessage }
                else { $Message = "Using the alias $Alias is deprecated. This alias will be removed in version $DeprecatedOn, use $FunctionName instead" }

                Write-Message -Message $Message -Level Warning -FunctionName $FunctionName -Once "Deprecated.Alias.$Alias"
            }
        }
    }
}
function Test-DbaLsnChain {
    <#
    .SYNOPSIS
        Checks that a filtered array from Get-FilteredRestore contains a restorabel chain of LSNs

    .DESCRIPTION
        Finds the anchoring Full backup (or multiple if it's a striped set).
        Then filters to ensure that all the backups are from that anchor point (LastLSN) and that they're all on the same RecoveryForkID
        Then checks that we have either enough Diffs and T-log backups to get to where we want to go. And checks that there is no break between
        LastLSN and FirstLSN in sequential files

    .PARAMETER FilteredRestoreFiles
        This is just an object consisting of the output from Read-DbaBackupHeader. Normally this will have been filtered down to a restorable chain
        before arriving here. (ie; only 1 anchoring Full backup)

    .NOTES
        Author: Stuart Moore (@napalmgram), stuart-moore.com
        Tags:
        dbatools PowerShell module (https://dbatools.io, [email protected])
        Copyright (C) 2016 Chrissy LeMaire
        License: MIT https://opensource.org/licenses/MIT

    .EXAMPLE
        Test-DbaLsnChain -FilteredRestoreFiles $FilteredFiles

        Checks that the Restore chain in $FilteredFiles is complete and can be fully restored

#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [object[]]$FilteredRestoreFiles,
        [switch]$Continue,
        [switch]$EnableException
    )


    begin {
        #Need to anchor  with full backup:
        $TestHistory = @()
    }
    process {
        foreach ($bh in $FilteredRestoreFiles) {
            $TestHistory += $bh
        }
    }
    end {
        if ($continue) {
            return $true
        }
        Write-Message -Level Verbose -Message "Testing LSN Chain"
        if ($null -eq $TestHistory[0].BackupTypeDescription) {
            $TypeName = 'Type'
        }
        else {
            $TypeName = "BackupTypeDescription"
        }
        Write-Message -Level VeryVerbose -Message "Testing LSN Chain - Type $typename"
        $FullDBAnchor = $TestHistory | Where-Object {$_.$TypeName -in ('Database', 'Full') }

        if (($FullDBAnchor | Group-Object -Property FirstLSN | Measure-Object).Count -ne 1) {
            $cnt = ($FullDBAnchor | Group-Object -Property FirstLSN | Measure-Object).Count
            foreach ($tFile in $FullDBAnchor) {
                Write-Message -Level Debug -Message "$($tfile.FirstLsn) - $($tfile.TypeName)"
            }
            Write-Message -Level Verbose -Message "db count = $cnt"
            Write-Message -Level Warning -Message "More than 1 full backup from a different LSN, or less than 1, neither supported"

            return $false
            break;
        }

        #Via LSN chain:
        [BigInt]$CheckPointLSN = ($FullDBAnchor | Select-Object -First 1).CheckPointLSN.ToString()
        [BigInt]$FullDBLastLSN = ($FullDBAnchor | Select-Object -First 1).LastLSN.ToString()
        $BackupWrongLSN = $FilteredRestoreFiles | Where-Object {$_.DatabaseBackupLSN -ne $CheckPointLSN}
        #Should be 0 in there, if not, lets check that they're from during the full backup
        if ($BackupWrongLSN.count -gt 0 ) {
            if (($BackupWrongLSN | Where-Object {[BigInt]$_.LastLSN.ToString() -lt $FullDBLastLSN}).count -gt 0) {
                Write-Message -Level Warning -Message "We have non matching LSNs - not supported"
                return $false
                break;
            }
        }
        $DiffAnchor = $TestHistory | Where-Object {$_.$TypeName -in ('Database Differential', 'Differential')}
        #Check for no more than a single Differential backup
        if (($DiffAnchor.FirstLSN | Select-Object -unique | Measure-Object).count -gt 1) {
            Write-Message -Level Warning -Message "More than 1 differential backup, not supported"
            return $false
            break;
        }
        elseif (($DiffAnchor | Measure-Object).Count -eq 1) {
            Write-Message -Level VeryVerbose -Message "Found a diff file, setting Log Anchor"
            $TlogAnchor = $DiffAnchor
        }
        else {
            $TlogAnchor = $FullDBAnchor
        }


        #Check T-log LSNs form a chain.
        $TranLogBackups = $TestHistory | Where-Object {$_.$TypeName -in ('Transaction Log', 'Log') -and $_.DatabaseBackupLSN -eq $FullDBAnchor.CheckPointLSN} | Sort-Object -Property LastLSN, FirstLsn
        for ($i = 0; $i -lt ($TranLogBackups.count)) {
            Write-Message -Level Debug -Message "looping t logs"
            if ($i -eq 0) {
                if ($TranLogBackups[$i].FirstLSN -gt $TlogAnchor.LastLSN) {
                    Write-Message -Level Warning -Message "Break in LSN Chain between $($TlogAnchor.FullName) and $($TranLogBackups[($i)].FullName) "
                    Write-Message -Level Verbose -Message "Anchor $($TlogAnchor.LastLSN) - FirstLSN $($TranLogBackups[$i].FirstLSN)"
                    return $false
                    break
                }
            }
            else {
                if ($TranLogBackups[($i - 1)].LastLsn -ne $TranLogBackups[($i)].FirstLSN -and ($TranLogBackups[($i)] -ne $TranLogBackups[($i - 1)])) {
                    Write-Message -Level Warning -Message "Break in transaction log between $($TranLogBackups[($i-1)].FullName) and $($TranLogBackups[($i)].FullName) "
                    return $false
                    break
                }
            }
            $i++

        }
        Write-Message -Level VeryVerbose -Message "Passed LSN Chain checks"
        return $true
    }
}
function Test-DbaRestoreVersion {
    <#
    .SYNOPSIS
        Checks that the restore files are from a version of SQL Server that can be restored on the target version

    .DESCRIPTION
        Finds the anchoring Full backup (or multiple if it's a striped set).
        Then filters to ensure that all the backups are from that anchor point (LastLSN) and that they're all on the same RecoveryForkID
        Then checks that we have either enough Diffs and T-log backups to get to where we want to go. And checks that there is no break between
        LastLSN and FirstLSN in sequential files

    .PARAMETER FilteredRestoreFiles
        This is just an object consisting of the output from Read-DbaBackupHeader. Normally this will have been filtered down to a restorable chain
        before arriving here. (ie; only 1 anchoring Full backup)

    .PARAMETER SqlInstance
        Sql Server Instance against which the restore is going to be performed

    .PARAMETER SqlCredential
        Credential for connecting to SqlInstance

    .PARAMETER SystemDatabaseRestore
        Switch when restoring system databases

    .NOTES
        Author: Stuart Moore (@napalmgram), stuart-moore.com
        Tags:
        dbatools PowerShell module (https://dbatools.io, [email protected])
        Copyright (C) 2016 Chrissy LeMaire
        License: MIT https://opensource.org/licenses/MIT

    .EXAMPLE
        Test-DbaRestoreVersion -FilteredRestoreFiles $FilteredFiles -SqlInstance server1\instance1

        Checks that the Restore chain in $FilteredFiles is compatible with the SQL Server version of server1\instance1

#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [object]$SqlInstance,
        [parameter(Mandatory = $true)]
        [object[]]$FilteredRestoreFiles,
        [PSCredential]$SqlCredential,
        [switch]$SystemDatabaseRestore
    )
    $RestoreVersion = ($FilteredRestoreFiles.SoftwareVersionMajor | Measure-Object -average).average
    Write-Message -Level Verbose -Message "RestoreVersion is $RestoreVersion"
    #Test to make sure we don't have an upgrade mid backup chain, there's a reason I'm paranoid..
    if ([int]$RestoreVersion -ne $RestoreVersion) {
        Write-Message -Level Warning -Message "Version number change during backups - $RestoreVersion"
        return $false
        break
    }
    #Can't restore backwards
    try {
        if ($SqlInstance -isnot [Microsoft.SqlServer.Management.Smo.SqlSmoObject]) {
            $Newconnection = $true
            $Server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
        }
        else {
            $server = $SqlInstance
        }
    }
    catch {
        Write-Message -Level Warning -Message "Cannot connect to $SqlInstance"
        break
    }

    if ($SystemDatabaseRestore) {
        if ($RestoreVersion -ne $Server.VersionMajor) {
            Write-Message -Level Warning -Message "For System Database restore versions must match)"
            return $false
            break
        }
    }
    else {
        if ($RestoreVersion -gt $Server.VersionMajor) {
            Write-Message -Level Warning -Message "Backups are from a newer version of SQL Server than $($Server.Name)"
            return $false
            break
        }

        if (($Server.VersionMajor -gt 10 -and $RestoreVersion -lt 9)  ) {
            Write-Message -Level Warning -Message "This version - $RestoreVersion - too old to restore on to $($Server.Name)"
            return $false
            break
        }
    }
    if ($Newconnection) {
        $server.ConnectionContext.Disconnect()
    }
    return $True
}

#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#

function Test-ElevationRequirement {
    <#
        .SYNOPSIS
            Command that tests, whether the process runs elevated and has to run as such.

        .DESCRIPTION
            Command that tests, whether the process runs elevated and has to run as such.
            Some commands require to be run elevated, when executed against localhost, but not when run against a remote computer.
            This command handles that test and manages the reaction to it.

        .PARAMETER ComputerName
            The computer that is being targeted by the calling command.
            This must be a localhost variety, for it to be able to fail.

        .PARAMETER Continue
            When using the native capability to terminate on fail, this will call continue in non-EnableException mode.

        .PARAMETER ContinueLabel
            When using the native capability to terminate on fail, and using a continue mode, the continue will continue with this label.

        .PARAMETER SilentlyContinue
            When using the native capability to terminate on fail, this will call continue in EnableException mode.

        .PARAMETER NoStop
            Does not call stop-function when the test fails, rather only returns $false instead

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .EXAMPLE
            $null = Test-ElevationRequirement -ComputerName $instance -Continue

            This will test whether the currently processed instance is localhost and the process is running elevated.
            If it should have elevation but is not running with elevation:
            - In silent mode it will termiante with an exception
            - In default mode, it will continue with the next instance

        .EXAMPLE
            if (-not ( Test-ElevationRequirement -ComputerName $instance -NoStop)) {
                # Do whatever
            }

        This will test whether the currently processed instance is localhost and the process is running elevated.
        If it isn't running elevated but should be, the overall condition will be met and the if-block is executed.
    #>
    [CmdletBinding(DefaultParameterSetName = 'Stop')]
    param (
        [DbaInstanceParameter]
        $ComputerName,

        [Parameter(ParameterSetName = 'Stop')]
        [switch]
        $Continue,

        [Parameter(ParameterSetName = 'Stop')]
        [string]
        $ContinueLabel,

        [Parameter(ParameterSetName = 'Stop')]
        [switch]
        $SilentlyContinue,

        [Parameter(ParameterSetName = 'NoStop')]
        [switch]
        $NoStop,

        [bool]
        [Alias('Silent')]
        $EnableException = $EnableException
    )

    $isElevated = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
    $testResult = $true
    if ($ComputerName.IsLocalHost -and (-not $isElevated)) { $testResult = $false }

    if ($PSCmdlet.ParameterSetName -like "NoStop") {
        return $testResult
    }
    elseif ($PSCmdlet.ParameterSetName -like "Stop") {
        if ($testResult) { return $testResult }

        $splatStopFunction = @{
            Message = "Console not elevated, but elevation is required to perform some actions on localhost for this command."
        }

        if (Test-Bound "Continue") { $splatStopFunction["Continue"] = $Continue }
        if (Test-Bound "ContinueLabel") { $splatStopFunction["ContinueLabel"] = $ContinueLabel }
        if (Test-Bound "SilentlyContinue") { $splatStopFunction["SilentlyContinue"] = $SilentlyContinue }

        . Stop-Function @splatStopFunction -FunctionName (Get-PSCallStack)[1].Command
        return $testResult
    }
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#

function Test-FunctionInterrupt {
    <#
        .SYNOPSIS
            Internal tool, used to gracefully interrupt a function.

        .DESCRIPTION
            This helper function is designed to work in tandem with Stop-Function.
            When gracefully terminating a function, there is a major issue:
            "Return" will only stop the current one of the three blocks (Begin, Process, End).
            All other statements have side effects or produce lots of red text.

            So, Stop-Function writes a variable into the parent scope, that signals the function should cease.
            This function then checks for that very variable and returns true if it is set.

            This avoids having to handle odd variables in the parent function and causes the least impact on contributors.

        .EXAMPLE
            if (Test-FunctionInterrupt) { return }

            The calling function will stop if this function returns true.
    #>
    [CmdletBinding()]
    param (

    )

    $var = Get-Variable -Name "__dbatools_interrupt_function_78Q9VPrM6999g6zo24Qn83m09XF56InEn4hFrA8Fwhu5xJrs6r" -Scope 1 -ErrorAction Ignore
    if ($var.Value) { return $true }

    return $false
}
function Test-HostOSLinux {
    param (
        [object]$SqlInstance,
        [object]$sqlcredential
    )

    $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $sqlcredential
    $server.ConnectionContext.ExecuteScalar("SELECT @@VERSION") -match "Linux"
}
#requires -version 3.0

function Test-PSRemoting {
    <#
    Jeff Hicks
    https://www.petri.com/test-network-connectivity-powershell-test-connection-cmdlet
#>
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUsePSCredentialType", "")]
    [Cmdletbinding()]
    param(
        [Parameter(Position = 0, Mandatory, ValueFromPipeline)]
        [DbaInstance]$ComputerName,
        $Credential = [System.Management.Automation.PSCredential]::Empty,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        Write-Message -Level VeryVerbose -Message "Testing $($ComputerName.Computername)"
        try {
            $null = Test-WSMan -ComputerName $ComputerName.ComputerName -Credential $Credential -Authentication Default -ErrorAction Stop
            $true
        }
        catch {
            Write-Message -Level Verbose -Message "Testing $($ComputerName.Computername)" -Target $ComputerName -ErrorRecord $_
            $false
        }

    } #process

} #close function
function Test-SqlAgent {
    <#
    .SYNOPSIS
        Internal function. Checks to see if SQL Server Agent is running on a server.
#>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [Alias("ServerInstance", "SqlServer")]
        [object]$SqlInstance,
        [PSCredential]$SqlCredential
    )

    if ($SqlInstance.GetType() -ne [Microsoft.SqlServer.Management.Smo.Server]) {
        $SqlInstance = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
    }

    if ($null -eq $SqlInstance.JobServer) { return $false }
    try { $null = $SqlInstance.JobServer.script(); return $true }
    catch { return $false }
}
function Test-SqlLoginAccess {
    <#
    .SYNOPSIS
        Internal function. Ensures login has access on SQL Server.
#>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [Alias("ServerInstance", "SqlServer")]
        [object]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string]$Login
        #[switch]$Detailed - can return if its a login or just has access
    )

    if ($SqlInstance.GetType() -ne [Microsoft.SqlServer.Management.Smo.Server]) {
        $SqlInstance = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
    }

    if (($SqlInstance.Logins.Name) -notcontains $Login) {
        try {
            $rows = $SqlInstance.ConnectionContext.ExecuteScalar("EXEC xp_logininfo '$Login'")

            if (($rows | Measure-Object).Count -eq 0) {
                return $false
            }
        }
        catch {
            return $false
        }
    }
    return $true
}
function Test-SqlQueryComplete {
    param (
        [Alias("SqlInstance", "SqlServer")]
        [object]$server,
        [string]$sql,
        [switch]$checkpid
    )

    if ($checkpid) {
        $sqlpid = " and session_id = $sqlpid"
    }

    $sqlpid = $server.ConnectionContext.ProcessID
    $sqlpid = " and session_id = $sqlpid"
    $sql = $sql.Replace("'", "''")
    $testsql = "select sqltext.text FROM sys.dm_exec_requests req CROSS APPLY sys.dm_exec_sql_text(sql_handle) AS sqltext where text = '$sql' $sqlpid"

    if ($server.ConnectionContext.ExecuteScalar($testsql) -ne $null) {
        return $false
    }
    else {
        return $true
    }
}
function Test-SqlSa {
    <#
    .SYNOPSIS
        Internal function. Ensures sysadmin account access on SQL Server.
#>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [Alias("ServerInstance", "SqlServer")]
        [object]$SqlInstance,
        [PSCredential]$SqlCredential
    )

    try {

        if ($SqlInstance.GetType() -eq [Microsoft.SqlServer.Management.Smo.Server]) {
            return ($SqlInstance.ConnectionContext.FixedServerRoles -match "SysAdmin")
        }

        $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
        return ($server.ConnectionContext.FixedServerRoles -match "SysAdmin")
    }
    catch { return $false }
}
function Update-ServiceStatus {
    <#
    .SYNOPSIS
        Internal function. Sends start/stop request to a SQL Server service and wait for the result.

    .DESCRIPTION
        Accepts objects from Get-DbaSqlService and performs a corresponding action.

    .PARAMETER Credential
        Credential object used to connect to the computer as a different user.

    .PARAMETER Timeout
        How long to wait for the start/stop request completion before moving on.

    .PARAMETER InputObject
        A collection of services from Get-DbaSqlService

    .PARAMETER Action
        Start or stop.

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .PARAMETER WhatIf
        Shows what would happen if the cmdlet runs. The cmdlet is not run.

    .PARAMETER Confirm
        Prompts you for confirmation before running the cmdlet.

    .NOTES
        Author: Kirill Kravtsov ( @nvarscar )
        Tags:
        dbatools PowerShell module (https://dbatools.io)
        Copyright (C) 2016 Chrissy LeMaire
        License: MIT https://opensource.org/licenses/MIT

    .EXAMPLE
        $InputObject = Get-DbaSqlService -ComputerName sql1
        Update-ServiceStatus -InputObject $InputObject -Action 'stop' -Timeout 30
        Update-ServiceStatus -InputObject $InputObject -Action 'start' -Timeout 30

        Restarts SQL services on sql1

    .EXAMPLE
        $InputObject = Get-DbaSqlService -ComputerName sql1
        $credential = Get-Credential
        Update-ServiceStatus -InputObject $InputObject -Action 'stop' -Timeout 0 -Credential $credential

        Stops SQL services on sql1 and waits indefinitely for them to stop. Uses $credential to authorize on the server.
#>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param(
        [parameter(ValueFromPipeline = $true, Mandatory = $true)]
        [object[]]$InputObject,
        [parameter(Mandatory = $true)]
        [string[]]$Action,
        [int]$Timeout = 30,
        [PSCredential] $Credential,
        [bool][Alias('Silent')]$EnableException
    )
    begin {
        $callStack = Get-PSCallStack
        if ($callStack.Length -gt 1) {
            $callerName = $callStack[1].Command
        }
        else {
            $callerName = $callStack[0].Command
        }
        #Prepare the service control script block
        $svcControlBlock = {
            param (
                $server,
                $service,
                $action,
                $timeout,
                [System.Management.Automation.PSCredential]
                $credential
            )

            #Perform $action
            if ($action -in 'start', 'restart') {
                $methodName = 'StartService'
                $desiredState = 'Running'
                $undesiredState = 'Stopped'
            }
            elseif ($action -eq 'stop') {
                $methodName = 'StopService'
                $desiredState = 'Stopped'
                $undesiredState = 'Running'
            }
            #Get CIM object
            try {
                $svc = Get-DbaCmObject -ComputerName $server -Namespace "root\cimv2" -query "SELECT * FROM Win32_Service WHERE name = '$service'" -Credential $credential
            }
            catch {
                throw $_
                break
            }
            #Invoke corresponding CIM method
            $x = Invoke-CimMethod -InputObject $svc -MethodName $methodName

            $result = [psobject](@{} | Select-Object ExitCode, ServiceState)
            #If command was not accepted
            if ($x.ReturnValue -ne 0) {
                $result.ExitCode = $x.ReturnValue
                $result.ServiceState = $svc.State
            }
            else {
                $startTime = Get-Date
                #Wait for the service to complete the action until timeout
                while ($true) {
                    try {
                        $svc = Get-DbaCmObject -ComputerName $server -Namespace "root\cimv2" -query "SELECT State FROM Win32_Service WHERE name = '$service'" -Credential $credential
                    }
                    catch {
                        throw $_
                        break
                    }
                    $result.ServiceState = $svc.State
                    #Succeeded
                    if ($svc.State -eq $desiredState) { $result.ExitCode = 0; break }
                    #Failed after being in the Pending state
                    if ($pending -and $svc.State -eq $undesiredState) { $result.ExitCode = -2; break }
                    #Timed out
                    if ($timeout -gt 0 -and ((Get-Date) - $startTime).TotalSeconds -gt $timeout) { $result.ExitCode = -1; break}
                    #Still pending
                    if ($svc.State -like '*Pending') { $pending = $true }
                    Start-Sleep -Milliseconds 100
                }
            }
            $result
        }

        $actionText = switch ($action) { stop { 'stopped' }; start { 'started' }; restart { 'restarted' } }
        #Setup initial session state
        $InitialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
        $InitialSessionState.ImportPSModule((get-module dbatools).modulebase + '\dbatools.psd1')
        #Create Runspace pool, min - 1, max - 50 sessions
        $runspacePool = [runspacefactory]::CreateRunspacePool(1, 50, $InitialSessionState, $Host)
        $runspacePool.Open()
    }

    process {
        $threads = @()

        #Get priorities on which the service startup/shutdown order is based
        $servicePriorityCollection = $InputObject.ServicePriority | Select-Object -unique | Sort-Object -Property @{ Expression = { [int]$_ }; Descending = $action -ne 'stop' }
        foreach ($priority in $servicePriorityCollection) {
            foreach ($service in ($InputObject | Where-Object { $_.ServicePriority -eq $priority })) {
                if ('dbatools.DbaSqlService' -in $service.PSObject.TypeNames) {
                    if (($service.State -eq 'Running' -and $action -eq 'start') -or ($service.State -eq 'Stopped' -and $action -eq 'stop')) {
                        Add-Member -Force -InputObject $service -NotePropertyName Status -NotePropertyValue 'Successful'
                        Add-Member -Force -InputObject $service -NotePropertyName Message -NotePropertyValue "The service is already $actionText, no action required"
                        Select-DefaultView -InputObject $service -Property ComputerName, ServiceName, State, Status, Message
                    }
                    elseif ($service.StartMode -eq 'Disabled' -and $action -in 'start', 'restart') {
                        Add-Member -Force -InputObject $service -NotePropertyName Status -NotePropertyValue 'Failed'
                        Add-Member -Force -InputObject $service -NotePropertyName Message -NotePropertyValue "The service is disabled and cannot be $actionText"
                        Select-DefaultView -InputObject $service -Property ComputerName, ServiceName, State, Status, Message
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess("Sending $action request to service $($service.ServiceName) on $($service.ComputerName)")) {
                            #Create parameters hashtable
                            $argsRunPool = @{
                                server     = $service.computerName
                                service    = $service.ServiceName
                                action     = $action
                                timeout    = $Timeout
                                credential = $Credential
                            }
                            Write-Message -Level Verbose -Message "Sending $action request to service $($service.ServiceName) on $($service.ComputerName) with timeout $Timeout"
                            #Create new runspace thread
                            $thread = [powershell]::Create()
                            $thread.RunspacePool = $runspacePool
                            $thread.AddScript($svcControlBlock) | Out-Null
                            $thread.AddParameters($argsRunPool) | Out-Null
                            #Start the thread
                            $handle = $thread.BeginInvoke()
                            $threads += [pscustomobject]@{
                                handle       = $handle
                                thread       = $thread
                                serviceName  = $service.ServiceName
                                computerName = $service.ComputerName
                                isRetrieved  = $false
                                started      = Get-Date
                            }
                        }
                    }
                }
                else {
                    Stop-Function -FunctionName $callerName -Message "Unknown object in pipeline - make sure to use Get-DbaSqlService cmdlet" -EnableException $EnableException
                    Return
                }
            }
            if ($Pscmdlet.ShouldProcess("Waiting for the services to $action")) {
                #Get job execution results
                while ($threads | Where-Object { $_.isRetrieved -eq $false }) {
                    foreach ($thread in ($threads | Where-Object { $_.isRetrieved -eq $false })) {
                        if ($thread.Handle.IsCompleted -eq $true) {
                            Write-Message -Level Verbose -Message "Processing runspace thread results from service $($thread.ServiceName) on $($thread.ComputerName)"
                            $jobResult = $null
                            try {
                                $jobResult = $thread.thread.EndInvoke($thread.handle)
                            }
                            catch {
                                $jobError = $_
                                Write-Message -Level Verbose -Message ("Could not return data from the runspace thread: " + $_.Exception.Message)
                            }
                            $thread.isRetrieved = $true
                            if ($thread.thread.HadErrors) {
                                if (!$jobError) { $jobError = $thread.thread.Streams.Error }
                                Stop-Function -EnableException $EnableException -FunctionName $callerName -Message ("The attempt to $action the service $($thread.ServiceName) on $($thread.ComputerName) returned the following error: " + ($jobError.Exception.Message -join ' ')) -Category ConnectionError -ErrorRecord $thread.thread.Streams.Error -Target $thread -Continue
                            }
                            elseif (!$jobResult) {
                                Stop-Function -EnableException $EnableException -FunctionName $callerName -Message ("The attempt to $action the service $($thread.ServiceName) on $($thread.ComputerName) did not return any results") -Category ConnectionError -ErrorRecord $_ -Target $thread -Continue
                            }
                            #Find a corresponding service object
                            $outObject = $InputObject | Where-Object { $_.ServiceName -eq $thread.serviceName -and $_.ComputerName -eq $thread.computerName }
                            #Set additional properties
                            $status = switch ($jobResult.ExitCode) {
                                0 { 'Successful' }
                                10 { 'Successful '} #Already running - FullText service is started automatically
                                default { 'Failed' }
                            }
                            Add-Member -Force -InputObject $outObject -NotePropertyName Status -NotePropertyValue $status
                            $message = switch ($jobResult.ExitCode) {
                                -2 { "The service failed to $action." }
                                -1 { "The attempt to $action the service has timed out." }
                                0 { "Service was successfully $actionText." }
                                default { "The attempt to $action the service returned the following error: " + (Get-DBASQLServiceErrorMessage $jobResult.ExitCode) }
                            }
                            Add-Member -Force -InputObject $outObject -NotePropertyName Message -NotePropertyValue $message
                            if ($jobResult.ServiceState) { $outObject.State = $jobResult.ServiceState }
                            #Dispose of the thread
                            $thread.thread.Dispose()

                            Select-DefaultView -InputObject $outObject -Property ComputerName, ServiceName, State, Status, Message
                        }
                        elseif ($Timeout -gt 0 -and ((Get-Date) - $thread.started).TotalSeconds -gt $Timeout) {
                            #Session has timed out - return failure and stop the thread

                            $thread.isRetrieved = $true
                            $outObject = $InputObject | Where-Object { $_.ServiceName -eq $thread.serviceName -and $_.ComputerName -eq $thread.computerName }
                            #Set additional properties
                            Add-Member -Force -InputObject $outObject -NotePropertyName Status -NotePropertyValue 'Failed'
                            Add-Member -Force -InputObject $outObject -NotePropertyName Message -NotePropertyValue "The attempt to $action the service has timed out."
                            $outObject.State = 'Unknown'
                            #Stop and dispose of the thread
                            $thread.thread.Stop()
                            $thread.thread.Dispose()

                            Select-DefaultView -InputObject $outObject -Property ComputerName, ServiceName, State, Status, Message
                        }
                    }
                    Start-Sleep -Milliseconds 50
                }
            }
        }
    }
    end {
        #Close the runspace pool
        $runspacePool.Close()
    }
}
function Update-SqlDbOwner {
    <#
    .SYNOPSIS
        Internal function. Updates specified database dbowner.
#>
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [object]$source,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [object]$destination,
        [string]$dbname,
        [PSCredential]$SourceSqlCredential,
        [PSCredential]$DestinationSqlCredential
    )

    $sourceserver = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
    try {
        if ($Destination -isnot [Microsoft.SqlServer.Management.Smo.SqlSmoObject]) {
            $destserver = Connect-SqlInstance -SqlInstance $Destination -SqlCredential $SqlCredential
        }
        else {
            $destserver = $Destination
        }
    }
    catch {
        Write-Message -Level Warning "Cannot connect to $SqlInstance"
        break
    }

    $source = $sourceserver.DomainInstanceName
    $destination = $destserver.DomainInstanceName

    if ($dbname.length -eq 0) {
        $databases = ($sourceserver.Databases | Where-Object { $destserver.databases.name -contains $_.name -and $_.IsSystemObject -eq $false }).Name
    }
    else { $databases = $dbname }

    foreach ($dbname in $databases) {
        $destdb = $destserver.databases[$dbname]
        $dbowner = $sourceserver.databases[$dbname].owner

        if ($destdb.owner -ne $dbowner) {
            if ($destdb.Status -ne 'Normal') { Write-Output "Database status not normal. Skipping dbowner update."; continue }

            if ($null -eq $dbowner -or $null -eq $destserver.logins[$dbowner]) {
                try {
                    $dbowner = ($destserver.logins | Where-Object { $_.id -eq 1 }).Name
                }
                catch {
                    $dbowner = "sa"
                }
            }

            try {
                if ($destdb.ReadOnly -eq $true) {
                    $changeroback = $true
                    Update-SqlDbReadOnly $destserver $dbname $false
                }

                $destdb.SetOwner($dbowner)
                Write-Output "Changed $dbname owner to $dbowner"

                if ($changeroback) {
                    Update-SqlDbReadOnly $destserver $dbname $true
                    $changeroback = $null
                }
            }
            catch {
                Write-Error "Failed to update $dbname owner to $dbowner."
            }
        }
        else { Write-Output "Proper owner already set on $dbname" }
    }
}
function Update-SqlDbReadOnly {
    <#
    .SYNOPSIS
        Internal function. Updates specified database to read-only or read-write. Necessary because SMO doesn't appear to support NO_WAIT.
#>
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [Alias("ServerInstance", "SqlServer")]
        [object]$SqlInstance,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$dbname,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [bool]$readonly
    )

    if ($readonly) {
        Stop-DbaProcess -SqlInstance $SqlInstance -Database $dbname
        $sql = "ALTER DATABASE [$dbname] SET READ_ONLY WITH NO_WAIT"
    }
    else {
        $sql = "ALTER DATABASE [$dbname] SET READ_WRITE WITH NO_WAIT"
    }

    try {
        $server = Connect-SqlInstance -SqlInstance $SqlInstance
        $null = $server.Query($sql)
        Write-Message -Level Verbose -Message "Changed ReadOnly status to $readonly for $dbname on $($server.name)"
        return $true
    }
    catch {
        Write-Message -Level Warning "Could not change readonly status for $dbname on $($server.name)"
        return $false
    }
}
function Update-SqlPermissions {
    <#
        .SYNOPSIS
            Internal function. Updates permission sets, roles, database mappings on server and databases
        .PARAMETER SourceServer
            Source Server
        .PARAMETER SourceLogin
            Source login
        .PARAMETER DestServer
            Destination Server
        .PARAMETER DestLogin
            Destination Login
        .PARAMETER EnableException
            Use this switch to disable any kind of verbose messages
    #>
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "")]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [object]$SourceServer,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [object]$SourceLogin,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [object]$DestServer,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [object]$DestLogin,
        [Alias('Silent')]
        [switch]$EnableException
    )

    $destination = $DestServer.DomainInstanceName
    $source = $SourceServer.DomainInstanceName
    $userName = $SourceLogin.Name

    # Server Roles: sysadmin, bulklogin, etc
    foreach ($role in $SourceServer.Roles) {
        $roleName = $role.Name
        $destRole = $DestServer.Roles[$roleName]

        if ($null -ne $destRole) {
            try {
                $destRoleMembers = $destRole.EnumMemberNames()
            }
            catch {
                $destRoleMembers = $destRole.EnumServerRoleMembers()
            }
        }

        try {
            $roleMembers = $role.EnumMemberNames()
        }
        catch {
            $roleMembers = $role.EnumServerRoleMembers()
        }

        if ($roleMembers -contains $userName) {
            if ($null -ne $destRole) {
                if ($Pscmdlet.ShouldProcess($destination, "Adding $userName to $roleName server role.")) {
                    try {
                        $destRole.AddMember($userName)
                        Write-Message -Level Verbose -Message "Adding $userName to $roleName server role on $destination successfully performed."
                    }
                    catch {
                        Stop-Function -Message "Failed to add $userName to $roleName server role on $destination." -Target $role -ErrorRecord $_
                    }
                }
            }
        }

        # Remove for Syncs
        if ($roleMembers -notcontains $userName -and $destRoleMembers -contains $userName -and $null -ne $destRole) {
            if ($Pscmdlet.ShouldProcess($destination, "Adding $userName to $roleName server role.")) {
                try {
                    $destRole.DropMember($userName)
                    Write-Message -Level Verbose -Message "Removing $userName from $destRoleName server role on $destination successfully performed."
                }
                catch {
                    Stop-Function -Message "Failed to remove $userName from $destRoleName server role on $destination." -Target $role -ErrorRecord $_
                }
            }
        }
    }

    $ownedJobs = $SourceServer.JobServer.Jobs | Where-Object OwnerLoginName -eq $userName
    foreach ($ownedJob in $ownedJobs) {
        if ($null -ne $DestServer.JobServer.Jobs[$ownedJob.Name]) {
            if ($Pscmdlet.ShouldProcess($destination, "Changing of job owner to $userName for $($ownedJob.Name).")) {
                try {
                    $destOwnedJob = $DestServer.JobServer.Jobs | Where-Object { $_.Name -eq $ownedJobs.Name }
                    $destOwnedJob.Set_OwnerLoginName($userName)
                    $destOwnedJob.Alter()
                    Write-Message -Level Verbose -Message "Changing job owner to $userName for $($ownedJob.Name) on $destination successfully performed."
                }
                catch {
                    Stop-Function -Message "Failed to change job owner for $($ownedJob.Name) on $destination." -Target $ownedJob -ErrorRecord $_
                }
            }
        }
    }

    if ($SourceServer.VersionMajor -ge 9 -and $DestServer.VersionMajor -ge 9) {
        <#
            These operations are only supported by SQL Server 2005 and above.
            Securables: Connect SQL, View any database, Administer Bulk Operations, etc.
        #>

        $perms = $SourceServer.EnumServerPermissions($userName)
        foreach ($perm in $perms) {
            $permState = $perm.PermissionState
            if ($permState -eq "GrantWithGrant") {
                $grantWithGrant = $true;
                $permState = "grant"
            }
            else {
                $grantWithGrant = $false
            }

            $permSet = New-Object Microsoft.SqlServer.Management.Smo.ServerPermissionSet($perm.PermissionType)
            if ($Pscmdlet.ShouldProcess($destination, "$permState on $($perm.PermissionType) for $userName.")) {
                try {
                    $DestServer.PSObject.Methods[$permState].Invoke($permSet, $userName, $grantWithGrant)
                    Write-Message -Level Verbose -Message "$permState $($perm.PermissionType) to $userName on $destination successfully performed."
                }
                catch {
                    Stop-Function -Message "Failed to $permState $($perm.PermissionType) to $userName on $destination." -Target $perm -ErrorRecord $_
                }
            }

            # for Syncs
            $destPerms = $DestServer.EnumServerPermissions($userName)
            foreach ($perm in $destPerms) {
                $permState = $perm.PermissionState
                $sourcePerm = $perms | Where-Object { $_.PermissionType -eq $perm.PermissionType -and $_.PermissionState -eq $permState }

                if ($null -eq $sourcePerm) {
                    if ($Pscmdlet.ShouldProcess($destination, "Revoking $($perm.PermissionType) for $userName.")) {
                        try {
                            $permSet = New-Object Microsoft.SqlServer.Management.Smo.ServerPermissionSet($perm.PermissionType)

                            if ($permState -eq "GrantWithGrant") {
                                $grantWithGrant = $true;
                                $permState = "grant"
                            }
                            else {
                                $grantWithGrant = $false
                            }

                            $DestServer.PSObject.Methods["Revoke"].Invoke($permSet, $userName, $false, $grantWithGrant)
                            Write-Message -Level Verbose -Message "Revoking $($perm.PermissionType) for $userName on $destination successfully performed."
                        }
                        catch {
                            Stop-Function -Message "Failed to revoke $($perm.PermissionType) from $userName on $destination." -Target $perm -ErrorRecord $_
                        }
                    }
                }
            }
        }

        # Credential mapping. Credential removal not currently supported for Syncs.
        $loginCredentials = $SourceServer.Credentials | Where-Object { $_.Identity -eq $SourceLogin.Name }
        foreach ($credential in $loginCredentials) {
            if ($null -eq $DestServer.Credentials[$credential.Name]) {
                if ($Pscmdlet.ShouldProcess($destination, "Creating credential $($credential.Name) for $userName.")) {
                    try {
                        $newCred = New-Object Microsoft.SqlServer.Management.Smo.Credential($DestServer, $credential.Name)
                        $newCred.Identity = $SourceLogin.Name
                        $newCred.Create()
                        Write-Message -Level Verbose -Message "Creating credential $($credential.Name) for $userName on $destination successfully performed."
                    }
                    catch {
                        Stop-Function -Message "Failed to create credential $($credential.Name) for $userName on $destination." -Target $credential -ErrorRecord $_
                    }
                }
            }
        }
    }

    if ($DestServer.VersionMajor -lt 9) {
        Write-Message -Level Warning -Message "SQL Server 2005 or greater required for database mappings.";
        continue
    }

    # For Sync, if info doesn't exist in EnumDatabaseMappings, then no big deal.
    foreach ($db in $DestLogin.EnumDatabaseMappings()) {
        $dbName = $db.DbName
        $destDb = $DestServer.Databases[$dbName]
        $sourceDb = $SourceServer.Databases[$dbName]
        $dbUsername = $db.Username;
        $dbLogin = $db.LoginName

        if ($null -ne $sourceDb) {
            if (!$sourceDb.IsAccessible) {
                Write-Message -Level Verbose -Message "Database [$($sourceDb.Name)] is not accessible on $source. Skipping."
                continue
            }
            if ($null -eq $sourceDb.Users[$dbUsername] -and $null -eq $destDb.Users[$dbUsername]) {
                if ($Pscmdlet.ShouldProcess($destination, "Dropping user $dbUsername from $dbName.")) {
                    try {
                        $destDb.Users[$dbUsername].Drop()
                        Write-Message -Level Verbose -Message "Dropping user $dbUsername (login: $dbLogin) from $dbName on destination successfully performed."
                        Write-Message -Level Verbose -Message "Any schema in $dbaName owned by $dbUsername may still exist."
                    }
                    catch {
                        Stop-Function -Message "Failed to drop $dbUsername (login: $dbLogin) from $dbName on destination." -Target $db -ErrorRecord $_
                    }
                }
            }

            # Remove user from role. Role removal not currently supported for Syncs.
            # TODO: reassign if dbo, application roles
            foreach ($destRole in $destDb.Roles) {
                $destRoleName = $destRole.Name
                $sourceRole = $sourceDb.Roles[$destRoleName]
                if ($null -eq $sourceRole) {
                    if ($sourceRole.EnumMembers() -notcontains $dbUsername -and $destRole.EnumMembers() -contains $dbUsername) {
                        if ($dbUsername -ne "dbo") {
                            if ($Pscmdlet.ShouldProcess($destination, "Dropping user $userName from $destRoleName database role in $dbName.")) {
                                try {
                                    $destRole.DropMember($dbUsername)
                                    $destDb.Alter()
                                    Write-Message -Level Verbose -Message "Dropping user $dbUsername (login: $dbLogin) from $destRoleName database role in $dbName on $destination successfully performed."
                                }
                                catch {
                                    Stop-Function -Message "Failed to remove $dbUsername (login: $dbLogin) from $destRoleName database role in $dbName on $destination." -Target $destRole -ErrorRecord $_
                                }
                            }
                        }
                    }
                }
            }

            # Remove Connect, Alter Any Assembly, etc
            $destPerms = $destDb.EnumDatabasePermissions($userName)
            $perms = $sourceDb.EnumDatabasePermissions($userName)
            # for Syncs
            foreach ($perm in $destPerms) {
                $permState = $perm.PermissionState
                $sourcePerm = $perms | Where-Object { $_.PermissionType -eq $perm.PermissionType -and $_.PermissionState -eq $permState }
                if ($null -eq $sourcePerm) {
                    if ($Pscmdlet.ShouldProcess($destination, "Revoking $($perm.PermissionType) from $userName in $dbName.")) {
                        try {
                            $permSet = New-Object Microsoft.SqlServer.Management.Smo.DatabasePermissionSet($perm.PermissionType)

                            if ($permState -eq "GrantWithGrant") {
                                $grantWithGrant = $true;
                                $permState = "grant"
                            }
                            else {
                                $grantWithGrant = $false
                            }

                            $destDb.PSObject.Methods["Revoke"].Invoke($permSet, $userName, $false, $grantWithGrant)
                            Write-Message -Level Verbose -Message "Revoking $($perm.PermissionType) from $userName in $dbName on $destination successfully performed."
                        }
                        catch {
                            Stop-Function -Message "Failed to revoke $($perm.PermissionType) from $userName in $dbName on $destination." -Target $perm -ErrorRecord $_
                        }
                    }
                }
            }
        }
    }

    # Adding database mappings and securables
    foreach ($db in $SourceLogin.EnumDatabaseMappings()) {
        $dbName = $db.DbName
        $destDb = $DestServer.Databases[$dbName]
        $sourceDb = $SourceServer.Databases[$dbName]
        $dbUsername = $db.Username;
        $dbLogin = $db.LoginName

        if ($null -ne $destDb) {
            if (!$destDb.IsAccessible) {
                Write-Message -Level Verbose -Message "Database [$dbName] is not accessible. Skipping."
                continue
            }
            if ($null -eq $destDb.Users[$dbUsername]) {
                if ($Pscmdlet.ShouldProcess($destination, "Adding $dbUsername to $dbName.")) {
                    $sql = $SourceServer.Databases[$dbName].Users[$dbUsername].Script() | Out-String
                    try {
                        $destDb.ExecuteNonQuery($sql)
                        Write-Message -Level Verbose -Message "Adding user $dbUsername (login: $dbLogin) to $dbName successfully performed."
                    }
                    catch {
                        Stop-Function -Message "Failed to add $dbUsername (login: $dbLogin) to $dbName on $destination." -Target $db -ErrorRecord $_
                    }
                }
            }

            # Db owner
            if ($sourceDb.Owner -eq $userName) {
                if ($Pscmdlet.ShouldProcess($destination, "Changing $dbName dbowner to $userName.")) {
                    try {
                        $result = Update-SqlDbOwner $SourceServer $DestServer -DbName $dbName
                        if ($result -eq $true) {
                            Write-Message -Level Verbose -Message "Changed $($destDb.Name) owner to $($sourceDb.owner)."
                        }
                        else {
                            Write-Message -Level Warning -Message "Failed to update $($destDb.Name) owner to $($sourceDb.owner)."
                        }
                    }
                    catch {
                        Write-Message -Level Warning -Message "Failed to update $($destDb.Name) owner to $($sourceDb.owner)."
                    }
                }
            }

            # Database Roles: db_owner, db_datareader, etc
            foreach ($role in $sourceDb.Roles) {
                if ($role.EnumMembers() -contains $userName) {
                    $roleName = $role.Name
                    $destDbRole = $destDb.Roles[$roleName]

                    if ($null -ne $destDbRole -and $dbUsername -ne "dbo" -and $destDbRole.EnumMembers() -notcontains $userName) {
                        if ($Pscmdlet.ShouldProcess($destination, "Adding $userName to $roleName database role in $dbName.")) {
                            try {
                                $destDbRole.AddMember($userName)
                                $destDb.Alter()
                                Write-Message -Level Verbose -Message "Adding $userName to $roleName database role in $dbName on $destination successfully performed."
                            }
                            catch {
                                Stop-Function -Message "Failed to add $userName to $roleName database role in $dbName on $destination." -Target $role -ErrorRecord $_
                            }
                        }
                    }
                }
            }

            # Connect, Alter Any Assembly, etc
            $perms = $sourceDb.EnumDatabasePermissions($userName)
            foreach ($perm in $perms) {
                $permState = $perm.PermissionState
                if ($permState -eq "GrantWithGrant") {
                    $grantWithGrant = $true;
                    $permState = "grant"
                }
                else {
                    $grantWithGrant = $false
                }
                $permSet = New-Object Microsoft.SqlServer.Management.Smo.DatabasePermissionSet($perm.PermissionType)

                if ($Pscmdlet.ShouldProcess($destination, "$permState on $($perm.PermissionType) for $userName on $dbName")) {
                    try {
                        $destDb.PSObject.Methods[$permState].Invoke($permSet, $userName, $grantWithGrant)
                        Write-Message -Level Verbose -Message "$permState on $($perm.PermissionType) to $userName on $dbName on $destination successfully performed."
                    }
                    catch {
                        Stop-Function -Message "Failed to perform $permState on $($perm.PermissionType) to $userName on $dbName on $destination." -Target $perm -ErrorRecord $_
                    }
                }
            }
        }
    }
}
function global:Where-DbaObject {
    <#
        .SYNOPSIS
            A slightly more efficient filter function than Where-Object.

        .DESCRIPTION
            A slightly more efficient filter function than Where-Object.
            In case multiple filters are set, any one hit will work.

        .PARAMETER InputObject
            The object to process.

        .PARAMETER PropertyName
            Whether a property should be tested, rather than the input object itself.

        .PARAMETER Equals
            Tests for equality.

        .PARAMETER NotEquals
            Tests for inequality.

        .PARAMETER Like
            Tests for similarity.

        .PARAMETER NotLike
            Tests for non-similarity.

        .PARAMETER In
            Tests, whether the input is contained in a specified list.

        .PARAMETER NotIn
            Tests, whether the input is not contained in a specified list.

        .PARAMETER Match
            Tests for regex match.

        .PARAMETER NotMatch
            Tests for regex non-match.

        .EXAMPLE
            dir | Where-DbaObject Length -gt 1024

            Scans the current folder and filters out all files smaller then 1024 bytes

        .EXAMPLE
            "foo","bar" | Where-DbaObject -match "o"

            Filters out all strings that don't contain the letter "o"
    #>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true, Mandatory = $true)]
        [object]
        $InputObject,

        [Parameter(Position = 0)]
        [Alias('Property')]
        [string]
        $PropertyName,

        [Alias('Eq')]
        [object]
        $Equals,

        [Alias('Ne')]
        [object]
        $NotEquals,

        [object]
        $Like,

        [object]
        $NotLike,

        [object]
        $In,

        [object]
        $NotIn,

        [object]
        $Match,

        [object]
        $NotMatch
    )

    begin {
        $TestEquals = Test-Bound -ParameterName Equals
        $TestNotEquals = Test-Bound -ParameterName NotEquals
        $TestLike = Test-Bound -ParameterName Like
        $TestNotLike = Test-Bound -ParameterName NotLike
        $TestIn = Test-Bound -ParameterName In
        $TestNotIn = Test-Bound -ParameterName NotIn
        $TestMatch = Test-Bound -ParameterName Match
        $TestNotMatch = Test-Bound -ParameterName NotMatch

        $TestObject = -not ($TestEquals -or $TestNotEquals -or $TestLike -or $TestNotLike -or $TestIn -or $TestNotIn -or $TestMatch -or $TestNotMatch)

        $TestProperty = Test-Bound -ParameterName PropertyName
    }
    process {
        foreach ($item in $InputObject) {
            #region Test Property
            if ($TestProperty) {
                if ($TestObject -and $item.$PropertyName) { return $item }

                if ($TestEquals -and ($item.$PropertyName -eq $Equals)) { return $item }
                if ($TestNotEquals -and ($item.$PropertyName -ne $NotEquals)) { return $item }
                if ($TestLike -and ($item.$PropertyName -like $Like)) { return $item }
                if ($TestNotLike -and ($item.$PropertyName -notlike $NotLike)) { return $item }
                if ($TestIn -and ($item.$PropertyName -In $In)) { return $item }
                if ($TestNotIn -and ($item.$PropertyName -NotIn $NotIn)) { return $item }
                if ($TestMatch -and ($item.$PropertyName -Match $Match)) { return $item }
                if ($TestNotMatch -and ($item.$PropertyName -NotMatch $NotMatch)) { return $item }
            }
            #endregion Test Property
            #region Test Object
            else {
                if ($TestObject -and $item) { return $item }

                if ($TestEquals -and ($item -eq $Equals)) { return $item }
                if ($TestNotEquals -and ($item -ne $NotEquals)) { return $item }
                if ($TestLike -and ($item -like $Like)) { return $item }
                if ($TestNotLike -and ($item -notlike $NotLike)) { return $item }
                if ($TestIn -and ($item -In $In)) { return $item }
                if ($TestNotIn -and ($item -NotIn $NotIn)) { return $item }
                if ($TestMatch -and ($item -Match $Match)) { return $item }
                if ($TestNotMatch -and ($item -NotMatch $NotMatch)) { return $item }
            }
            #endregion Test Object
        }
    }
    end {

    }
}

(Get-Item Function:\Where-DbaObject).Visibility = "Private"
function Write-HostColor {
    <#
    .SYNOPSIS
        Function that recognizes html-style tags to insert color into printed text.

    .DESCRIPTION
        Function that recognizes html-style tags to insert color into printed text.

        Color tags should be designed to look like this:
        <c="<console color>">Text</c>
        For example this would be a valid string:
        "This message should <c="red">partially be painted in red</c>!"

        This allows specifying color within strings and avoids having to piece together colored text in multiple calls to Write-Host.
        Only colors that are part of the ConsoleColor enumeration can be used. Bad colors will be ignored in favor of the default color.

    .PARAMETER String
        The message to write to host.

    .PARAMETER DefaultColor
        Default: (Get-DbaConfigValue -Name "message.infocolor")
        The color to write stuff to host in when no (or bad) color-code was specified.

    .EXAMPLE
        Write-HostColor -String 'This is going to be <c="red">bloody red</c> text! And this is <c="green">green stuff</c> for extra color'

        Will print the specified line in multiple colors

    .EXAMPLE
        $string1 = 'This is going to be <c="red">bloody red</c> text! And this is <c="green">green stuff</c> for extra color'
        $string2 = '<c="red">bloody red</c> text! And this is <c="green">green stuff</c> for extra color'
        $string3 = 'This is going to be <c="red">bloody red</c> text! And this is <c="green">green stuff</c>'
        $string1, $string2, $string3 | Write-HostColor -DefaultColor "Magenta"

        Will print all three lines, respecting the color-codes, but use the color "Magenta" as default color.

    .EXAMPLE
        $stringLong = @"
        Dear <c="red">Sirs</c><c="green"> and</c> <c="blue">Madams</c>,

        it has come to our attention that you are not sufficiently <c="darkblue">awesome!</c>
        Kindly improve your <c="yellow">AP</c> (<c="magenta">awesome-ness points</c>) by at least 50% to maintain you membership in Awesome Inc!

        You have <c="green">27 3/4</c> days time to meet this deadline. <c="darkyellow">After this we will unfortunately be forced to rend you assunder and sacrifice your remains to the devil</c>.

        Best regards,
        <c="red">Luzifer</c>
        "@
        Write-HostColor -String $stringLong

        Will print a long multiline text in its entirety while still respecting the colorcodes
#>
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingWriteHost", "")]
    [CmdletBinding()]
    Param (
        [Parameter(ValueFromPipeline = $true)]
        [string[]]
        $String,

        [ConsoleColor]
        $DefaultColor = (Get-DbaConfigValue -Name "message.infocolor")
    )
    process {
        foreach ($line in $String) {
            foreach ($row in $line.Split("`n").Split([environment]::NewLine)) {
                if ($row -notlike '*<c=["'']*["'']>*</c>*') { Write-Host -Object $row -ForegroundColor $DefaultColor }
                else {
                    $match = ($row | Select-String '<c=["''](.*?)["'']>(.*?)</c>' -AllMatches).Matches
                    $index = 0
                    $count = 0

                    while ($count -le $match.Count) {
                        if ($count -lt $Match.Count) {
                            Write-Host -Object $row.SubString($index, ($match[$count].Index - $Index)) -ForegroundColor $DefaultColor -NoNewline
                            try { Write-Host -Object $match[$count].Groups[2].Value -ForegroundColor $match[$count].Groups[1].Value -NoNewline -ErrorAction Stop }
                            catch { Write-Host -Object $match[$count].Groups[2].Value -ForegroundColor $DefaultColor -NoNewline -ErrorAction Stop }

                            $index = $match[$count].Index + $match[$count].Length
                            $count++
                        }
                        else {
                            Write-Host -Object $row.SubString($index) -ForegroundColor $DefaultColor
                            $count++
                        }
                    }
                }
            }
        }
    }
}

# SIG # Begin signature block
# MIIcYgYJKoZIhvcNAQcCoIIcUzCCHE8CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUp4fg3v2QNpi2WP6w02Eh4rxu
# bAaggheRMIIFGjCCBAKgAwIBAgIQAsF1KHTVwoQxhSrYoGRpyjANBgkqhkiG9w0B
# AQsFADByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD
# VQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFz
# c3VyZWQgSUQgQ29kZSBTaWduaW5nIENBMB4XDTE3MDUwOTAwMDAwMFoXDTIwMDUx
# MzEyMDAwMFowVzELMAkGA1UEBhMCVVMxETAPBgNVBAgTCFZpcmdpbmlhMQ8wDQYD
# VQQHEwZWaWVubmExETAPBgNVBAoTCGRiYXRvb2xzMREwDwYDVQQDEwhkYmF0b29s
# czCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAI8ng7JxnekL0AO4qQgt
# Kr6p3q3SNOPh+SUZH+SyY8EA2I3wR7BMoT7rnZNolTwGjUXn7bRC6vISWg16N202
# 1RBWdTGW2rVPBVLF4HA46jle4hcpEVquXdj3yGYa99ko1w2FOWzLjKvtLqj4tzOh
# K7wa/Gbmv0Si/FU6oOmctzYMI0QXtEG7lR1HsJT5kywwmgcjyuiN28iBIhT6man0
# Ib6xKDv40PblKq5c9AFVldXUGVeBJbLhcEAA1nSPSLGdc7j4J2SulGISYY7ocuX3
# tkv01te72Mv2KkqqpfkLEAQjXgtM0hlgwuc8/A4if+I0YtboCMkVQuwBpbR9/6ys
# Z+sCAwEAAaOCAcUwggHBMB8GA1UdIwQYMBaAFFrEuXsqCqOl6nEDwGD5LfZldQ5Y
# MB0GA1UdDgQWBBRcxSkFqeA3vvHU0aq2mVpFRSOdmjAOBgNVHQ8BAf8EBAMCB4Aw
# EwYDVR0lBAwwCgYIKwYBBQUHAwMwdwYDVR0fBHAwbjA1oDOgMYYvaHR0cDovL2Ny
# bDMuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC1jcy1nMS5jcmwwNaAzoDGGL2h0
# dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zaGEyLWFzc3VyZWQtY3MtZzEuY3JsMEwG
# A1UdIARFMEMwNwYJYIZIAYb9bAMBMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3
# LmRpZ2ljZXJ0LmNvbS9DUFMwCAYGZ4EMAQQBMIGEBggrBgEFBQcBAQR4MHYwJAYI
# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBOBggrBgEFBQcwAoZC
# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0U0hBMkFzc3VyZWRJ
# RENvZGVTaWduaW5nQ0EuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQAD
# ggEBANuBGTbzCRhgG0Th09J0m/qDqohWMx6ZOFKhMoKl8f/l6IwyDrkG48JBkWOA
# QYXNAzvp3Ro7aGCNJKRAOcIjNKYef/PFRfFQvMe07nQIj78G8x0q44ZpOVCp9uVj
# sLmIvsmF1dcYhOWs9BOG/Zp9augJUtlYpo4JW+iuZHCqjhKzIc74rEEiZd0hSm8M
# asshvBUSB9e8do/7RhaKezvlciDaFBQvg5s0fICsEhULBRhoyVOiUKUcemprPiTD
# xh3buBLuN0bBayjWmOMlkG1Z6i8DUvWlPGz9jiBT3ONBqxXfghXLL6n8PhfppBhn
# daPQO8+SqF5rqrlyBPmRRaTz2GQwggUwMIIEGKADAgECAhAECRgbX9W7ZnVTQ7Vv
# lVAIMA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdp
# Q2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0Rp
# Z2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0xMzEwMjIxMjAwMDBaFw0yODEw
# MjIxMjAwMDBaMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx
# GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0IFNI
# QTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EwggEiMA0GCSqGSIb3DQEBAQUA
# A4IBDwAwggEKAoIBAQD407Mcfw4Rr2d3B9MLMUkZz9D7RZmxOttE9X/lqJ3bMtdx
# 6nadBS63j/qSQ8Cl+YnUNxnXtqrwnIal2CWsDnkoOn7p0WfTxvspJ8fTeyOU5JEj
# lpB3gvmhhCNmElQzUHSxKCa7JGnCwlLyFGeKiUXULaGj6YgsIJWuHEqHCN8M9eJN
# YBi+qsSyrnAxZjNxPqxwoqvOf+l8y5Kh5TsxHM/q8grkV7tKtel05iv+bMt+dDk2
# DZDv5LVOpKnqagqrhPOsZ061xPeM0SAlI+sIZD5SlsHyDxL0xY4PwaLoLFH3c7y9
# hbFig3NBggfkOItqcyDQD2RzPJ6fpjOp/RnfJZPRAgMBAAGjggHNMIIByTASBgNV
# HRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEF
# BQcDAzB5BggrBgEFBQcBAQRtMGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRp
# Z2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0cDovL2NhY2VydHMuZGlnaWNlcnQu
# Y29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNydDCBgQYDVR0fBHoweDA6oDig
# NoY0aHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9v
# dENBLmNybDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0
# QXNzdXJlZElEUm9vdENBLmNybDBPBgNVHSAESDBGMDgGCmCGSAGG/WwAAgQwKjAo
# BggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAKBghghkgB
# hv1sAzAdBgNVHQ4EFgQUWsS5eyoKo6XqcQPAYPkt9mV1DlgwHwYDVR0jBBgwFoAU
# Reuir/SSy4IxLVGLp6chnfNtyA8wDQYJKoZIhvcNAQELBQADggEBAD7sDVoks/Mi
# 0RXILHwlKXaoHV0cLToaxO8wYdd+C2D9wz0PxK+L/e8q3yBVN7Dh9tGSdQ9RtG6l
# jlriXiSBThCk7j9xjmMOE0ut119EefM2FAaK95xGTlz/kLEbBw6RFfu6r7VRwo0k
# riTGxycqoSkoGjpxKAI8LpGjwCUR4pwUR6F6aGivm6dcIFzZcbEMj7uo+MUSaJ/P
# QMtARKUT8OZkDCUIQjKyNookAv4vcn4c10lFluhZHen6dGRrsutmQ9qzsIzV6Q3d
# 9gEgzpkxYz0IGhizgZtPxpMQBvwHgfqL2vmCSfdibqFT+hKUGIUukpHqaGxEMrJm
# oecYpJpkUe8wggZqMIIFUqADAgECAhADAZoCOv9YsWvW1ermF/BmMA0GCSqGSIb3
# DQEBBQUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAX
# BgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3Vy
# ZWQgSUQgQ0EtMTAeFw0xNDEwMjIwMDAwMDBaFw0yNDEwMjIwMDAwMDBaMEcxCzAJ
# BgNVBAYTAlVTMREwDwYDVQQKEwhEaWdpQ2VydDElMCMGA1UEAxMcRGlnaUNlcnQg
# VGltZXN0YW1wIFJlc3BvbmRlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
# ggEBAKNkXfx8s+CCNeDg9sYq5kl1O8xu4FOpnx9kWeZ8a39rjJ1V+JLjntVaY1sC
# SVDZg85vZu7dy4XpX6X51Id0iEQ7Gcnl9ZGfxhQ5rCTqqEsskYnMXij0ZLZQt/US
# s3OWCmejvmGfrvP9Enh1DqZbFP1FI46GRFV9GIYFjFWHeUhG98oOjafeTl/iqLYt
# WQJhiGFyGGi5uHzu5uc0LzF3gTAfuzYBje8n4/ea8EwxZI3j6/oZh6h+z+yMDDZb
# esF6uHjHyQYuRhDIjegEYNu8c3T6Ttj+qkDxss5wRoPp2kChWTrZFQlXmVYwk/PJ
# YczQCMxr7GJCkawCwO+k8IkRj3cCAwEAAaOCAzUwggMxMA4GA1UdDwEB/wQEAwIH
# gDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMIIBvwYDVR0g
# BIIBtjCCAbIwggGhBglghkgBhv1sBwEwggGSMCgGCCsGAQUFBwIBFhxodHRwczov
# L3d3dy5kaWdpY2VydC5jb20vQ1BTMIIBZAYIKwYBBQUHAgIwggFWHoIBUgBBAG4A
# eQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQByAHQAaQBmAGkAYwBhAHQA
# ZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBjAGUAcAB0AGEAbgBjAGUA
# IABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAgAEMAUAAvAEMAUABTACAA
# YQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQAGEAcgB0AHkAIABBAGcA
# cgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBtAGkAdAAgAGwAaQBhAGIA
# aQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBjAG8AcgBwAG8AcgBhAHQA
# ZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBlAHIAZQBuAGMAZQAuMAsG
# CWCGSAGG/WwDFTAfBgNVHSMEGDAWgBQVABIrE5iymQftHt+ivlcNK2cCzTAdBgNV
# HQ4EFgQUYVpNJLZJMp1KKnkag0v0HonByn0wfQYDVR0fBHYwdDA4oDagNIYyaHR0
# cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEQ0EtMS5jcmww
# OKA2oDSGMmh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJ
# RENBLTEuY3JsMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29j
# c3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdp
# Y2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURDQS0xLmNydDANBgkqhkiG9w0BAQUF
# AAOCAQEAnSV+GzNNsiaBXJuGziMgD4CH5Yj//7HUaiwx7ToXGXEXzakbvFoWOQCd
# 42yE5FpA+94GAYw3+puxnSR+/iCkV61bt5qwYCbqaVchXTQvH3Gwg5QZBWs1kBCg
# e5fH9j/n4hFBpr1i2fAnPTgdKG86Ugnw7HBi02JLsOBzppLA044x2C/jbRcTBu7k
# A7YUq/OPQ6dxnSHdFMoVXZJB2vkPgdGZdA0mxA5/G7X1oPHGdwYoFenYk+VVFvC7
# Cqsc21xIJ2bIo4sKHOWV2q7ELlmgYd3a822iYemKC23sEhi991VUQAOSK2vCUcIK
# SK+w1G7g9BQKOhvjjz3Kr2qNe9zYRDCCBs0wggW1oAMCAQICEAb9+QOWA63qAArr
# Pye7uhswDQYJKoZIhvcNAQEFBQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERp
# Z2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMb
# RGlnaUNlcnQgQXNzdXJlZCBJRCBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTIx
# MTExMDAwMDAwMFowYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IElu
# YzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQg
# QXNzdXJlZCBJRCBDQS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
# 6IItmfnKwkKVpYBzQHDSnlZUXKnE0kEGj8kz/E1FkVyBn+0snPgWWd+etSQVwpi5
# tHdJ3InECtqvy15r7a2wcTHrzzpADEZNk+yLejYIA6sMNP4YSYL+x8cxSIB8HqIP
# kg5QycaH6zY/2DDD/6b3+6LNb3Mj/qxWBZDwMiEWicZwiPkFl32jx0PdAug7Pe2x
# QaPtP77blUjE7h6z8rwMK5nQxl0SQoHhg26Ccz8mSxSQrllmCsSNvtLOBq6thG9I
# hJtPQLnxTPKvmPv2zkBdXPao8S+v7Iki8msYZbHBc63X8djPHgp0XEK4aH631XcK
# J1Z8D2KkPzIUYJX9BwSiCQIDAQABo4IDejCCA3YwDgYDVR0PAQH/BAQDAgGGMDsG
# A1UdJQQ0MDIGCCsGAQUFBwMBBggrBgEFBQcDAgYIKwYBBQUHAwMGCCsGAQUFBwME
# BggrBgEFBQcDCDCCAdIGA1UdIASCAckwggHFMIIBtAYKYIZIAYb9bAABBDCCAaQw
# OgYIKwYBBQUHAgEWLmh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVw
# b3NpdG9yeS5odG0wggFkBggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUA
# IABvAGYAIAB0AGgAaQBzACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4A
# cwB0AGkAdAB1AHQAZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQA
# aABlACAARABpAGcAaQBDAGUAcgB0ACAAQwBQAC8AQwBQAFMAIABhAG4AZAAgAHQA
# aABlACAAUgBlAGwAeQBpAG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUA
# bgB0ACAAdwBoAGkAYwBoACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkA
# IABhAG4AZAAgAGEAcgBlACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUA
# cgBlAGkAbgAgAGIAeQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4wCwYJYIZIAYb9bAMV
# MBIGA1UdEwEB/wQIMAYBAf8CAQAweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzAB
# hhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9j
# YWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQw
# gYEGA1UdHwR6MHgwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdp
# Q2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwOqA4oDaGNGh0dHA6Ly9jcmw0LmRpZ2lj
# ZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwHQYDVR0OBBYEFBUA
# EisTmLKZB+0e36K+Vw0rZwLNMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3z
# bcgPMA0GCSqGSIb3DQEBBQUAA4IBAQBGUD7Jtygkpzgdtlspr1LPUukxR6tWXHvV
# DQtBs+/sdR90OPKyXGGinJXDUOSCuSPRujqGcq04eKx1XRcXNHJHhZRW0eu7NoR3
# zCSl8wQZVann4+erYs37iy2QwsDStZS9Xk+xBdIOPRqpFFumhjFiqKgz5Js5p8T1
# zh14dpQlc+Qqq8+cdkvtX8JLFuRLcEwAiR78xXm8TBJX/l/hHrwCXaj++wc4Tw3G
# XZG5D2dFzdaD7eeSDY2xaYxP+1ngIw/Sqq4AfO6cQg7PkdcntxbuD8O9fAqg7iwI
# VYUiuOsYGk38KiGtSTGDR5V3cdyxG0tLHBCcdxTBnU8vWpUIKRAmMYIEOzCCBDcC
# AQEwgYYwcjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcG
# A1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBB
# c3N1cmVkIElEIENvZGUgU2lnbmluZyBDQQIQAsF1KHTVwoQxhSrYoGRpyjAJBgUr
# DgMCGgUAoHgwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMx
# DAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkq
# hkiG9w0BCQQxFgQUB1Wyrt0RMDa93igDCM6ESrh5bI4wDQYJKoZIhvcNAQEBBQAE
# ggEAPmZUArToLntLKcDE/1SRMETaApHoPpoAn4srxC/nubUmt0Eb0dpSjjlhFvU3
# USw6rMgeQaRF/gcxLIg0Ms2LKR9DTVmqscJWoKvacudxVnle5BReY6Ei7wko8kNR
# 9Qs4xJu6xbwJ1p6W87ilPJjs7hPgJwRK8v2nE429DAwUMlxaQZmpIZpvM6z8NVT6
# V9+Q7awFFrXZBMssQ8TpAfqKkIegfZHk+96pEK+O1mUdvd8gMzShvFhGWk0Kh1nb
# ZH5n5NLodHncK01j55TBDJDi2S+FPoosckVg5LghBqXHKB/V6ArZoIlP6XthAQhe
# fMmL3F1tmPWIvoPTL7UZc+UYzKGCAg8wggILBgkqhkiG9w0BCQYxggH8MIIB+AIB
# ATB2MGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNV
# BAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3VyZWQg
# SUQgQ0EtMQIQAwGaAjr/WLFr1tXq5hfwZjAJBgUrDgMCGgUAoF0wGAYJKoZIhvcN
# AQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTgwNzIzMTk1NzIxWjAj
# BgkqhkiG9w0BCQQxFgQUeu23aUPzsAaiWVSBhb2bx9BtxKcwDQYJKoZIhvcNAQEB
# BQAEggEAPT7qD3H4SPp+9NMQbjT/X7oYlQFUOndjt2Tj1xv92/IyPPMTpJkNIOyF
# FNIyj6+fuxwKlplzjL6y+EopJp/OsYvgUVAQXBs7THVYYjYa+hntj20SXSxDDdIQ
# 3QZBYl4JchRNsFwg5czrODqLJQH5kJ0djf5BGqWIMASGPTtxDYibMsmUAD2igUqH
# nHYDZqOTEBJweboVsoBQY2F9smtOse4FBq/AV4m/g+Y6jDNYLSy3NQpAKgc1QNdw
# E6DFQURsCN9lEU98omW+Djd18OhmHdZxJFDSqYcihFOIBFoAySAHbRDqyV8QhuI3
# CMsRIsRcQqFlRNvgKJYfNUSMZQXyWg==
# SIG # End signature block
tools\dbatools\appveyor.yml
 
tools\dbatools\bin\bcp\bcp.exe
md5: 0056F153E8574FD9C429946E4983E962 | sha1: 41E89575060091A52A52EAA1656B93D7DF67EA01 | sha256: D59D296E846CBD83F5E55CB5852CC2AAA9E1DECC8E0A324B7D0964C4CDF7B425 | sha512: 61F6608BA778FB3B5D1FC02F3F8EB2A006E5378ED629A70F095A62F116A4E4FA0C34B3033D618BF371E7EAB0EAC9FAD27E2AD586C7EF527BAAF72558F12BCD43
tools\dbatools\bin\bcp\Resources\1033\bcp.rll
 
tools\dbatools\bin\build-project.ps1
[CmdletBinding()]
param(
    [string] $ProjectPath = "$psscriptroot\projects\dbatools\dbatools.sln",
    [ValidateSet('ps3', 'ps4', 'Release', 'Debug')]
    [string] $MsbuildConfiguration,
    [Parameter(HelpMessage='Target to run instead of build')]
    [string] $MsbuildTarget = 'Build'
)

if ([string]::IsNullOrEmpty($MsbuildConfiguration)) {
    $MsbuildConfiguration = switch ($PSVersionTable.PSVersion.Major) {
        3 { "ps3" }
        4 { "ps4" }
        default { "Release" }
    }
}

function Get-MsBuildPath {
    [CmdletBinding()]
    [OutputType([string])]
    param()
    process{
        $system = [Appdomain]::CurrentDomain.GetAssemblies() | Where-Object FullName -like "System, *"
        $frameworkFolder = "Framework$(if ([intptr]::Size -eq 8) { "64" })"
        $rawPath = "$(Split-Path $system.Location)\..\..\..\..\$($frameworkFolder)\$($system.ImageRuntimeVersion)\msbuild.exe"
        (Resolve-Path $rawPath).Path
    }
}

$start = Get-Date
$msbuild = Get-MsBuildPath

if (-not (Test-Path $msbuild)) {
    throw "msbuild not found, cannot compile library! Check your .NET installation health, then try again. Path checked: $msbuild"
}

$msbuildOptions = ""
if ($env:APPVEYOR -eq 'True') {
    $msbuildOptions = '/logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"'
    $msbuildConfiguration = 'Debug'

    if (-not (Test-Path "C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll")) {
        throw "msbuild logger not found, cannot compile library! Check your .NET installation health, then try again. Path checked: $msbuild"
    }
}

if ( -not (Test-Path $ProjectPath)) {
    throw new-object 'System.IO.FileNotFoundException' 'Could not file project or solution', $ProjectPath
}

Write-Verbose -Message "Building the library with command $msbuild $ProjectPath /p:Configuration=$msbuildConfiguration $msbuildOptions /t:$MsBuildTarget"
& $msbuild $ProjectPath "/p:Configuration=$msbuildConfiguration" $msbuildOptions "/t:$MsBuildTarget"

if ($MsbuildTarget -eq 'Build') {
    try {
        Write-Verbose -Message "Found library, trying to copy & import"
        if ($script:alwaysBuildLibrary) { Move-Item -Path "$PSScriptRoot\dbatools.dll" -Destination $script:DllRoot -Force -ErrorAction Stop }
        else { Copy-Item -Path "$PSScriptRoot\dbatools.dll" -Destination $script:DllRoot -Force -ErrorAction Stop }
        Add-Type -Path "$script:DllRoot\dbatools.dll" -ErrorAction Stop
    }
    catch {
        Write-Verbose -Message "Failed to copy & import, attempting to import straight from the module directory"
        Add-Type -Path "$PSScriptRoot\dbatools.dll" -ErrorAction Stop
    }
    Write-Verbose -Message "Total duration: $((Get-Date) - $start)"
}
tools\dbatools\bin\build\vsts-build.ps1
<#
Write-Host "Current Path : $((Get-Location).Path)"

Write-Host @"
# Listing local variables #
#-------------------------#

"@

Get-Variable | Format-Table Name, @{
    n   = "Type"; e = {
        if ($_.Value -eq $null) { "<Null>" }
        else { $_.Value.GetType().FullName }
    }
}, Value | Out-String | Out-Host

Write-Host @"

# Listing environment variables #
#-------------------------------#

"@

Get-ChildItem "env:" | Out-Host

Write-Host @"

# Listing arguments #
#-------------------#

"@

$args | Format-List | Out-Host

Write-Host "########################################################################################################" -ForegroundColor DarkGreen
#>

$previousVersion = Import-Clixml ".\vsts-version.xml"
$currentVersion = [System.Diagnostics.FileVersionInfo]::GetVersionInfo((Get-Item "bin\dbatools.dll").FullName).FileVersion
Remove-Item ".\vsts-version.xml"

if ($previousVersion -ne $currentVersion)
{
    $branch = $env:BUILD_SOURCEBRANCHNAME
    Write-Host "Previous: $previousVersion | Current: $currentVersion | Library should be updated"

    git add .
    git commit -m "VSTS Library Compile ***NO_CI***"
    $errorMessage = git push "https://$env:[email protected]/sqlcollaborative/dbatools.git" head:$branch 2>&1
    if ($LASTEXITCODE -gt 0) { throw $errorMessage }
}
else
{
    Write-Host "Version: $currentVersion | Library is up to date"
}
tools\dbatools\bin\build\vsts-prebuild.ps1
$item = Get-Item "bin\dbatools.dll"
$version = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($item.FullName).FileVersion
$version | Export-Clixml ".\vsts-version.xml"
tools\dbatools\bin\dbatools-buildref-index.json
{
    "LastUpdated": "2018-07-20T00:00:00",
    "Data": [
    {
        "Version":  "8.0.47",
        "Name": "2000"
    },
    {
        "Version":  "8.0.78"
    },
    {
        "Version":  "8.0.100"
    },
    {
        "Version":  "8.0.190"
    },
    {
        "SP":  "RTM",
        "Version":  "8.0.194",
        "SupportedUntil":  "2000-11-30T00:00:00"
    },
    {
        "Version":  "8.0.204",
        "KBList":  "274329"
    },
    {
        "Version":  "8.0.205",
        "KBList":  "274330"
    },
    {
        "Version":  "8.0.210",
        "KBList":  "275900"
    },
    {
        "Version":  "8.0.211",
        "KBList":  "276329"
    },
    {
        "Version":  "8.0.217",
        "KBList":  "279293"
    },
    {
        "Version":  "8.0.218",
        "KBList":  "279183"
    },
    {
        "Version":  "8.0.222",
        "KBList":  "281769"
    },
    {
        "Version":  "8.0.223",
        "KBList":  "280380"
    },
    {
        "Version":  "8.0.225",
        "KBList":  "281663"
    },
    {
        "Version":  "8.0.226",
        "KBList":  "278239"
    },
    {
        "Version":  "8.0.231",
        "KBList":  "282279"
    },
    {
        "Version":  "8.0.233",
        "KBList":  "282416"
    },
    {
        "Version":  "8.0.239",
        "KBList":  "285290"
    },
    {
        "Version":  "8.0.249",
        "KBList":  "288122"
    },
    {
        "Version":  "8.0.250",
        "KBList":  "291683"
    },
    {
        "Version":  "8.0.251",
        "KBList":  "300194"
    },
    {
        "Version":  "8.0.287",
        "KBList":  "297209"
    },
    {
        "Version":  "8.0.296",
        "KBList":  "299717"
    },
    {
        "SP":  "SP1",
        "Version":  "8.0.384",
        "SupportedUntil":  "2002-02-28T00:00:00",
        "KBList":  "306908"
    },
    {
        "Version":  "8.0.428",
        "KBList":  "304850"
    },
    {
        "Version":  "8.0.443",
        "KBList":  "307538"
    },
    {
        "Version":  "8.0.444",
        "KBList":  [
                       "307655",
                       "30754"
                   ]
    },
    {
        "Version":  "8.0.452",
        "KBList":  "308547"
    },
    {
        "Version":  "8.0.469",
        "KBList":  "313005"
    },
    {
        "Version":  "8.0.471",
        "KBList":  "313302"
    },
    {
        "Version":  "8.0.473",
        "KBList":  "314003"
    },
    {
        "Version":  "8.0.474",
        "KBList":  "315395"
    },
    {
        "Version":  "8.0.475"
    },
    {
        "SP":  "SP2",
        "Version":  "8.0.532",
        "SupportedUntil":  "2003-04-07T00:00:00",
        "KBList":  "306908"
    },
    {
        "Version":  "8.0.534",
        "KBList":  "306908"
    },
    {
        "Version":  "8.0.552",
        "KBList":  [
                       "313002",
                       "313005"
                   ]
    },
    {
        "Version":  "8.0.558",
        "KBList":  [
                       "314003",
                       "315395"
                   ]
    },
    {
        "Version":  "8.0.561"
    },
    {
        "Version":  "8.0.568",
        "KBList":  "317748"
    },
    {
        "Version":  "8.0.578",
        "KBList":  [
                       "318405",
                       "317979"
                   ]
    },
    {
        "Version":  "8.0.584",
        "KBList":  "318530"
    },
    {
        "Version":  "8.0.594",
        "KBList":  [
                       "356774",
                       "319477"
                   ]
    },
    {
        "Version":  "8.0.599",
        "KBList":  "319869"
    },
    {
        "Version":  "8.0.604"
    },
    {
        "Version":  "8.0.608",
        "KBList":  "319507"
    },
    {
        "Version":  "8.0.644",
        "KBList":  "324186"
    },
    {
        "Version":  "8.0.650",
        "KBList":  "322853"
    },
    {
        "Version":  "8.0.652",
        "KBList":  "810010"
    },
    {
        "Version":  "8.0.655"
    },
    {
        "Version":  "8.0.661",
        "KBList":  "326999"
    },
    {
        "Version":  "8.0.665"
    },
    {
        "Version":  "8.0.667"
    },
    {
        "Version":  "8.0.678",
        "KBList":  "328354"
    },
    {
        "Version":  "8.0.679",
        "KBList":  "316333"
    },
    {
        "Version":  "8.0.682",
        "KBList":  "319851"
    },
    {
        "Version":  "8.0.686",
        "KBList":  "316333"
    },
    {
        "Version":  "8.0.688",
        "KBList":  "329487"
    },
    {
        "Version":  "8.0.689",
        "KBList":  "329499"
    },
    {
        "Version":  "8.0.690",
        "KBList":  "311104"
    },
    {
        "Version":  "8.0.693",
        "KBList":  "330212"
    },
    {
        "Version":  "8.0.695",
        "KBList":  [
                       "331965",
                       "331855"
                   ]
    },
    {
        "Version":  "8.0.696",
        "KBList":  [
                       "810052",
                       "810072"
                   ]
    },
    {
        "Version":  "8.0.700",
        "KBList":  "810072"
    },
    {
        "Version":  "8.0.701",
        "KBList":  [
                       "810163",
                       "810026"
                   ]
    },
    {
        "Version":  "8.0.702",
        "KBList":  "328551"
    },
    {
        "Version":  "8.0.703",
        "KBList":  "810526"
    },
    {
        "Version":  "8.0.705",
        "KBList":  "810920"
    },
    {
        "Version":  "8.0.710",
        "KBList":  "811052"
    },
    {
        "Version":  "8.0.713",
        "KBList":  "811205"
    },
    {
        "Version":  "8.0.714",
        "KBList":  "811478"
    },
    {
        "Version":  "8.0.715",
        "KBList":  [
                       "810688",
                       "811611"
                   ]
    },
    {
        "Version":  "8.0.718",
        "KBList":  "811703"
    },
    {
        "Version":  "8.0.721",
        "KBList":  [
                       "812393",
                       "81225"
                   ]
    },
    {
        "Version":  "8.0.723",
        "KBList":  "812798"
    },
    {
        "Version":  "8.0.725",
        "KBList":  [
                       "813494",
                       "812995"
                   ]
    },
    {
        "Version":  "8.0.728",
        "KBList":  "814460"
    },
    {
        "Version":  "8.0.730",
        "KBList":  "813769"
    },
    {
        "Version":  "8.0.733",
        "KBList":  "813759"
    },
    {
        "Version":  "8.0.735",
        "KBList":  "814889"
    },
    {
        "Version":  "8.0.736",
        "KBList":  "816937"
    },
    {
        "Version":  "8.0.741",
        "KBList":  "818096"
    },
    {
        "Version":  "8.0.743",
        "KBList":  [
                       "818406",
                       "818763"
                   ]
    },
    {
        "SP":  "SP3",
        "Version":  "8.0.760",
        "SupportedUntil":  "2007-07-10T00:00:00",
        "KBList":  "306908"
    },
    {
        "Version":  "8.0.762",
        "KBList":  "814032"
    },
    {
        "Version":  "8.0.763",
        "KBList":  "814113"
    },
    {
        "Version":  "8.0.765",
        "KBList":  [
                       "811611",
                       "810688",
                       "810163",
                       "813769"
                   ]
    },
    {
        "Version":  "8.0.769",
        "KBList":  [
                       "814892",
                       "814889"
                   ]
    },
    {
        "Version":  "8.0.775",
        "KBList":  "815115"
    },
    {
        "Version":  "8.0.779",
        "KBList":  "814035"
    },
    {
        "Version":  "8.0.780",
        "KBList":  [
                       "816084",
                       "816039",
                       "801185"
                   ]
    },
    {
        "Version":  "8.0.781",
        "KBList":  "815057"
    },
    {
        "Version":  "8.0.788",
        "KBList":  "816985"
    },
    {
        "Version":  "8.0.789",
        "KBList":  "816840"
    },
    {
        "Version":  "8.0.790",
        "KBList":  "817081"
    },
    {
        "Version":  "8.0.791",
        "KBList":  "815249"
    },
    {
        "Version":  "8.0.794",
        "KBList":  [
                       "817709",
                       "816440",
                       "813524",
                       "817464"
                   ]
    },
    {
        "Version":  "8.0.798",
        "KBList":  "817464"
    },
    {
        "Version":  "8.0.800",
        "KBList":  [
                       "818188",
                       "818097"
                   ]
    },
    {
        "Version":  "8.0.801",
        "KBList":  "181540"
    },
    {
        "Version":  "8.0.804",
        "KBList":  "818729"
    },
    {
        "Version":  "8.0.807",
        "KBList":  "818899"
    },
    {
        "Version":  "8.0.811",
        "KBList":  [
                       "891662",
                       "819248",
                       "818897"
                   ]
    },
    {
        "Version":  "8.0.814",
        "KBList":  "819662"
    },
    {
        "Version":  "8.0.816",
        "KBList":  "818766"
    },
    {
        "Version":  "8.0.818",
        "KBList":  [
                       "821280",
                       "826161",
                       "821277"
                   ]
    },
    {
        "Version":  "8.0.819",
        "KBList":  "826161"
    },
    {
        "Version":  "8.0.837",
        "KBList":  [
                       "823514",
                       "821740",
                       "821548",
                       "821741",
                       "820788"
                   ]
    },
    {
        "Version":  "8.0.839",
        "KBList":  [
                       "824027",
                       "823877"
                   ]
    },
    {
        "Version":  "8.0.840",
        "KBList":  "319477"
    },
    {
        "Version":  "8.0.841",
        "KBList":  "825225"
    },
    {
        "Version":  "8.0.842",
        "KBList":  "825043"
    },
    {
        "Version":  "8.0.844",
        "KBList":  "826080"
    },
    {
        "Version":  "8.0.845",
        "KBList":  [
                       "825854",
                       "826364"
                   ]
    },
    {
        "Version":  "8.0.847",
        "KBList":  "826433"
    },
    {
        "Version":  "8.0.848",
        "KBList":  "826822"
    },
    {
        "Version":  "8.0.850",
        "KBList":  [
                       "826906",
                       "826815",
                       "826860"
                   ]
    },
    {
        "Version":  "8.0.851",
        "KBList":  "826754"
    },
    {
        "Version":  "8.0.852",
        "KBList":  [
                       "827954",
                       "830466"
                   ]
    },
    {
        "Version":  "8.0.854",
        "KBList":  "828699"
    },
    {
        "Version":  "8.0.856",
        "KBList":  "828096"
    },
    {
        "Version":  "8.0.857",
        "KBList":  [
                       "828308",
                       "827714",
                       "828017"
                   ]
    },
    {
        "Version":  "8.0.858",
        "KBList":  "828637"
    },
    {
        "Version":  "8.0.859",
        "KBList":  "821334"
    },
    {
        "Version":  "8.0.863",
        "KBList":  [
                       "829444",
                       "829205"
                   ]
    },
    {
        "Version":  "8.0.865",
        "KBList":  [
                       "828945",
                       "830395"
                   ]
    },
    {
        "Version":  "8.0.866",
        "KBList":  "830366"
    },
    {
        "Version":  "8.0.869",
        "KBList":  "830588"
    },
    {
        "Version":  "8.0.870",
        "KBList":  "830262"
    },
    {
        "Version":  "8.0.871",
        "KBList":  [
                       "830860",
                       "830767"
                   ]
    },
    {
        "Version":  "8.0.873",
        "KBList":  "830887"
    },
    {
        "Version":  "8.0.876",
        "KBList":  [
                       "831997",
                       "830912"
                   ]
    },
    {
        "Version":  "8.0.878",
        "KBList":  "831950"
    },
    {
        "Version":  "8.0.879",
        "KBList":  "832977"
    },
    {
        "Version":  "8.0.891",
        "KBList":  "836141"
    },
    {
        "Version":  "8.0.892",
        "KBList":  "833710"
    },
    {
        "Version":  "8.0.904",
        "KBList":  "834453"
    },
    {
        "Version":  "8.0.908",
        "KBList":  "834290"
    },
    {
        "Version":  "8.0.910",
        "KBList":  "834798"
    },
    {
        "Version":  "8.0.911",
        "KBList":  "837957"
    },
    {
        "Version":  "8.0.913",
        "KBList":  "836651"
    },
    {
        "Version":  "8.0.915",
        "KBList":  "837401"
    },
    {
        "Version":  "8.0.916",
        "KBList":  "317989"
    },
    {
        "Version":  "8.0.919",
        "KBList":  "837957"
    },
    {
        "Version":  "8.0.922",
        "KBList":  "837970"
    },
    {
        "Version":  "8.0.923",
        "KBList":  "838460"
    },
    {
        "Version":  "8.0.926",
        "KBList":  "839523"
    },
    {
        "Version":  "8.0.927",
        "KBList":  "839688"
    },
    {
        "Version":  "8.0.928",
        "KBList":  "839589"
    },
    {
        "Version":  "8.0.929",
        "KBList":  "839529"
    },
    {
        "Version":  "8.0.933",
        "KBList":  "840856"
    },
    {
        "Version":  "8.0.934",
        "KBList":  "841404"
    },
    {
        "Version":  "8.0.935",
        "KBList":  "841404"
    },
    {
        "Version":  "8.0.936",
        "KBList":  "841627"
    },
    {
        "Version":  "8.0.937",
        "KBList":  "841776"
    },
    {
        "Version":  "8.0.944",
        "KBList":  "839280"
    },
    {
        "Version":  "8.0.948",
        "KBList":  "843263"
    },
    {
        "Version":  "8.0.949",
        "KBList":  "843266"
    },
    {
        "Version":  "8.0.952",
        "KBList":  [
                       "867880",
                       "867879",
                       "867878"
                   ]
    },
    {
        "Version":  "8.0.954",
        "KBList":  "843282"
    },
    {
        "Version":  "8.0.955",
        "KBList":  "867798"
    },
    {
        "Version":  "8.0.957",
        "KBList":  "870994"
    },
    {
        "Version":  "8.0.959",
        "KBList":  "878500"
    },
    {
        "Version":  "8.0.961",
        "KBList":  "873446"
    },
    {
        "Version":  "8.0.962",
        "KBList":  "883415"
    },
    {
        "Version":  "8.0.967",
        "KBList":  "878501"
    },
    {
        "Version":  "8.0.970",
        "KBList":  "872842"
    },
    {
        "Version":  "8.0.972",
        "KBList":  "885290"
    },
    {
        "Version":  "8.0.973",
        "KBList":  "884554"
    },
    {
        "Version":  "8.0.977",
        "KBList":  "888007"
    },
    {
        "Version":  "8.0.980",
        "KBList":  "887974"
    },
    {
        "Version":  "8.0.985",
        "KBList":  "889239"
    },
    {
        "Version":  "8.0.988",
        "KBList":  "889166"
    },
    {
        "Version":  "8.0.990",
        "KBList":  "890200"
    },
    {
        "Version":  "8.0.991",
        "KBList":  "889314"
    },
    {
        "Version":  "8.0.993",
        "KBList":  [
                       "890742",
                       "888444",
                       "890925"
                   ]
    },
    {
        "Version":  "8.0.994",
        "KBList":  [
                       "890767",
                       "890768",
                       "890942"
                   ]
    },
    {
        "Version":  "8.0.996",
        "KBList":  [
                       "891268",
                       "891017"
                   ]
    },
    {
        "Version":  "8.0.997",
        "KBList":  "891311"
    },
    {
        "Version":  "8.0.1000",
        "KBList":  "891585"
    },
    {
        "Version":  "8.0.1001",
        "KBList":  "892205"
    },
    {
        "Version":  "8.0.1003",
        "KBList":  "892923"
    },
    {
        "Version":  "8.0.1007",
        "KBList":  "893312"
    },
    {
        "Version":  "8.0.1009",
        "KBList":  "894257"
    },
    {
        "Version":  "8.0.1013",
        "KBList":  "891866"
    },
    {
        "Version":  "8.0.1014",
        "KBList":  [
                       "895187",
                       "895123"
                   ]
    },
    {
        "Version":  "8.0.1017",
        "KBList":  "896425"
    },
    {
        "Version":  "8.0.1019",
        "KBList":  "897572"
    },
    {
        "Version":  "8.0.1020",
        "KBList":  "896985"
    },
    {
        "Version":  "8.0.1021",
        "KBList":  "887700"
    },
    {
        "Version":  "8.0.1024",
        "KBList":  "898709"
    },
    {
        "Version":  "8.0.1025",
        "KBList":  [
                       "899430",
                       "899428"
                   ]
    },
    {
        "Version":  "8.0.1027",
        "KBList":  "900416"
    },
    {
        "Version":  "8.0.1029",
        "KBList":  "902852"
    },
    {
        "Version":  "8.0.1034",
        "KBList":  "915328"
    },
    {
        "Version":  "8.0.1035",
        "KBList":  "917593"
    },
    {
        "Version":  "8.0.1036",
        "KBList":  "929410"
    },
    {
        "Version":  "8.0.1037",
        "KBList":  "930484"
    },
    {
        "Version":  "8.0.1077",
        "KBList":  "983814"
    },
    {
        "Version":  "8.0.1547",
        "KBList":  "899410"
    },
    {
        "Version":  "8.0.2026"
    },
    {
        "SP":  [
                   "SP4",
                   "LATEST"
               ],
        "Version":  "8.0.2039",
        "SupportedUntil":  "2013-04-09T00:00:00",
        "KBList":  "306908"
    },
    {
        "Version":  "8.0.2040",
        "KBList":  "899761"
    },
    {
        "Version":  "8.0.2050",
        "KBList":  [
                       "941203",
                       "94811"
                   ]
    },
    {
        "Version":  "8.0.2055",
        "KBList":  "959240"
    },
    {
        "Version":  "8.0.2145",
        "KBList":  [
                       "826906",
                       "836651"
                   ]
    },
    {
        "Version":  "8.0.2147",
        "KBList":  "899410"
    },
    {
        "Version":  "8.0.2148",
        "KBList":  [
                       "899430",
                       "899431",
                       "900390",
                       "900404",
                       "901212",
                       "902150",
                       "902955"
                   ]
    },
    {
        "Version":  "8.0.2151",
        "KBList":  [
                       "903742",
                       "904244"
                   ]
    },
    {
        "Version":  "8.0.2156"
    },
    {
        "Version":  "8.0.2159",
        "KBList":  "907250"
    },
    {
        "Version":  "8.0.2162",
        "KBList":  "904660"
    },
    {
        "Version":  "8.0.2166",
        "KBList":  "909734"
    },
    {
        "Version":  "8.0.2168",
        "KBList":  "907813"
    },
    {
        "Version":  "8.0.2171",
        "KBList":  "909369"
    },
    {
        "Version":  "8.0.2172",
        "KBList":  "910707"
    },
    {
        "Version":  "8.0.2175",
        "KBList":  "911678"
    },
    {
        "Version":  "8.0.2180",
        "KBList":  [
                       "913684",
                       "913789"
                   ]
    },
    {
        "Version":  "8.0.2187",
        "KBList":  [
                       "916287",
                       "914384",
                       "898709",
                       "915065",
                       "915340"
                   ]
    },
    {
        "Version":  "8.0.2189",
        "KBList":  [
                       "916652",
                       "913438"
                   ]
    },
    {
        "Version":  "8.0.2191",
        "KBList":  [
                       "826906",
                       "836651"
                   ]
    },
    {
        "Version":  "8.0.2192",
        "KBList":  "917606"
    },
    {
        "Version":  "8.0.2194",
        "KBList":  [
                       "917972",
                       "917565"
                   ]
    },
    {
        "Version":  "8.0.2196",
        "KBList":  "919165"
    },
    {
        "Version":  "8.0.2197",
        "KBList":  [
                       "919133",
                       "919068",
                       "919399"
                   ]
    },
    {
        "Version":  "8.0.2199",
        "KBList":  "919221"
    },
    {
        "Version":  "8.0.2201",
        "KBList":  "920930"
    },
    {
        "Version":  "8.0.2207",
        "KBList":  "923344"
    },
    {
        "Version":  "8.0.2209",
        "KBList":  "923797"
    },
    {
        "Version":  "8.0.2215",
        "KBList":  [
                       "924662",
                       "923563",
                       "923796"
                   ]
    },
    {
        "Version":  "8.0.2217",
        "KBList":  "924664"
    },
    {
        "Version":  "8.0.2218",
        "KBList":  "925297"
    },
    {
        "Version":  "8.0.2223",
        "KBList":  [
                       "925678",
                       "925419"
                   ]
    },
    {
        "Version":  "8.0.2226",
        "KBList":  [
                       "925684",
                       "925732"
                   ]
    },
    {
        "Version":  "8.0.2229",
        "KBList":  "927186"
    },
    {
        "Version":  "8.0.2231",
        "KBList":  "928079"
    },
    {
        "Version":  "8.0.2232",
        "KBList":  "928568"
    },
    {
        "Version":  "8.0.2234",
        "KBList":  [
                       "929440",
                       "929131"
                   ]
    },
    {
        "Version":  "8.0.2236",
        "KBList":  "930484"
    },
    {
        "Version":  "8.0.2238",
        "KBList":  "931932"
    },
    {
        "Version":  "8.0.2242",
        "KBList":  "929131"
    },
    {
        "Version":  "8.0.2244",
        "KBList":  "934203"
    },
    {
        "Version":  "8.0.2245",
        "KBList":  "933573"
    },
    {
        "Version":  "8.0.2246",
        "KBList":  "935465"
    },
    {
        "Version":  "8.0.2248",
        "KBList":  "935950"
    },
    {
        "Version":  "8.0.2249",
        "KBList":  "936232"
    },
    {
        "Version":  "8.0.2253",
        "KBList":  "939317"
    },
    {
        "Version":  "8.0.2265",
        "KBList":  "944985"
    },
    {
        "Version":  "8.0.2271",
        "KBList":  "946584"
    },
    {
        "Version":  "8.0.2273",
        "KBList":  [
                       "941203",
                       "948111"
                   ]
    },
    {
        "Version":  "8.0.2279",
        "KBList":  "959678"
    },
    {
        "Version":  "8.0.2282",
        "KBList":  "960083"
    },
    {
        "Version":  "8.0.2283",
        "KBList":  "971524"
    },
    {
        "Version":  "8.0.2301",
        "KBList":  "983809"
    },
    {
        "Version":  "8.0.2305",
        "KBList":  "983811"
    },
    {
        "Version":  "9.0.0",
        "Name": "2005"
    },
    {
        "SP":  "RTM",
        "Version":  "9.0.1399",
        "SupportedUntil":  "2007-07-10T00:00:00"
    },
    {
        "Version":  "9.0.1406",
        "KBList":  "932557"
    },
    {
        "Version":  "9.0.1500",
        "KBList":  "910416"
    },
    {
        "Version":  "9.0.1502",
        "KBList":  "915793"
    },
    {
        "Version":  "9.0.1503",
        "KBList":  "911662"
    },
    {
        "Version":  "9.0.1514",
        "KBList":  "912471"
    },
    {
        "Version":  "9.0.1518",
        "KBList":  [
                       "913371",
                       "912472",
                       "913941"
                   ]
    },
    {
        "Version":  "9.0.1519",
        "KBList":  "913494"
    },
    {
        "Version":  "9.0.1528",
        "KBList":  [
                       "915122",
                       "915306",
                       "915307",
                       "915308",
                       "915309"
                   ]
    },
    {
        "Version":  "9.0.1531",
        "KBList":  "915918"
    },
    {
        "Version":  "9.0.1532",
        "KBList":  "916046"
    },
    {
        "Version":  "9.0.1533",
        "KBList":  "916086"
    },
    {
        "Version":  "9.0.1534",
        "KBList":  "916706"
    },
    {
        "Version":  "9.0.1536",
        "KBList":  "917016"
    },
    {
        "Version":  "9.0.1538",
        "KBList":  "917824"
    },
    {
        "Version":  "9.0.1539",
        "KBList":  "917738"
    },
    {
        "Version":  "9.0.1541",
        "KBList":  [
                       "917971",
                       "917888"
                   ]
    },
    {
        "Version":  "9.0.1545",
        "KBList":  "917905"
    },
    {
        "Version":  "9.0.1547",
        "KBList":  "918276"
    },
    {
        "Version":  "9.0.1550",
        "KBList":  [
                       "921106",
                       "917887"
                   ]
    },
    {
        "Version":  "9.0.1551",
        "KBList":  [
                       "922804",
                       "922527"
                   ]
    },
    {
        "Version":  "9.0.1554",
        "KBList":  "926292"
    },
    {
        "Version":  "9.0.1558",
        "KBList":  "926493"
    },
    {
        "Version":  "9.0.1561",
        "KBList":  "932556"
    },
    {
        "Version":  "9.0.2029"
    },
    {
        "Version":  "9.0.2040"
    },
    {
        "SP":  "SP1",
        "Version":  "9.0.2047",
        "SupportedUntil":  "2008-04-08T00:00:00",
        "KBList":  "913090"
    },
    {
        "Version":  "9.0.2050",
        "KBList":  "932555"
    },
    {
        "Version":  "9.0.2153",
        "KBList":  [
                       "919224",
                       "918222"
                   ]
    },
    {
        "Version":  "9.0.2156",
        "KBList":  "919611"
    },
    {
        "Version":  "9.0.2164",
        "KBList":  [
                       "919775",
                       "919636",
                       "918882",
                       "920206",
                       "921003",
                       "919929",
                       "920347",
                       "920346",
                       "919243"
                   ]
    },
    {
        "Version":  "9.0.2167",
        "KBList":  "920974"
    },
    {
        "Version":  "9.0.2174",
        "KBList":  "922063"
    },
    {
        "Version":  "9.0.2175",
        "KBList":  [
                       "921536",
                       "922438",
                       "922578",
                       "917905",
                       "921395"
                   ]
    },
    {
        "Version":  "9.0.2176",
        "KBList":  [
                       "923296",
                       "922594"
                   ]
    },
    {
        "Version":  "9.0.2181",
        "KBList":  [
                       "923634",
                       "923605"
                   ]
    },
    {
        "Version":  "9.0.2187",
        "KBList":  "923849"
    },
    {
        "Version":  "9.0.2189",
        "KBList":  "925153"
    },
    {
        "Version":  "9.0.2190",
        "KBList":  "925227"
    },
    {
        "Version":  "9.0.2191",
        "KBList":  "925135"
    },
    {
        "Version":  "9.0.2192",
        "KBList":  [
                       "925335",
                       "924954"
                   ]
    },
    {
        "Version":  "9.0.2194",
        "KBList":  "925744"
    },
    {
        "Version":  "9.0.2195",
        "KBList":  "926240"
    },
    {
        "Version":  "9.0.2196",
        "KBList":  [
                       "926335",
                       "926285"
                   ]
    },
    {
        "Version":  "9.0.2198",
        "KBList":  [
                       "925277",
                       "924808",
                       "926611",
                       "926773",
                       "926612",
                       "924624",
                       "924807",
                       "926106",
                       "926613"
                   ]
    },
    {
        "Version":  "9.0.2201",
        "KBList":  "927289"
    },
    {
        "Version":  "9.0.2202",
        "KBList":  "927643"
    },
    {
        "Version":  "9.0.2206",
        "KBList":  [
                       "928529",
                       "926493",
                       "928537",
                       "928083"
                   ]
    },
    {
        "Version":  "9.0.2207",
        "KBList":  [
                       "928789",
                       "928732",
                       "928394"
                   ]
    },
    {
        "Version":  "9.0.2208",
        "KBList":  "929179"
    },
    {
        "Version":  "9.0.2209",
        "KBList":  "929278"
    },
    {
        "Version":  "9.0.2211",
        "KBList":  [
                       "930283",
                       "930284"
                   ]
    },
    {
        "Version":  "9.0.2214",
        "KBList":  [
                       "929240",
                       "930505"
                   ]
    },
    {
        "Version":  "9.0.2216",
        "KBList":  "931821"
    },
    {
        "Version":  "9.0.2218",
        "KBList":  "931843"
    },
    {
        "Version":  "9.0.2219",
        "KBList":  "932115"
    },
    {
        "Version":  "9.0.2221",
        "KBList":  "931593"
    },
    {
        "Version":  "9.0.2223",
        "KBList":  "932393"
    },
    {
        "Version":  "9.0.2226",
        "KBList":  [
                       "933762",
                       "934065"
                   ]
    },
    {
        "Version":  "9.0.2227",
        "KBList":  "933265"
    },
    {
        "Version":  "9.0.2229",
        "KBList":  "935446"
    },
    {
        "Version":  "9.0.2230",
        "KBList":  "936179"
    },
    {
        "Version":  "9.0.2231",
        "KBList":  "934812"
    },
    {
        "Version":  "9.0.2232",
        "KBList":  "937277"
    },
    {
        "Version":  "9.0.2233",
        "KBList":  [
                       "937544",
                       "933499",
                       "937545"
                   ]
    },
    {
        "Version":  "9.0.2234",
        "KBList":  "937343"
    },
    {
        "Version":  "9.0.2236",
        "KBList":  [
                       "940286",
                       "940287"
                   ]
    },
    {
        "Version":  "9.0.2237",
        "KBList":  "940719"
    },
    {
        "Version":  "9.0.2239",
        "KBList":  "940961"
    },
    {
        "Version":  "9.0.2242",
        "KBList":  [
                       "943888",
                       "943389"
                   ]
    },
    {
        "Version":  "9.0.2243",
        "KBList":  "944968"
    },
    {
        "Version":  "9.0.2245",
        "KBList":  "933573"
    },
    {
        "Version":  "9.0.2249",
        "KBList":  "948344"
    },
    {
        "Version":  "9.0.3026",
        "KBList":  "929376"
    },
    {
        "Version":  "9.0.3027"
    },
    {
        "Version":  "9.0.3033"
    },
    {
        "SP":  "SP2",
        "Version":  "9.0.3042",
        "SupportedUntil":  "2010-01-12T00:00:00",
        "KBList":  "921896"
    },
    {
        "Version":  "9.0.3043",
        "KBList":  "933508"
    },
    {
        "Version":  "9.0.3050",
        "KBList":  "933508"
    },
    {
        "Version":  "9.0.3054",
        "KBList":  "934458"
    },
    {
        "Version":  "9.0.3068",
        "KBList":  "948109"
    },
    {
        "Version":  "9.0.3073",
        "KBList":  "954606"
    },
    {
        "Version":  "9.0.3077",
        "KBList":  "959420"
    },
    {
        "Version":  "9.0.3080",
        "KBList":  "970895"
    },
    {
        "Version":  "9.0.3152"
    },
    {
        "Version":  "9.0.3153",
        "KBList":  "937137"
    },
    {
        "Version":  "9.0.3154",
        "KBList":  "937137"
    },
    {
        "Version":  "9.0.3155",
        "KBList":  "937137"
    },
    {
        "Version":  "9.0.3156"
    },
    {
        "Version":  "9.0.3159"
    },
    {
        "Version":  "9.0.3161",
        "CU":  "CU1"
    },
    {
        "Version":  "9.0.3162",
        "KBList":  "937137"
    },
    {
        "Version":  "9.0.3166",
        "KBList":  "937137"
    },
    {
        "Version":  "9.0.3169",
        "KBList":  "937137"
    },
    {
        "Version":  "9.0.3171",
        "KBList":  "937745"
    },
    {
        "Version":  "9.0.3175",
        "CU":  "CU2"
    },
    {
        "Version":  "9.0.3177",
        "KBList":  "937137"
    },
    {
        "Version":  "9.0.3178",
        "KBList":  "937137"
    },
    {
        "Version":  "9.0.3179",
        "KBList":  "937137"
    },
    {
        "Version":  "9.0.3180",
        "KBList":  "939942"
    },
    {
        "Version":  "9.0.3182",
        "KBList":  "937137"
    },
    {
        "Version":  "9.0.3186",
        "CU":  "CU3"
    },
    {
        "Version":  "9.0.3194",
        "KBList":  "937137"
    },
    {
        "Version":  "9.0.3195"
    },
    {
        "Version":  "9.0.3200",
        "CU":  "CU4"
    },
    {
        "Version":  "9.0.3203",
        "KBList":  "937137"
    },
    {
        "Version":  "9.0.3205",
        "KBList":  "937137"
    },
    {
        "Version":  "9.0.3206"
    },
    {
        "Version":  "9.0.3208"
    },
    {
        "Version":  "9.0.3209"
    },
    {
        "Version":  "9.0.3215",
        "CU":  "CU5"
    },
    {
        "Version":  "9.0.3221",
        "KBList":  [
                       "942908",
                       "945442",
                       "945443",
                       "945916"
                   ]
    },
    {
        "Version":  "9.0.3222",
        "KBList":  [
                       "945640",
                       "945641",
                       "947196",
                       "947197"
                   ]
    },
    {
        "Version":  "9.0.3224",
        "KBList":  "947463"
    },
    {
        "Version":  "9.0.3228",
        "CU":  "CU6"
    },
    {
        "Version":  "9.0.3230",
        "KBList":  "949199"
    },
    {
        "Version":  "9.0.3231",
        "KBList":  [
                       "949595",
                       "949687"
                   ]
    },
    {
        "Version":  "9.0.3232",
        "KBList":  "949959"
    },
    {
        "Version":  "9.0.3233",
        "KBList":  "948108"
    },
    {
        "Version":  "9.0.3235",
        "KBList":  "950189"
    },
    {
        "Version":  "9.0.3239",
        "CU":  "CU7"
    },
    {
        "Version":  "9.0.3240",
        "KBList":  "951204"
    },
    {
        "Version":  "9.0.3242",
        "KBList":  "951190"
    },
    {
        "Version":  "9.0.3244",
        "KBList":  "952330"
    },
    {
        "Version":  "9.0.3246",
        "KBList":  "952233"
    },
    {
        "CU":  "CU8",
        "Version":  "9.0.3257",
        "KBList":  "951217"
    },
    {
        "Version":  "9.0.3259",
        "KBList":  [
                       "954669",
                       "954831"
                   ]
    },
    {
        "Version":  "9.0.3260",
        "KBList":  "954950"
    },
    {
        "Version":  "9.0.3282",
        "CU":  "CU9"
    },
    {
        "Version":  "9.0.3294",
        "CU":  "CU10"
    },
    {
        "CU":  "CU11",
        "Version":  "9.0.3301",
        "KBList":  "958735"
    },
    {
        "Version":  "9.0.3302",
        "KBList":  [
                       "961479",
                       "961648"
                   ]
    },
    {
        "Version":  "9.0.3303",
        "KBList":  "962290"
    },
    {
        "Version":  "9.0.3310",
        "KBList":  "960090"
    },
    {
        "CU":  "CU12",
        "Version":  "9.0.3315",
        "KBList":  "962970"
    },
    {
        "Version":  "9.0.3318",
        "KBList":  "967199"
    },
    {
        "Version":  "9.0.3320",
        "KBList":  "969142"
    },
    {
        "CU":  "CU13",
        "Version":  "9.0.3325",
        "KBList":  "967908"
    },
    {
        "Version":  "9.0.3327",
        "CU":  "CU14"
    },
    {
        "CU":  "CU15",
        "Version":  "9.0.3328",
        "KBList":  "970278"
    },
    {
        "Version":  "9.0.3353",
        "KBList":  "970896"
    },
    {
        "CU":  "CU16",
        "Version":  "9.0.3355",
        "KBList":  "974647"
    },
    {
        "CU":  "CU17",
        "Version":  "9.0.3356",
        "KBList":  "976952"
    },
    {
        "Version":  "9.0.4028"
    },
    {
        "SP":  "SP3",
        "Version":  "9.0.4035",
        "SupportedUntil":  "2012-01-10T00:00:00"
    },
    {
        "Version":  "9.0.4053",
        "KBList":  "970892"
    },
    {
        "Version":  "9.0.4060",
        "KBList":  "2494113"
    },
    {
        "Version":  "9.0.4207",
        "CU":  "CU1"
    },
    {
        "Version":  "9.0.4211",
        "CU":  "CU2"
    },
    {
        "Version":  "9.0.4216",
        "KBList":  "967101"
    },
    {
        "CU":  "CU3",
        "Version":  "9.0.4220",
        "KBList":  "967909"
    },
    {
        "Version":  "9.0.4224",
        "KBList":  "917606"
    },
    {
        "Version":  "9.0.4226",
        "CU":  "CU4"
    },
    {
        "CU":  "CU5",
        "Version":  "9.0.4230",
        "KBList":  "972511"
    },
    {
        "Version":  "9.0.4262",
        "KBList":  "970894"
    },
    {
        "CU":  "CU6",
        "Version":  "9.0.4266",
        "KBList":  "974648"
    },
    {
        "Version":  "9.0.4268",
        "KBList":  "977151"
    },
    {
        "CU":  "CU7",
        "Version":  "9.0.4273",
        "KBList":  "976951"
    },
    {
        "CU":  "CU8",
        "Version":  "9.0.4285",
        "KBList":  "978915"
    },
    {
        "CU":  "CU9",
        "Version":  "9.0.4294",
        "KBList":  "980176"
    },
    {
        "CU":  "CU10",
        "Version":  "9.0.4305",
        "KBList":  "983329"
    },
    {
        "CU":  "CU11",
        "Version":  "9.0.4309",
        "KBList":  "2258854"
    },
    {
        "CU":  "CU12",
        "Version":  "9.0.4311",
        "KBList":  "2345449"
    },
    {
        "CU":  "CU13",
        "Version":  "9.0.4315",
        "KBList":  "2438344"
    },
    {
        "CU":  "CU14",
        "Version":  "9.0.4317",
        "KBList":  "2489375"
    },
    {
        "CU":  "CU15",
        "Version":  "9.0.4325",
        "KBList":  "2507766"
    },
    {
        "Version":  "9.0.4340",
        "KBList":  "2494112"
    },
    {
        "Version":  "9.0.4912"
    },
    {
        "SP":  [
                   "SP4",
                   "LATEST"
               ],
        "Version":  "9.0.5000",
        "SupportedUntil":  "2016-04-12T00:00:00",
        "KBList":  "2463332"
    },
    {
        "Version":  "9.0.5057",
        "KBList":  "2494120"
    },
    {
        "Version":  "9.0.5069",
        "KBList":  "2716429"
    },
    {
        "CU":  "CU1",
        "Version":  "9.0.5254",
        "KBList":  "2464079"
    },
    {
        "CU":  "CU2",
        "Version":  "9.0.5259",
        "KBList":  "2489409"
    },
    {
        "CU":  "CU3",
        "Version":  "9.0.5266",
        "KBList":  "2507769"
    },
    {
        "Version":  "9.0.5292",
        "KBList":  "2494123"
    },
    {
        "Version":  "9.0.5294",
        "KBList":  "2572407"
    },
    {
        "Version":  "9.0.5295",
        "KBList":  "2598903"
    },
    {
        "Version":  "9.0.5296",
        "KBList":  "2615425"
    },
    {
        "Version":  "9.0.5324",
        "KBList":  "2716427"
    },
    {
        "Version":  "10.0.0",
        "Name": "2008"
    },
    {
        "Version":  "10.0.1019"
    },
    {
        "Version":  "10.0.1049"
    },
    {
        "Version":  "10.0.1075"
    },
    {
        "Version":  "10.0.1300"
    },
    {
        "Version":  "10.0.1442"
    },
    {
        "SP":  "RTM",
        "Version":  "10.0.1600",
        "SupportedUntil":  "2010-04-13T00:00:00"
    },
    {
        "Version":  "10.0.1750",
        "KBList":  "956718"
    },
    {
        "Version":  "10.0.1755",
        "KBList":  "957387"
    },
    {
        "CU":  "CU1",
        "Version":  "10.0.1763",
        "KBList":  "956717"
    },
    {
        "Version":  "10.0.1767",
        "KBList":  "958208"
    },
    {
        "Version":  "10.0.1771",
        "KBList":  "958611"
    },
    {
        "CU":  "CU2",
        "Version":  "10.0.1779",
        "KBList":  "958186"
    },
    {
        "CU":  "CU3",
        "Version":  "10.0.1787",
        "KBList":  "960484"
    },
    {
        "CU":  "CU4",
        "Version":  "10.0.1798",
        "KBList":  "963036"
    },
    {
        "CU":  "CU5",
        "Version":  "10.0.1806",
        "KBList":  "969531"
    },
    {
        "CU":  "CU6",
        "Version":  "10.0.1812",
        "KBList":  "971490"
    },
    {
        "CU":  "CU7",
        "Version":  "10.0.1818",
        "KBList":  "973601"
    },
    {
        "CU":  "CU8",
        "Version":  "10.0.1823",
        "KBList":  "975976"
    },
    {
        "CU":  "CU9",
        "Version":  "10.0.1828",
        "KBList":  "977444"
    },
    {
        "CU":  "CU10",
        "Version":  "10.0.1835",
        "KBList":  "979064"
    },
    {
        "Version":  "10.0.2520",
        "KBList":  "968369"
    },
    {
        "SP":  "SP1",
        "Version":  "10.0.2531",
        "SupportedUntil":  "2011-10-11T00:00:00",
        "KBList":  "968369"
    },
    {
        "Version":  "10.0.2573",
        "KBList":  "2494096"
    },
    {
        "CU":  "CU1",
        "Version":  "10.0.2710",
        "KBList":  "969099"
    },
    {
        "Version":  "10.0.2712",
        "KBList":  "970507"
    },
    {
        "CU":  "CU2",
        "Version":  "10.0.2714",
        "KBList":  "970315"
    },
    {
        "CU":  "CU3",
        "Version":  "10.0.2723",
        "KBList":  "971491"
    },
    {
        "CU":  "CU4",
        "Version":  "10.0.2734",
        "KBList":  "973602"
    },
    {
        "Version":  "10.0.2740",
        "KBList":  "976761"
    },
    {
        "CU":  "CU5",
        "Version":  "10.0.2746",
        "KBList":  "975977"
    },
    {
        "CU":  "CU6",
        "Version":  "10.0.2757",
        "KBList":  "977443"
    },
    {
        "CU":  "CU7",
        "Version":  "10.0.2766",
        "KBList":  "979065"
    },
    {
        "CU":  "CU8",
        "Version":  "10.0.2775",
        "KBList":  "981702"
    },
    {
        "Version":  "10.0.2787",
        "KBList":  "2231277"
    },
    {
        "CU":  "CU9",
        "Version":  "10.0.2789",
        "KBList":  "2083921"
    },
    {
        "CU":  "CU10",
        "Version":  "10.0.2799",
        "KBList":  "2279604"
    },
    {
        "CU":  "CU11",
        "Version":  "10.0.2804",
        "KBList":  "2413738"
    },
    {
        "CU":  "CU12",
        "Version":  "10.0.2808",
        "KBList":  "2467236"
    },
    {
        "CU":  "CU13",
        "Version":  "10.0.2816",
        "KBList":  "2497673"
    },
    {
        "CU":  "CU14",
        "Version":  "10.0.2821",
        "KBList":  "2527187"
    },
    {
        "Version":  "10.0.2841"
    },
    {
        "CU":  "CU15",
        "Version":  "10.0.2847",
        "KBList":  "2555406"
    },
    {
        "CU":  "CU16",
        "Version":  "10.0.2850",
        "KBList":  "2582282"
    },
    {
        "Version":  "10.0.3798",
        "KBList":  "979450"
    },
    {
        "SP":  "SP2",
        "Version":  "10.0.4000",
        "SupportedUntil":  "2012-10-09T00:00:00",
        "KBList":  "979450"
    },
    {
        "Version":  "10.0.4064",
        "KBList":  "2494088"
    },
    {
        "Version":  "10.0.4067",
        "KBList":  "2716434"
    },
    {
        "CU":  "CU1",
        "Version":  "10.0.4266",
        "KBList":  "2289254"
    },
    {
        "CU":  "CU2",
        "Version":  "10.0.4272",
        "KBList":  "2467239"
    },
    {
        "CU":  "CU3",
        "Version":  "10.0.4279",
        "KBList":  "2498535"
    },
    {
        "CU":  "CU4",
        "Version":  "10.0.4285",
        "KBList":  "2527180"
    },
    {
        "CU":  "CU5",
        "Version":  "10.0.4316",
        "KBList":  "2555408"
    },
    {
        "CU":  "CU6",
        "Version":  "10.0.4321",
        "KBList":  "2582285"
    },
    {
        "CU":  "CU7",
        "Version":  "10.0.4323",
        "KBList":  "2617148"
    },
    {
        "CU":  "CU8",
        "Version":  "10.0.4326",
        "KBList":  "2648096"
    },
    {
        "CU":  "CU9",
        "Version":  "10.0.4330",
        "KBList":  "2402659"
    },
    {
        "CU":  "CU10",
        "Version":  "10.0.4332",
        "KBList":  "2696625"
    },
    {
        "CU":  "CU11",
        "Version":  "10.0.4333",
        "KBList":  "2715951"
    },
    {
        "Version":  "10.0.4371",
        "KBList":  "2716433"
    },
    {
        "Version":  "10.0.5416",
        "KBList":  "2546945"
    },
    {
        "SP":  "SP3",
        "Version":  "10.0.5500",
        "SupportedUntil":  "2015-10-13T00:00:00",
        "KBList":  "2546951"
    },
    {
        "Version":  "10.0.5512",
        "KBList":  "2716436"
    },
    {
        "Version":  "10.0.5520",
        "KBList":  "2977321"
    },
    {
        "Version":  "10.0.5538",
        "KBList":  "3045305"
    },
    {
        "Version":  "10.0.5544",
        "KBList":  "3135244"
    },
    {
        "CU":  "CU1",
        "Version":  "10.0.5766",
        "KBList":  "2617146"
    },
    {
        "CU":  "CU2",
        "Version":  "10.0.5768",
        "KBList":  "2633143"
    },
    {
        "CU":  "CU3",
        "Version":  "10.0.5770",
        "KBList":  "2648098"
    },
    {
        "CU":  "CU4",
        "Version":  "10.0.5775",
        "KBList":  "2673383"
    },
    {
        "CU":  "CU5",
        "Version":  "10.0.5785",
        "KBList":  "2696626"
    },
    {
        "CU":  "CU6",
        "Version":  "10.0.5788",
        "KBList":  "271953"
    },
    {
        "CU":  "CU7",
        "Version":  "10.0.5794",
        "KBList":  "2738350"
    },
    {
        "Version":  "10.0.5826",
        "KBList":  "2716435"
    },
    {
        "CU":  "CU8",
        "Version":  "10.0.5828",
        "KBList":  "2771833"
    },
    {
        "CU":  "CU9",
        "Version":  "10.0.5829",
        "KBList":  "2799883"
    },
    {
        "Version":  "10.0.5835",
        "KBList":  "2814783"
    },
    {
        "CU":  "CU10",
        "Version":  "10.0.5840",
        "KBList":  "2834048"
    },
    {
        "CU":  "CU11",
        "Version":  "10.0.5841",
        "KBList":  "2834048"
    },
    {
        "CU":  "CU12",
        "Version":  "10.0.5844",
        "KBList":  "2863205"
    },
    {
        "CU":  "CU13",
        "Version":  "10.0.5846",
        "KBList":  "2880350"
    },
    {
        "CU":  "CU14",
        "Version":  "10.0.5848",
        "KBList":  "2893410"
    },
    {
        "CU":  "CU15",
        "Version":  "10.0.5850",
        "KBList":  "2923520"
    },
    {
        "CU":  "CU16",
        "Version":  "10.0.5852",
        "KBList":  "2936421"
    },
    {
        "CU":  "CU17",
        "Version":  "10.0.5861",
        "KBList":  "2958696"
    },
    {
        "Version":  "10.0.5867",
        "KBList":  "2877204"
    },
    {
        "Version":  "10.0.5869",
        "KBList":  "2977322"
    },
    {
        "Version":  "10.0.5890",
        "KBList":  "3045303"
    },
    {
        "Version":  "10.0.5894",
        "KBList":  "3135244"
    },
    {
        "SP":  [
                   "SP4",
                   "LATEST"
               ],
        "Version":  "10.0.6000",
        "SupportedUntil":  "2019-07-09T00:00:00",
        "KBList":  "2979596"
    },
    {
        "Version":  "10.0.6241",
        "KBList":  "3045311"
    },
    {
        "Version":  "10.0.6526",
        "KBList":  "3034373"
    },
    {
        "Version":  "10.0.6535",
        "KBList":  "3045308"
    },
    {
        "Version":  "10.0.6543",
        "KBList":  "3135244"
    },
    {
        "Version":  "10.0.6547",
        "KBList":  "3146034"
    },
    {
        "Version": "10.0.6556",
        "KBList": "4057114"
    },
    {
        "Version":  "10.50.1092",
        "Name": "2008R2"
    },
    {
        "Version":  "10.50.1352"
    },
    {
        "SP":  "RTM",
        "Version":  "10.50.1600",
        "SupportedUntil":  "2012-07-10T00:00:00"
    },
    {
        "Version":  "10.50.1617",
        "KBList":  "2494088"
    },
    {
        "CU":  "CU1",
        "Version":  "10.50.1702",
        "KBList":  "981355"
    },
    {
        "CU":  "CU2",
        "Version":  "10.50.1720",
        "KBList":  "2072493"
    },
    {
        "CU":  "CU3",
        "Version":  "10.50.1734",
        "KBList":  "2261464"
    },
    {
        "CU":  "CU4",
        "Version":  "10.50.1746",
        "KBList":  "2345451"
    },
    {
        "CU":  "CU5",
        "Version":  "10.50.1753",
        "KBList":  "2438347"
    },
    {
        "CU":  "CU6",
        "Version":  "10.50.1765",
        "KBList":  "2489376"
    },
    {
        "Version":  "10.50.1769",
        "KBList":  "2520808"
    },
    {
        "CU":  "CU7",
        "Version":  "10.50.1777",
        "KBList":  "2507770"
    },
    {
        "Version":  "10.50.1790",
        "KBList":  "2494086"
    },
    {
        "CU":  "CU8",
        "Version":  "10.50.1797",
        "KBList":  "2534352"
    },
    {
        "Version":  "10.50.1804",
        "KBList":  "2567713"
    },
    {
        "CU":  "CU9",
        "Version":  "10.50.1807",
        "KBList":  "2591746"
    },
    {
        "CU":  "CU10",
        "Version":  "10.50.1809",
        "KBList":  "2633145"
    },
    {
        "CU":  "CU11",
        "Version":  "10.50.1810",
        "KBList":  "2659692"
    },
    {
        "CU":  "CU12",
        "Version":  "10.50.1815",
        "KBList":  "2679366"
    },
    {
        "CU":  "CU13",
        "Version":  "10.50.1817",
        "KBList":  "2703280"
    },
    {
        "CU":  "CU14",
        "Version":  "10.50.2425",
        "KBList":  "2463333"
    },
    {
        "SP":  "SP1",
        "Version":  "10.50.2500",
        "SupportedUntil":  "2013-10-08T00:00:00",
        "KBList":  "2528583"
    },
    {
        "Version":  "10.50.2550",
        "KBList":  "27116440"
    },
    {
        "CU":  "CU1",
        "Version":  "10.50.2769",
        "KBList":  "2544793"
    },
    {
        "CU":  "CU2",
        "Version":  "10.50.2772",
        "KBList":  "2567714"
    },
    {
        "Version":  "10.50.2776"
    },
    {
        "CU":  "CU3",
        "Version":  "10.50.2789",
        "KBList":  "2591748"
    },
    {
        "CU":  "CU4",
        "Version":  "10.50.2796",
        "KBList":  "2633146"
    },
    {
        "Version":  "10.50.2799"
    },
    {
        "CU":  "CU5",
        "Version":  "10.50.2806",
        "KBList":  "2659694"
    },
    {
        "Version":  "10.50.2807"
    },
    {
        "CU":  "CU6",
        "Version":  "10.50.2811",
        "KBList":  "2679367"
    },
    {
        "CU":  "CU7",
        "Version":  "10.50.2817",
        "KBList":  "2703282"
    },
    {
        "CU":  "CU8",
        "Version":  "10.50.2822",
        "KBList":  "2723743"
    },
    {
        "Version":  "10.50.2861",
        "KBList":  "2716439"
    },
    {
        "CU":  "CU9",
        "Version":  "10.50.2866",
        "KBList":  "2756574"
    },
    {
        "CU":  "CU10",
        "Version":  "10.50.2868",
        "KBList":  "2783135"
    },
    {
        "CU":  "CU11",
        "Version":  "10.50.2869",
        "KBList":  "2812683"
    },
    {
        "CU":  "CU12",
        "Version":  "10.50.2874",
        "KBList":  "2828727"
    },
    {
        "CU":  "CU13",
        "Version":  "10.50.2876",
        "KBList":  "2855792"
    },
    {
        "Version":  "10.50.2881",
        "KBList":  "2868244"
    },
    {
        "Version":  "10.50.3720",
        "KBList":  "2630455"
    },
    {
        "SP":  "SP2",
        "Version":  "10.50.4000",
        "SupportedUntil":  "2015-10-13T00:00:00",
        "KBList":  "2630458"
    },
    {
        "Version":  "10.50.4033",
        "KBList":  "2977320"
    },
    {
        "Version":  "10.50.4042",
        "KBList":  "3045313"
    },
    {
        "CU":  "CU1",
        "Version":  "10.50.4260",
        "KBList":  "2720425"
    },
    {
        "CU":  "CU2",
        "Version":  "10.50.4263",
        "KBList":  "2740411"
    },
    {
        "CU":  "CU3",
        "Version":  "10.50.4266",
        "KBList":  "2754552"
    },
    {
        "CU":  "CU4",
        "Version":  "10.50.4270",
        "KBList":  "2777358"
    },
    {
        "CU":  "CU5",
        "Version":  "10.50.4276",
        "KBList":  "2797460"
    },
    {
        "Version":  "10.50.4279",
        "KBList":  "2830140"
    },
    {
        "CU":  "CU6",
        "Version":  "10.50.4285",
        "KBList":  "2830140"
    },
    {
        "CU":  "CU7",
        "Version":  "10.50.4286",
        "KBList":  "2844090"
    },
    {
        "CU":  "CU8",
        "Version":  "10.50.4290",
        "KBList":  "2871401"
    },
    {
        "CU":  "CU9",
        "Version":  "10.50.4295",
        "KBList":  "2887606"
    },
    {
        "CU":  "CU10",
        "Version":  "10.50.4297",
        "KBList":  "2908087"
    },
    {
        "CU":  "CU11",
        "Version":  "10.50.4302",
        "KBList":  "2926028"
    },
    {
        "CU":  "CU12",
        "Version":  "10.50.4305",
        "KBList":  "2938478"
    },
    {
        "CU":  "CU13",
        "Version":  "10.50.4319",
        "KBList":  "2967540"
    },
    {
        "Version":  "10.50.4339",
        "KBList":  "3045312"
    },
    {
        "Version":  "10.50.4343",
        "KBList":  "3135244"
    },
    {
        "SP":  [
                   "SP3",
                   "LATEST"
               ],
        "Version":  "10.50.6000",
        "SupportedUntil":  "2019-07-09T00:00:00",
        "KBList":  "2979597"
    },
    {
        "Version":  "10.50.6220",
        "KBList":  "3045316"
    },
    {
        "Version":  "10.50.6525",
        "KBList":  "3033860"
    },
    {
        "Version":  "10.50.6529",
        "KBList":  "3045314"
    },
    {
        "Version":  "10.50.6537",
        "KBList":  "3135244"
    },
    {
        "Version":  "10.50.6542",
        "KBList":  "3146034"
    },
    {
        "Version": "10.50.6560",
        "KBList": "4057113"
    },
    {
        "Version":  "11.0.1103",
        "Name": "2012"
    },
    {
        "Version":  "11.0.1440"
    },
    {
        "Version":  "11.0.1750"
    },
    {
        "Version":  "11.0.1913"
    },
    {
        "SP":  "RTM",
        "Version":  "11.0.2100",
        "SupportedUntil":  "2014-01-14T00:00:00"
    },
    {
        "Version":  "11.0.2214",
        "KBList":  "2685308"
    },
    {
        "Version":  "11.0.2218",
        "KBList":  "2716442"
    },
    {
        "CU":  "CU1",
        "Version":  "11.0.2316",
        "KBList":  "2679368"
    },
    {
        "CU":  "CU2",
        "Version":  "11.0.2325",
        "KBList":  "2703275"
    },
    {
        "CU":  "CU3",
        "Version":  "11.0.2332",
        "KBList":  "2723749"
    },
    {
        "Version":  "11.0.2376",
        "KBList":  "2716441"
    },
    {
        "CU":  "CU4",
        "Version":  "11.0.2383",
        "KBList":  "2758687"
    },
    {
        "CU":  "CU5",
        "Version":  "11.0.2395",
        "KBList":  "2777772"
    },
    {
        "CU":  "CU6",
        "Version":  "11.0.2401",
        "KBList":  "2728897"
    },
    {
        "CU":  "CU7",
        "Version":  "11.0.2405",
        "KBList":  "2823247"
    },
    {
        "CU":  "CU8",
        "Version":  "11.0.2410",
        "KBList":  "2844205"
    },
    {
        "CU":  "CU9",
        "Version":  "11.0.2419",
        "KBList":  "2867319"
    },
    {
        "CU":  "CU10",
        "Version":  "11.0.2420",
        "KBList":  "2891666"
    },
    {
        "CU":  "CU11",
        "Version":  "11.0.2424",
        "KBList":  "2908007"
    },
    {
        "Version":  "11.0.2809"
    },
    {
        "Version":  "11.0.2845"
    },
    {
        "SP":  "SP1",
        "Version":  "11.0.3000",
        "SupportedUntil":  "2015-07-14T00:00:00",
        "KBList":  "2674319"
    },
    {
        "Version":  "11.0.3128",
        "KBList":  "2793634"
    },
    {
        "Version":  "11.0.3153",
        "KBList":  "2977326"
    },
    {
        "Version":  "11.0.3156",
        "KBList":  "3045318"
    },
    {
        "CU":  "CU1",
        "Version":  "11.0.3321",
        "KBList":  "2765331"
    },
    {
        "Version":  "11.0.3335",
        "KBList":  "2800050"
    },
    {
        "CU":  "CU2",
        "Version":  "11.0.3339",
        "KBList":  "2790947"
    },
    {
        "CU":  "CU3",
        "Version":  "11.0.3349",
        "KBList":  "2812412"
    },
    {
        "Version":  "11.0.3350",
        "KBList":  "2832017"
    },
    {
        "CU":  "CU4",
        "Version":  "11.0.3368",
        "KBList":  "2833645"
    },
    {
        "CU":  "CU5",
        "Version":  "11.0.3373",
        "KBList":  "2861107"
    },
    {
        "CU":  "CU6",
        "Version":  "11.0.3381",
        "KBList":  "2874879"
    },
    {
        "CU":  "CU7",
        "Version":  "11.0.3393",
        "KBList":  "2894115"
    },
    {
        "CU":  "CU8",
        "Version":  "11.0.3401",
        "KBList":  "2917531"
    },
    {
        "CU":  "CU9",
        "Version":  "11.0.3412",
        "KBList":  "2931078"
    },
    {
        "CU":  "CU10",
        "Version":  "11.0.3431",
        "KBList":  "2954099"
    },
    {
        "Version":  "11.0.3437",
        "KBList":  "2969896"
    },
    {
        "CU":  "CU11",
        "Version":  "11.0.3449",
        "KBList":  "2975396"
    },
    {
        "CU":  "CU12",
        "Version":  "11.0.3460",
        "KBList":  "2977325"
    },
    {
        "CU":  "CU13",
        "Version":  "11.0.3470",
        "KBList":  "2991533"
    },
    {
        "Version":  "11.0.3482",
        "KBList":  "3002044"
    },
    {
        "CU":  "CU14",
        "Version":  "11.0.3486",
        "KBList":  "3023636"
    },
    {
        "CU":  "CU15",
        "Version":  "11.0.3487",
        "KBList":  "3038001"
    },
    {
        "CU":  "CU16",
        "Version":  "11.0.3492",
        "KBList":  "3052476"
    },
    {
        "Version":  "11.0.3513",
        "KBList":  "3045317"
    },
    {
        "SP":  "SP2",
        "Version":  "11.0.5058",
        "SupportedUntil":  "2017-01-10T00:00:00",
        "KBList":  "2958429"
    },
    {
        "Version":  "11.0.5343",
        "KBList":  "3045321"
    },
    {
        "Version":  "11.0.5352",
        "KBList":  "3135244"
    },
    {
        "Version":  "11.0.5388",
        "KBList":  "3194719"
    },
    {
        "Version":  "11.0.5522",
        "KBList":  "2969896"
    },
    {
        "CU":  "CU1",
        "Version":  "11.0.5532",
        "KBList":  "2976982"
    },
    {
        "CU":  "CU2",
        "Version":  "11.0.5548",
        "KBList":  "2983175"
    },
    {
        "CU":  "CU3",
        "Version":  "11.0.5556",
        "KBList":  "3002049"
    },
    {
        "CU":  "CU4",
        "Version":  "11.0.5569",
        "KBList":  "3007556"
    },
    {
        "Version":  "11.0.5571",
        "KBList":  "3034679"
    },
    {
        "CU":  "CU5",
        "Version":  "11.0.5582",
        "KBList":  "3037255"
    },
    {
        "CU":  "CU6",
        "Version":  "11.0.5592",
        "KBList":  "3052468"
    },
    {
        "Version":  "11.0.5613",
        "KBList":  "3045319"
    },
    {
        "CU":  "CU7",
        "Version":  "11.0.5623",
        "KBList":  "3072100"
    },
    {
        "Version":  "11.0.5629",
        "KBList":  "3087872"
    },
    {
        "CU":  "CU8",
        "Version":  "11.0.5634",
        "KBList":  "3082561"
    },
    {
        "Version":  "11.0.5636",
        "KBList":  "3097636"
    },
    {
        "CU":  "CU9",
        "Version":  "11.0.5641",
        "KBList":  "3098512"
    },
    {
        "CU":  "CU10",
        "Version":  "11.0.5644",
        "KBList":  "3120313"
    },
    {
        "CU":  "CU11",
        "Version":  "11.0.5646",
        "KBList":  "3137745"
    },
    {
        "CU":  "CU12",
        "Version":  "11.0.5649",
        "KBList":  "3152637"
    },
    {
        "CU":  "CU13",
        "Version":  "11.0.5655",
        "KBList":  "3165266"
    },
    {
        "CU":  "CU14",
        "Version":  "11.0.5657",
        "KBList":  "3180914"
    },
    {
        "CU":  "CU15",
        "Version":  "11.0.5676",
        "KBList":  [
                       "3205416",
                       "3194725"
                   ]
    },
    {
        "CU":  "CU16",
        "Version":  "11.0.5678",
        "KBList":  "3205054"
    },
    {
        "SP":  "SP3",
        "Version":  "11.0.6020",
        "SupportedUntil":  "2018-10-09T00:00:00",
        "KBList":  "3072779"
    },
    {
        "Version":  "11.0.6216",
        "KBList":  "3135244"
    },
    {
        "Version":  "11.0.6248",
        "KBList":  "3194721"
    },
    {
       "Version":  "11.0.6251",
       "KBList":  "4019092"
    },
    {
        "Version": "11.0.6260",
        "KBList": "4057115"
    },
    {
        "CU":  "CU1",
        "Version":  "11.0.6518",
        "KBList":  "3123299"
    },
    {
        "CU":  "CU2",
        "Version":  "11.0.6523",
        "KBList":  "3137746"
    },
    {
        "CU":  "CU3",
        "Version":  "11.0.6537",
        "KBList":  "3152635"
    },
    {
        "CU":  "CU4",
        "Version":  "11.0.6540",
        "KBList":  "3165264"
    },
    {
        "CU":  "CU5",
        "Version":  "11.0.6544",
        "KBList":  "3180915"
    },
    {
        "CU":  "CU6",
        "Version":  "11.0.6567",
        "KBList":  [
                       "3194992",
                       "3194724"
                   ]
    },
    {
        "CU":  "CU7",
        "Version":  "11.0.6579",
        "KBList":  "3205051"
    },
    {
        "CU":  "CU8",
        "Version":  "11.0.6594",
        "KBList":  "4013104"
    },
    {
        "CU":  "CU9",
        "Version":  "11.0.6598",
        "KBList":  "4016762"
    },
    {
        "CU":  "CU10",
        "Version":  "11.0.6607",
        "KBList":  [
                       "4025925",
                       "4019090"
                   ]
    },
    {
        "Version":  "11.0.6615",
        "KBList":  "4057121"
    },
    {
        "SP":  [
                   "SP4",
                   "LATEST"
               ],
        "SupportedUntil":  "2022-07-12T00:00:00",
        "Version":  "11.0.7001",
        "KBList":  "4018073"
    },
    {
        "Version": "11.0.7462",
        "KBList": "4057116"
    },
    {
        "Version": "11.0.7469",
        "KBList": "4091266"
    },
    {
        "Version":  "12.0.1524",
        "Name": "2014"
    },
    {
        "SP":  "RTM",
        "Version":  "12.0.2000",
        "SupportedUntil":  "2016-07-12T00:00:00"
    },
    {
        "Version":  "12.0.2254",
        "KBList":  "2977315"
    },
    {
        "Version":  "12.0.2269",
        "KBList":  "3045324"
    },
    {
        "Version":  "12.0.2271"
    },
    {
        "CU":  "CU1",
        "Version":  "12.0.2342",
        "KBList":  "2931693"
    },
    {
        "CU":  "CU2",
        "Version":  "12.0.2370",
        "KBList":  "2967546"
    },
    {
        "Version":  "12.0.2381",
        "KBList":  "2977316"
    },
    {
        "CU":  "CU3",
        "Version":  "12.0.2402",
        "KBList":  "2984923"
    },
    {
        "Version":  "12.0.2405",
        "KBList":  "2999809"
    },
    {
        "Version":  "12.0.2423",
        "KBList":  "3007050"
    },
    {
        "CU":  "CU4",
        "Version":  "12.0.2430",
        "KBList":  "2999197"
    },
    {
        "Version":  "12.0.2436",
        "KBList":  "3014867"
    },
    {
        "CU":  "CU5",
        "Version":  "12.0.2456",
        "KBList":  "3011055"
    },
    {
        "Version":  "12.0.2464",
        "KBList":  "3024815"
    },
    {
        "Version":  "12.0.2472",
        "KBList":  "3032087"
    },
    {
        "Version":  "12.0.2474",
        "KBList":  "3034679"
    },
    {
        "CU":  "CU6",
        "Version":  "12.0.2480",
        "KBList":  "3031047"
    },
    {
        "Version":  "12.0.2485",
        "KBList":  "3043788"
    },
    {
        "Version":  "12.0.2488",
        "KBList":  "3048751"
    },
    {
        "CU":  "CU7",
        "Version":  "12.0.2495",
        "KBList":  "3046038"
    },
    {
        "Version":  "12.0.2504",
        "KBList":  [
                       "3058512",
                       "2999809"
                   ]
    },
    {
        "Version":  "12.0.2505",
        "KBList":  "3052167"
    },
    {
        "Version":  "12.0.2506",
        "KBList":  "3063054"
    },
    {
        "CU":  "CU8",
        "Version":  "12.0.2546",
        "KBList":  "3067836"
    },
    {
        "Version":  "12.0.2548",
        "KBList":  "3045323"
    },
    {
        "CU":  "CU9",
        "Version":  "12.0.2553",
        "KBList":  "3075949"
    },
    {
        "CU":  "CU10",
        "Version":  "12.0.2556",
        "KBList":  "3094220"
    },
    {
        "CU":  "CU11",
        "Version":  "12.0.2560",
        "KBList":  "3106659"
    },
    {
        "CU":  "CU12",
        "Version":  "12.0.2564",
        "KBList":  "3130923"
    },
    {
        "CU":  "CU13",
        "Version":  "12.0.2568",
        "KBList":  "3144517"
    },
    {
        "CU":  "CU14",
        "Version":  "12.0.2569",
        "KBList":  "3158271"
    },
    {
        "SP":  "SP1",
        "Version":  "12.0.4100",
        "SupportedUntil":  "2017-10-10T00:00:00",
        "KBList":  "3058865"
    },
    {
        "Version":  "12.0.4213",
        "KBList":  "3070446"
    },
    {
        "Version":  "12.0.4219"
    },
    {
        "Version":  "12.0.4232",
        "KBList":  "3194720"
    },
    {
        "Version":  "12.0.4237",
        "KBList":  "4019091"
    },
    {
        "CU":  "CU1",
        "Version":  "12.0.4416",
        "KBList":  "3067839"
    },
    {
        "Version":  "12.0.4419",
        "KBList":  "3078973"
    },
    {
        "CU":  "CU2",
        "Version":  "12.0.4422",
        "KBList":  "3075950"
    },
    {
        "CU":  "CU3",
        "Version":  "12.0.4427",
        "KBList":  "3094221"
    },
    {
        "Version":  "12.0.4432",
        "KBList":  [
                       "3097972",
                       "319472"
                   ]
    },
    {
        "Version":  "12.0.4433",
        "KBList":  "3119148"
    },
    {
        "CU":  "CU4",
        "Version":  "12.0.4436",
        "KBList":  "3106660"
    },
    {
        "Version":  "12.0.4437",
        "KBList":  "3130999"
    },
    {
        "CU":  "CU5",
        "Version":  "12.0.4439",
        "KBList":  "3130926"
    },
    {
        "Version":  "12.0.4449",
        "KBList":  "3144524"
    },
    {
        "CU":  "CU6",
        "Version":  "12.0.4457",
        "KBList":  "3167392"
    },
    {
        "CU":  "CU7",
        "Version":  "12.0.4459",
        "KBList":  "3162659"
    },
    {
        "Version":  "12.0.4463",
        "KBList":  "3174370"
    },
    {
        "CU":  "CU8",
        "Version":  "12.0.4468",
        "KBList":  "917606"
    },
    {
        "CU":  "CU9",
        "Version":  "12.0.4474",
        "KBList":  "3186964"
    },
    {
        "Version":  "12.0.4487",
        "KBList":  "3194722"
    },
    {
        "CU":  "CU10",
        "Version":  "12.0.4491",
        "KBList":  "3204399"
    },
    {
        "Version":  "12.0.4502",
        "CU":  "CU11",
        "KBList":  "4010392"
    },
    {
        "Version":  "12.0.4511",
        "CU":  "CU12",
        "KBList":  "4017793"
    },
    {
        "Version":  "12.0.4522",
        "CU":  "CU13",
        "KBList":  [
                       "4019099",
                       "4032542"
                   ]
    },
    {
        "SP":  [
                   "SP2",
                   "LATEST"
               ],
        "Version":  "12.0.5000",
        "SupportedUntil":  "2024-07-09T00:00:00",
        "KBList":  "3171021"
    },
    {
        "Version":  "12.0.5203",
        "KBList":  "3194714"
    },
    {
        "Version": "12.0.5207",
        "KBList": "4019093"
    },
    {
        "Version": "12.0.5214",
        "KBList": "4057120"
    },
    {
        "CU":  "CU1",
        "Version":  "12.0.5511",
        "KBList":  "3178925"
    },
    {
        "CU":  "CU2",
        "Version":  "12.0.5522",
        "KBList":  "3188778"
    },
    {
        "Version":  "12.0.5532",
        "KBList":  "3194718"
    },
    {
        "CU":  "CU3",
        "Version":  "12.0.5538",
        "KBList":  "3204388"
    },
    {
        "CU":  "CU4",
        "Version":  "12.0.5540",
        "KBList":  "4010394"
    },
    {
        "CU":  "CU5",
        "Version":  "12.0.5546",
        "KBList":  "4013098"
    },
    {
        "Version":  "12.0.5553",
        "CU":  "CU6",
        "KBList":  [
                       "4019094",
                       "4036996"
                   ]
    },
    {
        "CU":  "CU7",
        "Version":  "12.0.5556",
        "KBList":  "4032541"
    },
    {
        "CU":  "CU8",
        "Version":  "12.0.5557",
        "KBList":  "4037356"
    },
    {
        "CU": "CU9",
        "Version": "12.0.5563",
        "KBList": "4055557"
    },
    {
        "CU": "CU10",
        "Version": "12.0.5571",
        "KBList": "4052725"
    },
    {
        "CU": "CU11",
        "Version": "12.0.5579",
        "KBList": "4077063"
    },
    {
        "CU": "CU12",
        "Version": "12.0.5589",
        "KBList": "4130489"
    },
    {
        "Version":  "13.0.200",
        "Name": "2016"
    },
    {
        "Version":  "13.0.300"
    },
    {
        "Version":  "13.0.407"
    },
    {
        "Version":  "13.0.500"
    },
    {
        "Version":  "13.0.600"
    },
    {
        "Version":  "13.0.700"
    },
    {
        "Version":  "13.0.800"
    },
    {
        "Version":  "13.0.900"
    },
    {
        "Version":  "13.0.1000"
    },
    {
        "Version":  "13.0.1100"
    },
    {
        "Version":  "13.0.1200"
    },
    {
        "Version":  "13.0.1250"
    },
    {
        "Version":  "13.0.1300"
    },
    {
        "Version":  "13.0.1400"
    },
    {
        "SP":  "RTM",
        "Version":  "13.0.1601",
        "SupportedUntil":  "2018-01-09T00:00:00"
    },
    {
        "Version":  "13.0.1708",
        "KBList":  "3164398"
    },
    {
        "Version":  "13.0.1722",
        "KBList":  "3194716"
    },
    {
        "Version":  "13.0.1728",
        "KBList":  "3210111"
    },
    {
        "Version":  "13.0.1742",
        "KBList":  "4019088"
    },
    {
        "Version": "13.0.1745",
        "KBList": "4058560"
    },
    {
        "CU":  "CU1",
        "Version":  "13.0.2149",
        "KBList":  "3164674"
    },
    {
        "CU":  "CU2",
        "Version":  "13.0.2164",
        "KBList":  "3182270"
    },
    {
        "Version":  "13.0.2169",
        "KBList":  "3195813"
    },
    {
        "Version":  "13.0.2170",
        "KBList":  "3199171"
    },
    {
        "CU":  "CU3",
        "Version":  "13.0.2186",
        "KBList":  [
                       "3205413",
                       "3194717"
                   ]
    },
    {
        "Version":  "13.0.2190",
        "KBList":  "3210110"
    },
    {
        "CU":  "CU4",
        "Version":  "13.0.2193",
        "KBList":  "3205052"
    },
    {
        "CU":  "CU5",
        "Version":  "13.0.2197",
        "KBList":  "4013105"
    },
    {
        "CU":  "CU6",
        "Version":  "13.0.2204",
        "KBList":  "4019914"
    },
    {
        "Version":  "13.0.2210",
        "CU":  "CU7",
        "KBList":  [
                       "4024304",
                       "4019086"
                   ]
    },
    {
        "CU":  "CU8",
        "Version":  "13.0.2213",
        "KBList":  "4040713"
    },
    {
        "CU": "CU9",
        "Version": "13.0.2216",
        "KBList": "4037357"
    },
    {
        "Version": "13.0.2218",
        "KBList": "4058559"
    },
    {
        "SP":  "SP1",
        "Version":  "13.0.4001",
        "SupportedUntil":  "2019-09-07T00:00:00",
        "KBList":  "3182545"
    },
    {
        "Version":  "13.0.4199",
        "KBList":  "3207512"
    },
    {
        "Version":  "13.0.4202",
        "KBList":  "3210089"
    },
    {
        "Version": "13.0.4206",
        "KBList": "4019089"
    },
    {
        "Version": "13.0.4210",
        "KBList": "4057118"
    },
    {
        "CU":  "CU1",
        "Version":  "13.0.4411",
        "KBList":  "3208177"
    },
    {
        "CU":  "CU2",
        "Version":  "13.0.4422",
        "KBList":  "4013106"
    },
    {
        "CU":  "CU3",
        "Version":  "13.0.4435",
        "KBList":  "4019916"
    },
    {
        "Version":  "13.0.4446",
        "CU":  "CU4",
        "KBList":  [
                       "4024305",
                       "4019095"
                   ]
    },
    {
        "CU":  "CU5",
        "Version":  "13.0.4451",
        "KBList":  "4040714"
    },
    {
        "CU": "CU6",
        "Version": "13.0.4457",
        "KBList": "4037354"
    },
    {
        "CU": "CU7",
        "Version": "13.0.4466",
        "KBList": "4057119"
    },
    {
        "CU": "CU8",
        "Version": "13.0.4474",
        "KBList": "4077064"
    },
    {
        "CU": "CU9",
        "Version": "13.0.4502",
        "KBList": "4100997"
    },
    {
        "CU": "CU10",
        "Version": "13.0.4514",
        "KBList": "4341569"
    },
    {
        "SP": [
            "SP2",
            "LATEST"
        ],
        "Version": "13.0.5026",
        "SupportedUntil": "2026-07-14T00:00:00",
        "KBList": "4052908"
    },
    {
        "CU": "CU1",
        "Version": "13.0.5149",
        "KBList": "4135048"
    },
    {
        "CU": "CU2",
        "Version": "13.0.5153",
        "KBList": "4340355"
    },
    {
        "Version":  "14.0.1",
        "Name": "2017"
    },
    {
        "Version":  "14.0.100"
    },
    {
        "Version":  "14.0.200"
    },
    {
        "Version":  "14.0.304"
    },
    {
        "Version":  "14.0.405"
    },
    {
        "Version":  "14.0.500"
    },
    {
        "Version":  "14.0.600"
    },
    {
        "Version":  "14.0.800"
    },
    {
        "Version":  "14.0.900"
    },
    {
        "SP":  [
                   "RTM",
                   "LATEST"
               ],
        "Version":  "14.0.1000",
        "SupportedUntil":  "2027-10-12T00:00:00"
    },
    {
        "CU":  "CU1",
        "Version":  "14.0.3006",
        "KBList":  "4038634"
    },
    {
        "CU": "CU2",
        "Version": "14.0.3008",
        "KBList": "4052574"
    },
    {
        "CU": "CU3",
        "Version": "14.0.3015",
        "KBList": "4052987"
    },
    {
        "CU": "CU4",
        "Version": "14.0.3022",
        "KBList": "4056498"
    },
    {
        "CU": "CU5",
        "Version": "14.0.3023",
        "KBList": "4092643"
    },
    {
        "CU": "CU6",
        "Version": "14.0.3025",
        "KBList": "4101464"
    },
    {
        "CU": "CU7",
        "Version": "14.0.3026",
        "KBList": "4229789"
    },
    {
        "CU": "CU8",
        "Version": "14.0.3029",
        "KBList": "4338363"
    },
    {
        "CU": "CU9",
        "Version": "14.0.3030",
        "KBList": "4341265"
    }
    ]
}
tools\dbatools\bin\dbatools-index.json
[
    {
        "CommandName":  "Add-DbaComputerCertificate",
        "Description":  "Adds a computer certificate from a local or remote computer.",
        "Tags":  "Certificate",
        "Synopsis":  "Adds a computer certificate - useful for older systems.",
        "Name":  "Add-DbaComputerCertificate",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eAdd-DbaComputerCertificate -ComputerName Server1 -Path C:\\temp\\cert.cer\r\n\r\nAdds the local C:\\temp\\cert.cer to the remote server Server1 in LocalMachine\\My (Personal).\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eAdd-DbaComputerCertificate -Path C:\\temp\\cert.cer\r\n\r\nAdds the local C:\\temp\\cert.cer to the local computer\u0027s LocalMachine\\My (Personal) certificate store.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Add-DbaPfDataCollectorCounter",
        "Description":  "Adds a Performance Data Collector Counter.",
        "Tags":  "PerfMon",
        "Synopsis":  "Adds a Performance Data Collector Counter.",
        "Name":  "Add-DbaPfDataCollectorCounter",
        "Links":  "https://dbatools.io/Add-DbaPfDataCollectorCounter",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eAdd-DbaPfDataCollectorCounter -ComputerName sql2017 -CollectorSet \u0027System Correlation\u0027 -Collector \r\nDataCollector01  -Counter \u0027\\LogicalDisk(*)\\Avg. Disk Queue Length\u0027\r\n\r\nAdds the \u0027\\LogicalDisk(*)\\Avg. Disk Queue Length\u0027 counter within the DataCollector01 collector within the System \r\nCorrelation collector set on sql2017.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfDataCollector | Out-GridView -PassThru | Add-DbaPfDataCollectorCounter -Counter \u0027\\LogicalDisk(*)\\Avg. \r\nDisk Queue Length\u0027 -Confirm\r\n\r\nAllows you to select which Data Collector you\u0027d like to add the counter \u0027\\LogicalDisk(*)\\Avg. Disk Queue Length\u0027 on \r\nlocalhost and prompts for confirmation.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Add-DbaRegisteredServer",
        "Description":  "Adds registered servers to SQL Server Central Management Server (CMS). If you need more flexiblity, look into Import-DbaRegisteredServer which\naccepts multiple kinds of input and allows you to add reg servers from different CMSes.",
        "Tags":  [
                     "RegisteredServer",
                     "CMS"
                 ],
        "Author":  "Chrissy LeMaire (@cl)",
        "Synopsis":  "Adds registered servers to SQL Server Central Management Server (CMS)",
        "Name":  "Add-DbaRegisteredServer",
        "Links":  "https://dbatools.io/Add-DbaRegisteredServer",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eAdd-DbaRegisteredServer -SqlInstance sql2008 -ServerName sql01\r\n\r\nCreates a registered server on sql2008\u0027s CMS which points to the SQL Server, sql01. When scrolling in CMS, the name \r\n\"sql01\" will be visible.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eAdd-DbaRegisteredServer -SqlInstance sql2008 -ServerName sql01 -Name \"The 2008 Clustered Instance\" -Description \r\n\"HR\u0027s Dedicated SharePoint instance\"\r\n\r\nCreates a registered server on sql2008\u0027s CMS which points to the SQL Server, sql01. When scrolling in CMS, \"The 2008 \r\nClustered Instance\" will be visible.\r\nClearly this is hard to explain ;)\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eAdd-DbaRegisteredServer -SqlInstance sql2008 -ServerName sql01 -Group hr\\Seattle\r\n\r\nCreates a registered server on sql2008\u0027s CMS which points to the SQL Server, sql01. When scrolling in CMS, the name \r\n\"sql01\" will be visible within the Seattle group which is in the hr group.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRegisteredServerGroup -SqlInstance sql2008 -Group hr\\Seattle | Add-DbaRegisteredServer -ServerName \r\nsql01111\r\n\r\nCreates a registered server on sql2008\u0027s CMS which points to the SQL Server, sql01. When scrolling in CMS, the name \r\n\"sql01\" will be visible within the Seattle group which is in the hr group.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Add-DbaRegisteredServerGroup",
        "Description":  "Adds registered server groups to SQL Server Central Management Server (CMS). If you need more flexiblity, look into Import-DbaRegisteredServer which\naccepts multiple kinds of input and allows you to add reg servers and groups from different CMSes.",
        "Tags":  [
                     "RegisteredServer",
                     "CMS"
                 ],
        "Author":  "Chrissy LeMaire (@cl)",
        "Synopsis":  "Adds registered server groups to SQL Server Central Management Server (CMS)",
        "Name":  "Add-DbaRegisteredServerGroup",
        "Links":  "https://dbatools.io/Add-DbaRegisteredServerGroup",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eAdd-DbaRegisteredServerGroup -SqlInstance sql2012 -Name HR\r\n\r\nCreates a registered server group called HR, in the root of sql2012\u0027s CMS\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eAdd-DbaRegisteredServerGroup -SqlInstance sql2012, sql2014 -Name subfolder -Group HR\r\n\r\nCreates a registered server group on sql2012 and sql2014 called subfolder within the HR group\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRegisteredServerGroup -SqlInstance sql2012, sql2014 -Group HR | Add-DbaRegisteredServerGroup -Name \r\nsubfolder\r\n\r\nCreates a registered server group on sql2012 and sql2014 called subfolder within the HR group of each server\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Backup-DbaDatabase",
        "Description":  "Performs a backup of a specified type of 1 or more databases on a single SQL Server Instance. These backups may be Full, Differential or Transaction log backups.",
        "Tags":  [
                     "DisasterRecovery",
                     "Backup",
                     "Restore"
                 ],
        "Author":  "Stuart Moore (@napalmgram), stuart-moore.com",
        "Synopsis":  "Backup one or more SQL Sever databases from a single SQL Server SqlInstance.",
        "Name":  "Backup-DbaDatabase",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eBackup-DbaDatabase -SqlInstance Server1 -Database HR, Finance\r\n\r\nThis will perform a full database backup on the databases HR and Finance on SQL Server Instance Server1 to Server1\u0027s \r\ndefault backup directory.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eBackup-DbaDatabase -SqlInstance sql2016 -BackupDirectory C:\\temp -Database AdventureWorks2014 -Type Full\r\n\r\nBacks up AdventureWorks2014 to sql2016\u0027s C:\\temp folder.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eBackup-DbaDatabase -SqlInstance sql2016 -AzureBaseUrl https://dbatoolsaz.blob.core.windows.net/azbackups/ \r\n-AzureCredential dbatoolscred -Type Full -CreateFolder\r\n\r\nPerforms a full backup of all databases on the sql2016 instance to their own containers under the \r\nhttps://dbatoolsaz.blob.core.windows.net/azbackups/ container on Azure blog storage using the sql credential \r\n\"dbatoolscred\" registered on the sql2016 instance.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Backup-DbaDatabaseMasterKey",
        "Description":  "Backs up specified database master key.",
        "Tags":  [
                     "Certificate",
                     "Database"
                 ],
        "Synopsis":  "Backs up specified database master key.",
        "Name":  "Backup-DbaDatabaseMasterKey",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eBackup-DbaDatabaseMasterKey -SqlInstance server1\\sql2016\r\n\r\nPrompts for export password, then logs into server1\\sql2016 with Windows credentials then backs up all database keys to \r\nthe default backup directory.\r\n\r\nComputerName : SERVER1\r\nInstanceName : SQL2016\r\nSqlInstance  : SERVER1\\SQL2016\r\nDatabase     : master\r\nFilename     : E:\\MSSQL13.SQL2016\\MSSQL\\Backup\\server1$sql2016-master-20170614162311.key\r\nStatus       : Success\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eBackup-DbaDatabaseMasterKey -SqlInstance Server1 -Database db1 -Path \\\\nas\\sqlbackups\\keys\r\n\r\nLogs into sql2016 with Windows credentials then backs up db1\u0027s keys to the \\\\nas\\sqlbackups\\keys directory.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Backup-DbaDbCertificate",
        "Description":  "Exports database certificates from SQL Server using SMO and outputs the .cer and .pvk files.",
        "Tags":  [
                     "Migration",
                     "Certificate"
                 ],
        "Author":  "Jess Pomfret (@jpomfret)",
        "Synopsis":  "Exports database certificates from SQL Server using SMO.",
        "Name":  "Backup-DbaDbCertificate",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eBackup-DbaDbCertificate -SqlInstance Server1\r\n\r\nExports all the certificates on the specified SQL Server to the default data path for the instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e$cred = Get-Credential sqladmin\r\n\r\nBackup-DbaDbCertificate -SqlInstance Server1 -SqlCredential $cred\r\n\r\nConnects using sqladmin credential and exports all the certificates on the specified SQL Server to the default data \r\npath for the instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eBackup-DbaDbCertificate -SqlInstance Server1 -Certificate Certificate1\r\n\r\nExports only the certificate named Certificate1 on the specified SQL Server to the default data path for the instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eBackup-DbaDbCertificate -SqlInstance Server1 -Database AdventureWorks\r\n\r\nExports only the certificates for AdventureWorks on the specified SQL Server to the default data path for the instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eBackup-DbaDbCertificate -SqlInstance Server1 -ExcludeDatabase AdventureWorks\r\n\r\nExports all certificates except those for AdventureWorks on the specified SQL Server to the default data path for the \r\ninstance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eBackup-DbaDbCertificate -SqlInstance Server1 -Path \\\\Server1\\Certificates -EncryptionPassword \r\n(ConvertTo-SecureString -force -AsPlainText GoodPass1234!!)\r\n\r\nExports all the certificates and private keys on the specified SQL Server.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 7 --------------------------\r\n\r\nPS C:\\\u003e$EncryptionPassword = ConvertTo-SecureString -AsPlainText \"GoodPass1234!!\" -force\r\n\r\n$DecryptionPassword = ConvertTo-SecureString -AsPlainText \"Password4567!!\" -force\r\nBackup-DbaDbCertificate -SqlInstance Server1 -EncryptionPassword $EncryptionPassword -DecryptionPassword \r\n$DecryptionPassword\r\nExports all the certificates on the specified SQL Server using the supplied DecryptionPassword, since an \r\nEncryptionPassword is specified private keys are also exported.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 8 --------------------------\r\n\r\nPS C:\\\u003eBackup-DbaDbCertificate -SqlInstance Server1 -Path \\\\Server1\\Certificates\r\n\r\nExports all certificates on the specified SQL Server to the specified path.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 9 --------------------------\r\n\r\nPS C:\\\u003eBackup-DbaDbCertificate -SqlInstance Server1 -Suffix DbaTools\r\n\r\nExports all certificates on the specified SQL Server to the specified path, appends DbaTools to the end of the \r\nfilenames.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 10 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbCertificate -SqlInstance sql2016 | Backup-DbaDbCertificate\r\n\r\nExports all certificates found on sql2016 to the default data directory.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Clear-DbaPlanCache",
        "Description":  "Checks ahoc and prepared plan cache for each database, if over 100 MBs removes from the cache.\n\nThis command automates that process.\n\nReferences: https://www.sqlskills.com/blogs/kimberly/plan-cache-adhoc-workloads-and-clearing-the-single-use-plan-cache-bloat/",
        "Tags":  "Memory",
        "Author":  "Tracy Boggiano, databasesuperhero.com",
        "Synopsis":  "Removes adhoc and prepared plan caches is single use plans are over defined threshold.",
        "Name":  "Clear-DbaPlanCache",
        "Links":  "https://dbatools.io/Clear-DbaPlanCache",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eClear-DbaPlanCache -SqlInstance sql2017 -Threshold 200\r\n\r\nLogs into the SQL Server instance \"sql2017\" and removes plan caches if over 200 MB.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eClear-DbaPlanCache -SqlInstance sql2017 -SqlCredential (Get-Credential sqladmin)\r\n\r\nLogs into the SQL instance using the SQL Login \u0027sqladmin\u0027 and then Windows instance as \u0027ad\\sqldba\u0027\r\nand removes if Threshold over 100 MB.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Clear-DbaSqlConnectionPool",
        "Description":  "This command resets (or empties) the connection pool.\n\nIf there are connections in use at the time of the call, they are marked appropriately and will be discarded (instead of being returned to the pool) when Close() is called on them.\n\nRef: https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.clearallpools(v=vs.110).aspx",
        "Tags":  "Connection",
        "Synopsis":  "Resets (or empties) the connection pool.",
        "Name":  "Clear-DbaSqlConnectionPool",
        "Links":  "https://dbatools.io/Clear-DbaSqlConnectionPool",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eClear-DbaSqlConnectionPool\r\n\r\nClears all local connection pools.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eClear-DbaSqlConnectionPool -ComputerName workstation27\r\n\r\nClears all connection pools on workstation27.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Clear-DbaWaitStatistics",
        "Description":  "Reset the aggregated statistics - basically just executes DBCC SQLPERF (N\u0027sys.dm_os_wait_stats\u0027, CLEAR)",
        "Tags":  "WaitStatistic",
        "Synopsis":  "Clears wait statistics",
        "Name":  "Clear-DbaWaitStatistics",
        "Links":  "https://dbatools.io/Clear-DbaWaitStatistics",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eClear-DbaWaitStatistics -SqlInstance sql2008, sqlserver2012\r\n\r\nAfter confirmation, clears wait stats on servers sql2008 and sqlserver2012\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eClear-DbaWaitStatistics -SqlInstance sql2008, sqlserver2012 -Confirm:$false\r\n\r\nClears wait stats on servers sql2008 and sqlserver2012, without prompting\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Connect-DbaInstance",
        "Description":  "This command is robust because it initializes properties that do not cause enumeration by default. It also supports both Windows and SQL Server authentication methods, and detects which to use based upon the provided credentials.\n\nBy default, this command also sets the connection\u0027s ApplicationName property  to \"dbatools PowerShell module - dbatools.io - custom connection\". If you\u0027re doing anything that requires profiling, you can look for this client name.\n\nAlternatively, you can pass in whichever client name you\u0027d like using the -ClientName parameter. There are a ton of other parameters for you to explore as well.\n\nSee https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.connectionstring.aspx\nand https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnectionstringbuilder.aspx,\nand https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.aspx\n\nTo execute SQL commands, you can use $server.ConnectionContext.ExecuteReader($sql) or $server.Databases[\u0027master\u0027].ExecuteNonQuery($sql)",
        "Tags":  [
                     "Connect",
                     "Connection"
                 ],
        "Synopsis":  "Creates a robust SMO SQL Server object.",
        "Name":  "Connect-DbaInstance",
        "Links":  "https://dbatools.io/Connect-DbaInstance",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eConnect-DbaInstance -SqlInstance sql2014\r\n\r\nCreates an SMO Server object that connects using Windows Authentication\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e$wincred = Get-Credential ad\\sqladmin\r\n\r\nConnect-DbaInstance -SqlInstance sql2014 -Credential $wincred\r\n\r\nCreates an SMO Server object that connects using alternative Windows credentials\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003e$sqlcred = Get-Credential sqladmin\r\n\r\n$server = Connect-DbaInstance -SqlInstance sql2014 -Credential $sqlcred\r\n\r\nLogin to sql2014 as SQL login sqladmin.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003e$server = Connect-DbaInstance -SqlInstance sql2014 -ClientName \"my connection\"\r\n\r\nCreates an SMO Server object that connects using Windows Authentication and uses the client name \"my connection\". So \r\nwhen you open up profiler or use extended events, you can search for \"my connection\".\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003e$server = Connect-DbaInstance -SqlInstance sql2014 -AppendConnectionString \"Packet \r\nSize=4096;AttachDbFilename=C:\\MyFolder\\MyDataFile.mdf;User Instance=true;\"\r\n\r\nCreates an SMO Server object that connects to sql2014 using Windows Authentication, then it sets the packet size (this \r\ncan also be done via -PacketSize) and other connection attributes.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003e$server = Connect-DbaInstance -SqlInstance sql2014 -NetworkProtocol TcpIp -MultiSubnetFailover\r\n\r\nCreates an SMO Server object that connects using Windows Authentication that uses TCP/IP and has MultiSubnetFailover \r\nenabled.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 7 --------------------------\r\n\r\nPS C:\\\u003e$server = Connect-DbaInstance sql2016 -ApplicationIntent ReadOnly\r\n\r\nConnects with ReadOnly ApplicationIntent.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "ConvertTo-DbaDataTable",
        "Description":  "Creates a DataTable based on an object\u0027s properties. This allows you to easily write to SQL Server tables.\n\nThanks to Chad Miller, this is based on his script. https://gallery.technet.microsoft.com/scriptcenter/4208a159-a52e-4b99-83d4-8048468d29dd\n\nIf the attempt to convert to datatable fails, try the -Raw parameter for less accurate datatype detection.",
        "Tags":  [
                     "DataTable",
                     "Table",
                     "Data"
                 ],
        "Synopsis":  "Creates a DataTable for an object.",
        "Name":  "ConvertTo-DbaDataTable",
        "Links":  "https://dbatools.io/ConvertTo-DbaDataTable",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-Service | ConvertTo-DbaDataTable\r\n\r\nCreates a DataTable from the output of Get-Service.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eConvertTo-DbaDataTable -InputObject $csv.cheesetypes\r\n\r\nCreates a DataTable from the CSV object $csv.cheesetypes.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003e$dblist | ConvertTo-DbaDataTable\r\n\r\nCreates a DataTable from the $dblist object passed in via pipeline.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-Process | ConvertTo-DbaDataTable -TimeSpanType TotalSeconds\r\n\r\nCreates a DataTable with the running processes and converts any TimeSpan property to TotalSeconds.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "ConvertTo-DbaXESession",
        "Description":  "Uses a slightly modified version of sp_SQLskills_ConvertTraceToExtendedEvents.sql to convert Traces to Extended Events.\n\nT-SQL code by: Jonathan M. Kehayias, SQLskills.com. T-SQL can be found in this module directory and at\nhttps://www.sqlskills.com/blogs/jonathan/converting-sql-trace-to-extended-events-in-sql-server-2012/",
        "Tags":  [
                     "Trace",
                     "ExtendedEvent"
                 ],
        "Synopsis":  "Uses a slightly modified version of sp_SQLskills_ConvertTraceToExtendedEvents.sql to convert Traces to Extended Events.",
        "Name":  "ConvertTo-DbaXESession",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaTrace -SqlInstance sql2017, sql2012 | Where Id -eq 2 | ConvertTo-DbaXESession -Name \u0027Test\u0027\r\n\r\nConverts Trace with ID 2 to a Session named Test on SQL Server instances named sql2017 and sql2012\r\nand creates the Session on each respective server.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaTrace -SqlInstance sql2014 | Out-GridView -PassThru | ConvertTo-DbaXESession -Name \u0027Test\u0027 | \r\nStart-DbaXESession\r\n\r\nConverts selected traces on sql2014 to sessions, creates the session, and starts it.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaTrace -SqlInstance sql2014 | Where Id -eq 1 | ConvertTo-DbaXESession -Name \u0027Test\u0027 -OutputScriptOnly\r\n\r\nConverts trace ID 1 on sql2014 to an Extended Event and outputs the resulting T-SQL.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Copy-DbaAgentAlert",
        "Description":  "By default, all alerts are copied. The -Alert parameter is auto-populated for command-line completion and can be used to copy only specific alerts.\n\nIf the alert already exists on the destination, it will be skipped unless -Force is used.",
        "Tags":  [
                     "Migration",
                     "Agent"
                 ],
        "Author":  "Chrissy LeMaire (@cl), netnerds.net",
        "Synopsis":  "Copy-DbaAgentAlert migrates alerts from one SQL Server to another.",
        "Name":  "Copy-DbaAgentAlert",
        "Links":  "https://dbatools.io/Copy-DbaAgentAlert",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaAgentAlert -Source sqlserver2014a -Destination sqlcluster\r\n\r\nCopies all alerts from sqlserver2014a to sqlcluster using Windows credentials. If alerts with the same name exist on \r\nsqlcluster, they will be skipped.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaAgentAlert -Source sqlserver2014a -Destination sqlcluster -Alert PSAlert -SourceSqlCredential $cred \r\n-Force\r\n\r\nCopies a only the alert named PSAlert from sqlserver2014a to sqlcluster using SQL credentials for sqlserver2014a and \r\nWindows credentials for sqlcluster. If a alert with the same name exists on sqlcluster, it will be dropped and \r\nrecreated because -Force was used.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaAgentAlert -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force\r\n\r\nShows what would happen if the command were executed using force.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Copy-DbaAgentCategory",
        "Description":  "By default, all SQL Agent categories for Jobs, Operators and Alerts are copied.\n\nThe -OperatorCategories parameter is auto-populated for command-line completion and can be used to copy only specific operator categories.\nThe -AgentCategories parameter is auto-populated for command-line completion and can be used to copy only specific agent categories.\nThe -JobCategories parameter is auto-populated for command-line completion and can be used to copy only specific job categories.\n\nIf the category already exists on the destination, it will be skipped unless -Force is used.",
        "Tags":  [
                     "Migration",
                     "Agent"
                 ],
        "Author":  "Chrissy LeMaire (@cl), netnerds.net",
        "Synopsis":  "Copy-DbaAgentCategory migrates SQL Agent categories from one SQL Server to another. This is similar to sp_add_category.\n\nhttps://msdn.microsoft.com/en-us/library/ms181597.aspx",
        "Name":  "Copy-DbaAgentCategory",
        "Links":  "https://dbatools.io/Copy-DbaAgentCategory",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaAgentCategory -Source sqlserver2014a -Destination sqlcluster\r\n\r\nCopies all operator categories from sqlserver2014a to sqlcluster using Windows authentication. If operator categories \r\nwith the same name exist on sqlcluster, they will be skipped.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaAgentCategory -Source sqlserver2014a -Destination sqlcluster -OperatorCategory PSOperator \r\n-SourceSqlCredential $cred -Force\r\n\r\nCopies a single operator category, the PSOperator operator category from sqlserver2014a to sqlcluster using SQL \r\ncredentials to authenticate to sqlserver2014a and Windows credentials for sqlcluster. If a operator category with the \r\nsame name exists on sqlcluster, it will be dropped and recreated because -Force was used.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaAgentCategory -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force\r\n\r\nShows what would happen if the command were executed using force.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Copy-DbaAgentJob",
        "Description":  "By default, all jobs are copied. The -Job parameter is auto-populated for command-line completion and can be used to copy only specific jobs.\n\nIf the job already exists on the destination, it will be skipped unless -Force is used.",
        "Tags":  [
                     "Migration",
                     "Agent",
                     "Job"
                 ],
        "Author":  "Chrissy LeMaire (@cl), netnerds.net",
        "Synopsis":  "Copy-DbaAgentJob migrates jobs from one SQL Server to another.",
        "Name":  "Copy-DbaAgentJob",
        "Links":  "https://dbatools.io/Copy-DbaAgentJob",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaAgentJob -Source sqlserver2014a -Destination sqlcluster\r\n\r\nCopies all jobs from sqlserver2014a to sqlcluster, using Windows credentials. If jobs with the same name exist on \r\nsqlcluster, they will be skipped.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaAgentJob -Source sqlserver2014a -Destination sqlcluster -Job PSJob -SourceSqlCredential $cred -Force\r\n\r\nCopies a single job, the PSJob job from sqlserver2014a to sqlcluster, using SQL credentials for sqlserver2014a and \r\nWindows credentials for sqlcluster. If a job with the same name exists on sqlcluster, it will be dropped and recreated \r\nbecause -Force was used.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaAgentJob -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force\r\n\r\nShows what would happen if the command were executed using force.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Copy-DbaAgentOperator",
        "Description":  "By default, all operators are copied. The -Operators parameter is auto-populated for command-line completion and can be used to copy only specific operators.\n\nIf the associated credentials for the operator do not exist on the destination, it will be skipped. If the operator already exists on the destination, it will be skipped unless -Force is used.",
        "Tags":  [
                     "Migration",
                     "Agent",
                     "Operator"
                 ],
        "Author":  "Chrissy LeMaire (@cl), netnerds.net",
        "Synopsis":  "Copy-DbaAgentOperator migrates operators from one SQL Server to another.",
        "Name":  "Copy-DbaAgentOperator",
        "Links":  "https://dbatools.io/Copy-DbaAgentOperator",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaAgentOperator -Source sqlserver2014a -Destination sqlcluster\r\n\r\nCopies all operators from sqlserver2014a to sqlcluster using Windows credentials. If operators with the same name exist \r\non sqlcluster, they will be skipped.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaAgentOperator -Source sqlserver2014a -Destination sqlcluster -Operator PSOperator -SourceSqlCredential \r\n$cred -Force\r\n\r\nCopies only the PSOperator operator from sqlserver2014a to sqlcluster using SQL credentials for sqlserver2014a and \r\nWindows credentials for sqlcluster. If an operator with the same name exists on sqlcluster, it will be dropped and \r\nrecreated because -Force was used.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaAgentOperator -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force\r\n\r\nShows what would happen if the command were executed using force.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Copy-DbaAgentProxyAccount",
        "Description":  "By default, all proxy accounts are copied. The -ProxyAccounts parameter is auto-populated for command-line completion and can be used to copy only specific proxy accounts.\n\nIf the associated credential for the account does not exist on the destination, it will be skipped. If the proxy account already exists on the destination, it will be skipped unless -Force is used.",
        "Tags":  [
                     "Migration",
                     "Agent"
                 ],
        "Author":  "Chrissy LeMaire (@cl), netnerds.net",
        "Synopsis":  "Copy-DbaAgentProxyAccount migrates proxy accounts from one SQL Server to another.",
        "Name":  "Copy-DbaAgentProxyAccount",
        "Links":  "https://dbatools.io/Copy-DbaAgentProxyAccount",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaAgentProxyAccount -Source sqlserver2014a -Destination sqlcluster\r\n\r\nCopies all proxy accounts from sqlserver2014a to sqlcluster using Windows credentials. If proxy accounts with the same \r\nname exist on sqlcluster, they will be skipped.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaAgentProxyAccount -Source sqlserver2014a -Destination sqlcluster -ProxyAccount PSProxy \r\n-SourceSqlCredential $cred -Force\r\n\r\nCopies only the PSProxy proxy account from sqlserver2014a to sqlcluster using SQL credentials for sqlserver2014a and \r\nWindows credentials for sqlcluster. If a proxy account with the same name exists on sqlcluster, it will be dropped and \r\nrecreated because -Force was used.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaAgentProxyAccount -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force\r\n\r\nShows what would happen if the command were executed using force.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Copy-DbaAgentSharedSchedule",
        "Description":  "All shared job schedules are copied.\n\nIf the associated credential for the account does not exist on the destination, it will be skipped. If the shared job schedule already exists on the destination, it will be skipped unless -Force is used.",
        "Tags":  [
                     "Migration",
                     "Agent"
                 ],
        "Author":  "Chrissy LeMaire (@cl), netnerds.net",
        "Synopsis":  "Copy-DbaAgentSharedSchedule migrates shared job schedules from one SQL Server to another.",
        "Name":  "Copy-DbaAgentSharedSchedule",
        "Links":  "https://dbatools.io/Copy-DbaAgentSharedSchedule",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaAgentSharedSchedule -Source sqlserver2014a -Destination sqlcluster\r\n\r\nCopies all shared job schedules from sqlserver2014a to sqlcluster using Windows credentials. If shared job schedules \r\nwith the same name exist on sqlcluster, they will be skipped.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaAgentSharedSchedule -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force\r\n\r\nShows what would happen if the command were executed using force.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Copy-DbaBackupDevice",
        "Description":  "Backups are migrated using Admin shares. If the destination directory does not exist, SQL Server\u0027s default backup directory will be used.\n\nIf a backup device with same name exists on destination, it will not be dropped and recreated unless -Force is used.",
        "Tags":  [
                     "Migration",
                     "Backup"
                 ],
        "Author":  "Chrissy LeMaire (@cl), netnerds.net",
        "Synopsis":  "Copies backup devices one by one. Copies both SQL code and the backup file itself.",
        "Name":  "Copy-DbaBackupDevice",
        "Links":  "https://dbatools.io/Copy-DbaBackupDevice",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaBackupDevice -Source sqlserver2014a -Destination sqlcluster\r\n\r\nCopies all server backup devices from sqlserver2014a to sqlcluster using Windows credentials. If backup devices with \r\nthe same name exist on sqlcluster, they will be skipped.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaBackupDevice -Source sqlserver2014a -Destination sqlcluster -BackupDevice backup01 -SourceSqlCredential \r\n$cred -Force\r\n\r\nCopies only the backup device named backup01 from sqlserver2014a to sqlcluster using SQL credentials for sqlserver2014a \r\n   and Windows credentials for sqlcluster. If a backup device with the same name exists on sqlcluster, it will be \r\ndropped and recreated because -Force was used.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaBackupDevice -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force\r\n\r\nShows what would happen if the command were executed using force.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Copy-DbaCentralManagementServer",
        "Description":  "Copy-DbaCentralManagementServer copies all groups, subgroups, and server instances from one SQL Server to another.",
        "Tags":  "Migration",
        "Author":  "Chrissy LeMaire (@cl), netnerds.net",
        "Synopsis":  "Migrates SQL Server Central Management groups and server instances from one SQL Server to another.",
        "Name":  "Copy-DbaCentralManagementServer",
        "Links":  "https://dbatools.io/Copy-DbaCentralManagementServer",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaCentralManagementServer -Source sqlserver2014a -Destination sqlcluster\r\n\r\nAll groups, subgroups, and server instances are copied from sqlserver\u0027s Central Management Server to sqlcluster\u0027s \r\nCentral Management Server.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaCentralManagementServer -Source sqlserver2014a -Destination sqlcluster -ServerGroup Group1,Group3\r\n\r\nTop-level groups Group1 and Group3 along with their subgroups and server instances are copied from sqlserver to \r\nsqlcluster.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaCentralManagementServer -Source sqlserver2014a -Destination sqlcluster -ServerGroup Group1,Group3 \r\n-SwitchServerName -SourceSqlCredential $SourceSqlCredential -DestinationSqlCredential $DestinationSqlCredential\r\n\r\nTop-level groups Group1 and Group3 along with their subgroups and server instances are copied from sqlserver to \r\nsqlcluster. When adding sql instances to sqlcluster, if the server name of the migrating instance is \"sqlcluster\", it \r\nwill be switched to \"sqlserver\".\r\n\r\nIf SwitchServerName is not specified, \"sqlcluster\" will be skipped.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Copy-DbaCredential",
        "Description":  "By using password decryption techniques provided by Antti Rantasaari (NetSPI, 2014), this script migrates SQL Server Credentials from one server to another while maintaining username and password.\n\nCredit: https://blog.netspi.com/decrypting-mssql-database-link-server-passwords/\nLicense: BSD 3-Clause http://opensource.org/licenses/BSD-3-Clause",
        "Tags":  [
                     "WSMan",
                     "Migration"
                 ],
        "Author":  "Chrissy LeMaire (@cl), netnerds.net",
        "Synopsis":  "Copy-DbaCredential migrates SQL Server Credentials from one SQL Server to another while maintaining Credential passwords.",
        "Name":  "Copy-DbaCredential",
        "Links":  "https://dbatools.io/Copy-DbaCredential",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaCredential -Source sqlserver2014a -Destination sqlcluster\r\n\r\nCopies all SQL Server Credentials on sqlserver2014a to sqlcluster. If Credentials exist on destination, they will be \r\nskipped.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaCredential -Source sqlserver2014a -Destination sqlcluster -Name \"PowerShell Proxy Account\" -Force\r\n\r\nCopies over one SQL Server Credential (PowerShell Proxy Account) from sqlserver to sqlcluster. If the Credential \r\nalready exists on the destination, it will be dropped and recreated.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Copy-DbaCustomError",
        "Description":  "By default, all custom errors are copied. The -CustomError parameter is auto-populated for command-line completion and can be used to copy only specific custom errors.\n\nIf the custom error already exists on the destination, it will be skipped unless -Force is used. The us_english version must be created first. If you drop the us_english version, all the other languages will be dropped for that specific ID as well.",
        "Tags":  [
                     "Migration",
                     "CustomError"
                 ],
        "Author":  "Chrissy LeMaire (@cl), netnerds.net",
        "Synopsis":  "Copy-DbaCustomError migrates custom errors (user defined messages), by the custom error ID, from one SQL Server to another.",
        "Name":  "Copy-DbaCustomError",
        "Links":  "https://dbatools.io/Copy-DbaCustomError",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaCustomError -Source sqlserver2014a -Destination sqlcluster\r\n\r\nCopies all server custom errors from sqlserver2014a to sqlcluster using Windows credentials. If custom errors with the \r\nsame name exist on sqlcluster, they will be skipped.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaCustomError -Source sqlserver2014a -SourceSqlCredential $scred -Destination sqlcluster \r\n-DestinationSqlCredential $dcred -CustomError 60000 -Force\r\n\r\nCopies only the custom error with ID number 60000 from sqlserver2014a to sqlcluster using SQL credentials for \r\nsqlserver2014a and Windows credentials for sqlcluster. If a custom error with the same name exists on sqlcluster, it \r\nwill be updated because -Force was used.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaCustomError -Source sqlserver2014a -Destination sqlcluster -ExcludeCustomError 60000 -Force\r\n\r\nCopies all the custom errors found on sqlserver2014a except the custom error with ID number 60000 to sqlcluster. If a \r\ncustom error with the same name exists on sqlcluster, it will be updated because -Force was used.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaCustomError -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force\r\n\r\nShows what would happen if the command were executed using force.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Copy-DbaDatabase",
        "Description":  "This script provides the ability to migrate databases using detach/copy/attach or backup/restore. This script works with named instances, clusters and SQL Server Express Edition.\n\nBy default, databases will be migrated to the destination SQL Server\u0027s default data and log directories. You can override this by specifying -ReuseSourceFolderStructure. Filestreams and filegroups are also migrated. Safety is emphasized.",
        "Tags":  [
                     "Migration",
                     "Backup",
                     "Restore"
                 ],
        "Author":  "Chrissy LeMaire (@cl), netnerds.net",
        "Synopsis":  "Migrates SQL Server databases from one SQL Server to another.",
        "Name":  "Copy-DbaDatabase",
        "Links":  "https://dbatools.io/Copy-DbaDatabase",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaDatabase -Source sql2014a -Destination sql2014b -Database TestDB -BackupRestore -NetworkShare \r\n\\\\fileshare\\sql\\migration\r\n\r\nMigrates a single user database TestDB using Backup and restore from instance sql2014a to sql2014b. Backup files are \r\nstored in \\\\fileshare\\sql\\migration.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaDatabase -Source sql2012 -Destination sql2014, sql2016 -DetachAttach -Reattach\r\n\r\nDatabases will be migrated from sql2012 to both sql2014 and sql2016 using the detach/copy files/attach method.The \r\nfollowing will be performed: kick all users out of the database, detach all data/log files, move files across the \r\nnetwork over an admin share (\\\\SqlSERVER\\M$\\MSSql...), attach file on destination server, reattach at source. If the \r\ndatabase files (*.mdf, *.ndf, *.ldf) on *destination* exist and aren\u0027t in use, they will be overwritten.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaDatabase -Source sql2014a -Destination sqlcluster, sql2016 -BackupRestore -UseLastBackups -Force\r\n\r\nMigrates all user databases to sqlcluster and sql2016 using the last Full, Diff and Log backups from sql204a. If the \r\ndatabases exists on the destinations, they will be dropped prior to attach.\r\n\r\nNote that the backups must exist in a location accessible by all destination servers, such a network share.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaDatabase -Source sql2014a -Destination sqlcluster -ExcludeDatabase Northwind, pubs -IncludeSupportDbs \r\n-Force -BackupRestore -NetworkShare \\\\fileshare\\sql\\migration\r\n\r\nMigrates all user databases except for Northwind and pubs by using backup/restore (copy-only). Backup files are stored \r\nin \\\\fileshare\\sql\\migration. If the database exists on the destination, it will be dropped prior to attach.\r\n\r\nIt also includes the support databases (ReportServer, ReportServerTempDb, distribution).\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Copy-DbaDatabaseAssembly",
        "Description":  "By default, all assemblies are copied.\n\nIf the assembly already exists on the destination, it will be skipped unless -Force is used.\n\nThis script does not yet copy dependencies or dependent objects.",
        "Tags":  [
                     "Migration",
                     "Assembly"
                 ],
        "Author":  "Chrissy LeMaire (@cl), netnerds.net",
        "Synopsis":  "Copy-DbaDatabaseAssembly migrates assemblies from one SQL Server to another.",
        "Name":  "Copy-DbaDatabaseAssembly",
        "Links":  "http://dbatools.io/Get-SqlDatabaseAssembly",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaDatabaseAssembly -Source sqlserver2014a -Destination sqlcluster\r\n\r\nCopies all assemblies from sqlserver2014a to sqlcluster using Windows credentials. If assemblies with the same name \r\nexist on sqlcluster, they will be skipped.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaDatabaseAssembly -Source sqlserver2014a -Destination sqlcluster -Assembly dbname.assemblyname, \r\ndbname3.anotherassembly -SourceSqlCredential $cred -Force\r\n\r\nCopies two assemblies, the dbname.assemblyname and dbname3.anotherassembly from sqlserver2014a to sqlcluster using SQL \r\ncredentials for sqlserver2014a and Windows credentials for sqlcluster. If an assembly with the same name exists on \r\nsqlcluster, it will be dropped and recreated because -Force was used.\r\n\r\nIn this example, anotherassembly will be copied to the dbname3 database on the server sqlcluster.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaThing -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force\r\n\r\nShows what would happen if the command were executed using force.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Copy-DbaDatabaseMail",
        "Description":  "By default, all mail configurations for Profiles, Accounts, Mail Servers and Configs are copied.",
        "Tags":  [
                     "Migration",
                     "Mail"
                 ],
        "Author":  "Chrissy LeMaire (@cl), netnerds.net",
        "Synopsis":  "Migrates Mail Profiles, Accounts, Mail Servers and Mail Server Configs from one SQL Server to another.",
        "Name":  "Copy-DbaDatabaseMail",
        "Links":  "https://dbatools.io/Copy-DbaDatabaseMail",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaDatabaseMail -Source sqlserver2014a -Destination sqlcluster\r\n\r\nCopies all database mail objects from sqlserver2014a to sqlcluster using Windows credentials. If database mail objects \r\nwith the same name exist on sqlcluster, they will be skipped.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaDatabaseMail -Source sqlserver2014a -Destination sqlcluster -SourceSqlCredential $cred\r\n\r\nCopies all database mail objects from sqlserver2014a to sqlcluster using SQL credentials for sqlserver2014a and Windows \r\ncredentials for sqlcluster.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaDatabaseMail -Source sqlserver2014a -Destination sqlcluster -WhatIf\r\n\r\nShows what would happen if the command were executed.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaDatabaseMail -Source sqlserver2014a -Destination sqlcluster -EnableException\r\n\r\nPerforms execution of function, and will throw a terminating exception if something breaks\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Copy-DbaEndpoint",
        "Description":  "By default, all endpoints are copied.\n\nIf the endpoint already exists on the destination, it will be skipped unless -Force is used.",
        "Tags":  [
                     "Migration",
                     "Endpoint"
                 ],
        "Author":  "Chrissy LeMaire (@cl), netnerds.net",
        "Synopsis":  "Copy-DbaEndpoint migrates server endpoints from one SQL Server to another.",
        "Name":  "Copy-DbaEndpoint",
        "Links":  "https://dbatools.io/Copy-DbaEndpoint",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaEndpoint -Source sqlserver2014a -Destination sqlcluster\r\n\r\nCopies all server endpoints from sqlserver2014a to sqlcluster, using Windows credentials. If endpoints with the same \r\nname exist on sqlcluster, they will be skipped.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaEndpoint -Source sqlserver2014a -SourceSqlCredential $cred -Destination sqlcluster -Endpoint tg_noDbDrop \r\n-Force\r\n\r\nCopies only the tg_noDbDrop endpoint from sqlserver2014a to sqlcluster using SQL credentials for sqlserver2014a and \r\nWindows credentials for sqlcluster. If an endpoint with the same name exists on sqlcluster, it will be dropped and \r\nrecreated because -Force was used.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaEndpoint -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force\r\n\r\nShows what would happen if the command were executed using force.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Copy-DbaExtendedEvent",
        "Description":  "Migrates SQL Extended Event Sessions except the two default sessions, AlwaysOn_health and system_health.\n\nBy default, all non-system Extended Events are migrated.",
        "Tags":  [
                     "Migration",
                     "ExtendedEvent",
                     "XEvent"
                 ],
        "Author":  "Chrissy LeMaire (@cl), netnerds.net",
        "Synopsis":  "Migrates SQL Extended Event Sessions except the two default sessions, AlwaysOn_health and system_health.",
        "Name":  "Copy-DbaExtendedEvent",
        "Links":  "https://dbatools.io/Copy-DbaExtendedEvent",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaExtendedEvent -Source sqlserver2014a -Destination sqlcluster\r\n\r\nCopies all Extended Event sessions from sqlserver2014a to sqlcluster using Windows credentials.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaExtendedEvent -Source sqlserver2014a -Destination sqlcluster -SourceSqlCredential $cred\r\n\r\nCopies all Extended Event sessions from sqlserver2014a to sqlcluster using SQL credentials for sqlserver2014a and \r\nWindows credentials for sqlcluster.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaExtendedEvent -Source sqlserver2014a -Destination sqlcluster -WhatIf\r\n\r\nShows what would happen if the command were executed.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaExtendedEvent -Source sqlserver2014a -Destination sqlcluster -XeSession CheckQueries, \r\nMonitorUserDefinedException\r\n\r\nCopies only the Extended Events named CheckQueries and MonitorUserDefinedException from sqlserver2014a to sqlcluster.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Copy-DbaLinkedServer",
        "Description":  "By using password decryption techniques provided by Antti Rantasaari (NetSPI, 2014), this script migrates SQL Server Linked Servers from one server to another, while maintaining username and password.\n\nCredit: https://blog.netspi.com/decrypting-mssql-database-link-server-passwords/\nLicense: BSD 3-Clause http://opensource.org/licenses/BSD-3-Clause",
        "Tags":  [
                     "WSMan",
                     "Migration",
                     "LinkedServer"
                 ],
        "Author":  "Chrissy LeMaire (@cl), netnerds.net",
        "Synopsis":  "Copy-DbaLinkedServer migrates Linked Servers from one SQL Server to another. Linked Server logins and passwords are migrated as well.",
        "Name":  "Copy-DbaLinkedServer",
        "Links":  "https://dbatools.io/Copy-DbaLinkedServer",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaLinkedServer -Source sqlserver2014a -Destination sqlcluster\r\n\r\nDescription\r\nCopies all SQL Server Linked Servers on sqlserver2014a to sqlcluster. If Linked Server exists on destination, it will \r\nbe skipped.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaLinkedServer -Source sqlserver2014a -Destination sqlcluster -LinkedServer SQL2K5,SQL2k -Force\r\n\r\nDescription\r\nCopies over two SQL Server Linked Servers (SQL2K and SQL2K2) from sqlserver to sqlcluster. If the credential already \r\nexists on the destination, it will be dropped.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Copy-DbaLogin",
        "Description":  "SQL Server 2000: Migrates logins with SIDs, passwords, server roles and database roles.\n\nSQL Server 2005 \u0026 newer: Migrates logins with SIDs, passwords, defaultdb, server roles \u0026 securables, database permissions \u0026 securables, login attributes (enforce password policy, expiration, etc.)\n\nThe login hash algorithm changed in SQL Server 2012, and is not backwards compatible with previous SQL Server versions. This means that while SQL Server 2000 logins can be migrated to SQL Server 2012, logins created in SQL Server 2012 can only be migrated to SQL Server 2012 and above.",
        "Tags":  [
                     "Migration",
                     "Login"
                 ],
        "Author":  "Chrissy LeMaire (@cl), netnerds.net",
        "Synopsis":  "Migrates logins from source to destination SQL Servers. Supports SQL Server versions 2000 and newer.",
        "Name":  "Copy-DbaLogin",
        "Links":  "https://dbatools.io/Copy-DbaLogin",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaLogin -Source sqlserver2014a -Destination sqlcluster -Force\r\n\r\nCopies all logins from Source Destination. If a SQL Login on Source exists on the Destination, the Login on Destination \r\nwill be dropped and recreated.\r\n\r\nIf active connections are found for a login, the copy of that Login will fail as it cannot be dropped.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaLogin -Source sqlserver2014a -Destination sqlcluster -Force -KillActiveConnection\r\n\r\nCopies all logins from Source Destination. If a SQL Login on Source exists on the Destination, the Login on Destination \r\nwill be dropped and recreated.\r\n\r\nIf any active connections are found they will be killed.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaLogin -Source sqlserver2014a -Destination sqlcluster -Exclude realcajun -SourceSqlCredential $scred \r\n-DestinationSqlCredential $dcred\r\n\r\nCopies all Logins from Source to Destination except for realcajun using SQL Authentication to connect to both instances.\r\n\r\nIf a Login already exists on the destination, it will not be migrated.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaLogin -Source sqlserver2014a -Destination sqlcluster -Login realcajun, netnerds -force\r\n\r\nCopies ONLY Logins netnerds and realcajun. If Login realcajun or netnerds exists on Destination, the existing Login(s) \r\nwill be dropped and recreated.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaLogin -Source sqlserver2014a -Destination sqlcluster -SyncOnly\r\n\r\nSyncs only SQL Server login permissions, roles, etc. Does not add or drop logins or users.\r\n\r\nIf a matching Login does not exist on Destination, the Login will be skipped.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaLogin -LoginRenameHashtable @{ \"OldUser\" =\"newlogin\" } -Source $Sql01 -Destination Localhost \r\n-SourceSqlCredential $sqlcred\r\n\r\nCopies OldUser and then renames it to newlogin.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 7 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaLogin -SqlInstance sql2016 | Out-GridView -Passthru | Copy-DbaLogin -Destination sql2017\r\n\r\nDisplays all available logins on sql2016 in a grid view, then copies all selected logins to sql2017.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Copy-DbaQueryStoreConfig",
        "Description":  "Copies the configuration of a Query Store enabled database and sets the copied configuration on other databases.",
        "Tags":  "QueryStore",
        "Author":  "Enrico van de Laar ( @evdlaar )",
        "Synopsis":  "Copies the configuration of a Query Store enabled database and sets the copied configuration on other databases.",
        "Name":  "Copy-DbaQueryStoreConfig",
        "Links":  "https://dbatools.io/Copy-QueryStoreConfig",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaQueryStoreConfig -Source ServerA\\SQL -SourceDatabase AdventureWorks -Destination ServerB\\SQL \r\n-AllDatabases\r\n\r\nCopy the Query Store configuration of the AdventureWorks database in the ServerA\\SQL instance and apply it on all user \r\ndatabases in the ServerB\\SQL Instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaQueryStoreConfig -Source ServerA\\SQL -SourceDatabase AdventureWorks -Destination ServerB\\SQL \r\n-DestinationDatabase WorldWideTraders\r\n\r\nCopy the Query Store configuration of the AdventureWorks database in the ServerA\\SQL instance and apply it to the \r\nWorldWideTraders database in the ServerB\\SQL Instance.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Copy-DbaResourceGovernor",
        "Description":  "By default, all non-system resource pools are migrated. If the pool already exists on the destination, it will be skipped unless -Force is used.\n\nThe -ResourcePool parameter is auto-populated for command-line completion and can be used to copy only specific objects.",
        "Tags":  [
                     "Migration",
                     "ResourceGovernor"
                 ],
        "Author":  "Chrissy LeMaire (@cl), netnerds.net",
        "Synopsis":  "Migrates Resource Pools",
        "Name":  "Copy-DbaResourceGovernor",
        "Links":  "https://dbatools.io/Copy-DbaResourceGovernor",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaResourceGovernor -Source sqlserver2014a -Destination sqlcluster\r\n\r\nCopies all extended event policies from sqlserver2014a to sqlcluster using Windows credentials to connect to the SQL \r\nServer instances..\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaResourceGovernor -Source sqlserver2014a -Destination sqlcluster -SourceSqlCredential $cred\r\n\r\nCopies all extended event policies from sqlserver2014a to sqlcluster using SQL credentials to connect to sqlserver2014a \r\nand Windows credentials to connect to sqlcluster.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaResourceGovernor -Source sqlserver2014a -Destination sqlcluster -WhatIf\r\n\r\nShows what would happen if the command were executed.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Copy-DbaServerAudit",
        "Description":  "By default, all audits are copied. The -Audit parameter is auto-populated for command-line completion and can be used to copy only specific audits.\n\nIf the audit already exists on the destination, it will be skipped unless -Force is used.",
        "Tags":  "Migration",
        "Author":  "Chrissy LeMaire (@cl), netnerds.net",
        "Synopsis":  "Copy-DbaServerAudit migrates server audits from one SQL Server to another.",
        "Name":  "Copy-DbaServerAudit",
        "Links":  "https://dbatools.io/Copy-DbaServerAudit",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaServerAudit -Source sqlserver2014a -Destination sqlcluster\r\n\r\nCopies all server audits from sqlserver2014a to sqlcluster, using Windows credentials. If audits with the same name \r\nexist on sqlcluster, they will be skipped.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaServerAudit -Source sqlserver2014a -Destination sqlcluster -Audit tg_noDbDrop -SourceSqlCredential $cred \r\n-Force\r\n\r\nCopies a single audit, the tg_noDbDrop audit from sqlserver2014a to sqlcluster, using SQL credentials for \r\nsqlserver2014a and Windows credentials for sqlcluster. If an audit with the same name exists on sqlcluster, it will be \r\ndropped and recreated because -Force was used.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaServerAudit -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force\r\n\r\nShows what would happen if the command were executed using force.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Copy-DbaServerAuditSpecification",
        "Description":  "By default, all audits are copied. The -AuditSpecification parameter is auto-populated for command-line completion and can be used to copy only specific audits.\n\nIf the audit specification already exists on the destination, it will be skipped unless -Force is used.",
        "Tags":  [
                     "Migration",
                     "ServerAudit",
                     "AuditSpecification"
                 ],
        "Author":  "Chrissy LeMaire (@cl), netnerds.net",
        "Synopsis":  "Copy-DbaServerAuditSpecification migrates server audit specifications from one SQL Server to another.",
        "Name":  "Copy-DbaServerAuditSpecification",
        "Links":  "https://dbatools.io/Copy-DbaServerAuditSpecification",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaServerAuditSpecification -Source sqlserver2014a -Destination sqlcluster\r\n\r\nCopies all server audits from sqlserver2014a to sqlcluster using Windows credentials to connect. If audits with the \r\nsame name exist on sqlcluster, they will be skipped.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaServerAuditSpecification -Source sqlserver2014a -Destination sqlcluster -ServerAuditSpecification \r\ntg_noDbDrop -SourceSqlCredential $cred -Force\r\n\r\nCopies a single audit, the tg_noDbDrop audit from sqlserver2014a to sqlcluster using SQL credentials to connect to \r\nsqlserver2014a and Windows credentials to connect to sqlcluster. If an audit specification with the same name exists on \r\nsqlcluster, it will be dropped and recreated because -Force was used.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaServerAuditSpecification -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force\r\n\r\nShows what would happen if the command were executed using force.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Copy-DbaServerTrigger",
        "Description":  "By default, all triggers are copied. The -ServerTrigger parameter is auto-populated for command-line completion and can be used to copy only specific triggers.\n\nIf the trigger already exists on the destination, it will be skipped unless -Force is used.",
        "Tags":  "Migration",
        "Author":  "Chrissy LeMaire (@cl), netnerds.net",
        "Synopsis":  "Copy-DbaServerTrigger migrates server triggers from one SQL Server to another.",
        "Name":  "Copy-DbaServerTrigger",
        "Links":  "https://dbatools.io/Copy-DbaServerTrigger",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaServerTrigger -Source sqlserver2014a -Destination sqlcluster\r\n\r\nCopies all server triggers from sqlserver2014a to sqlcluster, using Windows credentials. If triggers with the same name \r\nexist on sqlcluster, they will be skipped.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaServerTrigger -Source sqlserver2014a -Destination sqlcluster -ServerTrigger tg_noDbDrop \r\n-SourceSqlCredential $cred -Force\r\n\r\nCopies a single trigger, the tg_noDbDrop trigger from sqlserver2014a to sqlcluster, using SQL credentials for \r\nsqlserver2014a and Windows credentials for sqlcluster. If a trigger with the same name exists on sqlcluster, it will be \r\ndropped and recreated because -Force was used.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaServerTrigger -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force\r\n\r\nShows what would happen if the command were executed using force.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Copy-DbaSpConfigure",
        "Description":  "By default, all configuration values are copied. The -ConfigName parameter is auto-populated for command-line completion and can be used to copy only specific configs.",
        "Tags":  [
                     "Migration",
                     "Configure",
                     "SpConfigure"
                 ],
        "Author":  "Chrissy LeMaire (@cl), netnerds.net",
        "Synopsis":  "Copy-DbaSpConfigure migrates configuration values from one SQL Server to another.",
        "Name":  "Copy-DbaSpConfigure",
        "Links":  "https://dbatools.io/Copy-DbaSpConfigure",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaSpConfigure -Source sqlserver2014a -Destination sqlcluster\r\n\r\nCopies all sp_configure settings from sqlserver2014a to sqlcluster\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaSpConfigure -Source sqlserver2014a -Destination sqlcluster -ConfigName DefaultBackupCompression, \r\nIsSqlClrEnabled -SourceSqlCredential $cred -Force\r\n\r\nCopies the values for IsSqlClrEnabled and DefaultBackupCompression from sqlserver2014a to sqlcluster using SQL \r\ncredentials to authenticate to sqlserver2014a and Windows credentials to authenticate to sqlcluster.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaSpConfigure -Source sqlserver2014a -Destination sqlcluster -ExcludeConfigName DefaultBackupCompression, \r\nIsSqlClrEnabled\r\n\r\nCopies all configs except for IsSqlClrEnabled and DefaultBackupCompression, from sqlserver2014a to sqlcluster.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaSpConfigure -Source sqlserver2014a -Destination sqlcluster -WhatIf\r\n\r\nShows what would happen if the command were executed.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Copy-DbaSqlDataCollector",
        "Description":  "By default, all data collector objects are migrated. If the object already exists on the destination, it will be skipped unless -Force is used.\n\nThe -CollectionSet parameter is auto-populated for command-line completion and can be used to copy only specific objects.",
        "Tags":  [
                     "Migration",
                     "DataCollection"
                 ],
        "Author":  "Chrissy LeMaire (@cl), netnerds.net",
        "Synopsis":  "Migrates user SQL Data Collector collection sets. SQL Data Collector configuration is on the agenda, but it\u0027s hard.",
        "Name":  "Copy-DbaSqlDataCollector",
        "Links":  "https://dbatools.io/Copy-DbaSqlDataCollector",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaSqlDataCollector -Source sqlserver2014a -Destination sqlcluster\r\n\r\nCopies all Data Collector Objects and Configurations from sqlserver2014a to sqlcluster, using Windows credentials.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaSqlDataCollector -Source sqlserver2014a -Destination sqlcluster -SourceSqlCredential $cred\r\n\r\nCopies all Data Collector Objects and Configurations from sqlserver2014a to sqlcluster, using SQL credentials for \r\nsqlserver2014a and Windows credentials for sqlcluster.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaSqlDataCollector -Source sqlserver2014a -Destination sqlcluster -WhatIf\r\n\r\nShows what would happen if the command were executed.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaSqlDataCollector -Source sqlserver2014a -Destination sqlcluster -CollectionSet \u0027Server Activity\u0027, \u0027Table \r\nUsage Analysis\u0027\r\n\r\nCopies two Collection Sets, Server Activity and Table Usage Analysis, from sqlserver2014a to sqlcluster.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Copy-DbaSqlPolicyManagement",
        "Description":  "By default, all policies and conditions are copied. If an object already exist on the destination, it will be skipped unless -Force is used.\n\nThe -Policy and -Condition parameters are auto-populated for command-line completion and can be used to copy only specific objects.",
        "Tags":  "Migration",
        "Author":  "Chrissy LeMaire (@cl), netnerds.net",
        "Synopsis":  "Migrates SQL Policy Based Management Objects, including both policies and conditions.",
        "Name":  "Copy-DbaSqlPolicyManagement",
        "Links":  "https://dbatools.io/Copy-DbaSqlPolicyManagement",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaSqlPolicyManagement -Source sqlserver2014a -Destination sqlcluster\r\n\r\nCopies all policies and conditions from sqlserver2014a to sqlcluster, using Windows credentials.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaSqlPolicyManagement -Source sqlserver2014a -Destination sqlcluster -SourceSqlCredential $cred\r\n\r\nCopies all policies and conditions from sqlserver2014a to sqlcluster, using SQL credentials for sqlserver2014a and \r\nWindows credentials for sqlcluster.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaSqlPolicyManagement -Source sqlserver2014a -Destination sqlcluster -WhatIf\r\n\r\nShows what would happen if the command were executed.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaSqlPolicyManagement -Source sqlserver2014a -Destination sqlcluster -Policy \u0027xp_cmdshell must be disabled\u0027\r\n\r\nCopies only one policy, \u0027xp_cmdshell must be disabled\u0027 from sqlserver2014a to sqlcluster. No conditions are migrated.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Copy-DbaSqlServerAgent",
        "Description":  "A wrapper function that calls the associated Copy command for each of the object types seen in SSMS under SQL Server Agent. This also copies all of the the SQL Agent properties (job history max rows, DBMail profile name, etc.).\n\nYou must have sysadmin access and server version must be SQL Server version 2000 or greater.",
        "Tags":  [
                     "Migration",
                     "SqlServerAgent",
                     "SqlAgent"
                 ],
        "Author":  "Chrissy LeMaire (@cl), netnerds.net",
        "Synopsis":  "Copy SQL Server Agent from one server to another.",
        "Name":  "Copy-DbaSqlServerAgent",
        "Links":  "https://dbatools.io/Copy-DbaSqlServerAgent",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaSqlServerAgent -Source sqlserver2014a -Destination sqlcluster\r\n\r\nCopies all job server objects from sqlserver2014a to sqlcluster using Windows credentials for authentication. If job \r\nobjects with the same name exist on sqlcluster, they will be skipped.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaSqlServerAgent -Source sqlserver2014a -Destination sqlcluster -SourceSqlCredential $cred\r\n\r\nCopies all job objects from sqlserver2014a to sqlcluster using SQL credentials to authentication to sqlserver2014a and \r\nWindows credentials to authenticate to sqlcluster.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaSqlServerAgent -Source sqlserver2014a -Destination sqlcluster -WhatIf\r\n\r\nShows what would happen if the command were executed.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Copy-DbaSsisCatalog",
        "Description":  "By default, all folders, projects, and environments are copied. The -Project parameter can be specified to copy only one project, if desired.\n\nThe parameters get more granular from the Folder level. For example, specifying -Folder will only deploy projects/environments from within that folder.",
        "Tags":  [
                     "Migration",
                     "SSIS"
                 ],
        "Author":  "Phil Schwartz (philschwartz.me, @pschwartzzz)",
        "Synopsis":  "Copy-DbaSsisCatalog migrates Folders, SSIS projects, and environments from one SQL Server to another.",
        "Name":  "Copy-DbaSsisCatalog",
        "Links":  "https://dbatools.io/Copy-DbaSsisCatalog",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaSsisCatalog -Source sqlserver2014a -Destination sqlcluster\r\n\r\nCopies all folders, environments and SSIS Projects from sqlserver2014a to sqlcluster, using Windows credentials to \r\nauthenticate to both instances. If folders with the same name exist on the destination they will be skipped, but \r\nprojects will be redeployed.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaSsisCatalog -Source sqlserver2014a -Destination sqlcluster -Project Archive_Tables -SourceSqlCredential \r\n$cred -Force\r\n\r\nCopies a single Project, the Archive_Tables Project, from sqlserver2014a to sqlcluster using SQL credentials to \r\nauthenticate to sqlserver2014a and Windows credentials to authenticate to sqlcluster. If a Project with the same name \r\nexists on sqlcluster, it will be deleted and recreated because -Force was used.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaSsisCatalog -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force\r\n\r\nShows what would happen if the command were executed using force.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003e$SecurePW = Read-Host \"Enter password\" -AsSecureString\r\n\r\nCopy-DbaSsisCatalog -Source sqlserver2014a -Destination sqlcluster -CreateCatalogPassword $SecurePW\r\n\r\nDeploy entire SSIS catalog to an instance without a destination catalog. User prompts for creating the catalog on \r\nDestination will be bypassed.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Copy-DbaSysDbUserObject",
        "Description":  "Imports all user objects found in source SQL Server\u0027s master, msdb and model databases to the destination. This is useful because many DBAs store backup/maintenance procs/tables/triggers/etc (among other things) in master or msdb.\n\nIt is also useful for migrating objects within the model database.",
        "Tags":  [
                     "Migration",
                     "SystemDatabase",
                     "UserObject"
                 ],
        "Synopsis":  "Imports all user objects found in source SQL Server\u0027s master, msdb and model databases to the destination.",
        "Name":  "Copy-DbaSysDbUserObject",
        "Links":  "https://dbatools.io/Copy-DbaSysDbUserObject",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaSysDbUserObject $sourceServer $destserver\r\n\r\nCopies user objects from source to destination\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Copy-DbaTableData",
        "Description":  "Copies data between SQL Server tables using SQL Bulk Copy.\nThe same can be achieved also doing\n    $sourcetable = Invoke-SqlCmd2 -ServerInstance instance1 ... -As DataTable\n    Write-DbaDataTable -SqlInstance ... -InputObject $sourcetable\nbut it will force buffering the contents on the table in memory (high RAM usage for large tables).\nWith this function, a streaming copy will be done in the most speedy and least resource-intensive way.",
        "Tags":  "Migration",
        "Author":  "niphlod (Simone Bizzotto)",
        "Synopsis":  "Copies data between SQL Server tables.",
        "Name":  "Copy-DbaTableData",
        "Links":  "https://dbatools.io/Copy-DbaTableData",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaTableData -SqlInstance sql1 -Destination sql2 -Database dbatools_from -Table test_table\r\n\r\nCopies all the data from sql1 to sql2, using the database dbatools_from.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaTableData -SqlInstance sql1 -Destination sql2 -Database dbatools_from -DestinationDatabase dbatools_dest \r\n-Table test_table\r\n\r\nCopies all the data from sql1 to sql2, using the database dbatools_from as source and dbatools_dest as destination\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaTable -SqlInstance sql1 -Database tempdb -Table tb1, tb2 | Copy-DbaTableData -DestinationTable tb3\r\n\r\nCopies all data from tables tb1 and tb2 in tempdb on sql1 to tb3 in tempdb onsql1\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaTable -SqlInstance sql1 -Database tempdb -Table tb1, tb2 | Copy-DbaTableData -Destination sql2\r\n\r\nCopies data from tbl1 in tempdb on sql1 to tbl1 in tempdb on sql2\r\nthen\r\nCopies data from tbl2 in tempdb on sql1 to tbl2 in tempdb on sql2\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaTableData -SqlInstance sql1 -Destination sql2 -Database dbatools_from -Table test_table\r\n\r\nCopies all the data from sql1 to sql2, using the database dbatools_from.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaTableData -SqlInstance sql1 -Destination sql2 -Database dbatools_from -Table test_table -KeepIdentity \r\n-Truncate\r\n\r\nCopies all the data from sql1 to sql2, using the database dbatools_from, keeping identity columns and truncating the \r\ndestination\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 7 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaTableData -SqlInstance sql1 -Destination sql2 -Database dbatools_from -Table test_table -KeepIdentity \r\n-Truncate\r\n\r\nCopies all the data from sql1 to sql2, using the database dbatools_from, keeping identity columns and truncating the \r\ndestination\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Copy-DbaXESessionTemplate",
        "Description":  "Copies non-Microsoft templates from the dbatools template repository (\\bin\\xetemplates\\) to $home\\Documents\\SQL Server Management Studio\\Templates\\XEventTemplates.\n\nUseful for when you want to use the SSMS GUI.",
        "Tags":  [
                     "ExtendedEvent",
                     "XE",
                     "XEvent"
                 ],
        "Synopsis":  "Copies non-Microsoft templates from the dbatools template repository (\\bin\\xetemplates\\) to $home\\Documents\\SQL Server Management Studio\\Templates\\XEventTemplates.",
        "Name":  "Copy-DbaXESessionTemplate",
        "Links":  "https://dbatools.io/Copy-DbaXESessionTemplate",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaXESessionTemplate\r\n\r\nCopies non-Microsoft templates from the dbatools template repository (\\bin\\xetemplates\\) to $home\\Documents\\SQL Server \r\nManagement Studio\\Templates\\XEventTemplates.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eCopy-DbaXESessionTemplate -Path C:\\temp\\xetemplates\r\n\r\nCopies your templates from C:\\temp\\xetemplates to $home\\Documents\\SQL Server Management \r\nStudio\\Templates\\XEventTemplates.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Disable-DbaAgHadr",
        "Description":  "In order to build an AG a cluster has to be built and then the Hadr enabled for the SQL Server\nservice. This function disables that feature for the SQL Server service.",
        "Tags":  [
                     "Hadr",
                     "AG",
                     "AvailabilityGroup"
                 ],
        "Author":  "Shawn Melton (@wsmelton | http://blog.wsmelton.info)",
        "Synopsis":  "Disables the Hadr service setting on the specified SQL Server.",
        "Name":  "Disable-DbaAgHadr",
        "Links":  "https://dbatools.io/Disable-DbaAgHadr",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eDisable-DbaAgHadr -SqlInstance sql2016 -Force\r\n\r\nSets Hadr service to disabled for the instance sql2016, and restart the service to apply the change.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eDisable-DbaAgHadr -SqlInstance sql2012\\dev1 -Force\r\n\r\nSets Hadr service to disabled for the instance dev1 on sq2012, and restart the service to apply the change.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Disable-DbaForceNetworkEncryption",
        "Description":  "Disables Force Encryption for a SQL Server instance. Note that this requires access to the Windows Server, not the SQL instance itself.\n\nThis setting is found in Configuration Manager.",
        "Tags":  "Certificate",
        "Synopsis":  "Disables Force Encryption for a SQL Server instance",
        "Name":  "Disable-DbaForceNetworkEncryption",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eDisable-DbaForceNetworkEncryption\r\n\r\nDisables Force Encryption on the default (MSSQLSERVER) instance on localhost - requires (and checks for) RunAs admin.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eDisable-DbaForceNetworkEncryption -SqlInstance sql01\\SQL2008R2SP2\r\n\r\nDisables Force Network Encryption for the SQL2008R2SP2 on sql01. Uses Windows Credentials to both login and modify the \r\nregistry.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eDisable-DbaForceNetworkEncryption -SqlInstance sql01\\SQL2008R2SP2 -WhatIf\r\n\r\nShows what would happen if the command were executed.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Disable-DbaTraceFlag",
        "Description":  "The function will disable a Trace Flag that is currently running globally on the SQL Server instance(s) listed",
        "Tags":  "TraceFlag",
        "Author":  "Garry Bargsley (@gbargsley), http://blog.garrybargsley.com",
        "Synopsis":  "Disable a Global Trace Flag that is currently running",
        "Name":  "Disable-DbaTraceFlag",
        "Links":  "https://dbatools.io/Disable-DbaTraceFlag",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eDisable-DbaTraceFlag -SqlInstance sql2016 -TraceFlag 3226\r\n\r\nDisable the globally running trace flag 3226 on SQL Server instance sql2016\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Dismount-DbaDatabase",
        "Description":  "This command detaches one or more SQL Server databases. If necessary, -Force can be used to break mirrors and remove databases from availability groups prior to detaching.",
        "Tags":  "Database",
        "Synopsis":  "Detach a SQL Server Database.",
        "Name":  "Dismount-DbaDatabase",
        "Links":  "https://dbatools.io/Dismount-DbaDatabase",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eDetach-DbaDatabase -SqlInstance sql2016b -Database SharePoint_Config, WSS_Logging\r\n\r\nDetaches SharePoint_Config and WSS_Logging from sql2016b\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabase -SqlInstance sql2016b -Database \u0027PerformancePoint Service \r\nApplication_10032db0fa0041df8f913f558a5dc0d4\u0027 | Detach-DbaDatabase -Force\r\n\r\nDetaches \u0027PerformancePoint Service Application_10032db0fa0041df8f913f558a5dc0d4\u0027 from sql2016b. Since Force was \r\nspecified, if the database is part of mirror, the mirror will be broken prior to detaching.\r\n\r\nIf the database is part of an Availability Group, it will first be dropped prior to detachment.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabase -SqlInstance sql2016b -Database WSS_Logging | Detach-DbaDatabase -Force -WhatIf\r\n\r\nShows what would happen if the command were to execute (without actually executing the detach/break/remove commands).\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Enable-DbaAgHadr",
        "Description":  "In order to build an AG a cluster has to be built and then the Hadr enabled for the SQL Server\nservice. This function enables that feature for the SQL Server service.",
        "Tags":  [
                     "Hadr",
                     "AG",
                     "AvailabilityGroup"
                 ],
        "Author":  "Shawn Melton (@wsmelton | http://blog.wsmelton.info)",
        "Synopsis":  "Enables the Hadr service setting on the specified SQL Server.",
        "Name":  "Enable-DbaAgHadr",
        "Links":  "https://dbatools.io/Enable-DbaAgHadr",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eEnable-DbaAgHadr -SqlInstance sql2016 -Force\r\n\r\nSets Hadr service to enabled for the instance sql2016, and restart the service to apply the change.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eEnable-DbaAgHadr -SqlInstance sql2012\\dev1 -Force\r\n\r\nSets Hadr service to disabled for the instance dev1 on sq2012, and restart the service to apply the change.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Enable-DbaForceNetworkEncryption",
        "Description":  "Enables Force Encryption for a SQL Server instance. Note that this requires access to the Windows Server, not the SQL instance itself.\n\nThis setting is found in Configuration Manager.",
        "Tags":  "Certificate",
        "Synopsis":  "Enables Force Encryption for a SQL Server instance.",
        "Name":  "Enable-DbaForceNetworkEncryption",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eEnable-DbaForceNetworkEncryption\r\n\r\nEnables Force Encryption on the default (MSSQLSERVER) instance on localhost. Requires (and checks for) RunAs admin.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eEnable-DbaForceNetworkEncryption -SqlInstance sql01\\SQL2008R2SP2\r\n\r\nEnables Force Network Encryption for the SQL2008R2SP2 on sql01. Uses Windows Credentials to both connect and modify the \r\nregistry.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eEnable-DbaForceNetworkEncryption -SqlInstance sql01\\SQL2008R2SP2 -WhatIf\r\n\r\nShows what would happen if the command were executed.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Enable-DbaTraceFlag",
        "Description":  "The function will set one or multiple trace flags on the SQL Server instance(s) listed",
        "Tags":  "TraceFlag",
        "Author":  "Garry Bargsley (@gbargsley), http://blog.garrybargsley.com",
        "Synopsis":  "Enable Global Trace Flag(s)",
        "Name":  "Enable-DbaTraceFlag",
        "Links":  "https://dbatools.io/Enable-DbaTraceFlag",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eEnable-DbaTraceFlag -SqlInstance sql2016 -TraceFlag 3226\r\n\r\nEnable the trace flag 3226 on SQL Server instance sql2016\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eEnable-DbaTraceFlag -SqlInstance sql2016 -TraceFlag 1117, 1118\r\n\r\nEnable multiple trace flags on SQL Server instance sql2016\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Expand-DbaTLogResponsibly",
        "Description":  "As you may already know, having a transaction log file with too many Virtual Log Files (VLFs) can hurt your database performance in many ways.\n\nExample:\n    Too many VLFs can cause transaction log backups to slow down and can also slow down database recovery and, in extreme cases, even impact insert/update/delete performance.\n\n    References:\n        http://www.sqlskills.com/blogs/kimberly/transaction-log-vlfs-too-many-or-too-few/\n        http://blogs.msdn.com/b/saponsqlserver/archive/2012/02/22/too-many-virtual-log-files-vlfs-can-cause-slow-database-recovery.aspx\n        http://www.brentozar.com/blitz/high-virtual-log-file-vlf-count/\n\n    In order to get rid of this fragmentation we need to grow the file taking the following into consideration:\n        - How many VLFs are created when we perform a grow operation or when an auto-grow is invoked?\n\n    Note: In SQL Server 2014 this algorithm has changed (http://www.sqlskills.com/blogs/paul/important-change-vlf-creation-algorithm-sql-server-2014/)\n\nAttention:\n    We are growing in MB instead of GB because of known issue prior to SQL 2012:\n        More detail here:\n            http://www.sqlskills.com/BLOGS/PAUL/post/Bug-log-file-growth-broken-for-multiples-of-4GB.aspx\n        and\n            http://connect.microsoft.com/SqlInstance/feedback/details/481594/log-growth-not-working-properly-with-specific-growth-sizes-vlfs-also-not-created-appropriately\n        or\n            https://connect.microsoft.com/SqlInstance/feedback/details/357502/transaction-log-file-size-will-not-grow-exactly-4gb-when-filegrowth-4gb\n\nUnderstanding related problems:\n        http://www.sqlskills.com/blogs/kimberly/transaction-log-vlfs-too-many-or-too-few/\n        http://blogs.msdn.com/b/saponsqlserver/archive/2012/02/22/too-many-virtual-log-files-vlfs-can-cause-slow-database-recovery.aspx\n        http://www.brentozar.com/blitz/high-virtual-log-file-vlf-count/\n\nKnown bug before SQL Server 2012\n        http://www.sqlskills.com/BLOGS/PAUL/post/Bug-log-file-growth-broken-for-multiples-of-4GB.aspx\n        http://connect.microsoft.com/SqlInstance/feedback/details/481594/log-growth-not-working-properly-with-specific-growth-sizes-vlfs-also-not-created-appropriately\n        https://connect.microsoft.com/SqlInstance/feedback/details/357502/transaction-log-file-size-will-not-grow-exactly-4gb-when-filegrowth-4gb\n\nHow it works?\n    The transaction log will grow in chunks until it reaches the desired size.\n    Example: If you have a log file with 8192MB and you say that the target size is 81920MB (80GB) it will grow in chunks of 8192MB until it reaches 81920MB. 8192 -\u003e 16384 -\u003e 24576 ... 73728 -\u003e 81920",
        "Tags":  [
                     "Storage",
                     "Backup"
                 ],
        "Author":  "Claudio Silva (@ClaudioESSilva)",
        "Synopsis":  "This command will help you to automatically grow your transaction log  file in a responsible way (preventing the generation of too many VLFs).",
        "Name":  "Expand-DbaTLogResponsibly",
        "Links":  "https://dbatools.io/Expand-DbaTLogResponsibly",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eExpand-DbaTLogResponsibly -SqlInstance sqlcluster -Database db1 -TargetLogSizeMB 50000\r\n\r\nGrows the transaction log for database db1 on sqlcluster to 50000 MB and calculates the increment size.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eExpand-DbaTLogResponsibly -SqlInstance sqlcluster -Database db1, db2 -TargetLogSizeMB 10000 -IncrementSizeMB 200\r\n\r\nGrows the transaction logs for databases db1 and db2 on sqlcluster to 1000MB and sets the growth increment to 200MB.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eExpand-DbaTLogResponsibly -SqlInstance sqlcluster -Database db1 -TargetLogSizeMB 10000 -LogFileId 9\r\n\r\nGrows the transaction log file  with FileId 9 of the db1 database on sqlcluster instance to 10000MB.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eExpand-DbaTLogResponsibly -SqlInstance sqlcluster -Database (Get-Content D:\\DBs.txt) -TargetLogSizeMB 50000\r\n\r\nGrows the transaction log of the databases specified in the file \u0027D:\\DBs.txt\u0027 on sqlcluster instance to 50000MB.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eExpand-DbaTLogResponsibly -SqlInstance SqlInstance -Database db1,db2 -TargetLogSizeMB 100 -IncrementSizeMB 10 \r\n-ShrinkLogFile -ShrinkSizeMB 10 -BackupDirectory R:\\MSSQL\\Backup\r\n\r\nGrows the transaction logs for databases db1 and db2 on SQL server SQLInstance to 100MB, sets the incremental growth to \r\n10MB, shrinks the transaction log to 10MB and uses the directory R:\\MSSQL\\Backup for the required backups.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Export-DbaAvailabilityGroup",
        "Description":  "Exports SQL Server Availability Groups creation scripts to a T-SQL file. This is a function that is not available in SSMS.",
        "Tags":  [
                     "Hadr",
                     "AG",
                     "AvailabilityGroup"
                 ],
        "Author":  "Chris Sommer (@cjsommer), cjsommer.com",
        "Synopsis":  "Exports SQL Server Availability Groups to a T-SQL file.",
        "Name":  "Export-DbaAvailabilityGroup",
        "Links":  "https://dbatools.io/Export-DbaAvailabilityGroup",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eExport-DbaAvailabilityGroup -SqlInstance sql2012\r\n\r\nExports all Availability Groups from SQL server \"sql2012\". Output scripts are written to the Documents\\SqlAgExports \r\ndirectory by default.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eExport-DbaAvailabilityGroup -SqlInstance sql2012 -FilePath C:\\temp\\availability_group_exports\r\n\r\nExports all Availability Groups from SQL server \"sql2012\". Output scripts are written to the \r\nC:\\temp\\availability_group_exports directory.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eExport-DbaAvailabilityGroup -SqlInstance sql2012 -FilePath \u0027C:\\dir with spaces\\availability_group_exports\u0027 \r\n-AvailabilityGroups AG1,AG2\r\n\r\nExports Availability Groups AG1 and AG2 from SQL server \"sql2012\". Output scripts are written to the C:\\dir with \r\nspaces\\availability_group_exports directory.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eExport-DbaAvailabilityGroup -SqlInstance sql2014 -FilePath C:\\temp\\availability_group_exports -NoClobber\r\n\r\nExports all Availability Groups from SQL server \"sql2014\". Output scripts are written to the \r\nC:\\temp\\availability_group_exports directory. If the export file already exists it will not be overwritten.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Export-DbaDacpac",
        "Description":  "Using SQLPackage, export a dacpac from an instance of SQL Server.\n\nNote - Extract from SQL Server is notoriously flaky - for example if you have three part references to external databases it will not work.\n\nFor help with the extract action parameters and properties, refer to https://msdn.microsoft.com/en-us/library/hh550080(v=vs.103).aspx",
        "Tags":  [
                     "Migration",
                     "Database",
                     "Dacpac"
                 ],
        "Author":  "Richie lee (@bzzzt_io)",
        "Synopsis":  "Exports a dacpac from a server.",
        "Name":  "Export-DbaDacpac",
        "Links":  "https://dbatools.io/Export-DbaDacpac",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eExport-DbaDacpac -SqlInstance sql2016 -Database SharePoint_Config\r\n\r\nExports the dacpac for SharePoint_Config on sql2016 to $home\\Documents\\SharePoint_Config.dacpac\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e$moreprops = \"/p:VerifyExtraction=$true /p:CommandTimeOut=10\"\r\n\r\nExport-DbaDacpac -SqlInstance sql2016 -Database SharePoint_Config -Path C:\\temp -ExtendedProperties $moreprops\r\n\r\nSets the CommandTimeout to 10 then extracts the dacpac for SharePoint_Config on sql2016 to \r\nC:\\temp\\SharePoint_Config.dacpac then verifies extraction.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Export-DbaDiagnosticQuery",
        "Description":  "The default output format of Invoke-DbaDiagnosticQuery is a custom object. It can also output to CSV and Excel.\nHowever, CSV output can generate a lot of files and Excel output depends on the ImportExcel module by Doug Fike (https://github.com/dfinke/ImportExcel)\nExport-DbaDiagnosticQuery can be used to convert from the default export type to the other available export types.",
        "Tags":  "Query",
        "Author":  "Andre Kamman (@AndreKamman), http://clouddba.io",
        "Synopsis":  "Export-DbaDiagnosticQuery can convert ouput generated by Invoke-DbaDiagnosticQuery to CSV or Excel",
        "Name":  "Export-DbaDiagnosticQuery",
        "Links":  "https://dbatools.io/Export-DbaDiagnosticQuery",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaDiagnosticQuery -SqlInstance sql2016 | Export-DbaDiagnosticQuery -Path c:\\temp\r\n\r\nConverts output from Invoke-DbaDiagnosticQuery to multiple CSV files\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e$output = Invoke-DbaDiagnosticQuery -SqlInstance sql2016\r\n\r\nExport-DbaDiagnosticQuery -InputObject $output -ConvertTo Excel\r\n\r\nConverts output from Invoke-DbaDiagnosticQuery to Excel worksheet(s) in the Documents folder\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Export-DbaExecutionPlan",
        "Description":  "Exports execution plans to disk. Can pipe from Export-DbaExecutionPlan\n\nThanks to\n    https://www.simple-talk.com/sql/t-sql-programming/dmvs-for-query-plan-metadata/\n    and\n    http://www.scarydba.com/2017/02/13/export-plans-cache-sqlplan-file/\nfor the idea and query.",
        "Tags":  [
                     "Performance",
                     "ExecutionPlan"
                 ],
        "Synopsis":  "Exports execution plans to disk.",
        "Name":  "Export-DbaExecutionPlan",
        "Links":  "https://dbatools.io/Export-DbaExecutionPlan",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eExport-DbaExecutionPlan -SqlInstance sqlserver2014a\r\n\r\nExports all execution plans for sqlserver2014a.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eExport-DbaExecutionPlan -SqlInstance sqlserver2014a -Database db1, db2 -SinceLastExecution \u00277/1/2016 10:47:00\u0027\r\n\r\nExports all execution plans for databases db1 and db2 on sqlserver2014a since July 1, 2016 at 10:47 AM.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Export-DbaLogin",
        "Description":  "Exports Windows and SQL Logins to a T-SQL file. Export includes login, SID, password, default database, default language, server permissions, server roles, db permissions, db roles.",
        "Tags":  [
                     "Export",
                     "Login"
                 ],
        "Author":  "Chrissy LeMaire (@cl), netnerds.net",
        "Synopsis":  "Exports Windows and SQL Logins to a T-SQL file. Export includes login, SID, password, default database, default language, server permissions, server roles, db permissions, db roles.",
        "Name":  "Export-DbaLogin",
        "Links":  "https://dbatools.io/Export-DbaLogin",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eExport-DbaLogin -SqlInstance sql2005 -FilePath C:\\temp\\sql2005-logins.sql\r\n\r\nExports the logins for SQL Server \"sql2005\" and writes them to the file \"C:\\temp\\sql2005-logins.sql\"\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eExport-DbaLogin -SqlInstance sqlserver2014a -Exclude realcajun -SqlCredential $scred -FilePath \r\nC:\\temp\\logins.sql -Append\r\n\r\nAuthenticates to sqlserver2014a using SQL Authentication. Exports all logins except for realcajun to \r\nC:\\temp\\logins.sql, and appends to the file if it exists. If not, the file will be created.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eExport-DbaLogin -SqlInstance sqlserver2014a -Login realcajun, netnerds -FilePath C:\\temp\\logins.sql\r\n\r\nExports ONLY logins netnerds and realcajun FROM sqlserver2014a to the file  C:\\temp\\logins.sql\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eExport-DbaLogin -SqlInstance sqlserver2014a -Login realcajun, netnerds -Database HR, Accounting\r\n\r\nExports ONLY logins netnerds and realcajun FROM sqlserver2014a with the permissions on databases HR and Accounting\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eExport-DbaLogin -SqlInstance sqlserver2008 -Login realcajun, netnerds -FilePath C:\\temp\\login.sql \r\n-ExcludeGoBatchSeparator\r\n\r\nExports ONLY logins netnerds and realcajun FROM sqlserver2008 server, to the C:\\temp\\login.sql file without the \u0027GO\u0027 \r\nbatch separator.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eExport-DbaLogin -SqlInstance sqlserver2008 -Login realcajun -FilePath C:\\temp\\users.sql -DestinationVersion \r\nSQLServer2016\r\n\r\nExports login realcajun fron sqlsever2008 to the file C:\\temp\\users.sql with sintax to run on SQL Server 2016\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Export-DbaPfDataCollectorSetTemplate",
        "Description":  "Exports a Data Collector Set XML Template from Get-DbaPfDataCollectorSet. Exports to \"$home\\Documents\\Performance Monitor Templates\" by default.",
        "Tags":  [
                     "Performance",
                     "DataCollector"
                 ],
        "Synopsis":  "Exports a new Data Collector Set XML Template.",
        "Name":  "Export-DbaPfDataCollectorSetTemplate",
        "Links":  "https://dbatools.io/Export-DbaPfDataCollectorSetTemplate",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eExport-DbaPfDataCollectorSetTemplate -ComputerName sql2017 -Path C:\\temp\\pf\r\n\r\nExports all data collector sets from to the C:\\temp\\pf folder.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfDataCollectorSet ComputerName sql2017 -CollectorSet \u0027System Correlation\u0027 | \r\nExport-DbaPfDataCollectorSetTemplate -Path C:\\temp\r\n\r\nExports the \u0027System Correlation\u0027 data collector set from sql2017 to C:\\temp.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Export-DbaRegisteredServer",
        "Description":  "Exports registered servers and registered server groups to file",
        "Tags":  [
                     "RegisteredServer",
                     "CMS"
                 ],
        "Author":  "Chrissy LeMaire (@cl)",
        "Synopsis":  "Exports registered servers and registered server groups to file",
        "Name":  "Export-DbaRegisteredServer",
        "Links":  "https://dbatools.io/Export-DbaRegisteredServer",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eExport-DbaRegisteredServer -SqlInstance sql2008\r\n\r\nExports all Registered Server and Registered Server Groups on sql2008 to an automatically generated file name in the \r\ncurrent directory\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eExport-DbaRegisteredServer -SqlInstance sql2008 -Group hr\\Seattle -Path C:\\temp\\Seattle.xml\r\n\r\nExports all Registered Server and Registered Server Groups with the Seattle group within the HR group on sql2008 to \r\nC:\\temp\\Seattle.xml\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRegisteredServer -SqlInstance sql2008, sql2012 | Export-DbaRegisteredServer\r\n\r\nExports all registered servers on sql2008 and sql2012. Warning - each one will have its own individual file. Consider \r\npiping groups.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRegisteredServerGroup -SqlInstance sql2008, sql2012 | Export-DbaRegisteredServer\r\n\r\nExports all registered servers on sql2008 and sql2012, organized by group.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Export-DbaScript",
        "Description":  "Exports scripts from SQL Management Objects",
        "Tags":  [
                     "Migration",
                     "Backup",
                     "Export"
                 ],
        "Synopsis":  "Exports scripts from SQL Management Objects (SMO)",
        "Name":  "Export-DbaScript",
        "Links":  "https://dbatools.io/Export-DbaScript",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentJob -SqlInstance sql2016 | Export-DbaScript\r\n\r\nExports all jobs on the SQL Server sql2016 instance using a trusted connection - automatically determines filename as \r\n.\\sql2016-Job-Export-date.sql\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentJob -SqlInstance sql2016 | Export-DbaScript -Path C:\\temp\\export.sql -Append\r\n\r\nExports all jobs on the SQL Server sql2016 instance using a trusted connection - Will append the output to the file \r\nC:\\temp\\export.sql if it already exists\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentJob -SqlInstance sql2016 -Job syspolicy_purge_history, \u0027Hourly Log Backups\u0027 -SqlCredential \r\n(Get-Credential sqladmin) | Export-DbaScript -Path C:\\temp\\export.sql\r\n\r\nExports only syspolicy_purge_history and \u0027Hourly Log Backups\u0027 to C:temp\\export.sql and uses the SQL login \"sqladmin\" to \r\nlogin to sql2016\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentJob -SqlInstance sql2014 | Export-DbaJob -Passthru | ForEach-Object { \r\n$_.Replace(\u0027sql2014\u0027,\u0027sql2016\u0027) } | Set-Content -Path C:\\temp\\export.sql\r\n\r\nExports jobs and replaces all instances of the servername \"sql2014\" with \"sql2016\" then writes to C:\\temp\\export.sql\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003e$options = New-DbaScriptingOption\r\n\r\n$options.ScriptDrops = $false\r\n$options.WithDependencies = $true\r\nGet-DbaTable -SqlInstance sql2017 -Database PerformanceStore | Export-DbaScript -ScriptingOptionsObject $options\r\n\r\nExports Agent Jobs with the Scripting Options ScriptDrops set to $false and WithDependencies set to $true.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Export-DbaSpConfigure",
        "Description":  "Exports advanced sp_configure global configuration options to sql file.",
        "Tags":  [
                     "SpConfig",
                     "Configure",
                     "Configuration"
                 ],
        "Synopsis":  "Exports advanced sp_configure global configuration options to sql file.",
        "Name":  "Export-DbaSpConfigure",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eExport-DbaSpConfigure -SqlInstance sourceserver -Path C:\\temp\\sp_configure.sql\r\n\r\nExports the SPConfigure settings on sourceserver to the file C:\\temp\\sp_configure.sql\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Export-DbaUser",
        "Description":  "Exports users creation and its permissions to a T-SQL file or host. Export includes user, create and add to role(s), database level permissions, object level permissions.",
        "Tags":  [
                     "User",
                     "Export"
                 ],
        "Author":  "Claudio Silva (@ClaudioESSilva)",
        "Synopsis":  "Exports users creation and its permissions to a T-SQL file or host.",
        "Name":  "Export-DbaUser",
        "Links":  "https://dbatools.io/Export-DbaUser",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eExport-DbaUser -SqlInstance sql2005 -FilePath C:\\temp\\sql2005-users.sql\r\n\r\nExports SQL for the users in server \"sql2005\" and writes them to the file \"C:\\temp\\sql2005-users.sql\"\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eExport-DbaUser -SqlInstance sqlserver2014a $scred -FilePath C:\\temp\\users.sql -Append\r\n\r\nAuthenticates to sqlserver2014a using SQL Authentication. Exports all users to C:\\temp\\users.sql, and appends to the \r\nfile if it exists. If not, the file will be created.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eExport-DbaUser -SqlInstance sqlserver2014a -User User1, User2 -FilePath C:\\temp\\users.sql\r\n\r\nExports ONLY users User1 and User2 fron sqlsever2014a to the file  C:\\temp\\users.sql\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eExport-DbaUser -SqlInstance sqlserver2008 -User User1 -FilePath C:\\temp\\users.sql -DestinationVersion \r\nSQLServer2016\r\n\r\nExports user User1 fron sqlsever2008 to the file C:\\temp\\users.sql with sintax to run on SQL Server 2016\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eExport-DbaUser -SqlInstance sqlserver2008 -Database db1,db2 -FilePath C:\\temp\\users.sql\r\n\r\nExports ONLY users from db1 and db2 database on sqlserver2008 server, to the C:\\temp\\users.sql file.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003e$options = New-DbaScriptingOption\r\n\r\n$options.ScriptDrops = $false\r\n$options.WithDependencies = $true\r\n\r\nExport-DbaUser -SqlInstance sqlserver2008 -Database db1,db2 -FilePath C:\\temp\\users.sql -ScriptingOptionsObject $options\r\n\r\nExports ONLY users from db1 and db2 database on sqlserver2008 server, to the C:\\temp\\users.sql file.\r\nIt will not script drops but will script dependencies.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 7 --------------------------\r\n\r\nPS C:\\\u003eExport-DbaUser -SqlInstance sqlserver2008 -Database db1,db2 -FilePath C:\\temp\\users.sql -ExcludeGoBatchSeparator\r\n\r\nExports ONLY users from db1 and db2 database on sqlserver2008 server, to the C:\\temp\\users.sql file without the \u0027GO\u0027 \r\nbatch separator.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Export-DbaXECsv",
        "Description":  "Exports Extended Events to a CSV file.",
        "Tags":  [
                     "ExtendedEvent",
                     "XE",
                     "XEvent"
                 ],
        "Author":  "Gianluca Sartori (@spaghettidba)",
        "Synopsis":  "Exports Extended Events to a CSV file.",
        "Name":  "Export-DbaXECsv",
        "Links":  "https://dbatools.io/Export-DbaXECsv",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-ChildItem -Path C:\\temp\\sample.xel | Export-DbaXECsv -Path c:\\temp\\sample.csv\r\n\r\nWrites Extended Events data to the file \"C:\\temp\\events.csv\".\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaXESession -SqlInstance sql2014 -Session deadlocks | Export-DbaXECsv -Path c:\\temp\\events.csv\r\n\r\nWrites Extended Events data to the file \"C:\\temp\\events.csv\".\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Export-DbaXESessionTemplate",
        "Description":  "Exports an XESession XML Template either from the dbatools repository or a file you specify. Exports to \"$home\\Documents\\SQL Server Management Studio\\Templates\\XEventTemplates\" by default",
        "Tags":  [
                     "ExtendedEvent",
                     "XE",
                     "XEvent"
                 ],
        "Synopsis":  "Exports an XESession XML Template.",
        "Name":  "Export-DbaXESessionTemplate",
        "Links":  "https://dbatools.io/Export-DbaXESessionTemplate",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eExport-DbaXESessionTemplate -SqlInstance sql2017 -Path C:\\temp\\xe\r\n\r\nExports XE Session Template to the C:\\temp\\xe folder.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaXESession -SqlInstance sql2017 -Session session_health | Export-DbaXESessionTemplate -Path C:\\temp\r\n\r\nReturns a new XE Session object from sql2017 then adds an event, an action then creates it.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Find-DbaAgentJob",
        "Description":  "This command filters SQL Agent jobs giving the DBA a list of jobs that may need attention or could possibly be options for removal.",
        "Tags":  [
                     "Agent",
                     "Job"
                 ],
        "Author":  "Stephen Bennett (https://sqlnotesfromtheunderground.wordpress.com/)",
        "Synopsis":  "Find-DbaAgentJob finds agent job/s that fit certain search filters.",
        "Name":  "Find-DbaAgentJob",
        "Links":  "https://dbatools.io/Find-DbaAgentJob",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaAgentJob -SqlInstance Dev01 -JobName backup*\r\n\r\nReturns all agent job(s) that have backup in the name\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaAgentJob -SqlInstance Dev01, Dev02 -JobName Mybackup\r\n\r\nReturns all agent job(s) that are named exactly Mybackup\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaAgentJob -SqlInstance Dev01 -LastUsed 10\r\n\r\nReturns all agent job(s) that have not ran in 10 days\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaAgentJob -SqlInstance Dev01 -IsDisabled -IsNoEmailNotification -IsNotScheduled\r\n\r\nReturns all agent job(s) that are either disabled, have no email notification or don\u0027t have a schedule. returned with \r\ndetail\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003e$servers | Find-DbaAgentJob -IsFailed | Start-DbaAgentJob\r\n\r\nFinds all failed job then starts them. Consider using a -WhatIf at the end of Start-DbaAgentJob to see what it\u0027ll do \r\nfirst\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaAgentJob -SqlInstance Dev01 -LastUsed 10 -Exclude \"Yearly - RollUp Workload\", \"SMS - Notification\"\r\n\r\nReturns all agent jobs that havent ran in the last 10 ignoring jobs \"Yearly - RollUp Workload\" and \"SMS - Notification\"\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 7 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaAgentJob -SqlInstance Dev01 -Category \"REPL-Distribution\", \"REPL-Snapshot\" -Detailed | Format-Table \r\n-AutoSize -Wrap\r\n\r\nReturns all job/s on Dev01 that are in either category \"REPL-Distribution\" or \"REPL-Snapshot\" with detailed output\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 8 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaAgentJob -SqlInstance Dev01, Dev02 -IsFailed -Since \u00277/1/2016 10:47:00\u0027\r\n\r\nReturns all agent job(s) that have failed since July of 2016 (and still have history in msdb)\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 9 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRegisteredServer -SqlInstance CMSServer -Group Production | Find-DbaAgentJob -Disabled -IsNotScheduled | \r\nFormat-Table -AutoSize -Wrap\r\n\r\nQueries CMS server to return all SQL instances in the Production folder and then list out all agent jobs that have \r\neither been disabled or have no schedule.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Find-DbaBackup",
        "Description":  "Provides all of the same functionality for finding SQL backups to remove from disk as a standard maintenance plan would.\n\nAs an addition you have the ability to check the Archive bit on files before deletion. This will allow you to ensure backups have been archived to your archive location before removal.",
        "Tags":  "Backup",
        "Author":  "Chris Sommer, @cjsommer, www.cjsommer.com",
        "Synopsis":  "Finds SQL Server backups on disk.",
        "Name":  "Find-DbaBackup",
        "Links":  "https://dbatools.io/Find-DbaBackup",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaBackup -Path \u0027C:\\MSSQL\\SQL Backup\\\u0027 -BackupFileExtension trn -RetentionPeriod 48h\r\n\r\n\u0027*.trn\u0027 files in \u0027C:\\MSSQL\\SQL Backup\\\u0027 and all subdirectories that are more than 48 hours old will be included.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaBackup -Path \u0027C:\\MSSQL\\Backup\\\u0027 -BackupFileExtension bak -RetentionPeriod 7d -CheckArchiveBit\r\n\r\n\u0027*.bak\u0027 files in \u0027C:\\MSSQL\\Backup\\\u0027 and all subdirectories that are more than 7 days old will be included, but only if \r\nthe files have been backed up to another location as verified by checking the Archive bit.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Find-DbaCommand",
        "Description":  "Finds dbatools commands searching through the inline help text, building a consolidated json index and querying it because Get-Help is too slow",
        "Tags":  [
                     "Find",
                     "Help",
                     "Command"
                 ],
        "Author":  "Simone Bizzotto",
        "Synopsis":  "Finds dbatools commands searching through the inline help text",
        "Name":  "Find-DbaCommand",
        "Links":  "https://dbatools.io/Find-DbaCommand",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaCommand \"snapshot\"\r\n\r\nFor lazy typers: finds all commands searching the entire help for \"snapshot\"\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaCommand -Pattern \"snapshot\"\r\n\r\nFor rigorous typers: finds all commands searching the entire help for \"snapshot\"\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaCommand -Tag copy\r\n\r\nFinds all commands tagged with \"copy\"\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaCommand -Tag copy,user\r\n\r\nFinds all commands tagged with BOTH \"copy\" and \"user\"\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaCommand -Author chrissy\r\n\r\nFinds every command whose author contains our beloved \"chrissy\"\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaCommand -Author chrissy -Tag copy\r\n\r\nFinds every command whose author contains our beloved \"chrissy\" and it tagged as \"copy\"\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 7 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaCommand -Pattern snapshot -Rebuild\r\n\r\nFinds all commands searching the entire help for \"snapshot\", rebuilding the index (good for developers)\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Find-DbaDatabase",
        "Description":  "Allows you to search SQL Server instances for database that have either the same name, owner or service broker guid.\n\nThere a several reasons for the service broker guid not matching on a restored database primarily using alter database new broker. or turn off broker to return a guid of 0000-0000-0000-0000.",
        "Tags":  "Database",
        "Author":  "Stephen Bennett: https://sqlnotesfromtheunderground.wordpress.com/",
        "Synopsis":  "Find database/s on multiple servers that match criteria you input",
        "Name":  "Find-DbaDatabase",
        "Links":  "https://dbatools.io/Find-DbaDatabase",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaDatabase -SqlInstance \"DEV01\", \"DEV02\", \"UAT01\", \"UAT02\", \"PROD01\", \"PROD02\" -Pattern Report\r\n\r\nReturns all database from the SqlInstances that have a database with Report in the name\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaDatabase -SqlInstance \"DEV01\", \"DEV02\", \"UAT01\", \"UAT02\", \"PROD01\", \"PROD02\" -Pattern TestDB -Exact | \r\nSelect-Object *\r\n\r\nReturns all database from the SqlInstances that have a database named TestDB with a detailed output.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaDatabase -SqlInstance \"DEV01\", \"DEV02\", \"UAT01\", \"UAT02\", \"PROD01\", \"PROD02\" -Property ServiceBrokerGuid \r\n-Pattern \u0027-faeb-495a-9898-f25a782835f5\u0027 | Select-Object *\r\n\r\nReturns all database from the SqlInstances that have the same Service Broker GUID with a deatiled output\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Find-DbaDbGrowthEvent",
        "Description":  "Finds any database AutoGrow events in the Default Trace.\n\nThe following events are included:\n    92 - Data File Auto Grow\n    93 - Log File Auto Grow\n    94 - Data File Auto Shrink\n    95 - Log File Auto Shrink",
        "Tags":  [
                     "AutoGrow",
                     "Growth",
                     "Database"
                 ],
        "Author":  "Aaron Nelson",
        "Synopsis":  "Finds any database AutoGrow events in the Default Trace.",
        "Name":  "Find-DbaDbGrowthEvent",
        "Links":  "https://dbatools.io/Find-DbaDatabaseGrowthEvent",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaDatabaseGrowthEvent -SqlInstance localhost\r\n\r\nReturns any database AutoGrow events in the Default Trace with UTC time for the instance for every database on the \r\nlocalhost instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaDatabaseGrowthEvent -SqlInstance localhost -UseLocalTime\r\n\r\nReturns any database AutoGrow events in the Default Trace with the local time of the instance for every database on the \r\nlocalhost instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaDatabaseGrowthEvent -SqlInstance ServerA\\SQL2016, ServerA\\SQL2014\r\n\r\nReturns any database AutoGrow events in the Default Traces for every database on ServerA\\sql2016 \u0026 ServerA\\SQL2014.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaDatabaseGrowthEvent -SqlInstance ServerA\\SQL2016 | Format-Table -AutoSize -Wrap\r\n\r\nReturns any database AutoGrow events in the Default Trace for every database on the ServerA\\SQL2016 instance in a table \r\nformat.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaDatabaseGrowthEvent -SqlInstance ServerA\\SQL2016 -EventType Shrink\r\n\r\nReturns any database Auto Shrink events in the Default Trace for every database on the ServerA\\SQL2016 instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaDatabaseGrowthEvent -SqlInstance ServerA\\SQL2016 -EventType Growth -FileType Data\r\n\r\nReturns any database Auto Growth events on data files in the Default Trace for every database on the ServerA\\SQL2016 \r\ninstance.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Find-DbaDisabledIndex",
        "Description":  "This command will help you to find disabled indexes on a database or a list of databases.",
        "Tags":  "Index",
        "Author":  "Jason Squires, sqlnotnull.com",
        "Synopsis":  "Find Disabled indexes",
        "Name":  "Find-DbaDisabledIndex",
        "Links":  "https://dbatools.io/Find-DbadisabledIndex",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eFind-DbadisabledIndex -SqlInstance sql2005\r\n\r\nGenerates the SQL statements to drop the selected disabled indexes on server \"sql2005\".\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eFind-DbadisabledIndex -SqlInstance sqlserver2016 -SqlCredential $cred\r\n\r\nGenerates the SQL statements to drop the selected disabled indexes on server \"sqlserver2016\", using SQL Authentication \r\nto connect to the database.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eFind-DbadisabledIndex -SqlInstance sqlserver2016 -Database db1, db2\r\n\r\nGenerates the SQL Statement to drop selected indexes in databases db1 \u0026 db2 on server \"sqlserver2016\".\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eFind-DbadisabledIndex -SqlInstance sqlserver2016\r\n\r\nGenerates the SQL statements to drop selected indexes on all user databases.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Find-DbaDuplicateIndex",
        "Description":  "This command will help you to find duplicate and overlapping indexes on a database or a list of databases.\n\nOn SQL Server 2008 and higher, the IsFiltered property will also be checked\n\nAlso tells how much space you can save by dropping the index.\n\nWe show the type of compression so you can make a more considered decision.\n\nFor now only supports CLUSTERED and NONCLUSTERED indexes.\n\nYou can select the indexes you want to drop on the gridview and when clicking OK, the DROP statement will be generated.\n\nOutput:\n    TableName\n    IndexName\n    KeyColumns\n    IncludedColumns\n    IndexSizeMB\n    IndexType\n    CompressionDescription (When 2008+)\n    [RowCount]\n    IsDisabled\n    IsFiltered (When 2008+)",
        "Tags":  "Index",
        "Author":  "Claudio Silva (@ClaudioESSilva)",
        "Synopsis":  "Find duplicate and overlapping indexes.",
        "Name":  "Find-DbaDuplicateIndex",
        "Links":  "https://dbatools.io/Find-DbaDuplicateIndex",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaDuplicateIndex -SqlInstance sql2005 | Out-File -FilePath C:\\temp\\sql2005-DuplicateIndexes.sql\r\n\r\nGenerates SQL statements to drop the selected duplicate indexes in server \"sql2005\" and writes them to the file \r\n\"C:\\temp\\sql2005-DuplicateIndexes.sql\"\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaDuplicateIndex -SqlInstance sql2005 | Out-File -FilePath C:\\temp\\sql2005-DuplicateIndexes.sql -Append\r\n\r\nGenerates SQL statements to drop the selected duplicate indexes and writes/appends them to the file \r\n\"C:\\temp\\sql2005-DuplicateIndexes.sql\"\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaDuplicateIndex -SqlInstance sqlserver2014a -SqlCredential $cred\r\n\r\nFinds exact duplicate indexes on all user databases present on sqlserver2014a, using SQL authentication.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaDuplicateIndex -SqlInstance sqlserver2014a -Database db1, db2\r\n\r\nFinds exact duplicate indexes on the db1 and db2 databases.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaDuplicateIndex -SqlInstance sqlserver2014a -IncludeOverlapping\r\n\r\nFinds both duplicate and overlapping indexes on all user databases.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Find-DbaInstance",
        "Description":  "This function searches for SQL Server Instances.\n\nIt supports a variety of scans for this purpose which can be separated in two categories:\n- Discovery\n- Scan\n\nDiscovery:\nThis is where it compiles a list of computers / addresses to check.\nIt supports several methods of generating such lists (including Active Directory lookup or IP Ranges), but also supports specifying a list of computers to check.\n- For details on discovery, see the documentation on the \u0027-DiscoveryType\u0027 parameter\n- For details on explicitly providing a list, see the documentation on the \u0027-ComputerName\u0027 parameter\n\nScan:\nOnce a list of computers has been provided, this command will execute a variety of actions to determine any instances present for each of them.\nThis is described in more detail in the documentation on the \u0027-ScanType\u0027 parameter.\nAdditional parameters allow more granular control over individual scans (e.g. Credentials to use).\n\nNote on logging and auditing:\nThe Discovery phase is unproblematic since it is non-intrusive, however during the scan phase, all targeted computers may be accessed repeatedly.\nThis may cause issues with security teams, due to many logon events and possibly failed authentication.\nThis action constitutes a network scan, which may be illegal depending on the nation you are in and whether you own the network you scan.\nIf you are unsure whether you may use this command in your environment, check the detailed description on the \u0027-ScanType\u0027 parameter and contact your IT security team for advice.",
        "Tags":  [
                     "Instance",
                     "Connect",
                     "SqlServer"
                 ],
        "Author":  "Scott Sutherland, 2018 NetSPI",
        "Synopsis":  "Search for SQL Server Instances.",
        "Name":  "Find-DbaInstance",
        "Links":  "https://dbatools.io/Find-DbaInstance",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaInstance -DiscoveryType Domain,DataSourceEnumeration\r\n\r\nPerforms a network search for SQL Instances by:\r\n- Looking up the Service Principal Names of computers in active directory\r\n- Using the UDP broadcast based auto-discovery of SSMS\r\nAfter that it will extensively scan all hosts thus discovered for instances.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaInstance -DiscoveryType All\r\n\r\nPerforms a network search for SQL Instances, using all discovery protocols:\r\n- Active directory search for Service Principal Names\r\n- SQL Instance Enumeration (same as SSMS does)\r\n- All IPAddresses in the current computer\u0027s subnets of all connected network interfaces\r\nNote: This scan will take a long time, due to including the IP Scan\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-ADComputer -Filter \"*\" | Find-DbaInstance\r\n\r\nScans all computers in the domain for SQL Instances, using a deep probe:\r\n- Tries resolving the name in DNS\r\n- Tries pinging the computer\r\n- Tries listing all SQL Services using CIM/WMI\r\n- Tries discovering all instances via the browser service\r\n- Tries connecting to the default TCP Port (1433)\r\n- Tries connecting to the TCP port of each discovered instance\r\n- Tries to establish a SQL connection to the server using default windows credentials\r\n- Tries looking up the Service Principal Names for each instance\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-Content .\\servers.txt | Find-DbaInstance -SqlCredential $cred -ScanType Browser,SqlConnect\r\n\r\nReads all servers from the servers.txt file (one server per line),\r\nthen scans each of them for instances using the browser service\r\nand finally attempts to connect to each instance found using the specified credentials.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Find-DbaLoginInGroup",
        "Description":  "Outputs all the active directory groups members for a server, or limits it to find a specific AD user in the groups",
        "Tags":  [
                     "Login",
                     "AD",
                     "ActiveDirectory",
                     "Group",
                     "Security"
                 ],
        "Author":  "Stephen Bennett, https://sqlnotesfromtheunderground.wordpress.com/",
        "Synopsis":  "Finds Logins in Active Directory groups that have logins on the SQL Instance.",
        "Name":  "Find-DbaLoginInGroup",
        "Links":  "https://dbatools.io/Find-DbaLoginInGroup",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaLoginInGroup -SqlInstance DEV01 -Login \"MyDomain\\Stephen.Bennett\"\r\n\r\nReturns all active directory groups with logins on Sql Instance DEV01 that contain the AD user Stephen.Bennett.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaLoginInGroup -SqlInstance DEV01\r\n\r\nReturns all active directory users within all windows AD groups that have logins on the instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaLoginInGroup -SqlInstance DEV01 | Where-Object Login -like \u0027*stephen*\u0027\r\n\r\nReturns all active directory users within all windows AD groups that have logins on the instance whose login contains \r\n\u0027stephen\u0027\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Find-DbaOrphanedFile",
        "Description":  "This command searches all directories associated with SQL database files for database files that are not currently in use by the SQL Server instance.\n\nBy default, it looks for orphaned .mdf, .ldf and .ndf files in the root\\data directory, the default data path, the default log path, the system paths and any directory in use by any attached directory.\n\nYou can specify additional filetypes using the -FileType parameter, and additional paths to search using the -Path parameter.",
        "Tags":  [
                     "Orphan",
                     "Database",
                     "DatabaseFile"
                 ],
        "Author":  "Sander Stad (@sqlstad), sqlstad.nl",
        "Synopsis":  "Find-DbaOrphanedFile finds orphaned database files. Orphaned database files are files not associated with any attached database.",
        "Name":  "Find-DbaOrphanedFile",
        "Links":  "https://dbatools.io/Find-DbaOrphanedFile",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaOrphanedFile -SqlInstance sqlserver2014a\r\n\r\nConnects to sqlserver2014a, authenticating with Windows credentials, and searches for orphaned files. Returns server \r\nname, local filename, and unc path to file.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaOrphanedFile -SqlInstance sqlserver2014a -SqlCredential $cred\r\n\r\nConnects to sqlserver2014a, authenticating with SQL Server authentication, and searches for orphaned files. Returns \r\nserver name, local filename, and unc path to file.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaOrphanedFile -SqlInstance sql2014 -Path \u0027E:\\Dir1\u0027, \u0027E:\\Dir2\u0027\r\n\r\nFinds the orphaned files in \"E:\\Dir1\" and \"E:Dir2\" in addition to the default directories.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaOrphanedFile -SqlInstance sql2014 -LocalOnly\r\n\r\nReturns only the local filepaths for orphaned files.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaOrphanedFile -SqlInstance sql2014 -RemoteOnly\r\n\r\nReturns only the remote filepath for orphaned files.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaOrphanedFile -SqlInstance sql2014, sql2016 -FileType fsf, mld\r\n\r\nFinds the orphaned ending with \".fsf\" and \".mld\" in addition to the default filetypes \".mdf\", \".ldf\", \".ndf\" for both \r\nthe servers sql2014 and sql2016.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Find-DbaSimilarTable",
        "Description":  "This function can either run against specific databases or all databases searching all/specific tables and views including in system databases.\nTypically one would use this to find for example archive version(s) of a table whose structures are similar.\nThis can also be used to find tables/views that are very similar to a given table/view structure to see where a table/view might be used.\n\nMore information can be found here: https://sqljana.wordpress.com/2017/03/31/sql-server-find-tables-with-similar-table-structure/",
        "Tags":  "Table",
        "Author":  "Jana Sattainathan (@SQLJana - http://sqljana.wordpress.com)",
        "Synopsis":  "Returns all tables/views that are similar in structure by comparing the column names of matching and matched tables/views",
        "Name":  "Find-DbaSimilarTable",
        "Links":  "https://dbatools.io/Find-DbaSimilarTable",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaSimilarTable -SqlInstance DEV01\r\n\r\nSearches all user database tables and views for each, returns all tables or views with their matching tables/views and \r\nmatch percent\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaSimilarTable -SqlInstance DEV01 -Database AdventureWorks\r\n\r\nSearches AdventureWorks database and lists tables/views and their corresponding matching tables/views with match percent\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaSimilarTable -SqlInstance DEV01 -Database AdventureWorks -SchemaName HumanResource\r\n\r\nSearches AdventureWorks database and lists tables/views in the HumanResource schema with their corresponding matching \r\ntables/views with match percent\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaSimilarTable -SqlInstance DEV01 -Database AdventureWorks -SchemaName HumanResource -Table Employee\r\n\r\nSearches AdventureWorks database and lists tables/views in the HumanResource schema and table Employee with its \r\ncorresponding matching tables/views with match percent\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaSimilarTable -SqlInstance DEV01 -Database AdventureWorks -MatchPercentThreshold 60\r\n\r\nSearches AdventureWorks database and lists all tables/views with its corresponding matching tables/views with match \r\npercent greater than or equal to 60\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Find-DbaStoredProcedure",
        "Description":  "This function can either run against specific databases or all databases searching all user or user and system stored procedures.",
        "Tags":  [
                     "StoredProcedure",
                     "Proc"
                 ],
        "Author":  "Stephen Bennett, https://sqlnotesfromtheunderground.wordpress.com/",
        "Synopsis":  "Returns all stored procedures that contain a specific case-insensitive string or regex pattern.",
        "Name":  "Find-DbaStoredProcedure",
        "Links":  "https://dbatools.io/Find-DbaStoredProcedure",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaStoredProcedure -SqlInstance DEV01 -Pattern whatever\r\n\r\nSearches all user databases stored procedures for \"whatever\" in the textbody\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaStoredProcedure -SqlInstance sql2016 -Pattern \u0027\\w+@\\w+\\.\\w+\u0027\r\n\r\nSearches all databases for all stored procedures that contain a valid email pattern in the textbody\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaStoredProcedure -SqlInstance DEV01 -Database MyDB -Pattern \u0027some string\u0027 -Verbose\r\n\r\nSearches in \"mydb\" database stored procedures for \"some string\" in the textbody\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaStoredProcedure -SqlInstance sql2016 -Database MyDB -Pattern RUNTIME -IncludeSystemObjects\r\n\r\nSearches in \"mydb\" database stored procedures for \"runtime\" in the textbody\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Find-DbaTrigger",
        "Description":  "This function search on Instance, Database and Object level.\nIf you specify one or more databases, search on Server level will not be preformed.",
        "Tags":  "Trigger",
        "Author":  "Cláudio Silva, @ClaudioESSilva",
        "Synopsis":  "Returns all triggers that contain a specific case-insensitive string or regex pattern.",
        "Name":  "Find-DbaTrigger",
        "Links":  "https://dbatools.io/Find-DbaTrigger",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaTrigger -SqlInstance DEV01 -Pattern whatever\r\n\r\nSearches all user databases triggers for \"whatever\" in the textbody\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaTrigger -SqlInstance sql2016 -Pattern \u0027\\w+@\\w+\\.\\w+\u0027\r\n\r\nSearches all databases for all triggers that contain a valid email pattern in the textbody\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaTrigger -SqlInstance DEV01 -Database MyDB -Pattern \u0027some string\u0027 -Verbose\r\n\r\nSearches in \"mydb\" database triggers for \"some string\" in the textbody\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaTrigger -SqlInstance sql2016 -Database MyDB -Pattern RUNTIME -IncludeSystemObjects\r\n\r\nSearches in \"mydb\" database triggers for \"runtime\" in the textbody\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Find-DbaUnusedIndex",
        "Description":  "This command will help you to find Unused indexes on a database or a list of databases\n\nAlso tells how much space you can save by dropping the index.\nWe show the type of compression so you can make a more considered decision.\nFor now only supported for CLUSTERED and NONCLUSTERED indexes\n\nYou can select the indexes you want to drop on the gridview and by clicking OK the drop statement will be generated.",
        "Tags":  "Index",
        "Author":  "Aaron Nelson (@SQLvariant), SQLvariant.com",
        "Synopsis":  "Find Unused indexes",
        "Name":  "Find-DbaUnusedIndex",
        "Links":  "https://dbatools.io/Find-DbaUnusedIndex",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaUnusedIndex -SqlInstance sql2005 -FilePath C:\\temp\\sql2005-UnusedIndexes.sql\r\n\r\nGenerates the SQL statements to drop the selected unused indexes on server \"sql2005\". The statements are written to the \r\nfile \"C:\\temp\\sql2005-UnusedIndexes.sql\"\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaUnusedIndex -SqlInstance sql2005 -FilePath C:\\temp\\sql2005-UnusedIndexes.sql -Append\r\n\r\nGenerates the SQL statements to drop the selected unused indexes on server \"sql2005\". The statements are written to the \r\nfile \"C:\\temp\\sql2005-UnusedIndexes.sql\", appending if the file already exists.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaUnusedIndex -SqlInstance sqlserver2016 -SqlCredential $cred\r\n\r\nGenerates the SQL statements to drop the selected unused indexes on server \"sqlserver2016\", using SQL Authentication to \r\nconnect to the database.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaUnusedIndex -SqlInstance sqlserver2016 -Database db1, db2\r\n\r\nGenerates the SQL Statement to to drop selected indexes in databases db1 \u0026 db2 on server \"sqlserver2016\".\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaUnusedIndex -SqlInstance sqlserver2016\r\n\r\nGenerates the SQL statements to drop selected indexes on all user databases.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eFine-DbaUnusedIndex -SqlInstance sqlserver2016 -IgnoreUptime\r\n\r\nGenerates the SQL statements to drop selected indexes on all user databases even if the instance has been online for \r\nless than 7 days.\r\nNote that results may not have enough detail for all indexes, so care should be taken when using them or the generated \r\nscripts. Best practice is to allow a full week to capture the mmajority of index use cases\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Find-DbaUserObject",
        "Description":  "Looks at the below list of objects to see if they are either owned by a user or a specific user (using the parameter -Pattern)\n    Database Owner\n    Agent Job Owner\n    Used in Credential\n    USed in Proxy\n    SQL Agent Steps using a Proxy\n    Endpoints\n    Server Roles\n    Database Schemas\n    Database Roles\n    Database Assembles\n    Database Synonyms",
        "Tags":  "Object",
        "Author":  "Stephen Bennett, https://sqlnotesfromtheunderground.wordpress.com/",
        "Synopsis":  "Searches SQL Server to find user-owned objects (ie. not dbo or sa) or for any object owned by a specific user specified by the Pattern parameter.",
        "Name":  "Find-DbaUserObject",
        "Links":  "https://dbatools.io/Find-DbaUserObject",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaUserObject -SqlInstance DEV01 -Pattern ad\\stephen\r\n\r\nSearches user objects for owner ad\\stephen\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaUserObject -SqlInstance DEV01 -Verbose\r\n\r\nShows all user owned (non-sa, non-dbo) objects and verbose output\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Find-DbaView",
        "Description":  "This function can either run against specific databases or all databases searching all user or user and system views.",
        "Tags":  "View",
        "Author":  "Cláudio Silva (@ClaudioESSilva)",
        "Synopsis":  "Returns all views that contain a specific case-insensitive string or regex pattern.",
        "Name":  "Find-DbaView",
        "Links":  "https://dbatools.io/Find-DbaView",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaView -SqlInstance DEV01 -Pattern whatever\r\n\r\nSearches all user databases views for \"whatever\" in the textbody\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaView -SqlInstance sql2016 -Pattern \u0027\\w+@\\w+\\.\\w+\u0027\r\n\r\nSearches all databases for all views that contain a valid email pattern in the textbody\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaView -SqlInstance DEV01 -Database MyDB -Pattern \u0027some string\u0027 -Verbose\r\n\r\nSearches in \"mydb\" database views for \"some string\" in the textbody\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eFind-DbaView -SqlInstance sql2016 -Database MyDB -Pattern RUNTIME -IncludeSystemObjects\r\n\r\nSearches in \"mydb\" database views for \"runtime\" in the textbody\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Format-DbaBackupInformation",
        "Description":  "Performs various mapping on Backup History, ready restoring\nOptions include changing restore paths, backup paths, database name and many others",
        "Tags":  [
                     "DisasterRecovery",
                     "Backup",
                     "Restore"
                 ],
        "Author":  "Stuart Moore (@napalmgram stuart-moore.com )",
        "Synopsis":  "Transforms the data in a dbatools backuphistory object for a restore",
        "Name":  "Format-DbaBackupInformation",
        "Links":  "https://dbatools.io/Format-DbaBackupInformation",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003e$History | Format-DbaBackupInformation -ReplaceDatabaseName NewDb\r\n\r\nChanges as databasename references to NewDb, both in the database name and any restore paths. Note, this will fail if \r\nthe BackupHistory object contains backups for more than 1 database\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e$History | Format-DbaBackupInformation -ReplaceDatabaseName @{\u0027OldB\u0027=\u0027NewDb\u0027;\u0027ProdHr\u0027=\u0027DevHr\u0027}\r\n\r\nWill change all occurences of original database name in the backup history (names and restore paths) using the mapping \r\nin the hashtable.\r\nIn this example any occurance of OldDb will be replaced with NewDb and ProdHr with DevPR\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003e$History | Format-DbaBackupInformation -DataFileDirectory \u0027D:\\DataFiles\\\u0027 -LogFileDirectory \u0027E:\\LogFiles\\\r\n\r\nThis example with change the restore path for all datafiles (everything that is not a log file) to d:\\datafiles\r\nAnd all Transaction Log files will be restored to E:\\Logfiles\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003e$History | Formate-DbaBackupInformation -RebaseBackupFolder f:\\backups\r\n\r\nThis example changes the location that SQL Server will look for the backups. This is useful if you\u0027ve moved the backups \r\nto a different location\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaAgDatabase",
        "Description":  "Default view provides most common set of properties for information on the database in an Availability Group(s).\n\nInformation returned on the database will be specific to that replica, whether it is primary or a secondary.\n\nThis command will return an SMO object, but it is the AvailabilityDatabases object and not the Server.Databases object.",
        "Tags":  [
                     "Hadr",
                     "AG",
                     "AvailabilityGroup",
                     "Replica"
                 ],
        "Author":  "Shawn Melton (@wsmelton)",
        "Synopsis":  "Outputs the databases involved in the Availability Group(s) found on the server.",
        "Name":  "Get-DbaAgDatabase",
        "Links":  "https://dbatools.io/Get-DbaAgDatabase",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgDatabase -SqlInstance sqlserver2014a\r\n\r\nReturns basic information on all the databases in each Availability Group found on sqlserver2014a\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgDatabase -SqlInstance sqlserver2014a -AvailabilityGroup AG-a\r\n\r\nReturns basic information on all the databases in the Availability Group AG-a on sqlserver2014a\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgDatabase -SqlInstance sqlserver2014a -AvailabilityGroup AG-a -Database AG-Database\r\n\r\nReturns basic information on the database AG-Database found in the Availability Group AG-a on server sqlserver2014a\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaAgentAlert",
        "Description":  "This function returns SQL Agent alerts.",
        "Tags":  [
                     "Agent",
                     "SMO"
                 ],
        "Author":  "Klaas Vandenberghe ( @PowerDBAKlaas )",
        "Synopsis":  "Returns all SQL Agent alerts on a SQL Server Agent.",
        "Name":  "Get-DbaAgentAlert",
        "Links":  "https://dbatools.io/Get-DbaAgentAlert",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentAlert -SqlInstance ServerA,ServerB\\instanceB\r\n\r\nReturns all SQL Agent alerts on serverA and serverB\\instanceB\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e\u0027serverA\u0027,\u0027serverB\\instanceB\u0027 | Get-DbaAgentAlert\r\n\r\nReturns all SQL Agent alerts  on serverA and serverB\\instanceB\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaAgentJob",
        "Description":  "The Get-DbaAgentJob returns connected SMO object for SQL Agent Job information for each instance(s) of SQL Server.",
        "Tags":  [
                     "Job",
                     "Agent"
                 ],
        "Author":  "Garry Bargsley (@gbargsley), http://blog.garrybargsley.com",
        "Synopsis":  "Gets SQL Agent Job information for each instance(s) of SQL Server.",
        "Name":  "Get-DbaAgentJob",
        "Links":  "https://dbatools.io/Get-DbaAgentJob",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentJob -SqlInstance localhost\r\n\r\nReturns all SQL Agent Jobs on the local default SQL Server instance\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentJob -SqlInstance localhost, sql2016\r\n\r\nReturns all SQl Agent Jobs for the local and sql2016 SQL Server instances\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentJob -SqlInstance localhost -Job BackupData, BackupDiff\r\n\r\nReturns all SQL Agent Jobs named BackupData and BackupDiff from the local SQL Server instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentJob -SqlInstance localhost -ExcludeJob BackupDiff\r\n\r\nReturns all SQl Agent Jobs for the local SQL Server instances, except the BackupDiff Job.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentJob -SqlInstance localhost -NoDisabledJobs\r\n\r\nReturns all SQl Agent Jobs for the local SQL Server instances, excluding the disabled jobs.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003e$servers | Get-DbaAgentJob | Out-GridView -Passthru | Start-DbaAgentJob -WhatIf\r\n\r\nFind all of your Jobs from servers in the $server collection, select the jobs you want to start then see jobs would \r\nstart if you ran Start-DbaAgentJob\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaAgentJobCategory",
        "Description":  "Get-DbaAgentJobCategory makes it possible to retrieve the job categories.",
        "Tags":  [
                     "Agent",
                     "Job",
                     "JobCategory"
                 ],
        "Author":  "Sander Stad (@sqlstad, sqlstad.nl)",
        "Synopsis":  "Get-DbaAgentJobCategory retrieves the job categories.",
        "Name":  "Get-DbaAgentJobCategory",
        "Links":  "https://dbatools.io/Get-DbaAgentJobCategory",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentJobCategory -SqlInstance sql1\r\n\r\nReturn all the job categories.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentJobCategory -SqlInstance sql1 -Category \u0027Log Shipping\u0027\r\n\r\nReturn all the job categories that have the name \u0027Log Shipping\u0027.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentJobCategory -SqlInstance sstad-pc -CategoryType MultiServerJob\r\n\r\nReturn all the job categories that have a type MultiServerJob.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaAgentJobHistory",
        "Description":  "Get-DbaAgentJobHistory returns all information on the executions still available on each instance(s) of SQL Server submitted.\nThe cleanup of SQL Agent history determines how many records are kept.\n\nhttps://msdn.microsoft.com/en-us/library/ms201680.aspx\nhttps://msdn.microsoft.com/en-us/library/microsoft.sqlserver.management.smo.agent.jobhistoryfilter(v=sql.120).aspx",
        "Tags":  [
                     "Job",
                     "Agent"
                 ],
        "Author":  "Klaas Vandenberghe ( @PowerDbaKlaas )",
        "Synopsis":  "Gets execution history of SQL Agent Job on instance(s) of SQL Server.",
        "Name":  "Get-DbaAgentJobHistory",
        "Links":  "https://dbatools.io/Get-DbaAgentJobHistory",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentJobHistory -SqlInstance localhost\r\n\r\nReturns all SQL Agent Job execution results on the local default SQL Server instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentJobHistory -SqlInstance localhost, sql2016\r\n\r\nReturns all SQL Agent Job execution results for the local and sql2016 SQL Server instances.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003e\u0027sql1\u0027,\u0027sql2\\Inst2K17\u0027 | Get-DbaAgentJobHistory\r\n\r\nReturns all SQL Agent Job execution results for sql1 and sql2\\Inst2K17.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentJobHistory -SqlInstance sql2\\Inst2K17 | select *\r\n\r\nReturns all properties for all SQl Agent Job execution results on sql2\\Inst2K17.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentJobHistory -SqlInstance sql2\\Inst2K17 -Job \u0027Output File Cleanup\u0027\r\n\r\nReturns all properties for all SQl Agent Job execution results of the \u0027Output File Cleanup\u0027 job on sql2\\Inst2K17.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentJobHistory -SqlInstance sql2\\Inst2K17 -Job \u0027Output File Cleanup\u0027 -WithOutputFile\r\n\r\nReturns all properties for all SQl Agent Job execution results of the \u0027Output File Cleanup\u0027 job on sql2\\Inst2K17,\r\nwith additional properties that show the output filename path\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 7 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentJobHistory -SqlInstance sql2\\Inst2K17 -NoJobSteps\r\n\r\nReturns the SQL Agent Job execution results for the whole jobs on sql2\\Inst2K17, leaving out job step execution results.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 8 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentJobHistory -SqlInstance sql2\\Inst2K17 -StartDate \u00272017-05-22\u0027 -EndDate \u00272017-05-23 12:30:00\u0027\r\n\r\nReturns the SQL Agent Job execution results between 2017/05/22 00:00:00 and 2017/05/23 12:30:00 on sql2\\Inst2K17.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 9 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentJob -SqlInstance sql2016 | Where Name -match backup | Get-DbaAgentJobHistory\r\n\r\nGets all jobs with the name that match the regex pattern \"backup\" and then gets the job history from those. You can \r\nalso use -Like *backup* in this example.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaAgentJobOutputFile",
        "Description":  "This function returns for one or more SQL Instances the output file value for each step of one or many agent job with the Job Names\nprovided dynamically. It will not return anything if there is no Output File",
        "Tags":  [
                     "Agent",
                     "Job"
                 ],
        "Author":  "Rob Sewell (https://sqldbawithabeard.com)",
        "Synopsis":  "Returns the Output File for each step of one or many agent job with the Job Names provided dynamically if\nrequired for one or more SQL Instances",
        "Name":  "Get-DbaAgentJobOutputFile",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentJobOutputFile -SqlInstance SERVERNAME -Job \u0027The Agent Job\u0027\r\n\r\nThis will return the configured paths to the output files for each of the job step of the The Agent Job Job\r\non the SERVERNAME instance\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentJobOutputFile -SqlInstance SERVERNAME\r\n\r\nThis will return the configured paths to the output files for each of the job step of all the Agent Jobs\r\non the SERVERNAME instance\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentJobOutputFile -SqlInstance SERVERNAME,SERVERNAME2 -Job \u0027The Agent Job\u0027\r\n\r\nThis will return the configured paths to the output files for each of the job step of the The Agent Job Job\r\non the SERVERNAME instance and SERVERNAME2\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003e$Servers = \u0027SERVER\u0027,\u0027SERVER\\INSTANCE1\u0027\r\n\r\nGet-DbaAgentJobOutputFile -SqlInstance $Servers -Job \u0027The Agent Job\u0027 -OpenFile\r\n\r\nThis will return the configured paths to the output files for each of the job step of the The Agent Job Job\r\non the SERVER instance and the SERVER\\INSTANCE1 and open the files if they are available\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentJobOutputFile -SqlInstance SERVERNAME  | Out-GridView\r\n\r\nThis will return the configured paths to the output files for each of the job step of all the Agent Jobs\r\non the SERVERNAME instance and Pipe them to Out-GridView\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003e(Get-DbaAgentJobOutputFile -SqlInstance SERVERNAME | ogv -PassThru).FileName | Invoke-Item\r\n\r\nThis will return the configured paths to the output files for each of the job step of all the Agent Jobs\r\non the SERVERNAME instance and Pipe them to Out-GridView and enable you to choose the output\r\nfile and open it\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 7 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentJobOutputFile -SqlInstance SERVERNAME -Verbose\r\n\r\nThis will return the configured paths to the output files for each of the job step of all the Agent Jobs\r\non the SERVERNAME instance and also show the job steps without an output file\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaAgentJobStep",
        "Description":  "The Get-DbaAgentJobStep returns connected SMO object for SQL Agent Job Step for each instance(s) of SQL Server.",
        "Tags":  [
                     "Job",
                     "Agent"
                 ],
        "Author":  "Klaas Vandenberghe (@PowerDbaKlaas), http://powerdba.eu",
        "Synopsis":  "Gets SQL Agent Job Step information for each instance(s) of SQL Server.",
        "Name":  "Get-DbaAgentJobStep",
        "Links":  "https://dbatools.io/Get-DbaAgentJobStep",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentJobStep -SqlInstance localhost\r\n\r\nReturns all SQL Agent Job Steps on the local default SQL Server instance\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentJobStep -SqlInstance localhost, sql2016\r\n\r\nReturns all SQl Agent Job Steps for the local and sql2016 SQL Server instances\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentJobStep -SqlInstance localhost -Job BackupData, BackupDiff\r\n\r\nReturns all SQL Agent Job Steps for the jobs named BackupData and BackupDiff from the local SQL Server instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentJobStep -SqlInstance localhost -ExcludeJob BackupDiff\r\n\r\nReturns all SQl Agent Job Steps for the local SQL Server instances, except for the BackupDiff Job.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentJobStep -SqlInstance localhost -NoDisabledJobs\r\n\r\nReturns all SQl Agent Job Steps for the local SQL Server instances, excluding the disabled jobs.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003e$servers | Get-DbaAgentJobStep\r\n\r\nFind all of your Job Steps from servers in the $server collection\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaAgentLog",
        "Description":  "Gets the \"SQL Agent Error Log\" of an instance. Returns all 10 error logs by default.",
        "Tags":  "Logging",
        "Synopsis":  "Gets the \"SQL Agent Error Log\" of an instance",
        "Name":  "Get-DbaAgentLog",
        "Links":  "https://dbatools.io/Get-DbaAgentLog",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentLog -SqlInstance sql01\\sharepoint\r\n\r\nReturns the entire error log for the SQL Agent on sql01\\sharepoint\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentLog -SqlInstance sql01\\sharepoint -LogNumber 3, 6\r\n\r\nReturns log numbers 3 and 6 for the SQL Agent on sql01\\sharepoint\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003e$servers = \"sql2014\",\"sql2016\", \"sqlcluster\\sharepoint\"\r\n\r\n$servers | Get-DbaAgentLog -LogNumber 0\r\n\r\nReturns the most recent SQL Agent error logs for \"sql2014\",\"sql2016\" and \"sqlcluster\\sharepoint\"\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaAgentOperator",
        "Description":  "This function returns SQL Agent operators.",
        "Tags":  [
                     "Agent",
                     "Operator"
                 ],
        "Author":  "Klaas Vandenberghe ( @PowerDBAKlaas )",
        "Synopsis":  "Returns all SQL Agent operators on a SQL Server Agent.",
        "Name":  "Get-DbaAgentOperator",
        "Links":  "https://dbatools.io/Get-DbaAgentOperator",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentOperator -SqlInstance ServerA,ServerB\\instanceB\r\n\r\nReturns any SQL Agent operators on serverA and serverB\\instanceB\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e\u0027ServerA\u0027,\u0027ServerB\\instanceB\u0027 | Get-DbaAgentOperator\r\n\r\nReturns all SQL Agent operators  on serverA and serverB\\instanceB\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentOperator -SqlInstance ServerA -Operator Dba1,Dba2\r\n\r\nReturns only the SQL Agent Operators Dba1 and Dba2 on ServerA.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentOperator -SqlInstance ServerA,ServerB -ExcludeOperator Dba3\r\n\r\nReturns all the SQL Agent operators on ServerA and ServerB, except the Dba3 operator.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaAgentProxy",
        "Description":  "This function returns SQL Agent proxies.",
        "Tags":  [
                     "Agent",
                     "SMO"
                 ],
        "Author":  "Chrissy LeMaire ( @cl )",
        "Synopsis":  "Returns all SQL Agent proxies on a SQL Server Agent.",
        "Name":  "Get-DbaAgentProxy",
        "Links":  "https://dbatools.io/Get-DbaAgentProxy",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentProxy -SqlInstance ServerA,ServerB\\instanceB\r\n\r\nReturns all SQL Agent proxies on serverA and serverB\\instanceB\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e\u0027serverA\u0027,\u0027serverB\\instanceB\u0027 | Get-DbaAgentProxy\r\n\r\nReturns all SQL Agent proxies  on serverA and serverB\\instanceB\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaAgentSchedule",
        "Description":  "This function returns SQL Agent Shared Schedules.",
        "Tags":  [
                     "Agent",
                     "Schedule"
                 ],
        "Author":  "Chris McKeown (@devopsfu), http://www.devopsfu.com",
        "Synopsis":  "Returns all SQL Agent Shared Schedules on a SQL Server Agent.",
        "Name":  "Get-DbaAgentSchedule",
        "Links":  "https://dbatools.io/Get-DbaAgentSchedule",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentSchedule -SqlInstance localhost\r\n\r\nReturns all SQL Agent Shared Schedules on the local default SQL Server instance\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentSchedule -SqlInstance localhost, sql2016\r\n\r\nReturns all SQL Agent Shared Schedules for the local and sql2016 SQL Server instances\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaAgHadr",
        "Description":  "Gets the Hadr setting, from the service level, and returns true or false for the specified SQL Server instance.",
        "Tags":  [
                     "Hadr",
                     "AG",
                     "AvailabilityGroup"
                 ],
        "Author":  "Shawn Melton (@wsmelton | http://blog.wsmelton.info)",
        "Synopsis":  "Gets the Hadr service setting on the specified SQL Server instance.",
        "Name":  "Get-DbaAgHadr",
        "Links":  "https://dbatools.io/Get-DbaAgHadr",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgHadr -SqlInstance sql2016\r\n\r\nReturns a status of the Hadr setting for sql2016 SQL Server instance.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaAgListener",
        "Description":  "Default view provides most common set of properties for information on the database in an Availability Group(s).\n\nInformation returned on the database will be specific to that replica, whether it is primary or a secondary.\n\nThis command will return an SMO object, but it is the AvailabilityDatabases object  and not the Server.Databases object.",
        "Tags":  [
                     "AG",
                     "AvailabilityGroup",
                     "Listener"
                 ],
        "Author":  "Viorel Ciucu (@viorelciucu)",
        "Synopsis":  "Outputs the name of the Listener for the Availability Group(s) found on the server.",
        "Name":  "Get-DbaAgListener",
        "Links":  "https://dbatools.io/Get-DbaAgListener",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgListener -SqlInstance sqlserver2014a\r\n\r\nReturns basic information on the listener found on sqlserver2014a\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgListener -SqlInstance sqlserver2014a -AvailabilityGroup AG-a\r\n\r\nReturns basic information on the listener found on sqlserver2014a in the Availability Group AG-a\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaAgReplica",
        "Description":  "Default view provides most common set of properties for information on the Availability Group(s)\u0027 Replica.",
        "Tags":  [
                     "AG",
                     "AvailabilityGroup",
                     "Replica"
                 ],
        "Author":  "Shawn Melton (@wsmelton) | Chrissy LeMaire (@ctrlb)",
        "Synopsis":  "Outputs the Availability Group(s)\u0027 Replica object found on the server.",
        "Name":  "Get-DbaAgReplica",
        "Links":  "https://dbatools.io/Get-DbaAgReplica",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgReplica -SqlInstance sqlserver2014a\r\n\r\nReturns basic information on all the Availability Group(s) replica(s) found on sqlserver2014a\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgReplica -SqlInstance sqlserver2014a -AvailabilityGroup AG-a\r\n\r\nShows basic information on the replica(s) found on Availability Group AG-a on sqlserver2014a\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgReplica -SqlInstance sqlserver2014a | Select *\r\n\r\nReturns full object properties on all Availability Group(s) replica(s) on sqlserver2014a\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaAvailabilityGroup",
        "Description":  "Default view provides most common set of properties for information on the Availability Group(s).",
        "Tags":  [
                     "Hadr",
                     "AG",
                     "AvailabilityGroup"
                 ],
        "Author":  "Shawn Melton (@wsmelton) | Chrissy LeMaire (@ctrlb)",
        "Synopsis":  "Outputs the Availability Group(s) object found on the server.",
        "Name":  "Get-DbaAvailabilityGroup",
        "Links":  "https://dbatools.io/Get-DbaAvailabilityGroup",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAvailabilityGroup -SqlInstance sqlserver2014a\r\n\r\nReturns basic information on all the Availability Group(s) found on sqlserver2014a.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAvailabilityGroup -SqlInstance sqlserver2014a -AvailabilityGroup AG-a\r\n\r\nShows basic information on the Availability Group AG-a on sqlserver2014a.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAvailabilityGroup -SqlInstance sqlserver2014a | Select *\r\n\r\nReturns full object properties on all Availability Group(s) on sqlserver2014a.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAvailabilityGroup -SqlInstance sqlserver2014a -AvailabilityGroup AG-a -IsPrimary\r\n\r\nReturns true/false if the server, sqlserver2014a, is the primary replica for AG-a Availability Group.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaAvailableCollation",
        "Description":  "The Get-DbaAvailableCollation function returns the list of collations available on each SQL Server.\nOnly the connect permission is required to get this information.",
        "Tags":  [
                     "Collation",
                     "Configuration"
                 ],
        "Author":  "Bryan Hamby (@galador)",
        "Synopsis":  "Function to get available collations for a given SQL Server",
        "Name":  "Get-DbaAvailableCollation",
        "Links":  "https://dbatools.io/Get-DbaAvailableCollation",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAvailableCollation -SqlInstance sql2016\r\n\r\nGets all the collations from server sql2016 using NT authentication\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaBackupDevice",
        "Description":  "The Get-DbaBackupDevice command gets SQL Backup Device information for each instance(s) of SQL Server.",
        "Tags":  "Backup",
        "Author":  "Garry Bargsley (@gbargsley), http://blog.garrybargsley.com",
        "Synopsis":  "Gets SQL Backup Device information for each instance(s) of SQL Server.",
        "Name":  "Get-DbaBackupDevice",
        "Links":  "https://dbatools.io/Get-DbaBackupDevice",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaBackupDevice -SqlInstance localhost\r\n\r\nReturns all Backup Devices on the local default SQL Server instance\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaBackupDevice -SqlInstance localhost, sql2016\r\n\r\nReturns all Backup Devices for the local and sql2016 SQL Server instances\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaBackupHistory",
        "Description":  "Returns backup history details for some or all databases on a SQL Server.\n\nYou can even get detailed information (including file path) for latest full, differential and log files.\n\nBackups taken with the CopyOnly option will NOT be returned, unless the IncludeCopyOnly switch is present\n\nReference: http://www.sqlhub.com/2011/07/find-your-backup-history-in-sql-server.html",
        "Tags":  [
                     "DisasterRecovery",
                     "Backup"
                 ],
        "Synopsis":  "Returns backup history details for databases on a SQL Server.",
        "Name":  "Get-DbaBackupHistory",
        "Links":  "https://dbatools.io/Get-DbaBackupHistory",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaBackupHistory -SqlInstance SqlInstance2014a\r\n\r\nReturns server name, database, username, backup type, date for all backups databases on SqlInstance2014a. This may \r\nreturn many rows; consider using filters that are included in other examples.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e$cred = Get-Credential sqladmin\r\n\r\nGet-DbaBackupHistory -SqlInstance SqlInstance2014a -SqlCredential $cred\r\n\r\nDoes the same as above but logs in as SQL user \"sqladmin\"\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaBackupHistory -SqlInstance SqlInstance2014a -Database db1, db2 -Since \u00277/1/2016 10:47:00\u0027\r\n\r\nReturns backup information only for databases db1 and db2 on SqlInstance2014a since July 1, 2016 at 10:47 AM.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaBackupHistory -SqlInstance sql2014 -Database AdventureWorks2014, pubs -Force | Format-Table\r\n\r\nReturns information only for AdventureWorks2014 and pubs and formats the results as a table.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaBackupHistory -SqlInstance sql2014 -Database AdventureWorks2014 -Last\r\n\r\nReturns information about the most recent full, differential and log backups for AdventureWorks2014 on sql2014.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaBackupHistory -SqlInstance sql2014 -Database AdventureWorks2014 -Last -DeviceType Disk\r\n\r\nReturns information about the most recent full, differential and log backups for AdventureWorks2014 on sql2014, but \r\nonly for backups to disk.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 7 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaBackupHistory -SqlInstance sql2014 -Database AdventureWorks2014 -Last -DeviceType 148,107\r\n\r\nReturns information about the most recent full, differential and log backups for AdventureWorks2014 on sql2014, but \r\nonly for backups with device_type 148 and 107.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 8 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaBackupHistory -SqlInstance sql2014 -Database AdventureWorks2014 -LastFull\r\n\r\nReturns information about the most recent full backup for AdventureWorks2014 on sql2014.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 9 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaBackupHistory -SqlInstance sql2014 -Database AdventureWorks2014 -Type Full\r\n\r\nReturns information about all Full backups for AdventureWorks2014 on sql2014.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 10 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRegisteredServer -SqlInstance sql2016 | Get-DbaBackupHistory\r\n\r\nReturns database backup information for every database on every server listed in the Central Management Server on \r\nsql2016.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 11 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaBackupHistory -SqlInstance SqlInstance2014a, sql2016 -Force\r\n\r\nReturns detailed backup history for all databases on SqlInstance2014a and sql2016.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaBackupInformation",
        "Description":  "Upon being passed a list of potential backups files this command will scan the files, select those that contain SQL Server\nbackup sets. It will then filter those files down to a set\n\nThe function defaults to working on a remote instance. This means that all paths passed in must be relative to the remote instance.\nXpDirTree will be used to perform the file scans\n\nVarious means can be used to pass in a list of files to be considered. The default is to non recursively scan the folder\npassed in.",
        "Tags":  [
                     "DisasterRecovery",
                     "Backup",
                     "Restore"
                 ],
        "Synopsis":  "Scan backup files and creates a set, compatible with Restore-DbaDatabase",
        "Name":  "Get-DbaBackupInformation",
        "Links":  "https://dbatools.io/Get-DbaBackupInformation",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaBackupInformation -SqlInstance Server1 -Path c:\\backups\\ -DirectoryRecurse\r\n\r\nWill use the Server1 instance to recursively read all backup files under c:\\backups, and return a dbatools \r\nBackupHistory object\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaBackupInformation -SqlInstance Server1 -Path c:\\backups\\ -DirectoryRecurse -ExportPath \r\nc:\\store\\BackupHistory.xml\r\n\r\n#Copy the file  c:\\store\\BackupHistory.xml to another machine via preferred technique, and the on 2nd machine:\r\n\r\nGet-DbaBackupInformation -Import -Path  c:\\store\\BackupHistory.xml | Restore-DbaDatabase -SqlInstance Server2 \r\n-TrustDbBackupHistory\r\n\r\nThis allows you to move backup history across servers, or to preserve backup history even after the original server has \r\nbeen purged\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaBackupInformation -SqlInstance Server1 -Path c:\\backups\\ -DirectoryRecurse -ExportPath \r\nc:\\store\\BackupHistory.xml -PassThru |\r\n\r\nRestore-DbaDatabase -SqlInstance Server2 -TrustDbBackupHistory\r\n\r\nIn this example we gather backup information, export it to an xml file, and then pass it on through to \r\nRestore-DbaDatabase\r\nThis allows us to repeat the restore without having to scan all the backup files again\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-ChildItem c:\\backups\\ -recurse -files |\r\n\r\nWhere {$_.extension -in (\u0027.bak\u0027,\u0027.trn\u0027) -and $_.LastWriteTime -gt (get-date).AddMonths(-1)} |\r\n    Get-DbaBackupInformation -SqlInstance Server1 -ExportPath c:\\backupHistory.xml\r\n\r\nThis lets you keep a record of all backup history from the last month on hand to speed up refreshes\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003e$Backups = Get-DbaBackupInformation -SqlInstance Server1 -Path \\\\network\\backups\r\n\r\n$Backups += Get-DbaBackupInformation -SqlInstance Server2 -NoXpDirTree -Path c:\\backups\r\n\r\nScan the unc folder \\\\network\\backups with Server1, and then scan the C:\\backups folder on\r\nServer2 not using xp_dirtree, adding the results to the first set.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003e$Backups = Get-DbaBackupInformation -SqlInstance Server1 -Path \\\\network\\backups -MaintenanceSolution\r\n\r\nWhen MaintenanceSolution is indicated we know we are dealing with the output from Ola Hallengren\u0027s backup scripts. So \r\nwe make sure that a FULL folder exists in the first level of Path, if not we shortcut scanning all the files as we have \r\nnothing to work with\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 7 --------------------------\r\n\r\nPS C:\\\u003e$Backups = Get-DbaBackupInformation -SqlInstance Server1 -Path \\\\network\\backups -MaintenanceSolution \r\n-IgnoreLogBackup\r\n\r\nAs we know we are dealing with an Ola Hallengren style backup folder from the MaintenanceSolution switch, when \r\nIgnoreLogBackup is also included we can ignore the LOG folder to skip any scanning of log backups. Note this also means \r\nthen WON\u0027T be restored\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaClientAlias",
        "Description":  "Creates/updates a SQL Server alias by altering HKLM:\\SOFTWARE\\Microsoft\\MSSQLServer\\Client",
        "Tags":  "Alias",
        "Synopsis":  "Creates/updates a sql alias for the specified server - mimics cliconfg.exe",
        "Name":  "Get-DbaClientAlias",
        "Links":  "https://dbatools.io/Get-DbaClientAlias",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaClientAlias\r\n\r\nGets all SQL Server client aliases on the local computer\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaClientAlias -ComputerName workstationx\r\n\r\nGets all SQL Server client aliases on Workstationx\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaClientProtocol",
        "Description":  "Gets the SQL Server related client protocols on one or more computers.\n\nRequires Local Admin rights on destination computer(s).\nThe client protocols can be enabled and disabled when retrieved via WSMan.",
        "Tags":  "Protocol",
        "Author":  "Klaas Vandenberghe ( @PowerDBAKlaas )",
        "Synopsis":  "Gets the SQL Server related client protocols on a computer.",
        "Name":  "Get-DbaClientProtocol",
        "Links":  "https://dbatools.io/Get-DbaClientProtocol",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaClientProtocol -ComputerName sqlserver2014a\r\n\r\nGets the SQL Server related client protocols on computer sqlserver2014a.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e\u0027sql1\u0027,\u0027sql2\u0027,\u0027sql3\u0027 | Get-DbaClientProtocol\r\n\r\nGets the SQL Server related client protocols on computers sql1, sql2 and sql3.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaClientProtocol -ComputerName sql1,sql2 | Out-Gridview\r\n\r\nGets the SQL Server related client protocols on computers sql1 and sql2, and shows them in a grid view.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003e(Get-DbaClientProtocol -ComputerName sql2 | Where { $_.DisplayName = \u0027via\u0027 }).Disable()\r\n\r\nDisables the VIA ClientNetworkProtocol on computer sql2.\r\nIf succesfull, returncode 0 is shown.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaClusterNode",
        "Description":  "Returns the name of the current node(s) in the SQL Server cluster.\n\nIf the -ActiveNode Parameter is passed it only returns the name of the Server currently hosting the clustered instance.",
        "Tags":  [
                     "Cluster",
                     "WSFC",
                     "FCI"
                 ],
        "Synopsis":  "Returns the node(s) of a SQL Cluster.",
        "Name":  "Get-DbaClusterNode",
        "Links":  "https://dbatools.io/Get-DbaClusterNode",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaClusterNode -SqlInstance sqlcluster\r\n\r\nReturns all nodes in the cluster and details about each node.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaClusterNode -SqlInstance sqlcluster -ActiveNode\r\n\r\nReturns the name of the active node in the cluster\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaCmConnection",
        "Description":  "Retrieves windows management connections from the cache",
        "Tags":  [
                     "ComputerManagement",
                     "CIM"
                 ],
        "Author":  "Fred Winmann (@FredWeinmann)",
        "Synopsis":  "Retrieves windows management connections from the cache",
        "Name":  "Get-DbaCmConnection",
        "Links":  "https://dbatools.io/Get-DbaCmConnection",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaCmConnection\r\n\r\nList all cached connections.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaCmConnection sql2014\r\n\r\nList the cached connection - if any - to the server sql2014.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaCmConnection -UserName \"*charles*\"\r\n\r\nList all cached connection that use a username containing \"charles\" as default or override credentials.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaCmObject",
        "Description":  "This function centralizes all requests for information retrieved from Get-WmiObject or Get-CimInstance.\nIt uses different protocols as available in this order:\n- Cim over WinRM\n- Cim over DCOM\n- Wmi\n- Wmi over PowerShell Remoting\nIt remembers channels that didn\u0027t work and will henceforth avoid them. It remembers invalid credentials and will avoid reusing them.\nMuch of its behavior can be configured using Test-DbaWmConnection.",
        "Tags":  [
                     "ComputerManagement",
                     "CIM"
                 ],
        "Author":  "Fred Winmann (@FredWeinmann)",
        "Synopsis":  "Retrieves Wmi/Cim-Style information from computers.",
        "Name":  "Get-DbaCmObject",
        "Links":  "https://dbatools.io/Get-DbaCmObject",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaCmObject win32_OperatingSystem\r\n\r\nRetrieves the common operating system information from the local computer.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaCmObject -Computername \"sql2014\" -ClassName Win32_OperatingSystem -Credential $cred -DoNotUse CimRM\r\n\r\nRetrieves the common operating system information from the server sql2014.\r\nIt will use the credewntials stored in $cred to connect, unless they are known to not work, in which case they will \r\ndefault to windows credentials (unless another default has been set).\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaComputerCertificate",
        "Description":  "Gets computer certificates on localhost that are candidates for using with SQL Server\u0027s network encryption",
        "Tags":  "Certificate",
        "Synopsis":  "Simplifies finding computer certificates that are candidates for using with SQL Server\u0027s network encryption",
        "Name":  "Get-DbaComputerCertificate",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaComputerCertificate\r\n\r\nGets computer certificates on localhost that are candidates for using with SQL Server\u0027s network encryption\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaComputerCertificate -ComputerName sql2016\r\n\r\nGets computer certificates on sql2016 that are candidates for using with SQL Server\u0027s network encryption\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaComputerCertificate -ComputerName sql2016 -Thumbprint 8123472E32AB412ED4288888B83811DB8F504DED, \r\n04BFF8B3679BB01A986E097868D8D494D70A46D6\r\n\r\nGets computer certificates on sql2016 that match thumbprints 8123472E32AB412ED4288888B83811DB8F504DED or \r\n04BFF8B3679BB01A986E097868D8D494D70A46D6\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaComputerSystem",
        "Description":  "Gets computer system information from the server and returns as an object.",
        "Tags":  "ServerInfo",
        "Author":  "Shawn Melton (@wsmelton | http://blog.wsmelton.info)",
        "Synopsis":  "Gets computer system information from the server.",
        "Name":  "Get-DbaComputerSystem",
        "Links":  "https://dbatools.io/Get-DbaComputerSystem",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaComputerSystem\r\n\r\nReturns information about the local computer\u0027s computer system\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaComputerSystem -ComputerName sql2016\r\n\r\nReturns information about the sql2016\u0027s computer system\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaComputerSystem -ComputerName sql2016 -IncludeAws\r\n\r\nReturns information about the sql2016\u0027s computer system and includes additional properties around the EC2 instance.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaConfig",
        "Description":  "Retrieves configuration elements by name.\nCan be used to search the existing configuration list.",
        "Tags":  [
                     "Config",
                     "Module"
                 ],
        "Author":  "Friedrich Weinmann",
        "Synopsis":  "Retrieves configuration elements by name.",
        "Name":  "Get-DbaConfig",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaConfig \u0027Mail.To\u0027\r\n\r\nRetrieves the configuration element for the key \"Mail.To\"\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaConfig -Force\r\n\r\nRetrieve all configuration elements from all modules, even hidden ones.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaConfigValue",
        "Description":  "Returns the configuration value stored under the specified name.\nIt requires the full name (\u003cModule\u003e.\u003cName\u003e) and is usually only called by functions.",
        "Tags":  "Config",
        "Author":  "Friedrich Weinmann",
        "Synopsis":  "Returns the configuration value stored under the specified name.",
        "Name":  "Get-DbaConfigValue",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaConfigValue -Name \u0027System.MailServer\u0027\r\n\r\nReturns the configured value that was assigned to the key \u0027System.MailServer\u0027\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaConfigValue -Name \u0027Default.CoffeeMilk\u0027 -Fallback 0\r\n\r\nReturns the configured value for \u0027Default.CoffeeMilk\u0027. If no such value is configured, it returns \u00270\u0027 instead.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaConnection",
        "Description":  "Returns a bunch of information from dm_exec_connections which, according to Microsoft:\n\"Returns information about the connections established to this instance of SQL Server and the details of each connection. Returns server wide connection information for SQL Server. Returns current database connection information for SQL Database.\"",
        "Tags":  "Connection",
        "Synopsis":  "Returns a bunch of information from dm_exec_connections.",
        "Name":  "Get-DbaConnection",
        "Links":  "https://dbatools.io/Get-DbaConnection",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaConnection -SqlInstance sql2016, sql2017\r\n\r\nReturns client connection information from sql2016 and sql2017\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaCpuUsage",
        "Description":  "\"If there are a lot of processes running on your instance and the CPU is very high,\nthen it\u0027s hard to find the exact process eating up your CPU using just the SQL Server\ntools. One way to correlate the data between what is running within SQL Server and at\nthe Windows level is to use SPID and KPID values to get the exact process.\"\n\nThis command automates that process.\n\nReferences: https://www.mssqltips.com/sqlservertip/2454/how-to-find-out-how-much-cpu-a-sql-server-process-is-really-using/\n\nNote: This command returns results from all SQL instances on the destionation server but the process\ncolumn is specific to -SqlInstance passed.",
        "Tags":  "CPU",
        "Synopsis":  "Provides detailed CPU usage information about a SQL Server\u0027s process",
        "Name":  "Get-DbaCpuUsage",
        "Links":  "https://dbatools.io/Get-DbaCpuUsage",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaCpuUsage -SqlInstance sql2017\r\n\r\nLogs into the SQL Server instance \"sql2017\" and also the Computer itself (via WMI) to gather information\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e$usage = Get-DbaCpuUsage -SqlInstance sql2017\r\n\r\n$usage.Process\r\n\r\nExplores the processes (from Get-DbaProcess) associated with the usage results\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaCpuUsage -SqlInstance sql2017 -SqlCredential (Get-Credential sqladmin) -Credential (Get-Credential \r\nad\\sqldba)\r\n\r\nLogs into the SQL instance using the SQL Login \u0027sqladmin\u0027 and then Windows instance as \u0027ad\\sqldba\u0027\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaCredential",
        "Description":  "The Get-DbaCredential command gets SQL Credential information for each instance(s) of SQL Server.",
        "Tags":  "Credential",
        "Author":  "Garry Bargsley (@gbargsley), http://blog.garrybargsley.com",
        "Synopsis":  "Gets SQL Credential information for each instance(s) of SQL Server.",
        "Name":  "Get-DbaCredential",
        "Links":  "https://dbatools.io/Get-DbaCredential",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaCredential -SqlInstance localhost\r\n\r\nReturns all SQL Credentials on the local default SQL Server instance\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaCredential -SqlInstance localhost, sql2016 -Name \u0027PowerShell Proxy\u0027\r\n\r\nReturns the SQL Credentials named \u0027PowerShell Proxy\u0027 for the local and sql2016 SQL Server instances\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaCredential -SqlInstance localhost, sql2016 -Identity ad\\powershell\r\n\r\nReturns the SQL Credentials for the account \u0027ad\\powershell\u0027 on the local and sql2016 SQL Server instances\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaCustomError",
        "Description":  "The Get-DbaCustomError command gets SQL Custom Error Message information for each instance(s) of SQL Server.",
        "Tags":  "CustomError",
        "Author":  "Garry Bargsley (@gbargsley), http://blog.garrybargsley.com",
        "Synopsis":  "Gets SQL Custom Error Message information for each instance(s) of SQL Server.",
        "Name":  "Get-DbaCustomError",
        "Links":  "https://dbatools.io/Get-DbaCustomError",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaCustomError -SqlInstance localhost\r\n\r\nReturns all Custom Error Message(s) on the local default SQL Server instance\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaCustomError -SqlInstance localhost, sql2016\r\n\r\nReturns all Custom Error Message(s) for the local and sql2016 SQL Server instances\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaDatabase",
        "Description":  "The Get-DbaDatabase command gets SQL database information for each database that is present on the target instance(s) of\nSQL Server. If the name of the database is provided, the command will return only the specific database information.",
        "Tags":  "Database",
        "Author":  "Garry Bargsley (@gbargsley | http://blog.garrybargsley.com)",
        "Synopsis":  "Gets SQL Database information for each database that is present on the target instance(s) of SQL Server.",
        "Name":  "Get-DbaDatabase",
        "Links":  "https://dbatools.io/Get-DbaDatabase",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabase -SqlInstance localhost\r\n\r\nReturns all databases on the local default SQL Server instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabase -SqlInstance localhost -ExcludeAllUserDb\r\n\r\nReturns only the system databases on the local default SQL Server instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabase -SqlInstance localhost -ExcludeAllSystemDb\r\n\r\nReturns only the user databases on the local default SQL Server instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003e\u0027localhost\u0027,\u0027sql2016\u0027 | Get-DbaDatabase\r\n\r\nReturns databases on multiple instances piped into the function.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabase -SqlInstance SQL1\\SQLExpress -RecoveryModel full,Simple\r\n\r\nReturns only the user databases in Full or Simple recovery model from SQL Server instance SQL1\\SQLExpress.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabase -SqlInstance SQL1\\SQLExpress -Status Normal\r\n\r\nReturns only the user databases with status \u0027normal\u0027 from SQL Server instance SQL1\\SQLExpress.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 7 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabase -SqlInstance SQL1\\SQLExpress -IncludeLastUsed\r\n\r\nReturns the databases from SQL Server instance SQL1\\SQLExpress and includes the last used information\r\nfrom the sys.dm_db_index_usage_stats DMV.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 8 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabase -SqlInstance SQL1\\SQLExpress,SQL2 -ExcludeDatabase model,master\r\n\r\nReturns all databases except master and model from SQL Server instances SQL1\\SQLExpress and SQL2.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 9 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabase -SqlInstance SQL1\\SQLExpress,SQL2 -Encrypted\r\n\r\nReturns only databases using TDE from SQL Server instances SQL1\\SQLExpress and SQL2.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 10 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabase -SqlInstance SQL1\\SQLExpress,SQL2 -Access ReadOnly\r\n\r\nReturns only read only databases from SQL Server instances SQL1\\SQLExpress and SQL2.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 11 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabase -SqlInstance SQL2,SQL3 -Database OneDB,OtherDB\r\n\r\nReturns databases \u0027OneDb\u0027 and \u0027OtherDB\u0027 from SQL Server instances SQL2 and SQL3 if databases by those names exist on \r\nthose instances.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaDatabaseAssembly",
        "Description":  "The Get-DbaDatabaseAssembly command gets SQL Database Assembly information for each instance(s) of SQL Server.",
        "Tags":  [
                     "Assembly",
                     "Database"
                 ],
        "Author":  "Garry Bargsley (@gbargsley), http://blog.garrybargsley.com",
        "Synopsis":  "Gets SQL Database Assembly information for each instance(s) of SQL Server.",
        "Name":  "Get-DbaDatabaseAssembly",
        "Links":  "https://dbatools.io/Get-DbaDatabaseAssembly",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabaseAssembly -SqlInstance localhost\r\n\r\nReturns all Database Assembly on the local default SQL Server instance\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabaseAssembly -SqlInstance localhost, sql2016\r\n\r\nReturns all Database Assembly for the local and sql2016 SQL Server instances\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaDatabaseEncryption",
        "Description":  "Shows if a database has Transparent Data Encryption (TDE), any certificates, asymmetric keys or symmetric keys with details for each.",
        "Tags":  [
                     "Encryption",
                     "Database"
                 ],
        "Author":  "Stephen Bennett, https://sqlnotesfromtheunderground.wordpress.com/",
        "Synopsis":  "Returns a summary of encryption used on databases passed to it.",
        "Name":  "Get-DbaDatabaseEncryption",
        "Links":  "https://dbatools.io/Get-DbaDatabaseEncryption",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabaseEncryption -SqlInstance DEV01\r\n\r\nList all encryption found on the instance by database\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabaseEncryption -SqlInstance DEV01 -Database MyDB\r\n\r\nList all encryption found for the MyDB database.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabaseEncryption -SqlInstance DEV01 -ExcludeDatabase MyDB\r\n\r\nList all encryption found for all databases except MyDB.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabaseEncryption -SqlInstance DEV01 -IncludeSystemDBs\r\n\r\nList all encryption found for all databases including the system databases.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaDatabaseFile",
        "Description":  "Returns detailed information about database files. Does not use SMO - SMO causes enumeration and this command avoids that.",
        "Tags":  "Database",
        "Author":  "Stuart Moore (@napalmgram), stuart-moore.com",
        "Synopsis":  "Returns detailed information about database files.",
        "Name":  "Get-DbaDatabaseFile",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabaseFile -SqlInstance sql2016\r\n\r\nWill return an object containing all filegroups and their contained files for every database on the sql2016 SQL Server \r\ninstance\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabaseFile -SqlInstance sql2016 -Database Impromptu\r\n\r\nWill return an object containing all filegroups and their contained files for the Impromptu Database on the sql2016 SQL \r\nServer instance\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabaseFile -SqlInstance sql2016 -Database Impromptu, Trading\r\n\r\nWill return an object containing all filegroups and their contained files for the Impromptu and Trading databases on \r\nthe sql2016 SQL Server instance\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaDatabaseMasterKey",
        "Description":  "Gets specified database master key",
        "Tags":  [
                     "Certificate",
                     "Database"
                 ],
        "Synopsis":  "Gets specified database master key",
        "Name":  "Get-DbaDatabaseMasterKey",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabaseMasterKey -SqlInstance sql2016\r\n\r\nGets all master database keys\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabaseMasterKey -SqlInstance Server1 -Database db1\r\n\r\nGets the master key for the db1 database\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaDatabasePartitionFunction",
        "Description":  "Gets database Partition Functions",
        "Tags":  "Database",
        "Author":  "Klaas Vandenberghe ( @PowerDbaKlaas )",
        "Synopsis":  "Gets database Partition Functions",
        "Name":  "Get-DbaDatabasePartitionFunction",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabasePartitionFunction -SqlInstance sql2016\r\n\r\nGets all database Partition Functions\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabasePartitionFunction -SqlInstance Server1 -Database db1\r\n\r\nGets the Partition Functions for the db1 database\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabasePartitionFunction -SqlInstance Server1 -ExcludeDatabase db1\r\n\r\nGets the Partition Functions for all databases except db1\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003e\u0027Sql1\u0027,\u0027Sql2/sqlexpress\u0027 | Get-DbaDatabasePartitionFunction\r\n\r\nGets the Partition Functions for the databases on Sql1 and Sql2/sqlexpress\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaDatabasePartitionScheme",
        "Description":  "Gets database Partition Schemes",
        "Tags":  "Database",
        "Author":  "Klaas Vandenberghe ( @PowerDbaKlaas )",
        "Synopsis":  "Gets database Partition Schemes",
        "Name":  "Get-DbaDatabasePartitionScheme",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabasePartitionScheme -SqlInstance sql2016\r\n\r\nGets all database Partition Schemes\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabasePartitionScheme -SqlInstance Server1 -Database db1\r\n\r\nGets the Partition Schemes for the db1 database\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabasePartitionScheme -SqlInstance Server1 -ExcludeDatabase db1\r\n\r\nGets the Partition Schemes for all databases except db1\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003e\u0027Sql1\u0027,\u0027Sql2/sqlexpress\u0027 | Get-DbaDatabasePartitionScheme\r\n\r\nGets the Partition Schemes for the databases on Sql1 and Sql2/sqlexpress\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaDatabaseSpace",
        "Description":  "This function returns database file space information for a SQL Instance or group of SQL Instances. Information is based on a query against sys.database_files and the FILEPROPERTY function to query and return information.\n\nFile free space script borrowed and modified from Glenn Berry\u0027s DMV scripts (http://www.sqlskills.com/blogs/glenn/category/dmv-queries/)",
        "Tags":  [
                     "Database",
                     "Space",
                     "Storage"
                 ],
        "Author":  "Michael Fal (@Mike_Fal), http://mikefal.net",
        "Synopsis":  "Returns database file space information for database files on a SQL instance.",
        "Name":  "Get-DbaDatabaseSpace",
        "Links":  "https://dbatools.io/Get-DbaDatabaseSpace",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabaseSpace -SqlInstance localhost\r\n\r\nReturns all user database files and free space information for the localhost.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabaseSpace -SqlInstance localhost | Where-Object {$_.PercentUsed -gt 80}\r\n\r\nReturns all user database files and free space information for the local host. Filters the output object by any files \r\nthat have a percent used of greater than 80%.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003e\u0027localhost\u0027,\u0027localhost\\namedinstance\u0027 | Get-DbaDatabaseSpace\r\n\r\nReturns all user database files and free space information for the localhost and localhost\\namedinstance SQL Server \r\ninstances. Processes data via the pipeline.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabaseSpace -SqlInstance localhost -Database db1, db2\r\n\r\nReturns database files and free space information for the db1 and db2 on localhost.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaDatabaseState",
        "Description":  "Gets some common \"states\" on databases:\n - \"RW\" options : READ_ONLY or READ_WRITE\n - \"Status\" options : ONLINE, OFFLINE, EMERGENCY, RESTORING\n - \"Access\" options : SINGLE_USER, RESTRICTED_USER, MULTI_USER\n\nReturns an object with SqlInstance, Database, RW, Status, Access",
        "Tags":  "Database",
        "Author":  "niphlod",
        "Synopsis":  "Gets various options for databases, hereby called \"states\"",
        "Name":  "Get-DbaDatabaseState",
        "Links":  "https://dbatools.io/Get-DbaDatabaseState",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabaseState -SqlInstance sqlserver2014a\r\n\r\nGets options for all databases of the sqlserver2014a instance\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabaseState -SqlInstance sqlserver2014a -Database HR, Accounting\r\n\r\nGets options for both HR and Accounting database of the sqlserver2014a instance\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabaseState -SqlInstance sqlserver2014a -Exclude HR\r\n\r\nGets options for all databases of the sqlserver2014a instance except HR\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003e\u0027sqlserver2014a\u0027, \u0027sqlserver2014b\u0027 | Get-DbaDatabaseState\r\n\r\nGets options for all databases of sqlserver2014a and sqlserver2014b instances\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaDatabaseUdf",
        "Description":  "Gets database User Defined Functions",
        "Tags":  [
                     "Security",
                     "Database"
                 ],
        "Author":  "Klaas Vandenberghe ( @PowerDbaKlaas )",
        "Synopsis":  "Gets database User Defined Functions",
        "Name":  "Get-DbaDatabaseUdf",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabaseUdf -SqlInstance sql2016\r\n\r\nGets all database User Defined Functions\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabaseUdf -SqlInstance Server1 -Database db1\r\n\r\nGets the User Defined Functions for the db1 database\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabaseUdf -SqlInstance Server1 -ExcludeDatabase db1\r\n\r\nGets the User Defined Functions for all databases except db1\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabaseUdf -SqlInstance Server1 -ExcludeSystemUdf\r\n\r\nGets the User Defined Functions for all databases that are not system objects (there can be 100+ system User Defined \r\nFunctions in each DB)\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003e\u0027Sql1\u0027,\u0027Sql2/sqlexpress\u0027 | Get-DbaDatabaseUdf\r\n\r\nGets the User Defined Functions for the databases on Sql1 and Sql2/sqlexpress\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaDatabaseUser",
        "Description":  "Gets database users",
        "Tags":  [
                     "Security",
                     "Database"
                 ],
        "Author":  "Klaas Vandenberghe ( @PowerDbaKlaas )",
        "Synopsis":  "Gets database users",
        "Name":  "Get-DbaDatabaseUser",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabaseUser -SqlInstance sql2016\r\n\r\nGets all database users\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabaseUser -SqlInstance Server1 -Database db1\r\n\r\nGets the users for the db1 database\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabaseUser -SqlInstance Server1 -ExcludeDatabase db1\r\n\r\nGets the users for all databases except db1\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabaseUser -SqlInstance Server1 -ExcludeSystemUser\r\n\r\nGets the users for all databases that are not system objects, like \u0027dbo\u0027, \u0027guest\u0027 or \u0027INFORMATION_SCHEMA\u0027\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003e\u0027Sql1\u0027,\u0027Sql2/sqlexpress\u0027 | Get-DbaDatabaseUser\r\n\r\nGets the users for the databases on Sql1 and Sql2/sqlexpress\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaDatabaseView",
        "Description":  "Gets database views for each SqlInstance.",
        "Tags":  [
                     "Security",
                     "Database"
                 ],
        "Author":  "Klaas Vandenberghe ( @PowerDbaKlaas )",
        "Synopsis":  "Gets database views for each SqlInstance.",
        "Name":  "Get-DbaDatabaseView",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabaseView -SqlInstance sql2016\r\n\r\nGets all database views\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabaseView -SqlInstance Server1 -Database db1\r\n\r\nGets the views for the db1 database\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabaseView -SqlInstance Server1 -ExcludeDatabase db1\r\n\r\nGets the views for all databases except db1\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabaseView -SqlInstance Server1 -ExcludeSystemView\r\n\r\nGets the views for all databases that are not system objects (there can be 400+ system views in each DB)\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003e\u0027Sql1\u0027,\u0027Sql2/sqlexpress\u0027 | Get-DbaDatabaseView\r\n\r\nGets the views for the databases on Sql1 and Sql2/sqlexpress\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaDbCertificate",
        "Description":  "Gets database certificates",
        "Tags":  "Certificate",
        "Synopsis":  "Gets database certificates",
        "Name":  "Get-DbaDbCertificate",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbCertificate -SqlInstance sql2016\r\n\r\nGets all certificates\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbCertificate -SqlInstance Server1 -Database db1\r\n\r\nGets the certificate for the db1 database\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbCertificate -SqlInstance Server1 -Database db1 -Certificate cert1\r\n\r\nGets the cert1 certificate within the db1 database\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaDbCheckConstraint",
        "Description":  "Gets database Checks constraints.",
        "Tags":  "Database",
        "Author":  "Cláudio Silva ( @ClaudioESSilva | https://claudioessilva.eu)",
        "Synopsis":  "Gets database Check constraints.",
        "Name":  "Get-DbaDbCheckConstraint",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbCheckConstraint -SqlInstance sql2016\r\n\r\nGets all database check constraints.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbCheckConstraint -SqlInstance Server1 -Database db1\r\n\r\nGets the check constraints for the db1 database.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbCheckConstraint -SqlInstance Server1 -ExcludeDatabase db1\r\n\r\nGets the check constraints for all databases except db1.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbCheckConstraint -SqlInstance Server1 -ExcludeSystemTable\r\n\r\nGets the check constraints for all databases that are not system objects.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003e\u0027Sql1\u0027,\u0027Sql2/sqlexpress\u0027 | Get-DbaDbCheckConstraint\r\n\r\nGets the check constraints for the databases on Sql1 and Sql2/sqlexpress.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaDbCompression",
        "Description":  "This function gets the current size and compression for all objects in the specified database(s), if no database is specified it will return all objects in all user databases.",
        "Tags":  [
                     "Compression",
                     "Table",
                     "Database"
                 ],
        "Author":  "Jess Pomfret (@jpomfret jesspomfret.com)",
        "Synopsis":  "Gets tables and indexes size and current compression settings.",
        "Name":  "Get-DbaDbCompression",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbCompression -SqlInstance localhost\r\n\r\nReturns objects size and current compression level for all user databases.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbCompression -SqlInstance localhost -Database TestDatabase\r\n\r\nReturns objects size and current compression level for objects within the TestDatabase database.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbCompression -SqlInstance localhost -ExcludeDatabase TestDatabases\r\n\r\nReturns objects size and current compression level for objects in all databases except the TestDatabase database.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaDbExtentDiff",
        "Description":  "This is only an implementation of the script created by Paul S. Randal to find what percentage of a database has changed since the last full backup.\nhttps://www.sqlskills.com/blogs/paul/new-script-how-much-of-the-database-has-changed-since-the-last-full-backup/",
        "Tags":  [
                     "Backup",
                     "Database"
                 ],
        "Author":  "Viorel Ciucu, [email protected], cviorel.com",
        "Synopsis":  "What percentage of a database has changed since the last full backup",
        "Name":  "Get-DbaDbExtentDiff",
        "Links":  "http://dbatools.io/Get-DbaDbExtentDiff",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet the changes for the DBA database.\r\n\r\nGet-DbaDbExtentDiff -SqlInstance SQL2016 -Database DBA\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet the changes for the DB01 database on multiple servers.\r\n\r\nGet-DbaDbExtentDiff -SqlInstance $SQL2017N1, $SQL2017N2, $SQL2016 -Database DB01 -SqlCredential $Cred\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaDbForeignKey",
        "Description":  "Gets database Foreign Keys.",
        "Tags":  [
                     "Database",
                     "ForeignKey",
                     "Table"
                 ],
        "Author":  "Cláudio Silva ( @ClaudioESSilva | https://claudioessilva.eu)",
        "Synopsis":  "Gets database Foreign Keys.",
        "Name":  "Get-DbaDbForeignKey",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbForeignKey -SqlInstance sql2016\r\n\r\nGets all database Foreign Keys.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbForeignKey -SqlInstance Server1 -Database db1\r\n\r\nGets the Foreign Keys for the db1 database.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbForeignKey -SqlInstance Server1 -ExcludeDatabase db1\r\n\r\nGets the Foreign Keys for all databases except db1.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbForeignKey -SqlInstance Server1 -ExcludeSystemTable\r\n\r\nGets the Foreign Keys from all tables that are not system objects from all databases.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003e\u0027Sql1\u0027,\u0027Sql2/sqlexpress\u0027 | Get-DbaDbForeignKey\r\n\r\nGets the Foreign Keys for the databases on Sql1 and Sql2/sqlexpress.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaDbMailHistory",
        "Description":  "Gets the history of mail sent from a SQL instance",
        "Tags":  "Logging",
        "Synopsis":  "Gets the history of mail sent from a SQL instance",
        "Name":  "Get-DbaDbMailHistory",
        "Links":  "https://dbatools.io/Get-DbaDbMailHistory",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbMailHistory -SqlInstance sql01\\sharepoint\r\n\r\nReturns the entire dbmail history on sql01\\sharepoint\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbMailHistory -SqlInstance sql01\\sharepoint | Select *\r\n\r\nReturns the entire dbmail history on sql01\\sharepoint then return a bunch more columns\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003e$servers = \"sql2014\",\"sql2016\", \"sqlcluster\\sharepoint\"\r\n\r\n$servers | Get-DbaDbMailHistory\r\n\r\nReturns the all dbmail history for \"sql2014\",\"sql2016\" and \"sqlcluster\\sharepoint\"\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaDbMailLog",
        "Description":  "Gets the DBMail log from a SQL instance",
        "Tags":  "Logging",
        "Synopsis":  "Gets the DBMail log from a SQL instance",
        "Name":  "Get-DbaDbMailLog",
        "Links":  "https://dbatools.io/Get-DbaDbMailLog",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbMailLog -SqlInstance sql01\\sharepoint\r\n\r\nReturns the entire dbmail log on sql01\\sharepoint\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbMailLog -SqlInstance sql01\\sharepoint | Select *\r\n\r\nReturns the entire dbmail log on sql01\\sharepoint then return a bunch more columns\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003e$servers = \"sql2014\",\"sql2016\", \"sqlcluster\\sharepoint\"\r\n\r\n$servers | Get-DbaDbMailLog -Type Error, Information\r\n\r\nReturns only the Error and Information dbmail log for \"sql2014\",\"sql2016\" and \"sqlcluster\\sharepoint\"\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaDbPageInfo",
        "Description":  "Get-DbaDbPageInfo is able to return information about the pages in a database.\nIt\u0027s possible to return the information for multiple databases and filter on specific databases, schemas and tables.",
        "Tags":  [
                     "Database",
                     "Page"
                 ],
        "Synopsis":  "Get-DbaDbPageInfo will return page information for a database",
        "Name":  "Get-DbaDbPageInfo",
        "Links":  "https://dbatools.io/Get-DbaDbPageInfo",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbPageInfo -SqlInstance sql2017\r\n\r\nReturns page information for all databases on sql2017\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbPageInfo -SqlInstance sql2017, sql2016 -Database testdb\r\n\r\nReturns page information for the testdb on sql2017 and sql2016\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003e$servers | Get-DbaDatabase -Database testdb | Get-DbaDbPageInfo\r\n\r\nReturns page information for the testdb on all $servers\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaDbQueryStoreOptions",
        "Description":  "Retrieves and returns the Query Store configuration for every database that has the Query Store feature enabled.",
        "Tags":  "QueryStore",
        "Author":  "Enrico van de Laar ( @evdlaar )",
        "Synopsis":  "Get the Query Store configuration for Query Store enabled databases.",
        "Name":  "Get-DbaDbQueryStoreOptions",
        "Links":  "https://dbatools.io/Get-DbaQueryStoreOptions",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbQueryStoreOptions -SqlInstance ServerA\\sql\r\n\r\nReturns Query Store configuration settings for every database on the ServerA\\sql instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbQueryStoreOptions -SqlInstance ServerA\\sql | Where-Object {$_.ActualState -eq \"ReadWrite\"}\r\n\r\nReturns the Query Store configuration for all databases on ServerA\\sql where the Query Store feature is in Read/Write \r\nmode.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbQueryStoreOptions -SqlInstance localhost | format-table -AutoSize -Wrap\r\n\r\nReturns Query Store configuration settings for every database on the ServerA\\sql instance inside a table format.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaDbRecoveryModel",
        "Description":  "Get-DbaDbRecoveryModel displays the Recovery Model for all databases. This is the default, you can filter using -Database, -ExcludeDatabase, -RecoveryModel",
        "Tags":  [
                     "Recovery",
                     "RecoveryModel",
                     "Simple",
                     "Full",
                     "Bulk",
                     "BulkLogged"
                 ],
        "Author":  "Viorel Ciucu (@viorelciucu), https://www.cviorel.com",
        "Synopsis":  "Get-DbaDbRecoveryModel displays the Recovery Model.",
        "Name":  "Get-DbaDbRecoveryModel",
        "Links":  "https://dbatools.io/Get-DbaDbRecoveryModel",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbRecoveryModel -SqlInstance sql2014 -RecoveryModel BulkLogged -Verbose\r\n\r\nGets all databases on SQL Server instance sql2014 having RecoveryModel set to BulkLogged\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbRecoveryModel -SqlInstance sql2014 -Database TestDB\r\n\r\nGets recovery model information for TestDB. If TestDB does not exist on the instance we don\u0027t return anythig.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaDbRole",
        "Description":  "Get database roles on a Sql instance.\n\nDefault output includes columns SQLServer, Database, Role.",
        "Tags":  [
                     "Roles",
                     "Database",
                     "Security"
                 ],
        "Author":  "Klaas Vandenberghe ( @PowerDBAKlaas )",
        "Synopsis":  "Get database roles on a Sql instance.",
        "Name":  "Get-DbaDbRole",
        "Links":  "https://dbatools.io/Get-DbaDbRole",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbRole -SqlInstance ServerA\r\n\r\nReturns a custom object displaying SQLServer, Database, Role for all DatabaseRoles on sql instance ServerA.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbRole -SqlInstance ServerA | Out-Gridview\r\n\r\nReturns a gridview displaying SQLServer, Database, Role for all DatabaseRoles on sql instance ServerA.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbRole -SqlInstance ServerB\\sql16 -ExcludeDatabase DBADB,TestDB\r\n\r\nReturns SQLServer, Database, Role for DatabaseRoles on sql instance ServerB\\sql16, except those in databases DBADB and \r\nTestDB.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003e\u0027ServerB\\sql16\u0027,\u0027ServerA\u0027 | Get-DbaDbRole\r\n\r\nReturns SQLServer, Database, Role for DatabaseRoles on sql instances ServerA and ServerB\\sql16.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbRole -SqlInstance ServerB\\sql16 -Database AccountingDB\r\n\r\nReturns SQLServer, Database, Role for DatabaseRoles in database AccountingDB on sql instance ServerB\\sql16.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbRole -SqlInstance ServerB\\sql16 -ExcludeFixedRoles\r\n\r\nReturns SQLServer, Database, Role for DatabaseRoles on sql instance ServerB\\sql16, but not the fixed roles.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaDbSnapshot",
        "Description":  "Retrieves the list of database snapshot available, along with their base (the db they are the snapshot of) and creation time",
        "Tags":  "Snapshot",
        "Author":  "niphlod",
        "Synopsis":  "Get database snapshots with details",
        "Name":  "Get-DbaDbSnapshot",
        "Links":  "https://dbatools.io/Get-DbaDbSnapshot",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbSnapshot -SqlInstance sqlserver2014a\r\n\r\nReturns a custom object displaying Server, Database, DatabaseCreated, SnapshotOf, SizeMB, DatabaseCreated\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbSnapshot -SqlInstance sqlserver2014a -Database HR, Accounting\r\n\r\nReturns information for database snapshots having HR and Accounting as base dbs\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbSnapshot -SqlInstance sqlserver2014a -Snapshot HR_snapshot, Accounting_snapshot\r\n\r\nReturns information for database snapshots HR_snapshot and Accounting_snapshot\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaDbStoredProcedure",
        "Description":  "Gets database Stored Procedures",
        "Tags":  [
                     "Database",
                     "StoredProcedure",
                     "Proc"
                 ],
        "Author":  "Klaas Vandenberghe ( @PowerDbaKlaas )",
        "Synopsis":  "Gets database Stored Procedures",
        "Name":  "Get-DbaDbStoredProcedure",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbStoredProcedure -SqlInstance sql2016\r\n\r\nGets all database Stored Procedures\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbStoredProcedure -SqlInstance Server1 -Database db1\r\n\r\nGets the Stored Procedures for the db1 database\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbStoredProcedure -SqlInstance Server1 -ExcludeDatabase db1\r\n\r\nGets the Stored Procedures for all databases except db1\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbStoredProcedure -SqlInstance Server1 -ExcludeSystemSp\r\n\r\nGets the Stored Procedures for all databases that are not system objects\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003e\u0027Sql1\u0027,\u0027Sql2/sqlexpress\u0027 | Get-DbaDbStoredProcedure\r\n\r\nGets the Stored Procedures for the databases on Sql1 and Sql2/sqlexpress\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaDbVirtualLogFile",
        "Description":  "Having a transaction log file with too many virtual log files (VLFs) can hurt database performance.\n\nToo many VLFs can cause transaction log backups to slow down and can also slow down database recovery and, in extreme cases, even affect insert/update/delete performance.\n\nReferences:\n    http://www.sqlskills.com/blogs/kimberly/transaction-log-vlfs-too-many-or-too-few/\n    http://blogs.msdn.com/b/saponsqlserver/archive/2012/02/22/too-many-virtual-log-files-vlfs-can-cause-slow-database-recovery.aspx\n\nIf you\u0027ve got a high number of VLFs, you can use Expand-SqlTLogResponsibly to reduce the number.",
        "Tags":  [
                     "VLF",
                     "Database",
                     "LogFile"
                 ],
        "Synopsis":  "Returns database virtual log file information for database files on a SQL instance.",
        "Name":  "Get-DbaDbVirtualLogFile",
        "Links":  "https://dbatools.io/Get-DbaDbVirtualLogFile",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbVirtualLogFile -SqlInstance sqlcluster\r\n\r\nReturns all user database virtual log file details for the sqlcluster instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbVirtualLogFile -SqlInstance sqlserver | Group-Object -Property Database | Where-Object Count -gt 50\r\n\r\nReturns user databases that have 50 or more VLFs.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003e@(\u0027sqlserver\u0027,\u0027sqlcluster\u0027) | Get-DbaDbVirtualLogFile\r\n\r\nReturns all VLF information for the sqlserver and sqlcluster SQL Server instances. Processes data via the pipeline.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbVirtualLogFile -SqlInstance sqlcluster -Database db1, db2\r\n\r\nReturns the VLF counts for the db1 and db2 databases on sqlcluster.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaDefaultPath",
        "Description":  "Gets the default SQL Server paths for data, logs and backups",
        "Tags":  "Config",
        "Synopsis":  "Gets the default SQL Server paths for data, logs and backups",
        "Name":  "Get-DbaDefaultPath",
        "Links":  "https://dbatools.io/Get-DbaDefaultPath",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDefaultPath -SqlInstance sql01\\sharepoint\r\n\r\nReturns the default file paths for sql01\\sharepoint\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e$servers = \"sql2014\",\"sql2016\", \"sqlcluster\\sharepoint\"\r\n\r\n$servers | Get-DbaDefaultPath\r\n\r\nReturns the default file paths for \"sql2014\",\"sql2016\" and \"sqlcluster\\sharepoint\"\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaDependency",
        "Description":  "This function recursively finds all objects that depends on the input.\nIt will then retrieve rich information from them, including their creation scripts and the order in which it should be applied.\n\nBy using the \u0027Parents\u0027 switch, the function will instead retrieve all items that the input depends on (including their creation scripts).\n\nFor more details on dependency, see:\nhttps://technet.microsoft.com/en-us/library/ms345449(v=sql.105).aspx",
        "Tags":  [
                     "Database",
                     "Dependent",
                     "Dependency",
                     "Object"
                 ],
        "Synopsis":  "Finds object dependencies and their relevant creation scripts.",
        "Name":  "Get-DbaDependency",
        "Links":  "https://dbatools.io/Get-DbaDependency",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003e$table = (Get-DbaDatabase -SqlInstance sql2012 -Database Northwind).tables | Where Name -eq Customers\r\n\r\n$table | Get-DbaDependency\r\n\r\nReturns everything that depends on the \"Customers\" table\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaDetachedDatabaseInfo",
        "Description":  "Gathers the following information from detached database files: database name, SQL Server version (compatibility level), collation, and file structure.\n\n\"Data files\" and \"Log file\" report the structure of the data and log files as they were when the database was detached. \"Database version\" is the compatibility level.\n\nMDF files are most easily read by using a SQL Server to interpret them. Because of this, you must specify a SQL Server and the path must be relative to the SQL Server.",
        "Tags":  [
                     "Database",
                     "Detach"
                 ],
        "Synopsis":  "Get detailed information about detached SQL Server database files.",
        "Name":  "Get-DbaDetachedDatabaseInfo",
        "Links":  "https://dbatools.io/Get-DbaDetachedDatabaseInfo",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDetachedDatabaseInfo -SqlInstance sql2016 -Path M:\\Archive\\mydb.mdf\r\n\r\nReturns information about the detached database file M:\\Archive\\mydb.mdf using the SQL Server instance sql2016. The M \r\ndrive is relative to the SQL Server instance.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaDiskSpace",
        "Description":  "Returns a custom object with server name, name of disk, label of disk, total size, free size, percent free, block size and filesystem.\n\nBy default, this function only shows drives of types 2 and 3 (removable disk and local disk).\n\nRequires Windows administrator access on SQL Servers",
        "Tags":  [
                     "Storage",
                     "Disk"
                 ],
        "Author":  "Chrissy LeMaire ([email protected]) \u0026 Jakob Bindslet ([email protected])",
        "Synopsis":  "Displays disk information for all local disk on a server.",
        "Name":  "Get-DbaDiskSpace",
        "Links":  "https://dbatools.io/Get-DbaDiskSpace",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDiskSpace -ComputerName srv0042\r\n\r\nGet disk space for the server srv0042.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDiskSpace -ComputerName srv0042 -Unit MB\r\n\r\nGet disk space for the server srv0042 and displays in megabytes (MB).\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDiskSpace -ComputerName srv0042, srv0007 -Unit TB\r\n\r\nGet disk space from two servers and displays in terabytes (TB).\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDiskSpace -ComputerName srv0042 -Force\r\n\r\nGet all disk and volume space information.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDiskSpace -ComputerName srv0042 -ExcludeDrive \u0027C:\\\u0027\r\n\r\nGet all disk and volume space information.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaDistributor",
        "Description":  "This function locates and enumerates distributor information for a given SQL Server instance.",
        "Tags":  "Replication",
        "Author":  "William Durkin, @sql_williamd",
        "Synopsis":  "Gets the information about a replication distributor for a given SQL Server instance.",
        "Name":  "Get-DbaDistributor",
        "Links":  "https://dbatools.io/Get-DbaDistributor",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDistributor -SqlInstance sql2008, sqlserver2012\r\n\r\nRetrieve distributor information for servers sql2008 and sqlserver2012.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaDump",
        "Description":  "The type of dump included in the search include minidump, all-thread dump, or a full dump.  The files have an extendion of .mdmp.",
        "Tags":  [
                     "Engine",
                     "Corruption"
                 ],
        "Author":  "Garry Bargsley (@gbargsley), http://blog.garrybargsley.com",
        "Synopsis":  "Locate a SQL Server that has generated any memory dump files.",
        "Name":  "Get-DbaDump",
        "Links":  "https://dbatools.io/Get-DbaDump",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDump -SqlInstance sql2016\r\n\r\nShows the detailed information for memory dump(s) located on sql2016 instance\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDump -SqlInstance sql2016 -SqlCredential (Get-Credential sqladmin)\r\n\r\nShows the detailed information for memory dump(s) located on sql2016 instance. Logs into the SQL Server using the SQL \r\nlogin \u0027sqladmin\u0027\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaEndpoint",
        "Description":  "The Get-DbaEndpoint command gets SQL Endpoint(s) information for each instance(s) of SQL Server.",
        "Tags":  "Endpoint",
        "Author":  "Garry Bargsley (@gbargsley), http://blog.garrybargsley.com",
        "Synopsis":  "Gets SQL Endpoint(s) information for each instance(s) of SQL Server.",
        "Name":  "Get-DbaEndpoint",
        "Links":  "https://dbatools.io/Get-DbaEndpoint",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaEndpoint -SqlInstance localhost\r\n\r\nReturns all Endpoint(s) on the local default SQL Server instance\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaEndpoint -SqlInstance localhost, sql2016\r\n\r\nReturns all Endpoint(s) for the local and sql2016 SQL Server instances\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaErrorLog",
        "Description":  "Gets the \"SQL Error Log\" of an instance. Returns all 10 error logs by default.",
        "Tags":  [
                     "Instance",
                     "ErrorLog"
                 ],
        "Synopsis":  "Gets the \"SQL Error Log\" of an instance",
        "Name":  "Get-DbaErrorLog",
        "Links":  "https://dbatools.io/Get-DbaErrorLog",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaErrorLog -SqlInstance sql01\\sharepoint\r\n\r\nReturns every log entry from sql01\\sharepoint SQL Server instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaErrorLog -SqlInstance sql01\\sharepoint -LogNumber 3, 6\r\n\r\nReturns all log entries for log number 3 and 6 on sql01\\sharepoint SQL Server instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaErrorLog -SqlInstance sql01\\sharepoint -Source Logon\r\n\r\nReturns every log entry, with a source of Logon, from sql01\\sharepoint SQL Server instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaErrorLog -SqlInstance sql01\\sharepoint -LogNumber 3 -Text \"login failed\"\r\n\r\nReturns every log entry for log number 3, with \"login failed\" in the text, from sql01\\sharepoint SQL Server instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003e$servers = \"sql2014\",\"sql2016\", \"sqlcluster\\sharepoint\"\r\n\r\n$servers | Get-DbaErrorLog -LogNumber 0\r\n\r\nReturns the most recent SQL Server error logs for \"sql2014\",\"sql2016\" and \"sqlcluster\\sharepoint\"\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaErrorLog -SqlInstance sql01\\sharepoint -After \u002711/14/2006 00:00\u0027\r\n\r\nReturns every log entry found after the date 11/14/2006 00:00 from sql101\\sharepoint SQL Server instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 7 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaErrorLog -SqlInstance sql01\\sharepoint -Before \u002708/16/2016 00:00\u0027\r\n\r\nReturns every log entry found before the date 08/16/2016 00:00 from sql101\\sharepoint SQL Server instance.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaErrorLogConfig",
        "Description":  "Pulls the configuration for the ErrorLog on a given SQL Server instance.\n\nIncludes error log path, number of log files configured and size (SQL Server 2012+ only)",
        "Tags":  [
                     "Instance",
                     "ErrorLog"
                 ],
        "Author":  "Shawn Melton (@wsmelton)",
        "Synopsis":  "Pulls the configuration for the ErrorLog on a given SQL Server instance",
        "Name":  "Get-DbaErrorLogConfig",
        "Links":  "https://dbatools.io/Get-DbaErrorLogConfig",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaErrorLogConfig -SqlInstance server2017,server2014\r\n\r\nReturns error log configuration for server2017 and server2014\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaEstimatedCompletionTime",
        "Description":  "Gets execution and estimated completion time information for queries\n\nPercent complete will show for the following commands\n\nALTER INDEX REORGANIZE\nAUTO_SHRINK option with ALTER DATABASE\nBACKUP DATABASE\nDBCC CHECKDB\nDBCC CHECKFILEGROUP\nDBCC CHECKTABLE\nDBCC INDEXDEFRAG\nDBCC SHRINKDATABASE\nDBCC SHRINKFILE\nRECOVERY\nRESTORE DATABASE\nROLLBACK\nTDE ENCRYPTION\n\nFor additional information, check out https://blogs.sentryone.com/loriedwards/patience-dm-exec-requests/ and https://docs.microsoft.com/en-us/sql/relational-databases/system-dynamic-management-views/sys-dm-exec-requests-transact-sql",
        "Tags":  "Database",
        "Synopsis":  "Gets execution and estimated completion time information for queries",
        "Name":  "Get-DbaEstimatedCompletionTime",
        "Links":  "https://dbatools.io/Get-DbaEstimatedCompletionTime",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaEstimatedCompletionTime -SqlInstance sql2016\r\n\r\nGets estimated completion times for queries performed against the entire server\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaEstimatedCompletionTime -SqlInstance sql2016 | Select *\r\n\r\nGets estimated completion times for queries performed against the entire server PLUS the SQL query text of each command\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaEstimatedCompletionTime -SqlInstance sql2016 | Where-Object { $_.Text -match \u0027somequerytext\u0027 }\r\n\r\nGets results for commands whose queries only match specific text (match is like LIKE but way more powerful)\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaEstimatedCompletionTime -SqlInstance sql2016 -Database Northwind,pubs,Adventureworks2014\r\n\r\nGets estimated completion times for queries performed against the Northwind, pubs, and Adventureworks2014 databases\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaExecutionPlan",
        "Description":  "Gets execution plans and metadata. Can pipe to Export-DbaExecutionPlan :D\n\nThanks to\n    https://www.simple-talk.com/sql/t-sql-programming/dmvs-for-query-plan-metadata/\n    and\n    http://www.scarydba.com/2017/02/13/export-plans-cache-sqlplan-file/\nfor the idea and query.",
        "Tags":  "Performance",
        "Synopsis":  "Gets execution plans and metadata",
        "Name":  "Get-DbaExecutionPlan",
        "Links":  "https://dbatools.io/Get-DbaExecutionPlan",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaExecutionPlan -SqlInstance sqlserver2014a\r\n\r\nGets all execution plans on  sqlserver2014a\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaExecutionPlan -SqlInstance sqlserver2014a -Database db1, db2 -SinceLastExecution \u00277/1/2016 10:47:00\u0027\r\n\r\nGets all execution plans for databases db1 and db2 on sqlserver2014a since July 1, 2016 at 10:47 AM.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaExecutionPlan -SqlInstance sqlserver2014a, sql2016 -Exclude db1 | Format-Table\r\n\r\nGets execution plan info for all databases except db1 on sqlserver2014a and sql2016 and makes the output pretty\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaExecutionPlan -SqlInstance sql2014 -Database AdventureWorks2014, pubs -Force\r\n\r\nGets super detailed information for execution plans on only for AdventureWorks2014 and pubs\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaFile",
        "Description":  "This command searches all specified directories, allowing a DBA to see file information on a server without direct access\n\nYou can filter by extension using the -FileType parameter. By default, the default data directory will be returned. You can provide and additional paths to search using the -Path parameter.\n\nThanks to serg-52 for the query:  https://www.sqlservercentral.com/Forums/Topic1642213-391-1.aspx",
        "Tags":  "Discovery",
        "Author":  "Brandon Abshire, netnerds.net",
        "Synopsis":  "Get-DbaFile finds files in any directory specified on a remote SQL Server",
        "Name":  "Get-DbaFile",
        "Links":  "https://dbatools.io/Get-DbaFile",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaFile -SqlInstance sqlserver2014a -Path E:\\Dir1\r\n\r\nLogs into the SQL Server \"sqlserver2014a\" using Windows credentials and searches E:\\Dir for all files\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaFile -SqlInstance sqlserver2014a -SqlCredential $cred -Path \u0027E:\\sql files\u0027\r\n\r\nLogs into the SQL Server \"sqlserver2014a\" using alternative credentials and returns all files in \u0027E:\\sql files\u0027\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003e$all = Get-DbaDefaultPath -SqlInstance sql2014\r\n\r\nGet-DbaFile -SqlInstance sql2014 -Path $all.Data, $all.Log, $all.Backup -Depth 3\r\nReturns the files in the default data, log and backup directories on sql2014, 3 directories deep (recursively).\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaFile -SqlInstance sql2014 -Path \u0027E:\\Dir1\u0027, \u0027E:\\Dir2\u0027\r\n\r\nReturns the files in \"E:\\Dir1\" and \"E:Dir2\" on sql2014\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaFile -SqlInstance -Path \u0027E:\\Dir1\u0027 sql2014, sql2016 -FileType fsf, mld\r\n\r\nFinds files in E:\\Dir1 ending with \".fsf\" and \".mld\" for both the servers sql2014 and sql2016.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaFile -SqlInstance -Path \u0027E:\\Dir1\u0027 sql2014, sql2016 -FileType fsf, mld\r\n\r\nFinds files in E:\\Dir1 ending with \".fsf\" and \".mld\" for both the servers sql2014 and sql2016.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaForceNetworkEncryption",
        "Description":  "Gets Force Encryption settings for a SQL Server instance. Note that this requires access to the Windows Server - not the SQL instance itself.\n\nThis setting is found in Configuration Manager.",
        "Tags":  "Certificate",
        "Synopsis":  "Gets Force Encryption settings for a SQL Server instance",
        "Name":  "Get-DbaForceNetworkEncryption",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaForceNetworkEncryption\r\n\r\nGets Force Encryption properties on the default (MSSQLSERVER) instance on localhost - requires (and checks for) RunAs \r\nadmin.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaForceNetworkEncryption -SqlInstance sql01\\SQL2008R2SP2\r\n\r\nGets Force Network Encryption for the SQL2008R2SP2 on sql01. Uses Windows Credentials to both login and view the \r\nregistry.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaHelpIndex",
        "Description":  "This function will return detailed information on indexes (and optionally statistics) for all indexes in a database, or a given index should one be passed along.\nAs this uses SQL Server DMVs to access the data it will only work in 2005 and up (sorry folks still running SQL Server 2000).\nFor performance reasons certain statistics information will not be returned from SQL Server 2005 if an ObjectName is not provided.\n\nThe data includes:\n    - ObjectName: the table containing the index\n    - IndexType: clustered/non-clustered/columnstore and whether the index is unique/primary key\n    - KeyColumns: the key columns of the index\n    - IncludeColumns: any include columns in the index\n    - FilterDefinition: any filter that may have been used in the index\n    - DataCompression: row/page/none depending upon whether or not compression has been used\n    - IndexReads: the number of reads of the index since last restart or index rebuild\n    - IndexUpdates: the number of writes to the index since last restart or index rebuild\n    - SizeKB: the size the index in KB\n    - IndexRows: the number of the rows in the index (note filtered indexes will have fewer rows than exist in the table)\n    - IndexLookups: the number of lookups that have been performed (only applicable for the heap or clustered index)\n    - MostRecentlyUsed: when the index was most recently queried (default to 1900 for when never read)\n    - StatsSampleRows: the number of rows queried when the statistics were built/rebuilt (not included in SQL Server 2005 unless ObjectName is specified)\n    - StatsRowMods: the number of changes to the statistics since the last rebuild\n    - HistogramSteps: the number of steps in the statistics histogram (not included in SQL Server 2005 unless ObjectName is specified)\n    - StatsLastUpdated: when the statistics were last rebuilt (not included in SQL Server 2005 unless ObjectName is specified)",
        "Tags":  "Index",
        "Author":  "Nic Cain, https://sirsql.net/",
        "Synopsis":  "Returns size, row and configuration information for indexes in databases.",
        "Name":  "Get-DbaHelpIndex",
        "Links":  "https://dbatools.io/Get-DbaHelpIndex",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaHelpIndex -SqlInstance localhost -Database MyDB\r\n\r\nReturns information on all indexes on the MyDB database on the localhost.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaHelpIndex -SqlInstance localhost -Database MyDB,MyDB2\r\n\r\nReturns information on all indexes on the MyDB \u0026 MyDB2 databases.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaHelpIndex -SqlInstance localhost -Database MyDB -ObjectName dbo.Table1\r\n\r\nReturns index information on the object dbo.Table1 in the database MyDB.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaHelpIndex -SqlInstance localhost -Database MyDB -ObjectName dbo.Table1 -IncludeStats\r\n\r\nReturns information on the indexes and statistics for the table dbo.Table1 in the MyDB database.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaHelpIndex -SqlInstance localhost -Database MyDB -ObjectName dbo.Table1 -IncludeDataTypes\r\n\r\nReturns the index information for the table dbo.Table1 in the MyDB database, and includes the data types for the key \r\nand include columns.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaHelpIndex -SqlInstance localhost -Database MyDB -ObjectName dbo.Table1 -Raw\r\n\r\nReturns the index information for the table dbo.Table1 in the MyDB database, and returns the numerical data without \r\nlocalized separators.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 7 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaHelpIndex -SqlInstance localhost -Database MyDB -IncludeStats -Raw\r\n\r\nReturns the index information for all indexes in the MyDB database as well as their statistics, and formats the \r\nnumerical data without localized separators.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 8 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaHelpIndex -SqlInstance localhost -Database MyDB -IncludeFragmentation\r\n\r\nReturns the index information for all indexes in the MyDB database as well as their fragmentation\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 9 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabase -SqlInstance sql2017 -Database MyDB | Get-DbaHelpIndex\r\n\r\nReturns the index information for all indexes in the MyDB database\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaLastBackup",
        "Description":  "Retrieves and compares the date/time for the last known backups, as well as the creation date/time for the database.\n\nDefault output includes columns Server, Database, RecoveryModel, LastFullBackup, LastDiffBackup, LastLogBackup, SinceFull, SinceDiff, SinceLog, Status, DatabaseCreated, DaysSinceDbCreated.",
        "Tags":  [
                     "DisasterRecovery",
                     "Backup"
                 ],
        "Author":  "Klaas Vandenberghe ( @PowerDBAKlaas )",
        "Synopsis":  "Get date/time for last known backups of databases.",
        "Name":  "Get-DbaLastBackup",
        "Links":  "https://dbatools.io/Get-DbaLastBackup",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaLastBackup -SqlInstance ServerA\\sql987\r\n\r\nReturns a custom object displaying Server, Database, RecoveryModel, LastFullBackup, LastDiffBackup, LastLogBackup, \r\nSinceFull, SinceDiff, SinceLog, Status, DatabaseCreated, DaysSinceDbCreated\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaLastBackup -SqlInstance ServerA\\sql987\r\n\r\nReturns a custom object with Server name, Database name, and the date the last time backups were performed.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaLastBackup -SqlInstance ServerA\\sql987 | Select *\r\n\r\nReturns a custom object with Server name, Database name, and the date the last time backups were performed, and also \r\nrecoverymodel and calculations on how long ago backups were taken and what the status is.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaLastBackup -SqlInstance ServerA\\sql987 | Select * | Out-Gridview\r\n\r\nReturns a gridview displaying Server, Database, RecoveryModel, LastFullBackup, LastDiffBackup, LastLogBackup, \r\nSinceFull, SinceDiff, SinceLog, Status, DatabaseCreated, DaysSinceDbCreated.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaLastGoodCheckDb",
        "Description":  "Retrieves and compares the date/time for the last known good DBCC CHECKDB, as well as the creation date/time for the database.\n\nThis function supports SQL Server 2005 and higher.\n\nPlease note that this script uses the DBCC DBINFO() WITH TABLERESULTS. DBCC DBINFO has several known weak points, such as:\n- DBCC DBINFO is an undocumented feature/command.\n- The LastKnowGood timestamp is updated when a DBCC CHECKFILEGROUP is performed.\n- The LastKnowGood timestamp is updated when a DBCC CHECKDB WITH PHYSICAL_ONLY is performed.\n- The LastKnowGood timestamp does not get updated when a database in READ_ONLY.\n\nAn empty ($null) LastGoodCheckDb result indicates that a good DBCC CHECKDB has never been performed.\n\nSQL Server 2008R2 has a \"bug\" that causes each databases to possess two dbi_dbccLastKnownGood fields, instead of the normal one.\n\nThis script will only display this function to only display the newest timestamp. If -Verbose is specified, the function will announce every time more than one dbi_dbccLastKnownGood fields is encountered.",
        "Tags":  [
                     "CHECKDB",
                     "Database"
                 ],
        "Author":  "Jakob Bindslet ([email protected])",
        "Synopsis":  "Get date/time for last known good DBCC CHECKDB",
        "Name":  "Get-DbaLastGoodCheckDb",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaLastGoodCheckDb -SqlInstance ServerA\\sql987\r\n\r\nReturns a custom object displaying Server, Database, DatabaseCreated, LastGoodCheckDb, DaysSinceDbCreated, \r\nDaysSinceLastGoodCheckDb, Status and DataPurityEnabled\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaLastGoodCheckDb -SqlInstance ServerA\\sql987 -SqlCredential (Get-Credential sqladmin) | Format-Table \r\n-AutoSize\r\n\r\nReturns a formatted table displaying Server, Database, DatabaseCreated, LastGoodCheckDb, DaysSinceDbCreated, \r\nDaysSinceLastGoodCheckDb, Status and DataPurityEnabled. Authenticates using SQL Server authentication.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaLinkedServer",
        "Description":  "Retrieves information about each linked server on the instance",
        "Tags":  [
                     "LinkedServer",
                     "Linked"
                 ],
        "Author":  "Stephen Bennett ( https://sqlnotesfromtheunderground.wordpress.com/ )",
        "Synopsis":  "Gets all linked servers and summary of information from the sql servers listed",
        "Name":  "Get-DbaLinkedServer",
        "Links":  "https://dbatools.io/Get-DbaLinkedServer",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaLinkedServer -SqlInstance DEV01\r\n\r\nReturns all Linked Servers for the SQL Server instance DEV01\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaLocaleSetting",
        "Description":  "Gets the Locale settings on one or more computers.\n\nRequires Local Admin rights on destination computer(s).",
        "Tags":  "OS",
        "Author":  "Klaas Vandenberghe ( @PowerDBAKlaas )",
        "Synopsis":  "Gets the Locale settings on a computer.",
        "Name":  "Get-DbaLocaleSetting",
        "Links":  "https://dbatools.io/Get-DbaLocaleSetting",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaLocaleSetting -ComputerName sqlserver2014a\r\n\r\nGets the Locale settings on computer sqlserver2014a.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e\u0027sql1\u0027,\u0027sql2\u0027,\u0027sql3\u0027 | Get-DbaLocaleSetting\r\n\r\nGets the Locale settings on computers sql1, sql2 and sql3.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaLocaleSetting -ComputerName sql1,sql2 | Out-Gridview\r\n\r\nGets the Locale settings on computers sql1 and sql2, and shows them in a grid view.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaLogin",
        "Description":  "The Get-DbaLogin function returns an SMO Login object for the logins passed, if there are no users passed it will return all logins.",
        "Tags":  [
                     "Login",
                     "Security"
                 ],
        "Author":  "Mitchell Hamann (@SirCaptainMitch)",
        "Synopsis":  "Function to get an SMO login object of the logins for a given SQL Instance. Takes a server object from the pipe",
        "Name":  "Get-DbaLogin",
        "Links":  "https://dbatools.io/Get-DbaLogin",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaLogin -SqlInstance sql2016\r\n\r\nGets all the logins from server sql2016 using NT authentication and returns the SMO login objects\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaLogin -SqlInstance sql2016 -SqlCredential $sqlcred\r\n\r\nGets all the logins for a given SQL Server using a passed credential object and returns the SMO login objects\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaLogin -SqlInstance sql2016 -SqlCredential $sqlcred -Login dbatoolsuser,TheCaptain\r\n\r\nGet specific logins from server sql2016 returned as SMO login objects.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaLogin -SqlInstance sql2016 -IncludeFilter \u0027##*\u0027,\u0027NT *\u0027\r\n\r\nGet all user objects from server sql2016 beginning with \u0027##\u0027 or \u0027NT \u0027, returned as SMO login objects.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaLogin -SqlInstance sql2016 -ExcludeLogin dbatoolsuser\r\n\r\nGet all user objects from server sql2016 except the login dbatoolsuser, returned as SMO login objects.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaLogin -SqlInstance sql2016 -WindowsLogins\r\n\r\nGet all user objects from server sql2016 that are Windows Logins\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 7 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaLogin -SqlInstance sql2016 -WindowsLogins -IncludeFilter *Rob*\r\n\r\nGet all user objects from server sql2016 that are Windows Logins and have Rob in the name\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 8 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaLogin -SqlInstance sql2016 -SQLLogins\r\n\r\nGet all user objects from server sql2016 that are SQLLogins\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 9 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaLogin -SqlInstance sql2016 -SQLLogins -IncludeFilter *Rob*\r\n\r\nGet all user objects from server sql2016 that are SQLLogins  and have Rob in the name\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 10 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaLogin -SqlInstance sql2016 -NoSystem\r\n\r\nGet all user objects from server sql2016 that are not system objects\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 11 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaLogin -SqlInstance sql2016 -ExcludeFilter \u0027##*\u0027,\u0027NT *\u0027\r\n\r\nGet all user objects from server sql2016 except any beginning with \u0027##\u0027 or \u0027NT \u0027, returned as SMO login objects.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 12 --------------------------\r\n\r\nPS C:\\\u003e\u0027sql2016\u0027, \u0027sql2014\u0027 | Get-DbaLogin -SqlCredential $sqlcred\r\n\r\nUsing Get-DbaLogin on the pipeline, you can also specify which names you would like with -Login.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 13 --------------------------\r\n\r\nPS C:\\\u003e\u0027sql2016\u0027, \u0027sql2014\u0027 | Get-DbaLogin -SqlCredential $sqlcred -Locked\r\n\r\nUsing Get-DbaLogin on the pipeline to get all locked logins on servers sql2016 and sql2014.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 14 --------------------------\r\n\r\nPS C:\\\u003e\u0027sql2016\u0027, \u0027sql2014\u0027 | Get-DbaLogin -SqlCredential $sqlcred -HasAccess -Disabled\r\n\r\nUsing Get-DbaLogin on the pipeline to get all Disabled logins that have access on servers sql2016 or sql2014.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaLogShippingError",
        "Description":  "When your log shipping fails it\u0027s sometimes hard to see why is fails.\nUsing this function you\u0027ll be able to find out what went wrong in a short amount of time.",
        "Tags":  "LogShipping",
        "Author":  "Sander Stad (@sqlstad, sqlstad.nl)",
        "Synopsis":  "Get-DbaLogShippingError returns all the log shipping errors that occurred",
        "Name":  "Get-DbaLogShippingError",
        "Links":  "https://dbatools.io/Get-DbaLogShippingError",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaLogShippingError -SqlInstance sql1\r\n\r\nGet all the log shipping errors that occurred\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaLogShippingError -SqlInstance sql1 -Action Backup\r\n\r\nGet the errors that have something to do with the backup of the databases\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaLogShippingError -SqlInstance sql1 -Secondary\r\n\r\nGet the errors that occurred on the secondary instance.\r\nThis will return the copy of the restore actions because those only occur on the secondary instance\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaLogShippingError -SqlInstance sql1 -DateTimeFrom \"01/05/2018\"\r\n\r\nGet the errors that have occurred from \"01/05/2018\". This can also be of format \"yyyy-MM-dd\"\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaLogShippingError -SqlInstance sql1 -Secondary -DateTimeFrom \"01/05/2018\" -DateTimeTo \"2018-01-07\"\r\n\r\nGet the errors that have occurred between \"01/05/2018\" and \"01/07/2018\".\r\nSee that is doesn\u0027t matter how the date is represented.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaMaintenanceSolutionLog",
        "Description":  "Ola wrote a .sql script to get the content from the commandLog table. However, if LogToTable=\u0027N\u0027, there will be no logging in that table. This function reads the text files that are written in the SQL Instance\u0027s Log directory.",
        "Tags":  [
                     "Ola",
                     "Maintenance"
                 ],
        "Author":  "Klaas Vandenberghe ( @powerdbaklaas )",
        "Synopsis":  "Reads the log files generated by the IndexOptimize Agent Job from Ola Hallengren\u0027s MaintenanceSolution.",
        "Name":  "Get-DbaMaintenanceSolutionLog",
        "Links":  "https://dbatools.io/Get-DbaMaintenanceSolutionLog",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaMaintenanceSolutionLog -SqlInstance sqlserver2014a\r\n\r\nGets the outcome of the IndexOptimize job on sql instance sqlserver2014a.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaMaintenanceSolutionLog -SqlInstance sqlserver2014a -SqlCredential $credential\r\n\r\nGets the outcome of the IndexOptimize job on sqlserver2014a, using SQL Authentication.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003e\u0027sqlserver2014a\u0027, \u0027sqlserver2020test\u0027 | Get-DbaMaintenanceSolutionLog\r\n\r\nGets the outcome of the IndexOptimize job on sqlserver2014a and sqlserver2020test.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaMaintenanceSolutionLog -SqlInstance sqlserver2014a -Path \u0027D:\\logs\\maintenancesolution\\\u0027\r\n\r\nGets the outcome of the IndexOptimize job on sqlserver2014a, reading the log files in their custom location.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaMaintenanceSolutionLog -SqlInstance sqlserver2014a -Since \u00272017-07-18\u0027\r\n\r\nGets the outcome of the IndexOptimize job on sqlserver2014a, starting from july 18, 2017.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaMaintenanceSolutionLog -SqlInstance sqlserver2014a -LogType IndexOptimize\r\n\r\nGets the outcome of the IndexOptimize job on sqlserver2014a, the other options are not yet available! sorry\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaMaxMemory",
        "Description":  "This command retrieves the SQL Server \u0027Max Server Memory\u0027 configuration setting as well as the total  physical installed on the server.",
        "Tags":  [
                     "MaxMemory",
                     "Memory"
                 ],
        "Synopsis":  "Gets the \u0027Max Server Memory\u0027 configuration setting and the memory of the server.  Works on SQL Server 2000-2014.",
        "Name":  "Get-DbaMaxMemory",
        "Links":  "https://dbatools.io/Get-DbaMaxMemory",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaMaxMemory -SqlInstance sqlcluster,sqlserver2012\r\n\r\nGet memory settings for all servers within the SQL Server Central Management Server \"sqlcluster\".\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaMaxMemory -SqlInstance sqlcluster | Where-Object { $_.SqlMaxMB -gt $_.TotalMB }\r\n\r\nFind all servers in Server Central Management Server that have \u0027Max Server Memory\u0027 set to higher than the total memory \r\nof the server (think 2147483647)\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaMemoryUsage",
        "Description":  "Retrieves the amount of memory per performance counter. Default output includes columns Server, counter instance, counter, number of pages, memory in KB, memory in MB\nSSAS and SSIS are included.\n\nSSRS does not have memory counters, only memory shrinks and memory pressure state.\n\nThis function requires local admin role on the targeted computers.",
        "Tags":  "Memory",
        "Author":  "Klaas Vandenberghe ( @PowerDBAKlaas )",
        "Synopsis":  "Get amount of memory in use by *all* SQL Server components and instances",
        "Name":  "Get-DbaMemoryUsage",
        "Links":  "https://dbatools.io/Get-DbaMemoryUsage",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaMemoryUsage -ComputerName ServerA\r\n\r\nReturns a custom object displaying Server, counter instance, counter, number of pages, memory in KB, memory in MB\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaMemoryUsage -ComputerName ServerA\\sql987 -Simple\r\n\r\nReturns a custom object with Server, counter instance, counter, number of pages, memory in KB, memory in MB\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaMemoryUsage -ComputerName ServerA\\sql987 | Out-Gridview\r\n\r\nReturns a gridview displaying Server, counter instance, counter, number of pages, memory in KB, memory in MB\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaMsdtc",
        "Description":  "Returns a custom object with Computer name, state of the MSDTC Service, security settings of MSDTC and CID\u0027s\n\nRequires: Windows administrator access on Servers",
        "Tags":  [
                     "Msdtc",
                     "dtc"
                 ],
        "Author":  "Klaas Vandenberghe ( powerdbaklaas )",
        "Synopsis":  "Displays information about the Distributed Transaction Coordinator (MSDTC) on a server",
        "Name":  "Get-DbaMsdtc",
        "Links":  "https://dbatools.io/Get-DbaMsdtc",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaMsdtc -ComputerName srv0042\r\n\r\nGet DTC status for the server srv0042\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e$Computers = (Get-Content D:\\configfiles\\SQL\\MySQLInstances.txt | % {$_.split(\u0027\\\u0027)[0]})\r\n\r\n$Computers | Get-DbaMsdtc\r\n\r\nGet DTC status for all the computers in a .txt file\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaMsdtc -Computername $Computers | where { $_.dtcservicestate -ne \u0027running\u0027 }\r\n\r\nGet DTC status for all the computers where the MSDTC Service is not running\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaMsdtc -ComputerName srv0042 | Out-Gridview\r\n\r\nGet DTC status for the computer srv0042 and show in a grid view\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaNetworkActivity",
        "Description":  "Gets the Current traffic on every Network Interface on a computer.\nSee https://msdn.microsoft.com/en-us/library/aa394293(v=vs.85).aspx\n\nRequires Local Admin rights on destination computer(s).",
        "Tags":  "Network",
        "Author":  "Klaas Vandenberghe ( @PowerDBAKlaas )",
        "Synopsis":  "Gets the Current traffic on every Network Interface on a computer.",
        "Name":  "Get-DbaNetworkActivity",
        "Links":  "https://dbatools.io/Get-DbaNetworkActivity",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaNetworkActivity -ComputerName sqlserver2014a\r\n\r\nGets the Current traffic on every Network Interface on computer sqlserver2014a.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e\u0027sql1\u0027,\u0027sql2\u0027,\u0027sql3\u0027 | Get-DbaNetworkActivity\r\n\r\nGets the Current traffic on every Network Interface on computers sql1, sql2 and sql3.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaNetworkActivity -ComputerName sql1,sql2 | Out-Gridview\r\n\r\nGets the Current traffic on every Network Interface on computers sql1 and sql2, and shows them in a grid view.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaNetworkCertificate",
        "Description":  "Gets computer certificates on localhost that are candidates for using with SQL Server\u0027s network encryption",
        "Tags":  "Certificate",
        "Synopsis":  "Simplifies finding computer certificates that are candidates for using with SQL Server\u0027s network encryption",
        "Name":  "Get-DbaNetworkCertificate",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaNetworkCertificate\r\n\r\nGets computer certificates on localhost that are candidates for using with SQL Server\u0027s network encryption\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaNetworkCertificate -ComputerName sql2016\r\n\r\nGets computer certificates on sql2016 that are being used for SQL Server network encryption\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaOpenTransaction",
        "Description":  "This command is based on open transaction script published by Paul Randal.\nReference: https://www.sqlskills.com/blogs/paul/script-open-transactions-with-text-and-plans/",
        "Tags":  [
                     "Database",
                     "Process",
                     "Session",
                     "ActivityMonitor"
                 ],
        "Synopsis":  "Displays all open transactions.",
        "Name":  "Get-DbaOpenTransaction",
        "Links":  "https://dbatools.io/Get-DbaOpenTransaction",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaOpenTransaction -SqlInstance sqlserver2014a\r\n\r\nReturns open transactions for sqlserver2014a\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaOpenTransaction -SqlInstance sqlserver2014a -SqlCredential (Get-Credential sqladmin)\r\n\r\nLogs into sqlserver2014a using the login \"sqladmin\"\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaOperatingSystem",
        "Description":  "Gets operating system information from the server and returns as an object.",
        "Tags":  [
                     "ServerInfo",
                     "OperatingSystem"
                 ],
        "Author":  "Shawn Melton (@wsmelton | http://blog.wsmelton.info)",
        "Synopsis":  "Gets operating system information from the server.",
        "Name":  "Get-DbaOperatingSystem",
        "Links":  "https://dbatools.io/Get-DbaOperatingSystem",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaOperatingSystem\r\n\r\nReturns information about the local computer\u0027s operating system\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaOperatingSystem -ComputerName sql2016\r\n\r\nReturns information about the sql2016\u0027s operating system\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaOrphanUser",
        "Description":  "An orphan user is defined by a user that does not have their matching login. (Login property = \"\").",
        "Tags":  [
                     "Orphan",
                     "Database",
                     "User",
                     "Security",
                     "Login"
                 ],
        "Author":  "Claudio Silva (@ClaudioESSilva)",
        "Synopsis":  "Get orphaned users.",
        "Name":  "Get-DbaOrphanUser",
        "Links":  "https://dbatools.io/Get-DbaOrphanUser",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaOrphanUser -SqlInstance localhost\\sql2016\r\n\r\nFinds all orphan users without matching Logins in all databases present on server \u0027localhost\\sql2016\u0027.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaOrphanUser -SqlInstance localhost\\sql2016 -SqlCredential $cred\r\n\r\nFinds all orphan users without matching Logins in all databases present on server \u0027localhost\\sql2016\u0027. SQL Server \r\nauthentication will be used in connecting to the server.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaOrphanUser -SqlInstance localhost\\sql2016 -Database db1\r\n\r\nFinds orphan users without matching Logins in the db1 database present on server \u0027localhost\\sql2016\u0027.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaPageFileSetting",
        "Description":  "This command uses CIM (or other, related computer management tools) to detect the pagefile configuration of the target compuer(s).\nNote that this may require local administrator privileges for the relevant computers.",
        "Tags":  "CIM",
        "Author":  "Klaas Vandenberghe ( @PowerDBAKlaas )",
        "Synopsis":  "Returns information on the pagefile configuration of the target computer.",
        "Name":  "Get-DbaPageFileSetting",
        "Links":  "https://dbatools.io/Get-DbaPageFileSetting",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPageFileSetting -ComputerName ServerA,ServerB\r\n\r\nReturns a custom object displaying ComputerName, AutoPageFile, FileName, Status, LastModified, LastAccessed, \r\nAllocatedBaseSize, InitialSize, MaximumSize, PeakUsage, CurrentUsage  for ServerA and ServerB\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e\u0027ServerA\u0027 | Get-DbaPageFileSetting\r\n\r\nReturns a custom object displaying ComputerName, AutoPageFile, FileName, Status, LastModified, LastAccessed, \r\nAllocatedBaseSize, InitialSize, MaximumSize, PeakUsage, CurrentUsage  for ServerA\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaPermission",
        "Description":  "Retrieves a list of permissions\n\nPermissions link principals to securables.\nPrincipals exist on Windows, Instance and Database level.\nSecurables exist on Instance and Database level.\nA permission state can be GRANT, DENY or REVOKE.\nThe permission type can be SELECT, CONNECT, EXECUTE and more.\n\nSee https://msdn.microsoft.com/en-us/library/ms191291.aspx for more information",
        "Tags":  [
                     "Permissions",
                     "Databases"
                 ],
        "Author":  "Klaas Vandenberghe ( @PowerDBAKlaas )",
        "Synopsis":  "Get a list of Server and Database level permissions",
        "Name":  "Get-DbaPermission",
        "Links":  "https://dbatools.io/Get-DbaPermission",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPermission -SqlInstance ServerA\\sql987\r\n\r\nReturns a custom object with Server name, Database name, permission state, permission type, grantee and securable.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPermission -SqlInstance ServerA\\sql987 | Format-Table -AutoSize\r\n\r\nReturns a formatted table displaying Server, Database, permission state, permission type, grantee, granteetype, \r\nsecurable and securabletype.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPermission -SqlInstance ServerA\\sql987 -NoSystemObjects -IncludeServerLevel\r\n\r\nReturns a custom object with Server name, Database name, permission state, permission type, grantee and securable\r\nin all databases and on the server level, but not on system securables.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPermission -SqlInstance sql2016 -Database master\r\n\r\nReturns a custom object with permissions for the master database.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaPfAvailableCounter",
        "Description":  "Gathers list of all available counters on local or remote machines. Note, if you pass a credential object, it will be included in the output for easy reuse in your next piped command.\n\nThanks to Daniel Streefkerk for this super fast way of counters\nhttps://daniel.streefkerkonline.com/2016/02/18/use-powershell-to-list-all-windows-performance-counters-and-their-numeric-ids",
        "Tags":  [
                     "Performance",
                     "DataCollector",
                     "PerfCounter"
                 ],
        "Synopsis":  "Gathers list of all available counters on local or remote machines.",
        "Name":  "Get-DbaPfAvailableCounter",
        "Links":  "https://dbatools.io/Get-DbaPfAvailableCounter",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfAvailableCounter\r\n\r\nGets all available counters on the local machine.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfAvailableCounter -Pattern *sql*\r\n\r\nGets all counters matching sql on the local machine.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfAvailableCounter -ComputerName sql2017 -Pattern *sql*\r\n\r\nGets all counters matching sql on the remote server sql2017.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfAvailableCounter -Pattern *sql*\r\n\r\nGets all counters matching sql on the local machine.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfAvailableCounter -Pattern *sql* | Add-DbaPfDataCollectorCounter -CollectorSet \u0027Test Collector Set\u0027 \r\n-Collector DataCollector01\r\n\r\nAdds all counters matching \"sql\" to the DataCollector01 within the \u0027Test Collector Set\u0027 CollectorSet.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaPfDataCollector",
        "Description":  "Gets Performance Monitor Data Collectors.",
        "Tags":  "PerfMon",
        "Synopsis":  "Gets Performance Monitor Data Collectors.",
        "Name":  "Get-DbaPfDataCollector",
        "Links":  "https://dbatools.io/Get-DbaPfDataCollector",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfDataCollector\r\n\r\nGets all Collectors on localhost.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfDataCollector -ComputerName sql2017\r\n\r\nGets all Collectors on sql2017.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfDataCollector -ComputerName sql2017, sql2016 -Credential (Get-Credential) -CollectorSet \u0027System \r\nCorrelation\u0027\r\n\r\nGets all Collectors for the \u0027System Correlation\u0027 CollectorSet on sql2017 and sql2016 using alternative credentials.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfDataCollectorSet -CollectorSet \u0027System Correlation\u0027 | Get-DbaPfDataCollector\r\n\r\nGets all Collectors for the \u0027System Correlation\u0027 CollectorSet.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaPfDataCollectorCounter",
        "Description":  "Gets Performance Counters.",
        "Tags":  "PerfMon",
        "Synopsis":  "Gets Performance Counters.",
        "Name":  "Get-DbaPfDataCollectorCounter",
        "Links":  "https://dbatools.io/Get-DbaPfDataCollectorCounter",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfDataCollectorCounter\r\n\r\nGets all counters for all Collector Sets on localhost.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfDataCollectorCounter -ComputerName sql2017\r\n\r\nGets all counters for all Collector Sets on  on sql2017.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfDataCollectorCounter -ComputerName sql2017 -Counter \u0027\\Processor(_Total)\\% Processor Time\u0027\r\n\r\nGets the \u0027\\Processor(_Total)\\% Processor Time\u0027 counter on sql2017.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfDataCollectorCounter -ComputerName sql2017, sql2016 -Credential (Get-Credential) -CollectorSet \u0027System \r\nCorrelation\u0027\r\n\r\nGets all counters for the \u0027System Correlation\u0027 CollectorSet on sql2017 and sql2016 using alternative credentials.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfDataCollectorSet -CollectorSet \u0027System Correlation\u0027 | Get-DbaPfDataCollector | \r\nGet-DbaPfDataCollectorCounter\r\n\r\nGets all counters for the \u0027System Correlation\u0027 CollectorSet.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaPfDataCollectorCounterSample",
        "Description":  "Gets Performance Counter Samples.",
        "Tags":  "PerfMon",
        "Synopsis":  "Gets Performance Counter Samples.",
        "Name":  "Get-DbaPfDataCollectorCounterSample",
        "Links":  "https://dbatools.io/Get-DbaPfDataCollectorCounterSample",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfDataCollectorCounterSample\r\n\r\nGets a single sample for all counters for all Collector Sets on localhost.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfDataCollectorCounterSample -Counter \u0027\\Processor(_Total)\\% Processor Time\u0027\r\n\r\nGets a single sample for all counters for all Collector Sets on localhost.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfDataCollectorCounter -ComputerName sql2017, sql2016 | Out-GridView -PassThru | \r\nGet-DbaPfDataCollectorCounterSample -MaxSamples 10\r\n\r\nGets 10 samples for all counters for all Collector Sets for servers sql2016 and sql2017.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfDataCollectorCounterSample -ComputerName sql2017\r\n\r\nGets a single sample for all counters for all Collector Sets on sql2017.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfDataCollectorCounterSample -ComputerName sql2017, sql2016 -Credential (Get-Credential) -CollectorSet \r\n\u0027System Correlation\u0027\r\n\r\nGets a single sample for all counters for the \u0027System Correlation\u0027 CollectorSet on sql2017 and sql2016 using \r\nalternative credentials.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfDataCollectorCounterSample -CollectorSet \u0027System Correlation\u0027\r\n\r\nGets a single sample for all counters for the \u0027System Correlation\u0027 CollectorSet.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaPfDataCollectorSet",
        "Description":  "Gets Performance Monitor Data Collector Set.",
        "Tags":  "PerfMon",
        "Synopsis":  "Gets Performance Monitor Data Collector Set.",
        "Name":  "Get-DbaPfDataCollectorSet",
        "Links":  "https://dbatools.io/Get-DbaPfDataCollectorSet",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfDataCollectorSet\r\n\r\nGets all Collector Sets on localhost.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfDataCollectorSet -ComputerName sql2017\r\n\r\nGets all Collector Sets on sql2017.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfDataCollectorSet -ComputerName sql2017 -Credential (Get-Credential) -CollectorSet \u0027System Correlation\u0027\r\n\r\nGets the \u0027System Correlation\u0027 CollectorSet on sql2017 using alternative credentials.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfDataCollectorSet | Select *\r\n\r\nDisplays extra columns and also exposes the original COM object in DataCollectorSetObject.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaPfDataCollectorSetTemplate",
        "Description":  "Parses Perf Monitor XML templates. Defaults to parsing templates in the dbatools template repository (\\bin\\perfmontemplates\\).",
        "Tags":  [
                     "Performance",
                     "DataCollector",
                     "PerfCounter"
                 ],
        "Synopsis":  "Parses Perf Monitor templates. Defaults to parsing templates in the dbatools template repository (\\bin\\perfmontemplates\\).",
        "Name":  "Get-DbaPfDataCollectorSetTemplate",
        "Links":  "https://dbatools.io/Get-DbaPfDataCollectorSetTemplate",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfDataCollectorSetTemplate\r\n\r\nReturns information about all the templates in the local dbatools repository.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfDataCollectorSetTemplate | Out-GridView -PassThru | Import-DbaPfDataCollectorSetTemplate -ComputerName \r\nsql2017 | Start-DbaPfDataCollectorSet\r\n\r\nAllows you to select a template, then deploys it to sql2017 and immediately starts the DataCollectorSet.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfDataCollectorSetTemplate | Select-Object *\r\n\r\nReturns more information about the template, including the full path/filename.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaPlanCache",
        "Description":  "Checks ahoc and prepared plan cache for each database, if over 100 MBS you should consider you using Remove-DbaQueryPlan to clear the plan caches or turning on optimize for adhoc workloads configuration is running 2008 or later.\n\nReferences: https://www.sqlskills.com/blogs/kimberly/plan-cache-adhoc-workloads-and-clearing-the-single-use-plan-cache-bloat/\n\nNote: This command returns results from all SQL server instances on the destination server but the process column is specific to -SqlInstance passed.",
        "Tags":  "Memory",
        "Author":  "Tracy Boggiano, databasesuperhero.com",
        "Synopsis":  "Provides information about adhoc and prepared plan cache usage",
        "Name":  "Get-DbaPlanCache",
        "Links":  "https://dbatools.io/Get-DbaPlanCache",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPlanCache -SqlInstance sql2017\r\n\r\nReturns the single use plan cashe usage information for SQL Server instance 2017\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPlanCache -SqlInstance sql2017\r\n\r\nReturns the single use plan cashe usage information for SQL Server instance 2017\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPlanCache -SqlInstance sql2017 -SqlCredential (Get-Credential sqladmin)\r\n\r\nReturns the single use plan cashe usage information for SQL Server instance 2017 using login \u0027sqladmin\u0027\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaPolicy",
        "Description":  "Returns details of policies with the option to filter on Category and SystemObjects.",
        "Tags":  [
                     "Policy",
                     "PoilcyBasedManagement"
                 ],
        "Author":  "Stephen Bennett (https://sqlnotesfromtheunderground.wordpress.com/)",
        "Synopsis":  "Returns polices from policy based management from an instance.",
        "Name":  "Get-DbaPolicy",
        "Links":  "https://dbatools.io/Get-DbaPolicy",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPolicy -SqlInstance sql2016\r\n\r\nReturns all policies from sql2016 server\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPolicy -SqlInstance sql2016 -SqlCredential $cred\r\n\r\nUses a credential $cred to connect and return all policies from sql2016 instance\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPolicy -SqlInstance sql2016 -Category MorningCheck\r\n\r\nReturns all policies from sql2016 server that part of the PolicyCategory MorningCheck\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaPrivilege",
        "Description":  "Gets the users with local privileges \u0027Lock Pages in Memory\u0027, \u0027Instant File Initialization\u0027, \u0027Logon as Batch\u0027 on one or more computers.\n\nRequires Local Admin rights on destination computer(s).",
        "Tags":  "Privilege",
        "Author":  "Klaas Vandenberghe ( @PowerDBAKlaas )",
        "Synopsis":  "Gets the users with local privileges on one or more computers.",
        "Name":  "Get-DbaPrivilege",
        "Links":  "https://dbatools.io/Get-DbaPrivilege",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPrivilege -ComputerName sqlserver2014a\r\n\r\nGets the local privileges on computer sqlserver2014a.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e\u0027sql1\u0027,\u0027sql2\u0027,\u0027sql3\u0027 | Get-DbaPrivilege\r\n\r\nGets the local privileges on computers sql1, sql2 and sql3.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPrivilege -ComputerName sql1,sql2 | Out-Gridview\r\n\r\nGets the local privileges on computers sql1 and sql2, and shows them in a grid view.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaProcess",
        "Description":  "This command displays processes associated with a spid, login, host, program or database.\n\nThanks to Michael J Swart at https://sqlperformance.com/2017/07/sql-performance/find-database-connection-leaks for the query to get the last executed SQL statement, minutesasleep and host process ID.",
        "Tags":  [
                     "Process",
                     "Session",
                     "ActivityMonitor"
                 ],
        "Synopsis":  "This command displays SQL Server processes.",
        "Name":  "Get-DbaProcess",
        "Links":  "https://dbatools.io/Get-DbaProcess",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaProcess -SqlInstance sqlserver2014a -Login base\\ctrlb, sa\r\n\r\nShows information about the processes for base\\ctrlb and sa on sqlserver2014a. Windows Authentication is used in \r\nconnecting to sqlserver2014a.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaProcess -SqlInstance sqlserver2014a -SqlCredential $credential -Spid 56, 77\r\n\r\nShows information about the processes for spid 56 and 57. Uses alternative (SQL or Windows) credentials to authenticate \r\nto sqlserver2014a.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaProcess -SqlInstance sqlserver2014a -Program \u0027Microsoft SQL Server Management Studio\u0027\r\n\r\nShows information about the processes that were created in Microsoft SQL Server Management Studio.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaProcess -SqlInstance sqlserver2014a -Host workstationx, server100\r\n\r\nShows information about the processes that were initiated by hosts (computers/clients) workstationx and server 1000.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaQueryExecutionTime",
        "Description":  "Quickly find slow query executions within a database.  Results will include stored procedures and individual SQL statements.",
        "Tags":  [
                     "Query",
                     "Performance"
                 ],
        "Author":  "Brandon Abshire, netnerds.net",
        "Synopsis":  "Displays Stored Procedures and Ad hoc queries with the highest execution times.  Works on SQL Server 2008 and above.",
        "Name":  "Get-DbaQueryExecutionTime",
        "Links":  "https://dbatools.io/Get-DbaQueryExecutionTime",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaQueryExecutionTime -SqlInstance sql2008, sqlserver2012\r\n\r\nReturn the top 100 slowest stored procedures or statements for servers sql2008 and sqlserver2012.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaQueryExecutionTime -SqlInstance sql2008 -Database TestDB\r\n\r\nReturn the top 100 slowest stored procedures or statements on server sql2008 for only the TestDB database.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaQueryExecutionTime -SqlInstance sql2008 -Database TestDB -MaxResultsPerDb 100 -MinExecs 200 -MinExecMs \r\n1000\r\n\r\nReturn the top 100 slowest stored procedures or statements on server sql2008 for only the TestDB database,\r\nlimiting results to queries with more than 200 total executions and an execution time over 1000ms or higher.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaRegisteredServer",
        "Description":  "Returns an array of servers found in the CMS.",
        "Tags":  [
                     "RegisteredServer",
                     "CMS"
                 ],
        "Author":  "Bryan Hamby (@galador)",
        "Synopsis":  "Gets list of SQL Server objects stored in SQL Server Central Management Server (CMS).",
        "Name":  "Get-DbaRegisteredServer",
        "Links":  "https://dbatools.io/Get-DbaRegisteredServer",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRegisteredServer -SqlInstance sqlserver2014a\r\n\r\nGets a list of servers from the CMS on sqlserver2014a, using Windows Credentials.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRegisteredServer -SqlInstance sqlserver2014a -IncludeSelf\r\n\r\nGets a list of servers from the CMS on sqlserver2014a and includes sqlserver2014a in the output results.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRegisteredServer -SqlInstance sqlserver2014a -SqlCredential $credential | Select-Object -Unique \r\n-ExpandProperty ServerName\r\n\r\nReturns only the server names from the CMS on sqlserver2014a, using SQL Authentication to authenticate to the server.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRegisteredServer -SqlInstance sqlserver2014a -Group HR, Accounting\r\n\r\nGets a list of servers in the HR and Accounting groups from the CMS on sqlserver2014a.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRegisteredServer -SqlInstance sqlserver2014a -Group HR\\Development\r\n\r\nReturns a list of servers in the HR and sub-group Development from the CMS on sqlserver2014a.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaRegisteredServerGroup",
        "Description":  "Returns an array of Server Groups found in the CMS.",
        "Tags":  [
                     "RegisteredServer",
                     "CMS"
                 ],
        "Author":  "Tony Wilhelm (@tonywsql)",
        "Synopsis":  "Gets list of Server Groups objects stored in SQL Server Central Management Server (CMS).",
        "Name":  "Get-DbaRegisteredServerGroup",
        "Links":  "https://dbatools.io/Get-DbaRegisteredServerGroup",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRegisteredServerGroup -SqlInstance sqlserver2014a\r\n\r\nGets the top level groups from the CMS on sqlserver2014a, using Windows Credentials.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRegisteredServerGroup -SqlInstance sqlserver2014a -SqlCredential $credential\r\n\r\nGets the top level groups from the CMS on sqlserver2014a, using alternative credentials to authenticate to the server.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRegisteredServerGroup -SqlInstance sqlserver2014a -Group HR, Accounting\r\n\r\nGets the HR and Accounting groups from the CMS on sqlserver2014a.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRegisteredServerGroup -SqlInstance sqlserver2014a -Group HR\\Development\r\n\r\nReturns the sub-group Development of the HR group from the CMS on sqlserver2014a.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaRegisteredServerStore",
        "Description":  "Returns a SQL Server Registered Server Store object - useful for working with Central Management Store",
        "Tags":  [
                     "RegisteredServer",
                     "CMS"
                 ],
        "Synopsis":  "Returns a SQL Server Registered Server Store Object",
        "Name":  "Get-DbaRegisteredServerStore",
        "Links":  "https://dbatools.io/Get-DbaRegisteredServerStore",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRegisteredServerStore -SqlInstance sqlserver2014a\r\n\r\nReturns a SQL Server Registered Server Store Object from sqlserver2014a\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRegisteredServerStore -SqlInstance sqlserver2014a -SqlCredential (Get-Credential sqladmin)\r\n\r\nReturns a SQL Server Registered Server Store Object from sqlserver2014a  by logging in with the sqladmin login\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaResourceGovernorClassifierFunction",
        "Description":  "Gets the Resource Governor custom classifier Function which is used for customize the workload groups usage",
        "Tags":  [
                     "Migration",
                     "ResourceGovernor"
                 ],
        "Author":  "Alessandro Alpi (@suxstellino), alessandroalpi.blog",
        "Synopsis":  "Gets the Resource Governor custom classifier Function",
        "Name":  "Get-DbaResourceGovernorClassifierFunction",
        "Links":  "https://dbatools.io/Get-DbaResourceGovernorClassifierFunction",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaResourceGovernorClassifierFunction -SqlInstance sql2016\r\n\r\nGets the classifier function object of the SqlInstance sql2016\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e\u0027Sql1\u0027,\u0027Sql2/sqlexpress\u0027 | Get-DbaResourceGovernorClassifierFunction\r\n\r\nGets the classifier function object on Sql1 and Sql2/sqlexpress instances\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaRestoreHistory",
        "Description":  "By default, this command will return the server name, database, username, restore type, date, from file and to files.\n\nThanks to https://www.mssqltips.com/SqlInstancetip/1724/when-was-the-last-time-your-sql-server-database-was-restored/ for the query and https://sqlstudies.com/2016/07/27/when-was-this-database-restored/ for the idea.",
        "Tags":  [
                     "DisasterRecovery",
                     "Backup",
                     "Restore"
                 ],
        "Synopsis":  "Returns restore history details for databases on a SQL Server.",
        "Name":  "Get-DbaRestoreHistory",
        "Links":  "https://dbatools.io/Get-DbaRestoreHistory",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRestoreHistory -SqlInstance sql2016\r\n\r\nReturns server name, database, username, restore type, date for all restored databases on sql2016.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRestoreHistory -SqlInstance sql2016 -Database db1, db2 -Since \u00277/1/2016 10:47:00\u0027\r\n\r\nReturns restore information only for databases db1 and db2 on sql2016 since July 1, 2016 at 10:47 AM.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRestoreHistory -SqlInstance sql2014, sql2016 -Exclude db1\r\n\r\nLots of detailed information for all databases except db1 on sql2014 and sql2016.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRestoreHistory -SqlInstance sql2014 -Database AdventureWorks2014, pubs | Format-Table\r\n\r\nAdds From and To file information to output, returns information only for AdventureWorks2014 and pubs, and formats the \r\ndata as a table.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRegisteredServer -SqlInstance sql2016 | Get-DbaRestoreHistory\r\n\r\nReturns database restore information for every database on every server listed in the Central Management Server on \r\nsql2016.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaRoleMember",
        "Description":  "Get members of all roles on a Sql instance.\n\nDefault output includes columns SQLServer, Database, Role, Member.",
        "Tags":  [
                     "Role",
                     "Database",
                     "Security",
                     "Login"
                 ],
        "Author":  "Klaas Vandenberghe ( @PowerDBAKlaas )",
        "Synopsis":  "Get members of all roles on a Sql instance.",
        "Name":  "Get-DbaRoleMember",
        "Links":  "https://dbatools.io/Get-DbaRoleMember",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRoleMember -SqlInstance ServerA\r\n\r\nReturns a custom object displaying SQLServer, Database, Role, Member for all DatabaseRoles.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRoleMember -SqlInstance sql2016 | Out-Gridview\r\n\r\nReturns a gridview displaying SQLServer, Database, Role, Member for all DatabaseRoles.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRoleMember -SqlInstance ServerA\\sql987 -IncludeServerLevel\r\n\r\nReturns a gridview displaying SQLServer, Database, Role, Member for both ServerRoles and DatabaseRoles.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaRunningJob",
        "Description":  "This function returns agent jobs that active on the SQL Server instance when calling the command. The information is gathered the SMO JobServer.jobs and be returned either in detailed or standard format.",
        "Tags":  [
                     "Process",
                     "Session",
                     "ActivityMonitor",
                     "Agent",
                     "Job"
                 ],
        "Author":  "Stephen Bennett, https://sqlnotesfromtheunderground.wordpress.com/",
        "Synopsis":  "Returns all non-idle Agent jobs running on the server.",
        "Name":  "Get-DbaRunningJob",
        "Links":  "https://dbatools.io/Get-DbaRunningJob",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRunningJob -SqlInstance localhost\r\n\r\nReturns any active jobs on localhost.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRunningJob -SqlInstance localhost -Detailed\r\n\r\nReturns a detailed output of any active jobs on localhost.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003e\u0027localhost\u0027,\u0027localhost\\namedinstance\u0027 | Get-DbaRunningJob\r\n\r\nReturns all active jobs on multiple instances piped into the function.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaSchemaChangeHistory",
        "Description":  "Queries the default system trace for any DDL changes in the specified timeframe\nOnly works with SQL 2005 and later, as the system trace didn\u0027t exist before then",
        "Tags":  [
                     "Migration",
                     "Backup",
                     "Database"
                 ],
        "Author":  "Stuart Moore (@napalmgram - http://stuart-moore.com)",
        "Synopsis":  "Gets DDL changes logged in the system trace.",
        "Name":  "Get-DbaSchemaChangeHistory",
        "Links":  "https://dbatools.io/Get-DbaSchemaChangeHistory",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSchemaChangeHistory -SqlInstance localhost\r\n\r\nReturns all DDL changes made in all databases on the SQL Server instance localhost since the system trace began\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSchemaChangeHistory -SqlInstance localhost -Since (Get-Date).AddDays(-7)\r\n\r\nReturns all DDL changes made in all databases on the SQL Server instance localhost in the last 7 days\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSchemaChangeHistory -SqlInstance localhost -Database Finance, Prod -Since (Get-Date).AddDays(-7)\r\n\r\nReturns all DDL changes made in the Prod and Finance databases on the SQL Server instance localhost in the last 7 days\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSchemaChangeHistory -SqlInstance localhost -Database Finance -Object AccountsTable -Since \r\n(Get-Date).AddDays(-7)\r\n\r\nReturns all DDL changes made  to the AccountsTable object in the Finance database on the SQL Server instance localhost \r\nin the last 7 days\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaServerAudit",
        "Description":  "The Get-DbaServerAudit command gets SQL Security Audit information for each instance(s) of SQL Server.",
        "Tags":  [
                     "Audit",
                     "Security",
                     "SqlAudit"
                 ],
        "Author":  "Garry Bargsley (@gbargsley), http://blog.garrybargsley.com",
        "Synopsis":  "Gets SQL Security Audit information for each instance(s) of SQL Server.",
        "Name":  "Get-DbaServerAudit",
        "Links":  "https://dbatools.io/Get-DbaServerAudit",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaServerAudit -SqlInstance localhost\r\n\r\nReturns all Security Audits on the local default SQL Server instance\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaServerAudit -SqlInstance localhost, sql2016\r\n\r\nReturns all Security Audits for the local and sql2016 SQL Server instances\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaServerAuditSpecification",
        "Description":  "The Get-DbaServerAuditSpecification command gets SQL Security Audit Specification information for each instance(s) of SQL Server.",
        "Tags":  [
                     "Audit",
                     "Security",
                     "SqlAudit"
                 ],
        "Author":  "Garry Bargsley (@gbargsley), http://blog.garrybargsley.com",
        "Synopsis":  "Gets SQL Security Audit Specification information for each instance(s) of SQL Server.",
        "Name":  "Get-DbaServerAuditSpecification",
        "Links":  "https://dbatools.io/Get-DbaServerAuditSpecification",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaServerAuditSpecification -SqlInstance localhost\r\n\r\nReturns all Security Audit Specifications on the local default SQL Server instance\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaServerAuditSpecification -SqlInstance localhost, sql2016\r\n\r\nReturns all Security Audit Specifications for the local and sql2016 SQL Server instances\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaServerInstallDate",
        "Description":  "By default, this command returns for each SQL Instance instance passed in:\nSQL Instance install date, formatted as a string\nHosting Windows server install date, formatted as a string",
        "Tags":  "CIM",
        "Author":  "Mitchell Hamann (@SirCaptainMitch), mitchellhamann.com",
        "Synopsis":  "Returns the install date of a SQL Instance and Windows Server, depending on what is passed.",
        "Name":  "Get-DbaServerInstallDate",
        "Links":  "https://dbatools.io/Get-DbaServerInstallDate",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaServerInstallDate -SqlInstance SqlBox1\\Instance2\r\n\r\nReturns an object with SQL Instance Install date as a string and the Windows install date as string.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaServerInstallDate -SqlInstance winserver\\sqlexpress, sql2016\r\n\r\nReturns an object with SQL Instance Install date as a string and the Windows install date as a string for both \r\nSQLInstances that are passed to the cmdlet.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaServerInstallDate -SqlInstance sqlserver2014a, sql2016\r\n\r\nReturns an object with only the SQL Server Install date as a string.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaServerInstallDate -SqlInstance sqlserver2014a, sql2016 -IncludeWindows\r\n\r\nReturns an object with the Windows Install date and the SQL install date as a string.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRegisteredServer -SqlInstance sql2014 | Get-DbaServerInstallDate\r\n\r\nReturns an object with SQL Instance install date as a string for every server listed in the Central Management Server \r\non sql2014\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaServerProtocol",
        "Description":  "Gets the SQL Server related server protocols on one or more computers.\n\nRequires Local Admin rights on destination computer(s).\nThe server protocols can be enabled and disabled when retrieved via WSMan.",
        "Tags":  "Protocol",
        "Author":  "Klaas Vandenberghe ( @PowerDBAKlaas )",
        "Synopsis":  "Gets the SQL Server related server protocols on a computer.",
        "Name":  "Get-DbaServerProtocol",
        "Links":  "https://dbatools.io/Get-DbaServerProtocol",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaServerProtocol -ComputerName sqlserver2014a\r\n\r\nGets the SQL Server related server protocols on computer sqlserver2014a.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e\u0027sql1\u0027,\u0027sql2\u0027,\u0027sql3\u0027 | Get-DbaServerProtocol\r\n\r\nGets the SQL Server related server protocols on computers sql1, sql2 and sql3.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaServerProtocol -ComputerName sql1,sql2 | Out-Gridview\r\n\r\nGets the SQL Server related server protocols on computers sql1 and sql2, and shows them in a grid view.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003e(Get-DbaServerProtocol -ComputerName sql1 | Where { $_.DisplayName = \u0027via\u0027 }).Disable()\r\n\r\nDisables the VIA ServerNetworkProtocol on computer sql1.\r\nIf successful, returncode 0 is shown.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaServerRole",
        "Description":  "Gets the list of server-level roles for SQL Server instance.",
        "Tags":  [
                     "ServerRole",
                     "Security"
                 ],
        "Synopsis":  "Gets the list of server-level roles.",
        "Name":  "Get-DbaServerRole",
        "Links":  "https://dbatools.io/Get-DbaServerRole",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaServerRole -SqlInstance sql2016a\r\n\r\nOutputs list of server-level roles for sql2016a instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaServerRole -SqlInstance sql2017a -ExcludeFixedRole\r\n\r\nOutputs the server-level role(s) that are not fixed roles on sql2017a instance.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaSpConfigure",
        "Description":  "This function returns server level system configuration (sys.configuration/sp_configure) information. The information is gathered through SMO Configuration.Properties.\nThe data includes the default value for each configuration, for quick identification of values that may have been changed.",
        "Tags":  [
                     "SpConfig",
                     "Configure",
                     "Configuration"
                 ],
        "Author":  "Nic Cain, https://sirsql.net/",
        "Synopsis":  "Returns all server level system configuration (sys.configuration/sp_configure) information",
        "Name":  "Get-DbaSpConfigure",
        "Links":  "https://dbatools.io/Get-DbaSpConfigure",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSpConfigure -SqlInstance localhost\r\n\r\nReturns server level configuration data on the localhost (ServerName, Name, DisplayName, Description, IsAdvanced, \r\nIsDynamic, MinValue, MaxValue, ConfiguredValue, RunningValue, DefaultValue, IsRunningDefaultValue)\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e\u0027localhost\u0027,\u0027localhost\\namedinstance\u0027 | Get-DbaSpConfigure\r\n\r\nReturns system configuration information on multiple instances piped into the function\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSpConfigure -SqlInstance localhost\r\n\r\nReturns server level configuration data on the localhost (ServerName, Name, DisplayName, Description, IsAdvanced, \r\nIsDynamic, MinValue, MaxValue, ConfiguredValue, RunningValue, DefaultValue, IsRunningDefaultValue)\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSpConfigure -SqlInstance sql2012 -Name MaxServerMemory\r\n\r\nReturns only the system configuration for MaxServerMemory. Configs is auto-populated for tabbing convenience.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaSpn",
        "Description":  "Get a list of set SPNs. SPNs are set at the AD account level. You can either retrieve set SPNs for a computer, or any SPNs set for\na given active directory account. You can query one, or both. You\u0027ll get a list of every SPN found for either search term.",
        "Tags":  "SPN",
        "Author":  "Drew Furgiuele (@pittfurg), http://www.port1433.com",
        "Synopsis":  "Returns a list of set service principal names for a given computer/AD account",
        "Name":  "Get-DbaSpn",
        "Links":  "https://dbatools.io/Get-DbaSpn",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSpn -ServerName SQLSERVERA -Credential (Get-Credential)\r\n\r\nReturns a custom object with SearchTerm (ServerName) and the SPNs that were found\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSpn -AccountName domain\\account -Credential (Get-Credential)\r\n\r\nReturns a custom object with SearchTerm (domain account) and the SPNs that were found\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSpn -ServerName SQLSERVERA,SQLSERVERB -Credential (Get-Credential)\r\n\r\nReturns a custom object with SearchTerm (ServerName) and the SPNs that were found for multiple computers\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaSqlBuildReference",
        "Description":  "Returns info about the specific build of a SQL instance, including the SP, the CU and the reference KB, wherever possible.\nIt also includes End Of Support dates as specified on Microsoft Lifecycle Policy",
        "Tags":  "SqlBuild",
        "Author":  "niphlod",
        "Synopsis":  "Returns SQL Server Build infos on a SQL instance",
        "Name":  "Get-DbaSqlBuildReference",
        "Links":  "https://dbatools.io/Get-DbaSqlBuildReference",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSqlBuildReference -Build \"12.00.4502\"\r\n\r\nReturns information about a build identified by  \"12.00.4502\" (which is SQL 2014 with SP1 and CU11)\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSqlBuildReference -Build \"12.00.4502\" -Update\r\n\r\nReturns information about a build trying to fetch the most up to date index online. When the online version is newer, \r\nthe local one gets overwritten\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSqlBuildReference -Build \"12.0.4502\",\"10.50.4260\"\r\n\r\nReturns information builds identified by these versions strings\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRegisteredServer -SqlInstance sqlserver2014a | Get-DbaSqlBuildReference\r\n\r\nIntegrate with other commandlets to have builds checked for all your registered servers on sqlserver2014a\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaSqlFeature",
        "Description":  "Runs the SQL Server feature discovery report (setup.exe /Action=RunDiscovery)\n\nInspired by Dave Mason\u0027s (@BeginTry) post at\nhttps://itsalljustelectrons.blogspot.be/2018/04/SQL-Server-Discovery-Report.html\n\nAssumptions:\n1. The sub-folder \"Microsoft SQL Server\" exists in $env:ProgramFiles,\n    even if SQL was installed to a non-default path. This has been\n    verified on SQL 2008R2 and SQL 2012. Further verification may be needed.\n2. The discovery report displays installed components for the version of SQL\n    Server associated with setup.exe, along with installed components of all\n    lesser versions of SQL Server that are installed.",
        "Tags":  [
                     "Feature",
                     "Component"
                 ],
        "Author":  "Chrissy LeMaire (@cl)",
        "Synopsis":  "Runs the SQL Server feature discovery report (setup.exe /Action=RunDiscovery)",
        "Name":  "Get-DbaSqlFeature",
        "Links":  "https://dbatools.io/Get-DbaSqlFeature",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSqlFeature -ComputerName sql2017, sql2016, sql2005\r\n\r\nGets all SQL Server features for all instances on sql2017, sql2016 and sql2005.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSqlFeature -Verbose\r\n\r\nGets all SQL Server features for all instances on localhost. Outputs to screen if no instances are found.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSqlFeature -ComputerName sql2017 -Credential (Get-Credential ad\\sqladmin)\r\n\r\nGets all SQL Server features for all instances on sql2017 using the ad\\sqladmin credential (which has access to the \r\nWindows Server).\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaSqlInstanceProperty",
        "Description":  "The Get-DbaSqlInstanceProperty command gets SQL Server instance properties from the SMO object sqlserver.",
        "Tags":  [
                     "Instance",
                     "Configure",
                     "Configuration"
                 ],
        "Author":  "Klaas Vandenberghe (@powerdbaklaas)",
        "Synopsis":  "Gets SQL Server instance properties of one or more instance(s) of SQL Server.",
        "Name":  "Get-DbaSqlInstanceProperty",
        "Links":  "https://dbatools.io/Get-DbaSqlInstanceProperty",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSqlInstanceProperty -SqlInstance localhost\r\n\r\nReturns SQL Server instance properties on the local default SQL Server instance\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSqlInstanceProperty -SqlInstance sql2, sql4\\sqlexpress\r\n\r\nReturns SQL Server instance properties on default instance on sql2 and sqlexpress instance on sql4\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003e\u0027sql2\u0027,\u0027sql4\u0027 | Get-DbaSqlInstanceProperty\r\n\r\nReturns SQL Server instance properties on sql2 and sql4\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSqlInstanceProperty -SqlInstance sql2,sql4 -InstanceProperty DefaultFile\r\n\r\nReturns SQL Server instance property DefaultFile on instance sql2 and sql4\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSqlInstanceProperty -SqlInstance sql2,sql4 -ExcludeInstanceProperty DefaultFile\r\n\r\nReturns all SQL Server instance properties except DefaultFile on instance sql2 and sql4\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003e$cred = Get-Credential sqladmin\r\n\r\nGet-DbaSqlInstanceProperty -SqlInstance sql2 -SqlCredential $cred\r\n\r\nConnects using sqladmin credential and returns SQL Server instance properties from sql2\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaSqlInstanceUserOption",
        "Description":  "The Get-DbaSqlInstanceUserOption command gets SQL Instance user options from the SMO object sqlserver.",
        "Tags":  [
                     "Instance",
                     "Configure",
                     "UserOption"
                 ],
        "Author":  "Klaas Vandenberghe (@powerdbaklaas)",
        "Synopsis":  "Gets SQL Instance user options of one or more instance(s) of SQL Server.",
        "Name":  "Get-DbaSqlInstanceUserOption",
        "Links":  "https://dbatools.io/Get-DbaSqlInstanceUserOption",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSqlInstanceUserOption -SqlInstance localhost\r\n\r\nReturns SQL Instance user options on the local default SQL Server instance\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSqlInstanceUserOption -SqlInstance sql2, sql4\\sqlexpress\r\n\r\nReturns SQL Instance user options on default instance on sql2 and sqlexpress instance on sql4\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003e\u0027sql2\u0027,\u0027sql4\u0027 | Get-DbaSqlInstanceUserOption\r\n\r\nReturns SQL Instance user options on sql2 and sql4\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaSqlManagementObject",
        "Description":  "The Get-DbaSqlManagementObject returns an object with the Version and the\nAdd-Type Load Template for each version on the server.",
        "Tags":  "SMO",
        "Author":  "Ben Miller (@DBAduck - http://dbaduck.com)",
        "Synopsis":  "Gets SQL Mangaement Object versions installed on the machine.",
        "Name":  "Get-DbaSqlManagementObject",
        "Links":  "https://dbatools.io/Get-DbaSqlManagementObject",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSqlManagementObject\r\n\r\nReturns all versions of SMO on the computer\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSqlManagementObject -VersionNumber 13\r\n\r\nReturns just the version specified. If the version does not exist then it will return nothing.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaSqlModule",
        "Description":  "Quickly find modules (Stored Procs, Functions, Views, Constraints, Rules, Triggers, etc) that have been modified in a database, or across all databases.\nResults will exclude the module definition, but can be queried explicitly.",
        "Tags":  [
                     "StoredProcedure",
                     "Trigger"
                 ],
        "Author":  "Brandon Abshire, netnerds.net",
        "Synopsis":  "Displays all objects in sys.sys_modules after specified modification date.  Works on SQL Server 2008 and above.",
        "Name":  "Get-DbaSqlModule",
        "Links":  "https://dbatools.io/Get-DbaSqlModule",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSqlModule -SqlServer sql2008, sqlserver2012\r\n\r\nReturn all modules for servers sql2008 and sqlserver2012 sorted by Database, Modify_Date ASC.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSqlModule -SqlServer sql2008, sqlserver2012 | Select *\r\n\r\nShows hidden definition column (informative wall of text).\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSqlModule -SqlServer sql2008 -Database TestDB -ModifiedSince \"01/01/2017 10:00:00 AM\"\r\n\r\nReturn all modules on server sql2008 for only the TestDB database with a modified date after 01/01/2017 10:00:00 AM.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSqlModule -SqlServer sql2008 -Type View, Trigger, ScalarFunction\r\n\r\nReturn all modules on server sql2008 for all databases that are triggers, views or scalar functions.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaSqlProductKey",
        "Description":  "Using a string of servers, a text file, or Central Management Server to provide a list of servers, this script will go to each server and get the product key for all installed instances. Clustered instances are supported as well. Requires regular user access to the SQL instances, SMO installed locally, Remote Registry enabled and accessible by the account running the script.\n\nUses key decoder by Jakob Bindslet (http://goo.gl/1jiwcB)",
        "Tags":  [
                     "SQL",
                     "Product Key"
                 ],
        "Author":  "Chrissy LeMaire (@cl), netnerds.net",
        "Synopsis":  "Gets SQL Server Product Keys from local or destination SQL Servers. Works with SQL Server 2005-2016",
        "Name":  "Get-DbaSqlProductKey",
        "Links":  "https://dbatools.io/Get-DbaSqlProductKey",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSqlProductKey winxp, sqlservera, sqlserver2014a, win2k8\r\n\r\nGets SQL Server versions, editions and product keys for all instances within each server or workstation.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSqlProductKey -SqlCms sqlserver01\r\n\r\nGets SQL Server versions, editions and product keys for all instances within sqlserver01\u0027s Central Management Server\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSqlProductKey -ServersFromFile C:\\Scripts\\servers.txt\r\n\r\nGets SQL Server versions, editions and product keys for all instances listed within C:\\Scripts\\servers.txt\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaSqlRegistryRoot",
        "Description":  "Uses SQL WMI to find the Registry Root of each SQL Server instance on a computer",
        "Tags":  [
                     "Configuration",
                     "Registry"
                 ],
        "Synopsis":  "Uses SQL WMI to find the Registry Root of each SQL Server instance on a computer",
        "Name":  "Get-DbaSqlRegistryRoot",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSqlRegistryRoot\r\n\r\nGets the registry root for all instances on localhost\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSqlRegistryRoot -ComputerName server1\r\n\r\nGets the registry root for all instances on server1\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaSqlService",
        "Description":  "Gets the SQL Server related services on one or more computers.",
        "Tags":  [
                     "Service",
                     "SqlServer",
                     "Instance",
                     "Connect"
                 ],
        "Author":  "Klaas Vandenberghe ( @PowerDBAKlaas )",
        "Synopsis":  "Gets the SQL Server related services on a computer.",
        "Name":  "Get-DbaSqlService",
        "Links":  "https://dbatools.io/Get-DbaSqlService",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSqlService -ComputerName sqlserver2014a\r\n\r\nGets the SQL Server related services on computer sqlserver2014a.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e\u0027sql1\u0027,\u0027sql2\u0027,\u0027sql3\u0027 | Get-DbaSqlService\r\n\r\nGets the SQL Server related services on computers sql1, sql2 and sql3.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSqlService -ComputerName sql1,sql2 | Out-GridView\r\n\r\nGets the SQL Server related services on computers sql1 and sql2, and shows them in a grid view.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSqlService -ComputerName $MyServers -Type SSRS\r\n\r\nGets the SQL Server related services of type \"SSRS\" (Reporting Services) on computers in the variable MyServers.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003e$services = Get-DbaSqlService -ComputerName sql1 -Type Agent,Engine\r\n\r\n$services.ChangeStartMode(\u0027Manual\u0027)\r\n\r\nGets the SQL Server related services of types Sql Agent and DB Engine on computer sql1 and changes their startup mode \r\nto \u0027Manual\u0027.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003e(Get-DbaSqlService sql1 -Type Engine).Restart($true)\r\n\r\nCalls a Restart method for each Engine service on computer sql1 with -Force option.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaSsisEnvironmentVariable",
        "Description":  "This command gets all variables from specified environment from SSIS Catalog. All sensitive values are decrypted.\nThe function communicates directly with SSISDB database, \"SQL Server Integration Services\" service isn\u0027t queried there.\nEach parameter (besides SqlInstance and SqlCredential) acts as the filter to only include or exclude particular element",
        "Tags":  [
                     "SSIS",
                     "SSISDB",
                     "Variable"
                 ],
        "Author":  "Bartosz Ratajczyk ( @b_ratajczyk )",
        "Synopsis":  "This command gets specified SSIS Environment and all its variables",
        "Name":  "Get-DbaSsisEnvironmentVariable",
        "Links":  "https://dbatools.io/Get-DbaSsisEnvironmentVariable",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSsisEnvironmentVariable -SqlInstance localhost -Environment DEV -Folder DWH_ETL\r\n\r\nGets variables of \u0027DEV\u0027 environment located in \u0027DWH_ETL\u0027 folder on \u0027localhost\u0027 Server\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSsisEnvironmentVariable -SqlInstance localhost -Environment DEV -Folder DWH_ETL, DEV2, QA\r\n\r\nGets variables of \u0027DEV\u0027 environment(s) located in folders \u0027DWH_ETL\u0027, \u0027DEV2\u0027 and \u0027QA\u0027 on \u0027localhost\u0027 server\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSsisEnvironmentVariable -SqlInstance localhost -Environment DEV -FolderExclude DWH_ETL, DEV2, QA\r\n\r\nGets variables of \u0027DEV\u0027 environments located in folders other than \u0027DWH_ETL\u0027, \u0027DEV2\u0027 and \u0027QA\u0027 on \u0027localhost\u0027 server\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSsisEnvironmentVariable -SqlInstance localhost -Environment DEV, PROD -Folder DWH_ETL, DEV2, QA\r\n\r\nGets variables of \u0027DEV\u0027 and \u0027PROD\u0027 environment(s) located in folders \u0027DWH_ETL\u0027, \u0027DEV2\u0027 and \u0027QA\u0027 on \u0027localhost\u0027 server\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSsisEnvironmentVariable -SqlInstance localhost -EnvironmentExclude DEV, PROD -Folder DWH_ETL, DEV2, QA\r\n\r\nGets variables of environments other than \u0027DEV\u0027 and \u0027PROD\u0027 located in folders \u0027DWH_ETL\u0027, \u0027DEV2\u0027 and \u0027QA\u0027 on \u0027localhost\u0027 \r\nserver\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSsisEnvironmentVariable -SqlInstance localhost -EnvironmentExclude DEV, PROD -FolderExclude DWH_ETL, \r\nDEV2, QA\r\n\r\nGets variables of environments other than \u0027DEV\u0027 and \u0027PROD\u0027 located in folders other than \u0027DWH_ETL\u0027, \u0027DEV2\u0027 and \u0027QA\u0027 on \r\n\u0027localhost\u0027 server\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 7 --------------------------\r\n\r\nPS C:\\\u003e\u0027localhost\u0027 | Get-DbaSsisEnvironmentVariable -EnvironmentExclude DEV, PROD\r\n\r\nGets all SSIS environments except \u0027DEV\u0027 and \u0027PROD\u0027 from \u0027localhost\u0027 server. The server name comes from pipeline\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 8 --------------------------\r\n\r\nPS C:\\\u003e\u0027SRV1\u0027, \u0027SRV3\u0027 | Get-DbaSsisEnvironmentVariable\r\n\r\nGets all SSIS environments from \u0027SRV1\u0027 and \u0027SRV3\u0027 servers. The server\u0027s names come from pipeline\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 9 --------------------------\r\n\r\nPS C:\\\u003e\u0027SRV1\u0027, \u0027SRV2\u0027 | Get-DbaSsisEnvironmentVariable DEV | Out-GridView\r\n\r\nGets all variables from \u0027DEV\u0027 Environment(s) on servers \u0027SRV1\u0027 and \u0027SRV2\u0027 and outputs it as the GridView.\r\nThe server names come from the pipeline.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 10 --------------------------\r\n\r\nPS C:\\\u003e\u0027localhost\u0027 | Get-DbaSsisEnvironmentVariable -EnvironmentExclude DEV, PROD | Select-Object -Property Name, Value \r\n| Where-Object {$_.Name -match \u0027^a\u0027} | Out-GridView\r\n\r\nGets all variables from Environments other than \u0027DEV\u0027 and \u0027PROD\u0027 on \u0027localhost\u0027 server,\r\nselects Name and Value properties for variables that names start with letter \u0027a\u0027 and outputs it as the GridView\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaSsisExecutionHistory",
        "Description":  "This command gets execution history for SSIS executison given one or more instances and can be filtered by Project, Environment,Folder or Status.",
        "Tags":  [
                     "Migration",
                     "SSIS"
                 ],
        "Author":  "Chris Tucker (ChrisTucker, @ChrisTuc47368095)",
        "Synopsis":  "Get-DbaSsisHistory Retreives SSIS project and package execution History, and environments from one SQL Server to another.",
        "Name":  "Get-DbaSsisExecutionHistory",
        "Links":  "https://dbatools.io/Get-DbaSsisExecutionHistory",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSsisExecutionHistory -SqlInstance SMTQ01 -Folder SMTQ_PRC\r\n\r\nGet all history items for SMTQ01 in folder SMTQ_PRC.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSsisExecutionHistory -SqlInstance SMTQ01 -Status Failed,Cancelled\r\n\r\nGets all failed or canceled executions for SMTQ01.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSsisExecutionHistory -SqlInstance SMTQ01,SMTQ02 -Status Failed,Cancelled -Whatif\r\n\r\nShows what would happen if the command were executed and would return the SQL statement that would be executed per \r\ninstance.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaStartupParameter",
        "Description":  "Displays values for a detailed list of SQL Server Startup Parameters including Master Data Path, Master Log path, Error Log, Trace Flags, Parameter String and much more.\n\nThis command relies on remote Windows Server (SQL WMI/WinRm) access. You can pass alternative Windows credentials by using the -Credential parameter.\n\nSee https://msdn.microsoft.com/en-us/library/ms190737.aspx for more information.",
        "Tags":  [
                     "WSMan",
                     "SQLWMI",
                     "Memory"
                 ],
        "Synopsis":  "Displays values for a detailed list of SQL Server Startup Parameters.",
        "Name":  "Get-DbaStartupParameter",
        "Links":  "https://dbatools.io/Get-DbaStartupParameter",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaStartupParameter -SqlInstance sql2014\r\n\r\nLogs into SQL WMI as the current user then displays the values for numerous startup parameters.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e$wincred = Get-Credential ad\\sqladmin\r\n\r\nGet-DbaStartupParameter -SqlInstance sql2014 -Credential $wincred -Simple\r\n\r\nLogs in to WMI using the ad\\sqladmin credential and gathers simplified information about the SQL Server Startup \r\nParameters.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaSuspectPage",
        "Description":  "This function returns any records that were stored due to suspect pages in databases on a SQL Server Instance.",
        "Tags":  [
                     "Pages",
                     "DBCC"
                 ],
        "Author":  "Garry Bargsley (@gbargsley), http://blog.garrybargsley.com",
        "Synopsis":  "Returns data that is stored in SQL for Suspect Pages on the specified SQL Server Instance",
        "Name":  "Get-DbaSuspectPage",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSuspectPage -SqlInstance sql2016\r\n\r\nRetrieve any records stored for Suspect Pages on the sql2016 SQL Server.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSuspectPage -SqlInstance sql2016 -Database Test\r\n\r\nRetrieve any records stored for Suspect Pages on the sql2016 SQL Server and the Test database only.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaTable",
        "Description":  "Shows table information around table row and data sizes and if it has any table type information.",
        "Tags":  [
                     "Database",
                     "Tables"
                 ],
        "Author":  "Stephen Bennett, https://sqlnotesfromtheunderground.wordpress.com/",
        "Synopsis":  "Returns a summary of information on the tables",
        "Name":  "Get-DbaTable",
        "Links":  "https://dbatools.io/Get-DbaTable",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaTable -SqlInstance DEV01 -Database Test1\r\n\r\nReturn all tables in the Test1 database\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaTable -SqlInstance DEV01 -Database MyDB -Table MyTable\r\n\r\nReturn only information on the table MyTable from the database MyDB\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaTable -SqlInstance DEV01 -Table MyTable\r\n\r\nReturns information on table called MyTable if it exists in any database on the server, under any schema\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaTable -SqlInstance DEV01 -Table dbo.[First.Table]\r\n\r\nReturns information on table called First.Table on schema dbo if it exists in any database on the server\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003e\u0027localhost\u0027,\u0027localhost\\namedinstance\u0027 | Get-DbaTable -Database DBA -Table Commandlog\r\n\r\nReturns information on the CommandLog table in the DBA database on both instances localhost and the named instance \r\nlocalhost\\namedinstance\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaTcpPort",
        "Description":  "By default, this function returns just the TCP port used by the specified SQL Server.\n\nIf -Detailed is specified, the server name, IPAddress (ipv4 and ipv6), port number and an indicator of whether or not the port assignment is static are returned.\n\nRemote sqlwmi is used by default. If this doesn\u0027t work, then remoting is used. If neither work, it defaults to T-SQL which can provide only the port.",
        "Tags":  [
                     "SQLWMI",
                     "tcp"
                 ],
        "Synopsis":  "Returns the TCP port used by the specified SQL Server.",
        "Name":  "Get-DbaTcpPort",
        "Links":  "https://dbatools.io/Get-DbaTcpPort",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaTcpPort -SqlInstance sqlserver2014a\r\n\r\nReturns just the port number for the default instance on sqlserver2014a.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaTcpPort -SqlInstance winserver\\sqlexpress, sql2016\r\n\r\nReturns an object with server name and port number for the sqlexpress on winserver and the default instance on sql2016.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaTcpPort -SqlInstance sqlserver2014a, sql2016 -Detailed\r\n\r\nReturns an object with server name, IPAddress (ipv4 and ipv6), port and static ($true/$false) for sqlserver2014a and \r\nsql2016.\r\n\r\nRemote sqlwmi is used by default. If this doesn\u0027t work, then remoting is used. If neither work, it defaults to T-SQL \r\nwhich can provide only the port.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRegisteredServer -SqlInstance sql2014 | Get-DbaTcpPort -ExcludeIpv6 -Detailed\r\n\r\nReturns an object with server name, IPAddress (just ipv4), port and static ($true/$false) for every server listed in \r\nthe Central Management Server on sql2014.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaTempdbUsage",
        "Description":  "This function queries DMVs for running sessions using Tempdb and returns results if those sessions have user or internal space allocated or deallocated against them.",
        "Tags":  [
                     "Tempdb",
                     "Space"
                 ],
        "Synopsis":  "Gets Tempdb usage for running queries.",
        "Name":  "Get-DbaTempdbUsage",
        "Links":  "https://dbatools.io/Get-DbaTempdbUsage",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaTempdbUsage -SqlInstance localhost\\SQLDEV2K14\r\n\r\nGets tempdb usage for localhost\\SQLDEV2K14\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbatoolsLog",
        "Description":  "Returns log entries for dbatools. Handy when debugging or developing a script using it.",
        "Synopsis":  "Returns log entries for dbatools",
        "Name":  "Get-DbatoolsLog",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbatoolsLog\r\n\r\nReturns all log entries currently in memory.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbatoolsLog -Target \"a\" -Last 1 -Skip 1\r\n\r\nReturns all log entries that targeted the object \"a\" in the second last execution sent.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbatoolsLog -Tag \"fail\" -Last 5\r\n\r\nReturns all log entries within the last 5 executions that contained the tag \"fail\"\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaTopResourceUsage",
        "Description":  "Returns the top 20 resource consumers for cached queries based on four different metrics: duration, frequency, IO, and CPU.\n\nThis command is based off of queries provided by Michael J. Swart at http://michaeljswart.com/go/Top20\n\nPer Michael: \"I\u0027ve posted queries like this before, and others have written many other versions of this query. All these queries are based on sys.dm_exec_query_stats.\"",
        "Tags":  [
                     "Query",
                     "Performance"
                 ],
        "Synopsis":  "Returns the top 20 resource consumers for cached queries based on four different metrics: duration, frequency, IO, and CPU.",
        "Name":  "Get-DbaTopResourceUsage",
        "Links":  "https://dbatools.io/Get-DbaTopResourceUsage",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaTopResourceUsage -SqlInstance sql2008, sql2012\r\n\r\nReturn the 80 (20 x 4 types) top usage results by duration, frequency, IO, and CPU servers for servers sql2008 and \r\nsql2012\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaTopResourceUsage -SqlInstance sql2008 -Type Duration, Frequency -Database TestDB\r\n\r\nReturn the highest usage by duration (top 20) and frequency (top 20) for the TestDB on sql2008\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaTopResourceUsage -SqlInstance sql2016 -Limit 30\r\n\r\nReturn the highest usage by duration (top 30) and frequency (top 30) for the TestDB on sql2016\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaTopResourceUsage -SqlInstance sql2008, sql2012 -ExcludeSystem\r\n\r\nReturn the 80 (20 x 4 types) top usage results by duration, frequency, IO, and CPU servers for servers sql2008 and \r\nsql2012 without any System Objects\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaTopResourceUsage -SqlInstance sql2016| Select *\r\n\r\nReturn all the columns plus the QueryPlan column\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaTrace",
        "Description":  "This function returns a list of traces on a SQL Server instance and identifies the default trace file",
        "Tags":  [
                     "Security",
                     "Trace"
                 ],
        "Author":  "Garry Bargsley (@gbargsley), http://blog.garrybargsley.com",
        "Synopsis":  "Gets a list of trace(s) from specified SQL Server Instance",
        "Name":  "Get-DbaTrace",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaTrace -SqlInstance sql2016\r\n\r\nLists all the tracefiles on the sql2016 SQL Server.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaTrace -SqlInstance sql2016 -Default\r\n\r\nLists the default trace information on the sql2016 SQL Server.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaTraceFlag",
        "Description":  "Returns Trace Flags that are enabled globally on each instance(s) of SQL Server as an object.",
        "Tags":  "TraceFlag",
        "Author":  "Kevin Bullen (@sqlpadawan)",
        "Synopsis":  "Get global Trace Flag(s) information for each instance(s) of SQL Server.",
        "Name":  "Get-DbaTraceFlag",
        "Links":  "https://dbatools.io/Get-DbaTraceFlag",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaTraceFlag -SqlInstance localhost\r\n\r\nReturns all Trace Flag information on the local default SQL Server instance\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaTraceFlag -SqlInstance localhost, sql2016\r\n\r\nReturns all Trace Flag(s) for the local and sql2016 SQL Server instances\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaTraceFlag -SqlInstance localhost -TraceFlag 4199,3205\r\n\r\nReturns Trace Flag status for TF 4199 and 3205 for the local SQL Server instance if they are enabled.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaTrigger",
        "Description":  "Get all existing triggers on one or more SQL instances.\n\nDefault output includes columns ComputerName, SqlInstance, Database, TriggerName, IsEnabled and DateLastModified.",
        "Tags":  [
                     "Database",
                     "Triggers"
                 ],
        "Author":  "Klaas Vandenberghe ( @PowerDBAKlaas )",
        "Synopsis":  "Get all existing triggers on one or more SQL instances.",
        "Name":  "Get-DbaTrigger",
        "Links":  "https://dbatools.io/Get-DbaTrigger",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaTrigger -SqlInstance ComputerA\\sql987\r\n\r\nReturns a custom object displaying ComputerName, SqlInstance, Database, TriggerName, IsEnabled and DateLastModified.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaTrigger -SqlInstance \u0027ComputerA\\sql987\u0027,\u0027ComputerB\u0027\r\n\r\nReturns a custom object displaying ComputerName, SqlInstance, Database, TriggerName, IsEnabled and DateLastModified \r\nfrom two instances.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaTrigger -SqlInstance ComputerA\\sql987 | Out-Gridview\r\n\r\nReturns a gridview displaying ComputerName, SqlInstance, Database, TriggerName, IsEnabled and DateLastModified.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003e\u0027ComputerA\\sql987\u0027,\u0027ComputerB\u0027 | Get-DbaTrigger | Out-Gridview\r\n\r\nReturns a custom object displaying ComputerName, SqlInstance, Database, TriggerName, IsEnabled and DateLastModified \r\nfrom two instances.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaUptime",
        "Description":  "By default, this command returns for each SQL Server instance passed in:\nSQL Instance last startup time, Uptime as a PS TimeSpan, Uptime as a formatted string\nHosting Windows server last startup time, Uptime as a PS TimeSpan, Uptime as a formatted string",
        "Tags":  "CIM",
        "Author":  "Stuart Moore (@napalmgram), stuart-moore.com",
        "Synopsis":  "Returns the uptime of the SQL Server instance, and if required the hosting windows server",
        "Name":  "Get-DbaUptime",
        "Links":  "https://dbatools.io/Get-DbaUptime",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaUptime -SqlInstance SqlBox1\\Instance2\r\n\r\nReturns an object with SQL Server start time, uptime as TimeSpan object, uptime as a string, and Windows host boot \r\ntime, host uptime as TimeSpan objects and host uptime as a string for the sqlexpress instance on winserver\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaUptime -SqlInstance winserver\\sqlexpress, sql2016\r\n\r\nReturns an object with SQL Server start time, uptime as TimeSpan object, uptime as a string, and Windows host boot \r\ntime, host uptime as TimeSpan objects and host uptime as a string for the sqlexpress instance on host winserver  and \r\nthe default instance on host sql2016\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRegisteredServer -SqlInstance sql2014 | Get-DbaUptime\r\n\r\nReturns an object with SQL Server start time, uptime as TimeSpan object, uptime as a string, and Windows host boot \r\ntime, host uptime as TimeSpan objects and host uptime as a string for every server listed in the Central Management \r\nServer on sql2014\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaUserLevelPermission",
        "Description":  "This command will display all server logins, server level securable, database logins and database securables.\n\nDISA STIG implementators will find this command useful as it uses Permissions.sql provided by DISA.\n\nNote that if you Ctrl-C out of this command and end it prematurely, it will leave behind a STIG schema in tempdb.",
        "Tags":  [
                     "Discovery",
                     "Permissions",
                     "Security"
                 ],
        "Author":  "Brandon Abshire, netnerds.net",
        "Synopsis":  "Displays detailed permissions information for the server and database roles and securables.",
        "Name":  "Get-DbaUserLevelPermission",
        "Links":  "https://dbatools.io/Get-DbaUserLevelPermission",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaUserLevelPermission -SqlInstance sql2008, sqlserver2012\r\n\r\nCheck server and database permissions for servers sql2008 and sqlserver2012.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaUserLevelPermission -SqlInstance sql2008 -Database TestDB\r\n\r\nCheck server and database permissions on server sql2008 for only the TestDB database\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaUserLevelPermission -SqlInstance sql2008 -Database TestDB -IncludePublicGuest -IncludeSystemObjects\r\n\r\nCheck server and database permissions on server sql2008 for only the TestDB database,\r\nincluding public and guest grants, and sys schema objects.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaWaitingTask",
        "Description":  "This command is based on waiting task T-SQL script published by Paul Randal.\nReference: https://www.sqlskills.com/blogs/paul/updated-sys-dm_os_waiting_tasks-script-2/",
        "Tags":  [
                     "Waits",
                     "Task",
                     "WaitTask"
                 ],
        "Author":  "Shawn Melton (@wsmelton)",
        "Synopsis":  "Displays waiting task.",
        "Name":  "Get-DbaWaitingTask",
        "Links":  "https://dbatools.io/Get-DbaWaitingTask",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaWaitingTask -SqlInstance sqlserver2014a\r\n\r\nReturns the waiting task for all sessions on sqlserver2014a\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaWaitingTask -SqlInstance sqlserver2014a -IncludeSystemSpid\r\n\r\nReturns the waiting task for all sessions (user and system) on sqlserver2014a\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaWaitResource",
        "Description":  "Given a wait resource in the form of:\n    \u0027PAGE: 10:1:9180084 \u0027\nreturns the database, data file and the system object which is being waited up.\nGiven a wait resource in the form of:\n    \u0027KEY: 7:35457594073541168 (de21f92a1572)\u0027\nreturns the database, object and index that is being waited on, With the -row switch the row data will also be returned.",
        "Tags":  [
                     "Pages",
                     "DBCC"
                 ],
        "Author":  "Stuart Moore (@napalmgram), stuart-moore.com",
        "Synopsis":  "Returns the resource being waited upon",
        "Name":  "Get-DbaWaitResource",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaWaitResource -SqlInstance server1 -WaitResource \u0027PAGE: 10:1:9180084\u0027\r\n\r\nWill return an object containing; database name, data file name, schema name and the object which owns the resource\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaWaitResource -Sql Instance server2 -WaitResource \u0027KEY: 7:35457594073541168 (de21f92a1572)\u0027\r\n\r\nWill return an object containing; database name, schema name and index name which is being waited on.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaWaitResource -Sql Instance server2 -WaitResource \u0027KEY: 7:35457594073541168 (de21f92a1572)\u0027 -row\r\n\r\nWill return an object containing; database name, schema name and index name which is being waited on, and in addition \r\nthe contents of the locked row at the time the command is run.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaWaitStatistic",
        "Description":  "This command is based off of Paul Randal\u0027s post \"Wait statistics, or please tell me where it hurts\"\n\nReturns:\n            WaitType\n            Category\n            WaitSeconds\n            ResourceSeconds\n            SignalSeconds\n            WaitCount\n            Percentage\n            AverageWaitSeconds\n            AverageResourceSeconds\n            AverageSignalSeconds\n            URL\n\nReference: https://www.sqlskills.com/blogs/paul/wait-statistics-or-please-tell-me-where-it-hurts/",
        "Tags":  "WaitStatistic",
        "Synopsis":  "Displays wait statistics",
        "Name":  "Get-DbaWaitStatistic",
        "Links":  "https://dbatools.io/Get-DbaWaitStatistic",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaWaitStatistic -SqlInstance sql2008, sqlserver2012\r\n\r\nCheck wait statistics for servers sql2008 and sqlserver2012\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaWaitStatistic -SqlInstance sql2008 -Threshold 98 -IncludeIgnorable\r\n\r\nCheck wait statistics on server sql2008 for thresholds above 98% and include wait stats that are most often, but not \r\nalways, ignorable\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaWaitStatistic -SqlInstance sql2008 | Select *\r\n\r\nShows detailed notes, if available, from Paul\u0027s post\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003e$output = Get-DbaWaitStatistic -SqlInstance sql2008 -Threshold 100 -IncludeIgnorable | Select * | \r\nConvertTo-DbaDataTable\r\n\r\nCollects all Wait Statistics (including ignorable waits) on server sql2008 into a Data Table.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003e$output = Get-DbaWaitStatistic -SqlInstance sql2008\r\n\r\n$output\r\nforeach ($row in ($output | Sort-Object -Unique Url)) { Start-Process ($row).Url }\r\n\r\nDisplays the output then loads the associated sqlskills website for each result. Opens one tab per unique URL.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaWindowsLog",
        "Description":  "Gets Windows Application events associated with an instance",
        "Tags":  "Logging",
        "Author":  "Drew Furgiuele",
        "Synopsis":  "Gets Windows Application events associated with an instance",
        "Name":  "Get-DbaWindowsLog",
        "Links":  "https://dbatools.io/Get-DbaWindowsLog",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003e$ErrorLogs = Get-DbaWindowsLog -SqlInstance sql01\\sharepoint\r\n\r\n$ErrorLogs | Where-Object ErrorNumber -eq 18456\r\n\r\nReturns all lines in the errorlogs that have event number 18456 in them\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaXEObject",
        "Description":  "This function returns a list of Traces on the specified SQL Server instance(s) and identifies the default Trace File",
        "Tags":  [
                     "ExtendedEvent",
                     "XE",
                     "XEvent"
                 ],
        "Synopsis":  "Gets a list of trace(s) from specified SQL Server instance(s).",
        "Name":  "Get-DbaXEObject",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaXEObject -SqlInstance sql2016\r\n\r\nLists all the XE Objects on the sql2016 SQL Server.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaXEObject -SqlInstance sql2017 -Type Action, Event\r\n\r\nLists all the XE Objects of type Action and Event on the sql2017 SQL Server.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaXESession",
        "Description":  "Retrieves a list of Extended Events Sessions present on the specified SQL Server instance(s).",
        "Tags":  [
                     "ExtendedEvent",
                     "XE",
                     "XEvent"
                 ],
        "Author":  "Klaas Vandenberghe ( @PowerDBAKlaas )",
        "Synopsis":  "Gets a list of Extended Events Sessions from the specified SQL Server instance(s).",
        "Name":  "Get-DbaXESession",
        "Links":  "https://dbatools.io/Get-DbaXESession",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaXESession -SqlInstance ServerA\\sql987\r\n\r\nReturns a custom object with ComputerName, SQLInstance, Session, StartTime, Status and other properties.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaXESession -SqlInstance ServerA\\sql987 | Format-Table ComputerName, SqlInstance, Session, Status -AutoSize\r\n\r\nReturns a formatted table displaying ComputerName, SqlInstance, Session, and Status.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003e\u0027ServerA\\sql987\u0027,\u0027ServerB\u0027 | Get-DbaXESession\r\n\r\nReturns a custom object with ComputerName, SqlInstance, Session, StartTime, Status and other properties, from multiple \r\nSQL instances.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaXESessionTarget",
        "Description":  "Retrieves a list of Extended Events Session Targets from the specified SQL Server instance(s).",
        "Tags":  [
                     "ExtendedEvent",
                     "XE",
                     "XEvent"
                 ],
        "Synopsis":  "Get a list of Extended Events Session Targets from the specified SQL Server instance(s).",
        "Name":  "Get-DbaXESessionTarget",
        "Links":  "https://dbatools.io/Get-DbaXESessionTarget",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaXESessionTarget -SqlInstance ServerA\\sql987 -Session system_health\r\n\r\nShows targets for the system_health session on ServerA\\sql987.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaXESession -SqlInstance sql2016 -Session system_health | Get-DbaXESessionTarget\r\n\r\nReturns the targets for the system_health session on sql2016.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaXESession -SqlInstance sql2016 -Session system_health | Get-DbaXESessionTarget -Target package0.event_file\r\n\r\nReturn only the package0.event_file target for the system_health session on sql2016.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaXESessionTemplate",
        "Description":  "Parses Extended Event XML templates. Defaults to parsing templates in the dbatools template repository (\\bin\\xetemplates\\).\n\nThe default repository contains templates from:\n        Microsoft\u0027s Templates that come with SSMS\n        Jes Borland\u0027s \"Everyday Extended Events\" presentation and GitHub repository (https://github.com/grrlgeek/extended-events)\n        Christian Gräfe\u0027s XE Repo: https://github.com/chrgraefe/sqlscripts/blob/master/XE-Events/\n        Erin Stellato\u0027s Blog: https://www.sqlskills.com/blogs/erin/\n\nSome profile templates converted using:\n        sp_SQLskills_ConvertTraceToExtendedEvents.sql\n        Jonathan M. Kehayias, SQLskills.com\n        http://sqlskills.com/blogs/jonathan",
        "Tags":  [
                     "ExtendedEvent",
                     "XE",
                     "XEvent"
                 ],
        "Synopsis":  "Parses Extended Event XML templates. Defaults to parsing templates in the dbatools template repository (\\bin\\xetemplates\\).",
        "Name":  "Get-DbaXESessionTemplate",
        "Links":  "https://dbatools.io/Get-DbaXESessionTemplate",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaXESessionTemplate\r\n\r\nReturns information about all the templates in the local dbatools repository.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaXESessionTemplate | Out-GridView -PassThru | Import-DbaXESessionTemplate -SqlInstance sql2017 | \r\nStart-DbaXESession\r\n\r\nAllows you to select a Session template, then import it to the specified instance and start the session.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaXESessionTemplate -Path \"$home\\Documents\\SQL Server Management Studio\\Templates\\XEventTemplates\"\r\n\r\nReturns information about all the templates in your local XEventTemplates repository.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaXESessionTemplate -Pattern duration\r\n\r\nReturns information about all the templates that match the word \"duration\" in the title, category or body.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaXESessionTemplate | Select-Object *\r\n\r\nReturns more information about the template, including the full path/filename.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaXESmartTarget",
        "Description":  "Gets an XESmartTarget PowerShell Job created by Start-DbaXESmartTarget.",
        "Tags":  [
                     "ExtendedEvent",
                     "XE",
                     "XEvent"
                 ],
        "Synopsis":  "Gets an XESmartTarget PowerShell Job created by Start-DbaXESmartTarget.",
        "Name":  "Get-DbaXESmartTarget",
        "Links":  "https://dbatools.io/Get-DbaXESmartTarget\nhttps://github.com/spaghettidba/XESmartTarget/wiki",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaXESmartTarget\r\n\r\nGets an XESmartTarget PowerShell Job created by Start-DbaXESmartTarget.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Get-DbaXEStore",
        "Description":  "Get a Extended Events store",
        "Tags":  [
                     "ExtendedEvent",
                     "XE",
                     "XEvent"
                 ],
        "Synopsis":  "Get a Extended Events store",
        "Name":  "Get-DbaXEStore",
        "Links":  "https://dbatools.io/Get-DbaXEStore",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaXEStore -SqlInstance ServerA\\sql987\r\n\r\nReturns an XEvent Store.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Import-DbaCsvToSql",
        "Description":  "Import-DbaCsvToSql takes advantage of .NET\u0027s super fast SqlBulkCopy class to import CSV files into SQL Server at up to 90,000 rows a second.\n\nThe entire import is contained within a transaction, so if a failure occurs or the script is aborted, no changes will persist.\n\nIf the table specified does not exist, it will be automatically created using best guessed data types. In addition, the destination table can be truncated prior to import.\n\nThe Query parameter will be used to import only the data returned from a SQL Query executed against the CSV file(s). This function supports a number of bulk copy options. Please see parameter list for details.",
        "Tags":  "Migration",
        "Author":  "Chrissy LeMaire (@cl), netnerds.net",
        "Synopsis":  "Efficiently imports very large (and small) CSV files into SQL Server using only the .NET Framework and PowerShell.",
        "Name":  "Import-DbaCsvToSql",
        "Links":  "https://blog.netnerds.net/2015/09/Import-DbaCsvtosql-super-fast-csv-to-sql-server-import-powershell-module/",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eImport-DbaCsvToSql -Csv C:\\temp\\housing.csv -SqlInstance sql001 -Database markets\r\n\r\nImports the entire comma-delimited housing.csv to the SQL \"markets\" database on a SQL Server named sql001.\r\n\r\nSince a table name was not specified, the table name is automatically determined from filename as \"housing\" and a \r\nprompt will appear to confirm table name.\r\n\r\nThe first row is not skipped, as it does not contain column names.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eImport-DbaCsvToSql -Csv .\\housing.csv -SqlInstance sql001 -Database markets -Table housing -First 100000 -Safe \r\n-Delimiter \"`t\" -FirstRowColumns\r\n\r\nImports the first 100,000 rows of the tab delimited housing.csv file to the \"housing\" table in the \"markets\" database \r\non a SQL Server named sql001. Since -Safe was specified, the OleDB method will be used for the bulk import. The first \r\nrow is skipped, as it contains column names.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eImport-DbaCsvToSql -csv C:\\temp\\huge.txt -SqlInstance sqlcluster -Database locations -Table latitudes -Delimiter \r\n\"|\" -Turbo\r\n\r\nImports all records from the pipe delimited huge.txt file using the fastest method possible into the latitudes table \r\nwithin the locations database. Obtains a table lock for the duration of the bulk copy operation. This specific command \r\nhas been used\r\nto import over 10.5 million rows in 2 minutes.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eImport-DbaCsvToSql -Csv C:\\temp\\housing.csv, .\\housing2.csv -SqlInstance sql001 -Database markets -Table housing \r\n-Delimiter \"`t\" -query \"select top 100000 column1, column3 from csv\" -Truncate\r\n\r\nTruncates the \"housing\" table, then imports columns 1 and 3 of the first 100000 rows of the tab-delimited housing.csv \r\nin the C:\\temp directory, and housing2.csv in the current directory. Since the query is executed against both files, a \r\ntotal of 200,000 rows will be imported.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eImport-DbaCsvToSql -Csv C:\\temp\\housing.csv -SqlInstance sql001 -Database markets -Table housing -query \"select \r\naddress, zip from csv where state = \u0027Louisiana\u0027\" -FirstRowColumns -Truncate -FireTriggers\r\n\r\nUses the first line to determine CSV column names. Truncates the \"housing\" table on the SQL Server, then imports the \r\naddress and zip columns from all records in the housing.csv where the state equals Louisiana.\r\n\r\nTriggers are fired for all rows. Note that this does slightly slow down the import.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eImport-DbaCsvToSql -Csv c:\\temp\\SingleColumn.csv -SqlInstance sql001 -Database markets -Table TempTable \r\n-SingleColumn\r\n\r\nUpload the single column Csv SingleColumn.csv to Temptable which has just one column\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 7 --------------------------\r\n\r\nPS C:\\\u003eImport-DbaCsvToSql -Csv \"\\\\FileServer\\To Import\\housing.csv\" -SqlInstance sql001 -Database markets\r\n\r\nImports the entire comma-delimited housing.csv located in the share named \"To Import\" on FileServer to the SQL \r\n\"markets\" database on a SQL Server named sql001.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 8 --------------------------\r\n\r\nPS C:\\\u003eImport-DbaCsvToSql -Csv \u0027\\\\FileServer\\R$\\To Import\\housing.csv\u0027 -SqlInstance sql001 -Database markets\r\n\r\nImports the entire comma-delimited housing.csv located in the directory R:\\To Import on FileServer using the \r\nadministrative share to the SQL \"markets\" database on a SQL Server named sql001.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Import-DbaPfDataCollectorSetTemplate",
        "Description":  "Imports a new Performance Monitor Data Collector Set Template either from the dbatools repository or a file you specify.\nWhen importing data collector sets from the local instance, Run As Admin is required.\n\nNote: The included counters will be added for all SQL instances on the machine by default.\nFor specific instances in addition to the default, use -Instance.\n\nSee https://msdn.microsoft.com/en-us/library/windows/desktop/aa371952 for more information",
        "Tags":  [
                     "Performance",
                     "DataCollector",
                     "PerfCounter"
                 ],
        "Synopsis":  "Imports a new Performance Monitor Data Collector Set Template either from the dbatools repository or a file you specify.",
        "Name":  "Import-DbaPfDataCollectorSetTemplate",
        "Links":  "https://dbatools.io/Import-DbaPfDataCollectorSetTemplate",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eImport-DbaPfDataCollectorSetTemplate -ComputerName sql2017 -Template \u0027Long Running Query\u0027\r\n\r\nCreates a new data collector set named \u0027Long Running Query\u0027 from the dbatools repository on the SQL Server sql2017.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eImport-DbaPfDataCollectorSetTemplate -ComputerName sql2017 -Template \u0027Long Running Query\u0027 -DisplayName \u0027New Long \r\nrunning query\u0027 -Confirm\r\n\r\nCreates a new data collector set named \"New Long Running Query\" using the \u0027Long Running Query\u0027 template. Forces a \r\nconfirmation if the template exists.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfDataCollectorSet -ComputerName sql2017 -Session db_ola_health | Remove-DbaPfDataCollectorSet\r\n\r\nImport-DbaPfDataCollectorSetTemplate -ComputerName sql2017 -Template db_ola_health | Start-DbaPfDataCollectorSet\r\n\r\nImports a session if it exists, then recreates it using a template.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfDataCollectorSetTemplate | Out-GridView -PassThru | Import-DbaPfDataCollectorSetTemplate -ComputerName \r\nsql2017\r\n\r\nAllows you to select a Session template then import to an instance named sql2017.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eImport-DbaPfDataCollectorSetTemplate -ComputerName sql2017 -Template \u0027Long Running Query\u0027 -Instance SHAREPOINT\r\n\r\nCreates a new data collector set named \u0027Long Running Query\u0027 from the dbatools repository on the SQL Server sql2017 for \r\nboth the default and the SHAREPOINT instance.\r\n\r\nIf you\u0027d like to remove counters for the default instance, use Remove-DbaPfDataCollectorCounter.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Import-DbaRegisteredServer",
        "Description":  "Imports registered servers and registered server groups to SQL Server Central Management Server (CMS)",
        "Tags":  [
                     "RegisteredServer",
                     "CMS"
                 ],
        "Author":  "Chrissy LeMaire (@cl)",
        "Synopsis":  "Imports registered servers and registered server groups to SQL Server Central Management Server (CMS)",
        "Name":  "Import-DbaRegisteredServer",
        "Links":  "https://dbatools.io/Import-DbaRegisteredServer",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eImport-DbaRegisteredServer -SqlInstance sql2012 -Path C:\\temp\\corp-regservers.xml\r\n\r\nImports C:\\temp\\corp-regservers.xml to the CMS on sql2012\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eImport-DbaRegisteredServer -SqlInstance sql2008 -Group hr\\Seattle -Path C:\\temp\\Seattle.xml\r\n\r\nImports C:\\temp\\Seattle.xml to Seattle subgroup within the hr group on sql2008\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRegisteredServer -SqlInstance sql2008, sql2012 | Import-DbaRegisteredServer -SqlInstance sql2017\r\n\r\nImports all registered servers from sql2008 and sql2012 to sql2017\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRegisteredServerGroup -SqlInstance sql2008 -Group hr\\Seattle | Import-DbaRegisteredServer -SqlInstance \r\nsql2017 -Group Seattle\r\n\r\nImports all registered servers from the hr\\Seattle group on sql2008 to the Seattle group on sql2017\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Import-DbaSpConfigure",
        "Description":  "Updates sp_configure settings on destination server.",
        "Synopsis":  "Updates sp_configure settings on destination server.",
        "Name":  "Import-DbaSpConfigure",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eImport-DbaSpConfigure sqlserver sqlcluster $SourceSqlCredential $DestinationSqlCredential\r\n\r\nImports the sp_configure settings from the source server sqlserver and sets them on the sqlcluster server\r\nusing the SQL credentials stored in the variables\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eImport-DbaSpConfigure -SqlInstance sqlserver -Path .\\spconfig.sql -SqlCredential $SqlCredential\r\n\r\nImports the sp_configure settings from the file .\\spconfig.sql and sets them on the sqlcluster server\r\nusing the SQL credential stored in the variables\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Import-DbaXESessionTemplate",
        "Description":  "Imports a new XESession XML Template either from the dbatools repository or a file you specify.",
        "Tags":  [
                     "ExtendedEvent",
                     "XE",
                     "XEvent"
                 ],
        "Synopsis":  "Imports a new XESession XML Template",
        "Name":  "Import-DbaXESessionTemplate",
        "Links":  "https://dbatools.io/Import-DbaXESessionTemplate",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eImport-DbaXESessionTemplate -SqlInstance sql2017 -Template db_query_wait_stats\r\n\r\nCreates a new XESession named db_query_wait_stats from the dbatools repository to the SQL Server sql2017.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eImport-DbaXESessionTemplate -SqlInstance sql2017 -Template db_query_wait_stats -Name \"Query Wait Stats\"\r\n\r\nCreates a new XESession named \"Query Wait Stats\" using the db_query_wait_stats template.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaXESession -SqlInstance sql2017 -Session db_ola_health | Remove-DbaXESession\r\n\r\nImport-DbaXESessionTemplate -SqlInstance sql2017 -Template db_ola_health | Start-DbaXESession\r\n\r\nImports a session if it exists, then recreates it using a template.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaXESessionTemplate | Out-GridView -PassThru | Import-DbaXESessionTemplate -SqlInstance sql2017\r\n\r\nAllows you to select a Session template then import to an instance named sql2017.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Install-DbaFirstResponderKit",
        "Description":  "Downloads, extracts and installs the First Responder Kit stored procedures:\nsp_Blitz, sp_BlitzWho, sp_BlitzFirst, sp_BlitzIndex, sp_BlitzCache and sp_BlitzTrace, etc.\n\nFirst Responder Kit links:\nhttp://FirstResponderKit.org\nhttps://github.com/BrentOzarULTD/SQL-Server-First-Responder-Kit",
        "Tags":  [
                     "BrentOzar",
                     "FRK",
                     "FirstResponderKit"
                 ],
        "Author":  "Tara Kizer, Brent Ozar Unlimited (https://www.brentozar.com/)",
        "Synopsis":  "Installs or updates the First Responder Kit stored procedures.",
        "Name":  "Install-DbaFirstResponderKit",
        "Links":  "https://dbatools.io/Install-DbaFirstResponderKit",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eInstall-DbaFirstResponderKit -SqlInstance server1 -Database master\r\n\r\nLogs into server1 with Windows authentication and then installs the FRK in the master database.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eInstall-DbaFirstResponderKit -SqlInstance server1\\instance1 -Database DBA\r\n\r\nLogs into server1\\instance1 with Windows authentication and then installs the FRK in the DBA database.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eInstall-DbaFirstResponderKit -SqlInstance server1\\instance1 -Database master -SqlCredential $cred\r\n\r\nLogs into server1\\instance1 with SQL authentication and then installs the FRK in the master database.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eInstall-DbaFirstResponderKit -SqlInstance sql2016\\standardrtm, sql2016\\sqlexpress, sql2014\r\n\r\nLogs into sql2016\\standardrtm, sql2016\\sqlexpress and sql2014 with Windows authentication and then installs the FRK in \r\nthe master database.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003e$servers = \"sql2016\\standardrtm\", \"sql2016\\sqlexpress\", \"sql2014\"\r\n\r\n$servers | Install-DbaFirstResponderKit\r\n\r\nLogs into sql2016\\standardrtm, sql2016\\sqlexpress and sql2014 with Windows authentication and then installs the FRK in \r\nthe master database.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eInstall-DbaFirstResponderKit -SqlInstance sql2016 -Branch dev\r\n\r\nInstalls the dev branch version of the FRK in the master database on sql2016 instance.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Install-DbaMaintenanceSolution",
        "Description":  "This script will download and install the latest version of SQL Server Maintenance Solution created by Ola Hallengren",
        "Tags":  [
                     "Ola",
                     "Maintenance"
                 ],
        "Author":  "Viorel Ciucu, [email protected], cviorel.com",
        "Synopsis":  "Download and Install SQL Server Maintenance Solution created by Ola Hallengren (https://ola.hallengren.com)",
        "Name":  "Install-DbaMaintenanceSolution",
        "Links":  "http://dbatools.io/Install-DbaMaintenanceSolution",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eInstall-DbaMaintenanceSolution -SqlInstance RES14224 -Database DBA -CleanupTime 72\r\n\r\nInstalls Ola Hallengren\u0027s Solution objects on RES14224 in the DBA database.\r\nBackups will default to the default Backup Directory.\r\nIf the Maintenance Solution already exists, the script will be halted.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eInstall-DbaMaintenanceSolution -SqlInstance RES14224 -Database DBA -BackupLocation \"Z:\\SQLBackup\" -CleanupTime 72\r\n\r\nThis will create the Ola Hallengren\u0027s Solution objects. Existing objects are not affected in any way.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eInstall-DbaMaintenanceSolution -SqlInstance RES14224 -Database DBA -BackupLocation \"Z:\\SQLBackup\" -CleanupTime \r\n72 -ReplaceExisting\r\n\r\nThis will drop and then recreate the Ola Hallengren\u0027s Solution objects\r\nThe cleanup script will drop and recreate:\r\n    - TABLE [dbo].[CommandLog]\r\n    - STORED PROCEDURE [dbo].[CommandExecute]\r\n    - STORED PROCEDURE [dbo].[DatabaseBackup]\r\n    - STORED PROCEDURE [dbo].[DatabaseIntegrityCheck]\r\n    - STORED PROCEDURE [dbo].[IndexOptimize]\r\n\r\nThe following SQL Agent jobs will be deleted:\r\n    - \u0027Output File Cleanup\u0027\r\n    - \u0027IndexOptimize - USER_DATABASES\u0027\r\n    - \u0027sp_delete_backuphistory\u0027\r\n    - \u0027DatabaseBackup - USER_DATABASES - LOG\u0027\r\n    - \u0027DatabaseBackup - SYSTEM_DATABASES - FULL\u0027\r\n    - \u0027DatabaseBackup - USER_DATABASES - FULL\u0027\r\n    - \u0027sp_purge_jobhistory\u0027\r\n    - \u0027DatabaseIntegrityCheck - SYSTEM_DATABASES\u0027\r\n    - \u0027CommandLog Cleanup\u0027\r\n    - \u0027DatabaseIntegrityCheck - USER_DATABASES\u0027\r\n    - \u0027DatabaseBackup - USER_DATABASES - DIFF\u0027\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Install-DbaWatchUpdate",
        "Description":  "Adds the scheduled task to support Watch-DbaUpdate.",
        "Tags":  "Module",
        "Synopsis":  "Adds the scheduled task to support Watch-DbaUpdate.",
        "Name":  "Install-DbaWatchUpdate",
        "Links":  "https://dbatools.io/Install-DbaWatchUpdate",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eInstall-DbaWatchUpdate\r\n\r\nAdds the scheduled task needed by Watch-DbaUpdate\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eInstall-DbaWatchUpdate -TaskName MyScheduledTask\r\n\r\nWill create the scheduled task as the name MyScheduledTask\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Install-DbaWhoIsActive",
        "Description":  "This command downloads, extracts and installs sp_WhoisActive with Adam\u0027s permission. To read more about sp_WhoisActive, please visit http://whoisactive.com and http://sqlblog.com/blogs/adam_machanic/archive/tags/who+is+active/default.aspx\n\nPlease consider donating to Adam if you find this stored procedure helpful: http://tinyurl.com/WhoIsActiveDonate\n\nNote that you will be prompted a bunch of times to confirm an action.",
        "Synopsis":  "Automatically installs or updates sp_WhoisActive by Adam Machanic.",
        "Name":  "Install-DbaWhoIsActive",
        "Links":  "https://dbatools.io/Install-DbaWhoIsActive",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eInstall-DbaWhoIsActive -SqlInstance sqlserver2014a -Database master\r\n\r\nDownloads sp_WhoisActive from the internet and installs to sqlserver2014a\u0027s master database. Connects to SQL Server \r\nusing Windows Authentication.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eInstall-DbaWhoIsActive -SqlInstance sqlserver2014a -SqlCredential $cred\r\n\r\nPops up a dialog box asking which database on sqlserver2014a you want to install the procedure into. Connects to SQL \r\nServer using SQL Authentication.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eInstall-DbaWhoIsActive -SqlInstance sqlserver2014a -Database master -LocalFile \r\nc:\\SQLAdmin\\whoisactive_install.sql\r\n\r\nInstalls sp_WhoisActive to sqlserver2014a\u0027s master database from the local file whoisactive_install.sql\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003e$instances = Get-DbaRegisteredServer sqlserver\r\n\r\nInstall-DbaWhoIsActive -SqlInstance $instances -Database master\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Invoke-DbaAdvancedRestore",
        "Description":  "This is the final piece in the Restore-DbaDatabase Stack. Usually a BackupHistory object will arrive here from Restore-DbaDatabse via the following pipeline:\nGet-DbaBackupInformation  | Select-DbaBackupInformation | Format-DbaBackupInformation | Test-DbaBackupInformation | Invoke-DbaAdvancedRestore\n\nWe have exposed these functions publicly to allow advanced users to perform operations that we don\u0027t support, or won\u0027t add as they would make things too complex for the majority of our users\n\nFor example if you wanted to do some very complex redirection during a migration, then doing the rewrite of destinations may be better done with your own custom scripts rather than via Format-DbaBackupInformation\n\nWe would recommend ALWAYS pushing your input through Test-DbaBackupInformation just to make sure that it makes sense to us.",
        "Tags":  [
                     "Restore",
                     "Backup"
                 ],
        "Synopsis":  "Allows the restore of modified BackupHistory Objects\nFor 90% of users Restore-DbaDatabase should be your point of access to this function. The other 10% use it at their own risk",
        "Name":  "Invoke-DbaAdvancedRestore",
        "Links":  "https://dbatools.io/Invoke-DbaAdvancedRestore",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003e$BackupHistory | Invoke-DbaAdvancedRestore -SqlInstance MyInstance\r\n\r\nWill restore all the backups in the BackupHistory object according to the transformations it contains\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e$BackupHistory | Invoke-DbaAdvancedRestore -SqlInstance MyInstance -OutputScriptOnly\r\n\r\n$BackupHistory | Invoke-DbaAdvancedRestore -SqlInstance MyInstance\r\n\r\nFirst generates just the T-SQL restore scripts so they can be sanity checked, and then if they are good perform the \r\nfull restore. By reusing the BackupHistory object there is no need to rescan all the backup files again\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Invoke-DbaBalanceDataFiles",
        "Description":  "When you have a large database with a single data file and add another file, SQL Server will only use the new file until it\u0027s about the same size.\nYou may want to balance the data between all the data files.\n\nThe function will check the server version and edition to see if the it allows for online index rebuilds.\nIf the server does support it, it will try to rebuild the index online.\nIf the server doesn\u0027t support it, it will rebuild the index offline. Be carefull though, this can cause downtime\n\nThe tables must have a clustered index to be able to balance out the data.\nThe function does NOT yet support heaps.\n\nThe function will also check if the file groups are subject to balance out.\nA file group whould have at least have 2 data files and should be writable.\nIf a table is within such a file group it will be subject for processing. If not the table will be skipped.",
        "Tags":  [
                     "Database",
                     "FileManagement",
                     "File",
                     "Space"
                 ],
        "Author":  "Sander Stad (@sqlstad, sqlstad.nl)",
        "Synopsis":  "Re-balance data between data files",
        "Name":  "Invoke-DbaBalanceDataFiles",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaBalanceDataFiles -SqlInstance sql1 -Database db1\r\n\r\nThis command will distribute the data in database db1 on instance sql1\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaBalanceDataFiles -SqlInstance sql1 -Database db1 | Select-Object -ExpandProperty DataFilesEnd\r\n\r\nThis command will distribute the data in database db1 on instance sql1\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaBalanceDataFiles -SqlInstance sql1 -Database db1 -Table table1,table2,table5\r\n\r\nThis command will distribute the data for only the tables table1,table2 and table5\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaBalanceDataFiles -SqlInstance sql1 -Database db1 -RebuildOffline\r\n\r\nThis command will consider the fact that there might be a SQL Server edition that does not support online rebuilds of \r\nindexes.\r\nBy supplying this parameter you give permission to do the rebuilds offline if the edition does not support it.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Invoke-DbaCycleErrorLog",
        "Description":  "Cycles the current error log for the instance (SQL Server) and/or SQL Server Agent.",
        "Tags":  [
                     "Log",
                     "Cycle"
                 ],
        "Author":  "Shawn Melton (@wsmelton | http://blog.wsmelton.info)",
        "Synopsis":  "Cycles the current instance or agent log.",
        "Name":  "Invoke-DbaCycleErrorLog",
        "Links":  "https://dbatools.io/Invoke-DbaCycleLog",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaCycleLog -SqlInstance sql2016 -Type agent\r\n\r\nCycles the current error log for the SQL Server Agent on SQL Server instance sql2016\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaCycleLog -SqlInstance sql2016 -Type instance\r\n\r\nCycles the current error log for the SQL Server instance on SQL Server instance sql2016\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaCycleLog -SqlInstance sql2016\r\n\r\nCycles the current error log for both SQL Server instance and SQL Server Agent on SQL Server instance sql2016\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Invoke-DbaDatabaseClone",
        "Description":  "Clones a database schema and statistics.\n\nThis can be useful for testing query performance without requiring all the space needed for the data in the database.\n\nRead more at sqlperformance: https://sqlperformance.com/2016/08/sql-statistics/expanding-dbcc-clonedatabase\n\nThanks to Microsoft Tiger Team for the code and idea https://github.com/Microsoft/tigertoolbox/",
        "Tags":  [
                     "Statistics",
                     "Performance"
                 ],
        "Synopsis":  "Clones a database schema and statistics",
        "Name":  "Invoke-DbaDatabaseClone",
        "Links":  "https://dbatools.io/Invoke-DbaDatabaseClone",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaDatabaseClone -SqlInstance sql2016 -Database mydb -CloneDatabase myclone\r\n\r\nClones mydb to myclone on sql2016\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaDatabaseClone -SqlInstance sql2016 -Database mydb -CloneDatabase myclone, myclone2 -UpdateStatistics\r\n\r\nUpdates the statistics of mydb then clones to myclone and myclone2\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Invoke-DbaDatabaseShrink",
        "Description":  "Shrinks all files in a database. Databases should be shrunk only when completely necessary.\n\nMany awesome SQL people have written about why you should not shrink your data files. Paul Randal and Kalen Delaney wrote great posts about this topic:\n\n    http://www.sqlskills.com/blogs/paul/why-you-should-not-shrink-your-data-files\n    http://sqlmag.com/sql-server/shrinking-data-files\n\nHowever, there are some cases where a database will need to be shrunk. In the event that you must shrink your database:\n\n1. Ensure you have plenty of space for your T-Log to grow\n2. Understand that shrinks require a lot of CPU and disk resources\n3. Consider running DBCC INDEXDEFRAG or ALTER INDEX ... REORGANIZE after the shrink is complete.",
        "Tags":  [
                     "Shrink",
                     "Database"
                 ],
        "Synopsis":  "Shrinks all files in a database. This is a command that should rarely be used.\n\n- Shrinks can cause severe index fragmentation (to the tune of 99%)\n- Shrinks can cause massive growth in the database\u0027s transaction log\n- Shrinks can require a lot of time and system resources to perform data movement",
        "Name":  "Invoke-DbaDatabaseShrink",
        "Links":  "https://dbatools.io/Invoke-DbaDatabaseShrink",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaDatabaseShrink -SqlInstance sql2016 -Database Northwind,pubs,Adventureworks2014\r\n\r\nShrinks Northwind, pubs and Adventureworks2014 to have as little free space as possible.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaDatabaseShrink -SqlInstance sql2014 -Database AdventureWorks2014 -PercentFreeSpace 50\r\n\r\nShrinks AdventureWorks2014 to have 50% free space. So let\u0027s say AdventureWorks2014 was 1GB and it\u0027s using 100MB space. \r\nThe database free space would be reduced to 50MB.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaDatabaseShrink -SqlInstance sql2012 -AllUserDatabases\r\n\r\nShrinks all databases on SQL2012 (not ideal for production)\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Invoke-DbaDatabaseUpgrade",
        "Description":  "Updates compatibility level, then runs CHECKDB with data_purity, DBCC updateusage, sp_updatestats and finally sp_refreshview against all user views.",
        "Tags":  [
                     "Shrink",
                     "Database"
                 ],
        "Author":  "Stephen Bennett, https://sqlnotesfromtheunderground.wordpress.com/",
        "Synopsis":  "Take a database and upgrades it to compatibility of the SQL Instance its hosted on. Based on https://thomaslarock.com/2014/06/upgrading-to-sql-server-2014-a-dozen-things-to-check/",
        "Name":  "Invoke-DbaDatabaseUpgrade",
        "Links":  "https://dbatools.io/Invoke-DbaDatabaseUpgrade",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaDatabaseUpgrade -SqlInstance PRD-SQL-MSD01 -Database Test\r\n\r\nRuns the below processes against the databases\r\n-- Puts compatibility of database to level of SQL Instance\r\n-- Runs CHECKDB DATA_PURITY\r\n-- Runs DBCC UPDATESUSAGE\r\n-- Updates all users statistics\r\n-- Runs sp_refreshview against every view in the database\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaDatabaseUpgrade -SqlInstance PRD-SQL-INT01 -Database Test -NoRefreshView\r\n\r\nRuns the upgrade command skipping the sp_refreshview update on all views\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaDatabaseUpgrade -SqlInstance PRD-SQL-INT01 -Database Test -Force\r\n\r\nIf database Test is already at the correct compatibility, runs every necessary step\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabase -SqlInstance sql2016 | Out-GridView -Passthru | Invoke-DbaDatabaseUpgrade\r\n\r\nGet only specific databases using GridView and pass those to Invoke-DbaDatabaseUpgrade\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Invoke-DbaDbDecryptObject",
        "Description":  "When a procedure or a function is created with encryption and you lost the code you\u0027re in trouble.\nYou cannot alter the object or view the definition.\nWith this command you can search for the object and decrypt the it.\n\nThe command will output the results to the console.\nThere is an option to export all the results to a folder creating .sql files.\n\nMake sure the instance allowed dedicated administrator connections (DAC).\nThe binary versions of the objects can only be retrieved using a DAC connection.\nYou can check the DAC connection with:\n\u0027Get-DbaSpConfigure -SqlInstance [yourinstance] -ConfigName RemoteDacConnectionsEnabled\u0027\nIt should say 1 in the ConfiguredValue.\n\nTo change the configurations you can use the Set-DbaSpConfigure command:\n\u0027Set-DbaSpConfigure -SqlInstance [yourinstance] -ConfigName RemoteDacConnectionsEnabled -Value 1\u0027\nIn some cases you may need to reboot the instance.",
        "Tags":  [
                     "Encryption",
                     "Decrypt",
                     "Database"
                 ],
        "Author":  "Sander Stad (@sqlstad, sqlstad.nl)",
        "Synopsis":  "Invoke-DbaDbDecryptObject returns the decrypted version of an object",
        "Name":  "Invoke-DbaDbDecryptObject",
        "Links":  "https://dbatools.io/Invoke-DbaDbDecryptObject",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaDbDecryptObject -SqlInstance SQLDB1 -Database DB1 -ObjectName Function1\r\n\r\nDecrypt object \"Function1\" in DB1 of instance SQLDB1 and output the data to the user.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaDbDecryptObject -SqlInstance SQLDB1 -Database DB1 -ObjectName Function1 -ExportDestination \r\nC:\\temp\\decrypt\r\n\r\nDecrypt object \"Function1\" in DB1 of instance SQLDB1 and output the data to the folder \"C:\\temp\\decrypt\".\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaDbDecryptObject -SqlInstance SQLDB1 -Database DB1 -ExportDestination C:\\temp\\decrypt\r\n\r\nDecrypt all objects in DB1 of instance SQLDB1 and output the data to the folder \"C:\\temp\\decrypt\"\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaDbDecryptObject -SqlInstance SQLDB1 -Database DB1 -ObjectName Function1, Function2\r\n\r\nDecrypt objects \"Function1\" and \"Function2\" and output the data to the user.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003e\"SQLDB1\" | Invoke-DbaDbDecryptObject -Database DB1 -ObjectName Function1, Function2\r\n\r\nDecrypt objects \"Function1\" and \"Function2\" and output the data to the user using a pipeline for the instance.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Invoke-DbaDiagnosticQuery",
        "Description":  "This is the main function of the Sql Server Diagnostic Queries related functions in dbatools.\nThe diagnostic queries are developed and maintained by Glenn Berry and they can be found here along with a lot of documentation:\nhttp://www.sqlskills.com/blogs/glenn/category/dmv-queries/\n\nThe most recent version of the diagnostic queries are included in the dbatools module.\nBut it is possible to download a newer set or a specific version to an alternative location and parse and run those scripts.\nIt will run all or a selection of those scripts on one or multiple servers and return the result as a PowerShell Object",
        "Tags":  [
                     "Database",
                     "DMV"
                 ],
        "Author":  "André Kamman (@AndreKamman), http://clouddba.io",
        "Synopsis":  "Invoke-DbaDiagnosticQuery runs the scripts provided by Glenn Berry\u0027s DMV scripts on specified servers",
        "Name":  "Invoke-DbaDiagnosticQuery",
        "Links":  "https://dbatools.io/Invoke-DbaDiagnosticQuery",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaDiagnosticQuery -SqlInstance sql2016\r\n\r\nRun the selection made by the user on the Sql Server instance specified.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaDiagnosticQuery -SqlInstance sql2016 -UseSelectionHelper | Export-DbaDiagnosticQuery -Path \r\nC:\\temp\\gboutput\r\n\r\nProvides a gridview with all the queries to choose from and will run the selection made by the user on the SQL Server \r\ninstance specified.\r\nThen it will export the results to Export-DbaDiagnosticQuery.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003e# Exporting Queries To SQL Files\r\n\r\n- Parse the appropriate diagnostic queries by connecting to server to get version matched queries\r\n- Instead of running the diagnostic queries, export them to SQL files\r\n\r\n# Export All Queries to Disk\r\nInvoke-DbaDiagnosticQuery -sqlinstance localhost -ExportQueries -outputpath \"C:\\temp\\DiagnosticQueries\"\r\n\r\n# Export Database Specific Queries for all User Dbs\r\nInvoke-DbaDiagnosticQuery -sqlinstance localhost -DatabaseSpecific -DatabaseName \u0027tempdb\u0027 -ExportQueries -outputpath \r\n\"C:\\temp\\DiagnosticQueries\"\r\n\r\n# Export Database Specific Queries For One Target Database\r\nInvoke-DbaDiagnosticQuery -sqlinstance localhost -DatabaseSpecific -DatabaseName \u0027tempdb\u0027 -ExportQueries -outputpath \r\n\"C:\\temp\\DiagnosticQueries\"\r\n\r\n# Export Database Specific Queries For One Target Database and One Specific Query\r\nInvoke-DbaDiagnosticQuery -sqlinstance localhost -DatabaseSpecific -DatabaseName \u0027tempdb\u0027 -ExportQueries -outputpath \r\n\"C:\\temp\\DiagnosticQueries\" -queryname \u0027Database-scoped Configurations\u0027\r\n\r\n# Choose Queries To Export\r\nInvoke-DbaDiagnosticQuery -sqlinstance localhost -UseSelectionHelper\r\n\r\nThis will export with SqlInstance, DatabaseName, and QueryName as appropriate based on query.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003e# Export Queries (not run) as returned object\r\n\r\n[System.Management.Automation.PSObject[]]$results = Invoke-DbaDiagnosticQuery -SqlInstance localhost -whatif\r\n\r\nParse the appropriate diagnostic queries by connecting to server, and instead of running them, return as \r\n[pscustomobject[]] to work with further\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003e# Database Specific Handling\r\n\r\n$results = Invoke-DbaDiagnosticQuery -SqlInstance $script:instance2 -DatabaseSpecific -queryname \u0027Database-scoped \r\nConfigurations\u0027 -databasename $database\r\n\r\nRun diagnostic queries targeted at specific database, and only run database level queries against this database.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Invoke-DbaLogShipping",
        "Description":  "Invoke-DbaLogShipping helps to easily set up log shipping for one or more databases.\n\nThis function will make a lot of decisions for you assuming you want default values like a daily interval for the schedules with a 15 minute interval on the day.\nThere are some settings that cannot be made by the function and they need to be prepared before the function is executed.\n\nThe following settings need to be made before log shipping can be initiated:\n- Backup destination (the folder and the privileges)\n- Copy destination (the folder and the privileges)\n\n* Privileges\nMake sure your agent service on both the primary and the secondary instance is an Active Directory account.\nAlso have the credentials ready to set the folder permissions\n\n** Network share\nThe backup destination needs to be shared and have the share privileges of FULL CONTROL to Everyone.\n\n** NTFS permissions\nThe backup destination must have at least read/write permissions for the primary instance agent account.\nThe backup destination must have at least read permissions for the secondary instance agent account.\nThe copy destination must have at least read/write permission for the secondary instance agent acount.",
        "Tags":  "LogShipping",
        "Author":  "Sander Stad (@sqlstad, sqlstad.nl)",
        "Synopsis":  "Invoke-DbaLogShipping sets up log shipping for one or more databases",
        "Name":  "Invoke-DbaLogShipping",
        "Links":  "https://dbatools.io/Invoke-DbaLogShipping",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003e$params = @{\r\n\r\nSourceSqlInstance = \u0027sql1\u0027\r\n    DestinationSqlInstance = \u0027sql2\u0027\r\n    Database = \u0027db1\u0027\r\n    BackupNetworkPath= \u0027\\\\sql1\\logshipping\u0027\r\n    BackupLocalPath= \u0027D:\\Data\\logshipping\u0027\r\n    BackupScheduleFrequencyType = \u0027daily\u0027\r\n    BackupScheduleFrequencyInterval = 1\r\n    CompressBackup = $true\r\n    CopyScheduleFrequencyType = \u0027daily\u0027\r\n    CopyScheduleFrequencyInterval = 1\r\n    GenerateFullBackup = $true\r\n    RestoreScheduleFrequencyType = \u0027daily\u0027\r\n    RestoreScheduleFrequencyInterval = 1\r\n    SecondaryDatabaseSuffix = \u0027DR\u0027\r\n    CopyDestinationFolder = \u0027\\\\sql2\\logshippingdest\u0027\r\n    Force = $true\r\n}\r\n\r\nInvoke-DbaLogShipping @params\r\n\r\nSets up log shipping for database \"db1\" with the backup path to a network share allowing local backups.\r\nIt creates daily schedules for the backup, copy and restore job with all the defaults to be executed every 15 minutes \r\ndaily.\r\nThe secondary database will be called \"db1_LS\".\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e$params = @{\r\n\r\nSourceSqlInstance = \u0027sql1\u0027\r\n    DestinationSqlInstance = \u0027sql2\u0027\r\n    Database = \u0027db1\u0027\r\n    BackupNetworkPath= \u0027\\\\sql1\\logshipping\u0027\r\n    GenerateFullBackup = $true\r\n    Force = $true\r\n}\r\n\r\nInvoke-DbaLogShipping @params\r\n\r\nSets up log shipping with all defaults except that a backup file is generated.\r\nThe script will show a message that the copy destination has not been supplied and asks if you want to use the default \r\nwhich would be the backup directory of the secondary server with the folder \"logshipping\" i.e. \r\n\"D:\\SQLBackup\\Logshiping\".\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Invoke-DbaLogShippingRecovery",
        "Description":  "By default all the databases for a particular instance are recovered.\nIf the database is in the right state, either standby or recovering, the process will try to recover the database.\n\nAt first the function will check if the backup source directory can still be reached.\nIf so it will look up the last transaction log backup for the database. If that backup file is not the last copied file the log shipping copy job will be started.\nIf the directory cannot be reached for the function will continue to the restoring process.\nAfter the copy job check is performed the job is disabled to prevent the job to run.\n\nFor the restore the log shipping status is checked in the msdb database.\nIf the last restored file is not the same as the last file name found, the log shipping restore job will be executed.\nAfter the restore job check is performed the job is disabled to prevent the job to run\n\nThe last part is to set the database online by restoring the databases with recovery",
        "Tags":  "LogShipping",
        "Author":  "Sander Stad (@sqlstad, sqlstad.nl)",
        "Synopsis":  "Invoke-DbaLogShippingRecovery recovers log shipped databases to a normal state to act upon a migration or disaster.",
        "Name":  "Invoke-DbaLogShippingRecovery",
        "Links":  "https://dbatools.io/Invoke-DbaLogShippingRecovery",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaLogShippingRecovery -SqlServer server1\r\n\r\nRecovers all the databases on the instance that are enabled for log shipping\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaLogShippingRecovery -SqlServer server1 -SqlCredential $cred -Verbose\r\n\r\nRecovers all the databases on the instance that are enabled for log shipping using a credential\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaLogShippingRecovery -SqlServer server1 -database db_logship -Verbose\r\n\r\nRecovers the database \"db_logship\" to a normal status\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003edb1, db2, db3, db4 | Invoke-DbaLogShippingRecovery -SqlServer server1 -Verbose\r\n\r\nRecovers the database db1, db2, db3, db4 to a normal status\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaLogShippingRecovery -SqlServer server1 -WhatIf\r\n\r\nShows what would happen if the command were executed.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Invoke-DbaPfRelog",
        "Description":  "Pipeline-compatible wrapper for the relog command. Relog is useful for converting Windows Perfmon.\n\nExtracts performance counters from performance counter logs into other formats,\nsuch as text-TSV (for tab-delimited text), text-CSV (for comma-delimited text), binary-BIN, or SQL.\n\nrelog \"C:\\PerfLogs\\Admin\\System Correlation\\WORKSTATIONX_20180112-000001\\DataCollector01.blg\" -o C:\\temp\\foo.csv -f tsv\n\nIf you find any input hangs, please send us the output so we can accommodate for it then use -Raw for an immediate solution.",
        "Tags":  [
                     "Performance",
                     "DataCollector",
                     "PerfCounter",
                     "Relog"
                 ],
        "Synopsis":  "Pipeline-compatible wrapper for the relog command which is available on modern Windows platforms.",
        "Name":  "Invoke-DbaPfRelog",
        "Links":  "https://dbatools.io/Invoke-DbaPfRelog",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaPfRelog -Path C:\\temp\\perfmon.blg\r\n\r\nCreates C:\\temp\\perfmon.tsv from C:\\temp\\perfmon.blg.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaPfRelog -Path C:\\temp\\perfmon.blg -Destination C:\\temp\\a\\b\\c\r\n\r\nCreates the temp, a, and b directories if needed, then generates c.tsv (tab separated) from C:\\temp\\perfmon.blg.\r\n\r\nReturns the newly created file as a file object.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfDataCollectorSet -ComputerName sql2016 | Get-DbaPfDataCollector | Invoke-DbaPfRelog -Destination \r\nC:\\temp\\perf\r\n\r\nCreates C:\\temp\\perf if needed, then generates computername-datacollectorname.tsv (tab separated) from the latest logs \r\nof all data collector sets on sql2016. This destination format was chosen to avoid naming conflicts with piped input.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaPfRelog -Path C:\\temp\\perfmon.blg -Destination C:\\temp\\a\\b\\c -Raw\r\n\r\nCreates the temp, a, and b directories if needed, then generates c.tsv (tab separated) from C:\\temp\\perfmon.blg then \r\noutputs the raw results of the relog command.\r\n\r\n[Invoke-DbaPfRelog][21:21:35] relog \"C:\\temp\\perfmon.blg\" -f csv -o C:\\temp\\a\\b\\c\r\n\r\nInput\r\n----------------\r\nFile(s):\r\n    C:\\temp\\perfmon.blg (Binary)\r\n\r\nBegin:    1/13/2018 5:13:23\r\nEnd:      1/13/2018 14:29:55\r\nSamples:  2227\r\n\r\n100.00%\r\n\r\nOutput\r\n----------------\r\nFile:     C:\\temp\\a\\b\\c.csv\r\n\r\nBegin:    1/13/2018 5:13:23\r\nEnd:      1/13/2018 14:29:55\r\nSamples:  2227\r\n\r\nThe command completed successfully.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaPfRelog -Path \u0027C:\\temp\\perflog with spaces.blg\u0027 -Destination C:\\temp\\a\\b\\c -Type csv -BeginTime \r\n((Get-Date).AddDays(-30)) -EndTime ((Get-Date).AddDays(-1))\r\n\r\nCreates the temp, a, and b directories if needed, then generates c.csv (comma separated) from C:\\temp\\perflog with \r\nspaces.blg\u0027, starts 30 days ago and ends one day ago.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003e$servers | Get-DbaPfDataCollectorSet | Get-DbaPfDataCollector | Invoke-DbaPfRelog -Multithread -AllowClobber\r\n\r\nRelogs latest data files from all collectors within the servers listed in $servers.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 7 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfDataCollector -Collector DataCollector01 | Invoke-DbaPfRelog -AllowClobber -AllTime\r\n\r\nRelogs all the log files from the DataCollector01 on the local computer and allows overwrite.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Invoke-DbaSqlQuery",
        "Description":  "This function is a wrapper command around Invoke-DbaSqlAsync, which in turn is based on Invoke-SqlCmd2.\nIt was designed to be more convenient to use in a pipeline and to behave in a way consistent with the rest of our functions.",
        "Tags":  [
                     "Database",
                     "Query"
                 ],
        "Author":  "Fred Winmann (@FredWeinmann)",
        "Synopsis":  "A command to run explicit T-SQL commands or files.",
        "Name":  "Invoke-DbaSqlQuery",
        "Links":  "https://dbatools.io/Invoke-DbaSqlQuery",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaSqlQuery -SqlInstance server\\instance -Query \u0027SELECT foo FROM bar\u0027\r\n\r\nRuns the sql query \u0027SELECT foo FROM bar\u0027 against the instance \u0027server\\instance\u0027\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRegisteredServer -SqlInstance [SERVERNAME] -Group [GROUPNAME] | Invoke-DbaSqlQuery -Query \u0027SELECT foo \r\nFROM bar\u0027\r\n\r\nRuns the sql query \u0027SELECT foo FROM bar\u0027 against all instances in the group [GROUPNAME] on the CMS [SERVERNAME]\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003e\"server1\", \"server1\\nordwind\", \"server2\" | Invoke-DbaSqlQuery -File \"C:\\scripts\\sql\\rebuild.sql\"\r\n\r\nRuns the sql commands stored in rebuild.sql against the instances \"server1\", \"server1\\nordwind\" and \"server2\"\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabase -SqlInstance \"server1\", \"server1\\nordwind\", \"server2\" | Invoke-DbaSqlQuery -File \r\n\"C:\\scripts\\sql\\rebuild.sql\"\r\n\r\nRuns the sql commands stored in rebuild.sql against all accessible databases of the instances \"server1\", \r\n\"server1\\nordwind\" and \"server2\"\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Invoke-DbaWhoIsActive",
        "Description":  "Output results of Adam Machanic\u0027s sp_WhoIsActive\n\nThis command was built with Adam\u0027s permission. To read more about sp_WhoIsActive, please visit:\n\nUpdates: http://sqlblog.com/blogs/adam_machanic/archive/tags/who+is+active/default.aspx\n\nAlso, consider donating to Adam if you find this stored procedure helpful: http://tinyurl.com/WhoIsActiveDonate",
        "Tags":  [
                     "AdamMechanic",
                     "WhoIsActive",
                     "SpWhoIsActive"
                 ],
        "Synopsis":  "Outputs results of Adam Machanic\u0027s sp_WhoIsActive DataTable",
        "Name":  "Invoke-DbaWhoIsActive",
        "Links":  "https://dbatools.io/Invoke-DbaWhoIsActive",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaWhoIsActive -SqlInstance sqlserver2014a\r\n\r\nExecute sp_whoisactive on sqlserver2014a. This command expects sp_WhoIsActive to be in the master database. Logs into \r\nthe SQL Server with Windows credentials.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaWhoIsActive -SqlInstance sqlserver2014a -SqlCredential $credential -Database dbatools\r\n\r\nExecute sp_whoisactive on sqlserver2014a. This command expects sp_WhoIsActive to be in the dbatools database. Logs into \r\nthe SQL Server with SQL Authentication.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaWhoIsActive -SqlInstance sqlserver2014a -GetAverageTime\r\n\r\nSimilar to running sp_WhoIsActive @get_avg_time\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaWhoIsActive -SqlInstance sqlserver2014a -GetOuterCommand -FindBlockLeaders\r\n\r\nSimilar to running sp_WhoIsActive @get_outer_command = 1, @find_block_leaders = 1\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Invoke-DbaXeReplay",
        "Description":  "This command replays events from Read-DbaXEFile. It is simplistic in its approach.\n\n- Writes all queries to a temp sql file\n- Executes temp file using . $sqlcmd so that batches are executed properly\n- Deletes temp file",
        "Tags":  [
                     "ExtendedEvent",
                     "XE",
                     "XEvent"
                 ],
        "Synopsis":  "This command replays events from Read-DbaXEFile on one or more target servers",
        "Name":  "Invoke-DbaXeReplay",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRead-DbaXEFile -Path C:\\temp\\sample.xel | Invoke-DbaXeReplay -SqlInstance sql2017\r\n\r\nRuns all batch_text for sql_batch_completed against tempdb on sql2017.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eRead-DbaXEFile -Path C:\\temp\\sample.xel | Invoke-DbaXeReplay -SqlInstance sql2017 -Database planning -Event \r\nsql_batch_completed\r\n\r\nSets the *initial* database to planning then runs only sql_batch_completed against sql2017.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eRead-DbaXEFile -Path C:\\temp\\sample.xel | Invoke-DbaXeReplay -SqlInstance sql2017, sql2016\r\n\r\nRuns all batch_text for sql_batch_completed against tempdb on sql2017 and sql2016.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Invoke-Sqlcmd2",
        "Description":  "Runs a T-SQL script. Invoke-Sqlcmd2 runs the whole script and only captures the first selected result set, such as the output of PRINT statements when -verbose parameter is specified.\nParameterized queries are supported.\n\nHelp details below borrowed from Invoke-Sqlcmd",
        "Synopsis":  "Runs a T-SQL script.",
        "Name":  "Invoke-Sqlcmd2",
        "Links":  [
                      "https://github.com/sqlcollaborative/Invoke-SqlCmd2",
                      "https://github.com/RamblingCookieMonster/PowerShell"
                  ],
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eInvoke-Sqlcmd2 -ServerInstance \"MyComputer\\MyInstance\" -Query \"SELECT login_time AS \u0027StartTime\u0027 FROM \r\nsysprocesses WHERE spid = 1\"\r\n\r\nConnects to a named instance of the Database Engine on a computer and runs a basic T-SQL query.\r\n\r\nStartTime\r\n-----------\r\n2010-08-12 21:21:03.593\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eInvoke-Sqlcmd2 -ServerInstance \"MyComputer\\MyInstance\" -InputFile \"C:\\MyFolder\\tsqlscript.sql\" | Out-File \r\n-filePath \"C:\\MyFolder\\tsqlscript.rpt\"\r\n\r\nReads a file containing T-SQL statements, runs the file, and writes the output to another file.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eInvoke-Sqlcmd2  -ServerInstance \"MyComputer\\MyInstance\" -Query \"PRINT \u0027hello world\u0027\" -Verbose\r\n\r\nUses the PowerShell -Verbose parameter to return the message output of the PRINT command.\r\nVERBOSE: hello world\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eInvoke-Sqlcmd2 -ServerInstance MyServer\\MyInstance -Query \"SELECT ServerName, VCNumCPU FROM tblServerInfo\" -as \r\nPSObject | ?{$_.VCNumCPU -gt 8}\r\n\r\nInvoke-Sqlcmd2 -ServerInstance MyServer\\MyInstance -Query \"SELECT ServerName, VCNumCPU FROM tblServerInfo\" -as PSObject \r\n| ?{$_.VCNumCPU}\r\n\r\nThis example uses the PSObject output type to allow more flexibility when working with results.\r\n\r\nIf we used DataRow rather than PSObject, we would see the following behavior:\r\n    Each row where VCNumCPU does not exist would produce an error in the first example\r\n    Results would include rows where VCNumCPU has DBNull value in the second example\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003e\u0027Instance1\u0027, \u0027Server1/Instance1\u0027, \u0027Server2\u0027 | Invoke-Sqlcmd2 -query \"Sp_databases\" -as psobject \r\n-AppendServerInstance\r\n\r\nThis example lists databases for each instance.  It includes a column for the ServerInstance in question.\r\n    DATABASE_NAME          DATABASE_SIZE REMARKS        ServerInstance\r\n    -------------          ------------- -------        --------------\r\n    REDACTED                       88320                Instance1\r\n    master                         17920                Instance1\r\n    ...\r\n    msdb                          618112                Server1/Instance1\r\n    tempdb                        563200                Server1/Instance1\r\n    ...\r\n    OperationsManager           20480000                Server2\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003e#Construct a query using SQL parameters\r\n\r\n$Query = \"SELECT ServerName, VCServerClass, VCServerContact FROM tblServerInfo WHERE VCServerContact LIKE \r\n@VCServerContact AND VCServerClass LIKE @VCServerClass\"\r\n\r\n#Run the query, specifying values for SQL parameters\r\n    Invoke-Sqlcmd2 -ServerInstance SomeServer\\NamedInstance -Database ServerDB -query $query -SqlParameters @{ \r\nVCServerContact=\"%cookiemonster%\"; VCServerClass=\"Prod\" }\r\n\r\n    ServerName    VCServerClass VCServerContact\r\n    ----------    ------------- ---------------\r\n    SomeServer1   Prod          cookiemonster, blah\r\n    SomeServer2   Prod          cookiemonster\r\n    SomeServer3   Prod          blah, cookiemonster\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 7 --------------------------\r\n\r\nPS C:\\\u003eInvoke-Sqlcmd2 -SQLConnection $Conn -Query \"SELECT login_time AS \u0027StartTime\u0027 FROM sysprocesses WHERE spid = 1\"\r\n\r\nUses an existing SQLConnection and runs a basic T-SQL query against it\r\n\r\nStartTime\r\n-----------\r\n2010-08-12 21:21:03.593\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 8 --------------------------\r\n\r\nPS C:\\\u003eInvoke-SqlCmd -SQLConnection $Conn -Query \"SELECT ServerName FROM tblServerInfo WHERE ServerName LIKE \r\n@ServerName\" -SqlParameters @{\"ServerName = \"c-is-hyperv-1\"}\r\n\r\nExecutes a parameterized query against the existing SQLConnection, with a collection of one parameter to be passed to \r\nthe query when executed.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Measure-DbaBackupThroughput",
        "Description":  "Returns backup history details for one or more databases on a SQL Server.\n\nOutput looks like this:\nSqlInstance     : sql2016\nDatabase        : SharePoint_Config\nAvgThroughputMB : 1.07\nAvgSizeMB       : 24.17\nAvgDuration     : 00:00:01.1000000\nMinThroughputMB : 0.02\nMaxThroughputMB : 2.26\nMinBackupDate   : 8/6/2015 10:22:01 PM\nMaxBackupDate   : 6/19/2016 12:57:45 PM\nBackupCount     : 10",
        "Tags":  [
                     "Backup",
                     "Database"
                 ],
        "Synopsis":  "Determines how quickly SQL Server is backing up databases to media.",
        "Name":  "Measure-DbaBackupThroughput",
        "Links":  "https://dbatools.io/Measure-DbaBackupThroughput",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eMeasure-DbaBackupThroughput -SqlInstance sql2016\r\n\r\nParses every backup in msdb\u0027s backuphistory for stats on all databases.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eMeasure-DbaBackupThroughput -SqlInstance sql2016 -Database AdventureWorks2014\r\n\r\nParses every backup in msdb\u0027s backuphistory for stats on AdventureWorks2014.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eMeasure-DbaBackupThroughput -SqlInstance sql2005 -Last\r\n\r\nProcesses the last full, diff and log backups every backup for all databases on sql2005.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eMeasure-DbaBackupThroughput -SqlInstance sql2005 -Last -Type Log\r\n\r\nProcesses the last log backups every backup for all databases on sql2005.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eMeasure-DbaBackupThroughput -SqlInstance sql2016 -Since (Get-Date).AddDays(-7)\r\n\r\nGets backup calculations for the last week.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eMeasure-DbaBackupThroughput -SqlInstance sql2016 -Since (Get-Date).AddDays(-365) -Database bigoldb\r\n\r\nGets backup calculations, limited to the last year and only the bigoldb database\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Measure-DbaDiskSpaceRequirement",
        "Description":  "Returns a file list from source and destination where source file may overwrite destination. Complex scenarios where a new file may exist is taken into account.\nThis command will accept a hash object in pipeline with the following keys: Source, SourceDatabase, Destination. Using this command will provide a way to prepare before a complex migration with multiple databases from different sources and destinations.",
        "Tags":  [
                     "Database",
                     "DiskSpace",
                     "Migration"
                 ],
        "Author":  "Pollus Brodeur (@pollusb)",
        "Synopsis":  "Calculate the space needed to copy and possibly replace a database from one SQL server to another.",
        "Name":  "Measure-DbaDiskSpaceRequirement",
        "Links":  "https://dbatools.io/Measure-DbaDiskSpaceRequirement",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eMeasure-DbaDiskSpaceRequirement -Source INSTANCE1 -Database DB1 -Destination INSTANCE2\r\n\r\nCalculate space needed for a simple migration with one database with the same name at destination.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e@([PSCustomObject]@{Source=\u0027SQL1\u0027;Destination=\u0027SQL2\u0027;Database=\u0027DB1\u0027},\r\n\r\n[PSCustomObject]@{Source=\u0027SQL1\u0027;Destination=\u0027SQL2\u0027;Database=\u0027DB2\u0027}\r\n) | Measure-DbaDiskSpaceRequirement\r\n\r\nUsing a PSCustomObject with 2 databases to migrate on SQL2.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eImport-Csv -Path .\\migration.csv -Delimiter \"`t\" | Measure-DbaDiskSpaceRequirement | Format-Table -AutoSize\r\n\r\nUsing a CSV file. You will need to use this header line \"Source\u003ctab\u003eDestination\u003ctab\u003eDatabase\u003ctab\u003eDestinationDatabase\".\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eInvoke-DbaSqlCmd -SqlInstance DBA -Database Migrations -Query \u0027select Source, Destination, Database from \r\ndbo.Migrations\u0027 `\r\n\r\n| Measure-DbaDiskSpaceRequirement\r\n\r\nUsing a SQL table. We are DBA after all!\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Mount-DbaDatabase",
        "Description":  "This command will attach a SQL Server database.",
        "Tags":  "Database",
        "Synopsis":  "Attach a SQL Server Database - aliased to Attach-DbaDatabase",
        "Name":  "Mount-DbaDatabase",
        "Links":  "https://dbatools.io/Mount-DbaDatabase",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003e$fileStructure = New-Object System.Collections.Specialized.StringCollection\r\n\r\n$fileStructure.Add(\"E:\\archive\\example.mdf\")\r\n$filestructure.Add(\"E:\\archive\\example.ldf\")\r\n$filestructure.Add(\"E:\\archive\\example.ndf\")\r\nMount-DbaDatabase -SqlInstance sql2016 -Database example -FileStructure $fileStructure\r\n\r\nAttaches a database named \"example\" to sql2016 with the files \"E:\\archive\\example.mdf\", \"E:\\archive\\example.ldf\" and \r\n\"E:\\archive\\example.ndf\". The database owner will be set to sa and the attach option is None.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eMount-DbaDatabase -SqlInstance sql2016 -Database example\r\n\r\nSince the FileStructure was not provided, this command will attempt to determine it based on backup history. If found, \r\na database named example will be attached to sql2016.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eMount-DbaDatabase -SqlInstance sql2016 -Database example -WhatIf\r\n\r\nShows what would happen if the command were executed (without actually performing the command)\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Move-DbaRegisteredServer",
        "Description":  "Moves registered servers around SQL Server Central Management Server (CMS)",
        "Tags":  [
                     "RegisteredServer",
                     "CMS"
                 ],
        "Author":  "Chrissy LeMaire (@cl)",
        "Synopsis":  "Moves registered servers around SQL Server Central Management Server (CMS)",
        "Name":  "Move-DbaRegisteredServer",
        "Links":  "https://dbatools.io/Move-DbaRegisteredServer",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eMove-DbaRegisteredServer -SqlInstance sql2012 -Name \u0027Web SQL Cluster\u0027 -NewGroup HR\\Prod\r\n\r\nMoves the registered server on sql2012 titled \u0027Web SQL Cluster\u0027 to the Prod group within the HR group\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eMove-DbaRegisteredServer -SqlInstance sql2012 -Group HR\\Development -NewGroup HR\\Prod\r\n\r\nMoves all servers from the HR and sub-group Development to HR Prod\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRegisteredServer -SqlInstance sql2017 -Name \u0027Web SQL Cluster\u0027 | Move-DbaRegisteredServer -NewGroup Web\r\n\r\nMoves the registered server \u0027Web SQL Cluster\u0027 on sql2017 to the Web group, also on sql2017\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Move-DbaRegisteredServerGroup",
        "Description":  "Moves registered server groups around SQL Server Central Management Server (CMS).",
        "Tags":  [
                     "RegisteredServer",
                     "CMS"
                 ],
        "Author":  "Chrissy LeMaire (@cl)",
        "Synopsis":  "Moves registered server groups around SQL Server Central Management Server (CMS).",
        "Name":  "Move-DbaRegisteredServerGroup",
        "Links":  "https://dbatools.io/Move-DbaRegisteredServerGroup",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eMove-DbaRegisteredServerGroup -SqlInstance sql2012 -Group HR\\Development -NewGroup AD\\Prod\r\n\r\nMoves the Development group within HR to the Prod group within AD\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRegisteredServerGroup -SqlInstance sql2017 -Group HR\\Development| Move-DbaRegisteredServer -NewGroup Web\r\n\r\nMoves the Development group within HR to the Web group\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "New-DbaAgentJob",
        "Description":  "New-DbaAgentJob makes is possible to create a job in the SQL Server Agent.\nIt returns an array of the job(s) created",
        "Tags":  [
                     "Agent",
                     "Job",
                     "JobStep"
                 ],
        "Author":  "Sander Stad (@sqlstad, sqlstad.nl)",
        "Synopsis":  "New-DbaAgentJob creates a new job",
        "Name":  "New-DbaAgentJob",
        "Links":  "https://dbatools.io/New-DbaAgentJob",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaAgentJob -SqlInstance sql1 -Job \u0027Job One\u0027 -Description \u0027Just another job\u0027\r\n\r\nCreates a job with the name \"Job1\" and a small description\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaAgentJob -SqlInstance sql1 -Job \u0027Job One\u0027 -Disabled\r\n\r\nCreates the job but sets it to disabled\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaAgentJob -SqlInstance sql1 -Job \u0027Job One\u0027 -EventLogLevel OnSuccess\r\n\r\nCreates the job and sets the notification to write to the Windows Application event log on success\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaAgentJob -SqlInstance SSTAD-PC -Job \u0027Job One\u0027 -EmailLevel OnFailure -EmailOperator dba\r\n\r\nCreates the job and sets the notification to send an e-mail to the e-mail operator\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaAgentJob -SqlInstance sql1 -Job \u0027Job One\u0027 -Description \u0027Just another job\u0027 -Whatif\r\n\r\nDoesn\u0027t create the job but shows what would happen.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaAgentJob -SqlInstance sql1, sql2, sql3 -Job \u0027Job One\u0027\r\n\r\nCreates a job with the name \"Job One\" on multiple servers\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 7 --------------------------\r\n\r\nPS C:\\\u003e\"sql1\", \"sql2\", \"sql3\" | New-DbaAgentJob -Job \u0027Job One\u0027\r\n\r\nCreates a job with the name \"Job One\" on multiple servers using the pipe line\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "New-DbaAgentJobCategory",
        "Description":  "New-DbaAgentJobCategory makes it possible to create a job category that can be used with jobs.\nIt returns an array of the job(s) created .",
        "Tags":  [
                     "Agent",
                     "Job",
                     "JobCategory"
                 ],
        "Author":  "Sander Stad (@sqlstad, sqlstad.nl)",
        "Synopsis":  "New-DbaAgentJobCategory creates a new job category.",
        "Name":  "New-DbaAgentJobCategory",
        "Links":  "https://dbatools.io/New-DbaAgentJobCategory",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaAgentJobCategory -SqlInstance sql1 -Category \u0027Category 1\u0027\r\n\r\nCreates a new job category with the name \u0027Category 1\u0027.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaAgentJobCategory -SqlInstance sql1 -Category \u0027Category 2\u0027 -CategoryType MultiServerJob\r\n\r\nCreates a new job category with the name \u0027Category 2\u0027 and assign the category type for a multi server job.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "New-DbaAgentJobStep",
        "Description":  "New-DbaAgentJobStep creates a new job in the SQL Server Agent for a specific job",
        "Tags":  [
                     "Agent",
                     "Job",
                     "JobStep"
                 ],
        "Author":  "Sander Stad (@sqlstad, sqlstad.nl)",
        "Synopsis":  "New-DbaAgentJobStep creates a new job step for a job",
        "Name":  "New-DbaAgentJobStep",
        "Links":  "https://dbatools.io/New-DbaAgentJobStep",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaAgentJobStep -SqlInstance sql1 -Job Job1 -StepName Step1\r\n\r\nCreate a step in \"Job1\" with the name Step1 with the default subsystem TransactSql.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaAgentJobStep -SqlInstance sql1 -Job Job1 -StepName Step1 -Database msdb\r\n\r\nCreate a step in \"Job1\" with the name Step1 where the database will the msdb\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaAgentJobStep -SqlInstance sql1, sql2, sql3 -Job Job1 -StepName Step1 -Database msdb\r\n\r\nCreate a step in \"Job1\" with the name Step1 where the database will the \"msdb\" for multiple servers\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaAgentJobStep -SqlInstance sql1, sql2, sql3 -Job Job1, Job2, \u0027Job Three\u0027 -StepName Step1 -Database msdb\r\n\r\nCreate a step in \"Job1\" with the name Step1 where the database will the \"msdb\" for multiple servers for multiple jobs\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003esql1, sql2, sql3 | New-DbaAgentJobStep -Job Job1 -StepName Step1 -Database msdb\r\n\r\nCreate a step in \"Job1\" with the name Step1 where the database will the \"msdb\" for multiple servers using pipeline\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "New-DbaAgentProxy",
        "Description":  "Adds one or more proxies to SQL Server Agent",
        "Tags":  [
                     "Agent",
                     "Proxy"
                 ],
        "Synopsis":  "Adds one or more proxies to SQL Server Agent",
        "Name":  "New-DbaAgentProxy",
        "Links":  "https://dbatools.io/New-DbaAgentProxy",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaAgentProxy -SqlInstance sql2016 -Name STIG -Credential \u0027PowerShell Proxy\u0027\r\n\r\nCreates an Agent Proxy on sql2016 with the name STIG with the \u0027PowerShell Proxy\u0027 credential.\r\nThe proxy is automatically added to the CmdExec subsystem.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaAgentProxy -SqlInstance localhost\\sql2016 -Name STIG -Credential \u0027PowerShell Proxy\u0027 -Description \"Used \r\nfor auditing purposes\" -Login ad\\sqlstig -SubSystem CmdExec, PowerShell -ServerRole securtyadmin -MsdbRole \r\nServerGroupAdministratorRole\r\n\r\nCreates an Agent Proxy on sql2016 with the name STIG with the \u0027PowerShell Proxy\u0027 credential and the following \r\nprincipals:\r\n\r\nLogin: ad\\sqlstig\r\nServerRole: securtyadmin\r\nMsdbRole: ServerGroupAdministratorRole\r\n\r\nBy default, only sysadmins have access to create job steps with proxies. This will allow 3 additional principals access:\r\nThe proxy is then added to the CmdExec and PowerShell subsystems\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "New-DbaAgentSchedule",
        "Description":  "New-DbaAgentSchedule will help create a new schedule for a job.\nIf the job parameter is not supplied the schedule will not be attached to a job.",
        "Tags":  [
                     "Agent",
                     "Job",
                     "JobStep"
                 ],
        "Author":  "Sander Stad (@sqlstad, sqlstad.nl)",
        "Synopsis":  "New-DbaAgentSchedule creates a new schedule in the msdb database.",
        "Name":  "New-DbaAgentSchedule",
        "Links":  "https://dbatools.io/New-DbaAgentSchedule",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaAgentSchedule -SqlInstance localhost\\SQL2016 -Schedule daily -FrequencyType Daily -FrequencyInterval \r\nEveryday -Force\r\n\r\nCreates a schedule with a daily frequency every day. It assumes default values for the start date, start time, end date \r\nand end time due to -Force.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaAgentSchedule -SqlInstance sstad-pc -Schedule MonthlyTest -FrequencyType Monthly -FrequencyInterval 10 \r\n-FrequencyRecurrenceFactor 1 -Force\r\n\r\nCreate a schedule with a monhtly frequency occuring every 10th of the month. It assumes default values for the start \r\ndate, start time, end date and end time due to -Force.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "New-DbaClientAlias",
        "Description":  "Creates/updates a SQL Server alias by altering HKLM:\\SOFTWARE\\Microsoft\\MSSQLServer\\Client",
        "Tags":  "Alias",
        "Synopsis":  "Creates/updates a sql alias for the specified server - mimics cliconfg.exe",
        "Name":  "New-DbaClientAlias",
        "Links":  "https://dbatools.io/New-DbaClientAlias",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaClientAlias -ServerName sqlcluster\\sharepoint -Alias sp\r\n\r\nCreates a new TCP alias on the local workstation called sp, which points sqlcluster\\sharepoint\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaClientAlias -ServerName \u0027sqlcluster,14443\u0027 -Alias spinstance\r\n\r\nCreates a new TCP alias on the local workstation called spinstance, which points to sqlcluster, port 14443.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaClientAlias -ServerName sqlcluster\\sharepoint -Alias sp -Protocol NamedPipes\r\n\r\nCreates a new NamedPipes alias on the local workstation called sp, which points sqlcluster\\sharepoint\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "New-DbaCmConnection",
        "Description":  "Generates a connection object for use in remote computer management.\nThose objects are used for the purpose of cim/wmi queries, caching which protocol worked, optimizing performance and minimizing authentication errors.\n\nNew-DbaCmConnection will create a NEW object and overwrite any existing ones for the specified computer.\nFurthermore, information stored in the input beyond the computername will be discarded in favor of the new settings.\n\nUnless the connection cache has been disabled, all connections will automatically be registered in the cache, so no further action is necessary.\nThe output is primarily for information purposes, however it may be used to pass objects and circumvent the cache with those.\n\nNOTE: Generally, this function need not be used, as a first connection to a computer using any connecting function such as \"Get-DbaCmObject\" will automatically register a new default connection for it.\n\nThis function exists to be able to preconfigure connections.",
        "Tags":  [
                     "ComputerManagement",
                     "CIM"
                 ],
        "Author":  "Fred Winmann (@FredWeinmann)",
        "Synopsis":  "Generates a connection object for use in remote computer management.",
        "Name":  "New-DbaCmConnection",
        "Links":  "https://dbatools.io/New-DbaCmConnection",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaCmConnection -ComputerName sql2014 -UseWindowsCredentials -OverrideExplicitCredential \r\n-DisabledConnectionTypes CimRM\r\n\r\nReturns a new configuration object for connecting to the computer sql2014.\r\n- The current user credentials are set as valid\r\n- The connection is configured to ignore explicit credentials (so all connections use the windows credentials)\r\n- The connections will not try using CIM over WinRM\r\n\r\nUnless caching is globally disabled, this is automatically stored in the connection cache and will be applied \r\nautomatically.\r\nIn that (the default) case, the output is for information purposes only and need not be used.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-Content computers.txt | New-DbaCmConnection -Credential $cred -CimWinRMOptions $options \r\n-DisableBadCredentialCache -OverrideExplicitCredential\r\n\r\nGathers a list of computers from a text file, then creates and registers connections for each of them, setting them to \r\n...\r\n- use the credentials stored in $cred\r\n- use the options stored in $options when connecting using CIM over WinRM\r\n- not store credentials that are known to not work\r\n- to ignore explicitly specified credentials\r\n\r\nEssentially, this configures all connections to those computers to prefer failure with the specified credentials over \r\nusing alternative credentials.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "New-DbaComputerCertificate",
        "Description":  "Creates a new computer certificate - self-signed or signed by an Active Directory CA, using the Web Server certificate.\n\nBy default, a key with a length of 1024 and a friendly name of the machines FQDN is generated.\n\nThis command was originally intended to help automate the process so that SSL certificates can be available for enforcing encryption on connections.\n\nIt makes a lot of assumptions - namely, that your account is allowed to auto-enroll and that you have permission to do everything it needs to do ;)\n\nReferences:\nhttp://sqlmag.com/sql-server/7-steps-ssl-encryption\nhttps://azurebi.jppp.org/2016/01/23/using-lets-encrypt-certificates-for-secure-sql-server-connections/\nhttps://blogs.msdn.microsoft.com/sqlserverfaq/2016/09/26/creating-and-registering-ssl-certificates/\n\nThe certificate is generated using AD\u0027s webserver SSL template on the client machine and pushed to the remote machine.",
        "Tags":  "Certificate",
        "Synopsis":  "Creates a new computer certificate useful for Forcing Encryption",
        "Name":  "New-DbaComputerCertificate",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaComputerCertificate\r\n\r\nCreates a computer certificate signed by the local domain CA for the local machine with the keylength of 1024.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaComputerCertificate -ComputerName Server1\r\n\r\nCreates a computer certificate signed by the local domain CA _on the local machine_ for server1 with the keylength of \r\n1024.\r\n\r\nThe certificate is then copied to the new machine over WinRM and imported.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaComputerCertificate -ComputerName sqla, sqlb -ClusterInstanceName sqlcluster -KeyLength 4096\r\n\r\nCreates a computer certificate for sqlcluster, signed by the local domain CA, with the keylength of 4096.\r\n\r\nThe certificate is then copied to sqla _and_ sqlb over WinRM and imported.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaComputerCertificate -ComputerName Server1 -WhatIf\r\n\r\nShows what would happen if the command were run\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaComputerCertificate -SelfSigned\r\n\r\nCreates a self-signed certificate\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "New-DbaCredential",
        "Description":  "Creates a new credential",
        "Tags":  "Certificate",
        "Synopsis":  "Creates a new SQL Server credential",
        "Name":  "New-DbaCredential",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaCredential -SqlInstance Server1\r\n\r\nYou will be prompted to securely enter your password, then a credential will be created in the master database on \r\nserver1 if it does not exist.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaCredential -SqlInstance Server1 -Database db1 -Confirm:$false\r\n\r\nSuppresses all prompts to install but prompts to securely enter your password and creates a credential in the \u0027db1\u0027 \r\ndatabase\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nNew-DbaCredential -SqlInstance Server1 -Name AzureBackupBlobStore -Identity \u0027\u003cAzure Storage Account Name\u003e\u0027 -Password \r\n(ConvertTo-SecureString \u0027\u003cAzure Storage Account Access Key\u003e\u0027 -AsPlainText -Force)\r\n\r\nCreate credential on SQL Server 2012 CU2, SQL Server 2014 for use with BACKUP TO URL.\r\nCredentialIdentity needs to be supplied with the Azure Storage Account Name.\r\nPassword needs to be one of the Access Keys for the account.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nNew-DbaCredential -SqlInstance Server1 -Name \u0027https://\u003cAzure Storage Account Name\u003e.blob.core.windows.net/\u003cBlob Store \r\nContainer Name\u003e\u0027 -Identity \u0027SHARED ACCESS SIGNATURE\u0027 -Password (ConvertTo-SecureString \u0027\u003cShared Access Token\u003e\u0027 \r\n-AsPlainText -Force)\r\n\r\nCreate Credential on SQL Server 2016 or higher for use with BACKUP TO URL.\r\nName has to be the full URL for the blob store container that will be the backup target.\r\nPassword needs to be passed the Shared Access Token (SAS Key).\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "New-DbaDatabaseMasterKey",
        "Description":  "Creates a new database master key. If no database is specified, the master key will be created in master.",
        "Tags":  "Certificate",
        "Synopsis":  "Creates a new database master key",
        "Name":  "New-DbaDatabaseMasterKey",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaDatabaseMasterKey -SqlInstance Server1\r\n\r\nYou will be prompted to securely enter your password, then a master key will be created in the master database on \r\nserver1 if it does not exist.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaDatabaseMasterKey -SqlInstance Server1 -Database db1 -Confirm:$false\r\n\r\nSuppresses all prompts to install but prompts to securely enter your password and creates a master key in the \u0027db1\u0027 \r\ndatabase\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "New-DbaDbCertificate",
        "Description":  "Creates a new database certificate. If no database is specified, the certificate will be created in master.",
        "Tags":  "Certificate",
        "Synopsis":  "Creates a new database certificate",
        "Name":  "New-DbaDbCertificate",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaDbCertificate -SqlInstance Server1\r\n\r\nYou will be prompted to securely enter your password, then a certificate will be created in the master database on \r\nserver1 if it does not exist.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaDbCertificate -SqlInstance Server1 -Database db1 -Confirm:$false\r\n\r\nSuppresses all prompts to install but prompts to securely enter your password and creates a certificate in the \u0027db1\u0027 \r\ndatabase\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "New-DbaDbSnapshot",
        "Description":  "Creates database snapshots without hassles",
        "Tags":  [
                     "Snapshot",
                     "Restore",
                     "Database"
                 ],
        "Author":  "niphlod",
        "Synopsis":  "Creates database snapshots",
        "Name":  "New-DbaDbSnapshot",
        "Links":  "https://dbatools.io/New-DbaDbSnapshot",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaDbSnapshot -SqlInstance sqlserver2014a -Database HR, Accounting\r\n\r\nCreates snapshot for HR and Accounting, returning a custom object displaying Server, Database, DatabaseCreated, \r\nSnapshotOf, SizeMB, DatabaseCreated, PrimaryFilePath, Status, Notes\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaDbSnapshot -SqlInstance sqlserver2014a -Database HR -Name HR_snap\r\n\r\nCreates snapshot named \"HR_snap\" for HR\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaDbSnapshot -SqlInstance sqlserver2014a -Database HR -NameSuffix \u0027fool_{0}_snap\u0027\r\n\r\nCreates snapshot named \"fool_HR_snap\" for HR\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaDbSnapshot -SqlInstance sqlserver2014a -Database HR, Accounting -Path F:\\snapshotpath\r\n\r\nCreates snapshots for HR and Accounting databases, storing files under the F:\\snapshotpath\\ dir\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabase -SqlInstance sql2016 -Database df | New-DbaDbSnapshot\r\n\r\nCreates a snapshot for the database df on sql2016\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "New-DbaDbUser",
        "Description":  "Creates a new user for a specified database with provided specifications.",
        "Tags":  [
                     "Database",
                     "User"
                 ],
        "Author":  "Frank Henninger (@osiris687)",
        "Synopsis":  "Creates a new user for the specified database.",
        "Name":  "New-DbaDbUser",
        "Links":  "https://dbatools.io/New-DbaDbUser",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaDbUser -SqlInstance sqlserver2014 -Database DB1 -Login user1\r\n\r\nCreates a new sql user with login named user1 in the specified database.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaDbUser -SqlInstance sqlserver2014 -Database DB1 -Username user1\r\n\r\nCreates a new sql user without login named user1 in the specified database.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaDbUser -SqlInstance sqlserver2014 -Database DB1 -Login Login1 -Username user1\r\n\r\nCreates a new sql user named user1 mapped to Login1 in the specified database.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabaseUser -SqlInstance sqlserver1 -Database DB1 | New-DbaDbUser -SqlInstance sqlserver2 -Database DB1\r\n\r\nCopies users from sqlserver1.DB1 to sqlserver2.DB1. Does not copy permissions!\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "New-DbaLogin",
        "Description":  "Creates a new SQL Server login with provided specifications",
        "Tags":  "Login",
        "Author":  "Kirill Kravtsov (@nvarscar)",
        "Synopsis":  "Creates a new SQL Server login",
        "Name":  "New-DbaLogin",
        "Links":  "https://dbatools.io/New-DbaLogin",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaLogin -SqlInstance Server1,Server2 -Login Newlogin\r\n\r\nYou will be prompted to securely enter the password for a login [Newlogin]. The login would be created on servers \r\nServer1 and Server2 with default parameters.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e$securePassword = Read-Host \"Input password\" -AsSecureString\r\n\r\nNew-DbaLogin -SqlInstance Server1\\sql1 -Login Newlogin -Password $securePassword -PasswordPolicy -PasswordExpiration\r\n\r\nCreates a login on Server1\\sql1 with a predefined password. The login will have password and expiration policies \r\nenforced onto it.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaLogin -SqlInstance sql1 -Login Oldlogin | New-DbaLogin -SqlInstance sql1 -LoginRenameHashtable @{Oldlogin \r\n= \u0027Newlogin\u0027} -Force -NewSid -Disabled:$false\r\n\r\nCopies a login [Oldlogin] to the same instance sql1 with the same parameters (including password). New login will have \r\na new sid, a new name [Newlogin] and will not be disabled. Existing login [Newlogin] will be removed prior to creation.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaLogin -SqlInstance sql1 -Login Login1,Login2 | New-DbaLogin -SqlInstance sql2 -PasswordPolicy \r\n-PasswordExpiration -DefaultDatabase tempdb -Disabled\r\n\r\nCopies logins [Login1] and [Login2] from instance sql1 to instance sql2, but enforces password and expiration policies \r\nfor the new logins. New logins will also have a default database set to [tempdb] and will be created in a disabled \r\nstate.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "New-DbaPublishProfile",
        "Description":  "The New-PublishProfile command generates a standard publish profile xml file that can be used by the DacFx (this and everything else) to control the deployment of your dacpac\nThis generates a standard template XML which is enough to dpeloy a dacpac but it is highly recommended that you add additional options to the publish profile.\nIf you use Visual Studio you can open a publish.xml file and use the ui to edit the file -\nTo create a new file, right click on an SSDT project, choose \"Publish\" then \"Load Profile\" and load your profile or create a new one.\nOnce you have loaded it in Visual Studio, clicking advanced shows you the list of options available to you.\nFor a full list of options that you can add to the profile, google \"sqlpackage.exe command line switches\" or (https://msdn.microsoft.com/en-us/library/hh550080(v=vs.103).aspx)",
        "Tags":  "Dacpac",
        "Author":  "Richie lee (@bzzzt_io)",
        "Synopsis":  "Creates a new Publish Profile.",
        "Name":  "New-DbaPublishProfile",
        "Links":  "https://dbatools.io/New-DbaPublishProfile",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaPublishProfile -SqlInstance sql2017 -SqlCredential (Get-Credential) -Database WorldWideImporters -Path \r\nC:\\temp\r\n\r\nIn this example, a prompt will appear for alternative credentials, tghen a connection will be made to sql2017. Using \r\nthat connection,\r\nthe ConnectionString will be extracted and used within the Publish Profile XML file which will be created at \r\nC:\\temp\\sql2017-WorldWideImporters-publish.xml\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaPublishProfile -Database WorldWideImporters -Path C:\\temp -ConnectionString \r\n\"SERVER=(localdb)\\MSSQLLocalDB;Integrated Security=True;Database=master\"\r\n\r\nIn this example, no connections are made, and a Publish Profile XML would be created at \r\nC:\\temp\\localdb-MSSQLLocalDB-WorldWideImporters-publish.xml\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "New-DbaScriptingOption",
        "Description":  "Creates a new Microsoft.SqlServer.Management.Smo.ScriptingOptions object. Basically saves you the time from remembering the SMO assembly name ;)\n\nSee https://msdn.microsoft.com/en-us/library/microsoft.sqlserver.management.smo.scriptingoptions.aspx for more information",
        "Tags":  [
                     "Migration",
                     "Backup",
                     "DR"
                 ],
        "Synopsis":  "Creates a new Microsoft.SqlServer.Management.Smo.ScriptingOptions object",
        "Name":  "New-DbaScriptingOption",
        "Links":  "https://dbatools.io/New-DbaScriptingOption\nhttps://msdn.microsoft.com/en-us/library/microsoft.sqlserver.management.smo.scriptingoptions.aspx",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003e$options = New-DbaScriptingOption\r\n\r\n$options.ScriptDrops = $false\r\n$options.WithDependencies = $true\r\nGet-DbaAgentJob -SqlInstance sql2016 | Export-DbaScript -ScriptingOptionObject $options\r\n\r\nExports Agent Jobs with the Scripting Options ScriptDrops set to $false and WithDependencies set to true\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "New-DbaServiceMasterKey",
        "Description":  "Creates a new service master key in the master database",
        "Tags":  "Certificate",
        "Synopsis":  "Creates a new service master key",
        "Name":  "New-DbaServiceMasterKey",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaServiceMasterKey -SqlInstance Server1\r\n\r\nYou will be prompted to securely enter your Service Key Password twice, then a master key will be created in the master \r\ndatabase on server1 if it does not exist.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "New-DbaSqlConnectionString",
        "Description":  "Builds or extracts a SQL Server Connection String\n\nSee https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.connectionstring.aspx\nand https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnectionstringbuilder.aspx\nand https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.aspx",
        "Tags":  [
                     "Connection",
                     "Connect",
                     "ConnectionString"
                 ],
        "Synopsis":  "Builds or extracts a SQL Server Connection String",
        "Name":  "New-DbaSqlConnectionString",
        "Links":  "https://dbatools.io/New-DbaSqlConnectionString",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaSqlConnectionString -SqlInstance sql2014\r\n\r\nCreates a connection string that connects using Windows Authentication\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eConnect-DbaInstance -SqlInstance sql2016 | New-DbaSqlConnectionString\r\n\r\nBuilds a connected SMO object using Connect-DbaInstance then extracts and displays the connection string\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003e$wincred = Get-Credential ad\\sqladmin\r\n\r\nNew-DbaSqlConnectionString -SqlInstance sql2014 -Credential $wincred\r\n\r\nCreates a connection string that connects using alternative Windows credentials\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003e$sqlcred = Get-Credential sqladmin\r\n\r\n$server = New-DbaSqlConnectionString -SqlInstance sql2014 -Credential $sqlcred\r\n\r\nLogin to sql2014 as SQL login sqladmin.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003e$server = New-DbaSqlConnectionString -SqlInstance sql2014 -ClientName \"mah connection\"\r\n\r\nCreates a connection string that connects using Windows Authentication and uses the client name \"mah connection\". So \r\nwhen you open up profiler or use extended events, you can search for \"mah connection\".\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003e$server = New-DbaSqlConnectionString -SqlInstance sql2014 -AppendConnectionString \"Packet \r\nSize=4096;AttachDbFilename=C:\\MyFolder\\MyDataFile.mdf;User Instance=true;\"\r\n\r\nCreates a connection string that connects to sql2014 using Windows Authentication, then it sets the packet size (this \r\ncan also be done via -PacketSize) and other connection attributes.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 7 --------------------------\r\n\r\nPS C:\\\u003e$server = New-DbaSqlConnectionString -SqlInstance sql2014 -NetworkProtocol TcpIp -MultiSubnetFailover\r\n\r\nCreates a connection string with Windows Authentication that uses TCPIP and has MultiSubnetFailover enabled.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 8 --------------------------\r\n\r\nPS C:\\\u003e$connstring = New-DbaSqlConnectionString sql2016 -ApplicationIntent ReadOnly\r\n\r\nCreates a connection string with ReadOnly ApplicantionIntent.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "New-DbaSqlConnectionStringBuilder",
        "Description":  "Creates a System.Data.SqlClient.SqlConnectionStringBuilder from a connection string.",
        "Tags":  [
                     "SqlBuild",
                     "ConnectionString",
                     "Connection"
                 ],
        "Author":  "zippy1981",
        "Synopsis":  "Returns a System.Data.SqlClient.SqlConnectionStringBuilder with the string specified",
        "Name":  "New-DbaSqlConnectionStringBuilder",
        "Links":  "https://dbatools.io/New-DbaSqlConnectionStringBuilder",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaSqlConnectionStringBuilder\r\n\r\nReturns an empty ConnectionStringBuilder\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e\"Data Source=localhost,1433;Initial Catalog=AlwaysEncryptedSample;UID=sa;PWD=alwaysB3Encrypt1ng;Application \r\nName=Always Encrypted Sample MVC App;Column Encryption Setting=enabled\" | New-DbaSqlConnectionStringBuilder\r\n\r\nReturns a connection string builder that can be used to connect to the local sql server instance on the default port.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "New-DbaSqlDirectory",
        "Description":  "Uses master.dbo.xp_create_subdir to create the path\nReturns $true if the path can be created, $false otherwise",
        "Tags":  [
                     "Path",
                     "Directory",
                     "Folder"
                 ],
        "Author":  "Stuart Moore",
        "Synopsis":  "Creates new path as specified by the path variable",
        "Name":  "New-DbaSqlDirectory",
        "Links":  "https://dbatools.io/New-DbaSqlDirectory",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaSqlDirectory -SqlInstance sqlcluster -Path L:\\MSAS12.MSSQLSERVER\\OLAP\r\n\r\nIf the SQL Server instance sqlcluster can create the path L:\\MSAS12.MSSQLSERVER\\OLAP it will do and return $true, if \r\nnot it will return $false.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e$credential = Get-Credential\r\n\r\nNew-DbaSqlDirectory -SqlInstance sqlcluster -SqlCredential $credential -Path L:\\MSAS12.MSSQLSERVER\\OLAP\r\n\r\nIf the SQL Server instance sqlcluster can create the path L:\\MSAS12.MSSQLSERVER\\OLAP it will do and return $true, if \r\nnot it will return $false. Uses a SqlCredential to connect\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "New-DbaSsisCatalog",
        "Description":  "After installing the SQL Server Engine and SSIS you still have to enable the SSIS Catalog. This function will enable the catalog and gives the option of supplying the password.",
        "Tags":  [
                     "SSIS",
                     "SSISDB",
                     "Catalog"
                 ],
        "Author":  "Stephen Bennett, https://sqlnotesfromtheunderground.wordpress.com/",
        "Synopsis":  "Enables the SSIS Catalog on a SQL Server 2012+",
        "Name":  "New-DbaSsisCatalog",
        "Links":  "https://dbatools.io/New-DbaSsisCatalog",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003e$password = ConvertTo-SecureString MyVisiblePassWord -AsPlainText -Force\r\n\r\nNew-DbaSsisCatalog -SqlInstance sql2016 -Password $password\r\n\r\nCreates the SSIS Catalog on server DEV01 with the specified password.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e$password = Read-Host -AsSecureString -Prompt \"Enter password\"\r\n\r\nNew-DbaSsisCatalog -SqlInstance DEV01 -Password $password\r\n\r\nCreates the SSIS Catalog on server DEV01 with the specified password.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "New-DbatoolsSupportPackage",
        "Description":  "This function creates an extensive debugging package that can help with reproducing and fixing issues.\n\nThe file will be created on the desktop by default and will contain quite a bit of information:\n    - OS Information\n    - Hardware Information (CPU, Ram, things like that)\n    - .NET Information\n    - PowerShell Information\n    - Your input history\n    - The In-Memory message log\n    - The In-Memory error log\n    - Screenshot of the console buffer (Basically, everything written in your current console, even if you have to scroll upwards to see it.",
        "Tags":  "Debug",
        "Author":  "Fred Weinmann (@FredWeinmann)",
        "Synopsis":  "Creates a package of troubleshooting information that can be used by dbatools to help debug issues.",
        "Name":  "New-DbatoolsSupportPackage",
        "Links":  "https://dbatools.io/New-DbatoolsSupportPackage",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eNew-DbatoolsSupportPackage\r\n\r\nCreates a large support pack in order to help us troubleshoot stuff.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "New-DbaXESession",
        "Description":  "Creates a new XESession object - for the dogged (very manual, Import-DbaXESession is recommended). See the following for more info:\n\nhttps://docs.microsoft.com/en-us/sql/relational-databases/extended-events/use-the-powershell-provider-for-extended-events",
        "Tags":  [
                     "ExtendedEvent",
                     "XE",
                     "XEvent"
                 ],
        "Synopsis":  "Creates a new XESession object - for the dogged.",
        "Name":  "New-DbaXESession",
        "Links":  "https://dbatools.io/New-DbaXESession",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003e$session = New-DbaXESession -SqlInstance sql2017 -Name XeSession_Test\r\n\r\n$event = $session.AddEvent(\"sqlserver.file_written\")\r\n$event.AddAction(\"package0.callstack\")\r\n$session.Create()\r\n\r\nReturns a new XE Session object from sql2017 then adds an event, an action then creates it.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "New-DbaXESmartCsvWriter",
        "Description":  "This Response type is used to write Extended Events to a CSV file.",
        "Tags":  [
                     "ExtendedEvent",
                     "XE",
                     "XEvent"
                 ],
        "Synopsis":  "This Response type is used to write Extended Events to a CSV file.",
        "Name":  "New-DbaXESmartCsvWriter",
        "Links":  "https://dbatools.io/New-DbaXESmartCsvWriter\nhttps://github.com/spaghettidba/XESmartTarget/wiki",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003e$columns = \"cpu_time\", \"duration\", \"physical_reads\", \"logical_reads\", \"writes\", \"row_count\"\r\n\r\n$response = New-DbaXESmartCsvWriter -OutputFile c:\\temp\\workload.csv -OutputColumn $columns -OverWrite -Event \r\n\"sql_batch_completed\"\r\nStart-DbaXESmartTarget -SqlInstance localhost\\sql2017 -Session \"Profiler Standard\" -Responder $response\r\n\r\nWrites Extended Events to the file \"C:\\temp\\workload.csv\".\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "New-DbaXESmartEmail",
        "Description":  "This Response type can be used to send an email each time an event is captured.",
        "Tags":  [
                     "ExtendedEvent",
                     "XE",
                     "XEvent"
                 ],
        "Synopsis":  "This Response type can be used to send an email each time an event is captured.",
        "Name":  "New-DbaXESmartEmail",
        "Links":  "https://dbatools.io/New-DbaXESmartEmail\nhttps://github.com/spaghettidba/XESmartTarget/wiki",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003e$params = @{\r\n\r\nSmtpServer = \"smtp.ad.local\"\r\n    To = \"[email protected]\"\r\n    Sender = \"[email protected]\"\r\n    Subject = \"Query executed\"\r\n    Body = \"Query executed at {collection_time}\"\r\n    Attachment = \"batch_text\"\r\n    AttachmentFileName = \"query.sql\"\r\n}\r\n$emailresponse = New-DbaXESmartEmail @params\r\nStart-DbaXESmartTarget -SqlInstance sql2017 -Session querytracker -Responder $emailresponse\r\n\r\nSends an email each time a querytracker event is captured.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "New-DbaXESmartQueryExec",
        "Description":  "This Response type executes a T-SQL command against a target database whenever an event is recorded.",
        "Tags":  [
                     "ExtendedEvent",
                     "XE",
                     "XEvent"
                 ],
        "Synopsis":  "This Response type executes a T-SQL command against a target database whenever an event is recorded.",
        "Name":  "New-DbaXESmartQueryExec",
        "Links":  "https://dbatools.io/New-DbaXESmartQueryExec\nhttps://github.com/spaghettidba/XESmartTarget/wiki",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003e$response = New-DbaXESmartQueryExec -SqlInstance sql2017 -Database dbadb -Query \"update table set whatever = 1\"\r\n\r\nStart-DbaXESmartTarget -SqlInstance sql2017 -Session deadlock_tracker -Responder $response\r\n\r\nExecutes a T-SQL command against dbadb on sql2017 whenever a deadlock event is recorded.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "New-DbaXESmartReplay",
        "Description":  "This Response type can be used to replay execution related events to a target SQL Server instance. The events that you can replay are of the type sql_batch_completed and rpc_completed: all other events are ignored.",
        "Tags":  [
                     "ExtendedEvent",
                     "XE",
                     "XEvent"
                 ],
        "Synopsis":  "This Response type can be used to replay execution related events to a target SQL Server instance.",
        "Name":  "New-DbaXESmartReplay",
        "Links":  "https://dbatools.io/New-DbaXESmartReplay\nhttps://github.com/spaghettidba/XESmartTarget/wiki",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003e$response = New-DbaXESmartReplay -SqlInstance sql2017 -Database planning\r\n\r\nStart-DbaXESmartTarget -SqlInstance sql2016 -Session loadrelay -Responder $response\r\n\r\nReplays events from sql2016 on sql2017 in the planning database. Returns a PowerShell job object.\r\n\r\nTo see a list of all SmartTarget job objects, use Get-DbaXESmartTarget.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e$response = New-DbaXESmartReplay -SqlInstance sql2017 -Database planning\r\n\r\nStart-DbaXESmartTarget -SqlInstance sql2017 -Session \u0027Profiler Standard\u0027 -Responder $response -NotAsJob\r\n\r\nReplays events from the \u0027Profiler Standard\u0027 session on sql2016 to sql2017\u0027s planning database. Does not run as a job so \r\nyou can see the raw output.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "New-DbaXESmartTableWriter",
        "Description":  "This Response type is used to write Extended Events to a database table. The events are temporarily stored in memory before being written to the database at regular intervals.\n\nThe target table can be created manually upfront or you can let the TableAppenderResponse create a target table based on the fields and actions available in the events captured.\n\nThe columns of the target table and the fields/actions of the events are mapped by name (case-sensitive).",
        "Tags":  [
                     "ExtendedEvent",
                     "XE",
                     "XEvent"
                 ],
        "Synopsis":  "This Response type is used to write Extended Events to a database table.",
        "Name":  "New-DbaXESmartTableWriter",
        "Links":  "https://dbatools.io/New-DbaXESmartTableWriter\nhttps://github.com/spaghettidba/XESmartTarget/wiki",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003e$columns = \"cpu_time\", \"duration\", \"physical_reads\", \"logical_reads\", \"writes\", \"row_count\", \"batch_text\"\r\n\r\n$response = New-DbaXESmartTableWriter -SqlInstance sql2017 -Database dbadb -Table deadlocktracker -OutputColumn \r\n$columns -Filter \"duration \u003e 10000\"\r\nStart-DbaXESmartTarget -SqlInstance sql2017 -Session deadlock_tracker -Responder $response\r\n\r\nWrites Extended Events to the deadlocktracker table in dbadb on sql2017.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Publish-DbaDacpac",
        "Description":  "Deploying a dacpac uses the DacFx which historically needed to be installed on a machine prior to use. In 2016 the DacFx was supplied by Microsoft as a nuget package and this uses that nuget package.",
        "Tags":  [
                     "Migration",
                     "Database",
                     "Dacpac"
                 ],
        "Author":  "Richie lee (@bzzzt_io)",
        "Synopsis":  "The Publish-Database command takes a dacpac which is the output from an SSDT project and publishes it to a database. Changing the schema to match the dacpac and also to run any scripts in the dacpac (pre/post deploy scripts).",
        "Name":  "Publish-DbaDacpac",
        "Links":  "https://dbatools.io/Publish-DbaDacpac",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003ePublish-DbaDacpac -SqlInstance sql2017 -Database WideWorldImporters -Path \r\nC:\\temp\\sql2016-WideWorldImporters.dacpac -PublishXml C:\\temp\\sql2016-WideWorldImporters-publish.xml\r\n\r\nUpdates WideWorldImporters on sql2017 from the sql2016-WideWorldImporters.dacpac using the \r\nsql2016-WideWorldImporters-publish.xml publish profile\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaPublishProfile -SqlInstance sql2016 -Database db2 -Path C:\\temp\r\n\r\nExport-DbaDacpac -SqlInstance sql2016 -Database db2 | Publish-DbaDacpac -PublishXml C:\\temp\\sql2016-db2-publish.xml \r\n-Database db1, db2 -SqlInstance sql2017\r\n\r\nCreates a publish profile at C:\\temp\\sql2016-db2-publish.xml, exports the .dacpac to $home\\Documents\\sql2016-db2.dacpac\r\nthen publishes it to the sql2017 server database db2\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003e$loc = \"C:\\Users\\bob\\source\\repos\\Microsoft.Data.Tools.Msbuild\\lib\\net46\\Microsoft.SqlServer.Dac.dll\"\r\n\r\nPublish-DbaDacpac -SqlInstance \"local\" -Database WideWorldImporters -Path C:\\temp\\WideWorldImporters.dacpac -PublishXml \r\nC:\\temp\\WideWorldImporters.publish.xml -DacFxPath $loc\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Read-DbaAuditFile",
        "Description":  "Read Audit details from a sqlaudit file.",
        "Tags":  [
                     "ExtendedEvent",
                     "Audit"
                 ],
        "Synopsis":  "Read Audit details from a sqlaudit file.",
        "Name":  "Read-DbaAuditFile",
        "Links":  "https://dbatools.io/Read-DbaAuditFile",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRead-DbaAuditFile -Path C:\\temp\\logins.sqlaudit\r\n\r\nReturns events from C:\\temp\\logins.sqlaudit.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-ChildItem C:\\temp\\audit\\*.sqlaudit | Read-DbaAuditFile\r\n\r\nReturns events from all .sqlaudit files in C:\\temp\\audit.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaServerAudit -SqlInstance sql2014 -Audit LoginTracker | Read-DbaAuditFile\r\n\r\nReads remote Audit details by accessing the file over the admin UNC share.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Read-DbaBackupHeader",
        "Description":  "Reads full, differential and transaction log backups. An online SQL Server is required to parse the backup files and the path specified must be relative to that SQL Server.",
        "Tags":  [
                     "DisasterRecovery",
                     "Backup",
                     "Restore"
                 ],
        "Synopsis":  "Reads and displays detailed information about a SQL Server backup.",
        "Name":  "Read-DbaBackupHeader",
        "Links":  "https://dbatools.io/Read-DbaBackupHeader",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRead-DbaBackupHeader -SqlInstance sql2016 -Path S:\\backups\\mydb\\mydb.bak\r\n\r\nLogs into sql2016 using Windows authentication and reads the local file on sql2016, S:\\backups\\mydb\\mydb.bak.\r\n\r\nIf you are running this command on a workstation and connecting remotely, remember that sql2016 cannot access files on \r\nyour own workstation.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eRead-DbaBackupHeader -SqlInstance sql2016 -Path \\\\nas\\sql\\backups\\mydb\\mydb.bak, \r\n\\\\nas\\sql\\backups\\otherdb\\otherdb.bak\r\n\r\nLogs into sql2016 and reads two backup files - mydb.bak and otherdb.bak. The SQL Server service account must have \r\nrights to read this file.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eRead-DbaBackupHeader -SqlInstance . -Path C:\\temp\\myfile.bak -Simple\r\n\r\nLogs into the local workstation (or computer) and shows simplified output about C:\\temp\\myfile.bak. The SQL Server \r\nservice account must have rights to read this file.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003e$backupinfo = Read-DbaBackupHeader -SqlInstance . -Path C:\\temp\\myfile.bak\r\n\r\n$backupinfo.FileList\r\n\r\nDisplays detailed information about each of the datafiles contained in the backupset.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eRead-DbaBackupHeader -SqlInstance . -Path C:\\temp\\myfile.bak -FileList\r\n\r\nAlso returns detailed information about each of the datafiles contained in the backupset.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003e\"C:\\temp\\myfile.bak\", \"\\backupserver\\backups\\myotherfile.bak\" | Read-DbaBackupHeader -SqlInstance sql2016\r\n\r\nSimilar to running Read-DbaBackupHeader -SqlInstance sql2016 -Path \"C:\\temp\\myfile.bak\", \r\n\"\\backupserver\\backups\\myotherfile.bak\"\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 7 --------------------------\r\n\r\nPS C:\\\u003eGet-ChildItem \\\\nas\\sql\\*.bak | Read-DbaBackupHeader -SqlInstance sql2016\r\n\r\nGets a list of all .bak files on the \\\\nas\\sql share and reads the headers using the server named \"sql2016\". This means \r\nthat the server, sql2016, must have read access to the \\\\nas\\sql share.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 8 --------------------------\r\n\r\nPS C:\\\u003eRead-DbaBackupHeader -Path \r\nhttps://dbatoolsaz.blob.core.windows.net/azbackups/restoretime/restoretime_201705131850.bak\r\n\r\n-AzureCredential AzureBackupUser\r\n\r\nGets the backup header information from the SQL Server backup file stored at \r\nhttps://dbatoolsaz.blob.core.windows.net/azbackups/restoretime/restoretime_201705131850.bak on Azure\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Read-DbaTraceFile",
        "Description":  "Using the fn_trace_gettable function, a trace file is read and returned as a PowerShell object\n\nThis function returns the whole of the trace file. The information is presented in the format that the trace subsystem uses.",
        "Tags":  [
                     "Security",
                     "Trace"
                 ],
        "Synopsis":  "Reads SQL Server trace files",
        "Name":  "Read-DbaTraceFile",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRead-DbaTraceFile -SqlInstance sql2016 -Database master, tempdb -Path C:\\traces\\big.trc\r\n\r\nReads the tracefile C:\\traces\\big.trc, stored on the sql2016 sql server. Filters only results that have master or \r\ntempdb as the DatabaseName.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eRead-DbaTraceFile -SqlInstance sql2016 -Database master, tempdb -Path C:\\traces\\big.trc -TextData \u0027EXEC \r\nSP_PROCOPTION\u0027\r\n\r\nReads the tracefile C:\\traces\\big.trc, stored on the sql2016 sql server.\r\nFilters only results that have master or tempdb as the DatabaseName and that have \u0027EXEC SP_PROCOPTION\u0027 somewhere in the \r\ntext.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nRead-DbaTraceFile -SqlInstance sql2016 -Path C:\\traces\\big.trc -Where \"LinkedServerName = \u0027myls\u0027 and StartTime \r\n\u003e\u00275/30/2017 4:27:52 PM\u0027\"\r\n\r\nReads the tracefile C:\\traces\\big.trc, stored on the sql2016 sql server.\r\nFilters only results where LinkServerName = myls and StartTime is greater than \u00275/30/2017 4:27:52 PM\u0027.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaTrace -SqlInstance sql2014 | Read-DbaTraceFile\r\n\r\nReads every trace file on sql2014\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Read-DbaTransactionLog",
        "Description":  "Using the fn_dblog function, the live transaction log is read and returned as a PowerShell object\n\nThis function returns the whole of the log. The information is presented in the format that the logging subsystem uses.\n\nA soft limit of 0.5GB of log as been implemented. This is based on testing. This limit can be overridden\nat the users request, but please be aware that this may have an impact on your target databases and on the\nsystem running this function",
        "Tags":  [
                     "Database",
                     "Log",
                     "LogFile"
                 ],
        "Author":  "Stuart Moore (@napalmgram), stuart-moore.com",
        "Synopsis":  "Reads the live Transaction log from specified SQL Server Database",
        "Name":  "Read-DbaTransactionLog",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003e$Log = Read-DbaTransactionLog -SqlInstance sql2016 -Database MyDatabase\r\n\r\nWill read the contents of the transaction log of MyDatabase on SQL Server Instance sql2016 into the local PowerShell \r\nobject $Log\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e$Log = Read-DbaTransactionLog -SqlInstance sql2016 -Database MyDatabase -IgnoreLimit\r\n\r\nWill read the contents of the transaction log of MyDatabase on SQL Server Instance sql2016 into the local PowerShell \r\nobject $Log, ignoring the recommnedation of not returning more that 0.5GB of log\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Read-DbaXEFile",
        "Description":  "Read XEvents from a xel or xem file.",
        "Tags":  [
                     "ExtendedEvent",
                     "XE",
                     "XEvent"
                 ],
        "Synopsis":  "Read XEvents from a xel or xem file.",
        "Name":  "Read-DbaXEFile",
        "Links":  "https://dbatools.io/Read-DbaXEFile",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRead-DbaXEFile -Path C:\\temp\\deadocks.xel\r\n\r\nReturns events from C:\\temp\\deadocks.xel.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-ChildItem C:\\temp\\xe\\*.xel | Read-DbaXEFile\r\n\r\nReturns events from all .xel files in C:\\temp\\xe.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaXESession -SqlInstance sql2014 -Session deadlocks | Read-DbaXEFile\r\n\r\nReads remote XEvents by accessing the file over the admin UNC share.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Register-DbaConfig",
        "Description":  "Registers an existing configuration object in registry.\nThis allows simple persisting of settings across powershell consoles.\nIt also can be used to generate a registry template, which can then be used to create policies.",
        "Tags":  [
                     "Config",
                     "Module"
                 ],
        "Author":  "Friedrich Weinmann",
        "Synopsis":  "Registers an existing configuration object in registry.",
        "Name":  "Register-DbaConfig",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaConfig message.* | Register-DbaConfig\r\n\r\nRetrieves all configuration items that that start with message. and registers them in registry for the current user.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eRegister-DbaConfig -FullName \"developer.mode.enable\" -Scope SystemDefault\r\n\r\nRetrieves the configuration item \"developer.mode.enable\" and registers it in registry as the default setting for all \r\nusers on this machine.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eRegister-DbaConfig -Module message -Scope SystemMandatory\r\n\r\nRetrieves all configuration items of the module MyModule, then registers them in registry to enforce them for all users \r\non the current system.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Remove-DbaAgentJob",
        "Description":  "Remove-DbaAgentJob removes a a job in the SQL Server Agent.",
        "Tags":  [
                     "Agent",
                     "Job"
                 ],
        "Author":  "Sander Stad (@sqlstad, sqlstad.nl)",
        "Synopsis":  "Remove-DbaAgentJob removes a job.",
        "Name":  "Remove-DbaAgentJob",
        "Links":  "https://dbatools.io/Remove-DbaAgentJob",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaAgentJob -SqlInstance sql1 -Job Job1\r\n\r\nRemoves the job from the instance with the name Job1\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaAgentJob -SqlInstance sql1 -Job Job1 -KeepHistory\r\n\r\nRemoves the job but keeps the history\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaAgentJob -SqlInstance sql1 -Job Job1 -KeepUnusedSchedule\r\n\r\nRemoves the job but keeps the unused schedules\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaAgentJob -SqlInstance sql1, sql2, sql3 -Job Job1\r\n\r\nRemoves the job from multiple servers\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003esql1, sql2, sql3 | Remove-DbaAgentJob -Job Job1\r\n\r\nRemoves the job from multiple servers using pipe line\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Remove-DbaAgentJobCategory",
        "Description":  "Remove-DbaAgentJobCategory makes it possible to remove a job category.\nBe assured that the category you want to remove is not used with other jobs. If another job uses this category it will be get the category [Uncategorized (Local)].",
        "Tags":  [
                     "Agent",
                     "Job",
                     "JobCategory"
                 ],
        "Author":  "Sander Stad (@sqlstad, sqlstad.nl)",
        "Synopsis":  "Remove-DbaAgentJobCategory removes a job category.",
        "Name":  "Remove-DbaAgentJobCategory",
        "Links":  "https://dbatools.io/Remove-DbaAgentJobCategory",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaAgentJobCategory -SqlInstance sql1 -Category \u0027Category 1\u0027\r\n\r\nRemove the job category Category 1 from the instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaAgentJobCategory -SqlInstance sql1 -Category Category1, Category2, Category3\r\n\r\nRemove multiple job categories from the instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaAgentJobCategory -SqlInstance sql1, sql2, sql3 -Category Category1, Category2, Category3\r\n\r\nRemove multiple job categories from the multiple instances.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Remove-DbaAgentJobStep",
        "Description":  "Removes a job step from a SQL Server Agent job.",
        "Tags":  [
                     "Agent",
                     "Job",
                     "JobStep"
                 ],
        "Author":  "Sander Stad (@sqlstad, sqlstad.nl)",
        "Synopsis":  "Removes a step from the specified SQL Agent job.",
        "Name":  "Remove-DbaAgentJobStep",
        "Links":  "https://dbatools.io/Remove-DbaAgentJobStep",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaAgentJobStep -SqlInstance sql1 -Job Job1 -StepName Step1\r\n\r\nRemove \u0027Step1\u0027 from job \u0027Job1\u0027 on sql1.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaAgentJobStep -SqlInstance sql1 -Job Job1, Job2, Job3 -StepName Step1\r\n\r\nRemove the job step from multiple jobs.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaAgentJobStep -SqlInstance sql1, sql2, sql3 -Job Job1 -StepName Step1\r\n\r\nRemove the job step from the job on multiple servers.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003esql1, sql2, sql3 | Remove-DbaAgentJobStep -Job Job1 -StepName Step1\r\n\r\nRemove the job step from the job on multiple servers using pipeline.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Remove-DbaAgentSchedule",
        "Description":  "Remove-DbaAgentJobSchedule removes a a job in the SQL Server Agent.",
        "Tags":  [
                     "Agent",
                     "Job",
                     "Schedule"
                 ],
        "Author":  "Sander Stad (@sqlstad, sqlstad.nl)",
        "Synopsis":  "Remove-DbaAgentJobSchedule removes a job schedule.",
        "Name":  "Remove-DbaAgentSchedule",
        "Links":  "https://dbatools.io/Remove-DbaAgentJobSchedule",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaAgentSchedule -SqlInstance sql1 -Schedule weekly\r\n\r\nRemove the schedule weekly\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaAgentSchedule -SqlInstance sql1 -Schedule weekly -Force\r\n\r\nRemove the schedule weekly from the job even if the schedule is being used by another job.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaAgentSchedule -SqlInstance sql1 -Schedule daily, weekly\r\n\r\nRemove multiple schedule\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaAgentSchedule -SqlInstance sql1, sql2, sql3 -Schedule daily, weekly\r\n\r\nRemove the schedule on multiple servers for multiple schedules\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003esql1, sql2, sql3 | Remove-DbaAgentSchedule -Schedule daily, weekly\r\n\r\nRemove the schedule on multiple servers using pipe line\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentSchedule -SqlInstance sql1 -Schedule sched1, sched2, sched3 | Remove-DbaAgentSchedule\r\n\r\nRemove the schedules using a pipeline\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Remove-DbaBackup",
        "Description":  "Provides all of the same functionality for removing SQL backups from disk as a standard maintenance plan would.\n\nAs an addition you have the ability to check the Archive bit on files before deletion. This will allow you to ensure backups have been archived to your archive location before removal.\n\nAlso included is the ability to remove empty folders as part of this cleanup activity.",
        "Tags":  [
                     "Storage",
                     "DisasterRecovery",
                     "Backup"
                 ],
        "Author":  "Chris Sommer, @cjsommer, www.cjsommer.com",
        "Synopsis":  "Removes SQL Server backups from disk.",
        "Name":  "Remove-DbaBackup",
        "Links":  "https://dbatools.io/Remove-DbaBackup",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaBackup -Path \u0027C:\\MSSQL\\SQL Backup\\\u0027 -BackupFileExtension trn -RetentionPeriod 48h\r\n\r\n\u0027*.trn\u0027 files in \u0027C:\\MSSQL\\SQL Backup\\\u0027 and all subdirectories that are more than 48 hours old will be removed.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaBackup -Path \u0027C:\\MSSQL\\SQL Backup\\\u0027 -BackupFileExtension trn -RetentionPeriod 48h -WhatIf\r\n\r\nSame as example #1, but doesn\u0027t actually remove any files. The function will instead show you what would be done.\r\nThis is useful when first experimenting with using the function.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaBackup -Path \u0027C:\\MSSQL\\Backup\\\u0027 -BackupFileExtension bak -RetentionPeriod 7d -CheckArchiveBit\r\n\r\n\u0027*.bak\u0027 files in \u0027C:\\MSSQL\\Backup\\\u0027 and all subdirectories that are more than 7 days old will be removed, but only if \r\nthe files have been backed up to another location as verified by checking the Archive bit.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaBackup -Path \u0027C:\\MSSQL\\Backup\\\u0027 -BackupFileExtension bak -RetentionPeriod 1w -RemoveEmptyBackupFolder\r\n\r\n\u0027*.bak\u0027 files in \u0027C:\\MSSQL\\Backup\\\u0027 and all subdirectories that are more than 1 week old will be removed. Any folders \r\nleft empty will be removed as well.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Remove-DbaClientAlias",
        "Description":  "Removes a SQL Server alias by altering HKLM:\\SOFTWARE\\Microsoft\\MSSQLServer\\Client",
        "Tags":  "Alias",
        "Synopsis":  "Removes a sql alias for the specified server - mimics cliconfg.exe",
        "Name":  "Remove-DbaClientAlias",
        "Links":  "https://dbatools.io/Remove-DbaClientAlias",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaClientAlias -ComputerName workstationx -Alias sqlps\r\n\r\nRemoves the sqlps SQL client alias on workstationx\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaClientAlias | Remove-DbaClientAlias\r\n\r\nRemoves all SQL Server client aliases on the local computer\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Remove-DbaCmConnection",
        "Description":  "Removes connection objects from the connection cache used for remote computer management.",
        "Tags":  [
                     "ComputerManagement",
                     "CIM"
                 ],
        "Author":  "Fred Winmann (@FredWeinmann)",
        "Synopsis":  "Removes connection objects from the connection cache used for remote computer management.",
        "Name":  "Remove-DbaCmConnection",
        "Links":  "https://dbatools.io/Remove-DbaCmConnection",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaCmConnection -ComputerName sql2014\r\n\r\nRemoves the cached connection to the server sql2014 from the cache.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaCmConnection | Remove-DbaCmConnection\r\n\r\nClears the entire connection cache.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Remove-DbaComputerCertificate",
        "Description":  "Removes a computer certificate from a local or remote compuer",
        "Tags":  "Certificate",
        "Synopsis":  "Removes a computer certificate - useful for removing easily certs from remote computers",
        "Name":  "Remove-DbaComputerCertificate",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaComputerCertificate -ComputerName Server1 -Thumbprint C2BBE81A94FEE7A26FFF86C2DFDAF6BFD28C6C94\r\n\r\nRemoves certificate with thumbprint C2BBE81A94FEE7A26FFF86C2DFDAF6BFD28C6C94 in the LocalMachine store on Server1\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaComputerCertificate | Where-Object Thumbprint -eq E0A071E387396723C45E92D42B2D497C6A182340 | \r\nRemove-DbaComputerCertificate\r\n\r\nRemoves certificate using the pipeline\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaComputerCertificate -ComputerName Server1 -Thumbprint C2BBE81A94FEE7A26FFF86C2DFDAF6BFD28C6C94 -Store \r\nUser -Folder My\r\n\r\nRemoves certificate with thumbprint C2BBE81A94FEE7A26FFF86C2DFDAF6BFD28C6C94 in the User\\My (Personal) store on Server1\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Remove-DbaDatabase",
        "Description":  "Tries a bunch of different ways to remove a database or two or more.",
        "Tags":  [
                     "Delete",
                     "Databases"
                 ],
        "Synopsis":  "Drops a database, hopefully even the really stuck ones.",
        "Name":  "Remove-DbaDatabase",
        "Links":  "https://dbatools.io/Remove-DbaDatabase",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaDatabase -SqlInstance sql2016 -Database containeddb\r\n\r\nPrompts then removes the database containeddb on SQL Server sql2016\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaDatabase -SqlInstance sql2016 -Database containeddb, mydb\r\n\r\nPrompts then removes the databases containeddb and mydb on SQL Server sql2016\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaDatabase -SqlInstance sql2016 -Database containeddb -Confirm:$false\r\n\r\nDoes not prompt and swiftly removes containeddb on SQL Server sql2016\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabase -SqlInstance server\\instance -ExcludeAllSystemDb | Remove-DbaDatabase\r\n\r\nRemoves all the user databases from server\\instance\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabase -SqlInstance server\\instance -ExcludeAllSystemDb | Remove-DbaDatabase -Confirm:$false\r\n\r\nRemoves all the user databases from server\\instance without any confirmation\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Remove-DbaDatabaseMasterKey",
        "Description":  "Deletes specified database master key.",
        "Tags":  "Certificate",
        "Synopsis":  "Deletes specified database master key",
        "Name":  "Remove-DbaDatabaseMasterKey",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaDatabaseMasterKey -SqlInstance Server1\r\n\r\nThe master key in the master database on server1 will be removed if it exists.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaDatabaseMasterKey -SqlInstance Server1 -Database db1 -Confirm:$false\r\n\r\nSuppresses all prompts to remove the master key in the \u0027db1\u0027 database and drops the key.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Remove-DbaDatabaseSafely",
        "Description":  "Performs a DBCC CHECKDB on the database, backs up the database with Checksum and verify only to a final (golden) backup location, creates an Agent Job to restore from that backup, drops the database, runs the agent job to restore the database, performs a DBCC CHECKDB and drops the database.\n\nWith huge thanks to Grant Fritchey and his verify your backups video. Take a look, it\u0027s only 3 minutes long. http://sqlps.io/backuprant",
        "Tags":  [
                     "Database",
                     "Remove"
                 ],
        "Author":  "Rob Sewell @SQLDBAWithBeard, sqldbawithabeard.com",
        "Synopsis":  "Safely removes a SQL Database and creates an Agent Job to restore it.",
        "Name":  "Remove-DbaDatabaseSafely",
        "Links":  "https://dbatools.io/Remove-DbaDatabaseSafely",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaDatabaseSafely -SqlInstance \u0027Fade2Black\u0027 -Database RideTheLightning -BackupFolder \r\n\u0027C:\\MSSQL\\Backup\\Rationalised - DO NOT DELETE\u0027\r\n\r\nPerforms a DBCC CHECKDB on database RideTheLightning on server Fade2Black. If there are no errors, the database is \r\nbackup to the folder C:\\MSSQL\\Backup\\Rationalised - DO NOT DELETE. Then, an Agent job to restore the database from that \r\nbackup is created. The database is then dropped, the Agent job to restore it run, a DBCC CHECKDB run against the \r\nrestored database, and then it is dropped again.\r\n\r\nAny DBCC errors will be written to your documents folder\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e$Database = \u0027DemoNCIndex\u0027,\u0027RemoveTestDatabase\u0027\r\n\r\nRemove-DbaDatabaseSafely -SqlInstance \u0027Fade2Black\u0027 -Database $Database -BackupFolder \u0027C:\\MSSQL\\Backup\\Rationalised - DO \r\nNOT DELETE\u0027\r\n\r\nPerforms a DBCC CHECKDB on two databases, \u0027DemoNCIndex\u0027 and \u0027RemoveTestDatabase\u0027 on server Fade2Black. Then, an Agent \r\njob to restore each database from those backups is created. The databases are then dropped, the Agent jobs to restore \r\nthem run, a DBCC CHECKDB run against the restored databases, and then they are dropped again.\r\n\r\nAny DBCC errors will be written to your documents folder\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaDatabaseSafely -SqlInstance \u0027Fade2Black\u0027 -DestinationServer JusticeForAll -Database RideTheLightning \r\n-BackupFolder \u0027\\\\BACKUPSERVER\\BACKUPSHARE\\MSSQL\\Rationalised - DO NOT DELETE\u0027\r\n\r\nPerforms a DBCC CHECKDB on database RideTheLightning on server Fade2Black. If there are no errors, the database is \r\nbackup to the folder \\\\BACKUPSERVER\\BACKUPSHARE\\MSSQL\\Rationalised - DO NOT DELETE . Then, an Agent job is created on \r\nserver JusticeForAll to restore the database from that backup is created. The database is then dropped on Fade2Black, \r\nthe Agent job to restore it on JusticeForAll is run, a DBCC CHECKDB run against the restored database, and then it is \r\ndropped from JusticeForAll.\r\n\r\nAny DBCC errors will be written to your documents folder\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaDatabaseSafely -SqlInstance IronMaiden -Database $Database -DestinationServer TheWildHearts \r\n-BackupFolder Z:\\Backups -NoDbccCheckDb -UseDefaultFilePaths -JobOwner \u0027THEBEARD\\Rob\u0027\r\n\r\nFor the databases $Database on the server IronMaiden a DBCC CHECKDB will not be performed before backing up the \r\ndatabases to the folder Z:\\Backups. Then, an Agent job is created on server TheWildHearts with a Job Owner of \r\nTHEBEARD\\Rob to restore each database from that backup using the instance\u0027s default file paths. The database(s) is(are) \r\nthen dropped on IronMaiden, the Agent job(s) run, a DBCC CHECKDB run on the restored database(s), and then the \r\ndatabase(s) is(are) dropped.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaDatabaseSafely -SqlInstance IronMaiden -Database $Database -DestinationServer TheWildHearts \r\n-BackupFolder Z:\\Backups -UseDefaultFilePaths -ContinueAfterDbccError\r\n\r\nThe databases $Database on the server IronMaiden will be backed up the to the folder Z:\\Backups. Then, an Agent job is \r\ncreated on server TheWildHearts with a Job Owner of THEBEARD\\Rob to restore each database from that backup using the \r\ninstance\u0027s default file paths. The database(s) is(are) then dropped on IronMaiden, the Agent job(s) run, a DBCC CHECKDB \r\nrun on the restored database(s), and then the database(s) is(are) dropped.\r\n\r\nIf there is a DBCC Error, the function  will continue to perform rest of the actions and will create an Agent job with \r\n\u0027DBCCERROR\u0027 in the name and a Backup file with \u0027DBCCError\u0027 in the name.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Remove-DbaDbCertificate",
        "Description":  "Deletes specified database certificate",
        "Tags":  "Certificate",
        "Synopsis":  "Deletes specified database certificate",
        "Name":  "Remove-DbaDbCertificate",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaDbCertificate -SqlInstance Server1\r\n\r\nThe certificate in the master database on server1 will be removed if it exists.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaDbCertificate -SqlInstance Server1 -Database db1 -Confirm:$false\r\n\r\nSuppresses all prompts to remove the certificate in the \u0027db1\u0027 database and drops the key.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Remove-DbaDbSnapshot",
        "Description":  "Removes (drops) database snapshots from the server",
        "Tags":  [
                     "Snapshot",
                     "Database"
                 ],
        "Author":  "niphlod",
        "Synopsis":  "Removes database snapshots",
        "Name":  "Remove-DbaDbSnapshot",
        "Links":  "https://dbatools.io/Remove-DbaDbSnapshot",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaDbSnapshot -SqlInstance sql2014 -Snapshot HR_snap_20161201, HR_snap_20161101\r\n\r\nRemoves database snapshots named HR_snap_20161201 and HR_snap_20161101\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaDbSnapshot -SqlInstance sql2014 -Database HR, Accounting\r\n\r\nRemoves all database snapshots having HR and Accounting as base dbs\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbSnapshot -SqlInstance sql2014 -Database HR, Accounting | Remove-DbaDbSnapshot\r\n\r\nRemoves all database snapshots having HR and Accounting as base dbs\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaDbSnapshot -SqlInstance sql2014 -Snapshot HR_snapshot, Accounting_snapshot\r\n\r\nRemoves HR_snapshot and Accounting_snapshot\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbSnapshot -SqlInstance sql2016 | Where SnapshotOf -like \u0027*dumpsterfire*\u0027 | Remove-DbaDbSnapshot\r\n\r\nRemoves all snapshots associated with databases that have dumpsterfire in the name\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbSnapshot -SqlInstance sql2016 | Out-GridView -Passthru | Remove-DbaDbSnapshot\r\n\r\nAllows the selection of snapshots on sql2016 to remove\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 7 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaDbSnapshot -SqlInstance sql2014 -AllSnapshots\r\n\r\nRemoves all database snapshots from sql2014\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 8 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaDbSnapshot -SqlInstance sql2014 -AllSnapshots -Confirm\r\n\r\nRemoves all database snapshots from sql2014 and prompts for each database\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Remove-DbaDbUser",
        "Description":  "If user is the owner of a schema with the same name and if if the schema does not have any underlying objects the schema will be\ndropped.  If user owns more than one schema, the owner of the schemas that does not have the same name as the user, will be\nchanged to \u0027dbo\u0027. If schemas have underlying objects, you must specify the -Force parameter so the user can be dropped.",
        "Tags":  [
                     "Database",
                     "User",
                     "Login",
                     "Security"
                 ],
        "Author":  "Doug Meyers (@dgmyrs)",
        "Synopsis":  "Drop database user",
        "Name":  "Remove-DbaDbUser",
        "Links":  "https://dbatools.io/Remove-DbaDbUser",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaDbUser -SqlInstance sqlserver2014 -User user1\r\n\r\nDrops user1 from all databases it exists in on server \u0027sqlserver2014\u0027.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaDbUser -SqlInstance sqlserver2014 -Database database1 -User user1\r\n\r\nDrops user1 from the database1 database on server \u0027sqlserver2014\u0027.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaDbUser -SqlInstance sqlserver2014 -ExcludeDatabase model -User user1\r\n\r\nDrops user1 from all databases it exists in on server \u0027sqlserver2014\u0027 except for the model database.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabaseUser sqlserver2014 | Where-Object Name -In \"user1\" | Remove-DbaDbUser\r\n\r\nDrops user1 from all databases it exists in on server \u0027sqlserver2014\u0027.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Remove-DbaLogin",
        "Description":  "Tries a bunch of different ways to remove a Login or two or more.",
        "Tags":  [
                     "Delete",
                     "Logins"
                 ],
        "Synopsis":  "Drops a Login",
        "Name":  "Remove-DbaLogin",
        "Links":  "https://dbatools.io/Remove-DbaLogin",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaLogin -SqlInstance sql2016 -Login mylogin\r\n\r\nPrompts then removes the Login mylogin on SQL Server sql2016\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaLogin -SqlInstance sql2016 -Login mylogin, yourlogin\r\n\r\nPrompts then removes the Logins mylogin and yourlogin on SQL Server sql2016\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaLogin -SqlInstance sql2016 -Login mylogin -Confirm:$false\r\n\r\nDoes not prompt and swiftly removes mylogin on SQL Server sql2016\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaLogin -SqlInstance server\\instance -Login yourlogin | Remove-DbaLogin\r\n\r\nremoves mylogin on SQL Server server\\instance\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Remove-DbaNetworkCertificate",
        "Description":  "Removes the network certificate for SQL Server instance. This setting is found in Configuration Manager.",
        "Tags":  "Certificate",
        "Synopsis":  "Removes the network certificate for SQL Server instance",
        "Name":  "Remove-DbaNetworkCertificate",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaNetworkCertificate\r\n\r\nRemoves the Network Certificate for the default instance (MSSQLSERVER) on localhost\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaNetworkCertificate -SqlInstance sql1\\SQL2008R2SP2\r\n\r\nRemoves the Network Certificate for the SQL2008R2SP2 instance on sql1\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaNetworkCertificate -SqlInstance localhost\\SQL2008R2SP2 -WhatIf\r\n\r\nShows what would happen if the command were run\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Remove-DbaOrphanUser",
        "Description":  "An orphan user is defined by a user that does not have their matching login. (Login property = \"\").\n\nIf user is the owner of the schema with the same name and if if the schema does not have any underlying objects the schema will be dropped.\n\nIf user owns more than one schema, the owner of the schemas that does not have the same name as the user, will be changed to \u0027dbo\u0027. If schemas have underlying objects, you must specify the -Force parameter so the user can be dropped.\n\nIf exists a login to map the drop will not be performed unless you specify the -Force parameter (only when calling from Repair-DbaOrphanUser.",
        "Tags":  [
                     "Orphan",
                     "Database",
                     "Security",
                     "Login"
                 ],
        "Author":  "Claudio Silva (@ClaudioESSilva)",
        "Synopsis":  "Drop orphan users with no existing login to map",
        "Name":  "Remove-DbaOrphanUser",
        "Links":  "https://dbatools.io/Remove-DbaOrphanUser",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaOrphanUser -SqlInstance sql2005\r\n\r\nFinds and drops all orphan users without matching Logins in all databases present on server \u0027sql2005\u0027.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaOrphanUser -SqlInstance sqlserver2014a -SqlCredential $cred\r\n\r\nFinds and drops all orphan users without matching Logins in all databases present on server \u0027sqlserver2014a\u0027. SQL \r\nServer authentication will be used in connecting to the server.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaOrphanUser -SqlInstance sqlserver2014a -Database db1, db2 -Force\r\n\r\nFinds and drops orphan users even if they have a matching Login on both db1 and db2 databases.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaOrphanUser -SqlInstance sqlserver2014a -ExcludeDatabase db1, db2 -Force\r\n\r\nFinds and drops orphan users even if they have a matching Login from all databases except db1 and db2.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaOrphanUser -SqlInstance sqlserver2014a -User OrphanUser\r\n\r\nRemoves user OrphanUser from all databases only if there is no matching login.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaOrphanUser -SqlInstance sqlserver2014a -User OrphanUser -Force\r\n\r\nRemoves user OrphanUser from all databases even if they have a matching Login. Any schema that the user owns will \r\nchange ownership to dbo.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Remove-DbaPfDataCollectorCounter",
        "Description":  "Removes a Performance Data Collector Counter.",
        "Tags":  "PerfMon",
        "Synopsis":  "Removes a Performance Data Collector Counter.",
        "Name":  "Remove-DbaPfDataCollectorCounter",
        "Links":  "https://dbatools.io/Remove-DbaPfDataCollectorCounter",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaPfDataCollectorCounter -ComputerName sql2017 -CollectorSet \u0027System Correlation\u0027 -Collector \r\nDataCollector01  -Counter \u0027\\LogicalDisk(*)\\Avg. Disk Queue Length\u0027\r\n\r\nPrompts for confirmation then removes the \u0027\\LogicalDisk(*)\\Avg. Disk Queue Length\u0027 counter within the DataCollector01 \r\ncollector within the System Correlation collector set on sql2017.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfDataCollectorCounter | Out-GridView -PassThru | Remove-DbaPfDataCollectorCounter -Confirm:$false\r\n\r\nAllows you to select which counters you\u0027d like on localhost and does not prompt for confirmation.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Remove-DbaPfDataCollectorSet",
        "Description":  "Removes a Performance Monitor Data Collector Set. When removing data collector sets from the local instance, Run As Admin is required.",
        "Tags":  "PerfMon",
        "Synopsis":  "Removes a Performance Monitor Data Collector Set",
        "Name":  "Remove-DbaPfDataCollectorSet",
        "Links":  "https://dbatools.io/Remove-DbaPfDataCollectorSet",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaPfDataCollectorSet\r\n\r\nPrompts for confirmation then removes all ready Collectors on localhost.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaPfDataCollectorSet -ComputerName sql2017 -Confirm:$false\r\n\r\nAttempts to remove all ready Collectors on localhost and does not prompt to confirm.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaPfDataCollectorSet -ComputerName sql2017, sql2016 -Credential (Get-Credential) -CollectorSet \u0027System \r\nCorrelation\u0027\r\n\r\nPrompts for confirmation then removes the \u0027System Correlation\u0027 Collector on sql2017 and sql2016 using alternative \r\ncredentials.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfDataCollectorSet -CollectorSet \u0027System Correlation\u0027 | Remove-DbaPfDataCollectorSet\r\n\r\nRemoves the \u0027System Correlation\u0027 Collector.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfDataCollectorSet -CollectorSet \u0027System Correlation\u0027 | Stop-DbaPfDataCollectorSet | \r\nRemove-DbaPfDataCollectorSet\r\n\r\nStops and removes the \u0027System Correlation\u0027 Collector.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Remove-DbaRegisteredServer",
        "Description":  "Removes registered servers found in SQL Server Central Management Server (CMS).",
        "Tags":  [
                     "RegisteredServer",
                     "CMS"
                 ],
        "Author":  "Chrissy LeMaire (@cl)",
        "Synopsis":  "Removes registered servers found in SQL Server Central Management Server (CMS).",
        "Name":  "Remove-DbaRegisteredServer",
        "Links":  "https://dbatools.io/Remove-DbaRegisteredServer",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaRegisteredServer -SqlInstance sql2012 -Group HR, Accounting\r\n\r\nRemoves all servers from the HR and Accounting groups on sql2012\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaRegisteredServer -SqlInstance sql2012 -Group HR\\Development\r\n\r\nRemoves all servers from the HR and sub-group Development from the CMS on sql2012.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaRegisteredServer -SqlInstance sql2012 -Confirm:$false\r\n\r\nRemoves all registered servers on sql2012 and turns off all prompting\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Remove-DbaRegisteredServerGroup",
        "Description":  "Returns an array of Server Groups found in the CMS.",
        "Tags":  [
                     "RegisteredServer",
                     "CMS"
                 ],
        "Author":  "Chrissy LeMaire (@cl)",
        "Synopsis":  "Gets list of Server Groups objects stored in SQL Server Central Management Server (CMS).",
        "Name":  "Remove-DbaRegisteredServerGroup",
        "Links":  "https://dbatools.io/Remove-DbaRegisteredServerGroup",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaRegisteredServerGroup -SqlInstance sql2012 -Group HR, Accounting\r\n\r\nRemoves the HR and Accounting groups on sql2012\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaRegisteredServerGroup -SqlInstance sql2012 -Group HR\\Development -Confirm:$false\r\n\r\nRemoves the Development subgroup within the HR group on sql2012 and turns off all prompting\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Remove-DbaSpn",
        "Description":  "This function will connect to Active Directory and search for an account. If the account is found, it will attempt to remove the specified SPN. Once the SPN is removed, the function will also remove delegation to that service.\n\nIn order to run this function, the credential you provide must have write access to Active Directory.\n\nNote: This function supports -WhatIf",
        "Tags":  "SPN",
        "Author":  "Drew Furgiuele (@pittfurg), http://www.port1433.com",
        "Synopsis":  "Removes an SPN for a given service account in active directory and also removes delegation to the same SPN, if found",
        "Name":  "Remove-DbaSpn",
        "Links":  "https://dbatools.io/Remove-DbaSpn",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaSpn -SPN MSSQLSvc\\SQLSERVERA.domain.something -ServiceAccount domain\\account\r\n\r\nConnects to Active Directory and removes a provided SPN from the given account (and also the relative delegation)\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaSpn -SPN MSSQLSvc\\SQLSERVERA.domain.something -ServiceAccount domain\\account -EnableException\r\n\r\nConnects to Active Directory and removes a provided SPN from the given account, suppressing all error messages and \r\nthrow exceptions that can be caught instead\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaSpn -SPN MSSQLSvc\\SQLSERVERA.domain.something -ServiceAccount domain\\account -Credential \r\n(Get-Credential)\r\n\r\nConnects to Active Directory and removes a provided SPN to the given account. Uses alternative account to connect to AD.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaSpn -ComputerName sql2005 | Where { $_.isSet -eq $true } | Remove-DbaSpn -WhatIf\r\n\r\nShows what would happen trying to remove all set SPNs for sql2005 and the relative delegations\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaSpn -ComputerName sql2005 | Where { $_.isSet -eq $true } | Remove-DbaSpn\r\n\r\nRemoves all set SPNs for sql2005 and the relative delegations\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Remove-DbaTrace",
        "Description":  "Stops and closes the specified trace and deletes its definition from the server.",
        "Tags":  [
                     "Security",
                     "Trace"
                 ],
        "Synopsis":  "Stops and closes the specified trace and deletes its definition from the server.",
        "Name":  "Remove-DbaTrace",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaTrace -SqlInstance sql2008\r\n\r\nStops and removes all traces on sql2008\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaTrace -SqlInstance sql2008 -Id 1\r\n\r\nStops and removes all trace with ID 1 on sql2008\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaTrace -SqlInstance sql2008 | Out-GridView -PassThru | Remove-DbaTrace\r\n\r\nStops and removes selected traces on sql2008\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Remove-DbaXESession",
        "Description":  "This script removes Extended Events sessions on a SQL Server instance.",
        "Tags":  [
                     "ExtendedEvent",
                     "XE",
                     "XEvent"
                 ],
        "Synopsis":  "Removes Extended Events sessions.",
        "Name":  "Remove-DbaXESession",
        "Links":  "https://dbatools.io/Remove-DbaXESession",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaXESession -SqlInstance sql2012 -AllSessions\r\n\r\nRemoves all Extended Event Session on the sqlserver2014 instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eRemove-DbaXESession -SqlInstance sql2012 -Session xesession1,xesession2\r\n\r\nRemoves the xesession1 and xesession2 Extended Event sessions.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaXESession -SqlInstance sql2017 | Remove-DbaXESession -Confirm:$false\r\n\r\nRemoves all sessions from sql2017, bypassing prompts.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaXESession -SqlInstance sql2012 -Session xesession1 | Remove-DbaXESession\r\n\r\nRemoves the sessions returned from the Get-DbaXESession function.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Remove-DbaXESmartTarget",
        "Description":  "Removes an XESmartTarget PowerShell Job.",
        "Tags":  [
                     "ExtendedEvent",
                     "XE",
                     "XEvent"
                 ],
        "Synopsis":  "Removes an XESmartTarget PowerShell Job.",
        "Name":  "Remove-DbaXESmartTarget",
        "Links":  "https://dbatools.io/Remove-DbaXESmartTarget\nhttps://github.com/spaghettidba/XESmartTarget/wiki",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaXESmartTarget | Remove-DbaXESmartTarget\r\n\r\nRemoves all XESmartTarget jobs.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaXESmartTarget | Where-Object Id -eq 2 | Remove-DbaXESmartTarget\r\n\r\nRemoves a specific XESmartTarget job.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Rename-DbaDatabase",
        "Description":  "Can change every database metadata that can be renamed.\nThe ultimate goal is choosing to have a default template to enforce in your environment\nso your naming convention for every bit can be put in place in no time.\nThe process is as follows (it follows the hierarchy of the entities):\n    - database name is changed (optionally, forcing users out)\n    - filegroup name(s) are changed accordingly\n    - logical name(s) are changed accordingly\n    - physical file(s) are changed accordingly\n        - if Move is specified, the database will be taken offline and the move will initiate, then it will be taken online\n        - if Move is not specified, the database remains online (unless SetOffline), and you are in charge of moving files\nIf any of the above fails, the process stops.\nPlease take a backup of your databases BEFORE using this, and remember to backup AFTER (also a FULL backup of master)\n\nIt returns an object for each database with all the renames done, plus hidden properties showing a \"human\" representation of them.\n\nIt\u0027s better you store the resulting object in a variable so you can inspect it in case of issues, e.g. \"$result = Rename-DbaDatabase .....\"\n\nTo get a grasp without worrying of what would happen under the hood, use \"Rename-DbaDatabase .... -Preview | Select-Object *\"",
        "Tags":  [
                     "Database",
                     "Rename"
                 ],
        "Author":  "niphlod",
        "Synopsis":  "Changes database name, logical file names, file group names and physical file names (optionally handling the move). BETA VERSION.",
        "Name":  "Rename-DbaDatabase",
        "Links":  "https://dbatools.io/Rename-DbaDatabase",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -DatabaseName HR2 | select *\r\n\r\nShows the detailed resultset you\u0027ll get renaming the HR database to HR2 without doing anything\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eRename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -DatabaseName HR2\r\n\r\nRenames the HR database to HR2\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabase -SqlInstance sqlserver2014a -Database HR | Rename-DbaDatabase -DatabaseName HR2\r\n\r\nSame as before, but with a piped database (renames the HR database to HR2)\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nRename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -DatabaseName \"dbatools_\u003cDBN\u003e\"\r\n\r\nRenames the HR database to dbatools_HR\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nRename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -DatabaseName \"dbatools_\u003cDBN\u003e_\u003cDATE\u003e\"\r\n\r\nRenames the HR database to dbatools_HR_20170807 (if today is 07th Aug 2017)\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nRename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -FileGroupName \"dbatools_\u003cFGN\u003e\"\r\n\r\nRenames every FileGroup within HR to \"dbatools_[the original FileGroup name]\"\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 7 --------------------------\r\n\r\nRename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -DatabaseName \"dbatools_\u003cDBN\u003e\" -FileGroupName \"\u003cDBN\u003e_\u003cFGN\u003e\"\r\n\r\nRenames the HR database to \"dbatools_HR\", then renames every FileGroup within to \"dbatools_HR_[the original FileGroup \r\nname]\"\r\nNote the \"default recursive behaviour\" here: for all intents and purposes the result of the former can be obtained with \r\ntwo distinct calls:\r\nRename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -FileGroupName \"dbatools_\u003cDBN\u003e_\u003cFGN\u003e\"\r\nRename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -DatabaseName \"dbatools_\u003cDBN\u003e\"\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 8 --------------------------\r\n\r\nRename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -DatabaseName \"dbatools_\u003cDBN\u003e\" -FileName \"\u003cDBN\u003e_\u003cFGN\u003e_\u003cFNN\u003e\"\r\n\r\nRenames the HR database to \"dbatools_HR\" and then all filenames as \"dbatools_HR_[Name of the \r\nFileGroup]_[original_filename]\"\r\nThe db stays online (watch out!). You can then proceed manually to move/copy files by hand, set the db offline and then \r\nonline again to finish the rename process\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 9 --------------------------\r\n\r\nRename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -DatabaseName \"dbatools_\u003cDBN\u003e\" -FileName \r\n\"\u003cDBN\u003e_\u003cFGN\u003e_\u003cFNN\u003e\" -SetOffline\r\n\r\nRenames the HR database to \"dbatools_HR\" and then all filenames as \"dbatools_HR_[Name of the \r\nFileGroup]_[original_filename]\"\r\nThe db is then set offline (watch out!). You can then proceed manually to move/copy files by hand and then set it \r\nonline again to finish the rename process\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 10 --------------------------\r\n\r\nRename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -DatabaseName \"dbatools_\u003cDBN\u003e\" -FileName \r\n\"\u003cDBN\u003e_\u003cFGN\u003e_\u003cFNN\u003e\" -Move\r\n\r\nRenames the HR database to \"dbatools_HR\" and then all filenames as \"dbatools_HR_[Name of the \r\nFileGroup]_[original_filename]\"\r\nThe db is then set offline (watch out!). The function tries to do a simple rename and then sets the db online again to \r\nfinish the rename process\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Rename-DbaLogin",
        "Description":  "There are times where you might want to rename a login that was copied down, or if the name is not descriptive for what it does.\n\nIt can be a pain to update all of the mappings for a specific user, this does it for you.",
        "Tags":  "Login",
        "Author":  "Mitchell Hamann (@SirCaptainMitch)",
        "Synopsis":  "Rename-DbaLogin will rename login and database mapping for a specified login.",
        "Name":  "Rename-DbaLogin",
        "Links":  "https://dbatools.io/Rename-DbaLogin",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRename-DbaLogin -SqlInstance localhost -Login DbaToolsUser -NewLogin captain\r\n\r\nSQL Login Example\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eRename-DbaLogin -SqlInstance localhost -Login domain\\oldname -NewLogin domain\\newname\r\n\r\nChange the windowsuser login name.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eRename-DbaLogin -SqlInstance localhost -Login dbatoolsuser -NewLogin captain -WhatIf\r\n\r\nWhatIf Example\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Repair-DbaOrphanUser",
        "Description":  "An orphan user is defined by a user that does not have a matching login (Login property = \"\").\n\nIf the matching login exists it must be:\n    Enabled\n    Not a system object\n    Not locked\n    Have the same name that user\n\nYou can drop users that does not have their matching login by specifying the parameter -RemoveNotExisting.",
        "Tags":  "Orphan",
        "Author":  "Claudio Silva (@ClaudioESSilva)",
        "Synopsis":  "Finds orphan users with existing login and remaps them.",
        "Name":  "Repair-DbaOrphanUser",
        "Links":  "https://dbatools.io/Repair-DbaOrphanUser",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRepair-DbaOrphanUser -SqlInstance sql2005\r\n\r\nFinds and repairs all orphan users of all databases present on server \u0027sql2005\u0027\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eRepair-DbaOrphanUser -SqlInstance sqlserver2014a -SqlCredential $cred\r\n\r\nFinds and repair all orphan users in all databases present on server \u0027sqlserver2014a\u0027. SQL credentials are used to \r\nauthenticate to the server.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eRepair-DbaOrphanUser -SqlInstance sqlserver2014a -Database db1, db2\r\n\r\nFinds and repairs all orphan users in both db1 and db2 databases.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eRepair-DbaOrphanUser -SqlInstance sqlserver2014a -Database db1 -Users OrphanUser\r\n\r\nFinds and repairs user \u0027OrphanUser\u0027 in \u0027db1\u0027 database.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eRepair-DbaOrphanUser -SqlInstance sqlserver2014a -Users OrphanUser\r\n\r\nFinds and repairs user \u0027OrphanUser\u0027 on all databases\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eRepair-DbaOrphanUser -SqlInstance sqlserver2014a -RemoveNotExisting\r\n\r\nFinds all orphan users of all databases present on server \u0027sqlserver2014a\u0027. Removes all users that do not have  \r\nmatching Logins.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Repair-DbaServerName",
        "Description":  "When a SQL Server\u0027s host OS is renamed, the SQL Server should be as well. This helps with Availability Groups and Kerberos.\n\nThis command renames @@SERVERNAME to match with the Windows name. The new name is automatically determined. It does not matter if you use an alias to connect to the SQL instance.\n\nIf the automatically determined new name matches the old name, the command will not run.\n\nhttps://www.mssqltips.com/sqlservertip/2525/steps-to-change-the-server-name-for-a-sql-server-machine/",
        "Tags":  "SPN",
        "Synopsis":  "Renames @@SERVERNAME to match with the Windows name.",
        "Name":  "Repair-DbaServerName",
        "Links":  "https://dbatools.io/Repair-DbaServerName",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRepair-DbaServerName -SqlInstance sql2014\r\n\r\nChecks to see if the server name is updatable and changes the name with a number of prompts.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eRepair-DbaServerName -SqlInstance sql2014 -AutoFix\r\n\r\nChecks to see if the server name is updatable and automatically performs the change. Replication or mirroring will be \r\nbroken if necessary.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eRepair-DbaServerName -SqlInstance sql2014 -AutoFix -Force\r\n\r\nChecks to see if the server name is updatable and automatically performs the change, bypassing most prompts and \r\nconfirmations. Replication or mirroring will be broken if necessary.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Reset-DbaAdmin",
        "Description":  "This function allows administrators to regain access to local or remote SQL Servers by either resetting the sa password, adding the sysadmin role to existing login, or adding a new login (SQL or Windows) and granting it sysadmin privileges.\n\nThis is accomplished by stopping the SQL services or SQL Clustered Resource Group, then restarting SQL via the command-line using the /mReset-DbaAdmin parameter which starts the server in Single-User mode and only allows this script to connect.\n\nOnce the service is restarted, the following tasks are performed:\n- Login is added if it doesn\u0027t exist\n- If login is a Windows User, an attempt is made to ensure it exists\n- If login is a SQL Login, password policy will be set to OFF when creating the login, and SQL Server authentication will be set to Mixed Mode.\n- Login will be enabled and unlocked\n- Login will be added to sysadmin role\n\nIf failures occur at any point, a best attempt is made to restart the SQL Server.\n\nIn order to make this script as portable as possible, System.Data.SqlClient and Get-WmiObject are used (as opposed to requiring the Failover Cluster Admin tools or SMO).\n\nIf using this function against a remote SQL Server, ensure WinRM is configured and accessible. If this is not possible, run the script locally.\n\nTested on Windows XP, 7, 8.1, Server 2012 and Windows Server Technical Preview 2.\nTested on SQL Server 2005 SP4 through 2016 CTP2.",
        "Tags":  "WSMan",
        "Author":  "Chrissy LeMaire (@cl), netnerds.net",
        "Synopsis":  "This function allows administrators to regain access to SQL Servers in the event that passwords or access was lost.\n\nSupports SQL Server 2005 and above. Windows administrator access is required.",
        "Name":  "Reset-DbaAdmin",
        "Links":  "https://dbatools.io/Reset-DbaAdmin",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eReset-DbaAdmin -SqlInstance sqlcluster\r\n\r\nPrompts for password, then resets the \"sa\" account password on sqlcluster.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eReset-DbaAdmin -SqlInstance sqlserver\\sqlexpress -Login ad\\administrator\r\n\r\nPrompts user to confirm that they understand the SQL Service will be restarted.\r\n\r\nAdds the domain account \"ad\\administrator\" as a sysadmin to the SQL instance.\r\nIf the account already exists, it will be added to the sysadmin role.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eReset-DbaAdmin -SqlInstance sqlserver\\sqlexpress -Login sqladmin -Force\r\n\r\nSkips restart confirmation, prompts for password, then adds a SQL Login \"sqladmin\" with sysadmin privileges.\r\nIf the account already exists, it will be added to the sysadmin role and the password will be reset.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Resolve-DbaNetworkName",
        "Description":  "Retrieves the IPAddress, ComputerName from one computer.\nThe object can be used to take action against its name or IPAddress.\n\nFirst ICMP is used to test the connection, and get the connected IPAddress.\n\nMultiple protocols (e.g. WMI, CIM, etc) are attempted before giving up.\n\nImportant: Remember that FQDN doesn\u0027t always match \"ComputerName dot Domain\" as AD intends.\n    There are network setup (google \"disjoint domain\") where AD and DNS do not match.\n    \"Full computer name\" (as reported by sysdm.cpl) is the only match between the two,\n    and it matches the \"DNSHostName\"  property of the computer object stored in AD.\n    This means that the notation of FQDN that matches \"ComputerName dot Domain\" is incorrect\n    in those scenarios.\n    In other words, the \"suffix\" of the FQDN CAN be different from the AD Domain.\n\n    This cmdlet has been providing good results since its inception but for lack of useful\n    names some doubts may arise.\n    Let this clear the doubts:\n    - InputName: whatever has been passed in\n    - ComputerName: hostname only\n    - IPAddress: IP Address\n    - DNSHostName: hostname only, coming strictly from DNS (as reported from the calling computer)\n    - DNSDomain: domain only, coming strictly from DNS (as reported from the calling computer)\n    - Domain: domain only, coming strictly from AD (i.e. the domain the ComputerName is joined to)\n    - DNSHostEntry: Fully name as returned by DNS [System.Net.Dns]::GetHostEntry\n    - FQDN: \"legacy\" notation of ComputerName \"dot\" Domain (coming from AD)\n    - FullComputerName: Full name as configured from within the Computer (i.e. the only secure match between AD and DNS)\n\nSo, if you need to use something, go with FullComputerName, always, as it is the most correct in every scenario.",
        "Tags":  [
                     "Network",
                     "Resolve"
                 ],
        "Author":  "Klaas Vandenberghe ( @PowerDBAKlaas )",
        "Synopsis":  "Returns information about the network connection of the target computer including NetBIOS name, IP Address, domain name and fully qualified domain name (FQDN).",
        "Name":  "Resolve-DbaNetworkName",
        "Links":  "https://dbatools.io/Resolve-DbaNetworkName",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eResolve-DbaNetworkName -ComputerName ServerA\r\n\r\nReturns a custom object displaying InputName, ComputerName, IPAddress, DNSHostName, DNSDomain, Domain, DNSHostEntry, \r\nFQDN, DNSHostEntry for ServerA\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eResolve-DbaNetworkName -SqlInstance sql2016\\sqlexpress\r\n\r\nReturns a custom object displaying InputName, ComputerName, IPAddress, DNSHostName, DNSDomain, Domain, DNSHostEntry, \r\nFQDN, DNSHostEntry  for the SQL instance sql2016\\sqlexpress\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eResolve-DbaNetworkName -SqlInstance sql2016\\sqlexpress, sql2014\r\n\r\nReturns a custom object displaying InputName, ComputerName, IPAddress, DNSHostName, DNSDomain, Domain, DNSHostEntry, \r\nFQDN, DNSHostEntry  for the SQL instance sql2016\\sqlexpress and sql2014\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRegisteredServer -SqlInstance sql2014 | Resolve-DbaNetworkName\r\n\r\nReturns a custom object displaying InputName, ComputerName, IPAddress, DNSHostName, Domain, FQDN for all SQL Servers \r\nreturned by Get-DbaRegisteredServer\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Restart-DbaSqlService",
        "Description":  "Restarts the SQL Server related services on one or more computers. Will follow SQL Server service dependencies.\n\nRequires Local Admin rights on destination computer(s).",
        "Tags":  [
                     "Service",
                     "SqlServer",
                     "Instance",
                     "Connect"
                 ],
        "Author":  "Kirill Kravtsov( @nvarscar )",
        "Synopsis":  "Restarts SQL Server services on a computer.",
        "Name":  "Restart-DbaSqlService",
        "Links":  "https://dbatools.io/Restart-DbaSqlService",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRestart-DbaSqlService -ComputerName sqlserver2014a\r\n\r\nRestarts the SQL Server related services on computer sqlserver2014a.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e\u0027sql1\u0027,\u0027sql2\u0027,\u0027sql3\u0027| Get-DbaSqlService | Restart-DbaSqlService\r\n\r\nGets the SQL Server related services on computers sql1, sql2 and sql3 and restarts them.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eRestart-DbaSqlService -ComputerName sql1,sql2 -Instance MSSQLSERVER\r\n\r\nRestarts the SQL Server services related to the default instance MSSQLSERVER on computers sql1 and sql2.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eRestart-DbaSqlService -ComputerName $MyServers -Type SSRS\r\n\r\nRestarts the SQL Server related services of type \"SSRS\" (Reporting Services) on computers in the variable MyServers.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eRestart-DbaSqlService -ComputerName sql1 -Type Engine -Force\r\n\r\nRestarts SQL Server database engine services on sql1 forcing dependent SQL Server Agent services to restart as well.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Restore-DbaBackupFromDirectory",
        "Description":  "Many SQL Server database administrators use Ola Hallengren\u0027s SQL Server Maintenance Solution which can be found at http://ola.hallengren.com\n\nHallengren uses a predictable backup structure which made it relatively easy to create a script that can restore an entire SQL Server database instance, down to the master database (next version), to a new server. This script is intended to be used in the event that the originating SQL Server becomes unavailable, thus rendering my other SQL restore script (http://goo.gl/QmfQ6s) ineffective.",
        "Tags":  [
                     "DisasterRecovery",
                     "Backup",
                     "Restore"
                 ],
        "Synopsis":  "Restores SQL Server databases from the backup directory structure created by Ola Hallengren\u0027s database maintenance scripts. Different structures coming soon.",
        "Name":  "Restore-DbaBackupFromDirectory",
        "Links":  "https://dbatools.io/Restore-SqlBackupFromDirectory",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRestore-SqlBackupFromDirectory -SqlInstance sqlcluster -Path \\\\fileserver\\share\\sqlbackups\\SQLSERVER2014A\r\n\r\nAll user databases contained within \\\\fileserver\\share\\sqlbackups\\SQLSERVERA will be restored to sqlcluster, down the \r\nmost recent full/differential/logs.\r\n\r\n\r\nRequires -Version 3.0\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Restore-DbaDatabase",
        "Description":  "Upon being passed a list of potential backups files this command will scan the files, select those that contain SQL Server\nbackup sets. It will then filter those files down to a set that can perform the requested restore, checking that we have a\nfull restore chain to the point in time requested by the caller.\n\nThe function defaults to working on a remote instance. This means that all paths passed in must be relative to the remote instance.\nXpDirTree will be used to perform the file scans\n\n\nVarious means can be used to pass in a list of files to be considered. The default is to non recursively scan the folder\npassed in.",
        "Tags":  [
                     "DisasterRecovery",
                     "Backup",
                     "Restore"
                 ],
        "Author":  "Stuart Moore (@napalmgram), stuart-moore.com",
        "Synopsis":  "Restores a SQL Server Database from a set of backupfiles",
        "Name":  "Restore-DbaDatabase",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRestore-DbaDatabase -SqlInstance server1\\instance1 -Path \\\\server2\\backups\r\n\r\nScans all the backup files in \\\\server2\\backups, filters them and restores the database to server1\\instance1\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eRestore-DbaDatabase -SqlInstance server1\\instance1 -Path \\\\server2\\backups -MaintenanceSolutionBackup \r\n-DestinationDataDirectory c:\\restores\r\n\r\nScans all the backup files in \\\\server2\\backups$ stored in an Ola Hallengren style folder structure,\r\nfilters them and restores the database to the c:\\restores folder on server1\\instance1\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-ChildItem c:\\SQLbackups1\\, \\\\server\\sqlbackups2 | Restore-DbaDatabase -SqlInstance server1\\instance1\r\n\r\nTakes the provided files from multiple directories and restores them on  server1\\instance1\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003e$RestoreTime = Get-Date(\u002711:19 23/12/2016\u0027)\r\n\r\nRestore-DbaDatabase -SqlInstance server1\\instance1 -Path \\\\server2\\backups -MaintenanceSolutionBackup \r\n-DestinationDataDirectory c:\\restores -RestoreTime $RestoreTime\r\n\r\nScans all the backup files in \\\\server2\\backups stored in an Ola Hallengren style folder structure,\r\nfilters them and restores the database to the c:\\restores folder on server1\\instance1 up to 11:19 23/12/2016\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eRestore-DbaDatabase -SqlInstance server1\\instance1 -Path \\\\server2\\backups -DestinationDataDirectory c:\\restores \r\n-OutputScriptOnly | Select-Object -ExpandProperty Tsql | Out-File -Filepath c:\\scripts\\restore.sql\r\n\r\nScans all the backup files in \\\\server2\\backups stored in an Ola Hallengren style folder structure,\r\nfilters them and generate the T-SQL Scripts to restore the database to the latest point in time,\r\nand then stores the output in a file for later retrieval\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eRestore-DbaDatabase -SqlInstance server1\\instance1 -Path c:\\backups -DestinationDataDirectory c:\\DataFiles \r\n-DestinationLogDirectory c:\\LogFile\r\n\r\nScans all the files in c:\\backups and then restores them onto the SQL Server Instance server1\\instance1, placing data \r\nfiles\r\nc:\\DataFiles and all the log files into c:\\LogFiles\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 7 --------------------------\r\n\r\nPS C:\\\u003eRestore-DbaDatabase -SqlInstance server1\\instance1 -Path http://demo.blob.core.windows.net/backups/dbbackup.bak \r\n-AzureCredential MyAzureCredential\r\n\r\nWill restore the backup held at  http://demo.blob.core.windows.net/backups/dbbackup.bak to server1\\instance1. The \r\nconnection to Azure will be made using the\r\ncredential MyAzureCredential held on instance Server1\\instance1\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 8 --------------------------\r\n\r\nPS C:\\\u003e$File = Get-ChildItem c:\\backups, \\\\server1\\backups -recurse\r\n\r\n$File | Restore-DbaDatabase -SqlInstance Server1\\Instance -useDestinationDefaultDirectories\r\n\r\nThis will take all of the files found under the folders c:\\backups and \\\\server1\\backups, and pipeline them into\r\nRestore-DbaDatabase. Restore-DbaDatabase will then scan all of the files, and restore all of the databases included\r\nto the latest point in time covered by their backups. All data and log files will be moved to the default SQL Server\r\nfolder for those file types as defined on the target instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 9 --------------------------\r\n\r\nPS C:\\\u003e$files = Get-ChildItem C:\\dbatools\\db1\r\n\r\n#Restore database to a point in time\r\n$files | Restore-DbaDatabase -SqlInstance server\\instance1 `\r\n            -DestinationFilePrefix prefix -DatabaseName Restored  `\r\n            -RestoreTime (get-date \"14:58:30 22/05/2017\") `\r\n            -NoRecovery -WithReplace -StandbyDirectory C:\\dbatools\\standby\r\n\r\n#It\u0027s in standby so we can peek at it\r\nInvoke-Sqlcmd2 -ServerInstance server\\instance1 -Query \"select top 1 * from Restored.dbo.steps order by dt desc\"\r\n\r\n#Not quite there so let\u0027s roll on a bit:\r\n$files | Restore-DbaDatabase -SqlInstance server\\instance1 `\r\n            -DestinationFilePrefix prefix -DatabaseName Restored `\r\n            -continue -WithReplace -RestoreTime (get-date \"15:09:30 22/05/2017\") `\r\n            -StandbyDirectory C:\\dbatools\\standby\r\n\r\nInvoke-Sqlcmd2 -ServerInstance server\\instance1 -Query \"select top 1 * from restored.dbo.steps order by dt desc\"\r\n\r\nRestore-DbaDatabase -SqlInstance server\\instance1 `\r\n            -DestinationFilePrefix prefix -DatabaseName Restored `\r\n            -continue -WithReplace\r\n\r\nIn this example we step through the backup files held in c:\\dbatools\\db1 folder.\r\nFirst we restore the database to a point in time in standby mode. This means we can check some details in the databases\r\nWe then roll it on a further 9 minutes to perform some more checks\r\nAnd finally we continue by rolling it all the way forward to the latest point in the backup.\r\nAt each step, only the log files needed to roll the database forward are restored.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 10 --------------------------\r\n\r\nPS C:\\\u003eRestore-DbaDatabase -SqlInstance server\\instance1 -Path c:\\backups -DatabaseName example1 -WithNoRecovery\r\n\r\nRestore-DbaDatabase -SqlInstance server\\instance1 -Recover -DatabaseName example1\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 11 --------------------------\r\n\r\nPS C:\\\u003e$SuspectPage = Get-DbaSuspectPage -SqlInstance server\\instance1 -Database ProdFinance\r\n\r\nGet-DbaBackupHistory - SqlInstance server\\instance1 -Database -ProdFinance -Last | Restore-DbaDatabase -PageRestore \r\n$SuspectPage -PageRestoreTailFolder c:\\temp -TrustDbBackupHistory -AllowContinues\r\n\r\nGets a list of Suspect Pages using Get-DbaSuspectPage. The uses Get-DbaBackupHistory and Restore-DbaDatabase to perform \r\na restore of the suspect pages and bring them up to date\r\nIf server\\instance1 is Enterprise edition this will be done online, if not it will be performed offline\r\nAllowContinue is required to make sure we cope with existing files\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Restore-DbaDbCertificate",
        "Description":  "Imports certificates from.cer files using SMO.",
        "Tags":  [
                     "Migration",
                     "Certificate"
                 ],
        "Author":  "Jess Pomfret (@jpomfret)",
        "Synopsis":  "Imports certificates from .cer files using SMO.",
        "Name":  "Restore-DbaDbCertificate",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRestore-DbaDbCertificate -SqlInstance Server1 -Path \\\\Server1\\Certificates -password (ConvertTo-SecureString \r\n-force -AsPlainText GoodPass1234!!)\r\n\r\nImports all the certificates in the specified path.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Restore-DbaDbSnapshot",
        "Description":  "Restores the database from the snapshot, discarding every modification made to the database\nNB: Restoring to a snapshot will result in every other snapshot of the same database to be dropped\nIt also fixes some long-standing bugs in SQL Server when restoring from snapshots",
        "Tags":  [
                     "Snapshot",
                     "Backup",
                     "Restore",
                     "Database"
                 ],
        "Author":  "niphlod",
        "Synopsis":  "Restores databases from snapshots",
        "Name":  "Restore-DbaDbSnapshot",
        "Links":  "https://dbatools.io/Restore-DbaDbSnapshot",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eRestore-DbaDbSnapshot -SqlInstance sql2014 -Database HR, Accounting\r\n\r\nRestores HR and Accounting databases using the latest snapshot available\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eRestore-DbaDbSnapshot -SqlInstance sql2014 -Database HR -Force\r\n\r\nRestores HR database from latest snapshot and kills any active connections in the database on sql2014.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbSnapshot -SqlInstance sql2016 -Database HR | Restore-DbaDbSnapshot -Force\r\n\r\nRestores HR database from latest snapshot and kills any active connections in the database on sql2016.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDbSnapshot -SqlInstance sql2016 | Out-GridView -Passthru | Restore-DbaDbSnapshot\r\n\r\nAllows the selection of snapshots on sql2016 to restore\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eRestore-DbaDbSnapshot -SqlInstance sql2014 -Snapshot HR_snap_20161201, Accounting_snap_20161101\r\n\r\nRestores databases from snapshots named HR_snap_20161201 and Accounting_snap_20161101\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Save-DbaDiagnosticQueryScript",
        "Description":  "The dbatools module will have the diagnostic queries pre-installed. Use this only to update to a more recent version or specific versions.\n\nThis function is mainly used by Invoke-DbaDiagnosticQuery, but can also be used independently to download the Glenn Berry DMV scripts.\n\nUse this function to pre-download the scripts from a device with an Internet connection.\n\nThe function Invoke-DbaDiagnosticQuery will try to download these scripts automatically, but it obviously needs an internet connection to do that.",
        "Tags":  [
                     "Diagnostic",
                     "DMV",
                     "Troubleshooting"
                 ],
        "Author":  "André Kamman (@AndreKamman), http://clouddba.io",
        "Synopsis":  "Save-DbaDiagnosticQueryScript downloads the most recent version of all Glenn Berry DMV scripts",
        "Name":  "Save-DbaDiagnosticQueryScript",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eSave-DbaDiagnosticQueryScript -Path c:\\temp\r\n\r\nDownloads the most recent version of all Glenn Berry DMV scripts to the specified location.\r\nIf Path is not specified, the \"My Documents\" location will be used.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Select-DbaBackupInformation",
        "Description":  "Select-DbaBackupInformation filters out a subset of backups from the dbatools backup history object with parameters supplied.",
        "Tags":  [
                     "Backup",
                     "Restore"
                 ],
        "Author":  "Stuart Moore (@napalmgram stuart-moore.com )",
        "Synopsis":  "Select a subset of backups from a dbatools backup history object",
        "Name":  "Select-DbaBackupInformation",
        "Links":  "https://dbatools.io/Select-DbaBackupInformation",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003e$Backups = Get-DbaBackupInformation -SqlInstance Server1 -Path \\\\server1\\backups$\r\n\r\n$FilteredBackups = $Backups | Select-DbaBackupInformation -RestoreTime (Get-Date).AddHours(-1)\r\n\r\nReturns all backups needed to restore all the backups in \\\\server1\\backups$ to 1 hour ago\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e$Backups = Get-DbaBackupInformation -SqlInstance Server1 -Path \\\\server1\\backups$\r\n\r\n$FilteredBackups = $Backups | Select-DbaBackupInformation -RestoreTime (Get-Date).AddHours(-1) -DatabaseName ProdFinance\r\n\r\nReturns all the backups needed to restore Database ProdFinance to an hour ago\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003e$Backups = Get-DbaBackupInformation -SqlInstance Server1 -Path \\\\server1\\backups$\r\n\r\n$FilteredBackups = $Backups | Select-DbaBackupInformation -RestoreTime (Get-Date).AddHours(-1) -IgnoreLogs\r\n\r\nReturns all the backups in \\\\server1\\backups$ to restore to as close prior to 1 hour ago as can be managed with only \r\nfull and differential backups\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003e$Backups = Get-DbaBackupInformation -SqlInstance Server1 -Path \\\\server1\\backups$\r\n\r\n$FilteredBackups = $Backups | Select-DbaBackupInformation -RestoreTime (Get-Date).AddHours(-1) -IgnoreDiffs\r\n\r\nReturns all the backups in \\\\server1\\backups$ to restore to 1 hour ago using only Full and Diff backups.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Select-DbaObject",
        "Description":  "Wrapper around Select-Object, extends property parameter.\n\nThis function allows specifying in-line transformation of the properties specified without needing to use complex hashtables.\nWithout removing the ability to specify just those hashtables.\n\nSee the description of the Property parameter for an exhaustive list of legal notations.",
        "Synopsis":  "Wrapper around Select-Object, extends property parameter.",
        "Name":  "Select-DbaObject",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-ChildItem | Select-DbaObject Name, \"Length as Size\"\r\n\r\nSelects the properties Name and Length, renaming Length to Size in the process.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eImport-Csv .\\file.csv | Select-DbaObject Name, \"Length as Size to DbaSize\"\r\n\r\nSelects the properties Name and Length, renaming Length to Size and converting it to [DbaSize] (a userfriendly \r\nrepresentation of size numbers)\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003e$obj = [PSCustomObject]@{ Name = \"Foo\" }\r\n\r\nPS C:\\\u003e Get-ChildItem | Select-DbaObject FullName, Length, \"Name from obj\"\r\n\r\nSelects the properties FullName and Length from the input and the Name property from the object stored in $obj\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003e$list = @()\r\n\r\nPS C:\\\u003e $list += [PSCustomObject]@{ Type = \"Foo\"; ID = 1 }\r\nPS C:\\\u003e $list += [PSCustomObject]@{ Type = \"Bar\"; ID = 2 }\r\nPS C:\\\u003e $obj | Select-DbaObject Name, \"ID from list WHERE Type = Name\"\r\n\r\nThis allows you to LEFT JOIN contents of another variable.\r\nNote that it can only do simple property-matching at this point.\r\n\r\nIt will select Name from the objects stored in $obj, and for each of those the ID Property on any object in $list that \r\nhas a Type property of equal value as Name on the input.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Set-DbaAgentAlert",
        "Description":  "Set-DbaAgentAlert updates an alert in the SQL Server Agent with parameters supplied.",
        "Tags":  [
                     "Agent",
                     "Alert"
                 ],
        "Author":  "Garry Bargsley (@gbargsley, garrybargsley.com)",
        "Synopsis":  "Set-DbaAgentAlert updates a the status of a SQL Agent Alert.",
        "Name":  "Set-DbaAgentAlert",
        "Links":  "https://dbatools.io/Set-DbaAgentAlert",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaAgentAlert -SqlInstance sql1 -Alert \u0027Severity 025: Fatal Error\u0027 -Disabled\r\n\r\nChanges the alert to disabled.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaAgentAlert -SqlInstance sql1 -Alert \u0027Severity 025: Fatal Error\u0027, \u0027Error Number 825\u0027, \u0027Error Number 824\u0027 \r\n-Enabled\r\n\r\nChanges multiple alerts to enabled.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaAgentAlert -SqlInstance sql1, sql2, sql3 -Alert \u0027Severity 025: Fatal Error\u0027, \u0027Error Number 825\u0027, \u0027Error \r\nNumber 824\u0027 -Enabled\r\n\r\nChanges multiple alerts to enabled on multiple servers.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaAgentAlert -SqlInstance sql1 -Alert \u0027Severity 025: Fatal Error\u0027 -Disabled -Whatif\r\n\r\nDoesn\u0027t Change the alert but shows what would happen.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Set-DbaAgentJob",
        "Description":  "Set-DbaAgentJob updates a job in the SQL Server Agent with parameters supplied.",
        "Tags":  [
                     "Agent",
                     "Job"
                 ],
        "Author":  "Sander Stad (@sqlstad, sqlstad.nl)",
        "Synopsis":  "Set-DbaAgentJob updates a job.",
        "Name":  "Set-DbaAgentJob",
        "Links":  "https://dbatools.io/Set-DbaAgentJob",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaAgentJob sql1 -Job Job1 -Disabled\r\n\r\nChanges the job to disabled\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaAgentJob sql1 -Job Job1 -OwnerLogin user1\r\n\r\nChanges the owner of the job\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaAgentJob -SqlInstance sql1 -Job Job1 -EventLogLevel OnSuccess\r\n\r\nChanges the job and sets the notification to write to the Windows Application event log on success\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaAgentJob -SqlInstance sql1 -Job Job1 -EmailLevel OnFailure -EmailOperator dba\r\n\r\nChanges the job and sets the notification to send an e-mail to the e-mail operator\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaAgentJob -SqlInstance sql1 -Job Job1, Job2, Job3 -Enabled\r\n\r\nChanges multiple jobs to enabled\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaAgentJob -SqlInstance sql1, sql2, sql3 -Job Job1, Job2, Job3 -Enabled\r\n\r\nChanges multiple jobs to enabled on multiple servers\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 7 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaAgentJob -SqlInstance sql1 -Job Job1 -Description \u0027Just another job\u0027 -Whatif\r\n\r\nDoesn\u0027t Change the job but shows what would happen.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 8 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaAgentJob -SqlInstance sql1, sql2, sql3 -Job \u0027Job One\u0027 -Description \u0027Job One\u0027\r\n\r\nChanges a job with the name \"Job1\" on multiple servers to have another description\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 9 --------------------------\r\n\r\nPS C:\\\u003esql1, sql2, sql3 | Set-DbaAgentJob -Job Job1 -Description \u0027Job One\u0027\r\n\r\nChanges a job with the name \"Job1\" on multiple servers to have another description using pipe line\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Set-DbaAgentJobCategory",
        "Description":  "Set-DbaAgentJobCategory makes it possible to change a job category.",
        "Tags":  [
                     "Agent",
                     "Job",
                     "JobCategory"
                 ],
        "Author":  "Sander Stad (@sqlstad, sqlstad.nl)",
        "Synopsis":  "Set-DbaAgentJobCategory changes a job category.",
        "Name":  "Set-DbaAgentJobCategory",
        "Links":  "https://dbatools.io/Set-DbaAgentJobCategory",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaAgentJobCategory -SqlInstance sql1 -Category \u0027Category 1\u0027 -NewName \u0027Category 2\u0027\r\n\r\nChange the name of the category from \u0027Category 1\u0027 to \u0027Category 2\u0027.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaAgentJobCategory -SqlInstance sql1, sql2 -Category Category1, Category2 -NewName cat1, cat2\r\n\r\nRename multiple jobs in one go on multiple servers.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Set-DbaAgentJobOutputFile",
        "Description":  "Sets the Output File for a step of an agent job with the Job Names and steps provided dynamically if required",
        "Tags":  [
                     "Agent",
                     "Job",
                     "SqlAgent"
                 ],
        "Author":  "Rob Sewell (https://sqldbawithabeard.com)",
        "Synopsis":  "Set the output file for a step within an Agent job.",
        "Name":  "Set-DbaAgentJobOutputFile",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaAgentJobOutputFile -SqlInstance SERVERNAME -JobName \u0027The Agent Job\u0027 -OutPutFile \r\nE:\\Logs\\AgentJobStepOutput.txt\r\n\r\nSets the Job step for The Agent job on SERVERNAME to E:\\Logs\\AgentJobStepOutput.txt\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Set-DbaAgentJobStep",
        "Description":  "Set-DbaAgentJobStep updates a job step in the SQL Server Agent with parameters supplied.",
        "Tags":  [
                     "Agent",
                     "Job",
                     "JobStep"
                 ],
        "Author":  "Sander Stad (@sqlstad, sqlstad.nl)",
        "Synopsis":  "Set-DbaAgentJobStep updates a job step.",
        "Name":  "Set-DbaAgentJobStep",
        "Links":  "https://dbatools.io/Set-DbaAgentJobStep",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaAgentJobStep -SqlInstance sql1 -Job Job1 -StepName Step1 -NewName Step2\r\n\r\nChanges the name of the step in \"Job1\" with the name Step1 to Step2\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaAgentJobStep -SqlInstance sql1 -Job Job1 -StepName Step1 -Database msdb\r\n\r\nChanges the database of the step in \"Job1\" with the name Step1 to msdb\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaAgentJobStep -SqlInstance sql1 -Job Job1, Job2 -StepName Step1 -Database msdb\r\n\r\nChanges job steps in multiple jobs with the name Step1 to msdb\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaAgentJobStep -SqlInstance sql1, sql2, sql3 -Job Job1, Job2 -StepName Step1 -Database msdb\r\n\r\nChanges job steps in multiple jobs on multiple servers with the name Step1 to msdb\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaAgentJobStep -SqlInstance sql1, sql2, sql3 -Job Job1 -StepName Step1 -Database msdb\r\n\r\nChanges the database of the step in \"Job1\" with the name Step1 to msdb for multiple servers\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003esql1, sql2, sql3 | Set-DbaAgentJobStep -Job Job1 -StepName Step1 -Database msdb\r\n\r\nChanges the database of the step in \"Job1\" with the name Step1 to msdb for multiple servers using pipeline\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Set-DbaAgentSchedule",
        "Description":  "Set-DbaAgentSchedule will help update a schedule for a job. It does not attach the schedule to a job.",
        "Tags":  [
                     "Agent",
                     "Job",
                     "JobStep"
                 ],
        "Author":  "Sander Stad (@sqlstad, sqlstad.nl)",
        "Synopsis":  "Set-DbaAgentSchedule updates a schedule in the msdb database.",
        "Name":  "Set-DbaAgentSchedule",
        "Links":  "https://dbatools.io/Set-DbaAgentSchedule",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaAgentSchedule -SqlInstance sql1 -Job Job1 -ScheduleName daily -Enabled\r\n\r\nChanges the schedule for Job1 with the name \u0027daily\u0027 to enabled\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaAgentSchedule -SqlInstance sql1 -Job Job1 -ScheduleName daily -NewName weekly -FrequencyType Weekly \r\n-FrequencyInterval Monday, Wednesday, Friday\r\n\r\nChanges the schedule for Job1 with the name daily to have a new name weekly\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaAgentSchedule -SqlInstance sql1 -Job Job1, Job2, Job3 -ScheduleName daily -StartTime \u0027230000\u0027\r\n\r\nChanges the start time of the schedule for Job1 to 11 PM for multiple jobs\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaAgentSchedule -SqlInstance sql1, sql2, sql3 -Job Job1 -ScheduleName daily -Enabled\r\n\r\nChanges the schedule for Job1 with the name daily to enabled on multiple servers\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003esql1, sql2, sql3 | Set-DbaAgentSchedule -Job Job1 -ScheduleName \u0027daily\u0027 -Enabled\r\n\r\nChanges the schedule for Job1 with the name \u0027daily\u0027 to enabled on multiple servers using pipe line\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Set-DbaCmConnection",
        "Description":  "Configures a connection object for use in remote computer management.\nThis function will either create new records for computers that have no connection registered so far, or it will configure existing connections if already present.\n\nAs such it can be handy in making bulk-edits on connections or manually adjusting some settings.",
        "Tags":  [
                     "ComputerManagement",
                     "CIM"
                 ],
        "Author":  "Fred Winmann (@FredWeinmann)",
        "Synopsis":  "Configures a connection object for use in remote computer management.",
        "Name":  "Set-DbaCmConnection",
        "Links":  "https://dbatools.io/set-DbaCmConnection",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaCmConnection sql2014 | Set-DbaCmConnection -ClearBadCredential -UseWindowsCredentials\r\n\r\nRetrieves the already existing connection to sql2014, removes the list of not working credentials and configures it to \r\ndefault to the credentials of the logged on user.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaCmConnection | Set-DbaCmConnection -RemoveBadCredential $cred\r\n\r\nRemoves the credentials stored in $cred from all connections\u0027 list of \"known to not work\" credentials.\r\nHandy to update changes in privilege.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaCmConnection | Export-Clixml .\\connections.xml\r\n\r\nImport-Clixml .\\connections.xml | Set-DbaCmConnection -ResetConfiguration\r\n\r\nAt first, the current cached connections are stored in an xml file. At a later time - possibly in the profile when \r\nstarting the console again - those connections are imported again and applied again to the connection cache.\r\n\r\nIn this example, the configuration settings will also be reset, since after reimport those will be set to explicit, \r\nrather than deriving them from the global settings.\r\nIn many cases, using the default settings is desirable. For specific settings, use New-DbaCmConnection as part of the \r\nprofile in order to explicitly configure a connection.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Set-DbaConfig",
        "Description":  "This function creates or changes configuration values.\nThese are used in dbatools to provide dynamic configuration information outside the PowerShell variable system.",
        "Tags":  [
                     "Config",
                     "Module"
                 ],
        "Author":  "Friedrich Weinmann",
        "Synopsis":  "Sets configuration entries.",
        "Name":  "Set-DbaConfig",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaConfig -Name \u0027User\u0027 -Value \"Friedrich\" -Description \"The user under which the show must go on.\"\r\n\r\nCreates a configuration entry named \"User\" with the value \"Friedrich\"\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaConfig -Name \u0027mymodule.User\u0027 -Value \"Friedrich\" -Description \"The user under which the show must go on.\" \r\n-Handler $scriptBlock -Initialize -Validation String\r\n\r\nCreates a configuration entry ...\r\n- Named \"mymodule.user\"\r\n- With the value \"Friedrich\"\r\n- It adds a description as noted\r\n- It registers the scriptblock stored in $scriptBlock as handler\r\n- It initializes the script. This block only executes the first time a it is run like this. Subsequent calls will be \r\nignored.\r\n- It registers the basic string input type validator\r\nThis is the default example for modules using the configuration system.\r\nNote: While the -Handler parameter is optional, it is important to add it at the initial initialize call, if you are \r\nplanning to add it.\r\nOnly then will the system validate previous settings (such as what a user might have placed in his user profile)\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaConfig \u0027ConfigLink\u0027 \u0027https://www.example.com/config.xml\u0027 \u0027Company\u0027 -Hidden\r\n\r\nCreates a configuration entry named \"ConfigLink\" in the \"Company\" module with the value \r\n\u0027https://www.example.com/config.xml\u0027.\r\nThis entry is hidden from casual discovery using Get-Config.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaConfig \u0027Network.Firewall\u0027 \u002710.0.0.2\u0027 -Default\r\n\r\nCreates a configuration entry named \"Firewall\" in the \"Network\" module with the value \u002710.0.0.2\u0027\r\nThis is only set, if the setting does not exist yet. If it does, this command will apply no changes.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Set-DbaDatabaseOwner",
        "Description":  "This function will alter database ownership to match a specified login if their current owner does not match the target login. By default, the target login will be \u0027sa\u0027, but the function will allow the user to specify a different login for  ownership. The user can also apply this to all databases or only to a select list of databases (passed as either a comma separated list or a string array).\n\nBest Practice reference: http://weblogs.sqlteam.com/dang/archive/2008/01/13/Database-Owner-Troubles.aspx",
        "Tags":  [
                     "Database",
                     "Owner",
                     "DbOwner"
                 ],
        "Author":  "Michael Fal (@Mike_Fal), http://mikefal.net",
        "Synopsis":  "Sets database owners with a desired login if databases do not match that owner.",
        "Name":  "Set-DbaDatabaseOwner",
        "Links":  "https://dbatools.io/Set-DbaDatabaseOwner",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaDatabaseOwner -SqlInstance localhost\r\n\r\nSets database owner to \u0027sa\u0027 on all databases where the owner does not match \u0027sa\u0027.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaDatabaseOwner -SqlInstance localhost -TargetLogin DOMAIN\\account\r\n\r\nSets the database owner to DOMAIN\\account on all databases where the owner does not match DOMAIN\\account.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaDatabaseOwner -SqlInstance sqlserver -Database db1, db2\r\n\r\nSets database owner to \u0027sa\u0027 on the db1 and db2 databases if their current owner does not match \u0027sa\u0027.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Set-DbaDatabaseState",
        "Description":  "Sets some common \"states\" on databases:\n- \"RW\" options (ReadOnly, ReadWrite)\n- \"Status\" options (Online, Offline, Emergency, plus a special \"Detached\")\n- \"Access\" options (SingleUser, RestrictedUser, MultiUser)\n\nReturns an object with SqlInstance, Database, RW, Status, Access, Notes\n\nNotes gets filled when something went wrong setting the state",
        "Tags":  [
                     "Database",
                     "State"
                 ],
        "Author":  "niphlod",
        "Synopsis":  "Sets various options for databases, hereby called \"states\"",
        "Name":  "Set-DbaDatabaseState",
        "Links":  "https://dbatools.io/Set-DbaDatabaseState",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaDatabaseState -SqlInstance sqlserver2014a -Database HR -Offline\r\n\r\nSets the HR database as OFFLINE\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaDatabaseState -SqlInstance sqlserver2014a -AllDatabases -Exclude HR -Readonly -Force\r\n\r\nSets all databases of the sqlserver2014a instance, except for HR, as READ_ONLY\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabaseState -SqlInstance sql2016 | Where-Object Status -eq \u0027Offline\u0027 | Set-DbaDatabaseState -Online\r\n\r\nFinds all offline databases and sets them to online\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaDatabaseState -SqlInstance sqlserver2014a -Database HR -SingleUser\r\n\r\nSets the HR database as SINGLE_USER\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaDatabaseState -SqlInstance sqlserver2014a -Database HR -SingleUser -Force\r\n\r\nSets the HR database as SINGLE_USER, dropping all other connections (and rolling back open transactions)\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabase -SqlInstance sqlserver2014a -Database HR | Set-DbaDatabaseState -SingleUser -Force\r\n\r\nGets the databases from Get-DbaDatabase, and sets them as SINGLE_USER, dropping all other connections (and rolling back \r\nopen transactions)\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Set-DbaDbCompression",
        "Description":  "This function sets the appropriate compression recommendation, determined either by using the Tiger Team\u0027s query or set to the CompressionType parameter.\n\nRemember Uptime is critical for the Tiger Team query, the longer uptime, the more accurate the analysis is.\nYou would probably be best if you utilized Get-DbaUptime first, before running this command.\n\nSet-DbaDbCompression script derived from GitHub and the tigertoolbox\n(https://github.com/Microsoft/tigertoolbox/tree/master/Evaluate-Compression-Gains)",
        "Tags":  [
                     "Compression",
                     "Table",
                     "Database"
                 ],
        "Author":  "Jason Squires (@js_0505, [email protected])",
        "Synopsis":  "Sets tables and indexes with preferred compression setting.",
        "Name":  "Set-DbaDbCompression",
        "Links":  "https://dbatools.io/Set-DbaDbCompression",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaDbCompression -SqlInstance localhost -MaxRunTime 60 -PercentCompression 25\r\n\r\nSet the compression run time to 60 minutes and will start the compression of tables/indexes that have a difference of \r\n25% or higher between current and recommended.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaDbCompression -SqlInstance ServerA -Database DBName -CompressionType Page\r\n\r\nUtilizes Page compression for all objects in DBName on ServerA with no time limit.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaDbCompression -SqlInstance ServerA -Database DBName -PercentCompression 25 | Out-GridView\r\n\r\nWill compress tables/indexes within the specified database that would show any % improvement with compression and with \r\nno time limit. The results will be piped into a nicely formated GridView.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003e$testCompression = Test-DbaDbCompression -SqlInstance ServerA -Database DBName\r\n\r\nSet-DbaDbCompression -SqlInstance ServerA -Database DBName -InputObject $testCompression\r\n\r\nGets the compression suggestions from Test-DbaDbCompression into a variable, this can then be reviewed and passed into \r\nSet-DbaDbCompression.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003e$cred = Get-Credential sqladmin\r\n\r\nSet-DbaDbCompression -SqlInstance ServerA -ExcludeDatabase Database -SqlCredential $cred -MaxRunTime 60 \r\n-PercentCompression 25\r\n\r\nSet the compression run time to 60 minutes and will start the compression of tables/indexes for all databases except \r\nthe specified excluded database. Only objects that have a difference of 25% or higher between current and recommended \r\nwill be compressed.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003e$servers = \u0027Server1\u0027,\u0027Server2\u0027\r\n\r\nforeach ($svr in $servers)\r\n{\r\n    Set-DbaDbCompression -SqlInstance $svr -MaxRunTime 60 -PercentCompression 25 | Export-Csv -Path \r\nC:\\temp\\CompressionAnalysisPAC.csv -Append\r\n}\r\n\r\nSet the compression run time to 60 minutes and will start the compression of tables/indexes across all listed servers \r\nthat have a difference of 25% or higher between current and recommended. Output of command is exported to a csv.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Set-DbaDbQueryStoreOptions",
        "Description":  "Configure Query Store settings for a specific or multiple databases.",
        "Tags":  "QueryStore",
        "Author":  "Enrico van de Laar ( @evdlaar )",
        "Synopsis":  "Configure Query Store settings for a specific or multiple databases.",
        "Name":  "Set-DbaDbQueryStoreOptions",
        "Links":  "https://dbatools.io/Set-DbaQueryStoreOptions",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaDbQueryStoreOptions -SqlInstance ServerA\\SQL -State ReadWrite -FlushInterval 600 -CollectionInterval 10 \r\n-MaxSize 100 -CaptureMode All -CleanupMode Auto -StaleQueryThreshold 100 -AllDatabases\r\n\r\nConfigure the Query Store settings for all user databases in the ServerA\\SQL Instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaDbQueryStoreOptions -SqlInstance ServerA\\SQL -FlushInterval 600\r\n\r\nOnly configure the FlushInterval setting for all Query Store databases in the ServerA\\SQL Instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaDbQueryStoreOptions -SqlInstance ServerA\\SQL -Database AdventureWorks -State ReadWrite -FlushInterval 600 \r\n-CollectionInterval 10 -MaxSize 100 -CaptureMode all -CleanupMode Auto -StaleQueryThreshold 100\r\n\r\nConfigure the Query Store settings for the AdventureWorks database in the ServerA\\SQL Instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaDbQueryStoreOptions -SqlInstance ServerA\\SQL -Exclude AdventureWorks -State ReadWrite -FlushInterval 600 \r\n-CollectionInterval 10 -MaxSize 100 -CaptureMode all -CleanupMode Auto -StaleQueryThreshold 100\r\n\r\nConfigure the Query Store settings for all user databases except the AdventureWorks database in the ServerA\\SQL \r\nInstance.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Set-DbaDbRecoveryModel",
        "Description":  "Set-DbaDbRecoveryModel sets the Recovery Model for user databases.",
        "Tags":  [
                     "Recovery",
                     "RecoveryModel",
                     "Simple",
                     "Full",
                     "Bulk",
                     "BulkLogged"
                 ],
        "Author":  "Viorel Ciucu (@viorelciucu), https://www.cviorel.com",
        "Synopsis":  "Set-DbaDbRecoveryModel sets the Recovery Model.",
        "Name":  "Set-DbaDbRecoveryModel",
        "Links":  "https://dbatools.io/Set-DbaDbRecoveryModel",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaDbRecoveryModel -SqlInstance sql2014 -RecoveryModel BulkLogged -Database model -Confirm:$true -Verbose\r\n\r\nSets the Recovery Model to BulkLogged for database [model] on SQL Server instance sql2014. User is requested to confirm \r\nthe action.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaDatabase -SqlInstance sql2014 -Database TestDB | Set-DbaDbRecoveryModel -RecoveryModel Simple  \r\n-Confirm:$false\r\n\r\nSets the Recovery Model to Simple for database [TestDB] on SQL Server instance sql2014. Confirmation is not required.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaDbRecoveryModel -SqlInstance sql2014 -RecoveryModel Simple -Database TestDB -Confirm:$false\r\n\r\nSets the Recovery Model to Simple for database [TestDB] on SQL Server instance sql2014. Confirmation is not required.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaDbRecoveryModel -SqlInstance sql2014 -RecoveryModel Simple -AllDatabases -Confirm:$false\r\n\r\nSets the Recovery Model to Simple for ALL uses databases MODEL database on SQL Server instance sql2014. Runs without \r\nasking for confirmation.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaDbRecoveryModel -SqlInstance sql2014 -RecoveryModel BulkLogged -Database TestDB1, TestDB2 -Confirm:$false \r\n-Verbose\r\n\r\nSets the Recovery Model to BulkLogged for [TestDB1] and [TestDB2] databases on SQL Server instance sql2014. Runs \r\nwithout asking for confirmation.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Set-DbaErrorLogConfig",
        "Description":  "Sets the number of log files configured on all versions, and size in KB in SQL Server 2012+ and above.\n\nTo set the Path to the ErrorLog, use Set-DbaStartupParameter -ErrorLog. Note that this command requires\nremote, administrative access to the Windows/WMI server, similar to SQL Configuration Manager.",
        "Tags":  [
                     "Instance",
                     "ErrorLog"
                 ],
        "Author":  "Shawn Melton (@wsmelton)",
        "Synopsis":  "Set the configuration for the ErrorLog on a given SQL Server instance",
        "Name":  "Set-DbaErrorLogConfig",
        "Links":  "https://dbatools.io/Set-DbaErrorLogConfig",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaErrorLogConfig -SqlInstance sql2017,sql2014 -LogCount 25\r\n\r\nSets the number of error log files to 25 on sql2017 and sql2014\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaErrorLogConfig -SqlInstance sql2014 -LogSize 102400\r\n\r\nSets the size of the error log file, before it rolls over, to 102400 KB (100 MB) on sql2014\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaErrorLogConfig -SqlInstance sql2012 -LogCount 25 -LogSize 500\r\n\r\nSets the number of error log files to 25 and size before it will roll over to 500 KB on sql2012\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Set-DbaJobOwner",
        "Description":  "This function alters SQL Agent Job ownership to match a specified login if their current owner does not match the target login. By default, the target login will be \u0027sa\u0027, but the the user may specify a different login for ownership. This be applied to all jobs or only to a select collection of jobs.\n\nBest practice reference: http://sqlmag.com/blog/sql-server-tip-assign-ownership-jobs-sysadmin-account",
        "Tags":  [
                     "Agent",
                     "Job"
                 ],
        "Author":  "Michael Fal (@Mike_Fal), http://mikefal.net",
        "Synopsis":  "Sets SQL Agent job owners with a desired login if jobs do not match that owner.",
        "Name":  "Set-DbaJobOwner",
        "Links":  "https://dbatools.io/Set-DbaJobOwner",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaJobOwner -SqlInstance localhost\r\n\r\nSets SQL Agent Job owner to sa on all jobs where the owner does not match sa.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaJobOwner -SqlInstance localhost -Login DOMAIN\\account\r\n\r\nSets SQL Agent Job owner to sa on all jobs where the owner does not match \u0027DOMAIN\\account\u0027. Note\r\nthat Login must be a valid security principal that exists on the target server.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaJobOwner -SqlInstance localhost -Job job1, job2\r\n\r\nSets SQL Agent Job owner to \u0027sa\u0027 on the job1 and job2 jobs if their current owner does not match \u0027sa\u0027.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003e\u0027sqlserver\u0027,\u0027sql2016\u0027 | Set-DbaJobOwner\r\n\r\nSets SQL Agent Job owner to sa on all jobs where the owner does not match sa on both sqlserver and sql2016.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Set-DbaLogin",
        "Description":  "Set-DbaLogin will enable you to change the password, unlock, rename, disable or enable, deny or grant login privileges to the login.\nIt\u0027s also possible to add or remove server roles from the login.",
        "Tags":  "Login",
        "Synopsis":  "Set-DbaLogin makes it possible to make changes to one or more logins.",
        "Name":  "Set-DbaLogin",
        "Links":  "https://dbatools.io/Set-DbaLogin",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003e$password = ConvertTo-SecureString \"PlainTextPassword\" -AsPlainText -Force\r\n\r\n$cred = New-Object System.Management.Automation.PSCredential (\"username\", $password)\r\nSet-DbaLogin -SqlInstance sql1 -Login login1 -Password $cred -Unlock -MustChange\r\n\r\nSet the new password for login1 using a credential, unlock the account and set the option\r\nthat the usermust change password at next logon.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaLogin -SqlInstance sql1 -Login login1 -Enable\r\n\r\nEnable the login\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaLogin -SqlInstance sql1 -Login login1, login2, login3, login4 -Enable\r\n\r\nEnable multiple logins\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaLogin -SqlInstance sql1, sql2, sql3 -Login login1, login2, login3, login4 -Enable\r\n\r\nEnable multiple logins on multiple instances\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaLogin -SqlInstance sql1 -Login login1 -Disable\r\n\r\nDisable the login\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaLogin -SqlInstance sql1 -Login login1 -DenyLogin\r\n\r\nDeny the login to connect to the instance\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 7 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaLogin -SqlInstance sql1 -Login login1 -GrantLogin\r\n\r\nGrant the login to connect to the instance\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 8 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaLogin -SqlInstance sql1 -Login login1 -PasswordPolicyEnforced\r\n\r\nEnforces the password policy on a login\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 9 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaLogin -SqlInstance sql1 -Login login1 -PasswordPolicyEnforced:$false\r\n\r\nDisables enforcement of the password policy on a login\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 10 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaLogin -SqlInstance sql1 -Login test -AddRole serveradmin\r\n\r\nAdd the server role \"serveradmin\" to the login\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 11 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaLogin -SqlInstance sql1 -Login test -RemoveRole bulkadmin\r\n\r\nRemove the server role \"bulkadmin\" to the login\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Set-DbaMaxDop",
        "Description":  "Uses the Test-DbaMaxDop command to get the recommended value if -MaxDop parameter is not specified.\n\nThese are just general recommendations for SQL Server and are a good starting point for setting the \"max degree of parallelism\" option.\n\nYou can set MaxDop database scoped configurations if the server is version 2016 or higher",
        "Tags":  [
                     "MaxDop",
                     "SpConfigure"
                 ],
        "Author":  "Claudio Silva (@claudioessilva)",
        "Synopsis":  "Sets SQL Server maximum degree of parallelism (Max DOP), then displays information relating to SQL Server Max DOP configuration settings. Works on SQL Server 2005 and higher.",
        "Name":  "Set-DbaMaxDop",
        "Links":  "https://dbatools.io/Set-DbaMaxDop",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaMaxDop -SqlInstance sql2008, sql2012\r\n\r\nSets Max DOP to the recommended value for servers sql2008 and sql2012.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaMaxDop -SqlInstance sql2014 -MaxDop 4\r\n\r\nSets Max DOP to 4 for server sql2014.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaMaxDop -SqlInstance sql2008 | Set-DbaMaxDop\r\n\r\nGets the recommended Max DOP from Test-DbaMaxDop and applies it to to sql2008.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaMaxDop -SqlInstance sql2016 -Database db1\r\n\r\nSet recommended Max DOP for database db1 on server sql2016.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaMaxDop -SqlInstance sql2016 -AllDatabases\r\n\r\nSet recommended Max DOP for all databases on server sql2016.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Set-DbaMaxMemory",
        "Description":  "Sets SQL Server max memory then displays information relating to SQL Server Max Memory configuration settings.\n\nInspired by Jonathan Kehayias\u0027s post about SQL Server Max memory (http://bit.ly/sqlmemcalc), this uses a formula to\ndetermine the default optimum RAM to use, then sets the SQL max value to that number.\n\nJonathan notes that the formula used provides a *general recommendation* that doesn\u0027t account for everything that may\nbe going on in your specific environment.",
        "Tags":  [
                     "MaxMemory",
                     "Memory"
                 ],
        "Synopsis":  "Sets SQL Server \u0027Max Server Memory\u0027 configuration setting to a new value then displays information this setting.",
        "Name":  "Set-DbaMaxMemory",
        "Links":  "https://dbatools.io/Set-DbaMaxMemory",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaMaxMemory sqlserver1\r\n\r\nSet max memory to the recommended MB on just one server named \"sqlserver1\"\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaMaxMemory -SqlInstance sqlserver1 -MaxMB 2048\r\n\r\nExplicitly max memory to 2048 MB on just one server, \"sqlserver1\"\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRegisteredServer -SqlInstance sqlserver | Test-DbaMaxMemory | Where-Object { $_.SqlMaxMB -gt $_.TotalMB } \r\n| Set-DbaMaxMemory\r\n\r\nFind all servers in SQL Server Central Management server that have Max SQL memory set to higher than the total memory\r\nof the server (think 2147483647), then pipe those to Set-DbaMaxMemory and use the default recommendation.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Set-DbaNetworkCertificate",
        "Description":  "Sets the network certificate for SQL Server instance. This setting is found in Configuration Manager.\n\nThis command also grants read permissions for the service account on the certificate\u0027s private key.\n\nReferences:\nhttp://sqlmag.com/sql-server/7-steps-ssl-encryption\nhttps://azurebi.jppp.org/2016/01/23/using-lets-encrypt-certificates-for-secure-sql-server-connections/\nhttps://blogs.msdn.microsoft.com/sqlserverfaq/2016/09/26/creating-and-registering-ssl-certificates/",
        "Tags":  "Certificate",
        "Synopsis":  "Sets the network certificate for SQL Server instance",
        "Name":  "Set-DbaNetworkCertificate",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eNew-DbaComputerCertificate | Set-DbaNetworkCertificate -SqlInstance localhost\\SQL2008R2SP2\r\n\r\nCreates and imports a new certificate signed by an Active Directory CA on localhost then sets the network certificate \r\nfor the SQL2008R2SP2 to that newly created certificate.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaNetworkCertificate -SqlInstance sql1\\SQL2008R2SP2 -Thumbprint 1223FB1ACBCA44D3EE9640F81B6BA14A92F3D6E2\r\n\r\nSets the network certificate for the SQL2008R2SP2 instance to the certificate with the thumbprint of \r\n1223FB1ACBCA44D3EE9640F81B6BA14A92F3D6E2 in LocalMachine\\My on sql1\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Set-DbaPowerPlan",
        "Description":  "Sets the SQL Server OS\u0027s Power Plan. Defaults to High Performance which is best practice.\n\nIf your organization uses a custom power plan that is considered best practice, specify -CustomPowerPlan.\n\nReferences:\nhttps://support.microsoft.com/en-us/kb/2207548\nhttp://www.sqlskills.com/blogs/glenn/windows-power-plan-effects-on-newer-intel-processors/",
        "Tags":  [
                     "PowerPlan",
                     "OS",
                     "Configure"
                 ],
        "Synopsis":  "Sets the SQL Server OS\u0027s Power Plan.",
        "Name":  "Set-DbaPowerPlan",
        "Links":  "https://dbatools.io/Set-DbaPowerPlan",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaPowerPlan -ComputerName sqlserver2014a\r\n\r\nSets the Power Plan to High Performance. Skips it if its already set.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaPowerPlan -ComputerName sqlcluster -CustomPowerPlan \u0027Maximum Performance\u0027\r\n\r\nSets the Power Plan to the custom power plan called \"Maximum Performance\". Skips it if its already set.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Set-DbaPrivilege",
        "Description":  "Adds the SQL Service account to local privileges \u0027Lock Pages in Memory\u0027, \u0027Instant File Initialization\u0027, \u0027Logon as Batch\u0027 on one or more computers.\n\nRequires Local Admin rights on destination computer(s).",
        "Tags":  "Privilege",
        "Author":  "Klaas Vandenberghe ( @PowerDBAKlaas )",
        "Synopsis":  "Adds the SQL Service account to local privileges on one or more computers.",
        "Name":  "Set-DbaPrivilege",
        "Links":  "https://dbatools.io/Set-DbaPrivilege",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaPrivilege -ComputerName sqlserver2014a -Type LPIM,IFI\r\n\r\nAdds the SQL Service account(s) on computer sqlserver2014a to the local privileges \u0027SeManageVolumePrivilege\u0027 and \r\n\u0027SeLockMemoryPrivilege\u0027.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e\u0027sql1\u0027,\u0027sql2\u0027,\u0027sql3\u0027 | Set-DbaPrivilege -Type IFI\r\n\r\nAdds the SQL Service account(s) on computers sql1, sql2 and sql3 to the local privilege \u0027SeManageVolumePrivilege\u0027.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Set-DbaSpConfigure",
        "Description":  "This function changes the configured value for sp_configure settings. If the setting is dynamic this setting will be used, otherwise the user will be warned that a restart of SQL is required.\nThis is designed to be safe and will not allow for configurations to be set outside of the defined configuration min and max values.\nWhile it is possible to set below the min, or above the max this can cause serious problems with SQL Server (including startup failures), and so is not permitted.",
        "Tags":  "SpConfigure",
        "Author":  "Nic Cain, https://sirsql.net/",
        "Synopsis":  "Changes the server level system configuration (sys.configuration/sp_configure) value for a given configuration",
        "Name":  "Set-DbaSpConfigure",
        "Links":  "https://dbatools.io/Set-DbaSpConfigure",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaSpConfigure -SqlInstance localhost -Name ScanForStartupProcedures -Value 1\r\n\r\nAdjusts the Scan for startup stored procedures configuration value to 1 and notifies the user that this requires a SQL \r\nrestart to take effect\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSpConfigure -SqlInstance sql2017, sql2014 -Name XPCmdShellEnabled, IsSqlClrEnabled | Set-DbaSpConfigure \r\n-Value $false\r\n\r\nSets the values for XPCmdShellEnabled and IsSqlClrEnabled on sql2017 and sql2014 to False\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaSpConfigure -SqlInstance localhost -Name XPCmdShellEnabled -Value 1\r\n\r\nAdjusts the xp_cmdshell configuration value to 1.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaSpConfigure -SqlInstance localhost -Name XPCmdShellEnabled -Value 1 -WhatIf\r\n\r\nReturns information on the action that would be performed. No actual change will be made.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Set-DbaSpn",
        "Description":  "This function will connect to Active Directory and search for an account. If the account is found, it will attempt to add an SPN. Once the SPN\nis added, the function will also set delegation to that service, unless -NoDelegation is specified. In order to run this function, the credential you provide must have write\naccess to Active Directory.\n\nNote: This function supports -WhatIf",
        "Tags":  "SPN",
        "Author":  "Drew Furgiuele (@pittfurg), http://www.port1433.com",
        "Synopsis":  "Sets an SPN for a given service account in active directory (and also enables delegation to the same SPN by default)",
        "Name":  "Set-DbaSpn",
        "Links":  "https://dbatools.io/Set-DbaSpn",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaSpn -SPN MSSQLSvc\\SQLSERVERA.domain.something -ServiceAccount domain\\account\r\n\r\nConnects to Active Directory and adds a provided SPN to the given account.\r\n\r\nSet-DbaSpn -SPN MSSQLSvc\\SQLSERVERA.domain.something -ServiceAccount domain\\account -EnableException\r\n\r\nConnects to Active Directory and adds a provided SPN to the given account, suppressing all error messages and throw \r\nexceptions that can be caught instead\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaSpn -SPN MSSQLSvc\\SQLSERVERA.domain.something -ServiceAccount domain\\account -Credential (Get-Credential)\r\n\r\nConnects to Active Directory and adds a provided SPN to the given account. Uses alternative account to connect to AD.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaSpn -SPN MSSQLSvc\\SQLSERVERA.domain.something -ServiceAccount domain\\account -NoDelegation\r\n\r\nConnects to Active Directory and adds a provided SPN to the given account, without the delegation.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaSpn -ComputerName sql2016 | Where { $_.isSet -eq $false } | Set-DbaSpn\r\n\r\nSets all missing SPNs for sql2016\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaSpn -ComputerName sql2016 | Where { $_.isSet -eq $false } | Set-DbaSpn -WhatIf\r\n\r\nDisplays what would happen trying to set all missing SPNs for sql2016\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Set-DbaStartupParameter",
        "Description":  "Modifies the startup parameters for a specified SQL Server Instance\n\nFor full details of what each parameter does, please refer to this MSDN article - https://msdn.microsoft.com/en-us/library/ms190737(v=sql.105).aspx",
        "Tags":  [
                     "Service",
                     "Startup",
                     "Parameter",
                     "Configure"
                 ],
        "Author":  "Stuart Moore (@napalmgram), stuart-moore.com",
        "Synopsis":  "Sets the Startup Parameters for a SQL Server instance",
        "Name":  "Set-DbaStartupParameter",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaStartupParameter -SqlInstance server1\\instance1 -SingleUser\r\n\r\nWill configure the SQL Instance server1\\instance1 to startup up in Single User mode at next startup\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaStartupParameter -SqlInstance sql2016 -IncreasedExtents\r\n\r\nWill configure the SQL Instance sql2016 to IncreasedExtents = True (-E)\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaStartupParameter -SqlInstance sql2016  -IncreasedExtents:$false -WhatIf\r\n\r\nShows what would happen if you attempted to configure the SQL Instance sql2016 to IncreasedExtents = False (no -E)\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaStartupParameter -SqlInstance server1\\instance1 -SingleUser -TraceFlags 8032,8048\r\n\r\nThis will append Trace Flags 8032 and 8048 to the startup parameters\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaStartupParameter -SqlInstance sql2016 -SingleUser:$false -TraceFlagsOverride\r\n\r\nThis will remove all trace flags and set SinguleUser to false\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaStartupParameter -SqlInstance server1\\instance1 -SingleUser -TraceFlags 8032,8048 -TraceFlagsOverride\r\n\r\nThis will set Trace Flags 8032 and 8048 to the startup parameters, removing any existing Trace Flags\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 7 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaStartupParameter -SqlInstance sql2016 -SingleUser:$false -TraceFlagsOverride -Offline\r\n\r\nThis will remove all trace flags and set SinguleUser to false from an offline instance\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 8 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaStartupParameter -SqlInstance sql2016 -ErrorLog c:\\Sql\\ -Offline\r\n\r\nThis will attempt to change the ErrorLog path to c:\\sql\\. However, with the offline switch this will not happen. To \r\nforce it, use the -Force switch like so:\r\n\r\nSet-DbaStartupParameter -SqlInstance sql2016 -ErrorLog c:\\Sql\\ -Offline -Force\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 9 --------------------------\r\n\r\nPS C:\\\u003e$StartupConfig = Get-DbaStartupParameter -SqlInstance server1\\instance1\r\n\r\nSet-DbaStartupParameter -SqlInstance server1\\instance1 -SingleUser -NoLoggingToWinEvents\r\n#Restart your SQL instance with the tool of choice\r\n#Do Some work\r\nSet-DbaStartupParameter -SqlInstance server1\\instance1 -StartUpConfig $StartUpConfig\r\n#Restart your SQL instance with the tool of choice and you\u0027re back to normal\r\n\r\nIn this example we take a copy of the existing startup configuration of server1\\instance1\r\n\r\nWe then change the startup parameters ahead of some work\r\n\r\nAfter the work has been completed, we can push the original startup parameters back to server1\\instance1 and resume \r\nnormal operation\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Set-DbaTcpPort",
        "Description":  "This function changes the TCP port used by the specified SQL Server.",
        "Tags":  [
                     "Service",
                     "Port",
                     "TCP",
                     "Configure"
                 ],
        "Author":  "[email protected], @H0s0n77",
        "Synopsis":  "Changes the TCP port used by the specified SQL Server.",
        "Name":  "Set-DbaTcpPort",
        "Links":  "https://dbatools.io/Set-DbaTcpPort",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaTcpPort -SqlInstance SqlInstance2014a -Port 1433\r\n\r\nSets the port number 1433 for allips on the default instance on SqlInstance2014a\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaTcpPort -SqlInstance winserver\\sqlexpress -IpAddress 192.168.1.22 -Port 1433\r\n\r\nSets the port number 1433 for IP 192.168.1.22 on the sqlexpress instance on winserver\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaTcpPort -SqlInstance \u0027SQLDB2014A\u0027 ,\u0027SQLDB2016B\u0027 -port 1337\r\n\r\nSets the port number 1337 for ALLIP\u0027s on SqlInstance SQLDB2014A and SQLDB2016B\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Set-DbaTempDbConfiguration",
        "Description":  "Calculates tempdb size and file configurations based on passed parameters, calculated values, and Microsoft best practices. User must declare SQL Server to be configured and total data file size as mandatory values. Function then calculates the number of data files based on logical cores on the target host and create evenly sized data files based on the total data size declared by the user, with a log file 25% of the total data file size.\n\nOther parameters can adjust the settings as the user desires (such as different file paths, number of data files, and log file size). No functions that shrink or delete data files are performed. If you wish to do this, you will need to resize tempdb so that it is \"smaller\" than what the function will size it to before running the function.",
        "Tags":  [
                     "Tempdb",
                     "Space",
                     "Configure",
                     "Configuration"
                 ],
        "Author":  "Michael Fal (@Mike_Fal), http://mikefal.net",
        "Synopsis":  "Sets tempdb data and log files according to best practices.",
        "Name":  "Set-DbaTempDbConfiguration",
        "Links":  "https://dbatools.io/Set-DbaTempDbConfiguration",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaTempDbConfiguration -SqlInstance localhost -DataFileSizeMB 1000\r\n\r\nCreates tempdb with a number of data files equal to the logical cores where each file is equal to 1000MB divided by the \r\nnumber of logical cores, with a log file of 250MB.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaTempDbConfiguration -SqlInstance localhost -DataFileSizeMB 1000 -DataFileCount 8\r\n\r\nCreates tempdb with 8 data files, each one sized at 125MB, with a log file of 250MB.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaTempDbConfiguration -SqlInstance localhost -DataFileSizeMB 1000 -OutputScriptOnly\r\n\r\nProvides a SQL script output to configure tempdb according to the passed parameters.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaTempDbConfiguration -SqlInstance localhost -DataFileSizeMB 1000 -DisableGrowth\r\n\r\nDisables the growth for the data and log files.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eSet-DbaTempDbConfiguration -SqlInstance localhost -DataFileSizeMB 1000 -OutputScriptOnly\r\n\r\nReturns the T-SQL script representing tempdb configuration.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Show-DbaDatabaseList",
        "Description":  "Shows a list of databases in a GUI. Returns a string holding the name of the selected database. Hitting cancel returns null.",
        "Tags":  "Database",
        "Synopsis":  "Shows a list of databases in a GUI.",
        "Name":  "Show-DbaDatabaseList",
        "Links":  "https://dbatools.io/Show-DbaDatabaseList",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eShow-DbaDatabaseList -SqlInstance sqlserver2014a\r\n\r\nShows a GUI list of databases using Windows Authentication to connect to the SQL Server. Returns a string of the \r\nselected database.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eShow-DbaDatabaseList -Source sqlserver2014a -SqlCredential $cred\r\n\r\nShows a GUI list of databases using SQL credentials to connect to the SQL Server. Returns a string of the selected \r\ndatabase.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Show-DbaServerFileSystem",
        "Description":  "Similar to the remote file system popup you see when browsing a remote SQL Server in SQL Server Management Studio, this function allows you to traverse the remote SQL Server\u0027s file structure.\n\nShow-DbaServerFileSystem uses SQL Management Objects to browse the directories and what you see is limited to the permissions of the account running the command.",
        "Tags":  "Storage",
        "Synopsis":  "Shows file system on remote SQL Server in a local GUI and returns the selected directory name",
        "Name":  "Show-DbaServerFileSystem",
        "Links":  "https://dbatools.io/Show-DbaServerFileSystem",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eShow-DbaServerFileSystem -SqlInstance sqlserver2014a\r\n\r\nShows a list of databases using Windows Authentication to connect to the SQL Server. Returns a string of the selected \r\npath.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eShow-DbaServerFileSystem -Source sqlserver2014a -SqlCredential $cred\r\n\r\nShows a list of databases using SQL credentials to connect to the SQL Server. Returns a string of the selected path.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Start-DbaAgentJob",
        "Description":  "This command starts a job then returns connected SMO object for SQL Agent Job information for each instance(s) of SQL Server.",
        "Tags":  [
                     "Job",
                     "Agent"
                 ],
        "Synopsis":  "Starts a running SQL Server Agent Job.",
        "Name":  "Start-DbaAgentJob",
        "Links":  "https://dbatools.io/Start-DbaAgentJob",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eStart-DbaAgentJob -SqlInstance localhost\r\n\r\nStarts all running SQL Agent Jobs on the local SQL Server instance\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentJob -SqlInstance sql2016 -Job cdc.DBWithCDC_capture | Start-DbaAgentJob\r\n\r\nStarts the cdc.DBWithCDC_capture SQL Agent Job on sql2016\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eStart-DbaAgentJob -SqlInstance sql2016 -Job cdc.DBWithCDC_capture\r\n\r\nStarts the cdc.DBWithCDC_capture SQL Agent Job on sql2016\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003e$servers | Find-DbaAgentJob -IsFailed | Start-DbaAgentJob\r\n\r\nRestarts all failed jobs on all servers in the $servers collection\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eStart-DbaAgentJob -SqlInstance sql2016 -AllJobs\r\n\r\nStart all the jobs\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Start-DbaMigration",
        "Description":  "Start-DbaMigration consolidates most of the migration tools in dbatools into one command.  This is useful when you\u0027re looking to migrate entire instances. It less flexible than using the underlying functions. Think of it as an easy button. It migrates:\n\nAll user databases to exclude support databases such as ReportServerTempDB (Use -IncludeSupportDbs for this). Use -NoDatabases to skip.\nAll logins. Use -NoLogins to skip.\nAll database mail objects. Use -NoDatabaseMail\nAll credentials. Use -NoCredentials to skip.\nAll objects within the Job Server (SQL Agent). Use -NoAgentServer to skip.\nAll linked servers. Use -NoLinkedServers to skip.\nAll groups and servers within Central Management Server. Use -NoCentralManagementServer to skip.\nAll SQL Server configuration objects (everything in sp_configure). Use -NoSpConfigure to skip.\nAll user objects in system databases. Use -NoSysDbUserObjects to skip.\nAll system triggers. Use -NoSystemTriggers to skip.\nAll system backup devices. Use -NoBackupDevices to skip.\nAll Audits. Use -NoAudits to skip.\nAll Endpoints. Use -NoEndpoints to skip.\nAll Extended Events. Use -NoExtendedEvents to skip.\nAll Policy Management objects. Use -NoPolicyManagement to skip.\nAll Resource Governor objects. Use -NoResourceGovernor to skip.\nAll Server Audit Specifications. Use -NoServerAuditSpecifications to skip.\nAll Custom Errors (User Defined Messages). Use -NoCustomErrors to skip.\nCopies All Data Collector collection sets. Does not configure the server. Use -NoDataCollector to skip.\n\nThis script provides the ability to migrate databases using detach/copy/attach or backup/restore. SQL Server logins, including passwords, SID and database/server roles can also be migrated. In addition, job server objects can be migrated and server configuration settings can be exported or migrated. This script works with named instances, clusters and SQL Express.\n\nBy default, databases will be migrated to the destination SQL Server\u0027s default data and log directories. You can override this by specifying -ReuseSourceFolderStructure. Filestreams and filegroups are also migrated. Safety is emphasized.",
        "Tags":  "Migration",
        "Author":  "Chrissy LeMaire",
        "Synopsis":  "Migrates SQL Server *ALL* databases, logins, database mail profiles/accounts, credentials, SQL Agent objects, linked servers,\nCentral Management Server objects, server configuration settings (sp_configure), user objects in systems databases,\nsystem triggers and backup devices from one SQL Server to another.\n\nFor more granular control, please use one of the -No parameters and use the other functions available within the dbatools module.",
        "Name":  "Start-DbaMigration",
        "Links":  "https://dbatools.io/Start-DbaMigration",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eStart-DbaMigration -Source sqlserver\\instance -Destination sqlcluster -DetachAttach\r\n\r\nAll databases, logins, job objects and sp_configure options will be migrated from sqlserver\\instance to sqlcluster. \r\nDatabases will be migrated using the detach/copy files/attach method. Dbowner will be updated. User passwords, SIDs, \r\ndatabase roles and server roles will be migrated along with the login.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eStart-DbaMigration -Verbose -Source sqlcluster -Destination sql2016 -SourceSqlCredential $scred \r\n-ReuseSourceFolderStructure -DestinationSqlCredential $cred -Force -NetworkShare \r\n\\\\fileserver\\share\\sqlbackups\\Migration -BackupRestore\r\n\r\nMigrate databases uses backup/restore. Also migrate logins, database mail, credentials, SQL Agent, Central Management \r\nServer, SQL global configuration.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eStart-DbaMigration -Verbose -Source sqlcluster -Destination sql2016 -NoDatabases -NoLogins\r\n\r\nMigrates everything but logins and databases.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eStart-DbaMigration -Verbose -Source sqlcluster -Destination sql2016 -DetachAttach -Reattach -SetSourceReadonly\r\n\r\nMigrate databases using detach/copy/attach. Reattach at source and set source databases read-only. Also migrates \r\neverything else.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Start-DbaPfDataCollectorSet",
        "Description":  "Starts Performance Monitor Data Collector Set.",
        "Tags":  "PerfMon",
        "Synopsis":  "Starts Performance Monitor Data Collector Set.",
        "Name":  "Start-DbaPfDataCollectorSet",
        "Links":  "https://dbatools.io/Start-DbaPfDataCollectorSet",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eStart-DbaPfDataCollectorSet\r\n\r\nAttempts to start all ready Collectors on localhost.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eStart-DbaPfDataCollectorSet -ComputerName sql2017\r\n\r\nAttempts to start all ready Collectors on localhost.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eStart-DbaPfDataCollectorSet -ComputerName sql2017, sql2016 -Credential (Get-Credential) -CollectorSet \u0027System \r\nCorrelation\u0027\r\n\r\nStarts the \u0027System Correlation\u0027 Collector on sql2017 and sql2016 using alternative credentials.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfDataCollectorSet -CollectorSet \u0027System Correlation\u0027 | Start-DbaPfDataCollectorSet\r\n\r\nStarts the \u0027System Correlation\u0027 Collector.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Start-DbaSqlService",
        "Description":  "Starts the SQL Server related services on one or more computers. Will follow SQL Server service dependencies.\n\nRequires Local Admin rights on destination computer(s).",
        "Tags":  [
                     "Service",
                     "SqlServer",
                     "Instance",
                     "Connect"
                 ],
        "Author":  "Kirill Kravtsov( @nvarscar )",
        "Synopsis":  "Starts SQL Server services on a computer.",
        "Name":  "Start-DbaSqlService",
        "Links":  "https://dbatools.io/Start-DbaSqlService",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eStart-DbaSqlService -ComputerName sqlserver2014a\r\n\r\nStarts the SQL Server related services on computer sqlserver2014a.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e\u0027sql1\u0027,\u0027sql2\u0027,\u0027sql3\u0027| Get-DbaSqlService | Start-DbaSqlService\r\n\r\nGets the SQL Server related services on computers sql1, sql2 and sql3 and starts them.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eStart-DbaSqlService -ComputerName sql1,sql2 -Instance MSSQLSERVER\r\n\r\nStarts the SQL Server services related to the default instance MSSQLSERVER on computers sql1 and sql2.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eStart-DbaSqlService -ComputerName $MyServers -Type SSRS\r\n\r\nStarts the SQL Server related services of type \"SSRS\" (Reporting Services) on computers in the variable MyServers.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Start-DbaTrace",
        "Description":  "Starts SQL Server traces",
        "Tags":  [
                     "Security",
                     "Trace"
                 ],
        "Synopsis":  "Starts SQL Server traces",
        "Name":  "Start-DbaTrace",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eStart-DbaTrace -SqlInstance sql2008\r\n\r\nStarts all traces on sql2008\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eStart-DbaTrace -SqlInstance sql2008 -Id 1\r\n\r\nStarts all trace with ID 1 on sql2008\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaTrace -SqlInstance sql2008 | Out-GridView -PassThru | Start-DbaTrace\r\n\r\nStarts selected traces on sql2008\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Start-DbaXESession",
        "Description":  "This script starts Extended Events sessions on a SQL Server instance.",
        "Tags":  [
                     "ExtendedEvent",
                     "XE",
                     "XEvent"
                 ],
        "Author":  "Doug Meyers",
        "Synopsis":  "Starts Extended Events sessions.",
        "Name":  "Start-DbaXESession",
        "Links":  "https://dbatools.io/Start-DbaXESession",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eStart-DbaXESession -SqlInstance sqlserver2012 -AllSessions\r\n\r\nStarts all Extended Event Session on the sqlserver2014 instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eStart-DbaXESession -SqlInstance sqlserver2012 -Session xesession1,xesession2\r\n\r\nStarts the xesession1 and xesession2 Extended Event sessions.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eStart-DbaXESession -SqlInstance sqlserver2012 -Session xesession1,xesession2 -StopAt (Get-Date).AddMinutes(30)\r\n\r\nStarts the xesession1 and xesession2 Extended Event sessions and stops them in 30 minutes.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaXESession -SqlInstance sqlserver2012 -Session xesession1 | Start-DbaXESession\r\n\r\nStarts the sessions returned from the Get-DbaXESession function.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Start-DbaXESmartTarget",
        "Description":  "XESmartTarget offers the ability to set up complex actions in response to Extended Events captured in sessions, without writing a single line of code.\n\nSee more at https://github.com/spaghettidba/XESmartTarget/wiki",
        "Tags":  [
                     "ExtendedEvent",
                     "XE",
                     "XEvent"
                 ],
        "Synopsis":  "XESmartTarget runs as a client application for an Extended Events session running on a SQL Server instance.",
        "Name":  "Start-DbaXESmartTarget",
        "Links":  "https://dbatools.io/Start-DbaXESmartTarget\nhttps://github.com/spaghettidba/XESmartTarget/wiki",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003e$response = New-DbaXESmartQueryExec -SqlInstance sql2017 -Database dbadb -Query \"update table set whatever = 1\"\r\n\r\nStart-DbaXESmartTarget -SqlInstance sql2017 -Session deadlock_tracker -Responder $response\r\n\r\nExecutes a T-SQL command against dbadb on sql2017 whenever a deadlock event is recorded.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e$response = New-DbaXESmartQueryExec -SqlInstance sql2017 -Database dbadb -Query \"update table set whatever = 1\"\r\n\r\n$params = @{\r\n    SmtpServer = \"smtp.ad.local\"\r\n    To = \"[email protected]\"\r\n    Sender = \"[email protected]\"\r\n    Subject = \"Query executed\"\r\n    Body = \"Query executed at {collection_time}\"\r\n    Attachment = \"batch_text\"\r\n    AttachmentFileName = \"query.sql\"\r\n}\r\n$emailresponse = New-DbaXESmartEmail @params\r\nStart-DbaXESmartTarget -SqlInstance sql2017 -Session querytracker -Responder $response, $emailresponse\r\n\r\nExecutes a T-SQL command against dbadb on sql2017 and sends an email whenever a querytracker event is recorded.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003e$columns = \"cpu_time\", \"duration\", \"physical_reads\", \"logical_reads\", \"writes\", \"row_count\", \"batch_text\"\r\n\r\n$response = New-DbaXESmartTableWriter -SqlInstance sql2017 -Database dbadb -Table deadlocktracker -OutputColumns \r\n$columns -Filter \"duration \u003e 10000\"\r\nStart-DbaXESmartTarget -SqlInstance sql2017 -Session deadlock_tracker -Responder $response\r\n\r\nWrites Extended Events to the deadlocktracker table in dbadb on sql2017.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Stop-DbaAgentJob",
        "Description":  "This command stops a job then returns connected SMO object for SQL Agent Job information for each instance(s) of SQL Server.",
        "Tags":  [
                     "Job",
                     "Agent"
                 ],
        "Synopsis":  "Stops a running SQL Server Agent Job.",
        "Name":  "Stop-DbaAgentJob",
        "Links":  "https://dbatools.io/Stop-DbaAgentJob",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eStop-DbaAgentJob -SqlInstance localhost\r\n\r\nStops all running SQL Agent Jobs on the local SQL Server instance\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaAgentJob -SqlInstance sql2016 -Job cdc.DBWithCDC_capture | Stop-DbaAgentJob\r\n\r\nStops the cdc.DBWithCDC_capture SQL Agent Job on sql2016\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eStop-DbaAgentJob -SqlInstance sql2016 -Job cdc.DBWithCDC_capture\r\n\r\nStops the cdc.DBWithCDC_capture SQL Agent Job on sql2016\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Stop-DbaPfDataCollectorSet",
        "Description":  "Stops Performance Monitor Data Collector Set.",
        "Tags":  "PerfMon",
        "Synopsis":  "Stops Performance Monitor Data Collector Set.",
        "Name":  "Stop-DbaPfDataCollectorSet",
        "Links":  "https://dbatools.io/Stop-DbaPfDataCollectorSet",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eStop-DbaPfDataCollectorSet\r\n\r\nAttempts to stop all ready Collectors on localhost.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eStop-DbaPfDataCollectorSet -ComputerName sql2017\r\n\r\nAttempts to stop all ready Collectors on localhost.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eStop-DbaPfDataCollectorSet -ComputerName sql2017, sql2016 -Credential (Get-Credential) -CollectorSet \u0027System \r\nCorrelation\u0027\r\n\r\nStops the \u0027System Correlation\u0027 Collector on sql2017 and sql2016 using alternative credentials.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaPfDataCollectorSet -CollectorSet \u0027System Correlation\u0027 | Stop-DbaPfDataCollectorSet\r\n\r\nStops the \u0027System Correlation\u0027 Collector.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Stop-DbaProcess",
        "Description":  "This command kills all spids associated with a spid, login, host, program or database.\n\nIf you are attempting to kill your own login sessions, the process performing the kills will be skipped.",
        "Tags":  "Processes",
        "Synopsis":  "This command finds and kills SQL Server processes.",
        "Name":  "Stop-DbaProcess",
        "Links":  "https://dbatools.io/Stop-DbaProcess",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eStop-DbaProcess -SqlInstance sqlserver2014a -Login base\\ctrlb, sa\r\n\r\nFinds all processes for base\\ctrlb and sa on sqlserver2014a, then kills them. Uses Windows Authentication to login to \r\nsqlserver2014a.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eStop-DbaProcess -SqlInstance sqlserver2014a -SqlCredential $credential -Spids 56, 77\r\n\r\nFinds processes for spid 56 and 57, then kills them. Uses alternative (SQL or Windows) credentials to login to \r\nsqlserver2014a.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eStop-DbaProcess -SqlInstance sqlserver2014a -Programs \u0027Microsoft SQL Server Management Studio\u0027\r\n\r\nFinds processes that were created in Microsoft SQL Server Management Studio, then kills them.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eStop-DbaProcess -SqlInstance sqlserver2014a -Hosts workstationx, server100\r\n\r\nFinds processes that were initiated by hosts (computers/clients) workstationx and server 1000, then kills them.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eStop-DbaProcess -SqlInstance sqlserver2014  -Database tempdb -WhatIf\r\n\r\nShows what would happen if the command were executed.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaProcess -SqlInstance sql2016 -Programs \u0027dbatools PowerShell module - dbatools.io\u0027 | Stop-DbaProcess\r\n\r\nFinds processes that were created with dbatools, then kills them.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Stop-DbaSqlService",
        "Description":  "Stops the SQL Server related services on one or more computers. Will follow SQL Server service dependencies.\n\nRequires Local Admin rights on destination computer(s).",
        "Tags":  [
                     "Service",
                     "SqlServer",
                     "Instance",
                     "Connect"
                 ],
        "Author":  "Kirill Kravtsov( @nvarscar )",
        "Synopsis":  "Stops SQL Server services on a computer.",
        "Name":  "Stop-DbaSqlService",
        "Links":  "https://dbatools.io/Stop-DbaSqlService",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eStop-DbaSqlService -ComputerName sqlserver2014a\r\n\r\nStops the SQL Server related services on computer sqlserver2014a.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e\u0027sql1\u0027,\u0027sql2\u0027,\u0027sql3\u0027| Get-DbaSqlService | Stop-DbaSqlService\r\n\r\nGets the SQL Server related services on computers sql1, sql2 and sql3 and stops them.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eStop-DbaSqlService -ComputerName sql1,sql2 -Instance MSSQLSERVER\r\n\r\nStops the SQL Server services related to the default instance MSSQLSERVER on computers sql1 and sql2.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eStop-DbaSqlService -ComputerName $MyServers -Type SSRS\r\n\r\nStops the SQL Server related services of type \"SSRS\" (Reporting Services) on computers in the variable MyServers.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eStop-DbaSqlService -ComputerName sql1 -Type Engine -Force\r\n\r\nStops SQL Server database engine services on sql1 forcing dependent SQL Server Agent services to stop as well.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Stop-DbaTrace",
        "Description":  "Stops SQL Server traces",
        "Tags":  [
                     "Security",
                     "Trace"
                 ],
        "Synopsis":  "Stops SQL Server traces",
        "Name":  "Stop-DbaTrace",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eStop-DbaTrace -SqlInstance sql2008\r\n\r\nStops all traces on sql2008\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eStop-DbaTrace -SqlInstance sql2008 -Id 1\r\n\r\nStops all trace with ID 1 on sql2008\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaTrace -SqlInstance sql2008 | Out-GridView -PassThru | Stop-DbaTrace\r\n\r\nStops selected traces on sql2008\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Stop-DbaXESession",
        "Description":  "This script stops Extended Events sessions on a SQL Server instance.",
        "Tags":  [
                     "ExtendedEvent",
                     "XE",
                     "XEvent"
                 ],
        "Author":  "Doug Meyers",
        "Synopsis":  "Stops Extended Events sessions.",
        "Name":  "Stop-DbaXESession",
        "Links":  "https://dbatools.io/Stop-DbaXESession",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eStop-DbaXESession -SqlInstance sqlserver2012 -AllSessions\r\n\r\nStops all Extended Event Session on the sqlserver2014 instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eStop-DbaXESession -SqlInstance sqlserver2012 -Session xesession1,xesession2\r\n\r\nStops the xesession1 and xesession2 Extended Event sessions.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaXESession -SqlInstance sqlserver2012 -Session xesession1 | Stop-DbaXESession\r\n\r\nStops the sessions returned from the Get-DbaXESession function.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Stop-DbaXESmartTarget",
        "Description":  "Stops an XESmartTarget PowerShell Job. Useful if you want to run a target, but not right now.",
        "Tags":  [
                     "ExtendedEvent",
                     "XE",
                     "XEvent"
                 ],
        "Synopsis":  "Stops an XESmartTarget PowerShell Job. Useful if you want to run a target, but not right now.",
        "Name":  "Stop-DbaXESmartTarget",
        "Links":  "https://dbatools.io/Stop-DbaXESmartTarget\nhttps://github.com/spaghettidba/XESmartTarget/wiki",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaXESmartTarget | Stop-DbaXESmartTarget\r\n\r\nStops all XESmartTarget jobs.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaXESmartTarget | Where-Object Id -eq 2 | Stop-DbaXESmartTarget\r\n\r\nStops a specific XESmartTarget job.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Sync-DbaLoginPermission",
        "Description":  "Syncs only SQL Server login permissions, roles, etc. Does not add or drop logins. If a matching login does not exist on the destination, the login will be skipped. Credential removal is not currently supported for this operation.",
        "Tags":  [
                     "Migration",
                     "Login"
                 ],
        "Author":  "Chrissy LeMaire (@cl), netnerds.net",
        "Synopsis":  "Copies SQL login permissions from one server to another.",
        "Name":  "Sync-DbaLoginPermission",
        "Links":  "https://dbatools.io/Sync-DbaLoginPermission",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eSync-DbaLoginPermission -Source sqlserver2014a -Destination sqlcluster\r\n\r\nSyncs only SQL Server login permissions, roles, etc. Does not add or drop logins or users. To copy logins and their \r\npermissions, use Copy-SqlLogin.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eSync-DbaLoginPermission -Source sqlserver2014a -Destination sqlcluster -Exclude realcajun -SourceSqlCredential \r\n$scred -DestinationSqlCredential $dcred\r\n\r\nCopies all login permissions except for realcajun using SQL Authentication to connect to each server. If a login \r\nalready exists on the destination, the permissions will not be migrated.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eSync-DbaLoginPermission -Source sqlserver2014a -Destination sqlcluster -Login realcajun, netnerds\r\n\r\nCopies permissions ONLY for logins netnerds and realcajun.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Test-DbaBackupInformation",
        "Description":  "Normally takes in a backup history object from Format-DbaBackupInformation\n\nThis is then parse to check that it\u0027s valid for restore. Tests performed include:\n    Checking unbroken LSN chain\n    if the target database exists and WithReplace has been provided\n    if any files already exist, but owned by other databases\n    Creates any new folders required\n    That the backup files exists at the location specified, and can be seen by the Sql Instance\n\nif no errors are found then the objects for that database will me marked as Verified.",
        "Tags":  [
                     "Backup",
                     "Restore",
                     "DisasterRecovery"
                 ],
        "Author":  "Stuart Moore (@napalmgram stuart-moore.com )",
        "Synopsis":  "Tests a dbatools backup history object is correct for restoring",
        "Name":  "Test-DbaBackupInformation",
        "Links":  "https://dbatools.io/Test-DbaBackupInformation",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003e$BackupHistory | Test-DbaBackupInformation -SqlInstance MyInstance\r\n\r\n$PassedDbs = $BackupHistory | Where-Object {$_.IsVerified -eq $True}\r\n$FailedDbs = $BackupHistory | Where-Object {$_.IsVerified -ne $True}\r\n\r\nPass in a BackupHistory object to be tested against MyInstance.\r\n\r\nThose records that pass are marked as verified. We can then use the IsVerified property to divide the failures and \r\nsuccesses\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Test-DbaCmConnection",
        "Description":  "Tests over which paths a computer can be managed.\n\nThis function tries out the connectivity for:\n    - Cim over WinRM\n    - Cim over DCOM\n    - Wmi\n    - PowerShellRemoting\nResults will be written to the connectivity cache and will cause Get-DbaCmObject and Invoke-DbaCmMethod to connect using the way most likely to succeed. This way, it is likely the other commands will take less time to execute. These others too cache their results, in order to dynamically update connection statistics.\n\nThis function ignores global configuration settings limiting which protocols may be used.",
        "Tags":  [
                     "ComputerManagement",
                     "CIM"
                 ],
        "Author":  "Fred Winmann (@FredWeinmann)",
        "Synopsis":  "Tests over which paths a computer can be managed.",
        "Name":  "Test-DbaCmConnection",
        "Links":  "https://dbatools.io/Test-DbaCmConnection",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaCmConnection -ComputerName sql2014\r\n\r\nPerforms a full-spectrum connection test against the computer sql2014. The results will be reported and registered. \r\nFuture calls from Get-DbaCmObject will recognize the results and optimize the query.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaCmConnection -ComputerName sql2014 -Credential $null -Type CimDCOM, CimRM\r\n\r\nThis test will run a connectivity test of CIM over DCOM and CIM over WinRM against the computer sql2014 using Windows \r\nAuthentication.\r\n\r\nThe results will be reported and registered. Future calls from Get-DbaCmObject will recognize the results and optimize \r\nthe query.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Test-DbaConnection",
        "Description":  "Tests the ability to connect to an SQL Server instance outputting information about the server and instance.",
        "Tags":  [
                     "CIM",
                     "Test",
                     "Connection"
                 ],
        "Author":  "Chrissy LeMaire",
        "Synopsis":  "Tests the connection to a single instance.",
        "Name":  "Test-DbaConnection",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaConnection SQL2016\r\n\r\nComputerName         : SQL2016\r\nInstanceName         : MSSQLSERVER\r\nSqlInstance          : sql2016\r\nSqlVersion           : 13.0.4001\r\nConnectingAsUser     : BASE\\ctrlb\r\nConnectSuccess       : True\r\nAuthType             : Windows Authentication\r\nAuthScheme           : KERBEROS\r\nTcpPort              : 1433\r\nIPAddress            : 10.2.1.5\r\nNetBiosName          : sql2016.base.local\r\nIsPingable           : True\r\nPSRemotingAccessible : True\r\nDomainName           : base.local\r\nLocalWindows         : 10.0.15063.0\r\nLocalPowerShell      : 5.1.15063.502\r\nLocalCLR             : 4.0.30319.42000\r\nLocalSMOVersion      : 13.0.0.0\r\nLocalDomainUser      : True\r\nLocalRunAsAdmin      : False\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Test-DbaConnectionAuthScheme",
        "Description":  "By default, this command will return the ConnectName, ServerName, Transport and AuthScheme of the current connection.\n\nConnectName is the name you used to connect. ServerName is the name that the SQL Server reports as its @@SERVERNAME which is used to register its SPN. If you were expecting a Kerberos connection and got NTLM instead, ensure ConnectName and ServerName match.\n\nIf -Kerberos or -Ntlm is specified, the $true/$false results of the test will be returned. Returns $true or $false by default for one server. Returns Server name and Results for more than one server.",
        "Tags":  [
                     "SPN",
                     "Kerberos"
                 ],
        "Synopsis":  "Returns the transport protocol and authentication scheme of the connection. This is useful to determine if your connection is using Kerberos.",
        "Name":  "Test-DbaConnectionAuthScheme",
        "Links":  "https://dbatools.io/Test-DbaConnectionAuthScheme",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaConnectionAuthScheme -SqlInstance sqlserver2014a, sql2016\r\n\r\nReturns ConnectName, ServerName, Transport and AuthScheme for sqlserver2014a and sql2016.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaConnectionAuthScheme -SqlInstance sqlserver2014a -Kerberos\r\n\r\nReturns $true or $false depending on if the connection is Kerberos or not.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaConnectionAuthScheme -SqlInstance sqlserver2014a | Select-Object *\r\n\r\nReturns the results of \"SELECT * from sys.dm_exec_connections WHERE session_id = @@SPID\"\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Test-DbaDatabaseCollation",
        "Description":  "Compares Database Collations to Server Collation",
        "Tags":  [
                     "Database",
                     "Collation"
                 ],
        "Synopsis":  "Compares Database Collations to Server Collation",
        "Name":  "Test-DbaDatabaseCollation",
        "Links":  "https://dbatools.io/Test-DbaDatabaseCollation",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaDatabaseCollation -SqlInstance sqlserver2014a\r\n\r\nReturns server name, database name and true/false if the collations match for all databases on sqlserver2014a.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaDatabaseCollation -SqlInstance sqlserver2014a -Database db1, db2\r\n\r\nReturns inforamtion for the db1 and db2 databases on sqlserver2014a.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaDatabaseCollation -SqlInstance sqlserver2014a, sql2016 -Exclude db1\r\n\r\nReturns information for database and server collations for all databases except db1 on sqlserver2014a and sql2016.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRegisteredServer -SqlInstance sql2016 | Test-DbaDatabaseCollation\r\n\r\nReturns db/server collation information for every database on every server listed in the Central Management Server on \r\nsql2016.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Test-DbaDatabaseCompatibility",
        "Description":  "Compares Database Compatibility level to Server Compatibility",
        "Tags":  [
                     "Database",
                     "Compatibility"
                 ],
        "Synopsis":  "Compares Database Compatibility level to Server Compatibility",
        "Name":  "Test-DbaDatabaseCompatibility",
        "Links":  "https://dbatools.io/Test-DbaDatabaseCompatibility",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaDatabaseCompatibility -SqlInstance sqlserver2014a\r\n\r\nReturns server name, database name and true/false if the compatibility level match for all databases on sqlserver2014a.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaDatabaseCompatibility -SqlInstance sqlserver2014a -Database db1, db2\r\n\r\nReturns detailed information for database and server compatibility level for the db1 and db2 databases on \r\nsqlserver2014a.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaDatabaseCompatibility -SqlInstance sqlserver2014a, sql2016 -Exclude db1\r\n\r\nReturns detailed information for database and server compatibility level for all databases except db1 on sqlserver2014a \r\nand sql2016.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRegisteredServer -SqlInstance sql2014 | Test-DbaDatabaseCompatibility\r\n\r\nReturns db/server compatibility information for every database on every server listed in the Central Management Server \r\non sql2016.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Test-DbaDatabaseOwner",
        "Description":  "This function will check all databases on an instance against a SQL login to validate if that\nlogin owns those databases or not. By default, the function will check against \u0027sa\u0027 for\nownership, but the user can pass a specific login if they use something else.\n\nBest Practice reference: http://weblogs.sqlteam.com/dang/archive/2008/01/13/Database-Owner-Troubles.aspx",
        "Tags":  [
                     "Database",
                     "Owner",
                     "DbOwner"
                 ],
        "Author":  "Michael Fal (@Mike_Fal), http://mikefal.net",
        "Synopsis":  "Checks database owners against a login to validate which databases do not match that owner.",
        "Name":  "Test-DbaDatabaseOwner",
        "Links":  "https://dbatools.io/Test-DbaDatabaseOwner",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaDatabaseOwner -SqlInstance localhost\r\n\r\nReturns all databases where the owner does not match \u0027sa\u0027.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaDatabaseOwner -SqlInstance localhost -TargetLogin \u0027DOMAIN\\account\u0027\r\n\r\nReturns all databases where the owner does not match \u0027DOMAIN\\account\u0027.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Test-DbaDbCompression",
        "Description":  "This function returns the results of a full table/index compression analysis.\nThis function returns the best option to date for either NONE, Page, or Row Compression.\nRemember Uptime is critical, the longer uptime, the more accurate the analysis is.\nYou would probably be best if you utilized Get-DbaUptime first, before running this command.\n\nTest-DbaCompression script derived from GitHub and the tigertoolbox\n(https://github.com/Microsoft/tigertoolbox/tree/master/Evaluate-Compression-Gains)\nIn the output, you will find the following information:\nColumn Percent_Update shows the percentage of update operations on a specific table, index, or partition,\nrelative to total operations on that object. The lower the percentage of Updates\n(that is, the table, index, or partition is infrequently updated), the better candidate it is for page compression.\nColumn Percent_Scan shows the percentage of scan operations on a table, index, or partition, relative to total\noperations on that object. The higher the value of Scan (that is, the table, index, or partition is mostly scanned),\nthe better candidate it is for page compression.\nColumn Compression_Type_Recommendation can have four possible outputs indicating where there is most gain,\nif any: \u0027PAGE\u0027, \u0027ROW\u0027, \u0027NO_GAIN\u0027 or \u0027?\u0027. When the output is \u0027?\u0027 this approach could not give a recommendation,\nso as a rule of thumb I would lean to ROW if the object suffers mainly UPDATES, or PAGE if mainly INSERTS,\nbut this is where knowing your workload is essential. When the output is \u0027NO_GAIN\u0027 well, that means that according\nto sp_estimate_data_compression_savings no space gains will be attained when compressing, as in the above output example,\nwhere compressing would grow the affected object.\n\nNote: Note that this script will execute on the context of the current database.\nAlso be aware that this may take awhile to execute on large objects, because if the IS locks taken by the\nsp_estimate_data_compression_savings cannot be honored, the SP will be blocked.",
        "Tags":  [
                     "Compression",
                     "Table",
                     "Database"
                 ],
        "Author":  "Jason Squires (@js_0505, [email protected])",
        "Synopsis":  "Returns tables and indexes with preferred compression setting.",
        "Name":  "Test-DbaDbCompression",
        "Links":  "https://dbatools.io/Test-DbaCompression",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaCompression -SqlInstance localhost\r\n\r\nReturns all user database files and free space information for the local host\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaCompression -SqlInstance ServerA -Database DBName | Out-GridView\r\n\r\nReturns results of all potential compression options for a single database\r\nwith the recommendation of either Page or Row into and nicely formatted GridView\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaCompression -SqlInstance ServerA\r\n\r\nReturns results of all potential compression options for all databases\r\nwith the recommendation of either Page or Row\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003e$cred = Get-Credential sqladmin\r\n\r\nTest-DbaCompression -SqlInstance ServerA -ExcludeDatabase Database -SqlCredential $cred\r\nReturns results of all potential compression options for all databases\r\nwith the recommendation of either Page or Row\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003e$servers = \u0027Server1\u0027,\u0027Server2\u0027\r\n\r\nforeach ($svr in $servers)\r\n{\r\n    Test-DbaCompression -SqlInstance $svr | Export-Csv -Path C:\\temp\\CompressionAnalysisPAC.csv -Append\r\n}\r\n\r\nThis produces a full analysis of all your servers listed and is pushed to a csv for you to\r\nanalyze.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Test-DbaDbVirtualLogFile",
        "Description":  "Having a transaction log file with too many virtual log files (VLFs) can hurt database performance.\n\nToo many VLFs can cause transaction log backups to slow down and can also slow down database recovery and, in extreme cases, even affect insert/update/delete performance.\n\nReferences:\n    http://www.sqlskills.com/blogs/kimberly/transaction-log-vlfs-too-many-or-too-few/\n    http://blogs.msdn.com/b/saponsqlserver/archive/2012/02/22/too-many-virtual-log-files-vlfs-can-cause-slow-database-recovery.aspx\n\nIf you\u0027ve got a high number of VLFs, you can use Expand-SqlTLogResponsibly to reduce the number.",
        "Tags":  [
                     "VLF",
                     "Database"
                 ],
        "Synopsis":  "Returns calculations on the database virtual log files for database on a SQL instance.",
        "Name":  "Test-DbaDbVirtualLogFile",
        "Links":  "https://dbatools.io/Test-DbaDbVirtualLogFile",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaDbVirtualLogFile -SqlInstance sqlcluster\r\n\r\nReturns all user database virtual log file counts for the sqlcluster instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaDbVirtualLogFile -SqlInstance sqlserver | Where-Object {$_.Count -ge 50}\r\n\r\nReturns user databases that have 50 or more VLFs.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003e@(\u0027sqlserver\u0027,\u0027sqlcluster\u0027) | Test-DbaDbVirtualLogFile\r\n\r\nReturns all VLF information for the sqlserver and sqlcluster SQL Server instances. Processes data via the pipeline.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaDbVirtualLogFile -SqlInstance sqlcluster -Database db1, db2\r\n\r\nReturns VLF counts for the db1 and db2 databases on sqlcluster.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Test-DbaDiskAlignment",
        "Description":  "Returns $true or $false by default for one server. Returns Server name and IsBestPractice for more than one server.\n\nPlease refer to your storage vendor best practices before following any advice below.\n\nBy default issues with disk alignment should be resolved by a new installation of Windows Server 2008, Windows Vista, or later operating systems, but verifying disk alignment continues to be recommended as a best practice.\nWhile some versions of Windows use different starting alignments, if you are starting anew 1MB is generally the best practice offset for current operating systems (because it ensures that the partition offset % common stripe unit sizes == 0 )\n\nCaveats:\n* Dynamic drives (or those provisioned via third party software) may or may not have accurate results when polled by any of the built in tools, see your vendor for details.\n* Windows does not have a reliable way to determine stripe unit Sizes. These values are obtained from vendor disk management software or from your SAN administrator.\n* System drives in versions previous to Windows Server 2008 cannot be aligned, but it is generally not recommended to place SQL Server databases on system drives.",
        "Tags":  "Storage",
        "Author":  "Constantine Kokkinos (https://constantinekokkinos.com, @mobileck)",
        "Synopsis":  "Verifies that your non-dynamic disks are aligned according to physical constraints.",
        "Name":  "Test-DbaDiskAlignment",
        "Links":  "https://dbatools.io/Test-DbaDiskAlignment",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaDiskAlignment -ComputerName sqlserver2014a\r\n\r\nTests the disk alignment of a single server named sqlserver2014a\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaDiskAlignment -ComputerName sqlserver2014a, sqlserver2014b, sqlserver2014c\r\n\r\nTests the disk alignment of multiple servers\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Test-DbaDiskAllocation",
        "Description":  "Checks all disks on a computer for disk allocation units that match best practice recommendations. If one server is checked, only $true or $false is returned. If multiple servers are checked, each server\u0027s name and an IsBestPractice field are returned.\n\nSpecify -Detailed for details.\n\nReferences:\nhttps://technet.microsoft.com/en-us/library/dd758814(v=sql.100).aspx - \"The performance question here is usually not one of correlation per the formula, but whether the cluster size has been explicitly defined at 64 KB, which is a best practice for SQL Server.\"\n\nhttp://tk.azurewebsites.net/2012/08/",
        "Tags":  [
                     "CIM",
                     "Storage"
                 ],
        "Synopsis":  "Checks all disks on a computer to see if they are formatted with allocation units of 64KB.",
        "Name":  "Test-DbaDiskAllocation",
        "Links":  "https://dbatools.io/Test-DbaDiskAllocation",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaDiskAllocation -ComputerName sqlserver2014a\r\n\r\nScans all disks on server sqlserver2014a for best practice allocation unit size.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaDiskAllocation -ComputerName sqlserver2014 | Select-Output *\r\n\r\nScans all disks on server sqlserver2014a for allocation unit size and returns detailed results for each.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaDiskAllocation -ComputerName sqlserver2014a -NoSqlCheck\r\n\r\nScans all disks not hosting SQL Server data or log files on server sqlserver2014a for best practice allocation unit \r\nsize.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Test-DbaDiskSpeed",
        "Description":  "Tests how disks are performing.\n\nThis command uses a query from Rich Benner which was adapted from David Pless\u0027s article:\nhttps://blogs.msdn.microsoft.com/dpless/2010/12/01/leveraging-sys-dm_io_virtual_file_stats/\nhttps://github.com/RichBenner/PersonalCode/blob/master/Disk_Speed_Check.sql",
        "Tags":  "Performance",
        "Author":  "Chrissy LeMaire",
        "Synopsis":  "Tests how disks are performing.",
        "Name":  "Test-DbaDiskSpeed",
        "Links":  "https://dbatools.io/Test-DbaDiskSpeed",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaDiskSpeed -SqlInstance sql2008, sqlserver2012\r\n\r\nTests how disks are performing on sql2008 and sqlserver2012.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaDiskSpeed -SqlInstance sql2008 -Database tempdb\r\n\r\nTests how disks storing tempdb files on sql2008 are performing.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Test-DbaIdentityUsage",
        "Description":  "IDENTITY seeds have max values based off of their data type.  This module will locate identity columns and report the seed usage.",
        "Tags":  [
                     "Identity",
                     "Table",
                     "Column"
                 ],
        "Author":  "Brandon Abshire, netnerds.net",
        "Synopsis":  "Displays information relating to IDENTITY seed usage.  Works on SQL Server 2008 and above.",
        "Name":  "Test-DbaIdentityUsage",
        "Links":  "https://dbatools.io/Test-DbaIdentityUsage",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaIdentityUsage -SqlInstance sql2008, sqlserver2012\r\n\r\nCheck identity seeds for servers sql2008 and sqlserver2012.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaIdentityUsage -SqlInstance sql2008 -Database TestDB\r\n\r\nCheck identity seeds on server sql2008 for only the TestDB database\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaIdentityUsage -SqlInstance sql2008 -Database TestDB -Threshold 20\r\n\r\nCheck identity seeds on server sql2008 for only the TestDB database, limiting results to 20% utilization of seed range \r\nor higher\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Test-DbaJobOwner",
        "Description":  "This function checks all SQL Agent Jobs on an instance against a SQL login to validate if that login owns those SQL Agent Jobs or not.\n\nBy default, the function checks against \u0027sa\u0027 for ownership, but the user can pass a specific login if they use something else.\n\nOnly SQL Agent Jobs that do not match this ownership will be displayed.\n\nBest practice reference: http://sqlmag.com/blog/sql-server-tip-assign-ownership-jobs-sysadmin-account",
        "Tags":  [
                     "Agent",
                     "Job",
                     "Owner"
                 ],
        "Author":  "Michael Fal (@Mike_Fal), http://mikefal.net",
        "Synopsis":  "Checks SQL Agent Job owners against a login to validate which jobs do not match that owner.",
        "Name":  "Test-DbaJobOwner",
        "Links":  "https://dbatools.io/Test-DbaJobOwner",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaJobOwner -SqlInstance localhost\r\n\r\nReturns all SQL Agent Jobs where the owner does not match \u0027sa\u0027.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaJobOwner -SqlInstance localhost -ExcludeJob \u0027syspolicy_purge_history\u0027\r\n\r\nReturns SQL Agent Jobs except for the syspolicy_purge_history job\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaJobOwner -SqlInstance localhost -Login DOMAIN\\account\r\n\r\nReturns all SQL Agent Jobs where the owner does not match DOMAIN\\account. Note\r\nthat Login must be a valid security principal that exists on the target server.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Test-DbaLastBackup",
        "Description":  "Restores all or some of the latest backups and performs a DBCC CHECKDB.\n\n1. Gathers information about the last full backups\n2. Restores the backups to the Destination with a new name. If no Destination is specified, the originating SqlServer wil be used.\n3. The database is restored as \"dbatools-testrestore-$databaseName\" by default, but you can change dbatools-testrestore to whatever you would like using -Prefix\n4. The internal file names are also renamed to prevent conflicts with original database\n5. A DBCC CHECKDB is then performed\n6. And the test database is finally dropped",
        "Tags":  [
                     "DisasterRecovery",
                     "Backup",
                     "Restore"
                 ],
        "Synopsis":  "Quickly and easily tests the last set of full backups for a server.",
        "Name":  "Test-DbaLastBackup",
        "Links":  "https://dbatools.io/Test-DbaLastBackup",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaLastBackup -SqlInstance sql2016\r\n\r\nDetermines the last full backup for ALL databases, attempts to restore all databases (with a different name and file \r\nstructure), then performs a DBCC CHECKDB.\r\n\r\nOnce the test is complete, the test restore will be dropped.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaLastBackup -SqlInstance sql2016 -Database master\r\n\r\nDetermines the last full backup for master, attempts to restore it, then performs a DBCC CHECKDB.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaLastBackup -SqlInstance sql2016 -Database model, master -VerifyOnly\r\n\r\n\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaLastBackup -SqlInstance sql2016 -NoCheck -NoDrop\r\n\r\nSkips the DBCC CHECKDB check. This can help speed up the tests but makes it less tested. The test restores will remain \r\non the server.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaLastBackup -SqlInstance sql2016 -DataDirectory E:\\bigdrive -LogDirectory L:\\bigdrive -MaxMB 10240\r\n\r\nRestores data and log files to alternative locations and only restores databases that are smaller than 10 GB.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaLastBackup -SqlInstance sql2014 -Destination sql2016 -CopyFile\r\n\r\nCopies the backup files for sql2014 databases to sql2016 default backup locations and then attempts restore from there.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 7 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaLastBackup -SqlInstance sql2014 -Destination sql2016 -CopyFile -CopyPath \"\\\\BackupShare\\TestRestore\\\"\r\n\r\nCopies the backup files for sql2014 databases to sql2016 default backup locations and then attempts restore from there.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Test-DbaLinkedServerConnection",
        "Description":  "Test each linked server on the instance",
        "Tags":  "LinkedServer",
        "Author":  "Thomas LaRock ( https://thomaslarock.com )",
        "Synopsis":  "Test all linked servers from the sql servers passed",
        "Name":  "Test-DbaLinkedServerConnection",
        "Links":  "https://dbatools.io/Test-DbaLinkedServerConnection",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaLinkedServerConnection -SqlInstance DEV01\r\n\r\nTest all Linked Servers for the SQL Server instance DEV01\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaLinkedServerConnection -SqlInstance sql2016 | Out-File C:\\temp\\results.txt\r\n\r\nTest all Linked Servers for the SQL Server instance sql2016 and output results to file\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaLinkedServerConnection -SqlInstance sql2016, sql2014, sql2012\r\n\r\nTest all Linked Servers for the SQL Server instances sql2016, sql2014 and sql2012\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003e$servers = \"sql2016\",\"sql2014\",\"sql2012\"\r\n\r\n$servers | Test-DbaLinkedServerConnection -SqlCredential (Get-Credential sqladmin)\r\n\r\nTest all Linked Servers for the SQL Server instances sql2016, sql2014 and sql2012 using SQL login credentials\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003e$servers | Get-DbaLinkedServer | Test-DbaLinkedServerConnection\r\n\r\nTest all Linked Servers for the SQL Server instances sql2016, sql2014 and sql2012\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Test-DbaLoginPassword",
        "Description":  "The purpose of this function is to find SQL Server logins that have no password or the same password as login. You can add your own password to check for or add them to a csv file.\nBy default it will test for empty password and the same password as username.",
        "Author":  "Peter Samuelsson",
        "Synopsis":  "Test-DbaLoginPassword finds any logins on SQL instance that are SQL Logins and have a password that is either null or same as the login",
        "Name":  "Test-DbaLoginPassword",
        "Links":  "https://dbatools.io/Test-DbaLoginPassword",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaLoginPassword -SqlInstance Dev01\r\n\r\nTest all SQL logins that the password is null or same as username on SQL server instance Dev01\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaLoginPassword -SqlInstance Dev01 -Login sqladmin\r\n\r\nTest the \u0027sqladmin\u0027 SQL login that the password is null or same as username on SQL server instance Dev01\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaLoginPassword -SqlInstance Dev01 -Dictionary Test1,test2\r\n\r\nTest all SQL logins that the password is null, same as username or Test1,Test2 on SQL server instance Dev0\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaLogin -SqlInstance \"sql2017\",\"sql2016\" | Test-DbaLoginPassword\r\n\r\nTest all logins on sql2017 and sql2016\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003e$servers | Get-DbaLogin | Out-GridView -Passthru | Test-DbaLoginPassword\r\n\r\nTest selected logins on all servers in the $servers variable\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Test-DbaLogShippingStatus",
        "Description":  "Most of the time your log shipping \"just works\".\nChecking your log shipping status can be done really easy with this function.\n\nMake sure you\u0027re connecting to the monitoring instance of your log shipping infrastructure.\n\nThe function will return the status for a database. This can be one or more messages in a comma separated list.\nIf everything is OK with the database than you should only see the message \"All OK\".",
        "Tags":  "LogShipping",
        "Author":  "Sander Stad (@sqlstad, sqlstad.nl)",
        "Synopsis":  "Test-DbaLogShippingStatus returns the status of your log shipping databases",
        "Name":  "Test-DbaLogShippingStatus",
        "Links":  "https://dbatools.io/Test-DbaLogShippingStatus",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaLogShippingStatus -SqlInstance sql1\r\n\r\nRetrieves the log ship information from sql1 and displays all the information present including the status.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaLogShippingStatus -SqlInstance sql1 -Database AdventureWorks2014\r\n\r\nRetrieves the log ship information for just the database AdventureWorks.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaLogShippingStatus -SqlInstance sql1 -Primary\r\n\r\nRetrieves the log ship information and only returns the information for the databases on the primary instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaLogShippingStatus -SqlInstance sql1 -Secondary\r\n\r\nRetrieves the log ship information and only returns the information for the databases on the secondary instance.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaLogShippingStatus -SqlInstance sql1 -Simple\r\n\r\nRetrieves the log ship information and only returns the columns SQL Instance, Database, Instance Type and Status\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Test-DbaMaxDop",
        "Description":  "Inspired by Sakthivel Chidambaram\u0027s post about SQL Server MAXDOP Calculator (https://blogs.msdn.microsoft.com/sqlsakthi/p/maxdop-calculator-SqlInstance/),\nthis script displays a SQL Server\u0027s: max dop configured, and the calculated recommendation.\n\nFor SQL Server 2016 shows:\n    - Instance max dop configured and the calculated recommendation\n    - max dop configured per database (new feature)\n\nMore info:\n    https://support.microsoft.com/en-us/kb/2806535\n    https://blogs.msdn.microsoft.com/sqlsakthi/2012/05/23/wow-we-have-maxdop-calculator-for-sql-server-it-makes-my-job-easier/\n\nThese are just general recommendations for SQL Server and are a good starting point for setting the \"max degree of parallelism\" option.",
        "Tags":  [
                     "MaxDop",
                     "SpConfigure"
                 ],
        "Synopsis":  "Displays information relating to SQL Server Max Degree of Parallelism setting. Works on SQL Server 2005-2016.",
        "Name":  "Test-DbaMaxDop",
        "Links":  "https://dbatools.io/Test-DbaMaxDop",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaMaxDop -SqlInstance sql2008, sqlserver2012\r\n\r\nGet Max DOP setting for servers sql2008 and sqlserver2012 and also the recommended one.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaMaxDop -SqlInstance sql2014 | Select-Object *\r\n\r\nShows Max DOP setting for server sql2014 with the recommended value. Piping the output to Select-Object * will also \r\nshow the \u0027NUMANodes\u0027 and \u0027NumberOfCores\u0027 of each instance\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaMaxDop -SqlInstance sqlserver2016 | Select-Object *\r\n\r\nGet Max DOP setting for servers sql2016 with the recommended value. Piping the output to Select-Object * will also show \r\nthe \u0027NUMANodes\u0027 and \u0027NumberOfCores\u0027 of each instance. Because it is an 2016 instance will be shown \u0027InstanceVersion\u0027, \r\n\u0027Database\u0027 and \u0027DatabaseMaxDop\u0027 columns.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Test-DbaMaxMemory",
        "Description":  "Inspired by Jonathan Kehayias\u0027s post about SQL Server Max memory (http://bit.ly/sqlmemcalc), this script displays a SQL Server\u0027s: total memory, currently configured SQL max memory, and the calculated recommendation.\n\nJonathan notes that the formula used provides a *general recommendation* that doesn\u0027t account for everything that may be going on in your specific environment.",
        "Tags":  [
                     "MaxMemory",
                     "Memory"
                 ],
        "Synopsis":  "Calculates the recommended value for SQL Server \u0027Max Server Memory\u0027 configuration setting. Works on SQL Server 2000-2014.",
        "Name":  "Test-DbaMaxMemory",
        "Links":  "https://dbatools.io/Test-DbaMaxMemory",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaMaxMemory -SqlInstance sqlcluster,sqlserver2012\r\n\r\nCalculate the \u0027Max Server Memory\u0027 settings for all servers within the SQL Server Central Management Server \"sqlcluster\"\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaMaxMemory -SqlInstance sqlcluster | Where-Object { $_.SqlMaxMB -gt $_.TotalMB } | Set-DbaMaxMemory\r\n\r\nFind all servers in CMS that have Max SQL memory set to higher than the total memory of the server (think 2147483647) \r\nand set it to recommended value.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Test-DbaMigrationConstraint",
        "Description":  "When you want to migrate from a higher edition to a lower one there are some features that can\u0027t be used.\nThis function will validate if you have any of this features in use and will report to you.\nThe validation will be made ONLY on on SQL Server 2008 or higher using the \u0027sys.dm_db_persisted_sku_features\u0027 dmv.\n\nThis function only validate SQL Server 2008 versions or higher.\nThe editions supported by this function are:\n    - Enterprise\n    - Developer\n    - Evaluation\n    - Standard\n    - Express\n\nTake into account the new features introduced on SQL Server 2016 SP1 for all versions. More information at https://blogs.msdn.microsoft.com/sqlreleaseservices/sql-server-2016-service-pack-1-sp1-released/\n\nThe -Database parameter is auto-populated for command-line completion.",
        "Tags":  "Migration",
        "Author":  "Claudio Silva (@ClaudioESSilva)",
        "Synopsis":  "Show if you can migrate the database(s) between the servers.",
        "Name":  "Test-DbaMigrationConstraint",
        "Links":  "https://dbatools.io/Test-DbaMigrationConstraint",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaMigrationConstraint -Source sqlserver2014a -Destination sqlcluster\r\n\r\nAll databases on sqlserver2014a will be verified for features in use that can\u0027t be supported on sqlcluster.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaMigrationConstraint -Source sqlserver2014a -Destination sqlcluster -SqlCredential $cred\r\n\r\nAll databases will be verified for features in use that can\u0027t be supported on the destination server. SQL credentials \r\nare used to authenticate against sqlserver2014 and Windows Authentication is used for sqlcluster.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaMigrationConstraint -Source sqlserver2014a -Destination sqlcluster -Database db1\r\n\r\nOnly db1 database will be verified for features in use that can\u0027t be supported on the destination server.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Test-DbaNetworkLatency",
        "Description":  "This function is intended to help measure SQL Server network latency by establishing a connection and executing a simple query. This is a better than a simple ping because it actually creates the connection to the SQL Server and measures the time required for only the entire routine, but the duration of the query as well how long it takes for the results to be returned.\n\nBy default, this command will execute \"SELECT TOP 100 * FROM INFORMATION_SCHEMA.TABLES\" three times.\n\nIt will then output how long the entire connection and command took, as well as how long *only* the execution of the command took.\n\nThis allows you to see if the issue is with the connection or the SQL Server itself.",
        "Tags":  [
                     "Performance",
                     "Network"
                 ],
        "Synopsis":  "Tests how long a query takes to return from SQL Server",
        "Name":  "Test-DbaNetworkLatency",
        "Links":  "https://dbatools.io/Test-DbaNetworkLatency",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaNetworkLatency -SqlInstance sqlserver2014a, sqlcluster\r\n\r\nTests the roundtrip return of \"SELECT TOP 100 * FROM INFORMATION_SCHEMA.TABLES\" on sqlserver2014a and sqlcluster using \r\nWindows credentials.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaNetworkLatency -SqlInstance sqlserver2014a -SqlCredential $cred\r\n\r\nTests the execution results return of \"SELECT TOP 100 * FROM INFORMATION_SCHEMA.TABLES\" on sqlserver2014a using SQL \r\ncredentials.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaNetworkLatency -SqlInstance sqlserver2014a, sqlcluster, sqlserver -Query \"select top 10 * from \r\notherdb.dbo.table\" -Count 10\r\n\r\nTests the execution results return of \"select top 10 * from otherdb.dbo.table\" 10 times on sqlserver2014a, sqlcluster, \r\nand sqlserver using Windows credentials.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Test-DbaOptimizeForAdHoc",
        "Description":  "When this option is set, plan cache size is further reduced for single-use ad hoc OLTP workload.\n\nMore info: https://msdn.microsoft.com/en-us/library/cc645587.aspx\nhttp://www.sqlservercentral.com/blogs/glennberry/2011/02/25/some-suggested-sql-server-2008-r2-instance-configuration-settings/\n\nThese are just general recommendations for SQL Server and are a good starting point for setting the \"optimize for adhoc workloads\" option.",
        "Tags":  [
                     "Configure",
                     "SPConfigure"
                 ],
        "Author":  "Brandon Abshire, netnerds.net",
        "Synopsis":  "Displays information relating to SQL Server Optimize for AdHoc Workloads setting.  Works on SQL Server 2008-2016.",
        "Name":  "Test-DbaOptimizeForAdHoc",
        "Links":  "https://dbatools.io/Test-DbaOptimizeForAdHoc",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaOptimizeForAdHoc -SqlInstance sql2008, sqlserver2012\r\n\r\nValidates whether Optimize for AdHoc Workloads setting is enabled for servers sql2008 and sqlserver2012.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Test-DbaPowerPlan",
        "Description":  "Checks the Power Plan settings on a computer against best practices recommendations. If one server is checked, only $true or $false is returned. If multiple servers are checked, each server\u0027s name and an isBestPractice field are returned.\n\nReferences:\nhttps://support.microsoft.com/en-us/kb/2207548\nhttp://www.sqlskills.com/blogs/glenn/windows-power-plan-effects-on-newer-intel-processors/",
        "Tags":  "PowerPlan",
        "Synopsis":  "Checks the Power Plan settings for compliance with best practices, which recommend High Performance for SQL Server.",
        "Name":  "Test-DbaPowerPlan",
        "Links":  "https://dbatools.io/Test-DbaPowerPlan",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaPowerPlan -ComputerName sqlserver2014a\r\n\r\nChecks the Power Plan settings for sqlserver2014a and indicates whether or not it complies with best practices.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaPowerPlan -ComputerName sqlserver2014a -CustomPowerPlan \u0027Maximum Performance\u0027\r\n\r\nChecks the Power Plan settings for sqlserver2014a and indicates whether or not it is set to the custom plan \"Maximum \r\nPerformance\".\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Test-DbaRecoveryModel",
        "Description":  "When you switch a database into FULL recovery model, it will behave like a SIMPLE recovery model until a full backup is taken in order to begin a log backup chain.\n\nHowever, you may also desire to validate if a database is SIMPLE or BULK LOGGED on an instance.\n\nInspired by Paul Randal\u0027s post (http://www.sqlskills.com/blogs/paul/new-script-is-that-database-really-in-the-full-recovery-mode/)",
        "Tags":  [
                     "DisasterRecovery",
                     "Backup"
                 ],
        "Author":  "Claudio Silva (@ClaudioESSilva)",
        "Synopsis":  "Find if database is really a specific recovery model or not.",
        "Name":  "Test-DbaRecoveryModel",
        "Links":  "https://dbatools.io/Test-DbaRecoveryModel",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaRecoveryModel -SqlInstance sql2005\r\n\r\nShows all databases where the configured recovery model is FULL and indicates whether or not they are really in FULL \r\nrecovery model.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaRecoveryModel -SqlInstance . | Where-Object {$_.ActualRecoveryModel -ne \"FULL\"}\r\n\r\nOnly shows the databases that are functionally in \u0027simple\u0027 mode.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaRecoveryModel -SqlInstance sql2008 -RecoveryModel Bulk_Logged | Sort-Object Server  -Descending\r\n\r\nShows all databases where the configured recovery model is BULK_LOGGED and sort them by server name descending\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaRecoveryModel -SqlInstance localhost | Select-Object -Property *\r\n\r\nShows all of the properties for the databases that have Full Recovery Model\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Test-DbaServerName",
        "Description":  "When a SQL Server\u0027s host OS is renamed, the SQL Server should be as well. This helps with Availability Groups and Kerberos.\n\nThis command helps determine if your OS and SQL Server names match, and whether a rename is required.\n\nIt then checks conditions that would prevent a rename, such as database mirroring and replication.\n\nhttps://www.mssqltips.com/sqlservertip/2525/steps-to-change-the-server-name-for-a-sql-server-machine/",
        "Tags":  [
                     "SPN",
                     "ServerName"
                 ],
        "Synopsis":  "Tests to see if it\u0027s possible to easily rename the server at the SQL Server instance level, or if it even needs to be changed.",
        "Name":  "Test-DbaServerName",
        "Links":  "https://dbatools.io/Test-DbaServerName",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaServerName -SqlInstance sqlserver2014a\r\n\r\nReturns ServerInstanceName, SqlServerName, IsEqual and RenameRequired for sqlserver2014a.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaServerName -SqlInstance sqlserver2014a, sql2016\r\n\r\nReturns ServerInstanceName, SqlServerName, IsEqual and RenameRequired for sqlserver2014a and sql2016.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaServerName -SqlInstance sqlserver2014a, sql2016 -ExcludeSsrs\r\n\r\nReturns ServerInstanceName, SqlServerName, IsEqual and RenameRequired for sqlserver2014a and sql2016, but skips \r\nvalidating if SSRS is installed on both instances.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaServerName -SqlInstance sqlserver2014a, sql2016 | Select-Object *\r\n\r\nReturns ServerInstanceName, SqlServerName, IsEqual and RenameRequired for sqlserver2014a and sql2016.\r\n\r\nIf a Rename is required, it will also show Updatable, and Reasons if the servername is not updatable.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Test-DbaSpn",
        "Description":  "This function is designed to take in a server name(s) and attempt to determine required SPNs. It was initially written to mimic the (previously)\nbroken functionality of the Microsoft Kerberos Configuration manager and SQL Server 2016. The functon will connect to a remote server and,\nthrough WMI, discover all running intances of SQL Server. For any instances with TCP/IP enabled, the script will determine which port(s)\nthe instances are listening on and generate the required SPNs. For named instances NOT using dynamic ports, the script will generate a port-\nbased SPN for those instances as well.  At a minimum, the script will test a base, port-less SPN for each instance discovered.\n\nOnce the required SPNs are generated, the script will connect to Active Directory and search for any of the SPNs (if any) that are already\nset.\n\nThe function will return a custom object(s) that contains the server name checked, the instance name discovered, the account the service is\nrunning under, and what the \"required\" SPN should be. It will also return a boolean property indicating if the SPN is set in Active Directory\nor not.",
        "Tags":  "SPN",
        "Author":  "Drew Furgiuele (@pittfurg), http://www.port1433.com",
        "Synopsis":  "Test-DbaSpn will determine what SPNs *should* be set for a given server (and any instances of SQL running on it) and return\nwhether the SPNs are set or not.",
        "Name":  "Test-DbaSpn",
        "Links":  "https://dbatools.io/Test-DbaSpn",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaSpn -ComputerName SQLSERVERA -Credential (Get-Credential)\r\n\r\nConnects to a computer (SQLSERVERA) and queries WMI for all SQL instances and return \"required\" SPNs. It will then take \r\neach SPN it generates\r\nand query Active Directory to make sure the SPNs are set.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaSpn -ComputerName SQLSERVERA,SQLSERVERB -Credential (Get-Credential)\r\n\r\nConnects to multiple computers (SQLSERVERA, SQLSERVERB) and queries WMI for all SQL instances and return \"required\" \r\nSPNs.\r\nIt will then take each SPN it generates and query Active Directory to make sure the SPNs are set.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaSpn -ComputerName SQLSERVERC -Credential (Get-Credential)\r\n\r\nConnects to a computer (SQLSERVERC) on a specified and queries WMI for all SQL instances and return \"required\" SPNs.\r\nIt will then take each SPN it generates and query Active Directory to make sure the SPNs are set. Note that the \r\ncredential you pass must have be a valid login with appropriate rights on the domain\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Test-DbaSqlBuild",
        "Description":  "It answers the question \"is this build up to date ?\"\nReturns info about the specific build of a SQL instance, including the SP, the CU and the reference KB, End Of Support, wherever possible.\nIt adds a Compliance property as true/false, and adds details about the \"targeted compliance\"",
        "Tags":  "SqlBuild",
        "Author":  "niphlod",
        "Synopsis":  "Returns SQL Server Build \"compliance\" level on a build",
        "Name":  "Test-DbaSqlBuild",
        "Links":  "https://dbatools.io/Test-DbaSqlBuild",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaSqlBuild -Build \"12.0.5540\" -MinimumBuild \"12.0.5557\"\r\n\r\nReturns information about a build identified by \"12.0.5540\" (which is SQL 2014 with SP2 and CU4), which is not \r\ncompliant as the minimum required\r\nbuild is \"12.0.5557\" (which is SQL 2014 with SP2 and CU8)\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaSqlBuild -Build \"12.0.5540\" -MaxBehind \"1SP\"\r\n\r\nReturns information about a build identified by \"12.0.5540\", making sure it is AT MOST 1 Service Pack \"behind\". For \r\nthat version,\r\nthat identifies an SP2, means accepting as the lowest compliance version as \"12.0.4110\", that identifies 2014 with SP1\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaSqlBuild -Build \"12.0.5540\" -MaxBehind \"1SP 1CU\"\r\n\r\nReturns information about a build identified by \"12.0.5540\", making sure it is AT MOST 1 Service Pack \"behind\", plus 1 \r\nCU \"behind\". For that version,\r\nthat identifies an SP2 and CU, rolling back 1 SP brings you to \"12.0.4110\", but given the latest CU for SP1 is CU13, \r\nthe target \"compliant\" build\r\nwill be \"12.0.4511\", which is 2014 with SP1 and CU12\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaSqlBuild -Build \"12.0.5540\" -MaxBehind \"0CU\"\r\n\r\nReturns information about a build identified by \"12.0.5540\", making sure it is the latest CU release.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaSqlBuild -Build \"12.0.5540\" -Latest\r\n\r\nSame as previous, returns information about a build identified by \"12.0.5540\", making sure it is the latest build \r\navailable.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaSqlBuild -Build \"12.00.4502\" -MinimumBuild \"12.0.4511\" -Update\r\n\r\nSame as before, but tries to fetch the most up to date index online. When the online version is newer, the local one \r\ngets overwritten\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 7 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaSqlBuild -Build \"12.0.4502\",\"10.50.4260\" -MinimumBuild \"12.0.4511\"\r\n\r\nReturns information builds identified by these versions strings\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 8 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaRegisteredServer -SqlInstance sqlserver2014a | Test-DbaSqlBuild -MinimumBuild \"12.0.4511\"\r\n\r\nIntegrate with other commandlets to have builds checked for all your registered servers on sqlserver2014a\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Test-DbaSqlManagementObject",
        "Description":  "The Test-DbaSqlManagementObject returns True if the Version is on the computer, and False if it does not exist.",
        "Tags":  "SMO",
        "Author":  "Ben Miller (@DBAduck - http://dbaduck.com)",
        "Synopsis":  "Tests to see if the SMO version specified exists on the computer.",
        "Name":  "Test-DbaSqlManagementObject",
        "Links":  "https://dbatools.io/Test-DbaSqlManagementObject",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaSqlManagementObject -VersionNumber 13\r\n\r\nReturns True if the version exists, if it does not exist it will return False\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Test-DbaSqlPath",
        "Description":  "Uses master.dbo.xp_fileexist to determine if a file or directory exists.",
        "Tags":  [
                     "Path",
                     "ServiceAccount"
                 ],
        "Author":  "Chrissy LeMaire (@cl), netnerds.net",
        "Synopsis":  "Tests if file or directory exists from the perspective of the SQL Server service account.",
        "Name":  "Test-DbaSqlPath",
        "Links":  "https://dbatools.io/Test-DbaSqlPath",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaSqlPath -SqlInstance sqlcluster -Path L:\\MSAS12.MSSQLSERVER\\OLAP\r\n\r\nTests whether the service account running the \"sqlcluster\" SQL Server instance can access L:\\MSAS12.MSSQLSERVER\\OLAP. \r\nLogs into sqlcluster using Windows credentials.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e$credential = Get-Credential\r\n\r\nTest-DbaSqlPath -SqlInstance sqlcluster -SqlCredential $credential -Path L:\\MSAS12.MSSQLSERVER\\OLAP\r\n\r\nTests whether the service account running the \"sqlcluster\" SQL Server instance can access L:\\MSAS12.MSSQLSERVER\\OLAP. \r\nLogs into sqlcluster using SQL authentication.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Test-DbaTempDbConfiguration",
        "Description":  "Evaluates tempdb against a set of rules to match best practices. The rules are:\n\n* TF 1118 enabled - Is Trace Flag 1118 enabled (See KB328551).\n* File Count - Does the count of data files in tempdb match the number of logical cores, up to 8?\n* File Growth - Are any files set to have percentage growth? Best practice is all files have an explicit growth value.\n* File Location - Is tempdb located on the C:\\? Best practice says to locate it elsewhere.\n* File MaxSize Set (optional) - Do any files have a max size value? Max size could cause tempdb problems if it isn\u0027t allowed to grow.\n\nOther rules can be added at a future date.",
        "Tags":  [
                     "tempdb",
                     "configuration"
                 ],
        "Author":  "Michael Fal (@Mike_Fal), http://mikefal.net",
        "Synopsis":  "Evaluates tempdb against several rules to match best practices.",
        "Name":  "Test-DbaTempDbConfiguration",
        "Links":  "https://dbatools.io/Test-DbaTempDbConfiguration",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaTempDbConfiguration -SqlInstance localhost\r\n\r\nChecks tempdb on the localhost machine.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaTempDbConfiguration -SqlInstance localhost | Select-Object *\r\n\r\nChecks tempdb on the localhost machine. All rest results are shown.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Test-DbaWindowsLogin",
        "Description":  "The purpose of this function is to find SQL Server logins that are used by active directory users that are either disabled or removed from the domain. It allows you to keep your logins accurate and up to date by removing accounts that are no longer needed.",
        "Tags":  [
                     "Login",
                     "Security"
                 ],
        "Author":  "Stephen Bennett: https://sqlnotesfromtheunderground.wordpress.com/",
        "Synopsis":  "Test-DbaWindowsLogin finds any logins on SQL instance that are AD logins with either disabled AD user accounts or ones that no longer exist",
        "Name":  "Test-DbaWindowsLogin",
        "Links":  "https://dbatools.io/Test-DbaWindowsLogin",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaWindowsLogin -SqlInstance Dev01\r\n\r\nTests all logins in the current Active Directory domain that are either disabled or do not exist on the SQL Server \r\ninstance Dev01\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaWindowsLogin -SqlInstance Dev01 -FilterBy GroupsOnly | Select-Object -Property *\r\n\r\nTests all Active Directory groups that have logins on Dev01, and shows all information for those logins\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eTest-DbaWindowsLogin -SqlInstance Dev01 -IgnoreDomains testdomain\r\n\r\nTests all Domain logins excluding any that are from the testdomain\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Uninstall-DbaWatchUpdate",
        "Description":  "Removes the scheduled task created for Watch-DbaUpdate by Install-DbaWatchUpdate so that notifications no longer pop up.",
        "Tags":  [
                     "JustForFun",
                     "Module"
                 ],
        "Synopsis":  "Removes the scheduled task created for Watch-DbaUpdate by Install-DbaWatchUpdate so that notifications no longer pop up.",
        "Name":  "Uninstall-DbaWatchUpdate",
        "Links":  "https://dbatools.io/Uninstall-DbaWatchUpdate",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eUninstall-DbaWatchUpdate\r\n\r\nRemoves the scheduled task created by Install-DbaWatchUpdate.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Update-DbaSqlServiceAccount",
        "Description":  "Reconfigure the service account or update the password of the specified SQL Server service. The service will be restarted in the event of changing the account.",
        "Tags":  [
                     "Service",
                     "SqlServer",
                     "Instance",
                     "Connect"
                 ],
        "Author":  "Kirill Kravtsov (@nvarscar)",
        "Synopsis":  "Changes service account (or just its password) of the SQL Server service.",
        "Name":  "Update-DbaSqlServiceAccount",
        "Links":  null,
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003e$NewPassword = ConvertTo-SecureString \u0027Qwerty1234\u0027 -AsPlainText -Force\r\n\r\nUpdate-DbaSqlServiceAccount -ComputerName sql1 -ServiceName \u0027MSSQL$MYINSTANCE\u0027 -Password $NewPassword\r\n\r\nChanges the current service account\u0027s password of the service MSSQL$MYINSTANCE to \u0027Qwerty1234\u0027\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e$cred = Get-Credential\r\n\r\nGet-DbaSqlService sql1 -Type Engine,Agent -Instance MYINSTANCE | Update-DbaSqlServiceAccount -ServiceCredential $cred\r\n\r\nRequests credentials from the user and configures them as a service account for the SQL Server engine and agent \r\nservices of the instance sql1\\MYINSTANCE\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eUpdate-DbaSqlServiceAccount -ComputerName sql1,sql2 -ServiceName \u0027MSSQLSERVER\u0027,\u0027SQLSERVERAGENT\u0027 -Username \r\nNETWORKSERVICE\r\n\r\nConfigures SQL Server engine and agent services on the machines sql1 and sql2 to run under Network Service system user.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaSqlService sql1 -Type Engine -Instance MSSQLSERVER | Update-DbaSqlServiceAccount -Username \r\n\u0027MyDomain\\sqluser1\u0027\r\n\r\nConfigures SQL Server engine service on the machine sql1 to run under \u0027MyDomain\\sqluser1\u0027. Will request user to input \r\nthe account password.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Update-Dbatools",
        "Description":  "Exported function. Updates dbatools. Deletes current copy and replaces it with freshest copy.",
        "Tags":  "Module",
        "Synopsis":  "Exported function. Updates dbatools. Deletes current copy and replaces it with freshest copy.",
        "Name":  "Update-Dbatools",
        "Links":  "https://dbatools.io/Update-DbaTools",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eUpdate-Dbatools\r\n\r\nUpdates dbatools. Deletes current copy and replaces it with freshest copy.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eUpdate-Dbatools -dev\r\n\r\nUpdates dbatools to the current development branch. Deletes current copy and replaces it with latest from github.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Watch-DbaDbLogin",
        "Description":  "Watch-DbaDbLogin uses SQL Server DMVs to track logins into a SQL Server table. This is helpful when you need to migrate a SQL Server and update connection strings, but have inadequate documentation on which servers/applications are logging into your SQL instance.\n\nRunning this script every 5 minutes for a week should give you a sufficient idea about database and login usage.",
        "Tags":  "Login",
        "Author":  "Chrissy LeMaire (@cl), netnerds.net",
        "Synopsis":  "Tracks SQL Server logins: which host they came from, what database they\u0027re using, and what program is being used to log in.",
        "Name":  "Watch-DbaDbLogin",
        "Links":  "https://dbatools.io/Watch-DbaDbLogin",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eWatch-DbaDbLogin -SqlInstance sqlserver -SqlCms SqlCms1\r\n\r\nA list of all database instances within the Central Management Server SqlCms1 is generated. Using this list, the script \r\nenumerates all the processes and gathers login information and saves it to the table Dblogins in the DatabaseLogins \r\ndatabase on SQL Server sqlserver.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eWatch-DbaDbLogin -SqlInstance sqlcluster -Database CentralAudit -ServersFromFile .\\sqlservers.txt\r\n\r\nA list of servers is gathered from the file sqlservers.txt in the current directory. Using this list, the script \r\nenumerates all the processes and gathers login information and saves it to the table Dblogins in the CentralAudit \r\ndatabase on SQL Server sqlcluster.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eWatch-DbaDbLogin -SqlInstance sqlserver -SqlCms SqlCms1 -SqlCredential $cred\r\n\r\nA list of servers is generated using database instance names within the SQL2014Clusters group on the Central Management \r\nServer SqlCms1. Using this list, the script enumerates all the processes and gathers login information and saves it to \r\nthe table Dblogins in the DatabaseLogins database on sqlserver.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Watch-DbaUpdate",
        "Description":  "Just for fun - checks the PowerShell Gallery every 1 hour for updates to dbatools. Notifies once max per release.\n\nAnyone know how to make it clickable so that it opens an URL?",
        "Tags":  [
                     "JustForFun",
                     "Module"
                 ],
        "Synopsis":  "Just for fun - checks the PowerShell Gallery every 1 hour for updates to dbatools. Notifies once per release.",
        "Name":  "Watch-DbaUpdate",
        "Links":  "https://dbatools.io/Watch-DbaUpdate",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eWatch-DbaUpdate\r\n\r\nWatches the gallery for updates to dbatools.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Watch-DbaXESession",
        "Description":  "Watch live XEvent Data as it happens. This command runs until you stop the session, kill the PowerShell session, or Ctrl-C.\n\nThanks to Dave Mason (@BeginTry) for some straightforward code samples https://itsalljustelectrons.blogspot.be/2017/01/SQL-Server-Extended-Event-Handling-Via-Powershell.html",
        "Tags":  [
                     "ExtendedEvent",
                     "XE",
                     "XEvent"
                 ],
        "Synopsis":  "Watch live XEvent Data as it happens",
        "Name":  "Watch-DbaXESession",
        "Links":  "https://dbatools.io/Watch-DbaXESession",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003eWatch-DbaXESession -SqlInstance sql2017 -Session system_health\r\n\r\nShows events for the system_health session as it happens.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003eWatch-DbaXESession -SqlInstance sql2017 -Session system_health | Export-Csv -NoTypeInformation -Path \r\nC:\\temp\\system_health.csv\r\n\r\nExports live events to CSV. Ctrl-C may not not cancel out of it - fastest way is to stop the session.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003eGet-DbaXESession -SqlInstance sql2017 -Session system_health | Start-DbaXESession | Watch-DbaXESession | \r\nExport-Csv -NoTypeInformation -Path C:\\temp\\system_health.csv\r\n\r\nExports live events to CSV. Ctrl-C may not not cancel out of this. The fastest way to do so is to stop the session.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    },
    {
        "CommandName":  "Write-DbaDataTable",
        "Description":  "Writes a .NET DataTable to a SQL Server table using SQL Bulk Copy.",
        "Tags":  [
                     "DataTable",
                     "Insert"
                 ],
        "Synopsis":  "Writes data to a SQL Server Table.",
        "Name":  "Write-DbaDataTable",
        "Links":  "https://dbatools.io/Write-DbaDataTable",
        "Examples":  "\r\n-------------------------- EXAMPLE 1 --------------------------\r\n\r\nPS C:\\\u003e$DataTable = Import-Csv C:\\temp\\customers.csv | Out-DbaDataTable\r\n\r\nWrite-DbaDataTable -SqlInstance sql2014 -InputObject $DataTable -Table mydb.dbo.customers\r\n\r\nPerforms a bulk insert of all the data in customers.csv into database mydb, schema dbo, table customers. A progress bar \r\nwill be shown as rows are inserted. If the destination table does not exist, the import will be halted.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 2 --------------------------\r\n\r\nPS C:\\\u003e$DataTable = Import-Csv C:\\temp\\customers.csv | Out-DbaDataTable\r\n\r\n$DataTable | Write-DbaDataTable -SqlInstance sql2014 -Table mydb.dbo.customers\r\n\r\nPerforms a row by row insert of the data in customers.csv. This is significantly slower than a bulk insert and will not \r\nshow a progress bar.\r\n\r\nThis method is not recommended. Use -InputObject instead.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 3 --------------------------\r\n\r\nPS C:\\\u003e$DataTable = Import-Csv C:\\temp\\customers.csv | Out-DbaDataTable\r\n\r\nWrite-DbaDataTable -SqlInstance sql2014 -InputObject $DataTable -Table mydb.dbo.customers -AutoCreateTable\r\n\r\nPerforms a bulk insert of all the data in customers.csv. If mydb.dbo.customers does not exist, it will be created with \r\ninefficient but forgiving DataTypes.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 4 --------------------------\r\n\r\nPS C:\\\u003e$DataTable = Import-Csv C:\\temp\\customers.csv | Out-DbaDataTable\r\n\r\nWrite-DbaDataTable -SqlInstance sql2014 -InputObject $DataTable -Table mydb.dbo.customers -Truncate\r\n\r\nPerforms a bulk insert of all the data in customers.csv. Prior to importing into mydb.dbo.customers, the user is \r\ninformed that the table will be truncated and asks for confirmation. The user is prompted again to perform the import.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 5 --------------------------\r\n\r\nPS C:\\\u003e$DataTable = Import-Csv C:\\temp\\customers.csv | Out-DbaDataTable\r\n\r\nWrite-DbaDataTable -SqlInstance sql2014 -InputObject $DataTable -Database mydb -Table customers -KeepNulls\r\n\r\nPerforms a bulk insert of all the data in customers.csv into mydb.dbo.customers. Because Schema was not specified, dbo \r\nwas used. NULL values in the destination table will be preserved.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 6 --------------------------\r\n\r\nPS C:\\\u003e$passwd = ConvertTo-SecureString \"P@ssw0rd\" -AsPlainText -Force\r\n\r\n$AzureCredential = Mew-Object System.Management.Automation.PSCredential(\"AzureAccount\"),$passwd)\r\n$DataTable = Import-Csv C:\\temp\\customers.csv | Out-DbaDataTable\r\nWrite-DbaDataTable -SqlInstance AzureDB.database.windows.net -InputObject $DataTable -Database mydb -Table customers \r\n-KeepNulls -Credential $AzureCredential -BulkCopyTimeOut 300\r\n\r\nThis performs the same operation as the previous example, but against a SQL Azure Database instance using the required \r\ncredentials.\r\n\r\n\r\n\r\n\r\n-------------------------- EXAMPLE 7 --------------------------\r\n\r\nPS C:\\\u003e$process = Get-Process | Out-DbaDataTable\r\n\r\nWrite-DbaDataTable -InputObject $process -SqlInstance sql2014 -Database mydb -Table myprocesses -AutoCreateTable\r\n\r\nCreates a table based on the Process object with over 60 columns, converted from PowerShell data types to SQL Server \r\ndata types. After the table is created a bulk insert is performed to add process information into the table.\r\n\r\nThis is an example of the type conversion in action. All process properties are converted, including special types like \r\nTimeSpan. Script properties are resolved before the type conversion starts thanks to Out-DbaDataTable.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
    }
]
tools\dbatools\bin\dbatools.dll
md5: 82BF2F6ABB6B91C1D1BED3C61FB4B6A6 | sha1: FB286BA683EABD12FFA50DE2609F5487C9464860 | sha256: 9D1981CE62524E9664601EA97052F81F94E485373994A1224EEC2F8253156613 | sha512: 98588A1F60D0AF93CE83092013FBFDF451892E35B871663989755FD1F723EBA303ABD6E49BE616CF8C94DE5F792943D679EF90D713FCCE8C0582E703069B6F3A
tools\dbatools\bin\dbatools.pdb
 
tools\dbatools\bin\dbatools.xml
<?xml version="1.0"?>
<doc>
    <assembly>
        <name>dbatools</name>
    </assembly>
    <members>
        <member name="T:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand">
            <summary>
            Implements the Write-Message command, performing message handling and loggin
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand.Level">
            <summary>
            This parameter represents the verbosity of the message. The lower the number, the more important it is for a human user to read the message.
            By default, the levels are distributed like this:
            - 1-3 Direct verbose output to the user (using Write-Host)
            - 4-6 Output only visible when requesting extra verbosity (using Write-Verbose)
            - 1-9 Debugging information, written using Write-Debug
            
            In addition, it is possible to select the level "Warning" which moves the message out of the configurable range:
            The user will always be shown this message, unless he silences the entire verbosity.
            
            Possible levels:
            Critical (1), Important / Output / Host (2), Significant (3), VeryVerbose (4), Verbose (5), SomewhatVerbose (6), System (7), Debug (8), InternalComment (9), Warning (666)
            Either one of the strings or its respective number will do as input.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand.Message">
            <summary>
            The message to write/log. The function name and timestamp will automatically be prepended.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand.Tag">
            <summary>
            Tags to add to the message written.
            This allows filtering and grouping by category of message, targeting specific messages.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand.FunctionName">
            <summary>
            The name of the calling function.
            Will be automatically set, but can be overridden when necessary.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand.ModuleName">
            <summary>
            The name of the module, the calling function is part of.
            Will be automatically set, but can be overridden when necessary.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand.File">
            <summary>
            The file in which Write-Message was called.
            Will be automatically set, but can be overridden when necessary.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand.Line">
            <summary>
            The line on which Write-Message was called.
            Will be automatically set, but can be overridden when necessary.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand.ErrorRecord">
            <summary>
            If an error record should be noted with the message, add the full record here.
            Especially designed for use with Warning-mode, it can legally be used in either mode.
            The error will be added to the $Error variable and enqued in the logging/debugging system.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand.Exception">
            <summary>
            Allows specifying an inner exception as input object. This will be passed on to the logging and used for messages.
            When specifying both ErrorRecord AND Exception, Exception wins, but ErrorRecord is still used for record metadata.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand.Once">
            <summary>
            Setting this parameter will cause this function to write the message only once per session.
            The string passed here and the calling function's name are used to create a unique ID, which is then used to register the action in the configuration system.
            Thus will the lockout only be written if called once and not burden the system unduly.
            This lockout will be written as a hidden value, to see it use Get-DbaConfig -Force.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand.OverrideExceptionMessage">
            <summary>
            Disables automatic appending of exception messages.
            Use in cases where you already have a speaking message interpretation and do not need the original message.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand.Target">
            <summary>
            Add the object the message is all about, in order to simplify debugging / troubleshooting.
            For example, when calling this from a function targeting a remote computer, the computername could be specified here, allowing all messages to easily be correlated to the object processed.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand.EnableException">
            <summary>
            This parameters disables user-friendly warnings and enables the throwing of exceptions.
            This is less user friendly, but allows catching exceptions in calling scripts.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand.Breakpoint">
            <summary>
            Enables breakpoints on the current message. By default, setting '-Debug' will NOT cause an interrupt on the current position.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand._timestamp">
            <summary>
            The start time of the cmdlet
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand._silent">
            <summary>
            Whether this cmdlet is run in silent mode
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand._fromStopFunction">
            <summary>
            Whether this cmdlet was called by Stop-Function
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand._callStack">
            <summary>
            The current callstack
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand._stackDepth">
            <summary>
            How many items exist on the callstack
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand._message">
            <summary>
            The message to write
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand._messageSimple">
            <summary>
            The message simplified without timestamps. Used for logging.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand._messageColor">
            <summary>
            The message to write in color
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand._messageDeveloper">
            <summary>
            Non-colored version of developermode
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand._messageDeveloperColor">
            <summary>
            Colored version of developermode
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand._writeHostScript">
            <summary>
            Scriptblock that writes the host messages
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand._Tags">
            <summary>
            List of tags to process
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand._isDebug">
            <summary>
            Whether debug mode is enabled
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand._errorQualifiedMessage">
            <summary>
            The input message with the error content included if desired
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand._MessageSystem">
            <summary>
            The final message to use for internal logging
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand._MessageStreams">
            <summary>
            The final message to use for writing to streams, such as verbose or warning
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand._MessageHost">
            <summary>
            The final message to use for host messages (write using Write-HostColor)
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand._BreadCrumbsString">
            <summary>
            Provide breadcrumb queue of the callstack
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand._BreadCrumbsStringColored">
            <summary>
            Provide a breadcrumb queue of the callstack in color tags
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand.BeginProcessing">
            <summary>
            Processes the begin phase of the cmdlet
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand.ProcessRecord">
            <summary>
            Processes the process phase of the cmdlet
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand.ResolveTarget(System.Object)">
            <summary>
            Processes the target transform rules on an input object
            </summary>
            <param name="Item">The item to transform</param>
            <returns>The transformed object</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand.ResolveException(System.Exception)">
            <summary>
            Processes the specified exception specified
            </summary>
            <param name="Item">The exception to process</param>
            <returns>The transformed exception</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand.ResolveLevel(Sqlcollaborative.Dbatools.Message.MessageLevel)">
            <summary>
            Processs the input level and apply policy and rules
            </summary>
            <param name="Level">The original level of the message</param>
            <returns>The processed level</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand.GetMessage">
            <summary>
            Builds the message item for display of Verbose, Warning and Debug streams
            </summary>
            <returns>The message to return</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand.GetMessageSimple">
            <summary>
            Builds the base message for internal system use.
            </summary>
            <returns>The message to return</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand.GetMessageColor">
            <summary>
            Builds the message item if needed and returns it
            </summary>
            <returns>The message to return</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand.GetMessageDeveloper">
            <summary>
            Non-host output in developermode
            </summary>
            <returns>The string to write on messages that don't go straight to Write-HostColor</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Commands.WriteMessageCommand.GetMessageDeveloperColor">
            <summary>
            Host output in developermode
            </summary>
            <returns>The string to write on messages that go straight to Write-HostColor</returns>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Computer.DiskSpace">
            <summary>
            Data Container for the output of Get-DbaDiskSpace
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Computer.DiskSpace.ComputerName">
            <summary>
            The computer that was scanned
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Computer.DiskSpace.Name">
            <summary>
            Name of the disk
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Computer.DiskSpace.Label">
            <summary>
            Label of the disk
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Computer.DiskSpace.Capacity">
            <summary>
            What's the total capacity of the disk?
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Computer.DiskSpace.Free">
            <summary>
            How much is still free?
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Computer.DiskSpace.PercentFree">
            <summary>
            How much is still free
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Computer.DiskSpace.BlockSize">
            <summary>
            What blocksize is the object set to
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Computer.DiskSpace.FileSystem">
            <summary>
            What filesystem is installed on the system
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Computer.DiskSpace.Type">
            <summary>
            What kind of drive is it?
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Computer.DiskSpace.IsSqlDisk">
            <summary>
            Whether the drive is a sql disk. Nullable, because it is an optional property and may not always be included, thus a third state is necessary.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Computer.DiskSpace.Server">
            <summary>
            The computer that was scanned. Legacy-Name
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Computer.DiskSpace.DriveType">
            <summary>
            The type of drive this is in the legacy string notation
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Computer.DiskSpace.SizeInBytes">
            <summary>
            The total capacity in Bytes
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Computer.DiskSpace.FreeInBytes">
            <summary>
            The free space in Bytes
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Computer.DiskSpace.SizeInKB">
            <summary>
            The total capacity in KB
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Computer.DiskSpace.FreeInKB">
            <summary>
            The free space in KB
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Computer.DiskSpace.SizeInMB">
            <summary>
            The total capacity in MB
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Computer.DiskSpace.FreeInMB">
            <summary>
            The free space in MB
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Computer.DiskSpace.SizeInGB">
            <summary>
            The total capacity in GB
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Computer.DiskSpace.FreeInGB">
            <summary>
            The free space in GB
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Computer.DiskSpace.SizeInTB">
            <summary>
            The total capacity in TB
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Computer.DiskSpace.FreeInTB">
            <summary>
            The free space in TB
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Computer.DiskSpace.SizeInPB">
            <summary>
            The total capacity in PB
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Computer.DiskSpace.FreeInPB">
            <summary>
            The free space in PB
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Computer.DriveType">
            <summary>
            What kind of drive are you?
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Computer.DriveType.Unknown">
            <summary>
            The drive type is not actually known
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Computer.DriveType.NoRootDirectory">
            <summary>
            The drive has no root directory
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Computer.DriveType.RemovableDisk">
            <summary>
            The drive is a removable disk
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Computer.DriveType.LocalDisk">
            <summary>
            The drive is a local disk
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Computer.DriveType.NetworkDrive">
            <summary>
            The drive is a network drive
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Computer.DriveType.CompactDisk">
            <summary>
            The drive is a compact disk
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Computer.DriveType.RAMDisk">
            <summary>
            The drive is a RAM disk
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Computer.PageFileSetting">
            <summary>
            Data container, listing pagefile settings.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Computer.PageFileSetting.ComputerName">
            <summary>
            The name of the computer
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Computer.PageFileSetting.AutoPageFile">
            <summary>
            Whether Automatic PageFile management is enabled
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Computer.PageFileSetting.FileName">
            <summary>
            The pagefile name
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Computer.PageFileSetting.Status">
            <summary>
            The pagefile status
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Computer.PageFileSetting.SystemManaged">
            <summary>
            Whether the pagefile is system managed
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Computer.PageFileSetting.LastModified">
            <summary>
            When were the settings last changed
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Computer.PageFileSetting.LastAccessed">
            <summary>
            When were the settings last accessed
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Computer.PageFileSetting.AllocatedBaseSize">
            <summary>
            The base allocated pagefile size in MB
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Computer.PageFileSetting.InitialSize">
            <summary>
            The initial pagefile size in MB
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Computer.PageFileSetting.MaximumSize">
            <summary>
            The maximum pagefile size in MB
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Computer.PageFileSetting.PeakUsage">
            <summary>
            The maximum percent of the pagefile limit that has been used
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Computer.PageFileSetting.CurrentUsage">
            <summary>
            The currently used percentage of the pagefile limit that is in use.
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Configuration.ConfigScope">
            <summary>
            The location where a setting was applied
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Configuration.ConfigScope.UserDefault">
            <summary>
            The configuration is set as default value for the user
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Configuration.ConfigScope.UserMandatory">
            <summary>
            The configuration is enforced for the user
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Configuration.ConfigScope.SystemDefault">
            <summary>
            The configuration is set as default value for all users on the system
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Configuration.ConfigScope.SystemMandatory">
            <summary>
            The configuration is enforced for all users on the system.
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Configuration.ConfigurationHost">
            <summary>
            Host class providing static configuration settings that are constant across all runspaces within the process.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Configuration.ConfigurationHost.Configurations">
            <summary>
            Hashtable containing all the configuration entries
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Configuration.ConfigurationHost.Validation">
            <summary>
            Hashtable containing all the registered validations
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Configuration.ConfigurationHost.ImportFromRegistryDone">
            <summary>
            Whether the import from registry has been completed. Prevents multiple imports and overwrites when importing the module multiple times.
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Configuration.Config">
            <summary>
            Configuration Manager as well as individual configuration object.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Configuration.Config.Name">
            <summary>
            The Name of the setting
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Configuration.Config.FullName">
            <summary>
            The full name of the configuration entry, comprised of both Module and Name.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Configuration.Config.Module">
            <summary>
            The module of the setting. Helps being able to group configurations.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Configuration.Config.Description">
            <summary>
            A description of the specific setting
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Configuration.Config.Type">
            <summary>
            The data type of the value stored in the configuration element.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Configuration.Config.Value">
            <summary>
            The value stored in the configuration element
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Configuration.Config.Handler">
            <summary>
            The handler script that is run whenever the configuration value is set.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Configuration.Config.Validation">
            <summary>
            Validates the user input
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Configuration.Config.Hidden">
            <summary>
            Setting this to true will cause the element to not be discovered unless using the '-Force' parameter on "Get-DbaConfig"
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Configuration.Config.Initialized">
            <summary>
            Whether the setting has been initialized. This handles module imports and avoids modules overwriting settings when imported in multiple runspaces.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Configuration.Config.PolicySet">
            <summary>
            Whether this setting was set by policy
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Configuration.Config.PolicyEnforced">
            <summary>
            Whether this setting was set by policy and forbids deletion.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Configuration.Config.RegistryData">
            <summary>
            The finalized value to put into the registry value when using policy to set this setting.
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Connection.PSSessionContainer">
            <summary>
            The container that lists all sessions for a given runspace
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.PSSessionContainer.Runspace">
            <summary>
            The runspace that owns the sessions
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Connection.PSSessionContainer.CountExpired">
            <summary>
            The count of expired sessions registered
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.PSSessionContainer.Sessions">
            <summary>
            List of sessions and their associated computer names
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.PSSessionContainer.ConnectionTimestamps">
            <summary>
            List of timestamps, when the last command was run against them
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Connection.PSSessionContainer.#ctor(System.Guid)">
            <summary>
            Creates a list of sessions the current runspace is connected to.
            </summary>
            <param name="Runspace">The Guid of the runspace that is the owner of the registered sessions</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Connection.PSSessionContainer.Get(System.String)">
            <summary>
            Returns the requested session.
            </summary>
            <param name="ComputerName">The name of the host whose connection to retrieve</param>
            <returns>The established connection to the host, null if none exists.</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Connection.PSSessionContainer.Set(System.String,System.Management.Automation.Runspaces.PSSession)">
            <summary>
            Sets a session and writes its timestamp to the cache
            </summary>
            <param name="ComputerName">The hostname it connects to.</param>
            <param name="Session">The session that is being registered.</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Connection.PSSessionContainer.GetExpiredNames">
            <summary>
            Returns the name of hostnames with expired sessions
            </summary>
            <returns>THe hostnames whose session has expired</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Connection.PSSessionContainer.PurgeExpiredSession">
            <summary>
            Removes an expired session from the cache, an returns it, so it can be properly closed.
            </summary>
            <returns>Returns a session to disconnect</returns>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Connection.ConnectionHost">
            <summary>
            Provides static tools for managing connections
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ConnectionHost.Connections">
            <summary>
            List of all registered connections.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ConnectionHost.BadConnectionTimeout">
            <summary>
            The time interval that must pass, before a connection using a known to not work connection protocol is reattempted
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ConnectionHost.DisableCache">
            <summary>
            Globally disables all caching done by the Computer Management functions.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ConnectionHost.DisableBadCredentialCache">
            <summary>
            Disables the caching of bad credentials. dbatools caches bad logon credentials for wmi/cim and will not reuse them.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ConnectionHost.DisableCredentialAutoRegister">
            <summary>
            Disables the automatic registration of working credentials. dbatools will caches the last working credential when connecting using wmi/cim and will use those rather than using known bad credentials
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ConnectionHost.OverrideExplicitCredential">
            <summary>
            Enabling this will force the use of the last credentials known to work, rather than even trying explicit credentials.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ConnectionHost.EnableCredentialFailover">
            <summary>
            Enables automatic failover to working credentials, when passed credentials either are known, or turn out to not work.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ConnectionHost.DisableCimPersistence">
            <summary>
            Globally disables the persistence of Cim sessions used to connect to a target system.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ConnectionHost.DisableConnectionCimRM">
            <summary>
            Whether the CM connection using Cim over WinRM is disabled globally
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ConnectionHost.DisableConnectionCimDCOM">
            <summary>
            Whether the CM connection using Cim over DCOM is disabled globally
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ConnectionHost.DisableConnectionWMI">
            <summary>
            Whether the CM connection using WMI is disabled globally
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ConnectionHost.DisableConnectionPowerShellRemoting">
            <summary>
            Whether the CM connection using PowerShell Remoting is disabled globally
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ConnectionHost.SqlConnectionTimeout">
            <summary>
            The number of seconds before a sql connection attempt times out
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ConnectionHost.PSSessions">
            <summary>
            List of all session containers used to maintain a cache
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Connection.ConnectionHost.PSSessionGet(System.Guid,System.String)">
            <summary>
            Returns a registered session for a given computer on a given runspace. Returns null if nothing is registered.
            </summary>
            <param name="Runspace">The host runspace that opened the session</param>
            <param name="ComputerName">The computer connected to</param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Connection.ConnectionHost.PSSessionSet(System.Guid,System.String,System.Management.Automation.Runspaces.PSSession)">
            <summary>
            Registeres a remote session under the owning runspace in its respective computer name
            </summary>
            <param name="Runspace">The runspace that owns the session</param>
            <param name="ComputerName">The computer the session connects to</param>
            <param name="Session">The session object</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Connection.ConnectionHost.PSSessionPurgeExpired">
            <summary>
            Searches the cache for an expired remoting session and purges it. After purging it from the list, it still needs to be closed!
            </summary>
            <returns>The session purged that then needs to be closed</returns>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Connection.ConnectionHost.PSSessionCountExpired">
            <summary>
            The number of expired sessions 
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ConnectionHost.PSSessionTimeout">
            <summary>
            The time until established connections will be considered expired (if available)
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ConnectionHost.PSSessionCacheEnabled">
            <summary>
            Whether sessions should be cached at all
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Connection.ManagementConnection">
            <summary>
            Contains management connection information for a windows server
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Connection.ManagementConnection.ComputerName">
            <summary>
            The computer to connect to
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Connection.ManagementConnection.DisableBadCredentialCache">
            <summary>
            Locally disables the caching of bad credentials
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Connection.ManagementConnection.DisableCredentialAutoRegister">
            <summary>
            Locally disables the caching of working credentials
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Connection.ManagementConnection.OverrideExplicitCredential">
            <summary>
            Locally overrides explicit credentials with working ones that were cached
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Connection.ManagementConnection.EnableCredentialFailover">
            <summary>
            Locally enables automatic failover to working credentials, when passed credentials either are known, or turn out to not work.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Connection.ManagementConnection.DisableCimPersistence">
            <summary>
            Locally disables the persistence of Cim sessions used to connect to a target system.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Connection.ManagementConnection.DisabledConnectionTypes">
            <summary>
            Connectiontypes that will never be used
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Connection.ManagementConnection.RestoreDefaultConfiguration">
            <summary>
            Restores all deviations from public policy back to default
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ManagementConnection.OverrideConnectionPolicy">
            <summary>
            Whether this connection adhers to the global connection lockdowns or not
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Connection.ManagementConnection.CimRM">
            <summary>
            Did the last connection attempt using CimRM work?
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ManagementConnection.LastCimRM">
            <summary>
            When was the last connection attempt using CimRM?
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Connection.ManagementConnection.CimDCOM">
            <summary>
            Did the last connection attempt using CimDCOM work?
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ManagementConnection.LastCimDCOM">
            <summary>
            When was the last connection attempt using CimRM?
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Connection.ManagementConnection.Wmi">
            <summary>
            Did the last connection attempt using Wmi work?
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ManagementConnection.LastWmi">
            <summary>
            When was the last connection attempt using CimRM?
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Connection.ManagementConnection.PowerShellRemoting">
            <summary>
            Did the last connection attempt using PowerShellRemoting work?
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ManagementConnection.LastPowerShellRemoting">
            <summary>
            When was the last connection attempt using CimRM?
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Connection.ManagementConnection.ReportSuccess(Sqlcollaborative.Dbatools.Connection.ManagementConnectionType)">
            <summary>
            Report the successful connection against the computer of this connection
            </summary>
            <param name="Type">What connection type succeeded?</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Connection.ManagementConnection.ReportFailure(Sqlcollaborative.Dbatools.Connection.ManagementConnectionType)">
            <summary>
            Report the failure of connecting to the target computer
            </summary>
            <param name="Type">What connection type failed?</param>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ManagementConnection.Credentials">
            <summary>
            Any registered credentials to use on the connection.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ManagementConnection.WindowsCredentialsAreBad">
            <summary>
            Whether the default windows credentials are known to not work against the target.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ManagementConnection.UseWindowsCredentials">
            <summary>
            Whether windows credentials are known to be good. Do not build conditions on them being false, just on true.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ManagementConnection.KnownBadCredentials">
            <summary>
            Credentials known to not work. They will not be used when specified.
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Connection.ManagementConnection.AddBadCredential(System.Management.Automation.PSCredential)">
            <summary>
            Adds a credentials object to the list of credentials known to not work.
            </summary>
            <param name="Credential">The bad credential that must be punished</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Connection.ManagementConnection.AddGoodCredential(System.Management.Automation.PSCredential)">
            <summary>
            Reports a credentials object as being legit.
            </summary>
            <param name="Credential">The functioning credential that we may want to use again</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Connection.ManagementConnection.GetCredential(System.Management.Automation.PSCredential)">
            <summary>
            Calculates, which credentials to use. Will consider input, compare it with know not-working credentials or use the configured working credentials for that.
            </summary>
            <param name="Credential">Any credential object a user may have explicitly specified.</param>
            <returns>The Credentials to use</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Connection.ManagementConnection.IsBadCredential(System.Management.Automation.PSCredential)">
            <summary>
            Tests whether the input credential is on the list known, bad credentials
            </summary>
            <param name="Credential">The credential to test</param>
            <returns>True if the credential is known to not work, False if it is not yet known to not work</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Connection.ManagementConnection.RemoveBadCredential(System.Management.Automation.PSCredential)">
            <summary>
            Removes an item from the list of known bad credentials
            </summary>
            <param name="Credential">The credential to remove</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Connection.ManagementConnection.GetConnectionType(Sqlcollaborative.Dbatools.Connection.ManagementConnectionType,System.Boolean)">
            <summary>
            Returns the next connection type to try.
            </summary>
            <param name="ExcludedTypes">Exclude any type already tried and failed</param>
            <param name="Force">Overrides the timeout on bad connections</param>
            <returns>The next type to try.</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Connection.ManagementConnection.GetConnectionTypesTimed(System.DateTime)">
            <summary>
            Returns a list of all available connection types whose inherent timeout has expired.
            </summary>
            <param name="Timestamp">All last connection failures older than this point in time are considered to be expired</param>
            <returns>A list of all valid connection types</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Connection.ManagementConnection.GetConnectionTypesTimed(System.TimeSpan)">
            <summary>
            Returns a list of all available connection types whose inherent timeout has expired.
            </summary>
            <param name="Timespan">All last connection failures older than this far back into the past are considered to be expired</param>
            <returns>A list of all valid connection types</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Connection.ManagementConnection.#ctor">
            <summary>
            Creates a new, empty connection object. Necessary for serialization.
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Connection.ManagementConnection.#ctor(System.String)">
            <summary>
            Creates a new default connection object, containing only its computer's name and default results.
            </summary>
            <param name="ComputerName">The computer targeted. Will be forced to lowercase.</param>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Connection.ManagementConnection.CimWinRMOptions">
            <summary>
            The options ot use when establishing a CIM Session
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Connection.ManagementConnection.GetDefaultCimWsmanOptions">
            <summary>
            Returns the default wsman options object
            </summary>
            <returns>Something very default-y</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Connection.ManagementConnection.GetCimRMInstance(System.Management.Automation.PSCredential,System.String,System.String)">
            <summary>
            Get all cim instances of the appropriate class using WinRM
            </summary>
            <param name="Credential">The credentiuls to use for the connection.</param>
            <param name="Class">The class to query.</param>
            <param name="Namespace">The namespace to look in (defaults to root\cimv2).</param>
            <returns>Hopefully a mountainload of CimInstances</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Connection.ManagementConnection.QueryCimRMInstance(System.Management.Automation.PSCredential,System.String,System.String,System.String)">
            <summary>
            Get all cim instances matching the query using WinRM
            </summary>
            <param name="Credential">The credentiuls to use for the connection.</param>
            <param name="Query">The query to use requesting information.</param>
            <param name="Dialect">Defaults to WQL.</param>
            <param name="Namespace">The namespace to look in (defaults to root\cimv2).</param>
            <returns></returns>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Connection.ManagementConnection.CimDComOptions">
            <summary>
            The options ot use when establishing a CIM Session
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Connection.ManagementConnection.GetDefaultCimDcomOptions">
            <summary>
            Returns the default DCom options object
            </summary>
            <returns>Something very default-y</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Connection.ManagementConnection.GetCimDComInstance(System.Management.Automation.PSCredential,System.String,System.String)">
            <summary>
            Get all cim instances of the appropriate class using DCOM
            </summary>
            <param name="Credential">The credentiuls to use for the connection.</param>
            <param name="Class">The class to query</param>
            <param name="Namespace">The namespace to look in (defaults to root\cimv2)</param>
            <returns>Hopefully a mountainload of CimInstances</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Connection.ManagementConnection.QueryCimDCOMInstance(System.Management.Automation.PSCredential,System.String,System.String,System.String)">
            <summary>
            Get all cim instances matching the query using DCOM
            </summary>
            <param name="Credential">The credentiuls to use for the connection.</param>
            <param name="Query">The query to use requesting information.</param>
            <param name="Dialect">Defaults to WQL.</param>
            <param name="Namespace">The namespace to look in (defaults to root\cimv2).</param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Connection.ManagementConnection.ToString">
            <summary>
            Simple string representation
            </summary>
            <returns>Returns the computerName it is connection for</returns>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Connection.ManagementConnectionProtocolState">
            <summary>
            The various types of state a connection-protocol may have
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ManagementConnectionProtocolState.Unknown">
            <summary>
            The default initial state, before any tests are performed
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ManagementConnectionProtocolState.Success">
            <summary>
            A successful connection was last established
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ManagementConnectionProtocolState.Error">
            <summary>
            Connecting using the relevant protocol failed last it was tried
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ManagementConnectionProtocolState.Disabled">
            <summary>
            The relevant protocol has been disabled and should not be used
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Connection.ManagementConnectionType">
            <summary>
            The various ways to connect to a windows server fopr management purposes.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ManagementConnectionType.None">
            <summary>
            No Connection-Type
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ManagementConnectionType.CimRM">
            <summary>
            Cim over a WinRM connection
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ManagementConnectionType.CimDCOM">
            <summary>
            Cim over a DCOM connection
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ManagementConnectionType.Wmi">
            <summary>
            WMI Connection
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.ManagementConnectionType.PowerShellRemoting">
            <summary>
            Connecting with PowerShell remoting and performing WMI queries locally
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Connection.SqlConnectionProtocol">
            <summary>
            The protocol to connect over via SMO
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.SqlConnectionProtocol.Any">
            <summary>
            Connect using any protocol available
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.SqlConnectionProtocol.TCP">
            <summary>
            Connect using TCP/IP
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Connection.SqlConnectionProtocol.NP">
            <summary>
            Connect using named pipes or shared memory
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Database.BackupHistory">
            <summary>
            Object containing the information about the history of mankind ... or a database backup. WHo knows.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.BackupHistory.ComputerName">
            <summary>
            The name of the computer running MSSQL Server
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.BackupHistory.InstanceName">
            <summary>
            The Instance that was queried
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.BackupHistory.SqlInstance">
            <summary>
            The full Instance name as seen from outside
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.BackupHistory.Database">
            <summary>
            The Database that was backed up
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.BackupHistory.UserName">
            <summary>
            The user that is running the backup
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.BackupHistory.Start">
            <summary>
            When was the backup started
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.BackupHistory.End">
            <summary>
            When did the backup end
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.BackupHistory.Duration">
            <summary>
            What was the longest duration among the backups
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.BackupHistory.Path">
            <summary>
            Where is the backup stored
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.BackupHistory.TotalSize">
            <summary>
            What is the total size of the backup
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.BackupHistory.CompressedBackupSize">
            <summary>
            What is the total compressesed size of the backup
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.BackupHistory.CompressionRatio">
            <summary>
            What is the ratio of total size to compressed size of the backup
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.BackupHistory.Type">
            <summary>
            The kind of backup this was
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.BackupHistory.BackupSetId">
            <summary>
            The ID for the Backup job
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.BackupHistory.DeviceType">
            <summary>
            What kind of backup-device was the backup stored to
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.BackupHistory.Software">
            <summary>
            What is the name of the backup software?
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.BackupHistory.FullName">
            <summary>
            The full name of the backup
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.BackupHistory.FileList">
            <summary>
            The files that are part of this backup
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.BackupHistory.Position">
            <summary>
            The position of the backup
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.BackupHistory.FirstLsn">
            <summary>
            The first Log Sequence Number
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.BackupHistory.DatabaseBackupLsn">
            <summary>
            The Log Squence Number that marks the beginning of the backup
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.BackupHistory.CheckpointLsn">
            <summary>
            The checkpoint's Log Sequence Number
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.BackupHistory.LastLsn">
            <summary>
            The last Log Sequence Number
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.BackupHistory.SoftwareVersionMajor">
            <summary>
            The primary version number of the Sql Server
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.BackupHistory.IsCopyOnly">
            <summary>
            Was the backup performed with the CopyOnlyOption
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.BackupHistory.LastRecoveryForkGUID">
            <summary>
            Recovery Fork backup was takeon
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.BackupHistory.RecoveryModel">
            <summary>
            Recovery Model of the database when backup was taken
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Database.Dependency">
            <summary>
            Class containing all dependency information over a database object
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.Dependency.ComputerName">
            <summary>
            The name of the SQL server from whence the query came
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.Dependency.ServiceName">
            <summary>
            Name of the service running the database containing the dependency
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.Dependency.SqlInstance">
            <summary>
            The Instance the database containing the dependency is running in.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.Dependency.Dependent">
            <summary>
            The name of the dependent
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.Dependency.Type">
            <summary>
            The kind of object the dependent is
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.Dependency.Owner">
            <summary>
            The owner of the dependent (usually the Database)
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.Dependency.IsSchemaBound">
            <summary>
            Whether the dependency is Schemabound. If it is, then the creation statement order is of utmost importance.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.Dependency.Parent">
            <summary>
            The immediate parent of the dependent. Useful in multi-tier dependencies.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.Dependency.ParentType">
            <summary>
            The type of object the immediate parent is.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.Dependency.Script">
            <summary>
            The script used to create the object.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.Dependency.Tier">
            <summary>
            The tier in the dependency hierarchy tree. Used to determine, which dependency must be applied in which order.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.Dependency.Object">
            <summary>
            The smo object of the dependent.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.Dependency.Urn">
            <summary>
            The Uniform Resource Name of the dependent.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Database.Dependency.OriginalResource">
            <summary>
            The object of the original resource, from which the dependency hierachy has been calculated.
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.dbaSystem.StartTimeEntry">
            <summary>
            Entry containing the information of a step during the import sequence
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.dbaSystem.StartTimeEntry.Action">
            <summary>
            The action that has been taken
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.dbaSystem.StartTimeEntry.Timestamp">
            <summary>
            When was the action taken?
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.dbaSystem.StartTimeEntry.Runspace">
            <summary>
            The runspace the entry was written on
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.dbaSystem.StartTimeEntry.#ctor(System.String,System.DateTime,System.Guid)">
            <summary>
            Creates a new StartTimeEntry
            </summary>
            <param name="Action">The action that has been taken</param>
            <param name="Timestamp">When was the action taken?</param>
            <param name="Runspace">The runspace the entry was written on</param>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.dbaSystem.StartTimeResult">
            <summary>
            The processed result how long a given step took
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.dbaSystem.StartTimeResult.Action">
            <summary>
            What action was taken?
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.dbaSystem.StartTimeResult.Duration">
            <summary>
            How long did things take?
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.dbaSystem.StartTimeResult.Start">
            <summary>
            When did this action start?
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.dbaSystem.StartTimeResult.End">
            <summary>
            When did this action end?
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.dbaSystem.StartTimeResult.#ctor(System.String,System.DateTime,System.DateTime)">
            <summary>
            Creates a new StartTimeResult with all values preconfigured
            </summary>
            <param name="Action">The action that was taken</param>
            <param name="Start">When did the action start?</param>
            <param name="End">When did the action end?</param>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.dbaSystem.SystemHost">
            <summary>
            Provides system-wide static resources regarding the dbatools system runtime in general
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.dbaSystem.SystemHost.UnattendedMode">
            <summary>
            When this is set to true, functions must assume dbatools is in unattended mode. May not ask for user input of any kind.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.dbaSystem.SystemHost.ModuleBase">
            <summary>
            Path where the module was located when imported
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.dbaSystem.SystemHost.ModuleImported">
            <summary>
            Flag whether the module has ever been imported in the current process. If that is true, several things (such as importing libraries) is no longer necessary and will be skipped on import.
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.dbaSystem.DbaErrorRecord">
            <summary>
            An error record written by dbatools
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.dbaSystem.DbaErrorRecord.CategoryInfo">
            <summary>
            The category of the error
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.dbaSystem.DbaErrorRecord.ErrorDetails">
            <summary>
            The details on the error
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.dbaSystem.DbaErrorRecord.Exception">
            <summary>
            The actual exception thrown
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.dbaSystem.DbaErrorRecord.FullyQualifiedErrorId">
            <summary>
            The specific error identity, used to identify the target
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.dbaSystem.DbaErrorRecord.InvocationInfo">
            <summary>
            The details of how this was called.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.dbaSystem.DbaErrorRecord.ScriptStackTrace">
            <summary>
            The script's stacktrace
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.dbaSystem.DbaErrorRecord.TargetObject">
            <summary>
            The object being processed
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.dbaSystem.DbaErrorRecord.FunctionName">
            <summary>
            The name of the function throwing the error
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.dbaSystem.DbaErrorRecord.Timestamp">
            <summary>
            When was the error thrown
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.dbaSystem.DbaErrorRecord.Message">
            <summary>
            The message that was written in a userfriendly manner
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.dbaSystem.DbaErrorRecord.Runspace">
            <summary>
            The runspace the error occured on.
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.dbaSystem.DbaErrorRecord.#ctor">
            <summary>
            Create an empty record
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.dbaSystem.DbaErrorRecord.#ctor(System.Management.Automation.ErrorRecord,System.String,System.DateTime,System.String)">
            <summary>
            Create a filled out error record
            </summary>
            <param name="Record">The original error record</param>
            <param name="FunctionName">The function that wrote the error</param>
            <param name="Timestamp">When was the error generated</param>
            <param name="Message">What message was passed when writing the error</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.dbaSystem.DbaErrorRecord.#ctor(System.Management.Automation.ErrorRecord,System.String,System.DateTime,System.String,System.Guid)">
            <summary>
            Create a filled out error record
            </summary>
            <param name="Record">The original error record</param>
            <param name="FunctionName">The function that wrote the error</param>
            <param name="Timestamp">When was the error generated</param>
            <param name="Message">What message was passed when writing the error</param>
            <param name="Runspace">The ID of the runspace writing the error. Used to separate output between different runspaces in the same process.</param>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.dbaSystem.DebugHost">
            <summary>
            Hosts static debugging values and methods
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.dbaSystem.DebugHost.ImportTimeEntries">
            <summary>
            Lists the duration for the last import of dbatools.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.dbaSystem.DebugHost.ImportTime">
            <summary>
            Returns the calculated time each phase took during the last import of dbatool.
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Discovery.DbaBrowserReply">
            <summary>
            The reply the browser service gave
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Discovery.DbaBrowserReply.MachineName">
            <summary>
            The machine name of the computer
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Discovery.DbaBrowserReply.ComputerName">
            <summary>
            the computername of the computer
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Discovery.DbaBrowserReply.SqlInstance">
            <summary>
            The instance running on the computer
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Discovery.DbaBrowserReply.InstanceName">
            <summary>
            The name of the instance, running on the computer
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Discovery.DbaBrowserReply.TCPPort">
            <summary>
            The port number the instance is running under
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Discovery.DbaBrowserReply.Version">
            <summary>
            The version of the SQL Server
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Discovery.DbaBrowserReply.IsClustered">
            <summary>
            Whether the instance is part of a cluster or no.
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Discovery.DbaBrowserReply.ToString">
            <summary>
            Override in order to make it look neater in PowerShell
            </summary>
            <returns></returns>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Discovery.DbaInstanceAvailability">
            <summary>
            Indiciator for whether an instance is known to be available or not
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Discovery.DbaInstanceAvailability.Unknown">
            <summary>
            It is not known, whether the instance is available or not
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Discovery.DbaInstanceAvailability.Available">
            <summary>
            The instance is known to be available
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Discovery.DbaInstanceAvailability.Unavailable">
            <summary>
            The instance is known to be not available
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Discovery.DbaInstanceConfidenceLevel">
            <summary>
            How high is our confidence that this is a valid instance?
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Discovery.DbaInstanceConfidenceLevel.None">
            <summary>
            No confidence at all. There is virtually no way for this to be an instance
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Discovery.DbaInstanceConfidenceLevel.Low">
            <summary>
            We have a few indications, but couldn't follow them up
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Discovery.DbaInstanceConfidenceLevel.Medium">
            <summary>
            We're fairly sure this is legit, but can't guarantee it
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Discovery.DbaInstanceConfidenceLevel.High">
            <summary>
            This absolutely is an instance
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Discovery.DbaInstanceReport">
            <summary>
            The report on a discovered instance
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Discovery.DbaInstanceReport.MachineName">
            <summary>
            The computername of the underlying machine. Usually equal to the computername, but may differ in case of clusters
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Discovery.DbaInstanceReport.ComputerName">
            <summary>
            The computername of the target
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Discovery.DbaInstanceReport.InstanceName">
            <summary>
            The name of the instance
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Discovery.DbaInstanceReport.FullName">
            <summary>
            The full server instance name
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Discovery.DbaInstanceReport.SqlInstance">
            <summary>
            The full name usable to connect via SMO
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Discovery.DbaInstanceReport.Port">
            <summary>
            The port number the server listens on
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Discovery.DbaInstanceReport.Timestamp">
            <summary>
            When the scan was concluded
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Discovery.DbaInstanceReport.TcpConnected">
            <summary>
            Was a TCP connect successful?
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Discovery.DbaInstanceReport.SqlConnected">
            <summary>
            Was a connection via SQL successful (even if we got access denied)
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Discovery.DbaInstanceReport.DnsResolution">
            <summary>
            The DNS Resolution object
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Discovery.DbaInstanceReport.Ping">
            <summary>
            The ping resolution object
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Discovery.DbaInstanceReport.BrowseReply">
            <summary>
            The reply received from the browse request
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Discovery.DbaInstanceReport.Services">
            <summary>
            The windows services for the instance
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Discovery.DbaInstanceReport.SystemServices">
            <summary>
            The SQL Server services that do not belong to that instance alone
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Discovery.DbaInstanceReport.SPNs">
            <summary>
            Service Principal Names found
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Discovery.DbaInstanceReport.PortsScanned">
            <summary>
            The ports that have been scanned
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Discovery.DbaInstanceReport.Availability">
            <summary>
            What we know about its availability
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Discovery.DbaInstanceReport.Confidence">
            <summary>
            How confident we are, that this is a real instance
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Discovery.DbaInstanceReport.ScanTypes">
            <summary>
            What we used to scan the instance
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Discovery.DbaInstanceDiscoveryType">
            <summary>
            What discovery mechanisms to use
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Discovery.DbaInstanceDiscoveryType.IPRange">
            <summary>
            We shall sweep the network for instances, by targeting every IP within a range.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Discovery.DbaInstanceDiscoveryType.Domain">
            <summary>
            We shall search for SQL SPNs in active directory
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Discovery.DbaInstanceDiscoveryType.DataSourceEnumeration">
            <summary>
            We shall use the SSMS Data Sizrce Enumeration mechanism and hope for the best
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Discovery.DbaInstanceDiscoveryType.All">
            <summary>
            We shall use all tools in our control to find stuff
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Discovery.DbaInstanceScanType">
            <summary>
            The mechanisms we use to determine, whether a given host contains a legit instance
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Discovery.DbaInstanceScanType.TCPPort">
            <summary>
            Try connecting to specific ports
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Discovery.DbaInstanceScanType.SqlConnect">
            <summary>
            Try to connect to discovered instances (improves confidence)
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Discovery.DbaInstanceScanType.SqlService">
            <summary>
            Check the windows services on the target
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Discovery.DbaInstanceScanType.DNSResolve">
            <summary>
            Try resolving a computername in DNS
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Discovery.DbaInstanceScanType.SPN">
            <summary>
            Scan the SPNs for the targeted computer
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Discovery.DbaInstanceScanType.Browser">
            <summary>
            Try contacting the local browser service and demand answers
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Discovery.DbaInstanceScanType.Ping">
            <summary>
            See whether you can ping the host
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Discovery.DbaInstanceScanType.All">
            <summary>
            Do EVERYTHING
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Discovery.DbaInstanceScanType.Default">
            <summary>
            Do all the things we consider sane defaults
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Discovery.DbaPortReport">
            <summary>
            We tried to connect to a port, how did it go?
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Discovery.DbaPortReport.ComputerName">
            <summary>
            The name of the computer connected to
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Discovery.DbaPortReport.Port">
            <summary>
            The number of the port we tried to connect to.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Discovery.DbaPortReport.IsOpen">
            <summary>
            Whether the port was open
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Discovery.DbaPortReport.#ctor">
            <summary>
            Creates an empty report (serialization uses this)
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Discovery.DbaPortReport.#ctor(System.String,System.Int32,System.Boolean)">
            <summary>
            Creates a filled in report
            </summary>
            <param name="ComputerName">The name of the computer connected to</param>
            <param name="Port">The port we tried to connect to</param>
            <param name="IsOpen">Whether things worked out</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Discovery.DbaPortReport.ToString">
            <summary>
            Displays port connection reports in a user friendly manner
            </summary>
            <returns></returns>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Exceptions.BloodyHellGiveMeSomethingToWorkWithException">
            <summary>
            An exception that is thrown by parameter classes when given empty input
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Exceptions.BloodyHellGiveMeSomethingToWorkWithException.ParameterClass">
            <summary>
            The parameter class that did the throwing
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Exceptions.BloodyHellGiveMeSomethingToWorkWithException.#ctor(System.String,System.Exception)">
            <summary>
            Creates an exception with a message and a nested exception
            </summary>
            <param name="Message">The message to tell</param>
            <param name="Inner">The inner exception to nest</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Exceptions.BloodyHellGiveMeSomethingToWorkWithException.#ctor(System.String,System.String)">
            <summary>
            Creates an exception with a message and a ParameterClass
            </summary>
            <param name="Message">The message to tell</param>
            <param name="ParameterClass">The Parameter Class that threw the exception</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Exceptions.BloodyHellGiveMeSomethingToWorkWithException.#ctor(System.String,System.Exception,System.String)">
            <summary>
            Creates an exception with a message, a nested exception and a ParameterClass
            </summary>
            <param name="Message">The message to tell</param>
            <param name="Inner">The inner exception to nest</param>
            <param name="ParameterClass">The Parameter Class that threw the exception</param>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Maintenance.MaintenanceHost">
            <summary>
            Host class providing access to resources need to perform dbatools maintenance
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Maintenance.MaintenanceHost.Tasks">
            <summary>
            The register of available tasks.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Maintenance.MaintenanceHost.HasDueTasks">
            <summary>
            Whether there are any due tasks
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Maintenance.MaintenanceHost.GetNextTask(System.String[])">
            <summary>
            Returns the next task to perform. Returns null when there are no more tasks to perform
            </summary>
            <param name="Exclusions">List of tasks not to return, even if they are ready to execute again. This avoids one misconfigured task starving all lower priority tasks</param>
            <returns>The next task to perform.</returns>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Maintenance.MaintenancePriority">
            <summary>
            How high the priority of the task. Higher priority tasks take precedence over low priority tasks.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Maintenance.MaintenancePriority.Trivial">
            <summary>
            This task is completely trivial and can be done whenever there is some spare time for it
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Maintenance.MaintenancePriority.Low">
            <summary>
            The task is not very significant, but should be dealt with at some point
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Maintenance.MaintenancePriority.Medium">
            <summary>
            Average priority task
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Maintenance.MaintenancePriority.High">
            <summary>
            An important task that will take precedence over most other tasks
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Maintenance.MaintenancePriority.Critical">
            <summary>
            A task so critical, that it should be considered to move it to synchronous execution instead.
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Maintenance.MaintenanceTask">
            <summary>
            An individual task assigned to the maintenance engine
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Maintenance.MaintenanceTask.Name">
            <summary>
            The name of the task to execute. No duplciates are possible.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Maintenance.MaintenanceTask.Once">
            <summary>
            Whether the task should be done once only
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Maintenance.MaintenanceTask.Interval">
            <summary>
            The interval at which the task should be performed
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Maintenance.MaintenanceTask.Delay">
            <summary>
            If the task need not be performed right away, it can be delayed, in order to prioritize more important initialization tasks
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Maintenance.MaintenanceTask.Registered">
            <summary>
            When was the task first registered. Duplicate registration calls will not increment this value.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Maintenance.MaintenanceTask.LastExecution">
            <summary>
            When was the task last executed.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Maintenance.MaintenanceTask.Priority">
            <summary>
            How important is this task?
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Maintenance.MaintenanceTask.ScriptBlock">
            <summary>
            The task code to execute
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Maintenance.MaintenanceTask.IsDue">
            <summary>
            Whether the task is due and should be executed
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Message.LogHost">
            <summary>
            Provides static information storage for logging related settings, as well as housing the logging queues.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.LogHost.MaxErrorCount">
            <summary>
            The maximum numbers of error records maintained in-memory.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.LogHost.MaxMessageCount">
            <summary>
            The maximum number of messages that can be maintained in the in-memory message queue
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.LogHost.MaxMessagefileBytes">
            <summary>
            The maximum size of a given logfile. When reaching this limit, the file will be abandoned and a new log created. Set to 0 to not limit the size.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.LogHost.MaxMessagefileCount">
            <summary>
            The maximum number of logfiles maintained at a time. Exceeding this number will cause the oldest to be culled. Set to 0 to disable the limit.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.LogHost.MaxErrorFileBytes">
            <summary>
            The maximum size all error files combined may have. When this number is exceeded, the oldest entry is culled.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.LogHost.MaxTotalFolderSize">
            <summary>
            This is the upper limit of length all items in the log folder may have combined across all processes.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.LogHost.LoggingPath">
            <summary>
            Path to where the logfiles live.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.LogHost.MaxLogFileAge">
            <summary>
            Any logfile older than this will automatically be cleansed
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.LogHost.MessageLogFileEnabled">
            <summary>
            Governs, whether a log file for the system messages is written
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.LogHost.MessageLogEnabled">
            <summary>
            Governs, whether a log of recent messages is kept in memory
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.LogHost.ErrorLogFileEnabled">
            <summary>
            Governs, whether log files for errors are written
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.LogHost.ErrorLogEnabled">
            <summary>
            Governs, whether a log of recent errors is kept in memory
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.LogHost.OutQueueError">
            <summary>
            The outbound queue for errors. These will be processed and written to xml
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.LogHost.OutQueueLog">
            <summary>
            The outbound queue for logs. These will be processed and written to logfile
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Message.LogHost.GetErrors">
            <summary>
            Retrieves a copy of the Error stack
            </summary>
            <returns>All errors thrown by functions using the message or flowcontrol system</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Message.LogHost.GetLog">
            <summary>
            Retrieves a copy of the message log
            </summary>
            <returns>All messages logged this session.</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Message.LogHost.WriteErrorEntry(System.Management.Automation.ErrorRecord[],System.String,System.String,System.Collections.Generic.List{System.String},System.DateTime,System.String,System.Guid,System.String)">
            <summary>
            Write an error record to the log
            </summary>
            <param name="Record">The actual error record as powershell wrote it</param>
            <param name="FunctionName">The name of the function writing the error</param>
            <param name="ModuleName">The name of the module the function writing the error came from</param>
            <param name="Tags">The tags that were assigned to the error event</param>
            <param name="Timestamp">When was the error written</param>
            <param name="Message">What message was passed to the user</param>
            <param name="Runspace">The runspace the message was written from</param>
            <param name="ComputerName">The computer the error was written on</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Message.LogHost.WriteLogEntry(System.String,Sqlcollaborative.Dbatools.Message.LogEntryType,System.DateTime,System.String,System.String,System.Collections.Generic.List{System.String},Sqlcollaborative.Dbatools.Message.MessageLevel,System.Guid,System.String,System.String,System.Int32,System.Collections.Generic.IEnumerable{System.Management.Automation.CallStackFrame},System.String,System.Object)">
            <summary>
            Write a new entry to the log
            </summary>
            <param name="Message">The message to log</param>
            <param name="Type">The type of the message logged</param>
            <param name="Timestamp">When was the message generated</param>
            <param name="FunctionName">What function wrote the message</param>
            <param name="ModuleName">What module did the function writing this message come from?</param>
            <param name="Tags">The tags that were applied to the message</param>
            <param name="Level">At what level was the function written</param>
            <param name="Runspace">The runspace the message is coming from</param>
            <param name="ComputerName">The computer the message was generated on</param>
            <param name="File">The file from which the message was written</param>
            <param name="Line">The line on which the message was written</param>
            <param name="TargetObject">The object associated with a given message.</param>
            <param name="CallStack">The callstack at the moment the message was written.</param>
            <param name="Username">The name of the user under which the code being executed</param>
            <returns>The entry that is being written</returns>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Message.MessageEventSubscription">
            <summary>
            Condition and logic to be executed on message events
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageEventSubscription.Name">
            <summary>
            Name of the event subscription, must be unique.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageEventSubscription.ScriptBlock">
            <summary>
            Scriptblock to execute if the condition is met
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageEventSubscription._MessageFilter">
            <summary>
            The internally stored filter value for Message
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Message.MessageEventSubscription.MessageFilter">
            <summary>
            The value the Message is filtered by
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Message.MessageEventSubscription.MessageFilterSet">
            <summary>
            Whether filtering by Message was enabled
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageEventSubscription._ModuleNameFilter">
            <summary>
            The internally stored filter value for ModuleName
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Message.MessageEventSubscription.ModuleNameFilter">
            <summary>
            The value the ModuleName is filtered by
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Message.MessageEventSubscription.ModuleNameFilterSet">
            <summary>
            Whether filtering by ModuleName was enabled
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageEventSubscription._FunctionNameFilter">
            <summary>
            The internally stored filter value for FunctionName
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Message.MessageEventSubscription.FunctionNameFilter">
            <summary>
            The value the FunctionName is filtered by
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Message.MessageEventSubscription.FunctionNameFilterSet">
            <summary>
            Whether filtering by FunctionName was enabled
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageEventSubscription._TargetFilter">
            <summary>
            The internally stored filter value for Target
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Message.MessageEventSubscription.TargetFilter">
            <summary>
            The value the Target is filtered by
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Message.MessageEventSubscription.TargetFilterSet">
            <summary>
            Whether filtering by Target was enabled
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageEventSubscription._LevelFilter">
            <summary>
            The internally stored filter value for Level
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Message.MessageEventSubscription.LevelFilter">
            <summary>
            The value the Level is filtered by
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Message.MessageEventSubscription.LevelFilterSet">
            <summary>
            Whether filtering by Level was enabled
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageEventSubscription._TagFilter">
            <summary>
            The internally stored filter value for Tag
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Message.MessageEventSubscription.TagFilter">
            <summary>
            The value the Tag is filtered by
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Message.MessageEventSubscription.TagFilterSet">
            <summary>
            Whether filtering by Tag was enabled
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageEventSubscription._RunspaceFilter">
            <summary>
            The internally stored filter value for Runspace
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Message.MessageEventSubscription.RunspaceFilter">
            <summary>
            The value the Runspace is filtered by
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Message.MessageEventSubscription.RunspaceFilterSet">
            <summary>
            Whether filtering by Runspace was enabled
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Message.MessageEventSubscription.Applies(Sqlcollaborative.Dbatools.Message.LogEntry)">
            <summary>
            Checks, whether a given entry matches the filter defined in this subscription
            </summary>
            <param name="Entry">The entry to validate</param>
            <returns>Whether the subscription should react to this entry</returns>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Message.MessageLevelModifier">
            <summary>
            A modification to a given message's level
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageLevelModifier.Name">
            <summary>
            Name of the modifier. Prevents duplication in a multi-runspace scenario.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageLevelModifier.Modifier">
            <summary>
            The amount to modify the level by
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageLevelModifier.IncludeFunctionName">
            <summary>
            Apply modifier only to messages from this function.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageLevelModifier.ExcludeFunctionName">
            <summary>
            Apply modifier not when the message is written by this function.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageLevelModifier.IncludeModuleName">
            <summary>
            Apply modifier only to messages from this module
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageLevelModifier.ExcludeModuleName">
            <summary>
            Do not apply modifier to messages from this module
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageLevelModifier.IncludeTags">
            <summary>
            Only apply this modifier to a message that includes at least one of these tags
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageLevelModifier.ExcludeTags">
            <summary>
            Do not apply this modifier to a message that includes any of the following tags
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Message.MessageLevelModifier.AppliesTo(System.String,System.String,System.Collections.Generic.List{System.String})">
            <summary>
            Tests, whether a message a message should be modified by this modiier
            </summary>
            <param name="FunctionName">The name of the function writing the message</param>
            <param name="ModuleName">The name of the module, the function writing this message comes from</param>
            <param name="Tags">The tags of the message written</param>
            <returns>Whether the message applies</returns>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Message.TransformCondition">
            <summary>
            A condition, under which the object shall be transaformed
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.TransformCondition.TypeName">
            <summary>
            Name of the type. All similar types (as determined by the '-like' operator) will be transformed.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.TransformCondition.ModuleName">
            <summary>
            The name of the module to consider, using the -Like operator
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.TransformCondition.FunctionName">
            <summary>
            The name of the function name to consider, using the -Like operator
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.TransformCondition.ScriptBlock">
            <summary>
            The scriptblock that performs the transformation
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.TransformCondition.Type">
            <summary>
            What kind of transformation is being performed?
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Message.TransformCondition.#ctor(System.String,System.String,System.String,System.Management.Automation.ScriptBlock,Sqlcollaborative.Dbatools.Message.TransformType)">
            <summary>
            Initializes a transform condition
            </summary>
            <param name="TypeName">Only objects of similar name will be transformed</param>
            <param name="ModuleName">Only objects coming from similar modules will be considered</param>
            <param name="FunctionName">Only objects coming from similar functions will be considered</param>
            <param name="ScriptBlock">The scriptblock used for the transformation</param>
            <param name="Type">What kind of transformation this is</param>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Message.TransformError">
            <summary>
            An error occured during a message transformation
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.TransformError.Record">
            <summary>
            The error record of what went wrong
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.TransformError.FunctionName">
            <summary>
            The name of the function writing the message that failed to transform
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.TransformError.ModuleName">
            <summary>
            The name of the module the command writing the message came from
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.TransformError.Timestamp">
            <summary>
            When did it all happen?
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.TransformError.Object">
            <summary>
            The object that was supposed to be transformed
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.TransformError.Type">
            <summary>
            The kind of transform that failed
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.TransformError.Runspace">
            <summary>
            The runspace it all happened on
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Message.TransformError.#ctor(System.Management.Automation.ErrorRecord,System.String,System.String,System.Object,Sqlcollaborative.Dbatools.Message.TransformType,System.Guid)">
            <summary>
            Creates a new transform error
            </summary>
            <param name="Record">The record of what went wrong</param>
            <param name="FunctionName">The name of the function writing the transformed message</param>
            <param name="ModuleName">The module the function writing the transformed message is part of</param>
            <param name="Object">The object that should have been transformed</param>
            <param name="Type">The type of transform that was attempted</param>
            <param name="Runspace">The runspace it all happened on</param>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Message.TransformList">
            <summary>
            List engine, managing the lists for a message transformation type
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Message.TransformList.GetAll">
            <summary>
            Returns all entries in the list.
            </summary>
            <returns>The list of transforms contained within</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Message.TransformList.IsListed(Sqlcollaborative.Dbatools.Message.TransformCondition)">
            <summary>
            Returns whether the actual object is part of the list
            </summary>
            <param name="Condition">The object to test for list membership</param>
            <returns>Whether the object is listed</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Message.TransformList.IsContained(Sqlcollaborative.Dbatools.Message.TransformCondition)">
            <summary>
            Returns whether a condition with equal conditions already exists
            </summary>
            <param name="Condition">The condition to test</param>
            <returns>Whether the referenced condition is already listed</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Message.TransformList.Add(Sqlcollaborative.Dbatools.Message.TransformCondition)">
            <summary>
            Adds a condition to the list, if there is no equivalent condition present.
            </summary>
            <param name="Condition">The condition to add</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Message.TransformList.Remove(Sqlcollaborative.Dbatools.Message.TransformCondition)">
            <summary>
            Removes a condition from the lsit of conditional transforms
            </summary>
            <param name="Condition">The condition to remove</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Message.TransformList.Get(System.String,System.String,System.String)">
            <summary>
            Returns the first transform whose filter is similar enough to work out.
            </summary>
            <param name="TypeName">The name of the type to check for a transform</param>
            <param name="ModuleName">The module of the command that wrote the message with the transformable object</param>
            <param name="Functionname">The command that wrote the message with the transformable object</param>
            <returns>Either a transform or null, if no fitting transform was found</returns>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Message.TransformType">
            <summary>
            The messaging system provides these kinds of transformations for input.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.TransformType.Target">
            <summary>
            A target transform can transform the target object specified. Used for live-state objects that should not be serialized on a second thread.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.TransformType.Exception">
            <summary>
            An exception transform allows automatic transformation of exceptions. Primarily used to unwrap exceptions from an API that wraps all exceptions.
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Message.DbatoolsException">
            <summary>
            Wrapper class that can emulate any exception for purpose of serialization without blowing up the storage space consumed
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Message.DbatoolsException.GetException">
            <summary>
            Returns the original exception object that we interpreted. This is on purpose not a property, as we want to avoid messing with serialization size.
            </summary>
            <returns>The original exception that got thrown</returns>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.DbatoolsException.Message">
            <summary>
            The actual Exception Message
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.DbatoolsException.Source">
            <summary>
            The original source of the Exception
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.DbatoolsException.StackTrace">
            <summary>
            Where on the callstack did the exception occur?
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.DbatoolsException.TargetSite">
            <summary>
            What was the target site on the code that caused it. This property has been altered to avoid export issues, if a string representation is not sufficient, access the original exception using GetException()
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.DbatoolsException.HResult">
            <summary>
            The HResult of the exception. Useful in debugging native code errors.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.DbatoolsException.HelpLink">
            <summary>
            Link to a proper help article.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.DbatoolsException.Data">
            <summary>
            Additional data that has been appended
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.DbatoolsException.InnerException">
            <summary>
            The inner exception in a chain of exceptions.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.DbatoolsException.ExceptionTypeName">
            <summary>
            The full namespace name of the exception that has been wrapped.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.DbatoolsException.ExceptionData">
            <summary>
            Contains additional properties other exceptions might contain.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.DbatoolsException.CategoryInfo">
            <summary>
            The category of the error
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.DbatoolsException.ErrorDetails">
            <summary>
            The details on the error
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.DbatoolsException.FullyQualifiedErrorId">
            <summary>
            The specific error identity, used to identify the target
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.DbatoolsException.InvocationInfo">
            <summary>
            The details of how this was called.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.DbatoolsException.ScriptStackTrace">
            <summary>
            The script's stacktrace
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.DbatoolsException.TargetObject">
            <summary>
            The object being processed
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.DbatoolsException.FunctionName">
            <summary>
            The name of the function throwing the error
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.DbatoolsException.Timestamp">
            <summary>
            When was the error thrown
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.DbatoolsException.Runspace">
            <summary>
            The runspace the error occured on.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.DbatoolsException.ComputerName">
            <summary>
            The computer the error occured on.
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Message.DbatoolsException.#ctor">
            <summary>
            Creates an empty exception object. Mostly for serialization support
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Message.DbatoolsException.#ctor(System.Exception)">
            <summary>
            Creates an exception based on an original exception object
            </summary>
            <param name="Except">The exception to wrap around</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Message.DbatoolsException.#ctor(System.Management.Automation.ErrorRecord)">
            <summary>
            Creates a rich information exception object based on a full error record as recorded by PowerShell
            </summary>
            <param name="Record">The error record to copy from</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Message.DbatoolsException.#ctor(System.Exception,System.String,System.DateTime,System.String,System.Guid,System.String)">
            <summary>
            Creates a new exception object with rich meta information from the PowerShell runtime.
            </summary>
            <param name="Except">The exception thrown</param>
            <param name="FunctionName">The name of the function in which the error occured</param>
            <param name="Timestamp">When did the error occur</param>
            <param name="Message">The message to add to the exception</param>
            <param name="Runspace">The ID of the runspace from which the exception was thrown. Useful in multi-runspace scenarios.</param>
            <param name="ComputerName">The computer the error occured on.</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Message.DbatoolsException.#ctor(System.Management.Automation.ErrorRecord,System.String,System.DateTime,System.String,System.Guid,System.String)">
            <summary>
            Creates a new exception object with rich meta information from the PowerShell runtime.
            </summary>
            <param name="Record">The error record written</param>
            <param name="FunctionName">The name of the function in which the error occured</param>
            <param name="Timestamp">When did the error occur</param>
            <param name="Message">The message to add to the exception</param>
            <param name="Runspace">The ID of the runspace from which the exception was thrown. Useful in multi-runspace scenarios.</param>
            <param name="ComputerName">The computer the error occured on.</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Message.DbatoolsException.ToString">
            <summary>
            Returns a string representation of the exception.
            </summary>
            <returns></returns>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Message.DbatoolsExceptionRecord">
            <summary>
            Carrier class, designed to hold an arbitrary number of exceptions. Used for exporting to XML in nice per-incident packages.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.DbatoolsExceptionRecord.Runspace">
            <summary>
            Runspace where shit happened.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.DbatoolsExceptionRecord.ComputerName">
            <summary>
            The computer name the exception was written on
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.DbatoolsExceptionRecord.Timestamp">
            <summary>
            When did things go bad?
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.DbatoolsExceptionRecord.FunctionName">
            <summary>
            Name of the function, where fail happened.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.DbatoolsExceptionRecord.ModuleName">
            <summary>
            The module of the function where fail happened
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.DbatoolsExceptionRecord.Tags">
            <summary>
            The tags that were applied to the failure
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.DbatoolsExceptionRecord.Message">
            <summary>
            The message the poor user was shown.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Message.DbatoolsExceptionRecord.ExceptionType">
            <summary>
            Displays the name of the exception, the make scanning exceptions easier.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Message.DbatoolsExceptionRecord.TargetObject">
            <summary>
            The target object of the first exception in the list, if any
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.DbatoolsExceptionRecord.Exceptions">
            <summary>
            List of Exceptions that are part of the incident (usually - but not always - only one).
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Message.DbatoolsExceptionRecord.#ctor">
            <summary>
            Creates an empty container. Ideal for the homeworker who loves doing it all himself.
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Message.DbatoolsExceptionRecord.#ctor(Sqlcollaborative.Dbatools.Message.DbatoolsException)">
            <summary>
            Creates a container filled with the first exception.
            </summary>
            <param name="Exception"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Message.DbatoolsExceptionRecord.#ctor(System.Guid,System.String,System.DateTime,System.String,System.String,System.Collections.Generic.List{System.String},System.String)">
            <summary>
            Creates a container filled with the meta information but untouched by exceptions
            </summary>
            <param name="Runspace">The runspace where it all happened</param>
            <param name="ComputerName">The computer the error was recorded</param>
            <param name="Timestamp">When did it happen?</param>
            <param name="FunctionName">Where did it happen?</param>
            <param name="ModuleName">The name of the module where fail happened</param>
            <param name="Tags">The tags that were assigned to the failure</param>
            <param name="Message">What did the witness have to say?</param>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Message.LogEntry">
            <summary>
            An individual entry for the message log
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.LogEntry.Message">
            <summary>
            The message logged
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.LogEntry.Type">
            <summary>
            What kind of entry was this?
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.LogEntry.Timestamp">
            <summary>
            When was the message logged?
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.LogEntry.FunctionName">
            <summary>
            What function wrote the message
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.LogEntry.ModuleName">
            <summary>
            The name of the module of the function that wrote the message
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.LogEntry.Tags">
            <summary>
            The tags applied to the message
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.LogEntry.Level">
            <summary>
            What level was the message?
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.LogEntry.Runspace">
            <summary>
            What runspace was the message written from?
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.LogEntry.ComputerName">
            <summary>
            The computer the message was generated on
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.LogEntry.TargetObject">
            <summary>
            The object that was the focus of this message.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.LogEntry.File">
            <summary>
            The file from which the message was written.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.LogEntry.Line">
            <summary>
            The line on which the message was written.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.LogEntry.CallStack">
            <summary>
            The callstack when the message was written.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.LogEntry.Username">
            <summary>
            The user that did the writing.
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Message.LogEntry.#ctor">
            <summary>
            Creates an empty log entry
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Message.LogEntry.#ctor(System.String,Sqlcollaborative.Dbatools.Message.LogEntryType,System.DateTime,System.String,System.String,System.Collections.Generic.List{System.String},Sqlcollaborative.Dbatools.Message.MessageLevel,System.Guid,System.String,System.Object,System.String,System.Int32,System.Collections.Generic.IEnumerable{System.Management.Automation.CallStackFrame},System.String)">
            <summary>
            Creates a filled out log entry
            </summary>
            <param name="Message">The message that was logged</param>
            <param name="Type">The type(s) of message written</param>
            <param name="Timestamp">When was the message logged</param>
            <param name="FunctionName">What function wrote the message</param>
            <param name="ModuleName">Name of the module the function writing this message came from</param>
            <param name="Tags">Tags that were applied to the message</param>
            <param name="Level">What level was the message written at.</param>
            <param name="Runspace">The ID of the runspace that wrote the message.</param>
            <param name="ComputerName">The computer the message was generated on.</param>
            <param name="TargetObject">The object this message was all about.</param>
            <param name="File">The file of the code that wrote the message.</param>
            <param name="Line">The line on which the message was written.</param>
            <param name="CallStack">The callstack that triggered the write.</param>
            <param name="Username">The user responsible for running the code that is writing the message.</param>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Message.LogEntryType">
            <summary>
            The kind of information the logged entry was.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.LogEntryType.None">
            <summary>
            This entry wasn't written to any stream
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.LogEntryType.Information">
            <summary>
            A message that was written to the current host equivalent, if available also to the information stream
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.LogEntryType.Verbose">
            <summary>
            A message that was written to the verbose stream
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.LogEntryType.Debug">
            <summary>
            A message that was written to the Debug stream
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.LogEntryType.Warning">
            <summary>
            A message written to the warning stream
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Message.MessageHost">
            <summary>
            Provides static resources to the messaging subsystem
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageHost.MaximumInformation">
            <summary>
            The maximum message level to still display to the user directly.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageHost.MaximumVerbose">
            <summary>
            The maxium message level where verbose information is still written.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageHost.MaximumDebug">
            <summary>
            The maximum message level where debug information is still written.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageHost.MinimumInformation">
            <summary>
            The minimum required message level for messages that will be shown to the user.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageHost.MinimumVerbose">
            <summary>
            The minimum required message level where verbose information is written.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageHost.MinimumDebug">
            <summary>
            The minimum required message level where debug information is written.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageHost.InfoColor">
            <summary>
            The color stuff gets written to the console in
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageHost.InfoColorEmphasis">
            <summary>
            The color important stuff gets written to the console in
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageHost.InfoColorSubtle">
            <summary>
            The color background stuff gets written to the console in
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageHost.DeveloperColor">
            <summary>
            The color stuff gets written to the console in, when developer mode is enabled and the message would not have been written after all
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageHost.DeveloperMode">
            <summary>
            Enables the developer mode. In this all messages are written to the console, in order to make it easier to troubleshoot issues.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageHost.NestedLevelDecrement">
            <summary>
            Message levels can decrease by nested level. This causes messages to have an increasingly reduced level as the size of the callstack increases. 
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageHost.DisableVerbosity">
            <summary>
            Globally override all verbosity
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageHost.EnableMessageTimestamp">
            <summary>
            Include message timestamps in verbose message output
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageHost.EnableMessageDisplayCommand">
            <summary>
            Include the message display command in the verbose message output
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageHost.EnableMessageBreadcrumbs">
            <summary>
            Include the entire callstack in the verbose message output
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageHost.TransformErrorQueueSize">
            <summary>
            The size of the transform error queue. When adding more than this, the oldest entry will be discarded
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageHost.ExceptionTransforms">
            <summary>
            Provides the option to transform exceptions based on the original exception type
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageHost.TargetTransforms">
            <summary>
            Provides the option to transform target objects based on type. This is sometimes important when working with live state objects that should not be serialized.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageHost.TransformErrors">
            <summary>
            The list of transformation errors that occured.
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Message.MessageHost.GetTransformErrors">
            <summary>
            Returns the current queue of failed transformations
            </summary>
            <returns>The list of transformations that failed</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Message.MessageHost.WriteTransformError(System.Management.Automation.ErrorRecord,System.String,System.String,System.Object,Sqlcollaborative.Dbatools.Message.TransformType,System.Guid)">
            <summary>
            Writes a new transform error
            </summary>
            <param name="Record">The record of what went wrong</param>
            <param name="FunctionName">The name of the function writing the transformed message</param>
            <param name="ModuleName">The module the function writing the transformed message is part of</param>
            <param name="Object">The object that should have been transformed</param>
            <param name="Type">The type of transform that was attempted</param>
            <param name="Runspace">The runspace it all happened on</param>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageHost.ExceptionTransformList">
            <summary>
            List of custom transforms for exceptions
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageHost.TargetTransformlist">
            <summary>
            List of custom transforms for targets
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageHost.MessageLevelModifiers">
            <summary>
            List of all modifiers that apply to message levels
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageHost.Events">
            <summary>
            List of events that subscribe to messages being written
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Message.MessageLevel">
            <summary>
            The various levels of verbosity available.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageLevel.Critical">
            <summary>
            Very important message, should be shown to the user as a high priority
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageLevel.Important">
            <summary>
            Important message, the user should read this
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageLevel.Output">
            <summary>
            Important message, the user should read this
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageLevel.Significant">
            <summary>
            Message relevant to the user.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageLevel.VeryVerbose">
            <summary>
            Not important to the regular user, still of some interest to the curious
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageLevel.Verbose">
            <summary>
            Background process information, in case the user wants some detailed information on what is currently happening.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageLevel.SomewhatVerbose">
            <summary>
            A footnote in current processing, rarely of interest to the user
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageLevel.System">
            <summary>
            A message of some interest from an internal system persepctive, but largely irrelevant to the user.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageLevel.Debug">
            <summary>
            Something only of interest to a debugger
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageLevel.InternalComment">
            <summary>
            This message barely made the cut from being culled. Of purely development internal interest, and even there is 'interest' a strong word for it.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Message.MessageLevel.Warning">
            <summary>
            This message is a warning, sure sign something went badly wrong
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter">
            <summary>
            Parameter class that handles the various kinds of credential input
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter.Credential">
            <summary>
            The credential object received
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter.UserName">
            <summary>
            The name of the credential object
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter.Password">
            <summary>
            The password of the credential object
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter.#ctor(System.Management.Automation.PSCredential)">
            <summary>
            Creates a credential parameter from a PSCredential object
            </summary>
            <param name="Credential">A PSCredential object</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter.#ctor(System.Net.NetworkCredential)">
            <summary>
            Creates a credential parameter from a NetworkCredential object
            </summary>
            <param name="Credential">The credentials to use</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter.#ctor(System.String)">
            <summary>
            Creates a credential parameter from a string only. Will prompt the user for the rest of the input. Will provide an option to remember the credential under the name provided
            </summary>
            <param name="UserName">The username (and domain name as may be the case) to put a credential around</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter.#ctor(System.Object)">
            <summary>
            Creates a credential parameter from anything it nows how to handle
            </summary>
            <param name="Credential">The object to convert</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter.op_Implicit(Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter)~System.Management.Automation.PSCredential">
            <summary>
            Implicitly converts from DbaCredentialParameter to PSCredential
            </summary>
            <param name="Input">The DbaCredentialParameter to convert</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter.op_Implicit(System.Management.Automation.PSCredential)~Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter">
            <summary>
            Implicitly converts a PSCredential object to DbaCredenitalParameter
            </summary>
            <param name="Input">The PSCredential to convert</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter.op_Implicit(Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter)~System.Net.NetworkCredential">
            <summary>
            Implicitly converts from DbaCredentialParameter to NetworkCredential
            </summary>
            <param name="Input">The DbaCredentialParameter to convert</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter.op_Implicit(System.Net.NetworkCredential)~Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter">
            <summary>
            Implicitly converts a NetworkCredential object to DbaCredenitalParameter
            </summary>
            <param name="Input">The NetworkCredential to convert</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter.GetNetworkCredential">
            <summary>
            Legacy wrapper. While there exists implicit conversion, this allows using the object as before, avoiding errors for unknown method.
            </summary>
            <returns>A network credential object with the same credentials as the original object</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter.PromptForCredential(System.String)">
            <summary>
            Prompts the user for a password to complete a credentials object
            </summary>
            <param name="Name">The name of the user. If specified, this will be added to the prompt.</param>
            <returns>The finished PSCredential object</returns>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter.CredentialStore">
            <summary>
            Cached credentials, if the user stors them under a name.
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter.GetTypeCode">
            <summary>
            
            </summary>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter.ToBoolean(System.IFormatProvider)">
            <summary>
            
            </summary>
            <param name="Format"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter.ToChar(System.IFormatProvider)">
            <summary>
            
            </summary>
            <param name="Format"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter.ToSByte(System.IFormatProvider)">
            <summary>
            
            </summary>
            <param name="Format"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter.ToByte(System.IFormatProvider)">
            <summary>
            
            </summary>
            <param name="Format"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter.ToInt16(System.IFormatProvider)">
            <summary>
            
            </summary>
            <param name="Format"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter.ToUInt16(System.IFormatProvider)">
            <summary>
            
            </summary>
            <param name="Format"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter.ToInt32(System.IFormatProvider)">
            <summary>
            
            </summary>
            <param name="Format"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter.ToUInt32(System.IFormatProvider)">
            <summary>
            
            </summary>
            <param name="Format"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter.ToInt64(System.IFormatProvider)">
            <summary>
            
            </summary>
            <param name="Format"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter.ToUInt64(System.IFormatProvider)">
            <summary>
            
            </summary>
            <param name="Format"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter.ToSingle(System.IFormatProvider)">
            <summary>
            
            </summary>
            <param name="Format"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter.ToDouble(System.IFormatProvider)">
            <summary>
            
            </summary>
            <param name="Format"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter.ToDecimal(System.IFormatProvider)">
            <summary>
            
            </summary>
            <param name="Format"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter.ToDateTime(System.IFormatProvider)">
            <summary>
            
            </summary>
            <param name="Format"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter.ToString(System.IFormatProvider)">
            <summary>
            
            </summary>
            <param name="Format"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter.ToType(System.Type,System.IFormatProvider)">
            <summary>
            Tries to convert the credential parameter to one of its supported types
            </summary>
            <param name="TargetType">The type to convert to</param>
            <param name="Format">Irrelevant</param>
            <returns></returns>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Parameter.DbaDatabaseParameter">
            <summary>
            Parameter class that accepts anything pointing at a database
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Parameter.DbaDatabaseParameter.InputObject">
            <summary>
            The original object passed to the parameter
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Parameter.DbaDatabaseParameter.Database">
            <summary>
            The SMO Database Object
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Parameter.DbaDatabaseParameter.Name">
            <summary>
            The name of the database
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaDatabaseParameter.#ctor(System.String)">
            <summary>
            Accepts the name of a database and converts it to a DbaDatabaseParameter
            </summary>
            <param name="Name"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaDatabaseParameter.#ctor(System.Object)">
            <summary>
            Accepts anything and tries to convert it to a live SMO Database object
            </summary>
            <param name="Item">The item to convert</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaDatabaseParameter.ToString">
            <summary>
            Overrides the regular tostring to show something pleasant and useful
            </summary>
            <returns>The name of the database</returns>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Parameter.DbaDatabaseSmoParameter">
            <summary>
            Parameter class that only accepts live SMO Databases
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Parameter.DbaDatabaseSmoParameter.InputObject">
            <summary>
            The original object passed to the parameter
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Parameter.DbaDatabaseSmoParameter.Database">
            <summary>
            The SMO Database Object
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Parameter.DbaDatabaseSmoParameter.Name">
            <summary>
            The name of the database
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaDatabaseSmoParameter.#ctor(System.Object)">
            <summary>
            Accepts anything and tries to convert it to a live SMO Database object
            </summary>
            <param name="Item">The item to convert</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaDatabaseSmoParameter.ToString">
            <summary>
            Overrides the regular tostring to show something pleasant and useful
            </summary>
            <returns>The name of the database</returns>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Parameter.DbaSelectParameter">
            <summary>
            Class that automatically parses input chosen for the -Property parameter of Select-PSUObject
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Parameter.DbaSelectParameter.InputObject">
            <summary>
            The original input object
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Parameter.DbaSelectParameter.Value">
            <summary>
            The value as Select-Object wants it
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaSelectParameter.#ctor(System.String)">
            <summary>
            Builds a property parameter from string
            </summary>
            <param name="Value">The string to interpret</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaSelectParameter.#ctor(System.Collections.Hashtable)">
            <summary>
            Builds a select parameter from a hashtable (pretty straightforward)
            </summary>
            <param name="Hash">The hashtable to accept</param>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Parameter.DbaCmConnectionParameter">
            <summary>
            Input converter for Computer Management Information
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Parameter.DbaCmConnectionParameter.Connection">
            <summary>
            The resolved connection object
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Parameter.DbaCmConnectionParameter.Success">
            <summary>
            Whether input processing was successful
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Parameter.DbaCmConnectionParameter.InputObject">
            <summary>
            The object actually passed to the class
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaCmConnectionParameter.op_Implicit(Sqlcollaborative.Dbatools.Parameter.DbaCmConnectionParameter)~Sqlcollaborative.Dbatools.Connection.ManagementConnection">
            <summary>
            Implicitly convert all connection parameter objects to the connection-type
            </summary>
            <param name="Input">The parameter object to convert</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaCmConnectionParameter.#ctor(System.String)">
            <summary>
            Creates a new DbaWmConnectionParameter based on an input-name
            </summary>
            <param name="ComputerName">The name of the computer the connection is stored for.</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaCmConnectionParameter.#ctor(Sqlcollaborative.Dbatools.Connection.ManagementConnection)">
            <summary>
            Creates a new DbaWmConnectionParameter based on an already existing connection object.
            </summary>
            <param name="Connection">The connection to accept</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaCmConnectionParameter.#ctor(System.Object)">
            <summary>
            Tries to convert a generic input object into a true input.
            </summary>
            <param name="Input">Any damn object in the world</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaCmConnectionParameter.#ctor(Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter)">
            <summary>
            Creates a new DbaCmConnectionParameter based on an instance parameter
            </summary>
            <param name="Instance">The instance to interpret</param>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Parameter.DbaInstanceInputType">
            <summary>
            What kind of object was bound to the parameter class?
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Parameter.DbaInstanceInputType.Default">
            <summary>
            Anything, really. An unspecific not reusable type was bound
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Parameter.DbaInstanceInputType.Linked">
            <summary>
            A live smo linked server object was bound
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Parameter.DbaInstanceInputType.Server">
            <summary>
            A live smo server object was bound
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Parameter.DbaInstanceInputType.RegisteredServer">
            <summary>
            A Central Management Server RegisteredServer SMO object was bound
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Parameter.DbaInstanceInputType.ConnectionString">
            <summary>
            An actual connection string was specified. Connection strings are directly reused for SMO connections
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Parameter.DbaInstanceInputType.ConnectionStringLocalDB">
            <summary>
            A connection string pointing at a local, file-based DB
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Parameter.DbaInstanceInputType.SqlConnection">
            <summary>
            An already established sql connection to was created outside of SMO
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter">
            <summary>
            Input converter for instance information
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter.ComputerName">
            <summary>
            Name of the computer as resolvable by DNS
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter.InstanceName">
            <summary>
            Name of the instance on the target server
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter.Port">
            <summary>
            The port over which to connect to the server. Only present if non-default
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter.NetworkProtocol">
            <summary>
            The network protocol to connect over
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter.IsLocalHost">
            <summary>
            Verifies, whether the specified computer is localhost or not.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter.FullName">
            <summary>
            Full name of the instance, including the server-name
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter.FullSmoName">
            <summary>
            Full name of the instance, including the server-name, used when connecting via SMO
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter.SqlComputerName">
            <summary>
            Name of the computer as used in an SQL Statement
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter.SqlInstanceName">
            <summary>
            Name of the instance as used in an SQL Statement
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter.SqlFullName">
            <summary>
            Full name of the instance, including the server-name as used in an SQL statement
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter.IsConnectionString">
            <summary>
            Whether the input is a connection string
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter.InputObject">
            <summary>
            The original object passed to the parameter class.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter.Type">
            <summary>
            What kind of object was bound to the parameter class? For efficiency's purposes.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter.LinkedLive">
            <summary>
            Returns, whether a live SMO object was bound for the purpose of accessing LinkedServer functionality
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter.LinkedServer">
            <summary>
            Returns the available Linked Server objects from live objects only
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter.op_Implicit(Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter)~System.String">
            <summary>
            Converts the parameter class to its full name
            </summary>
            <param name="Input">The parameter class object to convert</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter.#ctor(System.String)">
            <summary>
            Creates a DBA Instance Parameter from string
            </summary>
            <param name="Name">The name of the instance</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter.#ctor(System.Net.IPAddress)">
            <summary>
            Creates a DBA Instance Parameter from an IPAddress
            </summary>
            <param name="Address"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter.#ctor(System.Net.NetworkInformation.PingReply)">
            <summary>
            Creates a DBA Instance Parameter from the reply to a ping
            </summary>
            <param name="Ping">The result of a ping</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter.#ctor(System.Net.IPHostEntry)">
            <summary>
            Creates a DBA Instance Parameter from the result of a dns resolution
            </summary>
            <param name="Entry">The result of a dns resolution, to be used for targetting the default instance</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter.#ctor(System.Data.SqlClient.SqlConnection)">
            <summary>
            Creates a DBA Instance Parameter from an established SQL Connection
            </summary>
            <param name="Connection">The connection to reuse</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter.#ctor(Sqlcollaborative.Dbatools.Discovery.DbaInstanceReport)">
            <summary>
            Accept and understand discovery reports.
            </summary>
            <param name="Report">The report to interpret</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter.#ctor(System.Object)">
            <summary>
            Creates a DBA Instance parameter from any object
            </summary>
            <param name="Input">Object to parse</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter.ToString">
            <summary>
            Overrides the regular <c>ToString()</c> to show something pleasant and useful
            </summary>
            <returns>The <see cref="P:Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter.FullSmoName"/></returns>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Parameter.ParameterContractAttribute">
            <summary>
            The attribute used to define the elements of a ParameterClass contract
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Parameter.ParameterContractAttribute.Type">
            <summary>
            Returns the type of the element this attribute is supposed to be attached to.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Parameter.ParameterContractAttribute.Behavior">
            <summary>
            Returns the behavior to expect from the contracted element. This sets the expectations on how this element is likely to act.
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Parameter.ParameterContractAttribute.#ctor(Sqlcollaborative.Dbatools.Parameter.ParameterContractType,Sqlcollaborative.Dbatools.Parameter.ParameterContractBehavior)">
            <summary>
            Ceates a perfectly common parameter contract attribute. For use with all parameter classes' public elements.
            </summary>
            <param name="Type"></param>
            <param name="Behavior"></param>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Parameter.ParameterContractBehavior">
            <summary>
            Defines how this element will behave
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Parameter.ParameterContractBehavior.NotContracted">
            <summary>
            This elements is not actually part of the contract. Generally you wouldn't want to add the attribute at all in that case. However, in some places it helps avoiding confusion.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Parameter.ParameterContractBehavior.Mandatory">
            <summary>
            This element may never be null and must be considered in all assignments. Even if the element is de facto not nullable, all constructors must assign it.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Parameter.ParameterContractBehavior.Optional">
            <summary>
            This element may contain data, but is not required to. In case of a method, it may simply do nothing
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Parameter.ParameterContractBehavior.Failable">
            <summary>
            This method may throw an error when executing and should always be handled with try/catch. Use this on methods that use external calls.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Parameter.ParameterContractBehavior.Arbiter">
            <summary>
            The content of the thus marked field determines the dependent's state. Generally, only if the arbiter is true, will the dependent elements be mandatory. This behavior may only be assigned to boolean fields.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Parameter.ParameterContractBehavior.Conditional">
            <summary>
            This behavior can be assigned together with the 'Mandatory' behavior. It means the field is only mandatory if an arbiter field is present and set to true.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Parameter.ParameterContractBehavior.Conversion">
            <summary>
            Converts content. Generally applied only to operators, but some methods may also convert information.
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Parameter.ParameterContractType">
            <summary>
            Defines what kind of element is granted the contract
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Parameter.ParameterContractType.Field">
            <summary>
            The contracted element is a field containing a value
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Parameter.ParameterContractType.Method">
            <summary>
            The contracted element is a method, performing an action
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Parameter.ParameterContractType.Operator">
            <summary>
            The contracted element is an operator, facilitating type conversion. Generally into a dedicated object type this parameterclass abstracts.
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Runspace.DbaRunspaceState">
            <summary>
            Contains the state a managed, unique runspace can be in.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Runspace.DbaRunspaceState.Running">
            <summary>
            The runspace is up and running
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Runspace.DbaRunspaceState.Stopping">
            <summary>
            The runspace has received the stop order, but has not yet obeyed it
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Runspace.DbaRunspaceState.Stopped">
            <summary>
            The runspace has followed its order to stop and is currently disabled
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Runspace.RunspaceContainer">
            <summary>
            Class that contains the logic necessary to manage a unique runspace
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Runspace.RunspaceContainer.Name">
            <summary>
            The name of the runspace.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Runspace.RunspaceContainer.RunspaceGuid">
            <summary>
            The Guid of the running Runspace
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Runspace.RunspaceContainer.SetScript(System.Management.Automation.ScriptBlock)">
            <summary>
            Sets the script to execute in the runspace. Will NOT take immediate effect. Only after restarting the runspace will it be used.
            </summary>
            <param name="Script">The scriptblock to execute</param>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Runspace.RunspaceContainer.State">
            <summary>
            The state the runspace currently is in.
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Runspace.RunspaceContainer.Start">
            <summary>
            Starts the Runspace.
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Runspace.RunspaceContainer.SetName(System.Management.Automation.Runspaces.Runspace)">
            <summary>
            Sets the name on a runspace. This WILL FAIL for PowerShell v3!
            </summary>
            <param name="Runspace">The runspace to be named</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Runspace.RunspaceContainer.Stop">
            <summary>
            Gracefully stops the Runspace
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Runspace.RunspaceContainer.Kill">
            <summary>
            Very ungracefully kills the runspace. Use only in the most dire emergency.
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Runspace.RunspaceContainer.SignalStopped">
            <summary>
            Signals the registered runspace has stopped execution
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Runspace.RunspaceContainer.#ctor(System.String,System.Management.Automation.ScriptBlock)">
            <summary>
            Creates a new runspace container with the basic information needed
            </summary>
            <param name="Name">The name of the Runspace</param>
            <param name="Script">The code using the runspace logic</param>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Runspace.RunspaceHost">
            <summary>
            Provides hosting for all registered runspaces
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Runspace.RunspaceHost.StopTimeoutSeconds">
            <summary>
            The number of seconds before a Stop command is interrupted and instead the runspace is gracelessly shut down.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Runspace.RunspaceHost.Runspaces">
            <summary>
            The dictionary containing the definitive list of unique Runspace
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.TabExpansion.TabCompletionSet">
            <summary>
            Contains information used to transmit Tepp Assignment
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.TabExpansion.TabCompletionSet.Command">
            <summary>
            The name of the command to complete. "*" if all commands that have the parameter should be selected instead
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.TabExpansion.TabCompletionSet.Parameter">
            <summary>
            The parameter to complete
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.TabExpansion.TabCompletionSet.Script">
            <summary>
            The name of the script to complete with
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.TabExpansion.TabCompletionSet.#ctor(System.String,System.String,System.String)">
            <summary>
            Creates a new tab completion set object with all information prefilled
            </summary>
            <param name="Command">The name of the command to complete. "*" if all commands that have the parameter should be selected instead</param>
            <param name="Parameter">The parameter to complete</param>
            <param name="Script">The name of the script to complete with</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.TabExpansion.TabCompletionSet.Applies(System.String,System.String)">
            <summary>
            Tests, whether the completion set applies to the specified parameter / command combination
            </summary>
            <param name="Command">The command to test</param>
            <param name="Parameter">The parameter of the command to test</param>
            <returns>Whether this completion set applies to the specified combination of parameter / command</returns>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.TabExpansion.InstanceAccess">
            <summary>
            Contains information on access to an instance
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.TabExpansion.InstanceAccess.InstanceName">
            <summary>
            The name of the instance to access
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.TabExpansion.InstanceAccess.IsSysAdmin">
            <summary>
            Whether the account had sysadmin privileges. On multiple user usage, the cache will prefer sysadmin accounts.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.TabExpansion.InstanceAccess.ConnectionObject">
            <summary>
            The actual connection object to connect with to the server
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.TabExpansion.InstanceAccess.LastAccess">
            <summary>
            When was the instance last accessed using dbatools
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.TabExpansion.InstanceAccess.LastUpdate">
            <summary>
            When was the instance's TEPP cache last updated
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.TabExpansion.ScriptContainer">
            <summary>
            Regular container to store scripts in, that are used in TEPP
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.TabExpansion.ScriptContainer.Name">
            <summary>
            The name of the scriptblock
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.TabExpansion.ScriptContainer.ScriptBlock">
            <summary>
            The scriptblock doing the logic
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.TabExpansion.ScriptContainer.LastExecution">
            <summary>
            The last time the scriptblock was called. Must be updated by the scriptblock itself
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.TabExpansion.ScriptContainer.LastDuration">
            <summary>
            The time it took to run the last time
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost">
            <summary>
            Class that handles the static fields supporting the dbatools TabExpansion implementation
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost.Scripts">
            <summary>
            Field containing the scripts that were registered.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost.Cache">
            <summary>
            The cache used by scripts utilizing TabExpansionPlusPlus in dbatools
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost.InstanceAccess">
            <summary>
            List of instances and when they were last accessed
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost.TeppGatherScriptsFast">
            <summary>
            Scripts that build the cache and are suitable for synchronous execution
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost.TeppGatherScriptsSlow">
            <summary>
            Scripts that build the cache and are not suitable for synchronous execution
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost.DbatoolsCommands">
            <summary>
            A list of all commands imported into dbatools
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost.TabCompletionSets">
            <summary>
            List of completion sets that should be processed into Tepp Assignments. Only populate this list on first import.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost.TeppAssignment">
            <summary>
            Maps a TEPP scriptblock to a command and parameter
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost.SetInstance(System.String,System.Object,System.Boolean)">
            <summary>
            Registers a new instance or updates an already existing one. Should only be called from Connect-SqlInstance and Connect-DbaSqlServer
            </summary>
            <param name="InstanceName">Name of the instance connected to</param>
            <param name="Connection">To connection object containing the relevant information for accessing the instance</param>
            <param name="IsSysAdmin">Whether the account connecting to the instnace has SA privileges</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost.GetTeppScript(System.String,System.String)">
            <summary>
            Returns the assigned scriptblock for a given parameter
            </summary>
            <param name="Command">The command that should be completed</param>
            <param name="Parameter">The parameter completion is provided for</param>
            <returns>Either the relevant script container or null</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost.SetTeppScript(System.String,System.String,System.String)">
            <summary>
            Assigns a registered script to the parameter of a command
            </summary>
            <param name="Command">The command for which to complete</param>
            <param name="Parameter">The parameter for which to complete</param>
            <param name="Script">To name of the script with which to complete</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost.AddTabCompletionSet(System.String,System.String,System.String)">
            <summary>
            Adds a completion set to the list of items to process
            </summary>
            <param name="Command">The command to complete for (accepts wildcard matching)</param>
            <param name="Parameter">The parameter to complete for (accepts wildcard matching)</param>
            <param name="Script">The script to register</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost.CalculateTabExpansion">
            <summary>
            Processes the content of TabCompletionSets and Scripts into TappAssignments based on the DbatoolsCommands list.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost.TeppDisabled">
            <summary>
            Whether TEPP in its entirety is disabled
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost.TeppAsyncDisabled">
            <summary>
            Whether asynchronous TEPP updating should be disabled
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost.TeppSyncDisabled">
            <summary>
            Whether synchronous TEPP updating should be disabled
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost.TeppUpdateInterval">
            <summary>
            The interval in which asynchronous TEPP cache updates are performed
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost.TeppUpdateTimeout">
            <summary>
            After this timespan of no requests to a server, the updates to its cache are disabled.
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.TypeConversion.DbaCredentialParameterConverter">
            <summary>
            Converts to and from DbaCredentialparameter
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.TypeConversion.DbaCredentialParameterConverter.CanConvertTo(System.Object,System.Type)">
            <summary>
            Verifies, whether a conversion for the object to the target type is possible
            </summary>
            <param name="SourceValue">The object to convert</param>
            <param name="DestinationType">The type to convert to</param>
            <returns>Whether it's possible, duh!</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.TypeConversion.DbaCredentialParameterConverter.ConvertTo(System.Object,System.Type,System.IFormatProvider,System.Boolean)">
            <summary>
            Converts from DbaCredentialparameter to whatever destination type is attempted
            </summary>
            <param name="sourceValue">The source object. Better be a DbaCredentialparameter!</param>
            <param name="destinationType">Should be a supported destination type</param>
            <param name="formatProvider">Irrelevant</param>
            <param name="ignoreCase">Irrelevant</param>
            <returns>The target content type</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.TypeConversion.DbaCredentialParameterConverter.CanConvertFrom(System.Object,System.Type)">
            <summary>
            Verifies, whether a conversion for the object from the source type to DbaCredentialParameter is possible
            </summary>
            <param name="SourceValue">The object to convert</param>
            <param name="DestinationType">The source type to convert to</param>
            <returns>Whether it's possible, duh!</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.TypeConversion.DbaCredentialParameterConverter.ConvertFrom(System.Object,System.Type,System.IFormatProvider,System.Boolean)">
            <summary>
            Converts a source object to DbaCredentialparameter
            </summary>
            <param name="sourceValue">The source object</param>
            <param name="destinationType">The destination type. Must be DbaCredentialParameter, or red stuff happens</param>
            <param name="formatProvider">Irrelevant</param>
            <param name="ignoreCase">Irrelevant</param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.TypeConversion.DbaCredentialParameterConverter.IsSupportedType(System.Type)">
            <summary>
            Returns, whether a given type is supported for conversion
            </summary>
            <param name="type">The type to validate</param>
            <returns>Whether it's a supported conversion</returns>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Utility.CredentialPrompt">
            <summary>
            Dedicated class to prompt for credentials
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.CredentialPrompt.Name">
            <summary>
            The name of the user to pre-fill the username field
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.CredentialPrompt.Window">
            <summary>
            The window of the prompt that was shown
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.CredentialPrompt.Username">
            <summary>
            The final, resulting username
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.CredentialPrompt.Password">
            <summary>
            The final, result password
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.CredentialPrompt.Remember">
            <summary>
            Whether the password should be remembered
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.CredentialPrompt.Finished">
            <summary>
            Marker indicating that execution has finished
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.CredentialPrompt.Cancelled">
            <summary>
            Whether the windoww was cancelled
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.CredentialPrompt.PromptForCredential">
            <summary>
            Start asking away
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.CredentialPrompt.GetCredential(System.String)">
            <summary>
            Executes a request for credentials on a dedicated STA thread
            </summary>
            <param name="Name">THe name of the user to put in the prompt</param>
            <returns>The result object</returns>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Utility.DbaValidatePatternAttribute">
            <summary>
            Validates that each parameter argument matches the RegexPattern
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.DbaValidatePatternAttribute.RegexPattern">
            <summary>
            Gets the Regex pattern to be used in the validation
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.DbaValidatePatternAttribute.Options">
            <summary>
            Gets or sets the Regex options to be used in the validation
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.DbaValidatePatternAttribute.ErrorMessage">
             <summary>
             Gets or sets the custom error message pattern that is displayed to the user.
            
             The text representation of the object being validated and the validating regex is passed as
             the first and second formatting parameters to the ErrorMessage formatting pattern.
             <example>
             [ValidatePattern("\s+", ErrorMessage="The text '{0}' did not pass validation of regex '{1}'")]
             </example>
             </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaValidatePatternAttribute.ValidateElement(System.Object)">
            <summary>
            Validates that each parameter argument matches the RegexPattern
            </summary>
            <param name="element">object to validate</param>
            <exception cref="T:System.Management.Automation.ValidationMetadataException">if <paramref name="element"/> is not a string
             that matches the pattern
             and for invalid arguments</exception>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaValidatePatternAttribute.#ctor(System.String)">
            <summary>
            Initializes a new instance of the PsfValidatePatternAttribute class
            </summary>
            <param name="regexPattern">Pattern string to match</param>
            <exception cref="T:System.ArgumentException">for invalid arguments</exception>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Utility.DbaValidateScriptAttribute">
            <summary>
            Class for validating against a script block.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.DbaValidateScriptAttribute.ErrorMessage">
             <summary>
             Gets or sets the custom error message that is displayed to the user.
            
             The item being validated and the validating scriptblock is passed as the first and second
             formatting argument.
            
             <example>
             [ValidateScript("$_ % 2", ErrorMessage = "The item '{0}' did not pass validation of script '{1}'")]
             </example>
             </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.DbaValidateScriptAttribute.ScriptBlock">
            <summary>
            Gets the scriptblock to be used in the validation
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaValidateScriptAttribute.ValidateElement(System.Object)">
            <summary>
            Validates that each parameter argument matches the scriptblock
            </summary>
            <param name="element">object to validate</param>
            <exception cref="T:System.Management.Automation.ValidationMetadataException">if <paramref name="element"/> is invalid</exception>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaValidateScriptAttribute.#ctor(System.Management.Automation.ScriptBlock)">
            <summary>
            Initializes a new instance of the ValidateScriptBlockAttribute class
            </summary>
            <param name="scriptBlock">Scriptblock to match</param>
            <exception cref="T:System.ArgumentException">for invalid arguments</exception>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Utility.SizeStyle">
            <summary>
            How size objects should be displayed
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.SizeStyle.Dynamic">
            <summary>
            The size object is styled dependend on the number stored within.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.SizeStyle.Plain">
            <summary>
            The size object is shown as a plain number
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.SizeStyle.Byte">
            <summary>
            The size object is styled as a byte number
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.SizeStyle.Kilobyte">
            <summary>
            The size object is styled as a kilobyte number
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.SizeStyle.Megabyte">
            <summary>
            The size object is styled as a megabyte number
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.SizeStyle.Gigabyte">
            <summary>
            The size object is styled as a Gigabyte number
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.SizeStyle.Terabyte">
            <summary>
            The size object is styled as a Terabyte number
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Utility.DateTimeExtension">
            <summary>
            Extends DateTime
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DateTimeExtension.CompareTo(System.DateTime,Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase)">
            <summary>
            Adds a compareTo method to DateTime to compare with DbaDateTimeBase
            </summary>
            <param name="Base">The extended DateTime object</param>
            <param name="comparedTo">The DbaDateTimeBase to compare with</param>
            <returns></returns>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Utility.DbaDate">
            <summary>
            A dbatools-internal datetime wrapper for neater display
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDate.#ctor(System.DateTime)">
            <summary>
            Constructs a generic timestamp object wrapper from an input timestamp object.
            </summary>
            <param name="Timestamp">The timestamp to wrap</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDate.#ctor(System.String)">
            <summary>
            Parses a string into a datetime object.
            </summary>
            <param name="Time">The time-string to parse</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDate.#ctor(System.Int64)">
            <summary>
            
            </summary>
            <param name="ticks"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDate.#ctor(System.Int64,System.DateTimeKind)">
            <summary>
            
            </summary>
            <param name="ticks"></param>
            <param name="kind"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDate.#ctor(System.Int32,System.Int32,System.Int32)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDate.#ctor(System.Int32,System.Int32,System.Int32,System.Globalization.Calendar)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
            <param name="calendar"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDate.#ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
            <param name="hour"></param>
            <param name="minute"></param>
            <param name="second"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDate.#ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.DateTimeKind)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
            <param name="hour"></param>
            <param name="minute"></param>
            <param name="second"></param>
            <param name="kind"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDate.#ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Globalization.Calendar)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
            <param name="hour"></param>
            <param name="minute"></param>
            <param name="second"></param>
            <param name="calendar"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDate.#ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
            <param name="hour"></param>
            <param name="minute"></param>
            <param name="second"></param>
            <param name="millisecond"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDate.#ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.DateTimeKind)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
            <param name="hour"></param>
            <param name="minute"></param>
            <param name="second"></param>
            <param name="millisecond"></param>
            <param name="kind"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDate.#ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Globalization.Calendar)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
            <param name="hour"></param>
            <param name="minute"></param>
            <param name="second"></param>
            <param name="millisecond"></param>
            <param name="calendar"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDate.#ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Globalization.Calendar,System.DateTimeKind)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
            <param name="hour"></param>
            <param name="minute"></param>
            <param name="second"></param>
            <param name="millisecond"></param>
            <param name="calendar"></param>
            <param name="kind"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDate.ToString">
            <summary>
            Provids the default-formated string, using the defined default formatting.
            </summary>
            <returns>Formatted datetime-string</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDate.op_Implicit(Sqlcollaborative.Dbatools.Utility.DbaDate)~System.DateTime">
            <summary>
            Implicitly convert to DateTime
            </summary>
            <param name="Base">The source object to convert</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDate.op_Implicit(System.DateTime)~Sqlcollaborative.Dbatools.Utility.DbaDate">
            <summary>
            Implicitly convert from DateTime
            </summary>
            <param name="Base">The object to convert</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDate.op_Implicit(Sqlcollaborative.Dbatools.Utility.DbaDate)~Sqlcollaborative.Dbatools.Utility.DbaDateTime">
            <summary>
            Implicitly convert to DbaDate
            </summary>
            <param name="Base">The source object to convert</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDate.op_Implicit(Sqlcollaborative.Dbatools.Utility.DbaDate)~Sqlcollaborative.Dbatools.Utility.DbaTime">
            <summary>
            Implicitly convert to DbaTime
            </summary>
            <param name="Base">The source object to convert</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDate.Generate(System.DateTime)">
            <summary>
            Generates a DbaDate object based off DateTime object. Will be null if Base is the start value (Tickes == 0).
            </summary>
            <param name="Base">The Datetime to base it off</param>
            <returns>The object to generate (or null)</returns>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Utility.DbaDateTime">
            <summary>
            A dbatools-internal datetime wrapper for neater display
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTime.#ctor(System.DateTime)">
            <summary>
            Constructs a generic timestamp object wrapper from an input timestamp object.
            </summary>
            <param name="Timestamp">The timestamp to wrap</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTime.#ctor(System.String)">
            <summary>
            Parses a string into a datetime object.
            </summary>
            <param name="Time">The time-string to parse</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTime.#ctor(System.Int64)">
            <summary>
            
            </summary>
            <param name="ticks"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTime.#ctor(System.Int64,System.DateTimeKind)">
            <summary>
            
            </summary>
            <param name="ticks"></param>
            <param name="kind"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTime.#ctor(System.Int32,System.Int32,System.Int32)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTime.#ctor(System.Int32,System.Int32,System.Int32,System.Globalization.Calendar)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
            <param name="calendar"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTime.#ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
            <param name="hour"></param>
            <param name="minute"></param>
            <param name="second"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTime.#ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.DateTimeKind)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
            <param name="hour"></param>
            <param name="minute"></param>
            <param name="second"></param>
            <param name="kind"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTime.#ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Globalization.Calendar)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
            <param name="hour"></param>
            <param name="minute"></param>
            <param name="second"></param>
            <param name="calendar"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTime.#ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
            <param name="hour"></param>
            <param name="minute"></param>
            <param name="second"></param>
            <param name="millisecond"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTime.#ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.DateTimeKind)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
            <param name="hour"></param>
            <param name="minute"></param>
            <param name="second"></param>
            <param name="millisecond"></param>
            <param name="kind"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTime.#ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Globalization.Calendar)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
            <param name="hour"></param>
            <param name="minute"></param>
            <param name="second"></param>
            <param name="millisecond"></param>
            <param name="calendar"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTime.#ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Globalization.Calendar,System.DateTimeKind)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
            <param name="hour"></param>
            <param name="minute"></param>
            <param name="second"></param>
            <param name="millisecond"></param>
            <param name="calendar"></param>
            <param name="kind"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTime.ToString">
            <summary>
            Provids the default-formated string, using the defined default formatting.
            </summary>
            <returns>Formatted datetime-string</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTime.op_Implicit(Sqlcollaborative.Dbatools.Utility.DbaDateTime)~System.DateTime">
            <summary>
            Implicitly convert to DateTime
            </summary>
            <param name="Base">The source object to convert</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTime.op_Implicit(System.DateTime)~Sqlcollaborative.Dbatools.Utility.DbaDateTime">
            <summary>
            Implicitly convert from DateTime
            </summary>
            <param name="Base">The object to convert</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTime.op_Implicit(Sqlcollaborative.Dbatools.Utility.DbaDateTime)~Sqlcollaborative.Dbatools.Utility.DbaDate">
            <summary>
            Implicitly convert to DbaDate
            </summary>
            <param name="Base">The source object to convert</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTime.op_Implicit(Sqlcollaborative.Dbatools.Utility.DbaDateTime)~Sqlcollaborative.Dbatools.Utility.DbaTime">
            <summary>
            Implicitly convert to DbaTime
            </summary>
            <param name="Base">The source object to convert</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTime.Generate(System.DateTime)">
            <summary>
            Generates a DbaDateTime object based off DateTime object. Will be null if Base is the start value (Tickes == 0).
            </summary>
            <param name="Base">The Datetime to base it off</param>
            <returns>The object to generate (or null)</returns>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase">
            <summary>
            Base class for wrapping around a DateTime object
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase._timestamp">
            <summary>
            The core resource, containing the actual timestamp
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.Date">
            <summary>
            Gets the date component of this instance.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.Day">
            <summary>
            Gets the day of the month represented by this instance.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.DayOfWeek">
            <summary>
            Gets the day of the week represented by this instance.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.DayOfYear">
            <summary>
            Gets the day of the year represented by this instance.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.Hour">
            <summary>
            Gets the hour component of the date represented by this instance.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.Kind">
            <summary>
            Gets a value that indicates whether the time represented by this instance is based on local time, Coordinated Universal Time (UTC), or neither.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.Millisecond">
            <summary>
            Gets the milliseconds component of the date represented by this instance.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.Minute">
            <summary>
            Gets the minute component of the date represented by this instance.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.Month">
            <summary>
            Gets the month component of the date represented by this instance.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.Second">
            <summary>
            Gets the seconds component of the date represented by this instance.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.Ticks">
            <summary>
            Gets the number of ticks that represent the date and time of this instance.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.TimeOfDay">
            <summary>
            Gets the time of day for this instance.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.Year">
            <summary>
            Gets the year component of the date represented by this instance.
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.#ctor">
            <summary>
            Constructor that should never be called, since this class should never be instantiated. It's there for implicit calls on child classes.
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.#ctor(System.DateTime)">
            <summary>
            Constructs a generic timestamp object wrapper from an input timestamp object.
            </summary>
            <param name="Timestamp">The timestamp to wrap</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.#ctor(System.String)">
            <summary>
            Parses a string into a datetime object.
            </summary>
            <param name="Time">The time-string to parse</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.#ctor(System.Int64)">
            <summary>
            
            </summary>
            <param name="ticks"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.#ctor(System.Int64,System.DateTimeKind)">
            <summary>
            
            </summary>
            <param name="ticks"></param>
            <param name="kind"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.#ctor(System.Int32,System.Int32,System.Int32)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.#ctor(System.Int32,System.Int32,System.Int32,System.Globalization.Calendar)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
            <param name="calendar"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.#ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
            <param name="hour"></param>
            <param name="minute"></param>
            <param name="second"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.#ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.DateTimeKind)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
            <param name="hour"></param>
            <param name="minute"></param>
            <param name="second"></param>
            <param name="kind"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.#ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Globalization.Calendar)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
            <param name="hour"></param>
            <param name="minute"></param>
            <param name="second"></param>
            <param name="calendar"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.#ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
            <param name="hour"></param>
            <param name="minute"></param>
            <param name="second"></param>
            <param name="millisecond"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.#ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.DateTimeKind)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
            <param name="hour"></param>
            <param name="minute"></param>
            <param name="second"></param>
            <param name="millisecond"></param>
            <param name="kind"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.#ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Globalization.Calendar)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
            <param name="hour"></param>
            <param name="minute"></param>
            <param name="second"></param>
            <param name="millisecond"></param>
            <param name="calendar"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.#ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Globalization.Calendar,System.DateTimeKind)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
            <param name="hour"></param>
            <param name="minute"></param>
            <param name="second"></param>
            <param name="millisecond"></param>
            <param name="calendar"></param>
            <param name="kind"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.Add(System.TimeSpan)">
            <summary>
            
            </summary>
            <param name="value"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.AddDays(System.Double)">
            <summary>
            
            </summary>
            <param name="value"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.AddHours(System.Double)">
            <summary>
            
            </summary>
            <param name="value"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.AddMilliseconds(System.Double)">
            <summary>
            
            </summary>
            <param name="value"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.AddMinutes(System.Double)">
            <summary>
            
            </summary>
            <param name="value"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.AddMonths(System.Int32)">
            <summary>
            
            </summary>
            <param name="months"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.AddSeconds(System.Double)">
            <summary>
            
            </summary>
            <param name="value"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.AddTicks(System.Int64)">
            <summary>
            
            </summary>
            <param name="value"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.AddYears(System.Int32)">
            <summary>
            
            </summary>
            <param name="value"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.CompareTo(System.Object)">
            <summary>
            
            </summary>
            <param name="value"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.CompareTo(System.DateTime)">
            <summary>
            
            </summary>
            <param name="value"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.Equals(System.Object)">
            <summary>
            
            </summary>
            <param name="value"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.Equals(System.DateTime)">
            <summary>
            
            </summary>
            <param name="value"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.GetDateTimeFormats">
            <summary>
            
            </summary>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.GetDateTimeFormats(System.IFormatProvider)">
            <summary>
            
            </summary>
            <param name="provider"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.GetDateTimeFormats(System.Char)">
            <summary>
            
            </summary>
            <param name="format"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.GetDateTimeFormats(System.Char,System.IFormatProvider)">
            <summary>
            
            </summary>
            <param name="format"></param>
            <param name="provider"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.GetBaseObject">
            <summary>
            Retrieve base DateTime object, this is a wrapper for
            </summary>
            <returns>Base DateTime object</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.GetHashCode">
            <summary>
            
            </summary>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.GetTypeCode">
            <summary>
            
            </summary>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.IsDaylightSavingTime">
            <summary>
            
            </summary>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.Subtract(System.DateTime)">
            <summary>
            
            </summary>
            <param name="value"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.Subtract(System.TimeSpan)">
            <summary>
            
            </summary>
            <param name="value"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.ToBinary">
            <summary>
            
            </summary>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.ToFileTime">
            <summary>
            
            </summary>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.ToFileTimeUtc">
            <summary>
            
            </summary>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.ToLocalTime">
            <summary>
            
            </summary>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.ToLongDateString">
            <summary>
            
            </summary>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.ToLongTimeString">
            <summary>
            
            </summary>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.ToOADate">
            <summary>
            
            </summary>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.ToShortDateString">
            <summary>
            
            </summary>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.ToShortTimeString">
            <summary>
            
            </summary>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.ToString(System.String)">
            <summary>
            
            </summary>
            <param name="format"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.ToString(System.IFormatProvider)">
            <summary>
            
            </summary>
            <param name="provider"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.ToString(System.String,System.IFormatProvider)">
            <summary>
            
            </summary>
            <param name="format"></param>
            <param name="provider"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.ToUniversalTime">
            <summary>
            
            </summary>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.ParseDateTime(System.String)">
            <summary>
            Parses input string into datetime
            </summary>
            <param name="Value">The string to parse</param>
            <returns>The resultant datetime.</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.op_Addition(Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase,System.TimeSpan)">
            <summary>
            
            </summary>
            <param name="Timestamp"></param>
            <param name="Duration"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.op_Subtraction(Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase,System.TimeSpan)">
            <summary>
            
            </summary>
            <param name="Timestamp"></param>
            <param name="Duration"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.op_Equality(Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase,Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase)">
            <summary>
            
            </summary>
            <param name="Timestamp1"></param>
            <param name="Timestamp2"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.op_Inequality(Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase,Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase)">
            <summary>
            
            </summary>
            <param name="Timestamp1"></param>
            <param name="Timestamp2"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.op_GreaterThan(Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase,Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase)">
            <summary>
            
            </summary>
            <param name="Timestamp1"></param>
            <param name="Timestamp2"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.op_LessThan(Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase,Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase)">
            <summary>
            
            </summary>
            <param name="Timestamp1"></param>
            <param name="Timestamp2"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.op_GreaterThanOrEqual(Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase,Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase)">
            <summary>
            
            </summary>
            <param name="Timestamp1"></param>
            <param name="Timestamp2"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.op_LessThanOrEqual(Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase,Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase)">
            <summary>
            
            </summary>
            <param name="Timestamp1"></param>
            <param name="Timestamp2"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.op_Implicit(Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase)~System.DateTime">
            <summary>
            Implicitly convert DbaDateTimeBase to DateTime
            </summary>
            <param name="Base">The source object to convert</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase.op_Implicit(System.DateTime)~Sqlcollaborative.Dbatools.Utility.DbaDateTimeBase">
            <summary>
            Implicitly convert DateTime to DbaDateTimeBase
            </summary>
            <param name="Base">The object to convert</param>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Utility.DbaTime">
            <summary>
            A dbatools-internal datetime wrapper for neater display
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTime.#ctor(System.DateTime)">
            <summary>
            Constructs a generic timestamp object wrapper from an input timestamp object.
            </summary>
            <param name="Timestamp">The timestamp to wrap</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTime.#ctor(System.String)">
            <summary>
            Parses a string into a datetime object.
            </summary>
            <param name="Time">The time-string to parse</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTime.#ctor(System.Int64)">
            <summary>
            
            </summary>
            <param name="ticks"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTime.#ctor(System.Int64,System.DateTimeKind)">
            <summary>
            
            </summary>
            <param name="ticks"></param>
            <param name="kind"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTime.#ctor(System.Int32,System.Int32,System.Int32)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTime.#ctor(System.Int32,System.Int32,System.Int32,System.Globalization.Calendar)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
            <param name="calendar"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTime.#ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
            <param name="hour"></param>
            <param name="minute"></param>
            <param name="second"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTime.#ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.DateTimeKind)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
            <param name="hour"></param>
            <param name="minute"></param>
            <param name="second"></param>
            <param name="kind"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTime.#ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Globalization.Calendar)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
            <param name="hour"></param>
            <param name="minute"></param>
            <param name="second"></param>
            <param name="calendar"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTime.#ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
            <param name="hour"></param>
            <param name="minute"></param>
            <param name="second"></param>
            <param name="millisecond"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTime.#ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.DateTimeKind)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
            <param name="hour"></param>
            <param name="minute"></param>
            <param name="second"></param>
            <param name="millisecond"></param>
            <param name="kind"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTime.#ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Globalization.Calendar)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
            <param name="hour"></param>
            <param name="minute"></param>
            <param name="second"></param>
            <param name="millisecond"></param>
            <param name="calendar"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTime.#ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Globalization.Calendar,System.DateTimeKind)">
            <summary>
            
            </summary>
            <param name="year"></param>
            <param name="month"></param>
            <param name="day"></param>
            <param name="hour"></param>
            <param name="minute"></param>
            <param name="second"></param>
            <param name="millisecond"></param>
            <param name="calendar"></param>
            <param name="kind"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTime.ToString">
            <summary>
            Provids the default-formated string, using the defined default formatting.
            </summary>
            <returns>Formatted datetime-string</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTime.op_Implicit(Sqlcollaborative.Dbatools.Utility.DbaTime)~System.DateTime">
            <summary>
            Implicitly convert to DateTime
            </summary>
            <param name="Base">The source object to convert</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTime.op_Implicit(System.DateTime)~Sqlcollaborative.Dbatools.Utility.DbaTime">
            <summary>
            Implicitly convert from DateTime
            </summary>
            <param name="Base">The object to convert</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTime.op_Implicit(Sqlcollaborative.Dbatools.Utility.DbaTime)~Sqlcollaborative.Dbatools.Utility.DbaDate">
            <summary>
            Implicitly convert to DbaDate
            </summary>
            <param name="Base">The source object to convert</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTime.op_Implicit(Sqlcollaborative.Dbatools.Utility.DbaTime)~Sqlcollaborative.Dbatools.Utility.DbaDateTime">
            <summary>
            Implicitly convert to DbaTime
            </summary>
            <param name="Base">The source object to convert</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTime.op_Implicit(Sqlcollaborative.Dbatools.Utility.DbaTime)~System.String">
            <summary>
            Implicitly convert to string
            </summary>
            <param name="Base">Object to convert</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTime.Generate(System.DateTime)">
            <summary>
            Generates a DbaDateTime object based off DateTime object. Will be null if Base is the start value (Tickes == 0).
            </summary>
            <param name="Base">The Datetime to base it off</param>
            <returns>The object to generate (or null)</returns>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan">
            <summary>
            A wrapper class, encapsuling a regular TimeSpan object. Used to provide custom timespan display.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.Days">
            <summary>
            Gets the days component of the time interval represented by the current TimeSpan structure.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.Hours">
            <summary>
            Gets the hours component of the time interval represented by the current TimeSpan structure.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.Milliseconds">
            <summary>
            Gets the milliseconds component of the time interval represented by the current TimeSpan structure.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.Minutes">
            <summary>
            Gets the minutes component of the time interval represented by the current TimeSpan structure.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.Seconds">
            <summary>
            Gets the seconds component of the time interval represented by the current TimeSpan structure.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.Ticks">
            <summary>
            Gets the number of ticks that represent the value of the current TimeSpan structure.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.TotalDays">
            <summary>
            Gets the value of the current TimeSpan structure expressed in whole and fractional days.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.TotalHours">
            <summary>
            Gets the value of the current TimeSpan structure expressed in whole and fractional hours.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.TotalMilliseconds">
            <summary>
            Gets the value of the current TimeSpan structure expressed in whole and fractional milliseconds.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.TotalMinutes">
            <summary>
            Gets the value of the current TimeSpan structure expressed in whole and fractional minutes.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.TotalSeconds">
            <summary>
            Gets the value of the current TimeSpan structure expressed in whole and fractional seconds.
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.#ctor(System.TimeSpan)">
            <summary>
            
            </summary>
            <param name="Timespan"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.#ctor(System.String)">
            <summary>
            Converts a string into a timespan
            </summary>
            <param name="Timespan">The string to convert</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.#ctor(System.Int64)">
            <summary>
            
            </summary>
            <param name="ticks"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.#ctor(System.Int32,System.Int32,System.Int32)">
            <summary>
            
            </summary>
            <param name="hours"></param>
            <param name="minutes"></param>
            <param name="seconds"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.#ctor(System.Int32,System.Int32,System.Int32,System.Int32)">
            <summary>
            
            </summary>
            <param name="days"></param>
            <param name="hours"></param>
            <param name="minutes"></param>
            <param name="seconds"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.#ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32)">
            <summary>
            
            </summary>
            <param name="days"></param>
            <param name="hours"></param>
            <param name="minutes"></param>
            <param name="seconds"></param>
            <param name="milliseconds"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.ParseTimeSpan(System.String)">
            <summary>
            Parses an input string as timespan
            </summary>
            <param name="Value">The string to interpret</param>
            <returns>The interpreted timespan value</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.Add(System.TimeSpan)">
            <summary>
            
            </summary>
            <param name="ts"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.CompareTo(System.Object)">
            <summary>
            
            </summary>
            <param name="value"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.CompareTo(System.TimeSpan)">
            <summary>
            
            </summary>
            <param name="value"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.CompareTo(Sqlcollaborative.Dbatools.Utility.DbaTimeSpan)">
            <summary>
            
            </summary>
            <param name="value"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.Duration">
            <summary>
            
            </summary>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.Equals(System.Object)">
            <summary>
            
            </summary>
            <param name="value"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.Equals(System.TimeSpan)">
            <summary>
            
            </summary>
            <param name="obj"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.GetBaseObject">
            <summary>
            Returns the wrapped base object
            </summary>
            <returns>The base object</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.GetHashCode">
            <summary>
            
            </summary>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.Negate">
            <summary>
            
            </summary>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.Subtract(System.TimeSpan)">
            <summary>
            
            </summary>
            <param name="ts"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.ToString">
            <summary>
            Returns the default string representation of the TimeSpan object
            </summary>
            <returns>The string representation of the DbaTimeSpan object</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.ToString(System.String)">
            <summary>
            
            </summary>
            <param name="format"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.ToString(System.String,System.IFormatProvider)">
            <summary>
            
            </summary>
            <param name="format"></param>
            <param name="formatProvider"></param>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.op_Implicit(Sqlcollaborative.Dbatools.Utility.DbaTimeSpan)~System.TimeSpan">
            <summary>
            Implicitly converts a DbaTimeSpan object into a TimeSpan object
            </summary>
            <param name="Base">The original object to revert</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTimeSpan.op_Implicit(System.TimeSpan)~Sqlcollaborative.Dbatools.Utility.DbaTimeSpan">
            <summary>
            Implicitly converts a TimeSpan object into a DbaTimeSpan object
            </summary>
            <param name="Base">The original object to wrap</param>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Utility.DbaTimeSpanPretty">
            <summary>
            Makes timespan great again
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTimeSpanPretty.FromMilliseconds(System.Double)">
            <summary>
            Creates a new, pretty timespan object from milliseconds
            </summary>
            <param name="Milliseconds">The milliseconds to convert from.</param>
            <returns>A pretty timespan object</returns>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.DbaTimeSpanPretty.Digits">
            <summary>
            The number of digits a pretty timespan should round to.
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTimeSpanPretty.#ctor(System.TimeSpan)">
            <summary>
            
            </summary>
            <param name="Timespan"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTimeSpanPretty.#ctor(System.String)">
            <summary>
            Converts a string into a timespan
            </summary>
            <param name="Timespan">The string to convert</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTimeSpanPretty.#ctor(System.Int64)">
            <summary>
            
            </summary>
            <param name="ticks"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTimeSpanPretty.#ctor(System.Int32,System.Int32,System.Int32)">
            <summary>
            
            </summary>
            <param name="hours"></param>
            <param name="minutes"></param>
            <param name="seconds"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTimeSpanPretty.#ctor(System.Int32,System.Int32,System.Int32,System.Int32)">
            <summary>
            
            </summary>
            <param name="days"></param>
            <param name="hours"></param>
            <param name="minutes"></param>
            <param name="seconds"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTimeSpanPretty.#ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32)">
            <summary>
            
            </summary>
            <param name="days"></param>
            <param name="hours"></param>
            <param name="minutes"></param>
            <param name="seconds"></param>
            <param name="milliseconds"></param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.DbaTimeSpanPretty.ToString">
            <summary>
            Creates extra-nice timespan formats
            </summary>
            <returns>Humanly readable timespans</returns>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Utility.RegexHelper">
            <summary>
            Static class that holds useful regex patterns, ready for use
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.RegexHelper.HostName">
            <summary>
            Pattern that checks for a valid hostname
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.RegexHelper.HostNameEx">
            <summary>
            Pattern that checks for valid hostnames within a larger text
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.RegexHelper.IPv4">
            <summary>
            Pattern that checks for a valid IPv4 address
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.RegexHelper.IPv4Ex">
            <summary>
            Pattern that checks for valid IPv4 addresses within a larger text
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.RegexHelper.IPv6">
            <summary>
            Will match a valid IPv6 address
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.RegexHelper.IPv6Ex">
            <summary>
            Will match any IPv6 address within a larger text
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.RegexHelper.ComputerTarget">
            <summary>
            Will match any string that in its entirety represents a valid target for dns- or ip-based targeting. Combination of HostName, IPv4 and IPv6
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.RegexHelper.Guid">
            <summary>
            Will match a valid Guid
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.RegexHelper.GuidEx">
            <summary>
            Will match any number of valid Guids in a larger text
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.RegexHelper.InstanceName">
            <summary>
            Will match a mostly valid instance name.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.RegexHelper.InstanceNameEx">
            <summary>
            Will match any instance of a mostly valid instance name.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.RegexHelper.SqlReservedKeyword">
            <summary>
            Matches a word against the list of officially reserved keywords
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.RegexHelper.SqlReservedKeywordEx">
            <summary>
            Will match any reserved keyword in a larger text
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.RegexHelper.SqlReservedKeywordOdbc">
            <summary>
            Matches a word against the list of officially reserved keywords for odbc
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.RegexHelper.SqlReservedKeywordOdbcEx">
            <summary>
            Will match any reserved odbc-keyword in a larger text
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.RegexHelper.SqlReservedKeywordFuture">
            <summary>
            Matches a word against the list of keywords that are likely to become reserved in the future
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.RegexHelper.SqlReservedKeywordFutureEx">
            <summary>
            Will match against the list of keywords that are likely to become reserved in the future and are used in a larger text
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Utility.Size">
            <summary>
            Class that reports File size.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.Size.Byte">
            <summary>
            Number of bytes contained in whatever object uses this object as a property
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.Size.Kilobyte">
            <summary>
            Kilobyte representation of the bytes
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.Size.Megabyte">
            <summary>
            Megabyte representation of the bytes
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.Size.Gigabyte">
            <summary>
            Gigabyte representation of the bytes
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.Size.Terabyte">
            <summary>
            Terabyte representation of the bytes
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.Size.Digits">
            <summary>
            Number if digits behind the dot.
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.Size.Style">
            <summary>
            How the size object should be displayed.
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.Size.ToString">
            <summary>
            Shows the default string representation of size
            </summary>
            <returns></returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.Size.Equals(System.Object)">
            <summary>
            Simple equality test
            </summary>
            <param name="obj">The object to test it against</param>
            <returns>True if equal, false elsewise</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.Size.GetHashCode">
            <inheritdoc cref="M:System.Int64.GetHashCode"/>
            <remarks>The hashcode of the underlying size</remarks>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.Size.#ctor">
            <summary>
            Creates an empty size.
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.Size.#ctor(System.Int64)">
            <summary>
            Creates a size with some content
            </summary>
            <param name="Byte">The length in bytes to set the size to</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.Size.CompareTo(Sqlcollaborative.Dbatools.Utility.Size)">
            <inheritdoc cref="M:System.IComparable`1.CompareTo(`0)"/>
            <remarks>For sorting</remarks>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.Size.CompareTo(System.Object)">
            <inheritdoc cref="M:System.IComparable.CompareTo(System.Object)"/>
            <remarks>For sorting</remarks>
            <exception cref="T:System.ArgumentException">If you compare with something invalid.</exception>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.Size.op_Addition(Sqlcollaborative.Dbatools.Utility.Size,Sqlcollaborative.Dbatools.Utility.Size)">
            <summary>
            Adds two sizes
            </summary>
            <param name="a">The first size to add</param>
            <param name="b">The second size to add</param>
            <returns>The sum of both sizes</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.Size.op_Subtraction(Sqlcollaborative.Dbatools.Utility.Size,Sqlcollaborative.Dbatools.Utility.Size)">
            <summary>
            Substracts two sizes
            </summary>
            <param name="a">The first size to substract</param>
            <param name="b">The second size to substract</param>
            <returns>The difference between both sizes</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.Size.op_Multiply(Sqlcollaborative.Dbatools.Utility.Size,System.Double)">
            <summary>
            Multiplies two sizes with each other
            </summary>
            <param name="a">The size to multiply</param>
            <param name="b">The size to multiply with</param>
            <returns>A multiplied size.</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.Size.op_Division(Sqlcollaborative.Dbatools.Utility.Size,System.Double)">
            <summary>
            Divides one size by another. 
            </summary>
            <param name="a">The size to divide</param>
            <param name="b">The size to divide with</param>
            <returns>Divided size (note: Cut off)</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.Size.op_Multiply(Sqlcollaborative.Dbatools.Utility.Size,Sqlcollaborative.Dbatools.Utility.Size)">
            <summary>
            Multiplies two sizes with each other
            </summary>
            <param name="a">The size to multiply</param>
            <param name="b">The size to multiply with</param>
            <returns>A multiplied size.</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.Size.op_Division(Sqlcollaborative.Dbatools.Utility.Size,Sqlcollaborative.Dbatools.Utility.Size)">
            <summary>
            Divides one size by another.
            </summary>
            <param name="a">The size to divide</param>
            <param name="b">The size to divide with</param>
            <returns>Divided size (note: Cut off)</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.Size.op_Implicit(System.Int32)~Sqlcollaborative.Dbatools.Utility.Size">
            <summary>
            Implicitly converts int to size
            </summary>
            <param name="a">The number to convert</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.Size.op_Implicit(System.Decimal)~Sqlcollaborative.Dbatools.Utility.Size">
            <summary>
            Implicitly converts int to size
            </summary>
            <param name="a">The number to convert</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.Size.op_Implicit(Sqlcollaborative.Dbatools.Utility.Size)~System.Int32">
            <summary>
            Implicitly converts size to int
            </summary>
            <param name="a">The size to convert</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.Size.op_Implicit(System.Int64)~Sqlcollaborative.Dbatools.Utility.Size">
            <summary>
            Implicitly converts long to size
            </summary>
            <param name="a">The number to convert</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.Size.op_Implicit(Sqlcollaborative.Dbatools.Utility.Size)~System.Int64">
            <summary>
            Implicitly converts size to long
            </summary>
            <param name="a">The size to convert</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.Size.op_Implicit(System.String)~Sqlcollaborative.Dbatools.Utility.Size">
            <summary>
            Implicitly converts string to size
            </summary>
            <param name="a">The string to convert</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.Size.op_Implicit(System.Double)~Sqlcollaborative.Dbatools.Utility.Size">
            <summary>
            Implicitly converts double to size
            </summary>
            <param name="a">The number to convert</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.Size.op_Implicit(Sqlcollaborative.Dbatools.Utility.Size)~System.Double">
            <summary>
            Implicitly converts size to double
            </summary>
            <param name="a">The size to convert</param>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Utility.UtilityHost">
            <summary>
            Provides static resources to utility-namespaced stuff
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.UtilityHost.DisableCustomDateTime">
            <summary>
            Restores all DateTime objects to their default display behavior
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.UtilityHost.DisableCustomTimeSpan">
            <summary>
            Restores all timespan objects to their default display behavior.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.UtilityHost.FormatDate">
            <summary>
            Formating string for date-style datetime objects.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.UtilityHost.FormatDateTime">
            <summary>
            Formating string for datetime-style datetime objects
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.UtilityHost.FormatTime">
            <summary>
            Formating string for time-style datetime objects
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.UtilityHost.SizeDigits">
            <summary>
            The number of digits a size object shows by default
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Utility.UtilityHost.SizeStyle">
            <summary>
            The way size objects are usually displayed
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.UtilityHost.IsLike(System.String,System.String,System.Boolean)">
            <summary>
            Implement's VB's Like operator logic.
            </summary>
            <param name="CaseSensitive">Whether the comparison is case sensitive</param>
            <param name="Pattern">The pattern the string is compared with</param>
            <param name="String">The string that is being compared with a pattern</param>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.UtilityHost.CharListToSet(System.String)">
            <summary>
            Converts a string of characters to a HashSet of characters. If the string
            contains character ranges, such as A-Z, all characters in the range are
            also added to the returned set of characters.
            </summary>
            <param name="charList">Character list string</param>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.UtilityHost.Callstack">
            <summary>
            Returns the current callstack
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.UtilityHost._CallstackNew">
            <summary>
            Returns the current callstack on PS4+
            </summary>
        </member>
        <member name="P:Sqlcollaborative.Dbatools.Utility.UtilityHost._CallstackOld">
            <summary>
            Returns the current callstack on PS3
            </summary>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Utility.Validation">
            <summary>
            Provides helper methods that aid in validating stuff.
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.Validation.IsLocalhost(System.String)">
            <summary>
            Tests whether a given string is the local host.
            Does NOT use DNS resolution, DNS aliases will NOT be recognized!
            </summary>
            <param name="Name">The name to test for being local host</param>
            <returns>Whether the name is localhost</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.Validation.IsRecommendedInstanceName(System.String)">
            <summary>
            Tests whether a given string is a recommended instance name. Recommended names musst be legal, nbot on the ODBC list and not on the list of words likely to become reserved keywords in the future.
            </summary>
            <param name="InstanceName">The name to test. MAY contain server name, but will NOT test the server.</param>
            <returns>Whether the name is recommended</returns>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.Validation.IsValidComputerTarget(System.String)">
            <summary>
            Tests whether a given string is a valid target for targeting as a computer. Will first convert from idn name.
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Utility.Validation.IsValidInstanceName(System.String,System.Boolean)">
            <summary>
            Tests whether a given string is a valid instance name.
            </summary>
            <param name="InstanceName">The name to test. MAY contain server name, but will NOT test the server.</param>
            <param name="Lenient">Setting this to true will make the validation ignore default and mssqlserver as illegal names (as they are illegal names for named instances, but legal for targeting)</param>
            <returns>Whether the name is legal</returns>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.Validation.LinkedServerResult">
            <summary>
            The results of testing linked server connectivity as seen from the server that was linked to.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Validation.LinkedServerResult.ComputerName">
            <summary>
            The name of the server running the tests
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Validation.LinkedServerResult.InstanceName">
            <summary>
            The name of the instance running the tests
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Validation.LinkedServerResult.SqlInstance">
            <summary>
            The full name of the instance running the tests
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Validation.LinkedServerResult.LinkedServerName">
            <summary>
            The name of the linked server, the connectivity with whom was tested
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Validation.LinkedServerResult.RemoteServer">
            <summary>
            The name of the remote computer running the linked server.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Validation.LinkedServerResult.Connectivity">
            <summary>
            The test result
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.Validation.LinkedServerResult.Result">
            <summary>
            Text interpretation of the result. Contains error messages if the test failed.
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Validation.LinkedServerResult.#ctor">
            <summary>
            Creates an empty object
            </summary>
        </member>
        <member name="M:Sqlcollaborative.Dbatools.Validation.LinkedServerResult.#ctor(System.String,System.String,System.String,System.String,System.String,System.Boolean,System.String)">
            <summary>
            Creates a test result with prefilled values
            </summary>
            <param name="ComputerName">The name of the server running the tests</param>
            <param name="InstanceName">The name of the instance running the tests</param>
            <param name="SqlInstance">The full name of the instance running the tests</param>
            <param name="LinkedServerName">The name of the linked server, the connectivity with whom was tested</param>
            <param name="RemoteServer">The name of the remote computer running the linked server.</param>
            <param name="Connectivity">The test result</param>
            <param name="Result">Text interpretation of the result. Contains error messages if the test failed.</param>
        </member>
        <member name="T:Sqlcollaborative.Dbatools.General.ExecutionMode">
            <summary>
            What kind of mode do you want to run a command in?
            This allows the user to choose how a dbatools function handles a bump in the execution where terminating directly may not be actually mandated.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.General.ExecutionMode.Strict">
            <summary>
            When encountering issues, terminate, or skip the currently processed input, rather than continue.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.General.ExecutionMode.Lazy">
            <summary>
            Continue as able with a best-effort attempt. Simple verbose output should do the rest.
            </summary>
        </member>
        <member name="F:Sqlcollaborative.Dbatools.General.ExecutionMode.Report">
            <summary>
            Continue, but provide output that can be used to identify the operations that had issues.
            </summary>
        </member>
    </members>
</doc>
tools\dbatools\bin\diagnosticquery\SQLServerDiagnosticQueries_2005_201806.sql

-- SQL Server 2005 Diagnostic Information Queries
-- Glenn Berry 
-- Last Modified: July 5, 2018
-- https://www.sqlserverperformance.wordpress.com/
-- https://www.sqlskills.com/blogs/glenn/
-- Twitter: GlennAlanBerry

-- Please listen to my Pluralsight courses
-- https://www.pluralsight.com/author/glenn-berry

-- If you want to find all of our SQLskills SQL101 blog posts, check out https://www.sqlskills.com/help/sql101/

-- Many of these queries will not work if you have databases in 80 compatibility mode
-- Please make sure you are using the correct version of these diagnostic queries for your version of SQL Server

--******************************************************************************
--*   Copyright (C) 2018 Glenn Berry, SQLskills.com
--*   All rights reserved. 
--*
--*   For more scripts and sample code, check out 
--*      https://www.sqlskills.com/blogs/glenn
--*
--*   You may alter this code for your own *non-commercial* purposes. You may
--*   republish altered code as long as you include this copyright and give due credit. 
--*
--*
--*   THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF 
--*   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED 
--*   TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
--*   PARTICULAR PURPOSE. 
--*
--******************************************************************************

-- Check the major product version to see if it is SQL Server 2005
IF NOT EXISTS (SELECT * WHERE CONVERT(varchar(128), SERVERPROPERTY('ProductVersion')) LIKE '9%')
	BEGIN
		DECLARE @ProductVersion varchar(128); 
		SET @ProductVersion = CONVERT(varchar(128), SERVERPROPERTY('ProductVersion'));
		RAISERROR ('Script does not match the ProductVersion [%s] of this instance. Many of these queries may not work on this version.' , 18 , 16 , @ProductVersion);
	END
	ELSE
		PRINT N'You have the correct major version of SQL Server for this diagnostic information script';


-- SQL Version information for current instance  (Query 1) (Version Info)
SELECT @@SERVERNAME AS [Server Name], @@VERSION AS [SQL Server and OS Version Info];
------

-- SQL Server 2005 is out of both mainstream and extended support from Microsoft
-- Build 9.0.5266 was the last cumulative update


--   SQL 2005 SP2 Builds             SQL 2005 SP3 Builds            SQL 2005 SP4 Builds
-- Build          Description         Build         Description     Build		   Description
-- 9.0.3042        SP2 RTM			  9.0.4035        SP3 RTM
-- 9.0.3161        SP2 CU1			  9.0.4207        SP3 CU1
-- 9.0.3175        SP2 CU2			  9.0.4211        SP3 CU2 
-- 9.0.3186        SP2 CU3			  9.0.4220       SP3 CU3         
-- 9.0.3200        SP2 CU4			  9.0.4226        SP3 CU4         
-- 9.0.3215        SP2 CU5			  9.0.4230		  SP3 CU5          
-- 9.0.3228		   SP2 CU6			  9.0.4266        SP3 CU6        
-- 9.0.3239        SP2 CU7			  9.0.4273		  SP3 CU7        
-- 9.0.3257        SP2 CU8			  9.0.4285        SP3 CU8
-- 9.0.3282        SP2 CU9			  9.0.4294		  SP3 CU9
-- 9.0.3294		   SP2 CU10			  9.0.4305        SP3 CU10
-- 9.0.3301		   SP2 CU11			  9.0.4309		  SP3 CU11  ---> 9.0.5000		SP4 RTM
-- 9.0.3315        SP2 CU12           9.0.4311        SP3 CU12  ---> 9.0.5254       SP4 CU1		12/22/2010
-- 9.0.3325		   SP2 CU13			  9.0.4315		  SP3 CU13
-- 9.0.3328        SP2 CU14			  9.0.4317		  SP3 CU14	---> 9.0.5259		SP4 CU2		 2/21/2011
-- 9.0.3330        SP2 CU15			  9.0.4325		  SP3 CU15	---> 9.0.5266		SP4 CU3      3/21/2011
-- 9.0.3355        SP2 CU16
-- 9.0.3356		   SP2 CU17



-- The SQL Server 2005 builds that were released after SQL Server 2005 Service Pack 2 was released
-- http://support.microsoft.com/kb/937137

-- The SQL Server 2005 builds that were released after SQL Server 2005 Service Pack 3 was released
-- http://support.microsoft.com/kb/960598

-- The SQL Server 2005 builds that were released after SQL Server 2005 Service Pack 4 was released 
-- http://support.microsoft.com/kb/2485757

-- SQL Server 2005 fell out of Mainsteam Support on April 12, 2011
-- This means no more Service Packs or Cumulative Updates
-- SQL Server 2005 ended Extended Support on April 12, 2016 

-- SQL Server 2005 Service Pack 4
-- http://www.microsoft.com/en-us/download/details.aspx?id=7218



-- When was SQL Server installed  (Query 2) (SQL Server Install Date)  
SELECT @@SERVERNAME AS [Server Name], create_date AS [SQL Server Install Date] 
FROM sys.server_principals WITH (NOLOCK)
WHERE name = N'NT AUTHORITY\SYSTEM'
OR name = N'NT AUTHORITY\NETWORK SERVICE' OPTION (RECOMPILE);
------

-- Tells you the date and time that SQL Server was installed
-- It is a good idea to know how old your instance is



-- Get selected server properties (Query 3) (Server Properties)
SELECT SERVERPROPERTY('MachineName') AS [MachineName], SERVERPROPERTY('ServerName') AS [ServerName],  
SERVERPROPERTY('InstanceName') AS [Instance], SERVERPROPERTY('IsClustered') AS [IsClustered], 
SERVERPROPERTY('ComputerNamePhysicalNetBIOS') AS [ComputerNamePhysicalNetBIOS], 
SERVERPROPERTY('Edition') AS [Edition], SERVERPROPERTY('ProductLevel') AS [ProductLevel], 
SERVERPROPERTY('ProductVersion') AS [ProductVersion], SERVERPROPERTY('ProcessID') AS [ProcessID],
SERVERPROPERTY('Collation') AS [Collation], SERVERPROPERTY('IsFullTextInstalled') AS [IsFullTextInstalled], 
SERVERPROPERTY('IsIntegratedSecurityOnly') AS [IsIntegratedSecurityOnly];
------

-- This gives you a lot of useful information about your instance of SQL Server,
-- such as the ProcessID for SQL Server and your collation


-- Get SQL Server Agent jobs and Category information (Query 4) (SQL Server Agent Jobs)
SELECT sj.name AS [Job Name], sj.[description] AS [Job Description], SUSER_SNAME(sj.owner_sid) AS [Job Owner],
sj.date_created AS [Date Created], sj.[enabled] AS [Job Enabled], 
sj.notify_email_operator_id, sj.notify_level_email, sc.name AS [CategoryName],
s.[enabled] AS [Sched Enabled], js.next_run_date, js.next_run_time
FROM msdb.dbo.sysjobs AS sj WITH (NOLOCK)
INNER JOIN msdb.dbo.syscategories AS sc WITH (NOLOCK)
ON sj.category_id = sc.category_id
LEFT OUTER JOIN msdb.dbo.sysjobschedules AS js WITH (NOLOCK)
ON sj.job_id = js.job_id
LEFT OUTER JOIN msdb.dbo.sysschedules AS s WITH (NOLOCK)
ON js.schedule_id = s.schedule_id
ORDER BY sj.name OPTION (RECOMPILE);
------

-- Gives you some basic information about your SQL Server Agent jobs, who owns them and how they are configured
-- Look for Agent jobs that are not owned by sa
-- Look for jobs that have a notify_email_operator_id set to 0 (meaning no operator)
-- Look for jobs that have a notify_level_email set to 0 (meaning no e-mail is ever sent)
--
-- MSDN sysjobs documentation
-- http://msdn.microsoft.com/en-us/library/ms189817.aspx


-- Get SQL Server Agent Alert Information (Query 5) (SQL Server Agent Alerts)
SELECT name, event_source, message_id, severity, [enabled], has_notification, 
       delay_between_responses, occurrence_count, last_occurrence_date, last_occurrence_time
FROM msdb.dbo.sysalerts WITH (NOLOCK)
ORDER BY name OPTION (RECOMPILE);
------

-- Gives you some basic information about your SQL Server Agent Alerts (which are different from SQL Server Agent jobs)
-- Read more about Agent Alerts here: https://www.sqlskills.com/blogs/glenn/creating-sql-server-agent-alerts-for-critical-errors/


-- Returns a list of all global trace flags that are enabled (Query 6) (Global Trace Flags)
DBCC TRACESTATUS (-1);
------
 
-- If no global trace flags are enabled, no results will be returned.
-- It is very useful to know what global trace flags are currently enabled as part of the diagnostic process.

-- Common trace flags that should be enabled in most cases
-- TF 1117 - When growing a data file, grow all files at the same time so they remain the same size, reducing allocation contention points
--           http://support2.microsoft.com/kb/2154845
-- 
-- TF 1118 - Helps alleviate allocation contention in tempdb, SQL Server allocates full extents to each database object, 
--           thereby eliminating the contention on SGAM pages (more important with older versions of SQL Server)
--           Recommendations to reduce allocation contention in SQL Server tempdb database
--           http://support2.microsoft.com/kb/2154845

-- TF 3226 - Supresses logging of successful database backup messages to the SQL Server Error Log
--           https://www.sqlskills.com/blogs/paul/fed-up-with-backup-success-messages-bloating-your-error-logs/


-- Hardware Information from SQL Server 2005  (Query 7) (Hardware Info)
-- (Cannot distinguish between HT and multi-core)
SELECT cpu_count AS [Logical CPU Count], hyperthread_ratio AS [Hyperthread Ratio],
cpu_count/hyperthread_ratio AS [Physical CPU Count], 
physical_memory_in_bytes/1048576 AS [Physical Memory (MB)]
FROM sys.dm_os_sys_info WITH (NOLOCK) OPTION (RECOMPILE);
------

-- Gives you some good basic hardware information about your database server


-- Get System Manufacturer and model number from (Query 8) (System Manufacturer)
-- SQL Server Error log. This query might take a few seconds 
-- if you have not recycled your error log recently  
EXEC sys.xp_readerrorlog 0, 1, N'Manufacturer';
------  

-- This can help you determine the capabilities
-- and capacities of your database server
-- This often comes back with no results on SQL Server 2005


-- Get processor description from Windows Registry  (Query 9) (Processor Description)
EXEC sys.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'HARDWARE\DESCRIPTION\System\CentralProcessor\0', N'ProcessorNameString';
------

-- Gives you the model number and rated clock speed of your processor(s)
-- Your processors may be running at less that the rated clock speed due
-- to the Windows Power Plan or hardware power management

-- You can use CPU-Z to get your actual CPU core speed and a lot of other useful information
-- http://www.cpuid.com/softwares/cpu-z.html

-- You can learn more about processor selection for SQL Server by following this link
-- https://www.sqlskills.com/blogs/glenn/processor-selection-for-sql-server/



-- Get configuration values for instance  (Query 10) (Configuration Values)
SELECT name, value, value_in_use, minimum, maximum, [description], is_dynamic, is_advanced
FROM sys.configurations WITH (NOLOCK)
ORDER BY name OPTION (RECOMPILE);
------

-- Focus on these settings:)
-- backup compression default (should be 1 in most cases)
-- clr enabled (only enable if it is needed)
-- cost threshold for parallelism (depends on your workload)
-- lightweight pooling (should be zero)
-- max degree of parallelism (depends on your workload)
-- max server memory (MB) (set to an appropriate value, not the default)
-- priority boost (should be zero)
-- remote admin connections (should be 1)


-- File names and paths for all user and system databases on instance  (Query 11) (Database Filenames and Paths)
SELECT DB_NAME([database_id]) AS [Database Name], 
       [file_id], [name], physical_name, [type_desc], state_desc,
	   is_percent_growth, growth,
	   CONVERT(bigint, growth/128.0) AS [Growth in MB], 
       CONVERT(bigint, size/128.0) AS [Total Size in MB]
FROM sys.master_files WITH (NOLOCK)
ORDER BY DB_NAME([database_id]), [file_id] OPTION (RECOMPILE);
------

-- Things to look at:
-- Are data files and log files on different drives?
-- Is everything on the C: drive?
-- Is TempDB on dedicated drives?
-- Is there only one TempDB data file?
-- Are all of the TempDB data files the same size?
-- Are there multiple data files for user databases?
-- Is percent growth enabled for any files (which is bad)?


-- Look for I/O requests taking longer than 15 seconds in the five most recent SQL Server Error Logs (Query 12) (IO Warnings)
CREATE TABLE #IOWarningResults(LogDate datetime, ProcessInfo sysname, LogText nvarchar(1000));

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 0, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 1, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 2, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 3, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 4, 1, N'taking longer than 15 seconds';

SELECT LogDate, ProcessInfo, LogText
FROM #IOWarningResults
ORDER BY LogDate DESC;

DROP TABLE #IOWarningResults; 
------ 

-- Finding 15 second I/O warnings in the SQL Server Error Log is useful evidence of
-- poor I/O performance (which might have many different causes)
-- Look to see if you see any patterns in the results (same files, same drives, same time of day, etc.)

-- Diagnostics in SQL Server help detect stalled and stuck I/O operations
-- https://support.microsoft.com/en-us/kb/897284


-- Drive level latency information (Query 13) (Drive Level Latency)
-- Based on code from Jimmy May
SELECT tab.[Drive], tab.volume_mount_point AS [Volume Mount Point], 
	CASE 
		WHEN num_of_reads = 0 THEN 0 
		ELSE (io_stall_read_ms/num_of_reads) 
	END AS [Read Latency],
	CASE 
		WHEN num_of_writes = 0 THEN 0 
		ELSE (io_stall_write_ms/num_of_writes) 
	END AS [Write Latency],
	CASE 
		WHEN (num_of_reads = 0 AND num_of_writes = 0) THEN 0 
		ELSE (io_stall/(num_of_reads + num_of_writes)) 
	END AS [Overall Latency],
	CASE 
		WHEN num_of_reads = 0 THEN 0 
		ELSE (num_of_bytes_read/num_of_reads) 
	END AS [Avg Bytes/Read],
	CASE 
		WHEN num_of_writes = 0 THEN 0 
		ELSE (num_of_bytes_written/num_of_writes) 
	END AS [Avg Bytes/Write],
	CASE 
		WHEN (num_of_reads = 0 AND num_of_writes = 0) THEN 0 
		ELSE ((num_of_bytes_read + num_of_bytes_written)/(num_of_reads + num_of_writes)) 
	END AS [Avg Bytes/Transfer]
FROM (SELECT LEFT(UPPER(mf.physical_name), 2) AS Drive, SUM(num_of_reads) AS num_of_reads,
	         SUM(io_stall_read_ms) AS io_stall_read_ms, SUM(num_of_writes) AS num_of_writes,
	         SUM(io_stall_write_ms) AS io_stall_write_ms, SUM(num_of_bytes_read) AS num_of_bytes_read,
	         SUM(num_of_bytes_written) AS num_of_bytes_written, SUM(io_stall) AS io_stall, vs.volume_mount_point 
      FROM sys.dm_io_virtual_file_stats(NULL, NULL) AS vfs
      INNER JOIN sys.master_files AS mf WITH (NOLOCK)
      ON vfs.database_id = mf.database_id AND vfs.file_id = mf.file_id
	  CROSS APPLY sys.dm_os_volume_stats(mf.database_id, mf.[file_id]) AS vs 
      GROUP BY LEFT(UPPER(mf.physical_name), 2), vs.volume_mount_point) AS tab
ORDER BY [Overall Latency] OPTION (RECOMPILE);
------

-- Shows you the drive-level latency for reads and writes, in milliseconds
-- Latency above 20-25ms is usually a problem


-- Calculates average stalls per read, per write, and per total input/output for each database file  (Query 14) (IO Stalls by File)
SELECT DB_NAME(fs.database_id) AS [Database Name], CAST(fs.io_stall_read_ms/(1.0 + fs.num_of_reads) AS NUMERIC(10,1)) AS [avg_read_stall_ms],
CAST(fs.io_stall_write_ms/(1.0 + fs.num_of_writes) AS NUMERIC(10,1)) AS [avg_write_stall_ms],
CAST((fs.io_stall_read_ms + fs.io_stall_write_ms)/(1.0 + fs.num_of_reads + fs.num_of_writes) AS NUMERIC(10,1)) AS [avg_io_stall_ms],
CONVERT(DECIMAL(18,2), mf.size/128.0) AS [File Size (MB)], mf.physical_name, mf.type_desc, fs.io_stall_read_ms, fs.num_of_reads, 
fs.io_stall_write_ms, fs.num_of_writes, fs.io_stall_read_ms + fs.io_stall_write_ms AS [io_stalls], fs.num_of_reads + fs.num_of_writes AS [total_io]
FROM sys.dm_io_virtual_file_stats(null,null) AS fs
INNER JOIN sys.master_files AS mf WITH (NOLOCK)
ON fs.database_id = mf.database_id
AND fs.[file_id] = mf.[file_id]
ORDER BY avg_io_stall_ms DESC OPTION (RECOMPILE);
------

-- Helps determine which database files on the entire instance have the most I/O bottlenecks
-- This can help you decide whether certain LUNs are overloaded and whether you might
-- want to move some files to a different location or perhaps improve your I/O performance


-- Recovery model, log reuse wait description, log file size, log usage size (Query 15) (Database Properties)
-- and compatibility level for all databases on instance
SELECT db.[name] AS [Database Name], SUSER_SNAME(db.owner_sid) AS [Database Owner], db.recovery_model_desc AS [Recovery Model], 
db.log_reuse_wait_desc AS [Log Reuse Wait Description], 
ls.cntr_value AS [Log Size (KB)], lu.cntr_value AS [Log Used (KB)],
CAST(CAST(lu.cntr_value AS FLOAT) / CAST(ls.cntr_value AS FLOAT)AS DECIMAL(18,2)) * 100 AS [Log Used %], 
db.[compatibility_level] AS [DB Compatibility Level], 
db.page_verify_option_desc AS [Page Verify Option], db.is_auto_create_stats_on, db.is_auto_update_stats_on,
db.is_auto_update_stats_async_on, db.is_parameterization_forced, 
db.snapshot_isolation_state_desc, db.is_read_committed_snapshot_on,
db.is_auto_close_on, db.is_auto_shrink_on, db.is_published
FROM sys.databases AS db WITH (NOLOCK)
INNER JOIN sys.dm_os_performance_counters AS lu WITH (NOLOCK)
ON db.name = lu.instance_name
INNER JOIN sys.dm_os_performance_counters AS ls WITH (NOLOCK)
ON db.name = ls.instance_name
WHERE lu.counter_name LIKE N'Log File(s) Used Size (KB)%' 
AND ls.counter_name LIKE N'Log File(s) Size (KB)%'
AND ls.cntr_value > 0 OPTION (RECOMPILE);
------

-- Things to look at:
-- How many databases are on the instance?
-- What recovery models are they using?
-- What is the log reuse wait description?
-- How full are the transaction logs ?
-- What compatibility level are the databases on? 
-- What is the Page Verify Option? (should be CHECKSUM)
-- Is Auto Update Statistics Asynchronously enabled?
-- Make sure auto_shrink and auto_close are not enabled!



-- Missing Indexes for all databases by Index Advantage  (Query 16) (Missing Indexes All Databases)
SELECT CONVERT(decimal(18,2), user_seeks * avg_total_user_cost * (avg_user_impact * 0.01)) AS [index_advantage], 
migs.last_user_seek, mid.[statement] AS [Database.Schema.Table],
mid.equality_columns, mid.inequality_columns, mid.included_columns,
migs.unique_compiles, migs.user_seeks, migs.avg_total_user_cost, migs.avg_user_impact
FROM sys.dm_db_missing_index_group_stats AS migs WITH (NOLOCK)
INNER JOIN sys.dm_db_missing_index_groups AS mig WITH (NOLOCK)
ON migs.group_handle = mig.index_group_handle
INNER JOIN sys.dm_db_missing_index_details AS mid WITH (NOLOCK)
ON mig.index_handle = mid.index_handle
ORDER BY index_advantage DESC OPTION (RECOMPILE);
------

-- Getting missing index information for all of the databases on the instance is very useful
-- Look at last user seek time, number of user seeks to help determine source and importance
-- Also look at avg_user_impact and avg_total_user_cost to help determine importance
-- SQL Server is overly eager to add included columns, so beware
-- Do not just blindly add indexes that show up from this query!!!



-- Get VLF Counts for all databases on the instance (Query 17) (VLF Counts)
-- (adapted from Michelle Ufford) 
CREATE TABLE #VLFInfo (FileID  int,
					   FileSize bigint, StartOffset bigint,
					   FSeqNo      bigint, [Status]    bigint,
					   Parity      bigint, CreateLSN   numeric(38));
	 
CREATE TABLE #VLFCountResults(DatabaseName sysname, VLFCount int);
	 
EXEC sp_MSforeachdb N'Use [?]; 

				INSERT INTO #VLFInfo 
				EXEC sp_executesql N''DBCC LOGINFO([?])''; 
	 
				INSERT INTO #VLFCountResults 
				SELECT DB_NAME(), COUNT(*) 
				FROM #VLFInfo; 

				TRUNCATE TABLE #VLFInfo;'
	 
SELECT DatabaseName, VLFCount  
FROM #VLFCountResults
ORDER BY VLFCount DESC;
	 
DROP TABLE #VLFInfo;
DROP TABLE #VLFCountResults;
------

-- High VLF counts can affect write performance 
-- and they can make full database restores and crash recovery take much longer
-- Try to keep your VLF counts under 200 in most cases



-- Get CPU utilization by database (Query 18) (CPU Usage by Database)
WITH DB_CPU_Stats
AS
(SELECT pa.DatabaseID, DB_Name(pa.DatabaseID) AS [Database Name], SUM(qs.total_worker_time/1000) AS [CPU_Time_Ms]
 FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
 CROSS APPLY (SELECT CONVERT(int, value) AS [DatabaseID] 
              FROM sys.dm_exec_plan_attributes(qs.plan_handle)
              WHERE attribute = N'dbid') AS pa
 GROUP BY DatabaseID)
SELECT ROW_NUMBER() OVER(ORDER BY [CPU_Time_Ms] DESC) AS [CPU Rank],
       [Database Name], [CPU_Time_Ms] AS [CPU Time (ms)], 
       CAST([CPU_Time_Ms] * 1.0 / SUM([CPU_Time_Ms]) OVER() * 100.0 AS DECIMAL(5, 2)) AS [CPU Percent]
FROM DB_CPU_Stats
WHERE DatabaseID <> 32767 -- ResourceDB
ORDER BY [CPU Rank] OPTION (RECOMPILE);
------

-- Helps determine which database is using the most CPU resources on the instance


-- Get I/O utilization by database (Query 19) (IO Usage By Database)
WITH Aggregate_IO_Statistics
AS
(SELECT DB_NAME(database_id) AS [Database Name],
CAST(SUM(num_of_bytes_read + num_of_bytes_written)/1048576 AS DECIMAL(12, 2)) AS io_in_mb
FROM sys.dm_io_virtual_file_stats(NULL, NULL) AS [DM_IO_STATS]
GROUP BY database_id)
SELECT ROW_NUMBER() OVER(ORDER BY io_in_mb DESC) AS [I/O Rank], [Database Name], io_in_mb AS [Total I/O (MB)],
       CAST(io_in_mb/ SUM(io_in_mb) OVER() * 100.0 AS DECIMAL(5,2)) AS [I/O Percent]
FROM Aggregate_IO_Statistics
ORDER BY [I/O Rank] OPTION (RECOMPILE);
------

-- Helps determine which database is using the most I/O resources on the instance


-- Get total buffer usage by database for current instance (Query 20) (Total Buffer Usage by Database)
-- This make take some time to run on a busy instance
WITH AggregateBufferPoolUsage
AS
(SELECT DB_NAME(database_id) AS [Database Name],
CAST(COUNT(*) * 8/1024.0 AS DECIMAL (10,2))  AS [CachedSize]
FROM sys.dm_os_buffer_descriptors WITH (NOLOCK)
WHERE database_id <> 32767 -- ResourceDB
GROUP BY DB_NAME(database_id))
SELECT ROW_NUMBER() OVER(ORDER BY CachedSize DESC) AS [Buffer Pool Rank], [Database Name], CachedSize AS [Cached Size (MB)],
       CAST(CachedSize / SUM(CachedSize) OVER() * 100.0 AS DECIMAL(5,2)) AS [Buffer Pool Percent]
FROM AggregateBufferPoolUsage
ORDER BY [Buffer Pool Rank] OPTION (RECOMPILE);
------

-- Tells you how much memory (in the buffer pool) 
-- is being used by each database on the instance


-- Clear Wait Stats with this command
-- DBCC SQLPERF('sys.dm_os_wait_stats', CLEAR);

-- Isolate top waits for server instance since last restart or wait statistics clear (Query 21) (Top Waits)
WITH [Waits] 
AS (SELECT wait_type, wait_time_ms/ 1000.0 AS [WaitS],
          (wait_time_ms - signal_wait_time_ms) / 1000.0 AS [ResourceS],
           signal_wait_time_ms / 1000.0 AS [SignalS],
           waiting_tasks_count AS [WaitCount],
           100.0 *  wait_time_ms / SUM (wait_time_ms) OVER() AS [Percentage],
           ROW_NUMBER() OVER(ORDER BY wait_time_ms DESC) AS [RowNum]
    FROM sys.dm_os_wait_stats WITH (NOLOCK)
    WHERE [wait_type] NOT IN (
        N'BROKER_EVENTHANDLER', N'BROKER_RECEIVE_WAITFOR', N'BROKER_TASK_STOP',
		N'BROKER_TO_FLUSH', N'BROKER_TRANSMITTER', N'CHECKPOINT_QUEUE',
        N'CHKPT', N'CLR_AUTO_EVENT', N'CLR_MANUAL_EVENT', N'CLR_SEMAPHORE',
        N'DBMIRROR_DBM_EVENT', N'DBMIRROR_EVENTS_QUEUE', N'DBMIRROR_WORKER_QUEUE',
		N'DBMIRRORING_CMD', N'DIRTY_PAGE_POLL', N'DISPATCHER_QUEUE_SEMAPHORE',
        N'EXECSYNC', N'FSAGENT', N'FT_IFTS_SCHEDULER_IDLE_WAIT', N'FT_IFTSHC_MUTEX',
        N'HADR_CLUSAPI_CALL', N'HADR_FILESTREAM_IOMGR_IOCOMPLETION', N'HADR_LOGCAPTURE_WAIT', 
		N'HADR_NOTIFICATION_DEQUEUE', N'HADR_TIMER_TASK', N'HADR_WORK_QUEUE',
        N'KSOURCE_WAKEUP', N'LAZYWRITER_SLEEP', N'LOGMGR_QUEUE', N'ONDEMAND_TASK_QUEUE',
        N'PWAIT_ALL_COMPONENTS_INITIALIZED', N'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP',
        N'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP', N'REQUEST_FOR_DEADLOCK_SEARCH',
		N'RESOURCE_QUEUE', N'SERVER_IDLE_CHECK', N'SLEEP_BPOOL_FLUSH', N'SLEEP_DBSTARTUP',
		N'SLEEP_DCOMSTARTUP', N'SLEEP_MASTERDBREADY', N'SLEEP_MASTERMDREADY',
        N'SLEEP_MASTERUPGRADED', N'SLEEP_MSDBSTARTUP', N'SLEEP_SYSTEMTASK', N'SLEEP_TASK',
        N'SLEEP_TEMPDBSTARTUP', N'SNI_HTTP_ACCEPT', N'SP_SERVER_DIAGNOSTICS_SLEEP',
		N'SQLTRACE_BUFFER_FLUSH', N'SQLTRACE_INCREMENTAL_FLUSH_SLEEP', N'SQLTRACE_WAIT_ENTRIES',
		N'WAIT_FOR_RESULTS', N'WAITFOR', N'WAITFOR_TASKSHUTDOWN', N'WAIT_XTP_HOST_WAIT',
		N'WAIT_XTP_OFFLINE_CKPT_NEW_LOG', N'WAIT_XTP_CKPT_CLOSE', N'XE_DISPATCHER_JOIN',
        N'XE_DISPATCHER_WAIT', N'XE_TIMER_EVENT')
    AND waiting_tasks_count > 0)
SELECT
    MAX (W1.wait_type) AS [WaitType],
	CAST (MAX (W1.Percentage) AS DECIMAL (5,2)) AS [Wait Percentage],
	CAST ((MAX (W1.WaitS) / MAX (W1.WaitCount)) AS DECIMAL (16,4)) AS [AvgWait_Sec],
    CAST ((MAX (W1.ResourceS) / MAX (W1.WaitCount)) AS DECIMAL (16,4)) AS [AvgRes_Sec],
    CAST ((MAX (W1.SignalS) / MAX (W1.WaitCount)) AS DECIMAL (16,4)) AS [AvgSig_Sec], 
    CAST (MAX (W1.WaitS) AS DECIMAL (16,2)) AS [Wait_Sec],
    CAST (MAX (W1.ResourceS) AS DECIMAL (16,2)) AS [Resource_Sec],
    CAST (MAX (W1.SignalS) AS DECIMAL (16,2)) AS [Signal_Sec],
    MAX (W1.WaitCount) AS [Wait Count],
	CAST (N'https://www.sqlskills.com/help/waits/' + W1.wait_type AS XML) AS [Help/Info URL]
FROM Waits AS W1
INNER JOIN Waits AS W2
ON W2.RowNum <= W1.RowNum
GROUP BY W1.RowNum, W1.wait_type
HAVING SUM (W2.Percentage) - MAX (W1.Percentage) < 99 -- percentage threshold
OPTION (RECOMPILE);
------

-- Cumulative wait stats are not as useful on an idle instance that is not under load or performance pressure

-- SQL Server Wait Types Library (Paul Randal)
-- https://www.sqlskills.com/help/waits/

-- The SQL Server Wait Type Repository
-- http://blogs.msdn.com/b/psssql/archive/2009/11/03/the-sql-server-wait-type-repository.aspx

-- Wait statistics, or please tell me where it hurts
-- https://www.sqlskills.com/blogs/paul/wait-statistics-or-please-tell-me-where-it-hurts/

-- SQL Server 2005 Performance Tuning using the Waits and Queues
-- http://technet.microsoft.com/en-us/library/cc966413.aspx

-- sys.dm_os_wait_stats (Transact-SQL)
-- http://msdn.microsoft.com/en-us/library/ms179984(v=sql.105).aspx



-- Signal Waits for instance  (Query 22) (Signal Waits)
SELECT CAST(100.0 * SUM(signal_wait_time_ms) / SUM (wait_time_ms) AS NUMERIC(20,2)) AS [% Signal (CPU) Waits],
CAST(100.0 * SUM(wait_time_ms - signal_wait_time_ms) / SUM (wait_time_ms) AS NUMERIC(20,2)) AS [% Resource Waits]
FROM sys.dm_os_wait_stats WITH (NOLOCK)
WHERE wait_type NOT IN (
        N'BROKER_EVENTHANDLER', N'BROKER_RECEIVE_WAITFOR', N'BROKER_TASK_STOP',
		N'BROKER_TO_FLUSH', N'BROKER_TRANSMITTER', N'CHECKPOINT_QUEUE',
        N'CHKPT', N'CLR_AUTO_EVENT', N'CLR_MANUAL_EVENT', N'CLR_SEMAPHORE',
        N'DBMIRROR_DBM_EVENT', N'DBMIRROR_EVENTS_QUEUE', N'DBMIRROR_WORKER_QUEUE',
		N'DBMIRRORING_CMD', N'DIRTY_PAGE_POLL', N'DISPATCHER_QUEUE_SEMAPHORE',
        N'EXECSYNC', N'FSAGENT', N'FT_IFTS_SCHEDULER_IDLE_WAIT', N'FT_IFTSHC_MUTEX',
        N'HADR_CLUSAPI_CALL', N'HADR_FILESTREAM_IOMGR_IOCOMPLETION', N'HADR_LOGCAPTURE_WAIT', 
		N'HADR_NOTIFICATION_DEQUEUE', N'HADR_TIMER_TASK', N'HADR_WORK_QUEUE',
        N'KSOURCE_WAKEUP', N'LAZYWRITER_SLEEP', N'LOGMGR_QUEUE', N'ONDEMAND_TASK_QUEUE',
        N'PWAIT_ALL_COMPONENTS_INITIALIZED', N'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP',
        N'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP', N'REQUEST_FOR_DEADLOCK_SEARCH',
		N'RESOURCE_QUEUE', N'SERVER_IDLE_CHECK', N'SLEEP_BPOOL_FLUSH', N'SLEEP_DBSTARTUP',
		N'SLEEP_DCOMSTARTUP', N'SLEEP_MASTERDBREADY', N'SLEEP_MASTERMDREADY',
        N'SLEEP_MASTERUPGRADED', N'SLEEP_MSDBSTARTUP', N'SLEEP_SYSTEMTASK', N'SLEEP_TASK',
        N'SLEEP_TEMPDBSTARTUP', N'SNI_HTTP_ACCEPT', N'SP_SERVER_DIAGNOSTICS_SLEEP',
		N'SQLTRACE_BUFFER_FLUSH', N'SQLTRACE_INCREMENTAL_FLUSH_SLEEP', N'SQLTRACE_WAIT_ENTRIES',
		N'WAIT_FOR_RESULTS', N'WAITFOR', N'WAITFOR_TASKSHUTDOWN', N'WAIT_XTP_HOST_WAIT',
		N'WAIT_XTP_OFFLINE_CKPT_NEW_LOG', N'WAIT_XTP_CKPT_CLOSE', N'XE_DISPATCHER_JOIN',
        N'XE_DISPATCHER_WAIT', N'XE_TIMER_EVENT') OPTION (RECOMPILE);
------

-- Signal Waits above 10-15% is usually a confirming sign of CPU pressure
-- Cumulative wait stats are not as useful on an idle instance that is not under load or performance pressure
-- Resource waits are non-CPU related waits


--  Get logins that are connected and how many sessions they have (Query 23) (Connection Counts)
SELECT login_name, [program_name], COUNT(session_id) AS [session_count] 
FROM sys.dm_exec_sessions WITH (NOLOCK)
GROUP BY login_name, [program_name]
ORDER BY COUNT(session_id) DESC OPTION (RECOMPILE);
------

-- This can help characterize your workload and
-- determine whether you are seeing a normal level of activity


-- Get a count of SQL connections by IP address (Query 24) (Connection Counts by IP Address)
SELECT ec.client_net_address, es.[program_name], es.[host_name], es.login_name, 
COUNT(ec.session_id) AS [connection count] 
FROM sys.dm_exec_sessions AS es WITH (NOLOCK) 
INNER JOIN sys.dm_exec_connections AS ec WITH (NOLOCK) 
ON es.session_id = ec.session_id 
GROUP BY ec.client_net_address, es.[program_name], es.[host_name], es.login_name  
ORDER BY ec.client_net_address, es.[program_name] OPTION (RECOMPILE);
------

-- This helps you figure where your database load is coming from
-- and verifies connectivity from other machines


-- Get Average Task Counts (run multiple times) (Query 25) (Avg Task Counts)
SELECT AVG(current_tasks_count) AS [Avg Task Count], 
AVG(runnable_tasks_count) AS [Avg Runnable Task Count],
AVG(pending_disk_io_count) AS [Avg Pending DiskIO Count]
FROM sys.dm_os_schedulers WITH (NOLOCK)
WHERE scheduler_id < 255 OPTION (RECOMPILE);
------

-- Sustained values above 10 suggest further investigation in that area
-- High Avg Task Counts are often caused by blocking/deadlocking or other resource contention

-- Sustained values above 1 suggest further investigation in that area
-- High Avg Runnable Task Counts are a good sign of CPU pressure
-- High Avg Pending DiskIO Counts are a sign of disk pressure

-- How to Do Some Very Basic SQL Server Monitoring
-- https://www.sqlskills.com/blogs/glenn/how-to-do-some-very-basic-sql-server-monitoring/



-- Get CPU Utilization History for last 256 minutes (in one minute intervals) (Query 26) (CPU Utilization History)
-- This version works with SQL Server 2005
DECLARE @ts_now bigint; 
SET @ts_now = (SELECT cpu_ticks / CONVERT(float, cpu_ticks_in_ms) FROM sys.dm_os_sys_info WITH (NOLOCK)); 

SELECT TOP(256) SQLProcessUtilization AS [SQL Server Process CPU Utilization], 
               SystemIdle AS [System Idle Process], 
               100 - SystemIdle - SQLProcessUtilization AS [Other Process CPU Utilization], 
               DATEADD(ms, -1 * (@ts_now - [timestamp]), GETDATE()) AS [Event Time] 
FROM ( 
	  SELECT record.value('(./Record/@id)[1]', 'int') AS record_id, 
			record.value('(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]', 'int') 
			AS [SystemIdle], 
			record.value('(./Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization)[1]', 
			'int') 
			AS [SQLProcessUtilization], [timestamp] 
	  FROM ( 
			SELECT [timestamp], CONVERT(xml, record) AS [record] 
			FROM sys.dm_os_ring_buffers WITH (NOLOCK)
			WHERE ring_buffer_type = N'RING_BUFFER_SCHEDULER_MONITOR' 
			AND record LIKE '%<SystemHealth>%') AS x 
	  ) AS y 
ORDER BY record_id DESC OPTION (RECOMPILE);
------

-- Look at the trend over the entire period. 
-- Also look at high sustained Other Process CPU Utilization values


-- Page Life Expectancy (PLE) value for each NUMA node in current instance  (Query 27) (PLE by NUMA Node)
SELECT @@SERVERNAME AS [Server Name], RTRIM([object_name]) AS [Object Name], instance_name, cntr_value AS [Page Life Expectancy]
FROM sys.dm_os_performance_counters WITH (NOLOCK)
WHERE [object_name] LIKE N'%Buffer Node%' -- Handles named instances
AND counter_name = N'Page life expectancy' OPTION (RECOMPILE);
------

-- PLE is a good measurement of memory pressure.
-- Higher PLE is better. Watch the trend over time, not the absolute value.
-- This will only return one row for non-NUMA systems.

-- Page Life Expectancy isn�t what you think�
-- https://www.sqlskills.com/blogs/paul/page-life-expectancy-isnt-what-you-think/



-- Memory Grants Pending value for current instance (Query 28) (Memory Grants Pending)
SELECT @@SERVERNAME AS [Server Name], RTRIM([object_name]) AS [Object Name], cntr_value AS [Memory Grants Pending]                                                                                                       
FROM sys.dm_os_performance_counters WITH (NOLOCK)
WHERE [object_name] LIKE N'%Memory Manager%' -- Handles named instances
AND counter_name = N'Memory Grants Pending' OPTION (RECOMPILE);
------

-- Memory Grants Pending above zero for a sustained period is a very strong indicator of memory pressure


-- Memory Clerk Usage for instance  (Query 29) (Memory Clerk Usage)
-- Look for high value for CACHESTORE_SQLCP (Ad-hoc query plans)
SELECT TOP(10) [type] AS [Memory Clerk Type], SUM(single_pages_kb)/1024 AS [SPA Memory Usage (MB)] 
FROM sys.dm_os_memory_clerks WITH (NOLOCK)
GROUP BY [type]  
ORDER BY SUM(single_pages_kb) DESC OPTION (RECOMPILE);
------

-- CACHESTORE_SQLCP  SQL Plans         
-- These are cached SQL statements or batches that aren't in stored procedures, functions and triggers
-- Watch out for high values for CACHESTORE_SQLCP

-- CACHESTORE_OBJCP  Object Plans      
-- These are compiled plans for stored procedures, functions and triggers



-- Find single-use, ad-hoc queries that are bloating the plan cache (Query 30) (Ad hoc Queries)
SELECT TOP(50) DB_NAME(t.[dbid]) AS [Database Name], t.[text] AS [Query Text], 
cp.objtype AS [Object Type], cp.cacheobjtype AS [Cache Object Type],  
cp.size_in_bytes/1024 AS [Plan Size in KB]
FROM sys.dm_exec_cached_plans AS cp WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t
WHERE cp.cacheobjtype = N'Compiled Plan' 
AND cp.objtype IN (N'Adhoc', N'Prepared') 
AND cp.usecounts = 1
ORDER BY cp.size_in_bytes DESC, DB_NAME(t.[dbid]) OPTION (RECOMPILE);
------

-- Gives you the text and size of single-use ad-hoc queries that waste space in plan cache
-- SQL Server Agent creates lots of ad-hoc, single use query plans in SQL Server 2005
-- Running DBCC FREESYSTEMCACHE ('SQL Plans') periodically may be required to better control this.
-- Enabling forced parameterization for the database can help, but test first!

-- Plan cache, adhoc workloads and clearing the single-use plan cache bloat
-- https://www.sqlskills.com/blogs/kimberly/plan-cache-adhoc-workloads-and-clearing-the-single-use-plan-cache-bloat/



-- Database specific queries *****************************************************************

-- **** Please switch to a user database that you are interested in! *****
USE YourDatabaseName; -- make sure to change to an actual database on your instance, not the master system database
GO

-- Individual File Sizes and space available for current database  (Query 31) (File Sizes and Space)
SELECT f.name AS [File Name] , f.physical_name AS [Physical Name], 
CAST((f.size/128.0) AS DECIMAL(15,2)) AS [Total Size in MB],
CAST(f.size/128.0 - CAST(FILEPROPERTY(f.name, 'SpaceUsed') AS int)/128.0 AS DECIMAL(15,2)) 
AS [Available Space In MB], [file_id], fg.name AS [Filegroup Name]
FROM sys.database_files AS f WITH (NOLOCK) 
LEFT OUTER JOIN sys.data_spaces AS fg WITH (NOLOCK) 
ON f.data_space_id = fg.data_space_id OPTION (RECOMPILE);
------

-- Look at how large and how full the files are and where they are located
-- Make sure the transaction log is not full!!



-- I/O Statistics by file for the current database  (Query 32) (IO Stats By File)
SELECT DB_NAME(DB_ID()) AS [Database Name], df.name AS [Logical Name], vfs.[file_id], 
df.physical_name AS [Physical Name], vfs.num_of_reads, vfs.num_of_writes, vfs.io_stall_read_ms, vfs.io_stall_write_ms,
CAST(100. * vfs.io_stall_read_ms/(vfs.io_stall_read_ms + vfs.io_stall_write_ms) AS DECIMAL(10,1)) AS [IO Stall Reads Pct],
CAST(100. * vfs.io_stall_write_ms/(vfs.io_stall_write_ms + vfs.io_stall_read_ms) AS DECIMAL(10,1)) AS [IO Stall Writes Pct],
(vfs.num_of_reads + vfs.num_of_writes) AS [Writes + Reads], 
CAST(vfs.num_of_bytes_read/1048576.0 AS DECIMAL(10, 2)) AS [MB Read], 
CAST(vfs.num_of_bytes_written/1048576.0 AS DECIMAL(10, 2)) AS [MB Written],
CAST(100. * vfs.num_of_reads/(vfs.num_of_reads + vfs.num_of_writes) AS DECIMAL(10,1)) AS [# Reads Pct],
CAST(100. * vfs.num_of_writes/(vfs.num_of_reads + vfs.num_of_writes) AS DECIMAL(10,1)) AS [# Write Pct],
CAST(100. * vfs.num_of_bytes_read/(vfs.num_of_bytes_read + vfs.num_of_bytes_written) AS DECIMAL(10,1)) AS [Read Bytes Pct],
CAST(100. * vfs.num_of_bytes_written/(vfs.num_of_bytes_read + vfs.num_of_bytes_written) AS DECIMAL(10,1)) AS [Written Bytes Pct]
FROM sys.dm_io_virtual_file_stats(DB_ID(), NULL) AS vfs
INNER JOIN sys.database_files AS df WITH (NOLOCK)
ON vfs.[file_id]= df.[file_id] OPTION (RECOMPILE);
------

-- This helps you characterize your workload better from an I/O perspective
-- It helps you determine whether you has an OLTP or DW/DSS type of workload



-- Cached SP's By Execution Count (SQL 2005)  (Query 33) (SP Execution Counts)
SELECT TOP(25) OBJECT_NAME(objectid, dbid) AS [SP Name], qt.[text] AS [SP Text], 
qs.execution_count AS [Execution Count],  
qs.execution_count/DATEDIFF(Minute, qs.creation_time, GETDATE()) AS [Calls/Minute],
qs.total_worker_time/qs.execution_count AS [AvgWorkerTime],
qs.total_worker_time AS [TotalWorkerTime],
qs.total_elapsed_time/qs.execution_count AS [AvgElapsedTime],
qs.max_logical_reads, qs.max_logical_writes, qs.total_physical_reads, 
DATEDIFF(Minute, qs.creation_time, GetDate()) AS [Age in Cache]
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(qs.[sql_handle]) AS qt
WHERE qt.[dbid] = DB_ID() 
ORDER BY qs.execution_count DESC OPTION (RECOMPILE);
------

-- Tells you which cached stored procedures are called the most often
-- This helps you characterize and baseline your workload


-- Top Cached SPs By Avg Elapsed Time (SQL 2005)  (Query 34) (SP Avg Elapsed Time)
SELECT TOP(25) OBJECT_NAME(objectid, dbid) AS [SP Name], qt.[text] AS [SP Text],  
ISNULL(qs.total_elapsed_time/qs.execution_count, 0) AS [AvgElapsedTime], 
qs.execution_count AS [Execution Count], qs.total_worker_time AS [TotalWorkerTime], 
qs.total_worker_time/qs.execution_count AS [AvgWorkerTime],
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.creation_time, GETDATE()), 0) AS [Calls/Minute],
qs.max_logical_reads, qs.max_logical_writes, 
DATEDIFF(Minute, qs.creation_time, GETDATE()) AS [Age in Cache]
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(qs.[sql_handle]) AS qt
WHERE qt.[dbid] = DB_ID() 
ORDER BY qs.total_elapsed_time/qs.execution_count DESC OPTION (RECOMPILE);
------

-- This helps you find long-running cached stored procedures that
-- may be easy to optimize with standard query tuning techniques

-- Cached SP's By Worker Time (SQL 2005) Worker time relates to CPU cost  (Query 35) (SP Worker Time)
SELECT TOP(25) OBJECT_NAME(objectid, dbid) AS [SP Name], qt.[text] AS [SP Text], 
qs.total_worker_time AS [TotalWorkerTime], qs.total_worker_time/qs.execution_count AS [AvgWorkerTime],
qs.execution_count AS [Execution Count], 
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.creation_time, GETDATE()), 0) AS [Calls/Minute],
ISNULL(qs.total_elapsed_time/qs.execution_count, 0) AS [AvgElapsedTime], 
qs.max_logical_reads, qs.max_logical_writes, 
DATEDIFF(Minute, qs.creation_time, GETDATE()) AS [Age in Cache]
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(qs.[sql_handle]) AS qt
WHERE qt.[dbid] = DB_ID() 
ORDER BY qs.total_worker_time DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a CPU perspective
-- You should look at this if you see signs of CPU pressure

-- Cached SP's By Logical Reads (SQL 2005) Logical reads relate to memory pressure  (Query 36) (SP Logical Reads)
SELECT TOP(25) OBJECT_NAME(objectid, dbid) AS [SP Name], qt.[text] AS [SP Text],  
total_logical_reads, qs.max_logical_reads,
total_logical_reads/qs.execution_count AS [AvgLogicalReads], qs.execution_count AS [Execution Count], 
qs.execution_count/DATEDIFF(Minute, qs.creation_time, GETDATE()) AS [Calls/Minute], 
qs.total_worker_time/qs.execution_count AS [AvgWorkerTime],
qs.total_worker_time AS [TotalWorkerTime],
qs.total_elapsed_time/qs.execution_count AS [AvgElapsedTime],
qs.total_logical_writes,
 qs.max_logical_writes, qs.total_physical_reads, 
DATEDIFF(Minute, qs.creation_time, GETDATE()) AS [Age in Cache]
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(qs.[sql_handle]) AS qt
WHERE qt.[dbid] = DB_ID() 
ORDER BY total_logical_reads DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a memory perspective
-- You should look at this if you see signs of memory pressure


-- Cached SP's By Physical Reads (SQL 2005) Physical reads relate to read I/O pressure  (Query 37) (SP Physical Reads)
SELECT TOP(25) OBJECT_NAME(objectid, dbid) AS [SP Name], qt.[text] AS [SP Text],  
qs.total_physical_reads, total_logical_reads, qs.max_logical_reads,
total_logical_reads/qs.execution_count AS [AvgLogicalReads], qs.execution_count AS [Execution Count], 
qs.execution_count/DATEDIFF(Minute, qs.creation_time, GETDATE()) AS [Calls/Minute], 
qs.total_worker_time/qs.execution_count AS [AvgWorkerTime],
qs.total_worker_time AS [TotalWorkerTime],
qs.total_elapsed_time/qs.execution_count AS [AvgElapsedTime],
DATEDIFF(Minute, qs.creation_time, GETDATE()) AS [Age in Cache]
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(qs.[sql_handle]) AS qt
WHERE qt.[dbid] = DB_ID() -- Filter by current database
AND qs.total_physical_reads > 0
ORDER BY qs.total_physical_reads DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a read I/O perspective
-- You should look at this if you see signs of I/O pressure or of memory pressure


-- Top Cached SPs By Total Logical Writes (SQL 2005)  (Query 38) (SP Logical Writes)
-- Logical writes relate to both memory and disk I/O pressure 
SELECT TOP(25) OBJECT_NAME(objectid, dbid) AS [SP Name], qt.[text] AS [SP Text],  
qs.total_logical_writes, qs.max_logical_writes,
qs.total_logical_writes/qs.execution_count AS [AvgLogicalWrites], qs.execution_count AS [Execution Count], 
qs.execution_count/DATEDIFF(Minute, qs.creation_time, GETDATE()) AS [Calls/Minute], 
qs.total_worker_time/qs.execution_count AS [AvgWorkerTime],
qs.total_worker_time AS [TotalWorkerTime],
qs.total_elapsed_time/qs.execution_count AS [AvgElapsedTime],
qs.total_physical_reads, 
DATEDIFF(Minute, qs.creation_time, GETDATE()) AS [Age in Cache]
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(qs.[sql_handle]) AS qt
WHERE qt.[dbid] = DB_ID()
AND qs.total_logical_writes > 0 
ORDER BY total_logical_writes DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a write I/O perspective
-- You should look at this if you see signs of I/O pressure or of memory pressure


-- Lists the top statements by average input/output usage for the current database  (Query 39) (Top IO Statements)
SELECT TOP(50) OBJECT_NAME(qt.objectid, dbid) AS [SP Name],
(qs.total_logical_reads + qs.total_logical_writes) /qs.execution_count AS [Avg IO], qs.execution_count AS [Execution Count],
SUBSTRING(qt.[text],qs.statement_start_offset/2, 
	(CASE 
		WHEN qs.statement_end_offset = -1 
	 THEN LEN(CONVERT(nvarchar(max), qt.[text])) * 2 
		ELSE qs.statement_end_offset 
	 END - qs.statement_start_offset)/2) AS [Query Text]	
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS qt
WHERE qt.[dbid] = DB_ID()
ORDER BY [Avg IO] DESC OPTION (RECOMPILE);
------

-- Helps you find the most expensive statements for I/O by SP



-- Possible Bad NC Indexes (writes > reads)  (Query 40) (Bad NC Indexes)
SELECT OBJECT_NAME(s.[object_id]) AS [Table Name], i.name AS [Index Name], i.index_id, 
i.is_disabled, i.is_hypothetical, i.has_filter, i.fill_factor,
user_updates AS [Total Writes], user_seeks + user_scans + user_lookups AS [Total Reads],
user_updates - (user_seeks + user_scans + user_lookups) AS [Difference]
FROM sys.dm_db_index_usage_stats AS s WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON s.[object_id] = i.[object_id]
AND i.index_id = s.index_id
WHERE OBJECTPROPERTY(s.[object_id],'IsUserTable') = 1
AND s.database_id = DB_ID()
AND s.user_updates > (s.user_seeks + s.user_scans + s.user_lookups)
AND i.index_id > 1 AND i.[type_desc] = N'NONCLUSTERED'
AND i.is_primary_key = 0 AND i.is_unique_constraint = 0
ORDER BY [Difference] DESC, [Total Writes] DESC, [Total Reads] ASC OPTION (RECOMPILE);
------

-- Look for indexes with high numbers of writes and zero or very low numbers of reads
-- Consider your complete workload, and how long your instance has been running
-- Investigate further before dropping an index!


-- Missing Indexes for current database by Index Advantage  (Query 41) (Missing Indexes)
SELECT DISTINCT CONVERT(decimal(18,2), user_seeks * avg_total_user_cost * (avg_user_impact * 0.01)) AS [index_advantage], 
migs.last_user_seek, mid.[statement] AS [Database.Schema.Table],
mid.equality_columns, mid.inequality_columns, mid.included_columns,
migs.unique_compiles, migs.user_seeks, migs.avg_total_user_cost, migs.avg_user_impact,
OBJECT_NAME(mid.[object_id]) AS [Table Name], p.rows AS [Table Rows]
FROM sys.dm_db_missing_index_group_stats AS migs WITH (NOLOCK)
INNER JOIN sys.dm_db_missing_index_groups AS mig WITH (NOLOCK)
ON migs.group_handle = mig.index_group_handle
INNER JOIN sys.dm_db_missing_index_details AS mid WITH (NOLOCK)
ON mig.index_handle = mid.index_handle
INNER JOIN sys.partitions AS p WITH (NOLOCK)
ON p.[object_id] = mid.[object_id]
WHERE mid.database_id = DB_ID()
AND p.index_id < 2 
ORDER BY index_advantage DESC OPTION (RECOMPILE);
------

-- Look at index advantage, last user seek time, number of user seeks to help determine source and importance
-- SQL Server is overly eager to add included columns, so beware
-- Do not just blindly add indexes that show up from this query!!!


-- Find missing index warnings for cached plans in the current database  (Query 42) (Missing Index Warnings)
-- Note: This query could take some time on a busy instance
SELECT TOP(25) OBJECT_NAME(objectid) AS [ObjectName], 
               query_plan, cp.objtype, cp.usecounts
FROM sys.dm_exec_cached_plans AS cp WITH (NOLOCK)
CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) AS qp
WHERE CAST(query_plan AS NVARCHAR(MAX)) LIKE N'%MissingIndex%'
AND dbid = DB_ID()
ORDER BY cp.usecounts DESC OPTION (RECOMPILE);
------

-- Helps you connect missing indexes to specific stored procedures
-- This can help you decide whether to add them or not


-- Breaks down buffers used by current database by object (table, index) in the buffer cache  (Query 43) (Buffer Usage)
-- Note: This query could take some time on a busy instance
SELECT OBJECT_NAME(p.[object_id]) AS [ObjectName], 
p.index_id, COUNT(*)/128 AS [buffer size(MB)],  COUNT(*) AS [buffer_count] 
FROM sys.allocation_units AS a
INNER JOIN sys.dm_os_buffer_descriptors AS b WITH (NOLOCK)
ON a.allocation_unit_id = b.allocation_unit_id
INNER JOIN sys.partitions AS p WITH (NOLOCK)
ON a.container_id = p.hobt_id
WHERE b.database_id = DB_ID()
AND p.[object_id] > 100
GROUP BY p.[object_id], p.index_id
ORDER BY buffer_count DESC OPTION (RECOMPILE);
------

-- Tells you what tables and indexes are using the most memory in the buffer cache


-- Get Table names, row counts  (Query 44) (Table Sizes)
SELECT OBJECT_NAME(object_id) AS [ObjectName], SUM(Rows) AS [RowCount]
FROM sys.partitions WITH (NOLOCK)
WHERE index_id < 2 --ignore the partitions from the non-clustered index if any
AND OBJECT_NAME(object_id) NOT LIKE N'sys%'
AND OBJECT_NAME(object_id) NOT LIKE N'queue_%' 
AND OBJECT_NAME(object_id) NOT LIKE N'filestream_tombstone%'
AND OBJECT_NAME(object_id) NOT LIKE N'fulltext%' 
GROUP BY [object_id]
ORDER BY SUM(Rows) DESC OPTION (RECOMPILE);
------

-- Gives you an idea of table sizes


-- Detect blocking (run multiple times)  (Query 45) (Detect Blocking)
SELECT t1.resource_type AS [lock type], DB_NAME(resource_database_id) AS [database],
t1.resource_associated_entity_id AS [blk object],t1.request_mode AS [lock req],  --- lock requested
t1.request_session_id AS [waiter sid], t2.wait_duration_ms AS [wait time],       -- spid of waiter  
(SELECT [text] FROM sys.dm_exec_requests AS r WITH (NOLOCK)                      -- get sql for waiter
CROSS APPLY sys.dm_exec_sql_text(r.[sql_handle]) 
WHERE r.session_id = t1.request_session_id) AS [waiter_batch],
(SELECT SUBSTRING(qt.[text],r.statement_start_offset/2, 
    (CASE WHEN r.statement_end_offset = -1 
    THEN LEN(CONVERT(nvarchar(max), qt.[text])) * 2 
    ELSE r.statement_end_offset END - r.statement_start_offset)/2) 
FROM sys.dm_exec_requests AS r WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(r.[sql_handle]) AS qt
WHERE r.session_id = t1.request_session_id) AS [waiter_stmt],					-- statement blocked
t2.blocking_session_id AS [blocker sid],										-- spid of blocker
(SELECT [text] FROM sys.sysprocesses AS p										-- get sql for blocker
CROSS APPLY sys.dm_exec_sql_text(p.[sql_handle]) 
WHERE p.spid = t2.blocking_session_id) AS [blocker_stmt]
FROM sys.dm_tran_locks AS t1 WITH (NOLOCK)
INNER JOIN sys.dm_os_waiting_tasks AS t2 WITH (NOLOCK)
ON t1.lock_owner_address = t2.resource_address OPTION (RECOMPILE);
------

-- Helps troubleshoot blocking and deadlocking issues
-- The results will change from second to second on a busy system
-- You should run this query multiple times when you see signs of blocking


-- When were Statistics last updated on all indexes?  (Query 46) (Statistics Update)
SELECT SCHEMA_NAME(o.Schema_ID) + N'.' + o.NAME AS [Object Name], o.type_desc AS [Object Type],
      i.name AS [Index Name], STATS_DATE(i.[object_id], i.index_id) AS [Statistics Date], 
      s.auto_created, s.no_recompute, s.user_created, st.row_count, st.used_page_count
FROM sys.objects AS o WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON o.[object_id] = i.[object_id]
INNER JOIN sys.stats AS s WITH (NOLOCK)
ON i.[object_id] = s.[object_id] 
AND i.index_id = s.stats_id
INNER JOIN sys.dm_db_partition_stats AS st WITH (NOLOCK)
ON o.[object_id] = st.[object_id]
AND i.[index_id] = st.[index_id]
WHERE o.[type] IN ('U', 'V')
AND st.row_count > 0
ORDER BY STATS_DATE(i.[object_id], i.index_id) DESC OPTION (RECOMPILE);
------  

-- Helps discover possible problems with out-of-date statistics
-- Also gives you an idea which indexes are the most active



-- Get fragmentation info for all indexes above a certain size in the current database  (Query 47) (Index Fragmentation)
-- Note: This query could take some time on a very large database
SELECT DB_NAME(ps.database_id) AS [Database Name], OBJECT_NAME(ps.OBJECT_ID) AS [Object Name], 
i.name AS [Index Name], ps.index_id, ps.index_type_desc, ps.avg_fragmentation_in_percent, 
ps.fragment_count, ps.page_count, i.fill_factor
FROM sys.dm_db_index_physical_stats(DB_ID(),NULL, NULL, NULL , N'LIMITED') AS ps
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON ps.[object_id] = i.[object_id] 
AND ps.index_id = i.index_id
WHERE ps.database_id = DB_ID()
AND ps.page_count > 2500
ORDER BY ps.avg_fragmentation_in_percent DESC OPTION (RECOMPILE);
------

-- Helps determine whether you have framentation in your relational indexes
-- and how effective your index maintenance strategy is


--- Index Read/Write stats (all tables in current DB) ordered by Reads  (Query 48) (Overall Index Usage - Reads)
SELECT OBJECT_NAME(s.[object_id]) AS [ObjectName], i.name AS [IndexName], i.index_id,
	   user_seeks + user_scans + user_lookups AS [Reads], s.user_updates AS [Writes],  
	   i.type_desc AS [IndexType], i.fill_factor AS [FillFactor]
FROM sys.dm_db_index_usage_stats AS s WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON s.[object_id] = i.[object_id]
WHERE OBJECTPROPERTY(s.[object_id],'IsUserTable') = 1
AND i.index_id = s.index_id
AND s.database_id = DB_ID()
ORDER BY user_seeks + user_scans + user_lookups DESC OPTION (RECOMPILE); -- Order by reads
------

-- Show which indexes in the current database are most active for Reads


--- Index Read/Write stats (all tables in current DB) ordered by Writes  (Query 49) (Overall Index Usage - Writes)
SELECT OBJECT_NAME(s.[object_id]) AS [ObjectName], i.name AS [IndexName], i.index_id,
	   s.user_updates AS [Writes], user_seeks + user_scans + user_lookups AS [Reads], 
	   i.type_desc AS [IndexType], i.fill_factor AS [FillFactor],
	   s.last_system_update, s.last_user_update
FROM sys.dm_db_index_usage_stats AS s WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON s.[object_id] = i.[object_id]
WHERE OBJECTPROPERTY(s.[object_id],'IsUserTable') = 1
AND i.index_id = s.index_id
AND s.database_id = DB_ID()
ORDER BY s.user_updates DESC OPTION (RECOMPILE);						 -- Order by writes
------

-- Show which indexes in the current database are most active for Writes


-- Get lock waits for current database (Query 50) (Lock Waits)
SELECT o.name AS [table_name], i.name AS [index_name], ios.index_id, ios.partition_number,
		SUM(ios.row_lock_wait_count) AS [total_row_lock_waits], 
		SUM(ios.row_lock_wait_in_ms) AS [total_row_lock_wait_in_ms],
		SUM(ios.page_lock_wait_count) AS [total_page_lock_waits],
		SUM(ios.page_lock_wait_in_ms) AS [total_page_lock_wait_in_ms],
		SUM(ios.page_lock_wait_in_ms)+ SUM(row_lock_wait_in_ms) AS [total_lock_wait_in_ms]
FROM sys.dm_db_index_operational_stats(DB_ID(), NULL, NULL, NULL) AS ios
INNER JOIN sys.objects AS o WITH (NOLOCK)
ON ios.[object_id] = o.[object_id]
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON ios.[object_id] = i.[object_id] 
AND ios.index_id = i.index_id
WHERE o.[object_id] > 100
GROUP BY o.name, i.name, ios.index_id, ios.partition_number
HAVING SUM(ios.page_lock_wait_in_ms)+ SUM(row_lock_wait_in_ms) > 0
ORDER BY total_lock_wait_in_ms DESC OPTION (RECOMPILE);
------

-- This query is helpful for troubleshooting blocking and deadlocking issues


-- Look at recent Full backups for the current database (Query 51) (Recent Full Backups)
SELECT TOP (30) bs.server_name, bs.database_name AS [Database Name], 
CONVERT (BIGINT, bs.backup_size / 1048576 ) AS [Backup Size (MB)],
DATEDIFF (SECOND, bs.backup_start_date, bs.backup_finish_date) AS [Backup Elapsed Time (sec)],
bs.backup_finish_date AS [Backup Finish Date], bmf.physical_device_name AS [Backup Location], bmf.physical_block_size
FROM msdb.dbo.backupset AS bs WITH (NOLOCK)
INNER JOIN msdb.dbo.backupmediafamily AS bmf WITH (NOLOCK)
ON bs.media_set_id = bmf.media_set_id  
WHERE DATEDIFF (SECOND, bs.backup_start_date, bs.backup_finish_date) > 0 
AND bs.backup_size > 0
AND bs.type = 'D' -- Change to L if you want Log backups
AND database_name = DB_NAME(DB_ID())
ORDER BY bs.backup_finish_date DESC OPTION (RECOMPILE);
------

-- Are your backup sizes and times changing over time?
-- Are you using backup compression?
-- Are you using backup checksums?
-- Are you doing copy_only backups?
-- Have you done any backup tuning with striped backups, or changing the parameters of the backup command?



-- These three Pluralsight Courses go into more detail about how to run these queries and interpret the results

-- SQL Server 2014 DMV Diagnostic Queries � Part 1 
-- https://bit.ly/2plxCer

-- SQL Server 2014 DMV Diagnostic Queries � Part 2
-- https://bit.ly/2IuJpzI

-- SQL Server 2014 DMV Diagnostic Queries � Part 3
-- https://bit.ly/2FIlCPb



-- Sign up for Microsoft Visual Studio Dev Essentials and get a free three month pass to Pluralsight

-- Microsoft Visual Studio Dev Essentials
-- http://bit.ly/1q6xbDL


-- Sign up for Microsoft Azure Essentials and get lots of free Azure usage credits, MCP exam voucher, three month Pluralsight subscription

-- Microsoft Azure Essentials
-- https://bit.ly/2JMWe8x


-- August 2017 blog series about upgrading and migrating SQL Server
-- https://bit.ly/2ftKVrX


tools\dbatools\bin\diagnosticquery\SQLServerDiagnosticQueries_2008R2_201806.sql

-- SQL Server 2008 R2 Diagnostic Information Queries
-- Glenn Berry 
-- Last Modified: July 10, 2018
-- https://www.sqlserverperformance.wordpress.com/
-- https://www.sqlskills.com/blogs/glenn/
-- Twitter: GlennAlanBerry

-- Please listen to my Pluralsight courses
-- https://www.pluralsight.com/author/glenn-berry

-- If you want to find all of our SQLskills SQL101 blog posts, check out https://www.sqlskills.com/help/sql101/

-- Many of these queries will not work if you have databases in 80 compatibility mode
-- Please make sure you are using the correct version of these diagnostic queries for your version of SQL Server

--******************************************************************************
--*   Copyright (C) 2018 Glenn Berry, SQLskills.com
--*   All rights reserved. 
--*
--*   For more scripts and sample code, check out 
--*      https://www.sqlskills.com/blogs/glenn
--*
--*   You may alter this code for your own *non-commercial* purposes. You may
--*   republish altered code as long as you include this copyright and give due credit. 
--*
--*
--*   THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF 
--*   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED 
--*   TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
--*   PARTICULAR PURPOSE. 
--*
--******************************************************************************

-- Note: A number of these queries will only work on SQL Server 2008 R2 SP1 or later
-- They are all noted in the instructions

-- Check the major product version to see if it is SQL Server 2008 R2
IF NOT EXISTS (SELECT * WHERE CONVERT(varchar(128), SERVERPROPERTY('ProductVersion')) LIKE '10.5%')
	BEGIN
		DECLARE @ProductVersion varchar(128) = CONVERT(varchar(128), SERVERPROPERTY('ProductVersion'));
		RAISERROR ('Script does not match the ProductVersion [%s] of this instance. Many of these queries may not work on this version.' , 18 , 16 , @ProductVersion);
	END
	ELSE
		PRINT N'You have the correct major version of SQL Server for this diagnostic information script';


-- Instance level queries *******************************

-- SQL and OS Version information for current instance  (Query 1) (Version Info)
SELECT SERVERPROPERTY ('MachineName') AS [Server Name], @@VERSION AS [SQL Server and OS Version Info];
------


-- SQL Server 2008 R2 Builds				SQL Server 2008 R2 SP1 Builds			SQL Server 2008 R2 SP2 Builds							SQL Server 2008 R2 SP3 Builds
-- Build			Description				Build		Description					Build		Description									Build		Description
-- 10.50.1092		August 2009 CTP2		
-- 10.50.1352		November 2009 CTP3
-- 10.50.1450		Release Candidate
-- 10.50.1600		RTM
-- 10.50.1702		RTM CU1
-- 10.50.1720		RTM CU2
-- 10.50.1734		RTM CU3
-- 10.50.1746		RTM CU4
-- 10.50.1753		RTM CU5
-- 10.50.1765		RTM CU6	 --->			10.50.2500	SP1 RTM
-- 10.50.1777		RTM CU7
-- 10.50.1797		RTM CU8	 --->			10.50.2769  SP1 CU1
-- 10.50.1804       RTM CU9  --->			10.50.2772  SP1 CU2
-- 10.50.1807		RTM CU10 --->           10.50.2789  SP1 CU3
-- 10.50.1809       RTM CU11 --->			10.50.2796  SP1 CU4 
-- 10.50.1810		RTM CU12 --->			10.50.2806	SP1 CU5		--->			10.50.4000	SP2 RTM
-- 10.50.1815		RTM CU13 --->           10.50.2811  SP1 CU6
-- 10.50.1817		RTM CU14 --->			10.50.2817  SP1 CU7		--->			10.50.4260	SP2 CU1			         7/24/2012
-- RTM Branch Retired        --->			10.50.2822  SP1 CU8     --->			10.50.4263  SP2 CU2                  8/31/2012   
--											10.50.2866  SP1 CU9     --->			10.50.4266  SP2 CU3					10/15/2012
--                                          10.50.2868  SP1 CU10    --->			10.50.4270  SP2 CU4					12/17/2012
--                                          10.50.2869  SP1 CU11    --->            10.50.4276  SP2 CU5				     2/18/2013
--                                          10.50.2874  SP1 CU12    --->            10.50.4279  SP2 CU6                  4/15/2013
--                                          10.50.2876  SP1 CU13    --->            10.50.4286  SP2 CU7					 6/17/2013
--                                          10.50.2881  SP1 CU14    --->            10.50.4290  SP2 CU8                  8/22/2013
--                                                                                  10.50.4295  SP2 CU9                 10/28/2013  
--                                                                                  10.50.4297  SP2 CU10                12/16/2013 
--                                                                                  10.50.4302  SP2 CU11                 2/17/2014
--                                                                                  10.50.4305	SP2 CU12                 4/21/2014
--                                                                                  10.50.4319  SP2 CU13                 6/30/2014   
--																																			10.50.6000	SP3 RTM		9/26/2014
--                                                                                                                                          10.50.6525  SP3 + HF     2/9/2015      http://support.microsoft.com/kb/3033860
-- Security Update for SQL Server 2008 R2 SP3 (KB4057113) https://www.microsoft.com/en-us/download/details.aspx?id=56415					10.50.6560	SP3 + HF	 1/5/2018	   Hot fix for Spectre/Meltdown


-- SQL Server 2008 R2 SP3 RTM plus an on-demand hotfix (Build 10.50.6525) is the final public build of SQL Server 2008 R2, barring any later security fixes.          

-- SQL Server 2008 R2 RTM was considered an "unsupported service pack" as of July 12, 2012
-- SQL Server 2008 R2 SP1 was considered an "unsupported service pack" as of August 8, 2013										

-- The SQL Server 2008 R2 builds that were released after SQL Server 2008 R2 was released
-- http://support.microsoft.com/kb/981356

-- The SQL Server 2008 R2 builds that were released after SQL Server 2008 R2 Service Pack 1 was released 
-- http://support.microsoft.com/kb/2567616

-- The SQL Server 2008 R2 builds that were released after SQL Server 2008 R2 Service Pack 2 was released
-- http://support.microsoft.com/kb/2730301 

-- SQL Server 2008 R2 SP2 CU13 is the final cumulative update for SQL Server 2008 R2 SP2

-- SQL Server 2008 R2 SP3 Release information
-- http://support2.microsoft.com/kb/2979597

-- Download SQL Server Management Studio (SSMS)
-- https://msdn.microsoft.com/en-us/library/mt238290.aspx



-- When was SQL Server installed  (Query 2) (SQL Server Install Date) 
SELECT @@SERVERNAME AS [Server Name], create_date AS [SQL Server Install Date] 
FROM sys.server_principals WITH (NOLOCK)
WHERE name = N'NT AUTHORITY\SYSTEM'
OR name = N'NT AUTHORITY\NETWORK SERVICE' OPTION (RECOMPILE);
------

-- Tells you the date and time that SQL Server was installed
-- It is a good idea to know how old your instance is


-- Get selected server properties (Query 3) (Server Properties)
SELECT SERVERPROPERTY('MachineName') AS [MachineName], SERVERPROPERTY('ServerName') AS [ServerName],  
SERVERPROPERTY('InstanceName') AS [Instance], SERVERPROPERTY('IsClustered') AS [IsClustered], 
SERVERPROPERTY('ComputerNamePhysicalNetBIOS') AS [ComputerNamePhysicalNetBIOS], 
SERVERPROPERTY('Edition') AS [Edition], SERVERPROPERTY('ProductLevel') AS [ProductLevel], 
SERVERPROPERTY('ProductVersion') AS [ProductVersion], SERVERPROPERTY('ProcessID') AS [ProcessID],
SERVERPROPERTY('Collation') AS [Collation], SERVERPROPERTY('IsFullTextInstalled') AS [IsFullTextInstalled], 
SERVERPROPERTY('IsIntegratedSecurityOnly') AS [IsIntegratedSecurityOnly];
------

-- This gives you a lot of useful information about your instance of SQL Server,
-- such as the ProcessID for SQL Server and your collation


-- Get SQL Server Agent jobs and Category information (Query 4) (SQL Server Agent Jobs)
SELECT sj.name AS [Job Name], sj.[description] AS [Job Description], SUSER_SNAME(sj.owner_sid) AS [Job Owner],
sj.date_created AS [Date Created], sj.[enabled] AS [Job Enabled], 
sj.notify_email_operator_id, sj.notify_level_email, sc.name AS [CategoryName],
s.[enabled] AS [Sched Enabled], js.next_run_date, js.next_run_time
FROM msdb.dbo.sysjobs AS sj WITH (NOLOCK)
INNER JOIN msdb.dbo.syscategories AS sc WITH (NOLOCK)
ON sj.category_id = sc.category_id
LEFT OUTER JOIN msdb.dbo.sysjobschedules AS js WITH (NOLOCK)
ON sj.job_id = js.job_id
LEFT OUTER JOIN msdb.dbo.sysschedules AS s WITH (NOLOCK)
ON js.schedule_id = s.schedule_id
ORDER BY sj.name OPTION (RECOMPILE);
------

-- Gives you some basic information about your SQL Server Agent jobs, who owns them and how they are configured
-- Look for Agent jobs that are not owned by sa
-- Look for jobs that have a notify_email_operator_id set to 0 (meaning no operator)
-- Look for jobs that have a notify_level_email set to 0 (meaning no e-mail is ever sent)
--
-- MSDN sysjobs documentation
-- http://msdn.microsoft.com/en-us/library/ms189817.aspx


-- Get SQL Server Agent Alert Information (Query 5) (SQL Server Agent Alerts)
SELECT name, event_source, message_id, severity, [enabled], has_notification, 
       delay_between_responses, occurrence_count, last_occurrence_date, last_occurrence_time
FROM msdb.dbo.sysalerts WITH (NOLOCK)
ORDER BY name OPTION (RECOMPILE);
------

-- Gives you some basic information about your SQL Server Agent Alerts (which are different from SQL Server Agent jobs)
-- Read more about Agent Alerts here: https://www.sqlskills.com/blogs/glenn/creating-sql-server-agent-alerts-for-critical-errors/


-- Returns a list of all global trace flags that are enabled (Query 6) (Global Trace Flags)
DBCC TRACESTATUS (-1);
------

-- If no global trace flags are enabled, no results will be returned.
-- It is very useful to know what global trace flags are currently enabled as part of the diagnostic process.

-- Common trace flags that should be enabled in most cases
-- TF 1117 - When growing a data file, grow all files at the same time so they remain the same size, reducing allocation contention points
--           http://support2.microsoft.com/kb/2154845
-- 
-- TF 1118 - Helps alleviate allocation contention in tempdb, SQL Server allocates full extents to each database object, 
--           thereby eliminating the contention on SGAM pages (more important with older versions of SQL Server)
--           Recommendations to reduce allocation contention in SQL Server tempdb database
--           http://support2.microsoft.com/kb/2154845

-- TF 2371 - Lowers auto update statistics threshold for large tables
--           http://blogs.msdn.com/b/saponsqlserver/archive/2011/09/07/changes-to-automatic-update-statistics-in-sql-server-traceflag-2371.aspx

-- TF 3226 - Supresses logging of successful database backup messages to the SQL Server Error Log
--           https://www.sqlskills.com/blogs/paul/fed-up-with-backup-success-messages-bloating-your-error-logs/


-- Windows information (SQL Server 2008 R2 SP1 or greater)  (Query 7) (Windows Info)
SELECT windows_release, windows_service_pack_level, 
       windows_sku, os_language_version
FROM sys.dm_os_windows_info WITH (NOLOCK) OPTION (RECOMPILE);
------

-- Gives you major OS version, Service Pack, Edition, and language info for the operating system 
-- 6.3 is either Windows 8.1 or Windows Server 2012 R2
-- 6.2 is either Windows 8 or Windows Server 2012
-- 6.1 is either Windows 7 or Windows Server 2008 R2
-- 6.0 is either Windows Vista or Windows Server 2008
-- 5.2 is either Windows XP or Windows Server 2003

-- Windows SKU codes
-- 4 is Enterprise Edition
-- 7 is Standard Server Edition
-- 8 is Datacenter Server Edition
-- 10 is Enterprise Server Edition
-- 48 is Professional Edition

-- 1033 for os_language_version is US-English

-- Hardware and Software Requirements for Installing SQL Server 2008 R2
-- http://msdn.microsoft.com/en-us/library/ms143506(v=sql.105).aspx

-- Using SQL Server in Windows 8, Windows 8.1, Windows Server 2012 and Windows Server 2012 R2 environments
-- http://support.microsoft.com/kb/2681562


-- SQL Server Services information (SQL Server 2008 R2 SP1 or greater)  (Query 8) (SQL Server Services Info)
SELECT servicename, process_id, startup_type_desc, status_desc, 
last_startup_time, service_account, is_clustered, cluster_nodename, [filename]
FROM sys.dm_server_services WITH (NOLOCK) OPTION (RECOMPILE);
------

-- Tells you the account being used for the SQL Server Service and the SQL Agent Service
-- Shows the processid, when they were last started, and their current status
-- Shows whether you are running on a failover cluster instance


-- SQL Server NUMA Node information  (Query 9) (SQL Server NUMA Info)
SELECT node_id, node_state_desc, memory_node_id, processor_group, online_scheduler_count, 
       active_worker_count, avg_load_balance, resource_monitor_state
FROM sys.dm_os_nodes WITH (NOLOCK) 
WHERE node_state_desc <> N'ONLINE DAC' OPTION (RECOMPILE);
------

-- Gives you some useful information about the composition 
-- and relative load on your NUMA nodes


-- Hardware information from SQL Server 2008 R2  (Query 10) (Hardware Info)
-- (Cannot distinguish between HT and multi-core)
SELECT cpu_count AS [Logical CPU Count], hyperthread_ratio AS [Hyperthread Ratio],
cpu_count/hyperthread_ratio AS [Physical CPU Count], 
physical_memory_in_bytes/1048576 AS [Physical Memory (MB)], 
sqlserver_start_time, affinity_type_desc 
FROM sys.dm_os_sys_info WITH (NOLOCK) OPTION (RECOMPILE);
------

-- Gives you some good basic hardware information about your database server


-- Get System Manufacturer and model number from  (Query 11) (System Manufacturer)
-- SQL Server Error log. This query might take a few seconds 
-- if you have not recycled your error log recently
EXEC sys.xp_readerrorlog 0, 1, N'Manufacturer';
------ 

-- This can help you determine the capabilities
-- and capacities of your database server


-- Get processor description from Windows Registry  (Query 12) (Processor Description)
EXEC sys.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'HARDWARE\DESCRIPTION\System\CentralProcessor\0', N'ProcessorNameString';
------

-- Gives you the model number and rated clock speed of your processor(s)
-- Your processors may be running at less than the rated clock speed due
-- to the Windows Power Plan or hardware power management

-- You can use CPU-Z to get your actual CPU core speed and a lot of other useful information
-- http://www.cpuid.com/softwares/cpu-z.html

-- You can learn more about processor selection for SQL Server by following this link
-- https://www.sqlskills.com/blogs/glenn/processor-selection-for-sql-server/



-- Get the current node name from your cluster nodes  (Query 13) (Cluster Node Properties)
-- (if your database server is in a failover cluster)
SELECT NodeName
FROM sys.dm_os_cluster_nodes WITH (NOLOCK) OPTION (RECOMPILE);
------

-- Knowing which node owns the cluster resources is critical
-- Especially when you are installing Windows or SQL Server updates
-- You will see no results if your instance is not clustered


-- Get configuration values for instance  (Query 14) (Configuration Values)
SELECT name, value, value_in_use, minimum, maximum, [description], is_dynamic, is_advanced
FROM sys.configurations WITH (NOLOCK)
ORDER BY name OPTION (RECOMPILE);
------

-- Focus on these settings:
-- backup compression default (should be 1 in most cases)
-- clr enabled (only enable if it is needed)
-- cost threshold for parallelism (depends on your workload)
-- lightweight pooling (should be zero)
-- max degree of parallelism (depends on your workload)
-- max server memory (MB) (set to an appropriate value, not the default)
-- optimize for ad hoc workloads (should be 1)
-- priority boost (should be zero)
-- remote admin connections (should be 1)




-- Get information on location, time and size of any memory dumps from SQL Server (SQL Server 2008 R2 SP1 or greater)  (Query 15) (Memory Dump Info)
SELECT [filename], creation_time, size_in_bytes/1048576.0 AS [Size (MB)]
FROM sys.dm_server_memory_dumps WITH (NOLOCK) 
ORDER BY creation_time DESC OPTION (RECOMPILE);
------

-- This will not return any rows if you have 
-- not had any memory dumps (which is a good thing)


-- File names and paths for all user and system databases on instance   (Query 16) (Database Filenames and Paths)
SELECT DB_NAME([database_id]) AS [Database Name], 
       [file_id], [name], physical_name, [type_desc], state_desc,
	   is_percent_growth, growth,
	   CONVERT(bigint, growth/128.0) AS [Growth in MB], 
       CONVERT(bigint, size/128.0) AS [Total Size in MB]
FROM sys.master_files WITH (NOLOCK)
ORDER BY DB_NAME([database_id]), [file_id] OPTION (RECOMPILE);
------

-- Things to look at:
-- Are data files and log files on different drives?
-- Is everything on the C: drive?
-- Is TempDB on dedicated drives?
-- Is there only one TempDB data file?
-- Are all of the TempDB data files the same size?
-- Are there multiple data files for user databases?
-- Is percent growth enabled for any files (which is bad)?


-- Volume info for all LUNS that have database files on the current instance (SQL Server 2008 R2 SP1 or greater)  (Query 17) (Volume Info)
SELECT DISTINCT vs.volume_mount_point, vs.file_system_type, 
vs.logical_volume_name, CONVERT(DECIMAL(18,2),vs.total_bytes/1073741824.0) AS [Total Size (GB)],
CONVERT(DECIMAL(18,2),vs.available_bytes/1073741824.0) AS [Available Size (GB)],  
CAST(CAST(vs.available_bytes AS FLOAT)/ CAST(vs.total_bytes AS FLOAT) AS DECIMAL(18,2)) * 100 AS [Space Free %] 
FROM sys.master_files AS f WITH (NOLOCK)
CROSS APPLY sys.dm_os_volume_stats(f.database_id, f.[file_id]) AS vs OPTION (RECOMPILE);
------

--Shows you the total and free space on the LUNs where you have database files


-- Look for I/O requests taking longer than 15 seconds in the five most recent SQL Server Error Logs (Query 18) (IO Warnings)
CREATE TABLE #IOWarningResults(LogDate datetime, ProcessInfo sysname, LogText nvarchar(1000));

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 0, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 1, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 2, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 3, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 4, 1, N'taking longer than 15 seconds';

SELECT LogDate, ProcessInfo, LogText
FROM #IOWarningResults
ORDER BY LogDate DESC;

DROP TABLE #IOWarningResults;
------  

-- Finding 15 second I/O warnings in the SQL Server Error Log is useful evidence of
-- poor I/O performance (which might have many different causes)
-- Look to see if you see any patterns in the results (same files, same drives, same time of day, etc.)

-- Diagnostics in SQL Server help detect stalled and stuck I/O operations
-- https://support.microsoft.com/en-us/kb/897284



-- Drive level latency information (Query 19) (Drive Level Latency)
-- Based on code from Jimmy May
SELECT tab.[Drive], tab.volume_mount_point AS [Volume Mount Point], 
	CASE 
		WHEN num_of_reads = 0 THEN 0 
		ELSE (io_stall_read_ms/num_of_reads) 
	END AS [Read Latency],
	CASE 
		WHEN num_of_writes = 0 THEN 0 
		ELSE (io_stall_write_ms/num_of_writes) 
	END AS [Write Latency],
	CASE 
		WHEN (num_of_reads = 0 AND num_of_writes = 0) THEN 0 
		ELSE (io_stall/(num_of_reads + num_of_writes)) 
	END AS [Overall Latency],
	CASE 
		WHEN num_of_reads = 0 THEN 0 
		ELSE (num_of_bytes_read/num_of_reads) 
	END AS [Avg Bytes/Read],
	CASE 
		WHEN num_of_writes = 0 THEN 0 
		ELSE (num_of_bytes_written/num_of_writes) 
	END AS [Avg Bytes/Write],
	CASE 
		WHEN (num_of_reads = 0 AND num_of_writes = 0) THEN 0 
		ELSE ((num_of_bytes_read + num_of_bytes_written)/(num_of_reads + num_of_writes)) 
	END AS [Avg Bytes/Transfer]
FROM (SELECT LEFT(UPPER(mf.physical_name), 2) AS Drive, SUM(num_of_reads) AS num_of_reads,
	         SUM(io_stall_read_ms) AS io_stall_read_ms, SUM(num_of_writes) AS num_of_writes,
	         SUM(io_stall_write_ms) AS io_stall_write_ms, SUM(num_of_bytes_read) AS num_of_bytes_read,
	         SUM(num_of_bytes_written) AS num_of_bytes_written, SUM(io_stall) AS io_stall, vs.volume_mount_point 
      FROM sys.dm_io_virtual_file_stats(NULL, NULL) AS vfs
      INNER JOIN sys.master_files AS mf WITH (NOLOCK)
      ON vfs.database_id = mf.database_id AND vfs.file_id = mf.file_id
	  CROSS APPLY sys.dm_os_volume_stats(mf.database_id, mf.[file_id]) AS vs 
      GROUP BY LEFT(UPPER(mf.physical_name), 2), vs.volume_mount_point) AS tab
ORDER BY [Overall Latency] OPTION (RECOMPILE);
------

-- Shows you the drive-level latency for reads and writes, in milliseconds
-- Latency above 20-25ms is usually a problem


-- Calculates average stalls per read, per write, and per total input/output for each database file  (Query 20) (IO Stalls by File)
SELECT DB_NAME(fs.database_id) AS [Database Name], CAST(fs.io_stall_read_ms/(1.0 + fs.num_of_reads) AS NUMERIC(10,1)) AS [avg_read_stall_ms],
CAST(fs.io_stall_write_ms/(1.0 + fs.num_of_writes) AS NUMERIC(10,1)) AS [avg_write_stall_ms],
CAST((fs.io_stall_read_ms + fs.io_stall_write_ms)/(1.0 + fs.num_of_reads + fs.num_of_writes) AS NUMERIC(10,1)) AS [avg_io_stall_ms],
CONVERT(DECIMAL(18,2), mf.size/128.0) AS [File Size (MB)], mf.physical_name, mf.type_desc, fs.io_stall_read_ms, fs.num_of_reads, 
fs.io_stall_write_ms, fs.num_of_writes, fs.io_stall_read_ms + fs.io_stall_write_ms AS [io_stalls], fs.num_of_reads + fs.num_of_writes AS [total_io]
FROM sys.dm_io_virtual_file_stats(null,null) AS fs
INNER JOIN sys.master_files AS mf WITH (NOLOCK)
ON fs.database_id = mf.database_id
AND fs.[file_id] = mf.[file_id]
ORDER BY avg_io_stall_ms DESC OPTION (RECOMPILE);
------

-- Helps determine which database files on the entire instance have the most I/O bottlenecks
-- This can help you decide whether certain LUNs are overloaded and whether you might
-- want to move some files to a different location or perhaps improve your I/O performance


-- Recovery model, log reuse wait description, log file size, log usage size  (Query 21) (Database Properties)
-- and compatibility level for all databases on instance
SELECT db.[name] AS [Database Name], db.recovery_model_desc AS [Recovery Model], 
db.log_reuse_wait_desc AS [Log Reuse Wait Description], 
ls.cntr_value AS [Log Size (KB)], lu.cntr_value AS [Log Used (KB)],
CAST(CAST(lu.cntr_value AS FLOAT) / CAST(ls.cntr_value AS FLOAT)AS DECIMAL(18,2)) * 100 AS [Log Used %], 
db.[compatibility_level] AS [DB Compatibility Level], 
db.page_verify_option_desc AS [Page Verify Option], db.is_auto_create_stats_on, db.is_auto_update_stats_on,
db.is_auto_update_stats_async_on, db.is_parameterization_forced, 
db.snapshot_isolation_state_desc, db.is_read_committed_snapshot_on,
db.is_auto_close_on, db.is_auto_shrink_on, db.is_cdc_enabled, db.is_published
FROM sys.databases AS db WITH (NOLOCK)
INNER JOIN sys.dm_os_performance_counters AS lu WITH (NOLOCK)
ON db.name = lu.instance_name
INNER JOIN sys.dm_os_performance_counters AS ls WITH (NOLOCK) 
ON db.name = ls.instance_name
WHERE lu.counter_name LIKE N'Log File(s) Used Size (KB)%' 
AND ls.counter_name LIKE N'Log File(s) Size (KB)%'
AND ls.cntr_value > 0
ORDER BY db.[name] OPTION (RECOMPILE);
------

-- Things to look at:
-- How many databases are on the instance?
-- What recovery models are they using?
-- What is the log reuse wait description?
-- How full are the transaction logs ?
-- What compatibility level are the databases on? 
-- What is the Page Verify Option? (should be CHECKSUM)
-- Is Auto Update Statistics Asynchronously enabled?
-- Make sure auto_shrink and auto_close are not enabled!



-- Missing Indexes for all databases by Index Advantage  (Query 22) (Missing Indexes All Databases)
SELECT CONVERT(decimal(18,2), user_seeks * avg_total_user_cost * (avg_user_impact * 0.01)) AS [index_advantage],  
migs.last_user_seek, mid.[statement] AS [Database.Schema.Table],
mid.equality_columns, mid.inequality_columns, mid.included_columns,
migs.unique_compiles, migs.user_seeks, migs.avg_total_user_cost, migs.avg_user_impact
FROM sys.dm_db_missing_index_group_stats AS migs WITH (NOLOCK)
INNER JOIN sys.dm_db_missing_index_groups AS mig WITH (NOLOCK)
ON migs.group_handle = mig.index_group_handle
INNER JOIN sys.dm_db_missing_index_details AS mid WITH (NOLOCK)
ON mig.index_handle = mid.index_handle
ORDER BY index_advantage DESC OPTION (RECOMPILE);
------

-- Getting missing index information for all of the databases on the instance is very useful
-- Look at last user seek time, number of user seeks to help determine source and importance
-- Also look at avg_user_impact and avg_total_user_cost to help determine importance
-- SQL Server is overly eager to add included columns, so beware
-- Do not just blindly add indexes that show up from this query!!!



-- Get VLF Counts for all databases on the instance (Query 23) (VLF Counts)
-- (adapted from Michelle Ufford) 
CREATE TABLE #VLFInfo (FileID  int,
					   FileSize bigint, StartOffset bigint,
					   FSeqNo      bigint, [Status]    bigint,
					   Parity      bigint, CreateLSN   numeric(38));
	 
CREATE TABLE #VLFCountResults(DatabaseName sysname, VLFCount int);
	 
EXEC sp_MSforeachdb N'Use [?]; 

				INSERT INTO #VLFInfo 
				EXEC sp_executesql N''DBCC LOGINFO([?])''; 
	 
				INSERT INTO #VLFCountResults 
				SELECT DB_NAME(), COUNT(*) 
				FROM #VLFInfo; 

				TRUNCATE TABLE #VLFInfo;'
	 
SELECT DatabaseName, VLFCount  
FROM #VLFCountResults
ORDER BY VLFCount DESC;
	 
DROP TABLE #VLFInfo;
DROP TABLE #VLFCountResults;
------

-- High VLF counts can affect write performance 
-- and they can make full database restores and crash recovery take much longer
-- Try to keep your VLF counts under 200 in most cases (depending on log file size)



-- Get CPU utilization by database (Query 24) (CPU Usage by Database)
WITH DB_CPU_Stats
AS
(SELECT pa.DatabaseID, DB_Name(pa.DatabaseID) AS [Database Name], SUM(qs.total_worker_time/1000) AS [CPU_Time_Ms]
 FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
 CROSS APPLY (SELECT CONVERT(int, value) AS [DatabaseID] 
              FROM sys.dm_exec_plan_attributes(qs.plan_handle)
              WHERE attribute = N'dbid') AS pa
 GROUP BY DatabaseID)
SELECT ROW_NUMBER() OVER(ORDER BY [CPU_Time_Ms] DESC) AS [CPU Rank],
       [Database Name], [CPU_Time_Ms] AS [CPU Time (ms)], 
       CAST([CPU_Time_Ms] * 1.0 / SUM([CPU_Time_Ms]) OVER() * 100.0 AS DECIMAL(5, 2)) AS [CPU Percent]
FROM DB_CPU_Stats
WHERE DatabaseID <> 32767 -- ResourceDB
ORDER BY [CPU Rank] OPTION (RECOMPILE);
------

-- Helps determine which database is using the most CPU resources on the instance


-- Get I/O utilization by database (Query 25) (IO Usage By Database)
WITH Aggregate_IO_Statistics
AS
(SELECT DB_NAME(database_id) AS [Database Name],
CAST(SUM(num_of_bytes_read + num_of_bytes_written)/1048576 AS DECIMAL(12, 2)) AS io_in_mb
FROM sys.dm_io_virtual_file_stats(NULL, NULL) AS [DM_IO_STATS]
GROUP BY database_id)
SELECT ROW_NUMBER() OVER(ORDER BY io_in_mb DESC) AS [I/O Rank], [Database Name], io_in_mb AS [Total I/O (MB)],
       CAST(io_in_mb/ SUM(io_in_mb) OVER() * 100.0 AS DECIMAL(5,2)) AS [I/O Percent]
FROM Aggregate_IO_Statistics
ORDER BY [I/O Rank] OPTION (RECOMPILE);
------

-- Helps determine which database is using the most I/O resources on the instance


-- Get total buffer usage by database for current instance  (Query 26) (Total Buffer Usage by Database)
-- This make take some time to run on a busy instance
WITH AggregateBufferPoolUsage
AS
(SELECT DB_NAME(database_id) AS [Database Name],
CAST(COUNT(*) * 8/1024.0 AS DECIMAL (10,2))  AS [CachedSize]
FROM sys.dm_os_buffer_descriptors WITH (NOLOCK)
WHERE database_id <> 32767 -- ResourceDB
GROUP BY DB_NAME(database_id))
SELECT ROW_NUMBER() OVER(ORDER BY CachedSize DESC) AS [Buffer Pool Rank], [Database Name], CachedSize AS [Cached Size (MB)],
       CAST(CachedSize / SUM(CachedSize) OVER() * 100.0 AS DECIMAL(5,2)) AS [Buffer Pool Percent]
FROM AggregateBufferPoolUsage
ORDER BY [Buffer Pool Rank] OPTION (RECOMPILE);
------

-- Tells you how much memory (in the buffer pool) 
-- is being used by each database on the instance


-- Clear Wait Stats with this command
-- DBCC SQLPERF('sys.dm_os_wait_stats', CLEAR);

-- Isolate top waits for server instance since last restart or wait statistics clear (Query 27) (Top Waits)
WITH [Waits] 
AS (SELECT wait_type, wait_time_ms/ 1000.0 AS [WaitS],
          (wait_time_ms - signal_wait_time_ms) / 1000.0 AS [ResourceS],
           signal_wait_time_ms / 1000.0 AS [SignalS],
           waiting_tasks_count AS [WaitCount],
           100.0 *  wait_time_ms / SUM (wait_time_ms) OVER() AS [Percentage],
           ROW_NUMBER() OVER(ORDER BY wait_time_ms DESC) AS [RowNum]
    FROM sys.dm_os_wait_stats WITH (NOLOCK)
    WHERE [wait_type] NOT IN (
        N'BROKER_EVENTHANDLER', N'BROKER_RECEIVE_WAITFOR', N'BROKER_TASK_STOP',
		N'BROKER_TO_FLUSH', N'BROKER_TRANSMITTER', N'CHECKPOINT_QUEUE',
        N'CHKPT', N'CLR_AUTO_EVENT', N'CLR_MANUAL_EVENT', N'CLR_SEMAPHORE',
        N'DBMIRROR_DBM_EVENT', N'DBMIRROR_EVENTS_QUEUE', N'DBMIRROR_WORKER_QUEUE',
		N'DBMIRRORING_CMD', N'DIRTY_PAGE_POLL', N'DISPATCHER_QUEUE_SEMAPHORE',
        N'EXECSYNC', N'FSAGENT', N'FT_IFTS_SCHEDULER_IDLE_WAIT', N'FT_IFTSHC_MUTEX',
        N'HADR_CLUSAPI_CALL', N'HADR_FILESTREAM_IOMGR_IOCOMPLETION', N'HADR_LOGCAPTURE_WAIT', 
		N'HADR_NOTIFICATION_DEQUEUE', N'HADR_TIMER_TASK', N'HADR_WORK_QUEUE',
        N'KSOURCE_WAKEUP', N'LAZYWRITER_SLEEP', N'LOGMGR_QUEUE', N'ONDEMAND_TASK_QUEUE',
        N'PWAIT_ALL_COMPONENTS_INITIALIZED', N'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP',
        N'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP', N'REQUEST_FOR_DEADLOCK_SEARCH',
		N'RESOURCE_QUEUE', N'SERVER_IDLE_CHECK', N'SLEEP_BPOOL_FLUSH', N'SLEEP_DBSTARTUP',
		N'SLEEP_DCOMSTARTUP', N'SLEEP_MASTERDBREADY', N'SLEEP_MASTERMDREADY',
        N'SLEEP_MASTERUPGRADED', N'SLEEP_MSDBSTARTUP', N'SLEEP_SYSTEMTASK', N'SLEEP_TASK',
        N'SLEEP_TEMPDBSTARTUP', N'SNI_HTTP_ACCEPT', N'SP_SERVER_DIAGNOSTICS_SLEEP',
		N'SQLTRACE_BUFFER_FLUSH', N'SQLTRACE_INCREMENTAL_FLUSH_SLEEP', N'SQLTRACE_WAIT_ENTRIES',
		N'WAIT_FOR_RESULTS', N'WAITFOR', N'WAITFOR_TASKSHUTDOWN', N'WAIT_XTP_HOST_WAIT',
		N'WAIT_XTP_OFFLINE_CKPT_NEW_LOG', N'WAIT_XTP_CKPT_CLOSE', N'XE_DISPATCHER_JOIN',
        N'XE_DISPATCHER_WAIT', N'XE_TIMER_EVENT')
    AND waiting_tasks_count > 0)
SELECT
    MAX (W1.wait_type) AS [WaitType],
	CAST (MAX (W1.Percentage) AS DECIMAL (5,2)) AS [Wait Percentage],
	CAST ((MAX (W1.WaitS) / MAX (W1.WaitCount)) AS DECIMAL (16,4)) AS [AvgWait_Sec],
    CAST ((MAX (W1.ResourceS) / MAX (W1.WaitCount)) AS DECIMAL (16,4)) AS [AvgRes_Sec],
    CAST ((MAX (W1.SignalS) / MAX (W1.WaitCount)) AS DECIMAL (16,4)) AS [AvgSig_Sec], 
    CAST (MAX (W1.WaitS) AS DECIMAL (16,2)) AS [Wait_Sec],
    CAST (MAX (W1.ResourceS) AS DECIMAL (16,2)) AS [Resource_Sec],
    CAST (MAX (W1.SignalS) AS DECIMAL (16,2)) AS [Signal_Sec],
    MAX (W1.WaitCount) AS [Wait Count],
	CAST (N'https://www.sqlskills.com/help/waits/' + W1.wait_type AS XML) AS [Help/Info URL]
FROM Waits AS W1
INNER JOIN Waits AS W2
ON W2.RowNum <= W1.RowNum
GROUP BY W1.RowNum, W1.wait_type
HAVING SUM (W2.Percentage) - MAX (W1.Percentage) < 99 -- percentage threshold
OPTION (RECOMPILE);
------

-- Cumulative wait stats are not as useful on an idle instance that is not under load or performance pressure

-- SQL Server Wait Types Library (Paul Randal)
-- https://www.sqlskills.com/help/waits/

-- The SQL Server Wait Type Repository
-- http://blogs.msdn.com/b/psssql/archive/2009/11/03/the-sql-server-wait-type-repository.aspx

-- Wait statistics, or please tell me where it hurts
-- https://www.sqlskills.com/blogs/paul/wait-statistics-or-please-tell-me-where-it-hurts/

-- SQL Server 2005 Performance Tuning using the Waits and Queues
-- http://technet.microsoft.com/en-us/library/cc966413.aspx

-- sys.dm_os_wait_stats (Transact-SQL)
-- http://msdn.microsoft.com/en-us/library/ms179984(v=sql.105).aspx




-- Signal Waits for instance  (Query 28) (Signal Waits)
SELECT CAST(100.0 * SUM(signal_wait_time_ms) / SUM (wait_time_ms) AS NUMERIC(20,2)) AS [% Signal (CPU) Waits],
CAST(100.0 * SUM(wait_time_ms - signal_wait_time_ms) / SUM (wait_time_ms) AS NUMERIC(20,2)) AS [% Resource Waits]
FROM sys.dm_os_wait_stats WITH (NOLOCK)
WHERE wait_type NOT IN (
        N'BROKER_EVENTHANDLER', N'BROKER_RECEIVE_WAITFOR', N'BROKER_TASK_STOP',
		N'BROKER_TO_FLUSH', N'BROKER_TRANSMITTER', N'CHECKPOINT_QUEUE',
        N'CHKPT', N'CLR_AUTO_EVENT', N'CLR_MANUAL_EVENT', N'CLR_SEMAPHORE',
        N'DBMIRROR_DBM_EVENT', N'DBMIRROR_EVENTS_QUEUE', N'DBMIRROR_WORKER_QUEUE',
		N'DBMIRRORING_CMD', N'DIRTY_PAGE_POLL', N'DISPATCHER_QUEUE_SEMAPHORE',
        N'EXECSYNC', N'FSAGENT', N'FT_IFTS_SCHEDULER_IDLE_WAIT', N'FT_IFTSHC_MUTEX',
        N'HADR_CLUSAPI_CALL', N'HADR_FILESTREAM_IOMGR_IOCOMPLETION', N'HADR_LOGCAPTURE_WAIT', 
		N'HADR_NOTIFICATION_DEQUEUE', N'HADR_TIMER_TASK', N'HADR_WORK_QUEUE',
        N'KSOURCE_WAKEUP', N'LAZYWRITER_SLEEP', N'LOGMGR_QUEUE', N'ONDEMAND_TASK_QUEUE',
        N'PWAIT_ALL_COMPONENTS_INITIALIZED', N'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP',
        N'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP', N'REQUEST_FOR_DEADLOCK_SEARCH',
		N'RESOURCE_QUEUE', N'SERVER_IDLE_CHECK', N'SLEEP_BPOOL_FLUSH', N'SLEEP_DBSTARTUP',
		N'SLEEP_DCOMSTARTUP', N'SLEEP_MASTERDBREADY', N'SLEEP_MASTERMDREADY',
        N'SLEEP_MASTERUPGRADED', N'SLEEP_MSDBSTARTUP', N'SLEEP_SYSTEMTASK', N'SLEEP_TASK',
        N'SLEEP_TEMPDBSTARTUP', N'SNI_HTTP_ACCEPT', N'SP_SERVER_DIAGNOSTICS_SLEEP',
		N'SQLTRACE_BUFFER_FLUSH', N'SQLTRACE_INCREMENTAL_FLUSH_SLEEP', N'SQLTRACE_WAIT_ENTRIES',
		N'WAIT_FOR_RESULTS', N'WAITFOR', N'WAITFOR_TASKSHUTDOWN', N'WAIT_XTP_HOST_WAIT',
		N'WAIT_XTP_OFFLINE_CKPT_NEW_LOG', N'WAIT_XTP_CKPT_CLOSE', N'XE_DISPATCHER_JOIN',
        N'XE_DISPATCHER_WAIT', N'XE_TIMER_EVENT') OPTION (RECOMPILE);
------

-- Signal Waits above 10-15% is usually a confirming sign of CPU pressure
-- Cumulative wait stats are not as useful on an idle instance that is not under load or performance pressure
-- Resource waits are non-CPU related waits


--  Get logins that are connected and how many sessions they have (Query 29) (Connection Counts)
SELECT login_name, [program_name], COUNT(session_id) AS [session_count] 
FROM sys.dm_exec_sessions WITH (NOLOCK)
GROUP BY login_name, [program_name]
ORDER BY COUNT(session_id) DESC OPTION (RECOMPILE);
------

-- This can help characterize your workload and
-- determine whether you are seeing a normal level of activity


-- Get a count of SQL connections by IP address (Query 30) (Connection Counts by IP Address)
SELECT ec.client_net_address, es.[program_name], es.[host_name], es.login_name, 
COUNT(ec.session_id) AS [connection count] 
FROM sys.dm_exec_sessions AS es WITH (NOLOCK) 
INNER JOIN sys.dm_exec_connections AS ec WITH (NOLOCK) 
ON es.session_id = ec.session_id 
GROUP BY ec.client_net_address, es.[program_name], es.[host_name], es.login_name  
ORDER BY ec.client_net_address, es.[program_name] OPTION (RECOMPILE);
------

-- This helps you figure where your database load is coming from
-- and verifies connectivity from other machines


-- Get Average Task Counts (run multiple times) (Query 31) (Avg Task Counts)
SELECT AVG(current_tasks_count) AS [Avg Task Count], 
AVG(runnable_tasks_count) AS [Avg Runnable Task Count],
AVG(pending_disk_io_count) AS [Avg Pending DiskIO Count]
FROM sys.dm_os_schedulers WITH (NOLOCK)
WHERE scheduler_id < 255 OPTION (RECOMPILE);
------

-- Sustained values above 10 suggest further investigation in that area
-- High Avg Task Counts are often caused by blocking/deadlocking or other resource contention

-- Sustained values above 1 suggest further investigation in that area
-- High Avg Runnable Task Counts are a good sign of CPU pressure
-- High Avg Pending DiskIO Counts are a sign of disk pressure

-- How to Do Some Very Basic SQL Server Monitoring
-- https://www.sqlskills.com/blogs/glenn/how-to-do-some-very-basic-sql-server-monitoring/



-- Get CPU Utilization History for last 256 minutes (in one minute intervals)  (Query 32) (CPU Utilization History)
-- This version works with SQL Server 2008 R2
DECLARE @ts_now bigint = (SELECT cpu_ticks/(cpu_ticks/ms_ticks) FROM sys.dm_os_sys_info WITH (NOLOCK)); 

SELECT TOP(256) SQLProcessUtilization AS [SQL Server Process CPU Utilization], 
               SystemIdle AS [System Idle Process], 
               100 - SystemIdle - SQLProcessUtilization AS [Other Process CPU Utilization], 
               DATEADD(ms, -1 * (@ts_now - [timestamp]), GETDATE()) AS [Event Time] 
FROM ( 
	  SELECT record.value('(./Record/@id)[1]', 'int') AS record_id, 
			record.value('(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]', 'int') 
			AS [SystemIdle], 
			record.value('(./Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization)[1]', 
			'int') 
			AS [SQLProcessUtilization], [timestamp] 
	  FROM ( 
			SELECT [timestamp], CONVERT(xml, record) AS [record] 
			FROM sys.dm_os_ring_buffers WITH (NOLOCK)
			WHERE ring_buffer_type = N'RING_BUFFER_SCHEDULER_MONITOR' 
			AND record LIKE N'%<SystemHealth>%') AS x 
	  ) AS y 
ORDER BY record_id DESC OPTION (RECOMPILE);
------

-- Look at the trend over the entire period. 
-- Also look at high sustained Other Process CPU Utilization values


-- Get top total worker time queries for entire instance (Query 33) (Top Worker Time Queries)
SELECT TOP(50) DB_NAME(t.[dbid]) AS [Database Name], t.[text] AS [Query Text],  
qs.total_worker_time AS [Total Worker Time], qs.min_worker_time AS [Min Worker Time],
qs.total_worker_time/qs.execution_count AS [Avg Worker Time], 
qs.max_worker_time AS [Max Worker Time], qs.execution_count AS [Execution Count], 
qs.total_elapsed_time/qs.execution_count AS [Avg Elapsed Time], 
qs.total_logical_reads/qs.execution_count AS [Avg Logical Reads], 
qs.total_physical_reads/qs.execution_count AS [Avg Physical Reads], qs.creation_time AS [Creation Time]
, qp.query_plan AS [Query Plan] -- comment out this column if copying results to Excel
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t 
CROSS APPLY sys.dm_exec_query_plan(plan_handle) AS qp 
ORDER BY qs.total_worker_time DESC OPTION (RECOMPILE);
------


-- Helps you find the most expensive queries from a CPU perspective across the entire instance



-- Good basic information about OS memory amounts and state  (Query 34) (System Memory)
SELECT total_physical_memory_kb/1024 AS [Physical Memory (MB)], 
       available_physical_memory_kb/1024 AS [Available Memory (MB)], 
       total_page_file_kb/1024 AS [Total Page File (MB)], 
	   available_page_file_kb/1024 AS [Available Page File (MB)], 
	   system_cache_kb/1024 AS [System Cache (MB)],
       system_memory_state_desc AS [System Memory State]
FROM sys.dm_os_sys_memory WITH (NOLOCK) OPTION (RECOMPILE);
------

-- You want to see "Available physical memory is high"
-- This indicates that you are not under external memory pressure


-- SQL Server Process Address space info  (Query 35) (Process Memory) 
-- (shows whether locked pages is enabled, among other things)
SELECT physical_memory_in_use_kb/1024 AS [SQL Server Memory Usage (MB)],
       large_page_allocations_kb, locked_page_allocations_kb, page_fault_count, 
	   memory_utilization_percentage, available_commit_limit_kb, 
	   process_physical_memory_low, process_virtual_memory_low
FROM sys.dm_os_process_memory WITH (NOLOCK) OPTION (RECOMPILE);
------

-- You want to see 0 for process_physical_memory_low
-- You want to see 0 for process_virtual_memory_low
-- This indicates that you are not under internal memory pressure


-- Page Life Expectancy (PLE) value for each NUMA node in current instance  (Query 36) (PLE by NUMA Node)
SELECT @@SERVERNAME AS [Server Name], RTRIM([object_name]) AS [Object Name], instance_name, cntr_value AS [Page Life Expectancy]
FROM sys.dm_os_performance_counters WITH (NOLOCK)
WHERE [object_name] LIKE N'%Buffer Node%' -- Handles named instances
AND counter_name = N'Page life expectancy' OPTION (RECOMPILE);
------

-- PLE is a good measurement of memory pressure.
-- Higher PLE is better. Watch the trend over time, not the absolute value.
-- This will only return one row for non-NUMA systems.

-- Page Life Expectancy isn�t what you think�
-- https://www.sqlskills.com/blogs/paul/page-life-expectancy-isnt-what-you-think/



-- Memory Grants Pending value for current instance  (Query 37) (Memory Grants Pending)
SELECT @@SERVERNAME AS [Server Name], RTRIM([object_name]) AS [Object Name], cntr_value AS [Memory Grants Pending]                                                                                                       
FROM sys.dm_os_performance_counters WITH (NOLOCK)
WHERE [object_name] LIKE N'%Memory Manager%' -- Handles named instances
AND counter_name = N'Memory Grants Pending' OPTION (RECOMPILE);
------

-- Memory Grants Pending above zero for a sustained period is a very strong indicator of memory pressure


-- Memory Clerk Usage for instance  (Query 38) (Memory Clerk Usage)
-- Look for high value for CACHESTORE_SQLCP (Ad-hoc query plans)
SELECT TOP(10) [type] AS [Memory Clerk Type], SUM(single_pages_kb)/1024 AS [SPA Memory Usage (MB)] 
FROM sys.dm_os_memory_clerks WITH (NOLOCK)
GROUP BY [type]  
ORDER BY SUM(single_pages_kb) DESC OPTION (RECOMPILE);
------

-- CACHESTORE_SQLCP  SQL Plans         
-- These are cached SQL statements or batches that aren't in stored procedures, functions and triggers
-- Watch out for high values for CACHESTORE_SQLCP

-- CACHESTORE_OBJCP  Object Plans      
-- These are compiled plans for stored procedures, functions and triggers



-- Find single-use, ad-hoc and prepared queries that are bloating the plan cache  (Query 39) (Ad hoc Queries)
SELECT TOP(50) DB_NAME(t.[dbid]) AS [Database Name], t.[text] AS [Query Text], 
cp.objtype AS [Object Type], cp.cacheobjtype AS [Cache Object Type],  
cp.size_in_bytes/1024 AS [Plan Size in KB]
FROM sys.dm_exec_cached_plans AS cp WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t
WHERE cp.cacheobjtype = N'Compiled Plan' 
AND cp.objtype IN (N'Adhoc', N'Prepared') 
AND cp.usecounts = 1
ORDER BY cp.size_in_bytes DESC, DB_NAME(t.[dbid]) OPTION (RECOMPILE);
------

-- Gives you the text, type and size of single-use ad-hoc and prepared queries that waste space in the plan cache
-- Enabling 'optimize for ad hoc workloads' for the instance can help (SQL Server 2008 and above only)
-- Running DBCC FREESYSTEMCACHE ('SQL Plans') periodically may be required to better control this.
-- Enabling forced parameterization for the database can help, but test first!

-- Plan cache, adhoc workloads and clearing the single-use plan cache bloat
-- https://www.sqlskills.com/blogs/kimberly/plan-cache-adhoc-workloads-and-clearing-the-single-use-plan-cache-bloat/



-- Database specific queries *****************************************************************

-- **** Please switch to a user database that you are interested in! *****
USE YourDatabaseName; -- make sure to change to an actual database on your instance, not the master system database
GO

-- Individual File Sizes and space available for current database  (Query 40) (File Sizes and Space)
SELECT f.name AS [File Name] , f.physical_name AS [Physical Name], 
CAST((f.size/128.0) AS DECIMAL(15,2)) AS [Total Size in MB],
CAST(f.size/128.0 - CAST(FILEPROPERTY(f.name, 'SpaceUsed') AS int)/128.0 AS DECIMAL(15,2)) 
AS [Available Space In MB], [file_id], fg.name AS [Filegroup Name]
FROM sys.database_files AS f WITH (NOLOCK) 
LEFT OUTER JOIN sys.data_spaces AS fg WITH (NOLOCK) 
ON f.data_space_id = fg.data_space_id OPTION (RECOMPILE);
------

-- Look at how large and how full the files are and where they are located
-- Make sure the transaction log is not full!!



-- I/O Statistics by file for the current database  (Query 41) (IO Stats By File)
SELECT DB_NAME(DB_ID()) AS [Database Name], df.name AS [Logical Name], vfs.[file_id], 
df.physical_name AS [Physical Name], vfs.num_of_reads, vfs.num_of_writes, vfs.io_stall_read_ms, vfs.io_stall_write_ms,
CAST(100. * vfs.io_stall_read_ms/(vfs.io_stall_read_ms + vfs.io_stall_write_ms) AS DECIMAL(10,1)) AS [IO Stall Reads Pct],
CAST(100. * vfs.io_stall_write_ms/(vfs.io_stall_write_ms + vfs.io_stall_read_ms) AS DECIMAL(10,1)) AS [IO Stall Writes Pct],
(vfs.num_of_reads + vfs.num_of_writes) AS [Writes + Reads], 
CAST(vfs.num_of_bytes_read/1048576.0 AS DECIMAL(10, 2)) AS [MB Read], 
CAST(vfs.num_of_bytes_written/1048576.0 AS DECIMAL(10, 2)) AS [MB Written],
CAST(100. * vfs.num_of_reads/(vfs.num_of_reads + vfs.num_of_writes) AS DECIMAL(10,1)) AS [# Reads Pct],
CAST(100. * vfs.num_of_writes/(vfs.num_of_reads + vfs.num_of_writes) AS DECIMAL(10,1)) AS [# Write Pct],
CAST(100. * vfs.num_of_bytes_read/(vfs.num_of_bytes_read + vfs.num_of_bytes_written) AS DECIMAL(10,1)) AS [Read Bytes Pct],
CAST(100. * vfs.num_of_bytes_written/(vfs.num_of_bytes_read + vfs.num_of_bytes_written) AS DECIMAL(10,1)) AS [Written Bytes Pct]
FROM sys.dm_io_virtual_file_stats(DB_ID(), NULL) AS vfs
INNER JOIN sys.database_files AS df WITH (NOLOCK)
ON vfs.[file_id]= df.[file_id] OPTION (RECOMPILE);
------

-- This helps you characterize your workload better from an I/O perspective for this database
-- It helps you determine whether you has an OLTP or DW/DSS type of workload



-- Top cached queries by Execution Count (SQL Server 2008 R2)  (Query 42) (Query Execution Counts)
-- SQL Server 2008 R2 SP1 and greater only
SELECT TOP (100) qs.execution_count, qs.total_rows, qs.last_rows, qs.min_rows, qs.max_rows,
qs.last_elapsed_time, qs.min_elapsed_time, qs.max_elapsed_time,
total_worker_time, total_logical_reads, 
SUBSTRING(qt.TEXT,qs.statement_start_offset/2 +1,
(CASE WHEN qs.statement_end_offset = -1
			THEN LEN(CONVERT(NVARCHAR(MAX), qt.TEXT)) * 2
	  ELSE qs.statement_end_offset END - qs.statement_start_offset)/2) AS query_text 
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS qt
ORDER BY qs.execution_count DESC OPTION (RECOMPILE);
------

-- Uses several new rows returned columns to help troubleshoot performance problems


-- Top Cached SPs By Execution Count (SQL 2008 R2) (Query 43) (SP Execution Counts)
SELECT TOP(100) p.name AS [SP Name], qs.execution_count,
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute],
qs.total_worker_time/qs.execution_count AS [AvgWorkerTime], qs.total_worker_time AS [TotalWorkerTime],  
qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time],
qs.cached_time
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
WHERE qs.database_id = DB_ID()
ORDER BY qs.execution_count DESC OPTION (RECOMPILE);
------

-- Tells you which cached stored procedures are called the most often
-- This helps you characterize and baseline your workload


-- Top Cached SPs By Avg Elapsed Time (SQL 2008 R2)  (Query 44) (SP Avg Elapsed Time) 
SELECT TOP(25) p.name AS [SP Name], qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time], 
qs.total_elapsed_time, qs.execution_count, ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, 
GETDATE()), 0) AS [Calls/Minute], qs.total_worker_time/qs.execution_count AS [AvgWorkerTime], 
qs.total_worker_time AS [TotalWorkerTime], qs.cached_time
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
WHERE qs.database_id = DB_ID()
ORDER BY avg_elapsed_time DESC OPTION (RECOMPILE);
------

-- This helps you find long-running cached stored procedures that
-- may be easy to optimize with standard query tuning techniques


-- Top Cached SPs By Avg Elapsed Time with execution time variability   (Query 45) (SP Avg Elapsed Variable Time)
SELECT TOP(25) p.name AS [SP Name], qs.execution_count, qs.min_elapsed_time,
qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time],
qs.max_elapsed_time, qs.last_elapsed_time,  qs.cached_time
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
WHERE qs.database_id = DB_ID()
ORDER BY avg_elapsed_time DESC OPTION (RECOMPILE);
------

-- This gives you some interesting information about the variability in the
-- execution time of your cached stored procedures, which is useful for tuning


-- Top Cached SPs By Total Worker time (SQL 2008 R2). Worker time relates to CPU cost  (Query 46) (SP Worker Time)
SELECT TOP(25) p.name AS [SP Name], qs.total_worker_time AS [TotalWorkerTime], 
qs.total_worker_time/qs.execution_count AS [AvgWorkerTime], qs.execution_count, 
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute],
qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count 
AS [avg_elapsed_time], qs.cached_time
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
WHERE qs.database_id = DB_ID()
ORDER BY qs.total_worker_time DESC OPTION (RECOMPILE);

-- This helps you find the most expensive cached stored procedures from a CPU perspective
-- You should look at this if you see signs of CPU pressure


-- Top Cached SPs By Total Logical Reads (SQL 2008 R2). Logical reads relate to memory pressure  (Query 47) (SP Logical Reads)
SELECT TOP(25) p.name AS [SP Name], qs.total_logical_reads AS [TotalLogicalReads], 
qs.total_logical_reads/qs.execution_count AS [AvgLogicalReads],qs.execution_count, 
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute], 
qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count 
AS [avg_elapsed_time], qs.cached_time
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
WHERE qs.database_id = DB_ID()
ORDER BY qs.total_logical_reads DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a memory perspective
-- You should look at this if you see signs of memory pressure


-- Top Cached SPs By Total Physical Reads (SQL 2008 R2). Physical reads relate to disk I/O pressure  (Query 48) (SP Physical Reads)
SELECT TOP(25) p.name AS [SP Name],qs.total_physical_reads AS [TotalPhysicalReads], 
qs.total_physical_reads/qs.execution_count AS [AvgPhysicalReads], qs.execution_count, 
qs.total_logical_reads,qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count 
AS [avg_elapsed_time], qs.cached_time 
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
WHERE qs.database_id = DB_ID()
AND qs.total_physical_reads > 0
ORDER BY qs.total_physical_reads DESC, qs.total_logical_reads DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a read I/O perspective
-- You should look at this if you see signs of I/O pressure or of memory pressure
       
-- Top Cached SPs By Total Logical Writes (SQL 2008 R2)  (Query 49) (SP Logical Writes)
-- Logical writes relate to both memory and disk I/O pressure 
SELECT TOP(25) p.name AS [SP Name], qs.total_logical_writes AS [TotalLogicalWrites], 
qs.total_logical_writes/qs.execution_count AS [AvgLogicalWrites], qs.execution_count,
ISNULL(qs.execution_count/DATEDIFF(Second, qs.cached_time, GETDATE()), 0) AS [Calls/Second],
qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time], 
qs.cached_time
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
WHERE qs.database_id = DB_ID()
AND qs.total_logical_writes > 0
ORDER BY qs.total_logical_writes DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a write I/O perspective
-- You should look at this if you see signs of I/O pressure or of memory pressure


-- Lists the top statements by average input/output usage for the current database  (Query 50) (Top IO Statements)
SELECT TOP(50) OBJECT_NAME(qt.objectid, dbid) AS [SP Name],
(qs.total_logical_reads + qs.total_logical_writes) /qs.execution_count AS [Avg IO], qs.execution_count AS [Execution Count],
SUBSTRING(qt.[text],qs.statement_start_offset/2, 
	(CASE 
		WHEN qs.statement_end_offset = -1 
	 THEN LEN(CONVERT(nvarchar(max), qt.[text])) * 2 
		ELSE qs.statement_end_offset 
	 END - qs.statement_start_offset)/2) AS [Query Text]	
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS qt
WHERE qt.[dbid] = DB_ID()
ORDER BY [Avg IO] DESC OPTION (RECOMPILE);
------

-- Helps you find the most expensive statements for I/O by SP



-- Possible Bad NC Indexes (writes > reads)  (Query 51) (Bad NC Indexes)
SELECT OBJECT_NAME(s.[object_id]) AS [Table Name], i.name AS [Index Name], i.index_id, 
i.is_disabled, i.is_hypothetical, i.has_filter, i.fill_factor,
user_updates AS [Total Writes], user_seeks + user_scans + user_lookups AS [Total Reads],
user_updates - (user_seeks + user_scans + user_lookups) AS [Difference]
FROM sys.dm_db_index_usage_stats AS s WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON s.[object_id] = i.[object_id]
AND i.index_id = s.index_id
WHERE OBJECTPROPERTY(s.[object_id],'IsUserTable') = 1
AND s.database_id = DB_ID()
AND s.user_updates > (s.user_seeks + s.user_scans + s.user_lookups)
AND i.index_id > 1 AND i.[type_desc] = N'NONCLUSTERED'
AND i.is_primary_key = 0 AND i.is_unique_constraint = 0
ORDER BY [Difference] DESC, [Total Writes] DESC, [Total Reads] ASC OPTION (RECOMPILE);
------

-- Look for indexes with high numbers of writes and zero or very low numbers of reads
-- Consider your complete workload, and how long your instance has been running
-- Investigate further before dropping an index!


-- Missing Indexes for current database by Index Advantage  (Query 52) (Missing Indexes)
SELECT DISTINCT CONVERT(decimal(18,2), user_seeks * avg_total_user_cost * (avg_user_impact * 0.01)) AS [index_advantage], 
migs.last_user_seek, mid.[statement] AS [Database.Schema.Table],
mid.equality_columns, mid.inequality_columns, mid.included_columns,
migs.unique_compiles, migs.user_seeks, migs.avg_total_user_cost, migs.avg_user_impact,
OBJECT_NAME(mid.[object_id]) AS [Table Name], p.rows AS [Table Rows]
FROM sys.dm_db_missing_index_group_stats AS migs WITH (NOLOCK)
INNER JOIN sys.dm_db_missing_index_groups AS mig WITH (NOLOCK)
ON migs.group_handle = mig.index_group_handle
INNER JOIN sys.dm_db_missing_index_details AS mid WITH (NOLOCK)
ON mig.index_handle = mid.index_handle
INNER JOIN sys.partitions AS p WITH (NOLOCK)
ON p.[object_id] = mid.[object_id]
WHERE mid.database_id = DB_ID()
AND p.index_id < 2 
ORDER BY index_advantage DESC OPTION (RECOMPILE);
------

-- Look at index advantage, last user seek time, number of user seeks to help determine source and importance
-- SQL Server is overly eager to add included columns, so beware
-- Do not just blindly add indexes that show up from this query!!!


-- Find missing index warnings for cached plans in the current database  (Query 53) (Missing Index Warnings)
-- Note: This query could take some time on a busy instance
SELECT TOP(25) OBJECT_NAME(objectid) AS [ObjectName], 
               query_plan, cp.objtype, cp.usecounts
FROM sys.dm_exec_cached_plans AS cp WITH (NOLOCK)
CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) AS qp
WHERE CAST(query_plan AS NVARCHAR(MAX)) LIKE N'%MissingIndex%'
AND dbid = DB_ID()
ORDER BY cp.usecounts DESC OPTION (RECOMPILE);
------

-- Helps you connect missing indexes to specific stored procedures
-- This can help you decide whether to add them or not


-- Breaks down buffers used by current database by object (table, index) in the buffer cache  (Query 54) (Buffer Usage)
-- Note: This query could take some time on a busy instance
SELECT OBJECT_NAME(p.[object_id]) AS [Object Name], p.index_id, 
CAST(COUNT(*)/128.0 AS DECIMAL(10, 2)) AS [Buffer size(MB)],  
COUNT(*) AS [BufferCount], p.Rows AS [Row Count],
p.data_compression_desc AS [Compression Type]
FROM sys.allocation_units AS a WITH (NOLOCK)
INNER JOIN sys.dm_os_buffer_descriptors AS b WITH (NOLOCK)
ON a.allocation_unit_id = b.allocation_unit_id
INNER JOIN sys.partitions AS p WITH (NOLOCK)
ON a.container_id = p.hobt_id
WHERE b.database_id = CONVERT(int,DB_ID())
AND p.[object_id] > 100
AND OBJECT_NAME(p.[object_id]) NOT LIKE N'plan_%'
AND OBJECT_NAME(p.[object_id]) NOT LIKE N'sys%'
AND OBJECT_NAME(p.[object_id]) NOT LIKE N'xml_index_nodes%'
GROUP BY p.[object_id], p.index_id, p.data_compression_desc, p.[Rows]
ORDER BY [BufferCount] DESC OPTION (RECOMPILE);
------

-- Tells you what tables and indexes are using the most memory in the buffer cache
-- It can help identify possible candidates for data compression


-- Get Table names, row counts, and compression status for clustered index or heap  (Query 55) (Table Sizes)
SELECT OBJECT_NAME(object_id) AS [ObjectName], 
SUM(Rows) AS [RowCount], data_compression_desc AS [CompressionType]
FROM sys.partitions WITH (NOLOCK)
WHERE index_id < 2 --ignore the partitions from the non-clustered index if any
AND OBJECT_NAME(object_id) NOT LIKE N'sys%'
AND OBJECT_NAME(object_id) NOT LIKE N'queue_%' 
AND OBJECT_NAME(object_id) NOT LIKE N'filestream_tombstone%' 
AND OBJECT_NAME(object_id) NOT LIKE N'fulltext%'
AND OBJECT_NAME(object_id) NOT LIKE N'ifts_comp_fragment%'
AND OBJECT_NAME(object_id) NOT LIKE N'xml_index_nodes%'
GROUP BY object_id, data_compression_desc
ORDER BY SUM(Rows) DESC OPTION (RECOMPILE);
------

-- Gives you an idea of table sizes, and possible data compression opportunities



-- Get some key table properties (Query 56) (Table Properties)
SELECT [name], create_date, lock_on_bulk_load, is_replicated, has_replication_filter, 
       is_tracked_by_cdc, lock_escalation_desc
FROM sys.tables WITH (NOLOCK) 
ORDER BY [name] OPTION (RECOMPILE);
------

-- Gives you some good information about your tables


-- Detect blocking (run multiple times)  (Query 57) (Detect Blocking)
SELECT t1.resource_type AS [lock type], DB_NAME(resource_database_id) AS [database],
t1.resource_associated_entity_id AS [blk object],t1.request_mode AS [lock req],  --- lock requested
t1.request_session_id AS [waiter sid], t2.wait_duration_ms AS [wait time],       -- spid of waiter  
(SELECT [text] FROM sys.dm_exec_requests AS r WITH (NOLOCK)                      -- get sql for waiter
CROSS APPLY sys.dm_exec_sql_text(r.[sql_handle]) 
WHERE r.session_id = t1.request_session_id) AS [waiter_batch],
(SELECT SUBSTRING(qt.[text],r.statement_start_offset/2, 
    (CASE WHEN r.statement_end_offset = -1 
    THEN LEN(CONVERT(nvarchar(max), qt.[text])) * 2 
    ELSE r.statement_end_offset END - r.statement_start_offset)/2) 
FROM sys.dm_exec_requests AS r WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(r.[sql_handle]) AS qt
WHERE r.session_id = t1.request_session_id) AS [waiter_stmt],					-- statement blocked
t2.blocking_session_id AS [blocker sid],										-- spid of blocker
(SELECT [text] FROM sys.sysprocesses AS p										-- get sql for blocker
CROSS APPLY sys.dm_exec_sql_text(p.[sql_handle]) 
WHERE p.spid = t2.blocking_session_id) AS [blocker_stmt]
FROM sys.dm_tran_locks AS t1 WITH (NOLOCK)
INNER JOIN sys.dm_os_waiting_tasks AS t2 WITH (NOLOCK)
ON t1.lock_owner_address = t2.resource_address OPTION (RECOMPILE);
------

-- Helps troubleshoot blocking and deadlocking issues
-- The results will change from second to second on a busy system
-- You should run this query multiple times when you see signs of blocking



-- When were Statistics last updated on all indexes?  (Query 58) (Statistics Update)
SELECT SCHEMA_NAME(o.Schema_ID) + N'.' + o.NAME AS [Object Name], o.type_desc AS [Object Type],
      i.name AS [Index Name], STATS_DATE(i.[object_id], i.index_id) AS [Statistics Date], 
      s.auto_created, s.no_recompute, s.user_created, st.row_count, st.used_page_count
FROM sys.objects AS o WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON o.[object_id] = i.[object_id]
INNER JOIN sys.stats AS s WITH (NOLOCK)
ON i.[object_id] = s.[object_id] 
AND i.index_id = s.stats_id
INNER JOIN sys.dm_db_partition_stats AS st WITH (NOLOCK)
ON o.[object_id] = st.[object_id]
AND i.[index_id] = st.[index_id]
WHERE o.[type] IN ('U', 'V')
AND st.row_count > 0
ORDER BY STATS_DATE(i.[object_id], i.index_id) DESC OPTION (RECOMPILE);
------  

-- Helps discover possible problems with out-of-date statistics
-- Also gives you an idea which indexes are the most active


-- Look at most frequently modified indexes and statistics (Query 59) (Volatile Indexes)
-- Requires SQL Server 2008 R2 SP2 or newer
SELECT o.name AS [Object Name], o.[object_id], o.type_desc, s.name AS [Statistics Name], 
       s.stats_id, s.no_recompute, s.auto_created, 
	   sp.modification_counter, sp.rows, sp.rows_sampled, sp.last_updated
FROM sys.objects AS o WITH (NOLOCK)
INNER JOIN sys.stats AS s WITH (NOLOCK)
ON s.object_id = o.object_id
CROSS APPLY sys.dm_db_stats_properties(s.object_id, s.stats_id) AS sp
WHERE o.type_desc NOT IN (N'SYSTEM_TABLE', N'INTERNAL_TABLE')
AND sp.modification_counter > 0
ORDER BY sp.modification_counter DESC, o.name OPTION (RECOMPILE);
------


-- Get fragmentation info for all indexes above a certain size in the current database  (Query 60) (Index Fragmentation)
-- Note: This query could take some time on a very large database
SELECT DB_NAME(ps.database_id) AS [Database Name], SCHEMA_NAME(o.[schema_id]) AS [Schema Name],
OBJECT_NAME(ps.OBJECT_ID) AS [Object Name], 
i.name AS [Index Name], ps.index_id, ps.index_type_desc, ps.avg_fragmentation_in_percent, 
ps.fragment_count, ps.page_count, i.fill_factor, i.has_filter, i.filter_definition, i.allow_page_locks
FROM sys.dm_db_index_physical_stats(DB_ID(),NULL, NULL, NULL , N'LIMITED') AS ps
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON ps.[object_id] = i.[object_id] 
AND ps.index_id = i.index_id
INNER JOIN sys.objects AS o WITH (NOLOCK)
ON i.[object_id] = o.[object_id]
WHERE ps.database_id = DB_ID()
AND ps.page_count > 2500
ORDER BY ps.avg_fragmentation_in_percent DESC OPTION (RECOMPILE);
------

-- Helps determine whether you have framentation in your relational indexes
-- and how effective your index maintenance strategy is


--- Index Read/Write stats (all tables in current DB) ordered by Reads  (Query 61) (Overall Index Usage - Reads)
SELECT OBJECT_NAME(s.[object_id]) AS [ObjectName], i.name AS [IndexName], i.index_id,
	   user_seeks + user_scans + user_lookups AS [Reads], s.user_updates AS [Writes],  
	   i.type_desc AS [IndexType], i.fill_factor AS [FillFactor], i.has_filter, i.filter_definition, 
	   s.last_user_scan, s.last_user_lookup, s.last_user_seek
FROM sys.dm_db_index_usage_stats AS s WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON s.[object_id] = i.[object_id]
WHERE OBJECTPROPERTY(s.[object_id],'IsUserTable') = 1
AND i.index_id = s.index_id
AND s.database_id = DB_ID()
ORDER BY user_seeks + user_scans + user_lookups DESC OPTION (RECOMPILE); -- Order by reads
------


-- Show which indexes in the current database are most active for Reads


--- Index Read/Write stats (all tables in current DB) ordered by Writes  (Query 62) (Overall Index Usage - Writes)
SELECT OBJECT_NAME(s.[object_id]) AS [ObjectName], i.name AS [IndexName], i.index_id,
	   s.user_updates AS [Writes], user_seeks + user_scans + user_lookups AS [Reads], 
	   i.type_desc AS [IndexType], i.fill_factor AS [FillFactor], i.has_filter, i.filter_definition,
	   s.last_system_update, s.last_user_update
FROM sys.dm_db_index_usage_stats AS s WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON s.[object_id] = i.[object_id]
WHERE OBJECTPROPERTY(s.[object_id],'IsUserTable') = 1
AND i.index_id = s.index_id
AND s.database_id = DB_ID()
ORDER BY s.user_updates DESC OPTION (RECOMPILE);						 -- Order by writes
------

-- Show which indexes in the current database are most active for Writes


-- Get lock waits for current database (Query 63) (Lock Waits)
SELECT o.name AS [table_name], i.name AS [index_name], ios.index_id, ios.partition_number,
		SUM(ios.row_lock_wait_count) AS [total_row_lock_waits], 
		SUM(ios.row_lock_wait_in_ms) AS [total_row_lock_wait_in_ms],
		SUM(ios.page_lock_wait_count) AS [total_page_lock_waits],
		SUM(ios.page_lock_wait_in_ms) AS [total_page_lock_wait_in_ms],
		SUM(ios.page_lock_wait_in_ms)+ SUM(row_lock_wait_in_ms) AS [total_lock_wait_in_ms]
FROM sys.dm_db_index_operational_stats(DB_ID(), NULL, NULL, NULL) AS ios
INNER JOIN sys.objects AS o WITH (NOLOCK)
ON ios.[object_id] = o.[object_id]
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON ios.[object_id] = i.[object_id] 
AND ios.index_id = i.index_id
WHERE o.[object_id] > 100
GROUP BY o.name, i.name, ios.index_id, ios.partition_number
HAVING SUM(ios.page_lock_wait_in_ms)+ SUM(row_lock_wait_in_ms) > 0
ORDER BY total_lock_wait_in_ms DESC OPTION (RECOMPILE);
------

-- This query is helpful for troubleshooting blocking and deadlocking issues


-- Look at recent Full backups for the current database (Query 64) (Recent Full Backups)
SELECT TOP (30) bs.machine_name, bs.server_name, bs.database_name AS [Database Name], bs.recovery_model,
CONVERT (BIGINT, bs.backup_size / 1048576 ) AS [Uncompressed Backup Size (MB)],
CONVERT (BIGINT, bs.compressed_backup_size / 1048576 ) AS [Compressed Backup Size (MB)],
CONVERT (NUMERIC (20,2), (CONVERT (FLOAT, bs.backup_size) /
CONVERT (FLOAT, bs.compressed_backup_size))) AS [Compression Ratio], bs.has_backup_checksums, bs.is_copy_only,
DATEDIFF (SECOND, bs.backup_start_date, bs.backup_finish_date) AS [Backup Elapsed Time (sec)],
bs.backup_finish_date AS [Backup Finish Date], bmf.physical_device_name AS [Backup Location], bmf.physical_block_size
FROM msdb.dbo.backupset AS bs WITH (NOLOCK)
INNER JOIN msdb.dbo.backupmediafamily AS bmf WITH (NOLOCK)
ON bs.media_set_id = bmf.media_set_id  
WHERE DATEDIFF (SECOND, bs.backup_start_date, bs.backup_finish_date) > 0 
AND bs.backup_size > 0
AND bs.type = 'D' -- Change to L if you want Log backups
AND database_name = DB_NAME(DB_ID())
ORDER BY bs.backup_finish_date DESC OPTION (RECOMPILE);
------

-- Are your backup sizes and times changing over time?
-- Are you using backup checksums?
-- Are you doing copy_only backups?
-- Have you done any backup tuning with striped backups, or changing the parameters of the backup command?


-- These three Pluralsight Courses go into more detail about how to run these queries and interpret the results

-- SQL Server 2014 DMV Diagnostic Queries � Part 1 
-- https://bit.ly/2plxCer

-- SQL Server 2014 DMV Diagnostic Queries � Part 2
-- https://bit.ly/2IuJpzI

-- SQL Server 2014 DMV Diagnostic Queries � Part 3
-- https://bit.ly/2FIlCPb



-- Sign up for Microsoft Visual Studio Dev Essentials and get a free three month pass to Pluralsight

-- Microsoft Visual Studio Dev Essentials
-- http://bit.ly/1q6xbDL


-- Sign up for Microsoft Azure Essentials and get lots of free Azure usage credits, MCP exam voucher, three month Pluralsight subscription

-- Microsoft Azure Essentials
-- https://bit.ly/2JMWe8x


-- August 2017 blog series about upgrading and migrating SQL Server
-- https://bit.ly/2ftKVrX
tools\dbatools\bin\diagnosticquery\SQLServerDiagnosticQueries_2008_201806.sql

-- SQL Server 2008 Diagnostic Information Queries
-- Glenn Berry 
-- Last Modified: July 5, 2018
-- https://sqlserverperformance.wordpress.com/
-- https://www.sqlskills.com/blogs/glenn/
-- Twitter: GlennAlanBerry

-- Please listen to my Pluralsight courses
-- https://www.pluralsight.com/author/glenn-berry

-- If you want to find all of our SQLskills SQL101 blog posts, check out https://www.sqlskills.com/help/sql101/

-- Many of these queries will not work if you have databases in 80 compatibility mode
-- Please make sure you are using the correct version of these diagnostic queries for your version of SQL Server

--******************************************************************************
--*   Copyright (C) 2018 Glenn Berry, SQLskills.com
--*   All rights reserved. 
--*
--*   For more scripts and sample code, check out 
--*      https://www.sqlskills.com/blogs/glenn
--*
--*   You may alter this code for your own *non-commercial* purposes. You may
--*   republish altered code as long as you include this copyright and give due credit. 
--*
--*
--*   THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF 
--*   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED 
--*   TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
--*   PARTICULAR PURPOSE. 
--*
--******************************************************************************

-- Check the major product version to see if it is SQL Server 2008
IF NOT EXISTS (SELECT * WHERE CONVERT(varchar(128), SERVERPROPERTY('ProductVersion')) LIKE '10%')
	BEGIN
		DECLARE @ProductVersion varchar(128) = CONVERT(varchar(128), SERVERPROPERTY('ProductVersion'));
		RAISERROR ('Script does not match the ProductVersion [%s] of this instance. Many of these queries may not work on this version.' , 18 , 16 , @ProductVersion);
	END
	ELSE
		PRINT N'You have the correct major version of SQL Server for this diagnostic information script';


-- Instance level queries *******************************

-- SQL and OS Version information for current instance  (Query 1) (Version Info)
SELECT @@SERVERNAME AS [Server Name], @@VERSION AS [SQL Server and OS Version Info];
------


-- SQL Server 2008 RTM Builds       SQL Server 2008 SP1 Builds       SQL Server 2008 SP2 Builds		      SQL Server 2008 SP3 Builds						SQL Server 2008 SP4 Builds
-- Build           Description      Build          Description		 Build     Description			      Build		    Description							Build		Description
-- 10.0.1600        Gold RTM
-- 10.0.1763        RTM CU1
-- 10.0.1779        RTM CU2
-- 10.0.1787        RTM CU3    -->	10.0.2531		SP1 RTM
-- 10.0.1798        RTM CU4    -->	10.0.2710       SP1 CU1
-- 10.0.1806        RTM CU5    -->	10.0.2714       SP1 CU2 
-- 10.0.1812		RTM CU6    -->	10.0.2723       SP1 CU3
-- 10.0.1818        RTM CU7    -->	10.0.2734       SP1 CU4
-- 10.0.1823        RTM CU8    -->	10.0.2746		SP1 CU5
-- 10.0.1828		RTM CU9    -->	10.0.2757		SP1 CU6
-- 10.0.1835	    RTM CU10   -->	10.0.2766		SP1 CU7
-- RTM Branch Retired          -->	10.0.2775		SP1 CU8		-->  10.0.4000	   SP2 RTM
--								    10.0.2789		SP1 CU9
--								    10.0.2799		SP1 CU10	
--								    10.0.2804		SP1 CU11	-->  10.0.4266     SP2 CU1		
--								    10.0.2808		SP1 CU12	-->  10.0.4272	   SP2 CU2	
--								    10.0.2816	    SP1 CU13    -->  10.0.4279     SP2 CU3	
--								    10.0.2821		SP1 CU14	-->  10.0.4285	   SP2 CU4	-->				10.0.5500		SP3 RTM
--								    10.0.2847		SP1 CU15	-->  10.0.4316	   SP2 CU5  
--								    10.0.2850		SP1 CU16	-->  10.0.4321	   SP2 CU6	-->				10.0.5766		SP3 CU1	      10/17/2011
--                                  SP1 Branch Retired          -->  10.0.4323     SP2 CU7  -->             10.0.5768       SP3 CU2       11/21/2011
--                                                                   10.0.4326	   SP2 CU8  -->             10.0.5770		SP3 CU3       1/16/2012
--														             10.0.4330	   SP2 CU9  -->				10.0.5775		SP3 CU4       3/19/2012
--															         10.0.4332	   SP2 CU10 -->             10.0.5785       SP3 CU5		  5/21/2012
--															         10.0.4333     SP2 CU11 -->			    10.0.5788       SP3 CU6		  7/16/2012
--															         SP2 Branch Retired					    10.0.5794       SP3 CU7		  9/17/2012
--																								            10.0.5828       SP3 CU8		  11/19/2012
--																								            10.0.5829       SP3 CU9       1/21/2013
--																								            10.0.5835       SP3 CU10      3/19/2013
--                                                                                                          10.0.5841       SP3 CU11      5/20/2013
--                                                                                                          10.0.5844       SP3 CU12      7/15/2013
--                                                                                                          10.0.5846       SP3 CU13      9/16/2013
--                                                                                                          10.0.5848       SP3 CU14      11/18/2013
--                                                                                                          10.0.5850		SP3 CU15      1/20/2014
--                                                                                                          10.0.5852       SP3 CU16      3/17/2014
--                                                                                                          10.0.5861       SP3 CU17      5/19/2014			
--                                                                                                          10.0.5867       SP3 CU17+					  10.0.6000		SP4 RTM		9/30/2014
--                                                                                                                                                        10.0.6526     SP4 + HF    2/9/2015 
-- Security Update for SQL Server 2008 SP4 (KB4057114)  https://www.microsoft.com/en-us/download/details.aspx?id=56418                                    10.0.6556		SP4 + HF	1/5/2018
--
-- SQL Server 2008 RTM is considered an "unsupported service pack" as of April 13, 2010
-- SQL Server 2008 SP1 is considered an "unsupported service pack" as of September 19, 2011
-- SQL Server 2008 SP2 is considered an "unsupported service pack" as of September 17, 2012
-- Any build older than 10.0.5500 is on an "unsupported service pack"

-- SQL Server 2008 fell out of Mainstream Support on July 8, 2014

-- The SQL Server 2008 builds that were released after SQL Server 2008 was released
-- http://support.microsoft.com/kb/956909
--
-- The SQL Server 2008 builds that were released after SQL Server 2008 Service Pack 1 was released
-- http://support.microsoft.com/kb/970365
--
-- The SQL Server 2008 builds that were released after SQL Server 2008 Service Pack 2 was released 
-- http://support.microsoft.com/kb/2402659	
--
-- The SQL Server 2008 builds that were released after SQL Server 2008 Service Pack 3 was released
-- http://support.microsoft.com/kb/2629969					   

-- Download SQL Server Management Studio (SSMS)
-- https://msdn.microsoft.com/en-us/library/mt238290.aspx



-- When was SQL Server installed  (Query 2) (SQL Server Install Date)   
SELECT @@SERVERNAME AS [Server Name], create_date AS [SQL Server Install Date] 
FROM sys.server_principals WITH (NOLOCK)
WHERE name = N'NT AUTHORITY\SYSTEM'
OR name = N'NT AUTHORITY\NETWORK SERVICE' OPTION (RECOMPILE);
------

-- Tells you the date and time that SQL Server was installed
-- It is a good idea to know how old your instance is


-- Get selected server properties (Query 3) (Server Properties)
SELECT SERVERPROPERTY('MachineName') AS [MachineName], SERVERPROPERTY('ServerName') AS [ServerName],  
SERVERPROPERTY('InstanceName') AS [Instance], SERVERPROPERTY('IsClustered') AS [IsClustered], 
SERVERPROPERTY('ComputerNamePhysicalNetBIOS') AS [ComputerNamePhysicalNetBIOS], 
SERVERPROPERTY('Edition') AS [Edition], SERVERPROPERTY('ProductLevel') AS [ProductLevel], 
SERVERPROPERTY('ProductVersion') AS [ProductVersion], SERVERPROPERTY('ProcessID') AS [ProcessID],
SERVERPROPERTY('Collation') AS [Collation], SERVERPROPERTY('IsFullTextInstalled') AS [IsFullTextInstalled], 
SERVERPROPERTY('IsIntegratedSecurityOnly') AS [IsIntegratedSecurityOnly];
------

-- This gives you a lot of useful information about your instance of SQL Server,
-- such as the ProcessID for SQL Server and your collation


-- Get SQL Server Agent jobs and Category information (Query 4) (SQL Server Agent Jobs)
SELECT sj.name AS [Job Name], sj.[description] AS [Job Description], SUSER_SNAME(sj.owner_sid) AS [Job Owner],
sj.date_created AS [Date Created], sj.[enabled] AS [Job Enabled], 
sj.notify_email_operator_id, sj.notify_level_email, sc.name AS [CategoryName],
s.[enabled] AS [Sched Enabled], js.next_run_date, js.next_run_time
FROM msdb.dbo.sysjobs AS sj WITH (NOLOCK)
INNER JOIN msdb.dbo.syscategories AS sc WITH (NOLOCK)
ON sj.category_id = sc.category_id
LEFT OUTER JOIN msdb.dbo.sysjobschedules AS js WITH (NOLOCK)
ON sj.job_id = js.job_id
LEFT OUTER JOIN msdb.dbo.sysschedules AS s WITH (NOLOCK)
ON js.schedule_id = s.schedule_id
ORDER BY sj.name OPTION (RECOMPILE);
------

-- Gives you some basic information about your SQL Server Agent jobs, who owns them and how they are configured
-- Look for Agent jobs that are not owned by sa
-- Look for jobs that have a notify_email_operator_id set to 0 (meaning no operator)
-- Look for jobs that have a notify_level_email set to 0 (meaning no e-mail is ever sent)
--
-- MSDN sysjobs documentation
-- http://msdn.microsoft.com/en-us/library/ms189817.aspx


-- Get SQL Server Agent Alert Information (Query 5) (SQL Server Agent Alerts)
SELECT name, event_source, message_id, severity, [enabled], has_notification, 
       delay_between_responses, occurrence_count, last_occurrence_date, last_occurrence_time
FROM msdb.dbo.sysalerts WITH (NOLOCK)
ORDER BY name OPTION (RECOMPILE);
------

-- Gives you some basic information about your SQL Server Agent Alerts (which are different from SQL Server Agent jobs)
-- Read more about Agent Alerts here: https://www.sqlskills.com/blogs/glenn/creating-sql-server-agent-alerts-for-critical-errors/


-- Returns a list of all global trace flags that are enabled (Query 6) (Global Trace Flags)
DBCC TRACESTATUS (-1);
------

-- If no global trace flags are enabled, no results will be returned.
-- It is very useful to know what global trace flags are currently enabled as part of the diagnostic process.

-- Common trace flags that should be enabled in most cases
-- TF 1117 - When growing a data file, grow all files at the same time so they remain the same size, reducing allocation contention points
--           http://support2.microsoft.com/kb/2154845
-- 
-- TF 1118 - Helps alleviate allocation contention in tempdb, SQL Server allocates full extents to each database object, 
--           thereby eliminating the contention on SGAM pages (more important with older versions of SQL Server)
--           Recommendations to reduce allocation contention in SQL Server tempdb database
--           http://support2.microsoft.com/kb/2154845

-- TF 2371 - Lowers auto update statistics threshold for large tables
--           http://blogs.msdn.com/b/saponsqlserver/archive/2011/09/07/changes-to-automatic-update-statistics-in-sql-server-traceflag-2371.aspx

-- TF 3226 - Supresses logging of successful database backup messages to the SQL Server Error Log
--           https://www.sqlskills.com/blogs/paul/fed-up-with-backup-success-messages-bloating-your-error-logs/


-- SQL Server NUMA Node information  (Query 7) (SQL Server NUMA Info)
SELECT node_id, node_state_desc, memory_node_id, online_scheduler_count, 
       active_worker_count, avg_load_balance, resource_monitor_state
FROM sys.dm_os_nodes WITH (NOLOCK) 
WHERE node_state_desc <> N'ONLINE DAC' OPTION (RECOMPILE);
------

-- Gives you some useful information about the composition 
-- and relative load on your NUMA nodes


-- Hardware information from SQL Server 2008  (Query 8) (Hardware Info)
-- (Cannot distinguish between HT and multi-core)
SELECT cpu_count AS [Logical CPU Count], hyperthread_ratio AS [Hyperthread Ratio],
cpu_count/hyperthread_ratio AS [Physical CPU Count], 
physical_memory_in_bytes/1048576 AS [Physical Memory (MB)], sqlserver_start_time 
FROM sys.dm_os_sys_info WITH (NOLOCK) OPTION (RECOMPILE);
------

-- Gives you some good basic hardware information about your database server


-- Get System Manufacturer and model number from  (Query 9) (System Manufacturer)
-- SQL Server Error log. This query might take a few seconds 
-- if you have not recycled your error log recently
EXEC sys.xp_readerrorlog 0, 1, N'Manufacturer';
------ 

-- This can help you determine the capabilities
-- and capacities of your database server


-- Get processor description from Windows Registry  (Query 10) (Processor Description)
EXEC sys.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'HARDWARE\DESCRIPTION\System\CentralProcessor\0', N'ProcessorNameString';
------

-- Gives you the model number and rated clock speed of your processor(s)
-- Your processors may be running at less that the rated clock speed due
-- to the Windows Power Plan or hardware power management

-- You can use CPU-Z to get your actual CPU core speed and a lot of other useful information
-- http://www.cpuid.com/softwares/cpu-z.html

-- You can learn more about processor selection for SQL Server by following this link
-- https://www.sqlskills.com/blogs/glenn/processor-selection-for-sql-server/



-- Get the current node name from your cluster nodes  (Query 11) (Cluster Node Properties)
-- (if your database server is in a failover cluster)
SELECT NodeName
FROM sys.dm_os_cluster_nodes WITH (NOLOCK) OPTION (RECOMPILE);
------

-- Knowing which node owns the cluster resources is critical
-- Especially when you are installing Windows or SQL Server updates
-- You will see no results if your instance is not clustered


-- Get configuration values for instance  (Query 12) (Configuration Values)
SELECT name, value, value_in_use, minimum, maximum, [description], is_dynamic, is_advanced
FROM sys.configurations WITH (NOLOCK)
ORDER BY name OPTION (RECOMPILE);
------

-- Focus on these settings:
-- backup compression default (should be 1 in most cases)
-- clr enabled (only enable if it is needed)
-- cost threshold for parallelism (depends on your workload)
-- lightweight pooling (should be zero)
-- max degree of parallelism (depends on your workload)
-- max server memory (MB) (set to an appropriate value, not the default)
-- optimize for ad hoc workloads (should be 1)
-- priority boost (should be zero)
-- remote admin connections (should be 1)





-- File names and paths for all user and system databases on instance   (Query 13) (Database Filenames and Paths)
SELECT DB_NAME([database_id]) AS [Database Name], 
       [file_id], [name], physical_name, [type_desc], state_desc,
	   is_percent_growth, growth,
	   CONVERT(bigint, growth/128.0) AS [Growth in MB], 
       CONVERT(bigint, size/128.0) AS [Total Size in MB]
FROM sys.master_files WITH (NOLOCK)
ORDER BY DB_NAME([database_id]), [file_id] OPTION (RECOMPILE);
------

-- Things to look at:
-- Are data files and log files on different drives?
-- Is everything on the C: drive?
-- Is TempDB on dedicated drives?
-- Is there only one TempDB data file?
-- Are all of the TempDB data files the same size?
-- Are there multiple data files for user databases?
-- Is percent growth enabled for any files (which is bad)?


-- Look for I/O requests taking longer than 15 seconds in the five most recent SQL Server Error Logs (Query 14) (IO Warnings)
CREATE TABLE #IOWarningResults(LogDate datetime, ProcessInfo sysname, LogText nvarchar(1000));

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 0, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 1, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 2, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 3, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 4, 1, N'taking longer than 15 seconds';

SELECT LogDate, ProcessInfo, LogText
FROM #IOWarningResults
ORDER BY LogDate DESC;

DROP TABLE #IOWarningResults;
------  

-- Finding 15 second I/O warnings in the SQL Server Error Log is useful evidence of
-- poor I/O performance (which might have many different causes)
-- Look to see if you see any patterns in the results (same files, same drives, same time of day, etc.)

-- Diagnostics in SQL Server help detect stalled and stuck I/O operations
-- https://support.microsoft.com/en-us/kb/897284


-- Drive level latency information (Query 15) (Drive Level Latency)
-- Based on code from Jimmy May
SELECT tab.[Drive], tab.volume_mount_point AS [Volume Mount Point], 
	CASE 
		WHEN num_of_reads = 0 THEN 0 
		ELSE (io_stall_read_ms/num_of_reads) 
	END AS [Read Latency],
	CASE 
		WHEN num_of_writes = 0 THEN 0 
		ELSE (io_stall_write_ms/num_of_writes) 
	END AS [Write Latency],
	CASE 
		WHEN (num_of_reads = 0 AND num_of_writes = 0) THEN 0 
		ELSE (io_stall/(num_of_reads + num_of_writes)) 
	END AS [Overall Latency],
	CASE 
		WHEN num_of_reads = 0 THEN 0 
		ELSE (num_of_bytes_read/num_of_reads) 
	END AS [Avg Bytes/Read],
	CASE 
		WHEN num_of_writes = 0 THEN 0 
		ELSE (num_of_bytes_written/num_of_writes) 
	END AS [Avg Bytes/Write],
	CASE 
		WHEN (num_of_reads = 0 AND num_of_writes = 0) THEN 0 
		ELSE ((num_of_bytes_read + num_of_bytes_written)/(num_of_reads + num_of_writes)) 
	END AS [Avg Bytes/Transfer]
FROM (SELECT LEFT(UPPER(mf.physical_name), 2) AS Drive, SUM(num_of_reads) AS num_of_reads,
	         SUM(io_stall_read_ms) AS io_stall_read_ms, SUM(num_of_writes) AS num_of_writes,
	         SUM(io_stall_write_ms) AS io_stall_write_ms, SUM(num_of_bytes_read) AS num_of_bytes_read,
	         SUM(num_of_bytes_written) AS num_of_bytes_written, SUM(io_stall) AS io_stall, vs.volume_mount_point 
      FROM sys.dm_io_virtual_file_stats(NULL, NULL) AS vfs
      INNER JOIN sys.master_files AS mf WITH (NOLOCK)
      ON vfs.database_id = mf.database_id AND vfs.file_id = mf.file_id
	  CROSS APPLY sys.dm_os_volume_stats(mf.database_id, mf.[file_id]) AS vs 
      GROUP BY LEFT(UPPER(mf.physical_name), 2), vs.volume_mount_point) AS tab
ORDER BY [Overall Latency] OPTION (RECOMPILE);
------

-- Shows you the drive-level latency for reads and writes, in milliseconds
-- Latency above 20-25ms is usually a problem


-- Calculates average stalls per read, per write, and per total input/output for each database file  (Query 16) (IO Stalls by File)
SELECT DB_NAME(fs.database_id) AS [Database Name], CAST(fs.io_stall_read_ms/(1.0 + fs.num_of_reads) AS NUMERIC(10,1)) AS [avg_read_stall_ms],
CAST(fs.io_stall_write_ms/(1.0 + fs.num_of_writes) AS NUMERIC(10,1)) AS [avg_write_stall_ms],
CAST((fs.io_stall_read_ms + fs.io_stall_write_ms)/(1.0 + fs.num_of_reads + fs.num_of_writes) AS NUMERIC(10,1)) AS [avg_io_stall_ms],
CONVERT(DECIMAL(18,2), mf.size/128.0) AS [File Size (MB)], mf.physical_name, mf.type_desc, fs.io_stall_read_ms, fs.num_of_reads, 
fs.io_stall_write_ms, fs.num_of_writes, fs.io_stall_read_ms + fs.io_stall_write_ms AS [io_stalls], fs.num_of_reads + fs.num_of_writes AS [total_io]
FROM sys.dm_io_virtual_file_stats(null,null) AS fs
INNER JOIN sys.master_files AS mf WITH (NOLOCK)
ON fs.database_id = mf.database_id
AND fs.[file_id] = mf.[file_id]
ORDER BY avg_io_stall_ms DESC OPTION (RECOMPILE);
------

-- Helps determine which database files on the entire instance have the most I/O bottlenecks
-- This can help you decide whether certain LUNs are overloaded and whether you might
-- want to move some files to a different location or perhaps improve your I/O performance


-- Recovery model, log reuse wait description, log file size, log usage size  (Query 17) (Database Properties)
-- and compatibility level for all databases on instance
SELECT db.[name] AS [Database Name], SUSER_SNAME(db.owner_sid) AS [Database Owner], db.recovery_model_desc AS [Recovery Model], 
db.log_reuse_wait_desc AS [Log Reuse Wait Description], 
ls.cntr_value AS [Log Size (KB)], lu.cntr_value AS [Log Used (KB)],
CAST(CAST(lu.cntr_value AS FLOAT) / CAST(ls.cntr_value AS FLOAT)AS DECIMAL(18,2)) * 100 AS [Log Used %], 
db.[compatibility_level] AS [DB Compatibility Level], 
db.page_verify_option_desc AS [Page Verify Option], db.is_auto_create_stats_on, db.is_auto_update_stats_on,
db.is_auto_update_stats_async_on, db.is_parameterization_forced, 
db.snapshot_isolation_state_desc, db.is_read_committed_snapshot_on,
db.is_auto_close_on, db.is_auto_shrink_on, db.is_cdc_enabled, db.is_published
FROM sys.databases AS db WITH (NOLOCK)
INNER JOIN sys.dm_os_performance_counters AS lu WITH (NOLOCK)
ON db.name = lu.instance_name
INNER JOIN sys.dm_os_performance_counters AS ls WITH (NOLOCK) 
ON db.name = ls.instance_name
WHERE lu.counter_name LIKE N'Log File(s) Used Size (KB)%' 
AND ls.counter_name LIKE N'Log File(s) Size (KB)%'
AND ls.cntr_value > 0 OPTION (RECOMPILE);
------

-- Things to look at:
-- How many databases are on the instance?
-- What recovery models are they using?
-- What is the log reuse wait description?
-- How full are the transaction logs ?
-- What compatibility level are the databases on? 
-- What is the Page Verify Option? (should be CHECKSUM)
-- Is Auto Update Statistics Asynchronously enabled?
-- Make sure auto_shrink and auto_close are not enabled!



-- Missing Indexes for all databases by Index Advantage  (Query 18) (Missing Indexes All Databases)
SELECT CONVERT(decimal(18,2), user_seeks * avg_total_user_cost * (avg_user_impact * 0.01)) AS [index_advantage], 
migs.last_user_seek, mid.[statement] AS [Database.Schema.Table],
mid.equality_columns, mid.inequality_columns, mid.included_columns,
migs.unique_compiles, migs.user_seeks, migs.avg_total_user_cost, migs.avg_user_impact
FROM sys.dm_db_missing_index_group_stats AS migs WITH (NOLOCK)
INNER JOIN sys.dm_db_missing_index_groups AS mig WITH (NOLOCK)
ON migs.group_handle = mig.index_group_handle
INNER JOIN sys.dm_db_missing_index_details AS mid WITH (NOLOCK)
ON mig.index_handle = mid.index_handle
ORDER BY index_advantage DESC OPTION (RECOMPILE);
------

-- Getting missing index information for all of the databases on the instance is very useful
-- Look at last user seek time, number of user seeks to help determine source and importance
-- Also look at avg_user_impact and avg_total_user_cost to help determine importance
-- SQL Server is overly eager to add included columns, so beware
-- Do not just blindly add indexes that show up from this query!!!



-- Get VLF Counts for all databases on the instance (Query 19) (VLF Counts)
-- (adapted from Michelle Ufford) 
CREATE TABLE #VLFInfo (FileID  int,
					   FileSize bigint, StartOffset bigint,
					   FSeqNo      bigint, [Status]    bigint,
					   Parity      bigint, CreateLSN   numeric(38));
	 
CREATE TABLE #VLFCountResults(DatabaseName sysname, VLFCount int);
	 
EXEC sp_MSforeachdb N'Use [?]; 

				INSERT INTO #VLFInfo 
				EXEC sp_executesql N''DBCC LOGINFO([?])''; 
	 
				INSERT INTO #VLFCountResults 
				SELECT DB_NAME(), COUNT(*) 
				FROM #VLFInfo; 

				TRUNCATE TABLE #VLFInfo;'
	 
SELECT DatabaseName, VLFCount  
FROM #VLFCountResults
ORDER BY VLFCount DESC;
	 
DROP TABLE #VLFInfo;
DROP TABLE #VLFCountResults;
------

-- High VLF counts can affect write performance 
-- and they can make full database restores and crash recovery take much longer
-- Try to keep your VLF counts under 200 in most cases



-- Get CPU utilization by database (Query 20) (CPU Usage by Database)
WITH DB_CPU_Stats
AS
(SELECT pa.DatabaseID, DB_Name(pa.DatabaseID) AS [Database Name], SUM(qs.total_worker_time/1000) AS [CPU_Time_Ms]
 FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
 CROSS APPLY (SELECT CONVERT(int, value) AS [DatabaseID] 
              FROM sys.dm_exec_plan_attributes(qs.plan_handle)
              WHERE attribute = N'dbid') AS pa
 GROUP BY DatabaseID)
SELECT ROW_NUMBER() OVER(ORDER BY [CPU_Time_Ms] DESC) AS [CPU Rank],
       [Database Name], [CPU_Time_Ms] AS [CPU Time (ms)], 
       CAST([CPU_Time_Ms] * 1.0 / SUM([CPU_Time_Ms]) OVER() * 100.0 AS DECIMAL(5, 2)) AS [CPU Percent]
FROM DB_CPU_Stats
WHERE DatabaseID <> 32767 -- ResourceDB
ORDER BY [CPU Rank] OPTION (RECOMPILE);
------

-- Helps determine which database is using the most CPU resources on the instance


-- Get I/O utilization by database (Query 21) (IO Usage By Database)
WITH Aggregate_IO_Statistics
AS
(SELECT DB_NAME(database_id) AS [Database Name],
CAST(SUM(num_of_bytes_read + num_of_bytes_written)/1048576 AS DECIMAL(12, 2)) AS io_in_mb
FROM sys.dm_io_virtual_file_stats(NULL, NULL) AS [DM_IO_STATS]
GROUP BY database_id)
SELECT ROW_NUMBER() OVER(ORDER BY io_in_mb DESC) AS [I/O Rank], [Database Name], io_in_mb AS [Total I/O (MB)],
       CAST(io_in_mb/ SUM(io_in_mb) OVER() * 100.0 AS DECIMAL(5,2)) AS [I/O Percent]
FROM Aggregate_IO_Statistics
ORDER BY [I/O Rank] OPTION (RECOMPILE);
------

-- Helps determine which database is using the most I/O resources on the instance


-- Get total buffer usage by database for current instance  (Query 22) (Total Buffer Usage by Database)
-- This make take some time to run on a busy instance
WITH AggregateBufferPoolUsage
AS
(SELECT DB_NAME(database_id) AS [Database Name],
CAST(COUNT(*) * 8/1024.0 AS DECIMAL (10,2))  AS [CachedSize]
FROM sys.dm_os_buffer_descriptors WITH (NOLOCK)
WHERE database_id <> 32767 -- ResourceDB
GROUP BY DB_NAME(database_id))
SELECT ROW_NUMBER() OVER(ORDER BY CachedSize DESC) AS [Buffer Pool Rank], [Database Name], CachedSize AS [Cached Size (MB)],
       CAST(CachedSize / SUM(CachedSize) OVER() * 100.0 AS DECIMAL(5,2)) AS [Buffer Pool Percent]
FROM AggregateBufferPoolUsage
ORDER BY [Buffer Pool Rank] OPTION (RECOMPILE);
------

-- Tells you how much memory (in the buffer pool) 
-- is being used by each database on the instance


-- Clear Wait Stats with this command
-- DBCC SQLPERF('sys.dm_os_wait_stats', CLEAR);

-- Isolate top waits for server instance since last restart or wait statistics clear (Query 23) (Top Waits)
WITH [Waits] 
AS (SELECT wait_type, wait_time_ms/ 1000.0 AS [WaitS],
          (wait_time_ms - signal_wait_time_ms) / 1000.0 AS [ResourceS],
           signal_wait_time_ms / 1000.0 AS [SignalS],
           waiting_tasks_count AS [WaitCount],
           100.0 *  wait_time_ms / SUM (wait_time_ms) OVER() AS [Percentage],
           ROW_NUMBER() OVER(ORDER BY wait_time_ms DESC) AS [RowNum]
    FROM sys.dm_os_wait_stats WITH (NOLOCK)
    WHERE [wait_type] NOT IN (
        N'BROKER_EVENTHANDLER', N'BROKER_RECEIVE_WAITFOR', N'BROKER_TASK_STOP',
		N'BROKER_TO_FLUSH', N'BROKER_TRANSMITTER', N'CHECKPOINT_QUEUE',
        N'CHKPT', N'CLR_AUTO_EVENT', N'CLR_MANUAL_EVENT', N'CLR_SEMAPHORE',
        N'DBMIRROR_DBM_EVENT', N'DBMIRROR_EVENTS_QUEUE', N'DBMIRROR_WORKER_QUEUE',
		N'DBMIRRORING_CMD', N'DIRTY_PAGE_POLL', N'DISPATCHER_QUEUE_SEMAPHORE',
        N'EXECSYNC', N'FSAGENT', N'FT_IFTS_SCHEDULER_IDLE_WAIT', N'FT_IFTSHC_MUTEX',
        N'HADR_CLUSAPI_CALL', N'HADR_FILESTREAM_IOMGR_IOCOMPLETION', N'HADR_LOGCAPTURE_WAIT', 
		N'HADR_NOTIFICATION_DEQUEUE', N'HADR_TIMER_TASK', N'HADR_WORK_QUEUE',
        N'KSOURCE_WAKEUP', N'LAZYWRITER_SLEEP', N'LOGMGR_QUEUE', N'ONDEMAND_TASK_QUEUE',
        N'PWAIT_ALL_COMPONENTS_INITIALIZED', N'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP',
        N'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP', N'REQUEST_FOR_DEADLOCK_SEARCH',
		N'RESOURCE_QUEUE', N'SERVER_IDLE_CHECK', N'SLEEP_BPOOL_FLUSH', N'SLEEP_DBSTARTUP',
		N'SLEEP_DCOMSTARTUP', N'SLEEP_MASTERDBREADY', N'SLEEP_MASTERMDREADY',
        N'SLEEP_MASTERUPGRADED', N'SLEEP_MSDBSTARTUP', N'SLEEP_SYSTEMTASK', N'SLEEP_TASK',
        N'SLEEP_TEMPDBSTARTUP', N'SNI_HTTP_ACCEPT', N'SP_SERVER_DIAGNOSTICS_SLEEP',
		N'SQLTRACE_BUFFER_FLUSH', N'SQLTRACE_INCREMENTAL_FLUSH_SLEEP', N'SQLTRACE_WAIT_ENTRIES',
		N'WAIT_FOR_RESULTS', N'WAITFOR', N'WAITFOR_TASKSHUTDOWN', N'WAIT_XTP_HOST_WAIT',
		N'WAIT_XTP_OFFLINE_CKPT_NEW_LOG', N'WAIT_XTP_CKPT_CLOSE', N'XE_DISPATCHER_JOIN',
        N'XE_DISPATCHER_WAIT', N'XE_TIMER_EVENT')
    AND waiting_tasks_count > 0)
SELECT
    MAX (W1.wait_type) AS [WaitType],
	CAST (MAX (W1.Percentage) AS DECIMAL (5,2)) AS [Wait Percentage],
	CAST ((MAX (W1.WaitS) / MAX (W1.WaitCount)) AS DECIMAL (16,4)) AS [AvgWait_Sec],
    CAST ((MAX (W1.ResourceS) / MAX (W1.WaitCount)) AS DECIMAL (16,4)) AS [AvgRes_Sec],
    CAST ((MAX (W1.SignalS) / MAX (W1.WaitCount)) AS DECIMAL (16,4)) AS [AvgSig_Sec], 
    CAST (MAX (W1.WaitS) AS DECIMAL (16,2)) AS [Wait_Sec],
    CAST (MAX (W1.ResourceS) AS DECIMAL (16,2)) AS [Resource_Sec],
    CAST (MAX (W1.SignalS) AS DECIMAL (16,2)) AS [Signal_Sec],
    MAX (W1.WaitCount) AS [Wait Count],
	CAST (N'https://www.sqlskills.com/help/waits/' + W1.wait_type AS XML) AS [Help/Info URL]
FROM Waits AS W1
INNER JOIN Waits AS W2
ON W2.RowNum <= W1.RowNum
GROUP BY W1.RowNum, W1.wait_type
HAVING SUM (W2.Percentage) - MAX (W1.Percentage) < 99 -- percentage threshold
OPTION (RECOMPILE);
------

-- Cumulative wait stats are not as useful on an idle instance that is not under load or performance pressure

-- SQL Server Wait Types Library (Paul Randal)
-- https://www.sqlskills.com/help/waits/

-- The SQL Server Wait Type Repository
-- http://blogs.msdn.com/b/psssql/archive/2009/11/03/the-sql-server-wait-type-repository.aspx

-- Wait statistics, or please tell me where it hurts
-- https://www.sqlskills.com/blogs/paul/wait-statistics-or-please-tell-me-where-it-hurts/

-- SQL Server 2005 Performance Tuning using the Waits and Queues
-- http://technet.microsoft.com/en-us/library/cc966413.aspx

-- sys.dm_os_wait_stats (Transact-SQL)
-- http://msdn.microsoft.com/en-us/library/ms179984(v=sql.105).aspx




-- Signal Waits for instance  (Query 24) (Signal Waits)
SELECT CAST(100.0 * SUM(signal_wait_time_ms) / SUM (wait_time_ms) AS NUMERIC(20,2)) AS [% Signal (CPU) Waits],
CAST(100.0 * SUM(wait_time_ms - signal_wait_time_ms) / SUM (wait_time_ms) AS NUMERIC(20,2)) AS [% Resource Waits]
FROM sys.dm_os_wait_stats WITH (NOLOCK)
WHERE wait_type NOT IN (
        N'BROKER_EVENTHANDLER', N'BROKER_RECEIVE_WAITFOR', N'BROKER_TASK_STOP',
		N'BROKER_TO_FLUSH', N'BROKER_TRANSMITTER', N'CHECKPOINT_QUEUE',
        N'CHKPT', N'CLR_AUTO_EVENT', N'CLR_MANUAL_EVENT', N'CLR_SEMAPHORE',
        N'DBMIRROR_DBM_EVENT', N'DBMIRROR_EVENTS_QUEUE', N'DBMIRROR_WORKER_QUEUE',
		N'DBMIRRORING_CMD', N'DIRTY_PAGE_POLL', N'DISPATCHER_QUEUE_SEMAPHORE',
        N'EXECSYNC', N'FSAGENT', N'FT_IFTS_SCHEDULER_IDLE_WAIT', N'FT_IFTSHC_MUTEX',
        N'HADR_CLUSAPI_CALL', N'HADR_FILESTREAM_IOMGR_IOCOMPLETION', N'HADR_LOGCAPTURE_WAIT', 
		N'HADR_NOTIFICATION_DEQUEUE', N'HADR_TIMER_TASK', N'HADR_WORK_QUEUE',
        N'KSOURCE_WAKEUP', N'LAZYWRITER_SLEEP', N'LOGMGR_QUEUE', N'ONDEMAND_TASK_QUEUE',
        N'PWAIT_ALL_COMPONENTS_INITIALIZED', N'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP',
        N'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP', N'REQUEST_FOR_DEADLOCK_SEARCH',
		N'RESOURCE_QUEUE', N'SERVER_IDLE_CHECK', N'SLEEP_BPOOL_FLUSH', N'SLEEP_DBSTARTUP',
		N'SLEEP_DCOMSTARTUP', N'SLEEP_MASTERDBREADY', N'SLEEP_MASTERMDREADY',
        N'SLEEP_MASTERUPGRADED', N'SLEEP_MSDBSTARTUP', N'SLEEP_SYSTEMTASK', N'SLEEP_TASK',
        N'SLEEP_TEMPDBSTARTUP', N'SNI_HTTP_ACCEPT', N'SP_SERVER_DIAGNOSTICS_SLEEP',
		N'SQLTRACE_BUFFER_FLUSH', N'SQLTRACE_INCREMENTAL_FLUSH_SLEEP', N'SQLTRACE_WAIT_ENTRIES',
		N'WAIT_FOR_RESULTS', N'WAITFOR', N'WAITFOR_TASKSHUTDOWN', N'WAIT_XTP_HOST_WAIT',
		N'WAIT_XTP_OFFLINE_CKPT_NEW_LOG', N'WAIT_XTP_CKPT_CLOSE', N'XE_DISPATCHER_JOIN',
        N'XE_DISPATCHER_WAIT', N'XE_TIMER_EVENT') OPTION (RECOMPILE);
------

-- Signal Waits above 10-15% is usually a confirming sign of CPU pressure
-- Cumulative wait stats are not as useful on an idle instance that is not under load or performance pressure
-- Resource waits are non-CPU related waits


--  Get logins that are connected and how many sessions they have (Query 25) (Connection Counts)
SELECT login_name, [program_name], COUNT(session_id) AS [session_count] 
FROM sys.dm_exec_sessions WITH (NOLOCK)
GROUP BY login_name, [program_name]
ORDER BY COUNT(session_id) DESC OPTION (RECOMPILE);
------

-- This can help characterize your workload and
-- determine whether you are seeing a normal level of activity


-- Get a count of SQL connections by IP address (Query 26) (Connection Counts by IP Address)
SELECT ec.client_net_address, es.[program_name], es.[host_name], es.login_name, 
COUNT(ec.session_id) AS [connection count] 
FROM sys.dm_exec_sessions AS es WITH (NOLOCK) 
INNER JOIN sys.dm_exec_connections AS ec WITH (NOLOCK) 
ON es.session_id = ec.session_id 
GROUP BY ec.client_net_address, es.[program_name], es.[host_name], es.login_name  
ORDER BY ec.client_net_address, es.[program_name] OPTION (RECOMPILE);
------

-- This helps you figure where your database load is coming from
-- and verifies connectivity from other machines


-- Get Average Task Counts (run multiple times) (Query 27) (Avg Task Counts)
SELECT AVG(current_tasks_count) AS [Avg Task Count], 
AVG(runnable_tasks_count) AS [Avg Runnable Task Count],
AVG(pending_disk_io_count) AS [Avg Pending DiskIO Count]
FROM sys.dm_os_schedulers WITH (NOLOCK)
WHERE scheduler_id < 255 OPTION (RECOMPILE);
------

-- Sustained values above 10 suggest further investigation in that area
-- High Avg Task Counts are often caused by blocking/deadlocking or other resource contention

-- Sustained values above 1 suggest further investigation in that area
-- High Avg Runnable Task Counts are a good sign of CPU pressure
-- High Avg Pending DiskIO Counts are a sign of disk pressure

-- How to Do Some Very Basic SQL Server Monitoring
-- https://www.sqlskills.com/blogs/glenn/how-to-do-some-very-basic-sql-server-monitoring/



-- Get CPU Utilization History for last 256 minutes (in one minute intervals)  (Query 28) (CPU Utilization History)
-- This version works with SQL Server 2008
DECLARE @ts_now bigint = (SELECT cpu_ticks/(cpu_ticks/ms_ticks) FROM sys.dm_os_sys_info WITH (NOLOCK)); 

SELECT TOP(256) SQLProcessUtilization AS [SQL Server Process CPU Utilization], 
               SystemIdle AS [System Idle Process], 
               100 - SystemIdle - SQLProcessUtilization AS [Other Process CPU Utilization], 
               DATEADD(ms, -1 * (@ts_now - [timestamp]), GETDATE()) AS [Event Time] 
FROM ( 
	  SELECT record.value('(./Record/@id)[1]', 'int') AS record_id, 
			record.value('(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]', 'int') 
			AS [SystemIdle], 
			record.value('(./Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization)[1]', 
			'int') 
			AS [SQLProcessUtilization], [timestamp] 
	  FROM ( 
			SELECT [timestamp], CONVERT(xml, record) AS [record] 
			FROM sys.dm_os_ring_buffers WITH (NOLOCK)
			WHERE ring_buffer_type = N'RING_BUFFER_SCHEDULER_MONITOR' 
			AND record LIKE N'%<SystemHealth>%') AS x 
	  ) AS y 
ORDER BY record_id DESC OPTION (RECOMPILE);
------

-- Look at the trend over the entire period. 
-- Also look at high sustained Other Process CPU Utilization values


-- Get top total worker time queries for entire instance (Query 29) (Top Worker Time Queries)
SELECT TOP(50) DB_NAME(t.[dbid]) AS [Database Name], t.[text] AS [Query Text],  
qs.total_worker_time AS [Total Worker Time], qs.min_worker_time AS [Min Worker Time],
qs.total_worker_time/qs.execution_count AS [Avg Worker Time], 
qs.max_worker_time AS [Max Worker Time], qs.execution_count AS [Execution Count], 
qs.total_elapsed_time/qs.execution_count AS [Avg Elapsed Time], 
qs.total_logical_reads/qs.execution_count AS [Avg Logical Reads], 
qs.total_physical_reads/qs.execution_count AS [Avg Physical Reads], qs.creation_time AS [Creation Time]
, qp.query_plan AS [Query Plan] -- comment out this column if copying results to Excel
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t 
CROSS APPLY sys.dm_exec_query_plan(plan_handle) AS qp 
ORDER BY qs.total_worker_time DESC OPTION (RECOMPILE);
------


-- Helps you find the most expensive queries from a CPU perspective across the entire instance



-- Good basic information about OS memory amounts and state  (Query 30) (System Memory)
SELECT total_physical_memory_kb/1024 AS [Physical Memory (MB)], 
       available_physical_memory_kb/1024 AS [Available Memory (MB)], 
       total_page_file_kb/1024 AS [Total Page File (MB)], 
	   available_page_file_kb/1024 AS [Available Page File (MB)], 
	   system_cache_kb/1024 AS [System Cache (MB)],
       system_memory_state_desc AS [System Memory State]
FROM sys.dm_os_sys_memory WITH (NOLOCK) OPTION (RECOMPILE);
------

-- You want to see "Available physical memory is high"
-- This indicates that you are not under external memory pressure


-- SQL Server Process Address space info  (Query 31) (Process Memory) 
-- (shows whether locked pages is enabled, among other things)
SELECT physical_memory_in_use_kb/1024 AS [SQL Server Memory Usage (MB)],
       large_page_allocations_kb, locked_page_allocations_kb, page_fault_count, 
	   memory_utilization_percentage, available_commit_limit_kb, 
	   process_physical_memory_low, process_virtual_memory_low
FROM sys.dm_os_process_memory WITH (NOLOCK) OPTION (RECOMPILE);
------

-- You want to see 0 for process_physical_memory_low
-- You want to see 0 for process_virtual_memory_low
-- This indicates that you are not under internal memory pressure


-- Page Life Expectancy (PLE) value for each NUMA node in current instance  (Query 32) (PLE by NUMA Node)
SELECT @@SERVERNAME AS [Server Name], RTRIM([object_name]) AS [Object Name], instance_name, cntr_value AS [Page Life Expectancy]
FROM sys.dm_os_performance_counters WITH (NOLOCK)
WHERE [object_name] LIKE N'%Buffer Node%' -- Handles named instances
AND counter_name = N'Page life expectancy' OPTION (RECOMPILE);
------

-- PLE is a good measurement of memory pressure.
-- Higher PLE is better. Watch the trend over time, not the absolute value.
-- This will only return one row for non-NUMA systems.

-- Page Life Expectancy isn�t what you think�
-- http://www.sqlskills.com/blogs/paul/page-life-expectancy-isnt-what-you-think/



-- Memory Grants Pending value for current instance  (Query 33) (Memory Grants Pending)
SELECT @@SERVERNAME AS [Server Name], RTRIM([object_name]) AS [Object Name], cntr_value AS [Memory Grants Pending]                                                                                                       
FROM sys.dm_os_performance_counters WITH (NOLOCK)
WHERE [object_name] LIKE N'%Memory Manager%' -- Handles named instances
AND counter_name = N'Memory Grants Pending' OPTION (RECOMPILE);
------

-- Memory Grants Pending above zero for a sustained period is a very strong indicator of memory pressure


-- Memory Clerk Usage for instance  (Query 34) (Memory Clerk Usage)
-- Look for high value for CACHESTORE_SQLCP (Ad-hoc query plans)
SELECT TOP(10) [type] AS [Memory Clerk Type], SUM(single_pages_kb)/1024 AS [SPA Memory Usage (MB)] 
FROM sys.dm_os_memory_clerks WITH (NOLOCK)
GROUP BY [type]  
ORDER BY SUM(single_pages_kb) DESC OPTION (RECOMPILE);
------

-- CACHESTORE_SQLCP  SQL Plans         
-- These are cached SQL statements or batches that aren't in stored procedures, functions and triggers
-- Watch out for high values for CACHESTORE_SQLCP

-- CACHESTORE_OBJCP  Object Plans      
-- These are compiled plans for stored procedures, functions and triggers



-- Find single-use, ad-hoc and prepared queries that are bloating the plan cache  (Query 35) (Ad hoc Queries)
SELECT TOP(50) DB_NAME(t.[dbid]) AS [Database Name], t.[text] AS [Query Text], 
cp.objtype AS [Object Type], cp.cacheobjtype AS [Cache Object Type],  
cp.size_in_bytes/1024 AS [Plan Size in KB]
FROM sys.dm_exec_cached_plans AS cp WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t
WHERE cp.cacheobjtype = N'Compiled Plan' 
AND cp.objtype IN (N'Adhoc', N'Prepared') 
AND cp.usecounts = 1
ORDER BY cp.size_in_bytes DESC, DB_NAME(t.[dbid]) OPTION (RECOMPILE);
------

-- Gives you the text, type and size of single-use ad-hoc and prepared queries that waste space in the plan cache
-- Enabling 'optimize for ad hoc workloads' for the instance can help (SQL Server 2008 and above only)
-- Running DBCC FREESYSTEMCACHE ('SQL Plans') periodically may be required to better control this.
-- Enabling forced parameterization for the database can help, but test first!

-- Plan cache, adhoc workloads and clearing the single-use plan cache bloat
-- https://www.sqlskills.com/blogs/kimberly/plan-cache-adhoc-workloads-and-clearing-the-single-use-plan-cache-bloat/



-- Database specific queries *****************************************************************

-- **** Please switch to a user database that you are interested in! *****
USE YourDatabaseName; -- make sure to change to an actual database on your instance, not the master system database
GO

-- Individual File Sizes and space available for current database  (Query 36) (File Sizes and Space)
SELECT f.name AS [File Name] , f.physical_name AS [Physical Name], 
CAST((f.size/128.0) AS DECIMAL(15,2)) AS [Total Size in MB],
CAST(f.size/128.0 - CAST(FILEPROPERTY(f.name, 'SpaceUsed') AS int)/128.0 AS DECIMAL(15,2)) 
AS [Available Space In MB], [file_id], fg.name AS [Filegroup Name]
FROM sys.database_files AS f WITH (NOLOCK) 
LEFT OUTER JOIN sys.data_spaces AS fg WITH (NOLOCK) 
ON f.data_space_id = fg.data_space_id OPTION (RECOMPILE);
------

-- Look at how large and how full the files are and where they are located
-- Make sure the transaction log is not full!!



-- I/O Statistics by file for the current database  (Query 37) (IO Stats By File)
SELECT DB_NAME(DB_ID()) AS [Database Name], df.name AS [Logical Name], vfs.[file_id], 
df.physical_name AS [Physical Name], vfs.num_of_reads, vfs.num_of_writes, vfs.io_stall_read_ms, vfs.io_stall_write_ms,
CAST(100. * vfs.io_stall_read_ms/(vfs.io_stall_read_ms + vfs.io_stall_write_ms) AS DECIMAL(10,1)) AS [IO Stall Reads Pct],
CAST(100. * vfs.io_stall_write_ms/(vfs.io_stall_write_ms + vfs.io_stall_read_ms) AS DECIMAL(10,1)) AS [IO Stall Writes Pct],
(vfs.num_of_reads + vfs.num_of_writes) AS [Writes + Reads], 
CAST(vfs.num_of_bytes_read/1048576.0 AS DECIMAL(10, 2)) AS [MB Read], 
CAST(vfs.num_of_bytes_written/1048576.0 AS DECIMAL(10, 2)) AS [MB Written],
CAST(100. * vfs.num_of_reads/(vfs.num_of_reads + vfs.num_of_writes) AS DECIMAL(10,1)) AS [# Reads Pct],
CAST(100. * vfs.num_of_writes/(vfs.num_of_reads + vfs.num_of_writes) AS DECIMAL(10,1)) AS [# Write Pct],
CAST(100. * vfs.num_of_bytes_read/(vfs.num_of_bytes_read + vfs.num_of_bytes_written) AS DECIMAL(10,1)) AS [Read Bytes Pct],
CAST(100. * vfs.num_of_bytes_written/(vfs.num_of_bytes_read + vfs.num_of_bytes_written) AS DECIMAL(10,1)) AS [Written Bytes Pct]
FROM sys.dm_io_virtual_file_stats(DB_ID(), NULL) AS vfs
INNER JOIN sys.database_files AS df WITH (NOLOCK)
ON vfs.[file_id]= df.[file_id] OPTION (RECOMPILE);
------

-- This helps you characterize your workload better from an I/O perspective for this database
-- It helps you determine whether you has an OLTP or DW/DSS type of workload



-- Top cached queries by Execution Count (SQL Server 2008)  (Query 38) (Query Execution Counts)
SELECT TOP (100) qs.execution_count, qs.total_worker_time, qs.total_logical_reads, qs.total_elapsed_time,
SUBSTRING(qt.TEXT,qs.statement_start_offset/2 +1,
(CASE WHEN qs.statement_end_offset = -1
			THEN LEN(CONVERT(NVARCHAR(MAX), qt.TEXT)) * 2
	  ELSE qs.statement_end_offset END - qs.statement_start_offset)/2) AS query_text 
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS qt
ORDER BY qs.execution_count DESC OPTION (RECOMPILE);
------


-- Look at non-stored procedure queries


-- Top Cached SPs By Execution Count (SQL 2008) (Query 39) (SP Execution Counts)
SELECT TOP(100) p.name AS [SP Name], qs.execution_count,
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute],
qs.total_worker_time/qs.execution_count AS [AvgWorkerTime], qs.total_worker_time AS [TotalWorkerTime],  
qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time],
qs.cached_time
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
WHERE qs.database_id = DB_ID()
ORDER BY qs.execution_count DESC OPTION (RECOMPILE);
------

-- Tells you which cached stored procedures are called the most often
-- This helps you characterize and baseline your workload


-- Top Cached SPs By Avg Elapsed Time (SQL 2008)  (Query 40) (SP Avg Elapsed Time)
SELECT TOP(25) p.name AS [SP Name], qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time], 
qs.total_elapsed_time, qs.execution_count, ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, 
GETDATE()), 0) AS [Calls/Minute], qs.total_worker_time/qs.execution_count AS [AvgWorkerTime], 
qs.total_worker_time AS [TotalWorkerTime], qs.cached_time
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
WHERE qs.database_id = DB_ID()
ORDER BY avg_elapsed_time DESC OPTION (RECOMPILE);
------

-- This helps you find long-running cached stored procedures that
-- may be easy to optimize with standard query tuning techniques


-- Top Cached SPs By Avg Elapsed Time with execution time variability   (Query 41) (SP Avg Elapsed Variable Time)
SELECT TOP(25) p.name AS [SP Name], qs.execution_count, qs.min_elapsed_time,
qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time],
qs.max_elapsed_time, qs.last_elapsed_time,  qs.cached_time
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
WHERE qs.database_id = DB_ID()
ORDER BY avg_elapsed_time DESC OPTION (RECOMPILE);
------

-- This gives you some interesting information about the variability in the
-- execution time of your cached stored procedures, which is useful for tuning


-- Top Cached SPs By Total Worker time (SQL 2008). Worker time relates to CPU cost  (Query 42) (SP Worker Time)
SELECT TOP(25) p.name AS [SP Name], qs.total_worker_time AS [TotalWorkerTime], 
qs.total_worker_time/qs.execution_count AS [AvgWorkerTime], qs.execution_count, 
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute],
qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count 
AS [avg_elapsed_time], qs.cached_time
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
WHERE qs.database_id = DB_ID()
ORDER BY qs.total_worker_time DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a CPU perspective
-- You should look at this if you see signs of CPU pressure


-- Top Cached SPs By Total Logical Reads (SQL 2008). Logical reads relate to memory pressure  (Query 43) (SP Logical Reads)
SELECT TOP(25) p.name AS [SP Name], qs.total_logical_reads AS [TotalLogicalReads], 
qs.total_logical_reads/qs.execution_count AS [AvgLogicalReads],qs.execution_count, 
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute], 
qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count 
AS [avg_elapsed_time], qs.cached_time
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
WHERE qs.database_id = DB_ID()
ORDER BY qs.total_logical_reads DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a memory perspective
-- You should look at this if you see signs of memory pressure


-- Top Cached SPs By Total Physical Reads (SQL 2008). Physical reads relate to disk I/O pressure  (Query 44) (SP Physical Reads)
SELECT TOP(25) p.name AS [SP Name],qs.total_physical_reads AS [TotalPhysicalReads], 
qs.total_physical_reads/qs.execution_count AS [AvgPhysicalReads], qs.execution_count, 
qs.total_logical_reads,qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count 
AS [avg_elapsed_time], qs.cached_time 
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
WHERE qs.database_id = DB_ID()
AND qs.total_physical_reads > 0
ORDER BY qs.total_physical_reads DESC, qs.total_logical_reads DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a read I/O perspective
-- You should look at this if you see signs of I/O pressure or of memory pressure
       
-- Top Cached SPs By Total Logical Writes (SQL 2008)  (Query 45) (SP Logical Writes) 
-- Logical writes relate to both memory and disk I/O pressure 
SELECT TOP(25) p.name AS [SP Name], qs.total_logical_writes AS [TotalLogicalWrites], 
qs.total_logical_writes/qs.execution_count AS [AvgLogicalWrites], qs.execution_count,
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute],
qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time], 
qs.cached_time
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
WHERE qs.database_id = DB_ID()
AND qs.total_logical_writes > 0
ORDER BY qs.total_logical_writes DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a write I/O perspective
-- You should look at this if you see signs of I/O pressure or of memory pressure


-- Lists the top statements by average input/output usage for the current database  (Query 46) (Top IO Statements)
SELECT TOP(50) OBJECT_NAME(qt.objectid, dbid) AS [SP Name],
(qs.total_logical_reads + qs.total_logical_writes) /qs.execution_count AS [Avg IO], qs.execution_count AS [Execution Count],
SUBSTRING(qt.[text],qs.statement_start_offset/2, 
	(CASE 
		WHEN qs.statement_end_offset = -1 
	 THEN LEN(CONVERT(nvarchar(max), qt.[text])) * 2 
		ELSE qs.statement_end_offset 
	 END - qs.statement_start_offset)/2) AS [Query Text]	
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS qt
WHERE qt.[dbid] = DB_ID()
ORDER BY [Avg IO] DESC OPTION (RECOMPILE);
------

-- Helps you find the most expensive statements for I/O by SP



-- Possible Bad NC Indexes (writes > reads)  (Query 47) (Bad NC Indexes)
SELECT OBJECT_NAME(s.[object_id]) AS [Table Name], i.name AS [Index Name], i.index_id, 
i.is_disabled, i.is_hypothetical, i.has_filter, i.fill_factor,
user_updates AS [Total Writes], user_seeks + user_scans + user_lookups AS [Total Reads],
user_updates - (user_seeks + user_scans + user_lookups) AS [Difference]
FROM sys.dm_db_index_usage_stats AS s WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON s.[object_id] = i.[object_id]
AND i.index_id = s.index_id
WHERE OBJECTPROPERTY(s.[object_id],'IsUserTable') = 1
AND s.database_id = DB_ID()
AND s.user_updates > (s.user_seeks + s.user_scans + s.user_lookups)
AND i.index_id > 1 AND i.[type_desc] = N'NONCLUSTERED'
AND i.is_primary_key = 0 AND i.is_unique_constraint = 0
ORDER BY [Difference] DESC, [Total Writes] DESC, [Total Reads] ASC OPTION (RECOMPILE);
------

-- Look for indexes with high numbers of writes and zero or very low numbers of reads
-- Consider your complete workload, and how long your instance has been running
-- Investigate further before dropping an index!


-- Missing Indexes for current database by Index Advantage  (Query 48) (Missing Indexes)
SELECT DISTINCT CONVERT(decimal(18,2), user_seeks * avg_total_user_cost * (avg_user_impact * 0.01)) AS [index_advantage], 
migs.last_user_seek, mid.[statement] AS [Database.Schema.Table],
mid.equality_columns, mid.inequality_columns, mid.included_columns,
migs.unique_compiles, migs.user_seeks, migs.avg_total_user_cost, migs.avg_user_impact,
OBJECT_NAME(mid.[object_id]) AS [Table Name], p.rows AS [Table Rows]
FROM sys.dm_db_missing_index_group_stats AS migs WITH (NOLOCK)
INNER JOIN sys.dm_db_missing_index_groups AS mig WITH (NOLOCK)
ON migs.group_handle = mig.index_group_handle
INNER JOIN sys.dm_db_missing_index_details AS mid WITH (NOLOCK)
ON mig.index_handle = mid.index_handle
INNER JOIN sys.partitions AS p WITH (NOLOCK)
ON p.[object_id] = mid.[object_id]
WHERE mid.database_id = DB_ID()
AND p.index_id < 2 
ORDER BY index_advantage DESC OPTION (RECOMPILE);
------

-- Look at index advantage, last user seek time, number of user seeks to help determine source and importance
-- SQL Server is overly eager to add included columns, so beware
-- Do not just blindly add indexes that show up from this query!!!


-- Find missing index warnings for cached plans in the current database  (Query 49) (Missing Index Warnings)
-- Note: This query could take some time on a busy instance
SELECT TOP(25) OBJECT_NAME(objectid) AS [ObjectName], 
               query_plan, cp.objtype, cp.usecounts
FROM sys.dm_exec_cached_plans AS cp WITH (NOLOCK)
CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) AS qp
WHERE CAST(query_plan AS NVARCHAR(MAX)) LIKE N'%MissingIndex%'
AND dbid = DB_ID()
ORDER BY cp.usecounts DESC OPTION (RECOMPILE);
------

-- Helps you connect missing indexes to specific stored procedures
-- This can help you decide whether to add them or not


-- Breaks down buffers used by current database by object (table, index) in the buffer cache  (Query 50) (Buffer Usage)
-- Note: This query could take some time on a busy instance
SELECT OBJECT_NAME(p.[object_id]) AS [Object Name], p.index_id, 
CAST(COUNT(*)/128.0 AS DECIMAL(10, 2)) AS [Buffer size(MB)],  
COUNT(*) AS [BufferCount], p.Rows AS [Row Count],
p.data_compression_desc AS [Compression Type]
FROM sys.allocation_units AS a WITH (NOLOCK)
INNER JOIN sys.dm_os_buffer_descriptors AS b WITH (NOLOCK)
ON a.allocation_unit_id = b.allocation_unit_id
INNER JOIN sys.partitions AS p WITH (NOLOCK)
ON a.container_id = p.hobt_id
WHERE b.database_id = CONVERT(int,DB_ID())
AND p.[object_id] > 100
GROUP BY p.[object_id], p.index_id, p.data_compression_desc, p.[Rows]
ORDER BY [BufferCount] DESC OPTION (RECOMPILE);
------

-- Tells you what tables and indexes are using the most memory in the buffer cache
-- It can help identify possible candidates for data compression


-- Get Table names, row counts, and compression status for clustered index or heap  (Query 51) (Table Sizes)
SELECT OBJECT_NAME(object_id) AS [ObjectName], 
SUM(Rows) AS [RowCount], data_compression_desc AS [CompressionType]
FROM sys.partitions WITH (NOLOCK)
WHERE index_id < 2 --ignore the partitions from the non-clustered index if any
AND OBJECT_NAME(object_id) NOT LIKE N'sys%'
AND OBJECT_NAME(object_id) NOT LIKE N'queue_%' 
AND OBJECT_NAME(object_id) NOT LIKE N'filestream_tombstone%' 
AND OBJECT_NAME(object_id) NOT LIKE N'fulltext%'
AND OBJECT_NAME(object_id) NOT LIKE N'ifts_comp_fragment%'
AND OBJECT_NAME(object_id) NOT LIKE N'xml_index_nodes%'
GROUP BY object_id, data_compression_desc
ORDER BY SUM(Rows) DESC OPTION (RECOMPILE);
------

-- Gives you an idea of table sizes, and possible data compression opportunities


-- Get some key table properties (Query 52) (Table Properties)
SELECT [name], create_date, lock_on_bulk_load, is_replicated, has_replication_filter, 
       is_tracked_by_cdc, lock_escalation_desc
FROM sys.tables WITH (NOLOCK) 
ORDER BY [name] OPTION (RECOMPILE);
------

-- Gives you some good information about your tables


-- Detect blocking (run multiple times)  (Query 53) (Detect Blocking)
SELECT t1.resource_type AS [lock type], DB_NAME(resource_database_id) AS [database],
t1.resource_associated_entity_id AS [blk object],t1.request_mode AS [lock req],  --- lock requested
t1.request_session_id AS [waiter sid], t2.wait_duration_ms AS [wait time],       -- spid of waiter  
(SELECT [text] FROM sys.dm_exec_requests AS r WITH (NOLOCK)                      -- get sql for waiter
CROSS APPLY sys.dm_exec_sql_text(r.[sql_handle]) 
WHERE r.session_id = t1.request_session_id) AS [waiter_batch],
(SELECT SUBSTRING(qt.[text],r.statement_start_offset/2, 
    (CASE WHEN r.statement_end_offset = -1 
    THEN LEN(CONVERT(nvarchar(max), qt.[text])) * 2 
    ELSE r.statement_end_offset END - r.statement_start_offset)/2) 
FROM sys.dm_exec_requests AS r WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(r.[sql_handle]) AS qt
WHERE r.session_id = t1.request_session_id) AS [waiter_stmt],					-- statement blocked
t2.blocking_session_id AS [blocker sid],										-- spid of blocker
(SELECT [text] FROM sys.sysprocesses AS p										-- get sql for blocker
CROSS APPLY sys.dm_exec_sql_text(p.[sql_handle]) 
WHERE p.spid = t2.blocking_session_id) AS [blocker_stmt]
FROM sys.dm_tran_locks AS t1 WITH (NOLOCK)
INNER JOIN sys.dm_os_waiting_tasks AS t2 WITH (NOLOCK)
ON t1.lock_owner_address = t2.resource_address OPTION (RECOMPILE);
------

-- Helps troubleshoot blocking and deadlocking issues
-- The results will change from second to second on a busy system
-- You should run this query multiple times when you see signs of blocking



-- When were Statistics last updated on all indexes?  (Query 54) (Statistics Update)
SELECT SCHEMA_NAME(o.Schema_ID) + N'.' + o.NAME AS [Object Name], o.type_desc AS [Object Type],
      i.name AS [Index Name], STATS_DATE(i.[object_id], i.index_id) AS [Statistics Date], 
      s.auto_created, s.no_recompute, s.user_created, st.row_count, st.used_page_count
FROM sys.objects AS o WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON o.[object_id] = i.[object_id]
INNER JOIN sys.stats AS s WITH (NOLOCK)
ON i.[object_id] = s.[object_id] 
AND i.index_id = s.stats_id
INNER JOIN sys.dm_db_partition_stats AS st WITH (NOLOCK)
ON o.[object_id] = st.[object_id]
AND i.[index_id] = st.[index_id]
WHERE o.[type] IN ('U', 'V')
AND st.row_count > 0
ORDER BY STATS_DATE(i.[object_id], i.index_id) DESC OPTION (RECOMPILE); 
------ 

-- Helps discover possible problems with out-of-date statistics
-- Also gives you an idea which indexes are the most active


-- Get fragmentation info for all indexes above a certain size in the current database  (Query 55) (Index Fragmentation)
-- Note: This query could take some time on a very large database
SELECT DB_NAME(ps.database_id) AS [Database Name], OBJECT_NAME(ps.OBJECT_ID) AS [Object Name], 
i.name AS [Index Name], ps.index_id, ps.index_type_desc, ps.avg_fragmentation_in_percent, 
ps.fragment_count, ps.page_count, i.fill_factor, i.has_filter, i.filter_definition
FROM sys.dm_db_index_physical_stats(DB_ID(),NULL, NULL, NULL , N'LIMITED') AS ps
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON ps.[object_id] = i.[object_id] 
AND ps.index_id = i.index_id
WHERE ps.database_id = DB_ID()
AND ps.page_count > 2500
ORDER BY ps.avg_fragmentation_in_percent DESC OPTION (RECOMPILE);
------

-- Helps determine whether you have framentation in your relational indexes
-- and how effective your index maintenance strategy is


--- Index Read/Write stats (all tables in current DB) ordered by Reads  (Query 56) (Overall Index Usage - Reads)
SELECT OBJECT_NAME(s.[object_id]) AS [ObjectName], i.name AS [IndexName], i.index_id,
	   user_seeks + user_scans + user_lookups AS [Reads], s.user_updates AS [Writes],  
	   i.type_desc AS [IndexType], i.fill_factor AS [FillFactor], i.has_filter, i.filter_definition, 
	   s.last_user_scan, s.last_user_lookup, s.last_user_seek
FROM sys.dm_db_index_usage_stats AS s WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON s.[object_id] = i.[object_id]
WHERE OBJECTPROPERTY(s.[object_id],'IsUserTable') = 1
AND i.index_id = s.index_id
AND s.database_id = DB_ID()
ORDER BY user_seeks + user_scans + user_lookups DESC OPTION (RECOMPILE); -- Order by reads
------


-- Show which indexes in the current database are most active for Reads


--- Index Read/Write stats (all tables in current DB) ordered by Writes  (Query 57) (Overall Index Usage - Writes)
SELECT OBJECT_NAME(s.[object_id]) AS [ObjectName], i.name AS [IndexName], i.index_id,
	   s.user_updates AS [Writes], user_seeks + user_scans + user_lookups AS [Reads], 
	   i.type_desc AS [IndexType], i.fill_factor AS [FillFactor], i.has_filter, i.filter_definition,
	   s.last_system_update, s.last_user_update
FROM sys.dm_db_index_usage_stats AS s WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON s.[object_id] = i.[object_id]
WHERE OBJECTPROPERTY(s.[object_id],'IsUserTable') = 1
AND i.index_id = s.index_id
AND s.database_id = DB_ID()
ORDER BY s.user_updates DESC OPTION (RECOMPILE);						 -- Order by writes
------

-- Show which indexes in the current database are most active for Writes


-- Get lock waits for current database (Query 58) (Lock Waits)
SELECT o.name AS [table_name], i.name AS [index_name], ios.index_id, ios.partition_number,
		SUM(ios.row_lock_wait_count) AS [total_row_lock_waits], 
		SUM(ios.row_lock_wait_in_ms) AS [total_row_lock_wait_in_ms],
		SUM(ios.page_lock_wait_count) AS [total_page_lock_waits],
		SUM(ios.page_lock_wait_in_ms) AS [total_page_lock_wait_in_ms],
		SUM(ios.page_lock_wait_in_ms)+ SUM(row_lock_wait_in_ms) AS [total_lock_wait_in_ms]
FROM sys.dm_db_index_operational_stats(DB_ID(), NULL, NULL, NULL) AS ios
INNER JOIN sys.objects AS o WITH (NOLOCK)
ON ios.[object_id] = o.[object_id]
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON ios.[object_id] = i.[object_id] 
AND ios.index_id = i.index_id
WHERE o.[object_id] > 100
GROUP BY o.name, i.name, ios.index_id, ios.partition_number
HAVING SUM(ios.page_lock_wait_in_ms)+ SUM(row_lock_wait_in_ms) > 0
ORDER BY total_lock_wait_in_ms DESC OPTION (RECOMPILE);
------

-- This query is helpful for troubleshooting blocking and deadlocking issues


-- Look at recent Full backups for the current database (Query 59) (Recent Full Backups)
SELECT TOP (30) bs.machine_name, bs.server_name, bs.database_name AS [Database Name], bs.recovery_model,
CONVERT (BIGINT, bs.backup_size / 1048576 ) AS [Uncompressed Backup Size (MB)],
CONVERT (BIGINT, bs.compressed_backup_size / 1048576 ) AS [Compressed Backup Size (MB)],
CONVERT (NUMERIC (20,2), (CONVERT (FLOAT, bs.backup_size) /
CONVERT (FLOAT, bs.compressed_backup_size))) AS [Compression Ratio], bs.has_backup_checksums, bs.is_copy_only,
DATEDIFF (SECOND, bs.backup_start_date, bs.backup_finish_date) AS [Backup Elapsed Time (sec)],
bs.backup_finish_date AS [Backup Finish Date], bmf.physical_device_name AS [Backup Location], bmf.physical_block_size
FROM msdb.dbo.backupset AS bs WITH (NOLOCK)
INNER JOIN msdb.dbo.backupmediafamily AS bmf WITH (NOLOCK)
ON bs.media_set_id = bmf.media_set_id  
WHERE DATEDIFF (SECOND, bs.backup_start_date, bs.backup_finish_date) > 0 
AND bs.backup_size > 0
AND bs.type = 'D' -- Change to L if you want Log backups
AND database_name = DB_NAME(DB_ID())
ORDER BY bs.backup_finish_date DESC OPTION (RECOMPILE);
------

-- Are your backup sizes and times changing over time?
-- Are you using backup compression?
-- Are you using backup checksums?
-- Are you doing copy_only backups?
-- Have you done any backup tuning with striped backups, or changing the parameters of the backup command?


-- These three Pluralsight Courses go into more detail about how to run these queries and interpret the results

-- SQL Server 2014 DMV Diagnostic Queries � Part 1 
-- https://bit.ly/2plxCer

-- SQL Server 2014 DMV Diagnostic Queries � Part 2
-- https://bit.ly/2IuJpzI

-- SQL Server 2014 DMV Diagnostic Queries � Part 3
-- https://bit.ly/2FIlCPb



-- Sign up for Microsoft Visual Studio Dev Essentials and get a free three month pass to Pluralsight

-- Microsoft Visual Studio Dev Essentials
-- http://bit.ly/1q6xbDL


-- Sign up for Microsoft Azure Essentials and get lots of free Azure usage credits, MCP exam voucher, three month Pluralsight subscription

-- Microsoft Azure Essentials
-- https://bit.ly/2JMWe8x


-- August 2017 blog series about upgrading and migrating SQL Server
-- https://bit.ly/2ftKVrX
tools\dbatools\bin\diagnosticquery\SQLServerDiagnosticQueries_2012_201806.sql

-- SQL Server 2012 Diagnostic Information Queries
-- Glenn Berry 
-- Last Modified: July 10, 2018
-- https://www.sqlskills.com/blogs/glenn/
-- http://sqlserverperformance.wordpress.com/
-- Twitter: GlennAlanBerry

-- Please listen to my Pluralsight courses
-- https://www.pluralsight.com/author/glenn-berry

-- If you want to find all of our SQLskills SQL101 blog posts, check out https://www.sqlskills.com/help/sql101/


-- Please make sure you are using the correct version of these diagnostic queries for your version of SQL Server


-- If you like PowerShell, there is a very useful community solution for running these queries in an automated fashion
-- https://dbatools.io/

-- Invoke-DbaDiagnosticQuery
-- https://dbatools.io/functions/invoke-dbadiagnosticquery/


--******************************************************************************
--*   Copyright (C) 2018 Glenn Berry, SQLskills.com
--*   All rights reserved. 
--*
--*   For more scripts and sample code, check out 
--*      https://www.sqlskills.com/blogs/glenn
--*
--*   You may alter this code for your own *non-commercial* purposes. You may
--*   republish altered code as long as you include this copyright and give due credit. 
--*
--*
--*   THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF 
--*   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED 
--*   TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
--*   PARTICULAR PURPOSE. 
--*
--******************************************************************************

-- Check the major product version to see if it is SQL Server 2012
IF NOT EXISTS (SELECT * WHERE CONVERT(varchar(128), SERVERPROPERTY('ProductVersion')) LIKE '11%')
	BEGIN
		DECLARE @ProductVersion varchar(128) = CONVERT(varchar(128), SERVERPROPERTY('ProductVersion'));
		RAISERROR ('Script does not match the ProductVersion [%s] of this instance. Many of these queries may not work on this version.' , 18 , 16 , @ProductVersion);
	END
	ELSE
		PRINT N'You have the correct major version of SQL Server for this diagnostic information script';


-- Instance level queries *******************************

-- SQL and OS Version information for current instance  (Query 1) (Version Info)
SELECT @@SERVERNAME AS [Server Name], @@VERSION AS [SQL Server and OS Version Info];
------

-- SQL Server 2012 RTM Branch Builds						SQL Server 2012 SP1 Branch Builds					SQL Server 2012 SP2 Branch Builds						SQL Server 2012 SP3 Branch Builds					SQL Server 2012 SP4 Branch Builds
-- Build			Description			Release Date		Build			Description		Release Date		Build			Description		    Release Date        Build			Description		    Release Date	Build			Description		    Release Date
-- 11.0.2100		RTM					  3/6/2012  
-- 11.0.2316		RTM CU1				 4/12/2012
-- 11.0.2325        RTM CU2				 6/18/2012 -->		11.0.3000		SP1 RTM			11/7/2012
-- 11.0.2332		RTM CU3				 8/31/2012
-- 11.0.2376	    RTM CU3 + QFE	     10/9/2012
-- 11.0.2383	    RTM CU4			    10/15/2012 -->		11.0.3321		SP1 CU1			11/20/2012
-- 11.0.2395		RTM CU5				12/17/2012 -->      11.0.3339		SP1 CU2			1/21/2013
-- 11.0.2401        RTM CU6              2/18/2013 -->      11.0.3349       SP1 CU3			3/18/2013
-- 11.0.2405        RTM CU7              4/15/2013 -->      11.0 3368       SP1 CU4         5/30/2013
-- 11.0.2410        RTM CU8              6/17/2013 -->      11.0.3373       SP1 CU5         7/15/2013
-- 11.0.2419        RTM CU9              8/20/2013 -->      11.0.3381		SP1 CU6			9/16/2013
-- 11.0.2420        RTM CU10            10/21/2013 -->		11.0.3393       SP1 CU7         11/18/2013
-- 11.0.2424        RTM CU11            12/16/2003 -->      11.0.3401       SP1 CU8         1/20/2014
--                                                          11.0.3412       SP1 CU9         3/17/2014 -->		11.0.5058		SP2 RTM			    6/10/2014
--                                                          11.0.3431       SP1 CU10        5/19/2014
--                                                          11.0.3449       SP1 CU11        7/21/2014 -->		11.0.5532		SP2 CU1			    7/23/2014
--                                                          11.0.3470       SP1 CU12        9/15/2014 -->       11.0.5548       SP2 CU2             9/15/2014
--                                                          11.0.3482		SP1 CU13        11/17/2014-->       11.0.5556		SP2 CU3            11/17/2014
--                                                          11.0.3486       SP1 CU14        1/19/2015 -->       11.0.5569       SP2 CU4             1/19/2015
--                                                                                                              11.0.5571       SP2 CU4 + COD HF     2/4/2015  
--                                                          11.0.3487		SP1 CU15		3/16/2015 -->       11.0.5582       SP2 CU5             3/16/2015
--															11.0.3492       SP1 CU16        5/18/2015 -->       11.0.5592		SP2 CU6				5/18/2015
--                                                                                                              11.0.5623       SP2 CU7				7/20/2015
--                                                                                                              11.0.5634		SP2 CU8				9/21/2015
--																												11.0.5641		SP2 CU9			   11/16/2015   ---->  11.0.6020		SP3 RTM			11/21/2015
--																												11.0.5644		SP2 CU10			1/18/2016   ---->  11.0.6518		SP3 CU1			 1/18/2016
--																												11.0.5646		SP2 CU11			3/21/2016	---->  11.0.6523		SP3 CU2			 3/21/2016
--																												11.0.5649		SP2 CU12			5/16/2016	---->  11.0.6537		SP3 CU3			 5/16/2016
--																												11.0.5655		SP2 CU13			7/18/2016	---->  11.0.6540		SP3 CU4			 7/18/2016	
--																												11.0.5657		SP2 CU14		    9/19/2016   ---->  11.0.6544		SP3 CU5			 9/20/2016
--																												11.0.5676		SP2 CU15 		   11/16/2016   ---->  11.0.6567		SP3 CU6 		11/16/2016
--																												11.0.5678		SP2 CU16			1/17/2017   ---->  11.0.6579		SP3 CU7			 1/17/2017
--																																									   11.0.6594		SP3 CU8			 3/20/2017
--																																									   11.0.6598		SP3 CU9			 5/15/2017																											                                                            				
--																																									   11.0.6607		SP3 CU10		  8/8/2017																											
--																																																							11.0.7001		SP4 RTM				10/3/2017	
-- 
-- Security Update for SQL Server 2012 SP4 (KB4057116) 
-- https://bit.ly/2F33Sc4
--                                                                                                                                                                                                                          11.0.7462	    Security Update		1/12/2018  (Security Update for SQL Server 2012 SP4 (KB4057116))
-- SQL Server 2012 Service Pack 4 (SP4) Released!
-- https://bit.ly/2qN8kr3

-- How to determine the version, edition and update level of SQL Server and its components 
-- https://bit.ly/2oAjKgW	

-- SQL Server 2012 SP3 build versions
-- https://bit.ly/2HFjAzA 

-- SQL Server 2012 SP2 build versions 
-- https://bit.ly/2qLqqcS

-- The SQL Server 2012 builds that were released after SQL Server 2012 Service Pack 1 was released
-- https://bit.ly/2HG21za

-- The SQL Server 2012 builds that were released after SQL Server 2012 was released
-- https://bit.ly/2K1xZnX

-- Where to find information about the latest SQL Server builds
-- https://bit.ly/2IGHbfY

-- Recommended updates and configuration options for SQL Server 2012 and SQL Server 2014 used with high-performance workloads
-- https://bit.ly/2Hy3zIZ

-- Performance and Stability Related Fixes in Post-SQL Server 2012 SP3 Builds
-- https://bit.ly/2woDJ4Z

-- Performance and Stability Related Fixes in Post-SQL Server 2012 SP2 Builds
-- https://bit.ly/2vuKZzp

-- Performance and Stability Related Fixes in Post-SQL Server 2012 SP1 Builds
-- https://bit.ly/2vBt1LC

-- Performance Related Fixes in Post-SQL Server 2012 RTM Builds
-- https://bit.ly/2vuIQn4

-- Announcing updates to the SQL Server Incremental Servicing Model (ISM)
-- https://bit.ly/1RzYITz

-- Update Center for Microsoft SQL Server
-- https://bit.ly/2pZptuQ

-- Download SQL Server Management Studio (SSMS)
-- https://bit.ly/1OcupT9

-- Download and install Microsoft SQL Operations Studio 
-- https://bit.ly/2vgke1A


-- Get socket, physical core and logical core count from the SQL Server Error log. (Query 2) (Core Counts)
-- This query might take a few seconds depending on the size of your error log 
EXEC sys.xp_readerrorlog 0, 1, N'detected', N'socket';
------

-- This can help you determine the exact core counts used by SQL Server and whether HT is enabled or not
-- It can also help you confirm your SQL Server licensing model
-- Be on the lookout for this message "using 40 logical processors based on SQL Server licensing" 
-- (when you have more than 40 logical cores) which means grandfathered Server/CAL licensing
-- This query will return no results if your error log has been recycled since the instance was last started
-- New in SQL Server 2012 SP4



-- Get selected server properties (Query 3) (Server Properties)
SELECT SERVERPROPERTY('MachineName') AS [MachineName], 
SERVERPROPERTY('ServerName') AS [ServerName],  
SERVERPROPERTY('InstanceName') AS [Instance], 
SERVERPROPERTY('IsClustered') AS [IsClustered], 
SERVERPROPERTY('ComputerNamePhysicalNetBIOS') AS [ComputerNamePhysicalNetBIOS], 
SERVERPROPERTY('Edition') AS [Edition], 
SERVERPROPERTY('ProductLevel') AS [ProductLevel],				-- What servicing branch (RTM/SP/CU)
SERVERPROPERTY('ProductUpdateLevel') AS [ProductUpdateLevel],	-- Within a servicing branch, what CU# is applied
SERVERPROPERTY('ProductVersion') AS [ProductVersion],
SERVERPROPERTY('ProductMajorVersion') AS [ProductMajorVersion], 
SERVERPROPERTY('ProductMinorVersion') AS [ProductMinorVersion], 
SERVERPROPERTY('ProductBuild') AS [ProductBuild], 
SERVERPROPERTY('ProductBuildType') AS [ProductBuildType],		      -- Is this a GDR or OD hotfix (NULL if on a CU build)
SERVERPROPERTY('ProductUpdateReference') AS [ProductUpdateReference], -- KB article number that is applicable for this build
SERVERPROPERTY('ProcessID') AS [ProcessID],
SERVERPROPERTY('Collation') AS [Collation], 
SERVERPROPERTY('IsFullTextInstalled') AS [IsFullTextInstalled], 
SERVERPROPERTY('IsIntegratedSecurityOnly') AS [IsIntegratedSecurityOnly],
SERVERPROPERTY('FilestreamConfiguredLevel') AS [FilestreamConfiguredLevel],
SERVERPROPERTY('IsHadrEnabled') AS [IsHadrEnabled], 
SERVERPROPERTY('HadrManagerStatus') AS [HadrManagerStatus],
SERVERPROPERTY('InstanceDefaultDataPath') AS [InstanceDefaultDataPath],
SERVERPROPERTY('InstanceDefaultLogPath') AS [InstanceDefaultLogPath],
SERVERPROPERTY('BuildClrVersion') AS [Build CLR Version];
------

-- This gives you a lot of useful information about your instance of SQL Server,
-- such as the ProcessID for SQL Server and your collation
-- Note: Some columns will be NULL on older SQL Server builds

-- SERVERPROPERTY (Transact-SQL)
-- https://bit.ly/2eeaXeI


-- Get instance-level configuration values for instance  (Query 4) (Configuration Values)
SELECT name, value, value_in_use, minimum, maximum, [description], is_dynamic, is_advanced
FROM sys.configurations WITH (NOLOCK)
ORDER BY name OPTION (RECOMPILE);
------

-- Focus on these settings:
-- backup compression default (should be 1 in most cases)
-- clr enabled (only enable if it is needed)
-- cost threshold for parallelism (depends on your workload)
-- lightweight pooling (should be zero)
-- max degree of parallelism (depends on your workload and hardware)
-- max server memory (MB) (set to an appropriate value, not the default)
-- optimize for ad hoc workloads (should be 1)
-- priority boost (should be zero)
-- remote admin connections (should be 1)


-- Returns a list of all global trace flags that are enabled (Query 5) (Global Trace Flags)
DBCC TRACESTATUS (-1);
------

-- If no global trace flags are enabled, no results will be returned.
-- It is very useful to know what global trace flags are currently enabled as part of the diagnostic process.

-- Common trace flags that should be enabled in most cases
-- TF 1117 - When growing a data file, grow all files at the same time so they remain the same size, reducing allocation contention points
--           https://bit.ly/2GY1kOl
-- 
-- TF 1118 - Helps alleviate allocation contention in tempdb, SQL Server allocates full extents to each database object, 
--           thereby eliminating the contention on SGAM pages (more important with older versions of SQL Server)
--           Recommendations to reduce allocation contention in SQL Server tempdb database
--           https://bit.ly/2GY1kOl

-- TF 2371 - Lowers auto update statistics threshold for large tables (on tables with more than 25,000 rows)
--           https://bit.ly/2HySkAg

-- TF 3023 - Enables backup checksum default
--           https://bit.ly/2vtjqqc

-- TF 3226 - Supresses logging of successful database backup messages to the SQL Server Error Log
--           https://bit.ly/2p6MTjS

-- TF 3449 - Enables use of dirty page manager (SQL Server 2012 SP3 CU3 and later)
--			 https://bit.ly/2uj0h5M

-- TF 6533 - Spatial performance improvements in SQL Server 2012 and 2014
--           https://bit.ly/2v7C7ze

-- TF 6534 - Enables use of native code to improve performance with spatial data
--           https://bit.ly/2HrQUpU

-- TF 8079 - Enables automatic soft-NUMA on systems with eight or more physical cores per NUMA node (with SQL Server 2012 SP4)
--           https://bit.ly/2qN8kr3

-- DBCC TRACEON - Trace Flags (Transact-SQL)
-- https://bit.ly/2FuSvPg


-- Returns status of instant file initialization (Query 6) (IFI Status)
EXEC sys.xp_readerrorlog 0, 1, N'Database Instant File Initialization';
------

-- Lets you determine whether Instant File Initialization (IFI) is enabled for the instance
-- This should be enabled in the vast majority of cases
-- (Added in SQL Server 2012 SP4)

-- Database Instant File Initialization
-- https://bit.ly/2nTX74y

-- Misconceptions around instant file initialization
-- https://bit.ly/2oBSKgZ



-- SQL Server Process Address space info  (Query 7) (Process Memory)
-- (shows whether locked pages is enabled, among other things)
SELECT physical_memory_in_use_kb/1024 AS [SQL Server Memory Usage (MB)],
	   locked_page_allocations_kb/1024 AS [SQL Server Locked Pages Allocation (MB)],
       large_page_allocations_kb/1024 AS [SQL Server Large Pages Allocation (MB)], 
	   page_fault_count, memory_utilization_percentage, available_commit_limit_kb, 
	   process_physical_memory_low, process_virtual_memory_low
FROM sys.dm_os_process_memory WITH (NOLOCK) OPTION (RECOMPILE);
------

-- You want to see 0 for process_physical_memory_low
-- You want to see 0 for process_virtual_memory_low
-- This indicates that you are not under internal memory pressure
-- If locked_page_allocations_kb > 0, then LPIM is enabled

-- How to enable the "locked pages" feature in SQL Server 2012
-- https://bit.ly/2F5UjOA

-- Memory Management Architecture Guide
-- https://bit.ly/2JKkadC 



-- SQL Server Services information (Query 8) (SQL Server Services Info)
SELECT servicename, process_id, startup_type_desc, status_desc, 
last_startup_time, service_account, is_clustered, cluster_nodename, [filename]
FROM sys.dm_server_services WITH (NOLOCK) OPTION (RECOMPILE);
------

-- Tells you the account being used for the SQL Server Service and the SQL Agent Service
-- Shows the process_id, when they were last started, and their current status
-- Also shows whether you are running on a failover cluster instance, and what node you are running on

-- sys.dm_server_services (Transact-SQL)
-- https://bit.ly/2oKa1Un


-- Last backup information by database  (Query 9) (Last Backup By Database)
SELECT ISNULL(d.[name], bs.[database_name]) AS [Database], d.recovery_model_desc AS [Recovery Model], 
       d.log_reuse_wait_desc AS [Log Reuse Wait Desc],
    MAX(CASE WHEN [type] = 'D' THEN bs.backup_finish_date ELSE NULL END) AS [Last Full Backup],
    MAX(CASE WHEN [type] = 'I' THEN bs.backup_finish_date ELSE NULL END) AS [Last Differential Backup],
    MAX(CASE WHEN [type] = 'L' THEN bs.backup_finish_date ELSE NULL END) AS [Last Log Backup]
FROM sys.databases AS d WITH (NOLOCK)
LEFT OUTER JOIN msdb.dbo.backupset AS bs WITH (NOLOCK)
ON bs.[database_name] = d.[name] 
AND bs.backup_finish_date > GETDATE()- 30
WHERE d.name <> N'tempdb'
GROUP BY ISNULL(d.[name], bs.[database_name]), d.recovery_model_desc, d.log_reuse_wait_desc, d.[name] 
ORDER BY d.recovery_model_desc, d.[name] OPTION (RECOMPILE);
------

-- This helps you spot runaway transaction logs and other issues with your backup schedule


-- Get SQL Server Agent jobs and Category information (Query 10) (SQL Server Agent Jobs)
SELECT sj.name AS [Job Name], sj.[description] AS [Job Description], SUSER_SNAME(sj.owner_sid) AS [Job Owner],
sj.date_created AS [Date Created], sj.[enabled] AS [Job Enabled], 
sj.notify_email_operator_id, sj.notify_level_email, sc.name AS [CategoryName],
s.[enabled] AS [Sched Enabled], js.next_run_date, js.next_run_time
FROM msdb.dbo.sysjobs AS sj WITH (NOLOCK)
INNER JOIN msdb.dbo.syscategories AS sc WITH (NOLOCK)
ON sj.category_id = sc.category_id
LEFT OUTER JOIN msdb.dbo.sysjobschedules AS js WITH (NOLOCK)
ON sj.job_id = js.job_id
LEFT OUTER JOIN msdb.dbo.sysschedules AS s WITH (NOLOCK)
ON js.schedule_id = s.schedule_id
ORDER BY sj.name OPTION (RECOMPILE);
------

-- Gives you some basic information about your SQL Server Agent jobs, who owns them and how they are configured
-- Look for Agent jobs that are not owned by sa
-- Look for jobs that have a notify_email_operator_id set to 0 (meaning no operator)
-- Look for jobs that have a notify_level_email set to 0 (meaning no e-mail is ever sent)
--
-- MSDN sysjobs documentation
-- https://bit.ly/2paDEOP

-- SQL Server Maintenance Solution
-- https://bit.ly/1pgchQu


-- Get SQL Server Agent Alert Information (Query 11) (SQL Server Agent Alerts)
SELECT name, event_source, message_id, severity, [enabled], has_notification, 
       delay_between_responses, occurrence_count, last_occurrence_date, last_occurrence_time
FROM msdb.dbo.sysalerts WITH (NOLOCK)
ORDER BY name OPTION (RECOMPILE);
------

-- Gives you some basic information about your SQL Server Agent Alerts (which are different from SQL Server Agent jobs)
-- Read more about Agent Alerts here: https://bit.ly/2Giz0Xf



-- Windows information (Query 12) (Windows Info)
SELECT windows_release, windows_service_pack_level, 
       windows_sku, os_language_version
FROM sys.dm_os_windows_info WITH (NOLOCK) OPTION (RECOMPILE);
------

-- Gives you major OS version, Service Pack, Edition, and language info for the operating system
-- 10.0 is either Windows 10 or Windows Server 2016
-- 6.3 is either Windows 8.1, or Windows Server 2012 R2  
-- 6.2 is either Windows 8 or Windows Server 2012
-- 6.1 is either Windows 7 or Windows Server 2008 R2
-- 6.0 is either Windows Vista or Windows Server 2008

-- Windows SKU codes
-- 4 is Enterprise Edition
-- 7 is Standard Server Edition
-- 8 is Datacenter Server Edition
-- 10 is Enterprise Server Edition
-- 48 is Professional Edition
-- 161 is Pro for Workstations

-- 1033 for os_language_version is US-English

-- SQL Server 2012 requires Windows Server 2008 SP2 or newer

-- Hardware and Software Requirements for Installing SQL Server 2012
-- https://bit.ly/1yRYXkQ

-- Using SQL Server in Windows 8 and later versions of Windows operating system 
-- https://bit.ly/2F7Ax0P



-- SQL Server NUMA Node information  (Query 13) (SQL Server NUMA Info)
SELECT node_id, node_state_desc, memory_node_id, processor_group, online_scheduler_count, 
       idle_scheduler_count, active_worker_count, avg_load_balance, resource_monitor_state
FROM sys.dm_os_nodes WITH (NOLOCK) 
WHERE node_state_desc <> N'ONLINE DAC' OPTION (RECOMPILE);
------

-- Gives you some useful information about the composition and relative load on your NUMA nodes
-- You want to see an equal number of schedulers on each NUMA node
-- Watch out if SQL Server 2012 Standard Edition has been installed 
-- on a physical or virtual machine with more than four sockets or more than 16 physical cores

-- sys.dm_os_nodes (Transact-SQL)
-- https://bit.ly/2pn5Mw8

-- Balancing Your Available SQL Server Core Licenses Evenly Across NUMA Nodes
-- https://bit.ly/2vfC4Rq



-- Good basic information about OS memory amounts and state  (Query 14) (System Memory)
SELECT total_physical_memory_kb/1024 AS [Physical Memory (MB)], 
       available_physical_memory_kb/1024 AS [Available Memory (MB)], 
       total_page_file_kb/1024 AS [Total Page File (MB)], 
	   available_page_file_kb/1024 AS [Available Page File (MB)], 
	   system_cache_kb/1024 AS [System Cache (MB)],
       system_memory_state_desc AS [System Memory State]
FROM sys.dm_os_sys_memory WITH (NOLOCK) OPTION (RECOMPILE);
------

-- You want to see "Available physical memory is high" for System Memory State
-- This indicates that you are not under external memory pressure

-- Possible System Memory State values:
-- Available physical memory is high
-- Physical memory usage is steady
-- Available physical memory is low
-- Available physical memory is running low
-- Physical memory state is transitioning

-- sys.dm_os_sys_memory (Transact-SQL)
-- https://bit.ly/2pcV0xq



-- You can skip the next two queries if you know you don't have a clustered instance


-- Get information about your cluster nodes and their status  (Query 15) (Cluster Node Properties)
-- (if your database server is in a failover cluster)
SELECT NodeName, status_description, is_current_owner
FROM sys.dm_os_cluster_nodes WITH (NOLOCK) OPTION (RECOMPILE);
------

-- Knowing which node owns the cluster resources is critical
-- Especially when you are installing Windows or SQL Server updates
-- You will see no results if your instance is not clustered

-- Recommended hotfixes and updates for Windows Server 2012 R2-based failover clusters
-- https://bit.ly/1z5BfCw


-- Get information about any AlwaysOn AG cluster this instance is a part of (Query 16) (AlwaysOn AG Cluster)
SELECT cluster_name, quorum_type_desc, quorum_state_desc
FROM sys.dm_hadr_cluster WITH (NOLOCK) OPTION (RECOMPILE);
------

-- You will see no results if your instance is not using AlwaysOn AGs



-- Hardware information from SQL Server 2012  (Query 17) (Hardware Info)
SELECT cpu_count AS [Logical CPU Count], scheduler_count, 
       hyperthread_ratio AS [Hyperthread Ratio],
       cpu_count/hyperthread_ratio AS [Physical CPU Count], 
       physical_memory_kb/1024 AS [Physical Memory (MB)], 
	   committed_kb/1024 AS [Committed Memory (MB)],
       committed_target_kb/1024 AS [Committed Target Memory (MB)],
       max_workers_count AS [Max Workers Count], 
	   affinity_type_desc AS [Affinity Type], 
       sqlserver_start_time AS [SQL Server Start Time], 
	   virtual_machine_type_desc AS [Virtual Machine Type]
FROM sys.dm_os_sys_info WITH (NOLOCK) OPTION (RECOMPILE);
------

-- Gives you some good basic hardware information about your database server
-- Cannot distinguish between HT and multi-core
-- Note: virtual_machine_type_desc of HYPERVISOR does not automatically mean you are running SQL Server inside of a VM
-- It merely indicates that you have a hypervisor running on your host

-- sys.dm_os_sys_info (Transact-SQL)
-- https://bit.ly/2pczOYs


-- Get System Manufacturer and model number from SQL Server Error log (Query 18) (System Manufacturer)
EXEC sys.xp_readerrorlog 0, 1, N'Manufacturer';
------ 

-- This can help you determine the capabilities and capacities of your database server
-- Can also be used to confirm if you are running in a VM
-- This query might take a few seconds if you have not recycled your error log recently
-- This query will return no results if your error log has been recycled since the instance was started


-- Get BIOS date from Windows Registry (Query 19) (BIOS Date)
EXEC sys.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'HARDWARE\DESCRIPTION\System\BIOS', N'BiosReleaseDate';
------

-- Helps you understand whether the main system BIOS is up to date, and the possible age of the hardware
-- Not as useful for virtualization


-- Get processor description from Windows Registry  (Query 20) (Processor Description)
EXEC sys.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'HARDWARE\DESCRIPTION\System\CentralProcessor\0', N'ProcessorNameString';
------

-- Gives you the model number and rated clock speed of your processor(s)
-- Your processors may be running at less than the rated clock speed due
-- to the Windows Power Plan or hardware power management

-- You can use CPU-Z to get your actual CPU core speed and a lot of other useful information
-- https://bit.ly/QhR6xF

-- You can learn more about processor selection for SQL Server by following this link
-- https://bit.ly/2F3aVlP



-- Get information on location, time and size of any memory dumps from SQL Server  (Query 21) (Memory Dump Info)
SELECT [filename], creation_time, size_in_bytes/1048576.0 AS [Size (MB)]
FROM sys.dm_server_memory_dumps WITH (NOLOCK) 
ORDER BY creation_time DESC OPTION (RECOMPILE);
------

-- This will not return any rows if you have 
-- not had any memory dumps (which is a good thing)

-- sys.dm_server_memory_dumps (Transact-SQL)
-- https://bit.ly/2elwWll



-- Look at Suspect Pages table (Query 22) (Suspect Pages)
SELECT DB_NAME(database_id) AS [Database Name], [file_id], page_id, 
       event_type, error_count, last_update_date 
FROM msdb.dbo.suspect_pages WITH (NOLOCK)
ORDER BY database_id OPTION (RECOMPILE);
------

-- event_type value descriptions
-- 1 = 823 error caused by an operating system CRC error
--     or 824 error other than a bad checksum or a torn page (for example, a bad page ID)
-- 2 = Bad checksum
-- 3 = Torn page
-- 4 = Restored (The page was restored after it was marked bad)
-- 5 = Repaired (DBCC repaired the page)
-- 7 = Deallocated by DBCC

-- Ideally, this query returns no results. The table is limited to 1000 rows.
-- If you do get results here, you should do further investigation to determine the root cause

-- Manage the suspect_pages Table
-- https://bit.ly/2Fvr1c9


-- Get number of data files in tempdb database (Query 23) (Tempdb Data Files)
EXEC sys.xp_readerrorlog 0, 1, N'The tempdb database has';
------

-- Get the number of data files in the tempdb database
-- 4-8 data files that are all the same size is a good starting point
-- This query will return no results if your error log has been recycled since the instance was last started
-- This will be blank unless you have Service Pack 4 or later


-- File names and paths for all user and system databases on instance  (Query 24) (Database Filenames and Paths)
SELECT DB_NAME([database_id]) AS [Database Name], 
       [file_id], [name], physical_name, [type_desc], state_desc,
	   is_percent_growth, growth,
	   CONVERT(bigint, growth/128.0) AS [Growth in MB], 
       CONVERT(bigint, size/128.0) AS [Total Size in MB]
FROM sys.master_files WITH (NOLOCK)
ORDER BY DB_NAME([database_id]), [file_id] OPTION (RECOMPILE);
------

-- Things to look at:
-- Are data files and log files on different drives?
-- Is everything on the C: drive?
-- Is tempdb on dedicated drives?
-- Is there only one tempdb data file?
-- Are all of the tempdb data files the same size?
-- Are there multiple data files for user databases?
-- Is percent growth enabled for any files (which is bad)?


-- Volume info for all LUNS that have database files on the current instance (Query 25) (Volume Info)
SELECT DISTINCT vs.volume_mount_point, vs.file_system_type, vs.logical_volume_name, 
CONVERT(DECIMAL(18,2), vs.total_bytes/1073741824.0) AS [Total Size (GB)],
CONVERT(DECIMAL(18,2), vs.available_bytes/1073741824.0) AS [Available Size (GB)],  
CONVERT(DECIMAL(18,2), vs.available_bytes * 1. / vs.total_bytes * 100.) AS [Space Free %],
vs.supports_compression, vs.is_compressed, 
vs.supports_sparse_files, vs.supports_alternate_streams
FROM sys.master_files AS f WITH (NOLOCK)
CROSS APPLY sys.dm_os_volume_stats(f.database_id, f.[file_id]) AS vs 
ORDER BY vs.volume_mount_point OPTION (RECOMPILE);
------

-- Shows you the total and free space on the LUNs where you have database files
-- Being low on free space can negatively affect performance

-- sys.dm_os_volume_stats (Transact-SQL)
-- https://bit.ly/2oBPNNr



-- Drive level latency information (Query 26) (Drive Level Latency)
-- Based on code from Jimmy May
SELECT tab.[Drive], tab.volume_mount_point AS [Volume Mount Point], 
	CASE 
		WHEN num_of_reads = 0 THEN 0 
		ELSE (io_stall_read_ms/num_of_reads) 
	END AS [Read Latency],
	CASE 
		WHEN num_of_writes = 0 THEN 0 
		ELSE (io_stall_write_ms/num_of_writes) 
	END AS [Write Latency],
	CASE 
		WHEN (num_of_reads = 0 AND num_of_writes = 0) THEN 0 
		ELSE (io_stall/(num_of_reads + num_of_writes)) 
	END AS [Overall Latency],
	CASE 
		WHEN num_of_reads = 0 THEN 0 
		ELSE (num_of_bytes_read/num_of_reads) 
	END AS [Avg Bytes/Read],
	CASE 
		WHEN num_of_writes = 0 THEN 0 
		ELSE (num_of_bytes_written/num_of_writes) 
	END AS [Avg Bytes/Write],
	CASE 
		WHEN (num_of_reads = 0 AND num_of_writes = 0) THEN 0 
		ELSE ((num_of_bytes_read + num_of_bytes_written)/(num_of_reads + num_of_writes)) 
	END AS [Avg Bytes/Transfer]
FROM (SELECT LEFT(UPPER(mf.physical_name), 2) AS Drive, SUM(num_of_reads) AS num_of_reads,
	         SUM(io_stall_read_ms) AS io_stall_read_ms, SUM(num_of_writes) AS num_of_writes,
	         SUM(io_stall_write_ms) AS io_stall_write_ms, SUM(num_of_bytes_read) AS num_of_bytes_read,
	         SUM(num_of_bytes_written) AS num_of_bytes_written, SUM(io_stall) AS io_stall, vs.volume_mount_point 
      FROM sys.dm_io_virtual_file_stats(NULL, NULL) AS vfs
      INNER JOIN sys.master_files AS mf WITH (NOLOCK)
      ON vfs.database_id = mf.database_id AND vfs.file_id = mf.file_id
	  CROSS APPLY sys.dm_os_volume_stats(mf.database_id, mf.[file_id]) AS vs 
      GROUP BY LEFT(UPPER(mf.physical_name), 2), vs.volume_mount_point) AS tab
ORDER BY [Overall Latency] OPTION (RECOMPILE);
------

-- Shows you the drive-level latency for reads and writes, in milliseconds
-- Latency above 30-40ms is usually a problem
-- These latency numbers include all file activity against all SQL Server 
-- database files on each drive since SQL Server was last started


-- Calculates average stalls per read, per write, and per total input/output for each database file  (Query 27) (IO Stalls by File)
SELECT DB_NAME(fs.database_id) AS [Database Name], CAST(fs.io_stall_read_ms/(1.0 + fs.num_of_reads) AS NUMERIC(10,1)) AS [avg_read_stall_ms],
CAST(fs.io_stall_write_ms/(1.0 + fs.num_of_writes) AS NUMERIC(10,1)) AS [avg_write_stall_ms],
CAST((fs.io_stall_read_ms + fs.io_stall_write_ms)/(1.0 + fs.num_of_reads + fs.num_of_writes) AS NUMERIC(10,1)) AS [avg_io_stall_ms],
CONVERT(DECIMAL(18,2), mf.size/128.0) AS [File Size (MB)], mf.physical_name, mf.type_desc, fs.io_stall_read_ms, fs.num_of_reads, 
fs.io_stall_write_ms, fs.num_of_writes, fs.io_stall_read_ms + fs.io_stall_write_ms AS [io_stalls], fs.num_of_reads + fs.num_of_writes AS [total_io]
FROM sys.dm_io_virtual_file_stats(null,null) AS fs
INNER JOIN sys.master_files AS mf WITH (NOLOCK)
ON fs.database_id = mf.database_id
AND fs.[file_id] = mf.[file_id]
ORDER BY avg_io_stall_ms DESC OPTION (RECOMPILE);
------

-- Helps determine which database files on the entire instance have the most I/O bottlenecks
-- This can help you decide whether certain LUNs are overloaded and whether you might
-- want to move some files to a different location or perhaps improve your I/O performance
-- These latency numbers include all file activity against each SQL Server 
-- database file since SQL Server was last started


-- Look for I/O requests taking longer than 15 seconds in the six most recent SQL Server Error Logs (Query 28) (IO Warnings)
CREATE TABLE #IOWarningResults(LogDate datetime, ProcessInfo sysname, LogText nvarchar(1000));

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 0, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 1, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 2, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 3, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 4, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 5, 1, N'taking longer than 15 seconds';

SELECT LogDate, ProcessInfo, LogText
FROM #IOWarningResults
ORDER BY LogDate DESC;

DROP TABLE #IOWarningResults;
------  

-- Finding 15 second I/O warnings in the SQL Server Error Log is useful evidence of
-- poor I/O performance (which might have many different causes)
-- Look to see if you see any patterns in the results (same files, same drives, same time of day, etc.)

-- Diagnostics in SQL Server help detect stalled and stuck I/O operations
-- https://bit.ly/2qtaw73



-- Recovery model, log reuse wait description, log file size, log usage size  (Query 29) (Database Properties)
-- and compatibility level for all databases on instance
SELECT db.[name] AS [Database Name], SUSER_SNAME(db.owner_sid) AS [Database Owner], db.recovery_model_desc AS [Recovery Model], 
db.state_desc, db.containment_desc, db.log_reuse_wait_desc AS [Log Reuse Wait Description], 
CONVERT(DECIMAL(18,2), ls.cntr_value/1024.0) AS [Log Size (MB)], CONVERT(DECIMAL(18,2), lu.cntr_value/1024.0) AS [Log Used (MB)],
CAST(CAST(lu.cntr_value AS FLOAT) / CAST(ls.cntr_value AS FLOAT)AS DECIMAL(18,2)) * 100 AS [Log Used %], 
db.[compatibility_level] AS [DB Compatibility Level], db.page_verify_option_desc AS [Page Verify Option], 
db.is_auto_create_stats_on, db.is_auto_update_stats_on, db.is_auto_update_stats_async_on, db.is_parameterization_forced, 
db.snapshot_isolation_state_desc, db.is_read_committed_snapshot_on, db.is_auto_close_on, db.is_auto_shrink_on, 
db.target_recovery_time_in_seconds, db.is_cdc_enabled, db.is_published, db.group_database_id, db.replica_id,
db.is_encrypted, de.encryption_state, de.percent_complete, de.key_algorithm, de.key_length
FROM sys.databases AS db WITH (NOLOCK)
INNER JOIN sys.dm_os_performance_counters AS lu WITH (NOLOCK)
ON db.name = lu.instance_name
INNER JOIN sys.dm_os_performance_counters AS ls WITH (NOLOCK)
ON db.name = ls.instance_name
LEFT OUTER JOIN sys.dm_database_encryption_keys AS de WITH (NOLOCK)
ON db.database_id = de.database_id
WHERE lu.counter_name LIKE N'Log File(s) Used Size (KB)%' 
AND ls.counter_name LIKE N'Log File(s) Size (KB)%'
AND ls.cntr_value > 0 
ORDER BY db.[name] OPTION (RECOMPILE);
------

-- Things to look at:
-- How many databases are on the instance?
-- What recovery models are they using?
-- What is the log reuse wait description?
-- How full are the transaction logs?
-- What compatibility level are the databases on? 
-- What is the Page Verify Option? (should be CHECKSUM)
-- Is Auto Update Statistics Asynchronously enabled?
-- Make sure auto_shrink and auto_close are not enabled!



-- Missing Indexes for all databases by Index Advantage  (Query 30) (Missing Indexes All Databases)
SELECT CONVERT(decimal(18,2), user_seeks * avg_total_user_cost * (avg_user_impact * 0.01)) AS [index_advantage],
FORMAT(migs.last_user_seek, 'yyyy-MM-dd HH:mm:ss') AS [last_user_seek], 
mid.[statement] AS [Database.Schema.Table],
COUNT(1) OVER(PARTITION BY mid.[statement]) AS [missing_indexes_for_table],
COUNT(1) OVER(PARTITION BY mid.[statement], equality_columns) AS [similar_missing_indexes_for_table],
mid.equality_columns, mid.inequality_columns, mid.included_columns,
migs.unique_compiles, migs.user_seeks, 
CONVERT(decimal(18,2), migs.avg_total_user_cost) AS [avg_total_user_cost], migs.avg_user_impact 
FROM sys.dm_db_missing_index_group_stats AS migs WITH (NOLOCK)
INNER JOIN sys.dm_db_missing_index_groups AS mig WITH (NOLOCK)
ON migs.group_handle = mig.index_group_handle
INNER JOIN sys.dm_db_missing_index_details AS mid WITH (NOLOCK)
ON mig.index_handle = mid.index_handle
ORDER BY index_advantage DESC OPTION (RECOMPILE);
------

-- Getting missing index information for all of the databases on the instance is very useful
-- Look at last user seek time, number of user seeks to help determine source and importance
-- Also look at avg_user_impact and avg_total_user_cost to help determine importance
-- SQL Server is overly eager to add included columns, so beware
-- Do not just blindly add indexes that show up from this query!!!

-- SQL Server Index Design Guide
-- https://bit.ly/2qtZr4N



-- Get VLF Counts for all databases on the instance (Query 31) (VLF Counts)
-- (adapted from Michelle Ufford) 
CREATE TABLE #VLFInfo (RecoveryUnitID int, FileID  int,
					   FileSize bigint, StartOffset bigint,
					   FSeqNo      bigint, [Status]    bigint,
					   Parity      bigint, CreateLSN   numeric(38));
	 
CREATE TABLE #VLFCountResults(DatabaseName sysname, VLFCount int);
	 
EXEC sp_MSforeachdb N'Use [?]; 

				INSERT INTO #VLFInfo 
				EXEC sp_executesql N''DBCC LOGINFO([?])''; 
	 
				INSERT INTO #VLFCountResults 
				SELECT DB_NAME(), COUNT(*) 
				FROM #VLFInfo; 

				TRUNCATE TABLE #VLFInfo;'
	 
SELECT DatabaseName, VLFCount  
FROM #VLFCountResults
ORDER BY VLFCount DESC;
	 
DROP TABLE #VLFInfo;
DROP TABLE #VLFCountResults;
------

-- High VLF counts can affect write performance to the log file 
-- and they can make full database restores and crash recovery take much longer
-- Try to keep your VLF counts under 200 in most cases (depending on log file size)



-- Get CPU utilization by database (Query 32) (CPU Usage by Database)
WITH DB_CPU_Stats
AS
(SELECT pa.DatabaseID, DB_Name(pa.DatabaseID) AS [Database Name], SUM(qs.total_worker_time/1000) AS [CPU_Time_Ms]
 FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
 CROSS APPLY (SELECT CONVERT(int, value) AS [DatabaseID] 
              FROM sys.dm_exec_plan_attributes(qs.plan_handle)
              WHERE attribute = N'dbid') AS pa
 GROUP BY DatabaseID)
SELECT ROW_NUMBER() OVER(ORDER BY [CPU_Time_Ms] DESC) AS [CPU Rank],
       [Database Name], [CPU_Time_Ms] AS [CPU Time (ms)], 
       CAST([CPU_Time_Ms] * 1.0 / SUM([CPU_Time_Ms]) OVER() * 100.0 AS DECIMAL(5, 2)) AS [CPU Percent]
FROM DB_CPU_Stats
WHERE DatabaseID <> 32767 -- ResourceDB
ORDER BY [CPU Rank] OPTION (RECOMPILE);
------

-- Helps determine which database is using the most CPU resources on the instance


-- Get I/O utilization by database (Query 33) (IO Usage By Database)
WITH Aggregate_IO_Statistics
AS
(SELECT DB_NAME(database_id) AS [Database Name],
CAST(SUM(num_of_bytes_read + num_of_bytes_written)/1048576 AS DECIMAL(12, 2)) AS io_in_mb
FROM sys.dm_io_virtual_file_stats(NULL, NULL) AS [DM_IO_STATS]
GROUP BY database_id)
SELECT ROW_NUMBER() OVER(ORDER BY io_in_mb DESC) AS [I/O Rank], [Database Name], io_in_mb AS [Total I/O (MB)],
       CAST(io_in_mb/ SUM(io_in_mb) OVER() * 100.0 AS DECIMAL(5,2)) AS [I/O Percent]
FROM Aggregate_IO_Statistics
ORDER BY [I/O Rank] OPTION (RECOMPILE);
------

-- Helps determine which database is using the most I/O resources on the instance


-- Get total buffer usage by database for current instance  (Query 34) (Total Buffer Usage by Database)
-- This make take some time to run on a busy instance
WITH AggregateBufferPoolUsage
AS
(SELECT DB_NAME(database_id) AS [Database Name],
CAST(COUNT(*) * 8/1024.0 AS DECIMAL (10,2))  AS [CachedSize]
FROM sys.dm_os_buffer_descriptors WITH (NOLOCK)
WHERE database_id <> 32767 -- ResourceDB
GROUP BY DB_NAME(database_id))
SELECT ROW_NUMBER() OVER(ORDER BY CachedSize DESC) AS [Buffer Pool Rank], [Database Name], CachedSize AS [Cached Size (MB)],
       CAST(CachedSize / SUM(CachedSize) OVER() * 100.0 AS DECIMAL(5,2)) AS [Buffer Pool Percent]
FROM AggregateBufferPoolUsage
ORDER BY [Buffer Pool Rank] OPTION (RECOMPILE);
------

-- Tells you how much memory (in the buffer pool) 
-- is being used by each database on the instance


-- Clear Wait Stats with this command
-- DBCC SQLPERF('sys.dm_os_wait_stats', CLEAR);

-- Isolate top waits for server instance since last restart or wait statistics clear  (Query 35) (Top Waits)
WITH [Waits] 
AS (SELECT wait_type, wait_time_ms/ 1000.0 AS [WaitS],
          (wait_time_ms - signal_wait_time_ms) / 1000.0 AS [ResourceS],
           signal_wait_time_ms / 1000.0 AS [SignalS],
           waiting_tasks_count AS [WaitCount],
           100.0 *  wait_time_ms / SUM (wait_time_ms) OVER() AS [Percentage],
           ROW_NUMBER() OVER(ORDER BY wait_time_ms DESC) AS [RowNum]
    FROM sys.dm_os_wait_stats WITH (NOLOCK)
    WHERE [wait_type] NOT IN (
        N'BROKER_EVENTHANDLER', N'BROKER_RECEIVE_WAITFOR', N'BROKER_TASK_STOP',
		N'BROKER_TO_FLUSH', N'BROKER_TRANSMITTER', N'CHECKPOINT_QUEUE',
        N'CHKPT', N'CLR_AUTO_EVENT', N'CLR_MANUAL_EVENT', N'CLR_SEMAPHORE',
        N'DBMIRROR_DBM_EVENT', N'DBMIRROR_EVENTS_QUEUE', N'DBMIRROR_WORKER_QUEUE',
		N'DBMIRRORING_CMD', N'DIRTY_PAGE_POLL', N'DISPATCHER_QUEUE_SEMAPHORE',
        N'EXECSYNC', N'FSAGENT', N'FT_IFTS_SCHEDULER_IDLE_WAIT', N'FT_IFTSHC_MUTEX',
        N'HADR_CLUSAPI_CALL', N'HADR_FILESTREAM_IOMGR_IOCOMPLETION', N'HADR_LOGCAPTURE_WAIT', 
		N'HADR_NOTIFICATION_DEQUEUE', N'HADR_TIMER_TASK', N'HADR_WORK_QUEUE',
        N'KSOURCE_WAKEUP', N'LAZYWRITER_SLEEP', N'LOGMGR_QUEUE', N'ONDEMAND_TASK_QUEUE',
        N'PREEMPTIVE_OS_QUERYREGISTRY', 
		N'PREEMPTIVE_HADR_LEASE_MECHANISM', N'PREEMPTIVE_SP_SERVER_DIAGNOSTICS',
		N'PWAIT_ALL_COMPONENTS_INITIALIZED', 
		N'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP',
        N'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP', N'REQUEST_FOR_DEADLOCK_SEARCH',
		N'RESOURCE_QUEUE', N'SERVER_IDLE_CHECK', N'SLEEP_BPOOL_FLUSH', N'SLEEP_DBSTARTUP',
		N'SLEEP_DCOMSTARTUP', N'SLEEP_MASTERDBREADY', N'SLEEP_MASTERMDREADY',
        N'SLEEP_MASTERUPGRADED', N'SLEEP_MSDBSTARTUP', N'SLEEP_SYSTEMTASK', N'SLEEP_TASK',
        N'SLEEP_TEMPDBSTARTUP', N'SNI_HTTP_ACCEPT', N'SP_SERVER_DIAGNOSTICS_SLEEP',
		N'SQLTRACE_BUFFER_FLUSH', N'SQLTRACE_INCREMENTAL_FLUSH_SLEEP', N'SQLTRACE_WAIT_ENTRIES',
		N'WAIT_FOR_RESULTS', N'WAITFOR', N'WAITFOR_TASKSHUTDOWN', N'WAIT_XTP_HOST_WAIT',
		N'WAIT_XTP_OFFLINE_CKPT_NEW_LOG', N'WAIT_XTP_CKPT_CLOSE', N'XE_DISPATCHER_JOIN',
        N'XE_DISPATCHER_WAIT', N'XE_TIMER_EVENT')
    AND waiting_tasks_count > 0)
SELECT
    MAX (W1.wait_type) AS [WaitType],
	CAST (MAX (W1.Percentage) AS DECIMAL (5,2)) AS [Wait Percentage],
	CAST ((MAX (W1.WaitS) / MAX (W1.WaitCount)) AS DECIMAL (16,4)) AS [AvgWait_Sec],
    CAST ((MAX (W1.ResourceS) / MAX (W1.WaitCount)) AS DECIMAL (16,4)) AS [AvgRes_Sec],
    CAST ((MAX (W1.SignalS) / MAX (W1.WaitCount)) AS DECIMAL (16,4)) AS [AvgSig_Sec], 
    CAST (MAX (W1.WaitS) AS DECIMAL (16,2)) AS [Wait_Sec],
    CAST (MAX (W1.ResourceS) AS DECIMAL (16,2)) AS [Resource_Sec],
    CAST (MAX (W1.SignalS) AS DECIMAL (16,2)) AS [Signal_Sec],
    MAX (W1.WaitCount) AS [Wait Count],
	CAST (N'https://www.sqlskills.com/help/waits/' + W1.wait_type AS XML) AS [Help/Info URL]
FROM Waits AS W1
INNER JOIN Waits AS W2
ON W2.RowNum <= W1.RowNum
GROUP BY W1.RowNum, W1.wait_type
HAVING SUM (W2.Percentage) - MAX (W1.Percentage) < 99 -- percentage threshold
OPTION (RECOMPILE);
------

-- Cumulative wait stats are not as useful on an idle instance that is not under load or performance pressure

-- SQL Server Wait Types Library (Paul Randal)
-- https://www.sqlskills.com/help/waits/

-- The SQL Server Wait Type Repository
-- http://blogs.msdn.com/b/psssql/archive/2009/11/03/the-sql-server-wait-type-repository.aspx

-- Wait statistics, or please tell me where it hurts
-- http://www.sqlskills.com/blogs/paul/wait-statistics-or-please-tell-me-where-it-hurts/

-- SQL Server 2005 Performance Tuning using the Waits and Queues
-- http://technet.microsoft.com/en-us/library/cc966413.aspx

-- sys.dm_os_wait_stats (Transact-SQL)
-- http://msdn.microsoft.com/en-us/library/ms179984(v=sql.120).aspx



-- Get a count of SQL connections by IP address (Query 36) (Connection Counts by IP Address)
SELECT ec.client_net_address, es.[program_name], es.[host_name], es.login_name, 
COUNT(ec.session_id) AS [connection count] 
FROM sys.dm_exec_sessions AS es WITH (NOLOCK) 
INNER JOIN sys.dm_exec_connections AS ec WITH (NOLOCK) 
ON es.session_id = ec.session_id 
GROUP BY ec.client_net_address, es.[program_name], es.[host_name], es.login_name  
ORDER BY ec.client_net_address, es.[program_name] OPTION (RECOMPILE);
------

-- This helps you figure where your database load is coming from
-- and verifies connectivity from other machines

-- Solving Connectivity errors to SQL Server
-- https://bit.ly/2EgzoD0



-- Get Average Task Counts (run multiple times)  (Query 37) (Avg Task Counts)
SELECT AVG(current_tasks_count) AS [Avg Task Count], 
AVG(work_queue_count) AS [Avg Work Queue Count],
AVG(runnable_tasks_count) AS [Avg Runnable Task Count],
AVG(pending_disk_io_count) AS [Avg Pending DiskIO Count]
FROM sys.dm_os_schedulers WITH (NOLOCK)
WHERE scheduler_id < 255 OPTION (RECOMPILE);
------

-- Sustained values above 10 suggest further investigation in that area
-- High Avg Task Counts are often caused by blocking/deadlocking or other resource contention

-- Sustained values above 1 suggest further investigation in that area
-- High Avg Runnable Task Counts are a good sign of CPU pressure
-- High Avg Pending DiskIO Counts are a sign of disk pressure

-- How to Do Some Very Basic SQL Server Monitoring
-- https://bit.ly/2q3Btgt



-- Detect blocking (run multiple times)  (Query 38) (Detect Blocking)
SELECT t1.resource_type AS [lock type], DB_NAME(resource_database_id) AS [database],
t1.resource_associated_entity_id AS [blk object],t1.request_mode AS [lock req],  -- lock requested
t1.request_session_id AS [waiter sid], t2.wait_duration_ms AS [wait time],       -- spid of waiter  
(SELECT [text] FROM sys.dm_exec_requests AS r WITH (NOLOCK)                      -- get sql for waiter
CROSS APPLY sys.dm_exec_sql_text(r.[sql_handle]) 
WHERE r.session_id = t1.request_session_id) AS [waiter_batch],
(SELECT SUBSTRING(qt.[text],r.statement_start_offset/2, 
    (CASE WHEN r.statement_end_offset = -1 
    THEN LEN(CONVERT(nvarchar(max), qt.[text])) * 2 
    ELSE r.statement_end_offset END - r.statement_start_offset)/2) 
FROM sys.dm_exec_requests AS r WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(r.[sql_handle]) AS qt
WHERE r.session_id = t1.request_session_id) AS [waiter_stmt],					-- statement blocked
t2.blocking_session_id AS [blocker sid],										-- spid of blocker
(SELECT [text] FROM sys.sysprocesses AS p										-- get sql for blocker
CROSS APPLY sys.dm_exec_sql_text(p.[sql_handle]) 
WHERE p.spid = t2.blocking_session_id) AS [blocker_batch]
FROM sys.dm_tran_locks AS t1 WITH (NOLOCK)
INNER JOIN sys.dm_os_waiting_tasks AS t2 WITH (NOLOCK)
ON t1.lock_owner_address = t2.resource_address OPTION (RECOMPILE);
------

-- Helps troubleshoot blocking and deadlocking issues
-- The results will change from second to second on a busy system
-- You should run this query multiple times when you see signs of blocking



-- Get CPU Utilization History for last 256 minutes (in one minute intervals)  (Query 39) (CPU Utilization History)
DECLARE @ts_now bigint = (SELECT cpu_ticks/(cpu_ticks/ms_ticks) FROM sys.dm_os_sys_info WITH (NOLOCK)); 

SELECT TOP(256) SQLProcessUtilization AS [SQL Server Process CPU Utilization], 
               SystemIdle AS [System Idle Process], 
               100 - SystemIdle - SQLProcessUtilization AS [Other Process CPU Utilization], 
               DATEADD(ms, -1 * (@ts_now - [timestamp]), GETDATE()) AS [Event Time] 
FROM (SELECT record.value('(./Record/@id)[1]', 'int') AS record_id, 
			record.value('(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]', 'int') 
			AS [SystemIdle], 
			record.value('(./Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization)[1]', 'int') 
			AS [SQLProcessUtilization], [timestamp] 
	  FROM (SELECT [timestamp], CONVERT(xml, record) AS [record] 
			FROM sys.dm_os_ring_buffers WITH (NOLOCK)
			WHERE ring_buffer_type = N'RING_BUFFER_SCHEDULER_MONITOR' 
			AND record LIKE N'%<SystemHealth>%') AS x) AS y 
ORDER BY record_id DESC OPTION (RECOMPILE);
------

-- Look at the trend over the entire period 
-- Also look at high sustained 'Other Process' CPU Utilization values
-- Note: This query sometimes gives inaccurate results (negative values)
-- on high core count (> 64 cores) systems


-- Get top total worker time queries for entire instance (Query 40) (Top Worker Time Queries)
SELECT TOP(50) DB_NAME(t.[dbid]) AS [Database Name], 
REPLACE(REPLACE(LEFT(t.[text], 255), CHAR(10),''), CHAR(13),'') AS [Short Query Text],  
qs.total_worker_time AS [Total Worker Time], qs.min_worker_time AS [Min Worker Time],
qs.total_worker_time/qs.execution_count AS [Avg Worker Time], 
qs.max_worker_time AS [Max Worker Time], 
qs.min_elapsed_time AS [Min Elapsed Time], 
qs.total_elapsed_time/qs.execution_count AS [Avg Elapsed Time], 
qs.max_elapsed_time AS [Max Elapsed Time],
qs.min_logical_reads AS [Min Logical Reads],
qs.total_logical_reads/qs.execution_count AS [Avg Logical Reads],
qs.max_logical_reads AS [Max Logical Reads], 
qs.execution_count AS [Execution Count],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index], 
qs.creation_time AS [Creation Time]
--,t.[text] AS [Query Text], qp.query_plan AS [Query Plan] -- uncomment out these columns if not copying results to Excel
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t 
CROSS APPLY sys.dm_exec_query_plan(plan_handle) AS qp 
ORDER BY qs.total_worker_time DESC OPTION (RECOMPILE);
------


-- Helps you find the most expensive queries from a CPU perspective across the entire instance
-- Can also help track down parameter sniffing issues



-- Page Life Expectancy (PLE) value for each NUMA node in current instance  (Query 41) (PLE by NUMA Node)
SELECT @@SERVERNAME AS [Server Name], RTRIM([object_name]) AS [Object Name], instance_name, cntr_value AS [Page Life Expectancy]
FROM sys.dm_os_performance_counters WITH (NOLOCK)
WHERE [object_name] LIKE N'%Buffer Node%' -- Handles named instances
AND counter_name = N'Page life expectancy' OPTION (RECOMPILE);
------

-- PLE is a good measurement of internal memory pressure
-- Higher PLE is better. Watch the trend over time, not the absolute value
-- This will only return one row for non-NUMA systems

-- Page Life Expectancy isn�t what you think�
-- https://bit.ly/2EgynLa


-- Memory Grants Pending value for current instance  (Query 42) (Memory Grants Pending)
SELECT @@SERVERNAME AS [Server Name], RTRIM([object_name]) AS [Object Name], cntr_value AS [Memory Grants Pending]                                                                                                       
FROM sys.dm_os_performance_counters WITH (NOLOCK)
WHERE [object_name] LIKE N'%Memory Manager%' -- Handles named instances
AND counter_name = N'Memory Grants Pending' OPTION (RECOMPILE);
------

-- Run multiple times, and run periodically if you suspect you are under memory pressure
-- Memory Grants Pending above zero for a sustained period is a very strong indicator of internal memory pressure


-- Memory Clerk Usage for instance  (Query 43) (Memory Clerk Usage)
-- Look for high value for CACHESTORE_SQLCP (Ad-hoc query plans)
SELECT TOP(10) mc.[type] AS [Memory Clerk Type], 
       CAST((SUM(mc.pages_kb)/1024.0) AS DECIMAL (15,2)) AS [Memory Usage (MB)] 
FROM sys.dm_os_memory_clerks AS mc WITH (NOLOCK)
GROUP BY mc.[type]  
ORDER BY SUM(mc.pages_kb) DESC OPTION (RECOMPILE);
------

-- MEMORYCLERK_SQLBUFFERPOOL was new for SQL Server 2012. It should be your highest consumer of memory

-- CACHESTORE_SQLCP  SQL Plans         
-- These are cached SQL statements or batches that aren't in stored procedures, functions and triggers
-- Watch out for high values for CACHESTORE_SQLCP
-- Enabling 'optimize for ad hoc workloads' at the instance level can help reduce this
-- Running DBCC FREESYSTEMCACHE ('SQL Plans') periodically may be required to better control this

-- CACHESTORE_OBJCP  Object Plans      
-- These are compiled plans for stored procedures, functions and triggers



-- Find single-use, ad-hoc and prepared queries that are bloating the plan cache  (Query 44) (Ad hoc Queries)
SELECT TOP(50) DB_NAME(t.[dbid]) AS [Database Name], t.[text] AS [Query Text], 
cp.objtype AS [Object Type], cp.cacheobjtype AS [Cache Object Type],  
cp.size_in_bytes/1024 AS [Plan Size in KB]
FROM sys.dm_exec_cached_plans AS cp WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t
WHERE cp.cacheobjtype = N'Compiled Plan' 
AND cp.objtype IN (N'Adhoc', N'Prepared') 
AND cp.usecounts = 1
ORDER BY cp.size_in_bytes DESC, DB_NAME(t.[dbid]) OPTION (RECOMPILE);
------

-- Gives you the text, type and size of single-use ad-hoc and prepared queries that waste space in the plan cache
-- Enabling 'optimize for ad hoc workloads' for the instance can help (SQL Server 2008 and above only)
-- Running DBCC FREESYSTEMCACHE ('SQL Plans') periodically may be required to better control this
-- Enabling forced parameterization for the database can help, but test first!

-- Plan cache, adhoc workloads and clearing the single-use plan cache bloat
-- https://www.sqlskills.com/blogs/kimberly/plan-cache-adhoc-workloads-and-clearing-the-single-use-plan-cache-bloat/


-- Get top total logical reads queries for entire instance (Query 45) (Top Logical Reads Queries)
SELECT TOP(50) DB_NAME(t.[dbid]) AS [Database Name],
REPLACE(REPLACE(LEFT(t.[text], 255), CHAR(10),''), CHAR(13),'') AS [Short Query Text], 
qs.total_logical_reads AS [Total Logical Reads],
qs.min_logical_reads AS [Min Logical Reads],
qs.total_logical_reads/qs.execution_count AS [Avg Logical Reads],
qs.max_logical_reads AS [Max Logical Reads],   
qs.min_worker_time AS [Min Worker Time],
qs.total_worker_time/qs.execution_count AS [Avg Worker Time], 
qs.max_worker_time AS [Max Worker Time], 
qs.min_elapsed_time AS [Min Elapsed Time], 
qs.total_elapsed_time/qs.execution_count AS [Avg Elapsed Time], 
qs.max_elapsed_time AS [Max Elapsed Time],
qs.execution_count AS [Execution Count], 
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index],
qs.creation_time AS [Creation Time]
--,t.[text] AS [Complete Query Text], qp.query_plan AS [Query Plan] -- uncomment out these columns if not copying results to Excel
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t 
CROSS APPLY sys.dm_exec_query_plan(plan_handle) AS qp 
ORDER BY qs.total_logical_reads DESC OPTION (RECOMPILE);
------


-- Helps you find the most expensive queries from a memory perspective across the entire instance
-- Can also help track down parameter sniffing issues


-- Get top average elapsed time queries for entire instance (Query 46) (Top Avg Elapsed Time Queries)
SELECT TOP(50) DB_NAME(t.[dbid]) AS [Database Name], 
REPLACE(REPLACE(LEFT(t.[text], 255), CHAR(10),''), CHAR(13),'') AS [Short Query Text],  
qs.total_elapsed_time/qs.execution_count AS [Avg Elapsed Time],
qs.min_elapsed_time, qs.max_elapsed_time, qs.last_elapsed_time,
qs.execution_count AS [Execution Count],  
qs.total_logical_reads/qs.execution_count AS [Avg Logical Reads], 
qs.total_physical_reads/qs.execution_count AS [Avg Physical Reads], 
qs.total_worker_time/qs.execution_count AS [Avg Worker Time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index],
qs.creation_time AS [Creation Time]
--,t.[text] AS [Complete Query Text], qp.query_plan AS [Query Plan] -- uncomment out these columns if not copying results to Excel
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t 
CROSS APPLY sys.dm_exec_query_plan(plan_handle) AS qp 
ORDER BY qs.total_elapsed_time/qs.execution_count DESC OPTION (RECOMPILE);
------

-- Helps you find the highest average elapsed time queries across the entire instance
-- Can also help track down parameter sniffing issues




-- Database specific queries *****************************************************************

-- **** Please switch to a user database that you are interested in! *****
--USE YourDatabaseName; -- make sure to change to an actual database on your instance, not the master system database
--GO

-- Individual File Sizes and space available for current database  (Query 47) (File Sizes and Space)
SELECT f.name AS [File Name] , f.physical_name AS [Physical Name], 
CAST((f.size/128.0) AS DECIMAL(15,2)) AS [Total Size in MB],
CAST(f.size/128.0 - CAST(FILEPROPERTY(f.name, 'SpaceUsed') AS int)/128.0 AS DECIMAL(15,2)) 
AS [Available Space In MB], f.[file_id], fg.name AS [Filegroup Name],
f.is_percent_growth, f.growth, fg.is_default, fg.is_read_only
FROM sys.database_files AS f WITH (NOLOCK) 
LEFT OUTER JOIN sys.filegroups AS fg WITH (NOLOCK)
ON f.data_space_id = fg.data_space_id
ORDER BY f.[file_id] OPTION (RECOMPILE);
------

-- Look at how large and how full the files are and where they are located
-- Make sure the transaction log is not full!!


-- I/O Statistics by file for the current database  (Query 48) (IO Stats By File)
SELECT DB_NAME(DB_ID()) AS [Database Name], df.name AS [Logical Name], vfs.[file_id], df.type_desc,
df.physical_name AS [Physical Name], CAST(vfs.size_on_disk_bytes/1048576.0 AS DECIMAL(10, 2)) AS [Size on Disk (MB)],
vfs.num_of_reads, vfs.num_of_writes, vfs.io_stall_read_ms, vfs.io_stall_write_ms,
CAST(100. * vfs.io_stall_read_ms/(vfs.io_stall_read_ms + vfs.io_stall_write_ms) AS DECIMAL(10,1)) AS [IO Stall Reads Pct],
CAST(100. * vfs.io_stall_write_ms/(vfs.io_stall_write_ms + vfs.io_stall_read_ms) AS DECIMAL(10,1)) AS [IO Stall Writes Pct],
(vfs.num_of_reads + vfs.num_of_writes) AS [Writes + Reads], 
CAST(vfs.num_of_bytes_read/1048576.0 AS DECIMAL(10, 2)) AS [MB Read], 
CAST(vfs.num_of_bytes_written/1048576.0 AS DECIMAL(10, 2)) AS [MB Written],
CAST(100. * vfs.num_of_reads/(vfs.num_of_reads + vfs.num_of_writes) AS DECIMAL(10,1)) AS [# Reads Pct],
CAST(100. * vfs.num_of_writes/(vfs.num_of_reads + vfs.num_of_writes) AS DECIMAL(10,1)) AS [# Write Pct],
CAST(100. * vfs.num_of_bytes_read/(vfs.num_of_bytes_read + vfs.num_of_bytes_written) AS DECIMAL(10,1)) AS [Read Bytes Pct],
CAST(100. * vfs.num_of_bytes_written/(vfs.num_of_bytes_read + vfs.num_of_bytes_written) AS DECIMAL(10,1)) AS [Written Bytes Pct]
FROM sys.dm_io_virtual_file_stats(DB_ID(), NULL) AS vfs
INNER JOIN sys.database_files AS df WITH (NOLOCK)
ON vfs.[file_id]= df.[file_id] OPTION (RECOMPILE);
------

-- This helps you characterize your workload better from an I/O perspective for this database
-- It helps you determine whether you has an OLTP or DW/DSS type of workload



-- Get most frequently executed queries for this database (Query 49) (Query Execution Counts)
SELECT TOP(50) LEFT(t.[text], 50) AS [Short Query Text], qs.execution_count AS [Execution Count],
qs.total_logical_reads AS [Total Logical Reads],
qs.total_logical_reads/qs.execution_count AS [Avg Logical Reads],
qs.total_worker_time AS [Total Worker Time],
qs.total_worker_time/qs.execution_count AS [Avg Worker Time], 
qs.total_elapsed_time AS [Total Elapsed Time],
qs.total_elapsed_time/qs.execution_count AS [Avg Elapsed Time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index], 
qs.creation_time AS [Creation Time]
--,t.[text] AS [Complete Query Text], qp.query_plan AS [Query Plan] -- uncomment out these columns if not copying results to Excel
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t 
CROSS APPLY sys.dm_exec_query_plan(plan_handle) AS qp 
WHERE t.dbid = DB_ID()
ORDER BY qs.execution_count DESC OPTION (RECOMPILE);
------


-- Queries 50 through 55 are the "Bad Man List" for stored procedures
-- Top Cached SPs By Execution Count (Query 50) (SP Execution Counts)
SELECT TOP(100) p.name AS [SP Name], qs.execution_count AS [Execution Count],
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute],
qs.total_elapsed_time/qs.execution_count AS [Avg Elapsed Time],
qs.total_worker_time/qs.execution_count AS [Avg Worker Time],    
qs.total_logical_reads/qs.execution_count AS [Avg Logical Reads],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index],
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND DATEDIFF(Minute, qs.cached_time, GETDATE()) > 0
ORDER BY qs.execution_count DESC OPTION (RECOMPILE);
------

-- Tells you which cached stored procedures are called the most often
-- This helps you characterize and baseline your workload


-- Top Cached SPs By Avg Elapsed Time (Query 51) (SP Avg Elapsed Time)
SELECT TOP(25) p.name AS [SP Name], qs.min_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time], 
qs.max_elapsed_time, qs.last_elapsed_time, qs.total_elapsed_time, qs.execution_count, 
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute], 
qs.total_worker_time/qs.execution_count AS [AvgWorkerTime], 
qs.total_worker_time AS [TotalWorkerTime],
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND DATEDIFF(Minute, qs.cached_time, GETDATE()) > 0
ORDER BY avg_elapsed_time DESC OPTION (RECOMPILE);
------

-- This helps you find high average elapsed time cached stored procedures that
-- may be easy to optimize with standard query tuning techniques



-- Top Cached SPs By Total Worker time. Worker time relates to CPU cost  (Query 52) (SP Worker Time)
SELECT TOP(25) p.name AS [SP Name], qs.total_worker_time AS [TotalWorkerTime], 
qs.total_worker_time/qs.execution_count AS [AvgWorkerTime], qs.execution_count, 
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute],
qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index],
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND DATEDIFF(Minute, qs.cached_time, GETDATE()) > 0
ORDER BY qs.total_worker_time DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a CPU perspective
-- You should look at this if you see signs of CPU pressure


-- Top Cached SPs By Total Logical Reads. Logical reads relate to memory pressure  (Query 53) (SP Logical Reads)
SELECT TOP(25) p.name AS [SP Name], qs.total_logical_reads AS [TotalLogicalReads], 
qs.total_logical_reads/qs.execution_count AS [AvgLogicalReads],qs.execution_count, 
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute], 
qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index], 
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND DATEDIFF(Minute, qs.cached_time, GETDATE()) > 0
ORDER BY qs.total_logical_reads DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a memory perspective
-- You should look at this if you see signs of memory pressure


-- Top Cached SPs By Total Physical Reads. Physical reads relate to disk read I/O pressure  (Query 54) (SP Physical Reads)
SELECT TOP(25) p.name AS [SP Name],qs.total_physical_reads AS [TotalPhysicalReads], 
qs.total_physical_reads/qs.execution_count AS [AvgPhysicalReads], qs.execution_count, 
qs.total_logical_reads,qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index],
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan 
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND qs.total_physical_reads > 0
ORDER BY qs.total_physical_reads DESC, qs.total_logical_reads DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a read I/O perspective
-- You should look at this if you see signs of I/O pressure or of memory pressure


       
-- Top Cached SPs By Total Logical Writes (Query 55) (SP Logical Writes)
-- Logical writes relate to both memory and disk I/O pressure 
SELECT TOP(25) p.name AS [SP Name], qs.total_logical_writes AS [TotalLogicalWrites], 
qs.total_logical_writes/qs.execution_count AS [AvgLogicalWrites], qs.execution_count,
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute],
qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index], 
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan 
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND qs.total_logical_writes > 0
AND DATEDIFF(Minute, qs.cached_time, GETDATE()) > 0
ORDER BY qs.total_logical_writes DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a write I/O perspective
-- You should look at this if you see signs of I/O pressure or of memory pressure


-- Lists the top statements by average input/output usage for the current database  (Query 56) (Top IO Statements)
SELECT TOP(50) OBJECT_NAME(qt.objectid, dbid) AS [SP Name],
(qs.total_logical_reads + qs.total_logical_writes) /qs.execution_count AS [Avg IO], qs.execution_count AS [Execution Count],
SUBSTRING(qt.[text],qs.statement_start_offset/2, 
	(CASE 
		WHEN qs.statement_end_offset = -1 
	 THEN LEN(CONVERT(nvarchar(max), qt.[text])) * 2 
		ELSE qs.statement_end_offset 
	 END - qs.statement_start_offset)/2) AS [Query Text]	
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS qt
WHERE qt.[dbid] = DB_ID()
ORDER BY [Avg IO] DESC OPTION (RECOMPILE);
------

-- Helps you find the most expensive statements for I/O by SP



-- Possible Bad NC Indexes (writes > reads)  (Query 57) (Bad NC Indexes)
SELECT OBJECT_NAME(s.[object_id]) AS [Table Name], i.name AS [Index Name], i.index_id, 
i.is_disabled, i.is_hypothetical, i.has_filter, i.fill_factor,
s.user_updates AS [Total Writes], s.user_seeks + s.user_scans + s.user_lookups AS [Total Reads],
s.user_updates - (s.user_seeks + s.user_scans + s.user_lookups) AS [Difference]
FROM sys.dm_db_index_usage_stats AS s WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON s.[object_id] = i.[object_id]
AND i.index_id = s.index_id
WHERE OBJECTPROPERTY(s.[object_id],'IsUserTable') = 1
AND s.database_id = DB_ID()
AND s.user_updates > (s.user_seeks + s.user_scans + s.user_lookups)
AND i.index_id > 1 AND i.[type_desc] = N'NONCLUSTERED'
AND i.is_primary_key = 0 AND i.is_unique_constraint = 0 AND i.is_unique = 0
ORDER BY [Difference] DESC, [Total Writes] DESC, [Total Reads] ASC OPTION (RECOMPILE);
------

-- Look for indexes with high numbers of writes and zero or very low numbers of reads
-- Consider your complete workload, and how long your instance has been running
-- Investigate further before dropping an index!


-- Missing Indexes for current database by Index Advantage  (Query 58) (Missing Indexes)
SELECT CONVERT(decimal(18,2), user_seeks * avg_total_user_cost * (avg_user_impact * 0.01)) AS [index_advantage], 
migs.last_user_seek, mid.[statement] AS [Database.Schema.Table],
COUNT(1) OVER(PARTITION BY mid.[statement]) AS [missing_indexes_for_table],
COUNT(1) OVER(PARTITION BY mid.[statement], equality_columns) AS [similar_missing_indexes_for_table],
mid.equality_columns, mid.inequality_columns, mid.included_columns,
migs.unique_compiles, migs.user_seeks, 
CONVERT(decimal(18,2), migs.avg_total_user_cost) AS [avg_total_user_cost], migs.avg_user_impact,
OBJECT_NAME(mid.[object_id]) AS [Table Name], p.rows AS [Table Rows]
FROM sys.dm_db_missing_index_group_stats AS migs WITH (NOLOCK)
INNER JOIN sys.dm_db_missing_index_groups AS mig WITH (NOLOCK)
ON migs.group_handle = mig.index_group_handle
INNER JOIN sys.dm_db_missing_index_details AS mid WITH (NOLOCK)
ON mig.index_handle = mid.index_handle
INNER JOIN sys.partitions AS p WITH (NOLOCK)
ON p.[object_id] = mid.[object_id]
WHERE mid.database_id = DB_ID()
AND p.index_id < 2 
ORDER BY index_advantage DESC OPTION (RECOMPILE);
------

-- Look at index advantage, last user seek time, number of user seeks to help determine source and importance
-- SQL Server is overly eager to add included columns, so beware
-- Do not just blindly add indexes that show up from this query!!!


-- Find missing index warnings for cached plans in the current database  (Query 59) (Missing Index Warnings)
-- Note: This query could take some time on a busy instance
SELECT TOP(25) OBJECT_NAME(objectid) AS [ObjectName], 
               cp.objtype, cp.usecounts, cp.size_in_bytes, query_plan
FROM sys.dm_exec_cached_plans AS cp WITH (NOLOCK)
CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) AS qp
WHERE CAST(query_plan AS NVARCHAR(MAX)) LIKE N'%MissingIndex%'
AND dbid = DB_ID()
ORDER BY cp.usecounts DESC OPTION (RECOMPILE);
------

-- Helps you connect missing indexes to specific stored procedures or queries
-- This can help you decide whether to add them or not


-- Breaks down buffers used by current database by object (table, index) in the buffer cache  (Query 60) (Buffer Usage)
-- Note: This query could take some time on a busy instance
SELECT OBJECT_NAME(p.[object_id]) AS [Object Name], p.index_id, 
CAST(COUNT(*)/128.0 AS DECIMAL(10, 2)) AS [Buffer size(MB)],  
COUNT(*) AS [BufferCount], p.[Rows] AS [Row Count],
p.data_compression_desc AS [Compression Type]
FROM sys.allocation_units AS a WITH (NOLOCK)
INNER JOIN sys.dm_os_buffer_descriptors AS b WITH (NOLOCK)
ON a.allocation_unit_id = b.allocation_unit_id
INNER JOIN sys.partitions AS p WITH (NOLOCK)
ON a.container_id = p.hobt_id
WHERE b.database_id = CONVERT(int, DB_ID())
AND p.[object_id] > 100
AND OBJECT_NAME(p.[object_id]) NOT LIKE N'plan_%'
AND OBJECT_NAME(p.[object_id]) NOT LIKE N'sys%'
AND OBJECT_NAME(p.[object_id]) NOT LIKE N'xml_index_nodes%'
GROUP BY p.[object_id], p.index_id, p.data_compression_desc, p.[Rows]
ORDER BY [BufferCount] DESC OPTION (RECOMPILE);
------

-- Tells you what tables and indexes are using the most memory in the buffer cache
-- It can help identify possible candidates for data compression


-- Get Table names, row counts, and compression status for clustered index or heap  (Query 61) (Table Sizes)
SELECT OBJECT_NAME(object_id) AS [ObjectName], 
SUM(Rows) AS [RowCount], data_compression_desc AS [CompressionType]
FROM sys.partitions WITH (NOLOCK)
WHERE index_id < 2 --ignore the partitions from the non-clustered index if any
AND OBJECT_NAME(object_id) NOT LIKE N'sys%'
AND OBJECT_NAME(object_id) NOT LIKE N'queue_%' 
AND OBJECT_NAME(object_id) NOT LIKE N'filestream_tombstone%' 
AND OBJECT_NAME(object_id) NOT LIKE N'fulltext%'
AND OBJECT_NAME(object_id) NOT LIKE N'ifts_comp_fragment%'
AND OBJECT_NAME(object_id) NOT LIKE N'filetable_updates%'
AND OBJECT_NAME(object_id) NOT LIKE N'xml_index_nodes%'
AND OBJECT_NAME(object_id) NOT LIKE N'sqlagent_job%'
AND OBJECT_NAME(object_id) NOT LIKE N'plan_persist%'
GROUP BY object_id, data_compression_desc
ORDER BY SUM(Rows) DESC OPTION (RECOMPILE);
------

-- Gives you an idea of table sizes, and possible data compression opportunities



-- Get some key table properties (Query 62) (Table Properties)
SELECT OBJECT_NAME(t.[object_id]) AS [ObjectName], p.[rows] AS [Table Rows], p.index_id, 
       p.data_compression_desc AS [Index Data Compression],
       t.create_date, t.lock_on_bulk_load, t.is_replicated, t.has_replication_filter, 
       t.is_tracked_by_cdc, t.lock_escalation_desc, t.is_filetable
FROM sys.tables AS t WITH (NOLOCK)
INNER JOIN sys.partitions AS p WITH (NOLOCK)
ON t.[object_id] = p.[object_id]
WHERE OBJECT_NAME(t.[object_id]) NOT LIKE N'sys%'
ORDER BY OBJECT_NAME(t.[object_id]), p.index_id OPTION (RECOMPILE);
------

-- Gives you some good information about your tables



-- When were Statistics last updated on all indexes?  (Query 63) (Statistics Update)
SELECT SCHEMA_NAME(o.Schema_ID) + N'.' + o.NAME AS [Object Name], o.type_desc AS [Object Type],
      i.name AS [Index Name], STATS_DATE(i.[object_id], i.index_id) AS [Statistics Date], 
      s.auto_created, s.no_recompute, s.user_created, s.is_temporary,
	  st.row_count, st.used_page_count
FROM sys.objects AS o WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON o.[object_id] = i.[object_id]
INNER JOIN sys.stats AS s WITH (NOLOCK)
ON i.[object_id] = s.[object_id] 
AND i.index_id = s.stats_id
INNER JOIN sys.dm_db_partition_stats AS st WITH (NOLOCK)
ON o.[object_id] = st.[object_id]
AND i.[index_id] = st.[index_id]
WHERE o.[type] IN ('U', 'V')
AND st.row_count > 0
ORDER BY STATS_DATE(i.[object_id], i.index_id) DESC OPTION (RECOMPILE);
------  

-- Helps discover possible problems with out-of-date statistics
-- Also gives you an idea which indexes are the most active

-- sys.stats (Transact-SQL)
-- https://msdn.microsoft.com/en-us/library/ms177623.aspx



-- Look at most frequently modified indexes and statistics (Query 64) (Volatile Indexes)
SELECT o.[name] AS [Object Name], o.[object_id], o.[type_desc], s.[name] AS [Statistics Name], 
       s.stats_id, s.no_recompute, s.auto_created, s.is_temporary,
	   sp.modification_counter, sp.[rows], sp.rows_sampled, sp.last_updated
FROM sys.objects AS o WITH (NOLOCK)
INNER JOIN sys.stats AS s WITH (NOLOCK)
ON s.object_id = o.object_id
CROSS APPLY sys.dm_db_stats_properties(s.object_id, s.stats_id) AS sp
WHERE o.[type_desc] NOT IN (N'SYSTEM_TABLE', N'INTERNAL_TABLE')
AND sp.modification_counter > 0
ORDER BY sp.modification_counter DESC, o.name OPTION (RECOMPILE);
------

-- This helps you understand your workload and make better decisions about 
-- things like data compression and adding new indexes to a table



-- Get fragmentation info for all indexes above a certain size in the current database  (Query 65) (Index Fragmentation)
-- Note: This query could take some time on a very large database
SELECT DB_NAME(ps.database_id) AS [Database Name], SCHEMA_NAME(o.[schema_id]) AS [Schema Name],
OBJECT_NAME(ps.OBJECT_ID) AS [Object Name], i.[name] AS [Index Name], ps.index_id, 
ps.index_type_desc, ps.avg_fragmentation_in_percent, 
ps.fragment_count, ps.page_count, i.fill_factor, i.has_filter, 
i.filter_definition, i.[allow_page_locks]
FROM sys.dm_db_index_physical_stats(DB_ID(),NULL, NULL, NULL , N'LIMITED') AS ps
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON ps.[object_id] = i.[object_id] 
AND ps.index_id = i.index_id
INNER JOIN sys.objects AS o WITH (NOLOCK)
ON i.[object_id] = o.[object_id]
WHERE ps.database_id = DB_ID()
AND ps.page_count > 2500
ORDER BY ps.avg_fragmentation_in_percent DESC OPTION (RECOMPILE);
------

-- Helps determine whether you have framentation in your relational indexes
-- and how effective your index maintenance strategy is


--- Index Read/Write stats (all tables in current DB) ordered by Reads  (Query 66) (Overall Index Usage - Reads)
SELECT OBJECT_NAME(i.[object_id]) AS [ObjectName], i.[name] AS [IndexName], i.index_id, 
       s.user_seeks, s.user_scans, s.user_lookups,
	   s.user_seeks + s.user_scans + s.user_lookups AS [Total Reads], 
	   s.user_updates AS [Writes],  
	   i.[type_desc] AS [Index Type], i.fill_factor AS [Fill Factor], i.has_filter, i.filter_definition, 
	   s.last_user_scan, s.last_user_lookup, s.last_user_seek
FROM sys.indexes AS i WITH (NOLOCK)
LEFT OUTER JOIN sys.dm_db_index_usage_stats AS s WITH (NOLOCK)
ON i.[object_id] = s.[object_id]
AND i.index_id = s.index_id
AND s.database_id = DB_ID()
WHERE OBJECTPROPERTY(i.[object_id],'IsUserTable') = 1
ORDER BY s.user_seeks + s.user_scans + s.user_lookups DESC OPTION (RECOMPILE); -- Order by reads
------

-- Show which indexes in the current database are most active for Reads


--- Index Read/Write stats (all tables in current DB) ordered by Writes  (Query 67) (Overall Index Usage - Writes)
SELECT OBJECT_NAME(i.[object_id]) AS [ObjectName], i.[name] AS [IndexName], i.index_id,
	   s.user_updates AS [Writes], s.user_seeks + s.user_scans + s.user_lookups AS [Total Reads], 
	   i.[type_desc] AS [Index Type], i.fill_factor AS [Fill Factor], i.has_filter, i.filter_definition,
	   s.last_system_update, s.last_user_update
FROM sys.indexes AS i WITH (NOLOCK)
LEFT OUTER JOIN sys.dm_db_index_usage_stats AS s WITH (NOLOCK)
ON i.[object_id] = s.[object_id]
AND i.index_id = s.index_id
AND s.database_id = DB_ID()
WHERE OBJECTPROPERTY(i.[object_id],'IsUserTable') = 1
ORDER BY s.user_updates DESC OPTION (RECOMPILE);						 -- Order by writes
------

-- Show which indexes in the current database are most active for Writes


-- Get lock waits for current database (Query 68) (Lock Waits)
SELECT o.name AS [table_name], i.name AS [index_name], ios.index_id, ios.partition_number,
		SUM(ios.row_lock_wait_count) AS [total_row_lock_waits], 
		SUM(ios.row_lock_wait_in_ms) AS [total_row_lock_wait_in_ms],
		SUM(ios.page_lock_wait_count) AS [total_page_lock_waits],
		SUM(ios.page_lock_wait_in_ms) AS [total_page_lock_wait_in_ms],
		SUM(ios.page_lock_wait_in_ms)+ SUM(row_lock_wait_in_ms) AS [total_lock_wait_in_ms]
FROM sys.dm_db_index_operational_stats(DB_ID(), NULL, NULL, NULL) AS ios
INNER JOIN sys.objects AS o WITH (NOLOCK)
ON ios.[object_id] = o.[object_id]
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON ios.[object_id] = i.[object_id] 
AND ios.index_id = i.index_id
WHERE o.[object_id] > 100
GROUP BY o.name, i.name, ios.index_id, ios.partition_number
HAVING SUM(ios.page_lock_wait_in_ms)+ SUM(row_lock_wait_in_ms) > 0
ORDER BY total_lock_wait_in_ms DESC OPTION (RECOMPILE);
------

-- This query is helpful for troubleshooting blocking and deadlocking issues


-- Look at recent Full backups for the current database (Query 69) (Recent Full Backups)
SELECT TOP (30) bs.machine_name, bs.server_name, bs.database_name AS [Database Name], bs.recovery_model,
CONVERT (BIGINT, bs.backup_size / 1048576 ) AS [Uncompressed Backup Size (MB)],
CONVERT (BIGINT, bs.compressed_backup_size / 1048576 ) AS [Compressed Backup Size (MB)],
CONVERT (NUMERIC (20,2), (CONVERT (FLOAT, bs.backup_size) /
CONVERT (FLOAT, bs.compressed_backup_size))) AS [Compression Ratio], bs.has_backup_checksums, bs.is_copy_only,
DATEDIFF (SECOND, bs.backup_start_date, bs.backup_finish_date) AS [Backup Elapsed Time (sec)],
bs.backup_finish_date AS [Backup Finish Date], bmf.physical_device_name AS [Backup Location], bmf.physical_block_size
FROM msdb.dbo.backupset AS bs WITH (NOLOCK)
INNER JOIN msdb.dbo.backupmediafamily AS bmf WITH (NOLOCK)
ON bs.media_set_id = bmf.media_set_id  
WHERE bs.database_name = DB_NAME(DB_ID())
AND bs.[type] = 'D' -- Change to L if you want Log backups
ORDER BY bs.backup_finish_date DESC OPTION (RECOMPILE);
------

-- Are your backup sizes and times changing over time?
-- Are you using backup compression?
-- Are you using backup checksums?
-- Are you doing copy_only backups?
-- Have you done any backup tuning with striped backups, or changing the parameters of the backup command?


-- These three Pluralsight Courses go into more detail about how to run these queries and interpret the results

-- SQL Server 2014 DMV Diagnostic Queries � Part 1 
-- https://bit.ly/2plxCer

-- SQL Server 2014 DMV Diagnostic Queries � Part 2
-- https://bit.ly/2IuJpzI

-- SQL Server 2014 DMV Diagnostic Queries � Part 3
-- https://bit.ly/2FIlCPb



-- Sign up for Microsoft Visual Studio Dev Essentials and get a free three month pass to Pluralsight

-- Microsoft Visual Studio Dev Essentials
-- https://bit.ly/1q6xbDL


-- Sign up for Microsoft Azure Essentials and get lots of free Azure usage credits, MCP exam voucher, three month Pluralsight subscription

-- Microsoft Azure Essentials
-- https://bit.ly/2JMWe8x


-- August 2017 blog series about upgrading and migrating SQL Server
-- https://bit.ly/2ftKVrX
tools\dbatools\bin\diagnosticquery\SQLServerDiagnosticQueries_2014_201806.sql

-- SQL Server 2014 Diagnostic Information Queries
-- Glenn Berry 
-- Last Modified: July 10, 2018
-- https://www.sqlskills.com/blogs/glenn/
-- http://sqlserverperformance.wordpress.com/
-- Twitter: GlennAlanBerry

-- Please listen to my Pluralsight courses
-- https://www.pluralsight.com/author/glenn-berry

-- If you want to find all of our SQLskills SQL101 blog posts, check out https://www.sqlskills.com/help/sql101/


-- Please make sure you are using the correct version of these diagnostic queries for your version of SQL Server!


-- If you like PowerShell, there is a very useful community solution for running these queries in an automated fashion
-- https://dbatools.io/

-- Invoke-DbaDiagnosticQuery
-- https://dbatools.io/functions/invoke-dbadiagnosticquery/


--******************************************************************************
--*   Copyright (C) 2018 Glenn Berry, SQLskills.com
--*   All rights reserved. 
--*
--*   For more scripts and sample code, check out 
--*      https://www.sqlskills.com/blogs/glenn
--*
--*   You may alter this code for your own *non-commercial* purposes. You may
--*   republish altered code as long as you include this copyright and give due credit. 
--*
--*
--*   THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF 
--*   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED 
--*   TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
--*   PARTICULAR PURPOSE. 
--*
--******************************************************************************

-- Check the major product version to see if it is SQL Server 2014 CTP2 or greater
IF NOT EXISTS (SELECT * WHERE CONVERT(varchar(128), SERVERPROPERTY('ProductVersion')) LIKE '12%')
	BEGIN
		DECLARE @ProductVersion varchar(128) = CONVERT(varchar(128), SERVERPROPERTY('ProductVersion'));
		RAISERROR ('Script does not match the ProductVersion [%s] of this instance. Many of these queries may not work on this version.' , 18 , 16 , @ProductVersion);
	END
	ELSE
		PRINT N'You have the correct major version of SQL Server for this diagnostic information script';
	

-- Instance level queries *******************************

-- SQL and OS Version information for current instance  (Query 1) (Version Info)
SELECT @@SERVERNAME AS [Server Name], @@VERSION AS [SQL Server and OS Version Info];
------

-- SQL Server 2014 RTM Branch Builds						SQL Server 2014 SP1 Branch Builds						SQL Server 2014 SP2 Branch Builds					
-- Build			Description			Release Date		Build			Description		Release Date			Build			Description		Release Date		
-- 11.0.9120        CTP1				6/2/2013
-- 12.0.1524		CTP2				10/15/2013
-- 12.0.2000        RTM					4/1/2014
-- 12.0.2342        CU1                 4/21/2014
-- 12.0.2370        CU2                 6/27/2014
-- 12.0.2402		CU3					8/18/2014
-- 12.0.2430        CU4					10/21/2014
-- 12.0.2456		CU5					12/17/2014	---->	12.0.4100		SP1 RTM			5/4/2015
-- 12.0.2474		CU5 + COD HF		2/3/2015   
-- 12.0.2480		CU6					2/16/2015
-- 12.0.2495        CU7                 4/20/2015
-- 12.0.2546		CU8					6/19/2015			12.0.4416		SP1 CU1			6/19/2015
-- 12.0.2553		CU9					8/17/2015			12.0.4422		SP1 CU2			8/17/2015
-- 12.0.2556		CU10				10/19/2015          12.0.4427		SP1 CU3			10/19/2015
-- 12.0.2560		CU11				12/21/2015			12.0.4436		SP1 CU4			12/21/2015
-- 12.0.2564		CU12				2/22/2016			12.0.4439		SP1 CU5			2/22/2016
-- 12.0.2568		CU13				4/18/2016			12.0.4449		SP1 CU6			4/18/2016 (Deprecated)
--															12.0.4457		SP1 CU6 		5/30/2016 (Re-release)
-- 12.0.2569		CU14				6/20/2016			12.0.4459		SP1 CU7			6/20/2016 ----------->	12.0.5000		SP2 RTM			7/11/2016
--															12.0.4468		SP1 CU8			8/15/2016				12.0.5511		SP2 CU1			8/25/2016
--                                                          12.0.4474		SP1 CU9		    10/17/2016				12.0.5522		SP2 CU2			10/17/2016
--															12.0.4487		SP1 CU9 + HF	11/8/2016				12.0.5532		SP2 CU2 + HF    11/8/2016  https://technet.microsoft.com/library/security/MS16-136
--                                                          12.0.4491       SP1 CU10		12/28/2016				12.0.5537		SP2 CU3			12/28/2016
--															12.0.4502		SP1 CU11		2/21/2017				12.0.5540		SP2 CU4			2/21/2017
--															12.0.4511		SP1 CU12        4/17/2017				12.0.5546		SP2 CU5			4/17/2017
--															12.0.4522		SP1	CU13		7/17/2017				12.0.5552		SP2 CU6			7/17/2017
--																													12.0.5556		SP2 CU7			8/28/2017
--																													12.0.5557		SP2 CU8			10/16/2017
--																													12.0.5563		SP2 CU9			12/18/2017
--																													12.0.5571		SP2 CU10		1/16/2018
--                                                                                                                  12.0.5579		SP2 CU11		3/19/2018
--																													12.0.5589		SP2 CU12		6/18/2018	



-- How to determine the version, edition and update level of SQL Server and its components 
-- https://bit.ly/2oAjKgW	

-- SQL Server 2014 build versions
-- https://bit.ly/2HpmYOG

-- Where to find information about the latest SQL Server builds
-- https://bit.ly/2IGHbfY

-- Recommended updates and configuration options for SQL Server 2012 and SQL Server 2014 used with high-performance workloads
-- https://bit.ly/2Hy3zIZ

-- Performance and Stability Related Fixes in Post-SQL Server 2014 RTM Builds
-- https://bit.ly/2Hx50HU

-- Performance and Stability Related Fixes in Post-SQL Server 2014 SP1 Builds
-- https://bit.ly/2GWLx6a

-- Performance and Stability Related Fixes in Post-SQL Server 2014 SP2 Builds
-- https://bit.ly/2iJ9G4N

-- Announcing updates to the SQL Server Incremental Servicing Model (ISM)
-- https://bit.ly/1RzYITz

-- Update Center for Microsoft SQL Server
-- https://bit.ly/2pZptuQ

-- Download SQL Server Management Studio (SSMS)
-- https://bit.ly/1OcupT9

-- Download and install Microsoft SQL Operations Studio 
-- https://bit.ly/2vgke1A


-- Get socket, physical core and logical core count from the SQL Server Error log. (Query 2) (Core Counts)
-- This query might take a few seconds depending on the size of your error log
EXEC sys.xp_readerrorlog 0, 1, N'detected', N'socket';
------

-- This can help you determine the exact core counts used by SQL Server and whether HT is enabled or not
-- It can also help you confirm your SQL Server licensing model
-- Be on the lookout for this message "using 40 logical processors based on SQL Server licensing" 
-- (when you have more than 40 logical cores) which means grandfathered Server/CAL licensing
-- This query will return no results if your error log has been recycled since the instance was last started



-- Get selected server properties (Query 3) (Server Properties)
SELECT SERVERPROPERTY('MachineName') AS [MachineName], 
SERVERPROPERTY('ServerName') AS [ServerName],  
SERVERPROPERTY('InstanceName') AS [Instance], 
SERVERPROPERTY('IsClustered') AS [IsClustered], 
SERVERPROPERTY('ComputerNamePhysicalNetBIOS') AS [ComputerNamePhysicalNetBIOS], 
SERVERPROPERTY('Edition') AS [Edition], 
SERVERPROPERTY('ProductLevel') AS [ProductLevel],				-- What servicing branch (RTM/SP/CU)
SERVERPROPERTY('ProductUpdateLevel') AS [ProductUpdateLevel],	-- Within a servicing branch, what CU# is applied
SERVERPROPERTY('ProductVersion') AS [ProductVersion],
SERVERPROPERTY('ProductMajorVersion') AS [ProductMajorVersion], 
SERVERPROPERTY('ProductMinorVersion') AS [ProductMinorVersion], 
SERVERPROPERTY('ProductBuild') AS [ProductBuild], 
SERVERPROPERTY('ProductBuildType') AS [ProductBuildType],		      -- Is this a GDR or OD hotfix (NULL if on a CU build)
SERVERPROPERTY('ProductUpdateReference') AS [ProductUpdateReference], -- KB article number that is applicable for this build
SERVERPROPERTY('ProcessID') AS [ProcessID],
SERVERPROPERTY('Collation') AS [Collation], 
SERVERPROPERTY('IsFullTextInstalled') AS [IsFullTextInstalled], 
SERVERPROPERTY('IsIntegratedSecurityOnly') AS [IsIntegratedSecurityOnly],
SERVERPROPERTY('FilestreamConfiguredLevel') AS [FilestreamConfiguredLevel],
SERVERPROPERTY('IsHadrEnabled') AS [IsHadrEnabled], 
SERVERPROPERTY('HadrManagerStatus') AS [HadrManagerStatus],
SERVERPROPERTY('InstanceDefaultDataPath') AS [InstanceDefaultDataPath],
SERVERPROPERTY('InstanceDefaultLogPath') AS [InstanceDefaultLogPath],
SERVERPROPERTY('BuildClrVersion') AS [Build CLR Version],
SERVERPROPERTY('IsXTPSupported') AS [IsXTPSupported];
------

-- This gives you a lot of useful information about your instance of SQL Server,
-- such as the ProcessID for SQL Server and your collation
-- Note: Some columns will be NULL on older SQL Server builds

-- SERVERPROPERTY (Transact-SQL)
-- https://bit.ly/2eeaXeI


-- Get instance-level configuration values for instance  (Query 4) (Configuration Values)
SELECT name, value, value_in_use, minimum, maximum, [description], is_dynamic, is_advanced
FROM sys.configurations WITH (NOLOCK)
ORDER BY name OPTION (RECOMPILE);
------

-- Focus on these settings:
-- backup checksum default (should be 1)
-- backup compression default (should be 1 in most cases)
-- clr enabled (only enable if it is needed)
-- cost threshold for parallelism (depends on your workload)
-- lightweight pooling (should be zero)
-- max degree of parallelism (depends on your workload and hardware)
-- max server memory (MB) (set to an appropriate value, not the default)
-- optimize for ad hoc workloads (should be 1)
-- priority boost (should be zero)
-- remote admin connections (should be 1)


-- Returns a list of all global trace flags that are enabled (Query 5) (Global Trace Flags)
DBCC TRACESTATUS (-1);
------

-- If no global trace flags are enabled, no results will be returned.
-- It is very useful to know what global trace flags are currently enabled as part of the diagnostic process.

-- Common trace flags that should be enabled in most cases
-- TF 1117 - When growing a data file, grow all files at the same time so they remain the same size, reducing allocation contention points
--           https://bit.ly/2GY1kOl
-- 
-- TF 1118 - Helps alleviate allocation contention in tempdb, SQL Server allocates full extents to each database object, 
--           thereby eliminating the contention on SGAM pages (more important with older versions of SQL Server)
--           Recommendations to reduce allocation contention in SQL Server tempdb database
--           https://bit.ly/2GY1kOl

-- TF 2371 - Lowers auto update statistics threshold for large tables (on tables with more than 25,000 rows)
--           https://bit.ly/2HySkAg

-- TF 3226 - Supresses logging of successful database backup messages to the SQL Server Error Log
--           https://bit.ly/2p6MTjS

-- TF 3449 - Enables use of dirty page manager (SQL Server 2014 SP1 CU7 and later)
--			 https://bit.ly/2uj0h5M

-- TF 6533 - Spatial performance improvements in SQL Server 2012 and 2014
--           https://bit.ly/2v7C7ze

-- TF 6534 - Enables use of native code to improve performance with spatial data
--           https://bit.ly/2HrQUpU

-- TF 8079 - Enables automatic soft-NUMA on systems with eight or more physical cores per NUMA node (with SQL Server 2014 SP2)
--           https://bit.ly/29B7oR8

-- DBCC TRACEON - Trace Flags (Transact-SQL)
-- https://bit.ly/2FuSvPg



-- Returns status of instant file initialization (Query 6) (IFI Status)
EXEC sys.xp_readerrorlog 0, 1, N'Database Instant File Initialization';
------

-- Lets you determine whether Instant File Initialization (IFI) is enabled for the instance
-- This should be enabled in the vast majority of cases


-- Database Instant File Initialization
-- https://bit.ly/2nTX74y

-- Misconceptions around instant file initialization
-- https://bit.ly/2oBSKgZ



-- SQL Server Process Address space info  (Query 7) (Process Memory)
-- (shows whether locked pages is enabled, among other things)
SELECT physical_memory_in_use_kb/1024 AS [SQL Server Memory Usage (MB)],
	   locked_page_allocations_kb/1024 AS [SQL Server Locked Pages Allocation (MB)],
       large_page_allocations_kb/1024 AS [SQL Server Large Pages Allocation (MB)], 
	   page_fault_count, memory_utilization_percentage, available_commit_limit_kb, 
	   process_physical_memory_low, process_virtual_memory_low
FROM sys.dm_os_process_memory WITH (NOLOCK) OPTION (RECOMPILE);
------

-- You want to see 0 for process_physical_memory_low
-- You want to see 0 for process_virtual_memory_low
-- This indicates that you are not under internal memory pressure
-- If locked_page_allocations_kb > 0, then LPIM is enabled

-- How to enable the "locked pages" feature in SQL Server 2012
-- https://bit.ly/2F5UjOA

-- Memory Management Architecture Guide
-- https://bit.ly/2JKkadC 



-- SQL Server Services information (Query 8) (SQL Server Services Info)
SELECT servicename, process_id, startup_type_desc, status_desc, 
last_startup_time, service_account, is_clustered, cluster_nodename, [filename]
FROM sys.dm_server_services WITH (NOLOCK) OPTION (RECOMPILE);
------

-- Tells you the account being used for the SQL Server Service and the SQL Agent Service
-- Shows the process_id, when they were last started, and their current status
-- Also shows whether you are running on a failover cluster instance, and what node you are running on

-- sys.dm_server_services (Transact-SQL)
-- https://bit.ly/2oKa1Un


-- Last backup information by database  (Query 9) (Last Backup By Database)
SELECT ISNULL(d.[name], bs.[database_name]) AS [Database], d.recovery_model_desc AS [Recovery Model], 
       d.log_reuse_wait_desc AS [Log Reuse Wait Desc],
    MAX(CASE WHEN [type] = 'D' THEN bs.backup_finish_date ELSE NULL END) AS [Last Full Backup],
    MAX(CASE WHEN [type] = 'I' THEN bs.backup_finish_date ELSE NULL END) AS [Last Differential Backup],
    MAX(CASE WHEN [type] = 'L' THEN bs.backup_finish_date ELSE NULL END) AS [Last Log Backup]
FROM sys.databases AS d WITH (NOLOCK)
LEFT OUTER JOIN msdb.dbo.backupset AS bs WITH (NOLOCK)
ON bs.[database_name] = d.[name] 
AND bs.backup_finish_date > GETDATE()- 30
WHERE d.name <> N'tempdb'
GROUP BY ISNULL(d.[name], bs.[database_name]), d.recovery_model_desc, d.log_reuse_wait_desc, d.[name] 
ORDER BY d.recovery_model_desc, d.[name] OPTION (RECOMPILE);
------

-- This helps you spot runaway transaction logs and other issues with your backup schedule


-- Get SQL Server Agent jobs and Category information (Query 10) (SQL Server Agent Jobs)
SELECT sj.name AS [Job Name], sj.[description] AS [Job Description], SUSER_SNAME(sj.owner_sid) AS [Job Owner],
sj.date_created AS [Date Created], sj.[enabled] AS [Job Enabled], 
sj.notify_email_operator_id, sj.notify_level_email, sc.name AS [CategoryName],
s.[enabled] AS [Sched Enabled], js.next_run_date, js.next_run_time
FROM msdb.dbo.sysjobs AS sj WITH (NOLOCK)
INNER JOIN msdb.dbo.syscategories AS sc WITH (NOLOCK)
ON sj.category_id = sc.category_id
LEFT OUTER JOIN msdb.dbo.sysjobschedules AS js WITH (NOLOCK)
ON sj.job_id = js.job_id
LEFT OUTER JOIN msdb.dbo.sysschedules AS s WITH (NOLOCK)
ON js.schedule_id = s.schedule_id
ORDER BY sj.name OPTION (RECOMPILE);
------

-- Gives you some basic information about your SQL Server Agent jobs, who owns them and how they are configured
-- Look for Agent jobs that are not owned by sa
-- Look for jobs that have a notify_email_operator_id set to 0 (meaning no operator)
-- Look for jobs that have a notify_level_email set to 0 (meaning no e-mail is ever sent)
--
-- MSDN sysjobs documentation
-- https://bit.ly/2paDEOP 

-- SQL Server Maintenance Solution
-- https://bit.ly/1pgchQu  


-- Get SQL Server Agent Alert Information (Query 11) (SQL Server Agent Alerts)
SELECT name, event_source, message_id, severity, [enabled], has_notification, 
       delay_between_responses, occurrence_count, last_occurrence_date, last_occurrence_time
FROM msdb.dbo.sysalerts WITH (NOLOCK)
ORDER BY name OPTION (RECOMPILE);
------

-- Gives you some basic information about your SQL Server Agent Alerts 
-- (which are different from SQL Server Agent jobs)
-- Read more about Agent Alerts here: https://bit.ly/2Giz0Xf 



-- Windows information (Query 12) (Windows Info)
SELECT windows_release, windows_service_pack_level, 
       windows_sku, os_language_version
FROM sys.dm_os_windows_info WITH (NOLOCK) OPTION (RECOMPILE);
------

-- Gives you major OS version, Service Pack, Edition, and language info for the operating system
-- 10.0 is either Windows 10 or Windows Server 2016
-- 6.3 is either Windows 8.1, or Windows Server 2012 R2  
-- 6.2 is either Windows 8 or Windows Server 2012
-- 6.1 is either Windows 7 or Windows Server 2008 R2
-- 6.0 is either Windows Vista or Windows Server 2008

-- Windows SKU codes
-- 4 is Enterprise Edition
-- 7 is Standard Server Edition
-- 8 is Datacenter Server Edition
-- 10 is Enterprise Server Edition
-- 48 is Professional Edition
-- 161 is Pro for Workstations

-- 1033 for os_language_version is US-English

-- SQL Server 2014 requires Windows Server 2008 SP2 or newer

-- Hardware and Software Requirements for Installing SQL Server 2014
-- https://bit.ly/1yRYXkQ

-- Using SQL Server in Windows 8 and later versions of Windows operating system 
-- https://bit.ly/2F7Ax0P



-- SQL Server NUMA Node information  (Query 13) (SQL Server NUMA Info)
SELECT node_id, node_state_desc, memory_node_id, processor_group, online_scheduler_count, 
       idle_scheduler_count, active_worker_count, avg_load_balance, resource_monitor_state
FROM sys.dm_os_nodes WITH (NOLOCK) 
WHERE node_state_desc <> N'ONLINE DAC' OPTION (RECOMPILE);
------

-- Gives you some useful information about the composition and relative load on your NUMA nodes
-- You want to see an equal number of schedulers on each NUMA node
-- Watch out if SQL Server 2014 Standard Edition has been installed 
-- on a physical or virtual machine with more than four sockets or more than 16 physical cores

-- sys.dm_os_nodes (Transact-SQL)
-- https://bit.ly/2pn5Mw8

-- Balancing Your Available SQL Server Core Licenses Evenly Across NUMA Nodes
-- https://bit.ly/2vfC4Rq



-- Good basic information about OS memory amounts and state  (Query 14) (System Memory)
SELECT total_physical_memory_kb/1024 AS [Physical Memory (MB)], 
       available_physical_memory_kb/1024 AS [Available Memory (MB)], 
       total_page_file_kb/1024 AS [Total Page File (MB)], 
	   available_page_file_kb/1024 AS [Available Page File (MB)], 
	   system_cache_kb/1024 AS [System Cache (MB)],
       system_memory_state_desc AS [System Memory State]
FROM sys.dm_os_sys_memory WITH (NOLOCK) OPTION (RECOMPILE);
------

-- You want to see "Available physical memory is high" for System Memory State
-- This indicates that you are not under external memory pressure

-- Possible System Memory State values:
-- Available physical memory is high
-- Physical memory usage is steady
-- Available physical memory is low
-- Available physical memory is running low
-- Physical memory state is transitioning

-- sys.dm_os_sys_memory (Transact-SQL)
-- https://bit.ly/2pcV0xq



-- You can skip the next two queries if you know you don't have a clustered instance


-- Get information about your cluster nodes and their status  (Query 15) (Cluster Node Properties)
-- (if your database server is in a failover cluster)
SELECT NodeName, status_description, is_current_owner
FROM sys.dm_os_cluster_nodes WITH (NOLOCK) OPTION (RECOMPILE);
------

-- Knowing which node owns the cluster resources is critical
-- Especially when you are installing Windows or SQL Server updates
-- You will see no results if your instance is not clustered

-- Recommended hotfixes and updates for Windows Server 2012 R2-based failover clusters
-- https://bit.ly/1z5BfCw


-- Get information about any AlwaysOn AG cluster this instance is a part of (Query 16) (AlwaysOn AG Cluster)
SELECT cluster_name, quorum_type_desc, quorum_state_desc
FROM sys.dm_hadr_cluster WITH (NOLOCK) OPTION (RECOMPILE);
------

-- You will see no results if your instance is not using AlwaysOn AGs


-- Good overview of AG health and status (Query 17) (AlwaysOn AG Status)
SELECT ag.name AS [AG Name], ar.replica_server_name, ar.availability_mode_desc, adc.[database_name], 
       drs.is_local, drs.is_primary_replica, drs.synchronization_state_desc, drs.is_commit_participant, 
	   drs.synchronization_health_desc, drs.recovery_lsn, drs.truncation_lsn, drs.last_sent_lsn, 
	   drs.last_sent_time, drs.last_received_lsn, drs.last_received_time, drs.last_hardened_lsn, 
	   drs.last_hardened_time, drs.last_redone_lsn, drs.last_redone_time, drs.log_send_queue_size, 
	   drs.log_send_rate, drs.redo_queue_size, drs.redo_rate, drs.filestream_send_rate, 
	   drs.end_of_log_lsn, drs.last_commit_lsn, drs.last_commit_time, drs.database_state_desc 
FROM sys.dm_hadr_database_replica_states AS drs WITH (NOLOCK)
INNER JOIN sys.availability_databases_cluster AS adc WITH (NOLOCK)
ON drs.group_id = adc.group_id 
AND drs.group_database_id = adc.group_database_id
INNER JOIN sys.availability_groups AS ag WITH (NOLOCK)
ON ag.group_id = drs.group_id
INNER JOIN sys.availability_replicas AS ar WITH (NOLOCK)
ON drs.group_id = ar.group_id 
AND drs.replica_id = ar.replica_id
ORDER BY ag.name, ar.replica_server_name, adc.[database_name] OPTION (RECOMPILE);

-- You will see no results if your instance is not using AlwaysOn AGs


-- Hardware information from SQL Server 2014  (Query 18) (Hardware Info)
SELECT cpu_count AS [Logical CPU Count], scheduler_count, 
       hyperthread_ratio AS [Hyperthread Ratio],
       cpu_count/hyperthread_ratio AS [Physical CPU Count], 
       physical_memory_kb/1024 AS [Physical Memory (MB)], 
	   committed_kb/1024 AS [Committed Memory (MB)],
       committed_target_kb/1024 AS [Committed Target Memory (MB)],
       max_workers_count AS [Max Workers Count], 
	   affinity_type_desc AS [Affinity Type], 
       sqlserver_start_time AS [SQL Server Start Time], 
	   virtual_machine_type_desc AS [Virtual Machine Type]
FROM sys.dm_os_sys_info WITH (NOLOCK) OPTION (RECOMPILE);
------

-- Gives you some good basic hardware information about your database server
-- Cannot distinguish between HT and multi-core
-- Note: virtual_machine_type_desc of HYPERVISOR does not automatically mean you are running SQL Server inside of a VM
-- It merely indicates that you have a hypervisor running on your host

-- sys.dm_os_sys_info (Transact-SQL)
-- https://bit.ly/2pczOYs

-- Soft NUMA configuration was a new column for SQL Server 2016
-- OFF = Soft-NUMA feature is OFF
-- ON = SQL Server automatically determines the NUMA node sizes for Soft-NUMA
-- MANUAL = Manually configured soft-NUMA

-- Configure SQL Server to Use Soft-NUMA (SQL Server)
-- https://bit.ly/2HTpKJt



-- Get System Manufacturer and model number from SQL Server Error log (Query 19) (System Manufacturer)
EXEC sys.xp_readerrorlog 0, 1, N'Manufacturer';
------ 

-- This can help you determine the capabilities and capacities of your database server
-- Can also be used to confirm if you are running in a VM
-- This query might take a few seconds if you have not recycled your error log recently
-- This query will return no results if your error log has been recycled since the instance was started


-- Get BIOS date from Windows Registry (Query 20) (BIOS Date)
EXEC sys.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'HARDWARE\DESCRIPTION\System\BIOS', N'BiosReleaseDate';
------

-- Helps you understand whether the main system BIOS is up to date, and the possible age of the hardware
-- Not as useful for virtualization


-- Get processor description from Windows Registry  (Query 21) (Processor Description)
EXEC sys.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'HARDWARE\DESCRIPTION\System\CentralProcessor\0', N'ProcessorNameString';
------

-- Gives you the model number and rated clock speed of your processor(s)
-- Your processors may be running at less than the rated clock speed due
-- to the Windows Power Plan or hardware power management

-- You can use CPU-Z to get your actual CPU core speed and a lot of other useful information
-- https://bit.ly/QhR6xF

-- You can learn more about processor selection for SQL Server by following this link
-- https://bit.ly/2F3aVlP



-- See if buffer pool extension (BPE) is enabled (Query 22) (BPE Configuration)
SELECT [path], state_description, current_size_in_kb, 
CAST(current_size_in_kb/1048576.0 AS DECIMAL(10,2)) AS [Size (GB)]
FROM sys.dm_os_buffer_pool_extension_configuration WITH (NOLOCK) OPTION (RECOMPILE);
------

-- BPE is available in both Standard Edition and Enterprise Edition
-- It is a more interesting feature for Standard Edition

-- Buffer Pool Extension to SSDs in SQL Server 2014
-- https://bit.ly/1bm08m8

-- Buffer Pool Extension
-- https://bit.ly/2oBuieO



-- Look at buffer descriptors to see BPE usage by database (Query 23) (BPE Usage) 
SELECT DB_NAME(database_id) AS [Database Name], COUNT(page_id) AS [Page Count],
CAST(COUNT(*)/128.0 AS DECIMAL(10, 2)) AS [Buffer size(MB)], 
AVG(read_microsec) AS [Avg Read Time (microseconds)]
FROM sys.dm_os_buffer_descriptors WITH (NOLOCK)
WHERE database_id <> 32767
AND is_in_bpool_extension = 1
GROUP BY DB_NAME(database_id) 
ORDER BY [Buffer size(MB)] DESC OPTION (RECOMPILE);
------

-- You will see no results if BPE is not enabled or if there is no BPE usage


-- Get information on location, time and size of any memory dumps from SQL Server  (Query 24) (Memory Dump Info)
SELECT [filename], creation_time, size_in_bytes/1048576.0 AS [Size (MB)]
FROM sys.dm_server_memory_dumps WITH (NOLOCK) 
ORDER BY creation_time DESC OPTION (RECOMPILE);
------

-- This will not return any rows if you have 
-- not had any memory dumps (which is a good thing)

-- sys.dm_server_memory_dumps (Transact-SQL)
-- https://bit.ly/2elwWll



-- Look at Suspect Pages table (Query 25) (Suspect Pages)
SELECT DB_NAME(database_id) AS [Database Name], [file_id], page_id, 
       event_type, error_count, last_update_date 
FROM msdb.dbo.suspect_pages WITH (NOLOCK)
ORDER BY database_id OPTION (RECOMPILE);
------

-- event_type value descriptions
-- 1 = 823 error caused by an operating system CRC error
--     or 824 error other than a bad checksum or a torn page (for example, a bad page ID)
-- 2 = Bad checksum
-- 3 = Torn page
-- 4 = Restored (The page was restored after it was marked bad)
-- 5 = Repaired (DBCC repaired the page)
-- 7 = Deallocated by DBCC

-- Ideally, this query returns no results. The table is limited to 1000 rows.
-- If you do get results here, you should do further investigation to determine the root cause

-- Manage the suspect_pages Table
-- https://bit.ly/2Fvr1c9


-- Get number of data files in tempdb database (Query 26) (Tempdb Data Files)
EXEC sys.xp_readerrorlog 0, 1, N'The tempdb database has';
------

-- Get the number of data files in the tempdb database
-- 4-8 data files that are all the same size is a good starting point
-- This query will return no results if your error log has been recycled since the instance was last started
-- This will be blank unless you have Service Pack 2 or later


-- File names and paths for all user and system databases on instance  (Query 27) (Database Filenames and Paths)
SELECT DB_NAME([database_id]) AS [Database Name], 
       [file_id], [name], physical_name, [type_desc], state_desc,
	   is_percent_growth, growth,
	   CONVERT(bigint, growth/128.0) AS [Growth in MB], 
       CONVERT(bigint, size/128.0) AS [Total Size in MB]
FROM sys.master_files WITH (NOLOCK)
ORDER BY DB_NAME([database_id]), [file_id] OPTION (RECOMPILE);
------

-- Things to look at:
-- Are data files and log files on different drives?
-- Is everything on the C: drive?
-- Is tempdb on dedicated drives?
-- Is there only one tempdb data file?
-- Are all of the tempdb data files the same size?
-- Are there multiple data files for user databases?
-- Is percent growth enabled for any files (which is bad)?


-- Volume info for all LUNS that have database files on the current instance (Query 28) (Volume Info)
SELECT DISTINCT vs.volume_mount_point, vs.file_system_type, vs.logical_volume_name, 
CONVERT(DECIMAL(18,2), vs.total_bytes/1073741824.0) AS [Total Size (GB)],
CONVERT(DECIMAL(18,2), vs.available_bytes/1073741824.0) AS [Available Size (GB)],  
CONVERT(DECIMAL(18,2), vs.available_bytes * 1. / vs.total_bytes * 100.) AS [Space Free %],
vs.supports_compression, vs.is_compressed, 
vs.supports_sparse_files, vs.supports_alternate_streams
FROM sys.master_files AS f WITH (NOLOCK)
CROSS APPLY sys.dm_os_volume_stats(f.database_id, f.[file_id]) AS vs 
ORDER BY vs.volume_mount_point OPTION (RECOMPILE);
------

-- Shows you the total and free space on the LUNs where you have database files
-- Being low on free space can negatively affect performance

-- sys.dm_os_volume_stats (Transact-SQL)
-- https://bit.ly/2oBPNNr



-- Drive level latency information (Query 29) (Drive Level Latency)
-- Based on code from Jimmy May
SELECT tab.[Drive], tab.volume_mount_point AS [Volume Mount Point], 
	CASE 
		WHEN num_of_reads = 0 THEN 0 
		ELSE (io_stall_read_ms/num_of_reads) 
	END AS [Read Latency],
	CASE 
		WHEN num_of_writes = 0 THEN 0 
		ELSE (io_stall_write_ms/num_of_writes) 
	END AS [Write Latency],
	CASE 
		WHEN (num_of_reads = 0 AND num_of_writes = 0) THEN 0 
		ELSE (io_stall/(num_of_reads + num_of_writes)) 
	END AS [Overall Latency],
	CASE 
		WHEN num_of_reads = 0 THEN 0 
		ELSE (num_of_bytes_read/num_of_reads) 
	END AS [Avg Bytes/Read],
	CASE 
		WHEN num_of_writes = 0 THEN 0 
		ELSE (num_of_bytes_written/num_of_writes) 
	END AS [Avg Bytes/Write],
	CASE 
		WHEN (num_of_reads = 0 AND num_of_writes = 0) THEN 0 
		ELSE ((num_of_bytes_read + num_of_bytes_written)/(num_of_reads + num_of_writes)) 
	END AS [Avg Bytes/Transfer]
FROM (SELECT LEFT(UPPER(mf.physical_name), 2) AS Drive, SUM(num_of_reads) AS num_of_reads,
	         SUM(io_stall_read_ms) AS io_stall_read_ms, SUM(num_of_writes) AS num_of_writes,
	         SUM(io_stall_write_ms) AS io_stall_write_ms, SUM(num_of_bytes_read) AS num_of_bytes_read,
	         SUM(num_of_bytes_written) AS num_of_bytes_written, SUM(io_stall) AS io_stall, vs.volume_mount_point 
      FROM sys.dm_io_virtual_file_stats(NULL, NULL) AS vfs
      INNER JOIN sys.master_files AS mf WITH (NOLOCK)
      ON vfs.database_id = mf.database_id AND vfs.file_id = mf.file_id
	  CROSS APPLY sys.dm_os_volume_stats(mf.database_id, mf.[file_id]) AS vs 
      GROUP BY LEFT(UPPER(mf.physical_name), 2), vs.volume_mount_point) AS tab
ORDER BY [Overall Latency] OPTION (RECOMPILE);
------

-- Shows you the drive-level latency for reads and writes, in milliseconds
-- Latency above 30-40ms is usually a problem
-- These latency numbers include all file activity against all SQL Server 
-- database files on each drive since SQL Server was last started


-- Calculates average stalls per read, per write, and per total input/output for each database file  (Query 30) (IO Stalls by File)
SELECT DB_NAME(fs.database_id) AS [Database Name], CAST(fs.io_stall_read_ms/(1.0 + fs.num_of_reads) AS NUMERIC(10,1)) AS [avg_read_stall_ms],
CAST(fs.io_stall_write_ms/(1.0 + fs.num_of_writes) AS NUMERIC(10,1)) AS [avg_write_stall_ms],
CAST((fs.io_stall_read_ms + fs.io_stall_write_ms)/(1.0 + fs.num_of_reads + fs.num_of_writes) AS NUMERIC(10,1)) AS [avg_io_stall_ms],
CONVERT(DECIMAL(18,2), mf.size/128.0) AS [File Size (MB)], mf.physical_name, mf.type_desc, fs.io_stall_read_ms, fs.num_of_reads, 
fs.io_stall_write_ms, fs.num_of_writes, fs.io_stall_read_ms + fs.io_stall_write_ms AS [io_stalls], fs.num_of_reads + fs.num_of_writes AS [total_io],
io_stall_queued_read_ms AS [Resource Governor Total Read IO Latency (ms)], io_stall_queued_write_ms AS [Resource Governor Total Write IO Latency (ms)] 
FROM sys.dm_io_virtual_file_stats(null,null) AS fs
INNER JOIN sys.master_files AS mf WITH (NOLOCK)
ON fs.database_id = mf.database_id
AND fs.[file_id] = mf.[file_id]
ORDER BY avg_io_stall_ms DESC OPTION (RECOMPILE);
------

-- Helps determine which database files on the entire instance have the most I/O bottlenecks
-- This can help you decide whether certain LUNs are overloaded and whether you might
-- want to move some files to a different location or perhaps improve your I/O performance
-- These latency numbers include all file activity against each SQL Server 
-- database file since SQL Server was last started


-- Look for I/O requests taking longer than 15 seconds in the six most recent SQL Server Error Logs (Query 31) (IO Warnings)
CREATE TABLE #IOWarningResults(LogDate datetime, ProcessInfo sysname, LogText nvarchar(1000));

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 0, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 1, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 2, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 3, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 4, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 5, 1, N'taking longer than 15 seconds';

SELECT LogDate, ProcessInfo, LogText
FROM #IOWarningResults
ORDER BY LogDate DESC;

DROP TABLE #IOWarningResults;
------  

-- Finding 15 second I/O warnings in the SQL Server Error Log is useful evidence of
-- poor I/O performance (which might have many different causes)
-- Look to see if you see any patterns in the results (same files, same drives, same time of day, etc.)

-- Diagnostics in SQL Server help detect stalled and stuck I/O operations
-- https://bit.ly/2qtaw73



-- Recovery model, log reuse wait description, log file size, log usage size  (Query 32) (Database Properties)
-- and compatibility level for all databases on instance
SELECT db.[name] AS [Database Name], SUSER_SNAME(db.owner_sid) AS [Database Owner], db.recovery_model_desc AS [Recovery Model], 
db.state_desc, db.containment_desc, db.log_reuse_wait_desc AS [Log Reuse Wait Description], 
CONVERT(DECIMAL(18,2), ls.cntr_value/1024.0) AS [Log Size (MB)], CONVERT(DECIMAL(18,2), lu.cntr_value/1024.0) AS [Log Used (MB)],
CAST(CAST(lu.cntr_value AS FLOAT) / CAST(ls.cntr_value AS FLOAT)AS DECIMAL(18,2)) * 100 AS [Log Used %], 
db.[compatibility_level] AS [DB Compatibility Level], db.page_verify_option_desc AS [Page Verify Option], 
db.is_auto_create_stats_on, db.is_auto_update_stats_on, db.is_auto_update_stats_async_on, db.is_parameterization_forced, 
db.snapshot_isolation_state_desc, db.is_read_committed_snapshot_on, db.is_auto_close_on, db.is_auto_shrink_on, 
db.target_recovery_time_in_seconds, db.is_cdc_enabled, db.is_published, db.is_distributor, db.is_encrypted,
db.group_database_id, db.replica_id,db.is_memory_optimized_elevate_to_snapshot_on, 
db.delayed_durability_desc, db.is_auto_create_stats_incremental_on,
db.is_encrypted, de.encryption_state, de.percent_complete, de.key_algorithm, de.key_length      
FROM sys.databases AS db WITH (NOLOCK)
INNER JOIN sys.dm_os_performance_counters AS lu WITH (NOLOCK)
ON db.name = lu.instance_name
INNER JOIN sys.dm_os_performance_counters AS ls WITH (NOLOCK)
ON db.name = ls.instance_name
LEFT OUTER JOIN sys.dm_database_encryption_keys AS de WITH (NOLOCK)
ON db.database_id = de.database_id
WHERE lu.counter_name LIKE N'Log File(s) Used Size (KB)%' 
AND ls.counter_name LIKE N'Log File(s) Size (KB)%'
AND ls.cntr_value > 0 
ORDER BY db.[name] OPTION (RECOMPILE);
------

-- Things to look at:
-- How many databases are on the instance?
-- What recovery models are they using?
-- What is the log reuse wait description?
-- How full are the transaction logs?
-- What compatibility level are the databases on? 
-- What is the Page Verify Option? (should be CHECKSUM)
-- Is Auto Update Statistics Asynchronously enabled?
-- Make sure auto_shrink and auto_close are not enabled!

 

-- Missing Indexes for all databases by Index Advantage  (Query 33) (Missing Indexes All Databases)
SELECT CONVERT(decimal(18,2), user_seeks * avg_total_user_cost * (avg_user_impact * 0.01)) AS [index_advantage],
FORMAT(migs.last_user_seek, 'yyyy-MM-dd HH:mm:ss') AS [last_user_seek], 
mid.[statement] AS [Database.Schema.Table],
COUNT(1) OVER(PARTITION BY mid.[statement]) AS [missing_indexes_for_table],
COUNT(1) OVER(PARTITION BY mid.[statement], equality_columns) AS [similar_missing_indexes_for_table],
mid.equality_columns, mid.inequality_columns, mid.included_columns,
migs.unique_compiles, migs.user_seeks, 
CONVERT(decimal(18,2), migs.avg_total_user_cost) AS [avg_total_user_cost], migs.avg_user_impact 
FROM sys.dm_db_missing_index_group_stats AS migs WITH (NOLOCK)
INNER JOIN sys.dm_db_missing_index_groups AS mig WITH (NOLOCK)
ON migs.group_handle = mig.index_group_handle
INNER JOIN sys.dm_db_missing_index_details AS mid WITH (NOLOCK)
ON mig.index_handle = mid.index_handle
ORDER BY index_advantage DESC OPTION (RECOMPILE);
------

-- Getting missing index information for all of the databases on the instance is very useful
-- Look at last user seek time, number of user seeks to help determine source and importance
-- Also look at avg_user_impact and avg_total_user_cost to help determine importance
-- SQL Server is overly eager to add included columns, so beware
-- Do not just blindly add indexes that show up from this query!!!

-- SQL Server Index Design Guide
-- https://bit.ly/2qtZr4N



-- Get VLF Counts for all databases on the instance (Query 34) (VLF Counts)
-- (adapted from Michelle Ufford) 
CREATE TABLE #VLFInfo (RecoveryUnitID int, FileID  int,
					   FileSize bigint, StartOffset bigint,
					   FSeqNo      bigint, [Status]    bigint,
					   Parity      bigint, CreateLSN   numeric(38));
	 
CREATE TABLE #VLFCountResults(DatabaseName sysname, VLFCount int);
	 
EXEC sp_MSforeachdb N'Use [?]; 

				INSERT INTO #VLFInfo 
				EXEC sp_executesql N''DBCC LOGINFO([?])''; 
	 
				INSERT INTO #VLFCountResults 
				SELECT DB_NAME(), COUNT(*) 
				FROM #VLFInfo; 

				TRUNCATE TABLE #VLFInfo;'
	 
SELECT DatabaseName, VLFCount  
FROM #VLFCountResults
ORDER BY VLFCount DESC;
	 
DROP TABLE #VLFInfo;
DROP TABLE #VLFCountResults;
------

-- High VLF counts can affect write performance to the log file 
-- and they can make full database restores and crash recovery take much longer
-- Try to keep your VLF counts under 200 in most cases (depending on log file size)

-- Important change to VLF creation algorithm in SQL Server 2014
-- https://bit.ly/2Hsjbg4

-- SQL Server Transaction Log Architecture and Management Guide
-- https://bit.ly/2JjmQRZ



-- Get CPU utilization by database (Query 35) (CPU Usage by Database)
WITH DB_CPU_Stats
AS
(SELECT pa.DatabaseID, DB_Name(pa.DatabaseID) AS [Database Name], SUM(qs.total_worker_time/1000) AS [CPU_Time_Ms]
 FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
 CROSS APPLY (SELECT CONVERT(int, value) AS [DatabaseID] 
              FROM sys.dm_exec_plan_attributes(qs.plan_handle)
              WHERE attribute = N'dbid') AS pa
 GROUP BY DatabaseID)
SELECT ROW_NUMBER() OVER(ORDER BY [CPU_Time_Ms] DESC) AS [CPU Rank],
       [Database Name], [CPU_Time_Ms] AS [CPU Time (ms)], 
       CAST([CPU_Time_Ms] * 1.0 / SUM([CPU_Time_Ms]) OVER() * 100.0 AS DECIMAL(5, 2)) AS [CPU Percent]
FROM DB_CPU_Stats
WHERE DatabaseID <> 32767 -- ResourceDB
ORDER BY [CPU Rank] OPTION (RECOMPILE);
------

-- Helps determine which database is using the most CPU resources on the instance


-- Get I/O utilization by database (Query 36) (IO Usage By Database)
WITH Aggregate_IO_Statistics
AS
(SELECT DB_NAME(database_id) AS [Database Name],
CAST(SUM(num_of_bytes_read + num_of_bytes_written)/1048576 AS DECIMAL(12, 2)) AS io_in_mb
FROM sys.dm_io_virtual_file_stats(NULL, NULL) AS [DM_IO_STATS]
GROUP BY database_id)
SELECT ROW_NUMBER() OVER(ORDER BY io_in_mb DESC) AS [I/O Rank], [Database Name], io_in_mb AS [Total I/O (MB)],
       CAST(io_in_mb/ SUM(io_in_mb) OVER() * 100.0 AS DECIMAL(5,2)) AS [I/O Percent]
FROM Aggregate_IO_Statistics
ORDER BY [I/O Rank] OPTION (RECOMPILE);
------

-- Helps determine which database is using the most I/O resources on the instance


-- Get total buffer usage by database for current instance  (Query 37) (Total Buffer Usage by Database)
-- This make take some time to run on a busy instance
WITH AggregateBufferPoolUsage
AS
(SELECT DB_NAME(database_id) AS [Database Name],
CAST(COUNT(*) * 8/1024.0 AS DECIMAL (10,2))  AS [CachedSize]
FROM sys.dm_os_buffer_descriptors WITH (NOLOCK)
WHERE database_id <> 32767 -- ResourceDB
GROUP BY DB_NAME(database_id))
SELECT ROW_NUMBER() OVER(ORDER BY CachedSize DESC) AS [Buffer Pool Rank], [Database Name], CachedSize AS [Cached Size (MB)],
       CAST(CachedSize / SUM(CachedSize) OVER() * 100.0 AS DECIMAL(5,2)) AS [Buffer Pool Percent]
FROM AggregateBufferPoolUsage
ORDER BY [Buffer Pool Rank] OPTION (RECOMPILE);
------

-- Tells you how much memory (in the buffer pool) 
-- is being used by each database on the instance


-- Clear Wait Stats with this command
-- DBCC SQLPERF('sys.dm_os_wait_stats', CLEAR);

-- Isolate top waits for server instance since last restart or wait statistics clear  (Query 38) (Top Waits)
WITH [Waits] 
AS (SELECT wait_type, wait_time_ms/ 1000.0 AS [WaitS],
          (wait_time_ms - signal_wait_time_ms) / 1000.0 AS [ResourceS],
           signal_wait_time_ms / 1000.0 AS [SignalS],
           waiting_tasks_count AS [WaitCount],
           100.0 *  wait_time_ms / SUM (wait_time_ms) OVER() AS [Percentage],
           ROW_NUMBER() OVER(ORDER BY wait_time_ms DESC) AS [RowNum]
    FROM sys.dm_os_wait_stats WITH (NOLOCK)
    WHERE [wait_type] NOT IN (
        N'BROKER_EVENTHANDLER', N'BROKER_RECEIVE_WAITFOR', N'BROKER_TASK_STOP',
		N'BROKER_TO_FLUSH', N'BROKER_TRANSMITTER', N'CHECKPOINT_QUEUE',
        N'CHKPT', N'CLR_AUTO_EVENT', N'CLR_MANUAL_EVENT', N'CLR_SEMAPHORE',
        N'DBMIRROR_DBM_EVENT', N'DBMIRROR_EVENTS_QUEUE', N'DBMIRROR_WORKER_QUEUE',
		N'DBMIRRORING_CMD', N'DIRTY_PAGE_POLL', N'DISPATCHER_QUEUE_SEMAPHORE',
        N'EXECSYNC', N'FSAGENT', N'FT_IFTS_SCHEDULER_IDLE_WAIT', N'FT_IFTSHC_MUTEX',
        N'HADR_CLUSAPI_CALL', N'HADR_FILESTREAM_IOMGR_IOCOMPLETION', N'HADR_LOGCAPTURE_WAIT', 
		N'HADR_NOTIFICATION_DEQUEUE', N'HADR_TIMER_TASK', N'HADR_WORK_QUEUE',
        N'KSOURCE_WAKEUP', N'LAZYWRITER_SLEEP', N'LOGMGR_QUEUE', N'ONDEMAND_TASK_QUEUE',
        N'PWAIT_ALL_COMPONENTS_INITIALIZED', 
		N'PREEMPTIVE_OS_AUTHENTICATIONOPS', N'PREEMPTIVE_OS_CREATEFILE', N'PREEMPTIVE_OS_GENERICOPS',
		N'PREEMPTIVE_OS_LIBRARYOPS', N'PREEMPTIVE_OS_QUERYREGISTRY',
		N'PREEMPTIVE_HADR_LEASE_MECHANISM', N'PREEMPTIVE_SP_SERVER_DIAGNOSTICS',
		N'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP',
        N'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP', N'QDS_SHUTDOWN_QUEUE', N'REQUEST_FOR_DEADLOCK_SEARCH',
		N'RESOURCE_QUEUE', N'SERVER_IDLE_CHECK', N'SLEEP_BPOOL_FLUSH', N'SLEEP_DBSTARTUP',
		N'SLEEP_DCOMSTARTUP', N'SLEEP_MASTERDBREADY', N'SLEEP_MASTERMDREADY',
        N'SLEEP_MASTERUPGRADED', N'SLEEP_MSDBSTARTUP', N'SLEEP_SYSTEMTASK', N'SLEEP_TASK',
        N'SLEEP_TEMPDBSTARTUP', N'SNI_HTTP_ACCEPT', N'SP_SERVER_DIAGNOSTICS_SLEEP',
		N'SQLTRACE_BUFFER_FLUSH', N'SQLTRACE_INCREMENTAL_FLUSH_SLEEP', N'SQLTRACE_WAIT_ENTRIES',
		N'WAIT_FOR_RESULTS', N'WAITFOR', N'WAITFOR_TASKSHUTDOWN', N'WAIT_XTP_HOST_WAIT',
		N'WAIT_XTP_OFFLINE_CKPT_NEW_LOG', N'WAIT_XTP_CKPT_CLOSE', N'XE_DISPATCHER_JOIN',
        N'XE_DISPATCHER_WAIT', N'XE_TIMER_EVENT')
    AND waiting_tasks_count > 0)
SELECT
    MAX (W1.wait_type) AS [WaitType],
	CAST (MAX (W1.Percentage) AS DECIMAL (5,2)) AS [Wait Percentage],
	CAST ((MAX (W1.WaitS) / MAX (W1.WaitCount)) AS DECIMAL (16,4)) AS [AvgWait_Sec],
    CAST ((MAX (W1.ResourceS) / MAX (W1.WaitCount)) AS DECIMAL (16,4)) AS [AvgRes_Sec],
    CAST ((MAX (W1.SignalS) / MAX (W1.WaitCount)) AS DECIMAL (16,4)) AS [AvgSig_Sec], 
    CAST (MAX (W1.WaitS) AS DECIMAL (16,2)) AS [Wait_Sec],
    CAST (MAX (W1.ResourceS) AS DECIMAL (16,2)) AS [Resource_Sec],
    CAST (MAX (W1.SignalS) AS DECIMAL (16,2)) AS [Signal_Sec],
    MAX (W1.WaitCount) AS [Wait Count],
	CAST (N'https://www.sqlskills.com/help/waits/' + W1.wait_type AS XML) AS [Help/Info URL]
FROM Waits AS W1
INNER JOIN Waits AS W2
ON W2.RowNum <= W1.RowNum
GROUP BY W1.RowNum, W1.wait_type
HAVING SUM (W2.Percentage) - MAX (W1.Percentage) < 99 -- percentage threshold
OPTION (RECOMPILE);
------

-- Cumulative wait stats are not as useful on an idle instance that is not under load or performance pressure

-- SQL Server Wait Types Library (Paul Randal)
-- https://bit.ly/2ePzYO2

-- The SQL Server Wait Type Repository
-- https://bit.ly/1afzfjC

-- Wait statistics, or please tell me where it hurts
-- https://bit.ly/2wsQHQE

-- SQL Server 2005 Performance Tuning using the Waits and Queues
-- https://bit.ly/1o2NFoF

-- sys.dm_os_wait_stats (Transact-SQL)
-- https://bit.ly/2Hjq9Yl



-- Get a count of SQL connections by IP address (Query 39) (Connection Counts by IP Address)
SELECT ec.client_net_address, es.[program_name], es.[host_name], es.login_name, 
COUNT(ec.session_id) AS [connection count] 
FROM sys.dm_exec_sessions AS es WITH (NOLOCK) 
INNER JOIN sys.dm_exec_connections AS ec WITH (NOLOCK) 
ON es.session_id = ec.session_id 
GROUP BY ec.client_net_address, es.[program_name], es.[host_name], es.login_name  
ORDER BY ec.client_net_address, es.[program_name] OPTION (RECOMPILE);
------

-- This helps you figure where your database load is coming from
-- and verifies connectivity from other machines

-- Solving Connectivity errors to SQL Server
-- https://bit.ly/2EgzoD0



-- Get Average Task Counts (run multiple times)  (Query 40) (Avg Task Counts)
SELECT AVG(current_tasks_count) AS [Avg Task Count], 
AVG(work_queue_count) AS [Avg Work Queue Count],
AVG(runnable_tasks_count) AS [Avg Runnable Task Count],
AVG(pending_disk_io_count) AS [Avg Pending DiskIO Count]
FROM sys.dm_os_schedulers WITH (NOLOCK)
WHERE scheduler_id < 255 OPTION (RECOMPILE);
------

-- Sustained values above 10 suggest further investigation in that area
-- High Avg Task Counts are often caused by blocking/deadlocking or other resource contention

-- Sustained values above 1 suggest further investigation in that area
-- High Avg Runnable Task Counts are a good sign of CPU pressure
-- High Avg Pending DiskIO Counts are a sign of disk pressure

-- How to Do Some Very Basic SQL Server Monitoring
-- https://bit.ly/2q3Btgt



-- Detect blocking (run multiple times)  (Query 41) (Detect Blocking)
SELECT t1.resource_type AS [lock type], DB_NAME(resource_database_id) AS [database],
t1.resource_associated_entity_id AS [blk object],t1.request_mode AS [lock req],  -- lock requested
t1.request_session_id AS [waiter sid], t2.wait_duration_ms AS [wait time],       -- spid of waiter  
(SELECT [text] FROM sys.dm_exec_requests AS r WITH (NOLOCK)                      -- get sql for waiter
CROSS APPLY sys.dm_exec_sql_text(r.[sql_handle]) 
WHERE r.session_id = t1.request_session_id) AS [waiter_batch],
(SELECT SUBSTRING(qt.[text],r.statement_start_offset/2, 
    (CASE WHEN r.statement_end_offset = -1 
    THEN LEN(CONVERT(nvarchar(max), qt.[text])) * 2 
    ELSE r.statement_end_offset END - r.statement_start_offset)/2) 
FROM sys.dm_exec_requests AS r WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(r.[sql_handle]) AS qt
WHERE r.session_id = t1.request_session_id) AS [waiter_stmt],					-- statement blocked
t2.blocking_session_id AS [blocker sid],										-- spid of blocker
(SELECT [text] FROM sys.sysprocesses AS p										-- get sql for blocker
CROSS APPLY sys.dm_exec_sql_text(p.[sql_handle]) 
WHERE p.spid = t2.blocking_session_id) AS [blocker_batch]
FROM sys.dm_tran_locks AS t1 WITH (NOLOCK)
INNER JOIN sys.dm_os_waiting_tasks AS t2 WITH (NOLOCK)
ON t1.lock_owner_address = t2.resource_address OPTION (RECOMPILE);
------

-- Helps troubleshoot blocking and deadlocking issues
-- The results will change from second to second on a busy system
-- You should run this query multiple times when you see signs of blocking



-- Get CPU Utilization History for last 256 minutes (in one minute intervals)  (Query 42) (CPU Utilization History)
DECLARE @ts_now bigint = (SELECT cpu_ticks/(cpu_ticks/ms_ticks) FROM sys.dm_os_sys_info WITH (NOLOCK)); 

SELECT TOP(256) SQLProcessUtilization AS [SQL Server Process CPU Utilization], 
               SystemIdle AS [System Idle Process], 
               100 - SystemIdle - SQLProcessUtilization AS [Other Process CPU Utilization], 
               DATEADD(ms, -1 * (@ts_now - [timestamp]), GETDATE()) AS [Event Time] 
FROM (SELECT record.value('(./Record/@id)[1]', 'int') AS record_id, 
			record.value('(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]', 'int') 
			AS [SystemIdle], 
			record.value('(./Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization)[1]', 'int') 
			AS [SQLProcessUtilization], [timestamp] 
	  FROM (SELECT [timestamp], CONVERT(xml, record) AS [record] 
			FROM sys.dm_os_ring_buffers WITH (NOLOCK)
			WHERE ring_buffer_type = N'RING_BUFFER_SCHEDULER_MONITOR' 
			AND record LIKE N'%<SystemHealth>%') AS x) AS y 
ORDER BY record_id DESC OPTION (RECOMPILE);
------

-- Look at the trend over the entire period 
-- Also look at high sustained 'Other Process' CPU Utilization values
-- Note: This query sometimes gives inaccurate results (negative values)
-- on high core count (> 64 cores) systems


-- Get top total worker time queries for entire instance (Query 43) (Top Worker Time Queries)
SELECT TOP(50) DB_NAME(t.[dbid]) AS [Database Name], 
REPLACE(REPLACE(LEFT(t.[text], 255), CHAR(10),''), CHAR(13),'') AS [Short Query Text],  
qs.total_worker_time AS [Total Worker Time], qs.min_worker_time AS [Min Worker Time],
qs.total_worker_time/qs.execution_count AS [Avg Worker Time], 
qs.max_worker_time AS [Max Worker Time], 
qs.min_elapsed_time AS [Min Elapsed Time], 
qs.total_elapsed_time/qs.execution_count AS [Avg Elapsed Time], 
qs.max_elapsed_time AS [Max Elapsed Time],
qs.min_logical_reads AS [Min Logical Reads],
qs.total_logical_reads/qs.execution_count AS [Avg Logical Reads],
qs.max_logical_reads AS [Max Logical Reads], 
qs.execution_count AS [Execution Count],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index], 
qs.creation_time AS [Creation Time]
--,t.[text] AS [Query Text], qp.query_plan AS [Query Plan] -- uncomment out these columns if not copying results to Excel
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t 
CROSS APPLY sys.dm_exec_query_plan(plan_handle) AS qp 
ORDER BY qs.total_worker_time DESC OPTION (RECOMPILE);
------


-- Helps you find the most expensive queries from a CPU perspective across the entire instance
-- Can also help track down parameter sniffing issues



-- Page Life Expectancy (PLE) value for each NUMA node in current instance  (Query 44) (PLE by NUMA Node)
SELECT @@SERVERNAME AS [Server Name], RTRIM([object_name]) AS [Object Name], instance_name, cntr_value AS [Page Life Expectancy]
FROM sys.dm_os_performance_counters WITH (NOLOCK)
WHERE [object_name] LIKE N'%Buffer Node%' -- Handles named instances
AND counter_name = N'Page life expectancy' OPTION (RECOMPILE);
------

-- PLE is a good measurement of internal memory pressure
-- Higher PLE is better. Watch the trend over time, not the absolute value
-- This will only return one row for non-NUMA systems

-- Page Life Expectancy isn�t what you think�
-- https://bit.ly/2EgynLa


-- Memory Grants Pending value for current instance  (Query 45) (Memory Grants Pending)
SELECT @@SERVERNAME AS [Server Name], RTRIM([object_name]) AS [Object Name], cntr_value AS [Memory Grants Pending]                                                                                                       
FROM sys.dm_os_performance_counters WITH (NOLOCK)
WHERE [object_name] LIKE N'%Memory Manager%' -- Handles named instances
AND counter_name = N'Memory Grants Pending' OPTION (RECOMPILE);
------

-- Run multiple times, and run periodically if you suspect you are under memory pressure
-- Memory Grants Pending above zero for a sustained period is a very strong indicator of internal memory pressure


-- Memory Clerk Usage for instance  (Query 46) (Memory Clerk Usage)
-- Look for high value for CACHESTORE_SQLCP (Ad-hoc query plans)
SELECT TOP(10) mc.[type] AS [Memory Clerk Type], 
       CAST((SUM(mc.pages_kb)/1024.0) AS DECIMAL (15,2)) AS [Memory Usage (MB)] 
FROM sys.dm_os_memory_clerks AS mc WITH (NOLOCK)
GROUP BY mc.[type]  
ORDER BY SUM(mc.pages_kb) DESC OPTION (RECOMPILE);
------

-- MEMORYCLERK_SQLBUFFERPOOL was new for SQL Server 2012. It should be your highest consumer of memory

-- CACHESTORE_SQLCP  SQL Plans         
-- These are cached SQL statements or batches that aren't in stored procedures, functions and triggers
-- Watch out for high values for CACHESTORE_SQLCP
-- Enabling 'optimize for ad hoc workloads' at the instance level can help reduce this
-- Running DBCC FREESYSTEMCACHE ('SQL Plans') periodically may be required to better control this

-- CACHESTORE_OBJCP  Object Plans      
-- These are compiled plans for stored procedures, functions and triggers



-- Find single-use, ad-hoc and prepared queries that are bloating the plan cache  (Query 47) (Ad hoc Queries)
SELECT TOP(50) DB_NAME(t.[dbid]) AS [Database Name], t.[text] AS [Query Text], 
cp.objtype AS [Object Type], cp.cacheobjtype AS [Cache Object Type],  
cp.size_in_bytes/1024 AS [Plan Size in KB]
FROM sys.dm_exec_cached_plans AS cp WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t
WHERE cp.cacheobjtype = N'Compiled Plan' 
AND cp.objtype IN (N'Adhoc', N'Prepared') 
AND cp.usecounts = 1
ORDER BY cp.size_in_bytes DESC, DB_NAME(t.[dbid]) OPTION (RECOMPILE);
------

-- Gives you the text, type and size of single-use ad-hoc and prepared queries that waste space in the plan cache
-- Enabling 'optimize for ad hoc workloads' for the instance can help (SQL Server 2008 and above only)
-- Running DBCC FREESYSTEMCACHE ('SQL Plans') periodically may be required to better control this
-- Enabling forced parameterization for the database can help, but test first!

-- Plan cache, adhoc workloads and clearing the single-use plan cache bloat
-- https://bit.ly/2EfYOkl


-- Get top total logical reads queries for entire instance (Query 48) (Top Logical Reads Queries)
SELECT TOP(50) DB_NAME(t.[dbid]) AS [Database Name],
REPLACE(REPLACE(LEFT(t.[text], 255), CHAR(10),''), CHAR(13),'') AS [Short Query Text], 
qs.total_logical_reads AS [Total Logical Reads],
qs.min_logical_reads AS [Min Logical Reads],
qs.total_logical_reads/qs.execution_count AS [Avg Logical Reads],
qs.max_logical_reads AS [Max Logical Reads],   
qs.min_worker_time AS [Min Worker Time],
qs.total_worker_time/qs.execution_count AS [Avg Worker Time], 
qs.max_worker_time AS [Max Worker Time], 
qs.min_elapsed_time AS [Min Elapsed Time], 
qs.total_elapsed_time/qs.execution_count AS [Avg Elapsed Time], 
qs.max_elapsed_time AS [Max Elapsed Time],
qs.execution_count AS [Execution Count], 
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index],
qs.creation_time AS [Creation Time]
--,t.[text] AS [Complete Query Text], qp.query_plan AS [Query Plan] -- uncomment out these columns if not copying results to Excel
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t 
CROSS APPLY sys.dm_exec_query_plan(plan_handle) AS qp 
ORDER BY qs.total_logical_reads DESC OPTION (RECOMPILE);
------


-- Helps you find the most expensive queries from a memory perspective across the entire instance
-- Can also help track down parameter sniffing issues


-- Get top average elapsed time queries for entire instance (Query 49) (Top Avg Elapsed Time Queries)
SELECT TOP(50) DB_NAME(t.[dbid]) AS [Database Name], 
REPLACE(REPLACE(LEFT(t.[text], 255), CHAR(10),''), CHAR(13),'') AS [Short Query Text],  
qs.total_elapsed_time/qs.execution_count AS [Avg Elapsed Time],
qs.min_elapsed_time, qs.max_elapsed_time, qs.last_elapsed_time,
qs.execution_count AS [Execution Count],  
qs.total_logical_reads/qs.execution_count AS [Avg Logical Reads], 
qs.total_physical_reads/qs.execution_count AS [Avg Physical Reads], 
qs.total_worker_time/qs.execution_count AS [Avg Worker Time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index],
qs.creation_time AS [Creation Time]
--,t.[text] AS [Complete Query Text], qp.query_plan AS [Query Plan] -- uncomment out these columns if not copying results to Excel
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t 
CROSS APPLY sys.dm_exec_query_plan(plan_handle) AS qp 
ORDER BY qs.total_elapsed_time/qs.execution_count DESC OPTION (RECOMPILE);
------

-- Helps you find the highest average elapsed time queries across the entire instance
-- Can also help track down parameter sniffing issues




-- Database specific queries *****************************************************************

-- **** Please switch to a user database that you are interested in! *****
--USE YourDatabaseName; -- make sure to change to an actual database on your instance, not the master system database
--GO

-- Individual File Sizes and space available for current database  (Query 50) (File Sizes and Space)
SELECT f.name AS [File Name] , f.physical_name AS [Physical Name], 
CAST((f.size/128.0) AS DECIMAL(15,2)) AS [Total Size in MB],
CAST(f.size/128.0 - CAST(FILEPROPERTY(f.name, 'SpaceUsed') AS int)/128.0 AS DECIMAL(15,2)) 
AS [Available Space In MB], f.[file_id], fg.name AS [Filegroup Name],
f.is_percent_growth, f.growth, fg.is_default, fg.is_read_only
FROM sys.database_files AS f WITH (NOLOCK) 
LEFT OUTER JOIN sys.filegroups AS fg WITH (NOLOCK)
ON f.data_space_id = fg.data_space_id
ORDER BY f.[file_id] OPTION (RECOMPILE);
------

-- Look at how large and how full the files are and where they are located
-- Make sure the transaction log is not full!!


-- Log space usage for current database  (Query 51) (Log Space Usage)
SELECT DB_NAME(lsu.database_id) AS [Database Name], db.recovery_model_desc AS [Recovery Model],
		CAST(lsu.total_log_size_in_bytes/1048576.0 AS DECIMAL(10, 2)) AS [Total Log Space (MB)],
		CAST(lsu.used_log_space_in_bytes/1048576.0 AS DECIMAL(10, 2)) AS [Used Log Space (MB)], 
		CAST(lsu.used_log_space_in_percent AS DECIMAL(10, 2)) AS [Used Log Space %],
		CAST(lsu.log_space_in_bytes_since_last_backup/1048576.0 AS DECIMAL(10, 2)) AS [Used Log Space Since Last Backup (MB)],
		db.log_reuse_wait_desc		 
FROM sys.dm_db_log_space_usage AS lsu WITH (NOLOCK)
INNER JOIN sys.databases AS db WITH (NOLOCK)
ON lsu.database_id = db.database_id
OPTION (RECOMPILE);
------

-- Look at log file size and usage, along with the log reuse wait description for the current database


-- I/O Statistics by file for the current database  (Query 52) (IO Stats By File)
SELECT DB_NAME(DB_ID()) AS [Database Name], df.name AS [Logical Name], vfs.[file_id], df.type_desc,
df.physical_name AS [Physical Name], CAST(vfs.size_on_disk_bytes/1048576.0 AS DECIMAL(10, 2)) AS [Size on Disk (MB)],
vfs.num_of_reads, vfs.num_of_writes, vfs.io_stall_read_ms, vfs.io_stall_write_ms,
CAST(100. * vfs.io_stall_read_ms/(vfs.io_stall_read_ms + vfs.io_stall_write_ms) AS DECIMAL(10,1)) AS [IO Stall Reads Pct],
CAST(100. * vfs.io_stall_write_ms/(vfs.io_stall_write_ms + vfs.io_stall_read_ms) AS DECIMAL(10,1)) AS [IO Stall Writes Pct],
(vfs.num_of_reads + vfs.num_of_writes) AS [Writes + Reads], 
CAST(vfs.num_of_bytes_read/1048576.0 AS DECIMAL(10, 2)) AS [MB Read], 
CAST(vfs.num_of_bytes_written/1048576.0 AS DECIMAL(10, 2)) AS [MB Written],
CAST(100. * vfs.num_of_reads/(vfs.num_of_reads + vfs.num_of_writes) AS DECIMAL(10,1)) AS [# Reads Pct],
CAST(100. * vfs.num_of_writes/(vfs.num_of_reads + vfs.num_of_writes) AS DECIMAL(10,1)) AS [# Write Pct],
CAST(100. * vfs.num_of_bytes_read/(vfs.num_of_bytes_read + vfs.num_of_bytes_written) AS DECIMAL(10,1)) AS [Read Bytes Pct],
CAST(100. * vfs.num_of_bytes_written/(vfs.num_of_bytes_read + vfs.num_of_bytes_written) AS DECIMAL(10,1)) AS [Written Bytes Pct]
FROM sys.dm_io_virtual_file_stats(DB_ID(), NULL) AS vfs
INNER JOIN sys.database_files AS df WITH (NOLOCK)
ON vfs.[file_id]= df.[file_id] OPTION (RECOMPILE);
------

-- This helps you characterize your workload better from an I/O perspective for this database
-- It helps you determine whether you has an OLTP or DW/DSS type of workload



-- Get most frequently executed queries for this database (Query 53) (Query Execution Counts)
SELECT TOP(50) LEFT(t.[text], 50) AS [Short Query Text], qs.execution_count AS [Execution Count],
qs.total_logical_reads AS [Total Logical Reads],
qs.total_logical_reads/qs.execution_count AS [Avg Logical Reads],
qs.total_worker_time AS [Total Worker Time],
qs.total_worker_time/qs.execution_count AS [Avg Worker Time], 
qs.total_elapsed_time AS [Total Elapsed Time],
qs.total_elapsed_time/qs.execution_count AS [Avg Elapsed Time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index], 
qs.creation_time AS [Creation Time]
--,t.[text] AS [Complete Query Text], qp.query_plan AS [Query Plan] -- uncomment out these columns if not copying results to Excel
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t 
CROSS APPLY sys.dm_exec_query_plan(plan_handle) AS qp 
WHERE t.dbid = DB_ID()
ORDER BY qs.execution_count DESC OPTION (RECOMPILE);
------


-- Queries 54 through 59 are the "Bad Man List" for stored procedures

-- Top Cached SPs By Execution Count (Query 54) (SP Execution Counts)
SELECT TOP(100) p.name AS [SP Name], qs.execution_count AS [Execution Count],
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute],
qs.total_elapsed_time/qs.execution_count AS [Avg Elapsed Time],
qs.total_worker_time/qs.execution_count AS [Avg Worker Time],    
qs.total_logical_reads/qs.execution_count AS [Avg Logical Reads],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index],
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND DATEDIFF(Minute, qs.cached_time, GETDATE()) > 0
ORDER BY qs.execution_count DESC OPTION (RECOMPILE);
------

-- Tells you which cached stored procedures are called the most often
-- This helps you characterize and baseline your workload


-- Top Cached SPs By Avg Elapsed Time (Query 55) (SP Avg Elapsed Time)
SELECT TOP(25) p.name AS [SP Name], qs.min_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time], 
qs.max_elapsed_time, qs.last_elapsed_time, qs.total_elapsed_time, qs.execution_count, 
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute], 
qs.total_worker_time/qs.execution_count AS [AvgWorkerTime], 
qs.total_worker_time AS [TotalWorkerTime],
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND DATEDIFF(Minute, qs.cached_time, GETDATE()) > 0
ORDER BY avg_elapsed_time DESC OPTION (RECOMPILE);
------

-- This helps you find high average elapsed time cached stored procedures that
-- may be easy to optimize with standard query tuning techniques



-- Top Cached SPs By Total Worker time. Worker time relates to CPU cost  (Query 56) (SP Worker Time)
SELECT TOP(25) p.name AS [SP Name], qs.total_worker_time AS [TotalWorkerTime], 
qs.total_worker_time/qs.execution_count AS [AvgWorkerTime], qs.execution_count, 
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute],
qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index],
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND DATEDIFF(Minute, qs.cached_time, GETDATE()) > 0
ORDER BY qs.total_worker_time DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a CPU perspective
-- You should look at this if you see signs of CPU pressure


-- Top Cached SPs By Total Logical Reads. Logical reads relate to memory pressure  (Query 57) (SP Logical Reads)
SELECT TOP(25) p.name AS [SP Name], qs.total_logical_reads AS [TotalLogicalReads], 
qs.total_logical_reads/qs.execution_count AS [AvgLogicalReads],qs.execution_count, 
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute], 
qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index], 
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND DATEDIFF(Minute, qs.cached_time, GETDATE()) > 0
ORDER BY qs.total_logical_reads DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a memory perspective
-- You should look at this if you see signs of memory pressure


-- Top Cached SPs By Total Physical Reads. Physical reads relate to disk read I/O pressure  (Query 58) (SP Physical Reads)
SELECT TOP(25) p.name AS [SP Name],qs.total_physical_reads AS [TotalPhysicalReads], 
qs.total_physical_reads/qs.execution_count AS [AvgPhysicalReads], qs.execution_count, 
qs.total_logical_reads,qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index],
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan 
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND qs.total_physical_reads > 0
ORDER BY qs.total_physical_reads DESC, qs.total_logical_reads DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a read I/O perspective
-- You should look at this if you see signs of I/O pressure or of memory pressure


       
-- Top Cached SPs By Total Logical Writes (Query 59) (SP Logical Writes)
-- Logical writes relate to both memory and disk I/O pressure 
SELECT TOP(25) p.name AS [SP Name], qs.total_logical_writes AS [TotalLogicalWrites], 
qs.total_logical_writes/qs.execution_count AS [AvgLogicalWrites], qs.execution_count,
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute],
qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index], 
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan 
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND qs.total_logical_writes > 0
AND DATEDIFF(Minute, qs.cached_time, GETDATE()) > 0
ORDER BY qs.total_logical_writes DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a write I/O perspective
-- You should look at this if you see signs of I/O pressure or of memory pressure


-- Lists the top statements by average input/output usage for the current database  (Query 60) (Top IO Statements)
SELECT TOP(50) OBJECT_NAME(qt.objectid, dbid) AS [SP Name],
(qs.total_logical_reads + qs.total_logical_writes) /qs.execution_count AS [Avg IO], qs.execution_count AS [Execution Count],
SUBSTRING(qt.[text],qs.statement_start_offset/2, 
	(CASE 
		WHEN qs.statement_end_offset = -1 
	 THEN LEN(CONVERT(nvarchar(max), qt.[text])) * 2 
		ELSE qs.statement_end_offset 
	 END - qs.statement_start_offset)/2) AS [Query Text]	
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS qt
WHERE qt.[dbid] = DB_ID()
ORDER BY [Avg IO] DESC OPTION (RECOMPILE);
------

-- Helps you find the most expensive statements for I/O by SP



-- Possible Bad NC Indexes (writes > reads)  (Query 61) (Bad NC Indexes)
SELECT OBJECT_NAME(s.[object_id]) AS [Table Name], i.name AS [Index Name], i.index_id, 
i.is_disabled, i.is_hypothetical, i.has_filter, i.fill_factor,
s.user_updates AS [Total Writes], s.user_seeks + s.user_scans + s.user_lookups AS [Total Reads],
s.user_updates - (s.user_seeks + s.user_scans + s.user_lookups) AS [Difference]
FROM sys.dm_db_index_usage_stats AS s WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON s.[object_id] = i.[object_id]
AND i.index_id = s.index_id
WHERE OBJECTPROPERTY(s.[object_id],'IsUserTable') = 1
AND s.database_id = DB_ID()
AND s.user_updates > (s.user_seeks + s.user_scans + s.user_lookups)
AND i.index_id > 1 AND i.[type_desc] = N'NONCLUSTERED'
AND i.is_primary_key = 0 AND i.is_unique_constraint = 0 AND i.is_unique = 0
ORDER BY [Difference] DESC, [Total Writes] DESC, [Total Reads] ASC OPTION (RECOMPILE);
------

-- Look for indexes with high numbers of writes and zero or very low numbers of reads
-- Consider your complete workload, and how long your instance has been running
-- Investigate further before dropping an index!


-- Missing Indexes for current database by Index Advantage  (Query 62) (Missing Indexes)
SELECT CONVERT(decimal(18,2), user_seeks * avg_total_user_cost * (avg_user_impact * 0.01)) AS [index_advantage], 
migs.last_user_seek, mid.[statement] AS [Database.Schema.Table],
COUNT(1) OVER(PARTITION BY mid.[statement]) AS [missing_indexes_for_table],
COUNT(1) OVER(PARTITION BY mid.[statement], equality_columns) AS [similar_missing_indexes_for_table],
mid.equality_columns, mid.inequality_columns, mid.included_columns,
migs.unique_compiles, migs.user_seeks, 
CONVERT(decimal(18,2), migs.avg_total_user_cost) AS [avg_total_user_cost], migs.avg_user_impact,
OBJECT_NAME(mid.[object_id]) AS [Table Name], p.rows AS [Table Rows]
FROM sys.dm_db_missing_index_group_stats AS migs WITH (NOLOCK)
INNER JOIN sys.dm_db_missing_index_groups AS mig WITH (NOLOCK)
ON migs.group_handle = mig.index_group_handle
INNER JOIN sys.dm_db_missing_index_details AS mid WITH (NOLOCK)
ON mig.index_handle = mid.index_handle
INNER JOIN sys.partitions AS p WITH (NOLOCK)
ON p.[object_id] = mid.[object_id]
WHERE mid.database_id = DB_ID()
AND p.index_id < 2 
ORDER BY index_advantage DESC OPTION (RECOMPILE);
------

-- Look at index advantage, last user seek time, number of user seeks to help determine source and importance
-- SQL Server is overly eager to add included columns, so beware
-- Do not just blindly add indexes that show up from this query!!!


-- Find missing index warnings for cached plans in the current database  (Query 63) (Missing Index Warnings)
-- Note: This query could take some time on a busy instance
SELECT TOP(25) OBJECT_NAME(objectid) AS [ObjectName], 
               cp.objtype, cp.usecounts, cp.size_in_bytes, query_plan
FROM sys.dm_exec_cached_plans AS cp WITH (NOLOCK)
CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) AS qp
WHERE CAST(query_plan AS NVARCHAR(MAX)) LIKE N'%MissingIndex%'
AND dbid = DB_ID()
ORDER BY cp.usecounts DESC OPTION (RECOMPILE);
------

-- Helps you connect missing indexes to specific stored procedures or queries
-- This can help you decide whether to add them or not


-- Breaks down buffers used by current database by object (table, index) in the buffer cache  (Query 64) (Buffer Usage)
-- Note: This query could take some time on a busy instance
SELECT OBJECT_NAME(p.[object_id]) AS [Object Name], p.index_id, 
CAST(COUNT(*)/128.0 AS DECIMAL(10, 2)) AS [Buffer size(MB)],  
COUNT(*) AS [BufferCount], p.[Rows] AS [Row Count],
p.data_compression_desc AS [Compression Type]
FROM sys.allocation_units AS a WITH (NOLOCK)
INNER JOIN sys.dm_os_buffer_descriptors AS b WITH (NOLOCK)
ON a.allocation_unit_id = b.allocation_unit_id
INNER JOIN sys.partitions AS p WITH (NOLOCK)
ON a.container_id = p.hobt_id
WHERE b.database_id = CONVERT(int, DB_ID())
AND p.[object_id] > 100
AND OBJECT_NAME(p.[object_id]) NOT LIKE N'plan_%'
AND OBJECT_NAME(p.[object_id]) NOT LIKE N'sys%'
AND OBJECT_NAME(p.[object_id]) NOT LIKE N'xml_index_nodes%'
GROUP BY p.[object_id], p.index_id, p.data_compression_desc, p.[Rows]
ORDER BY [BufferCount] DESC OPTION (RECOMPILE);
------

-- Tells you what tables and indexes are using the most memory in the buffer cache
-- It can help identify possible candidates for data compression


-- Get Table names, row counts, and compression status for clustered index or heap  (Query 65) (Table Sizes)
SELECT OBJECT_NAME(object_id) AS [ObjectName], 
SUM(Rows) AS [RowCount], data_compression_desc AS [CompressionType]
FROM sys.partitions WITH (NOLOCK)
WHERE index_id < 2 --ignore the partitions from the non-clustered index if any
AND OBJECT_NAME(object_id) NOT LIKE N'sys%'
AND OBJECT_NAME(object_id) NOT LIKE N'queue_%' 
AND OBJECT_NAME(object_id) NOT LIKE N'filestream_tombstone%' 
AND OBJECT_NAME(object_id) NOT LIKE N'fulltext%'
AND OBJECT_NAME(object_id) NOT LIKE N'ifts_comp_fragment%'
AND OBJECT_NAME(object_id) NOT LIKE N'filetable_updates%'
AND OBJECT_NAME(object_id) NOT LIKE N'xml_index_nodes%'
AND OBJECT_NAME(object_id) NOT LIKE N'sqlagent_job%'
AND OBJECT_NAME(object_id) NOT LIKE N'plan_persist%'
GROUP BY object_id, data_compression_desc
ORDER BY SUM(Rows) DESC OPTION (RECOMPILE);
------

-- Gives you an idea of table sizes, and possible data compression opportunities



-- Get some key table properties (Query 66) (Table Properties)
SELECT OBJECT_NAME(t.[object_id]) AS [ObjectName], p.[rows] AS [Table Rows], p.index_id, 
       p.data_compression_desc AS [Index Data Compression],
       t.create_date, t.lock_on_bulk_load, t.is_replicated, t.has_replication_filter, 
       t.is_tracked_by_cdc, t.lock_escalation_desc, t.is_filetable, 
	   t.is_memory_optimized, t.durability_desc  -- new for SQL Server 2014
FROM sys.tables AS t WITH (NOLOCK)
INNER JOIN sys.partitions AS p WITH (NOLOCK)
ON t.[object_id] = p.[object_id]
WHERE OBJECT_NAME(t.[object_id]) NOT LIKE N'sys%'
ORDER BY OBJECT_NAME(t.[object_id]), p.index_id OPTION (RECOMPILE);
------

-- Gives you some good information about your tables
-- Is Memory optimized and durability description are Hekaton-related properties that were new in SQL Server 2014



-- When were Statistics last updated on all indexes?  (Query 67) (Statistics Update)
SELECT SCHEMA_NAME(o.Schema_ID) + N'.' + o.[NAME] AS [Object Name], o.[type_desc] AS [Object Type],
      i.[name] AS [Index Name], STATS_DATE(i.[object_id], i.index_id) AS [Statistics Date], 
      s.auto_created, s.no_recompute, s.user_created, s.is_incremental, s.is_temporary,
	  st.row_count, st.used_page_count
FROM sys.objects AS o WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON o.[object_id] = i.[object_id]
INNER JOIN sys.stats AS s WITH (NOLOCK)
ON i.[object_id] = s.[object_id] 
AND i.index_id = s.stats_id
INNER JOIN sys.dm_db_partition_stats AS st WITH (NOLOCK)
ON o.[object_id] = st.[object_id]
AND i.[index_id] = st.[index_id]
WHERE o.[type] IN ('U', 'V')
AND st.row_count > 0
ORDER BY STATS_DATE(i.[object_id], i.index_id) DESC OPTION (RECOMPILE);
------  

-- Helps discover possible problems with out-of-date statistics
-- Also gives you an idea which indexes are the most active

-- sys.stats (Transact-SQL)
-- https://bit.ly/2GyAxrn

-- UPDATEs to Statistics (Erin Stellato)
-- https://bit.ly/2vhrYQy



-- Look at most frequently modified indexes and statistics (Query 68) (Volatile Indexes)
SELECT o.[name] AS [Object Name], o.[object_id], o.[type_desc], s.[name] AS [Statistics Name], 
       s.stats_id, s.no_recompute, s.auto_created, s.is_incremental, s.is_temporary,
	   sp.modification_counter, sp.[rows], sp.rows_sampled, sp.last_updated
FROM sys.objects AS o WITH (NOLOCK)
INNER JOIN sys.stats AS s WITH (NOLOCK)
ON s.object_id = o.object_id
CROSS APPLY sys.dm_db_stats_properties(s.object_id, s.stats_id) AS sp
WHERE o.[type_desc] NOT IN (N'SYSTEM_TABLE', N'INTERNAL_TABLE')
AND sp.modification_counter > 0
ORDER BY sp.modification_counter DESC, o.name OPTION (RECOMPILE);
------

-- This helps you understand your workload and make better decisions about 
-- things like data compression and adding new indexes to a table



-- Get fragmentation info for all indexes above a certain size in the current database  (Query 69) (Index Fragmentation)
-- Note: This query could take some time on a very large database
SELECT DB_NAME(ps.database_id) AS [Database Name], SCHEMA_NAME(o.[schema_id]) AS [Schema Name],
OBJECT_NAME(ps.OBJECT_ID) AS [Object Name], i.[name] AS [Index Name], ps.index_id, 
ps.index_type_desc, ps.avg_fragmentation_in_percent, 
ps.fragment_count, ps.page_count, i.fill_factor, i.has_filter, 
i.filter_definition, i.[allow_page_locks]
FROM sys.dm_db_index_physical_stats(DB_ID(),NULL, NULL, NULL , N'LIMITED') AS ps
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON ps.[object_id] = i.[object_id] 
AND ps.index_id = i.index_id
INNER JOIN sys.objects AS o WITH (NOLOCK)
ON i.[object_id] = o.[object_id]
WHERE ps.database_id = DB_ID()
AND ps.page_count > 2500
ORDER BY ps.avg_fragmentation_in_percent DESC OPTION (RECOMPILE);
------

-- Helps determine whether you have framentation in your relational indexes
-- and how effective your index maintenance strategy is


--- Index Read/Write stats (all tables in current DB) ordered by Reads  (Query 70) (Overall Index Usage - Reads)
SELECT OBJECT_NAME(i.[object_id]) AS [ObjectName], i.[name] AS [IndexName], i.index_id, 
       s.user_seeks, s.user_scans, s.user_lookups,
	   s.user_seeks + s.user_scans + s.user_lookups AS [Total Reads], 
	   s.user_updates AS [Writes],  
	   i.[type_desc] AS [Index Type], i.fill_factor AS [Fill Factor], i.has_filter, i.filter_definition, 
	   s.last_user_scan, s.last_user_lookup, s.last_user_seek
FROM sys.indexes AS i WITH (NOLOCK)
LEFT OUTER JOIN sys.dm_db_index_usage_stats AS s WITH (NOLOCK)
ON i.[object_id] = s.[object_id]
AND i.index_id = s.index_id
AND s.database_id = DB_ID()
WHERE OBJECTPROPERTY(i.[object_id],'IsUserTable') = 1
ORDER BY s.user_seeks + s.user_scans + s.user_lookups DESC OPTION (RECOMPILE); -- Order by reads
------

-- Show which indexes in the current database are most active for Reads


--- Index Read/Write stats (all tables in current DB) ordered by Writes  (Query 71) (Overall Index Usage - Writes)
SELECT OBJECT_NAME(i.[object_id]) AS [ObjectName], i.[name] AS [IndexName], i.index_id,
	   s.user_updates AS [Writes], s.user_seeks + s.user_scans + s.user_lookups AS [Total Reads], 
	   i.[type_desc] AS [Index Type], i.fill_factor AS [Fill Factor], i.has_filter, i.filter_definition,
	   s.last_system_update, s.last_user_update
FROM sys.indexes AS i WITH (NOLOCK)
LEFT OUTER JOIN sys.dm_db_index_usage_stats AS s WITH (NOLOCK)
ON i.[object_id] = s.[object_id]
AND i.index_id = s.index_id
AND s.database_id = DB_ID()
WHERE OBJECTPROPERTY(i.[object_id],'IsUserTable') = 1
ORDER BY s.user_updates DESC OPTION (RECOMPILE);						 -- Order by writes
------

-- Show which indexes in the current database are most active for Writes


-- Get in-memory OLTP index usage (Query 72) (XTP Index Usage)
SELECT OBJECT_NAME(i.[object_id]) AS [Object Name], i.index_id, i.[name] AS [Index Name],
       i.[type_desc], xis.scans_started, xis.scans_retries, 
	   xis.rows_touched, xis.rows_returned
FROM sys.dm_db_xtp_index_stats AS xis WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON i.[object_id] = xis.[object_id] 
AND i.index_id = xis.index_id 
ORDER BY OBJECT_NAME(i.[object_id]) OPTION (RECOMPILE);
------

-- This gives you some index usage statistics for in-memory OLTP
-- Returns no data if you are not using in-memory OLTP

-- Guidelines for Using Indexes on Memory-Optimized Tables
-- https://bit.ly/2GCP8lF


-- Get lock waits for current database (Query 73) (Lock Waits)
SELECT o.name AS [table_name], i.name AS [index_name], ios.index_id, ios.partition_number,
		SUM(ios.row_lock_wait_count) AS [total_row_lock_waits], 
		SUM(ios.row_lock_wait_in_ms) AS [total_row_lock_wait_in_ms],
		SUM(ios.page_lock_wait_count) AS [total_page_lock_waits],
		SUM(ios.page_lock_wait_in_ms) AS [total_page_lock_wait_in_ms],
		SUM(ios.page_lock_wait_in_ms)+ SUM(row_lock_wait_in_ms) AS [total_lock_wait_in_ms]
FROM sys.dm_db_index_operational_stats(DB_ID(), NULL, NULL, NULL) AS ios
INNER JOIN sys.objects AS o WITH (NOLOCK)
ON ios.[object_id] = o.[object_id]
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON ios.[object_id] = i.[object_id] 
AND ios.index_id = i.index_id
WHERE o.[object_id] > 100
GROUP BY o.name, i.name, ios.index_id, ios.partition_number
HAVING SUM(ios.page_lock_wait_in_ms)+ SUM(row_lock_wait_in_ms) > 0
ORDER BY total_lock_wait_in_ms DESC OPTION (RECOMPILE);
------

-- This query is helpful for troubleshooting blocking and deadlocking issues


-- Get input buffer information for the current database (Query 74) (Input Buffer)
SELECT es.session_id, DB_NAME(es.database_id) AS [Database Name],
es.login_time, es.cpu_time, es.logical_reads,
es.[status], ib.event_info AS [Input Buffer]
FROM sys.dm_exec_sessions AS es WITH (NOLOCK)
CROSS APPLY sys.dm_exec_input_buffer(es.session_id, NULL) AS ib
WHERE es.database_id = DB_ID()
AND es.session_id > 50
AND es.session_id <> @@SPID OPTION (RECOMPILE);

-- Gives you input buffer information from all non-system sessions for the current database
-- Replaces DBCC INPUTBUFFER
-- Requires SQL Server 2014 SP2 or later

-- New DMF for retrieving input buffer in SQL Server
-- https://bit.ly/2uHKMbz


-- Look at recent Full backups for the current database (Query 75) (Recent Full Backups)
SELECT TOP (30) bs.machine_name, bs.server_name, bs.database_name AS [Database Name], bs.recovery_model,
CONVERT (BIGINT, bs.backup_size / 1048576 ) AS [Uncompressed Backup Size (MB)],
CONVERT (BIGINT, bs.compressed_backup_size / 1048576 ) AS [Compressed Backup Size (MB)],
CONVERT (NUMERIC (20,2), (CONVERT (FLOAT, bs.backup_size) /
CONVERT (FLOAT, bs.compressed_backup_size))) AS [Compression Ratio], bs.has_backup_checksums, bs.is_copy_only, bs.encryptor_type,
DATEDIFF (SECOND, bs.backup_start_date, bs.backup_finish_date) AS [Backup Elapsed Time (sec)],
bs.backup_finish_date AS [Backup Finish Date], bmf.physical_device_name AS [Backup Location], bmf.physical_block_size
FROM msdb.dbo.backupset AS bs WITH (NOLOCK)
INNER JOIN msdb.dbo.backupmediafamily AS bmf WITH (NOLOCK)
ON bs.media_set_id = bmf.media_set_id  
WHERE bs.database_name = DB_NAME(DB_ID())
AND bs.[type] = 'D' -- Change to L if you want Log backups
ORDER BY bs.backup_finish_date DESC OPTION (RECOMPILE);
------

-- Are your backup sizes and times changing over time?
-- Are you using backup compression?
-- Are you using backup checksums?
-- Are you doing copy_only backups?
-- Are you doing encrypted backups?
-- Have you done any backup tuning with striped backups, or changing the parameters of the backup command?


-- These three Pluralsight Courses go into more detail about how to run these queries and interpret the results

-- SQL Server 2014 DMV Diagnostic Queries � Part 1 
-- https://bit.ly/2plxCer

-- SQL Server 2014 DMV Diagnostic Queries � Part 2
-- https://bit.ly/2IuJpzI

-- SQL Server 2014 DMV Diagnostic Queries � Part 3
-- https://bit.ly/2FIlCPb



-- Sign up for Microsoft Visual Studio Dev Essentials and get a free three month pass to Pluralsight

-- Microsoft Visual Studio Dev Essentials
-- https://bit.ly/1q6xbDL


-- Sign up for Microsoft Azure Essentials and get lots of free Azure usage credits, MCP exam voucher, three month Pluralsight subscription

-- Microsoft Azure Essentials
-- https://bit.ly/2JMWe8x


-- August 2017 blog series about upgrading and migrating SQL Server
-- https://bit.ly/2ftKVrX
tools\dbatools\bin\diagnosticquery\SQLServerDiagnosticQueries_2016SP2_201806.sql

-- SQL Server 2016 SP2 Diagnostic Information Queries
-- Glenn Berry 
-- Last Modified: July 23, 2018
-- https://www.sqlskills.com/blogs/glenn/
-- http://sqlserverperformance.wordpress.com/
-- Twitter: GlennAlanBerry

-- Please listen to my Pluralsight courses
-- https://www.pluralsight.com/author/glenn-berry

-- If you want to find all of our SQLskills SQL101 blog posts, check out https://bit.ly/2qLwfXW


-- Please make sure you are using the correct version of these diagnostic queries for your version of SQL Server


-- If you like PowerShell, there is a very useful community solution for running these queries in an automated fashion
-- https://dbatools.io/

-- Invoke-DbaDiagnosticQuery
-- https://dbatools.io/functions/invoke-dbadiagnosticquery/


--******************************************************************************
--*   Copyright (C) 2018 Glenn Berry, SQLskills.com
--*   All rights reserved. 
--*
--*   For more scripts and sample code, check out 
--*      https://www.sqlskills.com/blogs/glenn
--*
--*   You may alter this code for your own *non-commercial* purposes. You may
--*   republish altered code as long as you include this copyright and give due credit. 
--*
--*
--*   THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF 
--*   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED 
--*   TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
--*   PARTICULAR PURPOSE. 
--*
--******************************************************************************

-- Check the major product version to see if it is SQL Server 2017 CTP 1 or greater
IF EXISTS (SELECT * WHERE CONVERT(varchar(128), SERVERPROPERTY('ProductVersion')) LIKE '13%')
	BEGIN
		IF CONVERT(int, SERVERPROPERTY('ProductBuild')) >= 5026
			PRINT N'You have the correct Service Pack of SQL Server 2016 for this diagnostic information script';
		IF CONVERT(int, SERVERPROPERTY('ProductBuild')) < 5026
			PRINT N'You do NOT have the correct Service Pack of SQL Server 2016 for this diagnostic information script';		
	END
ELSE
	BEGIN
		DECLARE @ProductVersion varchar(128) = CONVERT(varchar(128), SERVERPROPERTY('ProductVersion'));
		RAISERROR ('Script does not match the ProductVersion [%s] of this instance. Many of these queries may not work on this version.' , 18 , 16 , @ProductVersion);
	END
	
	

-- Instance level queries *******************************

-- SQL and OS Version information for current instance  (Query 1) (Version Info)
SELECT @@SERVERNAME AS [Server Name], @@VERSION AS [SQL Server and OS Version Info];
------

-- SQL Server 2016 Builds																		
-- Build			Description			Release Date	URL to KB Article								
-- 13.0.5026.0		SP2 RTM				4/24/2018		https://bit.ly/2FEvN2q 
-- 13.0.5149.0		SP2 CU1				5/30/2018		https://support.microsoft.com/en-us/help/4135048/cumulative-update-1-for-sql-server-2016-sp2
-- 13.0.5153.0		SP2 CU2				7/16/2018		https://support.microsoft.com/en-us/help/4340355	

		
															

-- How to determine the version, edition and update level of SQL Server and its components 
-- https://bit.ly/2oAjKgW														

-- How to obtain the latest Service Pack for SQL Server 2016
-- https://bit.ly/2egtfzK

-- SQL Server 2016 build versions 
-- https://bit.ly/2epkTDT

-- Where to find information about the latest SQL Server builds
-- https://bit.ly/2IGHbfY

-- Performance and Stability Related Fixes in Post-SQL Server 2016 SP2 Builds
-- https://bit.ly/2K3LoPf		

-- Announcing updates to the SQL Server Incremental Servicing Model (ISM)
-- https://bit.ly/1RzYITz

-- Update Center for Microsoft SQL Server
-- https://bit.ly/2pZptuQ

-- Download SQL Server Management Studio (SSMS)
-- https://bit.ly/1OcupT9

-- Download and install Microsoft SQL Operations Studio 
-- https://bit.ly/2vgke1A



-- Get socket, physical core and logical core count from the SQL Server Error log. (Query 2) (Core Counts)
-- This query might take a few seconds depending on the size of your error log
EXEC sys.xp_readerrorlog 0, 1, N'detected', N'socket';
------

-- This can help you determine the exact core counts used by SQL Server and whether HT is enabled or not
-- It can also help you confirm your SQL Server licensing model
-- Be on the lookout for this message "using 40 logical processors based on SQL Server licensing" 
-- (when you have more than 40 logical cores) which means grandfathered Server/CAL licensing
-- This query will return no results if your error log has been recycled since the instance was last started



-- Get selected server properties (Query 3) (Server Properties)
SELECT SERVERPROPERTY('MachineName') AS [MachineName], 
SERVERPROPERTY('ServerName') AS [ServerName],  
SERVERPROPERTY('InstanceName') AS [Instance], 
SERVERPROPERTY('IsClustered') AS [IsClustered], 
SERVERPROPERTY('ComputerNamePhysicalNetBIOS') AS [ComputerNamePhysicalNetBIOS], 
SERVERPROPERTY('Edition') AS [Edition], 
SERVERPROPERTY('ProductLevel') AS [ProductLevel],				-- What servicing branch (RTM/SP/CU)
SERVERPROPERTY('ProductUpdateLevel') AS [ProductUpdateLevel],	-- Within a servicing branch, what CU# is applied
SERVERPROPERTY('ProductVersion') AS [ProductVersion],
SERVERPROPERTY('ProductMajorVersion') AS [ProductMajorVersion], 
SERVERPROPERTY('ProductMinorVersion') AS [ProductMinorVersion], 
SERVERPROPERTY('ProductBuild') AS [ProductBuild], 
SERVERPROPERTY('ProductBuildType') AS [ProductBuildType],			  -- Is this a GDR or OD hotfix (NULL if on a CU build)
SERVERPROPERTY('ProductUpdateReference') AS [ProductUpdateReference], -- KB article number that is applicable for this build
SERVERPROPERTY('ProcessID') AS [ProcessID],
SERVERPROPERTY('Collation') AS [Collation], 
SERVERPROPERTY('IsFullTextInstalled') AS [IsFullTextInstalled], 
SERVERPROPERTY('IsIntegratedSecurityOnly') AS [IsIntegratedSecurityOnly],
SERVERPROPERTY('FilestreamConfiguredLevel') AS [FilestreamConfiguredLevel],
SERVERPROPERTY('IsHadrEnabled') AS [IsHadrEnabled], 
SERVERPROPERTY('HadrManagerStatus') AS [HadrManagerStatus],
SERVERPROPERTY('InstanceDefaultDataPath') AS [InstanceDefaultDataPath],
SERVERPROPERTY('InstanceDefaultLogPath') AS [InstanceDefaultLogPath],
SERVERPROPERTY('BuildClrVersion') AS [Build CLR Version],
SERVERPROPERTY('IsXTPSupported') AS [IsXTPSupported],
SERVERPROPERTY('IsPolybaseInstalled') AS [IsPolybaseInstalled],				-- New for SQL Server 2016
SERVERPROPERTY('IsAdvancedAnalyticsInstalled') AS [IsRServicesInstalled];	-- New for SQL Server 2016
------

-- This gives you a lot of useful information about your instance of SQL Server,
-- such as the ProcessID for SQL Server and your collation
-- Note: Some columns will be NULL on older SQL Server builds

-- SERVERPROPERTY (Transact-SQL)
-- https://bit.ly/2eeaXeI



-- Get instance-level configuration values for instance  (Query 4) (Configuration Values)
SELECT name, value, value_in_use, minimum, maximum, [description], is_dynamic, is_advanced
FROM sys.configurations WITH (NOLOCK)
ORDER BY name OPTION (RECOMPILE);
------

-- Focus on these settings:
-- automatic soft-NUMA disabled (should be 0 in most cases)
-- backup checksum default (should be 1)
-- backup compression default (should be 1 in most cases)
-- clr enabled (only enable if it is needed)
-- cost threshold for parallelism (depends on your workload)
-- lightweight pooling (should be zero)
-- max degree of parallelism (depends on your workload and hardware)
-- max server memory (MB) (set to an appropriate value, not the default)
-- optimize for ad hoc workloads (should be 1)
-- priority boost (should be zero)
-- remote admin connections (should be 1)


-- sys.configurations (Transact-SQL)
-- https://bit.ly/2HsyDZI


-- Returns a list of all global trace flags that are enabled (Query 5) (Global Trace Flags)
DBCC TRACESTATUS (-1);
------

-- If no global trace flags are enabled, no results will be returned.
-- It is very useful to know what global trace flags are currently enabled as part of the diagnostic process.

-- Common trace flags that should be enabled in most cases
-- TF 3226 - Supresses logging of successful database backup messages to the SQL Server Error Log
--           https://bit.ly/2p6MTjS  

-- TF 6534 - Enables use of native code to improve performance with spatial data
--           https://bit.ly/2HrQUpU         

-- The behavior of TF 1117, 1118 are enabled for tempdb in SQL Server 2016 by default
-- SQL 2016 � It Just Runs Faster: -T1117 and -T1118 changes for TEMPDB and user databases
-- https://bit.ly/2lbNWxK           

-- The behavior of TF 2371 is enabled by default in SQL Server 2016 and newer (in compat level 130 and higher)

-- DBCC TRACEON - Trace Flags (Transact-SQL)
-- https://bit.ly/2FuSvPg





-- Returns status of instant file initialization (Query 6) (IFI Status)
EXEC sys.xp_readerrorlog 0, 1, N'Database Instant File Initialization';
------

-- Lets you determine whether Instant File Initialization (IFI) is enabled for the instance
-- This should be enabled in the vast majority of cases
-- SQL Server 2016 and newer lets you enable this during the SQL server installation process

-- Database Instant File Initialization
-- https://bit.ly/2nTX74y

-- Misconceptions around instant file initialization
-- https://bit.ly/2oBSKgZ



-- SQL Server Process Address space info  (Query 7) (Process Memory)
-- (shows whether locked pages is enabled, among other things)
SELECT physical_memory_in_use_kb/1024 AS [SQL Server Memory Usage (MB)],
	   locked_page_allocations_kb/1024 AS [SQL Server Locked Pages Allocation (MB)],
       large_page_allocations_kb/1024 AS [SQL Server Large Pages Allocation (MB)], 
	   page_fault_count, memory_utilization_percentage, available_commit_limit_kb, 
	   process_physical_memory_low, process_virtual_memory_low
FROM sys.dm_os_process_memory WITH (NOLOCK) OPTION (RECOMPILE);
------

-- You want to see 0 for process_physical_memory_low
-- You want to see 0 for process_virtual_memory_low
-- This indicates that you are not under internal memory pressure
-- If locked_page_allocations_kb > 0, then LPIM is enabled

-- How to enable the "locked pages" feature in SQL Server 2012
-- https://bit.ly/2F5UjOA

-- Memory Management Architecture Guide
-- https://bit.ly/2JKkadC 



-- SQL Server Services information (Query 8) (SQL Server Services Info)
SELECT servicename, process_id, startup_type_desc, status_desc, 
last_startup_time, service_account, is_clustered, cluster_nodename, [filename], 
instant_file_initialization_enabled 
FROM sys.dm_server_services WITH (NOLOCK) OPTION (RECOMPILE);
------

-- Tells you the account being used for the SQL Server Service and the SQL Agent Service
-- Shows the process_id, when they were last started, and their current status
-- Also shows whether you are running on a failover cluster instance, and what node you are running on
-- Also shows whether IFI is enabled

-- sys.dm_server_services (Transact-SQL)
-- https://bit.ly/2oKa1Un


-- Last backup information by database  (Query 9) (Last Backup By Database)
SELECT ISNULL(d.[name], bs.[database_name]) AS [Database], d.recovery_model_desc AS [Recovery Model], 
       d.log_reuse_wait_desc AS [Log Reuse Wait Desc],
    MAX(CASE WHEN [type] = 'D' THEN bs.backup_finish_date ELSE NULL END) AS [Last Full Backup],
    MAX(CASE WHEN [type] = 'I' THEN bs.backup_finish_date ELSE NULL END) AS [Last Differential Backup],
    MAX(CASE WHEN [type] = 'L' THEN bs.backup_finish_date ELSE NULL END) AS [Last Log Backup]
FROM sys.databases AS d WITH (NOLOCK)
LEFT OUTER JOIN msdb.dbo.backupset AS bs WITH (NOLOCK)
ON bs.[database_name] = d.[name] 
AND bs.backup_finish_date > GETDATE()- 30
WHERE d.name <> N'tempdb'
GROUP BY ISNULL(d.[name], bs.[database_name]), d.recovery_model_desc, d.log_reuse_wait_desc, d.[name] 
ORDER BY d.recovery_model_desc, d.[name] OPTION (RECOMPILE);
------

-- This helps you spot runaway transaction logs and other issues with your backup schedule


-- Get SQL Server Agent jobs and Category information (Query 10) (SQL Server Agent Jobs)
SELECT sj.name AS [Job Name], sj.[description] AS [Job Description], SUSER_SNAME(sj.owner_sid) AS [Job Owner],
sj.date_created AS [Date Created], sj.[enabled] AS [Job Enabled], 
sj.notify_email_operator_id, sj.notify_level_email, sc.name AS [CategoryName],
s.[enabled] AS [Sched Enabled], js.next_run_date, js.next_run_time
FROM msdb.dbo.sysjobs AS sj WITH (NOLOCK)
INNER JOIN msdb.dbo.syscategories AS sc WITH (NOLOCK)
ON sj.category_id = sc.category_id
LEFT OUTER JOIN msdb.dbo.sysjobschedules AS js WITH (NOLOCK)
ON sj.job_id = js.job_id
LEFT OUTER JOIN msdb.dbo.sysschedules AS s WITH (NOLOCK)
ON js.schedule_id = s.schedule_id
ORDER BY sj.name OPTION (RECOMPILE);
------

-- Gives you some basic information about your SQL Server Agent jobs, who owns them and how they are configured
-- Look for Agent jobs that are not owned by sa
-- Look for jobs that have a notify_email_operator_id set to 0 (meaning no operator)
-- Look for jobs that have a notify_level_email set to 0 (meaning no e-mail is ever sent)
--
-- MSDN sysjobs documentation
-- https://bit.ly/2paDEOP 

-- SQL Server Maintenance Solution
-- https://bit.ly/1pgchQu  


-- Get SQL Server Agent Alert Information (Query 11) (SQL Server Agent Alerts)
SELECT name, event_source, message_id, severity, [enabled], has_notification, 
       delay_between_responses, occurrence_count, last_occurrence_date, last_occurrence_time
FROM msdb.dbo.sysalerts WITH (NOLOCK)
ORDER BY name OPTION (RECOMPILE);
------

-- Gives you some basic information about your SQL Server Agent Alerts 
-- (which are different from SQL Server Agent jobs)
-- Read more about Agent Alerts here: https://bit.ly/2Giz0Xf 



-- Windows information (Query 12) (Windows Info)
SELECT windows_release, windows_service_pack_level, 
       windows_sku, os_language_version
FROM sys.dm_os_windows_info WITH (NOLOCK) OPTION (RECOMPILE);
------

-- Gives you major OS version, Service Pack, Edition, and language info for the operating system
-- 10.0 is either Windows 10 or Windows Server 2016
-- 6.3 is either Windows 8.1 or Windows Server 2012 R2 
-- 6.2 is either Windows 8 or Windows Server 2012


-- Windows SKU codes
-- 4 is Enterprise Edition
-- 7 is Standard Server Edition
-- 8 is Datacenter Server Edition
-- 10 is Enterprise Server Edition
-- 48 is Professional Edition
-- 161 is Pro for Workstations

-- 1033 for os_language_version is US-English

-- SQL Server 2016 requires Windows Server 2012 or newer

-- Quick-Start Installation of SQL Server 2016
-- https://bit.ly/2qtxQ3G

-- Hardware and Software Requirements for Installing SQL Server 2016
-- https://bit.ly/2JJIUTl

-- Using SQL Server in Windows 8 and later versions of Windows operating system 
-- https://bit.ly/2F7Ax0P


-- SQL Server NUMA Node information  (Query 13) (SQL Server NUMA Info)
SELECT node_id, node_state_desc, memory_node_id, processor_group, cpu_count, online_scheduler_count, 
       idle_scheduler_count, active_worker_count, avg_load_balance, resource_monitor_state
FROM sys.dm_os_nodes WITH (NOLOCK) 
WHERE node_state_desc <> N'ONLINE DAC' OPTION (RECOMPILE);
------

-- Gives you some useful information about the composition and relative load on your NUMA nodes
-- You want to see an equal number of schedulers on each NUMA node
-- Watch out if SQL Server 2017 Standard Edition has been installed 
-- on a physical or virtual machine with more than four sockets or more than 24 physical cores

-- sys.dm_os_nodes (Transact-SQL)
-- https://bit.ly/2pn5Mw8

-- Balancing Your Available SQL Server Core Licenses Evenly Across NUMA Nodes
-- https://bit.ly/2vfC4Rq



-- Good basic information about OS memory amounts and state  (Query 14) (System Memory)
SELECT total_physical_memory_kb/1024 AS [Physical Memory (MB)], 
       available_physical_memory_kb/1024 AS [Available Memory (MB)], 
       total_page_file_kb/1024 AS [Total Page File (MB)], 
	   available_page_file_kb/1024 AS [Available Page File (MB)], 
	   system_cache_kb/1024 AS [System Cache (MB)],
       system_memory_state_desc AS [System Memory State]
FROM sys.dm_os_sys_memory WITH (NOLOCK) OPTION (RECOMPILE);
------

-- You want to see "Available physical memory is high" for System Memory State
-- This indicates that you are not under external memory pressure

-- Possible System Memory State values:
-- Available physical memory is high
-- Physical memory usage is steady
-- Available physical memory is low
-- Available physical memory is running low
-- Physical memory state is transitioning

-- sys.dm_os_sys_memory (Transact-SQL)
-- https://bit.ly/2pcV0xq



-- You can skip the next two queries if you know you don't have a clustered instance


-- Get information about your cluster nodes and their status  (Query 15) (Cluster Node Properties)
-- (if your database server is in a failover cluster)
SELECT NodeName, status_description, is_current_owner
FROM sys.dm_os_cluster_nodes WITH (NOLOCK) OPTION (RECOMPILE);
------

-- Knowing which node owns the cluster resources is critical
-- Especially when you are installing Windows or SQL Server updates
-- You will see no results if your instance is not clustered

-- Recommended hotfixes and updates for Windows Server 2012 R2-based failover clusters
-- https://bit.ly/1z5BfCw


-- Get information about any AlwaysOn AG cluster this instance is a part of (Query 16) (AlwaysOn AG Cluster)
SELECT cluster_name, quorum_type_desc, quorum_state_desc
FROM sys.dm_hadr_cluster WITH (NOLOCK) OPTION (RECOMPILE);
------

-- You will see no results if your instance is not using AlwaysOn AGs


-- Good overview of AG health and status (Query 17) (AlwaysOn AG Status)
SELECT ag.name AS [AG Name], ar.replica_server_name, ar.availability_mode_desc, adc.[database_name], 
       drs.is_local, drs.is_primary_replica, drs.synchronization_state_desc, drs.is_commit_participant, 
	   drs.synchronization_health_desc, drs.recovery_lsn, drs.truncation_lsn, drs.last_sent_lsn, 
	   drs.last_sent_time, drs.last_received_lsn, drs.last_received_time, drs.last_hardened_lsn, 
	   drs.last_hardened_time, drs.last_redone_lsn, drs.last_redone_time, drs.log_send_queue_size, 
	   drs.log_send_rate, drs.redo_queue_size, drs.redo_rate, drs.filestream_send_rate, 
	   drs.end_of_log_lsn, drs.last_commit_lsn, drs.last_commit_time, drs.database_state_desc 
FROM sys.dm_hadr_database_replica_states AS drs WITH (NOLOCK)
INNER JOIN sys.availability_databases_cluster AS adc WITH (NOLOCK)
ON drs.group_id = adc.group_id 
AND drs.group_database_id = adc.group_database_id
INNER JOIN sys.availability_groups AS ag WITH (NOLOCK)
ON ag.group_id = drs.group_id
INNER JOIN sys.availability_replicas AS ar WITH (NOLOCK)
ON drs.group_id = ar.group_id 
AND drs.replica_id = ar.replica_id
ORDER BY ag.name, ar.replica_server_name, adc.[database_name] OPTION (RECOMPILE);

-- You will see no results if your instance is not using AlwaysOn AGs

-- SQL Server 2016 � It Just Runs Faster: Always On Availability Groups Turbocharged
-- https://bit.ly/2dn1H6r


-- Hardware information from SQL Server 2016 SP2  (Query 18) (Hardware Info)
SELECT cpu_count AS [Logical CPU Count], scheduler_count, 
       (socket_count * cores_per_socket) AS [Physical Core Count], 
       socket_count AS [Socket Count], cores_per_socket, numa_node_count,
       physical_memory_kb/1024 AS [Physical Memory (MB)], 
       max_workers_count AS [Max Workers Count], 
	   affinity_type_desc AS [Affinity Type], 
       sqlserver_start_time AS [SQL Server Start Time], 
	   virtual_machine_type_desc AS [Virtual Machine Type], 
       softnuma_configuration_desc AS [Soft NUMA Configuration], 
	   sql_memory_model_desc
FROM sys.dm_os_sys_info WITH (NOLOCK) OPTION (RECOMPILE);
------

-- Gives you some good basic hardware information about your database server
-- Note: virtual_machine_type_desc of HYPERVISOR does not automatically mean you are running SQL Server inside of a VM
-- It merely indicates that you have a hypervisor running on your host

-- sys.dm_os_sys_info (Transact-SQL)
-- https://bit.ly/2pczOYs

-- Soft NUMA configuration was a new column for SQL Server 2016
-- OFF = Soft-NUMA feature is OFF
-- ON = SQL Server automatically determines the NUMA node sizes for Soft-NUMA
-- MANUAL = Manually configured soft-NUMA

-- Configure SQL Server to Use Soft-NUMA (SQL Server)
-- https://bit.ly/2HTpKJt

-- sql_memory_model_desc values (Added in SQL Server 2016 SP1)
-- CONVENTIONAL
-- LOCK_PAGES
-- LARGE_PAGES
   

-- Get System Manufacturer and model number from SQL Server Error log (Query 19) (System Manufacturer)
EXEC sys.xp_readerrorlog 0, 1, N'Manufacturer';
------ 

-- This can help you determine the capabilities and capacities of your database server
-- Can also be used to confirm if you are running in a VM
-- This query might take a few seconds if you have not recycled your error log recently
-- This query will return no results if your error log has been recycled since the instance was started


-- Get BIOS date from Windows Registry (Query 20) (BIOS Date)
EXEC sys.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'HARDWARE\DESCRIPTION\System\BIOS', N'BiosReleaseDate';
------

-- Helps you understand whether the main system BIOS is up to date, and the possible age of the hardware
-- Not as useful for virtualization


-- Get processor description from Windows Registry  (Query 21) (Processor Description)
EXEC sys.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'HARDWARE\DESCRIPTION\System\CentralProcessor\0', N'ProcessorNameString';
------

-- Gives you the model number and rated clock speed of your processor(s)
-- Your processors may be running at less than the rated clock speed due
-- to the Windows Power Plan or hardware power management

-- You can use CPU-Z to get your actual CPU core speed and a lot of other useful information
-- https://bit.ly/QhR6xF

-- You can learn more about processor selection for SQL Server by following this link
-- https://bit.ly/2F3aVlP



-- See if buffer pool extension (BPE) is enabled (Query 22) (BPE Configuration)
SELECT [path], state_description, current_size_in_kb, 
CAST(current_size_in_kb/1048576.0 AS DECIMAL(10,2)) AS [Size (GB)]
FROM sys.dm_os_buffer_pool_extension_configuration WITH (NOLOCK) OPTION (RECOMPILE);
------

-- BPE is available in both Standard Edition and Enterprise Edition
-- It is a more interesting feature for Standard Edition

-- Buffer Pool Extension to SSDs in SQL Server 2014
-- https://bit.ly/1bm08m8

-- Buffer Pool Extension
-- https://bit.ly/2oBuieO



-- Look at buffer descriptors to see BPE usage by database (Query 23) (BPE Usage) 
SELECT DB_NAME(database_id) AS [Database Name], COUNT(page_id) AS [Page Count],
CAST(COUNT(*)/128.0 AS DECIMAL(10, 2)) AS [Buffer size(MB)], 
AVG(read_microsec) AS [Avg Read Time (microseconds)]
FROM sys.dm_os_buffer_descriptors WITH (NOLOCK)
WHERE database_id <> 32767
AND is_in_bpool_extension = 1
GROUP BY DB_NAME(database_id) 
ORDER BY [Buffer size(MB)] DESC OPTION (RECOMPILE);
------

-- You will see no results if BPE is not enabled or if there is no BPE usage


-- Get information on location, time and size of any memory dumps from SQL Server  (Query 24) (Memory Dump Info)
SELECT [filename], creation_time, size_in_bytes/1048576.0 AS [Size (MB)]
FROM sys.dm_server_memory_dumps WITH (NOLOCK) 
ORDER BY creation_time DESC OPTION (RECOMPILE);
------

-- This will not return any rows if you have 
-- not had any memory dumps (which is a good thing)

-- sys.dm_server_memory_dumps (Transact-SQL)
-- https://bit.ly/2elwWll



-- Look at Suspect Pages table (Query 25) (Suspect Pages)
SELECT DB_NAME(database_id) AS [Database Name], [file_id], page_id, 
       event_type, error_count, last_update_date 
FROM msdb.dbo.suspect_pages WITH (NOLOCK)
ORDER BY database_id OPTION (RECOMPILE);
------

-- event_type value descriptions
-- 1 = 823 error caused by an operating system CRC error
--     or 824 error other than a bad checksum or a torn page (for example, a bad page ID)
-- 2 = Bad checksum
-- 3 = Torn page
-- 4 = Restored (The page was restored after it was marked bad)
-- 5 = Repaired (DBCC repaired the page)
-- 7 = Deallocated by DBCC

-- Ideally, this query returns no results. The table is limited to 1000 rows.
-- If you do get results here, you should do further investigation to determine the root cause

-- Manage the suspect_pages Table
-- https://bit.ly/2Fvr1c9


-- Get number of data files in tempdb database (Query 26) (TempDB Data Files)
EXEC sys.xp_readerrorlog 0, 1, N'The tempdb database has';
------

-- Get the number of data files in the tempdb database
-- 4-8 data files that are all the same size is a good starting point
-- This query will return no results if your error log has been recycled since the instance was last started


-- File names and paths for all user and system databases on instance  (Query 27) (Database Filenames and Paths)
SELECT DB_NAME([database_id]) AS [Database Name], 
       [file_id], [name], physical_name, [type_desc], state_desc,
	   is_percent_growth, growth,
	   CONVERT(bigint, growth/128.0) AS [Growth in MB], 
       CONVERT(bigint, size/128.0) AS [Total Size in MB]
FROM sys.master_files WITH (NOLOCK)
ORDER BY DB_NAME([database_id]), [file_id] OPTION (RECOMPILE);
------

-- Things to look at:
-- Are data files and log files on different drives?
-- Is everything on the C: drive?
-- Is tempdb on dedicated drives?
-- Is there only one tempdb data file?
-- Are all of the tempdb data files the same size?
-- Are there multiple data files for user databases?
-- Is percent growth enabled for any files (which is bad)?



-- Volume info for all LUNS that have database files on the current instance (Query 28) (Volume Info)
SELECT DISTINCT vs.volume_mount_point, vs.file_system_type, vs.logical_volume_name, 
CONVERT(DECIMAL(18,2), vs.total_bytes/1073741824.0) AS [Total Size (GB)],
CONVERT(DECIMAL(18,2), vs.available_bytes/1073741824.0) AS [Available Size (GB)],  
CONVERT(DECIMAL(18,2), vs.available_bytes * 1. / vs.total_bytes * 100.) AS [Space Free %],
vs.supports_compression, vs.is_compressed, 
vs.supports_sparse_files, vs.supports_alternate_streams
FROM sys.master_files AS f WITH (NOLOCK)
CROSS APPLY sys.dm_os_volume_stats(f.database_id, f.[file_id]) AS vs 
ORDER BY vs.volume_mount_point OPTION (RECOMPILE);
------

-- Shows you the total and free space on the LUNs where you have database files
-- Being low on free space can negatively affect performance

-- sys.dm_os_volume_stats (Transact-SQL)
-- https://bit.ly/2oBPNNr



-- Drive level latency information (Query 29) (Drive Level Latency)
-- Based on code from Jimmy May
SELECT tab.[Drive], tab.volume_mount_point AS [Volume Mount Point], 
	CASE 
		WHEN num_of_reads = 0 THEN 0 
		ELSE (io_stall_read_ms/num_of_reads) 
	END AS [Read Latency],
	CASE 
		WHEN num_of_writes = 0 THEN 0 
		ELSE (io_stall_write_ms/num_of_writes) 
	END AS [Write Latency],
	CASE 
		WHEN (num_of_reads = 0 AND num_of_writes = 0) THEN 0 
		ELSE (io_stall/(num_of_reads + num_of_writes)) 
	END AS [Overall Latency],
	CASE 
		WHEN num_of_reads = 0 THEN 0 
		ELSE (num_of_bytes_read/num_of_reads) 
	END AS [Avg Bytes/Read],
	CASE 
		WHEN num_of_writes = 0 THEN 0 
		ELSE (num_of_bytes_written/num_of_writes) 
	END AS [Avg Bytes/Write],
	CASE 
		WHEN (num_of_reads = 0 AND num_of_writes = 0) THEN 0 
		ELSE ((num_of_bytes_read + num_of_bytes_written)/(num_of_reads + num_of_writes)) 
	END AS [Avg Bytes/Transfer]
FROM (SELECT LEFT(UPPER(mf.physical_name), 2) AS Drive, SUM(num_of_reads) AS num_of_reads,
	         SUM(io_stall_read_ms) AS io_stall_read_ms, SUM(num_of_writes) AS num_of_writes,
	         SUM(io_stall_write_ms) AS io_stall_write_ms, SUM(num_of_bytes_read) AS num_of_bytes_read,
	         SUM(num_of_bytes_written) AS num_of_bytes_written, SUM(io_stall) AS io_stall, vs.volume_mount_point 
      FROM sys.dm_io_virtual_file_stats(NULL, NULL) AS vfs
      INNER JOIN sys.master_files AS mf WITH (NOLOCK)
      ON vfs.database_id = mf.database_id AND vfs.file_id = mf.file_id
	  CROSS APPLY sys.dm_os_volume_stats(mf.database_id, mf.[file_id]) AS vs 
      GROUP BY LEFT(UPPER(mf.physical_name), 2), vs.volume_mount_point) AS tab
ORDER BY [Overall Latency] OPTION (RECOMPILE);
------

-- Shows you the drive-level latency for reads and writes, in milliseconds
-- Latency above 30-40ms is usually a problem
-- These latency numbers include all file activity against all SQL Server 
-- database files on each drive since SQL Server was last started


-- Calculates average stalls per read, per write, and per total input/output for each database file  (Query 30) (IO Stalls by File)
SELECT DB_NAME(fs.database_id) AS [Database Name], CAST(fs.io_stall_read_ms/(1.0 + fs.num_of_reads) AS NUMERIC(10,1)) AS [avg_read_stall_ms],
CAST(fs.io_stall_write_ms/(1.0 + fs.num_of_writes) AS NUMERIC(10,1)) AS [avg_write_stall_ms],
CAST((fs.io_stall_read_ms + fs.io_stall_write_ms)/(1.0 + fs.num_of_reads + fs.num_of_writes) AS NUMERIC(10,1)) AS [avg_io_stall_ms],
CONVERT(DECIMAL(18,2), mf.size/128.0) AS [File Size (MB)], mf.physical_name, mf.type_desc, fs.io_stall_read_ms, fs.num_of_reads, 
fs.io_stall_write_ms, fs.num_of_writes, fs.io_stall_read_ms + fs.io_stall_write_ms AS [io_stalls], fs.num_of_reads + fs.num_of_writes AS [total_io],
io_stall_queued_read_ms AS [Resource Governor Total Read IO Latency (ms)], io_stall_queued_write_ms AS [Resource Governor Total Write IO Latency (ms)] 
FROM sys.dm_io_virtual_file_stats(null,null) AS fs
INNER JOIN sys.master_files AS mf WITH (NOLOCK)
ON fs.database_id = mf.database_id
AND fs.[file_id] = mf.[file_id]
ORDER BY avg_io_stall_ms DESC OPTION (RECOMPILE);
------

-- Helps determine which database files on the entire instance have the most I/O bottlenecks
-- This can help you decide whether certain LUNs are overloaded and whether you might
-- want to move some files to a different location or perhaps improve your I/O performance
-- These latency numbers include all file activity against each SQL Server 
-- database file since SQL Server was last started


-- Look for I/O requests taking longer than 15 seconds in the six most recent SQL Server Error Logs (Query 31) (IO Warnings)
CREATE TABLE #IOWarningResults(LogDate datetime, ProcessInfo sysname, LogText nvarchar(1000));

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 0, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 1, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 2, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 3, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 4, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 5, 1, N'taking longer than 15 seconds';

SELECT LogDate, ProcessInfo, LogText
FROM #IOWarningResults
ORDER BY LogDate DESC;

DROP TABLE #IOWarningResults;
------  

-- Finding 15 second I/O warnings in the SQL Server Error Log is useful evidence of
-- poor I/O performance (which might have many different causes)
-- Look to see if you see any patterns in the results (same files, same drives, same time of day, etc.)

-- Diagnostics in SQL Server help detect stalled and stuck I/O operations
-- https://bit.ly/2qtaw73



-- Recovery model, log reuse wait description, log file size, log usage size  (Query 32) (Database Properties)
-- and compatibility level for all databases on instance
SELECT db.[name] AS [Database Name], SUSER_SNAME(db.owner_sid) AS [Database Owner], db.recovery_model_desc AS [Recovery Model], 
db.state_desc, db.containment_desc, db.log_reuse_wait_desc AS [Log Reuse Wait Description], 
CONVERT(DECIMAL(18,2), ls.cntr_value/1024.0) AS [Log Size (MB)], CONVERT(DECIMAL(18,2), lu.cntr_value/1024.0) AS [Log Used (MB)],
CAST(CAST(lu.cntr_value AS FLOAT) / CAST(ls.cntr_value AS FLOAT)AS DECIMAL(18,2)) * 100 AS [Log Used %], 
db.[compatibility_level] AS [DB Compatibility Level], 
db.is_mixed_page_allocation_on, db.page_verify_option_desc AS [Page Verify Option], 
db.is_auto_create_stats_on, db.is_auto_update_stats_on, db.is_auto_update_stats_async_on, db.is_parameterization_forced, 
db.snapshot_isolation_state_desc, db.is_read_committed_snapshot_on, db.is_auto_close_on, db.is_auto_shrink_on, 
db.target_recovery_time_in_seconds, db.is_cdc_enabled, db.is_published, db.is_distributor,
db.group_database_id, db.replica_id,db.is_memory_optimized_elevate_to_snapshot_on, 
db.delayed_durability_desc, db.is_auto_create_stats_incremental_on,
db.is_query_store_on, db.is_sync_with_backup, 
db.is_supplemental_logging_enabled, db.is_remote_data_archive_enabled,
db.is_encrypted, de.encryption_state, de.percent_complete, de.key_algorithm, de.key_length      
FROM sys.databases AS db WITH (NOLOCK)
INNER JOIN sys.dm_os_performance_counters AS lu WITH (NOLOCK)
ON db.name = lu.instance_name
INNER JOIN sys.dm_os_performance_counters AS ls WITH (NOLOCK)
ON db.name = ls.instance_name
LEFT OUTER JOIN sys.dm_database_encryption_keys AS de WITH (NOLOCK)
ON db.database_id = de.database_id
WHERE lu.counter_name LIKE N'Log File(s) Used Size (KB)%' 
AND ls.counter_name LIKE N'Log File(s) Size (KB)%'
AND ls.cntr_value > 0 
ORDER BY db.[name] OPTION (RECOMPILE);
------

-- Things to look at:
-- How many databases are on the instance?
-- What recovery models are they using?
-- What is the log reuse wait description?
-- How full are the transaction logs?
-- What compatibility level are the databases on? 
-- What is the Page Verify Option? (should be CHECKSUM)
-- Is Auto Update Statistics Asynchronously enabled?
-- Is Delayed Durability enabled
-- Make sure auto_shrink and auto_close are not enabled!

-- is_mixed_page_allocation_on is a new property for SQL Server 2016. Equivalent to TF 1118 for a user database
-- SQL Server 2016: Changes in default behavior for autogrow and allocations for tempdb and user databases
-- https://bit.ly/2evRZSR

-- A non-zero value for target_recovery_time_in_seconds means that indirect checkpoint is enabled 
-- If the setting has a zero value it indicates that automatic checkpoint is enabled

-- Changes in SQL Server 2016 Checkpoint Behavior
-- https://bit.ly/2pdggk3


-- Missing Indexes for all databases by Index Advantage  (Query 33) (Missing Indexes All Databases)
SELECT CONVERT(decimal(18,2), user_seeks * avg_total_user_cost * (avg_user_impact * 0.01)) AS [index_advantage],
FORMAT(migs.last_user_seek, 'yyyy-MM-dd HH:mm:ss') AS [last_user_seek], 
mid.[statement] AS [Database.Schema.Table],
COUNT(1) OVER(PARTITION BY mid.[statement]) AS [missing_indexes_for_table],
COUNT(1) OVER(PARTITION BY mid.[statement], equality_columns) AS [similar_missing_indexes_for_table],
mid.equality_columns, mid.inequality_columns, mid.included_columns,
migs.unique_compiles, migs.user_seeks, 
CONVERT(decimal(18,2), migs.avg_total_user_cost) AS [avg_total_user_cost], migs.avg_user_impact 
FROM sys.dm_db_missing_index_group_stats AS migs WITH (NOLOCK)
INNER JOIN sys.dm_db_missing_index_groups AS mig WITH (NOLOCK)
ON migs.group_handle = mig.index_group_handle
INNER JOIN sys.dm_db_missing_index_details AS mid WITH (NOLOCK)
ON mig.index_handle = mid.index_handle
ORDER BY index_advantage DESC OPTION (RECOMPILE);
------

-- Getting missing index information for all of the databases on the instance is very useful
-- Look at last user seek time, number of user seeks to help determine source and importance
-- Also look at avg_user_impact and avg_total_user_cost to help determine importance
-- SQL Server is overly eager to add included columns, so beware
-- Do not just blindly add indexes that show up from this query!!!

-- SQL Server Index Design Guide
-- https://bit.ly/2qtZr4N



-- Get VLF Counts for all databases on the instance (Query 34) (VLF Counts)
SELECT�[name] AS [Database Name],�[VLF Count] 
FROM�sys.databases�AS db WITH (NOLOCK)
CROSS APPLY�(SELECT�file_id, COUNT(*)�AS [VLF Count]�
			 FROM sys.dm_db_log_info(db.database_id) 
����������   GROUP BY�file_id)�AS li
ORDER BY [VLF Count] DESC  OPTION (RECOMPILE);
------

-- High VLF counts can affect write performance to the log file
-- and they can make full database restores and crash recovery take much longer
-- Try to keep your VLF counts under 200 in most cases (depending on log file size)

-- Important change to VLF creation algorithm in SQL Server 2014
-- https://bit.ly/2Hsjbg4

-- SQL Server Transaction Log Architecture and Management Guide
-- https://bit.ly/2JjmQRZ



-- Get CPU utilization by database (Query 35) (CPU Usage by Database)
WITH DB_CPU_Stats
AS
(SELECT pa.DatabaseID, DB_Name(pa.DatabaseID) AS [Database Name], SUM(qs.total_worker_time/1000) AS [CPU_Time_Ms]
 FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
 CROSS APPLY (SELECT CONVERT(int, value) AS [DatabaseID] 
              FROM sys.dm_exec_plan_attributes(qs.plan_handle)
              WHERE attribute = N'dbid') AS pa
 GROUP BY DatabaseID)
SELECT ROW_NUMBER() OVER(ORDER BY [CPU_Time_Ms] DESC) AS [CPU Rank],
       [Database Name], [CPU_Time_Ms] AS [CPU Time (ms)], 
       CAST([CPU_Time_Ms] * 1.0 / SUM([CPU_Time_Ms]) OVER() * 100.0 AS DECIMAL(5, 2)) AS [CPU Percent]
FROM DB_CPU_Stats
WHERE DatabaseID <> 32767 -- ResourceDB
ORDER BY [CPU Rank] OPTION (RECOMPILE);
------

-- Helps determine which database is using the most CPU resources on the instance


-- Get I/O utilization by database (Query 36) (IO Usage By Database)
WITH Aggregate_IO_Statistics
AS
(SELECT DB_NAME(database_id) AS [Database Name],
CAST(SUM(num_of_bytes_read + num_of_bytes_written)/1048576 AS DECIMAL(12, 2)) AS io_in_mb
FROM sys.dm_io_virtual_file_stats(NULL, NULL) AS [DM_IO_STATS]
GROUP BY database_id)
SELECT ROW_NUMBER() OVER(ORDER BY io_in_mb DESC) AS [I/O Rank], [Database Name], io_in_mb AS [Total I/O (MB)],
       CAST(io_in_mb/ SUM(io_in_mb) OVER() * 100.0 AS DECIMAL(5,2)) AS [I/O Percent]
FROM Aggregate_IO_Statistics
ORDER BY [I/O Rank] OPTION (RECOMPILE);
------

-- Helps determine which database is using the most I/O resources on the instance


-- Get total buffer usage by database for current instance  (Query 37) (Total Buffer Usage by Database)
-- This make take some time to run on a busy instance
WITH AggregateBufferPoolUsage
AS
(SELECT DB_NAME(database_id) AS [Database Name],
CAST(COUNT(*) * 8/1024.0 AS DECIMAL (10,2))  AS [CachedSize]
FROM sys.dm_os_buffer_descriptors WITH (NOLOCK)
WHERE database_id <> 32767 -- ResourceDB
GROUP BY DB_NAME(database_id))
SELECT ROW_NUMBER() OVER(ORDER BY CachedSize DESC) AS [Buffer Pool Rank], [Database Name], CachedSize AS [Cached Size (MB)],
       CAST(CachedSize / SUM(CachedSize) OVER() * 100.0 AS DECIMAL(5,2)) AS [Buffer Pool Percent]
FROM AggregateBufferPoolUsage
ORDER BY [Buffer Pool Rank] OPTION (RECOMPILE);
------

-- Tells you how much memory (in the buffer pool) 
-- is being used by each database on the instance


-- Get tempdb version store space usage by database (Query 38) (Version Store Space Usage)
SELECT DB_NAME(database_id) AS [Database Name],
       reserved_page_count AS [Version Store Reserved Page Count], 
	   reserved_space_kb/1024 AS [Version Store Reserved Space (MB)] 
FROM sys.dm_tran_version_store_space_usage WITH (NOLOCK) 
ORDER BY reserved_space_kb/1024 DESC OPTION (RECOMPILE);
------  

-- sys.dm_tran_version_store_space_usage (Transact-SQL)
-- https://bit.ly/2vh3Bmk




-- Clear Wait Stats with this command
-- DBCC SQLPERF('sys.dm_os_wait_stats', CLEAR);

-- Isolate top waits for server instance since last restart or wait statistics clear  (Query 39) (Top Waits)
WITH [Waits] 
AS (SELECT wait_type, wait_time_ms/ 1000.0 AS [WaitS],
          (wait_time_ms - signal_wait_time_ms) / 1000.0 AS [ResourceS],
           signal_wait_time_ms / 1000.0 AS [SignalS],
           waiting_tasks_count AS [WaitCount],
           100.0 *  wait_time_ms / SUM (wait_time_ms) OVER() AS [Percentage],
           ROW_NUMBER() OVER(ORDER BY wait_time_ms DESC) AS [RowNum]
    FROM sys.dm_os_wait_stats WITH (NOLOCK)
    WHERE [wait_type] NOT IN (
        N'BROKER_EVENTHANDLER', N'BROKER_RECEIVE_WAITFOR', N'BROKER_TASK_STOP',
		N'BROKER_TO_FLUSH', N'BROKER_TRANSMITTER', N'CHECKPOINT_QUEUE',
        N'CHKPT', N'CLR_AUTO_EVENT', N'CLR_MANUAL_EVENT', N'CLR_SEMAPHORE', N'CXCONSUMER',
        N'DBMIRROR_DBM_EVENT', N'DBMIRROR_EVENTS_QUEUE', N'DBMIRROR_WORKER_QUEUE',
		N'DBMIRRORING_CMD', N'DIRTY_PAGE_POLL', N'DISPATCHER_QUEUE_SEMAPHORE',
        N'EXECSYNC', N'FSAGENT', N'FT_IFTS_SCHEDULER_IDLE_WAIT', N'FT_IFTSHC_MUTEX',
        N'HADR_CLUSAPI_CALL', N'HADR_FILESTREAM_IOMGR_IOCOMPLETION', N'HADR_LOGCAPTURE_WAIT', 
		N'HADR_NOTIFICATION_DEQUEUE', N'HADR_TIMER_TASK', N'HADR_WORK_QUEUE',
        N'KSOURCE_WAKEUP', N'LAZYWRITER_SLEEP', N'LOGMGR_QUEUE', 
		N'MEMORY_ALLOCATION_EXT', N'ONDEMAND_TASK_QUEUE',
		N'PARALLEL_REDO_DRAIN_WORKER', N'PARALLEL_REDO_LOG_CACHE', N'PARALLEL_REDO_TRAN_LIST',
		N'PARALLEL_REDO_WORKER_SYNC', N'PARALLEL_REDO_WORKER_WAIT_WORK',
		N'PREEMPTIVE_HADR_LEASE_MECHANISM', N'PREEMPTIVE_SP_SERVER_DIAGNOSTICS',
		N'PREEMPTIVE_OS_LIBRARYOPS', N'PREEMPTIVE_OS_COMOPS', N'PREEMPTIVE_OS_CRYPTOPS',
		N'PREEMPTIVE_OS_PIPEOPS', N'PREEMPTIVE_OS_AUTHENTICATIONOPS',
		N'PREEMPTIVE_OS_GENERICOPS', N'PREEMPTIVE_OS_VERIFYTRUST',
		N'PREEMPTIVE_OS_FILEOPS', N'PREEMPTIVE_OS_DEVICEOPS', N'PREEMPTIVE_OS_QUERYREGISTRY',
		N'PREEMPTIVE_OS_WRITEFILE',
		N'PREEMPTIVE_XE_CALLBACKEXECUTE', N'PREEMPTIVE_XE_DISPATCHER',
		N'PREEMPTIVE_XE_GETTARGETSTATE', N'PREEMPTIVE_XE_SESSIONCOMMIT',
		N'PREEMPTIVE_XE_TARGETINIT', N'PREEMPTIVE_XE_TARGETFINALIZE',
        N'PWAIT_ALL_COMPONENTS_INITIALIZED', N'PWAIT_DIRECTLOGCONSUMER_GETNEXT',
		N'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP',
		N'QDS_ASYNC_QUEUE',
        N'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP', N'REQUEST_FOR_DEADLOCK_SEARCH',
		N'RESOURCE_QUEUE', N'SERVER_IDLE_CHECK', N'SLEEP_BPOOL_FLUSH', N'SLEEP_DBSTARTUP',
		N'SLEEP_DCOMSTARTUP', N'SLEEP_MASTERDBREADY', N'SLEEP_MASTERMDREADY',
        N'SLEEP_MASTERUPGRADED', N'SLEEP_MSDBSTARTUP', N'SLEEP_SYSTEMTASK', N'SLEEP_TASK',
        N'SLEEP_TEMPDBSTARTUP', N'SNI_HTTP_ACCEPT', N'SP_SERVER_DIAGNOSTICS_SLEEP',
		N'SQLTRACE_BUFFER_FLUSH', N'SQLTRACE_INCREMENTAL_FLUSH_SLEEP', N'SQLTRACE_WAIT_ENTRIES',
		N'WAIT_FOR_RESULTS', N'WAITFOR', N'WAITFOR_TASKSHUTDOWN', N'WAIT_XTP_HOST_WAIT',
		N'WAIT_XTP_OFFLINE_CKPT_NEW_LOG', N'WAIT_XTP_CKPT_CLOSE', N'WAIT_XTP_RECOVERY',
		N'XE_BUFFERMGR_ALLPROCESSED_EVENT', N'XE_DISPATCHER_JOIN',
        N'XE_DISPATCHER_WAIT', N'XE_LIVE_TARGET_TVF', N'XE_TIMER_EVENT')
    AND waiting_tasks_count > 0)
SELECT
    MAX (W1.wait_type) AS [WaitType],
	CAST (MAX (W1.Percentage) AS DECIMAL (5,2)) AS [Wait Percentage],
	CAST ((MAX (W1.WaitS) / MAX (W1.WaitCount)) AS DECIMAL (16,4)) AS [AvgWait_Sec],
    CAST ((MAX (W1.ResourceS) / MAX (W1.WaitCount)) AS DECIMAL (16,4)) AS [AvgRes_Sec],
    CAST ((MAX (W1.SignalS) / MAX (W1.WaitCount)) AS DECIMAL (16,4)) AS [AvgSig_Sec], 
    CAST (MAX (W1.WaitS) AS DECIMAL (16,2)) AS [Wait_Sec],
    CAST (MAX (W1.ResourceS) AS DECIMAL (16,2)) AS [Resource_Sec],
    CAST (MAX (W1.SignalS) AS DECIMAL (16,2)) AS [Signal_Sec],
    MAX (W1.WaitCount) AS [Wait Count],
	CAST (N'https://www.sqlskills.com/help/waits/' + W1.wait_type AS XML) AS [Help/Info URL]
FROM Waits AS W1
INNER JOIN Waits AS W2
ON W2.RowNum <= W1.RowNum
GROUP BY W1.RowNum, W1.wait_type
HAVING SUM (W2.Percentage) - MAX (W1.Percentage) < 99 -- percentage threshold
OPTION (RECOMPILE);
------

-- Cumulative wait stats are not as useful on an idle instance that is not under load or performance pressure

-- SQL Server Wait Types Library (Paul Randal)
-- https://bit.ly/2ePzYO2

-- The SQL Server Wait Type Repository
-- https://bit.ly/1afzfjC

-- Wait statistics, or please tell me where it hurts
-- https://bit.ly/2wsQHQE

-- SQL Server 2005 Performance Tuning using the Waits and Queues
-- https://bit.ly/1o2NFoF

-- sys.dm_os_wait_stats (Transact-SQL)
-- https://bit.ly/2Hjq9Yl



-- Get a count of SQL connections by IP address (Query 40) (Connection Counts by IP Address)
SELECT ec.client_net_address, es.[program_name], es.[host_name], es.login_name, 
COUNT(ec.session_id) AS [connection count] 
FROM sys.dm_exec_sessions AS es WITH (NOLOCK) 
INNER JOIN sys.dm_exec_connections AS ec WITH (NOLOCK) 
ON es.session_id = ec.session_id 
GROUP BY ec.client_net_address, es.[program_name], es.[host_name], es.login_name  
ORDER BY ec.client_net_address, es.[program_name] OPTION (RECOMPILE);
------

-- This helps you figure where your database load is coming from
-- and verifies connectivity from other machines

-- Solving Connectivity errors to SQL Server
-- https://bit.ly/2EgzoD0



-- Get Average Task Counts (run multiple times)  (Query 41) (Avg Task Counts)
SELECT AVG(current_tasks_count) AS [Avg Task Count], 
AVG(work_queue_count) AS [Avg Work Queue Count],
AVG(runnable_tasks_count) AS [Avg Runnable Task Count],
AVG(pending_disk_io_count) AS [Avg Pending DiskIO Count]
FROM sys.dm_os_schedulers WITH (NOLOCK)
WHERE scheduler_id < 255 OPTION (RECOMPILE);
------

-- Sustained values above 10 suggest further investigation in that area
-- High Avg Task Counts are often caused by blocking/deadlocking or other resource contention

-- Sustained values above 1 suggest further investigation in that area
-- High Avg Runnable Task Counts are a good sign of CPU pressure
-- High Avg Pending DiskIO Counts are a sign of disk pressure

-- How to Do Some Very Basic SQL Server Monitoring
-- https://bit.ly/2q3Btgt



-- Detect blocking (run multiple times)  (Query 42) (Detect Blocking)
SELECT t1.resource_type AS [lock type], DB_NAME(resource_database_id) AS [database],
t1.resource_associated_entity_id AS [blk object],t1.request_mode AS [lock req],  -- lock requested
t1.request_session_id AS [waiter sid], t2.wait_duration_ms AS [wait time],       -- spid of waiter  
(SELECT [text] FROM sys.dm_exec_requests AS r WITH (NOLOCK)                      -- get sql for waiter
CROSS APPLY sys.dm_exec_sql_text(r.[sql_handle]) 
WHERE r.session_id = t1.request_session_id) AS [waiter_batch],
(SELECT SUBSTRING(qt.[text],r.statement_start_offset/2, 
    (CASE WHEN r.statement_end_offset = -1 
    THEN LEN(CONVERT(nvarchar(max), qt.[text])) * 2 
    ELSE r.statement_end_offset END - r.statement_start_offset)/2) 
FROM sys.dm_exec_requests AS r WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(r.[sql_handle]) AS qt
WHERE r.session_id = t1.request_session_id) AS [waiter_stmt],					-- statement blocked
t2.blocking_session_id AS [blocker sid],										-- spid of blocker
(SELECT [text] FROM sys.sysprocesses AS p										-- get sql for blocker
CROSS APPLY sys.dm_exec_sql_text(p.[sql_handle]) 
WHERE p.spid = t2.blocking_session_id) AS [blocker_batch]
FROM sys.dm_tran_locks AS t1 WITH (NOLOCK)
INNER JOIN sys.dm_os_waiting_tasks AS t2 WITH (NOLOCK)
ON t1.lock_owner_address = t2.resource_address OPTION (RECOMPILE);
------

-- Helps troubleshoot blocking and deadlocking issues
-- The results will change from second to second on a busy system
-- You should run this query multiple times when you see signs of blocking



-- Get CPU Utilization History for last 256 minutes (in one minute intervals)  (Query 43) (CPU Utilization History)
DECLARE @ts_now bigint = (SELECT cpu_ticks/(cpu_ticks/ms_ticks) FROM sys.dm_os_sys_info WITH (NOLOCK)); 

SELECT TOP(256) SQLProcessUtilization AS [SQL Server Process CPU Utilization], 
               SystemIdle AS [System Idle Process], 
               100 - SystemIdle - SQLProcessUtilization AS [Other Process CPU Utilization], 
               DATEADD(ms, -1 * (@ts_now - [timestamp]), GETDATE()) AS [Event Time] 
FROM (SELECT record.value('(./Record/@id)[1]', 'int') AS record_id, 
			record.value('(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]', 'int') 
			AS [SystemIdle], 
			record.value('(./Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization)[1]', 'int') 
			AS [SQLProcessUtilization], [timestamp] 
	  FROM (SELECT [timestamp], CONVERT(xml, record) AS [record] 
			FROM sys.dm_os_ring_buffers WITH (NOLOCK)
			WHERE ring_buffer_type = N'RING_BUFFER_SCHEDULER_MONITOR' 
			AND record LIKE N'%<SystemHealth>%') AS x) AS y 
ORDER BY record_id DESC OPTION (RECOMPILE);
------

-- Look at the trend over the entire period 
-- Also look at high sustained 'Other Process' CPU Utilization values
-- Note: This query sometimes gives inaccurate results (negative values)
-- on high core count (> 64 cores) systems


-- Get top total worker time queries for entire instance (Query 44) (Top Worker Time Queries)
SELECT TOP(50) DB_NAME(t.[dbid]) AS [Database Name], 
REPLACE(REPLACE(LEFT(t.[text], 255), CHAR(10),''), CHAR(13),'') AS [Short Query Text],  
qs.total_worker_time AS [Total Worker Time], qs.min_worker_time AS [Min Worker Time],
qs.total_worker_time/qs.execution_count AS [Avg Worker Time], 
qs.max_worker_time AS [Max Worker Time], 
qs.min_elapsed_time AS [Min Elapsed Time], 
qs.total_elapsed_time/qs.execution_count AS [Avg Elapsed Time], 
qs.max_elapsed_time AS [Max Elapsed Time],
qs.min_logical_reads AS [Min Logical Reads],
qs.total_logical_reads/qs.execution_count AS [Avg Logical Reads],
qs.max_logical_reads AS [Max Logical Reads], 
qs.execution_count AS [Execution Count],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index], 
qs.creation_time AS [Creation Time]
--,t.[text] AS [Query Text], qp.query_plan AS [Query Plan] -- uncomment out these columns if not copying results to Excel
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t 
CROSS APPLY sys.dm_exec_query_plan(plan_handle) AS qp 
ORDER BY qs.total_worker_time DESC OPTION (RECOMPILE);
------


-- Helps you find the most expensive queries from a CPU perspective across the entire instance
-- Can also help track down parameter sniffing issues



-- Page Life Expectancy (PLE) value for each NUMA node in current instance  (Query 45) (PLE by NUMA Node)
SELECT @@SERVERNAME AS [Server Name], RTRIM([object_name]) AS [Object Name], instance_name, cntr_value AS [Page Life Expectancy]
FROM sys.dm_os_performance_counters WITH (NOLOCK)
WHERE [object_name] LIKE N'%Buffer Node%' -- Handles named instances
AND counter_name = N'Page life expectancy' OPTION (RECOMPILE);
------

-- PLE is a good measurement of internal memory pressure
-- Higher PLE is better. Watch the trend over time, not the absolute value
-- This will only return one row for non-NUMA systems

-- Page Life Expectancy isn�t what you think�
-- https://bit.ly/2EgynLa


-- Memory Grants Pending value for current instance  (Query 46) (Memory Grants Pending)
SELECT @@SERVERNAME AS [Server Name], RTRIM([object_name]) AS [Object Name], cntr_value AS [Memory Grants Pending]                                                                                                       
FROM sys.dm_os_performance_counters WITH (NOLOCK)
WHERE [object_name] LIKE N'%Memory Manager%' -- Handles named instances
AND counter_name = N'Memory Grants Pending' OPTION (RECOMPILE);
------

-- Run multiple times, and run periodically if you suspect you are under memory pressure
-- Memory Grants Pending above zero for a sustained period is a very strong indicator of internal memory pressure


-- Memory Clerk Usage for instance  (Query 47) (Memory Clerk Usage)
-- Look for high value for CACHESTORE_SQLCP (Ad-hoc query plans)
SELECT TOP(10) mc.[type] AS [Memory Clerk Type], 
       CAST((SUM(mc.pages_kb)/1024.0) AS DECIMAL (15,2)) AS [Memory Usage (MB)] 
FROM sys.dm_os_memory_clerks AS mc WITH (NOLOCK)
GROUP BY mc.[type]  
ORDER BY SUM(mc.pages_kb) DESC OPTION (RECOMPILE);
------

-- MEMORYCLERK_SQLBUFFERPOOL was new for SQL Server 2012. It should be your highest consumer of memory

-- CACHESTORE_SQLCP  SQL Plans         
-- These are cached SQL statements or batches that aren't in stored procedures, functions and triggers
-- Watch out for high values for CACHESTORE_SQLCP
-- Enabling 'optimize for ad hoc workloads' at the instance level can help reduce this
-- Running DBCC FREESYSTEMCACHE ('SQL Plans') periodically may be required to better control this

-- CACHESTORE_OBJCP  Object Plans      
-- These are compiled plans for stored procedures, functions and triggers

-- sys.dm_os_memory_clerks (Transact-SQL)
-- https://bit.ly/2H31xDR



-- Find single-use, ad-hoc and prepared queries that are bloating the plan cache  (Query 48) (Ad hoc Queries)
SELECT TOP(50) DB_NAME(t.[dbid]) AS [Database Name], t.[text] AS [Query Text], 
cp.objtype AS [Object Type], cp.cacheobjtype AS [Cache Object Type],  
cp.size_in_bytes/1024 AS [Plan Size in KB]
FROM sys.dm_exec_cached_plans AS cp WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t
WHERE cp.cacheobjtype = N'Compiled Plan' 
AND cp.objtype IN (N'Adhoc', N'Prepared') 
AND cp.usecounts = 1
ORDER BY cp.size_in_bytes DESC, DB_NAME(t.[dbid]) OPTION (RECOMPILE);
------

-- Gives you the text, type and size of single-use ad-hoc and prepared queries that waste space in the plan cache
-- Enabling 'optimize for ad hoc workloads' for the instance can help (SQL Server 2008 and above only)
-- Running DBCC FREESYSTEMCACHE ('SQL Plans') periodically may be required to better control this
-- Enabling forced parameterization for the database can help, but test first!

-- Plan cache, adhoc workloads and clearing the single-use plan cache bloat
-- https://bit.ly/2EfYOkl


-- Get top total logical reads queries for entire instance (Query 49) (Top Logical Reads Queries)
SELECT TOP(50) DB_NAME(t.[dbid]) AS [Database Name],
REPLACE(REPLACE(LEFT(t.[text], 255), CHAR(10),''), CHAR(13),'') AS [Short Query Text], 
qs.total_logical_reads AS [Total Logical Reads],
qs.min_logical_reads AS [Min Logical Reads],
qs.total_logical_reads/qs.execution_count AS [Avg Logical Reads],
qs.max_logical_reads AS [Max Logical Reads],   
qs.min_worker_time AS [Min Worker Time],
qs.total_worker_time/qs.execution_count AS [Avg Worker Time], 
qs.max_worker_time AS [Max Worker Time], 
qs.min_elapsed_time AS [Min Elapsed Time], 
qs.total_elapsed_time/qs.execution_count AS [Avg Elapsed Time], 
qs.max_elapsed_time AS [Max Elapsed Time],
qs.execution_count AS [Execution Count], 
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index],
qs.creation_time AS [Creation Time]
--,t.[text] AS [Complete Query Text], qp.query_plan AS [Query Plan] -- uncomment out these columns if not copying results to Excel
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t 
CROSS APPLY sys.dm_exec_query_plan(plan_handle) AS qp 
ORDER BY qs.total_logical_reads DESC OPTION (RECOMPILE);
------


-- Helps you find the most expensive queries from a memory perspective across the entire instance
-- Can also help track down parameter sniffing issues


-- Get top average elapsed time queries for entire instance (Query 50) (Top Avg Elapsed Time Queries)
SELECT TOP(50) DB_NAME(t.[dbid]) AS [Database Name], 
REPLACE(REPLACE(LEFT(t.[text], 255), CHAR(10),''), CHAR(13),'') AS [Short Query Text],  
qs.total_elapsed_time/qs.execution_count AS [Avg Elapsed Time],
qs.min_elapsed_time, qs.max_elapsed_time, qs.last_elapsed_time,
qs.execution_count AS [Execution Count],  
qs.total_logical_reads/qs.execution_count AS [Avg Logical Reads], 
qs.total_physical_reads/qs.execution_count AS [Avg Physical Reads], 
qs.total_worker_time/qs.execution_count AS [Avg Worker Time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index],
qs.creation_time AS [Creation Time]
--,t.[text] AS [Complete Query Text], qp.query_plan AS [Query Plan] -- uncomment out these columns if not copying results to Excel
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t 
CROSS APPLY sys.dm_exec_query_plan(plan_handle) AS qp 
ORDER BY qs.total_elapsed_time/qs.execution_count DESC OPTION (RECOMPILE);
------

-- Helps you find the highest average elapsed time queries across the entire instance
-- Can also help track down parameter sniffing issues


-- Look at UDF execution statistics (Query 51) (UDF Stats by DB)
SELECT TOP (25) DB_NAME(database_id) AS [Database Name], 
		   OBJECT_NAME(object_id, database_id) AS [Function Name],
		   total_worker_time, execution_count, total_elapsed_time,  
           total_elapsed_time/execution_count AS [avg_elapsed_time],  
           last_elapsed_time, last_execution_time, cached_time, [type_desc] 
FROM sys.dm_exec_function_stats WITH (NOLOCK) 
ORDER BY total_worker_time DESC OPTION (RECOMPILE);
------

-- sys.dm_exec_function_stats (Transact-SQL)
-- https://bit.ly/2q1Q6BM



-- Database specific queries *****************************************************************

-- **** Please switch to a user database that you are interested in! *****
--USE YourDatabaseName; -- make sure to change to an actual database on your instance, not the master system database
--GO

-- Individual File Sizes and space available for current database  (Query 52) (File Sizes and Space)
SELECT f.name AS [File Name] , f.physical_name AS [Physical Name], 
CAST((f.size/128.0) AS DECIMAL(15,2)) AS [Total Size in MB],
CAST(f.size/128.0 - CAST(FILEPROPERTY(f.name, 'SpaceUsed') AS int)/128.0 AS DECIMAL(15,2)) 
AS [Available Space In MB], f.[file_id], fg.name AS [Filegroup Name],
f.is_percent_growth, f.growth, fg.is_default, fg.is_read_only, 
fg.is_autogrow_all_files
FROM sys.database_files AS f WITH (NOLOCK) 
LEFT OUTER JOIN sys.filegroups AS fg WITH (NOLOCK)
ON f.data_space_id = fg.data_space_id
ORDER BY f.[file_id] OPTION (RECOMPILE);
------

-- Look at how large and how full the files are and where they are located
-- Make sure the transaction log is not full!!

-- is_autogrow_all_files was new for SQL Server 2016. Equivalent to TF 1117 for user databases

-- SQL Server 2016: Changes in default behavior for autogrow and allocations for tempdb and user databases
-- https://bit.ly/2evRZSR


-- Log space usage for current database  (Query 53) (Log Space Usage)
SELECT DB_NAME(lsu.database_id) AS [Database Name], db.recovery_model_desc AS [Recovery Model],
		CAST(lsu.total_log_size_in_bytes/1048576.0 AS DECIMAL(10, 2)) AS [Total Log Space (MB)],
		CAST(lsu.used_log_space_in_bytes/1048576.0 AS DECIMAL(10, 2)) AS [Used Log Space (MB)], 
		CAST(lsu.used_log_space_in_percent AS DECIMAL(10, 2)) AS [Used Log Space %],
		CAST(lsu.log_space_in_bytes_since_last_backup/1048576.0 AS DECIMAL(10, 2)) AS [Used Log Space Since Last Backup (MB)],
		db.log_reuse_wait_desc		 
FROM sys.dm_db_log_space_usage AS lsu WITH (NOLOCK)
INNER JOIN sys.databases AS db WITH (NOLOCK)
ON lsu.database_id = db.database_id
OPTION (RECOMPILE);
------

-- Look at log file size and usage, along with the log reuse wait description for the current database

-- sys.dm_db_log_space_usage (Transact-SQL)
-- https://bit.ly/2H4MQw9


-- Status of last VLF for current database  (Query 54) (Last VLF Status)
SELECT TOP(1) DB_NAME(li.database_id) AS [Database Name], li.[file_id],
              li.vlf_size_mb, li.vlf_sequence_number, li.vlf_active, li.vlf_status
FROM sys.dm_db_log_info(DB_ID()) AS li 
ORDER BY vlf_sequence_number DESC OPTION (RECOMPILE);
------

-- Determine whether you will be able to shrink the transaction log file

-- vlf_status Values
-- 0 is inactive 
-- 1 is initialized but unused 
-- 2 is active

-- sys.dm_db_log_info (Transact-SQL)
-- https://bit.ly/2EQUU1v



-- Get database scoped configuration values for current database (Query 55) (Database-scoped Configurations)
SELECT configuration_id, name, [value] AS [value_for_primary], value_for_secondary
FROM sys.database_scoped_configurations WITH (NOLOCK) OPTION (RECOMPILE);
------

-- This lets you see the value of these new properties for the current database

-- Clear plan cache for current database
-- ALTER DATABASE SCOPED CONFIGURATION CLEAR PROCEDURE_CACHE;

-- ALTER DATABASE SCOPED CONFIGURATION (Transact-SQL)
-- https://bit.ly/2sOH7nb


-- I/O Statistics by file for the current database  (Query 56) (IO Stats By File)
SELECT DB_NAME(DB_ID()) AS [Database Name], df.name AS [Logical Name], vfs.[file_id], df.type_desc,
df.physical_name AS [Physical Name], CAST(vfs.size_on_disk_bytes/1048576.0 AS DECIMAL(10, 2)) AS [Size on Disk (MB)],
vfs.num_of_reads, vfs.num_of_writes, vfs.io_stall_read_ms, vfs.io_stall_write_ms,
CAST(100. * vfs.io_stall_read_ms/(vfs.io_stall_read_ms + vfs.io_stall_write_ms) AS DECIMAL(10,1)) AS [IO Stall Reads Pct],
CAST(100. * vfs.io_stall_write_ms/(vfs.io_stall_write_ms + vfs.io_stall_read_ms) AS DECIMAL(10,1)) AS [IO Stall Writes Pct],
(vfs.num_of_reads + vfs.num_of_writes) AS [Writes + Reads], 
CAST(vfs.num_of_bytes_read/1048576.0 AS DECIMAL(10, 2)) AS [MB Read], 
CAST(vfs.num_of_bytes_written/1048576.0 AS DECIMAL(10, 2)) AS [MB Written],
CAST(100. * vfs.num_of_reads/(vfs.num_of_reads + vfs.num_of_writes) AS DECIMAL(10,1)) AS [# Reads Pct],
CAST(100. * vfs.num_of_writes/(vfs.num_of_reads + vfs.num_of_writes) AS DECIMAL(10,1)) AS [# Write Pct],
CAST(100. * vfs.num_of_bytes_read/(vfs.num_of_bytes_read + vfs.num_of_bytes_written) AS DECIMAL(10,1)) AS [Read Bytes Pct],
CAST(100. * vfs.num_of_bytes_written/(vfs.num_of_bytes_read + vfs.num_of_bytes_written) AS DECIMAL(10,1)) AS [Written Bytes Pct]
FROM sys.dm_io_virtual_file_stats(DB_ID(), NULL) AS vfs
INNER JOIN sys.database_files AS df WITH (NOLOCK)
ON vfs.[file_id]= df.[file_id] OPTION (RECOMPILE);
------

-- This helps you characterize your workload better from an I/O perspective for this database
-- It helps you determine whether you has an OLTP or DW/DSS type of workload



-- Get most frequently executed queries for this database (Query 57) (Query Execution Counts)
SELECT TOP(50) LEFT(t.[text], 50) AS [Short Query Text], qs.execution_count AS [Execution Count],
qs.total_logical_reads AS [Total Logical Reads],
qs.total_logical_reads/qs.execution_count AS [Avg Logical Reads],
qs.total_worker_time AS [Total Worker Time],
qs.total_worker_time/qs.execution_count AS [Avg Worker Time], 
qs.total_elapsed_time AS [Total Elapsed Time],
qs.total_elapsed_time/qs.execution_count AS [Avg Elapsed Time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index], 
qs.creation_time AS [Creation Time]
--,t.[text] AS [Complete Query Text], qp.query_plan AS [Query Plan] -- uncomment out these columns if not copying results to Excel
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t 
CROSS APPLY sys.dm_exec_query_plan(plan_handle) AS qp 
WHERE t.dbid = DB_ID()
ORDER BY qs.execution_count DESC OPTION (RECOMPILE);
------


-- Queries 58 through 63 are the "Bad Man List" for stored procedures

-- Top Cached SPs By Execution Count (Query 58) (SP Execution Counts)
SELECT TOP(100) p.name AS [SP Name], qs.execution_count AS [Execution Count],
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute],
qs.total_elapsed_time/qs.execution_count AS [Avg Elapsed Time],
qs.total_worker_time/qs.execution_count AS [Avg Worker Time],    
qs.total_logical_reads/qs.execution_count AS [Avg Logical Reads],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index],
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND DATEDIFF(Minute, qs.cached_time, GETDATE()) > 0
ORDER BY qs.execution_count DESC OPTION (RECOMPILE);
------

-- Tells you which cached stored procedures are called the most often
-- This helps you characterize and baseline your workload


-- Top Cached SPs By Avg Elapsed Time (Query 59) (SP Avg Elapsed Time)
SELECT TOP(25) p.name AS [SP Name], qs.min_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time], 
qs.max_elapsed_time, qs.last_elapsed_time, qs.total_elapsed_time, qs.execution_count, 
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute], 
qs.total_worker_time/qs.execution_count AS [AvgWorkerTime], 
qs.total_worker_time AS [TotalWorkerTime],
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND DATEDIFF(Minute, qs.cached_time, GETDATE()) > 0
ORDER BY avg_elapsed_time DESC OPTION (RECOMPILE);
------

-- This helps you find high average elapsed time cached stored procedures that
-- may be easy to optimize with standard query tuning techniques



-- Top Cached SPs By Total Worker time. Worker time relates to CPU cost  (Query 60) (SP Worker Time)
SELECT TOP(25) p.name AS [SP Name], qs.total_worker_time AS [TotalWorkerTime], 
qs.total_worker_time/qs.execution_count AS [AvgWorkerTime], qs.execution_count, 
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute],
qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index],
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND DATEDIFF(Minute, qs.cached_time, GETDATE()) > 0
ORDER BY qs.total_worker_time DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a CPU perspective
-- You should look at this if you see signs of CPU pressure


-- Top Cached SPs By Total Logical Reads. Logical reads relate to memory pressure  (Query 61) (SP Logical Reads)
SELECT TOP(25) p.name AS [SP Name], qs.total_logical_reads AS [TotalLogicalReads], 
qs.total_logical_reads/qs.execution_count AS [AvgLogicalReads],qs.execution_count, 
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute], 
qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index], 
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND DATEDIFF(Minute, qs.cached_time, GETDATE()) > 0
ORDER BY qs.total_logical_reads DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a memory perspective
-- You should look at this if you see signs of memory pressure


-- Top Cached SPs By Total Physical Reads. Physical reads relate to disk read I/O pressure  (Query 62) (SP Physical Reads)
SELECT TOP(25) p.name AS [SP Name],qs.total_physical_reads AS [TotalPhysicalReads], 
qs.total_physical_reads/qs.execution_count AS [AvgPhysicalReads], qs.execution_count, 
qs.total_logical_reads,qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index],
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan 
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND qs.total_physical_reads > 0
ORDER BY qs.total_physical_reads DESC, qs.total_logical_reads DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a read I/O perspective
-- You should look at this if you see signs of I/O pressure or of memory pressure
       


-- Top Cached SPs By Total Logical Writes (Query 63) (SP Logical Writes)
-- Logical writes relate to both memory and disk I/O pressure 
SELECT TOP(25) p.name AS [SP Name], qs.total_logical_writes AS [TotalLogicalWrites], 
qs.total_logical_writes/qs.execution_count AS [AvgLogicalWrites], qs.execution_count,
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute],
qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index], 
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan 
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND qs.total_logical_writes > 0
AND DATEDIFF(Minute, qs.cached_time, GETDATE()) > 0
ORDER BY qs.total_logical_writes DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a write I/O perspective
-- You should look at this if you see signs of I/O pressure or of memory pressure


-- Lists the top statements by average input/output usage for the current database  (Query 64) (Top IO Statements)
SELECT TOP(50) OBJECT_NAME(qt.objectid, dbid) AS [SP Name],
(qs.total_logical_reads + qs.total_logical_writes) /qs.execution_count AS [Avg IO], qs.execution_count AS [Execution Count],
SUBSTRING(qt.[text],qs.statement_start_offset/2, 
	(CASE 
		WHEN qs.statement_end_offset = -1 
	 THEN LEN(CONVERT(nvarchar(max), qt.[text])) * 2 
		ELSE qs.statement_end_offset 
	 END - qs.statement_start_offset)/2) AS [Query Text]	
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS qt
WHERE qt.[dbid] = DB_ID()
ORDER BY [Avg IO] DESC OPTION (RECOMPILE);
------

-- Helps you find the most expensive statements for I/O by SP



-- Possible Bad NC Indexes (writes > reads)  (Query 65) (Bad NC Indexes)
SELECT OBJECT_NAME(s.[object_id]) AS [Table Name], i.name AS [Index Name], i.index_id, 
i.is_disabled, i.is_hypothetical, i.has_filter, i.fill_factor,
s.user_updates AS [Total Writes], s.user_seeks + s.user_scans + s.user_lookups AS [Total Reads],
s.user_updates - (s.user_seeks + s.user_scans + s.user_lookups) AS [Difference]
FROM sys.dm_db_index_usage_stats AS s WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON s.[object_id] = i.[object_id]
AND i.index_id = s.index_id
WHERE OBJECTPROPERTY(s.[object_id],'IsUserTable') = 1
AND s.database_id = DB_ID()
AND s.user_updates > (s.user_seeks + s.user_scans + s.user_lookups)
AND i.index_id > 1 AND i.[type_desc] = N'NONCLUSTERED'
AND i.is_primary_key = 0 AND i.is_unique_constraint = 0 AND i.is_unique = 0
ORDER BY [Difference] DESC, [Total Writes] DESC, [Total Reads] ASC OPTION (RECOMPILE);
------

-- Look for indexes with high numbers of writes and zero or very low numbers of reads
-- Consider your complete workload, and how long your instance has been running
-- Investigate further before dropping an index!


-- Missing Indexes for current database by Index Advantage  (Query 66) (Missing Indexes)
SELECT CONVERT(decimal(18,2), user_seeks * avg_total_user_cost * (avg_user_impact * 0.01)) AS [index_advantage], 
migs.last_user_seek, mid.[statement] AS [Database.Schema.Table],
COUNT(1) OVER(PARTITION BY mid.[statement]) AS [missing_indexes_for_table],
COUNT(1) OVER(PARTITION BY mid.[statement], equality_columns) AS [similar_missing_indexes_for_table],
mid.equality_columns, mid.inequality_columns, mid.included_columns,
migs.unique_compiles, migs.user_seeks, 
CONVERT(decimal(18,2), migs.avg_total_user_cost) AS [avg_total_user_cost], migs.avg_user_impact,
OBJECT_NAME(mid.[object_id]) AS [Table Name], p.rows AS [Table Rows]
FROM sys.dm_db_missing_index_group_stats AS migs WITH (NOLOCK)
INNER JOIN sys.dm_db_missing_index_groups AS mig WITH (NOLOCK)
ON migs.group_handle = mig.index_group_handle
INNER JOIN sys.dm_db_missing_index_details AS mid WITH (NOLOCK)
ON mig.index_handle = mid.index_handle
INNER JOIN sys.partitions AS p WITH (NOLOCK)
ON p.[object_id] = mid.[object_id]
WHERE mid.database_id = DB_ID()
AND p.index_id < 2 
ORDER BY index_advantage DESC OPTION (RECOMPILE);
------

-- Look at index advantage, last user seek time, number of user seeks to help determine source and importance
-- SQL Server is overly eager to add included columns, so beware
-- Do not just blindly add indexes that show up from this query!!!


-- Find missing index warnings for cached plans in the current database  (Query 67) (Missing Index Warnings)
-- Note: This query could take some time on a busy instance
SELECT TOP(25) OBJECT_NAME(objectid) AS [ObjectName], 
               cp.objtype, cp.usecounts, cp.size_in_bytes, query_plan
FROM sys.dm_exec_cached_plans AS cp WITH (NOLOCK)
CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) AS qp
WHERE CAST(query_plan AS NVARCHAR(MAX)) LIKE N'%MissingIndex%'
AND dbid = DB_ID()
ORDER BY cp.usecounts DESC OPTION (RECOMPILE);
------

-- Helps you connect missing indexes to specific stored procedures or queries
-- This can help you decide whether to add them or not


-- Breaks down buffers used by current database by object (table, index) in the buffer cache  (Query 68) (Buffer Usage)
-- Note: This query could take some time on a busy instance
SELECT OBJECT_NAME(p.[object_id]) AS [Object Name], p.index_id, 
CAST(COUNT(*)/128.0 AS DECIMAL(10, 2)) AS [Buffer size(MB)],  
COUNT(*) AS [BufferCount], p.[Rows] AS [Row Count],
p.data_compression_desc AS [Compression Type]
FROM sys.allocation_units AS a WITH (NOLOCK)
INNER JOIN sys.dm_os_buffer_descriptors AS b WITH (NOLOCK)
ON a.allocation_unit_id = b.allocation_unit_id
INNER JOIN sys.partitions AS p WITH (NOLOCK)
ON a.container_id = p.hobt_id
WHERE b.database_id = CONVERT(int, DB_ID())
AND p.[object_id] > 100
AND OBJECT_NAME(p.[object_id]) NOT LIKE N'plan_%'
AND OBJECT_NAME(p.[object_id]) NOT LIKE N'sys%'
AND OBJECT_NAME(p.[object_id]) NOT LIKE N'xml_index_nodes%'
GROUP BY p.[object_id], p.index_id, p.data_compression_desc, p.[Rows]
ORDER BY [BufferCount] DESC OPTION (RECOMPILE);
------

-- Tells you what tables and indexes are using the most memory in the buffer cache
-- It can help identify possible candidates for data compression


-- Get Table names, row counts, and compression status for clustered index or heap  (Query 69) (Table Sizes)
SELECT OBJECT_NAME(object_id) AS [ObjectName], 
SUM(Rows) AS [RowCount], data_compression_desc AS [CompressionType]
FROM sys.partitions WITH (NOLOCK)
WHERE index_id < 2 --ignore the partitions from the non-clustered index if any
AND OBJECT_NAME(object_id) NOT LIKE N'sys%'
AND OBJECT_NAME(object_id) NOT LIKE N'queue_%' 
AND OBJECT_NAME(object_id) NOT LIKE N'filestream_tombstone%' 
AND OBJECT_NAME(object_id) NOT LIKE N'fulltext%'
AND OBJECT_NAME(object_id) NOT LIKE N'ifts_comp_fragment%'
AND OBJECT_NAME(object_id) NOT LIKE N'filetable_updates%'
AND OBJECT_NAME(object_id) NOT LIKE N'xml_index_nodes%'
AND OBJECT_NAME(object_id) NOT LIKE N'sqlagent_job%'  
AND OBJECT_NAME(object_id) NOT LIKE N'plan_persist%'  
GROUP BY object_id, data_compression_desc
ORDER BY SUM(Rows) DESC OPTION (RECOMPILE);
------

-- Gives you an idea of table sizes, and possible data compression opportunities



-- Get some key table properties (Query 70) (Table Properties)
SELECT OBJECT_NAME(t.[object_id]) AS [ObjectName], p.[rows] AS [Table Rows], p.index_id, 
       p.data_compression_desc AS [Index Data Compression],
       t.create_date, t.lock_on_bulk_load, t.is_replicated, t.has_replication_filter, 
       t.is_tracked_by_cdc, t.lock_escalation_desc, t.is_filetable, 
	   t.is_memory_optimized, t.durability_desc, 
	   t.temporal_type_desc, t.is_remote_data_archive_enabled, t.is_external -- new for SQL Server 2016
FROM sys.tables AS t WITH (NOLOCK)
INNER JOIN sys.partitions AS p WITH (NOLOCK)
ON t.[object_id] = p.[object_id]
WHERE OBJECT_NAME(t.[object_id]) NOT LIKE N'sys%'
ORDER BY OBJECT_NAME(t.[object_id]), p.index_id OPTION (RECOMPILE);
------

-- Gives you some good information about your tables
-- is_memory_optimized and durability_desc were new in SQL Server 2014
-- temporal_type_desc, is_remote_data_archive_enabled, is_external were new in SQL Server 2016

-- sys.tables (Transact-SQL)
-- https://bit.ly/2Gk7998



-- When were Statistics last updated on all indexes?  (Query 71) (Statistics Update)
SELECT SCHEMA_NAME(o.Schema_ID) + N'.' + o.[NAME] AS [Object Name], o.[type_desc] AS [Object Type],
      i.[name] AS [Index Name], STATS_DATE(i.[object_id], i.index_id) AS [Statistics Date], 
      s.auto_created, s.no_recompute, s.user_created, s.is_incremental, s.is_temporary,
	  st.row_count, st.used_page_count
FROM sys.objects AS o WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON o.[object_id] = i.[object_id]
INNER JOIN sys.stats AS s WITH (NOLOCK)
ON i.[object_id] = s.[object_id] 
AND i.index_id = s.stats_id
INNER JOIN sys.dm_db_partition_stats AS st WITH (NOLOCK)
ON o.[object_id] = st.[object_id]
AND i.[index_id] = st.[index_id]
WHERE o.[type] IN ('U', 'V')
AND st.row_count > 0
ORDER BY STATS_DATE(i.[object_id], i.index_id) DESC OPTION (RECOMPILE);
------  

-- Helps discover possible problems with out-of-date statistics
-- Also gives you an idea which indexes are the most active

-- sys.stats (Transact-SQL)
-- https://bit.ly/2GyAxrn

-- UPDATEs to Statistics (Erin Stellato)
-- https://bit.ly/2vhrYQy




-- Look at most frequently modified indexes and statistics (Query 72) (Volatile Indexes)
SELECT o.[name] AS [Object Name], o.[object_id], o.[type_desc], s.[name] AS [Statistics Name], 
       s.stats_id, s.no_recompute, s.auto_created, s.is_incremental, s.is_temporary,
	   sp.modification_counter, sp.[rows], sp.rows_sampled, sp.last_updated
FROM sys.objects AS o WITH (NOLOCK)
INNER JOIN sys.stats AS s WITH (NOLOCK)
ON s.object_id = o.object_id
CROSS APPLY sys.dm_db_stats_properties(s.object_id, s.stats_id) AS sp
WHERE o.[type_desc] NOT IN (N'SYSTEM_TABLE', N'INTERNAL_TABLE')
AND sp.modification_counter > 0
ORDER BY sp.modification_counter DESC, o.name OPTION (RECOMPILE);
------

-- This helps you understand your workload and make better decisions about 
-- things like data compression and adding new indexes to a table



-- Get fragmentation info for all indexes above a certain size in the current database  (Query 73) (Index Fragmentation)
-- Note: This query could take some time on a very large database
SELECT DB_NAME(ps.database_id) AS [Database Name], SCHEMA_NAME(o.[schema_id]) AS [Schema Name],
OBJECT_NAME(ps.OBJECT_ID) AS [Object Name], i.[name] AS [Index Name], ps.index_id, 
ps.index_type_desc, ps.avg_fragmentation_in_percent, 
ps.fragment_count, ps.page_count, i.fill_factor, i.has_filter, 
i.filter_definition, i.[allow_page_locks]
FROM sys.dm_db_index_physical_stats(DB_ID(),NULL, NULL, NULL , N'LIMITED') AS ps
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON ps.[object_id] = i.[object_id] 
AND ps.index_id = i.index_id
INNER JOIN sys.objects AS o WITH (NOLOCK)
ON i.[object_id] = o.[object_id]
WHERE ps.database_id = DB_ID()
AND ps.page_count > 2500
ORDER BY ps.avg_fragmentation_in_percent DESC OPTION (RECOMPILE);
------

-- Helps determine whether you have framentation in your relational indexes
-- and how effective your index maintenance strategy is


--- Index Read/Write stats (all tables in current DB) ordered by Reads  (Query 74) (Overall Index Usage - Reads)
SELECT OBJECT_NAME(i.[object_id]) AS [ObjectName], i.[name] AS [IndexName], i.index_id, 
       s.user_seeks, s.user_scans, s.user_lookups,
	   s.user_seeks + s.user_scans + s.user_lookups AS [Total Reads], 
	   s.user_updates AS [Writes],  
	   i.[type_desc] AS [Index Type], i.fill_factor AS [Fill Factor], i.has_filter, i.filter_definition, 
	   s.last_user_scan, s.last_user_lookup, s.last_user_seek
FROM sys.indexes AS i WITH (NOLOCK)
LEFT OUTER JOIN sys.dm_db_index_usage_stats AS s WITH (NOLOCK)
ON i.[object_id] = s.[object_id]
AND i.index_id = s.index_id
AND s.database_id = DB_ID()
WHERE OBJECTPROPERTY(i.[object_id],'IsUserTable') = 1
ORDER BY s.user_seeks + s.user_scans + s.user_lookups DESC OPTION (RECOMPILE); -- Order by reads
------

-- Show which indexes in the current database are most active for Reads


--- Index Read/Write stats (all tables in current DB) ordered by Writes  (Query 75) (Overall Index Usage - Writes)
SELECT OBJECT_NAME(i.[object_id]) AS [ObjectName], i.[name] AS [IndexName], i.index_id,
	   s.user_updates AS [Writes], s.user_seeks + s.user_scans + s.user_lookups AS [Total Reads], 
	   i.[type_desc] AS [Index Type], i.fill_factor AS [Fill Factor], i.has_filter, i.filter_definition,
	   s.last_system_update, s.last_user_update
FROM sys.indexes AS i WITH (NOLOCK)
LEFT OUTER JOIN sys.dm_db_index_usage_stats AS s WITH (NOLOCK)
ON i.[object_id] = s.[object_id]
AND i.index_id = s.index_id
AND s.database_id = DB_ID()
WHERE OBJECTPROPERTY(i.[object_id],'IsUserTable') = 1
ORDER BY s.user_updates DESC OPTION (RECOMPILE);						 -- Order by writes
------

-- Show which indexes in the current database are most active for Writes


-- Get in-memory OLTP index usage (Query 76) (XTP Index Usage)
SELECT OBJECT_NAME(i.[object_id]) AS [Object Name], i.index_id, i.[name] AS [Index Name],
       i.[type_desc], xis.scans_started, xis.scans_retries, 
	   xis.rows_touched, xis.rows_returned
FROM sys.dm_db_xtp_index_stats AS xis WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON i.[object_id] = xis.[object_id] 
AND i.index_id = xis.index_id 
ORDER BY OBJECT_NAME(i.[object_id]) OPTION (RECOMPILE);
------

-- This gives you some index usage statistics for in-memory OLTP
-- Returns no data if you are not using in-memory OLTP

-- Guidelines for Using Indexes on Memory-Optimized Tables
-- https://bit.ly/2GCP8lF



-- Look at Columnstore index physical statistics (Query 77) (Columnstore Index Physical Stat)
SELECT OBJECT_NAME(ps.object_id) AS [TableName],  
	i.[name] AS [IndexName], ps.index_id, ps.partition_number,
	ps.delta_store_hobt_id, ps.state_desc, ps.total_rows, ps.size_in_bytes,
	ps.trim_reason_desc, ps.generation, ps.transition_to_compressed_state_desc,
	ps.has_vertipaq_optimization, ps.deleted_rows,
	100 * (ISNULL(ps.deleted_rows, 0))/ps.total_rows AS [Fragmentation]
FROM sys.dm_db_column_store_row_group_physical_stats AS ps WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON ps.object_id = i.object_id 
AND ps.index_id = i.index_id
ORDER BY ps.object_id, ps.partition_number, ps.row_group_id OPTION (RECOMPILE);
------

-- sys.dm_db_column_store_row_group_physical_stats (Transact-SQL)
-- https://bit.ly/2q276XQ



-- Get lock waits for current database (Query 78) (Lock Waits)
SELECT o.name AS [table_name], i.name AS [index_name], ios.index_id, ios.partition_number,
		SUM(ios.row_lock_wait_count) AS [total_row_lock_waits], 
		SUM(ios.row_lock_wait_in_ms) AS [total_row_lock_wait_in_ms],
		SUM(ios.page_lock_wait_count) AS [total_page_lock_waits],
		SUM(ios.page_lock_wait_in_ms) AS [total_page_lock_wait_in_ms],
		SUM(ios.page_lock_wait_in_ms)+ SUM(row_lock_wait_in_ms) AS [total_lock_wait_in_ms]
FROM sys.dm_db_index_operational_stats(DB_ID(), NULL, NULL, NULL) AS ios
INNER JOIN sys.objects AS o WITH (NOLOCK)
ON ios.[object_id] = o.[object_id]
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON ios.[object_id] = i.[object_id] 
AND ios.index_id = i.index_id
WHERE o.[object_id] > 100
GROUP BY o.name, i.name, ios.index_id, ios.partition_number
HAVING SUM(ios.page_lock_wait_in_ms)+ SUM(row_lock_wait_in_ms) > 0
ORDER BY total_lock_wait_in_ms DESC OPTION (RECOMPILE);
------

-- This query is helpful for troubleshooting blocking and deadlocking issues



-- Look at UDF execution statistics (Query 79) (UDF Statistics)
SELECT OBJECT_NAME(object_id) AS [Function Name], execution_count,
	   total_worker_time, total_logical_reads, total_physical_reads, total_elapsed_time, 
	   total_elapsed_time/execution_count AS [avg_elapsed_time],
	   FORMAT(cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
FROM sys.dm_exec_function_stats WITH (NOLOCK) 
WHERE database_id = DB_ID()
ORDER BY total_worker_time DESC OPTION (RECOMPILE); 
------

-- New for SQL Server 2016
-- Helps you investigate scalar UDF performance issues
-- Does not return information for table valued functions

-- sys.dm_exec_function_stats (Transact-SQL)
-- https://bit.ly/2q1Q6BM


-- Get QueryStore Options for this database (Query 80) (QueryStore Options)
SELECT actual_state_desc, desired_state_desc, [interval_length_minutes],
       current_storage_size_mb, [max_storage_size_mb], 
	   query_capture_mode_desc, size_based_cleanup_mode_desc
FROM sys.database_query_store_options WITH (NOLOCK) OPTION (RECOMPILE);
------

-- New for SQL Server 2016
-- Requires that Query Store is enabled for this database

-- Tuning Workload Performance with Query Store
-- https://bit.ly/1kHSl7w


-- Get highest aggregate duration queries over last hour (Query 81) (High Aggregate Duration Queries)
WITH AggregatedDurationLastHour
AS
(SELECT q.query_id, SUM(count_executions * avg_duration) AS total_duration,
   COUNT (distinct p.plan_id) AS number_of_plans
   FROM sys.query_store_query_text AS qt WITH (NOLOCK)
   INNER JOIN sys.query_store_query AS q WITH (NOLOCK)
   ON qt.query_text_id = q.query_text_id
   INNER JOIN sys.query_store_plan AS p WITH (NOLOCK)
   ON q.query_id = p.query_id
   INNER JOIN sys.query_store_runtime_stats AS rs WITH (NOLOCK)
   ON rs.plan_id = p.plan_id
   INNER JOIN sys.query_store_runtime_stats_interval AS rsi WITH (NOLOCK)
   ON rsi.runtime_stats_interval_id = rs.runtime_stats_interval_id
   WHERE rsi.start_time >= DATEADD(hour, -1, GETUTCDATE()) 
   AND rs.execution_type_desc = N'Regular'
   GROUP BY q.query_id),
OrderedDuration AS
(SELECT query_id, total_duration, number_of_plans, 
 ROW_NUMBER () OVER (ORDER BY total_duration DESC, query_id) AS RN
 FROM AggregatedDurationLastHour)
SELECT OBJECT_NAME(q.object_id) AS [Containing Object], qt.query_sql_text, 
od.total_duration AS [Total Duration (microsecs)], 
od.number_of_plans AS [Plan Count],
p.is_forced_plan, p.is_parallel_plan, p.is_trivial_plan,
q.query_parameterization_type_desc, p.[compatibility_level],
p.last_compile_start_time, q.last_execution_time,
CONVERT(xml, p.query_plan) AS query_plan_xml 
FROM OrderedDuration AS od 
INNER JOIN sys.query_store_query AS q WITH (NOLOCK)
ON q.query_id  = od.query_id
INNER JOIN sys.query_store_query_text AS qt WITH (NOLOCK)
ON q.query_text_id = qt.query_text_id
INNER JOIN sys.query_store_plan AS p WITH (NOLOCK)
ON q.query_id = p.query_id
WHERE od.RN <= 50 
ORDER BY total_duration DESC OPTION (RECOMPILE);
------

-- New for SQL Server 2016
-- Requires that QueryStore is enabled for this database



-- Get highest aggregate CPU time queries over last hour (Query 82) (High Aggregate CPU Queries)
WITH AggregatedCPULastHour
AS
(SELECT q.query_id, SUM(rs.count_executions * rs.avg_cpu_time) AS total_cpu_time,
   COUNT (DISTINCT p.plan_id) AS number_of_plans
   FROM sys.query_store_query_text AS qt WITH (NOLOCK)
   INNER JOIN sys.query_store_query AS q WITH (NOLOCK)
   ON qt.query_text_id = q.query_text_id
   INNER JOIN sys.query_store_plan AS p WITH (NOLOCK)
   ON q.query_id = p.query_id
   INNER JOIN sys.query_store_runtime_stats AS rs WITH (NOLOCK)
   ON rs.plan_id = p.plan_id
   INNER JOIN sys.query_store_runtime_stats_interval AS rsi WITH (NOLOCK)
   ON rsi.runtime_stats_interval_id = rs.runtime_stats_interval_id
   WHERE rsi.start_time >= DATEADD(hour, -1, GETUTCDATE()) 
   AND rs.execution_type_desc = N'Regular'
   GROUP BY q.query_id), OrderedDuration 
AS
(SELECT query_id, total_cpu_time, number_of_plans, 
 ROW_NUMBER () OVER (ORDER BY total_cpu_time DESC, query_id) AS RN
 FROM AggregatedCPULastHour)
SELECT OBJECT_NAME(q.object_id) AS [Containing Object], qt.query_sql_text, 
od.total_cpu_time AS [Total CPU Time (microsecs)], 
od.number_of_plans AS [Plan Count],
p.is_forced_plan, p.is_parallel_plan, p.is_trivial_plan,
q.query_parameterization_type_desc, p.[compatibility_level],
p.last_compile_start_time, 
q.last_execution_time,
CONVERT(xml, p.query_plan) AS query_plan_xml 
FROM OrderedDuration AS od 
INNER JOIN sys.query_store_query AS q WITH (NOLOCK)
ON q.query_id  = od.query_id
INNER JOIN sys.query_store_query_text AS qt WITH (NOLOCK)
ON q.query_text_id = qt.query_text_id
INNER JOIN sys.query_store_plan AS p WITH (NOLOCK)
ON q.query_id = p.query_id
WHERE od.RN <= 50 
ORDER BY od.total_cpu_time DESC OPTION (RECOMPILE);
------

-- New for SQL Server 2016
-- Requires that QueryStore is enabled for this database




-- Get input buffer information for the current database (Query 83) (Input Buffer)
SELECT es.session_id, DB_NAME(es.database_id) AS [Database Name],
       es.login_time, es.cpu_time, es.logical_reads, es.memory_usage,
       es.[status], ib.event_info AS [Input Buffer]
FROM sys.dm_exec_sessions AS es WITH (NOLOCK)
CROSS APPLY sys.dm_exec_input_buffer(es.session_id, NULL) AS ib
WHERE es.database_id = DB_ID()
AND es.session_id > 50
AND es.session_id <> @@SPID OPTION (RECOMPILE);
------

-- Gives you input buffer information from all non-system sessions for the current database
-- Replaces DBCC INPUTBUFFER

-- New DMF for retrieving input buffer in SQL Server
-- https://bit.ly/2uHKMbz

-- sys.dm_exec_input_buffer (Transact-SQL)
-- https://bit.ly/2J5Hf9q



-- Look at recent Full backups for the current database (Query 84) (Recent Full Backups)
SELECT TOP (30) bs.machine_name, bs.server_name, bs.database_name AS [Database Name], bs.recovery_model,
CONVERT (BIGINT, bs.backup_size / 1048576 ) AS [Uncompressed Backup Size (MB)],
CONVERT (BIGINT, bs.compressed_backup_size / 1048576 ) AS [Compressed Backup Size (MB)],
CONVERT (NUMERIC (20,2), (CONVERT (FLOAT, bs.backup_size) /
CONVERT (FLOAT, bs.compressed_backup_size))) AS [Compression Ratio], bs.has_backup_checksums, bs.is_copy_only, bs.encryptor_type,
DATEDIFF (SECOND, bs.backup_start_date, bs.backup_finish_date) AS [Backup Elapsed Time (sec)],
bs.backup_finish_date AS [Backup Finish Date], bmf.physical_device_name AS [Backup Location], bmf.physical_block_size
FROM msdb.dbo.backupset AS bs WITH (NOLOCK)
INNER JOIN msdb.dbo.backupmediafamily AS bmf WITH (NOLOCK)
ON bs.media_set_id = bmf.media_set_id  
WHERE bs.database_name = DB_NAME(DB_ID())
AND bs.[type] = 'D' -- Change to L if you want Log backups
ORDER BY bs.backup_finish_date DESC OPTION (RECOMPILE);
------

-- Are your backup sizes and times changing over time?
-- Are you using backup compression?
-- Are you using backup checksums?
-- Are you doing copy_only backups?
-- Are you doing encrypted backups?
-- Have you done any backup tuning with striped backups, or changing the parameters of the backup command?

-- In SQL Server 2016, native SQL Server backup compression actually works 
-- much better with databases that are using TDE than in previous versions
-- https://bit.ly/28Rpb2x


-- These three Pluralsight Courses go into more detail about how to run these queries and interpret the results

-- SQL Server 2014 DMV Diagnostic Queries � Part 1 
-- https://bit.ly/2plxCer

-- SQL Server 2014 DMV Diagnostic Queries � Part 2
-- https://bit.ly/2IuJpzI

-- SQL Server 2014 DMV Diagnostic Queries � Part 3
-- https://bit.ly/2FIlCPb



-- Sign up for Microsoft Visual Studio Dev Essentials and get a free three month pass to Pluralsight

-- Microsoft Visual Studio Dev Essentials
-- http://bit.ly/1q6xbDL


-- Sign up for Microsoft Azure Essentials and get lots of free Azure usage credits, MCP exam voucher, three month Pluralsight subscription

-- Microsoft Azure Essentials
-- https://bit.ly/2JMWe8x


-- August 2017 blog series about upgrading and migrating SQL Server
-- https://bit.ly/2ftKVrX
tools\dbatools\bin\diagnosticquery\SQLServerDiagnosticQueries_2016_201806.sql

-- SQL Server 2016 Diagnostic Information Queries
-- Glenn Berry 
-- Last Modified: July 18, 2018
-- https://www.sqlskills.com/blogs/glenn/
-- http://sqlserverperformance.wordpress.com/
-- Twitter: GlennAlanBerry

-- Please listen to my Pluralsight courses
-- https://www.pluralsight.com/author/glenn-berry

-- If you want to find all of our SQLskills SQL101 blog posts, check out https://www.sqlskills.com/help/sql101/


-- Please make sure you are using the correct version of these diagnostic queries for your version of SQL Server


-- If you like PowerShell, there is a very useful community solution for running these queries in an automated fashion
-- https://dbatools.io/

-- Invoke-DbaDiagnosticQuery
-- https://dbatools.io/functions/invoke-dbadiagnosticquery/


--******************************************************************************
--*   Copyright (C) 2018 Glenn Berry, SQLskills.com
--*   All rights reserved. 
--*
--*   For more scripts and sample code, check out 
--*      https://www.sqlskills.com/blogs/glenn
--*
--*   You may alter this code for your own *non-commercial* purposes. You may
--*   republish altered code as long as you include this copyright and give due credit. 
--*
--*
--*   THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF 
--*   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED 
--*   TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
--*   PARTICULAR PURPOSE. 
--*
--******************************************************************************

-- Check the major product version to see if it is SQL Server 2016 CTP 2 or greater
IF NOT EXISTS (SELECT * WHERE CONVERT(varchar(128), SERVERPROPERTY('ProductVersion')) LIKE '13%')
	BEGIN
		DECLARE @ProductVersion varchar(128) = CONVERT(varchar(128), SERVERPROPERTY('ProductVersion'));
		RAISERROR ('Script does not match the ProductVersion [%s] of this instance. Many of these queries may not work on this version.' , 18 , 16 , @ProductVersion);
	END
	ELSE
		PRINT N'You have the correct major version of SQL Server for this diagnostic information script';
	

-- Instance level queries *******************************

-- SQL and OS Version information for current instance  (Query 1) (Version Info)
SELECT @@SERVERNAME AS [Server Name], @@VERSION AS [SQL Server and OS Version Info];
------

-- SQL Server 2016 RTM Branch Builds								-- SQL Server 2016 SP1 Branch Builds                     -- SQL Server 2016 SP2 Branch Builds										
-- Build			Description			Release Date				Build			Description			Release Date	     Build			Description			Release Date			
-- 13.0.200.172		CTP 2.0				5/26/2015
-- 13.0.300.44		CTP 2.1				6/14/2015
-- 13.0.407.1		CTP 2.2				7/28/2015
-- 13.0.500.53		CTP 2.3				9/4/2015
-- 13.0.600.65		CTP 2.4				9/30/2015
-- 13.0.700.242		CTP 3.0				10/29/2015
-- 13.0.900.73		CTP 3.2				12/12/2015
-- 13.0.1000.276	CTP 3.3				1/27/2016
-- 13.0.1100.288	RC0					3/2/2016
-- 13.0.1200.242	RC1					3/18/2016 
-- 13.0.1300.275	RC2					3/28/2016
-- 13.0.1400.361	RC3					4/11/2016
-- 13.0.1601.5		RTM					6/1/2016
-- 13.0.1708.0		RTM-GDR				6/12/2016
-- 13.0.2149.0		RTM CU1				7/25/2016
-- 13.0.2164.0		RTM CU2				9/22/2016
-- 13.0.2186.0		RTM CU3				11/16/2016	---->			13.0.4001.0		SP1 RTM				 11/16/2016
-- 13.0.2193.0		RTM CU4				1/18/2017   ---->			13.0.4411.0		SP1 CU1				 1/18/2017
-- 13.0.2197.0		RTM CU5				3/20/2017   ---->			13.0.4422.0		SP1 CU2				 3/20/2017
-- 13.0.2204.0		RTM CU6				5/15/2017   ---->			13.0.4435.0		SP1 CU3				 5/15/2017
-- 13.0.2210.0		RTM CU7				8/8/2017    ---->			13.0.4446.0		SP1 CU4				  8/8/2017
-- 13.0.2213.0		RTM CU8				9/18/2017   ---->           13.0.4451.0		SP1 CU5				 9/18/2017
-- 13.0.2216.0		RTM CU9				11/21/2017  ---->			13.0.4457.0		SP1 CU6				 11/21/2017
--																	13.0.4466.4		SP1 CU7				  1/4/2018
--																	13.0.4474.0		SP1 CU8				  3/20/2018	---->	13.0.5026.0		SP2 RTM				4/24/2018
--                                                                  13.0.4502.0		SP1 CU9				  5/30/2018 ---->   13.0.5149.0		SP2 CU1				5/30/2018
--                                                                  13.0.4514.0     SP1 CU10			  7/16/2018 ---->   13.0.5153.0     SP2 CU2				7/16/2018


-- How to determine the version, edition and update level of SQL Server and its components 
-- https://bit.ly/2oAjKgW														

-- How to obtain the latest Service Pack for SQL Server 2016
-- https://bit.ly/2egtfzK

-- Microsoft SQL Server 2016 SP1 Latest Cumulative Update
-- https://bit.ly/2jTwxWC

-- SQL Server 2016 build versions 
-- https://bit.ly/2epkTDT

-- Where to find information about the latest SQL Server builds
-- https://bit.ly/2IGHbfY

-- Performance and Stability Related Fixes in Post-SQL Server 2016 SP1 Builds
-- https://bit.ly/2gr7k9L

-- Performance and Stability Related Fixes in Post-SQL Server 2016 SP2 Builds
-- https://bit.ly/2K3LoPf			

-- Announcing updates to the SQL Server Incremental Servicing Model (ISM)
-- https://bit.ly/1RzYITz

-- Update Center for Microsoft SQL Server
-- https://bit.ly/2pZptuQ

-- Download SQL Server Management Studio (SSMS)
-- https://bit.ly/1OcupT9

-- Download and install Microsoft SQL Operations Studio 
-- https://bit.ly/2vgke1A
	


-- Get socket, physical core and logical core count from the SQL Server Error log. (Query 2) (Core Counts)
-- This query might take a few seconds depending on the size of your error log
EXEC sys.xp_readerrorlog 0, 1, N'detected', N'socket';
------

-- This can help you determine the exact core counts used by SQL Server and whether HT is enabled or not
-- It can also help you confirm your SQL Server licensing model
-- Be on the lookout for this message "using 40 logical processors based on SQL Server licensing" 
-- (when you have more than 40 logical cores) which means grandfathered Server/CAL licensing
-- This query will return no results if your error log has been recycled since the instance was last started



-- Get selected server properties (Query 3) (Server Properties)
SELECT SERVERPROPERTY('MachineName') AS [MachineName], 
SERVERPROPERTY('ServerName') AS [ServerName],  
SERVERPROPERTY('InstanceName') AS [Instance], 
SERVERPROPERTY('IsClustered') AS [IsClustered], 
SERVERPROPERTY('ComputerNamePhysicalNetBIOS') AS [ComputerNamePhysicalNetBIOS], 
SERVERPROPERTY('Edition') AS [Edition], 
SERVERPROPERTY('ProductLevel') AS [ProductLevel],				-- What servicing branch (RTM/SP/CU)
SERVERPROPERTY('ProductUpdateLevel') AS [ProductUpdateLevel],	-- Within a servicing branch, what CU# is applied
SERVERPROPERTY('ProductVersion') AS [ProductVersion],
SERVERPROPERTY('ProductMajorVersion') AS [ProductMajorVersion], 
SERVERPROPERTY('ProductMinorVersion') AS [ProductMinorVersion], 
SERVERPROPERTY('ProductBuild') AS [ProductBuild], 
SERVERPROPERTY('ProductBuildType') AS [ProductBuildType],			  -- Is this a GDR or OD hotfix (NULL if on a CU build)
SERVERPROPERTY('ProductUpdateReference') AS [ProductUpdateReference], -- KB article number that is applicable for this build
SERVERPROPERTY('ProcessID') AS [ProcessID],
SERVERPROPERTY('Collation') AS [Collation], 
SERVERPROPERTY('IsFullTextInstalled') AS [IsFullTextInstalled], 
SERVERPROPERTY('IsIntegratedSecurityOnly') AS [IsIntegratedSecurityOnly],
SERVERPROPERTY('FilestreamConfiguredLevel') AS [FilestreamConfiguredLevel],
SERVERPROPERTY('IsHadrEnabled') AS [IsHadrEnabled], 
SERVERPROPERTY('HadrManagerStatus') AS [HadrManagerStatus],
SERVERPROPERTY('InstanceDefaultDataPath') AS [InstanceDefaultDataPath],
SERVERPROPERTY('InstanceDefaultLogPath') AS [InstanceDefaultLogPath],
SERVERPROPERTY('BuildClrVersion') AS [Build CLR Version],
SERVERPROPERTY('IsXTPSupported') AS [IsXTPSupported],
SERVERPROPERTY('IsPolybaseInstalled') AS [IsPolybaseInstalled],				-- New for SQL Server 2016
SERVERPROPERTY('IsAdvancedAnalyticsInstalled') AS [IsRServicesInstalled];	-- New for SQL Server 2016
------

-- This gives you a lot of useful information about your instance of SQL Server,
-- such as the ProcessID for SQL Server and your collation
-- Note: Some columns will be NULL on older SQL Server builds

-- SERVERPROPERTY (Transact-SQL)
-- https://bit.ly/2eeaXeI



-- Get instance-level configuration values for instance  (Query 4) (Configuration Values)
SELECT name, value, value_in_use, minimum, maximum, [description], is_dynamic, is_advanced
FROM sys.configurations WITH (NOLOCK)
ORDER BY name OPTION (RECOMPILE);
------

-- Focus on these settings:
-- automatic soft-NUMA disabled (should be 0 in most cases)
-- backup checksum default (should be 1)
-- backup compression default (should be 1 in most cases)
-- clr enabled (only enable if it is needed)
-- cost threshold for parallelism (depends on your workload)
-- lightweight pooling (should be zero)
-- max degree of parallelism (depends on your workload and hardware)
-- max server memory (MB) (set to an appropriate value, not the default)
-- optimize for ad hoc workloads (should be 1)
-- priority boost (should be zero)
-- remote admin connections (should be 1)

-- New configuration options for SQL Server 2016
-- allow polybase export (Allow INSERT into a Hadoop external table)
-- automatic soft-NUMA disabled (Automatic soft-NUMA is enabled by default)
-- external scripts enabled (Allows execution of external scripts, for R Services)
-- hadoop connectivity (Configure SQL Server to connect to external Hadoop or Microsoft Azure storage blob data sources through PolyBase)
-- polybase network encryption (Configure SQL Server to encrypt control and data channels when using PolyBase)
-- remote data archive (Allow the use of the REMOTE_DATA_ARCHIVE data access for Stretch databases)

-- SQLSweet16!, Episode 1: Backup Compression for TDE-enabled Databases
-- https://bit.ly/28Rpb2x



-- Returns a list of all global trace flags that are enabled (Query 5) (Global Trace Flags)
DBCC TRACESTATUS (-1);
------

-- If no global trace flags are enabled, no results will be returned.
-- It is very useful to know what global trace flags are currently enabled as part of the diagnostic process.

-- Common trace flags that should be enabled in most cases
-- TF 3226 - Supresses logging of successful database backup messages to the SQL Server Error Log
--           https://bit.ly/2p6MTjS

-- TF 6534 - Enables use of native code to improve performance with spatial data
--           https://bit.ly/2HrQUpU

-- The behavior of TF 1117, 1118 are enabled for tempdb in SQL Server 2016 by default
-- SQL 2016 � It Just Runs Faster: -T1117 and -T1118 changes for TEMPDB and user databases
-- https://bit.ly/2lbNWxK           

-- The behavior of TF 2371 is enabled by default in SQL Server 2016 and newer (in compat level 130 and higher)

-- DBCC TRACEON - Trace Flags (Transact-SQL)
-- https://bit.ly/2FuSvPg



-- Returns status of instant file initialization (Query 6) (IFI Status)
EXEC sys.xp_readerrorlog 0, 1, N'Database Instant File Initialization';
------

-- Lets you determine whether Instant File Initialization (IFI) is enabled for the instance
-- This should be enabled in the vast majority of cases
-- SQL Server 2016 lets you enable this during the SQL server installation process

-- Database Instant File Initialization
-- https://bit.ly/2nTX74y

-- Misconceptions around instant file initialization
-- https://bit.ly/2oBSKgZ



-- SQL Server Process Address space info  (Query 7) (Process Memory)
-- (shows whether locked pages is enabled, among other things)
SELECT physical_memory_in_use_kb/1024 AS [SQL Server Memory Usage (MB)],
	   locked_page_allocations_kb/1024 AS [SQL Server Locked Pages Allocation (MB)],
       large_page_allocations_kb/1024 AS [SQL Server Large Pages Allocation (MB)], 
	   page_fault_count, memory_utilization_percentage, available_commit_limit_kb, 
	   process_physical_memory_low, process_virtual_memory_low
FROM sys.dm_os_process_memory WITH (NOLOCK) OPTION (RECOMPILE);
------

-- You want to see 0 for process_physical_memory_low
-- You want to see 0 for process_virtual_memory_low
-- This indicates that you are not under internal memory pressure
-- If locked_page_allocations_kb > 0, then LPIM is enabled

-- How to enable the "locked pages" feature in SQL Server 2012
-- https://bit.ly/2F5UjOA

-- Memory Management Architecture Guide
-- https://bit.ly/2JKkadC 



-- SQL Server Services information (Query 8) (SQL Server Services Info)
SELECT servicename, process_id, startup_type_desc, status_desc, 
last_startup_time, service_account, is_clustered, cluster_nodename, [filename], 
instant_file_initialization_enabled -- New in SQL Server 2016 SP1
FROM sys.dm_server_services WITH (NOLOCK) OPTION (RECOMPILE);
------

-- Tells you the account being used for the SQL Server Service and the SQL Agent Service
-- Shows the process_id, when they were last started, and their current status
-- Also shows whether you are running on a failover cluster instance, and what node you are running on
-- Also shows whether IFI is enabled

-- sys.dm_server_services (Transact-SQL)
-- https://bit.ly/2oKa1Un


-- Last backup information by database  (Query 9) (Last Backup By Database)
SELECT ISNULL(d.[name], bs.[database_name]) AS [Database], d.recovery_model_desc AS [Recovery Model], 
       d.log_reuse_wait_desc AS [Log Reuse Wait Desc],
    MAX(CASE WHEN [type] = 'D' THEN bs.backup_finish_date ELSE NULL END) AS [Last Full Backup],
    MAX(CASE WHEN [type] = 'I' THEN bs.backup_finish_date ELSE NULL END) AS [Last Differential Backup],
    MAX(CASE WHEN [type] = 'L' THEN bs.backup_finish_date ELSE NULL END) AS [Last Log Backup]
FROM sys.databases AS d WITH (NOLOCK)
LEFT OUTER JOIN msdb.dbo.backupset AS bs WITH (NOLOCK)
ON bs.[database_name] = d.[name] 
AND bs.backup_finish_date > GETDATE()- 30
WHERE d.name <> N'tempdb'
GROUP BY ISNULL(d.[name], bs.[database_name]), d.recovery_model_desc, d.log_reuse_wait_desc, d.[name] 
ORDER BY d.recovery_model_desc, d.[name] OPTION (RECOMPILE);
------

-- This helps you spot runaway transaction logs and other issues with your backup schedule


-- Get SQL Server Agent jobs and Category information (Query 10) (SQL Server Agent Jobs)
SELECT sj.name AS [Job Name], sj.[description] AS [Job Description], SUSER_SNAME(sj.owner_sid) AS [Job Owner],
sj.date_created AS [Date Created], sj.[enabled] AS [Job Enabled], 
sj.notify_email_operator_id, sj.notify_level_email, sc.name AS [CategoryName],
s.[enabled] AS [Sched Enabled], js.next_run_date, js.next_run_time
FROM msdb.dbo.sysjobs AS sj WITH (NOLOCK)
INNER JOIN msdb.dbo.syscategories AS sc WITH (NOLOCK)
ON sj.category_id = sc.category_id
LEFT OUTER JOIN msdb.dbo.sysjobschedules AS js WITH (NOLOCK)
ON sj.job_id = js.job_id
LEFT OUTER JOIN msdb.dbo.sysschedules AS s WITH (NOLOCK)
ON js.schedule_id = s.schedule_id
ORDER BY sj.name OPTION (RECOMPILE);
------

-- Gives you some basic information about your SQL Server Agent jobs, who owns them and how they are configured
-- Look for Agent jobs that are not owned by sa
-- Look for jobs that have a notify_email_operator_id set to 0 (meaning no operator)
-- Look for jobs that have a notify_level_email set to 0 (meaning no e-mail is ever sent)
--
-- MSDN sysjobs documentation
-- https://bit.ly/2paDEOP 

-- SQL Server Maintenance Solution
-- https://bit.ly/1pgchQu  


-- Get SQL Server Agent Alert Information (Query 11) (SQL Server Agent Alerts)
SELECT name, event_source, message_id, severity, [enabled], has_notification, 
       delay_between_responses, occurrence_count, last_occurrence_date, last_occurrence_time
FROM msdb.dbo.sysalerts WITH (NOLOCK)
ORDER BY name OPTION (RECOMPILE);
------

-- Gives you some basic information about your SQL Server Agent Alerts 
-- (which are different from SQL Server Agent jobs)
-- Read more about Agent Alerts here: https://bit.ly/2Giz0Xf 



-- Windows information (Query 12) (Windows Info)
SELECT windows_release, windows_service_pack_level, 
       windows_sku, os_language_version
FROM sys.dm_os_windows_info WITH (NOLOCK) OPTION (RECOMPILE);
------

-- Gives you major OS version, Service Pack, Edition, and language info for the operating system
-- 10.0 is either Windows 10 or Windows Server 2016
-- 6.3 is either Windows 8.1 or Windows Server 2012 R2 
-- 6.2 is either Windows 8 or Windows Server 2012


-- Windows SKU codes
-- 4 is Enterprise Edition
-- 7 is Standard Server Edition
-- 8 is Datacenter Server Edition
-- 10 is Enterprise Server Edition
-- 48 is Professional Edition
-- 161 is Pro for Workstations

-- 1033 for os_language_version is US-English

-- SQL Server 2016 requires Windows Server 2012 or newer

-- Quick-Start Installation of SQL Server 2016
-- https://bit.ly/2qtxQ3G

-- Hardware and Software Requirements for Installing SQL Server 2016
-- https://bit.ly/2JJIUTl

-- Using SQL Server in Windows 8 and later versions of Windows operating system 
-- https://bit.ly/2F7Ax0P


-- SQL Server NUMA Node information  (Query 13) (SQL Server NUMA Info)
SELECT node_id, node_state_desc, memory_node_id, processor_group, online_scheduler_count, 
       idle_scheduler_count, active_worker_count, avg_load_balance, resource_monitor_state
FROM sys.dm_os_nodes WITH (NOLOCK) 
WHERE node_state_desc <> N'ONLINE DAC' OPTION (RECOMPILE);
------

-- Gives you some useful information about the composition and relative load on your NUMA nodes
-- You want to see an equal number of schedulers on each NUMA node
-- Watch out if SQL Server 2016 Standard Edition has been installed 
-- on a physical or virtual machine with more than four sockets or more than 24 physical cores

-- sys.dm_os_nodes (Transact-SQL)
-- https://bit.ly/2pn5Mw8

-- Balancing Your Available SQL Server Core Licenses Evenly Across NUMA Nodes
-- https://bit.ly/2vfC4Rq



-- Good basic information about OS memory amounts and state  (Query 14) (System Memory)
SELECT total_physical_memory_kb/1024 AS [Physical Memory (MB)], 
       available_physical_memory_kb/1024 AS [Available Memory (MB)], 
       total_page_file_kb/1024 AS [Total Page File (MB)], 
	   available_page_file_kb/1024 AS [Available Page File (MB)], 
	   system_cache_kb/1024 AS [System Cache (MB)],
       system_memory_state_desc AS [System Memory State]
FROM sys.dm_os_sys_memory WITH (NOLOCK) OPTION (RECOMPILE);
------

-- You want to see "Available physical memory is high" for System Memory State
-- This indicates that you are not under external memory pressure

-- Possible System Memory State values:
-- Available physical memory is high
-- Physical memory usage is steady
-- Available physical memory is low
-- Available physical memory is running low
-- Physical memory state is transitioning

-- sys.dm_os_sys_memory (Transact-SQL)
-- https://bit.ly/2pcV0xq



-- You can skip the next two queries if you know you don't have a clustered instance


-- Get information about your cluster nodes and their status  (Query 15) (Cluster Node Properties)
-- (if your database server is in a failover cluster)
SELECT NodeName, status_description, is_current_owner
FROM sys.dm_os_cluster_nodes WITH (NOLOCK) OPTION (RECOMPILE);
------

-- Knowing which node owns the cluster resources is critical
-- Especially when you are installing Windows or SQL Server updates
-- You will see no results if your instance is not clustered

-- Recommended hotfixes and updates for Windows Server 2012 R2-based failover clusters
-- https://bit.ly/1z5BfCw


-- Get information about any AlwaysOn AG cluster this instance is a part of (Query 16) (AlwaysOn AG Cluster)
SELECT cluster_name, quorum_type_desc, quorum_state_desc
FROM sys.dm_hadr_cluster WITH (NOLOCK) OPTION (RECOMPILE);
------

-- You will see no results if your instance is not using AlwaysOn AGs


-- Good overview of AG health and status (Query 17) (AlwaysOn AG Status)
SELECT ag.name AS [AG Name], ar.replica_server_name, ar.availability_mode_desc, adc.[database_name], 
       drs.is_local, drs.is_primary_replica, drs.synchronization_state_desc, drs.is_commit_participant, 
	   drs.synchronization_health_desc, drs.recovery_lsn, drs.truncation_lsn, drs.last_sent_lsn, 
	   drs.last_sent_time, drs.last_received_lsn, drs.last_received_time, drs.last_hardened_lsn, 
	   drs.last_hardened_time, drs.last_redone_lsn, drs.last_redone_time, drs.log_send_queue_size, 
	   drs.log_send_rate, drs.redo_queue_size, drs.redo_rate, drs.filestream_send_rate, 
	   drs.end_of_log_lsn, drs.last_commit_lsn, drs.last_commit_time, drs.database_state_desc 
FROM sys.dm_hadr_database_replica_states AS drs WITH (NOLOCK)
INNER JOIN sys.availability_databases_cluster AS adc WITH (NOLOCK)
ON drs.group_id = adc.group_id 
AND drs.group_database_id = adc.group_database_id
INNER JOIN sys.availability_groups AS ag WITH (NOLOCK)
ON ag.group_id = drs.group_id
INNER JOIN sys.availability_replicas AS ar WITH (NOLOCK)
ON drs.group_id = ar.group_id 
AND drs.replica_id = ar.replica_id
ORDER BY ag.name, ar.replica_server_name, adc.[database_name] OPTION (RECOMPILE);

-- You will see no results if your instance is not using AlwaysOn AGs

-- SQL Server 2016 � It Just Runs Faster: Always On Availability Groups Turbocharged
-- https://bit.ly/2dn1H6r


-- Hardware information from SQL Server 2016  (Query 18) (Hardware Info)
SELECT cpu_count AS [Logical CPU Count], scheduler_count, 
       hyperthread_ratio AS [Hyperthread Ratio],
       cpu_count/hyperthread_ratio AS [Physical CPU Count], 
       physical_memory_kb/1024 AS [Physical Memory (MB)], 
	   committed_kb/1024 AS [Committed Memory (MB)],
       committed_target_kb/1024 AS [Committed Target Memory (MB)],
       max_workers_count AS [Max Workers Count], 
	   affinity_type_desc AS [Affinity Type], 
       sqlserver_start_time AS [SQL Server Start Time], 
	   virtual_machine_type_desc AS [Virtual Machine Type],
	   softnuma_configuration_desc AS [Soft NUMA Configuration], 
	   sql_memory_model_desc -- New in SQL Server 2016
FROM sys.dm_os_sys_info WITH (NOLOCK) OPTION (RECOMPILE);
------

-- Gives you some good basic hardware information about your database server
-- Note: virtual_machine_type_desc of HYPERVISOR does not automatically mean you are running SQL Server inside of a VM
-- It merely indicates that you have a hypervisor running on your host

-- sys.dm_os_sys_info (Transact-SQL)
-- https://bit.ly/2pczOYs

-- Soft NUMA configuration was a new column for SQL Server 2016
-- OFF = Soft-NUMA feature is OFF
-- ON = SQL Server automatically determines the NUMA node sizes for Soft-NUMA
-- MANUAL = Manually configured soft-NUMA

-- Configure SQL Server to Use Soft-NUMA (SQL Server)
-- https://bit.ly/2HTpKJt

-- sql_memory_model_desc values (Added in SQL Server 2016 SP1)
-- CONVENTIONAL
-- LOCK_PAGES
-- LARGE_PAGES
   

-- Get System Manufacturer and model number from SQL Server Error log (Query 19) (System Manufacturer)
EXEC sys.xp_readerrorlog 0, 1, N'Manufacturer';
------ 

-- This can help you determine the capabilities and capacities of your database server
-- Can also be used to confirm if you are running in a VM
-- This query might take a few seconds if you have not recycled your error log recently
-- This query will return no results if your error log has been recycled since the instance was started


-- Get BIOS date from Windows Registry (Query 20) (BIOS Date)
EXEC sys.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'HARDWARE\DESCRIPTION\System\BIOS', N'BiosReleaseDate';
------

-- Helps you understand whether the main system BIOS is up to date, and the possible age of the hardware
-- Not as useful for virtualization


-- Get processor description from Windows Registry  (Query 21) (Processor Description)
EXEC sys.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'HARDWARE\DESCRIPTION\System\CentralProcessor\0', N'ProcessorNameString';
------

-- Gives you the model number and rated clock speed of your processor(s)
-- Your processors may be running at less than the rated clock speed due
-- to the Windows Power Plan or hardware power management

-- You can use CPU-Z to get your actual CPU core speed and a lot of other useful information
-- https://bit.ly/QhR6xF

-- You can learn more about processor selection for SQL Server by following this link
-- https://bit.ly/2F3aVlP



-- See if buffer pool extension (BPE) is enabled (Query 22) (BPE Configuration)
SELECT [path], state_description, current_size_in_kb, 
CAST(current_size_in_kb/1048576.0 AS DECIMAL(10,2)) AS [Size (GB)]
FROM sys.dm_os_buffer_pool_extension_configuration WITH (NOLOCK) OPTION (RECOMPILE);
------

-- BPE is available in both Standard Edition and Enterprise Edition
-- It is a more interesting feature for Standard Edition

-- Buffer Pool Extension to SSDs in SQL Server 2014
-- https://bit.ly/1bm08m8

-- Buffer Pool Extension
-- https://bit.ly/2oBuieO



-- Look at buffer descriptors to see BPE usage by database (Query 23) (BPE Usage) 
SELECT DB_NAME(database_id) AS [Database Name], COUNT(page_id) AS [Page Count],
CAST(COUNT(*)/128.0 AS DECIMAL(10, 2)) AS [Buffer size(MB)], 
AVG(read_microsec) AS [Avg Read Time (microseconds)]
FROM sys.dm_os_buffer_descriptors WITH (NOLOCK)
WHERE database_id <> 32767
AND is_in_bpool_extension = 1
GROUP BY DB_NAME(database_id) 
ORDER BY [Buffer size(MB)] DESC OPTION (RECOMPILE);
------

-- You will see no results if BPE is not enabled or if there is no BPE usage


-- Get information on location, time and size of any memory dumps from SQL Server  (Query 24) (Memory Dump Info)
SELECT [filename], creation_time, size_in_bytes/1048576.0 AS [Size (MB)]
FROM sys.dm_server_memory_dumps WITH (NOLOCK) 
ORDER BY creation_time DESC OPTION (RECOMPILE);
------

-- This will not return any rows if you have 
-- not had any memory dumps (which is a good thing)

-- sys.dm_server_memory_dumps (Transact-SQL)
-- https://bit.ly/2elwWll



-- Look at Suspect Pages table (Query 25) (Suspect Pages)
SELECT DB_NAME(database_id) AS [Database Name], [file_id], page_id, 
       event_type, error_count, last_update_date 
FROM msdb.dbo.suspect_pages WITH (NOLOCK)
ORDER BY database_id OPTION (RECOMPILE);
------

-- event_type value descriptions
-- 1 = 823 error caused by an operating system CRC error
--     or 824 error other than a bad checksum or a torn page (for example, a bad page ID)
-- 2 = Bad checksum
-- 3 = Torn page
-- 4 = Restored (The page was restored after it was marked bad)
-- 5 = Repaired (DBCC repaired the page)
-- 7 = Deallocated by DBCC

-- Ideally, this query returns no results. The table is limited to 1000 rows.
-- If you do get results here, you should do further investigation to determine the root cause

-- Manage the suspect_pages Table
-- https://bit.ly/2Fvr1c9


-- Get number of data files in tempdb database (Query 26) (TempDB Data Files)
EXEC sys.xp_readerrorlog 0, 1, N'The tempdb database has';
------

-- Get the number of data files in the tempdb database
-- 4-8 data files that are all the same size is a good starting point
-- This query will return no results if your error log has been recycled since the instance was last started


-- File names and paths for all user and system databases on instance  (Query 27) (Database Filenames and Paths)
SELECT DB_NAME([database_id]) AS [Database Name], 
       [file_id], [name], physical_name, [type_desc], state_desc,
	   is_percent_growth, growth,
	   CONVERT(bigint, growth/128.0) AS [Growth in MB], 
       CONVERT(bigint, size/128.0) AS [Total Size in MB]
FROM sys.master_files WITH (NOLOCK)
ORDER BY DB_NAME([database_id]), [file_id] OPTION (RECOMPILE);
------

-- Things to look at:
-- Are data files and log files on different drives?
-- Is everything on the C: drive?
-- Is tempdb on dedicated drives?
-- Is there only one tempdb data file?
-- Are all of the tempdb data files the same size?
-- Are there multiple data files for user databases?
-- Is percent growth enabled for any files (which is bad)?


-- Volume info for all LUNS that have database files on the current instance (Query 28) (Volume Info)
SELECT DISTINCT vs.volume_mount_point, vs.file_system_type, vs.logical_volume_name, 
CONVERT(DECIMAL(18,2), vs.total_bytes/1073741824.0) AS [Total Size (GB)],
CONVERT(DECIMAL(18,2), vs.available_bytes/1073741824.0) AS [Available Size (GB)],  
CONVERT(DECIMAL(18,2), vs.available_bytes * 1. / vs.total_bytes * 100.) AS [Space Free %],
vs.supports_compression, vs.is_compressed, 
vs.supports_sparse_files, vs.supports_alternate_streams
FROM sys.master_files AS f WITH (NOLOCK)
CROSS APPLY sys.dm_os_volume_stats(f.database_id, f.[file_id]) AS vs 
ORDER BY vs.volume_mount_point OPTION (RECOMPILE);
------

-- Shows you the total and free space on the LUNs where you have database files
-- Being low on free space can negatively affect performance

-- sys.dm_os_volume_stats (Transact-SQL)
-- https://bit.ly/2oBPNNr



-- Drive level latency information (Query 29) (Drive Level Latency)
-- Based on code from Jimmy May
SELECT tab.[Drive], tab.volume_mount_point AS [Volume Mount Point], 
	CASE 
		WHEN num_of_reads = 0 THEN 0 
		ELSE (io_stall_read_ms/num_of_reads) 
	END AS [Read Latency],
	CASE 
		WHEN num_of_writes = 0 THEN 0 
		ELSE (io_stall_write_ms/num_of_writes) 
	END AS [Write Latency],
	CASE 
		WHEN (num_of_reads = 0 AND num_of_writes = 0) THEN 0 
		ELSE (io_stall/(num_of_reads + num_of_writes)) 
	END AS [Overall Latency],
	CASE 
		WHEN num_of_reads = 0 THEN 0 
		ELSE (num_of_bytes_read/num_of_reads) 
	END AS [Avg Bytes/Read],
	CASE 
		WHEN num_of_writes = 0 THEN 0 
		ELSE (num_of_bytes_written/num_of_writes) 
	END AS [Avg Bytes/Write],
	CASE 
		WHEN (num_of_reads = 0 AND num_of_writes = 0) THEN 0 
		ELSE ((num_of_bytes_read + num_of_bytes_written)/(num_of_reads + num_of_writes)) 
	END AS [Avg Bytes/Transfer]
FROM (SELECT LEFT(UPPER(mf.physical_name), 2) AS Drive, SUM(num_of_reads) AS num_of_reads,
	         SUM(io_stall_read_ms) AS io_stall_read_ms, SUM(num_of_writes) AS num_of_writes,
	         SUM(io_stall_write_ms) AS io_stall_write_ms, SUM(num_of_bytes_read) AS num_of_bytes_read,
	         SUM(num_of_bytes_written) AS num_of_bytes_written, SUM(io_stall) AS io_stall, vs.volume_mount_point 
      FROM sys.dm_io_virtual_file_stats(NULL, NULL) AS vfs
      INNER JOIN sys.master_files AS mf WITH (NOLOCK)
      ON vfs.database_id = mf.database_id AND vfs.file_id = mf.file_id
	  CROSS APPLY sys.dm_os_volume_stats(mf.database_id, mf.[file_id]) AS vs 
      GROUP BY LEFT(UPPER(mf.physical_name), 2), vs.volume_mount_point) AS tab
ORDER BY [Overall Latency] OPTION (RECOMPILE);
------

-- Shows you the drive-level latency for reads and writes, in milliseconds
-- Latency above 30-40ms is usually a problem
-- These latency numbers include all file activity against all SQL Server 
-- database files on each drive since SQL Server was last started


-- Calculates average stalls per read, per write, and per total input/output for each database file  (Query 30) (IO Stalls by File)
SELECT DB_NAME(fs.database_id) AS [Database Name], CAST(fs.io_stall_read_ms/(1.0 + fs.num_of_reads) AS NUMERIC(10,1)) AS [avg_read_stall_ms],
CAST(fs.io_stall_write_ms/(1.0 + fs.num_of_writes) AS NUMERIC(10,1)) AS [avg_write_stall_ms],
CAST((fs.io_stall_read_ms + fs.io_stall_write_ms)/(1.0 + fs.num_of_reads + fs.num_of_writes) AS NUMERIC(10,1)) AS [avg_io_stall_ms],
CONVERT(DECIMAL(18,2), mf.size/128.0) AS [File Size (MB)], mf.physical_name, mf.type_desc, fs.io_stall_read_ms, fs.num_of_reads, 
fs.io_stall_write_ms, fs.num_of_writes, fs.io_stall_read_ms + fs.io_stall_write_ms AS [io_stalls], fs.num_of_reads + fs.num_of_writes AS [total_io],
io_stall_queued_read_ms AS [Resource Governor Total Read IO Latency (ms)], io_stall_queued_write_ms AS [Resource Governor Total Write IO Latency (ms)] 
FROM sys.dm_io_virtual_file_stats(null,null) AS fs
INNER JOIN sys.master_files AS mf WITH (NOLOCK)
ON fs.database_id = mf.database_id
AND fs.[file_id] = mf.[file_id]
ORDER BY avg_io_stall_ms DESC OPTION (RECOMPILE);
------

-- Helps determine which database files on the entire instance have the most I/O bottlenecks
-- This can help you decide whether certain LUNs are overloaded and whether you might
-- want to move some files to a different location or perhaps improve your I/O performance
-- These latency numbers include all file activity against each SQL Server 
-- database file since SQL Server was last started


-- Look for I/O requests taking longer than 15 seconds in the six most recent SQL Server Error Logs (Query 31) (IO Warnings)
CREATE TABLE #IOWarningResults(LogDate datetime, ProcessInfo sysname, LogText nvarchar(1000));

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 0, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 1, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 2, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 3, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 4, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 5, 1, N'taking longer than 15 seconds';

SELECT LogDate, ProcessInfo, LogText
FROM #IOWarningResults
ORDER BY LogDate DESC;

DROP TABLE #IOWarningResults;
------  

-- Finding 15 second I/O warnings in the SQL Server Error Log is useful evidence of
-- poor I/O performance (which might have many different causes)
-- Look to see if you see any patterns in the results (same files, same drives, same time of day, etc.)

-- Diagnostics in SQL Server help detect stalled and stuck I/O operations
-- https://bit.ly/2qtaw73



-- Recovery model, log reuse wait description, log file size, log usage size  (Query 32) (Database Properties)
-- and compatibility level for all databases on instance
SELECT db.[name] AS [Database Name], SUSER_SNAME(db.owner_sid) AS [Database Owner], db.recovery_model_desc AS [Recovery Model], 
db.state_desc, db.containment_desc, db.log_reuse_wait_desc AS [Log Reuse Wait Description], 
CONVERT(DECIMAL(18,2), ls.cntr_value/1024.0) AS [Log Size (MB)], CONVERT(DECIMAL(18,2), lu.cntr_value/1024.0) AS [Log Used (MB)],
CAST(CAST(lu.cntr_value AS FLOAT) / CAST(ls.cntr_value AS FLOAT)AS DECIMAL(18,2)) * 100 AS [Log Used %], 
db.[compatibility_level] AS [DB Compatibility Level], 
db.is_mixed_page_allocation_on, db.page_verify_option_desc AS [Page Verify Option], 
db.is_auto_create_stats_on, db.is_auto_update_stats_on, db.is_auto_update_stats_async_on, db.is_parameterization_forced, 
db.snapshot_isolation_state_desc, db.is_read_committed_snapshot_on, db.is_auto_close_on, db.is_auto_shrink_on, 
db.target_recovery_time_in_seconds, db.is_cdc_enabled, db.is_published, db.is_distributor, db.is_encrypted,
db.group_database_id, db.replica_id,db.is_memory_optimized_elevate_to_snapshot_on, 
db.delayed_durability_desc, db.is_auto_create_stats_incremental_on,
db.is_query_store_on, db.is_sync_with_backup, 
db.is_supplemental_logging_enabled, db.is_remote_data_archive_enabled,
db.is_encrypted, de.encryption_state, de.percent_complete, de.key_algorithm, de.key_length      
FROM sys.databases AS db WITH (NOLOCK)
INNER JOIN sys.dm_os_performance_counters AS lu WITH (NOLOCK)
ON db.name = lu.instance_name
INNER JOIN sys.dm_os_performance_counters AS ls WITH (NOLOCK)
ON db.name = ls.instance_name
LEFT OUTER JOIN sys.dm_database_encryption_keys AS de WITH (NOLOCK)
ON db.database_id = de.database_id
WHERE lu.counter_name LIKE N'Log File(s) Used Size (KB)%' 
AND ls.counter_name LIKE N'Log File(s) Size (KB)%'
AND ls.cntr_value > 0 
ORDER BY db.[name] OPTION (RECOMPILE);
------

-- Things to look at:
-- How many databases are on the instance?
-- What recovery models are they using?
-- What is the log reuse wait description?
-- How full are the transaction logs?
-- What compatibility level are the databases on? 
-- What is the Page Verify Option? (should be CHECKSUM)
-- Is Auto Update Statistics Asynchronously enabled?
-- Is Delayed Durability enabled
-- Make sure auto_shrink and auto_close are not enabled!

-- is_mixed_page_allocation_on is a new property for SQL Server 2016. Equivalent to TF 1118 for a user database
-- SQL Server 2016: Changes in default behavior for autogrow and allocations for tempdb and user databases
-- https://bit.ly/2evRZSR

-- A non-zero value for target_recovery_time_in_seconds means that indirect checkpoint is enabled 
-- If the setting has a zero value it indicates that automatic checkpoint is enabled

-- Changes in SQL Server 2016 Checkpoint Behavior
-- https://bit.ly/2pdggk3


-- Missing Indexes for all databases by Index Advantage  (Query 33) (Missing Indexes All Databases)
SELECT CONVERT(decimal(18,2), user_seeks * avg_total_user_cost * (avg_user_impact * 0.01)) AS [index_advantage],
FORMAT(migs.last_user_seek, 'yyyy-MM-dd HH:mm:ss') AS [last_user_seek], 
mid.[statement] AS [Database.Schema.Table],
COUNT(1) OVER(PARTITION BY mid.[statement]) AS [missing_indexes_for_table],
COUNT(1) OVER(PARTITION BY mid.[statement], equality_columns) AS [similar_missing_indexes_for_table],
mid.equality_columns, mid.inequality_columns, mid.included_columns,
migs.unique_compiles, migs.user_seeks, 
CONVERT(decimal(18,2), migs.avg_total_user_cost) AS [avg_total_user_cost], migs.avg_user_impact 
FROM sys.dm_db_missing_index_group_stats AS migs WITH (NOLOCK)
INNER JOIN sys.dm_db_missing_index_groups AS mig WITH (NOLOCK)
ON migs.group_handle = mig.index_group_handle
INNER JOIN sys.dm_db_missing_index_details AS mid WITH (NOLOCK)
ON mig.index_handle = mid.index_handle
ORDER BY index_advantage DESC OPTION (RECOMPILE);
------

-- Getting missing index information for all of the databases on the instance is very useful
-- Look at last user seek time, number of user seeks to help determine source and importance
-- Also look at avg_user_impact and avg_total_user_cost to help determine importance
-- SQL Server is overly eager to add included columns, so beware
-- Do not just blindly add indexes that show up from this query!!!

-- SQL Server Index Design Guide
-- https://bit.ly/2qtZr4N



-- Get VLF Counts for all databases on the instance (Query 34) (VLF Counts)
-- (adapted from Michelle Ufford) 
CREATE TABLE #VLFInfo (RecoveryUnitID int, FileID  int,
					   FileSize bigint, StartOffset bigint,
					   FSeqNo      bigint, [Status]    bigint,
					   Parity      bigint, CreateLSN   numeric(38));
	 
CREATE TABLE #VLFCountResults(DatabaseName sysname, VLFCount int);
	 
EXEC sp_MSforeachdb N'Use [?]; 

				INSERT INTO #VLFInfo 
				EXEC sp_executesql N''DBCC LOGINFO([?])''; 
	 
				INSERT INTO #VLFCountResults 
				SELECT DB_NAME(), COUNT(*) 
				FROM #VLFInfo; 

				TRUNCATE TABLE #VLFInfo;'
	 
SELECT DatabaseName, VLFCount  
FROM #VLFCountResults
ORDER BY VLFCount DESC;
	 
DROP TABLE #VLFInfo;
DROP TABLE #VLFCountResults;
------

-- High VLF counts can affect write performance to the log file
-- and they can make full database restores and crash recovery take much longer
-- Try to keep your VLF counts under 200 in most cases (depending on log file size)

-- Important change to VLF creation algorithm in SQL Server 2014
-- https://bit.ly/2Hsjbg4

-- SQL Server Transaction Log Architecture and Management Guide
-- https://bit.ly/2JjmQRZ




-- Get CPU utilization by database (Query 35) (CPU Usage by Database)
WITH DB_CPU_Stats
AS
(SELECT pa.DatabaseID, DB_Name(pa.DatabaseID) AS [Database Name], SUM(qs.total_worker_time/1000) AS [CPU_Time_Ms]
 FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
 CROSS APPLY (SELECT CONVERT(int, value) AS [DatabaseID] 
              FROM sys.dm_exec_plan_attributes(qs.plan_handle)
              WHERE attribute = N'dbid') AS pa
 GROUP BY DatabaseID)
SELECT ROW_NUMBER() OVER(ORDER BY [CPU_Time_Ms] DESC) AS [CPU Rank],
       [Database Name], [CPU_Time_Ms] AS [CPU Time (ms)], 
       CAST([CPU_Time_Ms] * 1.0 / SUM([CPU_Time_Ms]) OVER() * 100.0 AS DECIMAL(5, 2)) AS [CPU Percent]
FROM DB_CPU_Stats
WHERE DatabaseID <> 32767 -- ResourceDB
ORDER BY [CPU Rank] OPTION (RECOMPILE);
------

-- Helps determine which database is using the most CPU resources on the instance


-- Get I/O utilization by database (Query 36) (IO Usage By Database)
WITH Aggregate_IO_Statistics
AS
(SELECT DB_NAME(database_id) AS [Database Name],
CAST(SUM(num_of_bytes_read + num_of_bytes_written)/1048576 AS DECIMAL(12, 2)) AS io_in_mb
FROM sys.dm_io_virtual_file_stats(NULL, NULL) AS [DM_IO_STATS]
GROUP BY database_id)
SELECT ROW_NUMBER() OVER(ORDER BY io_in_mb DESC) AS [I/O Rank], [Database Name], io_in_mb AS [Total I/O (MB)],
       CAST(io_in_mb/ SUM(io_in_mb) OVER() * 100.0 AS DECIMAL(5,2)) AS [I/O Percent]
FROM Aggregate_IO_Statistics
ORDER BY [I/O Rank] OPTION (RECOMPILE);
------

-- Helps determine which database is using the most I/O resources on the instance


-- Get total buffer usage by database for current instance  (Query 37) (Total Buffer Usage by Database)
-- This make take some time to run on a busy instance
WITH AggregateBufferPoolUsage
AS
(SELECT DB_NAME(database_id) AS [Database Name],
CAST(COUNT(*) * 8/1024.0 AS DECIMAL (10,2))  AS [CachedSize]
FROM sys.dm_os_buffer_descriptors WITH (NOLOCK)
WHERE database_id <> 32767 -- ResourceDB
GROUP BY DB_NAME(database_id))
SELECT ROW_NUMBER() OVER(ORDER BY CachedSize DESC) AS [Buffer Pool Rank], [Database Name], CachedSize AS [Cached Size (MB)],
       CAST(CachedSize / SUM(CachedSize) OVER() * 100.0 AS DECIMAL(5,2)) AS [Buffer Pool Percent]
FROM AggregateBufferPoolUsage
ORDER BY [Buffer Pool Rank] OPTION (RECOMPILE);
------

-- Tells you how much memory (in the buffer pool) 
-- is being used by each database on the instance


-- Clear Wait Stats with this command
-- DBCC SQLPERF('sys.dm_os_wait_stats', CLEAR);

-- Isolate top waits for server instance since last restart or wait statistics clear  (Query 38) (Top Waits)
WITH [Waits] 
AS (SELECT wait_type, wait_time_ms/ 1000.0 AS [WaitS],
          (wait_time_ms - signal_wait_time_ms) / 1000.0 AS [ResourceS],
           signal_wait_time_ms / 1000.0 AS [SignalS],
           waiting_tasks_count AS [WaitCount],
           100.0 *  wait_time_ms / SUM (wait_time_ms) OVER() AS [Percentage],
           ROW_NUMBER() OVER(ORDER BY wait_time_ms DESC) AS [RowNum]
    FROM sys.dm_os_wait_stats WITH (NOLOCK)
    WHERE [wait_type] NOT IN (
        N'BROKER_EVENTHANDLER', N'BROKER_RECEIVE_WAITFOR', N'BROKER_TASK_STOP',
		N'BROKER_TO_FLUSH', N'BROKER_TRANSMITTER', N'CHECKPOINT_QUEUE',
        N'CHKPT', N'CLR_AUTO_EVENT', N'CLR_MANUAL_EVENT', N'CLR_SEMAPHORE',
        N'DBMIRROR_DBM_EVENT', N'DBMIRROR_EVENTS_QUEUE', N'DBMIRROR_WORKER_QUEUE',
		N'DBMIRRORING_CMD', N'DIRTY_PAGE_POLL', N'DISPATCHER_QUEUE_SEMAPHORE',
        N'EXECSYNC', N'FSAGENT', N'FT_IFTS_SCHEDULER_IDLE_WAIT', N'FT_IFTSHC_MUTEX',
        N'HADR_CLUSAPI_CALL', N'HADR_FILESTREAM_IOMGR_IOCOMPLETION', N'HADR_LOGCAPTURE_WAIT', 
		N'HADR_NOTIFICATION_DEQUEUE', N'HADR_TIMER_TASK', N'HADR_WORK_QUEUE',
        N'KSOURCE_WAKEUP', N'LAZYWRITER_SLEEP', N'LOGMGR_QUEUE', 
		N'MEMORY_ALLOCATION_EXT', N'ONDEMAND_TASK_QUEUE',
		N'PARALLEL_REDO_DRAIN_WORKER', N'PARALLEL_REDO_LOG_CACHE', N'PARALLEL_REDO_TRAN_LIST',
		N'PARALLEL_REDO_WORKER_SYNC', N'PARALLEL_REDO_WORKER_WAIT_WORK',
		N'PREEMPTIVE_HADR_LEASE_MECHANISM', N'PREEMPTIVE_SP_SERVER_DIAGNOSTICS',
		N'PREEMPTIVE_OS_LIBRARYOPS', N'PREEMPTIVE_OS_COMOPS', N'PREEMPTIVE_OS_CRYPTOPS',
		N'PREEMPTIVE_OS_PIPEOPS', N'PREEMPTIVE_OS_AUTHENTICATIONOPS',
		N'PREEMPTIVE_OS_GENERICOPS', N'PREEMPTIVE_OS_VERIFYTRUST',
		N'PREEMPTIVE_OS_FILEOPS', N'PREEMPTIVE_OS_DEVICEOPS', N'PREEMPTIVE_OS_QUERYREGISTRY',
		N'PREEMPTIVE_OS_WRITEFILE',
		N'PREEMPTIVE_XE_CALLBACKEXECUTE', N'PREEMPTIVE_XE_DISPATCHER',
		N'PREEMPTIVE_XE_GETTARGETSTATE', N'PREEMPTIVE_XE_SESSIONCOMMIT',
		N'PREEMPTIVE_XE_TARGETINIT', N'PREEMPTIVE_XE_TARGETFINALIZE',
        N'PWAIT_ALL_COMPONENTS_INITIALIZED', N'PWAIT_DIRECTLOGCONSUMER_GETNEXT',
		N'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP',
		N'QDS_ASYNC_QUEUE',
        N'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP', N'REQUEST_FOR_DEADLOCK_SEARCH',
		N'RESOURCE_QUEUE', N'SERVER_IDLE_CHECK', N'SLEEP_BPOOL_FLUSH', N'SLEEP_DBSTARTUP',
		N'SLEEP_DCOMSTARTUP', N'SLEEP_MASTERDBREADY', N'SLEEP_MASTERMDREADY',
        N'SLEEP_MASTERUPGRADED', N'SLEEP_MSDBSTARTUP', N'SLEEP_SYSTEMTASK', N'SLEEP_TASK',
        N'SLEEP_TEMPDBSTARTUP', N'SNI_HTTP_ACCEPT', N'SP_SERVER_DIAGNOSTICS_SLEEP',
		N'SQLTRACE_BUFFER_FLUSH', N'SQLTRACE_INCREMENTAL_FLUSH_SLEEP', N'SQLTRACE_WAIT_ENTRIES',
		N'WAIT_FOR_RESULTS', N'WAITFOR', N'WAITFOR_TASKSHUTDOWN', N'WAIT_XTP_HOST_WAIT',
		N'WAIT_XTP_OFFLINE_CKPT_NEW_LOG', N'WAIT_XTP_CKPT_CLOSE', N'WAIT_XTP_RECOVERY',
		N'XE_BUFFERMGR_ALLPROCESSED_EVENT', N'XE_DISPATCHER_JOIN',
        N'XE_DISPATCHER_WAIT', N'XE_LIVE_TARGET_TVF', N'XE_TIMER_EVENT')
    AND waiting_tasks_count > 0)
SELECT
    MAX (W1.wait_type) AS [WaitType],
	CAST (MAX (W1.Percentage) AS DECIMAL (5,2)) AS [Wait Percentage],
	CAST ((MAX (W1.WaitS) / MAX (W1.WaitCount)) AS DECIMAL (16,4)) AS [AvgWait_Sec],
    CAST ((MAX (W1.ResourceS) / MAX (W1.WaitCount)) AS DECIMAL (16,4)) AS [AvgRes_Sec],
    CAST ((MAX (W1.SignalS) / MAX (W1.WaitCount)) AS DECIMAL (16,4)) AS [AvgSig_Sec], 
    CAST (MAX (W1.WaitS) AS DECIMAL (16,2)) AS [Wait_Sec],
    CAST (MAX (W1.ResourceS) AS DECIMAL (16,2)) AS [Resource_Sec],
    CAST (MAX (W1.SignalS) AS DECIMAL (16,2)) AS [Signal_Sec],
    MAX (W1.WaitCount) AS [Wait Count],
	CAST (N'https://www.sqlskills.com/help/waits/' + W1.wait_type AS XML) AS [Help/Info URL]
FROM Waits AS W1
INNER JOIN Waits AS W2
ON W2.RowNum <= W1.RowNum
GROUP BY W1.RowNum, W1.wait_type
HAVING SUM (W2.Percentage) - MAX (W1.Percentage) < 99 -- percentage threshold
OPTION (RECOMPILE);
------

-- Cumulative wait stats are not as useful on an idle instance that is not under load or performance pressure

-- SQL Server Wait Types Library (Paul Randal)
-- https://bit.ly/2ePzYO2

-- The SQL Server Wait Type Repository
-- https://bit.ly/1afzfjC

-- Wait statistics, or please tell me where it hurts
-- https://bit.ly/2wsQHQE

-- SQL Server 2005 Performance Tuning using the Waits and Queues
-- https://bit.ly/1o2NFoF

-- sys.dm_os_wait_stats (Transact-SQL)
-- https://bit.ly/2Hjq9Yl



-- Get a count of SQL connections by IP address (Query 39) (Connection Counts by IP Address)
SELECT ec.client_net_address, es.[program_name], es.[host_name], es.login_name, 
COUNT(ec.session_id) AS [connection count] 
FROM sys.dm_exec_sessions AS es WITH (NOLOCK) 
INNER JOIN sys.dm_exec_connections AS ec WITH (NOLOCK) 
ON es.session_id = ec.session_id 
GROUP BY ec.client_net_address, es.[program_name], es.[host_name], es.login_name  
ORDER BY ec.client_net_address, es.[program_name] OPTION (RECOMPILE);
------

-- This helps you figure where your database load is coming from
-- and verifies connectivity from other machines

-- Solving Connectivity errors to SQL Server
-- https://bit.ly/2EgzoD0



-- Get Average Task Counts (run multiple times)  (Query 40) (Avg Task Counts)
SELECT AVG(current_tasks_count) AS [Avg Task Count], 
AVG(work_queue_count) AS [Avg Work Queue Count],
AVG(runnable_tasks_count) AS [Avg Runnable Task Count],
AVG(pending_disk_io_count) AS [Avg Pending DiskIO Count]
FROM sys.dm_os_schedulers WITH (NOLOCK)
WHERE scheduler_id < 255 OPTION (RECOMPILE);
------

-- Sustained values above 10 suggest further investigation in that area
-- High Avg Task Counts are often caused by blocking/deadlocking or other resource contention

-- Sustained values above 1 suggest further investigation in that area
-- High Avg Runnable Task Counts are a good sign of CPU pressure
-- High Avg Pending DiskIO Counts are a sign of disk pressure

-- How to Do Some Very Basic SQL Server Monitoring
-- https://bit.ly/2q3Btgt



-- Detect blocking (run multiple times)  (Query 41) (Detect Blocking)
SELECT t1.resource_type AS [lock type], DB_NAME(resource_database_id) AS [database],
t1.resource_associated_entity_id AS [blk object],t1.request_mode AS [lock req],  -- lock requested
t1.request_session_id AS [waiter sid], t2.wait_duration_ms AS [wait time],       -- spid of waiter  
(SELECT [text] FROM sys.dm_exec_requests AS r WITH (NOLOCK)                      -- get sql for waiter
CROSS APPLY sys.dm_exec_sql_text(r.[sql_handle]) 
WHERE r.session_id = t1.request_session_id) AS [waiter_batch],
(SELECT SUBSTRING(qt.[text],r.statement_start_offset/2, 
    (CASE WHEN r.statement_end_offset = -1 
    THEN LEN(CONVERT(nvarchar(max), qt.[text])) * 2 
    ELSE r.statement_end_offset END - r.statement_start_offset)/2) 
FROM sys.dm_exec_requests AS r WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(r.[sql_handle]) AS qt
WHERE r.session_id = t1.request_session_id) AS [waiter_stmt],					-- statement blocked
t2.blocking_session_id AS [blocker sid],										-- spid of blocker
(SELECT [text] FROM sys.sysprocesses AS p										-- get sql for blocker
CROSS APPLY sys.dm_exec_sql_text(p.[sql_handle]) 
WHERE p.spid = t2.blocking_session_id) AS [blocker_batch]
FROM sys.dm_tran_locks AS t1 WITH (NOLOCK)
INNER JOIN sys.dm_os_waiting_tasks AS t2 WITH (NOLOCK)
ON t1.lock_owner_address = t2.resource_address OPTION (RECOMPILE);
------

-- Helps troubleshoot blocking and deadlocking issues
-- The results will change from second to second on a busy system
-- You should run this query multiple times when you see signs of blocking



-- Get CPU Utilization History for last 256 minutes (in one minute intervals)  (Query 42) (CPU Utilization History)
DECLARE @ts_now bigint = (SELECT cpu_ticks/(cpu_ticks/ms_ticks) FROM sys.dm_os_sys_info WITH (NOLOCK)); 

SELECT TOP(256) SQLProcessUtilization AS [SQL Server Process CPU Utilization], 
               SystemIdle AS [System Idle Process], 
               100 - SystemIdle - SQLProcessUtilization AS [Other Process CPU Utilization], 
               DATEADD(ms, -1 * (@ts_now - [timestamp]), GETDATE()) AS [Event Time] 
FROM (SELECT record.value('(./Record/@id)[1]', 'int') AS record_id, 
			record.value('(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]', 'int') 
			AS [SystemIdle], 
			record.value('(./Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization)[1]', 'int') 
			AS [SQLProcessUtilization], [timestamp] 
	  FROM (SELECT [timestamp], CONVERT(xml, record) AS [record] 
			FROM sys.dm_os_ring_buffers WITH (NOLOCK)
			WHERE ring_buffer_type = N'RING_BUFFER_SCHEDULER_MONITOR' 
			AND record LIKE N'%<SystemHealth>%') AS x) AS y 
ORDER BY record_id DESC OPTION (RECOMPILE);
------

-- Look at the trend over the entire period 
-- Also look at high sustained 'Other Process' CPU Utilization values
-- Note: This query sometimes gives inaccurate results (negative values)
-- on high core count (> 64 cores) systems


-- Get top total worker time queries for entire instance (Query 43) (Top Worker Time Queries)
SELECT TOP(50) DB_NAME(t.[dbid]) AS [Database Name], 
REPLACE(REPLACE(LEFT(t.[text], 255), CHAR(10),''), CHAR(13),'') AS [Short Query Text],  
qs.total_worker_time AS [Total Worker Time], qs.min_worker_time AS [Min Worker Time],
qs.total_worker_time/qs.execution_count AS [Avg Worker Time], 
qs.max_worker_time AS [Max Worker Time], 
qs.min_elapsed_time AS [Min Elapsed Time], 
qs.total_elapsed_time/qs.execution_count AS [Avg Elapsed Time], 
qs.max_elapsed_time AS [Max Elapsed Time],
qs.min_logical_reads AS [Min Logical Reads],
qs.total_logical_reads/qs.execution_count AS [Avg Logical Reads],
qs.max_logical_reads AS [Max Logical Reads], 
qs.execution_count AS [Execution Count],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index], 
qs.creation_time AS [Creation Time]
--,t.[text] AS [Query Text], qp.query_plan AS [Query Plan] -- uncomment out these columns if not copying results to Excel
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t 
CROSS APPLY sys.dm_exec_query_plan(plan_handle) AS qp 
ORDER BY qs.total_worker_time DESC OPTION (RECOMPILE);
------


-- Helps you find the most expensive queries from a CPU perspective across the entire instance
-- Can also help track down parameter sniffing issues



-- Page Life Expectancy (PLE) value for each NUMA node in current instance  (Query 44) (PLE by NUMA Node)
SELECT @@SERVERNAME AS [Server Name], RTRIM([object_name]) AS [Object Name], instance_name, cntr_value AS [Page Life Expectancy]
FROM sys.dm_os_performance_counters WITH (NOLOCK)
WHERE [object_name] LIKE N'%Buffer Node%' -- Handles named instances
AND counter_name = N'Page life expectancy' OPTION (RECOMPILE);
------

-- PLE is a good measurement of internal memory pressure
-- Higher PLE is better. Watch the trend over time, not the absolute value
-- This will only return one row for non-NUMA systems

-- Page Life Expectancy isn�t what you think�
-- https://bit.ly/2EgynLa


-- Memory Grants Pending value for current instance  (Query 45) (Memory Grants Pending)
SELECT @@SERVERNAME AS [Server Name], RTRIM([object_name]) AS [Object Name], cntr_value AS [Memory Grants Pending]                                                                                                       
FROM sys.dm_os_performance_counters WITH (NOLOCK)
WHERE [object_name] LIKE N'%Memory Manager%' -- Handles named instances
AND counter_name = N'Memory Grants Pending' OPTION (RECOMPILE);
------

-- Run multiple times, and run periodically if you suspect you are under memory pressure
-- Memory Grants Pending above zero for a sustained period is a very strong indicator of internal memory pressure


-- Memory Clerk Usage for instance  (Query 46) (Memory Clerk Usage)
-- Look for high value for CACHESTORE_SQLCP (Ad-hoc query plans)
SELECT TOP(10) mc.[type] AS [Memory Clerk Type], 
       CAST((SUM(mc.pages_kb)/1024.0) AS DECIMAL (15,2)) AS [Memory Usage (MB)] 
FROM sys.dm_os_memory_clerks AS mc WITH (NOLOCK)
GROUP BY mc.[type]  
ORDER BY SUM(mc.pages_kb) DESC OPTION (RECOMPILE);
------

-- MEMORYCLERK_SQLBUFFERPOOL was new for SQL Server 2012. It should be your highest consumer of memory

-- CACHESTORE_SQLCP  SQL Plans         
-- These are cached SQL statements or batches that aren't in stored procedures, functions and triggers
-- Watch out for high values for CACHESTORE_SQLCP
-- Enabling 'optimize for ad hoc workloads' at the instance level can help reduce this
-- Running DBCC FREESYSTEMCACHE ('SQL Plans') periodically may be required to better control this

-- CACHESTORE_OBJCP  Object Plans      
-- These are compiled plans for stored procedures, functions and triggers



-- Find single-use, ad-hoc and prepared queries that are bloating the plan cache  (Query 47) (Ad hoc Queries)
SELECT TOP(50) DB_NAME(t.[dbid]) AS [Database Name], t.[text] AS [Query Text], 
cp.objtype AS [Object Type], cp.cacheobjtype AS [Cache Object Type],  
cp.size_in_bytes/1024 AS [Plan Size in KB]
FROM sys.dm_exec_cached_plans AS cp WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t
WHERE cp.cacheobjtype = N'Compiled Plan' 
AND cp.objtype IN (N'Adhoc', N'Prepared') 
AND cp.usecounts = 1
ORDER BY cp.size_in_bytes DESC, DB_NAME(t.[dbid]) OPTION (RECOMPILE);
------

-- Gives you the text, type and size of single-use ad-hoc and prepared queries that waste space in the plan cache
-- Enabling 'optimize for ad hoc workloads' for the instance can help (SQL Server 2008 and above only)
-- Running DBCC FREESYSTEMCACHE ('SQL Plans') periodically may be required to better control this
-- Enabling forced parameterization for the database can help, but test first!

-- Plan cache, adhoc workloads and clearing the single-use plan cache bloat
-- https://bit.ly/2EfYOkl


-- Get top total logical reads queries for entire instance (Query 48) (Top Logical Reads Queries)
SELECT TOP(50) DB_NAME(t.[dbid]) AS [Database Name],
REPLACE(REPLACE(LEFT(t.[text], 255), CHAR(10),''), CHAR(13),'') AS [Short Query Text], 
qs.total_logical_reads AS [Total Logical Reads],
qs.min_logical_reads AS [Min Logical Reads],
qs.total_logical_reads/qs.execution_count AS [Avg Logical Reads],
qs.max_logical_reads AS [Max Logical Reads],   
qs.min_worker_time AS [Min Worker Time],
qs.total_worker_time/qs.execution_count AS [Avg Worker Time], 
qs.max_worker_time AS [Max Worker Time], 
qs.min_elapsed_time AS [Min Elapsed Time], 
qs.total_elapsed_time/qs.execution_count AS [Avg Elapsed Time], 
qs.max_elapsed_time AS [Max Elapsed Time],
qs.execution_count AS [Execution Count], 
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index],
qs.creation_time AS [Creation Time]
--,t.[text] AS [Complete Query Text], qp.query_plan AS [Query Plan] -- uncomment out these columns if not copying results to Excel
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t 
CROSS APPLY sys.dm_exec_query_plan(plan_handle) AS qp 
ORDER BY qs.total_logical_reads DESC OPTION (RECOMPILE);
------


-- Helps you find the most expensive queries from a memory perspective across the entire instance
-- Can also help track down parameter sniffing issues


-- Get top average elapsed time queries for entire instance (Query 49) (Top Avg Elapsed Time Queries)
SELECT TOP(50) DB_NAME(t.[dbid]) AS [Database Name], 
REPLACE(REPLACE(LEFT(t.[text], 255), CHAR(10),''), CHAR(13),'') AS [Short Query Text],  
qs.total_elapsed_time/qs.execution_count AS [Avg Elapsed Time],
qs.min_elapsed_time, qs.max_elapsed_time, qs.last_elapsed_time,
qs.execution_count AS [Execution Count],  
qs.total_logical_reads/qs.execution_count AS [Avg Logical Reads], 
qs.total_physical_reads/qs.execution_count AS [Avg Physical Reads], 
qs.total_worker_time/qs.execution_count AS [Avg Worker Time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index],
qs.creation_time AS [Creation Time]
--,t.[text] AS [Complete Query Text], qp.query_plan AS [Query Plan] -- uncomment out these columns if not copying results to Excel
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t 
CROSS APPLY sys.dm_exec_query_plan(plan_handle) AS qp 
ORDER BY qs.total_elapsed_time/qs.execution_count DESC OPTION (RECOMPILE);
------

-- Helps you find the highest average elapsed time queries across the entire instance
-- Can also help track down parameter sniffing issues


-- Look at UDF execution statistics (Query 50) (UDF Stats by DB)
SELECT TOP (25) DB_NAME(database_id) AS [Database Name], 
		   OBJECT_NAME(object_id, database_id) AS [Function Name],
		   total_worker_time, execution_count, total_elapsed_time,  
           total_elapsed_time/execution_count AS [avg_elapsed_time],  
           last_elapsed_time, last_execution_time, cached_time 
FROM sys.dm_exec_function_stats WITH (NOLOCK) 
ORDER BY total_worker_time DESC OPTION (RECOMPILE);
------

-- sys.dm_exec_function_stats (Transact-SQL)
-- https://bit.ly/2q1Q6BM



-- Database specific queries *****************************************************************

-- **** Please switch to a user database that you are interested in! *****
--USE YourDatabaseName; -- make sure to change to an actual database on your instance, not the master system database
--GO

-- Individual File Sizes and space available for current database  (Query 51) (File Sizes and Space)
SELECT f.name AS [File Name] , f.physical_name AS [Physical Name], 
CAST((f.size/128.0) AS DECIMAL(15,2)) AS [Total Size in MB],
CAST(f.size/128.0 - CAST(FILEPROPERTY(f.name, 'SpaceUsed') AS int)/128.0 AS DECIMAL(15,2)) 
AS [Available Space In MB], f.[file_id], fg.name AS [Filegroup Name],
f.is_percent_growth, f.growth, fg.is_default, fg.is_read_only, 
fg.is_autogrow_all_files -- New in SQL Server 2016
FROM sys.database_files AS f WITH (NOLOCK) 
LEFT OUTER JOIN sys.filegroups AS fg WITH (NOLOCK)
ON f.data_space_id = fg.data_space_id
ORDER BY f.[file_id] OPTION (RECOMPILE);
------

-- Look at how large and how full the files are and where they are located
-- Make sure the transaction log is not full!!

-- is_autogrow_all_files is new for SQL Server 2016. Equivalent to TF 1117 for user databases

-- SQL Server 2016: Changes in default behavior for autogrow and allocations for tempdb and user databases
-- https://bit.ly/2evRZSR


-- Log space usage for current database  (Query 52) (Log Space Usage)
SELECT DB_NAME(lsu.database_id) AS [Database Name], db.recovery_model_desc AS [Recovery Model],
		CAST(lsu.total_log_size_in_bytes/1048576.0 AS DECIMAL(10, 2)) AS [Total Log Space (MB)],
		CAST(lsu.used_log_space_in_bytes/1048576.0 AS DECIMAL(10, 2)) AS [Used Log Space (MB)], 
		CAST(lsu.used_log_space_in_percent AS DECIMAL(10, 2)) AS [Used Log Space %],
		CAST(lsu.log_space_in_bytes_since_last_backup/1048576.0 AS DECIMAL(10, 2)) AS [Used Log Space Since Last Backup (MB)],
		db.log_reuse_wait_desc		 
FROM sys.dm_db_log_space_usage AS lsu WITH (NOLOCK)
INNER JOIN sys.databases AS db WITH (NOLOCK)
ON lsu.database_id = db.database_id
OPTION (RECOMPILE);
------

-- Look at log file size and usage, along with the log reuse wait description for the current database



-- Get database scoped configuration values for current database (Query 53) (Database-scoped Configurations)
SELECT configuration_id, name, [value] AS [value_for_primary], value_for_secondary
FROM sys.database_scoped_configurations WITH (NOLOCK) OPTION (RECOMPILE);
------

-- This lets you see the value of these new properties for the current database

-- Clear plan cache for current database
-- ALTER DATABASE SCOPED CONFIGURATION CLEAR PROCEDURE_CACHE;

-- ALTER DATABASE SCOPED CONFIGURATION (Transact-SQL)
-- https://bit.ly/2sOH7nb


-- I/O Statistics by file for the current database  (Query 54) (IO Stats By File)
SELECT DB_NAME(DB_ID()) AS [Database Name], df.name AS [Logical Name], vfs.[file_id], df.type_desc,
df.physical_name AS [Physical Name], CAST(vfs.size_on_disk_bytes/1048576.0 AS DECIMAL(10, 2)) AS [Size on Disk (MB)],
vfs.num_of_reads, vfs.num_of_writes, vfs.io_stall_read_ms, vfs.io_stall_write_ms,
CAST(100. * vfs.io_stall_read_ms/(vfs.io_stall_read_ms + vfs.io_stall_write_ms) AS DECIMAL(10,1)) AS [IO Stall Reads Pct],
CAST(100. * vfs.io_stall_write_ms/(vfs.io_stall_write_ms + vfs.io_stall_read_ms) AS DECIMAL(10,1)) AS [IO Stall Writes Pct],
(vfs.num_of_reads + vfs.num_of_writes) AS [Writes + Reads], 
CAST(vfs.num_of_bytes_read/1048576.0 AS DECIMAL(10, 2)) AS [MB Read], 
CAST(vfs.num_of_bytes_written/1048576.0 AS DECIMAL(10, 2)) AS [MB Written],
CAST(100. * vfs.num_of_reads/(vfs.num_of_reads + vfs.num_of_writes) AS DECIMAL(10,1)) AS [# Reads Pct],
CAST(100. * vfs.num_of_writes/(vfs.num_of_reads + vfs.num_of_writes) AS DECIMAL(10,1)) AS [# Write Pct],
CAST(100. * vfs.num_of_bytes_read/(vfs.num_of_bytes_read + vfs.num_of_bytes_written) AS DECIMAL(10,1)) AS [Read Bytes Pct],
CAST(100. * vfs.num_of_bytes_written/(vfs.num_of_bytes_read + vfs.num_of_bytes_written) AS DECIMAL(10,1)) AS [Written Bytes Pct]
FROM sys.dm_io_virtual_file_stats(DB_ID(), NULL) AS vfs
INNER JOIN sys.database_files AS df WITH (NOLOCK)
ON vfs.[file_id]= df.[file_id] OPTION (RECOMPILE);
------

-- This helps you characterize your workload better from an I/O perspective for this database
-- It helps you determine whether you has an OLTP or DW/DSS type of workload



-- Get most frequently executed queries for this database (Query 55) (Query Execution Counts)
SELECT TOP(50) LEFT(t.[text], 50) AS [Short Query Text], qs.execution_count AS [Execution Count],
qs.total_logical_reads AS [Total Logical Reads],
qs.total_logical_reads/qs.execution_count AS [Avg Logical Reads],
qs.total_worker_time AS [Total Worker Time],
qs.total_worker_time/qs.execution_count AS [Avg Worker Time], 
qs.total_elapsed_time AS [Total Elapsed Time],
qs.total_elapsed_time/qs.execution_count AS [Avg Elapsed Time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index], 
qs.creation_time AS [Creation Time]
--,t.[text] AS [Complete Query Text], qp.query_plan AS [Query Plan] -- uncomment out these columns if not copying results to Excel
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t 
CROSS APPLY sys.dm_exec_query_plan(plan_handle) AS qp 
WHERE t.dbid = DB_ID()
ORDER BY qs.execution_count DESC OPTION (RECOMPILE);
------


-- Queries 56 through 61 are the "Bad Man List" for stored procedures

-- Top Cached SPs By Execution Count (Query 56) (SP Execution Counts)
SELECT TOP(100) p.name AS [SP Name], qs.execution_count AS [Execution Count],
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute],
qs.total_elapsed_time/qs.execution_count AS [Avg Elapsed Time],
qs.total_worker_time/qs.execution_count AS [Avg Worker Time],    
qs.total_logical_reads/qs.execution_count AS [Avg Logical Reads],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index],
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND DATEDIFF(Minute, qs.cached_time, GETDATE()) > 0
ORDER BY qs.execution_count DESC OPTION (RECOMPILE);
------

-- Tells you which cached stored procedures are called the most often
-- This helps you characterize and baseline your workload


-- Top Cached SPs By Avg Elapsed Time (Query 57) (SP Avg Elapsed Time)
SELECT TOP(25) p.name AS [SP Name], qs.min_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time], 
qs.max_elapsed_time, qs.last_elapsed_time, qs.total_elapsed_time, qs.execution_count, 
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute], 
qs.total_worker_time/qs.execution_count AS [AvgWorkerTime], 
qs.total_worker_time AS [TotalWorkerTime],
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND DATEDIFF(Minute, qs.cached_time, GETDATE()) > 0
ORDER BY avg_elapsed_time DESC OPTION (RECOMPILE);
------

-- This helps you find high average elapsed time cached stored procedures that
-- may be easy to optimize with standard query tuning techniques



-- Top Cached SPs By Total Worker time. Worker time relates to CPU cost  (Query 58) (SP Worker Time)
SELECT TOP(25) p.name AS [SP Name], qs.total_worker_time AS [TotalWorkerTime], 
qs.total_worker_time/qs.execution_count AS [AvgWorkerTime], qs.execution_count, 
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute],
qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index],
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND DATEDIFF(Minute, qs.cached_time, GETDATE()) > 0
ORDER BY qs.total_worker_time DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a CPU perspective
-- You should look at this if you see signs of CPU pressure


-- Top Cached SPs By Total Logical Reads. Logical reads relate to memory pressure  (Query 59) (SP Logical Reads)
SELECT TOP(25) p.name AS [SP Name], qs.total_logical_reads AS [TotalLogicalReads], 
qs.total_logical_reads/qs.execution_count AS [AvgLogicalReads],qs.execution_count, 
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute], 
qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index], 
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND DATEDIFF(Minute, qs.cached_time, GETDATE()) > 0
ORDER BY qs.total_logical_reads DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a memory perspective
-- You should look at this if you see signs of memory pressure


-- Top Cached SPs By Total Physical Reads. Physical reads relate to disk read I/O pressure  (Query 60) (SP Physical Reads)
SELECT TOP(25) p.name AS [SP Name],qs.total_physical_reads AS [TotalPhysicalReads], 
qs.total_physical_reads/qs.execution_count AS [AvgPhysicalReads], qs.execution_count, 
qs.total_logical_reads,qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index],
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan 
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND qs.total_physical_reads > 0
ORDER BY qs.total_physical_reads DESC, qs.total_logical_reads DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a read I/O perspective
-- You should look at this if you see signs of I/O pressure or of memory pressure
       


-- Top Cached SPs By Total Logical Writes (Query 61) (SP Logical Writes)
-- Logical writes relate to both memory and disk I/O pressure 
SELECT TOP(25) p.name AS [SP Name], qs.total_logical_writes AS [TotalLogicalWrites], 
qs.total_logical_writes/qs.execution_count AS [AvgLogicalWrites], qs.execution_count,
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute],
qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index], 
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan 
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND qs.total_logical_writes > 0
AND DATEDIFF(Minute, qs.cached_time, GETDATE()) > 0
ORDER BY qs.total_logical_writes DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a write I/O perspective
-- You should look at this if you see signs of I/O pressure or of memory pressure


-- Lists the top statements by average input/output usage for the current database  (Query 62) (Top IO Statements)
SELECT TOP(50) OBJECT_NAME(qt.objectid, dbid) AS [SP Name],
(qs.total_logical_reads + qs.total_logical_writes) /qs.execution_count AS [Avg IO], qs.execution_count AS [Execution Count],
SUBSTRING(qt.[text],qs.statement_start_offset/2, 
	(CASE 
		WHEN qs.statement_end_offset = -1 
	 THEN LEN(CONVERT(nvarchar(max), qt.[text])) * 2 
		ELSE qs.statement_end_offset 
	 END - qs.statement_start_offset)/2) AS [Query Text]	
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS qt
WHERE qt.[dbid] = DB_ID()
ORDER BY [Avg IO] DESC OPTION (RECOMPILE);
------

-- Helps you find the most expensive statements for I/O by SP



-- Possible Bad NC Indexes (writes > reads)  (Query 63) (Bad NC Indexes)
SELECT OBJECT_NAME(s.[object_id]) AS [Table Name], i.name AS [Index Name], i.index_id, 
i.is_disabled, i.is_hypothetical, i.has_filter, i.fill_factor,
s.user_updates AS [Total Writes], s.user_seeks + s.user_scans + s.user_lookups AS [Total Reads],
s.user_updates - (s.user_seeks + s.user_scans + s.user_lookups) AS [Difference]
FROM sys.dm_db_index_usage_stats AS s WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON s.[object_id] = i.[object_id]
AND i.index_id = s.index_id
WHERE OBJECTPROPERTY(s.[object_id],'IsUserTable') = 1
AND s.database_id = DB_ID()
AND s.user_updates > (s.user_seeks + s.user_scans + s.user_lookups)
AND i.index_id > 1 AND i.[type_desc] = N'NONCLUSTERED'
AND i.is_primary_key = 0 AND i.is_unique_constraint = 0 AND i.is_unique = 0
ORDER BY [Difference] DESC, [Total Writes] DESC, [Total Reads] ASC OPTION (RECOMPILE);
------

-- Look for indexes with high numbers of writes and zero or very low numbers of reads
-- Consider your complete workload, and how long your instance has been running
-- Investigate further before dropping an index!


-- Missing Indexes for current database by Index Advantage  (Query 64) (Missing Indexes)
SELECT CONVERT(decimal(18,2), user_seeks * avg_total_user_cost * (avg_user_impact * 0.01)) AS [index_advantage], 
migs.last_user_seek, mid.[statement] AS [Database.Schema.Table],
COUNT(1) OVER(PARTITION BY mid.[statement]) AS [missing_indexes_for_table],
COUNT(1) OVER(PARTITION BY mid.[statement], equality_columns) AS [similar_missing_indexes_for_table],
mid.equality_columns, mid.inequality_columns, mid.included_columns,
migs.unique_compiles, migs.user_seeks, 
CONVERT(decimal(18,2), migs.avg_total_user_cost) AS [avg_total_user_cost], migs.avg_user_impact,
OBJECT_NAME(mid.[object_id]) AS [Table Name], p.rows AS [Table Rows]
FROM sys.dm_db_missing_index_group_stats AS migs WITH (NOLOCK)
INNER JOIN sys.dm_db_missing_index_groups AS mig WITH (NOLOCK)
ON migs.group_handle = mig.index_group_handle
INNER JOIN sys.dm_db_missing_index_details AS mid WITH (NOLOCK)
ON mig.index_handle = mid.index_handle
INNER JOIN sys.partitions AS p WITH (NOLOCK)
ON p.[object_id] = mid.[object_id]
WHERE mid.database_id = DB_ID()
AND p.index_id < 2 
ORDER BY index_advantage DESC OPTION (RECOMPILE);
------

-- Look at index advantage, last user seek time, number of user seeks to help determine source and importance
-- SQL Server is overly eager to add included columns, so beware
-- Do not just blindly add indexes that show up from this query!!!


-- Find missing index warnings for cached plans in the current database  (Query 65) (Missing Index Warnings)
-- Note: This query could take some time on a busy instance
SELECT TOP(25) OBJECT_NAME(objectid) AS [ObjectName], 
               cp.objtype, cp.usecounts, cp.size_in_bytes, query_plan
FROM sys.dm_exec_cached_plans AS cp WITH (NOLOCK)
CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) AS qp
WHERE CAST(query_plan AS NVARCHAR(MAX)) LIKE N'%MissingIndex%'
AND dbid = DB_ID()
ORDER BY cp.usecounts DESC OPTION (RECOMPILE);
------

-- Helps you connect missing indexes to specific stored procedures or queries
-- This can help you decide whether to add them or not


-- Breaks down buffers used by current database by object (table, index) in the buffer cache  (Query 66) (Buffer Usage)
-- Note: This query could take some time on a busy instance
SELECT OBJECT_NAME(p.[object_id]) AS [Object Name], p.index_id, 
CAST(COUNT(*)/128.0 AS DECIMAL(10, 2)) AS [Buffer size(MB)],  
COUNT(*) AS [BufferCount], p.[Rows] AS [Row Count],
p.data_compression_desc AS [Compression Type]
FROM sys.allocation_units AS a WITH (NOLOCK)
INNER JOIN sys.dm_os_buffer_descriptors AS b WITH (NOLOCK)
ON a.allocation_unit_id = b.allocation_unit_id
INNER JOIN sys.partitions AS p WITH (NOLOCK)
ON a.container_id = p.hobt_id
WHERE b.database_id = CONVERT(int, DB_ID())
AND p.[object_id] > 100
AND OBJECT_NAME(p.[object_id]) NOT LIKE N'plan_%'
AND OBJECT_NAME(p.[object_id]) NOT LIKE N'sys%'
AND OBJECT_NAME(p.[object_id]) NOT LIKE N'xml_index_nodes%'
GROUP BY p.[object_id], p.index_id, p.data_compression_desc, p.[Rows]
ORDER BY [BufferCount] DESC OPTION (RECOMPILE);
------

-- Tells you what tables and indexes are using the most memory in the buffer cache
-- It can help identify possible candidates for data compression


-- Get Table names, row counts, and compression status for clustered index or heap  (Query 67) (Table Sizes)
SELECT OBJECT_NAME(object_id) AS [ObjectName], 
SUM(Rows) AS [RowCount], data_compression_desc AS [CompressionType]
FROM sys.partitions WITH (NOLOCK)
WHERE index_id < 2 --ignore the partitions from the non-clustered index if any
AND OBJECT_NAME(object_id) NOT LIKE N'sys%'
AND OBJECT_NAME(object_id) NOT LIKE N'queue_%' 
AND OBJECT_NAME(object_id) NOT LIKE N'filestream_tombstone%' 
AND OBJECT_NAME(object_id) NOT LIKE N'fulltext%'
AND OBJECT_NAME(object_id) NOT LIKE N'ifts_comp_fragment%'
AND OBJECT_NAME(object_id) NOT LIKE N'filetable_updates%'
AND OBJECT_NAME(object_id) NOT LIKE N'xml_index_nodes%'
AND OBJECT_NAME(object_id) NOT LIKE N'sqlagent_job%'  
AND OBJECT_NAME(object_id) NOT LIKE N'plan_persist%'  
GROUP BY object_id, data_compression_desc
ORDER BY SUM(Rows) DESC OPTION (RECOMPILE);
------

-- Gives you an idea of table sizes, and possible data compression opportunities



-- Get some key table properties (Query 68) (Table Properties)
SELECT OBJECT_NAME(t.[object_id]) AS [ObjectName], p.[rows] AS [Table Rows], p.index_id, 
       p.data_compression_desc AS [Index Data Compression],
       t.create_date, t.lock_on_bulk_load, t.is_replicated, t.has_replication_filter, 
       t.is_tracked_by_cdc, t.lock_escalation_desc, t.is_filetable, 
	   t.is_memory_optimized, t.durability_desc, 
	   t.temporal_type_desc, t.is_remote_data_archive_enabled, t.is_external -- new for SQL Server 2016
FROM sys.tables AS t WITH (NOLOCK)
INNER JOIN sys.partitions AS p WITH (NOLOCK)
ON t.[object_id] = p.[object_id]
WHERE OBJECT_NAME(t.[object_id]) NOT LIKE N'sys%'
ORDER BY OBJECT_NAME(t.[object_id]), p.index_id OPTION (RECOMPILE);
------

-- Gives you some good information about your tables
-- is_memory_optimized and durability_desc were new in SQL Server 2014
-- temporal_type_desc, is_remote_data_archive_enabled, is_external are new in SQL Server 2016

-- sys.tables (Transact-SQL)
-- https://bit.ly/2Gk7998



-- When were Statistics last updated on all indexes?  (Query 69) (Statistics Update)
SELECT SCHEMA_NAME(o.Schema_ID) + N'.' + o.[NAME] AS [Object Name], o.[type_desc] AS [Object Type],
      i.[name] AS [Index Name], STATS_DATE(i.[object_id], i.index_id) AS [Statistics Date], 
      s.auto_created, s.no_recompute, s.user_created, s.is_incremental, s.is_temporary,
	  st.row_count, st.used_page_count
FROM sys.objects AS o WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON o.[object_id] = i.[object_id]
INNER JOIN sys.stats AS s WITH (NOLOCK)
ON i.[object_id] = s.[object_id] 
AND i.index_id = s.stats_id
INNER JOIN sys.dm_db_partition_stats AS st WITH (NOLOCK)
ON o.[object_id] = st.[object_id]
AND i.[index_id] = st.[index_id]
WHERE o.[type] IN ('U', 'V')
AND st.row_count > 0
ORDER BY STATS_DATE(i.[object_id], i.index_id) DESC OPTION (RECOMPILE);
------  

-- Helps discover possible problems with out-of-date statistics
-- Also gives you an idea which indexes are the most active

-- sys.stats (Transact-SQL)
-- https://bit.ly/2GyAxrn

-- UPDATEs to Statistics (Erin Stellato)
-- https://bit.ly/2vhrYQy



-- Look at most frequently modified indexes and statistics (Query 70) (Volatile Indexes)
SELECT o.[name] AS [Object Name], o.[object_id], o.[type_desc], s.[name] AS [Statistics Name], 
       s.stats_id, s.no_recompute, s.auto_created, s.is_incremental, s.is_temporary,
	   sp.modification_counter, sp.[rows], sp.rows_sampled, sp.last_updated
FROM sys.objects AS o WITH (NOLOCK)
INNER JOIN sys.stats AS s WITH (NOLOCK)
ON s.object_id = o.object_id
CROSS APPLY sys.dm_db_stats_properties(s.object_id, s.stats_id) AS sp
WHERE o.[type_desc] NOT IN (N'SYSTEM_TABLE', N'INTERNAL_TABLE')
AND sp.modification_counter > 0
ORDER BY sp.modification_counter DESC, o.name OPTION (RECOMPILE);
------

-- This helps you understand your workload and make better decisions about 
-- things like data compression and adding new indexes to a table



-- Get fragmentation info for all indexes above a certain size in the current database  (Query 71) (Index Fragmentation)
-- Note: This query could take some time on a very large database
SELECT DB_NAME(ps.database_id) AS [Database Name], SCHEMA_NAME(o.[schema_id]) AS [Schema Name],
OBJECT_NAME(ps.OBJECT_ID) AS [Object Name], i.[name] AS [Index Name], ps.index_id, 
ps.index_type_desc, ps.avg_fragmentation_in_percent, 
ps.fragment_count, ps.page_count, i.fill_factor, i.has_filter, 
i.filter_definition, i.[allow_page_locks]
FROM sys.dm_db_index_physical_stats(DB_ID(),NULL, NULL, NULL , N'LIMITED') AS ps
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON ps.[object_id] = i.[object_id] 
AND ps.index_id = i.index_id
INNER JOIN sys.objects AS o WITH (NOLOCK)
ON i.[object_id] = o.[object_id]
WHERE ps.database_id = DB_ID()
AND ps.page_count > 2500
ORDER BY ps.avg_fragmentation_in_percent DESC OPTION (RECOMPILE);
------

-- Helps determine whether you have framentation in your relational indexes
-- and how effective your index maintenance strategy is


--- Index Read/Write stats (all tables in current DB) ordered by Reads  (Query 72) (Overall Index Usage - Reads)
SELECT OBJECT_NAME(i.[object_id]) AS [ObjectName], i.[name] AS [IndexName], i.index_id, 
       s.user_seeks, s.user_scans, s.user_lookups,
	   s.user_seeks + s.user_scans + s.user_lookups AS [Total Reads], 
	   s.user_updates AS [Writes],  
	   i.[type_desc] AS [Index Type], i.fill_factor AS [Fill Factor], i.has_filter, i.filter_definition, 
	   s.last_user_scan, s.last_user_lookup, s.last_user_seek
FROM sys.indexes AS i WITH (NOLOCK)
LEFT OUTER JOIN sys.dm_db_index_usage_stats AS s WITH (NOLOCK)
ON i.[object_id] = s.[object_id]
AND i.index_id = s.index_id
AND s.database_id = DB_ID()
WHERE OBJECTPROPERTY(i.[object_id],'IsUserTable') = 1
ORDER BY s.user_seeks + s.user_scans + s.user_lookups DESC OPTION (RECOMPILE); -- Order by reads
------

-- Show which indexes in the current database are most active for Reads


--- Index Read/Write stats (all tables in current DB) ordered by Writes  (Query 73) (Overall Index Usage - Writes)
SELECT OBJECT_NAME(i.[object_id]) AS [ObjectName], i.[name] AS [IndexName], i.index_id,
	   s.user_updates AS [Writes], s.user_seeks + s.user_scans + s.user_lookups AS [Total Reads], 
	   i.[type_desc] AS [Index Type], i.fill_factor AS [Fill Factor], i.has_filter, i.filter_definition,
	   s.last_system_update, s.last_user_update
FROM sys.indexes AS i WITH (NOLOCK)
LEFT OUTER JOIN sys.dm_db_index_usage_stats AS s WITH (NOLOCK)
ON i.[object_id] = s.[object_id]
AND i.index_id = s.index_id
AND s.database_id = DB_ID()
WHERE OBJECTPROPERTY(i.[object_id],'IsUserTable') = 1
ORDER BY s.user_updates DESC OPTION (RECOMPILE);						 -- Order by writes
------

-- Show which indexes in the current database are most active for Writes


-- Get in-memory OLTP index usage (Query 74) (XTP Index Usage)
SELECT OBJECT_NAME(i.[object_id]) AS [Object Name], i.index_id, i.[name] AS [Index Name],
       i.[type_desc], xis.scans_started, xis.scans_retries, 
	   xis.rows_touched, xis.rows_returned
FROM sys.dm_db_xtp_index_stats AS xis WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON i.[object_id] = xis.[object_id] 
AND i.index_id = xis.index_id 
ORDER BY OBJECT_NAME(i.[object_id]) OPTION (RECOMPILE);
------

-- This gives you some index usage statistics for in-memory OLTP
-- Returns no data if you are not using in-memory OLTP

-- Guidelines for Using Indexes on Memory-Optimized Tables
-- https://bit.ly/2GCP8lF


-- Look at Columnstore index physical statistics (Query 75) (Columnstore Index Physical Stat)
SELECT OBJECT_NAME(ps.object_id) AS [TableName],  
	i.[name] AS [IndexName], ps.index_id, ps.partition_number,
	ps.delta_store_hobt_id, ps.state_desc, ps.total_rows, ps.size_in_bytes,
	ps.trim_reason_desc, ps.generation, ps.transition_to_compressed_state_desc,
	ps.has_vertipaq_optimization, ps.deleted_rows,
	100 * (ISNULL(ps.deleted_rows, 0))/ps.total_rows AS [Fragmentation]
FROM sys.dm_db_column_store_row_group_physical_stats AS ps WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON ps.object_id = i.object_id 
AND ps.index_id = i.index_id
ORDER BY ps.object_id, ps.partition_number, ps.row_group_id OPTION (RECOMPILE);
------

-- sys.dm_db_column_store_row_group_physical_stats (Transact-SQL)
-- https://bit.ly/2q276XQ


-- Get lock waits for current database (Query 76) (Lock Waits)
SELECT o.name AS [table_name], i.name AS [index_name], ios.index_id, ios.partition_number,
		SUM(ios.row_lock_wait_count) AS [total_row_lock_waits], 
		SUM(ios.row_lock_wait_in_ms) AS [total_row_lock_wait_in_ms],
		SUM(ios.page_lock_wait_count) AS [total_page_lock_waits],
		SUM(ios.page_lock_wait_in_ms) AS [total_page_lock_wait_in_ms],
		SUM(ios.page_lock_wait_in_ms)+ SUM(row_lock_wait_in_ms) AS [total_lock_wait_in_ms]
FROM sys.dm_db_index_operational_stats(DB_ID(), NULL, NULL, NULL) AS ios
INNER JOIN sys.objects AS o WITH (NOLOCK)
ON ios.[object_id] = o.[object_id]
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON ios.[object_id] = i.[object_id] 
AND ios.index_id = i.index_id
WHERE o.[object_id] > 100
GROUP BY o.name, i.name, ios.index_id, ios.partition_number
HAVING SUM(ios.page_lock_wait_in_ms)+ SUM(row_lock_wait_in_ms) > 0
ORDER BY total_lock_wait_in_ms DESC OPTION (RECOMPILE);
------

-- This query is helpful for troubleshooting blocking and deadlocking issues



-- Look at UDF execution statistics (Query 77) (UDF Statistics)
SELECT OBJECT_NAME(object_id) AS [Function Name], execution_count,
	   total_worker_time, total_logical_reads, total_physical_reads, total_elapsed_time, 
	   total_elapsed_time/execution_count AS [avg_elapsed_time],
	   FORMAT(cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
FROM sys.dm_exec_function_stats WITH (NOLOCK) 
WHERE database_id = DB_ID()
ORDER BY total_worker_time DESC OPTION (RECOMPILE); 
------

-- New for SQL Server 2016
-- Helps you investigate scalar UDF performance issues
-- Does not return information for table valued functions

-- sys.dm_exec_function_stats (Transact-SQL)
-- https://bit.ly/2q1Q6BM


-- Get Query Store Options for this database (Query 78) (QueryStore Options)
SELECT actual_state_desc, desired_state_desc, [interval_length_minutes],
       current_storage_size_mb, [max_storage_size_mb], 
	   query_capture_mode_desc, size_based_cleanup_mode_desc
FROM sys.database_query_store_options WITH (NOLOCK) OPTION (RECOMPILE);
------

-- New for SQL Server 2016
-- Requires that Query Store is enabled for this database

-- Tuning Workload Performance with Query Store
-- https://bit.ly/1kHSl7w


-- Get highest aggregate duration queries over last hour (Query 79) (High Aggregate Duration Queries)
WITH AggregatedDurationLastHour
AS
(SELECT q.query_id, SUM(count_executions * avg_duration) AS total_duration,
   COUNT (distinct p.plan_id) AS number_of_plans
   FROM sys.query_store_query_text AS qt WITH (NOLOCK)
   INNER JOIN sys.query_store_query AS q WITH (NOLOCK)
   ON qt.query_text_id = q.query_text_id
   INNER JOIN sys.query_store_plan AS p WITH (NOLOCK)
   ON q.query_id = p.query_id
   INNER JOIN sys.query_store_runtime_stats AS rs WITH (NOLOCK)
   ON rs.plan_id = p.plan_id
   INNER JOIN sys.query_store_runtime_stats_interval AS rsi WITH (NOLOCK)
   ON rsi.runtime_stats_interval_id = rs.runtime_stats_interval_id
   WHERE rsi.start_time >= DATEADD(hour, -1, GETUTCDATE()) 
   AND rs.execution_type_desc = N'Regular'
   GROUP BY q.query_id),
OrderedDuration AS
(SELECT query_id, total_duration, number_of_plans, 
 ROW_NUMBER () OVER (ORDER BY total_duration DESC, query_id) AS RN
 FROM AggregatedDurationLastHour)
SELECT OBJECT_NAME(q.object_id) AS [Containing Object], qt.query_sql_text, 
od.total_duration AS [Total Duration (microsecs)], 
od.number_of_plans AS [Plan Count],
p.is_forced_plan, p.is_parallel_plan, p.is_trivial_plan,
q.query_parameterization_type_desc, p.[compatibility_level],
p.last_compile_start_time, q.last_execution_time,
CONVERT(xml, p.query_plan) AS query_plan_xml 
FROM OrderedDuration AS od 
INNER JOIN sys.query_store_query AS q WITH (NOLOCK)
ON q.query_id  = od.query_id
INNER JOIN sys.query_store_query_text AS qt WITH (NOLOCK)
ON q.query_text_id = qt.query_text_id
INNER JOIN sys.query_store_plan AS p WITH (NOLOCK)
ON q.query_id = p.query_id
WHERE od.RN <= 50 
ORDER BY total_duration DESC OPTION (RECOMPILE);
------

-- New for SQL Server 2016
-- Requires that QueryStore is enabled for this database


-- Get input buffer information for the current database (Query 80) (Input Buffer)
SELECT es.session_id, DB_NAME(es.database_id) AS [Database Name],
es.login_time, es.cpu_time, es.logical_reads,
es.[status], ib.event_info AS [Input Buffer]
FROM sys.dm_exec_sessions AS es WITH (NOLOCK)
CROSS APPLY sys.dm_exec_input_buffer(es.session_id, NULL) AS ib
WHERE es.database_id = DB_ID()
AND es.session_id > 50
AND es.session_id <> @@SPID OPTION (RECOMPILE);

-- Gives you input buffer information from all non-system sessions for the current database
-- Replaces DBCC INPUTBUFFER

-- New DMF for retrieving input buffer in SQL Server
-- https://bit.ly/2uHKMbz



-- Look at recent Full backups for the current database (Query 81) (Recent Full Backups)
SELECT TOP (30) bs.machine_name, bs.server_name, bs.database_name AS [Database Name], bs.recovery_model,
CONVERT (BIGINT, bs.backup_size / 1048576 ) AS [Uncompressed Backup Size (MB)],
CONVERT (BIGINT, bs.compressed_backup_size / 1048576 ) AS [Compressed Backup Size (MB)],
CONVERT (NUMERIC (20,2), (CONVERT (FLOAT, bs.backup_size) /
CONVERT (FLOAT, bs.compressed_backup_size))) AS [Compression Ratio], bs.has_backup_checksums, bs.is_copy_only, bs.encryptor_type,
DATEDIFF (SECOND, bs.backup_start_date, bs.backup_finish_date) AS [Backup Elapsed Time (sec)],
bs.backup_finish_date AS [Backup Finish Date], bmf.physical_device_name AS [Backup Location], bmf.physical_block_size
FROM msdb.dbo.backupset AS bs WITH (NOLOCK)
INNER JOIN msdb.dbo.backupmediafamily AS bmf WITH (NOLOCK)
ON bs.media_set_id = bmf.media_set_id  
WHERE bs.database_name = DB_NAME(DB_ID())
AND bs.[type] = 'D' -- Change to L if you want Log backups
ORDER BY bs.backup_finish_date DESC OPTION (RECOMPILE);
------

-- Are your backup sizes and times changing over time?
-- Are you using backup compression?
-- Are you using backup checksums?
-- Are you doing copy_only backups?
-- Are you doing encrypted backups?
-- Have you done any backup tuning with striped backups, or changing the parameters of the backup command?

-- In SQL Server 2016, native SQL Server backup compression actually works much better with databases that are using TDE than in previous versions
-- https://bit.ly/28Rpb2x


-- These three Pluralsight Courses go into more detail about how to run these queries and interpret the results

-- SQL Server 2014 DMV Diagnostic Queries � Part 1 
-- https://bit.ly/2plxCer

-- SQL Server 2014 DMV Diagnostic Queries � Part 2
-- https://bit.ly/2IuJpzI

-- SQL Server 2014 DMV Diagnostic Queries � Part 3
-- https://bit.ly/2FIlCPb



-- Sign up for Microsoft Visual Studio Dev Essentials and get a free three month pass to Pluralsight

-- Microsoft Visual Studio Dev Essentials
-- http://bit.ly/1q6xbDL


-- Sign up for Microsoft Azure Essentials and get lots of free Azure usage credits, MCP exam voucher, three month Pluralsight subscription

-- Microsoft Azure Essentials
-- https://bit.ly/2JMWe8x


-- August 2017 blog series about upgrading and migrating SQL Server
-- https://bit.ly/2ftKVrX



tools\dbatools\bin\diagnosticquery\SQLServerDiagnosticQueries_2017_201806.sql

-- SQL Server 2017 Diagnostic Information Queries
-- Glenn Berry 
-- Last Modified: July 23, 2018
-- https://www.sqlskills.com/blogs/glenn/
-- http://sqlserverperformance.wordpress.com/
-- Twitter: GlennAlanBerry

-- Please listen to my Pluralsight courses
-- https://www.pluralsight.com/author/glenn-berry

-- If you want to find all of our SQLskills SQL101 blog posts, check out https://bit.ly/2qLwfXW


-- Please make sure you are using the correct version of these diagnostic queries for your version of SQL Server


-- If you like PowerShell, there is a very useful community solution for running these queries in an automated fashion
-- https://dbatools.io/

-- Invoke-DbaDiagnosticQuery
-- https://dbatools.io/functions/invoke-dbadiagnosticquery/


--******************************************************************************
--*   Copyright (C) 2018 Glenn Berry, SQLskills.com
--*   All rights reserved. 
--*
--*   For more scripts and sample code, check out 
--*      https://www.sqlskills.com/blogs/glenn
--*
--*   You may alter this code for your own *non-commercial* purposes. You may
--*   republish altered code as long as you include this copyright and give due credit. 
--*
--*
--*   THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF 
--*   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED 
--*   TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
--*   PARTICULAR PURPOSE. 
--*
--******************************************************************************

-- Check the major product version to see if it is SQL Server 2017 CTP 1 or greater
IF NOT EXISTS (SELECT * WHERE CONVERT(varchar(128), SERVERPROPERTY('ProductVersion')) LIKE '14%')
	BEGIN
		DECLARE @ProductVersion varchar(128) = CONVERT(varchar(128), SERVERPROPERTY('ProductVersion'));
		RAISERROR ('Script does not match the ProductVersion [%s] of this instance. Many of these queries may not work on this version.' , 18 , 16 , @ProductVersion);
	END
	ELSE
		PRINT N'You have the correct major version of SQL Server for this diagnostic information script';
	

-- Instance level queries *******************************

-- SQL and OS Version information for current instance  (Query 1) (Version Info)
SELECT @@SERVERNAME AS [Server Name], @@VERSION AS [SQL Server and OS Version Info];
------

-- SQL Server 2017 Builds																		
-- Build			Description			Release Date	URL to KB Article								
-- 14.0.1.246		CTP 1.0				11/30/2016
-- 14.0.100.187		CTP 1.1				12/16/2016
-- 14.0.200.24		CTP 1.2				1/19/2017
-- 14.0.304.138		CTP 1.3				2/17/2017
-- 14.0.405.198		CTP 1.4				3/20/2017
-- 14.0.500.272		CTP 2.0				4/19/2017
-- 14.0.600.250		CTP 2.1				5/17/2017
-- 14.0.800.90		RC1					7/17/2017
-- 14.0.900.75		RC2					8/2/2017
-- 14.0.1000.169	RTM					10/2/2017
-- 14.0.3006.16		CU1					10/24/2017		https://support.microsoft.com/en-us/help/4038634
-- 14.0.3008.27		CU2					11/28/2017		https://support.microsoft.com/en-us/help/4052574
-- 14.0.3015.40		CU3					1/4/2018		https://support.microsoft.com/en-us/help/4052987
-- 14.0.3022.28		CU4					2/20/2018	    https://support.microsoft.com/en-us/help/4056498
-- 14.0.3023.8		CU5					3/20/2018		https://support.microsoft.com/en-us/help/4092643
-- 14.0.3025.34		CU6					4/17/2018	    https://support.microsoft.com/en-us/help/4101464
-- 14.0.3026.27		CU7					5/23/2018		https://support.microsoft.com/en-us/help/4229789
-- 14.0.3029.16		CU8					6/19/2018		https://support.microsoft.com/en-us/help/4338363
-- 14.0.3037.27		CU9					7/19/2018		https://support.microsoft.com/en-us/help/4341265 
		
															

-- How to determine the version, edition and update level of SQL Server and its components 
-- https://bit.ly/2oAjKgW	

-- SQL Server 2017 build versions
-- https://bit.ly/2FLY88I

-- Performance and Stability Fixes in SQL Server 2017 CU Builds
-- https://bit.ly/2GV3CNM

-- What's New in SQL Server 2017 (Database Engine)
-- https://bit.ly/2HjSeyQ

-- What's New in SQL Server 2017
-- https://bit.ly/2saQ4Yh

-- Announcing the Modern Servicing Model for SQL Server
-- https://bit.ly/2xHnh0l

-- SQL Server Service Packs are discontinued starting from SQL Server 2017 
-- https://bit.ly/2GTkbgt 

-- Update Center for Microsoft SQL Server
-- https://bit.ly/2pZptuQ

-- Download SQL Server Management Studio (SSMS)
-- https://bit.ly/1OcupT9

-- Download and install Microsoft SQL Operations Studio 
-- https://bit.ly/2vgke1A



-- Get socket, physical core and logical core count from the SQL Server Error log. (Query 2) (Core Counts)
-- This query might take a few seconds depending on the size of your error log
EXEC sys.xp_readerrorlog 0, 1, N'detected', N'socket';
------

-- This can help you determine the exact core counts used by SQL Server and whether HT is enabled or not
-- It can also help you confirm your SQL Server licensing model
-- Be on the lookout for this message "using 40 logical processors based on SQL Server licensing" 
-- (when you have more than 40 logical cores) which means grandfathered Server/CAL licensing
-- This query will return no results if your error log has been recycled since the instance was last started



-- Get selected server properties (Query 3) (Server Properties)
SELECT SERVERPROPERTY('MachineName') AS [MachineName], 
SERVERPROPERTY('ServerName') AS [ServerName],  
SERVERPROPERTY('InstanceName') AS [Instance], 
SERVERPROPERTY('IsClustered') AS [IsClustered], 
SERVERPROPERTY('ComputerNamePhysicalNetBIOS') AS [ComputerNamePhysicalNetBIOS], 
SERVERPROPERTY('Edition') AS [Edition], 
SERVERPROPERTY('ProductLevel') AS [ProductLevel],				-- What servicing branch (RTM/SP/CU)
SERVERPROPERTY('ProductUpdateLevel') AS [ProductUpdateLevel],	-- Within a servicing branch, what CU# is applied
SERVERPROPERTY('ProductVersion') AS [ProductVersion],
SERVERPROPERTY('ProductMajorVersion') AS [ProductMajorVersion], 
SERVERPROPERTY('ProductMinorVersion') AS [ProductMinorVersion], 
SERVERPROPERTY('ProductBuild') AS [ProductBuild], 
SERVERPROPERTY('ProductBuildType') AS [ProductBuildType],			  -- Is this a GDR or OD hotfix (NULL if on a CU build)
SERVERPROPERTY('ProductUpdateReference') AS [ProductUpdateReference], -- KB article number that is applicable for this build
SERVERPROPERTY('ProcessID') AS [ProcessID],
SERVERPROPERTY('Collation') AS [Collation], 
SERVERPROPERTY('IsFullTextInstalled') AS [IsFullTextInstalled], 
SERVERPROPERTY('IsIntegratedSecurityOnly') AS [IsIntegratedSecurityOnly],
SERVERPROPERTY('FilestreamConfiguredLevel') AS [FilestreamConfiguredLevel],
SERVERPROPERTY('IsHadrEnabled') AS [IsHadrEnabled], 
SERVERPROPERTY('HadrManagerStatus') AS [HadrManagerStatus],
SERVERPROPERTY('InstanceDefaultDataPath') AS [InstanceDefaultDataPath],
SERVERPROPERTY('InstanceDefaultLogPath') AS [InstanceDefaultLogPath],
SERVERPROPERTY('BuildClrVersion') AS [Build CLR Version],
SERVERPROPERTY('IsXTPSupported') AS [IsXTPSupported],
SERVERPROPERTY('IsPolybaseInstalled') AS [IsPolybaseInstalled],				-- New for SQL Server 2016
SERVERPROPERTY('IsAdvancedAnalyticsInstalled') AS [IsRServicesInstalled];	-- New for SQL Server 2016
------

-- This gives you a lot of useful information about your instance of SQL Server,
-- such as the ProcessID for SQL Server and your collation
-- Note: Some columns will be NULL on older SQL Server builds

-- SERVERPROPERTY (Transact-SQL)
-- https://bit.ly/2eeaXeI



-- Get instance-level configuration values for instance  (Query 4) (Configuration Values)
SELECT name, value, value_in_use, minimum, maximum, [description], is_dynamic, is_advanced
FROM sys.configurations WITH (NOLOCK)
ORDER BY name OPTION (RECOMPILE);
------

-- Focus on these settings:
-- automatic soft-NUMA disabled (should be 0 in most cases)
-- backup checksum default (should be 1)
-- backup compression default (should be 1 in most cases)
-- clr enabled (only enable if it is needed)
-- cost threshold for parallelism (depends on your workload)
-- lightweight pooling (should be zero)
-- max degree of parallelism (depends on your workload and hardware)
-- max server memory (MB) (set to an appropriate value, not the default)
-- optimize for ad hoc workloads (should be 1)
-- priority boost (should be zero)
-- remote admin connections (should be 1)

-- New configuration options for SQL Server 2017
-- clr strict security is new in SQL Server 2017, and is enabled by default

-- sys.configurations (Transact-SQL)
-- https://bit.ly/2HsyDZI


-- Returns a list of all global trace flags that are enabled (Query 5) (Global Trace Flags)
DBCC TRACESTATUS (-1);
------

-- If no global trace flags are enabled, no results will be returned.
-- It is very useful to know what global trace flags are currently enabled as part of the diagnostic process.

-- Common trace flags that should be enabled in most cases
-- TF 3226 - Supresses logging of successful database backup messages to the SQL Server Error Log
--           https://bit.ly/2p6MTjS  

-- TF 6534 - Enables use of native code to improve performance with spatial data
--           https://bit.ly/2HrQUpU         

-- The behavior of TF 1117, 1118 are enabled for tempdb in SQL Server 2016 by default
-- SQL 2016 � It Just Runs Faster: -T1117 and -T1118 changes for TEMPDB and user databases
-- https://bit.ly/2lbNWxK           

-- The behavior of TF 2371 is enabled by default in SQL Server 2016 and newer (in compat level 130 and higher)

-- DBCC TRACEON - Trace Flags (Transact-SQL)
-- https://bit.ly/2FuSvPg





-- Returns status of instant file initialization (Query 6) (IFI Status)
EXEC sys.xp_readerrorlog 0, 1, N'Database Instant File Initialization';
------

-- Lets you determine whether Instant File Initialization (IFI) is enabled for the instance
-- This should be enabled in the vast majority of cases
-- SQL Server 2016 and newer lets you enable this during the SQL server installation process

-- Database Instant File Initialization
-- https://bit.ly/2nTX74y

-- Misconceptions around instant file initialization
-- https://bit.ly/2oBSKgZ



-- SQL Server Process Address space info  (Query 7) (Process Memory)
-- (shows whether locked pages is enabled, among other things)
SELECT physical_memory_in_use_kb/1024 AS [SQL Server Memory Usage (MB)],
	   locked_page_allocations_kb/1024 AS [SQL Server Locked Pages Allocation (MB)],
       large_page_allocations_kb/1024 AS [SQL Server Large Pages Allocation (MB)], 
	   page_fault_count, memory_utilization_percentage, available_commit_limit_kb, 
	   process_physical_memory_low, process_virtual_memory_low
FROM sys.dm_os_process_memory WITH (NOLOCK) OPTION (RECOMPILE);
------

-- You want to see 0 for process_physical_memory_low
-- You want to see 0 for process_virtual_memory_low
-- This indicates that you are not under internal memory pressure
-- If locked_page_allocations_kb > 0, then LPIM is enabled

-- How to enable the "locked pages" feature in SQL Server 2012
-- https://bit.ly/2F5UjOA

-- Memory Management Architecture Guide
-- https://bit.ly/2JKkadC 



-- SQL Server Services information (Query 8) (SQL Server Services Info)
SELECT servicename, process_id, startup_type_desc, status_desc, 
last_startup_time, service_account, is_clustered, cluster_nodename, [filename], 
instant_file_initialization_enabled
FROM sys.dm_server_services WITH (NOLOCK) OPTION (RECOMPILE);
------

-- Tells you the account being used for the SQL Server Service and the SQL Agent Service
-- Shows the process_id, when they were last started, and their current status
-- Also shows whether you are running on a failover cluster instance, and what node you are running on
-- Also shows whether IFI is enabled

-- sys.dm_server_services (Transact-SQL)
-- https://bit.ly/2oKa1Un


-- Last backup information by database  (Query 9) (Last Backup By Database)
SELECT ISNULL(d.[name], bs.[database_name]) AS [Database], d.recovery_model_desc AS [Recovery Model], 
       d.log_reuse_wait_desc AS [Log Reuse Wait Desc],
    MAX(CASE WHEN [type] = 'D' THEN bs.backup_finish_date ELSE NULL END) AS [Last Full Backup],
    MAX(CASE WHEN [type] = 'I' THEN bs.backup_finish_date ELSE NULL END) AS [Last Differential Backup],
    MAX(CASE WHEN [type] = 'L' THEN bs.backup_finish_date ELSE NULL END) AS [Last Log Backup]
FROM sys.databases AS d WITH (NOLOCK)
LEFT OUTER JOIN msdb.dbo.backupset AS bs WITH (NOLOCK)
ON bs.[database_name] = d.[name] 
AND bs.backup_finish_date > GETDATE()- 30
WHERE d.name <> N'tempdb'
GROUP BY ISNULL(d.[name], bs.[database_name]), d.recovery_model_desc, d.log_reuse_wait_desc, d.[name] 
ORDER BY d.recovery_model_desc, d.[name] OPTION (RECOMPILE);
------

-- This helps you spot runaway transaction logs and other issues with your backup schedule


-- Get SQL Server Agent jobs and Category information (Query 10) (SQL Server Agent Jobs)
SELECT sj.name AS [Job Name], sj.[description] AS [Job Description], SUSER_SNAME(sj.owner_sid) AS [Job Owner],
sj.date_created AS [Date Created], sj.[enabled] AS [Job Enabled], 
sj.notify_email_operator_id, sj.notify_level_email, sc.name AS [CategoryName],
s.[enabled] AS [Sched Enabled], js.next_run_date, js.next_run_time
FROM msdb.dbo.sysjobs AS sj WITH (NOLOCK)
INNER JOIN msdb.dbo.syscategories AS sc WITH (NOLOCK)
ON sj.category_id = sc.category_id
LEFT OUTER JOIN msdb.dbo.sysjobschedules AS js WITH (NOLOCK)
ON sj.job_id = js.job_id
LEFT OUTER JOIN msdb.dbo.sysschedules AS s WITH (NOLOCK)
ON js.schedule_id = s.schedule_id
ORDER BY sj.name OPTION (RECOMPILE);
------

-- Gives you some basic information about your SQL Server Agent jobs, who owns them and how they are configured
-- Look for Agent jobs that are not owned by sa
-- Look for jobs that have a notify_email_operator_id set to 0 (meaning no operator)
-- Look for jobs that have a notify_level_email set to 0 (meaning no e-mail is ever sent)
--
-- MSDN sysjobs documentation
-- https://bit.ly/2paDEOP 

-- SQL Server Maintenance Solution
-- https://bit.ly/1pgchQu  


-- Get SQL Server Agent Alert Information (Query 11) (SQL Server Agent Alerts)
SELECT name, event_source, message_id, severity, [enabled], has_notification, 
       delay_between_responses, occurrence_count, last_occurrence_date, last_occurrence_time
FROM msdb.dbo.sysalerts WITH (NOLOCK)
ORDER BY name OPTION (RECOMPILE);
------

-- Gives you some basic information about your SQL Server Agent Alerts 
-- (which are different from SQL Server Agent jobs)
-- Read more about Agent Alerts here: https://bit.ly/2Giz0Xf 



-- Host information (Query 12) (Host Info)
SELECT host_platform, host_distribution, host_release, 
       host_service_pack_level, host_sku, os_language_version 
FROM sys.dm_os_host_info WITH (NOLOCK) OPTION (RECOMPILE); 
------

-- host_release codes (only valid for Windows)
-- 10.0 is either Windows 10 or Windows Server 2016
-- 6.3 is either Windows 8.1 or Windows Server 2012 R2 
-- 6.2 is either Windows 8 or Windows Server 2012


-- host_sku codes (only valid for Windows)
-- 4 is Enterprise Edition
-- 7 is Standard Server Edition
-- 8 is Datacenter Server Edition
-- 10 is Enterprise Server Edition
-- 48 is Professional Edition
-- 161 is Pro for Workstations

-- 1033 for os_language_version is US-English

-- SQL Server 2017 requires Windows Server 2012 or newer

-- Hardware and Software Requirements for Installing SQL Server
-- https://bit.ly/2y3ka5L

-- Using SQL Server in Windows 8 and later versions of Windows operating system
-- https://bit.ly/2F7Ax0P 


-- SQL Server NUMA Node information  (Query 13) (SQL Server NUMA Info)
SELECT node_id, node_state_desc, memory_node_id, processor_group, cpu_count, online_scheduler_count, 
       idle_scheduler_count, active_worker_count, avg_load_balance, resource_monitor_state
FROM sys.dm_os_nodes WITH (NOLOCK) 
WHERE node_state_desc <> N'ONLINE DAC' OPTION (RECOMPILE);
------

-- Gives you some useful information about the composition and relative load on your NUMA nodes
-- You want to see an equal number of schedulers on each NUMA node
-- Watch out if SQL Server 2017 Standard Edition has been installed 
-- on a physical or virtual machine with more than four sockets or more than 24 physical cores

-- sys.dm_os_nodes (Transact-SQL)
-- https://bit.ly/2pn5Mw8

-- Balancing Your Available SQL Server Core Licenses Evenly Across NUMA Nodes
-- https://bit.ly/2vfC4Rq



-- Good basic information about OS memory amounts and state  (Query 14) (System Memory)
SELECT total_physical_memory_kb/1024 AS [Physical Memory (MB)], 
       available_physical_memory_kb/1024 AS [Available Memory (MB)], 
       total_page_file_kb/1024 AS [Total Page File (MB)], 
	   available_page_file_kb/1024 AS [Available Page File (MB)], 
	   system_cache_kb/1024 AS [System Cache (MB)],
       system_memory_state_desc AS [System Memory State]
FROM sys.dm_os_sys_memory WITH (NOLOCK) OPTION (RECOMPILE);
------

-- You want to see "Available physical memory is high" for System Memory State
-- This indicates that you are not under external memory pressure

-- Possible System Memory State values:
-- Available physical memory is high
-- Physical memory usage is steady
-- Available physical memory is low
-- Available physical memory is running low
-- Physical memory state is transitioning

-- sys.dm_os_sys_memory (Transact-SQL)
-- https://bit.ly/2pcV0xq



-- You can skip the next two queries if you know you don't have a clustered instance


-- Get information about your cluster nodes and their status  (Query 15) (Cluster Node Properties)
-- (if your database server is in a failover cluster)
SELECT NodeName, status_description, is_current_owner
FROM sys.dm_os_cluster_nodes WITH (NOLOCK) OPTION (RECOMPILE);
------

-- Knowing which node owns the cluster resources is critical
-- Especially when you are installing Windows or SQL Server updates
-- You will see no results if your instance is not clustered

-- Recommended hotfixes and updates for Windows Server 2012 R2-based failover clusters
-- https://bit.ly/1z5BfCw


-- Get information about any AlwaysOn AG cluster this instance is a part of (Query 16) (AlwaysOn AG Cluster)
SELECT cluster_name, quorum_type_desc, quorum_state_desc
FROM sys.dm_hadr_cluster WITH (NOLOCK) OPTION (RECOMPILE);
------

-- You will see no results if your instance is not using AlwaysOn AGs


-- Good overview of AG health and status (Query 17) (AlwaysOn AG Status)
SELECT ag.name AS [AG Name], ar.replica_server_name, ar.availability_mode_desc, adc.[database_name], 
       drs.is_local, drs.is_primary_replica, drs.synchronization_state_desc, drs.is_commit_participant, 
	   drs.synchronization_health_desc, drs.recovery_lsn, drs.truncation_lsn, drs.last_sent_lsn, 
	   drs.last_sent_time, drs.last_received_lsn, drs.last_received_time, drs.last_hardened_lsn, 
	   drs.last_hardened_time, drs.last_redone_lsn, drs.last_redone_time, drs.log_send_queue_size, 
	   drs.log_send_rate, drs.redo_queue_size, drs.redo_rate, drs.filestream_send_rate, 
	   drs.end_of_log_lsn, drs.last_commit_lsn, drs.last_commit_time, drs.database_state_desc 
FROM sys.dm_hadr_database_replica_states AS drs WITH (NOLOCK)
INNER JOIN sys.availability_databases_cluster AS adc WITH (NOLOCK)
ON drs.group_id = adc.group_id 
AND drs.group_database_id = adc.group_database_id
INNER JOIN sys.availability_groups AS ag WITH (NOLOCK)
ON ag.group_id = drs.group_id
INNER JOIN sys.availability_replicas AS ar WITH (NOLOCK)
ON drs.group_id = ar.group_id 
AND drs.replica_id = ar.replica_id
ORDER BY ag.name, ar.replica_server_name, adc.[database_name] OPTION (RECOMPILE);

-- You will see no results if your instance is not using AlwaysOn AGs

-- SQL Server 2016 � It Just Runs Faster: Always On Availability Groups Turbocharged
-- https://bit.ly/2dn1H6r


-- Hardware information from SQL Server 2017  (Query 18) (Hardware Info)
SELECT cpu_count AS [Logical CPU Count], scheduler_count, 
       (socket_count * cores_per_socket) AS [Physical Core Count], 
       socket_count AS [Socket Count], cores_per_socket, numa_node_count,
       physical_memory_kb/1024 AS [Physical Memory (MB)], 
       max_workers_count AS [Max Workers Count], 
	   affinity_type_desc AS [Affinity Type], 
       sqlserver_start_time AS [SQL Server Start Time], 
	   virtual_machine_type_desc AS [Virtual Machine Type], 
       softnuma_configuration_desc AS [Soft NUMA Configuration], 
	   sql_memory_model_desc, process_physical_affinity -- New in SQL Server 2017
FROM sys.dm_os_sys_info WITH (NOLOCK) OPTION (RECOMPILE);
------

-- Gives you some good basic hardware information about your database server
-- Note: virtual_machine_type_desc of HYPERVISOR does not automatically mean you are running SQL Server inside of a VM
-- It merely indicates that you have a hypervisor running on your host

-- sys.dm_os_sys_info (Transact-SQL)
-- https://bit.ly/2pczOYs

-- Soft NUMA configuration was a new column for SQL Server 2016
-- OFF = Soft-NUMA feature is OFF
-- ON = SQL Server automatically determines the NUMA node sizes for Soft-NUMA
-- MANUAL = Manually configured soft-NUMA

-- Configure SQL Server to Use Soft-NUMA (SQL Server)
-- https://bit.ly/2HTpKJt

-- sql_memory_model_desc values (Added in SQL Server 2016 SP1)
-- CONVENTIONAL
-- LOCK_PAGES
-- LARGE_PAGES
   

-- Get System Manufacturer and model number from SQL Server Error log (Query 19) (System Manufacturer)
EXEC sys.xp_readerrorlog 0, 1, N'Manufacturer';
------ 

-- This can help you determine the capabilities and capacities of your database server
-- Can also be used to confirm if you are running in a VM
-- This query might take a few seconds if you have not recycled your error log recently
-- This query will return no results if your error log has been recycled since the instance was started


-- Get BIOS date from Windows Registry (Query 20) (BIOS Date)
EXEC sys.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'HARDWARE\DESCRIPTION\System\BIOS', N'BiosReleaseDate';
------

-- Helps you understand whether the main system BIOS is up to date, and the possible age of the hardware
-- Not as useful for virtualization


-- Get processor description from Windows Registry  (Query 21) (Processor Description)
EXEC sys.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'HARDWARE\DESCRIPTION\System\CentralProcessor\0', N'ProcessorNameString';
------

-- Gives you the model number and rated clock speed of your processor(s)
-- Your processors may be running at less than the rated clock speed due
-- to the Windows Power Plan or hardware power management

-- You can use CPU-Z to get your actual CPU core speed and a lot of other useful information
-- https://bit.ly/QhR6xF

-- You can learn more about processor selection for SQL Server by following this link
-- https://bit.ly/2F3aVlP



-- See if buffer pool extension (BPE) is enabled (Query 22) (BPE Configuration)
SELECT [path], state_description, current_size_in_kb, 
CAST(current_size_in_kb/1048576.0 AS DECIMAL(10,2)) AS [Size (GB)]
FROM sys.dm_os_buffer_pool_extension_configuration WITH (NOLOCK) OPTION (RECOMPILE);
------

-- BPE is available in both Standard Edition and Enterprise Edition
-- It is a more interesting feature for Standard Edition

-- Buffer Pool Extension to SSDs in SQL Server 2014
-- https://bit.ly/1bm08m8

-- Buffer Pool Extension
-- https://bit.ly/2oBuieO



-- Look at buffer descriptors to see BPE usage by database (Query 23) (BPE Usage) 
SELECT DB_NAME(database_id) AS [Database Name], COUNT(page_id) AS [Page Count],
CAST(COUNT(*)/128.0 AS DECIMAL(10, 2)) AS [Buffer size(MB)], 
AVG(read_microsec) AS [Avg Read Time (microseconds)]
FROM sys.dm_os_buffer_descriptors WITH (NOLOCK)
WHERE database_id <> 32767
AND is_in_bpool_extension = 1
GROUP BY DB_NAME(database_id) 
ORDER BY [Buffer size(MB)] DESC OPTION (RECOMPILE);
------

-- You will see no results if BPE is not enabled or if there is no BPE usage


-- Get information on location, time and size of any memory dumps from SQL Server  (Query 24) (Memory Dump Info)
SELECT [filename], creation_time, size_in_bytes/1048576.0 AS [Size (MB)]
FROM sys.dm_server_memory_dumps WITH (NOLOCK) 
ORDER BY creation_time DESC OPTION (RECOMPILE);
------

-- This will not return any rows if you have 
-- not had any memory dumps (which is a good thing)

-- sys.dm_server_memory_dumps (Transact-SQL)
-- https://bit.ly/2elwWll



-- Look at Suspect Pages table (Query 25) (Suspect Pages)
SELECT DB_NAME(database_id) AS [Database Name], [file_id], page_id, 
       event_type, error_count, last_update_date 
FROM msdb.dbo.suspect_pages WITH (NOLOCK)
ORDER BY database_id OPTION (RECOMPILE);
------

-- event_type value descriptions
-- 1 = 823 error caused by an operating system CRC error
--     or 824 error other than a bad checksum or a torn page (for example, a bad page ID)
-- 2 = Bad checksum
-- 3 = Torn page
-- 4 = Restored (The page was restored after it was marked bad)
-- 5 = Repaired (DBCC repaired the page)
-- 7 = Deallocated by DBCC

-- Ideally, this query returns no results. The table is limited to 1000 rows.
-- If you do get results here, you should do further investigation to determine the root cause

-- Manage the suspect_pages Table
-- https://bit.ly/2Fvr1c9


-- Get number of data files in tempdb database (Query 26) (TempDB Data Files)
EXEC sys.xp_readerrorlog 0, 1, N'The tempdb database has';
------

-- Get the number of data files in the tempdb database
-- 4-8 data files that are all the same size is a good starting point
-- This query will return no results if your error log has been recycled since the instance was last started


-- File names and paths for all user and system databases on instance  (Query 27) (Database Filenames and Paths)
SELECT DB_NAME([database_id]) AS [Database Name], 
       [file_id], [name], physical_name, [type_desc], state_desc,
	   is_percent_growth, growth,
	   CONVERT(bigint, growth/128.0) AS [Growth in MB], 
       CONVERT(bigint, size/128.0) AS [Total Size in MB]
FROM sys.master_files WITH (NOLOCK)
ORDER BY DB_NAME([database_id]), [file_id] OPTION (RECOMPILE);
------

-- Things to look at:
-- Are data files and log files on different drives?
-- Is everything on the C: drive?
-- Is tempdb on dedicated drives?
-- Is there only one tempdb data file?
-- Are all of the tempdb data files the same size?
-- Are there multiple data files for user databases?
-- Is percent growth enabled for any files (which is bad)?


-- Drive information for all fixed drives visible to the operating system (Query 28) (Fixed Drives)
SELECT fixed_drive_path, drive_type_desc, 
CONVERT(DECIMAL(18,2), free_space_in_bytes/1073741824.0) AS [Available Space (GB)]
FROM sys.dm_os_enumerate_fixed_drives WITH (NOLOCK) OPTION (RECOMPILE);
------

-- This shows all of your fixed drives, not just LUNs with SQL Server database files



-- Volume info for all LUNS that have database files on the current instance (Query 29) (Volume Info)
SELECT DISTINCT vs.volume_mount_point, vs.file_system_type, vs.logical_volume_name, 
CONVERT(DECIMAL(18,2), vs.total_bytes/1073741824.0) AS [Total Size (GB)],
CONVERT(DECIMAL(18,2), vs.available_bytes/1073741824.0) AS [Available Size (GB)],  
CONVERT(DECIMAL(18,2), vs.available_bytes * 1. / vs.total_bytes * 100.) AS [Space Free %],
vs.supports_compression, vs.is_compressed, 
vs.supports_sparse_files, vs.supports_alternate_streams
FROM sys.master_files AS f WITH (NOLOCK)
CROSS APPLY sys.dm_os_volume_stats(f.database_id, f.[file_id]) AS vs 
ORDER BY vs.volume_mount_point OPTION (RECOMPILE);
------

-- Shows you the total and free space on the LUNs where you have database files
-- Being low on free space can negatively affect performance

-- sys.dm_os_volume_stats (Transact-SQL)
-- https://bit.ly/2oBPNNr



-- Drive level latency information (Query 30) (Drive Level Latency)
-- Based on code from Jimmy May
SELECT tab.[Drive], tab.volume_mount_point AS [Volume Mount Point], 
	CASE 
		WHEN num_of_reads = 0 THEN 0 
		ELSE (io_stall_read_ms/num_of_reads) 
	END AS [Read Latency],
	CASE 
		WHEN num_of_writes = 0 THEN 0 
		ELSE (io_stall_write_ms/num_of_writes) 
	END AS [Write Latency],
	CASE 
		WHEN (num_of_reads = 0 AND num_of_writes = 0) THEN 0 
		ELSE (io_stall/(num_of_reads + num_of_writes)) 
	END AS [Overall Latency],
	CASE 
		WHEN num_of_reads = 0 THEN 0 
		ELSE (num_of_bytes_read/num_of_reads) 
	END AS [Avg Bytes/Read],
	CASE 
		WHEN num_of_writes = 0 THEN 0 
		ELSE (num_of_bytes_written/num_of_writes) 
	END AS [Avg Bytes/Write],
	CASE 
		WHEN (num_of_reads = 0 AND num_of_writes = 0) THEN 0 
		ELSE ((num_of_bytes_read + num_of_bytes_written)/(num_of_reads + num_of_writes)) 
	END AS [Avg Bytes/Transfer]
FROM (SELECT LEFT(UPPER(mf.physical_name), 2) AS Drive, SUM(num_of_reads) AS num_of_reads,
	         SUM(io_stall_read_ms) AS io_stall_read_ms, SUM(num_of_writes) AS num_of_writes,
	         SUM(io_stall_write_ms) AS io_stall_write_ms, SUM(num_of_bytes_read) AS num_of_bytes_read,
	         SUM(num_of_bytes_written) AS num_of_bytes_written, SUM(io_stall) AS io_stall, vs.volume_mount_point 
      FROM sys.dm_io_virtual_file_stats(NULL, NULL) AS vfs
      INNER JOIN sys.master_files AS mf WITH (NOLOCK)
      ON vfs.database_id = mf.database_id AND vfs.file_id = mf.file_id
	  CROSS APPLY sys.dm_os_volume_stats(mf.database_id, mf.[file_id]) AS vs 
      GROUP BY LEFT(UPPER(mf.physical_name), 2), vs.volume_mount_point) AS tab
ORDER BY [Overall Latency] OPTION (RECOMPILE);
------

-- Shows you the drive-level latency for reads and writes, in milliseconds
-- Latency above 30-40ms is usually a problem
-- These latency numbers include all file activity against all SQL Server 
-- database files on each drive since SQL Server was last started


-- Calculates average stalls per read, per write, and per total input/output for each database file  (Query 31) (IO Stalls by File)
SELECT DB_NAME(fs.database_id) AS [Database Name], CAST(fs.io_stall_read_ms/(1.0 + fs.num_of_reads) AS NUMERIC(10,1)) AS [avg_read_stall_ms],
CAST(fs.io_stall_write_ms/(1.0 + fs.num_of_writes) AS NUMERIC(10,1)) AS [avg_write_stall_ms],
CAST((fs.io_stall_read_ms + fs.io_stall_write_ms)/(1.0 + fs.num_of_reads + fs.num_of_writes) AS NUMERIC(10,1)) AS [avg_io_stall_ms],
CONVERT(DECIMAL(18,2), mf.size/128.0) AS [File Size (MB)], mf.physical_name, mf.type_desc, fs.io_stall_read_ms, fs.num_of_reads, 
fs.io_stall_write_ms, fs.num_of_writes, fs.io_stall_read_ms + fs.io_stall_write_ms AS [io_stalls], fs.num_of_reads + fs.num_of_writes AS [total_io],
io_stall_queued_read_ms AS [Resource Governor Total Read IO Latency (ms)], io_stall_queued_write_ms AS [Resource Governor Total Write IO Latency (ms)] 
FROM sys.dm_io_virtual_file_stats(null,null) AS fs
INNER JOIN sys.master_files AS mf WITH (NOLOCK)
ON fs.database_id = mf.database_id
AND fs.[file_id] = mf.[file_id]
ORDER BY avg_io_stall_ms DESC OPTION (RECOMPILE);
------

-- Helps determine which database files on the entire instance have the most I/O bottlenecks
-- This can help you decide whether certain LUNs are overloaded and whether you might
-- want to move some files to a different location or perhaps improve your I/O performance
-- These latency numbers include all file activity against each SQL Server 
-- database file since SQL Server was last started


-- Look for I/O requests taking longer than 15 seconds in the six most recent SQL Server Error Logs (Query 32) (IO Warnings)
CREATE TABLE #IOWarningResults(LogDate datetime, ProcessInfo sysname, LogText nvarchar(1000));

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 0, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 1, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 2, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 3, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 4, 1, N'taking longer than 15 seconds';

	INSERT INTO #IOWarningResults 
	EXEC xp_readerrorlog 5, 1, N'taking longer than 15 seconds';

SELECT LogDate, ProcessInfo, LogText
FROM #IOWarningResults
ORDER BY LogDate DESC;

DROP TABLE #IOWarningResults;
------  

-- Finding 15 second I/O warnings in the SQL Server Error Log is useful evidence of
-- poor I/O performance (which might have many different causes)
-- Look to see if you see any patterns in the results (same files, same drives, same time of day, etc.)

-- Diagnostics in SQL Server help detect stalled and stuck I/O operations
-- https://bit.ly/2qtaw73



-- Recovery model, log reuse wait description, log file size, log usage size  (Query 33) (Database Properties)
-- and compatibility level for all databases on instance
SELECT db.[name] AS [Database Name], SUSER_SNAME(db.owner_sid) AS [Database Owner], db.recovery_model_desc AS [Recovery Model], 
db.state_desc, db.containment_desc, db.log_reuse_wait_desc AS [Log Reuse Wait Description], 
CONVERT(DECIMAL(18,2), ls.cntr_value/1024.0) AS [Log Size (MB)], CONVERT(DECIMAL(18,2), lu.cntr_value/1024.0) AS [Log Used (MB)],
CAST(CAST(lu.cntr_value AS FLOAT) / CAST(ls.cntr_value AS FLOAT)AS DECIMAL(18,2)) * 100 AS [Log Used %], 
db.[compatibility_level] AS [DB Compatibility Level], 
db.is_mixed_page_allocation_on, db.page_verify_option_desc AS [Page Verify Option], 
db.is_auto_create_stats_on, db.is_auto_update_stats_on, db.is_auto_update_stats_async_on, db.is_parameterization_forced, 
db.snapshot_isolation_state_desc, db.is_read_committed_snapshot_on, db.is_auto_close_on, db.is_auto_shrink_on, 
db.target_recovery_time_in_seconds, db.is_cdc_enabled, db.is_published, db.is_distributor,
db.group_database_id, db.replica_id,db.is_memory_optimized_elevate_to_snapshot_on, 
db.delayed_durability_desc, db.is_auto_create_stats_incremental_on,
db.is_query_store_on, db.is_sync_with_backup, db.is_temporal_history_retention_enabled,
db.is_supplemental_logging_enabled, db.is_remote_data_archive_enabled,
db.is_encrypted, de.encryption_state, de.percent_complete, de.key_algorithm, de.key_length      
FROM sys.databases AS db WITH (NOLOCK)
INNER JOIN sys.dm_os_performance_counters AS lu WITH (NOLOCK)
ON db.name = lu.instance_name
INNER JOIN sys.dm_os_performance_counters AS ls WITH (NOLOCK)
ON db.name = ls.instance_name
LEFT OUTER JOIN sys.dm_database_encryption_keys AS de WITH (NOLOCK)
ON db.database_id = de.database_id
WHERE lu.counter_name LIKE N'Log File(s) Used Size (KB)%' 
AND ls.counter_name LIKE N'Log File(s) Size (KB)%'
AND ls.cntr_value > 0 
ORDER BY db.[name] OPTION (RECOMPILE);
------

-- Things to look at:
-- How many databases are on the instance?
-- What recovery models are they using?
-- What is the log reuse wait description?
-- How full are the transaction logs?
-- What compatibility level are the databases on? 
-- What is the Page Verify Option? (should be CHECKSUM)
-- Is Auto Update Statistics Asynchronously enabled?
-- Is Delayed Durability enabled
-- Make sure auto_shrink and auto_close are not enabled!

-- is_mixed_page_allocation_on is a new property for SQL Server 2016. Equivalent to TF 1118 for a user database
-- SQL Server 2016: Changes in default behavior for autogrow and allocations for tempdb and user databases
-- https://bit.ly/2evRZSR

-- A non-zero value for target_recovery_time_in_seconds means that indirect checkpoint is enabled 
-- If the setting has a zero value it indicates that automatic checkpoint is enabled

-- Changes in SQL Server 2016 Checkpoint Behavior
-- https://bit.ly/2pdggk3


-- Missing Indexes for all databases by Index Advantage  (Query 34) (Missing Indexes All Databases)
SELECT CONVERT(decimal(18,2), user_seeks * avg_total_user_cost * (avg_user_impact * 0.01)) AS [index_advantage],
FORMAT(migs.last_user_seek, 'yyyy-MM-dd HH:mm:ss') AS [last_user_seek], 
mid.[statement] AS [Database.Schema.Table],
COUNT(1) OVER(PARTITION BY mid.[statement]) AS [missing_indexes_for_table],
COUNT(1) OVER(PARTITION BY mid.[statement], equality_columns) AS [similar_missing_indexes_for_table],
mid.equality_columns, mid.inequality_columns, mid.included_columns,
migs.unique_compiles, migs.user_seeks, 
CONVERT(decimal(18,2), migs.avg_total_user_cost) AS [avg_total_user_cost], migs.avg_user_impact 
FROM sys.dm_db_missing_index_group_stats AS migs WITH (NOLOCK)
INNER JOIN sys.dm_db_missing_index_groups AS mig WITH (NOLOCK)
ON migs.group_handle = mig.index_group_handle
INNER JOIN sys.dm_db_missing_index_details AS mid WITH (NOLOCK)
ON mig.index_handle = mid.index_handle
ORDER BY index_advantage DESC OPTION (RECOMPILE);
------

-- Getting missing index information for all of the databases on the instance is very useful
-- Look at last user seek time, number of user seeks to help determine source and importance
-- Also look at avg_user_impact and avg_total_user_cost to help determine importance
-- SQL Server is overly eager to add included columns, so beware
-- Do not just blindly add indexes that show up from this query!!!

-- SQL Server Index Design Guide
-- https://bit.ly/2qtZr4N



-- Get VLF Counts for all databases on the instance (Query 35) (VLF Counts)
SELECT�[name] AS [Database Name],�[VLF Count] 
FROM�sys.databases�AS db WITH (NOLOCK)
CROSS APPLY�(SELECT�file_id, COUNT(*)�AS [VLF Count]�
			 FROM sys.dm_db_log_info(db.database_id) 
����������   GROUP BY�file_id)�AS li
ORDER BY [VLF Count] DESC  OPTION (RECOMPILE);
------

-- High VLF counts can affect write performance to the log file
-- and they can make full database restores and crash recovery take much longer
-- Try to keep your VLF counts under 200 in most cases (depending on log file size)

-- Important change to VLF creation algorithm in SQL Server 2014
-- https://bit.ly/2Hsjbg4

-- SQL Server Transaction Log Architecture and Management Guide
-- https://bit.ly/2JjmQRZ



-- Get CPU utilization by database (Query 36) (CPU Usage by Database)
WITH DB_CPU_Stats
AS
(SELECT pa.DatabaseID, DB_Name(pa.DatabaseID) AS [Database Name], SUM(qs.total_worker_time/1000) AS [CPU_Time_Ms]
 FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
 CROSS APPLY (SELECT CONVERT(int, value) AS [DatabaseID] 
              FROM sys.dm_exec_plan_attributes(qs.plan_handle)
              WHERE attribute = N'dbid') AS pa
 GROUP BY DatabaseID)
SELECT ROW_NUMBER() OVER(ORDER BY [CPU_Time_Ms] DESC) AS [CPU Rank],
       [Database Name], [CPU_Time_Ms] AS [CPU Time (ms)], 
       CAST([CPU_Time_Ms] * 1.0 / SUM([CPU_Time_Ms]) OVER() * 100.0 AS DECIMAL(5, 2)) AS [CPU Percent]
FROM DB_CPU_Stats
WHERE DatabaseID <> 32767 -- ResourceDB
ORDER BY [CPU Rank] OPTION (RECOMPILE);
------

-- Helps determine which database is using the most CPU resources on the instance


-- Get I/O utilization by database (Query 37) (IO Usage By Database)
WITH Aggregate_IO_Statistics
AS
(SELECT DB_NAME(database_id) AS [Database Name],
CAST(SUM(num_of_bytes_read + num_of_bytes_written)/1048576 AS DECIMAL(12, 2)) AS io_in_mb
FROM sys.dm_io_virtual_file_stats(NULL, NULL) AS [DM_IO_STATS]
GROUP BY database_id)
SELECT ROW_NUMBER() OVER(ORDER BY io_in_mb DESC) AS [I/O Rank], [Database Name], io_in_mb AS [Total I/O (MB)],
       CAST(io_in_mb/ SUM(io_in_mb) OVER() * 100.0 AS DECIMAL(5,2)) AS [I/O Percent]
FROM Aggregate_IO_Statistics
ORDER BY [I/O Rank] OPTION (RECOMPILE);
------

-- Helps determine which database is using the most I/O resources on the instance


-- Get total buffer usage by database for current instance  (Query 38) (Total Buffer Usage by Database)
-- This make take some time to run on a busy instance
WITH AggregateBufferPoolUsage
AS
(SELECT DB_NAME(database_id) AS [Database Name],
CAST(COUNT(*) * 8/1024.0 AS DECIMAL (10,2))  AS [CachedSize]
FROM sys.dm_os_buffer_descriptors WITH (NOLOCK)
WHERE database_id <> 32767 -- ResourceDB
GROUP BY DB_NAME(database_id))
SELECT ROW_NUMBER() OVER(ORDER BY CachedSize DESC) AS [Buffer Pool Rank], [Database Name], CachedSize AS [Cached Size (MB)],
       CAST(CachedSize / SUM(CachedSize) OVER() * 100.0 AS DECIMAL(5,2)) AS [Buffer Pool Percent]
FROM AggregateBufferPoolUsage
ORDER BY [Buffer Pool Rank] OPTION (RECOMPILE);
------

-- Tells you how much memory (in the buffer pool) 
-- is being used by each database on the instance


-- Get tempdb version store space usage by database (Query 39) (Version Store Space Usage)
SELECT DB_NAME(database_id) AS [Database Name],
       reserved_page_count AS [Version Store Reserved Page Count], 
	   reserved_space_kb/1024 AS [Version Store Reserved Space (MB)] 
FROM sys.dm_tran_version_store_space_usage WITH (NOLOCK) 
ORDER BY reserved_space_kb/1024 DESC OPTION (RECOMPILE);
------  

-- sys.dm_tran_version_store_space_usage (Transact-SQL)
-- https://bit.ly/2vh3Bmk




-- Clear Wait Stats with this command
-- DBCC SQLPERF('sys.dm_os_wait_stats', CLEAR);

-- Isolate top waits for server instance since last restart or wait statistics clear  (Query 40) (Top Waits)
WITH [Waits] 
AS (SELECT wait_type, wait_time_ms/ 1000.0 AS [WaitS],
          (wait_time_ms - signal_wait_time_ms) / 1000.0 AS [ResourceS],
           signal_wait_time_ms / 1000.0 AS [SignalS],
           waiting_tasks_count AS [WaitCount],
           100.0 *  wait_time_ms / SUM (wait_time_ms) OVER() AS [Percentage],
           ROW_NUMBER() OVER(ORDER BY wait_time_ms DESC) AS [RowNum]
    FROM sys.dm_os_wait_stats WITH (NOLOCK)
    WHERE [wait_type] NOT IN (
        N'BROKER_EVENTHANDLER', N'BROKER_RECEIVE_WAITFOR', N'BROKER_TASK_STOP',
		N'BROKER_TO_FLUSH', N'BROKER_TRANSMITTER', N'CHECKPOINT_QUEUE',
        N'CHKPT', N'CLR_AUTO_EVENT', N'CLR_MANUAL_EVENT', N'CLR_SEMAPHORE', N'CXCONSUMER',
        N'DBMIRROR_DBM_EVENT', N'DBMIRROR_EVENTS_QUEUE', N'DBMIRROR_WORKER_QUEUE',
		N'DBMIRRORING_CMD', N'DIRTY_PAGE_POLL', N'DISPATCHER_QUEUE_SEMAPHORE',
        N'EXECSYNC', N'FSAGENT', N'FT_IFTS_SCHEDULER_IDLE_WAIT', N'FT_IFTSHC_MUTEX',
        N'HADR_CLUSAPI_CALL', N'HADR_FILESTREAM_IOMGR_IOCOMPLETION', N'HADR_LOGCAPTURE_WAIT', 
		N'HADR_NOTIFICATION_DEQUEUE', N'HADR_TIMER_TASK', N'HADR_WORK_QUEUE',
        N'KSOURCE_WAKEUP', N'LAZYWRITER_SLEEP', N'LOGMGR_QUEUE', 
		N'MEMORY_ALLOCATION_EXT', N'ONDEMAND_TASK_QUEUE',
		N'PARALLEL_REDO_DRAIN_WORKER', N'PARALLEL_REDO_LOG_CACHE', N'PARALLEL_REDO_TRAN_LIST',
		N'PARALLEL_REDO_WORKER_SYNC', N'PARALLEL_REDO_WORKER_WAIT_WORK',
		N'PREEMPTIVE_HADR_LEASE_MECHANISM', N'PREEMPTIVE_SP_SERVER_DIAGNOSTICS',
		N'PREEMPTIVE_OS_LIBRARYOPS', N'PREEMPTIVE_OS_COMOPS', N'PREEMPTIVE_OS_CRYPTOPS',
		N'PREEMPTIVE_OS_PIPEOPS', N'PREEMPTIVE_OS_AUTHENTICATIONOPS',
		N'PREEMPTIVE_OS_GENERICOPS', N'PREEMPTIVE_OS_VERIFYTRUST',
		N'PREEMPTIVE_OS_FILEOPS', N'PREEMPTIVE_OS_DEVICEOPS', N'PREEMPTIVE_OS_QUERYREGISTRY',
		N'PREEMPTIVE_OS_WRITEFILE',
		N'PREEMPTIVE_XE_CALLBACKEXECUTE', N'PREEMPTIVE_XE_DISPATCHER',
		N'PREEMPTIVE_XE_GETTARGETSTATE', N'PREEMPTIVE_XE_SESSIONCOMMIT',
		N'PREEMPTIVE_XE_TARGETINIT', N'PREEMPTIVE_XE_TARGETFINALIZE',
        N'PWAIT_ALL_COMPONENTS_INITIALIZED', N'PWAIT_DIRECTLOGCONSUMER_GETNEXT',
		N'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP',
		N'QDS_ASYNC_QUEUE',
        N'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP', N'REQUEST_FOR_DEADLOCK_SEARCH',
		N'RESOURCE_QUEUE', N'SERVER_IDLE_CHECK', N'SLEEP_BPOOL_FLUSH', N'SLEEP_DBSTARTUP',
		N'SLEEP_DCOMSTARTUP', N'SLEEP_MASTERDBREADY', N'SLEEP_MASTERMDREADY',
        N'SLEEP_MASTERUPGRADED', N'SLEEP_MSDBSTARTUP', N'SLEEP_SYSTEMTASK', N'SLEEP_TASK',
        N'SLEEP_TEMPDBSTARTUP', N'SNI_HTTP_ACCEPT', N'SP_SERVER_DIAGNOSTICS_SLEEP',
		N'SQLTRACE_BUFFER_FLUSH', N'SQLTRACE_INCREMENTAL_FLUSH_SLEEP', N'SQLTRACE_WAIT_ENTRIES',
		N'WAIT_FOR_RESULTS', N'WAITFOR', N'WAITFOR_TASKSHUTDOWN', N'WAIT_XTP_HOST_WAIT',
		N'WAIT_XTP_OFFLINE_CKPT_NEW_LOG', N'WAIT_XTP_CKPT_CLOSE', N'WAIT_XTP_RECOVERY',
		N'XE_BUFFERMGR_ALLPROCESSED_EVENT', N'XE_DISPATCHER_JOIN',
        N'XE_DISPATCHER_WAIT', N'XE_LIVE_TARGET_TVF', N'XE_TIMER_EVENT')
    AND waiting_tasks_count > 0)
SELECT
    MAX (W1.wait_type) AS [WaitType],
	CAST (MAX (W1.Percentage) AS DECIMAL (5,2)) AS [Wait Percentage],
	CAST ((MAX (W1.WaitS) / MAX (W1.WaitCount)) AS DECIMAL (16,4)) AS [AvgWait_Sec],
    CAST ((MAX (W1.ResourceS) / MAX (W1.WaitCount)) AS DECIMAL (16,4)) AS [AvgRes_Sec],
    CAST ((MAX (W1.SignalS) / MAX (W1.WaitCount)) AS DECIMAL (16,4)) AS [AvgSig_Sec], 
    CAST (MAX (W1.WaitS) AS DECIMAL (16,2)) AS [Wait_Sec],
    CAST (MAX (W1.ResourceS) AS DECIMAL (16,2)) AS [Resource_Sec],
    CAST (MAX (W1.SignalS) AS DECIMAL (16,2)) AS [Signal_Sec],
    MAX (W1.WaitCount) AS [Wait Count],
	CAST (N'https://www.sqlskills.com/help/waits/' + W1.wait_type AS XML) AS [Help/Info URL]
FROM Waits AS W1
INNER JOIN Waits AS W2
ON W2.RowNum <= W1.RowNum
GROUP BY W1.RowNum, W1.wait_type
HAVING SUM (W2.Percentage) - MAX (W1.Percentage) < 99 -- percentage threshold
OPTION (RECOMPILE);
------

-- Cumulative wait stats are not as useful on an idle instance that is not under load or performance pressure

-- SQL Server Wait Types Library (Paul Randal)
-- https://bit.ly/2ePzYO2

-- The SQL Server Wait Type Repository
-- https://bit.ly/1afzfjC

-- Wait statistics, or please tell me where it hurts
-- https://bit.ly/2wsQHQE

-- SQL Server 2005 Performance Tuning using the Waits and Queues
-- https://bit.ly/1o2NFoF

-- sys.dm_os_wait_stats (Transact-SQL)
-- https://bit.ly/2Hjq9Yl



-- Get a count of SQL connections by IP address (Query 41) (Connection Counts by IP Address)
SELECT ec.client_net_address, es.[program_name], es.[host_name], es.login_name, 
COUNT(ec.session_id) AS [connection count] 
FROM sys.dm_exec_sessions AS es WITH (NOLOCK) 
INNER JOIN sys.dm_exec_connections AS ec WITH (NOLOCK) 
ON es.session_id = ec.session_id 
GROUP BY ec.client_net_address, es.[program_name], es.[host_name], es.login_name  
ORDER BY ec.client_net_address, es.[program_name] OPTION (RECOMPILE);
------

-- This helps you figure where your database load is coming from
-- and verifies connectivity from other machines

-- Solving Connectivity errors to SQL Server
-- https://bit.ly/2EgzoD0



-- Get Average Task Counts (run multiple times)  (Query 42) (Avg Task Counts)
SELECT AVG(current_tasks_count) AS [Avg Task Count], 
AVG(work_queue_count) AS [Avg Work Queue Count],
AVG(runnable_tasks_count) AS [Avg Runnable Task Count],
AVG(pending_disk_io_count) AS [Avg Pending DiskIO Count]
FROM sys.dm_os_schedulers WITH (NOLOCK)
WHERE scheduler_id < 255 OPTION (RECOMPILE);
------

-- Sustained values above 10 suggest further investigation in that area
-- High Avg Task Counts are often caused by blocking/deadlocking or other resource contention

-- Sustained values above 1 suggest further investigation in that area
-- High Avg Runnable Task Counts are a good sign of CPU pressure
-- High Avg Pending DiskIO Counts are a sign of disk pressure

-- How to Do Some Very Basic SQL Server Monitoring
-- https://bit.ly/2q3Btgt



-- Detect blocking (run multiple times)  (Query 43) (Detect Blocking)
SELECT t1.resource_type AS [lock type], DB_NAME(resource_database_id) AS [database],
t1.resource_associated_entity_id AS [blk object],t1.request_mode AS [lock req],  -- lock requested
t1.request_session_id AS [waiter sid], t2.wait_duration_ms AS [wait time],       -- spid of waiter  
(SELECT [text] FROM sys.dm_exec_requests AS r WITH (NOLOCK)                      -- get sql for waiter
CROSS APPLY sys.dm_exec_sql_text(r.[sql_handle]) 
WHERE r.session_id = t1.request_session_id) AS [waiter_batch],
(SELECT SUBSTRING(qt.[text],r.statement_start_offset/2, 
    (CASE WHEN r.statement_end_offset = -1 
    THEN LEN(CONVERT(nvarchar(max), qt.[text])) * 2 
    ELSE r.statement_end_offset END - r.statement_start_offset)/2) 
FROM sys.dm_exec_requests AS r WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(r.[sql_handle]) AS qt
WHERE r.session_id = t1.request_session_id) AS [waiter_stmt],					-- statement blocked
t2.blocking_session_id AS [blocker sid],										-- spid of blocker
(SELECT [text] FROM sys.sysprocesses AS p										-- get sql for blocker
CROSS APPLY sys.dm_exec_sql_text(p.[sql_handle]) 
WHERE p.spid = t2.blocking_session_id) AS [blocker_batch]
FROM sys.dm_tran_locks AS t1 WITH (NOLOCK)
INNER JOIN sys.dm_os_waiting_tasks AS t2 WITH (NOLOCK)
ON t1.lock_owner_address = t2.resource_address OPTION (RECOMPILE);
------

-- Helps troubleshoot blocking and deadlocking issues
-- The results will change from second to second on a busy system
-- You should run this query multiple times when you see signs of blocking



-- Get CPU Utilization History for last 256 minutes (in one minute intervals)  (Query 44) (CPU Utilization History)
DECLARE @ts_now bigint = (SELECT cpu_ticks/(cpu_ticks/ms_ticks) FROM sys.dm_os_sys_info WITH (NOLOCK)); 

SELECT TOP(256) SQLProcessUtilization AS [SQL Server Process CPU Utilization], 
               SystemIdle AS [System Idle Process], 
               100 - SystemIdle - SQLProcessUtilization AS [Other Process CPU Utilization], 
               DATEADD(ms, -1 * (@ts_now - [timestamp]), GETDATE()) AS [Event Time] 
FROM (SELECT record.value('(./Record/@id)[1]', 'int') AS record_id, 
			record.value('(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]', 'int') 
			AS [SystemIdle], 
			record.value('(./Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization)[1]', 'int') 
			AS [SQLProcessUtilization], [timestamp] 
	  FROM (SELECT [timestamp], CONVERT(xml, record) AS [record] 
			FROM sys.dm_os_ring_buffers WITH (NOLOCK)
			WHERE ring_buffer_type = N'RING_BUFFER_SCHEDULER_MONITOR' 
			AND record LIKE N'%<SystemHealth>%') AS x) AS y 
ORDER BY record_id DESC OPTION (RECOMPILE);
------

-- Look at the trend over the entire period 
-- Also look at high sustained 'Other Process' CPU Utilization values
-- Note: This query sometimes gives inaccurate results (negative values)
-- on high core count (> 64 cores) systems


-- Get top total worker time queries for entire instance (Query 45) (Top Worker Time Queries)
SELECT TOP(50) DB_NAME(t.[dbid]) AS [Database Name], 
REPLACE(REPLACE(LEFT(t.[text], 255), CHAR(10),''), CHAR(13),'') AS [Short Query Text],  
qs.total_worker_time AS [Total Worker Time], qs.min_worker_time AS [Min Worker Time],
qs.total_worker_time/qs.execution_count AS [Avg Worker Time], 
qs.max_worker_time AS [Max Worker Time], 
qs.min_elapsed_time AS [Min Elapsed Time], 
qs.total_elapsed_time/qs.execution_count AS [Avg Elapsed Time], 
qs.max_elapsed_time AS [Max Elapsed Time],
qs.min_logical_reads AS [Min Logical Reads],
qs.total_logical_reads/qs.execution_count AS [Avg Logical Reads],
qs.max_logical_reads AS [Max Logical Reads], 
qs.execution_count AS [Execution Count],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index], 
qs.creation_time AS [Creation Time]
--,t.[text] AS [Query Text], qp.query_plan AS [Query Plan] -- uncomment out these columns if not copying results to Excel
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t 
CROSS APPLY sys.dm_exec_query_plan(plan_handle) AS qp 
ORDER BY qs.total_worker_time DESC OPTION (RECOMPILE);
------


-- Helps you find the most expensive queries from a CPU perspective across the entire instance
-- Can also help track down parameter sniffing issues



-- Page Life Expectancy (PLE) value for each NUMA node in current instance  (Query 46) (PLE by NUMA Node)
SELECT @@SERVERNAME AS [Server Name], RTRIM([object_name]) AS [Object Name], instance_name, cntr_value AS [Page Life Expectancy]
FROM sys.dm_os_performance_counters WITH (NOLOCK)
WHERE [object_name] LIKE N'%Buffer Node%' -- Handles named instances
AND counter_name = N'Page life expectancy' OPTION (RECOMPILE);
------

-- PLE is a good measurement of internal memory pressure
-- Higher PLE is better. Watch the trend over time, not the absolute value
-- This will only return one row for non-NUMA systems

-- Page Life Expectancy isn�t what you think�
-- https://bit.ly/2EgynLa


-- Memory Grants Pending value for current instance  (Query 47) (Memory Grants Pending)
SELECT @@SERVERNAME AS [Server Name], RTRIM([object_name]) AS [Object Name], cntr_value AS [Memory Grants Pending]                                                                                                       
FROM sys.dm_os_performance_counters WITH (NOLOCK)
WHERE [object_name] LIKE N'%Memory Manager%' -- Handles named instances
AND counter_name = N'Memory Grants Pending' OPTION (RECOMPILE);
------

-- Run multiple times, and run periodically if you suspect you are under memory pressure
-- Memory Grants Pending above zero for a sustained period is a very strong indicator of internal memory pressure


-- Memory Clerk Usage for instance  (Query 48) (Memory Clerk Usage)
-- Look for high value for CACHESTORE_SQLCP (Ad-hoc query plans)
SELECT TOP(10) mc.[type] AS [Memory Clerk Type], 
       CAST((SUM(mc.pages_kb)/1024.0) AS DECIMAL (15,2)) AS [Memory Usage (MB)] 
FROM sys.dm_os_memory_clerks AS mc WITH (NOLOCK)
GROUP BY mc.[type]  
ORDER BY SUM(mc.pages_kb) DESC OPTION (RECOMPILE);
------

-- MEMORYCLERK_SQLBUFFERPOOL was new for SQL Server 2012. It should be your highest consumer of memory

-- CACHESTORE_SQLCP  SQL Plans         
-- These are cached SQL statements or batches that aren't in stored procedures, functions and triggers
-- Watch out for high values for CACHESTORE_SQLCP
-- Enabling 'optimize for ad hoc workloads' at the instance level can help reduce this
-- Running DBCC FREESYSTEMCACHE ('SQL Plans') periodically may be required to better control this

-- CACHESTORE_OBJCP  Object Plans      
-- These are compiled plans for stored procedures, functions and triggers

-- sys.dm_os_memory_clerks (Transact-SQL)
-- https://bit.ly/2H31xDR



-- Find single-use, ad-hoc and prepared queries that are bloating the plan cache  (Query 49) (Ad hoc Queries)
SELECT TOP(50) DB_NAME(t.[dbid]) AS [Database Name], t.[text] AS [Query Text], 
cp.objtype AS [Object Type], cp.cacheobjtype AS [Cache Object Type],  
cp.size_in_bytes/1024 AS [Plan Size in KB]
FROM sys.dm_exec_cached_plans AS cp WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t
WHERE cp.cacheobjtype = N'Compiled Plan' 
AND cp.objtype IN (N'Adhoc', N'Prepared') 
AND cp.usecounts = 1
ORDER BY cp.size_in_bytes DESC, DB_NAME(t.[dbid]) OPTION (RECOMPILE);
------

-- Gives you the text, type and size of single-use ad-hoc and prepared queries that waste space in the plan cache
-- Enabling 'optimize for ad hoc workloads' for the instance can help (SQL Server 2008 and above only)
-- Running DBCC FREESYSTEMCACHE ('SQL Plans') periodically may be required to better control this
-- Enabling forced parameterization for the database can help, but test first!

-- Plan cache, adhoc workloads and clearing the single-use plan cache bloat
-- https://bit.ly/2EfYOkl


-- Get top total logical reads queries for entire instance (Query 50) (Top Logical Reads Queries)
SELECT TOP(50) DB_NAME(t.[dbid]) AS [Database Name],
REPLACE(REPLACE(LEFT(t.[text], 255), CHAR(10),''), CHAR(13),'') AS [Short Query Text], 
qs.total_logical_reads AS [Total Logical Reads],
qs.min_logical_reads AS [Min Logical Reads],
qs.total_logical_reads/qs.execution_count AS [Avg Logical Reads],
qs.max_logical_reads AS [Max Logical Reads],   
qs.min_worker_time AS [Min Worker Time],
qs.total_worker_time/qs.execution_count AS [Avg Worker Time], 
qs.max_worker_time AS [Max Worker Time], 
qs.min_elapsed_time AS [Min Elapsed Time], 
qs.total_elapsed_time/qs.execution_count AS [Avg Elapsed Time], 
qs.max_elapsed_time AS [Max Elapsed Time],
qs.execution_count AS [Execution Count], 
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index],
qs.creation_time AS [Creation Time]
--,t.[text] AS [Complete Query Text], qp.query_plan AS [Query Plan] -- uncomment out these columns if not copying results to Excel
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t 
CROSS APPLY sys.dm_exec_query_plan(plan_handle) AS qp 
ORDER BY qs.total_logical_reads DESC OPTION (RECOMPILE);
------


-- Helps you find the most expensive queries from a memory perspective across the entire instance
-- Can also help track down parameter sniffing issues


-- Get top average elapsed time queries for entire instance (Query 51) (Top Avg Elapsed Time Queries)
SELECT TOP(50) DB_NAME(t.[dbid]) AS [Database Name], 
REPLACE(REPLACE(LEFT(t.[text], 255), CHAR(10),''), CHAR(13),'') AS [Short Query Text],  
qs.total_elapsed_time/qs.execution_count AS [Avg Elapsed Time],
qs.min_elapsed_time, qs.max_elapsed_time, qs.last_elapsed_time,
qs.execution_count AS [Execution Count],  
qs.total_logical_reads/qs.execution_count AS [Avg Logical Reads], 
qs.total_physical_reads/qs.execution_count AS [Avg Physical Reads], 
qs.total_worker_time/qs.execution_count AS [Avg Worker Time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index],
qs.creation_time AS [Creation Time]
--,t.[text] AS [Complete Query Text], qp.query_plan AS [Query Plan] -- uncomment out these columns if not copying results to Excel
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t 
CROSS APPLY sys.dm_exec_query_plan(plan_handle) AS qp 
ORDER BY qs.total_elapsed_time/qs.execution_count DESC OPTION (RECOMPILE);
------

-- Helps you find the highest average elapsed time queries across the entire instance
-- Can also help track down parameter sniffing issues


-- Look at UDF execution statistics (Query 52) (UDF Stats by DB)
SELECT TOP (25) DB_NAME(database_id) AS [Database Name], 
		   OBJECT_NAME(object_id, database_id) AS [Function Name],
		   total_worker_time, execution_count, total_elapsed_time,  
           total_elapsed_time/execution_count AS [avg_elapsed_time],  
           last_elapsed_time, last_execution_time, cached_time, [type_desc] 
FROM sys.dm_exec_function_stats WITH (NOLOCK) 
ORDER BY total_worker_time DESC OPTION (RECOMPILE);
------

-- sys.dm_exec_function_stats (Transact-SQL)
-- https://bit.ly/2q1Q6BM



-- Database specific queries *****************************************************************

-- **** Please switch to a user database that you are interested in! *****
--USE YourDatabaseName; -- make sure to change to an actual database on your instance, not the master system database
--GO

-- Individual File Sizes and space available for current database  (Query 53) (File Sizes and Space)
SELECT f.name AS [File Name] , f.physical_name AS [Physical Name], 
CAST((f.size/128.0) AS DECIMAL(15,2)) AS [Total Size in MB],
CAST(f.size/128.0 - CAST(FILEPROPERTY(f.name, 'SpaceUsed') AS int)/128.0 AS DECIMAL(15,2)) 
AS [Available Space In MB], f.[file_id], fg.name AS [Filegroup Name],
f.is_percent_growth, f.growth, fg.is_default, fg.is_read_only, 
fg.is_autogrow_all_files
FROM sys.database_files AS f WITH (NOLOCK) 
LEFT OUTER JOIN sys.filegroups AS fg WITH (NOLOCK)
ON f.data_space_id = fg.data_space_id
ORDER BY f.[file_id] OPTION (RECOMPILE);
------

-- Look at how large and how full the files are and where they are located
-- Make sure the transaction log is not full!!

-- is_autogrow_all_files was new for SQL Server 2016. Equivalent to TF 1117 for user databases

-- SQL Server 2016: Changes in default behavior for autogrow and allocations for tempdb and user databases
-- https://bit.ly/2evRZSR


-- Log space usage for current database  (Query 54) (Log Space Usage)
SELECT DB_NAME(lsu.database_id) AS [Database Name], db.recovery_model_desc AS [Recovery Model],
		CAST(lsu.total_log_size_in_bytes/1048576.0 AS DECIMAL(10, 2)) AS [Total Log Space (MB)],
		CAST(lsu.used_log_space_in_bytes/1048576.0 AS DECIMAL(10, 2)) AS [Used Log Space (MB)], 
		CAST(lsu.used_log_space_in_percent AS DECIMAL(10, 2)) AS [Used Log Space %],
		CAST(lsu.log_space_in_bytes_since_last_backup/1048576.0 AS DECIMAL(10, 2)) AS [Used Log Space Since Last Backup (MB)],
		db.log_reuse_wait_desc		 
FROM sys.dm_db_log_space_usage AS lsu WITH (NOLOCK)
INNER JOIN sys.databases AS db WITH (NOLOCK)
ON lsu.database_id = db.database_id
OPTION (RECOMPILE);
------

-- Look at log file size and usage, along with the log reuse wait description for the current database

-- sys.dm_db_log_space_usage (Transact-SQL)
-- https://bit.ly/2H4MQw9


-- Status of last VLF for current database  (Query 55) (Last VLF Status)
SELECT TOP(1) DB_NAME(li.database_id) AS [Database Name], li.[file_id],
              li.vlf_size_mb, li.vlf_sequence_number, li.vlf_active, li.vlf_status
FROM sys.dm_db_log_info(DB_ID()) AS li 
ORDER BY vlf_sequence_number DESC OPTION (RECOMPILE);
------

-- Determine whether you will be able to shrink the transaction log file

-- vlf_status Values
-- 0 is inactive 
-- 1 is initialized but unused 
-- 2 is active

-- sys.dm_db_log_info (Transact-SQL)
-- https://bit.ly/2EQUU1v



-- Get database scoped configuration values for current database (Query 56) (Database-scoped Configurations)
SELECT configuration_id, name, [value] AS [value_for_primary], value_for_secondary
FROM sys.database_scoped_configurations WITH (NOLOCK) OPTION (RECOMPILE);
------

-- This lets you see the value of these new properties for the current database

-- Clear plan cache for current database
-- ALTER DATABASE SCOPED CONFIGURATION CLEAR PROCEDURE_CACHE;

-- ALTER DATABASE SCOPED CONFIGURATION (Transact-SQL)
-- https://bit.ly/2sOH7nb


-- I/O Statistics by file for the current database  (Query 57) (IO Stats By File)
SELECT DB_NAME(DB_ID()) AS [Database Name], df.name AS [Logical Name], vfs.[file_id], df.type_desc,
df.physical_name AS [Physical Name], CAST(vfs.size_on_disk_bytes/1048576.0 AS DECIMAL(10, 2)) AS [Size on Disk (MB)],
vfs.num_of_reads, vfs.num_of_writes, vfs.io_stall_read_ms, vfs.io_stall_write_ms,
CAST(100. * vfs.io_stall_read_ms/(vfs.io_stall_read_ms + vfs.io_stall_write_ms) AS DECIMAL(10,1)) AS [IO Stall Reads Pct],
CAST(100. * vfs.io_stall_write_ms/(vfs.io_stall_write_ms + vfs.io_stall_read_ms) AS DECIMAL(10,1)) AS [IO Stall Writes Pct],
(vfs.num_of_reads + vfs.num_of_writes) AS [Writes + Reads], 
CAST(vfs.num_of_bytes_read/1048576.0 AS DECIMAL(10, 2)) AS [MB Read], 
CAST(vfs.num_of_bytes_written/1048576.0 AS DECIMAL(10, 2)) AS [MB Written],
CAST(100. * vfs.num_of_reads/(vfs.num_of_reads + vfs.num_of_writes) AS DECIMAL(10,1)) AS [# Reads Pct],
CAST(100. * vfs.num_of_writes/(vfs.num_of_reads + vfs.num_of_writes) AS DECIMAL(10,1)) AS [# Write Pct],
CAST(100. * vfs.num_of_bytes_read/(vfs.num_of_bytes_read + vfs.num_of_bytes_written) AS DECIMAL(10,1)) AS [Read Bytes Pct],
CAST(100. * vfs.num_of_bytes_written/(vfs.num_of_bytes_read + vfs.num_of_bytes_written) AS DECIMAL(10,1)) AS [Written Bytes Pct]
FROM sys.dm_io_virtual_file_stats(DB_ID(), NULL) AS vfs
INNER JOIN sys.database_files AS df WITH (NOLOCK)
ON vfs.[file_id]= df.[file_id] OPTION (RECOMPILE);
------

-- This helps you characterize your workload better from an I/O perspective for this database
-- It helps you determine whether you has an OLTP or DW/DSS type of workload



-- Get most frequently executed queries for this database (Query 58) (Query Execution Counts)
SELECT TOP(50) LEFT(t.[text], 50) AS [Short Query Text], qs.execution_count AS [Execution Count],
qs.total_logical_reads AS [Total Logical Reads],
qs.total_logical_reads/qs.execution_count AS [Avg Logical Reads],
qs.total_worker_time AS [Total Worker Time],
qs.total_worker_time/qs.execution_count AS [Avg Worker Time], 
qs.total_elapsed_time AS [Total Elapsed Time],
qs.total_elapsed_time/qs.execution_count AS [Avg Elapsed Time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index], 
qs.creation_time AS [Creation Time]
--,t.[text] AS [Complete Query Text], qp.query_plan AS [Query Plan] -- uncomment out these columns if not copying results to Excel
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t 
CROSS APPLY sys.dm_exec_query_plan(plan_handle) AS qp 
WHERE t.dbid = DB_ID()
ORDER BY qs.execution_count DESC OPTION (RECOMPILE);
------


-- Queries 59 through 64 are the "Bad Man List" for stored procedures

-- Top Cached SPs By Execution Count (Query 59) (SP Execution Counts)
SELECT TOP(100) p.name AS [SP Name], qs.execution_count AS [Execution Count],
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute],
qs.total_elapsed_time/qs.execution_count AS [Avg Elapsed Time],
qs.total_worker_time/qs.execution_count AS [Avg Worker Time],    
qs.total_logical_reads/qs.execution_count AS [Avg Logical Reads],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index],
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND DATEDIFF(Minute, qs.cached_time, GETDATE()) > 0
ORDER BY qs.execution_count DESC OPTION (RECOMPILE);
------

-- Tells you which cached stored procedures are called the most often
-- This helps you characterize and baseline your workload


-- Top Cached SPs By Avg Elapsed Time (Query 60) (SP Avg Elapsed Time)
SELECT TOP(25) p.name AS [SP Name], qs.min_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time], 
qs.max_elapsed_time, qs.last_elapsed_time, qs.total_elapsed_time, qs.execution_count, 
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute], 
qs.total_worker_time/qs.execution_count AS [AvgWorkerTime], 
qs.total_worker_time AS [TotalWorkerTime],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index],
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND DATEDIFF(Minute, qs.cached_time, GETDATE()) > 0
ORDER BY avg_elapsed_time DESC OPTION (RECOMPILE);
------

-- This helps you find high average elapsed time cached stored procedures that
-- may be easy to optimize with standard query tuning techniques



-- Top Cached SPs By Total Worker time. Worker time relates to CPU cost  (Query 61) (SP Worker Time)
SELECT TOP(25) p.name AS [SP Name], qs.total_worker_time AS [TotalWorkerTime], 
qs.total_worker_time/qs.execution_count AS [AvgWorkerTime], qs.execution_count, 
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute],
qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index],
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND DATEDIFF(Minute, qs.cached_time, GETDATE()) > 0
ORDER BY qs.total_worker_time DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a CPU perspective
-- You should look at this if you see signs of CPU pressure


-- Top Cached SPs By Total Logical Reads. Logical reads relate to memory pressure  (Query 62) (SP Logical Reads)
SELECT TOP(25) p.name AS [SP Name], qs.total_logical_reads AS [TotalLogicalReads], 
qs.total_logical_reads/qs.execution_count AS [AvgLogicalReads],qs.execution_count, 
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute], 
qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index], 
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND DATEDIFF(Minute, qs.cached_time, GETDATE()) > 0
ORDER BY qs.total_logical_reads DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a memory perspective
-- You should look at this if you see signs of memory pressure


-- Top Cached SPs By Total Physical Reads. Physical reads relate to disk read I/O pressure  (Query 63) (SP Physical Reads)
SELECT TOP(25) p.name AS [SP Name],qs.total_physical_reads AS [TotalPhysicalReads], 
qs.total_physical_reads/qs.execution_count AS [AvgPhysicalReads], qs.execution_count, 
qs.total_logical_reads,qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index],
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan 
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND qs.total_physical_reads > 0
ORDER BY qs.total_physical_reads DESC, qs.total_logical_reads DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a read I/O perspective
-- You should look at this if you see signs of I/O pressure or of memory pressure
       


-- Top Cached SPs By Total Logical Writes (Query 64) (SP Logical Writes)
-- Logical writes relate to both memory and disk I/O pressure 
SELECT TOP(25) p.name AS [SP Name], qs.total_logical_writes AS [TotalLogicalWrites], 
qs.total_logical_writes/qs.execution_count AS [AvgLogicalWrites], qs.execution_count,
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute],
qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index], 
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan 
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND qs.total_logical_writes > 0
AND DATEDIFF(Minute, qs.cached_time, GETDATE()) > 0
ORDER BY qs.total_logical_writes DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a write I/O perspective
-- You should look at this if you see signs of I/O pressure or of memory pressure


-- Lists the top statements by average input/output usage for the current database  (Query 65) (Top IO Statements)
SELECT TOP(50) OBJECT_NAME(qt.objectid, dbid) AS [SP Name],
(qs.total_logical_reads + qs.total_logical_writes) /qs.execution_count AS [Avg IO], qs.execution_count AS [Execution Count],
SUBSTRING(qt.[text],qs.statement_start_offset/2, 
	(CASE 
		WHEN qs.statement_end_offset = -1 
	 THEN LEN(CONVERT(nvarchar(max), qt.[text])) * 2 
		ELSE qs.statement_end_offset 
	 END - qs.statement_start_offset)/2) AS [Query Text]	
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS qt
WHERE qt.[dbid] = DB_ID()
ORDER BY [Avg IO] DESC OPTION (RECOMPILE);
------

-- Helps you find the most expensive statements for I/O by SP



-- Possible Bad NC Indexes (writes > reads)  (Query 66) (Bad NC Indexes)
SELECT OBJECT_NAME(s.[object_id]) AS [Table Name], i.name AS [Index Name], i.index_id, 
i.is_disabled, i.is_hypothetical, i.has_filter, i.fill_factor,
s.user_updates AS [Total Writes], s.user_seeks + s.user_scans + s.user_lookups AS [Total Reads],
s.user_updates - (s.user_seeks + s.user_scans + s.user_lookups) AS [Difference]
FROM sys.dm_db_index_usage_stats AS s WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON s.[object_id] = i.[object_id]
AND i.index_id = s.index_id
WHERE OBJECTPROPERTY(s.[object_id],'IsUserTable') = 1
AND s.database_id = DB_ID()
AND s.user_updates > (s.user_seeks + s.user_scans + s.user_lookups)
AND i.index_id > 1 AND i.[type_desc] = N'NONCLUSTERED'
AND i.is_primary_key = 0 AND i.is_unique_constraint = 0 AND i.is_unique = 0
ORDER BY [Difference] DESC, [Total Writes] DESC, [Total Reads] ASC OPTION (RECOMPILE);
------

-- Look for indexes with high numbers of writes and zero or very low numbers of reads
-- Consider your complete workload, and how long your instance has been running
-- Investigate further before dropping an index!


-- Missing Indexes for current database by Index Advantage  (Query 67) (Missing Indexes)
SELECT DISTINCT CONVERT(decimal(18,2), user_seeks * avg_total_user_cost * (avg_user_impact * 0.01)) AS [index_advantage], 
migs.last_user_seek, mid.[statement] AS [Database.Schema.Table],
mid.equality_columns, mid.inequality_columns, mid.included_columns,
migs.unique_compiles, migs.user_seeks, migs.avg_total_user_cost, migs.avg_user_impact,
OBJECT_NAME(mid.[object_id]) AS [Table Name], p.rows AS [Table Rows]
FROM sys.dm_db_missing_index_group_stats AS migs WITH (NOLOCK)
INNER JOIN sys.dm_db_missing_index_groups AS mig WITH (NOLOCK)
ON migs.group_handle = mig.index_group_handle
INNER JOIN sys.dm_db_missing_index_details AS mid WITH (NOLOCK)
ON mig.index_handle = mid.index_handle
INNER JOIN sys.partitions AS p WITH (NOLOCK)
ON p.[object_id] = mid.[object_id]
WHERE mid.database_id = DB_ID()
AND p.index_id < 2 
ORDER BY index_advantage DESC OPTION (RECOMPILE);
------

-- Look at index advantage, last user seek time, number of user seeks to help determine source and importance
-- SQL Server is overly eager to add included columns, so beware
-- Do not just blindly add indexes that show up from this query!!!


-- Find missing index warnings for cached plans in the current database  (Query 68) (Missing Index Warnings)
-- Note: This query could take some time on a busy instance
SELECT TOP(25) OBJECT_NAME(objectid) AS [ObjectName], 
               cp.objtype, cp.usecounts, cp.size_in_bytes, query_plan
FROM sys.dm_exec_cached_plans AS cp WITH (NOLOCK)
CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) AS qp
WHERE CAST(query_plan AS NVARCHAR(MAX)) LIKE N'%MissingIndex%'
AND dbid = DB_ID()
ORDER BY cp.usecounts DESC OPTION (RECOMPILE);
------

-- Helps you connect missing indexes to specific stored procedures or queries
-- This can help you decide whether to add them or not


-- Breaks down buffers used by current database by object (table, index) in the buffer cache  (Query 69) (Buffer Usage)
-- Note: This query could take some time on a busy instance
SELECT OBJECT_NAME(p.[object_id]) AS [Object Name], p.index_id, 
CAST(COUNT(*)/128.0 AS DECIMAL(10, 2)) AS [Buffer size(MB)],  
COUNT(*) AS [BufferCount], p.[Rows] AS [Row Count],
p.data_compression_desc AS [Compression Type]
FROM sys.allocation_units AS a WITH (NOLOCK)
INNER JOIN sys.dm_os_buffer_descriptors AS b WITH (NOLOCK)
ON a.allocation_unit_id = b.allocation_unit_id
INNER JOIN sys.partitions AS p WITH (NOLOCK)
ON a.container_id = p.hobt_id
WHERE b.database_id = CONVERT(int, DB_ID())
AND p.[object_id] > 100
AND OBJECT_NAME(p.[object_id]) NOT LIKE N'plan_%'
AND OBJECT_NAME(p.[object_id]) NOT LIKE N'sys%'
AND OBJECT_NAME(p.[object_id]) NOT LIKE N'xml_index_nodes%'
GROUP BY p.[object_id], p.index_id, p.data_compression_desc, p.[Rows]
ORDER BY [BufferCount] DESC OPTION (RECOMPILE);
------

-- Tells you what tables and indexes are using the most memory in the buffer cache
-- It can help identify possible candidates for data compression


-- Get Table names, row counts, and compression status for clustered index or heap  (Query 70) (Table Sizes)
SELECT OBJECT_NAME(object_id) AS [ObjectName], 
SUM(Rows) AS [RowCount], data_compression_desc AS [CompressionType]
FROM sys.partitions WITH (NOLOCK)
WHERE index_id < 2 --ignore the partitions from the non-clustered index if any
AND OBJECT_NAME(object_id) NOT LIKE N'sys%'
AND OBJECT_NAME(object_id) NOT LIKE N'queue_%' 
AND OBJECT_NAME(object_id) NOT LIKE N'filestream_tombstone%' 
AND OBJECT_NAME(object_id) NOT LIKE N'fulltext%'
AND OBJECT_NAME(object_id) NOT LIKE N'ifts_comp_fragment%'
AND OBJECT_NAME(object_id) NOT LIKE N'filetable_updates%'
AND OBJECT_NAME(object_id) NOT LIKE N'xml_index_nodes%'
AND OBJECT_NAME(object_id) NOT LIKE N'sqlagent_job%'  
AND OBJECT_NAME(object_id) NOT LIKE N'plan_persist%'  
GROUP BY object_id, data_compression_desc
ORDER BY SUM(Rows) DESC OPTION (RECOMPILE);
------

-- Gives you an idea of table sizes, and possible data compression opportunities



-- Get some key table properties (Query 71) (Table Properties)
SELECT OBJECT_NAME(t.[object_id]) AS [ObjectName], p.[rows] AS [Table Rows], p.index_id, 
       p.data_compression_desc AS [Index Data Compression],
       t.create_date, t.lock_on_bulk_load, t.is_replicated, t.has_replication_filter, 
       t.is_tracked_by_cdc, t.lock_escalation_desc, t.is_filetable, 
	   t.is_memory_optimized, t.durability_desc, 
	   t.temporal_type_desc, t.is_remote_data_archive_enabled, t.is_external -- new for SQL Server 2016
FROM sys.tables AS t WITH (NOLOCK)
INNER JOIN sys.partitions AS p WITH (NOLOCK)
ON t.[object_id] = p.[object_id]
WHERE OBJECT_NAME(t.[object_id]) NOT LIKE N'sys%'
ORDER BY OBJECT_NAME(t.[object_id]), p.index_id OPTION (RECOMPILE);
------

-- Gives you some good information about your tables
-- is_memory_optimized and durability_desc were new in SQL Server 2014
-- temporal_type_desc, is_remote_data_archive_enabled, is_external were new in SQL Server 2016

-- sys.tables (Transact-SQL)
-- https://bit.ly/2Gk7998



-- When were Statistics last updated on all indexes?  (Query 72) (Statistics Update)
SELECT SCHEMA_NAME(o.Schema_ID) + N'.' + o.[NAME] AS [Object Name], o.[type_desc] AS [Object Type],
      i.[name] AS [Index Name], STATS_DATE(i.[object_id], i.index_id) AS [Statistics Date], 
      s.auto_created, s.no_recompute, s.user_created, s.is_incremental, s.is_temporary,
	  st.row_count, st.used_page_count
FROM sys.objects AS o WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON o.[object_id] = i.[object_id]
INNER JOIN sys.stats AS s WITH (NOLOCK)
ON i.[object_id] = s.[object_id] 
AND i.index_id = s.stats_id
INNER JOIN sys.dm_db_partition_stats AS st WITH (NOLOCK)
ON o.[object_id] = st.[object_id]
AND i.[index_id] = st.[index_id]
WHERE o.[type] IN ('U', 'V')
AND st.row_count > 0
ORDER BY STATS_DATE(i.[object_id], i.index_id) DESC OPTION (RECOMPILE);
------  

-- Helps discover possible problems with out-of-date statistics
-- Also gives you an idea which indexes are the most active

-- sys.stats (Transact-SQL)
-- https://bit.ly/2GyAxrn

-- UPDATEs to Statistics (Erin Stellato)
-- https://bit.ly/2vhrYQy




-- Look at most frequently modified indexes and statistics (Query 73) (Volatile Indexes)
SELECT o.[name] AS [Object Name], o.[object_id], o.[type_desc], s.[name] AS [Statistics Name], 
       s.stats_id, s.no_recompute, s.auto_created, s.is_incremental, s.is_temporary,
	   sp.modification_counter, sp.[rows], sp.rows_sampled, sp.last_updated
FROM sys.objects AS o WITH (NOLOCK)
INNER JOIN sys.stats AS s WITH (NOLOCK)
ON s.object_id = o.object_id
CROSS APPLY sys.dm_db_stats_properties(s.object_id, s.stats_id) AS sp
WHERE o.[type_desc] NOT IN (N'SYSTEM_TABLE', N'INTERNAL_TABLE')
AND sp.modification_counter > 0
ORDER BY sp.modification_counter DESC, o.name OPTION (RECOMPILE);
------

-- This helps you understand your workload and make better decisions about 
-- things like data compression and adding new indexes to a table



-- Get fragmentation info for all indexes above a certain size in the current database  (Query 74) (Index Fragmentation)
-- Note: This query could take some time on a very large database
SELECT DB_NAME(ps.database_id) AS [Database Name], SCHEMA_NAME(o.[schema_id]) AS [Schema Name],
OBJECT_NAME(ps.OBJECT_ID) AS [Object Name], i.[name] AS [Index Name], ps.index_id, 
ps.index_type_desc, ps.avg_fragmentation_in_percent, 
ps.fragment_count, ps.page_count, i.fill_factor, i.has_filter, 
i.filter_definition, i.[allow_page_locks]
FROM sys.dm_db_index_physical_stats(DB_ID(),NULL, NULL, NULL , N'LIMITED') AS ps
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON ps.[object_id] = i.[object_id] 
AND ps.index_id = i.index_id
INNER JOIN sys.objects AS o WITH (NOLOCK)
ON i.[object_id] = o.[object_id]
WHERE ps.database_id = DB_ID()
AND ps.page_count > 2500
ORDER BY ps.avg_fragmentation_in_percent DESC OPTION (RECOMPILE);
------

-- Helps determine whether you have framentation in your relational indexes
-- and how effective your index maintenance strategy is


--- Index Read/Write stats (all tables in current DB) ordered by Reads  (Query 75) (Overall Index Usage - Reads)
SELECT OBJECT_NAME(i.[object_id]) AS [ObjectName], i.[name] AS [IndexName], i.index_id, 
       s.user_seeks, s.user_scans, s.user_lookups,
	   s.user_seeks + s.user_scans + s.user_lookups AS [Total Reads], 
	   s.user_updates AS [Writes],  
	   i.[type_desc] AS [Index Type], i.fill_factor AS [Fill Factor], i.has_filter, i.filter_definition, 
	   s.last_user_scan, s.last_user_lookup, s.last_user_seek
FROM sys.indexes AS i WITH (NOLOCK)
LEFT OUTER JOIN sys.dm_db_index_usage_stats AS s WITH (NOLOCK)
ON i.[object_id] = s.[object_id]
AND i.index_id = s.index_id
AND s.database_id = DB_ID()
WHERE OBJECTPROPERTY(i.[object_id],'IsUserTable') = 1
ORDER BY s.user_seeks + s.user_scans + s.user_lookups DESC OPTION (RECOMPILE); -- Order by reads
------

-- Show which indexes in the current database are most active for Reads


--- Index Read/Write stats (all tables in current DB) ordered by Writes  (Query 76) (Overall Index Usage - Writes)
SELECT OBJECT_NAME(i.[object_id]) AS [ObjectName], i.[name] AS [IndexName], i.index_id,
	   s.user_updates AS [Writes], s.user_seeks + s.user_scans + s.user_lookups AS [Total Reads], 
	   i.[type_desc] AS [Index Type], i.fill_factor AS [Fill Factor], i.has_filter, i.filter_definition,
	   s.last_system_update, s.last_user_update
FROM sys.indexes AS i WITH (NOLOCK)
LEFT OUTER JOIN sys.dm_db_index_usage_stats AS s WITH (NOLOCK)
ON i.[object_id] = s.[object_id]
AND i.index_id = s.index_id
AND s.database_id = DB_ID()
WHERE OBJECTPROPERTY(i.[object_id],'IsUserTable') = 1
ORDER BY s.user_updates DESC OPTION (RECOMPILE);						 -- Order by writes
------

-- Show which indexes in the current database are most active for Writes


-- Get in-memory OLTP index usage (Query 77) (XTP Index Usage)
SELECT OBJECT_NAME(i.[object_id]) AS [Object Name], i.index_id, i.[name] AS [Index Name],
       i.[type_desc], xis.scans_started, xis.scans_retries, 
	   xis.rows_touched, xis.rows_returned
FROM sys.dm_db_xtp_index_stats AS xis WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON i.[object_id] = xis.[object_id] 
AND i.index_id = xis.index_id 
ORDER BY OBJECT_NAME(i.[object_id]) OPTION (RECOMPILE);
------

-- This gives you some index usage statistics for in-memory OLTP
-- Returns no data if you are not using in-memory OLTP

-- Guidelines for Using Indexes on Memory-Optimized Tables
-- https://bit.ly/2GCP8lF



-- Look at Columnstore index physical statistics (Query 78) (Columnstore Index Physical Stat)
SELECT OBJECT_NAME(ps.object_id) AS [TableName],  
	i.[name] AS [IndexName], ps.index_id, ps.partition_number,
	ps.delta_store_hobt_id, ps.state_desc, ps.total_rows, ps.size_in_bytes,
	ps.trim_reason_desc, ps.generation, ps.transition_to_compressed_state_desc,
	ps.has_vertipaq_optimization, ps.deleted_rows,
	100 * (ISNULL(ps.deleted_rows, 0))/ps.total_rows AS [Fragmentation]
FROM sys.dm_db_column_store_row_group_physical_stats AS ps WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON ps.object_id = i.object_id 
AND ps.index_id = i.index_id
ORDER BY ps.object_id, ps.partition_number, ps.row_group_id OPTION (RECOMPILE);
------

-- sys.dm_db_column_store_row_group_physical_stats (Transact-SQL)
-- https://bit.ly/2q276XQ



-- Get lock waits for current database (Query 79) (Lock Waits)
SELECT o.name AS [table_name], i.name AS [index_name], ios.index_id, ios.partition_number,
		SUM(ios.row_lock_wait_count) AS [total_row_lock_waits], 
		SUM(ios.row_lock_wait_in_ms) AS [total_row_lock_wait_in_ms],
		SUM(ios.page_lock_wait_count) AS [total_page_lock_waits],
		SUM(ios.page_lock_wait_in_ms) AS [total_page_lock_wait_in_ms],
		SUM(ios.page_lock_wait_in_ms)+ SUM(row_lock_wait_in_ms) AS [total_lock_wait_in_ms]
FROM sys.dm_db_index_operational_stats(DB_ID(), NULL, NULL, NULL) AS ios
INNER JOIN sys.objects AS o WITH (NOLOCK)
ON ios.[object_id] = o.[object_id]
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON ios.[object_id] = i.[object_id] 
AND ios.index_id = i.index_id
WHERE o.[object_id] > 100
GROUP BY o.name, i.name, ios.index_id, ios.partition_number
HAVING SUM(ios.page_lock_wait_in_ms)+ SUM(row_lock_wait_in_ms) > 0
ORDER BY total_lock_wait_in_ms DESC OPTION (RECOMPILE);
------

-- This query is helpful for troubleshooting blocking and deadlocking issues



-- Look at UDF execution statistics (Query 80) (UDF Statistics)
SELECT OBJECT_NAME(object_id) AS [Function Name], execution_count,
	   total_worker_time, total_logical_reads, total_physical_reads, total_elapsed_time, 
	   total_elapsed_time/execution_count AS [avg_elapsed_time],
	   FORMAT(cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
FROM sys.dm_exec_function_stats WITH (NOLOCK) 
WHERE database_id = DB_ID()
ORDER BY total_worker_time DESC OPTION (RECOMPILE); 
------

-- New for SQL Server 2016
-- Helps you investigate scalar UDF performance issues
-- Does not return information for table valued functions

-- sys.dm_exec_function_stats (Transact-SQL)
-- https://bit.ly/2q1Q6BM


-- Get QueryStore Options for this database (Query 81) (QueryStore Options)
SELECT actual_state_desc, desired_state_desc, [interval_length_minutes],
       current_storage_size_mb, [max_storage_size_mb], 
	   query_capture_mode_desc, size_based_cleanup_mode_desc
FROM sys.database_query_store_options WITH (NOLOCK) OPTION (RECOMPILE);
------

-- New for SQL Server 2016
-- Requires that Query Store is enabled for this database

-- Make sure that the actual_state_desc is the same as desired_state_desc
-- Make sure that the current_storage_size_mb is less than the max_storage_size_mb

-- Tuning Workload Performance with Query Store
-- https://bit.ly/1kHSl7w


-- Get highest aggregate duration queries over last hour (Query 82) (High Aggregate Duration Queries)
WITH AggregatedDurationLastHour
AS
(SELECT q.query_id, SUM(rs.count_executions * rs.avg_duration) AS total_duration,
   COUNT (DISTINCT p.plan_id) AS number_of_plans
   FROM sys.query_store_query_text AS qt WITH (NOLOCK)
   INNER JOIN sys.query_store_query AS q WITH (NOLOCK)
   ON qt.query_text_id = q.query_text_id
   INNER JOIN sys.query_store_plan AS p WITH (NOLOCK)
   ON q.query_id = p.query_id
   INNER JOIN sys.query_store_runtime_stats AS rs WITH (NOLOCK)
   ON rs.plan_id = p.plan_id
   INNER JOIN sys.query_store_runtime_stats_interval AS rsi WITH (NOLOCK)
   ON rsi.runtime_stats_interval_id = rs.runtime_stats_interval_id
   WHERE rsi.start_time >= DATEADD(hour, -1, GETUTCDATE()) 
   AND rs.execution_type_desc = N'Regular'
   GROUP BY q.query_id), OrderedDuration 
AS
(SELECT query_id, total_duration, number_of_plans, 
 ROW_NUMBER () OVER (ORDER BY total_duration DESC, query_id) AS RN
 FROM AggregatedDurationLastHour)
SELECT OBJECT_NAME(q.object_id) AS [Containing Object], qt.query_sql_text, 
od.total_duration AS [Total Duration (microsecs)], 
od.number_of_plans AS [Plan Count],
p.is_forced_plan, p.is_parallel_plan, p.is_trivial_plan,
q.query_parameterization_type_desc, p.[compatibility_level],
p.last_compile_start_time, 
q.last_execution_time,
CONVERT(xml, p.query_plan) AS query_plan_xml 
FROM OrderedDuration AS od 
INNER JOIN sys.query_store_query AS q WITH (NOLOCK)
ON q.query_id  = od.query_id
INNER JOIN sys.query_store_query_text AS qt WITH (NOLOCK)
ON q.query_text_id = qt.query_text_id
INNER JOIN sys.query_store_plan AS p WITH (NOLOCK)
ON q.query_id = p.query_id
WHERE od.RN <= 50 
ORDER BY od.total_duration DESC OPTION (RECOMPILE);
------

-- New for SQL Server 2016
-- Requires that QueryStore is enabled for this database



-- Get highest aggregate CPU time queries over last hour (Query 83) (High Aggregate CPU Queries)
WITH AggregatedCPULastHour
AS
(SELECT q.query_id, SUM(rs.count_executions * rs.avg_cpu_time) AS total_cpu_time,
   COUNT (DISTINCT p.plan_id) AS number_of_plans
   FROM sys.query_store_query_text AS qt WITH (NOLOCK)
   INNER JOIN sys.query_store_query AS q WITH (NOLOCK)
   ON qt.query_text_id = q.query_text_id
   INNER JOIN sys.query_store_plan AS p WITH (NOLOCK)
   ON q.query_id = p.query_id
   INNER JOIN sys.query_store_runtime_stats AS rs WITH (NOLOCK)
   ON rs.plan_id = p.plan_id
   INNER JOIN sys.query_store_runtime_stats_interval AS rsi WITH (NOLOCK)
   ON rsi.runtime_stats_interval_id = rs.runtime_stats_interval_id
   WHERE rsi.start_time >= DATEADD(hour, -1, GETUTCDATE()) 
   AND rs.execution_type_desc = N'Regular'
   GROUP BY q.query_id), OrderedDuration 
AS
(SELECT query_id, total_cpu_time, number_of_plans, 
 ROW_NUMBER () OVER (ORDER BY total_cpu_time DESC, query_id) AS RN
 FROM AggregatedCPULastHour)
SELECT OBJECT_NAME(q.object_id) AS [Containing Object], qt.query_sql_text, 
od.total_cpu_time AS [Total CPU Time (microsecs)], 
od.number_of_plans AS [Plan Count],
p.is_forced_plan, p.is_parallel_plan, p.is_trivial_plan,
q.query_parameterization_type_desc, p.[compatibility_level],
p.last_compile_start_time, 
q.last_execution_time,
CONVERT(xml, p.query_plan) AS query_plan_xml 
FROM OrderedDuration AS od 
INNER JOIN sys.query_store_query AS q WITH (NOLOCK)
ON q.query_id  = od.query_id
INNER JOIN sys.query_store_query_text AS qt WITH (NOLOCK)
ON q.query_text_id = qt.query_text_id
INNER JOIN sys.query_store_plan AS p WITH (NOLOCK)
ON q.query_id = p.query_id
WHERE od.RN <= 50 
ORDER BY od.total_cpu_time DESC OPTION (RECOMPILE);
------

-- New for SQL Server 2016
-- Requires that QueryStore is enabled for this database


-- Get input buffer information for the current database (Query 84) (Input Buffer)
SELECT es.session_id, DB_NAME(es.database_id) AS [Database Name],
       es.login_time, es.cpu_time, es.logical_reads, es.memory_usage,
       es.[status], ib.event_info AS [Input Buffer]
FROM sys.dm_exec_sessions AS es WITH (NOLOCK)
CROSS APPLY sys.dm_exec_input_buffer(es.session_id, NULL) AS ib
WHERE es.database_id = DB_ID()
AND es.session_id > 50
AND es.session_id <> @@SPID OPTION (RECOMPILE);
------

-- Gives you input buffer information from all non-system sessions for the current database
-- Replaces DBCC INPUTBUFFER

-- New DMF for retrieving input buffer in SQL Server
-- https://bit.ly/2uHKMbz

-- sys.dm_exec_input_buffer (Transact-SQL)
-- https://bit.ly/2J5Hf9q



-- Get any resumable index rebuild operation information (Query 85) (Resumable Index Rebuild)
SELECT OBJECT_NAME(iro.object_id) AS [Object Name], iro.index_id, iro.name AS [Index Name],
       iro.sql_text, iro.last_max_dop_used, iro.partition_number, iro.state_desc, iro.start_time, iro.percent_complete
FROM  sys.index_resumable_operations AS iro WITH (NOLOCK)
OPTION (RECOMPILE);
------ 

-- index_resumable_operations (Transact-SQL)
-- https://bit.ly/2pYSWqq


-- Get database automatic tuning options (Query 86) (Automatic Tuning Options)
SELECT [name], desired_state_desc, actual_state_desc, reason_desc
FROM sys.database_automatic_tuning_options WITH (NOLOCK)
OPTION (RECOMPILE);
------ 

-- sys.database_automatic_tuning_options (Transact-SQL)
-- https://bit.ly/2FHhLkL



-- Look at recent Full backups for the current database (Query 87) (Recent Full Backups)
SELECT TOP (30) bs.machine_name, bs.server_name, bs.database_name AS [Database Name], bs.recovery_model,
CONVERT (BIGINT, bs.backup_size / 1048576 ) AS [Uncompressed Backup Size (MB)],
CONVERT (BIGINT, bs.compressed_backup_size / 1048576 ) AS [Compressed Backup Size (MB)],
CONVERT (NUMERIC (20,2), (CONVERT (FLOAT, bs.backup_size) /
CONVERT (FLOAT, bs.compressed_backup_size))) AS [Compression Ratio], bs.has_backup_checksums, bs.is_copy_only, bs.encryptor_type,
DATEDIFF (SECOND, bs.backup_start_date, bs.backup_finish_date) AS [Backup Elapsed Time (sec)],
bs.backup_finish_date AS [Backup Finish Date], bmf.physical_device_name AS [Backup Location], bmf.physical_block_size
FROM msdb.dbo.backupset AS bs WITH (NOLOCK)
INNER JOIN msdb.dbo.backupmediafamily AS bmf WITH (NOLOCK)
ON bs.media_set_id = bmf.media_set_id  
WHERE bs.database_name = DB_NAME(DB_ID())
AND bs.[type] = 'D' -- Change to L if you want Log backups
ORDER BY bs.backup_finish_date DESC OPTION (RECOMPILE);
------

-- Are your backup sizes and times changing over time?
-- Are you using backup compression?
-- Are you using backup checksums?
-- Are you doing copy_only backups?
-- Are you doing encrypted backups?
-- Have you done any backup tuning with striped backups, or changing the parameters of the backup command?

-- In SQL Server 2016, native SQL Server backup compression actually works 
-- much better with databases that are using TDE than in previous versions
-- https://bit.ly/28Rpb2x


-- These three Pluralsight Courses go into more detail about how to run these queries and interpret the results

-- SQL Server 2014 DMV Diagnostic Queries � Part 1 
-- https://bit.ly/2plxCer

-- SQL Server 2014 DMV Diagnostic Queries � Part 2
-- https://bit.ly/2IuJpzI

-- SQL Server 2014 DMV Diagnostic Queries � Part 3
-- https://bit.ly/2FIlCPb



-- Sign up for Microsoft Visual Studio Dev Essentials and get a free three month pass to Pluralsight

-- Microsoft Visual Studio Dev Essentials
-- http://bit.ly/1q6xbDL


-- Sign up for Microsoft Azure Essentials and get lots of free Azure usage credits, MCP exam voucher, three month Pluralsight subscription

-- Microsoft Azure Essentials
-- https://bit.ly/2JMWe8x


-- August 2017 blog series about upgrading and migrating SQL Server
-- https://bit.ly/2ftKVrX
tools\dbatools\bin\diagnosticquery\SQLServerDiagnosticQueries_AzureSQLDatabase_201806.sql

-- Azure SQL Database Diagnostic Information Queries
-- Glenn Berry 
-- Last Modified: July 18, 2018
-- https://www.sqlskills.com/blogs/glenn/
-- http://sqlserverperformance.wordpress.com/
-- Twitter: GlennAlanBerry

-- Please listen to my Pluralsight courses
-- https://www.pluralsight.com/author/glenn-berry

-- If you want to find all of our SQLskills SQL101 blog posts, check out https://www.sqlskills.com/help/sql101/



--******************************************************************************
--*   Copyright (C) 2018 Glenn Berry, SQLskills.com
--*   All rights reserved. 
--*
--*   For more scripts and sample code, check out 
--*      https://www.sqlskills.com/blogs/glenn
--*
--*   You may alter this code for your own *non-commercial* purposes. You may
--*   republish altered code as long as you include this copyright and give due credit. 
--*
--*
--*   THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF 
--*   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED 
--*   TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
--*   PARTICULAR PURPOSE. 
--*
--******************************************************************************

-- Make sure you are connected a user database, rather than the master system database


-- Server level queries *******************************

-- SQL and OS Version information for current instance  (Query 1) (Version Info)
SELECT @@SERVERNAME AS [Server Name], @@VERSION AS [SQL Server and OS Version Info];
------

-- Azure SQL Database does not expose as much information as on-premise SQL Server does
													


-- Get instance-level configuration values for instance  (Query 2) (Configuration Values)
SELECT name, value, value_in_use, minimum, maximum, [description], is_dynamic, is_advanced
FROM sys.configurations WITH (NOLOCK)
ORDER BY name OPTION (RECOMPILE);
------

-- All of these settings are read-only in Azure SQL Database, so they are informational only



-- SQL Server NUMA Node information  (Query 3) (SQL Server NUMA Info)
SELECT node_id, node_state_desc, memory_node_id, processor_group, cpu_count, online_scheduler_count, 
       idle_scheduler_count, active_worker_count, avg_load_balance, resource_monitor_state
FROM sys.dm_os_nodes WITH (NOLOCK) 
WHERE node_state_desc <> N'ONLINE DAC' OPTION (RECOMPILE);
------

-- Gives you some useful information about the composition and relative load on your NUMA nodes
-- You want to see an equal number of schedulers on each NUMA node



-- Get recent resource usage (Query 4) (Resource Usage)
SELECT avg_cpu_percent, avg_data_io_percent, avg_log_write_percent, avg_memory_usage_percent, xtp_storage_percent,
       max_worker_percent, max_session_percent, dtu_limit, avg_login_rate_percent, end_time 
FROM sys.dm_db_resource_stats WITH (NOLOCK) 
ORDER BY end_time DESC OPTION (RECOMPILE);
------

-- sys.dm_db_resource_stats (Azure SQL Database)
-- https://bit.ly/2HaSpKn



-- Calculates average stalls per read, per write, and per total input/output for each database file  (Query 5) (IO Stalls by File)
SELECT DB_NAME(fs.database_id) AS [Database Name], CAST(fs.io_stall_read_ms/(1.0 + fs.num_of_reads) AS NUMERIC(10,1)) AS [avg_read_stall_ms],
CAST(fs.io_stall_write_ms/(1.0 + fs.num_of_writes) AS NUMERIC(10,1)) AS [avg_write_stall_ms],
CAST((fs.io_stall_read_ms + fs.io_stall_write_ms)/(1.0 + fs.num_of_reads + fs.num_of_writes) AS NUMERIC(10,1)) AS [avg_io_stall_ms],
fs.io_stall_read_ms, fs.num_of_reads, 
fs.io_stall_write_ms, fs.num_of_writes, fs.io_stall_read_ms + fs.io_stall_write_ms AS [io_stalls], fs.num_of_reads + fs.num_of_writes AS [total_io],
io_stall_queued_read_ms AS [Resource Governor Total Read IO Latency (ms)], io_stall_queued_write_ms AS [Resource Governor Total Write IO Latency (ms)]
FROM sys.dm_io_virtual_file_stats(null,null) AS fs
ORDER BY avg_io_stall_ms DESC OPTION (RECOMPILE);
------

-- Helps determine which database files on the entire instance have the most I/O bottlenecks
-- This can help you decide whether certain LUNs are overloaded and whether you might
-- want to move some files to a different location or perhaps improve your I/O performance
-- These latency numbers include all file activity against each SQL Server 
-- database file since SQL Server was last started




-- Important database properties for all databases on instance   (Query 6) (Database Properties)
SELECT db.[name] AS [Database Name], db.recovery_model_desc AS [Recovery Model], 
db.state_desc, db.containment_desc, db.log_reuse_wait_desc AS [Log Reuse Wait Description], 
db.[compatibility_level] AS [DB Compatibility Level], 
db.is_mixed_page_allocation_on, db.page_verify_option_desc AS [Page Verify Option], 
db.is_auto_create_stats_on, db.is_auto_update_stats_on, db.is_auto_update_stats_async_on, db.is_parameterization_forced, 
db.snapshot_isolation_state_desc, db.is_read_committed_snapshot_on, db.is_auto_close_on, db.is_auto_shrink_on, 
db.target_recovery_time_in_seconds, db.is_cdc_enabled, db.is_memory_optimized_elevate_to_snapshot_on, 
db.delayed_durability_desc, db.is_auto_create_stats_incremental_on,
db.is_query_store_on, db.is_sync_with_backup, db.is_temporal_history_retention_enabled,
db.is_encrypted  
FROM sys.databases AS db WITH (NOLOCK)
ORDER BY db.[name] OPTION (RECOMPILE);
------

-- Things to look at:
-- How many databases are on the instance?
-- What recovery models are they using?
-- What is the log reuse wait description?
-- What compatibility level are the databases on? 
-- What is the Page Verify Option? (should be CHECKSUM)
-- Is Auto Update Statistics Asynchronously enabled?
-- Is Delayed Durability enabled?
-- Make sure auto_shrink and auto_close are not enabled!



-- Get VLF Counts for all databases on the instance (Query 7) (VLF Counts)
SELECT�[name] AS [Database Name],�[VLF Count] 
FROM�sys.databases�AS db WITH (NOLOCK)
CROSS APPLY�(SELECT�file_id, COUNT(*)�AS [VLF Count]�
			 FROM sys.dm_db_log_info(db.database_id) 
����������   GROUP BY�file_id)�AS li
ORDER BY [VLF Count] DESC  OPTION (RECOMPILE);
------

-- High VLF counts can affect write performance to the log file
-- and they can make full database restores and crash recovery take much longer
-- Try to keep your VLF counts under 200 in most cases (depending on log file size)

-- Important change to VLF creation algorithm in SQL Server 2014
-- https://bit.ly/2Hsjbg4




-- Get I/O utilization by database (Query 8) (IO Usage By Database)
WITH Aggregate_IO_Statistics
AS
(SELECT DB_NAME(database_id) AS [Database Name],
CAST(SUM(num_of_bytes_read + num_of_bytes_written)/1048576 AS DECIMAL(12, 2)) AS io_in_mb
FROM sys.dm_io_virtual_file_stats(NULL, NULL) AS [DM_IO_STATS]
WHERE database_id NOT IN (4, 5, 32767)
GROUP BY database_id)
SELECT ROW_NUMBER() OVER(ORDER BY io_in_mb DESC) AS [I/O Rank], [Database Name], 
      CAST(io_in_mb/ SUM(io_in_mb) OVER() * 100.0 AS DECIMAL(5,2)) AS [I/O Percent],
      io_in_mb AS [Total I/O (MB)]     
FROM Aggregate_IO_Statistics
ORDER BY [I/O Rank] OPTION (RECOMPILE);
------

-- Helps determine which database is using the most I/O resources on the instance


-- Get total buffer usage by database for current instance  (Query 9) (Total Buffer Usage by Database)
-- This make take some time to run on a busy instance
WITH AggregateBufferPoolUsage
AS
(SELECT DB_NAME(database_id) AS [Database Name], COUNT(page_id) AS [Page Count],
CAST(COUNT(*) * 8/1024.0 AS DECIMAL (10,2))  AS [CachedSize],
AVG(read_microsec) AS [Avg Read Time (microseconds)]
FROM sys.dm_os_buffer_descriptors WITH (NOLOCK)
WHERE database_id NOT IN (4, 5, 32767)
GROUP BY DB_NAME(database_id))
SELECT ROW_NUMBER() OVER(ORDER BY CachedSize DESC) AS [Buffer Pool Rank], [Database Name], 
       CAST(CachedSize / SUM(CachedSize) OVER() * 100.0 AS DECIMAL(5,2)) AS [Buffer Pool Percent],
       [Page Count], CachedSize AS [Cached Size (MB)], [Avg Read Time (microseconds)]
FROM AggregateBufferPoolUsage
ORDER BY [Buffer Pool Rank] OPTION (RECOMPILE);
------

-- Tells you how much memory (in the buffer pool) 
-- is being used by each database on the instance



-- Get a count of SQL connections by IP address (Query 10) (Connection Counts by IP Address)    
SELECT ec.client_net_address, es.[program_name], es.[host_name], es.login_name, 
COUNT(ec.session_id) AS [connection count] 
FROM sys.dm_exec_sessions AS es WITH (NOLOCK) 
INNER JOIN sys.dm_exec_connections AS ec WITH (NOLOCK) 
ON es.session_id = ec.session_id 
GROUP BY ec.client_net_address, es.[program_name], es.[host_name], es.login_name  
ORDER BY ec.client_net_address, es.[program_name] OPTION (RECOMPILE);
------

-- This helps you figure where your database load is coming from
-- and verifies connectivity from other machines

-- Solving Connectivity errors to SQL Server
-- https://bit.ly/2EgzoD0



-- Get Average Task Counts (run multiple times)  (Query 11) (Avg Task Counts)
SELECT AVG(current_tasks_count) AS [Avg Task Count], 
AVG(work_queue_count) AS [Avg Work Queue Count],
AVG(runnable_tasks_count) AS [Avg Runnable Task Count],
AVG(pending_disk_io_count) AS [Avg Pending DiskIO Count]
FROM sys.dm_os_schedulers WITH (NOLOCK)
WHERE scheduler_id < 255 OPTION (RECOMPILE);
------

-- Sustained values above 10 suggest further investigation in that area (depending on your Service Tier)
-- Avg Task Counts will be higher with lower service tiers
-- High Avg Task Counts are often caused by blocking/deadlocking or other resource contention

-- Sustained values above 1 suggest further investigation in that area
-- High Avg Runnable Task Counts are a good sign of CPU pressure
-- High Avg Pending DiskIO Counts are a sign of disk pressure



-- Detect blocking (run multiple times)  (Query 12) (Detect Blocking)						
SELECT t1.resource_type AS [lock type], DB_NAME(resource_database_id) AS [database],
t1.resource_associated_entity_id AS [blk object],t1.request_mode AS [lock req],  -- lock requested
t1.request_session_id AS [waiter sid], t2.wait_duration_ms AS [wait time],       -- spid of waiter  
(SELECT [text] FROM sys.dm_exec_requests AS r WITH (NOLOCK)                      -- get sql for waiter
CROSS APPLY sys.dm_exec_sql_text(r.[sql_handle]) 
WHERE r.session_id = t1.request_session_id) AS [waiter_batch],
(SELECT SUBSTRING(qt.[text],r.statement_start_offset/2, 
    (CASE WHEN r.statement_end_offset = -1 
    THEN LEN(CONVERT(nvarchar(max), qt.[text])) * 2 
    ELSE r.statement_end_offset END - r.statement_start_offset)/2) 
FROM sys.dm_exec_requests AS r WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(r.[sql_handle]) AS qt
WHERE r.session_id = t1.request_session_id) AS [waiter_stmt],					-- statement blocked
t2.blocking_session_id AS [blocker sid],										-- spid of blocker
(SELECT [text] FROM sys.sysprocesses AS p										-- get sql for blocker
CROSS APPLY sys.dm_exec_sql_text(p.[sql_handle]) 
WHERE p.spid = t2.blocking_session_id) AS [blocker_batch]
FROM sys.dm_tran_locks AS t1 WITH (NOLOCK)
INNER JOIN sys.dm_os_waiting_tasks AS t2 WITH (NOLOCK)
ON t1.lock_owner_address = t2.resource_address OPTION (RECOMPILE);
------

-- Helps troubleshoot blocking and deadlocking issues
-- The results will change from second to second on a busy system
-- You should run this query multiple times when you see signs of blocking



-- Page Life Expectancy (PLE) value for each NUMA node in current instance  (Query 13) (PLE by NUMA Node)
SELECT @@SERVERNAME AS [Server Name], RTRIM([object_name]) AS [Object Name], instance_name, cntr_value AS [Page Life Expectancy]
FROM sys.dm_os_performance_counters WITH (NOLOCK)
WHERE [object_name] LIKE N'%Buffer Node%' -- Handles named instances
AND counter_name = N'Page life expectancy' OPTION (RECOMPILE);
------

-- PLE is a good measurement of internal memory pressure
-- Higher PLE is better. Watch the trend over time, not the absolute value
-- This will only return one row for non-NUMA systems

-- Page Life Expectancy isn�t what you think�
-- https://bit.ly/2EgynLa


-- Memory Grants Pending value for current instance  (Query 14) (Memory Grants Pending)
SELECT @@SERVERNAME AS [Server Name], RTRIM([object_name]) AS [Object Name], cntr_value AS [Memory Grants Pending]                                                                                                       
FROM sys.dm_os_performance_counters WITH (NOLOCK)
WHERE [object_name] LIKE N'%Memory Manager%' -- Handles named instances
AND counter_name = N'Memory Grants Pending' OPTION (RECOMPILE);
------

-- Run multiple times, and run periodically if you suspect you are under memory pressure
-- Memory Grants Pending above zero for a sustained period is a very strong indicator of internal memory pressure


-- Memory Clerk Usage for instance  (Query 15) (Memory Clerk Usage)
-- Look for high value for CACHESTORE_SQLCP (Ad-hoc query plans)
SELECT TOP(10) mc.[type] AS [Memory Clerk Type], 
       CAST((SUM(mc.pages_kb)/1024.0) AS DECIMAL (15,2)) AS [Memory Usage (MB)] 
FROM sys.dm_os_memory_clerks AS mc WITH (NOLOCK)
GROUP BY mc.[type]  
ORDER BY SUM(mc.pages_kb) DESC OPTION (RECOMPILE);
------

-- MEMORYCLERK_SQLBUFFERPOOL was new for SQL Server 2012. It should be your highest consumer of memory

-- CACHESTORE_SQLCP  SQL Plans         
-- These are cached SQL statements or batches that aren't in stored procedures, functions and triggers
-- Watch out for high values for CACHESTORE_SQLCP
-- Enabling 'optimize for ad hoc workloads' at the instance level can help reduce this


-- CACHESTORE_OBJCP  Object Plans      
-- These are compiled plans for stored procedures, functions and triggers



-- Find single-use, ad-hoc and prepared queries that are bloating the plan cache  (Query 16) (Ad hoc Queries)   
SELECT TOP(50) DB_NAME(t.[dbid]) AS [Database Name], t.[text] AS [Query Text], 
cp.objtype AS [Object Type], cp.cacheobjtype AS [Cache Object Type],  
cp.size_in_bytes/1024 AS [Plan Size in KB]
FROM sys.dm_exec_cached_plans AS cp WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t
WHERE cp.cacheobjtype = N'Compiled Plan' 
AND cp.objtype IN (N'Adhoc', N'Prepared') 
AND cp.usecounts = 1
ORDER BY cp.size_in_bytes DESC, DB_NAME(t.[dbid]) OPTION (RECOMPILE);
------

-- Gives you the text, type and size of single-use ad-hoc and prepared queries that waste space in the plan cache
-- Enabling forced parameterization for the database can help, but test first!

-- Plan cache, adhoc workloads and clearing the single-use plan cache bloat
-- https://bit.ly/2EfYOkl






-- Database specific queries *****************************************************************


-- Azure SQL Database size  (Query 17) (Azure SQL DB Size)
SELECT CAST(SUM(CAST(FILEPROPERTY(name, 'SpaceUsed') AS bigint) * 8192.) / 1024 / 1024 AS DECIMAL(15,2)) AS [Database Size In MB],
       CAST(SUM(CAST(FILEPROPERTY(name, 'SpaceUsed') AS bigint) * 8192.) / 1024 / 1024 / 1024 AS DECIMAL(15,2)) AS [Database Size In GB]
FROM sys.database_files WITH (NOLOCK)
WHERE [type_desc] = N'ROWS' OPTION (RECOMPILE);
------

-- This gives you the actual space usage within the data file only, to match what the Azure portal shows for the database size

-- Determining Database Size in Azure SQL Database V12
-- https://bit.ly/2JjrqNh



-- Individual File Sizes and space available for current database  (Query 18) (File Sizes and Space)
SELECT f.name AS [File Name] , f.physical_name AS [Physical Name], 
CAST((f.size/128.0) AS DECIMAL(15,2)) AS [Total Size in MB],
CAST(f.size/128.0 - CAST(FILEPROPERTY(f.name, 'SpaceUsed') AS int)/128.0 AS DECIMAL(15,2)) 
AS [Available Space In MB], f.[file_id], fg.name AS [Filegroup Name],
f.is_percent_growth, f.growth, fg.is_default, fg.is_read_only, 
fg.is_autogrow_all_files
FROM sys.database_files AS f WITH (NOLOCK) 
LEFT OUTER JOIN sys.filegroups AS fg WITH (NOLOCK)
ON f.data_space_id = fg.data_space_id
ORDER BY f.[file_id] OPTION (RECOMPILE);
------

-- Look at how large and how full the files are and where they are located

-- is_autogrow_all_files was new for SQL Server 2016. Equivalent to TF 1117 for user databases

-- SQL Server 2016: Changes in default behavior for autogrow and allocations for tempdb and user databases
-- http://bit.ly/2evRZSR



-- Log space usage for current database  (Query 19) (Log Space Usage)
SELECT DB_NAME(lsu.database_id) AS [Database Name], db.recovery_model_desc AS [Recovery Model],
		CAST(lsu.total_log_size_in_bytes/1048576.0 AS DECIMAL(10, 2)) AS [Total Log Space (MB)],
		CAST(lsu.used_log_space_in_bytes/1048576.0 AS DECIMAL(10, 2)) AS [Used Log Space (MB)], 
		CAST(lsu.used_log_space_in_percent AS DECIMAL(10, 2)) AS [Used Log Space %],
		CAST(lsu.log_space_in_bytes_since_last_backup/1048576.0 AS DECIMAL(10, 2)) AS [Used Log Space Since Last Backup (MB)],
		db.log_reuse_wait_desc		 
FROM sys.dm_db_log_space_usage AS lsu WITH (NOLOCK)
INNER JOIN sys.databases AS db WITH (NOLOCK)
ON lsu.database_id = db.database_id
OPTION (RECOMPILE);
------

-- Look at log file size and usage, along with the log reuse wait description for the current database


-- Status of last VLF for current database  (Query 20) (Last VLF Status)
SELECT TOP(1) DB_NAME(li.database_id) AS [Database Name], li.[file_id],
               li.vlf_size_mb, li.vlf_sequence_number, li.vlf_active, li.vlf_status
FROM sys.dm_db_log_info(DB_ID()) AS li 
ORDER BY vlf_sequence_number DESC OPTION (RECOMPILE);
------

-- Determine whether you will be able to shrink the transaction log file

-- vlf_status Values
-- 0 is inactive 
-- 1 is initialized but unused 
-- 2 is active



-- Get database scoped configuration values for current database (Query 21) (Database-scoped Configurations)
SELECT configuration_id, name, [value] AS [value_for_primary]
FROM sys.database_scoped_configurations WITH (NOLOCK) OPTION (RECOMPILE);
------

-- This lets you see the value of these new properties for the current database

-- Clear plan cache for current database
-- ALTER DATABASE SCOPED CONFIGURATION CLEAR PROCEDURE_CACHE;

-- ALTER DATABASE SCOPED CONFIGURATION (Transact-SQL)
-- https://bit.ly/2sOH7nb


-- I/O Statistics by file for the current database  (Query 22) (IO Stats By File)
SELECT DB_NAME(DB_ID()) AS [Database Name], df.name AS [Logical Name], vfs.[file_id], df.type_desc,
df.physical_name AS [Physical Name], CAST(vfs.size_on_disk_bytes/1048576.0 AS DECIMAL(10, 2)) AS [Size on Disk (MB)],
vfs.num_of_reads, vfs.num_of_writes, vfs.io_stall_read_ms, vfs.io_stall_write_ms,
CAST(100. * vfs.io_stall_read_ms/(vfs.io_stall_read_ms + vfs.io_stall_write_ms) AS DECIMAL(10,1)) AS [IO Stall Reads Pct],
CAST(100. * vfs.io_stall_write_ms/(vfs.io_stall_write_ms + vfs.io_stall_read_ms) AS DECIMAL(10,1)) AS [IO Stall Writes Pct],
(vfs.num_of_reads + vfs.num_of_writes) AS [Writes + Reads], 
CAST(vfs.num_of_bytes_read/1048576.0 AS DECIMAL(10, 2)) AS [MB Read], 
CAST(vfs.num_of_bytes_written/1048576.0 AS DECIMAL(10, 2)) AS [MB Written],
CAST(100. * vfs.num_of_reads/(vfs.num_of_reads + vfs.num_of_writes) AS DECIMAL(10,1)) AS [# Reads Pct],
CAST(100. * vfs.num_of_writes/(vfs.num_of_reads + vfs.num_of_writes) AS DECIMAL(10,1)) AS [# Write Pct],
CAST(100. * vfs.num_of_bytes_read/(vfs.num_of_bytes_read + vfs.num_of_bytes_written) AS DECIMAL(10,1)) AS [Read Bytes Pct],
CAST(100. * vfs.num_of_bytes_written/(vfs.num_of_bytes_read + vfs.num_of_bytes_written) AS DECIMAL(10,1)) AS [Written Bytes Pct]
FROM sys.dm_io_virtual_file_stats(DB_ID(), NULL) AS vfs
INNER JOIN sys.database_files AS df WITH (NOLOCK)
ON vfs.[file_id]= df.[file_id] OPTION (RECOMPILE);
------

-- This helps you characterize your workload better from an I/O perspective for this database
-- It helps you determine whether you has an OLTP or DW/DSS type of workload


-- Isolate top waits for this database since last restart or failover (Query 23) (Top DB Waits)
WITH [Waits] 
AS (SELECT wait_type, wait_time_ms/ 1000.0 AS [WaitS],
          (wait_time_ms - signal_wait_time_ms) / 1000.0 AS [ResourceS],
           signal_wait_time_ms / 1000.0 AS [SignalS],
           waiting_tasks_count AS [WaitCount],
           100.0 *  wait_time_ms / SUM (wait_time_ms) OVER() AS [Percentage],
           ROW_NUMBER() OVER(ORDER BY wait_time_ms DESC) AS [RowNum]
    FROM sys.dm_db_wait_stats WITH (NOLOCK)
    WHERE [wait_type] NOT IN (
        N'BROKER_EVENTHANDLER', N'BROKER_RECEIVE_WAITFOR', N'BROKER_TASK_STOP',
		N'BROKER_TO_FLUSH', N'BROKER_TRANSMITTER', N'CHECKPOINT_QUEUE',
        N'CHKPT', N'CLR_AUTO_EVENT', N'CLR_MANUAL_EVENT', N'CLR_SEMAPHORE',
        N'DBMIRROR_DBM_EVENT', N'DBMIRROR_EVENTS_QUEUE', N'DBMIRROR_WORKER_QUEUE',
		N'DBMIRRORING_CMD', N'DIRTY_PAGE_POLL', N'DISPATCHER_QUEUE_SEMAPHORE',
        N'EXECSYNC', N'FSAGENT', N'FT_IFTS_SCHEDULER_IDLE_WAIT', N'FT_IFTSHC_MUTEX',
        N'HADR_CLUSAPI_CALL', N'HADR_FILESTREAM_IOMGR_IOCOMPLETION', N'HADR_LOGCAPTURE_WAIT', 
		N'HADR_NOTIFICATION_DEQUEUE', N'HADR_TIMER_TASK', N'HADR_WORK_QUEUE',
        N'KSOURCE_WAKEUP', N'LAZYWRITER_SLEEP', N'LOGMGR_QUEUE', 
		N'MEMORY_ALLOCATION_EXT', N'ONDEMAND_TASK_QUEUE',
		N'PREEMPTIVE_HADR_LEASE_MECHANISM', N'PREEMPTIVE_SP_SERVER_DIAGNOSTICS',
		N'PREEMPTIVE_ODBCOPS',
		N'PREEMPTIVE_OS_LIBRARYOPS', N'PREEMPTIVE_OS_COMOPS', N'PREEMPTIVE_OS_CRYPTOPS',
		N'PREEMPTIVE_OS_PIPEOPS', N'PREEMPTIVE_OS_AUTHENTICATIONOPS',
		N'PREEMPTIVE_OS_GENERICOPS', N'PREEMPTIVE_OS_VERIFYTRUST',
		N'PREEMPTIVE_OS_FILEOPS', N'PREEMPTIVE_OS_DEVICEOPS', N'PREEMPTIVE_OS_QUERYREGISTRY',
		N'PREEMPTIVE_OS_WRITEFILE',
		N'PREEMPTIVE_XE_CALLBACKEXECUTE', N'PREEMPTIVE_XE_DISPATCHER',
		N'PREEMPTIVE_XE_GETTARGETSTATE', N'PREEMPTIVE_XE_SESSIONCOMMIT',
		N'PREEMPTIVE_XE_TARGETINIT', N'PREEMPTIVE_XE_TARGETFINALIZE',
		N'PREEMPTIVE_XHTTP',
        N'PWAIT_ALL_COMPONENTS_INITIALIZED', N'PWAIT_DIRECTLOGCONSUMER_GETNEXT',
		N'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP',
		N'QDS_ASYNC_QUEUE',
        N'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP', N'REQUEST_FOR_DEADLOCK_SEARCH',
		N'RESOURCE_GOVERNOR_IDLE',
		N'RESOURCE_QUEUE', N'SERVER_IDLE_CHECK', N'SLEEP_BPOOL_FLUSH', N'SLEEP_DBSTARTUP',
		N'SLEEP_DCOMSTARTUP', N'SLEEP_MASTERDBREADY', N'SLEEP_MASTERMDREADY',
        N'SLEEP_MASTERUPGRADED', N'SLEEP_MSDBSTARTUP', N'SLEEP_SYSTEMTASK', N'SLEEP_TASK',
        N'SLEEP_TEMPDBSTARTUP', N'SNI_HTTP_ACCEPT', N'SP_SERVER_DIAGNOSTICS_SLEEP',
		N'SQLTRACE_BUFFER_FLUSH', N'SQLTRACE_INCREMENTAL_FLUSH_SLEEP', N'SQLTRACE_WAIT_ENTRIES',
		N'WAIT_FOR_RESULTS', N'WAITFOR', N'WAITFOR_TASKSHUTDOWN', N'WAIT_XTP_HOST_WAIT',
		N'WAIT_XTP_OFFLINE_CKPT_NEW_LOG', N'WAIT_XTP_CKPT_CLOSE', N'WAIT_XTP_RECOVERY',
		N'XE_BUFFERMGR_ALLPROCESSED_EVENT', N'XE_DISPATCHER_JOIN',
        N'XE_DISPATCHER_WAIT', N'XE_LIVE_TARGET_TVF', N'XE_TIMER_EVENT')
    AND waiting_tasks_count > 0)
SELECT
    MAX (W1.wait_type) AS [WaitType],
	CAST (MAX (W1.Percentage) AS DECIMAL (5,2)) AS [Wait Percentage],
	CAST ((MAX (W1.WaitS) / MAX (W1.WaitCount)) AS DECIMAL (16,4)) AS [AvgWait_Sec],
    CAST ((MAX (W1.ResourceS) / MAX (W1.WaitCount)) AS DECIMAL (16,4)) AS [AvgRes_Sec],
    CAST ((MAX (W1.SignalS) / MAX (W1.WaitCount)) AS DECIMAL (16,4)) AS [AvgSig_Sec],
    CAST (MAX (W1.WaitS) AS DECIMAL (16,2)) AS [Total_Wait_Sec],
    CAST (MAX (W1.ResourceS) AS DECIMAL (16,2)) AS [Resource_Sec],
    CAST (MAX (W1.SignalS) AS DECIMAL (16,2)) AS [Signal_Sec],
    MAX (W1.WaitCount) AS [Wait Count]   
FROM Waits AS W1
INNER JOIN Waits AS W2
ON W2.RowNum <= W1.RowNum
GROUP BY W1.RowNum
HAVING SUM (W2.Percentage) - MAX (W1.Percentage) < 99 -- percentage threshold
OPTION (RECOMPILE);
------

-- Cumulative wait stats are not as useful on an idle instance that is not under load or performance pressure

-- SQL Server Wait Types Library (Paul Randal)
-- https://www.sqlskills.com/help/waits/

-- The SQL Server Wait Type Repository
-- http://blogs.msdn.com/b/psssql/archive/2009/11/03/the-sql-server-wait-type-repository.aspx

-- Wait statistics, or please tell me where it hurts
-- https://www.sqlskills.com/blogs/paul/wait-statistics-or-please-tell-me-where-it-hurts/

-- SQL Server 2005 Performance Tuning using the Waits and Queues
-- http://technet.microsoft.com/en-us/library/cc966413.aspx

-- sys.dm_os_wait_stats (Transact-SQL)
-- http://msdn.microsoft.com/en-us/library/ms179984(v=sql.120).aspx



-- Get most frequently executed queries for this database (Query 24) (Query Execution Counts)
SELECT TOP(50) LEFT(t.[text], 50) AS [Short Query Text], qs.execution_count AS [Execution Count],
qs.total_logical_reads AS [Total Logical Reads],
qs.total_logical_reads/qs.execution_count AS [Avg Logical Reads],
qs.total_worker_time AS [Total Worker Time],
qs.total_worker_time/qs.execution_count AS [Avg Worker Time], 
qs.total_elapsed_time AS [Total Elapsed Time],
qs.total_elapsed_time/qs.execution_count AS [Avg Elapsed Time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index], 
qs.creation_time AS [Creation Time]
--,t.[text] AS [Complete Query Text], qp.query_plan AS [Query Plan] -- uncomment out these columns if not copying results to Excel
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t 
CROSS APPLY sys.dm_exec_query_plan(plan_handle) AS qp 
WHERE t.dbid = DB_ID()
ORDER BY qs.execution_count DESC OPTION (RECOMPILE);
------


-- Get top total worker time queries for this database (Query 25) (Top Worker Time Queries)		
SELECT TOP(50) DB_NAME(t.[dbid]) AS [Database Name], 
REPLACE(REPLACE(LEFT(t.[text], 50), CHAR(10),''), CHAR(13),'') AS [Short Query Text],  
qs.total_worker_time AS [Total Worker Time], qs.min_worker_time AS [Min Worker Time],
qs.total_worker_time/qs.execution_count AS [Avg Worker Time], 
qs.max_worker_time AS [Max Worker Time], 
qs.min_elapsed_time AS [Min Elapsed Time], 
qs.total_elapsed_time/qs.execution_count AS [Avg Elapsed Time], 
qs.max_elapsed_time AS [Max Elapsed Time],
qs.min_logical_reads AS [Min Logical Reads],
qs.total_logical_reads/qs.execution_count AS [Avg Logical Reads],
qs.max_logical_reads AS [Max Logical Reads], 
qs.execution_count AS [Execution Count],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index],  
qs.creation_time AS [Creation Time]
--,t.[text] AS [Query Text], qp.query_plan AS [Query Plan] -- uncomment out these columns if not copying results to Excel
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t 
CROSS APPLY sys.dm_exec_query_plan(plan_handle) AS qp
WHERE t.dbid = DB_ID() 
ORDER BY qs.total_worker_time DESC OPTION (RECOMPILE);
------

-- Helps you find the most expensive queries from a CPU perspective for this database
-- Can also help track down parameter sniffing issues


-- Get top total logical reads queries for this database (Query 26) (Top Logical Reads Queries)    
SELECT TOP(50) DB_NAME(t.[dbid]) AS [Database Name],
REPLACE(REPLACE(LEFT(t.[text], 50), CHAR(10),''), CHAR(13),'') AS [Short Query Text], 
qs.total_logical_reads AS [Total Logical Reads],
qs.min_logical_reads AS [Min Logical Reads],
qs.total_logical_reads/qs.execution_count AS [Avg Logical Reads],
qs.max_logical_reads AS [Max Logical Reads],   
qs.min_worker_time AS [Min Worker Time],
qs.total_worker_time/qs.execution_count AS [Avg Worker Time], 
qs.max_worker_time AS [Max Worker Time], 
qs.min_elapsed_time AS [Min Elapsed Time], 
qs.total_elapsed_time/qs.execution_count AS [Avg Elapsed Time], 
qs.max_elapsed_time AS [Max Elapsed Time],
qs.execution_count AS [Execution Count],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index],   
qs.creation_time AS [Creation Time]
--,t.[text] AS [Complete Query Text], qp.query_plan AS [Query Plan] -- uncomment out these columns if not copying results to Excel
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t 
CROSS APPLY sys.dm_exec_query_plan(plan_handle) AS qp
WHERE t.dbid = DB_ID()  
ORDER BY qs.total_logical_reads DESC OPTION (RECOMPILE);
------


-- Helps you find the most expensive queries from a memory perspective for this database
-- Can also help track down parameter sniffing issues



-- Get top average elapsed time queries for this database (Query 27) (Top Avg Elapsed Time Queries)   
SELECT TOP(50) DB_NAME(t.[dbid]) AS [Database Name], 
REPLACE(REPLACE(LEFT(t.[text], 255), CHAR(10),''), CHAR(13),'') AS [Short Query Text],  
qs.total_elapsed_time/qs.execution_count AS [Avg Elapsed Time],
qs.min_elapsed_time, qs.max_elapsed_time, qs.last_elapsed_time,
qs.execution_count AS [Execution Count],  
qs.total_logical_reads/qs.execution_count AS [Avg Logical Reads], 
qs.total_physical_reads/qs.execution_count AS [Avg Physical Reads], 
qs.total_worker_time/qs.execution_count AS [Avg Worker Time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index],  
qs.creation_time AS [Creation Time]
, qp.query_plan AS [Query Plan] -- comment out this column if copying results to Excel
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t 
CROSS APPLY sys.dm_exec_query_plan(plan_handle) AS qp
WHERE t.dbid = DB_ID()  
ORDER BY qs.total_elapsed_time/qs.execution_count DESC OPTION (RECOMPILE);
------

-- Helps you find the highest average elapsed time queries for this database
-- Can also help track down parameter sniffing issues



-- Top Cached SPs By Execution Count (Query 28) (SP Execution Counts)
SELECT TOP(100) p.name AS [SP Name], qs.execution_count AS [Execution Count],
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute],
qs.total_elapsed_time/qs.execution_count AS [Avg Elapsed Time],
qs.total_worker_time/qs.execution_count AS [Avg Worker Time],    
qs.total_logical_reads/qs.execution_count AS [Avg Logical Reads],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index],
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND DATEDIFF(Minute, qs.cached_time, GETDATE()) > 0
ORDER BY qs.execution_count DESC OPTION (RECOMPILE);
------

-- Tells you which cached stored procedures are called the most often
-- This helps you characterize and baseline your workload


-- Top Cached SPs By Avg Elapsed Time (Query 29) (SP Avg Elapsed Time)
SELECT TOP(25) p.name AS [SP Name], qs.min_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time], 
qs.max_elapsed_time, qs.last_elapsed_time, qs.total_elapsed_time, qs.execution_count, 
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute], 
qs.total_worker_time/qs.execution_count AS [AvgWorkerTime], 
qs.total_worker_time AS [TotalWorkerTime],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index],
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND DATEDIFF(Minute, qs.cached_time, GETDATE()) > 0
ORDER BY avg_elapsed_time DESC OPTION (RECOMPILE);
------

-- This helps you find high average elapsed time cached stored procedures that
-- may be easy to optimize with standard query tuning techniques



-- Top Cached SPs By Total Worker time. Worker time relates to CPU cost  (Query 30) (SP Worker Time)
SELECT TOP(25) p.name AS [SP Name], qs.total_worker_time AS [TotalWorkerTime], 
qs.total_worker_time/qs.execution_count AS [AvgWorkerTime], qs.execution_count, 
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute],
qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index],
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND DATEDIFF(Minute, qs.cached_time, GETDATE()) > 0
ORDER BY qs.total_worker_time DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a CPU perspective
-- You should look at this if you see signs of CPU pressure


-- Top Cached SPs By Total Logical Reads. Logical reads relate to memory pressure  (Query 31) (SP Logical Reads)
SELECT TOP(25) p.name AS [SP Name], qs.total_logical_reads AS [TotalLogicalReads], 
qs.total_logical_reads/qs.execution_count AS [AvgLogicalReads],qs.execution_count, 
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute], 
qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index], 
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND DATEDIFF(Minute, qs.cached_time, GETDATE()) > 0
ORDER BY qs.total_logical_reads DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a memory perspective
-- You should look at this if you see signs of memory pressure


-- Top Cached SPs By Total Physical Reads. Physical reads relate to disk read I/O pressure  (Query 32) (SP Physical Reads)
SELECT TOP(25) p.name AS [SP Name],qs.total_physical_reads AS [TotalPhysicalReads], 
qs.total_physical_reads/qs.execution_count AS [AvgPhysicalReads], qs.execution_count, 
qs.total_logical_reads,qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index],
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan 
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND qs.total_physical_reads > 0
ORDER BY qs.total_physical_reads DESC, qs.total_logical_reads DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a read I/O perspective
-- You should look at this if you see signs of I/O pressure or of memory pressure
       


-- Top Cached SPs By Total Logical Writes (Query 33) (SP Logical Writes)
-- Logical writes relate to both memory and disk I/O pressure 
SELECT TOP(25) p.name AS [SP Name], qs.total_logical_writes AS [TotalLogicalWrites], 
qs.total_logical_writes/qs.execution_count AS [AvgLogicalWrites], qs.execution_count,
ISNULL(qs.execution_count/DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute],
qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time],
CASE WHEN CONVERT(nvarchar(max), qp.query_plan) LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index], 
FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time], 
FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan 
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
AND qs.total_logical_writes > 0
AND DATEDIFF(Minute, qs.cached_time, GETDATE()) > 0
ORDER BY qs.total_logical_writes DESC OPTION (RECOMPILE);
------

-- This helps you find the most expensive cached stored procedures from a write I/O perspective
-- You should look at this if you see signs of I/O pressure or of memory pressure



-- Lists the top statements by average input/output usage for the current database  (Query 34) (Top IO Statements)
SELECT TOP(50) OBJECT_NAME(qt.objectid, dbid) AS [SP Name],
(qs.total_logical_reads + qs.total_logical_writes) /qs.execution_count AS [Avg IO], qs.execution_count AS [Execution Count],
SUBSTRING(qt.[text],qs.statement_start_offset/2, 
	(CASE 
		WHEN qs.statement_end_offset = -1 
	 THEN LEN(CONVERT(nvarchar(max), qt.[text])) * 2 
		ELSE qs.statement_end_offset 
	 END - qs.statement_start_offset)/2) AS [Query Text]	
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS qt
WHERE qt.[dbid] = DB_ID()
ORDER BY [Avg IO] DESC OPTION (RECOMPILE);
------

-- Helps you find the most expensive statements for I/O by SP



-- Possible Bad NC Indexes (writes > reads)  (Query 35) (Bad NC Indexes)
SELECT OBJECT_NAME(s.[object_id]) AS [Table Name], i.name AS [Index Name], i.index_id, 
i.is_disabled, i.is_hypothetical, i.has_filter, i.fill_factor,
user_updates AS [Total Writes], user_seeks + user_scans + user_lookups AS [Total Reads],
user_updates - (user_seeks + user_scans + user_lookups) AS [Difference]
FROM sys.dm_db_index_usage_stats AS s WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON s.[object_id] = i.[object_id]
AND i.index_id = s.index_id
WHERE OBJECTPROPERTY(s.[object_id],'IsUserTable') = 1
AND s.database_id = DB_ID()
AND user_updates > (user_seeks + user_scans + user_lookups)
AND i.index_id > 1
ORDER BY [Difference] DESC, [Total Writes] DESC, [Total Reads] ASC OPTION (RECOMPILE);
------

-- Look for indexes with high numbers of writes and zero or very low numbers of reads
-- Consider your complete workload, and how long your instance has been running
-- Investigate further before dropping an index!


-- Missing Indexes for current database by Index Advantage  (Query 36) (Missing Indexes)
SELECT DISTINCT CONVERT(decimal(18,2), user_seeks * avg_total_user_cost * (avg_user_impact * 0.01)) AS [index_advantage], 
migs.last_user_seek, mid.[statement] AS [Database.Schema.Table],
mid.equality_columns, mid.inequality_columns, mid.included_columns,
migs.unique_compiles, migs.user_seeks, migs.avg_total_user_cost, migs.avg_user_impact,
OBJECT_NAME(mid.[object_id]) AS [Table Name], p.rows AS [Table Rows]
FROM sys.dm_db_missing_index_group_stats AS migs WITH (NOLOCK)
INNER JOIN sys.dm_db_missing_index_groups AS mig WITH (NOLOCK)
ON migs.group_handle = mig.index_group_handle
INNER JOIN sys.dm_db_missing_index_details AS mid WITH (NOLOCK)
ON mig.index_handle = mid.index_handle
INNER JOIN sys.partitions AS p WITH (NOLOCK)
ON p.[object_id] = mid.[object_id]
WHERE mid.database_id = DB_ID() 
ORDER BY index_advantage DESC OPTION (RECOMPILE);
------

-- Look at index advantage, last user seek time, number of user seeks to help determine source and importance
-- SQL Server is overly eager to add included columns, so beware
-- Do not just blindly add indexes that show up from this query!!!


-- Find missing index warnings for cached plans in the current database  (Query 37) (Missing Index Warnings)
-- Note: This query could take some time on a busy instance
SELECT TOP(25) OBJECT_NAME(objectid) AS [ObjectName], 
               cp.objtype, cp.usecounts, cp.size_in_bytes, query_plan
FROM sys.dm_exec_cached_plans AS cp WITH (NOLOCK)
CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) AS qp
WHERE CAST(query_plan AS NVARCHAR(MAX)) LIKE N'%MissingIndex%'
AND dbid = DB_ID()
ORDER BY cp.usecounts DESC OPTION (RECOMPILE);
------

-- Helps you connect missing indexes to specific stored procedures or queries
-- This can help you decide whether to add them or not


-- Breaks down buffers used by current database by object (table, index) in the buffer cache  (Query 38) (Buffer Usage)
-- Note: This query could take some time on a busy instance
SELECT OBJECT_NAME(p.[object_id]) AS [Object Name], p.index_id, 
CAST(COUNT(*)/128.0 AS DECIMAL(10, 2)) AS [Buffer size(MB)],  
COUNT(*) AS [BufferCount], p.[Rows] AS [Row Count],
p.data_compression_desc AS [Compression Type]
FROM sys.allocation_units AS a WITH (NOLOCK)
INNER JOIN sys.dm_os_buffer_descriptors AS b WITH (NOLOCK)
ON a.allocation_unit_id = b.allocation_unit_id
INNER JOIN sys.partitions AS p WITH (NOLOCK)
ON a.container_id = p.hobt_id
WHERE b.database_id = CONVERT(int, DB_ID())
AND p.[object_id] > 100
AND OBJECT_NAME(p.[object_id]) NOT LIKE N'plan_%'
AND OBJECT_NAME(p.[object_id]) NOT LIKE N'sys%'
AND OBJECT_NAME(p.[object_id]) NOT LIKE N'xml_index_nodes%'
GROUP BY p.[object_id], p.index_id, p.data_compression_desc, p.[Rows]
ORDER BY [BufferCount] DESC OPTION (RECOMPILE);
------

-- Tells you what tables and indexes are using the most memory in the buffer cache
-- It can help identify possible candidates for data compression


-- Get Table names, row counts, and compression status for clustered index or heap  (Query 39) (Table Sizes)
SELECT OBJECT_NAME(object_id) AS [ObjectName], 
SUM(Rows) AS [RowCount], data_compression_desc AS [CompressionType]
FROM sys.partitions WITH (NOLOCK)
WHERE index_id < 2 --ignore the partitions from the non-clustered index if any
AND OBJECT_NAME(object_id) NOT LIKE N'sys%'
AND OBJECT_NAME(object_id) NOT LIKE N'queue_%' 
AND OBJECT_NAME(object_id) NOT LIKE N'filestream_tombstone%' 
AND OBJECT_NAME(object_id) NOT LIKE N'fulltext%'
AND OBJECT_NAME(object_id) NOT LIKE N'ifts_comp_fragment%'
AND OBJECT_NAME(object_id) NOT LIKE N'filetable_updates%'
AND OBJECT_NAME(object_id) NOT LIKE N'xml_index_nodes%'
AND OBJECT_NAME(object_id) NOT LIKE N'sqlagent_job%'  
AND OBJECT_NAME(object_id) NOT LIKE N'plan_persist%'  
AND OBJECT_NAME(object_id) NOT LIKE N'persistent_version%'
AND OBJECT_NAME(object_id) NOT LIKE N'database_firewall%'
AND OBJECT_NAME(object_id) NOT LIKE N'wpr_bucket%'
GROUP BY object_id, data_compression_desc
ORDER BY SUM(Rows) DESC OPTION (RECOMPILE);
------

-- Gives you an idea of table sizes, and possible data compression opportunities



-- Get some key table properties (Query 40) (Table Properties)
SELECT OBJECT_NAME(t.[object_id]) AS [ObjectName], p.[rows] AS [Table Rows], p.index_id, 
       p.data_compression_desc AS [Index Data Compression],
       t.create_date, t.lock_on_bulk_load,  
       t.is_tracked_by_cdc, t.lock_escalation_desc, t.is_filetable, 
	   t.is_memory_optimized, t.durability_desc, 
	   t.temporal_type_desc
FROM sys.tables AS t WITH (NOLOCK)
INNER JOIN sys.partitions AS p WITH (NOLOCK)
ON t.[object_id] = p.[object_id]
WHERE OBJECT_NAME(t.[object_id]) NOT LIKE N'sys%'
ORDER BY OBJECT_NAME(t.[object_id]), p.index_id OPTION (RECOMPILE);
------

-- Gives you some good information about your tables
-- is_memory_optimized and durability_desc were new in SQL Server 2014
-- temporal_type_desc, is_remote_data_archive_enabled, is_external are new in SQL Server 2016

-- sys.tables (Transact-SQL)
-- https://bit.ly/2Gk7998



-- When were Statistics last updated on all indexes?  (Query 41) (Statistics Update)
SELECT SCHEMA_NAME(o.Schema_ID) + N'.' + o.[NAME] AS [Object Name], o.[type_desc] AS [Object Type],
      i.[name] AS [Index Name], STATS_DATE(i.[object_id], i.index_id) AS [Statistics Date], 
      s.auto_created, s.no_recompute, s.user_created, s.is_incremental, s.is_temporary,
	  st.row_count, st.used_page_count
FROM sys.objects AS o WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON o.[object_id] = i.[object_id]
INNER JOIN sys.stats AS s WITH (NOLOCK)
ON i.[object_id] = s.[object_id] 
AND i.index_id = s.stats_id
INNER JOIN sys.dm_db_partition_stats AS st WITH (NOLOCK)
ON o.[object_id] = st.[object_id]
AND i.[index_id] = st.[index_id]
WHERE o.[type] IN ('U', 'V')
AND st.row_count > 0
ORDER BY STATS_DATE(i.[object_id], i.index_id) DESC OPTION (RECOMPILE);
------  

-- Helps discover possible problems with out-of-date statistics
-- Also gives you an idea which indexes are the most active

-- sys.stats (Transact-SQL)
-- https://bit.ly/2GyAxrn



-- Look at most frequently modified indexes and statistics (Query 42) (Volatile Indexes)
SELECT o.[name] AS [Object Name], o.[object_id], o.[type_desc], s.[name] AS [Statistics Name], 
       s.stats_id, s.no_recompute, s.auto_created, s.is_incremental, s.is_temporary,
	   sp.modification_counter, sp.[rows], sp.rows_sampled, sp.last_updated
FROM sys.objects AS o WITH (NOLOCK)
INNER JOIN sys.stats AS s WITH (NOLOCK)
ON s.object_id = o.object_id
CROSS APPLY sys.dm_db_stats_properties(s.object_id, s.stats_id) AS sp
WHERE o.[type_desc] NOT IN (N'SYSTEM_TABLE', N'INTERNAL_TABLE')
AND sp.modification_counter > 0
ORDER BY sp.modification_counter DESC, o.name OPTION (RECOMPILE);
------

-- This helps you understand your workload and make better decisions about 
-- things like data compression and adding new indexes to a table



-- Get fragmentation info for all indexes above a certain size in the current database  (Query 43) (Index Fragmentation)
-- Note: This query could take some time on a very large database
SELECT DB_NAME(ps.database_id) AS [Database Name], SCHEMA_NAME(o.[schema_id]) AS [Schema Name],
OBJECT_NAME(ps.OBJECT_ID) AS [Object Name], i.[name] AS [Index Name], ps.index_id, 
ps.index_type_desc, ps.avg_fragmentation_in_percent, 
ps.fragment_count, ps.page_count, i.fill_factor, i.has_filter, 
i.filter_definition, i.[allow_page_locks]
FROM sys.dm_db_index_physical_stats(DB_ID(),NULL, NULL, NULL , N'LIMITED') AS ps
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON ps.[object_id] = i.[object_id] 
AND ps.index_id = i.index_id
INNER JOIN sys.objects AS o WITH (NOLOCK)
ON i.[object_id] = o.[object_id]
WHERE ps.database_id = DB_ID()
AND ps.page_count > 2500
ORDER BY ps.avg_fragmentation_in_percent DESC OPTION (RECOMPILE);
------

-- Helps determine whether you have framentation in your relational indexes
-- and how effective your index maintenance strategy is


--- Index Read/Write stats (all tables in current DB) ordered by Reads  (Query 44) (Overall Index Usage - Reads)
SELECT OBJECT_NAME(i.[object_id]) AS [ObjectName], i.[name] AS [IndexName], i.index_id, 
       s.user_seeks, s.user_scans, s.user_lookups,
	   s.user_seeks + s.user_scans + s.user_lookups AS [Total Reads], 
	   s.user_updates AS [Writes],  
	   i.[type_desc] AS [Index Type], i.fill_factor AS [Fill Factor], i.has_filter, i.filter_definition, 
	   s.last_user_scan, s.last_user_lookup, s.last_user_seek
FROM sys.indexes AS i WITH (NOLOCK)
LEFT OUTER JOIN sys.dm_db_index_usage_stats AS s WITH (NOLOCK)
ON i.[object_id] = s.[object_id]
AND i.index_id = s.index_id
AND s.database_id = DB_ID()
WHERE OBJECTPROPERTY(i.[object_id],'IsUserTable') = 1
ORDER BY s.user_seeks + s.user_scans + s.user_lookups DESC OPTION (RECOMPILE); -- Order by reads
------

-- Show which indexes in the current database are most active for Reads


--- Index Read/Write stats (all tables in current DB) ordered by Writes  (Query 45) (Overall Index Usage - Writes)
SELECT OBJECT_NAME(i.[object_id]) AS [ObjectName], i.[name] AS [IndexName], i.index_id,
	   s.user_updates AS [Writes], s.user_seeks + s.user_scans + s.user_lookups AS [Total Reads], 
	   i.[type_desc] AS [Index Type], i.fill_factor AS [Fill Factor], i.has_filter, i.filter_definition,
	   s.last_system_update, s.last_user_update
FROM sys.indexes AS i WITH (NOLOCK)
LEFT OUTER JOIN sys.dm_db_index_usage_stats AS s WITH (NOLOCK)
ON i.[object_id] = s.[object_id]
AND i.index_id = s.index_id
AND s.database_id = DB_ID()
WHERE OBJECTPROPERTY(i.[object_id],'IsUserTable') = 1
ORDER BY s.user_updates DESC OPTION (RECOMPILE);						 -- Order by writes
------

-- Show which indexes in the current database are most active for Writes


-- Get in-memory OLTP index usage (Query 46) (XTP Index Usage)
SELECT OBJECT_NAME(i.[object_id]) AS [Object Name], i.index_id, i.[name] AS [Index Name],
       i.[type_desc], xis.scans_started, xis.scans_retries, 
	   xis.rows_touched, xis.rows_returned
FROM sys.dm_db_xtp_index_stats AS xis WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON i.[object_id] = xis.[object_id] 
AND i.index_id = xis.index_id 
ORDER BY OBJECT_NAME(i.[object_id]) OPTION (RECOMPILE);
------

-- This gives you some index usage statistics for in-memory OLTP
-- Returns no data if you are not using in-memory OLTP

-- Guidelines for Using Indexes on Memory-Optimized Tables
-- https://bit.ly/2GCP8lF



-- Look at Columnstore index physical statistics (Query 47) (Columnstore Index Physical Stat)
SELECT OBJECT_NAME(ps.object_id) AS [TableName],  
	i.[name] AS [IndexName], ps.index_id, ps.partition_number,
	ps.delta_store_hobt_id, ps.state_desc, ps.total_rows, ps.size_in_bytes,
	ps.trim_reason_desc, ps.generation, ps.transition_to_compressed_state_desc,
	ps.has_vertipaq_optimization, ps.deleted_rows,
	100 * (ISNULL(ps.deleted_rows, 0))/ps.total_rows AS [Fragmentation]
FROM sys.dm_db_column_store_row_group_physical_stats AS ps WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON ps.object_id = i.object_id 
AND ps.index_id = i.index_id
ORDER BY ps.object_id, ps.partition_number, ps.row_group_id OPTION (RECOMPILE);
------

-- sys.dm_db_column_store_row_group_physical_stats (Transact-SQL)
-- https://bit.ly/2q276XQ



-- Get lock waits for current database (Query 48) (Lock Waits)
SELECT o.name AS [table_name], i.name AS [index_name], ios.index_id, ios.partition_number,
		SUM(ios.row_lock_wait_count) AS [total_row_lock_waits], 
		SUM(ios.row_lock_wait_in_ms) AS [total_row_lock_wait_in_ms],
		SUM(ios.page_lock_wait_count) AS [total_page_lock_waits],
		SUM(ios.page_lock_wait_in_ms) AS [total_page_lock_wait_in_ms],
		SUM(ios.page_lock_wait_in_ms)+ SUM(row_lock_wait_in_ms) AS [total_lock_wait_in_ms]
FROM sys.dm_db_index_operational_stats(DB_ID(), NULL, NULL, NULL) AS ios
INNER JOIN sys.objects AS o WITH (NOLOCK)
ON ios.[object_id] = o.[object_id]
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON ios.[object_id] = i.[object_id] 
AND ios.index_id = i.index_id
WHERE o.[object_id] > 100
GROUP BY o.name, i.name, ios.index_id, ios.partition_number
HAVING SUM(ios.page_lock_wait_in_ms)+ SUM(row_lock_wait_in_ms) > 0
ORDER BY total_lock_wait_in_ms DESC OPTION (RECOMPILE);
------

-- This query is helpful for troubleshooting blocking and deadlocking issues



-- Look at UDF execution statistics (Query 49) (UDF Statistics)
SELECT OBJECT_NAME(object_id) AS [Function Name], total_worker_time,
       execution_count, total_elapsed_time,  
       total_elapsed_time/execution_count AS [avg_elapsed_time],  
       last_elapsed_time, last_execution_time, cached_time 
FROM sys.dm_exec_function_stats WITH (NOLOCK) 
WHERE database_id = DB_ID()
ORDER BY total_worker_time DESC OPTION (RECOMPILE); 
------


-- Helps you investigate scalar UDF performance issues

-- sys.dm_exec_function_stats (Transact-SQL)
-- https://bit.ly/2q1Q6BM




-- Get QueryStore Options for this database (Query 50) (QueryStore Options)
SELECT actual_state_desc, desired_state_desc,
       current_storage_size_mb, [max_storage_size_mb], 
	   query_capture_mode_desc, size_based_cleanup_mode_desc
FROM sys.database_query_store_options WITH (NOLOCK) OPTION (RECOMPILE);
------

-- New for SQL Server 2016
-- Requires that QueryStore is enabled for this database

-- Tuning Workload Performance with Query Store
-- https://bit.ly/1kHSl7w


-- Get highest aggregate duration queries over last hour (Query 51) (High Aggregate Duration Queries)
WITH AggregatedDurationLastHour
AS
(SELECT q.query_id, SUM(count_executions * avg_duration) AS total_duration,
   COUNT (distinct p.plan_id) AS number_of_plans
   FROM sys.query_store_query_text AS qt WITH (NOLOCK)
   INNER JOIN sys.query_store_query AS q WITH (NOLOCK)
   ON qt.query_text_id = q.query_text_id
   INNER JOIN sys.query_store_plan AS p WITH (NOLOCK)
   ON q.query_id = p.query_id
   INNER JOIN sys.query_store_runtime_stats AS rs WITH (NOLOCK)
   ON rs.plan_id = p.plan_id
   INNER JOIN sys.query_store_runtime_stats_interval AS rsi WITH (NOLOCK)
   ON rsi.runtime_stats_interval_id = rs.runtime_stats_interval_id
   WHERE rsi.start_time >= DATEADD(hour, -1, GETUTCDATE()) 
   AND rs.execution_type_desc = N'Regular'
   GROUP BY q.query_id),
OrderedDuration AS
(SELECT query_id, total_duration, number_of_plans, 
 ROW_NUMBER () OVER (ORDER BY total_duration DESC, query_id) AS RN
 FROM AggregatedDurationLastHour)
SELECT OBJECT_NAME(q.object_id) AS [Containing Object], qt.query_sql_text, 
od.total_duration AS [Total Duration (microsecs)], 
od.number_of_plans AS [Plan Count],
p.is_forced_plan, p.is_parallel_plan, p.is_trivial_plan,
q.query_parameterization_type_desc, p.[compatibility_level],
p.last_compile_start_time, q.last_execution_time,
CONVERT(xml, p.query_plan) AS query_plan_xml 
FROM OrderedDuration AS od 
INNER JOIN sys.query_store_query AS q WITH (NOLOCK)
ON q.query_id  = od.query_id
INNER JOIN sys.query_store_query_text AS qt WITH (NOLOCK)
ON q.query_text_id = qt.query_text_id
INNER JOIN sys.query_store_plan AS p WITH (NOLOCK)
ON q.query_id = p.query_id
WHERE od.RN <= 50 
ORDER BY total_duration DESC OPTION (RECOMPILE);
------

-- New for SQL Server 2016
-- Requires that QueryStore is enabled for this database


-- Get input buffer information for the current database (Query 52) (Input Buffer)
SELECT es.session_id, DB_NAME(es.database_id) AS [Database Name],
       es.login_time, es.cpu_time, es.logical_reads,
       es.[status], ib.event_info AS [Input Buffer]
FROM sys.dm_exec_sessions AS es WITH (NOLOCK)
CROSS APPLY sys.dm_exec_input_buffer(es.session_id, NULL) AS ib
WHERE es.database_id = DB_ID()
AND es.session_id > 50
AND es.session_id <> @@SPID OPTION (RECOMPILE);
------

-- Gives you input buffer information from all non-system sessions for the current database
-- Replaces DBCC INPUTBUFFER

-- New DMF for retrieving input buffer in SQL Server
-- https://bit.ly/2uHKMbz



-- Get any resumable index rebuild operation information (Query 53) (Resumable Index Rebuild)
SELECT OBJECT_NAME(iro.object_id) AS [Object Name], iro.index_id, iro.name AS [Index Name],
       iro.sql_text, iro.last_max_dop_used, iro.partition_number, iro.state_desc, iro.start_time, iro.percent_complete
FROM  sys.index_resumable_operations AS iro WITH (NOLOCK)
OPTION (RECOMPILE);
------ 

-- index_resumable_operations (Transact-SQL)
-- https://bit.ly/2pYSWqq


-- Get database automatic tuning options (Query 54) (Automatic Tuning Options)
SELECT [name], desired_state_desc, actual_state_desc, reason_desc
FROM sys.database_automatic_tuning_options WITH (NOLOCK)
OPTION (RECOMPILE);
------ 

-- sys.database_automatic_tuning_options (Transact-SQL)
-- https://bit.ly/2FHhLkL



-- Get geo-replication link status for all secondary databases (Query 55) (Geo-Replication Link Status)
SELECT link_guid, partner_server, partner_database, last_replication, 
       replication_lag_sec, replication_state_desc, role_desc, secondary_allow_connections_desc 
FROM sys.dm_geo_replication_link_status;
------  

-- sys.dm_geo_replication_link_status (Azure SQL Database)
-- https://bit.ly/2GwIqC2



-- Retrieve some Azure SQL Database properties (Query 56) (Azure SQL DB Properties)
SELECT DATABASEPROPERTYEX (DB_NAME(DB_ID()), 'Edition') AS [Database Edition],
	   DATABASEPROPERTYEX (DB_NAME(DB_ID()), 'ServiceObjective') AS [Service Objective],
	   DATABASEPROPERTYEX (DB_NAME(DB_ID()), 'MaxSizeInBytes') AS [Max Size In Bytes],
	   DATABASEPROPERTYEX (DB_NAME(DB_ID()), 'IsXTPSupported') AS [Is XTP Supported];   
------  

-- DATABASEPROPERTYEX (Transact-SQL)
-- https://bit.ly/2ItexPg





-- Sign up for Microsoft Visual Studio Dev Essentials and get a free three month pass to Pluralsight

-- Microsoft Visual Studio Dev Essentials
-- http://bit.ly/1q6xbDL


-- Sign up for Microsoft IT Pro Cloud Essentials and get lots of free Azure usage credits, MCP exam voucher, three month Pluralsight subscription

-- Microsoft IT Pro Cloud Essentials
-- http://bit.ly/2443SAd


tools\dbatools\bin\library.ps1
# Current library Version the module expects
$currentLibraryVersion = New-Object System.Version(0, 10, 0, 57)

<#
Library Versioning 101:
The version consists of 4 segments: Major, Minor, Build, Revision

Major: Should always be equal to the main version number of the dbatools PowerShell project.
Minor: Tracks major features within a major release. Increment on new features or significant structural changes. Reset to 0 when incrementing the major version.
Build: Tracks lesser functionality upgrades. Increment on all minor upgrades, reset to 0 when introducing a new major feature or major version.
Revision: Tracks all changes. Every single update to the library - bugfix, feature or redesign - increments the revision counter. It is never reset to 0.

Updating the library version number:
When changing the library version number, it is necessary to do so in TWO places:
- At the top of this very library.ps1
- Within AssemblyInfo.cs
These two locations MUST have matching version numbers, otherwise it will keep building the library and complaining about version mismatch!
#>

<#
#---------------------------------#
# Runtime configuration variables #
#---------------------------------#

The library recognizes a few external variables in order to customize its behavior on import.

$dbatools_strictsecuritymode
Setting this to $true will cause dbatools to always load the library directly from the module directory.
This is more secure, but less convenient when it comes to updating the module, as all consoles using it must be closed.

$dbatools_alwaysbuildlibrary
Setting this to $true will cause the module to always build the library from source, rather than reuse the binaries.
Mostly for developers working on the library.

#>

#region Test whether the module had already been imported
if (([System.Management.Automation.PSTypeName]'Sqlcollaborative.Dbatools.Configuration.Config').Type) {
    # No need to load the library again, if the module was once already imported.
    Write-Verbose -Message "Library already loaded, will not load again"
    $ImportLibrary = $false
}
else {
    Write-Verbose -Message "Library not present already, will import"
    $ImportLibrary = $true
}
#endregion Test whether the module had already been imported

if ($ImportLibrary) {
    #region Add Code
    try {
        $libraryBase = $ExecutionContext.SessionState.Module.ModuleBase + "\bin"
        # In strict security mode, only load from the already pre-compiled binary within the module
        if ($script:strictSecurityMode) {
            if (Test-Path -Path "$libraryBase\dbatools.dll") {
                Add-Type -Path "$libraryBase\dbatools.dll" -ErrorAction Stop
            }
            else {
                throw "Library not found, terminating!"
            }
        }
        # Else we prioritize user convenience
        else {
            $hasProject = Test-Path -Path "$libraryBase\projects\dbatools\dbatools.sln"
            $hasCompiledDll = Test-Path -Path "$libraryBase\dbatools.dll"

            if ((-not $script:alwaysBuildLibrary) -and $hasCompiledDll -and ([System.Diagnostics.FileVersionInfo]::GetVersionInfo("$libraryBase\dbatools.dll").FileVersion -eq $currentLibraryVersion)) {
                $start = Get-Date
                try {
                    Write-Verbose -Message "Found library, trying to copy & import"
                    if ($libraryBase -ne $script:DllRoot) { Copy-Item -Path "$libraryBase\dbatools.dll" -Destination $script:DllRoot -Force -ErrorAction Stop }
                    Add-Type -Path "$script:DllRoot\dbatools.dll" -ErrorAction Stop
                }
                catch {
                    Write-Verbose -Message "Failed to copy&import, attempting to import straight from the module directory"
                    Add-Type -Path "$libraryBase\dbatools.dll" -ErrorAction Stop
                }
                Write-Verbose -Message "Total duration: $((Get-Date) - $start)"
            }
            elseif ($hasProject) {
                "$($PSScriptRoot)\build-project.ps1"
            }
            else {
                throw "No valid dbatools library found! Check your module integrity"
            }
        }

        #region PowerShell TypeData
        Update-TypeData -TypeName "Sqlcollaborative.Dbatools.dbaSystem.DbatoolsException" -SerializationDepth 2 -ErrorAction Ignore
        Update-TypeData -TypeName "Sqlcollaborative.Dbatools.dbaSystem.DbatoolsExceptionRecord" -SerializationDepth 2 -ErrorAction Ignore
        #endregion PowerShell TypeData
    }
    catch {
        #region Warning
        Write-Warning @'
Dear User,

in the name of the dbatools team I apologize for the inconvenience.
Generally, when something goes wrong we try to handle and interpret in an
understandable manner. Unfortunately, something went awry with importing
our main library, so all the systems making this possible would not be initialized
yet. We have taken great pains to avoid this issue but this notification indicates
we have failed.

Please, in order to help us prevent this from happening again, visit us at:
https://github.com/sqlcollaborative/dbatools/issues
and tell us about this failure. All information will be appreciated, but
especially valuable are:
- Exports of the exception: $Error | Export-Clixml error.xml -Depth 4
- Screenshots
- Environment information (Operating System, Hardware Stats, .NET Version,
  PowerShell Version and whatever else you may consider of potential impact.)

Again, I apologize for the inconvenience and hope we will be able to speedily
resolve the issue.

Best Regards,
Friedrich Weinmann
aka "The guy who made most of The Library that Failed to import"

'@
        throw
        #endregion Warning
    }
    #endregion Add Code
}

#region Version Warning
if ($currentLibraryVersion -ne ([version](([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object ManifestModule -like "dbatools.dll").CustomAttributes | Where-Object AttributeType -like "System.Reflection.AssemblyFileVersionAttribute").ConstructorArguments.Value)) {
    Write-Warning @"
A version missmatch between the dbatools library loaded and the one expected by
this module. This usually happens when you update the dbatools module and use
Remove-Module / Import-Module in order to load the latest version without
starting a new PowerShell instance.

Please restart the console to apply the library update, or unexpected behavior will likely occur.

If the issues continue to persist, please Remove-Item '$script:PSModuleRoot\bin\dbatools.dll'
"@
}
#endregion Version Warning
tools\dbatools\bin\LumenWorks.Framework.IO.dll
md5: 0A263CE4A3000CCC9233910FA81A0505 | sha1: 9DC0FECF034A40787EC087FCEE48F168C90631CC | sha256: B7F1F2EAF736A7AB0299D03E58DE52CED0E9EAA6731E21D5587E85C1829825A6 | sha512: 16D4B169CF832BC6C6603E61278B0BF320E2D800460A3AE8D13C9BE26A381038AB9617B1A82988749A2880518944DCB19166E46F864A081D20853FE9D0275533
tools\dbatools\bin\perfmontemplates\collectorsets.xml
<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
  <Obj RefId="0">
    <TN RefId="0">
      <T>Deserialized.System.Management.Automation.PSCustomObject</T>
      <T>Deserialized.System.Object</T>
    </TN>
    <MS>
      <S N="Name">Long Running Queries</S>
      <S N="Source">Brad McGehee</S>
    </MS>
  </Obj>
  <Obj RefId="1">
    <TNRef RefId="0" />
    <MS>
      <S N="Name">PAL - SQL Server 2005</S>
      <S N="Source">Clint Huffman</S>
    </MS>
  </Obj>
  <Obj RefId="2">
    <TNRef RefId="0" />
    <MS>
      <S N="Name">PAL - SQL Server 2008 and R2</S>
      <S N="Source">Clint Huffman</S>
    </MS>
  </Obj>
  <Obj RefId="3">
    <TNRef RefId="0" />
    <MS>
      <S N="Name">PAL - SQL Server 2012</S>
      <S N="Source">Clint Huffman</S>
    </MS>
  </Obj>
  <Obj RefId="4">
    <TNRef RefId="0" />
    <MS>
      <S N="Name">PAL - SQL Server 2014 and Up</S>
      <S N="Source">Clint Huffman</S>
    </MS>
  </Obj>
</Objs>
tools\dbatools\bin\perfmontemplates\collectorsets\Long Running Queries.xml
<?xml version="1.0" encoding="UTF-16"?>
<DataCollectorSet>
	<Status>0</Status>
	<Duration>0</Duration>
	<Description>From Brad McGehee's article "Correlating SQL Server Profiler with Performance Monitor" on red-gate.com</Description>
	<DescriptionUnresolved>From Brad McGehee's article "Correlating SQL Server Profiler with Performance Monitor" on red-gate.com
	</DescriptionUnresolved>
	<DisplayName>Long Running Queries</DisplayName>
	<DisplayNameUnresolved>Long Running Queries</DisplayNameUnresolved>
	<SchedulesEnabled>-1</SchedulesEnabled>
	<LatestOutputLocation></LatestOutputLocation>
	<Name>Long Running Queries</Name>
	<OutputLocation></OutputLocation>
	<RootPath></RootPath>
	<Segment>0</Segment>
	<SegmentMaxDuration>0</SegmentMaxDuration>
	<SegmentMaxSize>0</SegmentMaxSize>
	<SerialNumber>3</SerialNumber>
	<Server>
	</Server>
	<Subdirectory>
	</Subdirectory>
	<SubdirectoryFormat>3</SubdirectoryFormat>
	<SubdirectoryFormatPattern>yyyyMMdd\-NNNNNN</SubdirectoryFormatPattern>
	<Task>
	</Task>
	<TaskRunAsSelf>0</TaskRunAsSelf>
	<TaskArguments>
	</TaskArguments>
	<TaskUserTextArguments>
	</TaskUserTextArguments>
	<UserAccount>SYSTEM</UserAccount>
	<Security></Security>
	<StopOnCompletion>0</StopOnCompletion>
	<PerformanceCounterDataCollector>
		<DataCollectorType>0</DataCollectorType>
		<Name>Long Running Queries Collector</Name>
		<FileName>Long Running Queries Collector</FileName>
		<FileNameFormat>1</FileNameFormat>
		<FileNameFormatPattern>
		</FileNameFormatPattern>
		<LogAppend>0</LogAppend>
		<LogCircular>0</LogCircular>
		<LogOverwrite>0</LogOverwrite>
		<LatestOutputLocation></LatestOutputLocation>
		<DataSourceName>
		</DataSourceName>
		<SampleInterval>15</SampleInterval>
		<SegmentMaxRecords>0</SegmentMaxRecords>
		<LogFileFormat>3</LogFileFormat>
		<Counter>\Memory\Available MBytes</Counter>
		<Counter>\Memory\Pages/sec</Counter>
		<Counter>\Processor(_Total)\% Processor Time</Counter>
		<Counter>\System\Processor Queue Length</Counter>
		<Counter>\LogicalDisk(*)\% Disk Read Time</Counter>
		<Counter>\LogicalDisk(*)\Avg. Disk Queue Length</Counter>
		<CounterDisplayName>\Memory\Available MBytes</CounterDisplayName>
		<CounterDisplayName>\Memory\Pages/sec</CounterDisplayName>
		<CounterDisplayName>\Processor(_Total)\% Processor Time</CounterDisplayName>
		<CounterDisplayName>\System\Processor Queue Length</CounterDisplayName>
		<CounterDisplayName>\LogicalDisk(*)\% Disk Read Time</CounterDisplayName>
		<CounterDisplayName>\LogicalDisk(*)\Avg. Disk Queue Length</CounterDisplayName>
	</PerformanceCounterDataCollector>
	<DataManager>
		<Enabled>0</Enabled>
		<CheckBeforeRunning>0</CheckBeforeRunning>
		<MinFreeDisk>0</MinFreeDisk>
		<MaxSize>0</MaxSize>
		<MaxFolderCount>0</MaxFolderCount>
		<ResourcePolicy>0</ResourcePolicy>
		<ReportFileName>report.html</ReportFileName>
		<RuleTargetFileName>report.xml</RuleTargetFileName>
		<EventsFileName>
		</EventsFileName>
	</DataManager>
</DataCollectorSet>
tools\dbatools\bin\perfmontemplates\collectorsets\PAL - SQL Server 2005.xml
<DataCollectorSet>
<Name>PAL - SQL Server 2005</Name>
<DisplayName>PAL - SQL Server 2005</DisplayName>
<DisplayNameUnresolved>PAL - SQL Server 2005</DisplayNameUnresolved>
<Description>Data Collector Sets from PAL, a performance monitor data aggregation tool by Microsoft. The toolset was created by Microsoft PFE Clint Huffman who worked with fellow experts to create a list specialized lists of counters. You can find the project at github.com/clinthuffman/PAL</Description>
<DescriptionUnresolved>Data Collector Sets from PAL, a performance monitor data aggregation tool by Microsoft. The toolset was created by Microsoft PFE Clint Huffman who worked with fellow experts to create a list specialized lists of counters. You can find the project at github.com/clinthuffman/PAL</DescriptionUnresolved>
<SubdirectoryFormat>3</SubdirectoryFormat>
<SubdirectoryFormatPattern>yyyyMMdd\-NNNNNN</SubdirectoryFormatPattern>
<PerformanceCounterDataCollector>
    <Name>PAL - SQL Server 2005 Collector</Name>
    <SampleInterval>15</SampleInterval>
    <Counter>\Cache\Dirty Pages</Counter>
    <Counter>\Cache\Lazy Write Flushes/sec</Counter>
    <Counter>\LogicalDisk(*)\% Free Space</Counter>
    <Counter>\LogicalDisk(*)\% Idle Time</Counter>
    <Counter>\LogicalDisk(*)\Avg. Disk Bytes/Read</Counter>
    <Counter>\LogicalDisk(*)\Avg. Disk Bytes/Transfer</Counter>
    <Counter>\LogicalDisk(*)\Avg. Disk Bytes/Write</Counter>
    <Counter>\LogicalDisk(*)\Avg. Disk Queue Length</Counter>
    <Counter>\LogicalDisk(*)\Avg. Disk sec/Read</Counter>
    <Counter>\LogicalDisk(*)\Avg. Disk sec/Transfer</Counter>
    <Counter>\LogicalDisk(*)\Avg. Disk sec/Write</Counter>
    <Counter>\LogicalDisk(*)\Current Disk Queue Length</Counter>
    <Counter>\LogicalDisk(*)\Disk Bytes/sec</Counter>
    <Counter>\LogicalDisk(*)\Disk Reads/sec</Counter>
    <Counter>\LogicalDisk(*)\Disk Transfers/sec</Counter>
    <Counter>\LogicalDisk(*)\Disk Writes/sec</Counter>
    <Counter>\LogicalDisk(*)\Free Megabytes</Counter>
    <Counter>\Memory\% Committed Bytes In Use</Counter>
    <Counter>\Memory\Available MBytes</Counter>
    <Counter>\Memory\Commit Limit</Counter>
    <Counter>\Memory\Committed Bytes</Counter>
    <Counter>\Memory\Free &amp; Zero Page List Bytes</Counter>
    <Counter>\Memory\Free System Page Table Entries</Counter>
    <Counter>\Memory\Long-Term Average Standby Cache Lifetime (s)</Counter>
    <Counter>\Memory\Pages Input/sec</Counter>
    <Counter>\Memory\Pages Output/sec</Counter>
    <Counter>\Memory\Pages/sec</Counter>
    <Counter>\Memory\Pool Nonpaged Bytes</Counter>
    <Counter>\Memory\Pool Paged Bytes</Counter>
    <Counter>\Memory\Pool Paged Resident Bytes</Counter>
    <Counter>\Memory\System Cache Resident Bytes</Counter>
    <Counter>\Memory\Transition Pages RePurposed/sec</Counter>
    <Counter>\Network Inspection System\Average inspection latency (sec/bytes)</Counter>
    <Counter>\Network Interface(*)\Bytes Received/sec</Counter>
    <Counter>\Network Interface(*)\Bytes Sent/sec</Counter>
    <Counter>\Network Interface(*)\Bytes Total/sec</Counter>
    <Counter>\Network Interface(*)\Current Bandwidth</Counter>
    <Counter>\Network Interface(*)\Output Queue Length</Counter>
    <Counter>\Network Interface(*)\Packets Outbound Errors</Counter>
    <Counter>\Network Interface(*)\Packets Received/sec</Counter>
    <Counter>\Network Interface(*)\Packets Sent/sec</Counter>
    <Counter>\Network Interface(*)\Packets/sec</Counter>
    <Counter>\Paging File(*)\% Usage</Counter>
    <Counter>\PhysicalDisk(*)\Avg. Disk Queue Length</Counter>
    <Counter>\PhysicalDisk(*)\Avg. Disk sec/Read</Counter>
    <Counter>\PhysicalDisk(*)\Avg. Disk sec/Write</Counter>
    <Counter>\PhysicalDisk(*)\Current Disk Queue Length</Counter>
    <Counter>\PhysicalDisk(*)\Disk Bytes/sec</Counter>
    <Counter>\Process(*)\% Processor Time</Counter>
    <Counter>\Process(*)\Handle Count</Counter>
    <Counter>\Process(*)\ID Process</Counter>
    <Counter>\Process(*)\IO Data Operations/sec</Counter>
    <Counter>\Process(*)\IO Other Operations/sec</Counter>
    <Counter>\Process(*)\IO Read Operations/sec</Counter>
    <Counter>\Process(*)\IO Write Operations/sec</Counter>
    <Counter>\Process(*)\Private Bytes</Counter>
    <Counter>\Process(*)\Thread Count</Counter>
    <Counter>\Process(*)\Virtual Bytes</Counter>
    <Counter>\Process(*)\Working Set</Counter>
    <Counter>\Process(sqlservr)\% Privileged Time</Counter>
    <Counter>\Processor Information(*)\% DPC Time</Counter>
    <Counter>\Processor Information(*)\% Interrupt Time</Counter>
    <Counter>\Processor Information(*)\% of Maximum Frequency</Counter>
    <Counter>\Processor Information(*)\% Privileged Time</Counter>
    <Counter>\Processor Information(*)\% Processor Time</Counter>
    <Counter>\Processor Information(*)\% User Time</Counter>
    <Counter>\Processor Information(*)\DPC Rate</Counter>
    <Counter>\Processor Information(*)\Parking Status</Counter>
    <Counter>\Processor(*)\% DPC Time</Counter>
    <Counter>\Processor(*)\% Interrupt Time</Counter>
    <Counter>\Processor(*)\% Privileged Time</Counter>
    <Counter>\Processor(*)\% Processor Time</Counter>
    <Counter>\Processor(*)\% User Time</Counter>
    <Counter>\Processor(*)\DPC Rate</Counter>
    <Counter>\Server\Pool Nonpaged Failures</Counter>
    <Counter>\Server\Pool Paged Failures</Counter>
    <Counter>\SQLServer:Access Methods\Forwarded Records/sec</Counter>
    <Counter>\SQLServer:Access Methods\FreeSpace Scans/sec</Counter>
    <Counter>\SQLServer:Access Methods\Full Scans/sec</Counter>
    <Counter>\SQLServer:Access Methods\Index Searches/sec</Counter>
    <Counter>\SQLServer:Access Methods\Page Splits/sec</Counter>
    <Counter>\SQLServer:Access Methods\Scan Point Revalidations/sec</Counter>
    <Counter>\SQLServer:Access Methods\Workfiles Created/sec</Counter>
    <Counter>\SQLServer:Access Methods\Worktables Created/sec</Counter>
    <Counter>\SQLServer:Buffer Manager\Buffer cache hit ratio</Counter>
    <Counter>\SQLServer:Buffer Manager\Checkpoint pages/sec</Counter>
    <Counter>\SQLServer:Buffer Manager\Free pages</Counter>
    <Counter>\SQLServer:Buffer Manager\Lazy writes/sec</Counter>
    <Counter>\SQLServer:Buffer Manager\Page life expectancy</Counter>
    <Counter>\SQLServer:Buffer Manager\Page lookups/sec</Counter>
    <Counter>\SQLServer:Buffer Manager\Page reads/sec</Counter>
    <Counter>\SQLServer:Buffer Manager\Page writes/sec</Counter>
    <Counter>\SQLServer:General Statistics\Logins/sec</Counter>
    <Counter>\SQLServer:General Statistics\Logouts/sec</Counter>
    <Counter>\SQLServer:General Statistics\User Connections</Counter>
    <Counter>\SQLServer:Latches\Latch Waits/sec</Counter>
    <Counter>\SQLServer:Latches\Total Latch Wait Time (ms)</Counter>
    <Counter>\SQLServer:Locks(*)\Lock Requests/sec</Counter>
    <Counter>\SQLServer:Locks(*)\Lock Timeouts/sec</Counter>
    <Counter>\SQLServer:Locks(*)\Lock Wait Time (ms)</Counter>
    <Counter>\SQLServer:Locks(*)\Lock Waits/sec</Counter>
    <Counter>\SQLServer:Locks(*)\Number of Deadlocks/sec</Counter>
    <Counter>\SQLServer:Memory Manager\Memory Grants Pending</Counter>
    <Counter>\SQLServer:Memory Manager\Target Server Memory(KB)</Counter>
    <Counter>\SQLServer:Memory Manager\Total Server Memory (KB)</Counter>
    <Counter>\SQLServer:SQL Statistics\Batch Requests/sec</Counter>
    <Counter>\SQLServer:SQL Statistics\SQL Compilations/sec</Counter>
    <Counter>\SQLServer:SQL Statistics\SQL Re-Compilations/sec</Counter>
    <Counter>\System\Context Switches/sec</Counter>
    <Counter>\System\Processor Queue Length</Counter>
    <Counter>\System\System Calls/sec</Counter>
    <Counter>\TCPv4\Connection Failures</Counter>
</PerformanceCounterDataCollector>
</DataCollectorSet>
tools\dbatools\bin\perfmontemplates\collectorsets\PAL - SQL Server 2008 and R2.xml
<DataCollectorSet>
<Name>PAL - SQL Server 2008 and R2</Name>
<DisplayName>PAL - SQL Server 2008 and R2</DisplayName>
<DisplayNameUnresolved>PAL - SQL Server 2008 and R2</DisplayNameUnresolved>
<Description>Data Collector Sets from PAL, a performance monitor data aggregation tool by Microsoft. The toolset was created by Microsoft PFE Clint Huffman who worked with fellow experts to create a list specialized lists of counters. You can find the project at github.com/clinthuffman/PAL</Description>
<DescriptionUnresolved>Data Collector Sets from PAL, a performance monitor data aggregation tool by Microsoft. The toolset was created by Microsoft PFE Clint Huffman who worked with fellow experts to create a list specialized lists of counters. You can find the project at github.com/clinthuffman/PAL</DescriptionUnresolved>
<SubdirectoryFormat>3</SubdirectoryFormat>
<SubdirectoryFormatPattern>yyyyMMdd\-NNNNNN</SubdirectoryFormatPattern>
<PerformanceCounterDataCollector>
    <Name>PAL - SQL Server 2008 and R2 Collector</Name>
    <SampleInterval>15</SampleInterval>
    <Counter>\Cache\Dirty Pages</Counter>
    <Counter>\Cache\Lazy Write Flushes/sec</Counter>
    <Counter>\LogicalDisk(*)\% Free Space</Counter>
    <Counter>\LogicalDisk(*)\% Idle Time</Counter>
    <Counter>\LogicalDisk(*)\Avg. Disk Bytes/Read</Counter>
    <Counter>\LogicalDisk(*)\Avg. Disk Bytes/Transfer</Counter>
    <Counter>\LogicalDisk(*)\Avg. Disk Bytes/Write</Counter>
    <Counter>\LogicalDisk(*)\Avg. Disk Queue Length</Counter>
    <Counter>\LogicalDisk(*)\Avg. Disk sec/Read</Counter>
    <Counter>\LogicalDisk(*)\Avg. Disk sec/Transfer</Counter>
    <Counter>\LogicalDisk(*)\Avg. Disk sec/Write</Counter>
    <Counter>\LogicalDisk(*)\Current Disk Queue Length</Counter>
    <Counter>\LogicalDisk(*)\Disk Bytes/sec</Counter>
    <Counter>\LogicalDisk(*)\Disk Reads/sec</Counter>
    <Counter>\LogicalDisk(*)\Disk Transfers/sec</Counter>
    <Counter>\LogicalDisk(*)\Disk Writes/sec</Counter>
    <Counter>\LogicalDisk(*)\Free Megabytes</Counter>
    <Counter>\Memory\% Committed Bytes In Use</Counter>
    <Counter>\Memory\Available MBytes</Counter>
    <Counter>\Memory\Commit Limit</Counter>
    <Counter>\Memory\Committed Bytes</Counter>
    <Counter>\Memory\Free &amp; Zero Page List Bytes</Counter>
    <Counter>\Memory\Free System Page Table Entries</Counter>
    <Counter>\Memory\Long-Term Average Standby Cache Lifetime (s)</Counter>
    <Counter>\Memory\Pages Input/sec</Counter>
    <Counter>\Memory\Pages Output/sec</Counter>
    <Counter>\Memory\Pages/sec</Counter>
    <Counter>\Memory\Pool Nonpaged Bytes</Counter>
    <Counter>\Memory\Pool Paged Bytes</Counter>
    <Counter>\Memory\Pool Paged Resident Bytes</Counter>
    <Counter>\Memory\System Cache Resident Bytes</Counter>
    <Counter>\Memory\Transition Pages RePurposed/sec</Counter>
    <Counter>\MSRS 2008 R2 Web Service\Cache Misses/Sec</Counter>
    <Counter>\MSRS 2008 R2 Web Service\Report Requests</Counter>
    <Counter>\MSRS 2008 R2 Web Service\Total Memory Cache Misses</Counter>
    <Counter>\MSRS 2008 R2 Web Service\Total Requests</Counter>
    <Counter>\Network Inspection System\Average inspection latency (sec/bytes)</Counter>
    <Counter>\Network Interface(*)\Bytes Received/sec</Counter>
    <Counter>\Network Interface(*)\Bytes Sent/sec</Counter>
    <Counter>\Network Interface(*)\Bytes Total/sec</Counter>
    <Counter>\Network Interface(*)\Current Bandwidth</Counter>
    <Counter>\Network Interface(*)\Output Queue Length</Counter>
    <Counter>\Network Interface(*)\Packets Outbound Errors</Counter>
    <Counter>\Network Interface(*)\Packets Received/sec</Counter>
    <Counter>\Network Interface(*)\Packets Sent/sec</Counter>
    <Counter>\Network Interface(*)\Packets/sec</Counter>
    <Counter>\Paging File(*)\% Usage</Counter>
    <Counter>\PhysicalDisk(*)\Avg. Disk Queue Length</Counter>
    <Counter>\PhysicalDisk(*)\Avg. Disk sec/Read</Counter>
    <Counter>\PhysicalDisk(*)\Avg. Disk sec/Write</Counter>
    <Counter>\PhysicalDisk(*)\Current Disk Queue Length</Counter>
    <Counter>\PhysicalDisk(*)\Disk Bytes/sec</Counter>
    <Counter>\Process(*)\Handle Count</Counter>
    <Counter>\Process(*)\ID Process</Counter>
    <Counter>\Process(*)\IO Data Operations/sec</Counter>
    <Counter>\Process(*)\IO Other Operations/sec</Counter>
    <Counter>\Process(*)\IO Read Operations/sec</Counter>
    <Counter>\Process(*)\IO Write Operations/sec</Counter>
    <Counter>\Process(*)\Private Bytes</Counter>
    <Counter>\Process(*)\Thread Count</Counter>
    <Counter>\Process(*)\Virtual Bytes</Counter>
    <Counter>\Process(*)\Working Set</Counter>
    <Counter>\Process(sqlservr)\% Privileged Time</Counter>
    <Counter>\Process(sqlservr)\% Processor Time</Counter>
    <Counter>\Processor Information(*)\% DPC Time</Counter>
    <Counter>\Processor Information(*)\% Interrupt Time</Counter>
    <Counter>\Processor Information(*)\% of Maximum Frequency</Counter>
    <Counter>\Processor Information(*)\% Privileged Time</Counter>
    <Counter>\Processor Information(*)\% Processor Time</Counter>
    <Counter>\Processor Information(*)\% User Time</Counter>
    <Counter>\Processor Information(*)\DPC Rate</Counter>
    <Counter>\Processor Information(*)\Parking Status</Counter>
    <Counter>\Processor(*)\% DPC Time</Counter>
    <Counter>\Processor(*)\% Interrupt Time</Counter>
    <Counter>\Processor(*)\% Privileged Time</Counter>
    <Counter>\Processor(*)\% Processor Time</Counter>
    <Counter>\Processor(*)\% User Time</Counter>
    <Counter>\Processor(*)\DPC Rate</Counter>
    <Counter>\ReportServer:Service\Errors Total</Counter>
    <Counter>\ReportServer:Service\Errors/sec</Counter>
    <Counter>\ReportServer:Service\Memory Pressure State</Counter>
    <Counter>\ReportServer:Service\Memory Shrink Amount</Counter>
    <Counter>\ReportServer:Service\Memory Shrink Notifications/sec</Counter>
    <Counter>\Server\Pool Nonpaged Failures</Counter>
    <Counter>\Server\Pool Paged Failures</Counter>
    <Counter>\SQLAgent:Jobs\Active jobs</Counter>
    <Counter>\SQLAgent:Jobs\Failed jobs</Counter>
    <Counter>\SQLAgent:Jobs\Job success rate</Counter>
    <Counter>\SQLAgent:Jobs\Successful jobs</Counter>
    <Counter>\SQLAgent:JobSteps\Active steps</Counter>
    <Counter>\SQLAgent:JobSteps\Total step retries</Counter>
    <Counter>\SQLServer:Access Methods\Forwarded Records/sec</Counter>
    <Counter>\SQLServer:Access Methods\FreeSpace Scans/sec</Counter>
    <Counter>\SQLServer:Access Methods\Full Scans/sec</Counter>
    <Counter>\SQLServer:Access Methods\Index Searches/sec</Counter>
    <Counter>\SQLServer:Access Methods\Page Splits/sec</Counter>
    <Counter>\SQLServer:Access Methods\Scan Point Revalidations/sec</Counter>
    <Counter>\SQLServer:Access Methods\Table Lock Escalations/sec</Counter>
    <Counter>\SQLServer:Access Methods\Workfiles Created/sec</Counter>
    <Counter>\SQLServer:Access Methods\Worktables Created/sec</Counter>
    <Counter>\SQLServer:Access Methods\Worktables From Cache Ratio</Counter>
    <Counter>\SQLServer:Buffer Manager\Buffer cache hit ratio</Counter>
    <Counter>\SQLServer:Buffer Manager\Checkpoint pages/sec</Counter>
    <Counter>\SQLServer:Buffer Manager\Free list stalls/sec</Counter>
    <Counter>\SQLServer:Buffer Manager\Free pages</Counter>
    <Counter>\SQLServer:Buffer Manager\Lazy writes/sec</Counter>
    <Counter>\SQLServer:Buffer Manager\Page life expectancy</Counter>
    <Counter>\SQLServer:Buffer Manager\Page lookups/sec</Counter>
    <Counter>\SQLServer:Buffer Manager\Page reads/sec</Counter>
    <Counter>\SQLServer:Buffer Manager\Page writes/sec</Counter>
    <Counter>\SQLServer:Buffer Manager\Readahead pages/sec</Counter>
    <Counter>\SQLServer:Buffer Manager\Stolen pages</Counter>
    <Counter>\SQLServer:Buffer Node(*)\Foreign pages</Counter>
    <Counter>\SQLServer:Buffer Node(*)\Page life expectancy</Counter>
    <Counter>\SQLServer:Databases(*)\Data File(s) Size (KB)</Counter>
    <Counter>\SQLServer:Databases(*)\Log Bytes Flushed/sec</Counter>
    <Counter>\SQLServer:Databases(*)\Log File(s) Size (KB)</Counter>
    <Counter>\SQLServer:Databases(*)\Log File(s) Used Size (KB)</Counter>
    <Counter>\SQLServer:Databases(*)\Log Flush Wait Time</Counter>
    <Counter>\SQLServer:Databases(*)\Log Flush Waits/sec</Counter>
    <Counter>\SQLServer:Databases(*)\Log Flushes/sec</Counter>
    <Counter>\SQLServer:Databases(*)\Log Growths</Counter>
    <Counter>\SQLServer:Databases(*)\Log Shrinks</Counter>
    <Counter>\SQLServer:Databases(*)\Log Truncations</Counter>
    <Counter>\SQLServer:Databases(*)\Percent Log Used</Counter>
    <Counter>\SQLServer:Deprecated Features(*)\Usage</Counter>
    <Counter>\SQLServer:General Statistics\Logins/sec</Counter>
    <Counter>\SQLServer:General Statistics\Logouts/sec</Counter>
    <Counter>\SQLServer:General Statistics\User Connections</Counter>
    <Counter>\SQLServer:Latches\Latch Waits/sec</Counter>
    <Counter>\SQLServer:Latches\Total Latch Wait Time (ms)</Counter>
    <Counter>\SQLServer:Locks(*)\Average Wait Time (ms)</Counter>
    <Counter>\SQLServer:Locks(*)\Lock Requests/sec</Counter>
    <Counter>\SQLServer:Locks(*)\Lock Timeouts/sec</Counter>
    <Counter>\SQLServer:Locks(*)\Lock Wait Time (ms)</Counter>
    <Counter>\SQLServer:Locks(*)\Lock Waits/sec</Counter>
    <Counter>\SQLServer:Locks(*)\Number of Deadlocks/sec</Counter>
    <Counter>\SQLServer:Memory Manager\Granted Workspace Memory (KB)</Counter>
    <Counter>\SQLServer:Memory Manager\Maximum Workspace Memory (KB)</Counter>
    <Counter>\SQLServer:Memory Manager\Memory Grants Outstanding</Counter>
    <Counter>\SQLServer:Memory Manager\Memory Grants Pending</Counter>
    <Counter>\SQLServer:Memory Manager\Target Server Memory(KB)</Counter>
    <Counter>\SQLServer:Memory Manager\Total Server Memory (KB)</Counter>
    <Counter>\SQLServer:Plan Cache(*)\Cache Hit Ratio</Counter>
    <Counter>\SQLServer:Resource Pool Stats(*)\CPU usage %</Counter>
    <Counter>\SQLServer:Resource Pool Stats(*)\Max memory (KB)</Counter>
    <Counter>\SQLServer:Resource Pool Stats(*)\Target memory (KB)</Counter>
    <Counter>\SQLServer:Resource Pool Stats(*)\Used memory (KB)</Counter>
    <Counter>\SQLServer:SQL Errors(*)\Errors/sec</Counter>
    <Counter>\SQLServer:SQL Statistics\Auto-Param Attempts/sec</Counter>
    <Counter>\SQLServer:SQL Statistics\Batch Requests/sec</Counter>
    <Counter>\SQLServer:SQL Statistics\Failed Auto-Params/sec</Counter>
    <Counter>\SQLServer:SQL Statistics\Safe Auto-Params/sec</Counter>
    <Counter>\SQLServer:SQL Statistics\SQL Attention rate</Counter>
    <Counter>\SQLServer:SQL Statistics\SQL Compilations/sec</Counter>
    <Counter>\SQLServer:SQL Statistics\SQL Re-Compilations/sec</Counter>
    <Counter>\SQLServer:SQL Statistics\Unsafe Auto-Params/sec</Counter>
    <Counter>\SQLServer:SSIS Pipeline 10.0\Buffer memory</Counter>
    <Counter>\SQLServer:SSIS Pipeline 10.0\Buffers in use</Counter>
    <Counter>\SQLServer:SSIS Pipeline 10.0\Buffers spooled</Counter>
    <Counter>\SQLServer:SSIS Pipeline 10.0\Flat buffers in use</Counter>
    <Counter>\SQLServer:SSIS Pipeline 10.0\Private buffers in use</Counter>
    <Counter>\SQLServer:Transactions\Free Space in tempdb (KB)</Counter>
    <Counter>\SQLServer:Transactions\Longest Transaction Running Time</Counter>
    <Counter>\SQLServer:Transactions\NonSnapshot Version Transactions</Counter>
    <Counter>\SQLServer:Transactions\Snapshot Transactions</Counter>
    <Counter>\SQLServer:Transactions\Version Cleanup rate (KB/s)</Counter>
    <Counter>\SQLServer:Transactions\Version Generation rate (KB/s)</Counter>
    <Counter>\SQLServer:Workload Group Stats(*)\CPU usage %</Counter>
    <Counter>\SQLServer:Workload Group Stats(*)\Queued requests</Counter>
    <Counter>\SQLServer:Workload Group Stats(*)\Requests completed/sec</Counter>
    <Counter>\System\Context Switches/sec</Counter>
    <Counter>\System\Processor Queue Length</Counter>
    <Counter>\System\System Calls/sec</Counter>
    <Counter>\TCPv4\Connection Failures</Counter>
</PerformanceCounterDataCollector>
</DataCollectorSet>
tools\dbatools\bin\perfmontemplates\collectorsets\PAL - SQL Server 2012.xml
<DataCollectorSet>
<Name>PAL - SQL Server 2012</Name>
<DisplayName>PAL - SQL Server 2012</DisplayName>
<DisplayNameUnresolved>PAL - SQL Server 2012</DisplayNameUnresolved>
<Description>Data Collector Sets from PAL, a performance monitor data aggregation tool by Microsoft. The toolset was created by Microsoft PFE Clint Huffman who worked with fellow experts to create a list specialized lists of counters. You can find the project at github.com/clinthuffman/PAL</Description>
<DescriptionUnresolved>Data Collector Sets from PAL, a performance monitor data aggregation tool by Microsoft. The toolset was created by Microsoft PFE Clint Huffman who worked with fellow experts to create a list specialized lists of counters. You can find the project at github.com/clinthuffman/PAL</DescriptionUnresolved>
<SubdirectoryFormat>3</SubdirectoryFormat>
<SubdirectoryFormatPattern>yyyyMMdd\-NNNNNN</SubdirectoryFormatPattern>
<PerformanceCounterDataCollector>
    <Name>PAL - SQL Server 2012 Collector</Name>
    <SampleInterval>15</SampleInterval>
    <Counter>\Cache\Dirty Pages</Counter>
    <Counter>\Cache\Lazy Write Flushes/sec</Counter>
    <Counter>\LogicalDisk(*)\% Free Space</Counter>
    <Counter>\LogicalDisk(*)\% Idle Time</Counter>
    <Counter>\LogicalDisk(*)\Avg. Disk Bytes/Read</Counter>
    <Counter>\LogicalDisk(*)\Avg. Disk Bytes/Transfer</Counter>
    <Counter>\LogicalDisk(*)\Avg. Disk Bytes/Write</Counter>
    <Counter>\LogicalDisk(*)\Avg. Disk Queue Length</Counter>
    <Counter>\LogicalDisk(*)\Avg. Disk sec/Read</Counter>
    <Counter>\LogicalDisk(*)\Avg. Disk sec/Transfer</Counter>
    <Counter>\LogicalDisk(*)\Avg. Disk sec/Write</Counter>
    <Counter>\LogicalDisk(*)\Current Disk Queue Length</Counter>
    <Counter>\LogicalDisk(*)\Disk Bytes/sec</Counter>
    <Counter>\LogicalDisk(*)\Disk Reads/sec</Counter>
    <Counter>\LogicalDisk(*)\Disk Transfers/sec</Counter>
    <Counter>\LogicalDisk(*)\Disk Writes/sec</Counter>
    <Counter>\LogicalDisk(*)\Free Megabytes</Counter>
    <Counter>\Memory\% Committed Bytes In Use</Counter>
    <Counter>\Memory\Available MBytes</Counter>
    <Counter>\Memory\Commit Limit</Counter>
    <Counter>\Memory\Committed Bytes</Counter>
    <Counter>\Memory\Free &amp; Zero Page List Bytes</Counter>
    <Counter>\Memory\Free System Page Table Entries</Counter>
    <Counter>\Memory\Long-Term Average Standby Cache Lifetime (s)</Counter>
    <Counter>\Memory\Pages Input/sec</Counter>
    <Counter>\Memory\Pages Output/sec</Counter>
    <Counter>\Memory\Pages/sec</Counter>
    <Counter>\Memory\Pool Nonpaged Bytes</Counter>
    <Counter>\Memory\Pool Paged Bytes</Counter>
    <Counter>\Memory\Pool Paged Resident Bytes</Counter>
    <Counter>\Memory\System Cache Resident Bytes</Counter>
    <Counter>\Memory\Transition Pages RePurposed/sec</Counter>
    <Counter>\MSRS 2011 Web Service\Cache Misses/Sec</Counter>
    <Counter>\MSRS 2011 Web Service\Report Requests</Counter>
    <Counter>\MSRS 2011 Web Service\Total Memory Cache Misses</Counter>
    <Counter>\MSRS 2011 Web Service\Total Requests</Counter>
    <Counter>\Network Inspection System\Average inspection latency (sec/bytes)</Counter>
    <Counter>\Network Interface(*)\Bytes Received/sec</Counter>
    <Counter>\Network Interface(*)\Bytes Sent/sec</Counter>
    <Counter>\Network Interface(*)\Bytes Total/sec</Counter>
    <Counter>\Network Interface(*)\Current Bandwidth</Counter>
    <Counter>\Network Interface(*)\Output Queue Length</Counter>
    <Counter>\Network Interface(*)\Packets Outbound Errors</Counter>
    <Counter>\Network Interface(*)\Packets Received/sec</Counter>
    <Counter>\Network Interface(*)\Packets Sent/sec</Counter>
    <Counter>\Network Interface(*)\Packets/sec</Counter>
    <Counter>\Paging File(*)\% Usage</Counter>
    <Counter>\PhysicalDisk(*)\Avg. Disk Queue Length</Counter>
    <Counter>\PhysicalDisk(*)\Avg. Disk sec/Read</Counter>
    <Counter>\PhysicalDisk(*)\Avg. Disk sec/Write</Counter>
    <Counter>\PhysicalDisk(*)\Current Disk Queue Length</Counter>
    <Counter>\PhysicalDisk(*)\Disk Bytes/sec</Counter>
    <Counter>\Process(*)\Handle Count</Counter>
    <Counter>\Process(*)\ID Process</Counter>
    <Counter>\Process(*)\IO Data Operations/sec</Counter>
    <Counter>\Process(*)\IO Other Operations/sec</Counter>
    <Counter>\Process(*)\IO Read Operations/sec</Counter>
    <Counter>\Process(*)\IO Write Operations/sec</Counter>
    <Counter>\Process(*)\Private Bytes</Counter>
    <Counter>\Process(*)\Thread Count</Counter>
    <Counter>\Process(*)\Virtual Bytes</Counter>
    <Counter>\Process(*)\Working Set</Counter>
    <Counter>\Process(sqlservr)\% Privileged Time</Counter>
    <Counter>\Process(sqlservr)\% Processor Time</Counter>
    <Counter>\Processor Information(*)\% DPC Time</Counter>
    <Counter>\Processor Information(*)\% Interrupt Time</Counter>
    <Counter>\Processor Information(*)\% of Maximum Frequency</Counter>
    <Counter>\Processor Information(*)\% Privileged Time</Counter>
    <Counter>\Processor Information(*)\% Processor Time</Counter>
    <Counter>\Processor Information(*)\% User Time</Counter>
    <Counter>\Processor Information(*)\DPC Rate</Counter>
    <Counter>\Processor Information(*)\Parking Status</Counter>
    <Counter>\Processor(*)\% DPC Time</Counter>
    <Counter>\Processor(*)\% Interrupt Time</Counter>
    <Counter>\Processor(*)\% Privileged Time</Counter>
    <Counter>\Processor(*)\% Processor Time</Counter>
    <Counter>\Processor(*)\% User Time</Counter>
    <Counter>\Processor(*)\DPC Rate</Counter>
    <Counter>\ReportServer:Service\Errors Total</Counter>
    <Counter>\ReportServer:Service\Errors/sec</Counter>
    <Counter>\ReportServer:Service\Memory Pressure State</Counter>
    <Counter>\ReportServer:Service\Memory Shrink Amount</Counter>
    <Counter>\ReportServer:Service\Memory Shrink Notifications/sec</Counter>
    <Counter>\Server\Pool Nonpaged Failures</Counter>
    <Counter>\Server\Pool Paged Failures</Counter>
    <Counter>\SQLAgent:Jobs\Active jobs</Counter>
    <Counter>\SQLAgent:Jobs\Failed jobs</Counter>
    <Counter>\SQLAgent:Jobs\Job success rate</Counter>
    <Counter>\SQLAgent:Jobs\Successful jobs</Counter>
    <Counter>\SQLAgent:JobSteps\Active steps</Counter>
    <Counter>\SQLAgent:JobSteps\Total step retries</Counter>
    <Counter>\SQLServer:Access Methods\Forwarded Records/sec</Counter>
    <Counter>\SQLServer:Access Methods\FreeSpace Scans/sec</Counter>
    <Counter>\SQLServer:Access Methods\Full Scans/sec</Counter>
    <Counter>\SQLServer:Access Methods\Index Searches/sec</Counter>
    <Counter>\SQLServer:Access Methods\Page Splits/sec</Counter>
    <Counter>\SQLServer:Access Methods\Scan Point Revalidations/sec</Counter>
    <Counter>\SQLServer:Access Methods\Table Lock Escalations/sec</Counter>
    <Counter>\SQLServer:Access Methods\Workfiles Created/sec</Counter>
    <Counter>\SQLServer:Access Methods\Worktables Created/sec</Counter>
    <Counter>\SQLServer:Access Methods\Worktables From Cache Ratio</Counter>
    <Counter>\SQLServer:Availability Replica(*)\Bytes Received from Replica/sec</Counter>
    <Counter>\SQLServer:Availability Replica(*)\Bytes Sent to Replica/sec</Counter>
    <Counter>\SQLServer:Availability Replica(*)\Bytes Sent to Transport/sec</Counter>
    <Counter>\SQLServer:Availability Replica(_Total)\Receives from Replica/sec</Counter>
    <Counter>\SQLServer:Availability Replica(_Total)\Resent Messages/sec</Counter>
    <Counter>\SQLServer:Availability Replica(_Total)\Sends to Replica/sec</Counter>
    <Counter>\SQLServer:Buffer Manager\Background writer pages/sec</Counter>
    <Counter>\SQLServer:Buffer Manager\Buffer cache hit ratio</Counter>
    <Counter>\SQLServer:Buffer Manager\Checkpoint pages/sec</Counter>
    <Counter>\SQLServer:Buffer Manager\Free list stalls/sec</Counter>
    <Counter>\SQLServer:Buffer Manager\Free pages</Counter>
    <Counter>\SQLServer:Buffer Manager\Lazy writes/sec</Counter>
    <Counter>\SQLServer:Buffer Manager\Page life expectancy</Counter>
    <Counter>\SQLServer:Buffer Manager\Page lookups/sec</Counter>
    <Counter>\SQLServer:Buffer Manager\Page reads/sec</Counter>
    <Counter>\SQLServer:Buffer Manager\Page writes/sec</Counter>
    <Counter>\SQLServer:Buffer Manager\Readahead pages/sec</Counter>
    <Counter>\SQLServer:Buffer Manager\Target pages</Counter>
    <Counter>\SQLServer:Buffer Node(*)\Database pages</Counter>
    <Counter>\SQLServer:Buffer Node(*)\Foreign pages</Counter>
    <Counter>\SQLServer:Buffer Node(*)\Local node page lookups/sec</Counter>
    <Counter>\SQLServer:Buffer Node(*)\Page life expectancy</Counter>
    <Counter>\SQLServer:Buffer Node(*)\Remote node page lookups/sec</Counter>
    <Counter>\SQLServer:Database Replica(*)\Log Bytes Received/sec</Counter>
    <Counter>\SQLServer:Database Replica(*)\Mirrored Write Transactions/sec</Counter>
    <Counter>\SQLServer:Database Replica(*)\Recovery Queue</Counter>
    <Counter>\SQLServer:Database Replica(_Total)\Log remaining for undo</Counter>
    <Counter>\SQLServer:Database Replica(_Total)\Log Send Queue</Counter>
    <Counter>\SQLServer:Database Replica(_Total)\Redo blocked/sec</Counter>
    <Counter>\SQLServer:Database Replica(_Total)\Redo Bytes Remaining</Counter>
    <Counter>\SQLServer:Database Replica(_Total)\Redone Bytes/sec</Counter>
    <Counter>\SQLServer:Database Replica(_Total)\Total Log requiring undo</Counter>
    <Counter>\SQLServer:Database Replica(_Total)\Transaction Delay</Counter>
    <Counter>\SQLServer:Databases(*)\Active Transactions</Counter>
    <Counter>\SQLServer:Databases(*)\Backup/Restore Throughput/sec</Counter>
    <Counter>\SQLServer:Databases(*)\Bulk Copy Throughput/sec</Counter>
    <Counter>\SQLServer:Databases(*)\Data File(s) Size (KB)</Counter>
    <Counter>\SQLServer:Databases(*)\Log Bytes Flushed/sec</Counter>
    <Counter>\SQLServer:Databases(*)\Log File(s) Size (KB)</Counter>
    <Counter>\SQLServer:Databases(*)\Log File(s) Used Size (KB)</Counter>
    <Counter>\SQLServer:Databases(*)\Log Flush Wait Time</Counter>
    <Counter>\SQLServer:Databases(*)\Log Flush Waits/sec</Counter>
    <Counter>\SQLServer:Databases(*)\Log Flushes/sec</Counter>
    <Counter>\SQLServer:Databases(*)\Log Growths</Counter>
    <Counter>\SQLServer:Databases(*)\Log Shrinks</Counter>
    <Counter>\SQLServer:Databases(*)\Log Truncations</Counter>
    <Counter>\SQLServer:Databases(*)\Percent Log Used</Counter>
    <Counter>\SQLServer:Deprecated Features(*)\Usage</Counter>
    <Counter>\SQLServer:General Statistics\Active Temp Tables</Counter>
    <Counter>\SQLServer:General Statistics\Logins/sec</Counter>
    <Counter>\SQLServer:General Statistics\Logouts/sec</Counter>
    <Counter>\SQLServer:General Statistics\Temp Tables Creation Rate</Counter>
    <Counter>\SQLServer:General Statistics\Temp Tables For Destruction</Counter>
    <Counter>\SQLServer:General Statistics\User Connections</Counter>
    <Counter>\SQLServer:Latches\Latch Waits/sec</Counter>
    <Counter>\SQLServer:Latches\Total Latch Wait Time (ms)</Counter>
    <Counter>\SQLServer:Locks(*)\Average Wait Time (ms)</Counter>
    <Counter>\SQLServer:Locks(*)\Lock Requests/sec</Counter>
    <Counter>\SQLServer:Locks(*)\Lock Timeouts/sec</Counter>
    <Counter>\SQLServer:Locks(*)\Lock Wait Time (ms)</Counter>
    <Counter>\SQLServer:Locks(*)\Lock Waits/sec</Counter>
    <Counter>\SQLServer:Locks(*)\Number of Deadlocks/sec</Counter>
    <Counter>\SQLServer:Memory Manager\Granted Workspace Memory (KB)</Counter>
    <Counter>\SQLServer:Memory Manager\Maximum Workspace Memory (KB)</Counter>
    <Counter>\SQLServer:Memory Manager\Memory Grants Outstanding</Counter>
    <Counter>\SQLServer:Memory Manager\Memory Grants Pending</Counter>
    <Counter>\SQLServer:Memory Manager\Optimizer Memory (KB)</Counter>
    <Counter>\SQLServer:Memory Manager\Stolen Server Memory (KB)</Counter>
    <Counter>\SQLServer:Memory Manager\Target Server Memory (KB)</Counter>
    <Counter>\SQLServer:Memory Manager\Target Server Memory(KB)</Counter>
    <Counter>\SQLServer:Memory Manager\Total Server Memory (KB)</Counter>
    <Counter>\SQLServer:Memory Node(*)\Database Node Memory (KB)</Counter>
    <Counter>\SQLServer:Memory Node(*)\Foreign Node Memory (KB)</Counter>
    <Counter>\SQLServer:Memory Node(*)\Stolen Node Memory (KB)</Counter>
    <Counter>\SQLServer:Memory Node(*)\Target Node Memory (KB)</Counter>
    <Counter>\SQLServer:Memory Node(000)\Total Node Memory (KB)</Counter>
    <Counter>\SQLServer:Plan Cache(*)\Cache Hit Ratio</Counter>
    <Counter>\SQLServer:Resource Pool Stats(*)\CPU usage %</Counter>
    <Counter>\SQLServer:Resource Pool Stats(*)\Max memory (KB)</Counter>
    <Counter>\SQLServer:Resource Pool Stats(*)\Target memory (KB)</Counter>
    <Counter>\SQLServer:Resource Pool Stats(*)\Used memory (KB)</Counter>
    <Counter>\SQLServer:SQL Errors(*)\Errors/sec</Counter>
    <Counter>\SQLServer:SQL Statistics\Auto-Param Attempts/sec</Counter>
    <Counter>\SQLServer:SQL Statistics\Batch Requests/sec</Counter>
    <Counter>\SQLServer:SQL Statistics\Failed Auto-Params/sec</Counter>
    <Counter>\SQLServer:SQL Statistics\Safe Auto-Params/sec</Counter>
    <Counter>\SQLServer:SQL Statistics\SQL Attention rate</Counter>
    <Counter>\SQLServer:SQL Statistics\SQL Compilations/sec</Counter>
    <Counter>\SQLServer:SQL Statistics\SQL Re-Compilations/sec</Counter>
    <Counter>\SQLServer:SQL Statistics\Unsafe Auto-Params/sec</Counter>
    <Counter>\SQLServer:Transactions\Free Space in tempdb (KB)</Counter>
    <Counter>\SQLServer:Transactions\Longest Transaction Running Time</Counter>
    <Counter>\SQLServer:Transactions\NonSnapshot Version Transactions</Counter>
    <Counter>\SQLServer:Transactions\Snapshot Transactions</Counter>
    <Counter>\SQLServer:Transactions\Transactions</Counter>
    <Counter>\SQLServer:Transactions\Version Cleanup rate (KB/s)</Counter>
    <Counter>\SQLServer:Transactions\Version Generation rate (KB/s)</Counter>
    <Counter>\SQLServer:Transactions\Version Store Size (KB)</Counter>
    <Counter>\SQLServer:User Settable(*)\Query</Counter>
    <Counter>\SQLServer:Workload Group Stats(*)\Active parallel threads</Counter>
    <Counter>\SQLServer:Workload Group Stats(*)\Active requests</Counter>
    <Counter>\SQLServer:Workload Group Stats(*)\CPU usage %</Counter>
    <Counter>\SQLServer:Workload Group Stats(*)\Queued requests</Counter>
    <Counter>\SQLServer:Workload Group Stats(*)\Reduced memory grants/sec</Counter>
    <Counter>\SQLServer:Workload Group Stats(*)\Requests completed/sec</Counter>
    <Counter>\SQLServer:Workload Group Stats(*)\Suboptimal plans/sec</Counter>
    <Counter>\System\Context Switches/sec</Counter>
    <Counter>\System\Processor Queue Length</Counter>
    <Counter>\System\System Calls/sec</Counter>
    <Counter>\TCPv4\Connection Failures</Counter>
</PerformanceCounterDataCollector>
</DataCollectorSet>
tools\dbatools\bin\perfmontemplates\collectorsets\PAL - SQL Server 2014 and Up.xml
<DataCollectorSet>
<Name>PAL - SQL Server 2014 and Up</Name>
<DisplayName>PAL - SQL Server 2014 and Up</DisplayName>
<DisplayNameUnresolved>PAL - SQL Server 2014 and Up</DisplayNameUnresolved>
<Description>Data Collector Sets from PAL, a performance monitor data aggregation tool by Microsoft. The toolset was created by Microsoft PFE Clint Huffman who worked with fellow experts to create a list specialized lists of counters. You can find the project at github.com/clinthuffman/PAL</Description>
<DescriptionUnresolved>Data Collector Sets from PAL, a performance monitor data aggregation tool by Microsoft. The toolset was created by Microsoft PFE Clint Huffman who worked with fellow experts to create a list specialized lists of counters. You can find the project at github.com/clinthuffman/PAL</DescriptionUnresolved>
<SubdirectoryFormat>3</SubdirectoryFormat>
<SubdirectoryFormatPattern>yyyyMMdd\-NNNNNN</SubdirectoryFormatPattern>
<PerformanceCounterDataCollector>
    <Name>PAL - SQL Server 2014 and Up Collector</Name>
    <SampleInterval>15</SampleInterval>
    <Counter>\Cache\Dirty Pages</Counter>
    <Counter>\Cache\Lazy Write Flushes/sec</Counter>
    <Counter>\LogicalDisk(*)\% Free Space</Counter>
    <Counter>\LogicalDisk(*)\% Idle Time</Counter>
    <Counter>\LogicalDisk(*)\Avg. Disk Bytes/Read</Counter>
    <Counter>\LogicalDisk(*)\Avg. Disk Bytes/Transfer</Counter>
    <Counter>\LogicalDisk(*)\Avg. Disk Bytes/Write</Counter>
    <Counter>\LogicalDisk(*)\Avg. Disk Queue Length</Counter>
    <Counter>\LogicalDisk(*)\Avg. Disk sec/Read</Counter>
    <Counter>\LogicalDisk(*)\Avg. Disk sec/Transfer</Counter>
    <Counter>\LogicalDisk(*)\Avg. Disk sec/Write</Counter>
    <Counter>\LogicalDisk(*)\Current Disk Queue Length</Counter>
    <Counter>\LogicalDisk(*)\Disk Bytes/sec</Counter>
    <Counter>\LogicalDisk(*)\Disk Reads/sec</Counter>
    <Counter>\LogicalDisk(*)\Disk Transfers/sec</Counter>
    <Counter>\LogicalDisk(*)\Disk Writes/sec</Counter>
    <Counter>\LogicalDisk(*)\Free Megabytes</Counter>
    <Counter>\Memory\% Committed Bytes In Use</Counter>
    <Counter>\Memory\Available MBytes</Counter>
    <Counter>\Memory\Commit Limit</Counter>
    <Counter>\Memory\Committed Bytes</Counter>
    <Counter>\Memory\Free &amp; Zero Page List Bytes</Counter>
    <Counter>\Memory\Free System Page Table Entries</Counter>
    <Counter>\Memory\Long-Term Average Standby Cache Lifetime (s)</Counter>
    <Counter>\Memory\Pages Input/sec</Counter>
    <Counter>\Memory\Pages Output/sec</Counter>
    <Counter>\Memory\Pages/sec</Counter>
    <Counter>\Memory\Pool Nonpaged Bytes</Counter>
    <Counter>\Memory\Pool Paged Bytes</Counter>
    <Counter>\Memory\Pool Paged Resident Bytes</Counter>
    <Counter>\Memory\System Cache Resident Bytes</Counter>
    <Counter>\Memory\Transition Pages RePurposed/sec</Counter>
    <Counter>\MSRS 2011 Web Service\Cache Misses/Sec</Counter>
    <Counter>\MSRS 2011 Web Service\Report Requests</Counter>
    <Counter>\MSRS 2011 Web Service\Total Memory Cache Misses</Counter>
    <Counter>\MSRS 2011 Web Service\Total Requests</Counter>
    <Counter>\Network Inspection System\Average inspection latency (sec/bytes)</Counter>
    <Counter>\Network Interface(*)\Bytes Received/sec</Counter>
    <Counter>\Network Interface(*)\Bytes Sent/sec</Counter>
    <Counter>\Network Interface(*)\Bytes Total/sec</Counter>
    <Counter>\Network Interface(*)\Current Bandwidth</Counter>
    <Counter>\Network Interface(*)\Output Queue Length</Counter>
    <Counter>\Network Interface(*)\Packets Outbound Errors</Counter>
    <Counter>\Network Interface(*)\Packets Received/sec</Counter>
    <Counter>\Network Interface(*)\Packets Sent/sec</Counter>
    <Counter>\Network Interface(*)\Packets/sec</Counter>
    <Counter>\Paging File(*)\% Usage</Counter>
    <Counter>\PhysicalDisk(*)\Avg. Disk Queue Length</Counter>
    <Counter>\PhysicalDisk(*)\Avg. Disk sec/Read</Counter>
    <Counter>\PhysicalDisk(*)\Avg. Disk sec/Write</Counter>
    <Counter>\PhysicalDisk(*)\Current Disk Queue Length</Counter>
    <Counter>\PhysicalDisk(*)\Disk Bytes/sec</Counter>
    <Counter>\Process(*)\Handle Count</Counter>
    <Counter>\Process(*)\ID Process</Counter>
    <Counter>\Process(*)\IO Data Operations/sec</Counter>
    <Counter>\Process(*)\IO Other Operations/sec</Counter>
    <Counter>\Process(*)\IO Read Operations/sec</Counter>
    <Counter>\Process(*)\IO Write Operations/sec</Counter>
    <Counter>\Process(*)\Private Bytes</Counter>
    <Counter>\Process(*)\Thread Count</Counter>
    <Counter>\Process(*)\Virtual Bytes</Counter>
    <Counter>\Process(*)\Working Set</Counter>
    <Counter>\Process(sqlservr)\% Privileged Time</Counter>
    <Counter>\Process(sqlservr)\% Processor Time</Counter>
    <Counter>\Processor Information(*)\% DPC Time</Counter>
    <Counter>\Processor Information(*)\% Interrupt Time</Counter>
    <Counter>\Processor Information(*)\% of Maximum Frequency</Counter>
    <Counter>\Processor Information(*)\% Privileged Time</Counter>
    <Counter>\Processor Information(*)\% Processor Time</Counter>
    <Counter>\Processor Information(*)\% User Time</Counter>
    <Counter>\Processor Information(*)\DPC Rate</Counter>
    <Counter>\Processor Information(*)\Parking Status</Counter>
    <Counter>\Processor(*)\% DPC Time</Counter>
    <Counter>\Processor(*)\% Interrupt Time</Counter>
    <Counter>\Processor(*)\% Privileged Time</Counter>
    <Counter>\Processor(*)\% Processor Time</Counter>
    <Counter>\Processor(*)\% User Time</Counter>
    <Counter>\Processor(*)\DPC Rate</Counter>
    <Counter>\ReportServer:Service\Errors Total</Counter>
    <Counter>\ReportServer:Service\Errors/sec</Counter>
    <Counter>\ReportServer:Service\Memory Pressure State</Counter>
    <Counter>\ReportServer:Service\Memory Shrink Amount</Counter>
    <Counter>\ReportServer:Service\Memory Shrink Notifications/sec</Counter>
    <Counter>\Server\Pool Nonpaged Failures</Counter>
    <Counter>\Server\Pool Paged Failures</Counter>
    <Counter>\SQLAgent:Jobs\Active jobs</Counter>
    <Counter>\SQLAgent:Jobs\Failed jobs</Counter>
    <Counter>\SQLAgent:Jobs\Job success rate</Counter>
    <Counter>\SQLAgent:Jobs\Successful jobs</Counter>
    <Counter>\SQLAgent:JobSteps\Active steps</Counter>
    <Counter>\SQLAgent:JobSteps\Total step retries</Counter>
    <Counter>\SQLServer:Access Methods\Forwarded Records/sec</Counter>
    <Counter>\SQLServer:Access Methods\FreeSpace Scans/sec</Counter>
    <Counter>\SQLServer:Access Methods\Full Scans/sec</Counter>
    <Counter>\SQLServer:Access Methods\Index Searches/sec</Counter>
    <Counter>\SQLServer:Access Methods\Page Splits/sec</Counter>
    <Counter>\SQLServer:Access Methods\Scan Point Revalidations/sec</Counter>
    <Counter>\SQLServer:Access Methods\Table Lock Escalations/sec</Counter>
    <Counter>\SQLServer:Access Methods\Workfiles Created/sec</Counter>
    <Counter>\SQLServer:Access Methods\Worktables Created/sec</Counter>
    <Counter>\SQLServer:Access Methods\Worktables From Cache Ratio</Counter>
    <Counter>\SQLServer:Availability Replica(*)\Bytes Received from Replica/sec</Counter>
    <Counter>\SQLServer:Availability Replica(*)\Bytes Sent to Replica/sec</Counter>
    <Counter>\SQLServer:Availability Replica(*)\Bytes Sent to Transport/sec</Counter>
    <Counter>\SQLServer:Availability Replica(_Total)\Receives from Replica/sec</Counter>
    <Counter>\SQLServer:Availability Replica(_Total)\Resent Messages/sec</Counter>
    <Counter>\SQLServer:Availability Replica(_Total)\Sends to Replica/sec</Counter>
    <Counter>\SQLServer:Buffer Manager(*)\Extension page unreferenced time</Counter>
    <Counter>\SQLServer:Buffer Manager\Background writer pages/sec</Counter>
    <Counter>\SQLServer:Buffer Manager\Buffer cache hit ratio</Counter>
    <Counter>\SQLServer:Buffer Manager\Checkpoint pages/sec</Counter>
    <Counter>\SQLServer:Buffer Manager\Extension free pages</Counter>
    <Counter>\SQLServer:Buffer Manager\Extension outstanding IO counter</Counter>
    <Counter>\SQLServer:Buffer Manager\Free list stalls/sec</Counter>
    <Counter>\SQLServer:Buffer Manager\Free pages</Counter>
    <Counter>\SQLServer:Buffer Manager\Lazy writes/sec</Counter>
    <Counter>\SQLServer:Buffer Manager\Page life expectancy</Counter>
    <Counter>\SQLServer:Buffer Manager\Page lookups/sec</Counter>
    <Counter>\SQLServer:Buffer Manager\Page reads/sec</Counter>
    <Counter>\SQLServer:Buffer Manager\Page writes/sec</Counter>
    <Counter>\SQLServer:Buffer Manager\Readahead pages/sec</Counter>
    <Counter>\SQLServer:Buffer Manager\Target pages</Counter>
    <Counter>\SQLServer:Buffer Node(*)\Database pages</Counter>
    <Counter>\SQLServer:Buffer Node(*)\Foreign pages</Counter>
    <Counter>\SQLServer:Buffer Node(*)\Local node page lookups/sec</Counter>
    <Counter>\SQLServer:Buffer Node(*)\Page life expectancy</Counter>
    <Counter>\SQLServer:Buffer Node(*)\Remote node page lookups/sec</Counter>
    <Counter>\SQLServer:Database Replica(*)\Log Bytes Received/sec</Counter>
    <Counter>\SQLServer:Database Replica(*)\Mirrored Write Transactions/sec</Counter>
    <Counter>\SQLServer:Database Replica(*)\Recovery Queue</Counter>
    <Counter>\SQLServer:Database Replica(_Total)\Log remaining for undo</Counter>
    <Counter>\SQLServer:Database Replica(_Total)\Log Send Queue</Counter>
    <Counter>\SQLServer:Database Replica(_Total)\Redo blocked/sec</Counter>
    <Counter>\SQLServer:Database Replica(_Total)\Redo Bytes Remaining</Counter>
    <Counter>\SQLServer:Database Replica(_Total)\Redone Bytes/sec</Counter>
    <Counter>\SQLServer:Database Replica(_Total)\Total Log requiring undo</Counter>
    <Counter>\SQLServer:Database Replica(_Total)\Transaction Delay</Counter>
    <Counter>\SQLServer:Databases(*)\Active Transactions</Counter>
    <Counter>\SQLServer:Databases(*)\Backup/Restore Throughput/sec</Counter>
    <Counter>\SQLServer:Databases(*)\Bulk Copy Throughput/sec</Counter>
    <Counter>\SQLServer:Databases(*)\Data File(s) Size (KB)</Counter>
    <Counter>\SQLServer:Databases(*)\Log Bytes Flushed/sec</Counter>
    <Counter>\SQLServer:Databases(*)\Log File(s) Size (KB)</Counter>
    <Counter>\SQLServer:Databases(*)\Log File(s) Used Size (KB)</Counter>
    <Counter>\SQLServer:Databases(*)\Log Flush Wait Time</Counter>
    <Counter>\SQLServer:Databases(*)\Log Flush Waits/sec</Counter>
    <Counter>\SQLServer:Databases(*)\Log Flushes/sec</Counter>
    <Counter>\SQLServer:Databases(*)\Log Growths</Counter>
    <Counter>\SQLServer:Databases(*)\Log Shrinks</Counter>
    <Counter>\SQLServer:Databases(*)\Log Truncations</Counter>
    <Counter>\SQLServer:Databases(*)\Percent Log Used</Counter>
    <Counter>\SQLServer:Deprecated Features(*)\Usage</Counter>
    <Counter>\SQLServer:General Statistics\Active Temp Tables</Counter>
    <Counter>\SQLServer:General Statistics\Logins/sec</Counter>
    <Counter>\SQLServer:General Statistics\Logouts/sec</Counter>
    <Counter>\SQLServer:General Statistics\Temp Tables Creation Rate</Counter>
    <Counter>\SQLServer:General Statistics\Temp Tables For Destruction</Counter>
    <Counter>\SQLServer:General Statistics\User Connections</Counter>
    <Counter>\SQLServer:Latches\Latch Waits/sec</Counter>
    <Counter>\SQLServer:Latches\Total Latch Wait Time (ms)</Counter>
    <Counter>\SQLServer:Locks(*)\Average Wait Time (ms)</Counter>
    <Counter>\SQLServer:Locks(*)\Lock Requests/sec</Counter>
    <Counter>\SQLServer:Locks(*)\Lock Timeouts/sec</Counter>
    <Counter>\SQLServer:Locks(*)\Lock Wait Time (ms)</Counter>
    <Counter>\SQLServer:Locks(*)\Lock Waits/sec</Counter>
    <Counter>\SQLServer:Locks(*)\Number of Deadlocks/sec</Counter>
    <Counter>\SQLServer:Memory Manager\Granted Workspace Memory (KB)</Counter>
    <Counter>\SQLServer:Memory Manager\Maximum Workspace Memory (KB)</Counter>
    <Counter>\SQLServer:Memory Manager\Memory Grants Outstanding</Counter>
    <Counter>\SQLServer:Memory Manager\Memory Grants Pending</Counter>
    <Counter>\SQLServer:Memory Manager\Optimizer Memory (KB)</Counter>
    <Counter>\SQLServer:Memory Manager\Stolen Server Memory (KB)</Counter>
    <Counter>\SQLServer:Memory Manager\Target Server Memory (KB)</Counter>
    <Counter>\SQLServer:Memory Manager\Target Server Memory(KB)</Counter>
    <Counter>\SQLServer:Memory Manager\Total Server Memory (KB)</Counter>
    <Counter>\SQLServer:Memory Node(*)\Database Node Memory (KB)</Counter>
    <Counter>\SQLServer:Memory Node(*)\Foreign Node Memory (KB)</Counter>
    <Counter>\SQLServer:Memory Node(*)\Stolen Node Memory (KB)</Counter>
    <Counter>\SQLServer:Memory Node(*)\Target Node Memory (KB)</Counter>
    <Counter>\SQLServer:Memory Node(000)\Total Node Memory (KB)</Counter>
    <Counter>\SQLServer:Plan Cache(*)\Cache Hit Ratio</Counter>
    <Counter>\SQLServer:Resource Pool Stats(*)\CPU usage %</Counter>
    <Counter>\SQLServer:Resource Pool Stats(*)\Max memory (KB)</Counter>
    <Counter>\SQLServer:Resource Pool Stats(*)\Target memory (KB)</Counter>
    <Counter>\SQLServer:Resource Pool Stats(*)\Used memory (KB)</Counter>
    <Counter>\SQLServer:SQL Errors(*)\Errors/sec</Counter>
    <Counter>\SQLServer:SQL Statistics\Auto-Param Attempts/sec</Counter>
    <Counter>\SQLServer:SQL Statistics\Batch Requests/sec</Counter>
    <Counter>\SQLServer:SQL Statistics\Failed Auto-Params/sec</Counter>
    <Counter>\SQLServer:SQL Statistics\Safe Auto-Params/sec</Counter>
    <Counter>\SQLServer:SQL Statistics\SQL Attention rate</Counter>
    <Counter>\SQLServer:SQL Statistics\SQL Compilations/sec</Counter>
    <Counter>\SQLServer:SQL Statistics\SQL Re-Compilations/sec</Counter>
    <Counter>\SQLServer:SQL Statistics\Unsafe Auto-Params/sec</Counter>
    <Counter>\SQLServer:Transactions\Free Space in tempdb (KB)</Counter>
    <Counter>\SQLServer:Transactions\Longest Transaction Running Time</Counter>
    <Counter>\SQLServer:Transactions\NonSnapshot Version Transactions</Counter>
    <Counter>\SQLServer:Transactions\Snapshot Transactions</Counter>
    <Counter>\SQLServer:Transactions\Transactions</Counter>
    <Counter>\SQLServer:Transactions\Version Cleanup rate (KB/s)</Counter>
    <Counter>\SQLServer:Transactions\Version Generation rate (KB/s)</Counter>
    <Counter>\SQLServer:Transactions\Version Store Size (KB)</Counter>
    <Counter>\SQLServer:User Settable(*)\Query</Counter>
    <Counter>\SQLServer:Workload Group Stats(*)\Active parallel threads</Counter>
    <Counter>\SQLServer:Workload Group Stats(*)\Active requests</Counter>
    <Counter>\SQLServer:Workload Group Stats(*)\CPU usage %</Counter>
    <Counter>\SQLServer:Workload Group Stats(*)\Queued requests</Counter>
    <Counter>\SQLServer:Workload Group Stats(*)\Reduced memory grants/sec</Counter>
    <Counter>\SQLServer:Workload Group Stats(*)\Requests completed/sec</Counter>
    <Counter>\SQLServer:Workload Group Stats(*)\Suboptimal plans/sec</Counter>
    <Counter>\System\Context Switches/sec</Counter>
    <Counter>\System\Processor Queue Length</Counter>
    <Counter>\System\System Calls/sec</Counter>
    <Counter>\TCPv4\Connection Failures</Counter>
</PerformanceCounterDataCollector>
</DataCollectorSet>
tools\dbatools\bin\projects\dbatools\dbatools.sln
 
tools\dbatools\bin\projects\dbatools\dbatools.Tests\Connection\ManagementConnectionTest.cs
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Sqlcollaborative.Dbatools.Connection
{
    //TODO: Add a reference to System.Management.Automation to the project so I can test PsCredential

    [TestClass]
    public class ManagementConnectionTest
    {
        [TestMethod]
        public void TestDefaults()
        {
            var mgmtCn = new ManagementConnection();
            Assert.IsNull(mgmtCn.ComputerName);
            Assert.IsFalse(mgmtCn.DisableBadCredentialCache);
            Assert.IsFalse(mgmtCn.DisableCimPersistence);
            Assert.IsFalse(mgmtCn.DisableCredentialAutoRegister);
            Assert.AreEqual(ManagementConnectionType.Wmi | ManagementConnectionType.PowerShellRemoting, mgmtCn.DisabledConnectionTypes);
            Assert.IsFalse(mgmtCn.EnableCredentialFailover);
            Assert.IsFalse(mgmtCn.OverrideExplicitCredential);
            Assert.IsFalse(mgmtCn.UseWindowsCredentials);
            Assert.IsFalse(mgmtCn.WindowsCredentialsAreBad);
            Assert.IsNull(mgmtCn.ToString());
        }

        [TestMethod]
        public void TestRestoreDefaultConfiguration()
        {
            var mgmtCn = new ManagementConnection();
            mgmtCn.DisableBadCredentialCache = true;
            mgmtCn.DisableCredentialAutoRegister = true;
            mgmtCn.OverrideExplicitCredential = true;
            mgmtCn.DisableCimPersistence = true;
            mgmtCn.EnableCredentialFailover = true;
            mgmtCn.RestoreDefaultConfiguration();

            Assert.IsFalse(mgmtCn.DisableBadCredentialCache);
            Assert.IsFalse(mgmtCn.DisableCimPersistence);
            Assert.IsFalse(mgmtCn.DisableCredentialAutoRegister);
            Assert.IsFalse(mgmtCn.EnableCredentialFailover);
            Assert.IsFalse(mgmtCn.OverrideExplicitCredential);
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools.Tests\dbatools.Tests.csproj
 
tools\dbatools\bin\projects\dbatools\dbatools.Tests\packages.config
<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="MSTest.TestFramework" version="1.3.2" targetFramework="net452" />
</packages>
tools\dbatools\bin\projects\dbatools\dbatools.Tests\Parameter\DbaInstanceParamaterTest.cs
using System;
using System.Net;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Sqlcollaborative.Dbatools.Connection;
using Sqlcollaborative.Dbatools.Exceptions;

namespace Sqlcollaborative.Dbatools.Parameter
{
    [TestClass]
    public class DbaInstanceParamaterTest
    {
        [TestMethod]
        public void TestStringConstructor()
        {
            var dbaInstanceParamater = new DbaInstanceParameter("someMachine");

            Assert.AreEqual("someMachine", dbaInstanceParamater.FullName);
            Assert.AreEqual("someMachine", dbaInstanceParamater.FullSmoName);
            Assert.AreEqual(SqlConnectionProtocol.Any, dbaInstanceParamater.NetworkProtocol);
            Assert.IsFalse(dbaInstanceParamater.IsLocalHost);
            Assert.IsFalse(dbaInstanceParamater.IsConnectionString);
        }

        [DataRow(null)]
        [DataRow("")]
        [DataRow(" ")]
        [DataRow("\n")]
        [DataRow(" \n \t")]
        [DataRow(" \v\t\t ")]
        [DataRow(null)]
        [ExpectedException(typeof(BloodyHellGiveMeSomethingToWorkWithException), "Bloody hell! Don't give me an empty string for an instance name!")]
        [TestMethod]
        public void TestEmptyString(string whitespace)
        {
            try
            {
                new DbaInstanceParameter(whitespace);
            }
            catch (BloodyHellGiveMeSomethingToWorkWithException ex)
            {
                Assert.AreEqual("DbaInstanceParameter", ex.ParameterClass);
                throw;
            }
        }

        [TestMethod]
        public void TestConnectionString()
        {
            var dbaInstanceParamater = new DbaInstanceParameter("Server=tcp:server.database.windows.net;Database=myDataBase;User ID =[LoginForDb]@[serverName]; Password = myPassword; Trusted_Connection = False;Encrypt = True; ");
            Assert.IsTrue(dbaInstanceParamater.IsConnectionString);
        }

        [ExpectedException(typeof(ArgumentException))]
        [TestMethod]
        public void TestConnectionStringBadKey()
        {
            new DbaInstanceParameter("Server=tcp:server.database.windows.net;Database=myDataBase;Trusted_Connection = True;Wrong=true");
        }

        [ExpectedException(typeof(FormatException))]
        [TestMethod]
        public void TestConnectionStringBadValue()
        {
            new DbaInstanceParameter("Server=tcp:server.database.windows.net;Database=myDataBase;Trusted_Connection=weird");
        }

        /// <summary>
        /// Checks that localhost\instancename is treated as a localhost connection
        /// </summary>
        [TestMethod]
        public void TestLocalhostNamedInstance()
        {
            var dbaInstanceParamater = new DbaInstanceParameter("localhost\\sql2008r2sp2");

            Assert.AreEqual("localhost\\sql2008r2sp2", dbaInstanceParamater.FullName);
            Assert.IsTrue(dbaInstanceParamater.IsLocalHost);
            Assert.AreEqual("localhost\\sql2008r2sp2", dbaInstanceParamater.FullSmoName);
            Assert.AreEqual("[localhost\\sql2008r2sp2]", dbaInstanceParamater.SqlFullName);
            Assert.AreEqual(SqlConnectionProtocol.Any, dbaInstanceParamater.NetworkProtocol);
            Assert.IsTrue(dbaInstanceParamater.IsLocalHost);
            Assert.IsFalse(dbaInstanceParamater.IsConnectionString);
        }

        /// <summary>
        /// Checks that . is treated as a localhost connection
        /// </summary>
        [TestMethod]
        public void TestDotHostname()
        {
            var dbaInstanceParamater = new DbaInstanceParameter(".");

            Assert.AreEqual(".", dbaInstanceParamater.ComputerName);
            Assert.AreEqual("[.]", dbaInstanceParamater.SqlComputerName);
            Assert.AreEqual(".", dbaInstanceParamater.FullName);
            Assert.IsTrue(dbaInstanceParamater.IsLocalHost);
            Assert.AreEqual("NP:.", dbaInstanceParamater.FullSmoName);
            Assert.AreEqual(@"MSSQLSERVER", dbaInstanceParamater.InstanceName);
            Assert.AreEqual(@"[MSSQLSERVER]", dbaInstanceParamater.SqlInstanceName);
            Assert.AreEqual(@"[.]", dbaInstanceParamater.SqlFullName);
            Assert.AreEqual(SqlConnectionProtocol.NP, dbaInstanceParamater.NetworkProtocol);
            Assert.IsTrue(dbaInstanceParamater.IsLocalHost);
            Assert.IsFalse(dbaInstanceParamater.IsConnectionString);
        }

        /// <summary>
        /// Checks that localdb named instances
        /// </summary>
        [TestMethod]
        //[Ignore()]
        public void TestLocalDb()
        {
            var dbaInstanceParamater = new DbaInstanceParameter(@"(LocalDb)\MSSQLLocalDB");

            Assert.AreEqual("localhost", dbaInstanceParamater.ComputerName);
            Assert.AreEqual("[localhost]", dbaInstanceParamater.SqlComputerName);
            Assert.AreEqual(@"(localdb)\MSSQLLocalDB", dbaInstanceParamater.FullName);
            Assert.AreEqual(@"(localdb)\MSSQLLocalDB", dbaInstanceParamater.FullSmoName);
            Assert.AreEqual(@"MSSQLLocalDB", dbaInstanceParamater.InstanceName);
            Assert.AreEqual(@"[MSSQLLocalDB]", dbaInstanceParamater.SqlInstanceName);
            Assert.AreEqual(SqlConnectionProtocol.Any, dbaInstanceParamater.NetworkProtocol);
            Assert.IsTrue(dbaInstanceParamater.IsLocalHost);
            Assert.IsFalse(dbaInstanceParamater.IsConnectionString);
        }

        /// <summary>
        /// Checks parsing of a localdb connectionstring
        /// </summary>
        [TestMethod]
        public void TestLocalDbConnectionString()
        {
            var dbaInstanceParamater = new DbaInstanceParameter(@"Data Source=(LocalDb)\MSSQLLocalDB;Initial Catalog=aspnet-MvcMovie;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\Movies.mdf");

            Assert.AreEqual("localhost", dbaInstanceParamater.ComputerName);
            Assert.AreEqual("[localhost]", dbaInstanceParamater.SqlComputerName);
            Assert.AreEqual(@"localhost\MSSQLLocalDB", dbaInstanceParamater.FullName);
            Assert.AreEqual(@"localhost\MSSQLLocalDB", dbaInstanceParamater.FullSmoName);
            Assert.AreEqual(@"localhost\MSSQLLocalDB", dbaInstanceParamater.ToString());
            Assert.AreEqual(@"MSSQLLocalDB", dbaInstanceParamater.InstanceName);
            Assert.AreEqual(SqlConnectionProtocol.Any, dbaInstanceParamater.NetworkProtocol);
            Assert.IsTrue(dbaInstanceParamater.IsLocalHost);
            Assert.IsTrue(dbaInstanceParamater.IsConnectionString);
        }

        /// <summary>
        /// Checks that 127.0.0.1 is treated as a localhost connection
        /// </summary>
        [DataRow("127.0.0.1")]
        [DataRow("::1")]
        [DataRow("0.0.0.0")]
        [DataRow("192.168.1.1")]
        [DataTestMethod]
        [TestMethod]
        public void TestIpAddressConstructor(string ipStr)
        {
            var ip = IPAddress.Parse(ipStr);
            var dbaInstanceParamater = new DbaInstanceParameter(ip);

            Assert.AreEqual(ip.ToString(), dbaInstanceParamater.FullName);
            Assert.AreEqual('[' + ip.ToString() + ']', dbaInstanceParamater.SqlFullName);
            Assert.AreEqual(ip.ToString(), dbaInstanceParamater.FullSmoName);
            Assert.AreEqual(ip.ToString(), dbaInstanceParamater.ToString());
            Assert.AreEqual(SqlConnectionProtocol.Any, dbaInstanceParamater.NetworkProtocol);
        }

        /// <summary>
        /// Checks that 127.0.0.1 is treated as a localhost connection
        /// </summary>
        [DataRow("127.0.0.1")]
        [DataRow("::1")]
        [DataRow("localhost")]
        [DataTestMethod]
        [TestMethod]
        public void TestLocalhost(string localhost)
        {
            var dbaInstanceParamater = new DbaInstanceParameter(localhost);

            Assert.AreEqual(localhost, dbaInstanceParamater.FullName);
            Assert.AreEqual('[' + localhost + ']', dbaInstanceParamater.SqlFullName);
            Assert.AreEqual(localhost, dbaInstanceParamater.FullSmoName);
            Assert.AreEqual(localhost, dbaInstanceParamater.ToString());
            Assert.AreEqual(SqlConnectionProtocol.Any, dbaInstanceParamater.NetworkProtocol);
            Assert.IsTrue(dbaInstanceParamater.IsLocalHost);
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools.Tests\Properties\AssemblyInfo.cs
using System.Reflection;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following 
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("dbatools.Tests")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("dbatools.Tests")]
[assembly: AssemblyCopyright("Copyright ©  2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible 
// to COM components.  If you need to access a type in this assembly from 
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("afd56ad2-f9f0-4c9d-901b-69ca1455c32a")]

// Version information for an assembly consists of the following four values:
//
//      Major Version
//      Minor Version 
//      Build Number
//      Revision
//
// You can specify all the values or you can default the Build and Revision Numbers 
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
tools\dbatools\bin\projects\dbatools\dbatools.Tests\Utility\SizeTest.cs
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Sqlcollaborative.Dbatools.Utility
{
    [TestClass]
    public class SizeTest
    {
        [TestMethod]
        public void TestDefaultContrustor()
        {
            var size = new Size();
            Assert.AreEqual(0, size.Byte);
            Assert.AreEqual("0 B", size.ToString());
            // Make sure the defaults come from the utility host.
            Assert.AreEqual(UtilityHost.SizeStyle, size.Style);
            Assert.AreEqual(UtilityHost.SizeDigits, size.Digits);
            // Make sure the default are what we explicitly what we expect them to be.
            Assert.AreEqual(SizeStyle.Dynamic, size.Style);
            Assert.AreEqual(2, size.Digits);
        }

        [TestMethod]
        public void TestGetHash()
        {
            var size = 10234453626262624;
            var byteSize = size ;
            Assert.AreEqual(size.GetHashCode(), byteSize.GetHashCode());
        }

        [TestMethod]
        public void TestCompareToObjOfSize()
        {
            var sizes = new Object[] { new Size(42), new Size(56), new Size(42) };
            Assert.AreEqual(-1, ((Size)sizes[0]).CompareTo(sizes[1]));
            Assert.AreEqual(0, ((Size)sizes[0]).CompareTo(sizes[2]));
            Assert.AreEqual(1, ((Size)sizes[1]).CompareTo(sizes[2]));
        }

        [TestMethod]
        public void TestCompareToObjOfInt()
        {
            var sizes = new[] { new Size(42), new Size(56), new Size(42) };
            Assert.AreEqual(-1, sizes[0].CompareTo(sizes[1].Byte));
            Assert.AreEqual(0, sizes[0].CompareTo(sizes[2].Byte));
            Assert.AreEqual(1, sizes[1].CompareTo(sizes[2].Byte));
        }

        [DataRow(42u, 56u)]
        [DataRow(10000u, 2500000u)]
        [DataRow(2u, 7u)]
        [TestMethod]
        public void TestCompareToObjOfUInt(uint a, uint b)
        {
            Assert.IsTrue(a < b, string.Format("Invalid test data, A ({0}) should be less than B ({1})", a, b));
            var sizes = new[] { (Size)a, (Size)b, (Size)a };
            Assert.AreEqual(-1, sizes[0].CompareTo(b));
            Assert.AreEqual(0, sizes[0].CompareTo(a));
            Assert.AreEqual(1, sizes[1].CompareTo(a));
        }

        [DataRow(42, 56)]
        [DataRow(10000, 2500000)]
        [DataRow(2, 7)]
        [TestMethod]
        public void TestCompareToObjOfDecimal(int a, int b)
        {
            Assert.IsTrue(a < b, string.Format("Invalid test data, A ({0}) should be less than B ({1})", a, b));
            var sizes = new[] { (Size)(decimal)a, (Size)(decimal)b, (Size)(decimal)a };
            Assert.AreEqual(-1, sizes[0].CompareTo((decimal)b));
            Assert.AreEqual(0, sizes[0].CompareTo((decimal)a));
            Assert.AreEqual(1, sizes[1].CompareTo((decimal)a));
        }

        [DataRow(42d, 56d)]
        [DataRow(10000d, 2500000d)]
        [DataRow(2d, 7d)]
        [TestMethod]
        public void TestCompareToObjOfDouble(double a, double b)
        {
            Assert.IsTrue(a < b, string.Format("Invalid test data, A ({0}) should be less than B ({1})", a, b));
            var sizes = new [] { (Size)a, (Size)b, (Size)a };
            Assert.AreEqual(-1, sizes[0].CompareTo(b));
            Assert.AreEqual(0, sizes[0].CompareTo(a));
            Assert.AreEqual(1, sizes[1].CompareTo(a));
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void TestCompareToObjOfInvalid()
        {
            var size = new Size();
            try
            {
                size.CompareTo(Guid.Empty);
            }
            catch (ArgumentException ex)
            {
                Assert.AreEqual("Cannot compare a Sqlcollaborative.Dbatools.Utility.Size to a System.Guid", ex.Message);
                throw;
            }
        }


        [DataRow(42, 56)]
        [DataRow(10000, 2500000)]
        [DataRow(2, 7)]
        [TestMethod]
        public void TestCompareToSize(int a, int b)
        {
            Assert.IsTrue(a < b, string.Format("Invalid test data, A ({0}) should be less than B ({1})", a, b));
            var sizes = new[] { new Size(a), new Size(b), new Size(a)};
            Assert.AreEqual(-1, sizes[0].CompareTo(sizes[1]));
            Assert.AreEqual(0, sizes[0].CompareTo(sizes[2]));
            Assert.AreEqual(1, sizes[1].CompareTo(sizes[2]));
        }

        [TestMethod]
        public void TestEqualsTo()
        {
            var sizes = new[] { new Size(42), new Size(56), new Size(42)};
            Assert.IsFalse(sizes[0].Equals(sizes[1]));
            Assert.IsTrue(sizes[0].Equals(sizes[2]));
        }

        [TestMethod]
        public void TestDigitsToString()
        {
            var size = new Size(5607509301657);
            Assert.AreEqual("5.10 TB", size.ToString());
            size.Digits = 1;
            Assert.AreEqual("5.1 TB", size.ToString());
            size.Digits = 0;
            Assert.AreEqual("5 TB", size.ToString());
            size.Digits = -42;
            Assert.AreEqual("5 TB", size.ToString());
        }

        [TestMethod]
        public void TestStyleToString()
        {
            var size = new Size(5607509301657);
            Assert.AreEqual("5.10 TB", size.ToString());
            size.Style = SizeStyle.Gigabyte;
            Assert.AreEqual("5,222.40 GB", size.ToString());
            size.Digits = 1;
            Assert.AreEqual("5,222.4 GB", size.ToString());
            size.Digits = 0;
            Assert.AreEqual("5,222 GB", size.ToString());
            size.Style = SizeStyle.Megabyte;
            Assert.AreEqual("5,347,738 MB", size.ToString());
            size.Style = SizeStyle.Kilobyte;
            Assert.AreEqual("5,476,083,302 KB", size.ToString());
            size.Style = SizeStyle.Byte;
            Assert.AreEqual("5607509301657 B", size.ToString());
            size.Style = SizeStyle.Plain;
            Assert.AreEqual("5607509301657", size.ToString());
            // Because the first time we were using dynamic styling.
            size.Style = SizeStyle.Terabyte;
            Assert.AreEqual("5 TB", size.ToString());
        }

        [TestMethod]
        public void TestTerabytes()
        {
            var size = new Size(5607509301657);
            Assert.AreEqual("5.10 TB", size.ToString());
        }

        [TestMethod]
        public void TestGigabytes()
        {
            var size = new Size(72 * (long)Math.Pow(1024, 3));
            Assert.AreEqual("72.00 GB", size.ToString());
        }

        [TestMethod]
        public void TestMegabytes()
        {
            var size = new Size(49 * (long)Math.Pow(1024, 2));
            Assert.AreEqual("49.00 MB", size.ToString());
        }

        [TestMethod]
        public void TestKilobytes()
        {
            var size = new Size(52 * (long)Math.Pow(1024, 1));
            Assert.AreEqual("52.00 KB", size.ToString());
        }

        [TestMethod]
        public void TestBytes()
        {
            var size = new Size(526);
            Assert.AreEqual("526 B", size.ToString());
            size.Digits = 34;
            Assert.AreEqual("526 B", size.ToString());
        }

        [TestMethod]
        public void TestUnlimited()
        {
            var size = new Size(-1);
            Assert.AreEqual("Unlimited", size.ToString());
        }

        [TestMethod]
        public void TestZeroBytes()
        {
            var size = new Size(0);
            Assert.AreEqual("0 B", size.ToString());
        }

        [TestMethod]
        public void TestNegativeBytes()
        {
            var size = new Size(-2);
            Assert.AreEqual("", size.ToString());
        }

        [TestMethod]
        public void TestLargeDecimal()
        {
            Size size = 1000000000000000m;
            Assert.AreEqual(909, size.Terabyte, 0.9);
            Assert.AreEqual(953674316, size.Megabyte, 0.9);
            Assert.AreEqual("909.49 TB", size.ToString());
            decimal reverse = size;
            Assert.AreEqual(1000000000000000m, reverse);
        }

        [TestMethod]
        public void TestLargeDouble()
        {
            Size size = 1000000000000000d;
            Assert.AreEqual(909, size.Terabyte, 0.9);
            Assert.AreEqual("909.49 TB", size.ToString());
            double reverse = size;
            Assert.AreEqual(1000000000000000d, reverse);
        }

        [TestMethod]
        public void TestInt32Cast()
        {
            int iSize = 1000000000;
            Size size = iSize;
            Assert.AreEqual(iSize, size.Byte);
            Assert.AreEqual(953, size.Megabyte, 0.9);
            Assert.AreEqual("953.67 MB", size.ToString());
            int reverse = size;
            Assert.AreEqual(1000000000, reverse);
        }

        [DataRow(1)]
        [DataRow(50)]
        [DataRow(230)]
        [DataRow(53687091200)] // 50 GB
        [DataRow(1000000000000000)]
        [TestMethod]
        public void TestGetHashCode(long iSize)
        {
            var size = new Size(iSize);
            Assert.AreEqual(iSize.GetHashCode(), size.GetHashCode());
        }

        [DataRow(2, 2, 4)]
        [DataRow(1024, 1024, 2048)]
        [DataRow(1024, -1024, 0)]
        [TestMethod]
        public void TestAddition(long a, long b, long result)
        {
            Assert.AreEqual(new Size(result), new Size(a) + new Size(b));
        }

        [DataRow(2, 2, 0)]
        [DataRow(1024, 1024, 0)]
        [DataRow(1024, -1024, 2048)]
        [TestMethod]
        public void TestSubtraction(long a, long b, long result)
        {
            Assert.AreEqual(new Size(result), new Size(a) - new Size(b));
        }

        [DataRow(2, 2, 4)]
        [DataRow(2, 3, 6)]
        [DataRow(1024, 1024, 1048576)]
        [DataRow(1024, -1024, -1048576)]
        [TestMethod]
        public void TestMultiplication(long a, long b, long result)
        {
            Assert.AreEqual(new Size(result), new Size(a) * new Size(b));
        }

        [DataRow(2, 2, 1)]
        [DataRow(2, 3, 0)]
        [DataRow(1024, 1024, 1)]
        [DataRow(1024, -1024, -1)]
        [TestMethod]
        public void TestDivision(long a, long b, long result)
        {
            Assert.AreEqual(new Size(result), new Size(a) / new Size(b));
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Commands\WriteMessageCommand.cs
using Sqlcollaborative.Dbatools.Message;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Management.Automation;
using System.Text.RegularExpressions;

namespace Sqlcollaborative.Dbatools.Commands
{
    /// <summary>
    /// Implements the Write-Message command, performing message handling and loggin
    /// </summary>
    [Cmdlet("Write", "Message")]
    public class WriteMessageCommand : PSCmdlet
    {
        #region Parameters
        /// <summary>
        /// This parameter represents the verbosity of the message. The lower the number, the more important it is for a human user to read the message.
        /// By default, the levels are distributed like this:
        /// - 1-3 Direct verbose output to the user (using Write-Host)
        /// - 4-6 Output only visible when requesting extra verbosity (using Write-Verbose)
        /// - 1-9 Debugging information, written using Write-Debug
        /// 
        /// In addition, it is possible to select the level "Warning" which moves the message out of the configurable range:
        /// The user will always be shown this message, unless he silences the entire verbosity.
        /// 
        /// Possible levels:
        /// Critical (1), Important / Output / Host (2), Significant (3), VeryVerbose (4), Verbose (5), SomewhatVerbose (6), System (7), Debug (8), InternalComment (9), Warning (666)
        /// Either one of the strings or its respective number will do as input.
        /// </summary>
        [Parameter()]
        public MessageLevel Level = MessageLevel.Verbose;

        /// <summary>
        /// The message to write/log. The function name and timestamp will automatically be prepended.
        /// </summary>
        [Parameter(Mandatory = true, Position = 0)]
        [AllowEmptyString]
        [AllowNull]
        public string Message;

        /// <summary>
        /// Tags to add to the message written.
		/// This allows filtering and grouping by category of message, targeting specific messages.
        /// </summary>
        [Parameter()]
        public string[] Tag;

        /// <summary>
        /// The name of the calling function.
		/// Will be automatically set, but can be overridden when necessary.
        /// </summary>
        [Parameter()]
        public string FunctionName;

        /// <summary>
        /// The name of the module, the calling function is part of.
		/// Will be automatically set, but can be overridden when necessary.
        /// </summary>
        [Parameter()]
        public string ModuleName;

        /// <summary>
        /// The file in which Write-Message was called.
		/// Will be automatically set, but can be overridden when necessary.
        /// </summary>
        [Parameter()]
        public string File;

        /// <summary>
        /// The line on which Write-Message was called.
		/// Will be automatically set, but can be overridden when necessary.
        /// </summary>
        [Parameter()]
        public int Line;

        /// <summary>
        /// If an error record should be noted with the message, add the full record here.
		/// Especially designed for use with Warning-mode, it can legally be used in either mode.
        /// The error will be added to the $Error variable and enqued in the logging/debugging system.
        /// </summary>
        [Parameter()]
        public ErrorRecord[] ErrorRecord;

        /// <summary>
        /// Allows specifying an inner exception as input object. This will be passed on to the logging and used for messages.
		/// When specifying both ErrorRecord AND Exception, Exception wins, but ErrorRecord is still used for record metadata.
        /// </summary>
        [Parameter()]
        public Exception Exception;

        /// <summary>
        /// Setting this parameter will cause this function to write the message only once per session.
		/// The string passed here and the calling function's name are used to create a unique ID, which is then used to register the action in the configuration system.
		/// Thus will the lockout only be written if called once and not burden the system unduly.
        /// This lockout will be written as a hidden value, to see it use Get-DbaConfig -Force.
        /// </summary>
        [Parameter()]
        public string Once;

        /// <summary>
        /// Disables automatic appending of exception messages.
		/// Use in cases where you already have a speaking message interpretation and do not need the original message.
        /// </summary>
        [Parameter()]
        public SwitchParameter OverrideExceptionMessage;

        /// <summary>
        /// Add the object the message is all about, in order to simplify debugging / troubleshooting.
		/// For example, when calling this from a function targeting a remote computer, the computername could be specified here, allowing all messages to easily be correlated to the object processed.
        /// </summary>
        [Parameter()]
        public object Target;

        /// <summary>
        /// This parameters disables user-friendly warnings and enables the throwing of exceptions.
		/// This is less user friendly, but allows catching exceptions in calling scripts.
        /// </summary>
        [Parameter()]
        public bool EnableException;

        /// <summary>
        /// Enables breakpoints on the current message. By default, setting '-Debug' will NOT cause an interrupt on the current position.
        /// </summary>
        [Parameter()]
        public SwitchParameter Breakpoint;
        #endregion Parameters

        #region Private fields
        /// <summary>
        /// The start time of the cmdlet
        /// </summary>
        private DateTime _timestamp;

        /// <summary>
        /// Whether this cmdlet is run in silent mode
        /// </summary>
        private bool _silent = false;

        /// <summary>
        /// Whether this cmdlet was called by Stop-Function
        /// </summary>
        private bool _fromStopFunction = false;

        /// <summary>
        /// The current callstack
        /// </summary>
        private IEnumerable<CallStackFrame> _callStack = null;

        /// <summary>
        /// How many items exist on the callstack
        /// </summary>
        private int _stackDepth;

        /// <summary>
        /// The message to write
        /// </summary>
        private string _message;

        /// <summary>
        /// The message simplified without timestamps. Used for logging.
        /// </summary>
        private string _messageSimple;

        /// <summary>
        /// The message to write in color
        /// </summary>
        private string _messageColor;

        /// <summary>
        /// Non-colored version of developermode
        /// </summary>
        private string _messageDeveloper;

        /// <summary>
        /// Colored version of developermode
        /// </summary>
        private string _messageDeveloperColor;

        /// <summary>
        /// Scriptblock that writes the host messages
        /// </summary>
        private static string _writeHostScript = @"
param ( $string )

if ([Sqlcollaborative.Dbatools.Message.MessageHost]::DeveloperMode) { Write-HostColor -String $string -DefaultColor ([Sqlcollaborative.Dbatools.Message.MessageHost]::DeveloperColor) -ErrorAction Ignore }
else { Write-HostColor -String $string -DefaultColor ([Sqlcollaborative.Dbatools.Message.MessageHost]::InfoColor) -ErrorAction Ignore }
";

        /// <summary>
        /// List of tags to process
        /// </summary>
        private List<string> _Tags = new List<string>();

        /// <summary>
        /// Whether debug mode is enabled
        /// </summary>
        private bool _isDebug;
        #endregion Private fields

        #region Private properties
        /// <summary>
        /// The input message with the error content included if desired
        /// </summary>
        private string _errorQualifiedMessage
        {
            get
            {
                if (ErrorRecord == null)
                    return Message;

                if (ErrorRecord.Length == 0)
                    return Message;

                if (OverrideExceptionMessage.ToBool())
                    return Message;

                if (Regex.IsMatch(Message, Regex.Escape(ErrorRecord[0].Exception.Message)))
                    return Message;

                return String.Format("{0} | {1}", Message, ErrorRecord[0].Exception.Message);
            }
        }

        /// <summary>
        /// The final message to use for internal logging
        /// </summary>
        private string _MessageSystem
        {
            get
            {
                return GetMessageSimple();
            }
        }

        /// <summary>
        /// The final message to use for writing to streams, such as verbose or warning
        /// </summary>
        private string _MessageStreams
        {
            get
            {
                if (MessageHost.DeveloperMode)
                    return GetMessageDeveloper();
                else
                    return GetMessage();
            }
        }

        /// <summary>
        /// The final message to use for host messages (write using Write-HostColor)
        /// </summary>
        private string _MessageHost
        {
            get
            {
                if (MessageHost.DeveloperMode)
                    return GetMessageDeveloperColor();
                else
                    return GetMessageColor();
            }
        }

        /// <summary>
        /// Provide breadcrumb queue of the callstack
        /// </summary>
        private string _BreadCrumbsString
        {
            get
            {
                string crumbs = String.Join(" > ", _callStack.Select(name => name.FunctionName).Where(name => name != "Write-Message" && name != "Stop-Function" && name != "<ScriptBlock>").Reverse().ToList());
                if (crumbs.EndsWith(FunctionName))
                    return String.Format("[{0}]\n    ", crumbs);
                return String.Format("[{0}] [{1}]\n    ", crumbs, FunctionName);
            }
        }

        /// <summary>
        /// Provide a breadcrumb queue of the callstack in color tags
        /// </summary>
        private string _BreadCrumbsStringColored
        {
            get
            {
                string crumbs = String.Join("</c> > <c='sub'>", _callStack.Select(name => name.FunctionName).Where(name => name != "Write-Message" && name != "Stop-Function" && name != "<ScriptBlock>").Reverse().ToList());
                if (crumbs.EndsWith(FunctionName))
                    return String.Format("[<c='sub'>{0}</c>]\n    ", crumbs);
                return String.Format("[<c='sub'>{0}</c>] [<c='sub'>{1}</c>]\n    ", crumbs, FunctionName);
            }
        }
        #endregion Private properties

        #region Cmdlet Implementation
        /// <summary>
        /// Processes the begin phase of the cmdlet
        /// </summary>
        protected override void BeginProcessing()
        {
            _timestamp = DateTime.Now;

            #region Resolving Meta Information
            _callStack = Utility.UtilityHost.Callstack;
            CallStackFrame callerFrame = null;
            if (_callStack.Count() > 0)
                callerFrame = _callStack.First();
            _stackDepth = _callStack.Count();

            if (callerFrame != null)
            {
                if (String.IsNullOrEmpty(FunctionName))
                {
                    if (callerFrame.InvocationInfo == null)
                        FunctionName = callerFrame.FunctionName;
                    else if (callerFrame.InvocationInfo.MyCommand == null)
                        FunctionName = callerFrame.InvocationInfo.InvocationName;
                    else if (callerFrame.InvocationInfo.MyCommand.Name != "")
                        FunctionName = callerFrame.InvocationInfo.MyCommand.Name;
                    else
                        FunctionName = callerFrame.FunctionName;
                }

                if (String.IsNullOrEmpty(ModuleName))
                    if ((callerFrame.InvocationInfo != null) && (callerFrame.InvocationInfo.MyCommand != null))
                        ModuleName = callerFrame.InvocationInfo.MyCommand.ModuleName;

                if (String.IsNullOrEmpty(File))
                    File = callerFrame.Position.File;

                if (Line <= 0)
                    Line = callerFrame.Position.EndLineNumber;

                if (callerFrame.FunctionName == "Stop-Function")
                    _fromStopFunction = true;
            }

            if (String.IsNullOrEmpty(FunctionName))
                FunctionName = "<Unknown>";
            if (String.IsNullOrEmpty(ModuleName))
                ModuleName = "<Unknown>";

            if (MessageHost.DisableVerbosity)
                _silent = true;

            if (Tag != null)
                foreach (string item in Tag)
                    _Tags.Add(item);

            _isDebug = (_callStack.Count() > 1) && _callStack.ElementAt(_callStack.Count() - 2).InvocationInfo.BoundParameters.ContainsKey("Debug");
            #endregion Resolving Meta Information
        }

        /// <summary>
        /// Processes the process phase of the cmdlet
        /// </summary>
        protected override void ProcessRecord()
        {
            #region Perform Transforms
            if ((!_fromStopFunction) && (Target != null))
                Target = ResolveTarget(Target);

            if (!_fromStopFunction)
            {
                if (Exception != null)
                    Exception = ResolveException(Exception);
                else if (ErrorRecord != null)
                {
                    Exception tempException = null;
                    for (int n = 0; n < ErrorRecord.Length; n++)
                    {
                        // If both Exception and ErrorRecord are specified, override the first error record's exception.
                        if ((n == 0) && (Exception != null))
                            tempException = Exception;
                        else
                            tempException = ResolveException(ErrorRecord[n].Exception);
                        if (tempException != ErrorRecord[n].Exception)
                            ErrorRecord[n] = new ErrorRecord(tempException, ErrorRecord[n].FullyQualifiedErrorId, ErrorRecord[n].CategoryInfo.Category, ErrorRecord[n].TargetObject);
                    }
                }
            }

            if (Level != MessageLevel.Warning)
                Level = ResolveLevel(Level);
            #endregion Perform Transforms

            #region Exception Integration
            /*
                While conclusive error handling must happen after message handling,
                in order to integrate the exception message into the actual message,
                it becomes necessary to first integrate the exception and error record parameters into a uniform view
	
                Note: Stop-Function never specifies this parameter, thus it is not necessary to check,
                whether this function was called from Stop-Function.
             */
            if ((ErrorRecord == null) && (Exception != null))
            {
                ErrorRecord = new ErrorRecord[1];
                ErrorRecord[0] = new ErrorRecord(Exception, String.Format("{0}_{1}", ModuleName, FunctionName), ErrorCategory.NotSpecified, Target);
            }
            #endregion Exception Integration

            #region Error handling
            if (ErrorRecord != null)
            {
                if (!_fromStopFunction)
                    if (EnableException)
                        foreach (ErrorRecord record in ErrorRecord)
                            WriteError(record);

                LogHost.WriteErrorEntry(ErrorRecord, FunctionName, ModuleName, _Tags, _timestamp, _MessageSystem, System.Management.Automation.Runspaces.Runspace.DefaultRunspace.InstanceId, Environment.MachineName);
            }
            #endregion Error handling

            LogEntryType channels = LogEntryType.None;

            #region Warning handling
            if (Level == MessageLevel.Warning)
            {
                if (!_silent)
                {
                    if (!String.IsNullOrEmpty(Once))
                    {
                        string onceName = String.Format("MessageOnce.{0}.{1}", FunctionName, Once).ToLower();
                        if (!(Configuration.ConfigurationHost.Configurations.ContainsKey(onceName) && (bool)Configuration.ConfigurationHost.Configurations[onceName].Value))
                        {
                            WriteWarning(_MessageStreams);
                            channels = channels | LogEntryType.Warning;

                            Configuration.Config cfg = new Configuration.Config();
                            cfg.Module = "messageonce";
                            cfg.Name = String.Format("{0}.{1}", FunctionName, Once).ToLower();
                            cfg.Hidden = true;
                            cfg.Description = "Locking setting that disables further display of the specified message";
                            cfg.Value = true;

                            Configuration.ConfigurationHost.Configurations[onceName] = cfg;
                        }
                    }
                    else
                    {
                        WriteWarning(_MessageStreams);
                        channels = channels | LogEntryType.Warning;
                    }
                }
                WriteDebug(_MessageStreams);
                channels = channels | LogEntryType.Debug;
            }
            #endregion Warning handling

            #region Message handling
            if (!_silent)
            {
                if ((MessageHost.MaximumInformation >= (int)Level) && (MessageHost.MinimumInformation <= (int)Level))
                {
                    if (!String.IsNullOrEmpty(Once))
                    {
                        string onceName = String.Format("MessageOnce.{0}.{1}", FunctionName, Once).ToLower();
                        if (!(Configuration.ConfigurationHost.Configurations.ContainsKey(onceName) && (bool)Configuration.ConfigurationHost.Configurations[onceName].Value))
                        {
                            InvokeCommand.InvokeScript(false, ScriptBlock.Create(_writeHostScript), null, _MessageHost);
                            channels = channels | LogEntryType.Information;

                            Configuration.Config cfg = new Configuration.Config();
                            cfg.Module = "messageonce";
                            cfg.Name = String.Format("{0}.{1}", FunctionName, Once).ToLower();
                            cfg.Hidden = true;
                            cfg.Description = "Locking setting that disables further display of the specified message";
                            cfg.Value = true;

                            Configuration.ConfigurationHost.Configurations[onceName] = cfg;
                        }
                    }
                    else
                    {
                        //InvokeCommand.InvokeScript(_writeHostScript, _MessageHost);
                        InvokeCommand.InvokeScript(false, ScriptBlock.Create(_writeHostScript), null, _MessageHost);
                        channels = channels | LogEntryType.Information;
                    }
                }
            }

            if ((MessageHost.MaximumVerbose >= (int)Level) && (MessageHost.MinimumVerbose <= (int)Level))
            {
                if ((_callStack.Count() > 1) && _callStack.ElementAt(_callStack.Count() - 2).InvocationInfo.BoundParameters.ContainsKey("Verbose"))
                    InvokeCommand.InvokeScript(@"$VerbosePreference = 'Continue'");
                //SessionState.PSVariable.Set("VerbosePreference", ActionPreference.Continue);

                WriteVerbose(_MessageStreams);
                channels = channels | LogEntryType.Verbose;
            }

            if ((MessageHost.MaximumDebug >= (int)Level) && (MessageHost.MinimumDebug <= (int)Level))
            {
                bool restoreInquire = false;
                if (_isDebug)
                {
                    if (Breakpoint.ToBool())
                        InvokeCommand.InvokeScript(false, ScriptBlock.Create(@"$DebugPreference = 'Inquire'"), null, null);
                    else
                    {
                        InvokeCommand.InvokeScript(false, ScriptBlock.Create(@"$DebugPreference = 'Continue'"), null, null);
                        restoreInquire = true;
                    }
                    WriteDebug(String.Format("{0} | {1}", Line, _MessageStreams));
                    channels = channels | LogEntryType.Debug;
                }
                else
                {
                    WriteDebug(_MessageStreams);
                    channels = channels | LogEntryType.Debug;
                }

                if (restoreInquire)
                    InvokeCommand.InvokeScript(false, ScriptBlock.Create(@"$DebugPreference = 'Inquire'"), null, null);
            }
            #endregion Message handling

            #region Logging
            LogEntry entry = LogHost.WriteLogEntry(_MessageSystem, channels, _timestamp, FunctionName, ModuleName, _Tags, Level, System.Management.Automation.Runspaces.Runspace.DefaultRunspace.InstanceId, Environment.MachineName, File, Line, _callStack, String.Format("{0}\\{1}", Environment.UserDomainName, Environment.UserName), Target);
            #endregion Logging

            foreach (MessageEventSubscription subscription in MessageHost.Events.Values)
                if (subscription.Applies(entry))
                {
                    try { InvokeCommand.InvokeScript(subscription.ScriptBlock.ToString(), entry); }
                    catch (Exception e) { WriteError(new ErrorRecord(e, "", ErrorCategory.NotSpecified, entry)); }
                }
        }
        #endregion Cmdlet Implementation

        #region Helper methods
        /// <summary>
        /// Processes the target transform rules on an input object
        /// </summary>
        /// <param name="Item">The item to transform</param>
        /// <returns>The transformed object</returns>
        private object ResolveTarget(object Item)
        {
            if (Item == null)
                return null;

            string lowTypeName = Item.GetType().FullName.ToLower();

            if (MessageHost.TargetTransforms.ContainsKey(lowTypeName))
            {
                try { return InvokeCommand.InvokeScript(false, ScriptBlock.Create(MessageHost.TargetTransforms[lowTypeName].ToString()), null, Item); }
                catch (Exception e)
                {
                    MessageHost.WriteTransformError(new ErrorRecord(e, "Write-Message", ErrorCategory.OperationStopped, null), FunctionName, ModuleName, Item, TransformType.Target, System.Management.Automation.Runspaces.Runspace.DefaultRunspace.InstanceId);
                    return Item;
                }
            }

            TransformCondition transform = MessageHost.TargetTransformlist.Get(lowTypeName, ModuleName, FunctionName);
            if (transform != null)
            {
                try { return InvokeCommand.InvokeScript(false, ScriptBlock.Create(transform.ScriptBlock.ToString()), null, Item); }
                catch (Exception e)
                {
                    MessageHost.WriteTransformError(new ErrorRecord(e, "Write-Message", ErrorCategory.OperationStopped, null), FunctionName, ModuleName, Item, TransformType.Target, System.Management.Automation.Runspaces.Runspace.DefaultRunspace.InstanceId);
                    return Item;
                }
            }

            return Item;
        }

        /// <summary>
        /// Processes the specified exception specified
        /// </summary>
        /// <param name="Item">The exception to process</param>
        /// <returns>The transformed exception</returns>
        private Exception ResolveException(Exception Item)
        {
            if (Item == null)
                return Item;

            string lowTypeName = Item.GetType().FullName.ToLower();

            if (MessageHost.ExceptionTransforms.ContainsKey(lowTypeName))
            {
                try { return (Exception)InvokeCommand.InvokeScript(false, ScriptBlock.Create(MessageHost.ExceptionTransforms[lowTypeName].ToString()), null, Item)[0].BaseObject; }
                catch (Exception e)
                {
                    MessageHost.WriteTransformError(new ErrorRecord(e, "Write-Message", ErrorCategory.OperationStopped, null), FunctionName, ModuleName, Item, TransformType.Exception, System.Management.Automation.Runspaces.Runspace.DefaultRunspace.InstanceId);
                    return Item;
                }
            }

            TransformCondition transform = MessageHost.ExceptionTransformList.Get(lowTypeName, ModuleName, FunctionName);
            if (transform != null)
            {
                try { return (Exception)InvokeCommand.InvokeScript(false, ScriptBlock.Create(transform.ScriptBlock.ToString()), null, Item)[0].BaseObject; }
                catch (Exception e)
                {
                    MessageHost.WriteTransformError(new ErrorRecord(e, "Write-Message", ErrorCategory.OperationStopped, null), FunctionName, ModuleName, Item, TransformType.Exception, System.Management.Automation.Runspaces.Runspace.DefaultRunspace.InstanceId);
                    return Item;
                }
            }

            return Item;
        }

        /// <summary>
        /// Processs the input level and apply policy and rules
        /// </summary>
        /// <param name="Level">The original level of the message</param>
        /// <returns>The processed level</returns>
        private MessageLevel ResolveLevel(MessageLevel Level)
        {
            int tempLevel = (int)Level;

            if (MessageHost.NestedLevelDecrement > 0)
            {
                int depth = _stackDepth - 2;
                if (_fromStopFunction)
                    depth--;
                tempLevel = tempLevel + depth * MessageHost.NestedLevelDecrement;
            }

            if (MessageHost.MessageLevelModifiers.Count > 0)
                foreach (MessageLevelModifier modifier in MessageHost.MessageLevelModifiers.Values)
                    if (modifier.AppliesTo(FunctionName, ModuleName, _Tags))
                        tempLevel = tempLevel + modifier.Modifier;

            if (tempLevel > 9)
                tempLevel = 9;
            if (tempLevel < 1)
                tempLevel = 1;

            return (MessageLevel)tempLevel;
        }

        /// <summary>
        /// Builds the message item for display of Verbose, Warning and Debug streams
        /// </summary>
        /// <returns>The message to return</returns>
        private string GetMessage()
        {
            if (!String.IsNullOrEmpty(_message))
                return _message;
            if (MessageHost.EnableMessageTimestamp && MessageHost.EnableMessageBreadcrumbs)
                _message = String.Format("[{0}]{1}{2}", _timestamp.ToString("HH:mm:ss"), _BreadCrumbsString, GetMessageSimple());
            else if (MessageHost.EnableMessageTimestamp && MessageHost.EnableMessageDisplayCommand)
                _message = String.Format("[{0}][{1}] {2}", _timestamp.ToString("HH:mm:ss"), FunctionName, GetMessageSimple());
            else if (MessageHost.EnableMessageTimestamp)
                _message = String.Format("[{0}] {1}", _timestamp.ToString("HH:mm:ss"), GetMessageSimple());
            else if (MessageHost.EnableMessageBreadcrumbs)
                _message = String.Format("{0}{1}", _BreadCrumbsString, GetMessageSimple());
            else if (MessageHost.EnableMessageDisplayCommand)
                _message = String.Format("[{0}] {1}", FunctionName, GetMessageSimple());
            else
                _message = GetMessageSimple();

            return _message;
        }

        /// <summary>
        /// Builds the base message for internal system use.
        /// </summary>
        /// <returns>The message to return</returns>
        private string GetMessageSimple()
        {
            if (!String.IsNullOrEmpty(_messageSimple))
                return _messageSimple;

            string baseMessage = _errorQualifiedMessage;
            foreach (Match match in Regex.Matches(baseMessage, "<c=[\"'](.*?)[\"']>(.*?)</c>"))
                baseMessage = Regex.Replace(baseMessage, Regex.Escape(match.Value), match.Groups[2].Value);
            _messageSimple = baseMessage;

            return _messageSimple;
        }

        /// <summary>
        /// Builds the message item if needed and returns it
        /// </summary>
        /// <returns>The message to return</returns>
        private string GetMessageColor()
        {
            if (!String.IsNullOrEmpty(_messageColor))
                return _messageColor;

            if (MessageHost.EnableMessageTimestamp && MessageHost.EnableMessageBreadcrumbs)
                _messageColor = String.Format("[<c='sub'>{0}</c>]{1} {2}", _timestamp.ToString("HH:mm:ss"), _BreadCrumbsStringColored, _errorQualifiedMessage);
            else if (MessageHost.EnableMessageTimestamp && MessageHost.EnableMessageDisplayCommand)
                _messageColor = String.Format("[<c='sub'>{0}</c>][<c='sub'>{1}</c>] {2}", _timestamp.ToString("HH:mm:ss"), FunctionName, _errorQualifiedMessage);
            else if (MessageHost.EnableMessageTimestamp)
                _messageColor = String.Format("[<c='sub'>{0}</c>] {1}", _timestamp.ToString("HH:mm:ss"), _errorQualifiedMessage);
            else if (MessageHost.EnableMessageBreadcrumbs)
                _messageColor = String.Format("{0}{1}", _BreadCrumbsStringColored, _errorQualifiedMessage);
            else if (MessageHost.EnableMessageDisplayCommand)
                _messageColor = String.Format("[<c='sub'>{0}</c>] {1}", FunctionName, _errorQualifiedMessage);
            else
                _messageColor = _errorQualifiedMessage;

            return _messageColor;
        }

        /// <summary>
        /// Non-host output in developermode
        /// </summary>
        /// <returns>The string to write on messages that don't go straight to Write-HostColor</returns>
        private string GetMessageDeveloper()
        {
            if (!String.IsNullOrEmpty(_messageDeveloper))
                return _messageDeveloper;

            string targetString = "";
            if (Target != null)
            {
                if (Target.ToString() != Target.GetType().FullName)
                    targetString = String.Format(" [T: {0}] ", Target.ToString());
                else
                    targetString = String.Format(" [T: <{0}>] ", Target.GetType().Name);
            }

            List<string> channelList = new List<string>();
            if (!_silent)
            {
                if (Level == MessageLevel.Warning)
                    channelList.Add("Warning");
                if ((MessageHost.MaximumInformation >= (int)Level) && (MessageHost.MinimumInformation <= (int)Level))
                    channelList.Add("Information");
            }
            if ((MessageHost.MaximumVerbose >= (int)Level) && (MessageHost.MinimumVerbose <= (int)Level))
                channelList.Add("Verbose");
            if ((MessageHost.MaximumDebug >= (int)Level) && (MessageHost.MinimumDebug <= (int)Level))
                channelList.Add("Debug");

            _messageDeveloper = String.Format(@"[{0}][{1}][L: {2}]{3}[C: {4}][EE: {5}][O: {6}]
    {7}", _timestamp.ToString("HH:mm:ss"), FunctionName, Level, targetString, String.Join(",", channelList), EnableException, (!String.IsNullOrEmpty(Once)), GetMessageSimple());

            return _messageDeveloper;
        }

        /// <summary>
        /// Host output in developermode
        /// </summary>
        /// <returns>The string to write on messages that go straight to Write-HostColor</returns>
        private string GetMessageDeveloperColor()
        {
            if (!String.IsNullOrEmpty(_messageDeveloperColor))
                return _messageDeveloperColor;

            string targetString = "";
            if (Target != null)
            {
                if (Target.ToString() != Target.GetType().FullName)
                    targetString = String.Format(" [<c='sub'>T:</c> <c='em'>{0}</c>] ", Target.ToString());
                else
                    targetString = String.Format(" [<c='sub'>T:</c> <c='em'><{0}></c>] ", Target.GetType().Name);
            }

            List<string> channelList = new List<string>();
            if (!_silent)
            {
                if (Level == MessageLevel.Warning)
                    channelList.Add("Warning");
                if ((MessageHost.MaximumInformation >= (int)Level) && (MessageHost.MinimumInformation <= (int)Level))
                    channelList.Add("Information");
            }
            if ((MessageHost.MaximumVerbose >= (int)Level) && (MessageHost.MinimumVerbose <= (int)Level))
                channelList.Add("Verbose");
            if ((MessageHost.MaximumDebug >= (int)Level) && (MessageHost.MinimumDebug <= (int)Level))
                channelList.Add("Debug");

            _messageDeveloperColor = String.Format(@"[<c='sub'>{0}</c>][<c='sub'>{1}</c>][<c='sub'>L:</c> <c='em'>{2}</c>]{3}[<c='sub'>C: <c='em'>{4}</c>][<c='sub'>EE: <c='em'>{5}</c>][<c='sub'>O: <c='em'>{6}</c>]
    {7}", _timestamp.ToString("HH:mm:ss"), FunctionName, Level, targetString, String.Join(",", channelList), EnableException, (!String.IsNullOrEmpty(Once)), _errorQualifiedMessage);

            return _messageDeveloperColor;
        }
        #endregion Helper methods
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Computer\DiskSpace.cs
using Sqlcollaborative.Dbatools.Utility;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Sqlcollaborative.Dbatools.Computer
{
    /// <summary>
    /// Data Container for the output of Get-DbaDiskSpace
    /// </summary>
    public class DiskSpace
    {
        /// <summary>
        /// The computer that was scanned
        /// </summary>
        public string ComputerName { get; set; }

        /// <summary>
        /// Name of the disk
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// Label of the disk
        /// </summary>
        public string Label { get; set; }

        /// <summary>
        /// What's the total capacity of the disk?
        /// </summary>
        public Size Capacity { get; set; }

        /// <summary>
        /// How much is still free?
        /// </summary>
        public Size Free { get; set; }

        /// <summary>
        /// How much is still free
        /// </summary>
        public double PercentFree
        {
            get
            {
                return Math.Round((double)((double)Free.Byte / (double)Capacity.Byte * 100), 2);
            }
        }

        /// <summary>
        /// What blocksize is the object set to
        /// </summary>
        public int BlockSize { get; set; }

        /// <summary>
        /// What filesystem is installed on the system
        /// </summary>
        public string FileSystem { get; set; }

        /// <summary>
        /// What kind of drive is it?
        /// </summary>
        public DriveType Type { get; set; }

        /// <summary>
        /// Whether the drive is a sql disk. Nullable, because it is an optional property and may not always be included, thus a third state is necessary.
        /// </summary>
        public Nullable<bool> IsSqlDisk { get; set; }

        #region Legacy Properties
        /// <summary>
        /// The computer that was scanned. Legacy-Name
        /// </summary>
        public string Server
        {
            get { return ComputerName; }
        }

        /// <summary>
        /// The type of drive this is in the legacy string notation
        /// </summary>
        public string DriveType
        {
            get
            {
                switch (Type)
                {
                    case Computer.DriveType.Unknown:
                        return "Unknown";
                    case Computer.DriveType.NoRootDirectory:
                        return "No Root Directory";
                    case Computer.DriveType.RemovableDisk:
                        return "Removable Disk";
                    case Computer.DriveType.LocalDisk:
                        return "Local Disk";
                    case Computer.DriveType.NetworkDrive:
                        return "Network Drive";
                    case Computer.DriveType.CompactDisk:
                        return "Compact Disk";
                    case Computer.DriveType.RAMDisk:
                        return "RAM Disk";
                    default:
                        return "Unknown";
                }
            }
        }

        /// <summary>
        /// The total capacity in Bytes
        /// </summary>
        public double SizeInBytes
        {
            get
            {
                return Capacity.Byte;
            }
        }

        /// <summary>
        /// The free space in Bytes
        /// </summary>
        public double FreeInBytes
        {
            get
            {
                return Free.Byte;
            }
        }

        /// <summary>
        /// The total capacity in KB
        /// </summary>
        public double SizeInKB
        {
            get
            {
                return Math.Round(Capacity.Kilobyte, 2);
            }
        }

        /// <summary>
        /// The free space in KB
        /// </summary>
        public double FreeInKB
        {
            get
            {
                return Math.Round(Free.Kilobyte, 2);
            }
        }

        /// <summary>
        /// The total capacity in MB
        /// </summary>
        public double SizeInMB
        {
            get
            {
                return Math.Round(Capacity.Megabyte, 2);
            }
        }

        /// <summary>
        /// The free space in MB
        /// </summary>
        public double FreeInMB
        {
            get
            {
                return Math.Round(Free.Megabyte, 2);
            }
        }

        /// <summary>
        /// The total capacity in GB
        /// </summary>
        public double SizeInGB
        {
            get
            {
                return Math.Round(Capacity.Gigabyte, 2);
            }
        }

        /// <summary>
        /// The free space in GB
        /// </summary>
        public double FreeInGB
        {
            get
            {
                return Math.Round(Free.Gigabyte, 2);
            }
        }

        /// <summary>
        /// The total capacity in TB
        /// </summary>
        public double SizeInTB
        {
            get
            {
                return Math.Round(Capacity.Terabyte, 2);
            }
        }

        /// <summary>
        /// The free space in TB
        /// </summary>
        public double FreeInTB
        {
            get
            {
                return Math.Round(Free.Terabyte, 2);
            }
        }

        /// <summary>
        /// The total capacity in PB
        /// </summary>
        public double SizeInPB
        {
            get
            {
                return Math.Round(Capacity.Terabyte / 1024, 2);
            }
        }

        /// <summary>
        /// The free space in PB
        /// </summary>
        public double FreeInPB
        {
            get
            {
                return Math.Round(Free.Terabyte / 1024, 2);
            }
        }
        #endregion Legacy Properties
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Computer\DriveType.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Sqlcollaborative.Dbatools.Computer
{
    /// <summary>
    /// What kind of drive are you?
    /// </summary>
    public enum DriveType
    {
        /// <summary>
        /// The drive type is not actually known
        /// </summary>
        Unknown = 0,

        /// <summary>
        /// The drive has no root directory
        /// </summary>
        NoRootDirectory = 1,

        /// <summary>
        /// The drive is a removable disk
        /// </summary>
        RemovableDisk = 2,

        /// <summary>
        /// The drive is a local disk
        /// </summary>
        LocalDisk = 3,

        /// <summary>
        /// The drive is a network drive
        /// </summary>
        NetworkDrive = 4,

        /// <summary>
        /// The drive is a compact disk
        /// </summary>
        CompactDisk = 5,

        /// <summary>
        /// The drive is a RAM disk
        /// </summary>
        RAMDisk = 6,
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Computer\PageFileSetting.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Sqlcollaborative.Dbatools.Computer
{
    /// <summary>
    /// Data container, listing pagefile settings.
    /// </summary>
    [Serializable]
    public class PageFileSetting
    {
        /// <summary>
        /// The name of the computer
        /// </summary>
        public string ComputerName;

        /// <summary>
        /// Whether Automatic PageFile management is enabled
        /// </summary>
        public bool AutoPageFile;

        /// <summary>
        /// The pagefile name
        /// </summary>
        public string FileName;

        /// <summary>
        /// The pagefile status
        /// </summary>
        public string Status;

        /// <summary>
        /// Whether the pagefile is system managed
        /// </summary>
        public Nullable<Boolean> SystemManaged;

        /// <summary>
        /// When were the settings last changed
        /// </summary>
        public Nullable<DateTime> LastModified;

        /// <summary>
        /// When were the settings last accessed
        /// </summary>
        public Nullable<DateTime> LastAccessed;

        /// <summary>
        /// The base allocated pagefile size in MB
        /// </summary>
        public Nullable<int> AllocatedBaseSize;

        /// <summary>
        /// The initial pagefile size in MB
        /// </summary>
        public Nullable<int> InitialSize;

        /// <summary>
        /// The maximum pagefile size in MB
        /// </summary>
        public Nullable<int> MaximumSize;

        /// <summary>
        /// The maximum percent of the pagefile limit that has been used
        /// </summary>
        public Nullable<int> PeakUsage;

        /// <summary>
        /// The currently used percentage of the pagefile limit that is in use.
        /// </summary>
        public Nullable<int> CurrentUsage;
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Configuration\Config.cs
using System;
using System.Management.Automation;

namespace Sqlcollaborative.Dbatools.Configuration
{
    /// <summary>
    /// Configuration Manager as well as individual configuration object.
    /// </summary>
    [Serializable]
    public class Config
    {
        /// <summary>
        /// The Name of the setting
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// The full name of the configuration entry, comprised of both Module and Name.
        /// </summary>
        public string FullName
        {
            get { return Module + "." + Name; }
        }

        /// <summary>
        /// The module of the setting. Helps being able to group configurations.
        /// </summary>
        public string Module { get; set; }

        /// <summary>
        /// A description of the specific setting
        /// </summary>
        public string Description { get; set; }

        /// <summary>
        /// The data type of the value stored in the configuration element.
        /// </summary>
        public string Type
        {
            get
            {
                if (Value == null)
                    return null;
                return Value.GetType().FullName;
            }
        }

        /// <summary>
        /// The value stored in the configuration element
        /// </summary>
        public Object Value { get; set; }

        /// <summary>
        /// The handler script that is run whenever the configuration value is set.
        /// </summary>
        public ScriptBlock Handler { get; set; }

        /// <summary>
        /// Validates the user input
        /// </summary>
        public ScriptBlock Validation { get; set; }

        /// <summary>
        /// Setting this to true will cause the element to not be discovered unless using the '-Force' parameter on "Get-DbaConfig"
        /// </summary>
        public bool Hidden { get; set; }

        /// <summary>
        /// Whether the setting has been initialized. This handles module imports and avoids modules overwriting settings when imported in multiple runspaces.
        /// </summary>
        public bool Initialized { get; set; }

        /// <summary>
        /// Whether this setting was set by policy
        /// </summary>
        public bool PolicySet { get; set; }

        /// <summary>
        /// Whether this setting was set by policy and forbids deletion.
        /// </summary>
        public bool PolicyEnforced
        {
            get { return _PolicyEnforced; }
            set
            {
                if (_PolicyEnforced == false) { _PolicyEnforced = value; }
            }
        }
        private bool _PolicyEnforced = false;

        /// <summary>
        /// The finalized value to put into the registry value when using policy to set this setting.
        /// </summary>
        public string RegistryData
        {
            get
            {
                switch (Type)
                {
                    case "System.Boolean":
                        if ((bool)Value)
                            return "bool:true";
                        return "bool:false";
                    case "System.Int16":
                        return String.Format("int:{0}", Value);
                    case "System.Int32":
                        return String.Format("int:{0}", Value);
                    case "System.Int64":
                        return String.Format("long:{0}", Value);
                    case "System.UInt16":
                        return String.Format("int:{0}", Value);
                    case "System.UInt32":
                        return String.Format("long:{0}", Value);
                    case "System.UInt64":
                        return String.Format("long:{0}", Value);
                    case "System.Double":
                        return String.Format("double:{0}", Value);
                    case "System.String":
                        return String.Format("string:{0}", Value);
                    case "System.TimeSpan":
                        return String.Format("timespan:{0}", ((TimeSpan)Value).Ticks);
                    case "System.DateTime":
                        return String.Format("datetime:{0}", ((DateTime)Value).Ticks);
                    case "System.ConsoleColor":
                        return String.Format("consolecolor:{0}", Value);
                    default:
                        return "<type not supported>";
                }
            }
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Configuration\ConfigScope.cs
using System;

namespace Sqlcollaborative.Dbatools.Configuration
{
    /// <summary>
    /// The location where a setting was applied
    /// </summary>
    [Flags]
    public enum ConfigScope
    {
        /// <summary>
        /// The configuration is set as default value for the user
        /// </summary>
        UserDefault = 1,

        /// <summary>
        /// The configuration is enforced for the user
        /// </summary>
        UserMandatory = 2,

        /// <summary>
        /// The configuration is set as default value for all users on the system
        /// </summary>
        SystemDefault = 4,

        /// <summary>
        /// The configuration is enforced for all users on the system.
        /// </summary>
        SystemMandatory = 8
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Configuration\ConfigurationHost.cs
using System.Collections.Generic;
using System.Management.Automation;

namespace Sqlcollaborative.Dbatools.Configuration
{
    /// <summary>
    /// Host class providing static configuration settings that are constant across all runspaces within the process.
    /// </summary>
    public static class ConfigurationHost
    {
        /// <summary>
        /// Hashtable containing all the configuration entries
        /// </summary>
        public static Dictionary<string, Config> Configurations = new Dictionary<string, Config>();

        /// <summary>
        /// Hashtable containing all the registered validations
        /// </summary>
        public static Dictionary<string, ScriptBlock> Validation = new Dictionary<string, ScriptBlock>();

        /// <summary>
        /// Whether the import from registry has been completed. Prevents multiple imports and overwrites when importing the module multiple times.
        /// </summary>
        public static bool ImportFromRegistryDone { get; set; }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Connection\ConnectionHost.cs
using System;
using System.Collections.Generic;
using System.Management.Automation.Runspaces;

namespace Sqlcollaborative.Dbatools.Connection
{
    /// <summary>
    /// Provides static tools for managing connections
    /// </summary>
    public static class ConnectionHost
    {
        /// <summary>
        /// List of all registered connections.
        /// </summary>
        public static Dictionary<string, ManagementConnection> Connections = new Dictionary<string, ManagementConnection>();

        #region Configuration Computer Management
        /// <summary>
        /// The time interval that must pass, before a connection using a known to not work connection protocol is reattempted
        /// </summary>
        public static TimeSpan BadConnectionTimeout = new TimeSpan(0, 15, 0);

        /// <summary>
        /// Globally disables all caching done by the Computer Management functions.
        /// </summary>
        public static bool DisableCache = false;

        /// <summary>
        /// Disables the caching of bad credentials. dbatools caches bad logon credentials for wmi/cim and will not reuse them.
        /// </summary>
        public static bool DisableBadCredentialCache = false;

        /// <summary>
        /// Disables the automatic registration of working credentials. dbatools will caches the last working credential when connecting using wmi/cim and will use those rather than using known bad credentials
        /// </summary>
        public static bool DisableCredentialAutoRegister = false;

        /// <summary>
        /// Enabling this will force the use of the last credentials known to work, rather than even trying explicit credentials.
        /// </summary>
        public static bool OverrideExplicitCredential = false;

        /// <summary>
        /// Enables automatic failover to working credentials, when passed credentials either are known, or turn out to not work.
        /// </summary>
        public static bool EnableCredentialFailover = false;

        /// <summary>
        /// Globally disables the persistence of Cim sessions used to connect to a target system.
        /// </summary>
        public static bool DisableCimPersistence = false;

        /// <summary>
        /// Whether the CM connection using Cim over WinRM is disabled globally
        /// </summary>
        public static bool DisableConnectionCimRM = false;

        /// <summary>
        /// Whether the CM connection using Cim over DCOM is disabled globally
        /// </summary>
        public static bool DisableConnectionCimDCOM = false;

        /// <summary>
        /// Whether the CM connection using WMI is disabled globally
        /// </summary>
        public static bool DisableConnectionWMI = true;

        /// <summary>
        /// Whether the CM connection using PowerShell Remoting is disabled globally
        /// </summary>
        public static bool DisableConnectionPowerShellRemoting = true;
        #endregion Configuration Computer Management

        #region Configuration Sql Connection
        /// <summary>
        /// The number of seconds before a sql connection attempt times out
        /// </summary>
        public static int SqlConnectionTimeout = 15;
        #endregion Configuration Sql Connection

        #region PowerShell remoting sessions
        /// <summary>
        /// List of all session containers used to maintain a cache
        /// </summary>
        public static Dictionary<Guid, PSSessionContainer> PSSessions = new Dictionary<Guid, PSSessionContainer>();

        #region Public operations
        /// <summary>
        /// Returns a registered session for a given computer on a given runspace. Returns null if nothing is registered.
        /// </summary>
        /// <param name="Runspace">The host runspace that opened the session</param>
        /// <param name="ComputerName">The computer connected to</param>
        /// <returns></returns>
        public static PSSession PSSessionGet(Guid Runspace, string ComputerName)
        {
            if (!PSSessions.ContainsKey(Runspace))
                return null;

            return PSSessions[Runspace].Get(ComputerName.ToLower());
        }

        /// <summary>
        /// Registeres a remote session under the owning runspace in its respective computer name
        /// </summary>
        /// <param name="Runspace">The runspace that owns the session</param>
        /// <param name="ComputerName">The computer the session connects to</param>
        /// <param name="Session">The session object</param>
        public static void PSSessionSet(Guid Runspace, string ComputerName, PSSession Session)
        {
            if (!PSSessionCacheEnabled)
                return;

            if (!PSSessions.ContainsKey(Runspace))
                PSSessions[Runspace] = new PSSessionContainer(Runspace);

            PSSessions[Runspace].Set(ComputerName.ToLower(), Session);
        }

        /// <summary>
        /// Searches the cache for an expired remoting session and purges it. After purging it from the list, it still needs to be closed!
        /// </summary>
        /// <returns>The session purged that then needs to be closed</returns>
        public static PSSession PSSessionPurgeExpired()
        {
            foreach (PSSessionContainer container in PSSessions.Values)
                if (container.CountExpired > 0)
                    return container.PurgeExpiredSession();

            return null;
        }

        /// <summary>
        /// The number of expired sessions 
        /// </summary>
        public static int PSSessionCountExpired
        {
            get
            {
                int num = 0;

                foreach (PSSessionContainer container in PSSessions.Values)
                    num += container.CountExpired;

                return num;
            }
        }
        #endregion Public operations

        #region Configuration
        /// <summary>
        /// The time until established connections will be considered expired (if available)
        /// </summary>
        public static TimeSpan PSSessionTimeout = new TimeSpan(0, 5, 0);

        /// <summary>
        /// Whether sessions should be cached at all
        /// </summary>
        public static bool PSSessionCacheEnabled = true;
        #endregion Configuration
        #endregion PowerShell remoting sessions
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Connection\ManagementConnection.cs
using System;
using System.Collections.Generic;
using System.Management.Automation;
using Microsoft.Management.Infrastructure;
using Microsoft.Management.Infrastructure.Options;

namespace Sqlcollaborative.Dbatools.Connection
{
    /// <summary>
    /// Contains management connection information for a windows server
    /// </summary>
    [Serializable]
    public class ManagementConnection
    {
        /// <summary>
        /// The computer to connect to
        /// </summary>
        public string ComputerName { get; set; }

        #region Configuration

        /// <summary>
        /// Locally disables the caching of bad credentials
        /// </summary>
        public bool DisableBadCredentialCache
        {
            get
            {
                switch (_disableBadCredentialCache)
                {
                    case -1:
                        return false;
                    case 1:
                        return true;
                    default:
                        return ConnectionHost.DisableBadCredentialCache;
                }
            }
            set {
                _disableBadCredentialCache = value ? 1 : -1;
            }
        }

        private int _disableBadCredentialCache;

        /// <summary>
        /// Locally disables the caching of working credentials
        /// </summary>
        public bool DisableCredentialAutoRegister
        {
            get
            {
                switch (_disableCredentialAutoRegister)
                {
                    case -1:
                        return false;
                    case 1:
                        return true;
                    default:
                        return ConnectionHost.DisableCredentialAutoRegister;
                }
            }
            set
            {
                _disableCredentialAutoRegister = value ? 1 : -1;
            }
        }

        private int _disableCredentialAutoRegister;

        /// <summary>
        /// Locally overrides explicit credentials with working ones that were cached
        /// </summary>
        public bool OverrideExplicitCredential
        {
            get
            {
                switch (_overrideExplicitCredential)
                {
                    case -1:
                        return false;
                    case 1:
                        return true;
                    default:
                        return ConnectionHost.OverrideExplicitCredential;
                }
            }
            set
            {
                _overrideExplicitCredential = value ? 1 : -1;
                
            }
        }

        private int _overrideExplicitCredential;

        /// <summary>
        /// Locally enables automatic failover to working credentials, when passed credentials either are known, or turn out to not work.
        /// </summary>
        public bool EnableCredentialFailover
        {
            get
            {
                switch (_enableCredentialFailover)
                {
                    case -1:
                        return false;
                    case 1:
                        return true;
                    default:
                        return ConnectionHost.EnableCredentialFailover;
                }
            }
            set
            {
                _enableCredentialFailover = value ? 1 : -1;
            }
        }

        private int _enableCredentialFailover;

        /// <summary>
        /// Locally disables the persistence of Cim sessions used to connect to a target system.
        /// </summary>
        public bool DisableCimPersistence
        {
            get
            {
                switch (_disableCimPersistence)
                {
                    case -1:
                        return false;
                    case 1:
                        return true;
                    default:
                        return ConnectionHost.DisableCimPersistence;
                }
            }
            set
            {
                _disableCimPersistence = value ? 1 : -1;
            }
        }

        private int _disableCimPersistence;

        /// <summary>
        /// Connectiontypes that will never be used
        /// </summary>
        public ManagementConnectionType DisabledConnectionTypes
        {
            get
            {
                ManagementConnectionType temp = ManagementConnectionType.None;
                if (CimRM == ManagementConnectionProtocolState.Disabled)
                {
                    temp = temp | ManagementConnectionType.CimRM;
                }
                if (CimDCOM == ManagementConnectionProtocolState.Disabled)
                {
                    temp = temp | ManagementConnectionType.CimDCOM;
                }
                if (Wmi == ManagementConnectionProtocolState.Disabled)
                {
                    temp = temp | ManagementConnectionType.Wmi;
                }
                if (PowerShellRemoting == ManagementConnectionProtocolState.Disabled)
                {
                    temp = temp | ManagementConnectionType.PowerShellRemoting;
                }
                return temp;
            }
            set
            {
                if ((value & ManagementConnectionType.CimRM) != 0)
                {
                    CimRM = ManagementConnectionProtocolState.Disabled;
                }
                else if ((CimRM & ManagementConnectionProtocolState.Disabled) != 0)
                {
                    CimRM = ManagementConnectionProtocolState.Unknown;
                }
                if ((value & ManagementConnectionType.CimDCOM) != 0)
                {
                    CimDCOM = ManagementConnectionProtocolState.Disabled;
                }
                else if ((CimDCOM & ManagementConnectionProtocolState.Disabled) != 0)
                {
                    CimDCOM = ManagementConnectionProtocolState.Unknown;
                }
                if ((value & ManagementConnectionType.Wmi) != 0)
                {
                    Wmi = ManagementConnectionProtocolState.Disabled;
                }
                else if ((Wmi & ManagementConnectionProtocolState.Disabled) != 0)
                {
                    Wmi = ManagementConnectionProtocolState.Unknown;
                }
                if ((value & ManagementConnectionType.PowerShellRemoting) != 0)
                {
                    PowerShellRemoting = ManagementConnectionProtocolState.Disabled;
                }
                else if ((PowerShellRemoting & ManagementConnectionProtocolState.Disabled) != 0)
                {
                    PowerShellRemoting = ManagementConnectionProtocolState.Unknown;
                }
            }
        }

        /// <summary>
        /// Restores all deviations from public policy back to default
        /// </summary>
        public void RestoreDefaultConfiguration()
        {
            _disableBadCredentialCache = 0;
            _disableCredentialAutoRegister = 0;
            _overrideExplicitCredential = 0;
            _disableCimPersistence = 0;
            _enableCredentialFailover = 0;
            OverrideConnectionPolicy = false;
        }

        #endregion Configuration

        #region Connection Stats
        /// <summary>
        /// Whether this connection adhers to the global connection lockdowns or not
        /// </summary>
        public bool OverrideConnectionPolicy = false;

        /// <summary>
        /// Did the last connection attempt using CimRM work?
        /// </summary>
        public ManagementConnectionProtocolState CimRM
        {
            get
            {
                if (!OverrideConnectionPolicy && ConnectionHost.DisableConnectionCimRM)
                    return ManagementConnectionProtocolState.Disabled;
                else
                    return _CimRM;
            }
            set { _CimRM = value; }
        }
        private ManagementConnectionProtocolState _CimRM = ManagementConnectionProtocolState.Unknown;

        /// <summary>
        /// When was the last connection attempt using CimRM?
        /// </summary>
        public DateTime LastCimRM;

        /// <summary>
        /// Did the last connection attempt using CimDCOM work?
        /// </summary>
        public ManagementConnectionProtocolState CimDCOM
        {
            get
            {
                if (!OverrideConnectionPolicy && ConnectionHost.DisableConnectionCimDCOM)
                    return ManagementConnectionProtocolState.Disabled;
                else
                    return _CimDCOM;
            }
            set { _CimDCOM = value; }
        }
        private ManagementConnectionProtocolState _CimDCOM = ManagementConnectionProtocolState.Unknown;

        /// <summary>
        /// When was the last connection attempt using CimRM?
        /// </summary>
        public DateTime LastCimDCOM;

        /// <summary>
        /// Did the last connection attempt using Wmi work?
        /// </summary>
        public ManagementConnectionProtocolState Wmi
        {
            get
            {
                if (!OverrideConnectionPolicy && ConnectionHost.DisableConnectionWMI)
                    return ManagementConnectionProtocolState.Disabled;
                else
                    return _Wmi;
            }
            set { _Wmi = value; }
        }
        private ManagementConnectionProtocolState _Wmi = ManagementConnectionProtocolState.Unknown;

        /// <summary>
        /// When was the last connection attempt using CimRM?
        /// </summary>
        public DateTime LastWmi;

        /// <summary>
        /// Did the last connection attempt using PowerShellRemoting work?
        /// </summary>
        public ManagementConnectionProtocolState PowerShellRemoting
        {
            get
            {
                if (!OverrideConnectionPolicy && ConnectionHost.DisableConnectionPowerShellRemoting)
                    return ManagementConnectionProtocolState.Disabled;
                else
                    return _PowerShellRemoting;
            }
            set { _PowerShellRemoting = value; }
        }
        private ManagementConnectionProtocolState _PowerShellRemoting = ManagementConnectionProtocolState.Unknown;

        /// <summary>
        /// When was the last connection attempt using CimRM?
        /// </summary>
        public DateTime LastPowerShellRemoting;

        /// <summary>
        /// Report the successful connection against the computer of this connection
        /// </summary>
        /// <param name="Type">What connection type succeeded?</param>
        public void ReportSuccess(ManagementConnectionType Type)
        {
            switch (Type)
            {
                case ManagementConnectionType.CimRM:
                    CimRM = ManagementConnectionProtocolState.Success;
                    LastCimRM = DateTime.Now;
                    break;

                case ManagementConnectionType.CimDCOM:
                    CimDCOM = ManagementConnectionProtocolState.Success;
                    LastCimDCOM = DateTime.Now;
                    break;

                case ManagementConnectionType.Wmi:
                    Wmi = ManagementConnectionProtocolState.Success;
                    LastWmi = DateTime.Now;
                    break;

                case ManagementConnectionType.PowerShellRemoting:
                    PowerShellRemoting = ManagementConnectionProtocolState.Success;
                    LastPowerShellRemoting = DateTime.Now;
                    break;
            }
        }

        /// <summary>
        /// Report the failure of connecting to the target computer
        /// </summary>
        /// <param name="Type">What connection type failed?</param>
        public void ReportFailure(ManagementConnectionType Type)
        {
            switch (Type)
            {
                case ManagementConnectionType.CimRM:
                    CimRM = ManagementConnectionProtocolState.Error;
                    LastCimRM = DateTime.Now;
                    break;

                case ManagementConnectionType.CimDCOM:
                    CimDCOM = ManagementConnectionProtocolState.Error;
                    LastCimDCOM = DateTime.Now;
                    break;

                case ManagementConnectionType.Wmi:
                    Wmi = ManagementConnectionProtocolState.Error;
                    LastWmi = DateTime.Now;
                    break;

                case ManagementConnectionType.PowerShellRemoting:
                    PowerShellRemoting = ManagementConnectionProtocolState.Error;
                    LastPowerShellRemoting = DateTime.Now;
                    break;
            }
        }

        #endregion Connection Stats

        #region Credential Management

        /// <summary>
        /// Any registered credentials to use on the connection.
        /// </summary>
        public PSCredential Credentials;

        /// <summary>
        /// Whether the default windows credentials are known to not work against the target.
        /// </summary>
        public bool WindowsCredentialsAreBad;

        /// <summary>
        /// Whether windows credentials are known to be good. Do not build conditions on them being false, just on true.
        /// </summary>
        public bool UseWindowsCredentials;

        /// <summary>
        /// Credentials known to not work. They will not be used when specified.
        /// </summary>
        public List<PSCredential> KnownBadCredentials = new List<PSCredential>();

        /// <summary>
        /// Adds a credentials object to the list of credentials known to not work.
        /// </summary>
        /// <param name="Credential">The bad credential that must be punished</param>
        public void AddBadCredential(PSCredential Credential)
        {
            if (DisableBadCredentialCache)
                return;

            if (Credential == null)
            {
                WindowsCredentialsAreBad = true;
                UseWindowsCredentials = false;
                return;
            }

            // If previously good credentials have been revoked, better remove them from the list
            if ((Credentials != null) && (Credentials.UserName.ToLower() == Credential.UserName.ToLower()))
            {
                if (Credentials.GetNetworkCredential().Password == Credential.GetNetworkCredential().Password)
                    Credentials = null;
            }

            foreach (PSCredential cred in KnownBadCredentials)
            {
                if (cred.UserName.ToLower() == Credential.UserName.ToLower())
                {
                    if (cred.GetNetworkCredential().Password == Credential.GetNetworkCredential().Password)
                        return;
                }
            }
            KnownBadCredentials.Add(Credential);
        }

        /// <summary>
        /// Reports a credentials object as being legit.
        /// </summary>
        /// <param name="Credential">The functioning credential that we may want to use again</param>
        public void AddGoodCredential(PSCredential Credential)
        {
            if (!DisableCredentialAutoRegister)
            {
                Credentials = Credential;
                if (Credential == null)
                {
                    UseWindowsCredentials = true;
                }
            }
        }

        /// <summary>
        /// Calculates, which credentials to use. Will consider input, compare it with know not-working credentials or use the configured working credentials for that.
        /// </summary>
        /// <param name="Credential">Any credential object a user may have explicitly specified.</param>
        /// <returns>The Credentials to use</returns>
        public PSCredential GetCredential(PSCredential Credential)
        {
            // If nothing was bound, return whatever is available
            // If something was bound, however explicit override is in effect AND either we have a good credential OR know Windows Credentials are good to use, use the cached credential
            // Without the additional logic conditions, OverrideExplicitCredential would override all input, even if we haven't found a working credential yet.
            if (OverrideExplicitCredential && (UseWindowsCredentials || (Credentials != null)))
            {
                return Credentials;
            }

            // Handle Windows authentication
            if (Credential == null)
            {
                if (WindowsCredentialsAreBad)
                {
                    if (EnableCredentialFailover && (Credentials != null))
                        return Credentials;
                    throw new PSArgumentException("Windows authentication was used, but is known to not work!",
                        "Credential");
                }
                return null;
            }

            // Compare with bad credential cache
            if (!DisableBadCredentialCache)
            {
                foreach (PSCredential cred in KnownBadCredentials)
                {
                    if (cred.UserName.ToLower() == Credential.UserName.ToLower())
                    {
                        if (cred.GetNetworkCredential().Password == Credential.GetNetworkCredential().Password)
                        {
                            if (EnableCredentialFailover)
                            {
                                if ((Credentials != null) || !WindowsCredentialsAreBad)
                                    return Credentials;
                                throw new PSArgumentException(
                                    "Specified credentials are known to not work! Credential failover is enabled but there are no known working credentials.",
                                    "Credential");
                            }
                            throw new PSArgumentException("Specified credentials are known to not work!",
                                "Credential");
                        }
                    }
                }
            }

            // Return unknown credential, so it may be tried out
            return Credential;
        }

        /// <summary>
        /// Tests whether the input credential is on the list known, bad credentials
        /// </summary>
        /// <param name="Credential">The credential to test</param>
        /// <returns>True if the credential is known to not work, False if it is not yet known to not work</returns>
        public bool IsBadCredential(PSCredential Credential)
        {
            if (Credential == null)
            {
                return WindowsCredentialsAreBad;
            }

            foreach (PSCredential cred in KnownBadCredentials)
            {
                if (cred.UserName.ToLower() == Credential.UserName.ToLower())
                {
                    if (cred.GetNetworkCredential().Password == Credential.GetNetworkCredential().Password)
                        return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Removes an item from the list of known bad credentials
        /// </summary>
        /// <param name="Credential">The credential to remove</param>
        public void RemoveBadCredential(PSCredential Credential)
        {
            if (Credential == null)
            {
                return;
            }

            foreach (PSCredential cred in KnownBadCredentials)
            {
                if (cred.UserName.ToLower() == Credential.UserName.ToLower())
                {
                    if (cred.GetNetworkCredential().Password == Credential.GetNetworkCredential().Password)
                    {
                        KnownBadCredentials.Remove(cred);
                    }
                }
            }
        }

        #endregion Credential Management

        #region Connection Types

        /// <summary>
        /// Returns the next connection type to try.
        /// </summary>
        /// <param name="ExcludedTypes">Exclude any type already tried and failed</param>
        /// <param name="Force">Overrides the timeout on bad connections</param>
        /// <returns>The next type to try.</returns>
        public ManagementConnectionType GetConnectionType(ManagementConnectionType ExcludedTypes, bool Force)
        {
            ManagementConnectionType temp = ExcludedTypes | DisabledConnectionTypes;

            #region Use working connections first

            if (((ManagementConnectionType.CimRM & temp) == 0) &&
                ((CimRM & ManagementConnectionProtocolState.Success) != 0))
                return ManagementConnectionType.CimRM;

            if (((ManagementConnectionType.CimDCOM & temp) == 0) &&
                ((CimDCOM & ManagementConnectionProtocolState.Success) != 0))
                return ManagementConnectionType.CimDCOM;

            if (((ManagementConnectionType.Wmi & temp) == 0) && ((Wmi & ManagementConnectionProtocolState.Success) != 0))
                return ManagementConnectionType.Wmi;

            if (((ManagementConnectionType.PowerShellRemoting & temp) == 0) &&
                ((PowerShellRemoting & ManagementConnectionProtocolState.Success) != 0))
                return ManagementConnectionType.PowerShellRemoting;

            #endregion Use working connections first

            #region Then prefer unknown connections

            if (((ManagementConnectionType.CimRM & temp) == 0) &&
                ((CimRM & ManagementConnectionProtocolState.Unknown) != 0))
                return ManagementConnectionType.CimRM;

            if (((ManagementConnectionType.CimDCOM & temp) == 0) &&
                ((CimDCOM & ManagementConnectionProtocolState.Unknown) != 0))
                return ManagementConnectionType.CimDCOM;

            if (((ManagementConnectionType.Wmi & temp) == 0) && ((Wmi & ManagementConnectionProtocolState.Unknown) != 0))
                return ManagementConnectionType.Wmi;

            if (((ManagementConnectionType.PowerShellRemoting & temp) == 0) &&
                ((PowerShellRemoting & ManagementConnectionProtocolState.Unknown) != 0))
                return ManagementConnectionType.PowerShellRemoting;

            #endregion Then prefer unknown connections

            #region Finally try what would not work previously

            if (((ManagementConnectionType.CimRM & temp) == 0) &&
                ((CimRM & ManagementConnectionProtocolState.Error) != 0) &&
                ((LastCimRM + ConnectionHost.BadConnectionTimeout < DateTime.Now) | Force))
                return ManagementConnectionType.CimRM;

            if (((ManagementConnectionType.CimDCOM & temp) == 0) &&
                ((CimDCOM & ManagementConnectionProtocolState.Error) != 0) &&
                ((LastCimDCOM + ConnectionHost.BadConnectionTimeout < DateTime.Now) | Force))
                return ManagementConnectionType.CimDCOM;

            if (((ManagementConnectionType.Wmi & temp) == 0) && ((Wmi & ManagementConnectionProtocolState.Error) != 0) &&
                ((LastWmi + ConnectionHost.BadConnectionTimeout < DateTime.Now) | Force))
                return ManagementConnectionType.Wmi;

            if (((ManagementConnectionType.PowerShellRemoting & temp) == 0) &&
                ((PowerShellRemoting & ManagementConnectionProtocolState.Error) != 0) &&
                ((LastPowerShellRemoting + ConnectionHost.BadConnectionTimeout < DateTime.Now) | Force))
                return ManagementConnectionType.PowerShellRemoting;

            #endregion Finally try what would not work previously

            // Do not try to use disabled protocols

            throw new PSInvalidOperationException("No connectiontypes left to try.");
        }

        /// <summary>
        /// Returns a list of all available connection types whose inherent timeout has expired.
        /// </summary>
        /// <param name="Timestamp">All last connection failures older than this point in time are considered to be expired</param>
        /// <returns>A list of all valid connection types</returns>
        public List<ManagementConnectionType> GetConnectionTypesTimed(DateTime Timestamp)
        {
            List<ManagementConnectionType> types = new List<ManagementConnectionType>();

            if (((DisabledConnectionTypes & ManagementConnectionType.CimRM) == 0) &&
                ((CimRM == ManagementConnectionProtocolState.Success) || (LastCimRM < Timestamp)))
                types.Add(ManagementConnectionType.CimRM);

            if (((DisabledConnectionTypes & ManagementConnectionType.CimDCOM) == 0) &&
                ((CimDCOM == ManagementConnectionProtocolState.Success) || (LastCimDCOM < Timestamp)))
                types.Add(ManagementConnectionType.CimDCOM);

            if (((DisabledConnectionTypes & ManagementConnectionType.Wmi) == 0) &&
                ((Wmi == ManagementConnectionProtocolState.Success) || (LastWmi < Timestamp)))
                types.Add(ManagementConnectionType.Wmi);

            if (((DisabledConnectionTypes & ManagementConnectionType.PowerShellRemoting) == 0) &&
                ((PowerShellRemoting == ManagementConnectionProtocolState.Success) ||
                 (LastPowerShellRemoting < Timestamp)))
                types.Add(ManagementConnectionType.PowerShellRemoting);

            return types;
        }

        /// <summary>
        /// Returns a list of all available connection types whose inherent timeout has expired.
        /// </summary>
        /// <param name="Timespan">All last connection failures older than this far back into the past are considered to be expired</param>
        /// <returns>A list of all valid connection types</returns>
        public List<ManagementConnectionType> GetConnectionTypesTimed(TimeSpan Timespan)
        {
            return GetConnectionTypesTimed(DateTime.Now - Timespan);
        }

        #endregion Connection Types

        #region Internals

        internal void CopyTo(ManagementConnection Connection)
        {
            Connection.ComputerName = ComputerName;

            Connection.CimRM = CimRM;
            Connection.LastCimRM = LastCimRM;
            Connection.CimDCOM = CimDCOM;
            Connection.LastCimDCOM = LastCimDCOM;
            Connection.Wmi = Wmi;
            Connection.LastWmi = LastWmi;
            Connection.PowerShellRemoting = PowerShellRemoting;
            Connection.LastPowerShellRemoting = LastPowerShellRemoting;

            Connection.Credentials = Credentials;
            Connection.OverrideExplicitCredential = OverrideExplicitCredential;
            Connection.KnownBadCredentials = KnownBadCredentials;
            Connection.WindowsCredentialsAreBad = WindowsCredentialsAreBad;
        }

        #endregion Internals

        #region Constructors

        /// <summary>
        /// Creates a new, empty connection object. Necessary for serialization.
        /// </summary>
        public ManagementConnection()
        {

        }

        /// <summary>
        /// Creates a new default connection object, containing only its computer's name and default results.
        /// </summary>
        /// <param name="ComputerName">The computer targeted. Will be forced to lowercase.</param>
        public ManagementConnection(string ComputerName)
        {
            this.ComputerName = ComputerName.ToLower();
            if (Utility.Validation.IsLocalhost(ComputerName))
                CimRM = ManagementConnectionProtocolState.Disabled;
        }

        #endregion Constructors

        #region CIM Execution

        #region WinRM

        /// <summary>
        /// The options ot use when establishing a CIM Session
        /// </summary>
        public WSManSessionOptions CimWinRMOptions
        {
            get
            {
                if (_CimWinRMOptions == null)
                {
                    return null;
                }
                return new WSManSessionOptions(_CimWinRMOptions);
            }
            set
            {
                cimWinRMSession = null;
                _CimWinRMOptions = value;
            }
        }

        private WSManSessionOptions _CimWinRMOptions;

        private CimSession cimWinRMSession;
        private PSCredential cimWinRMSessionLastCredential;

        private CimSession GetCimWinRMSession(PSCredential Credential)
        {
            // Prepare the last session if any
            CimSession tempSession = cimWinRMSession;

            // If we use different credentials than last time, now's the time to interrupt
            if (!(cimWinRMSessionLastCredential == null && Credential == null))
            {
                if (cimWinRMSessionLastCredential == null || Credential == null)
                    tempSession = null;
                else if (cimWinRMSessionLastCredential.UserName != Credential.UserName)
                    tempSession = null;
                else if (cimWinRMSessionLastCredential.GetNetworkCredential().Password !=
                         Credential.GetNetworkCredential().Password)
                    tempSession = null;
            }

            if (tempSession == null)
            {
                WSManSessionOptions options;
                if (CimWinRMOptions == null)
                {
                    options = GetDefaultCimWsmanOptions();
                }
                else
                {
                    options = CimWinRMOptions;
                }
                if (Credential != null)
                {
                    options.AddDestinationCredentials(new CimCredential(PasswordAuthenticationMechanism.Default,
                        Credential.GetNetworkCredential().Domain, Credential.GetNetworkCredential().UserName,
                        Credential.Password));
                }

                try
                {
                    tempSession = CimSession.Create(ComputerName, options);
                }
                catch (Exception e)
                {
                    bool testBadCredential = false;
                    try
                    {
                        string tempMessageId = ((CimException) (e.InnerException)).MessageId;
                        if (tempMessageId == "HRESULT 0x8007052e")
                            testBadCredential = true;
                        else if (tempMessageId == "HRESULT 0x80070005")
                            testBadCredential = true;
                    }
                    catch
                    {
                    }

                    if (testBadCredential)
                    {
                        throw new UnauthorizedAccessException("Invalid credentials!", e);
                    }
                    throw;
                }

                cimWinRMSessionLastCredential = Credential;
            }

            return tempSession;
        }

        /// <summary>
        /// Returns the default wsman options object
        /// </summary>
        /// <returns>Something very default-y</returns>
        private WSManSessionOptions GetDefaultCimWsmanOptions()
        {
            WSManSessionOptions options = new WSManSessionOptions();
            options.DestinationPort = 0;
            options.MaxEnvelopeSize = 0;
            options.CertCACheck = true;
            options.CertCNCheck = true;
            options.CertRevocationCheck = true;
            options.UseSsl = false;
            options.PacketEncoding = PacketEncoding.Utf8;
            options.NoEncryption = false;
            options.EncodePortInServicePrincipalName = false;

            return options;
        }

        /// <summary>
        /// Get all cim instances of the appropriate class using WinRM
        /// </summary>
        /// <param name="Credential">The credentiuls to use for the connection.</param>
        /// <param name="Class">The class to query.</param>
        /// <param name="Namespace">The namespace to look in (defaults to root\cimv2).</param>
        /// <returns>Hopefully a mountainload of CimInstances</returns>
        public object GetCimRMInstance(PSCredential Credential, string Class, string Namespace = @"root\cimv2")
        {
            CimSession tempSession;
            IEnumerable<CimInstance> result;

            tempSession = GetCimWinRMSession(Credential);
            result = tempSession.EnumerateInstances(Namespace, Class);
            
            if (DisableCimPersistence)
            {
                try
                {
                    tempSession.Close();
                }
                catch
                {
                }
                cimWinRMSession = null;
            }
            else
            {
                cimWinRMSession = tempSession;
            }
            return result;
        }

        /// <summary>
        /// Get all cim instances matching the query using WinRM
        /// </summary>
        /// <param name="Credential">The credentiuls to use for the connection.</param>
        /// <param name="Query">The query to use requesting information.</param>
        /// <param name="Dialect">Defaults to WQL.</param>
        /// <param name="Namespace">The namespace to look in (defaults to root\cimv2).</param>
        /// <returns></returns>
        public object QueryCimRMInstance(PSCredential Credential, string Query, string Dialect = "WQL",
            string Namespace = @"root\cimv2")
        {
            CimSession tempSession;
            IEnumerable<CimInstance> result = new List<CimInstance>();

            try
            {
                tempSession = GetCimWinRMSession(Credential);
                result = tempSession.QueryInstances(Namespace, Dialect, Query);
                result.GetEnumerator().MoveNext();
            }
            catch (Exception e)
            {
                bool testBadCredential = false;
                try
                {
                    string tempMessageId = ((CimException) e).MessageId;
                    if (tempMessageId == "HRESULT 0x8007052e")
                        testBadCredential = true;
                    else if (tempMessageId == "HRESULT 0x80070005")
                        testBadCredential = true;
                }
                catch
                {
                }

                if (testBadCredential)
                {
                    throw new UnauthorizedAccessException("Invalid credentials!", e);
                }
                throw;
            }

            if (DisableCimPersistence)
            {
                try
                {
                    tempSession.Close();
                }
                catch
                {
                }
                cimWinRMSession = null;
            }
            else
            {
                if (cimWinRMSession != tempSession)
                    cimWinRMSession = tempSession;
            }
            return result;
        }

        #endregion WinRM

        #region DCOM

        /// <summary>
        /// The options ot use when establishing a CIM Session
        /// </summary>
        public DComSessionOptions CimDComOptions
        {
            get
            {
                if (_CimDComOptions == null)
                {
                    return null;
                }
                DComSessionOptions options = new DComSessionOptions();
                options.PacketPrivacy = _CimDComOptions.PacketPrivacy;
                options.PacketIntegrity = _CimDComOptions.PacketIntegrity;
                options.Impersonation = _CimDComOptions.Impersonation;
                return options;
            }
            set
            {
                _CimDComOptions = null;
                _CimDComOptions = value;
            }
        }

        private DComSessionOptions _CimDComOptions;

        private CimSession cimDComSession;
        private PSCredential cimDComSessionLastCredential;

        private CimSession GetCimDComSession(PSCredential Credential)
        {
            // Prepare the last session if any
            CimSession tempSession = cimDComSession;

            // If we use different credentials than last time, now's the time to interrupt
            if (!(cimDComSessionLastCredential == null && Credential == null))
            {
                if (cimDComSessionLastCredential == null || Credential == null)
                    tempSession = null;
                else if (cimDComSessionLastCredential.UserName != Credential.UserName)
                    tempSession = null;
                else if (cimDComSessionLastCredential.GetNetworkCredential().Password !=
                         Credential.GetNetworkCredential().Password)
                    tempSession = null;
            }

            if (tempSession == null)
            {
                DComSessionOptions options = null;
                if (CimWinRMOptions == null)
                {
                    options = GetDefaultCimDcomOptions();
                }
                else
                {
                    options = CimDComOptions;
                }
                if (Credential != null)
                {
                    options.AddDestinationCredentials(new CimCredential(PasswordAuthenticationMechanism.Default,
                        Credential.GetNetworkCredential().Domain, Credential.GetNetworkCredential().UserName,
                        Credential.Password));
                }

                try
                {
                    tempSession = CimSession.Create(ComputerName, options);
                }
                catch (Exception e)
                {
                    bool testBadCredential = false;
                    try
                    {
                        string tempMessageId = ((CimException) (e.InnerException)).MessageId;
                        if (tempMessageId == "HRESULT 0x8007052e")
                            testBadCredential = true;
                        else if (tempMessageId == "HRESULT 0x80070005")
                            testBadCredential = true;
                    }
                    catch
                    {
                    }

                    if (testBadCredential)
                    {
                        throw new UnauthorizedAccessException("Invalid credentials!", e);
                    }
                    throw;
                }

                cimDComSessionLastCredential = Credential;
            }

            return tempSession;
        }

        /// <summary>
        /// Returns the default DCom options object
        /// </summary>
        /// <returns>Something very default-y</returns>
        private DComSessionOptions GetDefaultCimDcomOptions()
        {
            DComSessionOptions options = new DComSessionOptions();
            options.PacketPrivacy = true;
            options.PacketIntegrity = true;
            options.Impersonation = ImpersonationType.Impersonate;

            return options;
        }

        /// <summary>
        /// Get all cim instances of the appropriate class using DCOM
        /// </summary>
        /// <param name="Credential">The credentiuls to use for the connection.</param>
        /// <param name="Class">The class to query</param>
        /// <param name="Namespace">The namespace to look in (defaults to root\cimv2)</param>
        /// <returns>Hopefully a mountainload of CimInstances</returns>
        public object GetCimDComInstance(PSCredential Credential, string Class, string Namespace = @"root\cimv2")
        {
            CimSession tempSession;
            IEnumerable<CimInstance> result = new List<CimInstance>();

            tempSession = GetCimDComSession(Credential);
            result = tempSession.EnumerateInstances(Namespace, Class);

            if (DisableCimPersistence)
            {
                try
                {
                    tempSession.Close();
                }
                catch
                {
                }
                cimDComSession = null;
            }
            else
            {
                if (cimDComSession != tempSession)
                    cimDComSession = tempSession;
            }
            return result;
        }

        /// <summary>
        /// Get all cim instances matching the query using DCOM
        /// </summary>
        /// <param name="Credential">The credentiuls to use for the connection.</param>
        /// <param name="Query">The query to use requesting information.</param>
        /// <param name="Dialect">Defaults to WQL.</param>
        /// <param name="Namespace">The namespace to look in (defaults to root\cimv2).</param>
        /// <returns></returns>
        public object QueryCimDCOMInstance(PSCredential Credential, string Query, string Dialect = "WQL",
            string Namespace = @"root\cimv2")
        {
            CimSession tempSession;
            IEnumerable<CimInstance> result = new List<CimInstance>();

            tempSession = GetCimDComSession(Credential);
            result = tempSession.QueryInstances(Namespace, Dialect, Query);
            result.GetEnumerator().MoveNext();

            if (DisableCimPersistence)
            {
                try
                {
                    tempSession.Close();
                }
                catch
                {
                }
                cimDComSession = null;
            }
            else
            {
                if (cimDComSession != tempSession)
                    cimDComSession = tempSession;
            }
            return result;
        }

        #endregion DCOM

        #endregion CIM Execution

        /// <summary>
        /// Simple string representation
        /// </summary>
        /// <returns>Returns the computerName it is connection for</returns>
        public override string ToString()
        {
            return ComputerName;
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Connection\ManagementConnectionProtocolState.cs
namespace Sqlcollaborative.Dbatools.Connection
{
    /// <summary>
    /// The various types of state a connection-protocol may have
    /// </summary>
    public enum ManagementConnectionProtocolState
    {
        /// <summary>
        /// The default initial state, before any tests are performed
        /// </summary>
        Unknown = 1,

        /// <summary>
        /// A successful connection was last established
        /// </summary>
        Success = 2,

        /// <summary>
        /// Connecting using the relevant protocol failed last it was tried
        /// </summary>
        Error = 4,

        /// <summary>
        /// The relevant protocol has been disabled and should not be used
        /// </summary>
        Disabled = 8
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Connection\ManagementConnectionType.cs
using System;

namespace Sqlcollaborative.Dbatools.Connection
{
    /// <summary>
    /// The various ways to connect to a windows server fopr management purposes.
    /// </summary>
    [Flags]
    public enum ManagementConnectionType
    {
        /// <summary>
        /// No Connection-Type
        /// </summary>
        None = 0,

        /// <summary>
        /// Cim over a WinRM connection
        /// </summary>
        CimRM = 1,

        /// <summary>
        /// Cim over a DCOM connection
        /// </summary>
        CimDCOM = 2,

        /// <summary>
        /// WMI Connection
        /// </summary>
        Wmi = 4,

        /// <summary>
        /// Connecting with PowerShell remoting and performing WMI queries locally
        /// </summary>
        PowerShellRemoting = 8
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Connection\PSSessionContainer.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Management.Automation.Runspaces;
using System.Text;
using System.Threading.Tasks;

namespace Sqlcollaborative.Dbatools.Connection
{
    /// <summary>
    /// The container that lists all sessions for a given runspace
    /// </summary>
    public class PSSessionContainer
    {
        /// <summary>
        /// The runspace that owns the sessions
        /// </summary>
        public readonly Guid Runspace;

        /// <summary>
        /// The count of expired sessions registered
        /// </summary>
        public int CountExpired { get { return GetExpiredNames().Count; } }

        /// <summary>
        /// List of sessions and their associated computer names
        /// </summary>
        public Dictionary<string, PSSession> Sessions = new Dictionary<string, PSSession>();

        /// <summary>
        /// List of timestamps, when the last command was run against them
        /// </summary>
        public Dictionary<string, DateTime> ConnectionTimestamps = new Dictionary<string, DateTime>();

        /// <summary>
        /// Creates a list of sessions the current runspace is connected to.
        /// </summary>
        /// <param name="Runspace">The Guid of the runspace that is the owner of the registered sessions</param>
        public PSSessionContainer(Guid Runspace)
        {
            this.Runspace = Runspace;
        }

        /// <summary>
        /// Returns the requested session.
        /// </summary>
        /// <param name="ComputerName">The name of the host whose connection to retrieve</param>
        /// <returns>The established connection to the host, null if none exists.</returns>
        public PSSession Get(string ComputerName)
        {
            if (Sessions.ContainsKey(ComputerName.ToLower()))
                return Sessions[ComputerName.ToLower()];
            return null;
        }

        /// <summary>
        /// Sets a session and writes its timestamp to the cache
        /// </summary>
        /// <param name="ComputerName">The hostname it connects to.</param>
        /// <param name="Session">The session that is being registered.</param>
        public void Set(string ComputerName, PSSession Session)
        {
            if (!ConnectionHost.PSSessionCacheEnabled)
                return;
            Sessions[ComputerName.ToLower()] = Session;
            ConnectionTimestamps[ComputerName.ToLower()] = DateTime.Now;
        }

        /// <summary>
        /// Returns the name of hostnames with expired sessions
        /// </summary>
        /// <returns>THe hostnames whose session has expired</returns>
        public List<string> GetExpiredNames()
        {
            List<string> expired = new List<string>();

            foreach (string key in ConnectionTimestamps.Keys)
                if (ConnectionTimestamps[key] + ConnectionHost.PSSessionTimeout < DateTime.Now && Sessions[key] != null && Sessions[key].Availability != RunspaceAvailability.Busy)
                    expired.Add(key);

            return expired;
        }

        /// <summary>
        /// Removes an expired session from the cache, an returns it, so it can be properly closed.
        /// </summary>
        /// <returns>Returns a session to disconnect</returns>
        public PSSession PurgeExpiredSession()
        {
            foreach (string key in ConnectionTimestamps.Keys)
            {
                if (ConnectionTimestamps[key] + ConnectionHost.PSSessionTimeout < DateTime.Now && Sessions[key] != null && Sessions[key].Availability != RunspaceAvailability.Busy)
                {
                    PSSession session = Sessions[key];
                    Sessions.Remove(key);
                    ConnectionTimestamps.Remove(key);
                    return session;
                }
            }
            return null;
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Connection\SqlConnectionProtocol.cs
namespace Sqlcollaborative.Dbatools.Connection
{
    /// <summary>
    /// The protocol to connect over via SMO
    /// </summary>
    public enum SqlConnectionProtocol
    {
        /// <summary>
        /// Connect using any protocol available
        /// </summary>
        Any = 1,

        /// <summary>
        /// Connect using TCP/IP
        /// </summary>
        TCP = 2,

        /// <summary>
        /// Connect using named pipes or shared memory
        /// </summary>
        NP = 3
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Database\BackupHistory.cs
using System;
using System.Numerics;
using Sqlcollaborative.Dbatools.Utility;

namespace Sqlcollaborative.Dbatools.Database
{
    /// <summary>
    /// Object containing the information about the history of mankind ... or a database backup. WHo knows.
    /// </summary>
    public class BackupHistory
    {
        /// <summary>
        /// The name of the computer running MSSQL Server
        /// </summary>
        public string ComputerName;

        /// <summary>
        /// The Instance that was queried
        /// </summary>
        public string InstanceName;

        /// <summary>
        /// The full Instance name as seen from outside
        /// </summary>
        public string SqlInstance;

        /// <summary>
        /// The Database that was backed up
        /// </summary>
        public string Database;

        /// <summary>
        /// The user that is running the backup
        /// </summary>
        public string UserName;

        /// <summary>
        /// When was the backup started
        /// </summary>
        public DateTime Start;

        /// <summary>
        /// When did the backup end
        /// </summary>
        public DateTime End;

        /// <summary>
        /// What was the longest duration among the backups
        /// </summary>
        public DbaTimeSpan Duration;

        /// <summary>
        /// Where is the backup stored
        /// </summary>
        public string[] Path;

        /// <summary>
        /// What is the total size of the backup
        /// </summary>
        public Size TotalSize;

        /// <summary>
        /// What is the total compressesed size of the backup
        /// </summary>
        public Size CompressedBackupSize;

        /// <summary>
        /// What is the ratio of total size to compressed size of the backup
        /// </summary>
        public double CompressionRatio;

        /// <summary>
        /// The kind of backup this was
        /// </summary>
        public string Type;

        /// <summary>
        /// The ID for the Backup job
        /// </summary>
        public string BackupSetId;

        /// <summary>
        /// What kind of backup-device was the backup stored to
        /// </summary>
        public string DeviceType;

        /// <summary>
        /// What is the name of the backup software?
        /// </summary>
        public string Software;

        /// <summary>
        /// The full name of the backup
        /// </summary>
        public string[] FullName;

        /// <summary>
        /// The files that are part of this backup
        /// </summary>
        public object FileList;

        /// <summary>
        /// The position of the backup
        /// </summary>
        public int Position;

        /// <summary>
        /// The first Log Sequence Number
        /// </summary>
        public BigInteger FirstLsn;

        /// <summary>
        /// The Log Squence Number that marks the beginning of the backup
        /// </summary>
        public BigInteger DatabaseBackupLsn;

        /// <summary>
        /// The checkpoint's Log Sequence Number
        /// </summary>
        public BigInteger CheckpointLsn;

        /// <summary>
        /// The last Log Sequence Number
        /// </summary>
        public BigInteger LastLsn;

        /// <summary>
        /// The primary version number of the Sql Server
        /// </summary>
        public int SoftwareVersionMajor;
        
        /// <summary>
        /// Was the backup performed with the CopyOnlyOption
        /// </summary>
        public Boolean IsCopyOnly;

        /// <summary>
        /// Recovery Fork backup was takeon
        /// </summary>
        public Guid LastRecoveryForkGUID;

        /// <summary>
        /// Recovery Model of the database when backup was taken
        /// </summary>
        public string RecoveryModel;
        
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Database\Dependency.cs
using System;

namespace Sqlcollaborative.Dbatools.Database
{
    /// <summary>
    /// Class containing all dependency information over a database object
    /// </summary>
    [Serializable]
    public class Dependency
    {
        /// <summary>
        /// The name of the SQL server from whence the query came
        /// </summary>
        public string ComputerName;

        /// <summary>
        /// Name of the service running the database containing the dependency
        /// </summary>
        public string ServiceName;

        /// <summary>
        /// The Instance the database containing the dependency is running in.
        /// </summary>
        public string SqlInstance;

        /// <summary>
        /// The name of the dependent
        /// </summary>
        public string Dependent;

        /// <summary>
        /// The kind of object the dependent is
        /// </summary>
        public string Type;

        /// <summary>
        /// The owner of the dependent (usually the Database)
        /// </summary>
        public string Owner;

        /// <summary>
        /// Whether the dependency is Schemabound. If it is, then the creation statement order is of utmost importance.
        /// </summary>
        public bool IsSchemaBound;

        /// <summary>
        /// The immediate parent of the dependent. Useful in multi-tier dependencies.
        /// </summary>
        public string Parent;

        /// <summary>
        /// The type of object the immediate parent is.
        /// </summary>
        public string ParentType;

        /// <summary>
        /// The script used to create the object.
        /// </summary>
        public string Script;

        /// <summary>
        /// The tier in the dependency hierarchy tree. Used to determine, which dependency must be applied in which order.
        /// </summary>
        public int Tier;

        /// <summary>
        /// The smo object of the dependent.
        /// </summary>
        public object Object;

        /// <summary>
        /// The Uniform Resource Name of the dependent.
        /// </summary>
        public object Urn;

        /// <summary>
        /// The object of the original resource, from which the dependency hierachy has been calculated.
        /// </summary>
        public object OriginalResource;
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\dbaSystem\DbaErrorRecord.cs
using System;
using System.Management.Automation;

namespace Sqlcollaborative.Dbatools.dbaSystem
{
    /// <summary>
    /// An error record written by dbatools
    /// </summary>
    [Serializable]
    public class DbaErrorRecord
    {
        /// <summary>
        /// The category of the error
        /// </summary>
        public ErrorCategoryInfo CategoryInfo;

        /// <summary>
        /// The details on the error
        /// </summary>
        public ErrorDetails ErrorDetails;

        /// <summary>
        /// The actual exception thrown
        /// </summary>
        public Exception Exception;

        /// <summary>
        /// The specific error identity, used to identify the target
        /// </summary>
        public string FullyQualifiedErrorId;

        /// <summary>
        /// The details of how this was called.
        /// </summary>
        public object InvocationInfo;

        /// <summary>
        /// The script's stacktrace
        /// </summary>
        public string ScriptStackTrace;

        /// <summary>
        /// The object being processed
        /// </summary>
        public object TargetObject;

        /// <summary>
        /// The name of the function throwing the error
        /// </summary>
        public string FunctionName;

        /// <summary>
        /// When was the error thrown
        /// </summary>
        public DateTime Timestamp;

        /// <summary>
        /// The message that was written in a userfriendly manner
        /// </summary>
        public string Message;

        /// <summary>
        /// The runspace the error occured on.
        /// </summary>
        public Guid Runspace;

        /// <summary>
        /// Create an empty record
        /// </summary>
        public DbaErrorRecord()
        {

        }

        /// <summary>
        /// Create a filled out error record
        /// </summary>
        /// <param name="Record">The original error record</param>
        /// <param name="FunctionName">The function that wrote the error</param>
        /// <param name="Timestamp">When was the error generated</param>
        /// <param name="Message">What message was passed when writing the error</param>
        public DbaErrorRecord(ErrorRecord Record, string FunctionName, DateTime Timestamp, string Message)
        {
            this.FunctionName = FunctionName;
            this.Timestamp = Timestamp;
            this.Message = Message;

            CategoryInfo = Record.CategoryInfo;
            ErrorDetails = Record.ErrorDetails;
            Exception = Record.Exception;
            FullyQualifiedErrorId = Record.FullyQualifiedErrorId;
            InvocationInfo = Record.InvocationInfo;
            ScriptStackTrace = Record.ScriptStackTrace;
            TargetObject = Record.TargetObject;
        }

        /// <summary>
        /// Create a filled out error record
        /// </summary>
        /// <param name="Record">The original error record</param>
        /// <param name="FunctionName">The function that wrote the error</param>
        /// <param name="Timestamp">When was the error generated</param>
        /// <param name="Message">What message was passed when writing the error</param>
        /// <param name="Runspace">The ID of the runspace writing the error. Used to separate output between different runspaces in the same process.</param>
        public DbaErrorRecord(ErrorRecord Record, string FunctionName, DateTime Timestamp, string Message, Guid Runspace)
        {
            this.FunctionName = FunctionName;
            this.Timestamp = Timestamp;
            this.Message = Message;
            this.Runspace = Runspace;

            CategoryInfo = Record.CategoryInfo;
            ErrorDetails = Record.ErrorDetails;
            Exception = Record.Exception;
            FullyQualifiedErrorId = Record.FullyQualifiedErrorId;
            InvocationInfo = Record.InvocationInfo;
            ScriptStackTrace = Record.ScriptStackTrace;
            TargetObject = Record.TargetObject;
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\dbaSystem\DebugHost.cs
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Management.Automation;

namespace Sqlcollaborative.Dbatools.dbaSystem
{
    /// <summary>
    /// Hosts static debugging values and methods
    /// </summary>
    public static class DebugHost
    {
        #region Start Times
        /// <summary>
        /// Lists the duration for the last import of dbatools.
        /// </summary>
        public static List<StartTimeEntry> ImportTimeEntries = new List<StartTimeEntry>();

        /// <summary>
        /// Returns the calculated time each phase took during the last import of dbatool.
        /// </summary>
        public static List<StartTimeResult> ImportTime
        {
            get
            {
                List<StartTimeResult> result = new List<StartTimeResult>();
                int n = 0;
                foreach (StartTimeEntry entry in ImportTimeEntries)
                    if (n++ > 0)
                        result.Add(new StartTimeResult(entry.Action, ImportTimeEntries[n - 2].Timestamp, entry.Timestamp));

                return result;
            }
        }
        #endregion Start Times
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\dbaSystem\StartTimeEntry.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Sqlcollaborative.Dbatools.dbaSystem
{
    /// <summary>
    /// Entry containing the information of a step during the import sequence
    /// </summary>
    public class StartTimeEntry
    {
        /// <summary>
        /// The action that has been taken
        /// </summary>
        public string Action { get; set; }

        /// <summary>
        /// When was the action taken?
        /// </summary>
        public DateTime Timestamp { get; set; }

        /// <summary>
        /// The runspace the entry was written on
        /// </summary>
        public Guid Runspace;

        /// <summary>
        /// Creates a new StartTimeEntry
        /// </summary>
        /// <param name="Action">The action that has been taken</param>
        /// <param name="Timestamp">When was the action taken?</param>
        /// <param name="Runspace">The runspace the entry was written on</param>
        public StartTimeEntry(string Action, DateTime Timestamp, Guid Runspace)
        {
            this.Action = Action;
            this.Timestamp = Timestamp;
            this.Runspace = Runspace;
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\dbaSystem\StartTimeResult.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Sqlcollaborative.Dbatools.dbaSystem
{
    /// <summary>
    /// The processed result how long a given step took
    /// </summary>
    public class StartTimeResult
    {
        /// <summary>
        /// What action was taken?
        /// </summary>
        public string Action { get; set; }

        /// <summary>
        /// How long did things take?
        /// </summary>
        public TimeSpan Duration
        {
            get { return End - Start; }
        }

        /// <summary>
        /// When did this action start?
        /// </summary>
        public DateTime Start { get; set; }

        /// <summary>
        /// When did this action end?
        /// </summary>
        public DateTime End { get; set; }

        /// <summary>
        /// Creates a new StartTimeResult with all values preconfigured
        /// </summary>
        /// <param name="Action">The action that was taken</param>
        /// <param name="Start">When did the action start?</param>
        /// <param name="End">When did the action end?</param>
        public StartTimeResult(string Action, DateTime Start, DateTime End)
        {
            this.Action = Action;
            this.Start = Start;
            this.End = End;
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\dbaSystem\SystemHost.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Sqlcollaborative.Dbatools.dbaSystem
{
    /// <summary>
    /// Provides system-wide static resources regarding the dbatools system runtime in general
    /// </summary>
    public static class SystemHost
    {
        /// <summary>
        /// When this is set to true, functions must assume dbatools is in unattended mode. May not ask for user input of any kind.
        /// </summary>
        public static bool UnattendedMode = false;

        /// <summary>
        /// Path where the module was located when imported
        /// </summary>
        public static string ModuleBase
        {
            get { return _ModuleBase; }
            set
            {
                if (String.IsNullOrEmpty(_ModuleBase))
                    _ModuleBase = value;
            }
        }
        private static string _ModuleBase;

        /// <summary>
        /// Flag whether the module has ever been imported in the current process. If that is true, several things (such as importing libraries) is no longer necessary and will be skipped on import.
        /// </summary>
        public static bool ModuleImported;
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\dbatools.csproj
 
tools\dbatools\bin\projects\dbatools\dbatools\Discovery\DbaBrowserReply.cs
using System;

namespace Sqlcollaborative.Dbatools.Discovery
{
    /// <summary>
    /// The reply the browser service gave
    /// </summary>
    [Serializable]
    public class DbaBrowserReply
    {
        /// <summary>
        /// The machine name of the computer
        /// </summary>
        public string MachineName { get; set; }

        /// <summary>
        /// the computername of the computer
        /// </summary>
        public string ComputerName { get; set; }

        /// <summary>
        /// The instance running on the computer
        /// </summary>
        public string SqlInstance { get; set; }

        /// <summary>
        /// The name of the instance, running on the computer
        /// </summary>
        public string InstanceName { get; set; }

        /// <summary>
        /// The port number the instance is running under
        /// </summary>
        public int TCPPort { get; set; }

        /// <summary>
        /// The version of the SQL Server
        /// </summary>
        public string Version { get; set; }

        /// <summary>
        /// Whether the instance is part of a cluster or no.
        /// </summary>
        public bool IsClustered { get; set; }

        /// <summary>
        /// Override in order to make it look neater in PowerShell
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return SqlInstance;
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Discovery\DbaInstanceAvailability.cs
namespace Sqlcollaborative.Dbatools.Discovery
{
    /// <summary>
    /// Indiciator for whether an instance is known to be available or not
    /// </summary>
    public enum DbaInstanceAvailability
    {
        /// <summary>
        /// It is not known, whether the instance is available or not
        /// </summary>
        Unknown = 0,

        /// <summary>
        /// The instance is known to be available
        /// </summary>
        Available = 1,

        /// <summary>
        /// The instance is known to be not available
        /// </summary>
        Unavailable = 2
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Discovery\DbaInstanceConfidenceLevel.cs
namespace Sqlcollaborative.Dbatools.Discovery
{
    /// <summary>
    /// How high is our confidence that this is a valid instance?
    /// </summary>
    public enum DbaInstanceConfidenceLevel
    {
        /// <summary>
        /// No confidence at all. There is virtually no way for this to be an instance
        /// </summary>
        None = 0,

        /// <summary>
        /// We have a few indications, but couldn't follow them up
        /// </summary>
        Low = 1,

        /// <summary>
        /// We're fairly sure this is legit, but can't guarantee it
        /// </summary>
        Medium = 2,

        /// <summary>
        /// This absolutely is an instance
        /// </summary>
        High = 4
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Discovery\DbaInstanceDiscoveryType.cs
using System;

namespace Sqlcollaborative.Dbatools.Discovery
{
    /// <summary>
    /// What discovery mechanisms to use
    /// </summary>
    [Flags]
    public enum DbaInstanceDiscoveryType
    {
        /// <summary>
        /// We shall sweep the network for instances, by targeting every IP within a range.
        /// </summary>
        IPRange = 1,

        /// <summary>
        /// We shall search for SQL SPNs in active directory
        /// </summary>
        Domain = 2,

        /// <summary>
        /// We shall use the SSMS Data Sizrce Enumeration mechanism and hope for the best
        /// </summary>
        DataSourceEnumeration = 4,

        /// <summary>
        /// We shall use all tools in our control to find stuff
        /// </summary>
        All = 7
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Discovery\DbaInstanceReport.cs
using System;

namespace Sqlcollaborative.Dbatools.Discovery
{
    /// <summary>
    /// The report on a discovered instance
    /// </summary>
    [Serializable]
    public class DbaInstanceReport
    {
        /// <summary>
        /// The computername of the underlying machine. Usually equal to the computername, but may differ in case of clusters
        /// </summary>
        public string MachineName { get; set; }

        /// <summary>
        /// The computername of the target
        /// </summary>
        public string ComputerName { get; set; }

        /// <summary>
        /// The name of the instance
        /// </summary>
        public string InstanceName { get; set; }

        /// <summary>
        /// The full server instance name
        /// </summary>
        public string FullName
        {
            get
            {
                if (!String.IsNullOrEmpty(InstanceName) && !Utility.UtilityHost.IsLike(InstanceName, "MSSQLSERVER"))
                    return String.Format(@"{0}\{1}", ComputerName, InstanceName);
                else if ((Port == 1433) || (Utility.UtilityHost.IsLike(InstanceName, "MSSQLSERVER")))
                    return ComputerName;
                else
                    return String.Format(@"{0}:{1}", ComputerName, Port);
            }
            set { }
        }

        /// <summary>
        /// The full name usable to connect via SMO
        /// </summary>
        public string SqlInstance
        {
            get
            {
                if (!String.IsNullOrEmpty(InstanceName) && !Utility.UtilityHost.IsLike(InstanceName, "MSSQLSERVER"))
                    return String.Format(@"{0}\{1}", ComputerName, InstanceName);
                else if ((Port == 1433) || (Utility.UtilityHost.IsLike(InstanceName, "MSSQLSERVER")))
                    return ComputerName;
                else
                    return String.Format(@"{0},{1}", ComputerName, Port);
            }
            set { }
        }

        /// <summary>
        /// The port number the server listens on
        /// </summary>
        public int Port { get; set; }

        /// <summary>
        /// When the scan was concluded
        /// </summary>
        public DateTime Timestamp;

        /// <summary>
        /// Was a TCP connect successful?
        /// </summary>
        public bool TcpConnected { get; set; }

        /// <summary>
        /// Was a connection via SQL successful (even if we got access denied)
        /// </summary>
        public bool SqlConnected { get; set; }
    
        /// <summary>
        /// The DNS Resolution object
        /// </summary>
        public System.Net.IPHostEntry DnsResolution { get; set; }

        /// <summary>
        /// The ping resolution object
        /// </summary>
        public bool Ping { get; set; }

        /// <summary>
        /// The reply received from the browse request
        /// </summary>
        public DbaBrowserReply BrowseReply { get; set; }

        /// <summary>
        /// The windows services for the instance
        /// </summary>
        public object[] Services { get; set; }

        /// <summary>
        /// The SQL Server services that do not belong to that instance alone
        /// </summary>
        public object[] SystemServices { get; set; }

        /// <summary>
        /// Service Principal Names found
        /// </summary>
        public string[] SPNs { get; set; }

        /// <summary>
        /// The ports that have been scanned
        /// </summary>
        public DbaPortReport[] PortsScanned { get; set; }
    

        /// <summary>
        /// What we know about its availability
        /// </summary>
        public DbaInstanceAvailability Availability { get; set; }

        /// <summary>
        /// How confident we are, that this is a real instance
        /// </summary>
        public DbaInstanceConfidenceLevel Confidence { get; set; }

        /// <summary>
        /// What we used to scan the instance
        /// </summary>
        public DbaInstanceScanType ScanTypes { get; set; }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Discovery\DbaInstanceScanType.cs
using System;

namespace Sqlcollaborative.Dbatools.Discovery
{
    /// <summary>
    /// The mechanisms we use to determine, whether a given host contains a legit instance
    /// </summary>
    [Flags]
    public enum DbaInstanceScanType
    {
        /// <summary>
        /// Try connecting to specific ports
        /// </summary>
        TCPPort = 1,

        /// <summary>
        /// Try to connect to discovered instances (improves confidence)
        /// </summary>
        SqlConnect = 2,

        /// <summary>
        /// Check the windows services on the target
        /// </summary>
        SqlService = 4,

        /// <summary>
        /// Try resolving a computername in DNS
        /// </summary>
        DNSResolve = 8,

        /// <summary>
        /// Scan the SPNs for the targeted computer
        /// </summary>
        SPN = 16,

        /// <summary>
        /// Try contacting the local browser service and demand answers
        /// </summary>
        Browser = 32,

        /// <summary>
        /// See whether you can ping the host
        /// </summary>
        Ping = 64,

        /// <summary>
        /// Do EVERYTHING
        /// </summary>
        All = 127,

        /// <summary>
        /// Do all the things we consider sane defaults
        /// </summary>
        Default = 125,
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Discovery\DbaPortReport.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Sqlcollaborative.Dbatools.Discovery
{
    /// <summary>
    /// We tried to connect to a port, how did it go?
    /// </summary>
    [Serializable]
    public class DbaPortReport
    {
        /// <summary>
        /// The name of the computer connected to
        /// </summary>
        public string ComputerName;

        /// <summary>
        /// The number of the port we tried to connect to.
        /// </summary>
        public int Port;

        /// <summary>
        /// Whether the port was open
        /// </summary>
        public bool IsOpen;

        /// <summary>
        /// Creates an empty report (serialization uses this)
        /// </summary>
        public DbaPortReport()
        {

        }

        /// <summary>
        /// Creates a filled in report
        /// </summary>
        /// <param name="ComputerName">The name of the computer connected to</param>
        /// <param name="Port">The port we tried to connect to</param>
        /// <param name="IsOpen">Whether things worked out</param>
        public DbaPortReport(string ComputerName, int Port, bool IsOpen)
        {
            this.ComputerName = ComputerName;
            this.Port = Port;
            this.IsOpen = IsOpen;
        }

        /// <summary>
        /// Displays port connection reports in a user friendly manner
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return String.Format("{0}:{1} - {2}", ComputerName, Port, IsOpen);
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Exceptions\BloodyHellGiveMeSomethingToWorkWithException.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Sqlcollaborative.Dbatools.Exceptions
{
    /// <summary>
    /// An exception that is thrown by parameter classes when given empty input
    /// </summary>
    public class BloodyHellGiveMeSomethingToWorkWithException : ArgumentException
    {
        /// <summary>
        /// The parameter class that did the throwing
        /// </summary>
        public string ParameterClass;

        /// <summary>
        /// Creates an exception with a message and a nested exception
        /// </summary>
        /// <param name="Message">The message to tell</param>
        /// <param name="Inner">The inner exception to nest</param>
        internal BloodyHellGiveMeSomethingToWorkWithException(string Message, Exception Inner)
            : base(Message, Inner)
        {

        }

        /// <summary>
        /// Creates an exception with a message and a ParameterClass
        /// </summary>
        /// <param name="Message">The message to tell</param>
        /// <param name="ParameterClass">The Parameter Class that threw the exception</param>
        internal BloodyHellGiveMeSomethingToWorkWithException(string Message, string ParameterClass)
            : base(Message)
        {
            this.ParameterClass = ParameterClass;
        }

        /// <summary>
        /// Creates an exception with a message, a nested exception and a ParameterClass
        /// </summary>
        /// <param name="Message">The message to tell</param>
        /// <param name="Inner">The inner exception to nest</param>
        /// <param name="ParameterClass">The Parameter Class that threw the exception</param>
        internal BloodyHellGiveMeSomethingToWorkWithException(string Message, Exception Inner, string ParameterClass)
            : base(Message, Inner)
        {
            this.ParameterClass = ParameterClass;
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\General\ExecutionMode.cs
namespace Sqlcollaborative.Dbatools.General
{
    /// <summary>
    /// What kind of mode do you want to run a command in?
    /// This allows the user to choose how a dbatools function handles a bump in the execution where terminating directly may not be actually mandated.
    /// </summary>
    public enum ExecutionMode
    {
        /// <summary>
        /// When encountering issues, terminate, or skip the currently processed input, rather than continue.
        /// </summary>
        Strict,

        /// <summary>
        /// Continue as able with a best-effort attempt. Simple verbose output should do the rest.
        /// </summary>
        Lazy,

        /// <summary>
        /// Continue, but provide output that can be used to identify the operations that had issues.
        /// </summary>
        Report
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Maintenance\MaintenanceHost.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Sqlcollaborative.Dbatools.Maintenance
{
    /// <summary>
    /// Host class providing access to resources need to perform dbatools maintenance
    /// </summary>
    public static class MaintenanceHost
    {
        /// <summary>
        /// The register of available tasks.
        /// </summary>
        public static Dictionary<string, MaintenanceTask> Tasks = new Dictionary<string, MaintenanceTask>();

        /// <summary>
        /// Whether there are any due tasks
        /// </summary>
        public static bool HasDueTasks
        {
            get
            {
                foreach (MaintenanceTask task in Tasks.Values)
                    if (task.IsDue)
                        return true;

                return false;
            }
        }

        /// <summary>
        /// Returns the next task to perform. Returns null when there are no more tasks to perform
        /// </summary>
        /// <param name="Exclusions">List of tasks not to return, even if they are ready to execute again. This avoids one misconfigured task starving all lower priority tasks</param>
        /// <returns>The next task to perform.</returns>
        public static MaintenanceTask GetNextTask(string[] Exclusions)
        {
            MaintenanceTask tempTask = null;

            foreach (MaintenanceTask task in Tasks.Values)
                if (task.IsDue && (!Exclusions.Contains(task.Name)) && ((tempTask == null) || (task.Priority > tempTask.Priority)))
                    tempTask = task;

            return tempTask;
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Maintenance\MaintenancePriority.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Sqlcollaborative.Dbatools.Maintenance
{
    /// <summary>
    /// How high the priority of the task. Higher priority tasks take precedence over low priority tasks.
    /// </summary>
    public enum MaintenancePriority
    {
        /// <summary>
        /// This task is completely trivial and can be done whenever there is some spare time for it
        /// </summary>
        Trivial = 1,

        /// <summary>
        /// The task is not very significant, but should be dealt with at some point
        /// </summary>
        Low = 2,

        /// <summary>
        /// Average priority task
        /// </summary>
        Medium = 4,

        /// <summary>
        /// An important task that will take precedence over most other tasks
        /// </summary>
        High = 8,

        /// <summary>
        /// A task so critical, that it should be considered to move it to synchronous execution instead.
        /// </summary>
        Critical = 16
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Maintenance\MaintenanceTask.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Management.Automation;
using System.Text;
using System.Threading.Tasks;

namespace Sqlcollaborative.Dbatools.Maintenance
{
    /// <summary>
    /// An individual task assigned to the maintenance engine
    /// </summary>
    public class MaintenanceTask
    {
        /// <summary>
        /// The name of the task to execute. No duplciates are possible.
        /// </summary>
        public string Name;

        /// <summary>
        /// Whether the task should be done once only
        /// </summary>
        public bool Once;

        /// <summary>
        /// The interval at which the task should be performed
        /// </summary>
        public TimeSpan Interval = new TimeSpan(0);

        /// <summary>
        /// If the task need not be performed right away, it can be delayed, in order to prioritize more important initialization tasks
        /// </summary>
        public TimeSpan Delay = new TimeSpan(0);

        /// <summary>
        /// When was the task first registered. Duplicate registration calls will not increment this value.
        /// </summary>
        public DateTime Registered;

        /// <summary>
        /// When was the task last executed.
        /// </summary>
        public DateTime LastExecution;

        /// <summary>
        /// How important is this task?
        /// </summary>
        public MaintenancePriority Priority;

        /// <summary>
        /// The task code to execute
        /// </summary>
        public ScriptBlock ScriptBlock;

        /// <summary>
        /// Whether the task is due and should be executed
        /// </summary>
        public bool IsDue
        {
            get
            {
                if (Once && (LastExecution > Registered))
                    return false;

                if ((Delay.Ticks > 0) && ((Registered.Add(Delay)) > DateTime.Now))
                    return false;

                if ((LastExecution.Add(Interval)) > DateTime.Now)
                    return false;

                return true;
            }
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Message\DbatoolsException.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Management.Automation;

namespace Sqlcollaborative.Dbatools.Message
{
    /// <summary>
    /// Wrapper class that can emulate any exception for purpose of serialization without blowing up the storage space consumed
    /// </summary>
    [Serializable]
    public class DbatoolsException
    {
        private Exception _Exception;
        /// <summary>
        /// Returns the original exception object that we interpreted. This is on purpose not a property, as we want to avoid messing with serialization size.
        /// </summary>
        /// <returns>The original exception that got thrown</returns>
        public Exception GetException()
        {
            return _Exception;
        }

        #region Properties & Fields
        #region Wrapper around 'official' properties
        /// <summary>
        /// The actual Exception Message
        /// </summary>
        public string Message;

        /// <summary>
        /// The original source of the Exception
        /// </summary>
        public string Source;

        /// <summary>
        /// Where on the callstack did the exception occur?
        /// </summary>
        public string StackTrace;

        /// <summary>
        /// What was the target site on the code that caused it. This property has been altered to avoid export issues, if a string representation is not sufficient, access the original exception using GetException()
        /// </summary>
        public string TargetSite;

        /// <summary>
        /// The HResult of the exception. Useful in debugging native code errors.
        /// </summary>
        public int HResult;

        /// <summary>
        /// Link to a proper help article.
        /// </summary>
        public string HelpLink;

        /// <summary>
        /// Additional data that has been appended
        /// </summary>
        public IDictionary Data;

        /// <summary>
        /// The inner exception in a chain of exceptions.
        /// </summary>
        public DbatoolsException InnerException;
        #endregion Wrapper around 'official' properties

        #region Custom properties for exception abstraction
        /// <summary>
        /// The full namespace name of the exception that has been wrapped.
        /// </summary>
        public string ExceptionTypeName;

        /// <summary>
        /// Contains additional properties other exceptions might contain.
        /// </summary>
        public Hashtable ExceptionData = new Hashtable();
        #endregion Custom properties for exception abstraction

        #region ErrorRecord Data
        /// <summary>
        /// The category of the error
        /// </summary>
        public ErrorCategoryInfo CategoryInfo;

        /// <summary>
        /// The details on the error
        /// </summary>
        public ErrorDetails ErrorDetails;

        /// <summary>
        /// The specific error identity, used to identify the target
        /// </summary>
        public string FullyQualifiedErrorId;

        /// <summary>
        /// The details of how this was called.
        /// </summary>
        public object InvocationInfo;

        /// <summary>
        /// The script's stacktrace
        /// </summary>
        public string ScriptStackTrace;

        /// <summary>
        /// The object being processed
        /// </summary>
        public object TargetObject;

        /// <summary>
        /// The name of the function throwing the error
        /// </summary>
        public string FunctionName;

        /// <summary>
        /// When was the error thrown
        /// </summary>
        public DateTime Timestamp;

        /// <summary>
        /// The runspace the error occured on.
        /// </summary>
        public Guid Runspace;

        /// <summary>
        /// The computer the error occured on.
        /// </summary>
        public string ComputerName;
        #endregion ErrRecord Data
        #endregion Properties & Fields

        #region Constructors
        /// <summary>
        /// Creates an empty exception object. Mostly for serialization support
        /// </summary>
        public DbatoolsException()
        {

        }

        /// <summary>
        /// Creates an exception based on an original exception object
        /// </summary>
        /// <param name="Except">The exception to wrap around</param>
        public DbatoolsException(Exception Except)
        {
            _Exception = Except;

            Message = Except.Message;
            Source = Except.Source;
            StackTrace = Except.StackTrace;
            try { TargetSite = Except.TargetSite.ToString(); }
            catch { }
            HResult = Except.HResult;
            HelpLink = Except.HelpLink;
            Data = Except.Data;
            if (Except.InnerException != null) { InnerException = new DbatoolsException(Except.InnerException); }

            ExceptionTypeName = Except.GetType().FullName;

            PSObject tempObject = new PSObject(Except);
            List<string> defaultPropertyNames = new List<string>();
            defaultPropertyNames.Add("Data");
            defaultPropertyNames.Add("HelpLink");
            defaultPropertyNames.Add("HResult");
            defaultPropertyNames.Add("InnerException");
            defaultPropertyNames.Add("Message");
            defaultPropertyNames.Add("Source");
            defaultPropertyNames.Add("StackTrace");
            defaultPropertyNames.Add("TargetSite");

            foreach (PSPropertyInfo member in tempObject.Properties)
            {
                if (!defaultPropertyNames.Contains(member.Name))
                    ExceptionData[member.Name] = member.Value;
            }
        }

        /// <summary>
        /// Creates a rich information exception object based on a full error record as recorded by PowerShell
        /// </summary>
        /// <param name="Record">The error record to copy from</param>
        public DbatoolsException(ErrorRecord Record)
            : this(Record.Exception)
        {
            CategoryInfo = Record.CategoryInfo;
            ErrorDetails = Record.ErrorDetails;
            FullyQualifiedErrorId = Record.FullyQualifiedErrorId;
            InvocationInfo = Record.InvocationInfo;
            ScriptStackTrace = Record.ScriptStackTrace;
            TargetObject = Record.TargetObject;
        }

        /// <summary>
        /// Creates a new exception object with rich meta information from the PowerShell runtime.
        /// </summary>
        /// <param name="Except">The exception thrown</param>
        /// <param name="FunctionName">The name of the function in which the error occured</param>
        /// <param name="Timestamp">When did the error occur</param>
        /// <param name="Message">The message to add to the exception</param>
        /// <param name="Runspace">The ID of the runspace from which the exception was thrown. Useful in multi-runspace scenarios.</param>
        /// <param name="ComputerName">The computer the error occured on.</param>
        public DbatoolsException(Exception Except, string FunctionName, DateTime Timestamp, string Message, Guid Runspace, string ComputerName)
            : this(Except)
        {
            this.Runspace = Runspace;
            this.ComputerName = ComputerName;
            this.FunctionName = FunctionName;
            this.Timestamp = Timestamp;
            this.Message = Message;
        }

        /// <summary>
        /// Creates a new exception object with rich meta information from the PowerShell runtime.
        /// </summary>
        /// <param name="Record">The error record written</param>
        /// <param name="FunctionName">The name of the function in which the error occured</param>
        /// <param name="Timestamp">When did the error occur</param>
        /// <param name="Message">The message to add to the exception</param>
        /// <param name="Runspace">The ID of the runspace from which the exception was thrown. Useful in multi-runspace scenarios.</param>
        /// <param name="ComputerName">The computer the error occured on.</param>
        public DbatoolsException(ErrorRecord Record, string FunctionName, DateTime Timestamp, string Message, Guid Runspace, string ComputerName)
            : this(Record)
        {
            this.Runspace = Runspace;
            this.ComputerName = ComputerName;
            this.FunctionName = FunctionName;
            this.Timestamp = Timestamp;
            this.Message = Message;
        }
        #endregion Constructors

        /// <summary>
        /// Returns a string representation of the exception.
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return Message;
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Message\DbatoolsExceptionRecord.cs
using System;
using System.Collections.Generic;

namespace Sqlcollaborative.Dbatools.Message
{
    /// <summary>
    /// Carrier class, designed to hold an arbitrary number of exceptions. Used for exporting to XML in nice per-incident packages.
    /// </summary>
    [Serializable]
    public class DbatoolsExceptionRecord
    {
        /// <summary>
        /// Runspace where shit happened.
        /// </summary>
        public Guid Runspace;

        /// <summary>
        /// The computer name the exception was written on
        /// </summary>
        public string ComputerName;

        /// <summary>
        /// When did things go bad?
        /// </summary>
        public DateTime Timestamp;

        /// <summary>
        /// Name of the function, where fail happened.
        /// </summary>
        public string FunctionName;

        /// <summary>
        /// The module of the function where fail happened
        /// </summary>
        public string ModuleName;

        /// <summary>
        /// The tags that were applied to the failure
        /// </summary>
        public List<string> Tags = new List<string>();

        /// <summary>
        /// The message the poor user was shown.
        /// </summary>
        public string Message;

        /// <summary>
        /// Displays the name of the exception, the make scanning exceptions easier.
        /// </summary>
        public string ExceptionType
        {
            get
            {
                try
                {
                    if (Exceptions.Count > 0)
                    {
                        if ((Exceptions[0].GetException().GetType().FullName == "System.Exception") && (Exceptions[0].InnerException != null))
                            return Exceptions[0].InnerException.GetException().GetType().Name;

                        return Exceptions[0].GetException().GetType().Name;
                    }
                }
                catch { }

                return "";
            }
            set
            {

            }
        }

        /// <summary>
        /// The target object of the first exception in the list, if any
        /// </summary>
        public object TargetObject
        {
            get
            {
                if (Exceptions.Count > 0)
                    return Exceptions[0].TargetObject;
                return null;
            }
            set
            {

            }
        }

        /// <summary>
        /// List of Exceptions that are part of the incident (usually - but not always - only one).
        /// </summary>
        public List<DbatoolsException> Exceptions = new List<DbatoolsException>();

        /// <summary>
        /// Creates an empty container. Ideal for the homeworker who loves doing it all himself.
        /// </summary>
        public DbatoolsExceptionRecord()
        {

        }

        /// <summary>
        /// Creates a container filled with the first exception.
        /// </summary>
        /// <param name="Exception"></param>
        public DbatoolsExceptionRecord(DbatoolsException Exception)
        {
            Runspace = Exception.Runspace;
            Timestamp = Exception.Timestamp;
            FunctionName = Exception.FunctionName;
            Message = Exception.Message;
        }

        /// <summary>
        /// Creates a container filled with the meta information but untouched by exceptions
        /// </summary>
        /// <param name="Runspace">The runspace where it all happened</param>
        /// <param name="ComputerName">The computer the error was recorded</param>
        /// <param name="Timestamp">When did it happen?</param>
        /// <param name="FunctionName">Where did it happen?</param>
        /// <param name="ModuleName">The name of the module where fail happened</param>
        /// <param name="Tags">The tags that were assigned to the failure</param>
        /// <param name="Message">What did the witness have to say?</param>
        public DbatoolsExceptionRecord(Guid Runspace, string ComputerName, DateTime Timestamp, string FunctionName, string ModuleName, List<string> Tags, string Message)
        {
            this.Runspace = Runspace;
            this.ComputerName = ComputerName;
            this.Timestamp = Timestamp;
            this.FunctionName = FunctionName;
            this.ModuleName = ModuleName;
            this.Tags = Tags;
            this.Message = Message;
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Message\LogEntry.cs
using System;
using System.Collections.Generic;
using System.Management.Automation;

namespace Sqlcollaborative.Dbatools.Message
{
    /// <summary>
    /// An individual entry for the message log
    /// </summary>
    [Serializable]
    public class LogEntry
    {
        /// <summary>
        /// The message logged
        /// </summary>
        public string Message;

        /// <summary>
        /// What kind of entry was this?
        /// </summary>
        public LogEntryType Type;

        /// <summary>
        /// When was the message logged?
        /// </summary>
        public DateTime Timestamp;

        /// <summary>
        /// What function wrote the message
        /// </summary>
        public string FunctionName;

        /// <summary>
        /// The name of the module of the function that wrote the message
        /// </summary>
        public string ModuleName;

        /// <summary>
        /// The tags applied to the message
        /// </summary>
        public List<string> Tags = new List<string>();

        /// <summary>
        /// What level was the message?
        /// </summary>
        public MessageLevel Level;

        /// <summary>
        /// What runspace was the message written from?
        /// </summary>
        public Guid Runspace;

        /// <summary>
        /// The computer the message was generated on
        /// </summary>
        public string ComputerName;

        /// <summary>
        /// The object that was the focus of this message.
        /// </summary>
        public object TargetObject;

        /// <summary>
        /// The file from which the message was written.
        /// </summary>
        public string File;

        /// <summary>
        /// The line on which the message was written.
        /// </summary>
        public int Line;

        /// <summary>
        /// The callstack when the message was written.
        /// </summary>
        public IEnumerable<CallStackFrame> CallStack;

        /// <summary>
        /// The user that did the writing.
        /// </summary>
        public string Username;

        /// <summary>
        /// Creates an empty log entry
        /// </summary>
        public LogEntry()
        {

        }

        /// <summary>
        /// Creates a filled out log entry
        /// </summary>
        /// <param name="Message">The message that was logged</param>
        /// <param name="Type">The type(s) of message written</param>
        /// <param name="Timestamp">When was the message logged</param>
        /// <param name="FunctionName">What function wrote the message</param>
        /// <param name="ModuleName">Name of the module the function writing this message came from</param>
        /// <param name="Tags">Tags that were applied to the message</param>
        /// <param name="Level">What level was the message written at.</param>
        /// <param name="Runspace">The ID of the runspace that wrote the message.</param>
        /// <param name="ComputerName">The computer the message was generated on.</param>
        /// <param name="TargetObject">The object this message was all about.</param>
        /// <param name="File">The file of the code that wrote the message.</param>
        /// <param name="Line">The line on which the message was written.</param>
        /// <param name="CallStack">The callstack that triggered the write.</param>
        /// <param name="Username">The user responsible for running the code that is writing the message.</param>
        public LogEntry(string Message, LogEntryType Type, DateTime Timestamp, string FunctionName, string ModuleName, List<string> Tags, MessageLevel Level, Guid Runspace, string ComputerName, object TargetObject, string File, int Line, IEnumerable<CallStackFrame> CallStack, string Username)
        {
            this.Message = Message;
            this.Type = Type;
            this.Timestamp = Timestamp;
            this.FunctionName = FunctionName;
            this.ModuleName = ModuleName;
            this.Tags = Tags;
            this.Level = Level;
            this.Runspace = Runspace;
            this.ComputerName = ComputerName;
            this.TargetObject = TargetObject;
            this.File = File;
            this.Line = Line;
            this.CallStack = CallStack;
            this.Username = Username;
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Message\LogEntryType.cs
using System;

namespace Sqlcollaborative.Dbatools.Message
{
    /// <summary>
    /// The kind of information the logged entry was.
    /// </summary>
    [Flags]
    public enum LogEntryType
    {
        /// <summary>
        /// This entry wasn't written to any stream
        /// </summary>
        None = 0,

        /// <summary>
        /// A message that was written to the current host equivalent, if available also to the information stream
        /// </summary>
        Information = 1,

        /// <summary>
        /// A message that was written to the verbose stream
        /// </summary>
        Verbose = 2,

        /// <summary>
        /// A message that was written to the Debug stream
        /// </summary>
        Debug = 4,

        /// <summary>
        /// A message written to the warning stream
        /// </summary>
        Warning = 8
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Message\LogHost.cs
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Management.Automation;

namespace Sqlcollaborative.Dbatools.Message
{
    /// <summary>
    /// Provides static information storage for logging related settings, as well as housing the logging queues.
    /// </summary>
    public static class LogHost
    {
        #region Defines
        /// <summary>
        /// The maximum numbers of error records maintained in-memory.
        /// </summary>
        public static int MaxErrorCount = 128;

        /// <summary>
        /// The maximum number of messages that can be maintained in the in-memory message queue
        /// </summary>
        public static int MaxMessageCount = 1024;

        /// <summary>
        /// The maximum size of a given logfile. When reaching this limit, the file will be abandoned and a new log created. Set to 0 to not limit the size.
        /// </summary>
        public static int MaxMessagefileBytes = 5242880; // 5MB

        /// <summary>
        /// The maximum number of logfiles maintained at a time. Exceeding this number will cause the oldest to be culled. Set to 0 to disable the limit.
        /// </summary>
        public static int MaxMessagefileCount = 5;

        /// <summary>
        /// The maximum size all error files combined may have. When this number is exceeded, the oldest entry is culled.
        /// </summary>
        public static int MaxErrorFileBytes = 20971520; // 20MB

        /// <summary>
        /// This is the upper limit of length all items in the log folder may have combined across all processes.
        /// </summary>
        public static int MaxTotalFolderSize = 104857600; // 100MB

        /// <summary>
        /// Path to where the logfiles live.
        /// </summary>
        public static string LoggingPath;

        /// <summary>
        /// Any logfile older than this will automatically be cleansed
        /// </summary>
        public static TimeSpan MaxLogFileAge = new TimeSpan(7, 0, 0, 0);

        /// <summary>
        /// Governs, whether a log file for the system messages is written
        /// </summary>
        public static bool MessageLogFileEnabled = true;

        /// <summary>
        /// Governs, whether a log of recent messages is kept in memory
        /// </summary>
        public static bool MessageLogEnabled = true;

        /// <summary>
        /// Governs, whether log files for errors are written
        /// </summary>
        public static bool ErrorLogFileEnabled = true;

        /// <summary>
        /// Governs, whether a log of recent errors is kept in memory
        /// </summary>
        public static bool ErrorLogEnabled = true;
        #endregion Defines

        #region Queues
        private static ConcurrentQueue<DbatoolsExceptionRecord> ErrorRecords = new ConcurrentQueue<DbatoolsExceptionRecord>();

        private static ConcurrentQueue<LogEntry> LogEntries = new ConcurrentQueue<LogEntry>();

        /// <summary>
        /// The outbound queue for errors. These will be processed and written to xml
        /// </summary>
        public static ConcurrentQueue<DbatoolsExceptionRecord> OutQueueError = new ConcurrentQueue<DbatoolsExceptionRecord>();

        /// <summary>
        /// The outbound queue for logs. These will be processed and written to logfile
        /// </summary>
        public static ConcurrentQueue<LogEntry> OutQueueLog = new ConcurrentQueue<LogEntry>();
        #endregion Queues

        #region Access Queues
        /// <summary>
        /// Retrieves a copy of the Error stack
        /// </summary>
        /// <returns>All errors thrown by functions using the message or flowcontrol system</returns>
        public static DbatoolsExceptionRecord[] GetErrors()
        {
            DbatoolsExceptionRecord[] temp = new DbatoolsExceptionRecord[ErrorRecords.Count];
            ErrorRecords.CopyTo(temp, 0);
            return temp;
        }

        /// <summary>
        /// Retrieves a copy of the message log
        /// </summary>
        /// <returns>All messages logged this session.</returns>
        public static LogEntry[] GetLog()
        {
            LogEntry[] temp = new LogEntry[LogEntries.Count];
            LogEntries.CopyTo(temp, 0);
            return temp;
        }

        /// <summary>
        /// Write an error record to the log
        /// </summary>
        /// <param name="Record">The actual error record as powershell wrote it</param>
        /// <param name="FunctionName">The name of the function writing the error</param>
        /// <param name="ModuleName">The name of the module the function writing the error came from</param>
        /// <param name="Tags">The tags that were assigned to the error event</param>
        /// <param name="Timestamp">When was the error written</param>
        /// <param name="Message">What message was passed to the user</param>
        /// <param name="Runspace">The runspace the message was written from</param>
        /// <param name="ComputerName">The computer the error was written on</param>
        public static void WriteErrorEntry(ErrorRecord[] Record, string FunctionName, string ModuleName, List<string> Tags, DateTime Timestamp, string Message, Guid Runspace, string ComputerName)
        {
            DbatoolsExceptionRecord tempRecord = new DbatoolsExceptionRecord(Runspace, ComputerName, Timestamp, FunctionName, ModuleName, Tags, Message);
            foreach (ErrorRecord rec in Record)
            {
                tempRecord.Exceptions.Add(new DbatoolsException(rec, FunctionName, Timestamp, Message, Runspace, ComputerName));
            }

            if (ErrorLogFileEnabled) { OutQueueError.Enqueue(tempRecord); }
            if (ErrorLogEnabled) { ErrorRecords.Enqueue(tempRecord); }

            DbatoolsExceptionRecord tmp;
            while ((MaxErrorCount > 0) && (ErrorRecords.Count > MaxErrorCount))
            {
                ErrorRecords.TryDequeue(out tmp);
            }
        }

        /// <summary>
        /// Write a new entry to the log
        /// </summary>
        /// <param name="Message">The message to log</param>
        /// <param name="Type">The type of the message logged</param>
        /// <param name="Timestamp">When was the message generated</param>
        /// <param name="FunctionName">What function wrote the message</param>
        /// <param name="ModuleName">What module did the function writing this message come from?</param>
        /// <param name="Tags">The tags that were applied to the message</param>
        /// <param name="Level">At what level was the function written</param>
        /// <param name="Runspace">The runspace the message is coming from</param>
        /// <param name="ComputerName">The computer the message was generated on</param>
        /// <param name="File">The file from which the message was written</param>
        /// <param name="Line">The line on which the message was written</param>
        /// <param name="TargetObject">The object associated with a given message.</param>
        /// <param name="CallStack">The callstack at the moment the message was written.</param>
        /// <param name="Username">The name of the user under which the code being executed</param>
        /// <returns>The entry that is being written</returns>
        public static LogEntry WriteLogEntry(string Message, LogEntryType Type, DateTime Timestamp, string FunctionName, string ModuleName, List<string> Tags, MessageLevel Level, Guid Runspace, string ComputerName, string File, int Line, IEnumerable<CallStackFrame> CallStack, string Username, object TargetObject = null)
        {
            LogEntry temp = new LogEntry(Message, Type, Timestamp, FunctionName, ModuleName, Tags, Level, Runspace, ComputerName, TargetObject, File, Line, CallStack, Username);
            if (MessageLogFileEnabled) { OutQueueLog.Enqueue(temp); }
            if (MessageLogEnabled) { LogEntries.Enqueue(temp); }

            LogEntry tmp;
            while ((MaxMessageCount > 0) && (LogEntries.Count > MaxMessageCount))
            {
                LogEntries.TryDequeue(out tmp);
            }

            return temp;
        }
        #endregion Access Queues
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Message\MessageEventSubscription.cs
using Sqlcollaborative.Dbatools.Utility;
using System;
using System.Collections.Generic;
using System.Management.Automation;

namespace Sqlcollaborative.Dbatools.Message
{
    /// <summary>
    /// Condition and logic to be executed on message events
    /// </summary>
    public class MessageEventSubscription
    {
        /// <summary>
        /// Name of the event subscription, must be unique.
        /// </summary>
        public string Name;

        /// <summary>
        /// Scriptblock to execute if the condition is met
        /// </summary>
        public ScriptBlock ScriptBlock;

        /// <summary>
        /// The internally stored filter value for Message
        /// </summary>
        private string _MessageFilter;
        /// <summary>
        /// The value the Message is filtered by
        /// </summary>
        public string MessageFilter
        {
            get { return _MessageFilter; }
            set
            {
                _MessageFilter = value;
                MessageFilterSet = true;
            }
        }
        /// <summary>
        /// Whether filtering by Message was enabled
        /// </summary>
        public bool MessageFilterSet { get; private set; }

        /// <summary>
        /// The internally stored filter value for ModuleName
        /// </summary>
        private string _ModuleNameFilter;
        /// <summary>
        /// The value the ModuleName is filtered by
        /// </summary>
        public string ModuleNameFilter
        {
            get { return _ModuleNameFilter; }
            set
            {
                _ModuleNameFilter = value;
                ModuleNameFilterSet = true;
            }
        }
        /// <summary>
        /// Whether filtering by ModuleName was enabled
        /// </summary>
        public bool ModuleNameFilterSet { get; private set; }

        /// <summary>
        /// The internally stored filter value for FunctionName
        /// </summary>
        private string _FunctionNameFilter;
        /// <summary>
        /// The value the FunctionName is filtered by
        /// </summary>
        public string FunctionNameFilter
        {
            get { return _FunctionNameFilter; }
            set
            {
                _FunctionNameFilter = value;
                FunctionNameFilterSet = true;
            }
        }
        /// <summary>
        /// Whether filtering by FunctionName was enabled
        /// </summary>
        public bool FunctionNameFilterSet { get; private set; }

        /// <summary>
        /// The internally stored filter value for Target
        /// </summary>
        private object _TargetFilter;
        /// <summary>
        /// The value the Target is filtered by
        /// </summary>
        public object TargetFilter
        {
            get { return _TargetFilter; }
            set
            {
                _TargetFilter = value;
                TargetFilterSet = true;
            }
        }
        /// <summary>
        /// Whether filtering by Target was enabled
        /// </summary>
        public bool TargetFilterSet { get; private set; }

        /// <summary>
        /// The internally stored filter value for Level
        /// </summary>
        private List<MessageLevel> _LevelFilter;
        /// <summary>
        /// The value the Level is filtered by
        /// </summary>
        public List<MessageLevel> LevelFilter
        {
            get { return _LevelFilter; }
            set
            {
                _LevelFilter = value;
                LevelFilterSet = true;
            }
        }
        /// <summary>
        /// Whether filtering by Level was enabled
        /// </summary>
        public bool LevelFilterSet { get; private set; }

        /// <summary>
        /// The internally stored filter value for Tag
        /// </summary>
        private List<string> _TagFilter;
        /// <summary>
        /// The value the Tag is filtered by
        /// </summary>
        public List<string> TagFilter
        {
            get { return _TagFilter; }
            set
            {
                _TagFilter = value;
                TagFilterSet = true;
            }
        }
        /// <summary>
        /// Whether filtering by Tag was enabled
        /// </summary>
        public bool TagFilterSet { get; private set; }

        /// <summary>
        /// The internally stored filter value for Runspace
        /// </summary>
        private Guid _RunspaceFilter;
        /// <summary>
        /// The value the Runspace is filtered by
        /// </summary>
        public Guid RunspaceFilter
        {
            get { return _RunspaceFilter; }
            set
            {
                _RunspaceFilter = value;
                RunspaceFilterSet = true;
            }
        }
        /// <summary>
        /// Whether filtering by Runspace was enabled
        /// </summary>
        public bool RunspaceFilterSet { get; private set; }

        /// <summary>
        /// Checks, whether a given entry matches the filter defined in this subscription
        /// </summary>
        /// <param name="Entry">The entry to validate</param>
        /// <returns>Whether the subscription should react to this entry</returns>
        public bool Applies(LogEntry Entry)
        {
            if (MessageFilterSet && !UtilityHost.IsLike(Entry.Message, MessageFilter))
                return false;
            if (ModuleNameFilterSet && !UtilityHost.IsLike(Entry.ModuleName, ModuleNameFilter))
                return false;
            if (FunctionNameFilterSet && !UtilityHost.IsLike(Entry.FunctionName, FunctionNameFilter))
                return false;
            if (TargetFilterSet && (Entry.TargetObject != TargetFilter))
                return false;
            if (LevelFilterSet && !LevelFilter.Contains(Entry.Level))
                return false;
            if (TagFilterSet)
            {
                bool test = false;

                foreach (string tag in TagFilter)
                    foreach (string tag2 in Entry.Tags)
                        if (tag == tag2)
                            test = true;

                if (!test)
                    return false;
            }
            if (RunspaceFilterSet && RunspaceFilter != Entry.Runspace)
                return false;

            return true;
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Message\MessageHost.cs
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Management.Automation;

namespace Sqlcollaborative.Dbatools.Message
{
    /// <summary>
    /// Provides static resources to the messaging subsystem
    /// </summary>
    public static class MessageHost
    {
        #region Defines
        /// <summary>
        /// The maximum message level to still display to the user directly.
        /// </summary>
        public static int MaximumInformation = 3;

        /// <summary>
        /// The maxium message level where verbose information is still written.
        /// </summary>
        public static int MaximumVerbose = 6;

        /// <summary>
        /// The maximum message level where debug information is still written.
        /// </summary>
        public static int MaximumDebug = 9;

        /// <summary>
        /// The minimum required message level for messages that will be shown to the user.
        /// </summary>
        public static int MinimumInformation = 1;

        /// <summary>
        /// The minimum required message level where verbose information is written.
        /// </summary>
        public static int MinimumVerbose = 4;

        /// <summary>
        /// The minimum required message level where debug information is written.
        /// </summary>
        public static int MinimumDebug = 1;

        /// <summary>
        /// The color stuff gets written to the console in
        /// </summary>
        public static ConsoleColor InfoColor = ConsoleColor.Cyan;

        /// <summary>
        /// The color important stuff gets written to the console in
        /// </summary>
        public static ConsoleColor InfoColorEmphasis = ConsoleColor.Green;

        /// <summary>
        /// The color background stuff gets written to the console in
        /// </summary>
        public static ConsoleColor InfoColorSubtle = ConsoleColor.Gray;

        /// <summary>
        /// The color stuff gets written to the console in, when developer mode is enabled and the message would not have been written after all
        /// </summary>
        public static ConsoleColor DeveloperColor = ConsoleColor.Gray;

        /// <summary>
        /// Enables the developer mode. In this all messages are written to the console, in order to make it easier to troubleshoot issues.
        /// </summary>
        public static bool DeveloperMode = false;

        /// <summary>
        /// Message levels can decrease by nested level. This causes messages to have an increasingly reduced level as the size of the callstack increases. 
        /// </summary>
        public static int NestedLevelDecrement = 0;

        /// <summary>
        /// Globally override all verbosity
        /// </summary>
        public static bool DisableVerbosity = false;

        /// <summary>
        /// Include message timestamps in verbose message output
        /// </summary>
        public static bool EnableMessageTimestamp = true;

        /// <summary>
        /// Include the message display command in the verbose message output
        /// </summary>
        public static bool EnableMessageDisplayCommand = true;

        /// <summary>
        /// Include the entire callstack in the verbose message output
        /// </summary>
        public static bool EnableMessageBreadcrumbs = false;
        #endregion Defines

        #region Transformations
        /// <summary>
        /// The size of the transform error queue. When adding more than this, the oldest entry will be discarded
        /// </summary>
        public static int TransformErrorQueueSize = 512;

        /// <summary>
        /// Provides the option to transform exceptions based on the original exception type
        /// </summary>
        public static Dictionary<string, ScriptBlock> ExceptionTransforms = new Dictionary<string, ScriptBlock>();

        /// <summary>
        /// Provides the option to transform target objects based on type. This is sometimes important when working with live state objects that should not be serialized.
        /// </summary>
        public static Dictionary<string, ScriptBlock> TargetTransforms = new Dictionary<string, ScriptBlock>();

        /// <summary>
        /// The list of transformation errors that occured.
        /// </summary>
        private static ConcurrentQueue<TransformError> TransformErrors = new ConcurrentQueue<TransformError>();

        /// <summary>
        /// Returns the current queue of failed transformations
        /// </summary>
        /// <returns>The list of transformations that failed</returns>
        public static TransformError[] GetTransformErrors()
        {
            return TransformErrors.ToArray();
        }

        /// <summary>
        /// Writes a new transform error
        /// </summary>
        /// <param name="Record">The record of what went wrong</param>
        /// <param name="FunctionName">The name of the function writing the transformed message</param>
        /// <param name="ModuleName">The module the function writing the transformed message is part of</param>
        /// <param name="Object">The object that should have been transformed</param>
        /// <param name="Type">The type of transform that was attempted</param>
        /// <param name="Runspace">The runspace it all happened on</param>
        public static void WriteTransformError(ErrorRecord Record, string FunctionName, string ModuleName, object Object, TransformType Type, Guid Runspace)
        {
            TransformError tempError;

            TransformErrors.Enqueue(new TransformError(Record, FunctionName, ModuleName, Object, Type, Runspace));
            while (TransformErrors.Count > TransformErrorQueueSize)
                TransformErrors.TryDequeue(out tempError);
        }

        /// <summary>
        /// List of custom transforms for exceptions
        /// </summary>
        public static TransformList ExceptionTransformList = new TransformList();

        /// <summary>
        /// List of custom transforms for targets
        /// </summary>
        public static TransformList TargetTransformlist = new TransformList();

        /// <summary>
        /// List of all modifiers that apply to message levels
        /// </summary>
        public static Dictionary<string, MessageLevelModifier> MessageLevelModifiers = new Dictionary<string, MessageLevelModifier>();
        #endregion Transformations

        #region Events
        /// <summary>
        /// List of events that subscribe to messages being written
        /// </summary>
        public static Dictionary<string, MessageEventSubscription> Events = new Dictionary<string, MessageEventSubscription>();
        #endregion Events
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Message\MessageLevel.cs
namespace Sqlcollaborative.Dbatools.Message
{
    /// <summary>
    /// The various levels of verbosity available.
    /// </summary>
    public enum MessageLevel
    {
        /// <summary>
        /// Very important message, should be shown to the user as a high priority
        /// </summary>
        Critical = 1,

        /// <summary>
        /// Important message, the user should read this
        /// </summary>
        Important = 2,

        /// <summary>
        /// Important message, the user should read this
        /// </summary>
        Output = 2,

        /// <summary>
        /// Message relevant to the user.
        /// </summary>
        Significant = 3,

        /// <summary>
        /// Not important to the regular user, still of some interest to the curious
        /// </summary>
        VeryVerbose = 4,

        /// <summary>
        /// Background process information, in case the user wants some detailed information on what is currently happening.
        /// </summary>
        Verbose = 5,

        /// <summary>
        /// A footnote in current processing, rarely of interest to the user
        /// </summary>
        SomewhatVerbose = 6,

        /// <summary>
        /// A message of some interest from an internal system persepctive, but largely irrelevant to the user.
        /// </summary>
        System = 7,

        /// <summary>
        /// Something only of interest to a debugger
        /// </summary>
        Debug = 8,

        /// <summary>
        /// This message barely made the cut from being culled. Of purely development internal interest, and even there is 'interest' a strong word for it.
        /// </summary>
        InternalComment = 9,

        /// <summary>
        /// This message is a warning, sure sign something went badly wrong
        /// </summary>
        Warning = 666
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Message\MessageLevelModifier.cs
using System;
using System.Collections.Generic;

namespace Sqlcollaborative.Dbatools.Message
{
    /// <summary>
    /// A modification to a given message's level
    /// </summary>
    public class MessageLevelModifier
    {
        /// <summary>
        /// Name of the modifier. Prevents duplication in a multi-runspace scenario.
        /// </summary>
        public string Name;

        /// <summary>
        /// The amount to modify the level by
        /// </summary>
        public int Modifier;

        /// <summary>
        /// Apply modifier only to messages from this function.
        /// </summary>
        public string IncludeFunctionName;

        /// <summary>
        /// Apply modifier not when the message is written by this function.
        /// </summary>
        public string ExcludeFunctionName;

        /// <summary>
        /// Apply modifier only to messages from this module
        /// </summary>
        public string IncludeModuleName;

        /// <summary>
        /// Do not apply modifier to messages from this module
        /// </summary>
        public string ExcludeModuleName;

        /// <summary>
        /// Only apply this modifier to a message that includes at least one of these tags
        /// </summary>
        public List<string> IncludeTags = new List<string>();

        /// <summary>
        /// Do not apply this modifier to a message that includes any of the following tags
        /// </summary>
        public List<string> ExcludeTags = new List<string>();

        /// <summary>
        /// Tests, whether a message a message should be modified by this modiier
        /// </summary>
        /// <param name="FunctionName">The name of the function writing the message</param>
        /// <param name="ModuleName">The name of the module, the function writing this message comes from</param>
        /// <param name="Tags">The tags of the message written</param>
        /// <returns>Whether the message applies</returns>
        public bool AppliesTo(string FunctionName, string ModuleName, List<string> Tags)
        {
            // Negatives
            if (ExcludeFunctionName == FunctionName)
                return false;
            if (ExcludeModuleName == ModuleName)
                return false;
            if (Tags != null)
                foreach (string tag in ExcludeTags)
                    foreach (string tag2 in Tags)
                        if (tag == tag2)
                            return false;

            // Positives
            if (!String.IsNullOrEmpty(IncludeFunctionName))
                if (IncludeFunctionName != FunctionName)
                    return false;
            if (!String.IsNullOrEmpty(IncludeModuleName))
                if (IncludeModuleName != ModuleName)
                    return false;

            if (IncludeTags.Count > 0)
            {
                if (Tags != null)
                    foreach (string tag in IncludeTags)
                        foreach (string tag2 in Tags)
                            if (tag == tag2)
                                return true;

                return false;
            }

            return true;
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Message\TransformCondition.cs
using System.Management.Automation;

namespace Sqlcollaborative.Dbatools.Message
{
    /// <summary>
    /// A condition, under which the object shall be transaformed
    /// </summary>
    public class TransformCondition
    {
        /// <summary>
        /// Name of the type. All similar types (as determined by the '-like' operator) will be transformed.
        /// </summary>
        public string TypeName;

        /// <summary>
        /// The name of the module to consider, using the -Like operator
        /// </summary>
        public string ModuleName;

        /// <summary>
        /// The name of the function name to consider, using the -Like operator
        /// </summary>
        public string FunctionName;

        /// <summary>
        /// The scriptblock that performs the transformation
        /// </summary>
        public ScriptBlock ScriptBlock;

        /// <summary>
        /// What kind of transformation is being performed?
        /// </summary>
        public TransformType Type;

        /// <summary>
        /// Initializes a transform condition
        /// </summary>
        /// <param name="TypeName">Only objects of similar name will be transformed</param>
        /// <param name="ModuleName">Only objects coming from similar modules will be considered</param>
        /// <param name="FunctionName">Only objects coming from similar functions will be considered</param>
        /// <param name="ScriptBlock">The scriptblock used for the transformation</param>
        /// <param name="Type">What kind of transformation this is</param>
        public TransformCondition(string TypeName, string ModuleName, string FunctionName, ScriptBlock ScriptBlock, TransformType Type)
        {
            this.TypeName = TypeName;
            this.ModuleName = ModuleName;
            this.FunctionName = FunctionName;
            this.ScriptBlock = ScriptBlock;
            this.Type = Type;
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Message\TransformError.cs
using System;
using System.Management.Automation;

namespace Sqlcollaborative.Dbatools.Message
{
    /// <summary>
    /// An error occured during a message transformation
    /// </summary>
    public class TransformError
    {
        /// <summary>
        /// The error record of what went wrong
        /// </summary>
        public ErrorRecord Record;

        /// <summary>
        /// The name of the function writing the message that failed to transform
        /// </summary>
        public string FunctionName;

        /// <summary>
        /// The name of the module the command writing the message came from
        /// </summary>
        public string ModuleName;

        /// <summary>
        /// When did it all happen?
        /// </summary>
        public DateTime Timestamp;

        /// <summary>
        /// The object that was supposed to be transformed
        /// </summary>
        public object Object;

        /// <summary>
        /// The kind of transform that failed
        /// </summary>
        public TransformType Type;

        /// <summary>
        /// The runspace it all happened on
        /// </summary>
        public Guid Runspace;

        /// <summary>
        /// Creates a new transform error
        /// </summary>
        /// <param name="Record">The record of what went wrong</param>
        /// <param name="FunctionName">The name of the function writing the transformed message</param>
        /// <param name="ModuleName">The module the function writing the transformed message is part of</param>
        /// <param name="Object">The object that should have been transformed</param>
        /// <param name="Type">The type of transform that was attempted</param>
        /// <param name="Runspace">The runspace it all happened on</param>
        public TransformError(ErrorRecord Record, string FunctionName, string ModuleName, object Object, TransformType Type, Guid Runspace)
        {
            this.Record = Record;
            this.FunctionName = FunctionName;
            this.ModuleName = ModuleName;
            this.Object = Object;
            this.Type = Type;
            this.Runspace = Runspace;
            Timestamp = DateTime.Now;
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Message\TransformList.cs
using Sqlcollaborative.Dbatools.Utility;
using System.Collections.Generic;

namespace Sqlcollaborative.Dbatools.Message
{
    /// <summary>
    /// List engine, managing the lists for a message transformation type
    /// </summary>
    public class TransformList
    {
        private List<TransformCondition> list = new List<TransformCondition>();

        /// <summary>
        /// Returns all entries in the list.
        /// </summary>
        /// <returns>The list of transforms contained within</returns>
        public TransformCondition[] GetAll()
        {
            return list.ToArray();
        }

        /// <summary>
        /// Returns whether the actual object is part of the list
        /// </summary>
        /// <param name="Condition">The object to test for list membership</param>
        /// <returns>Whether the object is listed</returns>
        public bool IsListed(TransformCondition Condition)
        {
            return list.IndexOf(Condition) >= 0;
        }

        /// <summary>
        /// Returns whether a condition with equal conditions already exists
        /// </summary>
        /// <param name="Condition">The condition to test</param>
        /// <returns>Whether the referenced condition is already listed</returns>
        public bool IsContained(TransformCondition Condition)
        {
            foreach (TransformCondition con in list)
            {
                if (con.TypeName != Condition.TypeName)
                    continue;
                if (con.ModuleName != Condition.ModuleName)
                    continue;
                if (con.FunctionName != Condition.FunctionName)
                    continue;
                if (con.Type != Condition.Type)
                    continue;

                return true;
            }
            return false;
        }

        /// <summary>
        /// Adds a condition to the list, if there is no equivalent condition present.
        /// </summary>
        /// <param name="Condition">The condition to add</param>
        public void Add(TransformCondition Condition)
        {
            if (!IsContained(Condition))
                list.Add(Condition);
        }

        /// <summary>
        /// Removes a condition from the lsit of conditional transforms
        /// </summary>
        /// <param name="Condition">The condition to remove</param>
        public void Remove(TransformCondition Condition)
        {
            list.Remove(Condition);
        }

        /// <summary>
        /// Returns the first transform whose filter is similar enough to work out.
        /// </summary>
        /// <param name="TypeName">The name of the type to check for a transform</param>
        /// <param name="ModuleName">The module of the command that wrote the message with the transformable object</param>
        /// <param name="Functionname">The command that wrote the message with the transformable object</param>
        /// <returns>Either a transform or null, if no fitting transform was found</returns>
        public TransformCondition Get(string TypeName, string ModuleName, string Functionname)
        {
            foreach (TransformCondition con in list)
            {
                if (!UtilityHost.IsLike(TypeName, con.TypeName))
                    continue;
                if (!UtilityHost.IsLike(ModuleName, con.ModuleName))
                    continue;
                if (!UtilityHost.IsLike(Functionname, con.FunctionName))
                    continue;

                return con;
            }

            return null;
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Message\TransformType.cs
namespace Sqlcollaborative.Dbatools.Message
{
    /// <summary>
    /// The messaging system provides these kinds of transformations for input.
    /// </summary>
    public enum TransformType
    {
        /// <summary>
        /// A target transform can transform the target object specified. Used for live-state objects that should not be serialized on a second thread.
        /// </summary>
        Target = 1,

        /// <summary>
        /// An exception transform allows automatic transformation of exceptions. Primarily used to unwrap exceptions from an API that wraps all exceptions.
        /// </summary>
        Exception = 2,
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\obj\Release\dbatools.csproj.FileListAbsolute.txt
C:\github\dbatools\bin\projects\dbatools\dbatools\obj\Release\dbatools.csprojResolveAssemblyReference.cache
C:\github\dbatools\bin\projects\dbatools\dbatools\obj\Release\dbatools.dll
C:\github\dbatools\bin\dbatools.xml
C:\github\dbatools\bin\projects\dbatools\dbatools\obj\Release\dbatools.pdb
C:\github\dbatools\bin\dbatools.dll
C:\github\dbatools\bin\dbatools.pdb
tools\dbatools\bin\projects\dbatools\dbatools\obj\Release\dbatools.csprojResolveAssemblyReference.cache
 
tools\dbatools\bin\projects\dbatools\dbatools\obj\Release\dbatools.dll
md5: 6DF61CFE79E0E1C6C808BF7905946D6D | sha1: DB51AE2304B191EC69EF4FB1272C9DC98307FBBE | sha256: 749528A165255A7EBE62659EA9970BF8FDC302E7F79CC8677A4922C9FBE4FE03 | sha512: 6B42DE417905B8F6C7C2A8C6DE2BF029E300A82EBD9823038BAF3430F352AE0427CB08BEAFC27F9221829330412285B5105C977C1B8385384962703E466E0774
tools\dbatools\bin\projects\dbatools\dbatools\obj\Release\dbatools.pdb
 
tools\dbatools\bin\projects\dbatools\dbatools\Parameter\DbaCmConnectionParameter.cs
using System;
using System.Collections.Generic;
using System.Management.Automation;
using Sqlcollaborative.Dbatools.Connection;

namespace Sqlcollaborative.Dbatools.Parameter
{
        
    /// <summary>
    /// Input converter for Computer Management Information
    /// </summary>
    public class DbaCmConnectionParameter
    {
        #region Fields of contract
        /// <summary>
        /// The resolved connection object
        /// </summary>
        [ParameterContract(ParameterContractType.Field, ParameterContractBehavior.Mandatory | ParameterContractBehavior.Conditional)]
        public ManagementConnection Connection;

        /// <summary>
        /// Whether input processing was successful
        /// </summary>
        [ParameterContract(ParameterContractType.Field, ParameterContractBehavior.Mandatory | ParameterContractBehavior.Arbiter)]
        public bool Success;

        /// <summary>
        /// The object actually passed to the class
        /// </summary>
        [ParameterContract(ParameterContractType.Field, ParameterContractBehavior.Mandatory)]
        public object InputObject;
        #endregion Fields of contract

        /// <summary>
        /// Implicitly convert all connection parameter objects to the connection-type
        /// </summary>
        /// <param name="Input">The parameter object to convert</param>
        [ParameterContract(ParameterContractType.Operator, ParameterContractBehavior.Conversion)]
        public static implicit operator ManagementConnection(DbaCmConnectionParameter Input)
        {
            return Input.Connection;
        }

        /// <summary>
        /// Creates a new DbaWmConnectionParameter based on an input-name
        /// </summary>
        /// <param name="ComputerName">The name of the computer the connection is stored for.</param>
        public DbaCmConnectionParameter(string ComputerName)
        {
            InputObject = ComputerName;
            if (!Utility.Validation.IsValidComputerTarget(ComputerName))
            {
                Success = false;
                return;
            }


            bool test = false;
            try { test = ConnectionHost.Connections[ComputerName.ToLower()] != null; }
            catch { }

            if (test)
            {
                Connection = ConnectionHost.Connections[ComputerName.ToLower()];
            }

            else
            {
                Connection = new ManagementConnection(ComputerName.ToLower());
                ConnectionHost.Connections[Connection.ComputerName] = Connection;
            }

            Success = true;
        }

        /// <summary>
        /// Creates a new DbaWmConnectionParameter based on an already existing connection object.
        /// </summary>
        /// <param name="Connection">The connection to accept</param>
        public DbaCmConnectionParameter(ManagementConnection Connection)
        {
            InputObject = Connection;

            this.Connection = Connection;

            Success = true;
        }

        /// <summary>
        /// Tries to convert a generic input object into a true input.
        /// </summary>
        /// <param name="Input">Any damn object in the world</param>
        public DbaCmConnectionParameter(object Input)
        {
            InputObject = Input;
            PSObject tempInput = new PSObject(Input);
            string typeName = "";

            try { typeName = tempInput.TypeNames[0].ToLower(); }
            catch
            {
                Success = false;
                return;
            }

            switch (typeName)
            {
                case "Sqlcollaborative.Dbatools.connection.managementconnection":
                    try
                    {
                        ManagementConnection con = new ManagementConnection();
                        con.ComputerName = (string)tempInput.Properties["ComputerName"].Value;

                        con.CimRM = (ManagementConnectionProtocolState)tempInput.Properties["CimRM"].Value;
                        con.LastCimRM = (DateTime)tempInput.Properties["LastCimRM"].Value;
                        con.CimDCOM = (ManagementConnectionProtocolState)tempInput.Properties["CimDCOM"].Value;
                        con.LastCimDCOM = (DateTime)tempInput.Properties["LastCimDCOM"].Value;
                        con.Wmi = (ManagementConnectionProtocolState)tempInput.Properties["Wmi"].Value;
                        con.LastWmi = (DateTime)tempInput.Properties["LastWmi"].Value;
                        con.PowerShellRemoting = (ManagementConnectionProtocolState)tempInput.Properties["PowerShellRemoting"].Value;
                        con.LastPowerShellRemoting = (DateTime)tempInput.Properties["LastPowerShellRemoting"].Value;

                        con.Credentials = (PSCredential)tempInput.Properties["Credentials"].Value;
                        con.OverrideExplicitCredential = (bool)tempInput.Properties["OverrideExplicitCredential"].Value;
                        con.KnownBadCredentials = (List<PSCredential>)tempInput.Properties["KnownBadCredentials"].Value;
                        con.WindowsCredentialsAreBad = (bool)tempInput.Properties["WindowsCredentialsAreBad"].Value;
                        con.UseWindowsCredentials = (bool)tempInput.Properties["UseWindowsCredentials"].Value;

                        con.DisableBadCredentialCache = (bool)tempInput.Properties["DisableBadCredentialCache"].Value;
                        con.DisableCimPersistence = (bool)tempInput.Properties["DisableCimPersistence"].Value;
                        con.DisableCredentialAutoRegister = (bool)tempInput.Properties["DisableCredentialAutoRegister"].Value;
                        con.EnableCredentialFailover = (bool)tempInput.Properties["EnableCredentialFailover"].Value;

                    }
                    catch
                    {
                        Success = false;
                    }
                    break;

                default:
                    Success = false;
                    break;
            }
        }

        /// <summary>
        /// Creates a new DbaCmConnectionParameter based on an instance parameter
        /// </summary>
        /// <param name="Instance">The instance to interpret</param>
        public DbaCmConnectionParameter(DbaInstanceParameter Instance)
            : this(Instance.ComputerName)
        {

        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Parameter\DbaCredentialParameter.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Management.Automation;
using System.Net;
using System.Security;
using System.Text;
using System.Threading.Tasks;

namespace Sqlcollaborative.Dbatools.Parameter
{
    /// <summary>
    /// Parameter class that handles the various kinds of credential input
    /// </summary>
    [System.ComponentModel.TypeConverter(typeof(TypeConversion.DbaCredentialParameterConverter))]
    public class DbaCredentialParameter : IConvertible
    {
        #region Fields of contract
        /// <summary>
        /// The credential object received
        /// </summary>
        [ParameterContract(ParameterContractType.Field, ParameterContractBehavior.Mandatory)]
        public PSCredential Credential;

        /// <summary>
        /// The name of the credential object
        /// </summary>
        [ParameterContract(ParameterContractType.Field, ParameterContractBehavior.Mandatory)]
        public string UserName
        {
            get { return Credential.UserName; }
        }

        /// <summary>
        /// The password of the credential object
        /// </summary>
        [ParameterContract(ParameterContractType.Field, ParameterContractBehavior.Mandatory)]
        public SecureString Password
        {
            get { return Credential.Password; }
        }
        #endregion Fields of contract

        #region Constructors
        /// <summary>
        /// Creates a credential parameter from a PSCredential object
        /// </summary>
        /// <param name="Credential">A PSCredential object</param>
        public DbaCredentialParameter(PSCredential Credential)
        {
            this.Credential = Credential;
        }

        /// <summary>
        /// Creates a credential parameter from a NetworkCredential object
        /// </summary>
        /// <param name="Credential">The credentials to use</param>
        public DbaCredentialParameter(NetworkCredential Credential)
        {
            this.Credential = new PSCredential(String.Format("{0}\\{1}", Credential.Domain, Credential.UserName).Trim('\\'), Credential.SecurePassword);
        }

        /// <summary>
        /// Creates a credential parameter from a string only. Will prompt the user for the rest of the input. Will provide an option to remember the credential under the name provided
        /// </summary>
        /// <param name="UserName">The username (and domain name as may be the case) to put a credential around</param>
        public DbaCredentialParameter(string UserName)
        {
            if (CredentialStore.ContainsKey(UserName.ToLower()))
            {
                Credential = CredentialStore[UserName.ToLower()];
            }
            else if (dbaSystem.SystemHost.UnattendedMode)
                throw new InvalidOperationException("Cannot prompt for credentials in unattended mode!");
            else
                Credential = PromptForCredential(UserName);
        }

        /// <summary>
        /// Creates a credential parameter from anything it nows how to handle
        /// </summary>
        /// <param name="Credential">The object to convert</param>
        public DbaCredentialParameter(object Credential)
        {
            if (Credential is NetworkCredential)
                this.Credential = (new DbaCredentialParameter((NetworkCredential)Credential)).Credential;
            else if (Credential is PSCredential)
                this.Credential = (PSCredential)Credential;

            else
                throw new PSArgumentException("Invalid input type");
        }
        #endregion Constructors

        #region Conversion
        /// <summary>
        /// Implicitly converts from DbaCredentialParameter to PSCredential
        /// </summary>
        /// <param name="Input">The DbaCredentialParameter to convert</param>
        [ParameterContract(ParameterContractType.Operator, ParameterContractBehavior.Conversion)]
        public static implicit operator PSCredential(DbaCredentialParameter Input)
        {
            return Input.Credential;
        }

        /// <summary>
        /// Implicitly converts a PSCredential object to DbaCredenitalParameter
        /// </summary>
        /// <param name="Input">The PSCredential to convert</param>
        public static implicit operator DbaCredentialParameter(PSCredential Input)
        {
            return new DbaCredentialParameter(Input);
        }

        /// <summary>
        /// Implicitly converts from DbaCredentialParameter to NetworkCredential
        /// </summary>
        /// <param name="Input">The DbaCredentialParameter to convert</param>
        [ParameterContract(ParameterContractType.Operator, ParameterContractBehavior.Conversion)]
        public static implicit operator NetworkCredential(DbaCredentialParameter Input)
        {
            return Input.Credential.GetNetworkCredential();
        }

        /// <summary>
        /// Implicitly converts a NetworkCredential object to DbaCredenitalParameter
        /// </summary>
        /// <param name="Input">The NetworkCredential to convert</param>
        public static implicit operator DbaCredentialParameter(NetworkCredential Input)
        {
            return new DbaCredentialParameter(Input);
        }
        #endregion Conversion

        #region Utility
        /// <summary>
        /// Legacy wrapper. While there exists implicit conversion, this allows using the object as before, avoiding errors for unknown method.
        /// </summary>
        /// <returns>A network credential object with the same credentials as the original object</returns>
        [ParameterContract(ParameterContractType.Method, ParameterContractBehavior.Conversion)]
        public NetworkCredential GetNetworkCredential()
        {
            return Credential.GetNetworkCredential();
        }

        /// <summary>
        /// Prompts the user for a password to complete a credentials object
        /// </summary>
        /// <param name="Name">The name of the user. If specified, this will be added to the prompt.</param>
        /// <returns>The finished PSCredential object</returns>
        public static PSCredential PromptForCredential(string Name = "")
        {
            Utility.CredentialPrompt prompt = Utility.CredentialPrompt.GetCredential(Name);
            if (prompt.Cancelled)
                throw new ArgumentException("No credentials specified!");

            PSCredential cred = new PSCredential(prompt.Username, prompt.Password);
            if (prompt.Remember)
                CredentialStore[cred.UserName.ToLower()] = cred;

            return cred;
        }

        /// <summary>
        /// Cached credentials, if the user stors them under a name.
        /// </summary>
        internal static Dictionary<string, PSCredential> CredentialStore = new Dictionary<string, PSCredential>();
        #endregion Utility

        #region Interface Implementation
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public TypeCode GetTypeCode()
        {
            return TypeCode.Object;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Format"></param>
        /// <returns></returns>
        public bool ToBoolean(IFormatProvider Format)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Format"></param>
        /// <returns></returns>
        public char ToChar(IFormatProvider Format)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Format"></param>
        /// <returns></returns>
        public sbyte ToSByte(IFormatProvider Format)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Format"></param>
        /// <returns></returns>
        public byte ToByte(IFormatProvider Format)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Format"></param>
        /// <returns></returns>
        public short ToInt16(IFormatProvider Format)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Format"></param>
        /// <returns></returns>
        public ushort ToUInt16(IFormatProvider Format)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Format"></param>
        /// <returns></returns>
        public int ToInt32(IFormatProvider Format)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Format"></param>
        /// <returns></returns>
        public uint ToUInt32(IFormatProvider Format)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Format"></param>
        /// <returns></returns>
        public long ToInt64(IFormatProvider Format)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Format"></param>
        /// <returns></returns>
        public ulong ToUInt64(IFormatProvider Format)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Format"></param>
        /// <returns></returns>
        public Single ToSingle(IFormatProvider Format)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Format"></param>
        /// <returns></returns>
        public double ToDouble(IFormatProvider Format)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Format"></param>
        /// <returns></returns>
        public decimal ToDecimal(IFormatProvider Format)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Format"></param>
        /// <returns></returns>
        public DateTime ToDateTime(IFormatProvider Format)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Format"></param>
        /// <returns></returns>
        public string ToString(IFormatProvider Format)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// Tries to convert the credential parameter to one of its supported types
        /// </summary>
        /// <param name="TargetType">The type to convert to</param>
        /// <param name="Format">Irrelevant</param>
        /// <returns></returns>
        public object ToType(Type TargetType, IFormatProvider Format)
        {
            if (TargetType.FullName == "System.Management.Automation.PSCredential")
                return Credential;
            if (TargetType.FullName == "System.Net.NetworkCredential")
                return GetNetworkCredential();

            throw new NotSupportedException(String.Format("Converting from {0} to {1} is not supported!", GetType().FullName, TargetType.FullName));
        }
        #endregion Interface Implementation
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Parameter\DbaDatabaseParameter.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Management.Automation;
using System.Text;
using System.Threading.Tasks;
using Sqlcollaborative.Dbatools.Utility;

namespace Sqlcollaborative.Dbatools.Parameter
{
    /// <summary>
    /// Parameter class that accepts anything pointing at a database
    /// </summary>
    public class DbaDatabaseParameter
    {
        #region Fields of Contract
        /// <summary>
        /// The original object passed to the parameter
        /// </summary>
        [ParameterContract(ParameterContractType.Field, ParameterContractBehavior.Mandatory)]
        public object InputObject { get; set; }

        /// <summary>
        /// The SMO Database Object
        /// </summary>
        [ParameterContract(ParameterContractType.Field, ParameterContractBehavior.Optional)]
        public object Database { get; set; }

        /// <summary>
        /// The name of the database
        /// </summary>
        [ParameterContract(ParameterContractType.Field, ParameterContractBehavior.Mandatory)]
        public string Name { get; set; }
        #endregion Fields of Contract

        #region Constructors
        /// <summary>
        /// Accepts the name of a database and converts it to a DbaDatabaseParameter
        /// </summary>
        /// <param name="Name"></param>
        public DbaDatabaseParameter(string Name)
        {
            InputObject = Name;
            this.Name = Name;
        }

        /// <summary>
        /// Accepts anything and tries to convert it to a live SMO Database object
        /// </summary>
        /// <param name="Item">The item to convert</param>
        public DbaDatabaseParameter(object Item)
        {
            if (Item == null)
                throw new ArgumentException("Input must not be null!");

            InputObject = Item;
            PSObject tempInput = new PSObject(Item);

            if (tempInput.TypeNames.Contains("Microsoft.SqlServer.Management.Smo.Database"))
            {
                Database = Item;
                Name = (string)tempInput.Properties["Name"].Value;
                return;
            }

            foreach (PSPropertyInfo prop in tempInput.Properties)
            {
                if (UtilityHost.IsLike(prop.Name, "Database") && (prop.Value != null))
                {
                    PSObject tempDB = new PSObject(prop.Value);

                    if (tempDB.TypeNames.Contains("Microsoft.SqlServer.Management.Smo.Database"))
                    {
                        Database = prop.Value;
                        Name = (string)tempDB.Properties["Name"].Value;
                        return;
                    }
                }
            }

            throw new ArgumentException("Cannot interpret input as SMO Database object");
        }
        #endregion Constructors

        /// <summary>
        /// Overrides the regular tostring to show something pleasant and useful
        /// </summary>
        /// <returns>The name of the database</returns>
        public override string ToString()
        {
            return Name;
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Parameter\DbaDatabaseSmoParameter.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Management.Automation;
using System.Text;
using System.Threading.Tasks;
using Sqlcollaborative.Dbatools.Utility;

namespace Sqlcollaborative.Dbatools.Parameter
{
    /// <summary>
    /// Parameter class that only accepts live SMO Databases
    /// </summary>
    public class DbaDatabaseSmoParameter
    {
        #region Fields of Contract
        /// <summary>
        /// The original object passed to the parameter
        /// </summary>
        [ParameterContract(ParameterContractType.Field, ParameterContractBehavior.Mandatory)]
        public object InputObject { get; set; }

        /// <summary>
        /// The SMO Database Object
        /// </summary>
        [ParameterContract(ParameterContractType.Field, ParameterContractBehavior.Mandatory)]
        public object Database { get; set; }

        /// <summary>
        /// The name of the database
        /// </summary>
        [ParameterContract(ParameterContractType.Field, ParameterContractBehavior.Mandatory)]
        public string Name { get; set; }
        #endregion Fields of Contract

        #region Constructors
        /// <summary>
        /// Accepts anything and tries to convert it to a live SMO Database object
        /// </summary>
        /// <param name="Item">The item to convert</param>
        public DbaDatabaseSmoParameter(object Item)
        {
            if (Item == null)
                throw new ArgumentException("Input must not be null!");

            InputObject = Item;
            PSObject tempInput = new PSObject(Item);

            if (tempInput.TypeNames.Contains("Microsoft.SqlServer.Management.Smo.Database"))
            {
                Database = Item;
                Name = (string)tempInput.Properties["Name"].Value;
                return;
            }

            foreach (PSPropertyInfo prop in tempInput.Properties)
            {
                if (UtilityHost.IsLike(prop.Name, "Database") && (prop.Value != null))
                {
                    PSObject tempDB = new PSObject(prop.Value);

                    if (tempDB.TypeNames.Contains("Microsoft.SqlServer.Management.Smo.Database"))
                    {
                        Database = prop.Value;
                        Name = (string)tempDB.Properties["Name"].Value;
                        return;
                    }
                }
            }

            throw new ArgumentException("Cannot interpret input as SMO Database object");
        }
        #endregion Constructors

        /// <summary>
        /// Overrides the regular tostring to show something pleasant and useful
        /// </summary>
        /// <returns>The name of the database</returns>
        public override string ToString()
        {
            return Name;
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Parameter\DbaInstanceInputType.cs
namespace Sqlcollaborative.Dbatools.Parameter
{
    /// <summary>
    /// What kind of object was bound to the parameter class?
    /// </summary>
    public enum DbaInstanceInputType
    {
        /// <summary>
        /// Anything, really. An unspecific not reusable type was bound
        /// </summary>
        Default,

        /// <summary>
        /// A live smo linked server object was bound
        /// </summary>
        Linked,

        /// <summary>
        /// A live smo server object was bound
        /// </summary>
        Server,

        /// <summary>
        /// A Central Management Server RegisteredServer SMO object was bound
        /// </summary>
        RegisteredServer,

        /// <summary>
        /// An actual connection string was specified. Connection strings are directly reused for SMO connections
        /// </summary>
        ConnectionString,

        /// <summary>
        /// A connection string pointing at a local, file-based DB
        /// </summary>
        ConnectionStringLocalDB,

        /// <summary>
        /// An already established sql connection to was created outside of SMO
        /// </summary>
        SqlConnection
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Parameter\DbaInstanceParameter.cs
using System;
using System.Management.Automation;
using System.Net;
using System.Net.NetworkInformation;
using System.Text.RegularExpressions;
using Sqlcollaborative.Dbatools.Connection;
using Sqlcollaborative.Dbatools.Exceptions;
using Sqlcollaborative.Dbatools.Utility;

namespace Sqlcollaborative.Dbatools.Parameter
{
    /// <summary>
    /// Input converter for instance information
    /// </summary>
    public class DbaInstanceParameter
    {
        #region Fields of contract
        /// <summary>
        /// Name of the computer as resolvable by DNS
        /// </summary>
        [ParameterContract(ParameterContractType.Field, ParameterContractBehavior.Mandatory)]
        public string ComputerName
        {
            get
            {
                // Pretend to be localhost for all non-sql functions
                if (_ComputerName == "(localdb)")
                    return "localhost";
                return _ComputerName;
            }
        }

        /// <summary>
        /// Name of the instance on the target server
        /// </summary>
        [ParameterContract(ParameterContractType.Field, ParameterContractBehavior.Optional)]
        public string InstanceName
        {
            get
            {
                if (String.IsNullOrEmpty(_InstanceName))
                    return "MSSQLSERVER";
                return _InstanceName;
            }
        }

        /// <summary>
        /// The port over which to connect to the server. Only present if non-default
        /// </summary>
        [ParameterContract(ParameterContractType.Field, ParameterContractBehavior.Optional)]
        public int Port
        {
            get
            {
                if (_Port == 0 && String.IsNullOrEmpty(_InstanceName))
                    return 1433;
                return _Port;
            }
        }

        /// <summary>
        /// The network protocol to connect over
        /// </summary>
        [ParameterContract(ParameterContractType.Field, ParameterContractBehavior.Mandatory)]
        public SqlConnectionProtocol NetworkProtocol
        {
            get
            {
                return _NetworkProtocol;
            }
        }

        /// <summary>
        /// Verifies, whether the specified computer is localhost or not.
        /// </summary>
        [ParameterContract(ParameterContractType.Field, ParameterContractBehavior.Mandatory)]
        public bool IsLocalHost
        {
            get
            {
                // Pretend to be localhost for all non-sql functions
                if (_ComputerName == "(localdb)")
                    return true;
                return Utility.Validation.IsLocalhost(_ComputerName);
            }
        }

        /// <summary>
        /// Full name of the instance, including the server-name
        /// </summary>
        [ParameterContract(ParameterContractType.Field, ParameterContractBehavior.Mandatory)]
        public string FullName
        {
            get
            {
                string temp = _ComputerName;
                if (_Port > 0) { temp += (":" + _Port); }
                if (!String.IsNullOrEmpty(_InstanceName)) { temp += ("\\" + _InstanceName); }
                return temp;
            }
        }

        /// <summary>
        /// Full name of the instance, including the server-name, used when connecting via SMO
        /// </summary>
        [ParameterContract(ParameterContractType.Field, ParameterContractBehavior.Mandatory)]
        public string FullSmoName
        {
            get
            {
                string temp = _ComputerName;
                if (_NetworkProtocol == SqlConnectionProtocol.NP) { temp = "NP:" + temp; }
                if (_NetworkProtocol == SqlConnectionProtocol.TCP) { temp = "TCP:" + temp; }
                if (!String.IsNullOrEmpty(_InstanceName) && _Port > 0) { return String.Format(@"{0}\{1},{2}", temp, _InstanceName, _Port); }
                if (_Port > 0) { return temp + "," + _Port; }
                if (!String.IsNullOrEmpty(_InstanceName)) { return temp + "\\" + _InstanceName; }
                return temp;
            }
        }

        /// <summary>
        /// Name of the computer as used in an SQL Statement
        /// </summary>
        [ParameterContract(ParameterContractType.Field, ParameterContractBehavior.Mandatory)]
        public string SqlComputerName
        {
            get { return "[" + ComputerName + "]"; }
        }

        /// <summary>
        /// Name of the instance as used in an SQL Statement
        /// </summary>
        [ParameterContract(ParameterContractType.Field, ParameterContractBehavior.Mandatory)]
        public string SqlInstanceName
        {
            get
            {
                if (String.IsNullOrEmpty(_InstanceName))
                    return "[MSSQLSERVER]";
                return "[" + _InstanceName + "]";
            }
        }

        /// <summary>
        /// Full name of the instance, including the server-name as used in an SQL statement
        /// </summary>
        [ParameterContract(ParameterContractType.Field, ParameterContractBehavior.Mandatory)]
        public string SqlFullName
        {
            get
            {
                if (String.IsNullOrEmpty(_InstanceName)) { return "[" + _ComputerName + "]"; }
                return "[" + _ComputerName + "\\" + _InstanceName + "]";
            }
        }

        /// <summary>
        /// Whether the input is a connection string
        /// </summary>
        [ParameterContract(ParameterContractType.Field, ParameterContractBehavior.Mandatory)]
        public bool IsConnectionString { get; private set; }

        /// <summary>
        /// The original object passed to the parameter class.
        /// </summary>
        [ParameterContract(ParameterContractType.Field, ParameterContractBehavior.Mandatory)]
        public object InputObject;
        #endregion Fields of contract

        private string _ComputerName;
        private string _InstanceName;
        private int _Port;
        private SqlConnectionProtocol _NetworkProtocol = SqlConnectionProtocol.Any;

        #region Uncontracted properties
        /// <summary>
        /// What kind of object was bound to the parameter class? For efficiency's purposes.
        /// </summary>
        public DbaInstanceInputType Type
        {
            get
            {
                try
                {
                    PSObject tempObject = new PSObject(InputObject);
                    string typeName = tempObject.TypeNames[0].ToLower();

                    switch (typeName)
                    {
                        case "microsoft.sqlserver.management.smo.server":
                            return DbaInstanceInputType.Server;
                        case "microsoft.sqlserver.management.smo.linkedserver":
                            return DbaInstanceInputType.Linked;
                        case "microsoft.sqlserver.management.registeredservers.registeredserver":
                            return DbaInstanceInputType.RegisteredServer;
                        case "system.data.sqlclient.sqlconnection":
                            return DbaInstanceInputType.SqlConnection;
                        default:
                            return DbaInstanceInputType.Default;
                    }
                }
                catch { return DbaInstanceInputType.Default; }
            }
        }

        /// <summary>
        /// Returns, whether a live SMO object was bound for the purpose of accessing LinkedServer functionality
        /// </summary>
        public bool LinkedLive
        {
            get
            {
                return (((DbaInstanceInputType.Linked | DbaInstanceInputType.Server) & Type) != 0);
            }
        }

        /// <summary>
        /// Returns the available Linked Server objects from live objects only
        /// </summary>
        public object LinkedServer
        {
            get
            {
                switch (Type)
                {
                    case DbaInstanceInputType.Linked:
                        return InputObject;
                    case DbaInstanceInputType.Server:
                        PSObject tempObject = new PSObject(InputObject);
                        return tempObject.Properties["LinkedServers"].Value;
                    default:
                        return null;
                }
            }
        }
        #endregion Uncontracted properties

        /// <summary>
        /// Converts the parameter class to its full name
        /// </summary>
        /// <param name="Input">The parameter class object to convert</param>
        [ParameterContract(ParameterContractType.Operator, ParameterContractBehavior.Conversion)]
        public static implicit operator string(DbaInstanceParameter Input)
        {
            return Input.FullName;
        }

        #region Constructors
        /// <summary>
        /// Creates a DBA Instance Parameter from string
        /// </summary>
        /// <param name="Name">The name of the instance</param>
        public DbaInstanceParameter(string Name)
        {
            InputObject = Name;

            if (string.IsNullOrWhiteSpace(Name))
                throw new BloodyHellGiveMeSomethingToWorkWithException("Please provide an instance name", "DbaInstanceParameter");

            if (Name == ".")
            {
                _ComputerName = Name;
                _NetworkProtocol = SqlConnectionProtocol.NP;
                return;
            }
            
            string tempString = Name.Trim();
            tempString = Regex.Replace(tempString, @"^\[(.*)\]$", "$1");

            if (UtilityHost.IsLike(tempString, @".\*"))
            {
                _ComputerName = Name;
                _NetworkProtocol = SqlConnectionProtocol.NP;

                string instanceName = tempString.Substring(2);

                if (!Utility.Validation.IsValidInstanceName(instanceName))
                    throw new ArgumentException(String.Format("Failed to interpret instance name: '{0}' is not a legal name!", instanceName));

                _InstanceName = instanceName;

                return;
            }

            if (UtilityHost.IsLike(tempString, "*.WORKGROUP"))
            tempString = Regex.Replace(tempString, @"\.WORKGROUP$", "", RegexOptions.IgnoreCase);

            // Named Pipe path notation interpretation
            if (Regex.IsMatch(tempString, @"^\\\\[^\\]+\\pipe\\([^\\]+\\){0,1}sql\\query$", RegexOptions.IgnoreCase))
            {
                try
                {
                    _NetworkProtocol = SqlConnectionProtocol.NP;

                    _ComputerName = Regex.Match(tempString, @"^\\\\([^\\]+)\\").Groups[1].Value;

                    if (Regex.IsMatch(tempString, @"\\MSSQL\$[^\\]+\\", RegexOptions.IgnoreCase))
                        _InstanceName = Regex.Match(tempString, @"\\MSSQL\$([^\\]+)\\", RegexOptions.IgnoreCase).Groups[1].Value;
                }
                catch (Exception e)
                {
                    throw new ArgumentException(String.Format("Failed to interpret named pipe path notation: {0} | {1}", InputObject, e.Message), e);
                }

                return;
            }

            // Connection String interpretation
            try
            {
                System.Data.SqlClient.SqlConnectionStringBuilder connectionString =
                    new System.Data.SqlClient.SqlConnectionStringBuilder(tempString);
                DbaInstanceParameter tempParam = new DbaInstanceParameter(connectionString.DataSource);
                _ComputerName = tempParam.ComputerName;
                if (tempParam.InstanceName != "MSSQLSERVER")
                {
                    _InstanceName = tempParam.InstanceName;
                }
                if (tempParam.Port != 1433)
                {
                    _Port = tempParam.Port;
                }
                _NetworkProtocol = tempParam.NetworkProtocol;
                
                if (UtilityHost.IsLike(tempString, @"(localdb)\*"))
                    _NetworkProtocol = SqlConnectionProtocol.NP;

                IsConnectionString = true;

                return;
            }
            catch (ArgumentException ex)
            {
                string name = "unknown";
                try
                {
                    name = ex.TargetSite.GetParameters()[0].Name;
                }
                catch
                {
                }
                if (name == "keyword")
                {
                    throw;
                }
            }
            catch (FormatException)
            {
                throw;
            }
            catch { }

            // Handle and clear protocols. Otherwise it'd make port detection unneccessarily messy
            if (Regex.IsMatch(tempString, "^TCP:", RegexOptions.IgnoreCase)) //TODO: Use case insinsitive String.BeginsWith()
            {
                _NetworkProtocol = SqlConnectionProtocol.TCP;
                tempString = tempString.Substring(4);
            }
            if (Regex.IsMatch(tempString, "^NP:", RegexOptions.IgnoreCase)) // TODO: Use case insinsitive String.BeginsWith()
            {
                _NetworkProtocol = SqlConnectionProtocol.NP;
                tempString = tempString.Substring(3);
            }

            // Case: Default instance | Instance by port
            if (tempString.Split('\\').Length == 1)
            {
                if (Regex.IsMatch(tempString, @"[:,]\d{1,5}$") && !Regex.IsMatch(tempString, RegexHelper.IPv6) && ((tempString.Split(':').Length == 2) || (tempString.Split(',').Length == 2)))
                {
                    char delimiter;
                    if (Regex.IsMatch(tempString, @"[:]\d{1,5}$"))
                        delimiter = ':';
                    else
                        delimiter = ',';

                    try
                    {
                        Int32.TryParse(tempString.Split(delimiter)[1], out _Port);
                        if (_Port > 65535) { throw new PSArgumentException("Failed to parse instance name: " + tempString); }
                        tempString = tempString.Split(delimiter)[0];
                    }
                    catch
                    {
                        throw new PSArgumentException("Failed to parse instance name: " + Name);
                    }
                }

                if (Utility.Validation.IsValidComputerTarget(tempString))
                {
                    _ComputerName = tempString;
                }

                else
                {
                    throw new PSArgumentException("Failed to parse instance name: " + Name);
                }
            }

            // Case: Named instance
            else if (tempString.Split('\\').Length == 2)
            {
                string tempComputerName = tempString.Split('\\')[0];
                string tempInstanceName = tempString.Split('\\')[1];

                if (Regex.IsMatch(tempComputerName, @"[:,]\d{1,5}$") && !Regex.IsMatch(tempComputerName, RegexHelper.IPv6))
                {
                    char delimiter;
                    if (Regex.IsMatch(tempComputerName, @"[:]\d{1,5}$"))
                        delimiter = ':';
                    else
                        delimiter = ',';

                    try
                    {
                        Int32.TryParse(tempComputerName.Split(delimiter)[1], out _Port);
                        if (_Port > 65535) { throw new PSArgumentException("Failed to parse instance name: " + Name); }
                        tempComputerName = tempComputerName.Split(delimiter)[0];
                    }
                    catch
                    {
                        throw new PSArgumentException("Failed to parse instance name: " + Name);
                    }
                }
                else if (Regex.IsMatch(tempInstanceName, @"[:,]\d{1,5}$") && !Regex.IsMatch(tempInstanceName, RegexHelper.IPv6))
                {
                    char delimiter;
                    if (Regex.IsMatch(tempString, @"[:]\d{1,5}$"))
                        delimiter = ':';
                    else
                        delimiter = ',';

                    try
                    {
                        Int32.TryParse(tempInstanceName.Split(delimiter)[1], out _Port);
                        if (_Port > 65535) { throw new PSArgumentException("Failed to parse instance name: " + Name); }
                        tempInstanceName = tempInstanceName.Split(delimiter)[0];
                    }
                    catch
                    {
                        throw new PSArgumentException("Failed to parse instance name: " + Name);
                    }
                }

                // LocalDBs mostly ignore regular Instance Name rules, so that validation is only relevant for regular connections
                if (UtilityHost.IsLike(tempComputerName, "(localdb)") || (Utility.Validation.IsValidComputerTarget(tempComputerName) && Utility.Validation.IsValidInstanceName(tempInstanceName, true)))
                {
                    if (UtilityHost.IsLike(tempComputerName, "(localdb)"))
                        _ComputerName = "(localdb)";
                    else
                        _ComputerName = tempComputerName;
                    if ((tempInstanceName.ToLower() != "default") && (tempInstanceName.ToLower() != "mssqlserver"))
                        _InstanceName = tempInstanceName;
                }

                else
                {
                    throw new PSArgumentException(string.Format("Failed to parse instance name: {0}. Computer Name: {1}, Instance {2}", Name, tempComputerName, tempInstanceName));
                }
            }

            // Case: Bad input
            else { throw new PSArgumentException("Failed to parse instance name: " + Name); }
        }

        /// <summary>
        /// Creates a DBA Instance Parameter from an IPAddress
        /// </summary>
        /// <param name="Address"></param>
        public DbaInstanceParameter(IPAddress Address)
        {
            _ComputerName = Address.ToString();
            InputObject = Address;
        }

        /// <summary>
        /// Creates a DBA Instance Parameter from the reply to a ping
        /// </summary>
        /// <param name="Ping">The result of a ping</param>
        public DbaInstanceParameter(PingReply Ping)
        {
            _ComputerName = Ping.Address.ToString();
            InputObject = Ping;
        }

        /// <summary>
        /// Creates a DBA Instance Parameter from the result of a dns resolution
        /// </summary>
        /// <param name="Entry">The result of a dns resolution, to be used for targetting the default instance</param>
        public DbaInstanceParameter(IPHostEntry Entry)
        {
            _ComputerName = Entry.HostName;
            InputObject = Entry;
        }

        /// <summary>
        /// Creates a DBA Instance Parameter from an established SQL Connection
        /// </summary>
        /// <param name="Connection">The connection to reuse</param>
        public DbaInstanceParameter(System.Data.SqlClient.SqlConnection Connection)
        {
            InputObject = Connection;
            DbaInstanceParameter tempParam = new DbaInstanceParameter(Connection.DataSource);

            _ComputerName = tempParam.ComputerName;
            if (tempParam.InstanceName != "MSSQLSERVER")
            {
                _InstanceName = tempParam.InstanceName;
            }
            if (tempParam.Port != 1433)
            {
                _Port = tempParam.Port;
            }
            _NetworkProtocol = tempParam.NetworkProtocol;
        }

        /// <summary>
        /// Accept and understand discovery reports.
        /// </summary>
        /// <param name="Report">The report to interpret</param>
        public DbaInstanceParameter(Discovery.DbaInstanceReport Report)
            : this(Report.SqlInstance)
        {
            InputObject = Report;
        }

        /// <summary>
        /// Creates a DBA Instance parameter from any object
        /// </summary>
        /// <param name="Input">Object to parse</param>
        public DbaInstanceParameter(object Input)
        {
            InputObject = Input;
            PSObject tempInput = new PSObject(Input);
            string typeName = "";

            try { typeName = tempInput.TypeNames[0].ToLower(); }
            catch
            {
                throw new PSArgumentException("Failed to interpret input as Instance: " + Input);
            }

            typeName = typeName.Replace("Deserialized.", "");

            switch (typeName)
            {
                case "microsoft.sqlserver.management.smo.server":
                    try
                    {
                        if (tempInput.Properties["ServerType"] != null && (string)tempInput.Properties["ServerType"].Value.ToString() == "SqlAzureDatabase")
                            _ComputerName = (new DbaInstanceParameter((string)tempInput.Properties["Name"].Value)).ComputerName;
                        else
                        { 
                            if (tempInput.Properties["NetName"] != null)
                                _ComputerName = (string)tempInput.Properties["NetName"].Value;
                            else
                                _ComputerName = (new DbaInstanceParameter((string)tempInput.Properties["DomainInstanceName"].Value)).ComputerName;
                        }
                        _InstanceName = (string)tempInput.Properties["InstanceName"].Value;
                        PSObject tempObject = new PSObject(tempInput.Properties["ConnectionContext"].Value);

                        string tempConnectionString = (string)tempObject.Properties["ConnectionString"].Value;
                        tempConnectionString = tempConnectionString.Split(';')[0].Split('=')[1].Trim().Replace(" ", "");

                        if (Regex.IsMatch(tempConnectionString, @",\d{1,5}$") && (tempConnectionString.Split(',').Length == 2))
                        {
                            try { Int32.TryParse(tempConnectionString.Split(',')[1], out _Port); }
                            catch (Exception e)
                            {
                                throw new PSArgumentException("Failed to parse port number on connection string: " + tempConnectionString, e);
                            }
                            if (_Port > 65535) { throw new PSArgumentException("Failed to parse port number on connection string: " + tempConnectionString); }
                        }
                    }
                    catch (Exception e)
                    {
                        throw new PSArgumentException("Failed to interpret input as Instance: " + Input + " : " + e.Message, e);
                    }
                    break;
                case "microsoft.sqlserver.management.smo.linkedserver":
                    try
                    {
                        _ComputerName = (string)tempInput.Properties["Name"].Value;
                    }
                    catch (Exception e)
                    {
                        throw new PSArgumentException("Failed to interpret input as Instance: " + Input, e);
                    }
                    break;
                case "microsoft.activedirectory.management.adcomputer":
                    try
                    {
                        _ComputerName = (string)tempInput.Properties["Name"].Value;

                        // We prefer using the dnshostname whenever possible
                        if (tempInput.Properties["DNSHostName"].Value != null)
                        {
                            if (!String.IsNullOrEmpty((string)tempInput.Properties["DNSHostName"].Value))
                                _ComputerName = (string)tempInput.Properties["DNSHostName"].Value;
                        }
                    }
                    catch (Exception e)
                    {
                        throw new PSArgumentException("Failed to interpret input as Instance: " + Input, e);
                    }
                    break;
                case "microsoft.sqlserver.management.registeredservers.registeredserver":
                    try
                    {
                        //Pass the ServerName property of the SMO object to the string constrtuctor, 
                        //so we don't have to re-invent the wheel on instance name / port parsing
                        DbaInstanceParameter parm =
                            new DbaInstanceParameter((string) tempInput.Properties["ServerName"].Value);
                        _ComputerName = parm.ComputerName;

                        if (parm.InstanceName != "MSSQLSERVER")
                            _InstanceName = parm.InstanceName;

                        if (parm.Port != 1433)
                            _Port = parm.Port;
                    }
                    catch (Exception e)
                    {
                        throw new PSArgumentException("Failed to interpret input as Instance: " + Input, e);
                    }
                    break;
                default:
                    throw new PSArgumentException("Failed to interpret input as Instance: " + Input);
            }
        }
        #endregion Constructors

        /// <summary>
        /// Overrides the regular <c>ToString()</c> to show something pleasant and useful
        /// </summary>
        /// <returns>The <see cref="FullSmoName"/></returns>
        public override string ToString()
        {
            return FullSmoName;
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Parameter\DbaSelectParameter.cs
using System;
using System.Collections;
using System.Linq;
using System.Management.Automation;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace Sqlcollaborative.Dbatools.Parameter
{
    /// <summary>
    /// Class that automatically parses input chosen for the -Property parameter of Select-PSUObject
    /// </summary>
    public class DbaSelectParameter
    {
        /// <summary>
        /// The original input object
        /// </summary>
        public object InputObject;

        /// <summary>
        /// The value as Select-Object wants it
        /// </summary>
        public object Value;

        /// <summary>
        /// Builds a property parameter from string
        /// </summary>
        /// <param name="Value">The string to interpret</param>
        public DbaSelectParameter(string Value)
        {
            InputObject = Value;

            if (!Value.Contains(" "))
            {
                this.Value = Value;
                return;
            }

            #region Process Input
            // Runtime properties
            string valueName = "";
            string propertyName = "";
            string castType = "";
            string fromName = "_";
            string wherePropInput = "";
            string wherePropOutput = "";
            string sizeName = "";
            uint sizeDecimals = 0;
            bool sizeShow = false;

            string tempValue = Value.Trim();
            propertyName = tempValue.Split(' ')[0];
            valueName = propertyName;
            tempValue = tempValue.Substring(propertyName.Length);

            if (Regex.IsMatch(tempValue, @" as \w+", RegexOptions.IgnoreCase))
            {
                propertyName = Regex.Match(tempValue, @" as (\w+)", RegexOptions.IgnoreCase).Groups[1].Value;
                tempValue = Regex.Replace(tempValue, @" as \w+", "", RegexOptions.IgnoreCase);
            }

            if (Regex.IsMatch(tempValue, @" from [\w_]+", RegexOptions.IgnoreCase))
            {
                fromName = Regex.Match(tempValue, @" from ([\w_]+)", RegexOptions.IgnoreCase).Groups[1].Value;
                tempValue = Regex.Replace(tempValue, @" from [\w_]+", "", RegexOptions.IgnoreCase);
            }

            if (Regex.IsMatch(tempValue, @" where [\w_]+ = [\w_]+", RegexOptions.IgnoreCase))
            {
                Match match = Regex.Match(tempValue, @" where ([\w_]+) = ([\w_]+)", RegexOptions.IgnoreCase);
                wherePropOutput = match.Groups[1].Value;
                wherePropInput = match.Groups[2].Value;
                tempValue = Regex.Replace(tempValue, @" where [\w_]+ = [\w_]+", "", RegexOptions.IgnoreCase);
            }

            if (Regex.IsMatch(tempValue, @" to [\w\.]+", RegexOptions.IgnoreCase))
            {
                castType = Regex.Match(tempValue, @" to ([\w\.]+)", RegexOptions.IgnoreCase).Groups[1].Value;
                tempValue = Regex.Replace(tempValue, @" to [\w\.]+", "", RegexOptions.IgnoreCase);
            }

            if (Regex.IsMatch(tempValue, @" size \w+(:\d){1,2}", RegexOptions.IgnoreCase))
            {
                Match match = Regex.Match(tempValue, @" size (\w+)(:\d){1,2}", RegexOptions.IgnoreCase);
                sizeName = match.Groups[1].Value;
                sizeDecimals = UInt32.Parse(match.Groups[2].Captures[0].Value.Trim(':'));
                if (match.Groups[2].Captures.Count > 1)
                    sizeShow = match.Groups[2].Captures[1].Value == ":1";
                tempValue = Regex.Replace(tempValue, @" size \w+(:\d){1,2}", "", RegexOptions.IgnoreCase);
            }

            if (!String.IsNullOrEmpty(tempValue))
                throw new ArgumentException(String.Format("Failed to parse input! Original input: {0} | Unprocessed leftovers: {1}", Value, tempValue));
            #endregion Process Input

            #region Build Hashtable
            Hashtable table = new Hashtable();
            table["Name"] = propertyName;

            // Process cast strings
            string stringCast = "";
            if (!String.IsNullOrEmpty(castType))
                stringCast = String.Format("[{0}]", castType);

            // Process size strings
            string stringSizeStart = "";
            string stringSizeEnd = "";
            if (sizeName != "")
            {
                stringSizeStart = "[System.Math]::Round((";
                stringSizeEnd = String.Format(" / 1{0}), {1})", sizeName, sizeDecimals);
                if (sizeShow)
                {
                    stringSizeStart = String.Format("\"$({0}", stringSizeStart);
                    stringSizeEnd = String.Format("{0}) {1}\"", stringSizeEnd, sizeName);
                }
            }

            // Process value strings
            string stringGuidVar = "${" + Guid.NewGuid().ToString() + "}";
            string preLine = String.Format("{0} = $_\n", stringGuidVar);
            string stringValue = String.Format("${0}", fromName);
            if (fromName != "_" && wherePropInput != "")
            {
                stringValue = String.Format("({1} | Where-Object {2} -eq {0}.{3})", stringGuidVar, stringValue, wherePropOutput, wherePropInput);
            }

            if (propertyName != ".")
                stringValue = String.Format("{0}.{1}", stringValue, valueName);

            // <guid> = $_
            // "(<size>(<cast>(<value>))<sizeName>)"
            string script = String.Format("{0}({1}({2}({3})){4})", preLine, stringSizeStart, stringCast, stringValue, stringSizeEnd);

            table["Expression"] = ScriptBlock.Create(script);
            this.Value = table;
            #endregion Build Hashtable
        }

        /// <summary>
        /// Builds a select parameter from a hashtable (pretty straightforward)
        /// </summary>
        /// <param name="Hash">The hashtable to accept</param>
        public DbaSelectParameter(Hashtable Hash)
        {
            InputObject = Hash;
            Value = Hash;
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Parameter\ParameterContractAttribute.cs
using System;

namespace Sqlcollaborative.Dbatools.Parameter
{
    /// <summary>
    /// The attribute used to define the elements of a ParameterClass contract
    /// </summary>
    [AttributeUsage(AttributeTargets.All)]
    public class ParameterContractAttribute : Attribute
    {
        private ParameterContractType type;
        private ParameterContractBehavior behavior;

        /// <summary>
        /// Returns the type of the element this attribute is supposed to be attached to.
        /// </summary>
        public ParameterContractType Type
        {
            get
            {
                return type;
            }
        }

        /// <summary>
        /// Returns the behavior to expect from the contracted element. This sets the expectations on how this element is likely to act.
        /// </summary>
        public ParameterContractBehavior Behavior
        {
            get
            {
                return behavior;
            }
        }

        /// <summary>
        /// Ceates a perfectly common parameter contract attribute. For use with all parameter classes' public elements.
        /// </summary>
        /// <param name="Type"></param>
        /// <param name="Behavior"></param>
        public ParameterContractAttribute(ParameterContractType Type, ParameterContractBehavior Behavior)
        {
            type = Type;
            behavior = Behavior;
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Parameter\ParameterContractBehavior.cs
using System;

namespace Sqlcollaborative.Dbatools.Parameter
{
    /// <summary>
    /// Defines how this element will behave
    /// </summary>
    [Flags]
    public enum ParameterContractBehavior
    {
        /// <summary>
        /// This elements is not actually part of the contract. Generally you wouldn't want to add the attribute at all in that case. However, in some places it helps avoiding confusion.
        /// </summary>
        NotContracted = 0,

        /// <summary>
        /// This element may never be null and must be considered in all assignments. Even if the element is de facto not nullable, all constructors must assign it.
        /// </summary>
        Mandatory = 1,

        /// <summary>
        /// This element may contain data, but is not required to. In case of a method, it may simply do nothing
        /// </summary>
        Optional = 2,

        /// <summary>
        /// This method may throw an error when executing and should always be handled with try/catch. Use this on methods that use external calls.
        /// </summary>
        Failable = 4,

        /// <summary>
        /// The content of the thus marked field determines the dependent's state. Generally, only if the arbiter is true, will the dependent elements be mandatory. This behavior may only be assigned to boolean fields.
        /// </summary>
        Arbiter = 8,

        /// <summary>
        /// This behavior can be assigned together with the 'Mandatory' behavior. It means the field is only mandatory if an arbiter field is present and set to true.
        /// </summary>
        Conditional = 16,

        /// <summary>
        /// Converts content. Generally applied only to operators, but some methods may also convert information.
        /// </summary>
        Conversion = 32
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Parameter\ParameterContractType.cs
namespace Sqlcollaborative.Dbatools.Parameter
{
    /// <summary>
    /// Defines what kind of element is granted the contract
    /// </summary>
    public enum ParameterContractType
    {
        /// <summary>
        /// The contracted element is a field containing a value
        /// </summary>
        Field,

        /// <summary>
        /// The contracted element is a method, performing an action
        /// </summary>
        Method,

        /// <summary>
        /// The contracted element is an operator, facilitating type conversion. Generally into a dedicated object type this parameterclass abstracts.
        /// </summary>
        Operator
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Properties\AssemblyInfo.cs
using System.Reflection;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("dbatools")]
[assembly: AssemblyDescription("The dbatools PowerShell Module library")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("sqlcollaborative")]
[assembly: AssemblyProduct("dbatools")]
[assembly: AssemblyCopyright("Copyright ©  2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components.  If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("7a0d426f-2e98-4851-8af8-c1b2b17c052d")]

// Version information for an assembly consists of the following four values:
//
//      Major Version
//      Minor Version
//      Build Number
//      Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.10.0.57")]
[assembly: AssemblyFileVersion("0.10.0.57")]
tools\dbatools\bin\projects\dbatools\dbatools\Runspace\DbaRunspaceState.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Sqlcollaborative.Dbatools.Runspace
{
    /// <summary>
    /// Contains the state a managed, unique runspace can be in.
    /// </summary>
    public enum DbaRunspaceState
    {
        /// <summary>
        /// The runspace is up and running
        /// </summary>
        Running = 1,

        /// <summary>
        /// The runspace has received the stop order, but has not yet obeyed it
        /// </summary>
        Stopping = 2,

        /// <summary>
        /// The runspace has followed its order to stop and is currently disabled
        /// </summary>
        Stopped = 3,
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Runspace\RunspaceContainer.cs
using System;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Threading;

namespace Sqlcollaborative.Dbatools.Runspace
{
    /// <summary>
    /// Class that contains the logic necessary to manage a unique runspace
    /// </summary>
    public class RunspaceContainer
    {
        private ScriptBlock Script;

        private PowerShell Runspace;

        /// <summary>
        /// The name of the runspace.
        /// </summary>
        public readonly string Name;

        /// <summary>
        /// The Guid of the running Runspace
        /// </summary>
        public Guid RunspaceGuid
        {
            get { return Runspace.Runspace.InstanceId; }
        }

        /// <summary>
        /// Sets the script to execute in the runspace. Will NOT take immediate effect. Only after restarting the runspace will it be used.
        /// </summary>
        /// <param name="Script">The scriptblock to execute</param>
        public void SetScript(ScriptBlock Script)
        {
            this.Script = Script;
        }

        /// <summary>
        /// The state the runspace currently is in.
        /// </summary>
        public DbaRunspaceState State
        {
            get { return _State; }
        }
        private DbaRunspaceState _State = DbaRunspaceState.Stopped;

        /// <summary>
        /// Starts the Runspace.
        /// </summary>
        public void Start()
        {
            if ((Runspace != null) && (State == DbaRunspaceState.Stopped))
            {
                Kill();
            }

            if (Runspace == null)
            {
                Runspace = PowerShell.Create();
                try { SetName(Runspace.Runspace); }
                catch { }
                Runspace.AddScript(Script.ToString());
                _State = DbaRunspaceState.Running;
                try { Runspace.BeginInvoke(); }
                catch { _State = DbaRunspaceState.Stopped; }
            }
        }

        /// <summary>
        /// Sets the name on a runspace. This WILL FAIL for PowerShell v3!
        /// </summary>
        /// <param name="Runspace">The runspace to be named</param>
        private void SetName(System.Management.Automation.Runspaces.Runspace Runspace)
        {
            #if (NORUNSPACENAME)

            #else
            Runspace.Name = Name;
            #endif
        }

        /// <summary>
        /// Gracefully stops the Runspace
        /// </summary>
        public void Stop()
        {
            _State = DbaRunspaceState.Stopping;

            int i = 0;

            // Wait up to the limit for the running script to notice and kill itself
            if ((Runspace != null) && (Runspace.Runspace != null))
            {
                while ((Runspace.Runspace.RunspaceAvailability != RunspaceAvailability.Available) && (i < (10 * RunspaceHost.StopTimeoutSeconds)))
                {
                    i++;
                    Thread.Sleep(100);
                }
            }

            Kill();
        }

        /// <summary>
        /// Very ungracefully kills the runspace. Use only in the most dire emergency.
        /// </summary>
        public void Kill()
        {
            if (Runspace != null)
            {
                try { Runspace.Runspace.Close(); }
                catch { }
                Runspace.Dispose();
                Runspace = null;
            }

            _State = DbaRunspaceState.Stopped;
        }

        /// <summary>
        /// Signals the registered runspace has stopped execution
        /// </summary>
        public void SignalStopped()
        {
            _State = DbaRunspaceState.Stopped;
        }

        /// <summary>
        /// Creates a new runspace container with the basic information needed
        /// </summary>
        /// <param name="Name">The name of the Runspace</param>
        /// <param name="Script">The code using the runspace logic</param>
        public RunspaceContainer(string Name, ScriptBlock Script)
        {
            this.Name = Name.ToLower();
            this.Script = Script;
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Runspace\RunspaceHost.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Sqlcollaborative.Dbatools.Runspace
{
    /// <summary>
    /// Provides hosting for all registered runspaces
    /// </summary>
    public static class RunspaceHost
    {
        /// <summary>
        /// The number of seconds before a Stop command is interrupted and instead the runspace is gracelessly shut down.
        /// </summary>
        public static int StopTimeoutSeconds = 30;

        /// <summary>
        /// The dictionary containing the definitive list of unique Runspace
        /// </summary>
        public static Dictionary<string, RunspaceContainer> Runspaces = new Dictionary<string, RunspaceContainer>();
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\TabExpansion\InstanceAccess.cs
using System;

namespace Sqlcollaborative.Dbatools.TabExpansion
{
    /// <summary>
    /// Contains information on access to an instance
    /// </summary>
    public class InstanceAccess
    {
        /// <summary>
        /// The name of the instance to access
        /// </summary>
        public string InstanceName;

        /// <summary>
        /// Whether the account had sysadmin privileges. On multiple user usage, the cache will prefer sysadmin accounts.
        /// </summary>
        public bool IsSysAdmin;

        /// <summary>
        /// The actual connection object to connect with to the server
        /// </summary>
        public object ConnectionObject;

        /// <summary>
        /// When was the instance last accessed using dbatools
        /// </summary>
        public DateTime LastAccess;

        /// <summary>
        /// When was the instance's TEPP cache last updated
        /// </summary>
        public DateTime LastUpdate = new DateTime(1, 1, 1, 0, 0, 0);
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\TabExpansion\ScriptContainer.cs
using System;
using System.Management.Automation;

namespace Sqlcollaborative.Dbatools.TabExpansion
{
    /// <summary>
    /// Regular container to store scripts in, that are used in TEPP
    /// </summary>
    public class ScriptContainer
    {
        /// <summary>
        /// The name of the scriptblock
        /// </summary>
        public string Name;

        /// <summary>
        /// The scriptblock doing the logic
        /// </summary>
        public ScriptBlock ScriptBlock;

        /// <summary>
        /// The last time the scriptblock was called. Must be updated by the scriptblock itself
        /// </summary>
        public DateTime LastExecution;

        /// <summary>
        /// The time it took to run the last time
        /// </summary>
        public TimeSpan LastDuration;
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\TabExpansion\TabCompletionSet.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Sqlcollaborative.Dbatools.Utility;

namespace Sqlcollaborative.Dbatools.TabExpansion
{
    /// <summary>
    /// Contains information used to transmit Tepp Assignment
    /// </summary>
    public class TabCompletionSet
    {
        /// <summary>
        /// The name of the command to complete. "*" if all commands that have the parameter should be selected instead
        /// </summary>
        public string Command;

        /// <summary>
        /// The parameter to complete
        /// </summary>
        public string Parameter;

        /// <summary>
        /// The name of the script to complete with
        /// </summary>
        public string Script;

        /// <summary>
        /// Creates a new tab completion set object with all information prefilled
        /// </summary>
        /// <param name="Command">The name of the command to complete. "*" if all commands that have the parameter should be selected instead</param>
        /// <param name="Parameter">The parameter to complete</param>
        /// <param name="Script">The name of the script to complete with</param>
        public TabCompletionSet(string Command, string Parameter, string Script)
        {
            this.Command = Command;
            this.Parameter = Parameter;
            this.Script = Script;
        }

        /// <summary>
        /// Tests, whether the completion set applies to the specified parameter / command combination
        /// </summary>
        /// <param name="Command">The command to test</param>
        /// <param name="Parameter">The parameter of the command to test</param>
        /// <returns>Whether this completion set applies to the specified combination of parameter / command</returns>
        public bool Applies(string Command, string Parameter)
        {
            if ((UtilityHost.IsLike(Command, this.Command)) && (UtilityHost.IsLike(Parameter, this.Parameter)))
                return true;
            return false;
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\TabExpansion\TabExpansionHost.cs
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Threading;

namespace Sqlcollaborative.Dbatools.TabExpansion
{
    /// <summary>
    /// Class that handles the static fields supporting the dbatools TabExpansion implementation
    /// </summary>
    public static class TabExpansionHost
    {
        #region State information
        /// <summary>
        /// Field containing the scripts that were registered.
        /// </summary>
        public static ConcurrentDictionary<string, ScriptContainer> Scripts = new ConcurrentDictionary<string, ScriptContainer>();

        /// <summary>
        /// The cache used by scripts utilizing TabExpansionPlusPlus in dbatools
        /// </summary>
        public static Hashtable Cache = new Hashtable();

        /// <summary>
        /// List of instances and when they were last accessed
        /// </summary>
        public static ConcurrentDictionary<string, InstanceAccess> InstanceAccess = new ConcurrentDictionary<string, InstanceAccess>();

        /// <summary>
        /// Scripts that build the cache and are suitable for synchronous execution
        /// </summary>
        public static List<ScriptBlock> TeppGatherScriptsFast = new List<ScriptBlock>();

        /// <summary>
        /// Scripts that build the cache and are not suitable for synchronous execution
        /// </summary>
        public static List<ScriptBlock> TeppGatherScriptsSlow = new List<ScriptBlock>();

        /// <summary>
        /// A list of all commands imported into dbatools
        /// </summary>
        public static List<FunctionInfo> DbatoolsCommands = new List<FunctionInfo>();

        /// <summary>
        /// List of completion sets that should be processed into Tepp Assignments. Only populate this list on first import.
        /// </summary>
        public static List<TabCompletionSet> TabCompletionSets = new List<TabCompletionSet>();

        /// <summary>
        /// Maps a TEPP scriptblock to a command and parameter
        /// </summary>
        public static Dictionary<string, Dictionary<string, ScriptContainer>> TeppAssignment = new Dictionary<string, Dictionary<string, ScriptContainer>>();
        #endregion State information

        #region Utility methods
        /// <summary>
        /// Registers a new instance or updates an already existing one. Should only be called from Connect-SqlInstance and Connect-DbaSqlServer
        /// </summary>
        /// <param name="InstanceName">Name of the instance connected to</param>
        /// <param name="Connection">To connection object containing the relevant information for accessing the instance</param>
        /// <param name="IsSysAdmin">Whether the account connecting to the instnace has SA privileges</param>
        public static void SetInstance(string InstanceName, object Connection, bool IsSysAdmin)
        {
            string tempName = InstanceName.ToLower();

            if (!InstanceAccess.ContainsKey(tempName))
            {
                InstanceAccess tempAccess = new InstanceAccess();
                tempAccess.InstanceName = tempName;
                tempAccess.LastAccess = DateTime.Now;
                tempAccess.ConnectionObject = Connection;
                tempAccess.IsSysAdmin = IsSysAdmin;
                InstanceAccess[tempName] = tempAccess;
            }
            else
            {
                InstanceAccess[tempName].LastAccess = DateTime.Now;

                if (IsSysAdmin & !InstanceAccess[tempName].IsSysAdmin)
                {
                    InstanceAccess[tempName].ConnectionObject = Connection;
                    InstanceAccess[tempName].IsSysAdmin = IsSysAdmin;
                }
            }
        }

        /// <summary>
        /// Returns the assigned scriptblock for a given parameter
        /// </summary>
        /// <param name="Command">The command that should be completed</param>
        /// <param name="Parameter">The parameter completion is provided for</param>
        /// <returns>Either the relevant script container or null</returns>
        public static ScriptContainer GetTeppScript(string Command, string Parameter)
        {
            if (TeppAssignment.ContainsKey(Command) && TeppAssignment[Command].ContainsKey(Parameter))
                return TeppAssignment[Command][Parameter];
            return null;
        }

        /// <summary>
        /// Assigns a registered script to the parameter of a command
        /// </summary>
        /// <param name="Command">The command for which to complete</param>
        /// <param name="Parameter">The parameter for which to complete</param>
        /// <param name="Script">To name of the script with which to complete</param>
        public static void SetTeppScript(string Command, string Parameter, string Script)
        {
            if (!Scripts.ContainsKey(Script))
                return;

            if (!TeppAssignment.ContainsKey(Command))
                TeppAssignment[Command] = new Dictionary<string, ScriptContainer>();

            TeppAssignment[Command][Parameter] = Scripts[Script];
        }

        /// <summary>
        /// Adds a completion set to the list of items to process
        /// </summary>
        /// <param name="Command">The command to complete for (accepts wildcard matching)</param>
        /// <param name="Parameter">The parameter to complete for (accepts wildcard matching)</param>
        /// <param name="Script">The script to register</param>
        public static void AddTabCompletionSet(string Command, string Parameter, string Script)
        {
            // Only import on the first import
            if (!dbaSystem.SystemHost.ModuleImported)
                TabCompletionSets.Add(new TabCompletionSet(Command, Parameter, Script));
        }

        /// <summary>
        /// Processes the content of TabCompletionSets and Scripts into TappAssignments based on the DbatoolsCommands list.
        /// </summary>
        public static void CalculateTabExpansion()
        {
            foreach (FunctionInfo info in DbatoolsCommands)
                foreach (ParameterMetadata paramInfo in info.Parameters.Values)
                    foreach (TabCompletionSet set in TabCompletionSets)
                        if (set.Applies(info.Name, paramInfo.Name))
                            SetTeppScript(info.Name, paramInfo.Name, set.Script);
        }
        #endregion Utility methods

        #region Configuration
        /// <summary>
        /// Whether TEPP in its entirety is disabled
        /// </summary>
        public static bool TeppDisabled = false;

        /// <summary>
        /// Whether asynchronous TEPP updating should be disabled
        /// </summary>
        public static bool TeppAsyncDisabled = false;

        /// <summary>
        /// Whether synchronous TEPP updating should be disabled
        /// </summary>
        public static bool TeppSyncDisabled = true;

        /// <summary>
        /// The interval in which asynchronous TEPP cache updates are performed
        /// </summary>
        public static TimeSpan TeppUpdateInterval = new TimeSpan(0, 3, 0);

        /// <summary>
        /// After this timespan of no requests to a server, the updates to its cache are disabled.
        /// </summary>
        public static TimeSpan TeppUpdateTimeout = new TimeSpan(0, 0, 30);
        #endregion Configuration
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\TypeConversion\DbaCredentialParameterConverter.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Management.Automation;
using System.Text;
using System.Threading.Tasks;
using Sqlcollaborative.Dbatools.Parameter;

namespace Sqlcollaborative.Dbatools.TypeConversion
{
    /// <summary>
    /// Converts to and from DbaCredentialparameter
    /// </summary>
    public class DbaCredentialParameterConverter : PSTypeConverter
    {
        /// <summary>
        /// Verifies, whether a conversion for the object to the target type is possible
        /// </summary>
        /// <param name="SourceValue">The object to convert</param>
        /// <param name="DestinationType">The type to convert to</param>
        /// <returns>Whether it's possible, duh!</returns>
        public override bool CanConvertTo(object SourceValue, Type DestinationType)
        {
            if (SourceValue == null)
                return false;
            if (!(SourceValue is DbaCredentialParameter))
                return false;
            return IsSupportedType(DestinationType);
        }

        /// <summary>
        /// Converts from DbaCredentialparameter to whatever destination type is attempted
        /// </summary>
        /// <param name="sourceValue">The source object. Better be a DbaCredentialparameter!</param>
        /// <param name="destinationType">Should be a supported destination type</param>
        /// <param name="formatProvider">Irrelevant</param>
        /// <param name="ignoreCase">Irrelevant</param>
        /// <returns>The target content type</returns>
        public override object ConvertTo(object sourceValue, Type destinationType, IFormatProvider formatProvider, bool ignoreCase)
        {
            if (!CanConvertTo(sourceValue, destinationType))
                throw new ArgumentException("Conversion not supported!");

            switch (destinationType.FullName)
            {
                case "System.Net.NetworkCredential":
                    return (System.Net.NetworkCredential)sourceValue;
                case "System.Management.Automation.PSCredential":
                    return (PSCredential)sourceValue;
                default:
                    throw new InvalidCastException(String.Format("Cannot convert from {0} to {1}!", sourceValue.GetType().FullName, destinationType.FullName));
            }
        }

        /// <summary>
        /// Verifies, whether a conversion for the object from the source type to DbaCredentialParameter is possible
        /// </summary>
        /// <param name="SourceValue">The object to convert</param>
        /// <param name="DestinationType">The source type to convert to</param>
        /// <returns>Whether it's possible, duh!</returns>
        public override bool CanConvertFrom(object SourceValue, Type DestinationType)
        {
            if (DestinationType.FullName != "Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter")
                return false;
            if (SourceValue == null)
                return false;

            return IsSupportedType(SourceValue.GetType());
        }

        /// <summary>
        /// Converts a source object to DbaCredentialparameter
        /// </summary>
        /// <param name="sourceValue">The source object</param>
        /// <param name="destinationType">The destination type. Must be DbaCredentialParameter, or red stuff happens</param>
        /// <param name="formatProvider">Irrelevant</param>
        /// <param name="ignoreCase">Irrelevant</param>
        /// <returns></returns>
        public override object ConvertFrom(object sourceValue, Type destinationType, IFormatProvider formatProvider, bool ignoreCase)
        {
            if (!CanConvertFrom(sourceValue, destinationType))
                throw new ArgumentException("Conversion not supported!");

            return new DbaCredentialParameter(sourceValue);
        }

        /// <summary>
        /// Returns, whether a given type is supported for conversion
        /// </summary>
        /// <param name="type">The type to validate</param>
        /// <returns>Whether it's a supported conversion</returns>
        private bool IsSupportedType(Type type)
        {
            switch (type.FullName)
            {
                case "Sqlcollaborative.Dbatools.Parameter.DbaCredentialParameter":
                    return true;
                case "System.Net.NetworkCredential":
                    return true;
                case "System.Management.Automation.PSCredential":
                    return true;
                default:
                    return false;
            }
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Utility\CredentialPrompt.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;

namespace Sqlcollaborative.Dbatools.Utility
{
    /// <summary>
    /// Dedicated class to prompt for credentials
    /// </summary>
    public class CredentialPrompt
    {
        /// <summary>
        /// The name of the user to pre-fill the username field
        /// </summary>
        public string Name;

        /// <summary>
        /// The window of the prompt that was shown
        /// </summary>
        public Window Window;

        private PasswordBox password;

        /// <summary>
        /// The final, resulting username
        /// </summary>
        public string Username;

        /// <summary>
        /// The final, result password
        /// </summary>
        public SecureString Password;

        /// <summary>
        /// Whether the password should be remembered
        /// </summary>
        public bool Remember;

        /// <summary>
        /// Marker indicating that execution has finished
        /// </summary>
        public bool Finished = false;

        /// <summary>
        /// Whether the windoww was cancelled
        /// </summary>
        public bool Cancelled = false;

        /// <summary>
        /// Start asking away
        /// </summary>
        public void PromptForCredential()
        {
            Window window = new Window();
            window.Name = "MainWindow";
            window.Title = "Enter Credentials";
            window.Height = 210;
            window.Width = 300;
            window.Icon = null;
            window.Loaded += window_loaded;
            this.Window = window;

            Grid grid = new Grid();
            window.Content = grid;

            Label label_username = new Label();
            label_username.Content = "Username";
            label_username.Margin = new Thickness(10, 10, 0, 0);
            label_username.VerticalAlignment = VerticalAlignment.Top;
            label_username.HorizontalAlignment = HorizontalAlignment.Left;
            label_username.Height = 23;
            label_username.VerticalContentAlignment = VerticalAlignment.Bottom;
            label_username.HorizontalContentAlignment = HorizontalAlignment.Left;
            grid.Children.Add(label_username);

            TextBox tb_username = new TextBox();
            tb_username.Name = "tb_username";
            tb_username.Text = Name;
            tb_username.Margin = new Thickness(10, 33, 10, 0);
            tb_username.Height = 30;
            tb_username.BorderThickness = new Thickness(2);
            tb_username.VerticalAlignment = VerticalAlignment.Top;
            tb_username.VerticalContentAlignment = VerticalAlignment.Center;
            tb_username.HorizontalContentAlignment = HorizontalAlignment.Center;
            grid.Children.Add(tb_username);

            Label label_password = new Label();
            label_password.Content = "Password";
            label_password.Margin = new Thickness(10, 68, 0, 0);
            label_password.VerticalAlignment = VerticalAlignment.Top;
            label_password.HorizontalAlignment = HorizontalAlignment.Left;
            label_password.Height = 23;
            label_password.VerticalContentAlignment = VerticalAlignment.Bottom;
            label_password.HorizontalContentAlignment = HorizontalAlignment.Left;
            grid.Children.Add(label_password);

            PasswordBox tb_password = new PasswordBox();
            tb_password.Name = "tb_password";
            tb_password.Margin = new Thickness(10, 91, 10, 0);
            tb_password.Height = 30;
            tb_password.BorderThickness = new Thickness(2);
            tb_password.VerticalAlignment = VerticalAlignment.Top;
            tb_password.VerticalContentAlignment = VerticalAlignment.Center;
            tb_password.HorizontalContentAlignment = HorizontalAlignment.Center;
            grid.Children.Add(tb_password);
            password = tb_password;

            CheckBox cb_remember = new CheckBox();
            cb_remember.Name = "cb_remember";
            cb_remember.Content = "Remember Password";
            cb_remember.HorizontalAlignment = HorizontalAlignment.Left;
            cb_remember.Margin = new Thickness(10, 126, 0, 0);
            cb_remember.VerticalAlignment = VerticalAlignment.Top;
            grid.Children.Add(cb_remember);
            
            Button b_ok = new Button();
            b_ok.Name = "b_ok";
            b_ok.Content = "OK";
            b_ok.Margin = new Thickness(10, 150, 0, 0);
            b_ok.HorizontalAlignment = HorizontalAlignment.Left;
            b_ok.VerticalAlignment = VerticalAlignment.Top;
            b_ok.Width = 75;
            b_ok.IsDefault = true;
            b_ok.Click += button_ok_Click;
            grid.Children.Add(b_ok);

            Button b_cancel = new Button();
            b_cancel.Name = "b_cancel";
            b_cancel.Content = "Cancel";
            b_cancel.Margin = new Thickness(0, 150, 10, 0);
            b_cancel.HorizontalAlignment = HorizontalAlignment.Right;
            b_cancel.VerticalAlignment = VerticalAlignment.Top;
            b_cancel.Width = 75;
            b_cancel.IsCancel = true;
            b_cancel.Click += button_cancel_Click;
            grid.Children.Add(b_cancel);

            try
            {
                window.ShowDialog();
                Username = tb_username.Text;
                Password = tb_password.SecurePassword;
                Remember = (bool)cb_remember.IsChecked;
            }
            catch { }
            Finished = true;
        }

        #region EventHandler
        private void button_ok_Click(object sender, RoutedEventArgs e)
        {
            Window.Close();
        }

        private void button_cancel_Click(object sender, RoutedEventArgs e)
        {
            Cancelled = true;
        }
        
        private void window_loaded(object sender, EventArgs e)
        {
            Window.Activate();
            password.Focus();
        }
        #endregion EventHandler

        /// <summary>
        /// Executes a request for credentials on a dedicated STA thread
        /// </summary>
        /// <param name="Name">THe name of the user to put in the prompt</param>
        /// <returns>The result object</returns>
        public static CredentialPrompt GetCredential(string Name)
        {
            CredentialPrompt temp = new CredentialPrompt();
            temp.Name = Name;
            Thread thread = new Thread(new ThreadStart(temp.PromptForCredential));
            thread.SetApartmentState(ApartmentState.STA);
            thread.Start();
            while (!thread.IsAlive)
                Thread.Sleep(1);
            thread.Join();
            return temp;
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Utility\DateTimeExtension.cs
using System;

namespace Sqlcollaborative.Dbatools.Utility
{
    /// <summary>
    /// Extends DateTime
    /// </summary>
    public static class DateTimeExtension
    {
        /// <summary>
        /// Adds a compareTo method to DateTime to compare with DbaDateTimeBase
        /// </summary>
        /// <param name="Base">The extended DateTime object</param>
        /// <param name="comparedTo">The DbaDateTimeBase to compare with</param>
        /// <returns></returns>
        public static int CompareTo(this DateTime Base, DbaDateTimeBase comparedTo)
        {
            return Base.CompareTo(comparedTo);
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Utility\DbaDate.cs
using System;

namespace Sqlcollaborative.Dbatools.Utility
{
    /// <summary>
    /// A dbatools-internal datetime wrapper for neater display
    /// </summary>
    public class DbaDate : DbaDateTimeBase
    {
        #region Constructors
        /// <summary>
        /// Constructs a generic timestamp object wrapper from an input timestamp object.
        /// </summary>
        /// <param name="Timestamp">The timestamp to wrap</param>
        public DbaDate(DateTime Timestamp)
        {
            _timestamp = Timestamp;
        }

        /// <summary>
        /// Parses a string into a datetime object.
        /// </summary>
        /// <param name="Time">The time-string to parse</param>
        public DbaDate(string Time)
        {
            _timestamp = ParseDateTime(Time);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ticks"></param>
        public DbaDate(long ticks)
        {
            _timestamp = new DateTime(ticks);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ticks"></param>
        /// <param name="kind"></param>
        public DbaDate(long ticks, System.DateTimeKind kind)
        {
            _timestamp = new DateTime(ticks, kind);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        public DbaDate(int year, int month, int day)
        {
            _timestamp = new DateTime(year, month, day);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        /// <param name="calendar"></param>
        public DbaDate(int year, int month, int day, System.Globalization.Calendar calendar)
        {
            _timestamp = new DateTime(year, month, day, calendar);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        /// <param name="hour"></param>
        /// <param name="minute"></param>
        /// <param name="second"></param>
        public DbaDate(int year, int month, int day, int hour, int minute, int second)
        {
            _timestamp = new DateTime(year, month, day, hour, minute, second);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        /// <param name="hour"></param>
        /// <param name="minute"></param>
        /// <param name="second"></param>
        /// <param name="kind"></param>
        public DbaDate(int year, int month, int day, int hour, int minute, int second, System.DateTimeKind kind)
        {
            _timestamp = new DateTime(year, month, day, hour, minute, second, kind);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        /// <param name="hour"></param>
        /// <param name="minute"></param>
        /// <param name="second"></param>
        /// <param name="calendar"></param>
        public DbaDate(int year, int month, int day, int hour, int minute, int second, System.Globalization.Calendar calendar)
        {
            _timestamp = new DateTime(year, month, day, hour, minute, second, calendar);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        /// <param name="hour"></param>
        /// <param name="minute"></param>
        /// <param name="second"></param>
        /// <param name="millisecond"></param>
        public DbaDate(int year, int month, int day, int hour, int minute, int second, int millisecond)
        {
            _timestamp = new DateTime(year, month, day, hour, minute, second, millisecond);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        /// <param name="hour"></param>
        /// <param name="minute"></param>
        /// <param name="second"></param>
        /// <param name="millisecond"></param>
        /// <param name="kind"></param>
        public DbaDate(int year, int month, int day, int hour, int minute, int second, int millisecond, System.DateTimeKind kind)
        {
            _timestamp = new DateTime(year, month, day, hour, minute, second, millisecond, kind);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        /// <param name="hour"></param>
        /// <param name="minute"></param>
        /// <param name="second"></param>
        /// <param name="millisecond"></param>
        /// <param name="calendar"></param>
        public DbaDate(int year, int month, int day, int hour, int minute, int second, int millisecond, System.Globalization.Calendar calendar)
        {
            _timestamp = new DateTime(year, month, day, hour, minute, second, millisecond, calendar);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        /// <param name="hour"></param>
        /// <param name="minute"></param>
        /// <param name="second"></param>
        /// <param name="millisecond"></param>
        /// <param name="calendar"></param>
        /// <param name="kind"></param>
        public DbaDate(int year, int month, int day, int hour, int minute, int second, int millisecond, System.Globalization.Calendar calendar, System.DateTimeKind kind)
        {
            _timestamp = new DateTime(year, month, day, hour, minute, second, millisecond, calendar, kind);
        }
        #endregion Constructors

        /// <summary>
        /// Provids the default-formated string, using the defined default formatting.
        /// </summary>
        /// <returns>Formatted datetime-string</returns>
        public override string ToString()
        {
            if (UtilityHost.DisableCustomDateTime) { return _timestamp.ToString(); }
            return _timestamp.ToString(UtilityHost.FormatDate);
        }

        #region Implicit Conversions
        /// <summary>
        /// Implicitly convert to DateTime
        /// </summary>
        /// <param name="Base">The source object to convert</param>
        public static implicit operator DateTime(DbaDate Base)
        {
            return Base.GetBaseObject();
        }

        /// <summary>
        /// Implicitly convert from DateTime
        /// </summary>
        /// <param name="Base">The object to convert</param>
        public static implicit operator DbaDate(DateTime Base)
        {
            return new DbaDate(Base);
        }

        /// <summary>
        /// Implicitly convert to DbaDate
        /// </summary>
        /// <param name="Base">The source object to convert</param>
        public static implicit operator DbaDateTime(DbaDate Base)
        {
            return new DbaDateTime(Base.GetBaseObject());
        }

        /// <summary>
        /// Implicitly convert to DbaTime
        /// </summary>
        /// <param name="Base">The source object to convert</param>
        public static implicit operator DbaTime(DbaDate Base)
        {
            return new DbaTime(Base.GetBaseObject());
        }
        #endregion Implicit Conversions

        #region Statics
        /// <summary>
        /// Generates a DbaDate object based off DateTime object. Will be null if Base is the start value (Tickes == 0).
        /// </summary>
        /// <param name="Base">The Datetime to base it off</param>
        /// <returns>The object to generate (or null)</returns>
        public static DbaDate Generate(DateTime Base)
        {
            if (Base.Ticks == 0)
                return null;
            else
                return new DbaDate(Base);
        }
        #endregion Statics
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Utility\DbaDateTime.cs
using System;

namespace Sqlcollaborative.Dbatools.Utility
{
    /// <summary>
    /// A dbatools-internal datetime wrapper for neater display
    /// </summary>
    public class DbaDateTime : DbaDateTimeBase
    {
        #region Constructors
        /// <summary>
        /// Constructs a generic timestamp object wrapper from an input timestamp object.
        /// </summary>
        /// <param name="Timestamp">The timestamp to wrap</param>
        public DbaDateTime(DateTime Timestamp)
        {
            _timestamp = Timestamp;
        }

        /// <summary>
        /// Parses a string into a datetime object.
        /// </summary>
        /// <param name="Time">The time-string to parse</param>
        public DbaDateTime(string Time)
        {
            _timestamp = ParseDateTime(Time);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ticks"></param>
        public DbaDateTime(long ticks)
        {
            _timestamp = new DateTime(ticks);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ticks"></param>
        /// <param name="kind"></param>
        public DbaDateTime(long ticks, System.DateTimeKind kind)
        {
            _timestamp = new DateTime(ticks, kind);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        public DbaDateTime(int year, int month, int day)
        {
            _timestamp = new DateTime(year, month, day);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        /// <param name="calendar"></param>
        public DbaDateTime(int year, int month, int day, System.Globalization.Calendar calendar)
        {
            _timestamp = new DateTime(year, month, day, calendar);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        /// <param name="hour"></param>
        /// <param name="minute"></param>
        /// <param name="second"></param>
        public DbaDateTime(int year, int month, int day, int hour, int minute, int second)
        {
            _timestamp = new DateTime(year, month, day, hour, minute, second);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        /// <param name="hour"></param>
        /// <param name="minute"></param>
        /// <param name="second"></param>
        /// <param name="kind"></param>
        public DbaDateTime(int year, int month, int day, int hour, int minute, int second, System.DateTimeKind kind)
        {
            _timestamp = new DateTime(year, month, day, hour, minute, second, kind);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        /// <param name="hour"></param>
        /// <param name="minute"></param>
        /// <param name="second"></param>
        /// <param name="calendar"></param>
        public DbaDateTime(int year, int month, int day, int hour, int minute, int second, System.Globalization.Calendar calendar)
        {
            _timestamp = new DateTime(year, month, day, hour, minute, second, calendar);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        /// <param name="hour"></param>
        /// <param name="minute"></param>
        /// <param name="second"></param>
        /// <param name="millisecond"></param>
        public DbaDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond)
        {
            _timestamp = new DateTime(year, month, day, hour, minute, second, millisecond);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        /// <param name="hour"></param>
        /// <param name="minute"></param>
        /// <param name="second"></param>
        /// <param name="millisecond"></param>
        /// <param name="kind"></param>
        public DbaDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, System.DateTimeKind kind)
        {
            _timestamp = new DateTime(year, month, day, hour, minute, second, millisecond, kind);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        /// <param name="hour"></param>
        /// <param name="minute"></param>
        /// <param name="second"></param>
        /// <param name="millisecond"></param>
        /// <param name="calendar"></param>
        public DbaDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, System.Globalization.Calendar calendar)
        {
            _timestamp = new DateTime(year, month, day, hour, minute, second, millisecond, calendar);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        /// <param name="hour"></param>
        /// <param name="minute"></param>
        /// <param name="second"></param>
        /// <param name="millisecond"></param>
        /// <param name="calendar"></param>
        /// <param name="kind"></param>
        public DbaDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, System.Globalization.Calendar calendar, System.DateTimeKind kind)
        {
            _timestamp = new DateTime(year, month, day, hour, minute, second, millisecond, calendar, kind);
        }
        #endregion Constructors

        /// <summary>
        /// Provids the default-formated string, using the defined default formatting.
        /// </summary>
        /// <returns>Formatted datetime-string</returns>
        public override string ToString()
        {
            if (UtilityHost.DisableCustomDateTime) { return _timestamp.ToString(); }
            return _timestamp.ToString(UtilityHost.FormatDateTime);
        }

        #region Implicit Conversions
        /// <summary>
        /// Implicitly convert to DateTime
        /// </summary>
        /// <param name="Base">The source object to convert</param>
        public static implicit operator DateTime(DbaDateTime Base)
        {
            return Base.GetBaseObject();
        }

        /// <summary>
        /// Implicitly convert from DateTime
        /// </summary>
        /// <param name="Base">The object to convert</param>
        public static implicit operator DbaDateTime(DateTime Base)
        {
            return new DbaDateTime(Base);
        }

        /// <summary>
        /// Implicitly convert to DbaDate
        /// </summary>
        /// <param name="Base">The source object to convert</param>
        public static implicit operator DbaDate(DbaDateTime Base)
        {
            return new DbaDate(Base.GetBaseObject());
        }

        /// <summary>
        /// Implicitly convert to DbaTime
        /// </summary>
        /// <param name="Base">The source object to convert</param>
        public static implicit operator DbaTime(DbaDateTime Base)
        {
            return new DbaTime(Base.GetBaseObject());
        }
        #endregion Implicit Conversions

        #region Statics
        /// <summary>
        /// Generates a DbaDateTime object based off DateTime object. Will be null if Base is the start value (Tickes == 0).
        /// </summary>
        /// <param name="Base">The Datetime to base it off</param>
        /// <returns>The object to generate (or null)</returns>
        public static DbaDateTime Generate(DateTime Base)
        {
            if (Base.Ticks == 0)
                return null;
            else
                return new DbaDateTime(Base);
        }
        #endregion Statics
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Utility\DbaDateTimeBase.cs
using System;
using System.Globalization;
using System.Text.RegularExpressions;

namespace Sqlcollaborative.Dbatools.Utility
{
    /// <summary>
    /// Base class for wrapping around a DateTime object
    /// </summary>
    public class DbaDateTimeBase : IComparable, IComparable<DateTime>, IEquatable<DateTime> // IFormattable,
    {
        #region Properties
        /// <summary>
        /// The core resource, containing the actual timestamp
        /// </summary>
        internal DateTime _timestamp;

        /// <summary>
        /// Gets the date component of this instance.
        /// </summary>
        public DateTime Date
        {
            get { return _timestamp.Date; }
        }

        /// <summary>
        /// Gets the day of the month represented by this instance.
        /// </summary>
        public int Day
        {
            get { return _timestamp.Day; }
        }

        /// <summary>
        /// Gets the day of the week represented by this instance.
        /// </summary>
        public DayOfWeek DayOfWeek
        {
            get { return _timestamp.DayOfWeek; }
        }

        /// <summary>
        /// Gets the day of the year represented by this instance.
        /// </summary>
        public int DayOfYear
        {
            get { return _timestamp.DayOfYear; }
        }

        /// <summary>
        /// Gets the hour component of the date represented by this instance.
        /// </summary>
        public int Hour
        {
            get { return _timestamp.Hour; }
        }

        /// <summary>
        /// Gets a value that indicates whether the time represented by this instance is based on local time, Coordinated Universal Time (UTC), or neither.
        /// </summary>
        public DateTimeKind Kind
        {
            get { return _timestamp.Kind; }
        }

        /// <summary>
        /// Gets the milliseconds component of the date represented by this instance.
        /// </summary>
        public int Millisecond
        {
            get { return _timestamp.Millisecond; }
        }

        /// <summary>
        /// Gets the minute component of the date represented by this instance.
        /// </summary>
        public int Minute
        {
            get { return _timestamp.Minute; }
        }

        /// <summary>
        /// Gets the month component of the date represented by this instance.
        /// </summary>
        public int Month
        {
            get { return _timestamp.Month; }
        }

        /// <summary>
        /// Gets the seconds component of the date represented by this instance.
        /// </summary>
        public int Second
        {
            get { return _timestamp.Second; }
        }

        /// <summary>
        /// Gets the number of ticks that represent the date and time of this instance.
        /// </summary>
        public long Ticks
        {
            get { return _timestamp.Ticks; }
        }

        /// <summary>
        /// Gets the time of day for this instance.
        /// </summary>
        public TimeSpan TimeOfDay
        {
            get { return _timestamp.TimeOfDay; }
        }

        /// <summary>
        /// Gets the year component of the date represented by this instance.
        /// </summary>
        public int Year
        {
            get { return _timestamp.Year; }
        }
        #endregion Properties

        #region Constructors
        /// <summary>
        /// Constructor that should never be called, since this class should never be instantiated. It's there for implicit calls on child classes.
        /// </summary>
        public DbaDateTimeBase()
        {

        }

        /// <summary>
        /// Constructs a generic timestamp object wrapper from an input timestamp object.
        /// </summary>
        /// <param name="Timestamp">The timestamp to wrap</param>
        public DbaDateTimeBase(DateTime Timestamp)
        {
            _timestamp = Timestamp;
        }

        /// <summary>
        /// Parses a string into a datetime object.
        /// </summary>
        /// <param name="Time">The time-string to parse</param>
        public DbaDateTimeBase(string Time)
        {
            _timestamp = ParseDateTime(Time);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ticks"></param>
        public DbaDateTimeBase(long ticks)
        {
            _timestamp = new DateTime(ticks);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ticks"></param>
        /// <param name="kind"></param>
        public DbaDateTimeBase(long ticks, System.DateTimeKind kind)
        {
            _timestamp = new DateTime(ticks, kind);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        public DbaDateTimeBase(int year, int month, int day)
        {
            _timestamp = new DateTime(year, month, day);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        /// <param name="calendar"></param>
        public DbaDateTimeBase(int year, int month, int day, System.Globalization.Calendar calendar)
        {
            _timestamp = new DateTime(year, month, day, calendar);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        /// <param name="hour"></param>
        /// <param name="minute"></param>
        /// <param name="second"></param>
        public DbaDateTimeBase(int year, int month, int day, int hour, int minute, int second)
        {
            _timestamp = new DateTime(year, month, day, hour, minute, second);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        /// <param name="hour"></param>
        /// <param name="minute"></param>
        /// <param name="second"></param>
        /// <param name="kind"></param>
        public DbaDateTimeBase(int year, int month, int day, int hour, int minute, int second, System.DateTimeKind kind)
        {
            _timestamp = new DateTime(year, month, day, hour, minute, second, kind);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        /// <param name="hour"></param>
        /// <param name="minute"></param>
        /// <param name="second"></param>
        /// <param name="calendar"></param>
        public DbaDateTimeBase(int year, int month, int day, int hour, int minute, int second, System.Globalization.Calendar calendar)
        {
            _timestamp = new DateTime(year, month, day, hour, minute, second, calendar);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        /// <param name="hour"></param>
        /// <param name="minute"></param>
        /// <param name="second"></param>
        /// <param name="millisecond"></param>
        public DbaDateTimeBase(int year, int month, int day, int hour, int minute, int second, int millisecond)
        {
            _timestamp = new DateTime(year, month, day, hour, minute, second, millisecond);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        /// <param name="hour"></param>
        /// <param name="minute"></param>
        /// <param name="second"></param>
        /// <param name="millisecond"></param>
        /// <param name="kind"></param>
        public DbaDateTimeBase(int year, int month, int day, int hour, int minute, int second, int millisecond, System.DateTimeKind kind)
        {
            _timestamp = new DateTime(year, month, day, hour, minute, second, millisecond, kind);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        /// <param name="hour"></param>
        /// <param name="minute"></param>
        /// <param name="second"></param>
        /// <param name="millisecond"></param>
        /// <param name="calendar"></param>
        public DbaDateTimeBase(int year, int month, int day, int hour, int minute, int second, int millisecond, System.Globalization.Calendar calendar)
        {
            _timestamp = new DateTime(year, month, day, hour, minute, second, millisecond, calendar);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        /// <param name="hour"></param>
        /// <param name="minute"></param>
        /// <param name="second"></param>
        /// <param name="millisecond"></param>
        /// <param name="calendar"></param>
        /// <param name="kind"></param>
        public DbaDateTimeBase(int year, int month, int day, int hour, int minute, int second, int millisecond, System.Globalization.Calendar calendar, System.DateTimeKind kind)
        {
            _timestamp = new DateTime(year, month, day, hour, minute, second, millisecond, calendar, kind);
        }
        #endregion Constructors

        #region Methods
        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public DateTime Add(TimeSpan value)
        {
            return _timestamp.Add(value);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public DateTime AddDays(double value)
        {
            return _timestamp.AddDays(value);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public DateTime AddHours(double value)
        {
            return _timestamp.AddHours(value);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public DateTime AddMilliseconds(double value)
        {
            return _timestamp.AddMilliseconds(value);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public DateTime AddMinutes(double value)
        {
            return _timestamp.AddMinutes(value);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="months"></param>
        /// <returns></returns>
        public DateTime AddMonths(int months)
        {
            return _timestamp.AddMonths(months);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public DateTime AddSeconds(double value)
        {
            return _timestamp.AddSeconds(value);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public DateTime AddTicks(long value)
        {
            return _timestamp.AddTicks(value);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public DateTime AddYears(int value)
        {
            return _timestamp.AddYears(value);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public int CompareTo(System.Object value)
        {
            return _timestamp.CompareTo(value);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public int CompareTo(DateTime value)
        {
            return _timestamp.CompareTo(value);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public override bool Equals(System.Object value)
        {
            return _timestamp.Equals(value);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public bool Equals(DateTime value)
        {
            return _timestamp.Equals(value);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public string[] GetDateTimeFormats()
        {
            return _timestamp.GetDateTimeFormats();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="provider"></param>
        /// <returns></returns>
        public string[] GetDateTimeFormats(System.IFormatProvider provider)
        {
            return _timestamp.GetDateTimeFormats(provider);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="format"></param>
        /// <returns></returns>
        public string[] GetDateTimeFormats(char format)
        {
            return _timestamp.GetDateTimeFormats(format);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="format"></param>
        /// <param name="provider"></param>
        /// <returns></returns>
        public string[] GetDateTimeFormats(char format, System.IFormatProvider provider)
        {
            return _timestamp.GetDateTimeFormats(format, provider);
        }

        /// <summary>
        /// Retrieve base DateTime object, this is a wrapper for
        /// </summary>
        /// <returns>Base DateTime object</returns>
        public DateTime GetBaseObject()
        {
            return _timestamp;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return _timestamp.GetHashCode();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public System.TypeCode GetTypeCode()
        {
            return _timestamp.GetTypeCode();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public bool IsDaylightSavingTime()
        {
            return _timestamp.IsDaylightSavingTime();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public TimeSpan Subtract(DateTime value)
        {
            return _timestamp.Subtract(value);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public DateTime Subtract(TimeSpan value)
        {
            return _timestamp.Subtract(value);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public long ToBinary()
        {
            return _timestamp.ToBinary();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public long ToFileTime()
        {
            return _timestamp.ToFileTime();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public long ToFileTimeUtc()
        {
            return _timestamp.ToFileTimeUtc();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public DateTime ToLocalTime()
        {
            return _timestamp.ToLocalTime();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public string ToLongDateString()
        {
            return _timestamp.ToLongDateString();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public string ToLongTimeString()
        {
            return _timestamp.ToLongTimeString();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public double ToOADate()
        {
            return _timestamp.ToOADate();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public string ToShortDateString()
        {
            return _timestamp.ToShortDateString();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public string ToShortTimeString()
        {
            return _timestamp.ToShortTimeString();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="format"></param>
        /// <returns></returns>
        public string ToString(string format)
        {
            return _timestamp.ToString(format);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="provider"></param>
        /// <returns></returns>
        public string ToString(System.IFormatProvider provider)
        {
            return _timestamp.ToString(provider);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="format"></param>
        /// <param name="provider"></param>
        /// <returns></returns>
        public string ToString(string format, System.IFormatProvider provider)
        {
            return _timestamp.ToString(format, provider);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public DateTime ToUniversalTime()
        {
            return _timestamp.ToUniversalTime();
        }

        /// <summary>
        /// Parses input string into datetime
        /// </summary>
        /// <param name="Value">The string to parse</param>
        /// <returns>The resultant datetime.</returns>
        internal static DateTime ParseDateTime(string Value)
        {
            if (String.IsNullOrWhiteSpace(Value))
                throw new ArgumentNullException("Cannot parse empty string!");

            try { return DateTime.Parse(Value, CultureInfo.CurrentCulture); }
            catch { }
            try { return DateTime.Parse(Value, CultureInfo.InvariantCulture); }
            catch { }

            bool positive = !(Value.Contains("-"));
            string tempValue = Value.Replace("-", "").Trim();
            bool date = UtilityHost.IsLike(tempValue, "D *");
            if (date)
                tempValue = tempValue.Substring(2);
            TimeSpan timeResult = new TimeSpan();

            foreach (string element in tempValue.Split(' '))
            {
                if (Regex.IsMatch(element, @"^\d+$"))
                    timeResult = timeResult.Add(new TimeSpan(0, 0, Int32.Parse(element)));
                else if (UtilityHost.IsLike(element, "*ms") && Regex.IsMatch(element, @"^\d+ms$", RegexOptions.IgnoreCase))
                    timeResult = timeResult.Add(new TimeSpan(0, 0, 0, 0, Int32.Parse(Regex.Match(element, @"^(\d+)ms$", RegexOptions.IgnoreCase).Groups[1].Value)));
                else if (UtilityHost.IsLike(element, "*s") && Regex.IsMatch(element, @"^\d+s$", RegexOptions.IgnoreCase))
                    timeResult = timeResult.Add(new TimeSpan(0, 0, Int32.Parse(Regex.Match(element, @"^(\d+)s$", RegexOptions.IgnoreCase).Groups[1].Value)));
                else if (UtilityHost.IsLike(element, "*m") && Regex.IsMatch(element, @"^\d+m$", RegexOptions.IgnoreCase))
                    timeResult = timeResult.Add(new TimeSpan(0, Int32.Parse(Regex.Match(element, @"^(\d+)m$", RegexOptions.IgnoreCase).Groups[1].Value), 0));
                else if (UtilityHost.IsLike(element, "*h") && Regex.IsMatch(element, @"^\d+h$", RegexOptions.IgnoreCase))
                    timeResult = timeResult.Add(new TimeSpan(Int32.Parse(Regex.Match(element, @"^(\d+)h$", RegexOptions.IgnoreCase).Groups[1].Value), 0, 0));
                else if (UtilityHost.IsLike(element, "*d") && Regex.IsMatch(element, @"^\d+d$", RegexOptions.IgnoreCase))
                    timeResult = timeResult.Add(new TimeSpan(Int32.Parse(Regex.Match(element, @"^(\d+)d$", RegexOptions.IgnoreCase).Groups[1].Value), 0, 0, 0));
                else
                    throw new ArgumentException(String.Format("Failed to parse as timespan: {0} at {1}", Value, element));
            }

            DateTime result;
            if (!positive)
                result = DateTime.Now.Add(timeResult.Negate());
            else
                result = DateTime.Now.Add(timeResult);

            if (date)
                return result.Date;
            return result;
        }
        #endregion Methods

        #region Operators
        /// <summary>
        /// 
        /// </summary>
        /// <param name="Timestamp"></param>
        /// <param name="Duration"></param>
        /// <returns></returns>
        public static DbaDateTimeBase operator +(DbaDateTimeBase Timestamp, TimeSpan Duration)
        {
            return Timestamp.Add(Duration);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Timestamp"></param>
        /// <param name="Duration"></param>
        /// <returns></returns>
        public static DbaDateTimeBase operator -(DbaDateTimeBase Timestamp, TimeSpan Duration)
        {
            return Timestamp.Subtract(Duration);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Timestamp1"></param>
        /// <param name="Timestamp2"></param>
        /// <returns></returns>
        public static bool operator ==(DbaDateTimeBase Timestamp1, DbaDateTimeBase Timestamp2)
        {
            return (Timestamp1.GetBaseObject().Equals(Timestamp2.GetBaseObject()));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Timestamp1"></param>
        /// <param name="Timestamp2"></param>
        /// <returns></returns>
        public static bool operator !=(DbaDateTimeBase Timestamp1, DbaDateTimeBase Timestamp2)
        {
            return (!Timestamp1.GetBaseObject().Equals(Timestamp2.GetBaseObject()));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Timestamp1"></param>
        /// <param name="Timestamp2"></param>
        /// <returns></returns>
        public static bool operator >(DbaDateTimeBase Timestamp1, DbaDateTimeBase Timestamp2)
        {
            return Timestamp1.GetBaseObject() > Timestamp2.GetBaseObject();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Timestamp1"></param>
        /// <param name="Timestamp2"></param>
        /// <returns></returns>
        public static bool operator <(DbaDateTimeBase Timestamp1, DbaDateTimeBase Timestamp2)
        {
            return Timestamp1.GetBaseObject() < Timestamp2.GetBaseObject();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Timestamp1"></param>
        /// <param name="Timestamp2"></param>
        /// <returns></returns>
        public static bool operator >=(DbaDateTimeBase Timestamp1, DbaDateTimeBase Timestamp2)
        {
            return Timestamp1.GetBaseObject() >= Timestamp2.GetBaseObject();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Timestamp1"></param>
        /// <param name="Timestamp2"></param>
        /// <returns></returns>
        public static bool operator <=(DbaDateTimeBase Timestamp1, DbaDateTimeBase Timestamp2)
        {
            return Timestamp1.GetBaseObject() <= Timestamp2.GetBaseObject();
        }
        #endregion Operators

        #region Implicit Conversions
        /// <summary>
        /// Implicitly convert DbaDateTimeBase to DateTime
        /// </summary>
        /// <param name="Base">The source object to convert</param>
        public static implicit operator DateTime(DbaDateTimeBase Base)
        {
            return Base.GetBaseObject();
        }

        /// <summary>
        /// Implicitly convert DateTime to DbaDateTimeBase
        /// </summary>
        /// <param name="Base">The object to convert</param>
        public static implicit operator DbaDateTimeBase(DateTime Base)
        {
            return new DbaDateTimeBase(Base.Ticks, Base.Kind);
        }
        #endregion Implicit Conversions
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Utility\DbaTime.cs
using System;

namespace Sqlcollaborative.Dbatools.Utility
{
    /// <summary>
    /// A dbatools-internal datetime wrapper for neater display
    /// </summary>
    public class DbaTime : DbaDateTimeBase
    {
        #region Constructors
        /// <summary>
        /// Constructs a generic timestamp object wrapper from an input timestamp object.
        /// </summary>
        /// <param name="Timestamp">The timestamp to wrap</param>
        public DbaTime(DateTime Timestamp)
        {
            _timestamp = Timestamp;
        }

        /// <summary>
        /// Parses a string into a datetime object.
        /// </summary>
        /// <param name="Time">The time-string to parse</param>
        public DbaTime(string Time)
        {
            _timestamp = ParseDateTime(Time);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ticks"></param>
        public DbaTime(long ticks)
        {
            _timestamp = new DateTime(ticks);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ticks"></param>
        /// <param name="kind"></param>
        public DbaTime(long ticks, System.DateTimeKind kind)
        {
            _timestamp = new DateTime(ticks, kind);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        public DbaTime(int year, int month, int day)
        {
            _timestamp = new DateTime(year, month, day);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        /// <param name="calendar"></param>
        public DbaTime(int year, int month, int day, System.Globalization.Calendar calendar)
        {
            _timestamp = new DateTime(year, month, day, calendar);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        /// <param name="hour"></param>
        /// <param name="minute"></param>
        /// <param name="second"></param>
        public DbaTime(int year, int month, int day, int hour, int minute, int second)
        {
            _timestamp = new DateTime(year, month, day, hour, minute, second);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        /// <param name="hour"></param>
        /// <param name="minute"></param>
        /// <param name="second"></param>
        /// <param name="kind"></param>
        public DbaTime(int year, int month, int day, int hour, int minute, int second, System.DateTimeKind kind)
        {
            _timestamp = new DateTime(year, month, day, hour, minute, second, kind);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        /// <param name="hour"></param>
        /// <param name="minute"></param>
        /// <param name="second"></param>
        /// <param name="calendar"></param>
        public DbaTime(int year, int month, int day, int hour, int minute, int second, System.Globalization.Calendar calendar)
        {
            _timestamp = new DateTime(year, month, day, hour, minute, second, calendar);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        /// <param name="hour"></param>
        /// <param name="minute"></param>
        /// <param name="second"></param>
        /// <param name="millisecond"></param>
        public DbaTime(int year, int month, int day, int hour, int minute, int second, int millisecond)
        {
            _timestamp = new DateTime(year, month, day, hour, minute, second, millisecond);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        /// <param name="hour"></param>
        /// <param name="minute"></param>
        /// <param name="second"></param>
        /// <param name="millisecond"></param>
        /// <param name="kind"></param>
        public DbaTime(int year, int month, int day, int hour, int minute, int second, int millisecond, System.DateTimeKind kind)
        {
            _timestamp = new DateTime(year, month, day, hour, minute, second, millisecond, kind);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        /// <param name="hour"></param>
        /// <param name="minute"></param>
        /// <param name="second"></param>
        /// <param name="millisecond"></param>
        /// <param name="calendar"></param>
        public DbaTime(int year, int month, int day, int hour, int minute, int second, int millisecond, System.Globalization.Calendar calendar)
        {
            _timestamp = new DateTime(year, month, day, hour, minute, second, millisecond, calendar);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="day"></param>
        /// <param name="hour"></param>
        /// <param name="minute"></param>
        /// <param name="second"></param>
        /// <param name="millisecond"></param>
        /// <param name="calendar"></param>
        /// <param name="kind"></param>
        public DbaTime(int year, int month, int day, int hour, int minute, int second, int millisecond, System.Globalization.Calendar calendar, System.DateTimeKind kind)
        {
            _timestamp = new DateTime(year, month, day, hour, minute, second, millisecond, calendar, kind);
        }
        #endregion Constructors

        /// <summary>
        /// Provids the default-formated string, using the defined default formatting.
        /// </summary>
        /// <returns>Formatted datetime-string</returns>
        public override string ToString()
        {
            if (UtilityHost.DisableCustomDateTime) { return _timestamp.ToString(); }
            return _timestamp.ToString(UtilityHost.FormatTime);
        }

        #region Implicit Conversions
        /// <summary>
        /// Implicitly convert to DateTime
        /// </summary>
        /// <param name="Base">The source object to convert</param>
        public static implicit operator DateTime(DbaTime Base)
        {
            return Base.GetBaseObject();
        }

        /// <summary>
        /// Implicitly convert from DateTime
        /// </summary>
        /// <param name="Base">The object to convert</param>
        public static implicit operator DbaTime(DateTime Base)
        {
            return new DbaTime(Base);
        }

        /// <summary>
        /// Implicitly convert to DbaDate
        /// </summary>
        /// <param name="Base">The source object to convert</param>
        public static implicit operator DbaDate(DbaTime Base)
        {
            return new DbaDate(Base.GetBaseObject());
        }

        /// <summary>
        /// Implicitly convert to DbaTime
        /// </summary>
        /// <param name="Base">The source object to convert</param>
        public static implicit operator DbaDateTime(DbaTime Base)
        {
            return new DbaDateTime(Base.GetBaseObject());
        }

        /// <summary>
        /// Implicitly convert to string
        /// </summary>
        /// <param name="Base">Object to convert</param>
        public static implicit operator string(DbaTime Base)
        {
            return Base.ToString();
        }
        #endregion Implicit Conversions

        #region Statics
        /// <summary>
        /// Generates a DbaDateTime object based off DateTime object. Will be null if Base is the start value (Tickes == 0).
        /// </summary>
        /// <param name="Base">The Datetime to base it off</param>
        /// <returns>The object to generate (or null)</returns>
        public static DbaTime Generate(DateTime Base)
        {
            if (Base.Ticks == 0)
                return null;
            else
                return new DbaTime(Base);
        }
        #endregion Statics
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Utility\DbaTimeSpan.cs
using System;
using System.Globalization;
using System.Text.RegularExpressions;

namespace Sqlcollaborative.Dbatools.Utility
{
    /// <summary>
    /// A wrapper class, encapsuling a regular TimeSpan object. Used to provide custom timespan display.
    /// </summary>
    public class DbaTimeSpan : IComparable, IComparable<TimeSpan>, IComparable<DbaTimeSpan>, IEquatable<TimeSpan>
    {
        internal TimeSpan _timespan;

        #region Properties
        /// <summary>
        /// Gets the days component of the time interval represented by the current TimeSpan structure.
        /// </summary>
        public int Days
        {
            get
            {
                return _timespan.Days;
            }
        }

        /// <summary>
        /// Gets the hours component of the time interval represented by the current TimeSpan structure.
        /// </summary>
        public int Hours
        {
            get
            {
                return _timespan.Hours;
            }
        }

        /// <summary>
        /// Gets the milliseconds component of the time interval represented by the current TimeSpan structure.
        /// </summary>
        public int Milliseconds
        {
            get
            {
                return _timespan.Milliseconds;
            }
        }

        /// <summary>
        /// Gets the minutes component of the time interval represented by the current TimeSpan structure.
        /// </summary>
        public int Minutes
        {
            get
            {
                return _timespan.Minutes;
            }
        }

        /// <summary>
        /// Gets the seconds component of the time interval represented by the current TimeSpan structure.
        /// </summary>
        public int Seconds
        {
            get
            {
                return _timespan.Seconds;
            }
        }

        /// <summary>
        /// Gets the number of ticks that represent the value of the current TimeSpan structure.
        /// </summary>
        public long Ticks
        {
            get
            {
                return _timespan.Ticks;
            }
        }

        /// <summary>
        /// Gets the value of the current TimeSpan structure expressed in whole and fractional days.
        /// </summary>
        public double TotalDays
        {
            get
            {
                return _timespan.TotalDays;
            }
        }

        /// <summary>
        /// Gets the value of the current TimeSpan structure expressed in whole and fractional hours.
        /// </summary>
        public double TotalHours
        {
            get
            {
                return _timespan.TotalHours;
            }
        }

        /// <summary>
        /// Gets the value of the current TimeSpan structure expressed in whole and fractional milliseconds.
        /// </summary>
        public double TotalMilliseconds
        {
            get
            {
                return _timespan.TotalMilliseconds;
            }
        }

        /// <summary>
        /// Gets the value of the current TimeSpan structure expressed in whole and fractional minutes.
        /// </summary>
        public double TotalMinutes
        {
            get
            {
                return _timespan.TotalMinutes;
            }
        }

        /// <summary>
        /// Gets the value of the current TimeSpan structure expressed in whole and fractional seconds.
        /// </summary>
        public double TotalSeconds
        {
            get
            {
                return _timespan.TotalSeconds;
            }
        }
        #endregion Properties

        #region Constructors
        /// <summary>
        /// 
        /// </summary>
        /// <param name="Timespan"></param>
        public DbaTimeSpan(TimeSpan Timespan)
        {
            _timespan = Timespan;
        }

        /// <summary>
        /// Converts a string into a timespan
        /// </summary>
        /// <param name="Timespan">The string to convert</param>
        public DbaTimeSpan(string Timespan)
        {
            _timespan = ParseTimeSpan(Timespan);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ticks"></param>
        public DbaTimeSpan(long ticks)
        {
            _timespan = new TimeSpan(ticks);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="hours"></param>
        /// <param name="minutes"></param>
        /// <param name="seconds"></param>
        public DbaTimeSpan(int hours, int minutes, int seconds)
        {
            _timespan = new TimeSpan(hours, minutes, seconds);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="days"></param>
        /// <param name="hours"></param>
        /// <param name="minutes"></param>
        /// <param name="seconds"></param>
        public DbaTimeSpan(int days, int hours, int minutes, int seconds)
        {
            _timespan = new TimeSpan(days, hours, minutes, seconds);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="days"></param>
        /// <param name="hours"></param>
        /// <param name="minutes"></param>
        /// <param name="seconds"></param>
        /// <param name="milliseconds"></param>
        public DbaTimeSpan(int days, int hours, int minutes, int seconds, int milliseconds)
        {
            _timespan = new TimeSpan(days, hours, minutes, seconds, milliseconds);
        }
        #endregion Constructors

        #region Methods
        /// <summary>
        /// Parses an input string as timespan
        /// </summary>
        /// <param name="Value">The string to interpret</param>
        /// <returns>The interpreted timespan value</returns>
        internal static TimeSpan ParseTimeSpan(string Value)
        {
            if (String.IsNullOrWhiteSpace(Value))
                throw new ArgumentNullException("Cannot parse empty string!");

            try { return TimeSpan.Parse(Value, CultureInfo.CurrentCulture); }
            catch { }
            try { return TimeSpan.Parse(Value, CultureInfo.InvariantCulture); }
            catch { }

            bool positive = !(Value.Contains("-"));
            TimeSpan timeResult = new TimeSpan();
            string tempValue = Value.Replace("-", "").Trim();

            foreach (string element in tempValue.Split(' '))
            {
                if (Regex.IsMatch(element, @"^\d+$"))
                    timeResult = timeResult.Add(new TimeSpan(0, 0, Int32.Parse(element)));
                else if (UtilityHost.IsLike(element, "*ms") && Regex.IsMatch(element, @"^\d+ms$", RegexOptions.IgnoreCase))
                    timeResult = timeResult.Add(new TimeSpan(0, 0, 0, 0, Int32.Parse(Regex.Match(element, @"^(\d+)ms$", RegexOptions.IgnoreCase).Groups[1].Value)));
                else if (UtilityHost.IsLike(element, "*s") && Regex.IsMatch(element, @"^\d+s$", RegexOptions.IgnoreCase))
                    timeResult = timeResult.Add(new TimeSpan(0, 0, Int32.Parse(Regex.Match(element, @"^(\d+)s$", RegexOptions.IgnoreCase).Groups[1].Value)));
                else if (UtilityHost.IsLike(element, "*m") && Regex.IsMatch(element, @"^\d+m$", RegexOptions.IgnoreCase))
                    timeResult = timeResult.Add(new TimeSpan(0, Int32.Parse(Regex.Match(element, @"^(\d+)m$", RegexOptions.IgnoreCase).Groups[1].Value), 0));
                else if (UtilityHost.IsLike(element, "*h") && Regex.IsMatch(element, @"^\d+h$", RegexOptions.IgnoreCase))
                    timeResult = timeResult.Add(new TimeSpan(Int32.Parse(Regex.Match(element, @"^(\d+)h$", RegexOptions.IgnoreCase).Groups[1].Value), 0, 0));
                else if (UtilityHost.IsLike(element, "*d") && Regex.IsMatch(element, @"^\d+d$", RegexOptions.IgnoreCase))
                    timeResult = timeResult.Add(new TimeSpan(Int32.Parse(Regex.Match(element, @"^(\d+)d$", RegexOptions.IgnoreCase).Groups[1].Value), 0, 0, 0));
                else
                    throw new ArgumentException(String.Format("Failed to parse as timespan: {0} at {1}", Value, element));
            }

            if (!positive)
                return timeResult.Negate();
            return timeResult;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ts"></param>
        /// <returns></returns>
        public TimeSpan Add(TimeSpan ts)
        {
            return _timespan.Add(ts);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public int CompareTo(System.Object value)
        {
            return _timespan.CompareTo(value);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public int CompareTo(TimeSpan value)
        {
            return _timespan.CompareTo(value);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public int CompareTo(DbaTimeSpan value)
        {
            return _timespan.CompareTo(value.GetBaseObject());
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public TimeSpan Duration()
        {
            return _timespan.Duration();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public override bool Equals(System.Object value)
        {
            return _timespan.Equals(value);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public bool Equals(TimeSpan obj)
        {
            return _timespan.Equals(obj);
        }

        /// <summary>
        /// Returns the wrapped base object
        /// </summary>
        /// <returns>The base object</returns>
        public TimeSpan GetBaseObject()
        {
            return _timespan;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return _timespan.GetHashCode();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public TimeSpan Negate()
        {
            return _timespan.Negate();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ts"></param>
        /// <returns></returns>
        public TimeSpan Subtract(TimeSpan ts)
        {
            return _timespan.Subtract(ts);
        }

        /// <summary>
        /// Returns the default string representation of the TimeSpan object
        /// </summary>
        /// <returns>The string representation of the DbaTimeSpan object</returns>
        public override string ToString()
        {
            if (UtilityHost.DisableCustomTimeSpan) { return _timespan.ToString(); }
            else if (_timespan.Ticks % 10000000 == 0) { return _timespan.ToString(); }
            else
            {
                string temp = _timespan.ToString();

                if (_timespan.TotalSeconds < 10) { temp = temp.Substring(0, temp.LastIndexOf(".") + 3); }
                else if (_timespan.TotalSeconds < 100) { temp = temp.Substring(0, temp.LastIndexOf(".") + 2); }
                else { temp = temp.Substring(0, temp.LastIndexOf(".")); }

                return temp;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="format"></param>
        /// <returns></returns>
        public string ToString(string format)
        {
            return _timespan.ToString(format);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="format"></param>
        /// <param name="formatProvider"></param>
        /// <returns></returns>
        public string ToString(string format, System.IFormatProvider formatProvider)
        {
            return _timespan.ToString(format, formatProvider);
        }
        #endregion Methods

        #region Implicit Operators
        /// <summary>
        /// Implicitly converts a DbaTimeSpan object into a TimeSpan object
        /// </summary>
        /// <param name="Base">The original object to revert</param>
        public static implicit operator TimeSpan(DbaTimeSpan Base)
        {
            try { return Base.GetBaseObject(); }
            catch { }
            return new TimeSpan();
        }

        /// <summary>
        /// Implicitly converts a TimeSpan object into a DbaTimeSpan object
        /// </summary>
        /// <param name="Base">The original object to wrap</param>
        public static implicit operator DbaTimeSpan(TimeSpan Base)
        {
            return new DbaTimeSpan(Base);
        }
        #endregion Implicit Operators
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Utility\DbaTimeSpanPretty.cs
using System;

namespace Sqlcollaborative.Dbatools.Utility
{
    /// <summary>
    /// Makes timespan great again
    /// </summary>
    public class DbaTimeSpanPretty : DbaTimeSpan
    {
        #region Methods
        /// <summary>
        /// Creates a new, pretty timespan object from milliseconds
        /// </summary>
        /// <param name="Milliseconds">The milliseconds to convert from.</param>
        /// <returns>A pretty timespan object</returns>
        public static DbaTimeSpanPretty FromMilliseconds(double Milliseconds)
        {
            return new DbaTimeSpanPretty((long)(Milliseconds * 10000));
        }
        #endregion Methods

        #region Fields
        /// <summary>
        /// The number of digits a pretty timespan should round to.
        /// </summary>
        public int Digits = 2;
        #endregion Fields

        #region Constructors
        /// <summary>
        /// 
        /// </summary>
        /// <param name="Timespan"></param>
        public DbaTimeSpanPretty(TimeSpan Timespan)
            : base(Timespan)
        {

        }

        /// <summary>
        /// Converts a string into a timespan
        /// </summary>
        /// <param name="Timespan">The string to convert</param>
        public DbaTimeSpanPretty(string Timespan)
            : base(Timespan)
        {
            
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ticks"></param>
        public DbaTimeSpanPretty(long ticks)
            : base(ticks)
        {

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="hours"></param>
        /// <param name="minutes"></param>
        /// <param name="seconds"></param>
        public DbaTimeSpanPretty(int hours, int minutes, int seconds)
            : base(hours, minutes, seconds)
        {

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="days"></param>
        /// <param name="hours"></param>
        /// <param name="minutes"></param>
        /// <param name="seconds"></param>
        public DbaTimeSpanPretty(int days, int hours, int minutes, int seconds)
            : base(days, hours, minutes, seconds)
        {

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="days"></param>
        /// <param name="hours"></param>
        /// <param name="minutes"></param>
        /// <param name="seconds"></param>
        /// <param name="milliseconds"></param>
        public DbaTimeSpanPretty(int days, int hours, int minutes, int seconds, int milliseconds)
            : base(days, hours, minutes, seconds, milliseconds)
        {

        }
        #endregion Constructors

        /// <summary>
        /// Creates extra-nice timespan formats
        /// </summary>
        /// <returns>Humanly readable timespans</returns>
        public override string ToString()
        {
            if (UtilityHost.DisableCustomTimeSpan) { return _timespan.ToString(); }

            string temp = "";

            if (_timespan.TotalSeconds < 1)
            {
                temp = Math.Round(_timespan.TotalMilliseconds, Digits).ToString() + " ms";
            }
            else if (_timespan.TotalSeconds <= 60)
            {
                temp = Math.Round(_timespan.TotalSeconds, Digits).ToString() + " s";
            }
            else
            {
                if (_timespan.Ticks % 10000000 == 0) { return _timespan.ToString(); }
                else
                {
                    temp = _timespan.ToString();

                    temp = temp.Substring(0, temp.LastIndexOf("."));

                    return temp;
                }
            }

            return temp;
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Utility\DbaValidatePatternAttribute.cs
using System;
using System.Management.Automation;
using System.Text.RegularExpressions;

namespace Sqlcollaborative.Dbatools.Utility
{
    /// <summary>
    /// Validates that each parameter argument matches the RegexPattern
    /// </summary>
    public class DbaValidatePatternAttribute : ValidateEnumeratedArgumentsAttribute
    {
        /// <summary>
        /// Gets the Regex pattern to be used in the validation
        /// </summary>
        public string RegexPattern { get; private set; }

        /// <summary>
        /// Gets or sets the Regex options to be used in the validation
        /// </summary>
        public RegexOptions Options { set; get; }

        /// <summary>
        /// Gets or sets the custom error message pattern that is displayed to the user.
        ///
        /// The text representation of the object being validated and the validating regex is passed as
        /// the first and second formatting parameters to the ErrorMessage formatting pattern.
        /// <example>
        /// [ValidatePattern("\s+", ErrorMessage="The text '{0}' did not pass validation of regex '{1}'")]
        /// </example>
        /// </summary>
        public string ErrorMessage { get; set; }

        /// <summary>
        /// Validates that each parameter argument matches the RegexPattern
        /// </summary>
        /// <param name="element">object to validate</param>
        /// <exception cref="ValidationMetadataException">if <paramref name="element"/> is not a string
        ///  that matches the pattern
        ///  and for invalid arguments</exception>
        protected override void ValidateElement(object element)
        {
            if (element == null)
                throw new ValidationMetadataException("Argument Is Empty");

            string objectString = element.ToString();
            Regex regex = null;
            regex = new Regex(RegexPattern, Options);
            Match match = regex.Match(objectString);
            if (!match.Success)
            {
                var errorMessageFormat = String.IsNullOrEmpty(ErrorMessage) ? "Failed to validate: {0} against pattern {1}" : ErrorMessage;
                throw new ValidationMetadataException(String.Format(errorMessageFormat, element, RegexPattern));
            }
        }

        /// <summary>
        /// Initializes a new instance of the PsfValidatePatternAttribute class
        /// </summary>
        /// <param name="regexPattern">Pattern string to match</param>
        /// <exception cref="ArgumentException">for invalid arguments</exception>
        public DbaValidatePatternAttribute(string regexPattern)
        {
            Options = RegexOptions.IgnoreCase;
            if (String.IsNullOrEmpty(regexPattern)) {
                throw new ArgumentNullException("regexPattern", "Must specify a pattern!");
            }

            RegexPattern = regexPattern;
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Utility\DbaValidateScriptAttribute.cs
using System;
using System.Management.Automation;

namespace Sqlcollaborative.Dbatools.Utility
{
    /// <summary>
    /// Class for validating against a script block.
    /// </summary>
    public class DbaValidateScriptAttribute : ValidateEnumeratedArgumentsAttribute
    {
        /// <summary>
        /// Gets or sets the custom error message that is displayed to the user.
        ///
        /// The item being validated and the validating scriptblock is passed as the first and second
        /// formatting argument.
        ///
        /// <example>
        /// [ValidateScript("$_ % 2", ErrorMessage = "The item '{0}' did not pass validation of script '{1}'")]
        /// </example>
        /// </summary>
        public string ErrorMessage { get; set; }

        /// <summary>
        /// Gets the scriptblock to be used in the validation
        /// </summary>
        public ScriptBlock ScriptBlock { get; private set; }

        /// <summary>
        /// Validates that each parameter argument matches the scriptblock
        /// </summary>
        /// <param name="element">object to validate</param>
        /// <exception cref="ValidationMetadataException">if <paramref name="element"/> is invalid</exception>
        protected override void ValidateElement(object element)
        {
            if (element == null)
            {
                throw new ValidationMetadataException("ArgumentIsEmpty", null);
            }

            object result = ScriptBlock.Invoke(element);

            if (!LanguagePrimitives.IsTrue(result))
            {
                var errorMessageFormat = String.IsNullOrEmpty(ErrorMessage) ? "Error executing validation script: {0} against {{ {1} }}" : ErrorMessage;
                throw new ValidationMetadataException(String.Format(errorMessageFormat, element, ScriptBlock));
            }
        }

        /// <summary>
        /// Initializes a new instance of the ValidateScriptBlockAttribute class
        /// </summary>
        /// <param name="scriptBlock">Scriptblock to match</param>
        /// <exception cref="ArgumentException">for invalid arguments</exception>
        public DbaValidateScriptAttribute(ScriptBlock scriptBlock)
        {
            if (scriptBlock == null) {
                throw new ArgumentNullException("scriptBlock", "Need to specify a scriptblock!");
            }
            this.ScriptBlock = ScriptBlock;
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Utility\RegexHelper.cs
namespace Sqlcollaborative.Dbatools.Utility
{
    /// <summary>
    /// Static class that holds useful regex patterns, ready for use
    /// </summary>
    public static class RegexHelper
    {
        /// <summary>
        /// Pattern that checks for a valid hostname
        /// </summary>
        public static string HostName = @"^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-_]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-_]{0,61}[a-zA-Z0-9]))*$";

        /// <summary>
        /// Pattern that checks for valid hostnames within a larger text
        /// </summary>
        public static string HostNameEx = @"([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-_]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-_]{0,61}[a-zA-Z0-9]))*";

        /// <summary>
        /// Pattern that checks for a valid IPv4 address
        /// </summary>
        public static string IPv4 = @"^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$";

        /// <summary>
        /// Pattern that checks for valid IPv4 addresses within a larger text
        /// </summary>
        public static string IPv4Ex = @"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}";

        /// <summary>
        /// Will match a valid IPv6 address
        /// </summary>
        public static string IPv6 = @"^(?:^|(?<=\s))(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))(?=\s|$)$";

        /// <summary>
        /// Will match any IPv6 address within a larger text
        /// </summary>
        public static string IPv6Ex = @"(?:^|(?<=\s))(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))(?=\s|$)";

        /// <summary>
        /// Will match any string that in its entirety represents a valid target for dns- or ip-based targeting. Combination of HostName, IPv4 and IPv6
        /// </summary>
        public static string ComputerTarget = @"^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-_]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-_]{0,61}[a-zA-Z0-9]))*$|^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$|^(?:^|(?<=\s))(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))(?=\s|$)$";

        /// <summary>
        /// Will match a valid Guid
        /// </summary>
        public static string Guid = @"^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$";

        /// <summary>
        /// Will match any number of valid Guids in a larger text
        /// </summary>
        public static string GuidEx = @"(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})";

        /// <summary>
        /// Will match a mostly valid instance name.
        /// </summary>
        public static string InstanceName = @"^[\p{L}&_#][\p{L}\d\$#_]{0,15}$";

        /// <summary>
        /// Will match any instance of a mostly valid instance name.
        /// </summary>
        public static string InstanceNameEx = @"[\p{L}&_#][\p{L}\d\$#_]{0,15}";

        /// <summary>
        /// Matches a word against the list of officially reserved keywords
        /// </summary>
        public static string SqlReservedKeyword = @"^ADD$|^ALL$|^ALTER$|^AND$|^ANY$|^AS$|^ASC$|^AUTHORIZATION$|^BACKUP$|^BEGIN$|^BETWEEN$|^BREAK$|^BROWSE$|^BULK$|^BY$|^CASCADE$|^CASE$|^CHECK$|^CHECKPOINT$|^CLOSE$|^CLUSTERED$|^COALESCE$|^COLLATE$|^COLUMN$|^COMMIT$|^COMPUTE$|^CONSTRAINT$|^CONTAINS$|^CONTAINSTABLE$|^CONTINUE$|^CONVERT$|^CREATE$|^CROSS$|^CURRENT$|^CURRENT_DATE$|^CURRENT_TIME$|^CURRENT_TIMESTAMP$|^CURRENT_USER$|^CURSOR$|^DATABASE$|^DBCC$|^DEALLOCATE$|^DECLARE$|^DEFAULT$|^DELETE$|^DENY$|^DESC$|^DISK$|^DISTINCT$|^DISTRIBUTED$|^DOUBLE$|^DROP$|^DUMP$|^ELSE$|^END$|^ERRLVL$|^ESCAPE$|^EXCEPT$|^EXEC$|^EXECUTE$|^EXISTS$|^EXIT$|^EXTERNAL$|^FETCH$|^FILE$|^FILLFACTOR$|^FOR$|^FOREIGN$|^FREETEXT$|^FREETEXTTABLE$|^FROM$|^FULL$|^FUNCTION$|^GOTO$|^GRANT$|^GROUP$|^HAVING$|^HOLDLOCK$|^IDENTITY$|^IDENTITY_INSERT$|^IDENTITYCOL$|^IF$|^IN$|^INDEX$|^INNER$|^INSERT$|^INTERSECT$|^INTO$|^IS$|^JOIN$|^KEY$|^KILL$|^LEFT$|^LIKE$|^LINENO$|^LOAD$|^MERGE$|^NATIONAL$|^NOCHECK$|^NONCLUSTERED$|^NOT$|^NULL$|^NULLIF$|^OF$|^OFF$|^OFFSETS$|^ON$|^OPEN$|^OPENDATASOURCE$|^OPENQUERY$|^OPENROWSET$|^OPENXML$|^OPTION$|^OR$|^ORDER$|^OUTER$|^OVER$|^PERCENT$|^PIVOT$|^PLAN$|^PRECISION$|^PRIMARY$|^PRINT$|^PROC$|^PROCEDURE$|^PUBLIC$|^RAISERROR$|^READ$|^READTEXT$|^RECONFIGURE$|^REFERENCES$|^REPLICATION$|^RESTORE$|^RESTRICT$|^RETURN$|^REVERT$|^REVOKE$|^RIGHT$|^ROLLBACK$|^ROWCOUNT$|^ROWGUIDCOL$|^RULE$|^SAVE$|^SCHEMA$|^SECURITYAUDIT$|^SELECT$|^SEMANTICKEYPHRASETABLE$|^SEMANTICSIMILARITYDETAILSTABLE$|^SEMANTICSIMILARITYTABLE$|^SESSION_USER$|^SET$|^SETUSER$|^SHUTDOWN$|^SOME$|^STATISTICS$|^SYSTEM_USER$|^TABLE$|^TABLESAMPLE$|^TEXTSIZE$|^THEN$|^TO$|^TOP$|^TRAN$|^TRANSACTION$|^TRIGGER$|^TRUNCATE$|^TRY_CONVERT$|^TSEQUAL$|^UNION$|^UNIQUE$|^UNPIVOT$|^UPDATE$|^UPDATETEXT$|^USE$|^USER$|^VALUES$|^VARYING$|^VIEW$|^WAITFOR$|^WHEN$|^WHERE$|^WHILE$|^WITH$|^WITHIN GROUP$|^WRITETEXT$";

        /// <summary>
        /// Will match any reserved keyword in a larger text
        /// </summary>
        public static string SqlReservedKeywordEx = @"ADD|ALL|ALTER|AND|ANY|AS|ASC|AUTHORIZATION|BACKUP|BEGIN|BETWEEN|BREAK|BROWSE|BULK|BY|CASCADE|CASE|CHECK|CHECKPOINT|CLOSE|CLUSTERED|COALESCE|COLLATE|COLUMN|COMMIT|COMPUTE|CONSTRAINT|CONTAINS|CONTAINSTABLE|CONTINUE|CONVERT|CREATE|CROSS|CURRENT|CURRENT_DATE|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER|CURSOR|DATABASE|DBCC|DEALLOCATE|DECLARE|DEFAULT|DELETE|DENY|DESC|DISK|DISTINCT|DISTRIBUTED|DOUBLE|DROP|DUMP|ELSE|END|ERRLVL|ESCAPE|EXCEPT|EXEC|EXECUTE|EXISTS|EXIT|EXTERNAL|FETCH|FILE|FILLFACTOR|FOR|FOREIGN|FREETEXT|FREETEXTTABLE|FROM|FULL|FUNCTION|GOTO|GRANT|GROUP|HAVING|HOLDLOCK|IDENTITY|IDENTITY_INSERT|IDENTITYCOL|IF|IN|INDEX|INNER|INSERT|INTERSECT|INTO|IS|JOIN|KEY|KILL|LEFT|LIKE|LINENO|LOAD|MERGE|NATIONAL|NOCHECK|NONCLUSTERED|NOT|NULL|NULLIF|OF|OFF|OFFSETS|ON|OPEN|OPENDATASOURCE|OPENQUERY|OPENROWSET|OPENXML|OPTION|OR|ORDER|OUTER|OVER|PERCENT|PIVOT|PLAN|PRECISION|PRIMARY|PRINT|PROC|PROCEDURE|PUBLIC|RAISERROR|READ|READTEXT|RECONFIGURE|REFERENCES|REPLICATION|RESTORE|RESTRICT|RETURN|REVERT|REVOKE|RIGHT|ROLLBACK|ROWCOUNT|ROWGUIDCOL|RULE|SAVE|SCHEMA|SECURITYAUDIT|SELECT|SEMANTICKEYPHRASETABLE|SEMANTICSIMILARITYDETAILSTABLE|SEMANTICSIMILARITYTABLE|SESSION_USER|SET|SETUSER|SHUTDOWN|SOME|STATISTICS|SYSTEM_USER|TABLE|TABLESAMPLE|TEXTSIZE|THEN|TO|TOP|TRAN|TRANSACTION|TRIGGER|TRUNCATE|TRY_CONVERT|TSEQUAL|UNION|UNIQUE|UNPIVOT|UPDATE|UPDATETEXT|USE|USER|VALUES|VARYING|VIEW|WAITFOR|WHEN|WHERE|WHILE|WITH|WITHIN GROUP|WRITETEXT";

        /// <summary>
        /// Matches a word against the list of officially reserved keywords for odbc
        /// </summary>
        public static string SqlReservedKeywordOdbc = @"^ABSOLUTE$|^ACTION$|^ADA$|^ADD$|^ALL$|^ALLOCATE$|^ALTER$|^AND$|^ANY$|^ARE$|^AS$|^ASC$|^ASSERTION$|^AT$|^AUTHORIZATION$|^AVG$|^BEGIN$|^BETWEEN$|^BIT$|^BIT_LENGTH$|^BOTH$|^BY$|^CASCADE$|^CASCADED$|^CASE$|^CAST$|^CATALOG$|^CHAR$|^CHAR_LENGTH$|^CHARACTER$|^CHARACTER_LENGTH$|^CHECK$|^CLOSE$|^COALESCE$|^COLLATE$|^COLLATION$|^COLUMN$|^COMMIT$|^CONNECT$|^CONNECTION$|^CONSTRAINT$|^CONSTRAINTS$|^CONTINUE$|^CONVERT$|^CORRESPONDING$|^COUNT$|^CREATE$|^CROSS$|^CURRENT$|^CURRENT_DATE$|^CURRENT_TIME$|^CURRENT_TIMESTAMP$|^CURRENT_USER$|^CURSOR$|^DATE$|^DAY$|^DEALLOCATE$|^DEC$|^DECIMAL$|^DECLARE$|^DEFAULT$|^DEFERRABLE$|^DEFERRED$|^DELETE$|^DESC$|^DESCRIBE$|^DESCRIPTOR$|^DIAGNOSTICS$|^DISCONNECT$|^DISTINCT$|^DOMAIN$|^DOUBLE$|^DROP$|^ELSE$|^END$|^END-EXEC$|^ESCAPE$|^EXCEPT$|^EXCEPTION$|^EXEC$|^EXECUTE$|^EXISTS$|^EXTERNAL$|^EXTRACT$|^FALSE$|^FETCH$|^FIRST$|^FLOAT$|^FOR$|^FOREIGN$|^FORTRAN$|^FOUND$|^FROM$|^FULL$|^GET$|^GLOBAL$|^GO$|^GOTO$|^GRANT$|^GROUP$|^HAVING$|^HOUR$|^IDENTITY$|^IMMEDIATE$|^IN$|^INCLUDE$|^INDEX$|^INDICATOR$|^INITIALLY$|^INNER$|^INPUT$|^INSENSITIVE$|^INSERT$|^INT$|^INTEGER$|^INTERSECT$|^INTERVAL$|^INTO$|^IS$|^ISOLATION$|^JOIN$|^KEY$|^LANGUAGE$|^LAST$|^LEADING$|^LEFT$|^LEVEL$|^LIKE$|^LOCAL$|^LOWER$|^MATCH$|^MAX$|^MIN$|^MINUTE$|^MODULE$|^MONTH$|^NAMES$|^NATIONAL$|^NATURAL$|^NCHAR$|^NEXT$|^NO$|^NONE$|^NOT$|^NULL$|^NULLIF$|^NUMERIC$|^OCTET_LENGTH$|^OF$|^ON$|^ONLY$|^OPEN$|^OPTION$|^OR$|^ORDER$|^OUTER$|^OUTPUT$|^OVERLAPS$|^PAD$|^PARTIAL$|^PASCAL$|^POSITION$|^PRECISION$|^PREPARE$|^PRESERVE$|^PRIMARY$|^PRIOR$|^PRIVILEGES$|^PROCEDURE$|^PUBLIC$|^READ$|^REAL$|^REFERENCES$|^RELATIVE$|^RESTRICT$|^REVOKE$|^RIGHT$|^ROLLBACK$|^ROWS$|^SCHEMA$|^SCROLL$|^SECOND$|^SECTION$|^SELECT$|^SESSION$|^SESSION_USER$|^SET$|^SIZE$|^SMALLINT$|^SOME$|^SPACE$|^SQL$|^SQLCA$|^SQLCODE$|^SQLERROR$|^SQLSTATE$|^SQLWARNING$|^SUBSTRING$|^SUM$|^SYSTEM_USER$|^TABLE$|^TEMPORARY$|^THEN$|^TIME$|^TIMESTAMP$|^TIMEZONE_HOUR$|^TIMEZONE_MINUTE$|^TO$|^TRAILING$|^TRANSACTION$|^TRANSLATE$|^TRANSLATION$|^TRIM$|^TRUE$|^UNION$|^UNIQUE$|^UNKNOWN$|^UPDATE$|^UPPER$|^USAGE$|^USER$|^USING$|^VALUE$|^VALUES$|^VARCHAR$|^VARYING$|^VIEW$|^WHEN$|^WHENEVER$|^WHERE$|^WITH$|^WORK$|^WRITE$|^YEAR$|^ZONE$";

        /// <summary>
        /// Will match any reserved odbc-keyword in a larger text
        /// </summary>
        public static string SqlReservedKeywordOdbcEx = @"ABSOLUTE|ACTION|ADA|ADD|ALL|ALLOCATE|ALTER|AND|ANY|ARE|AS|ASC|ASSERTION|AT|AUTHORIZATION|AVG|BEGIN|BETWEEN|BIT|BIT_LENGTH|BOTH|BY|CASCADE|CASCADED|CASE|CAST|CATALOG|CHAR|CHAR_LENGTH|CHARACTER|CHARACTER_LENGTH|CHECK|CLOSE|COALESCE|COLLATE|COLLATION|COLUMN|COMMIT|CONNECT|CONNECTION|CONSTRAINT|CONSTRAINTS|CONTINUE|CONVERT|CORRESPONDING|COUNT|CREATE|CROSS|CURRENT|CURRENT_DATE|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER|CURSOR|DATE|DAY|DEALLOCATE|DEC|DECIMAL|DECLARE|DEFAULT|DEFERRABLE|DEFERRED|DELETE|DESC|DESCRIBE|DESCRIPTOR|DIAGNOSTICS|DISCONNECT|DISTINCT|DOMAIN|DOUBLE|DROP|ELSE|END|END-EXEC|ESCAPE|EXCEPT|EXCEPTION|EXEC|EXECUTE|EXISTS|EXTERNAL|EXTRACT|FALSE|FETCH|FIRST|FLOAT|FOR|FOREIGN|FORTRAN|FOUND|FROM|FULL|GET|GLOBAL|GO|GOTO|GRANT|GROUP|HAVING|HOUR|IDENTITY|IMMEDIATE|IN|INCLUDE|INDEX|INDICATOR|INITIALLY|INNER|INPUT|INSENSITIVE|INSERT|INT|INTEGER|INTERSECT|INTERVAL|INTO|IS|ISOLATION|JOIN|KEY|LANGUAGE|LAST|LEADING|LEFT|LEVEL|LIKE|LOCAL|LOWER|MATCH|MAX|MIN|MINUTE|MODULE|MONTH|NAMES|NATIONAL|NATURAL|NCHAR|NEXT|NO|NONE|NOT|NULL|NULLIF|NUMERIC|OCTET_LENGTH|OF|ON|ONLY|OPEN|OPTION|OR|ORDER|OUTER|OUTPUT|OVERLAPS|PAD|PARTIAL|PASCAL|POSITION|PRECISION|PREPARE|PRESERVE|PRIMARY|PRIOR|PRIVILEGES|PROCEDURE|PUBLIC|READ|REAL|REFERENCES|RELATIVE|RESTRICT|REVOKE|RIGHT|ROLLBACK|ROWS|SCHEMA|SCROLL|SECOND|SECTION|SELECT|SESSION|SESSION_USER|SET|SIZE|SMALLINT|SOME|SPACE|SQL|SQLCA|SQLCODE|SQLERROR|SQLSTATE|SQLWARNING|SUBSTRING|SUM|SYSTEM_USER|TABLE|TEMPORARY|THEN|TIME|TIMESTAMP|TIMEZONE_HOUR|TIMEZONE_MINUTE|TO|TRAILING|TRANSACTION|TRANSLATE|TRANSLATION|TRIM|TRUE|UNION|UNIQUE|UNKNOWN|UPDATE|UPPER|USAGE|USER|USING|VALUE|VALUES|VARCHAR|VARYING|VIEW|WHEN|WHENEVER|WHERE|WITH|WORK|WRITE|YEAR|ZONE";

        /// <summary>
        /// Matches a word against the list of keywords that are likely to become reserved in the future
        /// </summary>
        public static string SqlReservedKeywordFuture = @"^ABSOLUTE$|^ACTION$|^ADMIN$|^AFTER$|^AGGREGATE$|^ALIAS$|^ALLOCATE$|^ARE$|^ARRAY$|^ASENSITIVE$|^ASSERTION$|^ASYMMETRIC$|^AT$|^ATOMIC$|^BEFORE$|^BINARY$|^BIT$|^BLOB$|^BOOLEAN$|^BOTH$|^BREADTH$|^CALL$|^CALLED$|^CARDINALITY$|^CASCADED$|^CAST$|^CATALOG$|^CHAR$|^CHARACTER$|^CLASS$|^CLOB$|^COLLATION$|^COLLECT$|^COMPLETION$|^CONDITION$|^CONNECT$|^CONNECTION$|^CONSTRAINTS$|^CONSTRUCTOR$|^CORR$|^CORRESPONDING$|^COVAR_POP$|^COVAR_SAMP$|^CUBE$|^CUME_DIST$|^CURRENT_CATALOG$|^CURRENT_DEFAULT_TRANSFORM_GROUP$|^CURRENT_PATH$|^CURRENT_ROLE$|^CURRENT_SCHEMA$|^CURRENT_TRANSFORM_GROUP_FOR_TYPE$|^CYCLE$|^DATA$|^DATE$|^DAY$|^DEC$|^DECIMAL$|^DEFERRABLE$|^DEFERRED$|^DEPTH$|^DEREF$|^DESCRIBE$|^DESCRIPTOR$|^DESTROY$|^DESTRUCTOR$|^DETERMINISTIC$|^DIAGNOSTICS$|^DICTIONARY$|^DISCONNECT$|^DOMAIN$|^DYNAMIC$|^EACH$|^ELEMENT$|^END-EXEC$|^EQUALS$|^EVERY$|^EXCEPTION$|^FALSE$|^FILTER$|^FIRST$|^FLOAT$|^FOUND$|^FREE$|^FULLTEXTTABLE$|^FUSION$|^GENERAL$|^GET$|^GLOBAL$|^GO$|^GROUPING$|^HOLD$|^HOST$|^HOUR$|^IGNORE$|^IMMEDIATE$|^INDICATOR$|^INITIALIZE$|^INITIALLY$|^INOUT$|^INPUT$|^INT$|^INTEGER$|^INTERSECTION$|^INTERVAL$|^ISOLATION$|^ITERATE$|^LANGUAGE$|^LARGE$|^LAST$|^LATERAL$|^LEADING$|^LESS$|^LEVEL$|^LIKE_REGEX$|^LIMIT$|^LN$|^LOCAL$|^LOCALTIME$|^LOCALTIMESTAMP$|^LOCATOR$|^MAP$|^MATCH$|^MEMBER$|^METHOD$|^MINUTE$|^MOD$|^MODIFIES$|^MODIFY$|^MODULE$|^MONTH$|^MULTISET$|^NAMES$|^NATURAL$|^NCHAR$|^NCLOB$|^NEW$|^NEXT$|^NO$|^NONE$|^NORMALIZE$|^NUMERIC$|^OBJECT$|^OCCURRENCES_REGEX$|^OLD$|^ONLY$|^OPERATION$|^ORDINALITY$|^OUT$|^OUTPUT$|^OVERLAY$|^PAD$|^PARAMETER$|^PARAMETERS$|^PARTIAL$|^PARTITION$|^PATH$|^PERCENT_RANK$|^PERCENTILE_CONT$|^PERCENTILE_DISC$|^POSITION_REGEX$|^POSTFIX$|^PREFIX$|^PREORDER$|^PREPARE$|^PRESERVE$|^PRIOR$|^PRIVILEGES$|^RANGE$|^READS$|^REAL$|^RECURSIVE$|^REF$|^REFERENCING$|^REGR_AVGX$|^REGR_AVGY$|^REGR_COUNT$|^REGR_INTERCEPT$|^REGR_R2$|^REGR_SLOPE$|^REGR_SXX$|^REGR_SXY$|^REGR_SYY$|^RELATIVE$|^RELEASE$|^RESULT$|^RETURNS$|^ROLE$|^ROLLUP$|^ROUTINE$|^ROW$|^ROWS$|^SAVEPOINT$|^SCOPE$|^SCROLL$|^SEARCH$|^SECOND$|^SECTION$|^SENSITIVE$|^SEQUENCE$|^SESSION$|^SETS$|^SIMILAR$|^SIZE$|^SMALLINT$|^SPACE$|^SPECIFIC$|^SPECIFICTYPE$|^SQL$|^SQLEXCEPTION$|^SQLSTATE$|^SQLWARNING$|^START$|^STATE$|^STATEMENT$|^STATIC$|^STDDEV_POP$|^STDDEV_SAMP$|^STRUCTURE$|^SUBMULTISET$|^SUBSTRING_REGEX$|^SYMMETRIC$|^SYSTEM$|^TEMPORARY$|^TERMINATE$|^THAN$|^TIME$|^TIMESTAMP$|^TIMEZONE_HOUR$|^TIMEZONE_MINUTE$|^TRAILING$|^TRANSLATE_REGEX$|^TRANSLATION$|^TREAT$|^TRUE$|^UESCAPE$|^UNDER$|^UNKNOWN$|^UNNEST$|^USAGE$|^USING$|^VALUE$|^VAR_POP$|^VAR_SAMP$|^VARCHAR$|^VARIABLE$|^WHENEVER$|^WIDTH_BUCKET$|^WINDOW$|^WITHIN$|^WITHOUT$|^WORK$|^WRITE$|^XMLAGG$|^XMLATTRIBUTES$|^XMLBINARY$|^XMLCAST$|^XMLCOMMENT$|^XMLCONCAT$|^XMLDOCUMENT$|^XMLELEMENT$|^XMLEXISTS$|^XMLFOREST$|^XMLITERATE$|^XMLNAMESPACES$|^XMLPARSE$|^XMLPI$|^XMLQUERY$|^XMLSERIALIZE$|^XMLTABLE$|^XMLTEXT$|^XMLVALIDATE$|^YEAR$|^ZONE$";

        /// <summary>
        /// Will match against the list of keywords that are likely to become reserved in the future and are used in a larger text
        /// </summary>
        public static string SqlReservedKeywordFutureEx = @"ABSOLUTE|ACTION|ADMIN|AFTER|AGGREGATE|ALIAS|ALLOCATE|ARE|ARRAY|ASENSITIVE|ASSERTION|ASYMMETRIC|AT|ATOMIC|BEFORE|BINARY|BIT|BLOB|BOOLEAN|BOTH|BREADTH|CALL|CALLED|CARDINALITY|CASCADED|CAST|CATALOG|CHAR|CHARACTER|CLASS|CLOB|COLLATION|COLLECT|COMPLETION|CONDITION|CONNECT|CONNECTION|CONSTRAINTS|CONSTRUCTOR|CORR|CORRESPONDING|COVAR_POP|COVAR_SAMP|CUBE|CUME_DIST|CURRENT_CATALOG|CURRENT_DEFAULT_TRANSFORM_GROUP|CURRENT_PATH|CURRENT_ROLE|CURRENT_SCHEMA|CURRENT_TRANSFORM_GROUP_FOR_TYPE|CYCLE|DATA|DATE|DAY|DEC|DECIMAL|DEFERRABLE|DEFERRED|DEPTH|DEREF|DESCRIBE|DESCRIPTOR|DESTROY|DESTRUCTOR|DETERMINISTIC|DIAGNOSTICS|DICTIONARY|DISCONNECT|DOMAIN|DYNAMIC|EACH|ELEMENT|END-EXEC|EQUALS|EVERY|EXCEPTION|FALSE|FILTER|FIRST|FLOAT|FOUND|FREE|FULLTEXTTABLE|FUSION|GENERAL|GET|GLOBAL|GO|GROUPING|HOLD|HOST|HOUR|IGNORE|IMMEDIATE|INDICATOR|INITIALIZE|INITIALLY|INOUT|INPUT|INT|INTEGER|INTERSECTION|INTERVAL|ISOLATION|ITERATE|LANGUAGE|LARGE|LAST|LATERAL|LEADING|LESS|LEVEL|LIKE_REGEX|LIMIT|LN|LOCAL|LOCALTIME|LOCALTIMESTAMP|LOCATOR|MAP|MATCH|MEMBER|METHOD|MINUTE|MOD|MODIFIES|MODIFY|MODULE|MONTH|MULTISET|NAMES|NATURAL|NCHAR|NCLOB|NEW|NEXT|NO|NONE|NORMALIZE|NUMERIC|OBJECT|OCCURRENCES_REGEX|OLD|ONLY|OPERATION|ORDINALITY|OUT|OUTPUT|OVERLAY|PAD|PARAMETER|PARAMETERS|PARTIAL|PARTITION|PATH|PERCENT_RANK|PERCENTILE_CONT|PERCENTILE_DISC|POSITION_REGEX|POSTFIX|PREFIX|PREORDER|PREPARE|PRESERVE|PRIOR|PRIVILEGES|RANGE|READS|REAL|RECURSIVE|REF|REFERENCING|REGR_AVGX|REGR_AVGY|REGR_COUNT|REGR_INTERCEPT|REGR_R2|REGR_SLOPE|REGR_SXX|REGR_SXY|REGR_SYY|RELATIVE|RELEASE|RESULT|RETURNS|ROLE|ROLLUP|ROUTINE|ROW|ROWS|SAVEPOINT|SCOPE|SCROLL|SEARCH|SECOND|SECTION|SENSITIVE|SEQUENCE|SESSION|SETS|SIMILAR|SIZE|SMALLINT|SPACE|SPECIFIC|SPECIFICTYPE|SQL|SQLEXCEPTION|SQLSTATE|SQLWARNING|START|STATE|STATEMENT|STATIC|STDDEV_POP|STDDEV_SAMP|STRUCTURE|SUBMULTISET|SUBSTRING_REGEX|SYMMETRIC|SYSTEM|TEMPORARY|TERMINATE|THAN|TIME|TIMESTAMP|TIMEZONE_HOUR|TIMEZONE_MINUTE|TRAILING|TRANSLATE_REGEX|TRANSLATION|TREAT|TRUE|UESCAPE|UNDER|UNKNOWN|UNNEST|USAGE|USING|VALUE|VAR_POP|VAR_SAMP|VARCHAR|VARIABLE|WHENEVER|WIDTH_BUCKET|WINDOW|WITHIN|WITHOUT|WORK|WRITE|XMLAGG|XMLATTRIBUTES|XMLBINARY|XMLCAST|XMLCOMMENT|XMLCONCAT|XMLDOCUMENT|XMLELEMENT|XMLEXISTS|XMLFOREST|XMLITERATE|XMLNAMESPACES|XMLPARSE|XMLPI|XMLQUERY|XMLSERIALIZE|XMLTABLE|XMLTEXT|XMLVALIDATE|YEAR|ZONE";
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Utility\Size.cs
using System;

namespace Sqlcollaborative.Dbatools.Utility
{
    /// <summary>
    /// Class that reports File size.
    /// </summary>
    [Serializable]
    public class Size : IComparable<Size>, IComparable
    {
        /// <summary>
        /// Number of bytes contained in whatever object uses this object as a property
        /// </summary>
        public long Byte { get; set; }

        /// <summary>
        /// Kilobyte representation of the bytes
        /// </summary>
        public double Kilobyte
        {
            get
            {
                return (Byte / 1024d);
            }
        }

        /// <summary>
        /// Megabyte representation of the bytes
        /// </summary>
        public double Megabyte
        {
            get
            {
                return (Byte / 1048576d);
            }
        }

        /// <summary>
        /// Gigabyte representation of the bytes
        /// </summary>
        public double Gigabyte
        {
            get
            {
                return (Byte / 1073741824d);
            }
        }

        /// <summary>
        /// Terabyte representation of the bytes
        /// </summary>
        public double Terabyte
        {
            get
            {
                return (Byte / 1099511627776d);
            }
        }

        /// <summary>
        /// Number if digits behind the dot.
        /// </summary>
        public int? Digits
        {
            get
            {
                return _digits ?? UtilityHost.SizeDigits;
            }
            set
            {
                _digits = value == null ? null : (value.Value <= 0 ? 0 : value);
            }
        }
        private int? _digits;

        /// <summary>
        /// How the size object should be displayed.
        /// </summary>
        public SizeStyle? Style
        {
            get
            {
                return _style ?? UtilityHost.SizeStyle;
            }
            set
            {
                _style = value;
            }
        }
        private SizeStyle? _style;

        /// <summary>
        /// Shows the default string representation of size
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            string format = "{0:N" + Digits + "} {1}";
            switch (Style)
            {
                case SizeStyle.Plain:
                    return Byte.ToString();
                case SizeStyle.Byte:
                    return (String.Format("{0} {1}", Byte, "B"));
                case SizeStyle.Kilobyte:
                    return (String.Format(format, Kilobyte, "KB"));
                case SizeStyle.Megabyte:
                    return (String.Format(format, Megabyte, "MB"));
                case SizeStyle.Gigabyte:
                    return (String.Format(format, Gigabyte, "GB"));
                case SizeStyle.Terabyte:
                    return (String.Format(format, Terabyte, "TB"));
                default:
                    if (Terabyte > 1)
                    {
                        return (String.Format(format, Terabyte, "TB"));
                    }
                    if (Gigabyte > 1)
                    {
                        return (String.Format(format, Gigabyte, "GB"));
                    }
                    if (Megabyte > 1)
                    {
                        return (String.Format(format, Megabyte, "MB"));
                    }
                    if (Kilobyte > 1)
                    {
                        return (String.Format(format, Kilobyte, "KB"));
                    }
                    if (Byte > -1)
                    {
                        return (String.Format("{0} {1}", Byte, "B"));
                    }
                    if (Byte == -1)
                        return "Unlimited";
                    return "";
            }
        }

        /// <summary>
        /// Simple equality test
        /// </summary>
        /// <param name="obj">The object to test it against</param>
        /// <returns>True if equal, false elsewise</returns>
        public override bool Equals(object obj)
        {
            var size = obj as Size;
            return (size != null  && (Byte == size.Byte));
        }

        /// <inheritdoc cref="Int64.GetHashCode"/>
        /// <remarks>The hashcode of the underlying size</remarks>
        public override int GetHashCode()
        {
            // ReSharper disable once NonReadonlyMemberInGetHashCode
            return Byte.GetHashCode();
        }

        /// <summary>
        /// Creates an empty size.
        /// </summary>
        public Size()
        {

        }

        /// <summary>
        /// Creates a size with some content
        /// </summary>
        /// <param name="Byte">The length in bytes to set the size to</param>
        public Size(long Byte)
        {
            this.Byte = Byte;
        }

        /// <inheritdoc cref="IComparable{Size}.CompareTo"/>
        /// <remarks>For sorting</remarks>
        public int CompareTo(Size obj)
        {
            return Byte.CompareTo(obj.Byte);
        }

        /// <inheritdoc cref="IComparable.CompareTo"/>
        /// <remarks>For sorting</remarks>
        /// <exception cref="ArgumentException">If you compare with something invalid.</exception>
        public int CompareTo(object obj)
        {
            var size = obj as Size;
            if (size != null)
            {
                return CompareTo(size);
            }
            throw new ArgumentException(String.Format("Cannot compare a {0} to a {1}", typeof(Size).FullName, obj.GetType().FullName));
        }

        #region MathOperators
        /// <summary>
        /// Adds two sizes
        /// </summary>
        /// <param name="a">The first size to add</param>
        /// <param name="b">The second size to add</param>
        /// <returns>The sum of both sizes</returns>
        public static Size operator +(Size a, Size b)
        {
            return new Size(a.Byte + b.Byte);
        }

        /// <summary>
        /// Substracts two sizes
        /// </summary>
        /// <param name="a">The first size to substract</param>
        /// <param name="b">The second size to substract</param>
        /// <returns>The difference between both sizes</returns>
        public static Size operator -(Size a, Size b)
        {
            return new Size(a.Byte - b.Byte);
        }

        /// <summary>
        /// Multiplies two sizes with each other
        /// </summary>
        /// <param name="a">The size to multiply</param>
        /// <param name="b">The size to multiply with</param>
        /// <returns>A multiplied size.</returns>
        public static Size operator *(Size a, double b)
        {
            return new Size((long)(a.Byte * b));
        }

        /// <summary>
        /// Divides one size by another. 
        /// </summary>
        /// <param name="a">The size to divide</param>
        /// <param name="b">The size to divide with</param>
        /// <returns>Divided size (note: Cut off)</returns>
        public static Size operator /(Size a, double b)
        {
            return new Size((long)((double)a.Byte / b));
        }

        /// <summary>
        /// Multiplies two sizes with each other
        /// </summary>
        /// <param name="a">The size to multiply</param>
        /// <param name="b">The size to multiply with</param>
        /// <returns>A multiplied size.</returns>
        public static Size operator *(Size a, Size b)
        {
            return new Size(a.Byte * b.Byte);
        }

        /// <summary>
        /// Divides one size by another.
        /// </summary>
        /// <param name="a">The size to divide</param>
        /// <param name="b">The size to divide with</param>
        /// <returns>Divided size (note: Cut off)</returns>
        public static Size operator /(Size a, Size b)
        {
            return new Size((long)((double)a.Byte / (double)b.Byte));
        }

        #endregion
        #region ImplicitCasts

        /// <summary>
        /// Implicitly converts int to size
        /// </summary>
        /// <param name="a">The number to convert</param>
        public static implicit operator Size(int a)
        {
            return new Size(a);
        }

        /// <summary>
        /// Implicitly converts int to size
        /// </summary>
        /// <param name="a">The number to convert</param>
        public static implicit operator Size(decimal a)
        {
            return new Size((long)a);
        }

        /// <summary>
        /// Implicitly converts size to int
        /// </summary>
        /// <param name="a">The size to convert</param>
        public static implicit operator Int32(Size a)
        {
            return (Int32)a.Byte;
        }

        /// <summary>
        /// Implicitly converts long to size
        /// </summary>
        /// <param name="a">The number to convert</param>
        public static implicit operator Size(long a)
        {
            return new Size(a);
        }

        /// <summary>
        /// Implicitly converts size to long
        /// </summary>
        /// <param name="a">The size to convert</param>
        public static implicit operator Int64(Size a)
        {
            return a.Byte;
        }

        /// <summary>
        /// Implicitly converts string to size
        /// </summary>
        /// <param name="a">The string to convert</param>
        public static implicit operator Size(String a)
        {
            return new Size(Int64.Parse(a));
        }

        /// <summary>
        /// Implicitly converts double to size
        /// </summary>
        /// <param name="a">The number to convert</param>
        public static implicit operator Size(double a)
        {
            return new Size((long)a);
        }

        /// <summary>
        /// Implicitly converts size to double
        /// </summary>
        /// <param name="a">The size to convert</param>
        public static implicit operator double(Size a)
        {
            return a.Byte;
        }
        #endregion Operators
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Utility\SizeStyle.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Sqlcollaborative.Dbatools.Utility
{
    /// <summary>
    /// How size objects should be displayed
    /// </summary>
    public enum SizeStyle
    {
        /// <summary>
        /// The size object is styled dependend on the number stored within.
        /// </summary>
        Dynamic = 1,

        /// <summary>
        /// The size object is shown as a plain number
        /// </summary>
        Plain = 2,

        /// <summary>
        /// The size object is styled as a byte number
        /// </summary>
        Byte = 4,

        /// <summary>
        /// The size object is styled as a kilobyte number
        /// </summary>
        Kilobyte = 8,

        /// <summary>
        /// The size object is styled as a megabyte number
        /// </summary>
        Megabyte = 16,

        /// <summary>
        /// The size object is styled as a Gigabyte number
        /// </summary>
        Gigabyte = 32,

        /// <summary>
        /// The size object is styled as a Terabyte number
        /// </summary>
        Terabyte = 64
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Utility\UtilityHost.cs
using System;
using System.Collections.Generic;
using System.Management.Automation;
using System.Reflection;

namespace Sqlcollaborative.Dbatools.Utility
{
    /// <summary>
    /// Provides static resources to utility-namespaced stuff
    /// </summary>
    public static class UtilityHost
    {
        /// <summary>
        /// Restores all DateTime objects to their default display behavior
        /// </summary>
        public static bool DisableCustomDateTime = false;

        /// <summary>
        /// Restores all timespan objects to their default display behavior.
        /// </summary>
        public static bool DisableCustomTimeSpan = false;

        /// <summary>
        /// Formating string for date-style datetime objects.
        /// </summary>
        public static string FormatDate = "dd MMM yyyy";

        /// <summary>
        /// Formating string for datetime-style datetime objects
        /// </summary>
        public static string FormatDateTime = "yyyy-MM-dd HH:mm:ss.fff";

        /// <summary>
        /// Formating string for time-style datetime objects
        /// </summary>
        public static string FormatTime = "HH:mm:ss";

        /// <summary>
        /// The number of digits a size object shows by default
        /// </summary>
        public static int SizeDigits = 2;

        /// <summary>
        /// The way size objects are usually displayed
        /// </summary>
        public static SizeStyle SizeStyle = SizeStyle.Dynamic;

        /// <summary>
        /// Implement's VB's Like operator logic.
        /// </summary>
        /// <param name="CaseSensitive">Whether the comparison is case sensitive</param>
        /// <param name="Pattern">The pattern the string is compared with</param>
        /// <param name="String">The string that is being compared with a pattern</param>
        public static bool IsLike(string String, string Pattern, bool CaseSensitive = false)
        {
            if (!CaseSensitive)
            {
                String = String.ToLower();
                Pattern = Pattern.ToLower();
            }

            // Characters matched so far
            int matched = 0;

            // Loop through pattern string
            for (int i = 0; i < Pattern.Length;)
            {
                // Check for end of string
                if (matched > String.Length)
                    return false;

                // Get next pattern character
                char c = Pattern[i++];
                if (c == '[') // Character list
                {
                    // Test for exclude character
                    bool exclude = (i < Pattern.Length && Pattern[i] == '!');
                    if (exclude)
                        i++;
                    // Build character list
                    int j = Pattern.IndexOf(']', i);
                    if (j < 0)
                        j = String.Length;
                    HashSet<char> charList = CharListToSet(Pattern.Substring(i, j - i));
                    i = j + 1;

                    if (charList.Contains(String[matched]) == exclude)
                        return false;
                    matched++;
                }
                else if (c == '?') // Any single character
                {
                    matched++;
                }
                else if (c == '#') // Any single digit
                {
                    if (!Char.IsDigit(String[matched]))
                        return false;
                    matched++;
                }
                else if (c == '*') // Zero or more characters
                {
                    if (i < Pattern.Length)
                    {
                        // Matches all characters until
                        // next character in pattern
                        char next = Pattern[i];
                        int j = String.IndexOf(next, matched);
                        if (j < 0)
                            return false;
                        matched = j;
                    }
                    else
                    {
                        // Matches all remaining characters
                        matched = String.Length;
                        break;
                    }
                }
                else // Exact character
                {
                    if (matched >= String.Length || c != String[matched])
                        return false;
                    matched++;
                }
            }
            // Return true if all characters matched
            return (matched == String.Length);
        }

        /// <summary>
        /// Converts a string of characters to a HashSet of characters. If the string
        /// contains character ranges, such as A-Z, all characters in the range are
        /// also added to the returned set of characters.
        /// </summary>
        /// <param name="charList">Character list string</param>
        private static HashSet<char> CharListToSet(string charList)
        {
            HashSet<char> set = new HashSet<char>();

            for (int i = 0; i < charList.Length; i++)
            {
                if ((i + 1) < charList.Length && charList[i + 1] == '-')
                {
                    // Character range
                    char startChar = charList[i++];
                    i++; // Hyphen
                    char endChar = (char)0;
                    if (i < charList.Length)
                        endChar = charList[i++];
                    for (int j = startChar; j <= endChar; j++)
                        set.Add((char)j);
                }
                else set.Add(charList[i]);
            }
            return set;
        }

        /// <summary>
        /// Returns the current callstack
        /// </summary>
        public static IEnumerable<CallStackFrame> Callstack
        {
            get
            {
                // Works on PS4+
                try { return _CallstackNew; }

                // Needed for PS3
                catch { return _CallstackOld; }
            }
        }

        /// <summary>
        /// Returns the current callstack on PS4+
        /// </summary>
        private static IEnumerable<CallStackFrame> _CallstackNew
        {
            get
            {
                return System.Management.Automation.Runspaces.Runspace.DefaultRunspace.Debugger.GetCallStack();
            }
        }

        /// <summary>
        /// Returns the current callstack on PS3
        /// </summary>
        private static IEnumerable<CallStackFrame> _CallstackOld
        {
            get
            {
                MethodInfo method = System.Management.Automation.Runspaces.Runspace.DefaultRunspace.Debugger.GetType().GetMethod("GetCallStack", BindingFlags.NonPublic | BindingFlags.Instance);
                return (IEnumerable<CallStackFrame>)method.Invoke(System.Management.Automation.Runspaces.Runspace.DefaultRunspace.Debugger, null);
            }
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Utility\Validation.cs
using System;

namespace Sqlcollaborative.Dbatools.Utility
{
    using System.Net;
    using System.Net.NetworkInformation;
    using System.Text.RegularExpressions;
    /// <summary>
    /// Provides helper methods that aid in validating stuff.
    /// </summary>
    public static class Validation
    {
        /// <summary>
        /// Tests whether a given string is the local host.
        /// Does NOT use DNS resolution, DNS aliases will NOT be recognized!
        /// </summary>
        /// <param name="Name">The name to test for being local host</param>
        /// <returns>Whether the name is localhost</returns>
        public static bool IsLocalhost(string Name)
        {
            #region Handle IP Addresses
            try
            {
                IPAddress tempAddress;
                IPAddress.TryParse(Name, out tempAddress);
                if (IPAddress.IsLoopback(tempAddress))
                    return true;

                foreach (NetworkInterface netInterface in NetworkInterface.GetAllNetworkInterfaces())
                {
                    IPInterfaceProperties ipProps = netInterface.GetIPProperties();
                    foreach (UnicastIPAddressInformation addr in ipProps.UnicastAddresses)
                    {
                        if (tempAddress.ToString() == addr.Address.ToString())
                            return true;
                    }
                }
            }
            catch { }
            #endregion Handle IP Addresses

            #region Handle Names
            try
            {
                if (Name == ".")
                    return true;
                if (Name.ToLower() == "localhost")
                    return true;
                if (Name.ToLower() == Environment.MachineName.ToLower())
                    return true;
                if (Name.ToLower() == (Environment.MachineName + "." + Environment.GetEnvironmentVariable("USERDNSDOMAIN")).ToLower())
                    return true;
            }
            catch { }
            #endregion Handle Names
            return false;
        }

        /// <summary>
        /// Tests whether a given string is a recommended instance name. Recommended names musst be legal, nbot on the ODBC list and not on the list of words likely to become reserved keywords in the future.
        /// </summary>
        /// <param name="InstanceName">The name to test. MAY contain server name, but will NOT test the server.</param>
        /// <returns>Whether the name is recommended</returns>
        public static bool IsRecommendedInstanceName(string InstanceName)
        {
            string temp;
            if (InstanceName.Split('\\').Length == 1) { temp = InstanceName; }
            else if (InstanceName.Split('\\').Length == 2) { temp = InstanceName.Split('\\')[1]; }
            else { return false; }

            if (Regex.IsMatch(temp, RegexHelper.SqlReservedKeyword, RegexOptions.IgnoreCase)) { return false; }
            if (Regex.IsMatch(temp, RegexHelper.SqlReservedKeywordFuture, RegexOptions.IgnoreCase)) { return false; }
            if (Regex.IsMatch(temp, RegexHelper.SqlReservedKeywordOdbc, RegexOptions.IgnoreCase)) { return false; }

            if (temp.ToLower() == "default") { return false; }
            if (temp.ToLower() == "mssqlserver") { return false; }

            if (!Regex.IsMatch(temp, RegexHelper.InstanceName, RegexOptions.IgnoreCase)) { return false; }

            return true;
        }

        /// <summary>
        /// Tests whether a given string is a valid target for targeting as a computer. Will first convert from idn name.
        /// </summary>
        public static bool IsValidComputerTarget(string ComputerName)
        {
            try
            {
                System.Globalization.IdnMapping mapping = new System.Globalization.IdnMapping();
                string temp = mapping.GetAscii(ComputerName);
                return Regex.IsMatch(temp, RegexHelper.ComputerTarget);
            }
            catch { return false; }
        }

        /// <summary>
        /// Tests whether a given string is a valid instance name.
        /// </summary>
        /// <param name="InstanceName">The name to test. MAY contain server name, but will NOT test the server.</param>
        /// <param name="Lenient">Setting this to true will make the validation ignore default and mssqlserver as illegal names (as they are illegal names for named instances, but legal for targeting)</param>
        /// <returns>Whether the name is legal</returns>
        public static bool IsValidInstanceName(string InstanceName, bool Lenient = false)
        {
            string temp;
            if (InstanceName.Split('\\').Length == 1) { temp = InstanceName; }
            else if (InstanceName.Split('\\').Length == 2) { temp = InstanceName.Split('\\')[1]; }
            else { return false; }

            if (Regex.IsMatch(temp, RegexHelper.SqlReservedKeyword, RegexOptions.IgnoreCase)) { return false; }

            if (!Lenient)
            {
                if (temp.ToLower() == "default") { return false; }
                if (temp.ToLower() == "mssqlserver") { return false; }
            }

            if (!Regex.IsMatch(temp, RegexHelper.InstanceName, RegexOptions.IgnoreCase)) { return false; }

            return true;
        }
    }
}
tools\dbatools\bin\projects\dbatools\dbatools\Validation\LinkedServerResult.cs
using System;

namespace Sqlcollaborative.Dbatools.Validation
{
    /// <summary>
    /// The results of testing linked server connectivity as seen from the server that was linked to.
    /// </summary>
    [Serializable]
    public class LinkedServerResult
    {
        /// <summary>
        /// The name of the server running the tests
        /// </summary>
        public string ComputerName;

        /// <summary>
        /// The name of the instance running the tests
        /// </summary>
        public string InstanceName;

        /// <summary>
        /// The full name of the instance running the tests
        /// </summary>
        public string SqlInstance;

        /// <summary>
        /// The name of the linked server, the connectivity with whom was tested
        /// </summary>
        public string LinkedServerName;

        /// <summary>
        /// The name of the remote computer running the linked server.
        /// </summary>
        public string RemoteServer;

        /// <summary>
        /// The test result
        /// </summary>
        public bool Connectivity;

        /// <summary>
        /// Text interpretation of the result. Contains error messages if the test failed.
        /// </summary>
        public string Result;

        /// <summary>
        /// Creates an empty object
        /// </summary>
        public LinkedServerResult()
        {

        }

        /// <summary>
        /// Creates a test result with prefilled values
        /// </summary>
        /// <param name="ComputerName">The name of the server running the tests</param>
        /// <param name="InstanceName">The name of the instance running the tests</param>
        /// <param name="SqlInstance">The full name of the instance running the tests</param>
        /// <param name="LinkedServerName">The name of the linked server, the connectivity with whom was tested</param>
        /// <param name="RemoteServer">The name of the remote computer running the linked server.</param>
        /// <param name="Connectivity">The test result</param>
        /// <param name="Result">Text interpretation of the result. Contains error messages if the test failed.</param>
        public LinkedServerResult(string ComputerName, string InstanceName, string SqlInstance, string LinkedServerName, string RemoteServer, bool Connectivity, string Result)
        {
            this.ComputerName = ComputerName;
            this.InstanceName = InstanceName;
            this.SqlInstance = SqlInstance;
            this.LinkedServerName = LinkedServerName;
            this.RemoteServer = RemoteServer;
            this.Connectivity = Connectivity;
            this.Result = Result;
        }
    }
}
tools\dbatools\bin\projects\dbatools\msbuild.rsp
 
tools\dbatools\bin\PSScriptAnalyzerRules.psd1
@{
    IncludeRules = @(
                    'PSAvoidUsingCmdletAliases',
                    'PSAvoidUsingWriteHost',
                    'PSAvoidDefaultValueSwitchParameter',
                    'PSReservedCmdletChar',
                    'PSReservedParams',
                    'PSAvoidUsingUserNameAndPassWordParams',
                    'PSAvoidUsingPlaintTextForPassword',
                    'PSAvoidUsingWMICmdlet',
                    'PSAvoidUsingWriteHost',
                    'PSMisleadingBacktick',
                    'PSMissingModuleMainifestField',
                    'PSPossibleIncorrectComparisonWithNull',
                    'PSUseApprovedVerbs',
                    'PSUseOutputTypeCorrectly',
                    'PSShouldProcess',
                    'PSUserToExportFieldsInManifest',
                    'PSUseSingularNouns',
                    'PSAvoidUsingInvokeExpression',
                    'PSUseShouldProcessForStateChangingFunctions',
                    'PSUseDeclaredVarsMoreThanAssignments'
                    )
}
tools\dbatools\bin\smo\Accessibility.dll
md5: FC53B66F0B862A05CF533B44A1CAEDBB | sha1: 5E5BD9F1A9E6D8EA047E17BE5BE2D387CA1AB8E9 | sha256: 3594B22AB2FD554E1890B48D5E64C0A6A70A0CABD00B7453E8C921C11C08D5E6 | sha512: D45E01CA7A48124667FAA37A51F9452E95E69DC72C718D583E17A77888976C6B8378ED82CF0B9DB7BF1177E8D77FB0FC5A04846396D70867DA45BFCBE2DEB592
tools\dbatools\bin\smo\EnvDTE.dll
md5: F4B84B1EB2711575D6AB68576A1C9027 | sha1: D827A68F25AC23D72A0028A825A304BDF98B2517 | sha256: 2380C3B549B96BF791C735894D2D25CF102DD0094118337C4B4AFC60CFDE5812 | sha512: 8D252EC540CBFBBCE08A73ABD4E19908EBC02EA3CD46D34FD8EAA983DBBF8C5185A462FBDAF84A0583E1EDF98AD0E357D9F605AA3488B728A9AD9D0C817954A9
tools\dbatools\bin\smo\Microsoft.AnalysisServices.AppLocal.Core.dll
md5: 2ACB72F8B7FFFB8D53BC79C412FBD19A | sha1: 6B54CBF8F4305176C4F11737D75FA54C5D694904 | sha256: EAF198781427C8F148022C38FA89E8D6E77DCAD420F5EFC028FAB3E45452D7A3 | sha512: D3AABA6CFE2C6E84E6C04C46E4B9022DAE1B130A95E0D3442EF9F44A9300ED03A17022CFD575DD1FA1A6C188E2B92728293027011C0CB115488B335584473F47
tools\dbatools\bin\smo\Microsoft.AnalysisServices.AppLocal.dll
md5: FCAA6034315522B0A10F18B522A89602 | sha1: F6990976F55616AA986CCEC2719BDB17FA9C00C3 | sha256: 7C8FD89AF1C118D5DC0C46BEC6484358467E6EA278EFB8C5B4CA6B63C62B717D | sha512: 6D622B81BE12A2079BFE7724A5892245F43CEC0581EEFBA33B8A7241AF6E80DD58D239E7D2A42C47E499591865C3A098984C218ED74F6303B559067049E90EEA
tools\dbatools\bin\smo\Microsoft.Azure.KeyVault.Core.dll
md5: 8C454E6D06D56C19F355F702B15EBB15 | sha1: 6D4322B7BC25A50E0C5EFC80DD71824592D3A040 | sha256: 3A1475D6F1A99AB2A85AFEDFF3DB6454D901EBF1DE1D58E294EA2CB16516648A | sha512: 6D1C221430668BE2C7DAAE9D27AAA621038F8F52F5AC3CF9A6D02D10F33E85718E08017E5CF6CDB9E2CB10CE66EB9212DCC6C88FB17C4FB486C7D71720B6BDFB
tools\dbatools\bin\smo\Microsoft.Data.Edm.dll
md5: 383FE8CAD1F26D4A307FFD490B96D307 | sha1: B5D13E6B49B72825627B44C3501091A12580F741 | sha256: 2488A00E8045A929110BC35F4719D40C653C89F1799CB97767CB01FE94CF3E3B | sha512: E8F7A59764BE375BED343E3F60F12A7537973DA219D2101796CCCD455EB70ECD8AF784C0A2A7D96D7FB3D6A4DA73C784889DEC086019CF0D26CB700E738BB042
tools\dbatools\bin\smo\Microsoft.Data.OData.dll
md5: F631F51807B66AC4E3C9869122FC1309 | sha1: 496EBB02213A694101849ADFCE095F4671459D5B | sha256: 9E635E1F33493D570A5C42B394450569B14FF418026FE530CA364FC4AB395778 | sha512: 26FB696407D8218E80995732FA176BD7EDE900C358004AF4B7F5D359863A4ABC1B9462B6BCA3540C72A6A342CFED962252B06D58B63A469148EA48AE6D47AF53
tools\dbatools\bin\smo\Microsoft.Data.Tools.Components.dll
md5: FB02906D3F269BEEB3035CCA0F411E61 | sha1: 204B41159B0A875D24CD5762A98C6CC474A44CE2 | sha256: 5D97EC841E7A696EA54BEB6E499F52B203DED61335C82D63DE2D72C3FCD9121E | sha512: 6F5F9FB92A30038C9839E2524682ED45F0A96604E496D24B3D62E06B2D643CEB8AFD24A641B0DB0C89E52624F5C198033BE482F2539F6F0F29ED9208F56A9C9E
tools\dbatools\bin\smo\Microsoft.Data.Tools.Contracts.dll
md5: E388AB530054F8C12D01549D1106F57D | sha1: 5BD95EF1303E453B75E443D3E3F6CEB9C327C767 | sha256: 0570A8B2D205954D402DAC7CDE1C5D52C3774E6D96404959D5DD70FAA6248D52 | sha512: 03C5ACB9A4C0E5A49C3B86E2ABAE2B11485BD644E44B157E89B59C716945DB69F6BC2538BF744B6AA0C5B9F39D645B7ECC778BC12F804772F6AE4381E3622357
tools\dbatools\bin\smo\Microsoft.Data.Tools.Schema.Sql.dll
md5: 54A947A8CED5A122FFA7B0A84041420E | sha1: C631EA3C22410A317252CAF41950B75F4C5488BE | sha256: 17AB90D36B5E3E3F4D76E80213C9B539448FDD46FAFD2CBD306465044C1629EC | sha512: A7AF2D5E7A3E8DACDD44B9492C4A14C78633B51D0AEE58EDFE88EFC9D231C78F3CC42D66948DB8A1C58F44434B864EAE9D367C4E8F7B19A4099847D59A9DE07F
tools\dbatools\bin\smo\Microsoft.Data.Tools.Schema.SqlTasks.targets
 
tools\dbatools\bin\smo\Microsoft.Data.Tools.Schema.Tasks.Sql.dll
md5: B5AFC86E733FE19DAE8A6DFB71901A4E | sha1: F076620639B6EAF2A086292D46F479457730ACAA | sha256: 6387E707E5A9983C27879462AE1A9FE0AD336A27425113EE064380B7B006CD71 | sha512: 44469F97E547AE8B8EB18557BB2B607C3EAF914B0A1F9FC2691FA46493D6CDD554E52FA9F0147A7560D3C5D2E7F5B006ACDB65558340C9C5433016965AC0EC72
tools\dbatools\bin\smo\Microsoft.Data.Tools.Schema.Utilities.Sql.dll
md5: B3A6CA5EE94FF2E86811D687658555C1 | sha1: 5D22774B875DEC5AC4BDF36BF69C05E7E6280A81 | sha256: 1C3B90F76D5E7908C15688F5138EECDBC208D3C4D33ACE7CA79BCC52B3A17F14 | sha512: A76524B0D47BA4E5AE36A3EB9523AB598ABE7BB9F0C7354E2FDC278C3DFDA4CD2051749F36E8309E1D85ADD6392BEC93BB858E1622A3EE7F07B016A7AF24C314
tools\dbatools\bin\smo\Microsoft.Data.Tools.Utilities.dll
md5: C6CC649FA71CA67148297E8A5993DBE3 | sha1: 82CDF6397A03D9B8931A6BC8D6296951F790FB35 | sha256: EFF493CEFF05385D55183A1FBC45311AD92781A5484FB0E15A725016B7EFB8D9 | sha512: 6D655FE5BBE7A6B644F9A21F54613189DB9C55BD560B7D9565873650137ECB8EBAFC4DE1F04E4338E8EC670716394ECE9850C0DA654111D27F54FEDF9E15B474
tools\dbatools\bin\smo\Microsoft.DataTransfer.Common.Utils.dll
md5: B628255C8F1E2AFBCCF2D8DBF02779E4 | sha1: 2F53622D555DDD30882CF128AD69C38A1F4E392C | sha256: 25C8FBEA431843B4150E2E4A770139C3B52C8D3EEBA983D38B03313F00271D7E | sha512: 702F2036C303BD97C7A6A76EE79B03A0B46D604A0FAD306FEEE023D6857CACFFDBD291E95D094607AF5A90456F3C971E5939D91A43E8C37BBBEE911BA24B2BFB
tools\dbatools\bin\smo\Microsoft.Practices.TransientFaultHandling.Core.dll
md5: 04C9A563ED69D537D753B5A45F870B44 | sha1: 28EBC515826108FA736A5D34DDDCAE86A52FFC10 | sha256: 09AE9F682D0E610AE4C560891EB103C2F1E16B18780CC60856EEA5B953402F5D | sha512: 963CA4299050C8ECCE9D1DDD5DD569A9CC1E241880D6294D5E8D3CDD2E05A42B3ABB483013C96BF5F18B27BF1C39BF68484503F61D66DCA587C527CA7675CF22
tools\dbatools\bin\smo\Microsoft.SqlServer.ADONETSrc.dll
md5: 906581DA5946FE4352DB4BE645911E0C | sha1: 300CB414E57056A738458294C2EE712C11F1D526 | sha256: D05BD284A6A0D8E7A17E690A11B41365AD60AC8B350656E50B15C31C444AB4E0 | sha512: 1AC03C3F4B2EEE7F02A264A8D600B69746F66C4F7312736CEA2C9BD1113B11E1FDAC9040838B4E1EC696D7329B7964859C1FEA1834976E3FAF35BA2C6E0F0996
tools\dbatools\bin\smo\Microsoft.SqlServer.ASTasks.dll
md5: C35C5E48D1B31934489AC69C41929952 | sha1: 8FC17A2E3F774429E87EE8AD0EA609C4E9BB43C9 | sha256: 2FC3B752B425B64CB127E224834FE00781A82232DD97EAB9E4B10A23B03A6896 | sha512: 5F770C9D3C7B9FEEC2F24B43F94DA5976F5559ED103568AF69EFC10E95DCB363273D198A355410B2B03CB6E1463A7967382CBD4820F9D5DB66DEFADDFFB9931A
tools\dbatools\bin\smo\Microsoft.SqlServer.BatchParser.dll
md5: 9B2DA9E2F4CA29792614DA567EEE4DB4 | sha1: C44D1E8DEBBF1E197F4DF8F1FB7786EDDB00420E | sha256: 28DB74E05232B7C15F2832C548B77998164730D959564A3652CAD4937F445221 | sha512: 9449A2A614A9D06723B7687F933DB42984414DDDD4EAE8C0D807251E21199FD6ED8A3221BC29AA813121EE4D609785F7D085140725B5274F3AE3EBFFCDBAC3C7
tools\dbatools\bin\smo\Microsoft.SqlServer.BatchParserClient.dll
md5: C253D24E75845D8BA3B483CB5937E725 | sha1: 80B24C4001B0D6D99842048272B9CE8C17020557 | sha256: 01B28FA8B11AF5100098DD026E4C636736D932C0FDFBA00ED7C85054E1AE628B | sha512: B3A9B16342B0AC540B833FD57DE9D739E3507CDCB0503492BE42C5747561444812F8F024D5D443E9241744FDFB3A1303E762AC65DCA3DD0DF6EA6B322ACE4C11
tools\dbatools\bin\smo\Microsoft.SqlServer.BulkInsertTaskConnections.dll
md5: 336D96B873E2C70F6C81370F19521E09 | sha1: 3E9DEA38211CB0FA680127CAABD358F9DA54F270 | sha256: DFD6BE0724F0ECF76A1DA670B40A5D155C3B9043E598411B79813FF4B21984E4 | sha512: A02E4B667BCFAEB68D12515D38FA5F7CBBBE6983EEC989947941FA5FD63A717CC5F6A38E8B8FB24058D3D348959B0BA1A3D043EB211BB70CDA47B1684174CCE7
tools\dbatools\bin\smo\Microsoft.SqlServer.ConnectionInfo.dll
md5: 7446B5C8FFCBA09039ABF785D1BB4A88 | sha1: 6AD988335E008315F06A43A722DF69AB5B4722F0 | sha256: 3407221FE6F297E845AEB91A4886BB7B268A79CA2A73E273BDC2D36ECA26ADA6 | sha512: 752433FCFAE9F16292B07F18A9416A7FE786F82038BB7E0C30E79FD1B2ED68BC9DB60143B68F29B05CE412DB8ACE34E3A7609CDBE0FF14B0AB28AEEE98A43CB9
tools\dbatools\bin\smo\Microsoft.SqlServer.ConnectionInfoExtended.dll
md5: 9208D9667BAE268B63C09BF8E6C79FE4 | sha1: A3DA904733DE2F8161A1103F8061C4392D97BA6C | sha256: F0A1A6600895B521AFE9C17F545D851DB218BCCE93C76A3E96608115F4F4DEBC | sha512: DADDBD541CF0DE43E9D0BEDA524453EBEE533BDC30957E8F71C3D1F5CC76D0D3339061FB06C2FA9F9F92258E5E31FF13E6179F77FA30B1B984E1419E1FE589A7
tools\dbatools\bin\smo\Microsoft.SqlServer.CustomControls.dll
md5: EDCC8CFC99AAD924ADCA73096FEB892C | sha1: 92CD8EE21D8A07127BB0F379BFDBC48D54A998DF | sha256: 0D60EDB8ECAD19D45862A2738F7A688011DE28F2F2A5F92CC663898D4D091B17 | sha512: AE6080A45B9B16C07DA2FAD5E5DA719842A3D80EA910B59912251C511300DF26EB64FFD27D53D54A80040DB763980D5485E084A0B76797A88F3B9D72BA70E4A1
tools\dbatools\bin\smo\Microsoft.SqlServer.Dac.dll
md5: 11CE124B92362C6DAF66E038515A2DF5 | sha1: E11F403F9A92ED69CB5D3ECFF464B9286D50587C | sha256: DF15554804BB08351DF170434E2E994475F6AA3F5B6A465CA30052A13F6EAF0A | sha512: 5036C4855CE72374BC688010D4A0E5F0A9F55F08B5B4D5421C8A53EB4CB7DB53AC42493AA79D7774682427FAFDC38A47D083E16D5973E49BC821EFB88BFDD424
tools\dbatools\bin\smo\Microsoft.SqlServer.Dac.Extensions.dll
md5: 2DD7BA70365204298B4A381D500F433C | sha1: AF2655A5220761227E33EF89583346C7FFD9CD72 | sha256: 2A7D545774CAD03C72F3E6E5B06B65F75A716052B0A2D36E48DA3124BF0B54FD | sha512: 490F1D96C33FAEA13F73109D6068C887D86B86E7BDEA002DCBA42AB2DE3C47E8E63ED1C345AF252163D72FC6FE7045635A1C64BA8B3ACB3A9C50E7FF63584817
tools\dbatools\bin\smo\Microsoft.SqlServer.Dac.Extensions.xml
<?xml version="1.0"?>
<doc>
    <assembly>
        <name>Microsoft.SqlServer.Dac.Extensions</name>
    </assembly>
    <members>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisModelValidationMode">
            <summary>
            Determines the validation performed on the model prior to code analysis.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisModelValidationMode.Full">
            <summary>
            The TSqlModel is fully validated prior to code analysis.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisModelValidationMode.None">
            <summary>
            No validation is performed on the TSqlModel prior to code analysis
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisResult">
            <summary>
            The results of Code Analysis against a particular model. Will include any
            <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem"/>s found, in addition to any errors that occurred
            during analysis
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisResult.#ctor">
            <summary>
            Creates a <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisResult"/>
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisResult.GetAllErrors">
            <summary>
            Gets all errors, whether during initialization or during the analysis process. This
            does not include the <see cref="P:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisResult.Problems"/> found during analysis since these are
            returned separately.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisResult.SerializeResultsToXml(System.String)">
            <summary>
            Serializes the results in XML format to an output file.
            
            Only the problems discovered during analysis are output. No errors that occurred during
            the analysis process will be output.  
            </summary>
            <param name="outputFilename">
            The filename for the output file. 
            This should be a fully qualified path to the file, or if a relative file path is used
            then the resolved path will be relative to the location of the executable running for 
            your process.
            If a relative file path is used this w
            </param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisResult.SerializeResultsToXml(System.IO.Stream)">
            <summary>
            Serializes the results in XML format to an output stream. For backwards compatibility 
            
            Only the problems discovered during analysis are output. No errors that occurred during
            the analysis process will be output. 
            </summary>
            <param name="output">
            An output stream to write the XML results to. This must support write
            </param>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisResult.Problems">
            <summary>
            Any problems discovered by the rules run during code analysis
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisResult.AnalysisErrors">
            <summary>
            Errors and messages that occurred during analysis. 
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisResult.InitializationErrors">
            <summary>
            Gets errors that occurred during initialization of code analysis, rule lookup,
            and anything other than the analysis itself.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisResult.SuppressionErrors">
            <summary>
            Gets errors that occurred during calling of the message suppression routine.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisResult.AnalysisSucceeded">
            <summary>
            Was analysis completed successfully? Analysis may fail for a number of reasons, for instance
            if errors already exist in the model before processing.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService">
            <summary>
            A service that runs code analysis against a model and provides results to the caller.
             
            Note that this class is not thread-safe since multiple simulataneous calls to <see cref="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService.Analyze(Microsoft.SqlServer.Dac.Model.TSqlModel)"/> are
            not supported. However it is possible to call <see cref="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService.Cancel"/> from a thread while a separate thread is
            waiting on the <see cref="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService.Analyze(Microsoft.SqlServer.Dac.Model.TSqlModel)"/> method to complete.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService.GetRules">
            <summary>
            Gets the rules loaded by the service. The current state of these rules (enabled, disabled, rule problem severity)
            can be queried, as can information about the rule such as its ID and metadata. 
            </summary>
            <returns>
            list of <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.RuleDescriptor"/> objects representing rules discovered by the service, their metadata and status
            </returns>
            <remarks>To discover any problems loading rules, <see cref="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService.GetRuleLoadErrors"/> should be called</remarks>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService.GetRuleLoadErrors">
            <summary>
            Gets any errors that may have occurred during loading of the analysis rules.
            This causes rules to be loaded by the service.
            </summary>
            <returns>A list of errors that occurred during rule loading.</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService.ApplyRuleSettings(Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisRuleSettings)">
            <summary>
            Applies the specified rule settings against the service's configuration. 
            </summary>
            <param name="settings"><see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisRuleSettings"/> describing which rules should be run</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService.SetProblemSuppressor(System.Predicate{Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblemSuppressionContext})">
            <summary>
            Sets the problem suppression predicate to be applied when suppressing rules. 
            This is an optional feature that supports ignoring problems raised by a rule.
            The problem might be suppressed based on the element the problem was raised against, or the source 
            that the problem was found in. 
            </summary>
            <param name="shouldSuppressProblem">
            Predicate that should examine the suppression context and decide whether the problem needs to be suppressed
            </param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService.Cancel">
            <summary>
            Cancels execution of the analysis service
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService.Analyze(Microsoft.SqlServer.Dac.Model.TSqlModel)">
            <summary>
            Performs the analysis of the model. This will initialize all required resources, run analysis
            and report back results to the caller. 
            
            If the <see cref="P:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService.ResultsFile"/> and/or <see cref="P:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService.CodeAnalysisSucceededFile"/>
            are specified then these will be deleted at the start of the analysis, and new files created at the
            end of the analysis
            </summary>
            <param name="model">The <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/> to be analyzed</param>
            <returns>
            <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisResult"/> describing the problems found and any errors that
            occurred during processing
            </returns>
            <exception cref="T:System.ArgumentNullException">If the <paramref name="model"/> is null</exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService.Execute">
            <summary>
            Main entry point for task execution.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService.ExecuteValidateElementStatusStep">
            <summary>
            Ensure the model builds successfully - otherwise there is no point in continuing analysis
            since for SCA it's assumed that the model is valid already.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService.ExecuteInitializeMessageSuppressionStep">
            <summary>
            Wrap whatever message suppression logic has been defined in a routine that will
            call into that code and catch any errors thrown by customer logic
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService.SuppressProblemCallback(Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblemSuppressionContext)">
            <summary>
            Check if the rule in context is suppressed. Handles exceptions thrown by the problem suppression code
            so that execution does not get stopped due to issues in external code
            </summary>
            <param name="context"></param>
            <returns>Return true if rule is suppressed for modelElement; false otherwise</returns>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService.ResultsFile">
            <summary>
            Optional path to a results file. If this is specified, the analysis service will save all results to
            an XML file at the end of analysis. If no file path is specified then this will not be done automatically,
            but <see cref="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisResult.SerializeResultsToXml(System.String)"/> can be used to output the results after
            analysis has completed.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService.CodeAnalysisSucceededFile">
            <summary>
            Optional path to a file that is created if analysis succeeds. This can be used to check whether analysis
            should be run or not - for instance if analyzing a dacpac, comparing the modification time for the dacpac versus
            the analysis success file can tell you whether the dacpac is more recent than the last analysis.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService.ValidationMode">
            <summary>
            Determines the validation performed on the model prior to code analysis. This can be used to bypass
            the requirement that a TSqlModel be valid prior to analysis by a code analysis rule.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService.CancellationToken">
            <summary>
            The <see cref="P:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService.CancellationToken"/> used to indicate whether execution should be canceled. 
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService.IsCanceled">
            <summary>
            Gets a value that indicates whether the analysis was canceled
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisServiceFactory">
            <summary>
            Factory class that supports creation of <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService"/> objects.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisServiceFactory.CreateAnalysisService(Microsoft.SqlServer.Dac.Model.TSqlModel)">
            <summary>
            Creates an analysis service with the standard properties and all rules turned on by default.
            
            This is a convenience method that takes the <see cref="P:Microsoft.SqlServer.Dac.Model.TSqlModel.Version"/> from the model
            and passes it to the <see cref="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisServiceFactory.CreateAnalysisService(Microsoft.SqlServer.Dac.Model.SqlServerVersion)"/>
            method.
            </summary>
            <param name="model">
            The <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/> that will be analyzed. The version of the model must be known at the
            time the service is created since only rules compatible with that <see cref="T:Microsoft.SqlServer.Dac.Model.SqlServerVersion"/>
            will be loaded and run.
            </param>
            <returns>
            A <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService"/> that can analyze the contents of the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/>
            </returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisServiceFactory.CreateAnalysisService(Microsoft.SqlServer.Dac.Model.TSqlModel,Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisServiceSettings)">
            <summary>
            Creates an analysis service and configures it using the <paramref name="settings"/> passed to the method.
            
            The settings are a convenient way to set up the service in one step, but if you wish to
            specify some settings after creating the service this is possible via methods and properties on
            the <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService"/> itself.
            
            This is a convenience method that takes the <see cref="P:Microsoft.SqlServer.Dac.Model.TSqlModel.Version"/> from the model
            and passes it to the <see cref="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisServiceFactory.CreateAnalysisService(Microsoft.SqlServer.Dac.Model.SqlServerVersion)"/>
            method.
            </summary>
            <param name="model">
            The <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/> that will be analyzed. This must be known at the time the
            service is created since only rules compatible with the <see cref="T:Microsoft.SqlServer.Dac.Model.SqlServerVersion"/> for the model
            will be loaded and run.
            </param>
            <param name="settings">Settings object defining how to configure the service</param>
            <returns>A <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService"/> that can analyze the contents of the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/></returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisServiceFactory.CreateAnalysisService(Microsoft.SqlServer.Dac.Model.SqlServerVersion)">
            <summary>
            Creates an analysis service with the standard properties and all rules turned on by default.
            
            </summary>
            <param name="version">
            The expected <see cref="T:Microsoft.SqlServer.Dac.Model.SqlServerVersion"/> of models to be analyzed. This should be known at the time the
            service is created since only rules compatible with this <see cref="T:Microsoft.SqlServer.Dac.Model.SqlServerVersion"/> 
            will be loaded and run. 
            </param>
            <returns>
            A <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService"/> that can analyze the contents of the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/>
            </returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisServiceFactory.CreateAnalysisService(Microsoft.SqlServer.Dac.Model.SqlServerVersion,Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisServiceSettings)">
            <summary>
            Creates an analysis service and configures it using the <paramref name="settings"/> passed to the method.
            
            The settings are a convenient way to set up the service in one step, but if you wish to
            specify some settings after creating the service this is possible via methods and properties on
            the <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService"/> itself.
            </summary>
            <param name="version">
            The expected <see cref="T:Microsoft.SqlServer.Dac.Model.SqlServerVersion"/> of models to be analyzed. This must be known at the time the
            service is created since only rules compatible with this <see cref="T:Microsoft.SqlServer.Dac.Model.SqlServerVersion"/> 
            will be loaded and run. 
            </param>
            <param name="settings">Settings object defining how to configure the service</param>
            <returns>A <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService"/> that can analyze the contents of the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/></returns>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisServiceSettings">
            <summary>
            Defines the optional configuration settings for a <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService"/>. This can determine which
            rules are run, how to suppress certain problems, and where to save results to.
            
            These settings can also be set on the <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService"/> after it has been created, but are included
            here to make construction more convenient.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisServiceSettings.#ctor">
            <summary>
            Creates a new <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisServiceSettings"/> object.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisServiceSettings.RuleSettings">
            <summary>
            Optional rule settings define what rules should be run during analysis and the severity of the problems created. 
            If no <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisRuleSettings"/> are specified the default behavior is to run all discovered rules.
            
            This property is applied using <see cref="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService.ApplyRuleSettings(Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisRuleSettings)"/>. 
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisServiceSettings.ShouldSuppressProblem">
            <summary>
            An optional predicate that can be used to suppress problems raised by rules during analysis.
            See <see cref="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService.SetProblemSuppressor(System.Predicate{Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblemSuppressionContext})"/> for more information.
            
            This property is applied using <see cref="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService.SetProblemSuppressor(System.Predicate{Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblemSuppressionContext})"/>. 
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisServiceSettings.ResultsFile">
            <summary>
            Optional path to a results file. If this is specified, the analysis service will save all results to
            an XML file at the end of analysis. If no file path is specified then this will not be done automatically,
            but <see cref="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisResult.SerializeResultsToXml(System.String)"/> can be used to output the results after
            analysis has completed.
            
            This property directly maps to <see cref="P:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService.ResultsFile"/>.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisServiceSettings.CodeAnalysisSucceededFile">
            <summary>
            Optional path to a file that is created if analysis succeeds. This can be used to check whether analysis
            should be run or not - for instance if analyzing a dacpac, comparing the modification time for the dacpac versus
            the analysis success file can tell you whether the dacpac is more recent than the last analysis.
            
            This property directly maps to <see cref="P:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService.CodeAnalysisSucceededFile"/>.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisServiceSettings.ValidationMode">
            <summary>
            Determines the validation performed on the model prior to code analysis. This can be used to bypass
            the requirement that a TSqlModel be valid prior to analysis by a code analysis rule.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.Internal.RuleUtils">
            <summary>
            Internal rule utilities that will never meet the conditions for sharing with the public via an open API
            or inclusion in a sample project
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Internal.RuleUtils.TryGetTSqlObject(Microsoft.SqlServer.Dac.Model.TSqlModel,Microsoft.Data.Tools.Schema.Sql.SchemaModel.ISqlModelElement,Microsoft.SqlServer.Dac.Model.TSqlObject@)">
            <summary>
            Tries to map an internal element to a public element. 
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Internal.RuleUtils.GetSchemaObjectName(Microsoft.SqlServer.Dac.Model.TSqlModel,Microsoft.SqlServer.Dac.Model.TSqlObject)">
            <summary>
            Get the schema object name in EscapedFullyQualifiedName format from an ISqlModelElement
            </summary>
            <param name="model"></param>
            <param name="element"></param>
            <returns></returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Internal.RuleUtils.GetSchemaObjectName(Microsoft.SqlServer.Dac.Model.TSqlModel,Microsoft.SqlServer.Dac.Model.ElementDescriptor)">
            <summary>
            Get the schema object name in EscapedFullyQualifiedName format from a Sql element Descriptor
            </summary>
            <param name="model"></param>
            <param name="elementDescriptor"></param>
            <returns></returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Internal.RuleUtils.GetSchemaObjectName(Microsoft.SqlServer.Dac.Model.TSqlModel,Microsoft.SqlServer.Dac.Model.ElementDescriptor,Microsoft.SqlServer.Dac.Model.ElementNameStyle)">
            <summary>
            Get the schema object name in user's choice of format from a Sql element Descriptor
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Internal.RuleUtils.GetSchemaObjectName(Microsoft.SqlServer.Dac.Model.TSqlModel,Microsoft.SqlServer.Dac.Model.TSqlObject,Microsoft.SqlServer.Dac.Model.ElementNameStyle)">
            <summary>
            Get the schema object name in user's choice of format from an ISqlModelElement
            </summary> 
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.ProblemSuppressionException">
            <summary>
            Represents an exception that occurred when running the rule problem suppression test.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.DacModelException">
            <summary>
            Exception throw from DAC model errors.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.DacModelException.#ctor">
            <summary>
             Construct a new instance of the <see cref="T:Microsoft.SqlServer.Dac.Model.DacModelException"/> class.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.DacModelException.#ctor(System.String)">
            <summary>
            Construct a new instance of the <see cref="T:Microsoft.SqlServer.Dac.Model.DacModelException"/> class with the specified error message.
            </summary>
            <param name="message"></param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.DacModelException.#ctor(System.String,System.Exception)">
            <summary>
            Construct a new instance of the <see cref="T:Microsoft.SqlServer.Dac.Model.DacModelException"/> class with the specified error message
            and a reference to the inner exception that is the cause of this exception.
            </summary>
            <param name="message">
            The message that describes the error.
            </param>
            <param name="innerException">
            The exception that is the cause of the current exception, or a null reference if no inner exception is specified.
            </param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.DacModelException.#ctor(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext)">
             <summary>
            
             </summary>
             <param name="info"></param>
             <param name="context"></param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.DacModelException.GetObjectData(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext)">
            <summary>
            Write exception information to the supplied <see cref="T:System.Runtime.Serialization.SerializationInfo"/> object.
            </summary>
            <param name="info">
            <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds the serialized object data.
            </param>
            <param name="context">
            <see cref="T:System.Runtime.Serialization.StreamingContext"/> that contains contextual information about the source or destination.
            </param>
            <exception cref="T:System.ArgumentNullException">
            If <paramref name="info"/> is a null reference.
            </exception>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelException.Messages">
            <summary>
            Get collection of additional error, warning, and informational messages associated with this exception.
            </summary>
            <value>
            Collection of additional messages associated with this exception.
            </value>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.ProblemSuppressionException.#ctor">
            <summary>
             Construct a new instance of the <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.ProblemSuppressionException"/> class.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.ProblemSuppressionException.#ctor(System.String)">
            <summary>
            Construct a new instance of the <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.ProblemSuppressionException"/> class with the specified error message.
            </summary>
            <param name="message"></param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.ProblemSuppressionException.#ctor(System.String,System.Exception)">
            <summary>
            Construct a new instance of the <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.ProblemSuppressionException"/> class with the specified error message
            and a reference to the inner exception that is the cause of this exception.
            </summary>
            <param name="message">
            The message that describes the error.
            </param>
            <param name="innerException">
            The exception that is the cause of the current exception, or a null reference if no inner exception is specified.
            </param>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.ProjectProblemSuppressor">
            <summary>
            Represents the problem suppressor used by SSDT projects. The <see cref="P:Microsoft.SqlServer.Dac.CodeAnalysis.ProjectProblemSuppressor.ShouldSuppressProblem"/> method
            can be passed to <see cref="P:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisServiceSettings.ShouldSuppressProblem"/> in order to use this 
            class.
            
            This reads suppression information from a file with a name matching
            <see cref="F:Microsoft.SqlServer.Dac.CodeAnalysis.ProjectProblemSuppressor.SuppressionFilename"/> ("StaticCodeAnalysis.SuppressMessages.xml") in the root directory.
            
            All relative paths will be resolved relative to the project folder defined in the constructor
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.CodeAnalysis.ProjectProblemSuppressor.SuppressionFilename">
            <summary>
            The default file name for a message suppression file within a project.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.ProjectProblemSuppressor.CreateSuppressor(Microsoft.Data.Tools.Schema.Sql.RuleEngine.MessageSuppressionFileManager)">
            <summary>
            Internal factory method that creates a new <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.ProjectProblemSuppressor"/>.
            </summary>
            <param name="suppressionFileManager">
            The file manager to use for loading/saving suppression rules. 
            </param>
            <returns><see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.ProjectProblemSuppressor"/></returns>
            <exception cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.ProblemSuppressionException">
            Thrown if there was a problem parsing the XML
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.ProjectProblemSuppressor.CreateSuppressor(System.String)">
            <summary>
            Creates a new <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.ProjectProblemSuppressor"/>.
            </summary>
            <param name="projectFolder">
            The folder in which the XML Message suppression file is found. 
            Any rule suppressed using a relative file path will base the full path off
            this folder location. 
            </param>
            <returns><see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.ProjectProblemSuppressor"/> </returns>
            <exception cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.ProblemSuppressionException">
            Thrown if the Path to the XML file was invalid or if there was a problem parsing the XML
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.ProjectProblemSuppressor.CreateSuppressor(System.String,System.String)">
            <summary>
            Creates a new <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.ProjectProblemSuppressor"/>.
            </summary>
            <param name="projectFolder">
            The folder in which the XML Message suppression file is found. 
            Any rule suppressed using a relative file path will base the full path off
            this folder location. 
            </param>
            <param name="suppressionFilename">'
            The name of the XML message suppression file. This is expected to be under the project folder.
            </param>
            <returns><see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.ProjectProblemSuppressor"/> </returns>
            <exception cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.ProblemSuppressionException">
            Thrown if the Path to the XML file was invalid or if there was a problem parsing the XML
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.ProjectProblemSuppressor.ResetSuppressedProblems">
            <summary>
            Resets the suppressed problems to their default state. If an XML suppression file for the
            project existed already then the state will be reloaded from there, otherwise it will
            be reset so that no problems are suppressed.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.ProjectProblemSuppressor.UnsuppressRulesFromFile(System.String)">
            <summary>
            Removes all problem suppression directives for a particular filename.
            
            The updated set of problems to suppress will be immediately written out to the backing XML file that describes what
            rules should be suppressed for a given file. 
            </summary>
            <param name="fileName">path to the file</param>
            <exception cref="T:System.ArgumentNullException">If the <paramref name="fileName"/> is null</exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.ProjectProblemSuppressor.GetSuppressedProblems">
            <summary>
            Gets information on what problems will be suppressed. Specifically, a set of
            <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.SuppressedProblemInfo"/> objects will be returned that defines each combination of a
            rule being suppressed and the file name for which the rule is suppressed.
            </summary>
            <returns>A list of <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.SuppressedProblemInfo"/> objects defining each combination of a
            rule being suppressed and the file name for which the rule is suppressed</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.ProjectProblemSuppressor.AddSuppressedProblems(System.Collections.Generic.IEnumerable{Microsoft.SqlServer.Dac.CodeAnalysis.SuppressedProblemInfo})">
            <summary>
            Adds one or more instances of <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.SuppressedProblemInfo"/> to the set of suppressed problems.
            The full set of suppressed problems will be immediately written out to the backing XML file.
            </summary>
            <param name="suppressedProblems">
            An IEnumerable of <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.SuppressedProblemInfo"/> objects defining each combination of a 
            file for which some problems should be suppressed, and the specific rule whose problems should be
            suppressed.
            </param>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.ProjectProblemSuppressor.ShouldSuppressProblem">
            <summary>
            Gets the predicate that can be used to
            
            If any errors occur during
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.RuleDescriptor">
            <summary>
            Describes a rule discovered by the rule engine and supports configuration of its properties.
            Descriptors inherit properties from <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.RuleConfiguration"/>, to support enabling/disabling the rule during
            analysis and specifying the severity for problems created by the rule. 
            
            This class is not intended to be subclasses by external users - instances of <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.RuleDescriptor"/> are created
            by the analysis service.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.RuleConfiguration">
            <summary>
            Specifies how a rule should be configured - should this be enabled or disabled? What severity should be applied
            for the rule?
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.RuleConfiguration.#ctor(System.String)">
            <summary>
            Creates a new <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.RuleConfiguration"/> for a rule, using the default settings for <see cref="P:Microsoft.SqlServer.Dac.CodeAnalysis.RuleConfiguration.Enabled"/>
            and <see cref="P:Microsoft.SqlServer.Dac.CodeAnalysis.RuleConfiguration.Severity"/>.
            </summary>
            <param name="ruleId">ID of the rule this configuration specifies. Cannot be null or whitespace.</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.RuleConfiguration.#ctor(System.String,System.Boolean,Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblemSeverity)">
            <summary>
            Creates a new <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.RuleConfiguration"/> for a rule.
            </summary>
            <param name="ruleId">ID of the rule this configuration specifies. Cannot be null or whitespace.</param>
            <param name="enabled">Is this rule enabled or disabled?</param>
            <param name="severity">What is the severity of problems identified by this rule? Should be either 
            <see cref="F:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblemSeverity.Warning"/> or <see cref="F:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblemSeverity.Error"/>.</param>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.RuleConfiguration.RuleId">
            <summary>
            Gets the unique Id used to identify the rule. This is the fully qualified ID, which would usually be
            in the form "My.Org.MyRuleName".
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.RuleConfiguration.Namespace">
            <summary>
            Gets the namespace for the rule. This is the part of the rule that precedes the final "." in the ID name.
            For instance for a rule ID "My.Org.MyRuleName", the namespace would be "My.Org".
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.RuleConfiguration.ShortRuleId">
            <summary>
            Gets the last part of the rule ID. This is the part of the rule that follows the final "." in the ID name.
            For instance for a rule ID "My.Org.MyRuleName", the short rule ID would be "MyRuleName".
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.RuleConfiguration.Enabled">
            <summary>
            Specifies if the rule is enabled and should be included in the code analysis.
            
            The default value is true
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.RuleConfiguration.Severity">
            <summary>
            What severity should problems created by the rule have?
            
            The default severity is <see cref="F:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblemSeverity.Warning"/>
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.RuleDescriptor.#ctor(System.String)">
            <summary>
            Protected constructor to be called by implementing classes.
            </summary>
            <param name="ruleId">ID of the rule being described</param>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.RuleDescriptor.DisplayDescription">
            <summary>
            The description of the rule. This should be a short human readable description of what the
            rule is intended to warn against or block.
            
            This is automatically read from the <see cref="P:Microsoft.SqlServer.Dac.CodeAnalysis.RuleDescriptor.Metadata"/> property
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.RuleDescriptor.DisplayName">
            <summary>
            Display name describing the rule.
            
            This is automatically read from the <see cref="P:Microsoft.SqlServer.Dac.CodeAnalysis.RuleDescriptor.Metadata"/> property
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.RuleDescriptor.Rule">
            <summary>
            The actual instance of the <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.SqlAnalysisRule"/>
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.RuleDescriptor.Metadata">
            <summary>
            The <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.ISqlAnalysisRuleMetadata"/> describing the rule.
            This is a required property that must be defined by subclasses.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisRuleSettings">
            <summary>
            The settings used to configure rules used during analysis. 
            
            Before analysis begins, this settings object will be applied to the rules discovered 
            by the analysis service. If DisableRulesNotInSettings is set to true then any rules 
            not included in these settings will be disabled and not run during analysis. 
            
            These settings are applied by calling the <see cref="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisRuleSettings.ApplySettingsToRules(System.Collections.Generic.IEnumerable{Microsoft.SqlServer.Dac.CodeAnalysis.RuleConfiguration})"/> method
            on rules returned by the engine.
            
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisRuleSettings.#ctor">
            <summary>
            Creates a new <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisRuleSettings"/> object.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisRuleSettings.#ctor(System.Collections.Generic.IEnumerable{Microsoft.SqlServer.Dac.CodeAnalysis.RuleConfiguration})">
            <summary>
            Creates a new <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisRuleSettings"/> containing a set of rules.
            </summary>
            <param name="rules">The rules to include</param>
            <exception cref="T:System.ArgumentNullException">If <paramref name="rules"/> is null</exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisRuleSettings.CreateFromSettingsString(System.String,System.Collections.Generic.IList{Microsoft.SqlServer.Dac.Extensibility.ExtensibilityError}@)">
            <summary>
            Creates a <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisRuleSettings"/> configuration based on a settings string like the one used
            inside SSDT project files. This string defines what rules should be disabled or have their problems treated as
            errors instead of warnings. Any rule not included in the settings string will be enabled by default.
            </summary>
            <param name="settingsString">
            A semicolon delimited string identifying rules to be disabled or have problems treated as errors.
            The format is "-My.Disabled.Rule;+!My.Enabled.RuleWithError;-!My.Disabled.RuleWithError". Hence disabled rules should have "-" 
            before their ID and enabled rules with errors should have "+!" before their ID. 
            Only rules that are to be disabled or have their problems treated as errors should be included in the settings string.
            
            If this parameter is null or contains no valid rules, a default empty configuration will be returned. This will result in all
            discovered rules being run.
            </param>
            <param name="errors">List of errors found when processing the list.</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisRuleSettings.ConvertToSettingsString">
            <summary>
            Converts the rule configuration info in this <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisRuleSettings"/> object into a
            settings string like the one used
            inside SSDT project files. This string defines what rules should be disabled or have their problems treated as
            errors instead of warnings. Any rule not included in the settings string will be enabled by default.
            </summary>
            <returns>
            A semicolon delimited string identifying rules to be disabled or have problems treated as errors.
            The format is "-My.Disabled.Rule;+!My.Enabled.RuleWithError;-!My.Disabled.RuleWithError". Hence disabled rules have "-" 
            before their ID and enabled rules with errors have "+!" before their ID. 
            Only rules that are to be disabled or have their problems treated as errors should be included in the settings string.
            </returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisRuleSettings.AddRange(System.Collections.Generic.IEnumerable{Microsoft.SqlServer.Dac.CodeAnalysis.RuleConfiguration})">
            <summary>
            Adds multiple <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.RuleConfiguration"/> objects to the list of rules
            </summary>
            <param name="rules">The rules to include</param>
            <exception cref="T:System.ArgumentNullException">If <paramref name="rules"/> is null</exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisRuleSettings.Add(Microsoft.SqlServer.Dac.CodeAnalysis.RuleConfiguration)">
            <summary>
            Adds a new <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.RuleConfiguration"/> object to the list of rules
            </summary>
            <param name="item">item to be added</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisRuleSettings.Remove(Microsoft.SqlServer.Dac.CodeAnalysis.RuleConfiguration)">
            <summary>
            Removes a <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.RuleConfiguration"/> object from the list of rules.
            </summary>
            <param name="item">item to be removed</param>
            <returns>true if the item was successfully removed</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisRuleSettings.GetEnumerator">
            <summary>
            <see cref="M:System.Collections.IEnumerable.GetEnumerator"/>
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisRuleSettings.FindConfiguration(System.String)">
            <summary>
            Tries to find the <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.RuleConfiguration"/> that matches the specified
            <paramref name="ruleId"/>. If the configuration doesn't exist then null will be returned
            </summary>
            <param name="ruleId">The fully qualified rule ID</param>
            <returns><see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.RuleConfiguration"/>, or null if none matches the <paramref name="ruleId"/></returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisRuleSettings.TryGetRuleConfiguration(System.String,Microsoft.SqlServer.Dac.CodeAnalysis.RuleConfiguration@)">
            <summary>
            Tries to find the <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.RuleConfiguration"/> that matches the specified
            <paramref name="ruleId"/>.  
            </summary>
            <param name="ruleId">The fully qualified rule ID</param>
            <param name="config">out <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.RuleConfiguration"/>, or null if none matches the <paramref name="ruleId"/></param>
            <returns>true if a configuration matching the specified <paramref name="ruleId"/> was found, false otherwise</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisRuleSettings.ApplySettingsToRules(System.Collections.Generic.IEnumerable{Microsoft.SqlServer.Dac.CodeAnalysis.RuleConfiguration})">
            <summary>
            Applies these settings to another set of rules.
            </summary>
            <param name="rules">The rules the settings should be applied to</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisRuleSettings.EnableRule(System.String)">
            <summary>
            Includes a rule for execution
            </summary>
            <param name="ruleId">Fully qualified Id for the rule</param>
            <exception cref="T:System.ArgumentException">If the <paramref name="ruleId"/> is null or whitespace</exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisRuleSettings.DisableRule(System.String)">
            <summary>
            Excludes a rule from execution
            </summary>
            <param name="ruleId">Fully qualified Id for the rule</param>
            <exception cref="T:System.ArgumentException">If the <paramref name="ruleId"/> is null or whitespace</exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisRuleSettings.TreatRuleProblemAsWarning(System.String)">
            <summary>
            Treat any problems found a rule as warnings
            </summary>
            <param name="ruleId">Fully qualified Id for the rule</param>
            <exception cref="T:System.ArgumentException">If the <paramref name="ruleId"/> is null or whitespace</exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisRuleSettings.TreatRuleProblemAsError(System.String)">
            <summary>
            Treat any problems found a rule as errors
            </summary>
            <param name="ruleId">Fully qualified Id for the rule</param>
            <exception cref="T:System.ArgumentException">If the <paramref name="ruleId"/> is null or whitespace</exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisRuleSettings.IsRuleDisabled(System.String)">
            <summary>
            Is a particular rule in the rule settings disabled?
            </summary>
            <param name="ruleId">Fully qualified Id for the rule</param>
            <returns>true if the problem is disabled; false otherwise</returns>
            <exception cref="T:System.ArgumentException">If the <paramref name="ruleId"/> is null or whitespace</exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisRuleSettings.IsRuleProblemTreatedAsError(System.String)">
            <summary>
            Is a particular rule in the rule settings treated as error?
            </summary>
            <param name="ruleId">Fully qualified Id for the rule</param>
            <returns>true if the problem is treated as error; false otherwise</returns>
            <exception cref="T:System.ArgumentException">If the <paramref name="ruleId"/> is null or whitespace</exception>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisRuleSettings.DisableRulesNotInSettings">
            <summary>
            Should rules not found in these settings be disabled? The default is "false", so that
            any rules not explicitly covered in the settings will still be run during analysis. 
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.RuleException">
            <summary>
            Represent an exception that may occur during a code analysis run
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.RuleException.#ctor">
            <summary>
            Initialize a new instance of <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.RuleException"/> 
            Exception message and inner exceptions are set to null
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.RuleException.#ctor(System.String)">
            <summary>
            Initialize a new instance of <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.RuleException"/>  with a message
            </summary>
            <param name="message">Exception message</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.RuleException.#ctor(System.String,System.Exception)">
            <summary>
            Initialize a new instance of <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.RuleException"/> with a message and an inner exception
            </summary>
            <param name="message">Exception message</param>
            <param name="innerException">Inner exception to attached to this exception</param>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.CommonUtilities.ElementNameAstLocatorSchemaAnalyzer">
            <summary>
            The class to locate the accurate AST that defines the name for a model element
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.SchemaAnalyzer">
            <summary>
            The base class that defines analysis events. 
            
            This analyzer works against the <see cref="T:Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment"/>s that represent 
            the exact DDL language describing an object.
            
            This makes it easier to examine the actual script/fragment structure that represents 
            an object.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SchemaAnalyzer.BeginBatch(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlBatch)">
            <summary>
            This method handles pre-batchprocessing
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SchemaAnalyzer.EndBatch(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlBatch)">
            <summary>
            This method handles post-batchprocessing
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SchemaAnalyzer.BeginDdlStatement(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment)">
            <summary>
            This method handles pre-DDL statement processing 
            </summary>
            <param name="fragment">Node that will be processed</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SchemaAnalyzer.EndDdlStatement(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment)">
            <summary>
            This method handles post-DDL statement processing
            </summary>
            <param name="fragment">Node that was processed</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SchemaAnalyzer.IdentifiedElement(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment,Microsoft.SqlServer.Dac.Model.ElementDescriptor)">
            <summary>
            This method handles the discovery of a new Symbol
            </summary>
            <param name="fragment">Fragment the symbol belongs to</param>
            <param name="elementDescriptor">The element descriptor</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SchemaAnalyzer.BeginDmlStatement(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment)">
            <summary>
            This method handles the pre-DML statement processing
            </summary>
            <param name="fragment">Node that will be processed</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SchemaAnalyzer.EndDmlStatement(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment)">
            <summary>
            This method handles the post-DML statement processing
            </summary>
            <param name="fragment">Node that will be processed</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SchemaAnalyzer.VisitFragment(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment,Microsoft.SqlServer.Dac.Model.ElementDescriptor,Microsoft.SqlServer.Dac.Model.ElementDescriptorRelevance)">
            <summary>
            This method handles additional processing required for a node/schemaidentifier
            </summary>
            <param name="fragment">Node to be processed</param>
            <param name="elementDescriptor">Descriptor to be processed</param>
            <param name="relevance">Relevance</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SchemaAnalyzer.VisitAmbiguousFragment(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment,System.Collections.Generic.IEnumerable{Microsoft.SqlServer.Dac.Model.PotentialElementDescriptor})">
            <summary>
            This method handles additional processing required for a node/schemaidentifier
            </summary>
            <param name="fragment">Node to be processed</param>
            <param name="possibilities">An enumerable of possible hits</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SchemaAnalyzer.IdentifiedSupportingStatement(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment,Microsoft.SqlServer.Dac.Model.ElementDescriptor)">
            <summary>
            This method handles the discovery of a new supporting statement
            </summary>
            <param name="fragment">Node to be processed</param>
            <param name="elementDescriptor"></param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SchemaAnalyzer.AnalyzeScript(Microsoft.SqlServer.Dac.Model.TSqlModel,Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment)">
            <summary>
            Runs the schema analysis against a fragment
            </summary>
            <param name="model"><see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/> the fragment is linked to</param>
            <param name="sqlFragment">the fragment to analyze</param>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.CommonUtilities.ElementNameAstLocatorSchemaAnalyzer.NameAstNode">
            <summary>
            The TSqlFragment referring to the name of the element
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.CommonUtilities.ElementNameAstLocatorSchemaAnalyzer.ElementAstNode">
            <summary>
            The TSqlFragment referring to the element itself
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.SqlCodeAnalysisRule">
            <summary>
            Base class for SQL static code analysis rules. An analysis rule analyzes a model / model element and returns a list of
            problems found during analysis.
            
            Implementing classes must have a <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.ExportCodeAnalysisRuleAttribute"/> defined on the
            class definition to be discovered and used during code analysis. 
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.SqlAnalysisRule">
            <summary>
            Base class for all types of analysis rule. An analysis rule analyzes a model / model element and returns a list of
            problems found during analysis. 
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.SqlAnalysisRule.Analyze(Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleExecutionContext)">
            <summary>
            Performs analysis and returns a list of problems detected
            </summary>
            <param name="ruleExecutionContext">Contains the schema model and model element to analyze</param>
            <returns>The problems detected by the rule in the given element</returns>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.SqlAnalysisRule.SupportedElementTypes">
            <summary>
            Types of elements checked by this rule. Required if the rule scope is <see cref="F:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleScope.Element"/>,
            for <see cref="F:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleScope.Model"/> scoped rules this is not relevant
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Design.DataTypeCompatibilityRule.Analyze(Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleExecutionContext)">
            <summary>
            perform actual analysis
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.RuleSchemaAnalyzer">
            <summary>
            A type of <see cref="T:Microsoft.SqlServer.Dac.Model.SchemaAnalyzer"/> that can be used by code analysis 
            rules to examine the internal structure of a <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>.
            This analyzer works against the <see cref="T:Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment"/>s that represents 
            the exact DDL language describing an object.
            
            This makes it easier to examine the actual script/fragment structure that represents an object.
            </summary>
            <remarks>
            For some data sources such as dacpacs, the backing script and <see cref="T:Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment"/>s 
            will be auto-generated by DacFx. Any analyzer that tries to enforce stylistic rules such as
            blocking the use of brackets to escape object names should be disabled when running against 
            those sources - they would be raising issues about the default DacFx style conventions rather
            than the actual code contents of a project.
            </remarks>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.RuleSchemaAnalyzer.#ctor(Microsoft.SqlServer.Dac.CodeAnalysis.RuleDescriptor,Microsoft.SqlServer.Dac.Model.TSqlModel,Microsoft.SqlServer.Dac.Model.TSqlObject)">
            <summary>
            Creates a <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.RuleSchemaAnalyzer"/> that helps analyze an element on behalf of a rule
            </summary>
            <param name="rule">
            <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.RuleDescriptor"/> defining a rule and its metadata properties.
            This can be used to get the description for a rule and add it to any <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem"/>, among other things
            being created, or query any other metadata information about the rule
            </param>
            <param name="schemaModel">The <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/> being analyzed</param>
            <param name="element">A specific <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> whose backing <see cref="T:Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment"/>
            is to be analyzed</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.RuleSchemaAnalyzer.BeginBatch(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlBatch)">
            <summary>
            <see cref="M:Microsoft.SqlServer.Dac.Model.SchemaAnalyzer.BeginBatch(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlBatch)"/>
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.RuleSchemaAnalyzer.EndBatch(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlBatch)">
            <summary>
            <see cref="M:Microsoft.SqlServer.Dac.Model.SchemaAnalyzer.EndBatch(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlBatch)"/>
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.RuleSchemaAnalyzer.BeginDmlStatement(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment)">
            <summary>
            <see cref="M:Microsoft.SqlServer.Dac.Model.SchemaAnalyzer.BeginDmlStatement(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment)"/>
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.RuleSchemaAnalyzer.EndDmlStatement(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment)">
            <summary>
            <see cref="M:Microsoft.SqlServer.Dac.Model.SchemaAnalyzer.EndDmlStatement(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment)"/>
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.RuleSchemaAnalyzer.BeginDdlStatement(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment)">
            <summary>
            <see cref="M:Microsoft.SqlServer.Dac.Model.SchemaAnalyzer.BeginDdlStatement(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment)"/>
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.RuleSchemaAnalyzer.EndDdlStatement(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment)">
            <summary>
            <see cref="M:Microsoft.SqlServer.Dac.Model.SchemaAnalyzer.EndDdlStatement(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment)"/>
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.RuleSchemaAnalyzer.IdentifiedElement(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment,Microsoft.SqlServer.Dac.Model.ElementDescriptor)">
            <summary>
            <see cref="M:Microsoft.SqlServer.Dac.Model.SchemaAnalyzer.IdentifiedElement(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment,Microsoft.SqlServer.Dac.Model.ElementDescriptor)"/>
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.RuleSchemaAnalyzer.VisitFragment(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment,Microsoft.SqlServer.Dac.Model.ElementDescriptor,Microsoft.SqlServer.Dac.Model.ElementDescriptorRelevance)">
            <summary>
            <see cref="M:Microsoft.SqlServer.Dac.Model.SchemaAnalyzer.VisitFragment(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment,Microsoft.SqlServer.Dac.Model.ElementDescriptor,Microsoft.SqlServer.Dac.Model.ElementDescriptorRelevance)"/>
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.RuleSchemaAnalyzer.VisitAmbiguousFragment(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment,System.Collections.Generic.IEnumerable{Microsoft.SqlServer.Dac.Model.PotentialElementDescriptor})">
            <summary>
            <see cref="M:Microsoft.SqlServer.Dac.Model.SchemaAnalyzer.VisitAmbiguousFragment(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment,System.Collections.Generic.IEnumerable{Microsoft.SqlServer.Dac.Model.PotentialElementDescriptor})"/>
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.RuleSchemaAnalyzer.AnalyzeScriptForSqlRule(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment,System.Collections.Generic.List{Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem})">
            <summary>
            Runs rule analysis against a fragment and adds any problems found to the 
            <paramref name="problems"/> list.
            </summary>
            <param name="sqlFragment">A fragment whose contents should be analyzed</param>
            <param name="problems">Problems found by the analyzer are added to this list</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.RuleSchemaAnalyzer.TryGetModelElementFromPossibilities(System.Collections.Generic.IEnumerable{Microsoft.SqlServer.Dac.Model.PotentialElementDescriptor},Microsoft.SqlServer.Dac.Model.TSqlModel,Microsoft.SqlServer.Dac.Model.TSqlObject@,Microsoft.SqlServer.Dac.Model.ElementDescriptor@,Microsoft.SqlServer.Dac.Model.ElementDescriptorRelevance@)">
            <summary>
            Tries to determine the correct model element from a set of possible matching element descriptors.
            </summary>
            <param name="possibilities">Descriptors for  </param>
            <param name="model">The <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/></param>
            <param name="element">out <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> that matches the best possibility, or null if lookup failed</param>
            <param name="elementDescriptor">out <see cref="T:Microsoft.SqlServer.Dac.Model.ElementDescriptor"/> for the element that was matched, or
            null if the lookup failed</param>
            <param name="relevance">out <see cref="T:Microsoft.SqlServer.Dac.Model.ElementDescriptorRelevance"/> describing the relavance of the 
            <paramref name="elementDescriptor"/> to the <paramref name="element"/></param>
            <returns>true if the lookup succeeded</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.RuleSchemaAnalyzer.AddProblem(Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem)">
            <summary>
            add a problem found by the current analyzer
            </summary>
            <param name="problem"></param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.RuleSchemaAnalyzer.ClearProblems">
            <summary>
            clean up all the problems
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.RuleSchemaAnalyzer.DebugWriteLine(System.String)">
            <summary>
            Print a message to debug window in Debug mode
            </summary>
            <param name="msg"></param>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.RuleSchemaAnalyzer.Problems">
            <summary>
            Get all problems identified by this analyzer
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.RuleSchemaAnalyzer.SchemaModel">
            <summary>
            get the SQL schema manager used by the current analyzer
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.RuleSchemaAnalyzer.Rule">
            <summary>
            Get the the rule associated with the current analyzer
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.RuleSchemaAnalyzer.ModelElement">
            <summary>
            Get the element for which the current analyzer works
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Design.DataTypeCompatibilitySchemaAnalyzer.#ctor(Microsoft.SqlServer.Dac.CodeAnalysis.RuleDescriptor,Microsoft.SqlServer.Dac.Model.TSqlModel,Microsoft.SqlServer.Dac.Model.TSqlObject)">
            <summary>
            constructor
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Design.TypeCompatibilityChecker">
            <summary>
            Note: This class relies on internal code that is shared with the internal validation rules. In order to minimize code 
            reuse and avoid unnecessarily duplicating code, some parts of this class directly use and call into the internal model.
            This is viewed as acceptable as upgrading to the public API isn't really necessary and would not provide significant
            benefits to external users.
            
            In addition, some internal objects such as dynamic column sources cannot be mapped to the public model and hence
            it is necessary to use the internal object model in cases where those objects might have issues
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Design.TypeCompatibilityChecker.CollectLocalColumnDefinitions(System.Collections.Generic.Dictionary{Microsoft.SqlServer.Dac.Model.ElementDescriptor,Microsoft.SqlServer.TransactSql.ScriptDom.ColumnDefinition})">
            <summary>
            convert TSql.DataType to TSqlDataTypeInfo
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Design.TypeCompatibilityChecker.CheckVariableAssignment(Microsoft.SqlServer.TransactSql.ScriptDom.VariableReference,Microsoft.Data.Tools.Schema.Sql.Common.TypeEvaluator.TSqlDataTypeInfo,Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment)">
            <summary>
            This method is used to check variable assignment from proc/function return value to a variable
            Use this if you know the type of proc/function return value
            </summary>
            <param name="variable"></param>
            <param name="fromDataType"></param>
            <param name="fragmentToReport"></param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Design.TypeCompatibilityChecker.CheckAssignmentSetClause(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment)">
            <summary>
            we currently support
              column_name = expression, 
              @variable = expression, and
              @variable = column = expression  
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Design.TypeCompatibilityChecker.CollectParameterInfo(Microsoft.SqlServer.Dac.Model.TSqlObject,System.Collections.Generic.IList{Microsoft.SqlServer.TransactSql.ScriptDom.ExecuteParameter},Microsoft.SqlServer.Dac.Model.ModelCollationComparer,System.Collections.Generic.Dictionary{System.String,Microsoft.Data.Tools.Schema.Sql.Common.TypeEvaluator.TSqlDataTypeInfo}@,System.Collections.ObjectModel.ReadOnlyCollection{Microsoft.Data.Tools.Schema.Sql.SchemaModel.ParameterValueInfo}@)">
            <summary>
            Return a dictionary with parameter name key and type info value
            Return a list of parameter value info that is resolved using parameters from subroutine and execute statement
            </summary>
            <param name="subroutine"></param>
            <param name="executeParameterList"></param>
            <param name="comparer"></param>
            <param name="parameterNameToTypeMap"></param>
            <param name="parameterValueInfo"></param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Design.TypeCompatibilityChecker.TryGetParameters(Microsoft.SqlServer.Dac.Model.TSqlObject,System.Collections.Generic.IEnumerable{Microsoft.SqlServer.Dac.Model.TSqlObject}@)">
            <summary>
            All object types that match a subroutine are expected to have a "Parameters" relationship which we can read.
            We look up the relationship class dynamically since it will vary between Procedures, ScalarFunctions and other
            types
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Design.TypeCompatibilityChecker.TryGetReturnType(Microsoft.SqlServer.Dac.Model.TSqlObject,Microsoft.Data.Tools.Schema.Sql.Common.TypeEvaluator.TSqlDataTypeInfo@)">
            <summary>
            Attempts to look up the return type for a function
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Design.TypeCompatibilityChecker.GetTypeInfoForInsertTarget(Microsoft.SqlServer.TransactSql.ScriptDom.InsertStatement)">
            <summary>
            Must use the internal model here since dynamic object column sources aren't modelable in the public model,
            and this is a situation where those appear.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Design.TypeCompatibilityChecker.GetTypeInfoForAllColumnsOfTable(Microsoft.Data.Tools.Schema.SchemaModel.IModelElement)">
            <summary>
            This method heavily uses the internal implementation - again this whole class struggles to use the
            public model
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Design.TypeCompatibilityChecker.GetTypeInfoForColumn(Microsoft.SqlServer.Dac.Model.ElementDescriptor)">
            <summary>
            Note: This method uses internal methods
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Design.TypeCompatibilityChecker.GetTypeInfoForUdtUddt(Microsoft.SqlServer.Dac.Model.ElementDescriptor)">
            <summary>
            get TSqlDataTypeInfo for the acutal data type of a UDT and UDDT based on its 
            SqlElementDescriptor.
            </summary>
            <param name="uddtDescriptor">ElementDescriptor for the UDDT or UDT</param>
            <returns>actual data type if found, null otherwise</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Design.TypeCompatibilityChecker.TryGetFunctionDefinition(Microsoft.SqlServer.TransactSql.ScriptDom.FunctionCall,Microsoft.SqlServer.TransactSql.ScriptDom.FunctionStatementBody@,System.Collections.Generic.List{Microsoft.Data.Tools.Schema.Sql.Common.TypeEvaluator.TSqlDataTypeInfo}@)">
            <summary>
            Again we've failed to fully use the public model in this method, if this can be reviewed
            at a future date then perhaps this can be fixed.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Design.TypeCompatibilityChecker.IsAScalarModule(Microsoft.SqlServer.Dac.Model.ElementDescriptor)">
            <summary>
            Verifies a descriptor is a type of scalar module (ScalarFunctions and Aggregates count as this) 
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Design.TypeCompatibilityChecker.GetTypeInfoForSequenceName(Microsoft.SqlServer.TransactSql.ScriptDom.SchemaObjectName)">
            <summary>
            This method heavily uses the internal implementation - again this whole class struggles to use the
            public model
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Design.IdentitySchemaAnalyzer">
            <summary>
            The SchemaAnalyzer checks for the usages of system function @@IDENTITY
            and reports its possibe misuse.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Design.IdentitySchemaAnalyzer.#ctor(Microsoft.SqlServer.Dac.CodeAnalysis.RuleDescriptor,Microsoft.SqlServer.Dac.Model.TSqlModel,Microsoft.SqlServer.Dac.Model.TSqlObject)">
            <summary>
            Constructor with SqlAnalysisRule, SqlSchemaModel, and ISqlModelElement provided
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Design.OldStyleJoinSyntaxAnalysisRule.Analyze(Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleExecutionContext)">
            <summary>
            perform actual analysis
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Design.OldStyleJoinSyntaxSchemaAnalyzer.#ctor(Microsoft.SqlServer.Dac.CodeAnalysis.RuleDescriptor,Microsoft.SqlServer.Dac.Model.TSqlModel,Microsoft.SqlServer.Dac.Model.TSqlObject)">
            <summary>
            constructor
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Design.PopulateOutputParametersRule.Analyze(Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleExecutionContext)">
            <summary>
            perform actual analysis
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Design.PopulateOutputParametersSchemaAnalyzer.#ctor(Microsoft.SqlServer.Dac.CodeAnalysis.RuleDescriptor,Microsoft.SqlServer.Dac.Model.TSqlModel,Microsoft.SqlServer.Dac.Model.TSqlObject)">
            <summary>
            constructor
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Design.SelectAsteriskSchemaAnalyzer.#ctor(Microsoft.SqlServer.Dac.CodeAnalysis.RuleDescriptor,Microsoft.SqlServer.Dac.Model.TSqlModel,Microsoft.SqlServer.Dac.Model.TSqlObject)">
            <summary>
            Creates a new <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Design.SelectAsteriskSchemaAnalyzer"/> to analyze a particular element in the model.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Design.SmallVariableLengthTypesRule">
            <summary>
            This rule checks if a small length (less than three) is used for variable-length types, 
            which include VARCHAR, NVARCHAR, and VARBINARY
            Such data types can be used in various scenarios:
                column of regular tables
                column of temporary tables
                column of common table expressions
                subroutine parameter 
                variable
                scalar function return type
                user defined data types
                
            TODO, yangg: we currently dont' handle temporary table columns, cte columns, and variables, 
                         since interpretation is not ready yet
            
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Design.SmallVariableLengthTypesRule.Analyze(Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleExecutionContext)">
            <summary>
            perform actual analysis
            </summary> 
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Naming.ReservedWordsForTypeNameAnalysisRule.Analyze(Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleExecutionContext)">
            <summary>
            perform actual analysis
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Naming.ReservedWordsForTypeNameSchemaAnalyzer.#ctor(Microsoft.SqlServer.Dac.CodeAnalysis.RuleDescriptor,Microsoft.SqlServer.Dac.Model.TSqlModel,Microsoft.SqlServer.Dac.Model.TSqlObject)">
            <summary>
            Constructor with comparison manager 
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Naming.SpecialCharactersInObjectNameRule.DynamicObjectsLookup">
            <summary>
            Provides getters for the types that need to access dynamic objects - there's no clean way to do this.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Naming.SpecialCharactersInObjectNameRule.ProcessBodyScriptElements(Microsoft.SqlServer.Dac.Model.TSqlModel,Microsoft.SqlServer.Dac.Model.TSqlObject,System.Collections.Generic.IList{Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem})">
            <summary>
            A subset of top-level types such as Procedures have a body script containing
            dynamic objects. We don't want to miss flagging errors in these, but these
            aren't accessible via the public model. To work around this we use the internal
            model implementation for this work
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Naming.SpPrefixForStoredProceduresRule.Analyze(Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleExecutionContext)">
            <summary>
            perform actual analysis
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Performance.ExtractDeterministicFunctionCallRule.Analyze(Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleExecutionContext)">
            <summary>
            perform actual analysis
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Performance.ExtractDeterministicFunctionCallSchemaAnalyzer.#ctor(Microsoft.SqlServer.Dac.CodeAnalysis.RuleDescriptor,Microsoft.SqlServer.Dac.Model.TSqlModel,Microsoft.SqlServer.Dac.Model.TSqlObject)">
            <summary>
            constructor
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Performance.LikePercentRule">
            <summary>
            The base rule for checking LikePredicate, we suggest using "m%" instead of "%m".
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Performance.LikePercentRule.Analyze(Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleExecutionContext)">
            <summary>
            perform actual analysis
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Performance.LikePercentSchemaAnalyzer">
            <summary>
            This rule checks whether the patterns of the LIKE predicate from WHERE clauses start
            with "%" while match expression is a column, which can cause a table scan and degrade
            performance
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Performance.LikePercentSchemaAnalyzer.#ctor(Microsoft.SqlServer.Dac.CodeAnalysis.RuleDescriptor,Microsoft.SqlServer.Dac.Model.TSqlModel,Microsoft.SqlServer.Dac.Model.TSqlObject)">
            <summary>
            constructor
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Performance.ColumnInsideExpressionChecker">
            <summary>
            the class is to find out all the columns participating in an expression but not part of function call or ISNULL
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Performance.MoveColumnToOneSideRule.Analyze(Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleExecutionContext)">
            <summary>
            perform actual analysis
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Performance.MoveColumnToOneSideSchemaAnalyzer">
            <summary>
            This rule suggests moving columns to one side of en expression in the WHERE clause, 
            thus to avoid table scan. 
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Performance.MoveColumnToOneSideSchemaAnalyzer.#ctor(Microsoft.SqlServer.Dac.CodeAnalysis.RuleDescriptor,Microsoft.SqlServer.Dac.Model.TSqlModel,Microsoft.SqlServer.Dac.Model.TSqlObject)">
            <summary>
            constructor
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Performance.NonIndexedColumnFromInPredicateRule">
            <summary>
            When we see an In Predicate, we want to check whether there is Table Index
            that it could use to boost the performance. If not, we give a warning. This
            is because In Predicate is executed by a Table Scan instead of Index Seek, which
            hugely slows down the SQL query performance.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Performance.NonIndexedColumnFromInPredicateRule.Analyze(Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleExecutionContext)">
            <summary>
            perform actual analysis
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Performance.NonIndexedColumnFromInPredicateSchemaAnalyzer">
            <summary>
            This checks whether a column test expression of the IN predicate has an
            index or not. If it does not, the evaluation of the predicate can cause
            a table scan and degrade the performance.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Performance.NonIndexedColumnFromInPredicateSchemaAnalyzer.#ctor(Microsoft.SqlServer.Dac.CodeAnalysis.RuleDescriptor,Microsoft.SqlServer.Dac.Model.TSqlModel,Microsoft.SqlServer.Dac.Model.TSqlObject)">
            <summary>
            constructor
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Performance.NonIndexedColumnFromInPredicateSchemaAnalyzer.ColumnHasIndex(Microsoft.SqlServer.Dac.Model.TSqlObject)">
            <summary>
            Check whether the specified column has index or not.
            </summary>
            <returns>true when the column has index; false, otherwise</returns>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Performance.CheckNullableColumnHelper">
            <summary>
            the class is to check whether a nullable column exists in an expression
            other than ISNULL function and IS NULL / IS NOT NULL operators
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Performance.NullableColumnRule">
            <summary>
            The base rule for Nullable Column Performance Rule.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Performance.NullableColumnRule.Analyze(Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleExecutionContext)">
            <summary>
            perform actual analysis
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.Performance.NullableColumnSchemaAnalyzer">
            <summary>
            This rule is to find out all the nullable columns used in WHERE predicates which
            do not act as operands of IS NULL / IS NOT NULL or arguments of ISNULL function
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.ExportCodeAnalysisRuleAttribute">
            <summary>
            Attribute defining a rule export, and the metadata about that rule. Implements ISqlAnalysisRuleMetadata,
            which should be used on the importer side to ensure type consistency
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.ISqlAnalysisRuleMetadata">
            <summary>
            The metadata describing a rule - its namespace, id, scope etc.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.ISqlAnalysisRuleMetadata.Description">
            <summary>
            The description of the rule. This should be a short human readable description of what the
            rule is intended to warn against or block.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.ISqlAnalysisRuleMetadata.RuleScope">
            <summary>
            The scope of this rule. Choose between <see cref="F:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleScope.Element"/> and <see cref="F:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleScope.Model"/>.
            Note that for element scoped rules, the <see cref="P:Microsoft.SqlServer.Dac.CodeAnalysis.SqlAnalysisRule.SupportedElementTypes"/> property must return
            one or more element types that can are supported for analysis by the rule
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.ISqlAnalysisRuleMetadata.Category">
            <summary>
            (Optional) Category used to group the rule in Visual Studio UI. This should never be localized
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.ExportCodeAnalysisRuleAttribute.#ctor(System.String,System.String)">
            <summary>
            Constructor
            </summary>
            <param name="id">Unique ID used to identify this rule. Required parameter, rule will not load if this is missing or empty</param>
            <param name="displayName">Display name to show in the Visual Studio UI. Required parameter, rule will not load if this is missing or empty</param>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.ExportCodeAnalysisRuleAttribute.Description">
            <summary>
            The description of the rule. This should be a short human readable description of what the
            rule is intended to warn against or block. This field is localizable, but this should be done
            be subclassing <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.ExportCodeAnalysisRuleAttribute"/> and overriding the Description property. 
            </summary>
            <example>
            <code>
            // Example of an Attribute class that supports localized descriptions:
            public class TestLocalizedExportCodeAnalysisRuleAttribute : ExportCodeAnalysisRuleAttribute
            {
                private readonly string _descriptionResourceName;
                private string _descriptionValue;
            
                public TestLocalizedExportCodeAnalysisRuleAttribute(string id, string displayName, string descriptionResourceName)
                    : base(id, displayName)
                {
                    _descriptionResourceName = descriptionResourceName;
                }
            
                public override string Description
                {
                    get
                    {
                        if (_descriptionValue == null)
                        {
                            // Using the descriptionResourceName as the key for looking up the description in the resources file. 
                            // MyResources is a resource file in the same project as the LocalizedExportCodeAnalysisRuleAttribute class. 
                            // For each rule, and entry should be added to the resources file with the descriptionResourceName as the
                            // key and the description as the value
                            _descriptionValue = MyResources.ResourceManager.GetString(_descriptionResourceName);
                        }
                        return _descriptionValue;
                    }
                }
            }
            </code>
            </example>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.ExportCodeAnalysisRuleAttribute.RuleScope">
            <summary>
            <see cref="P:Microsoft.SqlServer.Dac.CodeAnalysis.ISqlAnalysisRuleMetadata.RuleScope"/>
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.ExportCodeAnalysisRuleAttribute.Category">
            <summary>
            <see cref="P:Microsoft.SqlServer.Dac.CodeAnalysis.ISqlAnalysisRuleMetadata.Category"/>
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.LocalizedExportCodeAnalysisRuleAttribute.GetAssembly">
            <summary>
            Rules in a different assembly would need to overwrite this
            </summary>
            <returns></returns>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString">
            <summary>
              A strongly-typed resource class, for looking up localized strings, etc.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.ResourceManager">
            <summary>
              Returns the cached ResourceManager instance used by this class.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.Culture">
            <summary>
              Overrides the current thread's CurrentUICulture property for all
              resource lookups using this strongly typed resource class.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.DataTypeCompatibility_ProblemDescription">
            <summary>
              Looks up a localized string similar to Data types of {0}({1}) and {2}({3}) are incompatible..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.DataTypeCompatibility_ProblemDescription_CompatibleCasting">
            <summary>
              Looks up a localized string similar to Expression is cast from {0} to {1}..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.DataTypeCompatibility_ProblemDescription_DataLossCasting">
            <summary>
              Looks up a localized string similar to Data loss might occur when casting from {0} to {1}..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.DataTypeCompatibility_ProblemDescription_ExplicitConversionReqruied">
            <summary>
              Looks up a localized string similar to Data types of {0} and {1} are not implicitly compatible. You must use the CONVERT or CAST function..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.DataTypeCompatibility_ProblemDescription_IncompatibleCasting">
            <summary>
              Looks up a localized string similar to Data types of {0} and {1} are incompatible..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.DataTypeCompatibility_ProblemDescription_IncorrectDataType">
            <summary>
              Looks up a localized string similar to The following data type is not valid in this context: {0}.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.DataTypeCompatibility_RuleName">
            <summary>
              Looks up a localized string similar to Maintain compatibility between data types..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.ExtractDeterministicFunctionCall_ProblemDescription">
            <summary>
              Looks up a localized string similar to Deterministic function call ({0}) might cause an unnecessary table scan..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.ExtractDeterministicFunctionCall_RuleName">
            <summary>
              Looks up a localized string similar to Extract deterministic function calls from WHERE predicates..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.Identity_ProblemDescription">
            <summary>
              Looks up a localized string similar to Potential misuse of system function @@IDENTITY..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.Identity_RuleName">
            <summary>
              Looks up a localized string similar to Consider using SCOPE_IDENTITY instead of @@IDENTITY..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.LikePercent_ProblemDescription">
            <summary>
              Looks up a localized string similar to Avoid using patterns that start with &quot;%&quot; in LIKE predicates.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.LikePercent_RuleName">
            <summary>
              Looks up a localized string similar to Avoid using patterns that start with “%” in LIKE predicates..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.MoveColumnToOneside_ProblemDescription">
            <summary>
              Looks up a localized string similar to A column in an expression to be compared in a predicate might cause a table scan and degrade performance..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.MoveColumnToOneside_RuleName">
            <summary>
              Looks up a localized string similar to In the comparison, simplify the expression that includes indexed columns..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.NonIndexedColumnFromInPredicate_ProblemDescription">
            <summary>
              Looks up a localized string similar to A column without an index that is used as an IN predicate test expression might degrade performance..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.NonIndexedColumnFromInPredicate_RuleName">
            <summary>
              Looks up a localized string similar to Avoid using columns that do not have an index as test expressions in IN predicates..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.NullableColumn_ProblemDescription">
            <summary>
              Looks up a localized string similar to Nullable columns can cause final results to be evaluated as NULL for the predicate..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.NullableColumn_RuleName">
            <summary>
              Looks up a localized string similar to Use ISNULL(column, default value) on nullable columns in expressions..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.OldStyleJoinSyntax_ProblemDescription">
            <summary>
              Looks up a localized string similar to Old-style JOIN syntax is used..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.OldStyleJoinSyntax_RuleName">
            <summary>
              Looks up a localized string similar to Avoid using deprecated syntax when you join tables or views..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.PopulateOutputParameters_ProblemDescription">
            <summary>
              Looks up a localized string similar to Output parameter ({0}) is not populated in all code paths..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.PopulateOutputParameters_RuleName">
            <summary>
              Looks up a localized string similar to Specify values for output parameters in all code paths..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.ReservedWordsForTypeName_FutureKeyword">
            <summary>
              Looks up a localized string similar to A future keyword.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.ReservedWordsForTypeName_Keyword">
            <summary>
              Looks up a localized string similar to A keyword.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.ReservedWordsForTypeName_OdbcKeyword">
            <summary>
              Looks up a localized string similar to An ODBC keyword.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.ReservedWordsForTypeName_ProblemDescription">
            <summary>
              Looks up a localized string similar to {0}({1}) is used as a type name..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.ReservedWordsForTypeName_RuleName">
            <summary>
              Looks up a localized string similar to Avoid using reserved words for type names..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.SelectAsterisk_ProblemDescription">
            <summary>
              Looks up a localized string similar to The shape of the result set produced by a SELECT * statement will change if the underlying table or view structure changes..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.SelectAsterisk_RuleName">
            <summary>
              Looks up a localized string similar to Avoid SELECT * in stored procedures, views, and table-valued functions..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.SmallVariableLengthTypes_OneElement">
            <summary>
              Looks up a localized string similar to one element.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.SmallVariableLengthTypes_ProblemDescription">
            <summary>
              Looks up a localized string similar to Avoid {0} of only {1}..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.SmallVariableLengthTypes_RuleName">
            <summary>
              Looks up a localized string similar to Avoid using types of variable length that are size 1 or 2..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.SmallVariableLengthTypes_TwoOrMoreElements">
            <summary>
              Looks up a localized string similar to {0} elements.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.SpecialCharactersInObjectName_ProblemDescription">
            <summary>
              Looks up a localized string similar to Object name({0}) contains special characters..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.SpecialCharactersInObjectName_RuleName">
            <summary>
              Looks up a localized string similar to Avoid using special characters in object names..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.SpPrefixForStoredProcedures_ProblemDescription">
            <summary>
              Looks up a localized string similar to Stored procedure({0}) includes sp_ prefix in its name..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Rules.SqlRulesResourceString.SpPrefixForStoredProcedures_RuleName">
            <summary>
              Looks up a localized string similar to Avoid using sp_ as a prefix for stored procedures..
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.Internal.TsqlScriptDomUtils">
            <summary>
            Utility class for TransactSql.ScriptDom functionality. These functions will not be
            part of the public API, but they may be added to the public samples project so that users can
            understand how we use the ScriptDom APIs for writing rules.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Internal.TsqlScriptDomUtils.IsSubroutineViewOrTrigger(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment)">
            <summary>
            Checks if a fragment represents the body of a subroutine, a view, 
            or a trigger. These have similar characteristics, for instance the ability to
            include select statements against tables. 
            </summary>
            <param name="fragment"><see cref="T:Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment"/> representing part of a TSQL object definition</param>
            <returns>true if this is a subroutine, a view or a trigger body</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Internal.TsqlScriptDomUtils.RemoveParenthesis(Microsoft.SqlServer.TransactSql.ScriptDom.ScalarExpression)">
            <summary>
            recursively remove parenthesises of an TSql parser expression
            </summary>
            <param name="expression">Expression to parse</param>
            <returns>Expression without parenthesis</returns>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.CodeAnalysis.SqlCodeAnalysisConstants.StaticCodeAnalysisProblemCategory">
            <summary>
            Error category for static code analysis
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.MefRuleEngineResources">
            <summary>
              A strongly-typed resource class, for looking up localized strings, etc.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.MefRuleEngineResources.ResourceManager">
            <summary>
              Returns the cached ResourceManager instance used by this class.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.MefRuleEngineResources.Culture">
            <summary>
              Overrides the current thread's CurrentUICulture property for all
              resource lookups using this strongly typed resource class.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.MefRuleEngineResources.ElementsListFormat">
            <summary>
              Looks up a localized string similar to {0}, {1}.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.MefRuleEngineResources.InvalidRuleStatusPrefix">
            <summary>
              Looks up a localized string similar to Invalid prefix for rule key: {0}. Ignored..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.MefRuleEngineResources.MissingSupportedElements">
            <summary>
              Looks up a localized string similar to Rule &apos;{0}&apos; in assembly &apos;{1}&apos; was ignored because it is missing SupportedElementTypes. This is required for Element scope rules.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.MefRuleEngineResources.NoSupportedElementsFound">
            <summary>
              Looks up a localized string similar to Rule &apos;{0}&apos; in assembly &apos;{1}&apos; was ignored because it has no SupportedElementTypes that are top-level types. Only top-level types are processed during analysis. The following types were ignored: [{2}].
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.MefRuleEngineResources.UnsupportedElementsFound">
            <summary>
              Looks up a localized string similar to Rule &apos;{0}&apos; in assembly &apos;{1}&apos; has one or more SupportedElementTypes that are not top-level types. These following types will be ignored during analysis: [{2}].
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.RuleEngine">
            <summary>
            Responsible for executing rules against a SQLModel.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.RuleEngine.ExecuteRules(Microsoft.SqlServer.Dac.Model.TSqlModel,System.Collections.Generic.IList{Microsoft.SqlServer.Dac.Extensibility.ExtensibilityError}@)">
            <summary>
            Execute all enabled rules against the specified model and each model element found in the model
            </summary>
            <param name="schemaModel">The model against which the rules are executed</param>
            <param name="errors">The errors that occurred when executing rules</param>
            <returns>The problems detected by executing the rules</returns>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.RuleEngine.AllRuleDescriptions">
            <summary>
            Used for testing purposes
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.RuleEngine.ModelRuleDescriptions">
            <summary>
            Used for testing purposes
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.RuleEngine.ElementRuleDescriptions">
            <summary>
            Used for testing purposes
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.RuleLoader`1.CreateStandardProperties">
            <summary>
            Creates a <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.EngineProperties"/> that uses standard extension lookup 
            properties and returns this to the caller
            </summary>
            <returns><see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.EngineProperties"/></returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.RuleLoader`1.SetRuleStoreFactory(Microsoft.SqlServer.Dac.CodeAnalysis.Engine.IRuleStoreFactory{`0})">
            <summary>
            For testing purposes only - injects a new rulestore factory for use when finding rules
            </summary>
            <param name="factory"></param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.RuleLoader`1.#ctor(Microsoft.SqlServer.Dac.CodeAnalysis.Engine.EngineProperties)">
            <summary>
            Initializes the <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.RuleLoader`1"/> 
            </summary>
            <param name="engineProperties"><see cref="T:Microsoft.SqlServer.Dac.Extensibility.CompositionProperties"/> defining how rules should be loaded</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.RuleLoader`1.GetAllRules(System.Collections.Generic.IList{Microsoft.SqlServer.Dac.Extensibility.ExtensibilityError}@)">
            <summary>
            Gets all rules.
            </summary>
            <param name="typeLoadErrors">Output <see cref="T:System.Collections.Generic.List`1"/> containing all errors that occurred during load. This will include general load errors
            for all contributors on the machine, plus load errors relating to version mismatch between requested contributor version
            and the available contributors on the machine</param>
            <returns></returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.RuleLoader`1.CreateRuleEngine(System.Collections.Generic.IList{Microsoft.SqlServer.Dac.Extensibility.ExtensibilityError}@)">
            <summary>
            Creates a RuleEngine.
            </summary>
            <param name="errors">Errors found while processing rules - are there duplicate rules or rules that do not include required metadata</param>
            <returns></returns>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.IRuleStore">
            <summary>
            A RuleStore that stores analysis rules and their metadata. Rules are accessible in their original form
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.IRuleStore.GetRuleDescriptors(System.Collections.Generic.IList{Microsoft.SqlServer.Dac.Extensibility.ExtensibilityError}@)">
            <summary>
            Gets the Code Analysis rules and their metadata wrapped inside RuleDescriptorImpl objects. The EngineProperties are used to
            filter rules by platform and to run a rule descriptor updater (if present), which can flag the rule as enabled/disabled
            and update the severity 
            </summary>
            <param name="errors">Errors found while processing rules - are there duplicate rules or rules that do not include required metadata</param>
            <returns></returns>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.RuleStore">
            <summary>
            For now only ISqlCodeAnalysisRule is supported via MEF - can change in the future if other rule types are required.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.RuleStore.GetRuleDescriptors(System.Collections.Generic.IList{Microsoft.SqlServer.Dac.Extensibility.ExtensibilityError}@)">
            <summary>
            Gets the Code Analysis rules and their metadata wrapped inside RuleDescriptorImpl objects. The EngineProperties are used to
            filter rules by platform and to run a rule descriptor updater (if present), which can flag the rule as enabled/disabled
            and update the severity 
            </summary>
            <param name="errors">Errors found while processing rules - are there duplicate rules or rules that do not include required metadata</param>
            <returns></returns>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.IRuleStoreFactory`1">
            <summary>
            Factory for loading rules via MEF extensibility. 
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.IRuleStoreFactory`1.CreateRuleStore(Microsoft.SqlServer.Dac.CodeAnalysis.Engine.EngineProperties,System.Collections.Generic.IList{Microsoft.SqlServer.Dac.Extensibility.ExtensibilityError}@)">
            <summary>
            Creates an IRuleStore with rules found by MEF
            </summary>
            <param name="properties">Properties defining the configuration for extensibility loading etc</param>
            <param name="errors">Errors found while loading rule extensions </param>
            <returns></returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.RuleStoreFactory`1.SetExtensionManagerFactory(Microsoft.SqlServer.Dac.Extensibility.IExtensionManagerFactory)">
            <summary>
            For testing purposes only - injects a new IExtensionManagerFactory which supplies types
            </summary>
            <param name="factory"></param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.SourceCodePositionProvider.TryGetSourceInformation(Microsoft.SqlServer.Dac.Model.TSqlObject,Microsoft.SqlServer.Dac.SourceInformation@)">
            <summary>
            Tries to find source information for a given TSqlObject
            </summary>
            <returns>true if source information was found</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.SourceCodePositionProvider.TryGetSourceInformation(Microsoft.SqlServer.Dac.Model.TSqlObject,Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment,Microsoft.SqlServer.Dac.SourceInformation@)">
            <summary>
            Tries to find source information for a given TSqlObject
            </summary>
            <returns>true if source information was found</returns>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblemSuppressionContext">
            <summary>
            The context information for suppressing a SCA problem
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblemSuppressionContext.#ctor(Microsoft.SqlServer.Dac.CodeAnalysis.RuleDescriptor,Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem,Microsoft.SqlServer.Dac.Model.TSqlModel)">
            <summary>
            Initialize a new instance of SqlRuleProblemSuppressionContext
            </summary>
            <param name="rule">The Rule detecting the problem</param>
            <param name="problem">The problem</param>
            <param name="schemaModel">The Schema Model being analyzed</param>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblemSuppressionContext.ModelElement">
            <summary>
            The element for which a problem was discovered
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblemSuppressionContext.SchemaModel">
            <summary>
            The Schema Model the rule is checking against
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblemSuppressionContext.Problem">
            <summary>
            The <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem"/> discovered by an analysis rule
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblemSuppressionContext.Rule">
            <summary>
            The Rule detecting the problem
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.EngineProperties">
            <summary>
            Properties to use when creating a rules engine. 
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.EngineProperties.RuleSettingsString">
            <summary>
            Optional string that can be used to disable rules
            and set rule severity
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.EngineProperties.SuppressProblem">
            <summary>
            The predicate to suppress problems detected by a rule against an element
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.Engine.EngineProperties.ExecuteCanceled">
            <summary>
            Callback function that tests whether to abort executing rules
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.ScaResources">
            <summary>
              A strongly-typed resource class, for looking up localized strings, etc.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.ScaResources.ResourceManager">
            <summary>
              Returns the cached ResourceManager instance used by this class.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.ScaResources.Culture">
            <summary>
              Overrides the current thread's CurrentUICulture property for all
              resource lookups using this strongly typed resource class.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.ScaResources.CodeAnalysis_ModelIsNotScriptBacked">
            <summary>
              Looks up a localized string similar to Static Code Analysis: Some rules may not run as expected because the model being analyzed does not contain scripts representing your schema objects. When analyzing code from a data source such as a Dacpac it is recommended that the source should be loaded as script-backed model to ensure correct rule operation.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.ScaResources.CodeAnalysisTask_Error">
            <summary>
              Looks up a localized string similar to Error.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.ScaResources.CodeAnalysisTask_InvalidElement">
            <summary>
              Looks up a localized string similar to {0} elements are in an error state.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.ScaResources.CodeAnalysisTask_MultipleProblemsFound">
            <summary>
              Looks up a localized string similar to Static Code Analysis: {0} problems have been detected..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.ScaResources.CodeAnalysisTask_NoProblemsFound">
            <summary>
              Looks up a localized string similar to Static Code Analysis: No problems have been detected..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.ScaResources.CodeAnalysisTask_OneProblemFound">
            <summary>
              Looks up a localized string similar to Static Code Analysis: One problem has been detected..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.ScaResources.CodeAnalysisTask_ProblemFormat">
            <summary>
              Looks up a localized string similar to {0}: {1}.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.ScaResources.CodeAnalysisTask_ResultsSavedIn">
            <summary>
              Looks up a localized string similar to The results are saved in {0}..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.ScaResources.SqlCodeAnalysisTask_Unknown">
            <summary>
              Looks up a localized string similar to Unknown.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.ScaResources.SqlCodeAnalysisTask_Warning">
            <summary>
              Looks up a localized string similar to Warning.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleExecutionContext">
            <summary>
            Defines the fields necessary for analysis, including the schema model and 
            model element to analyze.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleExecutionContext.SchemaModel">
            <summary>
            The <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/> being analyzed.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleExecutionContext.ModelElement">
            <summary>
            The <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> being analyzed.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleExecutionContext.RuleDescriptor">
            <summary>
            Describes the rule being executed, and can be used to access the 
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleExecutionContext.ScriptFragment">
            <summary>
            Gets the script fragment which defines the element being analyzed, if this is available. 
            May be null. 
            
            Tries to get the most suitable <see cref="T:Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment"/> for use during the rule analysis process.
            If the TSqlObject was originally built from a scripted source then the original source fragment will be returned.
            Otherwise a new AST will be generated from the <see cref="P:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleExecutionContext.ModelElement"/>/&gt;.
            This ensures that when reporting <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem"/>s the most accurate source information can be included
            in the error messages. 
            </summary>
            <remarks>
            See <see cref="M:Microsoft.SqlServer.Dac.TSqlModelUtils.TryGetFragmentForAnalysis(Microsoft.SqlServer.Dac.Model.TSqlObject,Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment@)"/> for implementation details
            </remarks>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem">
            <summary>
            Describes a problem found by a rule during analysis. Contains relevant information
            such as the Rule that found the problem, the SqlObject causing the problem, 
            the severity, and the error message to display.
            
            Source position information (source name, start line and column) are initially inferred based on
            the <see cref="P:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem.ModelElement"/> and <see cref="P:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem.Fragment"/> passed into the constructor. Note that if
            a <see cref="P:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem.Fragment"/> is passed in then the start line/column for that <see cref="T:Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment"/>
            will be used (if present), otherwise the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>'s values will be used (if present).
            Note that certain models such as those generated from a dacpac may not have source position
            information.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem.#ctor(System.String,Microsoft.SqlServer.Dac.Model.TSqlObject)">
            <summary>
            Constructs a new <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem"/>. Source position information
            (source name, start line and column) is inferred based on the <paramref name="modelElement"/>
            parameter, as long as this information is present in the model.
            </summary>
            <param name="description">Description of the problem</param>
            <param name="modelElement">The element that caused the problem  </param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem.#ctor(System.String,Microsoft.SqlServer.Dac.Model.TSqlObject,Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment)">
            <summary>
            Constructs a new <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem"/>. Source position information
            (source name, start line and column) is inferred based on the <paramref name="modelElement"/>
            and <paramref name="fragment"/> parameters, as long as this information is present in the model.
            </summary>
            <param name="description">Description of the problem</param>
            <param name="modelElement">The element that caused the problem</param>
            <param name="fragment"><see cref="T:Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment"/> specifying the precise </param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem.SetSourceInformation(Microsoft.SqlServer.Dac.SourceInformation)">
            <summary>
            Sets source position information (source name, start line and column) for this <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem"/>,
            overriding any position information inferred when the rule was created. 
            </summary>
            <param name="sourceInformation"><see cref="T:Microsoft.SqlServer.Dac.SourceInformation"/> specifying the values to set for the
            <see cref="P:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem.SourceName"/>, <see cref="P:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem.StartLine"/> and <see cref="P:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem.StartColumn"/> fields</param>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem.Rule">
            <summary>
            Description of the rule used to detect the current problem. Does not need to be set by the rule itself
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem.RuleId">
            <summary>
            Id of the rule that created this <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem"/>
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem.ModelElement">
            <summary>
            The <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> causing the current problem
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem.Fragment">
            <summary>
            <see cref="T:Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment"/> causing the problem
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem.Description">
            <summary>
            Problem description
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem.Severity">
            <summary>
            The severity of this problem, can be error, warning.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem.ErrorMessageString">
            <summary>
            The string for displaying the error message, based on rule information
            and the problem description
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem.SourceName">
            <summary>
            Name of the source this problem was found in. This is determined based on the 
            model element passed into the constructor. May be null if no source information
            was available. For instance models loaded from Dacpac files may not have source
            information available
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem.StartLine">
            <summary>
            The line the problem begins at, if known
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem.StartColumn">
            <summary>
            The column the problem begins at, if known
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblemSeverity">
            <summary>
            The type of message for reporting problems
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblemSeverity.Unknown">
            <summary>
            Unknown severity
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblemSeverity.Warning">
            <summary>
            Warning
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblemSeverity.Error">
            <summary>
            Error
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleScope">
            <summary>
            The scope examined by a static code analysis rule.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleScope.Element">
            <summary>
            The rule examines a single element.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleScope.Model">
            <summary>
            The rule examines the entire model.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.CodeAnalysis.SuppressedProblemInfo">
            <summary>
            Information about a problem being suppressed for a particular source. 
            This information includes the source name and the rule whose problems should be suppressed.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.SuppressedProblemInfo.#ctor(System.String,Microsoft.SqlServer.Dac.CodeAnalysis.RuleConfiguration)">
            <summary>
            Creates a <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.SuppressedProblemInfo"/> for a given source name and rule
            </summary>
            <param name="sourceName">
            Name of the source to be suppressed. This is commonly the path to a file on disk.
            </param>
            <param name="rule">Information about the rule whose problems should be suppressed</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.SuppressedProblemInfo.Equals(System.Object)">
            <summary>
            <see cref="M:System.Object.Equals(System.Object)"/>
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.CodeAnalysis.SuppressedProblemInfo.GetHashCode">
            <summary>
            <see cref="M:System.Object.GetHashCode"/>
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.SuppressedProblemInfo.SourceName">
            <summary>
            Gets the name of the source to be suppressed. This is commonly the path to a file on disk.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.CodeAnalysis.SuppressedProblemInfo.Rule">
            <summary>
            Gets a <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.RuleConfiguration"/> with information about the rule 
            whose problems should be suppressed
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Compare.DacSchemaComparisonMessage">
            <summary>
            Represents a problem encountered during schema comparison.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.DacSchemaComparisonMessage.Exception">
            <summary>
            Exception associated with the error, or null
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Compare.SchemaCompareApiResources">
            <summary>
              A strongly-typed resource class, for looking up localized strings, etc.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaCompareApiResources.ResourceManager">
            <summary>
              Returns the cached ResourceManager instance used by this class.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaCompareApiResources.Culture">
            <summary>
              Overrides the current thread's CurrentUICulture property for all
              resource lookups using this strongly typed resource class.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaCompareApiResources.ComparisonResultStateDoesNotAllowPublish">
            <summary>
              Looks up a localized string similar to Performing publish is not possible for this comparison result..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaCompareApiResources.ComparisonResultStateDoesNotAllowScriptGeneration">
            <summary>
              Looks up a localized string similar to Performing script generation is not possible for this comparison result..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaCompareApiResources.InvalidConnectionString">
            <summary>
              Looks up a localized string similar to The specified connection string is not valid..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaCompareApiResources.OperationFailedDueToAnUnexpectedException">
            <summary>
              Looks up a localized string similar to The operation failed..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaCompareApiResources.OperationFailedDueToException0">
            <summary>
              Looks up a localized string similar to The operation failed. The error was: {0}.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaCompareApiResources.OperationFailedDueToSourceDrift">
            <summary>
              Looks up a localized string similar to The operation could not continue because the source database was modified after schema comparison was completed..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaCompareApiResources.OperationFailedDueToSourceModelErrors">
            <summary>
              Looks up a localized string similar to The operation could not continue because the source model contains errors..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaCompareApiResources.OperationFailedDueToTargetDrift">
            <summary>
              Looks up a localized string similar to The operation could not continue because the target database was modified after schema comparison was completed..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaCompareApiResources.OperationFailedDueToTargetModelErrors">
            <summary>
              Looks up a localized string similar to The operation could not continue because the target model contains errors..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaCompareApiResources.Path0DoesntContainValidScmpFile">
            <summary>
              Looks up a localized string similar to The path &apos;{0}&apos; does not contain a valid schema compare file (.scmp file)..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaCompareApiResources.SchemaComparisonOnlySupportsDacpacAndDatabase">
            <summary>
              Looks up a localized string similar to SchemaComparison only supports database and dacpac endpoint types..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaCompareApiResources.UnableToDeserializeScmpFile">
            <summary>
              Looks up a localized string similar to The specified file could not be opened as a schema comparison file.  The file might be corrupt or it might refer to a target or source endpoint that is not of type database or dacpac. Only database endpoints and dacpac endpoints are supported..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaCompareApiResources.UpdateScriptsForDacpacsRequireDbName">
            <summary>
              Looks up a localized string similar to A non-empty database name must be specified in order to generate an update script for a SchemaCompareDacpacEndpoint..
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Compare.SchemaCompareEndpoint">
            <summary>
            Refers to a source or target for schema comparison
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Compare.SchemaCompareDacpacEndpoint">
            <summary>
            Refers to a dacpac file as either a source or target for schema comparison
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Compare.SchemaCompareDacpacEndpoint.#ctor(System.String)">
            <summary>
            Constructs a schema compare endpoint that refers to a dacpac file
            </summary>
            <param name="dacpacFilePath">Path to a dacpac file</param>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaCompareDacpacEndpoint.FilePath">
            <summary>
            The dacpac file path
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Compare.SchemaCompareDatabaseEndpoint">
            <summary>
            Refers to a database as either a source or target for schema comparison
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Compare.SchemaCompareDatabaseEndpoint.#ctor(System.String)">
            <summary>
            Constructs a schema compare endpoint that refers to a database
            </summary>
            <param name="connectionString">A connection string to a database instance</param>
            <exception cref="T:System.ArgumentNullException">The supplied connection string is null.</exception>
            <exception cref="T:System.FormatException">Invalid value within the connection string (for example, when a Boolean or numeric value was expected but not supplied).</exception>
            <exception cref="T:System.ArgumentException">The supplied connectionString is not valid.</exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Compare.SchemaCompareDatabaseEndpoint.#ctor(System.String,System.Security.SecureString)">
            <summary>
            Constructs a schema compare endpoint that refers to a database
            </summary>
            <param name="connectionString">A connection string to a database instance</param>
            <param name="password"><see cref="T:System.Security.SecureString"/> that supplies the password for the database connection used by this instance.</param>
            <exception cref="T:System.ArgumentNullException">The supplied connection string or password is null.</exception>
            <exception cref="T:System.FormatException">Invalid value within the connection string (for example, when a Boolean or numeric value was expected but not supplied).</exception>
            <exception cref="T:System.ArgumentException">The supplied connectionString is not valid.</exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Compare.SchemaCompareDatabaseEndpoint.#ctor(Microsoft.Data.Tools.Schema.Common.SqlClient.SqlConnectionFactory)">
            <summary>
            Constructs a schema compare endpoint that refers to a database
            </summary>
            <param name="connectionFactory">A connection string to a database instance</param>
            <exception cref="T:System.ArgumentNullException">The supplied connection factory is null.</exception>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaCompareDatabaseEndpoint.DatabaseName">
            <summary>
            The database name
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Compare.SchemaComparison">
            <summary>
            Class that allows comparing schema between two types of entities: databases and dacpac files
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Compare.SchemaComparison.#ctor(System.String)">
            <summary>
            SchemaComparison constructor that uses a schema compare file (.scmp file) for
            all comparison settings.  Only dacpac and/or database endpoints may be compared.
            </summary>
            <param name="scmpFilePath">The path to a schema compare file (.scmp file)</param>
            <exception cref="T:System.ArgumentException">If either the source or the target endpoint is neither a database nor a dacpac file.</exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Compare.SchemaComparison.#ctor(Microsoft.SqlServer.Dac.Compare.SchemaCompareEndpoint,Microsoft.SqlServer.Dac.Compare.SchemaCompareEndpoint)">
            <summary>
            SchemaComparison constructor that takes endpoints that specify the source and target for comparison.
            </summary>
            <param name="source">A <see cref="T:Microsoft.SqlServer.Dac.Compare.SchemaCompareEndpoint"/> that refers to a schema source.</param>
            <param name="target">An <see cref="T:Microsoft.SqlServer.Dac.Compare.SchemaCompareEndpoint"/> that refers to a target.  Comparison will update the target to match the source.</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Compare.SchemaComparison.Compare">
            <summary>
            Performs schema comparison, populating comparison results.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Compare.SchemaComparison.SaveToFile(System.String,System.Boolean)">
            <summary>
            Saves the SchemaComparison as an scmp file.
            </summary>
            <param name="filePath"></param>
            <param name="overwrite">When true, any existing file at filePath will be overwritten. If false, save will throw an
            IOException if a file already exists at filePath.</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Compare.SchemaComparison.SaveToStream(System.IO.Stream)">
            <summary>
            Saves the SchemaComparison as an scmp file into a stream
            </summary>
            <param name="stream">A stream that supports writing.</param>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaComparison.Source">
            <summary>
            The <see cref="T:Microsoft.SqlServer.Dac.Compare.SchemaCompareEndpoint"/> that refers to a schema source.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaComparison.Target">
            <summary>
            The <see cref="T:Microsoft.SqlServer.Dac.Compare.SchemaCompareEndpoint"/> that refers to a target.  Comparison will update the target to match the source.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaComparison.ExcludedSourceObjects">
            <summary>
            Elements in the source database model to exclude from comparison.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaComparison.ExcludedTargetObjects">
            <summary>
            Elements in the target database model to exclude from comparison.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaComparison.Options">
            <summary>
            Options that affect the behavior of package deployment.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Compare.SchemaDifferenceType">
            <summary>
            Represents the different possible types of <see cref="T:Microsoft.SqlServer.Dac.Compare.SchemaDifference"/>
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Compare.SchemaDifferenceType.Object">
            <summary>
            A model object (for example: column, parameter)
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Compare.SchemaDifferenceType.Property">
            <summary>
            A property of a model object (for example: Name, Collation)
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Compare.SchemaComparisonResult">
            <summary>
            Class that provides information about the differences between a source and target database.
            Differences are represented in a tree structure.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Compare.SchemaComparisonResult.Include(Microsoft.SqlServer.Dac.Compare.SchemaDifference)">
            <summary>
            Used to try to include a difference as part of the set of update actions that are scripted or published.
            </summary>
            <param name="node">The tree node to (possibly) modify</param>
            <returns>Returns true if, after completion, the node's state is included</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Compare.SchemaComparisonResult.Exclude(Microsoft.SqlServer.Dac.Compare.SchemaDifference)">
            <summary>
            Used to try to exclude a difference as part of the set of update actions that are scripted or published.
            </summary>
            <param name="node">The tree node to (possibly) modify</param>
            <returns>Returns true if, after completion, the node's state is not included</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Compare.SchemaComparisonResult.GetErrors">
            <summary>
            Provides an enumeration of errors that prevent successful schema compare operations.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Compare.SchemaComparisonResult.GenerateScript(System.String)">
            <summary>
            Generates a T-SQL update script that makes the target schema consistent with the source schema.
            </summary>
            <param name="databaseName">The name of the target database.</param>
            <returns>SchemaCompareScriptGenerationResult </returns>
            <exception cref="T:System.ArgumentException">If the target is a dacpac and a null or empty databaseName value was provided.</exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Compare.SchemaComparisonResult.PublishChangesToTarget">
            <summary>
            Generates and then executes a T-SQL update script that makes the target schema consistent with the source schema.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaComparisonResult.IsValid">
            <summary>
            Indicates whether comparison resulted in a valid, usable result
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaComparisonResult.IsEqual">
            <summary>
            Indicates whether the source and target endpoints are equal
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaComparisonResult.SourceModel">
            <summary>
            The source database model
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaComparisonResult.TargetModel">
            <summary>
            The target database model
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaComparisonResult.Differences">
            <summary>
            A tree that contains results of schema comparison of source and target endpoints.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Compare.SchemaCompareScriptGenerationResult">
            <summary>
            Result of generating scripts
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaCompareScriptGenerationResult.Script">
            <summary>
            The generated script
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaCompareScriptGenerationResult.MasterScript">
            <summary>
            When needed (e.g. for Azure SQL Database), a separate script to be executed against the master database on the target server
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaCompareScriptGenerationResult.Success">
            <summary>
            Indicates whether generating the script was successful.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaCompareScriptGenerationResult.Message">
            <summary>
            Provides context information in the event of an error.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaCompareScriptGenerationResult.Exception">
            <summary>
            Provides error information in the event of an exception.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Compare.SchemaComparePublishResult">
            <summary>
            Result of publishing comparison result
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaComparePublishResult.Script">
            <summary>
            The generated script
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaComparePublishResult.MasterScript">
            <summary>
            When needed (e.g. for Azure SQL Database), a separate script to be executed against the master database on the target server
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaComparePublishResult.Success">
            <summary>
            Indicates whether generating the script was successful.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaComparePublishResult.Errors">
            <summary>
            Any errors encountered while publishing
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Compare.SchemaDifference">
            <summary>
            Represents an object or property difference found during comparison of two database models.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaDifference.DifferenceType">
            <summary>
            Represents the type of this <see cref="T:Microsoft.SqlServer.Dac.Compare.SchemaDifference"/> node
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaDifference.UpdateAction">
            <summary>
            The update action required to make the schema of the target equal to the source schema.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaDifference.Included">
            <summary>
            Indicates whether this node and all of its children will be included during script generation or publish of the comparison result.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaDifference.IsExcludable">
            <summary>
            Indicates whether this node can be excluded from script generation and publish of the comparison result.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaDifference.Parent">
            <summary>
            The parent difference (of type <see cref="F:Microsoft.SqlServer.Dac.Compare.SchemaDifferenceType.Object"/>), if any, of this difference
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaDifference.Children">
            <summary>
            Object or property differences, if any, that relate to this difference.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaDifference.SourceObject">
            <summary>
            Object from the source database model
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaDifference.TargetObject">
            <summary>
            Object from the target database model
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaDifference.Name">
            <summary>
            Node name
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Compare.SchemaComparisonExcludedObjectId">
            <summary>
            Represents an object by name and type for use in <see cref="T:Microsoft.SqlServer.Dac.Compare.SchemaComparison"/> operations.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Compare.SchemaComparisonExcludedObjectId.#ctor(Microsoft.SqlServer.Dac.Model.ModelTypeClass,Microsoft.SqlServer.Dac.Model.ObjectIdentifier)">
            <summary>
            Constructs an identifier that refers to an object by type and name.
            </summary>
            <param name="typeClass">The type of an object</param>
            <param name="identifier">The ObjectIdentifier (name) of an object</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Compare.SchemaComparisonExcludedObjectId.#ctor(Microsoft.SqlServer.Dac.Model.ModelTypeClass,Microsoft.SqlServer.Dac.Model.ObjectIdentifier,Microsoft.SqlServer.Dac.Model.ModelTypeClass,Microsoft.SqlServer.Dac.Model.ObjectIdentifier)">
            <summary>
            Constructs an identifier that refers to an object by type and name.
            </summary>
            <param name="typeClass">The type of an object</param>
            <param name="identifier">The ObjectIdentifier (name) of an object</param>
            <param name="parentTypeClass">The type of the parent object. This is used to refer to an unnamed object by reference to its (named) parent.</param>
            <param name="parentIdentifier">The name of the parent object. This is used to refer to an unnamed object by reference to its (named) parent.</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Compare.SchemaComparisonExcludedObjectId.#ctor(System.String,Microsoft.SqlServer.Dac.Model.ObjectIdentifier)">
            <summary>
            Constructs an identifier that refers to an object by type and name.
            </summary>
            <param name="typeName">The type name used by schema comparison</param>
            <param name="identifier">The possibly null ObjectIdentifier (name) of an object</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Compare.SchemaComparisonExcludedObjectId.#ctor(System.String,Microsoft.SqlServer.Dac.Model.ObjectIdentifier,System.String,Microsoft.SqlServer.Dac.Model.ObjectIdentifier)">
            <summary>
            Constructs an identifier that refers to an object by type and name.
            </summary>
            <param name="typeName">The type name used by schema comparison</param>
            <param name="identifier">The possibly null ObjectIdentifier (name) of an object</param>
            <param name="parentTypeName">The type name of the parent object. This is used to refer to an unnamed object by reference to its (named) parent.</param>
            <param name="parentIdentifier">The name of the parent object. This is used to refer to an unnamed object by reference to its (named) parent.</param>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaComparisonExcludedObjectId.TypeName">
            <summary>
            The type name used by schema comparison
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaComparisonExcludedObjectId.Identifier">
            <summary>
            Specifies the name of an object
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaComparisonExcludedObjectId.ParentTypeName">
            <summary>
            The type name of the parent object. This is used to refer to an unnamed object by reference to its (named) parent.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Compare.SchemaComparisonExcludedObjectId.ParentIdentifier">
            <summary>
            Specifies the name of the parent object. This is used to refer to an unnamed object by reference to its (named) parent.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Compare.SchemaUpdateAction">
            <summary>
            Represents a schema update action
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Compare.SchemaUpdateAction.Delete">
            <summary>
            Delete - removing an object from the database
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Compare.SchemaUpdateAction.Change">
            <summary>
            Change - modifying an object in the database
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Compare.SchemaUpdateAction.Add">
            <summary>
            Add - adding an object to the database
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.DacPackageExtensions">
            <summary>
            Provides extension methods to support manipulation of DacPackage objects.
            These extension methods work on an existing <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/>
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacPackageExtensions.UpdateModel(Microsoft.SqlServer.Dac.DacPackage,Microsoft.SqlServer.Dac.Model.TSqlModel,Microsoft.SqlServer.Dac.PackageMetadata)">
            <summary>
            Updates the model in a <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/>, replacing the current model with a new one.
            
            Before updating, the model will be validated and if errors are encountered a <see cref="T:Microsoft.SqlServer.Dac.DacServicesException"/> 
            will be thrown. If callers wish to block on warnings as well as errors, they must validate the model by calling
            <see cref="M:Microsoft.SqlServer.Dac.Model.TSqlModel.Validate"/> and checking if any errors or warnings are included in the messages returned.
            
            Note: only the model is replaced - all other artifacts such as refactor log, pre-deployment script,
            post-deployment script and and contributor artifacts are not altered. If any of these artifacts
            rely on elements that are no longer in the updated model then deployment may fail. It is the 
            responsibility of the caller to ensure that these artifacts are consistent with the new model.
            The <see cref="T:System.IO.Packaging.Package"/> API can be used to update other artifacts such as the refactor log and scripts
            and keep them consistent with the updated model.
            These artifacts are stored as package parts and are identified by their URI. 
            Package parts can be examined using <see cref="M:System.IO.Packaging.Package.GetParts"/>. Please refer to the 
            <see cref="T:System.IO.Packaging.Package"/> API for further information about updating package parts.
            
            </summary>
            <param name="dacPackage"><see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> to be updated</param>
            <param name="newModel"><see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/> representing the model to save into the package.</param>
            <param name="packageMetadata"><see cref="T:Microsoft.SqlServer.Dac.PackageMetadata"/> describing the name, version and description to use
            for the <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/>, or null if the package information should remain the same. </param>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">If the <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> contains data.</exception>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">If there are any errors saving the model to the package,
            including errors validating the model.
            </exception>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">If the <paramref name="dacPackage"/> was not loaded with <see cref="F:System.IO.FileAccess.ReadWrite"/>.</exception>
            <exception cref="T:System.ArgumentNullException">If the <paramref name="dacPackage"/> or <paramref name="newModel"/> parameters are null.</exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacPackageExtensions.BuildPackage(System.String,Microsoft.SqlServer.Dac.Model.TSqlModel,Microsoft.SqlServer.Dac.PackageMetadata)">
            <summary>
            Creates a package with the specified <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/> and saves it to the specified location.
            </summary>
            <param name="packageFilePath">Path to the package file.</param>
            <param name="model"><see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/> representing the model to save into the package.</param>
            <param name="packageMetadata">Metadata information that describes the package.</param>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">If there are any errors during package serialization, including errors validating the model.</exception>
            <exception cref="T:System.ArgumentNullException">If the <paramref name="packageFilePath"/> or <paramref name="model"/> parameters are null.</exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacPackageExtensions.BuildPackage(System.String,Microsoft.SqlServer.Dac.Model.TSqlModel,Microsoft.SqlServer.Dac.PackageMetadata,Microsoft.SqlServer.Dac.PackageOptions)">
            <summary>
            Creates a package with the specified <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/> and saves it to the specified location.
            In addition to the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/>, other artifacts such as refactor log and deployment contributors can be specified in <paramref name="packageOptions"/>.
            </summary>
            <param name="packageFilePath">Path to the package file.</param>
            <param name="model"><see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/> representing the model to save into the package.</param>
            <param name="packageMetadata">Metadata information that describes the package.</param>
            <param name="packageOptions"><see cref="T:Microsoft.SqlServer.Dac.PackageOptions"/> defining advanced options and additional artifacts.</param>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">If there are any errors during package serialization, including errors validating the model.</exception>
            <exception cref="T:System.ArgumentNullException">If the <paramref name="packageFilePath"/> or <paramref name="model"/> parameters are null.</exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacPackageExtensions.BuildPackage(System.IO.Stream,Microsoft.SqlServer.Dac.Model.TSqlModel,Microsoft.SqlServer.Dac.PackageMetadata)">
            <summary>
            Creates a package with the specified <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/> and saves it to the specified <see cref="T:System.IO.Stream"/>.
            </summary>
            <param name="stream"><see cref="T:System.IO.Stream"/> to which the package content will be serialized.</param>
            <param name="model"><see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/> representing the model to save into the package.</param>
            <param name="packageMetadata">Metadata information that describes the package.</param>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">If there are any errors during package serialization, including errors validating the model.</exception>
            <exception cref="T:System.ArgumentNullException">If the <paramref name="stream"/> or <paramref name="model"/> parameters are null.</exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacPackageExtensions.BuildPackage(System.IO.Stream,Microsoft.SqlServer.Dac.Model.TSqlModel,Microsoft.SqlServer.Dac.PackageMetadata,Microsoft.SqlServer.Dac.PackageOptions)">
            <summary>
            Creates a package with the specified <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/> and saves it to the specified <see cref="T:System.IO.Stream"/>.
            In addition to the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/>, other artifacts such as refactor log and deployment contributors can be specified in <paramref name="packageOptions"/>.
            </summary>
            <param name="stream"><see cref="T:System.IO.Stream"/> to which the package content will be serialized.</param>
            <param name="model"><see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/> representing the model to save into the package.</param>
            <param name="packageMetadata">Metadata information that describes the package.</param>
            <param name="packageOptions"><see cref="T:Microsoft.SqlServer.Dac.PackageOptions"/> defining advanced options and additional artifacts.</param>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">If there are any errors during package serialization, including errors validating the model.</exception>
            <exception cref="T:System.ArgumentNullException">If the <paramref name="stream"/> or <paramref name="model"/> parameters are null.</exception>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.BuildContributor">
            <summary>
            Derivatives of the BuildContributor extension are executed during a 
            project build after the project's model has been constructed and verified.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.BuildContributor.OnExecute(Microsoft.SqlServer.Dac.Deployment.BuildContributorContext,System.Collections.Generic.IList{Microsoft.SqlServer.Dac.Extensibility.ExtensibilityError})">
            <summary>
            Called after the project's model has been completely built giving the build
            contributor an opportunity to examine the model and possible output
            additional files.
            </summary>
            <param name="context"><see cref="T:Microsoft.SqlServer.Dac.Deployment.BuildContributorContext"/> object</param>
            <param name="messages">Any messages to be published as part of the build process can be added to this List. 
            May relate to errors or can also be informational</param>
            <exception cref="T:Microsoft.SqlServer.Dac.Deployment.BuildFailedException">This type of exception should be thrown if the build cannot continue.</exception>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.BuildContributorContext">
            <summary>
            Provides the context for the build process that is used by <see cref="T:Microsoft.SqlServer.Dac.Deployment.BuildContributor"/> objects
            during project build. See documentation for the <see cref="P:Microsoft.SqlServer.Dac.Deployment.BuildContributorContext.Arguments"/> and <see cref="P:Microsoft.SqlServer.Dac.Deployment.BuildContributorContext.ExtensionFiles"/>
            properties for information on how to specify these inside a project file.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.BuildContributorContext.#ctor(System.Collections.Generic.Dictionary{System.String,System.Object},System.Collections.Generic.Dictionary{System.String,System.String},Microsoft.SqlServer.Dac.Model.TSqlModel,Microsoft.Build.Framework.ITaskItem[])">
            <summary>
            Initializes a new instance of the <see cref="T:Microsoft.SqlServer.Dac.Deployment.BuildContributorContext"/> class.
            </summary>
            <param name="buildProperties">A <see cref="T:System.Collections.Generic.Dictionary`2"/> of named property values. Canno be null</param>
            <param name="arguments">A <see cref="T:System.Collections.Generic.Dictionary`2"/> of command-line arguments and values. Cannot be null.</param>
            <param name="buildModel">A reference to the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/> of the project. Cannot be null.</param>
            <param name="extensionFiles">An array of <see cref="T:Microsoft.Build.Framework.ITaskItem"/> objects.</param>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.BuildContributorContext.BuildProperties">
            <summary>
            Gets a dictionary of named properties and their values
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.BuildContributorContext.Arguments">
            <summary>
            Gets a dictionary of command-line arguments and their values.
            When building in SSDT, arguments are available when found in a .sqlproj file or a referenced .targets file. 
            For instance to add a "RunMyContributor" argument with a value of 
            "true", the following would be added:
            
            &lt;PropertyGroup&gt;
                &lt;ContributorArguments Condition="'$(Configuration)' == 'Debug''"&gt;
                    $(ContributorArguments);RunMyContributor=True;
                &lt;/ContributorArguments&gt;
            &lt;/PropertyGroup&gt;
            
            
            In this case the argument is only added for Debug configuration.
            Using the above configuration it is possible to collect information from the MSBuild environment
            and pass it to the contributor. 
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.BuildContributorContext.Model">
            <summary>
            Gets the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/> of the project
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.BuildContributorContext.ExtensionFiles">
            <summary>
            Gets an array of <see cref="T:Microsoft.Build.Framework.ITaskItem"/> representing 
            Files from the project system which influence the build contributors.
            
            Input configuration files can be defined in a .sqlproj file as part of the MSBuild process. 
            Inside a .sqproj file, Build Configuration files are specified as follows:
            
            &lt;ItemGroup&gt;
              &lt;BuildExtensionConfiguration Include="MyContributorName.MyFileName1.sql" /&gt;
              &lt;BuildExtensionConfiguration Include="MyContributorName.MyFileName2.sql" /&gt;
            &lt;/ItemGroup&gt;
            
            
            Note that configuration files are accessible to all contributors. Contributors may employ a
            file naming pattern to identify which input files related to that contributor.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.BuildFailedException">
            <summary>
            A BuildFailedException should be thrown to indicate that the build cannot continue.
            This will stop the build process.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.BuildFailedException.#ctor(System.String)">
            <summary>
            Constructs a <see cref="T:Microsoft.SqlServer.Dac.Deployment.BuildFailedException"/> with a string exception message
            </summary>
            <param name="message">string describing the cause of the failure</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.BuildFailedException.#ctor(System.String,System.Exception)">
            <summary>
            Constructs a <see cref="T:Microsoft.SqlServer.Dac.Deployment.BuildFailedException"/> with a string exception message
            and an inner <see cref="T:System.Exception"/> providing more information about the failure
            </summary>
            <param name="message">string describing the cause of the failure</param>
            <param name="innerException"><see cref="T:System.Exception"/> that is the root cause of the failure</param>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.ContributorArgumentConfiguration">
            <summary>
            Instances of this class are created by Build and Deployment contributors during design-time to 
            initialize arguments that are passed to them at runtime. 
            
            In SSDT, these arguments must be specified using a ContributorArguments property
            in a .sqlproj file or a referenced .targets file. For instance to add a "RunMyContributor" argument with a value of 
            "true", the following would be added:
            &lt;PropertyGroup&gt;
                &lt;ContributorArguments Condition="'$(Configuration)' == 'Debug''"&gt;
                    $(ContributorArguments);RunMyContributor=True;
                &lt;/ContributorArguments&gt;
            &lt;PropertyGroup&gt;
            
            
            In this case the argument is only added for Debug configuration.
            Using the above configuration it is possible to collect information from the MSBuild environment
            and pass it to the contributor. 
            
            It is also possible to specify these when creating a package by using <see cref="M:Microsoft.SqlServer.Dac.DacPackageExtensions.BuildPackage(System.String,Microsoft.SqlServer.Dac.Model.TSqlModel,Microsoft.SqlServer.Dac.PackageMetadata,Microsoft.SqlServer.Dac.PackageOptions)"/> 
            and specifying <see cref="T:Microsoft.SqlServer.Dac.PackageOptions"/>.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.ContributorArgumentConfiguration.#ctor">
            <summary>
            Initializes a new instance of the <see cref="T:Microsoft.SqlServer.Dac.Deployment.ContributorArgumentConfiguration"/> class.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.ContributorArgumentConfiguration.#ctor(System.String,System.String)">
            <summary>
            Initializes a new instance of the <see cref="T:Microsoft.SqlServer.Dac.Deployment.ContributorArgumentConfiguration"/> class.
            </summary>
            <param name="name">The name of the argument.</param>
            <param name="value">The value of the argument.</param>
            <exception cref="T:System.ArgumentNullException">If the <paramref name="name"/> or <paramref name="value"/> parameters are null</exception>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.ContributorArgumentConfiguration.Name">
            <summary>
            Gets or sets the name of the argument.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.ContributorArgumentConfiguration.Value">
            <summary>
            Gets or sets the value of the argument expressed as a string.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.DeploymentContributorConfigurationStream">
            <summary>
            Represents a streamable version of a configuration file that is used by DacFx in the deployment and build processes. 
            These streams enable deployment extensions to enhance and extend the design and deployment experience.
            
            Input configuration files can be defined in a .sqlproj file as part of the MSBuild process.
            The files are treated as streams, and will be passed to contributors
            via the <see cref="M:Microsoft.SqlServer.Dac.Deployment.DeploymentContributor.EstablishDeploymentConfiguration(Microsoft.SqlServer.Dac.Deployment.DeploymentContributorConfigurationSetup)"/> method, where they can
            be copied as outputs that are stored in the generated dacpac. 
            
            These streams will then be made available 
            in the <see cref="M:Microsoft.SqlServer.Dac.Deployment.DeploymentContributor.ApplyDeploymentConfiguration(Microsoft.SqlServer.Dac.Deployment.DeploymentContributorContext,System.Collections.Generic.ICollection{Microsoft.SqlServer.Dac.Deployment.DeploymentContributorConfigurationStream})"/>, where they can be
            consumed for configuration and data during deployment. At the end of the 
            <see cref="M:Microsoft.SqlServer.Dac.Deployment.DeploymentContributor.ApplyDeploymentConfiguration(Microsoft.SqlServer.Dac.Deployment.DeploymentContributorContext,System.Collections.Generic.ICollection{Microsoft.SqlServer.Dac.Deployment.DeploymentContributorConfigurationStream})"/> method all streams will be disposed. 
            Contributors that need to obtain data from the streams must read and cache the data during this method -
            attempts to read from the stream during the <see cref="M:Microsoft.SqlServer.Dac.Deployment.DeploymentPlanContributor.OnExecute(Microsoft.SqlServer.Dac.Deployment.DeploymentPlanContributorContext)"/> method
            will fail.
            
            Inside a .sqproj file, Deployment Configuration files are specified as follows:
            &lt;ItemGroup&gt;
              &lt;DeploymentExtensionConfiguration Include="MyContributorName.MyFileName1.sql" /&gt;
              &lt;DeploymentExtensionConfiguration Include="MyContributorName.MyFileName2.sql" /&gt;
            &lt;/ItemGroup&gt;
            
            Note that configuration streams are accessible to all contributors. Contributors may employ a
            file naming pattern to identify which input files related to that contributor.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentContributorConfigurationStream.#ctor(System.String)">
            <summary>
            Creates an instance of the <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentContributorConfigurationStream"/>
            </summary>
            <param name="filePath">A file path for which a stream can be created</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentContributorConfigurationStream.GetStream">
            <summary>
            Gets the <see cref="T:System.IO.Stream"/>
            </summary>
            <returns><see cref="T:System.IO.Stream"/></returns>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.DeploymentContributorConfigurationStream.Filename">
            <summary>
            The filename used to represent this stream
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.DeploymentContributorInformation">
            <summary>
            Information that identifies a deployment contributor
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.DeploymentContributorInformation.ExtensionId">
            <summary>
            Id of the extension that implements the 
            deployment contributor
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.DeploymentContributorInformation.Version">
            <summary>
            Version of the deployment contributor extension (optional).
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.DeploymentStepInternal">
            <summary>
            Base class for implementing deployment step types.
            It should not be used for writing extensions. Extensions
            must directly implement DeploymentStep
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.DeploymentStep">
            <summary>
            Represents a step in a deployment plan.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentStep.ToString">
            <summary>
            Returns a string that describes this step.  The base returns the
            type of the step
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentStep.GenerateTSQL">
            <summary>
            Returns a list of strings that represent a set of TSQL script batches to be applied
            during deployment
            </summary>
            <returns></returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentStep.GenerateBatchScript(System.IO.TextWriter)">
            <summary>
            Helper that writes the set of batch scripts to the provided writer
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.DeploymentStep.Next">
            <summary>
            Gets the next step in the container, which is a <see cref="T:System.Collections.Generic.LinkedListNode`1"/> of type DeploymentStep.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.DeploymentStep.Previous">
            <summary>
            Gets the previous step in the container, which is a <see cref="T:System.Collections.Generic.LinkedListNode`1"/> of type DeploymentStep.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentStepInternal.#ctor">
            <summary>
            Internal custructor as class is meant for internal purpose
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentStepInternal.#ctor(Microsoft.Data.Tools.Schema.Sql.Deployment.DeploymentStep)">
            <summary>
            Internal custructor as class is meant for internal purpose
            </summary>
            <param name="internalStep"></param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentStepInternal.GenerateTSQL">
            <summary>
            Returns a list of strings that represent a set of TSQL script batches to be applied
            during deployment
            </summary>
            <returns></returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentStepInternal.ToString">
            <summary>
            Returns a string that describes this step.  The base returns the
            type of the step
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.AlterElementStep">
            <summary>
            A step in a deployment plan that represent an alter to an element. 
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.DeploymentScriptDomStep">
            <summary>
            Base class for script based deployment steps.
            Represents a deployment step that consists of an Abstract Syntax Tree (AST) and reference to a script Domain Object Model (DOM) generator.
            This class is for read-only purpose.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.DeploymentScriptDomStep.Message">
            <summary>
            Gets an optional message describing the step
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.DeploymentScriptDomStep.IsMessageInFirstBatch">
            <summary>
            Returns true if the step's message will be the first batch when calling
            GenerateTSql()
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.DeploymentScriptDomStep.Script">
            <summary>
            Gets the script for this step.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.DeploymentScriptDomStep.ScriptGenerator">
            <summary>
            Gets the ScriptGenerator for this step
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.AlterElementStep.SourceElement">
            <summary>
            Get the source element in the alter step
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.AlterElementStep.TargetElement">
            <summary>
            Gets the target element in the alter step
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.BeginPostDeploymentScriptStep">
            <summary>
            A step in the deployment plan that represents script deployment at the beginning the post-deployment.
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.EmptyStep">
            <summary>
            Represents an empty step in deployment plan.
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.BeginPreDeploymentScriptStep">
            <summary>
             A step in the deployment plan that represents script deployment at the beginning of pre-deployment.
             This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.CreateElementStep">
            <summary>
            The step in a deployment plan that represents a create of an element.
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.CreateElementStep.SourceElement">
            <summary>
            Get the source element of the create element step
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.DacBulkCopyStep">
            <summary>
            The DacBulkCopyStep uploads / streams data from the dac package to the target db.
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.DacDeleteFromTablesStep">
            <summary>
            The step in deployment plan that represents delete from table.
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.DacDropSystemVersioningStep">
            <summary>
            The step in deployment plan that represents drop of a temporal system-versioning clause
            and system-time PERIOD.
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.DacRestoreSystemVersioningStep">
            <summary>
            The step in deployment plan that represents creation of a temporal system-versioning clause
            and system-time PERIOD on the appropriate temporal columns.
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.SqlAddSystemVersioningStep">
            <summary>
            The step in deployment plan that represents creation of a temporal system-versioning clause
            and system-time PERIOD on the appropriate temporal columns.
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.SqlDropSystemVersioningStep">
            <summary>
            The step in deployment plan that represents drop of a temporal system-versioning clause
            and system-time PERIOD.
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.DacDropTemporalSchemaBoundElementStep">
            <summary>
            The step in deployment plan that represents drop of an temporal table schema-bound objects
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.DacDisableForeignKeysStep">
            <summary>
            The step in deployment plan that represents disabling foreign keys.
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.DacRestoreConstraintsStep">
            <summary>
            The step in deployment plan that represents restoring constraints.
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.DacSaveConstraintsStep">
            <summary>
            The step in deployment plan that represents saving constraints.
            This class is for read-only purpose.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.DeploymentScriptStep">
            <summary>
            The step in deployment plan that represents deploying scripts.
            This class can be instantiated and used by extensions.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentScriptStep.#ctor(System.String)">
            <summary>
            Creates an instance of <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentScriptStep"/>
            </summary>
            <param name="text">script to deploy</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentScriptStep.#ctor(System.Collections.Generic.IEnumerable{System.String})">
            <summary>
            Initializes a <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentScriptStep"/> with multiple TSQL scripts
            </summary>
            <param name="batches"><see cref="T:System.Collections.Generic.IEnumerable`1"/> of strings representing
            TSQL scripts</param>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.DropElementStep">
            <summary>
            A step in deployment step that represents dropping a sql object.
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.DropElementStep.TargetElement">
            <summary>
            Gets the target element
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.EndPostDeploymentScriptStep">
            <summary>
            A step in the deployment plan that represents script deployment at the end of post-deployment.
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.EndPreDeploymentScriptStep">
            <summary>
            A step in the deployment plan that represents script deployment at the end of pre-deployment.
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.SqlBeginAltersStep">
            <summary>
            Instances of this class mark the portion of the deployment plan where
            ALTERs (and CREATEs) are performed.
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.SqlBeginDropsStep">
            <summary>
            An instance of this class marks the beginning of the "drops" portion of the
            deployment plan.  Drops occur in the plan before Alters.
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.SqlBeginPreservationStep">
            <summary>
            This class marks the start (in a deployment plan) of the preservation of intent section of the
            plan.  These operations are ones taken to affect refactoring changes.
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.SqlBeginTransactionStep">
            <summary>
            This step marks the beginning of the transactional section of a deployment plan.
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.SqlChangeDatabaseStep">
            <summary>
            The step in deployment step that represents change database.
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlChangeDatabaseStep.DatabaseName">
            <summary>
            Gets the database name
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.SqlCreateDatabaseStep">
            <summary>
            The step in deployment that represents database creation.
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.SqlDropDatabaseStep">
            <summary>
            The step in deployment that represents database drop.
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.SqlCreateTrackingTableStep">
            <summary>
            The step in deployment that represents the creation of the tracking table (__ScriptTrackingLogs).
            This table is used to improve the reliability of deployment to SQL Azure.
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.SqlDropTrackingTableStep">
            <summary>
            The step in deployment that represents drop of the tracking table (__ScriptTrackingLogs).
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.SqlEndAltersStep">
            <summary>
            Instances of this class mark the portion of the deployment plan where
            ALTERs (and CREATEs) are completed.
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.SqlEndDropsStep">
            <summary>
            An instance of this class marks the end of the "drops" portion of the
            deployment plan.
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.SqlEndPreservationStep">
            <summary>
            This class marks the end (in a deployment plan) of the preservation of intent section of the
            plan.  These operations are ones taken to affect refactoring changes.
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.SqlEndTransactionStep">
            <summary>
            This step marks the end of the transactional section of a deployment script.
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.SqlFinalizeDatabaseAccessStep">
            <summary>
            This step is represents a step in the SQL deployment plan that finalizes the access settings to the database.  These
            settings include Read-Only or Read-Write access, restricted, single user or multi-user, and online versus offline.
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.SqlMoveSchemaStep">
            <summary>
            In the deployment plan, instances of this step represent moving the element from a previous schema to
            the new one.
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlMoveSchemaStep.MovedElement">
            <summary>
            Gets the moved element
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlMoveSchemaStep.NewSchema">
            <summary>
            Gets the new schema
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlMoveSchemaStep.PreviousName">
            <summary>
            Gets the previous name
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.SqlPrintStep">
            <summary>
            A type of step in a Sql deployment plan that signifies a SQL "print" statement.
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.SqlRenameStep">
            <summary>
            Represents a step in the deployment plan that renames a element.
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlRenameStep.RenamedElement">
            <summary>
            Gets the renamed object
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlRenameStep.OldName">
            <summary>
            Gets the old name of the object
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlRenameStep.NewName">
            <summary>
            Gets the new name of the object
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.SqlTableMigrationStep">
            <summary>
            Represents a data motion step in a sql deployment plan.
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlTableMigrationStep.SourceTable">
            <summary>
            Gets the source table object
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlTableMigrationStep.TargetTable">
            <summary>
            Gets the target table object
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.SqlColumnEncryptionMigrationStep">
            <summary>
            Represents a data motion step for column encryption in a sql deployment plan.
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlColumnEncryptionMigrationStep.SourceTable">
            <summary>
            Gets the source table object
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlColumnEncryptionMigrationStep.TargetTable">
            <summary>
            Gets the target table object
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.SqlEnableDatabaseChangeTrackingStep">
            <summary>
            Represents a step which will enable change tracking on the database
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.SqlDisableDatabaseChangeTrackingStep">
            <summary>
            Represents a step which will disable change tracking on the database
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.SqlColumnEncryptionSPRefreshStep">
            <summary>
            Represents a step which will execute sp_refresh stored procedures
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.SqlColumnEncryptionRenameToOriginalStep">
            <summary>
            Represents a rename back to original step for column encryption in a sql deployment plan.
            This class is for read-only purpose and cannot be instantiated.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlColumnEncryptionRenameToOriginalStep.SourceTable">
            <summary>
            Gets the source table object
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlColumnEncryptionRenameToOriginalStep.TargetTable">
            <summary>
            Gets the target table object
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.ExportBuildContributorAttribute">
            <summary>
            Concrete classes implementing <see cref="T:Microsoft.SqlServer.Dac.Deployment.BuildContributor"/> must add the 
            <see cref="T:Microsoft.SqlServer.Dac.Deployment.ExportBuildContributorAttribute"/> attribute to their class definition. This ensures 
            they will be detected and available for use during build.
            <see cref="T:Microsoft.SqlServer.Dac.Deployment.BuildContributor"/>s require a unique ID which can be based on their fully qualified type name, or explicitly
            passed as a string
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.ExportBuildContributorAttribute.#ctor(System.String,System.String)">
            <summary>
            Initializes a <see cref="T:Microsoft.SqlServer.Dac.Deployment.ExportBuildContributorAttribute"/>
            </summary>
            <param name="id">Unique Id used to identify the export </param>
            <param name="version">Optional string defining the version number of the extension. 
            Must be a valid version string </param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.ExportBuildContributorAttribute.#ctor(System.Type,System.String)">
            <summary>
            Intializes a <see cref="T:Microsoft.SqlServer.Dac.Deployment.ExportBuildContributorAttribute"/>
            </summary>
            <param name="implementingType">The Concrete type that extends <see cref="T:Microsoft.SqlServer.Dac.Deployment.BuildContributor"/> - this is used
            to generate the unique Id for this Export </param>
            <param name="version">Optional string defining the version number of the extension. 
            Must be a valid version string </param>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.DeploymentPlanExecutor">
            <summary>
            This class represents a deployment constributor that executes the deployment plan.  An
            example of a constributor would be one who executes deployment steps to create a 
            report about actions performed during a deployment
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.DeploymentPlanContributor">
            <summary>
            Represents a contributor to the deployment process
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.DeploymentContributor">
            <summary>
            The base class for deployment contributors.  Subclasses of this class
            participate in a deployment by modifying a deployment plan or by executing
            the plan
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentContributor.#ctor">
            <summary>
            Initializes a new instance of the <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentContributor"/> class.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentContributor.Dispose">
            <summary>
            Inherited from <see cref="M:System.IDisposable.Dispose"/>
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentContributor.Dispose(System.Boolean)">
            <summary>
            
            </summary>
            <param name="disposing"></param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentContributor.PublishMessage(Microsoft.SqlServer.Dac.Extensibility.ExtensibilityError)">
            <summary>
            Publishes a message to the deployment engine. The deployment engine will process and report the message to consumers.
            </summary>
            <param name="message">A <see cref="T:Microsoft.SqlServer.Dac.Extensibility.ExtensibilityError"/> that contains the message to publish</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentContributor.OnEstablishDeploymentConfiguration(Microsoft.SqlServer.Dac.Deployment.DeploymentContributorConfigurationSetup)">
            <summary>
            Called by the build process in DacFx to give your code an opportunity to modify deployment configuration.
            </summary>
            <param name="setup">The current <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentContributorConfigurationSetup"/> object.</param>
            <exception cref="T:Microsoft.SqlServer.Dac.Deployment.BuildFailedException">If there is a critical error that should stop the build process
            from continuing, implementing contributors may throw a <see cref="T:Microsoft.SqlServer.Dac.Deployment.BuildFailedException"/>.</exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentContributor.OnApplyDeploymentConfiguration(Microsoft.SqlServer.Dac.Deployment.DeploymentContributorContext,System.Collections.Generic.ICollection{Microsoft.SqlServer.Dac.Deployment.DeploymentContributorConfigurationStream})">
            <summary>
            Called by the deployment process in DacFx to give your code an opportunity to collect configuration information from the provided files.
            At the end of the method all streams will be disposed. 
            Contributors that need to obtain data from the streams must read and cache the data during this method -
            attempts to read from the stream during a later method such as the <see cref="M:Microsoft.SqlServer.Dac.Deployment.DeploymentPlanContributor.OnExecute(Microsoft.SqlServer.Dac.Deployment.DeploymentPlanContributorContext)"/> method
            will fail.
            </summary>
            <param name="context">A <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentContributorContext"/> object</param>
            <param name="configurationStreams">An <see cref="T:System.Collections.Generic.ICollection`1"/> of <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentContributorConfigurationStream"/> objects</param>
            <exception cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentFailedException">If there is a critical error that should stop the deployment process
            from continuing, implementing contributors may throw a <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentFailedException"/>.</exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentContributor.Cancel">
            <summary>
            Cancels execution of the contributor
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.DeploymentContributor.Canceled">
            <summary>
            Gets a value that indicates whether the contributor was canceled
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.DeploymentContributor.CancellationToken">
            <summary>
            The <see cref="P:Microsoft.SqlServer.Dac.Deployment.DeploymentContributor.CancellationToken"/> used to indicate whether contributor execution should be canceled
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentPlanContributor.OnExecute(Microsoft.SqlServer.Dac.Deployment.DeploymentPlanContributorContext)">
            <summary>
            Called by the deployment engine to allow custom contributors to execute their unique tasks
            </summary>
            <param name="context">A <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentContributorContext"/> object</param>
            <exception cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentFailedException">If there is a critical error the should stop the deployment process
            from continuing, implementing contributors may throw a <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentFailedException"/>.</exception>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.ExportDeploymentPlanExecutorAttribute">
            <summary>
            Concrete classes implementing <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentPlanExecutor"/> must add the 
            <see cref="T:Microsoft.SqlServer.Dac.Deployment.ExportDeploymentPlanExecutorAttribute"/> attribute to their class definition. This ensures 
            they will be detected and available for use during SQL Deployment.
            <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentPlanExecutor"/>s require a unique ID which can be based on their fully qualified type name, or explicitly
            passed as a string
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.ExportDeploymentPlanExecutorAttribute.#ctor(System.String,System.String)">
            <summary>
            Initializes a <see cref="T:Microsoft.SqlServer.Dac.Deployment.ExportDeploymentPlanExecutorAttribute"/>
            </summary>
            <param name="id">Unique Id used to identify the export </param>
            <param name="version">Optional string defining the version number of the extension. 
            Must be a valid version string </param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.ExportDeploymentPlanExecutorAttribute.#ctor(System.Type,System.String)">
            <summary>
            Intializes a <see cref="T:Microsoft.SqlServer.Dac.Deployment.ExportDeploymentPlanExecutorAttribute"/>
            </summary>
            <param name="implementingType">The Concrete type that extends <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentPlanExecutor"/> - this is used
            to generate the unique Id for this Export </param>
            <param name="version">Optional string defining the version number of the extension. 
            Must be a valid version string </param>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.DeploymentContributorConfigurationSetup">
            <summary>
            Represents the current setup for <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentContributor"/> objects. 
            The setup object can be used by deployment contributors during build operations to cause files 
            to be copied as outputs that are stored in the generated dacpac, or to create new files 
            that are stored in the dacpac. See the <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentContributorConfigurationStream"/> API
            for more information about specifying inputs.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentContributorConfigurationSetup.EnumerateInputs">
            <summary>
            Returns an enumerable collection of <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentContributorConfigurationStream"/> objects.
            </summary>
            <returns></returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentContributorConfigurationSetup.OpenNewOutput(System.String,System.Collections.Generic.IDictionary{System.String,System.String})">
            <summary>
            Returns a new output configuration file stream that is open for read and write.
            </summary>
            <param name="fileName">A partial file name.</param>
            <param name="metadata">The metadata to be saved to the file.</param>
            <returns></returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentContributorConfigurationSetup.CopyInputToOutput(Microsoft.SqlServer.Dac.Deployment.DeploymentContributorConfigurationStream)">
            <summary>
            Copies an input stream's contents into an output stream that is stored inside a package
            </summary>
            <param name="stream"><see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentContributorConfigurationStream"/> to be copied</param>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.DeploymentContributorConfigurationSetup.Properties">
            <summary>
            Gets a dictionary that contains the current properties and settings.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.DeploymentContributorConfigurationSetup.SqlCmdVariables">
            <summary>
            Gets the SqlCmd variables defined
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.DeploymentContributorContext">
            <summary>
            Provides a context for <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentContributor"/> objects in DacFx
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.DeploymentContributorContext.Arguments">
            <summary>
            Gets or sets a dictionary of named arguments and their values.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.DeploymentContributorContext.Options">
            <summary>
            Gets the options being used for this deployment
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.DeploymentContributorContext.IsAzureSource">
            <summary>
            Does the <see cref="P:Microsoft.SqlServer.Dac.Deployment.DeploymentContributorContext.Source"/> model a SQL Azure database.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.DeploymentContributorContext.IsAzureTarget">
            <summary>
            Does the <see cref="P:Microsoft.SqlServer.Dac.Deployment.DeploymentContributorContext.Target"/> model a SQL Azure database.
            
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.DeploymentContributorContext.Source">
            <summary>
            Gets the Source <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/>
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.DeploymentContributorContext.Target">
            <summary>
            Gets the Target <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/>
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.DeploymentFailedException">
            <summary>
            Represents an exception that occurs during deployment.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentFailedException.#ctor">
            <summary>
            Initializes a new instance of the <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentFailedException"/> class
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentFailedException.#ctor(System.String)">
            <summary>
            Initializes a new instance of the <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentFailedException"/> class
            </summary>
            <param name="message">Indicates the reason for the exception.</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentFailedException.#ctor(System.String,System.Exception)">
            <summary>
            Initializes a new instance of the <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentFailedException"/> class 
            </summary>
            <param name="message">Indicates the reason for the exception.</param>
            <param name="innerException">Indicates a nested exception.</param>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.DeploymentPlan">
            <summary>
            Represents the plan that is generated for Deployment. 
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentPlan.AddFirst(Microsoft.SqlServer.Dac.Deployment.DeploymentStep)">
            <summary>
            
            </summary>
            <param name="newStep"></param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentPlan.AddBefore(Microsoft.SqlServer.Dac.Deployment.DeploymentStep,Microsoft.SqlServer.Dac.Deployment.DeploymentStep)">
            <summary>
            
            </summary>
            <param name="step"></param>
            <param name="newStep"></param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentPlan.AddAfter(Microsoft.SqlServer.Dac.Deployment.DeploymentStep,Microsoft.SqlServer.Dac.Deployment.DeploymentStep)">
            <summary>
            
            </summary>
            <param name="step"></param>
            <param name="newStep"></param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentPlan.AddLast(Microsoft.SqlServer.Dac.Deployment.DeploymentStep)">
            <summary>
            
            </summary>
            <param name="newStep"></param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentPlan.AddLast(System.Collections.Generic.IEnumerable{Microsoft.SqlServer.Dac.Deployment.DeploymentStep})">
            <summary>
            
            </summary>
            <param name="steps"></param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentPlan.Remove(Microsoft.SqlServer.Dac.Deployment.DeploymentStep)">
            <summary>
            
            </summary>
            <param name="step"></param>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.DeploymentPlan.StepCount">
            <summary>
            Total number of steps in the plan
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.DeploymentPlan.Head">
            <summary>
            Gets the first deployment step
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.DeploymentPlan.Tail">
            <summary>
            Gets the last deployment step
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.DeploymentPlanHandle">
            <summary>
            
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.DeploymentPlanHandle.Head">
            <summary>
            Returns the head on the plan or null if the plan is empty
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.DeploymentPlanHandle.Tail">
            <summary>
            Returns the tail of the plan or null if the plan is empty
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.DeploymentPlanContributorContext">
            <summary>
            Provides a context for <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentPlanContributor"/> objects.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.DeploymentPlanContributorContext.ComparisonResult">
            <summary>
            Gets the <see cref="T:Microsoft.SqlServer.Dac.Deployment.ModelComparisonResult"/>
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.DeploymentPlanContributorContext.PlanHandle">
            <summary>
            Gets the <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentPlanHandle"/>
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.DeploymentPlanContributorContext.DeploymentMasterPath">
            <summary>
            Set if a deployment script is also being generated against an Azure target
            (which requires that the portion executed against master be its own script)
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.DeploymentPlanContributorContext.DeploymentScriptPath">
            <summary>
            Set if a deployment script is also being generated.  This
            enables other deployment contributors to correlate error messages back to a script/file
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.DeploymentPlanModifier">
            <summary>
            Represents a constributor that can modify a deployment plan
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentPlanModifier.AddBefore(Microsoft.SqlServer.Dac.Deployment.DeploymentPlanHandle,Microsoft.SqlServer.Dac.Deployment.DeploymentStep,Microsoft.SqlServer.Dac.Deployment.DeploymentStep)">
            <summary>
            Provides a way for subclasses to modify the plan by adding a
            step after the specified step
            </summary>
            <param name="handle">The <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentPlanHandle"/> for the plan</param>
            <param name="step">Identifies the <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentStep"/> before which the <paramref name="newStep"/> will be added.</param>
            <param name="newStep">The <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentStep"/> to be added</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentPlanModifier.AddAfter(Microsoft.SqlServer.Dac.Deployment.DeploymentPlanHandle,Microsoft.SqlServer.Dac.Deployment.DeploymentStep,Microsoft.SqlServer.Dac.Deployment.DeploymentStep)">
            <summary>
            Provides a way to modify the existing plan by adding a step after the existing step
            </summary>
            <param name="handle">The <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentPlanHandle"/> for the plan</param>
            <param name="step">Identifies the <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentStep"/> after which the <paramref name="newStep"/> will be added.</param>
            <param name="newStep">The <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentStep"/> to be added</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DeploymentPlanModifier.Remove(Microsoft.SqlServer.Dac.Deployment.DeploymentPlanHandle,Microsoft.SqlServer.Dac.Deployment.DeploymentStep)">
            <summary>
            Provides a way to remove the specified step from the plan
            </summary>
            <param name="handle">The <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentPlanHandle"/> for the plan</param>
            <param name="step">Identifies the <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentStep"/> to be removed.</param>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.ExportDeploymentPlanModifierAttribute">
            <summary>
            Concrete classes implementing <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentPlanModifier"/> must add the 
            <see cref="T:Microsoft.SqlServer.Dac.Deployment.ExportDeploymentPlanModifierAttribute"/> attribute to their class definition. This ensures 
            they will be detected and available for use during SQL Deployment.
            <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentPlanModifier"/>s require a unique ID which can be based on their fully qualified type name, or explicitly
            passed as a string
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.ExportDeploymentPlanModifierAttribute.#ctor(System.String,System.String)">
            <summary>
            Initializes a <see cref="T:Microsoft.SqlServer.Dac.Deployment.ExportDeploymentPlanModifierAttribute"/>
            </summary>
            <param name="id">Unique Id used to identify the export </param>
            <param name="version">Optional string defining the version number of the extension. 
            Must be a valid version string </param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.ExportDeploymentPlanModifierAttribute.#ctor(System.Type,System.String)">
            <summary>
            Intializes a <see cref="T:Microsoft.SqlServer.Dac.Deployment.ExportDeploymentPlanModifierAttribute"/>
            </summary>
            <param name="implementingType">The Concrete type that extends <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentPlanModifier"/> - this is used
            to generate the unique Id for this <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentPlanModifier"/> Export </param>
            <param name="version">Optional string defining the version number of the extension. 
            Must be a valid version string </param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.Internal.ContributorLoader`1.CreateLoader">
            <summary>
            Creates a <see cref="T:Microsoft.SqlServer.Dac.Deployment.Internal.ContributorLoader`1"/> that uses standard extension lookup 
            properties and returns this to the caller
            </summary>
            <returns><see cref="T:Microsoft.SqlServer.Dac.Deployment.Internal.ContributorLoader`1"/></returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.Internal.ContributorLoader`1.#ctor(Microsoft.SqlServer.Dac.Extensibility.CompositionProperties)">
            <summary>
            Initializes the <see cref="T:Microsoft.SqlServer.Dac.Deployment.Internal.ContributorLoader`1"/> 
            </summary>
            <param name="compositionProperties"><see cref="T:Microsoft.SqlServer.Dac.Extensibility.CompositionProperties"/> defining how contributors should be loaded</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.Internal.ContributorLoader`1.GetMatchingExtensions(System.Collections.Generic.IEnumerable{Microsoft.Data.Tools.Schema.Sql.Deployment.ContributorInfo},System.Collections.Generic.List{Microsoft.SqlServer.Dac.Extensibility.ExtensibilityError}@)">
            <summary>
            Gets all matching extensions for a set of required contributors.
            </summary>
            <param name="requiredContributors"><see cref="T:System.Collections.Generic.IEnumerable`1"/> specifying contributos to load and any version matching requirements</param>
            <param name="typeLoadErrors">Output <see cref="T:System.Collections.Generic.List`1"/> containing all errors that occurred during load. This will include general load errors
            for all contributors on the machine, plus load errors relating to version mismatch between requested contributor version
            and the available contributors on the machine</param>
            <returns></returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.Internal.ContributorLoader`1.GetAllExtensions(System.Collections.Generic.List{Microsoft.SqlServer.Dac.Extensibility.ExtensibilityError}@)">
            <summary>
            Gets all extensions matching the Contributor type.
            </summary>
            <param name="typeLoadErrors">Output <see cref="T:System.Collections.Generic.List`1"/> containing all errors that occurred during load. This will include general load errors
            for all contributors on the machine, plus load errors relating to version mismatch between requested contributor version
            and the available contributors on the machine</param>
            <returns></returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.Internal.ContributorLoader`1.LoadSupportedContributorIds(System.Collections.Generic.IEnumerable{Microsoft.Data.Tools.Schema.Sql.Deployment.ContributorInfo},System.Collections.Generic.Dictionary{System.String,`0}@,System.Collections.Generic.List{Microsoft.SqlServer.Dac.Extensibility.ExtensibilityError}@)">
            <summary>
            Finds and loads all contributors that match the expected <see cref="T:Microsoft.Data.Tools.Schema.Sql.Deployment.ContributorInfo"/> data
            </summary>
            <param name="expectedContributors">Input enumerable of <see cref="T:Microsoft.Data.Tools.Schema.Sql.Deployment.ContributorInfo"/>s defining the contributors being queried</param>
            <param name="loadedContributors"><see cref="T:System.Collections.Generic.Dictionary`2"/> mapping contributor ID to an instance of the contributor</param>
            <param name="loadErrors">Any <see cref="T:Microsoft.SqlServer.Dac.Extensibility.ExtensibilityError"/>s encountered while loading the contributors</param>
            <returns>List of <see cref="T:Microsoft.Data.Tools.Schema.Sql.Deployment.ContributorInfo"/>s describing the contributors that matched the query</returns>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.ModelComparisonChangeDefinitionService">
            <summary>
            Service that provides data about a <see cref="T:Microsoft.SqlServer.Dac.Deployment.ModelComparisonChangeDefinition"/>
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.ModelComparisonResultService">
            <summary>
            Service providing data required for a ModelComparisonResult
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.ModelComparisonResultService.IsEqual">
            <summary>
            If compared elements are equal
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.ModelComparisonResultService.ElementsEqual">
            <summary>
            List of elements that have no changes
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.ModelComparisonResultService.ElementGroupsEqual">
            <summary>
            List of element groups that are equal
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.ModelComparisonResultService.ElementsToAdd">
            <summary>
            List of elements exists in source, does not exist in target
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.ModelComparisonResultService.ElementsToDrop">
            <summary>
            List of elements exists in target, does not exist in source
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.ModelComparisonResultService.ElementsChanged">
            <summary>
            List of elements are changed.
            The result will keyed by elements in source, and it will have
            what are the changes together with that changed element.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.ModelComparisonResultService.ElementsPropertyChanged">
            <summary>
            List of elements changed because of properties are changed.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.ModelComparisonResultService.ElementsRelationshipEntryChanged">
            <summary>
            List of elements changed because of relationship entries are added or dropped
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.ModelComparisonResultService.ElementsComposingChildrenChanged">
            <summary>
            List of elements changed because of composed children are changed.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.ModelComparisonResultService.ElementsHierarchicalChildrenChanged">
            <summary>
            List of elements changed because of hierarchical dhildren are changed.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.Internal.InternalDeploymentContributorHelper`1">
            <summary>
            Helper class that converts and relays calls from Internal <see cref="T:Microsoft.Data.Tools.Schema.Sql.Deployment.DeploymentContributor"/> API
            to public external API
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.Internal.InternalDeploymentContributorHelper`1.ProcessContributorMethodCall(System.String,`0,System.Action{`0})">
            <summary>
            Calls a method on a contributor, handling message passing requirements. 
            Rethrows public <see cref="T:Microsoft.SqlServer.Dac.Deployment.DeploymentFailedException"/>s as internal
            <see cref="T:Microsoft.Data.Tools.Schema.Sql.Deployment.DeploymentFailedException"/>s.
            </summary>
            <param name="contributorId">Id of the contributor to be called </param>
            <param name="contributor">contributor to be called</param>
            <param name="contributorMethodCall">Action that </param>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.Internal.InternalDeploymentPlanContributorHelper`1">
            <summary>
            Helper class that converts and relays calls from Internal <see cref="T:Microsoft.Data.Tools.Schema.Sql.Deployment.DeploymentPlanContributor"/> API
            to public external API
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.Internal.InternalDeploymentPlanExecutor">
            <summary>
            A shim between the public API and the internal DacFx API. Responsible for loading all public DeploymentPlanExecutor contributors, and 
            relaying the calls from the internal API to the public API
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.Internal.InternalDeploymentPlanModifier">
            <summary>
            A shim between the public API and the internal DacFx API. Responsible for loading all public DeploymentPlanModifier contributors, and 
            relaying the calls from the internal API to the public API
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.Internal.InternalDeploymentPlanRelay">
            <summary>
            Acts as a relay between the internal and public DeploymentPlans. Converts
            Internal DeploymentStep objects to Public objects, and vice versa. 
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.Internal.InternalDeploymentPlanRelay.InternalStep">
            <summary>
            Wraps a step from the public API, ensuring it can be used by the internal API
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService">
            <summary>
            Service that provides data about a <see cref="T:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions"/>
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.AllowDropBlockingAssemblies">
            <summary>
            Get boolean that specifies whether CLR deployment will cause blocking assemblies to be dropped.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.AllowIncompatiblePlatform">
            <summary>
            Get boolean that specifies whether deployment will block due to platform compatibility.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.BackupDatabaseBeforeChanges">
            <summary>
            Get boolean that specifies whether a database backup will be performed before proceeding 
            with the actual deployment actions.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.BlockOnPossibleDataLoss">
            <summary>
            Get boolean that specifies whether deployment should stop if the operation could cause data loss.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.BlockWhenDriftDetected">
            <summary>
            Get boolean that specifies whether the system will check for differences between the 
            present state of the database and the registered state of the database and block deployment 
            if changes are detected.  Even if this option is set to true, drift detection will only occur 
            on a database if it was previously deployed with the <see cref="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.RegisterDataTierApplication"/> option enabled.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.CommentOutSetVarDeclarations">
            <summary>
            Get boolean that specifies whether the declaration of SQLCMD variables are commented 
            out in the script header.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.CompareUsingTargetCollation">
            <summary>
            Get boolean that specifies whether the target collation will be used for identifier 
            comparison.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.CreateNewDatabase">
            <summary>
            Get boolean that specifies whether the existing database will be dropped
            and a new database created before proceeding with the actual deployment actions.
            Acquires single-user mode before dropping the existing database.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.DeployDatabaseInSingleUserMode">
            <summary>
            Get boolean that specifies whether the system will acquire single-user mode on the target
            database during the duration of the deployment operation.
            </summary>
            <remarks>
            The database will be returned to multi-user mode after all changes are applied.
            Database may remain in single-user mode if an error occurs during execution.
            </remarks>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.DisableAndReenableDdlTriggers">
            <summary>
            Get boolean that specifies if all DDL triggers will be disabled for the duration of the 
            deployment operation and then re-enabled after all changes are applied.  
            </summary>
            <remarks>
            Triggers may remain disabled if an error occurs during execution.
            </remarks>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.DoNotAlterChangeDataCaptureObjects">
            <summary>
            Get boolean that specifies whether items configured for Change Data Capture (CDC)
            should be altered during deployment.  
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.DoNotAlterReplicatedObjects">
            <summary>
            Get boolean that specifies whether items configured for Replication
            should be altered during deployment.  
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.DropConstraintsNotInSource">
            <summary>
            Get boolean that specifies whether to drop all constraints that do not
            exist in the source model.  
            </summary>
            <remarks>
            This applies to check, default, foreign key, primary key, and unique constraints.
            </remarks>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.DropDmlTriggersNotInSource">
            <summary>
            Get boolean that specifies whether to drop all DML triggers that do not
            exist in the source model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.DropExtendedPropertiesNotInSource">
            <summary>
            Get boolean that specifies whether to drop all extended properties that do 
            not exist in the source model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.DropIndexesNotInSource">
            <summary>
            Get boolean that specifies whether to drop all indexes that do not
            exist in the source model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.DropObjectsNotInSource">
            <summary>
            Get boolean that specifies whether objects that exist in the target but not source should be dropped during deployment.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.DropPermissionsNotInSource">
            <summary>
            Get boolean that specifies whether to drop all permissions that do not
            exist in the source model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.DropRoleMembersNotInSource">
            <summary>
            Get boolean that specifies whether to drop all role memberships that do not
            exist in the source model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.DropStatisticsNotInSource">
            <summary>
            Get boolean that specifies whether to drop all statistics that do not
            exist in the source model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.GenerateSmartDefaults">
            <summary>
            Get boolean that specifies whether default values should be generated to populate NULL columns that are constrained to NOT NULL values.
            </summary>
            <remarks>
            This is useful when needing to add a new NOT NULL column to an existing table with data.
            </remarks>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreAnsiNulls">
            <summary>
            Get boolean that specifies whether to exclude the ANSI_NULL option from 
            consideration when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreAuthorizer">
            <summary>
            Get boolean that specifies whether to exclude the AUTHORIZATION option from 
            consideration when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreColumnCollation">
            <summary>
            Get boolean that specifies whether to exclude the collation specifier from 
            consideration when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreColumnOrder">
            <summary>
            Get boolean that specifies whether to exclude from consideration 
            the order of columns in tables when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreComments">
            <summary>
            Get boolean that specifies whether to exclude comments from 
            consideration when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreCryptographicProviderFilePath">
            <summary>
            Get boolean that specifies whether to exclude the file specification 
            of a cryptographic provider from consideration when comparing the source and 
            target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreDdlTriggerOrder">
            <summary>
            Get boolean that specifies whether to exclude DDL trigger order from 
            consideration when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreDdlTriggerState">
            <summary>
            Get boolean that specifies whether to exclude DDL trigger state from 
            consideration when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreDefaultSchema">
            <summary>
            Get boolean that specifies whether to exclude the DEFAULT_SCHEMA option from 
            consideration when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreDmlTriggerOrder">
            <summary>
            Get boolean that specifies whether to exclude DML trigger order from 
            consideration when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreDmlTriggerState">
            <summary>
            Get boolean that specifies whether to exclude DML trigger state from 
            consideration when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreExtendedProperties">
            <summary>
            Get boolean that specifies whether to exclude all extended properties from 
            consideration when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreFileAndLogFilePath">
            <summary>
            Get boolean that specifies whether to exclude the FILENAME option of 
            FILE objects from consideration when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreFilegroupPlacement">
            <summary>
            Get boolean that specifies whether to exclude the filegroup specifier 
            from consideration when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreFileSize">
            <summary>
            Get boolean that specifies whether to exclude the SIZE option of FILE objects 
            from consideration when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreFillFactor">
            <summary>
            Get boolean that specifies whether to exclude the FILLFACTOR option
            from consideration when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreFullTextCatalogFilePath">
            <summary>
            Get boolean that specifies whether to exclude the path specification of 
            FULLTEXT CATALOG objects from consideration when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreIdentitySeed">
            <summary>
            Get boolean that specifies whether to exclude the seed value of IDENTITY columns 
            from consideration when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreIncrement">
            <summary>
            Get boolean that specifies whether to exclude the increment value of IDENTITY columns 
            from consideration when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreIndexOptions">
            <summary>
            Get boolean that specifies whether to exclude differences in index options 
            from consideration when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreIndexPadding">
            <summary>
            Get boolean that specifies whether to exclude the PAD_INDEX option 
            from consideration when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreKeywordCasing">
            <summary>
            Get boolean that specifies whether to exclude difference in the casing of keywords 
            from consideration when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreLockHintsOnIndexes">
            <summary>
            Get boolean that specifies whether to exclude the ALLOW_ROW_LOCKS and 
            ALLOW_PAGE_LOGKS options from consideration when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreLoginSids">
            <summary>
            Get boolean that specifies whether to exclude the SID option of the LOGIN object 
            from consideration when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreNotForReplication">
            <summary>
            Get boolean that specifies whether to exclude the NOT FOR REPLICATION option 
            from consideration when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreObjectPlacementOnPartitionScheme">
            <summary>
            Get boolean that specifies whether to exclude the partition scheme object
            from consideration when comparing the source and target model for the following
            objects: Table, Index, Unique Key, Primary Key, and Queue.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnorePartitionSchemes">
            <summary>
            Get boolean that specifies whether to exclude the parameter type and 
            boundary VALUES of a PARTITION FUNCTION from consideration when comparing the 
            source and target model.  Also excludes FILEGROUP and partition function of a 
            PARTITION SCHEMA from consideration when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnorePermissions">
            <summary>
            Get boolean that specifies whether to exclude all permission statements 
            from consideration when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreQuotedIdentifiers">
            <summary>
            Get boolean that specifies whether to exclude the QUOTED_IDENTIFIER option 
            from consideration when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreRoleMembership">
            <summary>
            Get boolean that specifies whether to exclude all ROLE MEMBERSHIP objects 
            from consideration when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreRouteLifetime">
            <summary>
            Get boolean that specifies whether to exclude the LIFETIME option of ROUTE objects 
            from consideration when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreSemicolonBetweenStatements">
            <summary>
            Get boolean that specifies whether to exclude the existence or absence of semi-colons 
            from consideration when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreTableOptions">
            <summary>
            Get boolean that specifies whether the options on the target table are updated 
            to match the source table.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreUserSettingsObjects">
            <summary>
            Get boolean that specifies whether to exclude user settings 
            from consideration when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreWhitespace">
            <summary>
            Get boolean that specifies whether to exclude whitespace 
            from consideration when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreWithNocheckOnCheckConstraints">
            <summary>
            Get boolean that specifies whether to exclude the CHECK|NO CHECK option of a CHECK 
            constraint object from consideration when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IgnoreWithNocheckOnForeignKeys">
            <summary>
            Get boolean that specifies whether to exclude the CHECK|NO CHECK option of a FOREIGN KEY  
            constraint object from consideration when comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IncludeCompositeObjects">
            <summary>
            Get boolean that specifies whether to include referenced, external elements that also 
            compose the source model and then update the target database in a single deployment operation.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.IncludeTransactionalScripts">
            <summary>
            Get boolean that specifies whether to use transations during the deployment operation 
            and commit the transaction after all changes are successfully applied.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.NoAlterStatementsToChangeClrTypes">
            <summary>
            Get boolean that specifies whether to force a change to CLR assemblies by dropping and recreating them.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.PopulateFilesOnFileGroups">
            <summary>
            Get boolean that specifies whether files are supplied for filegroups defined in the deployment source.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.RegisterDataTierApplication">
            <summary>
            Get boolean that specifies whether the database will be registered as a Data-Tier Application.  
            If the target database is already a registered Data-Tier Application, then the registration will be updated.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.ScriptDatabaseCollation">
            <summary>
            Get boolean that specifies whether the target database should be altered to match the 
            source model's collation.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.ScriptDatabaseCompatibility">
            <summary>
            Get boolean that specifies whether the target database should be altered to match the 
            source model's compatibility level.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.ScriptDatabaseOptions">
            <summary>
            Get boolean that specifies whether the database options in the target database should 
            be updated to match the source model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.ScriptDeployStateChecks">
            <summary>
            Get boolean that specifies whether the target database should be checked to ensure that 
            it exists, is online and can be updated.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.ScriptFileSize">
            <summary>
            Get boolean that specifies whether a file size is specified when adding files to file groups.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.ScriptNewConstraintValidation">
            <summary>
            Get boolean that specifies whether constraints are validated after all changes are applied.
            </summary>
            <remarks>
            Constraints are always added with NOCHECK option; as a result their validation is skipped during creation.
            </remarks>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.ScriptRefreshModule">
            <summary>
            Get boolean that specifies whether referencing procedures are refreshed when referenced objects are updated.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.TargetConnectionString">
            <summary>
            Get string that specifies the target connection string
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.TargetDatabaseExists">
            <summary>
            Gets boolean that if set specifies whether the target database exists
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.TargetDatabaseName">
            <summary>
            Gets string that specifies the target database name
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.TargetingServerless">
            <summary>
            Get boolean that specifies whether the target server is LocalDB
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.TreatVerificationErrorsAsWarnings">
            <summary>
            Get boolean that specifies whether the deployment operation should proceed when errors are 
            generated during plan verification.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.UnmodifiableObjectWarnings">
            <summary>
            Get boolean that specifies whether the deployment operation should proceed when errors are 
            generated during plan verification.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.VerifyCollationCompatibility">
            <summary>
            Get boolean that specifies whether deployment will verify that the collation specified in the 
            source model is compatible with the collation specified in the target model.
            </summary>
            <value>
            True to continue if errors are generated during plan verification; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.VerifyDeployment">
            <summary>
            Get boolean that specifies whether the plan verification phase is executed or not.
            </summary>
            <value>
            True to perform plan verification; otherwise, false to skip it.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptionsService.SqlCommandVariableValues">
            <summary>
            Get dictionary of SQL command variable values, keyed by variable name.
            </summary>
            <value>
            Dictionary of SQL command variable values, keyed by variable name.
            </value>
            <remarks>
            Valid values must be provided for every variable before deployment, or failures may occur during deployment.
            </remarks>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.ModelComparisonChangeDefinition">
            <summary>
            Contains change details for a TSqlObject.
            Including changed properties, added/dropped/ordinal changed relationship entries,
            added/dropped/changed/ordinal changed composing children and 
            added/dropped/changed/ordinal changed hierarchical children.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.ModelComparisonChangeDefinition.#ctor(Microsoft.SqlServer.Dac.Deployment.ModelComparisonChangeDefinitionService)">
            <summary>
            Constructs a ModelComparisonChangeDefinition that uses a <see cref="T:Microsoft.SqlServer.Dac.Deployment.ModelComparisonChangeDefinitionService"/>
            to retrive definition data
            </summary>
            <param name="definitionService"></param>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.ModelComparisonChangeDefinition.TargetObject">
            <summary>
            Gets the target object of the change definition
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.ModelComparisonResult">
            <summary>
            SchemaModel compare result.
            Contains same elements list, elements needed to add to target list,
            elements needed to drop in target list, and changed elements list.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.ModelComparisonResult.#ctor(Microsoft.SqlServer.Dac.Deployment.ModelComparisonResultService)">
            <summary>
            Constructs a ModelComparisonResult using a <see cref="T:Microsoft.SqlServer.Dac.Deployment.ModelComparisonResultService"/>
            to obtain the result data
            </summary>
            <param name="resultService"></param>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.ModelComparisonResult.IsEqual">
            <summary>
            If compared elements are equal
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.ModelComparisonResult.ElementsEqual">
            <summary>
            List of elements that have no changes
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.ModelComparisonResult.ElementGroupsEqual">
            <summary>
            List of element groups that are equal
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.ModelComparisonResult.ElementsToAdd">
            <summary>
            List of elements exists in source, does not exist in target
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.ModelComparisonResult.ElementsToDrop">
            <summary>
            List of elements exists in target, does not exist in source
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.ModelComparisonResult.ElementsChanged">
            <summary>
            List of elements are changed.
            The result will keyed by elements in source, and it will have
            what are the changes together with that changed element.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.ModelComparisonResult.ElementsPropertyChanged">
            <summary>
            List of elements changed because of properties are changed.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.ModelComparisonResult.ElementsRelationshipEntryChanged">
            <summary>
            List of elements changed because of relationship entries are added or dropped
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.ModelComparisonResult.ElementsComposingChildrenChanged">
            <summary>
            List of elements changed because of composed children are changed.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.ModelComparisonResult.ElementsHierarchicalChildrenChanged">
            <summary>
            List of elements changed because of hierarchical dhildren are changed.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.SchemaDeploymentResources">
            <summary>
              A strongly-typed resource class, for looking up localized strings, etc.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SchemaDeploymentResources.ResourceManager">
            <summary>
              Returns the cached ResourceManager instance used by this class.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SchemaDeploymentResources.Culture">
            <summary>
              Overrides the current thread's CurrentUICulture property for all
              resource lookups using this strongly typed resource class.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SchemaDeploymentResources.DeployPlan_StepDoesNotHaveInternalMapping">
            <summary>
              Looks up a localized string similar to This step has no mapping in the internal deployment plan..
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions">
            <summary>
            Contains deployment options values used for deployment
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.AllowDropBlockingAssemblies">
            <summary>
            Get boolean that specifies whether CLR deployment will cause blocking assemblies to be dropped.
            </summary>
            <value>
            True to drop blocking assemblies during CLR deployment; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.AllowIncompatiblePlatform">
            <summary>
            Get boolean that specifies whether deployment will block due to platform compatibility.
            </summary>
            <value>
            True to block deployment to incompatible platforms; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.BackupDatabaseBeforeChanges">
            <summary>
            Get boolean that specifies whether a database backup will be performed before proceeding 
            with the actual deployment actions.
            </summary>
            <value>
            True to perform a database backup prior to deployment; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.BlockOnPossibleDataLoss">
            <summary>
            Get boolean that specifies whether deployment should stop if the operation could cause data loss.
            </summary>
            <value>
            True to stop deployment if possible data loss if detected; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.BlockWhenDriftDetected">
            <summary>
            Get boolean that specifies whether the system will check for differences between the 
            present state of the database and the registered state of the database and block deployment 
            if changes are detected.  Even if this option is set to true, drift detection will only occur 
            on a database if it was previously deployed with the <see cref="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.RegisterDataTierApplication"/> option enabled.
            </summary>
            <value>
            True to error is drift is detected; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.CommentOutSetVarDeclarations">
            <summary>
            Get boolean that specifies whether the declaration of SQLCMD variables are commented 
            out in the script header.
            </summary>
            <value>
            True to comment out these declarations; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.CompareUsingTargetCollation">
            <summary>
            Get boolean that specifies whether the source collation will be used for identifier 
            comparison.
            </summary>
            <value>
            False to use the source collation; otherwise, true to use the target collation.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.CreateNewDatabase">
            <summary>
            Get boolean that specifies whether the existing database will be dropped
            and a new database created before proceeding with the actual deployment actions.
            Acquires single-user mode before dropping the existing database.
            </summary>
            <value>
            True to drop and re-create the database; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.DeployDatabaseInSingleUserMode">
            <summary>
            Get boolean that specifies whether the system will acquire single-user mode on the target
            database during the duration of the deployment operation.
            </summary>
            <value>
            True to acquire single-user mode during deployment; otherwise, false.
            Default is false.
            </value>
            <remarks>
            The database will be returned to multi-user mode after all changes are applied.
            Database may remain in single-user mode if an error occurs during execution.
            </remarks>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.DisableAndReenableDdlTriggers">
            <summary>
            Get boolean that specifies if all DDL triggers will be disabled for the duration of the 
            deployment operation and then re-enabled after all changes are applied.  
            </summary>
            <value>
            True to disable DDL triggers during deployment; otherwise, false.
            Default is true.
            </value>
            <remarks>
            Triggers may remain disabled if an error occurs during execution.
            </remarks>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.DoNotAlterChangeDataCaptureObjects">
            <summary>
            Get boolean that specifies whether items configured for Change Data Capture (CDC)
            should be altered during deployment.  
            </summary>
            <value>
            True to not alter objects configured for CDC; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.DoNotAlterReplicatedObjects">
            <summary>
            Get boolean that specifies whether items configured for Replication
            should be altered during deployment.  
            </summary>
            <value>
            True to not alter objects configured for Replication; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.DropConstraintsNotInSource">
            <summary>
            Get boolean that specifies whether to drop all constraints that do not
            exist in the source model.  
            </summary>
            <value>
            True to drop constraints not in the source model; otherwise, false.
            Default is true.
            </value>
            <remarks>
            This applies to check, default, foreign key, primary key, and unique constraints.
            </remarks>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.DropDmlTriggersNotInSource">
            <summary>
            Get boolean that specifies whether to drop all DML triggers that do not
            exist in the source model.
            </summary>
            <value>
            True to drop DML triggers not in the source model; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.DropExtendedPropertiesNotInSource">
            <summary>
            Get boolean that specifies whether to drop all extended properties that do 
            not exist in the source model.
            </summary>
            <value>
            True to drop extended properties not in the source model; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.DropIndexesNotInSource">
            <summary>
            Get boolean that specifies whether to drop all indexes that do not
            exist in the source model.
            </summary>
            <value>
            True to drop indexes not in the source model; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.DropObjectsNotInSource">
            <summary>
            Get boolean that specifies whether objects that exist in the target but not source should be dropped during deployment.
            </summary>
            <value>
            True if objects that exist in the target but not source should be dropped; otherwise false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.DropPermissionsNotInSource">
            <summary>
            Get boolean that specifies whether to drop all permissions that do not
            exist in the source model.
            </summary>
            <value>
            True to drop permissions not in the source model; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.DropRoleMembersNotInSource">
            <summary>
            Get boolean that specifies whether to drop all role memberships that do not
            exist in the source model.
            </summary>
            <value>
            True to drop role memberships not in the source model; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.DropStatisticsNotInSource">
            <summary>
            Get boolean that specifies whether to drop all statistics that do not
            exist in the source model.
            </summary>
            <value>
            True to drop statistics not in the source model; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.GenerateSmartDefaults">
            <summary>
            Get boolean that specifies whether default values should be generated to populate NULL columns that are constrained to NOT NULL values.
            </summary>
            <value>
            True if default values should be generated; otherwise false.
            Default is false.
            </value>
            <remarks>
            This is useful when needing to add a new NOT NULL column to an existing table with data.
            </remarks>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreAnsiNulls">
            <summary>
            Get boolean that specifies whether to exclude the ANSI_NULL option from 
            consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in the ANSI_NULL option; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreAuthorizer">
            <summary>
            Get boolean that specifies whether to exclude the AUTHORIZATION option from 
            consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in the AUTHORIZATION option; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreColumnCollation">
            <summary>
            Get boolean that specifies whether to exclude the collation specifier from 
            consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in the collation specifier; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreColumnOrder">
            <summary>
            Get boolean that specifies whether to exclude from consideration 
            the order of columns in tables when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in column order; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreComments">
            <summary>
            Get boolean that specifies whether to exclude comments from 
            consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in comments; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreCryptographicProviderFilePath">
            <summary>
            Get boolean that specifies whether to exclude the file specification 
            of a cryptographic provider from consideration when comparing the source and 
            target model.
            </summary>
            <value>
            True to ignore differences in a cryptographic provider's  file specification; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreDdlTriggerOrder">
            <summary>
            Get boolean that specifies whether to exclude DDL trigger order from 
            consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in DDL trigger order; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreDdlTriggerState">
            <summary>
            Get boolean that specifies whether to exclude DDL trigger state from 
            consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in DDL trigger state; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreDefaultSchema">
            <summary>
            Get boolean that specifies whether to exclude the DEFAULT_SCHEMA option from 
            consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in the DEFAULT_SCHEMA options; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreDmlTriggerOrder">
            <summary>
            Get boolean that specifies whether to exclude DML trigger order from 
            consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in DDL trigger order; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreDmlTriggerState">
            <summary>
            Get boolean that specifies whether to exclude DML trigger state from 
            consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in DML trigger state; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreExtendedProperties">
            <summary>
            Get boolean that specifies whether to exclude all extended properties from 
            consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in extended properties; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreFileAndLogFilePath">
            <summary>
            Get boolean that specifies whether to exclude the FILENAME option of 
            FILE objects from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in the FILENAME option of FILE objects; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreFilegroupPlacement">
            <summary>
            Get boolean that specifies whether to exclude the filegroup specifier 
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in the filegroup specifier; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreFileSize">
            <summary>
            Get boolean that specifies whether to exclude the SIZE option of FILE objects 
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in the SIZE option of FILE objects; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreFillFactor">
            <summary>
            Get boolean that specifies whether to exclude the FILLFACTOR option
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in the FILLFACTOR option; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreFullTextCatalogFilePath">
            <summary>
            Get boolean that specifies whether to exclude the path specification of 
            FULLTEXT CATALOG objects from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in the path specification of FULLTEXT CATALOG objects; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreIdentitySeed">
            <summary>
            Get boolean that specifies whether to exclude the seed value of IDENTITY columns 
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in the seed value of IDENTITY columns; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreIncrement">
            <summary>
            Get boolean that specifies whether to exclude the increment value of IDENTITY columns 
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in the increment value of IDENTITY columns; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreIndexOptions">
            <summary>
            Get boolean that specifies whether to exclude differences in index options 
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in index options; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreIndexPadding">
            <summary>
            Get boolean that specifies whether to exclude the PAD_INDEX option 
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in the PAD_INDEX option; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreKeywordCasing">
            <summary>
            Get boolean that specifies whether to exclude difference in the casing of keywords 
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in the casing of keywords; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreLockHintsOnIndexes">
            <summary>
            Get boolean that specifies whether to exclude the ALLOW_ROW_LOCKS and 
            ALLOW_PAGE_LOGKS options from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore the ALLOW_ROW_LOCKS and ALLOW_PAGE_LOGKS options; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreLoginSids">
            <summary>
            Get boolean that specifies whether to exclude the SID option of the LOGIN object 
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore the SID option of the LOGIN object; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreNotForReplication">
            <summary>
            Get boolean that specifies whether to exclude the NOT FOR REPLICATION option 
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore the NOT FOR REPLICATION option; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreObjectPlacementOnPartitionScheme">
            <summary>
            Get boolean that specifies whether to exclude the partition scheme object
            from consideration when comparing the source and target model for the following
            objects: Table, Index, Unique Key, Primary Key, and Queue.
            </summary>
            <value>
            True to ignore partition schemes; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnorePartitionSchemes">
            <summary>
            Get boolean that specifies whether to exclude the parameter type and 
            boundary VALUES of a PARTITION FUNCTION from consideration when comparing the 
            source and target model.  Also excludes FILEGROUP and partition function of a 
            PARTITION SCHEMA from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore aspects of partition functions and schemes; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnorePermissions">
            <summary>
            Get boolean that specifies whether to exclude all permission statements 
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore all permission statements; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreQuotedIdentifiers">
            <summary>
            Get boolean that specifies whether to exclude the QUOTED_IDENTIFIER option 
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore the QUOTED_IDENTIFIER option; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreRoleMembership">
            <summary>
            Get boolean that specifies whether to exclude all ROLE MEMBERSHIP objects 
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore ROLE MEMBERSHIP objects; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreRouteLifetime">
            <summary>
            Get boolean that specifies whether to exclude the LIFETIME option of ROUTE objects 
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore the LIFETIME option of ROUTE objects; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreSemicolonBetweenStatements">
            <summary>
            Get boolean that specifies whether to exclude the existence or absence of semi-colons 
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore semi-colons; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreTableOptions">
            <summary>
            Get boolean that specifies whether the options on the target table are updated 
            to match the source table.
            </summary>
            <value>
            True to ignore difference in table options and not update the target table; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreUserSettingsObjects">
            <summary>
            Get boolean that specifies whether to exclude user settings 
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in user settings; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreWhitespace">
            <summary>
            Get boolean that specifies whether to exclude whitespace 
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences whitespace; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreWithNocheckOnCheckConstraints">
            <summary>
            Get boolean that specifies whether to exclude the CHECK|NO CHECK option of a CHECK 
            constraint object from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore the CHECK|NO CHECK option of a CHECK constraint object; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IgnoreWithNocheckOnForeignKeys">
            <summary>
            Get boolean that specifies whether to exclude the CHECK|NO CHECK option of a FOREIGN KEY  
            constraint object from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore the CHECK|NO CHECK option of a FOREIGN KEY constraint object; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IncludeCompositeObjects">
            <summary>
            Get boolean that specifies whether to include referenced, external elements that also 
            compose the source model and then update the target database in a single deployment operation.
            </summary>
            <value>
            True to include composite objects; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.IncludeTransactionalScripts">
            <summary>
            Get boolean that specifies whether to use transations during the deployment operation 
            and commit the transaction after all changes are successfully applied.
            </summary>
            <value>
            True to use transactions during deployment; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.NoAlterStatementsToChangeClrTypes">
            <summary>
            Get boolean that specifies whether to force a change to CLR assemblies by dropping and recreating them.
            </summary>
            <value>
            True if CLR assemblies should be dropped; otherwise false to allow ALTER statements to change CLR assemblies.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.PopulateFilesOnFileGroups">
            <summary>
            Get boolean that specifies whether files are supplied for filegroups defined in the deployment source.
            </summary>
            <value>
            True to specify files for filegroups; otherwise false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.RegisterDataTierApplication">
            <summary>
            Get boolean that specifies whether the database will be registered as a Data-Tier Application.  
            If the target database is already a registered Data-Tier Application, then the registration will be updated.
            </summary>
            <value>
            True to register the database as a Data-Tier Application; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.ScriptDatabaseCollation">
            <summary>
            Get boolean that specifies whether the target database should be altered to match the 
            source model's collation.
            </summary>
            <value>
            True to alter the target database's collation; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.ScriptDatabaseCompatibility">
            <summary>
            Get boolean that specifies whether the target database should be altered to match the 
            source model's compatibility level.
            </summary>
            <value>
            True to alter the target database's compatibility level; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.ScriptDatabaseOptions">
            <summary>
            Get boolean that specifies whether the database options in the target database should 
            be updated to match the source model.
            </summary>
            <value>
            True to alter the target database's options; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.ScriptDeployStateChecks">
            <summary>
            Get boolean that specifies whether the target database should be checked to ensure that 
            it exists, is online and can be updated.
            </summary>
            <value>
            True to perform state checks on the target database; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.ScriptFileSize">
            <summary>
            Get boolean that specifies whether a file size is specified when adding files to file groups.
            </summary>
            <value>
            True to specify a file size when adding files to file groups; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.ScriptNewConstraintValidation">
            <summary>
            Get boolean that specifies whether constraints are validated after all changes are applied.
            </summary>
            <value>
            True to validate check constraints; otherwise, false.
            Default is true.
            </value>
            <remarks>
            Constraints are always added with NOCHECK option; as a result their validation is skipped during creation.
            </remarks>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.ScriptRefreshModule">
            <summary>
            Get boolean that specifies whether referencing procedures are refreshed when referenced objects are updated.
            </summary>
            <value>
            True to refresh referencing procedures; otherwise false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.TargetConnectionString">
            <summary>
            Get string that specifies the target connection string
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.TargetDatabaseExists">
            <summary>
            Gets boolean that if set specifies whether the target database exists
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.TargetDatabaseName">
            <summary>
            Gets string that specifies the target database name
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.TargetingServerless">
            <summary>
            Get boolean that specifies whether the target server is LocalDB
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.TreatVerificationErrorsAsWarnings">
            <summary>
            Get boolean that specifies whether to treat errors that occur during publish verification as warnings. 
            The check is performed against the generated deployment plan before the plan is executed against the target database. 
            Plan verification detects problems, such as the loss of target-only objects (for example, indexes), that must be 
            dropped to make a change. Verification also detects situations where dependencies (such as tables or views) exist 
            because of a reference to a composite project, but do not exist in the target database. You might choose to treat 
            verification errors as warnings to get a complete list of issues instead of allowing the publish 
            action to stop when the first error occurs.
            </summary>
            <value>
            True to treat errors as warnings; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.UnmodifiableObjectWarnings">
            <summary>
            Get boolean that specifies whether warnings should be generated when differences are found 
            in objects that cannot be modified, for example, if the file size or file paths were different for a file.
            </summary>
            <value>
            True to generate warnings; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.VerifyCollationCompatibility">
            <summary>
            Get boolean that specifies whether deployment will verify that the collation specified in the 
            source model is compatible with the collation specified in the target model.
            </summary>
            <value>
            True to continue if errors are generated during plan verification; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.VerifyDeployment">
            <summary>
            Get boolean that specifies whether the plan verification phase is executed or not.
            </summary>
            <value>
            True to perform plan verification; otherwise, false to skip it.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.SqlDeploymentOptions.SqlCommandVariableValues">
            <summary>
            Get dictionary of SQL command variable values, keyed by variable name.
            </summary>
            <value>
            Dictionary of SQL command variable values, keyed by variable name.
            </value>
            <remarks>
            Valid values must be provided for every variable before deployment, or failures may occur during deployment.
            </remarks>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.DacExternalQueryScopes">
            <summary>
            Defines the type of objects to query from the model.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DacExternalQueryScopes.None">
            <summary>
            Filter out all elements.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DacExternalQueryScopes.UserDefined">
            <summary>
            Query user defined objects.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DacExternalQueryScopes.BuiltIn">
            <summary>
            Query built in system objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DacExternalQueryScopes.SameDatabase">
            <summary>
            Query user defined objects from referenced models where the referenced model is for the same database.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DacExternalQueryScopes.System">
            <summary>
            Query System objects that are not treated as built in. These are objects that appear when the master
            database is referenced
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DacExternalQueryScopes.DifferentDatabaseSameServer">
            <summary>
            Query references to external objects that are from a different database on the same server.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DacExternalQueryScopes.DifferentDatabaseDifferentServer">
            <summary>
            Query references to external objects that are from a different database on a different same server.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DacExternalQueryScopes.Default">
            <summary>
            Default query scope is to query both <see cref="F:Microsoft.SqlServer.Dac.Model.DacExternalQueryScopes.UserDefined"/>
            and <see cref="F:Microsoft.SqlServer.Dac.Model.DacExternalQueryScopes.BuiltIn"/> query scopes
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DacExternalQueryScopes.All">
            <summary>
            All elements in the model, including references to external elements.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.DacModelError">
            <summary>
            Represents a model blocking error.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelError.ErrorType">
            <summary>
            Type of model blocking error
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelError.ErrorCode">
            <summary>
            DacModelError error code
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelError.Line">
            <summary>
            Line Number of the error
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelError.Column">
            <summary>
            Column Number of the error
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelError.Prefix">
            <summary>
            DacModelError prefix
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelError.Message">
            <summary>
            Message from DacModelError
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelError.SourceName">
            <summary>
            The TSqlObject with error. 
            Can be null if the object creation failed completely.
            Could be a partially constructed object in case of partial failures in object creation.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelError.SourceType">
            <summary>
            Type of the TSqlObject associated with the error
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelError.Severity">
            <summary>
            Severity of the error
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.DisplayServices">
            <summary>
            Provides a set of services for providing user-visible values for objects in the public model API.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.DisplayServices.GetEscapedNameString(System.String)">
            <summary>
            Pass in a short name string, return back an escaped version of the name string.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.DisplayServices.GetElementName(Microsoft.SqlServer.Dac.Model.TSqlObject,Microsoft.SqlServer.Dac.Model.ElementNameStyle)">
            <summary>
            Get element name string using different display style.
            </summary>
            <param name="element"><see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> </param>
            <param name="style"></param>
            <returns></returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.DisplayServices.GetDisplayName(Microsoft.SqlServer.Dac.Model.ObjectIdentifier,Microsoft.SqlServer.Dac.Model.EscapeStyle,System.Boolean)">
            <summary>
            Get display name of an identifier, with the option to configure use of full/short name and whether to escape name parts.
            </summary>
            <param name="identifier">The name identifier that can contain multiple parts.</param>
            <param name="escapeStyle"><see cref="T:Microsoft.SqlServer.Dac.Model.EscapeStyle"/> defining whether the parts of the name should be escaped or not</param>
            <param name="fullName">Indicates if the name is constructed in full or only the significant last part is used.</param>
            <returns>Display name</returns>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ElementNameStyle">
            <summary>
            Defines the different naming styles supported by the display services
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ElementNameStyle.Unknown">
            <summary>
            Unknown or undefined style - should be avoided
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ElementNameStyle.SimpleName">
            <summary>
            A non-escaped short name style.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ElementNameStyle.EscapedSimpleName">
            <summary>
            An escaped short name style.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ElementNameStyle.FullyQualifiedName">
            <summary>
            A non-escaped fully qualified name style.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ElementNameStyle.EscapedFullyQualifiedName">
            <summary>
            An escaped fully qualified name style.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.EscapeStyle">
            <summary>
            Enum to specify escape style for displaying model name
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EscapeStyle.Escape">
            <summary>
            Names should be escaped
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EscapeStyle.DontEscape">
            <summary>
            Names should not be escaped
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EscapeStyle.EscapeIfNecessary">
            <summary>
            Names should be escaped if not doing so would cause errors
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ElementWithCommentedScriptBody`1">
            <summary>
            Base class for elements with commented out script body
            Parses the given script with all known prior platform parsers until the script is successfully parsed.
            On successful parssing, extracts the TSqlStatment specified by the type parameter T
            </summary>
            <typeparam name="T">The create sql statement</typeparam>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ElementWithCommentedScriptBody`1.GetParsedFragment">
            <summary>
            Parse the script with all known prior parsers
            </summary>
            <returns>Successfully parsed TSqlFragment parsed. null if not able to parse</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ElementWithCommentedScriptBody`1.ExtractSingleTSqlStatement">
            <summary>
            Extract the single TSQLStatment contained within the successfully parsed script.
            </summary>
            <returns>Single TSQLStatment within the successfully parsed script.</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ElementWithCommentedScriptBody`1.GetTSqlScriptWithCommentedScriptBody">
            <summary>
            Implemented by inherited members to comment out the script body of the Statement
            </summary>
            <returns>TSqlScipt of the element with commented out script body</returns>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ElementWithCommentedScriptBody`1.SqlPlatform">
            <summary>
            The SqlPlatform parser to first employ when trying to parse the script
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ElementWithCommentedScriptBody`1.ParsedFragment">
            <summary>
            Contains the successfully parsed TSqlFragment parsed by some prior platform parser.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ElementWithCommentedScriptBody`1.Statement">
            <summary>
            The single TSQLStatment contained within the script that was successfully parsed by some prior platform parser.
            Unpon construction, null value for Statement denotes either script was not successfully parsed or there were more than one statements.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ModelAnnotationPropertyContext">
            <summary>
            Property context for properties that are converted from annotations in the internal model.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ExternalDefinitionMetadataContext">
            <summary>
            <see cref="T:Microsoft.SqlServer.Dac.Model.ModelMetadataContext"/> implementation that handles external definition actions. These
            define a mapping from an internal <see cref="T:Microsoft.Data.Tools.Schema.SchemaModel.ModelElementClass"/> to a value in a public Enum type.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ElementDescriptor">
            <summary>
            Describes a SQL model element without instantiating a <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> in the
            <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/>.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ElementDescriptor.GetModelElement(Microsoft.SqlServer.Dac.Model.TSqlModel)">
            <summary>
            Gets the corresponding <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> that matches this descriptor from a model.
            If no object matches the descriptor then null will be returned
            </summary>
            <param name="model"><see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/> the object should be found in</param>
            <returns>
            <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> that this <see cref="T:Microsoft.SqlServer.Dac.Model.ElementDescriptor"/> maps to, or null
            if no object definitively matches this descriptor
            </returns>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ElementDescriptor.IsEmpty">
            <summary>
            Returns if descriptor is empty (doesn't contain any identifiers).  It is possible for
            a descriptor to have a generated name - i.e. based on the context of a statement
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ElementDescriptor.HasUsableName">
            <summary>
            Helper to signify whether this descriptor has a name that is usable - i.e. the
            name parts represent the name of a SQL object.  
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ElementDescriptor.IgnoreName">
            <summary>
            Gets if name should be ignored for this descriptor.  It is possible for a
            descriptor to have no name AND IgnoreName == false
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ElementDescriptor.TypeClass">
            <summary>
            Returns back the type class that maps to the ElementType.  
            
            If there is no public mapping for the internal type then null will be returned.
            
            If the internal type maps to more than one element class the first element class
            is returned.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ElementDescriptor.Identifiers">
            <summary>
            The parts of the elements name.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ElementDescriptor.ExternalReferenceParts">
            <summary>
            Returns external parts for elements name
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ElementDescriptorRelevance">
            <summary>
            Specifies how a fragment being visited is related to a descriptor
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ElementDescriptorRelevance.None">
            <summary>
            No relevance or unknown relevance
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ElementDescriptorRelevance.ContainerId">
            <summary>
            The descriptor is related to an object that contains the fragment 
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ElementDescriptorRelevance.SelfId">
            <summary>
            The descriptor represents the same concept as the fragment 
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.CreateProcedureStatementWithCommentedScriptBody">
            <summary>
            Represents substituting a CreateProcedureStatement with commented out script body
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.CreateViewStatementWithCommentedScriptBody">
            <summary>
            Represents substituting a CreateViewStatement with commented out script body
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ModelCollationComparer">
            <summary>
            Comparer that can compare strings and <see cref="T:Microsoft.SqlServer.Dac.Model.ObjectIdentifier"/>s using the collation of a <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/>.
            This can be very useful when comparing objects in the model since comparisons will be consistent with the expected comparison
            results in SQL Server
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ModelCollationComparer.Equals(System.String,System.String)">
            <summary>
            Compare if two objects are equal.
            </summary>
            <param name="x">Source object.</param>
            <param name="y">Target object.</param>
            <returns>True if equal, otherwise false.</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ModelCollationComparer.Equals(System.Collections.Generic.IList{System.String},System.Collections.Generic.IList{System.String})">
            <summary>
            Test if two identifiers are equal using collation of this comparer.
            </summary>
            <param name="x">List of name parts in the first identifier.</param>
            <param name="y">List of name parts in the second identifier.</param>
            <returns></returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ModelCollationComparer.Equals(Microsoft.SqlServer.Dac.Model.ObjectIdentifier,Microsoft.SqlServer.Dac.Model.ObjectIdentifier)">
            <summary>
            Test if two identifiers are equal using collation of this comparer.
            </summary>
            <param name="x">The first <see cref="T:Microsoft.SqlServer.Dac.Model.ObjectIdentifier"/>.</param>
            <param name="y">The second <see cref="T:Microsoft.SqlServer.Dac.Model.ObjectIdentifier"/>.</param>
            <returns></returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ModelCollationComparer.GetHashCode(Microsoft.SqlServer.Dac.Model.ObjectIdentifier)">
            <summary>
            Gets a Hashcode for the object that's compatible with the 
            <see cref="M:Microsoft.SqlServer.Dac.Model.ModelCollationComparer.Equals(Microsoft.SqlServer.Dac.Model.ObjectIdentifier,Microsoft.SqlServer.Dac.Model.ObjectIdentifier)"/> method of this comparer
            </summary>
            <param name="obj">The object to get a hashcode for</param>
            <returns>hashcode</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ModelCollationComparer.GetHashCode(System.Collections.Generic.IList{System.String})">
            <summary>
            Gets a Hashcode for the object that's compatible with the 
            <see cref="M:Microsoft.SqlServer.Dac.Model.ModelCollationComparer.Equals(System.Collections.Generic.IList{System.String},System.Collections.Generic.IList{System.String})"/> method of this comparer
            </summary>
            <param name="obj">The object to get a hashcode for</param>
            <returns>hashcode</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ModelCollationComparer.GetHashCode(System.String)">
            <summary>
            Gets a Hashcode for the object that's compatible with the 
            <see cref="M:Microsoft.SqlServer.Dac.Model.ModelCollationComparer.Equals(System.String,System.String)"/> method of this comparer
            </summary>
            <param name="obj">The object to get a hashcode for</param>
            <returns>hashcode</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ModelCollationComparer.Compare(System.String,System.String)">
            <summary>
            Compare two objects using collation of this comparer.
            </summary>
            <param name="x">Source object.</param>
            <param name="y">Target object.</param>
            <returns></returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ModelCollationComparer.Compare(System.Collections.Generic.IList{System.String},System.Collections.Generic.IList{System.String})">
            <summary>
            Compare two objects using collation of this comparer.
            </summary>
            <param name="x">Source object.</param>
            <param name="y">Target object.</param>
            <returns></returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ModelCollationComparer.Compare(Microsoft.SqlServer.Dac.Model.ObjectIdentifier,Microsoft.SqlServer.Dac.Model.ObjectIdentifier)">
            <summary>
            Compare two objects using collation of this comparer.
            </summary>
            <param name="x">Source object.</param>
            <param name="y">Target object.</param>
            <returns></returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.InternalModelUtils.IsInlineConstraintOrIndex(Microsoft.Data.Tools.Schema.SchemaModel.IModelElement)">
            <summary>
            Checks if an internal element represents an inline constraint or annotation
            </summary>
            <param name="element">The element to check</param>
            <returns>True if the element is an inline constraint or index. False otherwise.</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.InternalModelUtils.TryGetSingleReferencedElementInfo(Microsoft.Data.Tools.Schema.SchemaModel.ModelRelationshipClass,Microsoft.Data.Tools.Schema.SchemaModel.IModelElement,Microsoft.Data.Tools.Schema.SchemaModel.IModelElement@,Microsoft.Data.Tools.Schema.SchemaModel.ModelIdentifier@)">
            <summary>
            Tries to get info from a relationship on an element. The relationship must have single cardinality.
            If successful the information returned will at a minimum be the <see cref="T:Microsoft.Data.Tools.Schema.SchemaModel.ModelIdentifier"/>
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.PotentialElementDescriptor">
            <summary>
            This class represents an ambigous element descriptor, where it's not fully clear which
            element in the model a descriptor might map to
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PotentialElementDescriptor.Relevance">
            <summary>
            Gets the Relevance associated with the potential descriptor
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.SchemaAnalysisService">
            <summary>
            Internal service that links the public <see cref="T:Microsoft.SqlServer.Dac.Model.SchemaAnalyzer"/> code to the internal
            analyzer codebase
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SchemaAnalysisService.CreateAnalysisService(Microsoft.SqlServer.Dac.Model.SchemaAnalyzer)">
            <summary>
            Factory method to construct a service. May want to move this into the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObjectService"/>
            if we ever wanted to abstract away implementation details even further
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.SimpleModelRelationshipContext">
            <summary>
            Default Property context for all properties.
            </summary>
            <remarks>
            Provides default implementation for relationship access through the public model.
            </remarks>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SimpleModelRelationshipContext.CollapsedRelationshipClasses">
            <summary>
            The possible relationships for the collapsed reference lookup
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.DacModelMessage">
            <summary>
            Represents an error or a warning raised during model validation.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.DacModelResources">
            <summary>
              A strongly-typed resource class, for looking up localized strings, etc.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.ResourceManager">
            <summary>
              Returns the cached ResourceManager instance used by this class.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.Culture">
            <summary>
              Overrides the current thread's CurrentUICulture property for all
              resource lookups using this strongly typed resource class.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.AddObjectsErrorAddingObjects">
            <summary>
              Looks up a localized string similar to Add or update objects failed due to the following errors: .
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.AddObjectsModelErrorsExist">
            <summary>
              Looks up a localized string similar to Cannot add or update objects. The model has build blocking errors:.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.AstNotRetrieved">
            <summary>
              Looks up a localized string similar to The AST could not be retrieved for the specified object..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.AstNotSupportedError">
            <summary>
              Looks up a localized string similar to AST retrieval not supported for the specified object..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.AutoGeneratedCommentedScriptBodyText">
            <summary>
              Looks up a localized string similar to \nAuto generated text: The original script body has been commented in order to add this object to the model.\n.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.ConvertObject_SourceNameDifferentFromObjectSource">
            <summary>
              Looks up a localized string similar to Cannot convert object to script. The source name &apos;{0}&apos; does not match existing source name &apos;{1}&apos;. Changing source names is not supported..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.ConvertObject_SourceNameExists">
            <summary>
              Looks up a localized string similar to Cannot convert object to script. The source name &apos;{0}&apos; is already in use in this model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.ConvertObject_WhitespaceNotAllowed">
            <summary>
              Looks up a localized string similar to Source name cannot consist of whitespace.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.DacpacLoadReferenceError">
            <summary>
              Looks up a localized string similar to Cannot load package &apos;{0}&apos;. Required references are missing..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.DeleteObjectsErrorDeletingObjects">
            <summary>
              Looks up a localized string similar to Delete objects failed due to the following errors: .
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.DeleteObjectsModelErrorsExist">
            <summary>
              Looks up a localized string similar to Cannot delete objects from the model. The model has build blocking errors:.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.ErrorScriptNameValidation">
            <summary>
              Looks up a localized string similar to {0}: parameter {1} cannot be null, the empty string, all whitespace, or end in &quot;.xsd&quot;.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.GetObjectsTopLevelTypesRequiredError">
            <summary>
              Looks up a localized string similar to Type &apos;{0}&apos; is not a top-level type. Only top-level types can be queried for using the TSqlModel GetObject(s) methods..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.InconsistentPropertyTypes">
            <summary>
              Looks up a localized string similar to Not all property types match expected type &apos;{0}&apos;..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.InternalPropertyMappingInvalid">
            <summary>
              Looks up a localized string similar to Internal Error. No internal property mapped on internal type {0} for public property {1}..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.InternalPropertyTypeMismatchErrorMessage">
            <summary>
              Looks up a localized string similar to Internal Error. Data types for internal properties &apos;{0}.{1}&apos; and &apos;{2}.{3}&apos; do not match..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.InternalRelationshipMappingInvalid">
            <summary>
              Looks up a localized string similar to No internal relationship mapped on internal type {0} for public relationship {1}..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.InternalTypeMappingInvalid">
            <summary>
              Looks up a localized string similar to Internal Error. No public type mapping was found for internal type {0}, for element with id {1}..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.InvalidContextObjectErrorMessage">
            <summary>
              Looks up a localized string similar to Internal Error. Context object must implement ISqlModelElement..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.InvalidContextObjectForDefaultErrorMessage">
            <summary>
              Looks up a localized string similar to Internal Error. There is no context object to derive a default from..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.InvalidMetadataContextErrorMessage">
            <summary>
              Looks up a localized string similar to Metadata {0} has an invalid context type..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.InvalidMetadataTypeErrorMessage">
            <summary>
              Looks up a localized string similar to Data type {1} for metadata {0} cannot be cast to {2}..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.InvalidModelElementErrorMessage">
            <summary>
              Looks up a localized string similar to Internal Error. Internal elements must implement IModelElement..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.InvalidNavigationPathRelationship">
            <summary>
              Looks up a localized string similar to Relationship &apos;{0}.{1}&apos; cannot be used for type collapsing navigation as it is a one-to-many relationship..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.InvalidPropertyContextErrorMessage">
            <summary>
              Looks up a localized string similar to Property {0} has an invalid context type..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.InvalidPropertyTypeErrorMessage">
            <summary>
              Looks up a localized string similar to Data type {1} for property {0} cannot be cast to {2}..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.InvalidRelationshipContextErrorMessage">
            <summary>
              Looks up a localized string similar to Relationship {0} has an invalid context type..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.InvalidTypeErrorMessage">
            <summary>
              Looks up a localized string similar to Data type {0} cannot be cast to {1}..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.InvalidTypeNameErrorMessage">
            <summary>
              Looks up a localized string similar to The type name {0} is not valid..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.LoadScriptBackedModelFailed">
            <summary>
              Looks up a localized string similar to Loading source as a script-backed model failed due to the following errors:.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.MetadataDoesNotMapEnumErrorMessage">
            <summary>
              Looks up a localized string similar to Internal error. Type &apos;{0}&apos; is not an Enum, expected Enum for GetMetadata.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.MetadataNotSupportedOnTypeErrorMessage">
            <summary>
              Looks up a localized string similar to ModelMetadataClass {0} is not supported on type {1}..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.MissingMetadataMappingErrorMessage">
            <summary>
              Looks up a localized string similar to Metadata context must be mapped to at least one internal property..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.MissingPropertyMappingErrorMessage">
            <summary>
              Looks up a localized string similar to Internal Error. Property context must be mapped to at least one internal property..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.MissingReference">
            <summary>
              Looks up a localized string similar to No file was supplied for reference {0}; model load might fail. When {1} was created, the original referenced file was located {2}..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.MissingReference_NoPackagePath">
            <summary>
              Looks up a localized string similar to No file was supplied for reference {0}; model load might fail. When package was created, the original referenced file was located {1}..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.MissingRelationshipMappingErrorMessage">
            <summary>
              Looks up a localized string similar to Context must include at least one internal relationship mapping.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.ModelDisposedError">
            <summary>
              Looks up a localized string similar to The underlying model has been disposed..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.ModelErrorsExist">
            <summary>
              Looks up a localized string similar to The model has build blocking errors:.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.MoreThanOneObjectMatchedId">
            <summary>
              Looks up a localized string similar to More than one object matched the ID &apos;{0}&apos;..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.NoValidEnumForMetadata">
            <summary>
              Looks up a localized string similar to Type &apos;{0}&apos; does not have a mapping for the int value &apos;{1}&apos;.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.PropertiesNotSupportedForRelationshipError">
            <summary>
              Looks up a localized string similar to Properties are not supported on ModelRelationshipInstances for Relationship {0}.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.PropertyNotSupportedOnRelationshipErrorMessage">
            <summary>
              Looks up a localized string similar to ModelPropertyClass {0} is not supported on relationship {1}..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.PropertyNotSupportedOnTypeErrorMessage">
            <summary>
              Looks up a localized string similar to ModelPropertyClass {0} is not supported on type {1}..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.PublicContributorThrewExceptionMessage">
            <summary>
              Looks up a localized string similar to DeploymentContributor &apos;{0}&apos; threw exception. Message is: &apos;{1}&apos;.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.RelationshipNotSupportedOnTypeErrorMessage">
            <summary>
              Looks up a localized string similar to ModelRelationshipClass {0} is not supported on type {1}..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.SaveModelErrorsExist">
            <summary>
              Looks up a localized string similar to Cannot save package to file. The model has build blocking errors:.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.ScriptNotRetrieved">
            <summary>
              Looks up a localized string similar to The script could not be retrieved for the specified object..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.ScriptNotSupportedError">
            <summary>
              Looks up a localized string similar to Script retrieval not supported for the specified object..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.ServiceDisposedError">
            <summary>
              Looks up a localized string similar to The underlying model service has been disposed..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.UpdateModelErr">
            <summary>
              Looks up a localized string similar to Error updating model..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.UpdateModelErr_PackageContainsData">
            <summary>
              Looks up a localized string similar to Cannot update the model as the package contains data. This is not supported since it could cause errors during deployment.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DacModelResources.UpdateModelErr_UnsupportedDacpacVersion">
            <summary>
              Looks up a localized string similar to Updating package with version &apos;{0}&apos; is not supported. Only packages with version 3.0 or higher are supported for update..
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.DacQueryScopes">
            <summary>
            Defines the type of objects to query from the model.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DacQueryScopes.None">
            <summary>
            Filter out all elements.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DacQueryScopes.UserDefined">
            <summary>
            Query user defined objects.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DacQueryScopes.BuiltIn">
            <summary>
            Query built in system objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DacQueryScopes.SameDatabase">
            <summary>
            Query user defined objects from referenced models where the referenced model is for the same database.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DacQueryScopes.System">
            <summary>
            Query System objects that are not treated as built in. These are objects that appear when the master
            database is referenced
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DacQueryScopes.Default">
            <summary>
            Default query scope is to query both <see cref="F:Microsoft.SqlServer.Dac.Model.DacQueryScopes.UserDefined"/>
            and <see cref="F:Microsoft.SqlServer.Dac.Model.DacQueryScopes.BuiltIn"/> query scopes
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DacQueryScopes.All">
            <summary>
            All elements in the model, not including references to external elements in non-system databases.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SqlElementDescriptor.Equals(System.Object)">
            <summary>
            <see cref="M:System.Object.Equals(System.Object)"/>
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SqlElementDescriptor.GetHashCode">
            <summary>
            <see cref="M:System.Object.GetHashCode"/>
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.SqlModelCollationComparer">
            <summary>
            Internal SQL implementation of the <see cref="T:Microsoft.SqlServer.Dac.Model.ModelCollationComparer"/>. Uses the
            comparer built into an internal model for the comparison
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.SqlSchemaModelLoader">
            <summary>
            Responsible for loading a model from a dacpac
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.SqlSchemaModelLoader.ModelHeaderDataLoader">
            <summary>
            Loads references, SqlCmd variables, and refactor log data from a dacpac
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObjectService.ConvertToScriptedModel(System.Threading.CancellationToken)">
            <summary>
            Converts objects in a given scope into scriptable objects by deleting the internal representation of
            user defined objects and recreating them with a script-backed representation
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObjectService.ConvertToScriptedObject(Microsoft.SqlServer.Dac.Model.TSqlObject,System.String)">
            <summary>
            Converts a specific top-level object in the model into a scripted representation of itself. This is done
            by scripting out the element, then dropping this element, and finally adding it back in and resolving the model
            </summary>
            <returns>New copy of the TSqlObject, which will now be script-backed</returns>
            <exception cref="T:Microsoft.SqlServer.Dac.Model.DacModelException">If the object could not be scripted out, this class of object is never scriptable,
            or errors were found in the model during the script process </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.#ctor(Microsoft.SqlServer.Dac.Model.TSqlModelSchema,Microsoft.Data.Tools.Schema.SchemaModel.DataSchemaModel)">
            <summary>
            Internal for testing purposes only. Use SqlSchemaModelObjectService(InternalModel.DataSchemaModel model)for
            product code.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.#ctor(Microsoft.Data.Tools.Schema.SchemaModel.DataSchemaModel)">
            <summary>
            Public constructor to be used by product code
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.CreateParserRepository">
            <summary>
            Initialize TSqlParserRepository
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.SetScriptBacked(System.Boolean)">
            <summary>
            Internal method that enables setting whether the model is script-backed or not. This can be used to
            warn about code analysis running against a non-scripted model, for instance.
            </summary>
            <param name="isScriptBacked"></param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.TryGetObject(Microsoft.Data.Tools.Schema.SchemaModel.IModelElement,Microsoft.SqlServer.Dac.Model.TSqlObject@)">
            <summary>
            Internal utility method - wraps an internal model element in a public TSqlObject. Should never be exposed as part of the service.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.GetObjects(System.String,Microsoft.SqlServer.Dac.Model.ModelTypeClass[])">
            <summary>
            Retrieves all TSqlObjects in a given source document filtered by ModelTypeClass specified by the types parameter
            </summary>
            <param name="sourceName">Name of the source document</param>
            <param name="types">Array of ModelTypeClass that the resulting TSqlObject should be filtered on. 
            If the parameter is a empty list, all objects will be returned.</param>
            <returns>List of TSqlObjects</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.GetModelErrors">
            <summary>
            Retrieves all model errors.
            </summary>
            <returns>List of model errors</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.GetModelIdForNamedObjectId(Microsoft.SqlServer.Dac.Model.ObjectIdentifier)">
            <summary>
            Gets the internal that matches a public ID
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.GetAsValidIModelElement(System.Object)">
            <summary>
            Converts an object to an IModelElement, throwing if the cast is invalid
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.IsToplevelObject(Microsoft.SqlServer.Dac.Model.TSqlObject)">
            <summary>
            Returns if an object's create script is a top level T-SQL statement.
            </summary>
            <param name="tSqlObject"><see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> to query.</param>
            <returns>True if object's create statement is a top level statement.</returns>
            <exception cref="T:System.ArgumentNullException">If <paramref name="tSqlObject"/> is null.</exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.TryGetAst(Microsoft.SqlServer.Dac.Model.TSqlObject,Microsoft.SqlServer.TransactSql.ScriptDom.TSqlScript@)">
            <summary>
            Generates the AST for the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>.
            </summary>
            <returns>True is AST generated.</returns>
            <param name="ast">Generated AST.</param>
            <param name="tSqlObject"><see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> to query.</param>
            <exception cref="T:System.ArgumentNullException">Thrown if <paramref name="tSqlObject"/> is null.</exception>
            <exception cref="T:Microsoft.SqlServer.Dac.Model.DacModelException">Thrown if the <see cref="P:Microsoft.SqlServer.Dac.Model.TSqlObject.ContextObject"/> property of <paramref name="tSqlObject"/> does not implement correct type.</exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.TryGetScript(Microsoft.SqlServer.Dac.Model.TSqlObject,System.String@)">
            <summary>
            Generates the script for the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>.
            </summary>
            <returns>True is script generated.</returns>
            <param name="tSqlObject">The <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> to be scripted</param>
            <param name="script">Generated script.</param>
            <exception cref="T:System.ArgumentNullException">Thrown if <paramref name="tSqlObject"/> is null.</exception>
            <exception cref="T:Microsoft.SqlServer.Dac.Model.DacModelException">Thrown if the <see cref="P:Microsoft.SqlServer.Dac.Model.TSqlObject.ContextObject"/> property of <paramref name="tSqlObject"/> does not implement correct type.</exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.CreateCacheIdentifier(System.String)">
            <summary>
            Creates an identifier that is compatible with the internal model's treatment of source names.
            Internally the ScriptCache checks for "MSSQLL::" at the beginning of a source name. 
            
            This indicates that the name isn't a path and shouldn't be auto-expanded based on the current working directory.
            We don't want to treat source names passed into the public API as paths, especially since this would result in 
            the current working directory being added in front of any source name that wasn't a fully qualified path. 
            
            The tradeoff is external users of the API don't get the benefit of us auto-resolving relative paths for them,
            but that's an acceptable tradeoff
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.GetSourceNameFromCacheId(System.String)">
            <summary>
            Removes the "MSSQL::" string from the beginning of a cache identifier to get the source name.
            see <see cref="M:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.CreateCacheIdentifier(System.String)"/> for more information on why this behavior is needed
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.ExecutePackageOperationWithErrorHandling(System.Action)">
            <summary>
            Performs error handling around an operation on a DacPackage, such as creating or updating
            the package. There are a standard set of exceptions that can be thrown in this case
            </summary>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">Thrown if any error occurs when operating on the package</exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.GetSourceInformationInternal(Microsoft.Data.Tools.Schema.SchemaModel.ISourceInformation)">
            <summary>
            Gets source info from an internal source by stripping any cache ID info from the source name
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.ConvertToScriptedObject(Microsoft.SqlServer.Dac.Model.TSqlObject,System.String)">
            <summary>
            Converts a specific top-level object in the model into a scripted representation of itself. This is done
            by scripting out the element, then dropping this element, and finally adding it back in and resolving the model
            </summary>
            <returns>New copy of the TSqlObject, which will now be script-backed</returns>
            <exception cref="T:Microsoft.SqlServer.Dac.Model.DacModelException">If the object could not be scripted out, this class of object is never scriptable,
            or errors were found in the model during the script process </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.ConvertToScriptedObject(Microsoft.SqlServer.Dac.Model.TSqlObject,System.String,System.String)">
            <summary>
            For simplicity, keeping this method private for now. Consider exposing publicly as a perf improvement in scenarios where 
            caller clearly knows that they want to directly replace an unscripted model element (e.g. a table) with a new definition.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.ConvertToScriptedModel(System.Threading.CancellationToken)">
            <summary>
            Converts all user-defined objects in the model into scripted representations of themselves. This is done
            by scripting out the elements we're going to drop, then dropping all these elements, and finally adding them
            back in and resolving the model
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.CollectObjectsToBeRecreated(System.Collections.Generic.Dictionary{System.String,System.Tuple{Microsoft.SqlServer.Dac.Model.TSqlObject,System.String}},System.Threading.CancellationToken)">
            <summary>
            One step in converting objects to a scripted model: Must create script representations of each object to be deleted, 
            since we would like to remove them all and re-add them with only one Resolve operation. Otherwise we would have to 
            resolve after each delete and recreation to avoid unresolved references causing problems during scripting
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.DropAndRecreateElements(System.Collections.Generic.Dictionary{System.String,System.Tuple{Microsoft.SqlServer.Dac.Model.TSqlObject,System.String}},System.Threading.CancellationToken)">
            <summary>
            One step in converting objects to a scripted model: 
            for each of the objects to be dropped and recreated the element, its composed children and inline hierarchical 
            children are all deleted. Then they are added back into the model via the scripted representation of the objects.
            
            Note that this does not call Resolve on the model in order to support multiple updates followed by a single
            resolve, hence the caller must ensure the model is resolved once all these operations are completed.  
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.DoAddOrUpdateSubstitutedObject(Microsoft.Data.Tools.Schema.Sql.SchemaModel.ISqlModelElement,System.String,Microsoft.SqlServer.Dac.Model.TSqlObjectOptions,System.String,System.String)">
            <summary>
            If adding an object to the model fails due to model blocking errors and if the ModelLoadOption is set to not throw on errors,
            the original object is substituted with a replacement object whose errant parts are removed. These errors are common in case of objects with
            old deprecated syntax. 
            This method substitutes stored procedures and views with equivalent new objects that has commented out script body.
            In case of other types of objects, an empty element with just the name of the errant object is added to the model.
            </summary>
            <param name="element">The element that originally failed to add to the model.</param>
            <param name="sourceName">source name of the object</param>
            <param name="options">TSqlObjectOptions for the object</param>
            <param name="script">Script of the object</param>
            <param name="cacheIdentifier">cache identifier for the object</param>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.ModelBuilder">
            <summary>
            Internal for testing purposes only
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.DataSchemaModel">
            <summary>
            Internal, only to be accessed by internal services as required.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.ScopeFilter._includeAll">
            <summary>
            If query scope includes all elements, even externals
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.ScopeFilter._includeAllNonExternal">
            <summary>
            If query scope includes all elements, except
            non-Composite external
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.ScopeFilter._includeUserDefined">
            <summary>
            If query scope includes user defined elements
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.ScopeFilter._includeBuiltIn">
            <summary>
            if query scope includes built-in elements
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.ScopeFilter._includeSameDatabase">
            <summary>
            If query scope includes external elements in the same database
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.ScopeFilter._includeSystem">
            <summary>
            If query scope includes elements from system dacpac ("master" reference)
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.ScopeFilter._includeDifferentDatabaseSameServer">
            <summary>
            If query scope includes external elements in a different database on this server (for return of ID only)
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.ScopeFilter._includeDifferentDatabaseDifferentServer">
            <summary>
            If query scope includes external elements in a different database on a separate server (for return of ID only)
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.ScopeFilter.SetupScopeVariables(Microsoft.SqlServer.Dac.Model.DacQueryScopes)">
            <summary>
            Converts <see cref="T:Microsoft.SqlServer.Dac.Model.DacQueryScopes"/> into filter criteria to use when querying the internal model.
            </summary>
            <param name="queryScopes">QueryScope to expand.</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.ScopeFilter.SetupScopeVariables(Microsoft.SqlServer.Dac.Model.DacExternalQueryScopes)">
            <summary>
            Converts <see cref="T:Microsoft.SqlServer.Dac.Model.DacExternalQueryScopes"/> into filter criteria to use when querying the internal model.
            </summary>
            <param name="queryScopes">QueryScope to expand.</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.ScopeFilter.ShouldReturnElement(Microsoft.Data.Tools.Schema.SchemaModel.IModelElement)">
            <summary>
            Determines if the element matches the query criteria. Fully external elements 
            should never be returned from this method, though system elements may be supported.
            </summary>
            <param name="element">Element to consider</param>
            <returns>true if element matches query criteria. Otherwise false.</returns>
            /// <remarks></remarks>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.ScopeFilter.ShouldReturnIdForElement(Microsoft.Data.Tools.Schema.SchemaModel.IModelElement)">
            <summary>
            Determines if the element ID can be returned - external elements will support
            return of the external ID in a relationship instance, but should never support
            return of the actual element. 
            </summary>
            <param name="element">Element to consider</param>
            <returns>true if element matches query criteria. Otherwise false.</returns>
            /// <remarks></remarks>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SqlSchemaModelObjectService.ScopeFilter.QueryFilter">
            <summary>
            filter used when retrieving element
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ModelErrorSeverity">
            <summary>
            Represents the severity level of the model error
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelErrorSeverity.Unknown">
            <summary>
            Severity is unknown
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelErrorSeverity.Error">
            <summary>
            Model error
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelErrorSeverity.Warning">
            <summary>
            Warning in the model
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ModelErrorType">
            <summary>
            The model error type. Represents the various classification of model blocking errors.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelErrorType.ParserError">
            <summary>
            Represents a parser error in an object in the model
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelErrorType.InterpreterError">
            <summary>
            Represents a interpreter error in an object in the model
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelErrorType.ModelValidationError">
            <summary>
            Any other model blocking validation errors.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ModelExtractOptions">
            <summary>
            Defines options that affect the behavior of loading a model from a database.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ModelExtractOptions.#ctor">
            <summary>
            Construct a new instance of the <see cref="T:Microsoft.SqlServer.Dac.Model.ModelExtractOptions"/> class.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelExtractOptions.ExtractReferencedServerScopedElements">
            <summary>
            Get or set boolean that specifies whether server-scoped elements referenced by the source should be extracted.
            </summary>
            <value>
            True if references server-scoped elements should be extracted; otherwise false.
            Default value is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelExtractOptions.ExtractApplicationScopedObjectsOnly">
            <summary>
            Get or set boolean that specifies the scope of objects extracted from the source.
            </summary>
            <value>
            True if only application-scoped objects should be extracted;
            otherwise false to extract all object types.
            Default value is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelExtractOptions.IgnoreExtendedProperties">
            <summary>
            Get or set boolean that specifies whether extended properties should be ignored.
            </summary>
            <value>
            True if extended properties will be ignored; otherwise false.
            Default value is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelExtractOptions.IgnorePermissions">
            <summary>
            Get or set boolean that specifies whether permissions should be ignored.
            </summary>
            <value>
            True if permissions will be ignored; otherwise false.
            Default value is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelExtractOptions.IgnoreUserLoginMappings">
            <summary>
            Get or set boolean that specifies whether mappings between users and logins should be extracted from the source.
            </summary>
            <value>
            True if user login mappings will be ignored; otherwise false.
            Default value is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelExtractOptions.Storage">
            <summary>
            Get the type of backing storage for the schema model used during extraction.
            </summary>
            <value>
            Enumeration value that specifies the type of backing storage for the schema model used during extraction.
            Default value is File.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelExtractOptions.VerifyExtraction">
            <summary>
            Get or set boolean that specifies whether the extracted package should be verified.
            </summary>
            <value>
            True if the package should be verified; otherwise false.
            Default value is false.
            </value> 
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelExtractOptions.LoadAsScriptBackedModel">
            <summary>
            Should the model be loaded so that objects are backed up by scripted representations? In this case objects in the 
            <see cref="F:Microsoft.SqlServer.Dac.Model.DacQueryScopes.UserDefined"/> scope will have a source name and source position information. 
            
            When loading from a Dacpac or any other non-scripted source the model will not have source information. This
            means that when running static code analysis using the <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService"/> some rules may not work 
            correctly, and that existing objects in the model could not be replaced using the 
            <see cref="M:Microsoft.SqlServer.Dac.Model.TSqlModel.AddOrUpdateObjects(System.String,System.String,Microsoft.SqlServer.Dac.Model.TSqlObjectOptions)"/> method since there
            is no script with their original definition. For scenarios that use the <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService"/> setting this
            to true is strongly recommended. Similarly if you wish to update existing objects in the model setting this to true may
            be useful.
            
            Note that there is a potentially significant performance cost involved in creating a scripted model. All 
            top level objects in the <see cref="F:Microsoft.SqlServer.Dac.Model.DacQueryScopes.UserDefined"/> scope will be scripted out as strings 
            and then replaced with their scripted representations, after which the model will then have to fully resolve all 
            relationships. This will cause a one-time performance hit at the time the model is loaded. 
            </summary>
            <remarks>Default value is false, since there is a performance hit involved in creating a script-backed model from a 
            non-scripted source</remarks>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelExtractOptions.ExtractUsageProperties">
            <summary>
            Usage properties include Table.RowCount, Table.IndexSize, Table.DataSize, Table.UsedPages and Table.DataPages.
            When true, these properties are extracted from the database and are accessible in the model.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ModelLoadOptions">
            <summary>
            Options for loading a model from a data source such as a Dacpac file.
            
            These options can be very important depending on the scenario. For instance
            when running static code analysis using the <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService"/> it is strongly recommended that 
            the <see cref="P:Microsoft.SqlServer.Dac.Model.ModelLoadOptions.LoadAsScriptBackedModel"/> option be set to true, as many rules expect a fully scripted source and
            may not operate as expected on a non script-backed model. 
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ModelLoadOptions.#ctor">
            <summary>
            Constructs an instance of <see cref="T:Microsoft.SqlServer.Dac.Model.ModelLoadOptions"/> with the default options, which are
            to use <see cref="F:Microsoft.SqlServer.Dac.DacSchemaModelStorageType.Memory"/> storage and to set
            <see cref="P:Microsoft.SqlServer.Dac.Model.ModelLoadOptions.LoadAsScriptBackedModel"/> to be false.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ModelLoadOptions.#ctor(Microsoft.SqlServer.Dac.DacSchemaModelStorageType,System.Boolean)">
            <summary>
            Constructs an instance of <see cref="T:Microsoft.SqlServer.Dac.Model.ModelLoadOptions"/> with the specified options.
            </summary>
            <param name="modelStorageType">Value for the <see cref="P:Microsoft.SqlServer.Dac.Model.ModelLoadOptions.ModelStorageType"/> property</param>
            <param name="loadAsScriptBackedModel">Value for the <see cref="P:Microsoft.SqlServer.Dac.Model.ModelLoadOptions.LoadAsScriptBackedModel"/> property</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ModelLoadOptions.#ctor(Microsoft.SqlServer.Dac.DacSchemaModelStorageType,System.Boolean,System.Boolean)">
            <summary>
            Constructs an instance of <see cref="T:Microsoft.SqlServer.Dac.Model.ModelLoadOptions"/> with the specified options.
            </summary>
            <param name="modelStorageType">Value for the <see cref="P:Microsoft.SqlServer.Dac.Model.ModelLoadOptions.ModelStorageType"/> property</param>
            <param name="loadAsScriptBackedModel">Value for the <see cref="P:Microsoft.SqlServer.Dac.Model.ModelLoadOptions.LoadAsScriptBackedModel"/> property</param>
            <param name="throwOnModelErrors">Value for the <see cref="P:Microsoft.SqlServer.Dac.Model.ModelLoadOptions.ThrowOnModelErrors"/> property</param>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelLoadOptions.ModelStorageType">
            <summary>
            Should the model be stored in memory or should a file-backed storage be used?
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelLoadOptions.LoadAsScriptBackedModel">
            <summary>
            Should the model be loaded so that objects are backed up by scripted representations? In this case objects in the 
            <see cref="F:Microsoft.SqlServer.Dac.Model.DacQueryScopes.UserDefined"/> scope will have a source name and source position information. 
            
            When loading from a Dacpac or any other non-scripted source the model will not have source information. This
            means that when running static code analysis using the <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService"/> some rules may not work 
            correctly, and that existing objects in the model could not be replaced using the 
            <see cref="M:Microsoft.SqlServer.Dac.Model.TSqlModel.AddOrUpdateObjects(System.String,System.String,Microsoft.SqlServer.Dac.Model.TSqlObjectOptions)"/> method since there
            is no script with their original definition. For scenarios that use the <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService"/> setting this
            to true is strongly recommended. Similarly if you wish to update existing objects in the model setting this to true may
            be useful.
            
            Note that there is a potentially significant performance cost involved in creating a scripted model. All 
            top level objects in the <see cref="F:Microsoft.SqlServer.Dac.Model.DacQueryScopes.UserDefined"/> scope will be scripted out as strings 
            and then replaced with their scripted representations, after which the model will then have to fully resolve all 
            relationships. This will cause a one-time performance hit at the time the model is loaded. 
            </summary>
            <remarks>Default value is false, since there is a performance hit involved in creating a script-backed model from a 
            non-scripted source</remarks>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelLoadOptions.ThrowOnModelErrors">
            <summary>
            Defines whether the model edit APIs throw a <see cref="T:Microsoft.SqlServer.Dac.Model.DacModelException"/> if there are any blocking errors present in the model.
            
            When adding a new object or deleting/modifying existing objects in the model using API calls such as 
            <see cref="M:Microsoft.SqlServer.Dac.Model.TSqlModel.AddObjects(System.String)"/>, <see cref="M:Microsoft.SqlServer.Dac.Model.TSqlModel.AddOrUpdateObjects(System.String,System.String,Microsoft.SqlServer.Dac.Model.TSqlObjectOptions)"/>, <see cref="M:Microsoft.SqlServer.Dac.Model.TSqlModel.DeleteObjects(System.String)"/> 
            and <see cref="M:Microsoft.SqlServer.Dac.Model.TSqlModel.ConvertToScriptedObject(Microsoft.SqlServer.Dac.Model.TSqlObject,System.String)"/>, the model throws <see cref="T:Microsoft.SqlServer.Dac.Model.DacModelException"/> signaling 
            the presence of model blocking errors. Model blocking errors indicates the presence of serious errors such as parser, interpreter and other errors in the objects.
            Setting this option to false allows building the model even if it has such serious errors. Subsequently those objects with model errors can be retrieved using the
            <see cref="M:Microsoft.SqlServer.Dac.Model.TSqlModel.GetModelErrors"/> method.
            
            When objects with model blocking errors are added to the model, the object is edited such that errant parts are removed or commented out in a 
            best effort manner to preserve the parts of the object that do not have errors. For instance a stored procedure that has a parser error in the procedure body 
            will have its body commented out before adding to the model. As a worst case the object is completely dropped while preserving the model errors.
            
            Note that current implementation handles objects with model errors as follows
            1) Sql procedures and views are added to the model with commented body.
            2) Other object types are completely dropped while preserving the model errors.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ModelMetadataClass">
            <summary>
            The metadata class for metadata properties.
            </summary>
            <remarks>
            <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModelSchema"/> metadata classes provide access to the structure and instance data
            within a <c>TSqlSchemaModel</c> instance.
            </remarks>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ModelMetadataClass.GetValue``1(Microsoft.SqlServer.Dac.Model.TSqlObject)">
            <summary>
            Returns the metadata property value.
            </summary>
            <typeparam name="T"><see cref="T:System.Type"/> of the metadata property value.</typeparam>
            <param name="instance"><see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> instance to get the metadata property value from.</param>
            <returns>Metadata property value.</returns>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelMetadataClass.Name">
            <summary>
            Metadata property name.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelMetadataClass.DataType">
            <summary>
            Data type of the metadata property.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelMetadataClass.OwningType">
            <summary>
            Owning <see cref="T:Microsoft.SqlServer.Dac.Model.ModelTypeClass"/> for the <see cref="T:Microsoft.SqlServer.Dac.Model.ModelMetadataClass"/>.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ModelPropertyClass">
            <summary>
            The metadata class for properties.
            </summary>
            <remarks>
            <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModelSchema"/> metadata classes provide access to the structure and instance data
            within a <c>TSqlSchemaModel</c> instance.
            </remarks>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ModelPropertyClass.GetValue``1(Microsoft.SqlServer.Dac.Model.TSqlObject)">
            <summary>
            Returns the property value.
            </summary>
            <typeparam name="T"><see cref="T:System.Type"/> of the property value.</typeparam>
            <param name="instance"><see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> instance to get the property value from.</param>
            <returns>Property value.</returns>
            <exception cref="T:System.InvalidCastException">
            If the property value cannot be cast to <typeparamref name="T"/>.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ModelPropertyClass.GetDefaultValue``1(Microsoft.SqlServer.Dac.Model.SqlServerVersion)">
            <summary>
            Returns the default property value.
            
            Note: for <see cref="F:Microsoft.SqlServer.Dac.Model.SqlServerVersion.SqlAzure"/> the default value may depend on the
            <see cref="P:Microsoft.SqlServer.Dac.Model.TSqlModel.EngineVersion"/> being targeted. This method assumes the current default
            version is being used. To get a default value based on a particular for a particular EngineVersion, please use
            <see cref="M:Microsoft.SqlServer.Dac.Model.ModelPropertyClass.GetDefaultValue``1(Microsoft.SqlServer.Dac.Model.SqlServerVersion,System.Int32)"/>.
            </summary>
            <typeparam name="T"><see cref="T:System.Type"/> of the property value.</typeparam>
            <param name="version">The <see cref="T:Microsoft.SqlServer.Dac.Model.SqlServerVersion"/> to return the default value for.</param>
            <returns>Default value for specified <see cref="T:Microsoft.SqlServer.Dac.Model.SqlServerVersion"/>.</returns>
            <exception cref="T:System.InvalidCastException">
            If the property value cannot be cast to <typeparamref name="T"/>.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ModelPropertyClass.GetDefaultValue(Microsoft.SqlServer.Dac.Model.SqlServerVersion)">
            <summary>
            Returns the default property value.
            
            Note: for <see cref="F:Microsoft.SqlServer.Dac.Model.SqlServerVersion.SqlAzure"/> the default value may depend on the
            <see cref="P:Microsoft.SqlServer.Dac.Model.TSqlModel.EngineVersion"/> being targeted. This method assumes the current default
            version is being used. To get a default value based on a particular for a particular EngineVersion, please use
            <see cref="M:Microsoft.SqlServer.Dac.Model.ModelPropertyClass.GetDefaultValue(Microsoft.SqlServer.Dac.Model.SqlServerVersion,System.Int32)"/>.
            </summary>
            <param name="version">The <see cref="T:Microsoft.SqlServer.Dac.Model.SqlServerVersion"/> to return the default value for.</param>
            <returns>Default value for specified <see cref="T:Microsoft.SqlServer.Dac.Model.SqlServerVersion"/>.</returns>        
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ModelPropertyClass.IsDefaultValue(Microsoft.SqlServer.Dac.Model.TSqlObject,Microsoft.SqlServer.Dac.Model.SqlServerVersion)">
            <summary>
            Returns if the value is the platform default value.
            
            Note: for <see cref="F:Microsoft.SqlServer.Dac.Model.SqlServerVersion.SqlAzure"/> the default value may depend on the
            <see cref="P:Microsoft.SqlServer.Dac.Model.TSqlModel.EngineVersion"/> being targeted. This method assumes the current default
            version is being used. To validate the  default value based on a particular for a particular EngineVersion, please use
            <see cref="M:Microsoft.SqlServer.Dac.Model.ModelPropertyClass.IsDefaultValue(Microsoft.SqlServer.Dac.Model.TSqlObject,Microsoft.SqlServer.Dac.Model.SqlServerVersion,System.Int32)"/>.
            </summary>
            <param name="instance"><see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> instance to determine the default property value for.</param>
            <param name="version">The <see cref="T:Microsoft.SqlServer.Dac.Model.SqlServerVersion"/> to determine the default value for.</param>
            <returns>True if the property is the platform default. Otherwise false.</returns>
            <exception cref="T:Microsoft.SqlServer.Dac.Model.DacModelException">
            If the supplied <paramref name="instance"/> is not the owner of this property.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ModelPropertyClass.GetDefaultValue``1(Microsoft.SqlServer.Dac.Model.SqlServerVersion,System.Int32)">
            <summary>
            Returns the default property value.
            Note: for <see cref="F:Microsoft.SqlServer.Dac.Model.SqlServerVersion.SqlAzure"/> the default value may depend on the
            <see cref="P:Microsoft.SqlServer.Dac.Model.TSqlModel.EngineVersion"/> being targeted. 
            </summary>
            <typeparam name="T"><see cref="T:System.Type"/> of the property value.</typeparam>
            <param name="version">The <see cref="T:Microsoft.SqlServer.Dac.Model.SqlServerVersion"/> to return the default value for.</param>
            <param name="engineVersion">
            Optional value for the engine version. This maps to the
            <see cref="P:Microsoft.SqlServer.Dac.Model.TSqlModel.EngineVersion"/> property on a model.
            </param>
            <returns>Default value for specified <see cref="T:Microsoft.SqlServer.Dac.Model.SqlServerVersion"/>.</returns>
            <exception cref="T:System.InvalidCastException">
            If the property value cannot be cast to <typeparamref name="T"/>.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ModelPropertyClass.GetDefaultValue(Microsoft.SqlServer.Dac.Model.SqlServerVersion,System.Int32)">
            <summary>
            Returns the default property value.
            </summary>
            <param name="version">The <see cref="T:Microsoft.SqlServer.Dac.Model.SqlServerVersion"/> to return the default value for.</param>
            <param name="engineVersion">
            Optional value for the engine version. This maps to the <see cref="P:Microsoft.SqlServer.Dac.Model.TSqlModel.EngineVersion"/> property on a model.
            </param>
            <returns>Default value for specified <see cref="T:Microsoft.SqlServer.Dac.Model.SqlServerVersion"/>.</returns>        
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ModelPropertyClass.IsDefaultValue(Microsoft.SqlServer.Dac.Model.TSqlObject,Microsoft.SqlServer.Dac.Model.SqlServerVersion,System.Int32)">
            <summary>
            Returns if the value is the platform default value.
            </summary>
            <param name="instance"><see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> instance to determine the default property value for.</param>
            <param name="version">The <see cref="T:Microsoft.SqlServer.Dac.Model.SqlServerVersion"/> to determine the default value for.</param>
            <param name="engineVersion">
            Optional value for the engine version. This maps to the <see cref="P:Microsoft.SqlServer.Dac.Model.TSqlModel.EngineVersion"/> property on a model.
            </param>
            <returns>True if the property is the platform default. Otherwise false.</returns>
            <exception cref="T:Microsoft.SqlServer.Dac.Model.DacModelException">
            If the supplied <paramref name="instance"/> is not the owner of this property.
            </exception>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelPropertyClass.Name">
            <summary>
            Property name.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelPropertyClass.DataType">
            <summary>
            Data type of the property.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelPropertyClass.SupportedPlatforms">
            <summary>
            The <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlPlatforms"/> versions on which this relationship is supported.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelPropertyClass.OwningType">
            <summary>
            Owning <see cref="T:Microsoft.SqlServer.Dac.Model.ModelTypeClass"/> for the property, where this 
            property is attached to a Type. Null if this property is on a 
            <see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipClass"/>
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelPropertyClass.OwningRelationship">
            <summary>
            Owning <see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipClass"/> for the property, where this 
            property is attached to a Relationship. Null if this property is on a 
            <see cref="T:Microsoft.SqlServer.Dac.Model.ModelTypeClass"/>
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipClass">
            <summary>
            The metadata class for relationships.
            </summary>
            <remarks>
            <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModelSchema"/> metadata classes provide access to the structure and instance data
            within a <c>TSqlSchemaModel</c> instance.
            </remarks>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelRelationshipClass.Name">
            <summary>
            Relationship name.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelRelationshipClass.Type">
            <summary>
            Type of the relationship.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelRelationshipClass.FromObjectClass">
            <summary>
            The <see cref="T:Microsoft.SqlServer.Dac.Model.ModelTypeClass"/> of the referencing object for the relationship.
            This represents the class of objects the relationship comes from
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelRelationshipClass.SupportedPlatforms">
            <summary>
            The <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlPlatforms"/> versions on which this relationship is supported.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelRelationshipClass.Properties">
            <summary>
            Properties for the relationship.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance">
            <summary>
            Represents a reference from one <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> to another, which might be unresolved.
            </summary>
            <remarks>
            <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModelSchema"/> metadata classes provide access to the structure and instance data
            within a <c>TSqlModelSchema</c> instance.
            </remarks>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance.GetProperty``1(Microsoft.SqlServer.Dac.Model.ModelPropertyClass)">
            <summary>
            Retrieves properties for this relationship instance.
            </summary>
            <typeparam name="T"><see cref="T:System.Type"/> of the property value.</typeparam>
            <param name="property"><see cref="T:Microsoft.SqlServer.Dac.Model.ModelPropertyClass"/> property to retrieve.</param>
            <returns>Property value.</returns>
            <exception cref="T:Microsoft.SqlServer.Dac.Model.DacModelException">
            If the supplied <paramref name="property"/> is not supported on this instance.
            </exception>
            <exception cref="T:System.InvalidCastException">
            If the property type cannot be cast to T.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance.GetProperty(Microsoft.SqlServer.Dac.Model.ModelPropertyClass)">
            <summary>
            Retrieves properties for this relationship instance.
            </summary>
            <param name="property"><see cref="T:Microsoft.SqlServer.Dac.Model.ModelPropertyClass"/> property to retrieve.</param>
            <returns>Property value.</returns>
            <exception cref="T:Microsoft.SqlServer.Dac.Model.DacModelException">
            If the supplied <paramref name="property"/> is not supported on this instance.
            </exception>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance.Relationship">
            <summary>
            The <see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipClass"/> this instance represents.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance.FromObject">
            <summary>
            The <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> this instance is from. This is the referencing object
            </summary>
            <remarks>
            This property returns null if the relationship is unresolved or refers to elements outside the current <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/>.
            </remarks>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance.Object">
            <summary>
            The <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> this instance references. This is the referenced object.
            </summary>
            <remarks>
            This property returns null if the relationship is unresolved or refers to elements outside the current <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/>.
            </remarks>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance.ObjectName">
            <summary>
            The <see cref="T:Microsoft.SqlServer.Dac.Model.ObjectIdentifier"/> representing the name of the referenced object.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance.PropertyContext">
            <summary>
            The context boject to use for property lookup - if this isn't set then <see cref="M:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance.GetProperty``1(Microsoft.SqlServer.Dac.Model.ModelPropertyClass)"/>
            is not supported on this <see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance"/>. For internal use only
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance.Item(Microsoft.SqlServer.Dac.Model.ModelPropertyClass)">
            <summary>
            Retrieves properties for this relationship instance.
            </summary>
            <param name="property"><see cref="T:Microsoft.SqlServer.Dac.Model.ModelPropertyClass"/> property to retrieve.</param>
            <returns>Property value.</returns>
            <exception cref="T:Microsoft.SqlServer.Dac.Model.DacModelException">
            If the supplied <paramref name="property"/> is not supported on this instance.
            </exception>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ModelSchema">
            <summary>
            Model schema for TSqlModel
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ModelSchema.InitializeModelSchema">
            <summary>
            The ModelSchema has static state that is extremely useful when navigating a model's
            contents.  Under normal circumstances (use of ) the static state is indirectly initialized and clients 
            can use the state oblivious to how it has been initialized.  In other heads (commandline / API / unit tests) it is possible
            for the static state to be uninitialized prior to it's being used causing unexpected nulls and failures.  When implementing
            a new head that may directly use the ModelSchema prior to instantiating a TSqlModel call this method prior to
            accessing any static state.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ModelSchema.GetViewSubroutineAndTriggerClasses">
            <summary>
            Gets the set of classes representing Views, Subroutines and Triggers. This is a common set of classes used during
            analysis
            </summary>
            <returns>The <see cref="T:Microsoft.SqlServer.Dac.Model.ModelTypeClass"/>es representing view, subroutines and triggers</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ModelSchema.GetSubroutineClasses">
            <summary>
            Gets the set of classes representing Subroutines. This is a common set of classes used during
            analysis
            </summary>
            <returns>The <see cref="T:Microsoft.SqlServer.Dac.Model.ModelTypeClass"/>es representing subroutines</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ModelSchema.GetColumnSourceClasses">
            <summary>
            Gets the set of classes representing a potential source of columns. Tables, Views, Functions can all be 
            a source for this
            </summary>
            <returns>The <see cref="T:Microsoft.SqlServer.Dac.Model.ModelTypeClass"/>es representing view, subroutines and triggers</returns>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.SchemaInstance">
            <summary>
            Instance of the model schema
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.Column">
            <summary>
            Model type class for Column
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.TableValuedFunction">
            <summary>
            Model type class for TableValuedFunction
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.ScalarFunction">
            <summary>
            Model type class for ScalarFunction
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.ClrTableOption">
            <summary>
            Model type class for ClrTableOption
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.Aggregate">
            <summary>
            Model type class for Aggregate
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.ApplicationRole">
            <summary>
            Model type class for ApplicationRole
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.Index">
            <summary>
            Model type class for Index
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.Assembly">
            <summary>
            Model type class for Assembly
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.AssemblySource">
            <summary>
            Model type class for AssemblySource
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.AsymmetricKey">
            <summary>
            Model type class for AsymmetricKey
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.AuditAction">
            <summary>
            Model type class for AuditAction
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.AuditActionGroup">
            <summary>
            Model type class for AuditActionGroup
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.AuditActionSpecification">
            <summary>
            Model type class for AuditActionSpecification
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.BrokerPriority">
            <summary>
            Model type class for BrokerPriority
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.BuiltInServerRole">
            <summary>
            Model type class for BuiltInServerRole
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.DataType">
            <summary>
            Model type class for DataType
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.Certificate">
            <summary>
            Model type class for Certificate
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.CheckConstraint">
            <summary>
            Model type class for CheckConstraint
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.ClrTypeMethod">
            <summary>
            Model type class for ClrTypeMethod
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.ClrTypeMethodParameter">
            <summary>
            Model type class for ClrTypeMethodParameter
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.ClrTypeProperty">
            <summary>
            Model type class for ClrTypeProperty
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.ColumnStoreIndex">
            <summary>
            Model type class for ColumnStoreIndex
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.Contract">
            <summary>
            Model type class for Contract
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.Credential">
            <summary>
            Model type class for Credential
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.DatabaseCredential">
            <summary>
            Model type class for DatabaseCredential
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.CryptographicProvider">
            <summary>
            Model type class for CryptographicProvider
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.DatabaseAuditSpecification">
            <summary>
            Model type class for DatabaseAuditSpecification
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.DatabaseDdlTrigger">
            <summary>
            Model type class for DatabaseDdlTrigger
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.DatabaseEncryptionKey">
            <summary>
            Model type class for DatabaseEncryptionKey
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.DatabaseEventNotification">
            <summary>
            Model type class for DatabaseEventNotification
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.DatabaseMirroringLanguageSpecifier">
            <summary>
            Model type class for DatabaseMirroringLanguageSpecifier
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.DatabaseOptions">
            <summary>
            Model type class for DatabaseOptions
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.DataCompressionOption">
            <summary>
            Model type class for DataCompressionOption
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.Default">
            <summary>
            Model type class for Default
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.DefaultConstraint">
            <summary>
            Model type class for DefaultConstraint
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.DmlTrigger">
            <summary>
            Model type class for DmlTrigger
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.Endpoint">
            <summary>
            Model type class for Endpoint
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.ErrorMessage">
            <summary>
            Model type class for ErrorMessage
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.EventGroup">
            <summary>
            Model type class for EventGroup
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.EventSession">
            <summary>
            Model type class for EventSession
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.DatabaseEventSession">
            <summary>
            Model type class for DatabaseEventSession
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.EventSessionAction">
            <summary>
            Model type class for EventSessionAction
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.EventSessionDefinitions">
            <summary>
            Model type class for EventSessionDefinitions
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.EventSessionSetting">
            <summary>
            Model type class for EventSessionSetting
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.EventSessionTarget">
            <summary>
            Model type class for EventSessionTarget
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.EventTypeSpecifier">
            <summary>
            Model type class for EventTypeSpecifier
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.ExtendedProcedure">
            <summary>
            Model type class for ExtendedProcedure
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.ExtendedProperty">
            <summary>
            Model type class for ExtendedProperty
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.ExternalDataSource">
            <summary>
            Model type class for ExternalDataSource
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.ExternalFileFormat">
            <summary>
            Model type class for ExternalFileFormat
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.ExternalTable">
            <summary>
            Model type class for ExternalTable
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.SqlFile">
            <summary>
            Model type class for SqlFile
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.Filegroup">
            <summary>
            Model type class for Filegroup
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.ForeignKeyConstraint">
            <summary>
            Model type class for ForeignKeyConstraint
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.FullTextCatalog">
            <summary>
            Model type class for FullTextCatalog
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.FullTextIndex">
            <summary>
            Model type class for FullTextIndex
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.FullTextIndexColumnSpecifier">
            <summary>
            Model type class for FullTextIndexColumnSpecifier
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.FullTextStopList">
            <summary>
            Model type class for FullTextStopList
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.HttpProtocolSpecifier">
            <summary>
            Model type class for HttpProtocolSpecifier
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.LinkedServer">
            <summary>
            Model type class for LinkedServer
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.LinkedServerLogin">
            <summary>
            Model type class for LinkedServerLogin
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.Login">
            <summary>
            Model type class for Login
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.MasterKey">
            <summary>
            Model type class for MasterKey
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.MessageType">
            <summary>
            Model type class for MessageType
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.PartitionFunction">
            <summary>
            Model type class for PartitionFunction
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.PartitionScheme">
            <summary>
            Model type class for PartitionScheme
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.PartitionValue">
            <summary>
            Model type class for PartitionValue
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.Permission">
            <summary>
            Model type class for Permission
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.PrimaryKeyConstraint">
            <summary>
            Model type class for PrimaryKeyConstraint
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.Procedure">
            <summary>
            Model type class for Procedure
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.Queue">
            <summary>
            Model type class for Queue
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.QueueEventNotification">
            <summary>
            Model type class for QueueEventNotification
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.RemoteServiceBinding">
            <summary>
            Model type class for RemoteServiceBinding
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.ResourceGovernor">
            <summary>
            Model type class for ResourceGovernor
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.ResourcePool">
            <summary>
            Model type class for ResourcePool
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.Role">
            <summary>
            Model type class for Role
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.RoleMembership">
            <summary>
            Model type class for RoleMembership
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.Route">
            <summary>
            Model type class for Route
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.Rule">
            <summary>
            Model type class for Rule
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.Schema">
            <summary>
            Model type class for Schema
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.SearchProperty">
            <summary>
            Model type class for SearchProperty
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.SearchPropertyList">
            <summary>
            Model type class for SearchPropertyList
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.SecurityPolicy">
            <summary>
            Model type class for SecurityPolicy
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.SecurityPredicate">
            <summary>
            Model type class for SecurityPredicate
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.Sequence">
            <summary>
            Model type class for Sequence
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.ServerAudit">
            <summary>
            Model type class for ServerAudit
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.ServerAuditSpecification">
            <summary>
            Model type class for ServerAuditSpecification
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.ServerDdlTrigger">
            <summary>
            Model type class for ServerDdlTrigger
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.ServerEventNotification">
            <summary>
            Model type class for ServerEventNotification
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.ServerOptions">
            <summary>
            Model type class for ServerOptions
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.ServerRoleMembership">
            <summary>
            Model type class for ServerRoleMembership
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.Service">
            <summary>
            Model type class for Service
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.ServiceBrokerLanguageSpecifier">
            <summary>
            Model type class for ServiceBrokerLanguageSpecifier
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.Signature">
            <summary>
            Model type class for Signature
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.SignatureEncryptionMechanism">
            <summary>
            Model type class for SignatureEncryptionMechanism
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.SoapLanguageSpecifier">
            <summary>
            Model type class for SoapLanguageSpecifier
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.SoapMethodSpecification">
            <summary>
            Model type class for SoapMethodSpecification
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.SpatialIndex">
            <summary>
            Model type class for SpatialIndex
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.Statistics">
            <summary>
            Model type class for Statistics
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.Parameter">
            <summary>
            Model type class for Parameter
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.SymmetricKey">
            <summary>
            Model type class for SymmetricKey
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.SymmetricKeyPassword">
            <summary>
            Model type class for SymmetricKeyPassword
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.Synonym">
            <summary>
            Model type class for Synonym
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.Table">
            <summary>
            Model type class for Table
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.FileTable">
            <summary>
            Model type class for FileTable
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.TableType">
            <summary>
            Model type class for TableType
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.TableTypeCheckConstraint">
            <summary>
            Model type class for TableTypeCheckConstraint
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.TableTypeColumn">
            <summary>
            Model type class for TableTypeColumn
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.TableTypeDefaultConstraint">
            <summary>
            Model type class for TableTypeDefaultConstraint
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.TableTypeIndex">
            <summary>
            Model type class for TableTypeIndex
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.TableTypePrimaryKeyConstraint">
            <summary>
            Model type class for TableTypePrimaryKeyConstraint
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.TableTypeUniqueConstraint">
            <summary>
            Model type class for TableTypeUniqueConstraint
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.TcpProtocolSpecifier">
            <summary>
            Model type class for TcpProtocolSpecifier
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.UniqueConstraint">
            <summary>
            Model type class for UniqueConstraint
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.User">
            <summary>
            Model type class for User
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.UserDefinedServerRole">
            <summary>
            Model type class for UserDefinedServerRole
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.UserDefinedType">
            <summary>
            Model type class for UserDefinedType
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.View">
            <summary>
            Model type class for View
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.WorkloadGroup">
            <summary>
            Model type class for WorkloadGroup
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.XmlIndex">
            <summary>
            Model type class for XmlIndex
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.SelectiveXmlIndex">
            <summary>
            Model type class for SelectiveXmlIndex
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.XmlNamespace">
            <summary>
            Model type class for XmlNamespace
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.PromotedNodePathForXQueryType">
            <summary>
            Model type class for PromotedNodePathForXQueryType
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.PromotedNodePathForSqlType">
            <summary>
            Model type class for PromotedNodePathForSqlType
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ModelSchema.XmlSchemaCollection">
            <summary>
            Model type class for XmlSchemaCollection
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ColumnType">
            <summary>
            Specifies the type of Column a TSqlObject represents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ColumnType.Column">
            <summary>
            Column
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ColumnType.ComputedColumn">
            <summary>
            ComputedColumn
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ColumnType.ColumnSet">
            <summary>
            ColumnSet
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.TableTypeColumnType">
            <summary>
            Specifies the type of TableTypeColumn a TSqlObject represents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.TableTypeColumnType.Column">
            <summary>
            Column
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.TableTypeColumnType.ComputedColumn">
            <summary>
            ComputedColumn
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.FunctionType">
            <summary>
            Specifies the type of Function a TSqlObject represents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.FunctionType.InlineTableValuedFunction">
            <summary>
            InlineTableValuedFunction
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.FunctionType.MultiStatementTableValuedFunction">
            <summary>
            MultiStatementTableValuedFunction
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.SqlDataType">
            <summary>
            SQL Server built-in data types
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.Unknown">
            <summary>
            Nothing was defined.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.BigInt">
            <summary>
            Integer (whole number) data from -2^63 (-9,223,372,036,854,775,808) through 2^63-1 (9,223,372,036,854,775,807).
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.Int">
            <summary>
            Integer (whole number) data from -2^31 (-2,147,483,648) through 2^31 - 1 (2,147,483,647).
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.SmallInt">
            <summary>
            Integer data from -2^15 (-32,768) through 2^15 - 1 (32,767).
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.TinyInt">
            <summary>
            Integer data from 0 through 255.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.Bit">
            <summary>
            Integer data with either a 1 or 0 value.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.Decimal">
            <summary>
            Fixed precision and scale numeric data from -10^38 +1 through 10^38 -1.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.Numeric">
            <summary>
            Functionally equivalent to decimal.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.Money">
            <summary>
            Monetary data values from -2^63 (-922,337,203,685,477.5808) through 2^63 - 1 (+922,337,203,685,477.5807), with accuracy to a ten-thousandth of a monetary unit.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.SmallMoney">
            <summary>
            Monetary data values from -214,748.3648 through +214,748.3647, with accuracy to a ten-thousandth of a monetary unit.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.Float">
            <summary>
            Floating precision number data with the following valid values: -1.79E + 308 through -2.23E - 308, 0 and 2.23E + 308 through 1.79E + 308.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.Real">
            <summary>
            Floating precision number data with the following valid values: -3.40E + 38 through -1.18E - 38, 0 and 1.18E - 38 through 3.40E + 38.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.DateTime">
            <summary>
            Date and time data from January 1, 1753, through December 31, 9999, with an accuracy of three-hundredths of a second, or 3.33 milliseconds.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.SmallDateTime">
            <summary>
            Date and time data from January 1, 1900, through June 6, 2079, with an accuracy of one minute.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.Char">
            <summary>
            Fixed-length non-Unicode character data with a maximum length of 8,000 characters.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.VarChar">
            <summary>
            Variable-length non-Unicode data with a maximum of 8,000 characters.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.Text">
            <summary>
            Variable-length non-Unicode data with a maximum length of 2^31 - 1 (2,147,483,647) characters.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.NChar">
            <summary>
            Fixed-length Unicode data with a maximum length of 4,000 characters.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.NVarChar">
            <summary>
            Variable-length Unicode data with a maximum length of 4,000 characters. sysname is a system-supplied user-defined data type that is functionally equivalent to nvarchar(128) and is used to reference database object names.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.NText">
            <summary>
            Variable-length Unicode data with a maximum length of 2^30 - 1 (1,073,741,823) characters.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.Binary">
            <summary>
            Fixed-length binary data with a maximum length of 8,000 bytes.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.VarBinary">
            <summary>
            Variable-length binary data with a maximum length of 8,000 bytes.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.Image">
            <summary>
            Variable-length binary data with a maximum length of 2^31 - 1 (2,147,483,647) bytes.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.Cursor">
            <summary>
            A reference to a cursor.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.Variant">
            <summary>
            A data type that stores values of various SQL Server-supported data types, except text, ntext, timestamp, and sql_variant.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.Table">
            <summary>
            A special data type used to store a result set for later processing.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.Timestamp">
            <summary>
            A database-wide unique number that gets updated every time a row gets updated.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.UniqueIdentifier">
            <summary>
            Is a 16-byte GUID
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.Xml">
            <summary>
            xml data type
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.Date">
            <summary>
            Date-only
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.Time">
            <summary>
            Time only
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.DateTime2">
            <summary>
            Combination of data and time types, better precision than DataTime
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.DateTimeOffset">
            <summary>
            Same as DateTime2 with timezone offset added
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlDataType.Rowversion">
            <summary>
            (Alias for Timestamp) A database-wide unique number that gets updated every time a row gets updated.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.PermissionType">
            <summary>
            Sql permissions
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.Unknown">
            <summary>
            Unknown
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.Insert">
            <summary>
            Insert
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.Select">
            <summary>
            Select
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.Update">
            <summary>
            Update
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.Delete">
            <summary>
            Delete
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.References">
            <summary>
            References
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.CreateDatabase">
            <summary>
            CreateDatabase
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.CreateDefault">
            <summary>
            CreateDefault
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.CreateFunction">
            <summary>
            CreateFunction
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.CreateProcedure">
            <summary>
            CreateProcedure
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.CreateRule">
            <summary>
            CreateRule
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.CreateTable">
            <summary>
            CreateTable
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.CreateView">
            <summary>
            CreateView
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.BackupDatabase">
            <summary>
            BackupDatabase
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.BackupLog">
            <summary>
            BackupLog
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AdministerBulkOperations">
            <summary>
            AdministerBulkOperations
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.Alter">
            <summary>
            Alter
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnyApplicationRole">
            <summary>
            AlterAnyApplicationRole
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnyAssembly">
            <summary>
            AlterAnyAssembly
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnyAsymmetricKey">
            <summary>
            AlterAnyAsymmetricKey
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnyCertificate">
            <summary>
            AlterAnyCertificate
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnyConnection">
            <summary>
            AlterAnyConnection
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnyContract">
            <summary>
            AlterAnyContract
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnyCredential">
            <summary>
            AlterAnyCredential
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnyDatabase">
            <summary>
            AlterAnyDatabase
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnyDatabaseDdlTrigger">
            <summary>
            AlterAnyDatabaseDdlTrigger
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnyDatabaseEventNotification">
            <summary>
            AlterAnyDatabaseEventNotification
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnyDataspace">
            <summary>
            AlterAnyDataspace
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnyEndpoint">
            <summary>
            AlterAnyEndpoint
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnyEventNotification">
            <summary>
            AlterAnyEventNotification
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnyFulltextCatalog">
            <summary>
            AlterAnyFulltextCatalog
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnyLinkedServer">
            <summary>
            AlterAnyLinkedServer
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnyLogin">
            <summary>
            AlterAnyLogin
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnyMessageType">
            <summary>
            AlterAnyMessageType
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnyRemoteServiceBinding">
            <summary>
            AlterAnyRemoteServiceBinding
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnyRole">
            <summary>
            AlterAnyRole
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnyRoute">
            <summary>
            AlterAnyRoute
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnySchema">
            <summary>
            AlterAnySchema
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnyService">
            <summary>
            AlterAnyService
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnySymmetricKey">
            <summary>
            AlterAnySymmetricKey
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnyUser">
            <summary>
            AlterAnyUser
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterResources">
            <summary>
            AlterResources
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterServerState">
            <summary>
            AlterServerState
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterSettings">
            <summary>
            AlterSettings
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterTrace">
            <summary>
            AlterTrace
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.Authenticate">
            <summary>
            Authenticate
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AuthenticateServer">
            <summary>
            AuthenticateServer
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.Checkpoint">
            <summary>
            Checkpoint
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.Connect">
            <summary>
            Connect
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.ConnectReplication">
            <summary>
            ConnectReplication
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.ConnectSql">
            <summary>
            ConnectSql
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.Control">
            <summary>
            Control
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.ControlServer">
            <summary>
            ControlServer
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.CreateAggregate">
            <summary>
            CreateAggregate
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.CreateAnyDatabase">
            <summary>
            CreateAnyDatabase
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.CreateAssembly">
            <summary>
            CreateAssembly
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.CreateAsymmetricKey">
            <summary>
            CreateAsymmetricKey
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.CreateCertificate">
            <summary>
            CreateCertificate
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.CreateContract">
            <summary>
            CreateContract
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.CreateDatabaseDdlEventNotification">
            <summary>
            CreateDatabaseDdlEventNotification
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.CreateDdlEventNotification">
            <summary>
            CreateDdlEventNotification
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.CreateEndpoint">
            <summary>
            CreateEndpoint
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.CreateFulltextCatalog">
            <summary>
            CreateFulltextCatalog
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.CreateMessageType">
            <summary>
            CreateMessageType
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.CreateQueue">
            <summary>
            CreateQueue
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.CreateRemoteServiceBinding">
            <summary>
            CreateRemoteServiceBinding
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.CreateRole">
            <summary>
            CreateRole
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.CreateRoute">
            <summary>
            CreateRoute
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.CreateSchema">
            <summary>
            CreateSchema
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.CreateService">
            <summary>
            CreateService
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.CreateSymmetricKey">
            <summary>
            CreateSymmetricKey
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.CreateSynonym">
            <summary>
            CreateSynonym
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.CreateTraceEventNotification">
            <summary>
            CreateTraceEventNotification
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.CreateType">
            <summary>
            CreateType
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.CreateXmlSchemaCollection">
            <summary>
            CreateXmlSchemaCollection
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.Execute">
            <summary>
            Execute
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.ExternalAccessAssembly">
            <summary>
            ExternalAccessAssembly
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.Impersonate">
            <summary>
            Impersonate
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.Receive">
            <summary>
            Receive
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.Send">
            <summary>
            Send
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.Showplan">
            <summary>
            Showplan
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.Shutdown">
            <summary>
            Shutdown
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.SubscribeQueryNotifications">
            <summary>
            SubscribeQueryNotifications
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.TakeOwnership">
            <summary>
            TakeOwnership
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.UnsafeAssembly">
            <summary>
            UnsafeAssembly
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.ViewAnyDatabase">
            <summary>
            ViewAnyDatabase
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.ViewAnyDefinition">
            <summary>
            ViewAnyDefinition
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.ViewChangeTracking">
            <summary>
            ViewChangeTracking
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.ViewDatabaseState">
            <summary>
            ViewDatabaseState
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.ViewDefinition">
            <summary>
            ViewDefinition
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.ViewServerState">
            <summary>
            ViewServerState
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.All">
            <summary>
            All
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnyDatabaseAudit">
            <summary>
            AlterAnyDatabaseAudit
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnyServerAudit">
            <summary>
            AlterAnyServerAudit
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.CreateSequence">
            <summary>
            CreateSequence
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnyServerRole">
            <summary>
            AlterAnyServerRole
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.CreateServerRole">
            <summary>
            CreateServerRole
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.CreateAvailabilityGroup">
            <summary>
            CreateAvailabilityGroup
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnyAvailabilityGroup">
            <summary>
            AlterAnyAvailabilityGroup
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnyDatabaseEventSession">
            <summary>
            AlterAnyDatabaseEventSession
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.ConnectAnyDatabase">
            <summary>
            ConnectAnyDatabase
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.ImpersonateAnyLogin">
            <summary>
            ImpersonateAnyLogin
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.SelectAllUserSecurables">
            <summary>
            SelectAllUserSecurables
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnySecurityPolicy">
            <summary>
            AlterAnySecurityPolicy
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnyColumnEncryptionKey">
            <summary>
            AlterAnyColumnEncryptionKey
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnyColumnMasterKey">
            <summary>
            AlterAnyColumnMasterKey
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.Unmask">
            <summary>
            Unmask
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnyMask">
            <summary>
            AlterAnyMask
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.ViewAnyColumnMasterKeyDefinition">
            <summary>
            ViewAnyColumnMasterKeyDefinition
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.ViewAnyColumnEncryptionKeyDefinition">
            <summary>
            ViewAnyColumnEncryptionKeyDefinition
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnyExternalDataSource">
            <summary>
            AlterAnyExternalDataSource
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionType.AlterAnyExternalFileFormat">
            <summary>
            AlterAnyExternalFileFormat
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.AuthenticationModes">
            <summary>
            SQL Server 2005 authentication mode
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuthenticationModes.None">
            <summary>
            None
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuthenticationModes.Basic">
            <summary>
            Basic
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuthenticationModes.Digest">
            <summary>
            Digest
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuthenticationModes.Integrated">
            <summary>
            Integrated
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuthenticationModes.Ntlm">
            <summary>
            Ntlm
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuthenticationModes.Kerberos">
            <summary>
            Kerberos
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuthenticationModes.Negotiate">
            <summary>
            Negotiate
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.LockEscalationMethod">
            <summary>
            The types of lock escalation methods
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.LockEscalationMethod.Table">
            <summary>
            Table
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.LockEscalationMethod.Disable">
            <summary>
            Disable
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.LockEscalationMethod.Auto">
            <summary>
            Auto
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.CharacterSet">
            <summary>
            Possible character sets for SOAP endpoints
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.CharacterSet.Unknown">
            <summary>
            Unknown
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.CharacterSet.Sql">
            <summary>
            Sql
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.CharacterSet.Xml">
            <summary>
            Xml
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.DatabaseMirroringRole">
            <summary>
            Database mirroring role types for SQL Server 2005
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DatabaseMirroringRole.Unknown">
            <summary>
            Unknown
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DatabaseMirroringRole.Witness">
            <summary>
            Witness
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DatabaseMirroringRole.Partner">
            <summary>
            Partner
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DatabaseMirroringRole.All">
            <summary>
            All
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.PageVerifyMode">
            <summary>
            Database page verify mode for SQL Server 2005
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PageVerifyMode.Unknown">
            <summary>
            Unknown
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PageVerifyMode.Checksum">
            <summary>
            Checksum
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PageVerifyMode.TornPageDetection">
            <summary>
            TornPageDetection
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PageVerifyMode.None">
            <summary>
            None
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.EncryptionMode">
            <summary>
            Encryption mode for service broker endpoints
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EncryptionMode.Unknown">
            <summary>
            Unknown
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EncryptionMode.Disabled">
            <summary>
            Disabled
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EncryptionMode.Supported">
            <summary>
            Supported
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EncryptionMode.Required">
            <summary>
            Required
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.HttpPorts">
            <summary>
            Listening port types associated with an endpoint.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.HttpPorts.None">
            <summary>
            None
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.HttpPorts.Clear">
            <summary>
            Clear
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.HttpPorts.Ssl">
            <summary>
            Ssl
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.SoapLoginType">
            <summary>
            Possible values for LOGIN_TYPE option in SOAP endpoint
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SoapLoginType.Unknown">
            <summary>
            Unknown
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SoapLoginType.Mixed">
            <summary>
            Mixed
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SoapLoginType.Windows">
            <summary>
            Windows
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ParameterizationOption">
            <summary>
            Parameterization options for database
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ParameterizationOption.Unknown">
            <summary>
            Unknown
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ParameterizationOption.Simple">
            <summary>
            Simple
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ParameterizationOption.Forced">
            <summary>
            Forced
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ServiceBrokerEncryptionAlgorithm">
            <summary>
            Possible encryption algorithm options for service broker endpoints
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ServiceBrokerEncryptionAlgorithm.Unknown">
            <summary>
            Unknown
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ServiceBrokerEncryptionAlgorithm.Aes">
            <summary>
            Aes
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ServiceBrokerEncryptionAlgorithm.RC4">
            <summary>
            RC4
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ServiceBrokerOption">
            <summary>
            Possible service broker options for database
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ServiceBrokerOption.Unknown">
            <summary>
            Unknown
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ServiceBrokerOption.EnableBroker">
            <summary>
            EnableBroker
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ServiceBrokerOption.DisableBroker">
            <summary>
            DisableBroker
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ServiceBrokerOption.NewBroker">
            <summary>
            NewBroker
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ServiceBrokerOption.ErrorBrokerConversations">
            <summary>
            ErrorBrokerConversations
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.SoapFormat">
            <summary>
            Possible webmethod formats for SOAP endpoints
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SoapFormat.Unknown">
            <summary>
            Unknown
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SoapFormat.AllResults">
            <summary>
            AllResults
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SoapFormat.RowsetsOnly">
            <summary>
            RowsetsOnly
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SoapFormat.None">
            <summary>
            None
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.SoapSchema">
            <summary>
            Possible schema options for webmethods in SOAP endpoints
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SoapSchema.Unknown">
            <summary>
            Unknown
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SoapSchema.None">
            <summary>
            None
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SoapSchema.Standard">
            <summary>
            Standard
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SoapSchema.Default">
            <summary>
            Default
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.AssemblyPermissionSet">
            <summary>
            Possible permission sets for an assembly
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AssemblyPermissionSet.Unknown">
            <summary>
            Unknown
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AssemblyPermissionSet.Safe">
            <summary>
            Safe
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AssemblyPermissionSet.ExternalAccess">
            <summary>
            ExternalAccess
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AssemblyPermissionSet.Unsafe">
            <summary>
            Unsafe
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.AsymmetricKeyAlgorithm">
            <summary>
            Algorithm for an asymmetric keys
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AsymmetricKeyAlgorithm.Unknown">
            <summary>
            Unknown
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AsymmetricKeyAlgorithm.Rsa512">
            <summary>
            Rsa512
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AsymmetricKeyAlgorithm.Rsa1024">
            <summary>
            Rsa1024
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AsymmetricKeyAlgorithm.Rsa2048">
            <summary>
            Rsa2048
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.SymmetricKeyAlgorithm">
            <summary>
            Algorithms used for symmetric keys
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SymmetricKeyAlgorithm.Unknown">
            <summary>
            Unknown
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SymmetricKeyAlgorithm.Des">
            <summary>
            Des
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SymmetricKeyAlgorithm.TripleDes">
            <summary>
            TripleDes
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SymmetricKeyAlgorithm.RC2">
            <summary>
            RC2
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SymmetricKeyAlgorithm.RC4">
            <summary>
            RC4
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SymmetricKeyAlgorithm.DesX">
            <summary>
            DesX
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SymmetricKeyAlgorithm.Aes128">
            <summary>
            Aes128
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SymmetricKeyAlgorithm.Aes192">
            <summary>
            Aes192
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SymmetricKeyAlgorithm.Aes256">
            <summary>
            Aes256
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SymmetricKeyAlgorithm.RC4128">
            <summary>
            RC4128
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.AuditActionGroupType">
            <summary>
            SQL Server audit action groups
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.None">
            <summary>
            None
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.SuccessfulLogin">
            <summary>
            SuccessfulLogin
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.Logout">
            <summary>
            Logout
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.ServerStateChange">
            <summary>
            ServerStateChange
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.FailedLogin">
            <summary>
            FailedLogin
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.LoginChangePassword">
            <summary>
            LoginChangePassword
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.ServerRoleMemberChange">
            <summary>
            ServerRoleMemberChange
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.ServerPrincipalImpersonation">
            <summary>
            ServerPrincipalImpersonation
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.ServerObjectOwnershipChange">
            <summary>
            ServerObjectOwnershipChange
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.DatabaseMirroringLogin">
            <summary>
            DatabaseMirroringLogin
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.BrokerLogin">
            <summary>
            BrokerLogin
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.ServerPermissionChange">
            <summary>
            ServerPermissionChange
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.ServerObjectPermissionChange">
            <summary>
            ServerObjectPermissionChange
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.ServerOperation">
            <summary>
            ServerOperation
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.TraceChange">
            <summary>
            TraceChange
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.ServerObjectChange">
            <summary>
            ServerObjectChange
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.ServerPrincipalChange">
            <summary>
            ServerPrincipalChange
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.DatabasePermissionChange">
            <summary>
            DatabasePermissionChange
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.SchemaObjectPermissionChange">
            <summary>
            SchemaObjectPermissionChange
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.DatabaseRoleMemberChange">
            <summary>
            DatabaseRoleMemberChange
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.ApplicationRoleChangePassword">
            <summary>
            ApplicationRoleChangePassword
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.SchemaObjectAccess">
            <summary>
            SchemaObjectAccess
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.BackupRestore">
            <summary>
            BackupRestore
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.Dbcc">
            <summary>
            Dbcc
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.AuditChange">
            <summary>
            AuditChange
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.DatabaseChange">
            <summary>
            DatabaseChange
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.DatabaseObjectChange">
            <summary>
            DatabaseObjectChange
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.DatabasePrincipalChange">
            <summary>
            DatabasePrincipalChange
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.SchemaObjectChange">
            <summary>
            SchemaObjectChange
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.DatabasePrincipalImpersonation">
            <summary>
            DatabasePrincipalImpersonation
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.DatabaseObjectOwnershipChange">
            <summary>
            DatabaseObjectOwnershipChange
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.DatabaseOwnershipChange">
            <summary>
            DatabaseOwnershipChange
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.SchemaObjectOwnershipChange">
            <summary>
            SchemaObjectOwnershipChange
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.DatabaseObjectPermissionChange">
            <summary>
            DatabaseObjectPermissionChange
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.DatabaseOperation">
            <summary>
            DatabaseOperation
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.DatabaseObjectAccess">
            <summary>
            DatabaseObjectAccess
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.SuccessfulDatabaseAuthenticationGroup">
            <summary>
            SuccessfulDatabaseAuthenticationGroup
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.FailedDatabaseAuthenticationGroup">
            <summary>
            FailedDatabaseAuthenticationGroup
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.DatabaseLogoutGroup">
            <summary>
            DatabaseLogoutGroup
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.UserChangePasswordGroup">
            <summary>
            UserChangePasswordGroup
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.UserDefinedAuditGroup">
            <summary>
            UserDefinedAuditGroup
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.TransactionBegin">
            <summary>
            TransactionBegin
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.TransactionCommit">
            <summary>
            TransactionCommit
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.TransactionRollback">
            <summary>
            TransactionRollback
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.StatementRollback">
            <summary>
            StatementRollback
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditActionGroupType.TransactionGroup">
            <summary>
            TransactionGroup
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.AuditTarget">
            <summary>
            SQL Server audit targets
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditTarget.File">
            <summary>
            File
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditTarget.ApplicationLog">
            <summary>
            ApplicationLog
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuditTarget.SecurityLog">
            <summary>
            SecurityLog
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ChangeTrackingOption">
            <summary>
            Change tracking options
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ChangeTrackingOption.Unknown">
            <summary>
            Unknown
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ChangeTrackingOption.Auto">
            <summary>
            Auto
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ChangeTrackingOption.Manual">
            <summary>
            Manual
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ChangeTrackingOption.Off">
            <summary>
            Off
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.SymmetricKeyCreationDisposition">
            <summary>
            Asymmetric key creation disposition
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SymmetricKeyCreationDisposition.Unknown">
            <summary>
            Unknown
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SymmetricKeyCreationDisposition.CreateNew">
            <summary>
            CreateNew
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SymmetricKeyCreationDisposition.OpenExisting">
            <summary>
            OpenExisting
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.DatabaseAuditAction">
            <summary>
            Database-level auditable action
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DatabaseAuditAction.Select">
            <summary>
            Select
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DatabaseAuditAction.Update">
            <summary>
            Update
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DatabaseAuditAction.Insert">
            <summary>
            Insert
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DatabaseAuditAction.Delete">
            <summary>
            Delete
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DatabaseAuditAction.Execute">
            <summary>
            Execute
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DatabaseAuditAction.Receive">
            <summary>
            Receive
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DatabaseAuditAction.References">
            <summary>
            References
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.RecoveryMode">
            <summary>
            Database recovery mode
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.RecoveryMode.Unknown">
            <summary>
            Unknown
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.RecoveryMode.Simple">
            <summary>
            Simple
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.RecoveryMode.BulkLogged">
            <summary>
            BulkLogged
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.RecoveryMode.Full">
            <summary>
            Full
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.DelayedDurabilityMode">
            <summary>
            Database Delayed Durability mode
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DelayedDurabilityMode.Disabled">
            <summary>
            Disabled
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DelayedDurabilityMode.Allowed">
            <summary>
            Allowed
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DelayedDurabilityMode.Forced">
            <summary>
            Forced
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.UserAccessOption">
            <summary>
            User access mode for the database
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.UserAccessOption.Multiple">
            <summary>
            Multiple
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.UserAccessOption.Restricted">
            <summary>
            Restricted
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.UserAccessOption.Single">
            <summary>
            Single
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.CompressionLevel">
            <summary>
            Compression level for data
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.CompressionLevel.None">
            <summary>
            None
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.CompressionLevel.Row">
            <summary>
            Row
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.CompressionLevel.Page">
            <summary>
            Page
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.CompressionLevel.ColumnStore">
            <summary>
            ColumnStore
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.CompressionLevel.ColumnStoreArchive">
            <summary>
            ColumnStoreArchive
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.Degree">
            <summary>
            Grid density level in spatial index
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.Degree.None">
            <summary>
            None
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.Degree.Low">
            <summary>
            Low
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.Degree.Medium">
            <summary>
            Medium
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.Degree.High">
            <summary>
            High
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.State">
            <summary>
            State of the endpoint
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.State.Unknown">
            <summary>
            Unknown
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.State.Disabled">
            <summary>
            Disabled
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.State.Started">
            <summary>
            Started
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.State.Stopped">
            <summary>
            Stopped
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.EventGroupType">
            <summary>
            Sql event groups
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.Unknown">
            <summary>
            Unknown
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlApplicationRoleEvents">
            <summary>
            DdlApplicationRoleEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlAssemblyEvents">
            <summary>
            DdlAssemblyEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlAuthorizationDatabaseEvents">
            <summary>
            DdlAuthorizationDatabaseEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlAvailabilityGroupEvents">
            <summary>
            DdlAvailabilityGroupEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlCertificateEvents">
            <summary>
            DdlCertificateEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlContractEvents">
            <summary>
            DdlContractEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlDatabaseAuditEvents">
            <summary>
            DdlDatabaseAuditEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlDatabaseLevelEvents">
            <summary>
            DdlDatabaseLevelEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlDatabaseSecurityEvents">
            <summary>
            DdlDatabaseSecurityEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlEventNotificationEvents">
            <summary>
            DdlEventNotificationEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlFunctionEvents">
            <summary>
            DdlFunctionEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlGdrDatabaseEvents">
            <summary>
            DdlGdrDatabaseEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlIndexEvents">
            <summary>
            DdlIndexEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlMessageTypeEvents">
            <summary>
            DdlMessageTypeEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlPartitionEvents">
            <summary>
            DdlPartitionEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlPartitionFunctionEvents">
            <summary>
            DdlPartitionFunctionEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlPartitionSchemeEvents">
            <summary>
            DdlPartitionSchemeEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlProcedureEvents">
            <summary>
            DdlProcedureEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlQueueEvents">
            <summary>
            DdlQueueEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlRemoteServiceBindingEvents">
            <summary>
            DdlRemoteServiceBindingEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlRoleEvents">
            <summary>
            DdlRoleEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlRouteEvents">
            <summary>
            DdlRouteEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlSchemaEvents">
            <summary>
            DdlSchemaEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlSecurityPolicyEvents">
            <summary>
            DdlSecurityPolicyEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlColumnMasterKeyEvents">
            <summary>
            DdlColumnMasterKeyEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlColumnEncryptionKeyEvents">
            <summary>
            DdlColumnEncryptionKeyEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlExternalResourcePoolEvents">
            <summary>
            DdlExternalResourcePoolEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlExternalLibraryEvents">
            <summary>
            DdlExternalLibraryEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlServiceEvents">
            <summary>
            DdlServiceEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlSsbEvents">
            <summary>
            DdlSsbEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlStatisticsEvents">
            <summary>
            DdlStatisticsEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlSynonymEvents">
            <summary>
            DdlSynonymEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlTableEvents">
            <summary>
            DdlTableEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlTableViewEvents">
            <summary>
            DdlTableViewEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlTriggerEvents">
            <summary>
            DdlTriggerEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlTypeEvents">
            <summary>
            DdlTypeEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlUserEvents">
            <summary>
            DdlUserEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlViewEvents">
            <summary>
            DdlViewEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlXmlSchemaCollectionEvents">
            <summary>
            DdlXmlSchemaCollectionEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlAuthorizationServerEvents">
            <summary>
            DdlAuthorizationServerEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlEndpointEvents">
            <summary>
            DdlEndpointEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlEvents">
            <summary>
            DdlEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlGdrServerEvents">
            <summary>
            DdlGdrServerEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlLoginEvents">
            <summary>
            DdlLoginEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlServerLevelEvents">
            <summary>
            DdlServerLevelEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlServerSecurityEvents">
            <summary>
            DdlServerSecurityEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlAsymmetricKeyEvents">
            <summary>
            DdlAsymmetricKeyEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlBrokerPriorityEvents">
            <summary>
            DdlBrokerPriorityEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlCryptoSignatureEvents">
            <summary>
            DdlCryptoSignatureEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlDatabaseAuditSpecificationEvents">
            <summary>
            DdlDatabaseAuditSpecificationEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlDatabaseEncryptionKeyEvents">
            <summary>
            DdlDatabaseEncryptionKeyEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlDefaultEvents">
            <summary>
            DdlDefaultEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlExtendedPropertyEvents">
            <summary>
            DdlExtendedPropertyEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlFullTextCatalogEvents">
            <summary>
            DdlFullTextCatalogEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlFullTextStopListEvents">
            <summary>
            DdlFullTextStopListEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlMasterKeyEvents">
            <summary>
            DdlMasterKeyEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlPlanGuideEvents">
            <summary>
            DdlPlanGuideEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlRuleEvents">
            <summary>
            DdlRuleEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlSearchPropertyListEvents">
            <summary>
            DdlSearchPropertyListEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlSequenceEvents">
            <summary>
            DdlSequenceEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlSymmetricKeyEvents">
            <summary>
            DdlSymmetricKeyEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlCredentialEvents">
            <summary>
            DdlCredentialEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlDatabaseEvents">
            <summary>
            DdlDatabaseEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlCryptographicProviderEvents">
            <summary>
            DdlCryptographicProviderEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlEventSessionEvents">
            <summary>
            DdlEventSessionEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlExtendedProcedureEvents">
            <summary>
            DdlExtendedProcedureEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlLinkedServerEvents">
            <summary>
            DdlLinkedServerEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlLinkedServerLoginEvents">
            <summary>
            DdlLinkedServerLoginEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlMessageEvents">
            <summary>
            DdlMessageEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlRemoteServerEvents">
            <summary>
            DdlRemoteServerEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlResourceGovernorEvents">
            <summary>
            DdlResourceGovernorEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlResourcePool">
            <summary>
            DdlResourcePool
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlServerAuditEvents">
            <summary>
            DdlServerAuditEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlServerAuditSpecificationEvents">
            <summary>
            DdlServerAuditSpecificationEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlServiceMasterKeyEvents">
            <summary>
            DdlServiceMasterKeyEvents
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.DdlWorkloadGroup">
            <summary>
            DdlWorkloadGroup
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.TrcClr">
            <summary>
            TrcClr
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.TrcDatabase">
            <summary>
            TrcDatabase
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.TrcDeprecation">
            <summary>
            TrcDeprecation
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.TrcErrorsAndWarnings">
            <summary>
            TrcErrorsAndWarnings
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.TrcFullText">
            <summary>
            TrcFullText
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.TrcLocks">
            <summary>
            TrcLocks
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.TrcObjects">
            <summary>
            TrcObjects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.TrcOledb">
            <summary>
            TrcOledb
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.TrcPerformance">
            <summary>
            TrcPerformance
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.TrcQueryNotifications">
            <summary>
            TrcQueryNotifications
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.TrcSecurityAudit">
            <summary>
            TrcSecurityAudit
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.TrcServer">
            <summary>
            TrcServer
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.TrcStoredProcedures">
            <summary>
            TrcStoredProcedures
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.TrcTSql">
            <summary>
            TrcTSql
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.TrcUserConfigurable">
            <summary>
            TrcUserConfigurable
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventGroupType.TrcAllEvents">
            <summary>
            TrcAllEvents
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.EventType">
            <summary>
            Sql Server event types
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.Unknown">
            <summary>
            Unknown
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AddRoleMember">
            <summary>
            AddRoleMember
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterApplicationRole">
            <summary>
            AlterApplicationRole
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterAssembly">
            <summary>
            AlterAssembly
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterAuthorizationDatabase">
            <summary>
            AlterAuthorizationDatabase
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterAvailabilityGroup">
            <summary>
            AlterAvailabilityGroup
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterCertificate">
            <summary>
            AlterCertificate
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterColumnEncryptionKey">
            <summary>
            AlterColumnEncryptionKey
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterDatabaseAudit">
            <summary>
            AlterDatabaseAudit
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterFunction">
            <summary>
            AlterFunction
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterIndex">
            <summary>
            AlterIndex
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterMessageType">
            <summary>
            AlterMessageType
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterPartitionFunction">
            <summary>
            AlterPartitionFunction
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterPartitionScheme">
            <summary>
            AlterPartitionScheme
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterProcedure">
            <summary>
            AlterProcedure
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterQueue">
            <summary>
            AlterQueue
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterRemoteServiceBinding">
            <summary>
            AlterRemoteServiceBinding
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterRole">
            <summary>
            AlterRole
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterRoute">
            <summary>
            AlterRoute
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterSchema">
            <summary>
            AlterSchema
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterSecurityPolicy">
            <summary>
            AlterSecurityPolicy
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterService">
            <summary>
            AlterService
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterTable">
            <summary>
            AlterTable
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterTrigger">
            <summary>
            AlterTrigger
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterUser">
            <summary>
            AlterUser
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterView">
            <summary>
            AlterView
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterXmlSchemaCollection">
            <summary>
            AlterXmlSchemaCollection
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateApplicationRole">
            <summary>
            CreateApplicationRole
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateAssembly">
            <summary>
            CreateAssembly
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateAvailabilityGroup">
            <summary>
            CreateAvailabilityGroup
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateCertificate">
            <summary>
            CreateCertificate
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateContract">
            <summary>
            CreateContract
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateColumnEncryptionKey">
            <summary>
            CreateColumnEncryptionKey
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateColumnMasterKey">
            <summary>
            CreateColumnMasterKey
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateDatabaseAudit">
            <summary>
            CreateDatabaseAudit
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateEventNotification">
            <summary>
            CreateEventNotification
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateFunction">
            <summary>
            CreateFunction
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateIndex">
            <summary>
            CreateIndex
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateMessageType">
            <summary>
            CreateMessageType
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreatePartitionFunction">
            <summary>
            CreatePartitionFunction
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreatePartitionScheme">
            <summary>
            CreatePartitionScheme
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateProcedure">
            <summary>
            CreateProcedure
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateQueue">
            <summary>
            CreateQueue
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateRemoteServiceBinding">
            <summary>
            CreateRemoteServiceBinding
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateRole">
            <summary>
            CreateRole
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateRoute">
            <summary>
            CreateRoute
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateSchema">
            <summary>
            CreateSchema
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateSecurityPolicy">
            <summary>
            CreateSecurityPolicy
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateService">
            <summary>
            CreateService
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateStatistics">
            <summary>
            CreateStatistics
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateSynonym">
            <summary>
            CreateSynonym
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateTable">
            <summary>
            CreateTable
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateTrigger">
            <summary>
            CreateTrigger
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateType">
            <summary>
            CreateType
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateUser">
            <summary>
            CreateUser
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateView">
            <summary>
            CreateView
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateXmlIndex">
            <summary>
            CreateXmlIndex
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateXmlSchemaCollection">
            <summary>
            CreateXmlSchemaCollection
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DenyDatabase">
            <summary>
            DenyDatabase
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropApplicationRole">
            <summary>
            DropApplicationRole
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropAvailabilityGroup">
            <summary>
            DropAvailabilityGroup
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropAssembly">
            <summary>
            DropAssembly
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropCertificate">
            <summary>
            DropCertificate
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropColumnEncryptionKey">
            <summary>
            DropColumnEncryptionKey
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterDatabaseScopedConfiguration">
            <summary>
            AlterDatabaseScopedConfiguration
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateExternalResourcePool">
            <summary>
            CreateExternalResourcePool
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterExternalResourcePool">
            <summary>
            AlterExternalResourcePool
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropExternalResourcePool">
            <summary>
            DropExternalResourcePool
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateExternalLibrary">
            <summary>
            CreateExternalLibrary
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterExternalLibrary">
            <summary>
            AlterExternalLibrary
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropExternalLibrary">
            <summary>
            DropExternalLibrary
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropColumnMasterKey">
            <summary>
            DropColumnMasterKey
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropContract">
            <summary>
            DropContract
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropDatabaseAudit">
            <summary>
            DropDatabaseAudit
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropEventNotification">
            <summary>
            DropEventNotification
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropFunction">
            <summary>
            DropFunction
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropIndex">
            <summary>
            DropIndex
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropMessageType">
            <summary>
            DropMessageType
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropPartitionFunction">
            <summary>
            DropPartitionFunction
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropPartitionScheme">
            <summary>
            DropPartitionScheme
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropProcedure">
            <summary>
            DropProcedure
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropQueue">
            <summary>
            DropQueue
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropRemoteServiceBinding">
            <summary>
            DropRemoteServiceBinding
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropRole">
            <summary>
            DropRole
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropRoleMember">
            <summary>
            DropRoleMember
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropRoute">
            <summary>
            DropRoute
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropSchema">
            <summary>
            DropSchema
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropSecurityPolicy">
            <summary>
            DropSecurityPolicy
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropService">
            <summary>
            DropService
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropStatistics">
            <summary>
            DropStatistics
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropSynonym">
            <summary>
            DropSynonym
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropTable">
            <summary>
            DropTable
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropTrigger">
            <summary>
            DropTrigger
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropType">
            <summary>
            DropType
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropUser">
            <summary>
            DropUser
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropView">
            <summary>
            DropView
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropXmlSchemaCollection">
            <summary>
            DropXmlSchemaCollection
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.GrantDatabase">
            <summary>
            GrantDatabase
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.RevokeDatabase">
            <summary>
            RevokeDatabase
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.UpdateStatistics">
            <summary>
            UpdateStatistics
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AddServerRoleMember">
            <summary>
            AddServerRoleMember
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterAuthorizationServer">
            <summary>
            AlterAuthorizationServer
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterDatabase">
            <summary>
            AlterDatabase
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterEndpoint">
            <summary>
            AlterEndpoint
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterLogin">
            <summary>
            AlterLogin
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateDatabase">
            <summary>
            CreateDatabase
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateEndpoint">
            <summary>
            CreateEndpoint
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateLogin">
            <summary>
            CreateLogin
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DenyServer">
            <summary>
            DenyServer
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropDatabase">
            <summary>
            DropDatabase
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropEndpoint">
            <summary>
            DropEndpoint
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropLogin">
            <summary>
            DropLogin
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropServerRoleMember">
            <summary>
            DropServerRoleMember
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.GrantServer">
            <summary>
            GrantServer
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.RevokeServer">
            <summary>
            RevokeServer
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AddSignature">
            <summary>
            AddSignature
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AddSignatureSchemaObject">
            <summary>
            AddSignatureSchemaObject
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterAsymmetricKey">
            <summary>
            AlterAsymmetricKey
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterBrokerPriority">
            <summary>
            AlterBrokerPriority
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterDatabaseAuditSpecification">
            <summary>
            AlterDatabaseAuditSpecification
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterDatabaseEncryptionKey">
            <summary>
            AlterDatabaseEncryptionKey
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterExtendedProperty">
            <summary>
            AlterExtendedProperty
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterFullTextCatalog">
            <summary>
            AlterFullTextCatalog
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterFullTextIndex">
            <summary>
            AlterFullTextIndex
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterFullTextStopList">
            <summary>
            AlterFullTextStopList
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterMasterKey">
            <summary>
            AlterMasterKey
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterPlanGuide">
            <summary>
            AlterPlanGuide
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterSymmetricKey">
            <summary>
            AlterSymmetricKey
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.BindDefault">
            <summary>
            BindDefault
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.BindRule">
            <summary>
            BindRule
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateAsymmetricKey">
            <summary>
            CreateAsymmetricKey
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateBrokerPriority">
            <summary>
            CreateBrokerPriority
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateDatabaseAuditSpecification">
            <summary>
            CreateDatabaseAuditSpecification
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateDatabaseEncryptionKey">
            <summary>
            CreateDatabaseEncryptionKey
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateDefault">
            <summary>
            CreateDefault
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateExtendedProperty">
            <summary>
            CreateExtendedProperty
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateFullTextCatalog">
            <summary>
            CreateFullTextCatalog
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateFullTextIndex">
            <summary>
            CreateFullTextIndex
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateFullTextStopList">
            <summary>
            CreateFullTextStopList
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateMasterKey">
            <summary>
            CreateMasterKey
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreatePlanGuide">
            <summary>
            CreatePlanGuide
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateRule">
            <summary>
            CreateRule
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateSpatialIndex">
            <summary>
            CreateSpatialIndex
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateSymmetricKey">
            <summary>
            CreateSymmetricKey
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropAsymmetricKey">
            <summary>
            DropAsymmetricKey
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropBrokerPriority">
            <summary>
            DropBrokerPriority
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropDatabaseAuditSpecification">
            <summary>
            DropDatabaseAuditSpecification
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropDatabaseEncryptionKey">
            <summary>
            DropDatabaseEncryptionKey
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropDefault">
            <summary>
            DropDefault
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropExtendedProperty">
            <summary>
            DropExtendedProperty
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropFullTextCatalog">
            <summary>
            DropFullTextCatalog
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropFullTextIndex">
            <summary>
            DropFullTextIndex
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropFullTextStopList">
            <summary>
            DropFullTextStopList
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropMasterKey">
            <summary>
            DropMasterKey
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropPlanGuide">
            <summary>
            DropPlanGuide
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropRule">
            <summary>
            DropRule
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropSignature">
            <summary>
            DropSignature
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropSignatureSchemaObject">
            <summary>
            DropSignatureSchemaObject
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropSymmetricKey">
            <summary>
            DropSymmetricKey
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.Rename">
            <summary>
            Rename
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.UnbindDefault">
            <summary>
            UnbindDefault
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.UnbindRule">
            <summary>
            UnbindRule
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterCredential">
            <summary>
            AlterCredential
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterCryptographicProvider">
            <summary>
            AlterCryptographicProvider
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterEventSession">
            <summary>
            AlterEventSession
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterInstance">
            <summary>
            AlterInstance
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterLinkedServer">
            <summary>
            AlterLinkedServer
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterMessage">
            <summary>
            AlterMessage
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterRemoteServer">
            <summary>
            AlterRemoteServer
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterResourceGovernorConfig">
            <summary>
            AlterResourceGovernorConfig
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterResourcePool">
            <summary>
            AlterResourcePool
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterServerAudit">
            <summary>
            AlterServerAudit
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterServerAuditSpecification">
            <summary>
            AlterServerAuditSpecification
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterServiceMasterKey">
            <summary>
            AlterServiceMasterKey
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterWorkloadGroup">
            <summary>
            AlterWorkloadGroup
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateCredential">
            <summary>
            CreateCredential
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateCryptographicProvider">
            <summary>
            CreateCryptographicProvider
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateEventSession">
            <summary>
            CreateEventSession
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateExtendedProcedure">
            <summary>
            CreateExtendedProcedure
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateLinkedServer">
            <summary>
            CreateLinkedServer
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateLinkedServerLogin">
            <summary>
            CreateLinkedServerLogin
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateMessage">
            <summary>
            CreateMessage
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateRemoteServer">
            <summary>
            CreateRemoteServer
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateResourcePool">
            <summary>
            CreateResourcePool
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateServerAudit">
            <summary>
            CreateServerAudit
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateServerAuditSpecification">
            <summary>
            CreateServerAuditSpecification
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateWorkloadGroup">
            <summary>
            CreateWorkloadGroup
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropCredential">
            <summary>
            DropCredential
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropCryptographicProvider">
            <summary>
            DropCryptographicProvider
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropEventSession">
            <summary>
            DropEventSession
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropExtendedProcedure">
            <summary>
            DropExtendedProcedure
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropLinkedServer">
            <summary>
            DropLinkedServer
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropLinkedServerLogin">
            <summary>
            DropLinkedServerLogin
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropMessage">
            <summary>
            DropMessage
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropRemoteServer">
            <summary>
            DropRemoteServer
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropResourcePool">
            <summary>
            DropResourcePool
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropServerAudit">
            <summary>
            DropServerAudit
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropServerAuditSpecification">
            <summary>
            DropServerAuditSpecification
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropWorkloadGroup">
            <summary>
            DropWorkloadGroup
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AssemblyLoad">
            <summary>
            AssemblyLoad
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditAddDbUserEvent">
            <summary>
            AuditAddDbUserEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditAddLoginEvent">
            <summary>
            AuditAddLoginEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditAddLoginToServerRoleEvent">
            <summary>
            AuditAddLoginToServerRoleEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditAddMemberToDbRoleEvent">
            <summary>
            AuditAddMemberToDbRoleEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditAddRoleEvent">
            <summary>
            AuditAddRoleEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditAppRoleChangePasswordEvent">
            <summary>
            AuditAppRoleChangePasswordEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditBackupRestoreEvent">
            <summary>
            AuditBackupRestoreEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditChangeAuditEvent">
            <summary>
            AuditChangeAuditEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditChangeDatabaseOwner">
            <summary>
            AuditChangeDatabaseOwner
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditDatabaseManagementEvent">
            <summary>
            AuditDatabaseManagementEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditDatabaseObjectAccessEvent">
            <summary>
            AuditDatabaseObjectAccessEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditDatabaseObjectGdrEvent">
            <summary>
            AuditDatabaseObjectGdrEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditDatabaseObjectManagementEvent">
            <summary>
            AuditDatabaseObjectManagementEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditDatabaseObjectTakeOwnershipEvent">
            <summary>
            AuditDatabaseObjectTakeOwnershipEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditDatabaseOperationEvent">
            <summary>
            AuditDatabaseOperationEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditDatabasePrincipalImpersonationEvent">
            <summary>
            AuditDatabasePrincipalImpersonationEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditDatabasePrincipalManagementEvent">
            <summary>
            AuditDatabasePrincipalManagementEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditDatabaseScopeGdrEvent">
            <summary>
            AuditDatabaseScopeGdrEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditDbccEvent">
            <summary>
            AuditDbccEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditLogin">
            <summary>
            AuditLogin
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditLoginChangePasswordEvent">
            <summary>
            AuditLoginChangePasswordEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditLoginChangePropertyEvent">
            <summary>
            AuditLoginChangePropertyEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditLoginFailed">
            <summary>
            AuditLoginFailed
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditLoginGdrEvent">
            <summary>
            AuditLoginGdrEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditLogout">
            <summary>
            AuditLogout
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditSchemaObjectAccessEvent">
            <summary>
            AuditSchemaObjectAccessEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditSchemaObjectGdrEvent">
            <summary>
            AuditSchemaObjectGdrEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditSchemaObjectManagementEvent">
            <summary>
            AuditSchemaObjectManagementEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditSchemaObjectTakeOwnershipEvent">
            <summary>
            AuditSchemaObjectTakeOwnershipEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditServerAlterTraceEvent">
            <summary>
            AuditServerAlterTraceEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditServerObjectGdrEvent">
            <summary>
            AuditServerObjectGdrEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditServerObjectManagementEvent">
            <summary>
            AuditServerObjectManagementEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditServerObjectTakeOwnershipEvent">
            <summary>
            AuditServerObjectTakeOwnershipEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditServerOperationEvent">
            <summary>
            AuditServerOperationEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditServerPrincipalImpersonationEvent">
            <summary>
            AuditServerPrincipalImpersonationEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditServerPrincipalManagementEvent">
            <summary>
            AuditServerPrincipalManagementEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditServerScopeGdrEvent">
            <summary>
            AuditServerScopeGdrEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.BlockedProcessReport">
            <summary>
            BlockedProcessReport
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.BrokerQueueDisabled">
            <summary>
            BrokerQueueDisabled
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DataFileAutoGrow">
            <summary>
            DataFileAutoGrow
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DataFileAutoShrink">
            <summary>
            DataFileAutoShrink
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DatabaseMirroringStateChange">
            <summary>
            DatabaseMirroringStateChange
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DeadlockGraph">
            <summary>
            DeadlockGraph
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DeprecationAnnouncement">
            <summary>
            DeprecationAnnouncement
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DeprecationFinalSupport">
            <summary>
            DeprecationFinalSupport
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.ErrorLog">
            <summary>
            ErrorLog
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.EventLog">
            <summary>
            EventLog
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.Exception">
            <summary>
            Exception
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.ExchangeSpillEvent">
            <summary>
            ExchangeSpillEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.ExecutionWarnings">
            <summary>
            ExecutionWarnings
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.FtCrawlAborted">
            <summary>
            FtCrawlAborted
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.FtCrawlStarted">
            <summary>
            FtCrawlStarted
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.FtCrawlStopped">
            <summary>
            FtCrawlStopped
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.HashWarning">
            <summary>
            HashWarning
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.LockDeadlock">
            <summary>
            LockDeadlock
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.LockDeadlockChain">
            <summary>
            LockDeadlockChain
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.LockEscalation">
            <summary>
            LockEscalation
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.LogFileAutoGrow">
            <summary>
            LogFileAutoGrow
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.LogFileAutoShrink">
            <summary>
            LogFileAutoShrink
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.MissingColumnStatistics">
            <summary>
            MissingColumnStatistics
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.MissingJoinPredicate">
            <summary>
            MissingJoinPredicate
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.MountTape">
            <summary>
            MountTape
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.ObjectAltered">
            <summary>
            ObjectAltered
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.ObjectCreated">
            <summary>
            ObjectCreated
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.ObjectDeleted">
            <summary>
            ObjectDeleted
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.OledbCallEvent">
            <summary>
            OledbCallEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.OledbDataReadEvent">
            <summary>
            OledbDataReadEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.OledbErrors">
            <summary>
            OledbErrors
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.OledbProviderInformation">
            <summary>
            OledbProviderInformation
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.OledbQueryInterfaceEvent">
            <summary>
            OledbQueryInterfaceEvent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.QnDynamics">
            <summary>
            QnDynamics
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.QnParameterTable">
            <summary>
            QnParameterTable
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.QnSubscription">
            <summary>
            QnSubscription
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.QnTemplate">
            <summary>
            QnTemplate
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.QueueActivation">
            <summary>
            QueueActivation
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.ServerMemoryChange">
            <summary>
            ServerMemoryChange
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.ShowPlanAllForQueryCompile">
            <summary>
            ShowPlanAllForQueryCompile
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.ShowPlanXmlForQueryCompile">
            <summary>
            ShowPlanXmlForQueryCompile
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.ShowPlanXml">
            <summary>
            ShowPlanXml
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.ShowPlanXmlStatisticsProfile">
            <summary>
            ShowPlanXmlStatisticsProfile
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.SortWarnings">
            <summary>
            SortWarnings
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.SpCacheInsert">
            <summary>
            SpCacheInsert
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.SpCacheMiss">
            <summary>
            SpCacheMiss
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.SpCacheRemove">
            <summary>
            SpCacheRemove
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.SpRecompile">
            <summary>
            SpRecompile
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.SqlStmtRecompile">
            <summary>
            SqlStmtRecompile
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.TraceFileClose">
            <summary>
            TraceFileClose
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.UserErrorMessage">
            <summary>
            UserErrorMessage
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.UserConfigurable0">
            <summary>
            UserConfigurable0
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.UserConfigurable1">
            <summary>
            UserConfigurable1
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.UserConfigurable2">
            <summary>
            UserConfigurable2
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.UserConfigurable3">
            <summary>
            UserConfigurable3
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.UserConfigurable4">
            <summary>
            UserConfigurable4
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.UserConfigurable5">
            <summary>
            UserConfigurable5
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.UserConfigurable6">
            <summary>
            UserConfigurable6
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.UserConfigurable7">
            <summary>
            UserConfigurable7
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.UserConfigurable8">
            <summary>
            UserConfigurable8
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.UserConfigurable9">
            <summary>
            UserConfigurable9
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.XQueryStaticType">
            <summary>
            XQueryStaticType
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AuditFullText">
            <summary>
            AuditFullText
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.BitmapWarning">
            <summary>
            BitmapWarning
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CpuThresholdExceeded">
            <summary>
            CpuThresholdExceeded
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DatabaseSuspectDataPage">
            <summary>
            DatabaseSuspectDataPage
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateSequence">
            <summary>
            CreateSequence
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterSequence">
            <summary>
            AlterSequence
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropSequence">
            <summary>
            DropSequence
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateServerRole">
            <summary>
            CreateServerRole
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterServerRole">
            <summary>
            AlterServerRole
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropServerRole">
            <summary>
            DropServerRole
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterServerConfiguration">
            <summary>
            AlterServerConfiguration
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.CreateSearchPropertyList">
            <summary>
            CreateSearchPropertyList
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.AlterSearchPropertyList">
            <summary>
            AlterSearchPropertyList
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventType.DropSearchPropertyList">
            <summary>
            DropSearchPropertyList
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.DataSourceType">
            <summary>
            External data source type options
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DataSourceType.Hadoop">
            <summary>
            Hadoop
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DataSourceType.RDBMS">
            <summary>
            RDBMS
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DataSourceType.ShardMapManager">
            <summary>
            ShardMapManager
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.DataSourceType.BlobStorage">
            <summary>
            BlobStorage
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.FileFormatType">
            <summary>
            External file format type options.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.FileFormatType.DelimitedText">
            <summary>
            DelimitedText
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.FileFormatType.RcFile">
            <summary>
            RcFile
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.FileFormatType.Orc">
            <summary>
            Orc
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.FileFormatType.Parquet">
            <summary>
            Parquet
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.RejectType">
            <summary>
            External table reject type options.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.RejectType.Value">
            <summary>
            Value
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.RejectType.Percentage">
            <summary>
            Percentage
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.MemoryPartitionMode">
            <summary>
            Memory partion mode for event session
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.MemoryPartitionMode.None">
            <summary>
            None
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.MemoryPartitionMode.PerNode">
            <summary>
            PerNode
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.MemoryPartitionMode.PerCpu">
            <summary>
            PerCpu
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.EventRetentionMode">
            <summary>
            Event retention mode for event session
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventRetentionMode.AllowSingleEventLoss">
            <summary>
            AllowSingleEventLoss
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventRetentionMode.AllowMultipleEventLoss">
            <summary>
            AllowMultipleEventLoss
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventRetentionMode.NoEventLoss">
            <summary>
            NoEventLoss
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ForeignKeyAction">
            <summary>
            Actions taken when a key to which existing foreign key points is updated or deleted
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ForeignKeyAction.NoAction">
            <summary>
            NoAction
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ForeignKeyAction.Cascade">
            <summary>
            Cascade
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ForeignKeyAction.SetNull">
            <summary>
            SetNull
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ForeignKeyAction.SetDefault">
            <summary>
            SetDefault
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.Tessellation">
            <summary>
            Grid tessellation in spatial index
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.Tessellation.None">
            <summary>
            None
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.Tessellation.Geometry">
            <summary>
            Geometry
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.Tessellation.Geography">
            <summary>
            Geography
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.Tessellation.AutoGeometry">
            <summary>
            AutoGeometry
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.Tessellation.AutoGeography">
            <summary>
            AutoGeography
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.LoginEncryptionOption">
            <summary>
            SQL login encryption options
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.LoginEncryptionOption.Encrypt">
            <summary>
            Encrypt
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.LoginEncryptionOption.AlreadyEncrypted">
            <summary>
            AlreadyEncrypted
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.LoginEncryptionOption.AlreadyEncryptedOld">
            <summary>
            AlreadyEncryptedOld
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.MemoryUnit">
            <summary>
            Memory units for use in SQL models
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.MemoryUnit.Unspecified">
            <summary>
            Unspecified
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.MemoryUnit.Percent">
            <summary>
            Percent
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.MemoryUnit.Bytes">
            <summary>
            Bytes
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.MemoryUnit.KB">
            <summary>
            KB
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.MemoryUnit.MB">
            <summary>
            MB
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.MemoryUnit.GB">
            <summary>
            GB
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.MemoryUnit.TB">
            <summary>
            TB
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.MemoryUnit.PB">
            <summary>
            PB
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.MemoryUnit.EB">
            <summary>
            EB
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.MessageSentBy">
            <summary>
            Specifies message sender type
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.MessageSentBy.Unknown">
            <summary>
            Unknown
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.MessageSentBy.Initiator">
            <summary>
            Initiator
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.MessageSentBy.Target">
            <summary>
            Target
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.MessageSentBy.Any">
            <summary>
            Any
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ValidationMethod">
            <summary>
            Specifies message validation method
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ValidationMethod.Unknown">
            <summary>
            Unknown
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ValidationMethod.None">
            <summary>
            None
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ValidationMethod.Empty">
            <summary>
            Empty
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ValidationMethod.WellFormedXml">
            <summary>
            WellFormedXml
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ValidationMethod.ValidXml">
            <summary>
            ValidXml
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.PartitionRange">
            <summary>
            Specifies to which side of interval, left or right, the boundary value belongs
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PartitionRange.Unknown">
            <summary>
            Unknown
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PartitionRange.Left">
            <summary>
            Left
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PartitionRange.Right">
            <summary>
            Right
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.Payload">
            <summary>
            Payload types for endpoints
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.Payload.Unknown">
            <summary>
            Unknown
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.Payload.Soap">
            <summary>
            Soap
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.Payload.TSql">
            <summary>
            TSql
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.Payload.ServiceBroker">
            <summary>
            ServiceBroker
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.Payload.DatabaseMirroring">
            <summary>
            DatabaseMirroring
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.PermissionAction">
            <summary>
            SQL permission action types
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionAction.Grant">
            <summary>
            Grant
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.PermissionAction.Deny">
            <summary>
            Deny
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.SecondaryXmlIndexType">
            <summary>
            Types of secondary XML index
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SecondaryXmlIndexType.Unknown">
            <summary>
            Unknown
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SecondaryXmlIndexType.Path">
            <summary>
            Path
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SecondaryXmlIndexType.Property">
            <summary>
            Property
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SecondaryXmlIndexType.Value">
            <summary>
            Value
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.Protocol">
            <summary>
            Protocol types for endpoints
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.Protocol.Unknown">
            <summary>
            Unknown
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.Protocol.Http">
            <summary>
            Http
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.Protocol.Tcp">
            <summary>
            Tcp
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.TimeUnit">
            <summary>
            Describes the unit for retention period.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.TimeUnit.Unknown">
            <summary>
            Unknown
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.TimeUnit.Seconds">
            <summary>
            Seconds
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.TimeUnit.Days">
            <summary>
            Days
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.TimeUnit.Hours">
            <summary>
            Hours
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.TimeUnit.Minutes">
            <summary>
            Minutes
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.SamplingStyle">
            <summary>
            Sampling styles used to create statistics
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SamplingStyle.Automatic">
            <summary>
            Automatic
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SamplingStyle.Fullscan">
            <summary>
            Fullscan
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SamplingStyle.SamplePercentRows">
            <summary>
            SamplePercentRows
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SamplingStyle.SampleAbsoluteRows">
            <summary>
            SampleAbsoluteRows
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.OrderRestriction">
            <summary>
            Trigger oder restriction types
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.OrderRestriction.None">
            <summary>
            None
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.OrderRestriction.IsFirst">
            <summary>
            IsFirst
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.OrderRestriction.IsLast">
            <summary>
            IsLast
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.TriggerType">
            <summary>
            Trigger types
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.TriggerType.Unknown">
            <summary>
            Unknown
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.TriggerType.For">
            <summary>
            For
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.TriggerType.After">
            <summary>
            After
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.TriggerType.InsteadOf">
            <summary>
            InsteadOf
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.AuthenticationType">
            <summary>
            Database user Authentication type
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuthenticationType.None">
            <summary>
            User without a login, Application Role, Database Role,  Certificate mapped user or Asymmetric key mapped user
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuthenticationType.InstanceAuthentication">
            <summary>
            User with login 
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuthenticationType.DatabaseAuthentication">
            <summary>
            User with a password
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuthenticationType.WindowsAuthentication">
            <summary>
            Windows user with login, Windows user without a login or Windows group
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.AuthenticationType.ExternalAuthenticationProvider">
            <summary>
            User supplied by an external authentication provider (e.g. Azure Active Directory).
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.XmlStyle">
            <summary>
            XML data type style
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.XmlStyle.Unknown">
            <summary>
            Unknown
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.XmlStyle.Content">
            <summary>
            Content
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.XmlStyle.Document">
            <summary>
            Document
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.Containment">
            <summary>
            Database Containment types
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.Containment.None">
            <summary>
            None
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.Containment.Partial">
            <summary>
            Partial
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.FailureAction">
            <summary>
            Action type for audit failure
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.FailureAction.Continue">
            <summary>
            Continue
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.FailureAction.Shutdown">
            <summary>
            Shutdown
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.FailureAction.FailOperation">
            <summary>
            FailOperation
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.NonTransactedFileStreamAccess">
            <summary>
            FILESTREAM non-transactional access option values
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.NonTransactedFileStreamAccess.Off">
            <summary>
            Off
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.NonTransactedFileStreamAccess.ReadOnly">
            <summary>
            ReadOnly
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.NonTransactedFileStreamAccess.Full">
            <summary>
            Full
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.Durability">
            <summary>
            Durability of a table
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.Durability.SchemaAndData">
            <summary>
            Table is durable.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.Durability.SchemaOnly">
            <summary>
            Table is non-durable.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.EventSessionScope">
            <summary>
            Scope of the event session
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventSessionScope.Server">
            <summary>
            Server
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.EventSessionScope.Database">
            <summary>
            Database
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ColumnGeneratedAlwaysType">
            <summary>
            Column system-versioned generated always type
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ColumnGeneratedAlwaysType.None">
            <summary>
            Column is not a generated always type of column.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ColumnGeneratedAlwaysType.GeneratedAlwaysAsRowStart">
            <summary>
            Column has a type of 'generated always as row start'.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.ColumnGeneratedAlwaysType.GeneratedAlwaysAsRowEnd">
            <summary>
            Column has a type of 'generated always as row end'.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.UserType">
            <summary>
            User type (for use with Azure active directory users).
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.UserType.None">
            <summary>
            No specified Azure active directory user type.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.UserType.ExternalUser">
            <summary>
            An Azure active directory user.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.UserType.ExternalGroup">
            <summary>
            An Azure active directory group.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.SecurityPredicateOperation">
            <summary>
            The operation to which a security predicate applies
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SecurityPredicateOperation.All">
            <summary>
            All
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SecurityPredicateOperation.AfterInsert">
            <summary>
            AfterInsert
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SecurityPredicateOperation.AfterUpdate">
            <summary>
            AfterUpdate
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SecurityPredicateOperation.BeforeUpdate">
            <summary>
            BeforeUpdate
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SecurityPredicateOperation.BeforeDelete">
            <summary>
            BeforeDelete
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.SecurityPredicateType">
            <summary>
            The type of the security predicate
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SecurityPredicateType.Filter">
            <summary>
            Filter
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SecurityPredicateType.Block">
            <summary>
            Block
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.QueryStoreCaptureMode">
            <summary>
            Query Store SIZE_BASED_CLEANUP_MODE option values
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.QueryStoreCaptureMode.All">
            <summary>
            Query Store captures all queries. This is the default configuration value
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.QueryStoreCaptureMode.Auto">
            <summary>
            Query Store captures relevant queries based on execution count and resource consumption.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.QueryStoreCaptureMode.None">
            <summary>
            Query Store does not capture new queries.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.QueryStoreDesiredState">
            <summary>
            Query Store Operation Mode/Desired State option values
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.QueryStoreDesiredState.Off">
            <summary>
            Query Store feature is OFF.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.QueryStoreDesiredState.ReadOnly">
            <summary>
            Query Store does not record new data.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.QueryStoreDesiredState.ReadWrite">
            <summary>
            Query store records new data.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.Column">
            <summary>
            Model schema container class for Column
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Column.TypeClass">
            <summary>
            Type class for Column
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Column.ColumnType">
            <summary>
            ColumnType metadata class of Column
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Column.Collation">
            <summary>
            Collation property class of Column
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Column.IsIdentityNotForReplication">
            <summary>
            IsIdentityNotForReplication property class of Column
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Column.Nullable">
            <summary>
            Nullable property class of Column
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Column.IsRowGuidCol">
            <summary>
            IsRowGuidCol property class of Column
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Column.Sparse">
            <summary>
            Sparse property class of Column
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Column.Expression">
            <summary>
            Expression property class of Column
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Column.Persisted">
            <summary>
            Persisted property class of Column
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Column.PersistedNullable">
            <summary>
            PersistedNullable property class of Column
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Column.Scale">
            <summary>
            Scale property class of Column
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Column.Precision">
            <summary>
            Precision property class of Column
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Column.Length">
            <summary>
            Length property class of Column
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Column.IsMax">
            <summary>
            IsMax property class of Column
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Column.XmlStyle">
            <summary>
            XmlStyle property class of Column
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Column.EncryptionAlgorithmName">
            <summary>
            EncryptionAlgorithmName property class of Column
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Column.EncryptionType">
            <summary>
            EncryptionType property class of Column
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Column.GeneratedAlwaysType">
            <summary>
            GeneratedAlwaysType property class of Column
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Column.GraphType">
            <summary>
            GraphType property class of Column
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Column.IdentityIncrement">
            <summary>
            IdentityIncrement property class of Column
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Column.IdentitySeed">
            <summary>
            IdentitySeed property class of Column
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Column.IsFileStream">
            <summary>
            IsFileStream property class of Column
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Column.IsHidden">
            <summary>
            IsHidden property class of Column
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Column.IsIdentity">
            <summary>
            IsIdentity property class of Column
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Column.IsPseudoColumn">
            <summary>
            IsPseudoColumn property class of Column
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Column.MaskingFunction">
            <summary>
            MaskingFunction property class of Column
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Column.ExpressionDependencies">
            <summary>
            ExpressionDependencies relationship class of Column
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Column.XmlSchemaCollection">
            <summary>
            XmlSchemaCollection relationship class of Column
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Column.DataType">
            <summary>
            DataType relationship class of Column
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Column.ColumnEncryptionKey">
            <summary>
            ColumnEncryptionKey relationship class of Column
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.TableValuedFunction">
            <summary>
            Model schema container class for TableValuedFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableValuedFunction.TypeClass">
            <summary>
            Type class for TableValuedFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableValuedFunction.FunctionType">
            <summary>
            FunctionType metadata class of TableValuedFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableValuedFunction.ReturnsNullOnNullInput">
            <summary>
            ReturnsNullOnNullInput property class of TableValuedFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableValuedFunction.AnsiNullsOn">
            <summary>
            AnsiNullsOn property class of TableValuedFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableValuedFunction.CalledOnNullInput">
            <summary>
            CalledOnNullInput property class of TableValuedFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableValuedFunction.ExecuteAsCaller">
            <summary>
            ExecuteAsCaller property class of TableValuedFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableValuedFunction.WithEncryption">
            <summary>
            WithEncryption property class of TableValuedFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableValuedFunction.ExecuteAsOwner">
            <summary>
            ExecuteAsOwner property class of TableValuedFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableValuedFunction.QuotedIdentifierOn">
            <summary>
            QuotedIdentifierOn property class of TableValuedFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableValuedFunction.WithSchemaBinding">
            <summary>
            WithSchemaBinding property class of TableValuedFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableValuedFunction.ExecuteAsSelf">
            <summary>
            ExecuteAsSelf property class of TableValuedFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableValuedFunction.ReturnTableVariableName">
            <summary>
            ReturnTableVariableName property class of TableValuedFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableValuedFunction.ClassName">
            <summary>
            ClassName property class of TableValuedFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableValuedFunction.DataAccess">
            <summary>
            DataAccess property class of TableValuedFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableValuedFunction.FillRowMethodName">
            <summary>
            FillRowMethodName property class of TableValuedFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableValuedFunction.Deterministic">
            <summary>
            Deterministic property class of TableValuedFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableValuedFunction.Precise">
            <summary>
            Precise property class of TableValuedFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableValuedFunction.MethodName">
            <summary>
            MethodName property class of TableValuedFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableValuedFunction.SystemDataAccess">
            <summary>
            SystemDataAccess property class of TableValuedFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableValuedFunction.IsNativeCompiled">
            <summary>
            IsNativeCompiled property class of TableValuedFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableValuedFunction.IsReplicated">
            <summary>
            IsReplicated property class of TableValuedFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableValuedFunction.Assembly">
            <summary>
            Assembly relationship class of TableValuedFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableValuedFunction.TableOption">
            <summary>
            TableOption relationship class of TableValuedFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableValuedFunction.ReturnType">
            <summary>
            ReturnType relationship class of TableValuedFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableValuedFunction.BodyDependencies">
            <summary>
            BodyDependencies relationship class of TableValuedFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableValuedFunction.Columns">
            <summary>
            Columns relationship class of TableValuedFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableValuedFunction.Login">
            <summary>
            Login relationship class of TableValuedFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableValuedFunction.Parameters">
            <summary>
            Parameters relationship class of TableValuedFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableValuedFunction.Schema">
            <summary>
            Schema relationship class of TableValuedFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableValuedFunction.User">
            <summary>
            User relationship class of TableValuedFunction
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ScalarFunction">
            <summary>
            Model schema container class for ScalarFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ScalarFunction.TypeClass">
            <summary>
            Type class for ScalarFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ScalarFunction.ReturnsNullOnNullInput">
            <summary>
            ReturnsNullOnNullInput property class of ScalarFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ScalarFunction.AnsiNullsOn">
            <summary>
            AnsiNullsOn property class of ScalarFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ScalarFunction.CalledOnNullInput">
            <summary>
            CalledOnNullInput property class of ScalarFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ScalarFunction.ExecuteAsCaller">
            <summary>
            ExecuteAsCaller property class of ScalarFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ScalarFunction.WithEncryption">
            <summary>
            WithEncryption property class of ScalarFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ScalarFunction.ExecuteAsOwner">
            <summary>
            ExecuteAsOwner property class of ScalarFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ScalarFunction.QuotedIdentifierOn">
            <summary>
            QuotedIdentifierOn property class of ScalarFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ScalarFunction.WithSchemaBinding">
            <summary>
            WithSchemaBinding property class of ScalarFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ScalarFunction.ExecuteAsSelf">
            <summary>
            ExecuteAsSelf property class of ScalarFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ScalarFunction.ClassName">
            <summary>
            ClassName property class of ScalarFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ScalarFunction.DataAccess">
            <summary>
            DataAccess property class of ScalarFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ScalarFunction.FillRowMethodName">
            <summary>
            FillRowMethodName property class of ScalarFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ScalarFunction.Deterministic">
            <summary>
            Deterministic property class of ScalarFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ScalarFunction.Precise">
            <summary>
            Precise property class of ScalarFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ScalarFunction.MethodName">
            <summary>
            MethodName property class of ScalarFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ScalarFunction.SystemDataAccess">
            <summary>
            SystemDataAccess property class of ScalarFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ScalarFunction.IsNativeCompiled">
            <summary>
            IsNativeCompiled property class of ScalarFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ScalarFunction.IsReplicated">
            <summary>
            IsReplicated property class of ScalarFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ScalarFunction.Assembly">
            <summary>
            Assembly relationship class of ScalarFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ScalarFunction.ReturnType">
            <summary>
            ReturnType relationship class of ScalarFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ScalarFunction.BodyDependencies">
            <summary>
            BodyDependencies relationship class of ScalarFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ScalarFunction.Login">
            <summary>
            Login relationship class of ScalarFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ScalarFunction.Parameters">
            <summary>
            Parameters relationship class of ScalarFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ScalarFunction.Schema">
            <summary>
            Schema relationship class of ScalarFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ScalarFunction.User">
            <summary>
            User relationship class of ScalarFunction
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ClrTableOption">
            <summary>
            Model schema container class for ClrTableOption
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ClrTableOption.TypeClass">
            <summary>
            Type class for ClrTableOption
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ClrTableOption.ClassName">
            <summary>
            ClassName property class of ClrTableOption
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ClrTableOption.OrderColumns">
            <summary>
            OrderColumns relationship class of ClrTableOption
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ClrTableOption.OrderColumnsRelationship">
            <summary>
            Model schema container class for OrderColumnsRelationship
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ClrTableOption.OrderColumnsRelationship.RelationshipClass">
            <summary>
            Relationship class for OrderColumnsRelationship
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ClrTableOption.OrderColumnsRelationship.Ascending">
            <summary>
            Ascending property class of OrderColumnsRelationship
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.Aggregate">
            <summary>
            Model schema container class for Aggregate
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Aggregate.TypeClass">
            <summary>
            Type class for Aggregate
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Aggregate.InvariantToDuplicates">
            <summary>
            InvariantToDuplicates property class of Aggregate
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Aggregate.InvariantToNulls">
            <summary>
            InvariantToNulls property class of Aggregate
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Aggregate.NullIfEmpty">
            <summary>
            NullIfEmpty property class of Aggregate
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Aggregate.ClassName">
            <summary>
            ClassName property class of Aggregate
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Aggregate.Format">
            <summary>
            Format property class of Aggregate
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Aggregate.MaxByteSize">
            <summary>
            MaxByteSize property class of Aggregate
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Aggregate.ReturnType">
            <summary>
            ReturnType relationship class of Aggregate
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Aggregate.Assembly">
            <summary>
            Assembly relationship class of Aggregate
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Aggregate.Parameters">
            <summary>
            Parameters relationship class of Aggregate
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Aggregate.Schema">
            <summary>
            Schema relationship class of Aggregate
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ApplicationRole">
            <summary>
            Model schema container class for ApplicationRole
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ApplicationRole.TypeClass">
            <summary>
            Type class for ApplicationRole
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ApplicationRole.Password">
            <summary>
            Password property class of ApplicationRole
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ApplicationRole.DefaultSchema">
            <summary>
            DefaultSchema relationship class of ApplicationRole
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.Index">
            <summary>
            Model schema container class for Index
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Index.TypeClass">
            <summary>
            Type class for Index
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Index.AllowPageLocks">
            <summary>
            AllowPageLocks property class of Index
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Index.AllowRowLocks">
            <summary>
            AllowRowLocks property class of Index
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Index.IgnoreDuplicateKey">
            <summary>
            IgnoreDuplicateKey property class of Index
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Index.RecomputeStatistics">
            <summary>
            RecomputeStatistics property class of Index
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Index.IncrementalStatistics">
            <summary>
            IncrementalStatistics property class of Index
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Index.Clustered">
            <summary>
            Clustered property class of Index
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Index.Disabled">
            <summary>
            Disabled property class of Index
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Index.FileStreamNull">
            <summary>
            FileStreamNull property class of Index
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Index.WithPadIndex">
            <summary>
            WithPadIndex property class of Index
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Index.Unique">
            <summary>
            Unique property class of Index
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Index.FilterPredicate">
            <summary>
            FilterPredicate property class of Index
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Index.Hash">
            <summary>
            Hash property class of Index
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Index.BucketCount">
            <summary>
            BucketCount property class of Index
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Index.FillFactor">
            <summary>
            FillFactor property class of Index
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Index.Columns">
            <summary>
            Columns relationship class of Index
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Index.BodyDependencies">
            <summary>
            BodyDependencies relationship class of Index
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Index.DataCompressionOptions">
            <summary>
            DataCompressionOptions relationship class of Index
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Index.Filegroup">
            <summary>
            Filegroup relationship class of Index
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Index.FileStreamFilegroup">
            <summary>
            FileStreamFilegroup relationship class of Index
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Index.FileStreamPartitionScheme">
            <summary>
            FileStreamPartitionScheme relationship class of Index
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Index.IncludedColumns">
            <summary>
            IncludedColumns relationship class of Index
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Index.IndexedObject">
            <summary>
            IndexedObject relationship class of Index
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Index.PartitionColumn">
            <summary>
            PartitionColumn relationship class of Index
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Index.PartitionScheme">
            <summary>
            PartitionScheme relationship class of Index
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.Index.ColumnsRelationship">
            <summary>
            Model schema container class for ColumnsRelationship
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Index.ColumnsRelationship.RelationshipClass">
            <summary>
            Relationship class for ColumnsRelationship
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Index.ColumnsRelationship.Ascending">
            <summary>
            Ascending property class of ColumnsRelationship
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.Assembly">
            <summary>
            Model schema container class for Assembly
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Assembly.TypeClass">
            <summary>
            Type class for Assembly
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Assembly.Visible">
            <summary>
            Visible property class of Assembly
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Assembly.PermissionSet">
            <summary>
            PermissionSet property class of Assembly
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Assembly.AssemblySources">
            <summary>
            AssemblySources relationship class of Assembly
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Assembly.Authorizer">
            <summary>
            Authorizer relationship class of Assembly
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Assembly.ReferencedAssemblies">
            <summary>
            ReferencedAssemblies relationship class of Assembly
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.AssemblySource">
            <summary>
            Model schema container class for AssemblySource
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.AssemblySource.TypeClass">
            <summary>
            Type class for AssemblySource
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.AssemblySource.Source">
            <summary>
            Source property class of AssemblySource
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.AsymmetricKey">
            <summary>
            Model schema container class for AsymmetricKey
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.AsymmetricKey.TypeClass">
            <summary>
            Type class for AsymmetricKey
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.AsymmetricKey.Password">
            <summary>
            Password property class of AsymmetricKey
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.AsymmetricKey.EncryptedWithPassword">
            <summary>
            EncryptedWithPassword property class of AsymmetricKey
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.AsymmetricKey.Algorithm">
            <summary>
            Algorithm property class of AsymmetricKey
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.AsymmetricKey.CreationDisposition">
            <summary>
            CreationDisposition property class of AsymmetricKey
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.AsymmetricKey.ExecutableFile">
            <summary>
            ExecutableFile property class of AsymmetricKey
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.AsymmetricKey.File">
            <summary>
            File property class of AsymmetricKey
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.AsymmetricKey.ProviderKeyName">
            <summary>
            ProviderKeyName property class of AsymmetricKey
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.AsymmetricKey.Assembly">
            <summary>
            Assembly relationship class of AsymmetricKey
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.AsymmetricKey.Authorizer">
            <summary>
            Authorizer relationship class of AsymmetricKey
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.AsymmetricKey.Provider">
            <summary>
            Provider relationship class of AsymmetricKey
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.AuditAction">
            <summary>
            Model schema container class for AuditAction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.AuditAction.TypeClass">
            <summary>
            Type class for AuditAction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.AuditAction.Action">
            <summary>
            Action property class of AuditAction
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.AuditActionGroup">
            <summary>
            Model schema container class for AuditActionGroup
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.AuditActionGroup.TypeClass">
            <summary>
            Type class for AuditActionGroup
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.AuditActionGroup.ActionGroup">
            <summary>
            ActionGroup property class of AuditActionGroup
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.AuditActionSpecification">
            <summary>
            Model schema container class for AuditActionSpecification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.AuditActionSpecification.TypeClass">
            <summary>
            Type class for AuditActionSpecification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.AuditActionSpecification.AuditsDatabase">
            <summary>
            AuditsDatabase property class of AuditActionSpecification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.AuditActionSpecification.AuditActions">
            <summary>
            AuditActions relationship class of AuditActionSpecification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.AuditActionSpecification.Principals">
            <summary>
            Principals relationship class of AuditActionSpecification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.AuditActionSpecification.SecuredObject">
            <summary>
            SecuredObject relationship class of AuditActionSpecification
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.BrokerPriority">
            <summary>
            Model schema container class for BrokerPriority
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.BrokerPriority.TypeClass">
            <summary>
            Type class for BrokerPriority
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.BrokerPriority.RemoteServiceName">
            <summary>
            RemoteServiceName property class of BrokerPriority
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.BrokerPriority.PriorityLevel">
            <summary>
            PriorityLevel property class of BrokerPriority
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.BrokerPriority.ContractName">
            <summary>
            ContractName relationship class of BrokerPriority
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.BrokerPriority.LocalServiceName">
            <summary>
            LocalServiceName relationship class of BrokerPriority
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.BuiltInServerRole">
            <summary>
            Model schema container class for BuiltInServerRole
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.BuiltInServerRole.TypeClass">
            <summary>
            Type class for BuiltInServerRole
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.DataType">
            <summary>
            Model schema container class for DataType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DataType.TypeClass">
            <summary>
            Type class for DataType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DataType.UddtNullable">
            <summary>
            UddtNullable property class of DataType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DataType.UddtIsMax">
            <summary>
            UddtIsMax property class of DataType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DataType.UddtLength">
            <summary>
            UddtLength property class of DataType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DataType.UddtPrecision">
            <summary>
            UddtPrecision property class of DataType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DataType.UddtScale">
            <summary>
            UddtScale property class of DataType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DataType.SqlDataType">
            <summary>
            SqlDataType property class of DataType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DataType.Schema">
            <summary>
            Schema relationship class of DataType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DataType.Type">
            <summary>
            Type relationship class of DataType
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.Certificate">
            <summary>
            Model schema container class for Certificate
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Certificate.TypeClass">
            <summary>
            Type class for Certificate
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Certificate.ActiveForBeginDialog">
            <summary>
            ActiveForBeginDialog property class of Certificate
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Certificate.EncryptedWithPassword">
            <summary>
            EncryptedWithPassword property class of Certificate
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Certificate.EncryptionPassword">
            <summary>
            EncryptionPassword property class of Certificate
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Certificate.ExpiryDate">
            <summary>
            ExpiryDate property class of Certificate
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Certificate.StartDate">
            <summary>
            StartDate property class of Certificate
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Certificate.Subject">
            <summary>
            Subject property class of Certificate
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Certificate.PrivateKeyFilePath">
            <summary>
            PrivateKeyFilePath property class of Certificate
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Certificate.ExistingKeysFilePath">
            <summary>
            ExistingKeysFilePath property class of Certificate
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Certificate.IsExistingKeyFileExecutable">
            <summary>
            IsExistingKeyFileExecutable property class of Certificate
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Certificate.PrivateKeyDecryptionPassword">
            <summary>
            PrivateKeyDecryptionPassword property class of Certificate
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Certificate.PrivateKeyEncryptionPassword">
            <summary>
            PrivateKeyEncryptionPassword property class of Certificate
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Certificate.Authorizer">
            <summary>
            Authorizer relationship class of Certificate
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Certificate.ExistingKeysAssembly">
            <summary>
            ExistingKeysAssembly relationship class of Certificate
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.CheckConstraint">
            <summary>
            Model schema container class for CheckConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.CheckConstraint.TypeClass">
            <summary>
            Type class for CheckConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.CheckConstraint.Expression">
            <summary>
            Expression property class of CheckConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.CheckConstraint.Disabled">
            <summary>
            Disabled property class of CheckConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.CheckConstraint.NotForReplication">
            <summary>
            NotForReplication property class of CheckConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.CheckConstraint.ExpressionDependencies">
            <summary>
            ExpressionDependencies relationship class of CheckConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.CheckConstraint.Host">
            <summary>
            Host relationship class of CheckConstraint
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ClrTypeMethod">
            <summary>
            Model schema container class for ClrTypeMethod
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ClrTypeMethod.TypeClass">
            <summary>
            Type class for ClrTypeMethod
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ClrTypeMethod.Name">
            <summary>
            Name property class of ClrTypeMethod
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ClrTypeMethod.Parameters">
            <summary>
            Parameters relationship class of ClrTypeMethod
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ClrTypeMethod.ReturnType">
            <summary>
            ReturnType relationship class of ClrTypeMethod
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ClrTypeMethodParameter">
            <summary>
            Model schema container class for ClrTypeMethodParameter
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ClrTypeMethodParameter.TypeClass">
            <summary>
            Type class for ClrTypeMethodParameter
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ClrTypeMethodParameter.Name">
            <summary>
            Name property class of ClrTypeMethodParameter
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ClrTypeMethodParameter.IsOutput">
            <summary>
            IsOutput property class of ClrTypeMethodParameter
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ClrTypeMethodParameter.DataType">
            <summary>
            DataType relationship class of ClrTypeMethodParameter
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ClrTypeProperty">
            <summary>
            Model schema container class for ClrTypeProperty
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ClrTypeProperty.TypeClass">
            <summary>
            Type class for ClrTypeProperty
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ClrTypeProperty.Name">
            <summary>
            Name property class of ClrTypeProperty
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ClrTypeProperty.ClrType">
            <summary>
            ClrType relationship class of ClrTypeProperty
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ColumnStoreIndex">
            <summary>
            Model schema container class for ColumnStoreIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ColumnStoreIndex.TypeClass">
            <summary>
            Type class for ColumnStoreIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ColumnStoreIndex.Disabled">
            <summary>
            Disabled property class of ColumnStoreIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ColumnStoreIndex.Clustered">
            <summary>
            Clustered property class of ColumnStoreIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ColumnStoreIndex.FilterPredicate">
            <summary>
            FilterPredicate property class of ColumnStoreIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ColumnStoreIndex.CompressionDelay">
            <summary>
            CompressionDelay property class of ColumnStoreIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ColumnStoreIndex.Columns">
            <summary>
            Columns relationship class of ColumnStoreIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ColumnStoreIndex.BodyDependencies">
            <summary>
            BodyDependencies relationship class of ColumnStoreIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ColumnStoreIndex.DataCompressionOptions">
            <summary>
            DataCompressionOptions relationship class of ColumnStoreIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ColumnStoreIndex.Filegroup">
            <summary>
            Filegroup relationship class of ColumnStoreIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ColumnStoreIndex.IndexedObject">
            <summary>
            IndexedObject relationship class of ColumnStoreIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ColumnStoreIndex.PartitionColumn">
            <summary>
            PartitionColumn relationship class of ColumnStoreIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ColumnStoreIndex.PartitionScheme">
            <summary>
            PartitionScheme relationship class of ColumnStoreIndex
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ColumnStoreIndex.ColumnsRelationship">
            <summary>
            Model schema container class for ColumnsRelationship
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ColumnStoreIndex.ColumnsRelationship.RelationshipClass">
            <summary>
            Relationship class for ColumnsRelationship
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ColumnStoreIndex.ColumnsRelationship.Ascending">
            <summary>
            Ascending property class of ColumnsRelationship
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.Contract">
            <summary>
            Model schema container class for Contract
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Contract.TypeClass">
            <summary>
            Type class for Contract
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Contract.Messages">
            <summary>
            Messages relationship class of Contract
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Contract.Authorizer">
            <summary>
            Authorizer relationship class of Contract
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.Contract.MessagesRelationship">
            <summary>
            Model schema container class for MessagesRelationship
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Contract.MessagesRelationship.RelationshipClass">
            <summary>
            Relationship class for MessagesRelationship
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Contract.MessagesRelationship.Default">
            <summary>
            Default property class of MessagesRelationship
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Contract.MessagesRelationship.SentBy">
            <summary>
            SentBy property class of MessagesRelationship
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.Credential">
            <summary>
            Model schema container class for Credential
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Credential.TypeClass">
            <summary>
            Type class for Credential
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Credential.Identity">
            <summary>
            Identity property class of Credential
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Credential.Secret">
            <summary>
            Secret property class of Credential
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Credential.CryptographicProvider">
            <summary>
            CryptographicProvider relationship class of Credential
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.DatabaseCredential">
            <summary>
            Model schema container class for DatabaseCredential
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseCredential.TypeClass">
            <summary>
            Type class for DatabaseCredential
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseCredential.Identity">
            <summary>
            Identity property class of DatabaseCredential
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseCredential.Secret">
            <summary>
            Secret property class of DatabaseCredential
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.CryptographicProvider">
            <summary>
            Model schema container class for CryptographicProvider
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.CryptographicProvider.TypeClass">
            <summary>
            Type class for CryptographicProvider
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.CryptographicProvider.DllPath">
            <summary>
            DllPath property class of CryptographicProvider
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.CryptographicProvider.Enabled">
            <summary>
            Enabled property class of CryptographicProvider
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.DatabaseAuditSpecification">
            <summary>
            Model schema container class for DatabaseAuditSpecification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseAuditSpecification.TypeClass">
            <summary>
            Type class for DatabaseAuditSpecification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseAuditSpecification.WithState">
            <summary>
            WithState property class of DatabaseAuditSpecification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseAuditSpecification.AuditActionGroups">
            <summary>
            AuditActionGroups relationship class of DatabaseAuditSpecification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseAuditSpecification.AuditActions">
            <summary>
            AuditActions relationship class of DatabaseAuditSpecification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseAuditSpecification.ServerAudit">
            <summary>
            ServerAudit relationship class of DatabaseAuditSpecification
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.DatabaseDdlTrigger">
            <summary>
            Model schema container class for DatabaseDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseDdlTrigger.TypeClass">
            <summary>
            Type class for DatabaseDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseDdlTrigger.AnsiNullsOn">
            <summary>
            AnsiNullsOn property class of DatabaseDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseDdlTrigger.ExecuteAsCaller">
            <summary>
            ExecuteAsCaller property class of DatabaseDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseDdlTrigger.Disabled">
            <summary>
            Disabled property class of DatabaseDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseDdlTrigger.WithEncryption">
            <summary>
            WithEncryption property class of DatabaseDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseDdlTrigger.ExecuteAsOwner">
            <summary>
            ExecuteAsOwner property class of DatabaseDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseDdlTrigger.QuotedIdentifierOn">
            <summary>
            QuotedIdentifierOn property class of DatabaseDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseDdlTrigger.ExecuteAsSelf">
            <summary>
            ExecuteAsSelf property class of DatabaseDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseDdlTrigger.TriggerType">
            <summary>
            TriggerType property class of DatabaseDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseDdlTrigger.ClassName">
            <summary>
            ClassName property class of DatabaseDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseDdlTrigger.MethodName">
            <summary>
            MethodName property class of DatabaseDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseDdlTrigger.Assembly">
            <summary>
            Assembly relationship class of DatabaseDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseDdlTrigger.BodyDependencies">
            <summary>
            BodyDependencies relationship class of DatabaseDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseDdlTrigger.EventGroup">
            <summary>
            EventGroup relationship class of DatabaseDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseDdlTrigger.EventType">
            <summary>
            EventType relationship class of DatabaseDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseDdlTrigger.Login">
            <summary>
            Login relationship class of DatabaseDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseDdlTrigger.User">
            <summary>
            User relationship class of DatabaseDdlTrigger
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.DatabaseEncryptionKey">
            <summary>
            Model schema container class for DatabaseEncryptionKey
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseEncryptionKey.TypeClass">
            <summary>
            Type class for DatabaseEncryptionKey
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseEncryptionKey.Algorithm">
            <summary>
            Algorithm property class of DatabaseEncryptionKey
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseEncryptionKey.Certificate">
            <summary>
            Certificate relationship class of DatabaseEncryptionKey
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseEncryptionKey.AsymmetricKey">
            <summary>
            AsymmetricKey relationship class of DatabaseEncryptionKey
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.DatabaseEventNotification">
            <summary>
            Model schema container class for DatabaseEventNotification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseEventNotification.TypeClass">
            <summary>
            Type class for DatabaseEventNotification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseEventNotification.WithFanIn">
            <summary>
            WithFanIn property class of DatabaseEventNotification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseEventNotification.BrokerService">
            <summary>
            BrokerService property class of DatabaseEventNotification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseEventNotification.BrokerInstanceSpecifier">
            <summary>
            BrokerInstanceSpecifier property class of DatabaseEventNotification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseEventNotification.EventGroup">
            <summary>
            EventGroup relationship class of DatabaseEventNotification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseEventNotification.EventType">
            <summary>
            EventType relationship class of DatabaseEventNotification
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.DatabaseMirroringLanguageSpecifier">
            <summary>
            Model schema container class for DatabaseMirroringLanguageSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseMirroringLanguageSpecifier.TypeClass">
            <summary>
            Type class for DatabaseMirroringLanguageSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseMirroringLanguageSpecifier.WindowsAuthenticationMode">
            <summary>
            WindowsAuthenticationMode property class of DatabaseMirroringLanguageSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseMirroringLanguageSpecifier.UseCertificateFirst">
            <summary>
            UseCertificateFirst property class of DatabaseMirroringLanguageSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseMirroringLanguageSpecifier.EncryptionAlgorithmPart1">
            <summary>
            EncryptionAlgorithmPart1 property class of DatabaseMirroringLanguageSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseMirroringLanguageSpecifier.EncryptionAlgorithmPart2">
            <summary>
            EncryptionAlgorithmPart2 property class of DatabaseMirroringLanguageSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseMirroringLanguageSpecifier.EncryptionMode">
            <summary>
            EncryptionMode property class of DatabaseMirroringLanguageSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseMirroringLanguageSpecifier.RoleType">
            <summary>
            RoleType property class of DatabaseMirroringLanguageSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseMirroringLanguageSpecifier.AuthenticationCertificate">
            <summary>
            AuthenticationCertificate relationship class of DatabaseMirroringLanguageSpecifier
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.DatabaseOptions">
            <summary>
            Model schema container class for DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.TypeClass">
            <summary>
            Type class for DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.CompatibilityLevel">
            <summary>
            CompatibilityLevel property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.AllowSnapshotIsolation">
            <summary>
            AllowSnapshotIsolation property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.AnsiNullDefaultOn">
            <summary>
            AnsiNullDefaultOn property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.AnsiNullsOn">
            <summary>
            AnsiNullsOn property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.AnsiPaddingOn">
            <summary>
            AnsiPaddingOn property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.AnsiWarningsOn">
            <summary>
            AnsiWarningsOn property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.ArithAbortOn">
            <summary>
            ArithAbortOn property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.AutoClose">
            <summary>
            AutoClose property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.AutoCreateStatistics">
            <summary>
            AutoCreateStatistics property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.AutoCreateStatisticsIncremental">
            <summary>
            AutoCreateStatisticsIncremental property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.AutoShrink">
            <summary>
            AutoShrink property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.AutoUpdateStatisticsAsync">
            <summary>
            AutoUpdateStatisticsAsync property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.AutoUpdateStatistics">
            <summary>
            AutoUpdateStatistics property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.HonorBrokerPriority">
            <summary>
            HonorBrokerPriority property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.ChangeTrackingAutoCleanup">
            <summary>
            ChangeTrackingAutoCleanup property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.ChangeTrackingEnabled">
            <summary>
            ChangeTrackingEnabled property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.ConcatNullYieldsNull">
            <summary>
            ConcatNullYieldsNull property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.CursorCloseOnCommit">
            <summary>
            CursorCloseOnCommit property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.CursorDefaultGlobalScope">
            <summary>
            CursorDefaultGlobalScope property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.DateCorrelationOptimizationOn">
            <summary>
            DateCorrelationOptimizationOn property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.DBChainingOn">
            <summary>
            DBChainingOn property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.WithEncryption">
            <summary>
            WithEncryption property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.FullTextEnabled">
            <summary>
            FullTextEnabled property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.NestedTriggersOn">
            <summary>
            NestedTriggersOn property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.NumericRoundAbortOn">
            <summary>
            NumericRoundAbortOn property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.DatabaseStateOffline">
            <summary>
            DatabaseStateOffline property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.QuotedIdentifierOn">
            <summary>
            QuotedIdentifierOn property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.TransactionIsolationReadCommittedSnapshot">
            <summary>
            TransactionIsolationReadCommittedSnapshot property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.ReadOnly">
            <summary>
            ReadOnly property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.RecursiveTriggersOn">
            <summary>
            RecursiveTriggersOn property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.SupplementalLoggingOn">
            <summary>
            SupplementalLoggingOn property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.TornPageProtectionOn">
            <summary>
            TornPageProtectionOn property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.TransformNoiseWords">
            <summary>
            TransformNoiseWords property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.Trustworthy">
            <summary>
            Trustworthy property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.VardecimalStorageFormatOn">
            <summary>
            VardecimalStorageFormatOn property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.UserAccessOption">
            <summary>
            UserAccessOption property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.DelayedDurabilityMode">
            <summary>
            DelayedDurabilityMode property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.MemoryOptimizedElevateToSnapshot">
            <summary>
            MemoryOptimizedElevateToSnapshot property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.ChangeTrackingRetentionPeriod">
            <summary>
            ChangeTrackingRetentionPeriod property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.ChangeTrackingRetentionUnit">
            <summary>
            ChangeTrackingRetentionUnit property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.Collation">
            <summary>
            Collation property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.Containment">
            <summary>
            Containment property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.DefaultFullTextLanguage">
            <summary>
            DefaultFullTextLanguage property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.DefaultLanguage">
            <summary>
            DefaultLanguage property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.FileStreamDirectoryName">
            <summary>
            FileStreamDirectoryName property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.LegacyCardinalityEstimation">
            <summary>
            LegacyCardinalityEstimation property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.LegacyCardinalityEstimationForSecondary">
            <summary>
            LegacyCardinalityEstimationForSecondary property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.MaxDop">
            <summary>
            MaxDop property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.MaxDopForSecondary">
            <summary>
            MaxDopForSecondary property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.NonTransactedFileStreamAccess">
            <summary>
            NonTransactedFileStreamAccess property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.PageVerifyMode">
            <summary>
            PageVerifyMode property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.ParameterizationOption">
            <summary>
            ParameterizationOption property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.ParameterSniffing">
            <summary>
            ParameterSniffing property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.ParameterSniffingForSecondary">
            <summary>
            ParameterSniffingForSecondary property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.QueryOptimizerHotfixes">
            <summary>
            QueryOptimizerHotfixes property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.QueryOptimizerHotfixesForSecondary">
            <summary>
            QueryOptimizerHotfixesForSecondary property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.QueryStoreCaptureMode">
            <summary>
            QueryStoreCaptureMode property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.QueryStoreDesiredState">
            <summary>
            QueryStoreDesiredState property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.QueryStoreFlushInterval">
            <summary>
            QueryStoreFlushInterval property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.QueryStoreIntervalLength">
            <summary>
            QueryStoreIntervalLength property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.QueryStoreMaxPlansPerQuery">
            <summary>
            QueryStoreMaxPlansPerQuery property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.QueryStoreMaxStorageSize">
            <summary>
            QueryStoreMaxStorageSize property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.QueryStoreStaleQueryThreshold">
            <summary>
            QueryStoreStaleQueryThreshold property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.RecoveryMode">
            <summary>
            RecoveryMode property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.RemoteDataEnabled">
            <summary>
            RemoteDataEnabled property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.ServiceBrokerOption">
            <summary>
            ServiceBrokerOption property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.TargetRecoveryTimePeriod">
            <summary>
            TargetRecoveryTimePeriod property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.TargetRecoveryTimeUnit">
            <summary>
            TargetRecoveryTimeUnit property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.TemporalHistoryRetentionEnabled">
            <summary>
            TemporalHistoryRetentionEnabled property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.TwoDigitYearCutoff">
            <summary>
            TwoDigitYearCutoff property class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.DefaultFilegroup">
            <summary>
            DefaultFilegroup relationship class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.DefaultFileStreamFilegroup">
            <summary>
            DefaultFileStreamFilegroup relationship class of DatabaseOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseOptions.GenericDatabaseScopedConfigurationOptions">
            <summary>
            GenericDatabaseScopedConfigurationOptions relationship class of DatabaseOptions
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.DataCompressionOption">
            <summary>
            Model schema container class for DataCompressionOption
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DataCompressionOption.TypeClass">
            <summary>
            Type class for DataCompressionOption
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DataCompressionOption.CompressionLevel">
            <summary>
            CompressionLevel property class of DataCompressionOption
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DataCompressionOption.PartitionNumber">
            <summary>
            PartitionNumber property class of DataCompressionOption
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.Default">
            <summary>
            Model schema container class for Default
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Default.TypeClass">
            <summary>
            Type class for Default
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Default.Expression">
            <summary>
            Expression property class of Default
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Default.BoundObjects">
            <summary>
            BoundObjects relationship class of Default
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Default.Schema">
            <summary>
            Schema relationship class of Default
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.DefaultConstraint">
            <summary>
            Model schema container class for DefaultConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DefaultConstraint.TypeClass">
            <summary>
            Type class for DefaultConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DefaultConstraint.Expression">
            <summary>
            Expression property class of DefaultConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DefaultConstraint.Disabled">
            <summary>
            Disabled property class of DefaultConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DefaultConstraint.WithValues">
            <summary>
            WithValues property class of DefaultConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DefaultConstraint.Host">
            <summary>
            Host relationship class of DefaultConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DefaultConstraint.TargetColumn">
            <summary>
            TargetColumn relationship class of DefaultConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DefaultConstraint.ExpressionDependencies">
            <summary>
            ExpressionDependencies relationship class of DefaultConstraint
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.DmlTrigger">
            <summary>
            Model schema container class for DmlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DmlTrigger.TypeClass">
            <summary>
            Type class for DmlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DmlTrigger.AnsiNullsOn">
            <summary>
            AnsiNullsOn property class of DmlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DmlTrigger.ExecuteAsCaller">
            <summary>
            ExecuteAsCaller property class of DmlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DmlTrigger.Disabled">
            <summary>
            Disabled property class of DmlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DmlTrigger.WithEncryption">
            <summary>
            WithEncryption property class of DmlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DmlTrigger.NotForReplication">
            <summary>
            NotForReplication property class of DmlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DmlTrigger.ExecuteAsOwner">
            <summary>
            ExecuteAsOwner property class of DmlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DmlTrigger.QuotedIdentifierOn">
            <summary>
            QuotedIdentifierOn property class of DmlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DmlTrigger.ExecuteAsSelf">
            <summary>
            ExecuteAsSelf property class of DmlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DmlTrigger.WithAppend">
            <summary>
            WithAppend property class of DmlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DmlTrigger.TriggerType">
            <summary>
            TriggerType property class of DmlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DmlTrigger.ClassName">
            <summary>
            ClassName property class of DmlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DmlTrigger.DeleteOrderRestriction">
            <summary>
            DeleteOrderRestriction property class of DmlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DmlTrigger.InsertOrderRestriction">
            <summary>
            InsertOrderRestriction property class of DmlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DmlTrigger.IsDeleteTrigger">
            <summary>
            IsDeleteTrigger property class of DmlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DmlTrigger.IsInsertTrigger">
            <summary>
            IsInsertTrigger property class of DmlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DmlTrigger.IsNativeCompiled">
            <summary>
            IsNativeCompiled property class of DmlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DmlTrigger.IsSchemaBound">
            <summary>
            IsSchemaBound property class of DmlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DmlTrigger.IsUpdateTrigger">
            <summary>
            IsUpdateTrigger property class of DmlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DmlTrigger.MethodName">
            <summary>
            MethodName property class of DmlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DmlTrigger.UpdateOrderRestriction">
            <summary>
            UpdateOrderRestriction property class of DmlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DmlTrigger.TriggerObject">
            <summary>
            TriggerObject relationship class of DmlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DmlTrigger.Assembly">
            <summary>
            Assembly relationship class of DmlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DmlTrigger.BodyDependencies">
            <summary>
            BodyDependencies relationship class of DmlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DmlTrigger.Login">
            <summary>
            Login relationship class of DmlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DmlTrigger.User">
            <summary>
            User relationship class of DmlTrigger
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.Endpoint">
            <summary>
            Model schema container class for Endpoint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Endpoint.TypeClass">
            <summary>
            Type class for Endpoint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Endpoint.Payload">
            <summary>
            Payload property class of Endpoint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Endpoint.Protocol">
            <summary>
            Protocol property class of Endpoint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Endpoint.State">
            <summary>
            State property class of Endpoint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Endpoint.Authorizer">
            <summary>
            Authorizer relationship class of Endpoint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Endpoint.PayloadSpecifier">
            <summary>
            PayloadSpecifier relationship class of Endpoint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Endpoint.ProtocolSpecifier">
            <summary>
            ProtocolSpecifier relationship class of Endpoint
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ErrorMessage">
            <summary>
            Model schema container class for ErrorMessage
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ErrorMessage.TypeClass">
            <summary>
            Type class for ErrorMessage
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ErrorMessage.WithLog">
            <summary>
            WithLog property class of ErrorMessage
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ErrorMessage.Language">
            <summary>
            Language property class of ErrorMessage
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ErrorMessage.MessageNumber">
            <summary>
            MessageNumber property class of ErrorMessage
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ErrorMessage.MessageText">
            <summary>
            MessageText property class of ErrorMessage
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ErrorMessage.Severity">
            <summary>
            Severity property class of ErrorMessage
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.EventGroup">
            <summary>
            Model schema container class for EventGroup
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventGroup.TypeClass">
            <summary>
            Type class for EventGroup
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventGroup.Group">
            <summary>
            Group property class of EventGroup
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.EventSession">
            <summary>
            Model schema container class for EventSession
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventSession.TypeClass">
            <summary>
            Type class for EventSession
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventSession.StartupState">
            <summary>
            StartupState property class of EventSession
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventSession.TrackCausality">
            <summary>
            TrackCausality property class of EventSession
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventSession.MaxDispatchLatency">
            <summary>
            MaxDispatchLatency property class of EventSession
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventSession.MaxEventSizeUnit">
            <summary>
            MaxEventSizeUnit property class of EventSession
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventSession.MaxMemoryUnit">
            <summary>
            MaxMemoryUnit property class of EventSession
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventSession.EventRetentionMode">
            <summary>
            EventRetentionMode property class of EventSession
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventSession.MaxEventSize">
            <summary>
            MaxEventSize property class of EventSession
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventSession.MaxMemory">
            <summary>
            MaxMemory property class of EventSession
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventSession.MemoryPartitionMode">
            <summary>
            MemoryPartitionMode property class of EventSession
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventSession.SessionScope">
            <summary>
            SessionScope property class of EventSession
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventSession.EventDefinitions">
            <summary>
            EventDefinitions relationship class of EventSession
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventSession.EventTargets">
            <summary>
            EventTargets relationship class of EventSession
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.DatabaseEventSession">
            <summary>
            Model schema container class for DatabaseEventSession
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseEventSession.TypeClass">
            <summary>
            Type class for DatabaseEventSession
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseEventSession.StartupState">
            <summary>
            StartupState property class of DatabaseEventSession
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseEventSession.TrackCausality">
            <summary>
            TrackCausality property class of DatabaseEventSession
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseEventSession.MaxDispatchLatency">
            <summary>
            MaxDispatchLatency property class of DatabaseEventSession
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseEventSession.MaxEventSizeUnit">
            <summary>
            MaxEventSizeUnit property class of DatabaseEventSession
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseEventSession.MaxMemoryUnit">
            <summary>
            MaxMemoryUnit property class of DatabaseEventSession
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseEventSession.EventRetentionMode">
            <summary>
            EventRetentionMode property class of DatabaseEventSession
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseEventSession.MaxEventSize">
            <summary>
            MaxEventSize property class of DatabaseEventSession
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseEventSession.MaxMemory">
            <summary>
            MaxMemory property class of DatabaseEventSession
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseEventSession.MemoryPartitionMode">
            <summary>
            MemoryPartitionMode property class of DatabaseEventSession
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseEventSession.SessionScope">
            <summary>
            SessionScope property class of DatabaseEventSession
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseEventSession.EventDefinitions">
            <summary>
            EventDefinitions relationship class of DatabaseEventSession
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.DatabaseEventSession.EventTargets">
            <summary>
            EventTargets relationship class of DatabaseEventSession
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.EventSessionAction">
            <summary>
            Model schema container class for EventSessionAction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventSessionAction.TypeClass">
            <summary>
            Type class for EventSessionAction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventSessionAction.ActionName">
            <summary>
            ActionName property class of EventSessionAction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventSessionAction.EventModuleGuid">
            <summary>
            EventModuleGuid property class of EventSessionAction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventSessionAction.EventPackageName">
            <summary>
            EventPackageName property class of EventSessionAction
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.EventSessionDefinitions">
            <summary>
            Model schema container class for EventSessionDefinitions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventSessionDefinitions.TypeClass">
            <summary>
            Type class for EventSessionDefinitions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventSessionDefinitions.WhereExpression">
            <summary>
            WhereExpression property class of EventSessionDefinitions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventSessionDefinitions.EventModuleGuid">
            <summary>
            EventModuleGuid property class of EventSessionDefinitions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventSessionDefinitions.EventName">
            <summary>
            EventName property class of EventSessionDefinitions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventSessionDefinitions.EventPackageName">
            <summary>
            EventPackageName property class of EventSessionDefinitions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventSessionDefinitions.Actions">
            <summary>
            Actions relationship class of EventSessionDefinitions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventSessionDefinitions.AttributeSettings">
            <summary>
            AttributeSettings relationship class of EventSessionDefinitions
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.EventSessionSetting">
            <summary>
            Model schema container class for EventSessionSetting
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventSessionSetting.TypeClass">
            <summary>
            Type class for EventSessionSetting
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventSessionSetting.SettingValue">
            <summary>
            SettingValue property class of EventSessionSetting
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventSessionSetting.SettingName">
            <summary>
            SettingName property class of EventSessionSetting
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.EventSessionTarget">
            <summary>
            Model schema container class for EventSessionTarget
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventSessionTarget.TypeClass">
            <summary>
            Type class for EventSessionTarget
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventSessionTarget.EventModuleGuid">
            <summary>
            EventModuleGuid property class of EventSessionTarget
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventSessionTarget.EventPackageName">
            <summary>
            EventPackageName property class of EventSessionTarget
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventSessionTarget.TargetName">
            <summary>
            TargetName property class of EventSessionTarget
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventSessionTarget.ParameterSettings">
            <summary>
            ParameterSettings relationship class of EventSessionTarget
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.EventTypeSpecifier">
            <summary>
            Model schema container class for EventTypeSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventTypeSpecifier.TypeClass">
            <summary>
            Type class for EventTypeSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventTypeSpecifier.EventType">
            <summary>
            EventType property class of EventTypeSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.EventTypeSpecifier.Order">
            <summary>
            Order property class of EventTypeSpecifier
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ExtendedProcedure">
            <summary>
            Model schema container class for ExtendedProcedure
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExtendedProcedure.TypeClass">
            <summary>
            Type class for ExtendedProcedure
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExtendedProcedure.ExeccuteAsCaller">
            <summary>
            ExeccuteAsCaller property class of ExtendedProcedure
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExtendedProcedure.WithEncryption">
            <summary>
            WithEncryption property class of ExtendedProcedure
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExtendedProcedure.ExecuteAsOwner">
            <summary>
            ExecuteAsOwner property class of ExtendedProcedure
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExtendedProcedure.ExecuteAsSelf">
            <summary>
            ExecuteAsSelf property class of ExtendedProcedure
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExtendedProcedure.Login">
            <summary>
            Login relationship class of ExtendedProcedure
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExtendedProcedure.Parameters">
            <summary>
            Parameters relationship class of ExtendedProcedure
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExtendedProcedure.Schema">
            <summary>
            Schema relationship class of ExtendedProcedure
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExtendedProcedure.User">
            <summary>
            User relationship class of ExtendedProcedure
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ExtendedProperty">
            <summary>
            Model schema container class for ExtendedProperty
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExtendedProperty.TypeClass">
            <summary>
            Type class for ExtendedProperty
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExtendedProperty.Value">
            <summary>
            Value property class of ExtendedProperty
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExtendedProperty.Host">
            <summary>
            Host relationship class of ExtendedProperty
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ExternalDataSource">
            <summary>
            Model schema container class for ExternalDataSource
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalDataSource.TypeClass">
            <summary>
            Type class for ExternalDataSource
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalDataSource.DatabaseName">
            <summary>
            DatabaseName property class of ExternalDataSource
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalDataSource.DataSourceType">
            <summary>
            DataSourceType property class of ExternalDataSource
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalDataSource.Location">
            <summary>
            Location property class of ExternalDataSource
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalDataSource.ResourceManagerLocation">
            <summary>
            ResourceManagerLocation property class of ExternalDataSource
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalDataSource.ShardMapName">
            <summary>
            ShardMapName property class of ExternalDataSource
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalDataSource.Credential">
            <summary>
            Credential relationship class of ExternalDataSource
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ExternalFileFormat">
            <summary>
            Model schema container class for ExternalFileFormat
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalFileFormat.TypeClass">
            <summary>
            Type class for ExternalFileFormat
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalFileFormat.DataCompression">
            <summary>
            DataCompression property class of ExternalFileFormat
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalFileFormat.DateFormat">
            <summary>
            DateFormat property class of ExternalFileFormat
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalFileFormat.FieldTerminator">
            <summary>
            FieldTerminator property class of ExternalFileFormat
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalFileFormat.FormatType">
            <summary>
            FormatType property class of ExternalFileFormat
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalFileFormat.SerDeMethod">
            <summary>
            SerDeMethod property class of ExternalFileFormat
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalFileFormat.StringDelimiter">
            <summary>
            StringDelimiter property class of ExternalFileFormat
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalFileFormat.UseTypeDefault">
            <summary>
            UseTypeDefault property class of ExternalFileFormat
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ExternalTable">
            <summary>
            Model schema container class for ExternalTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalTable.TypeClass">
            <summary>
            Type class for ExternalTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalTable.DistributionPolicyType">
            <summary>
            DistributionPolicyType property class of ExternalTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalTable.ExternalObjectName">
            <summary>
            ExternalObjectName property class of ExternalTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalTable.ExternalSchemaName">
            <summary>
            ExternalSchemaName property class of ExternalTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalTable.IsAnsiNullsOn">
            <summary>
            IsAnsiNullsOn property class of ExternalTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalTable.IsFileStreamNull">
            <summary>
            IsFileStreamNull property class of ExternalTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalTable.IsQuotedIdentifierOn">
            <summary>
            IsQuotedIdentifierOn property class of ExternalTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalTable.IsTableLockOnBulkLoad">
            <summary>
            IsTableLockOnBulkLoad property class of ExternalTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalTable.Location">
            <summary>
            Location property class of ExternalTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalTable.LockEscalation">
            <summary>
            LockEscalation property class of ExternalTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalTable.RejectSampleValue">
            <summary>
            RejectSampleValue property class of ExternalTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalTable.RejectType">
            <summary>
            RejectType property class of ExternalTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalTable.RejectValue">
            <summary>
            RejectValue property class of ExternalTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalTable.Columns">
            <summary>
            Columns relationship class of ExternalTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalTable.DataCompressionOptions">
            <summary>
            DataCompressionOptions relationship class of ExternalTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalTable.DataSourceName">
            <summary>
            DataSourceName relationship class of ExternalTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalTable.FileFormatName">
            <summary>
            FileFormatName relationship class of ExternalTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalTable.Filegroup">
            <summary>
            Filegroup relationship class of ExternalTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalTable.FileStreamFilegroup">
            <summary>
            FileStreamFilegroup relationship class of ExternalTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalTable.FileStreamPartitionScheme">
            <summary>
            FileStreamPartitionScheme relationship class of ExternalTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalTable.PartitionColumn">
            <summary>
            PartitionColumn relationship class of ExternalTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalTable.PartitionScheme">
            <summary>
            PartitionScheme relationship class of ExternalTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalTable.Schema">
            <summary>
            Schema relationship class of ExternalTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ExternalTable.ShardingColumn">
            <summary>
            ShardingColumn relationship class of ExternalTable
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.SqlFile">
            <summary>
            Model schema container class for SqlFile
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SqlFile.TypeClass">
            <summary>
            Type class for SqlFile
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SqlFile.Offline">
            <summary>
            Offline property class of SqlFile
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SqlFile.Unlimited">
            <summary>
            Unlimited property class of SqlFile
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SqlFile.FileGrowth">
            <summary>
            FileGrowth property class of SqlFile
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SqlFile.FileGrowthUnit">
            <summary>
            FileGrowthUnit property class of SqlFile
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SqlFile.FileName">
            <summary>
            FileName property class of SqlFile
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SqlFile.IsLogFile">
            <summary>
            IsLogFile property class of SqlFile
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SqlFile.MaxSize">
            <summary>
            MaxSize property class of SqlFile
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SqlFile.MaxSizeUnit">
            <summary>
            MaxSizeUnit property class of SqlFile
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SqlFile.Size">
            <summary>
            Size property class of SqlFile
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SqlFile.SizeUnit">
            <summary>
            SizeUnit property class of SqlFile
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SqlFile.Filegroup">
            <summary>
            Filegroup relationship class of SqlFile
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.Filegroup">
            <summary>
            Model schema container class for Filegroup
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Filegroup.TypeClass">
            <summary>
            Type class for Filegroup
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Filegroup.ContainsFileStream">
            <summary>
            ContainsFileStream property class of Filegroup
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Filegroup.ReadOnly">
            <summary>
            ReadOnly property class of Filegroup
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Filegroup.ContainsMemoryOptimizedData">
            <summary>
            ContainsMemoryOptimizedData property class of Filegroup
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ForeignKeyConstraint">
            <summary>
            Model schema container class for ForeignKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ForeignKeyConstraint.TypeClass">
            <summary>
            Type class for ForeignKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ForeignKeyConstraint.Disabled">
            <summary>
            Disabled property class of ForeignKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ForeignKeyConstraint.NotForReplication">
            <summary>
            NotForReplication property class of ForeignKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ForeignKeyConstraint.DeleteAction">
            <summary>
            DeleteAction property class of ForeignKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ForeignKeyConstraint.UpdateAction">
            <summary>
            UpdateAction property class of ForeignKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ForeignKeyConstraint.Host">
            <summary>
            Host relationship class of ForeignKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ForeignKeyConstraint.Columns">
            <summary>
            Columns relationship class of ForeignKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ForeignKeyConstraint.ForeignColumns">
            <summary>
            ForeignColumns relationship class of ForeignKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ForeignKeyConstraint.ForeignTable">
            <summary>
            ForeignTable relationship class of ForeignKeyConstraint
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.FullTextCatalog">
            <summary>
            Model schema container class for FullTextCatalog
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FullTextCatalog.TypeClass">
            <summary>
            Type class for FullTextCatalog
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FullTextCatalog.AccentSensitivity">
            <summary>
            AccentSensitivity property class of FullTextCatalog
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FullTextCatalog.IsDefault">
            <summary>
            IsDefault property class of FullTextCatalog
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FullTextCatalog.Path">
            <summary>
            Path property class of FullTextCatalog
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FullTextCatalog.Authorizer">
            <summary>
            Authorizer relationship class of FullTextCatalog
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FullTextCatalog.Filegroup">
            <summary>
            Filegroup relationship class of FullTextCatalog
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.FullTextIndex">
            <summary>
            Model schema container class for FullTextIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FullTextIndex.TypeClass">
            <summary>
            Type class for FullTextIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FullTextIndex.ChangeTracking">
            <summary>
            ChangeTracking property class of FullTextIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FullTextIndex.UseSystemStopList">
            <summary>
            UseSystemStopList property class of FullTextIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FullTextIndex.Disabled">
            <summary>
            Disabled property class of FullTextIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FullTextIndex.Replicated">
            <summary>
            Replicated property class of FullTextIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FullTextIndex.StopListOff">
            <summary>
            StopListOff property class of FullTextIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FullTextIndex.UniqueIndexName">
            <summary>
            UniqueIndexName relationship class of FullTextIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FullTextIndex.Catalog">
            <summary>
            Catalog relationship class of FullTextIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FullTextIndex.Columns">
            <summary>
            Columns relationship class of FullTextIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FullTextIndex.Filegroup">
            <summary>
            Filegroup relationship class of FullTextIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FullTextIndex.IndexedObject">
            <summary>
            IndexedObject relationship class of FullTextIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FullTextIndex.SearchPropertyList">
            <summary>
            SearchPropertyList relationship class of FullTextIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FullTextIndex.StopList">
            <summary>
            StopList relationship class of FullTextIndex
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.FullTextIndexColumnSpecifier">
            <summary>
            Model schema container class for FullTextIndexColumnSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FullTextIndexColumnSpecifier.TypeClass">
            <summary>
            Type class for FullTextIndexColumnSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FullTextIndexColumnSpecifier.PartOfStatisticalSemantics">
            <summary>
            PartOfStatisticalSemantics property class of FullTextIndexColumnSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FullTextIndexColumnSpecifier.LanguageId">
            <summary>
            LanguageId property class of FullTextIndexColumnSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FullTextIndexColumnSpecifier.Column">
            <summary>
            Column relationship class of FullTextIndexColumnSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FullTextIndexColumnSpecifier.TypeColumn">
            <summary>
            TypeColumn relationship class of FullTextIndexColumnSpecifier
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.FullTextStopList">
            <summary>
            Model schema container class for FullTextStopList
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FullTextStopList.TypeClass">
            <summary>
            Type class for FullTextStopList
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FullTextStopList.Authorizer">
            <summary>
            Authorizer relationship class of FullTextStopList
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.HttpProtocolSpecifier">
            <summary>
            Model schema container class for HttpProtocolSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.HttpProtocolSpecifier.TypeClass">
            <summary>
            Type class for HttpProtocolSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.HttpProtocolSpecifier.CompressionEnabled">
            <summary>
            CompressionEnabled property class of HttpProtocolSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.HttpProtocolSpecifier.ListeningOnAllNoneReservedSites">
            <summary>
            ListeningOnAllNoneReservedSites property class of HttpProtocolSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.HttpProtocolSpecifier.ListeningOnAllSites">
            <summary>
            ListeningOnAllSites property class of HttpProtocolSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.HttpProtocolSpecifier.AuthenticationMode">
            <summary>
            AuthenticationMode property class of HttpProtocolSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.HttpProtocolSpecifier.AuthenticationRealm">
            <summary>
            AuthenticationRealm property class of HttpProtocolSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.HttpProtocolSpecifier.ClearPort">
            <summary>
            ClearPort property class of HttpProtocolSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.HttpProtocolSpecifier.DefaultLogonDomain">
            <summary>
            DefaultLogonDomain property class of HttpProtocolSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.HttpProtocolSpecifier.Path">
            <summary>
            Path property class of HttpProtocolSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.HttpProtocolSpecifier.Ports">
            <summary>
            Ports property class of HttpProtocolSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.HttpProtocolSpecifier.SslPort">
            <summary>
            SslPort property class of HttpProtocolSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.HttpProtocolSpecifier.Website">
            <summary>
            Website property class of HttpProtocolSpecifier
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.LinkedServer">
            <summary>
            Model schema container class for LinkedServer
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.LinkedServer.TypeClass">
            <summary>
            Type class for LinkedServer
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.LinkedServer.CollationCompatible">
            <summary>
            CollationCompatible property class of LinkedServer
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.LinkedServer.DataAccess">
            <summary>
            DataAccess property class of LinkedServer
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.LinkedServer.LazySchemaValidationEnabled">
            <summary>
            LazySchemaValidationEnabled property class of LinkedServer
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.LinkedServer.RemoteProcTransactionPromotionEnabled">
            <summary>
            RemoteProcTransactionPromotionEnabled property class of LinkedServer
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.LinkedServer.RpcEnabled">
            <summary>
            RpcEnabled property class of LinkedServer
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.LinkedServer.RpcOutEnabled">
            <summary>
            RpcOutEnabled property class of LinkedServer
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.LinkedServer.ProviderName">
            <summary>
            ProviderName property class of LinkedServer
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.LinkedServer.Catalog">
            <summary>
            Catalog property class of LinkedServer
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.LinkedServer.CollationName">
            <summary>
            CollationName property class of LinkedServer
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.LinkedServer.ConnectTimeout">
            <summary>
            ConnectTimeout property class of LinkedServer
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.LinkedServer.DataSource">
            <summary>
            DataSource property class of LinkedServer
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.LinkedServer.IsDistributor">
            <summary>
            IsDistributor property class of LinkedServer
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.LinkedServer.IsPublisher">
            <summary>
            IsPublisher property class of LinkedServer
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.LinkedServer.IsSubscriber">
            <summary>
            IsSubscriber property class of LinkedServer
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.LinkedServer.Location">
            <summary>
            Location property class of LinkedServer
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.LinkedServer.ProductName">
            <summary>
            ProductName property class of LinkedServer
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.LinkedServer.ProviderString">
            <summary>
            ProviderString property class of LinkedServer
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.LinkedServer.QueryTimeout">
            <summary>
            QueryTimeout property class of LinkedServer
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.LinkedServer.UseRemoteCollation">
            <summary>
            UseRemoteCollation property class of LinkedServer
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.LinkedServerLogin">
            <summary>
            Model schema container class for LinkedServerLogin
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.LinkedServerLogin.TypeClass">
            <summary>
            Type class for LinkedServerLogin
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.LinkedServerLogin.UseSelf">
            <summary>
            UseSelf property class of LinkedServerLogin
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.LinkedServerLogin.LinkedServerLoginName">
            <summary>
            LinkedServerLoginName property class of LinkedServerLogin
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.LinkedServerLogin.LinkedServerPassword">
            <summary>
            LinkedServerPassword property class of LinkedServerLogin
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.LinkedServerLogin.LinkedServer">
            <summary>
            LinkedServer relationship class of LinkedServerLogin
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.LinkedServerLogin.LocalLogin">
            <summary>
            LocalLogin relationship class of LinkedServerLogin
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.Login">
            <summary>
            Model schema container class for Login
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Login.TypeClass">
            <summary>
            Type class for Login
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Login.CheckExpiration">
            <summary>
            CheckExpiration property class of Login
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Login.CheckPolicy">
            <summary>
            CheckPolicy property class of Login
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Login.Disabled">
            <summary>
            Disabled property class of Login
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Login.MappedToWindowsLogin">
            <summary>
            MappedToWindowsLogin property class of Login
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Login.PasswordHashed">
            <summary>
            PasswordHashed property class of Login
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Login.PasswordMustChange">
            <summary>
            PasswordMustChange property class of Login
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Login.DefaultDatabase">
            <summary>
            DefaultDatabase property class of Login
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Login.DefaultLanguage">
            <summary>
            DefaultLanguage property class of Login
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Login.EncryptionOption">
            <summary>
            EncryptionOption property class of Login
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Login.Password">
            <summary>
            Password property class of Login
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Login.Sid">
            <summary>
            Sid property class of Login
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Login.AsymmetricKey">
            <summary>
            AsymmetricKey relationship class of Login
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Login.Certificate">
            <summary>
            Certificate relationship class of Login
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Login.Credential">
            <summary>
            Credential relationship class of Login
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.MasterKey">
            <summary>
            Model schema container class for MasterKey
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.MasterKey.TypeClass">
            <summary>
            Type class for MasterKey
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.MasterKey.Password">
            <summary>
            Password property class of MasterKey
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.MessageType">
            <summary>
            Model schema container class for MessageType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.MessageType.TypeClass">
            <summary>
            Type class for MessageType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.MessageType.ValidationMethod">
            <summary>
            ValidationMethod property class of MessageType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.MessageType.Authorizer">
            <summary>
            Authorizer relationship class of MessageType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.MessageType.XmlSchemaCollection">
            <summary>
            XmlSchemaCollection relationship class of MessageType
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.PartitionFunction">
            <summary>
            Model schema container class for PartitionFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PartitionFunction.TypeClass">
            <summary>
            Type class for PartitionFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PartitionFunction.Range">
            <summary>
            Range property class of PartitionFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PartitionFunction.ParameterType">
            <summary>
            ParameterType relationship class of PartitionFunction
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PartitionFunction.BoundaryValues">
            <summary>
            BoundaryValues relationship class of PartitionFunction
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.PartitionScheme">
            <summary>
            Model schema container class for PartitionScheme
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PartitionScheme.TypeClass">
            <summary>
            Type class for PartitionScheme
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PartitionScheme.AllToOneFilegroup">
            <summary>
            AllToOneFilegroup property class of PartitionScheme
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PartitionScheme.Filegroups">
            <summary>
            Filegroups relationship class of PartitionScheme
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PartitionScheme.PartitionFunction">
            <summary>
            PartitionFunction relationship class of PartitionScheme
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.PartitionScheme.FilegroupsRelationship">
            <summary>
            Model schema container class for FilegroupsRelationship
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PartitionScheme.FilegroupsRelationship.RelationshipClass">
            <summary>
            Relationship class for FilegroupsRelationship
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PartitionScheme.FilegroupsRelationship.IsDefault">
            <summary>
            IsDefault property class of FilegroupsRelationship
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.PartitionValue">
            <summary>
            Model schema container class for PartitionValue
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PartitionValue.TypeClass">
            <summary>
            Type class for PartitionValue
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PartitionValue.Expression">
            <summary>
            Expression property class of PartitionValue
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PartitionValue.ExpressionDependencies">
            <summary>
            ExpressionDependencies relationship class of PartitionValue
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.Permission">
            <summary>
            Model schema container class for Permission
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Permission.TypeClass">
            <summary>
            Type class for Permission
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Permission.WithAllPrivileges">
            <summary>
            WithAllPrivileges property class of Permission
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Permission.WithGrantOption">
            <summary>
            WithGrantOption property class of Permission
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Permission.PermissionType">
            <summary>
            PermissionType property class of Permission
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Permission.PermissionAction">
            <summary>
            PermissionAction property class of Permission
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Permission.ExcludedColumns">
            <summary>
            ExcludedColumns relationship class of Permission
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Permission.Grantee">
            <summary>
            Grantee relationship class of Permission
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Permission.Grantor">
            <summary>
            Grantor relationship class of Permission
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Permission.RevokedGrantOptionColumns">
            <summary>
            RevokedGrantOptionColumns relationship class of Permission
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Permission.SecuredObject">
            <summary>
            SecuredObject relationship class of Permission
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.PrimaryKeyConstraint">
            <summary>
            Model schema container class for PrimaryKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PrimaryKeyConstraint.TypeClass">
            <summary>
            Type class for PrimaryKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PrimaryKeyConstraint.AllowPageLocks">
            <summary>
            AllowPageLocks property class of PrimaryKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PrimaryKeyConstraint.AllowRowLocks">
            <summary>
            AllowRowLocks property class of PrimaryKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PrimaryKeyConstraint.IgnoreDuplicateKey">
            <summary>
            IgnoreDuplicateKey property class of PrimaryKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PrimaryKeyConstraint.RecomputeStatistics">
            <summary>
            RecomputeStatistics property class of PrimaryKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PrimaryKeyConstraint.Clustered">
            <summary>
            Clustered property class of PrimaryKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PrimaryKeyConstraint.Disabled">
            <summary>
            Disabled property class of PrimaryKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PrimaryKeyConstraint.FileStreamNull">
            <summary>
            FileStreamNull property class of PrimaryKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PrimaryKeyConstraint.WithPadIndex">
            <summary>
            WithPadIndex property class of PrimaryKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PrimaryKeyConstraint.Hash">
            <summary>
            Hash property class of PrimaryKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PrimaryKeyConstraint.BucketCount">
            <summary>
            BucketCount property class of PrimaryKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PrimaryKeyConstraint.FillFactor">
            <summary>
            FillFactor property class of PrimaryKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PrimaryKeyConstraint.Host">
            <summary>
            Host relationship class of PrimaryKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PrimaryKeyConstraint.Columns">
            <summary>
            Columns relationship class of PrimaryKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PrimaryKeyConstraint.DataCompressionOptions">
            <summary>
            DataCompressionOptions relationship class of PrimaryKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PrimaryKeyConstraint.Filegroup">
            <summary>
            Filegroup relationship class of PrimaryKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PrimaryKeyConstraint.FileStreamFilegroup">
            <summary>
            FileStreamFilegroup relationship class of PrimaryKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PrimaryKeyConstraint.FileStreamPartitionScheme">
            <summary>
            FileStreamPartitionScheme relationship class of PrimaryKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PrimaryKeyConstraint.PartitionColumn">
            <summary>
            PartitionColumn relationship class of PrimaryKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PrimaryKeyConstraint.PartitionScheme">
            <summary>
            PartitionScheme relationship class of PrimaryKeyConstraint
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.PrimaryKeyConstraint.ColumnsRelationship">
            <summary>
            Model schema container class for ColumnsRelationship
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PrimaryKeyConstraint.ColumnsRelationship.RelationshipClass">
            <summary>
            Relationship class for ColumnsRelationship
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PrimaryKeyConstraint.ColumnsRelationship.Ascending">
            <summary>
            Ascending property class of ColumnsRelationship
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.Procedure">
            <summary>
            Model schema container class for Procedure
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Procedure.TypeClass">
            <summary>
            Type class for Procedure
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Procedure.AnsiNullsOn">
            <summary>
            AnsiNullsOn property class of Procedure
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Procedure.ExecuteAsCaller">
            <summary>
            ExecuteAsCaller property class of Procedure
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Procedure.WithEncryption">
            <summary>
            WithEncryption property class of Procedure
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Procedure.ForReplication">
            <summary>
            ForReplication property class of Procedure
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Procedure.ExecuteAsOwner">
            <summary>
            ExecuteAsOwner property class of Procedure
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Procedure.QuotedIdentifierOn">
            <summary>
            QuotedIdentifierOn property class of Procedure
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Procedure.WithRecompile">
            <summary>
            WithRecompile property class of Procedure
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Procedure.ExecuteAsSelf">
            <summary>
            ExecuteAsSelf property class of Procedure
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Procedure.WithNativeCompilation">
            <summary>
            WithNativeCompilation property class of Procedure
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Procedure.WithSchemaBinding">
            <summary>
            WithSchemaBinding property class of Procedure
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Procedure.ClassName">
            <summary>
            ClassName property class of Procedure
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Procedure.IsReplicated">
            <summary>
            IsReplicated property class of Procedure
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Procedure.MethodName">
            <summary>
            MethodName property class of Procedure
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Procedure.ParentProcedure">
            <summary>
            ParentProcedure relationship class of Procedure
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Procedure.Assembly">
            <summary>
            Assembly relationship class of Procedure
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Procedure.BodyDependencies">
            <summary>
            BodyDependencies relationship class of Procedure
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Procedure.Login">
            <summary>
            Login relationship class of Procedure
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Procedure.Parameters">
            <summary>
            Parameters relationship class of Procedure
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Procedure.Schema">
            <summary>
            Schema relationship class of Procedure
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Procedure.User">
            <summary>
            User relationship class of Procedure
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.Queue">
            <summary>
            Model schema container class for Queue
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Queue.TypeClass">
            <summary>
            Type class for Queue
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Queue.ActivationStatusOn">
            <summary>
            ActivationStatusOn property class of Queue
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Queue.ActivationExecuteAsCaller">
            <summary>
            ActivationExecuteAsCaller property class of Queue
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Queue.ActivationExecuteAsOwner">
            <summary>
            ActivationExecuteAsOwner property class of Queue
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Queue.PoisonMessageHandlingStatusOn">
            <summary>
            PoisonMessageHandlingStatusOn property class of Queue
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Queue.RetentionOn">
            <summary>
            RetentionOn property class of Queue
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Queue.ActivationExecuteAsSelf">
            <summary>
            ActivationExecuteAsSelf property class of Queue
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Queue.StatusOn">
            <summary>
            StatusOn property class of Queue
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Queue.ActivationMaxQueueReaders">
            <summary>
            ActivationMaxQueueReaders property class of Queue
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Queue.ActivationProcedure">
            <summary>
            ActivationProcedure relationship class of Queue
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Queue.Columns">
            <summary>
            Columns relationship class of Queue
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Queue.Filegroup">
            <summary>
            Filegroup relationship class of Queue
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Queue.Login">
            <summary>
            Login relationship class of Queue
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Queue.PartitionColumn">
            <summary>
            PartitionColumn relationship class of Queue
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Queue.PartitionScheme">
            <summary>
            PartitionScheme relationship class of Queue
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Queue.Schema">
            <summary>
            Schema relationship class of Queue
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Queue.User">
            <summary>
            User relationship class of Queue
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.QueueEventNotification">
            <summary>
            Model schema container class for QueueEventNotification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.QueueEventNotification.TypeClass">
            <summary>
            Type class for QueueEventNotification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.QueueEventNotification.WithFanIn">
            <summary>
            WithFanIn property class of QueueEventNotification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.QueueEventNotification.BrokerService">
            <summary>
            BrokerService property class of QueueEventNotification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.QueueEventNotification.BrokerInstanceSpecifier">
            <summary>
            BrokerInstanceSpecifier property class of QueueEventNotification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.QueueEventNotification.EventGroup">
            <summary>
            EventGroup relationship class of QueueEventNotification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.QueueEventNotification.EventType">
            <summary>
            EventType relationship class of QueueEventNotification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.QueueEventNotification.Queue">
            <summary>
            Queue relationship class of QueueEventNotification
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.RemoteServiceBinding">
            <summary>
            Model schema container class for RemoteServiceBinding
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.RemoteServiceBinding.TypeClass">
            <summary>
            Type class for RemoteServiceBinding
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.RemoteServiceBinding.Anonymous">
            <summary>
            Anonymous property class of RemoteServiceBinding
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.RemoteServiceBinding.Service">
            <summary>
            Service property class of RemoteServiceBinding
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.RemoteServiceBinding.User">
            <summary>
            User relationship class of RemoteServiceBinding
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.RemoteServiceBinding.Authorizer">
            <summary>
            Authorizer relationship class of RemoteServiceBinding
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ResourceGovernor">
            <summary>
            Model schema container class for ResourceGovernor
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ResourceGovernor.TypeClass">
            <summary>
            Type class for ResourceGovernor
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ResourceGovernor.Enabled">
            <summary>
            Enabled property class of ResourceGovernor
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ResourceGovernor.ClassifierFunction">
            <summary>
            ClassifierFunction relationship class of ResourceGovernor
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ResourcePool">
            <summary>
            Model schema container class for ResourcePool
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ResourcePool.TypeClass">
            <summary>
            Type class for ResourcePool
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ResourcePool.CapCpuPercent">
            <summary>
            CapCpuPercent property class of ResourcePool
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ResourcePool.MaxCpuPercent">
            <summary>
            MaxCpuPercent property class of ResourcePool
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ResourcePool.MaxIopsPerVolume">
            <summary>
            MaxIopsPerVolume property class of ResourcePool
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ResourcePool.MaxMemoryPercent">
            <summary>
            MaxMemoryPercent property class of ResourcePool
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ResourcePool.MinCpuPercent">
            <summary>
            MinCpuPercent property class of ResourcePool
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ResourcePool.MinIopsPerVolume">
            <summary>
            MinIopsPerVolume property class of ResourcePool
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ResourcePool.MinMemoryPercent">
            <summary>
            MinMemoryPercent property class of ResourcePool
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.Role">
            <summary>
            Model schema container class for Role
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Role.TypeClass">
            <summary>
            Type class for Role
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Role.Authorizer">
            <summary>
            Authorizer relationship class of Role
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.RoleMembership">
            <summary>
            Model schema container class for RoleMembership
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.RoleMembership.TypeClass">
            <summary>
            Type class for RoleMembership
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.RoleMembership.Member">
            <summary>
            Member relationship class of RoleMembership
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.RoleMembership.Role">
            <summary>
            Role relationship class of RoleMembership
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.Route">
            <summary>
            Model schema container class for Route
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Route.TypeClass">
            <summary>
            Type class for Route
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Route.BrokerInstance">
            <summary>
            BrokerInstance property class of Route
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Route.ServiceName">
            <summary>
            ServiceName property class of Route
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Route.Address">
            <summary>
            Address property class of Route
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Route.Lifetime">
            <summary>
            Lifetime property class of Route
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Route.MirrorAddress">
            <summary>
            MirrorAddress property class of Route
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Route.Authorizer">
            <summary>
            Authorizer relationship class of Route
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.Rule">
            <summary>
            Model schema container class for Rule
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Rule.TypeClass">
            <summary>
            Type class for Rule
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Rule.Expression">
            <summary>
            Expression property class of Rule
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Rule.BoundObjects">
            <summary>
            BoundObjects relationship class of Rule
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Rule.Schema">
            <summary>
            Schema relationship class of Rule
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.Schema">
            <summary>
            Model schema container class for Schema
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Schema.TypeClass">
            <summary>
            Type class for Schema
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Schema.Authorizer">
            <summary>
            Authorizer relationship class of Schema
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.SearchProperty">
            <summary>
            Model schema container class for SearchProperty
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SearchProperty.TypeClass">
            <summary>
            Type class for SearchProperty
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SearchProperty.Description">
            <summary>
            Description property class of SearchProperty
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SearchProperty.Identifier">
            <summary>
            Identifier property class of SearchProperty
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SearchProperty.PropertySetGuid">
            <summary>
            PropertySetGuid property class of SearchProperty
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SearchProperty.SearchPropertyList">
            <summary>
            SearchPropertyList relationship class of SearchProperty
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.SearchPropertyList">
            <summary>
            Model schema container class for SearchPropertyList
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SearchPropertyList.TypeClass">
            <summary>
            Type class for SearchPropertyList
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SearchPropertyList.Authorizer">
            <summary>
            Authorizer relationship class of SearchPropertyList
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.SecurityPolicy">
            <summary>
            Model schema container class for SecurityPolicy
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SecurityPolicy.TypeClass">
            <summary>
            Type class for SecurityPolicy
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SecurityPolicy.Enabled">
            <summary>
            Enabled property class of SecurityPolicy
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SecurityPolicy.IsSchemaBound">
            <summary>
            IsSchemaBound property class of SecurityPolicy
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SecurityPolicy.NotForReplication">
            <summary>
            NotForReplication property class of SecurityPolicy
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SecurityPolicy.Predicates">
            <summary>
            Predicates relationship class of SecurityPolicy
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SecurityPolicy.Schema">
            <summary>
            Schema relationship class of SecurityPolicy
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.SecurityPredicate">
            <summary>
            Model schema container class for SecurityPredicate
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SecurityPredicate.TypeClass">
            <summary>
            Type class for SecurityPredicate
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SecurityPredicate.BodyScript">
            <summary>
            BodyScript property class of SecurityPredicate
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SecurityPredicate.Operation">
            <summary>
            Operation property class of SecurityPredicate
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SecurityPredicate.PredicateType">
            <summary>
            PredicateType property class of SecurityPredicate
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SecurityPredicate.BodyDependencies">
            <summary>
            BodyDependencies relationship class of SecurityPredicate
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SecurityPredicate.TargetObject">
            <summary>
            TargetObject relationship class of SecurityPredicate
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.Sequence">
            <summary>
            Model schema container class for Sequence
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Sequence.TypeClass">
            <summary>
            Type class for Sequence
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Sequence.NoMaxValue">
            <summary>
            NoMaxValue property class of Sequence
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Sequence.NoMinValue">
            <summary>
            NoMinValue property class of Sequence
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Sequence.IncrementValue">
            <summary>
            IncrementValue property class of Sequence
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Sequence.CacheSize">
            <summary>
            CacheSize property class of Sequence
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Sequence.IsCached">
            <summary>
            IsCached property class of Sequence
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Sequence.IsCycling">
            <summary>
            IsCycling property class of Sequence
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Sequence.MaxValue">
            <summary>
            MaxValue property class of Sequence
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Sequence.MinValue">
            <summary>
            MinValue property class of Sequence
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Sequence.StartValue">
            <summary>
            StartValue property class of Sequence
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Sequence.DataType">
            <summary>
            DataType relationship class of Sequence
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Sequence.Schema">
            <summary>
            Schema relationship class of Sequence
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ServerAudit">
            <summary>
            Model schema container class for ServerAudit
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerAudit.TypeClass">
            <summary>
            Type class for ServerAudit
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerAudit.Disabled">
            <summary>
            Disabled property class of ServerAudit
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerAudit.ReserveDiskSpace">
            <summary>
            ReserveDiskSpace property class of ServerAudit
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerAudit.UnlimitedFileSize">
            <summary>
            UnlimitedFileSize property class of ServerAudit
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerAudit.UnlimitedMaxRolloverFiles">
            <summary>
            UnlimitedMaxRolloverFiles property class of ServerAudit
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerAudit.OnFailure">
            <summary>
            OnFailure property class of ServerAudit
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerAudit.PredicateExpression">
            <summary>
            PredicateExpression property class of ServerAudit
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerAudit.AuditGuid">
            <summary>
            AuditGuid property class of ServerAudit
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerAudit.AuditTarget">
            <summary>
            AuditTarget property class of ServerAudit
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerAudit.FilePath">
            <summary>
            FilePath property class of ServerAudit
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerAudit.MaxFiles">
            <summary>
            MaxFiles property class of ServerAudit
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerAudit.MaxRolloverFiles">
            <summary>
            MaxRolloverFiles property class of ServerAudit
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerAudit.MaxSize">
            <summary>
            MaxSize property class of ServerAudit
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerAudit.MaxSizeUnit">
            <summary>
            MaxSizeUnit property class of ServerAudit
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerAudit.QueueDelay">
            <summary>
            QueueDelay property class of ServerAudit
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ServerAuditSpecification">
            <summary>
            Model schema container class for ServerAuditSpecification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerAuditSpecification.TypeClass">
            <summary>
            Type class for ServerAuditSpecification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerAuditSpecification.StateOn">
            <summary>
            StateOn property class of ServerAuditSpecification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerAuditSpecification.AuditActionGroups">
            <summary>
            AuditActionGroups relationship class of ServerAuditSpecification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerAuditSpecification.ServerAudit">
            <summary>
            ServerAudit relationship class of ServerAuditSpecification
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ServerDdlTrigger">
            <summary>
            Model schema container class for ServerDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerDdlTrigger.TypeClass">
            <summary>
            Type class for ServerDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerDdlTrigger.AnsiNullsOn">
            <summary>
            AnsiNullsOn property class of ServerDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerDdlTrigger.ExecuteAsCaller">
            <summary>
            ExecuteAsCaller property class of ServerDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerDdlTrigger.Disabled">
            <summary>
            Disabled property class of ServerDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerDdlTrigger.WithEncryption">
            <summary>
            WithEncryption property class of ServerDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerDdlTrigger.ExecuteAsOwner">
            <summary>
            ExecuteAsOwner property class of ServerDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerDdlTrigger.QuotedIdentifierOn">
            <summary>
            QuotedIdentifierOn property class of ServerDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerDdlTrigger.ExecuteAsSelf">
            <summary>
            ExecuteAsSelf property class of ServerDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerDdlTrigger.TriggerType">
            <summary>
            TriggerType property class of ServerDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerDdlTrigger.ClassName">
            <summary>
            ClassName property class of ServerDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerDdlTrigger.IsLogon">
            <summary>
            IsLogon property class of ServerDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerDdlTrigger.MethodName">
            <summary>
            MethodName property class of ServerDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerDdlTrigger.Assembly">
            <summary>
            Assembly relationship class of ServerDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerDdlTrigger.BodyDependencies">
            <summary>
            BodyDependencies relationship class of ServerDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerDdlTrigger.EventGroup">
            <summary>
            EventGroup relationship class of ServerDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerDdlTrigger.EventType">
            <summary>
            EventType relationship class of ServerDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerDdlTrigger.Login">
            <summary>
            Login relationship class of ServerDdlTrigger
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerDdlTrigger.User">
            <summary>
            User relationship class of ServerDdlTrigger
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ServerEventNotification">
            <summary>
            Model schema container class for ServerEventNotification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerEventNotification.TypeClass">
            <summary>
            Type class for ServerEventNotification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerEventNotification.WithFanIn">
            <summary>
            WithFanIn property class of ServerEventNotification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerEventNotification.BrokerService">
            <summary>
            BrokerService property class of ServerEventNotification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerEventNotification.BrokerInstanceSpecifier">
            <summary>
            BrokerInstanceSpecifier property class of ServerEventNotification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerEventNotification.EventGroup">
            <summary>
            EventGroup relationship class of ServerEventNotification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerEventNotification.EventType">
            <summary>
            EventType relationship class of ServerEventNotification
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ServerOptions">
            <summary>
            Model schema container class for ServerOptions
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerOptions.TypeClass">
            <summary>
            Type class for ServerOptions
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ServerRoleMembership">
            <summary>
            Model schema container class for ServerRoleMembership
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerRoleMembership.TypeClass">
            <summary>
            Type class for ServerRoleMembership
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerRoleMembership.Member">
            <summary>
            Member relationship class of ServerRoleMembership
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServerRoleMembership.Role">
            <summary>
            Role relationship class of ServerRoleMembership
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.Service">
            <summary>
            Model schema container class for Service
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Service.TypeClass">
            <summary>
            Type class for Service
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Service.UseDefaultContract">
            <summary>
            UseDefaultContract property class of Service
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Service.Authorizer">
            <summary>
            Authorizer relationship class of Service
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Service.Contracts">
            <summary>
            Contracts relationship class of Service
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Service.Queue">
            <summary>
            Queue relationship class of Service
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ServiceBrokerLanguageSpecifier">
            <summary>
            Model schema container class for ServiceBrokerLanguageSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServiceBrokerLanguageSpecifier.TypeClass">
            <summary>
            Type class for ServiceBrokerLanguageSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServiceBrokerLanguageSpecifier.WindowsAuthenticationMode">
            <summary>
            WindowsAuthenticationMode property class of ServiceBrokerLanguageSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServiceBrokerLanguageSpecifier.UseCertificateFirst">
            <summary>
            UseCertificateFirst property class of ServiceBrokerLanguageSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServiceBrokerLanguageSpecifier.MessageForwardingEnabled">
            <summary>
            MessageForwardingEnabled property class of ServiceBrokerLanguageSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServiceBrokerLanguageSpecifier.EncryptionAlgorithmPart1">
            <summary>
            EncryptionAlgorithmPart1 property class of ServiceBrokerLanguageSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServiceBrokerLanguageSpecifier.EncryptionAlgorithmPart2">
            <summary>
            EncryptionAlgorithmPart2 property class of ServiceBrokerLanguageSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServiceBrokerLanguageSpecifier.EncryptionMode">
            <summary>
            EncryptionMode property class of ServiceBrokerLanguageSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServiceBrokerLanguageSpecifier.MessageForwardSize">
            <summary>
            MessageForwardSize property class of ServiceBrokerLanguageSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ServiceBrokerLanguageSpecifier.AuthenticationCertificate">
            <summary>
            AuthenticationCertificate relationship class of ServiceBrokerLanguageSpecifier
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.Signature">
            <summary>
            Model schema container class for Signature
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Signature.TypeClass">
            <summary>
            Type class for Signature
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Signature.IsCounterSignature">
            <summary>
            IsCounterSignature property class of Signature
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Signature.EncryptionMechanism">
            <summary>
            EncryptionMechanism relationship class of Signature
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Signature.SignedObject">
            <summary>
            SignedObject relationship class of Signature
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.SignatureEncryptionMechanism">
            <summary>
            Model schema container class for SignatureEncryptionMechanism
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SignatureEncryptionMechanism.TypeClass">
            <summary>
            Type class for SignatureEncryptionMechanism
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SignatureEncryptionMechanism.Password">
            <summary>
            Password property class of SignatureEncryptionMechanism
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SignatureEncryptionMechanism.SignedBlob">
            <summary>
            SignedBlob property class of SignatureEncryptionMechanism
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SignatureEncryptionMechanism.AsymmetricKey">
            <summary>
            AsymmetricKey relationship class of SignatureEncryptionMechanism
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SignatureEncryptionMechanism.Certificate">
            <summary>
            Certificate relationship class of SignatureEncryptionMechanism
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.SoapLanguageSpecifier">
            <summary>
            Model schema container class for SoapLanguageSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SoapLanguageSpecifier.TypeClass">
            <summary>
            Type class for SoapLanguageSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SoapLanguageSpecifier.BatchesEnabled">
            <summary>
            BatchesEnabled property class of SoapLanguageSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SoapLanguageSpecifier.SessionsEnabled">
            <summary>
            SessionsEnabled property class of SoapLanguageSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SoapLanguageSpecifier.SessionTimeoutNever">
            <summary>
            SessionTimeoutNever property class of SoapLanguageSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SoapLanguageSpecifier.CharacterSet">
            <summary>
            CharacterSet property class of SoapLanguageSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SoapLanguageSpecifier.DatabaseName">
            <summary>
            DatabaseName property class of SoapLanguageSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SoapLanguageSpecifier.HeaderLimit">
            <summary>
            HeaderLimit property class of SoapLanguageSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SoapLanguageSpecifier.IsDefaultDatabase">
            <summary>
            IsDefaultDatabase property class of SoapLanguageSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SoapLanguageSpecifier.IsDefaultNamespace">
            <summary>
            IsDefaultNamespace property class of SoapLanguageSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SoapLanguageSpecifier.IsDefaultWsdlSpName">
            <summary>
            IsDefaultWsdlSpName property class of SoapLanguageSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SoapLanguageSpecifier.LoginType">
            <summary>
            LoginType property class of SoapLanguageSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SoapLanguageSpecifier.Namespace">
            <summary>
            Namespace property class of SoapLanguageSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SoapLanguageSpecifier.SchemaType">
            <summary>
            SchemaType property class of SoapLanguageSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SoapLanguageSpecifier.SessionTimeout">
            <summary>
            SessionTimeout property class of SoapLanguageSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SoapLanguageSpecifier.WsdlSpName">
            <summary>
            WsdlSpName property class of SoapLanguageSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SoapLanguageSpecifier.WebMethods">
            <summary>
            WebMethods relationship class of SoapLanguageSpecifier
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.SoapMethodSpecification">
            <summary>
            Model schema container class for SoapMethodSpecification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SoapMethodSpecification.TypeClass">
            <summary>
            Type class for SoapMethodSpecification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SoapMethodSpecification.SchemaType">
            <summary>
            SchemaType property class of SoapMethodSpecification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SoapMethodSpecification.Format">
            <summary>
            Format property class of SoapMethodSpecification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SoapMethodSpecification.WebMethodAlias">
            <summary>
            WebMethodAlias property class of SoapMethodSpecification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SoapMethodSpecification.WebMethodNamespace">
            <summary>
            WebMethodNamespace property class of SoapMethodSpecification
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SoapMethodSpecification.RelatedMethod">
            <summary>
            RelatedMethod relationship class of SoapMethodSpecification
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.SpatialIndex">
            <summary>
            Model schema container class for SpatialIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SpatialIndex.TypeClass">
            <summary>
            Type class for SpatialIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SpatialIndex.AllowPageLocks">
            <summary>
            AllowPageLocks property class of SpatialIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SpatialIndex.AllowRowLocks">
            <summary>
            AllowRowLocks property class of SpatialIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SpatialIndex.IgnoreDuplicateKey">
            <summary>
            IgnoreDuplicateKey property class of SpatialIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SpatialIndex.RecomputeStatistics">
            <summary>
            RecomputeStatistics property class of SpatialIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SpatialIndex.GridLevel1Density">
            <summary>
            GridLevel1Density property class of SpatialIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SpatialIndex.GridLevel2Density">
            <summary>
            GridLevel2Density property class of SpatialIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SpatialIndex.GridLevel3Density">
            <summary>
            GridLevel3Density property class of SpatialIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SpatialIndex.GridLevel4Density">
            <summary>
            GridLevel4Density property class of SpatialIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SpatialIndex.Disabled">
            <summary>
            Disabled property class of SpatialIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SpatialIndex.WithPadIndex">
            <summary>
            WithPadIndex property class of SpatialIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SpatialIndex.CellsPerObject">
            <summary>
            CellsPerObject property class of SpatialIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SpatialIndex.DataCompression">
            <summary>
            DataCompression property class of SpatialIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SpatialIndex.FillFactor">
            <summary>
            FillFactor property class of SpatialIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SpatialIndex.Tessellation">
            <summary>
            Tessellation property class of SpatialIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SpatialIndex.XMax">
            <summary>
            XMax property class of SpatialIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SpatialIndex.XMin">
            <summary>
            XMin property class of SpatialIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SpatialIndex.YMax">
            <summary>
            YMax property class of SpatialIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SpatialIndex.YMin">
            <summary>
            YMin property class of SpatialIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SpatialIndex.Column">
            <summary>
            Column relationship class of SpatialIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SpatialIndex.Filegroup">
            <summary>
            Filegroup relationship class of SpatialIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SpatialIndex.IndexedObject">
            <summary>
            IndexedObject relationship class of SpatialIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SpatialIndex.PartitionColumn">
            <summary>
            PartitionColumn relationship class of SpatialIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SpatialIndex.PartitionScheme">
            <summary>
            PartitionScheme relationship class of SpatialIndex
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.Statistics">
            <summary>
            Model schema container class for Statistics
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Statistics.TypeClass">
            <summary>
            Type class for Statistics
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Statistics.StatsStream">
            <summary>
            StatsStream property class of Statistics
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Statistics.FilterPredicate">
            <summary>
            FilterPredicate property class of Statistics
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Statistics.Incremental">
            <summary>
            Incremental property class of Statistics
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Statistics.NoRecompute">
            <summary>
            NoRecompute property class of Statistics
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Statistics.SampleSize">
            <summary>
            SampleSize property class of Statistics
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Statistics.SamplingStyle">
            <summary>
            SamplingStyle property class of Statistics
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Statistics.OnObject">
            <summary>
            OnObject relationship class of Statistics
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Statistics.Columns">
            <summary>
            Columns relationship class of Statistics
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Statistics.ExpressionDependencies">
            <summary>
            ExpressionDependencies relationship class of Statistics
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.Parameter">
            <summary>
            Model schema container class for Parameter
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Parameter.TypeClass">
            <summary>
            Type class for Parameter
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Parameter.DefaultExpression">
            <summary>
            DefaultExpression property class of Parameter
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Parameter.ReadOnly">
            <summary>
            ReadOnly property class of Parameter
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Parameter.Varying">
            <summary>
            Varying property class of Parameter
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Parameter.Scale">
            <summary>
            Scale property class of Parameter
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Parameter.Precision">
            <summary>
            Precision property class of Parameter
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Parameter.Length">
            <summary>
            Length property class of Parameter
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Parameter.IsMax">
            <summary>
            IsMax property class of Parameter
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Parameter.XmlStyle">
            <summary>
            XmlStyle property class of Parameter
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Parameter.IsNullable">
            <summary>
            IsNullable property class of Parameter
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Parameter.IsOutput">
            <summary>
            IsOutput property class of Parameter
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Parameter.XmlSchemaCollection">
            <summary>
            XmlSchemaCollection relationship class of Parameter
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Parameter.DataType">
            <summary>
            DataType relationship class of Parameter
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.SymmetricKey">
            <summary>
            Model schema container class for SymmetricKey
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SymmetricKey.TypeClass">
            <summary>
            Type class for SymmetricKey
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SymmetricKey.Algorithm">
            <summary>
            Algorithm property class of SymmetricKey
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SymmetricKey.CreationDisposition">
            <summary>
            CreationDisposition property class of SymmetricKey
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SymmetricKey.IdentityValue">
            <summary>
            IdentityValue property class of SymmetricKey
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SymmetricKey.KeySource">
            <summary>
            KeySource property class of SymmetricKey
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SymmetricKey.ProviderKeyName">
            <summary>
            ProviderKeyName property class of SymmetricKey
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SymmetricKey.AsymmetricKeys">
            <summary>
            AsymmetricKeys relationship class of SymmetricKey
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SymmetricKey.Certificates">
            <summary>
            Certificates relationship class of SymmetricKey
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SymmetricKey.Passwords">
            <summary>
            Passwords relationship class of SymmetricKey
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SymmetricKey.SymmetricKeys">
            <summary>
            SymmetricKeys relationship class of SymmetricKey
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SymmetricKey.Authorizer">
            <summary>
            Authorizer relationship class of SymmetricKey
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SymmetricKey.Provider">
            <summary>
            Provider relationship class of SymmetricKey
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.SymmetricKeyPassword">
            <summary>
            Model schema container class for SymmetricKeyPassword
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SymmetricKeyPassword.TypeClass">
            <summary>
            Type class for SymmetricKeyPassword
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SymmetricKeyPassword.Password">
            <summary>
            Password property class of SymmetricKeyPassword
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.Synonym">
            <summary>
            Model schema container class for Synonym
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Synonym.TypeClass">
            <summary>
            Type class for Synonym
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Synonym.ForObjectName">
            <summary>
            ForObjectName property class of Synonym
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Synonym.ForObject">
            <summary>
            ForObject relationship class of Synonym
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Synonym.Schema">
            <summary>
            Schema relationship class of Synonym
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.Table">
            <summary>
            Model schema container class for Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.TypeClass">
            <summary>
            Type class for Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.AnsiNullsOn">
            <summary>
            AnsiNullsOn property class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.ChangeDataCaptureEnabled">
            <summary>
            ChangeDataCaptureEnabled property class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.ChangeTrackingEnabled">
            <summary>
            ChangeTrackingEnabled property class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.FileStreamNull">
            <summary>
            FileStreamNull property class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.LargeValueTypesOutOfRow">
            <summary>
            LargeValueTypesOutOfRow property class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.QuotedIdentifierOn">
            <summary>
            QuotedIdentifierOn property class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.TableLockOnBulkLoad">
            <summary>
            TableLockOnBulkLoad property class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.TrackColumnsUpdated">
            <summary>
            TrackColumnsUpdated property class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.VardecimalStorageFormatEnabled">
            <summary>
            VardecimalStorageFormatEnabled property class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.MemoryOptimized">
            <summary>
            MemoryOptimized property class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.RowCount">
            <summary>
            RowCount property class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.DataSize">
            <summary>
            DataSize property class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.IndexSize">
            <summary>
            IndexSize property class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.DataPages">
            <summary>
            DataPages property class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.UsedPages">
            <summary>
            UsedPages property class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.Durability">
            <summary>
            Durability property class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.IsAutoGeneratedHistoryTable">
            <summary>
            IsAutoGeneratedHistoryTable property class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.IsEdge">
            <summary>
            IsEdge property class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.IsNode">
            <summary>
            IsNode property class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.IsReplicated">
            <summary>
            IsReplicated property class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.LockEscalation">
            <summary>
            LockEscalation property class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.RemoteDataEnabled">
            <summary>
            RemoteDataEnabled property class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.RetentionUnit">
            <summary>
            RetentionUnit property class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.RetentionValue">
            <summary>
            RetentionValue property class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.TextInRowSize">
            <summary>
            TextInRowSize property class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.Columns">
            <summary>
            Columns relationship class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.DataCompressionOptions">
            <summary>
            DataCompressionOptions relationship class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.Filegroup">
            <summary>
            Filegroup relationship class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.FilegroupForTextImage">
            <summary>
            FilegroupForTextImage relationship class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.FileStreamFilegroup">
            <summary>
            FileStreamFilegroup relationship class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.FileStreamPartitionScheme">
            <summary>
            FileStreamPartitionScheme relationship class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.PartitionColumn">
            <summary>
            PartitionColumn relationship class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.PartitionScheme">
            <summary>
            PartitionScheme relationship class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.Schema">
            <summary>
            Schema relationship class of Table
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.Table.TemporalSystemVersioningHistoryTable">
            <summary>
            TemporalSystemVersioningHistoryTable relationship class of Table
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.FileTable">
            <summary>
            Model schema container class for FileTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FileTable.TypeClass">
            <summary>
            Type class for FileTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FileTable.AnsiNullsOn">
            <summary>
            AnsiNullsOn property class of FileTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FileTable.FileStreamNull">
            <summary>
            FileStreamNull property class of FileTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FileTable.QuotedIdentifierOn">
            <summary>
            QuotedIdentifierOn property class of FileTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FileTable.TableLockOnBulkLoad">
            <summary>
            TableLockOnBulkLoad property class of FileTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FileTable.FileTableNamespaceEnabled">
            <summary>
            FileTableNamespaceEnabled property class of FileTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FileTable.FileTableCollateFilename">
            <summary>
            FileTableCollateFilename property class of FileTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FileTable.FileTableDirectory">
            <summary>
            FileTableDirectory property class of FileTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FileTable.LockEscalation">
            <summary>
            LockEscalation property class of FileTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FileTable.Columns">
            <summary>
            Columns relationship class of FileTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FileTable.DataCompressionOptions">
            <summary>
            DataCompressionOptions relationship class of FileTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FileTable.Filegroup">
            <summary>
            Filegroup relationship class of FileTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FileTable.FileStreamFilegroup">
            <summary>
            FileStreamFilegroup relationship class of FileTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FileTable.FileStreamPartitionScheme">
            <summary>
            FileStreamPartitionScheme relationship class of FileTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FileTable.PartitionColumn">
            <summary>
            PartitionColumn relationship class of FileTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FileTable.PartitionScheme">
            <summary>
            PartitionScheme relationship class of FileTable
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.FileTable.Schema">
            <summary>
            Schema relationship class of FileTable
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.TableType">
            <summary>
            Model schema container class for TableType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableType.TypeClass">
            <summary>
            Type class for TableType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableType.MemoryOptimized">
            <summary>
            MemoryOptimized property class of TableType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableType.Columns">
            <summary>
            Columns relationship class of TableType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableType.Constraints">
            <summary>
            Constraints relationship class of TableType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableType.Indexes">
            <summary>
            Indexes relationship class of TableType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableType.Schema">
            <summary>
            Schema relationship class of TableType
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.TableTypeCheckConstraint">
            <summary>
            Model schema container class for TableTypeCheckConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeCheckConstraint.TypeClass">
            <summary>
            Type class for TableTypeCheckConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeCheckConstraint.Expression">
            <summary>
            Expression property class of TableTypeCheckConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeCheckConstraint.ExpressionDependencies">
            <summary>
            ExpressionDependencies relationship class of TableTypeCheckConstraint
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.TableTypeColumn">
            <summary>
            Model schema container class for TableTypeColumn
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeColumn.TypeClass">
            <summary>
            Type class for TableTypeColumn
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeColumn.TableTypeColumnType">
            <summary>
            TableTypeColumnType metadata class of TableTypeColumn
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeColumn.Collation">
            <summary>
            Collation property class of TableTypeColumn
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeColumn.IdentityIncrement">
            <summary>
            IdentityIncrement property class of TableTypeColumn
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeColumn.IdentitySeed">
            <summary>
            IdentitySeed property class of TableTypeColumn
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeColumn.IsIdentity">
            <summary>
            IsIdentity property class of TableTypeColumn
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeColumn.Nullable">
            <summary>
            Nullable property class of TableTypeColumn
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeColumn.IsRowGuidCol">
            <summary>
            IsRowGuidCol property class of TableTypeColumn
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeColumn.Expression">
            <summary>
            Expression property class of TableTypeColumn
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeColumn.Persisted">
            <summary>
            Persisted property class of TableTypeColumn
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeColumn.PersistedNullable">
            <summary>
            PersistedNullable property class of TableTypeColumn
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeColumn.Scale">
            <summary>
            Scale property class of TableTypeColumn
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeColumn.Precision">
            <summary>
            Precision property class of TableTypeColumn
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeColumn.Length">
            <summary>
            Length property class of TableTypeColumn
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeColumn.IsMax">
            <summary>
            IsMax property class of TableTypeColumn
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeColumn.XmlStyle">
            <summary>
            XmlStyle property class of TableTypeColumn
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeColumn.ExpressionDependencies">
            <summary>
            ExpressionDependencies relationship class of TableTypeColumn
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeColumn.XmlSchemaCollection">
            <summary>
            XmlSchemaCollection relationship class of TableTypeColumn
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeColumn.DataType">
            <summary>
            DataType relationship class of TableTypeColumn
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.TableTypeDefaultConstraint">
            <summary>
            Model schema container class for TableTypeDefaultConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeDefaultConstraint.TypeClass">
            <summary>
            Type class for TableTypeDefaultConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeDefaultConstraint.Expression">
            <summary>
            Expression property class of TableTypeDefaultConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeDefaultConstraint.TargetColumn">
            <summary>
            TargetColumn relationship class of TableTypeDefaultConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeDefaultConstraint.ExpressionDependencies">
            <summary>
            ExpressionDependencies relationship class of TableTypeDefaultConstraint
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.TableTypeIndex">
            <summary>
            Model schema container class for TableTypeIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeIndex.TypeClass">
            <summary>
            Type class for TableTypeIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeIndex.Hash">
            <summary>
            Hash property class of TableTypeIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeIndex.BucketCount">
            <summary>
            BucketCount property class of TableTypeIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeIndex.IsDisabled">
            <summary>
            IsDisabled property class of TableTypeIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeIndex.IsUnique">
            <summary>
            IsUnique property class of TableTypeIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeIndex.Columns">
            <summary>
            Columns relationship class of TableTypeIndex
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.TableTypePrimaryKeyConstraint">
            <summary>
            Model schema container class for TableTypePrimaryKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypePrimaryKeyConstraint.TypeClass">
            <summary>
            Type class for TableTypePrimaryKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypePrimaryKeyConstraint.IgnoreDuplicateKey">
            <summary>
            IgnoreDuplicateKey property class of TableTypePrimaryKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypePrimaryKeyConstraint.Clustered">
            <summary>
            Clustered property class of TableTypePrimaryKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypePrimaryKeyConstraint.Hash">
            <summary>
            Hash property class of TableTypePrimaryKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypePrimaryKeyConstraint.BucketCount">
            <summary>
            BucketCount property class of TableTypePrimaryKeyConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypePrimaryKeyConstraint.Columns">
            <summary>
            Columns relationship class of TableTypePrimaryKeyConstraint
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.TableTypePrimaryKeyConstraint.ColumnsRelationship">
            <summary>
            Model schema container class for ColumnsRelationship
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypePrimaryKeyConstraint.ColumnsRelationship.RelationshipClass">
            <summary>
            Relationship class for ColumnsRelationship
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypePrimaryKeyConstraint.ColumnsRelationship.Ascending">
            <summary>
            Ascending property class of ColumnsRelationship
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.TableTypeUniqueConstraint">
            <summary>
            Model schema container class for TableTypeUniqueConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeUniqueConstraint.TypeClass">
            <summary>
            Type class for TableTypeUniqueConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeUniqueConstraint.IgnoreDuplicateKey">
            <summary>
            IgnoreDuplicateKey property class of TableTypeUniqueConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeUniqueConstraint.Clustered">
            <summary>
            Clustered property class of TableTypeUniqueConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeUniqueConstraint.Hash">
            <summary>
            Hash property class of TableTypeUniqueConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeUniqueConstraint.BucketCount">
            <summary>
            BucketCount property class of TableTypeUniqueConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeUniqueConstraint.Columns">
            <summary>
            Columns relationship class of TableTypeUniqueConstraint
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.TableTypeUniqueConstraint.ColumnsRelationship">
            <summary>
            Model schema container class for ColumnsRelationship
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeUniqueConstraint.ColumnsRelationship.RelationshipClass">
            <summary>
            Relationship class for ColumnsRelationship
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TableTypeUniqueConstraint.ColumnsRelationship.Ascending">
            <summary>
            Ascending property class of ColumnsRelationship
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.TcpProtocolSpecifier">
            <summary>
            Model schema container class for TcpProtocolSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TcpProtocolSpecifier.TypeClass">
            <summary>
            Type class for TcpProtocolSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TcpProtocolSpecifier.ListeningOnAllIPs">
            <summary>
            ListeningOnAllIPs property class of TcpProtocolSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TcpProtocolSpecifier.ListenerIPv4">
            <summary>
            ListenerIPv4 property class of TcpProtocolSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TcpProtocolSpecifier.ListenerIPv6">
            <summary>
            ListenerIPv6 property class of TcpProtocolSpecifier
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TcpProtocolSpecifier.ListenerPort">
            <summary>
            ListenerPort property class of TcpProtocolSpecifier
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.UniqueConstraint">
            <summary>
            Model schema container class for UniqueConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UniqueConstraint.TypeClass">
            <summary>
            Type class for UniqueConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UniqueConstraint.AllowPageLocks">
            <summary>
            AllowPageLocks property class of UniqueConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UniqueConstraint.AllowRowLocks">
            <summary>
            AllowRowLocks property class of UniqueConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UniqueConstraint.IgnoreDuplicateKey">
            <summary>
            IgnoreDuplicateKey property class of UniqueConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UniqueConstraint.RecomputeStatistics">
            <summary>
            RecomputeStatistics property class of UniqueConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UniqueConstraint.Clustered">
            <summary>
            Clustered property class of UniqueConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UniqueConstraint.Disabled">
            <summary>
            Disabled property class of UniqueConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UniqueConstraint.FileStreamNull">
            <summary>
            FileStreamNull property class of UniqueConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UniqueConstraint.WithPadIndex">
            <summary>
            WithPadIndex property class of UniqueConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UniqueConstraint.Hash">
            <summary>
            Hash property class of UniqueConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UniqueConstraint.BucketCount">
            <summary>
            BucketCount property class of UniqueConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UniqueConstraint.FillFactor">
            <summary>
            FillFactor property class of UniqueConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UniqueConstraint.Host">
            <summary>
            Host relationship class of UniqueConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UniqueConstraint.Columns">
            <summary>
            Columns relationship class of UniqueConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UniqueConstraint.DataCompressionOptions">
            <summary>
            DataCompressionOptions relationship class of UniqueConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UniqueConstraint.Filegroup">
            <summary>
            Filegroup relationship class of UniqueConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UniqueConstraint.FileStreamFilegroup">
            <summary>
            FileStreamFilegroup relationship class of UniqueConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UniqueConstraint.FileStreamPartitionScheme">
            <summary>
            FileStreamPartitionScheme relationship class of UniqueConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UniqueConstraint.PartitionColumn">
            <summary>
            PartitionColumn relationship class of UniqueConstraint
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UniqueConstraint.PartitionScheme">
            <summary>
            PartitionScheme relationship class of UniqueConstraint
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.UniqueConstraint.ColumnsRelationship">
            <summary>
            Model schema container class for ColumnsRelationship
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UniqueConstraint.ColumnsRelationship.RelationshipClass">
            <summary>
            Relationship class for ColumnsRelationship
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UniqueConstraint.ColumnsRelationship.Ascending">
            <summary>
            Ascending property class of ColumnsRelationship
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.User">
            <summary>
            Model schema container class for User
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.User.TypeClass">
            <summary>
            Type class for User
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.User.WithoutLogin">
            <summary>
            WithoutLogin property class of User
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.User.AuthenticationType">
            <summary>
            AuthenticationType property class of User
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.User.DefaultLanguage">
            <summary>
            DefaultLanguage property class of User
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.User.Password">
            <summary>
            Password property class of User
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.User.Sid">
            <summary>
            Sid property class of User
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.User.UserType">
            <summary>
            UserType property class of User
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.User.AsymmetricKey">
            <summary>
            AsymmetricKey relationship class of User
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.User.Certificate">
            <summary>
            Certificate relationship class of User
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.User.DefaultSchema">
            <summary>
            DefaultSchema relationship class of User
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.User.Login">
            <summary>
            Login relationship class of User
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.UserDefinedServerRole">
            <summary>
            Model schema container class for UserDefinedServerRole
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UserDefinedServerRole.TypeClass">
            <summary>
            Type class for UserDefinedServerRole
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UserDefinedServerRole.Authorizer">
            <summary>
            Authorizer relationship class of UserDefinedServerRole
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.UserDefinedType">
            <summary>
            Model schema container class for UserDefinedType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UserDefinedType.TypeClass">
            <summary>
            Type class for UserDefinedType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UserDefinedType.ByteOrdered">
            <summary>
            ByteOrdered property class of UserDefinedType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UserDefinedType.FixedLength">
            <summary>
            FixedLength property class of UserDefinedType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UserDefinedType.ClassName">
            <summary>
            ClassName property class of UserDefinedType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UserDefinedType.Format">
            <summary>
            Format property class of UserDefinedType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UserDefinedType.MaxByteSize">
            <summary>
            MaxByteSize property class of UserDefinedType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UserDefinedType.ValidationMethodName">
            <summary>
            ValidationMethodName property class of UserDefinedType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UserDefinedType.Assembly">
            <summary>
            Assembly relationship class of UserDefinedType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UserDefinedType.Methods">
            <summary>
            Methods relationship class of UserDefinedType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UserDefinedType.Properties">
            <summary>
            Properties relationship class of UserDefinedType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.UserDefinedType.Schema">
            <summary>
            Schema relationship class of UserDefinedType
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.View">
            <summary>
            Model schema container class for View
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.View.TypeClass">
            <summary>
            Type class for View
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.View.AnsiNullsOn">
            <summary>
            AnsiNullsOn property class of View
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.View.WithEncryption">
            <summary>
            WithEncryption property class of View
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.View.WithViewMetadata">
            <summary>
            WithViewMetadata property class of View
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.View.QuotedIdentifierOn">
            <summary>
            QuotedIdentifierOn property class of View
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.View.Replicated">
            <summary>
            Replicated property class of View
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.View.WithSchemaBinding">
            <summary>
            WithSchemaBinding property class of View
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.View.WithCheckOption">
            <summary>
            WithCheckOption property class of View
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.View.SelectStatement">
            <summary>
            SelectStatement property class of View
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.View.BodyDependencies">
            <summary>
            BodyDependencies relationship class of View
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.View.Columns">
            <summary>
            Columns relationship class of View
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.View.Schema">
            <summary>
            Schema relationship class of View
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.WorkloadGroup">
            <summary>
            Model schema container class for WorkloadGroup
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.WorkloadGroup.TypeClass">
            <summary>
            Type class for WorkloadGroup
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.WorkloadGroup.GroupMaxRequests">
            <summary>
            GroupMaxRequests property class of WorkloadGroup
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.WorkloadGroup.Importance">
            <summary>
            Importance property class of WorkloadGroup
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.WorkloadGroup.MaxDop">
            <summary>
            MaxDop property class of WorkloadGroup
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.WorkloadGroup.RequestMaxCpuTimeSec">
            <summary>
            RequestMaxCpuTimeSec property class of WorkloadGroup
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.WorkloadGroup.RequestMaxMemoryGrantPercent">
            <summary>
            RequestMaxMemoryGrantPercent property class of WorkloadGroup
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.WorkloadGroup.RequestMemoryGrantTimeoutSec">
            <summary>
            RequestMemoryGrantTimeoutSec property class of WorkloadGroup
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.WorkloadGroup.ResourcePool">
            <summary>
            ResourcePool relationship class of WorkloadGroup
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.XmlIndex">
            <summary>
            Model schema container class for XmlIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.XmlIndex.TypeClass">
            <summary>
            Type class for XmlIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.XmlIndex.AllowPageLocks">
            <summary>
            AllowPageLocks property class of XmlIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.XmlIndex.AllowRowLocks">
            <summary>
            AllowRowLocks property class of XmlIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.XmlIndex.IgnoreDuplicateKey">
            <summary>
            IgnoreDuplicateKey property class of XmlIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.XmlIndex.RecomputeStatistics">
            <summary>
            RecomputeStatistics property class of XmlIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.XmlIndex.Disabled">
            <summary>
            Disabled property class of XmlIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.XmlIndex.WithPadIndex">
            <summary>
            WithPadIndex property class of XmlIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.XmlIndex.SecondaryXmlIndexType">
            <summary>
            SecondaryXmlIndexType property class of XmlIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.XmlIndex.FillFactor">
            <summary>
            FillFactor property class of XmlIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.XmlIndex.IsPrimary">
            <summary>
            IsPrimary property class of XmlIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.XmlIndex.PrimaryXmlIndex">
            <summary>
            PrimaryXmlIndex relationship class of XmlIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.XmlIndex.Column">
            <summary>
            Column relationship class of XmlIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.XmlIndex.IndexedObject">
            <summary>
            IndexedObject relationship class of XmlIndex
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.SelectiveXmlIndex">
            <summary>
            Model schema container class for SelectiveXmlIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SelectiveXmlIndex.TypeClass">
            <summary>
            Type class for SelectiveXmlIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SelectiveXmlIndex.Disabled">
            <summary>
            Disabled property class of SelectiveXmlIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SelectiveXmlIndex.WithPadIndex">
            <summary>
            WithPadIndex property class of SelectiveXmlIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SelectiveXmlIndex.AllowRowLocks">
            <summary>
            AllowRowLocks property class of SelectiveXmlIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SelectiveXmlIndex.AllowPageLocks">
            <summary>
            AllowPageLocks property class of SelectiveXmlIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SelectiveXmlIndex.IgnoreDuplicateKey">
            <summary>
            IgnoreDuplicateKey property class of SelectiveXmlIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SelectiveXmlIndex.RecomputeStatistics">
            <summary>
            RecomputeStatistics property class of SelectiveXmlIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SelectiveXmlIndex.FillFactor">
            <summary>
            FillFactor property class of SelectiveXmlIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SelectiveXmlIndex.IsPrimary">
            <summary>
            IsPrimary property class of SelectiveXmlIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SelectiveXmlIndex.PrimarySelectiveXmlIndex">
            <summary>
            PrimarySelectiveXmlIndex relationship class of SelectiveXmlIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SelectiveXmlIndex.PrimaryPromotedPath">
            <summary>
            PrimaryPromotedPath relationship class of SelectiveXmlIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SelectiveXmlIndex.Column">
            <summary>
            Column relationship class of SelectiveXmlIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SelectiveXmlIndex.IndexedObject">
            <summary>
            IndexedObject relationship class of SelectiveXmlIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SelectiveXmlIndex.PromotedPaths">
            <summary>
            PromotedPaths relationship class of SelectiveXmlIndex
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.SelectiveXmlIndex.XmlNamespaces">
            <summary>
            XmlNamespaces relationship class of SelectiveXmlIndex
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.XmlNamespace">
            <summary>
            Model schema container class for XmlNamespace
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.XmlNamespace.TypeClass">
            <summary>
            Type class for XmlNamespace
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.XmlNamespace.NamespaceUri">
            <summary>
            NamespaceUri property class of XmlNamespace
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.XmlNamespace.Prefix">
            <summary>
            Prefix property class of XmlNamespace
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.PromotedNodePathForXQueryType">
            <summary>
            Model schema container class for PromotedNodePathForXQueryType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PromotedNodePathForXQueryType.TypeClass">
            <summary>
            Type class for PromotedNodePathForXQueryType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PromotedNodePathForXQueryType.IsSingleton">
            <summary>
            IsSingleton property class of PromotedNodePathForXQueryType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PromotedNodePathForXQueryType.MaxLength">
            <summary>
            MaxLength property class of PromotedNodePathForXQueryType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PromotedNodePathForXQueryType.NodePath">
            <summary>
            NodePath property class of PromotedNodePathForXQueryType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PromotedNodePathForXQueryType.Type">
            <summary>
            Type property class of PromotedNodePathForXQueryType
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.PromotedNodePathForSqlType">
            <summary>
            Model schema container class for PromotedNodePathForSqlType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PromotedNodePathForSqlType.TypeClass">
            <summary>
            Type class for PromotedNodePathForSqlType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PromotedNodePathForSqlType.Scale">
            <summary>
            Scale property class of PromotedNodePathForSqlType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PromotedNodePathForSqlType.Precision">
            <summary>
            Precision property class of PromotedNodePathForSqlType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PromotedNodePathForSqlType.Length">
            <summary>
            Length property class of PromotedNodePathForSqlType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PromotedNodePathForSqlType.IsMax">
            <summary>
            IsMax property class of PromotedNodePathForSqlType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PromotedNodePathForSqlType.IsSingleton">
            <summary>
            IsSingleton property class of PromotedNodePathForSqlType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PromotedNodePathForSqlType.NodePath">
            <summary>
            NodePath property class of PromotedNodePathForSqlType
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.PromotedNodePathForSqlType.DataType">
            <summary>
            DataType relationship class of PromotedNodePathForSqlType
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.XmlSchemaCollection">
            <summary>
            Model schema container class for XmlSchemaCollection
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.XmlSchemaCollection.TypeClass">
            <summary>
            Type class for XmlSchemaCollection
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.XmlSchemaCollection.Expression">
            <summary>
            Expression property class of XmlSchemaCollection
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.XmlSchemaCollection.Schema">
            <summary>
            Schema relationship class of XmlSchemaCollection
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ModelTypeClass">
            <summary>
             The metadata class for <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>s.
            </summary>
            <remarks>
            <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModelSchema"/> metadata classes provide access to the structure and instance data
            within a <c>TSqlModelSchema</c> instance.
            </remarks>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelTypeClass.Name">
            <summary>
            Type name.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelTypeClass.SupportedPlatforms">
            <summary>
            The <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlPlatforms"/> versions on which this type is supported.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelTypeClass.Properties">
            <summary>
            Properties for the type.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelTypeClass.Relationships">
            <summary>
            Relationships for the type.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ModelTypeClass.Metadata">
            <summary>
            Metadata properties for the type.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ObjectIdentifier">
             <summary>
            
             </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ObjectIdentifier.#ctor(System.String[])">
            <summary>
            Creates a object identifier with a name.
            </summary>
            <param name="parts">The parts of the identifier</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ObjectIdentifier.#ctor(System.Collections.Generic.IList{System.String})">
            <summary>
            Creates a object identifier with a name.
            </summary>
            <param name="parts">The parts of the identifier</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ObjectIdentifier.#ctor(System.Collections.Generic.IEnumerable{System.String})">
            <summary>
            Creates a object identifier with a name.
            </summary>
            <param name="parts">The parts of the identifier</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ObjectIdentifier.#ctor(System.Collections.Generic.IList{System.String},System.Collections.Generic.IList{System.String})">
            <summary>
            Creates a object identifier with a name that includes external parts.
            </summary>
            <param name="externalParts">External reference parts - used in 3 or 4 part references to
            objects in other databases or servers </param>
            <param name="parts">The parts of the identifier</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.ObjectIdentifier.ToString">
            <summary>
             Formats and escapes name parts into a single string
            </summary>
            <returns>escaped name.</returns>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ObjectIdentifier.Parts">
            <summary>
            Gets the parts of the identifier.
            </summary>
            <returns>The parts of the identifier.</returns>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ObjectIdentifier.HasName">
            <summary>
            Does this ObjectIdentifier have any <see cref="P:Microsoft.SqlServer.Dac.Model.ObjectIdentifier.Parts"/>
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ObjectIdentifier.ExternalParts">
            <summary>
            Gets the external reference parts.  Null if the identifier is not external.
            </summary>
            <returns>The external reference parts.</returns>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.ObjectIdentifier.HasExternalParts">
            <summary>
            Does this ObjectIdentifier have any <see cref="P:Microsoft.SqlServer.Dac.Model.ObjectIdentifier.ExternalParts"/>
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.RelationshipType">
            <summary>
            Relationship types.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.RelationshipType.Composing">
            <summary>
            A relationship where the child object cannot be created outside the parent's DDL create statement.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.RelationshipType.Hierarchical">
            <summary>
            A relationship where the child object can be created outside the parent's DDL create statement.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.RelationshipType.Peer">
            <summary>
            A relationship where the related object must be created outside the parent's DDL create statement.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.ScriptPropertyContext">
            <summary>
            Customized Context object for properties of type <see cref="T:Microsoft.Data.Tools.Schema.Sql.SchemaModel.SqlScriptProperty"/>.    
            </summary>
            <remarks>
            Returns the value of the script string.
            </remarks>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.SimpleModelPropertyContext">
            <summary>
            Default Property context for all properties.
            </summary>
            <remarks>
            Provides default implementation for property access through the public model.
            </remarks>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.SqlSchemaModelCreator.GetCollationToUse(System.String,Microsoft.Data.Tools.Schema.SchemaModel.SqlPlatforms)">
            <summary>
            Internal for test use only
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.SqlServerVersion">
            <summary>
            Specific SQL Server releases.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlServerVersion.Sql90">
             <summary>
            on-premises, version 9.0
             </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlServerVersion.Sql100">
            <summary>
            on-premises, version 10.0
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlServerVersion.SqlAzure">
            <summary>
            Azure
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlServerVersion.Sql110">
            <summary>
            on-premises, version 11.0
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlServerVersion.Sql120">
            <summary>
            on-premises, version 12.0
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlServerVersion.Sql130">
            <summary>
            on-premises, version 13.0
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.SqlServerVersion.Sql140">
            <summary>
            on-premises, version 14.0
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.TSqlModel">
            <summary>
            Represents a model of a SQL Server database schema.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlModel.LoadFromDacpac(System.IO.Stream,Microsoft.SqlServer.Dac.Model.ModelLoadOptions)">
            <summary>
            Loads a model from a <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> or <see cref="T:Microsoft.SqlServer.Dac.BacPackage"/>
            using the specified <paramref name="options"/> to configure what kind of storage to use, and whether to load a script-backed
            model or not.
            </summary>
            <param name="dacpacStream">A stream that contains the serialized dacpac.</param>
            <param name="options">
            The <see cref="T:Microsoft.SqlServer.Dac.Model.ModelLoadOptions"/> defining how the model should be loaded. For models to be analyzed in static code analysis,
            it's recommended that the <see cref="P:Microsoft.SqlServer.Dac.Model.ModelLoadOptions.LoadAsScriptBackedModel"/> be set to true. This is important since some
            rules may not work as expected unless this is true.
            </param>
            <exception cref="T:Microsoft.SqlServer.Dac.Model.DacModelException">
            If there are problems loading the package from the specified <paramref name="dacpacStream"/>. 
            This may occur if the package does not load correctly or if references required by
            the package are not found using the original path to the reference.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlModel.LoadFromDacpac(System.IO.Stream,Microsoft.SqlServer.Dac.Model.ModelLoadOptions,System.Threading.CancellationToken)">
            <summary>
            Loads a model from a <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> or <see cref="T:Microsoft.SqlServer.Dac.BacPackage"/>
            using the specified <paramref name="options"/> to configure what kind of storage to use, and whether to load a script-backed
            model or not.
            </summary>
            <param name="dacpacStream">A stream that contains the serialized dacpac.</param>
            <param name="options">
            The <see cref="T:Microsoft.SqlServer.Dac.Model.ModelLoadOptions"/> defining how the model should be loaded. For models to be analyzed in static code analysis,
            it's recommended that the <see cref="P:Microsoft.SqlServer.Dac.Model.ModelLoadOptions.LoadAsScriptBackedModel"/> be set to true. This is important since some
            rules may not work as expected unless this is true.
            </param>
            <param name="cancellationToken">A token used for cancelling model loading.</param>
            <exception cref="T:System.OperationCanceledException">When the load operation is canceled.</exception>
            <exception cref="T:Microsoft.SqlServer.Dac.Model.DacModelException">
            If there are problems loading the package from the specified <paramref name="dacpacStream"/>. 
            This may occur if the package does not load correctly or if references required by
            the package are not found using the original path to the reference.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlModel.LoadFromDacpac(System.String,Microsoft.SqlServer.Dac.Model.ModelLoadOptions,System.Threading.CancellationToken)">
            <summary>
            Loads a model from a <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> or <see cref="T:Microsoft.SqlServer.Dac.BacPackage"/>
            using the specified <paramref name="options"/> to configure what kind of storage to use, and whether to load a script-backed
            model or not.
            </summary>
            <param name="fileName">File path to package.</param> 
            <param name="options">
            The <see cref="T:Microsoft.SqlServer.Dac.Model.ModelLoadOptions"/> defining how the model should be loaded. For models to be analyzed in static code analysis,
            it's recommended that the <see cref="P:Microsoft.SqlServer.Dac.Model.ModelLoadOptions.LoadAsScriptBackedModel"/> be set to true. This is important since some
            rules may not work as expected unless this is true.
            </param>
            <param name="cancellationToken">A token used for cancelling model loading.</param>
            <exception cref="T:System.OperationCanceledException">When the load operation is canceled.</exception>
            <exception cref="T:Microsoft.SqlServer.Dac.Model.DacModelException">
            If there are problems loading the package from the specified <paramref name="fileName"/>. 
            This may occur if the package does not load correctly or if references required by
            the package are not found in either the same directory as the package, or using the original path
            to the reference.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlModel.LoadFromDacpac(System.String,Microsoft.SqlServer.Dac.Model.ModelLoadOptions)">
            <summary>
            Loads a model from a <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> or <see cref="T:Microsoft.SqlServer.Dac.BacPackage"/>
            using the specified <paramref name="options"/> to configure what kind of storage to use, and whether to load a script-backed
            model or not.
            </summary>
            <param name="fileName">File path to package.</param> 
            <param name="options">
            The <see cref="T:Microsoft.SqlServer.Dac.Model.ModelLoadOptions"/> defining how the model should be loaded. For models to be analyzed in static code analysis,
            it's recommended that the <see cref="P:Microsoft.SqlServer.Dac.Model.ModelLoadOptions.LoadAsScriptBackedModel"/> be set to true. This is important since some
            rules may not work as expected unless this is true.
            </param>
            <exception cref="T:Microsoft.SqlServer.Dac.Model.DacModelException">
            If there are problems loading the package from the specified <paramref name="fileName"/>. 
            This may occur if the package does not load correctly or if references required by
            the package are not found in either the same directory as the package, or using the original path
            to the reference.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlModel.LoadFromDatabase(System.String,Microsoft.SqlServer.Dac.Model.ModelExtractOptions,System.Nullable{System.Threading.CancellationToken})">
            <summary>
            Loads a model from a database using the specified <paramref name="options"/>.
            </summary>
            <param name="connectionString">A connection string specifying the database and catalog to load.</param>
            <param name="options">
            Optional <see cref="T:Microsoft.SqlServer.Dac.Model.ModelExtractOptions"/> instance used to specify options that affect the behavior of package extraction.
            For models to be analyzed in static code analysis, it's recommended that the 
            <see cref="P:Microsoft.SqlServer.Dac.Model.ModelExtractOptions.LoadAsScriptBackedModel"/> be set to true. This is important since some
            rules may not work as expected unless this is true.</param> 
            <param name="cancellationToken">Optional cancellation token used to cancel a load operation.</param>
            <exception cref="T:System.ArgumentNullException">If connectionString is null.</exception>
            <exception cref="T:System.ArgumentException">If connectionString is not a valid connection string
            or options.Storage is not a valid value of the enumerated type.</exception>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If there are problems extracting the model from the specified database. 
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlModel.LoadFromDatabaseWithAuthProvider(System.String,Microsoft.SqlServer.Dac.IUniversalAuthProvider,Microsoft.SqlServer.Dac.Model.ModelExtractOptions,System.Nullable{System.Threading.CancellationToken})">
            <summary>
            Loads a model from a database using the specified <paramref name="options"/>.
            </summary>
            <param name="connectionString">A connection string specifying the database and catalog to load.</param>
            <param name="authProvider"> 
            Optional universal authentication provider used to authenticate a <see cref="T:System.Data.SqlClient.SqlConnection"/> when opening a connection.</param>
            <param name="options">
            Optional <see cref="T:Microsoft.SqlServer.Dac.Model.ModelExtractOptions"/> instance used to specify options that affect the behavior of package extraction.
            For models to be analyzed in static code analysis, it's recommended that the 
            <see cref="P:Microsoft.SqlServer.Dac.Model.ModelExtractOptions.LoadAsScriptBackedModel"/> be set to true. This is important since some
            rules may not work as expected unless this is true.</param> 
            <param name="cancellationToken">Optional cancellation token used to cancel a load operation.</param>
            <exception cref="T:System.ArgumentNullException">If connectionString is null.</exception>
            <exception cref="T:System.ArgumentException">If connectionString is not a valid connection string
            or options.Storage is not a valid value of the enumerated type.</exception>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If there are problems extracting the model from the specified database. 
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlModel.LoadFromDatabase(System.String,System.Security.SecureString,Microsoft.SqlServer.Dac.Model.ModelExtractOptions,System.Nullable{System.Threading.CancellationToken})">
            <summary>
            Loads a model from a database using the specified <paramref name="options"/>.
            </summary>
            <param name="connectionString">A connection string specifying the database and catalog to load.</param>
            <param name="password">This password string used when connecting to the specified database.</param>
            <param name="options">
            Optional <see cref="T:Microsoft.SqlServer.Dac.Model.ModelExtractOptions"/> instance used to specify options that affect the behavior of package extraction.
            For models to be analyzed in static code analysis, it's recommended that the 
            <see cref="P:Microsoft.SqlServer.Dac.Model.ModelExtractOptions.LoadAsScriptBackedModel"/> be set to true. This is important since some
            rules may not work as expected unless this is true.</param> 
            <param name="cancellationToken">Optional cancellation token used to cancel a load operation.</param>
            <exception cref="T:System.ArgumentNullException">If connectionString is null.</exception>
            <exception cref="T:System.ArgumentException">If connectionString is not a valid connection string
            or options.Storage is not a valid value of the enumerated type.</exception>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If there are problems extracting the model from the specified database. 
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlModel.#ctor(System.String)">
            <summary>
            Loads a model from a <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> or <see cref="T:Microsoft.SqlServer.Dac.BacPackage"/>.
            
            This is equivalent to calling <see cref="M:Microsoft.SqlServer.Dac.Model.TSqlModel.LoadFromDacpac(System.String,Microsoft.SqlServer.Dac.Model.ModelLoadOptions)"/> with default values.
            </summary>
            <param name="fileName">File path to package.</param> 
            <remarks>
            By default DacSchemaModelStorageType.Memory is used for the model storage format.
            The dacpac model will not be script-backed as the <see cref="P:Microsoft.SqlServer.Dac.Model.ModelLoadOptions.LoadAsScriptBackedModel"/> will be set to false.
            Using <see cref="M:Microsoft.SqlServer.Dac.Model.TSqlModel.LoadFromDacpac(System.String,Microsoft.SqlServer.Dac.Model.ModelLoadOptions)"/> with <see cref="P:Microsoft.SqlServer.Dac.Model.ModelLoadOptions.LoadAsScriptBackedModel"/>
            set to true is recommended if the model will be used by the <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService"/> for static code analysis.
            </remarks>
            <exception cref="T:Microsoft.SqlServer.Dac.Model.DacModelException">
            If there are problems loading the package from the specified <paramref name="fileName"/>. 
            This may occur if the package does not load correctly or if references required by
            the package are not found in either the same directory as the package, or using the original path
            to the reference.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlModel.#ctor(System.String,Microsoft.SqlServer.Dac.DacSchemaModelStorageType)">
            <summary>
            Loads a model from a <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> or <see cref="T:Microsoft.SqlServer.Dac.BacPackage"/>.
            
            This is equivalent to calling <see cref="M:Microsoft.SqlServer.Dac.Model.TSqlModel.LoadFromDacpac(System.String,Microsoft.SqlServer.Dac.Model.ModelLoadOptions)"/> with default values for
            everything except the <paramref name="modelStorageType"/>.
            </summary>
            <param name="fileName">File path to package.</param>
            <param name="modelStorageType">
            Backing storage type for package instance.
            </param>
            <remarks>
            The dacpac model will not be script-backed as the <see cref="P:Microsoft.SqlServer.Dac.Model.ModelLoadOptions.LoadAsScriptBackedModel"/> will be set to false.
            Using <see cref="M:Microsoft.SqlServer.Dac.Model.TSqlModel.LoadFromDacpac(System.String,Microsoft.SqlServer.Dac.Model.ModelLoadOptions)"/> with <see cref="P:Microsoft.SqlServer.Dac.Model.ModelLoadOptions.LoadAsScriptBackedModel"/>
            set to true is recommended if the model will be used by the <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService"/> for static code analysis.
            </remarks>
            <exception cref="T:Microsoft.SqlServer.Dac.Model.DacModelException">
            If there are problems loading the package from the specified <paramref name="fileName"/>. 
            This may occur if the package does not load correctly or if references required by
            the package are not found in either the same directory as the package, or using the original path
            to the reference.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlModel.#ctor(System.String,Microsoft.SqlServer.Dac.DacSchemaModelStorageType,System.Threading.CancellationToken)">
            <summary>
            Loads a model from a <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> or <see cref="T:Microsoft.SqlServer.Dac.BacPackage"/>.
            
            This is equivalent to calling <see cref="M:Microsoft.SqlServer.Dac.Model.TSqlModel.LoadFromDacpac(System.String,Microsoft.SqlServer.Dac.Model.ModelLoadOptions)"/> with default values for
            everything except the <paramref name="modelStorageType"/>.
            </summary>
            <param name="fileName">File path to package.</param>
            <param name="modelStorageType">
            Backing storage type for package instance.
            </param>
            <param name="cancellationToken">A token used for cancelling model loading.</param>
            <remarks>
            The dacpac model will not be script-backed as the <see cref="P:Microsoft.SqlServer.Dac.Model.ModelLoadOptions.LoadAsScriptBackedModel"/> will be set to false.
            Using <see cref="M:Microsoft.SqlServer.Dac.Model.TSqlModel.LoadFromDacpac(System.String,Microsoft.SqlServer.Dac.Model.ModelLoadOptions)"/> with <see cref="P:Microsoft.SqlServer.Dac.Model.ModelLoadOptions.LoadAsScriptBackedModel"/>
            set to true is recommended if the model will be used by the <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService"/> for static code analysis.
            </remarks>
            <exception cref="T:Microsoft.SqlServer.Dac.Model.DacModelException">
            If there are problems loading the package from the specified <paramref name="fileName"/>. 
            This may occur if the package does not load correctly or if references required by
            the package are not found in either the same directory as the package, or using the original path
            to the reference.
            </exception>
            <exception cref="T:System.OperationCanceledException">When the load operation is canceled.</exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlModel.#ctor(Microsoft.SqlServer.Dac.Model.SqlServerVersion,Microsoft.SqlServer.Dac.Model.TSqlModelOptions)">
            <summary>
            Creates an empty model that targets a specific <see cref="T:Microsoft.SqlServer.Dac.Model.SqlServerVersion"/>
            </summary>
            <param name="modelTargetVersion">The version of SQL Server to target</param>
            <param name="modelCreationOptions">Options defining model properties, 
            including the collation and how to store the model (in memory or file backed)
            </param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlModel.GetObjectService">
            <summary>
            Internal method - for testing and utility purposes only
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlModel.GetObject(Microsoft.SqlServer.Dac.Model.ModelTypeClass,Microsoft.SqlServer.Dac.Model.ObjectIdentifier,Microsoft.SqlServer.Dac.Model.DacQueryScopes)">
            <summary>
            Returns <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>s  of the specified <paramref name="objectType" value="type"/> and <paramref name="id" value="identity"/>.
            Only top level <see cref="T:Microsoft.SqlServer.Dac.Model.ModelTypeClass"/> types are supported by this method - the <paramref name="objectType" value="type"/> parameter
            must be in the types returned from the <see cref="P:Microsoft.SqlServer.Dac.Model.TSqlModelSchema.TopLevelTypes"/> method or the call will fail.
            </summary>
            <param name="objectType">Type filter. Must be in the list of <see cref="T:Microsoft.SqlServer.Dac.Model.ModelTypeClass"/> types returned from
            the <see cref="P:Microsoft.SqlServer.Dac.Model.TSqlModelSchema.TopLevelTypes"/> method or the call will fail. </param>
            <param name="id">Unique identity of <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> to return.</param>
            <param name="queryScopes">Scope filter for queried objects.</param>
            <returns>Single TSqlObject or null.</returns>
            <exception cref="T:Microsoft.SqlServer.Dac.Model.DacModelException">
            If the supplied <paramref name="objectType"/> and <paramref name="id"/> result in multiple <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>s,
            or if the <paramref name="objectType"/> is not a top level type.
            </exception>
            <exception cref="T:System.ArgumentNullException">
            If the supplied <paramref name="id"/> is null.
            </exception>
            <exception cref="T:System.ArgumentNullException">
            If the supplied <paramref name="objectType"/> is null.
            </exception>
            <exception cref="T:System.Runtime.Remoting.RemotingException">
            If communication with the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObjectService"/> fails.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlModel.GetObjects(Microsoft.SqlServer.Dac.Model.DacQueryScopes,Microsoft.SqlServer.Dac.Model.ModelTypeClass[])">
            <summary>
            Returns all <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>s matching the list of <paramref name="typeFilters"/>.
            Only top level <see cref="T:Microsoft.SqlServer.Dac.Model.ModelTypeClass"/> types are supported by this method - all types in the
            <paramref name="typeFilters" value="type"/> parameter
            must be present in the <see cref="P:Microsoft.SqlServer.Dac.Model.TSqlModelSchema.TopLevelTypes"/> or the call will fail.
            </summary>
            <param name="queryScopes">Scope filter for queried objects. Must be in the list of <see cref="T:Microsoft.SqlServer.Dac.Model.ModelTypeClass"/> types returned from
            the <see cref="P:Microsoft.SqlServer.Dac.Model.TSqlModelSchema.TopLevelTypes"/> method or the call will fail.</param>
            <param name="typeFilters">List of <see cref="T:Microsoft.SqlServer.Dac.Model.ModelTypeClass"/>s to return.</param>
            <returns>All <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>s that match <paramref name="typeFilters"/>.</returns>
            <exception cref="T:Microsoft.SqlServer.Dac.Model.DacModelException">
            If any of the types in the <paramref name="typeFilters"/> parameter are not a top level type.
            </exception>
            <remarks>
            If no <paramref name="typeFilters"/> are supplied all <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>s are returned.
            </remarks>
            <exception cref="T:System.Runtime.Remoting.RemotingException">
            If communication with the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObjectService"/> fails.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlModel.GetObjects(Microsoft.SqlServer.Dac.Model.ModelTypeClass,Microsoft.SqlServer.Dac.Model.ObjectIdentifier,Microsoft.SqlServer.Dac.Model.DacQueryScopes)">
            <summary>
            Returns all <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>s that match the <see cref="T:Microsoft.SqlServer.Dac.Model.ModelTypeClass"/> and <see cref="T:Microsoft.SqlServer.Dac.Model.ObjectIdentifier"/>.
            Only top level <see cref="T:Microsoft.SqlServer.Dac.Model.ModelTypeClass"/> types are supported by this method - the <paramref name="objectType" value="type"/> parameter
            must be in the types returned from the <see cref="P:Microsoft.SqlServer.Dac.Model.TSqlModelSchema.TopLevelTypes"/> method or the call will fail.
            </summary>
            <param name="objectType">Type Filter. Must be in the list of <see cref="T:Microsoft.SqlServer.Dac.Model.ModelTypeClass"/> types returned from
            the <see cref="P:Microsoft.SqlServer.Dac.Model.TSqlModelSchema.TopLevelTypes"/> method or the call will fail.</param>
            <param name="id">Identity of the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>s to return.</param>
            <param name="queryScopes">Scope filter for queried objects.</param>
            <returns>All <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>s that match the <paramref name="objectType"/> and <paramref name="id"/>.</returns>
            <exception cref="T:Microsoft.SqlServer.Dac.Model.DacModelException">
            If the <paramref name="objectType"/> is not a top level type.
            </exception>
            <exception cref="T:System.ArgumentNullException">
            If the supplied <paramref name="id"/> is null.
            </exception>
            <exception cref="T:System.ArgumentNullException">
            If the supplied <paramref name="objectType"/> is null.
            </exception>
            <exception cref="T:System.Runtime.Remoting.RemotingException">
            If communication with the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObjectService"/> fails.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlModel.GetObjects(System.String,Microsoft.SqlServer.Dac.Model.ModelTypeClass[])">
            <summary>
            Returns all <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>s in a given <paramref name="sourceName"/> document filtered by <see cref="T:Microsoft.SqlServer.Dac.Model.ModelTypeClass"/>s
            specified by the <paramref name="typeFilters"/> parameter.
            </summary>
            <param name="sourceName">Name of the source document</param>
            <param name="typeFilters">Array of ModelTypeClass that the resulting <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> should be filtered on.</param>
            <returns>List of <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>s</returns>
            <remarks>
            If no <paramref name="typeFilters"/> are supplied all <see cref="T:Microsoft.SqlServer.Dac.Model.ModelTypeClass"/>s are returned.
            </remarks>
            <exception cref="T:System.Runtime.Remoting.RemotingException">
            If communication with the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObjectService"/> fails.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlModel.GetModelErrors">
            <summary>
            Returns all <see cref="T:Microsoft.SqlServer.Dac.Model.DacModelError"/>s that are present in the model.
            </summary>
            <remarks>
            Load the model with <see cref="P:Microsoft.SqlServer.Dac.Model.ModelLoadOptions.ThrowOnModelErrors"/> set to false to retrieve model errors.
            Refer <see cref="P:Microsoft.SqlServer.Dac.Model.ModelLoadOptions.ThrowOnModelErrors"/> for more information on model errors.
            </remarks>
            <exception cref="T:System.Runtime.Remoting.RemotingException">
            If communication with the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObjectService"/> fails.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlModel.AddObjects(System.String)">
            <summary>
            Adds objects to the model based on the contents of a TSql Script string. 
            The script should consist of valid TSql DDL statements.
            
            Objects added using this method cannot be updated or deleted at a later point 
            as update/delete requires a script name to be specified when adding the objects. If this
            is a requirement use the <see cref="M:Microsoft.SqlServer.Dac.Model.TSqlModel.AddOrUpdateObjects(System.String,System.String,Microsoft.SqlServer.Dac.Model.TSqlObjectOptions)"/> 
            method instead.
            </summary>
            <param name="inputScript">Script containing TSql DDL statements</param>
            <exception cref="T:System.ArgumentNullException">
            If the supplied <paramref name="inputScript"/> is null.
            </exception>
            <exception cref="T:System.Runtime.Remoting.RemotingException">
            If communication with the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObjectService"/> fails.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlModel.AddObjects(System.String,Microsoft.SqlServer.Dac.Model.TSqlObjectOptions)">
            <summary>
            Add Objects to the model based on the contents of a TSql Script string, 
            plus additional metadata defined by a <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObjectOptions"/> object
            The script should consist of valid TSql DDL statements. 
            
            Objects added using this method cannot be updated or deleted at a later point 
            as update/delete requires a script name to be specified when adding the objects. If this
            is a requirement use the <see cref="M:Microsoft.SqlServer.Dac.Model.TSqlModel.AddOrUpdateObjects(System.String,System.String,Microsoft.SqlServer.Dac.Model.TSqlObjectOptions)"/> 
            method instead.
            </summary>
            <param name="inputScript">Script containing TSql DDL statements</param>
            <param name="options">Options defining how to interpret the script</param>
            <exception cref="T:System.ArgumentNullException">
            If the supplied <paramref name="inputScript"/> is null.
            </exception>
            <exception cref="T:System.Runtime.Remoting.RemotingException">
            If communication with the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObjectService"/> fails.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlModel.AddObjects(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlScript)">
            <summary>
            Adds objects to the model based on the contents of a <see cref="T:Microsoft.SqlServer.TransactSql.ScriptDom.TSqlScript"/> object. 
            The script should be valid TSql with no parse errors. 
            
            Objects added using this method cannot be updated or deleted at a later point 
            as update/delete requires a script name to be specified when adding the objects. If this
            is a requirement use the <see cref="M:Microsoft.SqlServer.Dac.Model.TSqlModel.AddOrUpdateObjects(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlScript,System.String,Microsoft.SqlServer.Dac.Model.TSqlObjectOptions)"/> 
            method instead.
            </summary>
            <param name="inputScript">The <see cref="T:Microsoft.SqlServer.TransactSql.ScriptDom.TSqlScript"/> to add</param>
            <exception cref="T:System.ArgumentNullException">
            If the supplied <paramref name="inputScript"/> is null.
            </exception>
            <exception cref="T:System.Runtime.Remoting.RemotingException">
            If communication with the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObjectService"/> fails.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlModel.AddObjects(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlScript,Microsoft.SqlServer.Dac.Model.TSqlObjectOptions)">
            <summary>
            Add Objects to the model based on the contents of a <see cref="T:Microsoft.SqlServer.TransactSql.ScriptDom.TSqlScript"/> object, 
            plus additional metadata defined by a <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObjectOptions"/> object
            The script should be valid TSql with no parse errors. 
            
            Objects added using this method cannot be updated or deleted at a later point 
            as update/delete requires a script name to be specified when adding the objects. If this
            is a requirement use the <see cref="M:Microsoft.SqlServer.Dac.Model.TSqlModel.AddOrUpdateObjects(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlScript,System.String,Microsoft.SqlServer.Dac.Model.TSqlObjectOptions)"/> 
            method instead.
            </summary>
            <param name="inputScript">The <see cref="T:Microsoft.SqlServer.TransactSql.ScriptDom.TSqlScript"/> to add</param>
            <param name="options">Options defining how to interpret the script</param>
            <exception cref="T:System.ArgumentNullException">
            If the supplied <paramref name="inputScript"/> is null.
            </exception>
            <exception cref="T:System.Runtime.Remoting.RemotingException">
            If communication with the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObjectService"/> fails.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlModel.AddOrUpdateObjects(System.String,System.String,Microsoft.SqlServer.Dac.Model.TSqlObjectOptions)">
            <summary>
            Adds or updates the objects defined for a specified <paramref name="sourceName"/> with the
            objects defined in the <paramref name="inputScript"/>. If any objects were previously added
            with the same <paramref name="sourceName"/> these will be completely replaced
            The object definitions are based on the contents of a TSql Script string 
            plus additional metadata defined by a <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObjectOptions"/> object
            The script should consist of valid TSql DDL statements. 
            </summary>
            <param name="inputScript">Script containing TSql DDL statements</param>
            <param name="sourceName">
            A name to identify the <paramref name="inputScript"/>, for example a 
            fileName such as "MyTable.sql" or simply an alias like "dbo.Table". 
            Scripts are cached and TSqlObjects are linked to the source name.
            Any future Update/Delete operations will remove all existing objects
            with the same script name and replace them with the new objects.
            </param>
            <param name="options">Options defining how to interpret the script</param>
            <exception cref="T:System.ArgumentNullException">
            If the supplied <paramref name="inputScript"/> is null.
            </exception>
            <exception cref="T:System.ArgumentException">
            If the supplied <paramref name="sourceName"/> is null or whitespace, or if it ends
            in ".xsd". 
            Note: source names ending in ".xsd" are currently not supported. These relate 
            to Xml Schema Collections and adding of these is currently not supported in the public API.
            </exception>
            <exception cref="T:System.Runtime.Remoting.RemotingException">
            If communication with the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObjectService"/> fails.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlModel.AddOrUpdateObjects(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlScript,System.String,Microsoft.SqlServer.Dac.Model.TSqlObjectOptions)">
            <summary>
            Adds or updates the objects defined for a specified <paramref name="sourceName"/> with the
            objects defined in the <paramref name="inputScript"/>. If any objects were previously added
            with the same <paramref name="sourceName"/> these will be completely replaced
            The object definitions are based on the contents of a <see cref="T:Microsoft.SqlServer.TransactSql.ScriptDom.TSqlScript"/> object
            plus additional metadata defined by a <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObjectOptions"/> object
            The script should consist of valid TSql DDL statements. 
            </summary>
            <param name="inputScript">Script containing TSql DDL statements</param>
            <param name="sourceName">
            A name to identify the <paramref name="inputScript"/>, for example a 
            fileName such as "MyTable.sql" or simply an alias like "dbo.Table". 
            Scripts are cached and TSqlObjects are linked to the source name.
            Any future Update/Delete operations will remove all existing objects
            with the same script name and replace them with the new objects.
            </param>
            <param name="options">Options defining how to interpret the script</param>
            <exception cref="T:System.ArgumentNullException">
            If the supplied <paramref name="inputScript"/> is null.
            </exception>
            <exception cref="T:System.ArgumentException">
            If the supplied <paramref name="sourceName"/> is null or whitespace, or if it ends
            in ".xsd". 
            Note: source names ending in ".xsd" are currently not supported. These relate 
            to Xml Schema Collections and adding of these is currently not supported in the public API.
            </exception>
            <exception cref="T:System.Runtime.Remoting.RemotingException">
            If communication with the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObjectService"/> fails.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlModel.DeleteObjects(System.String)">
            <summary>
            Deletes any objects that were added to the model with the specified <paramref name="sourceName"/>.
            </summary>
            <param name="sourceName">
            A name to identify the source to be deleted, for example a 
            fileName such as "MyTable.sql" or simply an alias like "dbo.Table". 
            Scripts are cached and TSqlObjects are linked to the source name.
            </param>
            <exception cref="T:System.ArgumentException">
            If the supplied <paramref name="sourceName"/> is null or whitespace, or if it ends
            in ".xsd". 
            Note: source names ending in ".xsd" are currently not supported. These relate 
            to Xml Schema Collections and adding of these is currently not supported in the public API.
            </exception>
            <exception cref="T:System.Runtime.Remoting.RemotingException">
            If communication with the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObjectService"/> fails.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlModel.ConvertToScriptedObject(Microsoft.SqlServer.Dac.Model.TSqlObject,System.String)">
            <summary>
            Converts a specific top-level object in the model into a scripted representation of itself.
            This is useful when starting from a database sourced model and the caller wishes to modify this
            object's contents.  
            </summary>
            <param name="tSqlObject">Object to convert. Must not be null</param>
            <param name="sourceName">Optional source name. If left null a default name will be generated
            for the object. This name can be found using <see cref="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetSourceInformation"/>'s 
            SourceName property.
            </param>
            <returns>New copy of the TSqlObject, which will now be script-backed.
            Or if the object was already script-backed, will simply return this object
            </returns>
            <exception cref="T:Microsoft.SqlServer.Dac.Model.DacModelException">
            Thrown if the object could not be scripted out, this class of object is never scriptable,
            or errors were found in the model during the script process
            </exception>
            <exception cref="T:System.ArgumentException">If the oject is null, or if whitespace is used for the source name</exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlModel.Validate">
            <summary>
            Validates the model and returns a list of messages with any errors/warnings.
            </summary>
            <returns></returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlModel.CheckVersionCompatibility(Microsoft.SqlServer.Dac.Model.SqlServerVersion,System.Threading.CancellationToken)">
            <summary>
            Validates the model to determine whether it contains any elements that are not compatible with a target version of SQL Server or Azure SQL Database, 
            and returns any errors or warnings.
            </summary>
            <param name="targetVersion">The target sql version of the export</param>
            <param name="cancelToken">A token used for cancelling the model check.</param>
            <returns></returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlModel.CopyModelOptions">
            <summary>
            Copies the <see cref="T:Microsoft.SqlServer.Dac.Model.DatabaseOptions"/> for the model to a <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModelOptions"/> object.
            This is useful if you wish to duplicate the options for a model when creating a new model.
            </summary>
            <returns><see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModelOptions"/> with settings matching the database options of the 
            model</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlModel.Dispose">
            <summary>
            Releases all resources used by the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/> object.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModel.Version">
            <summary>
            The specific SQL Server release targeted by the model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModel.EngineVersion">
            <summary>
            The specific engine version targeted by the model.
            
            For On Premises platforms this will be consistent with the <see cref="T:Microsoft.SqlServer.Dac.Model.SqlServerVersion"/> - for instance
            <see cref="F:Microsoft.SqlServer.Dac.Model.SqlServerVersion.Sql90"/> will have an engine version of 9.
            
            For Microsoft Azure SQL Database the minimum valid version is 11, and the engine version will affect
            what features are supported in the model.  
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModel.IsScriptBacked">
            <summary>
            Is this a model that is backed by scripts? In this case objects in the 
            <see cref="F:Microsoft.SqlServer.Dac.Model.DacQueryScopes.UserDefined"/> scope will have source position information including 
            a source name and information. This is important for scenarios such as use of the 
            <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.CodeAnalysisService"/>. 
            
            Any empty <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/> created will be script-backed by default. Models loaded
            from a Dacpac will only be scipt backed if the <see cref="P:Microsoft.SqlServer.Dac.Model.ModelLoadOptions.LoadAsScriptBackedModel"/>
            option is enabled. Note that creating a script-backed model will take longer than using a standard model
            loaded from a Dacpac
            
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModel.DisplayServices">
            <summary>
            Gets the <see cref="P:Microsoft.SqlServer.Dac.Model.TSqlModel.DisplayServices"/> that support producing display properties for
            objects in the model.  
            </summary>
            <returns><see cref="P:Microsoft.SqlServer.Dac.Model.TSqlModel.DisplayServices"/></returns>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModel.CollationComparer">
            <summary>
            Comparer that can compare strings and <see cref="T:Microsoft.SqlServer.Dac.Model.ObjectIdentifier"/>s using the collation of this <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/>.
            This can be very useful when comparing objects in the model since comparisons will be consistent with the expected comparison
            results in SQL Server
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.TSqlModelOptions">
            <summary>
            Defines model wide options
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.#ctor">
            <summary>
            Constructs the TSqlModelOptions
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.#ctor(Microsoft.SqlServer.Dac.Model.TSqlModelOptions)">
            <summary>
            Constructs the TSqlModelOptions based on an existing options object
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.Copy(Microsoft.SqlServer.Dac.Model.TSqlModelOptions,Microsoft.SqlServer.Dac.Model.TSqlModelOptions)">
            <summary>
            Copies deployment options between objects.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.Collation">
            <summary>
            Specifies the Collation to use for the model. This is a 
            string such as "SQL_Latin1_General_CP1_CI_AS", which must
            be a valid SQL Server Collation Name
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.StorageType">
            <summary>
            Specifies the storage type - in memory or file backed. 
            Default value is <see cref="F:Microsoft.SqlServer.Dac.DacSchemaModelStorageType.Memory"/>
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.AllowSnapshotIsolation">
            <summary>
            Specifies the ALLOW_SNAPSHOT_ISOLATION database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.TransactionIsolationReadCommittedSnapshot">
            <summary>
            Specifies the READ_COMMITTED_SNAPSHOT database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.MemoryOptimizedElevateToSnapshot">
            <summary>
            Specifies the MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.AnsiNullDefaultOn">
            <summary>
            Specifies the ANSI_NULL_DEFAULT database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.AnsiNullsOn">
            <summary>
            Specifies the ANSI_NULLS database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.AnsiPaddingOn">
            <summary>
            Specifies the ANSI_PADDING database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.AnsiWarningsOn">
            <summary>
            Specifies the ANSI_WARNINGS database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.ArithAbortOn">
            <summary>
            Specifies the ARITH_ABORT database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.AutoClose">
            <summary>
            Specifies the AUTO_CLOSE database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.AutoCreateStatistics">
            <summary>
            Specifies the AUTO_CREATE_STATISTICS database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.AutoCreateStatisticsIncremental">
            <summary>
            Specifies the AUTO_CREATE_STATISTICS INCREMENTAL database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.AutoShrink">
            <summary>
            Specifies the AUTO_SHRINK database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.AutoUpdateStatistics">
            <summary>
            Specifies the AUTO_UPDATE_STATISTICS database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.AutoUpdateStatisticsAsync">
            <summary>
            Specifies the AUTO_UPDATE_STATISTICS_ASYNC database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.ChangeTrackingAutoCleanup">
            <summary>
            Specifies the AUTO_CLEANUP database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.ChangeTrackingEnabled">
            <summary>
            Specifies the CHANGE_TRACKING database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.ChangeTrackingRetentionPeriod">
            <summary>
            Specifies the period of the CHANGE_RETENTION database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.ChangeTrackingRetentionUnit">
            <summary>
            Specifies the unit of the CHANGE_RETENTION database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.CompatibilityLevel">
            <summary>
            Specifies the COMPATIBILITY_LEVEL database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.ConcatNullYieldsNull">
            <summary>
            Specifies the CONCAT_NULL_YIELDS_NULL database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.Containment">
            <summary>
            Specifies the CONTAINMENT database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.CursorCloseOnCommit">
            <summary>
            Specifies the CURSOR_CLOSE_ON_COMMIT database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.CursorDefaultGlobalScope">
            <summary>
            Specifies the CURSOR_DEFAULT database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.DatabaseStateOffline">
            <summary>
            Specifies the ONLINE | OFFLINE database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.DateCorrelationOptimizationOn">
            <summary>
            Specifies the DATE_CORRELATION_OPTIMIZATION database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.DefaultFullTextLanguage">
            <summary>
            Specifies the DEFAULT_FULLTEXT_LANGUAGE database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.DefaultLanguage">
            <summary>
            Specifies the DEFAULT_LANGUAGE database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.DBChainingOn">
            <summary>
            Specifies the DB_CHAINING database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.DbScopedConfigLegacyCardinalityEstimation">
            <summary>
            Specifies the Database Scoped Configuration LEGACY_CARDINALITY_ESTIMATION option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.DbScopedConfigLegacyCardinalityEstimationSecondary">
            <summary>
            Specifies the Database Scoped Configuration LEGACY_CARDINALITY_ESTIMATION option for secondaries.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.DbScopedConfigMaxDOP">
            <summary>
            Specifies the Database Scoped Configuration MAXDOP option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.DbScopedConfigMaxDOPSecondary">
            <summary>
            Specifies the Database Scoped Configuration MAXDOP option for secondaries.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.DbScopedConfigParameterSniffing">
            <summary>
            Specifies the Database Scoped Configuration PARAMETER_SNIFFING option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.DbScopedConfigParameterSniffingSecondary">
            <summary>
            Specifies the Database Scoped Configuration PARAMETER_SNIFFING option for secondaries.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.DbScopedConfigQueryOptimizerHotfixes">
            <summary>
            Specifies the Database Scoped Configuration QUERY_OPTIMIZER_HOTFIXES option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.DbScopedConfigQueryOptimizerHotfixesSecondary">
            <summary>
            Specifies the Database Scoped Configuration QUERY_OPTIMIZER_HOTFIXES option for secondaries.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.FileStreamDirectoryName">
            <summary>
            Specifies the DIRECTORY_NAME database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.FullTextEnabled">
            <summary>
            Specifies the sp_fulltext_database database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.HonorBrokerPriority">
            <summary>
            Specifies the HONOR_BROKER_PRIORITY database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.NestedTriggersOn">
            <summary>
            Specifies the NESTED_TRIGGER database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.NonTransactedFileStreamAccess">
            <summary>
            Specifies the NON_TRANSACTED_ACCESS database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.NumericRoundAbortOn">
            <summary>
            Specifies the NUMERIC_ROUNDABORT database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.PageVerifyMode">
            <summary>
            Specifies the PAGE_VERIFY database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.ParameterizationOption">
            <summary>
            Specifies the PARAMETERIZATION database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.QueryStoreCaptureMode">
            <summary>
            Specifies the query capture mode of QUERY_STORE database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.QueryStoreDesiredState">
            <summary>
            Specifies the operation mode of QUERY_STORE database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.QueryStoreFlushInterval">
            <summary>
            Specifies the Flush_Interval_Seconds setting of the QUERY_STORE database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.QueryStoreIntervalLength">
            <summary>
            Specifies the Interval_Length_Minutes setting of the QUERY_STORE database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.QueryStoreMaxStorageSize">
            <summary>
            Specifies the Max_Storage_Size_MB setting of the QUERY_STORE database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.QueryStoreMaxPlansPerQuery">
            <summary>
            Specifies the Max_Plans_Per_Query setting of the QUERY_STORE database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.QueryStoreStaleQueryThreshold">
            <summary>
            Specifies the Stale_Query_Threshold_Days setting of the QUERY_STORE database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.QuotedIdentifierOn">
            <summary>
            Specifies the QUOTED_IDENTIFIER database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.ReadOnly">
            <summary>
            Specifies the READ_ONLY | READ_WRITE database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.RecoveryMode">
            <summary>
            Specifies the RECOVERY database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.DelayedDurabilityMode">
            <summary>
            Specifies the DELAYED_DURABILITY database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.RecursiveTriggersOn">
            <summary>
            Specifies the RECURSIVE_TRIGGERS database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.ServiceBrokerOption">
            <summary>
            Specifies the ENABLE_BROKER | DISABLE_BROKER | NEW_BROKER | ERROR_BROKER_CONVERSATIONS database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.EngineVersion">
            <summary>
            Specifies what engine version should be targeted by the model.
            For On Premises platforms this value will be ignored as the engine version is fixed based on the platform.
            For the <see cref="F:Microsoft.SqlServer.Dac.Model.SqlServerVersion.SqlAzure"/> platform this value will be set and used
            to specify what T-SQL language features are supported. 
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.SupplementalLoggingOn">
            <summary>
            Specifies the SUPPLEMENTAL_LOGGING database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.TargetRecoveryTimePeriod">
            <summary>
            Specifies the period of the TARGET_RECOVERY_TIME database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.TargetRecoveryTimeUnit">
            <summary>
            Specifies the unit of the TARGET_RECOVERY_TIME database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.TornPageProtectionOn">
            <summary>
            Specifies the TORN_PAGE_DETECTION database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.TransformNoiseWords">
            <summary>
            Specifies the TRANSFORM_NOISE_WORDS database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.Trustworthy">
            <summary>
            Specifies the TRUSTWORTHY database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.TwoDigitYearCutoff">
            <summary>
            Specifies the TWO_DIGIT_YEAR_CUTOFF database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.VardecimalStorageFormatOn">
            <summary>
            Specifies the sp_db_vardecimal_storage_format database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.UserAccessOption">
            <summary>
            Specifies the SINGLE_USER | RESTRICTED_USER | MULTI_USER database option.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelOptions.WithEncryption">
            <summary>
            Specifies the ENCRYPTION database option.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.TSqlModelSchema">
            <summary>
            Defines a schema for a relational TSQL database model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelSchema.TopLevelTypes">
            <summary>
            All top level types supported in the model. These are the only types that
            can be queried for via the <see cref="M:Microsoft.SqlServer.Dac.Model.TSqlModel.GetObject(Microsoft.SqlServer.Dac.Model.ModelTypeClass,Microsoft.SqlServer.Dac.Model.ObjectIdentifier,Microsoft.SqlServer.Dac.Model.DacQueryScopes)"/> and 
            <see cref="M:Microsoft.SqlServer.Dac.Model.TSqlModel.GetObjects(Microsoft.SqlServer.Dac.Model.DacQueryScopes,Microsoft.SqlServer.Dac.Model.ModelTypeClass[])"/> 
            API calls.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlModelSchema.AllTypes">
            <summary>
            All types supported in the model. This will include types that are cannot be queried for using
            the <see cref="M:Microsoft.SqlServer.Dac.Model.TSqlModel.GetObject(Microsoft.SqlServer.Dac.Model.ModelTypeClass,Microsoft.SqlServer.Dac.Model.ObjectIdentifier,Microsoft.SqlServer.Dac.Model.DacQueryScopes)"/> and 
            <see cref="M:Microsoft.SqlServer.Dac.Model.TSqlModel.GetObjects(Microsoft.SqlServer.Dac.Model.DacQueryScopes,Microsoft.SqlServer.Dac.Model.ModelTypeClass[])"/> 
            API calls.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.TSqlObject">
            <summary>
            Represents an instance of an object for a SQL Server database schema.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.TryGetAst(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlScript@)">
            <summary>
            Attempts to generate an AST for the object. AST generation is only supported for top-level objects, with certain
            objects such as the <see cref="T:Microsoft.SqlServer.Dac.Model.DatabaseOptions"/> and inline constraints not supporting AST generation even though
            they are top level types.
            
            The generated AST is a newly generated object that defines the information about this <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> and any objects
            that would be scripted together with it. For example a Table would also script out the Columns and inline constraint definitions of a Table. 
            This method can be used to copy object information from one model to another, or to script out information about
            objects in the model.
            </summary>
            <param name="objectAst">Returns the generated AST if the call succeeded.</param>
            <returns>True if AST generated. Otherwise false.</returns>
            <remarks>
            The Ast will not have information about the original source position, such as the original source name and original
            position in an input script.
            </remarks>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetAst">
            <summary>
            Generates a new <see cref="T:Microsoft.SqlServer.TransactSql.ScriptDom.TSqlScript"/> representing the AST for the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>, if this is
            supported for the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>. AST generation is only supported for top-level objects, with certain
            objects such as the <see cref="T:Microsoft.SqlServer.Dac.Model.DatabaseOptions"/> and inline constraints not supporting AST generation even though
            they are top level types. If it's unknown whether the object is capable of being scripted
            then the <see cref="M:Microsoft.SqlServer.Dac.Model.TSqlObject.TryGetAst(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlScript@)"/> method should be used instead.
            
            The generated AST is a newly generated object that defines the information about this <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> and any objects
            that would be scripted together with it. For example a Table would also script out the Columns and inline constraint definitions of a Table. 
            This method can be used to copy object information from one model to another, or to script out information about
            objects in the model.
            </summary>
            <remarks>
            The AST will not have information about the original source position, such as the original source name and original
            position in an input script.
            </remarks>
            <returns>The generated AST object.</returns>
            <exception cref="T:Microsoft.SqlServer.Dac.Model.DacModelException">
            If <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> is not a top level statement object or if it does not support AST generation (for example
            if it is a <see cref="T:Microsoft.SqlServer.Dac.Model.DatabaseOptions"/> or a constraint that was originally defined inline with a Table).
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.TryGetScript(System.String@)">
            <summary>
            Attempts to generate a CREATE script from the objects AST. AST generation is only supported for top-level objects, with certain
            objects such as the <see cref="T:Microsoft.SqlServer.Dac.Model.DatabaseOptions"/> and inline constraints not supporting AST generation even though
            they are top level types. If it's unknown whether the object is capable of being scripted
            then the <see cref="M:Microsoft.SqlServer.Dac.Model.TSqlObject.TryGetAst(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlScript@)"/> method should be used instead.
            </summary>
            <param name="objectScript">The DDL Creation script for the object </param>
            <returns>True if the DDL creation script was created.</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetScript">
            <summary>
            Generates a CREATE script from the objects AST. AST generation is only supported for top-level objects, with certain
            objects such as the <see cref="T:Microsoft.SqlServer.Dac.Model.DatabaseOptions"/> and inline constraints not supporting AST generation even though
            they are top level types. If it's unknown whether the object is capable of being scripted
            then the <see cref="M:Microsoft.SqlServer.Dac.Model.TSqlObject.TryGetAst(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlScript@)"/> method should be used instead.
            </summary>
            <returns>DDL Creation script</returns>
            <exception cref="T:Microsoft.SqlServer.Dac.Model.DacModelException">
            If <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> is not a top level statement object or if it does not support script generation (for example
            if it is a <see cref="T:Microsoft.SqlServer.Dac.Model.DatabaseOptions"/> or a constraint that was originally defined inline with a Table).
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetSourceFragment">
            <summary>
            Note: Internal only for now. Dacpacs and database-targeted models will not have any source fragment information. 
             
            Gets the <see cref="T:Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment"/> which defines the object - may be null. This is the fragment from the original source,
            with original source position information (if available). 
            
            This should not be used when copying the object to a different model or when doing any external 
            manipulation of the object - in those cases the <see cref="M:Microsoft.SqlServer.Dac.Model.TSqlObject.TryGetAst(Microsoft.SqlServer.TransactSql.ScriptDom.TSqlScript@)"/> method should be used to get a 
            self-contained AST describing this Object.
            </summary>
            <returns><see cref="T:Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment"/>, or null if this was not present</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetProperty``1(Microsoft.SqlServer.Dac.Model.ModelPropertyClass)">
            <summary>
            Returns the property value.
            </summary>
            <typeparam name="T">Property type.</typeparam>
            <param name="property">Property.</param>
            <returns>Property Value.</returns>
            <exception cref="T:Microsoft.SqlServer.Dac.Model.DacModelException">
            If <paramref name="property"/> does not exists on the object.
            </exception>
            <exception cref="T:System.InvalidCastException">
            If the property type cannot be cast to T.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetProperty(Microsoft.SqlServer.Dac.Model.ModelPropertyClass)">
            <summary>
            Returns the property value.
            </summary>
            <param name="property">Property.</param>
            <returns>Property Value.</returns>
            <exception cref="T:Microsoft.SqlServer.Dac.Model.DacModelException">
            If <paramref name="property"/> does not exists on the object.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetMetadata``1(Microsoft.SqlServer.Dac.Model.ModelMetadataClass)">
            <summary>
            Returns the metadata property value.
            </summary>
            <typeparam name="T">Metadata property type.</typeparam>
            <param name="metadata">Metadata property name.</param>
            <returns>Metadata property value.</returns>
            <exception cref="T:Microsoft.SqlServer.Dac.Model.DacModelException">
            If <paramref name="metadata"/> does not exists on the object.
            </exception>
            <exception cref="T:System.InvalidCastException">
            If the metadata property type cannot be cast to T.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetMetadata(Microsoft.SqlServer.Dac.Model.ModelMetadataClass)">
            <summary>
            Returns the metadata property value.
            </summary>
            <param name="metadata">Metadata property name.</param>
            <returns>Metadata property value.</returns>
            <exception cref="T:Microsoft.SqlServer.Dac.Model.DacModelException">
            If <paramref name="metadata"/> does not exists on the object.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetParent">
            <summary>
            Returns the parent <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>, if it is within the
            <see cref="F:Microsoft.SqlServer.Dac.Model.DacQueryScopes.Default"/> scope and if there is only one <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>
            that could be the parent for this object..
            </summary>
            <returns>Parent object others null.</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetParent(Microsoft.SqlServer.Dac.Model.DacQueryScopes)">
            <summary>
            Returns the parent <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>, if it is within the
            specified <see cref="T:Microsoft.SqlServer.Dac.Model.DacQueryScopes"/> scope and if there is only one <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>
            that could be the parent for this object.
            </summary>
            <param name="queryScopes"><see cref="T:Microsoft.SqlServer.Dac.Model.DacQueryScopes"/> defining the scope objects must exist in.</param>
            <returns>Parent object or else null.</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetChildren">
            <summary>
            Returns all <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> child objects within the <see cref="F:Microsoft.SqlServer.Dac.Model.DacQueryScopes.Default"/> scope.
            </summary>
            <returns>All children.</returns>
            <remarks>
            References to elements not in the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/> are not returned.
            </remarks>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetChildren(Microsoft.SqlServer.Dac.Model.DacQueryScopes)">
            <summary>
            Returns all <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> child objects within the specified
            <see cref="T:Microsoft.SqlServer.Dac.Model.DacQueryScopes"/> scope.
            </summary>
            <param name="queryScopes"><see cref="T:Microsoft.SqlServer.Dac.Model.DacQueryScopes"/> defining the scope objects must exist in.</param>
            <returns>All children.</returns>
            <remarks>
            References to elements not in the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/> are not returned.
            </remarks>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetReferenced">
            <summary>
            Gets all referenced objects within the <see cref="F:Microsoft.SqlServer.Dac.Model.DacQueryScopes.Default"/> scope
            </summary>
            <returns><see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>s referenced by this object</returns>
            <remarks>
            References to elements not in the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/> are not returned. Use <see cref="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetReferencedRelationshipInstances"/> for a listing
            of all possible referenced elements.
            </remarks>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetReferenced(Microsoft.SqlServer.Dac.Model.DacQueryScopes)">
            <summary>
            Gets all referenced objects within a given <see cref="T:Microsoft.SqlServer.Dac.Model.DacQueryScopes"/> scope.
            </summary>
            <param name="queryScopes"><see cref="T:Microsoft.SqlServer.Dac.Model.DacQueryScopes"/> defining the scope objects must exist in.</param>
            <returns><see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>s referenced by this object</returns>
            <remarks>
            References to elements not in the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/> are not returned. Use <see cref="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetReferencedRelationshipInstances"/> for a listing
            of all possible referenced elements.
            </remarks>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetReferenced(Microsoft.SqlServer.Dac.Model.ModelRelationshipClass)">
            <summary>
            Gets referenced objects for a given <see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipClass"/>,
            where objects are within the <see cref="F:Microsoft.SqlServer.Dac.Model.DacQueryScopes.Default"/> scope
            </summary>
            <param name="relationshipType"><see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipClass"/> defining the relationship class to query for</param>
            <returns><see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>s referenced by this object for a given <see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipClass"/></returns>
            <remarks>
            References to elements not in the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/> are not returned. Use <see cref="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetReferencedRelationshipInstances"/> for a listing
            of all possible referenced elements.
            </remarks>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetReferenced(Microsoft.SqlServer.Dac.Model.ModelRelationshipClass,Microsoft.SqlServer.Dac.Model.DacQueryScopes)">
            <summary>
            Gets referenced objects for a given <see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipClass"/>,
            where objects are within a given <see cref="T:Microsoft.SqlServer.Dac.Model.DacQueryScopes"/> scope.
            </summary>
            <param name="relationshipType"><see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipClass"/> defining the relationship to query for</param>
            <param name="queryScopes"><see cref="T:Microsoft.SqlServer.Dac.Model.DacQueryScopes"/> defining the scope objects must exist in.</param>
            <returns><see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>s referenced by this object for a given <see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipClass"/></returns>
            <remarks>
            References to elements not in the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/> are not returned. Use <see cref="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetReferencedRelationshipInstances"/> for a listing
            of all possible referenced elements.
            </remarks>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetReferencedRelationshipInstances">
            <summary>
            For each object referenced by this <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>, returns the <see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance"/> defining
            the relationship data. This will include the referenced object if it exists, the <see cref="T:Microsoft.SqlServer.Dac.Model.ObjectIdentifier"/> used to identify the
            referenced object, and any properties specific to this relationship.
            Only objects within the <see cref="F:Microsoft.SqlServer.Dac.Model.DacExternalQueryScopes.Default"/> scope will be returned.
            </summary>
            <returns><see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance"/>s defining the relationships between this <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> and the objects
            referenced by it</returns>
            <remarks>
            The <see cref="P:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance.Object"/> field may be null if the element is not in the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/>. In these cases the
            <see cref="P:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance.ObjectName"/> field can be used to query information about the referenced object
            </remarks>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetReferencedRelationshipInstances(Microsoft.SqlServer.Dac.Model.DacQueryScopes)">
            <summary>
            For each object referenced by this <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>, returns the <see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance"/> defining
            the relationship data. This will include the referenced object if it exists, the <see cref="T:Microsoft.SqlServer.Dac.Model.ObjectIdentifier"/> used to identify the
            referenced object, and any properties specific to this relationship.
            Only objects within the specified <see cref="T:Microsoft.SqlServer.Dac.Model.DacQueryScopes"/> scope will be returned.
            To return non-composite or system references the <see cref="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetReferencedRelationshipInstances(Microsoft.SqlServer.Dac.Model.DacExternalQueryScopes)"/> method should be called instead
            </summary>
            <param name="queryScopes"><see cref="T:Microsoft.SqlServer.Dac.Model.DacQueryScopes"/> defining the scope objects must exist in.</param>
            <returns><see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance"/>s defining the relationships between this <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> and the objects
            referenced by it</returns>
            <remarks>
            The <see cref="P:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance.Object"/> field may be null if the element is not in the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/>. In these cases the
            <see cref="P:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance.ObjectName"/> field can be used to query information about the referenced object
            </remarks>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetReferencedRelationshipInstances(Microsoft.SqlServer.Dac.Model.ModelRelationshipClass)">
            <summary>
            For objects referenced by this <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> with a specific <see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipClass"/>, 
            returns the <see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance"/> defining
            the relationship data. This will include the referenced object if it exists, the <see cref="T:Microsoft.SqlServer.Dac.Model.ObjectIdentifier"/> used to identify the
            referenced object, and any properties specific to this relationship.
            Only objects within the <see cref="F:Microsoft.SqlServer.Dac.Model.DacExternalQueryScopes.Default"/> scope will be returned.
            </summary>
            <param name="relationshipType"><see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipClass"/> defining the relationship class to query for</param>
            <returns><see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance"/>s defining the relationships between this <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> and the objects
            referenced by it</returns>
            <remarks>
            The <see cref="P:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance.Object"/> field may be null if the element is not in the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/>. In these cases the
            <see cref="P:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance.ObjectName"/> field can be used to query information about the referenced object
            </remarks>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetReferencedRelationshipInstances(Microsoft.SqlServer.Dac.Model.ModelRelationshipClass,Microsoft.SqlServer.Dac.Model.DacQueryScopes)">
            <summary>
            For objects referenced by this <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> with a specific <see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipClass"/>, 
            returns the <see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance"/> defining
            the relationship data. This will include the referenced object if it exists, the <see cref="T:Microsoft.SqlServer.Dac.Model.ObjectIdentifier"/> used to identify the
            referenced object, and any properties specific to this relationship.
            Only objects within the specified <see cref="T:Microsoft.SqlServer.Dac.Model.DacQueryScopes"/> scope will be returned.
            To return non-composite or system references the <see cref="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetReferencedRelationshipInstances(Microsoft.SqlServer.Dac.Model.DacExternalQueryScopes)"/> method should be called instead
            </summary>
            <param name="relationshipType"><see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipClass"/> defining the relationship class to query for</param>
            <param name="queryScopes"><see cref="T:Microsoft.SqlServer.Dac.Model.DacQueryScopes"/> defining the scope objects must exist in.</param>
            <returns><see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance"/>s defining the relationships between this <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> and the objects
            referenced by it</returns>
            <remarks>
            The <see cref="P:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance.Object"/> field may be null if the element is not in the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/>. In these cases the
            <see cref="P:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance.ObjectName"/> field can be used to query information about the referenced object
            </remarks>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetReferencedRelationshipInstances(Microsoft.SqlServer.Dac.Model.DacExternalQueryScopes)">
            <summary>
            For each object referenced by this <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>, returns the <see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance"/> defining
            the relationship data. This will include the referenced object if it exists, the <see cref="T:Microsoft.SqlServer.Dac.Model.ObjectIdentifier"/> used to identify the
            referenced object, and any properties specific to this relationship.
            Only objects within the specified <see cref="T:Microsoft.SqlServer.Dac.Model.DacQueryScopes"/> scope will be returned.
            To return non-composite or system references the scope must be set to include externals. In addition, in this case only the
            <see cref="T:Microsoft.SqlServer.Dac.Model.ObjectIdentifier"/> of the referenced object will be returned
            </summary>
            <param name="queryScopes"><see cref="T:Microsoft.SqlServer.Dac.Model.DacExternalQueryScopes"/> defining the scope objects must exist in.</param>
            <returns><see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance"/>s defining the relationships between this <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> and the objects
            referenced by it</returns>
            <remarks>
            The <see cref="P:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance.Object"/> field may be null if the element is not in the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/>. In these cases the
            <see cref="P:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance.ObjectName"/> field can be used to query information about the referenced object
            </remarks>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetReferencedRelationshipInstances(Microsoft.SqlServer.Dac.Model.ModelRelationshipClass,Microsoft.SqlServer.Dac.Model.DacExternalQueryScopes)">
            <summary>
            For objects referenced by this <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> with a specific <see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipClass"/>, 
            returns the <see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance"/> defining
            the relationship data. This will include the referenced object if it exists, the <see cref="T:Microsoft.SqlServer.Dac.Model.ObjectIdentifier"/> used to identify the
            referenced object, and any properties specific to this relationship.
            Only objects within the specified <see cref="T:Microsoft.SqlServer.Dac.Model.DacExternalQueryScopes"/> scope will be returned.
            To return non-composite or system references the scope must be set to include externals. In addition, in this case only the
            <see cref="T:Microsoft.SqlServer.Dac.Model.ObjectIdentifier"/> of the referenced object will be returned
            </summary>
            <param name="relationshipType"><see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipClass"/> defining the relationship class to query for</param>
            <param name="queryScopes"><see cref="T:Microsoft.SqlServer.Dac.Model.DacExternalQueryScopes"/> defining the scope objects must exist in.</param>
            <returns><see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance"/>s defining the relationships between this <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> and the objects
            referenced by it</returns>
            <remarks>
            The <see cref="P:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance.Object"/> field may be null if the element is not in the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/>. In these cases the
            <see cref="P:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance.ObjectName"/> field can be used to query information about the referenced object
            </remarks>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.VerifyRelationshipOwner(Microsoft.SqlServer.Dac.Model.ModelRelationshipClass)">
            <summary>
            Verifies this object's class owns the relationship. Only valid for referenced relationships - referencing could be anything
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetReferencing">
            <summary>
            Gets all referencing objects within the <see cref="F:Microsoft.SqlServer.Dac.Model.DacQueryScopes.Default"/> scope
            </summary>
            <returns><see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>s referencing this object</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetReferencing(Microsoft.SqlServer.Dac.Model.DacQueryScopes)">
            <summary>
            Gets all referencing objects within a given <see cref="T:Microsoft.SqlServer.Dac.Model.DacQueryScopes"/> scope.
            </summary>
            <param name="queryScopes"><see cref="T:Microsoft.SqlServer.Dac.Model.DacQueryScopes"/> defining the scope objects must exist in.</param>
            <returns><see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>s referencing this object</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetReferencing(Microsoft.SqlServer.Dac.Model.ModelRelationshipClass)">
            <summary>
            Gets referencing objects, 
            where the relationship is defined by a specific <see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipClass"/> 
            and where objects are within the <see cref="F:Microsoft.SqlServer.Dac.Model.DacQueryScopes.Default"/> scope
            </summary>
            <param name="relationshipType"><see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipClass"/> defining the relationship class to query for</param>
            <returns><see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>s referencing this object for a given <see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipClass"/></returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetReferencing(Microsoft.SqlServer.Dac.Model.ModelRelationshipClass,Microsoft.SqlServer.Dac.Model.DacQueryScopes)">
            <summary>
            Gets referencing objects, 
            where the relationship is defined by a specific <see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipClass"/> 
            and where objects are within a given <see cref="T:Microsoft.SqlServer.Dac.Model.DacQueryScopes"/> scope.
            </summary>
            <param name="relationshipType"><see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipClass"/> defining the relationship to query for</param>
            <param name="queryScopes"><see cref="T:Microsoft.SqlServer.Dac.Model.DacQueryScopes"/> defining the scope objects must exist in.</param>
            <returns><see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>s referencing this object for a given <see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipClass"/></returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetReferencingRelationshipInstances">
            <summary>
            For each object referencing this <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>, returns the <see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance"/> defining
            the relationship data. 
            This will include the referencing object - definedby the <see cref="P:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance.FromObject"/> field, 
            and any properties specific to this relationship.
            Only objects within the specified <see cref="T:Microsoft.SqlServer.Dac.Model.DacQueryScopes"/> scope will be returned.
            </summary>
            <returns><see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance"/>s defining the relationships between this <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> and the objects
            referencing it</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetReferencingRelationshipInstances(Microsoft.SqlServer.Dac.Model.DacQueryScopes)">
            <summary>
            For each object referencing this <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>, returns the <see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance"/> defining
            the relationship data. 
            This will include the referencing object - definedby the <see cref="P:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance.FromObject"/> field, 
            and any properties specific to this relationship.
            Only objects within the <see cref="F:Microsoft.SqlServer.Dac.Model.DacQueryScopes.Default"/> scope will be returned.
            </summary>
            <param name="queryScopes"><see cref="T:Microsoft.SqlServer.Dac.Model.DacQueryScopes"/> defining the scope objects must exist in.</param>
            <returns><see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance"/>s defining the relationships between this <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> and the objects
            referencing it</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetReferencingRelationshipInstances(Microsoft.SqlServer.Dac.Model.ModelRelationshipClass)">
            <summary>
            For each object referencing this <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> with a specific <see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipClass"/>,
            returns the <see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance"/> defining the relationship data. 
            This will include the referencing object - definedby the <see cref="P:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance.FromObject"/> field, 
            and any properties specific to this relationship.
            Only objects within the <see cref="F:Microsoft.SqlServer.Dac.Model.DacQueryScopes.Default"/> scope will be returned.
            </summary>
            <param name="relationshipType"><see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipClass"/> defining the relationship class to query for</param>
            <returns><see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance"/>s defining the relationships between this <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> and the objects
            referencing it
            </returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetReferencingRelationshipInstances(Microsoft.SqlServer.Dac.Model.ModelRelationshipClass,Microsoft.SqlServer.Dac.Model.DacQueryScopes)">
            <summary>
            For each object referencing this <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> with a specific <see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipClass"/>,
            returns the <see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance"/> defining the relationship data. 
            This will include the referencing object - definedby the <see cref="P:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance.FromObject"/> field, 
            and any properties specific to this relationship.
            Only objects within the specified <see cref="T:Microsoft.SqlServer.Dac.Model.DacQueryScopes"/> scope will be returned.
            </summary>
            <param name="relationshipType"><see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipClass"/> defining the relationship class to query for</param>
            <param name="queryScopes"><see cref="T:Microsoft.SqlServer.Dac.Model.DacQueryScopes"/> defining the scope objects must exist in.</param>
            <returns><see cref="T:Microsoft.SqlServer.Dac.Model.ModelRelationshipInstance"/>s defining the relationships between this <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> and the objects
            referencing it
            </returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetSourceInformation">
            <summary>
            Gets the <see cref="T:Microsoft.SqlServer.Dac.SourceInformation"/> for this object, if this is available
            </summary>
            <returns>
            If source information is available returns the <see cref="T:Microsoft.SqlServer.Dac.SourceInformation"/> specifying the name of the script 
            this object is found in, plus start line and column. These values may be null or -1 if no source position information is present
            </returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.Equals(System.Object)">
            <summary>
            Checks if this <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> is equal to another <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> 
            </summary>
            <param name="obj">object to compare</param>
            <returns>true if they are equal</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.GetHashCode">
            <summary>
            Gets the Hashcode for this object
            </summary>
            <returns>int hashcode</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Model.TSqlObject.UpdateContextObject(System.Object)">
            <summary>
            Internal method that updates the context object for the public <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>
            wrapper. Should only be called if the original context object is no longer valid, for instance
            if it has been deleted from the model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlObject.Name">
            <summary>
            Identity of the object.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlObject.ObjectType">
            <summary>
            Type of the object.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlObject.Item(Microsoft.SqlServer.Dac.Model.ModelPropertyClass)">
            <summary>
            Returns the property value.
            </summary>
            <param name="property">Property.</param>
            <returns>Property value.</returns>
            <exception cref="T:Microsoft.SqlServer.Dac.Model.DacModelException">
            If <paramref name="property"/> does not exists on the object.
            </exception>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlObject.ContextObject">
            <summary>
            Returns the context object for this Sql Object
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.TSqlObjectOptions">
            <summary>
            Defines options to be used for specific TSqlObjects 
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlObjectOptions.QuotedIdentifier">
            <summary>
            Specifies if the QUOTED_IDENTIFIER setting for objects is on or off
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Model.TSqlObjectOptions.AnsiNulls">
            <summary>
            Specifies if the the ANSI_NULLS connection setting for objects is on or off
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Model.TSqlPlatforms">
            <summary>
            Specific SQL Server releases.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.TSqlPlatforms.Sql90">
             <summary>
            on-premises, version 9.0
             </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.TSqlPlatforms.Sql100">
            <summary>
            on-premises, version 10.0
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.TSqlPlatforms.SqlAzure">
            <summary>
            Microsoft Azure SQL Database, version 11
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.TSqlPlatforms.Sql110">
            <summary>
            on-premises, version 11.0
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.TSqlPlatforms.Sql120">
            <summary>
            on-premises, version 12.0
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.TSqlPlatforms.Sql130">
            <summary>
            on-premises, version 13.0
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.TSqlPlatforms.SqlAzureV12">
            <summary>
            Microsoft Azure SQL Database, version 12
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.TSqlPlatforms.Sql140">
            <summary>
            on-premises, version 14.0
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.TSqlPlatforms.OnPremises">
            <summary>
            All on-premises versions
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.TSqlPlatforms.Cloud">
            <summary>
            All cloud releases
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.Model.TSqlPlatforms.All">
            <summary>
            Combination of all SQL Server categories
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.PackageMetadata">
            <summary>
            Metadata information that describes a package.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.PackageMetadata.Name">
            <summary>
            Package name
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.PackageMetadata.Version">
            <summary>
            Package version
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.PackageMetadata.Description">
            <summary>
            Package description
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.PackageOptions">
            <summary>
            Defines advanced options and additional artifacts for package creation.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.PackageOptions.TreatWarningsAsErrors">
            <summary>
            Indicates whether warnings should be treated as errors during model validation 
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.PackageOptions.IgnoreValidationErrors">
            <summary>
            Indicates the list of errors that will be ignored during validation
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.PackageOptions.RefactorLogPath">
            <summary>
            Path to the file to be added as the refactor log content to the .dacpac
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.PackageOptions.DeploymentContributors">
            <summary>
            List of contributors that are required in order to deploy the .dacpac
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.PackageOptions.ContributorArguments">
            <summary>
            List of contributors that are required in order to deploy the .dacpac
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.PackageOptions.DeploymentContributorConfigurationStreams">
            <summary>
            List of contributors that are required in order to deploy the .dacpac
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.SourceInformation">
            <summary>
            Represents information about an item of interest in the model, 
            for example a <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem"/>. 
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.SourceInformation.#ctor(System.String,System.Int32,System.Int32)">
            <summary>
            Initializes an instance of a <see cref="T:Microsoft.SqlServer.Dac.SourceInformation"/> object.
            </summary>
            <param name="sourceName">Name representing the source for the information</param>
            <param name="startLine">start line, if known. May be -1 or 0 if the position is not known.</param>
            <param name="startColumn">start column, if known. May be -1 or 0 if the position is not known.</param>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.SourceInformation.SourceName">
            <summary>
            Name representing the source for the information
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.SourceInformation.StartLine">
            <summary>
            The start line. May be -1 or 0 if the position is not known.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.SourceInformation.StartColumn">
            <summary>
            The start column. May be -1 or 0 if the position is not known.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.TSqlModelUtils">
            <summary>
            Provides utility methods for the public model APIs.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.TSqlModelUtils.TryGetFragmentForAnalysis(Microsoft.SqlServer.Dac.Model.TSqlObject,Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment@)">
            <summary>
            Tries to get the most suitable <see cref="T:Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment"/> for use during the rule analysis process.
            If the TSqlObject was originally built from a scripted source then the original source fragment will be returned.
            
            Otherwise a new AST will be generated from the <paramref name="tSqlObject"/>.
             
            This ensures that when reporting <see cref="T:Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem"/>s the most accurate source information can be included
            in the error messages. 
            
            Note that a fragment will only be returned if this is
            supported for the <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/>. AST generation is only supported for top-level objects, with certain
            objects such as the <see cref="T:Microsoft.SqlServer.Dac.Model.DatabaseOptions"/> and inline constraints not supporting AST generation even though
            they are top level types.
            </summary>
            <param name="tSqlObject">The <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlObject"/> whose fragment is being requested</param>
            <param name="fragment">The <see cref="T:Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragment"/> for the <paramref name="tSqlObject"/>, if it is possible to
            get fragment information for the object</param>
            <returns>true if the fragment was obtained, false otherwise.</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.TSqlModelUtils.CalculatePlatformCompatibility(Microsoft.SqlServer.Dac.Model.SqlServerVersion)">
            <summary>
            Maps from a <see cref="T:Microsoft.SqlServer.Dac.Model.SqlServerVersion"/> to the <see cref="T:Microsoft.SqlServer.Dac.Extensibility.TSqlPlatformCompatibility"/> enum that matches it. The
            platform compatibility can be used to filter out extensions that don't support that platform, for instance code analysis
            rules.
            </summary>
            <param name="version">A <see cref="T:Microsoft.SqlServer.Dac.Model.SqlServerVersion"/>, for example the version for a <see cref="T:Microsoft.SqlServer.Dac.Model.TSqlModel"/></param>
            <returns>
            <see cref="T:Microsoft.SqlServer.Dac.Extensibility.TSqlPlatformCompatibility"/> indicating the compatibility level required for extensions. If no specific 
            version was passed into the method then the default will be to use <see cref="F:Microsoft.SqlServer.Dac.Extensibility.TSqlPlatformCompatibility.All"/> so
            that all extensions are loaded. 
            </returns>
        </member>
        <member name="T:AssemblyRef">
            <summary>
            Sets public key string for friend assemblies.
            </summary>
        </member>
        <member name="F:AssemblyRef.ProductPublicKey">
            <summary>No signing</summary>
        </member>
    </members>
</doc>
tools\dbatools\bin\smo\Microsoft.SqlServer.Dac.xml
<?xml version="1.0"?>
<doc>
    <assembly>
        <name>Microsoft.SqlServer.Dac</name>
    </assembly>
    <members>
        <member name="T:Microsoft.SqlServer.Dac.BacPackage">
            <summary>
            Representation of the artifact that contains the definition and data of a data-tier application.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.BacPackage.Dispose">
            <summary>
            Release the resources held by this instance.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.BacPackage.Unpack(System.String)">
            <summary>
            Place the contents of this package into the directory specified by <paramref name="directoryPath"/>.
            </summary>
            <param name="directoryPath">
            Base directory in which to place the package contents.
            </param>
            <exception cref="T:System.ArgumentException">
            If <paramref name="directoryPath"/> is a null reference or empty string.
            </exception>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If an error occurs while attempting to unpack the package.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.BacPackage.Load(System.String)">
            <summary>
            Load a package file specified by <paramref name="fileName"/>.
            </summary>
            <param name="fileName">
            Path to the package file.
            </param>
            <returns>
            <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> instance that represents the package loaded from the specified file.
            </returns>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If there is a problem reading the package; or if the package contains exported data.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.BacPackage.Load(System.String,Microsoft.SqlServer.Dac.DacSchemaModelStorageType)">
            <summary>
            Load a package file specified by <paramref name="fileName"/>.
            </summary>
            <param name="fileName">
            Path to the package file.
            </param>
            <param name="modelStorageType">
            Backing storage type for package instance.
            </param>
            <returns>
            <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> instance that represents the package loaded from the specified file.
            </returns>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If there is a problem reading the package; or if the package does not contain exported data.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.BacPackage.Load(System.IO.Stream)">
            <summary>
            Load a package from the specified <see cref="T:System.IO.Stream"/>.
            </summary>
            <param name="stream">
            Stream from which to read the package.
            </param>
            <returns>
            <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> instance that represents the package loaded from the specified file.
            </returns>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If there is a problem reading the package; or if the package does not contain exported data.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.BacPackage.Load(System.IO.Stream,Microsoft.SqlServer.Dac.DacSchemaModelStorageType)">
            <summary>
            Load a package from the specified <see cref="T:System.IO.Stream"/>.
            </summary>
            <param name="stream">
            Stream from which to read the package.
            </param>
            <param name="modelStorageType">
            Backing storage type for package instance.
            </param>
            <returns>
            <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> instance that represents the package loaded from the specified file.
            </returns>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If there is a problem reading the package; or if the package does not contain exported data.
            </exception>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.IUniversalAuthProvider">
            <summary>
            Implement this interface to create a string access token. This access token will be used to set 
            the <see cref="P:System.Data.SqlClient.SqlConnection.AccessToken"/> for any SqlConnection instances created when connecting to a database.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.IUniversalAuthProvider.GetValidAccessToken">
            <summary>
            Returns an valid access token.
            </summary>
            <returns>The access token to be used to set the AccessToken property of the SqlConnection.</returns>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.PublishResult">
            <summary>
            Contains the results of a publish or script operation. This will contain at least one of the <see cref="P:Microsoft.SqlServer.Dac.PublishResult.DeploymentReport"/>
            or <see cref="P:Microsoft.SqlServer.Dac.PublishResult.DatabaseScript"/> properties depending on what was requested in the <see cref="T:Microsoft.SqlServer.Dac.PublishOptions"/>
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.PublishResult.#ctor(System.String,System.String,System.String)">
            <summary>
            Constructs a <see cref="T:Microsoft.SqlServer.Dac.PublishResult"/> with the specified contents
            </summary>
            <param name="deployReport">set as the <see cref="P:Microsoft.SqlServer.Dac.PublishResult.DeploymentReport"/> to return</param>
            <param name="dbScript">set as the <see cref="P:Microsoft.SqlServer.Dac.PublishResult.DatabaseScript"/> to return</param>
            <param name="masterScript">set as the <see cref="P:Microsoft.SqlServer.Dac.PublishResult.MasterDbScript"/> to return</param>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.PublishResult.DeploymentReport">
            <summary>
            A Deployment Report, if one was requested.
            This report is a high-level summary of actions being performed during deployment.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.PublishResult.DatabaseScript">
            <summary>
            The DB-level deployment script, if one was requested.
            This script contains all operations that must be done against the database during deployment.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.PublishResult.MasterDbScript">
            <summary>
            The master database-level deployment script, if one was requested.
            This script is only created if Azure SQL DB is the target as USE statements are not supported on that platform.
            It contains all operations that must be done against the master database, for instance Create Database statements
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.PublishOptions">
            <summary>
            Configures options for what will be reported when performing certain operations from <see cref="T:Microsoft.SqlServer.Dac.DacServices"/>,
            in particular whether a DeployReport and/or DeployScript will be generated
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.PublishOptions.#ctor">
            <summary>
            Configures options for what will be reported when performing certain operations from <see cref="T:Microsoft.SqlServer.Dac.DacServices"/>,
            in particular whether a DeployReport and/or DeployScript will be generated
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.PublishOptions.GenerateDeploymentScript">
            <summary>
            Sets whether Deployment Script(s) should be generated during deploy.
            If true, a script to update the database will be generated, and a script to
            update Master may also be generated if the target is an Azure SQL DB and this
            database has not yet been created.
            </summary>
            <value>Defaults to true</value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.PublishOptions.GenerateDeploymentReport">
            <summary>
            Sets whether a Deployment Report should be generated during deploy.
            This report is a high-level summary of actions being performed during deployment.
            </summary>
            <value>Defaults to false</value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.PublishOptions.DatabaseScriptPath">
            <summary>
            Optional path to write the DB-level deployment script, if <see cref="P:Microsoft.SqlServer.Dac.PublishOptions.GenerateDeploymentScript"/> is true.
            This script contains all operations that must be done against the database during deployment.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.PublishOptions.MasterDbScriptPath">
            <summary>
            Optional path to write the master database-level deployment script, if <see cref="P:Microsoft.SqlServer.Dac.PublishOptions.GenerateDeploymentScript"/> is true.
            This script is only created if Azure SQL DB is the target as USE statements are not supported on that platform.
            It contains all operations that must be done against the master database, for instance Create Database statements
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.PublishOptions.DeployOptions">
            <summary>
            Optional <see cref="T:Microsoft.SqlServer.Dac.DacDeployOptions"/> to configure a range of deployment options. 
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.PublishOptions.CancelToken">
            <summary>
            Nullable <see cref="T:System.Threading.CancellationToken"/> to allow the caller to cancel a deployment
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.KeyVault.DacKeyVaultService">
            <summary>
            Provides a service for discovering and configuring a <see cref="T:Microsoft.SqlServer.Dac.KeyVault.KeyVaultAuthenticator"/> to handle key vault access requests.
            These requests will occur during deployment if an encrypted table is being altered. It also supports initialization of general 
            key vault support in an application
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.KeyVault.DacKeyVaultService.SetExtensionProperties(Microsoft.SqlServer.Dac.Extensibility.ExtensionProperties)">
            <summary>
            Internal for testing purposes only
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.KeyVault.DacKeyVaultService.SetDependencyManager(Microsoft.SqlServer.Dac.Extensibility.IDependencyManager)">
            <summary>
            Internal for testing purposes only.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.KeyVault.DacKeyVaultService.UpdateAuthInfo(Microsoft.SqlServer.Dac.KeyVault.KeyVaultAuthInfoBase)">
            <summary>
            Passes a <see cref="T:Microsoft.SqlServer.Dac.KeyVault.KeyVaultAuthInfoBase"/> object to the <see cref="T:Microsoft.SqlServer.Dac.KeyVault.KeyVaultAuthenticator"/>, to support scenarios
            where a user changes authentication method, for instance by logging into a different Azure account
            </summary>
            <param name="authInfo"></param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.KeyVault.DacKeyVaultService.InitializeAuthenticator">
            <summary>
            Gives the authenticator the opportunity to register with the core AlwaysEncrypted callstack. This is important
            in scenarios where Interactive authentication is being used and this is the global authenticator to be used in the application
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.KeyVault.DacKeyVaultService.Validate(System.Collections.Generic.IList{System.String},System.Threading.CancellationToken)">
            <summary>
            Callback function to validate the presence of AKV token for Column Master Key access
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.KeyVault.DacKeyVaultService.Instance">
            <summary>
            Singleton instance object for the <see cref="T:Microsoft.SqlServer.Dac.KeyVault.DacKeyVaultService"/>. 
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.KeyVault.DacKeyVaultService.DependencyManager">
            <summary>
            Get DependencyManager
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.KeyVault.DacKeyVaultService.Trace">
            <summary>
            Trace object to use for tracing
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.KeyVault.DacKeyVaultService.BlockifMissingAuthentication">
            <summary>
            Gives a way to set early failure if Azure Key Vault Provider is not setup.
            When this is true, deployment fails to start if there is no Provider defined
            contrary to default mode which lets the deployment go through and fails only when key is actually used.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.KeyVault.ClientAndSecretAuthInfo">
            <summary>
            Authentication info for client ID and Secret, which is commonly supported in command line scenarios.
            This can be obtained by using the Azure SDK to log into Azure and obtain this information.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.KeyVault.KeyVaultAuthInfoBase">
            <summary>
            Data class to define authentication info that can be passed to a <see cref="T:Microsoft.SqlServer.Dac.KeyVault.KeyVaultAuthenticator"/> when
            configuring 
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.KeyVault.ClientAndSecretAuthInfo.#ctor(System.String,System.String)">
            <summary>
            Constructor
            </summary>
            <param name="clientId">Must not be null or whitespace</param>
            <param name="secret">Must not be null</param>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.KeyVault.ClientAndSecretAuthInfo.ClientId">
            <summary>
            Client ID used to log in to Azure
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.KeyVault.ClientAndSecretAuthInfo.Secret">
            <summary>
            Secret that verifies the authentication to Azure
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.KeyVault.KeyVaultAuthenticator">
            <summary>
            Base class for any Azure Key Vault authentication provider. This is responsible for logging into Azure, obtaining
            access keys and returning to the caller. 
            Notes: Only one provider is allowed per process, with the first to register being the successful provider. In order to
            support <see cref="T:Microsoft.SqlServer.Dac.DacServices"/> and related APIs being used in multiple UI tools and the SqlPackage command line the
            binding to Azure Authentication DLLs is loosely coupled to the core Dac Framework using MEF. A default provider is available
            for use in SqlPackage and other command line scenarios, supporting basic credential based authentication. This can be 
            overridden in order to integrate with custom authentication pipelines or into your UI application by extending this method
            and providing an <see cref="T:Microsoft.SqlServer.Dac.Extensibility.ExportableAttribute"/> with typeof(KeyVaultAuthenticator), a unique ID and high priority
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.KeyVault.KeyVaultAuthenticator.GetToken(System.String,System.String,System.String)">
            <summary>
            Azure Key Vault authentication callback
            </summary>
            <param name="authority"></param>
            <param name="resource"></param>
            <param name="scope"></param>
            <returns></returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.KeyVault.KeyVaultAuthenticator.UpdateAuthInfo(Microsoft.SqlServer.Dac.KeyVault.KeyVaultAuthInfoBase)">
            <summary>
            Passes a <see cref="T:Microsoft.SqlServer.Dac.KeyVault.KeyVaultAuthInfoBase"/> object to the KeyVaultAuthenticator, to support scenarios
            where a user changes authentication method, for instance by logging into a different Azure account
            </summary>
            <param name="authInfo"></param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.KeyVault.KeyVaultAuthenticator.InitializeAuthenticator">
            <summary>
            Gives the authenticator the opportunity to register with the core AlwaysEncrypted callstack. This is important
            in scenarios where Interactive authentication is being used and this is the global authenticator to be used in the application
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.KeyVault.KeyVaultAuthenticator.Validate(System.Collections.Generic.IList{System.String},System.Threading.CancellationToken)">
            <summary>
            Called before Dac operations that may cause data movement against a table with encrypted columns will occur,
            for instance deployment of a dacpac
            </summary>
            <param name="keyVaultUrls">
            A list of URLs representing every Key Vault referenced by column master keys, where the key is involved in a data motion
            operation. 
            </param>
            <param name="cancelToken"></param>
            <returns></returns>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.KeyVault.KeyVaultValidationResult">
            <summary>
            Result returned from a key vault validator. Indicates success or failure, and any 
            relevant error messages
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.KeyVault.KeyVaultValidationResult.#ctor">
            <summary>
            Default constructor
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.KeyVault.KeyVaultValidationResult.AddError(System.String)">
            <summary>
            Adds an error to the list of Validation errors
            </summary>
            <param name="message"></param>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.KeyVault.KeyVaultValidationResult.IsValid">
            <summary>
            Was validation successful?
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.KeyVault.KeyVaultValidationResult.ValidationErrors">
            <summary>
            Errors that caused validation to fail, if any
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.NestedDeploymentPropertyAttribute">
            <summary>
            This class supports the product infrastructure and is not intended to be used directly 
            from your code.
            Indicates that a deployment property should be expanded for commandline property overriding
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.NestedDeploymentPropertyAttribute.#ctor">
            <summary>
            Default Constructor
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.DacExportOptions">
            <summary>
            Defines options that affect the behavior of package export from a database.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacExportOptions.#ctor">
            <summary>
            Construct a new instance of the <see cref="T:Microsoft.SqlServer.Dac.DacExportOptions"/> class.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacExportOptions.CommandTimeout">
            <summary>
            Specifies the command timeout in seconds when executing queries against SQLServer.
            </summary>
            <value>
            The command timeout in seconds.
            Default is 60
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacExportOptions.Storage">
            <summary>
            Get the type of backing storage for the schema model used during extraction.
            </summary>
            <value>
            Enumeration value that specifies the type of backing storage for the schema model used during extraction.
            Default value is File.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacExportOptions.TargetEngineVersion">
            <summary>
            Specifies what the target engine version is expected to be. 
            This is used during export to define the allowed engine version to validate against
            and whether the features of the database match the capabilities of that engine version.
            </summary>
            <value>
            The latest version of Azure contains support for additional capabilities. When this value is
            <see cref="F:Microsoft.SqlServer.Dac.EngineVersion.Default"/> or <see cref="F:Microsoft.SqlServer.Dac.EngineVersion.V11"/>,
            the export operation will fail if the target database is found to contain any objects that
            require a newer engine version.
            
            The default value for this option is <see cref="F:Microsoft.SqlServer.Dac.EngineVersion.Latest"/>, which means that 
            validation will ensure that the export operation will only fail if the target database is 
            found to contain any objects that are not supported in the latest engine version.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacExportOptions.VerifyFullTextDocumentTypesSupported">
            <summary>
            Get or set boolean that specifies whether the supported full-text document types for Microsoft Azure SQL Database v12 should be verified.
            </summary>
            <value>
            True if the package should be verified; otherwise false.
            Default value is false.
            </value> 
        </member>
        <member name="T:Microsoft.SqlServer.Dac.DacImportOptions">
            <summary>
            Defines options that affect the behavior of package import to a database.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacImportOptions.#ctor">
            <summary>
            Construct a new instance of the <see cref="T:Microsoft.SqlServer.Dac.DacImportOptions"/> class.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacImportOptions.ImportContributors">
            <summary>
            Specifies additional deployment contributors which should run.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacImportOptions.ImportContributorArguments">
            <summary>
            Specifies additional deployment contributor arguments.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacImportOptions.DatabaseSpecification">
            <summary>
            Defines optional parameters specific to a Microsoft Azure SQL Database.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacImportOptions.CommandTimeout">
            <summary>
            Specifies the command timeout in seconds when executing queries against SQLServer.
            </summary>
            <value>
            The command timeout in seconds.
            Default is 60
            </value>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.DacOperationProgressMessage">
            <summary>
             Progress message associated with the overal progress import or export operation.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.DacMessage">
            <summary>
            Data associated with an executing operation to report status updates or errors.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacMessage.ToString">
            <summary>
            Return a string that represents the current object.
            </summary>
            <returns>
            String that represents the current object.
            </returns>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacMessage.MessageType">
            <summary>
            Get the type of the event message.
            </summary>
            <value>
            Enumeration value that specifies the type of the event message.
            </value>
            <seealso cref="P:Microsoft.SqlServer.Dac.DacMessage.Message"/>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacMessage.Number">
            <summary>
            Get numeric value associated with the event message.
            </summary>
            <value>
            Numeric value associated with the message.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacMessage.Prefix">
            <summary>
            Get string prefix associated with the source of the event message.
            </summary>
            <value>
            String prefix associated with the source of the event message.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacMessage.Message">
            <summary>
            Get friendly message for the current status of an operation.
            </summary>
            <value>
            String message for the current status of an operation.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacOperationProgressMessage.Progress">
            <summary>
            Current progress in percent complete.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.DacDataProgressMessage">
            <summary>
             Data associated with an executing data operation to report status updates or errors related to progress.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDataProgressMessage.Progress">
            <summary>
            Current progress in percent complete.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDataProgressMessage.SchemaName">
            <summary>
            The schema name of the table whose progress is being reported.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDataProgressMessage.TableName">
            <summary>
            The name of the table whose progress is being reported.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.DacDeployOptions">
            <summary>
            Defines options that affect the behavior of package deployment to a database.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacDeployOptions.#ctor">
            <summary>
            Create a new instance of the <see cref="T:Microsoft.SqlServer.Dac.DacDeployOptions"/> class
            with default options.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacDeployOptions.EnableDnrOrdering(System.Boolean)">
            <summary>
            Interal mechanism to control deployment DNR ordering behavior
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacDeployOptions.#ctor(Microsoft.Data.Tools.Schema.Sql.Deployment.SqlDeploymentOptions)">
            <summary>
            Create a new instance of the <see cref="T:Microsoft.SqlServer.Dac.DacDeployOptions"/> class
            with specified options.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacDeployOptions.#ctor(Microsoft.Data.Tools.Schema.Sql.Deployment.SqlDeploymentOptions,System.Boolean)">
            <summary>
            Create a new instance of the <see cref="T:Microsoft.SqlServer.Dac.DacDeployOptions"/> class
            with specified options.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacDeployOptions.InitializeDefaults(Microsoft.Data.Tools.Schema.Sql.Deployment.SqlDeploymentOptions)">
            <summary>
            Initialize the expected defaults for this API.
            </summary>
            <remarks>
            Only defined where default values differ from the core defaults.
            When new options are added to SqlDeploymentOptions or a default value changed,
            all clients must determine if they must override the default.
            Redundantly specifying existing defaults doesn't reduce need for this
            process when adding options or changing defaults.  Unit tests should
            be employed to validating expectations for each deployment client
            that overrides default options (e.g. power-buffer, schema-compare, this api).
            When removing or renaming an option, the compiler will complain (good).
            </remarks>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.AdditionalDeploymentContributors">
            <summary>
            Specifies additional deployment contributors which should run - in addition
            to those specified in the dacpac.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.AdditionalDeploymentContributorArguments">
            <summary>
            Specifies additional deployment contributor arguments in addition to those already listed
            in the dacpac.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.AllowDropBlockingAssemblies">
            <summary>
            Get or set boolean that specifies whether CLR deployment will cause blocking assemblies to be dropped.
            </summary>
            <value>
            True to drop blocking assemblies during CLR deployment; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.AllowIncompatiblePlatform">
            <summary>
            Get or set boolean that specifies whether deployment will block due to platform compatibility.
            </summary>
            <value>
            True to block deployment to incompatible platforms; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.BackupDatabaseBeforeChanges">
            <summary>
            Get or set boolean that specifies whether a database backup will be performed before proceeding 
            with the actual deployment actions.
            </summary>
            <value>
            True to perform a database backup prior to deployment; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.BlockOnPossibleDataLoss">
            <summary>
            Get or set boolean that specifies whether deployment should stop if the operation could cause data loss.
            </summary>
            <value>
            True to stop deployment if possible data loss if detected; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.BlockWhenDriftDetected">
            <summary>
            Get or set boolean that specifies whether the system will check for differences between the 
            present state of the database and the registered state of the database and block deployment 
            if changes are detected.  Even if this option is set to true, drift detection will only occur 
            on a database if it was previously deployed with the <see cref="P:Microsoft.SqlServer.Dac.DacDeployOptions.RegisterDataTierApplication"/> option enabled.
            </summary>
            <value>
            True to error is drift is detected; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.CommandTimeout">
            <summary>
            Specifies the command timeout in seconds when executing queries against SQLServer.
            </summary>
            <value>
            The command timeout in seconds.
            Default is 60
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.CommentOutSetVarDeclarations">
            <summary>
            Get or set boolean that specifies whether the declaration of SQLCMD variables are commented 
            out in the script header.
            </summary>
            <value>
            True to comment out these declarations; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.CompareUsingTargetCollation">
            <summary>
            Get or set boolean that specifies whether the target collation will be used for identifier 
            comparison.
            </summary>
            <value>
            False to use the source collation; otherwise, true to use the target collation.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.CreateNewDatabase">
            <summary>
            Get or set boolean that specifies whether the existing database will be dropped
            and a new database created before proceeding with the actual deployment actions.
            Acquires single-user mode before dropping the existing database.
            </summary>
            <value>
            True to drop and re-create the database; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.DatabaseSpecification">
            <summary>
            Defines optional parameters specific to a Microsoft Azure SQL Database.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.DeployDatabaseInSingleUserMode">
            <summary>
            Get or set boolean that specifies whether the system will acquire single-user mode on the target
            database during the duration of the deployment operation.
            </summary>
            <value>
            True to acquire single-user mode during deployment; otherwise, false.
            Default is false.
            </value>
            <remarks>
            The database will be returned to multi-user mode after all changes are applied.
            Database may remain in single-user mode if an error occurs during execution.
            </remarks>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.DisableAndReenableDdlTriggers">
            <summary>
            Get or set boolean that specifies if all DDL triggers will be disabled for the duration of the 
            deployment operation and then re-enabled after all changes are applied.  
            </summary>
            <value>
            True to disable DDL triggers during deployment; otherwise, false.
            Default is true.
            </value>
            <remarks>
            Triggers may remain disabled if an error occurs during execution.
            </remarks>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.DoNotAlterChangeDataCaptureObjects">
            <summary>
            Get or set boolean that specifies whether items configured for Change Data Capture (CDC)
            should be altered during deployment.  
            </summary>
            <value>
            True to not alter objects configured for CDC; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.DoNotAlterReplicatedObjects">
            <summary>
            Get or set boolean that specifies whether items configured for Replication
            should be altered during deployment.  
            </summary>
            <value>
            True to not alter objects configured for Replication; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.DoNotDropObjectTypes">
            <summary>
            Get or set a collection of object types that will not be dropped from the target when
            no corresponding object exists in the source. Note that dropping and recreating objects
            of the specified type may still be necessary due to dependencies from other objects.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.DropConstraintsNotInSource">
            <summary>
            Get or set boolean that specifies whether to drop all constraints that do not
            exist in the source model.  
            </summary>
            <value>
            True to drop constraints not in the source model; otherwise, false.
            Default is true.
            </value>
            <remarks>
            This applies to check, default, foreign key, primary key, and unique constraints.
            </remarks>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.DropDmlTriggersNotInSource">
            <summary>
            Get or set boolean that specifies whether to drop all DML triggers that do not
            exist in the source model.
            </summary>
            <value>
            True to drop DML triggers not in the source model; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.DropExtendedPropertiesNotInSource">
            <summary>
            Get or set boolean that specifies whether to drop all extended properties that do 
            not exist in the source model.
            </summary>
            <value>
            True to drop extended properties not in the source model; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.DropIndexesNotInSource">
            <summary>
            Get or set boolean that specifies whether to drop all indexes that do not
            exist in the source model.
            </summary>
            <value>
            True to drop indexes not in the source model; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.DropObjectsNotInSource">
            <summary>
            Get or set boolean that specifies whether objects that exist in the target but not source should be dropped during deployment.
            </summary>
            <value>
            True if objects that exist in the target but not source should be dropped; otherwise false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.DropPermissionsNotInSource">
            <summary>
            Get or set boolean that specifies whether to drop all permissions that do not
            exist in the source model.
            </summary>
            <value>
            True to drop permissions not in the source model; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.DropRoleMembersNotInSource">
            <summary>
            Get or set boolean that specifies whether to drop all role memberships that do not
            exist in the source model.
            </summary>
            <value>
            True to drop role memberships not in the source model; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.DropStatisticsNotInSource">
            <summary>
            Get or set boolean that specifies whether to drop all role memberships that do not
            exist in the source model.
            </summary>
            <value>
            True to drop role memberships not in the source model; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.GenerateSmartDefaults">
            <summary>
            Get or set boolean that specifies whether default values should be generated to populate NULL columns that are constrained to NOT NULL values.
            </summary>
            <value>
            True if default values should be generated; otherwise false.
            Default is false.
            </value>
            <remarks>
            This is useful when needing to add a new NOT NULL column to an existing table with data.
            </remarks>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreAnsiNulls">
            <summary>
            Get or set boolean that specifies whether to exclude the ANSI_NULL option from 
            consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in the ANSI_NULL option; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreAuthorizer">
            <summary>
            Get or set boolean that specifies whether to exclude the AUTHORIZATION option from 
            consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in the AUTHORIZATION option; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreColumnCollation">
            <summary>
            Get or set boolean that specifies whether to exclude the collation specifier from 
            consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in the collation specifier; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreColumnOrder">
            <summary>
            Get or set boolean that specifies whether to exclude from consideration 
            the order of columns in tables when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in column order; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreComments">
            <summary>
            Get or set boolean that specifies whether to exclude comments from 
            consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in comments; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreCryptographicProviderFilePath">
            <summary>
            Get or set boolean that specifies whether to exclude the file specification 
            of a cryptographic provider from consideration when comparing the source and 
            target model.
            </summary>
            <value>
            True to ignore differences in a cryptographic provider's  file specification; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreDdlTriggerOrder">
            <summary>
            Get or set boolean that specifies whether to exclude DDL trigger order from 
            consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in DDL trigger order; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreDdlTriggerState">
            <summary>
            Get or set boolean that specifies whether to exclude DDL trigger state from 
            consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in DDL trigger state; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreDefaultSchema">
            <summary>
            Get or set boolean that specifies whether to exclude the DEFAULT_SCHEMA option from 
            consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in the DEFAULT_SCHEMA options; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreDmlTriggerOrder">
            <summary>
            Get or set boolean that specifies whether to exclude DML trigger order from 
            consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in DDL trigger order; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreDmlTriggerState">
            <summary>
            Get or set boolean that specifies whether to exclude DML trigger state from 
            consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in DML trigger state; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreExtendedProperties">
            <summary>
            Get or set boolean that specifies whether to exclude all extended properties from 
            consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in extended properties; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreFileAndLogFilePath">
            <summary>
            Get or set boolean that specifies whether to exclude the FILENAME option of 
            FILE objects from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in the FILENAME option of FILE objects; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreFilegroupPlacement">
            <summary>
            Get or set boolean that specifies whether to exclude the filegroup specifier 
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in the filegroup specifier; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreFileSize">
            <summary>
            Get or set boolean that specifies whether to exclude the SIZE option of FILE objects 
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in the SIZE option of FILE objects; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreFillFactor">
            <summary>
            Get or set boolean that specifies whether to exclude the FILLFACTOR option
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in the FILLFACTOR option; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreFullTextCatalogFilePath">
            <summary>
            Get or set boolean that specifies whether to exclude the path specification of 
            FULLTEXT CATALOG objects from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in the path specification of FULLTEXT CATALOG objects; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreIdentitySeed">
            <summary>
            Get or set boolean that specifies whether to exclude the seed value of IDENTITY columns 
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in the seed value of IDENTITY columns; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreIncrement">
            <summary>
            Get or set boolean that specifies whether to exclude the increment value of IDENTITY columns 
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in the increment value of IDENTITY columns; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreIndexOptions">
            <summary>
            Get or set boolean that specifies whether to exclude differences in index options 
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in index options; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreIndexPadding">
            <summary>
            Get or set boolean that specifies whether to exclude the PAD_INDEX option 
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in the PAD_INDEX option; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreKeywordCasing">
            <summary>
            Get or set boolean that specifies whether to exclude difference in the casing of keywords 
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in the casing of keywords; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreLockHintsOnIndexes">
            <summary>
            Get or set boolean that specifies whether to exclude the ALLOW_ROW_LOCKS and 
            ALLOW_PAGE_LOGKS options from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore the ALLOW_ROW_LOCKS and ALLOW_PAGE_LOGKS options; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreLoginSids">
            <summary>
            Get or set boolean that specifies whether to exclude the SID option of the LOGIN object 
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore the SID option of the LOGIN object; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.ExcludeObjectTypes">
            <summary>
            Get or set a collection of object types to exclude from consideration when
            comparing the source and target model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreNotForReplication">
            <summary>
            Get or set boolean that specifies whether to exclude the NOT FOR REPLICATION option 
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore the NOT FOR REPLICATION option; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreObjectPlacementOnPartitionScheme">
            <summary>
            Get or set boolean that specifies whether to exclude the partition scheme object
            from consideration when comparing the source and target model for the following
            objects: Table, Index, Unique Key, Primary Key, and Queue.
            </summary>
            <value>
            True to ignore partition schemes; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnorePartitionSchemes">
            <summary>
            Get or set boolean that specifies whether to exclude the parameter type and 
            boundary VALUES of a PARTITION FUNCTION from consideration when comparing the 
            source and target model.  Also excludes FILEGROUP and partition function of a 
            PARTITION SCHEMA from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore aspects of partition functions and schemes; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnorePermissions">
            <summary>
            Get or set boolean that specifies whether to exclude all permission statements 
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore all permission statements; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreQuotedIdentifiers">
            <summary>
            Get or set boolean that specifies whether to exclude the QUOTED_IDENTIFIER option 
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore the QUOTED_IDENTIFIER option; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreRoleMembership">
            <summary>
            Get or set boolean that specifies whether to exclude all ROLE MEMBERSHIP objects 
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore ROLE MEMBERSHIP objects; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreRouteLifetime">
            <summary>
            Get or set boolean that specifies whether to exclude the LIFETIME option of ROUTE objects 
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore the LIFETIME option of ROUTE objects; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreSemicolonBetweenStatements">
            <summary>
            Get or set boolean that specifies whether to exclude the existence or absence of semi-colons 
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore semi-colons; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreTableOptions">
            <summary>
            Get or set boolean that specifies whether the options on the target table are updated 
            to match the source table.
            </summary>
            <value>
            True to ignore difference in table options and not update the target table; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreUserSettingsObjects">
            <summary>
            Get or set boolean that specifies whether to exclude user settings 
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences in user settings; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreWhitespace">
            <summary>
            Get or set boolean that specifies whether to exclude whitespace 
            from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore differences whitespace; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreWithNocheckOnCheckConstraints">
            <summary>
            Get or set boolean that specifies whether to exclude the CHECK|NO CHECK option of a CHECK 
            constraint object from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore the CHECK|NO CHECK option of a CHECK constraint object; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IgnoreWithNocheckOnForeignKeys">
            <summary>
            Get or set boolean that specifies whether to exclude the CHECK|NO CHECK option of a FOREIGN KEY  
            constraint object from consideration when comparing the source and target model.
            </summary>
            <value>
            True to ignore the CHECK|NO CHECK option of a FOREIGN KEY constraint object; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.AllowUnsafeRowLevelSecurityDataMovement">
            <summary>
            Get or set boolean that specifies whether to ignore blocking data motion on RLS enabled tables
            </summary>
            <value>
            True to ignore block on data motion when Row level security is enabled on a table
            Default is false i.e. data motion is blocked with RLS by default.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IncludeCompositeObjects">
            <summary>
            Get or set boolean that specifies whether to include referenced, external elements that also 
            compose the source model and then update the target database in a single deployment operation.
            </summary>
            <value>
            True to include composite objects; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.IncludeTransactionalScripts">
            <summary>
            Get or set boolean that specifies whether to use transations during the deployment operation 
            and commit the transaction after all changes are successfully applied.
            </summary>
            <value>
            True to use transactions during deployment; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.NoAlterStatementsToChangeClrTypes">
            <summary>
            Get or set boolean that specifies whether to force a change to CLR assemblies by dropping and recreating them.
            </summary>
            <value>
            True if CLR assemblies should be dropped; otherwise false to allow ALTER statements to change CLR assemblies.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.PopulateFilesOnFileGroups">
            <summary>
            Get or set boolean that specifies whether files are supplied for filegroups defined in the deployment source.
            </summary>
            <value>
            True to specify files for filegroups; otherwise false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.RegisterDataTierApplication">
            <summary>
            Get or set boolean that specifies whether the database will be registered as a Data-Tier Application.  
            If the target database is already a registered Data-Tier Application, then the registration will be updated.
            </summary>
            <value>
            True to register the database as a Data-Tier Application; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.RunDeploymentPlanExecutors">
            <summary>
            Specifies whether DeploymentPlanExecutor contributors should be run when other operations are executed.
            Default is false.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.ScriptDatabaseCollation">
            <summary>
            Get or set boolean that specifies whether the target database should be altered to match the 
            source model's collation.
            </summary>
            <value>
            True to alter the target database's collation; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.ScriptDatabaseCompatibility">
            <summary>
            Get or set boolean that specifies whether the target database should be altered to match the 
            source model's compatibility level.
            </summary>
            <value>
            True to alter the target database's compatibility level; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.ScriptDatabaseOptions">
            <summary>
            Get or set boolean that specifies whether the database options in the target database should 
            be updated to match the source model.
            </summary>
            <value>
            True to alter the target database's options; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.ScriptDeployStateChecks">
            <summary>
            Get or set boolean that specifies whether the target database should be checked to ensure that 
            it exists, is online and can be updated.
            </summary>
            <value>
            True to perform state checks on the target database; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.ScriptFileSize">
            <summary>
            Get or set boolean that specifies whether a file size is specified when adding files to file groups.
            </summary>
            <value>
            True to specify a file size when adding files to file groups; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.ScriptNewConstraintValidation">
            <summary>
            Get or set boolean that specifies whether constraints are validated after all changes are applied.
            </summary>
            <value>
            True to validate check constraints; otherwise, false.
            Default is true.
            </value>
            <remarks>
            Constraints are always added with NOCHECK option; as a result their validation is skipped during creation.
            </remarks>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.ScriptRefreshModule">
            <summary>
            Get or set boolean that specifies whether referencing procedures are refreshed when referenced objects are updated.
            </summary>
            <value>
            True to refresh referencing procedures; otherwise false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.TreatVerificationErrorsAsWarnings">
            <summary>
            Get or set boolean that specifies whether to treat errors that occur during publish verification as warnings. 
            The check is performed against the generated deployment plan before the plan is executed against the target database. 
            Plan verification detects problems, such as the loss of target-only objects (for example, indexes), that must be 
            dropped to make a change. Verification also detects situations where dependencies (such as tables or views) exist 
            because of a reference to a composite project, but do not exist in the target database. You might choose to treat 
            verification errors as warnings to get a complete list of issues instead of allowing the publish 
            action to stop when the first error occurs.
            </summary>
            <value>
            True to treat errors as warnings; otherwise, false.
            Default is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.UnmodifiableObjectWarnings">
            <summary>
            Get or set boolean that specifies whether warnings should be generated when differences are found 
            in objects that cannot be modified, for example, if the file size or file paths were different for a file.
            </summary>
            <value>
            True to generate warnings; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.VerifyCollationCompatibility">
            <summary>
            Get or set boolean that specifies whether deployment will verify that the collation specified in the 
            source model is compatible with the collation specified in the target model.
            </summary>
            <value>
            True to continue if errors are generated during plan verification; otherwise, false.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.VerifyDeployment">
            <summary>
            Get or set boolean that specifies whether the plan verification phase is executed or not.
            </summary>
            <value>
            True to perform plan verification; otherwise, false to skip it.
            Default is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacDeployOptions.SqlCommandVariableValues">
            <summary>
            Get dictionary of SQL command variable values, keyed by variable name.
            </summary>
            <value>
            Dictionary of SQL command variable values, keyed by variable name.
            </value>
            <remarks>
            Valid values must be provided for every variable before deployment, or failures may occur during deployment.
            </remarks>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.DacErrorResources">
            <summary>
              A strongly-typed resource class, for looking up localized strings, etc.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ResourceManager">
            <summary>
              Returns the cached ResourceManager instance used by this class.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.Culture">
            <summary>
              Overrides the current thread's CurrentUICulture property for all
              resource lookups using this strongly typed resource class.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.BacpacShouldNotContainDeploymentScripts">
            <summary>
              Looks up a localized string similar to BACPAC should not contain either pre-deployment or post-deployment scripts..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.CannotWriteToPackage">
            <summary>
              Looks up a localized string similar to Cannot apply updates to a package loaded for read only..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ConflictingExtendedPropertyDropSetting">
            <summary>
              Looks up a localized string similar to DropExtendedPropertiesNotInSource conflicts with the selected DoNotDropObjectType ExtendedProperties.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ConflictingPermissionsDropSetting">
            <summary>
              Looks up a localized string similar to DropPermissionsNotInSource conflicts with the selected DoNotDropObjectType Permissions.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ConflictingRoleMembershipDropSetting">
            <summary>
              Looks up a localized string similar to DropRoleMembersNotInSource conflicts with the selected DoNotDropObjectType RoleMembership.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.DacMessageFormat">
            <summary>
              Looks up a localized string similar to {0} {1}{2}: {3}.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ElementValidationFailureMessage">
            <summary>
              Looks up a localized string similar to Error validating element {0}: {1}.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorAttemptToUpgradeExistingDatabase">
            <summary>
              Looks up a localized string similar to Cannot deploy to existing database when upgrading has been disabled..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorBacpacValidationFailed">
            <summary>
              Looks up a localized string similar to Validation of the schema model for data package failed. Check the inner exception for details..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorCannotOpenBacPackageWithoutData">
            <summary>
              Looks up a localized string similar to Cannot create a BACPAC from a file that does not contain exported data..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorCannotOpenDacPackageWithData">
            <summary>
              Looks up a localized string similar to Cannot create a DAC package from a file that contains exported data..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorConnectingToDatabase">
            <summary>
              Looks up a localized string similar to Could not connect to database server..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorContainingUnsupportedFullTextDocumentTypesForAzureV12">
            <summary>
              Looks up a localized string similar to Indexing for document type {0} is not supported on {1}..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorCouldNotDetermineExistenceOfDatabase">
            <summary>
              Looks up a localized string similar to Unable to determine the existence of database &apos;{0}&apos;. You must have a user with the same password in master or database &apos;{0}&apos;..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorCouldNotDetermineProviderFromPackage">
            <summary>
              Looks up a localized string similar to Could not determine SQL platform from package..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorCouldNotDetermineServerVersion">
            <summary>
              Looks up a localized string similar to Database source is not a supported version of SQL Server {0}: {1}..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorDataTableNotFound">
            <summary>
              Looks up a localized string similar to Table with schema &quot;{0}&quot; and name &quot;{1}&quot; was specified for data import or export, but does not exist in the database schema..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorDeletingFile">
            <summary>
              Looks up a localized string similar to An error occurred when deleting file &apos;{0}&apos;.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorDeployingPackage">
            <summary>
              Looks up a localized string similar to Could not deploy package..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorExportingPackage">
            <summary>
              Looks up a localized string similar to Could not export schema and data from database..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorExtractAllTableDataTrueAndTablesNotNull">
            <summary>
              Looks up a localized string similar to Argument &apos;ExtractAllTableData&apos; has an invalid value:&apos;true&apos;. &apos;ExtractAllTableData&apos; cannot be &apos;true&apos; because individual user tables are also specified using the &apos;TableData&apos; argument. Set &apos;ExtractAllTableData&apos; to &apos;false&apos; to extract data from the specified user tables..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorExtractingFromFileTable">
            <summary>
              Looks up a localized string similar to Extracting data from FileTable {0}.{1} is not supported..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorExtractingPackageFromDatabase">
            <summary>
              Looks up a localized string similar to Could not extract package from specified database..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorForeignKeyCannotReferenceTable">
            <summary>
              Looks up a localized string similar to Foreign key {0} cannot reference table {1} when used as part of a data package..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorFoundUnsupportedElementsForDataPackage">
            <summary>
              Looks up a localized string similar to One or more unsupported elements were found in the schema used as part of a data package..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorGeneratingDeploymentReport">
            <summary>
              Looks up a localized string similar to Could not generate deployment report..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorGeneratingDeploymentResults">
            <summary>
              Looks up a localized string similar to Could not generate deployment results..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorGeneratingDeploymentScript">
            <summary>
              Looks up a localized string similar to Could not generate deployment script..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorGeneratingDriftReport">
            <summary>
              Looks up a localized string similar to Could not generate drift report..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorImportingPackage">
            <summary>
              Looks up a localized string similar to Could not import package..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorInvalidPropertyName">
            <summary>
              Looks up a localized string similar to The property {0} specified is not present in the resource {1}..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorLoadingConstraints">
            <summary>
              Looks up a localized string similar to Error loading constraint information from target database.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorLoadingPackage">
            <summary>
              Looks up a localized string similar to Could not load package..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorLoadingPackageFrom">
            <summary>
              Looks up a localized string similar to Could not load package from &apos;{0}&apos;..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorReadingDacMetadataFromPackage">
            <summary>
              Looks up a localized string similar to Could not load data-tier application metadata from package..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorReadingFromFile">
            <summary>
              Looks up a localized string similar to An error occurred reading the contents of file &apos;{0}&apos;.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorReadingModelFromPackage">
            <summary>
              Looks up a localized string similar to Could not load schema model from package..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorReadingModelHeader">
            <summary>
              Looks up a localized string similar to Could not read schema model header information from package..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorReadingProfile">
            <summary>
              Looks up a localized string similar to Could not read profile properties: {0}.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorReadingProfileProperties">
            <summary>
              Looks up a localized string similar to Could not read profile properties: {0}.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorReadingScriptFromPackage">
            <summary>
              Looks up a localized string similar to Could not load script from package..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorRegisteringApplication">
            <summary>
              Looks up a localized string similar to Could not register DAC information..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorSavingPackage">
            <summary>
              Looks up a localized string similar to Could not save package to file..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorStreamDoesNotSupportReading">
            <summary>
              Looks up a localized string similar to Stream does not support reading..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorStreamDoesNotSupportSeeking">
            <summary>
              Looks up a localized string similar to Stream does not support seeking..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorStreamDoesNotSupportWriting">
            <summary>
              Looks up a localized string similar to Stream does not support writing..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorTranslatingPackage">
            <summary>
              Looks up a localized string similar to Could not translate package..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorUnpacking_FileExists">
            <summary>
              Looks up a localized string similar to Cannot unpack to the same location twice. The file &apos;{0}&apos; already exists in the unpack directory.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorUnpackingPackage">
            <summary>
              Looks up a localized string similar to Could not unpack the package..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorUnregisteringApplication">
            <summary>
              Looks up a localized string similar to Could not unregister DAC information..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorUnsupportedClrElementForDataPackage">
            <summary>
              Looks up a localized string similar to Element {0} is an unsupported CLR element that is not allowed when used as part of a data package..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorUnsupportedContainedUserElementForDataPackage">
            <summary>
              Looks up a localized string similar to Element {0} is a contained user that is not supported when used as a part of a data package..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorUnsupportedElementForDataPackage">
            <summary>
              Looks up a localized string similar to The element {0} is not supported when used as part of a data package (.bacpac file)..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorUnsupportedElementPropertyForDataPackage">
            <summary>
              Looks up a localized string similar to Element {0} has an unsupported property {1} set and is not supported when used as part of a data package..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorUnsupportedFileStreamColumnForDataPackage">
            <summary>
              Looks up a localized string similar to Element {0} is an unsupported FILESTREAM column that is not allowed when used as part of a data package..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorUnsupportedMultiPartNameForSynonym">
            <summary>
              Looks up a localized string similar to {0} is not supported on Microsoft Azure SQL Database as it aliases the external element {1}..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorUnsupportUserDefinedTypeColumnForDataPackage">
            <summary>
              Looks up a localized string similar to Element {0} is an unsupported CLR User-defined type column that is not allowed when used as part of a data package..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorUpdatingDacHistory">
            <summary>
              Looks up a localized string similar to Could not update the dac history table..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorValidationFailed">
            <summary>
              Looks up a localized string similar to Validation of the schema model for data package failed..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ErrorVerifyingDeploymentPlan">
            <summary>
              Looks up a localized string similar to Verification of the deployment plan failed..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ExportedRowsMayBeIncomplete">
            <summary>
              Looks up a localized string similar to The row-level security policies of this database restrict some users&apos; ability to access data in these tables: &apos;{0}&apos;. Please verify that the credentials you&apos;re using have sufficient privileges to access all the data in these tables..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ExportedRowsMayContainSomeMaskedData">
            <summary>
              Looks up a localized string similar to The database permissions may restrict the ability of some users&apos; to see the actual data contained in these tables: &apos;{0}&apos;. Please verify that the credentials you&apos;re using have sufficient privileges to see all data unmasked..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.InvalidArgumentValue">
            <summary>
              Looks up a localized string similar to {0} is not a valid value for {1}..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.InvalidModelHeaderData">
            <summary>
              Looks up a localized string similar to The model header data from the package &apos;{0}&apos; is invalid..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.NoKeyVaultAuthenticatorFound">
            <summary>
              Looks up a localized string similar to Cannot proceed as Key Vault support is not present in the current application. For Key Vault support during deployment, install DacFramework.msi and run SqlPackage.exe from its install location..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.PackagingModelFailed">
            <summary>
              Looks up a localized string similar to Packaging the model failed..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ReferencedPackageDoesnotExist">
            <summary>
              Looks up a localized string similar to The referenced package &apos;{0}&apos; doesn&apos;t exist..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ScriptRequiresOneOfTwoOptions">
            <summary>
              Looks up a localized string similar to At least one of the options {0} or {1} must be enabled..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.TranslatingModelFailed">
            <summary>
              Looks up a localized string similar to Translating the model failed..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.UnknownPackageVersion">
            <summary>
              Looks up a localized string similar to The version of the supplied package, {0}, is incompatible with this version of the DAC Framework..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacErrorResources.ValidatingTheModelAgainstTargetPlatformFailed">
            <summary>
              Looks up a localized string similar to Validating the model against target platform failed..
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.DacExceptionMessage">
            <summary>
            Data associated with an error with exception information.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacExceptionMessage.Exception">
            <summary>
            Get exception associated with the event message.
            </summary>
            <value>
            Exception object associated with the message.
            </value>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.DacAzureEdition">
            <summary>
            List of possible editions for a Microsoft Azure SQL Database
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.DacAzureEdition.Web">
            <summary>
            CREATE DATABASE database_name EDITION = <b>'web'</b>
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.DacAzureEdition.Business">
            <summary>
            CREATE DATABASE database_name EDITION = <b>'business'</b>
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.DacAzureEdition.Basic">
            <summary>
            CREATE DATABASE database_name EDITION = <b>'basic'</b>
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.DacAzureEdition.Standard">
            <summary>
            CREATE DATABASE database_name EDITION = <b>'standard'</b>
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.DacAzureEdition.Premium">
            <summary>
            CREATE DATABASE database_name EDITION = <b>'premium'</b>
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.DacAzureEdition.PremiumRS">
            <summary>
            CREATE DATABASE database_name EDITION = <b>'premiumRS'</b>
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.DacAzureEdition.Default">
            <summary>
            CREATE DATABASE database_name
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.DacAzureDatabaseSpecification">
            <summary>
            Defines optional parameters specific to a Microsoft Azure SQL Database
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacAzureDatabaseSpecification.#ctor">
            <summary>
            Default constructor
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacAzureDatabaseSpecification.Edition">
            <summary>
            Database Edition
            </summary>
            <remarks>
            CREATE DATABASE database_name  [ COLLATE collation_name ]
            {
               ({edition_options} [, ...n]) 
            }
            
            {edition_options} ::= 
            {
               (MAXSIZE = { 100 MB | 500 MB | 1 | 5 | 10 | 20 | 30 ... 150 ... 500 } GB
                |<b>(EDITION = {'web' | 'business' | 'basic' | 'standard' | 'premium' | 'premiumRS'})</b>
                |<b>(SERVICE_OBJECTIVE = { 'shared' | 'basic' | 'S0' | 'S1' | 'S2' | 'S3' | 'P1' | 'P2' | 'P3' | 'P4' | 'P6' | 'P11' | 'P15' | 'PRS1' | 'PRS2' | 'PRS4' | 'PRS6' })</b>
            }
            </remarks>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacAzureDatabaseSpecification.MaximumSize">
            <summary>
            Maximum size, in GB
            </summary>
            <remarks>
            CREATE DATABASE database_name  [ COLLATE collation_name ]
            {
               ({edition_options} [, ...n]) 
            }
            
            {edition_options} ::= 
            {
               <b>(MAXSIZE = { 100 MB | 500 MB | 1 | 5 | 10 | 20 | 30 ... 150 ... 500 } GB</b>
                |(EDITION = {'web' | 'business' | 'basic' | 'standard' | 'premium' | 'premiumRS'})
                |(SERVICE_OBJECTIVE = { 'shared' | 'basic' | 'S0' | 'S1' | 'S2' | 'S3' | 'P1' | 'P2' | 'P3' | 'P4' | 'P6' | 'P11' | 'P15' | 'PRS1' | 'PRS2' | 'PRS4' | 'PRS6' })
            }
            </remarks>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacAzureDatabaseSpecification.ServiceObjective">
            <summary>
            Database Performance Level
            </summary>
            <remarks>
            CREATE DATABASE database_name  [ COLLATE collation_name ]
            {
               ({edition_options} [, ...n]) 
            }
            
            {edition_options} ::= 
            {
               (MAXSIZE = { 100 MB | 500 MB | 1 | 5 | 10 | 20 | 30 ... 150 ... 500 } GB  
                |(EDITION = {'web' | 'business' | 'basic' | 'standard' | 'premium' | 'premiumRS'})
                |<b>(SERVICE_OBJECTIVE = { 'shared' | 'basic' | 'S0' | 'S1' | 'S2' | 'S3' | 'P1' | 'P2' | 'P3' | 'P4' | 'P6' | 'P11' | 'P15' | 'PRS1' | 'PRS2' | 'PRS4' | 'PRS6' })</b>
            }
            </remarks>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.DacExtractOptions">
            <summary>
            Defines options that affect the behavior of package extraction.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacExtractOptions.#ctor">
            <summary>
            Construct a new instance of the <see cref="T:Microsoft.SqlServer.Dac.DacExtractOptions"/> class.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacExtractOptions.CommandTimeout">
            <summary>
            Specifies the command timeout in seconds when executing queries against SQLServer.
            </summary>
            <value>
            The command timeout in seconds.
            Default is 60
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacExtractOptions.ExtractAllTableData">
            <summary>
            Get or set boolean that specifies whether data for all user tables will be extracted.
            </summary>
            <value>
            True if data for all user tables will be extracted; otherwise false.
            Default value is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacExtractOptions.ExtractReferencedServerScopedElements">
            <summary>
            Get or set boolean that specifies whether server-scoped elements referenced by the source should be extracted.
            </summary>
            <value>
            True if references server-scoped elements should be extracted; otherwise false.
            Default value is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacExtractOptions.ExtractApplicationScopedObjectsOnly">
            <summary>
            Get or set boolean that specifies the scope of objects extracted from the source.
            </summary>
            <value>
            True if only application-scoped objects should be extracted;
            otherwise false to extract all object types.
            Default value is true.
            </value>        
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacExtractOptions.IgnoreExtendedProperties">
            <summary>
            Get or set boolean that specifies whether extended properties should be ignored.
            </summary>
            <value>
            True if extended properties will be ignored; otherwise false.
            Default value is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacExtractOptions.IgnorePermissions">
            <summary>
            Get or set boolean that specifies whether permissions should be ignored.
            </summary>
            <value>
            True if permissions will be ignored; otherwise false.
            Default value is true.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacExtractOptions.IgnoreUserLoginMappings">
            <summary>
            Get or set boolean that specifies whether mappings between users and logins should be extracted from the source.
            </summary>
            <value>
            True if user login mappings will be ignored; otherwise false.
            Default value is false.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacExtractOptions.Storage">
            <summary>
            Get the type of backing storage for the schema model used during extraction.
            </summary>
            <value>
            Enumeration value that specifies the type of backing storage for the schema model used during extraction.
            Default value is File.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacExtractOptions.VerifyExtraction">
            <summary>
            Get or set boolean that specifies whether the extracted package should be verified.
            </summary>
            <value>
            True if the package should be verified; otherwise false.
            Default value is false.
            </value> 
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacExtractOptions.ExtractUsageProperties">
            <summary>
            Usage properties include Table.RowCount, Table.IndexSize, Table.DataSize, Table.UsedPages and Table.DataPages.
            When true, these properties are extracted from the database and are accessible in the model.
            </summary>
            <value>
            True if usage properties will be extracted; otherwise false.
            Default value is false.
            </value>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.DacMessageEventArgs">
            <summary>
            Data associated with event raised as an executing operation reports status updates or errors.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacMessageEventArgs.Message">
            <summary>
            Message information associated with this event instance.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.DacMessageType">
            <summary>
            Specifies the type (or severity) of message associated with a given event.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.DacMessageType.Message">
            <summary>
            Informational message.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.DacMessageType.Warning">
            <summary>
            Noncritical problem.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.DacMessageType.Error">
            <summary>
            Serious, possibly fatal issue.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.DacOperationStatus">
            <summary>
            Specifies the state of a service operation.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.DacOperationStatus.Pending">
            <summary>
            Operation has not yet started.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.DacOperationStatus.Running">
            <summary>
            Operation is executing.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.DacOperationStatus.Completed">
            <summary>
            Operation has finished execution.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.DacOperationStatus.Faulted">
            <summary>
            Operation did not complete execution.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.DacOperationStatus.Cancelled">
            <summary>
            Operation was cancelled.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.DacPackage">
            <summary>
            Representation of the artifact that contains the definition of a data-tier application.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacPackage.Dispose">
            <summary>
            Release the resources held by this instance.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacPackage.Unpack(System.String)">
            <summary>
            Place the contents of this package into the directory specified by <paramref name="directoryPath"/>.
            </summary>
            <param name="directoryPath">
            Base directory in which to place the package contents.
            </param>
            <exception cref="T:System.ArgumentException">
            If <paramref name="directoryPath"/> is a null reference or empty string.
            </exception>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If an error occurs while attempting to unpack the package.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacPackage.Unpack(Microsoft.SqlServer.Dac.IPackageSource,System.String)">
            <summary>
            Place the contents of this package into the directory specified by <paramref name="directoryPath"/>.
            </summary>
            <param name="packageSource">
            Object used to access package content.
            </param>
            <param name="directoryPath">
            Base directory in which to place the package contents.
            </param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacPackage.Load(System.String)">
            <summary>
            Load a package file specified by <paramref name="fileName"/>.
            </summary>
            <param name="fileName">
            Path to the package file.
            </param>
            <returns>
            <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> instance that represents the package loaded from the specified file.
            </returns>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If there is a problem reading the package; or if the package contains exported data.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacPackage.Load(System.String,Microsoft.SqlServer.Dac.DacSchemaModelStorageType)">
            <summary>
            Load a package file specified by <paramref name="fileName"/>.
            </summary>
            <param name="fileName">
            Path to the package file.
            </param>
            <param name="modelStorageType">
            Backing storage type for package instance.
            </param>
            <returns>
            <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> instance that represents the package loaded from the specified file.
            </returns>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If there is a problem reading the package; or if the package contains exported data.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacPackage.Load(System.String,Microsoft.SqlServer.Dac.DacSchemaModelStorageType,System.IO.FileAccess)">
            <summary>
            Load a package file specified by <paramref name="fileName"/>.
            </summary>
            <param name="fileName">
            Path to the package file.
            </param>
            <param name="modelStorageType">
            Backing storage type for package instance.
            </param>
            <param name="packageAccess">
            The access in which to open the package. Valid values are <see cref="F:System.IO.FileAccess.Read"/> or <see cref="F:System.IO.FileAccess.ReadWrite"/>.
            </param>
            <returns>
            <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> instance that represents the package loaded from the specified file.
            </returns>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If there is a problem reading the package; or if the package contains exported data.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacPackage.Load(System.IO.Stream)">
            <summary>
            Load a package from the specified <see cref="T:System.IO.Stream"/>.
            </summary>
            <param name="stream">
            Stream from which to read the package.
            </param>
            <returns>
            <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> instance that represents the package loaded from the specified <see cref="T:System.IO.Stream"/>.
            </returns>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If there is a problem reading the package; or if the package contains exported data.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacPackage.Load(System.IO.Stream,Microsoft.SqlServer.Dac.DacSchemaModelStorageType)">
            <summary>
            Load a package from the specified <see cref="T:System.IO.Stream"/>.
            </summary>
            <param name="stream">
            Stream from which to read the package.
            </param>
            <param name="modelStorageType">
            Backing storage type for package instance.
            </param>
            <returns>
            <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> instance that represents the package loaded from the specified <see cref="T:System.IO.Stream"/>.
            </returns>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If there is a problem reading the package; or if the package contains exported data.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacPackage.Load(System.IO.Stream,Microsoft.SqlServer.Dac.DacSchemaModelStorageType,System.IO.FileAccess)">
            <summary>
            Load a package from the specified <see cref="T:System.IO.Stream"/>.
            </summary>
            <param name="stream">
            Stream from which to read the package.
            </param>
            <param name="modelStorageType">
            Backing storage type for package instance.
            </param>
            <param name="packageAccess">
            The access in which to open the package. Valid values are <see cref="F:System.IO.FileAccess.Read"/> or <see cref="F:System.IO.FileAccess.ReadWrite"/>.
            </param>
            <returns>
            <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> instance that represents the package loaded from the specified <see cref="T:System.IO.Stream"/>.
            </returns>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If there is a problem reading the package; or if the package contains exported data.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacPackage.Save(System.IO.Stream,Microsoft.Data.Tools.Schema.SchemaModel.DataSchemaModel,Microsoft.Data.Tools.Schema.Sql.Dac.DacMetadata,System.Nullable{System.UInt32})">
            <summary>
            Write package with the supplied schema model and DAC metadata to the supplied <see cref="T:System.IO.Stream"/>.
            </summary>
            <param name="stream">
            <see cref="T:System.IO.Stream"/> to which to write the package.
            </param>
            <param name="model">
            Schema model to be serialized into package.
            </param>
            <param name="metadata">
            DAC metadata to be serialized into package.
            </param>
            <param name="minModelVersion">
            Optional minimum model schema version to use when saving model.
            </param>
            <returns>The model version used for the serialization</returns>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacPackage.Name">
            <summary>
            Get identifier for this package.
            </summary>
            <value>
            String identifier for this package.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacPackage.Description">
            <summary>
            Get an optional summary of this package.
            </summary>
            <value>
            String summary of this package; or null reference if there is no summary.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacPackage.Version">
            <summary>
            Get version information for this package.
            </summary>
            <value>
            <see cref="P:Microsoft.SqlServer.Dac.DacPackage.Version"/> structure that specifies the version information for this package.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacPackage.PreDeploymentScript">
            <summary>
            Get stream used to read and write script executed prior to deploying schema contained in this package.
            </summary>
            <value>
            <see cref="T:System.IO.Stream"/> instance used to read and write script contents.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacPackage.PostDeploymentScript">
            <summary>
            Get stream used to read and write script executed after deploying schema contained in this package.
            </summary>
            <value>
            <see cref="T:System.IO.Stream"/> instance used to read and write script contents.
            </value>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.DacProfile">
            <summary>
            Contains settings related to a specific deployment configuration.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacProfile.#ctor">
            <summary>
            Construct a new instance of the <see cref="T:Microsoft.SqlServer.Dac.DacProfile"/> class with a default set of deployment options.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacProfile.Load(System.String)">
            <summary>
            Create a <see cref="T:Microsoft.SqlServer.Dac.DacProfile"/> instance by reading serialized profile data from the specified file.
            </summary>
            <param name="fileName">
            Path to file with serialized profile data.
            </param>
            <returns>
            <see cref="T:Microsoft.SqlServer.Dac.DacProfile"/> instance initialized from the serialized file data.
            </returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacProfile.Load(System.IO.Stream)">
            <summary>
            Create a <see cref="T:Microsoft.SqlServer.Dac.DacProfile"/> instance by reading serialized profile data from the specified <see cref="T:System.IO.Stream"/>.
            </summary>
            <param name="stream">
            Stream from which to read serialized profile data.
            </param>
            <returns>
            <see cref="T:Microsoft.SqlServer.Dac.DacProfile"/> instance initialized from the serialized file data.
            </returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacProfile.LoadImpl(System.Func{Microsoft.Data.Tools.Schema.Sql.Deployment.PublishProfileSerializer})">
            <summary>
            Create a <see cref="T:Microsoft.SqlServer.Dac.DacProfile"/> instance by reading serialized profile data from the specified <see cref="T:System.IO.Stream"/>.
            </summary>
            <param name="serializerProvider">
            Provider that creates serializer from which to read parsed profile data.
            </param>
            <returns>
            <see cref="T:Microsoft.SqlServer.Dac.DacProfile"/> instance initialized from the serialized file data.
            </returns>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacProfile.DeployOptions">
            <summary>
            Get deployment options associated with this profile.
            </summary>
            <value>
            <see cref="T:Microsoft.SqlServer.Dac.DacDeployOptions"/> instance that specifies deployment options.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacProfile.TargetDatabaseName">
            <summary>
            Get or set name of database used as the target of a deployment.
            </summary>
            <value>
            String name of target database.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacProfile.TargetConnectionString">
            <summary>
            Get or set connection string for database server used as the target of a deployment.
            </summary>
            <value>
            String that specifies database server connection information.
            </value>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.DacProgressEventArgs">
            <summary>
            Data associated with event raised as an executing operation completes individual steps of processing.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacProgressEventArgs.Status">
            <summary>
            Get status of an active operation.
            </summary>
            <value>
            <see cref="T:Microsoft.SqlServer.Dac.DacOperationStatus"/> enumeration value that specifies the current status of an operation.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacProgressEventArgs.Message">
            <summary>
            Get a friendly message associated with the current step of an operation.
            </summary>
            <value>
            String message associated with the current step of an operation.
            </value>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacProgressEventArgs.OperationId">
            <summary>
            Gets the ID of the top level task the progress event relates to.
            </summary>
            <remarks>
                <ul>
                <li>Extract = 1</li>
                <li>Publish = 2</li>
                <li>ImportData = 3</li>
                <li>ExportData = 4</li>
                <li>Unregister = 5</li>
                <li>Register = 6</li>
                <li>DriftReport = 7</li>
                <li>DeployReport = 8</li>
                <li>Script = 9</li>
                </ul>
            </remarks>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.DacRetryMessage">
            <summary>
            This message is raised when the DacFx API retried a command or connection due to a
            transient error returned by the server.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacRetryMessage.RetryCount">
            <summary>
            The retry count when this error was encountered
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacRetryMessage.ServerErrorNumber">
            <summary>
            The number of the error from SQL Server or 0 if a different 
            exception was encountered (that invoked a retry)
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.DacSchemaModelStorageType">
            <summary>
            Specifies the type of backing storage for a schema model.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.DacSchemaModelStorageType.File">
            <summary>
            Model is back by file-based storage.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.DacSchemaModelStorageType.Memory">
            <summary>
            Model is stored completely in-memory.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.DacServicesException">
            <summary>
            Exception thrown for DAC service errors.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServicesException.#ctor">
            <summary>
            Construct a new instance of the <see cref="T:Microsoft.SqlServer.Dac.DacServicesException"/> class.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServicesException.#ctor(System.String)">
            <summary>
            Construct a new instance of the <see cref="T:Microsoft.SqlServer.Dac.DacServicesException"/> class with the specified error message.
            </summary>
            <param name="message">
            The message that describes the error.
            </param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServicesException.#ctor(System.String,System.Exception)">
            <summary>
            Construct a new instance of the <see cref="T:Microsoft.SqlServer.Dac.DacServicesException"/> class with the specified error message 
            and a reference to the inner exception that is the cause of this exception.
            </summary>
            <param name="message">
            The message that describes the error.
            </param>
            <param name="innerException">
            The exception that is the cause of the current exception, or a null reference if no inner exception is specified. 
            </param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServicesException.GetObjectData(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext)">
            <summary>
            Write exception information to the supplied <see cref="T:System.Runtime.Serialization.SerializationInfo"/> object.
            </summary>
            <param name="info">
            <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds the serialized object data.
            </param>
            <param name="context">
            <see cref="T:System.Runtime.Serialization.StreamingContext"/> that contains contextual information about the source or destination.
            </param>
            <exception cref="T:System.ArgumentNullException">
            If <paramref name="info"/> is a null reference.
            </exception>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DacServicesException.Messages">
            <summary>
            Get collection of additional error, warning, and informational messages associated with this exception.
            </summary>
            <value>
            Collection of additional messages associated with this exception.
            </value>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.DacServices">
            <summary>
            Provides operations to create and consume DAC packages represented by <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> instances.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.DacServices._userConnectionFactory">
            <summary>
            Connection factory using the connection string supplied by caller.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.Initialize">
            <summary>
            Called by classes within the API to initialize any static state
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.#ctor(System.String)">
            <summary>
            Construct a new instance of the <see cref="T:Microsoft.SqlServer.Dac.DacServices"/> class
            that operates against the database specified by the supplied connection string.
            </summary>
            <param name="connectionString">
            String that supplies connection information for the database connection used by this instance.
            </param>
            <exception cref="T:System.ArgumentNullException">
            If <paramref name="connectionString"/> is a null reference.
            </exception>
            <exception cref="T:System.ArgumentException">
            If the supplied <paramref name="connectionString"/> is not valid.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.#ctor(System.String,Microsoft.SqlServer.Dac.IUniversalAuthProvider)">
            <summary>
            Construct a new instance of the <see cref="T:Microsoft.SqlServer.Dac.DacServices"/> class
            that operates against the database specified by the supplied connection string and authentication provider.
            </summary>
            <param name="connectionString">
            String that supplies connection information for the database connection used by this instance.
            </param>
            <param name="authProvider">
            Provide the up-to-date access token for AAD Universal Login (smartcard, phone, etc) 
            </param>
            <exception cref="T:System.ArgumentNullException">
            If <paramref name="connectionString"/> is a null reference.
            If <paramref name="authProvider"/> is a null reference.
            </exception>
            <exception cref="T:System.ArgumentException">
            If the supplied <paramref name="connectionString"/> is not valid.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.#ctor(System.String,System.Security.SecureString)">
            <summary>
            Construct a new instance of the <see cref="T:Microsoft.SqlServer.Dac.DacServices"/> class
            that operates against the database specified by the supplied connection string.
            </summary>
            <param name="connectionString">
            String that supplies connection information for the database connection used by this instance.
            </param>
            <param name="password">
            SecureString that supplies password for the database connection used by this instance.
            </param>
            <exception cref="T:System.ArgumentNullException">
            If <paramref name="connectionString"/> is a null reference.
            </exception>
            <exception cref="T:System.ArgumentException">
            If the supplied <paramref name="connectionString"/> is not valid.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.Extract(System.String,System.String,System.String,System.Version,System.String,System.Collections.Generic.IEnumerable{System.Tuple{System.String,System.String}},Microsoft.SqlServer.Dac.DacExtractOptions,System.Nullable{System.Threading.CancellationToken})">
            <summary>
            Extract the schema from a database into a package.
            </summary>
            <param name="packageFileName">
            Path to the package file to be created.
            </param>
            <param name="databaseName">
            Name of the source database.
            </param>
            <param name="applicationName">
            String identifier for the DAC application.
            </param>
            <param name="applicationVersion">
            Version of the DAC application.
            </param>
            <param name="applicationDescription">
            Optional string summary of the DAC application.
            </param>
            <param name="tables">
            <para>
            Optional enumerable used to retrieve enumerator over set of tables for which reference data should be stored.
            For each <see cref="T:System.Tuple"/> in the enumeration the first item specifies the schema of the table, and the second specifies the base identifier of the table.
            </para>
            <para>
            If the value for this parameter is a null reference, no reference data will be stored.
            </para>
            </param>
            <param name="extractOptions">
            Optional <see cref="T:Microsoft.SqlServer.Dac.DacExtractOptions"/> instance used to specify options that affect the behavior of package extraction.
            </param>
            <param name="cancellationToken">
            Optional <see cref="T:System.Threading.CancellationToken"/> that can be used to indicate that the operation should be cancelled.
            Use of this object does not guarantee that the operation will be cancelled.
            </param>
            <exception cref="T:System.ArgumentException">
            If <paramref name="applicationName"/> is a null reference or empty string.
            </exception>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If a problem occurs during package extraction.
            </exception>
            <exception cref="T:System.OperationCanceledException">
            If the <see cref="T:System.Threading.CancellationToken"/> has a cancellation requested and the operation could be cancelled.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.Extract(System.IO.Stream,System.String,System.String,System.Version,System.String,System.Collections.Generic.IEnumerable{System.Tuple{System.String,System.String}},Microsoft.SqlServer.Dac.DacExtractOptions,System.Nullable{System.Threading.CancellationToken})">
            <summary>
            Extract the schema from a database into a package.
            </summary>
            <param name="packageStream">
            <see cref="T:System.IO.Stream"/> to which to write the package.
            </param>
            <param name="databaseName">
            Name of the source database.
            </param>
            <param name="applicationName">
            String identifier for the DAC application.
            </param>
            <param name="applicationVersion">
            Version of the DAC application.
            </param>
            <param name="applicationDescription">
            Optional string summary of the DAC application.
            </param>
            <param name="tables">
            <para>
            Optional enumerable used to retrieve enumerator over set of tables for which data should be exported.
            For each <see cref="T:System.Tuple"/> in the enumeration the first item specifies the schema of the table, and the second specifies the base identifier of the table.
            </para>
            <para>
            If the value for this parameter is a null reference, no data will be exported.
            </para>
            </param>
            <param name="extractOptions">
            Optional <see cref="T:Microsoft.SqlServer.Dac.DacExtractOptions"/> instance used to specify options that affect the behavior of package extraction.
            </param>
            <param name="cancellationToken">
            Optional <see cref="T:System.Threading.CancellationToken"/> that can be used to indicate that the operation should be cancelled.
            Use of this object does not guarantee that the operation will be cancelled.
            </param>
            <exception cref="T:System.ArgumentException">
            If <paramref name="applicationName"/> is a null reference or empty string.
            </exception>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If a problem occurs during package extraction; or the supplied stream is not suitable for writing the package.
            </exception>
            <exception cref="T:System.OperationCanceledException">
            If the <see cref="T:System.Threading.CancellationToken"/> has a cancellation requested and the operation could be cancelled.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.Script(Microsoft.SqlServer.Dac.DacPackage,System.String,Microsoft.SqlServer.Dac.PublishOptions)">
            <summary>
            Scripts the steps for a publish operation as a deployment script, deployment report, or both. 
            At least one of the <see cref="P:Microsoft.SqlServer.Dac.PublishOptions.GenerateDeploymentScript"/> or <see cref="P:Microsoft.SqlServer.Dac.PublishOptions.GenerateDeploymentReport"/> 
            options must be set to true. This method will not publish to the database 
            </summary>
            <param name="package">
            <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> containing schema to be deployed.
            </param>
            <param name="targetDatabaseName">
            Name of the target database for deployment.
            </param>
            <param name="publishOptions">
            Instance of <see cref="T:Microsoft.SqlServer.Dac.PublishOptions"/> that specifies what is reported back - the deployment script,
            deployment report, as well as how to configure various aspects of the deployment.
            </param>
            <returns><see cref="T:Microsoft.SqlServer.Dac.PublishResult"/>, containing the script(s) and/or report if requested by the caller</returns>
            <exception cref="T:System.ArgumentException">
            If the value for any of the required parameters is a null reference or an empty string.
            </exception>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If an error occurs during deployment.
            </exception>
            <exception cref="T:System.OperationCanceledException">
            If the <see cref="T:System.Threading.CancellationToken"/> has a cancellation requested and the operation could be cancelled.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.Script(Microsoft.SqlServer.Dac.DacPackage,Microsoft.SqlServer.Dac.DacPackage,System.String,Microsoft.SqlServer.Dac.PublishOptions)">
            <summary>
            Scripts the steps for a publish operation as a deployment script, deployment report, or both. 
            At least one of the <see cref="P:Microsoft.SqlServer.Dac.PublishOptions.GenerateDeploymentScript"/> or <see cref="P:Microsoft.SqlServer.Dac.PublishOptions.GenerateDeploymentReport"/> 
            options must be set to true. This method will not modify the target
            </summary>
            <param name="sourcePackage">
            <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> containing schema to be deployed.
            </param>
            <param name="targetPackage">
            <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> containing schema representing the target database.
            </param>
            <param name="targetDatabaseName">
            Name of the target database for deployment.
            </param>
            <param name="publishOptions">
            Instance of <see cref="T:Microsoft.SqlServer.Dac.PublishOptions"/> that specifies what is reported back - the deployment script,
            deployment report, as we all how to configure various aspects of the deployment.
            </param>
            <returns><see cref="T:Microsoft.SqlServer.Dac.PublishResult"/>, containing the script(s) and/or report if requested by the caller</returns>
            <exception cref="T:System.ArgumentException">
            If the value for any of the required parameters is a null reference or an empty string.
            </exception>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If an error occurs during deployment.
            </exception>
            <exception cref="T:System.OperationCanceledException">
            If the <see cref="T:System.Threading.CancellationToken"/> has a cancellation requested and the operation could be cancelled.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.Publish(Microsoft.SqlServer.Dac.DacPackage,System.String,Microsoft.SqlServer.Dac.PublishOptions)">
            <summary>
            Publish the supplied package to a database. This method supports returning the script used when publishing along with
            a deployment report summarizing the actions taken during Publish
            </summary>
            <param name="package">
            <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> containing schema to be deployed.
            </param>
            <param name="targetDatabaseName">
            Name of the target database for deployment.
            </param>
            <param name="publishOptions">
            Instance of <see cref="T:Microsoft.SqlServer.Dac.PublishOptions"/> that specifies what is reported back - the deployment script,
            deployment report, as we all how to configure various aspects of the deployment.
            </param>
            <returns><see cref="T:Microsoft.SqlServer.Dac.PublishResult"/>, containing the script(s) and/or report if requested by the caller</returns>
            <exception cref="T:System.ArgumentException">
            If the value for any of the required parameters is a null reference or an empty string.
            </exception>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If an error occurs during deployment.
            </exception>
            <exception cref="T:System.OperationCanceledException">
            If the <see cref="T:System.Threading.CancellationToken"/> has a cancellation requested and the operation could be cancelled.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.Deploy(Microsoft.SqlServer.Dac.DacPackage,System.String,System.Boolean,Microsoft.SqlServer.Dac.DacDeployOptions,System.Nullable{System.Threading.CancellationToken})">
            <summary>
            Deploy the supplied package to a database.
            </summary>
            <param name="package">
            <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> containing schema to be deployed.
            </param>
            <param name="targetDatabaseName">
            Name of the target database for deployment.
            </param>
            <param name="upgradeExisting">
            True to allow modification of existing database schema in order to match schema contained in source package;
            otherwise false to block modification of existing database.
            If the database does not exist this flag has no effect.
            </param>
            <param name="options">
            Instance of <see cref="T:Microsoft.SqlServer.Dac.DacDeployOptions"/> that specifies properties that affect various aspects of the deployment.
            </param>
            <param name="cancellationToken">
            Optional <see cref="T:System.Threading.CancellationToken"/> that can be used to indicate that the operation should be cancelled.
            Use of this object does not guarantee that the operation will be cancelled.
            </param>
            <exception cref="T:System.ArgumentException">
            If the value for any of the required parameters is a null reference or an empty string.
            </exception>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If <paramref name="upgradeExisting"/> is true and the target database exists;
            or if an error occurs during deployment.
            </exception>
            <exception cref="T:System.OperationCanceledException">
            If the <see cref="T:System.Threading.CancellationToken"/> has a cancellation requested and the operation could be cancelled.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.GenerateDeployScript(Microsoft.SqlServer.Dac.DacPackage,System.String,Microsoft.SqlServer.Dac.DacDeployOptions,System.Nullable{System.Threading.CancellationToken})">
            <summary>
            Create T-SQL script that can be used to deploy the schema of the supplied <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> to a database.
            </summary>
            <param name="package">
            <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> containing schema to be deployed.
            </param>
            <param name="targetDatabaseName">
            Name of the target database for deployment.
            </param>
            <param name="options">
            Instance of <see cref="T:Microsoft.SqlServer.Dac.DacDeployOptions"/> that specifies properties that affect various aspects of the deployment script creation.
            </param>
            <param name="cancellationToken">
            Optional <see cref="T:System.Threading.CancellationToken"/> that can be used to indicate that the operation should be cancelled.
            Use of this object does not guarantee that the operation will be cancelled.
            </param>
            <returns>
            String of T-SQL script used to create or update database schema based on the supplied <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/>.
            </returns>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If a problem occurs during script generation.
            </exception>
            <exception cref="T:System.OperationCanceledException">
            If the <see cref="T:System.Threading.CancellationToken"/> has a cancellation requested and the operation could be cancelled.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.GenerateDeployReport(Microsoft.SqlServer.Dac.DacPackage,System.String,Microsoft.SqlServer.Dac.DacDeployOptions,System.Nullable{System.Threading.CancellationToken})">
            <summary>
            Create XML report of the steps needed to deploy the schema of the supplied <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> to a database.
            </summary>
            <param name="package">
            <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> containing schema to be deployed.
            </param>
            <param name="targetDatabaseName">
            Name of the target database for deployment.
            </param>
            <param name="options">
            Instance of <see cref="T:Microsoft.SqlServer.Dac.DacDeployOptions"/> that specifies properties that affect various aspects of the deployment report creation.
            </param>
            <param name="cancellationToken">
            Optional <see cref="T:System.Threading.CancellationToken"/> that can be used to indicate that the operation should be cancelled.
            Use of this object does not guarantee that the operation will be cancelled.
            </param>
            <returns>
            String of XML that details the steps of the package deployment.
            </returns>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If a problem occurs during report generation.
            </exception>
            <exception cref="T:System.OperationCanceledException">
            If the <see cref="T:System.Threading.CancellationToken"/> has a cancellation requested and the operation could be cancelled.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.CreateController(Microsoft.Data.Tools.Schema.Sql.Deployment.SqlDeployment,Microsoft.Data.Tools.Schema.ErrorManager)">
            <summary>
            Creates an <see cref="T:Microsoft.Data.Tools.Schema.Sql.Deployment.IDeploymentController"/>, handling any errors thrown during contstruction
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.GenerateUniqueScriptFileName(System.String,System.Boolean)">
            <summary>
            Generate unique filename for a script
            </summary>
            <param name="databaseName">The target database name</param>
            <param name="isMaster">Is this the master database script</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.GenerateDriftReport(System.String,System.Nullable{System.Threading.CancellationToken})">
            <summary>
            Create XML report of the drift that has occurred since the database was last registered.
            </summary>
            <param name="targetDatabaseName">
            Name of the target database for deployment.
            </param>
            <param name="cancellationToken">
            Optional <see cref="T:System.Threading.CancellationToken"/> that can be used to indicate that the operation should be cancelled.
            Use of this object does not guarantee that the operation will be cancelled.
            </param>
            <returns>
            String of XML that details the database drift, or null if the database has not been registered.
            </returns>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If a problem occurs during report generation.
            </exception>
            <exception cref="T:System.OperationCanceledException">
            If the <see cref="T:System.Threading.CancellationToken"/> has a cancellation requested and the operation could be cancelled.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.GenerateDriftReport(System.String,Microsoft.SqlServer.Dac.DacSchemaModelStorageType,System.Nullable{System.Threading.CancellationToken})">
            <summary>
            Create XML report of the drift that has occurred since the database was last registered.
            </summary>
            <param name="targetDatabaseName">
            Name of the target database for deployment.
            </param>
            <param name="modelStorageType">
            Specifies the type of backing storage for a schema model.
            </param>
            <param name="cancellationToken">
            Optional <see cref="T:System.Threading.CancellationToken"/> that can be used to indicate that the operation should be cancelled.
            Use of this object does not guarantee that the operation will be cancelled.
            </param>
            <returns>
            String of XML that details the database drift, or null if the database has not been registered.
            </returns>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If a problem occurs during report generation.
            </exception>
            <exception cref="T:System.OperationCanceledException">
            If the <see cref="T:System.Threading.CancellationToken"/> has a cancellation requested and the operation could be cancelled.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.Register(System.String,System.String,System.Version,System.String)">
            <summary>
            Add DAC registration information for the specified target database.
            </summary>
            <param name="targetDatabaseName">
            Name of the database for which to add registration information.
            </param>
            <param name="applicationName">
            String identifier for the DAC application.
            </param>
            <param name="applicationVersion">
            Version of the DAC application.
            </param>
            <param name="applicationDescription">
            Optional string summary of the DAC application.
            </param>
            <exception cref="T:System.ArgumentException">
            If any of the required arguments are null or zero-length strings.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.Register(System.String,Microsoft.SqlServer.Dac.DacSchemaModelStorageType,System.String,System.Version,System.String)">
            <summary>
            Add DAC registration information for the specified target database.
            </summary>
            <param name="targetDatabaseName">
            Name of the database for which to add registration information.
            </param>
            <param name="modelStorageType">
            Specifies the type of backing storage for a schema model.
            </param>
            <param name="applicationName">
            String identifier for the DAC application.
            </param>
            <param name="applicationVersion">
            Version of the DAC application.
            </param>
            <param name="applicationDescription">
            Optional string summary of the DAC application.
            </param>
            <exception cref="T:System.ArgumentException">
            If any of the required arguments are null or zero-length strings.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.Unregister(System.String)">
            <summary>
            Remove DAC registration information for the specified target database.
            </summary>
            <param name="targetDatabaseName">
            Name of the database for which to remove registration information.
            </param>
            <exception cref="T:System.ArgumentException">
            If <paramref name="targetDatabaseName"/> is null or a zero-length string.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.ExportBacpac(System.String,System.String,System.Collections.Generic.IEnumerable{System.Tuple{System.String,System.String}},System.Nullable{System.Threading.CancellationToken})">
            <summary>
            Extract schema and export data from a database into a "bacpac" package.
            </summary>
            <param name="packageFileName">
            Path of the target package file.
            </param>
            <param name="databaseName">
            Name of the source database.
            </param>
            <param name="tables">
            <para>
            Optional enumerable used to retrieve enumerator over set of tables for which data should be exported.
            For each <see cref="T:System.Tuple"/> in the enumeration the first item specifies the schema of the table, and the second specifies the base identifier of the table.
            </para>
            <para>
            If the value for this parameter is a null reference, data for all tables will be exported.
            </para>
            </param>
            <param name="cancellationToken">
            Optional <see cref="T:System.Threading.CancellationToken"/> that can be used to indicate that the operation should be cancelled.
            Use of this object does not guarantee that the operation will be cancelled.
            </param>
            <exception cref="T:System.ArgumentException">
            If the value for any of the required parameters is a null reference or an empty string.
            </exception>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If an error occurs during schema deployment and data export;
            or if <paramref name="tables"/> specifies a table that does not exist in the schema contained in the supplied package.
            </exception>
            <exception cref="T:System.OperationCanceledException">
            If the <see cref="T:System.Threading.CancellationToken"/> has a cancellation requested and the operation could be cancelled.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.ExportBacpac(System.String,System.String,Microsoft.SqlServer.Dac.DacSchemaModelStorageType,System.Collections.Generic.IEnumerable{System.Tuple{System.String,System.String}},System.Nullable{System.Threading.CancellationToken})">
            <summary>
            Extract schema and export data from a database into a "bacpac" package.
            </summary>
            <param name="packageFileName">
            Path of the target package file.
            </param>
            <param name="databaseName">
            Name of the source database.
            </param>
            <param name="modelStorageType">
            Specifies the type of backing storage for a schema model.
            </param>
            <param name="tables">
            <para>
            Optional enumerable used to retrieve enumerator over set of tables for which data should be exported.
            For each <see cref="T:System.Tuple"/> in the enumeration the first item specifies the schema of the table, and the second specifies the base identifier of the table.
            </para>
            <para>
            If the value for this parameter is a null reference, data for all tables will be exported.
            </para>
            </param>
            <param name="cancellationToken">
            Optional <see cref="T:System.Threading.CancellationToken"/> that can be used to indicate that the operation should be cancelled.
            Use of this object does not guarantee that the operation will be cancelled.
            </param>
            <exception cref="T:System.ArgumentException">
            If the value for any of the required parameters is a null reference or an empty string.
            </exception>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If an error occurs during schema deployment and data export;
            or if <paramref name="tables"/> specifies a table that does not exist in the schema contained in the supplied package.
            </exception>
            <exception cref="T:System.OperationCanceledException">
            If the <see cref="T:System.Threading.CancellationToken"/> has a cancellation requested and the operation could be cancelled.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.ExportBacpac(System.String,System.String,Microsoft.SqlServer.Dac.DacExportOptions,System.Collections.Generic.IEnumerable{System.Tuple{System.String,System.String}},System.Nullable{System.Threading.CancellationToken})">
            <summary>
            Extract schema and export data from a database into a "bacpac" package.
            </summary>
            <param name="packageFileName">
            Path of the target package file.
            </param>
            <param name="databaseName">
            Name of the source database.
            </param>
            <param name="options">
             Instance of <see cref="T:Microsoft.SqlServer.Dac.DacExportOptions"/> that specifies properties that affect various aspects of the export.
            </param>
            <param name="tables">
            Optional enumerable used to retrieve enumerator over set of tables for which data should be exported.
            For each <see cref="T:System.Tuple"/> in the enumeration the first item specifies the schema of the table, and the second specifies the base identifier of the table.
            </param>
            <param name="cancellationToken">
            Optional <see cref="T:System.Threading.CancellationToken"/> that can be used to indicate that the operation should be cancelled.
            Use of this object does not guarantee that the operation will be cancelled.
            </param>
            <exception cref="T:System.ArgumentException">
            If the value for any of the required parameters is a null reference or an empty string.
            </exception>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If an error occurs during schema deployment and data export;
            or if <paramref name="options"/> specifies a table that does not exist in the schema contained in the supplied package.
            </exception>
            <exception cref="T:System.OperationCanceledException">
            If the <see cref="T:System.Threading.CancellationToken"/> has a cancellation requested and the operation could be cancelled.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.ExportBacpac(System.IO.Stream,System.String,System.Collections.Generic.IEnumerable{System.Tuple{System.String,System.String}},System.Nullable{System.Threading.CancellationToken})">
            <summary>
            Extract schema and export data from a database into a "bacpac" package.
            </summary>
            <param name="packageStream">
            <see cref="T:System.IO.Stream"/> to which to write the package.
            </param>
            <param name="databaseName">
            Name of the source database.
            </param>
            <param name="tables">
            <para>
            Optional enumerable used to retrieve enumerator over set of tables for which data should be exported.
            For each <see cref="T:System.Tuple"/> in the enumeration the first item specifies the schema of the table, and the second specifies the base identifier of the table.
            </para>
            <para>
            If the value for this parameter is a null reference, data for all tables will be exported.
            </para>
            </param>
            <param name="cancellationToken">
            Optional <see cref="T:System.Threading.CancellationToken"/> that can be used to indicate that the operation should be cancelled.
            Use of this object does not guarantee that the operation will be cancelled.
            </param>
            <exception cref="T:System.ArgumentException">
            If the value for any of the required parameters is a null reference or an empty string.
            </exception>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If an error occurs during schema deployment and data export;
            or if <paramref name="tables"/> specifies a table that does not exist in the schema contained in the supplied package;
            or the supplied stream is not suitable for writing the package.
            </exception>
            <exception cref="T:System.OperationCanceledException">
            If the <see cref="T:System.Threading.CancellationToken"/> has a cancellation requested and the operation could be cancelled.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.ExportBacpac(System.IO.Stream,System.String,Microsoft.SqlServer.Dac.DacSchemaModelStorageType,System.Collections.Generic.IEnumerable{System.Tuple{System.String,System.String}},System.Nullable{System.Threading.CancellationToken})">
            <summary>
            Extract schema and export data from a database into a "bacpac" package.
            </summary>
            <param name="packageStream">
            <see cref="T:System.IO.Stream"/> to which to write the package.
            </param>
            <param name="databaseName">
            Name of the source database.
            </param>
            <param name="modelStorageType">
            Specifies the type of backing storage for a schema model.
            </param>
            <param name="tables">
            <para>
            Optional enumerable used to retrieve enumerator over set of tables for which data should be exported.
            For each <see cref="T:System.Tuple"/> in the enumeration the first item specifies the schema of the table, and the second specifies the base identifier of the table.
            </para>
            <para>
            If the value for this parameter is a null reference, data for all tables will be exported.
            </para>
            </param>
            <param name="cancellationToken">
            Optional <see cref="T:System.Threading.CancellationToken"/> that can be used to indicate that the operation should be cancelled.
            Use of this object does not guarantee that the operation will be cancelled.
            </param>
            <exception cref="T:System.ArgumentException">
            If the value for any of the required parameters is a null reference or an empty string.
            </exception>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If an error occurs during schema deployment and data export;
            or if <paramref name="tables"/> specifies a table that does not exist in the schema contained in the supplied package;
            or the supplied stream is not suitable for writing the package.
            </exception>
            <exception cref="T:System.OperationCanceledException">
            If the <see cref="T:System.Threading.CancellationToken"/> has a cancellation requested and the operation could be cancelled.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.ExportBacpac(System.IO.Stream,System.String,Microsoft.SqlServer.Dac.DacExportOptions,System.Collections.Generic.IEnumerable{System.Tuple{System.String,System.String}},System.Nullable{System.Threading.CancellationToken})">
            <summary>
            Extract schema and export data from a database into a "bacpac" package.
            </summary>
            <param name="packageStream">
            <see cref="T:System.IO.Stream"/> to which to write the package.
            </param>
            <param name="databaseName">
            Name of the source database.
            </param>
            <param name="options">
            Optional instance of <see cref="T:Microsoft.SqlServer.Dac.DacExportOptions"/> that specifies properties that affect various aspects of the export.
            </param>
            <param name="tables">
            Optional enumerable used to retrieve enumerator over set of tables for which data should be exported.
            For each <see cref="T:System.Tuple"/> in the enumeration the first item specifies the schema of the table, and the second specifies the base identifier of the table.
            </param>
            <param name="cancellationToken">
            Optional <see cref="T:System.Threading.CancellationToken"/> that can be used to indicate that the operation should be cancelled.
            Use of this object does not guarantee that the operation will be cancelled.
            </param>
            <exception cref="T:System.ArgumentException">
            If the value for any of the required parameters is a null reference or an empty string.
            </exception>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If an error occurs during schema deployment and data export;
            or if <paramref name="options"/> specifies a table that does not exist in the schema contained in the supplied package.
            </exception>
            <exception cref="T:System.OperationCanceledException">
            If the <see cref="T:System.Threading.CancellationToken"/> has a cancellation requested and the operation could be cancelled.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.SetAmbientSetting(System.String,System.Object)">
            <summary>
            Populates ambient setting controlling
            deployment/retry behavior down the stack.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.CreateSettingsContext(Microsoft.SqlServer.Dac.DacLoggingContext)">
            <summary>
            Sets up a stack context to push a configured settings object to populate
            the ambient settings for consumption further down the stack
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.ImportBacpac(Microsoft.SqlServer.Dac.BacPackage,System.String,System.Nullable{System.Threading.CancellationToken})">
            <summary>
            Deploy schema and import table data from the supplied package to a database.
            </summary>
            <param name="package">
            <see cref="T:Microsoft.SqlServer.Dac.BacPackage"/> containing schema to be deployed and data to be imported.
            </param>
            <param name="targetDatabaseName">
            Name of the target database for deployment.
            </param>
            <param name="cancellationToken">
            Optional <see cref="T:System.Threading.CancellationToken"/> that can be used to indicate that the operation should be cancelled.
            Use of this object does not guarantee that the operation will be cancelled.
            </param>
            <exception cref="T:System.ArgumentException">
            If the value for any of the required parameters is a null reference or an empty string.
            </exception>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If an error occurs during schema deployment and data import.
            </exception>
            <exception cref="T:System.OperationCanceledException">
            If the <see cref="T:System.Threading.CancellationToken"/> has a cancellation requested and the operation could be cancelled.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.ImportBacpac(Microsoft.SqlServer.Dac.BacPackage,System.String,Microsoft.SqlServer.Dac.DacAzureDatabaseSpecification,System.Nullable{System.Threading.CancellationToken})">
            <summary>
            Deploy schema and import table data from the supplied package to a database.
            </summary>
            <param name="package">
            <see cref="T:Microsoft.SqlServer.Dac.BacPackage"/> containing schema to be deployed and data to be imported.
            </param>
            <param name="targetDatabaseName">
            Name of the target database for deployment.
            </param>
            <param name="creationDefaults">
            Optional default size and edition parameters used when creating a new Azure database.
            </param>
            <param name="cancellationToken">
            Optional <see cref="T:System.Threading.CancellationToken"/> that can be used to indicate that the operation should be cancelled.
            Use of this object does not guarantee that the operation will be cancelled.
            </param>
            <exception cref="T:System.ArgumentException">
            If the value for any of the required parameters is a null reference or an empty string.
            </exception>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If an error occurs during schema deployment and data import.
            </exception>
            <exception cref="T:System.OperationCanceledException">
            If the <see cref="T:System.Threading.CancellationToken"/> has a cancellation requested and the operation could be cancelled.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.ImportBacpac(Microsoft.SqlServer.Dac.BacPackage,System.String,Microsoft.SqlServer.Dac.DacImportOptions,System.Nullable{System.Threading.CancellationToken})">
            <summary>
            Deploy schema and import table data from the supplied package to a database.
            </summary>
            <param name="package">
            <see cref="T:Microsoft.SqlServer.Dac.BacPackage"/> containing schema to be deployed and data to be imported.
            </param>
            <param name="targetDatabaseName">
            Name of the target database for deployment.
            </param>
            <param name="importOptions">
             Instance of <see cref="T:Microsoft.SqlServer.Dac.DacImportOptions"/> that specifies properties that affect various aspects of the import.
            </param>
            <param name="cancellationToken">
            Optional <see cref="T:System.Threading.CancellationToken"/> that can be used to indicate that the operation should be cancelled.
            Use of this object does not guarantee that the operation will be cancelled.
            </param>
            <exception cref="T:System.ArgumentException">
            If the value for any of the required parameters is a null reference or an empty string.
            </exception>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If an error occurs during schema deployment and data import.
            </exception>
            <exception cref="T:System.OperationCanceledException">
            If the <see cref="T:System.Threading.CancellationToken"/> has a cancellation requested and the operation could be cancelled.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.SafeFileStreamGetter(System.String,System.IO.FileMode,System.IO.FileAccess,System.IO.FileShare,System.String)">
            <summary>
            Return FileStream after handling all acceptable exceptions.
            </summary>
            <param name="path">File path.</param>
            <param name="mode">File mode.</param>
            <param name="access">File access.</param>
            <param name="share">File share.</param>
            <param name="handledExceptionMessage">Message for handled exception.</param>
            <returns></returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.SafeFileAction(System.Action,System.String)">
            <summary>
            Catches common error cases when doing I/O operations and throws a <see cref="T:Microsoft.SqlServer.Dac.DacServicesException"/> with a predefined error message
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.GenerateCreateScript(Microsoft.SqlServer.Dac.DacPackage,System.String,Microsoft.SqlServer.Dac.DacDeployOptions)">
            <summary>
            Create T-SQL script that can be used to deploy the schema of the supplied <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> to a database.
            </summary>
            <param name="package">
            <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> containing schema to be deployed.
            </param>
            <param name="targetDatabaseName">
            Name of the target database for deployment.
            </param>
            <param name="options">
            Instance of <see cref="T:Microsoft.SqlServer.Dac.DacDeployOptions"/> that specifies properties that affect various aspects of the deployment script creation.
            </param>
            <returns>
            String of T-SQL script used to create database schema based on the supplied <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/>.
            </returns>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If a problem occurs during script generation.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.GenerateCreateScript(System.IO.Stream,Microsoft.SqlServer.Dac.DacPackage,System.String,Microsoft.SqlServer.Dac.DacDeployOptions)">
            <summary>
            Create T-SQL script that can be used to deploy the schema of the supplied <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> to a database.
            </summary>
            <param name="outputStream">
            <see cref="T:System.IO.Stream"/> to which to write the deployment script.
            </param>
            <param name="package">
            <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> containing schema to be deployed.
            </param>
            <param name="targetDatabaseName">
            Name of the target database for deployment.
            </param>
            <param name="options">
            Instance of <see cref="T:Microsoft.SqlServer.Dac.DacDeployOptions"/> that specifies properties that affect various aspects of the deployment script creation.
            </param>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If a problem occurs during script generation.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.GenerateDeployScript(Microsoft.SqlServer.Dac.DacPackage,Microsoft.SqlServer.Dac.DacPackage,System.String,Microsoft.SqlServer.Dac.DacDeployOptions)">
            <summary>
            Create T-SQL script that can be used to deploy the differences between the schemas of the supplied source and target <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> instances to a database.
            </summary>
            <param name="sourcePackage">
            <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> containing the source schema to be compared.
            </param>
            <param name="targetPackage">
            <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> containing the target schema to be compared.
            </param>
            <param name="targetDatabaseName">
            Name of the target database for deployment.
            </param>
            <param name="options">
            Instance of <see cref="T:Microsoft.SqlServer.Dac.DacDeployOptions"/> that specifies properties that affect various aspects of the deployment script creation.
            </param>
            <returns>
            String of T-SQL script used to create or update database schema based on the supplied <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/>.
            </returns>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If a problem occurs during script generation.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.GenerateDeployScript(System.IO.Stream,Microsoft.SqlServer.Dac.DacPackage,Microsoft.SqlServer.Dac.DacPackage,System.String,Microsoft.SqlServer.Dac.DacDeployOptions)">
            <summary>
            Create T-SQL script that can be used to deploy the differences between the schemas of the supplied source and target <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> instances to a database.
            </summary>
            <param name="outputStream">
            <see cref="T:System.IO.Stream"/> to which to write the deployment script.
            </param>
            <param name="sourcePackage">
            <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> containing the source schema to be compared.
            </param>
            <param name="targetPackage">
            <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> containing the target schema to be compared.
            </param>
            <param name="targetDatabaseName">
            Name of the target database for deployment.
            </param>
            <param name="options">
            Instance of <see cref="T:Microsoft.SqlServer.Dac.DacDeployOptions"/> that specifies properties that affect various aspects of the deployment script creation.
            </param>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If a problem occurs during script generation.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.GenerateDeployReport(Microsoft.SqlServer.Dac.DacPackage,Microsoft.SqlServer.Dac.DacPackage,System.String,Microsoft.SqlServer.Dac.DacDeployOptions)">
            <summary>
            Create XML report of the steps needed to deploy the differences between the schemas of the supplied source and target <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> instances to a database.
            </summary>
            <param name="sourcePackage">
            <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> containing the source schema to be compared.
            </param>
            <param name="targetPackage">
            <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> containing the target schema to be compared.
            </param>
            <param name="targetDatabaseName">
            Name of the target database for deployment.
            </param>
            <param name="options">
            Instance of <see cref="T:Microsoft.SqlServer.Dac.DacDeployOptions"/> that specifies properties that affect various aspects of the deployment report creation.
            </param>
            <returns>
            String of XML that details the steps of the deployment.
            </returns>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If a problem occurs during report generation.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.GenerateDeployReport(System.IO.Stream,Microsoft.SqlServer.Dac.DacPackage,Microsoft.SqlServer.Dac.DacPackage,System.String,Microsoft.SqlServer.Dac.DacDeployOptions)">
            <summary>
            Create XML report of the steps needed to deploy the differences between the schemas of the supplied source and target <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> instances to a database.
            </summary>
            <param name="outputStream">
            <see cref="T:System.IO.Stream"/> to which to write the deployment report.
            </param>
            <param name="sourcePackage">
            <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> containing the source schema to be compared.
            </param>
            <param name="targetPackage">
            <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> containing the target schema to be compared.
            </param>
            <param name="targetDatabaseName">
            Name of the target database for deployment.
            </param>
            <param name="options">
            Instance of <see cref="T:Microsoft.SqlServer.Dac.DacDeployOptions"/> that specifies properties that affect various aspects of the deployment report creation.
            </param>
            <exception cref="T:Microsoft.SqlServer.Dac.DacServicesException">
            If a problem occurs during report generation.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.ParseAndThrowIfUnsupportedConnectionStringBuilder(Microsoft.Data.Tools.Schema.Common.SqlClient.SqlConnectionFactory)">
            <summary>
            Evaluate the connection factory and determine if supplied connection factory is valid and supported by <see cref="T:Microsoft.SqlServer.Dac.DacServices"/>.
            </summary>
            <param name="connectionFactory">
            Connection factory to be validated.
            </param>
            <returns>Validated connection factory with only supported arguments present.</returns>
            <exception cref="T:System.ArgumentException">
            If the supplied <paramref name="connectionFactory"/> is not valid or if it specifies properties 
            or property values that are not supported by <see cref="T:Microsoft.SqlServer.Dac.DacServices"/>.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DacServices.ProbeIsolatedStorageAndSetAppDomainEvidenceIfNeccessary">
            <summary>
            Some CLR hosts do not set the AppDomain Evidence which is required for IsolatedStorage to function properly.  This 
            method probes if isolated storage is usable for the current user.  If isolated storage is not usable, it will attempt 
            to fix this by setting the AppDomain Evidence using private reflection.  Fixing isolated storage is a best effort
            attempt.  This method swallows all exceptions and lets the program proceed.
            </summary>cd
        </member>
        <member name="E:Microsoft.SqlServer.Dac.DacServices.ProgressChanged">
            <summary>
            Invoked as the state of an operation changes.
            </summary>
        </member>
        <member name="E:Microsoft.SqlServer.Dac.DacServices.Message">
            <summary>
            Invoked as an operation reports status updates or errors.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Data.DataException">
            <summary>
            This class is meant to be a public exception class - i.e. when throwing it ensure
            that the exception is meaningful to the end-developer.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Data.DataResources">
            <summary>
              A strongly-typed resource class, for looking up localized strings, etc.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Data.DataResources.ResourceManager">
            <summary>
              Returns the cached ResourceManager instance used by this class.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Data.DataResources.Culture">
            <summary>
              Overrides the current thread's CurrentUICulture property for all
              resource lookups using this strongly typed resource class.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Data.DataResources.CreateTargetModelFailed">
            <summary>
              Looks up a localized string similar to Failed to retrieve information from target database {0}.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Data.DataResources.DataPlanExecutionFailed">
            <summary>
              Looks up a localized string similar to Data plan execution failed with message {0}.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Data.DataResources.FailedDetectDspType">
            <summary>
              Looks up a localized string similar to Failed to retrieve information from target database to correctly model the database.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Data.DataResources.Skipped_SelectedColumn">
            <summary>
              Looks up a localized string similar to Excluded column [{0}].[{1}].[{2}] does not exist in the source model..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Data.DataResources.Skipped_SelectedTable">
            <summary>
              Looks up a localized string similar to Selected table [{0}].[{1}] does not have data and will not be populated..
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.DatabasePointer">
            <summary>
            Logically represents a database.  Can be used to retrieve information about the database
            such as a model and data
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.DatabasePointer.AddNewConstraints(Microsoft.Data.Tools.Schema.Sql.Deployment.ConstraintManager,Microsoft.Data.Tools.Schema.Common.SqlClient.SqlConnectionFactory)">
            <summary>
            Adds new constraints to the specified constraint manager.  Will throw a DataException if a error
            occurs with the connection to the DB
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.Data.DataDeployment">
            <summary>
            Represents a "top of stack" incremental data deployment engine.  This engine acts primarly as an orchestrator
            between other more fine-grained data deployment components.  It is meant to be a fire-and-dispose component, access to and control of 
            more fine grained components is left to direct manipulation of those components directly to avoid lifetime issues
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.Data.DataDeploymentOptions.AddSelectedTable(System.Tuple{System.String,System.String})">
            <summary>
            Adds a table to be considered when creating the data deployment plan
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.Data.DataDeploymentOptions.TableSelectionPolicy">
            <summary>
            Configures how tables will be selected for data deployment
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.Data.DataDeploymentOptions.StorageType">
            <summary>
            Storage to use when creating models of the source/target databases
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Deployment.Data.DataDeploymentOptions.TargetDatabaseName">
            <summary>
            Name of the target database.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.Data.DataPlanHandle">
            <summary>
            This class represents the disposable parts of the deployment plan.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.Data.DataPlanStrategy">
            <summary>
            Base class for different data deployment plan generation strategies
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.Data.PlanCreationContext">
            <summary>
            A data transfer object used to pass state between components used in plan creation
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.Data.PlanCreationContext.PopulateIncludedTables(System.Collections.Generic.HashSet{Microsoft.Data.Tools.Schema.Sql.SchemaModel.SqlElementDescriptor})">
             <summary>
             Called to populate selected data tables and columns based on
             policy specified in the options
            
             Assumption: the Options and Source model are populted before this method is called
             </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Deployment.Data.RipReplaceStrategy.AddToListIfNeeded(System.Collections.Generic.List{Microsoft.SqlServer.Dac.Deployment.Data.RipReplaceStrategy.GraphElem}@,Microsoft.Data.Tools.Schema.SchemaModel.IModelElement,Microsoft.SqlServer.Dac.Deployment.Data.RipReplaceStrategy.GraphElem)">
            <summary>
            This method adds new element to the list of schema-bound objects we're about to drop and re-create.
            If the input element is not present in the list, we create a new graph node and establish
            parent-child relationships. If already in the list, we just update parent-child relationship lists.
            </summary>
            <param name="list"></param>
            <param name="modelObject"></param>
            <param name="child"></param>
            <returns></returns>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Deployment.PlanExecutor">
            <summary>
            Executes a deployment plan against the specified target database
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceException">
            <summary>
            Thrown by the DeveloperInstanceManager 
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceManager">
            <summary>
            This class encapsulates dealing with the Serverless SQL Express technology SqlStudio uses
            as a default destination for database project deployment 
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceManager.CreateLocaldbInstanceName(System.String)">
            <summary>
            Returns a localdb instance named based on the specified named.  The returned
            name is in the format of (localdb)\namePrefix
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceManager.CreateInstance(System.String)">
            <summary>
            If necessary creates and then starts the specified instance and returns a connection string to connect 
            to master on the specified localdb instance.
            </summary>
            <returns>
            Returns a connection string to connect to the database.  Will throw DeveloperInstanceException
            if localdb is not installed or if an error occurs during creation of the connection string
            </returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceManager.DetachDatabase(System.String,System.String)">
            <summary>
            If the specified database exists on the instance detach it from the instance.  
            Any connections/transactions open on the database will be closed as part of the detach.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceManager.StopInstance(System.String)">
            <summary>
            Stops the specified instance if it is running
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceManager.TeardownInstance(System.String)">
            <summary>
            Tears down an instance by stopping and then deleting the specified instance.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceManager.CreateConnectionString(System.String)">
            <summary>
            Helper method that creates a connection string to 
            connect to the specified data source
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceManager.ThrowCouldNotSetup">
            <summary>
            Helper method that throws the well known "could not setup" exception
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceManager.CreateSafeInstanceName(System.String)">
            <summary>
            Helper to create a safe name prefix
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceManager.DoesDatabaseExist(System.Data.SqlClient.SqlConnection,System.String)">
            <summary>
            Helper to check if a database exists
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceManager.TestLocaldbConnectivityInstalled(System.String)">
            <summary>
            If not already tested, tests whether the localdb connectivity fix has been 
            installed.  This is a QFE ontop of .net framework 4.0.  It is assumed that the
            instance has already been started.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceManager.SetupLocalDBInstance(System.String)">
            <summary>
            Sets up the LocalDB instance.  Returns named pipe if successful
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceManager.BeginNewDatabaseWarmup(System.Data.SqlClient.SqlConnectionStringBuilder,System.String)">
            <summary>
            Helper method to "warmup" a new database after it has been created/attached
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceManager.BuildCreateDatabase(System.String,System.String,System.String,System.IO.DirectoryInfo,System.IO.DirectoryInfo)">
            <summary>
            Creates an AST that represents a create database statement to create a database
            on a serverless instance
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceManager.BuildDetachDatabase(System.String)">
            <summary>
            Builds an AST that will set the specified database to single user and detach it.
            </summary>
            <param name="dbName"></param>
            <returns></returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceManager.BuildDropDatabase(System.String)">
            <summary>
            Creates an AST to drop a database
            </summary>
            <param name="databaseName"></param>
            <returns></returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceManager.ExecuteScript(System.Data.SqlClient.SqlConnection,Microsoft.SqlServer.TransactSql.ScriptDom.TSqlScript)">
            <summary>
            Helper method that executes a set of batches against provided connection
            </summary>
            <param name="conn"></param>
            <param name="script"></param>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceManager.LocalDbInstalled">
            <summary>
            Returns true is localDb is install on the user's machine
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceManager.LocalDbWrapper">
            <summary>
            This class encalsulates management of LocalDB instances via the native control .dll
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceManager.LocalDbWrapper.Create(System.String)">
            <summary>
            Creates a new LocalDB instance using the specified instance name
            </summary>
            <returns>
            True if the instance was created or already existed
            </returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceManager.LocalDbWrapper.Start(System.String,System.String@)">
            <summary>
            Starts the specified instance.  
            </summary>
            <param name="instanceName">The name of an instance that was already created</param>
            <param name="namedPipe">The named pipe that can be used to communicate with the instance</param>
            <returns></returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceManager.LocalDbWrapper.Stop(System.String)">
            <summary>
            Stops the specified instance
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceManager.LocalDbWrapper.Delete(System.String)">
            <summary>
            Deletes the specified instance.  
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceManager.LocalDbWrapper.VerifyInstalled">
            <summary>
            Helper method to verify that LocalDB is installed - it will throw if not installed.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceManager.LocalDbWrapper.GetFunc``1(Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceManager.LocalDbWrapper.SafeLibraryHandle,System.String)">
            <summary>
            Helper to return back the specified delegate to PInvoke the specified function
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceManager.LocalDbWrapper.TryRetrieveApiLibPath(System.String,System.String@)">
            <summary>
            Helper method that looks in the registry to find the path to the control LocalDB library.
            The control library's constract specifies that the control library must be backwards compatible
            so we always load the latest instance of the control library and then specify the version we
            are conpatible with in subsequent calls into the library.  For instance, we may load LocalDB
            control library 12 and then specify that we want version 11 in a CreateInstance call.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceManager.LocalDbWrapper.Installed">
            <summary>
            Returns true is LocalDB is installed
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceManager.LocalDbWrapper.ReturnCodes">
            <summary>
            Encapsulates return codes from LocalDB
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceManager.LocalDbWrapper.SafeLibraryHandle">
            <summary>
            Wraps the safe handle to the native library
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceManager.LocalDbWrapper.LocalDBApi">
            <summary>
            Encapsulates the delegates that define the signatures of the functions on the control library.  These
            delegates are used to successfully PInvoke into the .dll
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceManager.LocalDbWrapper.LocalDBFunctionNames">
            <summary>
            Encapsulates the names of the functions on the controller .dll
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Data.LocalDb.LocalDbInstanceManager.LocalDbWrapper.NativeMethods">
            <summary>
            Additional functions that we PInvoke to manage loading the control .dll and
            find the control function addresses.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DeployOperation.UpdateDatabaseData(Microsoft.Data.Tools.Schema.Sql.Dac.LoggingContext,System.Threading.CancellationToken)">
            <summary>
            Update's the target database's data using the model's / data that has already be created.  Assumption - this
            method is called after the controller has been initialized and used to update the target database
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DeployOperation.SnapshotConstraintState">
            <summary>
            Snapshots constraint state in the target database prior to the database being updated
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.EngineVersion">
            <summary>
            Represents an option specifying what engine version should be set or allowed.
            Currently this option is only used in operations related to Microsoft Azure SQL Database.
            
            For instance this can be used during export to define the allowed engine version to validate against
            and whether the features of the database match the capabilities of that engine version.
            
            Similarly when defining creation options for a public TSqlModel in the extensibility APIs, this can be
            used to define what the expected engine version of the model should be set to.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.EngineVersion.Default">
            <summary>
            Use the default engine version when creating the model.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.EngineVersion.Latest">
            <summary>
            Use the latest engine version when creating the model. 
            This will always attempt to set the highest supported engine version as the
            target, and will mean that the model will allow the latest set of T-SQL language
            features to be supported 
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.EngineVersion.V11">
            <summary>
            Version 11 
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.EngineVersion.V12">
            <summary>
            Version 12
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.Extensions.DacExtensions">
            <summary>
            Provides extensions for interacting with DAC packages represented by <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> or <see cref="T:Microsoft.SqlServer.Dac.BacPackage"/> instances.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Extensions.DacExtensions.GetCollationString(Microsoft.SqlServer.Dac.DacPackage)">
            <summary>
            Returns the database collation for the package.
            </summary>
            <param name="package">
            <see cref="T:Microsoft.SqlServer.Dac.DacPackage"/> containing collation information.
            </param>
            <returns>
            The SQL Server collation string, or null if the collation cannot be retrieved.
            </returns>
            <exception cref="T:System.ArgumentException">
            If the value for any of the required parameters is a null reference.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Extensions.DacExtensions.GetCollationString(Microsoft.SqlServer.Dac.BacPackage)">
            <summary>
            Returns the database collation for the package.
            </summary>
            <param name="package">
            <see cref="T:Microsoft.SqlServer.Dac.BacPackage"/> containing collation information.
            </param>
            <returns>
            The SQL Server collation string, or null if the collation cannot be retrieved.
            </returns>
            <exception cref="T:System.ArgumentException">
            If the value for any of the required parameters is a null reference.
            </exception>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Extensions.DacExtensions.SetServiceSetting(Microsoft.SqlServer.Dac.DacServices,System.String,System.Object)">
            <summary>
            Configures a setting on the supplied DacServices instance.
            </summary>
            <param name="service">The services instance to apply the setting</param>
            <param name="settingName">The name of the setting</param>
            <param name="settingValue">The value of the setting or null to remove the setting</param>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.ExtractOperation.SchemaModel">
            <summary>
            Get <see cref="T:Microsoft.Data.Tools.Schema.SchemaModel.DataSchemaModel"/> instance that represents the extracted database schema.
            </summary>
            <value>
            <see cref="T:Microsoft.Data.Tools.Schema.SchemaModel.DataSchemaModel"/> instance created from extracted database schema.
            </value>
            <remarks>
            Use this property after executing this operation to retrieve the resulting model.
            Callers must handle disposal of this instance, as its lifetime is not managed by this class.
            </remarks>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.ModelValidation.CheckForUnsupportedAzureV1SchemaForBacpacData(Microsoft.Data.Tools.Schema.SchemaModel.DataSchemaModel,System.Collections.Generic.IEnumerable{Microsoft.Data.Tools.Schema.Sql.SchemaModel.SqlTableBase},System.Threading.CancellationToken,System.Action{Microsoft.SqlServer.Dac.DacMessage})">
            <summary>
            Determine if the supplied schema model contains any elements that are problematic
            when used with a package that contains data.
            </summary>
            <param name="model">
            <see cref="T:Microsoft.Data.Tools.Schema.SchemaModel.DataSchemaModel"/> to query for elements.
            </param>
            <param name="tables">
            Enumerable of data tables.
            </param>
            <param name="cancellationToken">
            <see cref="T:System.Threading.CancellationToken"/> that can be used to indicate that the operation should be cancelled.
            Use of this object does not guarantee that the operation will be cancelled.
            </param>
            <param name="errorHandler">
            Callback invoked when problematic elements are identified.
            A <see cref="T:Microsoft.SqlServer.Dac.DacMessage"/> instance is supplied that contains the details for the unsupported element.
            </param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.ModelValidation.CheckForSecurityPolicy(Microsoft.Data.Tools.Schema.SchemaModel.DataSchemaModel,System.Collections.Generic.IList{Microsoft.Data.Tools.Schema.Sql.SchemaModel.SqlTableBase},Microsoft.SqlServer.Dac.DacLoggingContext)">
            <summary>
            For platforms that support row-level security the exported rows set may be incomplete if the user
            is exporting a package under a database account that doesn't have enough privileges. Issue a warning
            so users can be aware of this situation
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.ModelValidation.CheckForUnmaskPermissionsNeeded(Microsoft.Data.Tools.Schema.SchemaModel.DataSchemaModel,System.Collections.Generic.IList{Microsoft.Data.Tools.Schema.Sql.SchemaModel.SqlTableBase},Microsoft.SqlServer.Dac.DacLoggingContext)">
            <summary>
            For platforms that support data masking the exported rows set may contain some masked data if the user
            is exporting a package under a database account that doesn't have enough privileges (UNMASK permission).
            Issue a warning so users can be aware of this situation.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.ObjectType">
            <summary>
            Defines object types that exist in SQL Server.
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.Aggregates">
            <summary>
            Aggregate objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.ApplicationRoles">
            <summary>
            Application role objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.Assemblies">
            <summary>
            Assembly objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.AssemblyFiles">
            <summary>
            Assembly files
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.AsymmetricKeys">
            <summary>
            Asymmetric key objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.BrokerPriorities">
            <summary>
            Broker priority objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.Certificates">
            <summary>
            Certificate objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.ColumnEncryptionKeys">
            <summary>
            Always Encrypted column encryption key objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.ColumnMasterKeys">
            <summary>
            Always Encrypted column master key objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.Contracts">
            <summary>
            Contract objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.DatabaseOptions">
            <summary>
            Database options
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.DatabaseRoles">
            <summary>
            Database role objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.DatabaseTriggers">
            <summary>
            Database DDL trigger objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.Defaults">
            <summary>
            Default objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.ExtendedProperties">
            <summary>
            Extended property objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.ExternalDataSources">
            <summary>
            External data source objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.ExternalFileFormats">
            <summary>
            External file format objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.ExternalTables">
            <summary>
            External table objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.Filegroups">
            <summary>
            Filegroup objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.FileTables">
            <summary>
            FileTable objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.FullTextCatalogs">
            <summary>
            Full-text catalog objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.FullTextStoplists">
            <summary>
            Full-text stoplist objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.MessageTypes">
            <summary>
            Message type objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.PartitionFunctions">
            <summary>
            Partition function objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.PartitionSchemes">
            <summary>
            Partition scheme objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.Permissions">
            <summary>
            Permission objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.Queues">
            <summary>
            Queue objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.RemoteServiceBindings">
            <summary>
            Remote service binding objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.RoleMembership">
            <summary>
            Role membership objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.Rules">
            <summary>
            Rule objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.ScalarValuedFunctions">
            <summary>
            Scalar valued function objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.SearchPropertyLists">
            <summary>
            Search property list objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.SecurityPolicies">
            <summary>
            Security policy objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.Sequences">
            <summary>
            Sequence objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.Services">
            <summary>
            Service objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.Signatures">
            <summary>
            Signature objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.StoredProcedures">
            <summary>
            Stored procedure objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.SymmetricKeys">
            <summary>
            Symmetric key objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.Synonyms">
            <summary>
            Synonym objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.Tables">
            <summary>
            Table objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.TableValuedFunctions">
            <summary>
            Table valued function objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.UserDefinedDataTypes">
            <summary>
            User defined data type (non-CLR) objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.UserDefinedTableTypes">
            <summary>
            User defined table type objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.ClrUserDefinedTypes">
            <summary>
            User defined type (CLR) objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.Users">
            <summary>
            User objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.Views">
            <summary>
            View objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.XmlSchemaCollections">
            <summary>
            Xml schema collection objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.Audits">
            <summary>
            Audit objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.Credentials">
            <summary>
            Credential objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.CryptographicProviders">
            <summary>
            Cryptographic provider objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.DatabaseAuditSpecifications">
            <summary>
            Database audit specification objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.DatabaseEncryptionKeys">
            <summary>
            Database encryption keys
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.DatabaseScopedCredentials">
            <summary>
            Database scoped credential objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.Endpoints">
            <summary>
            Endpoint objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.ErrorMessages">
            <summary>
            Error message objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.EventNotifications">
            <summary>
            Error notification objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.EventSessions">
            <summary>
            Event session objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.LinkedServerLogins">
            <summary>
            Linked server login objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.LinkedServers">
            <summary>
            Linked server objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.Logins">
            <summary>
            Login objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.MasterKeys">
            <summary>
            Master keys
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.Routes">
            <summary>
            Route objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.ServerAuditSpecifications">
            <summary>
            Server audit specification objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.ServerRoleMembership">
            <summary>
            Server role membership objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.ServerRoles">
            <summary>
            Server role objects
            </summary>
        </member>
        <member name="F:Microsoft.SqlServer.Dac.ObjectType.ServerTriggers">
            <summary>
            Server DDL trigger objects
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Operation.#ctor(System.String,System.Action{System.Object,System.Threading.CancellationToken})">
            <summary>
            Construct a new instance of the <see cref="T:Microsoft.SqlServer.Dac.Operation"/> class.
            </summary>
            <param name="caption">
            String describing the operation, suitable for display to user.
            </param>
            <param name="action">
            Delegate to invoke when the operation is executed.
            
            <para>
            The first parameter of the delegate is an opaque object that represents the operation.
            A concrete example of its usage is as the sender for events fired (indirectly) by this operation
            in order to correlate these events to the operation.
            </para>
            <para>
            The second parameter is a nullable <see cref="T:System.Threading.CancellationToken"/> that can be used
            to signal that the operation should be cancelled.
            </para>
            </param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.Operation.ToString">
            <summary>
            Return string representation of this instalce.
            </summary>
            <returns>
            String caption for this <see cref="T:Microsoft.SqlServer.Dac.Operation"/> instance.
            </returns>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.OperationContext.OperationCancelled">
            <summary>
            Get boolean that indicates whether any operation associated with this context reported a cancellation.
            </summary>
            <value>
            True if an operation reported being cancelled; otherwise false.
            </value>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.OperationResources">
            <summary>
              A strongly-typed resource class, for looking up localized strings, etc.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.OperationResources.ResourceManager">
            <summary>
              Returns the cached ResourceManager instance used by this class.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.OperationResources.Culture">
            <summary>
              Overrides the current thread's CurrentUICulture property for all
              resource lookups using this strongly typed resource class.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.OperationResources.BacpacModelValidationCaption">
            <summary>
              Looks up a localized string similar to Validating schema model for data package.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.OperationResources.CreatingDeploymentPlanCaption">
            <summary>
              Looks up a localized string similar to Creating deployment plan.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.OperationResources.DeployCaption">
            <summary>
              Looks up a localized string similar to Deploying package to database.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.OperationResources.DisablingIndexesCaption">
            <summary>
              Looks up a localized string similar to Disabling indexes.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.OperationResources.EnablingIndexesCaption">
            <summary>
              Looks up a localized string similar to Enabling indexes.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.OperationResources.ExecutingDeploymentPlanCaption">
            <summary>
              Looks up a localized string similar to Executing deployment plan.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.OperationResources.ExportCaption">
            <summary>
              Looks up a localized string similar to Exporting data from database.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.OperationResources.GenerateDeployReportCaption">
            <summary>
              Looks up a localized string similar to Generating deployment report.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.OperationResources.GenerateDeployScriptCaption">
            <summary>
              Looks up a localized string similar to Generating deployment script.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.OperationResources.GenerateDriftReportCaption">
            <summary>
              Looks up a localized string similar to Generating drift report.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.OperationResources.HistoryUpdateDisabled">
            <summary>
              Looks up a localized string similar to The dac history table will not be updated..
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.OperationResources.ImportCaption">
            <summary>
              Looks up a localized string similar to Importing package schema and data into database.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.OperationResources.PackageCaption">
            <summary>
              Looks up a localized string similar to Packaging model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.OperationResources.ProcessingTable">
            <summary>
              Looks up a localized string similar to Processing table: {0}.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.OperationResources.RegisterCaption">
            <summary>
              Looks up a localized string similar to Registering metadata for database.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.OperationResources.RemoveLoginsMessage">
            <summary>
              Looks up a localized string similar to Removing references from users to logins.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.OperationResources.ReportDeploymentPlanCaption">
            <summary>
              Looks up a localized string similar to Reporting deployment plan.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.OperationResources.ResolverCaption">
            <summary>
              Looks up a localized string similar to Resolving references in schema model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.OperationResources.ReverseEngineerCaption">
            <summary>
              Looks up a localized string similar to Extracting schema from database.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.OperationResources.TranslateCaption">
            <summary>
              Looks up a localized string similar to Translating package.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.OperationResources.UnregisterCaption">
            <summary>
              Looks up a localized string similar to Unregistering metadata for database.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.OperationResources.ValidationCaption">
            <summary>
              Looks up a localized string similar to Validating schema model.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.OperationResources.VerifyingDeploymentPlanCaption">
            <summary>
              Looks up a localized string similar to Verifying deployment plan.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.OptionDescriptionAttribute">
            <summary>
            Customizes the description of an option.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.OptionDescriptionAttribute.#ctor(System.Type,System.String)">
            <summary>
            Creates a description for an option.
            </summary>
            <param name="resourceClassType">The resource class to extract the resource string from.</param>
            <param name="resourcePropertyName">The resource name to extract.</param>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.OptionDescriptionAttribute.Description">
            <summary>
            The description of the property.    
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.DeploymentPropertyAliasAttribute">
            <summary>
            This class supports the product infrastructure and is not intended to be used directly 
            from your code.
            Defines an alias for the deployment property when used for commandline property overriding
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.DeploymentPropertyAliasAttribute.#ctor(System.String)">
            <summary>
            Defines an alias for a property.
            </summary>
        </member>
        <member name="P:Microsoft.SqlServer.Dac.DeploymentPropertyAliasAttribute.Alias">
            <summary>
            Returns the alias
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.OptionDisplayNameAttribute">
            <summary>
            Customizes the display name of an option.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.OptionDisplayNameAttribute.#ctor(System.Type,System.String)">
            <summary>
            Creates a display name for an option.
            </summary>
            <param name="resourceClassType">The resource class to extract the resource string from.</param>
            <param name="resourcePropertyName">The resource name to extract.</param>        
        </member>
        <member name="P:Microsoft.SqlServer.Dac.OptionDisplayNameAttribute.DisplayName">
            <summary>
            The display name for the property.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.IPackageSource">
            <summary>
            Encapsulates the sourcing of package content, 
            and exposes methods to instantiate various types of objects used to read the package content.
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.IPackageSource.OpenSqlPackage">
            <summary>
            Opens a readonly <see cref="T:Microsoft.Data.Tools.Schema.Sql.Build.SqlPackage"/> for the source
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.IPackageSource.OpenSqlPackageForUpdate">
            <summary>
            Opens a <see cref="T:Microsoft.Data.Tools.Schema.Sql.Build.SqlPackage"/> for the source, with the
            package set to an updateable state
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.IPackageSource.GetInputStream">
            <summary>
            Return a stream that can be used to read the package content.
            This stream must be disposed when it is no longer needed.
            </summary>
            <returns>
            <see cref="T:System.IO.Stream"/> used to read package content.
            </returns>
            <remarks>
            This method is a workaround for creating <see cref="T:Microsoft.Data.Tools.Schema.Sql.Dac.Data.BacpacPackage"/> instances from streams.
            It should not be used to create objects that are returned from other methods in this interface.
            </remarks>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.ReportMessageOperation">
            <summary>
            Reports all DataSchemaErrors added to an ErrorManager through the supplied MessageHandler.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.UnownedStream">
            <summary>
            Decorator for <see cref="T:System.IO.Stream"/> that avoids disposal of the underlying stream.
            </summary>
        </member>
        <member name="T:Microsoft.SqlServer.Dac.SqlConnectionStringBuilderHelper">
            <summary>
            Public class to support SQL Authentication configuration for DacFx
            </summary>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.SqlConnectionStringBuilderHelper.IsAuthenticationSupported">
            <summary>
            Checks whether "Authentication" is supported in the runtime environment
            The keyword "Authentication" is not supported until .Net4.6. If user uses a version of .Net less than 4.6, DacFx should avoid using "Authentication"
            </summary>
            <returns>true if "Authentication" is present in current .Net version</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.SqlConnectionStringBuilderHelper.SetAuthentication(System.Data.SqlClient.SqlConnectionStringBuilder,System.Data.SqlClient.SqlConnectionStringBuilder)">
            <summary>
            Set Authentication value from source builder to the target builder
            </summary>
            <param name="sourceBuilder">the source connection string builder</param>
            <param name="targetBuilder">the target connection string builder</param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.SqlConnectionStringBuilderHelper.SetAuthentication(System.Data.SqlClient.SqlConnectionStringBuilder,System.String)">
            <summary>
            Set Authentication using reflection
            </summary>
            <param name="sbConnectionString"></param>
            <param name="authenticationType"></param>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.SqlConnectionStringBuilderHelper.GetAuthenticationString(System.Data.SqlClient.SqlConnectionStringBuilder)">
            <summary>
            Get Authentication as string from connectionStringBuilder
            </summary>
            <param name="connectionStringBuilder"></param>
            <returns></returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.SqlConnectionStringBuilderHelper.IsAuthenticationSpecified(System.Data.SqlClient.SqlConnectionStringBuilder)">
            <summary>
            Determines whether a connection string builder has a value specified for the Authentication property.
            </summary>
            <param name="connectionStringBuilder">A connection string builder that might have authentication specified.</param>
            <returns>True if the authentication property is supported by the connection string builder and the authentication property is specified. False otherwise.</returns>
        </member>
        <member name="M:Microsoft.SqlServer.Dac.SqlConnectionStringBuilderHelper.GetConnectionStringWithAlwaysEncryptedSetting(System.Data.SqlClient.SqlConnectionStringBuilder,System.Boolean)">
            <summary>
            Sets AlwaysEncrypted setting in connection string if it is available in the .NET framework and returns the updated connection string
            No-op if there is not .NET 4.6
            </summary>
            <param name="connectionStringBuilder">A connection string builder that might have always encrypted setting specified.</param>
            <param name="enableAlwaysEncrypted">True, to enable always encrypted setting, else False</param>
            <returns>connection string with Column Encryption Setting set to Enabled or Disabled based on enableAlwaysEncrypted</returns>
        </member>
        <member name="T:AssemblyRef">
            <summary>
            Sets public key string for friend assemblies.
            </summary>
        </member>
        <member name="F:AssemblyRef.ProductPublicKey">
            <summary>No signing</summary>
        </member>
    </members>
</doc>
tools\dbatools\bin\smo\Microsoft.SqlServer.DataProfiler.dll
md5: FA8A67B27475A0EAB82E363FC75CA883 | sha1: 107383155087E4060F1ACB978AA55EE5E260A1EE | sha256: F7280FF10B081FBC00CC52A995E0221B518263AA2E48BF881D31B5F847FF6B5C | sha512: FA223C07D21AEDC75D15BEFC9A56B3ED92517A614A48A9CD7CE467EE7AE09124E1BCFBCA525CBF95C97522B18DF9CDD674E5CB1F918B5D255AF18B0A997A5DFA
tools\dbatools\bin\smo\Microsoft.SqlServer.DataProfilingTask.dll
md5: 73150668DDFFF82CEE04517DD8766BBA | sha1: 4FD69286EA548A65341B91D0A2C685F4339517ED | sha256: 426D104BAAB60E1240DA8483034CA1A73F6D1830FDAC2D0C04F1372B7EDFACD7 | sha512: A3E49D65D84CC0DAE6C49FDEE958CB6FB0E54EAAB49B68990EB1C451F93918F0AB7726AEEDCDD44E79F2279750B3E58A375A3893A524CF689AD952764EF5F237
tools\dbatools\bin\smo\Microsoft.SqlServer.Diagnostics.Strace.dll
md5: 60A5B58D4811F7B789C46EA4A60C5D44 | sha1: E6AAE343BC4773003B778BF5093B614CDAE1A780 | sha256: 07513615219EC9F1D35F569F74F182F4A5DA7F993E7422EC3EBDA7236CC929DA | sha512: 8E2825B6A12AC3EBBF9182F37421B33139E71D1774F902A12D813F706D727E1ACCD1F0CB49972F0433CF91DD76D86667E3738AE8EDB22805763647BC48CCF9FD
tools\dbatools\bin\smo\Microsoft.SqlServer.Dmf.Adapters.dll
md5: C1E211CF2EB92075AFBD0365E4DEAF8E | sha1: D17CE7A84006433891C4E17F8C08B17CB93A7BDD | sha256: 538A06B3A711E72767299DE175A184726070ED89964C63F98B8A631AD8EFC29A | sha512: 240332FF8DB5CFE34E1CB1473CB00D5BDB10BA6ED377B29312A577816CCDB5D9CCBADBA9089D5FE83D1B6749639B5423CC0D176A87576E619017AAEAFB74736E
tools\dbatools\bin\smo\Microsoft.SqlServer.Dmf.Common.dll
md5: F97ED27E3DFF82BB6916D9A7FCF4C96B | sha1: 619DD995541E9332B84B8ED78E67BA54EDCA1996 | sha256: 4A89D16E5729C78D05889392F92855B39B98CBABDB890593088EE70899E886DD | sha512: 13E9A1E31C20CA8DA65477016B686F22EE60801DDB81F20CE195D6521336231C45CB0505FCC162DEB6E4EB5FC97C1126937B935F90A6112B67BAC6EED72ABA86
tools\dbatools\bin\smo\Microsoft.SqlServer.Dmf.dll
md5: 8996E9E85A56CD6BE47185CB7EA7E054 | sha1: 040E099816CCD5C6894552B1F7FCF7D5938A86F9 | sha256: 359D48602701B565F9D78E88BDE599A8E57D900E80C4FF215EC953655C71F312 | sha512: 773726B775884F633CCCEFD9D4B44D9F004460EC2F3E6CDFC59B65C4AD80D1CEF3824B125B33BCC7AD1E15651D528C29618CCF1C5D8C8B58203DD0216FEDB71F
tools\dbatools\bin\smo\Microsoft.SqlServer.DmfSqlClrWrapper.dll
md5: 76CB51F60C62AED3179136345567052C | sha1: E5A923168B227C9161DB6EB325EFBBA98A29A593 | sha256: F1B48F092DD96FDCA48BF6D16CEA5087327EEA962D279A30CD1B221ECB295E97 | sha512: 6782765C846F97365DAE59F792851D555F83F6F3A8F8F754D827E59EA8BAF537B5D3D42156C4BDC16298EC06A39E3ABA27D8FCB2FE92581DF8F9A6922917A983
tools\dbatools\bin\smo\Microsoft.SqlServer.DMQueryTask.dll
md5: 5B504D36B35B52AA9E569FBC42CDBA55 | sha1: 0D72590285854D151C71FB512D3917B43A3D2EBD | sha256: 327BC784529F3B2B03E69B79A96432EEA47C2629AE80E76FDCC3D3E45205F822 | sha512: D674BFD5EE8E52FD112BE9984C4E1D003695D4C004EEAB0C54A0B5E99046A3F9D4250E1E7FA03FDC0DC5BA483C15D51A7B7CA0453D279FC0FB26FF2ED7FAFAA2
tools\dbatools\bin\smo\Microsoft.SqlServer.DTEnum.dll
md5: 9A06FD4ADEFB34648C9AC57919BF98CD | sha1: 94408C1C9AB7FEA481FE42C28538ADC09CE30112 | sha256: 966E80FBBBAF21ED8117DDAEB063D7199E1D3FC68096B486F4D7C544EA2356C5 | sha512: 38D7736AAACC9446EC74D74C74495B9E1DDC72C4CE00C1ECE54A8598153A03B8BED01937988073758FE6E314249DAC1459E5004A38B34D82AE4F5226C4E777A2
tools\dbatools\bin\smo\Microsoft.SqlServer.Dts.Design.dll
md5: CA7C579E70E957860CA79E3F3437665A | sha1: DD9ACA8DA7548A6137586FAA1C0C4633A20230D0 | sha256: 165582E0E3814B35B45C017D06AFFB18EAF4957CD7BCBF87E3E865B16AC049DF | sha512: 7D8EDD7059620EB2F3EF2E15F758D55FDA2B9BD0B1D1620FC22848CA95E45A78B5D04C4A8FA902D707191DE53D91FA6DF9542B2FBE31865C8A7A7A4FF06E893A
tools\dbatools\bin\smo\Microsoft.SqlServer.Dts.DtsClient.dll
md5: 8F6D455B7B412CEC1CF25564034E4A67 | sha1: 78B489E2FE7813999AC5E1C48B7C0B7B73B55B6F | sha256: F7427E4ABC6159A3F8AEC6FDF0433C776275C914DC13A0E82755E37B4276D639 | sha512: 950A9BC4D6C47264DEF4F2A6948D3B77B6CDE5D3FC339AA1470B598EB0C54FBA23390B7F4D5D183AF752507036685382C3E139F02A5D318D53CF7C54E2E8F827
tools\dbatools\bin\smo\Microsoft.SqlServer.DtsMsg.dll
md5: 9B34E393035B78E91A59EE7F7255D4C4 | sha1: 644B4804064C69D7BA1DEB528ED23C9351258AF9 | sha256: 3D4A1D0CD5F52DBFA15C449D69B311640A93069887E282A1DA583821D48EF75A | sha512: 8642620203F227645A276EA11C75935354B130E4F580986CCF43F41DCB79E27C5AC04D180EC2F02759884A434DE95728F1F5B514D1AD5B8EF800AD61CF6D399A
tools\dbatools\bin\smo\Microsoft.SqlServer.DTSPipelineWrap.dll
md5: 9F27EAF86D5B8EA6FD2283C2D37C0963 | sha1: 0A1C2E88B5C0DBC4ADAA15B32C5DA4AD34AFFD59 | sha256: A24766897C71E7A2557812557078803F4E85928F6B6D561E88B36BB107F8C414 | sha512: 713CEF0AC7A5B2694E6AF49B733176C0B387D60B4CC501D35BDB0A3D7796BDA038E63DFF8279464C21F6783631E739457DED8BA874AEC3683E484BDFC5BFCD07
tools\dbatools\bin\smo\Microsoft.SQLServer.DTSRuntimeWrap.dll
md5: 1808025F30B766A42C6557FDD8E4CE31 | sha1: 2B2D33D9ED5CA009509BA436ABF5F39B36761593 | sha256: 5A9B447C8C58DAF80F1C0ACE28D44F71C4F77AAEF4790C6D136806BB45928559 | sha512: 81E89953B97C5B5CE35515B5D02659FD6A0DEC0B3432452AFC933BE9923AF18304B2F875509587970A4DAB3C0A0124A78FB525919792FA9DAB75F78F47D4110C
tools\dbatools\bin\smo\Microsoft.SqlServer.DtsServer.Interop.dll
md5: 217726D0FB27A5718769AF62538C1111 | sha1: E347612E002DD666D45BA10A8217D2A0847C2308 | sha256: 5633029110E2265F35B21BDB4A4A2561C260321A8C2F12C3BFC5A437E0B96A98 | sha512: 52CA20C1FF7A0E4294FD8DDCC859051E6296B9B4374E381BFC3990765F1B4998418EC361887518896D478D1FA41ABFBA7B7B3C5ABDACCBFD6220D355D6737E02
tools\dbatools\bin\smo\Microsoft.SqlServer.DTSUtilities.dll
md5: AD1BE68905F4534D162E320CD3DC6016 | sha1: 45CF7277F1B963CE09E96D5119916325F009686D | sha256: E570C36BB270FA313C8CDA8EFDB5A8522078E3520ACB6F2A653B27203D6D5FAA | sha512: 31488E7D62477B5B2E5378968E0CCD80B8D82A0A73147E0814EDDE60DA10C9C0D246CC8A0493B3073A014304DDC37BEF247E413A31DC2C87BD15769B45C3CC28
tools\dbatools\bin\smo\Microsoft.SqlServer.Edition.dll
md5: 9FBE00E415F6DBAA5BCA8A9FDD2506A9 | sha1: 0DE63132874D6997749AA289B31B0820F1020216 | sha256: 0D692345743EF20EB95CF09F6318584CC70A8350A8D39353E829233CA6B3992A | sha512: FB99B562980DFEEFC4810BACBA1D7379FC74C45B7E9E8B9F244F4C482A634601AD7A7511A88515AEBE85268A980E787BD889A710C5A65B8D629D736476B1C714
tools\dbatools\bin\smo\Microsoft.SqlServer.ExecProcTask.dll
md5: A5CCBC5F0122BF766E7251B9EA60C79E | sha1: 545C929E4ADF837F983D4CA217459E4D4988CF1D | sha256: E70134A7426A92D3FBB7A90614941809BFDDE70C312BF7A2F4AEC3D50C16F55B | sha512: 6E6F6089A15E1BA180FE5E1D0D67AD293AE95260A25C75B9D2E050B9FDFF374E215C790009F14D7053854436CF0A405DA9A923CCF34FC0E2F91CDF708C21A6E4
tools\dbatools\bin\smo\Microsoft.SqlServer.ExpressionTask.dll
md5: BAD0D444806AF055F797AB1C191D04A0 | sha1: F85CA748805CBECFFEDA2702325F307A16543586 | sha256: A2FE6BBEE83CD732B03203D646CD43C5D3B5C8DD21D084355AE305DF53B72E51 | sha512: 43255B8E0CE3093C6F2C1D6BDDDA278E6D8BE77F6ED9D1953F0FA053C91AD3DBBC0EA7C9E877BE2A7D0D6B999540E7819094D99D508398552BD5B7208D2E816F
tools\dbatools\bin\smo\Microsoft.SqlServer.FileSystemTask.dll
md5: 90C2C3217432087A5605F68DC8E87BC4 | sha1: 996C90F40E8E12BA0FF0528CCEA7D6FEEBFDB100 | sha256: 9D0E7C23AF77B949B3BD2425101C3923BB55E77E4210D0CEDC46E4E2C28D2C75 | sha512: D00F67A5907D9977A9C011065D3FDF47EB8E6AE100D673BDC126ACADA407CAA630EEB7C5909FDAC9283CD866BA0B4EF0600EB05F839408FBF231D31DE6319E0F
tools\dbatools\bin\smo\Microsoft.SqlServer.ForEachADOEnumerator.dll
md5: 627297A014A4522B16821CB25CDFB70B | sha1: 13835110A730D2AB1219456F01FCED3C2A1D2B89 | sha256: AEC2982F6BF7D20B8E5E53D21E69B13D537D3860786198CFB2750DB0A3744D76 | sha512: 7A4E27FC516505C8B084B209AB863F3C0469D02C4F3C4A0E8E81227216A75F42057D4963E0848100619DE9D8631562545D3AF894FDE5F614EA01E5680DF5A5AA
tools\dbatools\bin\smo\Microsoft.SqlServer.ForEachFileEnumeratorWrap.dll
md5: B01F267ADCA1EEE8211650070701CA6B | sha1: 396F5618F61D09CFC22E79A9CCF63406B3F1AD6B | sha256: DB68A13EEE99161CB8D0CE78B6E1118066E73883E030C42C31495A39AE6AC21A | sha512: ECC6882816E334E3E677512E74EEEA3FFE25D150C883682026A4305998BDE2C479023C4EB5854803C64B5FF193408E81FAB4387D0803DA5BB7386C9ED9086F66
tools\dbatools\bin\smo\Microsoft.SqlServer.ForEachFromVarEnumerator.dll
md5: AAFB6E89CDB081BFDC78838F96D3E6A4 | sha1: 773CDFB32D60AA7EC341D7B6F1CA5CFC8FEDB463 | sha256: 8E3C22619F784921A1F1A5BED4B9B1F2D5B57DE5ACF1B2A0C31127F513AFFA3D | sha512: C3AF282190BF079DE47E4029657003889E094C545E2804EAD7F1B49F3B4AE21B2EF4440A915A019FEA4F38CC7E394C6ABD622B190487120F58A72DB7F917136E
tools\dbatools\bin\smo\Microsoft.SqlServer.ForEachNodeListEnumerator.dll
md5: C2E486A7F07F371272A385BA5EFBEF59 | sha1: CC3E5146A8A46969B1CF9DF97DA74D01332A3AE8 | sha256: 659037099AFFF1F72099887490C2C03992E485CB2CFDAAFE7F69CC4C3D259F23 | sha512: AE3A46CA7E144B7D81EA8699FFB2910AC14C02EFA775BF2209A1F73C7C7F6D40DFC185FF215684ED6E3ADD8829EDF851D987BDF13005B74553C3434F6AAEC107
tools\dbatools\bin\smo\Microsoft.SqlServer.ForEachSMOEnumerator.dll
md5: D4E9A25AC0B7C429691C94A1614B0280 | sha1: FE562E3B8A358FE5867D9C3EE8E745AFF7047243 | sha256: E472A22B108DA51FE5100D24639BECD0D7EB68E23FF04D55072FE94F03866697 | sha512: 68A6AF48FDB45CD68BEA343136D782B53E9903F32A5E6834F842EC61A0BD5BBEC6C2B334B3A46672F20AE734388A3C1BA5146D773A367A1D6426940A4F88554C
tools\dbatools\bin\smo\Microsoft.SqlServer.FtpTask.dll
md5: 941B9879FA51C15D53F5A56F3686AE83 | sha1: 68155088B96B2A9436C09EF9C900B402C17593D0 | sha256: B17A94D803407D77FC754E92556672014E1A4CA83A48276D76DE4BA4D459109C | sha512: 7C0D57DAA73B3F219C317D9DCA2C84B170734898832D3E65969869404C5A8A5A1E89E25DA48841748D6F965DED90EDDF915E1116A8B46BCF24365071C03ACF31
tools\dbatools\bin\smo\Microsoft.SqlServer.GridControl.dll
md5: 2405307D9C808607D3A6F8C5A8EE6013 | sha1: 7F542EF30F2AF0E292187974C1F36FEFAEAB770B | sha256: FE16B381576645245CE1A62EE632CC50CABB727A67C0F2474D73E98BAD8B0A28 | sha512: 8B7E9EE772FD79BE7783386B83DFF424D0D0656DA6F2653C599CDBA99155611FB2AD162184CD4F52D2297FB69A5A3B8FFFD4AB74A790C4CD4F022C94D52610CC
tools\dbatools\bin\smo\Microsoft.SqlServer.Instapi.dll
md5: 3EB1BAC30F8A982B28FC5E4EA238F2CB | sha1: 5EFAF96BB064ADA7A0C1A092922F92A13FF5CB58 | sha256: A724489361BD427B6879032302982F758882C8494BBA9732134C7BB130307005 | sha512: 492D2197A1E6301BED2997B12A354865BD9E32B208AC629F580F5D5F12EC5D55C0EF1D09AF2C17594B0B487C13D30B946D053EE67A0FBE6C5801B266D37E0FAD
tools\dbatools\bin\smo\Microsoft.SqlServer.IntegrationServices.ClusterManagement.dll
md5: AD8E65431EB8DB24CF5FF5818F2EA91E | sha1: A32F02131517E57FCB83E22836FCB69BEE616959 | sha256: CFB08F61B05A200916FB0C38BF326582F1FA417D085DEFF558C27D65744FCAF8 | sha512: 6E4F1AC61A4A376FF77E1F269FF8840DA40E49CBB6D85960FCF1777BC08A917D90BE35D85FE10E10BB38F0E4A0DEE264B92D827AE7191012D81520C2B3442D76
tools\dbatools\bin\smo\Microsoft.SqlServer.IntegrationServices.Common.ObjectModel.dll
md5: 6792E3C587306F369E5E635B6B74EFBA | sha1: 796DFC8296718042396698D757EACD1141BD592D | sha256: 7FB327A4756B618B1BC0670B10BFD871C4CE7CFC608267ED6B6912BC6FDDA28F | sha512: 809C895A3A01F27A8C1D6A82B552F4B0261009A635C6C46C8162DC15542276E49E429795C5D50B60ABAE614021C84F42A40F54235F6771A75E2CBA879768BC7B
tools\dbatools\bin\smo\Microsoft.SqlServer.IntegrationServices.ISServerDBUpgrade.dll
md5: 81246EDE8B7B4ABCA3A1E1E5BFD8E4DC | sha1: 38CB00A923BF9F895DDD5BAD9AA82D7135B55597 | sha256: E843499595D66EAAB818508EC84211735A49A7066965D5DC571FE2FA1E0ECCD8 | sha512: 475F646576E900C512BBDF2BC86C90F914FEA4F47F68951FE006FD8230F8D4E4F3BFA4198DE2C6EB62F541D2A9C86D9E040475D83821A75962F55A9741C1F296
tools\dbatools\bin\smo\Microsoft.SqlServer.IntegrationServices.ODataConnectionManager.dll
md5: 7EB877D9BABD3FACE8DFEDB2379BE334 | sha1: 7530405B39C0D8FF6FCF73AA4D2703B0F2BA2077 | sha256: BECC329BC42C35212E02A5F24655471A69CDFE091777F3D112CC3270209BB975 | sha512: 1A2E13BA56C1F492EF45493BDF86446B11E44751F1FECAD8BB1EB3CAA304EB14B043EFE3879875C8CEA660251499E9D1205AA2640F1197517C47E1605CA5E1AC
tools\dbatools\bin\smo\Microsoft.SqlServer.IntegrationServices.ODataSrc.dll
md5: 6A9E7E0416D67F882A4F9A39C98FE8A3 | sha1: 2409FF4A46857D2FA353ACDB377CF3ADFDF49701 | sha256: 193F2E1215044953528B1D371216DEADB20E8A859F1E25A22BEE81295C52A619 | sha512: 1D8C1B68EE2175789E61DFA0C68614A1E137C5AAC5E856FB06D2C1BBD1327CFA6E832E278EFFD2F640BDC430FF05BBD35EA3DF9BBABD11B96D08AF4A5603E0B1
tools\dbatools\bin\smo\Microsoft.SqlServer.IntegrationServices.Server.Common.dll
md5: D647ECCD24528FA56D7AC7BC8F2AE83E | sha1: 449020C537EC3194AAF257C1FC95D16430275263 | sha256: 12DE0550045DE4D9C9358FB8295529D293672E5B9E1C2B656A77B2D0F250D91D | sha512: 0E200918C96F4EBAAE6424F86890E256A1A78F14098C7111DC50A0D27CDC935B77D877AD816593CFDCD0BC5A1C86A02FC809A676B5C6083EAF5B8E6CD7A83005
tools\dbatools\bin\smo\Microsoft.SqlServer.IntegrationServices.Server.dll
md5: 004223AB64B373DCB3586615E3B8E4D4 | sha1: 0C4A3810F9D2866A7CEE78E3EBB2DA479EB7BDCF | sha256: E529EB3FBEBC65703A43618955FA734E85F3958A51D1BC8318D4B1234DCBF0E4 | sha512: 54EA36C5BB412EE78D8166CB3E03F7D8A9ED6CDCD9EAA98BD485A6C483B2070BCB7BD245FAEECEF6FFAD6710BAFA628879F99B189A2A26678FDE623249938022
tools\dbatools\bin\smo\Microsoft.SqlServer.IntegrationServices.Server.IPC.dll
md5: 012D68C5517F8F9C46321CA413BDA0A0 | sha1: AB98536551FD7AC656578F3DB3C7B430EC243A18 | sha256: 7E1701159A34FD657CCD8E364F973E84371A5317C23AF70735D75444A90EDAF2 | sha512: 3EBA6417A475928862CABBB05B2B1B9D351F1C67A782D5622EA117307738F2B6A4B9446EA1E6843276361B0EFB2D9AF40E27841C373593E84B2B20B6ADB52190
tools\dbatools\bin\smo\Microsoft.SqlServer.IntegrationServices.server.shared.dll
md5: 669589A39FB92BE6C172E80715F16BDE | sha1: 41E15C23E95935B59A587D0BA0BDAA563552A6F8 | sha256: 1CE7C5069F50B6F1E17CC94BFB1AA1C22D6CD240E33FF345EE09AF5A25B20A3A | sha512: 388E368C3C92700EF5E206DFD6E25CFD26CF86A500012750ED6A55AEAAF41B2FD295606222B9CEF395DB9795B73A534BE57FB04DEEA9401450A91562B806F691
tools\dbatools\bin\smo\Microsoft.SqlServer.IntegrationServices.SqlTaskScheduler.dll
md5: 85A7BAEF7FD06730AF3F01E6F0DCC6E3 | sha1: CCDC593F8DF2934C997DDF3D4596471019DC4F15 | sha256: 05DD2559C6639410472D57B1FADEDBC9CCA6EE91330ADD457F51FE8096E03268 | sha512: 8FFAC5289FBC1B653377332A5B03F0262F3CD6E761B2C1E877596607E1632F612B1E5D01B37AE588D6EA9FACC779E6A0C6DCE91AB6DD64B8A3F80A8E369B4FDE
tools\dbatools\bin\smo\Microsoft.SqlServer.IntegrationServices.TaskScheduler.dll
md5: 95F3B078B083EE46A1CD09C761B38CC8 | sha1: 1325FB4CC11A1DFFA405B274B23D9A2F0D1186AF | sha256: 64437046B67B757374662264C4A69BB1AFFA2C9F48A3AC25729716EB226E3302 | sha512: 3977034D77F146160C76758CEF6B21C27657C397294003BA66430B1F77339751DB65003B529811BC4BE31FA95362BA3857E2916439486C24C8354144766CC8C8
tools\dbatools\bin\smo\Microsoft.SqlServer.IntegrationServices.WorkerAgent.dll
md5: DB1BE51BC57A920A1677578687D1BB9D | sha1: 10E39F4C2560653F70E7DE20235A1C3F87A247D0 | sha256: CC67D9D707D0182979387A2B4576034DD744A3156E595CBD5E5EFB246CF228A1 | sha512: 7BBBF7D8313BA92DC7FB92402C5D08BB881EB3F4DA82C1BE4881B86D00525F352461DC6B15B83FC301A1E645CA5BAF9274EB2C8389F58C7B956F2C6B2AEE339F
tools\dbatools\bin\smo\Microsoft.SqlServer.ManagedConnections.dll
md5: 08AD73DCB92CFFB4362FF1EAE75A5DC1 | sha1: 69099372D1240ACF4B0B5CD478DE1A10E95C066E | sha256: 43627726A8E185DC60B424230D72F15E5ABE41ABB030D5B5DDC7E1779A7B07FA | sha512: A5C4EA691FA23C9E325C636EBBCF4CA76C975B9D0EA40FA0C678E2B44156DD6BF7CD35F2FB6A1B2E199F3332A6690A19936AA545FEB3BA6E94E8FE1224212EED
tools\dbatools\bin\smo\Microsoft.SqlServer.ManagedDTS.dll
md5: 303D4DC7C433D8F7CC5285B5009A6A23 | sha1: 70F6F58EC34021B8DF5EB4D07E45843B983A0589 | sha256: 9AF57AE6CF27451149491C6B7B53362429F950B29EFD0D9F5753D0315A5200D0 | sha512: 37F068620C063D7C5FBD2059326423E75AF165DFF9AEF6A90CBC8D77F6044C6192A4F93537E04D0982FD158B38277F07E74540C52C78C5DC1F7CF0C9F2D15A6B
tools\dbatools\bin\smo\Microsoft.SqlServer.Management.AlwaysEncrypted.AzureKeyVaultProvider.dll
md5: E7603CA9A6D52FF1B94961FDBA21E907 | sha1: 93820B1A32F4339D7ACD231EC274B185555D8003 | sha256: 8354A28C3D206D09408391C2BC2F742313D7BA00CF819AB71F459C4CBA8EFAAF | sha512: 8796BC8EC8E0D19B2DC35B9DC580117C198A87EA238846F3DCB5D73A04530C71F98B43CDD4E8A811F0424C432EB50C8B9C39FF9E22425FBF3F41D5A2760AE58C
tools\dbatools\bin\smo\Microsoft.SqlServer.Management.AlwaysEncrypted.Management.dll
md5: 218909BEBF9C6680C8272B80E138C203 | sha1: 856EE0E4CCF174B67FE7CAA67F32AF5B3D42590B | sha256: CF1E5D65439DA659F2E75B6D90BB70B3F2C41C8662748198E9BC140F8D3C5999 | sha512: 584C442B7CEE75DE2B310E89E64388CEC3215E71CE9802AECD343DA9B05AAD7C0592B12BCCB57C2B8EE03792336FE17698033E262839EA0A18C1024F5E35900F
tools\dbatools\bin\smo\Microsoft.SqlServer.Management.AlwaysEncrypted.Types.dll
md5: 48A4AB642B38CE7A622393F14CF7FE3D | sha1: AE4D13678665596300E674B42F234CB31B4C2951 | sha256: B18D0599D9F09E0BFD994B738985CFB9BC9B16222E007520866532EBC23BBAF4 | sha512: C9ED2BAA2F57922394DC6EE69D246E889454B83994E46C579751007E02B148DB5854C1C874134BAB6B4BA99FD19C5B229FB8796683A2C7AC0B3E4485EC1CEC12
tools\dbatools\bin\smo\Microsoft.SqlServer.Management.AzureAuthenticationManagement.dll
md5: 42DC8D5A51A02839A0F8CAD3193B0FDA | sha1: 7242310B52222C13120CBF8E9C2BBFC78B817BB4 | sha256: 7AC2D22FC6F209D9529CB75D41E2690AEBCBC8A50B52163FE46D370135ADB2C8 | sha512: 2292FE5EE8625F85C6FEA6F1D98CF9950C58606984E3ADEBA7AF4316EC164216B9E3BEAE02A5687BEA0BF7329C80867316267B3F2C508509D8B4D0D21C43D374
tools\dbatools\bin\smo\Microsoft.SqlServer.Management.CloudAdapter.Client.dll
md5: C4E8BDA8352BAFC6479935E5BE681DE1 | sha1: D749BAB24DEA51365FC552F0DF27F7FBF7CC8C8D | sha256: A835EFA42DF0A03511F2000074FA2797C1CC0E191ADA5CC6855F1DD5BE718297 | sha512: 74921AB418C54B830F93C6CF9D75D5C6DF65A87A50FF00F6FD9B1E8BB3946FFB2DB1F45C536C8ED971E0E5633D807B8E770F58322271C8391C9C60C063CF5136
tools\dbatools\bin\smo\Microsoft.SqlServer.Management.CloudAdapter.Data.dll
md5: 39833C208E63430932C4610B13609E74 | sha1: 3D788DA305935DCCC55D37A04E0A5F55642F7101 | sha256: B540D094ED65DAEA033124DCD59644501C33E5C675BA02658C020875E9036CEA | sha512: 4DF23A3A0C2E43C30D85FCF5626F688C1BBA58E1945097887F9FE86D40CBA1DF6D05B281DFB100AEDB3DE2185C9C3A6FE039751DD96AAC04719C6843B57A0563
tools\dbatools\bin\smo\Microsoft.SqlServer.Management.Collector.dll
md5: 5E8707E3C01D543CB2CC187C9D751B13 | sha1: 48F677C285B243B3A9CB7E7F0A669885A36AEE30 | sha256: EA358BD759BF39E7AF61847FFD212D9F3F6792365818540FE4B7D34786B4D021 | sha512: DC42259F540C80C539B64B315A5AA721F57731B5B72C6A9C7F7AEA86001212C1B695A9575C452BB8B07983545945E58A1AD8CE3A9E741EA4B76952E2B94D2A88
tools\dbatools\bin\smo\Microsoft.SqlServer.Management.CollectorEnum.dll
md5: F694E2DC474791E6DCF7AE2B45C87166 | sha1: 513ACDA265B9CA1737596876B5C7C6AF1A910DCA | sha256: B2CED4D657A9C6B088B34AFCF08C7861CB2D3C0AB86368E4CE29B071143A6B9A | sha512: 19ED17B7F89FB9C4A069DBDA9C039A9CB5BF5E0D2EF08A5400737A553371D8D75F8CC798B3BDD3FB776494F37323792A69A3501CD4AAD13D77B0EEE6802A1096
tools\dbatools\bin\smo\Microsoft.SqlServer.Management.CollectorTasks.dll
md5: A9AAB685F99868C1CCA8AC1A9E19D127 | sha1: B1DEB8B7C637668615D6E90585605C68E72DC476 | sha256: C6598F1DAFF84987055CD94EAE05739756CEC33D043CEBC7B943E3E2152FCE07 | sha512: 3EB9433A8962EEBD3334CE6E9EBD3D8BC8DF35E0B777A7CB6C7F3A40F5C3E22CB1FCFC6B5B5C87836DD460F3563D08BB7F9C628219B456AB8C51A39B26D22EFD
tools\dbatools\bin\smo\Microsoft.SqlServer.Management.Dac.UniversalAuthProvider.dll
md5: E0B91845A09F9781E17E4D97A2DDDDB1 | sha1: 805CDED255C6E96405300DD38282DFB4448E42A8 | sha256: 118B5F900233D8829B441E2DF8D8112BD8D80D6EF01425288AF9516D582803A0 | sha512: F02AA7E0B8164C60056A59C23547C0AE3C1FE6CE8C6B2DB298FCF84DA4B7066126415497B5149BB4DB3DE13304744A6B4FED5CF5F9B4C639E89F33B725724A8B
tools\dbatools\bin\smo\Microsoft.SqlServer.Management.HadrDmf.dll
md5: 668EDCC24CA7631D69A3AFD2A397FF64 | sha1: 4763D19FE90E5E3F60D2CCF1ECF68675A5254ACC | sha256: 43C3A35AFD6AE0E3687FCF10C68BDF77FB05867DE38BFC76BC23583B217D3B35 | sha512: E3A7DA94F4D1BAB7E689970E0A933737A870ED586D14F0203EBC62CC54CDFA4A8C2290D2DABF654F1D35AAA134DB7C768B8789658FDE8D294B0BC4B4CE2CDA2D
tools\dbatools\bin\smo\Microsoft.SqlServer.Management.HelpViewer.dll
md5: 021A6F00826A744FCE3347B4023CEE95 | sha1: 39F80C86CBC5837470EEDBD22C6887EEF53D720C | sha256: 7C7F8108DEB0D56AA89669CD3324E95291F58C38CF35BB26C4374F361A23CBD1 | sha512: D552165129613AA377859CFD6E9DF424FF883CCDB627B78C075B658279A43C1B6D15979B9C1852EEE1CBD6532A227DF0DAC763D81CAF74137F921B0796E30501
tools\dbatools\bin\smo\Microsoft.SqlServer.Management.InMemoryOLTPMigrationAdvisor.dll
md5: 27744AA6BD5F100B8A80F1AFA3921EBE | sha1: 4BA98603E4D11DB78D51DD0547E9AFF49DC2DE78 | sha256: 19F283BE677540BF8889C71806BB66EA8D3E59B92EF6F6058E66F7CF9B3839BB | sha512: CE8F9663B537AD710D97992F53829DB11C64DD6259DB060289C6BCF566FF3253511D5F242058D084AA1B08AE3B0EE6CFAF7F65AC5F3C8B5C18B1112CED2B2B27
tools\dbatools\bin\smo\Microsoft.SqlServer.Management.IntegrationServices.dll
md5: E3DDA0352B0088E2E98172332F431E6E | sha1: CF97B69757F6A4646F655B07DF305D29636BCE9A | sha256: 4DCE941B84109D54DD51461A548BFD8BB24E278A5C29CB125BE021913662DF29 | sha512: DB9B8DB6E0946F1C27D16C0E5EEFD56458185CFA01AC4F54CC3EE5FB721BC296B675C9BA0764DF46D3CD10040B144E81290E1F11CA5E64C344461EEBE4B439FD
tools\dbatools\bin\smo\Microsoft.SqlServer.Management.IntegrationServicesEnum.dll
md5: 277465E94385E56877A2DD957576CB64 | sha1: 15A7EB00B77509DCF0536C1630DD4DA14AAF8B44 | sha256: DB4546E7CAD2261223EA2C47CC4BAAE99F694085D9348F370DF1CADF46FA0075 | sha512: D2903B3BDF1C96CC35B29A379482EB453F3F36C4BF8228C38412CA6CA7FC2F8FE5F61ADEEF4AC37DAE239E8AF73A86A8E6108630B7D71A7AFB35DD466A609610
tools\dbatools\bin\smo\Microsoft.SqlServer.Management.RegisteredServers.dll
md5: 33E01BE5E65E6FAC06C95FFACE39EB51 | sha1: 74579D7B03125B349720772C1A97A4E00CAB1133 | sha256: ACB9E19B6A6CBC1B5CEE821A375ED4587D5C9D4AE5AA2DFA4F5CD62EBEF62749 | sha512: BFC124CFEAFDB4C197D7A2575252C318B48D29B6F0366AD482F296D8CE566DDC6F15C989D8932478E2969B500EEE21F737B806B7BA6A86DFE5A21DC14BBAC568
tools\dbatools\bin\smo\Microsoft.SqlServer.Management.Sdk.Scripting.dll
md5: D3D12A68A3322D2B4BCABFDEC2A06720 | sha1: 5CF6A8AD74A678BBB90094953FD541C326B9B230 | sha256: F422258ADF43DE7C6E6EB8EEB55B3AB11E1E9F996CBE8A39E853E6E2241892CB | sha512: 847B4595930C96583A5883CE96330280B2B08176E58BDB2D36AABF321D5BC3A9031811A5EABCD1CCEDAEF2E2AF2209F5C0800F71F0DEB614F5DB576C59C1641E
tools\dbatools\bin\smo\Microsoft.SqlServer.Management.Sdk.Sfc.dll
md5: A5D4E993C5F6C782AC33B49B68F1E27E | sha1: 5BD5CB47AFFFFD7E020D82189306BCB0FEC9A413 | sha256: 331194FC3A6A66982B6F775BBECF5199F7B8C5356A920BD44231160C63E02245 | sha512: 2CEC8182F0EAD3B73BB822FE9044AB8E2450159F7492577927830B11674A33AEDD8E454E0559E4694F3BAEF47EBF8320AC829F0A426338DB13FAD831F137F863
tools\dbatools\bin\smo\Microsoft.SqlServer.Management.SDK.SqlStudio.dll
md5: 8EFB3CDBE1DE83CE0670C649B766A76D | sha1: 63743941BE3158C423CC678891B8AF57E6071008 | sha256: CDCC8AD5C0F3C20BE315FBC3C766A531C9BECC89AED80F92A16DC062ECA632F5 | sha512: B226AAE407AC080E8EF23C0D9125E4710B0BE878D0CB33C0D3D90CF11290ACA9CD816BC4DB626D4ABA2F667EC962FAD4903D637FC99DF7BD29C912025FA70F88
tools\dbatools\bin\smo\Microsoft.SqlServer.Management.SmartAdminPolicies.dll
md5: 312A91796D44B1F2CE8D9D88BDD1B4E1 | sha1: C73C8477632C3547B38815A6C5A7141AF1902110 | sha256: 3C8DAA322542A081275EAF47FAF397A0E8D7B2E16FB4CF9BCED4A95C3F7D5A57 | sha512: 047C51512653A437C1B0176E1CF62975BC76783A584B2A3BC3EBC849C443C0A5624AAE6E259F44064EEFCFFBD1B842C759B45072F5312A2AF82FA5485759AAC4
tools\dbatools\bin\smo\Microsoft.SqlServer.Management.SqlParser.dll
md5: 2F2A839EF4155E2B26F6BA05E075B6F3 | sha1: BBC6AF7713B9BBA10553C56176567833F646BE1A | sha256: 77268A5014DDFD63E87FB4984FA34BE103539270F12EB501633FEFEC99FA3D1E | sha512: 3F62B7A0A20BD29FCAD1A2CFE62DAB74FF84025A0E7C0B6C0AD5B1DD7393E984B493B09177423C980105703F34D0EEF1EDDAE8C62DFC398BF6A4595B390CC6BF
tools\dbatools\bin\smo\Microsoft.SqlServer.Management.SystemMetadataProvider.dll
md5: 93A1D3ECA0651DBB6F7AAD154576C338 | sha1: 55C4B0BBA63759C44C4A192A33C4F96C06C1BDFA | sha256: 31302913BD2167D0FEA46930D746DCD6592871FEF974B865266FA467A04A266C | sha512: 0D99B5CDC4498314E9D58FA0FC4A4A2B8C297059EB65D1E207EF5D61AA56D74E275DA6F50F3421F4A23C72F6B5892097AAD517915319F3A9146512EE701102DF
tools\dbatools\bin\smo\Microsoft.SqlServer.Management.Utility.dll
md5: E2F350C2D2E5F7BCCB538FC002B1D42B | sha1: B18679210D8A2539A813C2A742C61C8970214D29 | sha256: DA19A0C3E5E3759D49E9BEF4F9B6AFAC6933DCF033F5A2A7D5ABF7B056FB90D2 | sha512: 2EAAB9572048A25D20B9D8EB707EC302C795692D807FE4DF90DBE2DAA3C708B7BAEAAD536C0D426AA10D7C4BE29BE139193690A761008DD850740ECBB21E7B66
tools\dbatools\bin\smo\Microsoft.SqlServer.Management.UtilityEnum.dll
md5: 808537ADDDBC7F9D7CB7C0C590DF692C | sha1: A59A90B4A2EB2106BCAD60E8FD2262F83794E018 | sha256: 81EF6AB722C276E6E022BAF21C1E83E879D56592BB6319AB25FEAF90D798EA62 | sha512: 070F15885253EEFAF0832BFA2502900B292FB31DE10432EECF30E23444232DD8FC9E06036ACB46BAB808FE5D663DE19105F5D01E9EBBE319C417209ACD0F8814
tools\dbatools\bin\smo\Microsoft.SqlServer.Management.XEvent.dll
md5: 277A3D4FB1362CCD4A33C7D4FF4A9EA4 | sha1: FFF47A49D5EA68C2B42663F85C07FFDF54D91807 | sha256: 74DCAF578AF7E821F8D7A278A551F4DC354C0B5929679A9CF9E849DE8CEA3EC0 | sha512: C8C75EB4C3ECFFE1B43FE6B1E7312077AD73938946E676A336160CAA427C6EBDC091F7910A41F17C7105280AC05F52E58927DEC71ED79117E8214067F50A5441
tools\dbatools\bin\smo\Microsoft.SqlServer.Management.XEventDbScoped.dll
md5: 08BD89DB5649CFB1873974BEBF95F218 | sha1: 32AE7370218C0123F80679FA38E5A94B0F87D531 | sha256: 3934CC842ECAAF05B0F7EE5F39540688BC04ED0F43F188B6F5CB127B0136CD18 | sha512: F7BA41D2B997B3447E0DEFA758C4EF93045E1491D7C9F5C013897256178EE4F62386F78E2FB80E63E7B36D2A25FFB2C4FA84CCE088915CD4571E5901B6C9A301
tools\dbatools\bin\smo\Microsoft.SqlServer.Management.XEventDbScopedEnum.dll
md5: 1E712AF4C36FB1E35F9BF79CA217E159 | sha1: FFEBD9FCB08005A35FA53963183B9E661F9B2263 | sha256: B3EAC5B9B2B5D608861FFDAAC7F788E027EE4329949621FFEC709DBD4D2232E9 | sha512: 2C1F9F494BD6F2551FC9083BE629DD840C68AA7C5E37323F071E0F5DFA239A6EAA6630ADCB7A46B73E8397C2529A595074BBE16CAB596D2F38DADED02BC857DB
tools\dbatools\bin\smo\Microsoft.SqlServer.Management.XEventEnum.dll
md5: 57BD65925D90B9ACD8234CA0785F4656 | sha1: 74A4919CD3A1099F00FB989F0AD93F9124C42C41 | sha256: 68562C7EA2F0070C8B81A15653F8950C4A2BE8CDE8D829BF2A2DDF4288B748FF | sha512: 2EEDDE3CF5700571A072F11C7DC6824549333EFAFDE684F511B0517595149088287706EB2C9B74D58C937721C19AF1AF49E8CBB92E1100F3ADD938C5322C591C
tools\dbatools\bin\smo\Microsoft.SqlServer.MSMQTask.dll
md5: CFD93BDCF83C739C276C1E86722D8B30 | sha1: 569A8E6553A66BAE284F937737E60D506F69B4E1 | sha256: A3DD617B546DBC27DECE1CB5C62CF22814CC18E53A35367367E5B53CFE4F7B4D | sha512: AD1A3540F3161CE55330BD3FEEC2330AA6DC97C16A3E826E4C97EC356D12110E3471CC859846D1907CBE3E3AAC442465BC442E0B39A59559E116B802EBE04A8A
tools\dbatools\bin\smo\Microsoft.SqlServer.OlapEnum.dll
md5: 0B480897CB1E688BCAD14B4905BDD4F0 | sha1: 24EE4CBA735262D79FBB9B7AAECF9E288ED3EF14 | sha256: 969BD6F5B1C5D559782F8E87CBDD2C627CF639CF595BDDF4B559000D656328DB | sha512: 8D363FAF072519B8E9DDEA38462A8F12400D4025B0D6928B50718D315F8C7799B6CAC3E4CD421F184F5434ACEE4B83BA6ADED993965C53A69382887351C9B129
tools\dbatools\bin\smo\Microsoft.SqlServer.PackageFormatUpdate.dll
md5: D663735B4F14F32BD470F084BE1C6215 | sha1: 5E2137C04E9475CD4F24FB571FDE59FD50CE75E9 | sha256: FE5F2927798A201B6F52F943612AC10C059A1E480C6AACA464FECA78A9EA22E7 | sha512: CA8F7D1851820AD5B088CAED9628D61076ED47125701F5BE5921775633509496135AE0EB4C4EB8CB215C9143E9A794D362BB8916D6EB327EAA85D462CDC1A51B
tools\dbatools\bin\smo\Microsoft.SqlServer.PipelineHost.dll
md5: 793598C84282E073326E14647D7D3711 | sha1: 3928D1538CD08AC82B88894776FCAABD67F7D037 | sha256: 2E1D92771E645A2A20184928D26F61292E341BE1E1823167A42E9E3D7C3BB6F7 | sha512: 5BADB4FB586AD732611C72DAAB718EC36F808B90815F75D1F04518F73EA7D63752B3C651CEB6346E3A3DD4A8D5F272CDD0864F80EA090D9AB6B48FB76F6BB983
tools\dbatools\bin\smo\Microsoft.SqlServer.PipelineXML.dll
md5: 110AC7E96C762C844D76132955F1F0A3 | sha1: B5057826F427C68D98E209BB60660110359CA996 | sha256: B95BD67FD24A153792399BB1972191C582CC3EA9A952A9EAE7FF6EF3F7C9D239 | sha512: 22BE7945B57DE661603B3ABA5479C0A8D191386C6100CD0C57A28F8A4EDABAF9DA773C82EDFBEDC8384DAE57F560B262E884DDF0D46C1C1259E0BB8E805CB77F
tools\dbatools\bin\smo\Microsoft.SqlServer.PolicyEnum.dll
md5: 581FB98877A22BA2A7EA3DF6B7130282 | sha1: 2F63F652416B40222FA444CF8E91405BBA26BA3B | sha256: 3A311FBBC53F5168E6E5030F7CF446A1E096B4F00A01590C8E369BCFCC5F1E19 | sha512: 2736E9F00D491382B87A4E79B04A49C41B203D8B82D74FEDE75026FAEC31A43CCBC9E07300F19E14A057A456CCE03DCE1CCA2FA651D4A6CABF7E257247115159
tools\dbatools\bin\smo\Microsoft.SqlServer.RegSvrEnum.dll
md5: 5890D898EDE91AAE18FE5A7A610DFEFC | sha1: 6E2504763E14B59FC847124DADCEC942C03B9247 | sha256: 997A08FBB4F698562CC497A061D23B60BE2B2889F4CBD3E74EC745C9C820D53E | sha512: 3614A66B9A6AE2CD21C7E4070CBF53AB1EEABF747EBE66E28E0CC0B6AD1A9B108179BBB3C09637F05B5B95CC39ED17FA721425A967B452AF030352EB91B55AC2
tools\dbatools\bin\smo\Microsoft.SqlServer.ReplEnum.dll
md5: 7D7BE01C1C2EBB973ABA33F7587FE04E | sha1: 2EE9CD9A998450729D79DD3A570948F61886510A | sha256: FA54A93E59A7FDEF47189A89B5B44ED466F1EEC092C3668A3FCEDB1975EF30A7 | sha512: 61CBA8EF9B02F457F6B057BB76BE697D05E8265EA2C10C020EF26EB31F603CB763A3550A71EFAE186CBB7376E3FE8802B1B1A797B666A4735A01395806BFB6D5
tools\dbatools\bin\smo\Microsoft.SqlServer.Replication.BusinessLogicSupport.dll
md5: CC304F5AA02D499866C037FE08EA24B0 | sha1: 21803E2AAE89A4F74E10453881A361F9DC59A8AA | sha256: DDE7397FDEB22206308931F6DA0DD2FE0E93F8AF97C1842C4D8B1688EBC7632E | sha512: FE21E3E0A37F1BC47A8F434005ED94DD6A94341D6099E4F288E6A1F78CADC98A7624564094C62022F6F80370C8C647D8F663A8DEC515300708C69326D0F24F79
tools\dbatools\bin\smo\Microsoft.SqlServer.Replication.dll
md5: 4E575BC1534DD45D1F32069A29D261CA | sha1: 34CC055999FC47534C70DE709037A7B7CD54D04E | sha256: 10F625817467AC23F55489CF84DA1C060859219BDD6B75B77A2CDD00F16509C4 | sha512: 6D06896FD901BAA6FFC066C8DEF634FA5214C0A222B9C2E534FCBC522EA82A44F2099B0872F93C2740AD7511CF02CD7B2AAADAADF6265FB5EAA4F1F50C4F9FB6
tools\dbatools\bin\smo\Microsoft.SqlServer.Rmo.dll
md5: 56A459CAB89BF18CD183490CE1BB7909 | sha1: E90C37447D7EA4EFAEEA58ABE3307CA97249213C | sha256: C16BE1791F423EE813AAC7E17E611A8A783FF2BA9B2BA12ED1A2992D5DC943FA | sha512: A9AA3F818D07E6D0F97B80FCAC994C7E50BDA286F489A7D20785C8A982B20E9088F9A38B3AF0D8B22D04449679704D887F3EF8B8986EF4CFADC0ACF99197BB97
tools\dbatools\bin\smo\Microsoft.SqlServer.ScriptTask.dll
md5: 2E3BCA6C01F4FCD26F958F8104F05266 | sha1: D73516F8C217D7A0BC642C21777E067CA807C226 | sha256: 1398EC9B09C19A2B516D8BD25259971B4F0BACA055BBB4939BA98A4335084826 | sha512: 045E18EF793086B6BF0835122C85C8A91FA039DE606A8497DF2AC39B7D147BC9D54564F7EF84AAB01B3A33263C38ED381E724C48CEE665F9A7A1387AEA8DAF7A
tools\dbatools\bin\smo\Microsoft.SqlServer.SendMailTask.dll
md5: 40E8ED2ADD7DAE72312D6D3F6C842E82 | sha1: 2D10FF6C7449EB8DBE2FB2EC2A72A1604065C9FE | sha256: 51E363174578DFE7BC82B24B5D758B24CEFD216786A0A9CB4B511F314F046F70 | sha512: 0BAECCF66D8E09C2F18BCB82BCEC89591E6D1757FFFE20B15B5A85B68BD84A576A79FA367626FC210D80D29C686B9FDE6D97988C483F11894474A2C313B62E66
tools\dbatools\bin\smo\Microsoft.SqlServer.ServiceBrokerEnum.dll
md5: CA09568CB5870FE4A7A431C726EF7894 | sha1: 72B0477EE1C843ADE4DADD0836636DA3C3A188BA | sha256: 7DE4C88949F8FAAE9AE8A9F6E494196FD4429DAD42F5E6269E9074FD70273F2F | sha512: D9FCFD3C67ADF76F1E9ED32EFFDE301BE99F8EAEDC38C11552772B097EE2EB94E8F92862184678A64EB6BD5BFC81B079B4F7BF44E6037E853EADA98ADB43F243
tools\dbatools\bin\smo\Microsoft.SqlServer.Smo.dll
md5: 6097BEBF3019EB269D6F0182899FDB51 | sha1: 90859109A74C7EAADD3D8E0A7BBC88198604F397 | sha256: F447685772B53977788255529661E372E43BB5FB776115E284BFD1A822E9E0C8 | sha512: 9AD640031D6323B4C90AE4968B45C1894FF4DEDD8B518FFF268FDE2C13E7E916EA9633F5BFDAEDE3C559E8436B72B3D511CB0B5C0FC0CF4F447511BC7F0D392F
tools\dbatools\bin\smo\Microsoft.SqlServer.SmoExtended.dll
md5: F5D99DB45C64A478C73D70531420DF36 | sha1: 397A46EF196684ABA88672B7305D7A908E421059 | sha256: 418903A38CA85F2EA4EA31E308AAFE3C3CCB11B272E4EC0379F6AF9D3954200E | sha512: B58A582324F989A66BE060413C1BC06169D7A6E632A55E5F3C9B6B2B8EE42E6BA88DC1CF6BB05AE1DF48E036B2AE2952DFFF2F9A3125052E3AF708F0DB3FB08B
tools\dbatools\bin\smo\Microsoft.SqlServer.SqlCEDest.dll
md5: 6D4377FFF7DD0F58A99EE3AB05A4B504 | sha1: 3088070AAD1582A90058266A605E106FB2590EAF | sha256: F87FE992E490713855C771EBED73857A1E81CB65E90E53C88A25A0B165C5A7F0 | sha512: 839BC260D5B6EA8652DD137D9A45AE6A3D3737595A459BFF6C113552A7C888A46FAF13F877708C3C3009DB8FB8C7957DD24A65D755625D09C6C991B8E8EDAD50
tools\dbatools\bin\smo\Microsoft.SqlServer.SqlClrProvider.dll
md5: 6CC17DD0A193D295744060E928F023A9 | sha1: 4B44EEE2ED8DED8C80C2576EEB63B027F02FC035 | sha256: 10EAA6597D7088D87ED9381FBD6FCDDD54156D7A32C1BC0C6E3A2B209CD9B3C9 | sha512: BC4B6B6AFA566248E6507691F4AF1286ABEEA60773908E1E0484F6E3107689B2A0A45A9CC46C1D05DA91349B90F1EA8A85CCDCB0954471B19B85B83CC16A5D7C
tools\dbatools\bin\smo\Microsoft.SqlServer.SqlEnum.dll
md5: F61256335158769DB203C5B975A92C89 | sha1: ECF3EC80DE98A265CFABD03FA7D62D07C6D48371 | sha256: 0AE5D2C8762B761EC9DA84A7A2F6BED55B4054F8B6B6CC8F8B93CED2B0CB528C | sha512: 039082C55A3B5160E7A845F7797990B8CB266A9958077481EA7C613E37F49C0771657769739607A21A217199D189CB5EB43C49C37F194F5EA72DE70463D3358B
tools\dbatools\bin\smo\Microsoft.SqlServer.SQLTask.dll
md5: 30412D38B8A760EDD652169120ED305A | sha1: 3399285024CDFD6432B3C801FA7F32C487F0EFA7 | sha256: E1280377D7E716FA966F4405253F89986C8574A476B1ACA6CAD1011A609F5CFD | sha512: 92137F95D7DF30984CEBD7E266D8B221609F026010DD496AE689AE4E753B836B47A60D5D1316C390A4090FC56FEFC46A912AAD448935BDB45E90BE01D92D8DEC
tools\dbatools\bin\smo\Microsoft.SqlServer.SQLTaskConnectionsWrap.dll
md5: CB799D930BA7BCEE68A9A9AD6CC3C627 | sha1: 5FFC9BAD3A2B71F209345BDDC50B4902E208FC5E | sha256: D58CC6B042532EB814989A96E9022146F8CAE4B3582D5F7D37F1DDB45A49A4DD | sha512: D09D56D38E746D70A2DF5BC98E7BDAC0492B1A43B35CC287E5692CBFC23DD556C5A178BAAB477C8503FBD2489328B7FE9416493054DB242C3D0F56DD69848A03
tools\dbatools\bin\smo\Microsoft.SqlServer.SqlTDiagm.dll
md5: 48FBD39370EE8AB525D703DCD560D059 | sha1: 0B4C3A5E9302FAE1C33B026FA3DAA22C47CEC364 | sha256: 32B79A8B4074B85489D7E0470D7E0EAE01992C7C14F6C8CF5578F4C855B5B304 | sha512: A295F434621D723B4F7FFE0C47E96AC73E10CA3AE71916054F127E25A223AD489E0B2C049BFA1FD9988000C82DB3C0AEF630C9A4CD27C5B9CAFBBC677647F581
tools\dbatools\bin\smo\Microsoft.SqlServer.SqlWmiManagement.dll
md5: 72A3A35A2991209FC316A9CA97FE93F7 | sha1: 1E81DCB081D49E9F149747754BF2D6A7598C5059 | sha256: CEF10D42FA3DDF73594685F4B6992C269E2A1D3B02B601E32E07D78C78801E5B | sha512: D3905773E84F45F41F590A1B2EFAAFDA73D181D1EB6CD8E43E0D87ADF05E16736A9E2B8B8BA74627356CAA505C9DFFEA5DDBF9AE8C4E704903773CE07E584B03
tools\dbatools\bin\smo\Microsoft.SqlServer.SString.dll
md5: 205163310761A94A9AC099CE9BDE75DD | sha1: 5B49C76A0B6B23B92FD487C4F2D48C4FD9E38F1A | sha256: 7266EC1582F9B9486D12C05BDF10787B844DB67262A3389F1119D4C747424C18 | sha512: 4F18CA9CB1CDFC954221C6E4F3FF84E845BA5D04FF4E2EF6431633BC8A21F6762A759777A19EB7C06C9CC951F852DED62EA59D2311BADC097C004D83D17DF976
tools\dbatools\bin\smo\Microsoft.SqlServer.TransactSql.ScriptDom.dll
md5: A0ED30A95E8F5D5DC8727141A4DE3EBE | sha1: C439A0A056A028467FE4AA9D5A0B69365A719448 | sha256: 89708A0EEAFEFA5153103613E45750B4774CAD15EE1FD38EB5DBC9F9FFD1A465 | sha512: B693B27A2960D8F4E87905DC70004B749D8B3D5ED08360CC91705F0CAC507688168CAE8B2479BAEC1DD2517579A9AA8952781528A533E3EB8A23095DD0ECCAFE
tools\dbatools\bin\smo\Microsoft.SqlServer.TransferDatabasesTask.dll
md5: C30CC2F227FD17E126EB3F70C5B6AC7F | sha1: EDCBA8082452065E8A0DE6930D002A171B29FECE | sha256: 1B28B7246010CF3B69168EF276944A1FEAB8C8996A27AA1AF895752EF6EC7076 | sha512: 7D90DABCF50245552588D249E6CD0D7FC386EF5B16FCA856C053C0FD7BE85C354BDC3DD73AAE8E95049AF06907ECB4C5569FC23AB0088F623F55532D52078B5F
tools\dbatools\bin\smo\Microsoft.SqlServer.TransferErrorMessagesTask.dll
md5: 4C257A1EC73609BBD29D91FB8707B4C8 | sha1: 8BD5C24F1A57B917DDBBE7FDA71F30D2EE762682 | sha256: C09FA78504083119C9D2E0ED01459FDBD29F5B0B9BE80C22E6A75E9E90E93DFC | sha512: 9D859DACA1ECC9F8A145AA059567405A2B1FDAE28995D296ABAD0FF802458DD58DE90D555CDC602916DD0E970A8BF685D5CEB319B0534081EBC14984D7599B9E
tools\dbatools\bin\smo\Microsoft.SqlServer.TransferJobsTask.dll
md5: 0F78972C6E36FF91618C19BC19EABE3F | sha1: ED26DF6D9C00631F7A86BC0924DF55F0AD91F998 | sha256: 668A41C6494FFBB278E0D5D6BC9575AD39029A1683C5CB2293505E1F452895F6 | sha512: 360A6B484D343171AF02374189CEF8029EFD391158B70A2B5BDA20BBE3F2790A89D831DB9353E100505F23740049DCAC49D849C3B2BB03D95C14C894E894F058
tools\dbatools\bin\smo\Microsoft.SqlServer.TransferLoginsTask.dll
md5: 75D060CDA0465EF9E24999DD4909AF9D | sha1: 572E54D6088F3D8C31D446827FC8066B5B42B658 | sha256: ACD852B24273FCAA0F9A299C50F1F8EBB18D9D16997A4C26A9D982BB51277690 | sha512: FDDE574A35DF6CD3E1BBED8C560B84F302F6BD0EEF9A701D9A2B73399106F376D53FAE284C4AB2C26F4EE4473D1F87331551F59D0D91D4C74319145F6FFA75E3
tools\dbatools\bin\smo\Microsoft.SqlServer.TransferObjectsTask.dll
md5: F37C4F93DD95ACD100D7E5B4DF19096F | sha1: 99EF82A0AA702B809FF0150900819D06B860758B | sha256: 9B1C8275A1B8D9223E79F921EF2C9D085E670825AF663FBA20C74BF34DCF333B | sha512: BA6DA3EE51EE14FB4DF8DA68B5DD2C064E973F2C5AD0A7976C8A77ED85EF9EDC1A1CFD2B22995A7BF9D5E2D1C1E546BB0EA9F670E47926BAC0859340DC2D24F1
tools\dbatools\bin\smo\Microsoft.SqlServer.TransferSqlServerObjectsTask.dll
md5: 731D98A438B48FE8719D5A4E68D94FF8 | sha1: 58E3BE8898D126DB40ACCAEF6EA8D2343644F366 | sha256: 6A4F97E1FAECA9895811E24750A1C9C6266A6EA55E16A8041309D49932315029 | sha512: 4CAFBCC4143ABC1A370E779D32C007F667D34CBEDB3ADAC689EC64474CEE3EA4BC14D11B0DBBA5234F15B2A92381EF9B8C070E8031A3D8E19FD9E78D85F1C47B
tools\dbatools\bin\smo\Microsoft.SqlServer.TransferStoredProceduresTask.dll
md5: 861A7CA84A9238F59ED81B11A75BA2B1 | sha1: BCF193A3F75377A2754CDE270C3BBBD5CD63F8AA | sha256: E07BB48467089204AC6A62B5BB6D6D38173759739ECB5E15D9B1FAC6D50D2AED | sha512: 6C5E0C258512C3D999F8C1F3A0D2993A8076C9BDF4F8354C3469B2C01B886ABED2A7040006034EB3906BFBD787DF5D30B348241B19EA11DA2BCBB641B9ADBB1D
tools\dbatools\bin\smo\Microsoft.SqlServer.TxScript.dll
md5: 9C81024E77FF5185E32B994BF747F85D | sha1: 35D13AE870851730E16F706E9182859C5A617952 | sha256: 8F2ED11359DD2901E5F54D47DD8979A119E677695839AD2D41DAADCD1A81C91B | sha512: 031C2F56F6FE0C32A151EE75F966C452BCB458404A230677EBDDFC59088D720E9C69841EF331701904D51C245E9FA4E4741F6EB5258BE56EB96F8487EA33B2CB
tools\dbatools\bin\smo\Microsoft.SqlServer.Types.dll
md5: 88CE5CE86DC287C6B761591F4DD830C5 | sha1: 4C46333752795CE43044EA58DA083D990C8A6FEA | sha256: FB7CD689B7DA6528EE708D4FAA4FBDBA5A49E29B27E6788B60BC4E36CB78389D | sha512: E56C507B1ED71708CB3584B554309EC4C6024C6493F95CF6DC1B05E445227E2B5413A9B67A0F0FBCA9E36440BD99799ABF8D12B35159DB5D3CCB6F90E6F67CA2
tools\dbatools\bin\smo\Microsoft.SqlServer.Types.resources.dll
md5: 7B0BC72C25EA22550F0D22EF8437F2BC | sha1: 26C09B3B4117703EBD55390FED2B18F3A2774B20 | sha256: 4A3C7EEFCFFA342081FA5B4345E9294232FA2A6EAAF331985BE49A89CAAFFB15 | sha512: B7F43747AFEFB8F829506BC427963BF4AF986DC13B1B117D430104DE3E106A23CB7DE55E1D4C927CE1E67BAC4C0DA9DAD11DAF4919F91F9A9D35B190E424C256
tools\dbatools\bin\smo\Microsoft.SqlServer.VSTAScriptingLib.dll
md5: 095EF125CD38AF9F242909D6C836DD34 | sha1: A8BDD868851FD42BEA6F4399BE5CE4FE1EB8F182 | sha256: F03BDE15A9E14CCDC78B36BD0AF8120C56A175E42A7ABBE21A71EFBD0CE505E8 | sha512: D6A2EBFE287A5E927E3DFB786F9A5EA71A4A889AC2F187FDB83A595F58EE910B9C09F9A5A421959E0F39E5EB8C007177D46806EC84A74B228552D8C31E33DD6A
tools\dbatools\bin\smo\Microsoft.SqlServer.VulnerabilityAssessment.Model.dll
md5: 5CDDEC5F7A85FAB81229795D6B660E70 | sha1: C6FF70DC95C725789A1D3EF1F796E44408C1B1BA | sha256: B7FC87FB2BCBB26D124E4D97EEAC1B50B2D515C1377DEE90A857A70BA167888F | sha512: A9B86579858F45BF96A8B521E5951AC954CB97344258CC5BF0D08687049986D6DAB3EAEC27C5946CDE344657F7CF1D142E13CB43F0EBFFFF534A7D80E437BC64
tools\dbatools\bin\smo\Microsoft.SqlServer.WebServiceTask.dll
md5: E171A0B4412D92ED42D59C3363312095 | sha1: D407561B49D2FE25C7C54B43C4720D6B9991170C | sha256: 2D1D0EFB9269D1ABB8E469CD2E31F3C91ACCF6A38DB4BBC03888207FA34C5078 | sha512: 0C8E89473DB81C5453317C96D270F51966C88DD68327EAE58F983FC022B95D3FC611AB132331D5EFB8E039734610E66B06CC1440AE2962E8B862B6186103D641
tools\dbatools\bin\smo\Microsoft.SqlServer.WMIDRTask.dll
md5: 301E13133F5BC1268E5BF7C1F4D3A89D | sha1: 9E7EEDF50A8B3A5025ADA38BB21EE4C27E58B19B | sha256: 187ADA76718B7DB4D0C8FB83F07CED8CAEDCF5002542E6F6997179FB34E79AA5 | sha512: DCA74C22E6A20F9046EA4F305719B48BA2EDB2DC13E008BD2C95647E55081A47C90F25720198363177F052EECA39D18D553DC665939E6672E06D10216165DB68
tools\dbatools\bin\smo\Microsoft.SqlServer.WmiEnum.dll
md5: 93C592750C90C91F4DB29B30CCC95876 | sha1: 9A2C69ED060F6FB1B988C2EE6B33B2AF1A5DB377 | sha256: 52FBC6F7A51ED206540CF1B11C0D24FE1BC952CBFBD3AFAAC03BAB0FF5BE815F | sha512: E2860813ED0445CC09365899320306A2DF16ECCB5499FCA6569E561A83E640B851EC508D812705F4D1DC6733152650E74AFB56B135B79178FEFDDE7869BC0D25
tools\dbatools\bin\smo\Microsoft.SqlServer.WMIEWTask.dll
md5: 647BAA1800BB83EFF3C5D06BD4064019 | sha1: FB22AB0E3AA32BE585EB53C9AF6766144DB264DC | sha256: 603E2EF29A6E14CCA88E9DAAEA7B18D8F93D66F0E3DA930B3ECBCB2EE5397C25 | sha512: C858502468897D815AADD862C1B70234ACB982F124752851876E3CC226BAE9839EB0508DC067219177EBB31E1B48D920FC349E63AABB3ABE8EFA421930341952
tools\dbatools\bin\smo\Microsoft.SqlServer.XE.Core.dll
md5: 6B8C7D5B982A0B811ACA7DDE8C052CD8 | sha1: 4CBC4C1700DEDC2E629FD45BC0C6A4DE7D9F976E | sha256: D20E11EB91D688A679DE2FEF527C71CA4DFD7DFBFE27F1040CC2408907C4624D | sha512: 86D4FEE406ABC28332BB839CC297994BFF60277A0E0A00134DE951602EB142DA59BF982A1593ECE683BD5245E9FF8329FC110EDE3551F9B4791E59DC578EF931
tools\dbatools\bin\smo\Microsoft.SqlServer.XEvent.Configuration.dll
md5: CA315AB021FAE0283195955A28B22273 | sha1: CA1A95C2785AB0201A9F1CDE582FFB3A6FE5D15D | sha256: E379B6DD301EFFBCCC0F22DF1C4AAAC146529ED86D5D3C1D1E2346B6B7555F23 | sha512: 02C109B03FA403EF7705295FC5182AADAB83D365546FC99E577F24CBBD7CF72467151FBC431B8783EE48B98BF095EFE84615F08971F3BE6220D473AC6FD85FA3
tools\dbatools\bin\smo\Microsoft.SqlServer.XEvent.dll
md5: EDD9944177424E14524D0B611374EF62 | sha1: 0D9AB9196D10D422CB653243A0F246899F1E34AD | sha256: BF8FBFCF1E48CD08E320DCBCCA0817E0CD6F2DB2F6FC330E196C28146DD32D94 | sha512: 349CA5C0B0CF16AEB232D38B1DFCD869F6954CADB73642E564AF63B7EB59159DFE5644E7FA9CABF762752644F26A9520F4E3397CC255BD4A49E9577B897C4CD3
tools\dbatools\bin\smo\Microsoft.SqlServer.XEvent.dll.xoeg8du.partial
 
tools\dbatools\bin\smo\Microsoft.SqlServer.XEvent.Linq.dll
md5: F10F56B135A824E8C52342475B8437D1 | sha1: 9C659517CF8C9FA34A2B784AA82C2E4C0314AA4A | sha256: DF1EBA2D957E89D4BDC0A403020C8FFCD660AA85CED0C25178BF800ED9CA5B86 | sha512: 166D3FC0576CB67CCE3F6270807A2D987647C34F9752B862BB921ADF69BBBCF0A2497C9BAAF526B139750C62E8FEBC71A2E2AD6DDEE62A83AD6A6CAA14154FB0
tools\dbatools\bin\smo\Microsoft.SqlServer.XEvent.Linq.dll.oc2ihg1.partial
 
tools\dbatools\bin\smo\Microsoft.SqlServer.XmlSrc.dll
md5: 4C6405F8AB9650878A067CC8A8AFF76E | sha1: 4B52514EAA163D6CBBF1824E7B9C0E5F246716D7 | sha256: 05487224CC045B3ADE51022BA4FFBCF6257746E69932193259FC01D12402A64E | sha512: 65C92ECA1E8970C483630DE055371461BA5699487F2B5A2902923BD4C630C61A3470FDA40D3A4F8BB38AAE4619CBBFCE210A1EC0696710841754C7110161BFC1
tools\dbatools\bin\smo\Microsoft.SqlServer.XMLTask.dll
md5: 0E43397934BA7384AA479CBAAD5F7566 | sha1: 8160D9494C5C3E1F52E0CF571CCDF0B11C48FC2A | sha256: A1229AE7D501E6A57EB7AEB1D6D2BDD473EDDBA39534D04B24ECDE69D06F5CCC | sha512: 77A83FC82FB824AC1A6C5A24738DEA1616DC6D4801AD6F1C15EB1D9A5A181B3A559D8F8DB6AC8F1D3CC92DEA2B97D675E5C92FA2CC1DE3CA504668F2445D68DE
tools\dbatools\bin\smo\smo-deps.txt
$phase1 = @()
$leftover = @()
dir | %{

 $file = $_
 try
 {
 Add-Type -Path $_.FullName -ErrorAction Stop
 $phase1 += $_.FullName
 }
 catch { $leftover += $file.FullName }
}

$dep = @()
dir | %{
    try { Add-Type -Path $_.FullName -ErrorAction Stop }
    catch { $_.Exception.LoaderExceptions.FileName | %{ $dep += $_ }}
}
$dep | select -unique
tools\dbatools\bin\smo\sqlpackage.exe
md5: 6C1C817073FB51447EF5097D4E1ED382 | sha1: 74C64BEE4897FF3CB0868C7A8F41CF86524B41B6 | sha256: A42C4410CF8448F59D3C6D4FD0E06DE5BC3005F7AF1D14401B83A6B706F5EC54 | sha512: EA49143B8EE1E65EF4414F3F38BA0424376C6DF147811C0CBD9FCDD81C451C6573C0FE27A98DB4CD79A89F5399FDEEC4FA19EB4E886251B023AFA78BAA12999F
tools\dbatools\bin\smo\sqlpackage.exe.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <startup useLegacyV2RuntimeActivationPolicy="true">
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/>
  </startup>
  <system.diagnostics>
    <!-- 
      Uncomment the settings below to enable tracing.
      Update the initializeData for the TextLogger with the desired trace file path.
    -->
    <!--
    <sources>
      <source name="Microsoft.Data.Tools.Diagnostics.Tracer" switchValue="Error">
        <listeners>
          <add name="TextLogger"
               type="System.Diagnostics.TextWriterTraceListener"
               initializeData="sqlpackage.trace.log"
               traceOutputOptions="DateTime, ThreadId" />
        </listeners>
      </source>
    </sources>
    -->
  </system.diagnostics>
  <!--
  The below runtime setting is a workaround for an issue resulting from a known breaking change in the SQL Server 2012 version of the System CLR Types. The original breaking change and resulting issue are both documented in the “SQL CLR Data Types” section of http://go.microsoft.com/fwlink/?LinkID=271559.
  -->
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.SqlServer.Types" publicKeyToken="89845dcd8080cc91" culture="neutral" />
        <bindingRedirect oldVersion="10.0.0.0-13.0.0.0" newVersion="14.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.SqlServer.TransactSql.ScriptDom" publicKeyToken="89845dcd8080cc91" culture="neutral"/>
        <bindingRedirect oldVersion="12.0.0.0-13.100.0.0" newVersion="14.0.0.0"/>
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.SqlServer.Dac" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
        <bindingRedirect oldVersion="11.0.0.0-13.0.0.0" newVersion="14.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.SqlServer.Dac.Extensions" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
        <bindingRedirect oldVersion="11.0.0.0-13.0.0.0" newVersion="14.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>
tools\dbatools\bin\sp_SQLskills_ConvertTraceToEEs.sql

/*****************************************************************************
*   FileName:  sp_SQLskills_ConvertTraceToExtendedEvents.sql
*
*   Summary:   Converts a SQL Trace definition on a SQL Server to a Extended
*                       Events Session using the SQLskills_Trace_XE_Column_Map table
*              to map the column definitions across.
*
*   Date:      October 2014
*
*   SQL Server Versions:
*                       2012, 2014
*         
******************************************************************************
*   Copyright (C) 2011 Jonathan M. Kehayias, SQLskills.com
*   All rights reserved. 
*
*   For more scripts and sample code, check out 
*      http://sqlskills.com/blogs/jonathan
*
*   You may alter this code for your own *non-commercial* purposes. You may
*   republish altered code as long as you include this copyright and give 
*      due credit. 
*
*
*   THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF 
*   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED 
*   TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
*   PARTICULAR PURPOSE. 
*
******************************************************************************/

-- Slight modifications and performance optimizations 
-- by Gianluca Sartori and Chrissy LeMaire

DECLARE @TraceID INT = --TRACEID--
DECLARE @SessionName NVARCHAR(128) = '--SESSIONNAME--'
DECLARE @PrintOutput BIT = 1
DECLARE @Execute BIT = 0

SET NOCOUNT ON

CREATE TABLE [#SQLskills_Trace_XE_Column_Map](
       [trace_event_id] [int] NOT NULL,
       [trace_column_id] [int] NOT NULL,
       [event_package_name] [nvarchar](60) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
       [xe_event_name] [nvarchar](60) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
       [column_name] [nvarchar](256) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
       [action_package_name] [nvarchar](60) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
       [xe_action_name] [nvarchar](60) COLLATE SQL_Latin1_General_CP1_CI_AS NULL
) ON [PRIMARY]

CREATE CLUSTERED INDEX CX_#SQLskills_Trace_XE_Column_Map ON [#SQLskills_Trace_XE_Column_Map](trace_event_id, trace_column_id)
INSERT INTO [#SQLskills_Trace_XE_Column_Map]
SELECT 
       te.trace_event_id,
       tc.trace_column_id AS trace_column_id,
       txem.package_name AS event_package_name,
       txem.xe_event_name,
       tab.column_name,
       CASE WHEN tab.column_name IS NOT NULL THEN NULL ELSE txam.package_name END AS action_package_name,
       CASE WHEN tab.column_name IS NOT NULL THEN NULL ELSE txam.xe_action_name END AS xe_action_name
FROM sys.trace_events AS te
JOIN sys.trace_categories AS cat
       ON te.category_id = cat.category_id
JOIN sys.trace_event_bindings AS teb
       ON te.trace_event_id = teb.trace_event_id
JOIN sys.trace_columns AS tc
       ON teb.trace_column_id = tc.trace_column_id
JOIN sys.trace_xe_event_map AS txem
       ON te.trace_event_id = txem.trace_event_id
LEFT JOIN (
          SELECT  p.name AS event_package_name,
                  o.name AS event_name,
                  oc.name AS column_name
          FROM sys.dm_xe_objects AS o
          INNER JOIN sys.dm_xe_object_columns AS oc 
              ON o.name = oc.object_name 	
              AND o.package_guid = oc.object_package_guid
          CROSS APPLY (
              SELECT name
              FROM sys.dm_xe_packages
              WHERE guid = o.package_guid
                  AND (capabilities IS NULL OR capabilities & 1 = 0)
          ) AS p
          WHERE (o.capabilities IS NULL OR o.capabilities & 1 = 0)
              AND o.object_type = 'event'
              AND oc.column_type = 'data'
       ) as tab
       ON tab.event_package_name COLLATE SQL_Latin1_General_CP1_CI_AS = txem.package_name COLLATE SQL_Latin1_General_CP1_CI_AS
              AND tab.event_name COLLATE SQL_Latin1_General_CP1_CI_AS = txem.xe_event_name COLLATE SQL_Latin1_General_CP1_CI_AS
              AND CASE
                        WHEN tc.name = 'ObjectID' THEN 'object_id'
                        WHEN tc.name = 'Type' THEN 'object_type'
                        --WHEN tc.name = 'NestLevel' THEN 'nest_level'
                        WHEN tc.name = 'ObjectName' THEN 'object_name'
                        WHEN tc.name = 'DatabaseID' THEN 'database_id'
                        WHEN tc.name = 'DatabaseName' THEN 'database_name'
                        WHEN tc.name = 'IndexID' THEN 'index_id'
                        WHEN tc.name = 'Reads' THEN 'logical_reads'
                        WHEN tc.name = 'CPU' THEN 'cpu_time'
                        WHEN tc.name = 'RowCounts' THEN 'row_count'
                        --WHEN tc.name = 'Severity' THEN 'error_severity'
                        --WHEN tc.name = 'Type' THEN 'object_type'
                        WHEN tc.name = 'Error' THEN 'error_number'
                        ELSE tc.name
                  END = tab.column_name
LEFT JOIN sys.trace_xe_action_map AS txam
       ON tc.trace_column_id = txam.trace_column_id
WHERE cat.name NOT IN ('User configurable')
  AND tc.name NOT IN ('EventClass', 'StartTime', 'EndTime')
  AND (tab.column_name IS NOT NULL
              OR txam.xe_action_name IS NOT NULL)
UNION ALL 
SELECT 
       CAST(trace_event_id AS INT) AS trace_event_id, 
--     trace_event_name, 
       CAST(trace_column_id AS INT) AS trace_column_id, 
--     trace_column_name, 
       event_package_name, 
       xe_event_name, 
       NULLIF(column_name, '') AS column_name, 
       NULLIF(action_package_name, '') AS action_package_name,
       NULLIF(xe_action_name, '') AS xe_action_name
FROM
(
       VALUES 
              ('10', 'RPC:Completed', '1', 'TextData', 'sqlserver', 'rpc_completed', 'statement', '', ''),
              ('10', 'RPC:Completed', '31', 'Error', 'sqlserver', 'rpc_completed', 'result', '', ''),
              ('11', 'RPC:Starting', '1', 'TextData', 'sqlserver', 'rpc_starting', 'statement', '', ''),
              ('12', 'SQL:BatchCompleted', '1', 'TextData', 'sqlserver', 'sql_batch_completed', 'batch_text', '', ''),
              ('12', 'SQL:BatchCompleted', '31', 'Error', 'sqlserver', 'sql_batch_completed', 'result', '', ''),
              ('13', 'SQL:BatchStarting', '1', 'TextData', 'sqlserver', 'sql_batch_starting', 'batch_text', '', ''),
              ('14', 'Audit Login', '1', 'TextData', 'sqlserver', 'login', 'options_text', '', ''),
              ('14', 'Audit Login', '2', 'BinaryData', 'sqlserver', 'login', 'options', '', ''),
              ('14', 'Audit Login', '25', 'IntegerData', 'sqlserver', 'login', 'packet_size', '', ''),
              ('17', 'ExistingConnection', '1', 'TextData', 'sqlserver', 'existing_connection', 'options_text', '', ''),
              ('17', 'ExistingConnection', '2', 'BinaryData', 'sqlserver', 'existing_connection', 'options', '', ''),
              ('17', 'ExistingConnection', '25', 'IntegerData', 'sqlserver', 'existing_connection', 'packet_size', '', ''),
              ('18', 'Audit Server Starts And Stops', '21', 'EventSubClass', 'sqlserver', 'server_start_stop', 'operation', '', ''),
              ('19', 'DTCTransaction', '2', 'BinaryData', 'sqlserver', 'dtc_transaction', 'unit_of_work_id', '', ''),
              ('19', 'DTCTransaction', '21', 'EventSubClass', 'sqlserver', 'dtc_transaction', 'transaction_state', '', ''),
              ('19', 'DTCTransaction', '25', 'IntegerData', 'sqlserver', 'dtc_transaction', 'isolation_level', '', ''),
              ('21', 'EventLog', '1', 'TextData', 'sqlserver', 'error_reported', 'message', '', ''),
              ('22', 'ErrorLog', '1', 'TextData', 'sqlserver', 'errorlog_written', 'message', '', ''),
              ('23', 'Lock:Released', '1', 'TextData', 'sqlserver', 'lock_released', 'resource_description', '', ''),
              ('23', 'Lock:Released', '2', 'BinaryData', 'sqlserver', 'lock_released', 'lockspace_workspace_id', '', ''),
              ('23', 'Lock:Released', '52', 'BigintData1', 'sqlserver', 'lock_released', 'resource_2', '', ''),
              ('23', 'Lock:Released', '56', 'ObjectID2', 'sqlserver', 'lock_released', 'associated_object_id', '', ''),
              ('23', 'Lock:Released', '57', 'Type', 'sqlserver', 'lock_released', 'resource_type', '', ''),
              ('23', 'Lock:Released', '58', 'OwnerID', 'sqlserver', 'lock_released', 'owner_type', '', ''),
              ('24', 'Lock:Acquired', '1', 'TextData', 'sqlserver', 'lock_acquired', 'resource_description', '', ''),
              ('24', 'Lock:Acquired', '2', 'BinaryData', 'sqlserver', 'lock_acquired', 'lockspace_workspace_id', '', ''),
              ('24', 'Lock:Acquired', '52', 'BigintData1', 'sqlserver', 'lock_acquired', 'resource_2', '', ''),
              ('24', 'Lock:Acquired', '56', 'ObjectID2', 'sqlserver', 'lock_acquired', 'associated_object_id', '', ''),
              ('24', 'Lock:Acquired', '57', 'Type', 'sqlserver', 'lock_acquired', 'resource_type', '', ''),
              ('24', 'Lock:Acquired', '58', 'OwnerID', 'sqlserver', 'lock_acquired', 'owner_type', '', ''),
              ('25', 'Lock:Deadlock', '1', 'TextData', 'sqlserver', 'lock_deadlock', 'resource_description', '', ''),
              ('25', 'Lock:Deadlock', '2', 'BinaryData', 'sqlserver', 'lock_deadlock', 'lockspace_workspace_id', '', ''),
              ('25', 'Lock:Deadlock', '25', 'IntegerData', 'sqlserver', 'lock_deadlock', 'deadlock_id', '', ''),
              ('25', 'Lock:Deadlock', '56', 'ObjectID2', 'sqlserver', 'lock_deadlock', 'associated_object_id', '', ''),
              ('25', 'Lock:Deadlock', '57', 'Type', 'sqlserver', 'lock_deadlock', 'resource_type', '', ''),
              ('25', 'Lock:Deadlock', '58', 'OwnerID', 'sqlserver', 'lock_deadlock', 'owner_type', '', ''),
              ('26', 'Lock:Cancel', '1', 'TextData', 'sqlserver', 'lock_cancel', 'resource_description', '', ''),
              ('26', 'Lock:Cancel', '2', 'BinaryData', 'sqlserver', 'lock_cancel', 'lockspace_workspace_id', '', ''),
              ('26', 'Lock:Cancel', '52', 'BigintData1', 'sqlserver', 'lock_cancel', 'resource_2', '', ''),
              ('26', 'Lock:Cancel', '56', 'ObjectID2', 'sqlserver', 'lock_cancel', 'associated_object_id', '', ''),
              ('26', 'Lock:Cancel', '57', 'Type', 'sqlserver', 'lock_cancel', 'resource_type', '', ''),
              ('26', 'Lock:Cancel', '58', 'OwnerID', 'sqlserver', 'lock_cancel', 'owner_type', '', ''),
              ('27', 'Lock:Timeout', '1', 'TextData', 'sqlserver', 'lock_timeout', 'resource_description', '', ''),
              ('27', 'Lock:Timeout', '2', 'BinaryData', 'sqlserver', 'lock_timeout', 'lockspace_workspace_id', '', ''),
              ('27', 'Lock:Timeout', '56', 'ObjectID2', 'sqlserver', 'lock_timeout', 'associated_object_id', '', ''),
              ('27', 'Lock:Timeout', '57', 'Type', 'sqlserver', 'lock_timeout', 'resource_type', '', ''),
              ('27', 'Lock:Timeout', '58', 'OwnerID', 'sqlserver', 'lock_timeout', 'owner_type', '', ''),
              ('28', 'Degree of Parallelism', '2', 'BinaryData', 'sqlserver', 'degree_of_parallelism', 'dop', '', ''),
              ('28', 'Degree of Parallelism', '21', 'EventSubClass', 'sqlserver', 'degree_of_parallelism', 'statement_type', '', ''),
              ('28', 'Degree of Parallelism', '25', 'IntegerData', 'sqlserver', 'degree_of_parallelism', 'workspace_memory_grant_kb', '', ''),
              ('34', 'SP:CacheMiss', '1', 'TextData', 'sqlserver', 'sp_cache_miss', 'cached_text', '', ''),
              ('34', 'SP:CacheMiss', '28', 'ObjectType', 'sqlserver', 'sp_cache_miss', 'object_type', '', ''),
              ('35', 'SP:CacheInsert', '1', 'TextData', 'sqlserver', 'sp_cache_insert', 'cached_text', '', ''),
              ('35', 'SP:CacheInsert', '28', 'ObjectType', 'sqlserver', 'sp_cache_insert', 'object_type', '', ''),
              ('36', 'SP:CacheRemove', '1', 'TextData', 'sqlserver', 'sp_cache_remove', 'cached_text', '', ''),
              ('36', 'SP:CacheRemove', '21', 'EventSubClass', 'sqlserver', 'sp_cache_remove', 'remove_method', '', ''),
              ('36', 'SP:CacheRemove', '28', 'ObjectType', 'sqlserver', 'sp_cache_remove', 'object_type', '', ''),
              ('37', 'SP:Recompile', '1', 'TextData', 'sqlserver', 'sql_statement_recompile', 'statement', '', ''),
              ('37', 'SP:Recompile', '21', 'EventSubClass', 'sqlserver', 'sql_statement_recompile', 'recompile_cause', '', ''),
              ('37', 'SP:Recompile', '28', 'ObjectType', 'sqlserver', 'sql_statement_recompile', 'object_type', '', ''),
              ('37', 'SP:Recompile', '29', 'NestLevel', 'sqlserver', 'sql_statement_recompile', 'nest_level', '', ''),
              ('37', 'SP:Recompile', '55', 'IntegerData2', 'sqlserver', 'sql_statement_recompile', 'offset_end', '', ''),
              ('38', 'SP:CacheHit', '1', 'TextData', 'sqlserver', 'sp_cache_hit', 'cached_text', '', ''),
              ('38', 'SP:CacheHit', '28', 'ObjectType', 'sqlserver', 'sp_cache_hit', 'object_type', '', ''),
              ('40', 'SQL:StmtStarting', '1', 'TextData', 'sqlserver', 'sql_statement_starting', 'statement', '', ''),
              ('40', 'SQL:StmtStarting', '29', 'NestLevel', 'sqlserver', 'sql_statement_starting', '', 'sqlserver', 'tsql_stack'),
              ('40', 'SQL:StmtStarting', '55', 'IntegerData2', 'sqlserver', 'sql_statement_starting', 'offset_end', '', ''),
              ('41', 'SQL:StmtCompleted', '1', 'TextData', 'sqlserver', 'sql_statement_completed', 'statement', '', ''),
              ('41', 'SQL:StmtCompleted', '25', 'IntegerData', 'sqlserver', 'sql_statement_completed', 'row_count', '', ''),
              ('41', 'SQL:StmtCompleted', '29', 'NestLevel', 'sqlserver', 'sql_statement_completed', '', 'sqlserver', 'tsql_stack'),
              ('41', 'SQL:StmtCompleted', '55', 'IntegerData2', 'sqlserver', 'sql_statement_completed', 'offset_end', '', ''),
              ('42', 'SP:Starting', '1', 'TextData', 'sqlserver', 'module_start', 'statement', '', ''),
              ('42', 'SP:Starting', '28', 'ObjectType', 'sqlserver', 'module_start', 'object_type', '', ''),
              ('42', 'SP:Starting', '29', 'NestLevel', 'sqlserver', 'module_start', '', 'sqlserver', 'tsql_stack'),
              ('42', 'SP:Starting', '62', 'SourceDatabaseID', 'sqlserver', 'module_start', 'source_database_id', '', ''),
              ('43', 'SP:Completed', '1', 'TextData', 'sqlserver', 'module_end', 'statement', '', ''),
              ('43', 'SP:Completed', '28', 'ObjectType', 'sqlserver', 'module_end', 'object_type', '', ''),
              ('43', 'SP:Completed', '29', 'NestLevel', 'sqlserver', 'module_end', '', 'sqlserver', 'tsql_stack'),
              ('43', 'SP:Completed', '62', 'SourceDatabaseID', 'sqlserver', 'module_end', 'source_database_id', '', ''),
              ('44', 'SP:StmtStarting', '1', 'TextData', 'sqlserver', 'sp_statement_starting', 'statement', '', ''),
              ('44', 'SP:StmtStarting', '28', 'ObjectType', 'sqlserver', 'sp_statement_starting', 'object_type', '', ''),
              ('44', 'SP:StmtStarting', '29', 'NestLevel', 'sqlserver', 'sp_statement_starting', 'nest_level', '', ''),
              ('44', 'SP:StmtStarting', '55', 'IntegerData2', 'sqlserver', 'sp_statement_starting', 'offset_end', '', ''),
              ('44', 'SP:StmtStarting', '62', 'SourceDatabaseID', 'sqlserver', 'sp_statement_starting', 'source_database_id', '', ''),
              ('45', 'SP:StmtCompleted', '1', 'TextData', 'sqlserver', 'sp_statement_completed', 'statement', '', ''),
              ('45', 'SP:StmtCompleted', '28', 'ObjectType', 'sqlserver', 'sp_statement_completed', 'object_type', '', ''),
              ('45', 'SP:StmtCompleted', '29', 'NestLevel', 'sqlserver', 'sp_statement_completed', 'nest_level', '', ''),
              ('45', 'SP:StmtCompleted', '55', 'IntegerData2', 'sqlserver', 'sp_statement_completed', 'offset_end', '', ''),
              ('45', 'SP:StmtCompleted', '62', 'SourceDatabaseID', 'sqlserver', 'sp_statement_completed', 'source_database_id', '', ''),
              ('46', 'Object:Created', '21', 'EventSubClass', 'sqlserver', 'object_created', 'ddl_phase', '', ''),
              --('46', 'Object:Created', '25', 'IntegerData', 'sqlserver', 'object_created', '', 'package0', 'attach_activity_id'),
              ('46', 'Object:Created', '28', 'ObjectType', 'sqlserver', 'object_created', 'object_type', '', ''),
              ('46', 'Object:Created', '56', 'ObjectID2', 'sqlserver', 'object_created', 'related_object_id', '', ''),
              ('47', 'Object:Deleted', '21', 'EventSubClass', 'sqlserver', 'object_deleted', 'ddl_phase', '', ''),
              --('47', 'Object:Deleted', '25', 'IntegerData', 'sqlserver', 'object_deleted', '', 'package0', 'attach_activity_id'),
              ('47', 'Object:Deleted', '28', 'ObjectType', 'sqlserver', 'object_deleted', 'object_type', '', ''),
              ('47', 'Object:Deleted', '56', 'ObjectID2', 'sqlserver', 'object_deleted', 'related_object_id', '', ''),
              ('50', 'SQLTransaction', '21', 'EventSubClass', 'sqlserver', 'sql_transaction', 'transaction_state', '', ''),
              ('50', 'SQLTransaction', '25', 'IntegerData', 'sqlserver', 'sql_transaction', 'transaction_type', '', ''),
              ('53', 'CursorOpen', '25', 'IntegerData', 'sqlserver', 'cursor_open', 'cursor_type', '', ''),
              ('53', 'CursorOpen', '33', 'Handle', 'sqlserver', 'cursor_open', 'protocol_execution_id', '', ''),
              ('54', 'TransactionLog', '22', 'ObjectID', 'sqlserver', 'transaction_log', 'alloc_unit_id', '', ''),
              ('54', 'TransactionLog', '24', 'IndexID', 'sqlserver', 'transaction_log', 'alloc_unit_id', '', ''),
              ('55', 'Hash Warning', '21', 'EventSubClass', 'sqlserver', 'hash_warning', 'hash_warning_type', '', ''),
              ('55', 'Hash Warning', '22', 'ObjectID', 'sqlserver', 'hash_warning', 'query_operation_node_id', '', ''),
              ('55', 'Hash Warning', '25', 'IntegerData', 'sqlserver', 'hash_warning', 'recursion_level', '', ''),
              ('58', 'Auto Stats', '1', 'TextData', 'sqlserver', 'auto_stats', 'statistics_list', '', ''),
              ('58', 'Auto Stats', '21', 'EventSubClass', 'sqlserver', 'auto_stats', 'status', '', ''),
              ('58', 'Auto Stats', '25', 'IntegerData', 'sqlserver', 'auto_stats', 'count', '', ''),
              ('58', 'Auto Stats', '31', 'Error', 'sqlserver', 'auto_stats', 'last_error', '', ''),
              ('58', 'Auto Stats', '55', 'IntegerData2', 'sqlserver', 'auto_stats', 'job_id', '', ''),
              ('58', 'Auto Stats', '57', 'Type', 'sqlserver', 'auto_stats', 'job_type', '', ''),
              ('59', 'Lock:Deadlock Chain', '1', 'TextData', 'sqlserver', 'lock_deadlock_chain', 'resource_description', '', ''),
              ('59', 'Lock:Deadlock Chain', '2', 'BinaryData', 'sqlserver', 'lock_deadlock_chain', 'lockspace_workspace_id', '', ''),
              ('59', 'Lock:Deadlock Chain', '21', 'EventSubClass', 'sqlserver', 'lock_deadlock_chain', 'resource_owner_type', '', ''),
              ('59', 'Lock:Deadlock Chain', '25', 'IntegerData', 'sqlserver', 'lock_deadlock_chain', 'deadlock_id', '', ''),
              ('59', 'Lock:Deadlock Chain', '56', 'ObjectID2', 'sqlserver', 'lock_deadlock_chain', 'associated_object_id', '', ''),
              ('59', 'Lock:Deadlock Chain', '57', 'Type', 'sqlserver', 'lock_deadlock_chain', 'resource_type', '', ''),
              ('59', 'Lock:Deadlock Chain', '58', 'OwnerID', 'sqlserver', 'lock_deadlock_chain', 'owner_type', '', ''),
              ('60', 'Lock:Escalation', '1', 'TextData', 'sqlserver', 'lock_escalation', 'statement', '', ''),
              ('60', 'Lock:Escalation', '21', 'EventSubClass', 'sqlserver', 'lock_escalation', 'escalation_cause', '', ''),
              ('60', 'Lock:Escalation', '25', 'IntegerData', 'sqlserver', 'lock_escalation', 'hobt_lock_count', '', ''),
              ('60', 'Lock:Escalation', '55', 'IntegerData2', 'sqlserver', 'lock_escalation', 'escalated_lock_count', '', ''),
              ('60', 'Lock:Escalation', '56', 'ObjectID2', 'sqlserver', 'lock_escalation', 'hobt_id', '', ''),
              ('60', 'Lock:Escalation', '57', 'Type', 'sqlserver', 'lock_escalation', 'resource_type', '', ''),
              ('60', 'Lock:Escalation', '58', 'OwnerID', 'sqlserver', 'lock_escalation', 'owner_type', '', ''),
              ('61', 'OLEDB Errors', '1', 'TextData', 'sqlserver', 'oledb_error', 'parameters', '', ''),
              ('61', 'OLEDB Errors', '31', 'Error', 'sqlserver', 'oledb_error', 'hresult', '', ''),
              ('61', 'OLEDB Errors', '45', 'LinkedServerName', 'sqlserver', 'oledb_error', 'linked_server_name', '', ''),
              ('61', 'OLEDB Errors', '46', 'ProviderName', 'sqlserver', 'oledb_error', 'provider_name', '', ''),
              ('61', 'OLEDB Errors', '47', 'MethodName', 'sqlserver', 'oledb_error', 'method_name', '', ''),
              ('67', 'Execution Warnings', '1', 'TextData', 'sqlserver', 'execution_warning', 'server_memory_grants', '', ''),
              ('67', 'Execution Warnings', '21', 'EventSubClass', 'sqlserver', 'execution_warning', 'warning_type', '', ''),
              ('68', 'Showplan Text (Unencoded)', '1', 'TextData', 'sqlserver', 'query_pre_execution_showplan', 'showplan_xml', '', ''),
              ('68', 'Showplan Text (Unencoded)', '2', 'BinaryData', 'sqlserver', 'query_pre_execution_showplan', 'estimated_cost', '', ''),
              ('68', 'Showplan Text (Unencoded)', '25', 'IntegerData', 'sqlserver', 'query_pre_execution_showplan', 'estimated_rows', '', ''),
              ('68', 'Showplan Text (Unencoded)', '28', 'ObjectType', 'sqlserver', 'query_pre_execution_showplan', 'object_type', '', ''),
              ('68', 'Showplan Text (Unencoded)', '29', 'NestLevel', 'sqlserver', 'query_pre_execution_showplan', 'nest_level', '', ''),
              ('69', 'Sort Warnings', '21', 'EventSubClass', 'sqlserver', 'sort_warning', 'sort_warning_type', '', ''),
              ('70', 'CursorPrepare', '33', 'Handle', 'sqlserver', 'cursor_prepare', 'protocol_execution_id', '', ''),
              ('71', 'Prepare SQL', '33', 'Handle', 'sqlserver', 'prepare_sql', 'statement_handle', '', ''),
              ('72', 'Exec Prepared SQL', '33', 'Handle', 'sqlserver', 'exec_prepared_sql', 'statement_handle', '', ''),
              ('73', 'Unprepare SQL', '33', 'Handle', 'sqlserver', 'unprepare_sql', 'statement_handle', '', ''),
              ('74', 'CursorExecute', '25', 'IntegerData', 'sqlserver', 'cursor_execute', 'cursor_type', '', ''),
              ('74', 'CursorExecute', '33', 'Handle', 'sqlserver', 'cursor_execute', 'protocol_execution_id', '', ''),
              ('76', 'CursorImplicitConversion', '2', 'BinaryData', 'sqlserver', 'cursor_implicit_conversion', 'final_cursor_type', '', ''),
              ('76', 'CursorImplicitConversion', '25', 'IntegerData', 'sqlserver', 'cursor_implicit_conversion', 'initial_cursor_type', '', ''),
              ('76', 'CursorImplicitConversion', '33', 'Handle', 'sqlserver', 'cursor_implicit_conversion', 'protocol_execution_id', '', ''),
              ('77', 'CursorUnprepare', '33', 'Handle', 'sqlserver', 'cursor_unprepare', 'protocol_execution_id', '', ''),
              ('78', 'CursorClose', '33', 'Handle', 'sqlserver', 'cursor_close', 'protocol_execution_id', '', ''),
              ('79', 'Missing Column Statistics', '1', 'TextData', 'sqlserver', 'missing_column_statistics', 'column_list', '', ''),
              ('81', 'Server Memory Change', '21', 'EventSubClass', 'sqlserver', 'server_memory_change', 'memory_change', '', ''),
              ('81', 'Server Memory Change', '25', 'IntegerData', 'sqlserver', 'server_memory_change', 'new_memory_size_mb', '', ''),
              ('92', 'Data File Auto Grow', '25', 'IntegerData', 'sqlserver', 'database_file_size_change', 'size_change_kb', '', ''),
              ('92', 'Data File Auto Grow', '36', 'FileName', 'sqlserver', 'database_file_size_change', 'file_name', '', ''),
              ('93', 'Log File Auto Grow', '25', 'IntegerData', 'sqlserver', 'database_file_size_change', 'size_change_kb', '', ''),
              ('93', 'Log File Auto Grow', '36', 'FileName', 'sqlserver', 'database_file_size_change', 'file_name', '', ''),
              ('94', 'Data File Auto Shrink', '25', 'IntegerData', 'sqlserver', 'database_file_size_change', 'size_change_kb', '', ''),
              ('94', 'Data File Auto Shrink', '36', 'FileName', 'sqlserver', 'database_file_size_change', 'file_name', '', ''),
              ('95', 'Log File Auto Shrink', '25', 'IntegerData', 'sqlserver', 'database_file_size_change', 'size_change_kb', '', ''),
              ('95', 'Log File Auto Shrink', '36', 'FileName', 'sqlserver', 'database_file_size_change', 'file_name', '', ''),
              ('96', 'Showplan Text', '2', 'BinaryData', 'sqlserver', 'query_pre_execution_showplan', 'estimated_cost', '', ''),
              ('96', 'Showplan Text', '25', 'IntegerData', 'sqlserver', 'query_pre_execution_showplan', 'estimated_rows', '', ''),
              ('96', 'Showplan Text', '28', 'ObjectType', 'sqlserver', 'query_pre_execution_showplan', 'object_type', '', ''),
              ('96', 'Showplan Text', '29', 'NestLevel', 'sqlserver', 'query_pre_execution_showplan', 'nest_level', '', ''),
              ('97', 'Showplan All', '2', 'BinaryData', 'sqlserver', 'query_pre_execution_showplan', 'estimated_cost', '', ''),
              ('97', 'Showplan All', '25', 'IntegerData', 'sqlserver', 'query_pre_execution_showplan', 'estimated_rows', '', ''),
              ('97', 'Showplan All', '28', 'ObjectType', 'sqlserver', 'query_pre_execution_showplan', 'object_type', '', ''),
              ('97', 'Showplan All', '29', 'NestLevel', 'sqlserver', 'query_pre_execution_showplan', 'nest_level', '', ''),
              ('98', 'Showplan Statistics Profile', '2', 'BinaryData', 'sqlserver', 'query_post_execution_showplan', 'estimated_cost', '', ''),
              ('98', 'Showplan Statistics Profile', '25', 'IntegerData', 'sqlserver', 'query_post_execution_showplan', 'estimated_rows', '', ''),
              ('98', 'Showplan Statistics Profile', '28', 'ObjectType', 'sqlserver', 'query_post_execution_showplan', 'object_type', '', ''),
              ('98', 'Showplan Statistics Profile', '29', 'NestLevel', 'sqlserver', 'query_post_execution_showplan', 'nest_level', '', ''),
              ('100', 'RPC Output Parameter', '1', 'TextData', 'sqlserver', 'rpc_completed', 'output_parameters', '', ''),
              ('119', 'OLEDB Call Event', '1', 'TextData', 'sqlserver', 'oledb_call', 'parameters', '', ''),
              ('119', 'OLEDB Call Event', '21', 'EventSubClass', 'sqlserver', 'oledb_call', 'opcode', '', ''),
              ('119', 'OLEDB Call Event', '31', 'Error', 'sqlserver', 'oledb_call', 'hresult', '', ''),
              ('119', 'OLEDB Call Event', '45', 'LinkedServerName', 'sqlserver', 'oledb_call', 'linked_server_name', '', ''),
              ('119', 'OLEDB Call Event', '46', 'ProviderName', 'sqlserver', 'oledb_call', 'provider_name', '', ''),
              ('119', 'OLEDB Call Event', '47', 'MethodName', 'sqlserver', 'oledb_call', 'method_name', '', ''),
              ('120', 'OLEDB QueryInterface Event', '1', 'TextData', 'sqlserver', 'oledb_query_interface', 'parameters', '', ''),
              ('120', 'OLEDB QueryInterface Event', '21', 'EventSubClass', 'sqlserver', 'oledb_query_interface', 'opcode', '', ''),
              ('120', 'OLEDB QueryInterface Event', '31', 'Error', 'sqlserver', 'oledb_query_interface', 'hresult', '', ''),
              ('120', 'OLEDB QueryInterface Event', '45', 'LinkedServerName', 'sqlserver', 'oledb_query_interface', 'linked_server_name', '', ''),
              ('120', 'OLEDB QueryInterface Event', '46', 'ProviderName', 'sqlserver', 'oledb_query_interface', 'provider_name', '', ''),
              ('120', 'OLEDB QueryInterface Event', '47', 'MethodName', 'sqlserver', 'oledb_query_interface', 'method_name', '', ''),
              ('121', 'OLEDB DataRead Event', '1', 'TextData', 'sqlserver', 'oledb_data_read', 'parameters', '', ''),
              ('121', 'OLEDB DataRead Event', '21', 'EventSubClass', 'sqlserver', 'oledb_data_read', 'opcode', '', ''),
              ('121', 'OLEDB DataRead Event', '31', 'Error', 'sqlserver', 'oledb_data_read', 'hresult', '', ''),
              ('121', 'OLEDB DataRead Event', '45', 'LinkedServerName', 'sqlserver', 'oledb_data_read', 'linked_server_name', '', ''),
              ('121', 'OLEDB DataRead Event', '46', 'ProviderName', 'sqlserver', 'oledb_data_read', 'provider_name', '', ''),
              ('121', 'OLEDB DataRead Event', '47', 'MethodName', 'sqlserver', 'oledb_data_read', 'method_name', '', ''),
              ('122', 'Showplan XML', '1', 'TextData', 'sqlserver', 'query_pre_execution_showplan', 'showplan_xml', '', ''),
              ('122', 'Showplan XML', '2', 'BinaryData', 'sqlserver', 'query_pre_execution_showplan', 'estimated_cost', '', ''),
              ('122', 'Showplan XML', '25', 'IntegerData', 'sqlserver', 'query_pre_execution_showplan', 'estimated_rows', '', ''),
              ('122', 'Showplan XML', '28', 'ObjectType', 'sqlserver', 'query_pre_execution_showplan', 'object_type', '', ''),
              ('122', 'Showplan XML', '29', 'NestLevel', 'sqlserver', 'query_pre_execution_showplan', 'nest_level', '', ''),
              ('124', 'Broker:Conversation', '1', 'TextData', 'sqlserver', 'broker_conversation', 'conversation_state', '', ''),
              ('124', 'Broker:Conversation', '21', 'EventSubClass', 'sqlserver', 'broker_conversation', 'conversation_action', '', ''),
              ('124', 'Broker:Conversation', '34', 'ObjectName', 'sqlserver', 'broker_conversation', 'conversation_handle', '', ''),
              ('124', 'Broker:Conversation', '38', 'RoleName', 'sqlserver', 'broker_conversation', 'is_initiator', '', ''),
              ('124', 'Broker:Conversation', '42', 'TargetLoginName', 'sqlserver', 'broker_conversation', 'service_contract_name', '', ''),
              ('124', 'Broker:Conversation', '47', 'MethodName', 'sqlserver', 'broker_conversation', 'conversation_group_id', '', ''),
              ('124', 'Broker:Conversation', '54', 'GUID', 'sqlserver', 'broker_conversation', 'conversation_id', '', ''),
              ('125', 'Deprecation Announcement', '1', 'TextData', 'sqlserver', 'deprecation_announcement', 'message', '', ''),
              ('125', 'Deprecation Announcement', '22', 'ObjectID', 'sqlserver', 'deprecation_announcement', 'feature_id', '', ''),
              ('125', 'Deprecation Announcement', '34', 'ObjectName', 'sqlserver', 'deprecation_announcement', 'feature', '', ''),
              ('125', 'Deprecation Announcement', '55', 'IntegerData2', 'sqlserver', 'deprecation_announcement', '', 'sqlserver', 'tsql_frame'),
              ('126', 'Deprecation Final Support', '1', 'TextData', 'sqlserver', 'deprecation_final_support', 'message', '', ''),
              ('126', 'Deprecation Final Support', '22', 'ObjectID', 'sqlserver', 'deprecation_final_support', 'feature_id', '', ''),
              ('126', 'Deprecation Final Support', '34', 'ObjectName', 'sqlserver', 'deprecation_final_support', 'feature', '', ''),
              ('126', 'Deprecation Final Support', '55', 'IntegerData2', 'sqlserver', 'deprecation_final_support', '', 'sqlserver', 'tsql_frame'),
              ('127', 'Exchange Spill Event', '21', 'EventSubClass', 'sqlserver', 'exchange_spill', 'opcode', '', ''),
              ('127', 'Exchange Spill Event', '22', 'ObjectID', 'sqlserver', 'exchange_spill', 'query_operation_node_id', '', ''),
              ('136', 'Broker:Conversation Group', '21', 'EventSubClass', 'sqlserver', 'broker_conversation_group', 'conversation_group_action', '', ''),
              ('136', 'Broker:Conversation Group', '54', 'GUID', 'sqlserver', 'broker_conversation_group', 'conversation_group_id', '', ''),
              ('137', 'Blocked process report', '1', 'TextData', 'sqlserver', 'blocked_process_report', 'blocked_process', '', ''),
              ('137', 'Blocked process report', '32', 'Mode', 'sqlserver', 'blocked_process_report', 'lock_mode', '', ''),
              ('138', 'Broker:Connection', '1', 'TextData', 'ucs', 'ucs_connection_setup', 'error_message', '', ''),
              ('138', 'Broker:Connection', '21', 'EventSubClass', 'ucs', 'ucs_connection_setup', 'setup_event', '', ''),
              ('138', 'Broker:Connection', '34', 'ObjectName', 'ucs', 'ucs_connection_setup', 'connection_id', '', ''),
              ('138', 'Broker:Connection', '54', 'GUID', 'ucs', 'ucs_connection_setup', 'address', '', ''),
              ('139', 'Broker:Forwarded Message Sent', '22', 'ObjectID', 'sqlserver', 'broker_forwarded_message_sent', 'time_to_live_sec', '', ''),
              ('139', 'Broker:Forwarded Message Sent', '23', 'Success', 'sqlserver', 'broker_forwarded_message_sent', 'live_time_sec', '', ''),
              ('139', 'Broker:Forwarded Message Sent', '24', 'IndexID', 'sqlserver', 'broker_forwarded_message_sent', 'remaining_hop_count', '', ''),
              ('139', 'Broker:Forwarded Message Sent', '25', 'IntegerData', 'sqlserver', 'broker_forwarded_message_sent', 'fragment_number', '', ''),
              ('139', 'Broker:Forwarded Message Sent', '36', 'FileName', 'sqlserver', 'broker_forwarded_message_sent', 'to_service_name', '', ''),
              ('139', 'Broker:Forwarded Message Sent', '37', 'OwnerName', 'sqlserver', 'broker_forwarded_message_sent', 'to_broker_name', '', ''),
              ('139', 'Broker:Forwarded Message Sent', '38', 'RoleName', 'sqlserver', 'broker_forwarded_message_sent', 'is_initiator', '', ''),
              ('139', 'Broker:Forwarded Message Sent', '39', 'TargetUserName', 'sqlserver', 'broker_forwarded_message_sent', 'from_service_name', '', ''),
              ('139', 'Broker:Forwarded Message Sent', '40', 'DBUserName', 'sqlserver', 'broker_forwarded_message_sent', 'from_broker_name', '', ''),
              ('139', 'Broker:Forwarded Message Sent', '42', 'TargetLoginName', 'sqlserver', 'broker_forwarded_message_sent', 'to_broker_name', '', ''),
              ('139', 'Broker:Forwarded Message Sent', '47', 'MethodName', 'sqlserver', 'broker_forwarded_message_sent', 'message_type_name', '', ''),
              ('139', 'Broker:Forwarded Message Sent', '52', 'BigintData1', 'sqlserver', 'broker_forwarded_message_sent', 'message_sequence', '', ''),
              ('139', 'Broker:Forwarded Message Sent', '54', 'GUID', 'sqlserver', 'broker_forwarded_message_sent', 'conversation_id', '', ''),
              ('140', 'Broker:Forwarded Message Dropped', '1', 'TextData', 'sqlserver', 'broker_forwarded_message_dropped', 'dropped_reason', '', ''),
              ('140', 'Broker:Forwarded Message Dropped', '20', 'Severity', 'sqlserver', 'broker_forwarded_message_dropped', 'error_severity', '', ''),
              ('140', 'Broker:Forwarded Message Dropped', '22', 'ObjectID', 'sqlserver', 'broker_forwarded_message_dropped', 'time_to_live_sec', '', ''),
              ('140', 'Broker:Forwarded Message Dropped', '24', 'IndexID', 'sqlserver', 'broker_forwarded_message_dropped', 'remaining_hop_count', '', ''),
              ('140', 'Broker:Forwarded Message Dropped', '25', 'IntegerData', 'sqlserver', 'broker_forwarded_message_dropped', 'fragment_number', '', ''),
              ('140', 'Broker:Forwarded Message Dropped', '30', 'State', 'sqlserver', 'broker_forwarded_message_dropped', 'error_state', '', ''),
              ('140', 'Broker:Forwarded Message Dropped', '36', 'FileName', 'sqlserver', 'broker_forwarded_message_dropped', 'to_service_name', '', ''),
              ('140', 'Broker:Forwarded Message Dropped', '37', 'OwnerName', 'sqlserver', 'broker_forwarded_message_dropped', 'to_broker_name', '', ''),
              ('140', 'Broker:Forwarded Message Dropped', '38', 'RoleName', 'sqlserver', 'broker_forwarded_message_dropped', 'is_initiator', '', ''),
              ('140', 'Broker:Forwarded Message Dropped', '39', 'TargetUserName', 'sqlserver', 'broker_forwarded_message_dropped', 'from_service_name', '', ''),
              ('140', 'Broker:Forwarded Message Dropped', '40', 'DBUserName', 'sqlserver', 'broker_forwarded_message_dropped', 'from_broker_name', '', ''),
              ('140', 'Broker:Forwarded Message Dropped', '42', 'TargetLoginName', 'sqlserver', 'broker_forwarded_message_dropped', 'to_broker_name', '', ''),
              ('140', 'Broker:Forwarded Message Dropped', '47', 'MethodName', 'sqlserver', 'broker_forwarded_message_dropped', 'message_type_name', '', ''),
              ('140', 'Broker:Forwarded Message Dropped', '52', 'BigintData1', 'sqlserver', 'broker_forwarded_message_dropped', 'message_sequence', '', ''),
              ('140', 'Broker:Forwarded Message Dropped', '54', 'GUID', 'sqlserver', 'broker_forwarded_message_dropped', 'conversation_id', '', ''),
              ('141', 'Broker:Message Classify', '21', 'EventSubClass', 'sqlserver', 'broker_message_classify', 'route_type', '', ''),
              ('141', 'Broker:Message Classify', '31', 'Error', 'sqlserver', 'broker_message_classify', 'delayed_error_number', '', ''),
              ('141', 'Broker:Message Classify', '36', 'FileName', 'sqlserver', 'broker_message_classify', 'to_service_name', '', ''),
              ('141', 'Broker:Message Classify', '37', 'OwnerName', 'sqlserver', 'broker_message_classify', 'to_broker_instance', '', ''),
              ('141', 'Broker:Message Classify', '38', 'RoleName', 'sqlserver', 'broker_message_classify', 'is_initiator', '', ''),
              ('141', 'Broker:Message Classify', '45', 'LinkedServerName', 'sqlserver', 'broker_message_classify', 'message_source', '', ''),
              ('141', 'Broker:Message Classify', '54', 'GUID', 'sqlserver', 'broker_message_classify', 'conversation_id', '', ''),
              ('142', 'Broker:Transmission', '20', 'Severity', 'sqlserver', 'broker_transmission_exception', 'error_severity', '', ''),
              ('142', 'Broker:Transmission', '30', 'State', 'sqlserver', 'broker_transmission_exception', 'error_state', '', ''),
              ('146', 'Showplan XML Statistics Profile', '1', 'TextData', 'sqlserver', 'query_post_execution_showplan', 'showplan_xml', '', ''),
              ('146', 'Showplan XML Statistics Profile', '2', 'BinaryData', 'sqlserver', 'query_post_execution_showplan', 'estimated_cost', '', ''),
              ('146', 'Showplan XML Statistics Profile', '25', 'IntegerData', 'sqlserver', 'query_post_execution_showplan', 'estimated_rows', '', ''),
              ('146', 'Showplan XML Statistics Profile', '28', 'ObjectType', 'sqlserver', 'query_post_execution_showplan', 'object_type', '', ''),
              ('146', 'Showplan XML Statistics Profile', '29', 'NestLevel', 'sqlserver', 'query_post_execution_showplan', 'nest_level', '', ''),
              ('148', 'Deadlock graph', '1', 'TextData', 'sqlserver', 'xml_deadlock_report', 'xml_report', '', ''),
              ('149', 'Broker:Remote Message Acknowledgement', '21', 'EventSubClass', 'sqlserver', 'broker_remote_message_acknowledgement', 'acknowledgement_type', '', ''),
              ('149', 'Broker:Remote Message Acknowledgement', '25', 'IntegerData', 'sqlserver', 'broker_remote_message_acknowledgement', 'acknowledgement_fragment_number', '', ''),
              ('149', 'Broker:Remote Message Acknowledgement', '38', 'RoleName', 'sqlserver', 'broker_remote_message_acknowledgement', 'is_initiator', '', ''),
              ('149', 'Broker:Remote Message Acknowledgement', '52', 'BigintData1', 'sqlserver', 'broker_remote_message_acknowledgement', 'acknowlegment_message_sequence', '', ''),
              ('149', 'Broker:Remote Message Acknowledgement', '53', 'BigintData2', 'sqlserver', 'broker_remote_message_acknowledgement', 'message_sequence', '', ''),
              ('149', 'Broker:Remote Message Acknowledgement', '54', 'GUID', 'sqlserver', 'broker_remote_message_acknowledgement', 'conversation_id', '', ''),
              ('149', 'Broker:Remote Message Acknowledgement', '55', 'IntegerData2', 'sqlserver', 'broker_remote_message_acknowledgement', 'fragment_number', '', ''),
              ('155', 'FT:Crawl Started', '1', 'TextData', 'sqlserver', 'full_text_crawl_started', 'crawl_operation', '', ''),
              ('160', 'Broker:Message Undeliverable', '1', 'TextData', 'sqlserver', 'broker_message_undeliverable', 'message_drop_reason', '', ''),
              ('160', 'Broker:Message Undeliverable', '20', 'Severity', 'sqlserver', 'broker_message_undeliverable', 'error_severity', '', ''),
              ('160', 'Broker:Message Undeliverable', '21', 'EventSubClass', 'sqlserver', 'broker_message_undeliverable', 'sequenced_message', '', ''),
              ('160', 'Broker:Message Undeliverable', '25', 'IntegerData', 'sqlserver', 'broker_message_undeliverable', 'message_fragment_number', '', ''),
              ('160', 'Broker:Message Undeliverable', '30', 'State', 'sqlserver', 'broker_message_undeliverable', 'error_state', '', ''),
              ('160', 'Broker:Message Undeliverable', '38', 'RoleName', 'sqlserver', 'broker_message_undeliverable', 'is_initiator', '', ''),
              ('160', 'Broker:Message Undeliverable', '52', 'BigintData1', 'sqlserver', 'broker_message_undeliverable', 'message_sequence_number', '', ''),
              ('160', 'Broker:Message Undeliverable', '53', 'BigintData2', 'sqlserver', 'broker_message_undeliverable', 'acknowledgement_sequence_number', '', ''),
              ('160', 'Broker:Message Undeliverable', '54', 'GUID', 'sqlserver', 'broker_message_undeliverable', 'conversation_id', '', ''),
              ('160', 'Broker:Message Undeliverable', '55', 'IntegerData2', 'sqlserver', 'broker_message_undeliverable', 'acknowledgement_fragment_number', '', ''),
              ('161', 'Broker:Corrupted Message', '1', 'TextData', 'sqlserver', 'broker_corrupted_message', 'corruption_description', '', ''),
              ('161', 'Broker:Corrupted Message', '20', 'Severity', 'sqlserver', 'broker_corrupted_message', 'error_severity', '', ''),
              ('161', 'Broker:Corrupted Message', '30', 'State', 'sqlserver', 'broker_corrupted_message', 'error_state', '', ''),
              ('162', 'User Error Message', '1', 'TextData', 'sqlserver', 'error_reported', 'message', '', ''),
              ('163', 'Broker:Activation', '1', 'TextData', 'sqlserver', 'broker_activation', 'activation_message', '', ''),
              ('163', 'Broker:Activation', '21', 'EventSubClass', 'sqlserver', 'broker_activation', 'activation_state', '', ''),
              ('163', 'Broker:Activation', '22', 'ObjectID', 'sqlserver', 'broker_activation', 'queue_id', '', ''),
              ('163', 'Broker:Activation', '25', 'IntegerData', 'sqlserver', 'broker_activation', 'active_task_count', '', ''),
              ('164', 'Object:Altered', '21', 'EventSubClass', 'sqlserver', 'object_altered', 'ddl_phase', '', ''),
              --('164', 'Object:Altered', '25', 'IntegerData', 'sqlserver', 'object_altered', '', 'package0', 'attach_activity_id'),
              ('164', 'Object:Altered', '28', 'ObjectType', 'sqlserver', 'object_altered', 'object_type', '', ''),
              ('164', 'Object:Altered', '56', 'ObjectID2', 'sqlserver', 'object_altered', 'related_object_id', '', ''),
              ('165', 'Performance statistics', '1', 'TextData', 'sqlserver', 'query_cache_removal_statistics', 'execution_statistics', '', ''),
              ('165', 'Performance statistics', '1', 'TextData', 'sqlserver', 'query_pre_execution_showplan', 'showplan_xml', '', ''),
              ('165', 'Performance statistics', '1', 'TextData', 'sqlserver', 'uncached_sql_batch_statistics', 'statement', '', ''),
              ('165', 'Performance statistics', '2', 'BinaryData', 'sqlserver', 'query_pre_execution_showplan', 'estimated_cost', '', ''),
              ('165', 'Performance statistics', '22', 'ObjectID', 'sqlserver', 'query_cache_removal_statistics', 'compiled_object_id', '', ''),
              ('165', 'Performance statistics', '25', 'IntegerData', 'sqlserver', 'query_cache_removal_statistics', 'begin_offset', '', ''),
              ('165', 'Performance statistics', '25', 'IntegerData', 'sqlserver', 'query_pre_execution_showplan', 'estimated_rows', '', ''),
              ('165', 'Performance statistics', '28', 'ObjectType', 'sqlserver', 'query_cache_removal_statistics', 'compiled_object_type', '', ''),
              ('165', 'Performance statistics', '28', 'ObjectType', 'sqlserver', 'query_pre_execution_showplan', 'object_type', '', ''),
              ('165', 'Performance statistics', '52', 'BigintData1', 'sqlserver', 'query_cache_removal_statistics', 'recompile_count', '', ''),
              ('165', 'Performance statistics', '55', 'IntegerData2', 'sqlserver', 'query_cache_removal_statistics', 'end_offset', '', ''),
              ('166', 'SQL:StmtRecompile', '1', 'TextData', 'sqlserver', 'sql_statement_recompile', 'statement', '', ''),
              ('166', 'SQL:StmtRecompile', '21', 'EventSubClass', 'sqlserver', 'sql_statement_recompile', 'recompile_cause', '', ''),
              ('166', 'SQL:StmtRecompile', '28', 'ObjectType', 'sqlserver', 'sql_statement_recompile', 'object_type', '', ''),
              ('166', 'SQL:StmtRecompile', '29', 'NestLevel', 'sqlserver', 'sql_statement_recompile', 'nest_level', '', ''),
              ('166', 'SQL:StmtRecompile', '55', 'IntegerData2', 'sqlserver', 'sql_statement_recompile', 'offset_end', '', ''),
              ('167', 'Database Mirroring State Change', '1', 'TextData', 'sqlserver', 'database_mirroring_state_change', 'state_change_desc', '', ''),
              ('167', 'Database Mirroring State Change', '25', 'IntegerData', 'sqlserver', 'database_mirroring_state_change', 'prior_state', '', ''),
              ('167', 'Database Mirroring State Change', '30', 'State', 'sqlserver', 'database_mirroring_state_change', 'new_state', '', ''),
              ('168', 'Showplan XML For Query Compile', '1', 'TextData', 'sqlserver', 'query_post_compilation_showplan', 'showplan_xml', '', ''),
              ('168', 'Showplan XML For Query Compile', '2', 'BinaryData', 'sqlserver', 'query_post_compilation_showplan', 'estimated_cost', '', ''),
              ('168', 'Showplan XML For Query Compile', '25', 'IntegerData', 'sqlserver', 'query_post_compilation_showplan', 'estimated_rows', '', ''),
              ('168', 'Showplan XML For Query Compile', '28', 'ObjectType', 'sqlserver', 'query_post_compilation_showplan', 'object_type', '', ''),
              ('168', 'Showplan XML For Query Compile', '29', 'NestLevel', 'sqlserver', 'query_post_compilation_showplan', 'nest_level', '', ''),
              ('169', 'Showplan All For Query Compile', '2', 'BinaryData', 'sqlserver', 'query_post_compilation_showplan', 'estimated_cost', '', ''),
              ('169', 'Showplan All For Query Compile', '25', 'IntegerData', 'sqlserver', 'query_post_compilation_showplan', 'estimated_rows', '', ''),
              ('169', 'Showplan All For Query Compile', '28', 'ObjectType', 'sqlserver', 'query_post_compilation_showplan', 'object_type', '', ''),
              ('169', 'Showplan All For Query Compile', '29', 'NestLevel', 'sqlserver', 'query_post_compilation_showplan', 'nest_level', '', ''),
              ('181', 'TM: Begin Tran starting', '1', 'TextData', 'sqlserver', 'begin_tran_starting', 'statement', '', ''),
              ('182', 'TM: Begin Tran completed', '1', 'TextData', 'sqlserver', 'begin_tran_completed', 'statement', '', ''),
              ('184', 'TM: Promote Tran completed', '2', 'BinaryData', 'sqlserver', 'promote_tran_completed', 'dtc_trasaction_token', '', ''),
              ('185', 'TM: Commit Tran starting', '1', 'TextData', 'sqlserver', 'commit_tran_starting', 'statement', '', ''),
              ('185', 'TM: Commit Tran starting', '21', 'EventSubClass', 'sqlserver', 'commit_tran_starting', 'new_transaction_started', '', ''),
              ('186', 'TM: Commit Tran completed', '1', 'TextData', 'sqlserver', 'commit_tran_completed', 'statement', '', ''),
              ('186', 'TM: Commit Tran completed', '21', 'EventSubClass', 'sqlserver', 'commit_tran_completed', 'new_transaction_started', '', ''),
              ('187', 'TM: Rollback Tran starting', '1', 'TextData', 'sqlserver', 'rollback_tran_starting', 'statement', '', ''),
              ('187', 'TM: Rollback Tran starting', '21', 'EventSubClass', 'sqlserver', 'rollback_tran_starting', 'new_transaction_started', '', ''),
              ('188', 'TM: Rollback Tran completed', '1', 'TextData', 'sqlserver', 'rollback_tran_completed', 'statement', '', ''),
              ('188', 'TM: Rollback Tran completed', '21', 'EventSubClass', 'sqlserver', 'rollback_tran_completed', 'new_transaction_started', '', ''),
              ('189', 'Lock:Timeout (timeout > 0)', '1', 'TextData', 'sqlserver', 'lock_timeout_greater_than_0', 'resource_description', '', ''),
              ('189', 'Lock:Timeout (timeout > 0)', '2', 'BinaryData', 'sqlserver', 'lock_timeout_greater_than_0', 'lockspace_workspace_id', '', ''),
              ('189', 'Lock:Timeout (timeout > 0)', '56', 'ObjectID2', 'sqlserver', 'lock_timeout_greater_than_0', 'associated_object_id', '', ''),
              ('189', 'Lock:Timeout (timeout > 0)', '57', 'Type', 'sqlserver', 'lock_timeout_greater_than_0', 'resource_type', '', ''),
              ('189', 'Lock:Timeout (timeout > 0)', '58', 'OwnerID', 'sqlserver', 'lock_timeout_greater_than_0', 'owner_type', '', ''),
              ('190', 'Progress Report: Online Index Operation', '21', 'EventSubClass', 'sqlserver', 'progress_report_online_index_operation', 'build_stage', '', ''),
              ('190', 'Progress Report: Online Index Operation', '52', 'BigintData1', 'sqlserver', 'progress_report_online_index_operation', 'rows_inserted', '', ''),
              ('190', 'Progress Report: Online Index Operation', '53', 'BigintData2', 'sqlserver', 'progress_report_online_index_operation', 'parallel_process_thread_id', '', ''),
              ('191', 'TM: Save Tran starting', '1', 'TextData', 'sqlserver', 'save_tran_starting', 'statement', '', ''),
              ('192', 'TM: Save Tran completed', '1', 'TextData', 'sqlserver', 'save_tran_completed', 'statement', '', ''),
              ('193', 'Background Job Error', '20', 'Severity', 'sqlserver', 'background_job_error', 'error_severity', '', ''),
              ('193', 'Background Job Error', '21', 'EventSubClass', 'sqlserver', 'background_job_error', 'job_failure_type', '', ''),
              ('193', 'Background Job Error', '25', 'IntegerData', 'sqlserver', 'background_job_error', 'retries', '', ''),
              ('193', 'Background Job Error', '30', 'State', 'sqlserver', 'background_job_error', 'error_state', '', ''),
              ('193', 'Background Job Error', '55', 'IntegerData2', 'sqlserver', 'background_job_error', 'job_id', '', ''),
              ('193', 'Background Job Error', '57', 'Type', 'sqlserver', 'background_job_error', 'job_type', '', ''),
              ('194', 'OLEDB Provider Information', '1', 'TextData', 'sqlserver', 'oledb_provider_information', 'parameters', '', ''),
              ('194', 'OLEDB Provider Information', '45', 'LinkedServerName', 'sqlserver', 'oledb_provider_information', 'linked_server_name', '', ''),
              ('194', 'OLEDB Provider Information', '46', 'ProviderName', 'sqlserver', 'oledb_provider_information', 'provider_name', '', ''),
              ('196', 'Assembly Load', '1', 'TextData', 'sqlserver', 'assembly_load', 'success', '', ''),
              ('196', 'Assembly Load', '22', 'ObjectID', 'sqlserver', 'assembly_load', 'assembly_id', '', ''),
              ('196', 'Assembly Load', '34', 'ObjectName', 'sqlserver', 'assembly_load', 'assembly_name', '', ''),
              ('198', 'XQuery Static Type', '1', 'TextData', 'sqlserver', 'xquery_static_type', 'inferred_type', '', ''),
              ('198', 'XQuery Static Type', '47', 'MethodName', 'sqlserver', 'xquery_static_type', 'oledb_method', '', ''),
              ('199', 'QN: Subscription', '1', 'TextData', 'sqlserver', 'qn_subscription', 'query_notification_xml_information', '', ''),
              ('199', 'QN: Subscription', '21', 'EventSubClass', 'sqlserver', 'qn_subscription', 'activity', '', ''),
              ('200', 'QN: Parameter table', '1', 'TextData', 'sqlserver', 'qn_parameter_table', 'query_notification_xml_information', '', ''),
              ('200', 'QN: Parameter table', '21', 'EventSubClass', 'sqlserver', 'qn_parameter_table', 'activity', '', ''),
              ('201', 'QN: Template', '1', 'TextData', 'sqlserver', 'qn_template', 'query_notification_xml_information', '', ''),
              ('201', 'QN: Template', '21', 'EventSubClass', 'sqlserver', 'qn_template', 'activity', '', ''),
              ('202', 'QN: Dynamics', '1', 'TextData', 'sqlserver', 'qn_dynamics', 'query_notification_xml_information', '', ''),
              ('202', 'QN: Dynamics', '21', 'EventSubClass', 'sqlserver', 'qn_dynamics', 'activity', '', ''),
              ('212', 'Bitmap Warning', '22', 'ObjectID', 'sqlserver', 'bitmap_disabled_warning', 'query_operation_node_id', '', ''),
              ('213', 'Database Suspect Data Page', '31', 'Error', 'sqlserver', 'database_suspect_data_page', 'page_error', '', ''),
              ('214', 'CPU threshold exceeded', '58', 'OwnerID', 'sqlserver', 'cpu_threshold_exceeded', 'session_id', '', ''),
              ('215', 'PreConnect:Starting', '21', 'EventSubClass', 'sqlserver', 'preconnect_starting', 'preconnect_type', '', ''),
              ('215', 'PreConnect:Starting', '28', 'ObjectType', 'sqlserver', 'preconnect_starting', 'object_type', '', ''),
              ('216', 'PreConnect:Completed', '21', 'EventSubClass', 'sqlserver', 'preconnect_completed', 'preconnect_type', '', ''),
              ('216', 'PreConnect:Completed', '28', 'ObjectType', 'sqlserver', 'preconnect_completed', 'object_type', '', ''),
              ('216', 'PreConnect:Completed', '30', 'State', 'sqlserver', 'preconnect_completed', 'error_state', '', ''),
              ('216', 'PreConnect:Completed', '39', 'TargetUserName', 'sqlserver', 'preconnect_completed', 'workload_group_name', '', '')
) AS tab (trace_event_id, trace_event_name, trace_column_id, trace_column_name, event_package_name, xe_event_name, column_name, action_package_name, xe_action_name)

-- Create table variable to hold the trace definition
DECLARE @TraceInfo TABLE
(
       eventid INT,
       te_name NVARCHAR(128) COLLATE SQL_Latin1_General_CP1_CI_AS,
       columnid INT,
       columnname NVARCHAR(128) COLLATE SQL_Latin1_General_CP1_CI_AS,
       event_package_name NVARCHAR(128) COLLATE SQL_Latin1_General_CP1_CI_AS,
       xe_event_name NVARCHAR(128) COLLATE SQL_Latin1_General_CP1_CI_AS,
       column_name NVARCHAR(128) COLLATE SQL_Latin1_General_CP1_CI_AS,
       action_package_name NVARCHAR(128) COLLATE SQL_Latin1_General_CP1_CI_AS,
       xe_action_name NVARCHAR(128) COLLATE SQL_Latin1_General_CP1_CI_AS,
       filter_operator NVARCHAR(128) COLLATE SQL_Latin1_General_CP1_CI_AS,
       filter_value sql_variant 
)

-- Query the trace functions to get the trace definition 
INSERT INTO @TraceInfo 
       (eventid, te_name, columnid, columnname, event_package_name, xe_event_name, 
        column_name, action_package_name, xe_action_name, filter_operator, filter_value)
SELECT 
       eventid,
       te.name,
       tgei.columnid,
       tc.name,
       event_package_name,
       xe_event_name,
       column_name,
       action_package_name,
       xe_action_name,
       CASE comparison_operator
              WHEN 0 THEN '='
              WHEN 1 THEN '<>'
              WHEN 2 THEN '>'
              WHEN 3 THEN '<'
              WHEN 4 THEN '>='
              WHEN 5 THEN '<='
              WHEN 6 THEN 'LIKE'
              WHEN 7 THEN 'NOT LIKE'
       END AS filter_operator,
       value AS filter_value
FROM sys.fn_trace_geteventinfo(@TraceID) AS tgei
LEFT JOIN sys.fn_trace_getfilterinfo(@TraceID) AS tfgi
       ON tgei.columnid = tfgi.columnid
              AND CAST(value AS NVARCHAR) NOT LIKE 'SQL Server Profiler%'
JOIN sys.trace_columns AS tc
       ON tgei.columnid = tc.trace_column_id
JOIN sys.trace_events AS te
       ON tgei.eventid = te.trace_event_id
LEFT JOIN [#SQLskills_Trace_XE_Column_Map] AS txcm
       ON tgei.eventid = txcm.trace_event_id
              AND tgei.columnid = txcm.trace_column_id
ORDER BY tgei.eventid, tgei.columnid

-- Generate the drop command for the Event Session if it already exists
DECLARE @DropCmd NVARCHAR(MAX) = 'IF EXISTS (SELECT 1 FROM sys.server_event_sessions WHERE name = '''+@SessionName+''')' + CHAR(10) +
'      DROP EVENT SESSION '+QUOTENAME(@SessionName)+' ON SERVER;' + CHAR(10)

-- Execute the drop command if @Execute = 1
IF @Execute = 1
       EXECUTE(@DropCmd);

-- Generate the start of the create statement for the Event Sesssion
DECLARE @sqlcmd NVARCHAR(MAX) = 'CREATE EVENT SESSION '+QUOTENAME(@SessionName)+ CHAR(10) +
'ON SERVER' + CHAR(10)

DECLARE @event_list NVARCHAR(MAX) = ''

-- Generate the Event list DDL definition for the event session
SELECT @event_list = @event_list + 
-- Add Events to Session
       CASE 
              WHEN event_package_name IS NULL 
                     THEN '/* ' + tab.te_name + ' is not implemented in Extended Events it may be a Server Audit Event */' + CHAR(10)
              WHEN event_package_name IS NOT NULL AND RowID <> 1
                     THEN '/* ' + tab.te_name + ' is implemented as the ' + tab.event_package_name + '.' + tab.xe_event_name + ' event in Extended Events */' + CHAR(10)
              ELSE
                     'ADD EVENT ' + event_package_name + '.' + xe_event_name + '(' + CHAR(10) +
                           -- Determine whether to create an Action List for Event
                           CASE 
                                  WHEN NOT EXISTS (SELECT 1
                                                                     FROM @TraceInfo AS ti
                                                                     WHERE ti.eventid = tab.eventid
                                                                       AND column_name IS NULL)
                                         OR RowID <> 1
                                         THEN ''
                                  ELSE 
                                         -- Build Action List for Event
                                         CHAR(9) + 'ACTION ' + CHAR(10) +
                                         CHAR(9) + '(' + CHAR(10) +
                                         STUFF((
                                                       SELECT 
                                                              CASE 
                                                                     WHEN action_package_name IS NULL AND columnname NOT IN ('StartTime', 'EndTime')
                                                                           THEN CHAR(9) + CHAR(9) + CHAR(9) + '-- ' + columnname + ' not implemented in XE for this event' + CHAR(10)
                                                                     WHEN num > 1 THEN  CHAR(9) + CHAR(9) + CHAR(9) + '-- ' + columnname + ' implemented by another Action in XE already' + CHAR(10)
                                                                     ELSE CHAR(9) + CHAR(9) + CHAR(9) + ', '+ action_package_name + '.' + xe_action_name  + CHAR(9) + '-- ' + columnname + ' from SQLTrace' + CHAR(10)
                                                              END
                                                       FROM (
                                                              SELECT eventid, action_package_name, xe_action_name, columnname, column_name, row_number() over (partition by eventid, action_package_name, xe_action_name order by columnname) AS num
                                                              FROM @TraceInfo AS ti
                                                              ) AS ti
                                                       WHERE ti.eventid = tab.eventid
                                                         AND column_name IS NULL
                                                       ORDER BY CASE WHEN xe_action_name IS NULL THEN 1 ELSE 0 END, xe_action_name
                                                       FOR XML PATH('')), 1, 4, CHAR(9) + CHAR(9) + CHAR(9)+' ') -- Actions
                                         + CHAR(9) + ')' + CHAR(10) 
                           END +
                     -- Build in Predicate information
                           CASE 
                                  WHEN NOT EXISTS (SELECT 1
                                                                     FROM @TraceInfo AS ti
                                                                     WHERE ti.eventid = tab.eventid
                                                                       AND filter_operator IS NOT NULL)
                                         THEN ''
                                  ELSE 
                                         CHAR(9) + 'WHERE ' + CHAR(10) +
                                         CHAR(9) + '(' + CHAR(10) +
                                         REPLACE(
                                                REPLACE(
                                                       STUFF(
                                                                     (
                                                                           SELECT 
                                                                                  CASE 
                                                                                         WHEN action_package_name IS NULL 
                                                                                                THEN CHAR(9) + CHAR(9) + CHAR(9) + 'AND ' + column_name + ' ' + filter_operator + ' ' + 
                                                                                                       CASE 
                                                                                                              WHEN SQL_VARIANT_PROPERTY(filter_value, 'BaseType') IN ('nvarchar', 'varchar', 'char', 'nchar') 
                                                                                                                     THEN '''%'+CAST(filter_value AS NVARCHAR)+'%'''
                                                                                                              ELSE CAST(filter_value AS NVARCHAR)
                                                                                                       END + CHAR(10)
                                                                                         ELSE CHAR(9) + CHAR(9) + CHAR(9) + 'AND '+ action_package_name + '.' + xe_action_name + ' ' + filter_operator + ' ' + 
                                                                                                       CASE 
                                                                                                              WHEN SQL_VARIANT_PROPERTY(filter_value, 'BaseType') IN ('nvarchar', 'varchar', 'char', 'nchar') 
                                                                                                                     THEN '''%'+CAST(filter_value AS NVARCHAR)+'%'''
                                                                                                              ELSE CAST(filter_value AS NVARCHAR)
                                                                                                       END + CHAR(10)
                                                                                  END
                                                                           FROM @TraceInfo AS ti
                                                                           WHERE ti.eventid = tab.eventid
                                                                             AND filter_operator IS NOT NULL
                                                                           ORDER BY xe_action_name
                                                                           FOR XML PATH('')
                                                                     ), 1, 7, CHAR(9) + CHAR(9) + CHAR(9)+'')
                                                              , '&gt;', '>')
                                                       , '&lt;', '<') -- Predicates
                                         + CHAR(9) + ')' 
                           END 
              + CHAR(10) + '),' + CHAR(10)
       END
FROM
(      
       SELECT eventid, te_name, event_package_name, xe_event_name, ROW_NUMBER() OVER (PARTITION BY event_package_name, xe_event_name ORDER BY eventid) AS RowID
       FROM
       (
              SELECT DISTINCT eventid, te_name, event_package_name, xe_event_name, ROW_NUMBER() OVER (PARTITION BY eventid ORDER BY event_package_name DESC) AS Row_ID
              FROM @TraceInfo
       ) AS tab2
       WHERE Row_ID = 1
) AS tab;

-- Add Event List to the output command
SET @sqlcmd = @sqlcmd + SUBSTRING(@event_list, 0, LEN(@event_list)-1)+CHAR(10)

-- Add target definitions to the output command based on trace configuration
SELECT @sqlcmd = @sqlcmd + 
CASE 
       WHEN path IS NULL THEN 'ADD TARGET package0.ring_buffer' + CHAR(10)
       ELSE 
'ADD TARGET package0.event_file' + CHAR(10) +
'(' + CHAR(10) +
'      SET filename = '''+ SUBSTRING(path, 0, LEN(path)-CHARINDEX('\', REVERSE(path))+1) + '\'+@SessionName+'.xel'',' + CHAR(10) +
'             max_file_size = ' + CAST(max_size AS NVARCHAR) + ',' + CHAR(10) +
'             max_rollover_files = ' + CAST(max_files AS NVARCHAR) + CHAR(10) +
')' + CHAR(10) 
END
FROM sys.traces   
WHERE id = @TraceID

-- Print the DDL for the Event Session
IF @PrintOutput = 1
BEGIN
       DECLARE @Position INT = 1, 
                     @Next INT = 0, 
                     @Delimeter NCHAR(1) = CHAR(10),
                     @WorkString VARCHAR(MAX) = @DropCmd + 'GO' + CHAR(10) + @sqlcmd;

       WHILE (1 = 1)
       BEGIN
              SELECT @Next = CHARINDEX(@Delimeter, @WorkString, @Position)

              IF (@Next = 0) 
                     BREAK

              IF (@Position <> @Next)
                     SELECT SUBSTRING(@WorkString, @Position, @Next - @Position) as SqlString

              SELECT @Position = @Next + 1
    END
END
DROP TABLE [#SQLskills_Trace_XE_Column_Map]
tools\dbatools\bin\sqlcmd\Resources\1033\license_SQLCMD.txt
MICROSOFT PRE-RELEASE SOFTWARE LICENSE TERMS
MICROSOFT SQL SERVER VNEXT COMMUNITY TECHNOLOGY PREVIEW 1 (CTP1)
These license terms are an agreement between Microsoft Corporation (or based on where you live, one of its affiliates) and you. Please read them. They apply to the pre-release software named above, which includes the media on which you received it, if any. The terms also apply to any Microsoft
•	updates,
•	supplements,
•	Internet-based services, and 
•	support services
for this software, unless other terms accompany those items. If so, those terms apply.
BY USING THE SOFTWARE, YOU ACCEPT THESE TERMS.  YOU MAY CHOOSE NOT TO ACCEPT THESE TERMS, IN WHICH CASE YOU MAY NOT USE THE SOFTWARE (IF YOU HAVE NOT ALREADY INSTALLED IT) OR WITHDRAW YOUR ACCEPTANCE ANY TIME BY UNINSTALLING THE SOFTWARE. 
We collect data about how you interact with this software. This includes data about the performance of the services, any problems you experience with them, and the features you use. This also includes data about your device and the network you use to connect to our services, including IP address, device identifiers, regional and language settings. It includes information about the operating systems and other software installed on your device, including product keys. By using this software, you consent to Microsoft’s collection of usage and performance data related to your use of the software.

If you comply with these license terms, you have the rights below.
1.	INSTALLATION AND USE RIGHTS. 
a.	Installation and Use.
•	You may install any number of copies of the software on your premises solely to internally evaluate it and provide feedback to Microsoft. 
•	You may not test the software in a live operating environment unless Microsoft permits you to do so under another agreement.
b.	Third Party Programs.  
The software may include third party components with separate legal notices or governed by other agreements, as may be described in the ThirdPartyNotices file accompanying the software.  Even if such components are governed by other agreements, the disclaimers and the limitations on and exclusions of damages below also apply.
2.	TERM. The term of this agreement is until 6/30/2017.
3.	PRE-RELEASE SOFTWARE. This software is a pre-release version. It may not work the way a final version of the software will. We may change it for the final, commercial version. We also may not release a commercial version.
4.	FEEDBACK. If you give feedback about the software to Microsoft, you give to Microsoft, without charge, the right to use, share and commercialize your feedback in any way and for any purpose. You also give to third parties, without charge, any patent rights needed for their products, technologies and services to use or interface with any specific parts of a Microsoft software or service that includes the feedback. You will not give feedback that is subject to a license that requires Microsoft to license its software or documentation to third parties because we include your feedback in them. These rights survive this agreement.
5.	SCOPE OF LICENSE. The software is licensed, not sold. This agreement only gives you some rights to use the software. Microsoft reserves all other rights. Unless applicable law gives you more rights despite this limitation, you may use the software only as expressly permitted in this agreement. In doing so, you must comply with any technical limitations in the software that only allow you to use it in certain ways. You may not
•	disclose the results of any benchmark tests of the software to any third party without Microsoft’s prior written approval;
•	work around any technical limitations in the software;
•	reverse engineer, decompile or disassemble the software, except and only to the extent that applicable law expressly permits, despite this limitation;
•	make more copies of the software than specified in this agreement or allowed by applicable law, despite this limitation;
•	publish the software for others to copy;
•	rent, lease or lend the software;
•	transfer the software or this agreement to any third party; or
•	use the software for commercial software hosting services.
6.	EXPORT RESTRICTIONS. The software is subject to United States export laws and regulations. You must comply with all domestic and international export laws and regulations that apply to the software. These laws include restrictions on destinations, end users and end use. For additional information, see www.microsoft.com/exporting. 
7.	SUPPORT SERVICES. Because this software is “as is,” we may not provide support services for it.
8.	ENTIRE AGREEMENT. This agreement, and the terms for supplements, updates, Internet-based services and support services that you use, are the entire agreement for the software and support services.
9.	APPLICABLE LAW.
a.	United States. If you acquired the software in the United States, Washington state law governs the interpretation of this agreement and applies to claims for breach of it, regardless of conflict of laws principles. The laws of the state where you live govern all other claims, including claims under state consumer protection laws, unfair competition laws, and in tort.
b.	Outside the United States. If you acquired the software in any other country, the laws of that country apply.
10.	LEGAL EFFECT. This agreement describes certain legal rights. You may have other rights under the laws of your country. You may also have rights with respect to the party from whom you acquired the software. This agreement does not change your rights under the laws of your country if the laws of your country do not permit it to do so.
11.	DISCLAIMER OF WARRANTY. THE SOFTWARE IS LICENSED “AS-IS.” YOU BEAR THE RISK OF USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES OR CONDITIONS. YOU MAY HAVE ADDITIONAL CONSUMER RIGHTS OR STATUTORY GUARANTEES UNDER YOUR LOCAL LAWS WHICH THIS AGREEMENT CANNOT CHANGE. TO THE EXTENT PERMITTED UNDER YOUR LOCAL LAWS, MICROSOFT EXCLUDES THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
FOR AUSTRALIA – YOU HAVE STATUTORY GUARANTEES UNDER THE AUSTRALIAN CONSUMER LAW AND NOTHING IN THESE TERMS IS INTENDED TO AFFECT THOSE RIGHTS.
12.	LIMITATION ON AND EXCLUSION OF REMEDIES AND DAMAGES. YOU CAN RECOVER FROM MICROSOFT AND ITS SUPPLIERS ONLY DIRECT DAMAGES UP TO U.S. $5.00. YOU CANNOT RECOVER ANY OTHER DAMAGES, INCLUDING CONSEQUENTIAL, LOST PROFITS, SPECIAL, INDIRECT OR INCIDENTAL DAMAGES.
This limitation applies to
•	anything related to the software, services, content (including code) on third party Internet sites, or third party programs; and
•	claims for breach of contract, breach of warranty, guarantee or condition, strict liability, negligence, or other tort to the extent permitted by applicable law.
It also applies even if Microsoft knew or should have known about the possibility of the damages. The above limitation or exclusion may not apply to you because your country may not allow the exclusion or limitation of incidental, consequential or other damages.
Please note: As this software is distributed in Quebec, Canada, these license terms are provided below in French.
Remarque : Ce logiciel étant distribué au Québec, Canada, certaines des clauses dans ce contrat sont fournies ci-dessous en français.
EXCLUSIONS DE GARANTIE. Le logiciel est concédé sous licence « en l’état ». Vous assumez tous les risques liés à son utilisation. Microsoft n’accorde aucune garantie ou condition expresse. Vous pouvez bénéficier de droits des consommateurs supplémentaires dans le cadre du droit local, que ce contrat ne peut modifier. Lorsque cela est autorisé par le droit local, Microsoft exclut les garanties implicites de qualité, d’adéquation à un usage particulier et d’absence de contrefaçon.

LIMITATION ET EXCLUSION DE RECOURS ET DE DOMMAGES. Vous pouvez obtenir de Microsoft et de ses fournisseurs une indemnisation en cas de dommages directs limitée uniquement à hauteur de 5,00 $ US. Vous ne pouvez prétendre à aucune indemnisation pour les autres dommages, y compris les dommages spéciaux, indirects ou accessoires et pertes de bénéfices.
Cette limitation concerne :
•	toute affaire liée au logiciel, aux services ou au contenu (y compris le code) figurant sur des sites Internet tiers ou dans des programmes tiers et
•	les réclamations au titre de violation de contrat ou de garantie, ou au titre de responsabilité stricte, de négligence ou d’une autre faute dans la limite autorisée par la loi en vigueur.
Elle s’applique également même si Microsoft connaissait l'éventualité d'un tel dommage. La limitation ou exclusion ci-dessus peut également ne pas vous être applicable, car votre pays n’autorise pas l’exclusion ou la limitation de responsabilité pour les dommages indirects, accessoires ou de quelque nature que ce soit.

EFFET JURIDIQUE. Le présent contrat décrit certains droits juridiques. Vous pourriez avoir d’autres droits prévus par les lois de votre pays. Le présent contrat ne modifie pas les droits que vous confèrent les lois de votre pays si celles-ci ne le permettent pas.


tools\dbatools\bin\sqlcmd\Resources\1033\SQLCMD.rll
 
tools\dbatools\bin\sqlcmd\SQLCMD.EXE
md5: 6F9AA3F25E87B31393521111E53481B8 | sha1: 8EBCE101C98ECDD15F3471F4268D0B835DBDC485 | sha256: EB9153A1B0EEF429572DD798179FC9016482A647049737EC0F5F02D1054DC49C | sha512: F3B21E59006672CC16B2F84EECA3231CA8818A0EC691131165C736F5F9FD6CFA4EE038A5DE0532C2A1C41C5B994DDF8D5EFC3B990A663B4CB5891855B1EC7DBF
tools\dbatools\bin\stig.sql
USE tempdb;
                GO

                IF EXISTS (SELECT 1 FROM sys.schemas WHERE name = 'STIG')
                BEGIN
	                DROP SCHEMA STIG
                END
                GO

                CREATE SCHEMA STIG
                GO

            /*
            Objects defined in this file:

            VIEW STIG.database_role_members
                Based on the system view sys.database_role_members, this presents the list of
                database role memberships using roles' and users' names rather than their id numbers.
                Although membership in database roles is hierarchical, this view lists only the direct memberships.

            FUNCTION STIG.database_roles_of(@database_principal sysname)
                Given the name of a database principal (user or role), this table-valued function returns
                a list of all the roles it belongs to, both directly and indirectly.

            FUNCTION STIG.members_of_db_role(@database_role sysname)
                Given the name of a database role, this table-valued function returns
                a list of all the roles and users that belong to it, both directly and indirectly.

            VIEW STIG.database_permissions
                Based on the system view sys.database_permissions, this provides additional, descriptive material.
                The list includes only those permissions explicitly granted (or denied) to a database user or role;
                it does not include permissions that are implicit or inherited from a higher-level role.
                Securable items that exist but have no explicit permissions assigned are included in the
                list, with the columns that describe the grantor and grantee left null.

            FUNCTION STIG.database_effective_permissions(@Grantee sysname)
                Given the name of a database principal (user or role), this table-valued function 
                returns information about permissions granted (or denied) to that user or database role,
                either directly or inherited from a higher-level role.


            VIEW STIG.server_role_members
                Based on the system view sys.server_role_members, this presents the list of
                server role memberships using roles' and users' names rather than their id numbers.
                Although membership in server roles is hierarchical, this view lists only the direct memberships.

            FUNCTION STIG.server_roles_of(@server_principal sysname)
                Given the name of a server principal (login or role), this table-valued function returns
                a list of all the roles it belongs to, both directly and indirectly.

            FUNCTION STIG.members_of_server_role(@server_role sysname)
                Given the name of a server role, this table-valued function returns
                a list of all the roles and logins that belong to it, both directly and indirectly.

            VIEW STIG.server_permissions
                Based on the system view sys.server_permissions, this provides additional, descriptive material.
                The list includes only those permissions explicitly granted (or denied) to a server login or role;
                it does not include permissions that are implicit or inherited from a higher-level role.
                Securable items that exist but have no explicit permissions assigned are included in the
                list, with columns describing the grantor and grantee left null.

            FUNCTION STIG.server_effective_permissions(@Grantee sysname)
                Given the name of a server principal (login or server role), this table-valued function 
                returns information about permissions granted (or denied) to that login or role,
                either directly or inherited from a higher-level role.
            */


            BEGIN TRY DROP VIEW STIG.database_role_members END TRY BEGIN CATCH END CATCH;
            GO

            CREATE VIEW STIG.database_role_members
            --  Based on the system view sys.database_role_members, this presents the list of
            --  database role memberships using roles' and users' names rather than their id numbers.
            --  Although membership in database roles is hierarchical, this view lists only the direct memberships.
            AS SELECT
                R.name  AS [Role],
                M.name  AS [Member]
            FROM
                <TARGETDB>.sys.database_role_members X
                INNER JOIN <TARGETDB>.sys.database_principals R ON R.principal_id = X.role_principal_id
                INNER JOIN <TARGETDB>.sys.database_principals M ON M.principal_id = X.member_principal_id
            ;
            GO


            BEGIN TRY DROP FUNCTION STIG.database_roles_of END TRY BEGIN CATCH END CATCH;
            GO

            CREATE FUNCTION STIG.database_roles_of(@database_principal sysname)
            --  Membership in database roles is hierarchical.
            --  Given the name of a database principal (user or role), this table-valued function returns
            --  a list of all the roles it belongs to, both directly and indirectly.
                RETURNS @T TABLE
                    (
                    [Member]            sysname,
                    [Role]              sysname, 
                    [via Member]        sysname, 
                    [Membership Chain]  nvarchar(max)
                    )
            AS BEGIN;
                WITH Membership AS
                (
                SELECT
                    [Member] AS [Member],
                    [Role], 
                    [Member] AS [via Member], 
                    CAST([Member] AS varchar(max)) + ' < ' + CAST([Role] AS varchar(max)) AS [Membership Chain]
                FROM
                    STIG.database_role_members
                WHERE 
                    [Member] = @database_principal
    
                UNION ALL
    
                SELECT
                    X.[Member],
                    R.[Role],
                    R.[Member] AS [via Member],
                    X.[Membership Chain] + ' < ' + CAST(R.[Role] AS varchar(max)) AS [Membership Chain]
                FROM 
                    Membership X
                    INNER JOIN STIG.database_role_members R ON X.[Role] = R.[Member]
                )
                INSERT INTO @T SELECT * FROM Membership;
            ExitFunction:
                RETURN;
            END;
            GO


            BEGIN TRY DROP FUNCTION STIG.members_of_db_role END TRY BEGIN CATCH END CATCH;
            GO

            CREATE FUNCTION STIG.members_of_db_role(@database_role sysname)
            --  Membership in database roles is hierarchical.
            --  Given the name of a database role, this table-valued function returns
            --  a list of all the roles and users that belong to it, both directly and indirectly.
                RETURNS @T TABLE
                    (
                    [Role]              sysname,
                    [Member]            sysname,
                    [via Role]          sysname, 
                    [Membership Chain]  nvarchar(max)
                    )
            AS BEGIN;
                WITH Membership AS
                (
                SELECT
                    [Role] AS [Role],
                    [Member], 
                    [Role] AS [via Role], 
                    CAST([Role] AS varchar(max)) + ' > ' + CAST([Member] AS varchar(max)) AS [Membership Chain]
                FROM
                    STIG.database_role_members
                WHERE 
                    [Role] = @database_role

                UNION ALL

                SELECT
                    X.[Role] AS [Role],
                    R.[Member],
                    R.[Role] AS [via Role],
                    X.[Membership Chain] + ' > ' + CAST(R.[Member] AS varchar(max)) AS [Membership Chain]
                FROM 
                    Membership X
                    INNER JOIN STIG.database_role_members R ON X.[Member] = R.[Role]
                )
                INSERT INTO @T SELECT * FROM Membership;
            ExitFunction:
                RETURN;
            END;
            GO




            BEGIN TRY DROP VIEW STIG.database_permissions END TRY BEGIN CATCH END CATCH;
            GO

            CREATE VIEW STIG.database_permissions
            --  Based on the system view sys.database_permissions, this provides additional, descriptive material.
            --  The list includes only those permissions explicitly granted (or denied) to a database user or role;
            --  it does not include permissions that are implicit or inherited from a higher-level role.
            --  Securable items that exist but have no explicit permissions assigned are included in the
            --  list, with the columns that describe the grantor and grantee left null.
            AS SELECT DISTINCT
                @@SERVERNAME        AS [Current Server],
                @@SERVICENAME       AS [Current Instance],
                '<TARGETDB>'        AS [Current DB],
                SYSTEM_USER         AS [Current Login],
                USER                AS [Current User],

                CASE
                    WHEN DP.class_desc = 'OBJECT_OR_COLUMN'             THEN CASE WHEN DP.minor_id > 0 THEN 'COLUMN' ELSE OB.type_desc END
                    WHEN DP.class_desc IS NOT NULL                      THEN DP.class_desc
                    WHEN DP.class_desc IS NULL AND DB.name IS NOT NULL  THEN 'DATABASE'
                    WHEN DP.class_desc IS NULL AND OB.name IS NOT NULL  THEN 'OBJECT_OR_COLUMN'
                    WHEN DP.class_desc IS NULL AND SC.name IS NOT NULL  THEN 'SCHEMA'
                    WHEN DP.class_desc IS NULL AND PR.name IS NOT NULL  THEN 'DATABASE_PRINCIPAL'
                    WHEN DP.class_desc IS NULL AND AY.name IS NOT NULL  THEN 'ASSEMBLY'
                    WHEN DP.class_desc IS NULL AND TP.name IS NOT NULL  THEN 'TYPE'
                    WHEN DP.class_desc IS NULL AND XS.name IS NOT NULL  THEN 'XML_SCHEMA_COLLECTION'
                    WHEN DP.class_desc IS NULL AND MT.name IS NOT NULL  THEN 'MESSAGE_TYPE'
                    WHEN DP.class_desc IS NULL AND VC.name IS NOT NULL  THEN 'SERVICE_CONTRACT'
                    WHEN DP.class_desc IS NULL AND SV.name IS NOT NULL  THEN 'SERVICE'
                    WHEN DP.class_desc IS NULL AND RS.name IS NOT NULL  THEN 'REMOTE_SERVICE_BINDING'
                    WHEN DP.class_desc IS NULL AND RT.name IS NOT NULL  THEN 'ROUTE'
                    WHEN DP.class_desc IS NULL AND FT.name IS NOT NULL  THEN 'FULLTEXT_CATALOG'
                    WHEN DP.class_desc IS NULL AND SK.name IS NOT NULL  THEN 'SYMMETRIC_KEY'
                    WHEN DP.class_desc IS NULL AND AK.name IS NOT NULL  THEN 'ASYMMETRIC_KEY'
                    WHEN DP.class_desc IS NULL AND CT.name IS NOT NULL  THEN 'CERTIFICATE'
                    ELSE NULL 
                END                 AS [Securable Type or Class],
                CASE
                    WHEN DP.class_desc = 'DATABASE'                     THEN PS.name
                    WHEN DP.class_desc = 'OBJECT_OR_COLUMN'             THEN schema_name(OB.schema_id)
                    WHEN DP.class_desc = 'SCHEMA'                       THEN P3.name
                    WHEN DP.class_desc = 'DATABASE_PRINCIPAL'           THEN coalesce(PR.default_schema_name, PE.name)
                    WHEN DP.class_desc = 'ASSEMBLY'                     THEN P4.name
                    WHEN DP.class_desc = 'TYPE'                         THEN schema_name(TP.schema_id)
                    WHEN DP.class_desc = 'XML_SCHEMA_COLLECTION'        THEN schema_name(XS.schema_id)
                    WHEN DP.class_desc = 'MESSAGE_TYPE'                 THEN P5.name 
                    WHEN DP.class_desc = 'SERVICE_CONTRACT'             THEN P6.name 
                    WHEN DP.class_desc = 'SERVICE'                      THEN P7.name 
                    WHEN DP.class_desc = 'REMOTE_SERVICE_BINDING'       THEN P8.name 
                    WHEN DP.class_desc = 'ROUTE'                        THEN P9.name
                    WHEN DP.class_desc = 'FULLTEXT_CATALOG'             THEN PA.name
                    WHEN DP.class_desc = 'SYMMETRIC_KEY'                THEN PB.name
                    WHEN DP.class_desc = 'ASYMMETRIC_KEY'               THEN PC.name
                    WHEN DP.class_desc = 'CERTIFICATE'                  THEN PD.name
                    WHEN DP.class_desc IS NULL AND DB.name IS NOT NULL  THEN PS.name
                    WHEN DP.class_desc IS NULL AND OB.name IS NOT NULL  THEN schema_name(OB.schema_id)
                    WHEN DP.class_desc IS NULL AND SC.name IS NOT NULL  THEN P3.name
                    WHEN DP.class_desc IS NULL AND PR.name IS NOT NULL  THEN PR.default_schema_name
                    WHEN DP.class_desc IS NULL AND AY.name IS NOT NULL  THEN P4.name
                    WHEN DP.class_desc IS NULL AND TP.name IS NOT NULL  THEN schema_name(TP.schema_id)
                    WHEN DP.class_desc IS NULL AND XS.name IS NOT NULL  THEN schema_name(XS.schema_id)
                    WHEN DP.class_desc IS NULL AND MT.name IS NOT NULL  THEN P5.name
                    WHEN DP.class_desc IS NULL AND VC.name IS NOT NULL  THEN P6.name
                    WHEN DP.class_desc IS NULL AND SV.name IS NOT NULL  THEN P7.name
                    WHEN DP.class_desc IS NULL AND RS.name IS NOT NULL  THEN P8.name
                    WHEN DP.class_desc IS NULL AND RT.name IS NOT NULL  THEN P9.name
                    WHEN DP.class_desc IS NULL AND FT.name IS NOT NULL  THEN PA.name
                    WHEN DP.class_desc IS NULL AND SK.name IS NOT NULL  THEN PB.name
                    WHEN DP.class_desc IS NULL AND AK.name IS NOT NULL  THEN PC.name
                    WHEN DP.class_desc IS NULL AND CT.name IS NOT NULL  THEN PD.name
                    ELSE NULL 
                END                 AS [Schema/Owner],
                CASE
                    WHEN DP.class_desc = 'DATABASE'                     THEN DB.name
                    WHEN DP.class_desc = 'OBJECT_OR_COLUMN'             THEN OB.name
                    WHEN DP.class_desc = 'SCHEMA'                       THEN SC.name
                    WHEN DP.class_desc = 'DATABASE_PRINCIPAL'           THEN PR.name
                    WHEN DP.class_desc = 'ASSEMBLY'                     THEN AY.name
                    WHEN DP.class_desc = 'TYPE'                         THEN TP.name
                    WHEN DP.class_desc = 'XML_SCHEMA_COLLECTION'        THEN XS.name
                    WHEN DP.class_desc = 'MESSAGE_TYPE'                 THEN cast(MT.name as sql_variant)
                    WHEN DP.class_desc = 'SERVICE_CONTRACT'             THEN cast(VC.name as sql_variant)
                    WHEN DP.class_desc = 'SERVICE'                      THEN cast(SV.name as sql_variant)
                    WHEN DP.class_desc = 'REMOTE_SERVICE_BINDING'       THEN RS.name
                    WHEN DP.class_desc = 'ROUTE'                        THEN RT.name
                    WHEN DP.class_desc = 'FULLTEXT_CATALOG'             THEN FT.name
                    WHEN DP.class_desc = 'SYMMETRIC_KEY'                THEN SK.name
                    WHEN DP.class_desc = 'ASYMMETRIC_KEY'               THEN AK.name
                    WHEN DP.class_desc = 'CERTIFICATE'                  THEN CT.name
                    WHEN DP.class_desc IS NULL AND DB.name IS NOT NULL  THEN DB.name 
                    WHEN DP.class_desc IS NULL AND OB.name IS NOT NULL  THEN OB.name
                    WHEN DP.class_desc IS NULL AND SC.name IS NOT NULL  THEN SC.name
                    WHEN DP.class_desc IS NULL AND PR.name IS NOT NULL  THEN PR.name
                    WHEN DP.class_desc IS NULL AND AY.name IS NOT NULL  THEN AY.name
                    WHEN DP.class_desc IS NULL AND TP.name IS NOT NULL  THEN TP.name
                    WHEN DP.class_desc IS NULL AND XS.name IS NOT NULL  THEN XS.name
                    WHEN DP.class_desc IS NULL AND MT.name IS NOT NULL  THEN cast(MT.name as sql_variant)
                    WHEN DP.class_desc IS NULL AND VC.name IS NOT NULL  THEN cast(VC.name as sql_variant)
                    WHEN DP.class_desc IS NULL AND SV.name IS NOT NULL  THEN cast(SV.name as sql_variant)
                    WHEN DP.class_desc IS NULL AND RS.name IS NOT NULL  THEN RS.name
                    WHEN DP.class_desc IS NULL AND RT.name IS NOT NULL  THEN RT.name
                    WHEN DP.class_desc IS NULL AND FT.name IS NOT NULL  THEN FT.name
                    WHEN DP.class_desc IS NULL AND SK.name IS NOT NULL  THEN SK.name
                    WHEN DP.class_desc IS NULL AND AK.name IS NOT NULL  THEN AK.name
                    WHEN DP.class_desc IS NULL AND CT.name IS NOT NULL  THEN CT.name
                    ELSE NULL 
                END                 AS [Securable],
                CM.name             AS [Column],
                P1.type_desc        AS [Grantee Type],
                P1.name             AS [Grantee],
                DP.permission_name  AS [Permission],    
                DP.state_desc       AS [State],
                P2.name             AS [Grantor],
                P2.type_desc        AS [Grantor Type],
                CASE
                    WHEN DP.class_desc = 'DATABASE'                     THEN 'sys.databases'
                    WHEN DP.class_desc = 'OBJECT_OR_COLUMN'             THEN 'sys.all_objects'
                    WHEN DP.class_desc = 'SCHEMA'                       THEN 'sys.schemas'
                    WHEN DP.class_desc = 'DATABASE_PRINCIPAL'           THEN 'sys.database_principals'
                    WHEN DP.class_desc = 'ASSEMBLY'                     THEN 'sys.assemblies'
                    WHEN DP.class_desc = 'TYPE'                         THEN 'sys.types'
                    WHEN DP.class_desc = 'XML_SCHEMA_COLLECTION'        THEN 'sys.xml_schema_collections'
                    WHEN DP.class_desc = 'MESSAGE_TYPE'                 THEN 'sys.service_message_types'
                    WHEN DP.class_desc = 'SERVICE_CONTRACT'             THEN 'sys.database_principals'
                    WHEN DP.class_desc = 'SERVICE'                      THEN 'sys.services'
                    WHEN DP.class_desc = 'REMOTE_SERVICE_BINDING'       THEN 'sys.remote_service_bindings'
                    WHEN DP.class_desc = 'ROUTE'                        THEN 'sys.routes'
                    WHEN DP.class_desc = 'FULLTEXT_CATALOG'             THEN 'sys.database_principals'
                    WHEN DP.class_desc = 'SYMMETRIC_KEY'                THEN 'sys.symmetric_keys'
                    WHEN DP.class_desc = 'ASYMMETRIC_KEY'               THEN 'sys.asymmetric_keys'
                    WHEN DP.class_desc = 'CERTIFICATE'                  THEN 'sys.certificates'
                    WHEN DP.class_desc IS NULL AND DB.name IS NOT NULL  THEN 'sys.databases' 
                    WHEN DP.class_desc IS NULL AND OB.name IS NOT NULL  THEN 'sys.all_objects'
                    WHEN DP.class_desc IS NULL AND SC.name IS NOT NULL  THEN 'sys.schemas'
                    WHEN DP.class_desc IS NULL AND PR.name IS NOT NULL  THEN 'sys.database_principals'
                    WHEN DP.class_desc IS NULL AND AY.name IS NOT NULL  THEN 'sys.assemblies'
                    WHEN DP.class_desc IS NULL AND TP.name IS NOT NULL  THEN 'sys.types'
                    WHEN DP.class_desc IS NULL AND XS.name IS NOT NULL  THEN 'sys.xml_schema_collections'
                    WHEN DP.class_desc IS NULL AND MT.name IS NOT NULL  THEN 'sys.service_message_types'
                    WHEN DP.class_desc IS NULL AND VC.name IS NOT NULL  THEN 'sys.database_principals'
                    WHEN DP.class_desc IS NULL AND SV.name IS NOT NULL  THEN 'sys.services'
                    WHEN DP.class_desc IS NULL AND RS.name IS NOT NULL  THEN 'sys.remote_service_bindings'
                    WHEN DP.class_desc IS NULL AND RT.name IS NOT NULL  THEN 'sys.routes'
                    WHEN DP.class_desc IS NULL AND FT.name IS NOT NULL  THEN 'sys.database_principals'
                    WHEN DP.class_desc IS NULL AND SK.name IS NOT NULL  THEN 'sys.symmetric_keys'
                    WHEN DP.class_desc IS NULL AND AK.name IS NOT NULL  THEN 'sys.asymmetric_keys'
                    WHEN DP.class_desc IS NULL AND CT.name IS NOT NULL  THEN 'sys.certificates'
                    ELSE '<TARGETDB>.sys.database_permissions'
                END                 AS [Source View]
            FROM
                <TARGETDB>.sys.database_permissions DP
                LEFT OUTER JOIN <TARGETDB>.sys.database_principals P1
                    ON  P1.principal_id = DP.grantee_principal_id
                LEFT OUTER JOIN <TARGETDB>.sys.database_principals P2
                    ON  P2.principal_id = DP.grantor_principal_id

                FULL OUTER JOIN <TARGETDB>.sys.databases DB
                    ON  DB.database_id = db_id(db_name(DP.major_id))
                    AND DP.class_desc = 'DATABASE'
                LEFT OUTER JOIN <TARGETDB>.sys.server_principals PS
                    ON  PS.sid = DB.owner_sid

                FULL OUTER JOIN <TARGETDB>.sys.all_objects OB
                    ON  DP.major_id   = OB.[object_id]
                    AND DP.class_desc = 'OBJECT_OR_COLUMN'
                LEFT OUTER JOIN <TARGETDB>.sys.all_columns CM
                    ON  CM.[object_id] = DP.major_id
                    AND CM.[column_id] = DP.minor_id

                FULL OUTER JOIN <TARGETDB>.sys.schemas SC
                    ON  DP.major_id   = SC.[schema_id]
                    AND DP.class_desc = 'SCHEMA'
                LEFT OUTER JOIN <TARGETDB>.sys.database_principals P3
                    ON  P3.principal_id = SC.principal_id

                FULL OUTER JOIN <TARGETDB>.sys.database_principals PR
                    ON  DP.major_id   = PR.principal_id
                    AND DP.class_desc = 'DATABASE_PRINCIPAL'
                LEFT OUTER JOIN <TARGETDB>.sys.database_principals PE
                    ON  PE.principal_id = PR.owning_principal_id

                FULL OUTER JOIN <TARGETDB>.sys.assemblies AY
                    ON  DP.major_id   = AY.assembly_id
                    AND DP.class_desc = 'ASSEMBLY'
                LEFT OUTER JOIN <TARGETDB>.sys.database_principals P4
                    ON  P4.principal_id = AY.principal_id

                FULL OUTER JOIN <TARGETDB>.sys.types TP
                    ON  DP.major_id   = TP.user_type_id
                    AND DP.class_desc = 'TYPE'

                FULL OUTER JOIN <TARGETDB>.sys.xml_schema_collections XS
                    ON  DP.major_id   = XS.xml_collection_id
                    AND DP.class_desc = 'XML_SCHEMA_COLLECTION'

                FULL OUTER JOIN <TARGETDB>.sys.service_message_types MT
                    ON  DP.major_id   = MT.message_type_id
                    AND DP.class_desc = 'MESSAGE_TYPE'
                LEFT OUTER JOIN <TARGETDB>.sys.database_principals P5
                    ON  P5.principal_id = MT.principal_id

                FULL OUTER JOIN <TARGETDB>.sys.service_contracts VC
                    ON  DP.major_id   = VC.service_contract_id
                    AND DP.class_desc = 'SERVICE_CONTRACT'
                LEFT OUTER JOIN <TARGETDB>.sys.database_principals P6
                    ON  P6.principal_id = VC.principal_id

                FULL OUTER JOIN <TARGETDB>.sys.services SV
                    ON  DP.major_id   = SV.service_id
                    AND DP.class_desc = 'SERVICE'
                LEFT OUTER JOIN <TARGETDB>.sys.database_principals P7
                    ON  P7.principal_id = SV.principal_id

                FULL OUTER JOIN <TARGETDB>.sys.remote_service_bindings RS
                    ON  DP.major_id   = RS.remote_service_binding_id
                    AND DP.class_desc = 'REMOTE_SERVICE_BINDING'
                LEFT OUTER JOIN <TARGETDB>.sys.database_principals P8
                    ON  P8.principal_id = RS.principal_id

                FULL OUTER JOIN <TARGETDB>.sys.routes RT
                    ON  DP.major_id   = RT.route_id
                    AND DP.class_desc = 'ROUTE'
                LEFT OUTER JOIN <TARGETDB>.sys.database_principals P9
                    ON  P9.principal_id = RT.principal_id

                FULL OUTER JOIN <TARGETDB>.sys.fulltext_catalogs FT
                    ON  DP.major_id   = FT.fulltext_catalog_id
                    AND DP.class_desc = 'FULLTEXT_CATALOG'
                LEFT OUTER JOIN <TARGETDB>.sys.database_principals PA
                    ON  PA.principal_id = FT.principal_id

                FULL OUTER JOIN <TARGETDB>.sys.symmetric_keys SK
                    ON  DP.major_id   = SK.symmetric_key_id
                    AND DP.class_desc = 'SYMMETRIC_KEY'
                LEFT OUTER JOIN <TARGETDB>.sys.database_principals PB
                    ON  PB.principal_id = SK.principal_id

                FULL OUTER JOIN <TARGETDB>.sys.asymmetric_keys AK
                    ON  DP.major_id   = AK.asymmetric_key_id
                    AND DP.class_desc = 'ASYMMETRIC_KEY'
                LEFT OUTER JOIN <TARGETDB>.sys.database_principals PC
                    ON  PC.principal_id = AK.principal_id

                FULL OUTER JOIN <TARGETDB>.sys.certificates CT
                    ON  DP.major_id   = CT.certificate_id
                    AND DP.class_desc = 'CERTIFICATE'
                LEFT OUTER JOIN <TARGETDB>.sys.database_principals PD
                    ON  PD.principal_id = CT.principal_id    
            ;
            GO



            BEGIN TRY DROP FUNCTION STIG.database_effective_permissions END TRY BEGIN CATCH END CATCH;
            GO

            CREATE FUNCTION STIG.database_effective_permissions(@Grantee sysname)
            --  Given the name of a database principal (user or role), this table-valued function 
            --  returns information about permissions granted (or denied) to that user or database role,
            --  either directly or inherited from a higher-level role.
                RETURNS @T TABLE
                    (
                    [Current Server]            sysname         null,
                    [Current Instance]          sysname         null,
                    [Current DB]                sysname         null,
                    [Current Login]             sysname         null,
                    [Current User]              sysname         null,
                    [Securable Type or Class]   nvarchar(60)    null,
                    [Schema/Owner]              sysname         null,
                    [Securable]                 sql_variant     null,
                    [Column]                    sysname         null,
                    [Effective Grantee]         sysname         null,
                    [Membership Chain]          nvarchar(max)   null,
                    [Direct Grantee]            sysname         null,
                    [Direct Grantee Type]       nvarchar(60)    null,
                    [Permission]                sysname         null,
                    [State]                     nvarchar(60)    null,
                    [Grantor]                   sysname         null,
                    [Grantor Type]              nvarchar(60)    null,
                    [source view]               sysname         null
                    )
            AS BEGIN;
                WITH Targets AS
                (
                SELECT [Role] AS [Principal], [Membership Chain], len([Membership Chain]) AS [Membership Chain Length] FROM STIG.database_roles_of(@Grantee)
                UNION ALL
                SELECT @Grantee AS [Principal], @Grantee AS [Membership Chain], len(@Grantee) AS [Membership Chain Length]    
                )
                INSERT INTO @T
                    SELECT TOP 100000000
                        P.[Current Server],
                        P.[Current Instance],
                        P.[Current DB],
                        P.[Current Login],
                        P.[Current User],
                        P.[Securable Type or Class],
                        P.[Schema/Owner],
                        P.[Securable],
                        P.[Column],
                        @Grantee                AS [Effective Grantee],
                        T.[Membership Chain]    AS [Membership Chain],
                        P.[Grantee]             AS [Direct Grantee],
                        P.[Grantee Type]        AS [Direct Grantee Type],
                        P.[Permission],
                        P.[State],
                        P.[Grantor],
                        P.[Grantor Type],
                        P.[source view]
                    FROM
                        STIG.database_permissions P
                        INNER JOIN Targets T ON T.[Principal] = P.[Grantee]
                    ORDER BY
                        P.[Securable Type or Class],
                        P.[Schema/Owner],
                        P.[Securable],
                        P.[Column],
                        T.[Membership Chain Length]
                    ;
                    UPDATE T
                    SET [State] = [State] + ' (schema denied)'
                    FROM @T T
                    WHERE
                        T.[State] <> 'DENY'
                    AND    T.[Securable Type or Class] <> 'SCHEMA'
                    AND 0 <
                        (
                        SELECT count(*) FROM @T X
                        WHERE
                            X.[Securable Type or Class] = 'SCHEMA'
                        AND X.[Securable]               = T.[Schema/Owner]
                        AND X.[Permission]              = T.[Permission]
                        AND X.[State]                   = 'DENY'
                        )
                    ;
                    UPDATE T
                    SET [State] = [State] + ' (denied)'
                    FROM @T T
                    WHERE
                        T.[State] <> 'DENY'
                    AND 0 <
                        (
                        SELECT count(*) FROM @T X
                        WHERE
                            X.[Securable Type or Class] = T.[Securable Type or Class]
                        AND    X.[Schema/Owner]         = T.[Schema/Owner]
                        AND X.[Securable]               = T.[Securable]
                        AND    (X.[Column] = T.[Column] OR (X.[Column] IS NULL) AND (T.[Column] IS NULL))
                        AND X.[Permission]              = T.[Permission]
                        AND X.[State]                   = 'DENY'
                        )
                    ;
                    UPDATE T
                    SET [State] = [State] + ' (column(s) denied)'
                    FROM @T T
                    WHERE T.[Securable Type or Class] <> 'COLUMN'
                    AND 0 <
                        (
                        SELECT count(*) FROM @T X
                        WHERE
                            X.[Securable Type or Class] = 'COLUMN'
                        AND X.[Schema/Owner]            = T.[Schema/Owner]
                        AND X.[Securable]               = T.[Securable]
                        AND X.[Permission]              = T.[Permission]
                        AND X.[State]                   = 'DENY'
                        )
                    ;
            ExitFunction:
                RETURN;
            END;
            GO



            BEGIN TRY DROP VIEW STIG.server_role_members END TRY BEGIN CATCH END CATCH;
            GO

            CREATE VIEW STIG.server_role_members
            --  Based on the system view sys.server_role_members, this presents the list of
            --  server role memberships using roles' and logins' names rather than their id numbers.
            --  Although membership in server roles is hierarchical, this view lists only the direct memberships.
            AS SELECT
                R.name    AS [Role],
                M.name    AS [Member]
            FROM
                <TARGETDB>.sys.server_role_members X
                INNER JOIN <TARGETDB>.sys.server_principals R ON R.principal_id = X.role_principal_id
                INNER JOIN <TARGETDB>.sys.server_principals M ON M.principal_id = X.member_principal_id
            ;
            GO


            BEGIN TRY DROP FUNCTION STIG.server_roles_of END TRY BEGIN CATCH END CATCH;
            GO

            CREATE FUNCTION STIG.server_roles_of(@server_principal sysname)
            --  Membership in server roles is hierarchical.
            --  Given the name of a server principal (login or role), this table-valued function returns
            --  a list of all the roles it belongs to, both directly and indirectly.
                RETURNS @T TABLE
                    (
                    [Member]            sysname,
                    [Role]              sysname, 
                    [via Member]        sysname, 
                    [Membership Chain]  nvarchar(max)
                    )
            AS BEGIN;
                WITH Membership AS
                (
                SELECT
                    [Member] AS [Member],
                    [Role], 
                    [Member] AS [via Member], 
                    CAST([Member] AS varchar(max)) + ' < ' + CAST([Role] AS varchar(max)) AS [Membership Chain]
                FROM
                    STIG.server_role_members
                WHERE 
                    [Member] = @server_principal
    
                UNION ALL
    
                SELECT
                    X.[Member],
                    R.[Role],
                    R.[Member] AS [via Member],
                    X.[Membership Chain] + ' < ' + CAST(R.[Role] AS varchar(max)) AS [Membership Chain]
                FROM 
                    Membership X
                    INNER JOIN STIG.server_role_members R ON X.[Role] = R.[Member]
                )
                INSERT INTO @T SELECT * FROM Membership;
            ExitFunction:
                RETURN;
            END;
            GO


            BEGIN TRY DROP FUNCTION STIG.members_of_server_role END TRY BEGIN CATCH END CATCH;
            GO

            CREATE FUNCTION STIG.members_of_server_role(@server_role sysname)
            --  Membership in server roles is hierarchical.
            --  Given the name of a server role, this table-valued function returns
            --  a list of all the roles and logins that belong to it, both directly and indirectly.
                RETURNS @T TABLE
                    (
                    [Role]              sysname,
                    [Member]            sysname,
                    [via Role]          sysname, 
                    [Membership Chain]  nvarchar(max)
                    )
            AS BEGIN;
                WITH Membership AS
                (
                SELECT
                    [Role] AS [Role],
                    [Member], 
                    [Role] AS [via Role], 
                    CAST([Role] AS varchar(max)) + ' > ' + CAST([Member] AS varchar(max)) AS [Membership Chain]
                FROM
                    STIG.server_role_members
                WHERE 
                    [Role] = @server_role

                UNION ALL

                SELECT
                    X.[Role] AS [Role],
                    R.[Member],
                    R.[Role] AS [via Role],
                    X.[Membership Chain] + ' > ' + CAST(R.[Member] AS varchar(max)) AS [Membership Chain]
                FROM 
                    Membership X
                    INNER JOIN STIG.server_role_members R ON X.[Member] = R.[Role]
                )
                INSERT INTO @T SELECT * FROM Membership;
            ExitFunction:
                RETURN;
            END;
            GO



            BEGIN TRY DROP VIEW STIG.server_permissions END TRY BEGIN CATCH END CATCH;
            GO

            CREATE VIEW STIG.server_permissions
            --  Based on the system view sys.server_permissions, this provides additional, descriptive material.
            --  The list includes only those permissions explicitly granted (or denied) to a server login or role;
            --  it does not include permissions that are implicit or inherited from a higher-level role.
            --  Securable items that exist but have no explicit permissions assigned are included in the
            --  list, with columns describing the grantor and grantee left null.
            AS SELECT DISTINCT
                @@SERVERNAME          AS [Current Server],
                @@SERVICENAME         AS [Current Instance],
                '<TARGETDB>'             AS [Current DB],
                SYSTEM_USER           AS [Current Login],
                USER                  AS [Current User],
                CASE
                    WHEN SP.class_desc IS NOT NULL THEN 
                        CASE
                            WHEN SP.class_desc = 'SERVER' AND S.is_linked = 0 THEN 'SERVER'
                            WHEN SP.class_desc = 'SERVER' AND S.is_linked = 1 THEN 'SERVER (linked)'
                            ELSE SP.class_desc
                        END
                    WHEN E.name IS NOT NULL THEN 'ENDPOINT'
                    WHEN S.name IS NOT NULL AND S.is_linked = 0 THEN 'SERVER'
                    WHEN S.name IS NOT NULL AND S.is_linked = 1 THEN 'SERVER (linked)'
                    WHEN P.name IS NOT NULL THEN 'SERVER_PRINCIPAL'
                    ELSE '???' 
                END                    AS [Securable Class],
                CASE
                    WHEN E.name IS NOT NULL THEN E.name
                    WHEN S.name IS NOT NULL THEN S.name 
                    WHEN P.name IS NOT NULL THEN P.name
                    ELSE '???' 
                END                    AS [Securable],
                P1.name                AS [Grantee],
                P1.type_desc           AS [Grantee Type],
                sp.permission_name     AS [Permission],
                sp.state_desc          AS [State],
                P2.name                AS [Grantor],
                P2.type_desc           AS [Grantor Type],
                CASE
                    WHEN SP.class_desc = 'SERVER'                       THEN 'sys.servers'
                    WHEN SP.class_desc = 'ENDPOINT'                     THEN 'sys.endpoints'
                    WHEN SP.class_desc = 'SERVER_PRINCIPAL'             THEN 'sys.server_principals'
                    WHEN SP.class_desc IS NULL AND S.name IS NOT NULL   THEN 'sys.servers'
                    WHEN SP.class_desc IS NULL AND E.name IS NOT NULL   THEN 'sys.endpoints' 
                    WHEN SP.class_desc IS NULL AND P.name IS NOT NULL   THEN 'sys.server_principals'
                    ELSE 'sys.server_permissions'
                END                 AS [Source View]
            FROM
                <TARGETDB>.sys.server_permissions SP
                INNER JOIN <TARGETDB>.sys.server_principals P1
                    ON P1.principal_id = SP.grantee_principal_id
                INNER JOIN <TARGETDB>.sys.server_principals P2
                    ON P2.principal_id = SP.grantor_principal_id

                FULL OUTER JOIN sys.servers S
                    ON  SP.class_desc = 'SERVER'
                    AND S.server_id = SP.major_id

                FULL OUTER JOIN <TARGETDB>.sys.endpoints E
                    ON  SP.class_desc = 'ENDPOINT'
                    AND E.endpoint_id = SP.major_id

                FULL OUTER JOIN <TARGETDB>.sys.server_principals P
                    ON  SP.class_desc = 'SERVER_PRINCIPAL'        
                    AND P.principal_id = SP.major_id
            ;
            GO


            BEGIN TRY DROP FUNCTION STIG.server_effective_permissions END TRY BEGIN CATCH END CATCH;
            GO

            CREATE FUNCTION STIG.server_effective_permissions(@Grantee sysname)
            --  Given the name of a server principal (login or server role), this table-valued function 
            --  returns information about permissions granted (or denied) to that login or role,
            --  either directly or inherited from a higher-level role.
                RETURNS @T TABLE
                    (
                    [Current Server]            sysname         null,
                    [Current Instance]          sysname         null,
                    [Current DB]                sysname         null,
                    [Current Login]             sysname         null,
                    [Current User]              sysname         null,
                    [Securable Class]           nvarchar(60)    null,
                    [Securable]                 sql_variant     null,
                    [Effective Grantee]         sysname         null,
                    [Membership Chain]          nvarchar(max)   null,
                    [Direct Grantee]            sysname         null,
                    [Direct Grantee Type]       nvarchar(60)    null,
                    [Permission]                sysname         null,
                    [State]                     nvarchar(60)    null,
                    [Grantor]                   sysname         null,
                    [Grantor Type]              nvarchar(60)    null,
                    [source view]               sysname         null
                    )
            AS BEGIN;
                WITH Targets AS
                (
                SELECT [Role] AS [Principal], [Membership Chain], len([Membership Chain]) AS [Membership Chain Length] FROM STIG.server_roles_of(@Grantee)
                UNION ALL
                SELECT @Grantee AS [Principal], @Grantee AS [Membership Chain], len(@Grantee) AS [Membership Chain Length]    
                )
                INSERT INTO @T
                    SELECT TOP 100000000
                        P.[Current Server],
                        P.[Current Instance],
                        P.[Current DB],
                        P.[Current Login],
                        P.[Current User],
                        P.[Securable Class],
                        P.[Securable],
                        @Grantee                AS [Effective Grantee],
                        T.[Membership Chain]    AS [Membership Chain],
                        P.[Grantee]             AS [Direct Grantee],
                        P.[Grantee Type]        AS [Direct Grantee Type],
                        P.[Permission],
                        P.[State],
                        P.[Grantor],
                        P.[Grantor Type],
                        P.[source view]
                    FROM
                        STIG.server_permissions P
                        INNER JOIN Targets T ON T.[Principal] = P.[Grantee]
                    ORDER BY
                        P.[Securable Class],
                        P.[Securable],
                        T.[Membership Chain Length]
                    ;
                    UPDATE T
                    SET [State] = [State] + ' (denied)'
                    FROM @T T
                    WHERE
                        T.[State] <> 'DENY'
                    AND 0 <
                        (
                        SELECT count(*) FROM @T X
                        WHERE
                            X.[Securable Class] = T.[Securable Class]
                        AND X.[Securable]       = T.[Securable]
                        AND X.[Permission]      = T.[Permission]
                        AND X.[State]           = 'DENY'
                        )
                    ;
            ExitFunction:
                RETURN;
            END;
            GO
tools\dbatools\bin\third-party-licenses\sql-server-data-tools-license-terms-vs2017.md
---
title: "SQL Server Data Tools and Visual Studio 2017 - License Terms | Microsoft Docs"
ms.custom: ""
ms.date: "08/04/2017"
ms.prod: "sql-non-specified"
ms.prod_service: "sql-non-specified"
ms.service: ""
ms.component: "ssdt"
ms.reviewer: ""
ms.suite: "sql"
ms.technology: 
  - "tools-ssdt"
ms.tgt_pltfrm: ""
ms.topic: "article"
ms.assetid: 
caps.latest.revision: 5
author: "stevestein"
ms.author: "sstein"
manager: "craigg"
ms.workload: "On Demand"
---
# SQL Server Data Tools - License Terms
[!INCLUDE[appliesto-ss-asdb-asdw-pdw-md](../includes/appliesto-ss-asdb-asdw-pdw-md.md)]
## MICROSOFT SOFTWARE LICENSE TERMS  
  
**MICROSOFT SQL SERVER DATA TOOLS**  
**MICROSOFT SQL SERVER DATA TOOLS FOR VISUAL STUDIO 2017**
---  
These license terms are an agreement between Microsoft Corporation (or based on where you live, one of its affiliates) and you. Please read them. They apply to the software named above, which includes the media on which you received it, if any. The terms also apply to any Microsoft  
* updates,  
* supplements,  
* Internet-based services, and  
* support services  
  
for this software, unless other terms accompany those items. If so, those terms apply.  
  
**BY USING THE SOFTWARE, YOU ACCEPT THESE TERMS. YOU MAY CHOOSE NOT TO ACCEPT THESE TERMS, IN WHICH CASE YOU MAY NOT USE THE SOFTWARE (IF YOU HAVE NOT ALREADY INSTALLED IT) OR WITHDRAW YOUR ACCEPTANCE ANY TIME BY UNINSTALLING THE SOFTWARE.**
  
---  
**If you comply with these license terms, you have the rights below.**  
**1.    INSTALLATION AND USE RIGHTS.**  
  
&nbsp;&nbsp;**a.    Installation and Use.**  
  
   * You may install and use any number of copies of the software on your devices to design, develop and test your programs.  
   * If the software includes Microsoft Visual Studio 2017, you may only use Microsoft Visual Studio 2017 components with Microsoft SQL Server Data Tools.
     
&nbsp;&nbsp;**b.    Other Microsoft Programs.**  

   * The software includes other Microsoft SQL Server technologies, Microsoft Visual Studio  2017, .NET Framework, Visual C++ Redistributable for Visual Studio 2017, and Microsoft Report Viewer 2016 Runtime in conjunction with the software licensed here. These components are governed by separate agreements and their own product support policies, as described in the license terms found in the installation directory for that component or in the “Licenses” folder accompanying the software.  
  
**2.    ADDITIONAL LICENSING REQUIREMENTS AND/OR USE RIGHTS.**  
  
&nbsp;&nbsp;**a.    Distributable Code.**  
  
&nbsp;&nbsp;&nbsp;&nbsp;**i.    Right to Use and Distribute. If you comply with the terms below:**  
* You may copy and distribute the object code form of the Microsoft SQL Server Data-Tier Application Framework (“**Distributable Code**”) in programs you develop; and   
* You may permit distributors of your programs to copy and distribute the Distributable Code as part of those programs.  
  
&nbsp;&nbsp;&nbsp;&nbsp;**ii.   Distribution Requirements. For any Distributable Code you distribute, you must**  
* add significant primary functionality to it in your programs; 
* for any Distributable Code having a filename extension of .lib, distribute only the results of running such Distributable Code through a linker with your program; 
* distribute Distributable Code included in a setup program only as part of that setup program without modification; 
* require distributors and external end users to agree to the Microsoft license terms included as part of our software setup program; 
* display your valid copyright notice on your programs; and 
* indemnify, defend, and hold harmless Microsoft from any claims, including attorneys’ fees, related to the distribution or use of your programs
 
  
&nbsp;&nbsp;&nbsp;&nbsp;**iii.  Distribution Restrictions. You may not**  
* alter any copyright, trademark or patent notice in the Distributable Code; 
* use Microsoft’s trademarks in your programs’ names or in a way that suggests your programs come from or are endorsed by Microsoft; 
* distribute Distributable Code to run on a platform other than the Windows platform; 
* include Distributable Code in malicious, deceptive or unlawful programs; or 
* modify or distribute the source code of any Distributable Code so that any part of it becomes subject to an Excluded License. An Excluded License is one that requires, as a condition of use, modification or distribution, that 
* the code be disclosed or distributed in source code form; or 
* others have the right to modify it. 
  
  
**3.    INTERNET-BASED SERVICES.** Microsoft provides Internet-based services with the software. It may change or cancel them at any time.  You may not use these devices in any way that could harm them or impair anyone else’s use of them.  You may not use the services to try to gain unauthorized access to any service, data, account or network by any means.  
  
&nbsp;&nbsp;**a.    SQL Server Reporting Services Map Report Item.** The software may include features that retrieve content such as maps, images and other data through the Bing Maps (or successor branded) application programming interface (the “Bing Maps APIs”). The purpose of these features is to create reports displaying data on top of maps, aerial and hybrid imagery. If these features are included, you may use them to create and view dynamic or static documents. This may be done only in conjunction with and through methods and means of access integrated in the software. You may not otherwise copy, store, archive, or create a database of the content available through the Bing Maps APIs. You may not use the following for any purpose even if they are available through the Bing Maps APIs:  
* Bing Maps APIs to provide sensor based guidance/routing, or  
* any Road Traffic Data or Bird’s Eye Imagery (or associated metadata).  
Your use of Bing Maps is also governed by the Bing Maps End User Terms of Use available at http://go.microsoft.com/?linkid=9710837 and the Bing Maps Privacy Statement available at http://go.microsoft.com/fwlink/?LinkID=248686.  
  
&nbsp;&nbsp;**b.**  This software is designed to allow users of SQL Server Integration Services (SSIS) to (a) move data between on-premises data-stores and Microsoft online services and (b) trigger certain actions in Microsoft online services. In order to do this, the software uses Internet Protocols to (i) send data, including your own data as designated by you and data about the software’s configuration, to these services, and (ii) request data, including your own data as designated by you and data about the nature and configuration of your Microsoft online services, from these services. Once you configure the software to communicate with these services, you may not receive separate notices when the software connects to these services.  
  
**4.    FEEDBACK.** If you give feedback about the software to Microsoft, you give to Microsoft, without charge, the right to use, share and commercialize your feedback in any way and for any purpose. You also give to third parties, without charge, any patent rights needed for their products, technologies and services to use or interface with any specific parts of a Microsoft software or service that includes the feedback. You will not give feedback that is subject to a license that requires Microsoft to license its software or documentation to third parties because we include your feedback in them. These rights survive this agreement.  
  
**5.  THIRD PARTY NOTICES.** The software may include third party components with separate legal notices or governed by other agreements, as may be described in the ThirdPartyNotices file accompanying the software.  Even if such components are governed by other agreements, the disclaimers and the limitations on and exclusions of damages below also apply.  
  
**6.    .NET FRAMEWORK SOFTWARE.** The software contains Microsoft .NET Framework software. This software is part of Windows. The license terms for Windows apply to your use of the .NET Framework software.  
  
**7.    SCOPE OF LICENSE.** The software is licensed, not sold. This agreement only gives you some rights to use the software. Microsoft reserves all other rights. Unless applicable law gives you more rights despite this limitation, you may use the software only as expressly permitted in this agreement. In doing so, you must comply with any technical limitations in the software that only allow you to use it in certain ways. You may not  
  
* disclose the results of any benchmark tests of the software, other than the Microsoft .NET Framework (see separate term above), to any third party without Microsoft’s prior written approval;  
* work around any technical limitations in the software;  
* reverse engineer, decompile or disassemble the software, except and only to the extent that applicable law expressly permits, despite this limitation;  
* make more copies of the software than specified in this agreement or allowed by applicable law, despite this limitation;  
* publish the software for others to copy;  
* rent, lease or lend the software;  
* transfer the software or this agreement to any third party; or  
* use the software for commercial software hosting services.  
  
**8.    EXPORT RESTRICTIONS.** The software is subject to United States export laws and regulations. You must comply with all domestic and international export laws and regulations that apply to the software. These laws include restrictions on destinations, end users and end use. For additional information, see www.microsoft.com/exporting.  
  
**9.    UPDATES.** The software may install automatic updates, which cannot be turned off. By using the software, you agree to receive automatic updates without any additional notice, and permit Microsoft to download and install them for you. You agree to obtain these updates only from Microsoft or Microsoft authorized sources. If you do not want software updates, disconnect your device from the internet or uninstall the software.  
  
**10.   SUPPORT SERVICES.** Because this software is “as is,” we may not provide support services for it.  
  
**11.   ENTIRE AGREEMENT.** This agreement, and the terms for supplements, updates, Internet-based services and support services that you use, are the entire agreement for the software and support services.  
  
**12.   APPLICABLE LAW.**  
  
&nbsp;&nbsp;**a.    United States.** If you acquired the software in the United States, Washington state law governs the interpretation of this agreement and applies to claims for breach of it, regardless of conflict of laws principles. The laws of the state where you live govern all other claims, including claims under state consumer protection laws, unfair competition laws, and in tort.  
  
&nbsp;&nbsp;**b.    Outside the United States.** If you acquired the software in any other country, the laws of that country apply.  
  
**13.   LEGAL EFFECT.** This agreement describes certain legal rights. You may have other rights under the laws of your country. You may also have rights with respect to the party from whom you acquired the software. This agreement does not change your rights under the laws of your country if the laws of your country do not permit it to do so.  
  
**14.   DISCLAIMER OF WARRANTY. The software is licensed “as-is.” You bear the risk of using it. Microsoft gives no express warranties, guarantees or conditions. You may have additional consumer rights or statutory guarantees under your local laws which this agreement cannot change. To the extent permitted under your local laws, Microsoft excludes the implied warranties of merchantability, fitness for a particular purpose and non-infringement.**  
  
&nbsp;&nbsp;FOR AUSTRALIA – You have statutory guarantees under the Australian Consumer Law and nothing in these terms is intended to affect those rights.  
  
**15.   LIMITATION ON AND EXCLUSION OF REMEDIES AND DAMAGES. You can recover from Microsoft and its suppliers only direct damages up to U.S. $5.00. You cannot recover any other damages, including consequential, lost profits, special, indirect or incidental damages.**  
  
This limitation applies to  
* anything related to the software, services, content (including code) on third party Internet sites, or third party programs; and  
* claims for breach of contract, breach of warranty, guarantee or condition, strict liability, negligence, or other tort to the extent permitted by applicable law.  
  
It also applies even if Microsoft knew or should have known about the possibility of the damages. The above limitation or exclusion may not apply to you because your country may not allow the exclusion or limitation of incidental, consequential or other damages.  
  
**Please note: As this software is distributed in Quebec, Canada, these license terms are provided below in French.**  
  
**Remarque : Ce logiciel étant distribué au Québec, Canada, certaines des clauses dans ce contrat sont fournies ci-dessous en français.**  
  
EXCLUSIONS DE GARANTIE. Le logiciel est concédé sous licence « en l’état ». Vous assumez tous les risques liés à son utilisation. Microsoft n’accorde aucune garantie ou condition expresse. Vous pouvez bénéficier de droits des consommateurs supplémentaires dans le cadre du droit local, que ce contrat ne peut modifier. Lorsque cela est autorisé par le droit local, Microsoft exclut les garanties implicites de qualité, d’adéquation à un usage particulier et d’absence de contrefaçon.  

LIMITATION ET EXCLUSION DE RECOURS ET DE DOMMAGES. Vous pouvez obtenir de Microsoft et de ses fournisseurs une indemnisation en cas de dommages directs limitée uniquement à hauteur de 5,00 $ US. Vous ne pouvez prétendre à aucune indemnisation pour les autres dommages, y compris les dommages spéciaux, indirects ou accessoires et pertes de bénéfices.  

Cette limitation concerne :  

toute affaire liée au logiciel, aux services ou au contenu (y compris le code) figurant sur des sites Internet tiers ou dans des programmes tiers et 
les réclamations au titre de violation de contrat ou de garantie, ou au titre de responsabilité stricte, de négligence ou d’une autre faute dans la limite autorisée par la loi en vigueur.  

Elle s’applique également même si Microsoft connaissait l'éventualité d'un tel dommage. La limitation ou exclusion ci-dessus peut également ne pas vous être applicable, car votre pays n’autorise pas l’exclusion ou la limitation de responsabilité pour les dommages indirects, accessoires ou de quelque nature que ce soit.  

EFFET JURIDIQUE. Le présent contrat décrit certains droits juridiques. Vous pourriez avoir d’autres droits prévus par les lois de votre pays. Le présent contrat ne modifie pas les droits que vous confèrent les lois de votre pays si celles-ci ne le permettent pas.
tools\dbatools\bin\third-party-licenses\sql-server-data-tools-license-terms.md
---
title: "SQL Server Data Tools - License Terms | Microsoft Docs"
ms.custom: ""
ms.date: "01/19/2017"
ms.prod: "sql-non-specified"
ms.prod_service: "sql-non-specified"
ms.service: ""
ms.component: "ssdt"
ms.reviewer: ""
ms.suite: "sql"
ms.technology: 
  - "tools-ssdt"
ms.tgt_pltfrm: ""
ms.topic: "article"
ms.assetid: 0ac5aa53-cdc3-4ced-9ab9-690be02aa951
caps.latest.revision: 5
author: "stevestein"
ms.author: "sstein"
manager: "jhubbard"
ms.workload: "On Demand"
---
# SQL Server Data Tools - License Terms
[!INCLUDE[appliesto-ss-asdb-asdw-pdw-md](../includes/appliesto-ss-asdb-asdw-pdw-md.md)]
## MICROSOFT SOFTWARE LICENSE TERMS  
  
**MICROSOFT SQL SERVER DATA TOOLS**  
  
---  
These license terms are an agreement between Microsoft Corporation (or based on where you live, one of its affiliates) and you. Please read them. They apply to the software named above, which includes the media on which you received it, if any. The terms also apply to any Microsoft  
* updates,  
* supplements,  
* Internet-based services, and  
* support services  
  
for this software, unless other terms accompany those items. If so, those terms apply.  
  
**BY USING THE SOFTWARE, YOU ACCEPT THESE TERMS. YOU MAY CHOOSE NOT TO ACCEPT THESE TERMS, IN WHICH CASE YOU MAY NOT USE THE SOFTWARE (IF YOU HAVE NOT ALREADY INSTALLED IT) OR WITHDRAW YOUR ACCEPTANCE ANY TIME BY UNINSTALLING THE SOFTWARE.**  
  
---  
**If you comply with these license terms, you have the rights below.**  
**1.    INSTALLATION AND USE RIGHTS.**  
  
&nbsp;&nbsp;**a.    Installation and Use.**  
  
   * You may install and use any number of copies of the software on your devices to design, develop and test your programs.  
     
&nbsp;&nbsp;**b.    Other Microsoft Programs.** The software includes other Microsoft SQL Server technologies, Microsoft Visual Studio 2015 Shell (Isolated) Redistributable Package, Microsoft Visual Studio 2015 Shell (Integrated) Redistributable Package, Microsoft Visual Studio Tools for Applications 2015, .NET Framework, Visual C++ Redistributable for Visual Studio 2015, and Microsoft Report Viewer 2016 Runtime in conjunction with the software licensed here. These components are governed by separate agreements and their own product support policies, as described in the license terms found in the installation directory for that component or in the “Licenses” folder accompanying the software.  
  
**2.    ADDITIONAL LICENSING REQUIREMENTS AND/OR USE RIGHTS.**  
  
&nbsp;&nbsp;**a.    Distributable Code.**  
  
&nbsp;&nbsp;&nbsp;&nbsp;**i.    Right to Use and Distribute. If you comply with the terms below:**  
* You may copy and distribute the object code form of the Microsoft SQL Server Data-Tier Application Framework (“**Distributable Code**”) in programs you develop; and   
* You may permit distributors of your programs to copy and distribute the Distributable Code as part of those programs.  
  
&nbsp;&nbsp;&nbsp;&nbsp;**ii.   Distribution Requirements. For any Distributable Code you distribute, you must**  
* add significant primary functionality to it in your programs;  
* for any Distributable Code having a filename extension of .lib, distribute only the results of running such Distributable Code through a linker with your program;  
* distribute Distributable Code included in a setup program only as part of that setup program without modification;  
* require distributors and external end users to agree to the Microsoft license terms included as part of our software setup program;   
* display your valid copyright notice on your programs; and  
* indemnify, defend, and hold harmless Microsoft from any claims, including attorneys’ fees, related to the distribution or use of your programs.  
  
&nbsp;&nbsp;&nbsp;&nbsp;**iii.  Distribution Restrictions. You may not**  
* alter any copyright, trademark or patent notice in the Distributable Code;  
* use Microsoft’s trademarks in your programs’ names or in a way that suggests your programs come from or are endorsed by Microsoft;  
* distribute Distributable Code to run on a platform other than the Windows platform;  
* include Distributable Code in malicious, deceptive or unlawful programs; or  
* modify or distribute the source code of any Distributable Code so that any part of it becomes subject to an Excluded License. An Excluded License is one that requires, as a condition of use, modification or distribution, that  
* the code be disclosed or distributed in source code form; or  
* others have the right to modify it.  
  
**3.    INTERNET-BASED SERVICES.** Microsoft provides Internet-based services with the software. It may change or cancel them at any time.  You may not use these devices in any way that could harm them or impair anyone else’s use of them.  You may not use the services to try to gain unauthorized access to any service, data, account or network by any means.  
  
&nbsp;&nbsp;**a.    SQL Server Reporting Services Map Report Item.** The software may include features that retrieve content such as maps, images and other data through the Bing Maps (or successor branded) application programming interface (the “Bing Maps APIs”). The purpose of these features is to create reports displaying data on top of maps, aerial and hybrid imagery. If these features are included, you may use them to create and view dynamic or static documents. This may be done only in conjunction with and through methods and means of access integrated in the software. You may not otherwise copy, store, archive, or create a database of the content available through the Bing Maps APIs. You may not use the following for any purpose even if they are available through the Bing Maps APIs:  
* Bing Maps APIs to provide sensor based guidance/routing, or  
* any Road Traffic Data or Bird’s Eye Imagery (or associated metadata).  
Your use of Bing Maps is also governed by the Bing Maps End User Terms of Use available at http://go.microsoft.com/?linkid=9710837 and the Bing Maps Privacy Statement available at http://go.microsoft.com/fwlink/?LinkID=248686.  
  
&nbsp;&nbsp;**b.**  This software is designed to allow users of SQL Server Integration Services (SSIS) to (a) move data between on-premises data-stores and Microsoft online services and (b) trigger certain actions in Microsoft online services. In order to do this, the software uses Internet Protocols to (i) send data, including your own data as designated by you and data about the software’s configuration, to these services, and (ii) request data, including your own data as designated by you and data about the nature and configuration of your Microsoft online services, from these services. Once you configure the software to communicate with these services, you may not receive separate notices when the software connects to these services.  
  
**4.    FEEDBACK.** If you give feedback about the software to Microsoft, you give to Microsoft, without charge, the right to use, share and commercialize your feedback in any way and for any purpose. You also give to third parties, without charge, any patent rights needed for their products, technologies and services to use or interface with any specific parts of a Microsoft software or service that includes the feedback. You will not give feedback that is subject to a license that requires Microsoft to license its software or documentation to third parties because we include your feedback in them. These rights survive this agreement.  
  
**5.  THIRD PARTY NOTICES.** The software may include third party components with separate legal notices or governed by other agreements, as may be described in the ThirdPartyNotices file accompanying the software.  Even if such components are governed by other agreements, the disclaimers and the limitations on and exclusions of damages below also apply.  
  
**6.    .NET FRAMEWORK SOFTWARE.** The software contains Microsoft .NET Framework software. This software is part of Windows. The license terms for Windows apply to your use of the .NET Framework software.  
  
**7.    SCOPE OF LICENSE.** The software is licensed, not sold. This agreement only gives you some rights to use the software. Microsoft reserves all other rights. Unless applicable law gives you more rights despite this limitation, you may use the software only as expressly permitted in this agreement. In doing so, you must comply with any technical limitations in the software that only allow you to use it in certain ways. You may not  
  
* disclose the results of any benchmark tests of the software, other than the Microsoft .NET Framework (see separate term above), to any third party without Microsoft’s prior written approval;  
* work around any technical limitations in the software;  
* reverse engineer, decompile or disassemble the software, except and only to the extent that applicable law expressly permits, despite this limitation;  
* make more copies of the software than specified in this agreement or allowed by applicable law, despite this limitation;  
* publish the software for others to copy;  
* rent, lease or lend the software;  
* transfer the software or this agreement to any third party; or  
* use the software for commercial software hosting services.  
  
**8.    EXPORT RESTRICTIONS.** The software is subject to United States export laws and regulations. You must comply with all domestic and international export laws and regulations that apply to the software. These laws include restrictions on destinations, end users and end use. For additional information, see www.microsoft.com/exporting.  
  
**9.    UPDATES.** The software may install automatic updates, which cannot be turned off. By using the software, you agree to receive automatic updates without any additional notice, and permit Microsoft to download and install them for you. You agree to obtain these updates only from Microsoft or Microsoft authorized sources. If you do not want software updates, disconnect your device from the internet or uninstall the software.  
  
**10.   SUPPORT SERVICES.** Because this software is “as is,” we may not provide support services for it.  
  
**11.   ENTIRE AGREEMENT.** This agreement, and the terms for supplements, updates, Internet-based services and support services that you use, are the entire agreement for the software and support services.  
  
**12.   APPLICABLE LAW.**  
  
&nbsp;&nbsp;**a.    United States.** If you acquired the software in the United States, Washington state law governs the interpretation of this agreement and applies to claims for breach of it, regardless of conflict of laws principles. The laws of the state where you live govern all other claims, including claims under state consumer protection laws, unfair competition laws, and in tort.  
  
&nbsp;&nbsp;**b.    Outside the United States.** If you acquired the software in any other country, the laws of that country apply.  
  
**13.   LEGAL EFFECT.** This agreement describes certain legal rights. You may have other rights under the laws of your country. You may also have rights with respect to the party from whom you acquired the software. This agreement does not change your rights under the laws of your country if the laws of your country do not permit it to do so.  
  
**14.   DISCLAIMER OF WARRANTY. The software is licensed “as-is.” You bear the risk of using it. Microsoft gives no express warranties, guarantees or conditions. You may have additional consumer rights or statutory guarantees under your local laws which this agreement cannot change. To the extent permitted under your local laws, Microsoft excludes the implied warranties of merchantability, fitness for a particular purpose and non-infringement.**  
  
&nbsp;&nbsp;FOR AUSTRALIA – You have statutory guarantees under the Australian Consumer Law and nothing in these terms is intended to affect those rights.  
  
**15.   LIMITATION ON AND EXCLUSION OF REMEDIES AND DAMAGES. You can recover from Microsoft and its suppliers only direct damages up to U.S. $5.00. You cannot recover any other damages, including consequential, lost profits, special, indirect or incidental damages.**  
  
This limitation applies to  
* anything related to the software, services, content (including code) on third party Internet sites, or third party programs; and  
* claims for breach of contract, breach of warranty, guarantee or condition, strict liability, negligence, or other tort to the extent permitted by applicable law.  
  
It also applies even if Microsoft knew or should have known about the possibility of the damages. The above limitation or exclusion may not apply to you because your country may not allow the exclusion or limitation of incidental, consequential or other damages.  
  
**Please note: As this software is distributed in Quebec, Canada, these license terms are provided below in French.**  
  
**Remarque : Ce logiciel étant distribué au Québec, Canada, certaines des clauses dans ce contrat sont fournies ci-dessous en français.**  
  
EXCLUSIONS DE GARANTIE. Le logiciel est concédé sous licence « en l’état ». Vous assumez tous les risques liés à son utilisation. Microsoft n’accorde aucune garantie ou condition expresse. Vous pouvez bénéficier de droits des consommateurs supplémentaires dans le cadre du droit local, que ce contrat ne peut modifier. Lorsque cela est autorisé par le droit local, Microsoft exclut les garanties implicites de qualité, d’adéquation à un usage particulier et d’absence de contrefaçon.  
  
LIMITATION ET EXCLUSION DE RECOURS ET DE DOMMAGES. Vous pouvez obtenir de Microsoft et de ses fournisseurs une indemnisation en cas de dommages directs limitée uniquement à hauteur de 5,00 $ US. Vous ne pouvez prétendre à aucune indemnisation pour les autres dommages, y compris les dommages spéciaux, indirects ou accessoires et pertes de bénéfices.  
  
Cette limitation concerne :  
  
toute affaire liée au logiciel, aux services ou au contenu (y compris le code) figurant sur des sites Internet tiers ou dans des programmes tiers et  
  
les réclamations au titre de violation de contrat ou de garantie, ou au titre de responsabilité stricte, de négligence ou d’une autre faute dans la limite autorisée par la loi en vigueur.  
  
Elle s’applique également même si Microsoft connaissait l'éventualité d'un tel dommage. La limitation ou exclusion ci-dessus peut également ne pas vous être applicable, car votre pays n’autorise pas l’exclusion ou la limitation de responsabilité pour les dommages indirects, accessoires ou de quelque nature que ce soit.  
  
EFFET JURIDIQUE. Le présent contrat décrit certains droits juridiques. Vous pourriez avoir d’autres droits prévus par les lois de votre pays. Le présent contrat ne modifie pas les droits que vous confèrent les lois de votre pays si celles-ci ne le permettent pas.
tools\dbatools\bin\third-party-licenses\ThirdPartyNoticesDacFramework.rtf
 
tools\dbatools\bin\thor.png
 
tools\dbatools\bin\type-extensions.ps1
# Only update on first import
if (-not ([Sqlcollaborative.Dbatools.dbaSystem.SystemHost]::ModuleImported)) {
    # Implement query accelerator for the server object
    Update-TypeData -TypeName Microsoft.SqlServer.Management.Smo.Server -MemberName Query -MemberType ScriptMethod -Value {
        param (
            $Query,

            $Database = "master",

            $AllTables = $false
        )

        if ($AllTables) { ($this.Databases[$Database].ExecuteWithResults($Query)).Tables }
        else { ($this.Databases[$Database].ExecuteWithResults($Query)).Tables[0] }
    } -ErrorAction Ignore

    Update-TypeData -TypeName Microsoft.SqlServer.Management.Smo.Server -MemberName Invoke -MemberType ScriptMethod -Value {
        param (
            $Command,

            $Database = "master"
        )

        $this.Databases[$Database].ExecuteNonQuery($Command)
    } -ErrorAction Ignore

    Update-TypeData -TypeName Microsoft.SqlServer.Management.Smo.Database -MemberName Query -MemberType ScriptMethod -Value {
        param (
            $Query,

            $AllTables = $false
        )

        if ($AllTables) { ($this.ExecuteWithResults($Query)).Tables }
        else { ($this.ExecuteWithResults($Query)).Tables[0] }
    } -ErrorAction Ignore

    Update-TypeData -TypeName Microsoft.SqlServer.Management.Smo.Database -MemberName Invoke -MemberType ScriptMethod -Value {
        param (
            $Command
        )

        $this.ExecuteNonQuery($Command)
    } -ErrorAction Ignore
}
tools\dbatools\bin\typealiases.ps1
# Obtain a reference to the TypeAccelerators type
$TAType = [psobject].Assembly.GetType("System.Management.Automation.TypeAccelerators")

# Define our type aliases
$TypeAliasTable = @{
    DbaInstance              = "Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter"
    DbaCmConnectionParameter = "Sqlcollaborative.Dbatools.Parameter.DbaCmConnectionParameter"
    DbaInstanceParameter     = "Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter"
    dbargx                   = "Sqlcollaborative.Dbatools.Utility.RegexHelper"
    dbatime                  = "Sqlcollaborative.Dbatools.Utility.DbaTime"
    dbadatetime              = "Sqlcollaborative.Dbatools.Utility.DbaDateTime"
    dbadate                  = "Sqlcollaborative.Dbatools.Utility.DbaDate"
    dbatimespan              = "Sqlcollaborative.Dbatools.Utility.DbaTimeSpan"
    prettytimespan           = "Sqlcollaborative.Dbatools.Utility.DbaTimeSpanPretty"
    dbasize                  = "Sqlcollaborative.Dbatools.Utility.Size"
    dbavalidate              = "Sqlcollaborative.Dbatools.Utility.Validation"
    DbaMode                  = "Sqlcollaborative.Dbatools.General.ExecutionMode"
    DbaCredential            = "Sqlcollaborative.Dbatools.Parameter.DbaCredentialparameter"
    DbaCredentialParameter   = "Sqlcollaborative.Dbatools.Parameter.DbaCredentialparameter"
    DbaDatabaseSmo           = "SqlCollaborative.Dbatools.Parameter.DbaDatabaseSmoParameter"
    DbaDatabaseSmoParameter  = "SqlCollaborative.Dbatools.Parameter.DbaDatabaseSmoParameter"
    DbaDatabase              = "SqlCollaborative.Dbatools.Parameter.DbaDatabaseParameter"
    DbaDatabaseParameter     = "SqlCollaborative.Dbatools.Parameter.DbaDatabaseParameter"
    DbaValidatePattern       = "Sqlcollaborative.Dbatools.Utility.DbaValidatePatternAttribute"
    DbaValidateScript        = "Sqlcollaborative.Dbatools.Utility.DbaValidateScriptAttribute"
}

# Add all type aliases
foreach ($TypeAlias in $TypeAliasTable.Keys) {
    try {
        $TAType::Add($TypeAlias, $TypeAliasTable[$TypeAlias])
    }
    catch {
    }
}
tools\dbatools\bin\XESmartTarget\CommandLine.dll
md5: EC0E2DD54144D6F6A317B7DAA715D418 | sha1: 62C2E04BBA8436912D9BCEC1EC4D4C7AFC843A47 | sha256: 4923528D3D18689D58FA30B3D822AB72A13BE21A57F13E0BC59B55B864424F7A | sha512: CCAF01F89FAB001EF2DED15B352E1DD2AB3967584A8720ACB7C6B7203240F9C896F7D46600B12E0E86B25B30779B1E5ED59CE97FB3F295AFB11D6391670265EF
tools\dbatools\bin\XESmartTarget\CsvHelper.dll
md5: C899757ED1CF951D4529025FCE183EC4 | sha1: 5C677F74BC6C460B1AFFEADFBA7F3CC4C81ED438 | sha256: 3C4F05B0EC0D23AEF09D09CE4AF8EA2C198FED0A154B72DFE353F350C6358EFA | sha512: EE823343B1E0EDCFCD3FA11249C8078B925A44D7E3A37B061C9D5DE86F7129E1BCAD2A7E182C8DD9D21CFD86698A0F2AA8FD8753152BFC3BA595FCD70C60BE64
tools\dbatools\bin\XESmartTarget\DouglasCrockford.JsMin.dll
md5: F5676A290241101B94366C062D8455CC | sha1: 4AF1BECC059E3F6F73AE1A8B29DF5B798F3A3C6F | sha256: 065884D15E79EA73630B430A7848F7ED6136FCFFC0B9E6C8AE37564EFACF3AE9 | sha512: 35426B8E728B0E5900B0C2A001824F2EABED3B5363AFF868A237CF9A182D71C419E5564D4D7EE9A1423554D0F18ACFD0958E4EF18487DA52D66CB5BF0B3C1732
tools\dbatools\bin\XESmartTarget\NLog.dll
md5: 169A2802F25F1F083432FC099F4B5E6C | sha1: E9AD81EA61A436303978AADC2E96F7968BCD2C5D | sha256: 4B8399F36A0ABEBD8B2A0DC58AF5D83F3A6AE5F2228AC49E1FE9F70497975635 | sha512: D4F6170448183D5839E44C3F5C971327824CC8B9A760DBEF325F3F866D4CA76AAB58D56A3BCE0A802FFC80CD67F0CF4C7A7DD62C48A9B0033098E2811185351B
tools\dbatools\bin\XESmartTarget\NLog.dll.nlog
 
tools\dbatools\bin\XESmartTarget\SmartFormat.dll
md5: 0B49B13390BF422346590320BB4AC4EC | sha1: 6EB23F32517DC5E7E1466E84AE5E244AE109EFCE | sha256: 59C1973BCD707DBD46FC1367CFFAC884D9BB1B5963553E34CB5555A071F6F606 | sha512: 203947FC6D8B33FDCD3735BC85914A20081B8D8E014B04BFB004D900D6BF4C76153B4212C8D77A3DC25ACF037CAF6EA40EB9B7937CBCD75BC324DC22D7A10AB9
tools\dbatools\bin\XESmartTarget\XESmartTarget.Core.dll
md5: F66FE7A8667D6D372462067727891BA8 | sha1: 62E245983621861AC3942977EF431A9885A578EE | sha256: 7E356E59D3C645CA0692F706AE06CF2DAC244A2C58B956A30639A3B21DA7ABC5 | sha512: 214B48198B3A24ED83870678DCAE23B8664403A9E19692E8FB814FE6DE9E6FAD1BF02F4E0418FFEF0C2D7077888CA8807DCD1A65B46DB0CE6BECE959A7914F1D
tools\dbatools\bin\xetemplates-metadata.xml
tools\dbatools\bin\XEtemplates\15 Second IO Error.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventconfig">
   <event_session name="15 Second IO Error" maxMemory="4" eventRetentionMode="allowSingleEventLoss" trackCausality="false" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="none">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->Everyday Extended Events</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->15 Second IO Error</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Captures query for reads > 15 seconds and writes > 15 seconds.</templateDescription>
      <event package="sqlserver" name="file_read_completed">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="database_name" />
         <action package="sqlserver" name="is_system" />
         <action package="sqlserver" name="session_id" />
         <action package="sqlserver" name="sql_text" />
         <parameter name="collect_io_data" value="1" />
         <parameter name="collect_path" value="1" />
         <predicate>
            <leaf>
               <comparator name="greater_than_uint64" package="package0" />
               <event name="file_read_completed" package="sqlserver" field="duration" />
               <value>15000</value>
            </leaf>
         </predicate>
      </event>
      <event package="sqlserver" name="file_write_completed">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="database_name" />
         <action package="sqlserver" name="is_system" />
         <action package="sqlserver" name="session_id" />
         <action package="sqlserver" name="sql_text" />
         <parameter name="collect_path" value="1" />
         <predicate>
            <leaf>
               <comparator name="greater_than_uint64" package="package0" />
               <event name="file_write_completed" package="sqlserver" field="duration" />
               <value>15000</value>
            </leaf>
         </predicate>
      </event>
      <target package="package0" name="event_file">
         <parameter name="filename" value="15_second_io_error" />
      </target>
   </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Activity Detail Tracking.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventconfig">
  <event_session name="Activity Detail Tracking" maxMemory="4" eventRetentionMode="allowSingleEventLoss" trackCausality="true" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="none">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->System Monitoring</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Activity Detail Tracking</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Similar to the 'Default Trace' that exists in the SQL Trace system. Use this template to track general activity on your system. The difference between this template and the 'Default Trace' is that this template does not include security audit events. If you would like to audit your system you should use the SQL Server Audit feature.</templateDescription>
    <event package="sqlserver" name="database_file_size_change">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="database_id" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="is_system" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="server_instance_name" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="server_principal_sid" />
      <action package="sqlserver" name="session_id" />
      <action package="sqlserver" name="session_server_principal_name" />
      <parameter name="collect_database_name" value="1" />
    </event>
    <event package="sqlserver" name="database_mirroring_state_change">
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="database_name" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="is_system" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="server_instance_name" />
      <action package="sqlserver" name="server_principal_sid" />
      <action package="sqlserver" name="session_id" />
      <action package="sqlserver" name="session_server_principal_name" />
      <action package="sqlserver" name="transaction_id" />
    </event>
    <event package="sqlserver" name="error_reported">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="database_name" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="is_system" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="server_instance_name" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="server_principal_sid" />
      <action package="sqlserver" name="session_id" />
      <action package="sqlserver" name="session_server_principal_name" />
      <action package="sqlserver" name="transaction_id" />
      <predicate>
        <or>
          <or>
            <or>
              <or>
                <leaf>
                  <comparator name="equal_int64" package="package0"></comparator>
                  <event name="error_reported" package="sqlserver" field="error_number"></event>
                  <value>8957</value>
                </leaf>
                <leaf>
                  <comparator name="equal_int64" package="package0"></comparator>
                  <event name="error_reported" package="sqlserver" field="error_number"></event>
                  <value>17550</value>
                </leaf>
              </or>
              <leaf>
                <comparator name="equal_int64" package="package0"></comparator>
                <event name="error_reported" package="sqlserver" field="error_number"></event>
                <value>17551</value>
              </leaf>
            </or>
            <leaf>
              <comparator name="equal_int64" package="package0"></comparator>
              <event name="error_reported" package="sqlserver" field="error_number"></event>
              <value>15457</value>
            </leaf>
          </or>
          <leaf>
            <comparator name="greater_than_int64" package="package0"></comparator>
            <event name="error_reported" package="sqlserver" field="severity"></event>
            <value>18</value>
          </leaf>
        </or>
      </predicate>
    </event>
    <event package="sqlserver" name="full_text_crawl_started">
      <action package="sqlserver" name="database_id" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="is_system" />
      <action package="sqlserver" name="server_instance_name" />
      <action package="sqlserver" name="session_id" />
      <action package="sqlserver" name="session_server_principal_name" />
      <action package="sqlserver" name="transaction_id" />
    </event>
    <event package="sqlserver" name="full_text_crawl_stopped">
      <action package="sqlserver" name="database_id" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="is_system" />
      <action package="sqlserver" name="server_instance_name" />
      <action package="sqlserver" name="session_id" />
      <action package="sqlserver" name="session_server_principal_name" />
      <action package="sqlserver" name="transaction_id" />
    </event>
    <event package="sqlserver" name="hash_warning">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="database_name" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="is_system" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="server_instance_name" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="server_principal_sid" />
      <action package="sqlserver" name="session_id" />
      <action package="sqlserver" name="session_resource_group_id" />
      <action package="sqlserver" name="session_server_principal_name" />
      <action package="sqlserver" name="transaction_id" />
      <action package="sqlserver" name="transaction_sequence" />
    </event>
    <event package="sqlserver" name="missing_column_statistics">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="database_name" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="is_system" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="server_instance_name" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="server_principal_sid" />
      <action package="sqlserver" name="session_id" />
      <action package="sqlserver" name="session_resource_group_id" />
      <action package="sqlserver" name="session_server_principal_name" />
      <action package="sqlserver" name="transaction_id" />
      <action package="sqlserver" name="transaction_sequence" />
    </event>
    <event package="sqlserver" name="missing_join_predicate">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="database_name" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="is_system" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="server_instance_name" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="server_principal_sid" />
      <action package="sqlserver" name="session_id" />
      <action package="sqlserver" name="session_resource_group_id" />
      <action package="sqlserver" name="session_server_principal_name" />
      <action package="sqlserver" name="transaction_id" />
      <action package="sqlserver" name="transaction_sequence" />
    </event>
    <event package="sqlserver" name="object_altered">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="client_pid" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="is_system" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="server_instance_name" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="server_principal_sid" />
      <action package="sqlserver" name="session_id" />
      <action package="sqlserver" name="session_resource_group_id" />
      <action package="sqlserver" name="session_server_principal_name" />
      <action package="sqlserver" name="transaction_sequence" />
      <parameter name="collect_database_name" value="1" />
      <predicate>
        <or>
          <leaf>
            <comparator name="not_equal_uint64" package="package0"></comparator>
            <event name="object_altered" package="sqlserver" field="database_id"></event>
            <value>2</value>
          </leaf>
          <and>
            <leaf>
              <comparator name="greater_than_int64" package="package0"></comparator>
              <event name="object_altered" package="sqlserver" field="object_id"></event>
              <value>0</value>
            </leaf>
            <not>
              <leaf>
                <comparator name="like_i_sql_unicode_string" package="sqlserver"></comparator>
                <event name="object_altered" package="sqlserver" field="object_name"></event>
                <value><![CDATA[#%]]></value>
              </leaf>
            </not>
          </and>
        </or>
      </predicate>
    </event>
    <event package="sqlserver" name="object_created">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="client_pid" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="is_system" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="server_instance_name" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="server_principal_sid" />
      <action package="sqlserver" name="session_id" />
      <action package="sqlserver" name="session_resource_group_id" />
      <action package="sqlserver" name="session_server_principal_name" />
      <action package="sqlserver" name="transaction_sequence" />
      <parameter name="collect_database_name" value="1" />
      <predicate>
        <or>
          <leaf>
            <comparator name="not_equal_uint64" package="package0"></comparator>
            <event name="object_created" package="sqlserver" field="database_id"></event>
            <value>2</value>
          </leaf>
          <and>
            <leaf>
              <comparator name="greater_than_int64" package="package0"></comparator>
              <event name="object_created" package="sqlserver" field="object_id"></event>
              <value>0</value>
            </leaf>
            <not>
              <leaf>
                <comparator name="like_i_sql_unicode_string" package="sqlserver"></comparator>
                <event name="object_created" package="sqlserver" field="object_name"></event>
                <value><![CDATA[#%]]></value>
              </leaf>
            </not>
          </and>
        </or>
      </predicate>
    </event>
    <event package="sqlserver" name="object_deleted">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="client_pid" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="is_system" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="server_instance_name" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="server_principal_sid" />
      <action package="sqlserver" name="session_id" />
      <action package="sqlserver" name="session_resource_group_id" />
      <action package="sqlserver" name="session_server_principal_name" />
      <action package="sqlserver" name="transaction_sequence" />
      <parameter name="collect_database_name" value="1" />
      <predicate>
        <or>
          <leaf>
            <comparator name="not_equal_uint64" package="package0"></comparator>
            <event name="object_deleted" package="sqlserver" field="database_id"></event>
            <value>2</value>
          </leaf>
          <and>
            <leaf>
              <comparator name="greater_than_int64" package="package0"></comparator>
              <event name="object_deleted" package="sqlserver" field="object_id"></event>
              <value>0</value>
            </leaf>
            <not>
              <leaf>
                <comparator name="like_i_sql_unicode_string" package="sqlserver"></comparator>
                <event name="object_deleted" package="sqlserver" field="object_name"></event>
                <value><![CDATA[#%]]></value>
              </leaf>
            </not>
          </and>
        </or>
      </predicate>
    </event>
    <event package="sqlserver" name="plan_guide_unsuccessful">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="database_name" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="is_system" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="server_instance_name" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="server_principal_sid" />
      <action package="sqlserver" name="session_id" />
      <action package="sqlserver" name="session_server_principal_name" />
      <action package="sqlserver" name="transaction_id" />
      <action package="sqlserver" name="transaction_sequence" />
    </event>
    <event package="sqlserver" name="server_memory_change">
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="is_system" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="server_instance_name" />
      <action package="sqlserver" name="session_id" />
      <action package="sqlserver" name="session_server_principal_name" />
      <action package="sqlserver" name="transaction_id" />
      <action package="sqlserver" name="transaction_sequence" />
    </event>
    <event package="sqlserver" name="server_start_stop">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="client_pid" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="is_system" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="server_instance_name" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="server_principal_sid" />
      <action package="sqlserver" name="session_id" />
      <action package="sqlserver" name="session_server_principal_name" />
    </event>
    <event package="sqlserver" name="sort_warning">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="database_name" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="is_system" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="server_instance_name" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="server_principal_sid" />
      <action package="sqlserver" name="session_id" />
      <action package="sqlserver" name="session_resource_group_id" />
      <action package="sqlserver" name="session_server_principal_name" />
      <action package="sqlserver" name="transaction_id" />
      <action package="sqlserver" name="transaction_sequence" />
    </event>
  </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Activity Tracking.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventconfig">
  <event_session name="Activity Tracking" maxMemory="4" eventRetentionMode="allowSingleEventLoss" trackCausality="true" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="none">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->System Monitoring</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Activity Tracking</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Similar to the 'Default Trace' that exists in the SQL Trace system. Use this template to track general activity on your system. The difference between this template and the 'Default Trace' is that this template does not include security audit events. If you would like to audit your system you should use the SQL Server Audit feature.</templateDescription>
    <event package="sqlserver" name="error_reported">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="database_name" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="username" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="session_id" />
      <action package="sqlserver" name="transaction_id" />
      <predicate>
        <or>
          <or>
            <or>
              <or>
                <leaf>
                  <comparator name="equal_int64" package="package0"></comparator>
                  <event name="error_reported" package="sqlserver" field="error_number"></event>
                  <value>8957</value>
                </leaf>
                <leaf>
                  <comparator name="equal_int64" package="package0"></comparator>
                  <event name="error_reported" package="sqlserver" field="error_number"></event>
                  <value>17550</value>
                </leaf>
              </or>
              <leaf>
                <comparator name="equal_int64" package="package0"></comparator>
                <event name="error_reported" package="sqlserver" field="error_number"></event>
                <value>17551</value>
              </leaf>
            </or>
            <leaf>
              <comparator name="equal_int64" package="package0"></comparator>
              <event name="error_reported" package="sqlserver" field="error_number"></event>
              <value>15457</value>
            </leaf>
          </or>
          <leaf>
            <comparator name="greater_than_int64" package="package0"></comparator>
            <event name="error_reported" package="sqlserver" field="severity"></event>
            <value>18</value>
          </leaf>
        </or>
      </predicate>
    </event>
    <event package="sqlserver" name="full_text_crawl_started">
      <action package="sqlserver" name="database_id" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="session_id" />
      <action package="sqlserver" name="transaction_id" />
    </event>
    <event package="sqlserver" name="fulltextlog_written">
      <action package="sqlserver" name="database_id" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="session_id" />
      <action package="sqlserver" name="transaction_id" />
    </event>
    <event package="sqlserver" name="hash_warning">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="database_name" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="username" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="session_id" />
      <action package="sqlserver" name="transaction_id" />

    </event>
    <event package="sqlserver" name="missing_column_statistics">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="database_name" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="username" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="session_id" />
      <action package="sqlserver" name="transaction_id" />

    </event>
    <event package="sqlserver" name="missing_join_predicate">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="database_name" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="username" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="session_id" />
      <action package="sqlserver" name="transaction_id" />
    </event>
    <event package="sqlserver" name="object_altered">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="client_pid" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="username" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="session_id" />
      <parameter name="collect_database_name" value="1" />
      <predicate>
        <or>
          <leaf>
            <comparator name="not_equal_uint64" package="package0"></comparator>
            <event name="object_altered" package="sqlserver" field="database_id"></event>
            <value>2</value>
          </leaf>
          <and>
            <leaf>
              <comparator name="greater_than_int64" package="package0"></comparator>
              <event name="object_altered" package="sqlserver" field="object_id"></event>
              <value>0</value>
            </leaf>
            <not>
              <leaf>
                <comparator name="like_i_sql_unicode_string" package="sqlserver"></comparator>
                <event name="object_altered" package="sqlserver" field="object_name"></event>
                <value><![CDATA[#%]]></value>
              </leaf>
            </not>
          </and>
        </or>
      </predicate>
    </event>
    <event package="sqlserver" name="object_created">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="client_pid" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="username" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="session_id" />

      <parameter name="collect_database_name" value="1" />
      <predicate>
        <or>
          <leaf>
            <comparator name="not_equal_uint64" package="package0"></comparator>
            <event name="object_created" package="sqlserver" field="database_id"></event>
            <value>2</value>
          </leaf>
          <and>
            <leaf>
              <comparator name="greater_than_int64" package="package0"></comparator>
              <event name="object_created" package="sqlserver" field="object_id"></event>
              <value>0</value>
            </leaf>
            <not>
              <leaf>
                <comparator name="like_i_sql_unicode_string" package="sqlserver"></comparator>
                <event name="object_created" package="sqlserver" field="object_name"></event>
                <value><![CDATA[#%]]></value>
              </leaf>
            </not>
          </and>
        </or>
      </predicate>
    </event>
    <event package="sqlserver" name="object_deleted">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="client_pid" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="username" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="session_id" />
      <parameter name="collect_database_name" value="1" />
      <predicate>
        <or>
          <leaf>
            <comparator name="not_equal_uint64" package="package0"></comparator>
            <event name="object_deleted" package="sqlserver" field="database_id"></event>
            <value>2</value>
          </leaf>
          <and>
            <leaf>
              <comparator name="greater_than_int64" package="package0"></comparator>
              <event name="object_deleted" package="sqlserver" field="object_id"></event>
              <value>0</value>
            </leaf>
            <not>
              <leaf>
                <comparator name="like_i_sql_unicode_string" package="sqlserver"></comparator>
                <event name="object_deleted" package="sqlserver" field="object_name"></event>
                <value><![CDATA[#%]]></value>
              </leaf>
            </not>
          </and>
        </or>
      </predicate>
    </event>
    <event package="sqlserver" name="sort_warning">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="database_name" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="username" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="session_id" />
      <action package="sqlserver" name="transaction_id" />
    </event>
  </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\AlwaysOn Health Enhanced.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventsconfig">
   <event_session name="AlwaysOn Health Enhanced" maxMemory="4" eventRetentionMode="allowSingleEventLoss" trackCausality="false" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="none">
      <templateCategory><!-- _locID_text="templateCategory" _locComment = "" -->System Monitoring</templateCategory>
      <templateName><!-- _locID_text = "templateName" _locComment = "" -->AlwaysOn Health Enhanced</templateName>
      <templateDescription><!-- _locID_text = "templateDescription" _locComment = "" -->Enhanced version of AlwaysOn_health</templateDescription>
      <event package="sqlserver" name="alwayson_ddl_executed">
         <action package="sqlserver" name="server_instance_name" />
      </event>
      <event package="sqlserver" name="availability_group_lease_expired">
         <action package="sqlserver" name="server_instance_name" />
      </event>
      <event package="sqlserver" name="availability_replica_automatic_failover_validation">
         <action package="sqlserver" name="server_instance_name" />
      </event>
      <event package="sqlserver" name="availability_replica_manager_state_change">
         <action package="sqlserver" name="server_instance_name" />
      </event>
      <event package="sqlserver" name="availability_replica_state">
         <action package="sqlserver" name="server_instance_name" />
      </event>
      <event package="sqlserver" name="availability_replica_state_change">
         <action package="sqlserver" name="server_instance_name" />
      </event>
      <event package="sqlserver" name="error_reported">
         <action package="sqlserver" name="server_instance_name" />
         <predicate>
            <or>
               <or>
                  <or>
                     <or>
                        <or>
                           <or>
                              <or>
                                 <or>
                                    <or>
                                       <or>
                                          <or>
                                             <or>
                                                <or>
                                                   <or>
                                                      <or>
                                                         <or>
                                                            <or>
                                                               <or>
                                                                  <or>
                                                                     <or>
                                                                        <or>
                                                                           <or>
                                                                              <or>
                                                                                 <or>
                                                                                    <or>
                                                                                       <or>
                                                                                          <or>
                                                                                             <or>
                                                                                                <or>
                                                                                                   <or>
                                                                                                      <leaf>
                                                                                                         <comparator name="equal_int64" package="package0" />
                                                                                                         <event name="error_reported" package="sqlserver" field="error_number" />
                                                                                                         <value>9691</value>
                                                                                                      </leaf>
                                                                                                      <leaf>
                                                                                                         <comparator name="equal_int64" package="package0" />
                                                                                                         <event name="error_reported" package="sqlserver" field="error_number" />
                                                                                                         <value>35204</value>
                                                                                                      </leaf>
                                                                                                   </or>
                                                                                                   <leaf>
                                                                                                      <comparator name="equal_int64" package="package0" />
                                                                                                      <event name="error_reported" package="sqlserver" field="error_number" />
                                                                                                      <value>9693</value>
                                                                                                   </leaf>
                                                                                                </or>
                                                                                                <leaf>
                                                                                                   <comparator name="equal_int64" package="package0" />
                                                                                                   <event name="error_reported" package="sqlserver" field="error_number" />
                                                                                                   <value>26024</value>
                                                                                                </leaf>
                                                                                             </or>
                                                                                             <leaf>
                                                                                                <comparator name="equal_int64" package="package0" />
                                                                                                <event name="error_reported" package="sqlserver" field="error_number" />
                                                                                                <value>28047</value>
                                                                                             </leaf>
                                                                                          </or>
                                                                                          <leaf>
                                                                                             <comparator name="equal_int64" package="package0" />
                                                                                             <event name="error_reported" package="sqlserver" field="error_number" />
                                                                                             <value>26023</value>
                                                                                          </leaf>
                                                                                       </or>
                                                                                       <leaf>
                                                                                          <comparator name="equal_int64" package="package0" />
                                                                                          <event name="error_reported" package="sqlserver" field="error_number" />
                                                                                          <value>9692</value>
                                                                                       </leaf>
                                                                                    </or>
                                                                                    <leaf>
                                                                                       <comparator name="equal_int64" package="package0" />
                                                                                       <event name="error_reported" package="sqlserver" field="error_number" />
                                                                                       <value>28034</value>
                                                                                    </leaf>
                                                                                 </or>
                                                                                 <leaf>
                                                                                    <comparator name="equal_int64" package="package0" />
                                                                                    <event name="error_reported" package="sqlserver" field="error_number" />
                                                                                    <value>28036</value>
                                                                                 </leaf>
                                                                              </or>
                                                                              <leaf>
                                                                                 <comparator name="equal_int64" package="package0" />
                                                                                 <event name="error_reported" package="sqlserver" field="error_number" />
                                                                                 <value>28048</value>
                                                                              </leaf>
                                                                           </or>
                                                                           <leaf>
                                                                              <comparator name="equal_int64" package="package0" />
                                                                              <event name="error_reported" package="sqlserver" field="error_number" />
                                                                              <value>28080</value>
                                                                           </leaf>
                                                                        </or>
                                                                        <leaf>
                                                                           <comparator name="equal_int64" package="package0" />
                                                                           <event name="error_reported" package="sqlserver" field="error_number" />
                                                                           <value>28091</value>
                                                                        </leaf>
                                                                     </or>
                                                                     <leaf>
                                                                        <comparator name="equal_int64" package="package0" />
                                                                        <event name="error_reported" package="sqlserver" field="error_number" />
                                                                        <value>26022</value>
                                                                     </leaf>
                                                                  </or>
                                                                  <leaf>
                                                                     <comparator name="equal_int64" package="package0" />
                                                                     <event name="error_reported" package="sqlserver" field="error_number" />
                                                                     <value>9642</value>
                                                                  </leaf>
                                                               </or>
                                                               <leaf>
                                                                  <comparator name="equal_int64" package="package0" />
                                                                  <event name="error_reported" package="sqlserver" field="error_number" />
                                                                  <value>35201</value>
                                                               </leaf>
                                                            </or>
                                                            <leaf>
                                                               <comparator name="equal_int64" package="package0" />
                                                               <event name="error_reported" package="sqlserver" field="error_number" />
                                                               <value>35202</value>
                                                            </leaf>
                                                         </or>
                                                         <leaf>
                                                            <comparator name="equal_int64" package="package0" />
                                                            <event name="error_reported" package="sqlserver" field="error_number" />
                                                            <value>35206</value>
                                                         </leaf>
                                                      </or>
                                                      <leaf>
                                                         <comparator name="equal_int64" package="package0" />
                                                         <event name="error_reported" package="sqlserver" field="error_number" />
                                                         <value>35207</value>
                                                      </leaf>
                                                   </or>
                                                   <leaf>
                                                      <comparator name="equal_int64" package="package0" />
                                                      <event name="error_reported" package="sqlserver" field="error_number" />
                                                      <value>26069</value>
                                                   </leaf>
                                                </or>
                                                <leaf>
                                                   <comparator name="equal_int64" package="package0" />
                                                   <event name="error_reported" package="sqlserver" field="error_number" />
                                                   <value>26070</value>
                                                </leaf>
                                             </or>
                                             <and>
                                                <leaf>
                                                   <comparator name="greater_than_int64" package="package0" />
                                                   <event name="error_reported" package="sqlserver" field="error_number" />
                                                   <value>41047</value>
                                                </leaf>
                                                <leaf>
                                                   <comparator name="less_than_int64" package="package0" />
                                                   <event name="error_reported" package="sqlserver" field="error_number" />
                                                   <value>41056</value>
                                                </leaf>
                                             </and>
                                          </or>
                                          <leaf>
                                             <comparator name="equal_int64" package="package0" />
                                             <event name="error_reported" package="sqlserver" field="error_number" />
                                             <value>41142</value>
                                          </leaf>
                                       </or>
                                       <leaf>
                                          <comparator name="equal_int64" package="package0" />
                                          <event name="error_reported" package="sqlserver" field="error_number" />
                                          <value>41144</value>
                                       </leaf>
                                    </or>
                                    <leaf>
                                       <comparator name="equal_int64" package="package0" />
                                       <event name="error_reported" package="sqlserver" field="error_number" />
                                       <value>1480</value>
                                    </leaf>
                                 </or>
                                 <leaf>
                                    <comparator name="equal_int64" package="package0" />
                                    <event name="error_reported" package="sqlserver" field="error_number" />
                                    <value>823</value>
                                 </leaf>
                              </or>
                              <leaf>
                                 <comparator name="equal_int64" package="package0" />
                                 <event name="error_reported" package="sqlserver" field="error_number" />
                                 <value>824</value>
                              </leaf>
                           </or>
                           <leaf>
                              <comparator name="equal_int64" package="package0" />
                              <event name="error_reported" package="sqlserver" field="error_number" />
                              <value>829</value>
                           </leaf>
                        </or>
                        <leaf>
                           <comparator name="equal_int64" package="package0" />
                           <event name="error_reported" package="sqlserver" field="error_number" />
                           <value>35264</value>
                        </leaf>
                     </or>
                     <leaf>
                        <comparator name="equal_int64" package="package0" />
                        <event name="error_reported" package="sqlserver" field="error_number" />
                        <value>35265</value>
                     </leaf>
                  </or>
                  <leaf>
                     <comparator name="equal_int64" package="package0" />
                     <event name="error_reported" package="sqlserver" field="error_number" />
                     <value>41188</value>
                  </leaf>
               </or>
               <leaf>
                  <comparator name="equal_int64" package="package0" />
                  <event name="error_reported" package="sqlserver" field="error_number" />
                  <value>41189</value>
               </leaf>
            </or>
         </predicate>
      </event>
      <event package="sqlserver" name="hadr_db_partner_set_sync_state">
         <action package="sqlserver" name="server_instance_name" />
      </event>
      <event package="sqlserver" name="lock_redo_blocked">
         <action package="sqlserver" name="server_instance_name" />
      </event>
      <target package="package0" name="event_file">
         <parameter name="filename" value="AlwaysOn Health Enhanced" />
         <parameter name="max_file_size" value="5" />
         <parameter name="max_rollover_files" value="4" />
      </target>
   </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Blocked Process Report.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventsconfig">
   <event_session name="Blocked Process Report" maxMemory="8" eventRetentionMode="allowSingleEventLoss" trackCausality="false" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="none">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->System Monitoring</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Blocked Process Report</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Captures the blocked process report. In order to capture a blocked process report, you must have the blocked process threshold system configuration option enabled (To capture blocking processes, configure blocked threshold (Set-DbaSpConfigure -SqlInstance sql2017 -ConfigName BlockedProcessThreshold -Value 5). </templateDescription>
      <event package="sqlserver" name="blocked_process_report" />
      <target package="package0" name="event_file">
         <parameter name="filename" value="Blocked Process Report" />
      </target>
   </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Connection Detail Tracking.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventconfig">
  <event_session name="Connection Detail Tracking" maxMemory="4" eventRetentionMode="allowSingleEventLoss" trackCausality="true" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="none">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->System Monitoring</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Connection Detail Tracking</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Tracks connection activity for a server. Normal connection activity is tracked using the login and logout events, and problems are recorded using the connectivity_ring_buffer_recorded event.</templateDescription>
    <event package="sqlserver" name="connectivity_ring_buffer_recorded">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_connection_id" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="context_info" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="session_id" />
    </event>
    <event package="sqlserver" name="login">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_connection_id" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="context_info" />
      <action package="sqlserver" name="server_instance_name" />
      <action package="sqlserver" name="server_principal_name" />
      <parameter name="collect_options_text" value="1" />
    </event>
    <event package="sqlserver" name="logout">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_connection_id" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="context_info" />
      <action package="sqlserver" name="server_instance_name" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="session_id" />
    </event>
    <target package="package0" name="ring_buffer" />
  </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Connection Tracking.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventconfig">
  <event_session name="Connection Tracking" maxMemory="4" eventRetentionMode="allowSingleEventLoss" trackCausality="true" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="none">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->System Monitoring</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Connection Tracking</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Tracks connection activity for a server. Normal connection activity is tracked using the login and logout events</templateDescription>
    <event package="sqlserver" name="login">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_connection_id" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="context_info" />
      <parameter name="collect_options_text" value="1" />
    </event>
    <event package="sqlserver" name="logout">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_connection_id" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="context_info" />
      <action package="sqlserver" name="session_id" />
    </event>
    <target package="package0" name="ring_buffer" />
  </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Count Query Locks.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventconfig">
  <event_session name="Count Query Locks" maxMemory="4" eventRetentionMode="allowSingleEventLoss" trackCausality="true" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="none">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->Locks and Blocks</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Count Query Locks</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Counts the number of locks acquired by each query based on the query_hash value. You can use this template to identify the most lock intensive queries for investigation and tuning.</templateDescription>
    <event package="sqlserver" name="lock_acquired">
      <action package="sqlserver" name="query_hash" />
      <predicate>
        <and>
          <leaf>
            <comparator name="greater_than_uint64" package="package0"></comparator>
            <event name="lock_acquired" package="sqlserver" field="database_id"></event>
            <value>4</value>
          </leaf>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <target package="package0" name="histogram">
      <parameter name="filtering_event_name" value="sqlserver.lock_acquired" />
      <parameter name="source" value="sqlserver.query_hash" />
    </target>
  </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Database File IO.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventconfig">
   <event_session name="Database File I/O" maxMemory="4" eventRetentionMode="allowSingleEventLoss" trackCausality="false" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="none">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->Everyday Extended Events</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Database File IO</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Use event counter and histogram to see which database file(s) have the most reads and writes.</templateDescription>
      <event package="sqlserver" name="file_read_completed">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="database_name" />
         <action package="sqlserver" name="plan_handle" />
         <action package="sqlserver" name="sql_text" />
         <parameter name="collect_io_data" value="1" />
         <parameter name="collect_path" value="1" />
         <predicate>
            <leaf>
               <comparator name="greater_than_uint64" package="package0" />
               <global name="database_id" package="sqlserver" />
               <value>4</value>
            </leaf>
         </predicate>
      </event>
      <event package="sqlserver" name="file_write_completed">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="database_name" />
         <action package="sqlserver" name="plan_handle" />
         <action package="sqlserver" name="sql_text" />
         <parameter name="collect_path" value="1" />
         <predicate>
            <leaf>
               <comparator name="greater_than_uint64" package="package0" />
               <global name="database_id" package="sqlserver" />
               <value>4</value>
            </leaf>
         </predicate>
      </event>
      <target package="package0" name="event_counter" />
      <target package="package0" name="histogram">
         <parameter name="filtering_event_name" value="sqlserver.file_read_completed" />
         <parameter name="source" value="file_id" />
         <parameter name="source_type" value="0" />
      </target>
   </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Database Health 2012.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventsconfig">
   <event_session name="Database Health 2012" maxMemory="4" eventRetentionMode="allowMultipleEventLoss" trackCausality="true" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="perNode">
   <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->Performance Store</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Database Health 2012</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Similar to system_health but for SQL Server 2012 databases by Ola Hallengren. This Extended Event captures most of the valuable data for troubleshooting such as deadlocks, blocking processes, slow statements, errors, file size changes, info for tempdb troubleshooting etc. To capture blocking processes, configure blocked threshold (Set-DbaSpConfigure -SqlInstance sql2017 -ConfigName BlockedProcessThreshold -Value 5)</templateDescription>
      <event package="sqlos" name="wait_info">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="client_pid" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="database_name" />
         <action package="sqlserver" name="nt_username" />
         <action package="sqlserver" name="plan_handle" />
         <action package="sqlserver" name="query_hash_signed" />
         <action package="sqlserver" name="query_plan_hash_signed" />
         <action package="sqlserver" name="request_id" />
         <action package="sqlserver" name="session_id" />
         <action package="sqlserver" name="session_resource_group_id" />
         <action package="sqlserver" name="transaction_id" />
         <action package="sqlserver" name="tsql_frame" />
         <action package="sqlserver" name="username" />
         <predicate>
            <and>
               <and>
                  <and>
                     <and>
                        <and>
                           <and>
                              <and>
                                 <leaf>
                                    <comparator name="equal_uint64" package="package0" />
                                    <event name="wait_info" package="sqlos" field="opcode" />
                                    <value>1</value>
                                 </leaf>
                                 <leaf>
                                    <comparator name="equal_boolean" package="package0" />
                                    <global name="is_system" package="sqlserver" />
                                    <value>false</value>
                                 </leaf>
                              </and>
                              <leaf>
                                 <comparator name="greater_than_equal_uint64" package="package0" />
                                 <event name="wait_info" package="sqlos" field="duration" />
                                 <value>5000</value>
                              </leaf>
                           </and>
                           <leaf>
                              <comparator name="not_equal_uint64" package="package0" />
                              <event name="wait_info" package="sqlos" field="wait_type" />
                              <value>225</value>
                           </leaf>
                        </and>
                        <leaf>
                           <comparator name="not_equal_uint64" package="package0" />
                           <event name="wait_info" package="sqlos" field="wait_type" />
                           <value>223</value>
                        </leaf>
                     </and>
                     <leaf>
                        <comparator name="not_equal_uint64" package="package0" />
                        <event name="wait_info" package="sqlos" field="wait_type" />
                        <value>377</value>
                     </leaf>
                  </and>
                  <leaf>
                     <comparator name="not_equal_uint64" package="package0" />
                     <event name="wait_info" package="sqlos" field="wait_type" />
                     <value>645</value>
                  </leaf>
               </and>
               <leaf>
                  <comparator name="not_equal_uint64" package="package0" />
                  <event name="wait_info" package="sqlos" field="wait_type" />
                  <value>738</value>
               </leaf>
            </and>
         </predicate>
      </event>
      <event package="sqlos" name="wait_info_external">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="client_pid" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="database_name" />
         <action package="sqlserver" name="nt_username" />
         <action package="sqlserver" name="plan_handle" />
         <action package="sqlserver" name="query_hash_signed" />
         <action package="sqlserver" name="query_plan_hash_signed" />
         <action package="sqlserver" name="request_id" />
         <action package="sqlserver" name="session_id" />
         <action package="sqlserver" name="session_resource_group_id" />
         <action package="sqlserver" name="transaction_id" />
         <action package="sqlserver" name="tsql_frame" />
         <action package="sqlserver" name="username" />
         <predicate>
            <and>
               <and>
                  <leaf>
                     <comparator name="equal_uint64" package="package0" />
                     <event name="wait_info_external" package="sqlos" field="opcode" />
                     <value>1</value>
                  </leaf>
                  <leaf>
                     <comparator name="equal_boolean" package="package0" />
                     <global name="is_system" package="sqlserver" />
                     <value>false</value>
                  </leaf>
               </and>
               <leaf>
                  <comparator name="greater_than_equal_uint64" package="package0" />
                  <event name="wait_info_external" package="sqlos" field="duration" />
                  <value>5000</value>
               </leaf>
            </and>
         </predicate>
      </event>
      <event package="sqlserver" name="blocked_process_report" />
      <event package="sqlserver" name="database_file_size_change">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="client_pid" />
         <action package="sqlserver" name="nt_username" />
         <action package="sqlserver" name="plan_handle" />
         <action package="sqlserver" name="request_id" />
         <action package="sqlserver" name="session_id" />
         <action package="sqlserver" name="session_resource_group_id" />
         <action package="sqlserver" name="transaction_id" />
         <action package="sqlserver" name="username" />
         <parameter name="collect_database_name" value="1" />
      </event>
      <event package="sqlserver" name="error_reported">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="client_pid" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="database_name" />
         <action package="sqlserver" name="nt_username" />
         <action package="sqlserver" name="plan_handle" />
         <action package="sqlserver" name="request_id" />
         <action package="sqlserver" name="session_id" />
         <action package="sqlserver" name="session_resource_group_id" />
         <action package="sqlserver" name="transaction_id" />
         <action package="sqlserver" name="tsql_frame" />
         <action package="sqlserver" name="username" />
         <predicate>
            <leaf>
               <comparator name="greater_than_int64" package="package0" />
               <event name="error_reported" package="sqlserver" field="severity" />
               <value>10</value>
            </leaf>
         </predicate>
      </event>
      <event package="sqlserver" name="module_end">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="client_pid" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="database_name" />
         <action package="sqlserver" name="nt_username" />
         <action package="sqlserver" name="plan_handle" />
         <action package="sqlserver" name="request_id" />
         <action package="sqlserver" name="session_id" />
         <action package="sqlserver" name="session_resource_group_id" />
         <action package="sqlserver" name="transaction_id" />
         <action package="sqlserver" name="tsql_frame" />
         <action package="sqlserver" name="username" />
         <predicate>
            <and>
               <leaf>
                  <comparator name="equal_boolean" package="package0" />
                  <global name="is_system" package="sqlserver" />
                  <value>false</value>
               </leaf>
               <leaf>
                  <comparator name="greater_than_equal_uint64" package="package0" />
                  <event name="module_end" package="sqlserver" field="duration" />
                  <value>5000000</value>
               </leaf>
            </and>
         </predicate>
      </event>
      <event package="sqlserver" name="rpc_completed">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="client_pid" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="database_name" />
         <action package="sqlserver" name="nt_username" />
         <action package="sqlserver" name="request_id" />
         <action package="sqlserver" name="session_id" />
         <action package="sqlserver" name="session_resource_group_id" />
         <action package="sqlserver" name="transaction_id" />
         <action package="sqlserver" name="username" />
         <predicate>
            <and>
               <leaf>
                  <comparator name="equal_boolean" package="package0" />
                  <global name="is_system" package="sqlserver" />
                  <value>false</value>
               </leaf>
               <or>
                  <leaf>
                     <comparator name="greater_than_equal_uint64" package="package0" />
                     <event name="rpc_completed" package="sqlserver" field="duration" />
                     <value>5000000</value>
                  </leaf>
                  <leaf>
                     <comparator name="not_equal_uint64" package="package0" />
                     <event name="rpc_completed" package="sqlserver" field="result" />
                     <value>0</value>
                  </leaf>
               </or>
            </and>
         </predicate>
      </event>
      <event package="sqlserver" name="sql_batch_completed">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="client_pid" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="database_name" />
         <action package="sqlserver" name="nt_username" />
         <action package="sqlserver" name="request_id" />
         <action package="sqlserver" name="session_id" />
         <action package="sqlserver" name="session_resource_group_id" />
         <action package="sqlserver" name="transaction_id" />
         <action package="sqlserver" name="username" />
         <parameter name="collect_batch_text" value="1" />
         <predicate>
            <and>
               <leaf>
                  <comparator name="equal_boolean" package="package0" />
                  <global name="is_system" package="sqlserver" />
                  <value>false</value>
               </leaf>
               <or>
                  <leaf>
                     <comparator name="greater_than_equal_uint64" package="package0" />
                     <event name="sql_batch_completed" package="sqlserver" field="duration" />
                     <value>5000000</value>
                  </leaf>
                  <leaf>
                     <comparator name="not_equal_uint64" package="package0" />
                     <event name="sql_batch_completed" package="sqlserver" field="result" />
                     <value>0</value>
                  </leaf>
               </or>
            </and>
         </predicate>
      </event>
      <event package="sqlserver" name="xml_deadlock_report" />
      <target package="package0" name="event_file">
         <parameter name="filename" value="Database Health 2012" />
         <parameter name="max_file_size" value="5" />
         <parameter name="max_rollover_files" value="100" />
      </target>
   </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Database Health 2014.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventsconfig">
   <event_session name="Database Health 2014" maxMemory="4" eventRetentionMode="allowMultipleEventLoss" trackCausality="true" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="perNode">
	<templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->Performance Store</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Database Health 2014</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Similar to system_health but for SQL Server 2014 databases by Ola Hallengren. This Extended Event captures most of the valuable data for troubleshooting such as deadlocks, blocking processes, slow statements, errors, file size changes, info for tempdb troubleshooting etc. To capture blocking processes, configure blocked threshold (Set-DbaSpConfigure -SqlInstance sql2017 -ConfigName BlockedProcessThreshold -Value 5)</templateDescription>
            <event package="sqlos" name="wait_completed">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="client_pid" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="database_name" />
         <action package="sqlserver" name="nt_username" />
         <action package="sqlserver" name="plan_handle" />
         <action package="sqlserver" name="query_hash_signed" />
         <action package="sqlserver" name="query_plan_hash_signed" />
         <action package="sqlserver" name="request_id" />
         <action package="sqlserver" name="session_id" />
         <action package="sqlserver" name="session_resource_group_id" />
         <action package="sqlserver" name="transaction_id" />
         <action package="sqlserver" name="tsql_frame" />
         <action package="sqlserver" name="username" />
         <predicate>
            <and>
               <and>
                  <and>
                     <and>
                        <and>
                           <and>
                              <leaf>
                                 <comparator name="equal_boolean" package="package0" />
                                 <global name="is_system" package="sqlserver" />
                                 <value>false</value>
                              </leaf>
                              <leaf>
                                 <comparator name="greater_than_equal_uint64" package="package0" />
                                 <event name="wait_completed" package="sqlos" field="duration" />
                                 <value>5000</value>
                              </leaf>
                           </and>
                           <leaf>
                              <comparator name="not_equal_uint64" package="package0" />
                              <event name="wait_completed" package="sqlos" field="wait_type" />
                              <value>233</value>
                           </leaf>
                        </and>
                        <leaf>
                           <comparator name="not_equal_uint64" package="package0" />
                           <event name="wait_completed" package="sqlos" field="wait_type" />
                           <value>231</value>
                        </leaf>
                     </and>
                     <leaf>
                        <comparator name="not_equal_uint64" package="package0" />
                        <event name="wait_completed" package="sqlos" field="wait_type" />
                        <value>395</value>
                     </leaf>
                  </and>
                  <leaf>
                     <comparator name="not_equal_uint64" package="package0" />
                     <event name="wait_completed" package="sqlos" field="wait_type" />
                     <value>666</value>
                  </leaf>
               </and>
               <leaf>
                  <comparator name="not_equal_uint64" package="package0" />
                  <event name="wait_completed" package="sqlos" field="wait_type" />
                  <value>766</value>
               </leaf>
            </and>
         </predicate>
      </event>
      <event package="sqlos" name="wait_info_external">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="client_pid" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="database_name" />
         <action package="sqlserver" name="nt_username" />
         <action package="sqlserver" name="plan_handle" />
         <action package="sqlserver" name="query_hash_signed" />
         <action package="sqlserver" name="query_plan_hash_signed" />
         <action package="sqlserver" name="request_id" />
         <action package="sqlserver" name="session_id" />
         <action package="sqlserver" name="session_resource_group_id" />
         <action package="sqlserver" name="transaction_id" />
         <action package="sqlserver" name="tsql_frame" />
         <action package="sqlserver" name="username" />
         <predicate>
            <and>
               <and>
                  <leaf>
                     <comparator name="equal_uint64" package="package0" />
                     <event name="wait_info_external" package="sqlos" field="opcode" />
                     <value>1</value>
                  </leaf>
                  <leaf>
                     <comparator name="equal_boolean" package="package0" />
                     <global name="is_system" package="sqlserver" />
                     <value>false</value>
                  </leaf>
               </and>
               <leaf>
                  <comparator name="greater_than_equal_uint64" package="package0" />
                  <event name="wait_info_external" package="sqlos" field="duration" />
                  <value>5000</value>
               </leaf>
            </and>
         </predicate>
      </event>
      <event package="sqlserver" name="blocked_process_report" />
      <event package="sqlserver" name="database_file_size_change">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="client_pid" />
         <action package="sqlserver" name="nt_username" />
         <action package="sqlserver" name="plan_handle" />
         <action package="sqlserver" name="request_id" />
         <action package="sqlserver" name="session_id" />
         <action package="sqlserver" name="session_resource_group_id" />
         <action package="sqlserver" name="transaction_id" />
         <action package="sqlserver" name="username" />
         <parameter name="collect_database_name" value="1" />
      </event>
      <event package="sqlserver" name="error_reported">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="client_pid" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="database_name" />
         <action package="sqlserver" name="nt_username" />
         <action package="sqlserver" name="plan_handle" />
         <action package="sqlserver" name="request_id" />
         <action package="sqlserver" name="session_id" />
         <action package="sqlserver" name="session_resource_group_id" />
         <action package="sqlserver" name="transaction_id" />
         <action package="sqlserver" name="tsql_frame" />
         <action package="sqlserver" name="username" />
         <predicate>
            <leaf>
               <comparator name="greater_than_int64" package="package0" />
               <event name="error_reported" package="sqlserver" field="severity" />
               <value>10</value>
            </leaf>
         </predicate>
      </event>
      <event package="sqlserver" name="module_end">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="client_pid" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="database_name" />
         <action package="sqlserver" name="nt_username" />
         <action package="sqlserver" name="plan_handle" />
         <action package="sqlserver" name="request_id" />
         <action package="sqlserver" name="session_id" />
         <action package="sqlserver" name="session_resource_group_id" />
         <action package="sqlserver" name="transaction_id" />
         <action package="sqlserver" name="tsql_frame" />
         <action package="sqlserver" name="username" />
         <predicate>
            <and>
               <leaf>
                  <comparator name="equal_boolean" package="package0" />
                  <global name="is_system" package="sqlserver" />
                  <value>false</value>
               </leaf>
               <leaf>
                  <comparator name="greater_than_equal_uint64" package="package0" />
                  <event name="module_end" package="sqlserver" field="duration" />
                  <value>5000000</value>
               </leaf>
            </and>
         </predicate>
      </event>
      <event package="sqlserver" name="rpc_completed">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="client_pid" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="database_name" />
         <action package="sqlserver" name="nt_username" />
         <action package="sqlserver" name="request_id" />
         <action package="sqlserver" name="session_id" />
         <action package="sqlserver" name="session_resource_group_id" />
         <action package="sqlserver" name="transaction_id" />
         <action package="sqlserver" name="username" />
         <predicate>
            <and>
               <leaf>
                  <comparator name="equal_boolean" package="package0" />
                  <global name="is_system" package="sqlserver" />
                  <value>false</value>
               </leaf>
               <or>
                  <leaf>
                     <comparator name="greater_than_equal_uint64" package="package0" />
                     <event name="rpc_completed" package="sqlserver" field="duration" />
                     <value>5000000</value>
                  </leaf>
                  <leaf>
                     <comparator name="not_equal_uint64" package="package0" />
                     <event name="rpc_completed" package="sqlserver" field="result" />
                     <value>0</value>
                  </leaf>
               </or>
            </and>
         </predicate>
      </event>
      <event package="sqlserver" name="sql_batch_completed">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="client_pid" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="database_name" />
         <action package="sqlserver" name="nt_username" />
         <action package="sqlserver" name="request_id" />
         <action package="sqlserver" name="session_id" />
         <action package="sqlserver" name="session_resource_group_id" />
         <action package="sqlserver" name="transaction_id" />
         <action package="sqlserver" name="username" />
         <parameter name="collect_batch_text" value="1" />
         <predicate>
            <and>
               <leaf>
                  <comparator name="equal_boolean" package="package0" />
                  <global name="is_system" package="sqlserver" />
                  <value>false</value>
               </leaf>
               <or>
                  <leaf>
                     <comparator name="greater_than_equal_uint64" package="package0" />
                     <event name="sql_batch_completed" package="sqlserver" field="duration" />
                     <value>5000000</value>
                  </leaf>
                  <leaf>
                     <comparator name="not_equal_uint64" package="package0" />
                     <event name="sql_batch_completed" package="sqlserver" field="result" />
                     <value>0</value>
                  </leaf>
               </or>
            </and>
         </predicate>
      </event>
      <event package="sqlserver" name="xml_deadlock_report">
         <action package="sqlserver" name="database_name" />
      </event>
      <target package="package0" name="event_file">
         <parameter name="filename" value="Database Health 2014" />
         <parameter name="max_file_size" value="5" />
         <parameter name="max_rollover_files" value="100" />
      </target>
   </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Database Health 2016 and Above.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventsconfig">
   <event_session name="Database Health 2016 and Above" maxMemory="4" eventRetentionMode="allowMultipleEventLoss" trackCausality="true" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="perNode">
      <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->Performance Store</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Database Health 2016 and Above</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Similar to system_health but for SQL Server 2016+ databases by Ola Hallengren. This Extended Event captures most of the valuable data for troubleshooting such as deadlocks, blocking processes, slow statements, errors, file size changes, info for tempdb troubleshooting etc. To capture blocking processes, configure blocked threshold (Set-DbaSpConfigure -SqlInstance sql2017 -ConfigName BlockedProcessThreshold -Value 5)</templateDescription>
    <event package="sqlos" name="wait_completed">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="client_pid" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="database_name" />
         <action package="sqlserver" name="nt_username" />
         <action package="sqlserver" name="plan_handle" />
         <action package="sqlserver" name="query_hash_signed" />
         <action package="sqlserver" name="query_plan_hash_signed" />
         <action package="sqlserver" name="request_id" />
         <action package="sqlserver" name="session_id" />
         <action package="sqlserver" name="session_resource_group_id" />
         <action package="sqlserver" name="transaction_id" />
         <action package="sqlserver" name="tsql_frame" />
         <action package="sqlserver" name="username" />
         <predicate>
            <and>
               <and>
                  <and>
                     <and>
                        <and>
                           <and>
                              <and>
                                 <and>
                                    <leaf>
                                       <comparator name="equal_boolean" package="package0" />
                                       <global name="is_system" package="sqlserver" />
                                       <value>false</value>
                                    </leaf>
                                    <leaf>
                                       <comparator name="greater_than_equal_uint64" package="package0" />
                                       <event name="wait_completed" package="sqlos" field="duration" />
                                       <value>5000</value>
                                    </leaf>
                                 </and>
                                 <leaf>
                                    <comparator name="not_equal_uint64" package="package0" />
                                    <event name="wait_completed" package="sqlos" field="wait_type" />
                                    <value>233</value>
                                 </leaf>
                              </and>
                              <leaf>
                                 <comparator name="not_equal_uint64" package="package0" />
                                 <event name="wait_completed" package="sqlos" field="wait_type" />
                                 <value>231</value>
                              </leaf>
                           </and>
                           <leaf>
                              <comparator name="not_equal_uint64" package="package0" />
                              <event name="wait_completed" package="sqlos" field="wait_type" />
                              <value>395</value>
                           </leaf>
                        </and>
                        <leaf>
                           <comparator name="not_equal_uint64" package="package0" />
                           <event name="wait_completed" package="sqlos" field="wait_type" />
                           <value>666</value>
                        </leaf>
                     </and>
                     <leaf>
                        <comparator name="not_equal_uint64" package="package0" />
                        <event name="wait_completed" package="sqlos" field="wait_type" />
                        <value>766</value>
                     </leaf>
                  </and>
                  <leaf>
                     <comparator name="not_equal_uint64" package="package0" />
                     <event name="wait_completed" package="sqlos" field="wait_type" />
                     <value>1114</value>
                  </leaf>
               </and>
               <leaf>
                  <comparator name="not_equal_uint64" package="package0" />
                  <event name="wait_completed" package="sqlos" field="wait_type" />
                  <value>1013</value>
               </leaf>
            </and>
         </predicate>
      </event>
      <event package="sqlos" name="wait_info_external">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="client_pid" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="database_name" />
         <action package="sqlserver" name="nt_username" />
         <action package="sqlserver" name="plan_handle" />
         <action package="sqlserver" name="query_hash_signed" />
         <action package="sqlserver" name="query_plan_hash_signed" />
         <action package="sqlserver" name="request_id" />
         <action package="sqlserver" name="session_id" />
         <action package="sqlserver" name="session_resource_group_id" />
         <action package="sqlserver" name="transaction_id" />
         <action package="sqlserver" name="tsql_frame" />
         <action package="sqlserver" name="username" />
         <predicate>
            <and>
               <and>
                  <leaf>
                     <comparator name="equal_uint64" package="package0" />
                     <event name="wait_info_external" package="sqlos" field="opcode" />
                     <value>1</value>
                  </leaf>
                  <leaf>
                     <comparator name="equal_boolean" package="package0" />
                     <global name="is_system" package="sqlserver" />
                     <value>false</value>
                  </leaf>
               </and>
               <leaf>
                  <comparator name="greater_than_equal_uint64" package="package0" />
                  <event name="wait_info_external" package="sqlos" field="duration" />
                  <value>5000</value>
               </leaf>
            </and>
         </predicate>
      </event>
      <event package="sqlserver" name="blocked_process_report" />
      <event package="sqlserver" name="database_file_size_change">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="client_pid" />
         <action package="sqlserver" name="nt_username" />
         <action package="sqlserver" name="plan_handle" />
         <action package="sqlserver" name="request_id" />
         <action package="sqlserver" name="session_id" />
         <action package="sqlserver" name="session_resource_group_id" />
         <action package="sqlserver" name="transaction_id" />
         <action package="sqlserver" name="username" />
         <parameter name="collect_database_name" value="1" />
      </event>
      <event package="sqlserver" name="error_reported">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="client_pid" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="database_name" />
         <action package="sqlserver" name="nt_username" />
         <action package="sqlserver" name="plan_handle" />
         <action package="sqlserver" name="request_id" />
         <action package="sqlserver" name="session_id" />
         <action package="sqlserver" name="session_resource_group_id" />
         <action package="sqlserver" name="transaction_id" />
         <action package="sqlserver" name="tsql_frame" />
         <action package="sqlserver" name="username" />
         <predicate>
            <leaf>
               <comparator name="greater_than_int64" package="package0" />
               <event name="error_reported" package="sqlserver" field="severity" />
               <value>10</value>
            </leaf>
         </predicate>
      </event>
      <event package="sqlserver" name="module_end">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="client_pid" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="database_name" />
         <action package="sqlserver" name="nt_username" />
         <action package="sqlserver" name="plan_handle" />
         <action package="sqlserver" name="request_id" />
         <action package="sqlserver" name="session_id" />
         <action package="sqlserver" name="session_resource_group_id" />
         <action package="sqlserver" name="transaction_id" />
         <action package="sqlserver" name="tsql_frame" />
         <action package="sqlserver" name="username" />
         <predicate>
            <and>
               <leaf>
                  <comparator name="equal_boolean" package="package0" />
                  <global name="is_system" package="sqlserver" />
                  <value>false</value>
               </leaf>
               <leaf>
                  <comparator name="greater_than_equal_uint64" package="package0" />
                  <event name="module_end" package="sqlserver" field="duration" />
                  <value>5000000</value>
               </leaf>
            </and>
         </predicate>
      </event>
      <event package="sqlserver" name="rpc_completed">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="client_pid" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="database_name" />
         <action package="sqlserver" name="nt_username" />
         <action package="sqlserver" name="request_id" />
         <action package="sqlserver" name="session_id" />
         <action package="sqlserver" name="session_resource_group_id" />
         <action package="sqlserver" name="transaction_id" />
         <action package="sqlserver" name="username" />
         <predicate>
            <and>
               <leaf>
                  <comparator name="equal_boolean" package="package0" />
                  <global name="is_system" package="sqlserver" />
                  <value>false</value>
               </leaf>
               <or>
                  <leaf>
                     <comparator name="greater_than_equal_uint64" package="package0" />
                     <event name="rpc_completed" package="sqlserver" field="duration" />
                     <value>5000000</value>
                  </leaf>
                  <leaf>
                     <comparator name="not_equal_uint64" package="package0" />
                     <event name="rpc_completed" package="sqlserver" field="result" />
                     <value>0</value>
                  </leaf>
               </or>
            </and>
         </predicate>
      </event>
      <event package="sqlserver" name="sql_batch_completed">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="client_pid" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="database_name" />
         <action package="sqlserver" name="nt_username" />
         <action package="sqlserver" name="request_id" />
         <action package="sqlserver" name="session_id" />
         <action package="sqlserver" name="session_resource_group_id" />
         <action package="sqlserver" name="transaction_id" />
         <action package="sqlserver" name="username" />
         <parameter name="collect_batch_text" value="1" />
         <predicate>
            <and>
               <leaf>
                  <comparator name="equal_boolean" package="package0" />
                  <global name="is_system" package="sqlserver" />
                  <value>false</value>
               </leaf>
               <or>
                  <leaf>
                     <comparator name="greater_than_equal_uint64" package="package0" />
                     <event name="sql_batch_completed" package="sqlserver" field="duration" />
                     <value>5000000</value>
                  </leaf>
                  <leaf>
                     <comparator name="not_equal_uint64" package="package0" />
                     <event name="sql_batch_completed" package="sqlserver" field="result" />
                     <value>0</value>
                  </leaf>
               </or>
            </and>
         </predicate>
      </event>
      <event package="sqlserver" name="xml_deadlock_report">
         <action package="sqlserver" name="database_name" />
      </event>
      <target package="package0" name="event_file">
         <parameter name="filename" value="Database Health 2016 and Above" />
         <parameter name="max_file_size" value="5" />
         <parameter name="max_rollover_files" value="100" />
      </target>
   </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Deadlock Graphs.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventconfig">
   <event_session name="Deadlock Graphs" maxMemory="4" eventRetentionMode="allowSingleEventLoss" trackCausality="false" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="none">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->Everyday Extended Events</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Deadlock Graphs</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Captures deadlock information. Careful, the Deadlock tab is sneaky!</templateDescription>
      <event package="sqlserver" name="database_xml_deadlock_report">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="database_name" />
         <action package="sqlserver" name="plan_handle" />
         <action package="sqlserver" name="sql_text" />
      </event>
      <event package="sqlserver" name="xml_deadlock_report">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="database_name" />
         <action package="sqlserver" name="plan_handle" />
         <action package="sqlserver" name="sql_text" />
      </event>
      <target package="package0" name="event_file">
         <parameter name="filename" value="capture_deadlock_graphs" />
      </target>
   </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Default Profiler Trace.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventconfig">
   <event_session name="Default Profiler Trace" maxMemory="4" eventRetentionMode="allowSingleEventLoss" trackCausality="false" dispatchLatency="1" maxEventSize="0" memoryPartitionMode="none">
      <templateCategory><!-- _locID_text="templateCategory" _locComment = "" -->System Monitoring</templateCategory>
      <templateName><!-- _locID_text = "templateName" _locComment = "" -->Default Profiler Trace</templateName>
      <templateDescription><!-- _locID_text = "templateDescription" _locComment = "" -->The default profiler trace, converted using sp_SQLskills_ConvertTraceToExtendedEvents.</templateDescription>
      <event package="sqlserver" name="existing_connection">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_pid" />
         <action package="sqlserver" name="nt_username" />
         <action package="sqlserver" name="server_principal_name" />
         <action package="sqlserver" name="session_id" />
      </event>
      <event package="sqlserver" name="login">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_pid" />
         <action package="sqlserver" name="nt_username" />
         <action package="sqlserver" name="server_principal_name" />
         <action package="sqlserver" name="session_id" />
      </event>
      <event package="sqlserver" name="logout">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_pid" />
         <action package="sqlserver" name="nt_username" />
         <action package="sqlserver" name="server_principal_name" />
         <action package="sqlserver" name="session_id" />
      </event>
      <event package="sqlserver" name="rpc_completed">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_pid" />
         <action package="sqlserver" name="nt_username" />
         <action package="sqlserver" name="server_principal_name" />
         <action package="sqlserver" name="session_id" />
      </event>
      <event package="sqlserver" name="sql_batch_completed">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_pid" />
         <action package="sqlserver" name="nt_username" />
         <action package="sqlserver" name="server_principal_name" />
         <action package="sqlserver" name="session_id" />
      </event>
      <event package="sqlserver" name="sql_batch_starting">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_pid" />
         <action package="sqlserver" name="nt_username" />
         <action package="sqlserver" name="server_principal_name" />
         <action package="sqlserver" name="session_id" />
      </event>
      <target package="package0" name="ring_buffer" />
   </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Deprecated Feature Usage.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventconfig">
   <event_session name="Deprecated Feature Usage" maxMemory="4" eventRetentionMode="allowSingleEventLoss" trackCausality="true" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="none">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->Features</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Deprecated Feature Usage</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Tracks deprecated feature usage.</templateDescription>
      <event package="sqlserver" name="deprecation_announcement">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="nt_username" />
         <action package="sqlserver" name="session_id" />
         <action package="sqlserver" name="session_nt_username" />
         <action package="sqlserver" name="sql_text" />
         <action package="sqlserver" name="username" />
         <predicate>
            <and>
               <leaf>
                  <comparator name="greater_than_uint64" package="package0" />
                  <global name="database_id" package="sqlserver" />
                  <value>4</value>
               </leaf>
               <leaf>
                  <comparator name="not_equal_i_sql_unicode_string" package="sqlserver" />
                  <global name="client_app_name" package="sqlserver" />
                  <value><![CDATA[Microsoft SQL Server Management Studio - Transact-SQL IntelliSense]]></value>
               </leaf>
            </and>
         </predicate>
      </event>
      <event package="sqlserver" name="deprecation_final_support">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="nt_username" />
         <action package="sqlserver" name="session_id" />
         <action package="sqlserver" name="session_nt_username" />
         <action package="sqlserver" name="sql_text" />
         <action package="sqlserver" name="username" />
         <predicate>
            <and>
               <leaf>
                  <comparator name="greater_than_uint64" package="package0" />
                  <global name="database_id" package="sqlserver" />
                  <value>4</value>
               </leaf>
               <leaf>
                  <comparator name="not_equal_i_sql_unicode_string" package="sqlserver" />
                  <global name="client_app_name" package="sqlserver" />
                  <value><![CDATA[Microsoft SQL Server Management Studio - Transact-SQL IntelliSense]]></value>
               </leaf>
            </and>
         </predicate>
      </event>
      <target package="package0" name="event_file">
         <parameter name="filename" value="Deprecated Feature Usage" />
         <parameter name="max_rollover_files" value="25" />
      </target>
   </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Function Executions.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventconfig">
   <event_session name="Function Executions" maxMemory="4" eventRetentionMode="allowSingleEventLoss" trackCausality="false" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="none">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->Everyday Extended Events</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Function Executions</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Use this to count the number of times a function is executed from a single SELECT statement.</templateDescription>
      <event package="sqlserver" name="sp_statement_completed">
         <action package="sqlserver" name="sql_text" />
         <action package="sqlserver" name="tsql_stack" />
      </event>
      <event package="sqlserver" name="sql_statement_completed">
         <action package="sqlserver" name="sql_text" />
         <action package="sqlserver" name="tsql_stack" />
      </event>
      <target package="package0" name="ring_buffer" />
   </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Index Page Splits.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventconfig">
   <event_session name="Index Page Splits" maxMemory="4" eventRetentionMode="allowSingleEventLoss" trackCausality="false" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="none">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->Everyday Extended Events</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Index Page Splits</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->See index page splits in action.</templateDescription>
      <event package="sqlserver" name="page_split">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="database_name" />
         <action package="sqlserver" name="sql_text" />
      </event>
      <target package="package0" name="event_file">
         <parameter name="filename" value="page_splits" />
         <parameter name="max_rollover_files" value="0" />
      </target>
   </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Log File IO Detail Tracking.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventconfig">
  <event_session name="Log File IO Detail Tracking" maxMemory="4" eventRetentionMode="allowSingleEventLoss" trackCausality="false" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="none">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->System Monitoring</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Log File IO Detail Tracking</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Monitors the IO for database log files on a server by tracking asynchronous IO, database log flushes, file writes, spinlock backoffs of type LOGFLUSHQ and waits of type WRITELOG. This template collects data in two ways: raw data is collected to a ring buffer and spinlock backoff information is aggregated based on the input buffer (sql_text). The session is filtered for a single log file per database; if you have multiple log files you can modify the filter for the file_write_completed and file_written events to include more than just file_id = 2.</templateDescription>
    <event package="sqlos" name="async_io_completed" />
    <event package="sqlos" name="async_io_requested" />
    <event package="sqlos" name="spinlock_backoff">
      <action package="sqlserver" name="sql_text" />
      <predicate>
        <leaf>
          <comparator name="equal_uint64" package="package0"></comparator>
          <event name="spinlock_backoff" package="sqlos" field="type"></event>
          <value>85</value>
        </leaf>
      </predicate>
    </event>
    <event package="sqlos" name="wait_info">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="is_system" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <and>
          <leaf>
            <comparator name="equal_uint64" package="package0"></comparator>
            <event name="wait_info" package="sqlos" field="opcode"></event>
            <value>1</value>
          </leaf>
          <leaf>
            <comparator name="equal_uint64" package="package0"></comparator>
            <event name="wait_info" package="sqlos" field="wait_type"></event>
            <value>182</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <event package="sqlserver" name="databases_log_flush" />
    <event package="sqlserver" name="file_write_completed">
      <parameter name="collect_path" value="1" />
      <predicate>
        <leaf>
          <comparator name="equal_uint64" package="package0"></comparator>
          <event name="file_write_completed" package="sqlserver" field="file_id"></event>
          <value>2</value>
        </leaf>
      </predicate>
    </event>
    <event package="sqlserver" name="file_written">
      <parameter name="collect_path" value="1" />
      <predicate>
        <leaf>
          <comparator name="equal_uint64" package="package0"></comparator>
          <event name="file_written" package="sqlserver" field="file_id"></event>
          <value>2</value>
        </leaf>
      </predicate>
    </event>
    <target package="package0" name="histogram">
      <parameter name="filtering_event_name" value="sqlos.spinlock_backoff" />
      <parameter name="source" value="sqlserver.sql_text" />
    </target>
    <target package="package0" name="ring_buffer" />
  </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Log File IO Tracking.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventconfig">
  <event_session name="Log File IO Tracking" maxMemory="4" eventRetentionMode="allowSingleEventLoss" trackCausality="false" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="none">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->System Monitoring</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Log File IO Tracking</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Monitors the IO for database log files by tracking waits of type WRITELOG.</templateDescription>
    <event package="sqlos" name="wait_info">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <and>
          <leaf>
            <comparator name="equal_uint64" package="package0"></comparator>
            <event name="wait_info" package="sqlos" field="opcode"></event>
            <value>1</value>
          </leaf>
          <leaf>
            <comparator name="equal_uint64" package="package0"></comparator>
            <event name="wait_info" package="sqlos" field="wait_type"></event>
            <value>182</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <target package="package0" name="ring_buffer" />
  </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Login Tracker.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventsconfig">
   <event_session name="Login Tracker" maxMemory="4" eventRetentionMode="allowSingleEventLoss" trackCausality="false" dispatchLatency="1" maxEventSize="0" memoryPartitionMode="none">
       <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->System Monitoring</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Login Tracker</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Monitors logins to databases</templateDescription>
      <event package="sqlserver" name="sql_statement_starting">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="database_name" />
         <action package="sqlserver" name="server_instance_name" />
         <action package="sqlserver" name="server_principal_name" />
         <parameter name="collect_statement" value="0" />
         <predicate>
            <and>
               <and>
                  <and>
                     <leaf>
                        <comparator name="equal_boolean" package="package0" />
                        <global name="is_system" package="sqlserver" />
                        <value>false</value>
                     </leaf>
                     <not>
                        <leaf>
                           <comparator name="like_i_sql_unicode_string" package="sqlserver" />
                           <global name="client_app_name" package="sqlserver" />
                           <value><![CDATA[%dbatools%]]></value>
                        </leaf>
                     </not>
                  </and>
                  <not>
                     <leaf>
                        <comparator name="like_i_sql_unicode_string" package="sqlserver" />
                        <global name="client_app_name" package="sqlserver" />
                        <value><![CDATA[%management studio%]]></value>
                     </leaf>
                  </not>
               </and>
               <leaf>
                  <comparator name="not_equal_i_sql_unicode_string" package="sqlserver" />
                  <global name="database_name" package="sqlserver" />
                  <value><![CDATA[tempdb]]></value>
               </leaf>
            </and>
         </predicate>
      </event>
      <target package="package0" name="event_file">
         <parameter name="filename" value="Login Tracker" />
         <parameter name="max_file_size" value="10" />
      </target>
   </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Long Running Queries.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventsconfig">
   <event_session name="Long Running Queries" maxMemory="4" eventRetentionMode="allowSingleEventLoss" trackCausality="true" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="none">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->Query Execution</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Long Running Queries</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Tracks queries that take longer than 10 seconds to run.</templateDescription>
      <event package="sqlserver" name="rpc_completed">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="database_name" />
         <action package="sqlserver" name="nt_username" />
         <action package="sqlserver" name="query_hash" />
         <action package="sqlserver" name="server_principal_name" />
         <action package="sqlserver" name="session_id" />
         <action package="sqlserver" name="sql_text" />
         <predicate>
            <and>
               <and>
                  <leaf>
                     <comparator name="greater_than_uint64" package="package0" />
                     <global name="database_id" package="sqlserver" />
                     <value>4</value>
                  </leaf>
                  <leaf>
                     <comparator name="equal_boolean" package="package0" />
                     <global name="is_system" package="sqlserver" />
                     <value>false</value>
                  </leaf>
               </and>
               <leaf>
                  <comparator name="greater_than_equal_uint64" package="package0" />
                  <event name="rpc_completed" package="sqlserver" field="duration" />
                  <value>1000000</value>
               </leaf>
            </and>
         </predicate>
      </event>
      <event package="sqlserver" name="sql_batch_completed">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="query_hash" />
         <action package="sqlserver" name="session_id" />
         <parameter name="collect_batch_text" value="1" />
         <predicate>
            <and>
               <and>
                  <leaf>
                     <comparator name="greater_than_uint64" package="package0" />
                     <global name="database_id" package="sqlserver" />
                     <value>4</value>
                  </leaf>
                  <leaf>
                     <comparator name="equal_boolean" package="package0" />
                     <global name="is_system" package="sqlserver" />
                     <value>false</value>
                  </leaf>
               </and>
               <leaf>
                  <comparator name="greater_than_equal_uint64" package="package0" />
                  <event name="sql_batch_completed" package="sqlserver" field="duration" />
                  <value>500000</value>
               </leaf>
            </and>
         </predicate>
      </event>
      <target package="package0" name="event_file">
         <parameter name="filename" value="Long Running Queries" />
         <parameter name="max_file_size" value="100" />
      </target>
      <target package="package0" name="ring_buffer" />
   </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Overly Complex Queries.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventsconfig">
   <event_session name="Overly Complex Queries" maxMemory="100" eventRetentionMode="allowSingleEventLoss" trackCausality="false" dispatchLatency="5" maxEventSize="0" memoryPartitionMode="none">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->Performance Store</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Overly Complex Queries</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Captures queries that report errors 16 and 8623. This generally means that the query was unnecessarily complex.</templateDescription>
      <event package="sqlserver" name="error_reported">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="client_pid" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="database_name" />
         <action package="sqlserver" name="nt_username" />
         <action package="sqlserver" name="plan_handle" />
         <action package="sqlserver" name="session_id" />
         <action package="sqlserver" name="sql_text" />
         <action package="sqlserver" name="transaction_id" />
         <action package="sqlserver" name="tsql_frame" />
         <action package="sqlserver" name="username" />
         <predicate>
            <and>
               <leaf>
                  <comparator name="equal_int64" package="package0" />
                  <event name="error_reported" package="sqlserver" field="severity" />
                  <value>16</value>
               </leaf>
               <leaf>
                  <comparator name="equal_int64" package="package0" />
                  <event name="error_reported" package="sqlserver" field="error_number" />
                  <value>8623</value>
               </leaf>
            </and>
         </predicate>
      </event>
      <target package="package0" name="event_file">
         <parameter name="filename" value="Overly Complex Queries" />
         <parameter name="max_file_size" value="200" />
         <parameter name="max_rollover_files" value="5" />
         <parameter name="metadatafile" value=" Overly Complex Queries.xem" />
      </target>
   </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Profiler SP Counts.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventconfig">
  <event_session name="Profiler SP Counts" maxMemory="4" eventRetentionMode="allowSingleEventLoss" trackCausality="true" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="none">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->Profiler Equivalents</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Profiler SP Counts</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Matches the 'SP_Counts' template in Profiler. 	Captures stored procedure execution behavior over time. </templateDescription>
    <event package="sqlserver" name="module_start">
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="database_name" />
      <action package="sqlserver" name="server_instance_name" />
      <predicate>
        <and>
          <leaf>
            <comparator name="greater_than_uint64" package="package0"></comparator>
            <global name="database_id" package="sqlserver"></global>
            <value>4</value>
          </leaf>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
  </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Profiler Standard.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventconfig">
  <event_session name="Profiler Standard" maxMemory="8" eventRetentionMode="allowSingleEventLoss" trackCausality="true" dispatchLatency="1" maxEventSize="0" memoryPartitionMode="perCpu">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->Profiler Equivalents</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Profiler Standard</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Matches the 'Standard' template in Profiler. Generic starting point for creating a trace. Captures all stored procedures and Transact-SQL batches that are run. Use to monitor general database server activity.</templateDescription>    
    <event package="sqlserver" name="login">
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="session_id" />
      <parameter name="collect_options_text" value="1" />
    </event>
    <event package="sqlserver" name="logout">
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="session_id" />
    </event>
    <event package="sqlserver" name="existing_connection">
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="session_id" />
      <parameter name="collect_options_text" value="1" />
    </event>
    <event package="sqlserver" name="rpc_completed">
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <leaf>
          <comparator name="equal_boolean" package="package0"></comparator>
          <global name="is_system" package="sqlserver"></global>
          <value>false</value>
        </leaf>
      </predicate>
    </event>
    <event package="sqlserver" name="sql_batch_completed">
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <leaf>
          <comparator name="equal_boolean" package="package0"></comparator>
          <global name="is_system" package="sqlserver"></global>
          <value>false</value>
        </leaf>
      </predicate>
    </event>
    <event package="sqlserver" name="sql_batch_starting">
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <leaf>
          <comparator name="equal_boolean" package="package0"></comparator>
          <global name="is_system" package="sqlserver"></global>
          <value>false</value>
        </leaf>
      </predicate>
    </event>
    <event package="sqlserver" name="attention">
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <leaf>
          <comparator name="equal_boolean" package="package0"></comparator>
          <global name="is_system" package="sqlserver"></global>
          <value>false</value>
        </leaf>
      </predicate>
    </event>
  </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Profiler TSQL Duration.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventconfig">
  <event_session name="Profiler TSQL Duration" maxMemory="4" eventRetentionMode="allowSingleEventLoss" trackCausality="true" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="none">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->Profiler Equivalents</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Profiler TSQL Duration</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Matches the 'TSQL_Duration' template in Profiler. Captures all Transact-SQL statements submitted to SQL Server by clients and their execution time (in microseconds). Use to identify slow queries. </templateDescription>
    <event package="sqlserver" name="rpc_completed">
      <action package="sqlserver" name="session_id" />              
      <parameter name="collect_data_stream" value="1" />      
      <predicate>
        <and>
          <leaf>
            <comparator name="greater_than_uint64" package="package0"></comparator>
            <global name="database_id" package="sqlserver"></global>
            <value>4</value>
          </leaf>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <event package="sqlserver" name="sql_batch_completed">
      <action package="sqlserver" name="session_id" />
      <predicate>
        <and>
          <leaf>
            <comparator name="greater_than_uint64" package="package0"></comparator>
            <global name="database_id" package="sqlserver"></global>
            <value>4</value>
          </leaf>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
  </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Profiler TSQL Locks.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventconfig">
  <event_session name="Profiler TSQL Locks" maxMemory="4" eventRetentionMode="allowSingleEventLoss" trackCausality="true" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="none">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->Profiler Equivalents</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Profiler TSQL Locks</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Matches the 'TSQL_Locks' template in Profiler. Captures all of the Transact-SQL statements that are submitted to SQL Server by clients along with exceptional lock events. Use to troubleshoot deadlocks, lock time-out, and lock escalation events. </templateDescription>
    <event package="sqlserver" name="blocked_process_report">
      <action package="sqlserver" name="session_id" />      
    </event>
    <event package="sqlserver" name="lock_cancel">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="database_name" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="session_id" />
    </event>
    <event package="sqlserver" name="lock_deadlock">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="database_name" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="session_id" />
    </event>
    <event package="sqlserver" name="lock_deadlock_chain">
      <action package="sqlserver" name="database_name" />
      <action package="sqlserver" name="session_id" />
    </event>
    <event package="sqlserver" name="lock_escalation">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="database_name" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="session_id" />
    </event>
    <event package="sqlserver" name="lock_timeout_greater_than_0">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="database_name" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="session_id" />
    </event>
    <event package="sqlserver" name="sp_statement_completed">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="database_name" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="session_id" />
      <parameter name="collect_statement" value="1" />
      <predicate>
        <and>
          <leaf>
            <comparator name="greater_than_uint64" package="package0"></comparator>
            <global name="database_id" package="sqlserver"></global>
            <value>4</value>
          </leaf>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <event package="sqlserver" name="sp_statement_starting">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="database_name" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="session_id" />
      <parameter name="collect_statement" value="1" />
      <predicate>
        <and>
          <leaf>
            <comparator name="greater_than_uint64" package="package0"></comparator>
            <global name="database_id" package="sqlserver"></global>
            <value>4</value>
          </leaf>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <event package="sqlserver" name="sql_statement_completed">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="database_name" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <and>
          <leaf>
            <comparator name="greater_than_uint64" package="package0"></comparator>
            <global name="database_id" package="sqlserver"></global>
            <value>4</value>
          </leaf>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>   
    <event package="sqlserver" name="sql_statement_starting">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="query_plan_hash" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <and>
          <and>
            <leaf>
              <comparator name="divides_by_uint64" package="package0"></comparator>
              <global name="session_id" package="sqlserver"></global>
              <value>5</value>
            </leaf>
            <leaf>
              <comparator name="greater_than_uint64" package="package0"></comparator>
              <global name="database_id" package="sqlserver"></global>
              <value>4</value>
            </leaf>
          </and>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <event package="sqlserver" name="xml_deadlock_report">
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="session_id" />
    </event>
  </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Profiler TSQL Replay.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventconfig">
  <event_session name="Profiler TSQL Replay" maxMemory="4" eventRetentionMode="allowSingleEventLoss" trackCausality="true" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="none">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->Profiler Equivalents</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Profiler TSQL Replay</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Matches the 'TSQL_Replay' template in Profiler. Use to perform iterative tuning, such as benchmark testing.</templateDescription>
    <event package="sqlserver" name="attention">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="database_name" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="is_system" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="server_instance_name" />
      <action package="sqlserver" name="session_id" />      
    </event>
    <event package="sqlserver" name="cursor_close">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="database_name" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="is_system" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="server_instance_name" />
      <action package="sqlserver" name="session_id" />      
    </event>
    <event package="sqlserver" name="cursor_execute">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="database_name" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="is_system" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="server_instance_name" />
      <action package="sqlserver" name="session_id" />      
    </event>
    <event package="sqlserver" name="cursor_open">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="database_name" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="is_system" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="server_instance_name" />
      <action package="sqlserver" name="session_id" />      
    </event>
    <event package="sqlserver" name="cursor_prepare">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="database_name" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="is_system" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="server_instance_name" />
      <action package="sqlserver" name="session_id" />      
    </event>
    <event package="sqlserver" name="cursor_unprepare">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="database_name" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="is_system" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="server_instance_name" />
      <action package="sqlserver" name="session_id" />      
    </event>
    <event package="sqlserver" name="exec_prepared_sql">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="database_name" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="is_system" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="server_instance_name" />
      <action package="sqlserver" name="session_id" />      
    </event>
    <event package="sqlserver" name="existing_connection">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="database_name" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="is_system" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="server_instance_name" />
      <action package="sqlserver" name="session_id" />      
    </event>
    <event package="sqlserver" name="login">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="database_name" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="is_system" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="server_instance_name" />
      <action package="sqlserver" name="session_id" />      
      <parameter name="collect_options_text" value="1" />
    </event>
    <event package="sqlserver" name="logout">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="database_name" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="is_system" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="server_instance_name" />
      <action package="sqlserver" name="session_id" />      
    </event>
    <event package="sqlserver" name="prepare_sql">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="database_name" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="is_system" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="server_instance_name" />
      <action package="sqlserver" name="session_id" />      
    </event>    
    <event package="sqlserver" name="rpc_completed">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="database_name" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="is_system" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="server_instance_name" />
      <action package="sqlserver" name="session_id" />      
    </event>
    <event package="sqlserver" name="rpc_starting">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="database_name" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="is_system" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="server_instance_name" />
      <action package="sqlserver" name="session_id" />      
    </event>
    <event package="sqlserver" name="sql_batch_completed">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="database_name" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="is_system" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="server_instance_name" />
      <action package="sqlserver" name="session_id" />
    </event>
    <event package="sqlserver" name="sql_batch_starting">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="client_pid" />
      <action package="sqlserver" name="client_hostname" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="database_name" />
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="is_system" />
      <action package="sqlserver" name="nt_username" />
      <action package="sqlserver" name="request_id" />
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="server_instance_name" />
      <action package="sqlserver" name="session_id" />
    </event>
  </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Profiler TSQL SPs.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventconfig">
  <event_session name="Profiler TSQL SPs" maxMemory="4" eventRetentionMode="allowSingleEventLoss" trackCausality="true" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="none">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->Profiler Equivalents</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Profiler TSQL SPs</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Matches the 'TSQL_SPs' template in Profiler. Captures detailed information about all executing stored procedures. Use to analyze the component steps of stored procedures. Add the sql_statement_recompile event if you suspect that procedures are being recompiled.</templateDescription>
    <event package="sqlserver" name="existing_connection">
      <action package="sqlserver" name="database_id" />            
      <action package="sqlserver" name="database_name" />            
      <action package="sqlserver" name="session_id" />            
      <action package="sqlserver" name="server_instance_name" />            
    </event>
    <event package="sqlserver" name="login">
      <action package="sqlserver" name="database_id" />            
      <action package="sqlserver" name="database_name" />            
      <action package="sqlserver" name="session_id" />            
      <action package="sqlserver" name="server_instance_name" />            
      <parameter name="collect_options_text" value="1" />
    </event>
    <event package="sqlserver" name="logout">
      <action package="sqlserver" name="database_id" />            
      <action package="sqlserver" name="database_name" />            
      <action package="sqlserver" name="session_id" />            
      <action package="sqlserver" name="server_instance_name" />            
    </event>
    <event package="sqlserver" name="module_end">
      <action package="sqlserver" name="database_id" />            
      <action package="sqlserver" name="database_name" />            
      <action package="sqlserver" name="session_id" />            
      <action package="sqlserver" name="server_instance_name" />                  
    </event>
    <event package="sqlserver" name="module_start">
      <action package="sqlserver" name="database_id" />            
      <action package="sqlserver" name="database_name" />            
      <action package="sqlserver" name="session_id" />            
      <action package="sqlserver" name="server_instance_name" />            
    </event>       
    <event package="sqlserver" name="rpc_starting">
      <action package="sqlserver" name="database_id" />            
      <action package="sqlserver" name="database_name" />            
      <action package="sqlserver" name="session_id" />            
      <action package="sqlserver" name="server_instance_name" />            
      <predicate>
        <and>
          <leaf>
            <comparator name="greater_than_uint64" package="package0"></comparator>
            <global name="database_id" package="sqlserver"></global>
            <value>4</value>
          </leaf>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <event package="sqlserver" name="sp_statement_starting">
      <action package="sqlserver" name="database_id" />            
      <action package="sqlserver" name="database_name" />            
      <action package="sqlserver" name="session_id" />            
      <action package="sqlserver" name="server_instance_name" />            
      <predicate>
        <and>
          <leaf>
            <comparator name="greater_than_uint64" package="package0"></comparator>
            <global name="database_id" package="sqlserver"></global>
            <value>4</value>
          </leaf>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>       
    <event package="sqlserver" name="sql_batch_starting">
      <action package="sqlserver" name="database_id" />            
      <action package="sqlserver" name="database_name" />            
      <action package="sqlserver" name="session_id" />            
      <action package="sqlserver" name="server_instance_name" />            
      <predicate>
        <and>
          <leaf>
            <comparator name="greater_than_uint64" package="package0"></comparator>
            <global name="database_id" package="sqlserver"></global>
            <value>4</value>
          </leaf>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>        
  </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Profiler TSQL.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventconfig">
  <event_session name="Profiler TSQL" maxMemory="8" eventRetentionMode="allowSingleEventLoss" trackCausality="true" dispatchLatency="5" maxEventSize="0" memoryPartitionMode="perCpu">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->Profiler Equivalents</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Profiler TSQL</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Matches the 'TSQL' template in Profiler. Captures all Transact-SQL statements that are submitted to SQL Server by clients and the time issued. Use to debug client applications.</templateDescription>
    <event package="sqlserver" name="login">
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="session_id" />
      <parameter name="collect_options_text" value="1" />
    </event>
    <event package="sqlserver" name="logout">
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="session_id" />
    </event>
    <event package="sqlserver" name="existing_connection">
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="session_id" />
    </event>
    <event package="sqlserver" name="rpc_starting">
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <leaf>
          <comparator name="equal_boolean" package="package0"></comparator>
          <global name="is_system" package="sqlserver"></global>
          <value>false</value>
        </leaf>
      </predicate>
    </event>
    <event package="sqlserver" name="sql_batch_starting">
      <action package="package0" name="event_sequence" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <leaf>
          <comparator name="equal_boolean" package="package0"></comparator>
          <global name="is_system" package="sqlserver"></global>
          <value>false</value>
        </leaf>
      </predicate>
    </event>
  </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Profiler Tuning.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventconfig">
  <event_session name="Profiler Tuning" maxMemory="4" eventRetentionMode="allowSingleEventLoss" trackCausality="true" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="none">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->Profiler Equivalents</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Profiler Tuning</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Matches the 'Tuning' template in Profiler. Captures information about stored procedures and Transact-SQL batch execution.</templateDescription>
    <event package="sqlserver" name="rpc_completed">
      <action package="sqlserver" name="database_id" />            
      <action package="sqlserver" name="database_name" />            
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="session_id" />                    
      <parameter name="collect_data_stream" value="1" />      
      <predicate>
        <and>
          <leaf>
            <comparator name="greater_than_uint64" package="package0"></comparator>
            <global name="database_id" package="sqlserver"></global>
            <value>4</value>
          </leaf>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <event package="sqlserver" name="sp_statement_completed">
      <action package="sqlserver" name="database_id" />            
      <action package="sqlserver" name="database_name" />            
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="session_id" />                    
      <predicate>
        <and>
          <leaf>
            <comparator name="greater_than_uint64" package="package0"></comparator>
            <global name="database_id" package="sqlserver"></global>
            <value>4</value>
          </leaf>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <event package="sqlserver" name="sql_batch_completed">
      <action package="sqlserver" name="database_id" />            
      <action package="sqlserver" name="database_name" />            
      <action package="sqlserver" name="server_principal_name" />
      <action package="sqlserver" name="session_id" />                    
      <predicate>
        <and>
          <leaf>
            <comparator name="greater_than_uint64" package="package0"></comparator>
            <global name="database_id" package="sqlserver"></global>
            <value>4</value>
          </leaf>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
  </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Queries and Resources.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventconfig">
   <event_session name="Queries and Resources" maxMemory="4" eventRetentionMode="allowSingleEventLoss" trackCausality="true" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="none">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->Everyday Extended Events</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Queries and Resources</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Captures query activity.</templateDescription>
      <event package="sqlserver" name="rpc_completed">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="query_hash" />
         <action package="sqlserver" name="session_id" />
         <parameter name="collect_statement" value="1" />
         <predicate>
            <and>
               <leaf>
                  <comparator name="greater_than_uint64" package="package0" />
                  <global name="database_id" package="sqlserver" />
                  <value>4</value>
               </leaf>
               <leaf>
                  <comparator name="equal_boolean" package="package0" />
                  <global name="is_system" package="sqlserver" />
                  <value>false</value>
               </leaf>
            </and>
         </predicate>
      </event>
      <event package="sqlserver" name="sp_statement_completed">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="query_hash" />
         <action package="sqlserver" name="query_plan_hash" />
         <action package="sqlserver" name="session_id" />
         <parameter name="collect_object_name" value="1" />
         <predicate>
            <and>
               <leaf>
                  <comparator name="greater_than_uint64" package="package0" />
                  <global name="database_id" package="sqlserver" />
                  <value>4</value>
               </leaf>
               <leaf>
                  <comparator name="equal_boolean" package="package0" />
                  <global name="is_system" package="sqlserver" />
                  <value>false</value>
               </leaf>
            </and>
         </predicate>
      </event>
      <event package="sqlserver" name="sql_batch_completed">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="query_hash" />
         <action package="sqlserver" name="session_id" />
         <predicate>
            <and>
               <leaf>
                  <comparator name="greater_than_uint64" package="package0" />
                  <global name="database_id" package="sqlserver" />
                  <value>4</value>
               </leaf>
               <leaf>
                  <comparator name="equal_boolean" package="package0" />
                  <global name="is_system" package="sqlserver" />
                  <value>false</value>
               </leaf>
            </and>
         </predicate>
      </event>
      <event package="sqlserver" name="sql_statement_completed">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="query_hash" />
         <action package="sqlserver" name="query_plan_hash" />
         <action package="sqlserver" name="session_id" />
         <predicate>
            <and>
               <leaf>
                  <comparator name="greater_than_uint64" package="package0" />
                  <global name="database_id" package="sqlserver" />
                  <value>4</value>
               </leaf>
               <leaf>
                  <comparator name="equal_boolean" package="package0" />
                  <global name="is_system" package="sqlserver" />
                  <value>false</value>
               </leaf>
            </and>
         </predicate>
      </event>
      <target package="package0" name="event_file">
         <parameter name="filename" value="capture_queries_and_resources" />
         <parameter name="max_rollover_files" value="0" />
      </target>
      <target package="package0" name="ring_buffer">
         <parameter name="max_memory" value="1048576" />
      </target>
   </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Query Batch Detail Sampling.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventconfig">
  <event_session name="Query Batch Detail Sampling" maxMemory="4" eventRetentionMode="allowSingleEventLoss" trackCausality="true" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="none">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->Query Execution</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Query Batch Detail Sampling</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Collects batch and RPC level statements as well as error information. You can use this template to understand the flow of queries that are executing on your system and track errors back to the queries that caused them. Events are only collected from 20% of the active sessions on the server at any given time. You can change the sampling rate by modifying the filter for the event session.</templateDescription>
    <event package="sqlserver" name="error_reported">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <and>
          <and>
            <leaf>
              <comparator name="divides_by_uint64" package="package0"></comparator>
              <global name="session_id" package="sqlserver"></global>
              <value>5</value>
            </leaf>
            <leaf>
              <comparator name="greater_than_uint64" package="package0"></comparator>
              <global name="database_id" package="sqlserver"></global>
              <value>4</value>
            </leaf>
          </and>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <event package="sqlserver" name="rpc_completed">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <and>
          <and>
            <leaf>
              <comparator name="divides_by_uint64" package="package0"></comparator>
              <global name="session_id" package="sqlserver"></global>
              <value>5</value>
            </leaf>
            <leaf>
              <comparator name="greater_than_uint64" package="package0"></comparator>
              <global name="database_id" package="sqlserver"></global>
              <value>4</value>
            </leaf>
          </and>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <event package="sqlserver" name="sql_batch_completed">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <and>
          <and>
            <leaf>
              <comparator name="divides_by_uint64" package="package0"></comparator>
              <global name="session_id" package="sqlserver"></global>
              <value>5</value>
            </leaf>
            <leaf>
              <comparator name="greater_than_uint64" package="package0"></comparator>
              <global name="database_id" package="sqlserver"></global>
              <value>4</value>
            </leaf>
          </and>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <target package="package0" name="ring_buffer" />
  </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Query Batch Sampling.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventconfig">
  <event_session name="Query Batch Sampling" maxMemory="4" eventRetentionMode="allowSingleEventLoss" trackCausality="true" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="none">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->Query Execution</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Query Batch Sampling</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Collects batch and RPC level statements as well as error information. You can use this template to understand the flow of queries that are executing on your system and track errors back to the queries that caused them. Events are only collected from 20% of the active sessions on the server at any given time. You can change the sampling rate by modifying the filter for the event session.</templateDescription>
    <event package="sqlserver" name="error_reported">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <and>
            <leaf>
              <comparator name="divides_by_uint64" package="package0"></comparator>
              <global name="session_id" package="sqlserver"></global>
              <value>5</value>
            </leaf>
            <leaf>
              <comparator name="greater_than_uint64" package="package0"></comparator>
              <global name="database_id" package="sqlserver"></global>
              <value>4</value>
            </leaf>
        </and>
      </predicate>
    </event>
    <event package="sqlserver" name="rpc_completed">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <and>
			<leaf>
			  <comparator name="divides_by_uint64" package="package0"></comparator>
			  <global name="session_id" package="sqlserver"></global>
			  <value>5</value>
			</leaf>
			<leaf>
			  <comparator name="greater_than_uint64" package="package0"></comparator>
			  <global name="database_id" package="sqlserver"></global>
			  <value>4</value>
			</leaf>
		</and>
      </predicate>
    </event>
    <event package="sqlserver" name="sql_batch_completed">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <and>
			 <leaf>
			  <comparator name="divides_by_uint64" package="package0"></comparator>
			  <global name="session_id" package="sqlserver"></global>
			  <value>5</value>
			</leaf>
			<leaf>
			  <comparator name="greater_than_uint64" package="package0"></comparator>
			  <global name="database_id" package="sqlserver"></global>
			  <value>4</value>
			</leaf>
        </and>
      </predicate>
    </event>
    <target package="package0" name="ring_buffer" />
  </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Query Batch Tracking.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventconfig">
  <event_session name="Query Batch Tracking" maxMemory="4" eventRetentionMode="allowSingleEventLoss" trackCausality="true" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="none">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->Query Execution</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Query Batch Tracking</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Collects batch and RPC level statements as well as error information. You can use this template to understand the flow of queries that are executing on your system and track errors back to the queries that caused them. All batch events are collected in this session so collection size may be very large. To reduce the collection size, consider using the Query Batch Sampling template, which already includes a filter.</templateDescription>    
    <event package="sqlserver" name="error_reported">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <and>
          <leaf>
            <comparator name="greater_than_uint64" package="package0"></comparator>
            <global name="database_id" package="sqlserver"></global>
            <value>4</value>
          </leaf>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <event package="sqlserver" name="rpc_completed">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <and>
          <leaf>
            <comparator name="greater_than_uint64" package="package0"></comparator>
            <global name="database_id" package="sqlserver"></global>
            <value>4</value>
          </leaf>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <event package="sqlserver" name="sql_batch_completed">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <and>
          <leaf>
            <comparator name="greater_than_uint64" package="package0"></comparator>
            <global name="database_id" package="sqlserver"></global>
            <value>4</value>
          </leaf>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <target package="package0" name="ring_buffer" />
  </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Query Detail Sampling.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventconfig">
  <event_session name="Query Detail Sampling" maxMemory="4" eventRetentionMode="allowSingleEventLoss" trackCausality="true" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="none">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->Query Execution</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Query Detail Sampling</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Collects detailed statement and error information. You can use this template to track each statement that has executed on your system as a result of query batches or stored procedures and track errors back to the specific statement that caused them. This template also collects the query hash and query plan hash for every statement it tracks. Events are only collected from 20% of the active sessions on the server at any given time. You can change the sampling rate by modifying the filter for the event session.</templateDescription>
    <event package="sqlserver" name="error_reported">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <and>
          <and>
            <leaf>
              <comparator name="divides_by_uint64" package="package0"></comparator>
              <global name="session_id" package="sqlserver"></global>
              <value>5</value>
            </leaf>
            <leaf>
              <comparator name="greater_than_uint64" package="package0"></comparator>
              <global name="database_id" package="sqlserver"></global>
              <value>4</value>
            </leaf>
          </and>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <event package="sqlserver" name="module_end">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="session_id" />
      <parameter name="collect_statement" value="1" />
      <predicate>
        <and>
          <and>
            <leaf>
              <comparator name="divides_by_uint64" package="package0"></comparator>
              <global name="session_id" package="sqlserver"></global>
              <value>5</value>
            </leaf>
            <leaf>
              <comparator name="greater_than_uint64" package="package0"></comparator>
              <global name="database_id" package="sqlserver"></global>
              <value>4</value>
            </leaf>
          </and>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <event package="sqlserver" name="rpc_completed">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <and>
          <and>
            <leaf>
              <comparator name="divides_by_uint64" package="package0"></comparator>
              <global name="session_id" package="sqlserver"></global>
              <value>5</value>
            </leaf>
            <leaf>
              <comparator name="greater_than_uint64" package="package0"></comparator>
              <global name="database_id" package="sqlserver"></global>
              <value>4</value>
            </leaf>
          </and>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <event package="sqlserver" name="sp_statement_completed">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="query_plan_hash" />
      <action package="sqlserver" name="session_id" />
      <parameter name="collect_object_name" value="1" />
      <predicate>
        <and>
          <and>
            <leaf>
              <comparator name="divides_by_uint64" package="package0"></comparator>
              <global name="session_id" package="sqlserver"></global>
              <value>5</value>
            </leaf>
            <leaf>
              <comparator name="greater_than_uint64" package="package0"></comparator>
              <global name="database_id" package="sqlserver"></global>
              <value>4</value>
            </leaf>
          </and>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <event package="sqlserver" name="sql_batch_completed">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <and>
          <and>
            <leaf>
              <comparator name="divides_by_uint64" package="package0"></comparator>
              <global name="session_id" package="sqlserver"></global>
              <value>5</value>
            </leaf>
            <leaf>
              <comparator name="greater_than_uint64" package="package0"></comparator>
              <global name="database_id" package="sqlserver"></global>
              <value>4</value>
            </leaf>
          </and>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <event package="sqlserver" name="sql_statement_completed">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="query_plan_hash" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <and>
          <and>
            <leaf>
              <comparator name="divides_by_uint64" package="package0"></comparator>
              <global name="session_id" package="sqlserver"></global>
              <value>5</value>
            </leaf>
            <leaf>
              <comparator name="greater_than_uint64" package="package0"></comparator>
              <global name="database_id" package="sqlserver"></global>
              <value>4</value>
            </leaf>
          </and>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <target package="package0" name="ring_buffer" />
  </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Query Detail Tracking.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventconfig">
  <event_session name="Query Detail Tracking" maxMemory="4" eventRetentionMode="allowSingleEventLoss" trackCausality="true" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="none">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->Query Execution</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Query Detail Tracking</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Collects detailed statement and error information. You can use this template to track each statement has executed on your system as a result of query batches or stored procedures and then track errors back to the specific statement that caused them. This template also collects the query hash and query plan hash for every statement it tracks. All statement events are collected in this session so collection size may be very large. To reduce the collection size consider using the Query Detail Sampling template, which already includes a filter.</templateDescription>
    <event package="sqlserver" name="error_reported">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <and>
          <leaf>
            <comparator name="greater_than_uint64" package="package0"></comparator>
            <global name="database_id" package="sqlserver"></global>
            <value>4</value>
          </leaf>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <event package="sqlserver" name="module_end">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="session_id" />
      <parameter name="collect_statement" value="1" />
      <predicate>
        <and>
          <leaf>
            <comparator name="greater_than_uint64" package="package0"></comparator>
            <global name="database_id" package="sqlserver"></global>
            <value>4</value>
          </leaf>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <event package="sqlserver" name="rpc_completed">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <and>
          <leaf>
            <comparator name="greater_than_uint64" package="package0"></comparator>
            <global name="database_id" package="sqlserver"></global>
            <value>4</value>
          </leaf>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <event package="sqlserver" name="sp_statement_completed">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="query_plan_hash" />
      <action package="sqlserver" name="session_id" />
      <parameter name="collect_object_name" value="1" />
      <predicate>
        <and>
          <leaf>
            <comparator name="greater_than_uint64" package="package0"></comparator>
            <global name="database_id" package="sqlserver"></global>
            <value>4</value>
          </leaf>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <event package="sqlserver" name="sql_batch_completed">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <and>
          <leaf>
            <comparator name="greater_than_uint64" package="package0"></comparator>
            <global name="database_id" package="sqlserver"></global>
            <value>4</value>
          </leaf>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <event package="sqlserver" name="sql_statement_completed">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="query_plan_hash" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <and>
          <leaf>
            <comparator name="greater_than_uint64" package="package0"></comparator>
            <global name="database_id" package="sqlserver"></global>
            <value>4</value>
          </leaf>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <target package="package0" name="ring_buffer" />
  </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Query Timeouts.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventconfig">
   <event_session name="Query Timeouts" maxMemory="4" eventRetentionMode="allowSingleEventLoss" trackCausality="true" dispatchLatency="5" maxEventSize="0" memoryPartitionMode="none">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->Query Execution</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Query Timeouts</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Tracks query timeouts.</templateDescription>
      <event package="sqlserver" name="sql_statement_completed">
         <action package="sqlserver" name="query_hash" />
         <action package="sqlserver" name="session_id" />
         <action package="sqlserver" name="tsql_stack" />
      </event>
      <event package="sqlserver" name="sql_statement_starting">
         <action package="sqlserver" name="query_hash" />
         <action package="sqlserver" name="session_id" />
         <action package="sqlserver" name="tsql_stack" />
      </event>
      <target package="package0" name="pair_matching">
         <parameter name="begin_event" value="sqlserver.sql_statement_starting" />
         <parameter name="begin_matching_actions" value="sqlserver.session_id, sqlserver.tsql_stack" />
         <parameter name="end_event" value="sqlserver.sql_statement_completed" />
         <parameter name="end_matching_actions" value="sqlserver.session_id, sqlserver.tsql_stack" />
         <parameter name="respond_to_memory_pressure" value="0" />
      </target>
   </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Query Wait Statistics Detail.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventconfig">
  <event_session name="Query Wait Statistics Detail" maxMemory="4" eventRetentionMode="allowSingleEventLoss" trackCausality="true" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="none">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->Query Execution</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Query Wait Statistics Detail</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Tracks internal and external wait statistics for individual query statements, batches and RPCs. This template also collects the query hash and query plan hash for every statement it tracks. Events are only collected from 20% of the active sessions on the server at any given time. You can change the sampling rate by modifying the filter for the event session.</templateDescription>
    <event package="sqlos" name="wait_info">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="query_plan_hash" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <and>
          <and>
            <leaf>
              <comparator name="divides_by_uint64" package="package0"></comparator>
              <global name="session_id" package="sqlserver"></global>
              <value>5</value>
            </leaf>
            <leaf>
              <comparator name="greater_than_uint64" package="package0"></comparator>
              <global name="database_id" package="sqlserver"></global>
              <value>4</value>
            </leaf>
          </and>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <event package="sqlos" name="wait_info_external">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="query_plan_hash" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <and>
          <and>
            <leaf>
              <comparator name="divides_by_uint64" package="package0"></comparator>
              <global name="session_id" package="sqlserver"></global>
              <value>5</value>
            </leaf>
            <leaf>
              <comparator name="greater_than_uint64" package="package0"></comparator>
              <global name="database_id" package="sqlserver"></global>
              <value>4</value>
            </leaf>
          </and>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <event package="sqlserver" name="rpc_completed">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <and>
          <and>
            <leaf>
              <comparator name="divides_by_uint64" package="package0"></comparator>
              <global name="session_id" package="sqlserver"></global>
              <value>5</value>
            </leaf>
            <leaf>
              <comparator name="greater_than_uint64" package="package0"></comparator>
              <global name="database_id" package="sqlserver"></global>
              <value>4</value>
            </leaf>
          </and>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <event package="sqlserver" name="rpc_starting">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <and>
          <and>
            <leaf>
              <comparator name="divides_by_uint64" package="package0"></comparator>
              <global name="session_id" package="sqlserver"></global>
              <value>5</value>
            </leaf>
            <leaf>
              <comparator name="greater_than_uint64" package="package0"></comparator>
              <global name="database_id" package="sqlserver"></global>
              <value>4</value>
            </leaf>
          </and>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <event package="sqlserver" name="sp_statement_completed">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="query_plan_hash" />
      <action package="sqlserver" name="session_id" />
      <parameter name="collect_object_name" value="1" />
      <parameter name="collect_statement" value="1" />
      <predicate>
        <and>
          <and>
            <leaf>
              <comparator name="divides_by_uint64" package="package0"></comparator>
              <global name="session_id" package="sqlserver"></global>
              <value>5</value>
            </leaf>
            <leaf>
              <comparator name="greater_than_uint64" package="package0"></comparator>
              <global name="database_id" package="sqlserver"></global>
              <value>4</value>
            </leaf>
          </and>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <event package="sqlserver" name="sp_statement_starting">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="query_plan_hash" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <and>
          <and>
            <leaf>
              <comparator name="divides_by_uint64" package="package0"></comparator>
              <global name="session_id" package="sqlserver"></global>
              <value>5</value>
            </leaf>
            <leaf>
              <comparator name="greater_than_uint64" package="package0"></comparator>
              <global name="database_id" package="sqlserver"></global>
              <value>4</value>
            </leaf>
          </and>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <event package="sqlserver" name="sql_batch_completed">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <and>
          <and>
            <leaf>
              <comparator name="divides_by_uint64" package="package0"></comparator>
              <global name="session_id" package="sqlserver"></global>
              <value>5</value>
            </leaf>
            <leaf>
              <comparator name="greater_than_uint64" package="package0"></comparator>
              <global name="database_id" package="sqlserver"></global>
              <value>4</value>
            </leaf>
          </and>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <event package="sqlserver" name="sql_batch_starting">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <and>
          <and>
            <leaf>
              <comparator name="divides_by_uint64" package="package0"></comparator>
              <global name="session_id" package="sqlserver"></global>
              <value>5</value>
            </leaf>
            <leaf>
              <comparator name="greater_than_uint64" package="package0"></comparator>
              <global name="database_id" package="sqlserver"></global>
              <value>4</value>
            </leaf>
          </and>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <event package="sqlserver" name="sql_statement_completed">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="query_plan_hash" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <and>
          <and>
            <leaf>
              <comparator name="divides_by_uint64" package="package0"></comparator>
              <global name="session_id" package="sqlserver"></global>
              <value>5</value>
            </leaf>
            <leaf>
              <comparator name="greater_than_uint64" package="package0"></comparator>
              <global name="database_id" package="sqlserver"></global>
              <value>4</value>
            </leaf>
          </and>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <event package="sqlserver" name="sql_statement_starting">
      <action package="sqlserver" name="client_app_name" />
      <action package="sqlserver" name="database_id" />
      <action package="sqlserver" name="query_hash" />
      <action package="sqlserver" name="query_plan_hash" />
      <action package="sqlserver" name="session_id" />
      <predicate>
        <and>
          <and>
            <leaf>
              <comparator name="divides_by_uint64" package="package0"></comparator>
              <global name="session_id" package="sqlserver"></global>
              <value>5</value>
            </leaf>
            <leaf>
              <comparator name="greater_than_uint64" package="package0"></comparator>
              <global name="database_id" package="sqlserver"></global>
              <value>4</value>
            </leaf>
          </and>
          <leaf>
            <comparator name="equal_boolean" package="package0"></comparator>
            <global name="is_system" package="sqlserver"></global>
            <value>false</value>
          </leaf>
        </and>
      </predicate>
    </event>
    <target package="package0" name="event_file">
      <parameter name="filename" value="query_wait_analysis" />
    </target>
  </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Query Wait Statistics.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventconfig">
   <event_session name="Query Wait Statistics" maxMemory="4" eventRetentionMode="allowSingleEventLoss" trackCausality="false" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="none">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->Everyday Extended Events</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Query Wait Statistics</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Tracks internal and external wait statistics.</templateDescription>
      <event package="sqlos" name="wait_completed">
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="database_name" />
         <action package="sqlserver" name="is_system" />
         <action package="sqlserver" name="session_id" />
         <parameter name="collect_wait_resource" value="1" />
         <predicate>
            <and>
               <leaf>
                  <comparator name="greater_than_uint64" package="package0" />
                  <global name="database_id" package="sqlserver" />
                  <value>4</value>
               </leaf>
               <leaf>
                  <comparator name="not_equal_boolean" package="package0" />
                  <global name="is_system" package="sqlserver" />
                  <value>false</value>
               </leaf>
            </and>
         </predicate>
      </event>
      <event package="sqlos" name="wait_info">
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="database_name" />
         <action package="sqlserver" name="is_system" />
         <action package="sqlserver" name="session_id" />
         <predicate>
            <and>
               <leaf>
                  <comparator name="greater_than_uint64" package="package0" />
                  <global name="database_id" package="sqlserver" />
                  <value>4</value>
               </leaf>
               <leaf>
                  <comparator name="not_equal_boolean" package="package0" />
                  <global name="is_system" package="sqlserver" />
                  <value>false</value>
               </leaf>
            </and>
         </predicate>
      </event>
      <event package="sqlos" name="wait_info_external">
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="database_name" />
         <action package="sqlserver" name="is_system" />
         <action package="sqlserver" name="session_id" />
         <predicate>
            <and>
               <leaf>
                  <comparator name="greater_than_uint64" package="package0" />
                  <global name="database_id" package="sqlserver" />
                  <value>4</value>
               </leaf>
               <leaf>
                  <comparator name="not_equal_boolean" package="package0" />
                  <global name="is_system" package="sqlserver" />
                  <value>false</value>
               </leaf>
            </and>
         </predicate>
      </event>
      <target package="package0" name="ring_buffer">
         <parameter name="max_memory" value="102400" />
      </target>
   </event_session>
</event_sessions>
tools\dbatools\bin\XEtemplates\Stored Procedure Parameters.xml
<event_sessions xmlns="http://schemas.microsoft.com/sqlserver/2008/07/extendedeventconfig">
   <event_session name="Stored Procedure Parameters" maxMemory="4" eventRetentionMode="allowSingleEventLoss" trackCausality="false" dispatchLatency="30" maxEventSize="0" memoryPartitionMode="none">
    <templateCategory>
      <!-- _locID_text="templateCategory" _locComment = "" -->Everyday Extended Events</templateCategory>
    <templateName>
      <!-- _locID_text = "templateName" _locComment = "" -->Stored Procedure Parameters</templateName>
    <templateDescription>
      <!-- _locID_text = "templateDescription" _locComment = "" -->Captures what parameters were used when a stored procedure was executed.</templateDescription>
      <event package="sqlserver" name="rpc_completed">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="database_name" />
         <action package="sqlserver" name="query_hash" />
         <action package="sqlserver" name="query_plan_hash" />
         <action package="sqlserver" name="sql_text" />
         <parameter name="collect_output_parameters" value="1" />
         <parameter name="collect_statement" value="1" />
         <predicate>
            <leaf>
               <comparator name="greater_than_uint64" package="package0" />
               <global name="database_id" package="sqlserver" />
               <value>4</value>
            </leaf>
         </predicate>
      </event>
      <event package="sqlserver" name="sql_batch_completed">
         <action package="sqlserver" name="client_app_name" />
         <action package="sqlserver" name="client_hostname" />
         <action package="sqlserver" name="database_id" />
         <action package="sqlserver" name="database_name" />
         <action package="sqlserver" name="query_hash" />
         <action package="sqlserver" name="query_plan_hash" />
         <action package="sqlserver" name="sql_text" />
         <predicate>
            <leaf>
               <comparator name="greater_than_uint64" package="package0" />
               <global name="database_id" package="sqlserver" />
               <value>4</value>
            </leaf>
         </predicate>
      </event>
      <target package="package0" name="event_file">
         <parameter name="filename" value="capture_parameters" />
         <parameter name="max_rollover_files" value="0" />
      </target>
   </event_session>
</event_sessions>
tools\dbatools\codecov.yml
 
tools\dbatools\contributing.md
Here, we'll help you understand how to contribute to the project, and talk about fun stuff like styles and guidelines.
# Contributing
Let's sum this up saying that we'd love your help. We're slowly getting ready for the 1.0 release albeit a bit too slowly!

There are several ways to contribute:
 - Create new commands (PowerShell/T-SQL knowledge required)
 - Report bugs (everyone can do it)
 - Tests (Pester knowledge required)
 - Documentation: functions, website, this guide, everything can be improved (everyone can)
 - Code review (PowerShell/T-SQL knowledge required)

If you wanna help reaching 1.0
   - Standardize param names (@wsmelton)
   - Create tests for existing functions (@cl and @niphlod)
   - Review existing function documentation (@alevyinroc or @gbargsley)
   - Prepare for 1.0 with "code style" (Bill of Health, more on that later)

We strive to make any area doubtless. In any case, the Slack channel is pretty active and every area has one (or more) volunteers to refer to in case of doubts.

## Documentation
Documentation is really the area we welcome any help possible. The documentation refers to both the CBH (Comment Based Help) and the [website documentation](https://dbatools.io/functions). The CBH documentation is included with each command and is the content you see when you run `Get-Help Function-Name`. If any of that content is not clear enough or if the examples in the functions are not working, you should say so (e.g. raise an issue on GitHub or comment in Slack). Even if you are a casual user or a PowerShell newbie, we need your angle to make it as straight forward and clear as possible.

## Contribute New Commands
Start out reviewing the [list of functions on the website](https://dbatools.io/functions/), or pulling the list from the module with `Get-Command -Module dbatools -CommandType Function | Out-GridView`. If you find something similar already exists, open [a new issue on GitHub](https://GitHub.com/sqlcollaborative/dbatools/issues/new) to request an enhancement to that command. New ideas already accepted can be found on [New Command](https://github.com/sqlcollaborative/dbatools/labels/Type%3A%20New%20Command) tagged issues. If nothing similar pops up, either ping @cl on Slack with your idea about the new command or open a new issue on GitHub with details or requirements you need.

## Report Bugs
[Open a new issue](https://dbatools.io/new-issue/) on GitHub and fill in all the details. The title should report the affected function, followed by a brief description (e.g. _Get-DbaDatabase - Add property x to default view_). The provided template holds most of the details coders need to fix the issue.

## Fix Bugs
We have a [step-by-step guide](https://dbatools.io/firstpull) if you don't know Github enough.
[Open a PR](https://GitHub.com/sqlcollaborative/dbatools/pulls) targeting ideally just one ps1 file (the PR needs to target the *development* branch), with the name of the function being fixed as a title. Everyone will chime in reviewing the code and either approve the PR or request changes. The more targeted and focused the PR, the easier to merge, the fastest to go into the next release. Keep them as simple as possible to speed up the process.

## Standardize Parameters and Variables
As a reference: when we refer to parameters these are the `$SqlInstance` type variables within the `param()` block that allow a function to accept input. A variable will be any `$whateverName` used within a function's code.

We chose to follow the standards below when creating parameters and variables for a function:

1) Any variable used in the parameter block must have first letter of each word capitalized. (e.g. `$SqlInstance`, `$ExcludeLogin`)
2) Any variable used in the parameter block **is required** to be singular.
3) Any variable not part of the parameter block, that is multiple words, will follow the camelCase format. (e.g. `$currentLogin`, `$dbLogin`)
4) Refrain from using single character variable names (e.g. `$i` or `$x`). Try to make them "readable" in the sense that if someone sees the variable name they can get a hint what it presents (e.g. `$db`, `$operatorName`).

When you are working with "objects" in SQL Server, say with databases, what variable name you use should be based on what operation you are doing. You can find examples of various situations in the current code of the module to see more detailed examples. As an example: in situations where you are looping over the databases for an instance, try to use a plural variable name for the collection and then single or abbreviated name in the loop for each object of that collection. e.g. `foreach ($db in $databases) {...`.

[This page](https://github.com/sqlcollaborative/dbatools/wiki/Standard-Documentation) sums up what we currently use. We aim at standardizing and reducing to a set of self-documenting and reusable parameters. If you have any questions around the above do not hesitate to ask in Slack.

## Tests
Remember that tests are needed to make sure dbatools code behaves properly. The ultimate goal is for any user to be able to run dbatools' tests within their environment and, depending on the result, be sure everything works as expected. Dbatools works on a matrix of environments that will hardly be fully covered by a Continuous Integration system. That being said, we have AppVeyor (see later) set up to run at each and every commit.

### How to write tests
To save resources and be more flexible, we split tests with tags into two main categories, "UnitTests" and "IntegrationTests". Below is a starting list of things to consider when writing your test:
- "UnitTests" do not require an instance to be up and running, and are easily the most flexible to be ran on every user computer. - "IntegrationTests" instead require one or more active instances, and there is a bit of setup to do in order to run them.
- Every one of the "IntegrationTests" may need to create a resource (e.g. a database).
- Every resource should be named with the "dbatoolsci_" prefix. _The test should attempt to clean up after itself leaving a pristine environment._
- Try to write tests thinking they may run in each and every user's test environment.

The dbatools-templates repository holds examples, but you can also inspect/copy/cannibalize existing tests. You'll see that every test file is named with a simple convention _Verb-Noun*.Tests.ps1_, and this is required by [Pester](https://GitHub.com/pester/Pester), which is the de-facto standard for running tests in PowerShell.

Tests make sure a "contract" is made between the code and its behavior: once a test is formalized, changes to the code itself or enhancement will be written making sure existing functionality is retained, making the entire dbatools experience more stable.

TODO: how to run tests in your environment, tests\manual.pester.ps1

### AppVeyor setup

AppVeyor is hooked up to test any commit, including PRs. Each commit triggers 5 builds, each referred to as a "scenario":
 - 2008R2 : a server with a single SQL Server 2008 R2 Express Edition instance available ($script:instance1)
 - 2016 : a server with a single SQL Server 2016 Developer Edition instance available ($script:instance2)
 - 2016_service: used to test service restarts
 - 2016_2017 : a server with two instances available, 2016 and 2017 Developer Edition
 - default: a server with two instances available, one SQL Server 2008 R2 Express Edition and a SQL Server 2016 Developer Edition

Builds are split among "scenario"(s) because not every test requires everything to be up and running, and resources on AppVeyor are constrained.

Ideally:
 1) Whenever possible, write UnitTests.
 2) You should write IntegrationTests ideally running in **EITHER** the 2008R2 or the 2016 "scenario".
 3) Default and 216_2017 are the most resource constrained and are left to run the Copy-* commands which are the only ones **needing** two active instances.
 4) If you want to write tests that, e.g, target **BOTH** 2008R2 and 2016, try to avoid writing tests that need both instances to be active at the same time.

AppVeyor is set up to recognize what "scenario" is required by your test, simply inspecting for the presence of combinations of "$script:instance1", "$script:instance2" and "$script:instance3". If you need to fall into case (4), write two test files, e.g. _Get-DbaFoo.first.Tests.ps1 (targeting $script:instance1 only) and _Get-DbaFoo.second.Tests.ps1_ (targeting $script:instance2 only). If you don't want to wait for the entire test suite to run (i.e. you need to run only Get-DbaFoo on AppVeyor), you can use a "magic command" within the commit message, namely `(do Get-DbaFoo)` . This will run only test files within the test folder matching this mask `tests\*Get-DbaFoo*.Tests.ps1`.

TODO: how to run your own AppVeyor before pushing a PR

### Code Coverage, AKA improving tests
A recent introduction in our CI pipeline is code coverage. [Dbatools' CodeCov](https://codecov.io/gh/sqlcollaborative/dbatools/branch/development) shows the percentage of the coverage. Each commit on GitHub triggers a build on AppVeyor. Every build on AppVeyor triggers a code coverage calculation, which is reported to codecov.io for public consumption. The more code covered the more stable the code will be. Once it reaches 100%, you can be pretty sure there will be zero surprises when you'll use the command.

If you want to start contributing new tests, choose the ones with no coverage. You can also inspect functions with low coverage and improve existing tests (https://dbatools.io/improving-tests/)


## Bill of Health

In order to reach 1.0, on top of everything discussed above, we choose to wait for each and every command to adhere to a fixed set of standards. Given most of those "checks" are on a function-by-function basis (with one or more activities tied to each "check"), we came up with the "Bill of Health". You can find it bill at [sqlcollaborative.github.io/boh](https://sqlcollaborative.github.io/boh).

When each and every function is healthy enough, the module itself will be ready for 1.0 release. We may continue to use the same approach (with different checks) for later releases.

There are a few checks which need a core developer to manually sign off the "check", but there are a lot everyone else can fix too, namely ScriptAnalyzer and CodeCoverage.
tools\dbatools\dbatools.psd1
#
# Module manifest for module 'dbatools'
#
# Generated by: Chrissy LeMaire
#
# Generated on: 9/8/2015
#
@{

    # Script module or binary module file associated with this manifest.
    RootModule             = 'dbatools.psm1'

    # Version number of this module.
    ModuleVersion          = '0.9.380'

    # ID used to uniquely identify this module
    GUID                   = '9d139310-ce45-41ce-8e8b-d76335aa1789'

    # Author of this module
    Author                 = 'Chrissy LeMaire'

    # Company or vendor of this module
    CompanyName            = 'dbatools.io'

    # Copyright statement for this module
    Copyright              = '2018 Chrissy LeMaire'

    # Description of the functionality provided by this module
    Description            = "The community module that enables SQL Server Pros to automate database development and server administration"

    # Minimum version of the Windows PowerShell engine required by this module
    PowerShellVersion      = '3.0'

    # Name of the Windows PowerShell host required by this module
    PowerShellHostName     = ''

    # Minimum version of the Windows PowerShell host required by this module
    PowerShellHostVersion  = ''

    # Minimum version of the .NET Framework required by this module
    DotNetFrameworkVersion = ''

    # Minimum version of the common language runtime (CLR) required by this module
    CLRVersion             = ''

    # Processor architecture (None, X86, Amd64, IA64) required by this module
    ProcessorArchitecture  = ''

    # Modules that must be imported into the global environment prior to importing this module
    RequiredModules        = @()

    # Assemblies that must be loaded prior to importing this module
    RequiredAssemblies     = @()

    # Script files () that are run in the caller's environment prior to importing this module
    ScriptsToProcess       = @()

    # Type files (xml) to be loaded when importing this module
    TypesToProcess         = @("xml\dbatools.Types.ps1xml")

    # Format files (xml) to be loaded when importing this module
    # "xml\dbatools.Format.ps1xml"
    FormatsToProcess       = @("xml\dbatools.Format.ps1xml")

    # Modules to import as nested modules of the module specified in ModuleToProcess
    NestedModules          = @()

    # Functions to export from this module
    FunctionsToExport      = @(
        'Start-DbaMigration',
        'Copy-DbaDatabase',
        'Copy-DbaLogin',
        'Copy-DbaSqlServerAgent',
        'Copy-DbaSpConfigure',
        'Copy-DbaLinkedServer',
        'Copy-DbaDatabaseMail',
        'Copy-DbaDatabaseAssembly',
        'Copy-DbaSqlPolicyManagement',
        'Copy-DbaAgentSharedSchedule',
        'Copy-DbaAgentOperator',
        'Copy-DbaAgentJob',
        'Copy-DbaSqlDataCollector',
        'Copy-DbaCustomError',
        'Copy-DbaServerAuditSpecification',
        'Copy-DbaEndpoint',
        'Copy-DbaServerAudit',
        'Copy-DbaServerRole',
        'Copy-DbaResourceGovernor',
        'Copy-DbaExtendedEvent',
        'Copy-DbaBackupDevice',
        'Copy-DbaServerTrigger',
        'Copy-DbaCredential',
        'Copy-DbaCentralManagementServer',
        'Copy-DbaSysDbUserObject',
        'Copy-DbaAgentProxyAccount',
        'Copy-DbaAgentAlert',
        'Import-DbaSpConfigure',
        'Export-DbaSpConfigure'
        'Get-DbaDetachedDatabaseInfo',
        'Restore-DbaBackupFromDirectory',
        'Test-DbaConnection',
        'Import-DbaCsvToSql',
        'Copy-DbaAgentCategory',
        'Update-Dbatools',
        'Test-DbaSqlPath',
        'Export-DbaLogin',
        'Reset-DbaAdmin',
        'Watch-DbaDbLogin',
        'Expand-DbaTLogResponsibly',
        'Test-DbaMigrationConstraint',
        'Get-DbaRegisteredServer',
        'Test-DbaNetworkLatency',
        'Find-DbaDuplicateIndex',
        'Show-DbaServerFileSystem',
        'Get-DbaDiskSpace',
        'Remove-DbaDatabaseSafely',
        'Show-DbaDatabaseList',
        'Set-DbaTempDbConfiguration',
        'Test-DbaTempDbConfiguration',
        'Repair-DbaOrphanUser',
        'Remove-DbaOrphanUser',
        'Find-DbaUnusedIndex',
        'Test-DbaDiskAllocation',
        'Test-DbaPowerPlan',
        'Set-DbaPowerPlan',
        'Test-DbaDiskAlignment',
        'Get-DbaDatabaseSpace',
        'Get-DbaClusterNode',
        'Test-DbaDatabaseOwner',
        'Set-DbaDatabaseOwner',
        'Test-DbaJobOwner',
        'Set-DbaJobOwner',
        'Test-DbaDbVirtualLogFile',
        'Get-DbaRestoreHistory',
        'Get-DbaTcpPort',
        'Test-DbaDatabaseCompatibility',
        'Test-DbaDatabaseCollation',
        'Test-DbaConnectionAuthScheme',
        'Test-DbaServerName',
        'Repair-DbaServerName',
        'Stop-DbaProcess',
        'Copy-DbaSsisCatalog',
        'Find-DbaOrphanedFile',
        'Get-DbaAvailabilityGroup',
        'Get-DbaLastGoodCheckDb',
        'Get-DbaProcess',
        'Get-DbaRunningJob',
        'Set-DbaMaxDop',
        'Test-DbaRecoveryModel',
        'Test-DbaMaxDop',
        'Remove-DbaBackup',
        'Get-DbaPermission',
        'Get-DbaLastBackup',
        'Connect-DbaInstance',
        'Get-DbaStartupParameter',
        'Get-DbaBackupHistory',
        'Read-DbaBackupHeader',
        'Test-DbaLastBackup',
        'Get-DbaMaxMemory',
        'Set-DbaMaxMemory',
        'Test-DbaMaxMemory',
        'Get-DbaDbSnapshot',
        'Remove-DbaDbSnapshot',
        'Get-DbaRoleMember',
        'Resolve-DbaNetworkName',
        'Test-DbaWindowsLogin',
        'Get-DbaMemoryUsage',
        'Export-DbaAvailabilityGroup',
        'Write-DbaDataTable',
        'New-DbaDbSnapshot',
        'Restore-DbaDbSnapshot',
        'Get-DbaTrigger',
        'Export-DbaUser',
        'Get-DbaDatabaseState',
        'Set-DbaDatabaseState',
        'Get-DbaHelpIndex',
        'Get-DbaAgentAlert',
        'Get-DbaAgentOperator',
        'Get-DbaPageFileSetting',
        'Get-DbaSpConfigure',
        'Rename-DbaLogin',
        'Find-DbaAgentJob',
        'Find-DbaDatabase',
        'Get-DbaMsdtc',
        'Get-DbaUptime',
        'Get-DbaXESession',
        'Test-DbaOptimizeForAdHoc',
        'Find-DbaStoredProcedure',
        'Measure-DbaBackupThroughput',
        'Find-DbaLoginInGroup',
        'Get-DbaSpn',
        'Test-DbaSpn',
        'Set-DbaSpn',
        'Remove-DbaSpn',
        'Get-DbaDatabase',
        'Find-DbaUserObject',
        'Get-DbaSqlService',
        'Get-DbaDependency',
        'Clear-DbaSqlConnectionPool',
        'Find-DbaCommand',
        'Get-DbaConfig',
        'Get-DbaConfigValue',
        'Set-DbaConfig',
        'Get-DbaClientProtocol',
        'Backup-DbaDatabase',
        'New-DbaSqlDirectory',
        'Get-DbaPrivilege',
        'Install-DbaWatchUpdate',
        'Watch-DbaUpdate',
        'Uninstall-DbaWatchUpdate',
        'Get-DbaDbQueryStoreOptions',
        'Set-DbaDbQueryStoreOptions',
        'Restore-DbaDatabase',
        'Copy-DbaQueryStoreConfig',
        'Get-DbaExecutionPlan',
        'Export-DbaExecutionPlan',
        'Get-DbaServerProtocol',
        'Get-DbaLocaleSetting',
        'Get-DbaSqlBuildReference',
        'Set-DbaSpConfigure',
        'Test-DbaIdentityUsage',
        'Get-DbaDatabaseAssembly',
        'Get-DbaAgentJob',
        'Get-DbaCustomError',
        'Get-DbaCredential',
        'Get-DbaBackupDevice',
        'Get-DbaAgentProxy',
        'Get-DbaDatabaseEncryption',
        'New-DbaSsisCatalog',
        'Remove-DbaDatabase',
        'Get-DbaQueryExecutionTime',
        'Get-DbaTempdbUsage',
        'Find-DbaDbGrowthEvent',
        'Get-DbaNetworkActivity',
        'Get-DbaAgentJobOutputFile',
        'Set-DbaAgentJobOutputFile',
        'Test-DbaLinkedServerConnection',
        'Get-DbaDatabaseFile',
        'Read-DbaTransactionLog',
        'Get-DbaTable',
        'Invoke-DbaDatabaseShrink',
        'Get-DbaEstimatedCompletionTime',
        'Get-DbaLinkedServer',
        'Set-DbaStartupParameter',
        'New-DbaAgentJob',
        'Export-DbaScript',
        'Get-DbaLogin',
        'New-DbaScriptingOption',
        'Save-DbaDiagnosticQueryScript',
        'Invoke-DbaDiagnosticQuery',
        'Export-DbaDiagnosticQuery',
        'Invoke-DbaWhoIsActive',
        'Install-DbaWhoIsActive',
        'Set-DbaAgentJob',
        'Remove-DbaAgentJob',
        'New-DbaAgentJobStep',
        'Set-DbaAgentJobStep',
        'Remove-DbaAgentJobStep',
        'New-DbaAgentSchedule',
        'Set-DbaAgentSchedule',
        'Remove-DbaAgentSchedule',
        'Backup-DbaDbCertificate',
        'Get-DbaDbCertificate',
        'Get-DbaCmConnection',
        'Get-DbaCmObject',
        'Get-DbaEndpoint',
        'Get-DbaDatabaseMasterKey',
        'Get-DbaSchemaChangeHistory',
        'Get-DbaServerAudit',
        'Get-DbaServerAuditSpecification',
        'Get-DbaSqlProductKey',
        'Get-DbatoolsLog',
        'Restore-DbaDbCertificate',
        'New-DbaDbCertificate',
        'New-DbaCmConnection',
        'New-DbaDatabaseMasterKey',
        'New-DbaServiceMasterKey',
        'New-DbatoolsSupportPackage',
        'Remove-DbaDbCertificate',
        'Remove-DbaCmConnection',
        'Remove-DbaDatabaseMasterKey',
        'Set-DbaCmConnection',
        'Set-DbaTcpPort',
        'Test-DbaCmConnection',
        'New-DbaSqlConnectionStringBuilder',
        'Get-DbaSqlInstanceProperty',
        'Get-DbaSqlInstanceUserOption',
        'New-DbaSqlConnectionString',
        'Get-DbaAgentSchedule',
        'Invoke-DbaLogShipping',
        'Read-DbaTraceFile',
        'New-DbaComputerCertificate',
        'Get-DbaComputerCertificate',
        'Add-DbaComputerCertificate',
        'Get-DbaNetworkCertificate',
        'Set-DbaNetworkCertificate',
        'Remove-DbaNetworkCertificate',
        'Enable-DbaForceNetworkEncryption',
        'Disable-DbaForceNetworkEncryption',
        'Get-DbaForceNetworkEncryption',
        'Remove-DbaComputerCertificate',
        'Get-DbaServerInstallDate',
        'Install-DbaFirstResponderKit',
        'Backup-DbaDatabaseMasterKey',
        'Get-DbaAgentJobHistory',
        'Get-DbaSsisEnvironmentVariable',
        'Get-DbaSqlManagementObject',
        'Test-DbaSqlManagementObject',
        'Get-DbaMaintenanceSolutionLog',
        'Invoke-DbaLogShippingRecovery',
        'Find-DbaTrigger',
        'Find-DbaView',
        'Invoke-DbaDatabaseUpgrade',
        'Get-DbaDatabaseUser',
        'Get-DbaWindowsLog',
        'Get-DbaErrorLog',
        'Get-DbaAgentLog',
        'Get-DbaDbMailLog',
        'Get-DbaDbMailHistory',
        'Get-DbaDatabaseView',
        'Get-DbaDatabaseUdf',
        'Get-DbaDatabasePartitionFunction',
        'Get-DbaDatabasePartitionScheme',
        'Get-DbaDefaultPath',
        'Get-DbaDbStoredProcedure',
        'Test-DbaDbCompression',
        'Mount-DbaDatabase',
        'Dismount-DbaDatabase',
        'Set-DbaPrivilege',
        'Get-DbaAgReplica',
        'Get-DbaAgDatabase',
        'Get-DbaSqlModule',
        'Get-DbaRegisteredServerStore',
        'Sync-DbaLoginPermission',
        'Invoke-Sqlcmd2',
        'New-DbaCredential',
        'Get-DbaFile',
        'Set-DbaDbCompression',
        'New-DbaClientAlias',
        'Get-DbaClientAlias',
        'Get-DbaOperatingSystem',
        'Install-DbaMaintenanceSolution',
        'Get-DbaComputerSystem',
        'Get-DbaTraceFlag',
        'Stop-DbaSqlService',
        'Start-DbaSqlService',
        'Restart-DbaSqlService',
        'Invoke-DbaCycleErrorLog',
        'Get-DbaSqlRegistryRoot',
        'Get-DbaAvailableCollation',
        'Get-DbaUserLevelPermission',
        'Get-DbaAgHadr',
        'Get-DbaPolicy',
        'Find-DbaSimilarTable',
        'Disable-DbaAgHadr',
        'Enable-DbaAgHadr',
        'Get-DbaTrace',
        'Get-DbaSuspectPage',
        'Get-DbaWaitStatistic',
        'Clear-DbaWaitStatistics',
        'Get-DbaTopResourceUsage',
        'New-DbaLogin',
        'Get-DbaAgListener',
        'Invoke-DbaDatabaseClone',
        'Read-DbaXEFile',
        'Get-DbaDistributor',
        'Update-DbaSqlServiceAccount',
        'Watch-DbaXESession',
        'Disable-DbaTraceFlag',
        'Enable-DbaTraceFlag',
        'Start-DbaAgentJob',
        'Stop-DbaAgentJob',
        'Remove-DbaClientAlias',
        'New-DbaAgentProxy',
        'Test-DbaLogShippingStatus',
        'Get-DbaXESessionTarget',
        'New-DbaXESmartTargetResponse',
        'New-DbaXESmartTarget',
        'Get-DbaDbVirtualLogFile',
        'Register-DbaConfig',
        'Get-DbaBackupInformation',
        'Start-DbaXESession',
        'Stop-DbaXESession',
        'Set-DbaDbRecoveryModel',
        'Get-DbaDbRecoveryModel',
        'Get-DbaWaitingTask',
        'Remove-DbaDbUser',
        'Get-DbaDump',
        'Invoke-DbaAdvancedRestore',
        'Format-DbaBackupInformation',
        'Get-DbaAgentJobStep',
        'Test-DbaBackupInformation',
        'Invoke-DbaBalanceDataFiles',
        'Select-DbaBackupInformation',
        'Rename-DbaDatabase',
        'New-DbaPublishProfile',
        'Publish-DbaDacpac',
        'Export-DbaDacpac',
        'Copy-DbaTableData',
        'Invoke-DbaSqlQuery',
        'Remove-DbaLogin',
        'Get-DbaFileStream',
        'Set-DbaFileStream',
        'Get-DbaAgentJobCategory',
        'New-DbaAgentJobCategory',
        'Remove-DbaAgentJobCategory',
        'Set-DbaAgentJobCategory',
        'Get-DbaDbRole',
        'Get-DbaServerRole',
        'Find-DbaBackup',
        'Get-DbaCpuUsage',
        'Remove-DbaXESession',
        'New-DbaXESession',
        'Import-DbaXESessionTemplate',
        'Get-DbaXEStore',
        'Export-DbaXESessionTemplate',
        'New-DbaXESmartTableWriter',
        'New-DbaXESmartReplay',
        'New-DbaXESmartEmail',
        'New-DbaXESmartQueryExec',
        'Start-DbaXESmartTarget',
        'Get-DbaOrphanUser',
        'Get-DbaOpenTransaction',
        'Get-DbaLogShippingError',
        'Test-DbaSqlBuild',
        'Get-DbaXESessionTemplate',
        'ConvertTo-DbaXESession',
        'Start-DbaTrace',
        'Stop-DbaTrace',
        'Remove-DbaTrace',
        'Set-DbaLogin',
        'Copy-DbaXESessionTemplate',
        'Get-DbaXEObject',
        'ConvertTo-DbaDataTable',
        'Find-DbaDisabledIndex',
        'Invoke-DbaPfRelog',
        'Get-DbaPfDataCollectorCounter',
        'Get-DbaPfDataCollectorCounterSample',
        'Get-DbaPfDataCollector',
        'Get-DbaPfDataCollectorSet',
        'Start-DbaPfDataCollectorSet',
        'Stop-DbaPfDataCollectorSet',
        'Export-DbaPfDataCollectorSetTemplate',
        'Get-DbaPfDataCollectorSetTemplate',
        'Import-DbaPfDataCollectorSetTemplate',
        'Remove-DbaPfDataCollectorSet',
        'Add-DbaPfDataCollectorCounter',
        'Remove-DbaPfDataCollectorCounter',
        'Get-DbaPfAvailableCounter',
        'Get-DbaXESmartTarget',
        'Remove-DbaXESmartTarget'
        'Stop-DbaXESmartTarget',
        'Get-DbaRegisteredServerGroup',
        'New-DbaDbUser',
        'Measure-DbaDiskSpaceRequirement',
        'New-DbaXESmartCsvWriter',
        'Export-DbaXECsv',
        'Invoke-DbaXeReplay',
        'Find-DbaInstance',
        'Test-DbaDiskSpeed',
        'Get-DbaDbExtentDiff',
        'Read-DbaAuditFile',
        'Get-DbaDbCompression',
        'Invoke-DbaDbDecryptObject',
        'Get-DbaDbForeignKey',
        'Get-DbaDbCheckConstraint',
        'Set-DbaAgentAlert',
        'Get-DbaSqlFeature',
        'Get-DbaWaitResource',
        'Get-DbaDbPageInfo',
        'Get-DbaConnection',
        'Test-DbaLoginPassword',
        'Get-DbaResourceGovernorClassifierFunction',
        'Get-DbaErrorLogConfig',
        'Set-DbaErrorLogConfig',
        'Select-DbaObject',
        'Add-DbaRegisteredServer',
        'Add-DbaRegisteredServerGroup',
        'Export-DbaRegisteredServer',
        'Import-DbaRegisteredServer',
        'Move-DbaRegisteredServer',
        'Move-DbaRegisteredServerGroup',
        'Remove-DbaRegisteredServer',
        'Remove-DbaRegisteredServerGroup',
        'Get-DbaPlanCache',
        'Clear-DbaPlanCache',
        'Get-DbaSsisExecutionHistory'
    )

    # Cmdlets to export from this module
    CmdletsToExport        = ''

    # Variables to export from this module
    VariablesToExport      = ''

    # Aliases to export from this module
    # Aliases are stored in dbatools.psm1
    # KEEP Detach-DbaDatabase, Dismount-DbaDatabase and Start-SqlMigration FOREVER
    AliasesToExport        = 'Detach-DbaDatabase', 'Attach-DbaDatabase',
    'Reset-SqlSaPassword',
    'Copy-SqlUserDefinedMessage',
    'Copy-SqlJobServer',
    'Restore-HallengrenBackup',
    'Update-SqlWhoIsActive',
    'Install-SqlWhoIsActive',
    'Show-SqlMigrationConstraint',
    'Test-SqlDiskAllocation',
    'Get-DiskSpace',
    'Get-SqlMaxMemory',
    'Set-SqlMaxMemory',
    'Show-SqlWhoIsActive',
    'Copy-SqlAgentCategory',
    'Copy-SqlAlert',
    'Copy-SqlAudit',
    'Copy-SqlAuditSpecification',
    'Copy-SqlBackupDevice',
    'Copy-SqlCentralManagementServer',
    'Copy-SqlCredential',
    'Copy-SqlCustomError',
    'Copy-SqlDatabase',
    'Copy-SqlDatabaseAssembly',
    'Copy-SqlDatabaseMail',
    'Copy-SqlDataCollector',
    'Copy-SqlEndpoint',
    'Copy-SqlExtendedEvent',
    'Copy-SqlJob',
    'Copy-SqlLinkedServer',
    'Copy-SqlLogin',
    'Copy-SqlOperator',
    'Copy-SqlPolicyManagement',
    'Copy-SqlProxyAccount',
    'Copy-SqlResourceGovernor',
    'Copy-SqlServerAgent',
    'Copy-SqlServerRole',
    'Copy-SqlServerTrigger',
    'Copy-SqlSharedSchedule',
    'Copy-SqlSpConfigure',
    'Copy-SqlSsisCatalog',
    'Copy-SqlSysDbUserObjects',
    'Expand-SqlTLogResponsibly',
    'Export-SqlLogin',
    'Export-SqlSpConfigure',
    'Export-SqlUser',
    'Find-SqlDuplicateIndex',
    'Find-SqlUnusedIndex',
    'Get-SqlRegisteredServerName',
    'Get-SqlServerKey',
    'Import-SqlSpConfigure',
    'Remove-SqlDatabaseSafely',
    'Remove-SqlOrphanUser',
    'Repair-SqlOrphanUser',
    'Reset-SqlAdmin',
    'Restore-SqlBackupFromDirectory',
    'Set-SqlTempDbConfiguration',
    'Show-SqlDatabaseList',
    'Show-SqlServerFileSystem',
    'Start-SqlMigration',
    'Sync-SqlLoginPermissions',
    'Test-SqlConnection',
    'Test-SqlMigrationConstraint',
    'Test-SqlNetworkLatency',
    'Test-SqlPath',
    'Test-SqlTempDbConfiguration',
    'Watch-SqlDbLogin',
    'Get-DbaDatabaseFreeSpace',
    'Get-DbaQueryStoreConfig',
    'Set-DbaQueryStoreConfig',
    'Get-DbaRegisteredServerName',
    'Connect-DbaSqlServer',
    'Get-DbaInstance',
    'Get-DbaXEventsSession',
    'Get-DbaXEventSession',
    'Get-DbaXEventSessionTarget',
    'Read-DbaXEventFile',
    'Watch-DbaXEventSession',
    'Get-DbaDatabaseCertificate',
    'New-DbaDatabaseCertificate',
    'Remove-DbaDatabaseCertificate',
    'Restore-DbaDatabaseCertificate',
    'Find-DbaDatabaseGrowthEvent',
    'Get-DbaTraceFile',
    'Out-DbaDataTable',
    'Invoke-DbaSqlCmd',
    'Get-DbaClusterActiveNode',
    'Test-DbaVirtualLogFile',
    'Test-DbaFullRecoveryModel',
    'Get-DbaDatabaseSnapshot',
    'New-DbaDatabaseSnapshot',
    'Remove-DbaDatabaseSnapshot',
    'Restore-DbaDatabaseSnapshot',
    'Sync-DbaSqlLoginPermission',
    'Get-DbaSqlLog',
    'Test-DbaValidLogin',
    'Get-DbaJobCategory'

    # List of all modules packaged with this module
    ModuleList             = @()

    # List of all files packaged with this module
    FileList               = ''

    PrivateData            = @{
        # PSData is module packaging and gallery metadata embedded in PrivateData
        # It's for rebuilding PowerShellGet (and PoshCode) NuGet-style packages
        # We had to do this because it's the only place we're allowed to extend the manifest
        # https://connect.microsoft.com/PowerShell/feedback/details/421837
        PSData = @{
            # The primary categorization of this module (from the TechNet Gallery tech tree).
            Category     = "Databases"

            # Keyword tags to help users find this module via navigations and search.
            Tags         = @('sqlserver', 'migrations', 'sql', 'dba', 'databases')

            # The web address of an icon which can be used in galleries to represent this module
            IconUri      = "https://dbatools.io/logo.png"

            # The web address of this module's project or support homepage.
            ProjectUri   = "https://dbatools.io"

            # The web address of this module's license. Points to a page that's embeddable and linkable.
            LicenseUri   = "https://opensource.org/licenses/MIT"

            # Release notes for this particular version of the module
            ReleaseNotes = "https://dbatools.io/releases"

            # If true, the LicenseUrl points to an end-user license (not just a source license) which requires the user agreement before use.
            # RequireLicenseAcceptance = ""

            # Indicates this is a pre-release/testing version of the module.
            IsPrerelease = 'True'
        }
    }
}

# SIG # Begin signature block
# MIIcYgYJKoZIhvcNAQcCoIIcUzCCHE8CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUAdFtgLjnLHTbM3pS4WOZA7Ea
# A5iggheRMIIFGjCCBAKgAwIBAgIQAsF1KHTVwoQxhSrYoGRpyjANBgkqhkiG9w0B
# AQsFADByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD
# VQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFz
# c3VyZWQgSUQgQ29kZSBTaWduaW5nIENBMB4XDTE3MDUwOTAwMDAwMFoXDTIwMDUx
# MzEyMDAwMFowVzELMAkGA1UEBhMCVVMxETAPBgNVBAgTCFZpcmdpbmlhMQ8wDQYD
# VQQHEwZWaWVubmExETAPBgNVBAoTCGRiYXRvb2xzMREwDwYDVQQDEwhkYmF0b29s
# czCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAI8ng7JxnekL0AO4qQgt
# Kr6p3q3SNOPh+SUZH+SyY8EA2I3wR7BMoT7rnZNolTwGjUXn7bRC6vISWg16N202
# 1RBWdTGW2rVPBVLF4HA46jle4hcpEVquXdj3yGYa99ko1w2FOWzLjKvtLqj4tzOh
# K7wa/Gbmv0Si/FU6oOmctzYMI0QXtEG7lR1HsJT5kywwmgcjyuiN28iBIhT6man0
# Ib6xKDv40PblKq5c9AFVldXUGVeBJbLhcEAA1nSPSLGdc7j4J2SulGISYY7ocuX3
# tkv01te72Mv2KkqqpfkLEAQjXgtM0hlgwuc8/A4if+I0YtboCMkVQuwBpbR9/6ys
# Z+sCAwEAAaOCAcUwggHBMB8GA1UdIwQYMBaAFFrEuXsqCqOl6nEDwGD5LfZldQ5Y
# MB0GA1UdDgQWBBRcxSkFqeA3vvHU0aq2mVpFRSOdmjAOBgNVHQ8BAf8EBAMCB4Aw
# EwYDVR0lBAwwCgYIKwYBBQUHAwMwdwYDVR0fBHAwbjA1oDOgMYYvaHR0cDovL2Ny
# bDMuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC1jcy1nMS5jcmwwNaAzoDGGL2h0
# dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zaGEyLWFzc3VyZWQtY3MtZzEuY3JsMEwG
# A1UdIARFMEMwNwYJYIZIAYb9bAMBMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3
# LmRpZ2ljZXJ0LmNvbS9DUFMwCAYGZ4EMAQQBMIGEBggrBgEFBQcBAQR4MHYwJAYI
# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBOBggrBgEFBQcwAoZC
# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0U0hBMkFzc3VyZWRJ
# RENvZGVTaWduaW5nQ0EuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQAD
# ggEBANuBGTbzCRhgG0Th09J0m/qDqohWMx6ZOFKhMoKl8f/l6IwyDrkG48JBkWOA
# QYXNAzvp3Ro7aGCNJKRAOcIjNKYef/PFRfFQvMe07nQIj78G8x0q44ZpOVCp9uVj
# sLmIvsmF1dcYhOWs9BOG/Zp9augJUtlYpo4JW+iuZHCqjhKzIc74rEEiZd0hSm8M
# asshvBUSB9e8do/7RhaKezvlciDaFBQvg5s0fICsEhULBRhoyVOiUKUcemprPiTD
# xh3buBLuN0bBayjWmOMlkG1Z6i8DUvWlPGz9jiBT3ONBqxXfghXLL6n8PhfppBhn
# daPQO8+SqF5rqrlyBPmRRaTz2GQwggUwMIIEGKADAgECAhAECRgbX9W7ZnVTQ7Vv
# lVAIMA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdp
# Q2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0Rp
# Z2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0xMzEwMjIxMjAwMDBaFw0yODEw
# MjIxMjAwMDBaMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx
# GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0IFNI
# QTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EwggEiMA0GCSqGSIb3DQEBAQUA
# A4IBDwAwggEKAoIBAQD407Mcfw4Rr2d3B9MLMUkZz9D7RZmxOttE9X/lqJ3bMtdx
# 6nadBS63j/qSQ8Cl+YnUNxnXtqrwnIal2CWsDnkoOn7p0WfTxvspJ8fTeyOU5JEj
# lpB3gvmhhCNmElQzUHSxKCa7JGnCwlLyFGeKiUXULaGj6YgsIJWuHEqHCN8M9eJN
# YBi+qsSyrnAxZjNxPqxwoqvOf+l8y5Kh5TsxHM/q8grkV7tKtel05iv+bMt+dDk2
# DZDv5LVOpKnqagqrhPOsZ061xPeM0SAlI+sIZD5SlsHyDxL0xY4PwaLoLFH3c7y9
# hbFig3NBggfkOItqcyDQD2RzPJ6fpjOp/RnfJZPRAgMBAAGjggHNMIIByTASBgNV
# HRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEF
# BQcDAzB5BggrBgEFBQcBAQRtMGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRp
# Z2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0cDovL2NhY2VydHMuZGlnaWNlcnQu
# Y29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNydDCBgQYDVR0fBHoweDA6oDig
# NoY0aHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9v
# dENBLmNybDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0
# QXNzdXJlZElEUm9vdENBLmNybDBPBgNVHSAESDBGMDgGCmCGSAGG/WwAAgQwKjAo
# BggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAKBghghkgB
# hv1sAzAdBgNVHQ4EFgQUWsS5eyoKo6XqcQPAYPkt9mV1DlgwHwYDVR0jBBgwFoAU
# Reuir/SSy4IxLVGLp6chnfNtyA8wDQYJKoZIhvcNAQELBQADggEBAD7sDVoks/Mi
# 0RXILHwlKXaoHV0cLToaxO8wYdd+C2D9wz0PxK+L/e8q3yBVN7Dh9tGSdQ9RtG6l
# jlriXiSBThCk7j9xjmMOE0ut119EefM2FAaK95xGTlz/kLEbBw6RFfu6r7VRwo0k
# riTGxycqoSkoGjpxKAI8LpGjwCUR4pwUR6F6aGivm6dcIFzZcbEMj7uo+MUSaJ/P
# QMtARKUT8OZkDCUIQjKyNookAv4vcn4c10lFluhZHen6dGRrsutmQ9qzsIzV6Q3d
# 9gEgzpkxYz0IGhizgZtPxpMQBvwHgfqL2vmCSfdibqFT+hKUGIUukpHqaGxEMrJm
# oecYpJpkUe8wggZqMIIFUqADAgECAhADAZoCOv9YsWvW1ermF/BmMA0GCSqGSIb3
# DQEBBQUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAX
# BgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3Vy
# ZWQgSUQgQ0EtMTAeFw0xNDEwMjIwMDAwMDBaFw0yNDEwMjIwMDAwMDBaMEcxCzAJ
# BgNVBAYTAlVTMREwDwYDVQQKEwhEaWdpQ2VydDElMCMGA1UEAxMcRGlnaUNlcnQg
# VGltZXN0YW1wIFJlc3BvbmRlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
# ggEBAKNkXfx8s+CCNeDg9sYq5kl1O8xu4FOpnx9kWeZ8a39rjJ1V+JLjntVaY1sC
# SVDZg85vZu7dy4XpX6X51Id0iEQ7Gcnl9ZGfxhQ5rCTqqEsskYnMXij0ZLZQt/US
# s3OWCmejvmGfrvP9Enh1DqZbFP1FI46GRFV9GIYFjFWHeUhG98oOjafeTl/iqLYt
# WQJhiGFyGGi5uHzu5uc0LzF3gTAfuzYBje8n4/ea8EwxZI3j6/oZh6h+z+yMDDZb
# esF6uHjHyQYuRhDIjegEYNu8c3T6Ttj+qkDxss5wRoPp2kChWTrZFQlXmVYwk/PJ
# YczQCMxr7GJCkawCwO+k8IkRj3cCAwEAAaOCAzUwggMxMA4GA1UdDwEB/wQEAwIH
# gDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMIIBvwYDVR0g
# BIIBtjCCAbIwggGhBglghkgBhv1sBwEwggGSMCgGCCsGAQUFBwIBFhxodHRwczov
# L3d3dy5kaWdpY2VydC5jb20vQ1BTMIIBZAYIKwYBBQUHAgIwggFWHoIBUgBBAG4A
# eQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQByAHQAaQBmAGkAYwBhAHQA
# ZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBjAGUAcAB0AGEAbgBjAGUA
# IABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAgAEMAUAAvAEMAUABTACAA
# YQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQAGEAcgB0AHkAIABBAGcA
# cgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBtAGkAdAAgAGwAaQBhAGIA
# aQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBjAG8AcgBwAG8AcgBhAHQA
# ZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBlAHIAZQBuAGMAZQAuMAsG
# CWCGSAGG/WwDFTAfBgNVHSMEGDAWgBQVABIrE5iymQftHt+ivlcNK2cCzTAdBgNV
# HQ4EFgQUYVpNJLZJMp1KKnkag0v0HonByn0wfQYDVR0fBHYwdDA4oDagNIYyaHR0
# cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEQ0EtMS5jcmww
# OKA2oDSGMmh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJ
# RENBLTEuY3JsMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29j
# c3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdp
# Y2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURDQS0xLmNydDANBgkqhkiG9w0BAQUF
# AAOCAQEAnSV+GzNNsiaBXJuGziMgD4CH5Yj//7HUaiwx7ToXGXEXzakbvFoWOQCd
# 42yE5FpA+94GAYw3+puxnSR+/iCkV61bt5qwYCbqaVchXTQvH3Gwg5QZBWs1kBCg
# e5fH9j/n4hFBpr1i2fAnPTgdKG86Ugnw7HBi02JLsOBzppLA044x2C/jbRcTBu7k
# A7YUq/OPQ6dxnSHdFMoVXZJB2vkPgdGZdA0mxA5/G7X1oPHGdwYoFenYk+VVFvC7
# Cqsc21xIJ2bIo4sKHOWV2q7ELlmgYd3a822iYemKC23sEhi991VUQAOSK2vCUcIK
# SK+w1G7g9BQKOhvjjz3Kr2qNe9zYRDCCBs0wggW1oAMCAQICEAb9+QOWA63qAArr
# Pye7uhswDQYJKoZIhvcNAQEFBQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERp
# Z2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMb
# RGlnaUNlcnQgQXNzdXJlZCBJRCBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTIx
# MTExMDAwMDAwMFowYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IElu
# YzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQg
# QXNzdXJlZCBJRCBDQS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
# 6IItmfnKwkKVpYBzQHDSnlZUXKnE0kEGj8kz/E1FkVyBn+0snPgWWd+etSQVwpi5
# tHdJ3InECtqvy15r7a2wcTHrzzpADEZNk+yLejYIA6sMNP4YSYL+x8cxSIB8HqIP
# kg5QycaH6zY/2DDD/6b3+6LNb3Mj/qxWBZDwMiEWicZwiPkFl32jx0PdAug7Pe2x
# QaPtP77blUjE7h6z8rwMK5nQxl0SQoHhg26Ccz8mSxSQrllmCsSNvtLOBq6thG9I
# hJtPQLnxTPKvmPv2zkBdXPao8S+v7Iki8msYZbHBc63X8djPHgp0XEK4aH631XcK
# J1Z8D2KkPzIUYJX9BwSiCQIDAQABo4IDejCCA3YwDgYDVR0PAQH/BAQDAgGGMDsG
# A1UdJQQ0MDIGCCsGAQUFBwMBBggrBgEFBQcDAgYIKwYBBQUHAwMGCCsGAQUFBwME
# BggrBgEFBQcDCDCCAdIGA1UdIASCAckwggHFMIIBtAYKYIZIAYb9bAABBDCCAaQw
# OgYIKwYBBQUHAgEWLmh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVw
# b3NpdG9yeS5odG0wggFkBggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUA
# IABvAGYAIAB0AGgAaQBzACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4A
# cwB0AGkAdAB1AHQAZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQA
# aABlACAARABpAGcAaQBDAGUAcgB0ACAAQwBQAC8AQwBQAFMAIABhAG4AZAAgAHQA
# aABlACAAUgBlAGwAeQBpAG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUA
# bgB0ACAAdwBoAGkAYwBoACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkA
# IABhAG4AZAAgAGEAcgBlACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUA
# cgBlAGkAbgAgAGIAeQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4wCwYJYIZIAYb9bAMV
# MBIGA1UdEwEB/wQIMAYBAf8CAQAweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzAB
# hhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9j
# YWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQw
# gYEGA1UdHwR6MHgwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdp
# Q2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwOqA4oDaGNGh0dHA6Ly9jcmw0LmRpZ2lj
# ZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwHQYDVR0OBBYEFBUA
# EisTmLKZB+0e36K+Vw0rZwLNMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3z
# bcgPMA0GCSqGSIb3DQEBBQUAA4IBAQBGUD7Jtygkpzgdtlspr1LPUukxR6tWXHvV
# DQtBs+/sdR90OPKyXGGinJXDUOSCuSPRujqGcq04eKx1XRcXNHJHhZRW0eu7NoR3
# zCSl8wQZVann4+erYs37iy2QwsDStZS9Xk+xBdIOPRqpFFumhjFiqKgz5Js5p8T1
# zh14dpQlc+Qqq8+cdkvtX8JLFuRLcEwAiR78xXm8TBJX/l/hHrwCXaj++wc4Tw3G
# XZG5D2dFzdaD7eeSDY2xaYxP+1ngIw/Sqq4AfO6cQg7PkdcntxbuD8O9fAqg7iwI
# VYUiuOsYGk38KiGtSTGDR5V3cdyxG0tLHBCcdxTBnU8vWpUIKRAmMYIEOzCCBDcC
# AQEwgYYwcjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcG
# A1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBB
# c3N1cmVkIElEIENvZGUgU2lnbmluZyBDQQIQAsF1KHTVwoQxhSrYoGRpyjAJBgUr
# DgMCGgUAoHgwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMx
# DAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkq
# hkiG9w0BCQQxFgQUefJjLVsFrsrqioQrSWMgKrclmjcwDQYJKoZIhvcNAQEBBQAE
# ggEAJaLpH4PpA3nPYx9NKXOCaIfbXI57QKyYocrTHHe0DHdJ9WYrgxnAFsluhrJ1
# e1ZtevUDzHrISOkpcTMf7vsIPYDuJEsX9ZydDJIO3NgVJrKjrzRD9f5U6swcDYub
# 2ZK5I7lbjXVZL9OMa8y5nj1eP5+GMZkL9kbtmbvLIIrHQrvNbWDBMsfzL5YGQh1h
# IGD3V8+bKwbbNkVjL5OjTh94Zbcrb+l8cC/LPRZViTECgoLsfqksolbGHLdNe4ir
# HytOSk9J6SL/CUjW/w854iie0L/G3Lb8E5X9e4WTBY9eoGKDjT400BFSUR1PELuO
# 78wQ/dP0LBEhtxzD61bmGPpqWqGCAg8wggILBgkqhkiG9w0BCQYxggH8MIIB+AIB
# ATB2MGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNV
# BAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3VyZWQg
# SUQgQ0EtMQIQAwGaAjr/WLFr1tXq5hfwZjAJBgUrDgMCGgUAoF0wGAYJKoZIhvcN
# AQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTgwNzIzMTk1NzE3WjAj
# BgkqhkiG9w0BCQQxFgQUpbSfJyQIWQpnbHDEgJKR1XvyoDYwDQYJKoZIhvcNAQEB
# BQAEggEAFuIdvfWf8GLaq4zxrpyJXLhechXyl/F+xSQcs8WfeH6XyFfclojnnCTn
# HhqDs0Dy3gv8M6G0HxKLQ11MawPBVm9S8rYsBifSf9w3ixWPjkVS2eAeC+n8nHfG
# 6Yt2Dk+1WDnVw7uNp55CHneZyxXHMvPjOfN+k3TA6xelZpvwuW3p4ae5qzgYiTNo
# 3fgj1wb9EUA+RxGRwyLS0SLMNhS8UN6J3XV6ZOCL7rLTKqJx/G14q4+mdBPaJiPT
# jvANiTQdsUkpYdG3i/HrlPzmdL/TW1D/Qd+VfYDhKJg4aJm2JUsZImEevk9bDAWL
# iswxdMxnRyVZIeBcOHCJyO5J7Cl1jA==
# SIG # End signature block
tools\dbatools\dbatools.psm1
$start = Get-Date

#region Import helper functions
function Import-ModuleFile {
    <#
    .SYNOPSIS
        Helps import dbatools files according to configuration

    .DESCRIPTION
        Helps import dbatools files according to configuration
        Always dotsource this function!

    .PARAMETER Path
        The full path to the file to import

    .EXAMPLE
        PS C:\> Import-ModuleFile -Path $function.FullName

        Imports the file stored at '$function.FullName'
#>
    [CmdletBinding()]
    Param (
        $Path
    )
    
    if ($script:doDotSource) { . $Path }
    else { $ExecutionContext.InvokeCommand.InvokeScript($false, ([scriptblock]::Create([io.file]::ReadAllText($Path))), $null, $null) }
}

function Write-ImportTime {
    <#
    .SYNOPSIS
        Writes an entry to the import module time debug list

    .DESCRIPTION
        Writes an entry to the import module time debug list

    .PARAMETER Text
        The message to write

    .EXAMPLE
        PS C:\> Write-ImportTime -Text "Starting SMO Import"

        Adds the message "Starting SMO Import" to the debug list
#>
    [CmdletBinding()]
    Param (
        [string]$Text,
        $Timestamp = (Get-Date)
    )
    
    if ($dbatools_disableTimeMeasurements) { return }
    
    if (-not $script:dbatools_ImportPerformance) { $script:dbatools_ImportPerformance = @() }
    
    if (([System.Management.Automation.PSTypeName]'Sqlcollaborative.Dbatools.Configuration.Config').Type -eq $null) {
        $script:dbatools_ImportPerformance += New-Object PSObject -Property @{ Time = $timestamp; Action = $Text }
    }
    else {
        if ([Sqlcollaborative.Dbatools.dbaSystem.DebugHost]::ImportTimeEntries.Count -eq 0) {
            foreach ($entry in $script:dbatools_ImportPerformance) { [Sqlcollaborative.Dbatools.dbaSystem.DebugHost]::ImportTimeEntries.Add((New-Object Sqlcollaborative.Dbatools.dbaSystem.StartTimeEntry($entry.Action, $entry.Time, ([System.Management.Automation.Runspaces.Runspace]::DefaultRunspace.InstanceId)))) }
        }
        
        [Sqlcollaborative.Dbatools.dbaSystem.DebugHost]::ImportTimeEntries.Add((New-Object Sqlcollaborative.Dbatools.dbaSystem.StartTimeEntry($Text, $timestamp, ([System.Management.Automation.Runspaces.Runspace]::DefaultRunspace.InstanceId))))
    }
}

Write-ImportTime -Text "Start" -Timestamp $start
Write-ImportTime -Text "Loading import helper functions"
#endregion Import helper functions

# Not supporting the provider path at this time 2/28/2017
if (((Resolve-Path .\).Path).StartsWith("SQLSERVER:\")) {
    Write-Warning "SQLSERVER:\ provider not supported. Please change to another directory and reload the module."
    Write-Warning "Going to continue loading anyway, but expect issues."
}

Write-ImportTime -Text "Resolved path to not SQLSERVER PSDrive"

$script:PSModuleRoot = $PSScriptRoot

#region Import Defines
$dbatoolsSystemUserNode = Get-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\System" -ErrorAction Ignore
$dbatoolsSystemSystemNode = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\System" -ErrorAction Ignore

#region Dot Sourcing
# Detect whether at some level dotsourcing was enforced
$script:doDotSource = $false
if ($dbatools_dotsourcemodule) { $script:doDotSource = $true }
if ($dbatoolsSystemSystemNode.DoDotSource) { $script:doDotSource = $true }
if ($dbatoolsSystemUserNode.DoDotSource) { $script:doDotSource = $true }
#endregion Dot Sourcing

#region Copy DLL Mode
$script:copyDllMode = $false
if ($dbatools_copydllmode) { $script:copyDllMode = $true }
if ($dbatoolsSystemSystemNode.CopyDllMode) { $script:copyDllMode = $true }
if ($dbatoolsSystemUserNode.CopyDllMode) { $script:copyDllMode = $true }
#endregion Copy DLL Mode

#region Always Compile
$script:alwaysBuildLibrary = $false
if ($dbatools_alwaysbuildlibrary) { $script:alwaysBuildLibrary = $true }
if ($dbatoolsSystemSystemNode.AlwaysBuildLibrary) { $script:alwaysBuildLibrary = $true }
if ($dbatoolsSystemUserNode.AlwaysBuildLibrary) { $script:alwaysBuildLibrary = $true }
#endregion Always Compile

#region Serial Import
$script:serialImport = $false
if ($dbatools_serialimport) { $script:serialImport = $true }
if ($dbatoolsSystemSystemNode.SerialImport) { $script:serialImport = $true }
if ($dbatoolsSystemUserNode.SerialImport) { $script:serialImport = $true }
#endregion Serial Import

#region Multi File Import
$script:multiFileImport = $false
if ($dbatools_serialimport) { $script:multiFileImport = $true }
if ($dbatoolsSystemSystemNode.MultiFileImport) { $script:multiFileImport = $true }
if ($dbatoolsSystemUserNode.MultiFileImport) { $script:multiFileImport = $true }
if (Test-Path -Path "$script:PSModuleRoot\.git") { $script:multiFileImport = $true }
#endregion Multi File Import

Write-ImportTime -Text "Validated defines"
#endregion Import Defines

Get-ChildItem -Path "$script:PSModuleRoot\bin\*.dll" -Recurse | Unblock-File -ErrorAction SilentlyContinue
Write-ImportTime -Text "Unblocking Files"

# Define folder in which to copy dll files before importing
if (-not $script:copyDllMode) { $script:DllRoot = "$script:PSModuleRoot\bin" }
else {
    $libraryTempPath = "$($env:TEMP)\dbatools-$(Get-Random -Minimum 1000000 -Maximum 9999999)"
    while (Test-Path -Path $libraryTempPath) {
        $libraryTempPath = "$($env:TEMP)\dbatools-$(Get-Random -Minimum 1000000 -Maximum 9999999)"
    }
    $script:DllRoot = $libraryTempPath
    $null = New-Item -Path $libraryTempPath -ItemType Directory
}

if (-not ([System.Management.Automation.PSTypeName]'Microsoft.SqlServer.Management.Smo.Server').Type) {
    . Import-ModuleFile "$script:PSModuleRoot\internal\scripts\smoLibraryImport.ps1"
    Write-ImportTime -Text "Starting import SMO libraries"
}

<#

    Do the rest of the loading

#>

# This technique helps a little bit
# https://becomelotr.wordpress.com/2017/02/13/expensive-dot-sourcing/

# Load our own custom library
# Should always come before function imports
. Import-ModuleFile "$script:PSModuleRoot\bin\library.ps1"
. Import-ModuleFile "$script:PSModuleRoot\bin\typealiases.ps1"
Write-ImportTime -Text "Loading dbatools library"

# Tell the library where the module is based, just in case
[Sqlcollaborative.Dbatools.dbaSystem.SystemHost]::ModuleBase = $script:PSModuleRoot

# Load configuration system
# Should always go after library and path setting
if (-not ([Sqlcollaborative.Dbatools.dbaSystem.SystemHost]::ModuleImported)) {
    . Import-ModuleFile "$script:PSModuleRoot\internal\configurations\configuration.ps1"
    Write-ImportTime -Text "Configuration System"
}
if (-not ([Sqlcollaborative.Dbatools.Message.LogHost]::LoggingPath)) {
    [Sqlcollaborative.Dbatools.Message.LogHost]::LoggingPath = "$($env:AppData)\PowerShell\dbatools"
}

if ($script:multiFileImport) {
    # All internal functions privately available within the toolset
    foreach ($function in (Get-ChildItem "$script:PSModuleRoot\internal\functions\*.ps1")) {
        . Import-ModuleFile $function.FullName
    }
    Write-ImportTime -Text "Loading Internal Commands"
    
    . Import-ModuleFile "$script:PSModuleRoot\internal\scripts\cmdlets.ps1"
    Write-ImportTime -Text "Registering cmdlets"
    
    # All exported functions
    foreach ($function in (Get-ChildItem "$script:PSModuleRoot\functions\*.ps1")) {
        . Import-ModuleFile $function.FullName
    }
    Write-ImportTime -Text "Loading Public Commands"
    
}
else {
    . "$script:PSModuleRoot\allcommands.ps1"
    Write-ImportTime -Text "Loading Public and Private Commands"
    
    . Import-ModuleFile "$script:PSModuleRoot\internal\scripts\cmdlets.ps1"
    Write-ImportTime -Text "Registering cmdlets"
}

# Run all optional code
# Note: Each optional file must include a conditional governing whether it's run at all.
# Validations were moved into the other files, in order to prevent having to update dbatools.psm1 every time
# 96ms
foreach ($function in (Get-ChildItem "$script:PSModuleRoot\optional\*.ps1")) {
    . Import-ModuleFile $function.FullName
}
Write-ImportTime -Text "Loading Optional Commands"

# Process TEPP parameters
. Import-ModuleFile "$script:PSModuleRoot\internal\scripts\insertTepp.ps1"
Write-ImportTime -Text "Loading TEPP"

# Process transforms
. Import-ModuleFile "$script:PSModuleRoot\internal\scripts\message-transforms.ps1"
Write-ImportTime -Text "Loading Message Transforms"

# Load scripts that must be individually run at the end #
#-------------------------------------------------------#

# Start the logging system (requires the configuration system up and running)
. Import-ModuleFile "$script:PSModuleRoot\internal\scripts\logfilescript.ps1"
Write-ImportTime -Text "Script: Logging"

# Start the tepp asynchronous update system (requires the configuration system up and running)
. Import-ModuleFile "$script:PSModuleRoot\internal\scripts\updateTeppAsync.ps1"
Write-ImportTime -Text "Script: Asynchronous TEPP Cache"

# Start the maintenance system (requires pretty much everything else already up and running)
. Import-ModuleFile "$script:PSModuleRoot\internal\scripts\dbatools-maintenance.ps1"
Write-ImportTime -Text "Script: Maintenance"

#region Aliases
# I renamed this function to be more accurate - 1ms
@(
    @{
        "AliasName"    = "Copy-SqlAgentCategory"
        "Definition"   = "Copy-DbaAgentCategory"
    },
    @{
        "AliasName"    = "Copy-SqlAlert"
        "Definition"   = "Copy-DbaAgentAlert"
    },
    @{
        "AliasName"    = "Copy-SqlAudit"
        "Definition"   = "Copy-DbaServerAudit"
    },
    @{
        "AliasName"    = "Copy-SqlAuditSpecification"
        "Definition"   = "Copy-DbaServerAuditSpecification"
    },
    @{
        "AliasName"    = "Copy-SqlBackupDevice"
        "Definition"   = "Copy-DbaBackupDevice"
    },
    @{
        "AliasName"    = "Copy-SqlCentralManagementServer"
        "Definition"   = "Copy-DbaCentralManagementServer"
    },
    @{
        "AliasName"    = "Copy-SqlCredential"
        "Definition"   = "Copy-DbaCredential"
    },
    @{
        "AliasName"    = "Copy-SqlCustomError"
        "Definition"   = "Copy-DbaCustomError"
    },
    @{
        "AliasName"    = "Copy-SqlDatabase"
        "Definition"   = "Copy-DbaDatabase"
    },
    @{
        "AliasName"    = "Copy-SqlDatabaseAssembly"
        "Definition"   = "Copy-DbaDatabaseAssembly"
    },
    @{
        "AliasName"    = "Copy-SqlDatabaseMail"
        "Definition"   = "Copy-DbaDatabaseMail"
    },
    @{
        "AliasName"    = "Copy-SqlDataCollector"
        "Definition"   = "Copy-DbaSqlDataCollector"
    },
    @{
        "AliasName"    = "Copy-SqlEndpoint"
        "Definition"   = "Copy-DbaEndpoint"
    },
    @{
        "AliasName"    = "Copy-SqlExtendedEvent"
        "Definition"   = "Copy-DbaExtendedEvent"
    },
    @{
        "AliasName"    = "Copy-SqlJob"
        "Definition"   = "Copy-DbaAgentJob"
    },
    @{
        "AliasName"    = "Copy-SqlJobServer"
        "Definition"   = "Copy-SqlServerAgent"
    },
    @{
        "AliasName"    = "Copy-SqlLinkedServer"
        "Definition"   = "Copy-DbaLinkedServer"
    },
    @{
        "AliasName"    = "Copy-SqlLogin"
        "Definition"   = "Copy-DbaLogin"
    },
    @{
        "AliasName"    = "Copy-SqlOperator"
        "Definition"   = "Copy-DbaAgentOperator"
    },
    @{
        "AliasName"    = "Copy-SqlPolicyManagement"
        "Definition"   = "Copy-DbaSqlPolicyManagement"
    },
    @{
        "AliasName"    = "Copy-SqlProxyAccount"
        "Definition"   = "Copy-DbaAgentProxyAccount"
    },
    @{
        "AliasName"    = "Copy-SqlResourceGovernor"
        "Definition"   = "Copy-DbaResourceGovernor"
    },
    @{
        "AliasName"    = "Copy-SqlServerAgent"
        "Definition"   = "Copy-DbaSqlServerAgent"
    },
    @{
        "AliasName"    = "Copy-SqlServerTrigger"
        "Definition"   = "Copy-DbaServerTrigger"
    },
    @{
        "AliasName"    = "Copy-SqlSharedSchedule"
        "Definition"   = "Copy-DbaAgentSharedSchedule"
    },
    @{
        "AliasName"    = "Copy-SqlSpConfigure"
        "Definition"   = "Copy-DbaSpConfigure"
    },
    @{
        "AliasName"    = "Copy-SqlSsisCatalog"
        "Definition"   = "Copy-DbaSsisCatalog"
    },
    @{
        "AliasName"    = "Copy-SqlSysDbUserObjects"
        "Definition"   = "Copy-DbaSysDbUserObject"
    },
    @{
        "AliasName"    = "Copy-SqlUserDefinedMessage"
        "Definition"   = "Copy-SqlCustomError"
    },
    @{
        "AliasName"    = "Expand-SqlTLogResponsibly"
        "Definition"   = "Expand-DbaTLogResponsibly"
    },
    @{
        "AliasName"    = "Export-SqlLogin"
        "Definition"   = "Export-DbaLogin"
    },
    @{
        "AliasName"    = "Export-SqlSpConfigure"
        "Definition"   = "Export-DbaSpConfigure"
    },
    @{
        "AliasName"    = "Export-SqlUser"
        "Definition"   = "Export-DbaUser"
    },
    @{
        "AliasName"    = "Find-SqlDuplicateIndex"
        "Definition"   = "Find-DbaDuplicateIndex"
    },
    @{
        "AliasName"    = "Find-SqlUnusedIndex"
        "Definition"   = "Find-DbaUnusedIndex"
    },
    @{
        "AliasName"    = "Get-SqlMaxMemory"
        "Definition"   = "Get-DbaMaxMemory"
    },
    @{
        "AliasName"    = "Get-SqlRegisteredServerName"
        "Definition"   = "Get-DbaRegisteredServer"
    },
    @{
        "AliasName"    = "Get-DbaRegisteredServerName"
        "Definition"   = "Get-DbaRegisteredServer"
    },
    @{
        "AliasName"    = "Get-SqlServerKey"
        "Definition"   = "Get-DbaSqlProductKey"
    },
    @{
        "AliasName"    = "Import-SqlSpConfigure"
        "Definition"   = "Import-DbaSpConfigure"
    },
    @{
        "AliasName"    = "Install-SqlWhoIsActive"
        "Definition"   = "Install-DbaWhoIsActive"
    },
    @{
        "AliasName"    = "Remove-SqlDatabaseSafely"
        "Definition"   = "Remove-DbaDatabaseSafely"
    },
    @{
        "AliasName"    = "Remove-SqlOrphanUser"
        "Definition"   = "Remove-DbaOrphanUser"
    },
    @{
        "AliasName"    = "Repair-SqlOrphanUser"
        "Definition"   = "Repair-DbaOrphanUser"
    },
    @{
        "AliasName"    = "Reset-SqlAdmin"
        "Definition"   = "Reset-DbaAdmin"
    },
    @{
        "AliasName"    = "Reset-SqlSaPassword"
        "Definition"   = "Reset-SqlAdmin"
    },
    @{
        "AliasName"    = "Restore-SqlBackupFromDirectory"
        "Definition"   = "Restore-DbaBackupFromDirectory"
    },
    @{
        "AliasName"    = "Set-SqlMaxMemory"
        "Definition"   = "Set-DbaMaxMemory"
    },
    @{
        "AliasName"    = "Set-SqlTempDbConfiguration"
        "Definition"   = "Set-DbaTempDbConfiguration"
    },
    @{
        "AliasName"    = "Show-SqlDatabaseList"
        "Definition"   = "Show-DbaDatabaseList"
    },
    @{
        "AliasName"    = "Show-SqlMigrationConstraint"
        "Definition"   = "Test-SqlMigrationConstraint"
    },
    @{
        "AliasName"    = "Show-SqlServerFileSystem"
        "Definition"   = "Show-DbaServerFileSystem"
    },
    @{
        "AliasName"    = "Show-SqlWhoIsActive"
        "Definition"   = "Invoke-DbaWhoIsActive"
    },
    @{
        "AliasName"    = "Start-SqlMigration"
        "Definition"   = "Start-DbaMigration"
    },
    @{
        "AliasName"    = "Sync-SqlLoginPermissions"
        "Definition"   = "Sync-DbaLoginPermission"
    },
    @{
        "AliasName"     = "Sync-DbaSqlLoginPermission"
        "Definition"    = "Sync-DbaLoginPermission"
    },
    @{
        "AliasName"    = "Test-SqlConnection"
        "Definition"   = "Test-DbaConnection"
    },
    @{
        "AliasName"    = "Test-SqlDiskAllocation"
        "Definition"   = "Test-DbaDiskAllocation"
    },
    @{
        "AliasName"    = "Test-SqlMigrationConstraint"
        "Definition"   = "Test-DbaMigrationConstraint"
    },
    @{
        "AliasName"    = "Test-SqlNetworkLatency"
        "Definition"   = "Test-DbaNetworkLatency"
    },
    @{
        "AliasName"    = "Test-SqlPath"
        "Definition"   = "Test-DbaSqlPath"
    },
    @{
        "AliasName"    = "Test-SqlTempDbConfiguration"
        "Definition"   = "Test-DbaTempDbConfiguration"
    },
    @{
        "AliasName"    = "Watch-SqlDbLogin"
        "Definition"   = "Watch-DbaDbLogin"
    },
    @{
        "AliasName"    = "Get-DiskSpace"
        "Definition"   = "Get-DbaDiskSpace"
    },
    @{
        "AliasName"    = "Restore-HallengrenBackup"
        "Definition"   = "Restore-SqlBackupFromDirectory"
    },
    @{
        "AliasName"    = "Get-DbaDatabaseFreeSpace"
        "Definition"   = "Get-DbaDatabaseSpace"
    },
    @{
        "AliasName"    = "Set-DbaQueryStoreConfig"
        "Definition"   = "Set-DbaDbQueryStoreOptions"
    },
    @{
        "AliasName"    = "Get-DbaQueryStoreConfig"
        "Definition"   = "Get-DbaDbQueryStoreOptions"
    },
    @{
        "AliasName"    = "Connect-DbaSqlServer"
        "Definition"   = "Connect-DbaInstance"
    },
    @{
        "AliasName"    = "Get-DbaInstance"
        "Definition"   = "Connect-DbaInstance"
    },
    @{
        "AliasName"    = "Get-DbaXEventSession"
        "Definition"   = "Get-DbaXESession"
    },
    @{
        "AliasName"    = "Get-DbaXEventSessionTarget"
        "Definition"   = "Get-DbaXESessionTarget"
    },
    @{
        "AliasName"    = "Read-DbaXEventFile"
        "Definition"   = "Read-DbaXEFile"
    },
    @{
        "AliasName"    = "Watch-DbaXEventSession"
        "Definition"   = "Watch-DbaXESession"
    },
    @{
        "AliasName"    = "Get-DbaDatabaseCertificate"
        "Definition"   = "Get-DbaDbCertificate"
    },
    @{
        "AliasName"    = "New-DbaDatabaseCertificate"
        "Definition"   = "New-DbaDbCertificate"
    },
    @{
        "AliasName"    = "Remove-DbaDatabaseCertificate"
        "Definition"   = "Remove-DbaDbCertificate"
    },
    @{
        "AliasName"    = "Restore-DbaDatabaseCertificate"
        "Definition"   = "Restore-DbaDbCertificate"
    },
    @{
        "AliasName"    = "Backup-DbaDatabaseCertificate"
        "Definition"   = "Backup-DbaDbCertificate"
    },
    @{
        "AliasName"    = "Find-DbaDatabaseGrowthEvent"
        "Definition"   = "Find-DbaDbGrowthEvent"
    },
    @{
        "AliasName"     = "Get-DbaTraceFile"
        "Definition"    = "Get-DbaTrace"
    },
    @{
        "AliasName"    = "Out-DbaDataTable"
        "Definition"   = "ConvertTo-DbaDataTable"
    },
    @{
        "AliasName"      = "Invoke-DbaSqlCmd"
        "Definition"     = "Invoke-DbaSqlQuery"
    },
    @{
        "AliasName"       = "Test-DbaVirtualLogFile"
        "Definition"      = "Test-DbaDbVirtualLogFile"
    },
    @{
        "AliasName"        = "Test-DbaFullRecoveryModel"
        "Definition"       = "Test-DbaRecoveryModel"
    }
     ,
    @{
        "AliasName"         = "Get-DbaDatabaseSnapshot"
        "Definition"        = "Get-DbaDbSnapshot"
    },
    @{
        "AliasName"         = "New-DbaDatabaseSnapshot"
        "Definition"        = "New-DbaDbSnapshot"
    },
    @{
        "AliasName"         = "Remove-DbaDatabaseSnapshot"
        "Definition"        = "Remove-DbaDbSnapshot"
    },
    @{
        "AliasName"         = "Restore-DbaDatabaseSnapshot"
        "Definition"        = "Restore-DbaDbSnapshot"
    },
    @{
        "AliasName"          = "Get-DbaSqlLog"
        "Definition"         = "Get-DbaErrorLog"
    }
     ,
    @{
        "AliasName"           = "Test-DbaValidLogin"
        "Definition"          = "Test-DbaWindowsLogin"
    },
    @{
        "AliasName"  = "Get-DbaJobCategory"
        "Definition" = "Get-DbaAgentJobCategory"
    }
) | ForEach-Object {
    if (-not (Test-Path Alias:$($_.AliasName))) { Set-Alias -Scope Global -Name $($_.AliasName) -Value $($_.Definition) }
}


# Leave forever
@(
    @{
        "AliasName"    = "Attach-DbaDatabase"
        "Definition"   = "Mount-DbaDatabase"
    },
    @{
        "AliasName"    = "Detach-DbaDatabase"
        "Definition"   = "Dismount-DbaDatabase"
    }
) | ForEach-Object {
    if (-not (Test-Path Alias:$($_.AliasName))) { Set-Alias -Scope Global -Name $($_.AliasName) -Value $($_.Definition) }
}
#endregion Aliases

#region Post-Import Cleanup
Write-ImportTime -Text "Loading Aliases"

$timeout = 20000
$timeSpent = 0
while (($script:smoRunspace.Runspace.RunspaceAvailability -eq 'Busy') -or ($script:dbatoolsConfigRunspace.Runspace.RunspaceAvailability -eq 'Busy')) {
    Start-Sleep -Milliseconds 50
    $timeSpent = $timeSpent + 50
    
    if ($timeSpent -ge $timeout) {
        Write-Warning @"
The module import has hit a timeout while waiting for some background tasks to finish.
This may result in some commands not working as intended.
This should not happen under reasonable circumstances, please file an issue at:
https://github.com/sqlcollaborative/dbatools/issues
Or contact us directly in the #dbatools channel of the SQL Server Community Slack Channel:
https://dbatools.io/slack/
Timeout waiting for temporary runspaces reached! The Module import will complete, but some things may not work as intended
"@
        $global:smoRunspace = $script:smoRunspace
        $global:dbatoolsConfigRunspace = $script:dbatoolsConfigRunspace
        break
    }
}

if ($script:smoRunspace) {
    $script:smoRunspace.Runspace.Close()
    $script:smoRunspace.Runspace.Dispose()
    $script:smoRunspace.Dispose()
    Remove-Variable -Name smoRunspace -Scope script
}

if ($script:dbatoolsConfigRunspace) {
    $script:dbatoolsConfigRunspace.Runspace.Close()
    $script:dbatoolsConfigRunspace.Runspace.Dispose()
    $script:dbatoolsConfigRunspace.Dispose()
    Remove-Variable -Name dbatoolsConfigRunspace -Scope script
}
Write-ImportTime -Text "Waiting for runspaces to finish"

if ($PSCommandPath -like "*.psm1") {
    Update-TypeData -AppendPath "$script:PSModuleRoot\xml\dbatools.Types.ps1xml"
    Write-ImportTime -Text "Loaded type extensions"
}
#. Import-ModuleFile "$script:PSModuleRoot\bin\type-extensions.ps1"
#Write-ImportTime -Text "Loaded type extensions"

[Sqlcollaborative.Dbatools.dbaSystem.SystemHost]::ModuleImported = $true;

#endregion Post-Import Cleanup


# SIG # Begin signature block
# MIIcYgYJKoZIhvcNAQcCoIIcUzCCHE8CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQU0IfF020iIPnA8RvWFpVFyqFX
# xd2ggheRMIIFGjCCBAKgAwIBAgIQAsF1KHTVwoQxhSrYoGRpyjANBgkqhkiG9w0B
# AQsFADByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD
# VQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFz
# c3VyZWQgSUQgQ29kZSBTaWduaW5nIENBMB4XDTE3MDUwOTAwMDAwMFoXDTIwMDUx
# MzEyMDAwMFowVzELMAkGA1UEBhMCVVMxETAPBgNVBAgTCFZpcmdpbmlhMQ8wDQYD
# VQQHEwZWaWVubmExETAPBgNVBAoTCGRiYXRvb2xzMREwDwYDVQQDEwhkYmF0b29s
# czCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAI8ng7JxnekL0AO4qQgt
# Kr6p3q3SNOPh+SUZH+SyY8EA2I3wR7BMoT7rnZNolTwGjUXn7bRC6vISWg16N202
# 1RBWdTGW2rVPBVLF4HA46jle4hcpEVquXdj3yGYa99ko1w2FOWzLjKvtLqj4tzOh
# K7wa/Gbmv0Si/FU6oOmctzYMI0QXtEG7lR1HsJT5kywwmgcjyuiN28iBIhT6man0
# Ib6xKDv40PblKq5c9AFVldXUGVeBJbLhcEAA1nSPSLGdc7j4J2SulGISYY7ocuX3
# tkv01te72Mv2KkqqpfkLEAQjXgtM0hlgwuc8/A4if+I0YtboCMkVQuwBpbR9/6ys
# Z+sCAwEAAaOCAcUwggHBMB8GA1UdIwQYMBaAFFrEuXsqCqOl6nEDwGD5LfZldQ5Y
# MB0GA1UdDgQWBBRcxSkFqeA3vvHU0aq2mVpFRSOdmjAOBgNVHQ8BAf8EBAMCB4Aw
# EwYDVR0lBAwwCgYIKwYBBQUHAwMwdwYDVR0fBHAwbjA1oDOgMYYvaHR0cDovL2Ny
# bDMuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC1jcy1nMS5jcmwwNaAzoDGGL2h0
# dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zaGEyLWFzc3VyZWQtY3MtZzEuY3JsMEwG
# A1UdIARFMEMwNwYJYIZIAYb9bAMBMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3
# LmRpZ2ljZXJ0LmNvbS9DUFMwCAYGZ4EMAQQBMIGEBggrBgEFBQcBAQR4MHYwJAYI
# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBOBggrBgEFBQcwAoZC
# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0U0hBMkFzc3VyZWRJ
# RENvZGVTaWduaW5nQ0EuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQAD
# ggEBANuBGTbzCRhgG0Th09J0m/qDqohWMx6ZOFKhMoKl8f/l6IwyDrkG48JBkWOA
# QYXNAzvp3Ro7aGCNJKRAOcIjNKYef/PFRfFQvMe07nQIj78G8x0q44ZpOVCp9uVj
# sLmIvsmF1dcYhOWs9BOG/Zp9augJUtlYpo4JW+iuZHCqjhKzIc74rEEiZd0hSm8M
# asshvBUSB9e8do/7RhaKezvlciDaFBQvg5s0fICsEhULBRhoyVOiUKUcemprPiTD
# xh3buBLuN0bBayjWmOMlkG1Z6i8DUvWlPGz9jiBT3ONBqxXfghXLL6n8PhfppBhn
# daPQO8+SqF5rqrlyBPmRRaTz2GQwggUwMIIEGKADAgECAhAECRgbX9W7ZnVTQ7Vv
# lVAIMA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdp
# Q2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0Rp
# Z2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0xMzEwMjIxMjAwMDBaFw0yODEw
# MjIxMjAwMDBaMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx
# GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0IFNI
# QTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EwggEiMA0GCSqGSIb3DQEBAQUA
# A4IBDwAwggEKAoIBAQD407Mcfw4Rr2d3B9MLMUkZz9D7RZmxOttE9X/lqJ3bMtdx
# 6nadBS63j/qSQ8Cl+YnUNxnXtqrwnIal2CWsDnkoOn7p0WfTxvspJ8fTeyOU5JEj
# lpB3gvmhhCNmElQzUHSxKCa7JGnCwlLyFGeKiUXULaGj6YgsIJWuHEqHCN8M9eJN
# YBi+qsSyrnAxZjNxPqxwoqvOf+l8y5Kh5TsxHM/q8grkV7tKtel05iv+bMt+dDk2
# DZDv5LVOpKnqagqrhPOsZ061xPeM0SAlI+sIZD5SlsHyDxL0xY4PwaLoLFH3c7y9
# hbFig3NBggfkOItqcyDQD2RzPJ6fpjOp/RnfJZPRAgMBAAGjggHNMIIByTASBgNV
# HRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEF
# BQcDAzB5BggrBgEFBQcBAQRtMGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRp
# Z2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0cDovL2NhY2VydHMuZGlnaWNlcnQu
# Y29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNydDCBgQYDVR0fBHoweDA6oDig
# NoY0aHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9v
# dENBLmNybDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0
# QXNzdXJlZElEUm9vdENBLmNybDBPBgNVHSAESDBGMDgGCmCGSAGG/WwAAgQwKjAo
# BggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAKBghghkgB
# hv1sAzAdBgNVHQ4EFgQUWsS5eyoKo6XqcQPAYPkt9mV1DlgwHwYDVR0jBBgwFoAU
# Reuir/SSy4IxLVGLp6chnfNtyA8wDQYJKoZIhvcNAQELBQADggEBAD7sDVoks/Mi
# 0RXILHwlKXaoHV0cLToaxO8wYdd+C2D9wz0PxK+L/e8q3yBVN7Dh9tGSdQ9RtG6l
# jlriXiSBThCk7j9xjmMOE0ut119EefM2FAaK95xGTlz/kLEbBw6RFfu6r7VRwo0k
# riTGxycqoSkoGjpxKAI8LpGjwCUR4pwUR6F6aGivm6dcIFzZcbEMj7uo+MUSaJ/P
# QMtARKUT8OZkDCUIQjKyNookAv4vcn4c10lFluhZHen6dGRrsutmQ9qzsIzV6Q3d
# 9gEgzpkxYz0IGhizgZtPxpMQBvwHgfqL2vmCSfdibqFT+hKUGIUukpHqaGxEMrJm
# oecYpJpkUe8wggZqMIIFUqADAgECAhADAZoCOv9YsWvW1ermF/BmMA0GCSqGSIb3
# DQEBBQUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAX
# BgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3Vy
# ZWQgSUQgQ0EtMTAeFw0xNDEwMjIwMDAwMDBaFw0yNDEwMjIwMDAwMDBaMEcxCzAJ
# BgNVBAYTAlVTMREwDwYDVQQKEwhEaWdpQ2VydDElMCMGA1UEAxMcRGlnaUNlcnQg
# VGltZXN0YW1wIFJlc3BvbmRlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
# ggEBAKNkXfx8s+CCNeDg9sYq5kl1O8xu4FOpnx9kWeZ8a39rjJ1V+JLjntVaY1sC
# SVDZg85vZu7dy4XpX6X51Id0iEQ7Gcnl9ZGfxhQ5rCTqqEsskYnMXij0ZLZQt/US
# s3OWCmejvmGfrvP9Enh1DqZbFP1FI46GRFV9GIYFjFWHeUhG98oOjafeTl/iqLYt
# WQJhiGFyGGi5uHzu5uc0LzF3gTAfuzYBje8n4/ea8EwxZI3j6/oZh6h+z+yMDDZb
# esF6uHjHyQYuRhDIjegEYNu8c3T6Ttj+qkDxss5wRoPp2kChWTrZFQlXmVYwk/PJ
# YczQCMxr7GJCkawCwO+k8IkRj3cCAwEAAaOCAzUwggMxMA4GA1UdDwEB/wQEAwIH
# gDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMIIBvwYDVR0g
# BIIBtjCCAbIwggGhBglghkgBhv1sBwEwggGSMCgGCCsGAQUFBwIBFhxodHRwczov
# L3d3dy5kaWdpY2VydC5jb20vQ1BTMIIBZAYIKwYBBQUHAgIwggFWHoIBUgBBAG4A
# eQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQByAHQAaQBmAGkAYwBhAHQA
# ZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBjAGUAcAB0AGEAbgBjAGUA
# IABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAgAEMAUAAvAEMAUABTACAA
# YQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQAGEAcgB0AHkAIABBAGcA
# cgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBtAGkAdAAgAGwAaQBhAGIA
# aQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBjAG8AcgBwAG8AcgBhAHQA
# ZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBlAHIAZQBuAGMAZQAuMAsG
# CWCGSAGG/WwDFTAfBgNVHSMEGDAWgBQVABIrE5iymQftHt+ivlcNK2cCzTAdBgNV
# HQ4EFgQUYVpNJLZJMp1KKnkag0v0HonByn0wfQYDVR0fBHYwdDA4oDagNIYyaHR0
# cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEQ0EtMS5jcmww
# OKA2oDSGMmh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJ
# RENBLTEuY3JsMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29j
# c3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdp
# Y2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURDQS0xLmNydDANBgkqhkiG9w0BAQUF
# AAOCAQEAnSV+GzNNsiaBXJuGziMgD4CH5Yj//7HUaiwx7ToXGXEXzakbvFoWOQCd
# 42yE5FpA+94GAYw3+puxnSR+/iCkV61bt5qwYCbqaVchXTQvH3Gwg5QZBWs1kBCg
# e5fH9j/n4hFBpr1i2fAnPTgdKG86Ugnw7HBi02JLsOBzppLA044x2C/jbRcTBu7k
# A7YUq/OPQ6dxnSHdFMoVXZJB2vkPgdGZdA0mxA5/G7X1oPHGdwYoFenYk+VVFvC7
# Cqsc21xIJ2bIo4sKHOWV2q7ELlmgYd3a822iYemKC23sEhi991VUQAOSK2vCUcIK
# SK+w1G7g9BQKOhvjjz3Kr2qNe9zYRDCCBs0wggW1oAMCAQICEAb9+QOWA63qAArr
# Pye7uhswDQYJKoZIhvcNAQEFBQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERp
# Z2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMb
# RGlnaUNlcnQgQXNzdXJlZCBJRCBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTIx
# MTExMDAwMDAwMFowYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IElu
# YzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQg
# QXNzdXJlZCBJRCBDQS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
# 6IItmfnKwkKVpYBzQHDSnlZUXKnE0kEGj8kz/E1FkVyBn+0snPgWWd+etSQVwpi5
# tHdJ3InECtqvy15r7a2wcTHrzzpADEZNk+yLejYIA6sMNP4YSYL+x8cxSIB8HqIP
# kg5QycaH6zY/2DDD/6b3+6LNb3Mj/qxWBZDwMiEWicZwiPkFl32jx0PdAug7Pe2x
# QaPtP77blUjE7h6z8rwMK5nQxl0SQoHhg26Ccz8mSxSQrllmCsSNvtLOBq6thG9I
# hJtPQLnxTPKvmPv2zkBdXPao8S+v7Iki8msYZbHBc63X8djPHgp0XEK4aH631XcK
# J1Z8D2KkPzIUYJX9BwSiCQIDAQABo4IDejCCA3YwDgYDVR0PAQH/BAQDAgGGMDsG
# A1UdJQQ0MDIGCCsGAQUFBwMBBggrBgEFBQcDAgYIKwYBBQUHAwMGCCsGAQUFBwME
# BggrBgEFBQcDCDCCAdIGA1UdIASCAckwggHFMIIBtAYKYIZIAYb9bAABBDCCAaQw
# OgYIKwYBBQUHAgEWLmh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVw
# b3NpdG9yeS5odG0wggFkBggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUA
# IABvAGYAIAB0AGgAaQBzACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4A
# cwB0AGkAdAB1AHQAZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQA
# aABlACAARABpAGcAaQBDAGUAcgB0ACAAQwBQAC8AQwBQAFMAIABhAG4AZAAgAHQA
# aABlACAAUgBlAGwAeQBpAG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUA
# bgB0ACAAdwBoAGkAYwBoACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkA
# IABhAG4AZAAgAGEAcgBlACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUA
# cgBlAGkAbgAgAGIAeQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4wCwYJYIZIAYb9bAMV
# MBIGA1UdEwEB/wQIMAYBAf8CAQAweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzAB
# hhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9j
# YWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQw
# gYEGA1UdHwR6MHgwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdp
# Q2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwOqA4oDaGNGh0dHA6Ly9jcmw0LmRpZ2lj
# ZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwHQYDVR0OBBYEFBUA
# EisTmLKZB+0e36K+Vw0rZwLNMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3z
# bcgPMA0GCSqGSIb3DQEBBQUAA4IBAQBGUD7Jtygkpzgdtlspr1LPUukxR6tWXHvV
# DQtBs+/sdR90OPKyXGGinJXDUOSCuSPRujqGcq04eKx1XRcXNHJHhZRW0eu7NoR3
# zCSl8wQZVann4+erYs37iy2QwsDStZS9Xk+xBdIOPRqpFFumhjFiqKgz5Js5p8T1
# zh14dpQlc+Qqq8+cdkvtX8JLFuRLcEwAiR78xXm8TBJX/l/hHrwCXaj++wc4Tw3G
# XZG5D2dFzdaD7eeSDY2xaYxP+1ngIw/Sqq4AfO6cQg7PkdcntxbuD8O9fAqg7iwI
# VYUiuOsYGk38KiGtSTGDR5V3cdyxG0tLHBCcdxTBnU8vWpUIKRAmMYIEOzCCBDcC
# AQEwgYYwcjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcG
# A1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBB
# c3N1cmVkIElEIENvZGUgU2lnbmluZyBDQQIQAsF1KHTVwoQxhSrYoGRpyjAJBgUr
# DgMCGgUAoHgwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMx
# DAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkq
# hkiG9w0BCQQxFgQU9R6LIcG7sWiHK3AwDl+9rXubWdAwDQYJKoZIhvcNAQEBBQAE
# ggEAIWhWGB+fjfa16YD/cFR5Q4t2+2lftO+kNpOaDYLjd91iGSzOSwQqeTFdVEeY
# 4a4X6rbdOGntnSUcKMzD9al6W+51zbo/FIytdPZWHHKuxfUb3h8vk0nJ+l0Pi6/t
# jGsPLLtUrKCh+bOETdbTVN6BGYzWOxJretqepGicvv/xor87LXfo0J64aGmzDMKc
# pVVORAf9c5o+ave1lQR6TxIj2iVfCTrHWzdlyJIeYREOBuOP2cmxyy176IzR1Ejf
# ZVQhk4wtA7W27RX9oS1cBD4/TVQFVSsxbXVAidceKajl5Zn2EiXDiFKkhACfKDP7
# FsNhgx3kYwX/2g8MklXYpHn7RKGCAg8wggILBgkqhkiG9w0BCQYxggH8MIIB+AIB
# ATB2MGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNV
# BAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3VyZWQg
# SUQgQ0EtMQIQAwGaAjr/WLFr1tXq5hfwZjAJBgUrDgMCGgUAoF0wGAYJKoZIhvcN
# AQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTgwNzIzMTk1NzE4WjAj
# BgkqhkiG9w0BCQQxFgQUVls01sk2osXSd5o+S7jrJCxKGygwDQYJKoZIhvcNAQEB
# BQAEggEALuroJ+81gJr33lB4bKsQ0vCieeIEIMoajPLgQnG1dlmuv1Pap1cPlEY+
# ur1VO7vs3g7nurL6GG9Jt1xwfWshpNOYr/2vwmN4FUpyK5+iyM+n8y7CcO7JmieX
# M4LcxcTgBaVQo66EAJar59XrwZ+8TTZUrt5EduZjm3F/8iTKrmeRg8x62Ny4nvH7
# gs+IN95bwU2vw3Iws+2DOmXFcUDz3VZZpf3ewUBw+RMg/luDTfmCH7CcpKXYh2Ap
# mqkuJQyTvKRKkYwy8dR1bYXV0qL1agvJQVOr8pDIgQ2vQ4rQ3E6l3K47lgfYJ4lu
# lHo6BQrmd2ZF9Tzttwwupuo1K6bVGw==
# SIG # End signature block
tools\dbatools\dbatools.psproj
 
tools\dbatools\dbatools.psprojs
 
tools\dbatools\en-us\about_dbatools.help.txt
TOPIC
	about_dbatools
	
SHORT DESCRIPTION
	Gives on overview over the advanced dbatools topics
	
LONG DESCRIPTION
	Welcome to the dbatools help central.
	This guide tries to show you how to best get help.
	
	Over the project's lifetime, dbatools has grown explosively and many
	features were added to cope with the sheer scale of this project. Thus,
	dbatools has become highly complex as far as a PowerShell module goes. With
	great complexity comes a great need for documentation.
	Which is where this article comes in.
	
	
	#-------------------------------------------------------------------------#
	#                           Table of Contents                             #
	#-------------------------------------------------------------------------#

	- Getting Help
	- Import Options
	
	
	#-------------------------------------------------------------------------#
	#                              Getting Help                               #
	#-------------------------------------------------------------------------#
	
	When something breaks unexpectedly, you'll probably want help. We have put
	together a guide to help you through our process:
	
	  Get-Help about_dbatools_support
	
	
	#-------------------------------------------------------------------------#
	#                             Import Options                              #
	#-------------------------------------------------------------------------#
	
	When designing the way the module is imported, we had to make several trade-
	offs. Not every decision we made is always the right way for everybody
	however, so we set up some options that let you decide, which way you
	prefer. Basically, we had to balance import time vs. security policies and
	resource cost. This guide explains what the settings do and how to set them:
	
	  Get-Help about_dbatools_importoptions
	
	
KEYWORDS
	dbatools general
tools\dbatools\en-us\about_dbatools_importoptions.help.txt
TOPIC
	about_dbatools_importoptions
	
SHORT DESCRIPTION
	Explains the various options of how to import dbatools.
	
LONG DESCRIPTION
	Welcome to our guide to importing dbatools. Since one glove fits NOT all,
	we have implemented various ways in which you can import the module. These
	options may have security implications, may be more aimed at developers, or
	may reduce resource need in return for slower import times.
	
	All these settings can be set by variable or registry key, making it
	possible to enforce these settings by policy.
	
	
	#-------------------------------------------------------------------------#
	#                                  Index                                  #
	#-------------------------------------------------------------------------#
	
	- Configuring settings
	- Serial import
	- Dotsource on import
	- Dll Copy on import
	- Build dbatools library on import
	
	
	#-------------------------------------------------------------------------#
	#                          Configuring settings                           #
	#-------------------------------------------------------------------------#
	
	Import settings can be set at three levels:
	
	- Per Script
	- Per User
	- Per Computer
	
	Per Script:
	When configured per script, declare the respective 'dbatools_XXXXX' variable
	to $true before import. The variable name is listed with the description of
	the individual options.
	Note that this must be done BEFORE importing dbatools. If the script is
	called from a powershell process that has dbatools already installed, it
	will be ignored and have no effect.
	
	Per User:
	The individual settings are DWORD properties found in the following key:
	
	  HKCU:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\System
	
	The value name is listed with the description of the individual options.
	Set the value to '1' in order to enable the option.
	
	Per Computer:
	The individual settings are DWORD properties found in the following key:
	
	  HKLM:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\System
	
	The value name is listed with the description of the individual options.
	Set the value to '1' in order to enable the option.
	
	Note on competing settings:
	This system works on an anybody-in basis. Meaning that if any single setting
	enables an import option, the others cannot disable the setting. A script
	that has been set to import serial will do so, even if the registry settings
	are explicitly set to '0'.
	
	
	#-------------------------------------------------------------------------#
	#                              Serial import                              #
	#-------------------------------------------------------------------------#
	
	  $dbatools_serialimport = $true | SerialImport: 1
	
	In order to optimize import speed, dbatools loads its components in
	parallel. This places a heavy load on CPU however. Since import speed isn't
	that important on unattended scripts, it is recommended to enable serial
	import for them, avoiding CPU spikes.
	
	
	#-------------------------------------------------------------------------#
	#                           Dotsource on import                           #
	#-------------------------------------------------------------------------#
	
	  $dbatools_dotsourcemodule = $true | DoDotSource: 1
	
	When loading the module, we read the files to memory and import straight,
	rather than importing the individual script files using dotsourcing. This is
	faster at no extra resource cost, especially on older PowerShell versions.
	The downside however is that the individual files are not checked for valid
	signatures. Company policy may require enabling this for security
	compliance. It also is often enabled by developers, as it makes debugging
	easier.
	
	
	#-------------------------------------------------------------------------#
	#                           Dll Copy on import                            #
	#-------------------------------------------------------------------------#
	
	  $dbatools_copydllmode = $true | CopyDllMode: 1
	
	The module includes a large number of dll files. These files will be locked
	as long as a PowerShell console using the module is open. This makes it
	impossible to update the module into the same folder (as older versions of
	PowerShell will do). In order to support a proper update, we support copy-
	ing the dll files and importing them from a separate folder. With that,
	users can then use our Update-Dbatools command to update the module while it
	is imported.
	Enabling this setting significantly increases import time and can be a
	security issue, only enable it for updating the module.
	
	
	#-------------------------------------------------------------------------#
	#                    Build dbatools library on import                     #
	#-------------------------------------------------------------------------#
	
	  $dbatools_alwaysbuildlibrary = $true | AlwaysBuildLibrary: 1
	
	This is a pure developer feature. Setting this will compile the dbatools
	library on every import. This feature requires an installed Visual Studio.
	This allows fast iterative tests on the library, but has no production use.
	
	
KEYWORDS
	dbatools import
tools\dbatools\en-us\about_dbatools_support.help.txt
TOPIC
	about_dbatools_support
	
SHORT DESCRIPTION
	Describes how to contact support and what information we need
	
LONG DESCRIPTION
	Welcome to the dbatools support guide.
	If you are reading this, odds are, something went wrong and you are looking
	for support. Sorry for the inconvenience - we try our best to have dbatools
	as free of bugs as we can manage. Doesn't always work out.
	
	This article describes how you can reach us and what information we need
	when you do.
	
	
	#-------------------------------------------------------------------------#
	#                           Table of Contents                             #
	#-------------------------------------------------------------------------#

	- Getting Help
	- Troubleshooting Support
	- A matter of timing
	- What happens now?
	- The world need not burn
	
	
	#-------------------------------------------------------------------------#
	#                              Getting Help                               #
	#-------------------------------------------------------------------------#
	
	When something breaks unexpectedly, you'll probably want help. Well, most of
	us in the team work on dbatools, because we a) have fun coding and b) like
	the community (there is no pay involved whatsoever). So, come see us in our
	Slack channel or file an issue on github. We will gladly attempt to help you
	with any technical issues using our module (we love our module, we'll remove
	any malfunctioning blemishes that we can!).
	
	In order to find us, you can visit us on Slack by following this link:
	https://dbatools.io/slack/
	This has the advantage of the easiest interaction and often the fastest
	resolution times for smaller issues.
	
	Alternatively, you can file an issue on github:
	https://github.com/sqlcollaborative/dbatools/issues
	(Don't worry: It's relatively straightforward and easy to do)
	
	When you describe an error, please be prepared to provide some information,
	such as what code you were trying to execute and what the error was. For
	more details on this, see the next chapter: "Troubleshooting Support"
	
	
	#-------------------------------------------------------------------------#
	#                         Troubleshooting Support                         #
	#-------------------------------------------------------------------------#
	
	When trying to figure out, what went wrong, we usually need information,
	quite a bit of it, in fact. Usual information we care about:
	- Code/line you were running
	- Output / Error / Warning received
	- Exception contents
	- Execution log
	- PowerShell Version
	- Operating System
	Now, we rarely need all of that, but often we don't know what of it we need
	before looking at it in more detail. That said, I highly recommend providing
	the first three points immediately, as they are fast to gather and already
	help a lot. We may ask for more as it comes up.
	
	# Code/line you were running #
	#----------------------------#
	
	Literally the code you executed. Make it as simple as possible and still
	produce the error. Make sure to replace sensitive data before posting it.
	If you can reduce it to a single line, that's great! (But don't sweat it if
	it's still more)
	
	# Output / Error / Warning received #
	#-----------------------------------#
	
	Generally, we catch errors and write warnings by default. Report what it
	wrote if that is the case. Same for red exception textes. Sometimes however,
	a command may just refuse to do anything at all. In that case it might just
	return nothing. If it doesn't write a warning or throw an exception, tell us
	what you expected it to do and what it did instead.
	Screenshots work very well for this.
	
	# Exception Contents #
	#--------------------#
	
	If the code threw an exception or wrote a warning, behind the scenes some
	code failed to work as designed. This failure information is usually the
	most valuable piece of information for troubleshooting. Generally, you can
	see that exception content by running the following line:
	
	  $error[0] | Select *
	
	Directly after the command failed. Screenshot it.
	
	# Execution Log #
	#---------------#
	
	Dbatools logs a lot of information about how a function processes its logic.
	You can access that information by running Get-DbatoolsLog. In order to 
	send in this information, start a new process and produce the error, then
	execute the following line:
	
	  Get-DbatoolsLog | Export-Csv messagelog.csv
	
	This will export the entire log into a csv file. You may want to edit out
	confidential information (replace it with something harmles) before
	submitting it.
	
	# PowerShell Version #
	#--------------------#
	
	We try to support versions 3-5.1 of PowerShell. Sometimes however we mess up
	and something doesn't work on all versions. Thus the actual PowerShell
	version is often of interest. You can find this information by running the
	following line:
	
	  $PSVersionTable
	
	# Operating System #
	#------------------#
	
	What version of Windows are you running your code on?
	
	# Ugh, this is a bit much, can't you just gather what you need yourself?! #
	#-------------------------------------------------------------------------#
	
	Yes, we can! We've got a command that gathers all the data that it can and
	bundles it in a zip file for submission. Beware though: It is seriously hard
	to hide or redact data from it, as it gathers a lot and stores it in an XML
	file that must be edited from PowerShell, if at all. If you don't
	particularly worry about sharing the data though, simply run this command:
	
	  New-DbatoolsSupportPackage
	
	It'll handle the gathering. Just submit the resultant zip file and we have
	all the information your console has to give.
	
	
	#-------------------------------------------------------------------------#
	#                            A matter of timing                           #
	#-------------------------------------------------------------------------#
	
	When gathering information for an error, we highly recommend taking the
	following steps:
	
	- Start a new PowerShell console
	- Perform the action necessary to reproduce the error
	- Gather data
	
	This has two key benefits:
	- The least amount of data gets collected, meaning we have to search through
	  less material to find the cause.
	- Reduces the likelyhood of accidentally sharing confidential data
	
	
	#-------------------------------------------------------------------------#
	#                            What happens now?                            #
	#-------------------------------------------------------------------------#
	
	Alright, you've gathered the data needed and told us about the issue? Good.
	What happens next usually depends on what kind of issue it is and where you
	went to report it:
	
	Reported on Slack
	- Minor bug: When you report what turns out to be a minor bug (minor
	  refering to the technical complexity of the issue, not your problem that
	  lead you to us), especially on the slack channels, odds are, on of us
	  picked it up and immediately resolved it. In this case we'll tell you
	  "it's handled" and that's it as far as you go: In the next release, the
	  issue will be gone.
	- Major issue: Some bugs are just too technically complex to solve on the
	  fly. In those cases we will ask you to file an issue or file it for you,
	  whichever you prefer. We'll try to see it resolved in a timely manner and
	  inform you as it has been resolved or ask you to try out a current build
	  and confirm it.
	
	Reported on GitHub
	The key differences between reporting a problem on Github and doing so on
	Slack is that are:
	
	a) We cannot ask questions directly as you post.
	b) Reporting the issue does not require team members to be present.
	
	We often ARE there, hanging out in Slack, but it is not automatically
	guaranteed. That being said, after you have filed an issue on GitHub, please
	keep track of that issue, as we will likely ask questions or ask you to test
	out solutions. Really, the process is mostly the same, only that since we
	can't make sure all the information we need is there when you report it, we
	may need to ask for it.
	
	
	#-------------------------------------------------------------------------#
	#                         The world need not burn                         #
	#-------------------------------------------------------------------------#
	
	Thank you for reading our advisory on receiving support. Note however: It is
	totally not necessary to wait until something breaks to contact us: Come on
	over into the slack channels and hang out, discuss sql server or the
	benefits of the	local breweries.
	
	
KEYWORDS
	dbatools general support help
tools\dbatools\functions\Add-DbaComputerCertificate.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Add-DbaComputerCertificate {
    <#
        .SYNOPSIS
            Adds a computer certificate - useful for older systems.

        .DESCRIPTION
            Adds a computer certificate from a local or remote computer.

        .PARAMETER ComputerName
            The target SQL Server. Defaults to localhost.

        .PARAMETER Credential
            Allows you to login to $ComputerName using alternative credentials.

        .PARAMETER Password
            The password for the certificate, if it is password protected.

        .PARAMETER Certificate
            The target certificate object.

        .PARAMETER Path
            The local path to the target certificate object.

        .PARAMETER Store
            Certificate store. Default is LocalMachine.

        .PARAMETER Folder
            Certificate folder. Default is My (Personal).

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .NOTES
            Tags: Certificate

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            Add-DbaComputerCertificate -ComputerName Server1 -Path C:\temp\cert.cer

            Adds the local C:\temp\cert.cer to the remote server Server1 in LocalMachine\My (Personal).

        .EXAMPLE
            Add-DbaComputerCertificate -Path C:\temp\cert.cer

            Adds the local C:\temp\cert.cer to the local computer's LocalMachine\My (Personal) certificate store.
    #>
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "High")]
    param (
        [Alias("ServerInstance", "SqlServer", "SqlInstance")]
        [DbaInstance[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [securestring]$Password,
        [parameter(ValueFromPipeline)]
        [System.Security.Cryptography.X509Certificates.X509Certificate2[]]$Certificate,
        [string]$Path,
        [string]$Store = "LocalMachine",
        [string]$Folder = "My",
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {

        if ($Path) {
            if (!(Test-Path -Path $Path)) {
                Stop-Function -Message "Path ($Path) does not exist." -Category InvalidArgument
                return
            }

            try {
                # This may be too much, but ¯\_(ツ)_/¯
                $bytes = [System.IO.File]::ReadAllBytes($Path)
                $Certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
                $Certificate.Import($bytes, $Password, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::DefaultKeySet)
            }
            catch {
                Stop-Function -Message "Can't import certificate." -ErrorRecord $_
                return
            }
        }

        #region Remoting Script
        $scriptBlock = {

            param (
                $CertificateData,

                [securestring]$Password,

                $Store,

                $Folder
            )

            $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
            $cert.Import($CertificateData, $Password, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::DefaultKeySet)
            Write-Message -Level Verbose -Message "Importing cert to $Folder\$Store"
            $tempStore = New-Object System.Security.Cryptography.X509Certificates.X509Store($Folder, $Store)
            $tempStore.Open('ReadWrite')
            $tempStore.Add($cert)
            $tempStore.Close()

            Write-Message -Level Verbose -Message "Searching Cert:\$Store\$Folder"
            Get-ChildItem "Cert:\$Store\$Folder" -Recurse | Where-Object { $_.Thumbprint -eq $cert.Thumbprint }
        }
        #endregion Remoting Script
    }
    process {
        if (Test-FunctionInterrupt) { return }

        if (-not $Certificate) {
            Stop-Function -Message "You must specify either Certificate or Path" -Category InvalidArgument
            return
        }

        foreach ($cert in $Certificate) {

            try {
                $certData = $cert.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::PFX, $Password)
            }
            catch {
                Stop-Function -Message "Can't export certificate" -ErrorRecord $_ -Continue
            }

            foreach ($computer in $ComputerName) {

                if ($PScmdlet.ShouldProcess("local", "Connecting to $computer to import cert")) {
                    try {
                        Invoke-Command2 -ComputerName $computer -Credential $Credential -ArgumentList $certdata, $Password, $Store, $Folder -ScriptBlock $scriptblock -ErrorAction Stop |
                            Select-DefaultView -Property FriendlyName, DnsNameList, Thumbprint, NotBefore, NotAfter, Subject, Issuer
                    }
                    catch {
                        Stop-Function -Message "Failure" -ErrorRecord $_ -Target $computer -Continue
                    }
                }
            }
        }
    }
}
tools\dbatools\functions\Add-DbaPfDataCollectorCounter.ps1
function Add-DbaPfDataCollectorCounter {
    <#
        .SYNOPSIS
            Adds a Performance Data Collector Counter.

        .DESCRIPTION
            Adds a Performance Data Collector Counter.

        .PARAMETER ComputerName
            The target computer. Defaults to localhost.

        .PARAMETER Credential
            Allows you to login to $ComputerName using alternative credentials. To use:

            $cred = Get-Credential, then pass $cred object to the -Credential parameter.
    
        .PARAMETER CollectorSet
            The Collector Set name.

        .PARAMETER Collector
            The Collector name.
    
        .PARAMETER Counter
            The Counter name. This must be in the form of '\Processor(_Total)\% Processor Time'.
    
        .PARAMETER InputObject
            Accepts the object output by Get-DbaPfDataCollector via the pipeline.
    
        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
                   
        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
    
        .NOTES
            Tags: PerfMon
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
    
        .LINK
            https://dbatools.io/Add-DbaPfDataCollectorCounter

        .EXAMPLE
            Add-DbaPfDataCollectorCounter -ComputerName sql2017 -CollectorSet 'System Correlation' -Collector DataCollector01  -Counter '\LogicalDisk(*)\Avg. Disk Queue Length'
    
            Adds the '\LogicalDisk(*)\Avg. Disk Queue Length' counter within the DataCollector01 collector within the System Correlation collector set on sql2017.
    
        .EXAMPLE
            Get-DbaPfDataCollector | Out-GridView -PassThru | Add-DbaPfDataCollectorCounter -Counter '\LogicalDisk(*)\Avg. Disk Queue Length' -Confirm
    
            Allows you to select which Data Collector you'd like to add the counter '\LogicalDisk(*)\Avg. Disk Queue Length' on localhost and prompts for confirmation.
    #>
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Low")]
    param (
        [DbaInstance[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias("DataCollectorSet")]
        [string[]]$CollectorSet,
        [Alias("DataCollector")]
        [string[]]$Collector,
        [Alias("Name")]
        [parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [object[]]$Counter,
        [parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [switch]$EnableException
    )
    begin {
        $setscript = {
            $setname = $args[0]; $Addxml = $args[1]
            $set = New-Object -ComObject Pla.DataCollectorSet
            $set.SetXml($Addxml)
            $set.Commit($setname, $null, 0x0003) #add or modify.
            $set.Query($setname, $Null)
        }
    }
    process {
        if ($InputObject.Credential -and (Test-Bound -ParameterName Credential -Not)) {
            $Credential = $InputObject.Credential
        }
        
        if (($InputObject | Get-Member -MemberType NoteProperty -ErrorAction SilentlyContinue).Count -le 3 -and $InputObject.ComputerName -and $InputObject.Name) {
            # it's coming from Get-DbaPfAvailableCounter
            $ComputerName = $InputObject.ComputerName
            $Counter = $InputObject.Name
            $InputObject = $null
        }
        
        if (-not $InputObject -or ($InputObject -and (Test-Bound -ParameterName ComputerName))) {
            foreach ($computer in $ComputerName) {
                $InputObject += Get-DbaPfDataCollector -ComputerName $computer -Credential $Credential -CollectorSet $CollectorSet -Collector $Collector
            }
        }
        
        if ($InputObject) {
            if (-not $InputObject.DataCollectorObject) {
                Stop-Function -Message "InputObject is not of the right type. Please use Get-DbaPfDataCollector or Get-DbaPfAvailableCounter."
                return
            }
        }
        
        foreach ($object in $InputObject) {
            $computer = $InputObject.ComputerName
            $null = Test-ElevationRequirement -ComputerName $computer -Continue
            $setname = $InputObject.DataCollectorSet
            $collectorname = $InputObject.Name
            $xml = [xml]($InputObject.DataCollectorSetXml)
            
            foreach ($countername in $counter) {
                $node = $xml.SelectSingleNode("//Name[.='$collectorname']")
                $newitem = $xml.CreateElement('Counter')
                $null = $newitem.PsBase.InnerText = $countername
                $null = $node.ParentNode.AppendChild($newitem)
                $newitem = $xml.CreateElement('CounterDisplayName')
                $null = $newitem.PsBase.InnerText = $countername
                $null = $node.ParentNode.AppendChild($newitem)
            }
            $plainxml = $xml.OuterXml
            
            if ($Pscmdlet.ShouldProcess("$computer", "Adding $counters to $collectorname with the $setname collection set")) {
                try {
                    $results = Invoke-Command2 -ComputerName $computer -Credential $Credential -ScriptBlock $setscript -ArgumentList $setname, $plainxml -ErrorAction Stop
                    Write-Message -Level Verbose -Message " $results"
                    Get-DbaPfDataCollectorCounter -ComputerName $computer -Credential $Credential -CollectorSet $setname -Collector $collectorname -Counter $counter
                }
                catch {
                    Stop-Function -Message "Failure importing $Countername to $computer." -ErrorRecord $_ -Target $computer -Continue
                }
            }
        }
    }
}
tools\dbatools\functions\Add-DbaRegisteredServer.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Add-DbaRegisteredServer {
    <#
        .SYNOPSIS
            Adds registered servers to SQL Server Central Management Server (CMS)

        .DESCRIPTION
            Adds registered servers to SQL Server Central Management Server (CMS). If you need more flexiblity, look into Import-DbaRegisteredServer which
            accepts multiple kinds of input and allows you to add reg servers from different CMSes.

        .PARAMETER SqlInstance
            The target SQL Server instance

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER ServerName
            Server Name is the actual SQL instance name (labeled Server Name)

        .PARAMETER Name
            Name is basically the nickname in SSMS CMS interface (labeled Registered Server Name)

        .PARAMETER Description
            Adds a description for the registered server

        .PARAMETER Group
            Adds the registered server to a specific group.

        .PARAMETER InputObject
            Allows the piping of a registered server group

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.

            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.

            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Chrissy LeMaire (@cl)
            Tags: RegisteredServer, CMS

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Add-DbaRegisteredServer

        .EXAMPLE
           Add-DbaRegisteredServer -SqlInstance sql2008 -ServerName sql01

           Creates a registered server on sql2008's CMS which points to the SQL Server, sql01. When scrolling in CMS, the name "sql01" will be visible.

        .EXAMPLE
           Add-DbaRegisteredServer -SqlInstance sql2008 -ServerName sql01 -Name "The 2008 Clustered Instance" -Description "HR's Dedicated SharePoint instance"

           Creates a registered server on sql2008's CMS which points to the SQL Server, sql01. When scrolling in CMS, "The 2008 Clustered Instance" will be visible.
           Clearly this is hard to explain ;)

        .EXAMPLE
           Add-DbaRegisteredServer -SqlInstance sql2008 -ServerName sql01 -Group hr\Seattle

           Creates a registered server on sql2008's CMS which points to the SQL Server, sql01. When scrolling in CMS, the name "sql01" will be visible within the Seattle group which is in the hr group.

        .EXAMPLE
           Get-DbaRegisteredServerGroup -SqlInstance sql2008 -Group hr\Seattle | Add-DbaRegisteredServer -ServerName sql01111

           Creates a registered server on sql2008's CMS which points to the SQL Server, sql01. When scrolling in CMS, the name "sql01" will be visible within the Seattle group which is in the hr group.
    #>
    [CmdletBinding(SupportsShouldProcess)]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory)]
        [string]$ServerName,
        [string]$Name = $ServerName,
        [string]$Description,
        [object]$Group,
        [parameter(ValueFromPipeline)]
        [Microsoft.SqlServer.Management.RegisteredServers.ServerGroup[]]$InputObject,
        [switch]$EnableException
    )
    process {
        if (-not $InputObject -and -not $SqlInstance) {
            Stop-Function -Message "You must either pipe in a registered server group or specify a sqlinstance"
            return
        }
        
        # double check in case a null name was bound
        if (-not $Name) {
            $Name = $ServerName
        }
        
        foreach ($instance in $SqlInstance) {
            if (($Group)) {
                if ($Group -is [Microsoft.SqlServer.Management.RegisteredServers.ServerGroup]) {
                    $InputObject += Get-DbaRegisteredServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Group $Group.Name
                }
                else {
                    $InputObject += Get-DbaRegisteredServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Group $Group
                }
            }
            else {
                $InputObject += Get-DbaRegisteredServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Id 1
            }

            if (-not $InputObject) {
                Stop-Function -Message "No matching groups found on $instance" -Continue
            }
        }

        foreach ($reggroup in $InputObject) {
            $parentserver = Get-RegServerParent -InputObject $reggroup

            if ($null -eq $parentserver) {
                Stop-Function -Message "Something went wrong and it's hard to explain, sorry. This basically shouldn't happen." -Continue
            }

            $server = $parentserver.ServerConnection.SqlConnectionObject

            if ($Pscmdlet.ShouldProcess($parentserver.SqlInstance, "Adding $ServerName")) {
                try {
                    $newserver = New-Object Microsoft.SqlServer.Management.RegisteredServers.RegisteredServer($reggroup, $Name)
                    $newserver.ServerName = $ServerName
                    $newserver.Description = $Description
                    $newserver.Create()

                    Get-DbaRegisteredServer -SqlInstance $server -Name $Name -ServerName $ServerName
                }
                catch {
                    Stop-Function -Message "Failed to add $ServerName on $($parentserver.SqlInstance)" -ErrorRecord $_ -Continue
                }
            }
        }
    }
}
tools\dbatools\functions\Add-DbaRegisteredServerGroup.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Add-DbaRegisteredServerGroup {
    <#
        .SYNOPSIS
            Adds registered server groups to SQL Server Central Management Server (CMS)

        .DESCRIPTION
            Adds registered server groups to SQL Server Central Management Server (CMS). If you need more flexiblity, look into Import-DbaRegisteredServer which
            accepts multiple kinds of input and allows you to add reg servers and groups from different CMSes.

        .PARAMETER SqlInstance
            The target SQL Server instance

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Name
            The name of the registered server group

        .PARAMETER Description
            The description for the registered server group

        .PARAMETER Group
            The SQL Server Central Management Server group. If no groups are specified, the new group will be created at the root.

        .PARAMETER InputObject
            Allows results from Get-DbaRegisteredServerGroup to be piped in

        .PARAMETER IncludeRegisteredServers
            Create the registered servers within the group, too

        .PARAMETER InputObject
            Allows results from Get-DbaRegisteredServerGroup to be piped in

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.

            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.

            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Chrissy LeMaire (@cl)
            Tags: RegisteredServer, CMS

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Add-DbaRegisteredServerGroup

        .EXAMPLE
            Add-DbaRegisteredServerGroup -SqlInstance sql2012 -Name HR

            Creates a registered server group called HR, in the root of sql2012's CMS

        .EXAMPLE
            Add-DbaRegisteredServerGroup -SqlInstance sql2012, sql2014 -Name subfolder -Group HR

            Creates a registered server group on sql2012 and sql2014 called subfolder within the HR group

    .EXAMPLE
            Get-DbaRegisteredServerGroup -SqlInstance sql2012, sql2014 -Group HR | Add-DbaRegisteredServerGroup -Name subfolder

            Creates a registered server group on sql2012 and sql2014 called subfolder within the HR group of each server
    #>
    [CmdletBinding(SupportsShouldProcess)]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory)]
        [string]$Name,
        [string]$Description,
        [string]$Group,
        [parameter(ValueFromPipeline)]
        [Microsoft.SqlServer.Management.RegisteredServers.ServerGroup[]]$InputObject,
        [switch]$EnableException
    )
    process {
        if (-not $InputObject -and -not $SqlInstance) {
            Stop-Function -Message "You must either pipe in a registered server group or specify a sqlinstance"
            return
        }
        foreach ($instance in $SqlInstance) {
            if ((Test-Bound -ParameterName Group)) {
                $InputObject += Get-DbaRegisteredServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Group $Group
            }
            else {
                $InputObject += Get-DbaRegisteredServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Id 1
            }
        }

        foreach ($reggroup in $InputObject) {
            $parentserver = Get-RegServerParent -InputObject $reggroup
            $server = $parentserver.ServerConnection.ServerInstance.SqlConnectionObject

            if ($null -eq $parentserver) {
                Stop-Function -Message "Something went wrong and it's hard to explain, sorry. This basically shouldn't happen." -Continue
            }

            if ($Pscmdlet.ShouldProcess($parentserver.SqlInstance, "Adding $Name")) {
                try {
                    $newgroup = New-Object Microsoft.SqlServer.Management.RegisteredServers.ServerGroup($reggroup, $Name)
                    $newgroup.Description = $Description
                    $newgroup.Create()
                    
                    Get-DbaRegisteredServerGroup -SqlInstance $parentserver.ServerConnection.SqlConnectionObject -Group (Get-RegServerGroupReverseParse -object $newgroup)
                    $parentserver.ServerConnection.Disconnect()
                }
                catch {
                    Stop-Function -Message "Failed to add $reggroup on $server" -ErrorRecord $_ -Continue
                }
            }
        }
    }
}
tools\dbatools\functions\Backup-DbaDatabase.ps1
function Backup-DbaDatabase {
    <#
            .SYNOPSIS
                Backup one or more SQL Sever databases from a single SQL Server SqlInstance.

            .DESCRIPTION
                Performs a backup of a specified type of 1 or more databases on a single SQL Server Instance. These backups may be Full, Differential or Transaction log backups.

            .PARAMETER SqlInstance
                The SQL Server instance hosting the databases to be backed up.

            .PARAMETER SqlCredential
                Credentials to connect to the SQL Server instance if the calling user doesn't have permission.

            .PARAMETER Database
                The database(s) to process. This list is auto-populated from the server. If unspecified, all databases will be processed.

            .PARAMETER ExcludeDatabase
                The database(s) to exclude. This list is auto-populated from the server.

            .PARAMETER BackupFileName
                The name of the file to backup to. This is only accepted for single database backups.
                If no name is specified then the backup files will be named DatabaseName_yyyyMMddHHmm (i.e. "Database1_201714022131") with the appropriate extension.

                If the same name is used repeatedly, SQL Server will add backups to the same file at an incrementing position.

                SQL Server needs permissions to write to the specified location. Path names are based on the SQL Server (C:\ is the C drive on the SQL Server, not the machine running the script).

            .PARAMETER BackupDirectory
                Path in which to place the backup files. If not specified, the backups will be placed in the default backup location for SqlInstance.
                If multiple paths are specified, the backups will be striped across these locations. This will overwrite the FileCount option.

                If the path does not exist, Sql Server will attempt to create it. Folders are created by the Sql Instance, and checks will be made for write permissions.

                File Names with be suffixed with x-of-y to enable identifying striped sets, where y is the number of files in the set and x ranges from 1 to y.

            .PARAMETER CopyOnly
                If this switch is enabled, CopyOnly backups will be taken. By default function performs a normal backup, these backups interfere with the restore chain of the database. CopyOnly backups will not interfere with the restore chain of the database.

                For more details please refer to this MSDN article - https://msdn.microsoft.com/en-us/library/ms191495.aspx

            .PARAMETER Type
                The type of SQL Server backup to perform. Accepted values are "Full", "Log", "Differential", "Diff", "Database"

            .PARAMETER FileCount
                This is the number of striped copies of the backups you wish to create.    This value is overwritten if you specify multiple Backup Directories.

            .PARAMETER CreateFolder
                If this switch is enabled, each database will be backed up into a separate folder on each of the paths specified by BackupDirectory.

            .PARAMETER CompressBackup
                If this switch is enabled, the function will try to perform a compressed backup if supported by the version and edition of SQL Server. Otherwise, this function will use the server's default setting for compression.

            .PARAMETER MaxTransferSize
                Sets the size of the unit of transfer. Values must be a multiple of 64kb.

            .PARAMETER Blocksize
                Specifies the block size to use. Must be one of 0.5KB, 1KB, 2KB, 4KB, 8KB, 16KB, 32KB or 64KB. This can be specified in bytes.
                Refer to https://msdn.microsoft.com/en-us/library/ms178615.aspx for more detail

            .PARAMETER BufferCount
                Number of I/O buffers to use to perform the operation.
                Refer to https://msdn.microsoft.com/en-us/library/ms178615.aspx for more detail

            .PARAMETER Checksum
                If this switch is enabled, the backup checksum will be calculated.

            .PARAMETER Verify
                If this switch is enabled, the backup will be verified by running a RESTORE VERIFYONLY against the SqlInstance

            .PARAMETER WithFormat
                 Formats the media as the first step of the backup operation. NOTE: This will set Initialize and SkipTapeHeader to $true.

            .PARAMETER Initialize
                 Initializes the media as part of the backup operation.

            .PARAMETER SkipTapeHeader
                 Initializes the media as part of the backup operation.

            .PARAMETER InputObject
                Internal parameter

            .PARAMETER AzureBaseUrl
                The URL to the basecontainer of an Azure storage account to write backups to.

                If specified, the only other parameters than can be used are "NoCopyOnly", "Type", "CompressBackup", "Checksum", "Verify", "AzureCredential", "CreateFolder".

            .PARAMETER AzureCredential
                The name of the credential on the SQL instance that can write to the AzureBaseUrl.

            .PARAMETER NoRecovery
                This is passed in to perform a tail log backup if needed

            .PARAMETER BuildPath
                By default this command won't attempt to create missing paths, this switch will change the behavious so that it wll

            .PARAMETER IgnoreFileChecks
                This switch stops the function from checking for the validity of paths. This can be useful if SQL Server only has read access to the backup area.
                Note, that as we can't check the path you may well end up with errors.

            .PARAMETER OutputScriptOnly
                Switch causes only the T-SQL script for the backup to be generated. Will not create any paths if they do not exist

            .PARAMETER EnableException
                By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
                This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
                Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

            .PARAMETER WhatIf
                If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

            .PARAMETER Confirm
                If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

            .NOTES
                Tags: DisasterRecovery, Backup, Restore
                Author: Stuart Moore (@napalmgram), stuart-moore.com

                Website: https://dbatools.io
                Copyright: (C) Chrissy LeMaire, [email protected]
                License: MIT https://opensource.org/licenses/MIT

            .EXAMPLE
                Backup-DbaDatabase -SqlInstance Server1 -Database HR, Finance

                This will perform a full database backup on the databases HR and Finance on SQL Server Instance Server1 to Server1's default backup directory.

            .EXAMPLE
                Backup-DbaDatabase -SqlInstance sql2016 -BackupDirectory C:\temp -Database AdventureWorks2014 -Type Full

                Backs up AdventureWorks2014 to sql2016's C:\temp folder.

            .EXAMPLE
                Backup-DbaDatabase -SqlInstance sql2016 -AzureBaseUrl https://dbatoolsaz.blob.core.windows.net/azbackups/ -AzureCredential dbatoolscred -Type Full -CreateFolder

                Performs a full backup of all databases on the sql2016 instance to their own containers under the https://dbatoolsaz.blob.core.windows.net/azbackups/ container on Azure blog storage using the sql credential "dbatoolscred" registered on the sql2016 instance.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "")] #For AzureCredential
    param (
        [parameter(ParameterSetName = "Pipe", Mandatory = $true)]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [string[]]$BackupDirectory,
        [string]$BackupFileName,
        [switch]$CopyOnly,
        [ValidateSet('Full', 'Log', 'Differential', 'Diff', 'Database')]
        [string]$Type = 'Database',
        [parameter(ParameterSetName = "NoPipe", Mandatory = $true, ValueFromPipeline = $true)]
        [object[]]$InputObject,
        [switch]$CreateFolder,
        [int]$FileCount = 0,
        [switch]$CompressBackup,
        [switch]$Checksum,
        [switch]$Verify,
        [int]$MaxTransferSize,
        [int]$BlockSize,
        [int]$BufferCount,
        [string]$AzureBaseUrl,
        [string]$AzureCredential,
        [switch]$NoRecovery,
        [switch]$BuildPath,
        [switch]$WithFormat,
        [switch]$Initialize,
        [switch]$SkipTapeHeader,
        [switch]$IgnoreFileChecks,
        [switch]$OutputScriptOnly,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {

        if ($SqlInstance.length -ne 0) {
            Write-Message -Level Verbose -Message "Connecting to $SqlInstance"
            try {
                $Server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential -AzureUnsupported
            }
            catch {
                Stop-Function -Message "Cannot connect to $SqlInstance" -ErrorRecord $_
                return
            }

            if ($Database) {
                $InputObject = $server.Databases | Where-Object Name -in $Database
            }
            else {
                $InputObject = $server.Databases | Where-Object Name -ne 'tempdb'
            }

            if ($ExcludeDatabase) {
                $InputObject = $InputObject | Where-Object Name -notin $ExcludeDatabase
            }

            if ($BackupDirectory.Count -gt 1) {
                Write-Message -Level Verbose -Message "Multiple Backup Directories, striping"
                $Filecount = $BackupDirectory.Count
            }

            if ($InputObject.Count -gt 1 -and $BackupFileName -ne '') {
                Stop-Function -Message "1 BackupFile specified, but more than 1 database."
                return
            }

            if (($MaxTransferSize % 64kb) -ne 0 -or $MaxTransferSize -gt 4mb) {
                Stop-Function -Message "MaxTransferSize value must be a multiple of 64kb and no greater than 4MB"
                return
            }
            if ($BlockSize) {
                if ($BlockSize -notin (0.5kb, 1kb, 2kb, 4kb, 8kb, 16kb, 32kb, 64kb)) {
                    Stop-Function -Message "Block size must be one of 0.5kb,1kb,2kb,4kb,8kb,16kb,32kb,64kb"
                    return
                }
            }
            if ('' -ne $AzureBaseUrl) {
                if ($null -eq $AzureCredential) {
                    Stop-Function -Message "You must provide the credential name for the Azure Storage Account"
                    return
                }
                $AzureBaseUrl = $AzureBaseUrl.Trim("/")
                $FileCount = 1
                $BackupDirectory = $AzureBaseUrl
            }

            if ($OutputScriptOnly) {
                $IgnoreFileChecks = $true
            }
        }
    }

    process {
        if (!$SqlInstance -and !$InputObject) {
            Stop-Function -Message "You must specify a server and database or pipe some databases"
            return
        }

        Write-Message -Level Verbose -Message "$($InputObject.Count) database to backup"

        foreach ($Database in $InputObject) {
            $ProgressId = Get-Random
            $failures = @()
            $dbname = $Database.Name

            if ($dbname -eq "tempdb") {
                Stop-Function -Message "Backing up tempdb not supported" -Continue
            }

            if ('Normal' -notin ($Database.Status -split ',')) {
                Stop-Function -Message "Database status not Normal. $dbname skipped." -Continue
            }

            if ($Database.DatabaseSnapshotBaseName) {
                Stop-Function -Message "Backing up snapshots not supported. $dbname skipped." -Continue
            }

            if ($null -eq $server) { $server = $Database.Parent }

            Write-Message -Level Verbose -Message "Backup database $database"

            if ($null -eq $Database.RecoveryModel) {
                $Database.RecoveryModel = $server.Databases[$Database.Name].RecoveryModel
                Write-Message -Level Verbose -Message "$dbname is in $($Database.RecoveryModel) recovery model"
            }

            # Fixes one-off cases of StackOverflowException crashes, see issue 1481
            $dbRecovery = $Database.RecoveryModel.ToString()
            if ($dbRecovery -eq 'Simple' -and $Type -eq 'Log') {
                $failreason = "$database is in simple recovery mode, cannot take log backup"
                $failures += $failreason
                Write-Message -Level Warning -Message "$failreason"
            }

            $lastfull = $database.Refresh().LastBackupDate.Year

            if ($Type -notin @("Database", "Full") -and $lastfull -eq 1) {
                $failreason = "$database does not have an existing full backup, cannot take log or differentialbackup"
                $failures += $failreason
                Write-Message -Level Warning -Message "$failreason"
            }

            if ($CopyOnly -ne $true) {
                $CopyOnly = $false
            }

            $server.ConnectionContext.StatementTimeout = 0
            $backup = New-Object Microsoft.SqlServer.Management.Smo.Backup
            $backup.Database = $Database.Name
            $Suffix = "bak"

            if ($CompressBackup) {
                if ($server.Edition -like 'Express*' -or ($server.VersionMajor -eq 10 -and $server.VersionMinor -eq 0 -and $server.Edition -notlike '*enterprise*') -or $server.VersionMajor -lt 10) {
                    Write-Message -Level Warning -Message "Compression is not supported with this version/edition of Sql Server"
                }
                else {
                    Write-Message -Level Verbose -Message "Compression enabled"
                    $backup.CompressionOption = 1
                }
            }

            if ($Checksum) {
                $backup.Checksum = $true
            }

            if ($Type -in 'Diff', 'Differential') {
                Write-Message -Level VeryVerbose -Message "Creating differential backup"
                $SMOBackuptype = "Database"
                $backup.Incremental = $true
                $outputType = 'Differential'
            }
            $Backup.NoRecovery = $false
            if ($Type -eq "Log") {
                Write-Message -Level VeryVerbose -Message "Creating log backup"
                $Suffix = "trn"
                $OutputType = 'Log'
                $SMOBackupType = 'Log'
                $Backup.NoRecovery = $NoRecovery
            }

            if ($Type -in 'Full', 'Database') {
                Write-Message -Level VeryVerbose -Message "Creating full backup"
                $SMOBackupType = "Database"
                $OutputType = 'Full'
            }

            $backup.CopyOnly = $copyonly
            $backup.Action = $SMOBackupType
            if ('' -ne $AzureBaseUrl) {
                $backup.CredentialName = $AzureCredential
            }

            Write-Message -Level Verbose -Message "Building file name"

            $BackupFinalName = ''
            $FinalBackupPath = @()
            if ('NUL' -eq $BackupFileName) {
                $FinalBackupPath += 'NUL:'
                $IgnoreFileChecks = $true
            }
            elseif ('' -ne $BackupFileName) {
                $File = New-Object System.IO.FileInfo($BackupFileName)
                $BackupFinalName = $file.Name
                $suffix = $file.extension -Replace '^\.',''
                if ( '' -ne (Split-Path $BackupFileName)) {
                    Write-Message -Level Verbose -Message "Fully qualified path passed in"
                    $FinalBackupPath += [IO.Path]::GetFullPath($file.DirectoryName)
                }
            }
            else {
                $timestamp = (Get-Date -Format yyyyMMddHHmm)
                Write-Message -Level VeryVerbose -Message "Setting filename"
                $BackupFinalName = "$($dbname)_$timestamp.$suffix"
            }

            Write-Message -Level Verbose -Message "Building backup path"
            if ($FinalBackupPath.Count -eq 0) {
                $FinalBackupPath += $BackupDirectory
            }

            if ($BackupDirectory.Count -eq 1 -and $Filecount -gt 1) {
                for ($i = 0; $i -lt ($Filecount - 1); $i++) {
                    $FinalBackupPath += $FinalBackupPath[0]
                }
            }

            if ($AzureBaseUrl -or $AzureCredential) {
                $slash = "/"
            }
            else {
                $slash = "\"
            }
            if ($FinalBackupPath.Count -gt 1) {
                $File = New-Object System.IO.FileInfo($BackupFinalName)
                for ($i = 0; $i -lt $FinalBackupPath.Count; $i++) {
                    $FinalBackupPath[$i] = $FinalBackupPath[$i] + $slash + $($File.BaseName) + "-$($i+1)-of-$FileCount.$suffix"
                }
            }
            elseif ($FinalBackupPath[0] -ne 'NUL:') {
                $FinalBackupPath[0] = $FinalBackupPath[0] + $slash + $BackupFinalName
            }

            if ($CreateFolder -and $FinalBackupPath[0] -ne 'NUL:') {
                for ($i = 0; $i -lt $FinalBackupPath.Count; $i++) {
                    $parent = [IO.Path]::GetDirectoryName($FinalBackupPath[$i])
                    $leaf = [IO.Path]::GetFileName($FinalBackupPath[$i])
                    $FinalBackupPath[$i] = [IO.Path]::Combine($parent, $dbname, $leaf)
                }
            }

            if (-not $IgnoreFileChecks -and -not $AzureBaseUrl) {
                $parentPaths = ($FinalBackupPath | ForEach-Object { Split-Path $_ } | Select-Object -Unique)
                foreach ($parentPath in $parentPaths) {
                    if (-not (Test-DbaSqlPath -SqlInstance $server -Path $parentPath)) {
                        if (($BuildPath -eq $true) -or ($CreateFolder -eq $True)) {
                            $null = New-DbaSqlDirectory -SqlInstance $server -Path $parentPath
                        }
                        else {
                            $failreason += "SQL Server cannot check if $parentPath exists. You can try disabiling this check with -IgnoreFileChecks"
                            $failures += $failreason
                            Write-Message -Level Warning -Message "$failreason"
                        }
                    }
                }
            }


            if ('' -eq $AzureBaseUrl -and $BackupDirectory) {
                $FinalBackupPath = $FinalBackupPath | ForEach-Object { [IO.Path]::GetFullPath($_) }
            }


            $script = $null
            $backupComplete = $false

            if (!$failures) {
                $Filecount = $FinalBackupPath.Count

                foreach ($backupfile in $FinalBackupPath) {
                    $device = New-Object Microsoft.SqlServer.Management.Smo.BackupDeviceItem
                    if ('' -ne $AzureBaseUrl) {
                        $device.DeviceType = "URL"
                    }
                    else {
                        $device.DeviceType = "File"
                    }
                    
                    if ($WithFormat) {
                        Write-Message -Message "WithFormat specified. Ensuring Initialize and SkipTapeHeader are set to true." -Level Verbose
                        $Initialize = $true
                        $SkipTapeHeader = $true
                    }
                    
                    $backup.FormatMedia = $WithFormat
                    $backup.Initialize = $Initialize
                    $backup.SkipTapeHeader = $SkipTapeHeader
                    $device.Name = $backupfile
                    $backup.Devices.Add($device)
                }
                $humanBackupFile = $FinalBackupPath -Join ','
                Write-Message -Level Verbose -Message "Devices added"
                $percent = [Microsoft.SqlServer.Management.Smo.PercentCompleteEventHandler] {
                    Write-Progress -id $ProgressId -activity "Backing up database $dbname to $humanBackupFile" -percentcomplete $_.Percent -status ([System.String]::Format("Progress: {0} %", $_.Percent))
                }
                $backup.add_PercentComplete($percent)
                $backup.PercentCompleteNotification = 1
                $backup.add_Complete($complete)

                if ($MaxTransferSize) {
                    $backup.MaxTransferSize = $MaxTransferSize
                }
                if ($BufferCount) {
                    $backup.BufferCount = $BufferCount
                }
                if ($BlockSize) {
                    $backup.Blocksize = $BlockSize
                }

                Write-Progress -id $ProgressId -activity "Backing up database $dbname to $humanBackupFile" -percentcomplete 0 -status ([System.String]::Format("Progress: {0} %", 0))

                try {
                    if ($Pscmdlet.ShouldProcess($server.Name, "Backing up $dbname to $humanBackupFile")) {
                        if ($OutputScriptOnly -ne $True) {
                            $Filelist = @()
                            $FileList += $server.Databases[$dbname].FileGroups.Files | Select-Object @{ Name = "FileType"; Expression = { "D" } }, @{ Name = "Type"; Expression = { "D" } }, @{ Name = "LogicalName"; Expression = { $_.Name } }, @{ Name = "PhysicalName"; Expression = { $_.FileName } }
                            $FileList += $server.Databases[$dbname].LogFiles | Select-Object @{ Name = "FileType"; Expression = { "L" } }, @{ Name = "Type"; Expression = { "L" } }, @{ Name = "LogicalName"; Expression = { $_.Name } }, @{ Name = "PhysicalName"; Expression = { $_.FileName } }

                            $backup.SqlBackup($server)
                            $script = $backup.Script($server)
                            Write-Progress -id $ProgressId -activity "Backing up database $dbname to $backupfile" -status "Complete" -Completed
                            $BackupComplete = $true
                            if ($server.VersionMajor -eq '8') {
                                $HeaderInfo = Get-BackupAncientHistory -SqlInstance $server -Database $dbname
                            }
                            else {
                                $HeaderInfo = Get-DbaBackupHistory -SqlInstance $server -Database $dbname -Last -IncludeCopyOnly | Sort-Object -Property End -Descending | Select-Object -First 1
                            }
                            $Verified = $false
                            if ($Verify) {
                                $verifiedresult = [PSCustomObject]@{
                                    SqlInstance          = $server.name
                                    DatabaseName         = $dbname
                                    BackupComplete       = $BackupComplete
                                    BackupFilesCount     = $FinalBackupPath.Count
                                    BackupFile           = (Split-Path $FinalBackupPath -Leaf)
                                    BackupFolder         = (Split-Path $FinalBackupPath | Sort-Object -Unique)
                                    BackupPath           = ($FinalBackupPath | Sort-Object -Unique)
                                    Script               = $script
                                    Notes                = $failures -join (',')
                                    FullName             = ($FinalBackupPath | Sort-Object -Unique)
                                    FileList             = $FileList
                                    SoftwareVersionMajor = $server.VersionMajor
                                    Type                 = $outputType
                                    FirstLsn             = $HeaderInfo.FirstLsn
                                    DatabaseBackupLsn    = $HeaderInfo.DatabaseBackupLsn
                                    CheckPointLsn        = $HeaderInfo.CheckPointLsn
                                    LastLsn              = $HeaderInfo.LastLsn
                                    BackupSetId          = $HeaderInfo.BackupSetId
                                    LastRecoveryForkGUID = $HeaderInfo.LastRecoveryForkGUID
                                } | Restore-DbaDatabase -SqlInstance $server -DatabaseName DbaVerifyOnly -VerifyOnly -TrustDbBackupHistory -DestinationFilePrefix DbaVerifyOnly
                                if ($verifiedResult[0] -eq "Verify successful") {
                                    $failures += $verifiedResult[0]
                                    $Verified = $true
                                }
                                else {
                                    $failures += $verifiedResult[0]
                                    $Verified = $false
                                }
                            }
                            $HeaderInfo | Add-Member -Type NoteProperty -Name BackupComplete -Value $BackupComplete
                            $HeaderInfo | Add-Member -Type NoteProperty -Name BackupFile -Value (Split-Path $FinalBackupPath -Leaf)
                            $HeaderInfo | Add-Member -Type NoteProperty -Name BackupFilesCount -Value $FinalBackupPath.Count
                            if ($FinalBackupPath[0] -eq 'NUL:') {
                                $pathresult = "NUL:"
                            }
                            else {
                                $pathresult = (Split-Path $FinalBackupPath | Sort-Object -Unique)
                            }
                            $HeaderInfo | Add-Member -Type NoteProperty -Name BackupFolder -Value $pathresult
                            $HeaderInfo | Add-Member -Type NoteProperty -Name BackupPath -Value ($FinalBackupPath | Sort-Object -Unique)
                            $HeaderInfo | Add-Member -Type NoteProperty -Name DatabaseName -Value $dbname
                            $HeaderInfo | Add-Member -Type NoteProperty -Name Notes -Value ($failures -join (','))
                            $HeaderInfo | Add-Member -Type NoteProperty -Name Script -Value $script
                            $HeaderInfo | Add-Member -Type NoteProperty -Name Verified -Value $Verified
                        }
                        else {
                            $backup.Script($server)
                        }
                    }
                }
                catch {
                    if ($NoRecovery -and ($_.Exception.InnerException.InnerException.InnerException -like '*cannot be opened. It is in the middle of a restore.')) {
                        Write-Message -Message "Exception thrown by db going into restoring mode due to recovery" -Leve Verbose
                    }
                    else {
                        Write-Progress -id $ProgressId -activity "Backup" -status "Failed" -completed
                        Stop-Function -message "Backup Failed:  $($_.Exception.Message)" -EnableException $EnableException -ErrorRecord $_ -Continue
                        $BackupComplete = $false
                    }
                }
            }
            $OutputExclude = 'FullName', 'FileList', 'SoftwareVersionMajor'
            if ($failures.Count -eq 0) {
                $OutputExclude += ('Notes', 'FirstLsn', 'DatabaseBackupLsn', 'CheckpointLsn', 'LastLsn', 'BackupSetId', 'LastRecoveryForkGuid')
            }
            $headerinfo | Select-DefaultView -ExcludeProperty $OutputExclude
            $BackupFileName = $null
        }
    }
}
tools\dbatools\functions\Backup-DbaDatabaseMasterKey.ps1
function Backup-DbaDatabaseMasterKey {
    <#
.SYNOPSIS
Backs up specified database master key.

.DESCRIPTION
Backs up specified database master key.

.PARAMETER SqlInstance
The target SQL Server instance.

.PARAMETER SqlCredential
Allows you to login to SQL Server using alternative credentials.

.PARAMETER Database
Backup master key from specific database(s).

.PARAMETER ExcludeDatabase
The database(s) to exclude - this list is auto-populated from the server.

.PARAMETER Path
The directory to export the key. If no path is specified, the default backup directory for the instance will be used.

.PARAMETER Password
The password to encrypt the exported key. This must be a SecureString.

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Certificate, Database

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
Backup-DbaDatabaseMasterKey -SqlInstance server1\sql2016

Prompts for export password, then logs into server1\sql2016 with Windows credentials then backs up all database keys to the default backup directory.

ComputerName : SERVER1
InstanceName : SQL2016
SqlInstance  : SERVER1\SQL2016
Database     : master
Filename     : E:\MSSQL13.SQL2016\MSSQL\Backup\server1$sql2016-master-20170614162311.key
Status       : Success

.EXAMPLE
Backup-DbaDatabaseMasterKey -SqlInstance Server1 -Database db1 -Path \\nas\sqlbackups\keys

Logs into sql2016 with Windows credentials then backs up db1's keys to the \\nas\sqlbackups\keys directory.

#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Security.SecureString]$Password,
        [string]$Path,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {

            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $databases = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                $databases = $databases | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }

            if (Test-Bound -ParameterName Path -Not) {
                $Path = $server.BackupDirectory
            }

            if (!$Path) {
                Stop-Function -Message "Path discovery failed. Please explicitly specify -Path" -Target $server -Continue
            }

            if (!(Test-DbaSqlPath -SqlInstance $server -Path $Path)) {
                Stop-Function -Message "$instance cannot access $Path" -Target $server -InnerErrorRecord $_ -Continue
            }

            foreach ($db in $databases) {

                if (!$db.IsAccessible) {
                    Write-Message -Level Warning -Message "Database $db is not accessible. Skipping."
                    continue
                }

                $masterkey = $db.MasterKey

                if (!$masterkey) {
                    Write-Message -Message "No master key exists in the $db database on $instance" -Target $db -Level Verbose
                    continue
                }

                # If you pass a password param, then you will not be prompted for each database, but it wouldn't be a good idea to build in insecurity
                if (Test-Bound -ParameterName Password -Not) {
                    $password = Read-Host -AsSecureString -Prompt "You must enter Service Key password for $instance"
                    $password2 = Read-Host -AsSecureString -Prompt "Type the password again"

                    if (([System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($password))) -ne ([System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($password2)))) {
                        Stop-Function -Message "Passwords do not match" -Continue
                    }
                }

                $time = (Get-Date -Format yyyMMddHHmmss)
                $dbname = $db.name
                $Path = $Path.TrimEnd("\")
                $fileinstance = $instance.ToString().Replace('\', '$')
                $filename = "$Path\$fileinstance-$dbname-$time.key"

                try {
                    $masterkey.export($filename, [System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($password)))
                    $status = "Success"
                }
                catch {
                    $status = "Failure"
                    Write-Message -Level Warning -Message "Backup failure: $($_.Exception.InnerException)"
                }

                Add-Member -Force -InputObject $masterkey -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                Add-Member -Force -InputObject $masterkey -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                Add-Member -Force -InputObject $masterkey -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                Add-Member -Force -InputObject $masterkey -MemberType NoteProperty -Name Database -value $dbname
                Add-Member -Force -InputObject $masterkey -MemberType NoteProperty -Name Filename -value $filename
                Add-Member -Force -InputObject $masterkey -MemberType NoteProperty -Name Status -value $status

                Select-DefaultView -InputObject $masterkey -Property ComputerName, InstanceName, SqlInstance, Database, 'Filename as Path', Status
            }
        }
    }
}
tools\dbatools\functions\Backup-DbaDbCertificate.ps1
function Backup-DbaDbCertificate {
    <#
        .SYNOPSIS
            Exports database certificates from SQL Server using SMO.

        .DESCRIPTION
            Exports database certificates from SQL Server using SMO and outputs the .cer and .pvk files.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Certificate
            Exports certificate that matches the name(s).

        .PARAMETER Database
            Exports the encryptor for specific database(s).

        .PARAMETER ExcludeDatabase
            Database(s) to skip when exporting encryptors.

        .PARAMETER EncryptionPassword
            A string value that specifies the system path to encrypt the private key.

        .PARAMETER DecryptionPassword
            A string value that specifies the system path to decrypt the private key.

        .PARAMETER Path
            The path to output the files to. The path is relative to the SQL Server itself. If no path is specified, the default data directory will be used.

        .PARAMETER Suffix
            The suffix of the filename of the exported certificate.

        .PARAMETER InputObject
            Certificate object

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .NOTES
            Author: Jess Pomfret (@jpomfret)
            Tags: Migration, Certificate

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            Backup-DbaDbCertificate -SqlInstance Server1
            Exports all the certificates on the specified SQL Server to the default data path for the instance.

        .EXAMPLE
            $cred = Get-Credential sqladmin
            Backup-DbaDbCertificate -SqlInstance Server1 -SqlCredential $cred

            Connects using sqladmin credential and exports all the certificates on the specified SQL Server to the default data path for the instance.

        .EXAMPLE
            Backup-DbaDbCertificate -SqlInstance Server1 -Certificate Certificate1
            Exports only the certificate named Certificate1 on the specified SQL Server to the default data path for the instance.

        .EXAMPLE
            Backup-DbaDbCertificate -SqlInstance Server1 -Database AdventureWorks
            Exports only the certificates for AdventureWorks on the specified SQL Server to the default data path for the instance.

        .EXAMPLE
            Backup-DbaDbCertificate -SqlInstance Server1 -ExcludeDatabase AdventureWorks
            Exports all certificates except those for AdventureWorks on the specified SQL Server to the default data path for the instance.

        .EXAMPLE
            Backup-DbaDbCertificate -SqlInstance Server1 -Path \\Server1\Certificates -EncryptionPassword (ConvertTo-SecureString -force -AsPlainText GoodPass1234!!)
            Exports all the certificates and private keys on the specified SQL Server.

        .EXAMPLE
            $EncryptionPassword = ConvertTo-SecureString -AsPlainText "GoodPass1234!!" -force
            $DecryptionPassword = ConvertTo-SecureString -AsPlainText "Password4567!!" -force
            Backup-DbaDbCertificate -SqlInstance Server1 -EncryptionPassword $EncryptionPassword -DecryptionPassword $DecryptionPassword
            Exports all the certificates on the specified SQL Server using the supplied DecryptionPassword, since an EncryptionPassword is specified private keys are also exported.

        .EXAMPLE
            Backup-DbaDbCertificate -SqlInstance Server1 -Path \\Server1\Certificates
            Exports all certificates on the specified SQL Server to the specified path.

        .EXAMPLE
            Backup-DbaDbCertificate -SqlInstance Server1 -Suffix DbaTools
            Exports all certificates on the specified SQL Server to the specified path, appends DbaTools to the end of the filenames.

        .EXAMPLE
            Get-DbaDbCertificate -SqlInstance sql2016 | Backup-DbaDbCertificate
            Exports all certificates found on sql2016 to the default data directory.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory, ParameterSetName = "instance")]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(ParameterSetName = "instance")]
        [object[]]$Certificate,
        [parameter(ParameterSetName = "instance")]
        [object[]]$Database,
        [parameter(ParameterSetName = "instance")]
        [object[]]$ExcludeDatabase,
        [parameter(Mandatory = $false)]
        [Security.SecureString]$EncryptionPassword,
        [parameter(Mandatory = $false)]
        [Security.SecureString]$DecryptionPassword,
        [System.IO.FileInfo]$Path,
        [string]$Suffix = "$(Get-Date -format 'yyyyMMddHHmmssms')",
        [parameter(ValueFromPipeline, ParameterSetName = "collection")]
        [Microsoft.SqlServer.Management.Smo.Certificate[]]$InputObject,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {

        if ($EncryptionPassword.Length -eq 0 -and $DecryptionPassword.Length -gt 0) {
            Stop-Function -Message "If you specify an decryption password, you must also specify an encryption password" -Target $DecryptionPassword
        }

        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Backup-DbaDatabaseCertificate

        function export-cert ($cert) {
            $certName = $cert.Name
            $db = $cert.Parent
            $server = $db.Parent
            $instance = $server.Name
            $actualPath = $Path

            if ($null -eq $actualPath) {
                $actualPath = Get-SqlDefaultPaths -SqlInstance $server -filetype Data
            }

            $fullCertName = "$actualPath\$certName$Suffix"
            $exportPathKey = "$fullCertName.pvk"

            if (!(Test-DbaSqlPath -SqlInstance $server -Path $actualPath)) {
                Stop-Function -Message "$SqlInstance cannot access $actualPath" -Target $actualPath
            }

            if ($Pscmdlet.ShouldProcess($instance, "Exporting certificate $certName from $db on $instance to $actualPath")) {
                Write-Message -Level Verbose -Message "Exporting Certificate: $certName to $fullCertName"
                try {

                    $exportPathCert = "$fullCertName.cer"

                    # because the password shouldn't go to memory...
                    if ($EncryptionPassword.Length -gt 0 -and $DecryptionPassword.Length -gt 0) {

                        Write-Message -Level Verbose -Message "Both passwords passed in. Will export both cer and pvk."

                        $cert.export(
                            $exportPathCert,
                            $exportPathKey,
                            [System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($EncryptionPassword)),
                            [System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($DecryptionPassword))
                        )
                    }
                    elseif ($EncryptionPassword.Length -gt 0 -and $DecryptionPassword.Length -eq 0) {
                        Write-Message -Level Verbose -Message "Only encryption password passed in. Will export both cer and pvk."

                        $cert.export(
                            $exportPathCert,
                            $exportPathKey,
                            [System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($EncryptionPassword))
                        )
                    }
                    else {
                        Write-Message -Level Verbose -Message "No passwords passed in. Will export just cer."
                        $exportPathKey = "Password required to export key"
                        $cert.export($exportPathCert)
                    }

                    [pscustomobject]@{
                        ComputerName   = $server.ComputerName
                        InstanceName   = $server.ServiceName
                        SqlInstance    = $server.DomainInstanceName
                        Database       = $db.Name
                        Certificate    = $certName
                        Path           = $exportPathCert
                        Key            = $exportPathKey
                        ExportPath     = $exportPathCert
                        ExportKey      = $exportPathKey
                        exportPathCert = $exportPathCert
                        exportPathKey  = $exportPathKey
                        Status         = "Success"
                    } | Select-DefaultView -ExcludeProperty exportPathCert, exportPathKey, ExportPath, ExportKey
                }
                catch {

                    if ($_.Exception.InnerException) {
                        $exception = $_.Exception.InnerException.ToString() -Split "System.Data.SqlClient.SqlException: "
                        $exception = ($exception[1] -Split "at Microsoft.SqlServer.Management.Common.ConnectionManager")[0]
                    }
                    else {
                        $exception = $_.Exception
                    }
                    [pscustomobject]@{
                        ComputerName   = $server.ComputerName
                        InstanceName   = $server.ServiceName
                        SqlInstance    = $server.DomainInstanceName
                        Database       = $db.Name
                        Certificate    = $certName
                        Path           = $exportPathCert
                        Key            = $exportPathKey
                        ExportPath     = $exportPathCert
                        ExportKey      = $exportPathKey
                        exportPathCert = $exportPathCert
                        exportPathKey  = $exportPathKey
                        Status         = "Failure: $exception"
                    } | Select-DefaultView -ExcludeProperty exportPathCert, exportPathKey, ExportPath, ExportKey
                    Stop-Function -Message "$certName from $db on $instance cannot be exported." -Continue -Target $cert -ErrorRecord $_
                }
            }
        }
    }

    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                return
            }
            $databases = Get-DbaDatabase -SqlInstance $server | Where-Object IsAccessible

            if ($Database) {
                $databases = $databases | Where-Object Name -in $Database
            }
            if ($ExcludeDatabase) {
                $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }
            foreach ($db in $databases.Name) {
                $DBInputObject = Get-DbaDbCertificate -SqlInstance $server -Database $db
                if ($Certificate) {
                    $InputObject += $DBInputObject | Where-Object Name -In $Certificate
                }
                else {
                    $InputObject += $DBInputObject | Where-Object Name -NotLike "##*"
                }
                if (!$InputObject) {
                    Write-Message -Level Output -Message "No certificates found to export in $db."
                    continue
                }
            }

        }

        foreach ($cert in $InputObject) {
            if ($cert.Name.StartsWith("##")) {
                Write-Message -Level Output -Message "Skipping system cert $cert"
            }
            else {
                export-cert $cert
            }
        }
    }
}
tools\dbatools\functions\Clear-DbaPlanCache.ps1
function Clear-DbaPlanCache {
    <#
        .SYNOPSIS
            Removes adhoc and prepared plan caches is single use plans are over defined threshold.

        .DESCRIPTION
            Checks ahoc and prepared plan cache for each database, if over 100 MBs removes from the cache.

            This command automates that process.

            References: https://www.sqlskills.com/blogs/kimberly/plan-cache-adhoc-workloads-and-clearing-the-single-use-plan-cache-bloat/

        .PARAMETER SqlInstance
            The target SQL Server instance.

        .PARAMETER SqlCredential
           Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Threshold
            Memory used threshold.

        .PARAMETER InputObject
            Enables results to be piped in from Get-DbaPlanCache.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Memory
            Author: Tracy Boggiano, databasesuperhero.com

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: GNU GPL v3 https://opensource.org/licenses/GPL-3.0

        .LINK
            https://dbatools.io/Clear-DbaPlanCache

        .EXAMPLE
            Clear-DbaPlanCache -SqlInstance sql2017 -Threshold 200

            Logs into the SQL Server instance "sql2017" and removes plan caches if over 200 MB.

        .EXAMPLE
            Clear-DbaPlanCache -SqlInstance sql2017 -SqlCredential (Get-Credential sqladmin)

            Logs into the SQL instance using the SQL Login 'sqladmin' and then Windows instance as 'ad\sqldba'
            and removes if Threshold over 100 MB.
    #>
    [CmdletBinding(SupportsShouldProcess)]
    Param (
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [int]$Threshold = 100,
        [parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            $InputObject += Get-DbaPlanCache -SqlInstance $instance -SqlCredential $SqlCredential
        }

        foreach ($result in $InputObject) {
            if ($result.MB -ge $Threshold) {
                if ($Pscmdlet.ShouldProcess($($result.SqlInstance), "Cleared SQL Plans plan cache")) {
                    $server.Query("DBCC FREESYSTEMCACHE('SQL Plans')")
                    [pscustomobject]@{
                        ComputerName = $result.ComputerName
                        InstanceName = $result.InstanceName
                        SqlInstance  = $result.SqlInstance
                        Size         = $result.Size
                        Status       = "Plan cache cleared"
                    }
                }
            }
            else {
                if ($Pscmdlet.ShouldProcess($($result.SqlInstance), "Results $($result.Size) below threshold")) {
                    [pscustomobject]@{
                        ComputerName = $result.ComputerName
                        InstanceName = $result.InstanceName
                        SqlInstance  = $result.SqlInstance
                        Size         = $result.Size
                        Status       = "Plan cache size below threshold ($Threshold) "
                    }
                    Write-Message -Level Verbose -Message "Plan cache size below threshold ($Threshold) "
                }
            }
        }
    }
}
tools\dbatools\functions\Clear-DbaSqlConnectionPool.ps1
function Clear-DbaSqlConnectionPool {
    <#
    .SYNOPSIS
        Resets (or empties) the connection pool.

    .DESCRIPTION
        This command resets (or empties) the connection pool.

        If there are connections in use at the time of the call, they are marked appropriately and will be discarded (instead of being returned to the pool) when Close() is called on them.

        Ref: https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.clearallpools(v=vs.110).aspx

    .PARAMETER ComputerName
        Target computer(s). If no computer name is specified, the local computer is targeted.

    .PARAMETER Credential
        Alternate credential object to use for accessing the target computer(s).

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Tags: Connection

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Clear-DbaSqlConnectionPool

    .EXAMPLE
        Clear-DbaSqlConnectionPool

        Clears all local connection pools.

    .EXAMPLE
        Clear-DbaSqlConnectionPool -ComputerName workstation27

        Clears all connection pools on workstation27.
#>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Alias("cn", "host", "Server")]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [switch][Alias('Silent')]
        $EnableException
    )
    
    process {
        # TODO: https://jamessdixon.wordpress.com/2013/01/22/ado-net-and-connection-pooling
        
        foreach ($computer in $ComputerName) {
            try {
                if (-not $computer.IsLocalhost) {
                    Write-Message -Level Verbose -Message "Clearing all pools on remote computer $computer"
                    if (Test-Bound 'Credential') {
                        Invoke-Command2 -ComputerName $computer -Credential $Credential -ScriptBlock { [System.Data.SqlClient.SqlConnection]::ClearAllPools() }
                    }
                    else {
                        Invoke-Command2 -ComputerName $computer -ScriptBlock { [System.Data.SqlClient.SqlConnection]::ClearAllPools() }
                    }
                }
                else {
                    Write-Message -Level Verbose -Message "Clearing all local pools"
                    if (Test-Bound 'Credential') {
                        Invoke-Command2 -Credential $Credential -ScriptBlock { [System.Data.SqlClient.SqlConnection]::ClearAllPools() }
                    }
                    else {
                        Invoke-Command2 -ScriptBlock { [System.Data.SqlClient.SqlConnection]::ClearAllPools() }
                    }
                }
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_ -Target $computer -Continue
            }
        }
    }
}
tools\dbatools\functions\Clear-DbaWaitStatistics.ps1
function Clear-DbaWaitStatistics {
    <#
    .SYNOPSIS
        Clears wait statistics

    .DESCRIPTION
        Reset the aggregated statistics - basically just executes DBCC SQLPERF (N'sys.dm_os_wait_stats', CLEAR)

    .PARAMETER SqlInstance
        Allows you to specify a comma separated list of servers to query.

    .PARAMETER SqlCredential
        Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

    .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Tags: WaitStatistic
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Clear-DbaWaitStatistics

    .EXAMPLE
        Clear-DbaWaitStatistics -SqlInstance sql2008, sqlserver2012
        After confirmation, clears wait stats on servers sql2008 and sqlserver2012

    .EXAMPLE
        Clear-DbaWaitStatistics -SqlInstance sql2008, sqlserver2012 -Confirm:$false
        Clears wait stats on servers sql2008 and sqlserver2012, without prompting
    #>
    [CmdletBinding(ConfirmImpact = 'High', SupportsShouldProcess)]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($Pscmdlet.ShouldProcess($instance, "Performing CLEAR of sys.dm_os_wait_stats")) {
                try {
                    $server.Query("DBCC SQLPERF (N'sys.dm_os_wait_stats', CLEAR);")
                    $status = "Success"
                }
                catch {
                    $status = $_.Exception
                }

                [PSCustomObject]@{
                    ComputerName = $server.ComputerName
                    InstanceName = $server.ServiceName
                    SqlInstance  = $server.DomainInstanceName
                    Status       = $status
                }
            }
        }
    }
}
tools\dbatools\functions\Connect-DbaInstance.ps1
function Connect-DbaInstance {
    <#
    .SYNOPSIS
        Creates a robust SMO SQL Server object.

    .DESCRIPTION
        This command is robust because it initializes properties that do not cause enumeration by default. It also supports both Windows and SQL Server authentication methods, and detects which to use based upon the provided credentials.

        By default, this command also sets the connection's ApplicationName property  to "dbatools PowerShell module - dbatools.io - custom connection". If you're doing anything that requires profiling, you can look for this client name.

        Alternatively, you can pass in whichever client name you'd like using the -ClientName parameter. There are a ton of other parameters for you to explore as well.

        See https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.connectionstring.aspx
        and https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnectionstringbuilder.aspx,
        and https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.aspx

        To execute SQL commands, you can use $server.ConnectionContext.ExecuteReader($sql) or $server.Databases['master'].ExecuteNonQuery($sql)

    .PARAMETER SqlInstance
        SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

    .PARAMETER Credential
        Credential object used to connect to the SQL Server Instance as a different user. This can be a Windows or SQL Server account. Windows users are determined by the existence of a backslash, so if you are intending to use an alternative Windows connection instead of a SQL login, ensure it contains a backslash.

    .PARAMETER Database
        The database(s) to process. This list is auto-populated from the server.

    .PARAMETER AccessToken
        Gets or sets the access token for the connection.

    .PARAMETER AppendConnectionString
        Appends to the current connection string. Note that you cannot pass authentication information using this method. Use -SqlInstance and optionally -SqlCredential to set authentication information.

    .PARAMETER ApplicationIntent
        Declares the application workload type when connecting to a server.

        Valid values are "ReadOnly" and "ReadWrite".

    .PARAMETER BatchSeparator
        A string to separate groups of SQL statements being executed. By default, this is "GO".

    .PARAMETER ClientName
        By default, this command sets the client's ApplicationName property to "dbatools PowerShell module - dbatools.io - custom connection" if you're doing anything that requires profiling, you can look for this client name. Using -ClientName allows you to set your own custom client application name.

    .PARAMETER ConnectTimeout
        The length of time (in seconds) to wait for a connection to the server before terminating the attempt and generating an error.

        Valid values are integers between 0 and 2147483647.

        When opening a connection to a Azure SQL Database, set the connection timeout to 30 seconds.

    .PARAMETER EncryptConnection
        If this switch is enabled, SQL Server uses SSL encryption for all data sent between the client and server if the server has a certificate installed.

        For more information, see Connection String Syntax. https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/connection-string-syntax

        Beginning in .NET Framework 4.5, when TrustServerCertificate is false and Encrypt is true, the server name (or IP address) in a SQL Server SSL certificate must exactly match the server name (or IP address) specified in the connection string. Otherwise, the connection attempt will fail. For information about support for certificates whose subject starts with a wildcard character (*), see Accepted wildcards used by server certificates for server authentication. https://support.microsoft.com/en-us/help/258858/accepted-wildcards-used-by-server-certificates-for-server-authenticati

    .PARAMETER FailoverPartner
        The name of the failover partner server where database mirroring is configured.

        If the value of this key is "" (an empty string), then Initial Catalog must be present in the connection string, and its value must not be "".

        The server name can be 128 characters or less.

        If you specify a failover partner but the failover partner server is not configured for database mirroring and the primary server (specified with the Server keyword) is not available, then the connection will fail.

        If you specify a failover partner and the primary server is not configured for database mirroring, the connection to the primary server (specified with the Server keyword) will succeed if the primary server is available.


    .PARAMETER IsActiveDirectoryUniversalAuth
        If this switch is enabled, the connection will be configured to use Azure Active Directory authentication.

    .PARAMETER LockTimeout
        Sets the time in seconds required for the connection to time out when the current transaction is locked.

    .PARAMETER MaxPoolSize
        Sets the maximum number of connections allowed in the connection pool for this specific connection string.

    .PARAMETER MinPoolSize
        Sets the minimum number of connections allowed in the connection pool for this specific connection string.

    .PARAMETER MultipleActiveResultSets
        If this switch is enabled, an application can maintain multiple active result sets (MARS).

        If this switch is not enabled, an application must process or cancel all result sets from one batch before it can execute any other batch on that connection.

    .PARAMETER MultiSubnetFailover
        If this switch is enabled, and your application is connecting to an AlwaysOn availability group (AG) on different subnets, detection of and connection to the currently active server will be faster. For more information about SqlClient support for Always On Availability Groups, see https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql/sqlclient-support-for-high-availability-disaster-recovery

    .PARAMETER NetworkProtocol
        Explicitly sets the network protocol used to connect to the server.

        Valid values are "TcpIp","NamedPipes","Multiprotocol","AppleTalk","BanyanVines","Via","SharedMemory" and "NWLinkIpxSpx"

    .PARAMETER NonPooledConnection
        If this switch is enabled, a non-pooled connection will be requested.

    .PARAMETER PacketSize
        Sets the size in bytes of the network packets used to communicate with an instance of SQL Server. Must match at server.

    .PARAMETER PooledConnectionLifetime
        When a connection is returned to the pool, its creation time is compared with the current time and the connection is destroyed if that time span (in seconds) exceeds the value specified by Connection Lifetime. This is useful in clustered configurations to force load balancing between a running server and a server just brought online.

        A value of zero (0) causes pooled connections to have the maximum connection timeout.

    .PARAMETER SqlExecutionModes
        The SqlExecutionModes enumeration contains values that are used to specify whether the commands sent to the referenced connection to the server are executed immediately or saved in a buffer.

        Valid values include "CaptureSql", "ExecuteAndCaptureSql" and "ExecuteSql".

    .PARAMETER StatementTimeout
        Sets the number of seconds a statement is given to run before failing with a timeout error.

    .PARAMETER TrustServerCertificate
        When this switch is enabled, the channel will be encrypted while bypassing walking the certificate chain to validate trust.

    .PARAMETER WorkstationId
        Sets the name of the workstation connecting to SQL Server.

    .PARAMETER SqlConnectionOnly
        Instead of returning a rich SMO server object, this command will only return a SqlConnection object when setting this switch.

    .NOTES
        Tags: Connect, Connection
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Connect-DbaInstance

    .EXAMPLE
        Connect-DbaInstance -SqlInstance sql2014

        Creates an SMO Server object that connects using Windows Authentication

    .EXAMPLE
        $wincred = Get-Credential ad\sqladmin
        Connect-DbaInstance -SqlInstance sql2014 -Credential $wincred

        Creates an SMO Server object that connects using alternative Windows credentials

    .EXAMPLE
        $sqlcred = Get-Credential sqladmin
        $server = Connect-DbaInstance -SqlInstance sql2014 -Credential $sqlcred

        Login to sql2014 as SQL login sqladmin.

    .EXAMPLE
        $server = Connect-DbaInstance -SqlInstance sql2014 -ClientName "my connection"

        Creates an SMO Server object that connects using Windows Authentication and uses the client name "my connection". So when you open up profiler or use extended events, you can search for "my connection".

    .EXAMPLE
        $server = Connect-DbaInstance -SqlInstance sql2014 -AppendConnectionString "Packet Size=4096;AttachDbFilename=C:\MyFolder\MyDataFile.mdf;User Instance=true;"

        Creates an SMO Server object that connects to sql2014 using Windows Authentication, then it sets the packet size (this can also be done via -PacketSize) and other connection attributes.

    .EXAMPLE
        $server = Connect-DbaInstance -SqlInstance sql2014 -NetworkProtocol TcpIp -MultiSubnetFailover

        Creates an SMO Server object that connects using Windows Authentication that uses TCP/IP and has MultiSubnetFailover enabled.

    .EXAMPLE
        $server = Connect-DbaInstance sql2016 -ApplicationIntent ReadOnly

        Connects with ReadOnly ApplicationIntent.
#>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("SqlCredential")]
        [PSCredential]$Credential,
        [object[]]$Database,
        [string]$AccessToken,
        [ValidateSet('ReadOnly', 'ReadWrite')]
        [string]$ApplicationIntent,
        [string]$BatchSeparator,
        [string]$ClientName = "dbatools PowerShell module - dbatools.io - custom connection",
        [int]$ConnectTimeout = ([Sqlcollaborative.Dbatools.Connection.ConnectionHost]::SqlConnectionTimeout),
        [switch]$EncryptConnection,
        [string]$FailoverPartner,
        [switch]$IsActiveDirectoryUniversalAuth,
        [int]$LockTimeout,
        [int]$MaxPoolSize,
        [int]$MinPoolSize,
        [switch]$MultipleActiveResultSets,
        [switch]$MultiSubnetFailover,
        [ValidateSet('TcpIp', 'NamedPipes', 'Multiprotocol', 'AppleTalk', 'BanyanVines', 'Via', 'SharedMemory', 'NWLinkIpxSpx')]
        [string]$NetworkProtocol,
        [switch]$NonPooledConnection,
        [int]$PacketSize,
        [int]$PooledConnectionLifetime,
        [ValidateSet('CaptureSql', 'ExecuteAndCaptureSql', 'ExecuteSql')]
        [string]$SqlExecutionModes,
        [int]$StatementTimeout,
        [switch]$TrustServerCertificate,
        [string]$WorkstationId,
        [string]$AppendConnectionString,
        [switch]$SqlConnectionOnly
    )
    begin {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Connect-DbaSqlServer
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-DbaInstance

        $loadedSmoVersion = [AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.Fullname -like "Microsoft.SqlServer.SMO,*" }

        if ($loadedSmoVersion) {
            $loadedSmoVersion = $loadedSmoVersion | ForEach-Object {
                if ($_.Location -match "__") {
                    ((Split-Path (Split-Path $_.Location) -Leaf) -split "__")[0]
                }
                else {
                    ((Get-ChildItem -Path $_.Location).VersionInfo.ProductVersion)
                }
            }
        }
        #'PrimaryFilePath' seems the culprit for slow SMO on databases
        $Fields2000_Db = 'Collation', 'CompatibilityLevel', 'CreateDate', 'ID', 'IsAccessible', 'IsFullTextEnabled', 'IsSystemObject', 'IsUpdateable', 'LastBackupDate', 'LastDifferentialBackupDate', 'LastLogBackupDate', 'Name', 'Owner', 'ReadOnly', 'RecoveryModel', 'ReplicationOptions', 'Status', 'Version'
        $Fields200x_Db = $Fields2000_Db + @('BrokerEnabled', 'DatabaseSnapshotBaseName', 'IsMirroringEnabled', 'Trustworthy')
        $Fields201x_Db = $Fields200x_Db + @('ActiveConnections', 'AvailabilityDatabaseSynchronizationState', 'AvailabilityGroupName', 'ContainmentType', 'EncryptionEnabled')

        $Fields2000_Login = 'CreateDate' , 'DateLastModified' , 'DefaultDatabase' , 'DenyWindowsLogin' , 'IsSystemObject' , 'Language' , 'LanguageAlias' , 'LoginType' , 'Name' , 'Sid' , 'WindowsLoginAccessType'
        $Fields200x_Login = $Fields2000_Login + @('AsymmetricKey', 'Certificate', 'Credential', 'ID', 'IsDisabled', 'IsLocked', 'IsPasswordExpired', 'MustChangePassword', 'PasswordExpirationEnabled', 'PasswordPolicyEnforced')
        $Fields201x_Login = $Fields200x_Login + @('PasswordHashAlgorithm')


    }
    process {
        foreach ($instance in $SqlInstance) {
            if ($instance.Type -like "Server") {
                if ($instance.InputObject.ConnectionContext.IsOpen -eq $false) {
                    $instance.InputObject.ConnectionContext.Connect()
                }
                if ($SqlConnectionOnly) { return $instance.InputObject.ConnectionContext.SqlConnectionObject }
                else { return $instance.InputObject }
            }
            if ($instance.Type -like "SqlConnection") {
                $server = New-Object Microsoft.SqlServer.Management.Smo.Server($instance.InputObject)

                if ($server.ConnectionContext.IsOpen -eq $false) {
                    $server.ConnectionContext.Connect()
                }
                if ($SqlConnectionOnly) { return $server.ConnectionContext.SqlConnectionObject }
                else {
                    if (-not $server.ComputerName) {
                        $parsedcomputername = $server.NetName
                        if (-not $parsedcomputername) {
                            $parsedcomputername = ([dbainstance]$instance).ComputerName
                        }
                        Add-Member -InputObject $server -NotePropertyName ComputerName -NotePropertyValue $parsedcomputername -Force
                    }
                    return $server
                }
            }

            if ($instance.IsConnectionString) { $server = New-Object Microsoft.SqlServer.Management.Smo.Server($instance.InputObject) }
            else { $server = New-Object Microsoft.SqlServer.Management.Smo.Server $instance.FullSmoName }

            if ($AppendConnectionString) {
                $connstring = $server.ConnectionContext.ConnectionString
                $server.ConnectionContext.ConnectionString = "$connstring;$appendconnectionstring"
                $server.ConnectionContext.Connect()
            }
            else {

                $server.ConnectionContext.ApplicationName = $ClientName

                if (Test-Bound -ParameterName 'AccessToken') { $server.ConnectionContext.AccessToken = $AccessToken }
                if (Test-Bound -ParameterName 'BatchSeparator') { $server.ConnectionContext.BatchSeparator = $BatchSeparator }
                if (Test-Bound -ParameterName 'ConnectTimeout') { $server.ConnectionContext.ConnectTimeout = $ConnectTimeout }
                if (Test-Bound -ParameterName 'Database') { $server.ConnectionContext.DatabaseName = $Database }
                if (Test-Bound -ParameterName 'EncryptConnection') { $server.ConnectionContext.EncryptConnection = $true }
                if (Test-Bound -ParameterName 'IsActiveDirectoryUniversalAuth') { $server.ConnectionContext.IsActiveDirectoryUniversalAuth = $true }
                if (Test-Bound -ParameterName 'LockTimeout') { $server.ConnectionContext.LockTimeout = $LockTimeout }
                if (Test-Bound -ParameterName 'MaxPoolSize') { $server.ConnectionContext.MaxPoolSize = $MaxPoolSize }
                if (Test-Bound -ParameterName 'MinPoolSize') { $server.ConnectionContext.MinPoolSize = $MinPoolSize }
                if (Test-Bound -ParameterName 'MultipleActiveResultSets') { $server.ConnectionContext.MultipleActiveResultSets = $true }
                if (Test-Bound -ParameterName 'NetworkProtocol') { $server.ConnectionContext.NetworkProtocol = $NetworkProtocol }
                if (Test-Bound -ParameterName 'NonPooledConnection') { $server.ConnectionContext.NonPooledConnection = $true }
                if (Test-Bound -ParameterName 'PacketSize') { $server.ConnectionContext.PacketSize = $PacketSize }
                if (Test-Bound -ParameterName 'PooledConnectionLifetime') { $server.ConnectionContext.PooledConnectionLifetime = $PooledConnectionLifetime }
                if (Test-Bound -ParameterName 'StatementTimeout') { $server.ConnectionContext.StatementTimeout = $StatementTimeout }
                if (Test-Bound -ParameterName 'SqlExecutionModes') { $server.ConnectionContext.SqlExecutionModes = $SqlExecutionModes }
                if (Test-Bound -ParameterName 'TrustServerCertificate') { $server.ConnectionContext.TrustServerCertificate = $true }
                if (Test-Bound -ParameterName 'WorkstationId') { $server.ConnectionContext.WorkstationId = $WorkstationId }

                $connstring = $server.ConnectionContext.ConnectionString
                if (Test-Bound -ParameterName 'MultiSubnetFailover') { $connstring = "$connstring;MultiSubnetFailover=True" }
                if (Test-Bound -ParameterName 'FailoverPartner') { $connstring = "$connstring;Failover Partner=$FailoverPartner" }
                if (Test-Bound -ParameterName 'ApplicationIntent') { $connstring = "$connstring;ApplicationIntent=$ApplicationIntent" }

                if ($connstring -ne $server.ConnectionContext.ConnectionString) {
                    $server.ConnectionContext.ConnectionString = $connstring
                }

                try {
                    if ($null -ne $Credential.username) {
                        $username = ($Credential.username).TrimStart("\")

                        if ($username -like "*\*") {
                            $username = $username.Split("\")[1]
                            $authtype = "Windows Authentication with Credential"
                            $server.ConnectionContext.LoginSecure = $true
                            $server.ConnectionContext.ConnectAsUser = $true
                            $server.ConnectionContext.ConnectAsUserName = $username
                            $server.ConnectionContext.ConnectAsUserPassword = ($Credential).GetNetworkCredential().Password
                        }
                        else {
                            $authtype = "SQL Authentication"
                            $server.ConnectionContext.LoginSecure = $false
                            $server.ConnectionContext.set_Login($username)
                            $server.ConnectionContext.set_SecurePassword($Credential.Password)
                        }
                    }

                    if ($NonPooled) {
                        $server.ConnectionContext.Connect()
                    }
                    elseif ($authtype -eq "Windows Authentication with Credential") {
                        # Make it connect in a natural way, hard to explain.
                        $null = $server.IsMemberOfWsfcCluster
                    }
                    else {
                        $server.ConnectionContext.SqlConnectionObject.Open()
                    }
                }
                catch {
                    $message = $_.Exception.InnerException.InnerException
                    $message = $message.ToString()
                    $message = ($message -Split '-->')[0]
                    $message = ($message -Split 'at System.Data.SqlClient')[0]
                    $message = ($message -Split 'at System.Data.ProviderBase')[0]
                    throw "Can't connect to $instance`: $message "
                }

            }

            if ($loadedSmoVersion -ge 11) {
                if ($server.VersionMajor -eq 8) {
                    # 2000
                    $initFieldsDb = New-Object System.Collections.Specialized.StringCollection
                    [void]$initFieldsDb.AddRange($Fields2000_Db)
                    $initFieldsLogin = New-Object System.Collections.Specialized.StringCollection
                    [void]$initFieldsLogin.AddRange($Fields2000_Login)
                    $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Database], $initFieldsDb)
                    $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Login], $initFieldsLogin)
                }

                elseif ($server.VersionMajor -eq 9 -or $server.VersionMajor -eq 10) {
                    # 2005 and 2008
                    $initFieldsDb = New-Object System.Collections.Specialized.StringCollection
                    [void]$initFieldsDb.AddRange($Fields200x_Db)
                    $initFieldsLogin = New-Object System.Collections.Specialized.StringCollection
                    [void]$initFieldsLogin.AddRange($Fields200x_Login)
                    $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Database], $initFieldsDb)
                    $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Login], $initFieldsLogin)
                }

                else {
                    # 2012 and above
                    $initFieldsDb = New-Object System.Collections.Specialized.StringCollection
                    [void]$initFieldsDb.AddRange($Fields201x_Db)
                    $initFieldsLogin = New-Object System.Collections.Specialized.StringCollection
                    [void]$initFieldsLogin.AddRange($Fields201x_Login)
                    $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Database], $initFieldsDb)
                    $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Login], $initFieldsLogin)
                }
            }

            if ($SqlConnectionOnly) {
                return $server.ConnectionContext.SqlConnectionObject
            }
            else {
                if (-not $server.ComputerName) {
                    $parsedcomputername = $server.NetName
                    if (-not $parsedcomputername) {
                        $parsedcomputername = ([dbainstance]$instance).ComputerName
                    }
                    Add-Member -InputObject $server -NotePropertyName ComputerName -NotePropertyValue $parsedcomputername -Force
                }
                return $server
            }
        }
    }
}
tools\dbatools\functions\ConvertTo-DbaDataTable.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function ConvertTo-DbaDataTable {
    <#
        .SYNOPSIS
            Creates a DataTable for an object.

        .DESCRIPTION
            Creates a DataTable based on an object's properties. This allows you to easily write to SQL Server tables.

            Thanks to Chad Miller, this is based on his script. https://gallery.technet.microsoft.com/scriptcenter/4208a159-a52e-4b99-83d4-8048468d29dd

            If the attempt to convert to datatable fails, try the -Raw parameter for less accurate datatype detection.

        .PARAMETER InputObject
            The object to transform into a DataTable.

        .PARAMETER TimeSpanType
            Specifies the type to convert TimeSpan objects into. Default is 'TotalMilliseconds'. Valid options are: 'Ticks', 'TotalDays', 'TotalHours', 'TotalMinutes', 'TotalSeconds', 'TotalMilliseconds', and 'String'.

        .PARAMETER SizeType
            Specifies the type to convert DbaSize objects to. Default is 'Int64'. Valid options are 'Int32', 'Int64', and 'String'.

        .PARAMETER IgnoreNull
            If this switch is enabled, objects with null values will be ignored (empty rows will be added by default).

        .PARAMETER Raw
            If this switch is enabled, the DataTable will be created with strings. No attempt will be made to parse/determine data types.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: DataTable, Table, Data
            Website: https://dbatools.io/
            Copyright: (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/ConvertTo-DbaDataTable

        .OUTPUTS
            System.Object[]

        .EXAMPLE
            Get-Service | ConvertTo-DbaDataTable

            Creates a DataTable from the output of Get-Service.

        .EXAMPLE
            ConvertTo-DbaDataTable -InputObject $csv.cheesetypes

            Creates a DataTable from the CSV object $csv.cheesetypes.

        .EXAMPLE
            $dblist | ConvertTo-DbaDataTable

            Creates a DataTable from the $dblist object passed in via pipeline.

        .EXAMPLE
            Get-Process | ConvertTo-DbaDataTable -TimeSpanType TotalSeconds

            Creates a DataTable with the running processes and converts any TimeSpan property to TotalSeconds.
    #>
    [CmdletBinding()]
    [OutputType([System.Object[]])]
    param (
        [Parameter(Position = 0,
            Mandatory = $true,
            ValueFromPipeline = $true)]
        [AllowNull()]
        [PSObject[]]$InputObject,
        [Parameter(Position = 1)]
        [ValidateSet("Ticks",
            "TotalDays",
            "TotalHours",
            "TotalMinutes",
            "TotalSeconds",
            "TotalMilliseconds",
            "String")]
        [ValidateNotNullOrEmpty()]
        [string]$TimeSpanType = "TotalMilliseconds",
        [ValidateSet("Int64", "Int32", "String")]
        [string]$SizeType = "Int64",
        [switch]$IgnoreNull,
        [switch]$Raw,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Write-Message -Level Debug -Message "Bound parameters: $($PSBoundParameters.Keys -join ", ")"
        Write-Message -Level Debug -Message "TimeSpanType = $TimeSpanType | SizeType = $SizeType"
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Alias Out-DbaDataTable

        function Convert-Type {
            # This function will check so that the type is an accepted type which could be used when inserting into a table.
            # If a type is accepted (included in the $type array) then it will be passed on, otherwise it will first change type before passing it on.
            # Special types will have both their types converted as well as the value.
            # TimeSpan is a special type and will be converted into the $timespantype. (default: TotalMilliseconds) so that the timespan can be stored in a database further down the line.
            [CmdletBinding()]
            param (
                $type,

                $value,

                $timespantype = 'TotalMilliseconds',

                $sizetype = 'Int64'
            )

            $types = [System.Collections.ArrayList]@(
                'System.Int32',
                'System.UInt32',
                'System.Int16',
                'System.UInt16',
                'System.Int64',
                'System.UInt64',
                'System.Decimal',
                'System.Single',
                'System.Double',
                'System.Byte',
                'System.SByte',
                'System.Boolean',
                'System.DateTime',
                'System.Guid',
                'System.Char'
            )

            # The $special variable is used to mark the return value if a conversion was made on the value itself.
            # If this is set to true the original value will later be ignored when updating the DataTable.
            # And the value returned from this function will be used instead. (cannot modify existing properties)
            $special = $false
            $specialType = ""

            # Special types need to be converted in some way.
            # This attempt is to convert timespan into something that works in a table.
            # I couldn't decide on what to convert it to so the user can decide.
            # If the parameter is not used, TotalMilliseconds will be used as default.
            # Ticks are more accurate but I think milliseconds are more useful most of the time.
            if (($type -eq 'System.TimeSpan') -or ($type -eq 'Sqlcollaborative.Dbatools.Utility.DbaTimeSpan') -or ($type -eq 'Sqlcollaborative.Dbatools.Utility.DbaTimeSpanPretty')) {
                $special = $true
                if ($timespantype -eq 'String') {
                    $value = $value.ToString()
                    $type = 'System.String'
                }
                else {
                    # Let's use Int64 for all other types than string.
                    # We could match the type more closely with the timespantype but that can be added in the future if needed.
                    $value = $value.$timespantype
                    $type = 'System.Int64'
                }
                $specialType = 'Timespan'
            }
            elseif ($type -eq 'Sqlcollaborative.Dbatools.Utility.Size') {
                $special = $true
                switch ($sizetype) {
                    'Int64' {
                        $value = $value.Byte
                        $type = 'System.Int64'
                    }
                    'Int32' {
                        $value = $value.Byte
                        $type = 'System.Int32'
                    }
                    'String' {
                        $value = $value.ToString()
                        $type = 'System.String'
                    }
                }
                $specialType = 'Size'
            }
            elseif (-not ($type -in $types)) {
                # All types which are not found in the array will be converted into strings.
                # In this way we dont ignore it completely and it will be clear in the end why it looks as it does.
                $type = 'System.String'
            }

            # return a hashtable instead of an object. I like hashtables :)
            return @{ type = $type; Value = $value; Special = $special; SpecialType = $specialType }
        }

        function Convert-SpecialType {
            <#
            .SYNOPSIS
                Converts a value for a known column.

            .DESCRIPTION
                Converts a value for a known column.

            .PARAMETER Value
                The value to convert

            .PARAMETER Type
                The special type for which to convert

            .PARAMETER SizeType
                The size type defined by the user

            .PARAMETER TimeSpanType
                The timespan type defined by the user
        #>
            [CmdletBinding()]
            Param (
                $Value,
                [ValidateSet('Timespan', 'Size')] [string]$Type,
                [string]$SizeType,
                [string]$TimeSpanType
            )

            switch ($Type) {
                'Size' {
                    if ($SizeType -eq 'String') { return $Value.ToString() }
                    else { return $Value.Byte }
                }
                'Timespan' {
                    if ($TimeSpanType -eq 'String') {
                        $Value.ToString()
                    }
                    else {
                        $Value.$TimeSpanType
                    }
                }
            }
        }

        function Add-Column {
            <#
            .SYNOPSIS
                Adds a column to the datatable in progress.

            .DESCRIPTION
                Adds a column to the datatable in progress.

            .PARAMETER Property
                The property for which to add a column.

            .PARAMETER DataTable
                Autofilled. The table for which to add a column.

            .PARAMETER TimeSpanType
                Autofilled. How should timespans be handled?

            .PARAMETER SizeType
                Autofilled. How should sizes be handled?

            .PARAMETER Raw
                Autofilled. Whether the column should be string, no matter the input.
        #>
            [CmdletBinding()]
            Param (
                [System.Management.Automation.PSPropertyInfo]$Property,
                [System.Data.DataTable]$DataTable = $datatable,
                [string]$TimeSpanType = $TimeSpanType,
                [string]$SizeType = $SizeType,
                [bool]$Raw = $Raw
            )

            $type = $property.TypeNameOfValue
            try {
                if ($Property.MemberType -like 'ScriptProperty') {
                    $type = $Property.GetType().FullName
                }
            }
            catch { $type = 'System.String' }

            $converted = Convert-Type -type $type -value $property.Value -timespantype $TimeSpanType -sizetype $SizeType

            $column = New-Object System.Data.DataColumn
            $column.ColumnName = $property.Name.ToString()
            if (-not $Raw) {
                $column.DataType = [System.Type]::GetType($converted.type)
            }
            $null = $DataTable.Columns.Add($column)
            $converted
        }

        $datatable = New-Object System.Data.DataTable

        # Accelerate subsequent lookups of columns and special type columns
        $columns = @()
        $specialColumns = @()
        $specialColumnsType = @{ }

        $ShouldCreateColumns = $true
    }

    process {
        #region Handle null objects
        if ($null -eq $InputObject) {
            if (-not $IgnoreNull) {
                $datarow = $datatable.NewRow()
                $datatable.Rows.Add($datarow)
            }

            # Only ends the current process block
            return
        }
        #endregion Handle null objects


        foreach ($object in $InputObject) {
            #region Handle null objects
            if ($null -eq $object) {
                if (-not $IgnoreNull) {
                    $datarow = $datatable.NewRow()
                    $datatable.Rows.Add($datarow)
                }
                continue
            }
            #endregion Handle null objects

            #Handle rows already being System.Data.DataRow
            if ($object.GetType().FullName -eq 'System.Data.DataRow') {
                if ($ShouldCreateColumns) {
                    $datatable = $object.Table.Copy()
                    $ShouldCreateColumns = $false
                }
                continue
            }

            # The new row to insert
            $datarow = $datatable.NewRow()

            #region Process Properties
            $objectProperties = $object.PSObject.Properties
            foreach ($property in $objectProperties) {
                #region Create Columns as needed
                if ($ShouldCreateColumns) {
                    $newColumn = Add-Column -Property $property
                    $columns += $property.Name
                    if ($newColumn.Special) {
                        $specialColumns += $property.Name
                        $specialColumnsType[$property.Name] = $newColumn.SpecialType
                    }
                }
                #endregion Create Columns as needed

                # Handle null properties, as well as properties with access errors
                try {
                    $propValueLength = $property.value.length
                }
                catch {
                    $propValueLength = 0
                }

                #region Insert value into column of row
                if ($propValueLength -gt 0) {
                    # If the typename was a special typename we want to use the value returned from Convert-Type instead.
                    # We might get error if we try to change the value for $property.value if it is read-only. That's why we use $converted.value instead.
                    if ($property.Name -in $specialColumns) {
                        $datarow.Item($property.Name) = Convert-SpecialType -Value $property.value -Type $specialColumnsType[$property.Name] -SizeType $SizeType -TimeSpanType $TimeSpanType
                    }
                    else {
                        if ($property.value.ToString().length -eq 15) {
                            if ($property.value.ToString() -eq 'System.Object[]') {
                                $value = $property.value -join ", "
                            }
                            elseif ($property.value.ToString() -eq 'System.String[]') {
                                $value = $property.value -join ", "
                            }
                            else {
                                $value = $property.value
                            }
                        }
                        else {
                            $value = $property.value
                        }

                        try {
                            $datarow.Item($property.Name) = $value
                        }
                        catch {
                            if ($property.Name -notin $columns) {
                                try {
                                    $newColumn = Add-Column -Property $property
                                    $columns += $property.Name
                                    if ($newColumn.Special) {
                                        $specialColumns += $property.Name
                                        $specialColumnsType[$property.Name] = $newColumn.SpecialType
                                    }

                                    $datarow.Item($property.Name) = $newColumn.Value
                                }
                                catch {
                                    Write-Message -Level Warning -Message "Failed to add property $($property.Name) from $object" -ErrorRecord $_ -Target $object
                                }
                            }
                            else {
                                Write-Message -Level Warning -Message "Failed to add property $($property.Name) from $object" -ErrorRecord $_ -Target $object
                            }
                        }
                    }
                }
                #endregion Insert value into column of row
            }

            $datatable.Rows.Add($datarow)
            # If this is the first non-null object then the columns has just been created.
            # Set variable to false to skip creating columns from now on.
            if ($ShouldCreateColumns) {
                $ShouldCreateColumns = $false
            }
            #endregion Process Properties
        }
    }

    end {
        Write-Message -Level InternalComment -Message "Finished."
        , $datatable
    }
}
tools\dbatools\functions\ConvertTo-DbaXESession.ps1
function ConvertTo-DbaXESession {
    <#
        .SYNOPSIS
            Uses a slightly modified version of sp_SQLskills_ConvertTraceToExtendedEvents.sql to convert Traces to Extended Events.

        .DESCRIPTION
            Uses a slightly modified version of sp_SQLskills_ConvertTraceToExtendedEvents.sql to convert Traces to Extended Events.

            T-SQL code by: Jonathan M. Kehayias, SQLskills.com. T-SQL can be found in this module directory and at
            https://www.sqlskills.com/blogs/jonathan/converting-sql-trace-to-extended-events-in-sql-server-2012/

        .PARAMETER InputObject
            Specifies a Trace object output by Get-DbaTrace.

        .PARAMETER Name
            The name of the Trace to convert. If the name exists, characters will be appended to it.

        .PARAMETER OutputScriptOnly
            Outputs the T-SQL script to create the XE session and does not execute it.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Trace, ExtendedEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            Get-DbaTrace -SqlInstance sql2017, sql2012 | Where Id -eq 2 | ConvertTo-DbaXESession -Name 'Test'

            Converts Trace with ID 2 to a Session named Test on SQL Server instances named sql2017 and sql2012
            and creates the Session on each respective server.

       .EXAMPLE
            Get-DbaTrace -SqlInstance sql2014 | Out-GridView -PassThru | ConvertTo-DbaXESession -Name 'Test' | Start-DbaXESession

            Converts selected traces on sql2014 to sessions, creates the session, and starts it.

        .EXAMPLE
            Get-DbaTrace -SqlInstance sql2014 | Where Id -eq 1 | ConvertTo-DbaXESession -Name 'Test' -OutputScriptOnly

            Converts trace ID 1 on sql2014 to an Extended Event and outputs the resulting T-SQL.
    #>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory, ValueFromPipeline)]
        [object[]]$InputObject,
        [parameter(Mandatory)]
        [string]$Name,
        [switch]$OutputScriptOnly,
        [switch]$EnableException
    )
    begin {
        $rawsql = Get-Content "$script:PSModuleRoot\bin\sp_SQLskills_ConvertTraceToEEs.sql" -Raw
    }
    process {
        foreach ($trace in $InputObject) {
            if (-not $trace.id -and -not $trace.Parent) {
                Stop-Function -Message "Input is of the wrong type. Use Get-DbaTrace." -Continue
                return
            }

            $server = $trace.Parent

            if ($server.VersionMajor -lt 11) {
                Stop-Function -Message "SQL Server version 2012+ required - $server not supported."
                return
            }

            $tempdb = $server.Databases['tempdb']
            $traceid = $trace.id

            if ((Get-DbaXESession -SqlInstance $server -Session $PSBoundParameters.Name)) {
                $oldname = $name
                $Name = "$name-$traceid"
                Write-Message -Level Output -Message "XE Session $oldname already exists on $server, trying $name."
            }

            if ((Get-DbaXESession -SqlInstance $server -Session $Name)) {
                $oldname = $name
                $Name = "$name-$(Get-Random)"
                Write-Message -Level Output -Message "XE Session $oldname already exists on $server, trying $name."
            }

            $sql = $rawsql.Replace("--TRACEID--", $traceid)
            $sql = $sql.Replace("--SESSIONNAME--", $name)

            try {
                Write-Message -Level Verbose -Message "Executing SQL in tempdb."
                $results = $tempdb.ExecuteWithResults($sql).Tables.Rows.SqlString
            }
            catch {
                Stop-Function -Message "Issue creating, dropping or executing sp_SQLskills_ConvertTraceToExtendedEvents in tempdb on $server." -Target $server -ErrorRecord $_
            }

            $results = $results -join "`r`n"

            if ($OutputScriptOnly) {
                $results
            }
            else {
                Write-Message -Level Verbose -Message "Creating XE Session $name."
                try {
                    $tempdb.ExecuteNonQuery($results)
                }
                catch {
                    Stop-Function -Message "Issue creating extended event $name on $server." -Target $server -ErrorRecord $_
                }
                Get-DbaXESession -SqlInstance $server -Session $name
            }
        }
    }
}
tools\dbatools\functions\Copy-DbaAgentAlert.ps1
function Copy-DbaAgentAlert {
    <#
        .SYNOPSIS
            Copy-DbaAgentAlert migrates alerts from one SQL Server to another.

        .DESCRIPTION
            By default, all alerts are copied. The -Alert parameter is auto-populated for command-line completion and can be used to copy only specific alerts.

            If the alert already exists on the destination, it will be skipped unless -Force is used.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Alert
            The alert(s) to process. This list is auto-populated from the server. If unspecified, all alerts will be processed.

        .PARAMETER ExcludeAlert
            The alert(s) to exclude. This list is auto-populated from the server.

        .PARAMETER IncludeDefaults
            Copy SQL Agent defaults such as FailSafeEmailAddress, ForwardingServer, and PagerSubjectTemplate.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER Force
            If this switch is enabled, the Alert will be dropped and recreated on Destination.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration, Agent
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaAgentAlert

        .EXAMPLE
            Copy-DbaAgentAlert -Source sqlserver2014a -Destination sqlcluster

            Copies all alerts from sqlserver2014a to sqlcluster using Windows credentials. If alerts with the same name exist on sqlcluster, they will be skipped.

        .EXAMPLE
            Copy-DbaAgentAlert -Source sqlserver2014a -Destination sqlcluster -Alert PSAlert -SourceSqlCredential $cred -Force

            Copies a only the alert named PSAlert from sqlserver2014a to sqlcluster using SQL credentials for sqlserver2014a and Windows credentials for sqlcluster. If a alert with the same name exists on sqlcluster, it will be dropped and recreated because -Force was used.

        .EXAMPLE
            Copy-DbaAgentAlert -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force

            Shows what would happen if the command were executed using force.
    #>
    [cmdletbinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]$SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [object[]]$Alert,
        [object[]]$ExcludeAlert,
        [switch]$IncludeDefaults,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
            $serverAlerts = $sourceServer.JobServer.Alerts
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            $destAlerts = $destServer.JobServer.Alerts
            
            if ($IncludeDefaults -eq $true) {
                if ($PSCmdlet.ShouldProcess($destinstance, "Creating Alert Defaults")) {
                    $copyAgentAlertStatus = [pscustomobject]@{
                        SourceServer = $sourceServer.Name
                        DestinationServer = $destServer.Name
                        Name         = "Alert Defaults"
                        Type         = "Alert Defaults"
                        Status       = $null
                        Notes        = $null
                        DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                    }
                    try {
                        Write-Message -Message "Creating Alert Defaults" -Level Verbose
                        $sql = $sourceServer.JobServer.AlertSystem.Script() | Out-String
                        $sql = $sql -replace [Regex]::Escape("'$source'"), "'$destinstance'"
                        
                        Write-Message -Message $sql -Level Debug
                        $null = $destServer.Query($sql)
                        
                        $copyAgentAlertStatus.Status = "Successful"
                    }
                    catch {
                        $copyAgentAlertStatus.Status = "Failed"
                        $copyAgentAlertStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        Stop-Function -Message "Issue creating alert defaults." -Category InvalidOperation -ErrorRecord $_ -Target $destServer -Continue
                    }
                    $copyAgentAlertStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                }
            }
            
            foreach ($serverAlert in $serverAlerts) {
                $alertName = $serverAlert.name
                $copyAgentAlertStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name         = $alertName
                    Type         = "Agent Alert"
                    Notes        = $null
                    Status       = $null
                    DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                }
                if (($Alert -and $Alert -notcontains $alertName) -or ($ExcludeAlert -and $ExcludeAlert -contains $alertName)) {
                    continue
                }
                
                if ($destAlerts.name -contains $serverAlert.name) {
                    if ($force -eq $false) {
                        if ($PSCmdlet.ShouldProcess($destinstance, "Alert [$alertName] exists at destination. Use -Force to drop and migrate.")) {
                            $copyAgentAlertStatus.Status = "Skipped"
                            $copyAgentAlertStatus.Notes = "Already exists"
                            $copyAgentAlertStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Write-Message -Message "Alert [$alertName] exists at destination. Use -Force to drop and migrate." -Level Verbose
                        }
                        continue
                    }
                    
                    if ($PSCmdlet.ShouldProcess($destinstance, "Dropping alert $alertName and recreating")) {
                        try {
                            Write-Message -Message "Dropping Alert $alertName on $destServer." -Level Verbose
                            
                            $sql = "EXEC msdb.dbo.sp_delete_alert @name = N'$($alertname)';"
                            Write-Message -Message $sql -Level Debug
                            $null = $destServer.Query($sql)
                            $destAlerts.Refresh()
                        }
                        catch {
                            $copyAgentAlertStatus.Status = "Failed"
                            $copyAgentAlertStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Stop-Function -Message "Issue dropping/recreating alert" -Category InvalidOperation -ErrorRecord $_ -Target $destServer -Continue
                        }
                    }
                }
                
                if ($destAlerts | Where-Object { $_.Severity -eq $serverAlert.Severity -and $_.MessageID -eq $serverAlert.MessageID -and $_.DatabaseName -eq $serverAlert.DatabaseName -and $_.EventDescriptionKeyword -eq $serverAlert.EventDescriptionKeyword }) {
                    if ($PSCmdlet.ShouldProcess($destinstance, "Checking for conflicts")) {
                        $conflictMessage = "Alert [$alertName] has already been defined to use"
                        if ($serverAlert.Severity -gt 0) { $conflictMessage += " severity $($serverAlert.Severity)" }
                        if ($serverAlert.MessageID -gt 0) { $conflictMessage += " error number $($serverAlert.MessageID)" }
                        if ($serverAlert.DatabaseName) { $conflictMessage += " on database '$($serverAlert.DatabaseName)'" }
                        if ($serverAlert.EventDescriptionKeyword) { $conflictMessage += " with error text '$($serverAlert.Severity)'" }
                        $conflictMessage += ". Skipping."
                        
                        Write-Message -Level Verbose -Message $conflictMessage
                        $copyAgentAlertStatus.Status = "Skipped"
                        $copyAgentAlertStatus.Notes = $conflictMessage
                        $copyAgentAlertStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    continue
                }
                if ($serverAlert.JobName -and $destServer.JobServer.Jobs.Name -NotContains $serverAlert.JobName) {
                    Write-Message -Level Verbose -Message "Alert [$alertName] has job [$($serverAlert.JobName)] configured as response. The job does not exist on destination $destServer. Skipping."
                    if ($PSCmdlet.ShouldProcess($destinstance, "Checking for conflicts")) {
                        $copyAgentAlertStatus.Status = "Skipped"
                        $copyAgentAlertStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    continue
                }
                
                if ($PSCmdlet.ShouldProcess($destinstance, "Creating Alert $alertName")) {
                    try {
                        Write-Message -Message "Copying Alert $alertName" -Level Verbose
                        $sql = $serverAlert.Script() | Out-String
                        $sql = $sql -replace "@job_id=N'........-....-....-....-............", "@job_id=N'00000000-0000-0000-0000-000000000000"
                        
                        Write-Message -Message $sql -Level Debug
                        $null = $destServer.Query($sql)
                        
                        $copyAgentAlertStatus.Status = "Successful"
                        $copyAgentAlertStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copyAgentAlertStatus.Status = "Failed"
                        $copyAgentAlertStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        Stop-Function -Message "Issue creating alert" -Category InvalidOperation -ErrorRecord $_ -Target $destServer -Continue
                    }
                }
                
                $destServer.JobServer.Alerts.Refresh()
                $destServer.JobServer.Jobs.Refresh()
                
                $newAlert = $destServer.JobServer.Alerts[$alertName]
                $notifications = $serverAlert.EnumNotifications()
                $jobName = $serverAlert.JobName
                
                # JobId = 00000000-0000-0000-0000-000 means the Alert does not execute/is attached to a SQL Agent Job.
                if ($serverAlert.JobId -ne '00000000-0000-0000-0000-000000000000') {
                    $copyAgentAlertStatus = [pscustomobject]@{
                        SourceServer = $sourceServer.Name
                        DestinationServer = $destServer.Name
                        Name         = $alertName
                        Type         = "Agent Alert Job Association"
                        Notes        = "Associated with $jobName"
                        Status       = $null
                        DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                    }
                    if ($PSCmdlet.ShouldProcess($destinstance, "Adding $alertName to $jobName")) {
                        try {
                        <# THERE needs to be validation within this block to see if the $jobName actually exists on the source server. #>
                            Write-Message -Message "Adding $alertName to $jobName" -Level Verbose
                            $newJob = $destServer.JobServer.Jobs[$jobName]
                            $newJobId = ($newJob.JobId) -replace " ", ""
                            $sql = $sql -replace '00000000-0000-0000-0000-000000000000', $newJobId
                            $sql = $sql -replace 'sp_add_alert', 'sp_update_alert'
                            
                            Write-Message -Message $sql -Level Debug
                            $null = $destServer.Query($sql)
                            
                            $copyAgentAlertStatus.Status = "Successful"
                            $copyAgentAlertStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        }
                        catch {
                            $copyAgentAlertStatus.Status = "Failed"
                            $copyAgentAlertStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Stop-Function -Message "Issue adding alert to job" -Category InvalidOperation -ErrorRecord $_ -Target $destServer
                        }
                    }
                }
                
                if ($PSCmdlet.ShouldProcess($destinstance, "Moving Notifications $alertName")) {
                    try {
                        $copyAgentAlertStatus = [pscustomobject]@{
                            SourceServer = $sourceServer.Name
                            DestinationServer = $destServer.Name
                            Name         = $alertName
                            Type         = "Agent Alert Notification"
                            Notes        = $null
                            Status       = $null
                            DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                        }
                        # cant add them this way, we need to modify the existing one or give all options that are supported.
                        foreach ($notify in $notifications) {
                            $notifyCollection = @()
                            if ($notify.UseNetSend -eq $true) {
                                Write-Message -Message "Adding net send" -Level Verbose
                                $notifyCollection += "NetSend"
                            }
                            
                            if ($notify.UseEmail -eq $true) {
                                Write-Message -Message "Adding email" -Level Verbose
                                $notifyCollection += "NotifyEmail"
                            }
                            
                            if ($notify.UsePager -eq $true) {
                                Write-Message -Message "Adding pager" -Level Verbose
                                $notifyCollection += "Pager"
                            }
                            
                            $notifyMethods = $notifyCollection -join ", "
                            $newAlert.AddNotification($notify.OperatorName, [Microsoft.SqlServer.Management.Smo.Agent.NotifyMethods]$notifyMethods)
                        }
                        $copyAgentAlertStatus.Status = "Successful"
                        $copyAgentAlertStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copyAgentAlertStatus.Status = "Failed"
                        $copyAgentAlertStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        Stop-Function -Message "Issue moving notifications for the alert" -Category InvalidOperation -ErrorRecord $_ -Target $destServer
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlAlert
    }
}
tools\dbatools\functions\Copy-DbaAgentCategory.ps1
#ValidationTags#Messaging#
function Copy-DbaAgentCategory {
    <#
        .SYNOPSIS
            Copy-DbaAgentCategory migrates SQL Agent categories from one SQL Server to another. This is similar to sp_add_category.

            https://msdn.microsoft.com/en-us/library/ms181597.aspx

        .DESCRIPTION
            By default, all SQL Agent categories for Jobs, Operators and Alerts are copied.

            The -OperatorCategories parameter is auto-populated for command-line completion and can be used to copy only specific operator categories.
            The -AgentCategories parameter is auto-populated for command-line completion and can be used to copy only specific agent categories.
            The -JobCategories parameter is auto-populated for command-line completion and can be used to copy only specific job categories.

            If the category already exists on the destination, it will be skipped unless -Force is used.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER CategoryType
            Specifies the Category Type to migrate. Valid options are "Job", "Alert" and "Operator". When CategoryType is specified, all categories from the selected type will be migrated. For granular migrations, use the three parameters below.

        .PARAMETER OperatorCategory
            This parameter is auto-populated for command-line completion and can be used to copy only specific operator categories.

        .PARAMETER AgentCategory
            This parameter is auto-populated for command-line completion and can be used to copy only specific agent categories.

        .PARAMETER JobCategory
            This parameter is auto-populated for command-line completion and can be used to copy only specific job categories.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER Force
            If this switch is enabled, the Category will be dropped and recreated on Destination.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration, Agent
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaAgentCategory

        .EXAMPLE
            Copy-DbaAgentCategory -Source sqlserver2014a -Destination sqlcluster

            Copies all operator categories from sqlserver2014a to sqlcluster using Windows authentication. If operator categories with the same name exist on sqlcluster, they will be skipped.

        .EXAMPLE
            Copy-DbaAgentCategory -Source sqlserver2014a -Destination sqlcluster -OperatorCategory PSOperator -SourceSqlCredential $cred -Force

            Copies a single operator category, the PSOperator operator category from sqlserver2014a to sqlcluster using SQL credentials to authenticate to sqlserver2014a and Windows credentials for sqlcluster. If a operator category with the same name exists on sqlcluster, it will be dropped and recreated because -Force was used.

        .EXAMPLE
            Copy-DbaAgentCategory -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force

            Shows what would happen if the command were executed using force.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldprocess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]$SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [Parameter(ParameterSetName = 'SpecificAlerts')]
        [ValidateSet('Job', 'Alert', 'Operator')]
        [string[]]$CategoryType,
        [string[]]$JobCategory,
        [string[]]$AgentCategory,
        [string[]]$OperatorCategory,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    
    begin {
        function Copy-JobCategory {
            <#
                .SYNOPSIS
                    Copy-JobCategory migrates job categories from one SQL Server to another.

                .DESCRIPTION
                    By default, all job categories are copied. The -JobCategories parameter is auto-populated for command-line completion and can be used to copy only specific job categories.

                    If the associated credential for the category does not exist on the destination, it will be skipped. If the job category already exists on the destination, it will be skipped unless -Force is used.
            #>
            param (
                [string[]]$jobCategories
            )
            
            process {
                
                $serverJobCategories = $sourceServer.JobServer.JobCategories | Where-Object ID -ge 100
                $destJobCategories = $destServer.JobServer.JobCategories | Where-Object ID -ge 100
                
                foreach ($jobCategory in $serverJobCategories) {
                    $categoryName = $jobCategory.Name
                    
                    $copyJobCategoryStatus = [pscustomobject]@{
                        SourceServer = $sourceServer.Name
                        DestinationServer = $destServer.Name
                        Name         = $categoryName
                        Type         = "Agent Job Category"
                        Status       = $null
                        Notes        = $null
                        DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                    }
                    
                    if ($jobCategories.Count -gt 0 -and $jobCategories -notcontains $categoryName) {
                        continue
                    }
                    
                    if ($destJobCategories.Name -contains $jobCategory.name) {
                        if ($force -eq $false) {
                            $copyJobCategoryStatus.Status = "Skipped"
                            $copyJobCategoryStatus.Notes = "Already exists"
                            $copyJobCategoryStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Write-Message -Level Verbose -Message "Job category $categoryName exists at destination. Use -Force to drop and migrate."
                            continue
                        }
                        else {
                            if ($Pscmdlet.ShouldProcess($destinstance, "Dropping job category $categoryName")) {
                                try {
                                    Write-Message -Level Verbose -Message "Dropping Job category $categoryName"
                                    $destServer.JobServer.JobCategories[$categoryName].Drop()
                                }
                                catch {
                                    $copyJobCategoryStatus.Status = "Failed"
                                    $copyJobCategoryStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                    Stop-Function -Message "Issue dropping job category" -Target $categoryName -ErrorRecord $_ -Continue
                                }
                            }
                        }
                    }
                    
                    if ($Pscmdlet.ShouldProcess($destinstance, "Creating Job category $categoryName")) {
                        try {
                            Write-Message -Level Verbose -Message "Copying Job category $categoryName"
                            $sql = $jobCategory.Script() | Out-String
                            Write-Message -Level Debug -Message "SQL Statement: $sql"
                            $destServer.Query($sql)
                            
                            $copyJobCategoryStatus.Status = "Successful"
                            $copyJobCategoryStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        }
                        catch {
                            $copyJobCategoryStatus.Status = "Failed"
                            $copyJobCategoryStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Stop-Function -Message "Issue copying job category" -Target $categoryName -ErrorRecord $_
                        }
                    }
                }
            }
        }
        
        function Copy-OperatorCategory {
            <#
                .SYNOPSIS
                    Copy-OperatorCategory migrates operator categories from one SQL Server to another.

                .DESCRIPTION
                    By default, all operator categories are copied. The -OperatorCategories parameter is auto-populated for command-line completion and can be used to copy only specific operator categories.

                    If the associated credential for the category does not exist on the destination, it will be skipped. If the operator category already exists on the destination, it will be skipped unless -Force is used.
            #>
            [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldprocess = $true)]
            param (
                [string[]]$operatorCategories
            )
            process {
                $serverOperatorCategories = $sourceServer.JobServer.OperatorCategories | Where-Object ID -ge 100
                $destOperatorCategories = $destServer.JobServer.OperatorCategories | Where-Object ID -ge 100
                
                foreach ($operatorCategory in $serverOperatorCategories) {
                    $categoryName = $operatorCategory.Name
                    
                    $copyOperatorCategoryStatus = [pscustomobject]@{
                        SourceServer = $sourceServer.Name
                        DestinationServer = $destServer.Name
                        Type         = "Agent Operator Category"
                        Name         = $categoryName
                        Status       = $null
                        Notes        = $null
                        DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                    }
                    
                    if ($operatorCategories.Count -gt 0 -and $operatorCategories -notcontains $categoryName) {
                        continue
                    }
                    
                    if ($destOperatorCategories.Name -contains $operatorCategory.Name) {
                        if ($force -eq $false) {
                            $copyOperatorCategoryStatus.Status = "Skipped"
                            $copyOperatorCategoryStatus.Notes = "Already exists"
                            $copyOperatorCategoryStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Write-Message -Level Verbose -Message "Operator category $categoryName exists at destination. Use -Force to drop and migrate."
                            continue
                        }
                        else {
                            if ($Pscmdlet.ShouldProcess($destinstance, "Dropping operator category $categoryName and recreating")) {
                                try {
                                    Write-Message -Level Verbose -Message "Dropping Operator category $categoryName"
                                    $destServer.JobServer.OperatorCategories[$categoryName].Drop()
                                    Write-Message -Level Verbose -Message "Copying Operator category $categoryName"
                                    $sql = $operatorCategory.Script() | Out-String
                                    Write-Message -Level Debug -Message $sql
                                    $destServer.Query($sql)
                                }
                                catch {
                                    $copyOperatorCategoryStatus.Status = "Failed"
                                    $copyOperatorCategoryStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                    Stop-Function -Message "Issue dropping operator category" -Target $categoryName -ErrorRecord $_
                                }
                            }
                        }
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Creating Operator category $categoryName")) {
                            try {
                                Write-Message -Level Verbose -Message "Copying Operator category $categoryName"
                                $sql = $operatorCategory.Script() | Out-String
                                Write-Message -Level Debug -Message $sql
                                $destServer.Query($sql)
                                
                                $copyOperatorCategoryStatus.Status = "Successful"
                                $copyOperatorCategoryStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            }
                            catch {
                                $copyOperatorCategoryStatus.Status = "Failed"
                                $copyOperatorCategoryStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                Stop-Function -Message "Issue copying operator category" -Target $categoryName -ErrorRecord $_
                            }
                        }
                    }
                }
            }
        }
        
        function Copy-AlertCategory {
            <#
                .SYNOPSIS
                    Copy-AlertCategory migrates alert categories from one SQL Server to another.

                .DESCRIPTION
                    By default, all alert categories are copied. The -AlertCategories parameter is auto-populated for command-line completion and can be used to copy only specific alert categories.

                    If the associated credential for the category does not exist on the destination, it will be skipped. If the alert category already exists on the destination, it will be skipped unless -Force is used.
            #>
            [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldprocess = $true)]
            param (
                [string[]]$AlertCategories
            )
            
            process {
                if ($sourceServer.VersionMajor -lt 9 -or $destServer.VersionMajor -lt 9) {
                    throw "Server AlertCategories are only supported in SQL Server 2005 and above. Quitting."
                }
                
                $serverAlertCategories = $sourceServer.JobServer.AlertCategories | Where-Object ID -ge 100
                $destAlertCategories = $destServer.JobServer.AlertCategories | Where-Object ID -ge 100
                
                foreach ($alertCategory in $serverAlertCategories) {
                    $categoryName = $alertCategory.Name
                    
                    $copyAlertCategoryStatus = [pscustomobject]@{
                        SourceServer = $sourceServer.Name
                        DestinationServer = $destServer.Name
                        Type         = "Agent Alert Category"
                        Name         = $categoryName
                        Status       = $null
                        Notes        = $null
                        DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                    }
                    
                    if ($alertCategories.Length -gt 0 -and $alertCategories -notcontains $categoryName) {
                        continue
                    }
                    
                    if ($destAlertCategories.Name -contains $alertCategory.name) {
                        if ($force -eq $false) {
                            $copyAlertCategoryStatus.Status = "Skipped"
                            $copyAlertCategoryStatus.Notes = "Already exists"
                            $copyAlertCategoryStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Write-Message -Level Verbose -Message "Alert category $categoryName exists at destination. Use -Force to drop and migrate."
                            continue
                        }
                        else {
                            if ($Pscmdlet.ShouldProcess($destinstance, "Dropping alert category $categoryName and recreating")) {
                                try {
                                    Write-Message -Level Verbose -Message "Dropping Alert category $categoryName"
                                    $destServer.JobServer.AlertCategories[$categoryName].Drop()
                                    Write-Message -Level Verbose -Message "Copying Alert category $categoryName"
                                    $sql = $alertcategory.Script() | Out-String
                                    Write-Message -Level Debug -Message "SQL Statement: $sql"
                                    $destServer.Query($sql)
                                }
                                catch {
                                    $copyAlertCategoryStatus.Status = "Failed"
                                    $copyAlertCategoryStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                    Stop-Function -Message "Issue dropping alert category" -Target $categoryName -ErrorRecord $_
                                }
                            }
                        }
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Creating Alert category $categoryName")) {
                            try {
                                Write-Message -Level Verbose -Message "Copying Alert category $categoryName"
                                $sql = $alertCategory.Script() | Out-String
                                Write-Message -Level Debug -Message $sql
                                $destServer.Query($sql)
                                
                                $copyAlertCategoryStatus.Status = "Successful"
                                $copyAlertCategoryStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            }
                            catch {
                                $copyAlertCategoryStatus.Status = "Failed"
                                $copyAlertCategoryStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                Stop-Function -Message "Issue creating alert category" -Target $categoryName -ErrorRecord $_
                            }
                        }
                    }
                }
            }
        }
        
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            
            if ($CategoryType.count -gt 0) {
                
                switch ($CategoryType) {
                    "Job" {
                        Copy-JobCategory
                    }
                    
                    "Alert" {
                        Copy-AlertCategory
                    }
                    
                    "Operator" {
                        Copy-OperatorCategory
                    }
                }
                continue
            }
            
            if (($OperatorCategory.Count + $AlertCategory.Count + $jobCategory.Count) -gt 0) {
                
                if ($OperatorCategory.Count -gt 0) {
                    Copy-OperatorCategory -OperatorCategories $OperatorCategory
                }
                
                if ($AlertCategory.Count -gt 0) {
                    Copy-AlertCategory -AlertCategories $AlertCategory
                }
                
                if ($jobCategory.Count -gt 0) {
                    Copy-JobCategory -JobCategories $jobCategory
                }
                continue
            }
            Copy-OperatorCategory
            Copy-AlertCategory
            Copy-JobCategory
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlAgentCategory
    }
}
tools\dbatools\functions\Copy-DbaAgentJob.ps1
function Copy-DbaAgentJob {
    <#
        .SYNOPSIS
            Copy-DbaAgentJob migrates jobs from one SQL Server to another.

        .DESCRIPTION
            By default, all jobs are copied. The -Job parameter is auto-populated for command-line completion and can be used to copy only specific jobs.

            If the job already exists on the destination, it will be skipped unless -Force is used.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Job
            The job(s) to process. This list is auto-populated from the server. If unspecified, all jobs will be processed.

        .PARAMETER ExcludeJob
            The job(s) to exclude. This list is auto-populated from the server.

        .PARAMETER DisableOnSource
            If this switch is enabled, the job will be disabled on the source server.

        .PARAMETER DisableOnDestination
            If this switch is enabled, the newly migrated job will be disabled on the destination server.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER Force
            If this switch is enabled, the Job will be dropped and recreated on Destination.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration, Agent, Job
            Author: Chrissy LeMaire (@cl), netnerds.net

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaAgentJob

        .EXAMPLE
            Copy-DbaAgentJob -Source sqlserver2014a -Destination sqlcluster

            Copies all jobs from sqlserver2014a to sqlcluster, using Windows credentials. If jobs with the same name exist on sqlcluster, they will be skipped.

        .EXAMPLE
            Copy-DbaAgentJob -Source sqlserver2014a -Destination sqlcluster -Job PSJob -SourceSqlCredential $cred -Force

            Copies a single job, the PSJob job from sqlserver2014a to sqlcluster, using SQL credentials for sqlserver2014a and Windows credentials for sqlcluster. If a job with the same name exists on sqlcluster, it will be dropped and recreated because -Force was used.

        .EXAMPLE
            Copy-DbaAgentJob -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force

            Shows what would happen if the command were executed using force.
    #>
    [cmdletbinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]$SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [object[]]$Job,
        [object[]]$ExcludeJob,
        [switch]$DisableOnSource,
        [switch]$DisableOnDestination,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        
        $serverJobs = $sourceServer.JobServer.Jobs
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            $destJobs = $destServer.JobServer.Jobs
            
            foreach ($serverJob in $serverJobs) {
                $jobName = $serverJob.name
                $jobId = $serverJob.JobId
                
                $copyJobStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name         = $jobName
                    Type         = "Agent Job"
                    Status       = $null
                    Notes        = $null
                    DateTime     = [DbaDateTime](Get-Date)
                }
                
                if ($Job -and $jobName -notin $Job -or $jobName -in $ExcludeJob) {
                    Write-Message -Level Verbose -Message "Job [$jobName] filtered. Skipping."
                    continue
                }
                Write-Message -Message "Working on job: $jobName" -Level Verbose
                $sql = "
                SELECT sp.[name] AS MaintenancePlanName
                FROM msdb.dbo.sysmaintplan_plans AS sp
                INNER JOIN msdb.dbo.sysmaintplan_subplans AS sps
                    ON sps.plan_id = sp.id
                WHERE job_id = '$($jobId)'"
                Write-Message -Message $sql -Level Debug
                
                $MaintenancePlanName = $sourceServer.Query($sql).MaintenancePlanName
                
                if ($MaintenancePlanName) {
                    if ($Pscmdlet.ShouldProcess($destinstance, "Job [$jobName] is associated with Maintenance Plan: $MaintenancePlanNam")) {
                        $copyJobStatus.Status = "Skipped"
                        $copyJobStatus.Notes = "Job is associated with maintenance plan"
                        $copyJobStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        Write-Message -Level Verbose -Message "Job [$jobName] is associated with Maintenance Plan: $MaintenancePlanName"
                    }
                    continue
                }
                
                $dbNames = $serverJob.JobSteps.DatabaseName | Where-Object { $_.Length -gt 0 }
                $missingDb = $dbNames | Where-Object { $destServer.Databases.Name -notcontains $_ }
                
                if ($missingDb.Count -gt 0 -and $dbNames.Count -gt 0) {
                    if ($Pscmdlet.ShouldProcess($destinstance, "Database(s) $missingDb doesn't exist on destination. Skipping job [$jobName].")) {
                        $missingDb = ($missingDb | Sort-Object | Get-Unique) -join ", "
                        $copyJobStatus.Status = "Skipped"
                        $copyJobStatus.Notes = "Job is dependent on database: $missingDb"
                        $copyJobStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        Write-Message -Level Verbose -Message "Database(s) $missingDb doesn't exist on destination. Skipping job [$jobName]."
                    }
                    continue
                }
                
                $missingLogin = $serverJob.OwnerLoginName | Where-Object { $destServer.Logins.Name -notcontains $_ }
                
                if ($missingLogin.Count -gt 0) {
                    if ($force -eq $false) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Login(s) $missingLogin doesn't exist on destination. Use -Force to set owner to [sa]. Skipping job [$jobName].")) {
                            $missingLogin = ($missingLogin | Sort-Object | Get-Unique) -join ", "
                            $copyJobStatus.Status = "Skipped"
                            $copyJobStatus.Notes = "Job is dependent on login $missingLogin"
                            $copyJobStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Write-Message -Level Verbose -Message "Login(s) $missingLogin doesn't exist on destination. Use -Force to set owner to [sa]. Skipping job [$jobName]."
                        }
                        continue
                    }
                }
                
                $proxyNames = $serverJob.JobSteps.ProxyName | Where-Object { $_.Length -gt 0 }
                $missingProxy = $proxyNames | Where-Object { $destServer.JobServer.ProxyAccounts.Name -notcontains $_ }
                
                if ($missingProxy.Count -gt 0 -and $proxyNames.Count -gt 0) {
                    if ($Pscmdlet.ShouldProcess($destinstance, "Proxy Account(s) $($proxyNames[0]) doesn't exist on destination. Skipping job [$jobName].")) {
                        $missingProxy = ($missingProxy | Sort-Object | Get-Unique) -join ", "
                        $copyJobStatus.Status = "Skipped"
                        $copyJobStatus.Notes = "Job is dependent on proxy $($proxyNames[0])"
                        $copyJobStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        Write-Message -Level Verbose -Message "Proxy Account(s) $($proxyNames[0]) doesn't exist on destination. Skipping job [$jobName]."
                    }
                    continue
                }
                
                $operators = $serverJob.OperatorToEmail, $serverJob.OperatorToNetSend, $serverJob.OperatorToPage | Where-Object { $_.Length -gt 0 }
                $missingOperators = $operators | Where-Object { $destServer.JobServer.Operators.Name -notcontains $_ }
                
                if ($missingOperators.Count -gt 0 -and $operators.Count -gt 0) {
                    if ($Pscmdlet.ShouldProcess($destinstance, "Operator(s) $($missingOperator) doesn't exist on destination. Skipping job [$jobName]")) {
                        $missingOperator = ($operators | Sort-Object | Get-Unique) -join ", "
                        $copyJobStatus.Status = "Skipped"
                        $copyJobStatus.Notes = "Job is dependent on operator $missingOperator"
                        $copyJobStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        Write-Message -Level Verbose -Message "Operator(s) $($missingOperator) doesn't exist on destination. Skipping job [$jobName]"
                    }
                    continue
                }
                
                if ($destJobs.name -contains $serverJob.name) {
                    if ($force -eq $false) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Job $jobName exists at destination. Use -Force to drop and migrate.")) {
                            $copyJobStatus.Status = "Skipped"
                            $copyJobStatus.Notes = "Job already exists on destination"
                            $copyJobStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Write-Message -Level Verbose -Message "Job $jobName exists at destination. Use -Force to drop and migrate."
                        }
                        continue
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Dropping job $jobName and recreating")) {
                            try {
                                Write-Message -Message "Dropping Job $jobName" -Level Verbose
                                $destServer.JobServer.Jobs[$jobName].Drop()
                            }
                            catch {
                                $copyJobStatus.Status = "Failed"
                                $copyJobStatus.Notes = (Get-ErrorMessage -Record $_).Message
                                $copyJobStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                Stop-Function -Message "Issue dropping job" -Target $jobName -ErrorRecord $_ -Continue
                            }
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Creating Job $jobName")) {
                    try {
                        Write-Message -Message "Copying Job $jobName" -Level Verbose
                        $sql = $serverJob.Script() | Out-String
                        
                        if ($missingLogin.Count -gt 0 -and $force) {
                            $saLogin = Get-SqlSaLogin -SqlInstance $destServer
                            $sql = $sql -replace [Regex]::Escape("@owner_login_name=N'$missingLogin'"), [Regex]::Escape("@owner_login_name=N'$saLogin'")
                        }
                        
                        Write-Message -Message $sql -Level Debug
                        $destServer.Query($sql)
                        
                        $destServer.JobServer.Jobs.Refresh()
                        $destServer.JobServer.Jobs[$serverJob.name].IsEnabled = $sourceServer.JobServer.Jobs[$serverJob.name].IsEnabled
                        $destServer.JobServer.Jobs[$serverJob.name].Alter()
                    }
                    catch {
                        $copyJobStatus.Status = "Failed"
                        $copyJobStatus.Notes = (Get-ErrorMessage -Record $_)
                        $copyJobStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        Stop-Function -Message "Issue copying job" -Target $jobName -ErrorRecord $_ -Continue
                    }
                }
                
                if ($DisableOnDestination) {
                    if ($Pscmdlet.ShouldProcess($destinstance, "Disabling $jobName")) {
                        Write-Message -Message "Disabling $jobName on $destinstance" -Level Verbose
                        $destServer.JobServer.Jobs[$serverJob.name].IsEnabled = $False
                        $destServer.JobServer.Jobs[$serverJob.name].Alter()
                    }
                }
                
                if ($DisableOnSource) {
                    if ($Pscmdlet.ShouldProcess($source, "Disabling $jobName")) {
                        Write-Message -Message "Disabling $jobName on $source" -Level Verbose
                        $serverJob.IsEnabled = $false
                        $serverJob.Alter()
                    }
                }
                if ($Pscmdlet.ShouldProcess($destinstance, "Reporting status of migration for $jobname")) {
                    $copyJobStatus.Status = "Successful"
                    $copyJobStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlJob
    }
}
tools\dbatools\functions\Copy-DbaAgentOperator.ps1
function Copy-DbaAgentOperator {
    <#
        .SYNOPSIS
            Copy-DbaAgentOperator migrates operators from one SQL Server to another.

        .DESCRIPTION
            By default, all operators are copied. The -Operators parameter is auto-populated for command-line completion and can be used to copy only specific operators.

            If the associated credentials for the operator do not exist on the destination, it will be skipped. If the operator already exists on the destination, it will be skipped unless -Force is used.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Operator
            The operator(s) to process. This list is auto-populated from the server. If unspecified, all operators will be processed.

        .PARAMETER ExcludeOperator
            The operators(s) to exclude. This list is auto-populated from the server.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER Force
            If this switch is enabled, the Operator will be dropped and recreated on Destination.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration, Agent, Operator
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaAgentOperator

        .EXAMPLE
            Copy-DbaAgentOperator -Source sqlserver2014a -Destination sqlcluster

            Copies all operators from sqlserver2014a to sqlcluster using Windows credentials. If operators with the same name exist on sqlcluster, they will be skipped.

        .EXAMPLE
            Copy-DbaAgentOperator -Source sqlserver2014a -Destination sqlcluster -Operator PSOperator -SourceSqlCredential $cred -Force

            Copies only the PSOperator operator from sqlserver2014a to sqlcluster using SQL credentials for sqlserver2014a and Windows credentials for sqlcluster. If an operator with the same name exists on sqlcluster, it will be dropped and recreated because -Force was used.

        .EXAMPLE
            Copy-DbaAgentOperator -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force

            Shows what would happen if the command were executed using force.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]
        $SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]
        $DestinationSqlCredential,
        [object[]]$Operator,
        [object[]]$ExcludeOperator,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        $serverOperator = $sourceServer.JobServer.Operators
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            
            $destOperator = $destServer.JobServer.Operators
            $failsafe = $destServer.JobServer.AlertSystem | Select-Object FailSafeOperator
            foreach ($sOperator in $serverOperator) {
                $operatorName = $sOperator.Name
                
                $copyOperatorStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name         = $operatorName
                    Type         = "Agent Operator"
                    Status       = $null
                    Notes        = $null
                    DateTime     = [DbaDateTime](Get-Date)
                }
                
                if ($Operator -and $Operator -notcontains $operatorName -or $ExcludeOperator -in $operatorName) {
                    continue
                }
                
                if ($destOperator.Name -contains $sOperator.Name) {
                    if ($force -eq $false) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Operator $operatorName exists at destination. Use -Force to drop and migrate.")) {
                            $copyOperatorStatus.Status = "Skipped"
                            $copyOperatorStatus.Notes = "Already exists"
                            $copyOperatorStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Write-Message -Level Verbose -Message "Operator $operatorName exists at destination. Use -Force to drop and migrate."
                        }
                        continue
                    }
                    else {
                        if ($failsafe.FailSafeOperator -eq $operatorName) {
                            Write-Message -Level Verbose -Message "$operatorName is the failsafe operator. Skipping drop."
                            continue
                        }
                        
                        if ($Pscmdlet.ShouldProcess($destinstance, "Dropping operator $operatorName and recreating")) {
                            try {
                                Write-Message -Level Verbose -Message "Dropping Operator $operatorName"
                                $destServer.JobServer.Operators[$operatorName].Drop()
                            }
                            catch {
                                $copyOperatorStatus.Status = "Failed"
                                $copyOperatorStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                
                                Stop-Function -Message "Issue dropping operator" -Category InvalidOperation -ErrorRecord $_ -Target $destServer -Continue
                            }
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Creating Operator $operatorName")) {
                    try {
                        Write-Message -Level Verbose -Message "Copying Operator $operatorName"
                        $sql = $sOperator.Script() | Out-String
                        Write-Message -Level Debug -Message $sql
                        $destServer.Query($sql)
                        
                        $copyOperatorStatus.Status = "Successful"
                        $copyOperatorStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copyOperatorStatus.Status = "Failed"
                        $copyOperatorStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        Stop-Function -Message "Issue creating operator." -Category InvalidOperation -ErrorRecord $_ -Target $destServer
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlOperator
    }
}
tools\dbatools\functions\Copy-DbaAgentProxyAccount.ps1
function Copy-DbaAgentProxyAccount {
    <#
        .SYNOPSIS
            Copy-DbaAgentProxyAccount migrates proxy accounts from one SQL Server to another.

        .DESCRIPTION
            By default, all proxy accounts are copied. The -ProxyAccounts parameter is auto-populated for command-line completion and can be used to copy only specific proxy accounts.

            If the associated credential for the account does not exist on the destination, it will be skipped. If the proxy account already exists on the destination, it will be skipped unless -Force is used.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER Force
            If this switch is enabled, the Operator will be dropped and recreated on Destination.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration, Agent
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaAgentProxyAccount

        .EXAMPLE
            Copy-DbaAgentProxyAccount -Source sqlserver2014a -Destination sqlcluster

            Copies all proxy accounts from sqlserver2014a to sqlcluster using Windows credentials. If proxy accounts with the same name exist on sqlcluster, they will be skipped.

        .EXAMPLE
            Copy-DbaAgentProxyAccount -Source sqlserver2014a -Destination sqlcluster -ProxyAccount PSProxy -SourceSqlCredential $cred -Force

            Copies only the PSProxy proxy account from sqlserver2014a to sqlcluster using SQL credentials for sqlserver2014a and Windows credentials for sqlcluster. If a proxy account with the same name exists on sqlcluster, it will be dropped and recreated because -Force was used.

        .EXAMPLE
            Copy-DbaAgentProxyAccount -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force

            Shows what would happen if the command were executed using force.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]
        $SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]
        $DestinationSqlCredential,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 9
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        $serverProxyAccounts = $sourceServer.JobServer.ProxyAccounts
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            
            $destProxyAccounts = $destServer.JobServer.ProxyAccounts
            
            foreach ($proxyAccount in $serverProxyAccounts) {
                $proxyName = $proxyAccount.Name
                
                $copyAgentProxyAccountStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name         = $null
                    Type         = "Agent Proxy"
                    Status       = $null
                    Notes        = $null
                    DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                }
                
                if ($proxyAccounts.Length -gt 0 -and $proxyAccounts -notcontains $proxyName) {
                    continue
                }
                
                # Proxy accounts rely on Credential accounts
                $credentialName = $proxyAccount.CredentialName
                $copyAgentProxyAccountStatus.Name = $credentialName
                $copyAgentProxyAccountStatus.Type = "Credential"
                
                try {
                    $credentialtest = $destServer.Credentials[$CredentialName]
                }
                catch {
                    # don't care
                }
                
                if ($null -eq $credentialtest) {
                    $copyAgentProxyAccountStatus.Status = "Skipped"
                    $copyAgentProxyAccountStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    Write-Message -Level Verbose -Message "Associated credential account, $CredentialName, does not exist on $destinstance. Skipping migration of $proxyName."
                    continue
                }
                
                if ($destProxyAccounts.Name -contains $proxyName) {
                    $copyAgentProxyAccountStatus.Name = $proxyName
                    $copyAgentProxyAccountStatus.Type = "ProxyAccount"
                    
                    if ($force -eq $false) {
                        $copyAgentProxyAccountStatus.Status = "Skipped"
                        $copyAgentProxyAccountStatus
                        Write-Message -Level Verbose -Message "Server proxy account $proxyName exists at destination. Use -Force to drop and migrate."
                        continue
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Dropping server proxy account $proxyName and recreating")) {
                            try {
                                Write-Message -Level Verbose -Message "Dropping server proxy account $proxyName"
                                $destServer.JobServer.ProxyAccounts[$proxyName].Drop()
                            }
                            catch {
                                $copyAgentProxyAccountStatus.Status = "Failed"
                                $copyAgentProxyAccountStatus.Notes = "Could not drop"
                                $copyAgentProxyAccountStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                Stop-Function -Message "Issue dropping proxy account" -Target $proxyName -ErrorRecord $_ -Continue
                            }
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Creating server proxy account $proxyName")) {
                    $copyAgentProxyAccountStatus.Name = $proxyName
                    $copyAgentProxyAccountStatus.Type = "ProxyAccount"
                    
                    try {
                        Write-Message -Level Verbose -Message "Copying server proxy account $proxyName"
                        $sql = $proxyAccount.Script() | Out-String
                        Write-Message -Level Debug -Message $sql
                        $destServer.Query($sql)
                        
                        # Will fixing this misspelled status cause problems downstream?
                        $copyAgentProxyAccountStatus.Status = "Successful"
                        $copyAgentProxyAccountStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $exceptionstring = $_.Exception.InnerException.ToString()
                        if ($exceptionstring -match 'subsystem') {
                            $copyAgentProxyAccountStatus.Status = "Skipping"
                            $copyAgentProxyAccountStatus.Notes = "Failure"
                            $copyAgentProxyAccountStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            
                            Write-Message -Level Verbose -Message "One or more subsystems do not exist on the destination server. Skipping that part."
                        }
                        else {
                            $copyAgentProxyAccountStatus.Status = "Failed"
                            $copyAgentProxyAccountStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            
                            Stop-Function -Message "Issue creating proxy account" -Target $proxyName -ErrorRecord $_
                        }
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlProxyAccount
    }
}
tools\dbatools\functions\Copy-DbaAgentSharedSchedule.ps1
function Copy-DbaAgentSharedSchedule {
    <#
        .SYNOPSIS
            Copy-DbaAgentSharedSchedule migrates shared job schedules from one SQL Server to another.

        .DESCRIPTION
            All shared job schedules are copied.

            If the associated credential for the account does not exist on the destination, it will be skipped. If the shared job schedule already exists on the destination, it will be skipped unless -Force is used.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER Force
            If this switch is enabled, the Operator will be dropped and recreated on Destination.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration, Agent
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaAgentSharedSchedule

        .EXAMPLE
            Copy-DbaAgentSharedSchedule -Source sqlserver2014a -Destination sqlcluster

            Copies all shared job schedules from sqlserver2014a to sqlcluster using Windows credentials. If shared job schedules with the same name exist on sqlcluster, they will be skipped.

        .EXAMPLE
            Copy-DbaAgentSharedSchedule -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force

            Shows what would happen if the command were executed using force.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]
        $SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]
        $DestinationSqlCredential,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 9
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        $serverSchedules = $sourceServer.JobServer.SharedSchedules
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            
            $destSchedules = $destServer.JobServer.SharedSchedules
            foreach ($schedule in $serverSchedules) {
                $scheduleName = $schedule.Name
                $copySharedScheduleStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Type         = "Agent Schedule"
                    Name         = $scheduleName
                    Status       = $null
                    Notes        = $null
                    DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                }
                
                if ($schedules.Length -gt 0 -and $schedules -notcontains $scheduleName) {
                    continue
                }
                
                if ($destSchedules.Name -contains $scheduleName) {
                    if ($force -eq $false) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Shared job schedule $scheduleName exists at destination. Use -Force to drop and migrate.")) {
                            $copySharedScheduleStatus.Status = "Skipped"
                            $copySharedScheduleStatus.Notes = "Already exists"
                            $copySharedScheduleStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Write-Message -Level Verbose -Message "Shared job schedule $scheduleName exists at destination. Use -Force to drop and migrate."
                            continue
                        }
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Schedule [$scheduleName] has associated jobs. Skipping.")) {
                            if ($destServer.JobServer.Jobs.JobSchedules.Name -contains $scheduleName) {
                                $copySharedScheduleStatus.Status = "Skipped"
                                $copySharedScheduleStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                Write-Message -Level Verbose -Message "Schedule [$scheduleName] has associated jobs. Skipping."
                            }
                            continue
                        }
                        else {
                            if ($Pscmdlet.ShouldProcess($destinstance, "Dropping schedule $scheduleName and recreating")) {
                                try {
                                    Write-Message -Level Verbose -Message "Dropping schedule $scheduleName"
                                    $destServer.JobServer.SharedSchedules[$scheduleName].Drop()
                                }
                                catch {
                                    $copySharedScheduleStatus.Status = "Failed"
                                    $copySharedScheduleStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                    Stop-Function -Message "Issue dropping schedule" -Target $scheduleName -ErrorRecord $_ -Continue
                                }
                            }
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Creating schedule $scheduleName")) {
                    try {
                        Write-Message -Level Verbose -Message "Copying schedule $scheduleName"
                        $sql = $schedule.Script() | Out-String
                        
                        Write-Message -Level Debug -Message $sql
                        $destServer.Query($sql)
                        
                        $copySharedScheduleStatus.Status = "Successful"
                        $copySharedScheduleStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copySharedScheduleStatus.Status = "Failed"
                        $copySharedScheduleStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        Stop-Function -Message "Issue creating schedule" -Target $scheduleName -ErrorRecord $_ -Continue
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlSharedSchedule
    }
}
tools\dbatools\functions\Copy-DbaBackupDevice.ps1
function Copy-DbaBackupDevice {
    <#
        .SYNOPSIS
            Copies backup devices one by one. Copies both SQL code and the backup file itself.

        .DESCRIPTION
            Backups are migrated using Admin shares. If the destination directory does not exist, SQL Server's default backup directory will be used.

            If a backup device with same name exists on destination, it will not be dropped and recreated unless -Force is used.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER BackupDevice
            BackupDevice to be copied. Auto-populated list of devices. If not provided all BackupDevice(s) will be copied.

        .PARAMETER Force
            If this switch is enabled, backup device(s) will be dropped and recreated if they already exists on destination.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration, Backup
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaBackupDevice

        .EXAMPLE
            Copy-DbaBackupDevice -Source sqlserver2014a -Destination sqlcluster

            Copies all server backup devices from sqlserver2014a to sqlcluster using Windows credentials. If backup devices with the same name exist on sqlcluster, they will be skipped.

        .EXAMPLE
            Copy-DbaBackupDevice -Source sqlserver2014a -Destination sqlcluster -BackupDevice backup01 -SourceSqlCredential $cred -Force

            Copies only the backup device named backup01 from sqlserver2014a to sqlcluster using SQL credentials for sqlserver2014a    and Windows credentials for sqlcluster. If a backup device with the same name exists on sqlcluster, it will be dropped and recreated because -Force was used.

        .EXAMPLE
            Copy-DbaBackupDevice -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force

            Shows what would happen if the command were executed using force.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]$SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [object[]]$BackupDevice,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        $serverBackupDevices = $sourceServer.BackupDevices
        $sourceNetBios = $Source.ComputerName
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            $destBackupDevices = $destServer.BackupDevices
            $destNetBios = $destinstance.ComputerName
            
            foreach ($currentBackupDevice in $serverBackupDevices) {
                $deviceName = $currentBackupDevice.Name
                
                $copyBackupDeviceStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name         = $deviceName
                    Type         = "Backup Device"
                    Status       = $null
                    Notes        = $null
                    DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                }
                
                if ($BackupDevice -and $BackupDevice -notcontains $deviceName) {
                    continue
                }
                
                if ($destBackupDevices.Name -contains $deviceName) {
                    if ($force -eq $false) {
                        $copyBackupDeviceStatus.Status = "Skipped"
                        $copyBackupDeviceStatus.Notes = "Already exists"
                        $copyBackupDeviceStatus
                        
                        Write-Message -Level Verbose -Message "backup device $deviceName exists at destination. Use -Force to drop and migrate."
                        continue
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Dropping backup device $deviceName")) {
                            try {
                                Write-Message -Level Verbose -Message "Dropping backup device $deviceName"
                                $destServer.BackupDevices[$deviceName].Drop()
                            }
                            catch {
                                $copyBackupDeviceStatus.Status = "Failed"
                                $copyBackupDeviceStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                
                                Stop-Function -Message "Issue dropping backup device" -Target $deviceName -ErrorRecord $_ -Continue
                            }
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Generating SQL code for $deviceName")) {
                    Write-Message -Level Verbose -Message "Scripting out SQL for $deviceName"
                    try {
                        $sql = $currentBackupDevice.Script() | Out-String
                        $sql = $sql -replace [Regex]::Escape("'$source'"), "'$destinstance'"
                    }
                    catch {
                        $copyBackupDeviceStatus.Status = "Failed"
                        $copyBackupDeviceStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Stop-Function -Message "Issue scripting out backup device" -Target $deviceName -ErrorRecord $_ -Continue
                    }
                }
                
                if ($Pscmdlet.ShouldProcess("console", "Stating that the actual file copy is about to occur")) {
                    Write-Message -Level Verbose -Message "Preparing to copy actual backup file"
                }
                
                $path = Split-Path $sourceServer.BackupDevices[$deviceName].PhysicalLocation
                $destPath = Join-AdminUnc $destNetBios $path
                $sourcepath = Join-AdminUnc $sourceNetBios $sourceServer.BackupDevices[$deviceName].PhysicalLocation
                
                Write-Message -Level Verbose -Message "Checking if directory $destPath exists"
                
                if ($(Test-DbaSqlPath -SqlInstance $destinstance -Path $path) -eq $false) {
                    $backupDirectory = $destServer.BackupDirectory
                    $destPath = Join-AdminUnc $destNetBios $backupDirectory
                    
                    if ($Pscmdlet.ShouldProcess($destinstance, "Updating create code to use new path")) {
                        Write-Message -Level Verbose -Message "$path doesn't exist on $destinstance"
                        Write-Message -Level Verbose -Message "Using default backup directory $backupDirectory"
                        
                        try {
                            Write-Message -Level Verbose -Message "Updating $deviceName to use $backupDirectory"
                            $sql = $sql -replace [Regex]::Escape($path), $backupDirectory
                        }
                        catch {
                            $copyBackupDeviceStatus.Status = "Failed"
                            $copyBackupDeviceStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            
                            Stop-Function -Message "Issue updating script of backup device with new path" -Target $deviceName -ErrorRecord $_ -Continue
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Copying $sourcepath to $destPath using BITSTransfer")) {
                    try {
                        Start-BitsTransfer -Source $sourcepath -Destination $destPath -ErrorAction Stop
                        Write-Message -Level Verbose -Message "Backup device $deviceName successfully copied"
                    }
                    catch {
                        $copyBackupDeviceStatus.Status = "Failed"
                        $copyBackupDeviceStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Stop-Function -Message "Issue copying backup device to destination" -Target $deviceName -ErrorRecord $_ -Continue
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Adding backup device $deviceName")) {
                    Write-Message -Level Verbose -Message "Adding backup device $deviceName on $destinstance"
                    try {
                        $destServer.Query($sql)
                        $destServer.BackupDevices.Refresh()
                        
                        $copyBackupDeviceStatus.Status = "Successful"
                        $copyBackupDeviceStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copyBackupDeviceStatus.Status = "Failed"
                        $copyBackupDeviceStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Stop-Function -Message "Issue adding backup device" -Target $deviceName -ErrorRecord $_ -Continue
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlBackupDevice
    }
}
tools\dbatools\functions\Copy-DbaCentralManagementServer.ps1
function Copy-DbaCentralManagementServer {
    <#
        .SYNOPSIS
            Migrates SQL Server Central Management groups and server instances from one SQL Server to another.

        .DESCRIPTION
            Copy-DbaCentralManagementServer copies all groups, subgroups, and server instances from one SQL Server to another.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER CMSGroup
            This is an auto-populated array that contains your Central Management Server top-level groups on Source. You can specify one, many or none.

            If CMSGroup is not specified, all groups in your Central Management Server will be copied.

        .PARAMETER SwitchServerName
            If this switch is enabled, all instance names will be changed from Source to Destination.

            Central Management Server does not allow you to add a shared registered server with the same name as the Configuration Server.

        .PARAMETER Force
            If this switch is enabled, group(s) will be dropped and recreated if they already exists on destination.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaCentralManagementServer

        .EXAMPLE
            Copy-DbaCentralManagementServer -Source sqlserver2014a -Destination sqlcluster

            All groups, subgroups, and server instances are copied from sqlserver's Central Management Server to sqlcluster's Central Management Server.

        .EXAMPLE
            Copy-DbaCentralManagementServer -Source sqlserver2014a -Destination sqlcluster -ServerGroup Group1,Group3

            Top-level groups Group1 and Group3 along with their subgroups and server instances are copied from sqlserver to sqlcluster.

        .EXAMPLE
            Copy-DbaCentralManagementServer -Source sqlserver2014a -Destination sqlcluster -ServerGroup Group1,Group3 -SwitchServerName -SourceSqlCredential $SourceSqlCredential -DestinationSqlCredential $DestinationSqlCredential

            Top-level groups Group1 and Group3 along with their subgroups and server instances are copied from sqlserver to sqlcluster. When adding sql instances to sqlcluster, if the server name of the migrating instance is "sqlcluster", it will be switched to "sqlserver".

            If SwitchServerName is not specified, "sqlcluster" will be skipped.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    Param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]$SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [object[]]$CMSGroup,
        [switch]$SwitchServerName,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        function Invoke-ParseServerGroup {
            [cmdletbinding()]
            param (
                $sourceGroup,
                $destinationGroup,
                $SwitchServerName
            )
            if ($destinationGroup.Name -eq "DatabaseEngineServerGroup" -and $sourceGroup.Name -ne "DatabaseEngineServerGroup") {
                $currentServerGroup = $destinationGroup
                $groupName = $sourceGroup.Name
                $destinationGroup = $destinationGroup.ServerGroups[$groupName]
                
                $copyDestinationGroupStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name         = $groupName
                    Type         = "CMS Destination Group"
                    Status       = $null
                    Notes        = $null
                    DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                }
                
                if ($null -ne $destinationGroup) {
                    
                    if ($force -eq $false) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Checking to see if $groupName exists")) {
                            $copyDestinationGroupStatus.Status = "Skipped"
                            $copyDestinationGroupStatus.Notes = "Already exists"
                            $copyDestinationGroupStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Write-Message -Level Verbose -Message "Destination group $groupName exists at destination. Use -Force to drop and migrate."
                        }
                        continue
                    }
                    if ($Pscmdlet.ShouldProcess($destinstance, "Dropping group $groupName")) {
                        try {
                            Write-Message -Level Verbose -Message "Dropping group $groupName"
                            $destinationGroup.Drop()
                        }
                        catch {
                            $copyDestinationGroupStatus.Status = "Failed"
                            $copyDestinationGroupStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            
                            Stop-Function -Message "Issue dropping group" -Target $groupName -ErrorRecord $_ -Continue
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Creating group $groupName")) {
                    Write-Message -Level Verbose -Message "Creating group $($sourceGroup.Name)"
                    $destinationGroup = New-Object Microsoft.SqlServer.Management.RegisteredServers.ServerGroup($currentServerGroup, $sourceGroup.Name)
                    $destinationGroup.Create()
                    
                    $copyDestinationGroupStatus.Status = "Successful"
                    $copyDestinationGroupStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                }
            }
            
            # Add Servers
            foreach ($instance in $sourceGroup.RegisteredServers) {
                $instanceName = $instance.Name
                $serverName = $instance.ServerName
                
                $copyInstanceStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name         = $instanceName
                    Type         = "CMS Instance"
                    Status       = $null
                    Notes        = $null
                    DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                }
                
                if ($serverName.ToLower() -eq $toCmStore.DomainInstanceName.ToLower()) {
                    if ($Pscmdlet.ShouldProcess($destinstance, "Checking to see if server is the CMS equals current server name")) {
                        if ($SwitchServerName) {
                            $serverName = $fromCmStore.DomainInstanceName
                            $instanceName = $fromCmStore.DomainInstanceName
                            Write-Message -Level Verbose -Message "SwitchServerName was used and new CMS equals current server name. $($toCmStore.DomainInstanceName.ToLower()) changed to $serverName."
                        }
                        else {
                            $copyInstanceStatus.Status = "Skipped"
                            $copyInstanceStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            
                            Write-Message -Level Verbose -Message "$serverName is Central Management Server. Add prohibited. Skipping."
                            continue
                        }
                    }
                }
                
                if ($destinationGroup.RegisteredServers.Name -contains $instanceName) {
                    
                    if ($force -eq $false) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Checking to see if $instanceName in $groupName exists")) {
                            $copyInstanceStatus.Status = "Skipped"
                            $copyInstanceStatus.Notes = "Already exists"
                            $copyInstanceStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            
                            Write-Message -Level Verbose -Message "Instance $instanceName exists in group $groupName at destination. Use -Force to drop and migrate."
                        }
                        continue
                    }
                    
                    if ($Pscmdlet.ShouldProcess($destinstance, "Dropping instance $instanceName from $groupName and recreating")) {
                        try {
                            Write-Message -Level Verbose -Message "Dropping instance $instance from $groupName"
                            $destinationGroup.RegisteredServers[$instanceName].Drop()
                        }
                        catch {
                            $copyInstanceStatus.Status = "Failed"
                            $copyInstanceStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            
                            Stop-Function -Message "Issue dropping instance from group" -Target $instanceName -ErrorRecord $_ -Continue
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Copying $instanceName")) {
                    $newServer = New-Object Microsoft.SqlServer.Management.RegisteredServers.RegisteredServer($destinationGroup, $instanceName)
                    $newServer.ServerName = $serverName
                    $newServer.Description = $instance.Description
                    
                    if ($serverName -ne $fromCmStore.DomainInstanceName) {
                        $newServer.SecureConnectionString = $instance.SecureConnectionString.ToString()
                        $newServer.ConnectionString = $instance.ConnectionString.ToString()
                    }
                    
                    try {
                        $newServer.Create()
                        
                        $copyInstanceStatus.Status = "Successful"
                        $copyInstanceStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copyInstanceStatus.Status = "Failed"
                        $copyInstanceStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        if ($_.Exception -match "same name") {
                            Stop-Function -Message "Could not add Switched Server instance name." -Target $instanceName -ErrorRecord $_ -Continue
                        }
                        else {
                            Stop-Function -Message "Failed to add $serverName" -Target $instanceName -ErrorRecord $_ -Continue
                        }
                    }
                    Write-Message -Level Verbose -Message "Added Server $serverName as $instanceName to $($destinationGroup.Name)"
                }
            }
            
            # Add Groups
            foreach ($fromSubGroup in $sourceGroup.ServerGroups) {
                $fromSubGroupName = $fromSubGroup.Name
                $toSubGroup = $destinationGroup.ServerGroups[$fromSubGroupName]
                
                $copyGroupStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name         = $fromSubGroupName
                    Type         = "CMS Group"
                    Status       = $null
                    Notes        = $null
                    DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                }
                
                if ($null -ne $toSubGroup) {
                    if ($force -eq $false) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Checking to see if subgroup $fromSubGroupName exists")) {
                            $copyGroupStatus.Status = "Skipped"
                            $copyGroupStatus.Notes = "Already exists"
                            $copyGroupStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            
                            Write-Message -Level Verbose -Message "Subgroup $fromSubGroupName exists at destination. Use -Force to drop and migrate."
                        }
                        continue
                    }
                    
                    if ($Pscmdlet.ShouldProcess($destinstance, "Dropping subgroup $fromSubGroupName recreating")) {
                        try {
                            Write-Message -Level Verbose -Message "Dropping subgroup $fromSubGroupName"
                            $toSubGroup.Drop()
                        }
                        catch {
                            $copyGroupStatus.Status = "Failed"
                            $copyGroupStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            
                            Stop-Function -Message "Issue dropping subgroup" -Target $toSubGroup -ErrorRecord $_ -Continue
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Creating group $($fromSubGroup.Name)")) {
                    Write-Message -Level Verbose -Message "Creating group $($fromSubGroup.Name)"
                    $toSubGroup = New-Object Microsoft.SqlServer.Management.RegisteredServers.ServerGroup($destinationGroup, $fromSubGroup.Name)
                    $toSubGroup.create()
                    
                    $copyGroupStatus.Status = "Successful"
                    $copyGroupStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                }
                
                Invoke-ParseServerGroup -sourceGroup $fromSubGroup -destinationgroup $toSubGroup -SwitchServerName $SwitchServerName
            }
        }
        
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 10
            $fromCmStore = Get-DbaRegisteredServerStore -SqlInstance $sourceServer
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
    }
    
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            $toCmStore = Get-DbaRegisteredServerStore -SqlInstance $destServer
            
            $stores = $fromCmStore.DatabaseEngineServerGroup
            if ($CMSGroup) {
                $stores = @();
                foreach ($groupName in $CMSGroup) {
                    $stores += $fromCmStore.DatabaseEngineServerGroup.ServerGroups[$groupName]
                }
            }
            
            foreach ($store in $stores) {
                Invoke-ParseServerGroup -sourceGroup $store -destinationgroup $toCmStore.DatabaseEngineServerGroup -SwitchServerName $SwitchServerName
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlCentralManagementServer
    }
}
tools\dbatools\functions\Copy-DbaCredential.ps1
function Copy-DbaCredential {
    <#
        .SYNOPSIS
            Copy-DbaCredential migrates SQL Server Credentials from one SQL Server to another while maintaining Credential passwords.

        .DESCRIPTION
            By using password decryption techniques provided by Antti Rantasaari (NetSPI, 2014), this script migrates SQL Server Credentials from one server to another while maintaining username and password.

            Credit: https://blog.netspi.com/decrypting-mssql-database-link-server-passwords/
            License: BSD 3-Clause http://opensource.org/licenses/BSD-3-Clause

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Credential
             This command requires access to the Windows OS via PowerShell remoting. Use this credential to connect to Windows using alternative credentials.

        .PARAMETER Name
            Only include specific names
            Note: if spaces exist in the credential name, you will have to type "" or '' around it.

        .PARAMETER ExcludeName
            Excluded credential names
    
        .PARAMETER Identity
            Only include specific identities
            Note: if spaces exist in the credential identity, you will have to type "" or '' around it.

        .PARAMETER ExcludeIdentity
            Excluded identities

        .PARAMETER Force
            If this switch is enabled, the Credential will be dropped and recreated if it already exists on Destination.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: WSMan, Migration
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires:
                - PowerShell Version 3.0, SQL Server SMO,
                - Administrator access on Windows
                - sysadmin access on SQL Server.
                - DAC access enabled for local (default)
            Limitations: Hasn't been tested thoroughly. Works on Win8.1 and SQL Server 2012 & 2014 so far.

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaCredential

        .EXAMPLE
            Copy-DbaCredential -Source sqlserver2014a -Destination sqlcluster

            Copies all SQL Server Credentials on sqlserver2014a to sqlcluster. If Credentials exist on destination, they will be skipped.

        .EXAMPLE
            Copy-DbaCredential -Source sqlserver2014a -Destination sqlcluster -Name "PowerShell Proxy Account" -Force

            Copies over one SQL Server Credential (PowerShell Proxy Account) from sqlserver to sqlcluster. If the Credential already exists on the destination, it will be dropped and recreated.
    #>
    [CmdletBinding(SupportsShouldProcess)]
    param (
        [parameter(Mandatory)]
        [DbaInstanceParameter]$Source,
        [PSCredential]
        $SourceSqlCredential,
        [PSCredential]
        $Credential,
        [parameter(Mandatory)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [string[]]$Name,
        [string[]]$ExcludeName,
        [Alias('CredentialIdentity')]
        [string[]]$Identity,
        [Alias('ExcludeCredentialIdentity')]
        [string[]]$ExcludeIdentity,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $null = Test-ElevationRequirement -ComputerName $Source.ComputerName

        function Copy-Credential {
            <#
                .SYNOPSIS
                    Copies Credentials from one server to another using a combination of SMO's .Script() and manual password updates.

                .OUTPUT
                    System.Data.DataTable
            #>
            param (
                [string[]]$Credentials,
                [bool]$Force
            )

            Write-Message -Level Verbose -Message "Collecting Credential logins and passwords on $($sourceServer.Name)"
            $sourceCredentials = Get-DecryptedObject -SqlInstance $sourceServer -Type Credential
            $credentialList = Get-DbaCredential -SqlInstance $sourceServer -Name $Name -ExcludeName $ExcludeName -Identity $Identity -ExcludeIdentity $ExcludeIdentity
            
            Write-Message -Level Verbose -Message "Starting migration"
            foreach ($credential in $credentialList) {
                $destServer.Credentials.Refresh()
                $credentialName = $credential.Name

                $copyCredentialStatus = [pscustomobject]@{
                    SourceServer      = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Type              = "Credential"
                    Name              = $credentialName
                    Status            = $null
                    Notes             = $null
                    DateTime          = [DbaDateTime](Get-Date)
                }

                if ($null -ne $destServer.Credentials[$credentialName]) {
                    if (!$force) {
                        $copyCredentialStatus.Status = "Skipping"
                        $copyCredentialStatus.Notes = "Already exists"
                        $copyCredentialStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject

                        Write-Message -Level Verbose -Message "$credentialName exists $($destServer.Name). Skipping."
                        continue
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance.Name, "Dropping $identity")) {
                            $destServer.Credentials[$credentialName].Drop()
                            $destServer.Credentials.Refresh()
                        }
                    }
                }
                
                Write-Message -Level Verbose -Message "Attempting to migrate $credentialName"
                try {
                    $currentCred = $sourceCredentials | Where-Object { $_.Name -eq "[$credentialName]" }
                    $sqlcredentialName = $credentialName.Replace("'", "''")
                    $identity = $currentCred.Identity.Replace("'", "''")
                    $password = $currentCred.Password.Replace("'","''")
                    if ($Pscmdlet.ShouldProcess($destinstance.Name, "Copying $identity")) {
                        $destServer.Query("CREATE CREDENTIAL [$sqlcredentialName] WITH IDENTITY = N'$identity', SECRET = N'$password'")
                        $destServer.Credentials.Refresh()
                        Write-Message -Level Verbose -Message "$credentialName successfully copied"
                    }

                    $copyCredentialStatus.Status = "Successful"
                    $copyCredentialStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                }
                catch {
                    $copyCredentialStatus.Status = "Failed"
                    $copyCredentialStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject

                    Stop-Function -Message "Error creating credential" -Target $credentialName -ErrorRecord $_
                }
            }
        }
        
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 9
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance
            return
        }
        
        if ($null -ne $SourceSqlCredential.Username) {
            Write-Message -Level Verbose -Message "You are using SQL credentials and this script requires Windows admin access to the $Source server. Trying anyway."
        }
        
        $sourceNetBios = Resolve-NetBiosName $sourceServer
        
        Invoke-SmoCheck -SqlInstance $sourceServer
        
        Write-Message -Level Verbose -Message "Checking if Remote Registry is enabled on $source"
        try {
            Invoke-Command2 -ComputerName $sourceNetBios -Credential $credential -ScriptBlock { Get-ItemProperty -Path "HKLM:\SOFTWARE\" }
        }
        catch {
            Stop-Function -Message "Can't connect to registry on $source" -Target $sourceNetBios -ErrorRecord $_
            return
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }
        
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            Invoke-SmoCheck -SqlInstance $destServer
            
            Copy-Credential $credentials -force:$force
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlCredential
    }
}
tools\dbatools\functions\Copy-DbaCustomError.ps1
function Copy-DbaCustomError {
    <#
        .SYNOPSIS
            Copy-DbaCustomError migrates custom errors (user defined messages), by the custom error ID, from one SQL Server to another.

        .DESCRIPTION
            By default, all custom errors are copied. The -CustomError parameter is auto-populated for command-line completion and can be used to copy only specific custom errors.

            If the custom error already exists on the destination, it will be skipped unless -Force is used. The us_english version must be created first. If you drop the us_english version, all the other languages will be dropped for that specific ID as well.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER CustomError
            The custom error(s) to process. This list is auto-populated from the server. If unspecified, all custom errors will be processed.

        .PARAMETER ExcludeCustomError
            The custom error(s) to exclude. This list is auto-populated from the server.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER Force
            If this switch is enabled, the custom error will be dropped and recreated if it already exists on Destination.

        .NOTES
            Tags: Migration, CustomError
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaCustomError

        .EXAMPLE
            Copy-DbaCustomError -Source sqlserver2014a -Destination sqlcluster

            Copies all server custom errors from sqlserver2014a to sqlcluster using Windows credentials. If custom errors with the same name exist on sqlcluster, they will be skipped.

        .EXAMPLE
            Copy-DbaCustomError -Source sqlserver2014a -SourceSqlCredential $scred -Destination sqlcluster -DestinationSqlCredential $dcred -CustomError 60000 -Force

            Copies only the custom error with ID number 60000 from sqlserver2014a to sqlcluster using SQL credentials for sqlserver2014a and Windows credentials for sqlcluster. If a custom error with the same name exists on sqlcluster, it will be updated because -Force was used.

        .EXAMPLE
            Copy-DbaCustomError -Source sqlserver2014a -Destination sqlcluster -ExcludeCustomError 60000 -Force

            Copies all the custom errors found on sqlserver2014a except the custom error with ID number 60000 to sqlcluster. If a custom error with the same name exists on sqlcluster, it will be updated because -Force was used.

        .EXAMPLE
            Copy-DbaCustomError -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force

            Shows what would happen if the command were executed using force.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]
        $SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]
        $DestinationSqlCredential,
        [object[]]$CustomError,
        [object[]]$ExcludeCustomError,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 9
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        $orderedCustomErrors = @($sourceServer.UserDefinedMessages | Where-Object Language -eq "us_english")
        $orderedCustomErrors += $sourceServer.UserDefinedMessages | Where-Object Language -ne "us_english"
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            # US has to go first
            $destCustomErrors = $destServer.UserDefinedMessages
            
            foreach ($currentCustomError in $orderedCustomErrors) {
                $customErrorId = $currentCustomError.ID
                $language = $currentCustomError.Language.ToString()
                
                $copyCustomErrorStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Type         = "Custom error"
                    Name         = $currentCustomError
                    Status       = $null
                    Notes        = $null
                    DateTime     = [DbaDateTime](Get-Date)
                }
                
                if ($CustomError -and ($customErrorId -notin $CustomError -or $customErrorId -in $ExcludeCustomError)) {
                    continue
                }
                
                if ($destCustomErrors.ID -contains $customErrorId) {
                    if ($force -eq $false) {
                        $copyCustomErrorStatus.Status = "Skipped"
                        $copyCustomErrorStatus.Notes = "Already exists"
                        $copyCustomErrorStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Write-Message -Level Verbose -Message "Custom error $customErrorId $language exists at destination. Use -Force to drop and migrate."
                        continue
                    }
                    else {
                        If ($Pscmdlet.ShouldProcess($destinstance, "Dropping custom error $customErrorId $language and recreating")) {
                            try {
                                Write-Message -Level Verbose -Message "Dropping custom error $customErrorId (drops all languages for custom error $customErrorId)"
                                $destServer.UserDefinedMessages[$customErrorId, $language].Drop()
                            }
                            catch {
                                $copyCustomErrorStatus.Status = "Failed"
                                $copyCustomErrorStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                
                                Stop-Function -Message "Issue dropping custom error" -Target $customErrorId -ErrorRecord $_ -Continue
                            }
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Creating custom error $customErrorId $language")) {
                    try {
                        Write-Message -Level Verbose -Message "Copying custom error $customErrorId $language"
                        $sql = $currentCustomError.Script() | Out-String
                        Write-Message -Level Debug -Message $sql
                        $destServer.Query($sql)
                        
                        $copyCustomErrorStatus.Status = "Successful"
                        $copyCustomErrorStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copyCustomErrorStatus.Status = "Failed"
                        $copyCustomErrorStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Stop-Function -Message "Issue creating custom error" -Target $customErrorId -ErrorRecord $_
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlCustomError
    }
}
tools\dbatools\functions\Copy-DbaDatabase.ps1
function Copy-DbaDatabase {
    <#
        .SYNOPSIS
            Migrates SQL Server databases from one SQL Server to another.

        .DESCRIPTION
            This script provides the ability to migrate databases using detach/copy/attach or backup/restore. This script works with named instances, clusters and SQL Server Express Edition.

            By default, databases will be migrated to the destination SQL Server's default data and log directories. You can override this by specifying -ReuseSourceFolderStructure. Filestreams and filegroups are also migrated. Safety is emphasized.

        .PARAMETER Source
            Source SQL Server.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You may specify multiple servers.

            Note that when using -BackupRestore with multiple servers, the backup will only be performed once and backups will be deleted at the end (if you didn't specify -NoBackupCleanup).

            When using -DetachAttach with multiple servers, -Reattach must be specified.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Migrates only specified databases. This list is auto-populated from the server for tab completion. Multiple databases may be specified as a collection.

        .PARAMETER ExcludeDatabase
            Excludes specified databases when performing -AllDatabases migrations. This list is auto-populated from the Source for tab completion.

        .PARAMETER AllDatabases
            If this switch is enabled, all user databases will be migrated. System and support databases will not be migrated. Requires -BackupRestore or -DetachAttach.

        .PARAMETER BackupRestore
            If this switch is enabled, the copy-only backup and restore method will be used to migrate the database(s). This method requires that you specify -NetworkShare in a valid UNC format (\\server\share).

            Backups will be immediately deleted after use unless -NoBackupCleanup is specified.

        .PARAMETER NetworkShare
            Specifies the network location for the backup files. The SQL Server service accounts must have read/write permission on this path.

        .PARAMETER WithReplace
            If this switch is enabled, the restore is executed with WITH REPLACE.

        .PARAMETER NoRecovery
            If this switch is enabled, the restore is executed with WITH NORECOVERY. Ideal for staging.

        .PARAMETER NoBackupCleanup
            If this switch is enabled, backups generated by this cmdlet will not be deleted after they are restored. The default behavior is to delete these backups.

        .PARAMETER NumberFiles
            Number of files to split the backup. Default is 3.

        .PARAMETER DetachAttach
            If this switch is enabled, the detach/copy/attach method is used to perform database migrations. No files are deleted on Source. If Destination attachment fails, the Source database will be reattached. File copies are performed over administrative shares (\\server\x$\mssql) using BITS. If a database is being mirrored, the mirror will be broken prior to migration.

        .PARAMETER Reattach
            If this switch is enabled, all databases are reattached to Source after DetachAttach migration.

        .PARAMETER SetSourceReadOnly
            If this switch is enabled, all migrated databases are set to ReadOnly on Source prior to detach/attach & backup/restore.

            If -Reattach is used, databases are set to read-only after reattaching.

        .PARAMETER ReuseSourceFolderStructure
            If this switch is enabled, databases will be migrated to a data and log directory structure on Destination mirroring that used on Source. By default, the default data and log directories for Destination will be used when the databases are migrated.

            The structure on Source  will be kept exactly, so consider this if you're migrating between different versions and use part of Microsoft's default Sql structure (MSSql12.INSTANCE, etc)

            To reuse Destination folder structure, use the  -WithReplace switch.

        .PARAMETER IncludeSupportDbs
            If this switch is enabled, ReportServer, ReportServerTempDb, SSISDB, and distribution databases will be copied if they exist on Source. A log file named $SOURCE-$destinstance-$date-Sqls.csv will be written to the current directory.

            Use of this switch requires -BackupRestore or -DetachAttach as well.

        .PARAMETER InputObject
            A collection of dbobjects from the pipeline.

        .PARAMETER UseLastBackups
            Use the last full, diff and logs instead of performing backups. Note that the backups must exist in a location accessible by all destination servers, such a network share.

        .PARAMETER NoCopyOnly
             If this switch is enabled, backups will be taken without COPY_ONLY. This will break the LSN backup chain, which will interfere with the restore chain of the database.

            By default this switch is disabled, so backups will be taken with COPY_ONLY. This will preserve the LSN backup chain.

            For more details please refer to this MSDN article - https://msdn.microsoft.com/en-us/library/ms191495.aspx

        .PARAMETER SetSourceOffline
            If this switch is enabled, the Source database will be set to Offline after being copied.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER Force
            If this switch is enabled, existing databases on Destination with matching names from Source will be dropped. If using -DetachReattach, mirrors will be broken and the database(s) dropped from Availability Groups.

        .NOTES
            Tags: Migration, Backup, Restore
            Author: Chrissy LeMaire (@cl), netnerds.net

            Requires: sysadmin access on SQL Servers
            Limitations: Doesn't cover what it doesn't cover (replication, certificates, etc)

            SQL Server 2000 databases cannot be directly migrated to SQL Server 2012 and above.
            Logins within SQL Server 2012 and above logins cannot be migrated to SQL Server 2008 R2 and below.

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaDatabase

        .EXAMPLE
            Copy-DbaDatabase -Source sql2014a -Destination sql2014b -Database TestDB -BackupRestore -NetworkShare \\fileshare\sql\migration

            Migrates a single user database TestDB using Backup and restore from instance sql2014a to sql2014b. Backup files are stored in \\fileshare\sql\migration.

        .EXAMPLE
            Copy-DbaDatabase -Source sql2012 -Destination sql2014, sql2016 -DetachAttach -Reattach

            Databases will be migrated from sql2012 to both sql2014 and sql2016 using the detach/copy files/attach method.The following will be performed: kick all users out of the database, detach all data/log files, move files across the network over an admin share (\\SqlSERVER\M$\MSSql...), attach file on destination server, reattach at source. If the database files (*.mdf, *.ndf, *.ldf) on *destination* exist and aren't in use, they will be overwritten.

        .EXAMPLE
            Copy-DbaDatabase -Source sql2014a -Destination sqlcluster, sql2016 -BackupRestore -UseLastBackups -Force

            Migrates all user databases to sqlcluster and sql2016 using the last Full, Diff and Log backups from sql204a. If the databases exists on the destinations, they will be dropped prior to attach.

            Note that the backups must exist in a location accessible by all destination servers, such a network share.

        .EXAMPLE
            Copy-DbaDatabase -Source sql2014a -Destination sqlcluster -ExcludeDatabase Northwind, pubs -IncludeSupportDbs -Force -BackupRestore -NetworkShare \\fileshare\sql\migration

            Migrates all user databases except for Northwind and pubs by using backup/restore (copy-only). Backup files are stored in \\fileshare\sql\migration. If the database exists on the destination, it will be dropped prior to attach.

            It also includes the support databases (ReportServer, ReportServerTempDb, distribution).
    #>
    [CmdletBinding(DefaultParameterSetName = "DbBackup", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $false)]
        [DbaInstanceParameter]$Source,
        [PSCredential]$SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Alias("All")]
        [parameter(ParameterSetName = "DbBackup")]
        [parameter(ParameterSetName = "DbAttachDetach")]
        [switch]$AllDatabases,
        [parameter(Mandatory = $true, ParameterSetName = "DbBackup")]
        [switch]$BackupRestore,
        [parameter(ParameterSetName = "DbBackup",
                   HelpMessage = "Specify a valid network share in the format \\server\share that can be accessed by your account and the SQL Server service accounts for both Source and Destination.")]
        [string]$NetworkShare,
        [parameter(ParameterSetName = "DbBackup")]
        [switch]$WithReplace,
        [parameter(ParameterSetName = "DbBackup")]
        [switch]$NoRecovery,
        [parameter(ParameterSetName = "DbBackup")]
        [switch]$NoBackupCleanup,
        [parameter(ParameterSetName = "DbBackup")]
        [ValidateRange(1, 64)]
        [int]$NumberFiles = 3,
        [parameter(Mandatory = $true, ParameterSetName = "DbAttachDetach")]
        [switch]$DetachAttach,
        [parameter(ParameterSetName = "DbAttachDetach")]
        [switch]$Reattach,
        [parameter(ParameterSetName = "DbBackup")]
        [parameter(ParameterSetName = "DbAttachDetach")]
        [switch]$SetSourceReadOnly,
        [Alias("ReuseFolderStructure")]
        [parameter(ParameterSetName = "DbBackup")]
        [parameter(ParameterSetName = "DbAttachDetach")]
        [switch]$ReuseSourceFolderStructure,
        [parameter(ParameterSetName = "DbBackup")]
        [parameter(ParameterSetName = "DbAttachDetach")]
        [switch]$IncludeSupportDbs,
        [parameter(ParameterSetName = "DbBackup")]
        [switch]$UseLastBackups,
        [parameter(ValueFromPipeline)]
        [Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
        [switch]$NoCopyOnly,
        [switch]$SetSourceOffline,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        $CopyOnly = -not $NoCopyOnly

        if ($BackupRestore -and (-not $NetworkShare -and -not $UseLastBackups)) {
            Stop-Function -Message "When using -BackupRestore, you must specify -NetworkShare or -UseLastBackups"
            return
        }
        if ($NetworkShare -and $UseLastBackups) {
            Stop-Function -Message "-NetworkShare cannot be used with -UseLastBackups because the backup path is determined by the paths in the last backups"
            return
        }
        if ($DetachAttach -and -not $Reattach -and $Destination.Count -gt 1) {
            Stop-Function -Message "When using -DetachAttach with multiple servers, you must specify -Reattach to reattach database at source"
            return
        }
        function Join-AdminUnc {
            <#
        .SYNOPSIS
        Internal function. Parses a path to make it an admin UNC.
        #>
            [CmdletBinding()]
            param (
                [Parameter(Mandatory = $true)]
                [ValidateNotNullOrEmpty()]
                [string]$servername,
                [Parameter(Mandatory = $true)]
                [ValidateNotNullOrEmpty()]
                [string]$filepath

            )

            if ($script:sameserver) { return $filepath }
            if (-not $filepath) { return }
            if ($filepath.StartsWith("\\")) { return $filepath }

            $servername = $servername.Split("\")[0]

            if ($filepath.length -gt 0 -and $filepath -ne [System.DbNull]::Value) {
                $newpath = Join-Path "\\$servername\" $filepath.replace(':', '$')
                return $newpath
            }
            else { return }
        }

        function Get-SqlFileStructure {
            $dbcollection = @{ };
            $databaseProgressbar = 0

            foreach ($db in $databaseList) {
                Write-Progress -Id 1 -Activity "Processing database file structure" -PercentComplete ($databaseProgressbar / $dbCount * 100) -Status "Processing $databaseProgressbar of $dbCount."
                $dbName = $db.Name
                Write-Message -Level Verbose -Message $dbName

                $databaseProgressbar++
                $dbStatus = $db.status.toString()
                if ($dbStatus.StartsWith("Normal") -eq $false) { continue }
                $destinstancefiles = @{ }; $sourcefiles = @{ }

                $where = "Filetype <> 'LOG' and Filetype <> 'FULLTEXT'"

                $datarows = $dbFileTable.Tables.Select("dbname = '$dbName' and $where")

                # Data Files
                foreach ($file in $datarows) {
                    # Destination File Structure
                    $d = @{ }
                    if ($ReuseSourceFolderStructure) {
                        $d.physical = $file.filename
                    }
                    elseif ($WithReplace) {
                        $name = $file.Name
                        $destfile = $remoteDbFileTable.Tables[0].Select("dbname = '$dbName' and name = '$name'")
                        $d.physical = $destfile.filename

                        if ($null -eq $d.physical) {
                            $directory = Get-SqlDefaultPaths $destServer data
                            $fileName = Split-Path $file.filename -Leaf
                            $d.physical = "$directory\$fileName"
                        }
                    }
                    else {
                        $directory = Get-SqlDefaultPaths $destServer data
                        $fileName = Split-Path $file.filename -Leaf
                        $d.physical = "$directory\$fileName"
                    }
                    $d.logical = $file.Name

                    $d.remotefilename = Join-AdminUNC $destNetBios $d.physical
                    $destinstancefiles.add($file.Name, $d)

                    # Source File Structure
                    $s = @{ }
                    $s.logical = $file.Name
                    $s.physical = $file.filename
                    $s.remotefilename = Join-AdminUNC $sourceNetBios $s.physical
                    $sourcefiles.add($file.Name, $s)
                }

                # Add support for Full Text Catalogs in SQL Server 2005 and below
                if ($sourceServer.VersionMajor -lt 10) {
                    try {
                        $fttable = $null = $sourceServer.Databases[$dbName].ExecuteWithResults('sp_help_fulltext_catalogs')
                        $allrows = $fttable.Tables[0].rows
                    }
                    catch {
                        # Nothing, it's just not enabled
                    }

                    foreach ($ftc in $allrows) {
                        # Destination File Structure
                        $d = @{ }
                        $pre = "sysft_"
                        $name = $ftc.Name
                        $physical = $ftc.Path # RootPath
                        $logical = "$pre$name"
                        if ($ReuseSourceFolderStructure) {
                            $d.physical = $physical
                        }
                        else {
                            $directory = Get-SqlDefaultPaths $destServer data
                            if ($destServer.VersionMajor -lt 10) { $directory = "$directory\FTDATA" }
                            $fileName = Split-Path($physical) -leaf
                            $d.physical = "$directory\$fileName"
                        }
                        $d.logical = $logical
                        $d.remotefilename = Join-AdminUNC $destNetBios $d.physical
                        $destinstancefiles.add($logical, $d)

                        # Source File Structure
                        $s = @{ }
                        $pre = "sysft_"
                        $name = $ftc.Name
                        $physical = $ftc.Path # RootPath
                        $logical = "$pre$name"

                        $s.logical = $logical
                        $s.physical = $physical
                        $s.remotefilename = Join-AdminUNC $sourceNetBios $s.physical
                        $sourcefiles.add($logical, $s)
                    }
                }

                $where = "Filetype = 'LOG'"
                $datarows = $dbFileTable.Tables[0].Select("dbname = '$dbName' and $where")

                # Log Files
                foreach ($file in $datarows) {
                    $d = @{ }
                    if ($ReuseSourceFolderStructure) {
                        $d.physical = $file.filename
                    }
                    elseif ($WithReplace) {
                        $name = $file.Name
                        $destfile = $remoteDbFileTable.Tables[0].Select("dbname = '$dbName' and name = '$name'")
                        $d.physical = $destfile.filename

                        if ($null -eq $d.physical) {
                            $directory = Get-SqlDefaultPaths $destServer data
                            $fileName = Split-Path $file.filename -Leaf
                            $d.physical = "$directory\$fileName"
                        }
                    }
                    else {
                        $directory = Get-SqlDefaultPaths $destServer log
                        $fileName = Split-Path $file.filename -Leaf
                        $d.physical = "$directory\$fileName"
                    }
                    $d.logical = $file.Name
                    $d.remotefilename = Join-AdminUNC $destNetBios $d.physical
                    $destinstancefiles.add($file.Name, $d)

                    $s = @{ }
                    $s.logical = $file.Name
                    $s.physical = $file.filename
                    $s.remotefilename = Join-AdminUNC $sourceNetBios $s.physical
                    $sourcefiles.add($file.Name, $s)
                }

                $location = @{ }
                $location.add("Destination", $destinstancefiles)
                $location.add("Source", $sourcefiles)
                $dbcollection.Add($($db.Name), $location)
            }

            $fileStructure = [pscustomobject]@{ "databases" = $dbcollection }
            Write-Progress -id 1 -Activity "Processing database file structure" -Status "Completed" -Completed
            return $fileStructure
        }

        function Dismount-SqlDatabase {
            [CmdletBinding()]
            param (
                [object]$server,
                [string]$dbName
            )

            $currentdb = $server.databases[$dbName]
            if ($currentdb.IsMirroringEnabled) {
                try {
                    Write-Message -Level Verbose -Message "Breaking mirror for $dbName"
                    $currentdb.ChangeMirroringState([Microsoft.SqlServer.Management.Smo.MirroringOption]::Off)
                    $currentdb.Alter()
                    $currentdb.Refresh()
                    Write-Message -Level Verbose -Message "Could not break mirror for $dbName. Skipping."
                }
                catch {
                    Stop-Function -Message "Issue breaking mirror." -Target $dbName -ErrorRecord $_
                    return $false
                }
            }

            if ($currentdb.AvailabilityGroupName.Length -gt 0) {
                $agName = $currentdb.AvailabilityGroupName
                Write-Message -Level Verbose -Message "Attempting remove from Availability Group $agName."
                try {
                    $server.AvailabilityGroups[$currentdb.AvailabilityGroupName].AvailabilityDatabases[$dbName].Drop()
                    Write-Message -Level Verbose -Message "Successfully removed $dbName from  detach from $agName on $($server.Name)."
                }
                catch {
                    Stop-Function -Message "Could not remove $dbName from $agName on $($server.Name)." -Target $dbName -ErrorRecord $_
                    return $false
                }
            }

            Write-Message -Level Verbose -Message "Attempting detach from $dbName from $source."

            ####### Using Sql to detach does not modify the $currentdb collection #######

            $server.KillAllProcesses($dbName)

            try {
                $sql = "ALTER DATABASE [$dbName] SET SINGLE_USER WITH ROLLBACK IMMEDIATE"
                Write-Message -Level Verbose -Message $sql
                $null = $server.Query($sql)
                Write-Message -Level Verbose -Message "Successfully set $dbName to single-user from $source."
            }
            catch {
                Stop-Function -Message "Issue setting database to single-user." -Target $dbName -ErrorRecord $_
            }

            try {
                $sql = "EXEC master.dbo.sp_detach_db N'$dbName'"
                Write-Message -Level Verbose -Message $sql
                $null = $server.Query($sql)
                Write-Message -Level Verbose -Message "Successfully detached $dbName from $source."
                return $true
            }
            catch {
                Stop-Function -Message "Issue detaching database." -Target $dbName -ErrorRecord $_
                return $false
            }
        }

        function Mount-SqlDatabase {
            [CmdletBinding()]
            param (
                [object]$server,
                [string]$dbName,
                [object]$fileStructure,
                [string]$dbOwner
            )

            if ($null -eq $server.Logins.Item($dbOwner)) {
                try {
                    $dbOwner = ($destServer.logins | Where-Object { $_.id -eq 1 }).Name
                }
                catch {
                    $dbOwner = "sa"
                }
            }
            try {
                $null = $server.AttachDatabase($dbName, $fileStructure, $dbOwner, [Microsoft.SqlServer.Management.Smo.AttachOptions]::None)
                return $true
            }
            catch {
                Stop-Function -Message "Issue mounting database." -ErrorRecord $_
                return $false
            }
        }

        function Start-SqlFileTransfer {
            <#

            SYNOPSIS
            Internal function. Uses BITS to transfer detached files (.mdf, .ndf, .ldf, and filegroups) to
            another server over admin UNC paths. Locations of data files are kept in the
            custom object generated by Get-SqlFileStructure

            #>
            param (
                [object]$fileStructure,
                [string]$dbName
            )

            $copydb = $fileStructure.databases[$dbName]
            $dbsource = $copydb.source
            $dbdestination = $copydb.destination

            foreach ($file in $dbsource.keys) {
                $remotefilename = $dbdestination[$file].remotefilename
                $from = $dbsource[$file].remotefilename
                try {
                    if (Test-Path $from -pathtype container) {
                        $null = New-Item -ItemType Directory -Path $remotefilename -Force
                        Start-BitsTransfer -Source "$from\*.*" -Destination $remotefilename

                        $directories = (Get-ChildItem -recurse $from | Where-Object { $_.PsIsContainer }).FullName
                        foreach ($directory in $directories) {
                            $newdirectory = $directory.replace($from, $remotefilename)
                            $null = New-Item -ItemType Directory -Path $newdirectory -Force
                            Start-BitsTransfer -Source "$directory\*.*" -Destination $newdirectory
                        }
                    }
                    else {
                        Write-Message -Level Verbose -Message "Copying $from for $dbName."
                        Start-BitsTransfer -Source $from -Destination $remotefilename
                    }
                }
                catch {
                    try {
                        # Sometimes BITS trips out temporarily on cloned drives.
                        Start-BitsTransfer -Source $from -Destination $remotefilename
                    }
                    catch {
                        Write-Message -Level Verbose -Message "Start-BitsTransfer did not succeed. Now attempting with Copy-Item - no progress bar will be shown."
                        try {
                            Copy-Item -Path $from -Destination $remotefilename -ErrorAction Stop
                        }
                        catch {
                            Write-Message -Level Verbose -Message "Access denied. This can happen for a number of reasons including issues with cloned disks."
                            Stop-Function -Message "Alternatively, you may need to run PowerShell as Administrator, especially when running on localhost." -Target $from -ErrorRecord $_
                            return
                        }
                    }
                }
            }
            return $true
        }

        function Start-SqlDetachAttach {
            <#

            .SYNOPSIS
            Internal function. Performs checks, then executes Dismount-SqlDatabase on a database, copies its files to the new server,    then performs Mount-SqlDatabase. $sourceServer and $destServer are SMO server objects.

            $fileStructure is a custom object generated by Get-SqlFileStructure

            #>
            [CmdletBinding()]
            param (
                [object]$sourceServer,
                [object]$destServer,
                [object]$fileStructure,
                [string]$dbName
            )

            $destfilestructure = New-Object System.Collections.Specialized.StringCollection
            $sourceFileStructure = New-Object System.Collections.Specialized.StringCollection
            $dbOwner = $sourceServer.databases[$dbName].owner

            if ($null -eq $dbOwner) {
                try {
                    $dbOwner = ($destServer.logins | Where-Object { $_.id -eq 1 }).Name
                }
                catch {
                    $dbOwner = "sa"
                }
            }

            foreach ($file in $fileStructure.databases[$dbName].destination.values) { $null = $destfilestructure.add($file.physical) }
            foreach ($file in $fileStructure.databases[$dbName].source.values) { $null = $sourceFileStructure.add($file.physical) }

            $detachresult = Dismount-SqlDatabase $sourceServer $dbName

            if ($detachresult) {
                $transfer = Start-SqlFileTransfer $fileStructure $dbName
                if ($transfer -eq $false) { Write-Warning "Could not copy files."; return "Could not copy files." }
                $attachresult = Mount-SqlDatabase $destServer $dbName $destfilestructure $dbOwner

                if ($attachresult -eq $true) {
                    # add to added dbs because ATTACH was successful
                    Write-Message -Level Verbose -Message "Successfully attached $dbName to $destinstance."
                    return $true
                }
                else {
                    # add to failed because ATTACH was unsuccessful
                    Write-Message -Level Verbose -Message "Could not attach $dbName."
                    return "Could not attach database."
                }
            }
            else {
                # add to failed because DETACH was unsuccessful
                Write-Message -Level Verbose -Message "Could not detach $dbName."
                return "Could not detach database."
            }
        }
        $backupCollection = @()
    }
    process {
        if (Test-FunctionInterrupt) { return }
        
        # testing twice for whatif reasons
        if ($BackupRestore -and (-not $NetworkShare -and -not $UseLastBackups)) {
            Stop-Function -Message "When using -BackupRestore, you must specify -NetworkShare or -UseLastBackups"
            return
        }
        if ($NetworkShare -and $UseLastBackups) {
            Stop-Function -Message "-NetworkShare cannot be used with -UseLastBackups because the backup path is determined by the paths in the last backups"
            return
        }
        if ($DetachAttach -and -not $Reattach -and $Destination.Count -gt 1) {
            Stop-Function -Message "When using -DetachAttach with multiple servers, you must specify -Reattach to reattach database at source"
            return
        }
        if (($AllDatabases -or $IncludeSupportDbs -or $Database) -and !$DetachAttach -and !$BackupRestore) {
            Stop-Function -Message "You must specify -DetachAttach or -BackupRestore when migrating databases."
            return
        }

        if (-not $AllDatabases -and -not $IncludeSupportDbs -and -not $Database -and -not $InputObject) {
            Stop-Function -Message "You must specify a -AllDatabases or -Database to continue."
            return
        }

        if ($InputObject) {
            $Source = $InputObject[0].Parent
            $Database = $InputObject.Name
        }


        if ($Database -contains "master" -or $Database -contains "msdb" -or $Database -contains "tempdb") {
            Stop-Function -Message "Migrating system databases is not currently supported." -Continue
        }

        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }

        Invoke-SmoCheck -SqlInstance $sourceServer
        $sourceNetBios = $sourceServer.ComputerName

        Write-Message -Level Verbose -Message "Ensuring user databases exist (counting databases)."
        $dbTotal = $sourceServer.Databases.Count

        if ($dbTotal -le 4) {
            Stop-Function -Message "No user databases to migrate. Quitting."
            return
        }

        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }

            if ($sourceServer.ComputerName -eq $destServer.ComputerName) {
                $script:sameserver = $true
            }
            else {
                $script:sameserver = $false
            }

            if ($script:sameserver -and $DetachAttach) {
                if (-not (Test-ElevationRequirement -ComputerName $sourceServer)) { return }
            }

            $destVersionLower = $destServer.VersionMajor -lt $sourceServer.VersionMajor
            $destVersionMinorLow = ($destServer.VersionMajor -eq 10 -and $sourceServer.VersionMajor -eq 10) -and ($destServer.VersionMinor -lt $sourceServer.VersionMinor)

            if ($destVersionLower -or $destVersionMinorLow) {
                Stop-Function -Message "Error: copy database cannot be made from newer $($sourceServer.VersionString) to older $($destServer.VersionString) SQL Server version."
                return
            }

            if ($DetachAttach) {
                if ($sourceServer.ComputerName -eq $env:COMPUTERNAME -or $destServer.ComputerName -eq $env:COMPUTERNAME) {
                    if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
                        Write-Message -Level Verbose -Message "When running DetachAttach locally on the console, it's possible you'll need to Run As Administrator. Trying anyway."
                    }
                }
            }

            if ($NetworkShare) {
                if ($(Test-DbaSqlPath -SqlInstance $sourceServer -Path $NetworkShare) -eq $false) {
                    Write-Message -Level Verbose -Message "$Source may not be able to access $NetworkShare. Trying anyway."
                }

                if ($(Test-DbaSqlPath -SqlInstance $destServer -Path $NetworkShare) -eq $false) {
                    Write-Message -Level Verbose -Message "$destinstance may not be able to access $NetworkShare. Trying anyway."
                }

                if ($NetworkShare.StartsWith('\\')) {
                    try {
                        $shareServer = ($NetworkShare -split "\\")[2]
                        $hostEntry = ([Net.Dns]::GetHostEntry($shareServer)).HostName -split "\."

                        if ($shareServer -ne $hostEntry[0]) {
                            Write-Message -Level Verbose -Message "Using CNAME records for the network share may present an issue if an SPN has not been created. Trying anyway. If it doesn't work, use a different (A record) hostname."
                        }
                    }
                    catch {
                        Stop-Function -Message "Error validating unc path: $_"
                        return
                    }
                }
            }

            $destNetBios = $destserver.ComputerName

            Write-Message -Level Verbose -Message "Performing SMO version check."
            Invoke-SmoCheck -SqlInstance $destServer

            Write-Message -Level Verbose -Message "Checking to ensure the source isn't the same as the destination."
            if ($source -eq $destinstance) {
                Stop-Function -Message "Source and Destination SQL Servers instances are the same. Quitting." -Continue
            }

            if ($NetworkShare.Length -gt 0) {
                Write-Message -Level Verbose -Message "Checking to ensure network path is valid."
                if (!($NetworkShare.StartsWith("\\")) -and !$script:sameserver) {
                    Stop-Function -Message "Network share must be a valid UNC path (\\server\share)." -Continue
                }

                if (-not $script:sameserver) {
                    try {
                        if ((Test-Path $NetworkShare -ErrorAction Stop)) {
                            Write-Message -Level Verbose -Message "$NetworkShare share can be accessed."
                        }
                    }
                    catch {
                        Write-Message -Level Verbose -Message "$NetworkShare share cannot be accessed. Still trying anyway, in case the SQL Server service accounts have access."
                    }
                }
            }

            Write-Message -Level Verbose -Message "Checking to ensure server is not SQL Server 7 or below."
            if ($sourceServer.VersionMajor -lt 8 -and $destServer.VersionMajor -lt 8) {
                Stop-Function -Message "This script can only be run on SQL Server 2000 and above. Quitting." -Continue
            }

            Write-Message -Level Verbose -Message "Checking to ensure detach/attach is not attempted on SQL Server 2000."
            if ($destServer.VersionMajor -lt 9 -and $DetachAttach) {
                Stop-Function -Message "Detach/Attach not supported when destination SQL Server is version 2000. Quitting." -Target $destServer -Continue
            }

            Write-Message -Level Verbose -Message "Checking to ensure SQL Server 2000 migration isn't directly attempted to SQL Server 2012."
            if ($sourceServer.VersionMajor -lt 9 -and $destServer.VersionMajor -gt 10) {
                Stop-Function -Message "SQL Server 2000 databases cannot be migrated to SQL Server versions 2012 and above. Quitting." -Target $destServer -Continue
            }

            Write-Message -Level Verbose -Message "Warning if migration from 2005 to 2012 and above and attach/detach is used."
            if ($sourceServer.VersionMajor -eq 9 -and $destServer.VersionMajor -gt 9 -and !$BackupRestore -and !$Force -and $DetachAttach) {
                Stop-Function -Message "Backup and restore is the safest method for migrating from SQL Server 2005 to other SQL Server versions. Please use the -BackupRestore switch or override this requirement by specifying -Force." -Continue
            }

            if ($sourceServer.Collation -ne $destServer.Collation) {
                Write-Message -Level Verbose -Message "Warning on different collation."
                Write-Message -Level Verbose -Message "Collation on $Source, $($sourceServer.Collation) differs from the $destinstance, $($destServer.Collation)."
            }

            Write-Message -Level Verbose -Message "Ensuring destination server version is equal to or greater than source."
            if ($sourceServer.VersionMajor -ge $destServer.VersionMajor) {
                if ($sourceServer.VersionMinor -gt $destServer.VersionMinor) {
                    Stop-Function -Message "Source SQL Server version build must be <= destination SQL Server for database migration." -Continue
                }
            }

            # SMO's filestreamlevel is sometimes null
            $sql = "select coalesce(SERVERPROPERTY('FilestreamConfiguredLevel'),0) as fs"
            $sourceFilestream = $sourceServer.ConnectionContext.ExecuteScalar($sql)
            $destFilestream = $destServer.ConnectionContext.ExecuteScalar($sql)
            if ($sourceFilestream -gt 0 -and $destFilestream -eq 0) {
                $fsWarning = $true
            }

            Write-Message -Level Verbose -Message "Writing warning about filestream being enabled."
            if ($fsWarning) {
                Write-Message -Level Verbose -Message "FILESTREAM enabled on $source but not $destinstance. Databases that use FILESTREAM will be skipped."
            }

            if ($DetachAttach -eq $true) {
                Write-Message -Level Verbose -Message "Checking access to remote directories."
                $remoteSourcePath = Join-AdminUNC $sourceNetBios (Get-SqlDefaultPaths -SqlInstance $sourceServer -filetype data)

                if ((Test-Path $remoteSourcePath) -ne $true -and $DetachAttach) {
                    Write-Message -Level Warning -Message "Can't access remote Sql directories on $source which is required to perform detach/copy/attach."
                    Write-Message -Level Warning -Message "You can manually try accessing $remoteSourcePath to diagnose any issues."
                    Stop-Function -Message "Halting database migration"
                    return
                }

                $remoteDestPath = Join-AdminUNC $destNetBios (Get-SqlDefaultPaths -SqlInstance $destServer -filetype data)
                If ((Test-Path $remoteDestPath) -ne $true -and $DetachAttach) {
                    Write-Message -Level Warning -Message "Can't access remote Sql directories on $destinstance which is required to perform detach/copy/attach."
                    Write-Message -Level Warning -Message "You can manually try accessing $remoteDestPath to diagnose any issues."
                    Stop-Function -Message "Halting database migration" -Continue
                }
            }

            if (($Database -or $ExcludeDatabase -or $IncludeSupportDbs) -and (!$DetachAttach -and !$BackupRestore)) {
                Stop-Function -Message "You did not select a migration method. Please use -BackupRestore or -DetachAttach."
                return
            }

            if ((!$Database -and !$AllDatabases -and !$IncludeSupportDbs) -and ($DetachAttach -or $BackupRestore)) {
                Stop-Function -Message "You did not select any databases to migrate. Please use -AllDatabases or -Database or -IncludeSupportDbs."
                return
            }

            Write-Message -Level Verbose -Message "Building database list."
            $databaseList = New-Object System.Collections.ArrayList
            $SupportDBs = "ReportServer", "ReportServerTempDB", "distribution"
            foreach ($currentdb in ($sourceServer.Databases | Where-Object IsAccessible)) {
                $dbName = $currentdb.Name
                $dbOwner = $currentdb.Owner

                if ($currentdb.Id -le 4) { continue }
                if ($Database -and $Database -notcontains $dbName) { continue }
                if ($IncludeSupportDBs -eq $false -and $SupportDBs -contains $dbName) { continue }
                if ($IncludeSupportDBs -eq $true -and $SupportDBs -notcontains $dbName) {
                    if ($AllDatabases -eq $false -and $Database.length -eq 0) { continue }
                }
                $null = $databaseList.Add($currentdb)
            }

            Write-Message -Level Verbose -Message "Performing count."
            $dbCount = $databaseList.Count

            Write-Message -Level Verbose -Message "Building file structure inventory for $dbCount databases."

            if ($sourceServer.VersionMajor -eq 8) {
                $sql = "select DB_NAME (dbid) as dbname, name, filename, CASE WHEN groupid = 0 THEN 'LOG' ELSE 'ROWS' END as filetype from sysaltfiles"
            }
            else {
                $sql = "SELECT db.Name AS dbname, type_desc AS FileType, mf.Name, Physical_Name AS filename FROM sys.master_files mf INNER JOIN  sys.databases db ON db.database_id = mf.database_id"
            }

            $dbFileTable = $sourceServer.Databases['master'].ExecuteWithResults($sql)

            if ($destServer.VersionMajor -eq 8) {
                $sql = "select DB_NAME (dbid) as dbname, name, filename, CASE WHEN groupid = 0 THEN 'LOG' ELSE 'ROWS' END as filetype from sysaltfiles"
            }
            else {
                $sql = "SELECT db.Name AS dbname, type_desc AS FileType, mf.Name, Physical_Name AS filename FROM sys.master_files mf INNER JOIN  sys.databases db ON db.database_id = mf.database_id"
            }

            $remoteDbFileTable = $destServer.Databases['master'].ExecuteWithResults($sql)

            $fileStructure = Get-SqlFileStructure -sourceserver $sourceServer -destserver $destServer -databaselist $databaseList -ReuseSourceFolderStructure $ReuseSourceFolderStructure

            $elapsed = [System.Diagnostics.Stopwatch]::StartNew()
            $started = Get-Date
            $script:TimeNow = (Get-Date -UFormat "%m%d%Y%H%M%S")

            if ($AllDatabases -or $ExcludeDatabase.length -gt 0 -or $IncludeSupportDbs -or $Database.length -gt 0) {
                foreach ($currentdb in $databaseList) {
                    $dbName = $currentdb.Name
                    $dbOwner = $currentdb.Owner

                    $copyDatabaseStatus = [pscustomobject]@{
                        SourceServer = $sourceServer.Name
                        DestinationServer = $destServer.Name
                        Name         = $dbName
                        DestinationDatabase = $dbname
                        Type         = "Database"
                        Status       = $null
                        Notes        = $null
                        DateTime     = [DbaDateTime](Get-Date)
                    }

                    Write-Message -Level Verbose -Message "`n######### Database: $dbName #########"
                    $dbStart = Get-Date

                    if ($ExcludeDatabase -contains $dbName) {
                        Write-Message -Level Verbose -Message "$dbName excluded. Skipping."
                        continue
                    }

                    Write-Message -Level Verbose -Message "Checking for accessibility."
                    if ($currentdb.IsAccessible -eq $false) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Skipping $dbName. Database is inaccessible.")) {
                            Write-Message -Level Verbose -Message "Skipping $dbName. Database is inaccessible."
                            
                            $copyDatabaseStatus.Status = "Skipped"
                            $copyDatabaseStatus.Notes = "Database is not accessible"
                            $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        }
                        continue
                    }

                    if ($fsWarning) {
                        $fsRows = $dbFileTable.Tables[0].Select("dbname = '$dbName' and FileType = 'FileStream'")
                        
                        if ($fsRows.Count -gt 0) {
                            if ($Pscmdlet.ShouldProcess($destinstance, "Skipping $dbName (contains FILESTREAM).")) {
                                Write-Message -Level Verbose -Message "Skipping $dbName (contains FILESTREAM)."
                                $copyDatabaseStatus.Status = "Skipped"
                                $copyDatabaseStatus.Notes = "Contains FILESTREAM"
                                $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            }
                            continue
                        }
                    }

                    if ($ReuseSourceFolderStructure) {
                        $fgRows = $dbFileTable.Tables[0].Select("dbname = '$dbName' and FileType = 'ROWS'")[0]
                        $remotePath = Split-Path $fgRows.Filename
                        
                        if (!(Test-DbaSqlPath -SqlInstance $destServer -Path $remotePath)) {
                            if ($Pscmdlet.ShouldProcess($destinstance, "$remotePath does not exist on $destinstance and ReuseSourceFolderStructure was specified")) {
                                # Stop-Function -Message "Cannot resolve $remotePath on $source. `n`nYou have specified ReuseSourceFolderStructure and exact folder structure does not exist. Halting script."
                                $copyDatabaseStatus.Status = "Failed"
                                $copyDatabaseStatus.Notes = "$remotePath does not exist on $destinstance and ReuseSourceFolderStructure was specified" #"Can't resolve $remotePath"
                                $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            }
                            continue
                        }
                    }

                    Write-Message -Level Verbose -Message "Checking Availability Group status."
                    if ($currentdb.AvailabilityGroupName.Length -gt 0 -and !$force -and $DetachAttach) {
                        $agName = $currentdb.AvailabilityGroupName
                        Write-Message -Level Verbose -Message "Database is part of an Availability Group ($agName). Use -Force to drop from $agName and migrate. Alternatively, you can use the safer backup/restore method."
                        continue
                    }

                    $dbStatus = $currentdb.Status.ToString()
                    
                    if ($dbStatus.StartsWith("Normal") -eq $false) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "$dbName is not in a Normal state. Skipping.")) {
                            Write-Message -Level Verbose -Message "$dbName is not in a Normal state. Skipping."
                            
                            $copyDatabaseStatus.Status = "Skipped"
                            $copyDatabaseStatus.Notes = "Not in normal state"
                            $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        }
                        continue
                    }
                    
                    if ($currentdb.ReplicationOptions -ne "None" -and $DetachAttach -eq $true) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "$dbName is part of replication. Skipping.")) {
                            Write-Message -Level Verbose -Message "$dbName is part of replication. Skipping."
                            
                            $copyDatabaseStatus.Status = "Skipped"
                            $copyDatabaseStatus.Notes = "Part of replication"
                            $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        }
                        continue
                    }
                    
                    if ($currentdb.IsMirroringEnabled -and !$force -and $DetachAttach) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Database is being mirrored. Use -Force to break mirror and migrate. Alternatively, you can use the safer backup/restore method.")) {
                            Write-Message -Level Verbose -Message "Database is being mirrored. Use -Force to break mirror and migrate. Alternatively, you can use the safer backup/restore method."
                            
                            $copyDatabaseStatus.Status = "Skipped"
                            $copyDatabaseStatus.Notes = "Database is mirrored"
                            $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        }
                        
                        continue
                    }
                    
                    if (($null -ne $destServer.Databases[$dbName]) -and !$force -and !$WithReplace) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "$dbname exists at destination. Use -Force to drop and migrate. Aborting routine for this database.")) {
                            Write-Message -Level Verbose -Message "$dbname exists at destination. Use -Force to drop and migrate. Aborting routine for this database."
                            
                            $copyDatabaseStatus.Status = "Skipped"
                            $copyDatabaseStatus.Notes = "Already exists"
                            $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        }
                        continue
                    }
                    elseif ($null -ne $destServer.Databases[$dbName] -and $force) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "DROP DATABASE $dbName")) {
                            Write-Message -Level Verbose -Message "$dbName already exists. -Force was specified. Dropping $dbName on $destinstance."
                            $removeresult = Remove-DbaDatabase -SqlInstance $destserver -Database $dbname -Confirm:$false
                            $dropResult = $removeresult.Status -eq 'Dropped'

                            if ($dropResult -eq $false) {
                                Write-Message -Level Verbose -Message "Database could not be dropped. Aborting routine for this database."

                                $copyDatabaseStatus.Status = "Failed"
                                $copyDatabaseStatus.Notes = "Could not drop database"
                                $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                continue
                            }
                        }
                    }

                    if ($force) {
                        $WithReplace = $true
                    }

                    Write-Message -Level Verbose -Message "Started: $dbStart."

                    if ($sourceServer.VersionMajor -ge 9) {
                        $sourceDbOwnerChaining = $sourceServer.Databases[$dbName].DatabaseOwnershipChaining
                        $sourceDbTrustworthy = $sourceServer.Databases[$dbName].Trustworthy
                        $sourceDbBrokerEnabled = $sourceServer.Databases[$dbName].BrokerEnabled
                    }

                    $sourceDbReadOnly = $sourceServer.Databases[$dbName].ReadOnly

                    if ($SetSourceReadOnly) {
                        If ($Pscmdlet.ShouldProcess($source, "Set $dbName to read-only")) {
                            Write-Message -Level Verbose -Message "Setting database to read-only."
                            $result = Update-SqldbReadOnly -SqlInstance $sourceServer -dbname $dbName -readonly:$true

                            if ($result -eq $false) {
                                Write-Message -Level Verbose -Message "Couldn't set database to read-only. Aborting routine for this database."
                                continue
                            }
                        }
                    }

                    if ($BackupRestore) {
                        if ($UseLastBackups) {
                            $whatifmsg = "Gathering last backup information for $dbName from $Source and restoring"
                        }
                        else {
                            $whatifmsg = "Backup $dbName from $source and restoring"
                        }
                        If ($Pscmdlet.ShouldProcess($destinstance, $whatifmsg)) {
                            if ($UseLastBackups) {
                                $backupTmpResult = Get-DbaBackupHistory -SqlInstance $sourceServer -Database $dbName -IncludeCopyOnly -Last
                                if (-not $backupTmpResult) {
                                    $copyDatabaseStatus.Type = "Database (BackupRestore)"
                                    $copyDatabaseStatus.Status = "Failed"
                                    $copyDatabaseStatus.Notes = "No backups for $dbName on $source"
                                    $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                    continue
                                }
                            }
                            else {
                                $backupTmpResult = $backupCollection | Where-Object Database -eq $dbName
                                if (-not $backupTmpResult) {
                                    $backupTmpResult = Backup-DbaDatabase -SqlInstance $sourceServer -Database $dbName -BackupDirectory $NetworkShare -FileCount $numberfiles -CopyOnly:$CopyOnly
                                }
                                if ($backupTmpResult) {
                                    $backupCollection += $backupTmpResult
                                }
                                $backupResult = $BackupTmpResult.BackupComplete
                                if (-not $backupResult) {
                                    $serviceAccount = $sourceServer.ServiceAccount
                                    Write-Message -Level Verbose -Message "Backup Failed. Does SQL Server account $serviceAccount have access to $($NetworkShare)? Aborting routine for this database."

                                    $copyDatabaseStatus.Status = "Failed"
                                    $copyDatabaseStatus.Notes = "Backup failed. Verify service account access to $NetworkShare."
                                    $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                    continue
                                }
                            }
                            Write-Message -Level Verbose -Message "Reuse = $ReuseSourceFolderStructure."
                            try {
                                $msg = $null
                                $restoreResultTmp = $backupTmpResult | Restore-DbaDatabase -SqlInstance $destServer -DatabaseName $dbName -ReuseSourceFolderStructure:$ReuseSourceFolderStructure -NoRecovery:$NoRecovery -TrustDbBackupHistory -WithReplace:$WithReplace -EnableException
                            }
                            catch {
                                $msg = $_.Exception.InnerException.InnerException.InnerException.InnerException.Message
                                Stop-Function -Message "Failure attempting to restore $dbName to $destinstance" -Exception $_.Exception.InnerException.InnerException.InnerException.InnerException
                            }
                            $restoreResult = $restoreResultTmp.RestoreComplete

                            if ($restoreResult -eq $true) {
                                Write-Message -Level Verbose -Message "Successfully restored $dbName to $destinstance."
                                $copyDatabaseStatus.Status = "Successful"
                                $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            }
                            else {
                                if ($ReuseSourceFolderStructure) {
                                    Write-Message -Level Verbose -Message "Failed to restore $dbName to $destinstance. You specified -ReuseSourceFolderStructure. Does the exact same destination directory structure exist?"
                                    Write-Message -Level Verbose -Message "Aborting routine for this database."

                                    $copyDatabaseStatus.Status = "Failed"
                                    $copyDatabaseStatus.Notes = "Failed to restore. ReuseSourceFolderStructure was specified, verify same directory structure exist on destination."
                                    $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                    continue
                                }
                                else {
                                    Write-Message -Level Verbose -Message "Failed to restore $dbName to $destinstance. Aborting routine for this database."

                                    $copyDatabaseStatus.Status = "Failed"
                                    if (-not $msg) {
                                        $msg = "Failed to restore database"
                                    }
                                    $copyDatabaseStatus.Notes = $msg
                                    $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                    continue
                                }
                            }
                            if (-not $NoBackupCleanUp -and $Destination.Count -eq 1) {
                                foreach ($backupFile in ($backupTmpResult.BackupPath)) {
                                    try {
                                        if (Test-Path $backupFile -ErrorAction Stop) {
                                            Write-Message -Level Verbose -Message "Deleting $backupFile."
                                            Remove-Item $backupFile -ErrorAction Stop
                                        }
                                    }
                                    catch {
                                        try {
                                            Write-Message -Level Verbose -Message "Trying alternate SQL method to delete $backupFile."
                                            $sql = "EXEC master.sys.xp_delete_file 0, '$backupFile'"
                                            Write-Message -Level Debug -Message $sql
                                            $null = $sourceServer.Query($sql)
                                        }
                                        catch {
                                            Write-Message -Level Verbose -Message "Cannot delete backup file $backupFile."

                                            # Set NoBackupCleanup so that there's a warning at the end
                                            $NoBackupCleanup = $true
                                        }
                                    }
                                }
                            }
                        }

                        $dbFinish = Get-Date
                        if ($NoRecovery -eq $false) {
                            # needed because the newly restored database doesn't show up
                            $destServer.Databases.Refresh()
                            $dbOwner = $sourceServer.Databases[$dbName].Owner
                            if ($null -eq $dbOwner -or $destServer.Logins.Name -notcontains $dbOwner) {
                                $dbOwner = Get-SaLoginName -SqlInstance $destServer
                            }
                            Write-Message -Level Verbose -Message "Updating database owner to $dbOwner."
                            $OwnerResult = Set-DbaDatabaseOwner -SqlInstance $destServer -Database $dbName -TargetLogin $dbOwner -EnableException
                            if ($OwnerResult.Length -eq 0) {
                                Write-Message -Level Verbose -Message "Failed to update database owner."
                            }
                        }
                    }

                    if ($DetachAttach) {
                        $copyDatabaseStatus.Type = "Database (DetachAttach)"

                        $sourceFileStructure = New-Object System.Collections.Specialized.StringCollection
                        foreach ($file in $fileStructure.Databases[$dbName].Source.Values) {
                            $null = $sourceFileStructure.Add($file.Physical)
                        }

                        $dbOwner = $sourceServer.Databases[$dbName].Owner

                        if ($null -eq $dbOwner -or $destServer.Logins.Name -notcontains $dbOwner) {
                            $dbOwner = Get-SaLoginName -SqlInstance $destServer
                        }

                        if ($Pscmdlet.ShouldProcess($destinstance, "Detach $dbName from $source and attach, then update dbowner")) {
                            $migrationResult = Start-SqlDetachAttach $sourceServer $destServer $fileStructure $dbName

                            $dbFinish = Get-Date

                            if ($reattach -eq $true) {
                                $sourceServer.Databases.Refresh()
                                $destServer.Databases.Refresh()
                                $result = Mount-SqlDatabase $sourceServer $dbName $sourceFileStructure $dbOwner

                                if ($result -eq $true) {
                                    $sourceServer.Databases[$dbName].DatabaseOwnershipChaining = $sourceDbOwnerChaining
                                    $sourceServer.Databases[$dbName].Trustworthy = $sourceDbTrustworthy
                                    $sourceServer.Databases[$dbName].BrokerEnabled = $sourceDbBrokerEnabled
                                    $sourceServer.Databases[$dbName].Alter()

                                    if ($SetSourceReadOnly) {
                                        $null = Update-SqldbReadOnly -SqlInstance $sourceServer -dbname $dbName -readonly $true
                                    }
                                    else {
                                        $null = Update-SqldbReadOnly -SqlInstance $sourceServer -dbname $dbName -readonly $sourceDbReadOnly
                                    }

                                    Write-Message -Level Verbose -Message "Successfully reattached $dbName to $source."
                                }
                                else {
                                    Write-Message -Level Verbose -Message "Could not reattach $dbName to $source."
                                    $copyDatabaseStatus.Status = "Failed"
                                    $copyDatabaseStatus.Notes = "Could not reattach database to $source"
                                    $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                }
                            }

                            if ($migrationResult -eq $true) {
                                Write-Message -Level Verbose -Message "Successfully attached $dbName to $destinstance."
                                $copyDatabaseStatus.Status = "Successful"
                                $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            }
                            else {
                                Write-Message -Level Verbose -Message "Failed to attach $dbName to $destinstance. Aborting routine for this database."

                                $copyDatabaseStatus.Status = "Failed"
                                $copyDatabaseStatus.Notes = "Failed to attach database to destination"
                                $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject

                                continue
                            }
                        }
                    }
                    $destServer.Databases.Refresh()

                    # restore potentially lost settings
                    if ($destServer.VersionMajor -ge 9 -and $NoRecovery -eq $false) {
                        if ($sourceDbOwnerChaining -ne $destServer.Databases[$dbName].DatabaseOwnershipChaining) {
                            if ($Pscmdlet.ShouldProcess($destinstance, "Updating DatabaseOwnershipChaining on $dbName")) {
                                try {
                                    $destServer.Databases[$dbName].DatabaseOwnershipChaining = $sourceDbOwnerChaining
                                    $destServer.Databases[$dbName].Alter()
                                    Write-Message -Level Verbose -Message "Successfully updated DatabaseOwnershipChaining for $sourceDbOwnerChaining on $dbName on $destinstance."
                                }
                                catch {
                                    $copyDatabaseStatus.Status = "Successful - failed to apply DatabaseOwnershipChaining."
                                    $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                    Stop-Function -Message "Failed to update DatabaseOwnershipChaining for $sourceDbOwnerChaining on $dbName on $destinstance." -Target $destinstance -ErrorRecord $_ -Continue
                                }
                            }
                        }

                        if ($sourceDbTrustworthy -ne $destServer.Databases[$dbName].Trustworthy) {
                            if ($Pscmdlet.ShouldProcess($destinstance, "Updating Trustworthy on $dbName")) {
                                try {
                                    $destServer.Databases[$dbName].Trustworthy = $sourceDbTrustworthy
                                    $destServer.Databases[$dbName].Alter()
                                    Write-Message -Level Verbose -Message "Successfully updated Trustworthy to $sourceDbTrustworthy for $dbName on $destinstance"
                                }
                                catch {
                                    $copyDatabaseStatus.Status = "Successful - failed to apply Trustworthy"
                                    $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                    Stop-Function -Message "Failed to update Trustworthy to $sourceDbTrustworthy for $dbName on $destinstance." -Target $destinstance -ErrorRecord $_ -Continue
                                }
                            }
                        }

                        if ($sourceDbBrokerEnabled -ne $destServer.Databases[$dbName].BrokerEnabled) {
                            if ($Pscmdlet.ShouldProcess($destinstance, "Updating BrokerEnabled on $dbName")) {
                                try {
                                    $destServer.Databases[$dbName].BrokerEnabled = $sourceDbBrokerEnabled
                                    $destServer.Databases[$dbName].Alter()
                                    Write-Message -Level Verbose -Message "Successfully updated BrokerEnabled to $sourceDbBrokerEnabled for $dbName on $destinstance."
                                }
                                catch {
                                    $copyDatabaseStatus.Status = "Successful - failed to apply BrokerEnabled"
                                    $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                    Stop-Function -Message "Failed to update BrokerEnabled to $sourceDbBrokerEnabled for $dbName on $destinstance." -Target $destinstance -ErrorRecord $_ -Continue
                                }
                            }
                        }
                    }

                    if ($sourceDbReadOnly -ne $destServer.Databases[$dbName].ReadOnly -and $NoRecovery -eq $false) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Updating ReadOnly status on $dbName")) {
                            $update = Update-SqldbReadOnly -SqlInstance $destServer -dbname $dbName -readonly $sourceDbReadOnly
                            if ($update -eq $true) {
                                Write-Message -Level Verbose -Message "Successfully updated readonly status on $dbName."
                            }
                            else {
                                $copyDatabaseStatus.Status = "Successful - failed to apply ReadOnly."
                                $copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                Stop-Function -Message "Failed to update ReadOnly status on $dbName." -Target $destinstance -ErrorRecord $_ -Continue
                            }
                        }
                    }

                    if ($SetSourceOffline -and $sourceServer.databases[$dbName].status -notlike '*offline*') {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Setting $dbName offline on $source")) {
                            Stop-DbaProcess -SqlInstance $sourceServer -Database $dbName
                            Set-DbaDatabaseState -SqlInstance $sourceServer -SqlCredential $SourceSqlCredential -database $dbName -Offline
                        }
                    }

                    $dbTotalTime = $dbFinish - $dbStart
                    $dbTotalTime = ($dbTotalTime.ToString().Split(".")[0])

                    Write-Message -Level Verbose -Message "Finished: $dbFinish."
                    Write-Message -Level Verbose -Message "Elapsed time: $dbTotalTime."

                } # end db by db processing
            }
        }
    }
    end {
        if (Test-FunctionInterrupt) { return }
        if (-not $NoBackupCleanUp -and $Destination.Count -gt 1) {
            foreach ($backupFile in ($backupCollection.BackupPath)) {
                try {
                    if (Test-Path $backupFile -ErrorAction Stop) {
                        Write-Message -Level Verbose -Message "Deleting $backupFile."
                        Remove-Item $backupFile -ErrorAction Stop
                    }
                }
                catch {
                    try {
                        Write-Message -Level Verbose -Message "Trying alternate SQL method to delete $backupFile."
                        $sql = "EXEC master.sys.xp_delete_file 0, '$backupFile'"
                        Write-Message -Level Debug -Message $sql
                        $null = $sourceServer.Query($sql)
                    }
                    catch {
                        Write-Message -Level Verbose -Message "Cannot delete backup file $backupFile."
                    }
                }
            }
        }
        if (Test-FunctionInterrupt) { return }
        if ($null -ne $elapsed) {
            $totalTime = ($elapsed.Elapsed.toString().Split(".")[0])

            Write-Message -Level Verbose -Message "`nDatabase migration finished"
            Write-Message -Level Verbose -Message "Migration started: $started"
            Write-Message -Level Verbose -Message "Migration completed: $(Get-Date)"
            Write-Message -Level Verbose -Message "Total Elapsed time: $totalTime"

            if ($NetworkShare.length -gt 0 -and $NoBackupCleanup) {
                Write-Message -Level Verbose -Message "Backups still exist at $NetworkShare."
            }
        }
        else {
            Write-Message -Level Verbose -Message "No work was done, as we stopped during setup phase"
        }
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlDatabase
    }
}
tools\dbatools\functions\Copy-DbaDatabaseAssembly.ps1
function Copy-DbaDatabaseAssembly {
    <#
        .SYNOPSIS
            Copy-DbaDatabaseAssembly migrates assemblies from one SQL Server to another.

        .DESCRIPTION
            By default, all assemblies are copied.

            If the assembly already exists on the destination, it will be skipped unless -Force is used.

            This script does not yet copy dependencies or dependent objects.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Assembly
            The assembly(ies) to process. This list is auto-populated from the server. If unspecified, all assemblies will be processed.

        .PARAMETER ExcludeAssembly
            The assembly(ies) to exclude. This list is auto-populated from the server.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER Force
            If this switch is enabled, existing assemblies on Destination with matching names from Source will be dropped.

        .NOTES
            Tags: Migration, Assembly
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            http://dbatools.io/Get-SqlDatabaseAssembly

        .EXAMPLE
            Copy-DbaDatabaseAssembly -Source sqlserver2014a -Destination sqlcluster

            Copies all assemblies from sqlserver2014a to sqlcluster using Windows credentials. If assemblies with the same name exist on sqlcluster, they will be skipped.

        .EXAMPLE
            Copy-DbaDatabaseAssembly -Source sqlserver2014a -Destination sqlcluster -Assembly dbname.assemblyname, dbname3.anotherassembly -SourceSqlCredential $cred -Force

            Copies two assemblies, the dbname.assemblyname and dbname3.anotherassembly from sqlserver2014a to sqlcluster using SQL credentials for sqlserver2014a and Windows credentials for sqlcluster. If an assembly with the same name exists on sqlcluster, it will be dropped and recreated because -Force was used.

            In this example, anotherassembly will be copied to the dbname3 database on the server sqlcluster.

        .EXAMPLE
            Copy-DbaThing -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force

            Shows what would happen if the command were executed using force.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]$SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [object[]]$Assembly,
        [object[]]$ExcludeAssembly,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 9
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        $sourceAssemblies = @()
        foreach ($database in ($sourceServer.Databases | Where-Object IsAccessible)) {
            Write-Message -Level Verbose -Message "Processing $database on source"
            
            try {
                # a bug here requires a try/catch
                $userAssemblies = $database.Assemblies | Where-Object IsSystemObject -eq $false
                foreach ($assembly in $userAssemblies) {
                    $sourceAssemblies += $assembly
                }
            }
            catch { }
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            
            $destAssemblies = @()
            foreach ($database in $destServer.Databases) {
                Write-Message -Level VeryVerbose -Message "Processing $database on destination"
                try {
                    # a bug here requires a try/catch
                    $userAssemblies = $database.Assemblies | Where-Object IsSystemObject -eq $false
                    foreach ($assembly in $userAssemblies) {
                        $destAssemblies += $assembly
                    }
                }
                catch { }
            }
            foreach ($currentAssembly in $sourceAssemblies) {
                $assemblyName = $currentAssembly.Name
                $dbName = $currentAssembly.Parent.Name
                $destDb = $destServer.Databases[$dbName]
                Write-Message -Level VeryVerbose -Message "Processing $assemblyName on $dbname"
                $copyDbAssemblyStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    SourceDatabase = $dbName
                    DestinationServer = $destServer.Name
                    DestinationDatabase = $destDb
                    type         = "Database Assembly"
                    Name         = $assemblyName
                    Status       = $null
                    Notes        = $null
                    DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                }
                
                
                if (!$destDb) {
                    $copyDbAssemblyStatus.Status = "Skipped"
                    $copyDbAssemblyStatus.Notes = "Destination database does not exist"
                    $copyDbAssemblyStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    
                    Write-Message -Level Verbose -Message "Destination database $dbName does not exist. Skipping $assemblyName.";
                    continue
                }
                
                if ((Test-Bound -ParameterName Assembly) -and $Assembly -notcontains "$dbName.$assemblyName" -or $ExcludeAssembly -contains "$dbName.$assemblyName") {
                    continue
                }
                
                if ($currentAssembly.AssemblySecurityLevel -eq "External" -and -not $destDb.Trustworthy) {
                    if ($Pscmdlet.ShouldProcess($destinstance, "Setting $dbName to External")) {
                        Write-Message -Level Verbose -Message "Setting $dbName Security Level to External on $destinstance."
                        $sql = "ALTER DATABASE $dbName SET TRUSTWORTHY ON"
                        try {
                            Write-Message -Level Debug -Message $sql
                            $destServer.Query($sql)
                        }
                        catch {
                            $copyDbAssemblyStatus.Status = "Failed"
                            $copyDbAssemblyStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            
                            Stop-Function -Message "Issue setting security level." -Target $destDb -ErrorRecord $_
                        }
                    }
                }
                
                if ($destServer.Databases[$dbName].Assemblies.Name -contains $currentAssembly.name) {
                    if ($force -eq $false) {
                        $copyDbAssemblyStatus.Status = "Skipped"
                        $copyDbAssemblyStatus.Notes = "Already exists"
                        $copyDbAssemblyStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Write-Message -Level Verbose -Message "Assembly $assemblyName exists at destination in the $dbName database. Use -Force to drop and migrate."
                        continue
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Dropping assembly $assemblyName and recreating")) {
                            try {
                                Write-Message -Level Verbose -Message "Dropping assembly $assemblyName."
                                Write-Message -Level Verbose -Message "This won't work if there are dependencies."
                                $destServer.Databases[$dbName].Assemblies[$assemblyName].Drop()
                                Write-Message -Level Verbose -Message "Copying assembly $assemblyName."
                                $sql = $currentAssembly.Script()
                                Write-Message -Level Debug -Message $sql
                                $destServer.Query($sql, $dbName)
                            }
                            catch {
                                $copyDbAssemblyStatus.Status = "Failed"
                                $copyDbAssemblyStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                
                                Stop-Function -Message "Issue dropping assembly." -Target $assemblyName -ErrorRecord $_ -Continue
                            }
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Creating assembly $assemblyName")) {
                    try {
                        Write-Message -Level Verbose -Message "Copying assembly $assemblyName from database."
                        $sql = $currentAssembly.Script()
                        Write-Message -Level Debug -Message $sql
                        $destServer.Query($sql, $dbName)
                        
                        $copyDbAssemblyStatus.Status = "Successful"
                        $copyDbAssemblyStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                    }
                    catch {
                        $copyDbAssemblyStatus.Status = "Failed"
                        $copyDbAssemblyStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Stop-Function -Message "Issue creating assembly." -Target $assemblyName -ErrorRecord $_
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlDatabaseAssembly
    }
}
tools\dbatools\functions\Copy-DbaDatabaseMail.ps1
function Copy-DbaDatabaseMail {
    <#
    .SYNOPSIS
        Migrates Mail Profiles, Accounts, Mail Servers and Mail Server Configs from one SQL Server to another.

    .DESCRIPTION
        By default, all mail configurations for Profiles, Accounts, Mail Servers and Configs are copied.

    .PARAMETER Source
        Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

    .PARAMETER SourceSqlCredential
        Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

    .PARAMETER Destination
        Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

    .PARAMETER DestinationSqlCredential
        Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

    .PARAMETER Type
        Specifies the object type to migrate. Valid options are "Job", "Alert" and "Operator". When Type is specified, all categories from the selected type will be migrated.

    .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .PARAMETER Force
        If this switch is enabled, existing objects on Destination with matching names from Source will be dropped.

    .NOTES
        Tags: Migration, Mail
        Author: Chrissy LeMaire (@cl), netnerds.net
        Requires: sysadmin access on SQL Servers

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Copy-DbaDatabaseMail

    .EXAMPLE
        Copy-DbaDatabaseMail -Source sqlserver2014a -Destination sqlcluster

        Copies all database mail objects from sqlserver2014a to sqlcluster using Windows credentials. If database mail objects with the same name exist on sqlcluster, they will be skipped.

    .EXAMPLE
        Copy-DbaDatabaseMail -Source sqlserver2014a -Destination sqlcluster -SourceSqlCredential $cred

        Copies all database mail objects from sqlserver2014a to sqlcluster using SQL credentials for sqlserver2014a and Windows credentials for sqlcluster.

    .EXAMPLE
        Copy-DbaDatabaseMail -Source sqlserver2014a -Destination sqlcluster -WhatIf

        Shows what would happen if the command were executed.

    .EXAMPLE
        Copy-DbaDatabaseMail -Source sqlserver2014a -Destination sqlcluster -EnableException

        Performs execution of function, and will throw a terminating exception if something breaks
    #>
    [cmdletbinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [Parameter(ParameterSetName = 'SpecificTypes')]
        [ValidateSet('ConfigurationValues', 'Profiles', 'Accounts', 'mailServers')]
        [string[]]$Type,
        [PSCredential]$SourceSqlCredential,
        [PSCredential]$DestinationSqlCredential,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        function Copy-DbaDatabaseMailConfig {
            [cmdletbinding(SupportsShouldProcess)]
            param ()

            Write-Message -Message "Migrating mail server configuration values." -Level Verbose
            $copyMailConfigStatus = [pscustomobject]@{
                SourceServer      = $sourceServer.Name
                DestinationServer = $destServer.Name
                Name              = "Server Configuration"
                Type              = "Mail Configuration"
                Status            = $null
                Notes             = $null
                DateTime          = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
            }
            if ($pscmdlet.ShouldProcess($destinstance, "Migrating all mail server configuration values.")) {
                try {
                    $sql = $mail.ConfigurationValues.Script() | Out-String
                    $sql = $sql -replace [Regex]::Escape("'$source'"), "'$destinstance'"
                    Write-Message -Message $sql -Level Debug
                    $destServer.Query($sql) | Out-Null
                    $mail.ConfigurationValues.Refresh()
                    $copyMailConfigStatus.Status = "Successful"
                }
                catch {
                    $copyMailConfigStatus.Status = "Failed"
                    $copyMailConfigStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    Stop-Function -Message "Unable to migrate mail configuration." -Category InvalidOperation -InnerErrorRecord $_ -Target $destServer
                }
                $copyMailConfigStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
            }
        }
        
        function Copy-DbaDatabaseAccount {
            [cmdletbinding(SupportsShouldProcess)]
            $sourceAccounts = $sourceServer.Mail.Accounts
            $destAccounts = $destServer.Mail.Accounts

            Write-Message -Message "Migrating accounts." -Level Verbose
            foreach ($account in $sourceAccounts) {
                $accountName = $account.name
                $copyMailAccountStatus = [pscustomobject]@{
                    SourceServer      = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name              = $accountName
                    Type              = "Mail Account"
                    Status            = $null
                    Notes             = $null
                    DateTime          = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                }

                if ($accounts.count -gt 0 -and $accounts -notcontains $accountName) {
                    continue
                }

                if ($destAccounts.name -contains $accountName) {
                    if ($force -eq $false) {
                        If ($pscmdlet.ShouldProcess($destinstance, "Account $accountName exists at destination. Use -Force to drop and migrate.")) {
                            $copyMailAccountStatus.Status = "Skipped"
                            $copyMailAccountStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Write-Message -Message "Account $accountName exists at destination. Use -Force to drop and migrate." -Level Verbose
                        }
                        continue
                    }

                    If ($pscmdlet.ShouldProcess($destinstance, "Dropping account $accountName and recreating.")) {
                        try {
                            Write-Message -Message "Dropping account $accountName." -Level Verbose
                            $destServer.Mail.Accounts[$accountName].Drop()
                            $destServer.Mail.Accounts.Refresh()
                        }
                        catch {
                            $copyMailAccountStatus.Status = "Failed"
                            $copyMailAccountStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Stop-Function -Message "Issue dropping account." -Target $accountName -Category InvalidOperation -InnerErrorRecord $_ -Continue
                        }
                    }
                }

                if ($pscmdlet.ShouldProcess($destinstance, "Migrating account $accountName.")) {
                    try {
                        Write-Message -Message "Copying mail account $accountName." -Level Verbose
                        $sql = $account.Script() | Out-String
                        $sql = $sql -replace [Regex]::Escape("'$source'"), "'$destinstance'"
                        Write-Message -Message $sql -Level Debug
                        $destServer.Query($sql) | Out-Null
                        $copyMailAccountStatus.Status = "Successful"
                    }
                    catch {
                        $copyMailAccountStatus.Status = "Failed"
                        $copyMailAccountStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        Stop-Function -Message "Issue copying mail account." -Target $accountName -Category InvalidOperation -InnerErrorRecord $_
                    }
                    $copyMailAccountStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                }
            }
        }
        
        function Copy-DbaDatabaseMailProfile {

            $sourceProfiles = $sourceServer.Mail.Profiles
            $destProfiles = $destServer.Mail.Profiles

            Write-Message -Message "Migrating mail profiles." -Level Verbose
            foreach ($profile in $sourceProfiles) {

                $profileName = $profile.name
                $copyMailProfileStatus = [pscustomobject]@{
                    SourceServer      = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name              = $profileName
                    Type              = "Mail Profile"
                    Status            = $null
                    Notes             = $null
                    DateTime          = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                }

                if ($profiles.count -gt 0 -and $profiles -notcontains $profileName) {
                    continue
                }

                if ($destProfiles.name -contains $profileName) {
                    if ($force -eq $false) {
                        If ($pscmdlet.ShouldProcess($destinstance, "Profile $profileName exists at destination. Use -Force to drop and migrate.")) {
                            $copyMailProfileStatus.Status = "Skipped"
                            $copyMailProfileStatus.Notes = "Already exists"
                            $copyMailProfileStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Write-Message -Message "Profile $profileName exists at destination. Use -Force to drop and migrate." -Level Verbose
                        }
                        continue
                    }

                    If ($pscmdlet.ShouldProcess($destinstance, "Dropping profile $profileName and recreating.")) {
                        try {
                            Write-Message -Message "Dropping profile $profileName." -Level Verbose
                            $destServer.Mail.Profiles[$profileName].Drop()
                            $destServer.Mail.Profiles.Refresh()
                        }
                        catch {
                            $copyMailProfileStatus.Status = "Failed"
                            $copyMailProfileStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Stop-Function -Message "Issue dropping profile." -Target $profileName -Category InvalidOperation -InnerErrorRecord $_ -Continue
                        }
                    }
                }

                if ($pscmdlet.ShouldProcess($destinstance, "Migrating mail profile $profileName.")) {
                    try {
                        Write-Message -Message "Copying mail profile $profileName." -Level Verbose
                        $sql = $profile.Script() | Out-String
                        $sql = $sql -replace [Regex]::Escape("'$source'"), "'$destinstance'"
                        Write-Message -Message $sql -Level Debug
                        $destServer.Query($sql) | Out-Null
                        $destServer.Mail.Profiles.Refresh()
                        $copyMailProfileStatus.Status = "Successful"
                    }
                    catch {
                        $copyMailProfileStatus.Status = "Failed"
                        $copyMailProfileStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        Stop-Function -Message "Issue copying mail profile." -Target $profileName -Category InvalidOperation -InnerErrorRecord $_
                    }
                    $copyMailProfileStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                }
            }
        }
        
        function Copy-DbaDatabaseMailServer {
            [cmdletbinding(SupportsShouldProcess)]
            $sourceMailServers = $sourceServer.Mail.Accounts.MailServers
            $destMailServers = $destServer.Mail.Accounts.MailServers

            Write-Message -Message "Migrating mail servers." -Level Verbose
            foreach ($mailServer in $sourceMailServers) {
                $mailServerName = $mailServer.name
                $copyMailServerStatus = [pscustomobject]@{
                    SourceServer      = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name              = $mailServerName
                    Type              = "Mail Server"
                    Status            = $null
                    Notes             = $null
                    DateTime          = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                }
                if ($mailServers.count -gt 0 -and $mailServers -notcontains $mailServerName) {
                    continue
                }

                if ($destMailServers.name -contains $mailServerName) {
                    if ($force -eq $false) {
                        if ($pscmdlet.ShouldProcess($destinstance, "Mail server $mailServerName exists at destination. Use -Force to drop and migrate.")) {
                            $copyMailServerStatus.Status = "Skipped"
                            $copyMailServerStatus.Notes = "Already exists"
                            $copyMailServerStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Write-Message -Message "Mail server $mailServerName exists at destination. Use -Force to drop and migrate." -Level Verbose
                        }
                        continue
                    }

                    If ($pscmdlet.ShouldProcess($destinstance, "Dropping mail server $mailServerName and recreating.")) {
                        try {
                            Write-Message -Message "Dropping mail server $mailServerName." -Level Verbose
                            $destServer.Mail.Accounts.MailServers[$mailServerName].Drop()
                        }
                        catch {
                            $copyMailServerStatus.Status = "Failed"
                            $copyMailServerStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            Stop-Function -Message "Issue dropping mail server." -Target $mailServerName -Category InvalidOperation -InnerErrorRecord $_ -Continue
                        }
                    }
                }

                if ($pscmdlet.ShouldProcess($destinstance, "Migrating account mail server $mailServerName.")) {
                    try {
                        Write-Message -Message "Copying mail server $mailServerName." -Level Verbose
                        $sql = $mailServer.Script() | Out-String
                        $sql = $sql -replace [Regex]::Escape("'$source'"), "'$destinstance'"
                        Write-Message -Message $sql -Level Debug
                        $destServer.Query($sql) | Out-Null
                        $copyMailServerStatus.Status = "Successful"
                    }
                    catch {
                        $copyMailServerStatus.Status = "Failed"
                        $copyMailServerStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        Stop-Function -Message "Issue copying mail server" -Target $mailServerName -Category InvalidOperation -InnerErrorRecord $_
                    }
                    $copyMailServerStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                }
            }
        }
        
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 9
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        $mail = $sourceServer.mail
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            
            if ($type.Count -gt 0) {
                
                switch ($type) {
                    "ConfigurationValues" {
                        Copy-DbaDatabaseMailConfig
                        $destServer.Mail.ConfigurationValues.Refresh()
                    }
                    
                    "Profiles" {
                        Copy-DbaDatabaseMailProfile
                        $destServer.Mail.Profiles.Refresh()
                    }
                    
                    "Accounts" {
                        Copy-DbaDatabaseAccount
                        $destServer.Mail.Accounts.Refresh()
                    }
                    
                    "mailServers" {
                        Copy-DbaDatabaseMailServer
                    }
                }
                
                continue
            }
            
            if (($profiles.count + $accounts.count + $mailServers.count) -gt 0) {
                
                if ($profiles.count -gt 0) {
                    Copy-DbaDatabaseMailProfile -Profiles $profiles
                    $destServer.Mail.Profiles.Refresh()
                }
                
                if ($accounts.count -gt 0) {
                    Copy-DbaDatabaseAccount -Accounts $accounts
                    $destServer.Mail.Accounts.Refresh()
                }
                
                if ($mailServers.count -gt 0) {
                    Copy-DbaDatabaseMailServer -mailServers $mailServers
                }
                
                continue
            }
            
            Copy-DbaDatabaseMailConfig
            $destServer.Mail.ConfigurationValues.Refresh()
            Copy-DbaDatabaseAccount
            $destServer.Mail.Accounts.Refresh()
            Copy-DbaDatabaseMailProfile
            $destServer.Mail.Profiles.Refresh()
            Copy-DbaDatabaseMailServer
            $copyMailConfigStatus
            $copyMailAccountStatus
            $copyMailProfileStatus
            $copyMailServerStatus
            $enableDBMailStatus
            
        <# ToDo: Use Get/Set-DbaSpConfigure once the dynamic parameters are replaced. #>
            
            if (($sourceDbMailEnabled -eq 1) -and ($destDbMailEnabled -eq 0)) {
                if ($pscmdlet.ShouldProcess($destinstance, "Enabling Database Mail")) {
                    $sourceDbMailEnabled = ($sourceServer.Configuration.DatabaseMailEnabled).ConfigValue
                    Write-Message -Message "$sourceServer DBMail configuration value: $sourceDbMailEnabled." -Level Verbose
                    
                    $destDbMailEnabled = ($destServer.Configuration.DatabaseMailEnabled).ConfigValue
                    Write-Message -Message "$destServer DBMail configuration value: $destDbMailEnabled." -Level Verbose
                    $enableDBMailStatus = [pscustomobject]@{
                        SourceServer = $sourceServer.name
                        DestinationServer = $destServer.name
                        Name         = "Enabled on Destination"
                        Type         = "Mail Configuration"
                        Status       = if ($destDbMailEnabled -eq 1) { "Enabled" } else { $null }
                        DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                    }
                    try {
                        Write-Message -Message "Enabling Database Mail on $destServer." -Level Verbose
                        $destServer.Configuration.DatabaseMailEnabled.ConfigValue = 1
                        $destServer.Alter()
                        $enableDBMailStatus.Status = "Successful"
                    }
                    catch {
                        $enableDBMailStatus.Status = "Failed"
                        $enableDBMailStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        Stop-Function -Message "Cannot enable Database Mail." -Category InvalidOperation -ErrorRecord $_ -Target $destServer
                    }
                    $enableDBMailStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlDatabaseMail
    }
}
tools\dbatools\functions\Copy-DbaEndpoint.ps1
function Copy-DbaEndpoint {
    <#
        .SYNOPSIS
            Copy-DbaEndpoint migrates server endpoints from one SQL Server to another.

        .DESCRIPTION
            By default, all endpoints are copied.

            If the endpoint already exists on the destination, it will be skipped unless -Force is used.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Endpoint
            The endpoint(s) to process. This list is auto-populated from the server. If unspecified, all endpoints will be processed.

        .PARAMETER ExcludeEndpoint
            The endpoint(s) to exclude. This list is auto-populated from the server.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER Force
            If this switch is enabled, existing endpoints on Destination with matching names from Source will be dropped.

        .NOTES
            Tags: Migration, Endpoint
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaEndpoint

        .EXAMPLE
            Copy-DbaEndpoint -Source sqlserver2014a -Destination sqlcluster

            Copies all server endpoints from sqlserver2014a to sqlcluster, using Windows credentials. If endpoints with the same name exist on sqlcluster, they will be skipped.

        .EXAMPLE
            Copy-DbaEndpoint -Source sqlserver2014a -SourceSqlCredential $cred -Destination sqlcluster -Endpoint tg_noDbDrop -Force

            Copies only the tg_noDbDrop endpoint from sqlserver2014a to sqlcluster using SQL credentials for sqlserver2014a and Windows credentials for sqlcluster. If an endpoint with the same name exists on sqlcluster, it will be dropped and recreated because -Force was used.

        .EXAMPLE
            Copy-DbaEndpoint -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force

            Shows what would happen if the command were executed using force.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]
        $SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]
        $DestinationSqlCredential,
        [object[]]$Endpoint,
        [object[]]$ExcludeEndpoint,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 9
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        $serverEndpoints = $sourceServer.Endpoints | Where-Object IsSystemObject -eq $false
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            $destEndpoints = $destServer.Endpoints
            
            foreach ($currentEndpoint in $serverEndpoints) {
                $endpointName = $currentEndpoint.Name
                
                $copyEndpointStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name         = $endpointName
                    Type         = "Endpoint"
                    Status       = $null
                    Notes        = $null
                    DateTime     = [DbaDateTime](Get-Date)
                }
                
                if ($Endpoint -and $Endpoint -notcontains $endpointName -or $ExcludeEndpoint -contains $endpointName) {
                    continue
                }
                
                if ($destEndpoints.Name -contains $endpointName) {
                    if ($force -eq $false) {
                        $copyEndpointStatus.Status = "Skipped"
                        $copyEndpointStatus.Notes = "Already exists"
                        $copyEndpointStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Write-Message -Level Verbose -Message "Server endpoint $endpointName exists at destination. Use -Force to drop and migrate."
                        continue
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Dropping server endpoint $endpointName and recreating.")) {
                            try {
                                Write-Message -Level Verbose -Message "Dropping server endpoint $endpointName."
                                $destServer.Endpoints[$endpointName].Drop()
                            }
                            catch {
                                $copyEndpointStatus.Status = "Failed"
                                $copyEndpointStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                
                                Stop-Function -Message "Issue dropping server endpoint." -Target $endpointName -ErrorRecord $_ -Continue
                            }
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Creating server endpoint $endpointName.")) {
                    try {
                        Write-Message -Level Verbose -Message "Copying server endpoint $endpointName."
                        $destServer.Query($currentEndpoint.Script()) | Out-Null
                        
                        $copyEndpointStatus.Status = "Successful"
                        $copyEndpointStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copyEndpointStatus.Status = "Failed"
                        $copyEndpointStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Stop-Function -Message "Issue creating server endpoint." -Target $endpointName -ErrorRecord $_
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlEndpoint
    }
}
tools\dbatools\functions\Copy-DbaExtendedEvent.ps1
function Copy-DbaExtendedEvent {
    <#
        .SYNOPSIS
            Migrates SQL Extended Event Sessions except the two default sessions, AlwaysOn_health and system_health.

        .DESCRIPTION
            Migrates SQL Extended Event Sessions except the two default sessions, AlwaysOn_health and system_health.

            By default, all non-system Extended Events are migrated.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER XeSession
            The Extended Event Session(s) to process. This list is auto-populated from the server. If unspecified, all Extended Event Sessions will be processed.

        .PARAMETER ExcludeXeSession
            The Extended Event Session(s) to exclude. This list is auto-populated from the server.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER Force
            If this switch is enabled, existing Extended Events sessions on Destination with matching names from Source will be dropped.

        .NOTES
            Tags: Migration, ExtendedEvent, XEvent
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaExtendedEvent

        .EXAMPLE
            Copy-DbaExtendedEvent -Source sqlserver2014a -Destination sqlcluster

            Copies all Extended Event sessions from sqlserver2014a to sqlcluster using Windows credentials.

        .EXAMPLE
            Copy-DbaExtendedEvent -Source sqlserver2014a -Destination sqlcluster -SourceSqlCredential $cred

            Copies all Extended Event sessions from sqlserver2014a to sqlcluster using SQL credentials for sqlserver2014a and Windows credentials for sqlcluster.

        .EXAMPLE
            Copy-DbaExtendedEvent -Source sqlserver2014a -Destination sqlcluster -WhatIf

            Shows what would happen if the command were executed.

        .EXAMPLE
            Copy-DbaExtendedEvent -Source sqlserver2014a -Destination sqlcluster -XeSession CheckQueries, MonitorUserDefinedException

            Copies only the Extended Events named CheckQueries and MonitorUserDefinedException from sqlserver2014a to sqlcluster.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]
        $SourceSqlCredential,
        [PSCredential]
        $DestinationSqlCredential,
        [object[]]$XeSession,
        [object[]]$ExcludeXeSession,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 10
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        $sourceSqlConn = $sourceServer.ConnectionContext.SqlConnectionObject
        $sourceSqlStoreConnection = New-Object Microsoft.SqlServer.Management.Sdk.Sfc.SqlStoreConnection $sourceSqlConn
        $sourceStore = New-Object  Microsoft.SqlServer.Management.XEvent.XEStore $sourceSqlStoreConnection
        $storeSessions = $sourceStore.Sessions | Where-Object { $_.Name -notin 'AlwaysOn_health', 'system_health' }
        if ($XeSession) {
            $storeSessions = $storeSessions | Where-Object Name -In $XeSession
        }
        if ($ExcludeXeSession) {
            $storeSessions = $storeSessions | Where-Object Name -NotIn $ExcludeXeSession
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            
            $destSqlConn = $destServer.ConnectionContext.SqlConnectionObject
            $destSqlStoreConnection = New-Object Microsoft.SqlServer.Management.Sdk.Sfc.SqlStoreConnection $destSqlConn
            $destStore = New-Object  Microsoft.SqlServer.Management.XEvent.XEStore $destSqlStoreConnection
            
            Write-Message -Level Verbose -Message "Migrating sessions."
            foreach ($session in $storeSessions) {
                $sessionName = $session.Name
                
                $copyXeSessionStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name         = $sessionName
                    Type         = "Extended Event"
                    Status       = $null
                    Notes        = $null
                    DateTime     = [DbaDateTime](Get-Date)
                }
                
                if ($null -ne $destStore.Sessions[$sessionName]) {
                    if ($force -eq $false) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Extended Event Session '$sessionName' was skipped because it already exists on $destinstance.")) {
                            $copyXeSessionStatus.Status = "Skipped"
                            $copyXeSessionStatus.Notes = "Already exists"
                            $copyXeSessionStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            
                            Write-Message -Level Verbose -Message "Extended Event Session '$sessionName' was skipped because it already exists on $destinstance."
                            Write-Message -Level Verbose -Message "Use -Force to drop and recreate."
                        }
                        continue
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Attempting to drop $sessionName")) {
                            Write-Message -Level Verbose -Message "Extended Event Session '$sessionName' exists on $destinstance."
                            Write-Message -Level Verbose -Message "Force specified. Dropping $sessionName."
                            
                            try {
                                $destStore.Sessions[$sessionName].Drop()
                            }
                            catch {
                                $copyXeSessionStatus.Status = "Failed"
                                $copyXeSessionStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                
                                Stop-Function -Message "Unable to drop session. Moving on." -Target $sessionName -ErrorRecord $_ -Continue
                            }
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Migrating session $sessionName")) {
                    try {
                        $sql = $session.ScriptCreate().GetScript() | Out-String
                        
                        Write-Message -Level Debug -Message $sql
                        Write-Message -Level Verbose -Message "Migrating session $sessionName."
                        $null = $destServer.Query($sql)
                        
                        if ($session.IsRunning -eq $true) {
                            $destStore.Sessions.Refresh()
                            $destStore.Sessions[$sessionName].Start()
                        }
                        
                        $copyXeSessionStatus.Status = "Successful"
                        $copyXeSessionStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copyXeSessionStatus.Status = "Failed"
                        $copyXeSessionStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Stop-Function -Message "Unable to create session." -Target $sessionName -ErrorRecord $_
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlExtendedEvent
    }
}
tools\dbatools\functions\Copy-DbaLinkedServer.ps1
function Copy-DbaLinkedServer {
    <#
        .SYNOPSIS
            Copy-DbaLinkedServer migrates Linked Servers from one SQL Server to another. Linked Server logins and passwords are migrated as well.

        .DESCRIPTION
            By using password decryption techniques provided by Antti Rantasaari (NetSPI, 2014), this script migrates SQL Server Linked Servers from one server to another, while maintaining username and password.

            Credit: https://blog.netspi.com/decrypting-mssql-database-link-server-passwords/
            License: BSD 3-Clause http://opensource.org/licenses/BSD-3-Clause

        .PARAMETER Source
            Source SQL Server (2005 and above). You must have sysadmin access to both SQL Server and Windows.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server (2005 and above). You must have sysadmin access to both SQL Server and Windows.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER LinkedServer
            The linked server(s) to process - this list is auto-populated from the server. If unspecified, all linked servers will be processed.

        .PARAMETER ExcludeLinkedServer
            The linked server(s) to exclude - this list is auto-populated from the server

        .PARAMETER UpgradeSqlClient
            Upgrade any SqlClient Linked Server to the current Version

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER Force
            By default, if a Linked Server exists on the source and destination, the Linked Server is not copied over. Specifying -force will drop and recreate the Linked Server on the Destination server.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: WSMan, Migration, LinkedServer
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers, Remote Registry & Remote Administration enabled and accessible on source server.

            Limitations: Hasn't been tested thoroughly. Works on Win8.1 and SQL Server 2012 & 2014 so far.
            This just copies the SQL portion. It does not copy files (ie. a local SQLite database, or Microsoft Access DB), nor does it configure ODBC entries.

        .LINK
            https://dbatools.io/Copy-DbaLinkedServer

        .EXAMPLE
            Copy-DbaLinkedServer -Source sqlserver2014a -Destination sqlcluster

            Description
            Copies all SQL Server Linked Servers on sqlserver2014a to sqlcluster. If Linked Server exists on destination, it will be skipped.

        .EXAMPLE
            Copy-DbaLinkedServer -Source sqlserver2014a -Destination sqlcluster -LinkedServer SQL2K5,SQL2k -Force

            Description
            Copies over two SQL Server Linked Servers (SQL2K and SQL2K2) from sqlserver to sqlcluster. If the credential already exists on the destination, it will be dropped.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    Param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]$SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [object[]]$LinkedServer,
        [object[]]$ExcludeLinkedServer,
        [switch]$UpgradeSqlClient,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        $null = Test-ElevationRequirement -ComputerName $Source.ComputerName
        function Copy-DbaLinkedServers {
            param (
                [string[]]$LinkedServer,
                [bool]$force
            )

            Write-Message -Level Verbose -Message "Collecting Linked Server logins and passwords on $($sourceServer.Name)."
            $sourcelogins = Get-DecryptedObject -SqlInstance $sourceServer -Type LinkedServer

            $serverlist = $sourceServer.LinkedServers

            if ($LinkedServer) {
                $serverlist = $serverlist | Where-Object Name -In $LinkedServer
            }
            if ($ExcludeLinkedServer) {
                $serverList = $serverlist | Where-Object Name -NotIn $ExcludeLinkedServer
            }

            foreach ($currentLinkedServer in $serverlist) {
                $provider = $currentLinkedServer.ProviderName
                try {
                    $destServer.LinkedServers.Refresh()
                    $destServer.LinkedServers.LinkedServerLogins.Refresh()
                }
                catch { }

                $linkedServerName = $currentLinkedServer.Name

                $copyLinkedServer = [pscustomobject]@{
                    SourceServer      = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name              = $linkedServerName
                    Type              = "Linked Server"
                    Status            = $null
                    Notes             = $provider
                    DateTime          = [DbaDateTime](Get-Date)
                }

                # This does a check to warn of missing OleDbProviderSettings but should only be checked on SQL on Windows
                if ($destServer.Settings.OleDbProviderSettings.Name.Length -ne 0) {
                    if (!$destServer.Settings.OleDbProviderSettings.Name -contains $provider -and !$provider.StartsWith("SQLN")) {
                        $copyLinkedServer.Status = "Skipped"
                        $copyLinkedServer.Notes = "Missing provider"
                        $copyLinkedServer | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject

                        Write-Message -Level Verbose -Message "$($destServer.Name) does not support the $provider provider. Skipping $linkedServerName."
                        continue
                    }
                }

                if ($null -ne $destServer.LinkedServers[$linkedServerName]) {
                    if (!$force) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "$linkedServerName exists $($destServer.Name). Skipping.")) {
                            $copyLinkedServer.Status = "Skipped"
                            $copyLinkedServer | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            
                            Write-Message -Level Verbose -Message "$linkedServerName exists $($destServer.Name). Skipping."
                        }
                        continue
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Dropping $linkedServerName")) {
                            if ($currentLinkedServer.Name -eq 'repl_distributor') {
                                Write-Message -Level Verbose -Message "repl_distributor cannot be dropped. Not going to try."
                                continue
                            }

                            $destServer.LinkedServers[$linkedServerName].Drop($true)
                            $destServer.LinkedServers.refresh()
                        }
                    }
                }

                Write-Message -Level Verbose -Message "Attempting to migrate: $linkedServerName."
                If ($Pscmdlet.ShouldProcess($destinstance, "Migrating $linkedServerName")) {
                    try {
                        $sql = $currentLinkedServer.Script() | Out-String
                        Write-Message -Level Debug -Message $sql

                        if ($UpgradeSqlClient -and $sql -match "sqlncli") {
                            $destProviders = $destServer.Settings.OleDbProviderSettings | Where-Object { $_.Name -like 'SQLNCLI*' }
                            $newProvider = $destProviders | Sort-Object Name -Descending | Select-Object -First 1 -ExpandProperty Name

                            Write-Message -Level Verbose -Message "Changing sqlncli to $newProvider"
                            $sql = $sql -replace ("sqlncli[0-9]+", $newProvider)
                        }

                        $destServer.Query($sql)
                        $destServer.LinkedServers.Refresh()
                        Write-Message -Level Verbose -Message "$linkedServerName successfully copied."

                        $copyLinkedServer.Status = "Successful"
                        $copyLinkedServer | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copyLinkedServer.Status = "Failed"
                        $copyLinkedServer | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject

                        Stop-Function -Message "Issue adding linked server $destServer." -Target $linkedServerName -InnerErrorRecord $_
                        $skiplogins = $true
                    }
                }

                if ($skiplogins -ne $true) {
                    $destlogins = $destServer.LinkedServers[$linkedServerName].LinkedServerLogins
                    $lslogins = $sourcelogins | Where-Object { $_.Name -eq $linkedServerName }

                    foreach ($login in $lslogins) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Migrating $($login.Login)")) {
                            $currentlogin = $destlogins | Where-Object { $_.RemoteUser -eq $login.Identity }

                            $copyLinkedServer.Type = $login.Identity

                            if ($currentlogin.RemoteUser.length -ne 0) {
                                try {
                                    $currentlogin.SetRemotePassword($login.Password)
                                    $currentlogin.Alter()

                                    $copyLinkedServer.Status = "Successful"
                                    $copyLinkedServer | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                }
                                catch {
                                    $copyLinkedServer.Status = "Failed"
                                    $copyLinkedServer | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject

                                    Stop-Function -Message "Failed to copy login." -Target $login -InnerErrorRecord $_
                                }
                            }
                        }
                    }
                }
            }
        }
        
        if ($null -ne $SourceSqlCredential.Username) {
            Write-Message -Level Verbose -Message "You are using a SQL Credential. Note that this script requires Windows Administrator access on the source server. Attempting with $($SourceSqlCredential.Username)."
        }
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
            return
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        if (!(Test-SqlSa -SqlInstance $sourceServer -SqlCredential $SourceSqlCredential)) {
            Stop-Function -Message "Not a sysadmin on $source. Quitting." -Target $sourceServer
            return
        }
        Write-Message -Level Verbose -Message "Getting NetBios name for $source."
        $sourceNetBios = Resolve-NetBiosName $sourceserver
        
        Write-Message -Level Verbose -Message "Checking if Remote Registry is enabled on $source."
        try {
            Invoke-Command2 -Raw -Credential $Credential -ComputerName $sourceNetBios -ScriptBlock { Get-ItemProperty -Path "HKLM:\SOFTWARE\" } -ErrorAction Stop
        }
        catch {
            Stop-Function -Message "Can't connect to registry on $source." -Target $sourceNetBios -ErrorRecord $_
            return
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }
        
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            if (!(Test-SqlSa -SqlInstance $destServer -SqlCredential $DestinationSqlCredential)) {
                Stop-Function -Message "Not a sysadmin on $destinstance" -Target $destServer -Continue
            }
            
            # Magic happens here
            Copy-DbaLinkedServers $LinkedServer -Force:$force
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlLinkedServer
    }
}
tools\dbatools\functions\Copy-DbaLogin.ps1
function Copy-DbaLogin {
    <#
        .SYNOPSIS
            Migrates logins from source to destination SQL Servers. Supports SQL Server versions 2000 and newer.

        .DESCRIPTION
            SQL Server 2000: Migrates logins with SIDs, passwords, server roles and database roles.

            SQL Server 2005 & newer: Migrates logins with SIDs, passwords, defaultdb, server roles & securables, database permissions & securables, login attributes (enforce password policy, expiration, etc.)

            The login hash algorithm changed in SQL Server 2012, and is not backwards compatible with previous SQL Server versions. This means that while SQL Server 2000 logins can be migrated to SQL Server 2012, logins created in SQL Server 2012 can only be migrated to SQL Server 2012 and above.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Login
            The login(s) to process. Options for this list are auto-populated from the server. If unspecified, all logins will be processed.

        .PARAMETER ExcludeLogin
            The login(s) to exclude. Options for this list are auto-populated from the server.

        .PARAMETER ExcludeSystemLogin
            If this switch is enabled, NT SERVICE accounts will be skipped.

        .PARAMETER SyncOnly
            If this switch is enabled, only SQL Server login permissions, roles, etc. will be synced. Logins and users will not be added or dropped.  If a matching Login does not exist on the destination, the Login will be skipped.
            Credential removal is not currently supported for this parameter.

        .PARAMETER SyncSaName
            If this switch is enabled, the name of the sa account will be synced between Source and Destination

        .PARAMETER OutFile
            Calls Export-SqlLogin and exports all logins to a T-SQL formatted file. This does not perform a copy, so no destination is required.

        .PARAMETER InputObject
            Takes the parameters required from a Login object that has been piped into the command

        .PARAMETER LoginRenameHashtable
            Pass a hash table into this parameter to be passed into Rename-DbaLogin to update the Login and mappings after the Login is completed.

        .PARAMETER KillActiveConnection
            If this switch and -Force are enabled, all active connections and sessions on Destination will be killed.

            A login cannot be dropped when it has active connections on the instance.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER Force
            If this switch is enabled, the Login(s) will be dropped and recreated on Destination. Logins that own Agent jobs cannot be dropped at this time.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration, Login
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaLogin

        .EXAMPLE
            Copy-DbaLogin -Source sqlserver2014a -Destination sqlcluster -Force

            Copies all logins from Source Destination. If a SQL Login on Source exists on the Destination, the Login on Destination will be dropped and recreated.

            If active connections are found for a login, the copy of that Login will fail as it cannot be dropped.

        .EXAMPLE
            Copy-DbaLogin -Source sqlserver2014a -Destination sqlcluster -Force -KillActiveConnection

            Copies all logins from Source Destination. If a SQL Login on Source exists on the Destination, the Login on Destination will be dropped and recreated.

            If any active connections are found they will be killed.

        .EXAMPLE
            Copy-DbaLogin -Source sqlserver2014a -Destination sqlcluster -Exclude realcajun -SourceSqlCredential $scred -DestinationSqlCredential $dcred

            Copies all Logins from Source to Destination except for realcajun using SQL Authentication to connect to both instances.

            If a Login already exists on the destination, it will not be migrated.

        .EXAMPLE
            Copy-DbaLogin -Source sqlserver2014a -Destination sqlcluster -Login realcajun, netnerds -force

            Copies ONLY Logins netnerds and realcajun. If Login realcajun or netnerds exists on Destination, the existing Login(s) will be dropped and recreated.

        .EXAMPLE
            Copy-DbaLogin -Source sqlserver2014a -Destination sqlcluster -SyncOnly

            Syncs only SQL Server login permissions, roles, etc. Does not add or drop logins or users.

            If a matching Login does not exist on Destination, the Login will be skipped.

        .EXAMPLE
            Copy-DbaLogin -LoginRenameHashtable @{ "OldUser" ="newlogin" } -Source $Sql01 -Destination Localhost -SourceSqlCredential $sqlcred

            Copies OldUser and then renames it to newlogin.

        .EXAMPLE
            Get-DbaLogin -SqlInstance sql2016 | Out-GridView -Passthru | Copy-DbaLogin -Destination sql2017

            Displays all available logins on sql2016 in a grid view, then copies all selected logins to sql2017.
    #>

    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess)]
    Param (
        [parameter(ParameterSetName = "SqlInstance", Mandatory)]
        [DbaInstanceParameter]$Source,
        [PSCredential]$SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [object[]]$Login,
        [object[]]$ExcludeLogin,
        [switch]$ExcludeSystemLogin,
        [switch]$SyncOnly,
        [parameter(ParameterSetName = "Live")]
        [parameter(ParameterSetName = "SqlInstance")]
        [switch]$SyncSaName,
        [parameter(ParameterSetName = "File", Mandatory)]
        [string]$OutFile,
        [parameter(ParameterSetName = "InputObject", ValueFromPipeline)]
        [object]$InputObject,
        [hashtable]$LoginRenameHashtable,
        [switch]$KillActiveConnection,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        function Copy-Login {
            foreach ($sourceLogin in $sourceServer.Logins) {
                $userName = $sourceLogin.name
                
                $copyLoginStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Type         = "Login - $($sourceLogin.LoginType)"
                    Name         = $userName
                    DestinationLogin = $userName
                    SourceLogin  = $userName
                    Status       = $null
                    Notes        = $null
                    DateTime     = [DbaDateTime](Get-Date)
                }
                
                if ($Login -and $Login -notcontains $userName -or $ExcludeLogin -contains $userName) { continue }

                if ($sourceLogin.id -eq 1) { continue }

                if ($userName.StartsWith("##") -or $userName -eq 'sa') {
                    Write-Message -Level Verbose -Message "Skipping $userName."
                    continue
                }

                $serverName = Resolve-NetBiosName $sourceServer

                $currentLogin = $sourceServer.ConnectionContext.truelogin

                if ($currentLogin -eq $userName -and $force) {
                    if ($Pscmdlet.ShouldProcess("console", "Stating $userName is skipped because it is performing the migration.")) {
                        Write-Message -Level Verbose -Message "Cannot drop login performing the migration. Skipping."
                        $copyLoginStatus.Status = "Skipped"
                        $copyLoginStatus.Notes = "Current login"
                        $copyLoginStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    continue
                }
                
                if (($destServer.LoginMode -ne [Microsoft.SqlServer.Management.Smo.ServerLoginMode]::Mixed) -and ($sourceLogin.LoginType -eq [Microsoft.SqlServer.Management.Smo.LoginType]::SqlLogin)) {
                    Write-Message -Level Verbose -Message "$Destination does not have Mixed Mode enabled. [$userName] is an SQL Login. Enable mixed mode authentication after the migration completes to use this type of login."
                }

                $userBase = ($userName.Split("\")[0]).ToLower()

                if ($serverName -eq $userBase -or $userName.StartsWith("NT ")) {
                    if ($sourceServer.ComputerName -ne $destServer.ComputerName) {
                        if ($Pscmdlet.ShouldProcess("console", "Stating $userName was skipped because it is a local machine name.")) {
                            Write-Message -Level Verbose -Message "$userName was skipped because it is a local machine name."
                            $copyLoginStatus.Status = "Skipped"
                            $copyLoginStatus.Notes = "Local machine name"
                            $copyLoginStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        }
                        continue
                    }
                    else {
                        if ($ExcludeSystemLogin) {
                            if ($Pscmdlet.ShouldProcess("console", "$userName was skipped because ExcludeSystemLogin was specified.")) {
                                Write-Message -Level Verbose -Message "$userName was skipped because ExcludeSystemLogin was specified."
                                
                                $copyLoginStatus.Status = "Skipped"
                                $copyLoginStatus.Notes = "System login"
                                $copyLoginStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            }
                            continue
                        }
                        
                        if ($Pscmdlet.ShouldProcess("console", "Stating local login $userName since the source and destination server reside on the same machine.")) {
                            Write-Message -Level Verbose -Message "Copying local login $userName since the source and destination server reside on the same machine."
                        }
                    }
                }
                
                if ($null -ne $destServer.Logins.Item($userName) -and !$force) {
                    if ($Pscmdlet.ShouldProcess("console", "Stating $userName is skipped because it exists at destination.")) {
                        Write-Message -Level Verbose -Message "$userName already exists in destination. Use -Force to drop and recreate."
                        $copyLoginStatus.Status = "Skipped"
                        $copyLoginStatus.Notes = "Already exists"
                        $copyLoginStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    continue
                }
                
                if ($null -ne $destServer.Logins.Item($userName) -and $force) {
                    if ($userName -eq $destServer.ServiceAccount) {
                        if ($Pscmdlet.ShouldProcess("console", "$userName is the destination service account. Skipping drop.")) {
                            Write-Message -Level Verbose -Message "$userName is the destination service account. Skipping drop."
                            
                            $copyLoginStatus.Status = "Skipped"
                            $copyLoginStatus.Notes = "Destination service account"
                            $copyLoginStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        }
                        continue
                    }
                    
                    if ($Pscmdlet.ShouldProcess($destinstance, "Dropping $userName")) {

                        # Kill connections, delete user
                        Write-Message -Level Verbose -Message "Attempting to migrate $userName"
                        Write-Message -Level Verbose -Message "Force was specified. Attempting to drop $userName on $destinstance."

                        try {
                            $ownedDbs = $destServer.Databases | Where-Object Owner -eq $userName

                            foreach ($ownedDb in $ownedDbs) {
                                Write-Message -Level Verbose -Message "Changing database owner for $($ownedDb.name) from $userName to sa."
                                $ownedDb.SetOwner('sa')
                                $ownedDb.Alter()
                            }

                            $ownedJobs = $destServer.JobServer.Jobs | Where-Object OwnerLoginName -eq $userName

                            foreach ($ownedJob in $ownedJobs) {
                                Write-Message -Level Verbose -Message "Changing job owner for $($ownedJob.name) from $userName to sa."
                                $ownedJob.Set_OwnerLoginName('sa')
                                $ownedJob.Alter()
                            }

                            $activeConnections = $destServer.EnumProcesses() | Where-Object Login -eq $userName

                            if ($activeConnections -and $KillActiveConnection) {
                                if (!$destServer.Logins.Item($userName).IsDisabled) {
                                    $disabled = $true
                                    $destServer.Logins.Item($userName).Disable()
                                }

                                $activeConnections | ForEach-Object { $destServer.KillProcess($_.Spid) }
                                Write-Message -Level Verbose -Message "-KillActiveConnection was provided. There are $($activeConnections.Count) active connections killed."
                                # just in case the kill didn't work, it'll leave behind a disabled account
                                if ($disabled) { $destServer.Logins.Item($userName).Enable() }
                            }
                            elseif ($activeConnections) {
                                Write-Message -Level Verbose -Message "There are $($activeConnections.Count) active connections found for the login $userName. Utilize -KillActiveConnection with -Force to kill the connections."
                            }
                            $destServer.Logins.Item($userName).Drop()

                            Write-Message -Level Verbose -Message "Successfully dropped $userName on $destinstance."
                        }
                        catch {
                            $copyLoginStatus.Status = "Failed"
                            $copyLoginStatus.Notes = (Get-ErrorMessage -Record $_).Message
                            $copyLoginStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject

                            Stop-Function -Message "Could not drop $userName." -Category InvalidOperation -ErrorRecord $_ -Target $destServer -Continue 3>$null
                        }
                    }
                }

                if ($Pscmdlet.ShouldProcess($destinstance, "Adding SQL login $userName")) {

                    Write-Message -Level Verbose -Message "Attempting to add $userName to $destinstance."
                    $destLogin = New-Object Microsoft.SqlServer.Management.Smo.Login($destServer, $userName)

                    Write-Message -Level Verbose -Message "Setting $userName SID to source username SID."
                    $destLogin.Set_Sid($sourceLogin.Get_Sid())

                    $defaultDb = $sourceLogin.DefaultDatabase

                    Write-Message -Level Verbose -Message "Setting login language to $($sourceLogin.Language)."
                    $destLogin.Language = $sourceLogin.Language

                    if ($null -eq $destServer.databases[$defaultDb]) {
                        # we end up here when the default database on source doesn't exist on dest
                        # if source login is a sysadmin, then set the default database to master
                        # if not, set it to tempdb (see #303)
                        $OrigdefaultDb = $defaultDb
                        try { $sourcesysadmins = $sourceServer.roles['sysadmin'].EnumMemberNames() }
                        catch { $sourcesysadmins = $sourceServer.roles['sysadmin'].EnumServerRoleMembers() }
                        if ($sourcesysadmins -contains $userName) {
                            $defaultDb = "master"
                        }
                        else {
                            $defaultDb = "tempdb"
                        }
                        Write-Message -Level Verbose -Message "$OrigdefaultDb does not exist on destination. Setting defaultdb to $defaultDb."
                    }

                    Write-Message -Level Verbose -Message "Set $userName defaultdb to $defaultDb."
                    $destLogin.DefaultDatabase = $defaultDb

                    $checkexpiration = "ON"; $checkpolicy = "ON"

                    if ($sourceLogin.PasswordPolicyEnforced -eq $false) { $checkpolicy = "OFF" }

                    if (!$sourceLogin.PasswordExpirationEnabled) { $checkexpiration = "OFF" }

                    $destLogin.PasswordPolicyEnforced = $sourceLogin.PasswordPolicyEnforced
                    $destLogin.PasswordExpirationEnabled = $sourceLogin.PasswordExpirationEnabled

                    # Attempt to add SQL Login User
                    if ($sourceLogin.LoginType -eq "SqlLogin") {
                        $destLogin.LoginType = "SqlLogin"
                        $sourceLoginname = $sourceLogin.name

                        switch ($sourceServer.versionMajor) {
                            0 { $sql = "SELECT CONVERT(VARBINARY(256),password) as hashedpass FROM master.dbo.syslogins WHERE loginname='$sourceLoginname'" }
                            8 { $sql = "SELECT CONVERT(VARBINARY(256),password) as hashedpass FROM dbo.syslogins WHERE name='$sourceLoginname'" }
                            9 { $sql = "SELECT CONVERT(VARBINARY(256),password_hash) as hashedpass FROM sys.sql_logins where name='$sourceLoginname'" }
                            default {
                                $sql = "SELECT CAST(CONVERT(VARCHAR(256), CAST(LOGINPROPERTY(name,'PasswordHash')
                        AS VARBINARY(256)), 1) AS NVARCHAR(max)) AS hashedpass FROM sys.server_principals
                        WHERE principal_id = $($sourceLogin.id)"
                            }
                        }

                        try {
                            $hashedPass = $sourceServer.ConnectionContext.ExecuteScalar($sql)
                        }
                        catch {
                            $hashedPassDt = $sourceServer.Databases['master'].ExecuteWithResults($sql)
                            $hashedPass = $hashedPassDt.Tables[0].Rows[0].Item(0)
                        }

                        if ($hashedPass.GetType().Name -ne "String") {
                            $passString = "0x"; $hashedPass | ForEach-Object { $passString += ("{0:X}" -f $_).PadLeft(2, "0") }
                            $hashedPass = $passString
                        }

                        try {
                            $destLogin.Create($hashedPass, [Microsoft.SqlServer.Management.Smo.LoginCreateOptions]::IsHashed)
                            $destLogin.Refresh()
                            Write-Message -Level Verbose -Message "Successfully added $userName to $destinstance."

                            $copyLoginStatus.Status = "Successful"
                            $copyLoginStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        }
                        catch {
                            try {
                                $sid = "0x"; $sourceLogin.sid | ForEach-Object { $sid += ("{0:X}" -f $_).PadLeft(2, "0") }
                                $sql = "CREATE LOGIN [$userName] WITH PASSWORD = $hashedPass HASHED, SID = $sid,
                                                DEFAULT_DATABASE = [$defaultDb], CHECK_POLICY = $checkpolicy,
                                                CHECK_EXPIRATION = $checkexpiration, DEFAULT_LANGUAGE = [$($sourceLogin.Language)]"

                                $null = $destServer.Query($sql)

                                $destLogin = $destServer.logins[$userName]
                                Write-Message -Level Verbose -Message "Successfully added $userName to $destinstance."

                                $copyLoginStatus.Status = "Successful"
                                $copyLoginStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject

                            }
                            catch {
                                $copyLoginStatus.Status = "Failed"
                                $copyLoginStatus.Notes = (Get-ErrorMessage -Record $_).Message
                                $copyLoginStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject

                                Stop-Function -Message "Failed to add $userName to $destinstance." -Category InvalidOperation -ErrorRecord $_ -Target $destServer -Continue 3>$null
                            }
                        }
                    }
                    # Attempt to add Windows User
                    elseif ($sourceLogin.LoginType -eq "WindowsUser" -or $sourceLogin.LoginType -eq "WindowsGroup") {
                        Write-Message -Level Verbose -Message "Adding as login type $($sourceLogin.LoginType)"
                        $destLogin.LoginType = $sourceLogin.LoginType

                        Write-Message -Level Verbose -Message "Setting language as $($sourceLogin.Language)"
                        $destLogin.Language = $sourceLogin.Language

                        try {
                            $destLogin.Create()
                            $destLogin.Refresh()
                            Write-Message -Level Verbose -Message "Successfully added $userName to $destinstance."

                            $copyLoginStatus.Status = "Successful"
                            $copyLoginStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject

                        }
                        catch {
                            $copyLoginStatus.Status = "Failed"
                            $copyLoginStatus.Notes = (Get-ErrorMessage -Record $_).Message
                            $copyLoginStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject

                            Stop-Function -Message "Failed to add $userName to $destinstance" -Category InvalidOperation -ErrorRecord $_ -Target $destServer -Continue 3>$null
                        }
                    }
                    # This script does not currently support certificate mapped or asymmetric key users.
                    else {
                        Write-Message -Level Verbose -Message "$($sourceLogin.LoginType) logins not supported. $($sourceLogin.name) skipped."

                        $copyLoginStatus.Status = "Skipped"
                        $copyLoginStatus.Notes = "$($sourceLogin.LoginType) not supported"
                        $copyLoginStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject

                        continue
                    }

                    if ($sourceLogin.IsDisabled) {
                        try {
                            $destLogin.Disable()
                        }
                        catch {
                            $copyLoginStatus.Status = "Successful - but could not disable on destination"
                            $copyLoginStatus.Notes = (Get-ErrorMessage -Record $_).Message
                            $copyLoginStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject

                            Stop-Function -Message "$userName disabled on source, could not be disabled on $destinstance." -Category InvalidOperation -ErrorRecord $_ -Target $destServer  3>$null
                        }
                    }
                    if ($sourceLogin.DenyWindowsLogin) {
                        try {
                            $destLogin.DenyWindowsLogin = $true
                        }
                        catch {
                            $copyLoginStatus.Status = "Successful - but could not deny login on destination"
                            $copyLoginStatus.Notes = (Get-ErrorMessage -Record $_).Message
                            $copyLoginStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject

                            Stop-Function -Message "$userName denied login on source, could not be denied login on $destinstance." -Category InvalidOperation -ErrorRecord $_ -Target $destServer 3>$null
                        }
                    }
                }
                if ($Pscmdlet.ShouldProcess($destinstance, "Updating SQL login $userName permissions")) {
                    Update-SqlPermissions -sourceserver $sourceServer -sourcelogin $sourceLogin -destserver $destServer -destlogin $destLogin
                }

                if ($LoginRenameHashtable.Keys -contains $userName) {
                    $NewLogin = $LoginRenameHashtable[$userName]

                    if ($Pscmdlet.ShouldProcess($destinstance, "Renaming SQL Login $userName to $NewLogin")) {
                        try {
                            Rename-DbaLogin -SqlInstance $destServer -Login $userName -NewLogin $NewLogin

                            $copyLoginStatus.DestinationLogin = $NewLogin
                            $copyLoginStatus.Status = "Successful"
                            $copyLoginStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject

                        }
                        catch {
                            $copyLoginStatus.DestinationLogin = $NewLogin
                            $copyLoginStatus.Status = "Failed to rename"
                            $copyLoginStatus.Notes = (Get-ErrorMessage -Record $_).Message
                            $copyLoginStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject

                            Stop-Function -Message "Issue renaming $userName to $NewLogin" -Category InvalidOperation -ErrorRecord $_ -Target $destServer 3>$null
                        }
                    }
                }
            }
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }
        if ($InputObject) {
            $Source = $InputObject[0].Parent.Name
            $Sourceserver = $InputObject[0].Parent
            $Login = $InputObject.Name
        }
        else {
            try {
                Write-Message -Level Verbose -Message "Connecting to $Source"
                $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
                return
            }
        }
        $sourceVersionMajor = $sourceServer.VersionMajor

        if ($OutFile) {
            Export-DbaLogin -SqlInstance $sourceServer -FilePath $OutFile -Login $Login -ExcludeLogin $ExcludeLogin
            continue
        }

        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            
            $destVersionMajor = $destServer.VersionMajor
            if ($sourceVersionMajor -gt 10 -and $destVersionMajor -lt 11) {
                Stop-Function -Message "Login migration from version $sourceVersionMajor to $destVersionMajor is not supported." -Category InvalidOperation -ErrorRecord $_ -Target $sourceServer
            }

            if ($sourceVersionMajor -lt 8 -or $destVersionMajor -lt 8) {
                Stop-Function -Message "SQL Server 7 and below are not supported." -Category InvalidOperation -ErrorRecord $_ -Target $sourceServer
            }
            
            if ($SyncOnly) {
                if ($Pscmdlet.ShouldProcess($destinstance, "Syncing $Login permissions")) {
                    Sync-DbaLoginPermission -Source $sourceServer -Destination $destServer -Login $Login -ExcludeLogin $ExcludeLogin
                    continue
                }
            }
            
            Write-Message -Level Verbose -Message "Attempting Login Migration."
            Copy-Login -sourceserver $sourceServer -destserver $destServer -Login $Login -Exclude $ExcludeLogin

            if ($SyncSaName) {
                $sa = $sourceServer.Logins | Where-Object id -eq 1
                $destSa = $destServer.Logins | Where-Object id -eq 1
                $saName = $sa.Name
                if ($saName -ne $destSa.name) {
                    Write-Message -Level Verbose -Message "Changing sa username to match source ($saName)."
                    if ($Pscmdlet.ShouldProcess($destinstance, "Changing sa username to match source ($saName)")) {
                        $destSa.Rename($saName)
                        $destSa.Alter()
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlLogin
    }
}
tools\dbatools\functions\Copy-DbaQueryStoreConfig.ps1
function Copy-DbaQueryStoreConfig {
    <#
        .SYNOPSIS
            Copies the configuration of a Query Store enabled database and sets the copied configuration on other databases.

        .DESCRIPTION
            Copies the configuration of a Query Store enabled database and sets the copied configuration on other databases.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2016 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER SourceDatabase
            Specifies the database to copy the Query Store configuration from.

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2016 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER DestinationDatabase
            Specifies a list of databases that will receive a copy of the Query Store configuration of the SourceDatabase.

        .PARAMETER Exclude
            Specifies a list of databases which will NOT receive a copy of the Query Store configuration.

        .PARAMETER AllDatabases
            If this switch is enabled, the Query Store configuration will be copied to all databases on the destination instance.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Enrico van de Laar ( @evdlaar )
            Tags: QueryStore

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-QueryStoreConfig

        .EXAMPLE
            Copy-DbaQueryStoreConfig -Source ServerA\SQL -SourceDatabase AdventureWorks -Destination ServerB\SQL -AllDatabases

            Copy the Query Store configuration of the AdventureWorks database in the ServerA\SQL instance and apply it on all user databases in the ServerB\SQL Instance.

        .EXAMPLE
            Copy-DbaQueryStoreConfig -Source ServerA\SQL -SourceDatabase AdventureWorks -Destination ServerB\SQL -DestinationDatabase WorldWideTraders

            Copy the Query Store configuration of the AdventureWorks database in the ServerA\SQL instance and apply it to the WorldWideTraders database in the ServerB\SQL Instance.
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]$SourceSqlCredential,
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [object]$SourceDatabase,
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [object[]]$DestinationDatabase,
        [object[]]$Exclude,
        [switch]$AllDatabases,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Write-Message -Message "Connecting to source: $Source." -Level Verbose
        try {
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
        }
        catch {
            Stop-Function -Message "Can't connect to $Source." -ErrorRecord $_ -Target $Source
            return
        }
    }
    
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            # Grab the Query Store configuration from the SourceDatabase through the Get-DbaQueryStoreConfig function
            $SourceQSConfig = Get-DbaDbQueryStoreOptions -SqlInstance $sourceServer -Database $SourceDatabase
            
            if (!$DestinationDatabase -and !$Exclude -and !$AllDatabases) {
                Stop-Function -Message "You must specify databases to execute against using either -DestinationDatabase, -Exclude or -AllDatabases." -Continue
            }
            
            foreach ($destinationServer in $destinstance) {
                
                try {
                    Write-Message -Level Verbose -Message "Connecting to $destinstance"
                    $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential
                }
                catch {
                    Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
                }
                
                # We have to exclude all the system databases since they cannot have the Query Store feature enabled
                $dbs = Get-DbaDatabase -SqlInstance $destServer -ExcludeAllSystemDb
                
                if ($DestinationDatabase.count -gt 0) {
                    $dbs = $dbs | Where-Object { $DestinationDatabase -contains $_.Name }
                }
                
                if ($Exclude.count -gt 0) {
                    $dbs = $dbs | Where-Object { $exclude -notcontains $_.Name }
                }
                
                if ($dbs.count -eq 0) {
                    Stop-Function -Message "No matching databases found. Check the spelling and try again." -Continue
                }
                
                foreach ($db in $dbs) {
                    # skipping the database if the source and destination are the same instance
                    if (($sourceServer.Name -eq $destinationServer) -and ($SourceDatabase -eq $db.Name)) {
                        continue
                    }
                    Write-Message -Message "Processing destination database: $db on $destinationServer." -Level Verbose
                    $copyQueryStoreStatus = [pscustomobject]@{
                        SourceServer = $sourceServer.name
                        SourceDatabase = $SourceDatabase
                        DestinationServer = $destinationServer
                        Name         = $db.name
                        Type         = "QueryStore Configuration"
                        Status       = $null
                        DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                    }
                    
                    if ($db.IsAccessible -eq $false) {
                        $copyQueryStoreStatus.Status = "Skipped"
                        Stop-Function -Message "The database $db on server $destinationServer is not accessible. Skipping database." -Continue
                    }
                    
                    Write-Message -Message "Executing Set-DbaQueryStoreConfig." -Level Verbose
                    # Set the Query Store configuration through the Set-DbaQueryStoreConfig function
                    try {
                        $null = Set-DbaDbQueryStoreOptions -SqlInstance $destinationServer -SqlCredential $DestinationSqlCredential `
                                                        -Database $db.name `
                                                        -State $SourceQSConfig.ActualState `
                                                        -FlushInterval $SourceQSConfig.FlushInterval `
                                                        -CollectionInterval $SourceQSConfig.CollectionInterval `
                                                        -MaxSize $SourceQSConfig.MaxSize `
                                                        -CaptureMode $SourceQSConfig.CaptureMode `
                                                        -CleanupMode $SourceQSConfig.CleanupMode `
                                                        -StaleQueryThreshold $SourceQSConfig.StaleQueryThreshold
                        $copyQueryStoreStatus.Status = "Successful"
                    }
                    catch {
                        $copyQueryStoreStatus.Status = "Failed"
                        Stop-Function -Message "Issue setting Query Store on $db." -Target $db -ErrorRecord $_ -Continue
                    }
                    $copyQueryStoreStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                }
            }
        }
    }
}
tools\dbatools\functions\Copy-DbaResourceGovernor.ps1
function Copy-DbaResourceGovernor {
    <#
        .SYNOPSIS
            Migrates Resource Pools

        .DESCRIPTION
            By default, all non-system resource pools are migrated. If the pool already exists on the destination, it will be skipped unless -Force is used.

            The -ResourcePool parameter is auto-populated for command-line completion and can be used to copy only specific objects.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2008 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER ResourcePool
            Specifies the resource pool(s) to process. Options for this list are auto-populated from the server. If unspecified, all resource pools will be processed.

        .PARAMETER ExcludeResourcePool
            Specifies the resource pool(s) to exclude. Options for this list are auto-populated from the server

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER Force
            If this switch is enabled, the policies will be dropped and recreated on Destination.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration, ResourceGovernor
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaResourceGovernor

        .EXAMPLE
            Copy-DbaResourceGovernor -Source sqlserver2014a -Destination sqlcluster

            Copies all extended event policies from sqlserver2014a to sqlcluster using Windows credentials to connect to the SQL Server instances..

        .EXAMPLE
            Copy-DbaResourceGovernor -Source sqlserver2014a -Destination sqlcluster -SourceSqlCredential $cred

            Copies all extended event policies from sqlserver2014a to sqlcluster using SQL credentials to connect to sqlserver2014a and Windows credentials to connect to sqlcluster.

        .EXAMPLE
            Copy-DbaResourceGovernor -Source sqlserver2014a -Destination sqlcluster -WhatIf

            Shows what would happen if the command were executed.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]$SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [object[]]$ResourcePool,
        [object[]]$ExcludeResourcePool,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 10
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        $sourceClassifierFunction = Get-DbaResourceGovernorClassifierFunction -SqlInstance $sourceServer
        
       foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            $destClassifierFunction = Get-DbaResourceGovernorClassifierFunction -SqlInstance $destServer
            
            $copyResourceGovSetting = [pscustomobject]@{
                SourceServer = $sourceServer.Name
                DestinationServer = $destServer.Name
                Type         = "Resource Governor Settings"
                Name         = "All Settings"
                Status       = $null
                Notes        = $null
                DateTime     = [DbaDateTime](Get-Date)
            }
            
            $copyResourceGovClassifierFunc = [pscustomobject]@{
                SourceServer = $sourceServer.Name
                DestinationServer = $destServer.Name
                Type         = "Resource Governor Settings"
                Name         = "Classifier Function"
                Status       = $null
                Notes        = $null
                DateTime     = [DbaDateTime](Get-Date)
            }
            
            if ($Pscmdlet.ShouldProcess($destinstance, "Updating Resource Governor settings")) {
                if ($destServer.Edition -notmatch 'Enterprise' -and $destServer.Edition -notmatch 'Datacenter' -and $destServer.Edition -notmatch 'Developer') {
                    Write-Message -Level Verbose -Message "The resource governor is not available in this edition of SQL Server. You can manipulate resource governor metadata but you will not be able to apply resource governor configuration. Only Enterprise edition of SQL Server supports resource governor."
                }
                else {
                    try {
                        Write-Message -Level Verbose -Message "Managing classifier function."
                        if (!$sourceClassifierFunction) {
                            $copyResourceGovClassifierFunc.Status = "Skipped"
                            $copyResourceGovClassifierFunc.Notes = $null
                            $copyResourceGovClassifierFunc | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        }
                        else {
                            $fullyQualifiedFunctionName = $sourceClassifierFunction.Schema + "." + $sourceClassifierFunction.Name
                            
                            if (!$destClassifierFunction) {
                                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential
                                $destFunction = $destServer.Databases["master"].UserDefinedFunctions[$sourceClassifierFunction.Name]
                                if ($destFunction) {
                                    Write-Message -Level Verbose -Message "Dropping the function with the source classifier function name."
                                    $destFunction.Drop()
                                }
                                
                                Write-Message -Level Verbose -Message "Creating function."
                                $destServer.Query($sourceClassifierFunction.Script())
                                
                                $sql = "ALTER RESOURCE GOVERNOR WITH (CLASSIFIER_FUNCTION = $fullyQualifiedFunctionName);"
                                Write-Message -Level Debug -Message $sql
                                Write-Message -Level Verbose -Message "Mapping Resource Governor classifier function."
                                $destServer.Query($sql)
                                
                                $copyResourceGovClassifierFunc.Status = "Successful"
                                $copyResourceGovClassifierFunc.Notes = "The new classifier function has been created"
                                $copyResourceGovClassifierFunc | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                            }
                            else {
                                if ($Force -eq $false) {
                                    $copyResourceGovClassifierFunc.Status = "Skipped"
                                    $copyResourceGovClassifierFunc.Notes = "A classifier function already exists"
                                    $copyResourceGovClassifierFunc | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                }
                                else {
                                    
                                    $sql = "ALTER RESOURCE GOVERNOR WITH (CLASSIFIER_FUNCTION = NULL);"
                                    Write-Message -Level Debug -Message $sql
                                    Write-Message -Level Verbose -Message "Disabling the Resource Governor."
                                    $destServer.Query($sql)
                                    
                                    $sql = "ALTER RESOURCE GOVERNOR RECONFIGURE;"
                                    Write-Message -Level Debug -Message $sql
                                    Write-Message -Level Verbose -Message "Reconfiguring Resource Governor."
                                    $destServer.Query($sql)
                                    
                                    Write-Message -Level Verbose -Message "Dropping the destination classifier function."
                                    $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential
                                    $destFunction = $destServer.Databases["master"].UserDefinedFunctions[$sourceClassifierFunction.Name]
                                    $destClassifierFunction.Drop()
                                    
                                    Write-Message -Level Verbose -Message "Re-creating the Resource Governor classifier function."
                                    $destServer.Query($sourceClassifierFunction.Script())
                                    
                                    $sql = "ALTER RESOURCE GOVERNOR WITH (CLASSIFIER_FUNCTION = $fullyQualifiedFunctionName);"
                                    Write-Message -Level Debug -Message $sql
                                    Write-Message -Level Verbose -Message "Mapping Resource Governor classifier function."
                                    $destServer.Query($sql)
                                    
                                    $copyResourceGovClassifierFunc.Status = "Successful"
                                    $copyResourceGovClassifierFunc.Notes = "The old classifier function has been overwritten."
                                    $copyResourceGovClassifierFunc | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                }
                            }
                        }
                    }
                    catch {
                        $copyResourceGovSetting.Status = "Failed"
                        $copyResourceGovSetting.Notes = (Get-ErrorMessage -Record $_)
                        $copyResourceGovSetting | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Stop-Function -Message "Not able to update settings." -Target $destServer -ErrorRecord $_
                    }
                }
            }
            
            # Pools
            if ($ResourcePool) {
                $pools = $sourceServer.ResourceGovernor.ResourcePools | Where-Object Name -In $ResourcePool
            }
            elseif ($ExcludeResourcePool) {
                $pool = $sourceServer.ResourceGovernor.ResourcePools | Where-Object Name -NotIn $ExcludeResourcePool
            }
            else {
                $pools = $sourceServer.ResourceGovernor.ResourcePools | Where-Object { $_.Name -notin "internal", "default" }
            }
            
            Write-Message -Level Verbose -Message "Migrating pools."
            foreach ($pool in $pools) {
                $poolName = $pool.Name
                
                $copyResourceGovPool = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Type         = "Resource Governor Pool"
                    Name         = $poolName
                    Status       = $null
                    Notes        = $null
                    DateTime     = [DbaDateTime](Get-Date)
                }
                
                if ($null -ne $destServer.ResourceGovernor.ResourcePools[$poolName]) {
                    if ($force -eq $false) {
                        Write-Message -Level Verbose -Message "Pool '$poolName' was skipped because it already exists on $destinstance. Use -Force to drop and recreate."
                        
                        $copyResourceGovPool.Status = "Skipped"
                        $copyResourceGovPool.Notes = "Already exists"
                        $copyResourceGovPool | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        continue
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Attempting to drop $poolName")) {
                            Write-Message -Level Verbose -Message "Pool '$poolName' exists on $destinstance."
                            Write-Message -Level Verbose -Message "Force specified. Dropping $poolName."
                            
                            try {
                                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential
                                $destPool = $destServer.ResourceGovernor.ResourcePools[$poolName]
                                $workloadGroups = $destPool.WorkloadGroups
                                foreach ($workloadGroup in $workloadGroups) {
                                    $workloadGroup.Drop()
                                }
                                $destPool.Drop()
                                $destServer.ResourceGovernor.Alter()
                            }
                            catch {
                                $copyResourceGovPool.Status = "Failed to drop from Destination"
                                $copyResourceGovPool.Notes = (Get-ErrorMessage -Record $_)
                                $copyResourceGovPool | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                
                                Stop-Function -Message "Unable to drop: $_ Moving on." -Target $destPool -ErrorRecord $_ -Continue
                            }
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Migrating pool $poolName")) {
                    try {
                        $sql = $pool.Script() | Out-String
                        Write-Message -Level Debug -Message $sql
                        Write-Message -Level Verbose -Message "Copying pool $poolName."
                        $destServer.Query($sql)
                        
                        $copyResourceGovPool.Status = "Successful"
                        $copyResourceGovPool | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        $workloadGroups = $pool.WorkloadGroups
                        foreach ($workloadGroup in $workloadGroups) {
                            $workgroupName = $workloadGroup.Name
                            
                            $copyResourceGovWorkGroup = [pscustomobject]@{
                                SourceServer = $sourceServer.Name
                                DestinationServer = $destServer.Name
                                Type         = "Resource Governor Pool Workgroup"
                                Name         = $workgroupName
                                Status       = $null
                                Notes        = $null
                                DateTime     = [DbaDateTime](Get-Date)
                            }
                            
                            $sql = $workloadGroup.Script() | Out-String
                            Write-Message -Level Debug -Message $sql
                            Write-Message -Level Verbose -Message "Copying $workgroupName."
                            $destServer.Query($sql)
                            
                            $copyResourceGovWorkGroup.Status = "Successful"
                            $copyResourceGovWorkGroup | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        }
                    }
                    catch {
                        if ($copyResourceGovWorkGroup) {
                            $copyResourceGovWorkGroup.Status = "Failed"
                            $copyResourceGovWorkGroup.Notes = (Get-ErrorMessage -Record $_)
                            $copyResourceGovWorkGroup | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        }
                        Stop-Function -Message "Unable to migrate pool." -Target $pool -ErrorRecord $_
                    }
                }
            }
            
            if ($Pscmdlet.ShouldProcess($destinstance, "Reconfiguring")) {
                if ($destServer.Edition -notmatch 'Enterprise' -and $destServer.Edition -notmatch 'Datacenter' -and $destServer.Edition -notmatch 'Developer') {
                    Write-Message -Level Verbose -Message "The resource governor is not available in this edition of SQL Server. You can manipulate resource governor metadata but you will not be able to apply resource governor configuration. Only Enterprise edition of SQL Server supports resource governor."
                }
                else {
                    
                    Write-Message -Level Verbose -Message "Reconfiguring Resource Governor."
                    try {
                        if (!$sourceServer.ResourceGovernor.Enabled) {
                            $sql = "ALTER RESOURCE GOVERNOR DISABLE"
                            $destServer.Query($sql)
                        }
                        else {
                            $sql = "ALTER RESOURCE GOVERNOR RECONFIGURE"
                            $destServer.Query($sql)
                        }
                    }
                    catch {
                        $altermsg = $_.Exception
                    }
                    
                    
                    $copyResourceGovReconfig = [pscustomobject]@{
                        SourceServer = $sourceServer.Name
                        DestinationServer = $destServer.Name
                        Type         = "Reconfigure Resource Governor"
                        Name         = "Reconfigure Resource Governor"
                        Status       = "Successful"
                        Notes        = $altermsg
                        DateTime     = [DbaDateTime](Get-Date)
                    }
                    $copyResourceGovReconfig | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlResourceGovernor
    }
}
tools\dbatools\functions\Copy-DbaServerAudit.ps1
function Copy-DbaServerAudit {
    <#
        .SYNOPSIS
            Copy-DbaServerAudit migrates server audits from one SQL Server to another.

        .DESCRIPTION
            By default, all audits are copied. The -Audit parameter is auto-populated for command-line completion and can be used to copy only specific audits.

            If the audit already exists on the destination, it will be skipped unless -Force is used.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Audit
            The audit(s) to process. Options for this list are auto-populated from the server. If unspecified, all audits will be processed.

        .PARAMETER ExcludeAudit
            The audit(s) to exclude. Options for this list are auto-populated from the server.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER Force
            If this switch is enabled, the audits will be dropped and recreated on Destination.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaServerAudit

        .EXAMPLE
            Copy-DbaServerAudit -Source sqlserver2014a -Destination sqlcluster

            Copies all server audits from sqlserver2014a to sqlcluster, using Windows credentials. If audits with the same name exist on sqlcluster, they will be skipped.

        .EXAMPLE
            Copy-DbaServerAudit -Source sqlserver2014a -Destination sqlcluster -Audit tg_noDbDrop -SourceSqlCredential $cred -Force

            Copies a single audit, the tg_noDbDrop audit from sqlserver2014a to sqlcluster, using SQL credentials for sqlserver2014a and Windows credentials for sqlcluster. If an audit with the same name exists on sqlcluster, it will be dropped and recreated because -Force was used.

        .EXAMPLE
            Copy-DbaServerAudit -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force

            Shows what would happen if the command were executed using force.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]
        $SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]
        $DestinationSqlCredential,
        [object[]]$Audit,
        [object[]]$ExcludeAudit,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 10
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        $serverAudits = $sourceServer.Audits
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            $destAudits = $destServer.Audits
            foreach ($currentAudit in $serverAudits) {
                $auditName = $currentAudit.Name
                
                $copyAuditStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name         = $auditName
                    Type         = "Server Audit"
                    Status       = $null
                    Notes        = $null
                    DateTime     = [DbaDateTime](Get-Date)
                }
                
                if ($Audit -and $auditName -notin $Audit -or $auditName -in $ExcludeAudit) {
                    continue
                }
                
                $sql = $currentAudit.Script() | Out-String
                
                if ($destAudits.Name -contains $auditName) {
                    if ($force -eq $false) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Server audit $auditName exists at destination. Use -Force to drop and migrate.")) {
                            $copyAuditStatus.Status = "Skipped"
                            $copyAuditStatus.Notes = "Already exists"
                            Write-Message -Level Verbose -Message "Server audit $auditName exists at destination. Use -Force to drop and migrate."
                        }
                        continue
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Dropping server audit $auditName")) {
                            try {
                                Write-Message -Level Verbose -Message "Dropping server audit $auditName."
                                foreach ($spec in $destServer.ServerAuditSpecifications) {
                                    if ($auditSpecification.Auditname -eq $auditName) {
                                        $auditSpecification.Drop()
                                    }
                                }
                                
                                $destServer.audits[$auditName].Disable()
                                $destServer.audits[$auditName].Alter()
                                $destServer.audits[$auditName].Drop()
                            }
                            catch {
                                $copyAuditStatus.Status = "Failed"
                                $copyAuditStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                
                                Stop-Function -Message "Issue dropping audit from destination." -Target $auditName -ErrorRecord $_
                            }
                        }
                    }
                }
                
                if ($null -ne ($currentAudit.Filepath) -and -not (Test-DbaSqlPath -SqlInstance $destServer -Path $currentAudit.Filepath)) {
                    if ($Force -eq $false) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "$($currentAudit.Filepath) does not exist on $destinstance. Skipping $auditName. Specify -Force to create the directory.")) {
                            $copyAuditStatus.Status = "Skipped"
                            $copyAuditStatus.Notes = "$($currentAudit.Filepath) does not exist on $destinstance. Skipping $auditName. Specify -Force to create the directory."
                            $copyAuditStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        }
                        continue
                    }
                    else {
                        Write-Message -Level Verbose -Message "Force specified. Creating directory."
                        
                        $destNetBios = Resolve-NetBiosName $destServer
                        $path = Join-AdminUnc $destNetBios $currentAudit.Filepath
                        $root = $currentAudit.Filepath.Substring(0, 3)
                        $rootUnc = Join-AdminUnc $destNetBios $root
                        
                        if ((Test-Path $rootUnc) -eq $true) {
                            if ($Pscmdlet.ShouldProcess($destinstance, "Creating directory $($currentAudit.Filepath)")) {
                                try {
                                    $null = New-DbaSqlDirectory -SqlInstance $destServer -Path $currentAudit.Filepath -EnableException
                                }
                                catch {
                                    Write-Message -Level Warning -Message "Couldn't create directory $($currentAudit.Filepath). Using default data directory."
                                    $datadir = Get-SqlDefaultPaths $destServer data
                                    $sql = $sql.Replace($currentAudit.FilePath, $datadir)
                                }
                            }
                        }
                        else {
                            $datadir = Get-SqlDefaultPaths $destServer data
                            $sql = $sql.Replace($currentAudit.FilePath, $datadir)
                        }
                    }
                }
                if ($Pscmdlet.ShouldProcess($destinstance, "Creating server audit $auditName")) {
                    try {
                        Write-Message -Level Verbose -Message "File path $($currentAudit.Filepath) exists on $destinstance."
                        Write-Message -Level Verbose -Message "Copying server audit $auditName."
                        $destServer.Query($sql)
                        
                        $copyAuditStatus.Status = "Successful"
                        $copyAuditStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copyAuditStatus.Status = "Failed"
                        $copyAuditStatus.Notes = (Get-ErrorMessage -Record $_)
                        $copyAuditStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Stop-Function -Message "Issue creating audit." -Target $auditName -ErrorRecord $_
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlAudit
    }
}
tools\dbatools\functions\Copy-DbaServerAuditSpecification.ps1
function Copy-DbaServerAuditSpecification {
    <#
        .SYNOPSIS
            Copy-DbaServerAuditSpecification migrates server audit specifications from one SQL Server to another.

        .DESCRIPTION
            By default, all audits are copied. The -AuditSpecification parameter is auto-populated for command-line completion and can be used to copy only specific audits.

            If the audit specification already exists on the destination, it will be skipped unless -Force is used.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER AuditSpecification
            The Server Audit Specification(s) to process. Options for this list are auto-populated from the server. If unspecified, all Server Audit Specifications will be processed.

        .PARAMETER ExcludeAuditSpecification
            The Server Audit Specification(s) to exclude. Options for this list are auto-populated from the server

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER Force
            If this switch is enabled, the Audits Specifications will be dropped and recreated on Destination.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration,ServerAudit,AuditSpecification
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaServerAuditSpecification

        .EXAMPLE
            Copy-DbaServerAuditSpecification -Source sqlserver2014a -Destination sqlcluster

            Copies all server audits from sqlserver2014a to sqlcluster using Windows credentials to connect. If audits with the same name exist on sqlcluster, they will be skipped.

        .EXAMPLE
            Copy-DbaServerAuditSpecification -Source sqlserver2014a -Destination sqlcluster -ServerAuditSpecification tg_noDbDrop -SourceSqlCredential $cred -Force

            Copies a single audit, the tg_noDbDrop audit from sqlserver2014a to sqlcluster using SQL credentials to connect to sqlserver2014a and Windows credentials to connect to sqlcluster. If an audit specification with the same name exists on sqlcluster, it will be dropped and recreated because -Force was used.

        .EXAMPLE
            Copy-DbaServerAuditSpecification -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force

            Shows what would happen if the command were executed using force.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]$SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [object[]]$AuditSpecification,
        [object[]]$ExcludeAuditSpecification,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 10
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        
        if (!(Test-SqlSa -SqlInstance $sourceServer -SqlCredential $SourceSqlCredential)) {
            Stop-Function -Message "Not a sysadmin on $source. Quitting."
            return
        }
        
        $AuditSpecifications = $sourceServer.ServerAuditSpecifications
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            
            if (!(Test-SqlSa -SqlInstance $destServer -SqlCredential $DestinationSqlCredential)) {
                Stop-Function -Message "Not a sysadmin on $destinstance. Quitting."
                return
            }
            
            if ($destServer.VersionMajor -lt $sourceServer.VersionMajor) {
                Stop-Function -Message "Migration from version $($destServer.VersionMajor) to version $($sourceServer.VersionMajor) is not supported."
                return
            }
            $destAudits = $destServer.ServerAuditSpecifications
            foreach ($auditSpec in $AuditSpecifications) {
                $auditSpecName = $auditSpec.Name
                
                $copyAuditSpecStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Type         = "Server Audit Specification"
                    Name         = $auditSpecName
                    Status       = $null
                    Notes        = $null
                    DateTime     = [DbaDateTime](Get-Date)
                }
                
                if ($AuditSpecification -and $auditSpecName -notin $AuditSpecification -or $auditSpecName -in $ExcludeAuditSpecification) {
                    continue
                }
                
                $destServer.Audits.Refresh()
                if ($destServer.Audits.Name -notcontains $auditSpec.AuditName) {
                    if ($Pscmdlet.ShouldProcess($destinstance, "Audit $($auditSpec.AuditName) does not exist on $destinstance. Skipping $auditSpecName.")) {
                        $copyAuditSpecStatus.Status = "Skipped"
                        $copyAuditSpecStatus.Notes = "Audit $($auditSpec.AuditName) does not exist on $destinstance. Skipping $auditSpecName."
                        Write-Message -Level Warning -Message "Audit $($auditSpec.AuditName) does not exist on $destinstance. Skipping $auditSpecName."
                        $copyAuditSpecStatus
                    }
                    continue
                }
                
                if ($destAudits.name -contains $auditSpecName) {
                    if ($force -eq $false) {
                        Write-Message -Level Verbose -Message "Server audit $auditSpecName exists at destination. Use -Force to drop and migrate."
                        
                        $copyAuditSpecStatus.Status = "Skipped"
                        $copyAuditSpecStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        continue
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Dropping server audit $auditSpecName and recreating")) {
                            try {
                                Write-Message -Level Verbose -Message "Dropping server audit $auditSpecName"
                                $destServer.ServerAuditSpecifications[$auditSpecName].Drop()
                            }
                            catch {
                                $copyAuditSpecStatus.Status = "Failed"
                                $copyAuditSpecStatus.Notes = (Get-ErrorMessage -Record $_)
                                $copyAuditSpecStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                
                                Stop-Function -Message "Issue dropping audit spec" -Target $auditSpecName -ErrorRecord $_ -Continue
                            }
                        }
                    }
                }
                if ($Pscmdlet.ShouldProcess($destinstance, "Creating server audit $auditSpecName")) {
                    try {
                        Write-Message -Level Verbose -Message "Copying server audit $auditSpecName"
                        $sql = $auditSpec.Script() | Out-String
                        Write-Message -Level Debug -Message $sql
                        $destServer.Query($sql)
                        
                        $copyAuditSpecStatus.Status = "Successful"
                        $copyAuditSpecStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copyAuditSpecStatus.Status = "Failed"
                        $copyAuditSpecStatus.Notes = (Get-ErrorMessage -Record $_)
                        $copyAuditSpecStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Stop-Function -Message "Issue creating audit spec on destination" -Target $auditSpecName -ErrorRecord $_
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlAuditSpecification
    }
}
tools\dbatools\functions\Copy-DbaServerTrigger.ps1
function Copy-DbaServerTrigger {
    <#
        .SYNOPSIS
            Copy-DbaServerTrigger migrates server triggers from one SQL Server to another.

        .DESCRIPTION
            By default, all triggers are copied. The -ServerTrigger parameter is auto-populated for command-line completion and can be used to copy only specific triggers.

            If the trigger already exists on the destination, it will be skipped unless -Force is used.

        .PARAMETER Source
            Source SQL Server.You must have sysadmin access and server version must be SQL Server version 2000 or greater.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination Sql Server. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER ServerTrigger
            The Server Trigger(s) to process - this list is auto-populated from the server. If unspecified, all Server Triggers will be processed.

        .PARAMETER ExcludeServerTrigger
            The Server Trigger(s) to exclude - this list is auto-populated from the server

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER Force
            Drops and recreates the Trigger if it exists

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaServerTrigger

        .EXAMPLE
            Copy-DbaServerTrigger -Source sqlserver2014a -Destination sqlcluster

            Copies all server triggers from sqlserver2014a to sqlcluster, using Windows credentials. If triggers with the same name exist on sqlcluster, they will be skipped.

        .EXAMPLE
            Copy-DbaServerTrigger -Source sqlserver2014a -Destination sqlcluster -ServerTrigger tg_noDbDrop -SourceSqlCredential $cred -Force

            Copies a single trigger, the tg_noDbDrop trigger from sqlserver2014a to sqlcluster, using SQL credentials for sqlserver2014a and Windows credentials for sqlcluster. If a trigger with the same name exists on sqlcluster, it will be dropped and recreated because -Force was used.

        .EXAMPLE
            Copy-DbaServerTrigger -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force

            Shows what would happen if the command were executed using force.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]
        $SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]
        $DestinationSqlCredential,
        [object[]]$ServerTrigger,
        [object[]]$ExcludeServerTrigger,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 9
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        $serverTriggers = $sourceServer.Triggers
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            if ($destServer.VersionMajor -lt $sourceServer.VersionMajor) {
                Stop-Function -Message "Migration from version $($destServer.VersionMajor) to version $($sourceServer.VersionMajor) is not supported."
                return
            }
            $destTriggers = $destServer.Triggers
            
            foreach ($trigger in $serverTriggers) {
                $triggerName = $trigger.Name
                
                $copyTriggerStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name         = $triggerName
                    Type         = "Server Trigger"
                    Status       = $null
                    Notes        = $null
                    DateTime     = [DbaDateTime](Get-Date)
                }
                
                if ($ServerTrigger -and $triggerName -notin $ServerTrigger -or $triggerName -in $ExcludeServerTrigger) {
                    continue
                }
                
                if ($destTriggers.Name -contains $triggerName) {
                    if ($force -eq $false) {
                        Write-Message -Level Verbose -Message "Server trigger $triggerName exists at destination. Use -Force to drop and migrate."
                        
                        $copyTriggerStatus.Status = "Skipped"
                        $copyTriggerStatus.Status = "Already exists"
                        $copyTriggerStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        continue
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Dropping server trigger $triggerName and recreating")) {
                            try {
                                Write-Message -Level Verbose -Message "Dropping server trigger $triggerName"
                                $destServer.Triggers[$triggerName].Drop()
                            }
                            catch {
                                $copyTriggerStatus.Status = "Failed"
                                $copyTriggerStatus.Notes = (Get-ErrorMessage -Record $_)
                                $copyTriggerStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                
                                Stop-Function -Message "Issue dropping trigger on destination" -Target $triggerName -ErrorRecord $_ -Continue
                            }
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Creating server trigger $triggerName")) {
                    try {
                        Write-Message -Level Verbose -Message "Copying server trigger $triggerName"
                        $sql = $trigger.Script() | Out-String
                        $sql = $sql -replace "CREATE TRIGGER", "`nGO`nCREATE TRIGGER"
                        $sql = $sql -replace "ENABLE TRIGGER", "`nGO`nENABLE TRIGGER"
                        Write-Message -Level Debug -Message $sql
                        
                        foreach ($query in ($sql -split '\nGO\b')) {
                            $destServer.Query($query) | Out-Null
                        }
                        
                        $copyTriggerStatus.Status = "Successful"
                        $copyTriggerStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copyTriggerStatus.Status = "Failed"
                        $copyTriggerStatus.Notes = (Get-ErrorMessage -Record $_)
                        $copyTriggerStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Stop-Function -Message "Issue creating trigger on destination" -Target $triggerName -ErrorRecord $_
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlServerTrigger
    }
}
tools\dbatools\functions\Copy-DbaSpConfigure.ps1
function Copy-DbaSpConfigure {
    <#
        .SYNOPSIS
            Copy-DbaSpConfigure migrates configuration values from one SQL Server to another.

        .DESCRIPTION
            By default, all configuration values are copied. The -ConfigName parameter is auto-populated for command-line completion and can be used to copy only specific configs.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER ConfigName
            Specifies the configuration setting to process. Options for this list are auto-populated from the server. If unspecified, all ConfigNames will be processed.

        .PARAMETER ExcludeConfigName
            Specifies the configuration settings to exclude. Options for this list are auto-populated from the server.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration, Configure, SpConfigure
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaSpConfigure

        .EXAMPLE
            Copy-DbaSpConfigure -Source sqlserver2014a -Destination sqlcluster

            Copies all sp_configure settings from sqlserver2014a to sqlcluster

        .EXAMPLE
            Copy-DbaSpConfigure -Source sqlserver2014a -Destination sqlcluster -ConfigName DefaultBackupCompression, IsSqlClrEnabled -SourceSqlCredential $cred -Force

            Copies the values for IsSqlClrEnabled and DefaultBackupCompression from sqlserver2014a to sqlcluster using SQL credentials to authenticate to sqlserver2014a and Windows credentials to authenticate to sqlcluster.

        .EXAMPLE
            Copy-DbaSpConfigure -Source sqlserver2014a -Destination sqlcluster -ExcludeConfigName DefaultBackupCompression, IsSqlClrEnabled

            Copies all configs except for IsSqlClrEnabled and DefaultBackupCompression, from sqlserver2014a to sqlcluster.

        .EXAMPLE
            Copy-DbaSpConfigure -Source sqlserver2014a -Destination sqlcluster -WhatIf

            Shows what would happen if the command were executed.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]$SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [object[]]$ConfigName,
        [object[]]$ExcludeConfigName,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
            $sourceProps = Get-DbaSpConfigure -SqlInstance $sourceServer
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential
                $destProps = Get-DbaSpConfigure -SqlInstance $destServer
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            
            foreach ($sourceProp in $sourceProps) {
                $displayName = $sourceProp.DisplayName
                $sConfigName = $sourceProp.ConfigName
                $sConfiguredValue = $sourceProp.ConfiguredValue
                $requiresRestart = $sourceProp.IsDynamic
                
                $copySpConfigStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name         = $sConfigName
                    Type         = "Configuration Value"
                    Status       = $null
                    Notes        = $null
                    DateTime     = [DbaDateTime](Get-Date)
                }
                
                if ($ConfigName -and $sConfigName -notin $ConfigName -or $sConfigName -in $ExcludeConfigName) {
                    continue
                }
                
                $destProp = $destProps | Where-Object ConfigName -eq $sConfigName
                if (!$destProp) {
                    Write-Message -Level Verbose -Message "Configuration $sConfigName ('$displayName') does not exist on the destination instance."
                    
                    $copySpConfigStatus.Status = "Skipped"
                    $copySpConfigStatus.Notes = "Configuration does not exist on destination"
                    $copySpConfigStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    
                    continue
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Updating $sConfigName [$displayName]")) {
                    try {
                        $destOldConfigValue = $destProp.ConfiguredValue
                        
                        if ($sConfiguredValue -ne $destOldConfigValue) {
                            $result = Set-DbaSpConfigure -SqlInstance $destServer -Name $sConfigName -Value $sConfiguredValue -EnableException -WarningAction SilentlyContinue
                            if ($result) {
                                Write-Message -Level Verbose -Message "Updated $($destProp.ConfigName) ($($destProp.DisplayName)) from $destOldConfigValue to $sConfiguredValue."
                            }
                        }
                        if ($requiresRestart -eq $false) {
                            Write-Message -Level Verbose -Message "Configuration option $sConfigName ($displayName) requires restart."
                            $copySpConfigStatus.Notes = "Requires restart"
                        }
                        $copySpConfigStatus.Status = "Successful"
                        $copySpConfigStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        if ($_.Exception -match 'the same as the') {
                            $copySpConfigStatus.Status = "Successful"
                        }
                        else {
                            $copySpConfigStatus.Status = "Failed"
                            $copySpConfigStatus.Notes = (Get-ErrorMessage -Record $_)
                        }
                        $copySpConfigStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Stop-Function -Message "Could not set $($destProp.ConfigName) to $sConfiguredValue." -Target $sConfigName -ErrorRecord $_
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlSpConfigure
    }
}
tools\dbatools\functions\Copy-DbaSqlDataCollector.ps1
function Copy-DbaSqlDataCollector {
    <#
        .SYNOPSIS
            Migrates user SQL Data Collector collection sets. SQL Data Collector configuration is on the agenda, but it's hard.

        .DESCRIPTION
            By default, all data collector objects are migrated. If the object already exists on the destination, it will be skipped unless -Force is used.

            The -CollectionSet parameter is auto-populated for command-line completion and can be used to copy only specific objects.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination Sql Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER CollectionSet
            The collection set(s) to process - this list is auto-populated from the server. If unspecified, all collection sets will be processed.

        .PARAMETER ExcludeCollectionSet
            The collection set(s) to exclude - this list is auto-populated from the server

        .PARAMETER NoServerReconfig
            Upcoming parameter to enable server reconfiguration

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER Force
            If collection sets exists on destination server, it will be dropped and recreated.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration,DataCollection
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaSqlDataCollector

        .EXAMPLE
            Copy-DbaSqlDataCollector -Source sqlserver2014a -Destination sqlcluster

            Copies all Data Collector Objects and Configurations from sqlserver2014a to sqlcluster, using Windows credentials.

        .EXAMPLE
            Copy-DbaSqlDataCollector -Source sqlserver2014a -Destination sqlcluster -SourceSqlCredential $cred

            Copies all Data Collector Objects and Configurations from sqlserver2014a to sqlcluster, using SQL credentials for sqlserver2014a and Windows credentials for sqlcluster.

        .EXAMPLE
            Copy-DbaSqlDataCollector -Source sqlserver2014a -Destination sqlcluster -WhatIf

            Shows what would happen if the command were executed.

        .EXAMPLE
            Copy-DbaSqlDataCollector -Source sqlserver2014a -Destination sqlcluster -CollectionSet 'Server Activity', 'Table Usage Analysis'

            Copies two Collection Sets, Server Activity and Table Usage Analysis, from sqlserver2014a to sqlcluster.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]
        $SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]
        $DestinationSqlCredential,
        [object[]]$CollectionSet,
        [object[]]$ExcludeCollectionSet,
        [switch]$NoServerReconfig,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 10
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        $sourceSqlConn = $sourceServer.ConnectionContext.SqlConnectionObject
        $sourceSqlStoreConnection = New-Object Microsoft.SqlServer.Management.Sdk.Sfc.SqlStoreConnection $sourceSqlConn
        $sourceStore = New-Object Microsoft.SqlServer.Management.Collector.CollectorConfigStore $sourceSqlStoreConnection
        $configDb = $sourceStore.ScriptAlter().GetScript() | Out-String
        $configDb = $configDb -replace [Regex]::Escape("'$source'"), "'$destReplace'"
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            if ($NoServerReconfig -eq $false) {
                if ($Pscmdlet.ShouldProcess($destinstance, "Server reconfiguration not yet supported. Only Collection Set migration will be migrated at this time.")) {
                    Write-Message -Level Verbose -Message "Server reconfiguration not yet supported. Only Collection Set migration will be migrated at this time."
                    $NoServerReconfig = $true
                    
            <# for future use when this support is added #>
                    $copyServerConfigStatus = [pscustomobject]@{
                        SourceServer = $sourceServer.Name
                        DestinationServer = $destServer.Name
                        Name         = $userName
                        Type         = "Data Collection Server Config"
                        Status       = "Skipped"
                        Notes        = "Not supported at this time"
                        DateTime     = [DbaDateTime](Get-Date)
                    }
                    $copyServerConfigStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                }
            }
            $destSqlConn = $destServer.ConnectionContext.SqlConnectionObject
            $destSqlStoreConnection = New-Object Microsoft.SqlServer.Management.Sdk.Sfc.SqlStoreConnection $destSqlConn
            $destStore = New-Object Microsoft.SqlServer.Management.Collector.CollectorConfigStore $destSqlStoreConnection
            
            if (!$NoServerReconfig) {
                if ($Pscmdlet.ShouldProcess($destinstance, "Attempting to modify Data Collector configuration")) {
                    try {
                        $sql = "Unknown at this time"
                        $destServer.Query($sql)
                        $destStore.Alter()
                    }
                    catch {
                        $copyServerConfigStatus.Status = "Failed"
                        $copyServerConfigStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        Stop-Function -Message "Issue modifying Data Collector configuration" -Target $destServer -ErrorRecord $_
                    }
                }
            }
            
            if ($destStore.Enabled -eq $false) {
                Write-Message -Level Verbose -Message "The Data Collector must be setup initially for Collection Sets to be migrated. Setup the Data Collector and try again."
                continue
            }
            
            $storeCollectionSets = $sourceStore.CollectionSets | Where-Object { $_.IsSystem -eq $false }
            if ($CollectionSet) {
                $storeCollectionSets = $storeCollectionSets | Where-Object Name -In $CollectionSet
            }
            if ($ExcludeCollectionSet) {
                $storeCollectionSets = $storeCollectionSets | Where-Object Name -NotIn $ExcludeCollectionSet
            }
            
            Write-Message -Level Verbose -Message "Migrating collection sets"
            foreach ($set in $storeCollectionSets) {
                $collectionName = $set.Name
                
                $copyCollectionSetStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name         = $collectionName
                    Type         = "Collection Set"
                    Status       = $null
                    Notes        = $null
                    DateTime     = [DbaDateTime](Get-Date)
                }
                
                if ($null -ne $destStore.CollectionSets[$collectionName]) {
                    if ($force -eq $false) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Collection Set '$collectionName' was skipped because it already exists on $destinstance. Use -Force to drop and recreate")) {
                            Write-Message -Level Verbose -Message "Collection Set '$collectionName' was skipped because it already exists on $destinstance. Use -Force to drop and recreate"
                            
                            $copyCollectionSetStatus.Status = "Skipped"
                            $copyCollectionSetStatus.Notes = "Already exists"
                            $copyCollectionSetStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        }
                        continue
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Attempting to drop $collectionName")) {
                            Write-Message -Level Verbose -Message "Collection Set '$collectionName' exists on $destinstance"
                            Write-Message -Level Verbose -Message "Force specified. Dropping $collectionName."
                            
                            try {
                                $destStore.CollectionSets[$collectionName].Drop()
                            }
                            catch {
                                $copyCollectionSetStatus.Status = "Failed to drop on destination"
                                $copyCollectionSetStatus.Notes = (Get-ErrorMessage -Record $_)
                                $copyCollectionSetStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                Stop-Function -Message "Issue dropping collection" -Target $collectionName -ErrorRecord $_ -Continue
                            }
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Migrating collection set $collectionName")) {
                    try {
                        $sql = $set.ScriptCreate().GetScript() | Out-String
                        $sql = $sql -replace [Regex]::Escape("'$source'"), "'$destinstance'"
                        Write-Message -Level Debug -Message $sql
                        Write-Message -Level Verbose -Message "Migrating collection set $collectionName"
                        $destServer.Query($sql)
                        
                        $copyCollectionSetStatus.Status = "Successful"
                        $copyCollectionSetStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copyCollectionSetStatus.Status = "Failed to create collection"
                        $copyCollectionSetStatus.Notes = (Get-ErrorMessage -Record $_)
                        
                        Stop-Function -Message "Issue creating collection set" -Target $collectionName -ErrorRecord $_
                    }
                    
                    try {
                        if ($set.IsRunning) {
                            Write-Message -Level Verbose -Message "Starting collection set $collectionName"
                            $destStore.CollectionSets.Refresh()
                            $destStore.CollectionSets[$collectionName].Start()
                        }
                        
                        $copyCollectionSetStatus.Status = "Successful started Collection"
                        $copyCollectionSetStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copyCollectionSetStatus.Status = "Failed to start collection"
                        $copyCollectionSetStatus.Notes = (Get-ErrorMessage -Record $_)
                        $copyCollectionSetStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Stop-Function -Message "Issue starting collection set" -Target $collectionName -ErrorRecord $_
                    }
                }
            }
        }
    }
    end {
        if (Test-FunctionInterrupt) { return }
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlDataCollector
    }
}
tools\dbatools\functions\Copy-DbaSqlPolicyManagement.ps1
function Copy-DbaSqlPolicyManagement {
    <#
        .SYNOPSIS
            Migrates SQL Policy Based Management Objects, including both policies and conditions.

        .DESCRIPTION
            By default, all policies and conditions are copied. If an object already exist on the destination, it will be skipped unless -Force is used.

            The -Policy and -Condition parameters are auto-populated for command-line completion and can be used to copy only specific objects.

        .PARAMETER Source
            Source SQL Server.You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination Sql Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Policy
            The policy(ies) to process - this list is auto-populated from the server. If unspecified, all policies will be processed.

        .PARAMETER ExcludePolicy
            The policy(ies) to exclude - this list is auto-populated from the server

        .PARAMETER Condition
            The condition(s) to process - this list is auto-populated from the server. If unspecified, all conditions will be processed.

        .PARAMETER ExcludeCondition
            The condition(s) to exclude - this list is auto-populated from the server

        .PARAMETER Force
            If policies exists on destination server, it will be dropped and recreated.

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaSqlPolicyManagement

        .EXAMPLE
            Copy-DbaSqlPolicyManagement -Source sqlserver2014a -Destination sqlcluster

            Copies all policies and conditions from sqlserver2014a to sqlcluster, using Windows credentials.

        .EXAMPLE
            Copy-DbaSqlPolicyManagement -Source sqlserver2014a -Destination sqlcluster -SourceSqlCredential $cred

            Copies all policies and conditions from sqlserver2014a to sqlcluster, using SQL credentials for sqlserver2014a and Windows credentials for sqlcluster.

        .EXAMPLE
            Copy-DbaSqlPolicyManagement -Source sqlserver2014a -Destination sqlcluster -WhatIf

            Shows what would happen if the command were executed.

        .EXAMPLE
            Copy-DbaSqlPolicyManagement -Source sqlserver2014a -Destination sqlcluster -Policy 'xp_cmdshell must be disabled'

            Copies only one policy, 'xp_cmdshell must be disabled' from sqlserver2014a to sqlcluster. No conditions are migrated.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]
        $SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]
        $DestinationSqlCredential,
        [object[]]$Policy,
        [object[]]$ExcludePolicy,
        [object[]]$Condition,
        [object[]]$ExcludeCondition,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 10
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        $sourceSqlConn = $sourceServer.ConnectionContext.SqlConnectionObject
        $sourceSqlStoreConnection = New-Object Microsoft.SqlServer.Management.Sdk.Sfc.SqlStoreConnection $sourceSqlConn
        $sourceStore = New-Object  Microsoft.SqlServer.Management.DMF.PolicyStore $sourceSqlStoreConnection
        $storePolicies = $sourceStore.Policies | Where-Object { $_.IsSystemObject -eq $false }
        $storeConditions = $sourceStore.Conditions | Where-Object { $_.IsSystemObject -eq $false }
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            $destSqlConn = $destServer.ConnectionContext.SqlConnectionObject
            $destSqlStoreConnection = New-Object Microsoft.SqlServer.Management.Sdk.Sfc.SqlStoreConnection $destSqlConn
            $destStore = New-Object  Microsoft.SqlServer.Management.DMF.PolicyStore $destSqlStoreConnection
            
            if ($Policy) {
                $storePolicies = $storePolicies | Where-Object Name -In $Policy
            }
            if ($ExcludePolicy) {
                $storePolicies = $storePolicies | Where-Object Name -NotIn $ExcludePolicy
            }
            if ($Condition) {
                $storeConditions = $storeConditions | Where-Object Name -In $Condition
            }
            if ($ExcludeCondition) {
                $storeConditions = $storeConditions | Where-Object Name -NotIn $ExcludeCondition
            }
            
            if ($Policy -and $Condition) {
                $storeConditions = $null
                $storePolicies = $null
            }
            
        <#
                        Conditions
        #>
            
            Write-Message -Level Verbose -Message "Migrating conditions"
            foreach ($condition in $storeConditions) {
                $conditionName = $condition.Name
                
                $copyConditionStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name         = $conditionName
                    Type         = "Policy Condition"
                    Status       = $null
                    Notes        = $null
                    DateTime     = [DbaDateTime](Get-Date)
                }
                
                if ($null -ne $destStore.Conditions[$conditionName]) {
                    if ($force -eq $false) {
                        Write-Message -Level Verbose -Message "condition '$conditionName' was skipped because it already exists on $destinstance. Use -Force to drop and recreate"
                        
                        $copyConditionStatus.Status = "Skipped"
                        $copyConditionStatus.Notes = "Already exists"
                        $copyConditionStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        continue
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Attempting to drop $conditionName")) {
                            Write-Message -Level Verbose -Message "Condition '$conditionName' exists on $destinstance. Force specified. Dropping $conditionName."
                            
                            try {
                                $dependentPolicies = $destStore.Conditions[$conditionName].EnumDependentPolicies()
                                foreach ($dependent in $dependentPolicies) {
                                    $dependent.Drop()
                                    $destStore.Conditions.Refresh()
                                }
                                $destStore.Conditions[$conditionName].Drop()
                            }
                            catch {
                                $copyConditionStatus.Status = "Failed"
                                $copyConditionStatus.Notes = (Get-ErrorMessage -Record $_).Message
                                $copyConditionStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                Stop-Function -Message "Issue dropping condition on $destinstance" -Target $conditionName -ErrorRecord $_ -Continue
                            }
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Migrating condition $conditionName")) {
                    try {
                        $sql = $condition.ScriptCreate().GetScript() | Out-String
                        Write-Message -Level Debug -Message $sql
                        Write-Message -Level Verbose -Message "Copying condition $conditionName"
                        $null = $destServer.Query($sql)
                        $destStore.Conditions.Refresh()
                        
                        $copyConditionStatus.Status = "Successful"
                        $copyConditionStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copyConditionStatus.Status = "Failed"
                        $copyConditionStatus.Notes = (Get-ErrorMessage -Record $_).Message
                        $copyConditionStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        Stop-Function -Message "Issue creating condition on $destinstance" -Target $conditionName -ErrorRecord $_
                    }
                }
            }
            
        <#
                        Policies
        #>
            
            Write-Message -Level Verbose -Message "Migrating policies"
            foreach ($policy in $storePolicies) {
                $policyName = $policy.Name
                
                $copyPolicyStatus = [pscustomobject]@{
                    SourceServer = $sourceServer.Name
                    DestinationServer = $destServer.Name
                    Name         = $policyName
                    Type         = "Policy"
                    Status       = $null
                    Notes        = $null
                    DateTime     = [DbaDateTime](Get-Date)
                }
                
                if ($null -ne $destStore.Policies[$policyName]) {
                    if ($force -eq $false) {
                        Write-Message -Level Verbose -Message "Policy '$policyName' was skipped because it already exists on $destinstance. Use -Force to drop and recreate"
                        
                        $copyPolicyStatus.Status = "Skipped"
                        $copyPolicyStatus.Notes = "Already exists"
                        $copyPolicyStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        continue
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Attempting to drop $policyName")) {
                            Write-Message -Level Verbose -Message "Policy '$policyName' exists on $destinstance. Force specified. Dropping $policyName."
                            
                            try {
                                $destStore.Policies[$policyName].Drop()
                                $destStore.Policies.refresh()
                            }
                            catch {
                                $copyPolicyStatus.Status = "Failed"
                                $copyPolicyStatus.Notes = (Get-ErrorMessage -Record $_).Message
                                $copyPolicyStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                                
                                Stop-Function -Message "Issue dropping policy on $destinstance" -Target $policyName -ErrorRecord $_ -Continue
                            }
                        }
                    }
                }
                
                if ($Pscmdlet.ShouldProcess($destinstance, "Migrating policy $policyName")) {
                    try {
                        $destStore.Conditions.Refresh()
                        $destStore.Policies.Refresh()
                        $sql = $policy.ScriptCreateWithDependencies().GetScript() | Out-String
                        Write-Message -Level Debug -Message $sql
                        Write-Message -Level Verbose -Message "Copying policy $policyName"
                        $null = $destServer.Query($sql)
                        
                        $copyPolicyStatus.Status = "Successful"
                        $copyPolicyStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    catch {
                        $copyPolicyStatus.Status = "Failed"
                        $copyPolicyStatus.Notes = (Get-ErrorMessage -Record $_).Message
                        $copyPolicyStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                        
                        # This is usually because of a duplicate dependent from above. Just skip for now.
                        Stop-Function -Message "Issue creating policy on $destinstance" -Target $policyName -ErrorRecord $_ -Continue
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlPolicyManagement
    }
}
tools\dbatools\functions\Copy-DbaSqlServerAgent.ps1
function Copy-DbaSqlServerAgent {
    <#
        .SYNOPSIS
            Copy SQL Server Agent from one server to another.

        .DESCRIPTION
            A wrapper function that calls the associated Copy command for each of the object types seen in SSMS under SQL Server Agent. This also copies all of the the SQL Agent properties (job history max rows, DBMail profile name, etc.).

            You must have sysadmin access and server version must be SQL Server version 2000 or greater.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER DisableJobsOnDestination
            If this switch is enabled, the jobs will be disabled on Destination after copying.

        .PARAMETER DisableJobsOnSource
            If this switch is enabled, the jobs will be disabled on Source after copying.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER Force
            If this switch is enabled, existing objects on Destination with matching names from Source will be dropped, then copied.

        .NOTES
            Tags: Migration, SqlServerAgent, SqlAgent
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaSqlServerAgent

        .EXAMPLE
            Copy-DbaSqlServerAgent -Source sqlserver2014a -Destination sqlcluster

            Copies all job server objects from sqlserver2014a to sqlcluster using Windows credentials for authentication. If job objects with the same name exist on sqlcluster, they will be skipped.

        .EXAMPLE
            Copy-DbaSqlServerAgent -Source sqlserver2014a -Destination sqlcluster -SourceSqlCredential $cred

            Copies all job objects from sqlserver2014a to sqlcluster using SQL credentials to authentication to sqlserver2014a and Windows credentials to authenticate to sqlcluster.

        .EXAMPLE
            Copy-DbaSqlServerAgent -Source sqlserver2014a -Destination sqlcluster -WhatIf

            Shows what would happen if the command were executed.
    #>
    [cmdletbinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]$SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [Switch]$DisableJobsOnDestination,
        [Switch]$DisableJobsOnSource,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        Invoke-SmoCheck -SqlInstance $sourceServer
        $sourceAgent = $sourceServer.JobServer
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            Invoke-SmoCheck -SqlInstance $destServer
            # All of these support whatif inside of them
            Copy-DbaAgentCategory -Source $sourceServer -Destination $destServer -Force:$force
            
            $destServer.JobServer.JobCategories.Refresh()
            $destServer.JobServer.OperatorCategories.Refresh()
            $destServer.JobServer.AlertCategories.Refresh()
            
            Copy-DbaAgentOperator -Source $sourceServer -Destination $destServer -Force:$force
            $destServer.JobServer.Operators.Refresh()
            
            Copy-DbaAgentAlert -Source $sourceServer -Destination $destServer -Force:$force -IncludeDefaults
            $destServer.JobServer.Alerts.Refresh()
            
            Copy-DbaAgentProxyAccount -Source $sourceServer -Destination $destServer -Force:$force
            $destServer.JobServer.ProxyAccounts.Refresh()
            
            Copy-DbaAgentSharedSchedule -Source $sourceServer -Destination $destServer -Force:$force
            $destServer.JobServer.SharedSchedules.Refresh()
            
            $destServer.JobServer.Refresh()
            $destServer.Refresh()
            Copy-DbaAgentJob -Source $sourceServer -Destination $destServer -Force:$force -DisableOnDestination:$DisableJobsOnDestination -DisableOnSource:$DisableJobsOnSource
            
            # To do
        <#
            Copy-DbaAgentMasterServer -Source $sourceServer -Destination $destServer -Force:$force
            Copy-DbaAgentTargetServer -Source $sourceServer -Destination $destServer -Force:$force
            Copy-DbaAgentTargetServerGroup -Source $sourceServer -Destination $destServer -Force:$force
        #>
            
        <# Here are the properties which must be migrated separately #>
            $copyAgentPropStatus = [pscustomobject]@{
                SourceServer = $sourceServer.Name
                DestinationServer = $destServer.Name
                Name         = "Server level properties"
                Type         = "Agent Properties"
                Status       = $null
                Notes        = $null
                DateTime     = [DbaDateTime](Get-Date)
            }
            
            if ($Pscmdlet.ShouldProcess($destinstance, "Copying Agent Properties")) {
                try {
                    Write-Message -Level Verbose -Message "Copying SQL Agent Properties"
                    $sql = $sourceAgent.Script() | Out-String
                    $sql = $sql -replace [Regex]::Escape("'$source'"), "'$destinstance'"
                    $sql = $sql -replace [Regex]::Escape("@errorlog_file="), [Regex]::Escape("--@errorlog_file=")
                    $sql = $sql -replace [Regex]::Escape("@auto_start="), [Regex]::Escape("--@auto_start=")
                    Write-Message -Level Debug -Message $sql
                    $null = $destServer.Query($sql)
                    
                    $copyAgentPropStatus.Status = "Successful"
                    $copyAgentPropStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                }
                catch {
                    $message = $_.Exception.InnerException.InnerException.InnerException.Message
                    if (-not $message) { $message = $_.Exception.Message }
                    $copyAgentPropStatus.Status = "Failed"
                    $copyAgentPropStatus.Notes = $message
                    $copyAgentPropStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    Stop-Function -Message $message -Target $destinstance
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlServerAgent
    }
}
tools\dbatools\functions\Copy-DbaSsisCatalog.ps1
#ValidationTags#Messaging#
function Copy-DbaSsisCatalog {
    <#
        .SYNOPSIS
           Copy-DbaSsisCatalog migrates Folders, SSIS projects, and environments from one SQL Server to another.

        .DESCRIPTION
            By default, all folders, projects, and environments are copied. The -Project parameter can be specified to copy only one project, if desired.

            The parameters get more granular from the Folder level. For example, specifying -Folder will only deploy projects/environments from within that folder.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2012 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2012 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Force
            If this switch is enabled, the SSIS Catalog will be dropped and recreated on Destination if it already exists.

        .PARAMETER Project
            Specifies a source Project name.

        .PARAMETER Folder
            Specifies a source folder name.

        .PARAMETER Environment
            Specifies an environment to copy.

        .PARAMETER EnableSqlClr
            If this switch is enabled and Destination does not have the SQL CLR configuration option enabled, user prompts for enabling it on Destination will be skipped. SQL CLR is required for SSISDB.

        .PARAMETER CreateCatalogPassword
            Specifies a secure string to use in creating an SSISDB catalog on Destination. If this is specified, prompts for the password will be skipped.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration, SSIS
            Author: Phil Schwartz (philschwartz.me, @pschwartzzz)

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaSsisCatalog

        .EXAMPLE
            Copy-DbaSsisCatalog -Source sqlserver2014a -Destination sqlcluster

            Copies all folders, environments and SSIS Projects from sqlserver2014a to sqlcluster, using Windows credentials to authenticate to both instances. If folders with the same name exist on the destination they will be skipped, but projects will be redeployed.

        .EXAMPLE
            Copy-DbaSsisCatalog -Source sqlserver2014a -Destination sqlcluster -Project Archive_Tables -SourceSqlCredential $cred -Force

            Copies a single Project, the Archive_Tables Project, from sqlserver2014a to sqlcluster using SQL credentials to authenticate to sqlserver2014a and Windows credentials to authenticate to sqlcluster. If a Project with the same name exists on sqlcluster, it will be deleted and recreated because -Force was used.

        .EXAMPLE
            Copy-DbaSsisCatalog -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force

            Shows what would happen if the command were executed using force.

        .EXAMPLE
            $SecurePW = Read-Host "Enter password" -AsSecureString
            Copy-DbaSsisCatalog -Source sqlserver2014a -Destination sqlcluster -CreateCatalogPassword $SecurePW

            Deploy entire SSIS catalog to an instance without a destination catalog. User prompts for creating the catalog on Destination will be bypassed.

    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$SourceSqlCredential,
        [PSCredential]$DestinationSqlCredential,
        [String]$Project,
        [String]$Folder,
        [String]$Environment,
        [System.Security.SecureString]$CreateCatalogPassword,
        [Switch]$EnableSqlClr,
        [Switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    <# Developer note: The throw calls must stay in this command #>
    begin {
        function Get-RemoteIntegrationService {
            param (
                [Object]$Computer
            )
            $result = Get-DbaSqlService -ComputerName $Computer -Type SSIS
            if ($result) {
                $running = $false
                foreach ($service in $result) {
                    if (!$service.State -eq "Running") {
                        Write-Message -Level Warning -Message "Service $($service.DisplayName) was found on the destination, but is currently not running."
                    }
                    else {
                        Write-Message -Level Verbose -Message "Service $($service.DisplayName) was found running on the destination."
                        $running = $true
                    }
                }
            }
            else {
                throw "No Integration Services service was found on the destination, please ensure the feature is installed and running."
            }
        }

        function Invoke-ProjectDeployment {
            param (
                [String]$Project,
                [String]$Folder
            )
            $sqlConn = New-Object System.Data.SqlClient.SqlConnection
            $sqlConn.ConnectionString = $sourceConnection.ConnectionContext.ConnectionString
            if ($sqlConn.State -eq "Closed") {
                $sqlConn.Open()
            }
            try {
                Write-Message -Level Verbose -Message "Deploying project $Project from folder $Folder."
                $cmd = New-Object System.Data.SqlClient.SqlCommand
                $cmd.CommandType = "StoredProcedure"
                $cmd.connection = $sqlConn
                $cmd.CommandText = "SSISDB.Catalog.get_project"
                $cmd.Parameters.Add("@folder_name", $Folder) | out-null;
                $cmd.Parameters.Add("@project_name", $Project) | out-null;
                [byte[]]$results = $cmd.ExecuteScalar();
                if ($null -ne $results) {
                    $destFolder = $destinationFolders | Where-Object { $_.Name -eq $Folder }
                    $deployedProject = $destFolder.DeployProject($Project, $results)
                    if ($deployedProject.Status -ne "Success") {
                        Stop-Function -Message "An error occurred deploying project $Project." -Target $Project -Continue
                    }
                }
                else {
                    Stop-Function -Message "Failed deploying $Project from folder $Folder." -Target $Project -Continue
                }
            }
            catch {
                Stop-Function -Message "Failed to deploy project." -Target $Project -ErrorRecord $_
            }
            finally {
                if ($sqlConn.State -eq "Open") {
                    $sqlConn.Close()
                }
            }
        }
        function New-CatalogFolder {
            param (
                [String]$Folder,
                [String]$Description,
                [Switch]$Force
            )
            if ($Force) {
                $remove = $destinationFolders | Where-Object { $_.Name -eq $Folder }
                $envs = $remove.Environments.Name
                foreach ($e in $envs) {
                    $remove.Environments[$e].Drop()
                }
                $projs = $remove.Projects.Name
                foreach ($p in $projs) {
                    $remove.Projects[$p].Drop()
                }
                $remove.Drop()
                $destinationCatalog.Alter()
                $destinationCatalog.Refresh()
            }
            Write-Message -Level Verbose -Message "Creating folder $Folder."
            $destFolder = New-Object "$ISNamespace.CatalogFolder" ($destinationCatalog, $Folder, $Description)
            $destFolder.Create()
            $destFolder.Alter()
            $destFolder.Refresh()
        }
        function New-FolderEnvironment {
            param (
                [String]$Folder,
                [String]$Environment,
                [Switch]$Force
            )
            $envDestFolder = $destinationFolders | Where-Object { $_.Name -eq $Folder }
            if ($force) {
                $envDestFolder.Environments[$Environment].Drop()
                $envDestFolder.Alter()
                $envDestFolder.Refresh()
            }
            $srcEnv = ($sourceFolders | Where-Object { $_.Name -eq $Folder }).Environments[$Environment]
            $targetEnv = New-Object "$ISNamespace.EnvironmentInfo" ($envDestFolder, $srcEnv.Name, $srcEnv.Description)
            foreach ($var in $srcEnv.Variables) {
                if ($var.Value.ToString() -eq "") {
                    $finalValue = ""
                }
                else {
                    $finalValue = $var.Value
                }
                $targetEnv.Variables.Add($var.Name, $var.Type, $finalValue, $var.Sensitive, $var.Description)
            }
            Write-Message -Level Verbose -Message "Creating environment $Environment."
            $targetEnv.Create()
            $targetEnv.Alter()
            $targetEnv.Refresh()
        }
        function New-SSISDBCatalog {
            param (
                [System.Security.SecureString]$Password
            )

            if (!$Password) {
                Write-Message -Level Verbose -Message "SSISDB Catalog requires a password."
                $pass1 = Read-Host "Enter a password" -AsSecureString
                $plainTextPass1 = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($pass1))
                $pass2 = Read-Host "Re-enter password" -AsSecureString
                $plainTextPass2 = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($pass2))
                if ($plainTextPass1 -ne $plainTextPass2) {
                    throw "Validation error, passwords entered do not match."
                }
                $plainTextPass = $plainTextPass1
            }
            else {
                $plainTextPass = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password))
            }

            $catalog = New-Object "$ISNamespace.Catalog" ($destinationSSIS, "SSISDB", $plainTextPass)
            $catalog.Create()
            $catalog.Refresh()
        }

        $ISNamespace = "Microsoft.SqlServer.Management.IntegrationServices"
        
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 11
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source integration services."
            $sourceSSIS = New-Object "$ISNamespace.IntegrationServices" $sourceConnection
        }
        catch {
            Stop-Function -Message "There was an error connecting to the source integration services." -Target $sourceConnection -ErrorRecord $_
            return
        }
        
        $sourceCatalog = $sourceSSIS.Catalogs | Where-Object { $_.Name -eq "SSISDB" }
        if (!$sourceCatalog) {
            Stop-Function -Message "The source SSISDB catalog on $Source does not exist."
            return
        }
        $sourceFolders = $sourceCatalog.Folders
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destinationConnection = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 1
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            
            try {
                Get-RemoteIntegrationService -Computer $destinstance
            }
            catch {
                Stop-Function -Message "An error occurred when checking the destination for Integration Services. Is Integration Services installed?" -Target $destinstance -ErrorRecord $_
            }
            
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance integration services."
                $destinationSSIS = New-Object "$ISNamespace.IntegrationServices" $destinationConnection
            }
            catch {
                Stop-Function -Message "There was an error connecting to the destination integration services." -Target $destinationCon -ErrorRecord $_
            }
            
            $destinationCatalog = $destinationSSIS.Catalogs | Where-Object { $_.Name -eq "SSISDB" }
            $destinationFolders = $destinationCatalog.Folders
            
            if (!$destinationCatalog) {
                if (!$destinationConnection.Configuration.IsSqlClrEnabled.ConfigValue) {
                    if ($Pscmdlet.ShouldProcess($destinstance, "Enabling SQL CLR configuration option.")) {
                        if (!$EnableSqlClr) {
                            $message = "The destination does not have SQL CLR configuration option enabled (required by SSISDB), would you like to enable it?"
                            $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Enable SQL CLR on $destinstance."
                            $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Exit."
                            $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
                            $result = $host.ui.PromptForChoice($null, $message, $options, 0)
                            switch ($result) {
                                0 {
                                    continue
                                }
                                1 {
                                    return
                                }
                            }
                        }
                        Write-Message -Level Verbose -Message "Enabling SQL CLR configuration option at the destination."
                        if ($destinationConnection.Configuration.ShowAdvancedOptions.ConfigValue -eq $false) {
                            $destinationConnection.Configuration.ShowAdvancedOptions.ConfigValue = $true
                            $changeback = $true
                        }
                        
                        $destinationConnection.Configuration.IsSqlClrEnabled.ConfigValue = $true
                        
                        if ($changeback -eq $true) {
                            $destinationConnection.Configuration.ShowAdvancedOptions.ConfigValue = $false
                        }
                        $destinationConnection.Configuration.Alter()
                    }
                }
                else {
                    Write-Message -Level Verbose -Message "SQL CLR configuration option is already enabled at the destination."
                }
                if ($Pscmdlet.ShouldProcess($destinstance, "Create destination SSISDB Catalog")) {
                    if (!$CreateCatalogPassword) {
                        $message = "The destination SSISDB catalog does not exist, would you like to create one?"
                        $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Create an SSISDB catalog on $destinstance."
                        $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Exit."
                        $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
                        $result = $host.ui.PromptForChoice($null, $message, $options, 0)
                        switch ($result) {
                            0 {
                                New-SSISDBCatalog
                            }
                            1 {
                                return
                            }
                        }
                    }
                    else {
                        New-SSISDBCatalog -Password $CreateCatalogPassword
                    }
                    
                    $destinationSSIS.Refresh()
                    $destinationCatalog = $destinationSSIS.Catalogs | Where-Object { $_.Name -eq "SSISDB" }
                    $destinationFolders = $destinationCatalog.Folders
                }
                else {
                    throw "The destination SSISDB catalog does not exist."
                }
            }
            if ($folder) {
                if ($sourceFolders.Name -contains $folder) {
                    $srcFolder = $sourceFolders | Where-Object { $_.Name -eq $folder }
                    if ($destinationFolders.Name -contains $folder) {
                        if (!$force) {
                            Write-Message -Level Warning -Message "Integration services catalog folder $folder exists at destination. Use -Force to drop and recreate."
                        }
                        else {
                            if ($Pscmdlet.ShouldProcess($destinstance, "Dropping folder $folder and recreating")) {
                                try {
                                    New-CatalogFolder -Folder $srcFolder.Name -Description $srcFolder.Description -Force
                                }
                                catch {
                                    Stop-Function -Message "Issue dropping folder" -Target $folder -ErrorRecord $_
                                }
                                
                            }
                        }
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Creating folder $folder")) {
                            try {
                                New-CatalogFolder -Folder $srcFolder.Name -Description $srcFolder.Description
                            }
                            catch {
                                Stop-Function -Message "Issue creating folder" -Target $folder -ErrorRecord $_
                            }
                        }
                    }
                }
                else {
                    throw "The source folder provided does not exist in the source Integration Services catalog."
                }
            }
            else {
                foreach ($srcFolder in $sourceFolders) {
                    if ($destinationFolders.Name -notcontains $srcFolder.Name) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Creating folder $($srcFolder.Name)")) {
                            try {
                                New-CatalogFolder -Folder $srcFolder.Name -Description $srcFolder.Description
                            }
                            catch {
                                Stop-Function -Message "Issue creating folder" -Target $srcFolder -ErrorRecord $_ -Continue
                            }
                        }
                    }
                    else {
                        if (!$force) {
                            Write-Message -Level Warning -Message "Integration services catalog folder $($srcFolder.Name) exists at destination. Use -Force to drop and recreate."
                            continue
                        }
                        else {
                            if ($Pscmdlet.ShouldProcess($destinstance, "Dropping folder $($srcFolder.Name) and recreating")) {
                                try {
                                    New-CatalogFolder -Folder $srcFolder.Name -Description $srcFolder.Description -Force
                                }
                                catch {
                                    Stop-Function -Message "Issue dropping folder" -Target $srcFolder -ErrorRecord $_
                                }
                            }
                        }
                    }
                }
            }
            
            # Refresh folders for project and environment deployment
            if ($Pscmdlet.ShouldProcess($destinstance, "Refresh folders for project deployment")) {
                try {
                    $destinationFolders.Alter()
                }
                catch {
                    # Sometimes it says Alter() doesn't exist
                }
                $destinationFolders.Refresh()
            }
            
            if ($folder) {
                $sourceFolders = $sourceFolders | Where-Object { $_.Name -eq $folder }
                if (!$sourceFolders) {
                    throw "The source folder $folder does not exist in the source Integration Services catalog."
                }
            }
            if ($project) {
                $folderDeploy = $sourceFolders | Where-Object { $_.Projects.Name -eq $project }
                if (!$folderDeploy) {
                    throw "The project $project cannot be found in the source Integration Services catalog."
                }
                else {
                    foreach ($f in $folderDeploy) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Deploying project $project from folder $($f.Name)")) {
                            try {
                                Invoke-ProjectDeployment -Folder $f.Name -Project $project
                            }
                            catch {
                                Stop-Function -Message "Issue deploying project" -Target $project -ErrorRecord $_
                            }
                        }
                    }
                }
            }
            else {
                foreach ($curFolder in $sourceFolders) {
                    foreach ($proj in $curFolder.Projects) {
                        if ($Pscmdlet.ShouldProcess($destinstance, "Deploying project $($proj.Name) from folder $($curFolder.Name)")) {
                            try {
                                Invoke-ProjectDeployment -Project $proj.Name -Folder $curFolder.Name
                            }
                            catch {
                                Stop-Function -Message "Issue deploying project" -Target $proj -ErrorRecord $_
                            }
                        }
                    }
                }
            }
            
            if ($environment) {
                $folderDeploy = $sourceFolders | Where-Object { $_.Environments.Name -eq $environment }
                if (!$folderDeploy) {
                    throw "The environment $environment cannot be found in the source Integration Services catalog."
                }
                else {
                    foreach ($f in $folderDeploy) {
                        if ($destinationFolders[$f.Name].Environments.Name -notcontains $environment) {
                            if ($Pscmdlet.ShouldProcess($destinstance, "Deploying environment $environment from folder $($f.Name)")) {
                                try {
                                    New-FolderEnvironment -Folder $f.Name -Environment $environment
                                }
                                catch {
                                    Stop-Function -Message "Issue deploying environment" -Target $environment -ErrorRecord $_
                                }
                            }
                        }
                        else {
                            if (!$force) {
                                Write-Message -Level Warning -Message "Integration services catalog environment $environment exists in folder $($f.Name) at destination. Use -Force to drop and recreate."
                            }
                            else {
                                If ($Pscmdlet.ShouldProcess($destinstance, "Dropping existing environment $environment and deploying environment $environment from folder $($f.Name)")) {
                                    try {
                                        New-FolderEnvironment -Folder $f.Name -Environment $environment -Force
                                    }
                                    catch {
                                        Stop-Function -Message "Issue dropping existing environment" -Target $environment -ErrorRecord $_
                                    }
                                }
                            }
                        }
                    }
                }
            }
            else {
                foreach ($curFolder in $sourceFolders) {
                    foreach ($env in $curFolder.Environments) {
                        if ($destinationFolders[$curFolder.Name].Environments.Name -notcontains $env.Name) {
                            if ($Pscmdlet.ShouldProcess($destinstance, "Deploying environment $($env.Name) from folder $($curFolder.Name)")) {
                                try {
                                    New-FolderEnvironment -Environment $env.Name -Folder $curFolder.Name
                                }
                                catch {
                                    Stop-Function -Message "Issue deploying environment" -Target $env -ErrorRecord $_
                                }
                            }
                        }
                        else {
                            if (!$force) {
                                Write-Message -Level Warning -Message "Integration services catalog environment $($env.Name) exists in folder $($curFolder.Name) at destination. Use -Force to drop and recreate."
                                continue
                            }
                            else {
                                if ($Pscmdlet.ShouldProcess($destinstance, "Deploying environment $($env.Name) from folder $($curFolder.Name)")) {
                                    try {
                                        New-FolderEnvironment -Environment $env.Name -Folder $curFolder.Name -Force
                                    }
                                    catch {
                                        Stop-Function -Message "Issue deploying environment" -Target $env -ErrorRecord $_
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlSsisCatalog
    }
}
tools\dbatools\functions\Copy-DbaSysDbUserObject.ps1
function Copy-DbaSysDbUserObject {
    <#
        .SYNOPSIS
            Imports all user objects found in source SQL Server's master, msdb and model databases to the destination.

        .DESCRIPTION
            Imports all user objects found in source SQL Server's master, msdb and model databases to the destination. This is useful because many DBAs store backup/maintenance procs/tables/triggers/etc (among other things) in master or msdb.

            It is also useful for migrating objects within the model database.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Classic
            Perform the migration the old way
    
        .PARAMETER Force
            Drop destination objects first. Has no effect if you use Classic. This doesn't work really well, honestly.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration, SystemDatabase, UserObject

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaSysDbUserObject

        .EXAMPLE
            Copy-DbaSysDbUserObject $sourceServer $destserver

            Copies user objects from source to destination
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [DbaInstanceParameter]$Source,
        [PSCredential]$SourceSqlCredential,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [DbaInstanceParameter[]]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [switch]$Force,
        [switch]$Classic,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        function get-sqltypename ($type) {
            switch ($type) {
                "VIEW" { "view" }
                "SQL_TABLE_VALUED_FUNCTION" { "User table valued fsunction" }
                "DEFAULT_CONSTRAINT" { "User default constraint" }
                "SQL_STORED_PROCEDURE" { "User stored procedure" }
                "RULE" { "User rule" }
                "SQL_INLINE_TABLE_VALUED_FUNCTION" { "User inline table valued function" }
                "SQL_TRIGGER" { "User server trigger" }
                "SQL_SCALAR_FUNCTION" { "User scalar function" }
                default { $type }
            }
        }
    }
    process {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source"
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
            return
        }
        
        if (!(Test-SqlSa -SqlInstance $sourceServer -SqlCredential $SourceSqlCredential)) {
            Stop-Function -Message "Not a sysadmin on $source. Quitting."
            return
        }
        
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $destinstance"
                $destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
            }
            
            if (!(Test-SqlSa -SqlInstance $destServer -SqlCredential $DestinationSqlCredential)) {
                Stop-Function -Message "Not a sysadmin on $destinstance" -Continue
            }
            
            $systemDbs = "master", "model", "msdb"
            
            if (-not $Classic) {
                foreach ($systemDb in $systemDbs) {
                    $smodb = $sourceServer.databases[$systemDb]
                    $destdb = $destserver.databases[$systemDb]
                    
                    $tables = $smodb.Tables | Where-Object IsSystemObject -ne $true
                    $schemas = $smodb.Schemas | Where-Object IsSystemObject -ne $true
                    
                    foreach ($schema in $schemas) {
                        $copyobject = [pscustomobject]@{
                            SourceServer = $sourceServer.Name
                            DestinationServer = $destServer.Name
                            Name         = $schema
                            Type         = "User schema in $systemDb"
                            Status       = $null
                            Notes        = $null
                            DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                        }
                        
                        $destschema = $destdb.Schemas | Where-Object Name -eq $schema.Name
                        $schmadoit = $true
                        
                        if ($destschema) {
                            if (-not $force) {
                                $copyobject.Status = "Skipped"
                                $copyobject.Notes = "$schema exists on destination"
                                $schmadoit = $false
                            }
                            else {
                                if ($PSCmdlet.ShouldProcess($destServer, "Dropping schema $schema in $systemDb")) {
                                    try {
                                        Write-Message -Level Verbose -Message "Force specified. Dropping $schema in $destdb on $destinstance"
                                        $destschema.Drop()
                                    }
                                    catch {
                                        $schmadoit = $false
                                        $copyobject.Status = "Failed"
                                        $copyobject.Notes = $_.Exception.InnerException.InnerException.InnerException.Message
                                    }
                                }
                            }
                        }
                        
                        if ($schmadoit) {
                            $transfer = New-Object Microsoft.SqlServer.Management.Smo.Transfer $smodb
                            $null = $transfer.CopyAllObjects = $false
                            $null = $transfer.Options.WithDependencies = $true
                            $null = $transfer.ObjectList.Add($schema)
                            $sql = $transfer.ScriptTransfer()
                            if ($PSCmdlet.ShouldProcess($destServer, "Attempting to add schema $($schema.Name) to $systemDb")) {
                                try {
                                    Write-Message -Level Debug -Message "$sql"
                                    $null = $destServer.Query($sql, $systemDb)
                                    $copyobject.Status = "Successful"
                                    $copyobject.Notes = "May have also created dependencies"
                                }
                                catch {
                                    $copyobject.Status = "Failed"
                                    $copyobject.Notes = (Get-ErrorMessage -Record $_)
                                }
                            }
                        }
                        
                        $copyobject | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    
                    foreach ($table in $tables) {
                        $copyobject = [pscustomobject]@{
                            SourceServer = $sourceServer.Name
                            DestinationServer = $destServer.Name
                            Name         = $table
                            Type         = "User table in $systemDb"
                            Status       = $null
                            Notes        = $null
                            DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                        }
                        
                        $desttable = $destdb.Tables.Item($table.Name, $table.Schema)
                        $doit = $true
                        
                        if ($desttable) {
                            if (-not $force) {
                                $copyobject.Status = "Skipped"
                                $copyobject.Notes = "$table exists on destination"
                                $doit = $false
                            }
                            else {
                                if ($PSCmdlet.ShouldProcess($destServer, "Dropping table $table in $systemDb")) {
                                    try {
                                        Write-Message -Level Verbose -Message "Force specified. Dropping $table in $destdb on $destinstance"
                                        $desttable.Drop()
                                    }
                                    catch {
                                        $doit = $false
                                        $copyobject.Status = "Failed"
                                        $copyobject.Notes = $_.Exception.InnerException.InnerException.InnerException.Message
                                    }
                                }
                            }
                        }
                        
                        if ($doit) {
                            $transfer = New-Object Microsoft.SqlServer.Management.Smo.Transfer $smodb
                            $null = $transfer.CopyAllObjects = $false
                            $null = $transfer.Options.WithDependencies = $true
                            $null = $transfer.ObjectList.Add($table)
                            $sql = $transfer.ScriptTransfer()
                            if ($PSCmdlet.ShouldProcess($destServer, "Attempting to add table $table to $systemDb")) {
                                try {
                                    Write-Message -Level Debug -Message "$sql"
                                    $null = $destServer.Query($sql, $systemDb)
                                    $copyobject.Status = "Successful"
                                    $copyobject.Notes = "May have also created dependencies"
                                }
                                catch {
                                    $copyobject.Status = "Failed"
                                    $copyobject.Notes = (Get-ErrorMessage -Record $_)
                                }
                            }
                        }
                        $copyobject | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                    
                    $userobjects = Get-DbaSqlModule -SqlInstance $sourceserver -Database $systemDb -NoSystemObjects | Sort-Object Type
                    Write-Message -Level Verbose -Message "Copying from $systemDb"
                    foreach ($userobject in $userobjects) {
                        
                        $name = "[$($userobject.SchemaName)].[$($userobject.Name)]"
                        $db = $userobject.Database
                        $type = get-sqltypename $userobject.Type
                        $sql = $userobject.Definition
                        $schema = $userobject.SchemaName
                        
                        $copyobject = [pscustomobject]@{
                            SourceServer = $sourceServer.Name
                            DestinationServer = $destServer.Name
                            Name         = $name
                            Type         = "$type in $systemDb"
                            Status       = $null
                            Notes        = $null
                            DateTime     = [Sqlcollaborative.Dbatools.Utility.DbaDateTime](Get-Date)
                        }
                        Write-Message -Level Debug -Message $sql
                        try {
                            Write-Message -Level Verbose -Message "Searching for $name in $db on $destinstance"
                            $result = Get-DbaSqlModule -SqlInstance $destServer -NoSystemObjects -Database $db |
                            Where-Object { $psitem.Name -eq $userobject.Name -and $psitem.Type -eq $userobject.Type }
                            if ($result) {
                                Write-Message -Level Verbose -Message "Found $name in $db on $destinstance"
                                if (-not $Force) {
                                    $copyobject.Status = "Skipped"
                                    $copyobject.Notes = "$name exists on destination"
                                }
                                else {
                                    $smobject = switch ($userobject.Type) {
                                        "VIEW" { $smodb.Views.Item($userobject.Name, $userobject.SchemaName) }
                                        "SQL_STORED_PROCEDURE" { $smodb.StoredProcedures.Item($userobject.Name, $userobject.SchemaName) }
                                        "RULE" { $smodb.Rules.Item($userobject.Name, $userobject.SchemaName) }
                                        "SQL_TRIGGER" { $smodb.Triggers.Item($userobject.Name, $userobject.SchemaName) }
                                        "SQL_TABLE_VALUED_FUNCTION" { $smodb.UserDefinedFunctions.Item($name) }
                                        "SQL_INLINE_TABLE_VALUED_FUNCTION" { $smodb.UserDefinedFunctions.Item($name) }
                                        "SQL_SCALAR_FUNCTION" { $smodb.UserDefinedFunctions.Item($name) }
                                    }
                                    
                                    if ($smobject) {
                                        Write-Message -Level Verbose -Message "Force specified. Dropping $smobject on $destdb on $destinstance using SMO"
                                        $transfer = New-Object Microsoft.SqlServer.Management.Smo.Transfer $smodb
                                        $null = $transfer.CopyAllObjects = $false
                                        $null = $transfer.Options.WithDependencies = $true
                                        $null = $transfer.ObjectList.Add($smobject)
                                        $null = $transfer.Options.ScriptDrops = $true
                                        $dropsql = $transfer.ScriptTransfer()
                                        Write-Message -Level Debug -Message "$dropsql"
                                        if ($PSCmdlet.ShouldProcess($destServer, "Attempting to drop $type $name from $systemDb")) {
                                            $null = $destdb.Query("$dropsql")
                                        }
                                    }
                                    else {
                                        if ($PSCmdlet.ShouldProcess($destServer, "Attempting to drop $type $name from $systemDb using T-SQL")) {
                                            $null = $destdb.Query("DROP FUNCTION $($userobject.name)")
                                        }
                                    }
                                    if ($PSCmdlet.ShouldProcess($destServer, "Attempting to add $type $name to $systemDb")) {
                                        $null = $destdb.Query("$sql")
                                        $copyobject.Status = "Successful"
                                    }
                                }
                            }
                            else {
                                if ($PSCmdlet.ShouldProcess($destServer, "Attempting to add $type $name to $systemDb")) {
                                    $null = $destdb.Query("$sql")
                                    $copyobject.Status = "Successful"
                                }
                            }
                        }
                        catch {
                            try {
                                $smobject = switch ($userobject.Type) {
                                    "VIEW" { $smodb.Views.Item($userobject.Name, $userobject.SchemaName) }
                                    "SQL_STORED_PROCEDURE" { $smodb.StoredProcedures.Item($userobject.Name, $userobject.SchemaName) }
                                    "RULE" { $smodb.Rules.Item($userobject.Name, $userobject.SchemaName) }
                                    "SQL_TRIGGER" { $smodb.Triggers.Item($userobject.Name, $userobject.SchemaName) }
                                }
                                if ($smobject) {
                                    $transfer = New-Object Microsoft.SqlServer.Management.Smo.Transfer $smodb
                                    $null = $transfer.CopyAllObjects = $false
                                    $null = $transfer.Options.WithDependencies = $true
                                    $null = $transfer.ObjectList.Add($smobject)
                                    $sql = $transfer.ScriptTransfer()
                                    Write-Message -Level Debug -Message "$sql"
                                    Write-Message -Level Verbose -Message "Adding $smoobject on $destdb on $destinstance"
                                    if ($PSCmdlet.ShouldProcess($destServer, "Attempting to add $type $name to $systemDb")) {
                                        $null = $destdb.Query("$sql")
                                    }
                                    $copyobject.Status = "Successful"
                                    $copyobject.Notes = "May have also installed dependencies"
                                }
                                else {
                                    $copyobject.Status = "Failed"
                                    $copyobject.Notes = (Get-ErrorMessage -Record $_)
                                }
                            }
                            catch {
                                $copyobject.Status = "Failed"
                                $copyobject.Notes = (Get-ErrorMessage -Record $_)
                            }
                        }
                        $copyobject | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
                    }
                }
            }
            else {
                foreach ($systemDb in $systemDbs) {
                    $sysdb = $sourceServer.databases[$systemDb]
                    $transfer = New-Object Microsoft.SqlServer.Management.Smo.Transfer $sysdb
                    $transfer.CopyAllObjects = $false
                    $transfer.CopyAllDatabaseTriggers = $true
                    $transfer.CopyAllDefaults = $true
                    $transfer.CopyAllRoles = $true
                    $transfer.CopyAllRules = $true
                    $transfer.CopyAllSchemas = $true
                    $transfer.CopyAllSequences = $true
                    $transfer.CopyAllSqlAssemblies = $true
                    $transfer.CopyAllSynonyms = $true
                    $transfer.CopyAllTables = $true
                    $transfer.CopyAllViews = $true
                    $transfer.CopyAllStoredProcedures = $true
                    $transfer.CopyAllUserDefinedAggregates = $true
                    $transfer.CopyAllUserDefinedDataTypes = $true
                    $transfer.CopyAllUserDefinedTableTypes = $true
                    $transfer.CopyAllUserDefinedTypes = $true
                    $transfer.CopyAllUserDefinedFunctions = $true
                    $transfer.CopyAllUsers = $true
                    $transfer.PreserveDbo = $true
                    $transfer.Options.AllowSystemObjects = $false
                    $transfer.Options.ContinueScriptingOnError = $true
                    $transfer.Options.IncludeDatabaseRoleMemberships = $true
                    $transfer.Options.Indexes = $true
                    $transfer.Options.Permissions = $true
                    $transfer.Options.WithDependencies = $false
                    
                    Write-Message -Level Output -Message "Copying from $systemDb."
                    try {
                        $sqlQueries = $transfer.ScriptTransfer()
                        
                        foreach ($sql in $sqlQueries) {
                            Write-Message -Level Debug -Message "$sql"
                            if ($PSCmdlet.ShouldProcess($destServer, $sql)) {
                                try {
                                    $destServer.Query($sql, $systemDb)
                                }
                                catch {
                                    # Don't care - long story having to do with duplicate stuff
                                }
                            }
                        }
                    }
                    catch {
                        # Don't care - long story having to do with duplicate stuff
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlSysDbUserObjects
    }
}
tools\dbatools\functions\Copy-DbaTableData.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Copy-DbaTableData {
    <#
        .SYNOPSIS
            Copies data between SQL Server tables.

        .DESCRIPTION
            Copies data between SQL Server tables using SQL Bulk Copy.
            The same can be achieved also doing
                $sourcetable = Invoke-SqlCmd2 -ServerInstance instance1 ... -As DataTable
                Write-DbaDataTable -SqlInstance ... -InputObject $sourcetable
            but it will force buffering the contents on the table in memory (high RAM usage for large tables).
            With this function, a streaming copy will be done in the most speedy and least resource-intensive way.

        .PARAMETER SqlInstance
            Source SQL Server.You must have sysadmin access and server version must be SQL Server version 2000 or greater.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination Sql Server. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database to copy the table from.

        .PARAMETER DestinationDatabase
            The database to copy the table to. If not specified, it is assumed to be the same of Database

        .PARAMETER Table
            Define a specific table you would like to use as source. You can specify up to three-part name like db.sch.tbl.
            If the object has special characters please wrap them in square brackets [ ].
            This dbo.First.Table will try to find table named 'Table' on schema 'First' and database 'dbo'.
            The correct way to find table named 'First.Table' on schema 'dbo' is passing dbo.[First.Table]

        .PARAMETER DestinationTable
            The table you want to use as destination. If not specified, it is assumed to be the same of Table

        .PARAMETER Query
            If you want to copy only a portion, specify the query (but please, select all the columns, or nasty things will happen)

        .PARAMETER BatchSize
            The BatchSize for the import defaults to 5000.

        .PARAMETER NotifyAfter
            Sets the option to show the notification after so many rows of import

        .PARAMETER NoTableLock
            If this switch is enabled, a table lock (TABLOCK) will not be placed on the destination table. By default, this operation will lock the destination table while running.

        .PARAMETER CheckConstraints
            If this switch is enabled, the SqlBulkCopy option to process check constraints will be enabled.

            Per Microsoft "Check constraints while data is being inserted. By default, constraints are not checked."

        .PARAMETER FireTriggers
            If this switch is enabled, the SqlBulkCopy option to fire insert triggers will be enabled.

            Per Microsoft "When specified, cause the server to fire the insert triggers for the rows being inserted into the Database."

        .PARAMETER KeepIdentity
            If this switch is enabled, the SqlBulkCopy option to preserve source identity values will be enabled.

            Per Microsoft "Preserve source identity values. When not specified, identity values are assigned by the destination."

        .PARAMETER KeepNulls
            If this switch is enabled, the SqlBulkCopy option to preserve NULL values will be enabled.

            Per Microsoft "Preserve null values in the destination table regardless of the settings for default values. When not specified, null values are replaced by default values where applicable."

        .PARAMETER Truncate
            If this switch is enabled, the destination table will be truncated after prompting for confirmation.

        .PARAMETER BulkCopyTimeOut
            Value in seconds for the BulkCopy operations timeout. The default is 30 seconds.

        .PARAMETER InputObject
            Enables piping of Table objects from Get-DbaTable

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration
            Author: niphlod (Simone Bizzotto)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaTableData

        .EXAMPLE
            Copy-DbaTableData -SqlInstance sql1 -Destination sql2 -Database dbatools_from -Table test_table

            Copies all the data from sql1 to sql2, using the database dbatools_from.

        .EXAMPLE
            Copy-DbaTableData -SqlInstance sql1 -Destination sql2 -Database dbatools_from -DestinationDatabase dbatools_dest -Table test_table

            Copies all the data from sql1 to sql2, using the database dbatools_from as source and dbatools_dest as destination

        .EXAMPLE
            Get-DbaTable -SqlInstance sql1 -Database tempdb -Table tb1, tb2 | Copy-DbaTableData -DestinationTable tb3

            Copies all data from tables tb1 and tb2 in tempdb on sql1 to tb3 in tempdb onsql1

        .EXAMPLE
            Get-DbaTable -SqlInstance sql1 -Database tempdb -Table tb1, tb2 | Copy-DbaTableData -Destination sql2

            Copies data from tbl1 in tempdb on sql1 to tbl1 in tempdb on sql2
            then
            Copies data from tbl2 in tempdb on sql1 to tbl2 in tempdb on sql2

        .EXAMPLE
            Copy-DbaTableData -SqlInstance sql1 -Destination sql2 -Database dbatools_from -Table test_table

            Copies all the data from sql1 to sql2, using the database dbatools_from.

        .EXAMPLE
            Copy-DbaTableData -SqlInstance sql1 -Destination sql2 -Database dbatools_from -Table test_table -KeepIdentity -Truncate

            Copies all the data from sql1 to sql2, using the database dbatools_from, keeping identity columns and truncating the destination

        .EXAMPLE
            Copy-DbaTableData -SqlInstance sql1 -Destination sql2 -Database dbatools_from -Table test_table -KeepIdentity -Truncate

            Copies all the data from sql1 to sql2, using the database dbatools_from, keeping identity columns and truncating the destination

    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [Alias("ServerInstance", "SqlServer", "Source")]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [DbaInstanceParameter]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [string]$Database,
        [string]$DestinationDatabase,
        [string[]]$Table,
        [string]$Query,
        [int]$BatchSize = 50000,
        [int]$NotifyAfter = 5000,
        [string]$DestinationTable,
        [switch]$NoTableLock,
        [switch]$CheckConstraints,
        [switch]$FireTriggers,
        [switch]$KeepIdentity,
        [switch]$KeepNulls,
        [switch]$Truncate,
        [int]$bulkCopyTimeOut = 5000,
        [Parameter(ValueFromPipeline)]
        [Microsoft.SqlServer.Management.Smo.Table[]]$InputObject,
        [switch]$EnableException
    )

    begin {
        # Getting the total rows copied is a challenge. Use SqlBulkCopyExtension.
        # http://stackoverflow.com/questions/1188384/sqlbulkcopy-row-count-when-complete

        $sourcecode = 'namespace System.Data.SqlClient {
            using Reflection;

            public static class SqlBulkCopyExtension
            {
                const String _rowsCopiedFieldName = "_rowsCopied";
                static FieldInfo _rowsCopiedField = null;

                public static int RowsCopiedCount(this SqlBulkCopy bulkCopy)
                {
                    if (_rowsCopiedField == null) _rowsCopiedField = typeof(SqlBulkCopy).GetField(_rowsCopiedFieldName, BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance);
                    return (int)_rowsCopiedField.GetValue(bulkCopy);
                }
            }
        }'

        Add-Type -ReferencedAssemblies System.Data.dll -TypeDefinition $sourcecode -ErrorAction SilentlyContinue
        $bulkCopyOptions = 0
        $options = "TableLock", "CheckConstraints", "FireTriggers", "KeepIdentity", "KeepNulls", "Default"

        foreach ($option in $options) {
            $optionValue = Get-Variable $option -ValueOnly -ErrorAction SilentlyContinue
            if ($option -eq "TableLock" -and (!$NoTableLock)) {
                $optionValue = $true
            }
            if ($optionValue -eq $true) {
                $bulkCopyOptions += $([Data.SqlClient.SqlBulkCopyOptions]::$option).value__
            }
        }
    }

    process {
        if ((Test-Bound -Not -ParameterName Table, SqlInstance) -and (Test-Bound -Not -ParameterName InputObject)) {
            Stop-Function -Message "You must pipe in a table or specify SqlInstance, Database and Table."
            return
        }

        if ($SqlInstance) {
            if ((Test-Bound -Not -ParameterName Database)) {
                Stop-Function -Message "Database is required when passing a SqlInstance" -Target $Table
                return
            }

            try {
                $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $SqlInstance
                return
            }

            if ($Database -notin $server.Databases.Name) {
                Stop-Function -Message "Database $Database doesn't exist on $server"
                return
            }

            try {
                $InputObject += Get-DbaTable -SqlInstance $server -Table $Table -Database $Database -EnableException -Verbose:$false
            }
            catch {
                Stop-Function -Message "Unable to determine source table : $Table"
                return
            }
        }

        foreach ($sqltable in $InputObject) {
            $Database = $sqltable.Parent.Name
            $server = $sqltable.Parent.Parent

            if ((Test-Bound -Not -ParameterName DestinationDatabase)) {
                $DestinationDatabase = $Database
            }

            if ((Test-Bound -Not -ParameterName DestinationTable)) {
                $DestinationTable = $sqltable.Name
            }

            if ((Test-Bound -Not -ParameterName Destination)) {
                $destServer = $server
            }
            else {
                try {
                    $destServer = Connect-SqlInstance -SqlInstance $Destination -SqlCredential $DestinationSqlCredential
                }
                catch {
                    Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Destination
                    return
                }
            }

            if ($DestinationDatabase -notin $destServer.Databases.Name) {
                Stop-Function -Message "Database $DestinationDatabase doesn't exist on $destServer"
                return
            }

            try {
                $desttable = Get-DbaTable -SqlInstance $destServer -Table $DestinationTable -Database $DestinationDatabase -EnableException -Verbose:$false | Select-Object -First 1
            }
            catch {
                Stop-Function -Message "Unable to determine destination table: $DestinationTable"
                return
            }

            if (-not $desttable) {
                Stop-Function -Message "$DestinationTable does not exist on destination"
                return
            }

            $connstring = $destServer.ConnectionContext.ConnectionString

            $fqtnfrom = "$($server.Databases[$Database]).$sqltable"
            $fqtndest = "$($destServer.Databases[$DestinationDatabase]).$desttable"

            if (Test-Bound -ParameterName Query -Not) {
                $Query = "SELECT * FROM $fqtnfrom"
            }
            try {
                if ($Truncate -eq $true) {
                    if ($Pscmdlet.ShouldProcess($destServer, "Truncating table $fqtndest")) {
                        $null = $destServer.Databases[$DestinationDatabase].ExecuteNonQuery("TRUNCATE TABLE $fqtndest")
                    }
                }
                $cmd = $server.ConnectionContext.SqlConnectionObject.CreateCommand()
                $cmd.CommandText = $Query
                if ($server.ConnectionContext.IsOpen -eq $false) {
                    $server.ConnectionContext.SqlConnectionObject.Open()
                }
                $bulkCopy = New-Object Data.SqlClient.SqlBulkCopy("$connstring;Database=$DestinationDatabase", $bulkCopyOptions)
                $bulkCopy.DestinationTableName = $fqtndest
                $bulkCopy.EnableStreaming = $true
                $bulkCopy.BatchSize = $BatchSize
                $bulkCopy.NotifyAfter = $NotifyAfter
                $bulkCopy.BulkCopyTimeOut = $BulkCopyTimeOut

                $elapsed = [System.Diagnostics.Stopwatch]::StartNew()
                # Add RowCount output
                $bulkCopy.Add_SqlRowsCopied({
                        $RowsPerSec = [math]::Round($args[1].RowsCopied / $elapsed.ElapsedMilliseconds * 1000.0, 1)
                        Write-Progress -id 1 -activity "Inserting rows" -Status ([System.String]::Format("{0} rows ({1} rows/sec)", $args[1].RowsCopied, $RowsPerSec))
                    })

                if ($Pscmdlet.ShouldProcess($destServer, "Writing rows to $fqtndest")) {
                    $reader = $cmd.ExecuteReader()
                    $bulkCopy.WriteToServer($reader)
                    $RowsTotal = [System.Data.SqlClient.SqlBulkCopyExtension]::RowsCopiedCount($bulkCopy)
                    $TotalTime = [math]::Round($elapsed.Elapsed.TotalSeconds, 1)
                    Write-Message -Level Verbose -Message "$RowsTotal rows inserted in $TotalTime sec"
                    if ($rowCount -is [int]) {
                        Write-Progress -id 1 -activity "Inserting rows" -status "Complete" -Completed
                    }
                }

                $bulkCopy.Close()
                $bulkCopy.Dispose()
                $reader.Close()

                [pscustomobject]@{
                    SourceInstance       = $server.Name
                    SourceDatabase       = $Database
                    SourceTable          = $sqltable.Name
                    DestinationInstance  = $destServer.name
                    DestinationDatabase  = $DestinationDatabase
                    DestinationTable     = $desttable.Name
                    RowsCopied           = $rowstotal
                    Elapsed              = [prettytimespan]$elapsed.Elapsed
                }
            }
            catch {
                Stop-Function -Message "Something went wrong" -ErrorRecord $_ -Target $server -continue
            }
        }
    }
}
tools\dbatools\functions\Copy-DbaXESessionTemplate.ps1
function Copy-DbaXESessionTemplate {
    <#
        .SYNOPSIS
            Copies non-Microsoft templates from the dbatools template repository (\bin\xetemplates\) to $home\Documents\SQL Server Management Studio\Templates\XEventTemplates.

        .DESCRIPTION
            Copies non-Microsoft templates from the dbatools template repository (\bin\xetemplates\) to $home\Documents\SQL Server Management Studio\Templates\XEventTemplates.

            Useful for when you want to use the SSMS GUI.

        .PARAMETER Path
            The path to the template directory. Defaults to the dbatools template repository (\bin\xetemplates\).

        .PARAMETER Destination
            Path to the Destination directory, defaults to $home\Documents\SQL Server Management Studio\Templates\XEventTemplates.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Copy-DbaXESessionTemplate

        .EXAMPLE
            Copy-DbaXESessionTemplate

            Copies non-Microsoft templates from the dbatools template repository (\bin\xetemplates\) to $home\Documents\SQL Server Management Studio\Templates\XEventTemplates.

        .EXAMPLE
            Copy-DbaXESessionTemplate -Path C:\temp\xetemplates

            Copies your templates from C:\temp\xetemplates to $home\Documents\SQL Server Management Studio\Templates\XEventTemplates.

    #>
    [CmdletBinding()]
    param (
        [string[]]$Path = "$script:PSModuleRoot\bin\xetemplates",
        [string]$Destination = "$home\Documents\SQL Server Management Studio\Templates\XEventTemplates",
        [switch]$EnableException
    )
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($destinstance in $Destination) {
            if (-not (Test-Path -Path $destinstance)) {
                try {
                    $null = New-Item -ItemType Directory -Path $destinstance -ErrorAction Stop
                }
                catch {
                    Stop-Function -Message "Failure" -ErrorRecord $_ -Target $destinstance
                }
            }
            try {
                $files = (Get-DbaXESessionTemplate -Path $Path | Where-Object Source -ne Microsoft).Path
                foreach ($file in $files) {
                    Write-Message -Level Output -Message "Copying $($file.Name) to $destinstance."
                    Copy-Item -Path $file -Destination $destinstance -ErrorAction Stop
                }
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_ -Target $path
            }
        }
    }
}
tools\dbatools\functions\Disable-DbaAgHadr.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Disable-DbaAgHadr {
    <#
        .SYNOPSIS
            Disables the Hadr service setting on the specified SQL Server.

        .DESCRIPTION
            In order to build an AG a cluster has to be built and then the Hadr enabled for the SQL Server
            service. This function disables that feature for the SQL Server service.

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to.

        .PARAMETER Credential
            Credential object used to connect to the Windows server itself as a different user

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER Force
            Will restart SQL Server and SQL Server Agent service to apply the change.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Hadr, AG, AvailabilityGroup
            Author: Shawn Melton (@wsmelton | http://blog.wsmelton.info)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Disable-DbaAgHadr

        .EXAMPLE
            Disable-DbaAgHadr -SqlInstance sql2016 -Force

            Sets Hadr service to disabled for the instance sql2016, and restart the service to apply the change.

        .EXAMPLE
            Disable-DbaAgHadr -SqlInstance sql2012\dev1 -Force

            Sets Hadr service to disabled for the instance dev1 on sq2012, and restart the service to apply the change.
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$Credential,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        function GetDbaAgHadr {
            [CmdletBinding()]
            param (
                [parameter(Mandatory = $true, ValueFromPipeline = $true)]
                [Alias("ServerInstance", "SqlServer")]
                [DbaInstanceParameter[]]$SqlInstance,
                [PSCredential]$Credential,
                [Alias('Silent')]
                [switch]$EnableException
            )
            process {
                foreach ($instance in $SqlInstance) {

                    try {
                        $computer = $computerName = $instance.ComputerName
                        $instanceName = $instance.InstanceName
                        Write-Message -Level Verbose -Message "Connecting to $computer"
                        $currentState = Invoke-ManagedComputerCommand -ComputerName $computerName -ScriptBlock { $wmi.Services[$args[0]] | Select-Object IsHadrEnabled } -ArgumentList $instanceName -Credential $Credential
                    }
                    catch {
                        Stop-Function -Message "Failure connecting to $computer" -Category ConnectionError -ErrorRecord $_ -Target $instance
                        return
                    }

                    if ($null -eq $currentState.IsHadrEnabled) {
                        $isenabled = $false
                    }
                    else {
                        $isenabled = $currentState.IsHadrEnabled
                    }
                    [PSCustomObject]@{
                        ComputerName     = $computer
                        InstanceName     = $instanceName
                        SqlInstance      = $instance.FullName
                        IsHadrEnabled    = $isenabled
                    }
                }
            }
        }
    }

    process {
        foreach ($instance in $SqlInstance) {
            $computer = $computerFullName = $instance.ComputerName
            $instanceName = $instance.InstanceName
            if (-not (Test-ElevationRequirement -ComputerName $instance)) {
                return
            }
            $noChange = $false

            switch ($instance.InstanceName) {
                'MSSQLSERVER' { $agentName = 'SQLSERVERAGENT' }
                default { $agentName = "SQLAgent`$$instanceName" }
            }

            try {
                Write-Message -Level Verbose -Message "Checking current Hadr setting for $computer"
                $currentState = GetDbaAgHadr -SqlInstance $instance -Credential $Credential
            }
            catch {
                Stop-Function -Message "Failure to pull current state of Hadr setting on $computer" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $isHadrEnabled = $currentState.IsHadrEnabled
            Write-Message -Level InternalComment -Message "$instance Hadr current value: $isHadrEnabled"

            # hadr results from sql wmi can be iffy, skip the check
            <#
            if (-not $isHadrEnabled) {
                Write-Message -Level Warning -Message "Hadr is already disabled for instance: $($instance.FullName)"
                $noChange = $true
                continue
            }
            #>

            $scriptblock = {
                $instance = $args[0]
                $sqlService = $wmi.Services | Where-Object DisplayName -eq "SQL Server ($instance)"
                $sqlService.ChangeHadrServiceSetting(0)
            }

            if ($noChange -eq $false) {
                if ($PSCmdlet.ShouldProcess($instance, "Changing Hadr from $isHadrEnabled to 0 for $instance")) {
                    try {
                        Invoke-ManagedComputerCommand -ComputerName $computerFullName -Credential $Credential -ScriptBlock $scriptblock -ArgumentList $instancename
                    }
                    catch {
                        Stop-Function -Continue -Message "Failure on $($instance.FullName) | This may be because AlwaysOn Availability Groups feature requires the x86(non-WOW) or x64 Enterprise Edition of SQL Server 2012 (or later version) running on Windows Server 2008 (or later version) with WSFC hotfix KB 2494036 installed."
                    }
                }
                if (Test-Bound 'Force') {
                    if ($PSCmdlet.ShouldProcess($instance, "Force provided, restarting Engine and Agent service for $instance on $computerFullName")) {
                        try {
                            $null = Stop-DbaSqlService -ComputerName $computerFullName -InstanceName $instanceName -Type Agent, Engine
                            $null = Start-DbaSqlService -ComputerName $computerFullName -InstanceName $instanceName -Type Agent, Engine
                        }
                        catch {
                            Stop-Function -Message "Issue restarting $instance" -Target $instance -Continue
                        }
                    }
                }
                $newState = GetDbaAgHadr -SqlInstance $instance -Credential $Credential

                if (Test-Bound -Not -ParameterName Force) {
                    Write-Message -Level Warning -Message "You must restart the SQL Server for it to take effect."
                }

                [PSCustomObject]@{
                    ComputerName   = $newState.ComputerName
                    InstanceName   = $newState.InstanceName
                    SqlInstance    = $newState.SqlInstance
                    IsHadrEnabled  = $false
                }
            }
        }
    }
}
tools\dbatools\functions\Disable-DbaForceNetworkEncryption.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#

function Disable-DbaForceNetworkEncryption {
    <#
        .SYNOPSIS
            Disables Force Encryption for a SQL Server instance

        .DESCRIPTION
            Disables Force Encryption for a SQL Server instance. Note that this requires access to the Windows Server, not the SQL instance itself.

            This setting is found in Configuration Manager.

        .PARAMETER SqlInstance
            The target SQL Server. Defaults to localhost.

        .PARAMETER Credential
            Allows you to login to the computer (not SQL Server instance) using alternative Windows credentials.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .EXAMPLE
            Disable-DbaForceNetworkEncryption

            Disables Force Encryption on the default (MSSQLSERVER) instance on localhost - requires (and checks for) RunAs admin.

        .EXAMPLE
            Disable-DbaForceNetworkEncryption -SqlInstance sql01\SQL2008R2SP2

            Disables Force Network Encryption for the SQL2008R2SP2 on sql01. Uses Windows Credentials to both login and modify the registry.

        .EXAMPLE
            Disable-DbaForceNetworkEncryption -SqlInstance sql01\SQL2008R2SP2 -WhatIf

            Shows what would happen if the command were executed.

        .NOTES
            Tags: Certificate

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
    #>
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Low")]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer", "ComputerName")]
        [DbaInstanceParameter[]]$SqlInstance = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {

        foreach ($instance in $sqlinstance) {
            Write-Message -Level VeryVerbose -Message "Processing $instance." -Target $instance
            $null = Test-ElevationRequirement -ComputerName $instance -Continue

            Write-Message -Level Verbose -Message "Resolving hostname."
            $resolved = $null
            $resolved = Resolve-DbaNetworkName -ComputerName $instance -Turbo

            if ($null -eq $resolved) {
                Stop-Function -Message "Can't resolve $instance." -Target $instance -Continue -Category InvalidArgument
            }

            Write-Message -Level Output -Message "Connecting to SQL WMI on $($instance.ComputerName)."
            try {
                $sqlwmi = Invoke-ManagedComputerCommand -ComputerName $resolved.FullComputerName -ScriptBlock { $wmi.Services } -Credential $Credential -ErrorAction Stop | Where-Object DisplayName -eq "SQL Server ($($instance.InstanceName))"
            }
            catch {
                Stop-Function -Message "Failed to access $instance." -Target $instance -Continue -ErrorRecord $_
            }

            $regroot = ($sqlwmi.AdvancedProperties | Where-Object Name -eq REGROOT).Value
            $vsname = ($sqlwmi.AdvancedProperties | Where-Object Name -eq VSNAME).Value
            try {
                $instancename = $sqlwmi.DisplayName.Replace('SQL Server (', '').Replace(')', '') # Don't clown, I don't know regex :(
            }
            catch {
                # Probably because the instance name has been aliased or does not exist or samthin
            }
            $serviceaccount = $sqlwmi.ServiceAccount

            if ([System.String]::IsNullOrEmpty($regroot)) {
                $regroot = $sqlwmi.AdvancedProperties | Where-Object { $_ -match 'REGROOT' }
                $vsname = $sqlwmi.AdvancedProperties | Where-Object { $_ -match 'VSNAME' }

                if (![System.String]::IsNullOrEmpty($regroot)) {
                    $regroot = ($regroot -Split 'Value\=')[1]
                    $vsname = ($vsname -Split 'Value\=')[1]
                }
                else {
                    Stop-Function -Message "Can't find instance $vsname on $instance." -Continue -Category ObjectNotFound -Target $instance
                }
            }

            if ([System.String]::IsNullOrEmpty($vsname)) { $vsname = $instance }

            Write-Message -Level Output -Message "Regroot: $regroot" -Target $instance
            Write-Message -Level Output -Message "ServiceAcct: $serviceaccount" -Target $instance
            Write-Message -Level Output -Message "InstanceName: $instancename" -Target $instance
            Write-Message -Level Output -Message "VSNAME: $vsname" -Target $instance

            $scriptblock = {
                $regpath = "Registry::HKEY_LOCAL_MACHINE\$($args[0])\MSSQLServer\SuperSocketNetLib"
                $cert = (Get-ItemProperty -Path $regpath -Name Certificate).Certificate
                $oldvalue = (Get-ItemProperty -Path $regpath -Name ForceEncryption).ForceEncryption
                Set-ItemProperty -Path $regpath -Name ForceEncryption -Value $false
                $forceencryption = (Get-ItemProperty -Path $regpath -Name ForceEncryption).ForceEncryption

                [pscustomobject]@{
                    ComputerName          = $env:COMPUTERNAME
                    InstanceName          = $args[2]
                    SqlInstance           = $args[1]
                    ForceEncryption       = ($forceencryption -eq $true)
                    CertificateThumbprint = $cert
                }
            }

            if ($PScmdlet.ShouldProcess("local", "Connecting to $instance to modify the ForceEncryption value in $regroot for $($instance.InstanceName)")) {
                try {
                    Invoke-Command2 -ComputerName $resolved.FullComputerName -Credential $Credential -ArgumentList $regroot, $vsname, $instancename -ScriptBlock $scriptblock -ErrorAction Stop
                    Write-Message -Level Critical -Message "Force encryption was successfully set on $($resolved.FullComputerName) for the $instancename instance. You must now restart the SQL Server for changes to take effect." -Target $instance
                }
                catch {
                    Stop-Function -Message "Failed to connect to $($resolved.FullComputerName) using PowerShell remoting!" -ErrorRecord $_ -Target $instance -Continue
                }
            }
        }
    }
}
tools\dbatools\functions\Disable-DbaTraceFlag.ps1
function Disable-DbaTraceFlag {
    <#
    .SYNOPSIS
        Disable a Global Trace Flag that is currently running

    .DESCRIPTION
        The function will disable a Trace Flag that is currently running globally on the SQL Server instance(s) listed

    .PARAMETER SqlInstance
        Allows you to specify a comma separated list of servers to query.

    .PARAMETER SqlCredential
        Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

    .PARAMETER TraceFlag
        Trace flag number to enable globally

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Tags: TraceFlag
        Author: Garry Bargsley (@gbargsley), http://blog.garrybargsley.com

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Disable-DbaTraceFlag

    .EXAMPLE
        Disable-DbaTraceFlag -SqlInstance sql2016 -TraceFlag 3226
        Disable the globally running trace flag 3226 on SQL Server instance sql2016
#>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory)]
        [int[]]$TraceFlag,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $current = Get-DbaTraceFlag -SqlInstance $server -EnableException

            foreach ($tf in $TraceFlag) {
                $TraceFlagInfo = [pscustomobject]@{
                    SourceServer = $server.ComputerName
                    InstanceName = $server.ServiceName
                    SqlInstance  = $server.DomainInstanceName
                    TraceFlag    = $tf
                    Status       = $null
                    Notes        = $null
                    DateTime     = [DbaDateTime](Get-Date)
                }
                if ($tf -notin $current.TraceFlag) {
                    $TraceFlagInfo.Status = 'Skipped'
                    $TraceFlagInfo.Notes = "Trace Flag is not running."
                    $TraceFlagInfo
                    Write-Message -Level Warning -Message "Trace Flag $tf is not currently running on $instance"
                    continue
                }

                try {
                    $query = "DBCC TRACEOFF ($tf, -1)"
                    $server.Query($query)
                }
                catch {
                    $TraceFlagInfo.Status = "Failed"
                    $TraceFlagInfo.Notes = $_.Exception.Message
                    $TraceFlagInfo
                    Stop-Function -Message "Failure" -ErrorRecord $_ -Target $server -Continue
                }
                $TraceFlagInfo.Status = "Successful"
                $TraceFlagInfo
            }
        }
    }
}
tools\dbatools\functions\Dismount-DbaDatabase.ps1
function Dismount-DbaDatabase {
    <#
        .SYNOPSIS
            Detach a SQL Server Database.

        .DESCRIPTION
            This command detaches one or more SQL Server databases. If necessary, -Force can be used to break mirrors and remove databases from availability groups prior to detaching.

        .PARAMETER SqlInstance
            The SQL Server instance.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to detach.

        .PARAMETER FileStructure
            A StringCollection object value that contains a list database files. If FileStructure is not specified, BackupHistory will be used to guess the structure.

        .PARAMETER InputObject
            A collection of databases (such as returned by Get-DbaDatabase), to be detached.

        .PARAMETER UpdateStatistics
            If this switch is enabled, statistics for the database will be updated prior to detaching it.

        .PARAMETER Force
            If this switch is enabled and the database is part of a mirror, the mirror will be broken. If the database is part of an Availability Group, it will be removed from the AG.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Dismount-DbaDatabase

        .EXAMPLE
            Detach-DbaDatabase -SqlInstance sql2016b -Database SharePoint_Config, WSS_Logging

            Detaches SharePoint_Config and WSS_Logging from sql2016b

        .EXAMPLE
            Get-DbaDatabase -SqlInstance sql2016b -Database 'PerformancePoint Service Application_10032db0fa0041df8f913f558a5dc0d4' | Detach-DbaDatabase -Force

            Detaches 'PerformancePoint Service Application_10032db0fa0041df8f913f558a5dc0d4' from sql2016b. Since Force was specified, if the database is part of mirror, the mirror will be broken prior to detaching.

            If the database is part of an Availability Group, it will first be dropped prior to detachment.

            .EXAMPLE
            Get-DbaDatabase -SqlInstance sql2016b -Database WSS_Logging | Detach-DbaDatabase -Force -WhatIf

            Shows what would happen if the command were to execute (without actually executing the detach/break/remove commands).

    #>
    [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = "Default")]
    Param (
        [parameter(Mandatory, ParameterSetName = 'SqlInstance')]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory, ParameterSetName = 'SqlInstance')]
        [string]$Database,
        [parameter(Mandatory, ParameterSetName = 'Pipeline', ValueFromPipeline)]
        [Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
        [Switch]$UpdateStatistics,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($Database) {
                $InputObject += $server.Databases | Where-Object Name -in $Database
            }
            else {
                $InputObject += $server.Databases
            }

            if ($ExcludeDatabase) {
                $InputObject = $InputObject | Where-Object Name -NotIn $ExcludeDatabase
            }
        }

        foreach ($db in $InputObject) {
            $db.Refresh()
            $server = $db.Parent

            if ($db.IsSystemObject) {
                Stop-Function -Message "$db is a system database and cannot be detached using this method." -Target $db -Continue
            }

            Write-Message -Level Verbose -Message "Checking replication status."
            if ($db.ReplicationOptions -ne "None") {
                Stop-Function -Message "Skipping $db  on $server because it is replicated." -Target $db -Continue
            }

            # repeat because different servers could be piped in
            $snapshots = (Get-DbaDbSnapshot -SqlInstance $server).SnapshotOf
            Write-Message -Level Verbose -Message "Checking for snaps"
            if ($db.Name -in $snapshots) {
                Write-Message -Level Warning -Message "Database $db has snapshots, you need to drop them before detaching. Skipping $db on $server."
                Continue
            }

            Write-Message -Level Verbose -Message "Checking mirror status"
            if ($db.IsMirroringEnabled -and !$Force) {
                Stop-Function -Message "$db on $server is being mirrored. Use -Force to break mirror or use the safer backup/restore method." -Target $db -Continue
            }

            Write-Message -Level Verbose -Message "Checking Availability Group status"

            if ($db.AvailabilityGroupName -and !$Force) {
                $ag = $db.AvailabilityGroupName
                Stop-Function -Message "$db on $server is part of an Availability Group ($ag). Use -Force to drop from $ag availability group to detach. Alternatively, you can use the safer backup/restore method." -Target $db -Continue
            }

            $sessions = Get-DbaProcess -SqlInstance $db.Parent -Database $db.Name

            if ($sessions -and !$Force) {
                Stop-Function -Message "$db on $server currently has connected users and cannot be dropped. Use -Force to kill all connections and detach the database." -Target $db -Continue
            }

            if ($force) {

                if ($sessions) {
                    If ($Pscmdlet.ShouldProcess($server, "Killing $($sessions.count) sessions which are connected to $db")) {
                        $null = $sessions | Stop-DbaProcess -ErrorAction SilentlyContinue -WarningAction SilentlyContinue
                    }
                }

                if ($db.IsMirroringEnabled) {
                    If ($Pscmdlet.ShouldProcess($server, "Breaking mirror for $db on $server")) {
                        try {
                            Write-Message -Level Warning -Message "Breaking mirror for $db on $server."
                            $db.ChangeMirroringState([Microsoft.SqlServer.Management.Smo.MirroringOption]::Off)
                            $db.Alter()
                            $db.Refresh()
                        }
                        catch {
                            Stop-Function -Message "Could not break mirror for $db on $server - not detaching." -Target $db -ErrorRecord $_ -Continue
                        }
                    }
                }

                if ($db.AvailabilityGroupName) {
                    $ag = $db.AvailabilityGroupName
                    If ($Pscmdlet.ShouldProcess($server, "Attempting remove $db on $server from Availability Group $ag")) {
                        try {
                            $server.AvailabilityGroups[$ag].AvailabilityDatabases[$db.name].Drop()
                            Write-Message -Level Verbose -Message "Successfully removed $db from  detach from $ag on $server."
                        }
                        catch {
                            if ($_.Exception.InnerException) {
                                $exception = $_.Exception.InnerException.ToString() -Split "System.Data.SqlClient.SqlException: "
                                $exception = " | $(($exception[1] -Split "at Microsoft.SqlServer.Management.Common.ConnectionManager")[0])".TrimEnd()
                            }

                            Stop-Function -Message "Could not remove $db from $ag on $server $exception." -Target $db -ErrorRecord $_ -Continue
                        }
                    }
                }

                $sessions = Get-DbaProcess -SqlInstance $db.Parent -Database $db.Name

                if ($sessions) {
                    If ($Pscmdlet.ShouldProcess($server, "Killing $($sessions.count) sessions which are still connected to $db")) {
                        $null = $sessions | Stop-DbaProcess -ErrorAction SilentlyContinue -WarningAction SilentlyContinue
                    }
                }
            }

            If ($Pscmdlet.ShouldProcess($server, "Detaching $db on $server")) {
                try {
                    $server.DetachDatabase($db.Name, $UpdateStatistics)

                    [pscustomobject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Database     = $db.name
                        DetachResult = "Success"
                    }
                }
                catch {
                    Stop-Function -Message "Failure" -Target $db -ErrorRecord $_ -Continue
                }
            }
        }
    }
}
tools\dbatools\functions\Enable-DbaAgHadr.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Enable-DbaAgHadr {
    <#
        .SYNOPSIS
            Enables the Hadr service setting on the specified SQL Server.

        .DESCRIPTION
            In order to build an AG a cluster has to be built and then the Hadr enabled for the SQL Server
            service. This function enables that feature for the SQL Server service.

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to.

        .PARAMETER Credential
            Credential object used to connect to the Windows server itself as a different user

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER Force
            Will restart SQL Server and SQL Server Agent service to apply the change.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Hadr, AG, AvailabilityGroup
            Author: Shawn Melton (@wsmelton | http://blog.wsmelton.info)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Enable-DbaAgHadr

        .EXAMPLE
            Enable-DbaAgHadr -SqlInstance sql2016 -Force

            Sets Hadr service to enabled for the instance sql2016, and restart the service to apply the change.

        .EXAMPLE
            Enable-DbaAgHadr -SqlInstance sql2012\dev1 -Force

            Sets Hadr service to disabled for the instance dev1 on sq2012, and restart the service to apply the change.
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$Credential,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        function GetDbaAgHadr {
    <#
        .SYNOPSIS
            Gets the Hadr service setting on the specified SQL Server instance.

        .DESCRIPTION
            Gets the Hadr setting, from the service level, and returns true or false for the specified SQL Server instance.

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to.

        .PARAMETER Credential
            Credential object used to connect to the Windows server itself as a different user

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Hadr, AG, AvailabilityGroup
            Author: Shawn Melton (@wsmelton | http://blog.wsmelton.info)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/GetDbaAgHadr

        .EXAMPLE
            GetDbaAgHadr -SqlInstance sql2016

            Returns a status of the Hadr setting for sql2016 SQL Server instance.
    #>
            [CmdletBinding()]
            param (
                [parameter(Mandatory = $true, ValueFromPipeline = $true)]
                [Alias("ServerInstance", "SqlServer")]
                [DbaInstanceParameter[]]$SqlInstance,
                [PSCredential]$Credential,
                [Alias('Silent')]
                [switch]$EnableException
            )
            process {
                foreach ($instance in $SqlInstance) {

                    try {
                        $computer = $computerName = $instance.ComputerName
                        $instanceName = $instance.InstanceName
                        Write-Message -Level Verbose -Message "Connecting to $computer"
                        $currentState = Invoke-ManagedComputerCommand -ComputerName $computerName -ScriptBlock { $wmi.Services[$args[0]] | Select-Object IsHadrEnabled } -ArgumentList $instanceName -Credential $Credential
                    }
                    catch {
                        Stop-Function -Message "Failure connecting to $computer" -Category ConnectionError -ErrorRecord $_ -Target $instance
                        return
                    }

                    if ($null -eq $currentState.IsHadrEnabled) {
                        $isenabled = $false
                    }
                    else {
                        $isenabled = $currentState.IsHadrEnabled
                    }
                    [PSCustomObject]@{
                        ComputerName     = $computer
                        InstanceName     = $instanceName
                        SqlInstance      = $instance.FullName
                        IsHadrEnabled    = $isenabled
                    }
                }
            }
        }
    }
    process {
        foreach ($instance in $SqlInstance) {
            $computer = $computerFullName = $instance.ComputerName
            $instanceName = $instance.InstanceName
            if (-not (Test-ElevationRequirement -ComputerName $instance)) {
                return
            }
            $noChange = $false

            switch ($instance.InstanceName) {
                'MSSQLSERVER' { $agentName = 'SQLSERVERAGENT' }
                default { $agentName = "SQLAgent`$$instanceName" }
            }

            try {
                Write-Message -Level Verbose -Message "Checking current Hadr setting for $computer"
                $currentState = GetDbaAgHadr -SqlInstance $instance -Credential $Credential
            }
            catch {
                Stop-Function -Message "Failure to pull current state of Hadr setting on $computer" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            $isHadrEnabled = $currentState.IsHadrEnabled
            Write-Message -Level InternalComment -Message "$instance Hadr current value: $isHadrEnabled"

            # hadr results from sql wmi can be iffy, skip the check
            <#
            if ($isHadrEnabled) {
                Write-Message -Level Warning -Message "Hadr is already enabled for instance: $($instance.FullName)"
                $noChange = $true
                continue
            }
            #>

            $scriptblock = {
                $instance = $args[0]
                $sqlService = $wmi.Services | Where-Object DisplayName -eq "SQL Server ($instance)"
                $sqlService.ChangeHadrServiceSetting(1)
            }

            if ($noChange -eq $false) {
                if ($PSCmdlet.ShouldProcess($instance, "Changing Hadr from $isHadrEnabled to 1 for $instance")) {
                    try {
                        Invoke-ManagedComputerCommand -ComputerName $computerFullName -Credential $Credential -ScriptBlock $scriptblock -ArgumentList $instancename
                    }
                    catch {
                        Stop-Function -Continue -Message "Failure on $($instance.FullName) | This may be because AlwaysOn Availability Groups feature requires the x86(non-WOW) or x64 Enterprise Edition of SQL Server 2012 (or later version) running on Windows Server 2008 (or later version) with WSFC hotfix KB 2494036 installed."
                    }
                }
            }

            if (Test-Bound -ParameterName Force) {
                if ($PSCmdlet.ShouldProcess($instance, "Force provided, restarting Engine and Agent service for $instance on $computerFullName")) {
                    try {
                        $null = Stop-DbaSqlService -ComputerName $computerFullName -InstanceName $instanceName -Type Agent, Engine
                        $null = Start-DbaSqlService -ComputerName $computerFullName -InstanceName $instanceName -Type Agent, Engine
                    }
                    catch {
                        Stop-Function -Message "Issue restarting $instance" -Target $instance -Continue
                    }
                }
            }
            $newState = GetDbaAgHadr -SqlInstance $instance -Credential $Credential

            if (Test-Bound -Not -ParameterName Force) {
                Write-Message -Level Warning -Message "You must restart the SQL Server for it to take effect."
            }

            [PSCustomObject]@{
                ComputerName    = $newState.ComputerName
                InstanceName    = $newState.InstanceName
                SqlInstance     = $newState.SqlInstance
                IsHadrEnabled   = $true
            }
        }
    }
}
tools\dbatools\functions\Enable-DbaForceNetworkEncryption.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Enable-DbaForceNetworkEncryption {
    <#
        .SYNOPSIS
            Enables Force Encryption for a SQL Server instance.

        .DESCRIPTION
            Enables Force Encryption for a SQL Server instance. Note that this requires access to the Windows Server, not the SQL instance itself.

            This setting is found in Configuration Manager.

        .PARAMETER SqlInstance
            The target SQL Server.

        .PARAMETER Credential
            Allows you to login to the computer (not SQL Server instance) using alternative Windows credentials

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .EXAMPLE
            Enable-DbaForceNetworkEncryption

            Enables Force Encryption on the default (MSSQLSERVER) instance on localhost. Requires (and checks for) RunAs admin.

        .EXAMPLE
            Enable-DbaForceNetworkEncryption -SqlInstance sql01\SQL2008R2SP2

            Enables Force Network Encryption for the SQL2008R2SP2 on sql01. Uses Windows Credentials to both connect and modify the registry.

        .EXAMPLE
            Enable-DbaForceNetworkEncryption -SqlInstance sql01\SQL2008R2SP2 -WhatIf

            Shows what would happen if the command were executed.

        .NOTES
            Tags: Certificate

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
    #>
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Low", DefaultParameterSetName = 'Default')]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer", "ComputerName")]
        [DbaInstanceParameter[]]
        $SqlInstance = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {

        foreach ($instance in $sqlinstance) {
            Write-Message -Level VeryVerbose -Message "Processing $instance." -Target $instance
            $null = Test-ElevationRequirement -ComputerName $instance -Continue

            Write-Message -Level Verbose -Message "Resolving hostname."
            $resolved = $null
            $resolved = Resolve-DbaNetworkName -ComputerName $instance -Turbo

            if ($null -eq $resolved) {
                Stop-Function -Message "Can't resolve $instance." -Target $instance -Continue -Category InvalidArgument
            }

            Write-Message -Level Output -Message "Connecting to SQL WMI on $($instance.ComputerName)."
            try {
                $sqlwmi = Invoke-ManagedComputerCommand -ComputerName $resolved.FullComputerName -ScriptBlock { $wmi.Services } -Credential $Credential -ErrorAction Stop | Where-Object DisplayName -eq "SQL Server ($($instance.InstanceName))"
            }
            catch {
                Stop-Function -Message "Failed to access $instance" -Target $instance -Continue -ErrorRecord $_
            }

            $regroot = ($sqlwmi.AdvancedProperties | Where-Object Name -eq REGROOT).Value
            $vsname = ($sqlwmi.AdvancedProperties | Where-Object Name -eq VSNAME).Value
            try {
                $instancename = $sqlwmi.DisplayName.Replace('SQL Server (', '').Replace(')', '') # Don't clown, I don't know regex :(
            }
            catch {
                # Probably because the instance name has been aliased or does not exist or samthin
            }
            $serviceaccount = $sqlwmi.ServiceAccount

            if ([System.String]::IsNullOrEmpty($regroot)) {
                $regroot = $sqlwmi.AdvancedProperties | Where-Object { $_ -match 'REGROOT' }
                $vsname = $sqlwmi.AdvancedProperties | Where-Object { $_ -match 'VSNAME' }

                if (![System.String]::IsNullOrEmpty($regroot)) {
                    $regroot = ($regroot -Split 'Value\=')[1]
                    $vsname = ($vsname -Split 'Value\=')[1]
                }
                else {
                    Stop-Function -Message "Can't find instance $vsname on $instance." -Continue -Category ObjectNotFound -Target $instance
                }
            }

            if ([System.String]::IsNullOrEmpty($vsname)) { $vsname = $instance }

            Write-Message -Level Output -Message "Regroot: $regroot" -Target $instance
            Write-Message -Level Output -Message "ServiceAcct: $serviceaccount" -Target $instance
            Write-Message -Level Output -Message "InstanceName: $instancename" -Target $instance
            Write-Message -Level Output -Message "VSNAME: $vsname" -Target $instance

            $scriptblock = {
                $regpath = "Registry::HKEY_LOCAL_MACHINE\$($args[0])\MSSQLServer\SuperSocketNetLib"
                $cert = (Get-ItemProperty -Path $regpath -Name Certificate).Certificate
                $oldvalue = (Get-ItemProperty -Path $regpath -Name ForceEncryption).ForceEncryption
                Set-ItemProperty -Path $regpath -Name ForceEncryption -Value $true
                $forceencryption = (Get-ItemProperty -Path $regpath -Name ForceEncryption).ForceEncryption

                [pscustomobject]@{
                    ComputerName          = $env:COMPUTERNAME
                    InstanceName          = $args[2]
                    SqlInstance           = $args[1]
                    ForceEncryption       = ($forceencryption -eq $true)
                    CertificateThumbprint = $cert
                }
            }

            if ($PScmdlet.ShouldProcess("local", "Connecting to $instance to modify the ForceEncryption value in $regroot for $($instance.InstanceName)")) {
                try {
                    Invoke-Command2 -ComputerName $resolved.FullComputerName -Credential $Credential -ArgumentList $regroot, $vsname, $instancename -ScriptBlock $scriptblock -ErrorAction Stop | Select-Object -Property * -ExcludeProperty PSComputerName, RunspaceId, PSShowComputerName
                    Write-Message -Level Critical -Message "Force encryption was successfully set on $($resolved.FullComputerName) for the $instancename instance. You must now restart the SQL Server for changes to take effect." -Target $instance
                }
                catch {
                    Stop-Function -Message "Failed to connect to $($resolved.FullComputerName) using PowerShell remoting!" -ErrorRecord $_ -Target $instance -Continue
                }
            }
        }
    }
}
tools\dbatools\functions\Enable-DbaTraceFlag.ps1
function Enable-DbaTraceFlag {
    <#
    .SYNOPSIS
        Enable Global Trace Flag(s)
    .DESCRIPTION
        The function will set one or multiple trace flags on the SQL Server instance(s) listed

    .PARAMETER SqlInstance
        Allows you to specify a comma separated list of servers to query.

    .PARAMETER SqlCredential
        Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

    .PARAMETER TraceFlag
        Trace flag number(s) to enable globally

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Tags: TraceFlag
        Author: Garry Bargsley (@gbargsley), http://blog.garrybargsley.com

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Enable-DbaTraceFlag

    .EXAMPLE
        Enable-DbaTraceFlag -SqlInstance sql2016 -TraceFlag 3226
        Enable the trace flag 3226 on SQL Server instance sql2016

    .EXAMPLE
        Enable-DbaTraceFlag -SqlInstance sql2016 -TraceFlag 1117, 1118
        Enable multiple trace flags on SQL Server instance sql2016
#>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory)]
        [int[]]$TraceFlag,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $CurrentRunningTraceFlags = Get-DbaTraceFlag -SqlInstance $server -EnableException

            # We could combine all trace flags but the granularity is worth it
            foreach ($tf in $TraceFlag) {
                $TraceFlagInfo = [PSCustomObject]@{
                    SourceServer = $server.ComputerName
                    InstanceName = $server.ServiceName
                    SqlInstance  = $server.DomainInstanceName
                    TraceFlag    = $tf
                    Status       = $null
                    Notes        = $null
                    DateTime     = [DbaDateTime](Get-Date)
                }
                if ($CurrentRunningTraceFlags.TraceFlag -contains $tf) {
                    $TraceFlagInfo.Status = 'Skipped'
                    $TraceFlagInfo.Notes = "The Trace flag is already running."
                    $TraceFlagInfo
                    Write-Message -Level Warning -Message "The Trace flag [$tf] is already running globally."
                    continue
                }

                try {
                    $query = "DBCC TRACEON ($tf, -1)"
                    $server.Query($query)
                    $server.Refresh()
                }
                catch {
                    $TraceFlagInfo.Status = "Failed"
                    $TraceFlagInfo.Notes = $_.Exception.Message
                    $TraceFlagInfo
                    Stop-Function -Message "Failure" -ErrorRecord $_ -Target $server -Continue
                }
                $TraceFlagInfo.Status = "Successful"
                $TraceFlagInfo
            }
        }
    }
}
tools\dbatools\functions\Expand-DbaTLogResponsibly.ps1
function Expand-DbaTLogResponsibly {
    <#
        .SYNOPSIS
            This command will help you to automatically grow your transaction log  file in a responsible way (preventing the generation of too many VLFs).

        .DESCRIPTION
            As you may already know, having a transaction log file with too many Virtual Log Files (VLFs) can hurt your database performance in many ways.

            Example:
                Too many VLFs can cause transaction log backups to slow down and can also slow down database recovery and, in extreme cases, even impact insert/update/delete performance.

                References:
                    http://www.sqlskills.com/blogs/kimberly/transaction-log-vlfs-too-many-or-too-few/
                    http://blogs.msdn.com/b/saponsqlserver/archive/2012/02/22/too-many-virtual-log-files-vlfs-can-cause-slow-database-recovery.aspx
                    http://www.brentozar.com/blitz/high-virtual-log-file-vlf-count/

                In order to get rid of this fragmentation we need to grow the file taking the following into consideration:
                    - How many VLFs are created when we perform a grow operation or when an auto-grow is invoked?

                Note: In SQL Server 2014 this algorithm has changed (http://www.sqlskills.com/blogs/paul/important-change-vlf-creation-algorithm-sql-server-2014/)

            Attention:
                We are growing in MB instead of GB because of known issue prior to SQL 2012:
                    More detail here:
                        http://www.sqlskills.com/BLOGS/PAUL/post/Bug-log-file-growth-broken-for-multiples-of-4GB.aspx
                    and
                        http://connect.microsoft.com/SqlInstance/feedback/details/481594/log-growth-not-working-properly-with-specific-growth-sizes-vlfs-also-not-created-appropriately
                    or
                        https://connect.microsoft.com/SqlInstance/feedback/details/357502/transaction-log-file-size-will-not-grow-exactly-4gb-when-filegrowth-4gb

            Understanding related problems:
                    http://www.sqlskills.com/blogs/kimberly/transaction-log-vlfs-too-many-or-too-few/
                    http://blogs.msdn.com/b/saponsqlserver/archive/2012/02/22/too-many-virtual-log-files-vlfs-can-cause-slow-database-recovery.aspx
                    http://www.brentozar.com/blitz/high-virtual-log-file-vlf-count/

            Known bug before SQL Server 2012
                    http://www.sqlskills.com/BLOGS/PAUL/post/Bug-log-file-growth-broken-for-multiples-of-4GB.aspx
                    http://connect.microsoft.com/SqlInstance/feedback/details/481594/log-growth-not-working-properly-with-specific-growth-sizes-vlfs-also-not-created-appropriately
                    https://connect.microsoft.com/SqlInstance/feedback/details/357502/transaction-log-file-size-will-not-grow-exactly-4gb-when-filegrowth-4gb

            How it works?
                The transaction log will grow in chunks until it reaches the desired size.
                Example: If you have a log file with 8192MB and you say that the target size is 81920MB (80GB) it will grow in chunks of 8192MB until it reaches 81920MB. 8192 -> 16384 -> 24576 ... 73728 -> 81920

        .PARAMETER SqlInstance
            The target SQL Server instance.

        .PARAMETER Database
            The database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER TargetLogSizeMB
            Specifies the target size of the transaction log file in megabytes.

        .PARAMETER IncrementSizeMB
            Specifies the amount the transaction log should grow in megabytes. If this value differs from the suggested value based on your TargetLogSizeMB, you will be prompted to confirm your choice.

            This value will be calculated if not specified.

        .PARAMETER LogFileId
            Specifies the file number(s) of additional transaction log files to grow.

            If this value is not specified, only the first transaction log file will be processed.

        .PARAMETER ShrinkLogFile
            If this switch is enabled, your transaction log files will be shrunk.

        .PARAMETER ShrinkSizeMB
            Specifies the target size of the transaction log file for the shrink operation.

        .PARAMETER BackupDirectory
            Specifies the location of your backups. Backups must be performed to shrink the transaction log.

            If this value is not specified, the SQL Server instance's default backup directory will be used.

         .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER ExcludeDatabase
            The database(s) to exclude. Options for this list are auto-populated from the server.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Storage, Backup
            This script uses Get-DbaDiskSpace dbatools command to get the TLog's drive free space

            Author: Claudio Silva (@ClaudioESSilva)
            Requires: ALTER DATABASE permission
            Limitations: Freespace cannot be validated on the directory where the log file resides in SQL Server 2005.

            Website: https://dbatools.io
            Copyright (C) 2016 Chrissy LeMaire
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Expand-DbaTLogResponsibly

        .EXAMPLE
            Expand-DbaTLogResponsibly -SqlInstance sqlcluster -Database db1 -TargetLogSizeMB 50000

            Grows the transaction log for database db1 on sqlcluster to 50000 MB and calculates the increment size.

        .EXAMPLE
            Expand-DbaTLogResponsibly -SqlInstance sqlcluster -Database db1, db2 -TargetLogSizeMB 10000 -IncrementSizeMB 200

            Grows the transaction logs for databases db1 and db2 on sqlcluster to 1000MB and sets the growth increment to 200MB.

        .EXAMPLE
            Expand-DbaTLogResponsibly -SqlInstance sqlcluster -Database db1 -TargetLogSizeMB 10000 -LogFileId 9

            Grows the transaction log file  with FileId 9 of the db1 database on sqlcluster instance to 10000MB.

        .EXAMPLE
            Expand-DbaTLogResponsibly -SqlInstance sqlcluster -Database (Get-Content D:\DBs.txt) -TargetLogSizeMB 50000

            Grows the transaction log of the databases specified in the file 'D:\DBs.txt' on sqlcluster instance to 50000MB.

        .EXAMPLE
            Expand-DbaTLogResponsibly -SqlInstance SqlInstance -Database db1,db2 -TargetLogSizeMB 100 -IncrementSizeMB 10 -ShrinkLogFile -ShrinkSizeMB 10 -BackupDirectory R:\MSSQL\Backup

            Grows the transaction logs for databases db1 and db2 on SQL server SQLInstance to 100MB, sets the incremental growth to 10MB, shrinks the transaction log to 10MB and uses the directory R:\MSSQL\Backup for the required backups.
    #>
    [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Default')]
    param (
        [parameter(Position = 1, Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [parameter(Position = 3)]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [parameter(Position = 4)]
        [object[]]$ExcludeDatabase,
        [parameter(Position = 5, Mandatory = $true)]
        [int]$TargetLogSizeMB,
        [parameter(Position = 6)]
        [int]$IncrementSizeMB = -1,
        [parameter(Position = 7)]
        [int]$LogFileId = -1,
        [parameter(Position = 8, ParameterSetName = 'Shrink', Mandatory = $true)]
        [switch]$ShrinkLogFile,
        [parameter(Position = 9, ParameterSetName = 'Shrink', Mandatory = $true)]
        [int]$ShrinkSizeMB,
        [parameter(Position = 10, ParameterSetName = 'Shrink')]
        [AllowEmptyString()]
        [string]$BackupDirectory,
        [switch][Alias('Silent')]
        $EnableException
    )

    begin {
        Write-Message -Level Verbose -Message "Set ErrorActionPreference to Inquire."
        $ErrorActionPreference = 'Inquire'

        #Convert MB to KB (SMO works in KB)
        Write-Message -Level Verbose -Message "Convert variables MB to KB (SMO works in KB)."
        [int]$TargetLogSizeKB = $TargetLogSizeMB * 1024
        [int]$LogIncrementSize = $incrementSizeMB * 1024
        [int]$ShrinkSize = $ShrinkSizeMB * 1024
        [int]$SuggestLogIncrementSize = 0
        [bool]$LogByFileID = if ($LogFileId -eq -1) {
            $false
        }
        else {
            $true
        }

        #Set base information
        Write-Message -Level Verbose -Message "Initialize the instance '$SqlInstance'."

        $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential

        if ($ShrinkLogFile -eq $true) {
            if ($BackupDirectory.length -eq 0) {
                $backupdirectory = $server.Settings.BackupDirectory
            }

            $pathexists = Test-DbaSqlPath -SqlInstance $server -Path $backupdirectory

            if ($pathexists -eq $false) {
                Stop-Function -Message "Backup directory does not exist."
            }
        }
    }

    process {

        try {

            [datetime]$initialTime = Get-Date

            #control the iteration number
            $databaseProgressbar = 0;

            Write-Message -Level Verbose -Message "Resolving NetBIOS name."
            $sourcenetbios = Resolve-NetBiosName $server

            $databases = $server.Databases | Where-Object IsAccessible
            Write-Message -Level Verbose -Message "Number of databases found: $($databases.Count)."
            if ($Database) {
                $databases = $databases | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $database = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }

            #go through all databases
            Write-Message -Level Verbose -Message "Processing...foreach database..."
            foreach ($db in $databases.Name) {
                Write-Message -Level Verbose -Message "Working on $db."
                $databaseProgressbar += 1

                #set step to reutilize on logging operations
                [string]$step = "$databaseProgressbar/$($Database.Count)"

                if ($server.Databases[$db]) {
                    Write-Progress `
                        -Id 1 `
                        -Activity "Using database: $db on Instance: '$SqlInstance'" `
                        -PercentComplete ($databaseProgressbar / $Database.Count * 100) `
                        -Status "Processing - $databaseProgressbar of $($Database.Count)"

                    #Validate which file will grow
                    if ($LogByFileID) {
                        $logfile = $server.Databases[$db].LogFiles.ItemById($LogFileId)
                    }
                    else {
                        $logfile = $server.Databases[$db].LogFiles[0]
                    }

                    $numLogfiles = $server.Databases[$db].LogFiles.Count

                    Write-Message -Level Verbose -Message "$step - Use log file: $logfile."
                    $currentSize = $logfile.Size
                    $currentSizeMB = $currentSize / 1024

                    #Get the number of VLFs
                    $initialVLFCount = Test-DbaDbVirtualLogFile -SqlInstance $server -Database $db

                    Write-Message -Level Verbose -Message "$step - Log file current size: $([System.Math]::Round($($currentSize/1024.0), 2)) MB "
                    [long]$requiredSpace = ($TargetLogSizeKB - $currentSize)

                    Write-Message -Level Verbose -Message "Verifying if sufficient space exists ($([System.Math]::Round($($requiredSpace / 1024.0), 2))MB) on the volume to perform this task."

                    [long]$TotalTLogFreeDiskSpaceKB = 0
                    Write-Message -Level Verbose -Message "Get TLog drive free space"
                    [object]$AllDrivesFreeDiskSpace = Get-DbaDiskSpace -ComputerName $sourcenetbios | Select-Object Name, SizeInKB

                    #Verify path using Split-Path on $logfile.FileName in backwards. This way we will catch the LUNs. Example: "K:\Log01" as LUN name. Need to add final backslash if not there
                    $DrivePath = Split-Path $logfile.FileName -parent
                    $DrivePath = if (!($DrivePath.EndsWith("\"))) { "$DrivePath\" }
                    else { $DrivePath }
                    Do {
                        if ($AllDrivesFreeDiskSpace | Where-Object { $DrivePath -eq "$($_.Name)" }) {
                            $TotalTLogFreeDiskSpaceKB = ($AllDrivesFreeDiskSpace | Where-Object { $DrivePath -eq $_.Name }).SizeInKB
                            $match = $true
                            break
                        }
                        else {
                            $match = $false
                            $DrivePath = Split-Path $DrivePath -parent
                            $DrivePath = if (!($DrivePath.EndsWith("\"))) { "$DrivePath\" }
                            else { $DrivePath }
                        }

                    }
                    while (!$match -or ([string]::IsNullOrEmpty($DrivePath)))

                    Write-Message -Level Verbose -Message "Total TLog Free Disk Space in MB: $([System.Math]::Round($($TotalTLogFreeDiskSpaceKB / 1024.0), 2))"

                    if (($TotalTLogFreeDiskSpaceKB -le 0) -or ([string]::IsNullOrEmpty($TotalTLogFreeDiskSpaceKB))) {
                        $title = "Choose increment value for database '$db':"
                        $message = "Cannot validate freespace on drive where the log file resides. Do you wish to continue? (Y/N)"
                        $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Will continue"
                        $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Will exit"
                        $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
                        $result = $host.ui.PromptForChoice($title, $message, $options, 0)
                        #no
                        if ($result -eq 1) {
                            Write-Message -Level Warning -Message "You have cancelled the execution"
                            return
                        }
                    }

                    if ($requiredSpace -gt $TotalTLogFreeDiskSpaceKB) {
                        Write-Message -Level Verbose -Message "There is not enough space on volume to perform this task. `r`n" `
                            "Available space: $([System.Math]::Round($($TotalTLogFreeDiskSpaceKB / 1024.0), 2))MB;`r`n" `
                            "Required space: $([System.Math]::Round($($requiredSpace / 1024.0), 2))MB;"
                        return
                    }
                    else {
                        if ($currentSize -ige $TargetLogSizeKB -and ($ShrinkLogFile -eq $false)) {
                            Write-Message -Level Verbose -Message "$step - [INFO] The T-Log file '$logfile' size is already equal or greater than target size - No action required."
                        }
                        else {
                            Write-Message -Level Verbose -Message "$step - [OK] There is sufficient free space to perform this task."

                            # If SQL Server version is greater or equal to 2012
                            if ($server.Version.Major -ge "11") {
                                switch ($TargetLogSizeMB) {
                                    { $_ -le 64 } { $SuggestLogIncrementSize = 64 }
                                    { $_ -ge 64 -and $_ -lt 256 } { $SuggestLogIncrementSize = 256 }
                                    { $_ -ge 256 -and $_ -lt 1024 } { $SuggestLogIncrementSize = 512 }
                                    { $_ -ge 1024 -and $_ -lt 4096 } { $SuggestLogIncrementSize = 1024 }
                                    { $_ -ge 4096 -and $_ -lt 8192 } { $SuggestLogIncrementSize = 2048 }
                                    { $_ -ge 8192 -and $_ -lt 16384 } { $SuggestLogIncrementSize = 4096 }
                                    { $_ -ge 16384 } { $SuggestLogIncrementSize = 8192 }
                                }
                            }
                            # 2008 R2 or under
                            else {
                                switch ($TargetLogSizeMB) {
                                    { $_ -le 64 } { $SuggestLogIncrementSize = 64 }
                                    { $_ -ge 64 -and $_ -lt 256 } { $SuggestLogIncrementSize = 256 }
                                    { $_ -ge 256 -and $_ -lt 1024 } { $SuggestLogIncrementSize = 512 }
                                    { $_ -ge 1024 -and $_ -lt 4096 } { $SuggestLogIncrementSize = 1024 }
                                    { $_ -ge 4096 -and $_ -lt 8192 } { $SuggestLogIncrementSize = 2048 }
                                    { $_ -ge 8192 -and $_ -lt 16384 } { $SuggestLogIncrementSize = 4000 }
                                    { $_ -ge 16384 } { $SuggestLogIncrementSize = 8000 }
                                }

                                if (($IncrementSizeMB % 4096) -eq 0) {
                                    Write-Message -Level Verbose -Message "Your instance version is below SQL 2012, remember the known BUG mentioned on HELP. `r`nUse Get-Help Expand-DbaTLogFileResponsibly to read help`r`nUse a different value for incremental size.`r`n"
                                    return
                                }
                            }
                            Write-Message -Level Verbose -Message "Instance $server version: $($server.Version.Major) - Suggested TLog increment size: $($SuggestLogIncrementSize)MB"

                            # Shrink Log File to desired size before re-growth to desired size (You need to remove as many VLF's as possible to ensure proper growth)
                            $ShrinkSizeMB = $ShrinkSize / 1024
                            if ($ShrinkLogFile -eq $true) {
                                if ($server.Databases[$db].RecoveryModel -eq [Microsoft.SqlServer.Management.Smo.RecoveryModel]::Simple) {
                                    Write-Message -Level Warning -Message "Database '$db' is in Simple RecoveryModel which does not allow log backups. Do not specify -ShrinkLogFile and -ShrinkSizeMB parameters."
                                    Continue
                                }

                                try {
                                    $sql = "SELECT last_log_backup_lsn FROM sys.database_recovery_status WHERE database_id = DB_ID('$db')"
                                    $sqlResult = $server.ConnectionContext.ExecuteWithResults($sql);

                                    if ($sqlResult.Tables[0].Rows[0]["last_log_backup_lsn"] -is [System.DBNull]) {
                                        Write-Message -Level Warning -Message "First, you need to make a full backup before you can do Tlog backup on database '$db' (last_log_backup_lsn is null)."
                                        Continue
                                    }
                                }
                                catch {
                                    Stop-Function -Message "Can't execute SQL on $server. `r`n $($_)" -Continue
                                }

                                If ($Pscmdlet.ShouldProcess($($server.name), "Backing up TLog for $db")) {
                                    Write-Message -Level Verbose -Message "We are about to backup the Tlog for database '$db' to '$backupdirectory' and shrink the log."
                                    Write-Message -Level Verbose -Message "Starting Size = $currentSizeMB."

                                    $DefaultCompression = $server.Configuration.DefaultBackupCompression.ConfigValue

                                    if ($currentSizeMB -gt $ShrinkSizeMB) {
                                        $backupRetries = 1
                                        Do {
                                            try {
                                                $percent = $null
                                                $backup = New-Object Microsoft.SqlServer.Management.Smo.Backup
                                                $backup.Action = [Microsoft.SqlServer.Management.Smo.BackupActionType]::Log
                                                $backup.BackupSetDescription = "Transaction Log backup of " + $db
                                                $backup.BackupSetName = $db + " Backup"
                                                $backup.Database = $db
                                                $backup.MediaDescription = "Disk"
                                                $dt = get-date -format yyyyMMddHHmmssms
                                                $null = $backup.Devices.AddDevice($backupdirectory + "\" + $db + "_db_" + $dt + ".trn", 'File')
                                                if ($DefaultCompression = $true) {
                                                    $backup.CompressionOption = 1
                                                }
                                                else {
                                                    $backup.CompressionOption = 0
                                                }
                                                $null = [Microsoft.SqlServer.Management.Smo.PercentCompleteEventHandler] {
                                                    Write-Progress -id 2 -ParentId 1 -activity "Backing up $db to $server" -percentcomplete $_.Percent -status ([System.String]::Format("Progress: {0} %", $_.Percent))
                                                }
                                                $backup.add_PercentComplete($percent)
                                                $backup.PercentCompleteNotification = 10
                                                $backup.add_Complete($complete)
                                                Write-Progress -id 2 -ParentId 1 -activity "Backing up $db to $server" -percentcomplete 0 -Status ([System.String]::Format("Progress: {0} %", 0))
                                                $backup.SqlBackup($server)
                                                Write-Progress -id 2 -ParentId 1 -activity "Backing up $db to $server" -status "Complete" -Completed
                                                $logfile.Shrink($ShrinkSizeMB, [Microsoft.SqlServer.Management.SMO.ShrinkMethod]::TruncateOnly)
                                                $logfile.Refresh()
                                            }
                                            catch {
                                                Write-Progress -id 1 -activity "Backup" -status "Failed" -completed
                                                Stop-Function -Message "Backup failed for database" -ErrorRecord $_ -Target $db -Continue
                                                Continue
                                            }

                                        }
                                        while (($logfile.Size / 1024) -gt $ShrinkSizeMB -and ++$backupRetries -lt 6)

                                        $currentSize = $logfile.Size
                                        Write-Message -Level Verbose -Message "TLog backup and truncate for database '$db' finished. Current TLog size after $backupRetries backups is $($currentSize/1024)MB"
                                    }
                                }
                            }

                            # SMO uses values in KB
                            $SuggestLogIncrementSize = $SuggestLogIncrementSize * 1024

                            # If default, use $SuggestedLogIncrementSize
                            if ($IncrementSizeMB -eq -1) {
                                $LogIncrementSize = $SuggestLogIncrementSize
                            }
                            else {
                                $title = "Choose increment value for database '$db':"
                                $message = "The input value for increment size was $([System.Math]::Round($LogIncrementSize/1024, 0))MB. However the suggested value for increment is $($SuggestLogIncrementSize/1024)MB.`r`nDo you want to use the suggested value of $([System.Math]::Round($SuggestLogIncrementSize/1024, 0))MB insted of $([System.Math]::Round($LogIncrementSize/1024, 0))MB"
                                $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Uses recomended size."
                                $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Will use parameter value."
                                $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
                                $result = $host.ui.PromptForChoice($title, $message, $options, 0)
                                #yes
                                if ($result -eq 0) {
                                    $LogIncrementSize = $SuggestLogIncrementSize
                                }
                            }

                            #start growing file
                            If ($Pscmdlet.ShouldProcess($($server.name), "Starting log growth. Increment chunk size: $($LogIncrementSize/1024)MB for database '$db'")) {
                                Write-Message -Level Verbose -Message "Starting log growth. Increment chunk size: $($LogIncrementSize/1024)MB for database '$db'"

                                Write-Message -Level Verbose -Message "$step - While current size less than target log size."

                                while ($currentSize -lt $TargetLogSizeKB) {

                                    Write-Progress `
                                        -Id 2 `
                                        -ParentId 1 `
                                        -Activity "Growing file $logfile on '$db' database" `
                                        -PercentComplete ($currentSize / $TargetLogSizeKB * 100) `
                                        -Status "Remaining - $([System.Math]::Round($($($TargetLogSizeKB - $currentSize) / 1024.0), 2)) MB"

                                    Write-Message -Level Verbose -Message "$step - Verifying if the log can grow or if it's already at the desired size."
                                    if (($TargetLogSizeKB - $currentSize) -lt $LogIncrementSize) {
                                        Write-Message -Level Verbose -Message "$step - Log size is lower than the increment size. Setting current size equals $TargetLogSizeKB."
                                        $currentSize = $TargetLogSizeKB
                                    }
                                    else {
                                        Write-Message -Level Verbose -Message "$step - Grow the $logfile file in $([System.Math]::Round($($LogIncrementSize / 1024.0), 2)) MB"
                                        $currentSize += $LogIncrementSize
                                    }

                                    #When -WhatIf Switch, do not run
                                    if ($PSCmdlet.ShouldProcess("$step - File will grow to $([System.Math]::Round($($currentSize/1024.0), 2)) MB", "This action will grow the file $logfile on database $db to $([System.Math]::Round($($currentSize/1024.0), 2)) MB .`r`nDo you wish to continue?", "Perform grow")) {
                                        Write-Message -Level Verbose -Message "$step - Set size $logfile to $([System.Math]::Round($($currentSize/1024.0), 2)) MB"
                                        $logfile.size = $currentSize

                                        Write-Message -Level Verbose -Message "$step - Applying changes"
                                        $logfile.Alter()
                                        Write-Message -Level Verbose -Message "$step - Changes have been applied"

                                        #Will put the info like VolumeFreeSpace up to date
                                        $logfile.Refresh()
                                    }
                                }

                                Write-Message -Level Verbose -Message "`r`n$step - [OK] Growth process for logfile '$logfile' on database '$db', has been finished."

                                Write-Message -Level Verbose -Message "$step - Grow $logfile log file on $db database finished."
                            }
                        }
                    } #else space available
                }
                #else verifying existence
                else {
                    Write-Message -Level Verbose -Message "Database '$db' does not exist on instance '$SqlInstance'."
                }

                #Get the number of VLFs
                $currentVLFCount = Test-DbaDbVirtualLogFile -SqlInstance $server -Database $db

                [pscustomobject]@{
                    ComputerName    = $server.ComputerName
                    InstanceName    = $server.ServiceName
                    SqlInstance     = $server.DomainInstanceName
                    Database        = $db
                    ID              = $logfile.ID
                    Name            = $logfile.Name
                    LogFileCount    = $numLogfiles
                    InitialSize     = [dbasize]($currentSizeMB * 1024)
                    CurrentSize     = [dbasize]($TargetLogSizeMB * 1024)
                    InitialVLFCount = $initialVLFCount.Total
                    CurrentVLFCount = $currentVLFCount.Total
                } | Select-DefaultView -ExcludeProperty LogFileCount
            } #foreach database
        }
        catch {
            Stop-Function -Message "Logfile $logfile on database $db not processed. Error: $($_.Exception.Message). Line Number:  $($_InvocationInfo.ScriptLineNumber)" -Continue
        }
    }

    end {
        Write-Message -Level Verbose -Message "Process finished $((Get-Date) - ($initialTime))"
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Expand-SqlTLogResponsibly
    }
}
tools\dbatools\functions\Export-DbaAvailabilityGroup.ps1
#ValidationTags#Messaging#
function Export-DbaAvailabilityGroup {
    <#
        .SYNOPSIS
            Exports SQL Server Availability Groups to a T-SQL file.

        .DESCRIPTION
            Exports SQL Server Availability Groups creation scripts to a T-SQL file. This is a function that is not available in SSMS.

        .PARAMETER SqlInstance
            The SQL Server instance name. SQL Server 2012 and above supported.

        .PARAMETER FilePath
            The directory name where the output files will be written. A sub directory with the format 'ServerName$InstanceName' will be created. A T-SQL scripts named 'AGName.sql' will be created under this subdirectory for each scripted Availability Group.

        .PARAMETER AvailabilityGroup
            The Availability Group(s) to export - this list is auto-populated from the server. If unspecified, all logins will be processed.

        .PARAMETER ExcludeAvailabilityGroup
            The Availability Group(s) to exclude - this list is auto-populated from the server.

        .PARAMETER NoClobber
            Do not overwrite existing export files.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER WhatIf
            Shows you what it'd output if you were to run the command

        .PARAMETER Confirm
            Confirms each step/line of output

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Hadr, AG, AvailabilityGroup
            Author: Chris Sommer (@cjsommer), cjsommer.com

            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Export-DbaAvailabilityGroup

        .EXAMPLE
            Export-DbaAvailabilityGroup -SqlInstance sql2012

            Exports all Availability Groups from SQL server "sql2012". Output scripts are written to the Documents\SqlAgExports directory by default.

        .EXAMPLE
            Export-DbaAvailabilityGroup -SqlInstance sql2012 -FilePath C:\temp\availability_group_exports

            Exports all Availability Groups from SQL server "sql2012". Output scripts are written to the C:\temp\availability_group_exports directory.

        .EXAMPLE
            Export-DbaAvailabilityGroup -SqlInstance sql2012 -FilePath 'C:\dir with spaces\availability_group_exports' -AvailabilityGroups AG1,AG2

            Exports Availability Groups AG1 and AG2 from SQL server "sql2012". Output scripts are written to the C:\dir with spaces\availability_group_exports directory.

        .EXAMPLE
            Export-DbaAvailabilityGroup -SqlInstance sql2014 -FilePath C:\temp\availability_group_exports -NoClobber

            Exports all Availability Groups from SQL server "sql2014". Output scripts are written to the C:\temp\availability_group_exports directory. If the export file already exists it will not be overwritten.
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$AvailabilityGroup,
        [object[]]$ExcludeAvailabilityGroup,
        [Alias("OutputLocation", "Path")]
        [string]$FilePath = "$([Environment]::GetFolderPath("MyDocuments"))\SqlAgExport",
        [switch]$NoClobber,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($server.IsHadrEnabled -eq $false) {
                Stop-Function -Message "Hadr is not enabled on this instance" -Continue
            }
            else {
                # Get all of the Availability Groups and filter if required
                $ags = $server.AvailabilityGroups
            }

            if (Test-Bound 'AvailabilityGroup') {
                $ags = $ags | Where-Object Name -In $AvailabilityGroup
            }
            if (Test-Bound 'ExcludeAvailabilityGroup') {
                $ags = $ags | Where-Object Name -NotIn $ExcludeAvailabilityGroup
            }

            if ($ags) {

                # Set and create the OutputLocation if it doesn't exist
                $sqlinst = $instance.ToString().Replace('\', '$')
                $outputLocation = "$FilePath\$sqlinst"

                if (!(Test-Path $outputLocation -PathType Container)) {
                    $null = New-Item -Path $outputLocation -ItemType Directory -Force
                }

                # Script each Availability Group
                foreach ($ag in $ags) {
                    $agName = $ag.Name

                    # Set the outfile name
                    if ($AppendDateToOutputFilename.IsPresent) {
                        $formatteddate = (Get-Date -Format 'yyyyMMdd_hhmm')
                        $outFile = "$outputLocation\${AGname}_${formatteddate}.sql"
                    }
                    else {
                        $outFile = "$outputLocation\$agName.sql"
                    }

                    # Check NoClobber and script out the AG
                    if ($NoClobber.IsPresent -and (Test-Path -Path $outFile -PathType Leaf)) {
                        Write-Message -Level Warning -Message "OutputFile $outFile already exists. Skipping due to -NoClobber parameter"
                    }
                    else {
                        Write-Message -Level Verbose -Message "Scripting Availability Group [$agName] on $instance to $outFile"

                        # Create comment block header for AG script
                        "/*" | Out-File -FilePath $outFile -Encoding ASCII -Force
                        " * Created by dbatools 'Export-DbaAvailabilityGroup' cmdlet on '$(Get-Date)'" | Out-File -FilePath $outFile -Encoding ASCII -Append
                        " * See https://dbatools.io/Export-DbaAvailabilityGroup for more help" | Out-File -FilePath $outFile -Encoding ASCII -Append

                        # Output AG and listener names
                        " *" | Out-File -FilePath $outFile -Encoding ASCII -Append
                        " * Availability Group Name: $($ag.name)" | Out-File -FilePath $outFile -Encoding ASCII -Append
                        $ag.AvailabilityGroupListeners | ForEach-Object { " * Listener Name: $($_.name)" } | Out-File -FilePath $outFile -Encoding ASCII -Append

                        # Output all replicas
                        " *" | Out-File -FilePath $outFile -Encoding ASCII -Append
                        $ag.AvailabilityReplicas | ForEach-Object { " * Replica: $($_.name)" } | Out-File -FilePath $outFile -Encoding ASCII -Append

                        # Output all databases
                        " *" | Out-File -FilePath $outFile -Encoding ASCII -Append
                        $ag.AvailabilityDatabases | ForEach-Object { " * Database: $($_.name)" } | Out-File -FilePath $outFile -Encoding ASCII -Append

                        # $ag | Select-Object -Property * | Out-File -FilePath $outFile -Encoding ASCII -Append

                        "*/" | Out-File -FilePath $outFile -Encoding ASCII -Append

                        # Script the AG
                        try {
                            $ag.Script() | Out-File -FilePath $outFile -Encoding ASCII -Append
                            Get-ChildItem $outFile
                        }
                        catch {
                            Stop-Function -ErrorRecord $_ -Message "Error scripting out the availability groups. This is likely due to a bug in SMO." -Continue
                        }
                    }
                }
            }
            else {
                Write-Message -Level Output -Message "No Availability Groups detected on $instance"
            }
        }
    }
}
tools\dbatools\functions\Export-DbaDacpac.ps1
function Export-DbaDacpac {
    <#
    .SYNOPSIS
    Exports a dacpac from a server.

    .DESCRIPTION
    Using SQLPackage, export a dacpac from an instance of SQL Server.

    Note - Extract from SQL Server is notoriously flaky - for example if you have three part references to external databases it will not work.

    For help with the extract action parameters and properties, refer to https://msdn.microsoft.com/en-us/library/hh550080(v=vs.103).aspx

    .PARAMETER SqlInstance
    SQL Server name or SMO object representing the SQL Server to connect to and publish to.

    .PARAMETER SqlCredential
    Allows you to login to servers using alternative logins instead Integrated, accepts Credential object created by Get-Credential

    .PARAMETER Path
    The directory where the .dacpac files will be exported to. Defaults to documents.

    .PARAMETER Database
    The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

    .PARAMETER ExcludeDatabase
    The database(s) to exclude - this list is auto-populated from the server

    .PARAMETER AllUserDatabases
    Run command against all user databases

    .PARAMETER ExtendedParameters
    Optional parameters used to extract the DACPAC. More information can be found at
    https://msdn.microsoft.com/en-us/library/hh550080.aspx

    .PARAMETER ExtendedProperties
    Optional properties used to extract the DACPAC. More information can be found at
    https://msdn.microsoft.com/en-us/library/hh550080.aspx


    .PARAMETER EnableException
    By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
    Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
    Tags: Migration, Database, Dacpac
    Author: Richie lee (@bzzzt_io)

    Website: https://dbatools.io
    Copyright: (C) Chrissy LeMaire, [email protected]
    License: MIT https://opensource.org/licenses/MIT

    .LINK
    https://dbatools.io/Export-DbaDacpac

    .EXAMPLE
    Export-DbaDacpac -SqlInstance sql2016 -Database SharePoint_Config
    Exports the dacpac for SharePoint_Config on sql2016 to $home\Documents\SharePoint_Config.dacpac

    .EXAMPLE
    $moreprops = "/p:VerifyExtraction=$true /p:CommandTimeOut=10"
    Export-DbaDacpac -SqlInstance sql2016 -Database SharePoint_Config -Path C:\temp -ExtendedProperties $moreprops

    Sets the CommandTimeout to 10 then extracts the dacpac for SharePoint_Config on sql2016 to C:\temp\SharePoint_Config.dacpac then verifies extraction.


    #>
    [CmdletBinding()]
    param
    (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$AllUserDatabases,
        [string]$Path = "$home\Documents",
        [string]$ExtendedParameters,
        [string]$ExtendedProperties,
        [switch]$EnableException
    )

    process {
        if ((Test-Bound -Not -ParameterName Database) -and (Test-Bound -Not -ParameterName ExcludeDatabase) -and (Test-Bound -Not -ParameterName AllUserDatabases)) {
            Stop-Function -Message "You must specify databases to execute against using either -Database, -ExcludeDatabase or -AllUserDatabases"
        }

        if (-not (Test-Path $Path)) {
            Stop-Function -Message "$Path doesn't exist or access denied"
        }

        if ((Get-Item $path) -isnot [System.IO.DirectoryInfo]) {
            Stop-Function -Message "Path must be a directory"
        }

        foreach ($instance in $sqlinstance) {

            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            $cleaninstance = $instance.ToString().Replace('\', '-')

            $dbs = $server.Databases | Where-Object { $_.IsSystemObject -eq $false -and $_.IsAccessible }

            if ($Database) {
                $dbs = $dbs | Where-Object Name -in $Database
                if (-not $dbs.name) {
                    Stop-Function -Message "Database $Database does not exist on $instance" -Target $instance -Continue
                }
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -notin $ExcludeDatabase
            }

            foreach ($db in $dbs) {
                $dbname = $db.name
                $connstring = $server.ConnectionContext.ConnectionString.Replace('"', "'")
                if ($connstring -notmatch 'Database=') {
                    $connstring = "$connstring;Database=$dbname"
                }
                $filename = "$Path\$cleaninstance-$dbname.dacpac"
                Write-Message -Level Verbose -Message "Exporting $filename"
                Write-Message -Level Verbose -Message "Using connection string $connstring"

                $sqlPackageArgs = "/action:Extract /tf:""$filename"" /SourceConnectionString:""$connstring"" $ExtendedParameters $ExtendedProperties"
                $resultstime = [diagnostics.stopwatch]::StartNew()

                try {
                    $startprocess = New-Object System.Diagnostics.ProcessStartInfo
                    $startprocess.FileName = "$script:PSModuleRoot\bin\smo\sqlpackage.exe"
                    $startprocess.Arguments = $sqlPackageArgs
                    $startprocess.RedirectStandardError = $true
                    $startprocess.RedirectStandardOutput = $true
                    $startprocess.UseShellExecute = $false
                    $startprocess.CreateNoWindow = $true
                    $process = New-Object System.Diagnostics.Process
                    $process.StartInfo = $startprocess
                    $process.Start() | Out-Null
                    $process.WaitForExit()
                    $stdout = $process.StandardOutput.ReadToEnd()
                    $stderr = $process.StandardError.ReadToEnd()
                    Write-Message -level Verbose -Message "StandardOutput: $stdout"

                    [pscustomobject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Database     = $dbname
                        Path         = $filename
                        Elapsed      = [prettytimespan]($resultstime.Elapsed)
                    } | Select-DefaultView -ExcludeProperty ComputerName, InstanceName
                }
                catch {
                    Stop-Function -Message "SQLPackage Failure" -ErrorRecord $_ -Continue
                }

                if ($process.ExitCode -ne 0) {
                    Stop-Function -Message "Standard output - $stderr" -Continue
                }
            }
        }
    }
}
tools\dbatools\functions\Export-DbaDiagnosticQuery.ps1
function Export-DbaDiagnosticQuery {
    <#
        .SYNOPSIS
            Export-DbaDiagnosticQuery can convert ouput generated by Invoke-DbaDiagnosticQuery to CSV or Excel

        .DESCRIPTION
            The default output format of Invoke-DbaDiagnosticQuery is a custom object. It can also output to CSV and Excel.
            However, CSV output can generate a lot of files and Excel output depends on the ImportExcel module by Doug Fike (https://github.com/dfinke/ImportExcel)
            Export-DbaDiagnosticQuery can be used to convert from the default export type to the other available export types.

        .PARAMETER InputObject
            Specifies the objects to convert

        .PARAMETER ConvertTo
            Specifies the output type. Valid choices are Excel and CSV. CSV is the default.

        .PARAMETER Path
            Specifies the path to the output files.

        .PARAMETER Suffix
            Suffix for the filename. It's datetime by default.

        .PARAMETER NoPlanExport
            Use this switch to suppress exporting of .sqlplan files

        .PARAMETER NoQueryExport
            Use this switch to suppress exporting of .sql files

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Query
            Author: Andre Kamman (@AndreKamman), http://clouddba.io

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Export-DbaDiagnosticQuery

        .EXAMPLE
            Invoke-DbaDiagnosticQuery -SqlInstance sql2016 | Export-DbaDiagnosticQuery -Path c:\temp

            Converts output from Invoke-DbaDiagnosticQuery to multiple CSV files

        .EXAMPLE
            $output = Invoke-DbaDiagnosticQuery -SqlInstance sql2016
            Export-DbaDiagnosticQuery -InputObject $output -ConvertTo Excel

            Converts output from Invoke-DbaDiagnosticQuery to Excel worksheet(s) in the Documents folder
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [object[]]$InputObject,
        [ValidateSet("Excel", "Csv")]
        [string]$ConvertTo = "Csv",
        [System.IO.FileInfo]$Path = [Environment]::GetFolderPath("mydocuments"),
        [string]$Suffix = "$(Get-Date -format 'yyyyMMddHHmmssms')",
        [switch]$NoPlanExport,
        [switch]$NoQueryExport,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        if ($ConvertTo -eq "Excel") {
            try {
                Import-Module ImportExcel -ErrorAction Stop
            }
            catch {
                $message = "Failed to load module, exporting to Excel feature is not available
                            Install the module from: https://github.com/dfinke/ImportExcel
                            Valid alternative conversion format is csv"
                Stop-Function -Message $message
                return
            }
        }

        if (!$(Test-Path $Path)) {
            try {
                New-Item $Path -ItemType Directory -ErrorAction Stop | Out-Null
                Write-Message -Level Output -Message "Created directory $Path"
            }
            catch {
                Stop-Function -Message "Failed to create directory $Path" -Continue
            }
        }

        Function Remove-InvalidFileNameChars {
            [CmdletBinding()]
            param (
                [Parameter(Mandatory = $true,
                    Position = 0,
                    ValueFromPipeline = $true,
                    ValueFromPipelineByPropertyName = $true)]
                [String]$Name
            )
            $Name = $Name.Replace(" ", "-")
            $invalidChars = [IO.Path]::GetInvalidFileNameChars() -join ''
            $re = "[{0}]" -f [RegEx]::Escape($invalidChars)
            return ($Name -replace $re)
        }
    }

    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($row in $InputObject) {
            $result = $row.Result
            $name = $row.Name
            $SqlInstance = $row.SqlInstance.Replace("\", "$")
            $dbname = $row.Database
            $number = $row.Number
            $note = $row.Note

            if ($null -eq $result) {
                Stop-Function -Message "Result was empty for $name" -Target $result -Continue
            }

            $queryname = Remove-InvalidFileNameChars -Name $Name
            $excelfilename = "$Path\$SqlInstance-DQ-$Suffix.xlsx"
            $exceldbfilename = "$Path\$SqlInstance-DQ-$dbname-$Suffix.xlsx"
            $csvdbfilename = "$Path\$SqlInstance-$dbname-DQ-$number-$queryname-$Suffix.csv"
            $csvfilename = "$Path\$SqlInstance-DQ-$number-$queryname-$Suffix.csv"

            $columnnameoptions = "Query Plan", "QueryPlan", "Query_Plan", "query_plan_xml"
            if (($result | Get-Member | Where-Object Name -in $columnnameoptions).Count -gt 0) {
                $plannr = 0
                $columnname = ($result | Get-Member | Where-Object Name -In $columnnameoptions).Name
                foreach ($plan in $result."$columnname") {
                    $plannr += 1
                    if ($row.DatabaseSpecific) {
                        $planfilename = "$Path\$SqlInstance-$dbname-DQ-$number-$queryname-$plannr-$Suffix.sqlplan"
                    }
                    else {
                        $planfilename = "$Path\$SqlInstance-DQ-$number-$queryname-$plannr-$Suffix.sqlplan"
                    }

                    if (!$NoPlanExport) {
                        Write-Message -Level Output -Message "Exporting $planfilename"
                        if ($plan) {$plan | Out-File -FilePath $planfilename}
                    }
                }

                $result = $result | Select-Object * -ExcludeProperty "$columnname"
            }

            $columnnameoptions = "Complete Query Text", "QueryText", "Query Text", "Query_Text", "query_sql_text"
            if (($result | Get-Member | Where-Object Name -In $columnnameoptions ).Count -gt 0) {
                $sqlnr = 0
                $columnname = ($result | Get-Member | Where-Object Name -In $columnnameoptions).Name
                foreach ($sql in $result."$columnname") {
                    $sqlnr += 1
                    if ($row.DatabaseSpecific) {
                        $sqlfilename = "$Path\$SqlInstance-$dbname-DQ-$number-$queryname-$sqlnr-$Suffix.sql"
                    }
                    else {
                        $sqlfilename = "$Path\$SqlInstance-DQ-$number-$queryname-$sqlnr-$Suffix.sql"
                    }

                    if (!$NoQueryExport) {
                        Write-Message -Level Output -Message "Exporting $sqlfilename"
                        if ($sql) {$sql | Out-File -FilePath $sqlfilename}
                    }
                }

                $result = $result | Select-Object * -ExcludeProperty "$columnname"
            }

            switch ($ConvertTo) {
                "Excel" {
                    if ($row.DatabaseSpecific) {
                        Write-Message -Level Output -Message "Exporting $exceldbfilename"
                        $result | Export-Excel -Path $exceldbfilename -WorkSheetname $Name -AutoSize -AutoFilter -BoldTopRow -FreezeTopRow
                    }
                    else {
                        Write-Message -Level Output -Message "Exporting $excelfilename"
                        $result | Export-Excel -Path $excelfilename -WorkSheetname $Name -AutoSize -AutoFilter -BoldTopRow -FreezeTopRow
                    }
                }
                "csv" {
                    if ($row.DatabaseSpecific) {
                        Write-Message -Level Output -Message "Exporting $csvdbfilename"
                        $result | Export-Csv -Path $csvdbfilename -NoTypeInformation -Append
                    }
                    else {
                        Write-Message -Level Output -Message "Exporting $csvfilename"
                        $result | Export-Csv -Path $csvfilename -NoTypeInformation -Append
                    }
                }
            }
        }
    }
}
tools\dbatools\functions\Export-DbaExecutionPlan.ps1
#ValidationTags#Messaging#
function Export-DbaExecutionPlan {
    <#
        .SYNOPSIS
            Exports execution plans to disk.

        .DESCRIPTION
            Exports execution plans to disk. Can pipe from Export-DbaExecutionPlan

            Thanks to
                https://www.simple-talk.com/sql/t-sql-programming/dmvs-for-query-plan-metadata/
                and
                http://www.scarydba.com/2017/02/13/export-plans-cache-sqlplan-file/
            for the idea and query.

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to.

        .PARAMETER SqlCredential
            Credential object used to connect to the SQL Server as a different user

        .PARAMETER Database
            The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto-populated from the server

        .PARAMETER SinceCreation
            Datetime object used to narrow the results to a date

        .PARAMETER SinceLastExecution
            Datetime object used to narrow the results to a date

        .PARAMETER Path
            The directory where all of the sqlxml files will be exported

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER PipedObject
            Internal parameter

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Performance, ExecutionPlan
            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Export-DbaExecutionPlan

        .EXAMPLE
            Export-DbaExecutionPlan -SqlInstance sqlserver2014a

            Exports all execution plans for sqlserver2014a.

        .EXAMPLE
            Export-DbaExecutionPlan -SqlInstance sqlserver2014a -Database db1, db2 -SinceLastExecution '7/1/2016 10:47:00'

            Exports all execution plans for databases db1 and db2 on sqlserver2014a since July 1, 2016 at 10:47 AM.
    #>
    [cmdletbinding(SupportsShouldProcess = $true, DefaultParameterSetName = "Default")]
    param (
        [parameter(ParameterSetName = 'NotPiped', Mandatory)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [parameter(ParameterSetName = 'NotPiped')]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [parameter(ParameterSetName = 'Piped', Mandatory)]
        [parameter(ParameterSetName = 'NotPiped', Mandatory)]
        [string]$Path,
        [parameter(ParameterSetName = 'NotPiped')]
        [datetime]$SinceCreation,
        [parameter(ParameterSetName = 'NotPiped')]
        [datetime]$SinceLastExecution,
        [Parameter(ParameterSetName = 'Piped', Mandatory, ValueFromPipeline)]
        [object[]]$PipedObject,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        if ($SinceCreation -ne $null) {
            $SinceCreation = $SinceCreation.ToString("yyyy-MM-dd HH:mm:ss")
        }

        if ($SinceLastExecution -ne $null) {
            $SinceLastExecution = $SinceLastExecution.ToString("yyyy-MM-dd HH:mm:ss")
        }

        function Export-Plan {
            param(
                [object]$object
            )
            $instanceName = $object.SqlInstance
            $dbName = $object.DatabaseName
            $queryPosition = $object.QueryPosition
            $sqlHandle = "0x"; $object.SqlHandle | ForEach-Object { $sqlHandle += ("{0:X}" -f $_).PadLeft(2, "0") }
            $sqlHandle = $sqlHandle.TrimStart('0x02000000').TrimEnd('0000000000000000000000000000000000000000')
            $shortName = "$instanceName-$dbName-$queryPosition-$sqlHandle"

            foreach ($queryPlan in $object.BatchQueryPlanRaw) {
                $fileName = "$path\$shortName-batch.sqlplan"

                try {
                    if ($Pscmdlet.ShouldProcess("localhost", "Writing XML file to $fileName")) {
                        $queryPlan.Save($fileName)
                    }
                }
                catch {
                    Stop-Function -Message "Skipped query plan for $fileName because it is null." -Target $fileName -ErrorRecord $_ -Continue
                }
            }

            foreach ($statementPlan in $object.SingleStatementPlanRaw) {
                $fileName = "$path\$shortName.sqlplan"

                try {
                    if ($Pscmdlet.ShouldProcess("localhost", "Writing XML file to $fileName")) {
                        $statementPlan.Save($fileName)
                    }
                }
                catch {
                    Stop-Function -Message "Skipped statement plan for $fileName because it is null." -Target $fileName -ErrorRecord $_ -Continue
                }
            }

            if ($Pscmdlet.ShouldProcess("console", "Showing output object")) {
                Add-Member -Force -InputObject $object -MemberType NoteProperty -Name OutputFile -Value $fileName
                Select-DefaultView -InputObject $object -Property ComputerName, InstanceName, SqlInstance, DatabaseName, SqlHandle, CreationTime, LastExecutionTime, OutputFile
            }
        }
    }

    process {
        if (!(Test-Path $Path)) {
            $null = New-Item -ItemType Directory -Path $Path
        }

        if ($PipedObject) {
            foreach ($object in $pipedobject) {
                Export-Plan $object
                return
            }
        }

        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $select = "SELECT DB_NAME(deqp.dbid) as DatabaseName, OBJECT_NAME(deqp.objectid) as ObjectName,
                    detqp.query_plan AS SingleStatementPlan,
                    deqp.query_plan AS BatchQueryPlan,
                    ROW_NUMBER() OVER ( ORDER BY Statement_Start_offset ) AS QueryPosition,
                    sql_handle as SqlHandle,
                    plan_handle as PlanHandle,
                    creation_time as CreationTime,
                    last_execution_time as LastExecutionTime"

            $from = " FROM sys.dm_exec_query_stats deqs
                        CROSS APPLY sys.dm_exec_text_query_plan(deqs.plan_handle,
                            deqs.statement_start_offset,
                            deqs.statement_end_offset) AS detqp
                        CROSS APPLY sys.dm_exec_query_plan(deqs.plan_handle) AS deqp
                        CROSS APPLY sys.dm_exec_sql_text(deqs.plan_handle) AS execText"

            if ($ExcludeDatabase -or $Database -or $SinceCreation.Length -gt 0 -or $SinceLastExecution.length -gt 0 -or $ExcludeEmptyQueryPlan -eq $true) {
                $where = " WHERE "
            }

            $whereArray = @()

            if ($Database -gt 0) {
                $dbList = $Database -join "','"
                $whereArray += " DB_NAME(deqp.dbid) in ('$dbList') "
            }

            if (Test-Bound 'SinceCreation') {
                Write-Message -Level Verbose -Message "Adding creation time"
                $whereArray += " creation_time >= '$SinceCreation' "
            }

            if (Test-Bound 'SinceLastExecution') {
                Write-Message -Level Verbose -Message "Adding last execution time"
                $whereArray += " last_execution_time >= '$SinceLastExecution' "
            }

            if (Test-Bound 'ExcludeDatabase') {
                $dbList = $ExcludeDatabase -join "','"
                $whereArray += " DB_NAME(deqp.dbid) not in ('$dbList') "
            }

            if (Test-Bound 'ExcludeEmptyQueryPlan') {
                $whereArray += " detqp.query_plan is not null"
            }

            if ($where.Length -gt 0) {
                $whereArray = $whereArray -join " and "
                $where = "$where $whereArray"
            }

            $sql = "$select $from $where"
            Write-Message -Level Debug -Message "SQL Statement: $sql"
            try {
                $dataTable = $server.ConnectionContext.ExecuteWithResults($sql).Tables
            }
            catch {
                Stop-Function -Message "Issue collecting execution plans" -Target $instance -ErroRecord $_ -Continue
            }

            foreach ($row in ($dataTable.Rows)) {
                $sqlHandle = "0x"; $row.sqlhandle | ForEach-Object { $sqlHandle += ("{0:X}" -f $_).PadLeft(2, "0") }
                $planhandle = "0x"; $row.planhandle | ForEach-Object { $planhandle += ("{0:X}" -f $_).PadLeft(2, "0") }

                $object = [pscustomobject]@{
                    ComputerName           = $server.ComputerName
                    InstanceName           = $server.ServiceName
                    SqlInstance            = $server.DomainInstanceName
                    DatabaseName           = $row.DatabaseName
                    SqlHandle              = $sqlHandle
                    PlanHandle             = $planhandle
                    SingleStatementPlan    = $row.SingleStatementPlan
                    BatchQueryPlan         = $row.BatchQueryPlan
                    QueryPosition          = $row.QueryPosition
                    CreationTime           = $row.CreationTime
                    LastExecutionTime      = $row.LastExecutionTime
                    BatchQueryPlanRaw      = [xml]$row.BatchQueryPlan
                    SingleStatementPlanRaw = [xml]$row.SingleStatementPlan
                }
                Export-Plan $object
            }
        }
    }
}
tools\dbatools\functions\Export-DbaLogin.ps1
function Export-DbaLogin {
    <#
        .SYNOPSIS
            Exports Windows and SQL Logins to a T-SQL file. Export includes login, SID, password, default database, default language, server permissions, server roles, db permissions, db roles.

        .DESCRIPTION
            Exports Windows and SQL Logins to a T-SQL file. Export includes login, SID, password, default database, default language, server permissions, server roles, db permissions, db roles.

        .PARAMETER SqlInstance
            The SQL Server instance name. SQL Server 2000 and above supported.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Login
            The login(s) to process. Options for this list are auto-populated from the server. If unspecified, all logins will be processed.

        .PARAMETER ExcludeLogin
            The login(s) to exclude. Options for this list are auto-populated from the server.

        .PARAMETER Database
            The database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER FilePath
            The file to write to.

        .PARAMETER NoClobber
            If this switch is enabled, a file already existing at the path specified by FilePath will not be overwritten.

        .PARAMETER Append
            If this switch is enabled, content will be appended to a file already existing at the path specified by FilePath. If the file does not exist, it will be created.

        .PARAMETER NoJobs
            If this switch is enabled, Agent job ownership will not be exported.

        .PARAMETER NoDatabases
            If this switch is enabled, mappings for databases will not be exported.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER ExcludeGoBatchSeparator
            If specified, will NOT script the 'GO' batch separator.

        .PARAMETER DestinationVersion
            To say to which version the script should be generated. If not specified will use instance major version.

        .NOTES
            Tags: Export, Login
            Author: Chrissy LeMaire (@cl), netnerds.net
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Export-DbaLogin

        .EXAMPLE
            Export-DbaLogin -SqlInstance sql2005 -FilePath C:\temp\sql2005-logins.sql

            Exports the logins for SQL Server "sql2005" and writes them to the file "C:\temp\sql2005-logins.sql"

        .EXAMPLE
            Export-DbaLogin -SqlInstance sqlserver2014a -Exclude realcajun -SqlCredential $scred -FilePath C:\temp\logins.sql -Append

            Authenticates to sqlserver2014a using SQL Authentication. Exports all logins except for realcajun to C:\temp\logins.sql, and appends to the file if it exists. If not, the file will be created.

        .EXAMPLE
            Export-DbaLogin -SqlInstance sqlserver2014a -Login realcajun, netnerds -FilePath C:\temp\logins.sql

            Exports ONLY logins netnerds and realcajun FROM sqlserver2014a to the file  C:\temp\logins.sql

        .EXAMPLE
            Export-DbaLogin -SqlInstance sqlserver2014a -Login realcajun, netnerds -Database HR, Accounting

            Exports ONLY logins netnerds and realcajun FROM sqlserver2014a with the permissions on databases HR and Accounting

        .EXAMPLE
            Export-DbaLogin -SqlInstance sqlserver2008 -Login realcajun, netnerds -FilePath C:\temp\login.sql -ExcludeGoBatchSeparator

            Exports ONLY logins netnerds and realcajun FROM sqlserver2008 server, to the C:\temp\login.sql file without the 'GO' batch separator.

        .EXAMPLE
            Export-DbaLogin -SqlInstance sqlserver2008 -Login realcajun -FilePath C:\temp\users.sql -DestinationVersion SQLServer2016

            Exports login realcajun fron sqlsever2008 to the file C:\temp\users.sql with sintax to run on SQL Server 2016
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [object[]]$Login,
        [object[]]$ExcludeLogin,
        [Alias("Databases")]
        [object[]]$Database,
        [Alias("OutFile", "Path", "FileName")]
        [string]$FilePath,
        [Alias("NoOverwrite")]
        [switch]$NoClobber,
        [switch]$Append,
        [switch]$NoDatabases,
        [switch]$NoJobs,
        [Alias('Silent')]
        [switch]$EnableException,
        [switch]$ExcludeGoBatchSeparator,
        [ValidateSet('SQLServer2000', 'SQLServer2005', 'SQLServer2008/2008R2', 'SQLServer2012', 'SQLServer2014', 'SQLServer2016', 'SQLServer2017')]
        [string]$DestinationVersion
    )

    begin {

        if ($FilePath) {
            if ($FilePath -notlike "*\*") {
                $FilePath = ".\$filepath"
            }
            $directory = Split-Path $FilePath
            $exists = Test-Path $directory

            if ($exists -eq $false) {
                Write-Message -Level Warning -Message "Parent directory $directory does not exist."
            }
        }

        $outsql = @()

        $versions = @{
            'SQLServer2000'        = 'Version80'
            'SQLServer2005'        = 'Version90'
            'SQLServer2008/2008R2' = 'Version100'
            'SQLServer2012'        = 'Version110'
            'SQLServer2014'        = 'Version120'
            'SQLServer2016'        = 'Version130'
            'SQLServer2017'        = 'Version140'
        }

        $versionsNumbers = @{
            '8'  = 'Version80'
            '9'  = 'Version90'
            '10' = 'Version100'
            '11' = 'Version110'
            '12' = 'Version120'
            '13' = 'Version130'
            '14' = 'Version140'
        }
    }
    process {
        if (Test-FunctionInterrupt) {
            return
        }

        Write-Message -Level Verbose -Message "Connecting to $sqlinstance."
        $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $sqlcredential

        if ([string]::IsNullOrEmpty($destinationVersion)) {
            #Get compatibility level for scripting the objects
            $scriptVersion = $versionsNumbers[$server.VersionMajor.ToString()]
        }
        else {
            $scriptVersion = $versions[$destinationVersion]
        }

        if ($NoDatabases -eq $false -or $Database) {
            # if we got a database or a list of databases passed
            # and we need to enumerate mappings, login.enumdatabasemappings() takes forever
            # the cool thing though is that database.enumloginmappings() is fast. A lot.
            # if we get a list of databases passed (or even the default list of all the databases)
            # we save outself a call to enumloginmappings if there is no map at all
            $DbMapping = @()
            $DbsToMap = $server.Databases
            if ($Database) {
                $DbsToMap = $DbsToMap | Where-Object Name -in $Database
            }
            foreach ($db in $DbsToMap) {
                if ($db.IsAccessible -eq $false) {
                    continue
                }
                $dbmap = $db.EnumLoginMappings()
                foreach ($el in $dbmap) {
                    $DbMapping += [pscustomobject]@{
                        Database  = $db.Name
                        UserName  = $el.Username
                        LoginName = $el.LoginName
                    }
                }
            }
        }

        foreach ($sourceLogin in $server.Logins) {
            $userName = $sourceLogin.name

            if ($Login -and $Login -notcontains $userName -or $ExcludeLogin -contains $userName) {
                continue
            }

            if ($userName.StartsWith("##") -or $userName -eq 'sa') {
                Write-Message -Level Warning -Message "Skipping $userName."
                continue
            }

            $serverName = $server

            $userBase = ($userName.Split("\")[0]).ToLower()
            if ($serverName -eq $userBase -or $userName.StartsWith("NT ")) {
                if ($Pscmdlet.ShouldProcess("console", "Stating $userName is skipped because it is a local machine name.")) {
                    Write-Message -Level Warning -Message "$userName is skipped because it is a local machine name."
                    continue
                }
            }

            if ($Pscmdlet.ShouldProcess("Outfile", "Adding T-SQL for login $userName")) {
                if ($FilePath) {
                    Write-Message -Level Verbose -Message "Exporting $userName."
                }

                $outsql += "`r`nUSE master`n"
                # Getting some attributes
                $defaultDb = $sourceLogin.DefaultDatabase
                $language = $sourceLogin.Language

                if ($sourceLogin.PasswordPolicyEnforced -eq $false) {
                    $checkPolicy = "OFF"
                }
                else {
                    $checkPolicy = "ON"
                }

                if (!$sourceLogin.PasswordExpirationEnabled) {
                    $checkExpiration = "OFF"
                }
                else {
                    $checkExpiration = "ON"
                }

                # Attempt to script out SQL Login
                if ($sourceLogin.LoginType -eq "SqlLogin") {
                    $sourceLoginName = $sourceLogin.name

                    switch ($server.versionMajor) {
                        0 {
                            $sql = "SELECT CONVERT(VARBINARY(256),password) AS hashedpass FROM master.dbo.syslogins WHERE loginname='$sourceLoginName'"
                        }
                        8 {
                            $sql = "SELECT CONVERT(VARBINARY(256),password) AS hashedpass FROM dbo.syslogins WHERE name='$sourceLoginName'"
                        }
                        9 {
                            $sql = "SELECT CONVERT(VARBINARY(256),password_hash) as hashedpass FROM sys.sql_logins WHERE name='$sourceLoginName'"
                        }
                        default {
                            $sql = "SELECT CAST(CONVERT(varchar(256), CAST(LOGINPROPERTY(name,'PasswordHash') AS VARBINARY(256)), 1) AS NVARCHAR(max)) AS hashedpass FROM sys.server_principals WHERE principal_id = $($sourceLogin.id)"
                        }
                    }

                    try {
                        $hashedPass = $server.ConnectionContext.ExecuteScalar($sql)
                    }
                    catch {
                        $hashedPassDt = $server.Databases['master'].ExecuteWithResults($sql)
                        $hashedPass = $hashedPassDt.Tables[0].Rows[0].Item(0)
                    }

                    if ($hashedPass.GetType().Name -ne "String") {
                        $passString = "0x"; $hashedPass | ForEach-Object {
                            $passString += ("{0:X}" -f $_).PadLeft(2, "0")
                        }
                        $hashedPass = $passString
                    }

                    $sid = "0x"; $sourceLogin.sid | ForEach-Object {
                        $sid += ("{0:X}" -f $_).PadLeft(2, "0")
                    }
                    $outsql += "IF NOT EXISTS (SELECT loginname FROM master.dbo.syslogins WHERE name = '$userName') CREATE LOGIN [$userName] WITH PASSWORD = $hashedPass HASHED, SID = $sid, DEFAULT_DATABASE = [$defaultDb], CHECK_POLICY = $checkPolicy, CHECK_EXPIRATION = $checkExpiration, DEFAULT_LANGUAGE = [$language]"
                }
                # Attempt to script out Windows User
                elseif ($sourceLogin.LoginType -eq "WindowsUser" -or $sourceLogin.LoginType -eq "WindowsGroup") {
                    $outsql += "IF NOT EXISTS (SELECT loginname FROM master.dbo.syslogins WHERE name = '$userName') CREATE LOGIN [$userName] FROM WINDOWS WITH DEFAULT_DATABASE = [$defaultDb], DEFAULT_LANGUAGE = [$language]"
                }
                # This script does not currently support certificate mapped or asymmetric key users.
                else {
                    Write-Message -Level Warning -Message "$($sourceLogin.LoginType) logins not supported. $($sourceLogin.Name) skipped."
                    continue
                }

                if ($sourceLogin.IsDisabled) {
                    $outsql += "ALTER LOGIN [$userName] DISABLE"
                }

                if ($sourceLogin.DenyWindowsLogin) {
                    $outsql += "DENY CONNECT SQL TO [$userName]"
                }
            }

            # Server Roles: sysadmin, bulklogin, etc
            foreach ($role in $server.Roles) {
                $roleName = $role.Name

                # SMO changed over time
                try {
                    $roleMembers = $role.EnumMemberNames()
                }
                catch {
                    $roleMembers = $role.EnumServerRoleMembers()
                }

                if ($roleMembers -contains $userName) {
                    if (($server.VersionMajor -lt 11 -and [string]::IsNullOrEmpty($destinationVersion)) -or ($DestinationVersion -in "SQLServer2000", "SQLServer2005", "SQLServer2008/2008R2")) {
                        $outsql += "EXEC sys.sp_addsrvrolemember @rolename=N'$roleName', @loginame=N'$userName'"
                    }
                    else {
                        $outsql += "ALTER SERVER ROLE [$roleName] ADD MEMBER [$userName]"
                    }
                }
            }

            if ($NoJobs -eq $false) {
                $ownedJobs = $server.JobServer.Jobs | Where-Object { $_.OwnerLoginName -eq $userName }

                foreach ($ownedJob in $ownedJobs) {
                    $outsql += "`n`rUSE msdb`n"
                    $outsql += "EXEC msdb.dbo.sp_update_job @job_name=N'$ownedJob', @owner_login_name=N'$userName'"
                }
            }

            if ($server.VersionMajor -ge 9) {
                # These operations are only supported by SQL Server 2005 and above.
                # Securables: Connect SQL, View any database, Administer Bulk Operations, etc.

                $perms = $server.EnumServerPermissions($userName)
                $outsql += "`n`rUSE master`n"
                foreach ($perm in $perms) {
                    $permState = $perm.permissionstate
                    $permType = $perm.PermissionType
                    $grantor = $perm.grantor

                    if ($permState -eq "GrantWithGrant") {
                        $grantWithGrant = "WITH GRANT OPTION"
                        $permState = "GRANT"
                    }
                    else {
                        $grantWithGrant = $null
                    }

                    $outsql += "$permState $permType TO [$userName] $grantWithGrant AS [$grantor]"
                }

                # Credential mapping. Credential removal not currently supported for Syncs.
                $loginCredentials = $server.Credentials | Where-Object { $_.Identity -eq $sourceLogin.Name }
                foreach ($credential in $loginCredentials) {
                    $credentialName = $credential.Name
                    $outsql += "PRINT '$userName is associated with the $credentialName credential'"
                }
            }

            if ($NoDatabases -eq $false) {
                $dbs = $sourceLogin.EnumDatabaseMappings()

                if ($Database) {
                    $dbs = $dbs | Where-Object { $_.DBName -in $Database }
                }

                # Adding database mappings and securables
                foreach ($db in $dbs) {
                    $dbName = $db.dbname
                    $sourceDb = $server.Databases[$dbName]
                    $dbUserName = $db.username

                    $outsql += "`r`nUSE [$dbName]`n"
                    try {
                        $sql = $server.Databases[$dbName].Users[$dbUserName].Script()
                        $outsql += $sql
                    }
                    catch {
                        Write-Message -Level Warning -Message "User cannot be found in selected database."
                    }

                    # Skipping updating dbowner

                    # Database Roles: db_owner, db_datareader, etc
                    foreach ($role in $sourceDb.Roles) {
                        if ($role.EnumMembers() -contains $dbUserName) {
                            $roleName = $role.Name
                            if (($server.VersionMajor -lt 11 -and [string]::IsNullOrEmpty($destinationVersion)) -or ($DestinationVersion -in "SQLServer2000", "SQLServer2005", "SQLServer2008/2008R2")) {
                                $outsql += "EXEC sys.sp_addrolemember @rolename=N'$roleName', @membername=N'$dbUserName'"
                            }
                            else {
                                $outsql += "ALTER ROLE [$roleName] ADD MEMBER [$dbUserName]"
                            }
                        }
                    }

                    # Connect, Alter Any Assembly, etc
                    $perms = $sourceDb.EnumDatabasePermissions($dbUserName)
                    foreach ($perm in $perms) {
                        $permState = $perm.PermissionState
                        $permType = $perm.PermissionType
                        $grantor = $perm.Grantor

                        if ($permState -eq "GrantWithGrant") {
                            $grantWithGrant = "WITH GRANT OPTION"
                            $permState = "GRANT"
                        }
                        else {
                            $grantWithGrant = $null
                        }

                        $outsql += "$permState $permType TO [$userName] $grantWithGrant AS [$grantor]"
                    }
                }
            }
        }
    }
    end {
        $sql = $sql | Where-Object { $_ -notlike "CREATE USER [dbo] FOR LOGIN * WITH DEFAULT_SCHEMA=[dbo]" }

        if ($ExcludeGoBatchSeparator) {
            $sql = $outsql
        }
        else {
            $sql = $outsql -join "`r`nGO`r`n"
            #add the final GO
            $sql += "`r`nGO"
        }

        if ($FilePath) {
            $sql | Out-File -Encoding UTF8 -FilePath $FilePath -Append:$Append -NoClobber:$NoClobber
            Get-ChildItem $FilePath
        }
        else {
            $sql
        }
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Export-SqlLogin
    }
}
tools\dbatools\functions\Export-DbaPfDataCollectorSetTemplate.ps1
function Export-DbaPfDataCollectorSetTemplate {
    <#
        .SYNOPSIS
            Exports a new Data Collector Set XML Template.

        .DESCRIPTION
            Exports a Data Collector Set XML Template from Get-DbaPfDataCollectorSet. Exports to "$home\Documents\Performance Monitor Templates" by default.

        .PARAMETER ComputerName
            The target computer. Defaults to localhost.

        .PARAMETER Credential
            Allows you to login to $ComputerName using alternative credentials. To use:

            $cred = Get-Credential, then pass $cred object to the -Credential parameter.

        .PARAMETER CollectorSet
            The name of the collector set(s) to export.

        .PARAMETER Path
            The path to export the file. Can be .xml or directory.

        .PARAMETER InputObject
            Accepts the object output by Get-DbaPfDataCollectorSetTemplate via the pipeline.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Performance, DataCollector
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Export-DbaPfDataCollectorSetTemplate

        .EXAMPLE
            Export-DbaPfDataCollectorSetTemplate -ComputerName sql2017 -Path C:\temp\pf

            Exports all data collector sets from to the C:\temp\pf folder.

        .EXAMPLE
            Get-DbaPfDataCollectorSet ComputerName sql2017 -CollectorSet 'System Correlation' | Export-DbaPfDataCollectorSetTemplate -Path C:\temp

            Exports the 'System Correlation' data collector set from sql2017 to C:\temp.
    #>
    [CmdletBinding()]
    param (
        [DbaInstance[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias("DataCollectorSet")]
        [string[]]$CollectorSet,
        [string]$Path = "$home\Documents\Performance Monitor Templates",
        [Parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [switch]$EnableException
    )
    process {
        if ($InputObject.Credential -and (Test-Bound -ParameterName Credential -Not)) {
            $Credential = $InputObject.Credential
        }

        if (-not $InputObject -or ($InputObject -and (Test-Bound -ParameterName ComputerName))) {
            foreach ($computer in $ComputerName) {
                $InputObject += Get-DbaPfDataCollectorSet -ComputerName $computer -Credential $Credential -CollectorSet $CollectorSet
            }
        }

        foreach ($object in $InputObject) {
            if (-not $object.DataCollectorSetObject) {
                Stop-Function -Message "InputObject is not of the right type. Please use Get-DbaPfDataCollectorSet."
                return
            }

            $csname = Remove-InvalidFileNameChars -Name $object.Name

            if ($path.EndsWith(".xml")) {
                $filename = $path
            }
            else {
                $filename = "$path\$csname.xml"
                if (-not (Test-Path -Path $path)) {
                    $null = New-Item -Type Directory -Path $path
                }
            }
            Write-Message -Level Verbose -Message "Wrote $csname to $filename."
            Set-Content -Path $filename -Value $object.Xml -Encoding Unicode
            Get-ChildItem -Path $filename
        }
    }
}
tools\dbatools\functions\Export-DbaRegisteredServer.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Export-DbaRegisteredServer {
    <#
        .SYNOPSIS
            Exports registered servers and registered server groups to file

        .DESCRIPTION
            Exports registered servers and registered server groups to file

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Group
            Exports a specific group.

        .PARAMETER CredentialPersistenceType
            Used to specify how the login and passwords are persisted. Valid values include None, PersistLoginName and PersistLoginNameAndPassword.

        .PARAMETER Path
            The path to the exported file. If no path is specified, one will be created.

        .PARAMETER InputObject
            Enables piping from Get-DbaRegisteredServer, Get-DbaRegisteredServerGroup, CSVs and other objects.

            If importing from CSV or other object, a column named ServerName is required. Optional columns include Name, Description and Group.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.

            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.

            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Chrissy LeMaire (@cl)
            Tags: RegisteredServer, CMS

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Export-DbaRegisteredServer

        .EXAMPLE
           Export-DbaRegisteredServer -SqlInstance sql2008

           Exports all Registered Server and Registered Server Groups on sql2008 to an automatically generated file name in the current directory

        .EXAMPLE
           Export-DbaRegisteredServer -SqlInstance sql2008 -Group hr\Seattle -Path C:\temp\Seattle.xml

           Exports all Registered Server and Registered Server Groups with the Seattle group within the HR group on sql2008 to C:\temp\Seattle.xml

        .EXAMPLE
           Get-DbaRegisteredServer -SqlInstance sql2008, sql2012 | Export-DbaRegisteredServer

           Exports all registered servers on sql2008 and sql2012. Warning - each one will have its own individual file. Consider piping groups.

        .EXAMPLE
           Get-DbaRegisteredServerGroup -SqlInstance sql2008, sql2012 | Export-DbaRegisteredServer

           Exports all registered servers on sql2008 and sql2012, organized by group.
    #>
    [CmdletBinding()]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [string]$Path,
        [ValidateSet("None", "PersistLoginName", "PersistLoginNameAndPassword")]
        [string]$CredentialPersistenceType = "None",
        [switch]$EnableException
    )
    begin {
        if ((Test-Bound -ParameterName Path)) {
            if ($Path -notmatch '\\') {
                $Path = ".\$Path"
            }

            $directory = Split-Path $Path
            if (-not (Test-Path $directory)) {
                New-Item -Path $directory -ItemType Directory
            }
        }
        else {
            $timeNow = (Get-Date -uformat "%m%d%Y%H%M%S")
        }
    }
    process {
        foreach ($instance in $SqlInstance) {
            $InputObject += Get-DbaRegisteredServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Id 1
        }

        foreach ($object in $InputObject) {
            try {
                if ($object -is [Microsoft.SqlServer.Management.RegisteredServers.RegisteredServersStore]) {
                    $object = Get-DbaRegisteredServerGroup -SqlInstance $object.ServerConnection.SqlConnectionObject -Id 1
                }

                if ($object -is [Microsoft.SqlServer.Management.RegisteredServers.RegisteredServer]) {
                    if ((Test-Bound -ParameterName Path -Not)) {
                        $servername = $object.SqlInstance.Replace('\', '$')
                        $regservername = $object.Name.Replace('\', '$')
                        $Path = "$serverName-regserver-$regservername-$timeNow.xml"
                    }
                    $object.Export($Path, $CredentialPersistenceType)
                }
                elseif ($object -is [Microsoft.SqlServer.Management.RegisteredServers.ServerGroup]) {
                    if ((Test-Bound -ParameterName Path -Not)) {
                        $servername = $object.SqlInstance.Replace('\', '$')
                        $regservergroup = $object.Name.Replace('\', '$')
                        $Path = "$serverName-reggroup-$regservergroup-$timeNow.xml"
                    }
                    $object.Export($Path, $CredentialPersistenceType)
                }
                else {
                    Stop-Function -Message "InputObject is not a registered server or server group" -Continue
                }
                Get-ChildItem $Path -ErrorAction Stop
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_
            }
        }
    }
}
tools\dbatools\functions\Export-DbaScript.ps1
function Export-DbaScript {
    <#
        .SYNOPSIS
            Exports scripts from SQL Management Objects (SMO)

        .DESCRIPTION
            Exports scripts from SQL Management Objects

        .PARAMETER InputObject
            A SQL Managment Object such as the one returned from Get-DbaLogin

        .PARAMETER Path
            The output filename and location. If no path is specified, one will be created. If the file already exists, the output will be appended.

        .PARAMETER Encoding
            Specifies the file encoding. The default is UTF8.

            Valid values are:
            -- ASCII: Uses the encoding for the ASCII (7-bit) character set.
            -- BigEndianUnicode: Encodes in UTF-16 format using the big-endian byte order.
            -- Byte: Encodes a set of characters into a sequence of bytes.
            -- String: Uses the encoding type for a string.
            -- Unicode: Encodes in UTF-16 format using the little-endian byte order.
            -- UTF7: Encodes in UTF-7 format.
            -- UTF8: Encodes in UTF-8 format.
            -- Unknown: The encoding type is unknown or invalid. The data can be treated as binary.

        .PARAMETER Passthru
            Output script to console

        .PARAMETER ScriptingOptionsObject
            An SMO Scripting Object that can be used to customize the output - see New-DbaScriptingOption

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed

        .PARAMETER NoClobber
            Do not overwrite file

        .PARAMETER Append
            Append to file

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration, Backup, Export

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Export-DbaScript

        .EXAMPLE
            Get-DbaAgentJob -SqlInstance sql2016 | Export-DbaScript

            Exports all jobs on the SQL Server sql2016 instance using a trusted connection - automatically determines filename as .\sql2016-Job-Export-date.sql

        .EXAMPLE
            Get-DbaAgentJob -SqlInstance sql2016 | Export-DbaScript -Path C:\temp\export.sql -Append

            Exports all jobs on the SQL Server sql2016 instance using a trusted connection - Will append the output to the file C:\temp\export.sql if it already exists

        .EXAMPLE
            Get-DbaAgentJob -SqlInstance sql2016 -Job syspolicy_purge_history, 'Hourly Log Backups' -SqlCredential (Get-Credential sqladmin) | Export-DbaScript -Path C:\temp\export.sql

            Exports only syspolicy_purge_history and 'Hourly Log Backups' to C:temp\export.sql and uses the SQL login "sqladmin" to login to sql2016

        .EXAMPLE
            Get-DbaAgentJob -SqlInstance sql2014 | Export-DbaJob -Passthru | ForEach-Object { $_.Replace('sql2014','sql2016') } | Set-Content -Path C:\temp\export.sql

            Exports jobs and replaces all instances of the servername "sql2014" with "sql2016" then writes to C:\temp\export.sql

        .EXAMPLE
            $options = New-DbaScriptingOption
            $options.ScriptDrops = $false
            $options.WithDependencies = $true
            Get-DbaTable -SqlInstance sql2017 -Database PerformanceStore | Export-DbaScript -ScriptingOptionsObject $options

            Exports Agent Jobs with the Scripting Options ScriptDrops set to $false and WithDependencies set to $true.
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [object[]]$InputObject,
        [Alias("ScriptingOptionObject")]
        [Microsoft.SqlServer.Management.Smo.ScriptingOptions]$ScriptingOptionsObject,
        [string]$Path,
        [ValidateSet('ASCII', 'BigEndianUnicode', 'Byte', 'String', 'Unicode', 'UTF7', 'UTF8', 'Unknown')]
        [string]$Encoding = 'UTF8',
        [switch]$Passthru,
        [switch]$NoClobber,
        [switch]$Append,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $executingUser = [Security.Principal.WindowsIdentity]::GetCurrent().Name
        $commandName = $MyInvocation.MyCommand.Name
        $timeNow = (Get-Date -uformat "%m%d%Y%H%M%S")
        $prefixArray = @()
    }

    process {
        foreach ($object in $InputObject) {

            $typename = $object.GetType().ToString()

            if ($typename.StartsWith('Microsoft.SqlServer.')) {
                $shortype = $typename.Split(".")[-1]
            }
            else {
                Stop-Function -Message "InputObject is of type $typename which is not a SQL Management Object. Only SMO objects are supported." -Category InvalidData -Target $object -Continue
            }

            if ($shortype -in "LinkedServer", "Credential", "Login") {
                Write-Message -Level Warning -Message "Support for $shortype is limited at this time. No passwords, hashed or otherwise, will be exported if they exist."
            }

            # Just gotta add the stuff that Nic Cain added to his script

            if ($shortype -eq "Configuration") {
                Write-Message -Level Warning -Message "Support for $shortype is limited at this time."
            }

            # Find the server object to pass on to the function
            $parent = $object.parent

            do {
                if ($parent.Urn.Type -ne "Server") {
                    $parent = $parent.Parent
                }
            }
            until (($parent.Urn.Type -eq "Server") -or (-not $parent))

            if (-not $parent) {
                Stop-Function -Message "Failed to find valid SMO server object in input: $object." -Category InvalidData -Target $object -Continue
            }

            try {
                $server = $parent
                $serverName = $server.Name.Replace('\', '$')

                if ($ScriptingOptionsObject) {
                    $scripter = New-Object Microsoft.SqlServer.Management.Smo.Scripter $server
                    $scripter.Options = $ScriptingOptionsObject
                }

                if (!$passthru) {
                    if ($path) {
                        $actualPath = $path
                    }
                    else {
                        $actualPath = "$serverName-$shortype-Export-$timeNow.sql"
                    }
                }

                $prefix = "/*`n`tCreated by $executingUser using dbatools $commandName for objects on $serverName at $(Get-Date)`n`tSee https://dbatools.io/$commandName for more information`n*/"

                if ($passthru) {
                    $prefix | Out-String
                }
                else {
                    if ($prefixArray -notcontains $actualPath) {

                        if ((Test-Path -Path $actualPath) -and $NoClobber) {
                            Stop-Function -Message "File already exists. If you want to overwrite it remove the -NoClobber parameter. If you want to append data, please Use -Append parameter." -Target $actualPath -Continue
                        }
                        #Only at the first output we use the passed variables Append & NoClobber. For this execution the next ones need to buse -Append
                        $prefix | Out-File -FilePath $actualPath -Encoding $encoding -Append:$Append -NoClobber:$NoClobber
                        $prefixArray += $actualPath
                    }
                }

                if ($Pscmdlet.ShouldProcess($env:computername, "Exporting $object from $server to $actualPath")) {
                    Write-Message -Level Verbose -Message "Exporting $object"

                    if ($passthru) {
                        if ($ScriptingOptionsObject) {
                            foreach ($script in $scripter.EnumScript($object)) {
                                $script | Out-String
                            }
                        }
                        else {
                            $object.Script() | Out-String
                        }
                    }
                    else {
                        if ($ScriptingOptionsObject) {
                            foreach ($script in $scripter.EnumScript($object)) {
                                $script | Out-File -FilePath $actualPath -Encoding $encoding -Append
                            }
                        }
                        else {
                            $object.Script() | Out-File -FilePath $actualPath -Encoding $encoding -Append
                        }
                    }
                }

                if (!$passthru) {
                    Write-Message -Level Output -Message "Exported $object on $($server.Name) to $actualPath"
                }
            }
            catch {
                $message = $_.Exception.InnerException.InnerException.InnerException.Message
                if (-not $message) {
                    $message = $_.Exception
                }
                Stop-Function -Message "Failure on $($server.Name) | $message" -Target $server
            }
        }
    }
}
tools\dbatools\functions\Export-DbaSpConfigure.ps1
function Export-DbaSpConfigure {
    <#
        .SYNOPSIS
            Exports advanced sp_configure global configuration options to sql file.

        .DESCRIPTION
            Exports advanced sp_configure global configuration options to sql file.

        .PARAMETER SqlInstance
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2005 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Path
            Specifies the path to a file which will contain the sp_configure queries necessary to replicate the configuration settings on another instance. This file is suitable for input into Import-DbaSPConfigure.

        .PARAMETER Whatif
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .NOTES
            Tags: SpConfig, Configure, Configuration
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            Export-DbaSpConfigure -SqlInstance sourceserver -Path C:\temp\sp_configure.sql

            Exports the SPConfigure settings on sourceserver to the file C:\temp\sp_configure.sql

        .OUTPUTS
            File to disk, and string path.

    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [string]$Path,
        [PSCredential]$SqlCredential
    )

    begin {
        $server = Connect-SqlInstance $sqlinstance $SqlCredential

        if ($server.versionMajor -lt 9) {
            Write-Error "Windows 2000 is not supported for sp_configure export."
            break
        }

        if ($path.length -eq 0) {
            $timenow = (Get-Date -uformat "%m%d%Y%H%M%S")
            $mydocs = [Environment]::GetFolderPath('MyDocuments')
            $path = "$mydocs\$($server.name.replace('\', '$'))-$timenow-sp_configure.sql"
        }

    }

    process {
        try {
            Set-Content -Path $path "EXEC sp_configure 'show advanced options' , 1;  RECONFIGURE WITH OVERRIDE"
        }
        catch {
            throw "Can't write to $path"
        }

        $server.Configuration.ShowAdvancedOptions.ConfigValue = $true
        $server.Configuration.Alter($true)
        foreach ($sourceprop in $server.Configuration.Properties) {
            $displayname = $sourceprop.DisplayName
            $configvalue = $sourceprop.ConfigValue
            Add-Content -Path $path "EXEC sp_configure '$displayname' , $configvalue;"
        }
        Add-Content -Path $path "EXEC sp_configure 'show advanced options' , 0;"
        Add-Content -Path $Path "RECONFIGURE WITH OVERRIDE"
        $server.Configuration.ShowAdvancedOptions.ConfigValue = $false
        $server.Configuration.Alter($true)
        return $path
    }

    end {
        $server.ConnectionContext.Disconnect()

        If ($Pscmdlet.ShouldProcess("console", "Showing finished message")) {
            Write-Output "Server configuration export finished"
        }
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Export-SqlSpConfigure
    }
}
tools\dbatools\functions\Export-DbaUser.ps1
function Export-DbaUser {
    <#
        .SYNOPSIS
            Exports users creation and its permissions to a T-SQL file or host.

        .DESCRIPTION
            Exports users creation and its permissions to a T-SQL file or host. Export includes user, create and add to role(s), database level permissions, object level permissions.

        .PARAMETER SqlInstance
            The SQL Server instance name. SQL Server 2000 and above supported.

        .PARAMETER SqlCredential
            Allows you to login to servers using alternative credentials

            $scred = Get-Credential, then pass $scred object to the -SqlCredential parameter

            Windows Authentication will be used if SqlCredential is not specified

        .PARAMETER Database
            The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto-populated from the server

        .PARAMETER User
            Export only the specified database user(s). If not specified will export all users from the database(s)

        .PARAMETER DestinationVersion
            To say to which version the script should be generated. If not specified will use database compatibility level

        .PARAMETER FilePath
            The file to write to.

        .PARAMETER NoClobber
            Do not overwrite file

        .PARAMETER Append
            Append to file

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER ScriptingOptionsObject
            A Microsoft.SqlServer.Management.Smo.ScriptingOptions object with the options that you want to use to generate the t-sql script.
            You can use the NEw-DbaScriptingOption to generate it.

        .PARAMETER ExcludeGoBatchSeparator
            If specified, will NOT script the 'GO' batch separator.

        .NOTES
            Tags: User, Export
            Author: Claudio Silva (@ClaudioESSilva)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Export-DbaUser

        .EXAMPLE
            Export-DbaUser -SqlInstance sql2005 -FilePath C:\temp\sql2005-users.sql

            Exports SQL for the users in server "sql2005" and writes them to the file "C:\temp\sql2005-users.sql"

        .EXAMPLE
            Export-DbaUser -SqlInstance sqlserver2014a $scred -FilePath C:\temp\users.sql -Append

            Authenticates to sqlserver2014a using SQL Authentication. Exports all users to C:\temp\users.sql, and appends to the file if it exists. If not, the file will be created.

        .EXAMPLE
            Export-DbaUser -SqlInstance sqlserver2014a -User User1, User2 -FilePath C:\temp\users.sql

            Exports ONLY users User1 and User2 fron sqlsever2014a to the file  C:\temp\users.sql

        .EXAMPLE
            Export-DbaUser -SqlInstance sqlserver2008 -User User1 -FilePath C:\temp\users.sql -DestinationVersion SQLServer2016

            Exports user User1 fron sqlsever2008 to the file C:\temp\users.sql with sintax to run on SQL Server 2016

        .EXAMPLE
            Export-DbaUser -SqlInstance sqlserver2008 -Database db1,db2 -FilePath C:\temp\users.sql

            Exports ONLY users from db1 and db2 database on sqlserver2008 server, to the C:\temp\users.sql file.

        .EXAMPLE
            $options = New-DbaScriptingOption
            $options.ScriptDrops = $false
            $options.WithDependencies = $true

            Export-DbaUser -SqlInstance sqlserver2008 -Database db1,db2 -FilePath C:\temp\users.sql -ScriptingOptionsObject $options

            Exports ONLY users from db1 and db2 database on sqlserver2008 server, to the C:\temp\users.sql file.
            It will not script drops but will script dependencies.

        .EXAMPLE
            Export-DbaUser -SqlInstance sqlserver2008 -Database db1,db2 -FilePath C:\temp\users.sql -ExcludeGoBatchSeparator

            Exports ONLY users from db1 and db2 database on sqlserver2008 server, to the C:\temp\users.sql file without the 'GO' batch separator.

    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    [OutputType([String])]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [object[]]$User,
        [ValidateSet('SQLServer2000', 'SQLServer2005', 'SQLServer2008/2008R2', 'SQLServer2012', 'SQLServer2014', 'SQLServer2016', 'SQLServer2017')]
        [string]$DestinationVersion,
        [Alias("OutFile", "Path", "FileName")]
        [string]$FilePath,
        [Alias("NoOverwrite")]
        [switch]$NoClobber,
        [switch]$Append,
        [Alias('Silent')]
        [switch]$EnableException,
        [Microsoft.SqlServer.Management.Smo.ScriptingOptions]$ScriptingOptionsObject = $null,
        [switch]$ExcludeGoBatchSeparator
    )

    begin {
        if ($FilePath) {
            if ($FilePath -notlike "*\*") { $FilePath = ".\$filepath" }
            $directory = Split-Path $FilePath
            $exists = Test-Path $directory

            if ($exists -eq $false) {
                Stop-Function -Message "Parent directory $directory does not exist"
                return
            }
        }

        $outsql = @()

        $versions = @{
            'SQLServer2000'        = 'Version80'
            'SQLServer2005'        = 'Version90'
            'SQLServer2008/2008R2' = 'Version100'
            'SQLServer2012'        = 'Version110'
            'SQLServer2014'        = 'Version120'
            'SQLServer2016'        = 'Version130'
            'SQLServer2017'        = 'Version140'
        }

        $versionName = @{
            'Version80'  = 'SQLServer2000'
            'Version90'  = 'SQLServer2005'
            'Version100' = 'SQLServer2008/2008R2'
            'Version110' = 'SQLServer2012'
            'Version120' = 'SQLServer2014'
            'Version130' = 'SQLServer2016'
            'Version140' = 'SQLServer2017'
        }

    }
    process {
        if (Test-FunctionInterrupt) { return }

        try {
            Write-Message -Level Verbose -Message "Connecting to $sqlinstance"
            $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
        }

        if (!$database) {
            $databases = $server.Databases | Where-Object { $ExcludeDatabase -notcontains $_.Name -and $_.IsAccessible -eq $true }
        }
        else {
            if ($pipedatabase) {
                $source = $pipedatabase[0].parent.name
                $databases = $pipedatabase.name
            }
            else {
                $databases = $server.Databases | Where-Object { $_.IsAccessible -eq $true -and ($database -contains $_.Name) }
            }
        }

        if ($exclude) {
            $databases = $databases | Where-Object Name -notin $ExcludeDatabase
        }

        if (@($databases).Count -gt 0) {

            #Database Permissions
            foreach ($db in $databases) {
                if ([string]::IsNullOrEmpty($destinationVersion)) {
                    #Get compatibility level for scripting the objects
                    $scriptVersion = $db.CompatibilityLevel
                }
                else {
                    $scriptVersion = $versions[$destinationVersion]
                }
                $versionNameDesc = $versionName[$scriptVersion.ToString()]

                #If not passed create new ScriptingOption. Otherwise use the one that was passed
                if ($null -eq $ScriptingOptionsObject) {
                    $ScriptingOptionsObject = New-DbaScriptingOption
                    $ScriptingOptionsObject.TargetServerVersion = [Microsoft.SqlServer.Management.Smo.SqlServerVersion]::$scriptVersion
                    $ScriptingOptionsObject.AllowSystemObjects = $false
                    $ScriptingOptionsObject.IncludeDatabaseRoleMemberships = $true
                    $ScriptingOptionsObject.ContinueScriptingOnError = $false
                    $ScriptingOptionsObject.IncludeDatabaseContext = $false
                    $ScriptingOptionsObject.IncludeIfNotExists = $true
                }

                Write-Message -Level Output -Message "Validating users on database $db"

                if ($User.Count -eq 0) {
                    $users = $db.Users | Where-Object { $_.IsSystemObject -eq $false -and $_.Name -notlike "##*" }
                }
                else {
                    if ($pipedatabase) {
                        $source = $pipedatabase[3].parent.name
                        $users = $pipedatabase.name
                    }
                    else {
                        $users = $db.Users | Where-Object { $User -contains $_.Name -and $_.IsSystemObject -eq $false -and $_.Name -notlike "##*" }
                    }
                }
                # Store roles between users so if we hit the same one we dont create it again
                $roles = @()
                if ($users.Count -gt 0) {
                    foreach ($dbuser in $users) {
                        Write-Message -Level Output -Message "Generating script for user $dbuser"

                        #setting database
                        $outsql += "USE [" + $db.Name + "]"

                        try {
                            #Fixed Roles #Dependency Issue. Create Role, before add to role.
                            foreach ($rolePermission in ($db.Roles | Where-Object { $_.IsFixedRole -eq $false })) {
                                foreach ($rolePermissionScript in $rolePermission.Script($ScriptingOptionsObject)) {
                                    if ($rolePermission.ToString() -notin $roles) {
                                        $roles += , $rolePermission.ToString()
                                        $outsql += "$($rolePermissionScript.ToString())"
                                    }

                                }
                            }

                            #Database Create User(s) and add to Role(s)
                            foreach ($dbUserPermissionScript in $dbuser.Script($ScriptingOptionsObject)) {
                                if ($dbuserPermissionScript.Contains("sp_addrolemember")) {
                                    $execute = "EXEC "
                                }
                                else {
                                    $execute = ""
                                }
                                $outsql += "$execute$($dbUserPermissionScript.ToString())"
                            }

                            #Database Permissions
                            foreach ($databasePermission in $db.EnumDatabasePermissions() | Where-Object { @("sa", "dbo", "information_schema", "sys") -notcontains $_.Grantee -and $_.Grantee -notlike "##*" -and ($dbuser.Name -contains $_.Grantee) }) {
                                if ($databasePermission.PermissionState -eq "GrantWithGrant") {
                                    $withGrant = " WITH GRANT OPTION"
                                    $grantDatabasePermission = 'GRANT'
                                }
                                else {
                                    $withGrant = " "
                                    $grantDatabasePermission = $databasePermission.PermissionState.ToString().ToUpper()
                                }

                                $outsql += "$($grantDatabasePermission) $($databasePermission.PermissionType) TO [$($databasePermission.Grantee)]$withGrant AS [$($databasePermission.Grantor)];"
                            }

                            #Database Object Permissions
                            # NB: This is a bit of a mess for a couple of reasons
                            # 1. $db.EnumObjectPermissions() doesn't enumerate all object types
                            # 2. Some (x)Collection types can have EnumObjectPermissions() called
                            #    on them directly (e.g. AssemblyCollection); others can't (e.g.
                            #    ApplicationRoleCollection). Those that can't we iterate the
                            #    collection explicitly and add each object's permission.

                            $perms = New-Object System.Collections.ArrayList

                            $null = $perms.AddRange($db.EnumObjectPermissions($dbuser.Name))

                            foreach ($item in $db.ApplicationRoles) {
                                $null = $perms.AddRange($item.EnumObjectPermissions($dbuser.Name))
                            }

                            foreach ($item in $db.Assemblies) {
                                $null = $perms.AddRange($item.EnumObjectPermissions($dbuser.Name))
                            }

                            foreach ($item in $db.Certificates) {
                                $null = $perms.AddRange($item.EnumObjectPermissions($dbuser.Name))
                            }

                            foreach ($item in $db.DatabaseRoles) {
                                $null = $perms.AddRange($item.EnumObjectPermissions($dbuser.Name))
                            }

                            foreach ($item in $db.FullTextCatalogs) {
                                $null = $perms.AddRange($item.EnumObjectPermissions($dbuser.Name))
                            }

                            foreach ($item in $db.FullTextStopLists) {
                                $null = $perms.AddRange($item.EnumObjectPermissions($dbuser.Name))
                            }

                            foreach ($item in $db.SearchPropertyLists) {
                                $null = $perms.AddRange($item.EnumObjectPermissions($dbuser.Name))
                            }

                            foreach ($item in $db.ServiceBroker.MessageTypes) {
                                $null = $perms.AddRange($item.EnumObjectPermissions($dbuser.Name))
                            }

                            foreach ($item in $db.RemoteServiceBindings) {
                                $null = $perms.AddRange($item.EnumObjectPermissions($dbuser.Name))
                            }

                            foreach ($item in $db.ServiceBroker.Routes) {
                                $null = $perms.AddRange($item.EnumObjectPermissions($dbuser.Name))
                            }

                            foreach ($item in $db.ServiceBroker.ServiceContracts) {
                                $null = $perms.AddRange($item.EnumObjectPermissions($dbuser.Name))
                            }

                            foreach ($item in $db.ServiceBroker.Services) {
                                $null = $perms.AddRange($item.EnumObjectPermissions($dbuser.Name))
                            }

                            if ($scriptVersion -ne "Version80") {
                                foreach ($item in $db.AsymmetricKeys) {
                                    $null = $perms.AddRange($item.EnumObjectPermissions($dbuser.Name))
                                }
                            }

                            foreach ($item in $db.SymmetricKeys) {
                                $null = $perms.AddRange($item.EnumObjectPermissions($dbuser.Name))
                            }

                            foreach ($item in $db.XmlSchemaCollections) {
                                $null = $perms.AddRange($item.EnumObjectPermissions($dbuser.Name))
                            }

                            foreach ($objectPermission in $perms | Where-Object { @("sa", "dbo", "information_schema", "sys") -notcontains $_.Grantee -and $_.Grantee -notlike "##*" -and $_.Grantee -eq $dbuser.Name }) {
                                switch ($objectPermission.ObjectClass) {
                                    'ApplicationRole' {
                                        $object = 'APPLICATION ROLE::[{0}]' -f $objectPermission.ObjectName
                                    }
                                    'AsymmetricKey' {
                                        $object = 'ASYMMETRIC KEY::[{0}]' -f $objectPermission.ObjectName
                                    }
                                    'Certificate' {
                                        $object = 'CERTIFICATE::[{0}]' -f $objectPermission.ObjectName
                                    }
                                    'DatabaseRole' {
                                        $object = 'ROLE::[{0}]' -f $objectPermission.ObjectName
                                    }
                                    'FullTextCatalog' {
                                        $object = 'FULLTEXT CATALOG::[{0}]' -f $objectPermission.ObjectName
                                    }
                                    'FullTextStopList' {
                                        $object = 'FULLTEXT STOPLIST::[{0}]' -f $objectPermission.ObjectName
                                    }
                                    'MessageType' {
                                        $object = 'Message Type::[{0}]' -f $objectPermission.ObjectName
                                    }
                                    'ObjectOrColumn' {
                                        if ($scriptVersion -ne "Version80") {
                                            $object = 'OBJECT::[{0}].[{1}]' -f $objectPermission.ObjectSchema, $objectPermission.ObjectName
                                            if ($null -ne $objectPermission.ColumnName) {
                                                $object += '([{0}])' -f $objectPermission.ColumnName
                                            }
                                        }
                                        #At SQL Server 2000 OBJECT did not exists
                                        else {
                                            $object = '[{0}].[{1}]' -f $objectPermission.ObjectSchema, $objectPermission.ObjectName
                                        }
                                    }
                                    'RemoteServiceBinding' {
                                        $object = 'REMOTE SERVICE BINDING::[{0}]' -f $objectPermission.ObjectName
                                    }
                                    'Schema' {
                                        $object = 'SCHEMA::[{0}]' -f $objectPermission.ObjectName
                                    }
                                    'SearchPropertyList' {
                                        $object = 'SEARCH PROPERTY LIST::[{0}]' -f $objectPermission.ObjectName
                                    }
                                    'Service' {
                                        $object = 'SERVICE::[{0}]' -f $objectPermission.ObjectName
                                    }
                                    'ServiceContract' {
                                        $object = 'CONTRACT::[{0}]' -f $objectPermission.ObjectName
                                    }
                                    'ServiceRoute' {
                                        $object = 'ROUTE::[{0}]' -f $objectPermission.ObjectName
                                    }
                                    'SqlAssembly' {
                                        $object = 'ASSEMBLY::[{0}]' -f $objectPermission.ObjectName
                                    }
                                    'SymmetricKey' {
                                        $object = 'SYMMETRIC KEY::[{0}]' -f $objectPermission.ObjectName
                                    }
                                    'User' {
                                        $object = 'USER::[{0}]' -f $objectPermission.ObjectName
                                    }
                                    'UserDefinedType' {
                                        $object = 'TYPE::[{0}].[{1}]' -f $objectPermission.ObjectSchema, $objectPermission.ObjectName
                                    }
                                    'XmlNamespace' {
                                        $object = 'XML SCHEMA COLLECTION::[{0}]' -f $objectPermission.ObjectName
                                    }
                                }

                                if ($objectPermission.PermissionState -eq "GrantWithGrant") {
                                    $withGrant = " WITH GRANT OPTION"
                                    $grantObjectPermission = 'GRANT'
                                }
                                else {
                                    $withGrant = " "
                                    $grantObjectPermission = $objectPermission.PermissionState.ToString().ToUpper()
                                }

                                $outsql += "$grantObjectPermission $($objectPermission.PermissionType) ON $object TO [$($objectPermission.Grantee)]$withGrant AS [$($objectPermission.Grantor)];"
                            }

                        }
                        catch {
                            Stop-Function -Message "This user may be using functionality from $($versionName[$db.CompatibilityLevel.ToString()]) that does not exist on the destination version ($versionNameDesc)." -Continue -InnerErrorRecord $_ -Target $db
                        }
                    }
                }
                else {
                    Write-Message -Level Output -Message "No users found on database '$db'"
                }

                #reset collection
                $users = $null
            }
        }
        else {
            Write-Message -Level Output -Message "No users found on instance '$server'"
        }
    }

    end {
        if (Test-FunctionInterrupt) { return }

        if ($ExcludeGoBatchSeparator) {
            $sql = $outsql
        }
        else {
            $sql = $outsql -join "`r`nGO`r`n"
            #add the final GO
            $sql += "`r`nGO"
        }

        if ($FilePath) {
            $sql | Out-File -Encoding UTF8 -FilePath $FilePath -Append:$Append -NoClobber:$NoClobber
        }
        else {
            $sql
        }
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Export-SqlUser
    }
}
tools\dbatools\functions\Export-DbaXECsv.ps1
function Export-DbaXECsv {
    <#
        .SYNOPSIS
            Exports Extended Events to a CSV file.

        .DESCRIPTION
            Exports Extended Events to a CSV file.

        .PARAMETER Path
            Specifies the InputObject to the output CSV file

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER InputObject
            Allows Piping

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Author: Gianluca Sartori (@spaghettidba)

            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
            SmartTarget: by Gianluca Sartori (@spaghettidba)

        .LINK
            https://dbatools.io/Export-DbaXECsv

        .EXAMPLE
            Get-ChildItem -Path C:\temp\sample.xel | Export-DbaXECsv -Path c:\temp\sample.csv

            Writes Extended Events data to the file "C:\temp\events.csv".

         .EXAMPLE
            Get-DbaXESession -SqlInstance sql2014 -Session deadlocks | Export-DbaXECsv -Path c:\temp\events.csv

            Writes Extended Events data to the file "C:\temp\events.csv".
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias('FullName')]
        [object[]]$InputObject,
        [parameter(Mandatory)]
        [string]$Path,
        [switch]$EnableException
    )
    begin {
        try {
            Add-Type -Path "$script:PSModuleRoot\bin\XESmartTarget\XESmartTarget.Core.dll" -ErrorAction Stop
        }
        catch {
            Stop-Function -Message "Could not load XESmartTarget.Core.dll" -ErrorRecord $_ -Target "XESmartTarget"
            return
        }

        function Get-FileFromXE ($InputObject) {
            if ($InputObject.TargetFile) {
                if ($InputObject.TargetFile.Length -eq 0) {
                    Stop-Function -Message "This session does not have an associated Target File."
                    return
                }

                $instance = [dbainstance]$InputObject.ComputerName

                if ($instance.IsLocalHost) {
                    $xelpath = $InputObject.TargetFile
                }
                else {
                    $xelpath = $InputObject.RemoteTargetFile
                }

                if ($xelpath -notmatch ".xel") {
                    $xelpath = "$xelpath*.xel"
                }

                try {
                    Get-ChildItem -Path $xelpath -ErrorAction Stop
                }
                catch {
                    Stop-Function -Message "Failure" -ErrorRecord $_
                }
            }
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }

        $getfiles = Get-FileFromXE $InputObject

        if ($getfiles) {
            $InputObject += $getfiles
        }

        foreach ($file in $InputObject) {
            if ($file -is [System.String]) {
                $currentfile = $file
            }
            elseif ($file -is [System.IO.FileInfo]) {
                $currentfile = $file.FullName
            }
            elseif ($file -is [Microsoft.SqlServer.Management.XEvent.Session]) {
                # it was taken care of above
                continue
            }
            else {
                Stop-Function -Message "Unsupported file type."
                return
            }

            $accessible = Test-Path -Path $currentfile
            $whoami = whoami

            if (-not $accessible) {
                if ($file.Status -eq "Stopped") { continue }
                Stop-Function -Continue -Message "$currentfile cannot be accessed from $($env:COMPUTERNAME). Does $whoami have access?"
            }

            if (-not (Test-Path $Path)) {
                if ([String]::IsNullOrEmpty([IO.Path]::GetExtension($Path))) {
                    New-Item $Path -ItemType directory | Out-Null
                    $outDir = $Path
                    $outFile = [IO.Path]::GetFileNameWithoutExtension($currentfile) + ".csv"
                }
                else {
                    $outDir = [IO.Path]::GetDirectoryName($Path)
                    $outFile = [IO.Path]::GetFileName($Path)
                }
            }
            else {
                if ((Get-Item $Path) -is [System.IO.DirectoryInfo]) {
                    $outDir = $Path
                    $outFile = [IO.Path]::GetFileNameWithoutExtension($currentfile) + ".csv"
                }
                else {
                    $outDir = [IO.Path]::GetDirectoryName($Path)
                    $outFile = [IO.Path]::GetFileName($Path)
                }
            }

            $adapter = New-Object XESmartTarget.Core.Utils.XELFileCSVAdapter
            $adapter.InputFile = $currentfile
            $adapter.OutputFile = (Join-Path $outDir $outFile)

            try {
                $adapter.Convert()
                $file = Get-ChildItem -Path $adapter.OutputFile

                if ($file.Length -eq 0) {
                    Remove-Item -Path $adapter.OutputFile
                }
                else {
                    $file
                }
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_ -Target "XESmartTarget" -Continue
            }
        }
    }
}
tools\dbatools\functions\Export-DbaXESessionTemplate.ps1
function Export-DbaXESessionTemplate {
    <#
        .SYNOPSIS
            Exports an XESession XML Template.

        .DESCRIPTION
            Exports an XESession XML Template either from the dbatools repository or a file you specify. Exports to "$home\Documents\SQL Server Management Studio\Templates\XEventTemplates" by default

        .PARAMETER SqlInstance
            Target SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Session
            The Name of the session(s) to export.

        .PARAMETER Path
            The path to export the file into. Can be .xml or directory.

        .PARAMETER InputObject
            Specifies an XE Session output by Get-DbaXESession.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Export-DbaXESessionTemplate

        .EXAMPLE
            Export-DbaXESessionTemplate -SqlInstance sql2017 -Path C:\temp\xe

            Exports XE Session Template to the C:\temp\xe folder.

        .EXAMPLE
            Get-DbaXESession -SqlInstance sql2017 -Session session_health | Export-DbaXESessionTemplate -Path C:\temp

            Returns a new XE Session object from sql2017 then adds an event, an action then creates it.

    #>
    [CmdletBinding()]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Session,
        [string]$Path = "$home\Documents\SQL Server Management Studio\Templates\XEventTemplates",
        [Parameter(ValueFromPipeline)]
        [Microsoft.SqlServer.Management.XEvent.Session[]]$InputObject,
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $InputObject += Get-DbaXESession -SqlInstance $instance -SqlCredential $SqlCredential -Session $Session -EnableException
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
        }

        foreach ($xes in $InputObject) {
            $xesname = Remove-InvalidFileNameChars -Name $xes.Name

            if (-not (Test-Path -Path $Path)) {
                Stop-Function -Message "$Path does not exist." -Target $Path
            }

            if ($path.EndsWith(".xml")) {
                $filename = $path
            }
            else {
                $filename = "$path\$xesname.xml"
            }
            Write-Message -Level Verbose -Message "Wrote $xesname to $filename"
            [Microsoft.SqlServer.Management.XEvent.XEStore]::SaveSessionToTemplate($xes, $filename, $true)
            Get-ChildItem -Path $filename
        }
    }
}
tools\dbatools\functions\Find-DbaAgentJob.ps1
function Find-DbaAgentJob {
    <#
        .SYNOPSIS
            Find-DbaAgentJob finds agent job/s that fit certain search filters.

        .DESCRIPTION
            This command filters SQL Agent jobs giving the DBA a list of jobs that may need attention or could possibly be options for removal.

        .PARAMETER SqlInstance
            The SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SqlCredential
            Allows you to login to servers using SQL Logins as opposed to Windows Auth/Integrated/Trusted.

        .PARAMETER JobName
            Filter agent jobs to only the name(s) you list.
            Supports regular expression (e.g. MyJob*) being passed in.

        .PARAMETER ExcludeJobName
            Allows you to enter an array of agent job names to ignore

        .PARAMETER StepName
            Filter based on StepName.
            Supports regular expression (e.g. MyJob*) being passed in.

        .PARAMETER LastUsed
            Find all jobs that havent ran in the INT number of previous day(s)

        .PARAMETER IsDisabled
            Find all jobs that are disabled

        .PARAMETER IsFailed
            Find all jobs that have failed

        .PARAMETER IsNotScheduled
            Find all jobs with no schedule assigned

        .PARAMETER IsNoEmailNotification
            Find all jobs without email notification configured

        .PARAMETER Category
            Filter based on agent job categories

        .PARAMETER Owner
            Filter based on owner of the job/s

        .PARAMETER Since
            Datetime object used to narrow the results to a date

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Agent, Job
            Author: Stephen Bennett (https://sqlnotesfromtheunderground.wordpress.com/)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Find-DbaAgentJob

        .EXAMPLE
            Find-DbaAgentJob -SqlInstance Dev01 -JobName backup*

            Returns all agent job(s) that have backup in the name

        .EXAMPLE
            Find-DbaAgentJob -SqlInstance Dev01, Dev02 -JobName Mybackup

            Returns all agent job(s) that are named exactly Mybackup

        .EXAMPLE
            Find-DbaAgentJob -SqlInstance Dev01 -LastUsed 10

            Returns all agent job(s) that have not ran in 10 days

        .EXAMPLE
            Find-DbaAgentJob -SqlInstance Dev01 -IsDisabled -IsNoEmailNotification -IsNotScheduled

            Returns all agent job(s) that are either disabled, have no email notification or don't have a schedule. returned with detail

        .EXAMPLE
            $servers | Find-DbaAgentJob -IsFailed | Start-DbaAgentJob

            Finds all failed job then starts them. Consider using a -WhatIf at the end of Start-DbaAgentJob to see what it'll do first

        .EXAMPLE
            Find-DbaAgentJob -SqlInstance Dev01 -LastUsed 10 -Exclude "Yearly - RollUp Workload", "SMS - Notification"

            Returns all agent jobs that havent ran in the last 10 ignoring jobs "Yearly - RollUp Workload" and "SMS - Notification"

        .EXAMPLE
            Find-DbaAgentJob -SqlInstance Dev01 -Category "REPL-Distribution", "REPL-Snapshot" -Detailed | Format-Table -AutoSize -Wrap

            Returns all job/s on Dev01 that are in either category "REPL-Distribution" or "REPL-Snapshot" with detailed output

        .EXAMPLE
            Find-DbaAgentJob -SqlInstance Dev01, Dev02 -IsFailed -Since '7/1/2016 10:47:00'

            Returns all agent job(s) that have failed since July of 2016 (and still have history in msdb)

        .EXAMPLE
            Get-DbaRegisteredServer -SqlInstance CMSServer -Group Production | Find-DbaAgentJob -Disabled -IsNotScheduled | Format-Table -AutoSize -Wrap

            Queries CMS server to return all SQL instances in the Production folder and then list out all agent jobs that have either been disabled or have no schedule.
    #>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]
        $SqlCredential,
        [Alias("Name")]
        [string[]]$JobName,
        [string[]]$ExcludeJobName,
        [string[]]$StepName,
        [int]$LastUsed,
        [Alias("Disabled")]
        [switch]$IsDisabled,
        [Alias("Failed")]
        [switch]$IsFailed,
        [Alias("NoSchedule")]
        [switch]$IsNotScheduled,
        [Alias("NoEmailNotification")]
        [switch]$IsNoEmailNotification,
        [string[]]$Category,
        [string]$Owner,
        [datetime]$Since,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        if ($IsFailed, [boolean]$JobName, [boolean]$StepName, [boolean]$LastUsed.ToString(), $IsDisabled, $IsNotScheduled, $IsNoEmailNotification, [boolean]$Category, [boolean]$Owner, [boolean]$ExcludeJobName -notcontains $true) {
            Stop-Function -Message "At least one search term must be specified"
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Running Scan on: $instance"

            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $jobs = $server.JobServer.jobs
            $output = @()

            if ($IsFailed) {
                Write-Message -Level Verbose -Message "Checking for failed jobs."
                $output += $jobs | Where-Object LastRunOutcome -eq "Failed"
            }

            if ($JobName) {
                Write-Message -Level Verbose -Message "Retrieving jobs by their name."
                $output += Get-JobList -SqlInstance $server -JobFilter $JobName
            }

            if ($StepName) {
                Write-Message -Level Verbose -Message "Retrieving jobs by their step names."
                $output += Get-JobList -SqlInstance $server -StepFilter $StepName
            }

            if ($LastUsed) {
                $DaysBack = $LastUsed * -1
                $SinceDate = (Get-date).AddDays($DaysBack)
                Write-Message -Level Verbose -Message "Finding job/s not ran in last $LastUsed days"
                $output += $jobs | Where-Object { $_.LastRunDate -le $SinceDate }
            }

            if ($IsDisabled) {
                Write-Message -Level Verbose -Message "Finding job/s that are disabled"
                $output += $jobs | Where-Object IsEnabled -eq $false
            }

            if ($IsNotScheduled) {
                Write-Message -Level Verbose -Message "Finding job/s that have no schedule defined"
                $output += $jobs | Where-Object HasSchedule -eq $false
            }
            if ($IsNoEmailNotification) {
                Write-Message -Level Verbose -Message "Finding job/s that have no email operator defined"
                $output += $jobs | Where-Object { [string]::IsNullOrEmpty($_.OperatorToEmail) -eq $true }
            }

            if ($Category) {
                Write-Message -Level Verbose -Message "Finding job/s that have the specified category defined"
                $output += $jobs | Where-Object { $Category -contains $_.Category }
            }

            if ($Owner) {
                Write-Message -Level Verbose -Message "Finding job/s with owner critera"
                if ($Owner -match "-") {
                    $OwnerMatch = $Owner -replace "-", ""
                    Write-Message -Level Verbose -Message "Checking for jobs that NOT owned by: $OwnerMatch"
                    $output += $server.JobServer.jobs | Where-Object { $OwnerMatch -notcontains $_.OwnerLoginName }
                }
                else {
                    Write-Message -Level Verbose -Message "Checking for jobs that are owned by: $owner"
                    $output += $server.JobServer.jobs | Where-Object { $Owner -contains $_.OwnerLoginName }
                }
            }

            if ($Exclude) {
                Write-Message -Level Verbose -Message "Excluding job/s based on Exclude"
                $output = $output | Where-Object { $Exclude -notcontains $_.Name }
            }

            if ($Since) {
                #$Since = $Since.ToString("yyyy-MM-dd HH:mm:ss")
                Write-Message -Level Verbose -Message "Getting only jobs whose LastRunDate is greater than or equal to $since"
                $output = $output | Where-Object { $_.LastRunDate -ge $since }
            }

            $jobs = $output | Select-Object -Unique

            foreach ($job in $jobs) {
                Add-Member -Force -InputObject $job -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                Add-Member -Force -InputObject $job -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                Add-Member -Force -InputObject $job -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                Add-Member -Force -InputObject $job -MemberType NoteProperty -Name JobName -value $job.Name


                Select-DefaultView -InputObject $job -Property ComputerName, InstanceName, SqlInstance, Name, Category, OwnerLoginName, CurrentRunStatus, CurrentRunRetryAttempt, 'IsEnabled as Enabled', LastRunDate, LastRunOutcome, DateCreated, HasSchedule, OperatorToEmail, 'DateCreated as CreateDate'
            }
        }
    }
}
tools\dbatools\functions\Find-DbaBackup.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Find-DbaBackup {
    <#
        .SYNOPSIS
            Finds SQL Server backups on disk.

        .DESCRIPTION
            Provides all of the same functionality for finding SQL backups to remove from disk as a standard maintenance plan would.

            As an addition you have the ability to check the Archive bit on files before deletion. This will allow you to ensure backups have been archived to your archive location before removal.

        .PARAMETER Path
            Specifies the name of the base level folder to search for backup files.

        .PARAMETER BackupFileExtension
            Specifies the filename extension of the backup files you wish to find (typically 'bak', 'trn' or 'log'). Do not include the period.

        .PARAMETER RetentionPeriod
            Specifies the retention period for backup files. Correct format is ##U.

            ## is the retention value and must be an integer value
            U signifies the units where the valid units are:
            h = hours
            d = days
            w = weeks
            m = months

            Formatting Examples:
            '48h' = 48 hours
            '7d' = 7 days
            '4w' = 4 weeks
            '1m' = 1 month

        .PARAMETER CheckArchiveBit
            If this switch is enabled, the filesystem Archive bit is checked.
            If this bit is set (which translates to "it has not been backed up to another location yet"), the file won't be included.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Backup
            Author: Chris Sommer, @cjsommer, www.cjsommer.com

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Find-DbaBackup

        .EXAMPLE
            Find-DbaBackup -Path 'C:\MSSQL\SQL Backup\' -BackupFileExtension trn -RetentionPeriod 48h

            '*.trn' files in 'C:\MSSQL\SQL Backup\' and all subdirectories that are more than 48 hours old will be included.

        .EXAMPLE
            Find-DbaBackup -Path 'C:\MSSQL\Backup\' -BackupFileExtension bak -RetentionPeriod 7d -CheckArchiveBit

            '*.bak' files in 'C:\MSSQL\Backup\' and all subdirectories that are more than 7 days old will be included, but only if the files have been backed up to another location as verified by checking the Archive bit.

    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, HelpMessage = "Full path to the root level backup folder (ex. 'C:\SQL\Backups'")]
        [Alias("BackupFolder")]
        [string]$Path,
        [parameter(Mandatory = $true, HelpMessage = "Backup File extension to remove (ex. bak, trn, dif)")]
        [string]$BackupFileExtension ,
        [parameter(Mandatory = $true, HelpMessage = "Backup retention period. (ex. 24h, 7d, 4w, 6m)")]
        [string]$RetentionPeriod ,
        [parameter(Mandatory = $false)]
        [switch]$CheckArchiveBit = $false ,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        ### Local Functions
        function Convert-UserFriendlyRetentionToDatetime {
            [cmdletbinding()]
            param (
                [string]$UserFriendlyRetention
            )

            <#
            Convert a user friendly retention value into a datetime.
            The last character of the string will indicate units (validated)
            Valid units are: (h = hours, d = days, w = weeks, m = months)

            The preceeding characters are the value and must be an integer (validated)

            Examples:
                '48h' = 48 hours
                '7d' = 7 days
                '4w' = 4 weeks
                '1m' = 1 month
            #>

            [int]$Length = ($UserFriendlyRetention).Length
            $Value = ($UserFriendlyRetention).Substring(0, $Length - 1)
            $Units = ($UserFriendlyRetention).Substring($Length - 1, 1)

            # Validate that $Units is an accepted unit of measure
            if ( $Units -notin @('h', 'd', 'w', 'm') ) {
                throw "RetentionPeriod '$UserFriendlyRetention' units invalid! See Get-Help for correct formatting and examples."
            }

            # Validate that $Value is an INT
            if ( ![int]::TryParse($Value, [ref]"") ) {
                throw "RetentionPeriod '$UserFriendlyRetention' format invalid! See Get-Help for correct formatting and examples."
            }

            switch ($Units) {
                'h' { $UnitString = 'Hours'; [datetime]$ReturnDatetime = (Get-Date).AddHours( - $Value)  }
                'd' { $UnitString = 'Days'; [datetime]$ReturnDatetime = (Get-Date).AddDays( - $Value)   }
                'w' { $UnitString = 'Weeks'; [datetime]$ReturnDatetime = (Get-Date).AddDays( - $Value * 7) }
                'm' { $UnitString = 'Months'; [datetime]$ReturnDatetime = (Get-Date).AddMonths( - $Value) }
            }
            $ReturnDatetime
        }

        # Validations
        # Ensure BackupFileExtension does not begin with a .
        if ($BackupFileExtension -match "^[.]") {
            Write-Message -Level Warning -Message "Parameter -BackupFileExtension begins with a period '$BackupFileExtension'. A period is automatically prepended to -BackupFileExtension and need not be passed in."
        }
        # Ensure Path is a proper path
        if (!(Test-Path $Path -PathType 'Container')) {
            Stop-Function -Message "$Path not found"
        }

    }
    process {
        if (Test-FunctionInterrupt) { return }
        # Process stuff
        Write-Message -Message "Finding backups on $Path" -Level Verbose
        # Convert Retention Value to an actual DateTime
        try {
            $RetentionDate = Convert-UserFriendlyRetentionToDatetime -UserFriendlyRetention $RetentionPeriod
            Write-Message -Message "Backup Retention Date set to $RetentionDate" -Level Verbose
        }
        catch {
            Stop-Function -Message "Failed to interpret retention time!" -ErrorRecord $_
        }

        # Filter out unarchived files if -CheckArchiveBit parameter is used
        if ($CheckArchiveBit) {
            Write-Message -Message "Removing only archived files." -Level Verbose
            filter DbaArchiveBitFilter {
                if ($_.Attributes -notmatch "Archive") {
                    $_
                }
            }
        }
        else {
            filter DbaArchiveBitFilter {
                $_
            }
        }
        # Enumeration may take a while. Without resorting to "esoteric" file listing facilities
        # and given we need to fetch at least the LastWriteTime, let's just use "streaming" processing
        # here to avoid issues like described in #970
        Get-ChildItem $Path -Filter "*.$BackupFileExtension" -File -Recurse -ErrorAction SilentlyContinue -ErrorVariable EnumErrors |
            Where-Object LastWriteTime -lt $RetentionDate | DbaArchiveBitFilter
        if ($EnumErrors) {
            Write-Message "Errors encountered enumerating files." -Level Warning -ErrorRecord $EnumErrors
        }
    }
}
tools\dbatools\functions\Find-DbaCommand.ps1
#ValidationTags#Messaging#
function Find-DbaCommand {
    <#
        .SYNOPSIS
            Finds dbatools commands searching through the inline help text

        .DESCRIPTION
            Finds dbatools commands searching through the inline help text, building a consolidated json index and querying it because Get-Help is too slow

        .PARAMETER Tag
            Finds all commands tagged with this auto-populated tag

        .PARAMETER Author
            Finds all commands tagged with this author

        .PARAMETER MinimumVersion
            Finds all commands tagged with this auto-populated minimum version

        .PARAMETER MaximumVersion
            Finds all commands tagged with this auto-populated maximum version

        .PARAMETER Rebuild
            Rebuilds the index

        .PARAMETER Pattern
            Searches help for all commands in dbatools for the specified pattern and displays all results

        .PARAMETER Confirm
            Confirms overwrite of index

        .PARAMETER WhatIf
            Displays what would happen if the command is run

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Find,Help,Command
            Author: Simone Bizzotto

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Find-DbaCommand

        .EXAMPLE
            Find-DbaCommand "snapshot"

            For lazy typers: finds all commands searching the entire help for "snapshot"

        .EXAMPLE
            Find-DbaCommand -Pattern "snapshot"

            For rigorous typers: finds all commands searching the entire help for "snapshot"

        .EXAMPLE
            Find-DbaCommand -Tag copy

            Finds all commands tagged with "copy"

        .EXAMPLE
            Find-DbaCommand -Tag copy,user

            Finds all commands tagged with BOTH "copy" and "user"

        .EXAMPLE
            Find-DbaCommand -Author chrissy

            Finds every command whose author contains our beloved "chrissy"

        .EXAMPLE
            Find-DbaCommand -Author chrissy -Tag copy

            Finds every command whose author contains our beloved "chrissy" and it tagged as "copy"

        .EXAMPLE
            Find-DbaCommand -Pattern snapshot -Rebuild

            Finds all commands searching the entire help for "snapshot", rebuilding the index (good for developers)
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [String]$Pattern,
        [String[]]$Tag,
        [String]$Author,
        [String]$MinimumVersion,
        [String]$MaximumVersion,
        [switch]$Rebuild,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        $tagsRex = ([regex]'(?m)^[\s]{0,15}Tags:(.*)$')
        $authorRex = ([regex]'(?m)^[\s]{0,15}Author:(.*)$')
        $minverRex = ([regex]'(?m)^[\s]{0,15}MinimumVersion:(.*)$')
        $maxverRex = ([regex]'(?m)^[\s]{0,15}MaximumVersion:(.*)$')

        function Get-DbaHelp([String]$commandName) {
            $thishelp = Get-Help $commandName -Full
            $thebase = @{ }
            $thebase.CommandName = $commandName
            $thebase.Name = $thishelp.Name

            ## fetch the description
            $thebase.Description = $thishelp.Description.Text

            ## fetch examples
            $thebase.Examples = $thishelp.Examples | Out-String -Width 120

            ## fetch help link
            $thebase.Links = ($thishelp.relatedLinks).NavigationLink.Uri

            ## fetch the synopsis
            $thebase.Synopsis = $thishelp.Synopsis

            ## store notes
            $as = $thishelp.AlertSet | Out-String -Width 120

            ## fetch the tags
            $tags = $tagsrex.Match($as).Groups[1].Value
            if ($tags) {
                $thebase.Tags = $tags.Split(',').Trim()
            }
            ## fetch the author
            $author = $authorRex.Match($as).Groups[1].Value
            if ($author) {
                $thebase.Author = $author.Trim()
            }

            ## fetch MinimumVersion
            $MinimumVersion = $minverRex.Match($as).Groups[1].Value
            if ($MinimumVersion) {
                $thebase.MinimumVersion = $MinimumVersion.Trim()
            }

            ## fetch MaximumVersion
            $MaximumVersion = $maxverRex.Match($as).Groups[1].Value
            if ($MaximumVersion) {
                $thebase.MaximumVersion = $MaximumVersion.Trim()
            }

            [pscustomobject]$thebase
        }

        function Get-DbaIndex() {
            if ($Pscmdlet.ShouldProcess($dest, "Recreating index")) {
                $dbamodule = Get-Module -Name dbatools
                $allCommands = $dbamodule.ExportedCommands.Values | Where-Object CommandType -EQ 'Function'

                $helpcoll = New-Object System.Collections.Generic.List[System.Object]
                foreach ($command in $allCommands) {
                    $x = Get-DbaHelp "$command"
                    $helpcoll.Add($x)
                }
                # $dest = Get-DbaConfigValue -Name 'Path.TagCache' -Fallback "$(Resolve-Path $PSScriptRoot\..)\dbatools-index.json"
                $dest = "$moduleDirectory\bin\dbatools-index.json"
                $helpcoll | ConvertTo-Json | Out-File $dest -Encoding UTF8
            }
        }

        $moduleDirectory = (Get-Module -Name dbatools).ModuleBase
    }
    process {
        $Pattern = $Pattern.TrimEnd("s")
        $idxFile = "$moduleDirectory\bin\dbatools-index.json"
        if (!(Test-Path $idxFile) -or $Rebuild) {
            Write-Message -Level Verbose -Message "Rebuilding index into $idxFile"
            $swRebuild = [system.diagnostics.stopwatch]::StartNew()
            Get-DbaIndex
            Write-Message -Level Verbose -Message "Rebuild done in $($swRebuild.ElapsedMilliseconds)ms"
        }
        $consolidated = Get-Content -Raw $idxFile | ConvertFrom-Json
        $result = $consolidated
        if ($Pattern.Length -gt 0) {
            $result = $result | Where-Object { $_.PsObject.Properties.Value -like "*$Pattern*" }
        }

        if ($Tag.Length -gt 0) {
            foreach ($t in $Tag) {
                $result = $result | Where-Object Tags -Contains $t
            }
        }

        if ($Author.Length -gt 0) {
            $result = $result | Where-Object Author -Like "*$Author*"
        }

        if ($MinimumVersion.Length -gt 0) {
            $result = $result | Where-Object MinimumVersion -GE $MinimumVersion
        }

        if ($MaximumVersion.Length -gt 0) {
            $result = $result | Where-Object MaximumVersion -LE $MaximumVersion
        }

        Select-DefaultView -InputObject $result -Property CommandName, Synopsis
    }
}
tools\dbatools\functions\Find-DbaDatabase.ps1
#ValidationTags#Messaging#
function Find-DbaDatabase {
    <#
        .SYNOPSIS
            Find database/s on multiple servers that match criteria you input

        .DESCRIPTION
            Allows you to search SQL Server instances for database that have either the same name, owner or service broker guid.

            There a several reasons for the service broker guid not matching on a restored database primarily using alter database new broker. or turn off broker to return a guid of 0000-0000-0000-0000.

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to.

        .PARAMETER SqlCredential
            Credential object used to connect to the SQL Server as a different user

        .PARAMETER Property
            What you would like to search on. Either Database Name, Owner, or Service Broker GUID. Database name is the default.

        .PARAMETER Pattern
            Value that is searched for. This is a regular expression match but you can just use a plain ol string like 'dbareports'

        .PARAMETER Exact
            Search for an exact match instead of a pattern

        .PARAMETER Detailed
            Output all properties, will be depreciated in 1.0.0 release.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database
            Author: Stephen Bennett: https://sqlnotesfromtheunderground.wordpress.com/

            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Find-DbaDatabase

        .EXAMPLE
            Find-DbaDatabase -SqlInstance "DEV01", "DEV02", "UAT01", "UAT02", "PROD01", "PROD02" -Pattern Report

            Returns all database from the SqlInstances that have a database with Report in the name

        .EXAMPLE
            Find-DbaDatabase -SqlInstance "DEV01", "DEV02", "UAT01", "UAT02", "PROD01", "PROD02" -Pattern TestDB -Exact | Select-Object *

            Returns all database from the SqlInstances that have a database named TestDB with a detailed output.

        .EXAMPLE
            Find-DbaDatabase -SqlInstance "DEV01", "DEV02", "UAT01", "UAT02", "PROD01", "PROD02" -Property ServiceBrokerGuid -Pattern '-faeb-495a-9898-f25a782835f5' | Select-Object *

            Returns all database from the SqlInstances that have the same Service Broker GUID with a deatiled output
    #>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [ValidateSet('Name', 'ServiceBrokerGuid', 'Owner')]
        [string]$Property = 'Name',
        [parameter(Mandatory = $true)]
        [string]$Pattern,
        [switch]$Exact,
        [switch]$Detailed,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter Detailed
    }
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($exact -eq $true) {
                $dbs = $server.Databases | Where-Object IsAccessible | Where-Object { $_.$property -eq $pattern }
            }
            else {
                try {
                    $dbs = $server.Databases | Where-Object IsAccessible | Where-Object { $_.$property.ToString() -match $pattern }
                }
                catch {
                    # they probably put asterisks thinking it's a like
                    $Pattern = $Pattern -replace '\*', ''
                    $Pattern = $Pattern -replace '\%', ''
                    $dbs = $server.Databases | Where-Object { $_.$property.ToString() -match $pattern }
                }
            }

            foreach ($db in $dbs) {

                $extendedproperties = @()
                foreach ($xp in $db.ExtendedProperties) {
                    $extendedproperties += [PSCustomObject]@{
                        Name  = $db.ExtendedProperties[$xp.Name].Name
                        Value = $db.ExtendedProperties[$xp.Name].Value
                    }
                }

                if ($extendedproperties.count -eq 0) { $extendedproperties = 0 }

                [PSCustomObject]@{
                    ComputerName       = $server.ComputerName
                    InstanceName       = $server.ServiceName
                    SqlInstance        = $server.Name
                    Name               = $db.Name
                    SizeMB             = $db.Size
                    Owner              = $db.Owner
                    CreateDate         = $db.CreateDate
                    ServiceBrokerGuid  = $db.ServiceBrokerGuid
                    Tables             = ($db.Tables | Where-Object { $_.IsSystemObject -eq $false }).Count
                    StoredProcedures   = ($db.StoredProcedures | Where-Object { $_.IsSystemObject -eq $false }).Count
                    Views              = ($db.Views | Where-Object { $_.IsSystemObject -eq $false }).Count
                    ExtendedProperties = $extendedproperties
                    Database           = $db
                } | Select-DefaultView -ExcludeProperty Database, ExtendedProperties, ServiceBrokerGuid, StoredProcedures, Tables, Views
            }
        }
    }
}
tools\dbatools\functions\Find-DbaDbGrowthEvent.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Find-DbaDbGrowthEvent {
    <#
        .SYNOPSIS
            Finds any database AutoGrow events in the Default Trace.

        .DESCRIPTION
            Finds any database AutoGrow events in the Default Trace.

            The following events are included:
                92 - Data File Auto Grow
                93 - Log File Auto Grow
                94 - Data File Auto Shrink
                95 - Log File Auto Shrink

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto-populated from the server

        .PARAMETER EventType
            Provide a filter on growth event type to filter the results.

            Allowed values: Growth, Shrink

        .PARAMETER FileType
            Provide a filter on file type to filter the results.

            Allowed vaules: Data, Log

        .PARAMETER UseLocalTime
            Return the local time of the instance instead of converting to UTC.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: AutoGrow,Growth,Database
            Author: Aaron Nelson
            Query Extracted from SQL Server Management Studio (SSMS) 2016.

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Find-DbaDatabaseGrowthEvent

        .EXAMPLE
            Find-DbaDatabaseGrowthEvent -SqlInstance localhost

            Returns any database AutoGrow events in the Default Trace with UTC time for the instance for every database on the localhost instance.

        .EXAMPLE
            Find-DbaDatabaseGrowthEvent -SqlInstance localhost -UseLocalTime

            Returns any database AutoGrow events in the Default Trace with the local time of the instance for every database on the localhost instance.

        .EXAMPLE
            Find-DbaDatabaseGrowthEvent -SqlInstance ServerA\SQL2016, ServerA\SQL2014

            Returns any database AutoGrow events in the Default Traces for every database on ServerA\sql2016 & ServerA\SQL2014.

        .EXAMPLE
            Find-DbaDatabaseGrowthEvent -SqlInstance ServerA\SQL2016 | Format-Table -AutoSize -Wrap

            Returns any database AutoGrow events in the Default Trace for every database on the ServerA\SQL2016 instance in a table format.

        .EXAMPLE
            Find-DbaDatabaseGrowthEvent -SqlInstance ServerA\SQL2016 -EventType Shrink

            Returns any database Auto Shrink events in the Default Trace for every database on the ServerA\SQL2016 instance.

        .EXAMPLE
            Find-DbaDatabaseGrowthEvent -SqlInstance ServerA\SQL2016 -EventType Growth -FileType Data

            Returns any database Auto Growth events on data files in the Default Trace for every database on the ServerA\SQL2016 instance.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [ValidateSet('Growth', 'Shrink')]
        [string]$EventType,
        [ValidateSet('Data', 'Log')]
        [string]$FileType,
        [switch]$UseLocalTime,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $eventClass = New-Object System.Collections.ArrayList
        92..95 | ForEach-Object { $null = $eventClass.Add($_) }

        if (Test-Bound 'EventType', 'FileType') {
            switch ($FileType) {
                'Data' {
                    <# should only contain events for data: 92 (grow), 94 (shrink) #>
                    $eventClass.Remove(93)
                    $eventClass.Remove(95)
                }
                'Log' {
                    <# should only contain events for log: 93 (grow), 95 (shrink) #>
                    $eventClass.Remove(92)
                    $eventClass.Remove(94)
                }
            }
            switch ($EventType) {
                'Growth' {
                    <# should only contain events for growth: 92 (data), 93 (log) #>
                    $eventClass.Remove(94)
                    $eventClass.Remove(95)
                }
                'Shrink' {
                    <# should only contain events for shrink: 94 (data), 95 (log) #>
                    $eventClass.Remove(92)
                    $eventClass.Remove(93)
                }
            }
        }

        $eventClassFilter = $eventClass -join ","

        $sql = "
            BEGIN TRY
                IF (SELECT CONVERT(INT,[value_in_use]) FROM sys.configurations WHERE [name] = 'default trace enabled' ) = 1
                    BEGIN
                        DECLARE @curr_tracefilename VARCHAR(500);
                        DECLARE @base_tracefilename VARCHAR(500);
                        DECLARE @indx INT;

                        SELECT @curr_tracefilename = [path]
                        FROM sys.traces
                        WHERE is_default = 1 ;

                        SET @curr_tracefilename = REVERSE(@curr_tracefilename);
                        SELECT @indx  = PATINDEX('%\%', @curr_tracefilename);
                        SET @curr_tracefilename = REVERSE(@curr_tracefilename);
                        SET @base_tracefilename = LEFT( @curr_tracefilename,LEN(@curr_tracefilename) - @indx) + '\log.trc';

                        SELECT
                            SERVERPROPERTY('MachineName') AS ComputerName,
                            ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
                            SERVERPROPERTY('ServerName') AS SqlInstance,
                            CONVERT(INT,(DENSE_RANK() OVER (ORDER BY [StartTime] DESC))%2) AS OrderRank,
                                CONVERT(INT, [EventClass]) AS EventClass,
                            [DatabaseName],
                            [Filename],
                            CONVERT(INT,(Duration/1000)) AS Duration,
                            $(if (-not $UseLocalTime) { "
                            DATEADD (MINUTE, DATEDIFF(MINUTE, GETDATE(), GETUTCDATE()), [StartTime]) AS StartTime,  -- Convert to UTC time
                            DATEADD (MINUTE, DATEDIFF(MINUTE, GETDATE(), GETUTCDATE()), [EndTime]) AS EndTime,  -- Convert to UTC time"
                            }
                            else { "
                            [StartTime] AS StartTime,
                            [EndTime] AS EndTime,"
                            })
                            ([IntegerData]*8.0/1024) AS ChangeInSize,
                            ApplicationName,
                            HostName,
                            SessionLoginName,
                            SPID
                        FROM::fn_trace_gettable( @base_tracefilename, DEFAULT )
                        WHERE
                            [EventClass] IN ($eventClassFilter)
                            AND [ServerName] = @@SERVERNAME
                            AND [DatabaseName] IN (_DatabaseList_)
                        ORDER BY [StartTime] DESC;
                    END
                ELSE
                    SELECT
                        SERVERPROPERTY('MachineName') AS ComputerName,
                        ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
                        SERVERPROPERTY('ServerName') AS SqlInstance,
                        -100 AS [OrderRank],
                        -1 AS [OrderRank],
                        0 AS [EventClass],
                        0 [DatabaseName],
                        0 AS [Filename],
                        0 AS [Duration],
                        0 AS [StartTime],
                        0 AS [EndTime],
                        0 AS ChangeInSize,
                        0 AS [ApplicationName],
                        0 AS [HostName],
                        0 AS [SessionLoginName],
                        0 AS [SPID]
            END	TRY
            BEGIN CATCH
                SELECT
                    SERVERPROPERTY('MachineName') AS ComputerName,
                    ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
                    SERVERPROPERTY('ServerName') AS SqlInstance,
                    -100 AS [OrderRank],
                    -100 AS [OrderRank],
                    ERROR_NUMBER() AS [EventClass],
                    ERROR_SEVERITY() AS [DatabaseName],
                    ERROR_STATE() AS [Filename],
                    ERROR_MESSAGE() AS [Duration],
                    1 AS [StartTime],
                    1 AS [EndTime],
                    1 AS [ChangeInSize],
                    1 AS [ApplicationName],
                    1 AS [HostName],
                    1 AS [SessionLoginName],
                    1 AS [SPID]
            END CATCH"

        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Find-DbaDatabaseGrowthEvent
    }
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $dbs = $server.Databases

            if ($Database) {
                $dbs = $dbs | Where-Object Name -In $Database
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            #Create dblist name in 'bd1', 'db2' format
            $dbsList = "'$($($dbs | ForEach-Object {$_.Name}) -join "','")'"
            Write-Message -Level Verbose -Message "Executing query against $dbsList on $instance"

            $sql = $sql -replace '_DatabaseList_', $dbsList
            Write-Message -Level Debug -Message "Executing SQL Statement:`n $sql"

            $defaults = 'ComputerName', 'InstanceName', 'SqlInstance', 'EventClass', 'DatabaseName', 'Filename', 'Duration', 'StartTime', 'EndTime', 'ChangeInSize', 'ApplicationName', 'HostName'

            try {
                Select-DefaultView -InputObject $server.Query($sql) -Property $defaults
            }
            catch {
                Stop-Function -Message "Issue collecting data on $server" -Target $server -ErrorRecord $_ -Exception $_.Exception.InnerException.InnerException.InnerException -Continue
            }
        }
    }
}
tools\dbatools\functions\Find-DbaDisabledIndex.ps1
function Find-DbaDisabledIndex {
    <#
        .SYNOPSIS
            Find Disabled indexes

        .DESCRIPTION
            This command will help you to find disabled indexes on a database or a list of databases.

        .PARAMETER SqlInstance
            The SQL Server you want to check for disabled indexes.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies the database(s) to exclude from processing. Options for this list are auto-populated from the server.

        .PARAMETER NoClobber
            If this switch is enabled, the output file will not be overwritten.

        .PARAMETER Append
            If this switch is enabled, content will be appended to the output file.

            .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Index
            Author: Jason Squires, sqlnotnull.com

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Find-DbadisabledIndex

        .EXAMPLE
            Find-DbadisabledIndex -SqlInstance sql2005

            Generates the SQL statements to drop the selected disabled indexes on server "sql2005".

        .EXAMPLE
            Find-DbadisabledIndex -SqlInstance sqlserver2016 -SqlCredential $cred

            Generates the SQL statements to drop the selected disabled indexes on server "sqlserver2016", using SQL Authentication to connect to the database.

        .EXAMPLE
            Find-DbadisabledIndex -SqlInstance sqlserver2016 -Database db1, db2

            Generates the SQL Statement to drop selected indexes in databases db1 & db2 on server "sqlserver2016".

        .EXAMPLE
            Find-DbadisabledIndex -SqlInstance sqlserver2016

            Generates the SQL statements to drop selected indexes on all user databases.

    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]
        $SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$NoClobber,
        [switch]$Append,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $sql = "
        SELECT DB_NAME() AS 'DatabaseName'
        ,s.name AS 'SchemaName'
        ,t.name AS 'TableName'
        ,i.object_id AS ObjectId
        ,i.name AS 'IndexName'
        ,i.index_id as 'IndexId'
        ,i.type_desc as 'TypeDesc'
        FROM sys.tables t
        JOIN sys.schemas s
            ON t.schema_id = s.schema_id
        JOIN sys.indexes i
            ON i.object_id = t.object_id
        WHERE i.is_disabled = 1"
    }
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential  -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($Database) {
                $databases = $server.Databases | Where-Object Name -in $database
            }
            else {
                $databases = $server.Databases | Where-Object IsAccessible -eq $true
            }

            if ($databases.Count -gt 0) {
                foreach ($db in $databases.name) {

                    if ($ExcludeDatabase -contains $db -or $null -eq $server.Databases[$db]) {
                        continue
                    }

                    try {
                        if ($PSCmdlet.ShouldProcess($db, "Getting disabled indexes")) {
                            Write-Message -Level Verbose -Message "Getting indexes from database '$db'."
                            Write-Message -Level Debug -Message "SQL Statement: $sql"
                            $disabledIndex = $server.Databases[$db].ExecuteWithResults($sql)

                            if ($disabledIndex.Tables[0].Rows.Count -gt 0) {
                                $results = $disabledIndex.Tables[0];
                                if ($results.Count -gt 0 -or !([string]::IsNullOrEmpty($results))) {
                                    foreach ($index in $results) {
                                        $index
                                    }
                                }
                            }
                            else {
                                Write-Message -Level Verbose -Message "No Disabled indexes found!"
                            }
                        }
                    }
                    catch {
                        Stop-Function -Message "Issue gathering indexes" -Category InvalidOperation -InnerErrorRecord $_ -Target $db
                    }
                }
            }
            else {
                Write-Message -Level Verbose -Message "There are no databases to analyse."
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-SqlDisabledIndex
    }
}
tools\dbatools\functions\Find-DbaDuplicateIndex.ps1
function Find-DbaDuplicateIndex {
    <#
        .SYNOPSIS
            Find duplicate and overlapping indexes.

        .DESCRIPTION
            This command will help you to find duplicate and overlapping indexes on a database or a list of databases.

            On SQL Server 2008 and higher, the IsFiltered property will also be checked

            Also tells how much space you can save by dropping the index.

            We show the type of compression so you can make a more considered decision.

            For now only supports CLUSTERED and NONCLUSTERED indexes.

            You can select the indexes you want to drop on the gridview and when clicking OK, the DROP statement will be generated.

            Output:
                TableName
                IndexName
                KeyColumns
                IncludedColumns
                IndexSizeMB
                IndexType
                CompressionDescription (When 2008+)
                [RowCount]
                IsDisabled
                IsFiltered (When 2008+)

        .PARAMETER SqlInstance
            The SQL Server you want to check for duplicate indexes.

        .PARAMETER SqlCredential
             Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER IncludeOverlapping
            If this switch is enabled, indexes which are partially duplicated will be returned.

            Example: If the first key column is the same between two indexes, but one has included columns and the other not, this will be shown.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER Force
            If this switch is enabled, the DROP statement(s) will be executed instead of being written to the output file.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Index
            Author: Claudio Silva (@ClaudioESSilva)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Find-DbaDuplicateIndex

        .EXAMPLE
            Find-DbaDuplicateIndex -SqlInstance sql2005 | Out-File -FilePath C:\temp\sql2005-DuplicateIndexes.sql

            Generates SQL statements to drop the selected duplicate indexes in server "sql2005" and writes them to the file "C:\temp\sql2005-DuplicateIndexes.sql"

        .EXAMPLE
            Find-DbaDuplicateIndex -SqlInstance sql2005 | Out-File -FilePath C:\temp\sql2005-DuplicateIndexes.sql -Append

            Generates SQL statements to drop the selected duplicate indexes and writes/appends them to the file "C:\temp\sql2005-DuplicateIndexes.sql"

        .EXAMPLE
            Find-DbaDuplicateIndex -SqlInstance sqlserver2014a -SqlCredential $cred

            Finds exact duplicate indexes on all user databases present on sqlserver2014a, using SQL authentication.

        .EXAMPLE
            Find-DbaDuplicateIndex -SqlInstance sqlserver2014a -Database db1, db2

            Finds exact duplicate indexes on the db1 and db2 databases.

        .EXAMPLE
            Find-DbaDuplicateIndex -SqlInstance sqlserver2014a -IncludeOverlapping

            Finds both duplicate and overlapping indexes on all user databases.

    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [switch]$IncludeOverlapping,
        [switch]$EnableException
    )

    begin {
        $exactDuplicateQuery2005 = "
            WITH CTE_IndexCols
            AS (
                SELECT i.[object_id]
                    ,i.index_id
                    ,OBJECT_SCHEMA_NAME(i.[object_id]) AS SchemaName
                    ,OBJECT_NAME(i.[object_id]) AS TableName
                    ,NAME AS IndexName
                    ,ISNULL(STUFF((
                                SELECT ', ' + col.NAME + ' ' + CASE
                                        WHEN idxCol.is_descending_key = 1
                                            THEN 'DESC'
                                        ELSE 'ASC'
                                        END -- Include column order (ASC / DESC)
                                FROM sys.index_columns idxCol
                                INNER JOIN sys.columns col ON idxCol.[object_id] = col.[object_id]
                                    AND idxCol.column_id = col.column_id
                                WHERE i.[object_id] = idxCol.[object_id]
                                    AND i.index_id = idxCol.index_id
                                    AND idxCol.is_included_column = 0
                                ORDER BY idxCol.key_ordinal
                                FOR XML PATH('')
                                ), 1, 2, ''), '') AS KeyColumns
                    ,ISNULL(STUFF((
                                SELECT ', ' + col.NAME + ' ' + CASE
                                        WHEN idxCol.is_descending_key = 1
                                            THEN 'DESC'
                                        ELSE 'ASC'
                                        END -- Include column order (ASC / DESC)
                                FROM sys.index_columns idxCol
                                INNER JOIN sys.columns col ON idxCol.[object_id] = col.[object_id]
                                    AND idxCol.column_id = col.column_id
                                WHERE i.[object_id] = idxCol.[object_id]
                                    AND i.index_id = idxCol.index_id
                                    AND idxCol.is_included_column = 1
                                ORDER BY idxCol.key_ordinal
                                FOR XML PATH('')
                                ), 1, 2, ''), '') AS IncludedColumns
                    ,i.[type_desc] AS IndexType
                    ,i.is_disabled AS IsDisabled
                FROM sys.indexes AS i
                WHERE i.index_id > 0 -- Exclude HEAPS
                    AND i.[type_desc] IN (
                        'CLUSTERED'
                        ,'NONCLUSTERED'
                        )
                    AND OBJECT_SCHEMA_NAME(i.[object_id]) <> 'sys'
                )
                ,CTE_IndexSpace
            AS (
                SELECT s.[object_id]
                    ,s.index_id
                    ,SUM(s.[used_page_count]) * 8 / 1024.0 AS IndexSizeMB
                    ,SUM(p.[rows]) AS [RowCount]
                FROM sys.dm_db_partition_stats AS s
                INNER JOIN sys.partitions p WITH (NOLOCK) ON s.[partition_id] = p.[partition_id]
                    AND s.[object_id] = p.[object_id]
                    AND s.index_id = p.index_id
                WHERE s.index_id > 0 -- Exclude HEAPS
                    AND OBJECT_SCHEMA_NAME(s.[object_id]) <> 'sys'
                GROUP BY s.[object_id]
                    ,s.index_id
                )
            SELECT DB_NAME() AS DatabaseName
                ,CI1.SchemaName + '.' + CI1.TableName AS 'TableName'
                ,CI1.IndexName
                ,CI1.KeyColumns
                ,CI1.IncludedColumns
                ,CI1.IndexType
                ,CSPC.IndexSizeMB
                ,CSPC.[RowCount]
                ,CI1.IsDisabled
            FROM CTE_IndexCols AS CI1
            INNER JOIN CTE_IndexSpace AS CSPC ON CI1.[object_id] = CSPC.[object_id]
                AND CI1.index_id = CSPC.index_id
            WHERE EXISTS (
                    SELECT 1
                    FROM CTE_IndexCols CI2
                    WHERE CI1.SchemaName = CI2.SchemaName
                        AND CI1.TableName = CI2.TableName
                        AND CI1.KeyColumns = CI2.KeyColumns
                        AND CI1.IncludedColumns = CI2.IncludedColumns
                        AND CI1.IndexName <> CI2.IndexName
                    )"

        $overlappingQuery2005 = "
            WITH CTE_IndexCols
            AS (
                SELECT i.[object_id]
                    ,i.index_id
                    ,OBJECT_SCHEMA_NAME(i.[object_id]) AS SchemaName
                    ,OBJECT_NAME(i.[object_id]) AS TableName
                    ,NAME AS IndexName
                    ,ISNULL(STUFF((
                                SELECT ', ' + col.NAME + ' ' + CASE
                                        WHEN idxCol.is_descending_key = 1
                                            THEN 'DESC'
                                        ELSE 'ASC'
                                        END -- Include column order (ASC / DESC)
                                FROM sys.index_columns idxCol
                                INNER JOIN sys.columns col ON idxCol.[object_id] = col.[object_id]
                                    AND idxCol.column_id = col.column_id
                                WHERE i.[object_id] = idxCol.[object_id]
                                    AND i.index_id = idxCol.index_id
                                    AND idxCol.is_included_column = 0
                                ORDER BY idxCol.key_ordinal
                                FOR XML PATH('')
                                ), 1, 2, ''), '') AS KeyColumns
                    ,ISNULL(STUFF((
                                SELECT ', ' + col.NAME + ' ' + CASE
                                        WHEN idxCol.is_descending_key = 1
                                            THEN 'DESC'
                                        ELSE 'ASC'
                                        END -- Include column order (ASC / DESC)
                                FROM sys.index_columns idxCol
                                INNER JOIN sys.columns col ON idxCol.[object_id] = col.[object_id]
                                    AND idxCol.column_id = col.column_id
                                WHERE i.[object_id] = idxCol.[object_id]
                                    AND i.index_id = idxCol.index_id
                                    AND idxCol.is_included_column = 1
                                ORDER BY idxCol.key_ordinal
                                FOR XML PATH('')
                                ), 1, 2, ''), '') AS IncludedColumns
                    ,i.[type_desc] AS IndexType
                    ,i.is_disabled AS IsDisabled
                FROM sys.indexes AS i
                WHERE i.index_id > 0 -- Exclude HEAPS
                    AND i.[type_desc] IN (
                        'CLUSTERED'
                        ,'NONCLUSTERED'
                        )
                    AND OBJECT_SCHEMA_NAME(i.[object_id]) <> 'sys'
                )
                ,CTE_IndexSpace
            AS (
                SELECT s.[object_id]
                    ,s.index_id
                    ,SUM(s.[used_page_count]) * 8 / 1024.0 AS IndexSizeMB
                    ,SUM(p.[rows]) AS [RowCount]
                FROM sys.dm_db_partition_stats AS s
                INNER JOIN sys.partitions p WITH (NOLOCK) ON s.[partition_id] = p.[partition_id]
                    AND s.[object_id] = p.[object_id]
                    AND s.index_id = p.index_id
                WHERE s.index_id > 0 -- Exclude HEAPS
                    AND OBJECT_SCHEMA_NAME(s.[object_id]) <> 'sys'
                GROUP BY s.[object_id]
                    ,s.index_id
                )
            SELECT DB_NAME() AS DatabaseName
                ,CI1.SchemaName + '.' + CI1.TableName AS 'TableName'
                ,CI1.IndexName
                ,CI1.KeyColumns
                ,CI1.IncludedColumns
                ,CI1.IndexType
                ,CSPC.IndexSizeMB
                ,CSPC.[RowCount]
                ,CI1.IsDisabled
            FROM CTE_IndexCols AS CI1
            INNER JOIN CTE_IndexSpace AS CSPC ON CI1.[object_id] = CSPC.[object_id]
                AND CI1.index_id = CSPC.index_id
            WHERE EXISTS (
                    SELECT 1
                    FROM CTE_IndexCols CI2
                    WHERE CI1.SchemaName = CI2.SchemaName
                        AND CI1.TableName = CI2.TableName
                        AND (
                            (
                                CI1.KeyColumns LIKE CI2.KeyColumns + '%'
                                AND SUBSTRING(CI1.KeyColumns, LEN(CI2.KeyColumns) + 1, 1) = ' '
                                )
                            OR (
                                CI2.KeyColumns LIKE CI1.KeyColumns + '%'
                                AND SUBSTRING(CI2.KeyColumns, LEN(CI1.KeyColumns) + 1, 1) = ' '
                                )
                            )
                        AND CI1.IndexName <> CI2.IndexName
                    )"

        # Support Compression 2008+
        $exactDuplicateQuery = "
            WITH CTE_IndexCols
            AS (
                SELECT i.[object_id]
                    ,i.index_id
                    ,OBJECT_SCHEMA_NAME(i.[object_id]) AS SchemaName
                    ,OBJECT_NAME(i.[object_id]) AS TableName
                    ,NAME AS IndexName
                    ,ISNULL(STUFF((
                                SELECT ', ' + col.NAME + ' ' + CASE
                                        WHEN idxCol.is_descending_key = 1
                                            THEN 'DESC'
                                        ELSE 'ASC'
                                        END -- Include column order (ASC / DESC)
                                FROM sys.index_columns idxCol
                                INNER JOIN sys.columns col ON idxCol.[object_id] = col.[object_id]
                                    AND idxCol.column_id = col.column_id
                                WHERE i.[object_id] = idxCol.[object_id]
                                    AND i.index_id = idxCol.index_id
                                    AND idxCol.is_included_column = 0
                                ORDER BY idxCol.key_ordinal
                                FOR XML PATH('')
                                ), 1, 2, ''), '') AS KeyColumns
                    ,ISNULL(STUFF((
                                SELECT ', ' + col.NAME + ' ' + CASE
                                        WHEN idxCol.is_descending_key = 1
                                            THEN 'DESC'
                                        ELSE 'ASC'
                                        END -- Include column order (ASC / DESC)
                                FROM sys.index_columns idxCol
                                INNER JOIN sys.columns col ON idxCol.[object_id] = col.[object_id]
                                    AND idxCol.column_id = col.column_id
                                WHERE i.[object_id] = idxCol.[object_id]
                                    AND i.index_id = idxCol.index_id
                                    AND idxCol.is_included_column = 1
                                ORDER BY idxCol.key_ordinal
                                FOR XML PATH('')
                                ), 1, 2, ''), '') AS IncludedColumns
                    ,i.[type_desc] AS IndexType
                    ,i.is_disabled AS IsDisabled
                    ,i.has_filter AS IsFiltered
                FROM sys.indexes AS i
                WHERE i.index_id > 0 -- Exclude HEAPS
                    AND i.[type_desc] IN (
                        'CLUSTERED'
                        ,'NONCLUSTERED'
                        )
                    AND OBJECT_SCHEMA_NAME(i.[object_id]) <> 'sys'
                )
                ,CTE_IndexSpace
            AS (
                SELECT s.[object_id]
                    ,s.index_id
                    ,SUM(s.[used_page_count]) * 8 / 1024.0 AS IndexSizeMB
                    ,SUM(p.[rows]) AS [RowCount]
                    ,p.data_compression_desc AS CompressionDescription
                FROM sys.dm_db_partition_stats AS s
                INNER JOIN sys.partitions p WITH (NOLOCK) ON s.[partition_id] = p.[partition_id]
                    AND s.[object_id] = p.[object_id]
                    AND s.index_id = p.index_id
                WHERE s.index_id > 0 -- Exclude HEAPS
                    AND OBJECT_SCHEMA_NAME(s.[object_id]) <> 'sys'
                GROUP BY s.[object_id]
                    ,s.index_id
                    ,p.data_compression_desc
                )
            SELECT DB_NAME() AS DatabaseName
                ,CI1.SchemaName + '.' + CI1.TableName AS 'TableName'
                ,CI1.IndexName
                ,CI1.KeyColumns
                ,CI1.IncludedColumns
                ,CI1.IndexType
                ,CSPC.IndexSizeMB
                ,CSPC.CompressionDescription
                ,CSPC.[RowCount]
                ,CI1.IsDisabled
                ,CI1.IsFiltered
            FROM CTE_IndexCols AS CI1
            INNER JOIN CTE_IndexSpace AS CSPC ON CI1.[object_id] = CSPC.[object_id]
                AND CI1.index_id = CSPC.index_id
            WHERE EXISTS (
                    SELECT 1
                    FROM CTE_IndexCols CI2
                    WHERE CI1.SchemaName = CI2.SchemaName
                        AND CI1.TableName = CI2.TableName
                        AND CI1.KeyColumns = CI2.KeyColumns
                        AND CI1.IncludedColumns = CI2.IncludedColumns
                        AND CI1.IsFiltered = CI2.IsFiltered
                        AND CI1.IndexName <> CI2.IndexName
                    )"

        $overlappingQuery = "
            WITH CTE_IndexCols AS
            (
                SELECT
                        i.[object_id]
                        ,i.index_id
                        ,OBJECT_SCHEMA_NAME(i.[object_id]) AS SchemaName
                        ,OBJECT_NAME(i.[object_id]) AS TableName
                        ,Name AS IndexName
                        ,ISNULL(STUFF((SELECT ', ' + col.NAME + ' ' + CASE
                                                                    WHEN idxCol.is_descending_key = 1 THEN 'DESC'
                                                                    ELSE 'ASC'
                                                                END -- Include column order (ASC / DESC)
                                FROM sys.index_columns idxCol
                                    INNER JOIN sys.columns col
                                    ON idxCol.[object_id] = col.[object_id]
                                    AND idxCol.column_id = col.column_id
                                WHERE i.[object_id] = idxCol.[object_id]
                                AND i.index_id = idxCol.index_id
                                AND idxCol.is_included_column = 0
                                ORDER BY idxCol.key_ordinal
                        FOR XML PATH('')), 1, 2, ''), '') AS KeyColumns
                        ,ISNULL(STUFF((SELECT ', ' + col.NAME + ' ' + CASE
                                                                    WHEN idxCol.is_descending_key = 1 THEN 'DESC'
                                                                    ELSE 'ASC'
                                                                END -- Include column order (ASC / DESC)
                                FROM sys.index_columns idxCol
                                    INNER JOIN sys.columns col
                                    ON idxCol.[object_id] = col.[object_id]
                                    AND idxCol.column_id = col.column_id
                                WHERE i.[object_id] = idxCol.[object_id]
                                AND i.index_id = idxCol.index_id
                                AND idxCol.is_included_column = 1
                                ORDER BY idxCol.key_ordinal
                        FOR XML PATH('')), 1, 2, ''), '') AS IncludedColumns
                        ,i.[type_desc] AS IndexType
                        ,i.is_disabled AS IsDisabled
                        ,i.has_filter AS IsFiltered
                FROM sys.indexes AS i
                WHERE i.index_id > 0 -- Exclude HEAPS
                AND i.[type_desc] IN ('CLUSTERED', 'NONCLUSTERED')
                AND OBJECT_SCHEMA_NAME(i.[object_id]) <> 'sys'
            ),
            CTE_IndexSpace AS
            (
            SELECT
                        s.[object_id]
                        ,s.index_id
                        ,SUM(s.[used_page_count]) * 8 / 1024.0 AS IndexSizeMB
                        ,SUM(p.[rows]) AS [RowCount]
                        ,p.data_compression_desc AS CompressionDescription
                FROM sys.dm_db_partition_stats AS s
                    INNER JOIN sys.partitions p WITH (NOLOCK)
                    ON s.[partition_id] = p.[partition_id]
                    AND s.[object_id] = p.[object_id]
                    AND s.index_id = p.index_id
                WHERE s.index_id > 0 -- Exclude HEAPS
                    AND OBJECT_SCHEMA_NAME(s.[object_id]) <> 'sys'
                GROUP BY s.[object_id], s.index_id, p.data_compression_desc
            )
            SELECT
                    DB_NAME() AS DatabaseName
                    ,CI1.SchemaName + '.' + CI1.TableName AS 'TableName'
                    ,CI1.IndexName
                    ,CI1.KeyColumns
                    ,CI1.IncludedColumns
                    ,CI1.IndexType
                    ,CSPC.IndexSizeMB
                    ,CSPC.CompressionDescription
                    ,CSPC.[RowCount]
                    ,CI1.IsDisabled
                    ,CI1.IsFiltered
            FROM CTE_IndexCols AS CI1
                INNER JOIN CTE_IndexSpace AS CSPC
                ON CI1.[object_id] = CSPC.[object_id]
                AND CI1.index_id = CSPC.index_id
            WHERE EXISTS (SELECT 1
                            FROM CTE_IndexCols CI2
                        WHERE CI1.SchemaName = CI2.SchemaName
                            AND CI1.TableName = CI2.TableName
                            AND (
                                        (CI1.KeyColumns like CI2.KeyColumns + '%' and SUBSTRING(CI1.KeyColumns,LEN(CI2.KeyColumns)+1,1) = ' ')
                                    OR (CI2.KeyColumns like CI1.KeyColumns + '%' and SUBSTRING(CI2.KeyColumns,LEN(CI1.KeyColumns)+1,1) = ' ')
                                )
                            AND CI1.IsFiltered = CI2.IsFiltered
                            AND CI1.IndexName <> CI2.IndexName
                        )"
    }
    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $sqlinstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($database) {
                $databases = $server.Databases | Where-Object Name -in $database
            }
            else {
                $databases = $server.Databases | Where-Object IsAccessible -eq $true
            }

            foreach ($db in $databases) {
                try {
                    Write-Message -Level Verbose -Message "Getting indexes from database '$db'."

                    $query = if ($server.versionMajor -eq 9) {
                        if ($IncludeOverlapping) { $overlappingQuery2005 }
                        else { $exactDuplicateQuery2005 }
                    }
                    else {
                        if ($IncludeOverlapping) { $overlappingQuery }
                        else { $exactDuplicateQuery }
                    }

                    $db.Query($query)

                }
                catch {
                    Stop-Function -Message "Query failure" -Target $db
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-SqlDuplicateIndex
    }
}
tools\dbatools\functions\Find-DbaInstance.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Find-DbaInstance {
    <#
        .SYNOPSIS
            Search for SQL Server Instances.

        .DESCRIPTION
            This function searches for SQL Server Instances.

            It supports a variety of scans for this purpose which can be separated in two categories:
            - Discovery
            - Scan

            Discovery:
            This is where it compiles a list of computers / addresses to check.
            It supports several methods of generating such lists (including Active Directory lookup or IP Ranges), but also supports specifying a list of computers to check.
            - For details on discovery, see the documentation on the '-DiscoveryType' parameter
            - For details on explicitly providing a list, see the documentation on the '-ComputerName' parameter

            Scan:
            Once a list of computers has been provided, this command will execute a variety of actions to determine any instances present for each of them.
            This is described in more detail in the documentation on the '-ScanType' parameter.
            Additional parameters allow more granular control over individual scans (e.g. Credentials to use).

            Note on logging and auditing:
            The Discovery phase is unproblematic since it is non-intrusive, however during the scan phase, all targeted computers may be accessed repeatedly.
            This may cause issues with security teams, due to many logon events and possibly failed authentication.
            This action constitutes a network scan, which may be illegal depending on the nation you are in and whether you own the network you scan.
            If you are unsure whether you may use this command in your environment, check the detailed description on the '-ScanType' parameter and contact your IT security team for advice.

        .PARAMETER ComputerName
            The computer to scan. Can be a variety of input types, including text or the output of Get-ADComputer.
            Any extra instance information (such as connection strings or live sql server connections) beyond the computername will be discarded.

        .PARAMETER DiscoveryType
            The mechanisms to be used to discover instances.
            Supports any combination of:
            - Service Principal Name lookup ('Domain'; from Active Directory)
            - SQL Instance Enumeration ('DataSourceEnumeration'; same as SSMS uses)
            - IP Address range ('IPRange'; all IP Addresses will be scanned)

            SPN Lookup:
            The function tries to connect active directory to look up all computers with registered SQL Instances.
            Not all instances need to be registered properly, making this not 100% reliable.
            By default, your nearest Domain Controller is contacted for this scan.
            However it is possible to explicitly state the DC to contact using its DistinguishedName and the '-DomainController' parameter.
            If credentials were specified using the '-Credential' parameter, those same credentials are used to perform this lookup, allowing the scan of other domains.

            SQL Instance Enumeration:
            This uses the default UDP Broadcast based instance enumeration used by SSMS to detect instances.
            Note that the result from this is not used in the actual scan, but only to compile a list of computers to scan.
            To enable the same results for the scan, ensure that the 'Browser' scan is enabled.

            IP Address range:
            This 'Discovery' uses a range of IPAddresses and simply passes them on to be tested.
            See the 'Description' part of help on security issues of network scanning.
            By default, it will enumerate all ethernet network adapters on the local computer and scan the entire subnet they are on.
            By using the '-IpAddress' parameter, custom network ranges can be specified.

        .PARAMETER Credential
            The credentials to use on windows network connection.
            These credentials are used for:
            - Contact to domain controllers for SPN lookups (only if explicit Domain Controller is specified)
            - CIM/WMI contact to the scanned computers during the scan phase (see the '-ScanType' parameter documentation on affected scans).

        .PARAMETER SqlCredential
            The credentials used to connect to SqlInstances to during the scan phase.
            See the '-ScanType' parameter documentation on affected scans.

        .PARAMETER ScanType
            The scans are the individual methods used to retrieve information about the scanned computer and any potentially installed instances.
            This parameter is optional, by default all scans except for establishing an actual SQL connection are performed.
            Scans can be specified in any arbitrary combination, however at least one instance detecting scan needs to be specified in order for data to be returned.

            Scans:
            DNSResolve
            - Tries resolving the computername in DNS
            Ping
            - Tries pinging the computer. Failure will NOT terminate scans.
            SQLService
            - Tries listing all SQL Services using CIM/WMI
            - This scan uses credentials specified in the '-Credential' parameter if any.
            - This scan detects instances.
            - Success in this scan guarantees high confidence (See parameter '-MinimumConfidence' for details).
            Browser
            - Tries discovering all instances via the browser service
            - This scan detects instances.
            TCPPort
            - Tries connecting to the TCP Ports.
            - By default, port 1433 is connected to.
            - The parameter '-TCPPort' can be used to provide a list of port numbers to scan.
            - This scan detects possible instances. Since other services might bind to a given port, this is not the most reliable test.
            - This scan is also used to validate found SPNs if both scans are used in combination
            SqlConnect
            - Tries to establish a SQL connection to the server
            - Uses windows credentials by default
            - Specify custom credentials using the '-SqlCredential' parameter
            - This scan is not used by default
            - Success in this scan guarantees high confidence (See parameter '-MinimumConfidence' for details).
            SPN
            - Tries looking up the Service Principal Names for each instance
            - Will use the nearest Domain Controller by default
            - Target a specific domain controller using the '-DomainController' parameter
            - If using the '-DomainController' parameter, use the '-Credential' parameter to specify the credentials used to connect

        .PARAMETER IpAddress
            This parameter can be used to override the defaults for the IPRange discovery.
            This parameter accepts a list of strings supporting any combination of:
            - Plain IP Addresses (e.g.: "10.1.1.1")
            - IP Address Ranges (e.g.: "10.1.1.1-10.1.1.5")
            - IP Address & Subnet Mask (e.g.: "10.1.1.1/255.255.255.0")
            - IP Address & Subnet Length: (e.g.: "10.1.1.1/24)
            Overlapping addresses will not result in duplicate scans.

        .PARAMETER DomainController
            The domain controller to contact for SPN lookups / searches.
            Uses the credentials from the '-Credential' parameter if specified.

        .PARAMETER TCPPort
            The ports to scan in the TCP Port Scan method.
            Defaults to 1433.

        .PARAMETER MinimumConfidence
            This command tries to discover instances, which isn't always a sure thing.
            Depending on the number and type of scans completed, we have different levels of confidence in our results.
            By default, we will return anything that we have at least a low confidence of being an instance.
            These are the confidence levels we support and how they are determined:
            - High: Established SQL Connection (including rejection for bad credentials) or service scan.
            - Medium: Browser reply or a combination of TCPConnect _and_ SPN test.
            - Low: Either TCPConnect _or_ SPN
            - None: Computer existence could be verified, but no sign of an SQL Instance

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Instance, Connect, SqlServer
            Author: Scott Sutherland, 2018 NetSPI

            Conversion & Refactoring by: Friedrich Weinmann
            Outside resources used and modified:
            https://gallery.technet.microsoft.com/scriptcenter/List-the-IP-addresses-in-a-60c5bb6b

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Find-DbaInstance

        .EXAMPLE
            PS C:\> Find-DbaInstance -DiscoveryType Domain,DataSourceEnumeration

            Performs a network search for SQL Instances by:
            - Looking up the Service Principal Names of computers in active directory
            - Using the UDP broadcast based auto-discovery of SSMS
            After that it will extensively scan all hosts thus discovered for instances.

        .EXAMPLE
            PS C:\> Find-DbaInstance -DiscoveryType All

            Performs a network search for SQL Instances, using all discovery protocols:
            - Active directory search for Service Principal Names
            - SQL Instance Enumeration (same as SSMS does)
            - All IPAddresses in the current computer's subnets of all connected network interfaces
            Note: This scan will take a long time, due to including the IP Scan

        .EXAMPLE
            PS C:\> Get-ADComputer -Filter "*" | Find-DbaInstance

            Scans all computers in the domain for SQL Instances, using a deep probe:
            - Tries resolving the name in DNS
            - Tries pinging the computer
            - Tries listing all SQL Services using CIM/WMI
            - Tries discovering all instances via the browser service
            - Tries connecting to the default TCP Port (1433)
            - Tries connecting to the TCP port of each discovered instance
            - Tries to establish a SQL connection to the server using default windows credentials
            - Tries looking up the Service Principal Names for each instance

        .EXAMPLE
            PS C:\> Get-Content .\servers.txt | Find-DbaInstance -SqlCredential $cred -ScanType Browser,SqlConnect

            Reads all servers from the servers.txt file (one server per line),
            then scans each of them for instances using the browser service
            and finally attempts to connect to each instance found using the specified credentials.
    #>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, ParameterSetName = 'Computer', ValueFromPipeline = $true)]
        [DbaInstance[]]$ComputerName,
        [Parameter(Mandatory = $true, ParameterSetName = 'Discover')]
        [Sqlcollaborative.Dbatools.Discovery.DbaInstanceDiscoveryType]$DiscoveryType,
        [System.Management.Automation.PSCredential]$Credential,
        [System.Management.Automation.PSCredential]$SqlCredential,
        [Sqlcollaborative.Dbatools.Discovery.DbaInstanceScanType]$ScanType = "Default",
        [Parameter(ParameterSetName = 'Discover')]
        [string[]]$IpAddress,
        [string]$DomainController,
        [int[]]$TCPPort = 1433,
        [Sqlcollaborative.Dbatools.Discovery.DbaInstanceConfidenceLevel]$MinimumConfidence = 'Low',
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        # TCPPort = 1 | SqlService = 4 | SPN = 16 | Browser = 32
        if (-not ($ScanType -band 53)) {
            Stop-Function -Message "Invalid Scan Types specified: $ScanType | Specify at least one of the following types: Browser, SqlService, SPN, TCPPort, Default or All. Otherwise no detection will be possible." -EnableException $EnableException -Category InvalidArgument
            return
        }

        #region Utility Functions
        function Test-SqlInstance {
            <#
            .SYNOPSIS
                Performs the actual scanning logic

            .DESCRIPTION
                Performs the actual scanning logic
                Each potential target is accessed using the specified scan routines.

            .PARAMETER Target
                The target to scan.

            .EXAMPLE
                PS C:\> Test-SqlInstance
        #>
            [CmdletBinding()]
            param (
                [Parameter(ValueFromPipeline = $true)][DbaInstance[]]$Target,
                [PSCredential]$Credential,
                [PSCredential]$SqlCredential,
                [Sqlcollaborative.Dbatools.Discovery.DbaInstanceScanType]$ScanType,
                [string]$DomainController,
                [int[]]$TCPPort = 1433,
                [Sqlcollaborative.Dbatools.Discovery.DbaInstanceConfidenceLevel]$MinimumConfidence,
                [switch]$EnableException
            )

            begin {
                [System.Collections.ArrayList]$computersScanned = @()
            }

            process {
                foreach ($computer in $Target) {
                    if ($computersScanned.Contains($computer.ComputerName)) {
                        continue
                    }
                    else {
                        $null = $computersScanned.Add($computer.ComputerName)
                    }
                    Write-Message -Level Verbose -Message "Processing: $($computer)" -Target $computer -FunctionName Find-DbaInstance

                    #region Null variables to prevent scope lookup on conditional existence
                    $resolution = $null
                    $pingReply = $null
                    $sPNs = @()
                    $ports = @()
                    $browseResult = $null
                    $services = @()
                    $serverObject = $null
                    $browseFailed = $false
                    #endregion Null variables to prevent scope lookup on conditional existence

                    #region Gather data
                    if ($ScanType -band [Sqlcollaborative.Dbatools.Discovery.DbaInstanceScanType]::DNSResolve) {
                        try { $resolution = [System.Net.Dns]::GetHostEntry($computer.ComputerName) }
                        catch { }
                    }

                    if ($ScanType -band [Sqlcollaborative.Dbatools.Discovery.DbaInstanceScanType]::Ping) {
                        $ping = New-Object System.Net.NetworkInformation.Ping
                        try { $pingReply = $ping.Send($computer.ComputerName) }
                        catch { }
                    }

                    if ($ScanType -band [Sqlcollaborative.Dbatools.Discovery.DbaInstanceScanType]::SPN) {
                        $computerByName = $computer.ComputerName
                        if ($resolution.HostName) { $computerByName = $resolution.HostName }
                        if ($computerByName -notmatch "$([dbargx]::IPv4)|$([dbargx]::IPv6)") {
                            try { $sPNs = Get-DomainSPN -DomainController $DomainController -Credential $Credential -ComputerName $computerByName -GetSPN }
                            catch { }
                        }
                    }

                    if ($ScanType -band [Sqlcollaborative.Dbatools.Discovery.DbaInstanceScanType]::TCPPort) {
                        $ports = $TCPPort | Test-TcpPort -ComputerName $computer
                    }

                    if ($ScanType -band [Sqlcollaborative.Dbatools.Discovery.DbaInstanceScanType]::Browser) {
                        try {
                            $browseResult = Get-SQLInstanceBrowserUDP -ComputerName $computer -EnableException
                        }
                        catch {
                            $browseFailed = $true
                        }
                    }

                    if ($ScanType -band [Sqlcollaborative.Dbatools.Discovery.DbaInstanceScanType]::SqlService) {
                        if ($Credential) { $services = Get-DbaSqlService -ComputerName $computer -Credential $Credential -EnableException -ErrorAction Ignore -WarningAction SilentlyCOntinue }
                        else { $services = Get-DbaSqlService -ComputerName $computer -ErrorAction Ignore -WarningAction SilentlyContinue }
                    }
                    #endregion Gather data

                    #region Gather list of found instance indicators
                    $instanceNames = @()
                    if ($Services) {
                        $Services | Select-Object -ExpandProperty InstanceName -Unique | Where-Object { $_ -and ($instanceNames -notcontains $_) } | ForEach-Object {
                            $instanceNames += $_
                        }
                    }
                    if ($browseResult) {
                        $browseResult | Select-Object -ExpandProperty InstanceName -Unique | Where-Object { $_ -and ($instanceNames -notcontains $_) } | ForEach-Object {
                            $instanceNames += $_
                        }
                    }

                    $portsDetected = @()
                    foreach ($portResult in $ports) {
                        if ($portResult.IsOpen) { $portsDetected += $portResult.Port }
                    }
                    foreach ($sPN in $sPNs) {
                        try { $inst = $sPN.Split(':')[1] }
                        catch { continue }

                        try {
                            [int]$portNumber = $inst
                            if ($portNumber -and ($portsDetected -notcontains $portNumber)) {
                                $portsDetected += $portNumber
                            }
                        }
                        catch {
                            if ($inst -and ($instanceNames -notcontains $inst)) {
                                $instanceNames += $inst
                            }
                        }
                    }
                    #endregion Gather list of found instance indicators

                    #region Case: Nothing found
                    if ((-not $instanceNames) -and (-not $portsDetected)) {
                        if ($resolution -or ($pingReply.Status -like "Success")) {
                            if ($MinimumConfidence -eq [Sqlcollaborative.Dbatools.Discovery.DbaInstanceConfidenceLevel]::None) {
                                New-Object Sqlcollaborative.Dbatools.Discovery.DbaInstanceReport -Property @{
                                    MachineName  = $computer.ComputerName
                                    ComputerName = $computer.ComputerName
                                    Ping         = $pingReply.Status -like 'Success'
                                }
                            }
                            else {
                                Write-Message -Level Verbose -Message "Computer $computer could be contacted, but no trace of an SQL Instance was found. Skipping..." -Target $computer -FunctionName Find-DbaInstance
                            }
                        }
                        else {
                            Write-Message -Level Verbose -Message "Computer $computer could not be contacted, skipping." -Target $computer -FunctionName Find-DbaInstance
                        }

                        continue
                    }
                    #endregion Case: Nothing found

                    [System.Collections.ArrayList]$masterList = @()

                    #region Case: Named instance found
                    foreach ($instance in $instanceNames) {
                        $object = New-Object Sqlcollaborative.Dbatools.Discovery.DbaInstanceReport
                        $object.MachineName = $computer.ComputerName
                        $object.ComputerName = $computer.ComputerName
                        $object.InstanceName = $instance
                        $object.DnsResolution = $resolution
                        $object.Ping = $pingReply.Status -like 'Success'
                        $object.ScanTypes = $ScanType
                        $object.Services = $services | Where-Object InstanceName -EQ $instance
                        $object.SystemServices = $services | Where-Object { -not $_.InstanceName }
                        $object.SPNs = $sPNs

                        if ($result = $browseResult | Where-Object InstanceName -EQ $instance) {
                            $object.BrowseReply = $result
                        }
                        if ($ports) {
                            $object.PortsScanned = $ports
                        }

                        if ($object.BrowseReply) {
                            $object.Confidence = 'Medium'
                            if ($object.BrowseReply.TCPPort) {
                                $object.Port = $object.BrowseReply.TCPPort

                                $object.PortsScanned | Where-Object Port -EQ $object.Port | ForEach-Object {
                                    $object.TcpConnected = $_.IsOpen
                                }
                            }
                        }
                        if ($object.Services) {
                            $object.Confidence = 'High'

                            $engine = $object.Services | Where-Object ServiceType -EQ "Engine"
                            switch ($engine.State) {
                                "Running" { $object.Availability = 'Available' }
                                "Stopped" { $object.Availability = 'Unavailable' }
                                default { $object.Availability = 'Unknown' }
                            }
                        }

                        $object.Timestamp = Get-Date

                        $masterList += $object
                    }
                    #endregion Case: Named instance found

                    #region Case: Port number found
                    foreach ($port in $portsDetected) {
                        if ($masterList.Port -contains $port) { continue }

                        $object = New-Object Sqlcollaborative.Dbatools.Discovery.DbaInstanceReport
                        $object.MachineName = $computer.ComputerName
                        $object.ComputerName = $computer.ComputerName
                        $object.Port = $port
                        $object.DnsResolution = $resolution
                        $object.Ping = $pingReply.Status -like 'Success'
                        $object.ScanTypes = $ScanType
                        $object.SystemServices = $services | Where-Object { -not $_.InstanceName }
                        $object.SPNs = $sPNs
                        $object.Confidence = 'Low'
                        if ($ports) {
                            $object.PortsScanned = $ports

                            if (($ports | Where-Object IsOpen).Port -eq 1433) {
                                $object.Confidence = 'Medium'
                            }
                        }

                        if (($ports.Port -contains $port) -and ($sPNs | Where-Object { $_ -like "*:$port" })) {
                            $object.Confidence = 'Medium'
                        }

                        $object.PortsScanned | Where-Object Port -EQ $object.Port | ForEach-Object {
                            $object.TcpConnected = $_.IsOpen
                        }
                        $object.Timestamp = Get-Date

                        if ($masterList.SqlInstance -contains $object.SqlInstance) {
                            continue
                        }

                        $masterList += $object
                    }
                    #endregion Case: Port number found

                    if ($ScanType -band [Sqlcollaborative.Dbatools.Discovery.DbaInstanceScanType]::SqlConnect) {
                        $instanceHash = @{ }
                        $toDelete = @()
                        foreach ($dataSet in $masterList) {
                            try {
                                $server = Connect-SqlInstance -SqlInstance $dataSet.FullSmoName -SqlCredential $SqlCredential
                                $dataSet.SqlConnected = $true
                                $dataSet.Confidence = 'High'

                                # Remove duplicates
                                if ($instanceHash.ContainsKey($server.DomainInstanceName)) {
                                    $toDelete += $dataSet
                                }
                                else {
                                    $instanceHash[$server.DomainInstanceName] = $dataSet

                                    try {
                                        $dataSet.MachineName = $server.ComputerNamePhysicalNetBIOS
                                    }
                                    catch { }
                                }
                            }
                            catch {
                                # Error class definitions
                                # https://docs.microsoft.com/en-us/sql/relational-databases/errors-events/database-engine-error-severities
                                # 24 or less means an instance was found, but had some issues

                                #region Processing error (Access denied, server error, ...)
                                if ($_.Exception.InnerException.Errors.Class -lt 25) {
                                    # There IS an SQL Instance and it listened to network traffic
                                    $dataSet.SqlConnected = $true
                                    $dataSet.Confidence = 'High'
                                }
                                #endregion Processing error (Access denied, server error, ...)

                                #region Other connection errors
                                else {
                                    $dataSet.SqlConnected = $false
                                }
                                #endregion Other connection errors
                            }
                        }

                        foreach ($item in $toDelete) {
                            $masterList.Remove($item)
                        }
                    }

                    $masterList
                }
            }
        }

        function Get-DomainSPN {
            <#
            .SYNOPSIS
                Returns all computernames with registered MSSQL SPNs.

            .DESCRIPTION
                Returns all computernames with registered MSSQL SPNs.

            .PARAMETER DomainController
                The domain controller to ask.

            .PARAMETER Credential
                The credentials to use while asking.

            .PARAMETER ComputerName
                Filter by computername

            .PARAMETER GetSPN
                Returns the service SPNs instead of the hostname

            .EXAMPLE
                PS C:\> Get-DomainSPN -DomainController $DomainController -Credential $Credential

                Returns all computernames with MSQL SPNs known to $DomainController, assuming credentials are valid.
        #>
            [CmdletBinding()]
            param (
                [string]$DomainController,
                [object]$Credential,
                [string]$ComputerName = "*",
                [switch]$GetSPN
            )

            try {
                if ($DomainController) {
                    if ($Credential) {
                        $entry = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList "LDAP://$DomainController", $Credential.UserName, $Credential.GetNetworkCredential().Password
                    }
                    else {
                        $entry = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList "LDAP://$DomainController"
                    }
                }
                else {
                    $entry = [ADSI]''
                }
                $objSearcher = New-Object -TypeName System.DirectoryServices.DirectorySearcher -ArgumentList $entry

                $objSearcher.PageSize = 200
                $objSearcher.Filter = "(&(objectcategory=computer)(servicePrincipalName=MSSQLsvc*)(|(name=$ComputerName)(dnshostname=$ComputerName)))"
                $objSearcher.SearchScope = 'Subtree'

                $results = $objSearcher.FindAll()
                foreach ($computer in $results) {
                    if ($GetSPN) {
                        $computer.Properties["serviceprincipalname"] | Where-Object { $_ -like "MSSQLsvc*:*" }
                    }
                    else {
                        if ($computer.Properties["dnshostname"]) {
                            $computer.Properties["dnshostname"][0]
                        }
                        else {
                            $computer.Properties["name"][0]
                        }
                    }
                }
            }
            catch {
                throw
            }
        }

        function Get-SQLInstanceBrowserUDP {
            <#
            .SYNOPSIS
                Requests a list of instances from the browser service.

            .DESCRIPTION
                Requests a list of instances from the browser service.

            .PARAMETER ComputerName
                Computer name or IP address to enumerate SQL Instance from.

            .PARAMETER UDPTimeOut
                Timeout in seconds. Longer timeout = more accurate.

            .PARAMETER EnableException
                By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
                This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
                Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

            .EXAMPLE
                PS C:\> Get-SQLInstanceBrowserUDP -ComputerName 'sql2017'

                Contacts the browsing service on sql2017 and requests its instance information.

            .NOTES
                Original Author: Eric Gruber
                Editors:
                - Scott Sutherland (Pipeline and timeout mods)
                - Friedrich Weinmann (Cleanup & dbatools Standardization)

        #>
            [CmdletBinding()]
            param (
                [Parameter(Mandatory = $true, ValueFromPipeline = $true)][DbaInstance[]]$ComputerName,
                [int]$UDPTimeOut = 2,
                [switch]$EnableException
            )

            process {
                foreach ($computer in $ComputerName) {
                    try {
                        #region Connect to browser service and receive response
                        $UDPClient = New-Object -TypeName System.Net.Sockets.Udpclient
                        $UDPClient.Client.ReceiveTimeout = $UDPTimeOut * 1000
                        $UDPClient.Connect($computer.ComputerName, 1434)
                        $UDPPacket = 0x03
                        $UDPEndpoint = New-Object -TypeName System.Net.IpEndPoint -ArgumentList ([System.Net.Ipaddress]::Any, 0)
                        $UDPClient.Client.Blocking = $true
                        [void]$UDPClient.Send($UDPPacket, $UDPPacket.Length)
                        $BytesRecived = $UDPClient.Receive([ref]$UDPEndpoint)
                        # Skip first three characters, since those contain trash data (SSRP metadata)
                        #$Response = [System.Text.Encoding]::ASCII.GetString($BytesRecived[3..($BytesRecived.Length - 1)])
                        $Response = [System.Text.Encoding]::ASCII.GetString($BytesRecived)
                        #endregion Connect to browser service and receive response

                        #region Parse Output
                        $Response | Select-String "(ServerName;(\w+);InstanceName;(\w+);IsClustered;(\w+);Version;(\d+\.\d+\.\d+\.\d+);(tcp;(\d+)){0,1})" -AllMatches | Select-Object -ExpandProperty Matches | ForEach-Object {
                            $obj = New-Object Sqlcollaborative.Dbatools.Discovery.DbaBrowserReply -Property @{
                                MachineName  = $computer.ComputerName
                                ComputerName = $_.Groups[2].Value
                                SqlInstance  = "$($_.Groups[2].Value)\$($_.Groups[3].Value)"
                                InstanceName = $_.Groups[3].Value
                                Version      = $_.Groups[5].Value
                                IsClustered  = "Yes" -eq $_.Groups[4].Value
                            }
                            if ($_.Groups[7].Success) {
                                $obj.TCPPort = $_.Groups[7].Value
                            }
                            $obj
                        }
                        #endregion Parse Output

                        $UDPClient.Close()
                    }
                    catch {
                        try {
                            $UDPClient.Close()
                        }
                        catch {
                        }

                        if ($EnableException) { throw }
                    }
                }
            }
        }

        function Test-TcpPort {
            <#
            .SYNOPSIS
                Tests whether a TCP Port is open or not.

            .DESCRIPTION
                Tests whether a TCP Port is open or not.

            .PARAMETER ComputerName
                The name of the computer to scan.

            .PARAMETER Port
                The port(s) to scan.

            .EXAMPLE
                PS C:\> $ports | Test-TcpPort -ComputerName "foo"

                Tests for each port in $ports whether the TCP port is open on computer "foo"
        #>
            [CmdletBinding()]
            param (
                [DbaInstance]$ComputerName,
                [Parameter(ValueFromPipeline = $true)][int[]]$Port
            )

            begin {
                $client = New-Object Net.Sockets.TcpClient
            }
            process {
                foreach ($item in $Port) {
                    try {
                        $client.Connect($ComputerName.ComputerName, $item)
                        if ($client.Connected) {
                            $client.Close()
                            New-Object -TypeName Sqlcollaborative.Dbatools.Discovery.DbaPortReport -ArgumentList $ComputerName.ComputerName, $item, $true
                        }
                        else {
                            New-Object -TypeName Sqlcollaborative.Dbatools.Discovery.DbaPortReport -ArgumentList $ComputerName.ComputerName, $item, $false
                        }
                    }
                    catch {
                        New-Object -TypeName Sqlcollaborative.Dbatools.Discovery.DbaPortReport -ArgumentList $ComputerName.ComputerName, $item, $false
                    }
                }
            }
        }

        function Get-IPrange {
            <#
            .SYNOPSIS
                Get the IP addresses in a range

            .DESCRIPTION
                A detailed description of the Get-IPrange function.

            .PARAMETER Start
                A description of the Start parameter.

            .PARAMETER End
                A description of the End parameter.

            .PARAMETER IPAddress
                A description of the IPAddress parameter.

            .PARAMETER Mask
                A description of the Mask parameter.

            .PARAMETER Cidr
                A description of the Cidr parameter.

            .EXAMPLE
                Get-IPrange -Start 192.168.8.2 -End 192.168.8.20

            .EXAMPLE
                Get-IPrange -IPAddress 192.168.8.2 -Mask 255.255.255.0

            .EXAMPLE
                Get-IPrange -IPAddress 192.168.8.3 -Cidr 24

            .NOTES
                Author: BarryCWT
                Reference: https://gallery.technet.microsoft.com/scriptcenter/List-the-IP-addresses-in-a-60c5bb6b
        #>

            param
            (
                [string]$Start,
                [string]$End,
                [string]$IPAddress,
                [string]$Mask,
                [int]$Cidr
            )

            function IP-toINT64 {
                param ($ip)

                $octets = $ip.split(".")
                return [int64]([int64]$octets[0] * 16777216 + [int64]$octets[1] * 65536 + [int64]$octets[2] * 256 + [int64]$octets[3])
            }

            function INT64-toIP {
                param ([int64]$int)

                return ([System.Net.IPAddress](([math]::truncate($int/16777216)).tostring() + "." + ([math]::truncate(($int % 16777216)/65536)).tostring() + "." + ([math]::truncate(($int % 65536)/256)).tostring() + "." + ([math]::truncate($int % 256)).tostring()))
            }

            if ($Cidr) {
                $maskaddr = [Net.IPAddress]::Parse((INT64-toIP -int ([convert]::ToInt64(("1" * $Cidr + "0" * (32 - $Cidr)), 2))))
            }
            if ($Mask) {
                $maskaddr = [Net.IPAddress]::Parse($Mask)
            }
            if ($IPAddress) {
                $ipaddr = [Net.IPAddress]::Parse($IPAddress)
                $networkaddr = new-object net.ipaddress ($maskaddr.address -band $ipaddr.address)
                $broadcastaddr = new-object net.ipaddress (([system.net.ipaddress]::parse("255.255.255.255").address -bxor $maskaddr.address -bor $networkaddr.address))
                $startaddr = IP-toINT64 -ip $networkaddr.ipaddresstostring
                $endaddr = IP-toINT64 -ip $broadcastaddr.ipaddresstostring
            }
            else {
                $startaddr = IP-toINT64 -ip $Start
                $endaddr = IP-toINT64 -ip $End
            }

            for ($i = $startaddr; $i -le $endaddr; $i++) {
                INT64-toIP -int $i
            }
        }

        function Resolve-IPRange {
            <#
            .SYNOPSIS
                Returns a number of IPAddresses based on range specified.

            .DESCRIPTION
                Returns a number of IPAddresses based on range specified.
                Warning: A too large range can lead to memory exceptions.

                Scans subnet of active computer if no address is specified.

            .PARAMETER IpAddress
                The address / range / mask / cidr to scan. Example input:
                - 10.1.1.1
                - 10.1.1.1/24
                - 10.1.1.1-10.1.1.254
                - 10.1.1.1/255.255.255.0
        #>
            [CmdletBinding()]
            param (
                [AllowEmptyString()][string]$IpAddress
            )

            #region Scan defined range
            if ($IpAddress) {
                #region Determine processing mode
                $mode = 'Unknown'
                if ($IpAddress -like "*/*") {
                    $parts = $IpAddress.Split("/")

                    $address = $parts[0]
                    if ($parts[1] -match ([dbargx]::IPv4)) {
                        $mask = $parts[1]
                        $mode = 'Mask'
                    }
                    elseif ($parts[1] -as [int]) {
                        $cidr = [int]$parts[1]

                        if (($cidr -lt 8) -or ($cidr -gt 31)) {
                            throw "$IpAddress does not contain a valid cidr mask!"
                        }

                        $mode = 'CIDR'
                    }
                    else {
                        throw "$IpAddress is not a valid IP Range!"
                    }
                }
                elseif ($IpAddress -like "*-*") {
                    $rangeStart = $IpAddress.Split("-")[0]
                    $rangeEnd = $IpAddress.Split("-")[1]

                    if ($rangeStart -notmatch ([dbargx]::IPv4)) {
                        throw "$IpAddress is not a valid IP Range!"
                    }
                    if ($rangeEnd -notmatch ([dbargx]::IPv4)) {
                        throw "$IpAddress is not a valid IP Range!"
                    }

                    $mode = 'Range'
                }
                else {
                    if ($IpAddress -notmatch ([dbargx]::IPv4)) {
                        throw "$IpAddress is not a valid IP Address!"
                    }
                    return $IpAddress
                }
                #endregion Determine processing mode

                switch ($mode) {
                    'CIDR' {
                        Get-IPrange -IPAddress $address -Cidr $cidr
                    }
                    'Mask' {
                        Get-IPrange -IPAddress $address -Mask $mask
                    }
                    'Range' {
                        Get-IPrange -Start $rangeStart -End $rangeEnd
                    }
                }
            }
            #endregion Scan defined range

            #region Scan own computer range
            else {
                foreach ($interface in ([System.Net.NetworkInformation.NetworkInterface]::GetAllNetworkInterfaces() | Where-Object NetworkInterfaceType -Like '*Ethernet*')) {
                    foreach ($property in ($interface.GetIPProperties().UnicastAddresses | Where-Object { $_.Address.AddressFamily -like "InterNetwork" })) {
                        Get-IPrange -IPAddress $property.Address -Cidr $property.PrefixLength
                    }
                }
            }
            #endregion Scan own computer range
        }
        #endregion Utility Functions

        #region Build parameter Splat for scan
        $paramTestSqlInstance = @{
            ScanType          = $ScanType
            TCPPort           = $TCPPort
            EnableException   = $EnableException
            MinimumConfidence = $MinimumConfidence
        }

        # Only specify when passed by user to avoid credential prompts on PS3/4
        if ($SqlCredential) {
            $paramTestSqlInstance["SqlCredential"] = $SqlCredential
        }
        if ($Credential) {
            $paramTestSqlInstance["Credential"] = $Credential
        }
        if ($DomainController) {
            $paramTestSqlInstance["DomainController"] = $DomainController
        }
        #endregion Build parameter Splat for scan

        # Prepare item processing in a pipeline compliant way
        $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Test-SqlInstance', [System.Management.Automation.CommandTypes]::Function)
        $scriptCmd = {
            & $wrappedCmd @paramTestSqlInstance
        }
        $steppablePipeline = $scriptCmd.GetSteppablePipeline()
        $steppablePipeline.Begin($true)
    }

    process {
        if (Test-FunctionInterrupt) { return }
        #region Process items or discover stuff
        switch ($PSCmdlet.ParameterSetName) {
            'Computer' {
                $ComputerName | Invoke-SteppablePipeline -Pipeline $steppablePipeline
            }
            'Discover' {
                #region Discovery: DataSource Enumeration
                if ($DiscoveryType -band ([Sqlcollaborative.Dbatools.Discovery.DbaInstanceDiscoveryType]::DataSourceEnumeration)) {
                    try {
                        # Discover instances
                        foreach ($instance in ([System.Data.Sql.SqlDataSourceEnumerator]::Instance.GetDataSources())) {
                            if ($instance.InstanceName -ne [System.DBNull]::Value) {
                                $steppablePipeline.Process("$($instance.Servername)\$($instance.InstanceName)")
                            }
                            else {
                                $steppablePipeline.Process($instance.Servername)
                            }
                        }
                    }
                    catch {
                        Write-Message -Level Warning -Message "Datasource enumeration failed" -ErrorRecord $_ -EnableException $EnableException.ToBool()
                    }
                }
                #endregion Discovery: DataSource Enumeration

                #region Discovery: SPN Search
                if ($DiscoveryType -band ([Sqlcollaborative.Dbatools.Discovery.DbaInstanceDiscoveryType]::Domain)) {
                    try {
                        Get-DomainSPN -DomainController $DomainController -Credential $Credential -ErrorAction Stop | Invoke-SteppablePipeline -Pipeline $steppablePipeline
                    }
                    catch {
                        Write-Message -Level Warning -Message "Failed to execute Service Principal Name discovery" -ErrorRecord $_ -EnableException $EnableException.ToBool()
                    }
                }
                #endregion Discovery: SPN Search

                #region Discovery: IP Range
                if ($DiscoveryType -band ([Sqlcollaborative.Dbatools.Discovery.DbaInstanceDiscoveryType]::IPRange)) {
                    if ($IpAddress) {
                        foreach ($address in $IpAddress) {
                            Resolve-IPRange -IpAddress $address | Invoke-SteppablePipeline -Pipeline $steppablePipeline
                        }
                    }
                    else {
                        Resolve-IPRange | Invoke-SteppablePipeline -Pipeline $steppablePipeline
                    }
                }
                #endregion Discovery: IP Range
            }
            default {
                Stop-Function -Message "Invalid parameterset, some developer probably had a beer too much. Please file an issue so we can fix this" -EnableException $EnableException
                return
            }
        }
        #endregion Process items or discover stuff
    }

    end {
        if (Test-FunctionInterrupt) {
            return
        }
        $steppablePipeline.End()
    }
}
tools\dbatools\functions\Find-DbaLoginInGroup.ps1
#ValidationTags#Messaging#
function Find-DbaLoginInGroup {
    <#
        .SYNOPSIS
            Finds Logins in Active Directory groups that have logins on the SQL Instance.

        .DESCRIPTION
            Outputs all the active directory groups members for a server, or limits it to find a specific AD user in the groups

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a
            collection and receive pipeline input.

        .PARAMETER SqlCredential
            PSCredential object to connect under. If not specified, current Windows login will be used.

        .PARAMETER Login
            Find all AD Groups used on the instance that an individual login is a member of.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Login, AD, ActiveDirectory, Group, Security
            Author: Stephen Bennett, https://sqlnotesfromtheunderground.wordpress.com/
            Author: Simone Bizzotto, @niphlod

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Find-DbaLoginInGroup

        .EXAMPLE
            Find-DbaLoginInGroup -SqlInstance DEV01 -Login "MyDomain\Stephen.Bennett"

            Returns all active directory groups with logins on Sql Instance DEV01 that contain the AD user Stephen.Bennett.

        .EXAMPLE
            Find-DbaLoginInGroup -SqlInstance DEV01

            Returns all active directory users within all windows AD groups that have logins on the instance.

        .EXAMPLE
            Find-DbaLoginInGroup -SqlInstance DEV01 | Where-Object Login -like '*stephen*'

            Returns all active directory users within all windows AD groups that have logins on the instance whose login contains 'stephen'

    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$Login,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        try {
            Add-Type -AssemblyName System.DirectoryServices.AccountManagement
        }
        catch {
            Stop-Function -Message "Failed to load Assembly needed" -ErrorRecord $_
        }

        function Get-AllLogins {
            param
            (
                [string]$ADGroup,
                [string[]]$discard,
                [string]$ParentADGroup
            )
            begin {
                $output = @()
            }
            process {
                try {
                    $domain = $AdGroup.Split("\")[0]
                    $ads = New-Object System.DirectoryServices.AccountManagement.PrincipalContext('Domain', $domain)
                    [string]$groupName = $AdGroup
                    $group = [System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity($ads, $groupName);
                    $subgroups = @()
                    foreach ($member in $group.Members) {
                        $memberDomain = $member.DistinguishedName -Split "," | Where-Object { $_ -like "DC=*" } | Select-Object -first 1 | ForEach-Object { $_.ToUpper() -replace "DC=", '' }
                        if ($member.StructuralObjectClass -eq "group") {
                            $fullName = $memberDomain + "\" + $member.SamAccountName
                            if ($fullName -in $discard) {
                                Write-Message -Level Verbose -Message "skipping $fullName, already enumerated"
                                continue
                            }
                            else {
                                $subgroups += $fullName
                            }
                        }
                        else {
                            $output += [PSCustomObject]@{
                                SqlInstance        = $server.Name
                                InstanceName       = $server.ServiceName
                                ComputerName       = $server.ComputerName
                                Login              = $memberDomain + "\" + $member.SamAccountName
                                DisplayName        = $member.DisplayName
                                MemberOf           = $AdGroup
                                ParentADGroupLogin = $ParentADGroup
                            }
                        }
                    }
                }
                catch {
                    Stop-Function -Message "Failed to connect to Group: $member." -Target $member -ErrorRecord $_
                }
                $discard += $ADGroup
                foreach ($gr in $subgroups) {
                    if ($gr -notin $discard) {
                        $discard += $gr
                        Write-Message -Level Verbose -Message "Looking at $gr, recursively."
                        Get-AllLogins -ADGroup $gr -discard $discard -ParentADGroup $ParentADGroup
                    }
                }
            }
            end {
                $output
            }
        }
    }

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $AdGroups = $server.Logins | Where-Object { $_.LoginType -eq "WindowsGroup" -and $_.Name -ne "BUILTIN\Administrators" -and $_.Name -notlike "*NT SERVICE*" }

            foreach ($AdGroup in $AdGroups) {
                Write-Message -Level Verbose -Message "Looking at Group: $AdGroup"
                $ADGroupOut += Get-AllLogins $AdGroup.Name -ParentADGroup $AdGroup.Name
            }

            if (-not $Login) {
                $res = $ADGroupOut
            }
            else {
                $res = $ADGroupOut | Where-Object { $Login -contains $_.Login }
                if ($res.Length -eq 0) {
                    Write-Message -Level Warning -Message "No logins matching $($Login -join ',') found connecting to $server"
                    continue
                }
            }
            Select-DefaultView -InputObject $res -Property SqlInstance, Login, DisplayName, MemberOf, ParentADGroupLogin
        }
    }
}
tools\dbatools\functions\Find-DbaOrphanedFile.ps1
#ValidationTags#FlowControl,Pipeline#
function Find-DbaOrphanedFile {
    <#
        .SYNOPSIS
            Find-DbaOrphanedFile finds orphaned database files. Orphaned database files are files not associated with any attached database.

        .DESCRIPTION
            This command searches all directories associated with SQL database files for database files that are not currently in use by the SQL Server instance.

            By default, it looks for orphaned .mdf, .ldf and .ndf files in the root\data directory, the default data path, the default log path, the system paths and any directory in use by any attached directory.

            You can specify additional filetypes using the -FileType parameter, and additional paths to search using the -Path parameter.

        .PARAMETER SqlInstance
            The SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Path
            Specifies one or more directories to search in addition to the default data and log directories.

        .PARAMETER FileType
            Specifies file extensions other than mdf, ldf and ndf to search for. Do not include the dot (".") when specifying the extension.

        .PARAMETER LocalOnly
            If this switch is enabled, only local filenames will be returned. Using this switch with multiple servers is not recommended since it does not return the associated server name.

        .PARAMETER RemoteOnly
            If this switch is enabled, only remote filenames will be returned.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Orphan, Database, DatabaseFile
            Author: Sander Stad (@sqlstad), sqlstad.nl
            Requires: sysadmin access on SQL Servers

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

            Thanks to Paul Randal's notes on FILESTREAM which can be found at http://www.sqlskills.com/blogs/paul/filestream-directory-structure/

        .LINK
            https://dbatools.io/Find-DbaOrphanedFile

        .EXAMPLE
            Find-DbaOrphanedFile -SqlInstance sqlserver2014a

            Connects to sqlserver2014a, authenticating with Windows credentials, and searches for orphaned files. Returns server name, local filename, and unc path to file.

        .EXAMPLE
            Find-DbaOrphanedFile -SqlInstance sqlserver2014a -SqlCredential $cred

            Connects to sqlserver2014a, authenticating with SQL Server authentication, and searches for orphaned files. Returns server name, local filename, and unc path to file.

        .EXAMPLE
            Find-DbaOrphanedFile -SqlInstance sql2014 -Path 'E:\Dir1', 'E:\Dir2'

            Finds the orphaned files in "E:\Dir1" and "E:Dir2" in addition to the default directories.

        .EXAMPLE
            Find-DbaOrphanedFile -SqlInstance sql2014 -LocalOnly

            Returns only the local filepaths for orphaned files.

        .EXAMPLE
            Find-DbaOrphanedFile -SqlInstance sql2014 -RemoteOnly

            Returns only the remote filepath for orphaned files.

        .EXAMPLE
            Find-DbaOrphanedFile -SqlInstance sql2014, sql2016 -FileType fsf, mld

            Finds the orphaned ending with ".fsf" and ".mld" in addition to the default filetypes ".mdf", ".ldf", ".ndf" for both the servers sql2014 and sql2016.

    #>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [parameter(Mandatory = $false)]
        [object]$SqlCredential,
        [parameter(Mandatory = $false)]
        [string[]]$Path,
        [string[]]$FileType,
        [switch]$LocalOnly,
        [switch]$RemoteOnly,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        function Get-SQLDirTreeQuery {
            param($PathList)
            # use sysaltfiles in lower versions

            $q1 = "CREATE TABLE #enum ( id int IDENTITY, fs_filename nvarchar(512), depth int, is_file int, parent nvarchar(512) ); DECLARE @dir nvarchar(512);"
            $q2 = "SET @dir = 'dirname';

                INSERT INTO #enum( fs_filename, depth, is_file )
                EXEC xp_dirtree @dir, 1, 1;

                UPDATE #enum
                SET parent = @dir,
                fs_filename = ltrim(rtrim(fs_filename))
                WHERE parent IS NULL;"

            $query_files_sql = "SELECT e.fs_filename AS filename, e.parent
                    FROM #enum AS e
                    WHERE e.fs_filename NOT IN( 'xtp', '5', '`$FSLOG', '`$HKv2', 'filestream.hdr' )
                    AND is_file = 1;"

            # build the query string based on how many directories they want to enumerate
            $sql = $q1
            $sql += $($PathList | Where-Object { $_ -ne '' } | ForEach-Object { "$([System.Environment]::Newline)$($q2 -Replace 'dirname', $_)" })
            $sql += $query_files_sql
            Write-Message -Level Debug -Message $sql
            return $sql
        }
        function Get-SqlFileStructure {
            param
            (
                [Parameter(Mandatory = $true, Position = 1)]
                [Microsoft.SqlServer.Management.Smo.SqlSmoObject]$smoserver
            )
            if ($smoserver.versionMajor -eq 8) {
                $sql = "select filename from sysaltfiles"
            }
            else {
                $sql = "select physical_name as filename from sys.master_files"
            }

            $dbfiletable = $smoserver.ConnectionContext.ExecuteWithResults($sql)
            $ftfiletable = $dbfiletable.Tables[0].Clone()
            $dbfiletable.Tables[0].TableName = "data"

            # Add support for Full Text Catalogs in Sql Server 2005 and below
            if ($server.VersionMajor -lt 10) {
                $databaselist = $smoserver.Databases | Select-Object -property  Name, IsFullTextEnabled
                foreach ($db in $databaselist) {
                    if ($db.IsFullTextEnabled -eq $false) {
                        continue
                    }
                    $database = $db.name
                    $fttable = $null = $smoserver.Databases[$database].ExecuteWithResults('sp_help_fulltext_catalogs')
                    foreach ($ftc in $fttable.Tables[0].rows) {
                        $null = $ftfiletable.Rows.add($ftc.Path)
                    }
                }
            }

            $null = $dbfiletable.Tables.Add($ftfiletable)
            return $dbfiletable.Tables.Filename
        }

        function Format-Path {
            param ($path)

            $path = $path.Trim()
            #Thank you windows 2000
            $path = $path -replace '[^A-Za-z0-9 _\.\-\\:]', '__'
            return $path
        }

        $FileType += "mdf", "ldf", "ndf"
        $systemfiles = "distmdl.ldf", "distmdl.mdf", "mssqlsystemresource.ldf", "mssqlsystemresource.mdf"

        $FileTypeComparison = $FileType | ForEach-Object {$_.ToLower()} | Where-Object { $_ } | Sort-Object | Get-Unique
    }

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            # Reset all the arrays
            $dirtreefiles = $valid = $paths = $matching = @()

            $filestructure = Get-SqlFileStructure $server

            # Get any paths associated with current data and log files
            foreach ($file in $filestructure) {
                $paths += Split-Path -Path $file -Parent
            }

            # Get the default data and log directories from the instance
            Write-Message -Level Debug -Message "Adding paths"
            $paths += $server.RootDirectory + "\DATA"
            $paths += Get-SqlDefaultPaths $server data
            $paths += Get-SqlDefaultPaths $server log
            $paths += $server.MasterDBPath
            $paths += $server.MasterDBLogPath
            $paths += $Path
            $paths = $paths | ForEach-Object { "$_".TrimEnd("\") } | Sort-Object | Get-Unique
            $sql = Get-SQLDirTreeQuery $paths
            $datatable = $server.Databases['master'].ExecuteWithResults($sql).Tables[0]

            foreach ($row in $datatable) {
                $fullpath = [IO.Path]::combine($row.parent, $row.filename)
                $dirtreefiles += [pscustomobject]@{
                    FullPath   = $fullpath
                    Comparison = [IO.Path]::GetFullPath($(Format-Path $fullpath))
                }
            }
            $dirtreefiles = $dirtreefiles | Where-Object { $_ } | Sort-Object Comparison -Unique

            foreach ($file in $filestructure) {
                $valid += [IO.Path]::GetFullPath($(Format-Path $file))
            }

            $valid = $valid | Sort-Object | Get-Unique

            foreach ($file in $dirtreefiles.Comparison) {
                foreach ($type in $FileTypeComparison) {
                    if ($file.ToLower().EndsWith($type)) {
                        $matching += $file
                        break
                    }
                }
            }

            $dirtreematcher = @{}
            foreach ($el in $dirtreefiles) {
                $dirtreematcher[$el.Comparison] = $el.Fullpath
            }

            foreach ($file in $matching) {
                if ($file -notin $valid) {
                    $fullpath = $dirtreematcher[$file]

                    $filename = Split-Path $fullpath -Leaf

                    if ($filename -in $systemfiles) { continue }

                    $result = [pscustomobject]@{
                        Server         = $server.name
                        ComputerName   = $server.ComputerName
                        InstanceName   = $server.ServiceName
                        SqlInstance    = $server.DomainInstanceName
                        Filename       = $fullpath
                        RemoteFilename = Join-AdminUnc -Servername $server.ComputerName -Filepath $fullpath
                    }

                    if ($LocalOnly -eq $true) {
                        ($result | Select-Object filename).filename
                        continue
                    }

                    if ($RemoteOnly -eq $true) {
                        ($result | Select-Object remotefilename).remotefilename
                        continue
                    }

                    $result | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, Filename, RemoteFilename

                }
            }

        }
    }
    end {
        if ($result.count -eq 0) {
            Write-Message -Level Verbose -Message "No orphaned files found"
        }
    }
}
tools\dbatools\functions\Find-DbaSimilarTable.ps1
function Find-DbaSimilarTable {
    <#
        .SYNOPSIS
            Returns all tables/views that are similar in structure by comparing the column names of matching and matched tables/views

        .DESCRIPTION
            This function can either run against specific databases or all databases searching all/specific tables and views including in system databases.
            Typically one would use this to find for example archive version(s) of a table whose structures are similar.
            This can also be used to find tables/views that are very similar to a given table/view structure to see where a table/view might be used.

            More information can be found here: https://sqljana.wordpress.com/2017/03/31/sql-server-find-tables-with-similar-table-structure/

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto-populated from the server

        .PARAMETER SchemaName
            If you are looking in a specific schema whose table structures is to be used as reference structure, provide the name of the schema.
            If no schema is provided, looks at all schemas

        .PARAMETER TableName
            If you are looking in a specific table whose structure is to be used as reference structure, provide the name of the table.
            If no table is provided, looks at all tables
            If the table name exists in multiple schemas, all of them would qualify

        .PARAMETER ExcludeViews
            By default, views are included. You can exclude them by setting this switch to $false
            This excludes views in both matching and matched list

        .PARAMETER IncludeSystemDatabases
            By default system databases are ignored but you can include them within the search using this parameter

        .PARAMETER MatchPercentThreshold
            The minimum percentage of column names that should match between the matching and matched objects.
            Entries with no matches are eliminated

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Table
            Author: Jana Sattainathan (@SQLJana - http://sqljana.wordpress.com)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Find-DbaSimilarTable

        .EXAMPLE
            Find-DbaSimilarTable -SqlInstance DEV01

            Searches all user database tables and views for each, returns all tables or views with their matching tables/views and match percent

        .EXAMPLE
            Find-DbaSimilarTable -SqlInstance DEV01 -Database AdventureWorks

            Searches AdventureWorks database and lists tables/views and their corresponding matching tables/views with match percent

        .EXAMPLE
            Find-DbaSimilarTable -SqlInstance DEV01 -Database AdventureWorks -SchemaName HumanResource

            Searches AdventureWorks database and lists tables/views in the HumanResource schema with their corresponding matching tables/views with match percent

        .EXAMPLE
            Find-DbaSimilarTable -SqlInstance DEV01 -Database AdventureWorks -SchemaName HumanResource -Table Employee

            Searches AdventureWorks database and lists tables/views in the HumanResource schema and table Employee with its corresponding matching tables/views with match percent

        .EXAMPLE
            Find-DbaSimilarTable -SqlInstance DEV01 -Database AdventureWorks -MatchPercentThreshold 60

            Searches AdventureWorks database and lists all tables/views with its corresponding matching tables/views with match percent greater than or equal to 60
    #>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [string]$SchemaName,
        [string]$TableName,
        [switch]$ExcludeViews,
        [switch]$IncludeSystemDatabases,
        [int]$MatchPercentThreshold,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $everyServerVwCount = 0

        $sqlSelect = "WITH ColCountsByTable
                AS
                (
                      SELECT
                            c.TABLE_CATALOG,
                            c.TABLE_SCHEMA,
                            c.TABLE_NAME,
                            COUNT(1) AS Column_Count
                      FROM INFORMATION_SCHEMA.COLUMNS c
                      GROUP BY
                            c.TABLE_CATALOG,
                            c.TABLE_SCHEMA,
                            c.TABLE_NAME
                )
                SELECT
                      100 * COUNT(c2.COLUMN_NAME) /*Matching_Column_Count*/ / MIN(ColCountsByTable.Column_Count) /*Column_Count*/ AS MatchPercent,
                      DENSE_RANK() OVER(ORDER BY c.TABLE_CATALOG, c.TABLE_SCHEMA, c.TABLE_NAME) TableNameRankInDB,
                      c.TABLE_CATALOG AS DatabaseName,
                      c.TABLE_SCHEMA AS SchemaName,
                      c.TABLE_NAME AS TableName,
                      t.TABLE_TYPE AS TableType,
                      MIN(ColCountsByTable.Column_Count) AS ColumnCount,
                      c2.TABLE_CATALOG AS MatchingDatabaseName,
                      c2.TABLE_SCHEMA AS MatchingSchemaName,
                      c2.TABLE_NAME AS MatchingTableName,
                      t2.TABLE_TYPE AS MatchingTableType,
                      COUNT(c2.COLUMN_NAME) AS MatchingColumnCount
                FROM INFORMATION_SCHEMA.TABLES t
                      INNER JOIN INFORMATION_SCHEMA.COLUMNS c
                            ON t.TABLE_CATALOG = c.TABLE_CATALOG
                                  AND t.TABLE_SCHEMA = c.TABLE_SCHEMA
                                  AND t.TABLE_NAME = c.TABLE_NAME
                      INNER JOIN ColCountsByTable
                            ON t.TABLE_CATALOG = ColCountsByTable.TABLE_CATALOG
                                  AND t.TABLE_SCHEMA = ColCountsByTable.TABLE_SCHEMA
                                  AND t.TABLE_NAME = ColCountsByTable.TABLE_NAME
                      LEFT OUTER JOIN INFORMATION_SCHEMA.COLUMNS c2
                            ON t.TABLE_NAME != c2.TABLE_NAME
                                  AND c.COLUMN_NAME = c2.COLUMN_NAME
                      LEFT JOIN INFORMATION_SCHEMA.TABLES t2
                            ON c2.TABLE_NAME = t2.TABLE_NAME"

        $sqlWhere = "
                WHERE "

        $sqlGroupBy = "
                GROUP BY
                      c.TABLE_CATALOG,
                      c.TABLE_SCHEMA,
                      c.TABLE_NAME,
                      t.TABLE_TYPE,
                      c2.TABLE_CATALOG,
                      c2.TABLE_SCHEMA,
                      c2.TABLE_NAME,
                      t2.TABLE_TYPE "

        $sqlHaving = "
                HAVING
                    /*Match_Percent should be greater than 0 at minimum!*/
                    "

        $sqlOrderBy = "
                ORDER BY
                      MatchPercent DESC"


        $sql = ''
        $wherearray = @()

        if ($ExcludeViews) {
            $wherearray += " (t.TABLE_TYPE <> 'VIEW' AND t2.TABLE_TYPE <> 'VIEW') "
        }

        if ($SchemaName) {
            $wherearray += (" (c.TABLE_SCHEMA = '{0}') " -f $SchemaName.Replace("'", "''")) #Replace single quotes with two single quotes!
        }

        if ($TableName) {
            $wherearray += (" (c.TABLE_NAME = '{0}') " -f $TableName.Replace("'", "''")) #Replace single quotes with two single quotes!

        }

        if ($wherearray.length -gt 0) {
            $sqlWhere = "$sqlWhere " + ($wherearray -join " AND ")
        }
        else {
            $sqlWhere = ""
        }


        $matchThreshold = 0
        if ($MatchPercentThreshold) {
            $matchThreshold = $MatchPercentThreshold
        }
        else {
            $matchThreshold = 0
        }

        $sqlHaving += (" (100 * COUNT(c2.COLUMN_NAME) / MIN(ColCountsByTable.Column_Count) >= {0}) " -f $matchThreshold)



        $sql = "$sqlSelect $sqlWhere $sqlGroupBy $sqlHaving $sqlOrderBy"

        Write-Message -Level Debug -Message $sql

    }

    process {
        foreach ($Instance in $SqlInstance) {

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }


            #Use IsAccessible instead of Status -eq 'normal' because databases that are on readable secondaries for AG or mirroring replicas will cause errors to be thrown
            if ($IncludeSystemDatabases) {
                $dbs = $server.Databases | Where-Object { $_.IsAccessible -eq $true }
            }
            else {
                $dbs = $server.Databases | Where-Object { $_.IsAccessible -eq $true -and $_.IsSystemObject -eq $false }
            }

            if ($Database) {
                $dbs = $server.Databases | Where-Object Name -In $Database
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }


            $totalCount = 0
            $dbCount = $dbs.count
            foreach ($db in $dbs) {

                Write-Message -Level Verbose -Message "Searching on database $db"
                $rows = $db.Query($sql)

                foreach ($row in $rows) {
                    [PSCustomObject]@{
                        ComputerName              = $server.ComputerName
                        InstanceName              = $server.ServiceName
                        SqlInstance               = $server.DomainInstanceName
                        Table                     = "$($row.DatabaseName).$($row.SchemaName).$($row.TableName)"
                        MatchingTable             = "$($row.MatchingDatabaseName).$($row.MatchingSchemaName).$($row.MatchingTableName)"
                        MatchPercent              = $row.MatchPercent
                        OriginalDatabaseName      = $row.DatabaseName
                        OriginalSchemaName        = $row.SchemaName
                        OriginalTableName         = $row.TableName
                        OriginalTableNameRankInDB = $row.TableNameRankInDB
                        OriginalTableType         = $row.TableType
                        OriginalColumnCount       = $row.ColumnCount
                        MatchingDatabaseName      = $row.MatchingDatabaseName
                        MatchingSchemaName        = $row.MatchingSchemaName
                        MatchingTableName         = $row.MatchingTableName
                        MatchingTableType         = $row.MatchingTableType
                        MatchingColumnCount       = $row.MatchingColumnCount
                    }
                }

                $vwCount = $vwCount + $rows.Count
                $totalCount = $totalCount + $rows.Count
                $everyServerVwCount = $everyServerVwCount + $rows.Count

                Write-Message -Level Verbose -Message "Found $vwCount tables/views in $db"
            }

            Write-Message -Level Verbose -Message "Found $totalCount total tables/views in $dbCount databases"
        }
    }
    end {
        Write-Message -Level Verbose -Message "Found $everyServerVwCount total tables/views"
    }
}
tools\dbatools\functions\Find-DbaStoredProcedure.ps1
function Find-DbaStoredProcedure {
    <#
        .SYNOPSIS
            Returns all stored procedures that contain a specific case-insensitive string or regex pattern.

        .DESCRIPTION
            This function can either run against specific databases or all databases searching all user or user and system stored procedures.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto-populated from the server

        .PARAMETER Pattern
            String pattern that you want to search for in the stored procedure textbody

        .PARAMETER IncludeSystemObjects
            By default, system stored procedures are ignored but you can include them within the search using this parameter.

            Warning - this will likely make it super slow if you run it on all databases.

        .PARAMETER IncludeSystemDatabases
            By default system databases are ignored but you can include them within the search using this parameter

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: StoredProcedure, Proc
            Author: Stephen Bennett, https://sqlnotesfromtheunderground.wordpress.com/

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Find-DbaStoredProcedure

        .EXAMPLE
            Find-DbaStoredProcedure -SqlInstance DEV01 -Pattern whatever

            Searches all user databases stored procedures for "whatever" in the textbody

        .EXAMPLE
            Find-DbaStoredProcedure -SqlInstance sql2016 -Pattern '\w+@\w+\.\w+'

            Searches all databases for all stored procedures that contain a valid email pattern in the textbody

        .EXAMPLE
            Find-DbaStoredProcedure -SqlInstance DEV01 -Database MyDB -Pattern 'some string' -Verbose

            Searches in "mydb" database stored procedures for "some string" in the textbody

        .EXAMPLE
            Find-DbaStoredProcedure -SqlInstance sql2016 -Database MyDB -Pattern RUNTIME -IncludeSystemObjects

            Searches in "mydb" database stored procedures for "runtime" in the textbody
    #>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [parameter(Mandatory = $true)]
        [string]$Pattern,
        [switch]$IncludeSystemObjects,
        [switch]$IncludeSystemDatabases,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $sql = "SELECT OBJECT_SCHEMA_NAME(p.object_id) as ProcSchema, p.name, m.definition as TextBody FROM sys.sql_modules m, sys.procedures p WHERE m.object_id = p.object_id"
        if (!$IncludeSystemObjects) { $sql = "$sql AND p.is_ms_shipped = 0" }
        $everyserverspcount = 0
    }
    process {
        foreach ($Instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $Instance"
                $server = Connect-SqlInstance -SqlInstance $Instance -SqlCredential $SqlCredential
            }
            catch {
                Write-Message -Level Warning -Message "Failed to connect to: $Instance"
                continue
            }

            if ($server.versionMajor -lt 9) {
                Write-Message -Level Warning -Message "This command only supports SQL Server 2005 and above."
                Continue
            }

            if ($IncludeSystemDatabases) {
                $dbs = $server.Databases | Where-Object { $_.Status -eq "normal" }
            }
            else {
                $dbs = $server.Databases | Where-Object { $_.Status -eq "normal" -and $_.IsSystemObject -eq $false }
            }

            if ($Database) {
                $dbs = $dbs | Where-Object Name -In $Database
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            $totalcount = 0
            $dbcount = $dbs.count
            foreach ($db in $dbs) {
                Write-Message -Level Verbose -Message "Searching on database $db"

                # If system objects aren't needed, find stored procedure text using SQL
                # This prevents SMO from having to enumerate

                if (!$IncludeSystemObjects) {
                    Write-Message -Level Debug -Message $sql
                    $rows = $db.ExecuteWithResults($sql).Tables.Rows
                    $sproccount = 0

                    foreach ($row in $rows) {
                        $totalcount++; $sproccount++; $everyserverspcount++

                        $procSchema = $row.ProcSchema
                        $proc = $row.Name

                        Write-Message -Level Verbose -Message "Looking in stored procedure: $procSchema.$proc textBody for $pattern"
                        if ($row.TextBody -match $Pattern) {
                            $sp = $db.StoredProcedures | Where-Object {$_.Schema -eq $procSchema -and $_.Name -eq $proc}

                            $StoredProcedureText = $sp.TextBody.split("`n")
                            $spTextFound = $StoredProcedureText | Select-String -Pattern $Pattern | ForEach-Object { "(LineNumber: $($_.LineNumber)) $($_.ToString().Trim())" }

                            [PSCustomObject]@{
                                ComputerName             = $server.ComputerName
                                SqlInstance              = $server.ServiceName
                                Database                 = $db.Name
                                Schema                   = $sp.Schema
                                Name                     = $sp.Name
                                Owner                    = $sp.Owner
                                IsSystemObject           = $sp.IsSystemObject
                                CreateDate               = $sp.CreateDate
                                LastModified             = $sp.DateLastModified
                                StoredProcedureTextFound = $spTextFound -join "`n"
                                StoredProcedure          = $sp
                                StoredProcedureFullText  = $sp.TextBody
                            } | Select-DefaultView -ExcludeProperty StoredProcedure, StoredProcedureFullText
                        }
                    }
                }
                else {
                    $storedprocedures = $db.StoredProcedures

                    foreach ($sp in $storedprocedures) {
                        $totalcount++; $sproccount++; $everyserverspcount++

                        $procSchema = $sp.Schema
                        $proc = $sp.Name

                        Write-Message -Level Verbose -Message "Looking in stored procedure $procSchema.$proc textBody for $pattern"
                        if ($sp.TextBody -match $Pattern) {

                            $StoredProcedureText = $sp.TextBody.split("`n")
                            $spTextFound = $StoredProcedureText | Select-String -Pattern $Pattern | ForEach-Object { "(LineNumber: $($_.LineNumber)) $($_.ToString().Trim())" }

                            [PSCustomObject]@{
                                ComputerName             = $server.ComputerName
                                SqlInstance              = $server.ServiceName
                                Database                 = $db.Name
                                Schema                   = $sp.Schema
                                Name                     = $sp.Name
                                Owner                    = $sp.Owner
                                IsSystemObject           = $sp.IsSystemObject
                                CreateDate               = $sp.CreateDate
                                LastModified             = $sp.DateLastModified
                                StoredProcedureTextFound = $spTextFound -join "`n"
                                StoredProcedure          = $sp
                                StoredProcedureFullText  = $sp.TextBody
                            } | Select-DefaultView -ExcludeProperty StoredProcedure, StoredProcedureFullText
                        }
                    }
                }
                Write-Message -Level Verbose -Message "Evaluated $sproccount stored procedures in $db"
            }
            Write-Message -Level Verbose -Message "Evaluated $totalcount total stored procedures in $dbcount databases"
        }
    }
    end {
        Write-Message -Level Verbose -Message "Evaluated $everyserverspcount total stored procedures"
    }
}
tools\dbatools\functions\Find-DbaTrigger.ps1
function Find-DbaTrigger {
    <#
        .SYNOPSIS
            Returns all triggers that contain a specific case-insensitive string or regex pattern.

        .DESCRIPTION
            This function search on Instance, Database and Object level.
            If you specify one or more databases, search on Server level will not be preformed.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto-populated from the server

        .PARAMETER Pattern
            String pattern that you want to search for in the trigger textbody

        .PARAMETER TriggerLevel
            Allows specify the trigger level that you want to search. By default is All (Server, Database, Object).

        .PARAMETER IncludeSystemObjects
            By default, system triggers are ignored but you can include them within the search using this parameter.

            Warning - this will likely make it super slow if you run it on all databases.

        .PARAMETER IncludeSystemDatabases
            By default system databases are ignored but you can include them within the search using this parameter

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Trigger
            Author: Cláudio Silva, @ClaudioESSilva

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Find-DbaTrigger

        .EXAMPLE
            Find-DbaTrigger -SqlInstance DEV01 -Pattern whatever

            Searches all user databases triggers for "whatever" in the textbody

        .EXAMPLE
            Find-DbaTrigger -SqlInstance sql2016 -Pattern '\w+@\w+\.\w+'

            Searches all databases for all triggers that contain a valid email pattern in the textbody

        .EXAMPLE
            Find-DbaTrigger -SqlInstance DEV01 -Database MyDB -Pattern 'some string' -Verbose

            Searches in "mydb" database triggers for "some string" in the textbody

        .EXAMPLE
            Find-DbaTrigger -SqlInstance sql2016 -Database MyDB -Pattern RUNTIME -IncludeSystemObjects

            Searches in "mydb" database triggers for "runtime" in the textbody
    #>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [parameter(Mandatory = $true)]
        [string]$Pattern,
        [ValidateSet('All', 'Server', 'Database', 'Object')]
        [string]$TriggerLevel = 'All',
        [switch]$IncludeSystemObjects,
        [switch]$IncludeSystemDatabases,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $sqlDatabaseTriggers = "SELECT tr.name, m.definition as TextBody FROM sys.sql_modules m, sys.triggers tr WHERE m.object_id = tr.object_id AND tr.parent_class = 0"

        $sqlTableTriggers = "SELECT OBJECT_SCHEMA_NAME(tr.parent_id) TableSchema, OBJECT_NAME(tr.parent_id) AS TableName, tr.name, m.definition as TextBody FROM sys.sql_modules m, sys.triggers tr WHERE m.object_id = tr.object_id AND tr.parent_class = 1"
        if (!$IncludeSystemObjects) { $sqlTableTriggers = "$sqlTableTriggers AND tr.is_ms_shipped = 0" }

        $everyserverstcount = 0
    }
    process {
        foreach ($Instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $Instance"
                $server = Connect-SqlInstance -SqlInstance $Instance -SqlCredential $SqlCredential
            }
            catch {
                Write-Message -Level Warning -Message "Failed to connect to: $Instance"
                continue
            }

            if ($server.versionMajor -lt 9) {
                Write-Message -Level Warning -Message "This command only supports SQL Server 2005 and above."
                Continue
            }

            #search at instance level. Only if no database was specified
            if ((-Not $Database) -and ($TriggerLevel -in @('All', 'Server'))) {
                foreach ($trigger in $server.Triggers) {
                    $everyserverstcount++; $triggercount++
                    Write-Message -Level Debug -Message "Looking in Trigger: $trigger TextBody for $pattern"
                    if ($trigger.TextBody -match $Pattern) {

                        $triggerText = $trigger.TextBody.split("`n`r")
                        $trTextFound = $triggerText | Select-String -Pattern $Pattern | ForEach-Object { "(LineNumber: $($_.LineNumber)) $($_.ToString().Trim())" }

                        [PSCustomObject]@{
                            ComputerName     = $server.ComputerName
                            SqlInstance      = $server.ServiceName
                            TriggerLevel     = "Server"
                            Database         = $null
                            Object           = $null
                            Name             = $trigger.Name
                            IsSystemObject   = $trigger.IsSystemObject
                            CreateDate       = $trigger.CreateDate
                            LastModified     = $trigger.DateLastModified
                            TriggerTextFound = $trTextFound -join "`n"
                            Trigger          = $trigger
                            TriggerFullText  = $trigger.TextBody
                        } | Select-DefaultView -ExcludeProperty Trigger, TriggerFullText
                    }
                }
                Write-Message -Level Verbose -Message "Evaluated $triggercount triggers in $server"
            }

            if ($IncludeSystemDatabases) {
                $dbs = $server.Databases | Where-Object { $_.Status -eq "normal" }
            }
            else {
                $dbs = $server.Databases | Where-Object { $_.Status -eq "normal" -and $_.IsSystemObject -eq $false }
            }

            if ($Database) {
                $dbs = $dbs | Where-Object Name -In $Database
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            $totalcount = 0
            $dbcount = $dbs.count

            if ($TriggerLevel -in @('All', 'Database', 'Object')) {
                foreach ($db in $dbs) {

                    Write-Message -Level Verbose -Message "Searching on database $db"

                    # If system objects aren't needed, find trigger text using SQL
                    # This prevents SMO from having to enumerate

                    if (!$IncludeSystemObjects) {
                        if ($TriggerLevel -in @('All', 'Database')) {
                            #Get Database Level triggers (DDL)
                            Write-Message -Level Debug -Message $sqlDatabaseTriggers
                            $rows = $db.ExecuteWithResults($sqlDatabaseTriggers).Tables.Rows
                            $triggercount = 0

                            foreach ($row in $rows) {
                                $totalcount++; $triggercount++; $everyserverstcount++

                                $trigger = $row.name

                                Write-Message -Level Verbose -Message "Looking in trigger $trigger for textBody with pattern $pattern on database $db"
                                if ($row.TextBody -match $Pattern) {
                                    $tr = $db.Triggers | Where-Object name -eq $row.name

                                    $triggerText = $tr.TextBody.split("`n`r")
                                    $trTextFound = $triggerText | Select-String -Pattern $Pattern | ForEach-Object { "(LineNumber: $($_.LineNumber)) $($_.ToString().Trim())" }

                                    [PSCustomObject]@{
                                        ComputerName     = $server.ComputerName
                                        SqlInstance      = $server.ServiceName
                                        TriggerLevel     = "Database"
                                        Database         = $db.name
                                        Object           = $tr.Parent
                                        Name             = $tr.Name
                                        IsSystemObject   = $tr.IsSystemObject
                                        CreateDate       = $tr.CreateDate
                                        LastModified     = $tr.DateLastModified
                                        TriggerTextFound = $trTextFound -join "`n"
                                        Trigger          = $tr
                                        TriggerFullText  = $tr.TextBody
                                    } | Select-DefaultView -ExcludeProperty Trigger, TriggerFullText
                                }
                            }
                        }

                        if ($TriggerLevel -in @('All', 'Object')) {
                            #Get Object Level triggers (DML)
                            Write-Message -Level Debug -Message $sqlTableTriggers
                            $rows = $db.ExecuteWithResults($sqlTableTriggers).Tables.Rows
                            $triggercount = 0

                            foreach ($row in $rows) {
                                $totalcount++; $triggercount++; $everyserverstcount++

                                $trigger = $row.name
                                $triggerParentSchema = $row.TableSchema
                                $triggerParent = $row.TableName

                                Write-Message -Level Verbose -Message "Looking in trigger $trigger for textBody with pattern $pattern in object $triggerParentSchema.$triggerParent at database $db"
                                if ($row.TextBody -match $Pattern) {

                                    $tr = ($db.Tables | Where-Object {$_.Name -eq $triggerParent -and $_.Schema -eq $triggerParentSchema}).Triggers | Where-Object name -eq $row.name

                                    $triggerText = $tr.TextBody.split("`n`r")
                                    $trTextFound = $triggerText | Select-String -Pattern $Pattern | ForEach-Object { "(LineNumber: $($_.LineNumber)) $($_.ToString().Trim())" }

                                    [PSCustomObject]@{
                                        ComputerName     = $server.ComputerName
                                        SqlInstance      = $server.ServiceName
                                        TriggerLevel     = "Object"
                                        Database         = $db.name
                                        Object           = $tr.Parent
                                        Name             = $tr.Name
                                        IsSystemObject   = $tr.IsSystemObject
                                        CreateDate       = $tr.CreateDate
                                        LastModified     = $tr.DateLastModified
                                        TriggerTextFound = $trTextFound -join "`n"
                                        Trigger          = $tr
                                        TriggerFullText  = $tr.TextBody
                                    } | Select-DefaultView -ExcludeProperty Trigger, TriggerFullText
                                }
                            }
                        }
                    }
                    else {
                        if ($TriggerLevel -in @('All', 'Database')) {
                            #Get Database Level triggers (DDL)
                            $triggers = $db.Triggers

                            $triggercount = 0

                            foreach ($tr in $triggers) {
                                $totalcount++; $triggercount++; $everyserverstcount++
                                $trigger = $tr.Name

                                Write-Message -Level Verbose -Message "Looking in trigger $trigger for textBody with pattern $pattern on database $db"
                                if ($tr.TextBody -match $Pattern) {

                                    $triggerText = $tr.TextBody.split("`n`r")
                                    $trTextFound = $triggerText | Select-String -Pattern $Pattern | ForEach-Object { "(LineNumber: $($_.LineNumber)) $($_.ToString().Trim())" }

                                    [PSCustomObject]@{
                                        ComputerName     = $server.ComputerName
                                        SqlInstance      = $server.ServiceName
                                        TriggerLevel     = "Database"
                                        Database         = $db.name
                                        Object           = $tr.Parent
                                        Name             = $tr.Name
                                        IsSystemObject   = $tr.IsSystemObject
                                        CreateDate       = $tr.CreateDate
                                        LastModified     = $tr.DateLastModified
                                        TriggerTextFound = $trTextFound -join "`n"
                                        Trigger          = $tr
                                        TriggerFullText  = $tr.TextBody
                                    } | Select-DefaultView -ExcludeProperty Trigger, TriggerFullText
                                }
                            }
                        }

                        if ($TriggerLevel -in @('All', 'Object')) {
                            #Get Object Level triggers (DML)
                            $triggers = $db.Tables | ForEach-Object {$_.Triggers}

                            $triggercount = 0

                            foreach ($tr in $triggers) {
                                $totalcount++; $triggercount++; $everyserverstcount++
                                $trigger = $tr.Name

                                Write-Message -Level Verbose -Message "Looking in trigger $trigger for textBody with pattern $pattern in object $($tr.Parent) at database $db"
                                if ($tr.TextBody -match $Pattern) {

                                    $triggerText = $tr.TextBody.split("`n`r")
                                    $trTextFound = $triggerText | Select-String -Pattern $Pattern | ForEach-Object { "(LineNumber: $($_.LineNumber)) $($_.ToString().Trim())" }

                                    [PSCustomObject]@{
                                        ComputerName     = $server.ComputerName
                                        SqlInstance      = $server.ServiceName
                                        TriggerLevel     = "Object"
                                        Database         = $db.name
                                        Object           = $tr.Parent
                                        Name             = $tr.Name
                                        IsSystemObject   = $tr.IsSystemObject
                                        CreateDate       = $tr.CreateDate
                                        LastModified     = $tr.DateLastModified
                                        TriggerTextFound = $trTextFound -join "`n"
                                        Trigger          = $tr
                                        TriggerFullText  = $tr.TextBody
                                    } | Select-DefaultView -ExcludeProperty Trigger, TriggerFullText
                                }
                            }
                        }
                    }
                    Write-Message -Level Verbose -Message "Evaluated $triggercount triggers in $db"
                }
            }
            Write-Message -Level Verbose -Message "Evaluated $totalcount total triggers in $dbcount databases"
        }
    }
    end {
        Write-Message -Level Verbose -Message "Evaluated $everyserverstcount total triggers"
    }
}
tools\dbatools\functions\Find-DbaUnusedIndex.ps1
function Find-DbaUnusedIndex {
    <#
        .SYNOPSIS
            Find Unused indexes

        .DESCRIPTION
            This command will help you to find Unused indexes on a database or a list of databases

            Also tells how much space you can save by dropping the index.
            We show the type of compression so you can make a more considered decision.
            For now only supported for CLUSTERED and NONCLUSTERED indexes

            You can select the indexes you want to drop on the gridview and by clicking OK the drop statement will be generated.

        .PARAMETER SqlInstance
            The SQL Server you want to check for unused indexes.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies the database(s) to exclude from processing. Options for this list are auto-populated from the server.

        .PARAMETER FilePath
            Specifies the path of a file to write the DROP statements to.

        .PARAMETER NoClobber
            If this switch is enabled, the output file will not be overwritten.

        .PARAMETER Append
            If this switch is enabled, content will be appended to the output file.

        .PARAMETER IgnoreUptime
            Less than 7 days uptime can mean that analysis of unused indexes is unreliable, and normally no results will be returned. By setting this option results will be returned even if the Instance has been running for less that 7 days.

            .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Index
            Author: Aaron Nelson (@SQLvariant), SQLvariant.com

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Find-DbaUnusedIndex

        .EXAMPLE
            Find-DbaUnusedIndex -SqlInstance sql2005 -FilePath C:\temp\sql2005-UnusedIndexes.sql

            Generates the SQL statements to drop the selected unused indexes on server "sql2005". The statements are written to the file "C:\temp\sql2005-UnusedIndexes.sql"

        .EXAMPLE
            Find-DbaUnusedIndex -SqlInstance sql2005 -FilePath C:\temp\sql2005-UnusedIndexes.sql -Append

            Generates the SQL statements to drop the selected unused indexes on server "sql2005". The statements are written to the file "C:\temp\sql2005-UnusedIndexes.sql", appending if the file already exists.

        .EXAMPLE
            Find-DbaUnusedIndex -SqlInstance sqlserver2016 -SqlCredential $cred

            Generates the SQL statements to drop the selected unused indexes on server "sqlserver2016", using SQL Authentication to connect to the database.

        .EXAMPLE
            Find-DbaUnusedIndex -SqlInstance sqlserver2016 -Database db1, db2

            Generates the SQL Statement to to drop selected indexes in databases db1 & db2 on server "sqlserver2016".

        .EXAMPLE
            Find-DbaUnusedIndex -SqlInstance sqlserver2016

            Generates the SQL statements to drop selected indexes on all user databases.

        .EXAMPLE
            Fine-DbaUnusedIndex -SqlInstance sqlserver2016 -IgnoreUptime

            Generates the SQL statements to drop selected indexes on all user databases even if the instance has been online for less than 7 days.
            Note that results may not have enough detail for all indexes, so care should be taken when using them or the generated scripts. Best practice is to allow a full week to capture the mmajority of index use cases

    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Alias("OutFile", "Path")]
        [string]$FilePath,
        [switch]$NoClobber,
        [switch]$Append,
        [switch]$IgnoreUptime,
        [switch][Alias('Silent')]
        $EnableException
    )

    begin {

        # Support Compression 2008+
        $unusedQuery = "
        SELECT DB_NAME(database_id) AS 'DatabaseName'
        ,s.name AS 'SchemaName'
        ,t.name AS 'TableName'
        ,i.object_id AS ObjectId
        ,i.name AS 'IndexName'
        ,i.index_id as 'IndexId'
        ,i.type_desc as 'TypeDesc'
        ,user_seeks as 'UserSeeks'
        ,user_scans as 'UserScans'
        ,user_lookups  as 'UserLookups'
        ,user_updates  as 'UserUpdates'
        ,last_user_seek  as 'LastUserSeek'
        ,last_user_scan  as 'LastUserScan'
        ,last_user_lookup  as 'LastUserLookup'
        ,last_user_UPDATE  as 'LastUserUpdate'
        ,system_seeks  as 'SystemSeeks'
        ,system_scans  as 'SystemScans'
        ,system_lookups  as 'SystemLookup'
        ,system_updates  as 'SystemUpdates'
        ,last_system_seek  as 'LastSystemSeek'
        ,last_system_scan  as 'LastSystemScan'
        ,last_system_lookup  as 'LastSystemLookup'
        ,last_system_update as 'LastSystemUpdate'
        FROM SYS.TABLES T
        JOIN SYS.SCHEMAS S
            ON T.schema_id = s.schema_id
        JOIN SYS.indexes i
            ON i.object_id = t.object_id LEFT OUTER
        JOIN sys.dm_db_index_usage_stats iu
            ON iu.object_id = i.object_id
                AND iu.index_id = i.index_id
        WHERE iu.database_id = DB_ID()
                AND OBJECTPROPERTY(i.[object_id], 'IsMSShipped') = 0
                AND user_seeks = 0
                AND user_scans = 0
                AND user_lookups = 0
                AND i.type_desc NOT IN ('HEAP', 'CLUSTERED COLUMNSTORE')"

        if ($FilePath.Length -gt 0) {
            if ($FilePath -notlike "*\*") {
                $FilePath = ".\$FilePath"
            }
            $directory = Split-Path $FilePath
            $exists = Test-Path $directory

            if ($exists -eq $false) {
                Stop-Function -Message "Parent directory $directory does not exist."
                return
            }
        }

        Write-Message -Level Output -Message "Connecting to SQL Server."
        $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
    }
    process {
        if (Test-FunctionInterrupt) { return }

        if ($server.VersionMajor -lt 9) {
            Stop-Function -Message "This function does not support versions lower than SQL Server 2005 (v9)."
            return
        }

        $lastRestart = $server.Databases['tempdb'].CreateDate
        $endDate = Get-Date -Date $lastRestart
        $diffDays = (New-TimeSpan -Start $endDate -End (Get-Date)).Days

        if ($diffDays -le 6) {
            if ($IgnoreUptime -ne $true) {
                Stop-Function -Message "The SQL Service was restarted on $lastRestart, which is not long enough for a solid evaluation."
                return
            }
            else {
                Write-Message -Level Warning -Message "The SQL Service was restarted on $lastRestart, which is not long enough for a solid evaluation."
            }
        }

        <#
            Validate if server version is:
                - sql 2012 and if have SP3 CU3 (Build 6537) or higher
                - sql 2014 and if have SP2 (Build 5000) or higher
            If the major version is the same but the build is lower, throws the message
        #>
        if (
            ($server.VersionMajor -eq 11 -and $server.BuildNumber -lt 6537) `
            -or ($server.VersionMajor -eq 12 -and $server.BuildNumber -lt 5000)
        ) {
            Stop-Function -Message "This SQL version has a known issue. Rebuilding an index clears any existing row entry from sys.dm_db_index_usage_stats for that index.`r`nPlease refer to connect item: https://support.microsoft.com/en-us/help/3160407/fix-sys-dm-db-index-usage-stats-missing-information-after-index-rebuil"
            return
        }

        if ($diffDays -le 33) {
            Write-Message -Level Warning -Message "The SQL Service was restarted on $lastRestart, which may not be long enough for a solid evaluation."
        }

        if ($pipedatabase.Length -gt 0) {
            $database = $pipedatabase.name
        }

        if ($database.Count -eq 0) {
            $database = ($server.Databases | Where-Object { $_.IsSystemObject -eq 0 -and $_.IsAccessible }).Name
        }

        if ($database.Count -gt 0) {
            foreach ($db in $database) {
                if ($ExcludeDatabase -contains $db -or $null -eq $server.Databases[$db]) {
                    continue
                }
                if ($server.Databases[$db].IsAccessible -eq $false) {
                    Write-Message -Level Warning -Message "Database [$db] is not accessible."
                    continue
                }
                try {
                    Write-Message -Level Output -Message "Getting indexes from database '$db'."

                    $sql = $unusedQuery

                    $unusedIndex = $server.Databases[$db].ExecuteWithResults($sql)

                    $scriptGenerated = $false

                    if ($unusedIndex.Tables[0].Rows.Count -gt 0) {
                        $indexesToDrop = $unusedIndex.Tables[0]

                        if ($indexesToDrop.Count -gt 0 -or !([string]::IsNullOrEmpty($indexesToDrop))) {

                            foreach ($index in $indexesToDrop) {
                                if ($FilePath.Length -gt 0) {
                                    Write-Message -Level Output -Message "Exporting $($index.TableName).$($index.IndexName)"
                                    $sqlout += "USE [$($index.DatabaseName)]`r`n"
                                    $sqlout += "GO`r`n"
                                    $sqlout += "IF EXISTS (SELECT 1 FROM sys.indexes WHERE [object_id] = OBJECT_ID('$($index.SchemaName).$($index.TableName)') AND name = '$($index.IndexName)')`r`n"
                                    $sqlout += "DROP INDEX $($index.SchemaName).$($index.TableName).$($index.IndexName)`r`n"
                                    $sqlout += "GO`r`n`r`n"`

                                }
                            }

                            if ($FilePath.Length -gt 0) {
                                $sqlout | Out-File -FilePath $FilePath -Append:$Append -NoClobber:$NoClobber
                            }
                            else {
                                $indexesToDrop
                            }

                            $scriptGenerated = $true
                        }
                    }
                    else {
                        Write-Message -Level Output -Message "No Unused indexes found!"
                    }
                }
                catch {
                    Stop-Function -Message "Issue gathering indexes" -Category InvalidOperation -ErrorRecord $_ -Target $db
                }
            }

            if ($scriptGenerated) {
                Write-Message -Level Warning -Message "Confirm the generated script before execute!"
            }
            if ($FilePath.Length -gt 0) {
                Write-Message -Level Output -Message "Script generated to $FilePath"
            }
        }
        else {
            Write-Message -Level Output -Message "There are no databases to analyse."
        }
    }
    end {
        if (Test-FunctionInterrupt) {
            return
        }
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Get-SqlUnusedIndex
    }
}
tools\dbatools\functions\Find-DbaUserObject.ps1
#ValidationTags#Messaging#
function Find-DbaUserObject {
    <#
        .SYNOPSIS
            Searches SQL Server to find user-owned objects (ie. not dbo or sa) or for any object owned by a specific user specified by the Pattern parameter.

        .DESCRIPTION
            Looks at the below list of objects to see if they are either owned by a user or a specific user (using the parameter -Pattern)
                Database Owner
                Agent Job Owner
                Used in Credential
                USed in Proxy
                SQL Agent Steps using a Proxy
                Endpoints
                Server Roles
                Database Schemas
                Database Roles
                Database Assembles
                Database Synonyms

        .PARAMETER SqlInstance
            SqlInstance name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Pattern
            The regex pattern that the command will search for

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Object
            Author: Stephen Bennett, https://sqlnotesfromtheunderground.wordpress.com/
            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Find-DbaUserObject

        .EXAMPLE
            Find-DbaUserObject -SqlInstance DEV01 -Pattern ad\stephen

            Searches user objects for owner ad\stephen

        .EXAMPLE
            Find-DbaUserObject -SqlInstance DEV01 -Verbose

            Shows all user owned (non-sa, non-dbo) objects and verbose output
    #>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlInstances")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string]$Pattern,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        if ($Pattern -match '^[\w\d\.-]+\\[\w\d\.-]+$') {
            Write-Message -Level Verbose -Message "Too few slashes, adding extra as required by regex"
            $Pattern = $Pattern.Replace('\', '\\')
        }
    }
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $saname = Get-SaLoginName $server

            ## Credentials
            if (-not $pattern) {
                Write-Message -Level Verbose -Message "Gathering data on instance objects"
                $creds = $server.Credentials
                $proxies = $server.JobServer.ProxyAccounts
                $endPoints = $server.Endpoints | Where-Object { $_.Owner -ne $saname }

                Write-Message -Level Verbose -Message "Gather data on Agent Jobs ownership"
                $jobs = $server.JobServer.Jobs | Where-Object { $_.OwnerLoginName -ne $saname }
            }
            else {
                Write-Message -Level Verbose -Message "Gathering data on instance objects"
                $creds = $server.Credentials | Where-Object { $_.Identity -match $pattern }
                $proxies = $server.JobServer.ProxyAccounts | Where-Object { $_.CredentialIdentity -match $pattern }
                $endPoints = $server.Endpoints | Where-Object { $_.Owner -match $pattern }

                Write-Message -Level Verbose -Message "Gather data on Agent Jobs ownership"
                $jobs = $server.JobServer.Jobs | Where-Object { $_.OwnerLoginName -match $pattern }
            }

            ## dbs
            if (-not $pattern) {
                foreach ($db in $server.Databases | Where-Object { $_.Owner -ne $saname }) {
                    Write-Message -Level Verbose -Message "checking if $db is owned "

                    [PSCustomObject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Type         = "Database"
                        Owner        = $db.Owner
                        Name         = $db.Name
                        Parent       = $db.Parent.Name
                    }
                }
            }
            else {
                foreach ($db in $server.Databases | Where-Object { $_.Owner -match $pattern }) {
                    [PSCustomObject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Type         = "Database"
                        Owner        = $db.Owner
                        Name         = $db.Name
                        Parent       = $db.Parent.Name
                    }
                }
            }

            ## agent jobs
            if (-not $pattern) {
                foreach ($job in $server.JobServer.Jobs | Where-Object { $_.OwnerLoginName -ne $saname }) {
                    [PSCustomObject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Type         = "Agent Job"
                        Owner        = $job.OwnerLoginName
                        Name         = $job.Name
                        Parent       = $job.Parent.Name
                    }
                }
            }
            else {
                foreach ($job in $server.JobServer.Jobs | Where-Object { $_.OwnerLoginName -match $pattern }) {
                    [PSCustomObject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Type         = "Agent Job"
                        Owner        = $job.OwnerLoginName
                        Name         = $job.Name
                        Parent       = $job.Parent.Name
                    }
                }
            }

            ## credentials
            foreach ($cred in $creds) {
                ## list credentials using the account

                [PSCustomObject]@{
                    ComputerName = $server.ComputerName
                    InstanceName = $server.ServiceName
                    SqlInstance  = $server.DomainInstanceName
                    Type         = "Credential"
                    Owner        = $cred.Identity
                    Name         = $cred.Name
                    Parent       = $cred.Parent.Name
                }
            }

            ## proxies
            foreach ($proxy in $proxies) {
                [PSCustomObject]@{
                    ComputerName = $server.ComputerName
                    InstanceName = $server.ServiceName
                    SqlInstance  = $server.DomainInstanceName
                    Type         = "Proxy"
                    Owner        = $proxy.CredentialIdentity
                    Name         = $proxy.Name
                    Parent       = $proxy.Parent.Name
                }

                ## list agent jobs steps using proxy
                foreach ($job in $server.JobServer.Jobs) {
                    foreach ($step in $job.JobSteps | Where-Object { $_.ProxyName -eq $proxy.Name }) {
                        [PSCustomObject]@{
                            ComputerName = $server.ComputerName
                            InstanceName = $server.ServiceName
                            SqlInstance  = $server.DomainInstanceName
                            Type         = "Agent Step"
                            Owner        = $step.ProxyName
                            Name         = $step.Name
                            Parent       = $step.Parent.Name #$step.Name
                        }
                    }
                }
            }


            ## endpoints
            foreach ($endPoint in $endPoints) {
                [PSCustomObject]@{
                    ComputerName = $server.ComputerName
                    InstanceName = $server.ServiceName
                    SqlInstance  = $server.DomainInstanceName
                    Type         = "Endpoint"
                    Owner        = $endpoint.Owner
                    Name         = $endPoint.Name
                    Parent       = $endPoint.Parent.Name
                }
            }

            ## Server Roles
            if (-not $pattern) {
                foreach ($role in $server.Roles | Where-Object { $_.Owner -ne $saname }) {
                    Write-Message -Level Verbose -Message "checking if $db is owned "
                    [PSCustomObject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Type         = "Server Role"
                        Owner        = $role.Owner
                        Name         = $role.Name
                        Parent       = $role.Parent.Name
                    }
                }
            }
            else {
                foreach ($role in $server.Roles | Where-Object { $_.Owner -match $pattern }) {
                    [PSCustomObject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Type         = "Server Role"
                        Owner        = $role.Owner
                        Name         = $role.Name
                        Parent       = $role.Parent.Name
                    }
                }
            }

            ## Loop internal database
            foreach ($db in $server.Databases | Where-Object IsAccessible) {
                Write-Message -Level Verbose -Message "Gather user owned object in database: $db"
                ##schemas
                $sysSchemas = "DatabaseMailUserRole", "db_ssisadmin", "db_ssisltduser", "db_ssisoperator", "SQLAgentOperatorRole", "SQLAgentReaderRole", "SQLAgentUserRole", "TargetServersRole", "RSExecRole"

                if (-not $pattern) {
                    $schemas = $db.Schemas | Where-Object { $_.IsSystemObject -eq 0 -and $_.Owner -ne "dbo" -and $sysSchemas -notcontains $_.Owner }
                }
                else {
                    $schemas = $db.Schemas | Where-Object { $_.IsSystemObject -eq 0 -and $_.Owner -match $pattern -and $sysSchemas -notcontains $_.Owner }
                }
                foreach ($schema in $schemas) {
                    [PSCustomObject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Type         = "Schema"
                        Owner        = $schema.Owner
                        Name         = $schema.Name
                        Parent       = $schema.Parent.Name
                    }
                }

                ## database roles
                if (-not $pattern) {
                    $roles = $db.Roles | Where-Object { $_.IsSystemObject -eq 0 -and $_.Owner -ne "dbo" }
                }
                else {
                    $roles = $db.Roles | Where-Object { $_.IsSystemObject -eq 0 -and $_.Owner -match $pattern }
                }
                foreach ($role in $roles) {
                    [PSCustomObject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Type         = "Database Role"
                        Owner        = $role.Owner
                        Name         = $role.Name
                        Parent       = $role.Parent.Name
                    }
                }

                ## assembly
                if (-not $pattern) {
                    $assemblies = $db.Assemblies | Where-Object { $_.IsSystemObject -eq 0 -and $_.Owner -ne "dbo" }
                }
                else {
                    $assemblies = $db.Assemblies | Where-Object { $_.IsSystemObject -eq 0 -and $_.Owner -match $pattern }
                }

                foreach ($assembly in $assemblies) {
                    [PSCustomObject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Type         = "Database Assembly"
                        Owner        = $assembly.Owner
                        Name         = $assembly.Name
                        Parent       = $assembly.Parent.Name
                    }
                }

                ## synonyms
                if (-not $pattern) {
                    $synonyms = $db.Synonyms | Where-Object { $_.IsSystemObject -eq 0 -and $_.Owner -ne "dbo" }
                }
                else {
                    $synonyms = $db.Synonyms | Where-Object { $_.IsSystemObject -eq 0 -and $_.Owner -match $pattern }
                }

                foreach ($synonym in $synonyms) {
                    [PSCustomObject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Type         = "Database Synonyms"
                        Owner        = $synonym.Owner
                        Name         = $synonym.Name
                        Parent       = $synonym.Parent.Name
                    }
                }
            }
        }
    }
}
tools\dbatools\functions\Find-DbaView.ps1
function Find-DbaView {
    <#
        .SYNOPSIS
            Returns all views that contain a specific case-insensitive string or regex pattern.

        .DESCRIPTION
            This function can either run against specific databases or all databases searching all user or user and system views.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto-populated from the server

        .PARAMETER Pattern
            String pattern that you want to search for in the view textbody

        .PARAMETER IncludeSystemObjects
            By default, system views are ignored but you can include them within the search using this parameter.

            Warning - this will likely make it super slow if you run it on all databases.

        .PARAMETER IncludeSystemDatabases
            By default system databases are ignored but you can include them within the search using this parameter

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: View
            Author: Cláudio Silva (@ClaudioESSilva)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Find-DbaView

        .EXAMPLE
            Find-DbaView -SqlInstance DEV01 -Pattern whatever

            Searches all user databases views for "whatever" in the textbody

        .EXAMPLE
            Find-DbaView -SqlInstance sql2016 -Pattern '\w+@\w+\.\w+'

            Searches all databases for all views that contain a valid email pattern in the textbody

        .EXAMPLE
            Find-DbaView -SqlInstance DEV01 -Database MyDB -Pattern 'some string' -Verbose

            Searches in "mydb" database views for "some string" in the textbody

        .EXAMPLE
            Find-DbaView -SqlInstance sql2016 -Database MyDB -Pattern RUNTIME -IncludeSystemObjects

            Searches in "mydb" database views for "runtime" in the textbody
    #>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [parameter(Mandatory = $true)]
        [string]$Pattern,
        [switch]$IncludeSystemObjects,
        [switch]$IncludeSystemDatabases,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $sql = "SELECT OBJECT_SCHEMA_NAME(vw.object_id) as ViewSchema, vw.name, m.definition as TextBody FROM sys.sql_modules m, sys.views vw WHERE m.object_id = vw.object_id"
        if (!$IncludeSystemObjects) { $sql = "$sql AND vw.is_ms_shipped = 0" }
        $everyservervwcount = 0
    }
    process {
        foreach ($Instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $Instance"
                $server = Connect-SqlInstance -SqlInstance $Instance -SqlCredential $SqlCredential
            }
            catch {
                Write-Message -Level Warning -Message "Failed to connect to: $Instance"
                continue
            }

            if ($server.versionMajor -lt 9) {
                Write-Message -Level Warning -Message "This command only supports SQL Server 2005 and above."
                Continue
            }

            if ($IncludeSystemDatabases) {
                $dbs = $server.Databases | Where-Object { $_.Status -eq "normal" }
            }
            else {
                $dbs = $server.Databases | Where-Object { $_.Status -eq "normal" -and $_.IsSystemObject -eq $false }
            }

            if ($Database) {
                $dbs = $dbs | Where-Object Name -In $Database
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            $totalcount = 0
            $dbcount = $dbs.count
            foreach ($db in $dbs) {
                Write-Message -Level Verbose -Message "Searching on database $db"

                # If system objects aren't needed, find view text using SQL
                # This prevents SMO from having to enumerate

                if (!$IncludeSystemObjects) {
                    Write-Message -Level Debug -Message $sql
                    $rows = $db.ExecuteWithResults($sql).Tables.Rows
                    $vwcount = 0

                    foreach ($row in $rows) {
                        $totalcount++; $vwcount++; $everyservervwcount++

                        $viewSchema = $row.ViewSchema
                        $view = $row.name

                        Write-Message -Level Verbose -Message "Looking in View: $viewSchema.$view TextBody for $pattern"
                        if ($row.TextBody -match $Pattern) {
                            $vw = $db.Views | Where-Object {$_.Schema -eq $viewSchema -and $_.Name -eq $view}

                            $viewText = $vw.TextBody.split("`n`r")
                            $vwTextFound = $viewText | Select-String -Pattern $Pattern | ForEach-Object { "(LineNumber: $($_.LineNumber)) $($_.ToString().Trim())" }

                            [PSCustomObject]@{
                                ComputerName   = $server.ComputerName
                                SqlInstance    = $server.ServiceName
                                Database       = $db.Name
                                Schema         = $vw.Schema
                                Name           = $vw.Name
                                Owner          = $vw.Owner
                                IsSystemObject = $vw.IsSystemObject
                                CreateDate     = $vw.CreateDate
                                LastModified   = $vw.DateLastModified
                                ViewTextFound  = $vwTextFound -join "`n"
                                View           = $vw
                                ViewFullText   = $vw.TextBody
                            } | Select-DefaultView -ExcludeProperty View, ViewFullText
                        }
                    }
                }
                else {
                    $Views = $db.Views

                    foreach ($vw in $Views) {
                        $totalcount++; $vwcount++; $everyservervwcount++

                        $viewSchema = $row.ViewSchema
                        $view = $vw.Name

                        Write-Message -Level Verbose -Message "Looking in View: $viewSchema.$view TextBody for $pattern"
                        if ($vw.TextBody -match $Pattern) {

                            $viewText = $vw.TextBody.split("`n`r")
                            $vwTextFound = $viewText | Select-String -Pattern $Pattern | ForEach-Object { "(LineNumber: $($_.LineNumber)) $($_.ToString().Trim())" }

                            [PSCustomObject]@{
                                ComputerName   = $server.ComputerName
                                SqlInstance    = $server.ServiceName
                                Database       = $db.Name
                                Schema         = $vw.Schema
                                Name           = $vw.Name
                                Owner          = $vw.Owner
                                IsSystemObject = $vw.IsSystemObject
                                CreateDate     = $vw.CreateDate
                                LastModified   = $vw.DateLastModified
                                ViewTextFound  = $vwTextFound -join "`n"
                                View           = $vw
                                ViewFullText   = $vw.TextBody
                            } | Select-DefaultView -ExcludeProperty View, ViewFullText
                        }
                    }
                }
                Write-Message -Level Verbose -Message "Evaluated $vwcount views in $db"
            }
            Write-Message -Level Verbose -Message "Evaluated $totalcount total views in $dbcount databases"
        }
    }
    end {
        Write-Message -Level Verbose -Message "Evaluated $everyservervwcount total views"
    }
}
tools\dbatools\functions\Format-DbaBackupInformation.ps1
function Format-DbaBackupInformation {
    <#
        .SYNOPSIS
            Transforms the data in a dbatools backuphistory object for a restore

        .DESCRIPTION
            Performs various mapping on Backup History, ready restoring
            Options include changing restore paths, backup paths, database name and many others

        .PARAMETER BackupHistory
            A dbatools backupHistory object, normally this will have been created using Select-DbaBackupInformation

        .PARAMETER ReplaceDatabaseName
            If a single value is provided, this will be replaced do all occurrences a database name
            If a Hashtable is passed in, each database name mention will be replaced as specified. If a database's name does not appear it will not be replace
            DatabaseName will also be replaced where it  occurs in the file paths of data and log files.
            Please note, that this won't change the Logical Names of datafiles, that has to be done with a separate Alter DB call

        .PARAMETER DatabaseNamePrefix
            This string will be prefixed to all restored database's name

        .PARAMETER DataFileDirectory
            This will move ALL restored files to this location during the restore

        .PARAMETER LogFileDirectory
            This will move all log files to this location, overriding DataFileDirectory

        .PARAMETER DestinationFileStreamDirectory
            This move the FileStream folder and contents to the new location, overriding DataFileDirectory

        .PARAMETER FileNamePrefix
            This string will  be prefixed to all restored files (Data and Log)

        .PARAMETER RebaseBackupFolder
            Use this to rebase where your backups are stored.

        .PARAMETER Continue
            Indicates that this is a continuing restore

        .PARAMETER DatabaseFilePrefix
            A string that will be prefixed to every file restored

        .PARAMETER DatabaseFileSuffix
            A string that will be suffixed to every file restored

        .PARAMETER ReplaceDbNameInFile
            If set, will replace the old databasename with the new name if it occurs in the file name

        .PARAMETER FileMapping
            A hashtable that can be used to move specific files to a location.
            $FileMapping = @{'DataFile1'='c:\restoredfiles\Datafile1.mdf';'DataFile3'='d:\DataFile3.mdf'}
            And files not specified in the mapping will be restored to their original location
            This Parameter is exclusive with DestinationDataDirectory
            If specified, this will override any other file renaming/relocation options.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: DisasterRecovery, Backup, Restore
            Author:Stuart Moore (@napalmgram stuart-moore.com )

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Format-DbaBackupInformation

        .EXAMPLE
            $History | Format-DbaBackupInformation -ReplaceDatabaseName NewDb

            Changes as databasename references to NewDb, both in the database name and any restore paths. Note, this will fail if the BackupHistory object contains backups for more than 1 database

        .EXAMPLE
            $History | Format-DbaBackupInformation -ReplaceDatabaseName @{'OldB'='NewDb';'ProdHr'='DevHr'}

            Will change all occurences of original database name in the backup history (names and restore paths) using the mapping in the hashtable.
            In this example any occurance of OldDb will be replaced with NewDb and ProdHr with DevPR

        .EXAMPLE
            $History | Format-DbaBackupInformation -DataFileDirectory 'D:\DataFiles\' -LogFileDirectory 'E:\LogFiles\

            This example with change the restore path for all datafiles (everything that is not a log file) to d:\datafiles
            And all Transaction Log files will be restored to E:\Logfiles

        .EXAMPLE
            $History | Formate-DbaBackupInformation -RebaseBackupFolder f:\backups

            This example changes the location that SQL Server will look for the backups. This is useful if you've moved the backups to a different location
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [object[]]$BackupHistory,
        [object]$ReplaceDatabaseName,
        [switch]$ReplaceDbNameInFile,
        [string]$DataFileDirectory,
        [string]$LogFileDirectory,
        [string]$DestinationFileStreamDirectory,
        [string]$DatabaseNamePrefix,
        [string]$DatabaseFilePrefix,
        [string]$DatabaseFileSuffix,
        [string]$RebaseBackupFolder,
        [switch]$Continue,
        [hashtable]$FileMapping,
        [switch]$EnableException
    )
    Begin {

        Write-Message -Message "Starting" -Level Verbose
        if ($null -ne $ReplaceDatabaseName) {
            if ($ReplaceDatabaseName -is [string] -or $ReplaceDatabaseName.ToString() -ne 'System.Collections.Hashtable') {
                Write-Message -Message "String passed in for DB rename" -Level Verbose
                $ReplaceDatabaseNameType = 'single'
            }
            elseif ($ReplaceDatabaseName -is [HashTable] -or $ReplaceDatabaseName.ToString() -eq 'System.Collections.Hashtable' ) {
                Write-Message -Message "Hashtable passed in for DB rename" -Level Verbose
                $ReplaceDatabaseNameType = 'multi'
            }
            else {
                Write-Message -Message "ReplacemenDatabaseName is $($ReplaceDatabaseName.Gettype().ToString()) - $ReplaceDatabaseName" -level Verbose
            }
        }
        if ((Test-Bound -Parameter DataFileDirectory) -and $DataFileDirectory[-1] -eq '\' ) {
            $DataFileDirectory = $DataFileDirectory.substring(0, $DataFileDirectory.length - 1)
        }
        if ((Test-Bound -Parameter DestinationFileStreamDirectory) -and $DestinationFileStreamDirectory[-1] -eq '\' ) {
            $DestinationFileStreamDirectory = $DestinationFileStreamDirectory.substring(0, $DestinationFileStreamDirectory.length - 1)
        }
        if ((Test-Bound -Parameter LogFileDirectory) -and $LogFileDirectory[-1] -eq '\' ) {
            $LogFileDirectory = $LogFileDirectory.substring(0, $LogFileDirectory.length - 1)
        }
        if ((Test-Bound -Parameter RebaseBackupFolder) -and $RebaseBackupFolder[-1] -eq '\' ) {
            $RebaseBackupFolder = $RebaseBackupFolder.substring(0, $RebaseBackupFolder.length - 1)
        }
    }


    Process {

        ForEach ($History in $BackupHistory) {
            if ("OriginalDatabase" -notin $History.PSobject.Properties.name) {
                $History | Add-Member -Name 'OriginalDatabase' -Type NoteProperty -Value $History.Database
            }
            if ("OriginalFileList" -notin $History.PSobject.Properties.name) {
                $History | Add-Member -Name 'OriginalFileList' -Type NoteProperty -Value ''
                $History | ForEach-Object {$_.OriginalFileList = $_.FileList}
            }
            if ("OriginalFullName" -notin $History.PSobject.Properties.name) {
                $History | Add-Member -Name 'OriginalFullName' -Type NoteProperty -Value $History.FullName
            }
            if ("IsVerified" -notin $History.PSobject.Properties.name) {
                $History | Add-Member -Name 'IsVerified' -Type NoteProperty -Value $False
            }
            Switch ($History.Type) {
                'Full' {$History.Type = 'Database'}
                'Differential' {$History.Type = 'Database Differential'}
                'Log' {$History.Type = 'Transaction Log'}
            }


            if ($ReplaceDatabaseNameType -eq 'single' -and $ReplaceDatabaseName -ne '' ) {
                $History.Database = $ReplaceDatabaseName
                $ReplaceMentName = $ReplaceDatabaseName
                Write-Message -Message "New DbName (String) = $($History.Database)" -Level Verbose
            }
            elseif ($ReplaceDatabaseNameType -eq 'multi') {
                if ($null -ne $ReplaceDatabaseName[$History.Database]) {
                    $History.Database = $ReplaceDatabaseName[$History.Database]
                    $ReplacementName = $ReplaceDatabaseName[$History.Database]
                    Write-Message -Message "New DbName (Hash) = $($History.Database)" -Level Verbose
                }
            }
            $History.Database = $DatabaseNamePrefix + $History.Database
            if ($true -ne $Continue) {
                $History.FileList | ForEach-Object {
                    if ($null -ne $FileMapping ) {
                        if ($null -ne $FileMapping[$_.LogicalName]) {
                            $_.PhysicalName = $FileMapping[$_.LogicalName]
                        }
                    }
                    else {
                        if ($ReplaceDbNameInFile -eq $true) {
                            $_.PhysicalName = $_.PhysicalName -Replace $History.OriginalDatabase, $History.Database
                        }
                        Write-message -Message " 1 PhysicalName = $($_.PhysicalName) " -Level Verbose
                        $Pname = [System.Io.FileInfo]$_.PhysicalName
                        $RestoreDir = $Pname.DirectoryName
                        if ($_.Type -eq 'D' -or $_.FileType -eq 'D') {
                            if ('' -ne $DataFileDirectory) {
                                $RestoreDir = $DataFileDirectory
                            }
                        }
                        elseif ($_.Type -eq 'L' -or $_.FileType -eq 'L') {
                            if ('' -ne $LogFileDirectory) {
                                $RestoreDir = $LogFileDirectory
                            }
                            elseif ('' -ne $DataFileDirectory) {
                                $RestoreDir = $DataFileDirectory
                            }
                        }
                        elseif ($_.Type -eq 'S' -or $_.FileType -eq 'S') {
                            if ('' -ne $DestinationFileStreamDirectory) {
                                $RestoreDir = $DestinationFileStreamDirectory
                            }
                            elseif ('' -ne $DataFileDirectory) {
                                $RestoreDir = $DataFileDirectory
                            }
                        }

                        $_.PhysicalName = $RestoreDir + "\" + $DatabaseFilePrefix + $Pname.BaseName + $DatabaseFileSuffix + $pname.extension
                        Write-message -Message "PhysicalName = $($_.PhysicalName) " -Level Verbose
                    }
                }
            }
            if ($null -ne $RebaseBackupFolder -and $History.FullName[0] -notmatch 'http') {
                $History.FullName | ForEach-Object {
                    $file = [System.IO.FileInfo]$_
                    $_ = $RebaseBackupFolder + "\" + $file.BaseName + $file.Extension
                }
            }
            $History
        }
    }
}
tools\dbatools\functions\Get-DbaAgDatabase.ps1
function Get-DbaAgDatabase {
    <#
        .SYNOPSIS
            Outputs the databases involved in the Availability Group(s) found on the server.

        .DESCRIPTION
            Default view provides most common set of properties for information on the database in an Availability Group(s).

            Information returned on the database will be specific to that replica, whether it is primary or a secondary.

            This command will return an SMO object, but it is the AvailabilityDatabases object and not the Server.Databases object.

        .PARAMETER SqlInstance
            The SQL Server instance. Server version must be SQL Server version 2012 or higher.

        .PARAMETER SqlCredential
            Allows you to login to servers using SQL Logins instead of Windows Authentication (AKA Integrated or Trusted).

        .PARAMETER AvailabilityGroup
            Specify the Availability Group name that you want to get information on.

        .PARAMETER Database
            Specify the database(s) to pull information for. This list is auto-populated from the server for tab completion. Multiple databases can be specified. If none are specified all databases will be processed.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Hadr, AG, AvailabilityGroup, Replica
            Author: Shawn Melton (@wsmelton)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaAgDatabase

        .EXAMPLE
            Get-DbaAgDatabase -SqlInstance sqlserver2014a

            Returns basic information on all the databases in each Availability Group found on sqlserver2014a

        .EXAMPLE
            Get-DbaAgDatabase -SqlInstance sqlserver2014a -AvailabilityGroup AG-a

            Returns basic information on all the databases in the Availability Group AG-a on sqlserver2014a

        .EXAMPLE
            Get-DbaAgDatabase -SqlInstance sqlserver2014a -AvailabilityGroup AG-a -Database AG-Database

            Returns basic information on the database AG-Database found in the Availability Group AG-a on server sqlserver2014a
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(ValueFromPipeline = $true)]
        [object[]]$AvailabilityGroup,
        [object[]]$Database,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($serverName in $SqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $serverName -SqlCredential $SqlCredential -MinimumVersion 11
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($server.IsHadrEnabled -eq $false) {
                Stop-Function -Message "Availability Group (HADR) is not configured for the instance: $serverName." -Target $serverName -Continue
            }

            $ags = $server.AvailabilityGroups
            if ($AvailabilityGroup) {
                $ags = $ags | Where-Object Name -in $AvailabilityGroup
            }

            foreach ($ag in $ags) {
                $agDatabases = $ag.AvailabilityDatabases
                foreach ($agDb in $agDatabases) {
                    if ($Database -and $agDb.Name -notmatch $Database) {
                        continue
                    }

                    Add-Member -Force -InputObject $agDb -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                    Add-Member -Force -InputObject $agDb -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                    Add-Member -Force -InputObject $agDb -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                    Add-Member -Force -InputObject $agDb -MemberType NoteProperty -Name Replica -value $server.ComputerName

                    $defaults = 'ComputerName', 'InstanceName', 'SqlInstance', 'Parent as AvailabilityGroup', 'Replica', 'Name as DatabaseName', 'SynchronizationState', 'IsFailoverReady', 'IsJoined', 'IsSuspended'
                    Select-DefaultView -InputObject $agDb -Property $defaults
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaAgentAlert.ps1
function Get-DbaAgentAlert {
    <#
        .SYNOPSIS
            Returns all SQL Agent alerts on a SQL Server Agent.

        .DESCRIPTION
            This function returns SQL Agent alerts.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .NOTES
            Author: Klaas Vandenberghe ( @PowerDBAKlaas )
            Tags: Agent, SMO
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .LINK
            https://dbatools.io/Get-DbaAgentAlert

        .EXAMPLE
            Get-DbaAgentAlert -SqlInstance ServerA,ServerB\instanceB
            Returns all SQL Agent alerts on serverA and serverB\instanceB

        .EXAMPLE
            'serverA','serverB\instanceB' | Get-DbaAgentAlert
            Returns all SQL Agent alerts  on serverA and serverB\instanceB
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "Instance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]
        $SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException

    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            Write-Message -Level Verbose -Message "Getting Edition from $server"
            Write-Message -Level Verbose -Message "$server is a $($server.Edition)"

            if ($server.Edition -like 'Express*') {
                Stop-Function -Message "There is no SQL Agent on $server, it's a $($server.Edition)" -Continue
            }

            $defaults = "ComputerName", "SqlInstance", "InstanceName", "Name", "ID", "JobName", "AlertType", "CategoryName", "Severity", "IsEnabled", "DelayBetweenResponses", "LastRaised", "OccurrenceCount"

            $alerts = $server.Jobserver.Alerts

            foreach ($alert in $alerts) {
                $lastraised = [dbadatetime]$alert.LastOccurrenceDate

                Add-Member -Force -InputObject $alert -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                Add-Member -Force -InputObject $alert -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                Add-Member -Force -InputObject $alert -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                Add-Member -Force -InputObject $alert -MemberType NoteProperty Notifications -value $alert.EnumNotifications()
                Add-Member -Force -InputObject $alert -MemberType NoteProperty LastRaised -value $lastraised

                Select-DefaultView -InputObject $alert -Property $defaults
            }
        }
    }
}
tools\dbatools\functions\Get-DbaAgentJob.ps1
#ValidationTags#Messaging#
function Get-DbaAgentJob {
    <#
        .SYNOPSIS
            Gets SQL Agent Job information for each instance(s) of SQL Server.

        .DESCRIPTION
            The Get-DbaAgentJob returns connected SMO object for SQL Agent Job information for each instance(s) of SQL Server.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Job
            The job(s) to process - this list is auto-populated from the server. If unspecified, all jobs will be processed.

        .PARAMETER ExcludeJob
            The job(s) to exclude - this list is auto-populated from the server.

        .PARAMETER NoDisabledJobs
            Switch will exclude disabled jobs from the output.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Job, Agent
            Author: Garry Bargsley (@gbargsley), http://blog.garrybargsley.com

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaAgentJob

        .EXAMPLE
            Get-DbaAgentJob -SqlInstance localhost

            Returns all SQL Agent Jobs on the local default SQL Server instance

        .EXAMPLE
            Get-DbaAgentJob -SqlInstance localhost, sql2016

            Returns all SQl Agent Jobs for the local and sql2016 SQL Server instances

        .EXAMPLE
            Get-DbaAgentJob -SqlInstance localhost -Job BackupData, BackupDiff

            Returns all SQL Agent Jobs named BackupData and BackupDiff from the local SQL Server instance.

        .EXAMPLE
            Get-DbaAgentJob -SqlInstance localhost -ExcludeJob BackupDiff

            Returns all SQl Agent Jobs for the local SQL Server instances, except the BackupDiff Job.

        .EXAMPLE
            Get-DbaAgentJob -SqlInstance localhost -NoDisabledJobs

            Returns all SQl Agent Jobs for the local SQL Server instances, excluding the disabled jobs.

        .EXAMPLE
            $servers | Get-DbaAgentJob | Out-GridView -Passthru | Start-DbaAgentJob -WhatIf

            Find all of your Jobs from servers in the $server collection, select the jobs you want to start then see jobs would start if you ran Start-DbaAgentJob
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Job,
        [object[]]$ExcludeJob,
        [switch]$NoDisabledJobs,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $jobs = $server.JobServer.Jobs

            if ($Job) {
                $jobs = $jobs | Where-Object Name -In $Job
            }
            if ($ExcludeJob) {
                $jobs = $jobs | Where-Object Name -NotIn $ExcludeJob
            }
            if ($NoDisabledJobs) {
                $jobs = $Jobs | Where-Object IsEnabled -eq $true
            }

            foreach ($agentJob in $jobs) {
                Add-Member -Force -InputObject $agentJob -MemberType NoteProperty -Name ComputerName -value $agentJob.Parent.Parent.ComputerName
                Add-Member -Force -InputObject $agentJob -MemberType NoteProperty -Name InstanceName -value $agentJob.Parent.Parent.ServiceName
                Add-Member -Force -InputObject $agentJob -MemberType NoteProperty -Name SqlInstance -value $agentJob.Parent.Parent.DomainInstanceName

                Select-DefaultView -InputObject $agentJob -Property ComputerName, InstanceName, SqlInstance, Name, Category, OwnerLoginName, CurrentRunStatus, CurrentRunRetryAttempt, 'IsEnabled as Enabled', LastRunDate, LastRunOutcome, DateCreated, HasSchedule, OperatorToEmail, 'DateCreated as CreateDate'
            }
        }
    }
}
tools\dbatools\functions\Get-DbaAgentJobCategory.ps1
function Get-DbaAgentJobCategory {
    <#
        .SYNOPSIS
            Get-DbaAgentJobCategory retrieves the job categories.

        .DESCRIPTION
            Get-DbaAgentJobCategory makes it possible to retrieve the job categories.

        .PARAMETER SqlInstance
             SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Category
            The name of the category to filter out. If no category is used all catgories will be returned.

        .PARAMETER CategoryType
            The type of category. This can be "LocalJob", "MultiServerJob" or "None".
            If no category is used all catgories types will be returned.

        .PARAMETER Force
            The force parameter will ignore some errors in the parameters and assume defaults.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Agent, Job, JobCategory
            Author: Sander Stad (@sqlstad, sqlstad.nl)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaAgentJobCategory

        .EXAMPLE
            Get-DbaAgentJobCategory -SqlInstance sql1

            Return all the job categories.

        .EXAMPLE
            Get-DbaAgentJobCategory -SqlInstance sql1 -Category 'Log Shipping'

            Return all the job categories that have the name 'Log Shipping'.

        .EXAMPLE
            Get-DbaAgentJobCategory -SqlInstance sstad-pc -CategoryType MultiServerJob

            Return all the job categories that have a type MultiServerJob.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [ValidateNotNullOrEmpty()]
        [string[]]$Category,
        [ValidateSet("LocalJob", "MultiServerJob", "None")]
        [string]$CategoryType,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {

        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance."
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            # get all the job categories
            $jobCategories = $server.JobServer.JobCategories |
                Where-Object {
                ($_.Name -in $Category -or !$Category) -and
                ($_.CategoryType -in $CategoryType -or !$CategoryType)
            }

            # Set the default output
            $defaults = 'ComputerName', 'InstanceName', 'SqlInstance', 'Name', 'ID', 'CategoryType', 'JobCount'

            # Loop through each of the categories
            try {
                foreach ($cat in $jobCategories) {

                    # Get the jobs associated with the category
                    $jobCount = ($server.JobServer.Jobs | Where-Object {$_.CategoryID -eq $cat.ID}).Count

                    # Add new properties to the category object
                    Add-Member -Force -InputObject $cat -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                    Add-Member -Force -InputObject $cat -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                    Add-Member -Force -InputObject $cat -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                    Add-Member -Force -InputObject $cat -MemberType NoteProperty -Name JobCount -Value $jobCount

                    # Show the result
                    Select-DefaultView -InputObject $cat -Property $defaults
                }
            }
            catch {
                Stop-Function -ErrorRecord $_ -Target $instance -Message "Failure. Collection may have been modified" -Continue
            }

        } # for each instance

    } # end process

    end {
        if (Test-FunctionInterrupt) { return }
        Write-Message -Message "Finished retrieving job category." -Level Verbose
    }

}
tools\dbatools\functions\Get-DbaAgentJobHistory.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaAgentJobHistory {
    <#
        .SYNOPSIS
            Gets execution history of SQL Agent Job on instance(s) of SQL Server.

        .DESCRIPTION
            Get-DbaAgentJobHistory returns all information on the executions still available on each instance(s) of SQL Server submitted.
            The cleanup of SQL Agent history determines how many records are kept.

            https://msdn.microsoft.com/en-us/library/ms201680.aspx
            https://msdn.microsoft.com/en-us/library/microsoft.sqlserver.management.smo.agent.jobhistoryfilter(v=sql.120).aspx

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Job
            The name of the job from which the history is wanted. If unspecified, all jobs will be processed.

        .PARAMETER ExcludeJob
            The job(s) to exclude - this list is auto-populated from the server

        .PARAMETER StartDate
            The DateTime starting from which the history is wanted. If unspecified, all available records will be processed.

        .PARAMETER EndDate
            The DateTime before which the history is wanted. If unspecified, all available records will be processed.

        .PARAMETER NoJobSteps
            Use this switch to discard all job steps, and return only the job totals

        .PARAMETER WithOutputFile
            Use this switch to retrieve the output file (only if you want step details). Bonus points, we handle the quirks
            of SQL Agent tokens to the best of our knowledge (https://technet.microsoft.com/it-it/library/ms175575(v=sql.110).aspx)

        .PARAMETER JobCollection
            An array of SMO jobs

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Job, Agent
            Author: Klaas Vandenberghe ( @PowerDbaKlaas )
            Editor: niphlod

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaAgentJobHistory

        .EXAMPLE
            Get-DbaAgentJobHistory -SqlInstance localhost

            Returns all SQL Agent Job execution results on the local default SQL Server instance.

        .EXAMPLE
            Get-DbaAgentJobHistory -SqlInstance localhost, sql2016

            Returns all SQL Agent Job execution results for the local and sql2016 SQL Server instances.

        .EXAMPLE
            'sql1','sql2\Inst2K17' | Get-DbaAgentJobHistory

            Returns all SQL Agent Job execution results for sql1 and sql2\Inst2K17.

        .EXAMPLE
            Get-DbaAgentJobHistory -SqlInstance sql2\Inst2K17 | select *

            Returns all properties for all SQl Agent Job execution results on sql2\Inst2K17.

        .EXAMPLE
            Get-DbaAgentJobHistory -SqlInstance sql2\Inst2K17 -Job 'Output File Cleanup'

            Returns all properties for all SQl Agent Job execution results of the 'Output File Cleanup' job on sql2\Inst2K17.


        .EXAMPLE
            Get-DbaAgentJobHistory -SqlInstance sql2\Inst2K17 -Job 'Output File Cleanup' -WithOutputFile

            Returns all properties for all SQl Agent Job execution results of the 'Output File Cleanup' job on sql2\Inst2K17,
            with additional properties that show the output filename path

        .EXAMPLE
            Get-DbaAgentJobHistory -SqlInstance sql2\Inst2K17 -NoJobSteps

            Returns the SQL Agent Job execution results for the whole jobs on sql2\Inst2K17, leaving out job step execution results.

        .EXAMPLE
            Get-DbaAgentJobHistory -SqlInstance sql2\Inst2K17 -StartDate '2017-05-22' -EndDate '2017-05-23 12:30:00'

            Returns the SQL Agent Job execution results between 2017/05/22 00:00:00 and 2017/05/23 12:30:00 on sql2\Inst2K17.

        .EXAMPLE
            Get-DbaAgentJob -SqlInstance sql2016 | Where Name -match backup | Get-DbaAgentJobHistory

            Gets all jobs with the name that match the regex pattern "backup" and then gets the job history from those. You can also use -Like *backup* in this example.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    param (
        [parameter(Mandatory, ValueFromPipeline, ParameterSetName = "Server")]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]
        $SqlCredential,
        [object[]]$Job,
        [object[]]$ExcludeJob,
        [DateTime]$StartDate = "1900-01-01",
        [DateTime]$EndDate = $(Get-Date),
        [switch]$NoJobSteps,
        [switch]$WithOutputFile,
        [parameter(Mandatory, ValueFromPipeline, ParameterSetName = "Collection")]
        [Microsoft.SqlServer.Management.Smo.Agent.Job]$JobCollection,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $filter = New-Object Microsoft.SqlServer.Management.Smo.Agent.JobHistoryFilter
        $filter.StartRunDate = $StartDate
        $filter.EndRunDate = $EndDate


        if ($NoJobSteps -and $WithOutputFile) {
            Stop-Function -Message "You can't use -NoJobSteps and -WithOutputFile together"
        }

        function Get-JobHistory {
            [CmdletBinding()]
            param (
                $Server,
                $Job,
                [switch]$WithOutputFile
            )
            $tokenrex = [regex]'\$\((?<method>[^()]+)\((?<tok>[^)]+)\)\)|\$\((?<tok>[^)]+)\)'
            $propmap = @{
                'INST'      = $Server.ServiceName
                'MACH'      = $Server.ComputerName
                'SQLDIR'    = $Server.InstallDataDirectory
                'SQLLOGDIR' = $Server.ErrorLogPath
                #'STEPCT' loop number ?
                'SRVR'      = $Server.DomainInstanceName
                # WMI( property ) impossible
            }


            $squote_rex = [regex]"(?<!')'(?!')"
            $dquote_rex = [regex]'(?<!")"(?!")'
            $rbrack_rex = [regex]'(?<!])](?!])'

            function Resolve-TokenEscape($method, $value) {
                if (!$method) {
                    return $value
                }
                $value = switch ($method) {
                    'ESCAPE_SQUOTE' { $squote_rex.Replace($value, "''") }
                    'ESCAPE_DQUOTE' { $dquote_rex.Replace($value, '""') }
                    'ESCAPE_RBRACKET' { $rbrack_rex.Replace($value, ']]') }
                    'ESCAPE_NONE' { $value }
                    default { $value }
                }
                return $value
            }

            #'STEPID' =  stepid
            #'STRTTM' job begin time
            #'STRTDT' job begin date
            #'JOBID' = JobId
            function Resolve-JobToken($exec, $outfile, $outcome) {
                $n = $tokenrex.Matches($outfile)
                foreach ($x in $n) {
                    $tok = $x.Groups['tok'].Value
                    $EscMethod = $x.Groups['method'].Value
                    if ($propmap.containskey($tok)) {
                        $repl = Resolve-TokenEscape -method $EscMethod -value $propmap[$tok]
                        $outfile = $outfile.Replace($x.Value, $repl)
                    }
                    elseif ($tok -eq 'STEPID') {
                        $repl = Resolve-TokenEscape -method $EscMethod -value $exec.StepID
                        $outfile = $outfile.Replace($x.Value, $repl)
                    }
                    elseif ($tok -eq 'JOBID') {
                        # convert(binary(16), ?)
                        $repl = @('0x') + @($exec.JobID.ToByteArray() | foreach { $_.ToString('X2') }) -join ''
                        $repl = Resolve-TokenEscape -method $EscMethod -value $repl
                        $outfile = $outfile.Replace($x.Value, $repl)
                    }
                    elseif ($tok -eq 'STRTDT') {
                        $repl = Resolve-TokenEscape -method $EscMethod -value $outcome.RunDate.toString('yyyyMMdd')
                        $outfile = $outfile.Replace($x.Value, $repl)
                    }
                    elseif ($tok -eq 'STRTTM') {
                        $repl = Resolve-TokenEscape -method $EscMethod -value ([int]$outcome.RunDate.toString('HHmmss')).toString()
                        $outfile = $outfile.Replace($x.Value, $repl)
                    }
                    elseif ($tok -eq 'DATE') {
                        $repl = Resolve-TokenEscape -method $EscMethod -value $exec.RunDate.toString('yyyyMMdd')
                        $outfile = $outfile.Replace($x.Value, $repl)
                    }
                    elseif ($tok -eq 'TIME') {
                        $repl = Resolve-TokenEscape -method $EscMethod -value ([int]$exec.RunDate.toString('HHmmss')).toString()
                        $outfile = $outfile.Replace($x.Value, $repl)
                    }
                }
                return $outfile
            }
            try {
                Write-Message -Message "Attempting to get job history from $instance" -Level Verbose
                if ($Job) {
                    foreach ($currentjob in $Job) {
                        $filter.JobName = $currentjob
                        $executions += $server.JobServer.EnumJobHistory($filter)
                    }
                }
                else {
                    $executions = $server.JobServer.EnumJobHistory($filter)
                }
                if ($NoJobSteps) {
                    $executions = $executions | Where-Object { $_.StepID -eq 0 }
                }

                if ($WithOutputFile) {
                    $outmap = @{}
                    $outfiles = Get-DbaAgentJobOutputFile -SqlInstance $Server -SqlCredential $SqlCredential -Job $Job

                    foreach ($out in $outfiles) {
                        if (!$outmap.ContainsKey($out.Job)) {
                            $outmap[$out.Job] = @{}
                        }
                        $outmap[$out.Job][$out.StepId] = $out.OutputFileName
                    }
                }
                $outcome = [pscustomobject]@{}
                foreach ($execution in $executions) {
                    $status = switch ($execution.RunStatus) {
                        0 { "Failed" }
                        1 { "Succeeded" }
                        2 { "Retry" }
                        3 { "Canceled" }
                    }

                    Add-Member -Force -InputObject $execution -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                    Add-Member -Force -InputObject $execution -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                    Add-Member -Force -InputObject $execution -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                    $DurationInSeconds = ($execution.RunDuration % 100) + [int]( ($execution.RunDuration % 10000 ) / 100 ) * 60 + [int]( ($execution.RunDuration % 1000000 ) / 10000 ) * 60 * 60
                    Add-Member -Force -InputObject $execution -MemberType NoteProperty -Name StartDate -value ([dbadatetime]$execution.RunDate)
                    Add-Member -Force -InputObject $execution -MemberType NoteProperty -Name EndDate -value ([dbadatetime]$execution.RunDate.AddSeconds($DurationInSeconds))
                    Add-Member -Force -InputObject $execution -MemberType NoteProperty -Name Duration -value ([prettytimespan](New-TimeSpan -Seconds $DurationInSeconds))
                    Add-Member -Force -InputObject $execution -MemberType NoteProperty -Name Status -value $status
                    if ($WithOutputFile) {
                        if ($execution.StepID -eq 0) {
                            $outcome = $execution
                        }
                        try {
                            $outname = $outmap[$execution.JobName][$execution.StepID]
                            $outname = Resolve-JobToken -exec $execution -outcome $outcome -outfile $outname
                            $outremote = Join-AdminUNC $Server.ComputerName $outname
                        }
                        catch {
                            $outname = ''
                            $outremote = ''
                        }
                        Add-Member -Force -InputObject $execution -MemberType NoteProperty -Name OutputFileName -value $outname
                        Add-Member -Force -InputObject $execution -MemberType NoteProperty -Name RemoteOutputFileName -value $outremote
                        Select-DefaultView -InputObject $execution -Property ComputerName, InstanceName, SqlInstance, 'JobName as Job', StepName, RunDate, StartDate, EndDate, Duration, Status, OperatorEmailed, Message, OutputFileName, RemoteOutputFileName
                    }
                    else {
                        Select-DefaultView -InputObject $execution -Property ComputerName, InstanceName, SqlInstance, 'JobName as Job', StepName, RunDate, StartDate, EndDate, Duration, Status, OperatorEmailed, Message
                    }

                }
            }
            catch {
                Stop-Function -Message "Could not get Agent Job History from $instance" -Target $instance -Continue
            }
        }
    }

    process {

        if (Test-FunctionInterrupt) { return }

        if ($JobCollection) {
            foreach ($currentjob in $JobCollection) {
                Get-JobHistory -Server $currentjob.Parent.Parent -Job $currentjob.Name -WithOutputFile:$WithOutputFile
            }
        }

        foreach ($instance in $SqlInstance) {
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }


            if ($ExcludeJob) {
                $jobs = $server.JobServer.Jobs.Name | Where-Object { $_ -notin $ExcludeJob }
                foreach ($currentjob in $jobs) {
                    Get-JobHistory -Server $server -Job $currentjob -WithOutputFile:$WithOutputFile
                }
            }
            else {
                Get-JobHistory -Server $server -Job $Job -WithOutputFile:$WithOutputFile
            }
        }
    }
}
tools\dbatools\functions\Get-DbaAgentJobOutputFile.ps1
function Get-DbaAgentJobOutputFile {
    <#
        .Synopsis
            Returns the Output File for each step of one or many agent job with the Job Names provided dynamically if
            required for one or more SQL Instances

        .DESCRIPTION
            This function returns for one or more SQL Instances the output file value for each step of one or many agent job with the Job Names
            provided dynamically. It will not return anything if there is no Output File

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SQLCredential
            Credential object used to connect to the SQL Server as a different user be it Windows or SQL Server. Windows users are determiend by
            the existence of a backslash, so if you are intending to use an alternative Windows connection instead of a SQL login, ensure it
            contains a backslash.

        .PARAMETER Job
            The job(s) to process - this list is auto-populated from the server. If unspecified, all jobs will be processed.

        .PARAMETER ExcludeJob
            The job(s) to exclude - this list is auto-populated from the server

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Agent, Job
            Author: Rob Sewell (https://sqldbawithabeard.com)
            Editor: niphlod

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            Get-DbaAgentJobOutputFile -SqlInstance SERVERNAME -Job 'The Agent Job'

            This will return the configured paths to the output files for each of the job step of the The Agent Job Job
            on the SERVERNAME instance

        .EXAMPLE
            Get-DbaAgentJobOutputFile -SqlInstance SERVERNAME

            This will return the configured paths to the output files for each of the job step of all the Agent Jobs
            on the SERVERNAME instance

        .EXAMPLE
            Get-DbaAgentJobOutputFile -SqlInstance SERVERNAME,SERVERNAME2 -Job 'The Agent Job'

            This will return the configured paths to the output files for each of the job step of the The Agent Job Job
            on the SERVERNAME instance and SERVERNAME2

        .EXAMPLE
            $Servers = 'SERVER','SERVER\INSTANCE1'
            Get-DbaAgentJobOutputFile -SqlInstance $Servers -Job 'The Agent Job' -OpenFile

            This will return the configured paths to the output files for each of the job step of the The Agent Job Job
            on the SERVER instance and the SERVER\INSTANCE1 and open the files if they are available

        .EXAMPLE
            Get-DbaAgentJobOutputFile -SqlInstance SERVERNAME  | Out-GridView

            This will return the configured paths to the output files for each of the job step of all the Agent Jobs
            on the SERVERNAME instance and Pipe them to Out-GridView

        .EXAMPLE
            (Get-DbaAgentJobOutputFile -SqlInstance SERVERNAME | ogv -PassThru).FileName | Invoke-Item

            This will return the configured paths to the output files for each of the job step of all the Agent Jobs
            on the SERVERNAME instance and Pipe them to Out-GridView and enable you to choose the output
            file and open it

        .EXAMPLE
            Get-DbaAgentJobOutputFile -SqlInstance SERVERNAME -Verbose

            This will return the configured paths to the output files for each of the job step of all the Agent Jobs
            on the SERVERNAME instance and also show the job steps without an output file
    #>
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true, HelpMessage = 'The SQL Server Instance',
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false,
            Position = 0)]
        [ValidateNotNull()]
        [ValidateNotNullOrEmpty()]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Parameter(Mandatory = $false, HelpMessage = 'SQL Credential',
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false,
            Position = 1)]
        [PSCredential]$SqlCredential,
        [object[]]$Job,
        [object[]]$ExcludeJob,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $sqlinstance) {
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $jobs = $Server.JobServer.Jobs
            if ($Job) {
                $jobs = $jobs | Where-Object Name -In $Job
            }
            if ($ExcludeJob) {
                $jobs = $jobs | Where-Object Name -NotIn $ExcludeJob
            }
            foreach ($j in $Jobs) {
                foreach ($Step in $j.JobSteps) {
                    if ($Step.OutputFileName) {
                        [pscustomobject]@{
                            ComputerName         = $server.ComputerName
                            InstanceName         = $server.ServiceName
                            SqlInstance          = $server.DomainInstanceName
                            Job                  = $j.Name
                            JobStep              = $Step.Name
                            OutputFileName       = $Step.OutputFileName
                            RemoteOutputFileName = Join-AdminUNC $Server.ComputerName $Step.OutputFileName
                            StepId               = $Step.Id
                        } | Select-DefaultView -ExcludeProperty StepId
                    }
                    else {
                        Write-Message -Level Verbose -Message "$step for $j has no output file"
                    }
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaAgentJobStep.ps1
function Get-DbaAgentJobStep {
    <#
        .SYNOPSIS
            Gets SQL Agent Job Step information for each instance(s) of SQL Server.

        .DESCRIPTION
            The Get-DbaAgentJobStep returns connected SMO object for SQL Agent Job Step for each instance(s) of SQL Server.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Job
            The job(s) to process - this list is auto-populated from the server. If unspecified, all jobs will be processed.

        .PARAMETER ExcludeJob
            The job(s) to exclude - this list is auto-populated from the server.

        .PARAMETER NoDisabledJobs
            Switch will exclude disabled jobs from the output.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Job, Agent
            Author: Klaas Vandenberghe (@PowerDbaKlaas), http://powerdba.eu

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaAgentJobStep

        .EXAMPLE
            Get-DbaAgentJobStep -SqlInstance localhost

            Returns all SQL Agent Job Steps on the local default SQL Server instance

        .EXAMPLE
            Get-DbaAgentJobStep -SqlInstance localhost, sql2016

            Returns all SQl Agent Job Steps for the local and sql2016 SQL Server instances

        .EXAMPLE
            Get-DbaAgentJobStep -SqlInstance localhost -Job BackupData, BackupDiff

            Returns all SQL Agent Job Steps for the jobs named BackupData and BackupDiff from the local SQL Server instance.

        .EXAMPLE
            Get-DbaAgentJobStep -SqlInstance localhost -ExcludeJob BackupDiff

            Returns all SQl Agent Job Steps for the local SQL Server instances, except for the BackupDiff Job.

        .EXAMPLE
            Get-DbaAgentJobStep -SqlInstance localhost -NoDisabledJobs

            Returns all SQl Agent Job Steps for the local SQL Server instances, excluding the disabled jobs.

        .EXAMPLE
            $servers | Get-DbaAgentJobStep

            Find all of your Job Steps from servers in the $server collection
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]
        $SqlCredential,
        [object[]]$Job,
        [object[]]$ExcludeJob,
        [switch]$NoDisabledJobs,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            Write-Message -Level Verbose -Message "Collecting jobs on $instance"
            $jobs = $server.JobServer.Jobs

            if ($Job) {
                $jobs = $jobs | Where-Object Name -In $Job
            }
            if ($ExcludeJob) {
                $jobs = $jobs | Where-Object Name -NotIn $ExcludeJob
            }
            if ($NoDisabledJobs) {
                $jobs = $Jobs | Where-Object IsEnabled -eq $true
            }
            Write-Message -Level Verbose -Message "Collecting job steps on $instance"
            foreach ($agentJobStep in $jobs.jobsteps) {
                Add-Member -Force -InputObject $agentJobStep -MemberType NoteProperty -Name ComputerName -value $agentJobStep.Parent.Parent.Parent.ComputerName
                Add-Member -Force -InputObject $agentJobStep -MemberType NoteProperty -Name InstanceName -value $agentJobStep.Parent.Parent.Parent.ServiceName
                Add-Member -Force -InputObject $agentJobStep -MemberType NoteProperty -Name SqlInstance -value $agentJobStep.Parent.Parent.Parent.DomainInstanceName
                Add-Member -Force -InputObject $agentJobStep -MemberType NoteProperty -Name AgentJob -value $agentJobStep.Parent.Name

                Select-DefaultView -InputObject $agentJobStep -Property ComputerName, InstanceName, SqlInstance, AgentJob, Name, SubSystem, LastRunDate, LastRunOutcome, State
            }
        }
    }
}
tools\dbatools\functions\Get-DbaAgentLog.ps1
function Get-DbaAgentLog {
    <#
    .SYNOPSIS
        Gets the "SQL Agent Error Log" of an instance

    .DESCRIPTION
        Gets the "SQL Agent Error Log" of an instance. Returns all 10 error logs by default.

    .PARAMETER SqlInstance
        SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

    .PARAMETER SqlCredential
        Allows you to login to servers using SQL Logins as opposed to Windows Auth/Integrated/Trusted.

    .PARAMETER LogNumber
        An Int32 value that specifies the index number of the error log required. Error logs are listed 0 through 9 where 0 is the current error log and 9 is the oldest.

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Tags: Logging
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Get-DbaAgentLog

    .EXAMPLE
        Get-DbaAgentLog -SqlInstance sql01\sharepoint

        Returns the entire error log for the SQL Agent on sql01\sharepoint

    .EXAMPLE
        Get-DbaAgentLog -SqlInstance sql01\sharepoint -LogNumber 3, 6

        Returns log numbers 3 and 6 for the SQL Agent on sql01\sharepoint

    .EXAMPLE
        $servers = "sql2014","sql2016", "sqlcluster\sharepoint"
        $servers | Get-DbaAgentLog -LogNumber 0

        Returns the most recent SQL Agent error logs for "sql2014","sql2016" and "sqlcluster\sharepoint"
#>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [ValidateRange(0, 9)]
        [int[]]$LogNumber,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($LogNumber) {
                foreach ($number in $lognumber) {
                    try {
                        foreach ($object in $server.JobServer.ReadErrorLog($number)) {
                            Write-Message -Level Verbose -Message "Processing $object"
                            Add-Member -Force -InputObject $object -MemberType NoteProperty ComputerName -value $server.ComputerName
                            Add-Member -Force -InputObject $object -MemberType NoteProperty InstanceName -value $server.ServiceName
                            Add-Member -Force -InputObject $object -MemberType NoteProperty SqlInstance -value $server.DomainInstanceName

                            # Select all of the columns you'd like to show
                            Select-DefaultView -InputObject $object -Property ComputerName, InstanceName, SqlInstance, LogDate, ProcessInfo, Text
                        }
                    }
                    catch {
                        Stop-Function -Continue -Target $server -Message "Could not read from SQL Server Agent"
                    }
                }
            }
            else {
                try {
                    foreach ($object in $server.JobServer.ReadErrorLog()) {
                        Write-Message -Level Verbose -Message "Processing $object"
                        Add-Member -Force -InputObject $object -MemberType NoteProperty ComputerName -value $server.ComputerName
                        Add-Member -Force -InputObject $object -MemberType NoteProperty InstanceName -value $server.ServiceName
                        Add-Member -Force -InputObject $object -MemberType NoteProperty SqlInstance -value $server.DomainInstanceName

                        # Select all of the columns you'd like to show
                        Select-DefaultView -InputObject $object -Property ComputerName, InstanceName, SqlInstance, LogDate, ProcessInfo, Text
                    }
                }
                catch {
                    Stop-Function -Continue -Target $server -Message "Could not read from SQL Server Agent"
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaAgentOperator.ps1
function Get-DbaAgentOperator {
    <#
        .SYNOPSIS
            Returns all SQL Agent operators on a SQL Server Agent.

        .DESCRIPTION
            This function returns SQL Agent operators.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Operator
            The operator(s) to process - this list is auto-populated from the server. If unspecified, all operators will be processed.

        .PARAMETER ExcludeOperator
            The operator(s) to exclude - this list is auto-populated from the server

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Agent, Operator
            Author: Klaas Vandenberghe ( @PowerDBAKlaas )

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaAgentOperator

        .EXAMPLE
            Get-DbaAgentOperator -SqlInstance ServerA,ServerB\instanceB

            Returns any SQL Agent operators on serverA and serverB\instanceB

        .EXAMPLE
            'ServerA','ServerB\instanceB' | Get-DbaAgentOperator

            Returns all SQL Agent operators  on serverA and serverB\instanceB

        .EXAMPLE
            Get-DbaAgentOperator -SqlInstance ServerA -Operator Dba1,Dba2

            Returns only the SQL Agent Operators Dba1 and Dba2 on ServerA.

        .EXAMPLE
            Get-DbaAgentOperator -SqlInstance ServerA,ServerB -ExcludeOperator Dba3

            Returns all the SQL Agent operators on ServerA and ServerB, except the Dba3 operator.
    #>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]
        $SqlCredential,
        [object[]]$Operator,
        [object[]]$ExcludeOperator,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            Write-Message -Level Verbose -Message "Getting Edition from $server"
            Write-Message -Level Verbose -Message "$server is a $($server.Edition)"

            if ($server.Edition -like 'Express*') {
                Stop-Function -Message "There is no SQL Agent on $server, it's a $($server.Edition)" -Continue -Target $server
            }

            $defaults = "ComputerName", "SqlInstance", "InstanceName", "Name", "ID", "Enabled as IsEnabled", "EmailAddress", "LastEmail"

            if ($Operator) {
                $operators = $server.JobServer.Operators | Where-Object Name -In $Operator
            }
            elseif ($ExcludeOperator) {
                $operators = $server.JobServer.Operators | Where-Object Name -NotIn $ExcludeOperator
            }
            else {
                $operators = $server.JobServer.Operators
            }

            foreach ($operat in $operators) {

                $jobs = $server.JobServer.jobs | Where-Object { $_.OperatorToEmail, $_.OperatorToNetSend, $_.OperatorToPage -contains $operat.Name }
                $lastemail = [dbadatetime]$operat.LastEmailDate

                Add-Member -Force -InputObject $operat -MemberType NoteProperty -Name ComputerName -Value $server.ComputerName
                Add-Member -Force -InputObject $operat -MemberType NoteProperty -Name InstanceName -Value $server.ServiceName
                Add-Member -Force -InputObject $operat -MemberType NoteProperty -Name SqlInstance -Value $server.DomainInstanceName
                Add-Member -Force -InputObject $operat -MemberType NoteProperty -Name RelatedJobs -Value $jobs
                Add-Member -Force -InputObject $operat -MemberType NoteProperty -Name LastEmail -Value $lastemail
                Select-DefaultView -InputObject $operat -Property $defaults
            }
        }
    }
}
tools\dbatools\functions\Get-DbaAgentProxy.ps1
function Get-DbaAgentProxy {
    <#
        .SYNOPSIS
            Returns all SQL Agent proxies on a SQL Server Agent.

        .DESCRIPTION
            This function returns SQL Agent proxies.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Chrissy LeMaire ( @cl )
            Tags: Agent, SMO
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaAgentProxy

        .EXAMPLE
            Get-DbaAgentProxy -SqlInstance ServerA,ServerB\instanceB
            Returns all SQL Agent proxies on serverA and serverB\instanceB

        .EXAMPLE
            'serverA','serverB\instanceB' | Get-DbaAgentProxy
            Returns all SQL Agent proxies  on serverA and serverB\instanceB
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "Instance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            
            Write-Message -Level Verbose -Message "Getting Edition from $server"
            Write-Message -Level Verbose -Message "$server is a $($server.Edition)"
            
            if ($server.Edition -like 'Express*') {
                Stop-Function -Message "There is no SQL Agent on $server, it's a $($server.Edition)" -Continue
            }
            
            $defaults = "ComputerName", "SqlInstance", "InstanceName", "Name", "ID", "CredentialID", "CredentialIdentity", "CredentialName", "Description", "IsEnabled"
            
            $proxies = $server.Jobserver.ProxyAccounts
            
            foreach ($proxy in $proxies) {
                Add-Member -Force -InputObject $proxy -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                Add-Member -Force -InputObject $proxy -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                Add-Member -Force -InputObject $proxy -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                Select-DefaultView -InputObject $proxy -Property $defaults
            }
        }
    }
}
tools\dbatools\functions\Get-DbaAgentSchedule.ps1
function Get-DbaAgentSchedule {
    <#
        .SYNOPSIS
            Returns all SQL Agent Shared Schedules on a SQL Server Agent.

        .DESCRIPTION
            This function returns SQL Agent Shared Schedules.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Schedule
            Parameter to filter the schedules returned

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Agent, Schedule
            Author: Chris McKeown (@devopsfu), http://www.devopsfu.com

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaAgentSchedule

        .EXAMPLE
            Get-DbaAgentSchedule -SqlInstance localhost

            Returns all SQL Agent Shared Schedules on the local default SQL Server instance

        .EXAMPLE
            Get-DbaAgentSchedule -SqlInstance localhost, sql2016

            Returns all SQL Agent Shared Schedules for the local and sql2016 SQL Server instances
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "Instance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Schedules")]
        [object[]]$Schedule,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        function Get-ScheduleDescription {
            param (
                [Parameter(Mandatory = $true)]
                [ValidateNotNullOrEmpty()]
                [object]$Schedule

            )

            # Get the culture to make sure the right date and time format is displayed
            $datetimeFormat = (Get-culture).DateTimeFormat

            # Set the intial description
            $description = ""

            # Get the date and time values
            $startDate = Get-Date $Schedule.ActiveStartDate -format $datetimeFormat.ShortDatePattern
            $startTime = Get-Date ($Schedule.ActiveStartTimeOfDay.ToString()) -format $datetimeFormat.LongTimePattern
            $endDate = Get-Date $Schedule.ActiveEndDate -format $datetimeFormat.ShortDatePattern
            $endTime = Get-Date ($Schedule.ActiveEndTimeOfDay.ToString()) -format $datetimeFormat.LongTimePattern

            # Start setting the description based on the frequency type
            switch ($schedule.FrequencyTypes) {
                {($_ -eq 1) -or ($_ -eq "Once")} { $description += "Occurs on $startDate at $startTime" }
                {($_ -in 4, 8, 16, 32) -or ($_ -in "Daily", "Weekly", "Monthly")} { $description += "Occurs every "}
                {($_ -eq 64) -or ($_ -eq "AutoStart")} {$description += "Start automatically when SQL Server Agent starts "}
                {($_ -eq 128) -or ($_ -eq "OnIdle")} {$description += "Start whenever the CPUs become idle"}
            }

            # Check the frequency types for daily or weekly i.e.
            switch ($schedule.FrequencyTypes) {
                # Daily
                {$_ -in 4, "Daily"} {
                    if ($Schedule.FrequencyInterval -eq 1) {
                        $description += "day "
                    }
                    elseif ($Schedule.FrequencyInterval -gt 1) {
                        $description += "$($Schedule.FrequencyInterval) day(s) "
                    }
                }

                # Weekly
                {$_ -in 8, "Weekly"} {
                    # Check if it's for one or more weeks
                    if ($Schedule.FrequencyRecurrenceFactor -eq 1) {
                        $description += "week on "
                    }
                    elseif ($Schedule.FrequencyRecurrenceFactor -gt 1) {
                        $description += "$($Schedule.FrequencyRecurrenceFactor) week(s) on "
                    }

                    # Save the interval for the loop
                    $frequencyInterval = $Schedule.FrequencyInterval

                    # Create the array to hold the days
                    $days = ($false, $false, $false, $false, $false, $false, $false)

                    # Loop through the days
                    while ($frequencyInterval -gt 0) {

                        switch ($FrequenctInterval) {
                            {($frequencyInterval - 64) -ge 0} {
                                $days[5] = "Saturday"
                                $frequencyInterval -= 64
                            }
                            {($frequencyInterval - 32) -ge 0} {
                                $days[4] = "Friday"
                                $frequencyInterval -= 32
                            }
                            {($frequencyInterval - 16) -ge 0} {
                                $days[3] = "Thursday"
                                $frequencyInterval -= 16
                            }
                            {($frequencyInterval - 8) -ge 0} {
                                $days[2] = "Wednesday"
                                $frequencyInterval -= 8
                            }
                            {($frequencyInterval - 4) -ge 0} {
                                $days[1] = "Tuesday"
                                $frequencyInterval -= 4
                            }
                            {($frequencyInterval - 2) -ge 0} {
                                $days[0] = "Monday"
                                $frequencyInterval -= 2
                            }
                            {($frequencyInterval - 1) -ge 0} {
                                $days[6] = "Sunday"
                                $frequencyInterval -= 1
                            }
                        }

                    }

                    # Add the days to the description by selecting the days and exploding the array
                    $description += ($days | Where-Object {$_ -ne $false}) -join ", "
                    $description += " "

                }

                # Monthly
                {$_ -in 16, "Monthly"} {
                    # Check if it's for one or more months
                    if ($Schedule.FrequencyRecurrenceFactor -eq 1) {
                        $description += "month "
                    }
                    elseif ($Schedule.FrequencyRecurrenceFactor -gt 1) {
                        $description += "$($Schedule.FrequencyRecurrenceFactor) month(s) "
                    }

                    # Add the interval
                    $description += "on day $($Schedule.FrequencyInterval) of that month "
                }

                # Monthly relative
                {$_ -in 32, "MonthlyRelative"} {
                    # Check for the relative day
                    switch ($Schedule.FrequencyRelativeIntervals) {
                        {$_ -in 1, "First"} {$description += "first "}
                        {$_ -in 2, "Second"} {$description += "second "}
                        {$_ -in 4, "Third"} {$description += "third "}
                        {$_ -in 8, "Fourth"} {$description += "fourth "}
                        {$_ -in 16, "Last"} {$description += "last "}
                    }

                    # Get the relative day of the week
                    switch ($Schedule.FrequencyInterval) {
                        1 { $description += "Sunday "}
                        2 { $description += "Monday "}
                        3 { $description += "Tuesday "}
                        4 { $description += "Wednesday "}
                        5 { $description += "Thursday "}
                        6 { $description += "Friday "}
                        7 { $description += "Saturday "}
                        8 { $description += "Day "}
                        9 { $description += "Weekday "}
                        10 { $description += "Weekend day "}
                    }

                    $description += "of every $($Schedule.FrequencyRecurrenceFactor) month(s) "

                }
            }

            # Check the frequency type
            if ($schedule.FrequencyTypes -notin 64, 128) {

                # Check the subday types for minutes or hours i.e.
                if ($schedule.FrequencySubDayInterval -in 0, 1) {
                    $description += "at $startTime. "
                }
                else {

                    switch ($Schedule.FrequencySubDayTypes) {
                        {$_ -in 2, "Seconds"} { $description += "every $($schedule.FrequencySubDayInterval) second(s) "}
                        {$_ -in 4, "Minutes"} {$description += "every $($schedule.FrequencySubDayInterval) minute(s) " }
                        {$_ -in 8, "Hours"} { $description += "every $($schedule.FrequencySubDayInterval) hour(s) " }
                    }

                    $description += "between $startTime and $endTime. "
                }

                # Check if an end date has been given
                if ($Schedule.ActiveEndDate.Year -eq 9999) {
                    $description += "Schedule will be used starting on $startDate."
                }
                else {
                    $description += "Schedule will used between $startDate and $endDate."
                }
            }

            return $description
        }
    }

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($server.Edition -like 'Express*') {
                Stop-Function -Message "$($server.Edition) does not support SQL Server Agent. Skipping $server." -Continue
            }

            if ($Schedule) {
                $scheduleCollection = $server.JobServer.SharedSchedules | Where-Object { $_.Name -in $Schedule }
            }
            else {
                $scheduleCollection = $server.JobServer.SharedSchedules
            }

        }

        $defaults = "ComputerName", "InstanceName", "SqlInstance", "Name as ScheduleName", "ActiveEndDate", "ActiveEndTimeOfDay", "ActiveStartDate", "ActiveStartTimeOfDay", "DateCreated", "FrequencyInterval", "FrequencyRecurrenceFactor", "FrequencyRelativeIntervals", "FrequencySubDayInterval", "FrequencySubDayTypes", "FrequencyTypes", "IsEnabled", "JobCount", "Description"

        foreach ($schedule in $scheduleCollection) {
            $description = Get-ScheduleDescription -Schedule $schedule

            Add-Member -Force -InputObject $schedule -MemberType NoteProperty ComputerName -value $server.ComputerName
            Add-Member -Force -InputObject $schedule -MemberType NoteProperty InstanceName -value $server.ServiceName
            Add-Member -Force -InputObject $schedule -MemberType NoteProperty SqlInstance -value $server.DomainInstanceName
            Add-Member -Force -InputObject $schedule -MemberType NoteProperty Description -Value $description

            Select-DefaultView -InputObject $schedule -Property $defaults
        }

    }
}
tools\dbatools\functions\Get-DbaAgHadr.ps1
function Get-DbaAgHadr {
    <#
        .SYNOPSIS
            Gets the Hadr service setting on the specified SQL Server instance.

        .DESCRIPTION
            Gets the Hadr setting, from the service level, and returns true or false for the specified SQL Server instance.

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to.

        .PARAMETER SqlCredential
            Credential object used to connect to the SQL instance

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Hadr, AG, AvailabilityGroup
            Author: Shawn Melton (@wsmelton | http://blog.wsmelton.info)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaAgHadr

        .EXAMPLE
            Get-DbaAgHadr -SqlInstance sql2016

            Returns a status of the Hadr setting for sql2016 SQL Server instance.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            Add-Member -Force -InputObject $server -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
            Add-Member -Force -InputObject $server -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
            Add-Member -Force -InputObject $server -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName

            Select-DefaultView -InputObject $server -Property 'ComputerName', 'InstanceName', 'SqlInstance', 'IsHadrEnabled'
        }
    }
}
tools\dbatools\functions\Get-DbaAgListener.ps1
function Get-DbaAgListener {
    <#
        .SYNOPSIS
            Outputs the name of the Listener for the Availability Group(s) found on the server.

        .DESCRIPTION
            Default view provides most common set of properties for information on the database in an Availability Group(s).

            Information returned on the database will be specific to that replica, whether it is primary or a secondary.

            This command will return an SMO object, but it is the AvailabilityDatabases object  and not the Server.Databases object.

        .PARAMETER SqlInstance
            The SQL Server instance. Server version must be SQL Server version 2012 or higher.

        .PARAMETER SqlCredential
            Allows you to login to servers using SQL Logins instead of Windows Authentication (AKA Integrated or Trusted).

        .PARAMETER AvailabilityGroup
            Specify the Availability Group name that you want to get information on.

        .PARAMETER Listener
            Specify the Listener name that you want to get information on.

        .PARAMETER InputObject
            Piped in Availability Group objects

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: AG, AvailabilityGroup, Listener
            Author: Viorel Ciucu (@viorelciucu)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaAgListener

        .EXAMPLE
            Get-DbaAgListener -SqlInstance sqlserver2014a

            Returns basic information on the listener found on sqlserver2014a

        .EXAMPLE
            Get-DbaAgListener -SqlInstance sqlserver2014a -AvailabilityGroup AG-a

            Returns basic information on the listener found on sqlserver2014a in the Availability Group AG-a

    #>
    [CmdletBinding()]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(ValueFromPipeline = $true)]
        [string[]]$AvailabilityGroup,
        [string[]]$Listener,
        [object[]]$InputObject,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            $InputObject += Get-DbaAvailabilityGroup -SqlInstance $instance -SqlCredential $SqlCredential -AvailabilityGroup $AvailabilityGroup
        }
        if (Test-Bound -ParameterName Listener) {
            $InputObject = $InputObject | Where-Object { $_.AvailabilityGroupListeners.Name -contains $Listener }
        }

        $defaults = 'ComputerName', 'InstanceName', 'SqlInstance', 'AvailabilityGroup', 'Name', 'PortNumber', 'ClusterIPConfiguration'
        foreach ($aglistener in $InputObject.AvailabilityGroupListeners) {
            $server = $aglistener.Parent.Parent
            Add-Member -Force -InputObject $aglistener -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
            Add-Member -Force -InputObject $aglistener -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
            Add-Member -Force -InputObject $aglistener -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
            Add-Member -Force -InputObject $aglistener -MemberType NoteProperty -Name AvailabilityGroup -value $aglistener.Parent.Name
            Select-DefaultView -InputObject $aglistener -Property $defaults
        }
    }
}
tools\dbatools\functions\Get-DbaAgReplica.ps1
function Get-DbaAgReplica {
    <#
        .SYNOPSIS
            Outputs the Availability Group(s)' Replica object found on the server.

        .DESCRIPTION
            Default view provides most common set of properties for information on the Availability Group(s)' Replica.

        .PARAMETER SqlInstance
            The SQL Server instance. Server version must be SQL Server version 2012 or higher.

        .PARAMETER SqlCredential
            Allows you to login to servers using SQL Logins as opposed to Windows Auth/Integrated/Trusted.

        .PARAMETER AvailabilityGroup
            Specify the Availability Group name that you want to get information on.

        .PARAMETER Replica
            Specify the replica to pull information on, is dependent up name that you want to get information on.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: AG, AvailabilityGroup, Replica
            Author: Shawn Melton (@wsmelton) | Chrissy LeMaire (@ctrlb)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaAgReplica

        .EXAMPLE
            Get-DbaAgReplica -SqlInstance sqlserver2014a

            Returns basic information on all the Availability Group(s) replica(s) found on sqlserver2014a

        .EXAMPLE
            Get-DbaAgReplica -SqlInstance sqlserver2014a -AvailabilityGroup AG-a

            Shows basic information on the replica(s) found on Availability Group AG-a on sqlserver2014a

        .EXAMPLE
            Get-DbaAgReplica -SqlInstance sqlserver2014a | Select *

            Returns full object properties on all Availability Group(s) replica(s) on sqlserver2014a
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(ValueFromPipeline = $true)]
        [object[]]$AvailabilityGroup,
        [object[]]$Replica,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($serverName in $SqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $serverName -SqlCredential $SqlCredential -MinimumVersion 11
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($server.IsHadrEnabled -eq $false) {
                Stop-Function -Message "Availability Group (HADR) is not configured for the instance: $serverName" -Target $serverName -Continue
            }

            $ags = $server.AvailabilityGroups
            if ($AvailabilityGroup) {
                $ags = $ags | Where-Object Name -in $AvailabilityGroup
            }

            foreach ($ag in $ags) {
                $replicas = $ag.AvailabilityReplicas
                foreach ($currentReplica in $replicas) {
                    if ($Replica -and $currentReplica.Name -notmatch $Replica) {
                        continue
                    }

                    Add-Member -Force -InputObject $currentReplica -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                    Add-Member -Force -InputObject $currentReplica -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                    Add-Member -Force -InputObject $currentReplica -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName

                    $defaults = 'ComputerName', 'InstanceName', 'SqlInstance', 'Parent as AvailabilityGroup', 'Name as Replica', 'Role', 'ConnectionState', 'RollupSynchronizationState', 'AvailabilityMode', 'BackupPriority', 'EndpointUrl', 'SessionTimeout', 'FailoverMode', 'ReadonlyRoutingList'
                    Select-DefaultView -InputObject $currentReplica -Property $defaults
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaAvailabilityGroup.ps1
function Get-DbaAvailabilityGroup {
    <#
        .SYNOPSIS
            Outputs the Availability Group(s) object found on the server.

        .DESCRIPTION
            Default view provides most common set of properties for information on the Availability Group(s).

        .PARAMETER SqlInstance
            The SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2012 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER AvailabilityGroup
            Specifies the Availability Group name that you want to get information on.

        .PARAMETER IsPrimary
            If this switch is enabled, a boolean indicating whether SqlInstance is the Primary replica in the AG is returned.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Hadr, AG, AvailabilityGroup
            Author: Shawn Melton (@wsmelton) | Chrissy LeMaire (@ctrlb)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaAvailabilityGroup

        .EXAMPLE
            Get-DbaAvailabilityGroup -SqlInstance sqlserver2014a

            Returns basic information on all the Availability Group(s) found on sqlserver2014a.

        .EXAMPLE
            Get-DbaAvailabilityGroup -SqlInstance sqlserver2014a -AvailabilityGroup AG-a

            Shows basic information on the Availability Group AG-a on sqlserver2014a.

        .EXAMPLE
            Get-DbaAvailabilityGroup -SqlInstance sqlserver2014a | Select *

            Returns full object properties on all Availability Group(s) on sqlserver2014a.

        .EXAMPLE
            Get-DbaAvailabilityGroup -SqlInstance sqlserver2014a -AvailabilityGroup AG-a -IsPrimary

            Returns true/false if the server, sqlserver2014a, is the primary replica for AG-a Availability Group.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$AvailabilityGroup,
        [switch]$IsPrimary,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($serverName in $SqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $serverName -SqlCredential $SqlCredential -MinimumVersion 11
            }
            catch {
                Stop-Function -Message "Failure." -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($server.IsHadrEnabled -eq $false) {
                Stop-Function -Message "Availability Group (HADR) is not configured for the instance: $serverName." -Target $serverName -Continue
            }

            $ags = $server.AvailabilityGroups
            if ($AvailabilityGroup) {
                $ags = $ags | Where-Object Name -in $AvailabilityGroup
            }

            foreach ($ag in $ags) {
                Add-Member -Force -InputObject $ag -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                Add-Member -Force -InputObject $ag -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                Add-Member -Force -InputObject $ag -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName

                if ($IsPrimary) {
                    $defaults = 'ComputerName', 'InstanceName', 'SqlInstance', 'Name as AvailabilityGroup', 'IsPrimary'
                    $value = $false
                    if ($ag.PrimaryReplicaServerName -eq $server.Name) {
                        $value = $true
                    }
                    Add-Member -Force -InputObject $ag -MemberType NoteProperty -Name IsPrimary -Value $value
                    Select-DefaultView -InputObject $ag -Property $defaults
                }
                else {
                    $defaults = 'ComputerName', 'InstanceName', 'SqlInstance', 'LocalReplicaRole', 'Name as AvailabilityGroup', 'PrimaryReplicaServerName as PrimaryReplica', 'AutomatedBackupPreference', 'AvailabilityReplicas', 'AvailabilityDatabases', 'AvailabilityGroupListeners'
                    Select-DefaultView -InputObject $ag -Property $defaults
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaAvailableCollation.ps1
function Get-DbaAvailableCollation {
    <#
        .SYNOPSIS
            Function to get available collations for a given SQL Server

        .DESCRIPTION
            The Get-DbaAvailableCollation function returns the list of collations available on each SQL Server.
            Only the connect permission is required to get this information.

        .PARAMETER SqlInstance
            The SQL Server instance, or instances. Only connect permission is required.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Collation, Configuration
            Author: Bryan Hamby (@galador)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaAvailableCollation

        .EXAMPLE
            Get-DbaAvailableCollation -SqlInstance sql2016

            Gets all the collations from server sql2016 using NT authentication
    #>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        #Functions to get/cache the code page and language description.
        #It runs about 9x faster caching these (2 vs 18 seconds) in my test,
        #since there are so many duplicates

        #No longer supported by Windows, but still shows up in SQL Server
        #http://www.databaseteam.org/1-ms-sql-server/982faddda7a789a1.htm
        $locales = @{66577 = "Japanese_Unicode"}
        $codePages = @{}

        function Get-LocaleDescription ($LocaleId) {
            if ($locales.ContainsKey($LocaleId)) {
                $localeName = $locales.Get_Item($LocaleId)
            }
            else {
                try {
                    $localeName = (Get-Language $LocaleId).DisplayName
                }
                catch {
                    $localeName = $null
                }
                $locales.Set_Item($LocaleId, $localeName)
            }
            return $localeName
        }

        function Get-CodePageDescription ($codePageId) {
            if ($codePages.ContainsKey($codePageId)) {
                $codePageName = $codePages.Get_Item($codePageId)
            }
            else {
                try {
                    $codePageName = (Get-CodePage $codePageId).EncodingName
                }
                catch {
                    $codePageName = $null
                }
                $codePages.Set_Item($codePageId, $codePageName)
            }
            return $codePageName
        }
    }

    process {
        foreach ($Instance in $sqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $availableCollations = $server.EnumCollations()
            foreach ($collation in $availableCollations) {
                Add-Member -Force -InputObject $collation -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                Add-Member -Force -InputObject $collation -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                Add-Member -Force -InputObject $collation -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                Add-Member -Force -InputObject $collation -MemberType NoteProperty -Name CodePageName -Value (Get-CodePageDescription $collation.CodePage)
                Add-Member -Force -InputObject $collation -MemberType NoteProperty -Name LocaleName -Value (Get-LocaleDescription $collation.LocaleID)
            }

            Select-DefaultView -InputObject $availableCollations -Property ComputerName, InstanceName, SqlInstance, Name, CodePage, CodePageName, LocaleID, LocaleName, Description
        }
    }
}
tools\dbatools\functions\Get-DbaBackupDevice.ps1
#ValidationTags#Messaging#
function Get-DbaBackupDevice {
    <#
        .SYNOPSIS
            Gets SQL Backup Device information for each instance(s) of SQL Server.

        .DESCRIPTION
            The Get-DbaBackupDevice command gets SQL Backup Device information for each instance(s) of SQL Server.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function
            to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Backup
            Author: Garry Bargsley (@gbargsley), http://blog.garrybargsley.com

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaBackupDevice

        .EXAMPLE
            Get-DbaBackupDevice -SqlInstance localhost

            Returns all Backup Devices on the local default SQL Server instance

        .EXAMPLE
            Get-DbaBackupDevice -SqlInstance localhost, sql2016

            Returns all Backup Devices for the local and sql2016 SQL Server instances
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            foreach ($backupDevice in $server.BackupDevices) {
                Add-Member -Force -InputObject $backupDevice -MemberType NoteProperty -Name ComputerName -value $backupDevice.Parent.ComputerName
                Add-Member -Force -InputObject $backupDevice -MemberType NoteProperty -Name InstanceName -value $backupDevice.Parent.ServiceName
                Add-Member -Force -InputObject $backupDevice -MemberType NoteProperty -Name SqlInstance -value $backupDevice.Parent.DomainInstanceName

                Select-DefaultView -InputObject $backupDevice -Property ComputerName, InstanceName, SqlInstance, Name, BackupDeviceType, PhysicalLocation, SkipTapeLabel
            }
        }
    }
}
tools\dbatools\functions\Get-DbaBackupHistory.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaBackupHistory {
    <#
        .SYNOPSIS
            Returns backup history details for databases on a SQL Server.

        .DESCRIPTION
            Returns backup history details for some or all databases on a SQL Server.

            You can even get detailed information (including file path) for latest full, differential and log files.

            Backups taken with the CopyOnly option will NOT be returned, unless the IncludeCopyOnly switch is present

            Reference: http://www.sqlhub.com/2011/07/find-your-backup-history-in-sql-server.html

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Credential object used to connect to the SQL Server Instance as a different user. This can be a Windows or SQL Server account. Windows users are determined by the existence of a backslash, so if you are intending to use an alternative Windows connection instead of a SQL login, ensure it contains a backslash.

        .PARAMETER Database
            Specifies one or more database(s) to process. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies one or more database(s) to exclude from processing.

        .PARAMETER IncludeCopyOnly
            By default Get-DbaBackupHistory will ignore backups taken with the CopyOnly option. This switch will include them

        .PARAMETER Force
            If this switch is enabled, a large amount of information is returned, similar to what SQL Server itself returns.

        .PARAMETER Since
            Specifies a DateTime object to use as the starting point for the search for backups.

        .PARAMETER Last
            If this switch is enabled, the most recent full chain of full, diff and log backup sets is returned.

        .PARAMETER LastFull
            If this switch is enabled, the most recent full backup set is returned.

        .PARAMETER LastDiff
            If this switch is enabled, the most recent differential backup set is returned.

        .PARAMETER LastLog
            If this switch is enabled, the most recent log backup is returned.

        .PARAMETER DeviceType
            Specifies a filter for backup sets based on DeviceTypes. Valid options are 'Disk','Permanent Disk Device', 'Tape', 'Permanent Tape Device','Pipe','Permanent Pipe Device','Virtual Device', in addition to custom integers for your own DeviceTypes.

        .PARAMETER Raw
            If this switch is enabled, one object per backup file is returned. Otherwise, media sets (striped backups across multiple files) will be grouped into a single return object.

        .PARAMETER Type
            Specifies one or more types of backups to return. Valid options are 'Full', 'Log', 'Differential', 'File', 'Differential File', 'Partial Full', and 'Partial Differential'. Otherwise, all types of backups will be returned unless one of the -Last* switches is enabled.

        .PARAMETER LastLsn
            Specifies a minimum LSN to use in filtering backup history. Only backups with an LSN greater than this value will be returned, which helps speed the retrieval process.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: DisasterRecovery, Backup
            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaBackupHistory

        .EXAMPLE
            Get-DbaBackupHistory -SqlInstance SqlInstance2014a

            Returns server name, database, username, backup type, date for all backups databases on SqlInstance2014a. This may return many rows; consider using filters that are included in other examples.

        .EXAMPLE
            $cred = Get-Credential sqladmin
            Get-DbaBackupHistory -SqlInstance SqlInstance2014a -SqlCredential $cred

            Does the same as above but logs in as SQL user "sqladmin"

        .EXAMPLE
            Get-DbaBackupHistory -SqlInstance SqlInstance2014a -Database db1, db2 -Since '7/1/2016 10:47:00'

            Returns backup information only for databases db1 and db2 on SqlInstance2014a since July 1, 2016 at 10:47 AM.

        .EXAMPLE
            Get-DbaBackupHistory -SqlInstance sql2014 -Database AdventureWorks2014, pubs -Force | Format-Table

            Returns information only for AdventureWorks2014 and pubs and formats the results as a table.

        .EXAMPLE
            Get-DbaBackupHistory -SqlInstance sql2014 -Database AdventureWorks2014 -Last

            Returns information about the most recent full, differential and log backups for AdventureWorks2014 on sql2014.

        .EXAMPLE
            Get-DbaBackupHistory -SqlInstance sql2014 -Database AdventureWorks2014 -Last -DeviceType Disk

            Returns information about the most recent full, differential and log backups for AdventureWorks2014 on sql2014, but only for backups to disk.

        .EXAMPLE
            Get-DbaBackupHistory -SqlInstance sql2014 -Database AdventureWorks2014 -Last -DeviceType 148,107

            Returns information about the most recent full, differential and log backups for AdventureWorks2014 on sql2014, but only for backups with device_type 148 and 107.

        .EXAMPLE
            Get-DbaBackupHistory -SqlInstance sql2014 -Database AdventureWorks2014 -LastFull

            Returns information about the most recent full backup for AdventureWorks2014 on sql2014.

        .EXAMPLE
            Get-DbaBackupHistory -SqlInstance sql2014 -Database AdventureWorks2014 -Type Full

            Returns information about all Full backups for AdventureWorks2014 on sql2014.

        .EXAMPLE
            Get-DbaRegisteredServer -SqlInstance sql2016 | Get-DbaBackupHistory

            Returns database backup information for every database on every server listed in the Central Management Server on sql2016.

        .EXAMPLE
            Get-DbaBackupHistory -SqlInstance SqlInstance2014a, sql2016 -Force

            Returns detailed backup history for all databases on SqlInstance2014a and sql2016.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]
        $SqlInstance,
        [Alias("Credential")]
        [PsCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$IncludeCopyOnly,
        [Parameter(ParameterSetName = "NoLast")]
        [switch]$Force,
        [Parameter(ParameterSetName = "NoLast")]
        [DateTime]$Since,
        [Parameter(ParameterSetName = "Last")]
        [switch]$Last,
        [Parameter(ParameterSetName = "Last")]
        [switch]$LastFull,
        [Parameter(ParameterSetName = "Last")]
        [switch]$LastDiff,
        [Parameter(ParameterSetName = "Last")]
        [switch]$LastLog,
        [string[]]$DeviceType,
        [switch]$Raw,
        [bigint]$LastLsn,
        [ValidateSet("Full", "Log", "Differential", "File", "Differential File", "Partial Full", "Partial Differential")]
        [string[]]$Type,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Write-Message -Level System -Message "Active Parameter set: $($PSCmdlet.ParameterSetName)."
        Write-Message -Level System -Message "Bound parameters: $($PSBoundParameters.Keys -join ", ")"

        $deviceTypeMapping = @{
            'Disk'                  = 2
            'Permanent Disk Device' = 102
            'Tape'                  = 5
            'Permanent Tape Device' = 105
            'Pipe'                  = 6
            'Permanent Pipe Device' = 106
            'Virtual Device'        = 7
            'URL'                   = 9
        }
        $deviceTypeFilter = @()
        foreach ($devType in $DeviceType) {
            if ($devType -in $deviceTypeMapping.Keys) {
                $deviceTypeFilter += $deviceTypeMapping[$devType]
            }
            else {
                $deviceTypeFilter += $devType
            }
        }
        $backupTypeMapping = @{
            'Log'                  = 'L'
            'Full'                 = 'D'
            'File'                 = 'F'
            'Differential'         = 'I'
            'Differential File'    = 'G'
            'Partial Full'         = 'P'
            'Partial Differential' = 'Q'
        }
        $backupTypeFilter = @()
        foreach ($typeFilter in $Type) {
            $backupTypeFilter += $backupTypeMapping[$typeFilter]
        }

    }

    process {
        foreach ($instance in $SqlInstance) {

            try {
                Write-Message -Level Verbose -Message "Connecting to $instance." -Target $instance
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($server.VersionMajor -ge 10) {
                $compressedFlag = $true
                # 2008 introduced compressed_backup_size
                $backupCols = "
                backupset.backup_size AS TotalSize,
                backupset.compressed_backup_size as CompressedBackupSize"
            }
            else {
                $compressedFlag = $false
                $backupCols = "
                backupset.backup_size AS TotalSize,
                NULL as CompressedBackupSize"
            }

            $databases = @()
            if ($null -ne $Database) {
                foreach ($db in $Database) {
                    $databases += [PSCustomObject]@{name = $db}
                }
            }
            else {
                $databases = $server.Databases
            }
            if ($ExcludeDatabase) {
                $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }
            foreach ($d in $deviceTypeFilter) {
                $deviceTypeFilterRight = "IN ('" + ($deviceTypeFilter -Join "','") + "')"
            }

            foreach ($b in $backupTypeFilter) {
                $backupTypeFilterRight = "IN ('" + ($backupTypeFilter -Join "','") + "')"
            }

            if ($last) {
                foreach ($db in $databases) {

                    #Get the full and build upwards
                    $allBackups = @()
                    $allBackups += $fullDb = Get-DbaBackupHistory -SqlInstance $server -Database $db.Name -LastFull -raw:$Raw -DeviceType $DeviceType -IncludeCopyOnly:$IncludeCopyOnly
                    $diffDb = Get-DbaBackupHistory -SqlInstance $server -Database $db.Name -LastDiff -raw:$Raw -DeviceType $DeviceType -IncludeCopyOnly:$IncludeCopyOnly
                    if ($diffDb.LastLsn -gt $fullDb.LastLsn -and $diffDb.DatabaseBackupLSN -eq $fullDb.CheckPointLSN ) {
                        Write-Message -Level Verbose -Message "Valid Differential backup "
                        $allBackups += $diffDb
                        $tlogStartDsn = ($diffDb.FirstLsn -as [bigint])
                    }
                    else {
                        Write-Message -Level Verbose -Message "No Diff found"
                        try {
                            [bigint]$tlogStartDsn = $fullDb.FirstLsn.ToString()
                        }
                        catch {
                            continue
                        }
                    }
                    $allBackups += Get-DbaBackupHistory -SqlInstance $server -Database $db.Name -raw:$raw -DeviceType $DeviceType -LastLsn $tlogStartDsn -IncludeCopyOnly:$IncludeCopyOnly | Where-Object {
                        $_.Type -eq 'Log' -and [bigint]$_.LastLsn -gt [bigint]$tlogStartDsn -and [bigint]$_.DatabaseBackupLSN -eq [bigint]$fullDb.CheckPointLSN -and $_.LastRecoveryForkGuid -eq $fullDb.LastRecoveryForkGuid
                    }
                    #This line does the output for -Last!!!
                    $allBackups |  Sort-Object -Property LastLsn, Type
                }
                continue
            }

            if ($LastFull -or $LastDiff -or $LastLog) {
                if ($LastFull) {
                    $first = 'D'; $second = 'P'
                }
                if ($LastDiff) {
                    $first = 'I'; $second = 'Q'
                }
                if ($LastLog) {
                    $first = 'L'; $second = 'L'
                }
                $databases = $databases | Select-Object -Unique -Property Name
                $sql = ""
                foreach ($db in $databases) {
                    Write-Message -Level Verbose -Message "Processing $($db.name)" -Target $db
                    $whereCopyOnly = $null
                    if ($true -ne $IncludeCopyOnly) {
                        $whereCopyOnly = " AND is_copy_only='0' "
                    }
                    if ($deviceTypeFilter) {
                        $devTypeFilterWhere = "AND mediafamily.device_type $deviceTypeFilterRight"
                    }
                    # recap for future editors (as this has been discussed over and over):
                    #   - original editors (from hereon referred as "we") rank over backupset.last_lsn desc, backupset.backup_finish_date desc for a good reason: DST
                    #     all times are recorded with the timezone of the server
                    #   - we thought about ranking over backupset.backup_set_id desc, backupset.last_lsn desc, backupset.backup_finish_date desc
                    #     but there is no explicit documentation about "when" a row gets inserted into backupset. Theoretically it _could_
                    #     happen that backup_set_id for the same database has not the same order of last_lsn.
                    #   - given ultimately to restore something lsn IS the source of truth, we decided to trust that and only that
                    #   - we know that sometimes it happens to drop a database without deleting the history. Assuming then to create a database with the same name,
                    #     and given the lsn are composed in the first part by the VLF SeqID, it happens seldomly that for the same database_name backupset holds
                    #     last_lsn out of order. To avoid this behaviour, we filter by database_guid choosing the guid that has MAX(backup_finish_date), as we know
                    #     last_lsn cannot be out-of-order for the same database, and the same database cannot have different database_guid
                    $sql += "
                                SELECT
                                    a.BackupSetRank,
                                    a.Server,
                                    a.[Database],
                                    a.Username,
                                    a.Start,
                                    a.[End],
                                    a.Duration,
                                    a.[Path],
                                    a.Type,
                                    a.TotalSize,
                                    a.CompressedBackupSize,
                                    a.MediaSetId,
                                    a.BackupSetID,
                                    a.Software,
                                    a.position,
                                    a.first_lsn,
                                    a.database_backup_lsn,
                                    a.checkpoint_lsn,
                                    a.last_lsn,
                                    a.first_lsn as 'FirstLSN',
                                    a.database_backup_lsn as 'DatabaseBackupLsn',
                                    a.checkpoint_lsn as 'CheckpointLsn',
                                    a.last_lsn as 'LastLsn',
                                    a.software_major_version,
                                    a.DeviceType,
                                    a.is_copy_only,
                                    a.last_recovery_fork_guid
                                FROM (SELECT
                                  RANK() OVER (ORDER BY backupset.last_lsn desc, backupset.backup_finish_date DESC) AS 'BackupSetRank',
                                  backupset.database_name AS [Database],
                                  backupset.user_name AS Username,
                                  backupset.backup_start_date AS Start,
                                  backupset.server_name as [Server],
                                  backupset.backup_finish_date AS [End],
                                  DATEDIFF(SECOND, backupset.backup_start_date, backupset.backup_finish_date) AS Duration,
                                  mediafamily.physical_device_name AS Path,
                                  $backupCols,
                                  CASE backupset.type
                                    WHEN 'L' THEN 'Log'
                                    WHEN 'D' THEN 'Full'
                                    WHEN 'F' THEN 'File'
                                    WHEN 'I' THEN 'Differential'
                                    WHEN 'G' THEN 'Differential File'
                                    WHEN 'P' THEN 'Partial Full'
                                    WHEN 'Q' THEN 'Partial Differential'
                                    ELSE NULL
                                  END AS Type,
                                  backupset.media_set_id AS MediaSetId,
                                  mediafamily.media_family_id as mediafamilyid,
                                  backupset.backup_set_id as BackupSetID,
                                  CASE mediafamily.device_type
                                    WHEN 2 THEN 'Disk'
                                    WHEN 102 THEN 'Permanent Disk Device'
                                    WHEN 5 THEN 'Tape'
                                    WHEN 105 THEN 'Permanent Tape Device'
                                    WHEN 6 THEN 'Pipe'
                                    WHEN 106 THEN 'Permanent Pipe Device'
                                    WHEN 7 THEN 'Virtual Device'
                                    WHEN 9 THEN 'URL'
                                    ELSE 'Unknown'
                                    END AS DeviceType,
                                  backupset.position,
                                  backupset.first_lsn,
                                  backupset.database_backup_lsn,
                                  backupset.checkpoint_lsn,
                                  backupset.last_lsn,
                                  backupset.software_major_version,
                                  mediaset.software_name AS Software,
                                  backupset.is_copy_only,
                                  backupset.last_recovery_fork_guid,
                                  backupset.recovery_model
                                FROM msdb..backupmediafamily AS mediafamily
                                JOIN msdb..backupmediaset AS mediaset
                                  ON mediafamily.media_set_id = mediaset.media_set_id
                                JOIN msdb..backupset AS backupset
                                  ON backupset.media_set_id = mediaset.media_set_id
                                JOIN (
                                    SELECT DISTINCT database_guid, database_name, backup_finish_date
                                    FROM msdb..backupset
                                    WHERE backupset.database_name = '$($db.Name)'
                                ) dbguid
                                  ON dbguid.database_name = backupset.database_name
                                  AND dbguid.database_guid = backupset.database_guid
                                JOIN (
                                    SELECT database_name, MAX(backup_finish_date) max_finish_date
                                    FROM msdb..backupset
                                    WHERE backupset.database_name = '$($db.Name)'
                                    GROUP BY database_name
                                ) dbguid_support
                                  ON dbguid_support.database_name = backupset.database_name
                                  AND dbguid.backup_finish_date = dbguid_support.max_finish_date
                                WHERE backupset.database_name = '$($db.Name)' $whereCopyOnly
                                AND (type = '$first' OR type = '$second')
                                $devTypeFilterWhere
                                ) AS a
                                WHERE a.BackupSetRank = 1
                                ORDER BY a.Type;
                                "
                }
                $sql = $sql -join "; "
            }
            else {
                if ($Force -eq $true) {
                    $select = "SELECT * "
                }
                else {
                    $select = "
                            SELECT
                              backupset.database_name AS [Database],
                              backupset.user_name AS Username,
                              backupset.server_name as [server],
                              backupset.backup_start_date AS [Start],
                              backupset.backup_finish_date AS [End],
                              DATEDIFF(SECOND, backupset.backup_start_date, backupset.backup_finish_date) AS Duration,
                              mediafamily.physical_device_name AS Path,
                              $backupCols,
                              CASE backupset.type
                                WHEN 'L' THEN 'Log'
                                WHEN 'D' THEN 'Full'
                                WHEN 'F' THEN 'File'
                                WHEN 'I' THEN 'Differential'
                                WHEN 'G' THEN 'Differential File'
                                WHEN 'P' THEN 'Partial Full'
                                WHEN 'Q' THEN 'Partial Differential'
                                ELSE NULL
                              END AS Type,
                              backupset.media_set_id AS MediaSetId,
                              mediafamily.media_family_id as MediaFamilyId,
                              backupset.backup_set_id as BackupSetId,
                              CASE mediafamily.device_type
                                WHEN 2 THEN 'Disk'
                                WHEN 102 THEN 'Permanent Disk Device'
                                WHEN 5 THEN 'Tape'
                                WHEN 105 THEN 'Permanent Tape Device'
                                WHEN 6 THEN 'Pipe'
                                WHEN 106 THEN 'Permanent Pipe Device'
                                WHEN 7 THEN 'Virtual Device'
                                WHEN 9 THEN 'URL'
                                ELSE 'Unknown'
                              END AS DeviceType,
                              backupset.position,
                              backupset.first_lsn,
                              backupset.database_backup_lsn,
                              backupset.checkpoint_lsn,
                              backupset.last_lsn,
                              backupset.first_lsn as 'FirstLSN',
                              backupset.database_backup_lsn as 'DatabaseBackupLsn',
                              backupset.checkpoint_lsn as 'CheckpointLsn',
                              backupset.last_lsn as 'LastLsn',
                              backupset.software_major_version,
                              mediaset.software_name AS Software,
                              backupset.is_copy_only,
                              backupset.last_recovery_fork_guid,
                              backupset.recovery_model"
                }

                $from = " FROM msdb..backupmediafamily mediafamily
                             INNER JOIN msdb..backupmediaset mediaset ON mediafamily.media_set_id = mediaset.media_set_id
                             INNER JOIN msdb..backupset backupset ON backupset.media_set_id = mediaset.media_set_id"
                if ($Database -or $Since -or $Last -or $LastFull -or $LastLog -or $LastDiff -or $deviceTypeFilter -or $LastLsn -or $backupTypeFilter) {
                    $where = " WHERE "
                }

                $whereArray = @()

                if ($Database.length -gt 0) {
                    $dbList = $Database -join "','"
                    $whereArray += "database_name IN ('$dbList')"
                }

                if ($true -ne $IncludeCopyOnly) {
                    $whereArray += "is_copy_only='0'"
                }

                if ($Last -or $LastFull -or $LastLog -or $LastDiff) {
                    $tempWhere = $whereArray -join " AND "
                    $whereArray += "type = 'Full' AND mediaset.media_set_id = (SELECT TOP 1 mediaset.media_set_id $from $tempWhere ORDER BY backupset.last_lsn DESC)"
                }

                if ($null -ne $Since) {
                    $whereArray += "backupset.backup_finish_date >= '$($Since.ToString("yyyy-MM-ddTHH:mm:ss"))'"
                }

                if ($deviceTypeFilter) {
                    $whereArray += "mediafamily.device_type $deviceTypeFilterRight"
                }
                if ($backupTypeFilter) {
                    $whereArray += "backupset.type $backupTypeFilterRight"
                }

                if ($LastLsn) {
                    $whereArray += "backupset.last_lsn > $LastLsn"
                }
                if ($where.Length -gt 0) {
                    $whereArray = $whereArray -join " AND "
                    $where = "$where $whereArray"
                }

                $sql = "$select $from $where ORDER BY backupset.last_lsn DESC"
            }

            Write-Message -Level Debug -Message "SQL Statement: `n$sql"
            Write-Message -Level SomewhatVerbose -Message "Executing sql query."
            $results = $server.ConnectionContext.ExecuteWithResults($sql).Tables.Rows | Select-Object * -ExcludeProperty BackupSetRank, RowError, RowState, Table, ItemArray, HasErrors

            if ($raw) {
                Write-Message -Level SomewhatVerbose -Message "Processing as Raw Output."
                $results | Select-Object *, @{ Name = "FullName"; Expression = { $_.Path } }
                Write-Message -Level SomewhatVerbose -Message "$($results.Count) result sets found."
            }
            else {
                Write-Message -Level SomewhatVerbose -Message "Processing as grouped output."
                $groupedResults = $results | Group-Object -Property BackupsetId
                Write-Message -Level SomewhatVerbose -Message "$($groupedResults.Count) result-groups found."
                $groupResults = @()
                $backupSetIds = $groupedResults.Name
                $backupSetIdsList = $backupSetIds -Join ","
                if ($groupedResults.Count -gt 0) {
                    $backupSetIdsWhere = "backup_set_id IN ($backupSetIdsList)"
                    $fileAllSql = "SELECT backup_set_id, file_type as FileType, logical_name as LogicalName, physical_name as PhysicalName
                                   FROM msdb..backupfile WHERE $backupSetIdsWhere"
                    Write-Message -Level Debug -Message "FileSQL: $fileAllSql"
                    $fileListResults = $server.Query($fileAllSql)
                }
                else {
                    $fileListResults = @()
                }
                $fileListHash = @{}
                foreach ($fl in $fileListResults) {
                    if (-not($fileListHash.ContainsKey($fl.backup_set_id))) {
                        $fileListHash[$fl.backup_set_id] = @()
                    }
                    $fileListHash[$fl.backup_set_id] += $fl
                }
                foreach ($group in $groupedResults) {
                    $commonFields = $group.Group[0]
                    $groupLength = $group.Group.Count
                    if ($groupLength -eq 1) {
                        $start = $commonFields.Start
                        $end = $commonFields.End
                        $duration = New-TimeSpan -Seconds $commonFields.Duration
                    }
                    else {
                        $start = ($group.Group.Start | Measure-Object -Minimum).Minimum
                        $end = ($group.Group.End | Measure-Object -Maximum).Maximum
                        $duration = New-TimeSpan -Seconds ($group.Group.Duration | Measure-Object -Maximum).Maximum
                    }
                    $compressedBackupSize = $commonFields.CompressedBackupSize
                    if ($compressedFlag -eq $true) {
                        $ratio = [Math]::Round(($commonFields.TotalSize) / ($compressedBackupSize), 2)
                    }
                    else {
                        $compressedBackupSize = $null
                        $ratio = 1
                    }
                    $historyObject = New-Object Sqlcollaborative.Dbatools.Database.BackupHistory
                    $historyObject.ComputerName = $server.ComputerName
                    $historyObject.InstanceName = $server.ServiceName
                    $historyObject.SqlInstance = $server.DomainInstanceName
                    $historyObject.Database = $commonFields.Database
                    $historyObject.UserName = $commonFields.UserName
                    $historyObject.Start = $start
                    $historyObject.End = $end
                    $historyObject.Duration = $duration
                    $historyObject.Path = $group.Group.Path
                    $historyObject.TotalSize = $commonFields.TotalSize
                    $historyObject.CompressedBackupSize = $compressedBackupSize
                    $historyObject.CompressionRatio = $ratio
                    $historyObject.Type = $commonFields.Type
                    $historyObject.BackupSetId = $commonFields.BackupSetId
                    $historyObject.DeviceType = $commonFields.DeviceType
                    $historyObject.Software = $commonFields.Software
                    $historyObject.FullName = $group.Group.Path
                    $historyObject.FileList = $fileListHash[$commonFields.BackupSetID] | Select-Object FileType, LogicalName, PhysicalName
                    $historyObject.Position = $commonFields.Position
                    $historyObject.FirstLsn = $commonFields.First_LSN
                    $historyObject.DatabaseBackupLsn = $commonFields.database_backup_lsn
                    $historyObject.CheckpointLsn = $commonFields.checkpoint_lsn
                    $historyObject.LastLsn = $commonFields.Last_Lsn
                    $historyObject.SoftwareVersionMajor = $commonFields.Software_Major_Version
                    $historyObject.IsCopyOnly = ($commonFields.is_copy_only -eq 1)
                    $historyObject.LastRecoveryForkGuid = $commonFields.last_recovery_fork_guid
                    $historyObject.RecoveryModel = $commonFields.Recovery_Model
                    $historyObject
                }
                $groupResults | Sort-Object -Property LastLsn, Type
            }
        }
    }
}
tools\dbatools\functions\Get-DbaBackupInformation.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaBackupInformation {
    <#
        .SYNOPSIS
            Scan backup files and creates a set, compatible with Restore-DbaDatabase

        .DESCRIPTION
            Upon being passed a list of potential backups files this command will scan the files, select those that contain SQL Server
            backup sets. It will then filter those files down to a set

            The function defaults to working on a remote instance. This means that all paths passed in must be relative to the remote instance.
            XpDirTree will be used to perform the file scans

            Various means can be used to pass in a list of files to be considered. The default is to non recursively scan the folder
            passed in.

        .PARAMETER Path
            Path to SQL Server backup files.

            Paths passed in as strings will be scanned using the desired method, default is a non recursive folder scan
            Accepts multiple paths separated by ','

            Or it can consist of FileInfo objects, such as the output of Get-ChildItem or Get-Item. This allows you to work with
            your own file structures as needed

        .PARAMETER SqlInstance
            The SQL Server instance to be used to read the headers of the backup files

        .PARAMETER SqlCredential
            Allows you to login to servers using SQL Logins as opposed to Windows Auth/Integrated/Trusted.

        .PARAMETER DatabaseName
            An array of Database Names to filter by. If empty all databases are returned.

        .PARAMETER SourceInstance
            If provided only backup originating from this destination will be returned. This SQL instance will not be connected to or involved in this work

        .PARAMETER NoXpDirTree
            If this switch is set, then Files will be parsed as locally files. This can cause failures if the running user can see files that the parsing SQL Instance cannot

        .PARAMETER DirectoryRecurse
            If specified the provided path/directory will be traversed (only applies if not using XpDirTree)

        .PARAMETER Anonymise
            If specified we will output the results with ComputerName, InstanceName, Database, UserName, and Paths hashed out
            This options is mainly for use if we need you to submit details for fault finding to the dbatools team

        .PARAMETER ExportPath
            If specified the output will export via CliXml format to the specified file. This allows you to store the backup history object for later usage, or move it between computers

        .PARAMETER NoClobber
            If specified will stop Export from overwriting an existing file, the default is to overwrite

        .PARAMETER PassThru
            When data is exported the cmdlet will return no other output, this switch means it will also return the normal output which can be then piped into another command

        .PARAMETER MaintenanceSolution
            This switch tells the function that the folder is the root of a Ola Hallengren backup folder

        .PARAMETER IgnoreLogBackup
            This switch only works with the MaintenanceSolution switch. With an Ola Hallengren style backup we can be sure that the LOG folder contains only log backups and skip it.
            For all other scenarios we need to read the file headers to be sure.

        .PARAMETER AzureCredential
            The name of the SQL Server credential to be used if restoring from an Azure hosted backup

        .PARAMETER Import
            When specified along with a path the command will import a previously exported BackupHistory object from an xml file.

        .PARAMETER EnableException
            Replaces user friendly yellow warnings with bloody red exceptions of doom!
            Use this if you want the function to throw terminating errors you want to catch.

        .EXAMPLE
            Get-DbaBackupInformation -SqlInstance Server1 -Path c:\backups\ -DirectoryRecurse

            Will use the Server1 instance to recursively read all backup files under c:\backups, and return a dbatools BackupHistory object

        .NOTES
            Tags: DisasterRecovery, Backup, Restore

            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaBackupInformation

        .EXAMPLE
            Get-DbaBackupInformation -SqlInstance Server1 -Path c:\backups\ -DirectoryRecurse -ExportPath c:\store\BackupHistory.xml

            #Copy the file  c:\store\BackupHistory.xml to another machine via preferred technique, and the on 2nd machine:

            Get-DbaBackupInformation -Import -Path  c:\store\BackupHistory.xml | Restore-DbaDatabase -SqlInstance Server2 -TrustDbBackupHistory

            This allows you to move backup history across servers, or to preserve backup history even after the original server has been purged

        .EXAMPLE
            Get-DbaBackupInformation -SqlInstance Server1 -Path c:\backups\ -DirectoryRecurse -ExportPath c:\store\BackupHistory.xml -PassThru |
                    Restore-DbaDatabase -SqlInstance Server2 -TrustDbBackupHistory

            In this example we gather backup information, export it to an xml file, and then pass it on through to Restore-DbaDatabase
            This allows us to repeat the restore without having to scan all the backup files again

        .EXAMPLE
            Get-ChildItem c:\backups\ -recurse -files |
                Where {$_.extension -in ('.bak','.trn') -and $_.LastWriteTime -gt (get-date).AddMonths(-1)} |
                Get-DbaBackupInformation -SqlInstance Server1 -ExportPath c:\backupHistory.xml

            This lets you keep a record of all backup history from the last month on hand to speed up refreshes

        .EXAMPLE
            $Backups = Get-DbaBackupInformation -SqlInstance Server1 -Path \\network\backups
            $Backups += Get-DbaBackupInformation -SqlInstance Server2 -NoXpDirTree -Path c:\backups

            Scan the unc folder \\network\backups with Server1, and then scan the C:\backups folder on
            Server2 not using xp_dirtree, adding the results to the first set.

        .EXAMPLE
            $Backups = Get-DbaBackupInformation -SqlInstance Server1 -Path \\network\backups -MaintenanceSolution

            When MaintenanceSolution is indicated we know we are dealing with the output from Ola Hallengren's backup scripts. So we make sure that a FULL folder exists in the first level of Path, if not we shortcut scanning all the files as we have nothing to work with

        .EXAMPLE
            $Backups = Get-DbaBackupInformation -SqlInstance Server1 -Path \\network\backups -MaintenanceSolution -IgnoreLogBackup

            As we know we are dealing with an Ola Hallengren style backup folder from the MaintenanceSolution switch, when IgnoreLogBackup is also included we can ignore the LOG folder to skip any scanning of log backups. Note this also means then WON'T be restored
    #>
    [CmdletBinding( DefaultParameterSetName = "Create")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [object[]]$Path,
        [parameter(Mandatory = $true, ParameterSetName = "Create")]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [parameter(ParameterSetName = "Create")]
        [PSCredential]$SqlCredential,
        [string[]]$DatabaseName,
        [string[]]$SourceInstance,
        [parameter(ParameterSetName = "Create")]
        [Switch]$NoXpDirTree,
        [parameter(ParameterSetName = "Create")]
        [switch]$DirectoryRecurse,
        [switch]$EnableException,
        [switch]$MaintenanceSolution,
        [switch]$IgnoreLogBackup,
        [string]$ExportPath,
        [string]$AzureCredential,
        [parameter(ParameterSetName = "Import")]
        [switch]$Import,
        [switch][Alias('Anonymize')]$Anonymise,
        [Switch]$NoClobber,
        [Switch]$PassThru

    )
    begin {
        function Get-HashString {
            param(
                [String]$InString
            )

            $StringBuilder = New-Object System.Text.StringBuilder
            [System.Security.Cryptography.HashAlgorithm]::Create("md5").ComputeHash([System.Text.Encoding]::UTF8.GetBytes($InString))| ForEach-Object {
                [Void]$StringBuilder.Append($_.ToString("x2"))
            }
            return $StringBuilder.ToString()
        }
        Write-Message -Level InternalComment -Message "Starting"
        Write-Message -Level Debug -Message "Parameters bound: $($PSBoundParameters.Keys -join ", ")"

        if (Test-Bound -ParameterName ExportPath) {
            if ($true -eq $NoClobber) {
                if (Test-Path $ExportPath) {
                    Stop-Function -Message "$ExportPath exists and NoClobber set"
                    return
                }
            }
        }
        if ($PSCmdlet.ParameterSetName -eq "Create") {
            try {
                $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                return
            }
        }

        if ($true -eq $IgnoreLogBackup -and $true -ne $MaintenanceSolution) {
            Write-Message -Message "IgnoreLogBackup can only by used with MaintenanceSolution. Will not be used" -Level Warning
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }
        if ((Test-Bound -Parameter Import) -and ($true -eq $Import)) {
            foreach ($f in $Path) {
                if (Test-Path -Path $f) {
                    $GroupResults += Import-CliXml -Path $f
                    foreach ($group in  $GroupResults) {
                        $Group.FirstLsn = [BigInt]$group.FirstLSN.ToString()
                        $Group.CheckpointLSN = [BigInt]$group.CheckpointLSN.ToString()
                        $Group.DatabaseBackupLsn = [BigInt]$group.DatabaseBackupLsn.ToString()
                        $Group.LastLsn = [BigInt]$group.LastLsn.ToString()
                    }
                }
                else {
                    Write-Message -Message "$f does not exist or is unreadable" -Level Warning
                }
            }
        }
        else {
            $Files = @()
            $groupResults = @()
            if ($Path[0] -match 'http') { $NoXpDirTree = $true }
            if ($NoXpDirTree -ne $true) {
                foreach ($f in $path) {
                    if ([System.IO.Path]::GetExtension($f).Length -gt 1) {
                        if ("Fullname" -notin $f.PSobject.Properties.name) {
                            $f = $f | Select-Object *, @{ Name = "FullName"; Expression = { $f } }
                        }
                        Write-Message -Message "Testing a single file $f " -Level Verbose
                        if ((Test-DbaSqlPath -Path $f.fullname -SqlInstance $server)) {
                            $files += $f
                        }
                        else {
                            Write-Message -Level Verbose -Message "$server cannot 'see' file $($f.FullName)"
                        }
                    }
                    elseif ($True -eq $MaintenanceSolution) {
                        if ($true -eq $IgnoreLogBackup -and [System.IO.Path]::GetDirectoryName($f) -like '*LOG') {
                            Write-Message -Level Verbose -Message "Skipping Log Backups as requested"
                        }
                        else {
                            Write-Message -Level Verbose -Message "OLA - Getting folder contents"
                            $Files += Get-XpDirTreeRestoreFile -Path $f -SqlInstance $server
                        }
                    }
                    else {
                        Write-Message -Message "Testing a folder $f" -Level Verbose
                        $Files += $Check = Get-XpDirTreeRestoreFile -Path $f -SqlInstance $server
                        if ($null -eq $check) {
                            Write-Message -Message "Nothing returned from $f" -Level Verbose
                        }
                    }
                }
            }
            else {
                ForEach ($f in $path) {
                    Write-Message -Level VeryVerbose -Message "Not using sql for $f"
                    if ($f -is [System.IO.FileSystemInfo]) {
                        if ($f.PsIsContainer -eq $true -and $true -ne $MaintenanceSolution) {
                            Write-Message -Level VeryVerbose -Message "folder $($f.fullname)"
                            $Files += Get-ChildItem -Path $f.fullname -File -Recurse:$DirectoryRecurse
                        }
                        elseif ($f.PsIsContainer -eq $true -and $true -eq $MaintenanceSolution) {
                            if ($IgnoreLogBackup -and $f -notlike '*LOG' ) {
                                Write-Message -Level Verbose -Message "Skipping Log backups for Maintenance backups"
                            }
                            else {
                                $Files += Get-ChildItem -Path $f.fullname -File -Recurse:$DirectoryRecurse
                            }
                        }
                        elseif ($true -eq $MaintenanceSolution) {
                            $Files += Get-ChildItem -Path $f.fullname -Recurse:$DirectoryRecurse
                        }
                        else {
                            Write-Message -Level VeryVerbose -Message "File"
                            $Files += $f.fullname
                        }
                    }
                    else {
                        if ($true -eq $MaintenanceSolution) {
                            $Files += Get-XpDirTreeRestoreFile -Path $f\FULL -SqlInstance $server -NoRecurse
                            $Files += Get-XpDirTreeRestoreFile -Path $f\DIFF -SqlInstance $server -NoRecurse
                            $Files += Get-XpDirTreeRestoreFile -Path $f\LOG -SqlInstance $server -NoRecurse
                        }
                        else {
                            Write-Message -Level VeryVerbose -Message "File"
                            $Files += $f
                        }
                    }
                }
            }

            if ($True -eq $MaintenanceSolution -and $True -eq $IgnoreLogBackup) {
                Write-Message -Level Verbose -Message "Skipping Log Backups as requested"
                $Files = $Files | Where-Object {$_.FullName -notlike '*\LOG\*'}
            }

            Write-Message -Level Verbose -Message "Reading backup headers of $($Files.Count) files"
            try {
                $FileDetails = Read-DbaBackupHeader -SqlInstance $server -Path $Files -AzureCredential $AzureCredential -EnableException
            }
            catch {
                Stop-Function -Message "Failure reading backup header" -ErrorRecord $_ -Target $server -Continue
            }
            
            $groupdetails = $FileDetails | group-object -Property BackupSetGUID

            foreach ($Group in $GroupDetails) {
                $historyObject = New-Object Sqlcollaborative.Dbatools.Database.BackupHistory
                $historyObject.ComputerName = $group.group[0].MachineName
                $historyObject.InstanceName = $group.group[0].ServiceName
                $historyObject.SqlInstance = $group.group[0].ServerName
                $historyObject.Database = $group.Group[0].DatabaseName
                $historyObject.UserName = $group.Group[0].UserName
                $historyObject.Start = [DateTime]$group.Group[0].BackupStartDate
                $historyObject.End = [DateTime]$group.Group[0].BackupFinishDate
                $historyObject.Duration = ([DateTime]$group.Group[0].BackupFinishDate - [DateTime]$group.Group[0].BackupStartDate)
                $historyObject.Path = [string[]]$Group.Group.BackupPath
                $historyObject.FileList = ($group.Group.FileList | select-object Type, LogicalName, PhysicalName)
                $historyObject.TotalSize = ($Group.Group.BackupSize | Measure-Object -Sum).Sum
                $HistoryObject.CompressedBackupSize = ($Group.Group.CompressedBackupSize | Measure-Object -Sum).Sum
                $historyObject.Type = $group.Group[0].BackupTypeDescription
                $historyObject.BackupSetId = $group.group[0].BackupSetGUID
                $historyObject.DeviceType = 'Disk'
                $historyObject.FullName = $Group.Group.BackupPath
                $historyObject.Position = $group.Group[0].Position
                $historyObject.FirstLsn = $group.Group[0].FirstLSN
                $historyObject.DatabaseBackupLsn = $group.Group[0].DatabaseBackupLSN
                $historyObject.CheckpointLSN = $group.Group[0].CheckpointLSN
                $historyObject.LastLsn = $group.Group[0].LastLsn
                $historyObject.SoftwareVersionMajor = $group.Group[0].SoftwareVersionMajor
                $historyObject.RecoveryModel = $group.Group.RecoveryModel
                $groupResults += $historyObject
            }
        }
        if (Test-Bound 'SourceInstance') {
            $groupResults = $groupResults | Where-Object {$_.InstanceName -in $SourceInstance}
        }

        if (Test-Bound 'DatabaseName') {
            $groupResults = $groupResults | Where-Object {$_.Database -in $DatabaseName}
        }
        if ($true -eq $Anonymise) {
            foreach ($group in $GroupResults) {
                $group.ComputerName = Get-HashString -InString $group.ComputerName
                $group.InstanceName = Get-HashString -InString $group.InstanceName
                $group.SqlInstance = Get-HashString -InString $group.SqlInstance
                $group.Database = Get-HashString -InString $group.Database
                $group.UserName = Get-HashString -InString $group.UserName
                $group.Path = Get-HashString -InString  $Group.Path
                $group.FullName = Get-HashString -InString $Group.Fullname
            }
        }
        if ((Test-Bound -parameterName exportpath) -and $null -ne $ExportPath) {
            $groupResults | Export-CliXml -Path $ExportPath -Depth 5 -NoClobber:$NoClobber
            if ($true -ne $PassThru) {
                return
            }
        }
        $groupResults | Sort-Object -Property End -Descending
    }
}
tools\dbatools\functions\Get-DbaClientAlias.ps1
function Get-DbaClientAlias {
    <#
    .SYNOPSIS
    Creates/updates a sql alias for the specified server - mimics cliconfg.exe

    .DESCRIPTION
    Creates/updates a SQL Server alias by altering HKLM:\SOFTWARE\Microsoft\MSSQLServer\Client

    .PARAMETER ComputerName
    The target computer where the alias will be created

    .PARAMETER Credential
    Allows you to login to remote computers using alternative credentials

    .PARAMETER EnableException
    By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
    Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
    Tags: Alias

    Website: https://dbatools.io
    Copyright: (C) Chrissy LeMaire, [email protected]
    License: MIT https://opensource.org/licenses/MIT

    .LINK
    https://dbatools.io/Get-DbaClientAlias

        .EXAMPLE
    Get-DbaClientAlias
    Gets all SQL Server client aliases on the local computer

    .EXAMPLE
    Get-DbaClientAlias -ComputerName workstationx
    Gets all SQL Server client aliases on Workstationx
#>
    [CmdletBinding()]
    Param (
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($computer in $ComputerName) {
            $scriptblock = {

                function Get-ItemPropertyValue {
                    Param (
                        [parameter()]
                        [String]$Path,
                        [parameter()]
                        [String]$Name
                    )
                    (Get-ItemProperty -LiteralPath $Path -Name $Name).$Name
                }

                $basekeys = "HKLM:\SOFTWARE\WOW6432Node\Microsoft\MSSQLServer", "HKLM:\SOFTWARE\Microsoft\MSSQLServer"

                foreach ($basekey in $basekeys) {

                    if ((Test-Path $basekey) -eq $false) {
                        Write-Warning "Base key ($basekey) does not exist. Quitting."
                        continue
                    }

                    $client = "$basekey\Client"

                    if ((Test-Path $client) -eq $false) {
                        continue
                    }

                    $connect = "$client\ConnectTo"

                    if ((Test-Path $connect) -eq $false) {
                        continue
                    }

                    if ($basekey -like "*WOW64*") {
                        $architecture = "32-bit"
                    }
                    else {
                        $architecture = "64-bit"
                    }

                    # "Creating/updating alias for $ComputerName for $architecture"
                    $all = Get-Item -Path $connect
                    foreach ($entry in $all.Property) {
                        $value = Get-ItemPropertyValue -Path $connect -Name $entry
                        $clean = $value.Replace('DBNMPNTW,', '').Replace('DBMSSOCN,', '')
                        if ($value.StartsWith('DBMSSOCN')) { $protocol = 'TCP/IP' } else { $protocol = 'Named Pipes' }

                        [pscustomobject]@{
                            ComputerName   = $env:COMPUTERNAME
                            NetworkLibrary = $protocol
                            ServerName     = $clean
                            AliasName      = $entry
                            AliasString    = $value
                            Architecture   = $architecture
                        }
                    }
                }
            }

            if ($PScmdlet.ShouldProcess($computer, "Getting aliases")) {
                try {
                    Invoke-Command2 -ComputerName $computer -Credential $Credential -ScriptBlock $scriptblock -ErrorAction Stop |
                        Select-DefaultView -Property ComputerName, Architecture, NetworkLibrary, ServerName, AliasName
                }
                catch {
                    Stop-Function -Message "Failure" -ErrorRecord $_ -Target $computer -Continue
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaClientProtocol.ps1
function Get-DbaClientProtocol {
    <#
        .SYNOPSIS
            Gets the SQL Server related client protocols on a computer.

        .DESCRIPTION
            Gets the SQL Server related client protocols on one or more computers.

            Requires Local Admin rights on destination computer(s).
            The client protocols can be enabled and disabled when retrieved via WSMan.

        .PARAMETER ComputerName
            The SQL Server (or server in general) that you're connecting to. This command handles named instances.

        .PARAMETER Credential
            Credential object used to connect to the computer as a different user.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Protocol
            Author: Klaas Vandenberghe ( @PowerDBAKlaas )

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaClientProtocol

        .EXAMPLE
            Get-DbaClientProtocol -ComputerName sqlserver2014a

            Gets the SQL Server related client protocols on computer sqlserver2014a.

        .EXAMPLE
            'sql1','sql2','sql3' | Get-DbaClientProtocol

            Gets the SQL Server related client protocols on computers sql1, sql2 and sql3.

        .EXAMPLE
            Get-DbaClientProtocol -ComputerName sql1,sql2 | Out-Gridview

            Gets the SQL Server related client protocols on computers sql1 and sql2, and shows them in a grid view.

        .EXAMPLE
            (Get-DbaClientProtocol -ComputerName sql2 | Where { $_.DisplayName = 'via' }).Disable()

            Disables the VIA ClientNetworkProtocol on computer sql2.
            If succesfull, returncode 0 is shown.
#>
    [CmdletBinding()]
    param (
        [parameter(ValueFromPipeline)]
        [Alias("cn", "host", "Server")]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential] $Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ( $computer in $ComputerName.ComputerName ) {
            $server = Resolve-DbaNetworkName -ComputerName $computer -Credential $credential
            if ( $server.FullComputerName ) {
                $computer = $server.FullComputerName
                Write-Message -Level Verbose -Message "Getting SQL Server namespace on $computer"
                $namespace = Get-DbaCmObject -ComputerName $computer -Namespace root\Microsoft\SQLServer -Query "Select * FROM __NAMESPACE WHERE Name LIke 'ComputerManagement%'" -ErrorAction SilentlyContinue |
                    Where-Object {(Get-DbaCmObject -ComputerName $computer -Namespace $("root\Microsoft\SQLServer\" + $_.Name) -ClassName ClientNetworkProtocol -ErrorAction SilentlyContinue).count -gt 0} |
                    Sort-Object Name -Descending | Select-Object -First 1

                if ( $namespace.Name ) {
                    Write-Message -Level Verbose -Message "Getting Cim class ClientNetworkProtocol in Namespace $($namespace.Name) on $computer"
                    try {
                        $prot = Get-DbaCmObject -ComputerName $computer -Namespace $("root\Microsoft\SQLServer\" + $namespace.Name) -ClassName ClientNetworkProtocol -ErrorAction SilentlyContinue

                        $prot | Add-Member -Force -MemberType ScriptProperty -Name IsEnabled -Value { switch ( $this.ProtocolOrder ) { 0 { $false } default { $true } } }
                        $prot | Add-Member -Force -MemberType ScriptMethod -Name Enable -Value {Invoke-CimMethod -MethodName SetEnable -InputObject $this }
                        $prot | Add-Member -Force -MemberType ScriptMethod -Name Disable -Value {Invoke-CimMethod -MethodName SetDisable -InputObject $this }

                        foreach ( $protocol in $prot ) {
                            Select-DefaultView -InputObject $protocol -Property 'PSComputerName as ComputerName', 'ProtocolDisplayName as DisplayName', 'ProtocolDll as DLL', 'ProtocolOrder as Order', 'IsEnabled'
                        }
                    }
                    catch {
                        Write-Message -Level Warning -Message "No Sql ClientNetworkProtocol found on $computer"
                    }
                } #if namespace
                else {
                    Write-Message -Level Warning -Message "No ComputerManagement Namespace on $computer. Please note that this function is available from SQL 2005 up."
                } #else no namespace
            } #if computername
            else {
                Write-Message -Level Warning -Message "Failed to connect to $computer"
            }
        } #foreach computer
    }
}
tools\dbatools\functions\Get-DbaClusterNode.ps1
function Get-DbaClusterNode {
    <#
        .SYNOPSIS
            Returns the node(s) of a SQL Cluster.

        .DESCRIPTION
            Returns the name of the current node(s) in the SQL Server cluster.

            If the -ActiveNode Parameter is passed it only returns the name of the Server currently hosting the clustered instance.

        .PARAMETER SqlInstance
            Specifies the SQL Server clustered instance to check.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER ActiveNode
            If this parameter is selected the cmdlet will only return the Active Node in the cluster.

        .PARAMETER Detailed
            Output all properties, will be deprecated in 1.0.0 release.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Cluster, WSFC, FCI
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaClusterNode

        .EXAMPLE
            Get-DbaClusterNode -SqlInstance sqlcluster

            Returns all nodes in the cluster and details about each node.

        .EXAMPLE
            Get-DbaClusterNode -SqlInstance sqlcluster -ActiveNode

            Returns the name of the active node in the cluster

    #>
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [switch]$ActiveNode,
        [switch]$Detailed,
        [alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter Detailed
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Alias Get-DbaClusterActiveNode
        $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential -MinimumVersion 10
    }

    process {
        if ($server.IsClustered -eq $false) {
            Stop-Function -Message "Not a clusterd instance." -Category ConnectionError -ErrorRecord $_ -Target $SqlInstance -Continue
        }

        # If the -ActiveNode switch is selected only the primary node is returned.
        if ($ActiveNode) {
            try {
                $sql = "SELECT * FROM sys.dm_os_cluster_nodes where is_current_owner = 1"
                $datatable = $server.query($sql)

                [PSCustomObject]@{
                    ComputerName      = $datatable.nodename
                    InstanceName      = $server.ServiceName
                    SqlInstance       = $server.DomainInstanceName
                    Status            = $datatable.Status
                    StatusDescription = $datatable.StatusDescription
                    CurrentOwner      = $datatable.is_current_owner
                } | Select-DefaultView -Property ComputerName
            }
            catch {
                Stop-Function -Message "Unable to query sys.dm_os_cluster_nodes on $server." -ErrorRecord $_ -Target $SqlInstance -Continue
            }
        }
        #Default Execution of this function
        else {
            try {
                $sql = "SELECT * FROM sys.dm_os_cluster_nodes"
                $datatable = $server.query($sql)

                foreach ($data in $datatable) {
                    [PSCustomObject]@{
                        ComputerName      = $data.nodename
                        InstanceName      = $server.ServiceName
                        SqlInstance       = $server.DomainInstanceName
                        Status            = $data.Status
                        StatusDescription = $data.StatusDescription
                        CurrentOwner      = $data.is_current_owner
                    } | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, StatusDescription, CurrentOwner
                }
            }
            catch {
                Stop-Function -Message "Unable to query sys.dm_os_cluster_nodes on $server." -ErrorRecord $_ -Target $SqlInstance -Continue
            }
        }
    }
}
tools\dbatools\functions\Get-DbaCmConnection.ps1
function Get-DbaCmConnection {
    <#
        .SYNOPSIS
            Retrieves windows management connections from the cache

        .DESCRIPTION
            Retrieves windows management connections from the cache

        .PARAMETER ComputerName
            The computername to ComputerName for.

        .PARAMETER UserName
            Username on credentials to look for. Will not find connections using the default windows credentials.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ComputerManagement, CIM
            Author: Fred Winmann (@FredWeinmann)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaCmConnection

        .EXAMPLE
            Get-DbaCmConnection

            List all cached connections.

        .EXAMPLE
            Get-DbaCmConnection sql2014

            List the cached connection - if any - to the server sql2014.

        .EXAMPLE
            Get-DbaCmConnection -UserName "*charles*"

            List all cached connection that use a username containing "charles" as default or override credentials.
    #>
    [CmdletBinding()]
    param
    (
        [Parameter(Position = 0, ValueFromPipeline = $true)]
        [Alias('Filter')]
        [String[]]
        $ComputerName = "*",

        [String]
        $UserName = "*",

        [switch]
        [Alias('Silent')]$EnableException
    )

    BEGIN {
        Write-Message -Level InternalComment -Message "Starting"
        Write-Message -Level Verbose -Message "Bound parameters: $($PSBoundParameters.Keys -join ", ")"
    }
    PROCESS {
        foreach ($name in $ComputerName) {
            Write-Message -Level VeryVerbose -Message "Processing search. ComputerName: '$name' | Username: '$UserName'"
            ([Sqlcollaborative.Dbatools.Connection.ConnectionHost]::Connections.Values | Where-Object { ($_.ComputerName -like $name) -and ($_.Credentials.UserName -like $UserName) })
        }
    }
    END {
        Write-Message -Level InternalComment -Message "Ending"
    }
}
tools\dbatools\functions\Get-DbaCmObject.ps1
function Get-DbaCmObject {
    <#
        .SYNOPSIS
            Retrieves Wmi/Cim-Style information from computers.

        .DESCRIPTION
            This function centralizes all requests for information retrieved from Get-WmiObject or Get-CimInstance.
            It uses different protocols as available in this order:
            - Cim over WinRM
            - Cim over DCOM
            - Wmi
            - Wmi over PowerShell Remoting
            It remembers channels that didn't work and will henceforth avoid them. It remembers invalid credentials and will avoid reusing them.
            Much of its behavior can be configured using Test-DbaWmConnection.

        .PARAMETER ClassName
            The name of the class to retrieve.

        .PARAMETER Query
            The Wmi/Cim query tu run against the server.

        .PARAMETER ComputerName
            The computer(s) to connect to. Defaults to localhost.

        .PARAMETER Credential
            Credentials to use. Invalid credentials will be stored in a credentials cache and not be reused.

        .PARAMETER Namespace
            The namespace of the class to use.

        .PARAMETER DoNotUse
            Connection Protocols that should not be used.

        .PARAMETER Force
            Overrides some checks that might otherwise halt execution as a precaution
            - Ignores timeout on bad connections

        .PARAMETER SilentlyContinue
            Use in conjunction with the -EnableException switch.
            By default, Get-DbaCmObject will throw a terminating exception when connecting to a target is impossible in exception enabled mode.
            Setting this switch will cause it write a non-terminating exception and continue with the next computer.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ComputerManagement, CIM
            Author: Fred Winmann (@FredWeinmann)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaCmObject

        .EXAMPLE
            Get-DbaCmObject win32_OperatingSystem

            Retrieves the common operating system information from the local computer.

        .EXAMPLE
            Get-DbaCmObject -Computername "sql2014" -ClassName Win32_OperatingSystem -Credential $cred -DoNotUse CimRM

            Retrieves the common operating system information from the server sql2014.
            It will use the credewntials stored in $cred to connect, unless they are known to not work, in which case they will default to windows credentials (unless another default has been set).
    #>
    [CmdletBinding(DefaultParameterSetName = "Class")]
    param (
        [Parameter(Mandatory = $true, Position = 0, ParameterSetName = "Class")]
        [Alias('Class')]
        [string]
        $ClassName,

        [Parameter(Mandatory = $true, Position = 0, ParameterSetName = "Query")]
        [string]
        $Query,

        [Parameter(ValueFromPipeline = $true)]
        [Sqlcollaborative.Dbatools.Parameter.DbaCmConnectionParameter[]]
        $ComputerName = $env:COMPUTERNAME,

        [System.Management.Automation.PSCredential]
        $Credential,

        [string]
        $Namespace = "root\cimv2",

        [Sqlcollaborative.Dbatools.Connection.ManagementConnectionType[]]
        $DoNotUse = "None",

        [switch]
        $Force,

        [switch]
        $SilentlyContinue,

        [switch]
        [Alias('Silent')]$EnableException
    )

    Begin {
        #region Configuration Values
        $disable_cache = [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::DisableCache

        Write-Message -Level Verbose -Message "Configuration loaded | Cache disabled: $disable_cache"
        #endregion Configuration Values

        $ParSet = $PSCmdlet.ParameterSetName
    }
    Process {
        :main foreach ($connectionObject in $ComputerName) {
            if (-not $connectionObject.Success) { Stop-Function -Message "Failed to interpret input: $($connectionObject.Input)" -Category InvalidArgument -Target $connectionObject.Input -Continue -SilentlyContinue:$SilentlyContinue }

            # Since all connection caching runs using lower-case strings, making it lowercase here simplifies things.
            $computer = $connectionObject.Connection.ComputerName.ToLower()

            Write-Message -Message "[$computer] Retrieving Management Information" -Level VeryVerbose -Target $computer

            $connection = $connectionObject.Connection

            # Ensure using the right credentials
            try { $cred = $connection.GetCredential($Credential) }
            catch {
                $message = "Bad credentials! "
                if ($Credential) { $message += "The credentials for $($Credential.UserName) are known to not work. " }
                else { $message += "The windows credentials are known to not work. " }
                if ($connection.EnableCredentialFailover -or $connection.OverrideExplicitCredential) { $message += "The connection is configured to use credentials that are known to be good, but none have been registered yet. " }
                elseif ($connection.Credentials) { $message += "Working credentials are known for $($connection.Credentials.UserName), however the connection is not configured to automatically use them. This can be done using 'Set-DbaCmConnection -ComputerName $connection -OverrideExplicitCredential' " }
                elseif ($connection.UseWindowsCredentials) { $message += "The windows credentials are known to work, however the connection is not configured to automatically use them. This can be done using 'Set-DbaCmConnection -ComputerName $connection -OverrideExplicitCredential' " }
                $message += $_.Exception.Message
                Stop-Function -Message $message -ErrorRecord $_ -Target $connection -Continue -OverrideExceptionMessage
            }

            # Flags-Enumerations cannot be added in PowerShell 4 or older.
            # Thus we create a string and convert it afterwards.
            $enabledProtocols = "None"
            if ($connection.CimRM -notlike "Disabled") { $enabledProtocols += ", CimRM" }
            if ($connection.CimDCOM -notlike "Disabled") { $enabledProtocols += ", CimDCOM" }
            if ($connection.Wmi -notlike "Disabled") { $enabledProtocols += ", Wmi" }
            if ($connection.PowerShellRemoting -notlike "Disabled") { $enabledProtocols += ", PowerShellRemoting" }
            [Sqlcollaborative.Dbatools.Connection.ManagementConnectionType]$enabledProtocols = $enabledProtocols

            # Create list of excluded connection types (Duplicates don't matter)
            $excluded = @()
            foreach ($item in $DoNotUse) { $excluded += $item }

            :sub while ($true) {
                try { $conType = $connection.GetConnectionType(($excluded -join ","), $Force) }
                catch {
                    if (-not $disable_cache) { [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::Connections[$computer] = $connection }
                    Stop-Function -Message "[$computer] Unable to find a connection to the target system. Ensure the name is typed correctly, and the server allows any of the following protocols: $enabledProtocols" -Target $computer -Category OpenError -Continue -ContinueLabel "main" -SilentlyContinue:$SilentlyContinue -ErrorRecord $_
                }

                switch ($conType.ToString()) {
                    #region CimRM
                    "CimRM" {
                        Write-Message -Level Verbose -Message "[$computer] Accessing computer using Cim over WinRM"
                        try {
                            if ($ParSet -eq "Class") { $connection.GetCimRMInstance($cred, $ClassName, $Namespace) }
                            else { $connection.QueryCimRMInstance($cred, $Query, "WQL", $Namespace) }

                            Write-Message -Level Verbose -Message "[$computer] Accessing computer using Cim over WinRM - Success!"
                            $connection.ReportSuccess('CimRM')
                            $connection.AddGoodCredential($cred)
                            if (-not $disable_cache) { [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::Connections[$computer] = $connection }
                            continue main
                        }
                        catch {
                            Write-Message -Level Verbose -Message "[$computer] Accessing computer using Cim over WinRM - Failed!"

                            # 1 = Generic runtime error
                            if ($_.Exception.InnerException.StatusCode -eq 1) {
                                # 0x8007052e, 0x80070005 : Authentication error, bad credential
                                if (($_.Exception.InnerException -eq 0x8007052e) -or ($_.Exception.InnerException -eq 0x80070005)) {
                                    # Ignore the global setting for bad credential cache disabling, since the connection object is aware of that state and will ignore input if it should.
                                    # This is due to the ability to locally override the global setting, thus it must be done on the object and can then be done in code
                                    $connection.AddBadCredential($cred)
                                    if (-not $disable_cache) { [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::Connections[$computer] = $connection }
                                    Stop-Function -Message "[$computer] Invalid connection credentials" -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue -OverrideExceptionMessage
                                }
                                elseif ($_.Exception.InnerException.MessageId -eq "HRESULT 0x80041013") {
                                    if ($ParSet -eq "Class") { Stop-Function -Message "[$computer] Failed to access $class in namespace $Namespace!" -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue -Exception $_.Exception.InnerException }
                                    else { Stop-Function -Message "[$computer] Failed to execute $query in namespace $Namespace!" -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue -Exception $_.Exception.InnerException }
                                }
                                else {
                                    $connection.ReportFailure('CimRM')
                                    $excluded += "CimRM"
                                    continue sub
                                }
                            }

                            # 2 = Access to specific resource denied
                            elseif ($_.Exception.InnerException.StatusCode -eq 2) {
                                Stop-Function -Message "[$computer] Access to computer granted, but access to $Namespace\$ClassName denied!" -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue -OverrideExceptionMessage
                            }

                            # 3 = Invalid Namespace
                            elseif ($_.Exception.InnerException.StatusCode -eq 3) {
                                Stop-Function -Message "[$computer] Invalid namespace: $Namespace" -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue -OverrideExceptionMessage
                            }
                            # 5 = Invalid Class
                            # See here for code reference: https://msdn.microsoft.com/en-us/library/cc150671(v=vs.85).aspx
                            elseif ($_.Exception.InnerException.StatusCode -eq 5) {
                                Stop-Function -Message "[$computer] Invalid class name ($ClassName), not found in current namespace ($Namespace)" -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue -OverrideExceptionMessage
                            }

                            # 0 & ExtendedStatus = Weird issue beyond the scope of the CIM standard. Often a server-side issue
                            elseif (($_.Exception.InnerException.StatusCode -eq 0) -and ($_.Exception.InnerException.ErrorData.original_error -like "__ExtendedStatus")) {
                                Stop-Function -Message "[$computer] Something went wrong when looking for $ClassName, in $Namespace. This often indicates issues with the target system." -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue
                            }
                            else {
                                $connection.ReportFailure('CimRM')
                                $excluded += "CimRM"
                                continue sub
                            }
                        }
                    }
                    #endregion CimRM

                    #region CimDCOM
                    "CimDCOM" {
                        Write-Message -Level Verbose -Message "[$computer] Accessing computer using Cim over DCOM"
                        try {
                            if ($ParSet -eq "Class") { $connection.GetCimDCOMInstance($cred, $ClassName, $Namespace) }
                            else { $connection.QueryCimDCOMInstance($cred, $Query, "WQL", $Namespace) }

                            Write-Message -Level Verbose -Message "[$computer] Accessing computer using Cim over DCOM - Success!"
                            $connection.ReportSuccess('CimDCOM')
                            $connection.AddGoodCredential($cred)
                            if (-not $disable_cache) { [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::Connections[$computer] = $connection }
                            continue main
                        }
                        catch {
                            Write-Message -Level Verbose -Message "[$computer] Accessing computer using Cim over DCOM - Failed!"

                            # 1 = Generic runtime error
                            if ($_.Exception.InnerException.StatusCode -eq 1) {
                                # 0x8007052e, 0x80070005 : Authentication error, bad credential
                                if (($_.Exception.InnerException -eq 0x8007052e) -or ($_.Exception.InnerException -eq 0x80070005)) {
                                    # Ignore the global setting for bad credential cache disabling, since the connection object is aware of that state and will ignore input if it should.
                                    # This is due to the ability to locally override the global setting, thus it must be done on the object and can then be done in code
                                    $connection.AddBadCredential($cred)
                                    if (-not $disable_cache) { [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::Connections[$computer] = $connection }
                                    Stop-Function -Message "[$computer] Invalid connection credentials" -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue -OverrideExceptionMessage
                                }
                                elseif ($_.Exception.InnerException.MessageId -eq "HRESULT 0x80041013") {
                                    if ($ParSet -eq "Class") { Stop-Function -Message "[$computer] Failed to access $class in namespace $Namespace!" -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue -Exception $_.Exception.InnerException }
                                    else { Stop-Function -Message "[$computer] Failed to execute $query in namespace $Namespace!" -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue -Exception $_.Exception.InnerException }
                                }
                                else {
                                    $connection.ReportFailure('CimDCOM')
                                    $excluded += "CimDCOM"
                                    continue sub
                                }
                            }

                            # 2 = Access to specific resource denied
                            elseif ($_.Exception.InnerException.StatusCode -eq 2) {
                                Stop-Function -Message "[$computer] Access to computer granted, but access to $Namespace\$ClassName denied!" -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue -OverrideExceptionMessage
                            }

                            # 3 = Invalid Namespace
                            elseif ($_.Exception.InnerException.StatusCode -eq 3) {
                                Stop-Function -Message "[$computer] Invalid namespace: $Namespace" -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue -OverrideExceptionMessage
                            }

                            # 5 = Invalid Class
                            # See here for code reference: https://msdn.microsoft.com/en-us/library/cc150671(v=vs.85).aspx
                            elseif ($_.Exception.InnerException.StatusCode -eq 5) {
                                Stop-Function -Message "[$computer] Invalid class name ($ClassName), not found in current namespace ($Namespace)" -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue -OverrideExceptionMessage
                            }

                            # 0 & ExtendedStatus = Weird issue beyond the scope of the CIM standard. Often a server-side issue
                            elseif (($_.Exception.InnerException.StatusCode -eq 0) -and ($_.Exception.InnerException.ErrorData.original_error -like "__ExtendedStatus")) {
                                Stop-Function -Message "[$computer] Something went wrong when looking for $ClassName, in $Namespace. This often indicates issues with the target system." -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue
                            }

                            else {
                                $connection.ReportFailure('CimDCOM')
                                $excluded += "CimDCOM"
                                continue sub
                            }
                        }
                    }
                    #endregion CimDCOM

                    #region Wmi
                    "Wmi" {
                        Write-Message -Level Verbose -Message "[$computer] Accessing computer using WMI"
                        try {
                            switch ($ParSet) {
                                "Class" {
                                    $parameters = @{
                                        ComputerName = $computer
                                        ClassName    = $ClassName
                                        ErrorAction  = 'Stop'
                                    }
                                    if ($cred) { $parameters["Credential"] = $cred }
                                    if (Test-Bound "Namespace") { $parameters["Namespace"] = $Namespace }

                                }
                                "Query" {
                                    $parameters = @{
                                        ComputerName = $computer
                                        Query        = $Query
                                        ErrorAction  = 'Stop'
                                    }
                                    if ($cred) { $parameters["Credential"] = $cred }
                                    if (Test-Bound "Namespace") { $parameters["Namespace"] = $Namespace }
                                }
                            }

                            Get-WmiObject @parameters

                            Write-Message -Level Verbose -Message "[$computer] Accessing computer using WMI - Success!"
                            $connection.ReportSuccess('Wmi')
                            $connection.AddGoodCredential($cred)
                            if (-not $disable_cache) { [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::Connections[$computer] = $connection }
                            continue main
                        }
                        catch {
                            Write-Message -Level Verbose -Message "[$computer] Accessing computer using WMI - Failed!" -ErrorRecord $_

                            if ($_.CategoryInfo.Reason -eq "UnauthorizedAccessException") {
                                # Ignore the global setting for bad credential cache disabling, since the connection object is aware of that state and will ignore input if it should.
                                # This is due to the ability to locally override the global setting, thus it must be done on the object and can then be done in code
                                $connection.AddBadCredential($cred)
                                if (-not $disable_cache) { [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::Connections[$computer] = $connection }
                                Stop-Function -Message "[$computer] Invalid connection credentials" -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue
                            }
                            elseif ($_.CategoryInfo.Category -eq "InvalidType") {
                                Stop-Function -Message "[$computer] Invalid class name ($ClassName), not found in current namespace ($Namespace)" -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue
                            }
                            elseif ($_.Exception.ErrorCode -eq "ProviderLoadFailure") {
                                Stop-Function -Message "[$computer] Failed to access: $ClassName, in namespace: $Namespace - There was a provider error. This indicates a potential issue with WMI on the server side." -Target $computer -Continue -ContinueLabel "main" -ErrorRecord $_ -SilentlyContinue:$SilentlyContinue
                            }
                            else {
                                $connection.ReportFailure('Wmi')
                                $excluded += "Wmi"
                                continue sub
                            }
                        }
                    }
                    #endregion Wmi

                    #region PowerShell Remoting
                    "PowerShellRemoting" {
                        try {
                            Write-Message -Level Verbose -Message "[$computer] Accessing computer using PowerShell Remoting"
                            $scp_string = "Get-WmiObject -Class $ClassName -ErrorAction Stop"
                            if ($PSBoundParameters.ContainsKey("Namespace")) { $scp_string += " -Namespace $Namespace" }

                            $parameters = @{
                                ScriptBlock  = ([System.Management.Automation.ScriptBlock]::Create($scp_string))
                                ComputerName = $ComputerName
                                ErrorAction  = 'Stop'
                            }
                            if ($Credential) { $parameters["Credential"] = $Credential }
                            Invoke-Command @parameters

                            Write-Message -Level Verbose -Message "[$computer] Accessing computer using PowerShell Remoting - Success!"
                            $connection.ReportSuccess('PowerShellRemoting')
                            $connection.AddGoodCredential($cred)
                            if (-not $disable_cache) { [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::Connections[$computer] = $connection }
                            continue main
                        }
                        catch {
                            # Will always consider authenticated, since any call with credentials to a server that doesn't exist will also carry invalid credentials error.
                            # There simply is no way to differentiate between actual authentication errors and server not reached
                            $connection.ReportFailure('PowerShellRemoting')
                            $excluded += "PowerShellRemoting"
                            continue sub
                        }
                    }
                    #endregion PowerShell Remoting
                }
            }
        }
    }
    End {

    }
}
tools\dbatools\functions\Get-DbaComputerCertificate.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaComputerCertificate {
    <#
        .SYNOPSIS
            Simplifies finding computer certificates that are candidates for using with SQL Server's network encryption

        .DESCRIPTION
            Gets computer certificates on localhost that are candidates for using with SQL Server's network encryption

        .PARAMETER ComputerName
            The target SQL Server - defaults to localhost. If target is a cluster, you must specify the distinct nodes.

        .PARAMETER Credential
            Allows you to login to $ComputerName using alternative credentials.

        .PARAMETER Store
            Certificate store - defaults to LocalMachine

        .PARAMETER Folder
            Certificate folder - defaults to My (Personal)

        .PARAMETER Path
            The path to a certificate - basically changes the path into a certificate object

        .PARAMETER Thumbprint
            Return certificate based on thumbprint

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Certificate
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            Get-DbaComputerCertificate
            Gets computer certificates on localhost that are candidates for using with SQL Server's network encryption

        .EXAMPLE
            Get-DbaComputerCertificate -ComputerName sql2016

            Gets computer certificates on sql2016 that are candidates for using with SQL Server's network encryption

        .EXAMPLE
            Get-DbaComputerCertificate -ComputerName sql2016 -Thumbprint 8123472E32AB412ED4288888B83811DB8F504DED, 04BFF8B3679BB01A986E097868D8D494D70A46D6

            Gets computer certificates on sql2016 that match thumbprints 8123472E32AB412ED4288888B83811DB8F504DED or 04BFF8B3679BB01A986E097868D8D494D70A46D6
    #>
    [CmdletBinding()]
    param (
        [parameter(ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer", "SqlInstance")]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [string]$Store = "LocalMachine",
        [string]$Folder = "My",
        [string]$Path,
        [string[]]$Thumbprint,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        #region Scriptblock for remoting
        $scriptblock = {
            param (
                $Thumbprint,
                $Store,
                $Folder,
                $Path
            )

            if ($Path) {
                $bytes = [System.IO.File]::ReadAllBytes($path)
                $Certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
                $Certificate.Import($bytes, $null, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::DefaultKeySet)
                return $Certificate
            }

            if ($Thumbprint) {
                try {
                    Write-Verbose "Searching Cert:\$Store\$Folder"
                    Get-ChildItem "Cert:\$Store\$Folder" -Recurse | Where-Object Thumbprint -in $Thumbprint
                }
                catch {
                    # don't care - there's a weird issue with remoting where an exception gets thrown for no apparent reason
                }
            }
            else {
                try {
                    Write-Verbose "Searching Cert:\$Store\$Folder"
                    Get-ChildItem "Cert:\$Store\$Folder" -Recurse | Where-Object { "$($_.EnhancedKeyUsageList)" -match '1\.3\.6\.1\.5\.5\.7\.3\.1' }
                }
                catch {
                    # still don't care
                }
            }
        }
        #endregion Scriptblock for remoting
    }

    process {
        foreach ($computer in $computername) {
            try {
                Invoke-Command2 -ComputerName $computer -Credential $Credential -ScriptBlock $scriptblock -ArgumentList $thumbprint, $Store, $Folder, $Path -ErrorAction Stop | Select-DefaultView -Property FriendlyName, DnsNameList, Thumbprint, NotBefore, NotAfter, Subject, Issuer
            }
            catch {
                Stop-Function -Message "Issue connecting to computer" -ErrorRecord $_ -Target $computer -Continue
            }
        }
    }
}
tools\dbatools\functions\Get-DbaComputerSystem.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaComputerSystem {
    <#
        .SYNOPSIS
            Gets computer system information from the server.

        .DESCRIPTION
            Gets computer system information from the server and returns as an object.

        .PARAMETER ComputerName
            Target computer(s). If no computer name is specified, the local computer is targeted

        .PARAMETER Credential
            Alternate credential object to use for accessing the target computer(s).

        .PARAMETER IncludeAws
            If computer is hosted in AWS Infrastructure as a Service (IaaS), additional information will be included.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ServerInfo
            Author: Shawn Melton (@wsmelton | http://blog.wsmelton.info)

            Website: https: //dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaComputerSystem

        .EXAMPLE
            Get-DbaComputerSystem

            Returns information about the local computer's computer system

        .EXAMPLE
            Get-DbaComputerSystem -ComputerName sql2016

            Returns information about the sql2016's computer system

        .EXAMPLE
            Get-DbaComputerSystem -ComputerName sql2016 -IncludeAws

            Returns information about the sql2016's computer system and includes additional properties around the EC2 instance.
    #>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Alias("cn", "host", "Server")]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [switch]$IncludeAws,
        [switch][Alias('Silent')]
        $EnableException
    )
    process {
        foreach ($computer in $ComputerName) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $computer"
                $server = Resolve-DbaNetworkName -ComputerName $computer.ComputerName -Credential $Credential

                $computerResolved = $server.FullComputerName

                if (!$computerResolved) {
                    Stop-Function -Message "Unable to resolve hostname of $computer. Skipping." -Continue
                }

                if (Test-Bound "Credential") {
                    $computerSystem = Get-DbaCmObject -ClassName Win32_ComputerSystem -ComputerName $computerResolved -Credential $Credential
                }
                else {
                    $computerSystem = Get-DbaCmObject -ClassName Win32_ComputerSystem -ComputerName $computerResolved
                }

                $adminPasswordStatus =
                switch ($computerSystem.AdminPasswordStatus) {
                    0 { "Disabled" }
                    1 { "Enabled" }
                    2 { "Not Implemented" }
                    3 { "Unknown" }
                    default { "Unknown" }
                }

                $domainRole =
                switch ($computerSystem.DomainRole) {
                    0 { "Standalone Workstation" }
                    1 { "Member Workstation" }
                    2 { "Standalone Server" }
                    3 { "Member Server" }
                    4 { "Backup Domain Controller" }
                    5 { "Primary Domain Controller" }
                }

                $isHyperThreading = $false
                if ($computerSystem.NumberOfLogicalProcessors -gt $computerSystem.NumberofProcessors) {
                    $isHyperThreading = $true
                }

                if ($IncludeAws) {
                    $isAws = Invoke-Command2 -ComputerName $computerResolved -Credential $Credential -ScriptBlock { ((Invoke-WebRequest -TimeoutSec 15 -Uri 'http://169.254.169.254').StatusCode) -eq 200 } -Raw

                    if ($isAws) {
                        $scriptBlock = {
                            [PSCustomObject]@{
                                AmiId                 = (Invoke-WebRequest -Uri 'http://169.254.169.254/latest/meta-data/ami-id').Content
                                IamRoleArn            = ((Invoke-WebRequest -Uri 'http://169.254.169.254/latest/meta-data/iam/info').Content | ConvertFrom-Json).InstanceProfileArn
                                InstanceId            = (Invoke-WebRequest -Uri 'http://169.254.169.254/latest/meta-data/instance-id').Content
                                InstanceType          = (Invoke-WebRequest -Uri 'http://169.254.169.254/latest/meta-data/instance-type').Content
                                AvailabilityZone      = (Invoke-WebRequest -Uri 'http://169.254.169.254/latest/meta-data/placement/availability-zone').Content
                                PublicHostname        = (Invoke-WebRequest -Uri 'http://169.254.169.254/latest/meta-data/public-hostname').Content
                            }
                        }
                        $awsProps = Invoke-Command2 -ComputerName $computerResolved -Credential $Credential -ScriptBlock $scriptBlock
                    }
                    else {
                        Write-Message -Level Warning -Message "$computerResolved was not found to be an EC2 instance. Verify http://169.254.169.254 is accessible on the computer."
                    }
                }
                $inputObject = [PSCustomObject]@{
                    ComputerName                 = $computerResolved
                    Domain                       = $computerSystem.Domain
                    DomainRole                   = $domainRole
                    Manufacturer                 = $computerSystem.Manufacturer
                    Model                        = $computerSystem.Model
                    SystemFamily                 = $computerSystem.SystemFamily
                    SystemSkuNumber              = $computerSystem.SystemSKUNumber
                    SystemType                   = $computerSystem.SystemType
                    NumberLogicalProcessors      = $computerSystem.NumberOfLogicalProcessors
                    NumberProcessors             = $computerSystem.NumberOfProcessors
                    IsHyperThreading             = $isHyperThreading
                    TotalPhysicalMemory          = [DbaSize]$computerSystem.TotalPhysicalMemory
                    IsDaylightSavingsTime        = $computerSystem.EnableDaylightSavingsTime
                    DaylightInEffect             = $computerSystem.DaylightInEffect
                    DnsHostName                  = $computerSystem.DNSHostName
                    IsSystemManagedPageFile      = $computerSystem.AutomaticManagedPagefile
                    AdminPasswordStatus          = $adminPasswordStatus
                }
                if ($IncludeAws -and $isAws) {
                    Add-Member -Force -InputObject $inputObject -MemberType NoteProperty -Name AwsAmiId -Value $awsProps.AmiId
                    Add-Member -Force -InputObject $inputObject -MemberType NoteProperty -Name AwsIamRoleArn -Value $awsProps.IamRoleArn
                    Add-Member -Force -InputObject $inputObject -MemberType NoteProperty -Name AwsEc2InstanceId -Value $awsProps.InstanceId
                    Add-Member -Force -InputObject $inputObject -MemberType NoteProperty -Name AwsEc2InstanceType -Value $awsProps.InstanceType
                    Add-Member -Force -InputObject $inputObject -MemberType NoteProperty -Name AwsAvailabilityZone -Value $awsProps.AvailabilityZone
                    Add-Member -Force -InputObject $inputObject -MemberType NoteProperty -Name AwsPublicHostName -Value $awsProps.PublicHostname
                }
                $excludes = 'SystemSkuNumber', 'IsDaylightSavingsTime', 'DaylightInEffect', 'DnsHostName', 'AdminPasswordStatus'
                Select-DefaultView -InputObject $inputObject -ExcludeProperty $excludes
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_ -Target $computer -Continue
            }
        }
    }
}
tools\dbatools\functions\Get-DbaConfig.ps1
function Get-DbaConfig {
    <#
        .SYNOPSIS
            Retrieves configuration elements by name.

        .DESCRIPTION
            Retrieves configuration elements by name.
            Can be used to search the existing configuration list.

        .PARAMETER FullName
            Default: "*"
            Search for configurations using the full name

        .PARAMETER Name
            Default: "*"
            The name of the configuration element(s) to retrieve.
            May be any string, supports wildcards.

        .PARAMETER Module
            Default: "*"
            Search configuration by module.

        .PARAMETER Force
            Overrides the default behavior and also displays hidden configuration values.

        .NOTES
            Tags: Config, Module
            Author: Friedrich Weinmann

        .EXAMPLE
            PS C:\> Get-DbaConfig 'Mail.To'

            Retrieves the configuration element for the key "Mail.To"

        .EXAMPLE
            PS C:\> Get-DbaConfig -Force

            Retrieve all configuration elements from all modules, even hidden ones.
    #>
    [CmdletBinding(DefaultParameterSetName = "FullName")]
    Param (
        [Parameter(ParameterSetName = "FullName", Position = 0)]
        [string]
        $FullName = "*",

        [Parameter(ParameterSetName = "Module", Position = 1)]
        [string]
        $Name = "*",

        [Parameter(ParameterSetName = "Module", Position = 0)]
        [string]
        $Module = "*",

        [switch]
        $Force
    )

    switch ($PSCmdlet.ParameterSetName) {
        "Module" {
            $Name = $Name.ToLower()
            $Module = $Module.ToLower()

            [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations.Values | Where-Object { ($_.Name -like $Name) -and ($_.Module -like $Module) -and ((-not $_.Hidden) -or ($Force)) } | Sort-Object Module, Name
        }

        "FullName" {
            [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations.Values | Where-Object { ("$($_.Module).$($_.Name)" -like $FullName) -and ((-not $_.Hidden) -or ($Force)) } | Sort-Object Module, Name
        }
    }
}
tools\dbatools\functions\Get-DbaConfigValue.ps1
function Get-DbaConfigValue {
    <#
        .SYNOPSIS
            Returns the configuration value stored under the specified name.

        .DESCRIPTION
            Returns the configuration value stored under the specified name.
            It requires the full name (<Module>.<Name>) and is usually only called by functions.

        .PARAMETER FullName
            The full name (<Module>.<Name>) of the configured value to return.

        .PARAMETER Fallback
            A fallback value to use, if no value was registered to a specific configuration element.
            This basically is a default value that only applies on a "per call" basis, rather than a system-wide default.

        .PARAMETER NotNull
            By default, this function returns null if one tries to retrieve the value from either a Configuration that does not exist or a Configuration whose value was set to null.
            However, sometimes it may be important that some value was returned.
            By specifying this parameter, the function will throw an error if no value was found at all.

        .EXAMPLE
            PS C:\> Get-DbaConfigValue -Name 'System.MailServer'

            Returns the configured value that was assigned to the key 'System.MailServer'

        .EXAMPLE
            PS C:\> Get-DbaConfigValue -Name 'Default.CoffeeMilk' -Fallback 0

            Returns the configured value for 'Default.CoffeeMilk'. If no such value is configured, it returns '0' instead.

        .NOTES
            Author: Friedrich Weinmann
            Tags: Config
    #>
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSPossibleIncorrectComparisonWithNull", "")]
    [CmdletBinding()]
    Param (
        [Alias('Name')]
        [Parameter(Mandatory = $true)]
        [string]
        $FullName,

        [object]
        $Fallback,

        [switch]
        $NotNull
    )

    $FullName = $FullName.ToLower()

    $temp = $null
    $temp = [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations[$FullName].Value
    if ($temp -eq $null) { $temp = $Fallback }

    if ($NotNull -and ($temp -eq $null)) {
        Stop-Function -Message "No Configuration Value available for $Name" -EnableException $true -Category InvalidData -Target $FullName
    }
    else {
        return $temp
    }
}
tools\dbatools\functions\Get-DbaConnection.ps1
#ValidationTags#CodeStyle,Messaging,FlowControl,Pipeline#
function Get-DbaConnection {
    <#
        .SYNOPSIS
            Returns a bunch of information from dm_exec_connections.

        .DESCRIPTION
            Returns a bunch of information from dm_exec_connections which, according to Microsoft:
            "Returns information about the connections established to this instance of SQL Server and the details of each connection. Returns server wide connection information for SQL Server. Returns current database connection information for SQL Database."
    
        .PARAMETER SqlInstance
            The target SQL Server instance. Server(s) must be SQL Server 2005 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Connection
            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaConnection

        .EXAMPLE
            Get-DbaConnection -SqlInstance sql2016, sql2017
            
            Returns client connection information from sql2016 and sql2017
    #>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential", "Cred")]
        [PSCredential]$SqlCredential,
        [switch]$EnableException
    )
    
    begin {
        $sql = "SELECT  SERVERPROPERTY('MachineName') AS ComputerName,
                            ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
                            SERVERPROPERTY('ServerName') AS SqlInstance,
                            session_id as SessionId, most_recent_session_id as MostRecentSessionId, connect_time as ConnectTime,
                            net_transport as Transport, protocol_type as ProtocolType, protocol_version as ProtocolVersion,
                            endpoint_id as EndpointId, encrypt_option as EncryptOption, auth_scheme as AuthScheme, node_affinity as NodeAffinity,
                            num_reads as Reads, num_writes as Writes, last_read as LastRead, last_write as LastWrite,
                            net_packet_size as PacketSize, client_net_address as ClientNetworkAddress, client_tcp_port as ClientTcpPort,
                            local_net_address as ServerNetworkAddress, local_tcp_port as ServerTcpPort, connection_id as ConnectionId,
                            parent_connection_id as ParentConnectionId, most_recent_sql_handle as MostRecentSqlHandle
                            FROM sys.dm_exec_connections"
    }
    
    process {
        foreach ($instance in $SqlInstance) {
            
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            
            Write-Message -Level Debug -Message "Getting results for the following query: $sql."
            try {
                $server.Query($sql)
            }
            catch {
                Stop-Function -Message "Failure" -Target $server -Exception $_ -Continue
            }
        }
    }
}
tools\dbatools\functions\Get-DbaCpuUsage.ps1
function Get-DbaCpuUsage {
<#
    .SYNOPSIS
        Provides detailed CPU usage information about a SQL Server's process

    .DESCRIPTION
        "If there are a lot of processes running on your instance and the CPU is very high,
        then it's hard to find the exact process eating up your CPU using just the SQL Server
        tools. One way to correlate the data between what is running within SQL Server and at
        the Windows level is to use SPID and KPID values to get the exact process."

        This command automates that process.

        References: https://www.mssqltips.com/sqlservertip/2454/how-to-find-out-how-much-cpu-a-sql-server-process-is-really-using/

        Note: This command returns results from all SQL instances on the destionation server but the process
        column is specific to -SqlInstance passed.

    .PARAMETER SqlInstance
        Allows you to specify a comma separated list of servers to query.

    .PARAMETER SqlCredential
        Allows you to login to the SQL instance using alternative credentials.

    .PARAMETER Credential
        Allows you to login to the Windows Server using alternative credentials.

    .PARAMETER Threshold
        CPU threshold.

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Tags: CPU
        dbatools PowerShell module (https://dbatools.io, [email protected])
        Copyright (C) 2016 Chrissy LeMaire
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Get-DbaCpuUsage

    .EXAMPLE
        Get-DbaCpuUsage -SqlInstance sql2017

        Logs into the SQL Server instance "sql2017" and also the Computer itself (via WMI) to gather information

    .EXAMPLE
        $usage = Get-DbaCpuUsage -SqlInstance sql2017
        $usage.Process

        Explores the processes (from Get-DbaProcess) associated with the usage results

    .EXAMPLE
        Get-DbaCpuUsage -SqlInstance sql2017 -SqlCredential (Get-Credential sqladmin) -Credential (Get-Credential ad\sqldba)

        Logs into the SQL instance using the SQL Login 'sqladmin' and then Windows instance as 'ad\sqldba'
#>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [PSCredential]$Credential,
        [int]$Threshold = 0,
        [switch]$EnableException
    )
    begin {
        # This can likely be enumerated but I don't know hows
        $threadstates = [pscustomobject]@{
            0 = 'Initialized. It is recognized by the microkernel.'
            1 = 'Ready. It is prepared to run on the next available processor.'
            2 = 'Running. It is executing.'
            3 = 'Standby. It is about to run. Only one thread may be in this state at a time.'
            4 = 'Terminated. It is finished executing.'
            5 = 'Waiting. It is not ready for the processor. When ready, it will be rescheduled.'
            6 = 'Transition. The thread is waiting for resources other than the processor.'
            7 = 'Unknown. The thread state is unknown.'
        }

        $threadwaitreasons = [pscustomobject]@{
            0 = 'Executive'
            1 = 'FreePage'
            2 = 'PageIn'
            3 = 'PoolAllocation'
            4 = 'ExecutionDelay'
            5 = 'FreePage'
            6 = 'PageIn'
            7 = 'Executive'
            8 = 'FreePage'
            9 = 'PageIn'
            10 = 'PoolAllocation'
            11 = 'ExecutionDelay'
            12 = 'FreePage'
            13 = 'PageIn'
            14 = 'EventPairHigh'
            15 = 'EventPairLow'
            16 = 'LPCReceive'
            17 = 'LPCReply'
            18 = 'VirtualMemory'
            19 = 'PageOut'
            20 = 'Unknown'
        }
    }
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $processes = Get-DbaProcess -SqlInstance $server
            $threads = Get-DbaCmObject -ComputerName $instance.ComputerName -ClassName Win32_PerfFormattedData_PerfProc_Thread -Credential $Credential | Where-Object { $_.Name -like 'sql*' -and $_.PercentProcessorTime -ge $Threshold }

            if ($server.VersionMajor -eq 8) {
                $spidcollection = $server.Query("select spid, kpid from sysprocesses")
            }
            else {
                $spidcollection = $server.Query("select t.os_thread_id as kpid, s.session_id as spid
            from sys.dm_exec_sessions s
            join sys.dm_exec_requests er on s.session_id = er.session_id
            join sys.dm_os_workers w on er.task_address = w.task_address
            join sys.dm_os_threads t on w.thread_address = t.thread_address")
            }

            foreach ($thread in $threads) {
                $spid = ($spidcollection | Where-Object kpid -eq $thread.IDThread).spid
                $process = $processes | Where-Object spid -eq $spid
                $threadwaitreason = $thread.ThreadWaitReason
                $threadstate = $thread.ThreadState
                $ThreadStateValue = $threadstates.$threadstate
                $ThreadWaitReasonValue = $threadwaitreasons.$threadwaitreason

                Add-Member -Force -InputObject $thread -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                Add-Member -Force -InputObject $thread -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                Add-Member -Force -InputObject $thread -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                Add-Member -Force -InputObject $thread -MemberType NoteProperty -Name Processes -Value ($processes | Where-Object HostProcessID -eq $thread.IDProcess)
                Add-Member -Force -InputObject $thread -MemberType NoteProperty -Name ThreadStateValue -Value $ThreadStateValue
                Add-Member -Force -InputObject $thread -MemberType NoteProperty -Name ThreadWaitReasonValue -Value $ThreadWaitReasonValue
                Add-Member -Force -InputObject $thread -MemberType NoteProperty -Name Process -Value $process
                Add-Member -Force -InputObject $thread -MemberType NoteProperty -Name Query -Value $process.LastQuery
                Add-Member -Force -InputObject $thread -MemberType NoteProperty -Name Spid -Value $spid

                Select-DefaultView -InputObject $thread -Property ComputerName, InstanceName, SqlInstance, Name, ContextSwitchesPersec, ElapsedTime, IDProcess, Spid, PercentPrivilegedTime, PercentProcessorTime, PercentUserTime, PriorityBase, PriorityCurrent, StartAddress, ThreadStateValue, ThreadWaitReasonValue, Process, Query
            }
        }
    }
}
tools\dbatools\functions\Get-DbaCredential.ps1
#ValidationTags#Messaging,FlowControl,CodeStyle#
function Get-DbaCredential {
    <#
        .SYNOPSIS
            Gets SQL Credential information for each instance(s) of SQL Server.

        .DESCRIPTION
            The Get-DbaCredential command gets SQL Credential information for each instance(s) of SQL Server.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function
            to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Name
            Only include specific names
            Note: if spaces exist in the credential name, you will have to type "" or '' around it.

        .PARAMETER ExcludeName
            Excluded credential names

        .PARAMETER Identity
            Only include specific identities
            Note: if spaces exist in the credential identity, you will have to type "" or '' around it.

        .PARAMETER ExcludeIdentity
            Excluded identities

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Credential
            Author: Garry Bargsley (@gbargsley), http://blog.garrybargsley.com

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaCredential

        .EXAMPLE
            Get-DbaCredential -SqlInstance localhost

            Returns all SQL Credentials on the local default SQL Server instance

        .EXAMPLE
            Get-DbaCredential -SqlInstance localhost, sql2016 -Name 'PowerShell Proxy'

            Returns the SQL Credentials named 'PowerShell Proxy' for the local and sql2016 SQL Server instances

        .EXAMPLE
            Get-DbaCredential -SqlInstance localhost, sql2016 -Identity ad\powershell

            Returns the SQL Credentials for the account 'ad\powershell' on the local and sql2016 SQL Server instances
    #>
    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "")]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$Name,
        [string[]]$ExcludeName,
        [Alias('CredentialIdentity')]
        [string[]]$Identity,
        [Alias('ExcludeCredentialIdentity')]
        [string[]]$ExcludeIdentity,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $credential = $server.Credentials

            if ($Name) {
                $credential = $credential | Where-Object { $Name -contains $_.Name }
            }

            if ($ExcludeName) {
                $credential = $credential | Where-Object { $ExcludeName -notcontains $_.Name }
            }

            if ($Identity) {
                $credential = $credential | Where-Object { $Identity -contains $_.Identity }
            }

            if ($ExcludeIdentity) {
                $credential = $credential | Where-Object { $ExcludeIdentity -notcontains $_.Identity }
            }

            foreach ($currentcredential in $credential) {
                Add-Member -Force -InputObject $currentcredential -MemberType NoteProperty -Name ComputerName -value $currentcredential.Parent.ComputerName
                Add-Member -Force -InputObject $currentcredential -MemberType NoteProperty -Name InstanceName -value $currentcredential.Parent.ServiceName
                Add-Member -Force -InputObject $currentcredential -MemberType NoteProperty -Name SqlInstance -value $currentcredential.Parent.DomainInstanceName

                Select-DefaultView -InputObject $currentcredential -Property ComputerName, InstanceName, SqlInstance, ID, Name, Identity, MappedClassType, ProviderName
            }
        }
    }
}
tools\dbatools\functions\Get-DbaCustomError.ps1
#ValidationTags#Messaging,FlowControl,CodeStyle#
function Get-DbaCustomError {
    <#
        .SYNOPSIS
            Gets SQL Custom Error Message information for each instance(s) of SQL Server.

        .DESCRIPTION
            The Get-DbaCustomError command gets SQL Custom Error Message information for each instance(s) of SQL Server.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function
            to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: CustomError
            Author: Garry Bargsley (@gbargsley), http://blog.garrybargsley.com
            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaCustomError

        .EXAMPLE
            Get-DbaCustomError -SqlInstance localhost

            Returns all Custom Error Message(s) on the local default SQL Server instance

        .EXAMPLE
            Get-DbaCustomError -SqlInstance localhost, sql2016

            Returns all Custom Error Message(s) for the local and sql2016 SQL Server instances
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            foreach ($customError in $server.UserDefinedMessages) {
                Add-Member -Force -InputObject $customError -MemberType NoteProperty -Name ComputerName -value $customError.Parent.ComputerName
                Add-Member -Force -InputObject $customError -MemberType NoteProperty -Name InstanceName -value $customError.Parent.ServiceName
                Add-Member -Force -InputObject $customError -MemberType NoteProperty -Name SqlInstance -value $customError.Parent.DomainInstanceName

                Select-DefaultView -InputObject $customError -Property ComputerName, InstanceName, SqlInstance, ID, Text, LanguageID, Language
            }
        }
    }
}
tools\dbatools\functions\Get-DbaDatabase.ps1
#ValidationTags#CodeStyle,Messaging,FlowControl,Pipeline#
function Get-DbaDatabase {
    <#
        .SYNOPSIS
            Gets SQL Database information for each database that is present on the target instance(s) of SQL Server.

        .DESCRIPTION
            The Get-DbaDatabase command gets SQL database information for each database that is present on the target instance(s) of
            SQL Server. If the name of the database is provided, the command will return only the specific database information.

         .PARAMETER SqlInstance
            The SQL Server instance to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies one or more database(s) to process. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies one or more database(s) to exclude from processing.

        .PARAMETER ExcludeAllUserDb
            If this switch is enabled, only databases which are not User databases will be processed.

            This parameter cannot be used with -ExcludeAllSystemDb.

        .PARAMETER ExcludeAllSystemDb
            If this switch is enabled, only databases which are not System databases will be processed.

            This parameter cannot be used with -ExcludeAllUserDb.

        .PARAMETER Status
            Specifies one or more database statuses to filter on. Only databases in the status(es) listed will be returned. Valid options for this parameter are 'Emergency', 'Normal', 'Offline', 'Recovering', 'Restoring', 'Standby', and 'Suspect'.

        .PARAMETER Access
            Filters databases returned by their access type. Valid options for this parameter are 'ReadOnly' and 'ReadWrite'. If omitted, no filtering is performed.

        .PARAMETER Owner
            Specifies one or more database owners. Only databases owned by the listed owner(s) will be returned.

        .PARAMETER Encrypted
            If this switch is enabled, only databases which have Transparent Data Encryption (TDE) enabled will be returned.

        .PARAMETER RecoveryModel
            Filters databases returned by their recovery model. Valid options for this parameter are 'Full', 'Simple', and 'BulkLogged'.

        .PARAMETER NoFullBackup
            If this switch is enabled, only databases without a full backup recorded by SQL Server will be returned. This will also indicate which of these databases only have CopyOnly full backups.

        .PARAMETER NoFullBackupSince
            Only databases which haven't had a full backup since the specified DateTime will be returned.

        .PARAMETER NoLogBackup
            If this switch is enabled, only databases without a log backup recorded by SQL Server will be returned. This will also indicate which of these databases only have CopyOnly log backups.

        .PARAMETER NoLogBackupSince
            Only databases which haven't had a log backup since the specified DateTime will be returned.

        .PARAMETER IncludeLastUsed
            If this switch is enabled, the last used read & write times for each database will be returned. This data is retrieved from sys.dm_db_index_usage_stats which is reset when SQL Server is restarted.

        .PARAMETER OnlyAccessible
           If this switch is enabled, only accessible databases are returned (huge speedup in SMO enumeration)

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database
            Author: Garry Bargsley (@gbargsley | http://blog.garrybargsley.com)
            Author: Klaas Vandenberghe ( @PowerDbaKlaas )
            Author: Simone Bizzotto ( @niphlod )

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaDatabase

        .EXAMPLE
            Get-DbaDatabase -SqlInstance localhost

            Returns all databases on the local default SQL Server instance.

        .EXAMPLE
            Get-DbaDatabase -SqlInstance localhost -ExcludeAllUserDb

            Returns only the system databases on the local default SQL Server instance.

        .EXAMPLE
            Get-DbaDatabase -SqlInstance localhost -ExcludeAllSystemDb

            Returns only the user databases on the local default SQL Server instance.

        .EXAMPLE
            'localhost','sql2016' | Get-DbaDatabase

            Returns databases on multiple instances piped into the function.

        .EXAMPLE
            Get-DbaDatabase -SqlInstance SQL1\SQLExpress -RecoveryModel full,Simple

            Returns only the user databases in Full or Simple recovery model from SQL Server instance SQL1\SQLExpress.

        .EXAMPLE
            Get-DbaDatabase -SqlInstance SQL1\SQLExpress -Status Normal

            Returns only the user databases with status 'normal' from SQL Server instance SQL1\SQLExpress.

        .EXAMPLE
            Get-DbaDatabase -SqlInstance SQL1\SQLExpress -IncludeLastUsed

            Returns the databases from SQL Server instance SQL1\SQLExpress and includes the last used information
            from the sys.dm_db_index_usage_stats DMV.

        .EXAMPLE
            Get-DbaDatabase -SqlInstance SQL1\SQLExpress,SQL2 -ExcludeDatabase model,master

            Returns all databases except master and model from SQL Server instances SQL1\SQLExpress and SQL2.

        .EXAMPLE
            Get-DbaDatabase -SqlInstance SQL1\SQLExpress,SQL2 -Encrypted

            Returns only databases using TDE from SQL Server instances SQL1\SQLExpress and SQL2.

        .EXAMPLE
            Get-DbaDatabase -SqlInstance SQL1\SQLExpress,SQL2 -Access ReadOnly

            Returns only read only databases from SQL Server instances SQL1\SQLExpress and SQL2.

        .EXAMPLE
            Get-DbaDatabase -SqlInstance SQL2,SQL3 -Database OneDB,OtherDB

            Returns databases 'OneDb' and 'OtherDB' from SQL Server instances SQL2 and SQL3 if databases by those names exist on those instances.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    Param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Alias("SystemDbOnly", "NoUserDb")]
        [switch]$ExcludeAllUserDb,
        [Alias("UserDbOnly", "NoSystemDb")]
        [switch]$ExcludeAllSystemDb,
        [string[]]$Owner,
        [switch]$Encrypted,
        [ValidateSet('EmergencyMode', 'Normal', 'Offline', 'Recovering', 'Restoring', 'Standby', 'Suspect')]
        [string[]]$Status = @('EmergencyMode', 'Normal', 'Offline', 'Recovering', 'Restoring', 'Standby', 'Suspect'),
        [ValidateSet('ReadOnly', 'ReadWrite')]
        [string]$Access,
        [ValidateSet('Full', 'Simple', 'BulkLogged')]
        [string[]]$RecoveryModel = @('Full', 'Simple', 'BulkLogged'),
        [switch]$NoFullBackup,
        [datetime]$NoFullBackupSince,
        [switch]$NoLogBackup,
        [datetime]$NoLogBackupSince,
        [Alias('Silent')]
        [switch]$EnableException,
        [switch]$IncludeLastUsed,
        [switch]$OnlyAccessible
    )

    begin {

        if ($ExcludeAllUserDb -and $ExcludeAllSystemDb) {
            Stop-Function -Message "You cannot specify both ExcludeAllUserDb and ExcludeAllSystemDb." -Continue -EnableException $EnableException
        }

    }
    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if (!$IncludeLastUsed) {
                $dblastused = $null
            }
            else {
                ## Get last used information from the DMV
                $querylastused = "WITH agg AS
                (
                  SELECT
                       max(last_user_seek) last_user_seek,
                       max(last_user_scan) last_user_scan,
                       max(last_user_lookup) last_user_lookup,
                       max(last_user_update) last_user_update,
                       sd.name dbname
                   FROM
                       sys.dm_db_index_usage_stats, master..sysdatabases sd
                   WHERE
                     database_id = sd.dbid AND database_id > 4
                      group by sd.name
                )
                SELECT
                   dbname,
                   last_read = MAX(last_read),
                   last_write = MAX(last_write)
                FROM
                (
                   SELECT dbname, last_user_seek, NULL FROM agg
                   UNION ALL
                   SELECT dbname, last_user_scan, NULL FROM agg
                   UNION ALL
                   SELECT dbname, last_user_lookup, NULL FROM agg
                   UNION ALL
                   SELECT dbname, NULL, last_user_update FROM agg
                ) AS x (dbname, last_read, last_write)
                GROUP BY
                   dbname
                ORDER BY 1;"
                # put a function around this to enable Pester Testing and also to ease any future changes
                function Invoke-QueryDBlastUsed {
                    $server.Query($querylastused)
                }
                $dblastused = Invoke-QueryDBlastUsed
            }

            if ($ExcludeAllUserDb) {
                $DBType = @($true)
            }
            elseif ($ExcludeAllSystemDb) {
                $DBType = @($false)
            }
            else {
                $DBType = @($false, $true)
            }

            $AccessibleFilter = switch ($OnlyAccessible) {
                $true { @($true) }
                default { @($true, $false) }
            }

            $Readonly = switch ($Access) {
                'Readonly' { @($true) }
                'ReadWrite' { @($false) }
                default { @($true, $false) }
            }
            $Encrypt = switch (Test-Bound $Encrypted) {
                $true { @($true) }
                default { @($true, $false, $null) }
            }
            function Invoke-QueryRawDatabases {
                if ($server.VersionMajor -eq 8) {
                    $server.Query("SELECT *, SUSER_NAME(sid) AS [Owner] FROM master.dbo.sysdatabases")
                }
                else {
                    $server.Query("SELECT *, SUSER_NAME(owner_sid) AS [Owner] FROM sys.databases")
                }
            }
            $backed_info = Invoke-QueryRawDatabases
            $backed_info = $backed_info | Where-Object {
                ($_.name -in $Database -or !$Database) -and
                ($_.name -notin $ExcludeDatabase -or !$ExcludeDatabase) -and
                ($_.Owner -in $Owner -or !$Owner) -and
                ($_.state -ne 6 -or !$OnlyAccessible)
            }

            $inputObject = @()
            foreach($dt in $backed_info) {
                $inputObject += $server.Databases[$dt.name]
            }
            $inputobject = $inputObject |
                Where-Object {
                ($_.Name -in $Database -or !$Database) -and
                ($_.Name -notin $ExcludeDatabase -or !$ExcludeDatabase) -and
                ($_.Owner -in $Owner -or !$Owner) -and
                $_.ReadOnly -in $Readonly -and
                $_.IsAccessible -in $AccessibleFilter -and
                $_.IsSystemObject -in $DBType -and
                ((Compare-Object @($_.Status.tostring().split(',').trim()) $Status -ExcludeDifferent -IncludeEqual).inputobject.count -ge 1 -or !$status) -and
                $_.RecoveryModel -in $RecoveryModel -and
                $_.EncryptionEnabled -in $Encrypt
            }
            if ($NoFullBackup -or $NoFullBackupSince) {
                $dabs = (Get-DbaBackupHistory -SqlInstance $server -LastFull )
                if ($null -ne $NoFullBackupSince) {
                    $dabsWithinScope = ($dabs | Where-Object End -lt $NoFullBackupSince)

                    $inputobject = $inputobject | Where-Object { $_.Name -in $dabsWithinScope.Database -and $_.Name -ne 'tempdb' }
                }
                else {
                    $inputObject = $inputObject | Where-Object { $_.Name -notin $dabs.Database -and $_.Name -ne 'tempdb' }
                }

            }
            if ($NoLogBackup -or $NoLogBackupSince) {
                $dabs = (Get-DbaBackupHistory -SqlInstance $server -LastLog )
                if ($null -ne $NoLogBackupSince) {
                    $dabsWithinScope = ($dabs | Where-Object End -lt $NoLogBackupSince)
                    $inputobject = $inputobject |
                        Where-Object { $_.Name -in $dabsWithinScope.Database -and $_.Name -ne 'tempdb' -and $_.RecoveryModel -ne 'Simple' }
                }
                else {
                    $inputobject = $inputObject |
                        Where-Object { $_.Name -notin $dabs.Database -and $_.Name -ne 'tempdb' -and $_.RecoveryModel -ne 'Simple' }
                }
            }

            $defaults = 'ComputerName', 'InstanceName', 'SqlInstance', 'Name', 'Status', 'IsAccessible', 'RecoveryModel',
            'LogReuseWaitStatus', 'Size as SizeMB', 'CompatibilityLevel as Compatibility', 'Collation', 'Owner',
            'LastBackupDate as LastFullBackup', 'LastDifferentialBackupDate as LastDiffBackup',
            'LastLogBackupDate as LastLogBackup'

            if ($NoFullBackup -or $NoFullBackupSince -or $NoLogBackup -or $NoLogBackupSince) {
                $defaults += ('Notes')
            }
            if ($IncludeLastUsed) {
                # Add Last Used to the default view
                $defaults += ('LastRead as LastIndexRead', 'LastWrite as LastIndexWrite')
            }

            try {
                foreach ($db in $inputobject) {

                    $Notes = $null
                    if ($NoFullBackup -or $NoFullBackupSince) {
                        if (@($db.EnumBackupSets()).count -eq @($db.EnumBackupSets() | Where-Object { $_.IsCopyOnly }).count -and (@($db.EnumBackupSets()).count -gt 0)) {
                            $Notes = "Only CopyOnly backups"
                        }
                    }

                    $lastusedinfo = $dblastused | Where-Object { $_.dbname -eq $db.name }
                    Add-Member -Force -InputObject $db -MemberType NoteProperty BackupStatus -value $Notes
                    Add-Member -Force -InputObject $db -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                    Add-Member -Force -InputObject $db -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                    Add-Member -Force -InputObject $db -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                    Add-Member -Force -InputObject $db -MemberType NoteProperty -Name LastRead -value $lastusedinfo.last_read
                    Add-Member -Force -InputObject $db -MemberType NoteProperty -Name LastWrite -value $lastusedinfo.last_write
                    Select-DefaultView -InputObject $db -Property $defaults
                    #try { $server.Databases.Refresh() } catch {}
                }
            }
            catch {
                Stop-Function -ErrorRecord $_ -Target $instance -Message "Failure. Collection may have been modified. If so, please use parens (Get-DbaDatabase ....) | when working with commands that modify the collection such as Remove-DbaDatabase." -Continue
            }
        }
    }
}
tools\dbatools\functions\Get-DbaDatabaseAssembly.ps1
#ValidationTags#Messaging,FlowControl,CodeStyle#
function Get-DbaDatabaseAssembly {
    <#
        .SYNOPSIS
            Gets SQL Database Assembly information for each instance(s) of SQL Server.

        .DESCRIPTION
            The Get-DbaDatabaseAssembly command gets SQL Database Assembly information for each instance(s) of SQL Server.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function
            to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Assembly, Database
            Author: Garry Bargsley (@gbargsley), http://blog.garrybargsley.com
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaDatabaseAssembly

        .EXAMPLE
            Get-DbaDatabaseAssembly -SqlInstance localhost

            Returns all Database Assembly on the local default SQL Server instance

        .EXAMPLE
            Get-DbaDatabaseAssembly -SqlInstance localhost, sql2016

            Returns all Database Assembly for the local and sql2016 SQL Server instances
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            foreach ($database in ($server.Databases | Where-Object IsAccessible)) {
                try {
                    foreach ($assembly in $database.assemblies) {

                        Add-Member -Force -InputObject $assembly -MemberType NoteProperty -Name ComputerName -value $assembly.Parent.Parent.ComputerName
                        Add-Member -Force -InputObject $assembly -MemberType NoteProperty -Name InstanceName -value $assembly.Parent.Parent.ServiceName
                        Add-Member -Force -InputObject $assembly -MemberType NoteProperty -Name SqlInstance -value $assembly.Parent.Parent.DomainInstanceName

                        Select-DefaultView -InputObject $assembly -Property ComputerName, InstanceName, SqlInstance, ID, Name, Owner, 'AssemblySecurityLevel as SecurityLevel', CreateDate, IsSystemObject, Version
                    }
                }
                catch {
                    Stop-Function -Message "Issue pulling assembly information" -Target $assembly -ErrorRecord $_ -Continue
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaDatabaseEncryption.ps1
function Get-DbaDatabaseEncryption {
    <#
        .SYNOPSIS
            Returns a summary of encryption used on databases passed to it.

        .DESCRIPTION
            Shows if a database has Transparent Data Encryption (TDE), any certificates, asymmetric keys or symmetric keys with details for each.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto-populated from the server.

        .PARAMETER IncludeSystemDBs
            Switch parameter that when used will display system database information.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Encryption, Database
            Author: Stephen Bennett, https://sqlnotesfromtheunderground.wordpress.com/
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaDatabaseEncryption

        .EXAMPLE
            Get-DbaDatabaseEncryption -SqlInstance DEV01

            List all encryption found on the instance by database

        .EXAMPLE
            Get-DbaDatabaseEncryption -SqlInstance DEV01 -Database MyDB

            List all encryption found for the MyDB database.

        .EXAMPLE
            Get-DbaDatabaseEncryption -SqlInstance DEV01 -ExcludeDatabase MyDB

            List all encryption found for all databases except MyDB.

        .EXAMPLE
            Get-DbaDatabaseEncryption -SqlInstance DEV01 -IncludeSystemDBs

            List all encryption found for all databases including the system databases.
    #>
    [CmdletBinding()]
    param ([parameter(ValueFromPipeline, Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$IncludeSystemDBs,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            #For each SQL Server in collection, connect and get SMO object
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            #If IncludeSystemDBs is true, include systemdbs
            #only look at online databases (Status equal normal)
            try {
                if ($Database) {
                    $dbs = $server.Databases | Where-Object Name -In $Database
                }
                elseif ($IncludeSystemDBs) {
                    $dbs = $server.Databases | Where-Object IsAccessible
                }
                else {
                    $dbs = $server.Databases | Where-Object { $_.IsAccessible -and $_.IsSystemObject -eq 0 }
                }

                if ($ExcludeDatabase) {
                    $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
                }
            }
            catch {
                Stop-Function -Message "Unable to gather dbs for $instance" -Target $instance -Continue
            }

            foreach ($db in $dbs) {
                Write-Message -Level Verbose -Message "Processing $db"

                if ($db.EncryptionEnabled -eq $true) {
                    [PSCustomObject]@{
                        ComputerName             = $server.ComputerName
                        InstanceName             = $server.ServiceName
                        SqlInstance              = $server.DomainInstanceName
                        Database                 = $db.Name
                        Encryption               = "EncryptionEnabled (TDE)"
                        Name                     = $null
                        LastBackup               = $null
                        PrivateKeyEncryptionType = $null
                        EncryptionAlgorithm      = $null
                        KeyLength                = $null
                        Owner                    = $null
                        Object                   = $null
                        ExpirationDate           = $null
                    }

                }

                foreach ($cert in $db.Certificates) {
                    [PSCustomObject]@{
                        ComputerName             = $server.ComputerName
                        InstanceName             = $server.ServiceName
                        SqlInstance              = $server.DomainInstanceName
                        Database                 = $db.Name
                        Encryption               = "Certificate"
                        Name                     = $cert.Name
                        LastBackup               = $cert.LastBackupDate
                        PrivateKeyEncryptionType = $cert.PrivateKeyEncryptionType
                        EncryptionAlgorithm      = $null
                        KeyLength                = $null
                        Owner                    = $cert.Owner
                        Object                   = $cert
                        ExpirationDate           = $cert.ExpirationDate
                    }

                }

                foreach ($ak in $db.AsymmetricKeys) {
                    [PSCustomObject]@{
                        ComputerName             = $server.ComputerName
                        InstanceName             = $server.ServiceName
                        SqlInstance              = $server.DomainInstanceName
                        Database                 = $db.Name
                        Encryption               = "Asymmetric key"
                        Name                     = $ak.Name
                        LastBackup               = $null
                        PrivateKeyEncryptionType = $ak.PrivateKeyEncryptionType
                        EncryptionAlgorithm      = $ak.KeyEncryptionAlgorithm
                        KeyLength                = $ak.KeyLength
                        Owner                    = $ak.Owner
                        Object                   = $ak
                        ExpirationDate           = $null
                    }

                }
                foreach ($sk in $db.SymmetricKeys) {
                    [PSCustomObject]@{
                        Server                   = $server.name
                        Instance                 = $server.InstanceName
                        Database                 = $db.Name
                        Encryption               = "Symmetric key"
                        Name                     = $sk.Name
                        LastBackup               = $null
                        PrivateKeyEncryptionType = $sk.PrivateKeyEncryptionType
                        EncryptionAlgorithm      = $ak.EncryptionAlgorithm
                        KeyLength                = $sk.KeyLength
                        Owner                    = $sk.Owner
                        Object                   = $sk
                        ExpirationDate           = $null
                    }
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaDatabaseFile.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaDatabaseFile {
    <#
    .SYNOPSIS
    Returns detailed information about database files.

    .DESCRIPTION
    Returns detailed information about database files. Does not use SMO - SMO causes enumeration and this command avoids that.

    .PARAMETER SqlInstance
    The target SQL Server instance(s)

    .PARAMETER SqlCredential
    Credentials to connect to the SQL Server instance if the calling user doesn't have permission

    .PARAMETER Database
    The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

    .PARAMETER ExcludeDatabase
    The database(s) to exclude - this list is auto-populated from the server

    .PARAMETER InputObject
    A piped collection of database objects

    .PARAMETER EnableException
    By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
    Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
    Author: Stuart Moore (@napalmgram), stuart-moore.com
    Tags: Database
    Website: https://dbatools.io
    Copyright: (C) Chrissy LeMaire, [email protected]
    License: MIT https://opensource.org/licenses/MIT

    .EXAMPLE
    Get-DbaDatabaseFile -SqlInstance sql2016

    Will return an object containing all filegroups and their contained files for every database on the sql2016 SQL Server instance

    .EXAMPLE
    Get-DbaDatabaseFile -SqlInstance sql2016 -Database Impromptu

    Will return an object containing all filegroups and their contained files for the Impromptu Database on the sql2016 SQL Server instance

    .EXAMPLE
    Get-DbaDatabaseFile -SqlInstance sql2016 -Database Impromptu, Trading

    Will return an object containing all filegroups and their contained files for the Impromptu and Trading databases on the sql2016 SQL Server instance

    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    param (
        [parameter(ParameterSetName = "Pipe", Mandatory, ValueFromPipeline)]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [object[]]$InputObject,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {

        foreach ($instance in $sqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $sql = "select
            fg.name as FileGroupName,
            df.file_id as 'ID',
            df.Type,
            df.type_desc as TypeDescription,
            df.name as LogicalName,
            mf.physical_name as PhysicalName,
            df.state_desc as State,
            df.max_size as MaxSize,
            case mf.is_percent_growth when 1 then df.growth else df.Growth*8 end as Growth,
            fileproperty(df.name, 'spaceused') as UsedSpace,
            df.size as Size,
            vfs.size_on_disk_bytes as size_on_disk_bytes,
            case df.state_desc when 'OFFLINE' then 'True' else 'False' End as IsOffline,
            case mf.is_read_only when 1 then 'True' when 0 then 'False' End as IsReadOnly,
            case mf.is_media_read_only when 1 then 'True' when 0 then 'False' End as IsReadOnlyMedia,
            case mf.is_sparse when 1 then 'True' when 0 then 'False' End as IsSparse,
            case mf.is_percent_growth when 1 then 'Percent' when 0 then 'kb' End as GrowthType,
            case mf.is_read_only when 1 then 'True' when 0 then 'False' End as IsReadOnly,
            vfs.num_of_writes as NumberOfDiskWrites,
            vfs.num_of_reads as NumberOfDiskReads,
            vfs.num_of_bytes_read as BytesReadFromDisk,
            vfs.num_of_bytes_written as BytesWrittenToDisk,
            fg.data_space_id as FileGroupDataSpaceId,
            fg.Type as FileGroupType,
            fg.type_desc as FileGroupTypeDescription,
            case fg.is_default When 1 then 'True' when 0 then 'False' end as FileGroupDefault,
            fg.is_read_only as FileGroupReadOnly"

            $sqlfrom = "from sys.database_files df
            left outer join  sys.filegroups fg on df.data_space_id=fg.data_space_id
            inner join sys.dm_io_virtual_file_stats(db_id(),NULL) vfs on df.file_id=vfs.file_id
            inner join sys.master_files mf on df.file_id = mf.file_id
            and mf.database_id = db_id()"

            $sql2008 = ",vs.available_bytes as 'VolumeFreeSpace'"
            $sql2008from = "cross apply sys.dm_os_volume_stats(db_id(),df.file_id) vs"

            $sql2000 = "select
            fg.groupname as FileGroupName,
            df.fileid as ID,
            CONVERT(INT,df.status & 0x40) / 64 as Type,
            case CONVERT(INT,df.status & 0x40) / 64 when 1 then 'LOG' else 'ROWS' end as TypeDescription,
            df.name as LogicalName,
            df.filename as PhysicalName,
            'Existing' as State,
            df.maxsize as MaxSize,
            case CONVERT(INT,df.status & 0x100000) / 1048576 when 1 then df.growth when 0 then df.growth*8 End as Growth,
            fileproperty(df.name, 'spaceused') as UsedSpace,
            df.size as Size,
            case CONVERT(INT,df.status & 0x20000000) / 536870912 when 1 then 'True' else 'False' End as IsOffline,
            case CONVERT(INT,df.status & 0x10) / 16 when 1 then 'True' when 0 then 'False' End as IsReadOnly,
            case CONVERT(INT,df.status & 0x1000) / 4096 when 1 then 'True' when 0 then 'False' End as IsReadOnlyMedia,
            case CONVERT(INT,df.status & 0x10000000) / 268435456 when 1 then 'True' when 0 then 'False' End as IsSparse,
            case CONVERT(INT,df.status & 0x100000) / 1048576 when 1 then 'Percent' when 0 then 'kb' End as GrowthType,
            case CONVERT(INT,df.status & 0x1000) / 4096 when 1 then 'True' when 0 then 'False' End as IsReadOnly,
            fg.groupid as FileGroupDataSpaceId,
            NULL as FileGroupType,
            NULL AS FileGroupTypeDescription,
            CAST(fg.Status & 0x10 as BIT) as FileGroupDefault,
            CAST(fg.Status & 0x8 as BIT) as FileGroupReadOnly
            from sysfiles df
            left outer join  sysfilegroups fg on df.groupid=fg.groupid"

            if ($Database) {
                $InputObject = $server.Databases | Where-Object Name -in $database
            }
            else {
                $InputObject = $server.Databases
            }

            if ($ExcludeDatabase) {
                $InputObject = $InputObject | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $InputObject) {
                if (!$db.IsAccessible) {
                    Write-Message -Level Warning -Message "Database $db is not accessible. Skipping"
                    continue
                }
                Write-Message -Level Verbose -Message "Querying database $db"

                $version = Test-DbaDatabaseCompatibility -SqlInstance $server -Database $db.Name | Select-Object DatabaseCompatibility
                $version = + ($version.DatabaseCompatibility.ToString().replace("Version", "")) / 10

                if ($version -ge 11) {
                    $query = ($sql, $sql2008, $sqlfrom, $sql2008from) -Join "`n"
                }
                elseif ($version -ge 9) {
                    $query = ($sql, $sqlfrom) -Join "`n"
                }
                else {
                    $query = $sql2000
                }

                Write-Message -Level Debug -Message "SQL Statement: $query"

                $results = $server.Query($query, $db.Name)

                foreach ($result in $results) {
                    $size = [dbasize]($result.Size * 8192)
                    $usedspace = [dbasize]($result.UsedSpace * 8192)
                    $maxsize = $result.MaxSize
                    # calculation is done here because for snapshots or sparse files size is not the "virtual" size
                    # (master_files.Size) but the currently allocated one (dm_io_virtual_file_stats.size_on_disk_bytes)
                    $AvailableSpace = $size - $usedspace
                    if ($result.size_on_disk_bytes) {
                        $size = [dbasize]($result.size_on_disk_bytes)
                    }
                    if ($maxsize -gt -1) {
                        $maxsize = [dbasize]($result.MaxSize * 8192)
                    }
                    else {
                        $maxsize = [dbasize]($result.MaxSize)
                    }

                    if ($result.VolumeFreeSpace) {
                        $VolumeFreeSpace = [dbasize]$result.VolumeFreeSpace
                    }
                    else {
                        # to get drive free space for each drive that a database has files on
                        # when database compatibility lower than 110. Lets do this with query2
                        $query2 = @'
-- to get drive free space for each drive that a database has files on
DECLARE @FixedDrives TABLE(Drive CHAR(1), MB_Free BIGINT);
INSERT @FixedDrives EXEC sys.xp_fixeddrives;

SELECT DISTINCT fd.MB_Free, LEFT(df.physical_name, 1) AS [Drive]
FROM @FixedDrives AS fd
INNER JOIN sys.database_files AS df
ON fd.Drive = LEFT(df.physical_name, 1);
'@
                        # if the server has one drive xp_fixeddrives returns one row, but we still need $disks to be an array.
                        $disks = @($server.Query($query2, $db.Name))
                        $MbFreeColName = $disks[0].psobject.Properties.Name
                        # get the free MB value for the drive in question
                        $free = $disks | Where-Object { $_.drive -eq $result.PhysicalName.Substring(0, 1) } | Select-Object $MbFreeColName
                        
                        $VolumeFreeSpace = [dbasize](($free.MB_Free) * 1024 * 1024)
                    }
                    if ($result.GrowthType -eq "Percent") {
                        $nextgrowtheventadd = [dbasize]($result.size * ($result.Growth * 0.01) * 1024)
                    }
                    else {
                        $nextgrowtheventadd = [dbasize]($result.Growth * 8 * 1024)
                    }
                    if ( ($nextgrowtheventadd.Byte -gt ($MaxSize.Byte - $size.Byte)) -and $maxsize -gt 0 ) { [dbasize]$nextgrowtheventadd = 0 }

                    [PSCustomObject]@{
                        ComputerName             = $server.ComputerName
                        InstanceName             = $server.ServiceName
                        SqlInstance              = $server.DomainInstanceName
                        Database                 = $db.name
                        FileGroupName            = $result.FileGroupName
                        ID                       = $result.ID
                        Type                     = $result.Type
                        TypeDescription          = $result.TypeDescription
                        LogicalName              = $result.LogicalName.Trim()
                        PhysicalName             = $result.PhysicalName.Trim()
                        State                    = $result.State
                        MaxSize                  = $maxsize
                        Growth                   = $result.Growth
                        GrowthType               = $result.GrowthType
                        NextGrowthEventSize      = $nextgrowtheventadd
                        Size                     = $size
                        UsedSpace                = $usedspace
                        AvailableSpace           = $AvailableSpace
                        IsOffline                = $result.IsOffline
                        IsReadOnly               = $result.IsReadOnly
                        IsReadOnlyMedia          = $result.IsReadOnlyMedia
                        IsSparse                 = $result.IsSparse
                        NumberOfDiskWrites       = $result.NumberOfDiskWrites
                        NumberOfDiskReads        = $result.NumberOfDiskReads
                        ReadFromDisk             = [dbasize]$result.BytesReadFromDisk
                        WrittenToDisk            = [dbasize]$result.BytesWrittenToDisk
                        VolumeFreeSpace          = $VolumeFreeSpace
                        FileGroupDataSpaceId     = $result.FileGroupDataSpaceId
                        FileGroupType            = $result.FileGroupType
                        FileGroupTypeDescription = $result.FileGroupTypeDescription
                        FileGroupDefault         = $result.FileGroupDefault
                        FileGroupReadOnly        = $result.FileGroupReadOnly
                    }
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaDatabaseMasterKey.ps1
function Get-DbaDatabaseMasterKey {
    <#
.SYNOPSIS
Gets specified database master key

.DESCRIPTION
Gets specified database master key

.PARAMETER SqlInstance
The target SQL Server instance

.PARAMETER SqlCredential
Allows you to login to SQL Server using alternative credentials

.PARAMETER Database
Get master key from specific database

.PARAMETER ExcludeDatabase
The database(s) to exclude - this list is auto-populated from the server

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Certificate, Database

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
Get-DbaDatabaseMasterKey -SqlInstance sql2016

Gets all master database keys

.EXAMPLE
Get-DbaDatabaseMasterKey -SqlInstance Server1 -Database db1

Gets the master key for the db1 database

#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $databases = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                $databases = $databases | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $databases) {
                if (!$db.IsAccessible) {
                    Write-Message -Level Warning -Message "Database $db is not accessible. Skipping."
                    continue
                }

                $masterkey = $db.MasterKey

                if (!$masterkey) {
                    Write-Message -Message "No master key exists in the $db database on $instance" -Target $db -Level Verbose
                    continue
                }

                Add-Member -Force -InputObject $masterkey -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                Add-Member -Force -InputObject $masterkey -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                Add-Member -Force -InputObject $masterkey -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                Add-Member -Force -InputObject $masterkey -MemberType NoteProperty -Name Database -value $db.Name

                Select-DefaultView -InputObject $masterkey -Property ComputerName, InstanceName, SqlInstance, Database, CreateDate, DateLastModified, IsEncryptedByServer
            }
        }
    }
}
tools\dbatools\functions\Get-DbaDatabasePartitionFunction.ps1
function Get-DbaDatabasePartitionFunction {
    <#
.SYNOPSIS
Gets database Partition Functions

.DESCRIPTION
Gets database Partition Functions

.PARAMETER SqlInstance
The target SQL Server instance(s)

.PARAMETER SqlCredential
Allows you to login to SQL Server using alternative credentials

.PARAMETER Database
To get users from specific database(s)

.PARAMETER ExcludeDatabase
The database(s) to exclude - this list is auto populated from the server

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Database
Author: Klaas Vandenberghe ( @PowerDbaKlaas )

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
Get-DbaDatabasePartitionFunction -SqlInstance sql2016

Gets all database Partition Functions

.EXAMPLE
Get-DbaDatabasePartitionFunction -SqlInstance Server1 -Database db1

Gets the Partition Functions for the db1 database

.EXAMPLE
Get-DbaDatabasePartitionFunction -SqlInstance Server1 -ExcludeDatabase db1

Gets the Partition Functions for all databases except db1

.EXAMPLE
'Sql1','Sql2/sqlexpress' | Get-DbaDatabasePartitionFunction

Gets the Partition Functions for the databases on Sql1 and Sql2/sqlexpress

#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $databases = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                $databases = $databases | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $databases) {
                if (!$db.IsAccessible) {
                    Write-Message -Level Warning -Message "Database $db is not accessible. Skipping."
                    continue
                }

                $partitionfunctions = $db.partitionfunctions

                if (!$partitionfunctions) {
                    Write-Message -Message "No Partition Functions exist in the $db database on $instance" -Target $db -Level Verbose
                    continue
                }

                $partitionfunctions | foreach {

                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name Database -value $db.Name

                    Select-DefaultView -InputObject $_ -Property ComputerName, InstanceName, SqlInstance, Database, CreateDate, Name, NumberOfPartitions
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaDatabasePartitionScheme.ps1
function Get-DbaDatabasePartitionScheme {
    <#
.SYNOPSIS
Gets database Partition Schemes

.DESCRIPTION
Gets database Partition Schemes

.PARAMETER SqlInstance
The target SQL Server instance(s)

.PARAMETER SqlCredential
Allows you to login to SQL Server using alternative credentials

.PARAMETER Database
To get users from specific database(s)

.PARAMETER ExcludeDatabase
The database(s) to exclude - this list is auto populated from the server

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Database
Author: Klaas Vandenberghe ( @PowerDbaKlaas )

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
Get-DbaDatabasePartitionScheme -SqlInstance sql2016

Gets all database Partition Schemes

.EXAMPLE
Get-DbaDatabasePartitionScheme -SqlInstance Server1 -Database db1

Gets the Partition Schemes for the db1 database

.EXAMPLE
Get-DbaDatabasePartitionScheme -SqlInstance Server1 -ExcludeDatabase db1

Gets the Partition Schemes for all databases except db1

.EXAMPLE
'Sql1','Sql2/sqlexpress' | Get-DbaDatabasePartitionScheme

Gets the Partition Schemes for the databases on Sql1 and Sql2/sqlexpress

#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $databases = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                $databases = $databases | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $databases) {
                if (!$db.IsAccessible) {
                    Write-Message -Level Warning -Message "Database $db is not accessible. Skipping."
                    continue
                }

                $PartitionSchemes = $db.PartitionSchemes

                if (!$PartitionSchemes) {
                    Write-Message -Message "No Partition Schemes exist in the $db database on $instance" -Target $db -Level Verbose
                    continue
                }

                $PartitionSchemes | foreach {

                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name Database -value $db.Name

                    Select-DefaultView -InputObject $_ -Property ComputerName, InstanceName, SqlInstance, Database, Name, PartitionFunction
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaDatabaseSpace.ps1
function Get-DbaDatabaseSpace {
    <#
        .SYNOPSIS
            Returns database file space information for database files on a SQL instance.

        .DESCRIPTION
            This function returns database file space information for a SQL Instance or group of SQL Instances. Information is based on a query against sys.database_files and the FILEPROPERTY function to query and return information.

            File free space script borrowed and modified from Glenn Berry's DMV scripts (http://www.sqlskills.com/blogs/glenn/category/dmv-queries/)

        .PARAMETER SqlInstance
            Specifies the SQL Server instance(s) to scan.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies the database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies the database(s) to exclude from processing. Options for this list are auto-populated from the server.

        .PARAMETER IncludeSystemDBs
            If this switch is enabled, system databases will be processed. By default, only user databases are processed.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database, Space, Storage
            Author: Michael Fal (@Mike_Fal), http://mikefal.net
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaDatabaseSpace

        .EXAMPLE
            Get-DbaDatabaseSpace -SqlInstance localhost

            Returns all user database files and free space information for the localhost.

        .EXAMPLE
            Get-DbaDatabaseSpace -SqlInstance localhost | Where-Object {$_.PercentUsed -gt 80}

            Returns all user database files and free space information for the local host. Filters the output object by any files that have a percent used of greater than 80%.

        .EXAMPLE
            'localhost','localhost\namedinstance' | Get-DbaDatabaseSpace

            Returns all user database files and free space information for the localhost and localhost\namedinstance SQL Server instances. Processes data via the pipeline.

        .EXAMPLE
            Get-DbaDatabaseSpace -SqlInstance localhost -Database db1, db2

            Returns database files and free space information for the db1 and db2 on localhost.
    #>
    [CmdletBinding()]
    param ([parameter(ValueFromPipeline, Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [System.Management.Automation.PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$IncludeSystemDBs,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Write-Message -Level System -Message "Bound parameters: $($PSBoundParameters.Keys -join ", ")."

        $sql = "SELECT SERVERPROPERTY('MachineName') AS ComputerName,
                                   ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
                                   SERVERPROPERTY('ServerName') AS SqlInstance,
                    DB_NAME() as DBName
                    ,f.name AS [FileName]
                    ,fg.name AS [Filegroup]
                    ,f.physical_name AS [PhysicalName]
                    ,f.type_desc AS [FileType]
                    ,CAST(CAST(FILEPROPERTY(f.name, 'SpaceUsed') AS int)/128.0 AS FLOAT) as [UsedSpaceMB]
                    ,CAST(f.size/128.0 - CAST(FILEPROPERTY(f.name, 'SpaceUsed') AS int)/128.0 AS FLOAT) AS [FreeSpaceMB]
                    ,CAST((f.size/128.0) AS FLOAT) AS [FileSizeMB]
                    ,CAST((FILEPROPERTY(f.name, 'SpaceUsed')/(f.size/1.0)) * 100 as FLOAT) as [PercentUsed]
                    ,CAST((f.growth/128.0) AS FLOAT) AS [GrowthMB]
                    ,CASE is_percent_growth WHEN 1 THEN 'pct' WHEN 0 THEN 'MB' ELSE 'Unknown' END AS [GrowthType]
                    ,CASE f.max_size WHEN -1 THEN 2147483648. ELSE CAST((f.max_size/128.0) AS FLOAT) END AS [MaxSizeMB]
                    ,CAST((f.size/128.0) AS FLOAT) - CAST(CAST(FILEPROPERTY(f.name, 'SpaceUsed') AS int)/128.0 AS FLOAT) AS [SpaceBeforeAutoGrow]
                    ,CASE f.max_size	WHEN (-1)
                                        THEN CAST(((2147483648.) - CAST(FILEPROPERTY(f.name, 'SpaceUsed') AS int))/128.0 AS FLOAT)
                                        ELSE CAST((f.max_size - CAST(FILEPROPERTY(f.name, 'SpaceUsed') AS int))/128.0 AS FLOAT)
                                        END AS [SpaceBeforeMax]
                    ,CASE f.growth	WHEN 0 THEN 0.00
                                    ELSE	CASE f.is_percent_growth	WHEN 0
                                                    THEN	CASE f.max_size
                                                            WHEN (-1)
                                                            THEN CAST(((((2147483648.)-f.Size)/f.Growth)*f.Growth)/128.0 AS FLOAT)
                                                            ELSE CAST((((f.max_size-f.Size)/f.Growth)*f.Growth)/128.0 AS FLOAT)
                                                            END
                                                    WHEN 1
                                                    THEN	CASE f.max_size
                                                            WHEN (-1)
                                                            THEN CAST(CONVERT([int],f.Size*power((1)+CONVERT([float],f.Growth)/(100),CONVERT([int],log10(CONVERT([float],(2147483648.))/CONVERT([float],f.Size))/log10((1)+CONVERT([float],f.Growth)/(100)))))/128.0 AS FLOAT)
                                                            ELSE CAST(CONVERT([int],f.Size*power((1)+CONVERT([float],f.Growth)/(100),CONVERT([int],log10(CONVERT([float],f.Max_Size)/CONVERT([float],f.Size))/log10((1)+CONVERT([float],f.Growth)/(100)))))/128.0 AS FLOAT)
                                                            END
                                                    ELSE (0)
                                                    END
                                    END AS [PossibleAutoGrowthMB]
                    , CASE f.max_size	WHEN -1 THEN 0
                                        ELSE CASE f.growth
                                                WHEN 0 THEN (f.max_size - f.size)/128
                                                ELSE	CASE f.is_percent_growth
                                                        WHEN 0
                                                        THEN CAST((f.max_size - f.size - (	CONVERT(FLOAT,FLOOR((f.max_size-f.Size)/f.Growth)*f.Growth)))/128.0 AS FLOAT)
                                                        ELSE CAST((f.max_size - f.size - (	CONVERT([int],f.Size*power((1)+CONVERT([float],f.Growth)/(100),CONVERT([int],log10(CONVERT([float],f.Max_Size)/CONVERT([float],f.Size))/log10((1)+CONVERT([float],f.Growth)/(100)))))))/128.0 AS FLOAT)
                                                        END
                                                END
                                    END AS [UnusableSpaceMB]

                FROM sys.database_files AS f WITH (NOLOCK)
                LEFT OUTER JOIN sys.filegroups AS fg WITH (NOLOCK)
                ON f.data_space_id = fg.data_space_id"
    }

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level VeryVerbose -Message "Connecting to $instance." -Target $instance
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failed to process Instance $Instance." -ErrorRecord $_ -Target $instance -Continue
            }

            if ($server.VersionMajor -lt 9) {
                Write-Message -Level Warning -Message "SQL Server 2000 not supported. $server skipped."
                continue
            }

            #If IncludeSystemDBs is true, include systemdbs
            #look at all databases, online/offline/accessible/inaccessible and tell user if a db can't be queried.
            try {
                if (Test-Bound "Database") {
                    $dbs = $server.Databases | Where-Object Name -In $Database
                }
                elseif ($IncludeSystemDBs) {
                    $dbs = $server.Databases | Where-Object IsAccessible
                }
                else {
                    $dbs = $server.Databases | Where-Object { $_.IsAccessible -and $_.IsSystemObject -eq 0 }
                }

                if (Test-Bound "ExcludeDatabase") {
                    $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
                }
            }
            catch {
                Stop-Function -Message "Unable to gather databases for $instance." -ErrorRecord $_ -Continue
            }

            foreach ($db in $dbs) {
                try {
                    Write-Message -Level Verbose -Message "Querying $instance - $db."
                    If ($db.status -ne 'Normal' -or $db.IsAccessible -eq $false) {
                        Write-Message -Level Warning -Message "$db is not accessible." -Target $db
                        continue
                    }
                    #Execute query against individual database and add to output
                    foreach ($row in ($db.ExecuteWithResults($sql)).Tables.Rows) {
                        if ($row.UsedSpaceMB -is [System.DBNull]) {
                            $UsedMB = 0
                        }
                        else {
                            $UsedMB = [Math]::Round($row.UsedSpaceMB)
                        }
                        if ($row.FreeSpaceMB -is [System.DBNull]) {
                            $FreeMB = 0
                        }
                        else {
                            $FreeMB = [Math]::Round($row.FreeSpaceMB)
                        }
                        if ($row.PercentUsed -is [System.DBNull]) {
                            $PercentUsed = 0
                        }
                        else {
                            $PercentUsed = [Math]::Round($row.PercentUsed)
                        }
                        if ($row.SpaceBeforeMax -is [System.DBNull]) {
                            $SpaceUntilMax = 0
                        }
                        else {
                            $SpaceUntilMax = [Math]::Round($row.SpaceBeforeMax)
                        }
                        if ($row.UnusableSpaceMB -is [System.DBNull]) {
                            $UnusableSpace = 0
                        }
                        else {
                            $UnusableSpace = [Math]::Round($row.UnusableSpaceMB)
                        }

                        [pscustomobject]@{
                            ComputerName         = $server.ComputerName
                            InstanceName         = $server.ServiceName
                            SqlInstance          = $server.DomainInstanceName
                            Database             = $row.DBName
                            FileName             = $row.FileName
                            FileGroup            = $row.FileGroup
                            PhysicalName         = $row.PhysicalName
                            FileType             = $row.FileType
                            UsedSpaceMB          = $UsedMB
                            FreeSpaceMB          = $FreeMB
                            FileSizeMB           = $row.FileSizeMB
                            PercentUsed          = $PercentUsed
                            AutoGrowth           = $row.GrowthMB
                            AutoGrowType         = $row.GrowthType
                            SpaceUntilMaxSizeMB  = $SpaceUntilMax
                            AutoGrowthPossibleMB = $row.PossibleAutoGrowthMB
                            UnusableSpaceMB      = $UnusableSpace
                        }
                    }
                }
                catch {
                    Stop-Function -Message "Unable to query $instance - $db." -Target $db -ErrorRecord $_ -Continue
                }
            }
        }
    }

    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Get-DbaDatabaseFreeSpace
    }
}

tools\dbatools\functions\Get-DbaDatabaseState.ps1
#ValidationTags#Messaging,FlowControl,Pipeline#
function Get-DbaDatabaseState {
    <#
.SYNOPSIS
Gets various options for databases, hereby called "states"

.DESCRIPTION
Gets some common "states" on databases:
 - "RW" options : READ_ONLY or READ_WRITE
 - "Status" options : ONLINE, OFFLINE, EMERGENCY, RESTORING
 - "Access" options : SINGLE_USER, RESTRICTED_USER, MULTI_USER

Returns an object with SqlInstance, Database, RW, Status, Access

.PARAMETER SqlInstance
The SQL Server that you're connecting to

.PARAMETER SqlCredential
Credential object used to connect to the SQL Server as a different user

.PARAMETER Database
The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

.PARAMETER ExcludeDatabase
The database(s) to exclude - this list is auto-populated from the server

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Database
Author: niphlod

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Get-DbaDatabaseState

.EXAMPLE
Get-DbaDatabaseState -SqlInstance sqlserver2014a

Gets options for all databases of the sqlserver2014a instance

.EXAMPLE
Get-DbaDatabaseState -SqlInstance sqlserver2014a -Database HR, Accounting

Gets options for both HR and Accounting database of the sqlserver2014a instance

.EXAMPLE
Get-DbaDatabaseState -SqlInstance sqlserver2014a -Exclude HR

Gets options for all databases of the sqlserver2014a instance except HR

.EXAMPLE
'sqlserver2014a', 'sqlserver2014b' | Get-DbaDatabaseState

Gets options for all databases of sqlserver2014a and sqlserver2014b instances

#>
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseLiteralInitializerForHashtable", "")]
    [CmdletBinding()]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {

        $DbStatesQuery = @'
SELECT
Name   = name,
Access = user_access_desc,
Status = state_desc,
RW     = CASE WHEN is_read_only = 0 THEN 'READ_WRITE' ELSE 'READ_ONLY' END
FROM sys.databases
'@

    }
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            $dbStates = $server.Query($DbStatesQuery)
            $dbs = $dbStates | Where-Object { @('master', 'model', 'msdb', 'tempdb', 'distribution') -notcontains $_.Name }
            if ($Database) {
                $dbs = $dbs | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }
            # "normal" hashtable doesn't account for case sensitivity
            $dbStatesHash = New-Object -TypeName System.Collections.Hashtable
            foreach ($db in $dbStates) {
                $dbStatesHash.Add($db.Name, [pscustomobject]@{
                        Access = $db.Access
                        Status = $db.Status
                        RW     = $db.RW
                    })
            }
            foreach ($db in $dbs) {
                $db_status = $dbStatesHash[$db.Name]
                [PSCustomObject]@{
                    SqlInstance  = $server.Name
                    InstanceName = $server.ServiceName
                    ComputerName = $server.ComputerName
                    DatabaseName = $db.Name
                    RW           = $db_status.RW
                    Status       = $db_status.Status
                    Access       = $db_status.Access
                    Database     = $server.Databases[$db.Name]
                } | Select-DefaultView -ExcludeProperty Database
            }
        }
    }
}

tools\dbatools\functions\Get-DbaDatabaseUdf.ps1
function Get-DbaDatabaseUdf {
    <#
.SYNOPSIS
Gets database User Defined Functions

.DESCRIPTION
Gets database User Defined Functions

.PARAMETER SqlInstance
The target SQL Server instance(s)

.PARAMETER SqlCredential
Allows you to login to SQL Server using alternative credentials

.PARAMETER Database
To get User Defined Functions from specific database(s)

.PARAMETER ExcludeDatabase
The database(s) to exclude - this list is auto populated from the server

.PARAMETER ExcludeSystemUdf
This switch removes all system objects from the UDF collection

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Security, Database
Author: Klaas Vandenberghe ( @PowerDbaKlaas )

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
Get-DbaDatabaseUdf -SqlInstance sql2016

Gets all database User Defined Functions

.EXAMPLE
Get-DbaDatabaseUdf -SqlInstance Server1 -Database db1

Gets the User Defined Functions for the db1 database

.EXAMPLE
Get-DbaDatabaseUdf -SqlInstance Server1 -ExcludeDatabase db1

Gets the User Defined Functions for all databases except db1

.EXAMPLE
Get-DbaDatabaseUdf -SqlInstance Server1 -ExcludeSystemUdf

Gets the User Defined Functions for all databases that are not system objects (there can be 100+ system User Defined Functions in each DB)

.EXAMPLE
'Sql1','Sql2/sqlexpress' | Get-DbaDatabaseUdf

Gets the User Defined Functions for the databases on Sql1 and Sql2/sqlexpress

#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$ExcludeSystemUdf,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $databases = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                $databases = $databases | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $databases) {

                $UserDefinedFunctions = $db.UserDefinedFunctions

                if (!$UserDefinedFunctions) {
                    Write-Message -Message "No User Defined Functions exist in the $db database on $instance" -Target $db -Level Verbose
                    continue
                }
                if (Test-Bound -ParameterName ExcludeSystemUdf) {
                    $UserDefinedFunctions = $UserDefinedFunctions | Where-Object { $_.IsSystemObject -eq $false }
                }

                $UserDefinedFunctions | foreach {

                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name Database -value $db.Name

                    Select-DefaultView -InputObject $_ -Property ComputerName, InstanceName, SqlInstance, Database, Schema, CreateDate, DateLastModified, Name, DataType
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaDatabaseUser.ps1
function Get-DbaDatabaseUser {
    <#
.SYNOPSIS
Gets database users

.DESCRIPTION
Gets database users

.PARAMETER SqlInstance
The target SQL Server instance(s)

.PARAMETER SqlCredential
Allows you to login to SQL Server using alternative credentials

.PARAMETER Database
To get users from specific database(s)

.PARAMETER ExcludeDatabase
The database(s) to exclude - this list is auto populated from the server

.PARAMETER ExcludeSystemUser
This switch removes all system objects from the user collection

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Security, Database
Author: Klaas Vandenberghe ( @PowerDbaKlaas )

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
Get-DbaDatabaseUser -SqlInstance sql2016

Gets all database users

.EXAMPLE
Get-DbaDatabaseUser -SqlInstance Server1 -Database db1

Gets the users for the db1 database

.EXAMPLE
Get-DbaDatabaseUser -SqlInstance Server1 -ExcludeDatabase db1

Gets the users for all databases except db1

.EXAMPLE
Get-DbaDatabaseUser -SqlInstance Server1 -ExcludeSystemUser

Gets the users for all databases that are not system objects, like 'dbo', 'guest' or 'INFORMATION_SCHEMA'

.EXAMPLE
'Sql1','Sql2/sqlexpress' | Get-DbaDatabaseUser

Gets the users for the databases on Sql1 and Sql2/sqlexpress

#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$ExcludeSystemUser,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $databases = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                $databases = $databases | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $databases) {

                $users = $db.users

                if (!$users) {
                    Write-Message -Message "No users exist in the $db database on $instance" -Target $db -Level Verbose
                    continue
                }
                if (Test-Bound -ParameterName ExcludeSystemUser) {
                    $users = $users | Where-Object { $_.IsSystemObject -eq $false }
                }

                $users | foreach {

                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name Database -value $db.Name

                    Select-DefaultView -InputObject $_ -Property ComputerName, InstanceName, SqlInstance, Database, CreateDate, DateLastModified, Name, Login, LoginType, AuthenticationType, State, HasDbAccess, DefaultSchema
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaDatabaseView.ps1
function Get-DbaDatabaseView {
    <#
        .SYNOPSIS
            Gets database views for each SqlInstance.

        .DESCRIPTION
            Gets database views for each SqlInstance.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            To get views from specific database(s) - this list is auto populated from the server.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto populated from the server.

        .PARAMETER ExcludeSystemView
            This switch removes all system objects from the view collection.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Security, Database
            Author: Klaas Vandenberghe ( @PowerDbaKlaas )

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            Get-DbaDatabaseView -SqlInstance sql2016

            Gets all database views

        .EXAMPLE
            Get-DbaDatabaseView -SqlInstance Server1 -Database db1

            Gets the views for the db1 database

        .EXAMPLE
            Get-DbaDatabaseView -SqlInstance Server1 -ExcludeDatabase db1

            Gets the views for all databases except db1

        .EXAMPLE
            Get-DbaDatabaseView -SqlInstance Server1 -ExcludeSystemView

            Gets the views for all databases that are not system objects (there can be 400+ system views in each DB)

        .EXAMPLE
            'Sql1','Sql2/sqlexpress' | Get-DbaDatabaseView

            Gets the views for the databases on Sql1 and Sql2/sqlexpress

#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$ExcludeSystemView,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $databases = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                $databases = $databases | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $databases) {
                $views = $db.views

                if (!$views) {
                    Write-Message -Message "No views exist in the $db database on $instance" -Target $db -Level Verbose
                    continue
                }
                if (Test-Bound -ParameterName ExcludeSystemView) {
                    $views = $views | Where-Object { $_.IsSystemObject -eq $false }
                }

                $views | Foreach-Object {

                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                    Add-Member -Force -InputObject $_ -MemberType NoteProperty -Name Database -value $db.Name

                    Select-DefaultView -InputObject $_ -Property ComputerName, InstanceName, SqlInstance, Database, Schema, CreateDate, DateLastModified, Name
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaDbCertificate.ps1
function Get-DbaDbCertificate {
    <#
.SYNOPSIS
Gets database certificates

.DESCRIPTION
Gets database certificates

.PARAMETER SqlInstance
The target SQL Server instance

.PARAMETER SqlCredential
Allows you to login to SQL Server using alternative credentials

.PARAMETER Database
Get certificate from specific database

.PARAMETER ExcludeDatabase
Database(s) to ignore when retrieving certificates.

.PARAMETER Certificate
Get specific certificate

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Certificate
Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
Get-DbaDbCertificate -SqlInstance sql2016

Gets all certificates

.EXAMPLE
Get-DbaDbCertificate -SqlInstance Server1 -Database db1

Gets the certificate for the db1 database

.EXAMPLE
Get-DbaDbCertificate -SqlInstance Server1 -Database db1 -Certificate cert1

Gets the cert1 certificate within the db1 database

#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [object[]]$Certificate,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Get-DbaDatabaseCertificate
    }
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $databases = Get-DbaDatabase -SqlInstance $server | Where-Object IsAccessible

            if ($Database) {
                $databases = $databases | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $databases) {
                if (!$db.IsAccessible) {
                    Write-Message -Level Warning -Message "$db is not accessible, skipping"
                    continue
                }
                $dbName = $db.Name
                $currentdb = $server.Databases[$dbName]

                if ($null -eq $currentdb) {
                    Write-Message -Message "Database '$db' does not exist on $instance" -Target $currentdb -Level Verbose
                    continue
                }

                if ($null -eq $currentdb.Certificates) {
                    Write-Message -Message "No certificate exists in the $db database on $instance" -Target $currentdb -Level Verbose
                    continue
                }

                $certs = $currentdb.Certificates
                if ($Certificate) {
                    $certs = $certs | Where-Object Name -in $Certificate
                }

                foreach ($cert in $certs) {

                    Add-Member -Force -InputObject $cert -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                    Add-Member -Force -InputObject $cert -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                    Add-Member -Force -InputObject $cert -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                    Add-Member -Force -InputObject $cert -MemberType NoteProperty -Name Database -value $currentdb.Name

                    Select-DefaultView -InputObject $cert -Property ComputerName, InstanceName, SqlInstance, Database, Name, Subject, StartDate, ActiveForServiceBrokerDialog, ExpirationDate, Issuer, LastBackupDate, Owner, PrivateKeyEncryptionType, Serial
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaDbCheckConstraint.ps1
function Get-DbaDbCheckConstraint {
    <#
        .SYNOPSIS
            Gets database Check constraints.

        .DESCRIPTION
            Gets database Checks constraints.

        .PARAMETER SqlInstance
            The target SQL Server instance(s)

        .PARAMETER SqlCredential
            Allows you to login to SQL Server using alternative credentials

        .PARAMETER Database
            To get Checks from specific database(s)

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto populated from the server

        .PARAMETER ExcludeSystemTable
            This switch removes all system objects from the table collection

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database
            Author: Cláudio Silva ( @ClaudioESSilva | https://claudioessilva.eu)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            Get-DbaDbCheckConstraint -SqlInstance sql2016

            Gets all database check constraints.

        .EXAMPLE
            Get-DbaDbCheckConstraint -SqlInstance Server1 -Database db1

            Gets the check constraints for the db1 database.

        .EXAMPLE
            Get-DbaDbCheckConstraint -SqlInstance Server1 -ExcludeDatabase db1

            Gets the check constraints for all databases except db1.

        .EXAMPLE
            Get-DbaDbCheckConstraint -SqlInstance Server1 -ExcludeSystemTable

            Gets the check constraints for all databases that are not system objects.

        .EXAMPLE
            'Sql1','Sql2/sqlexpress' | Get-DbaDbCheckConstraint

            Gets the check constraints for the databases on Sql1 and Sql2/sqlexpress.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$ExcludeSystemTable,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $databases = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                $databases = $databases | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $databases) {
                if (!$db.IsAccessible) {
                    Write-Message -Level Warning -Message "Database $db is not accessible. Skipping."
                    continue
                }

                foreach($tbl in $db.Tables) {
                    if ( (Test-Bound -ParameterName ExcludeSystemTable) -and $tbl.IsSystemObject ) {
                        continue
                    }

                    if ($tbl.Checks.Count -eq 0) {
                        Write-Message -Message "No Checks exist in $tbl table on the $db database on $instance" -Target $tbl -Level Verbose
                        continue
                    }

                    foreach ($ck in $tbl.Checks) {
                        Add-Member -Force -InputObject $ck -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                        Add-Member -Force -InputObject $ck -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                        Add-Member -Force -InputObject $ck -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                        Add-Member -Force -InputObject $ck -MemberType NoteProperty -Name Database -value $db.Name

                        $defaults = 'ComputerName', 'InstanceName', 'SqlInstance', 'Database', 'Parent', 'ID', 'CreateDate',
                        'DateLastModified', 'Name', 'IsEnabled', 'IsChecked', 'NotForReplication', 'Text', 'State'
                        Select-DefaultView -InputObject $ck -Property $defaults
                    }
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaDbCompression.ps1
function Get-DbaDbCompression {
    <#
        .SYNOPSIS
            Gets tables and indexes size and current compression settings.

        .DESCRIPTION
            This function gets the current size and compression for all objects in the specified database(s), if no database is specified it will return all objects in all user databases.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process - this list is auto populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto populated from the server.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Jess Pomfret (@jpomfret jesspomfret.com)
            Tags: Compression, Table, Database
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            Get-DbaDbCompression -SqlInstance localhost

            Returns objects size and current compression level for all user databases.

        .EXAMPLE
            Get-DbaDbCompression -SqlInstance localhost -Database TestDatabase

            Returns objects size and current compression level for objects within the TestDatabase database.

            .EXAMPLE
            Get-DbaDbCompression -SqlInstance localhost -ExcludeDatabase TestDatabases

            Returns objects size and current compression level for objects in all databases except the TestDatabase database.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level VeryVerbose -Message "Connecting to $instance" -Target $instance
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failed to process Instance $Instance" -ErrorRecord $_ -Target $instance -Continue
            }

            try {
                $dbs = $server.Databases | Where-Object { $_.IsAccessible -and $_.IsSystemObject -eq 0 }

                if ($Database) {
                    $dbs = $dbs | Where-Object { $_.Name -In $Database }
                }

                if ($ExcludeDatabase) {
                    $dbs = $dbs | Where-Object { $_.Name -NotIn $ExcludeDatabase }
                }
            }
            catch {
                Stop-Function -Message "Unable to gather list of databases for $instance" -Target $instance -ErrorRecord $_ -Continue
            }

            foreach ($db in $dbs) {
                try {
                    foreach ($obj in $server.Databases[$($db.name)].Tables) {
                        if ($obj.HasHeapIndex) {
                            foreach ($p in $obj.PhysicalPartitions) {
                                [pscustomobject]@{
                                    ComputerName        = $server.ComputerName
                                    InstanceName        = $server.ServiceName
                                    SqlInstance         = $server.DomainInstanceName
                                    Database            = $db.Name
                                    Schema              = $obj.Schema
                                    TableName           = $obj.Name
                                    IndexName           = $null
                                    Partition           = $p.PartitionNumber
                                    IndexID             = 0
                                    IndexType           = "Heap"
                                    DataCompression     = $p.DataCompression
                                    SizeCurrent         = [dbasize]($obj.DataSpaceUsed * 1024)
                                    RowCount            = $obj.RowCount
                                }
                            }
                        }

                        foreach ($index in $obj.Indexes) {
                            foreach ($p in $index.PhysicalPartitions) {
                                [pscustomobject]@{
                                    ComputerName        = $server.ComputerName
                                    InstanceName        = $server.ServiceName
                                    SqlInstance         = $server.DomainInstanceName
                                    Database            = $db.Name
                                    Schema              = $obj.Schema
                                    TableName           = $obj.Name
                                    IndexName           = $index.Name
                                    Partition           = $p.PartitionNumber
                                    IndexID             = $index.ID
                                    IndexType           = $index.IndexType
                                    DataCompression     = $p.DataCompression
                                    SizeCurrent         = if($index.IndexType -eq "ClusteredIndex") { [dbasize]($obj.DataSpaceUsed * 1024) } else { [dbasize]($index.SpaceUsed * 1024) }
                                    RowCount            = $p.RowCount
                                }
                            }
                        }

                    }
                }
                catch {
                    Stop-Function -Message "Unable to query $instance - $db" -Target $db -ErrorRecord $_ -Continue
                }

            }
        }
    }
}
tools\dbatools\functions\Get-DbaDbExtentDiff.ps1
function Get-DbaDbExtentDiff {
    <#
        .SYNOPSIS
            What percentage of a database has changed since the last full backup

        .DESCRIPTION
            This is only an implementation of the script created by Paul S. Randal to find what percentage of a database has changed since the last full backup.
            https://www.sqlskills.com/blogs/paul/new-script-how-much-of-the-database-has-changed-since-the-last-full-backup/

        .PARAMETER SqlInstance
            The target SQL Server instance

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto-populated from the server

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Backup, Database
            Author: Viorel Ciucu, [email protected], cviorel.com

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: GNU GPL v3 https://opensource.org/licenses/GPL-3.0

        .LINK
            http://dbatools.io/Get-DbaDbExtentDiff

        .EXAMPLE
            Get the changes for the DBA database.
            Get-DbaDbExtentDiff -SqlInstance SQL2016 -Database DBA

        .EXAMPLE
            Get the changes for the DB01 database on multiple servers.
            Get-DbaDbExtentDiff -SqlInstance $SQL2017N1, $SQL2017N2, $SQL2016 -Database DB01 -SqlCredential $Cred
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias('ServerInstance', 'SqlServer')]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$EnableException
    )

    begin {
        $rex = [regex]':(?<extent>[\d]+)\)'
        function Get-DbaExtent ([string[]]$field) {
            $res = 0
            foreach ($f in $field) {
                $extents = $rex.Matches($f)
                if ($extents.Count -eq 1) {
                    $res += 1
                }
                else {
                    $pages = [int]$extents[1].Groups['extent'].Value - [int]$extents[0].Groups['extent'].Value
                    $res += $pages / 8 + 1
                }
            }
            return $res
        }
    }

    process {

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-DbaInstance -SqlInstance $instance -SqlCredential $SqlCredential -NonPooled
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $dbs = $server.Databases

            if ($Database) {
                $dbs = $dbs | Where-Object Name -In $Database
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            $sourcedbs = @()
            foreach ($db in $dbs) {
                if ($db.IsAccessible -ne $true) {
                    Write-Message -Level Verbose -Message "$db is not accessible on $instance, skipping"
                }
                else {
                    $sourcedbs += $db
                }
            }

            if ($server.VersionMajor -ge 14 ) {
                foreach ($db in $sourcedbs) {
                    $DBCCPageQueryDMV = "
                        SELECT
                        SUM(total_page_count) / 8 as [ExtentsTotal],
                        SUM(modified_extent_page_count) / 8 as [ExtentsChanged],
                        100.0 * SUM(modified_extent_page_count)/SUM(total_page_count) as [ChangedPerc]
                        FROM sys.dm_db_file_space_usage
                    "
                    $DBCCPageResults = $server.Query($DBCCPageQueryDMV, $db.Name)
                    [pscustomobject]@{
                        ComputerName   = $server.ComputerName
                        InstanceName   = $server.ServiceName
                        SqlInstance    = $server.DomainInstanceName
                        DatabaseName   = $db.Name
                        ExtentsTotal   = $DBCCPageResults.ExtentsTotal
                        ExtentsChanged = $DBCCPageResults.ExtentsChanged
                        ChangedPerc    = [math]::Round($DBCCPageResults.ChangedPerc, 2)
                    }
                }
            }
            else {
                $MasterFilesQuery = "
                        SELECT [file_id], [size], database_id, db_name(database_id) as dbname FROM master.sys.master_files
                        WHERE [type_desc] = N'ROWS'
                    "
                $MasterFiles = $server.Query($MasterFilesQuery)
                $MasterFiles = $MasterFiles | Where-Object dbname -In $sourcedbs.Name
                $MasterFilesGrouped = $MasterFiles | Group-Object -Property dbname

                foreach ($db in $MasterFilesGrouped) {
                    $sizeTotal = 0
                    $dbExtents = @()
                    foreach ($results in $db.Group) {
                        $extentID = 0
                        $sizeTotal = $sizeTotal + $results.size / 8
                        while ($extentID -lt $results.size) {
                            $pageID = $extentID + 6
                            $DBCCPageQuery = "DBCC PAGE ('$($results.dbname)', $($results.file_id), $pageID, 3)  WITH TABLERESULTS, NO_INFOMSGS"
                            $DBCCPageResults = $server.Query($DBCCPageQuery)
                            $dbExtents += $DBCCPageResults | Where-Object { $_.VALUE -eq '    CHANGED' -And $_.ParentObject -like 'DIFF_MAP*'}
                            $extentID = $extentID + 511232
                        }
                    }
                    $extents = Get-DbaExtent $dbExtents.Field
                    [pscustomobject]@{
                        ComputerName   = $server.ComputerName
                        InstanceName   = $server.ServiceName
                        SqlInstance    = $server.DomainInstanceName
                        DatabaseName   = $db.Name
                        ExtentsTotal   = $sizeTotal
                        ExtentsChanged = $extents
                        ChangedPerc    = [math]::Round(($extents / $sizeTotal * 100), 2)
                    }
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaDbForeignKey.ps1
function Get-DbaDbForeignKey {
    <#
        .SYNOPSIS
            Gets database Foreign Keys.

        .DESCRIPTION
            Gets database Foreign Keys.

        .PARAMETER SqlInstance
            The target SQL Server instance(s)

        .PARAMETER SqlCredential
            Allows you to login to SQL Server using alternative credentials

        .PARAMETER Database
            To get Foreign Keys from specific database(s)

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto populated from the server

        .PARAMETER ExcludeSystemTable
            This switch removes all system objects from the tables collection

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database,ForeignKey, Table
            Author: Cláudio Silva ( @ClaudioESSilva | https://claudioessilva.eu)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            Get-DbaDbForeignKey -SqlInstance sql2016

            Gets all database Foreign Keys.

        .EXAMPLE
            Get-DbaDbForeignKey -SqlInstance Server1 -Database db1

            Gets the Foreign Keys for the db1 database.

        .EXAMPLE
            Get-DbaDbForeignKey -SqlInstance Server1 -ExcludeDatabase db1

            Gets the Foreign Keys for all databases except db1.

        .EXAMPLE
            Get-DbaDbForeignKey -SqlInstance Server1 -ExcludeSystemTable

            Gets the Foreign Keys from all tables that are not system objects from all databases.

        .EXAMPLE
            'Sql1','Sql2/sqlexpress' | Get-DbaDbForeignKey

            Gets the Foreign Keys for the databases on Sql1 and Sql2/sqlexpress.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$ExcludeSystemTable,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $databases = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                $databases = $databases | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $databases) {
                if (!$db.IsAccessible) {
                    Write-Message -Level Warning -Message "Database $db is not accessible. Skipping."
                    continue
                }

                foreach($tbl in $db.Tables) {
                    if ( (Test-Bound -ParameterName ExcludeSystemTable) -and $tbl.IsSystemObject ) {
                        continue
                    }

                    if ($tbl.ForeignKeys.Count -eq 0) {
                        Write-Message -Message "No Foreign Keys exist in $tbl table on the $db database on $instance" -Target $tbl -Level Verbose
                        continue
                    }

                    foreach ($fk in $tbl.ForeignKeys) {
                        Add-Member -Force -InputObject $fk -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                        Add-Member -Force -InputObject $fk -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                        Add-Member -Force -InputObject $fk -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                        Add-Member -Force -InputObject $fk -MemberType NoteProperty -Name Database -value $db.Name

                        $defaults = 'ComputerName', 'InstanceName', 'SqlInstance', 'Database', 'Table', 'ID', 'CreateDate',
                        'DateLastModified', 'Name', 'IsEnabled', 'IsChecked', 'NotForReplication', 'ReferencedKey', 'ReferencedTable', 'ReferencedTableSchema'
                        Select-DefaultView -InputObject $fk -Property $defaults
                    }
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaDbMailHistory.ps1
function Get-DbaDbMailHistory {
    <#
    .SYNOPSIS
        Gets the history of mail sent from a SQL instance

    .DESCRIPTION
        Gets the history of mail sent from a SQL instance

    .PARAMETER SqlInstance
        The SQL Server instance, or instances.

    .PARAMETER SqlCredential
        Allows you to login to servers using SQL Logins as opposed to Windows Auth/Integrated/Trusted.

    .PARAMETER Since
    Datetime object used to narrow the results to the send request date

    .PARAMETER Status
    Narrow the results by status. Valid values include Unsent, Sent, Failed and Retrying

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Tags: Logging
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Get-DbaDbMailHistory

    .EXAMPLE
        Get-DbaDbMailHistory -SqlInstance sql01\sharepoint

        Returns the entire dbmail history on sql01\sharepoint

    .EXAMPLE
        Get-DbaDbMailHistory -SqlInstance sql01\sharepoint | Select *

        Returns the entire dbmail history on sql01\sharepoint then return a bunch more columns

    .EXAMPLE
        $servers = "sql2014","sql2016", "sqlcluster\sharepoint"
        $servers | Get-DbaDbMailHistory

        Returns the all dbmail history for "sql2014","sql2016" and "sqlcluster\sharepoint"

#>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [DateTime]$Since,
        [ValidateSet('Unsent', 'Sent', 'Failed', 'Retrying')]
        [string]$Status,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category Connectiondbmail -dbmailRecord $_ -Target $instance -Continue
            }

            $sql = "SELECT SERVERPROPERTY('MachineName') AS ComputerName,
                    ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
                    SERVERPROPERTY('ServerName') AS SqlInstance,
                    mailitem_id as MailItemId,
                    a.profile_id as ProfileId,
                    p.name as Profile,
                    recipients as Recipients,
                    copy_recipients as CopyRecipients,
                    blind_copy_recipients as BlindCopyRecipients,
                    subject as Subject,
                    body as Body,
                    body_format as BodyFormat,
                    importance as Importance,
                    sensitivity as Sensitivity,
                    file_attachments as FileAttachments,
                    attachment_encoding as AttachmentEncoding,
                    query as Query,
                    execute_query_database as ExecuteQueryDatabase,
                    attach_query_result_as_file as AttachQueryResultAsFile,
                    query_result_header as QueryResultHeader,
                    query_result_width as QueryResultWidth,
                    query_result_separator as QueryResultSeparator,
                    exclude_query_output as ExcludeQueryOutput,
                    append_query_error as AppendQueryError,
                    send_request_date as SendRequestDate,
                    send_request_user as SendRequestUser,
                    sent_account_id as SentAccountId,
                    CASE sent_status
                    WHEN 'unsent' THEN 'Unsent'
                    WHEN 'sent' THEN 'Sent'
                    WHEN 'failed' THEN 'Failed'
                    WHEN 'retrying' THEN 'Retrying'
                    END AS SentStatus,
                    sent_date as SentDate,
                    last_mod_date as LastModDate,
                    a.last_mod_user as LastModUser
                    from msdb.dbo.sysmail_allitems a
                    join msdb.dbo.sysmail_profile p
                    on a.profile_id = p.profile_id"

            if ($Since -or $Status) {
                $wherearray = @()

                if ($Since) {
                    $wherearray += "send_request_date >= '$($Since.ToString("yyyy-MM-ddTHH:mm:ss"))'"
                }

                if ($Status) {
                    $Status = $Status -join "', '"
                    $wherearray += "sent_status in ('$Status')"
                }

                $wherearray = $wherearray -join ' and '
                $where = "where $wherearray"
                $sql = "$sql $where"
            }

            Write-Message -Level Debug -Message $sql

            try {
                $server.Query($sql) | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, Profile, Recipients, CopyRecipients, BlindCopyRecipients, Subject, Importance, Sensitivity, FileAttachments, AttachmentEncoding, SendRequestDate, SendRequestUser, SentStatus, SentDate
            }
            catch {
                Stop-Function -Message "Query failure" -ErrorRecord $_ -Continue
            }
        }
    }
}
tools\dbatools\functions\Get-DbaDbMailLog.ps1
function Get-DbaDbMailLog {
    <#
    .SYNOPSIS
        Gets the DBMail log from a SQL instance

    .DESCRIPTION
        Gets the DBMail log from a SQL instance

    .PARAMETER SqlInstance
        The SQL Server instance, or instances.

    .PARAMETER SqlCredential
        Allows you to login to servers using SQL Logins as opposed to Windows Auth/Integrated/Trusted.

    .PARAMETER Since
    Datetime object used to narrow the results to the send request date

    .PARAMETER Type
    Narrow the results by type. Valid values include Error, Warning, Success, Information, Internal

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Tags: Logging
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Get-DbaDbMailLog

    .EXAMPLE
        Get-DbaDbMailLog -SqlInstance sql01\sharepoint

        Returns the entire dbmail log on sql01\sharepoint

    .EXAMPLE
        Get-DbaDbMailLog -SqlInstance sql01\sharepoint | Select *

        Returns the entire dbmail log on sql01\sharepoint then return a bunch more columns

    .EXAMPLE
        $servers = "sql2014","sql2016", "sqlcluster\sharepoint"
        $servers | Get-DbaDbMailLog -Type Error, Information

        Returns only the Error and Information dbmail log for "sql2014","sql2016" and "sqlcluster\sharepoint"

#>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [DateTime]$Since,
        [ValidateSet('Error', 'Warning', 'Success', 'Information', 'Internal')]
        [string[]]$Type,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category Connectiondbmail -dbmailRecord $_ -Target $instance -Continue
            }

            $sql = "SELECT SERVERPROPERTY('MachineName') AS ComputerName,
            ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
            SERVERPROPERTY('ServerName') AS SqlInstance,
            log_id as LogId,
            CASE event_type
            WHEN 'error' THEN 'Error'
            WHEN 'warning' THEN 'Warning'
            WHEN 'information' THEN 'Information'
            WHEN 'success' THEN 'Success'
            WHEN 'internal' THEN 'Internal'
            ELSE event_type
            END as EventType,
            log_date as LogDate,
            REPLACE(description, CHAR(10)+')', '') as Description,
            process_id as ProcessId,
            mailitem_id as MailItemId,
            account_id as AccountId,
            last_mod_date as LastModDate,
            last_mod_user as LastModUser,
            last_mod_user as [Login]
            FROM msdb.dbo.sysmail_event_log"

            if ($Since -or $Type) {
                $wherearray = @()

                if ($Since) {
                    $wherearray += "log_date >= '$($Since.ToString("yyyy-MM-ddTHH:mm:ss"))'"
                }

                if ($Type) {
                    $combinedtype = $Type -join "', '"
                    $wherearray += "event_type in ('$combinedtype')"
                }

                $wherearray = $wherearray -join ' and '
                $where = "where $wherearray"
                $sql = "$sql $where"
            }

            Write-Message -Level Debug -Message $sql

            try {
                $server.Query($sql) | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, LogDate, EventType, Description, Login
            }
            catch {
                Stop-Function -Message "Query failure" -InnerErrorRecord $_ -Continue
            }
        }
    }
}
tools\dbatools\functions\Get-DbaDbPageInfo.ps1
#ValidationTags#CodeStyle,Messaging,FlowControl,Pipeline#
function Get-DbaDbPageInfo {
    <#
        .SYNOPSIS
            Get-DbaDbPageInfo will return page information for a database

        .DESCRIPTION
            Get-DbaDbPageInfo is able to return information about the pages in a database.
            It's possible to return the information for multiple databases and filter on specific databases, schemas and tables.

        .PARAMETER SqlInstance
            The target SQL Server instance(s)

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Filter to only get specific databases

        .PARAMETER Schema
            Filter to only get specific schemas

        .PARAMETER Table
            Filter to only get specific tables

        .PARAMETER InputObject
            Enables piping from Get-DbaDatabase

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database, Page
            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaDbPageInfo

        .EXAMPLE
            Get-DbaDbPageInfo -SqlInstance sql2017

            Returns page information for all databases on sql2017

        .EXAMPLE
            Get-DbaDbPageInfo -SqlInstance sql2017, sql2016 -Database testdb

            Returns page information for the testdb on sql2017 and sql2016

        .EXAMPLE
            $servers | Get-DbaDatabase -Database testdb | Get-DbaDbPageInfo

            Returns page information for the testdb on all $servers
    #>
    [CmdLetBinding()]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$Database,
        [string[]]$Schema,
        [string[]]$Table,
        [parameter(ValueFromPipeline)]
        [Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
        [switch]$EnableException
    )
    begin {
        $sql = "SELECT SERVERPROPERTY('MachineName') AS ComputerName,
        ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
        SERVERPROPERTY('ServerName') AS SqlInstance, [Database] = DB_NAME(DB_ID()),
        ss.name AS [Schema], st.name AS [Table], dbpa.page_type_desc AS PageType,
                        dbpa.page_free_space_percent AS PageFreePercent,
                        IsAllocated =
                          CASE dbpa.is_allocated
                             WHEN 0 THEN 'False'
                             WHEN 1 THEN 'True'
                          END,
                        IsMixedPage =
                          CASE dbpa.is_mixed_page_allocation
                             WHEN 0 THEN 'False'
                             WHEN 1 THEN 'True'
                          END
                        FROM sys.dm_db_database_page_allocations(DB_ID(), NULL, NULL, NULL, 'DETAILED') AS dbpa
                        INNER JOIN sys.tables AS st ON st.object_id = dbpa.object_id
                        INNER JOIN sys.schemas AS ss ON ss.schema_id = st.schema_id"

        if ($Schema) {
            $sql = "$sql WHERE ss.name IN ('$($Schema -join "','")')"
        }

        if ($Table) {
            if ($schema) {
                $sql = "$sql AND st.name IN ('$($Table -join "','")')"
            }
            else {
                $sql = "$sql WHERE st.name IN ('$($Table -join "','")')"
            }
        }
    }
    process {
        # Loop through all the instances
        foreach ($instance in $SqlInstance) {

            # Try connecting to the instance
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 11
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($Database) {
                $InputObject += $server.Databases | Where-Object { $_.Name -in $Database }
            }
            else {
                $InputObject += $server.Databases
            }
        }

        # Loop through each of databases
        foreach ($db in $InputObject) {
            # Revalidate the version of the server in case db is piped in
            try {
                if ($db.Parent.VersionMajor -ge 11) {
                    $db.Query($sql)
                }
                else
                {
                    Stop-Function -Message "Unsupported SQL Server version" -Target $db -Continue
                }
            }
            catch {
                Stop-Function -Message "Something went wrong executing the query" -ErrorRecord $_ -Target $instance -Continue
            }
        }
    }
}
tools\dbatools\functions\Get-DbaDbQueryStoreOptions.ps1
#ValidationTags#CodeStyle,Messaging,FlowControl,Pipeline#
function Get-DbaDbQueryStoreOptions {
    <#
        .SYNOPSIS
        Get the Query Store configuration for Query Store enabled databases.

        .DESCRIPTION
        Retrieves and returns the Query Store configuration for every database that has the Query Store feature enabled.

        .OUTPUTS
        Microsoft.SqlServer.Management.Smo.QueryStoreOptions

        .PARAMETER SqlInstance
        The SQL Server that you're connecting to.

        .PARAMETER SqlCredential
        SqlCredential object used to connect to the SQL Server as a different user.

        .PARAMETER Database
        The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
        The database(s) to exclude - this list is auto-populated from the server

        .PARAMETER EnableException
                By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
                This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
                Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
        Tags: QueryStore
        Author: Enrico van de Laar ( @evdlaar )
        Author: Klaas Vandenberghe ( @PowerDBAKlaas )

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

        .LINK
        https://dbatools.io/Get-DbaQueryStoreOptions

        .EXAMPLE
        Get-DbaDbQueryStoreOptions -SqlInstance ServerA\sql

        Returns Query Store configuration settings for every database on the ServerA\sql instance.

        .EXAMPLE
        Get-DbaDbQueryStoreOptions -SqlInstance ServerA\sql | Where-Object {$_.ActualState -eq "ReadWrite"}

        Returns the Query Store configuration for all databases on ServerA\sql where the Query Store feature is in Read/Write mode.

        .EXAMPLE
        Get-DbaDbQueryStoreOptions -SqlInstance localhost | format-table -AutoSize -Wrap

        Returns Query Store configuration settings for every database on the ServerA\sql instance inside a table format.

#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        $ExcludeDatabase += 'master', 'tempdb'
    }
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 13
            }
            catch {
                Write-Message -Level Warning -Message "Can't connect to $instance. Moving on."
                continue
            }

            # We have to exclude all the system databases since they cannot have the Query Store feature enabled
            $dbs = Get-DbaDatabase -SqlInstance $server -ExcludeDatabase $ExcludeDatabase -Database $Database | Where-Object IsAccessible

            foreach ($db in $dbs) {
                Write-Message -Level Verbose -Message "Processing $($db.Name) on $instance"
                $QSO = $db.QueryStoreOptions

                Add-Member -Force -InputObject $QSO -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                Add-Member -Force -InputObject $QSO -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                Add-Member -Force -InputObject $QSO -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                Add-Member -Force -InputObject $QSO -MemberType NoteProperty Database -value $db.Name
                Select-DefaultView -InputObject $QSO -Property ComputerName, InstanceName, SqlInstance, Database, ActualState, DataFlushIntervalInSeconds, StatisticsCollectionIntervalInMinutes, MaxStorageSizeInMB, CurrentStorageSizeInMB, QueryCaptureMode, SizeBasedCleanupMode, StaleQueryThresholdInDays
            }
        }
    }
}
tools\dbatools\functions\Get-DbaDbRecoveryModel.ps1
function Get-DbaDbRecoveryModel {
    <#
        .SYNOPSIS
            Get-DbaDbRecoveryModel displays the Recovery Model.

        .DESCRIPTION
            Get-DbaDbRecoveryModel displays the Recovery Model for all databases. This is the default, you can filter using -Database, -ExcludeDatabase, -RecoveryModel

        .PARAMETER SqlInstance
            The SQL Server instance.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process - this list is auto-populated from the server. if unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto-populated from the server

        .PARAMETER RecoveryModel
            Filters the output based on Recovery Model. Valid options are Simple, Full and BulkLogged

            Details about the recovery models can be found here:
            https://docs.microsoft.com/en-us/sql/relational-databases/backup-restore/recovery-models-sql-server

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Recovery, RecoveryModel, Simple, Full, Bulk, BulkLogged
            Author: Viorel Ciucu (@viorelciucu), https://www.cviorel.com

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaDbRecoveryModel

        .EXAMPLE
            Get-DbaDbRecoveryModel -SqlInstance sql2014 -RecoveryModel BulkLogged -Verbose

            Gets all databases on SQL Server instance sql2014 having RecoveryModel set to BulkLogged

        .EXAMPLE
            Get-DbaDbRecoveryModel -SqlInstance sql2014 -Database TestDB

            Gets recovery model information for TestDB. If TestDB does not exist on the instance we don't return anythig.

    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [ValidateSet('Simple', 'Full', 'BulkLogged')]
        [string[]]$RecoveryModel,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$EnableException
    )
    begin {
        $defaults = 'ComputerName', 'InstanceName', 'SqlInstance', 'Name', 'Status', 'IsAccessible', 'RecoveryModel',
        'LastBackupDate as LastFullBackup', 'LastDifferentialBackupDate as LastDiffBackup',
        'LastLogBackupDate as LastLogBackup'
    }
    process {
        $params = @{
            SqlInstance     = $SqlInstance
            SqlCredential   = $SqlCredential
            Database        = $Database
            ExcludeDatabase = $ExcludeDatabase
            EnableException = $EnableException
        }

        if ($RecoveryModel) {
            Get-DbaDatabase @params | Where-Object RecoveryModel -in $RecoveryModel | Where-Object IsAccessible | Select-DefaultView -Property $defaults
        }
        else {
            Get-DbaDatabase @params | Select-DefaultView -Property $defaults
        }
    }
}
tools\dbatools\functions\Get-DbaDbRole.ps1
function Get-DbaDbRole {
    <#
.SYNOPSIS
Get database roles on a Sql instance.

.DESCRIPTION
Get database roles on a Sql instance.

Default output includes columns SQLServer, Database, Role.

.PARAMETER SQLInstance
The SQL Server that you're connecting to.

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Database
The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

.PARAMETER ExcludeDatabase
The database(s) to exclude - this list is auto-populated from the server

.PARAMETER ExcludeFixedRole
Excludes all fixed roles.

.PARAMETER Credential
Credential object used to connect to the SQL Server as a different user.

.PARAMETER EnableException
    By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
    Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Roles, Database, Security
Author: Klaas Vandenberghe ( @PowerDBAKlaas )

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
 https://dbatools.io/Get-DbaDbRole

.EXAMPLE
Get-DbaDbRole -SqlInstance ServerA

Returns a custom object displaying SQLServer, Database, Role for all DatabaseRoles on sql instance ServerA.

.EXAMPLE
Get-DbaDbRole -SqlInstance ServerA | Out-Gridview

Returns a gridview displaying SQLServer, Database, Role for all DatabaseRoles on sql instance ServerA.

.EXAMPLE
Get-DbaDbRole -SqlInstance ServerB\sql16 -ExcludeDatabase DBADB,TestDB

Returns SQLServer, Database, Role for DatabaseRoles on sql instance ServerB\sql16, except those in databases DBADB and TestDB.

.EXAMPLE
'ServerB\sql16','ServerA' | Get-DbaDbRole

Returns SQLServer, Database, Role for DatabaseRoles on sql instances ServerA and ServerB\sql16.

.EXAMPLE
Get-DbaDbRole -SqlInstance ServerB\sql16 -Database AccountingDB

Returns SQLServer, Database, Role for DatabaseRoles in database AccountingDB on sql instance ServerB\sql16.

.EXAMPLE
Get-DbaDbRole -SqlInstance ServerB\sql16 -ExcludeFixedRoles

Returns SQLServer, Database, Role for DatabaseRoles on sql instance ServerB\sql16, but not the fixed roles.

#>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias('SqlServer', 'ServerInstance')]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$ExcludeFixedRole,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {

        foreach ($instance in $sqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $dbs = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                Write-Message -Level Verbose -Message "Databases to check: $Database"
                $dbs = $dbs | Where-Object Name -In $Database
            }

            if ($ExcludeDatabase) {
                Write-Message -Level Verbose -Message "Databases excluded from check: $ExcludeDatabase"
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $dbs) {
                Write-Message -Level Verbose -Message "Checking accessibility of $db on $instance"

                if ($db.IsAccessible -ne $true) {
                    Write-Message -Level Warning -Message "Database $db on $instance is not accessible"
                    continue
                }

                $dbroles = $db.roles
                Write-Message -Level Verbose -Message "Getting Database Roles for $db on $instance"

                if ($ExcludeFixedRole) {
                    $dbroles = $dbroles | Where-Object IsFixedRole -eq $false
                }

                foreach ($dbrole in $dbroles) {
                    Add-Member -Force -InputObject $dbrole -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                    Add-Member -Force -InputObject $dbrole -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                    Add-Member -Force -InputObject $dbrole -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                    Add-Member -Force -InputObject $dbrole -MemberType NoteProperty -Name Database -value $db.Name

                    Select-DefaultView -InputObject $dbrole -Property ComputerName, InstanceName, SqlInstance, Database, Name, Owner, CreateDate, DateLastModified, IsFixedRole
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaDbSnapshot.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaDbSnapshot {
    <#
    .SYNOPSIS
        Get database snapshots with details
    .DESCRIPTION
        Retrieves the list of database snapshot available, along with their base (the db they are the snapshot of) and creation time
    .PARAMETER SqlInstance
        The SQL Server that you're connecting to.
    .PARAMETER SqlCredential
        Credential object used to connect to the SQL Server as a different user
    .PARAMETER Database
        Return information for only specific databases
    .PARAMETER ExcludeDatabase
        The database(s) to exclude - this list is auto-populated from the server
    .PARAMETER Snapshot
        Return information for only specific snapshots
    .PARAMETER ExcludeSnapshot
        The snapshot(s) to exclude - this list is auto-populated from the server
    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
    .NOTES
        Tags: Snapshot
        Author: niphlod
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT
    .LINK
         https://dbatools.io/Get-DbaDbSnapshot
    .EXAMPLE
        Get-DbaDbSnapshot -SqlInstance sqlserver2014a
        Returns a custom object displaying Server, Database, DatabaseCreated, SnapshotOf, SizeMB, DatabaseCreated
    .EXAMPLE
        Get-DbaDbSnapshot -SqlInstance sqlserver2014a -Database HR, Accounting
        Returns information for database snapshots having HR and Accounting as base dbs
    .EXAMPLE
        Get-DbaDbSnapshot -SqlInstance sqlserver2014a -Snapshot HR_snapshot, Accounting_snapshot
        Returns information for database snapshots HR_snapshot and Accounting_snapshot
#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [object[]]$Snapshot,
        [object[]]$ExcludeSnapshot,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            $dbs = $server.Databases | Where-Object DatabaseSnapshotBaseName
            if ($Database) {
                $dbs = $dbs | Where-Object { $Database -contains $_.DatabaseSnapshotBaseName }
            }
            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object { $ExcludeDatabase -notcontains $_.DatabaseSnapshotBaseName }
            }
            if ($Snapshot) {
                $dbs = $dbs | Where-Object { $Snapshot -contains $_.Name }
            }
            if (!$Snapshot -and !$Database) {
                $dbs = $dbs | Where-Object IsDatabaseSnapshot -eq $true | Sort-Object DatabaseSnapshotBaseName, Name
            }
            if ($ExcludeSnapshot) {
                $dbs = $dbs | Where-Object { $ExcludeSnapshot -notcontains $_.Name }
            }
            foreach ($db in $dbs) {
                try {
                    $BytesOnDisk = $db.Query("SELECT SUM(BytesOnDisk) AS BytesOnDisk FROM fn_virtualfilestats(DB_ID(),NULL) S JOIN sys.databases D on D.database_id = S.dbid", $db.Name)
                    Add-Member -Force -InputObject $db -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                    Add-Member -Force -InputObject $db -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                    Add-Member -Force -InputObject $db -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                    Add-Member -Force -InputObject $db -MemberType NoteProperty -Name DiskUsage -value ([dbasize]($BytesOnDisk.BytesOnDisk))
                    Select-DefaultView -InputObject $db -Property ComputerName, InstanceName, SqlInstance, Name, 'DatabaseSnapshotBaseName as SnapshotOf', CreateDate, DiskUsage
                }
                catch {
                    Stop-Function -Message "Failure" -ErrorRecord $_ -Target $db -Continue
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Get-DbaDatabaseSnapshot
    }
}
tools\dbatools\functions\Get-DbaDbStoredProcedure.ps1
function Get-DbaDbStoredProcedure {
    <#
        .SYNOPSIS
            Gets database Stored Procedures

        .DESCRIPTION
            Gets database Stored Procedures

        .PARAMETER SqlInstance
            The target SQL Server instance(s)

        .PARAMETER SqlCredential
            Allows you to login to SQL Server using alternative credentials

        .PARAMETER Database
            To get Stored Procedures from specific database(s)

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto populated from the server

        .PARAMETER ExcludeSystemSp
            This switch removes all system objects from the Stored Procedure collection

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database, StoredProcedure, Proc
            Author: Klaas Vandenberghe ( @PowerDbaKlaas )

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            Get-DbaDbStoredProcedure -SqlInstance sql2016

            Gets all database Stored Procedures

        .EXAMPLE
            Get-DbaDbStoredProcedure -SqlInstance Server1 -Database db1

            Gets the Stored Procedures for the db1 database

        .EXAMPLE
            Get-DbaDbStoredProcedure -SqlInstance Server1 -ExcludeDatabase db1

            Gets the Stored Procedures for all databases except db1

        .EXAMPLE
            Get-DbaDbStoredProcedure -SqlInstance Server1 -ExcludeSystemSp

            Gets the Stored Procedures for all databases that are not system objects

        .EXAMPLE
            'Sql1','Sql2/sqlexpress' | Get-DbaDbStoredProcedure

            Gets the Stored Procedures for the databases on Sql1 and Sql2/sqlexpress
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$ExcludeSystemSp,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $databases = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                $databases = $databases | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $databases) {
                if (!$db.IsAccessible) {
                    Write-Message -Level Warning -Message "Database $db is not accessible. Skipping."
                    continue
                }
                if ($db.StoredProcedures.Count -eq 0) {
                    Write-Message -Message "No Stored Procedures exist in the $db database on $instance" -Target $db -Level Output
                    continue
                }

                foreach ($proc in $db.StoredProcedures) {
                    if ( (Test-Bound -ParameterName ExcludeSystemSp) -and $proc.IsSystemObject ) {
                        continue
                    }

                    Add-Member -Force -InputObject $proc -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                    Add-Member -Force -InputObject $proc -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                    Add-Member -Force -InputObject $proc -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                    Add-Member -Force -InputObject $proc -MemberType NoteProperty -Name Database -value $db.Name

                    $defaults = 'ComputerName', 'InstanceName', 'SqlInstance', 'Database', 'Schema', 'ID as ObjectId', 'CreateDate',
                    'DateLastModified', 'Name', 'ImplementationType', 'Startup'
                    Select-DefaultView -InputObject $proc -Property $defaults
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaDbVirtualLogFile.ps1
#ValidationTags#CodeStyle,Messaging,FlowControl,Pipeline#
function Get-DbaDbVirtualLogFile {
    <#
        .SYNOPSIS
            Returns database virtual log file information for database files on a SQL instance.

        .DESCRIPTION
            Having a transaction log file with too many virtual log files (VLFs) can hurt database performance.

            Too many VLFs can cause transaction log backups to slow down and can also slow down database recovery and, in extreme cases, even affect insert/update/delete performance.

            References:
                http://www.sqlskills.com/blogs/kimberly/transaction-log-vlfs-too-many-or-too-few/
                http://blogs.msdn.com/b/saponsqlserver/archive/2012/02/22/too-many-virtual-log-files-vlfs-can-cause-slow-database-recovery.aspx

            If you've got a high number of VLFs, you can use Expand-SqlTLogResponsibly to reduce the number.

        .PARAMETER SqlInstance
            Specifies the SQL Server instance(s) to scan.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies the database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies the database(s) to exclude from processing. Options for this list are auto-populated from the server.

        .PARAMETER IncludeSystemDBs
            If this switch is enabled, system database information will be displayed.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: VLF, Database, LogFile

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaDbVirtualLogFile

        .EXAMPLE
            Get-DbaDbVirtualLogFile -SqlInstance sqlcluster

            Returns all user database virtual log file details for the sqlcluster instance.

        .EXAMPLE
            Get-DbaDbVirtualLogFile -SqlInstance sqlserver | Group-Object -Property Database | Where-Object Count -gt 50

            Returns user databases that have 50 or more VLFs.

        .EXAMPLE
            @('sqlserver','sqlcluster') | Get-DbaDbVirtualLogFile

            Returns all VLF information for the sqlserver and sqlcluster SQL Server instances. Processes data via the pipeline.

        .EXAMPLE
            Get-DbaDbVirtualLogFile -SqlInstance sqlcluster -Database db1, db2

            Returns the VLF counts for the db1 and db2 databases on sqlcluster.
    #>
    [CmdletBinding()]
    [OutputType([System.Collections.ArrayList])]
    param ([parameter(ValueFromPipeline, Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$IncludeSystemDBs,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $dbs = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                $dbs = $dbs | Where-Object Name -in $Database
            }
            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            if (!$IncludeSystemDBs) {
                $dbs = $dbs | Where-Object IsSystemObject -eq $false
            }

            foreach ($db in $dbs) {
                try {
                    $data = $db.Query("DBCC LOGINFO")

                    foreach ($d in $data) {
                        [pscustomobject]@{
                            ComputerName   = $server.ComputerName
                            InstanceName   = $server.ServiceName
                            SqlInstance    = $server.DomainInstanceName
                            Database       = $db.Name
                            RecoveryUnitId = $d.RecoveryUnitId
                            FileId         = $d.FileId
                            FileSize       = $d.FileSize
                            StartOffset    = $d.StartOffset
                            FSeqNo         = $d.FSeqNo
                            Status         = $d.Status
                            Parity         = $d.Parity
                            CreateLsn      = $d.CreateLSN
                        }
                    }
                }
                catch {
                    Stop-Function -Message "Unable to query $($db.name) on $instance." -ErrorRecord $_ -Target $db -Continue
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaDefaultPath.ps1
function Get-DbaDefaultPath {
    <#
    .SYNOPSIS
        Gets the default SQL Server paths for data, logs and backups

    .DESCRIPTION
        Gets the default SQL Server paths for data, logs and backups

    .PARAMETER SqlInstance
        The SQL Server instance, or instances.

    .PARAMETER SqlCredential
        Allows you to login to servers using SQL Logins as opposed to Windows Auth/Integrated/Trusted.

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Tags: Config
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Get-DbaDefaultPath

    .EXAMPLE
        Get-DbaDefaultPath -SqlInstance sql01\sharepoint

        Returns the default file paths for sql01\sharepoint

    .EXAMPLE
        $servers = "sql2014","sql2016", "sqlcluster\sharepoint"
        $servers | Get-DbaDefaultPath

        Returns the default file paths for "sql2014","sql2016" and "sqlcluster\sharepoint"

#>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -AzureUnsupported
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $dataPath = $server.DefaultFile
            if ($dataPath.Length -eq 0) {
                $dataPath = $server.ConnectionContext.ExecuteScalar("SELECT SERVERPROPERTY('InstanceDefaultdataPath')")
            }

            if ($dataPath -eq [System.DBNull]::Value -or $dataPath.Length -eq 0) {
                $dataPath = Split-Path (Get-DbaDatabase -SqlInstance $server -Database model).FileGroups[0].Files[0].FileName
            }

            if ($dataPath.Length -eq 0) {
                $dataPath = $server.Information.MasterDbPath
            }

            $logPath = $server.DefaultLog

            if ($logPath.Length -eq 0) {
                $logPath = $server.ConnectionContext.ExecuteScalar("SELECT SERVERPROPERTY('InstanceDefaultLogPath')")
            }

            if ($logPath -eq [System.DBNull]::Value -or $logPath.Length -eq 0) {
                $logPath = Split-Path (Get-DbaDatabase -SqlInstance $server -Database model).LogFiles.FileName
            }

            if ($logPath.Length -eq 0) {
                $logPath = $server.Information.MasterDbLogPath
            }

            $dataPath = $dataPath.Trim().TrimEnd("\")
            $logPath = $logPath.Trim().TrimEnd("\")

            [PSCustomObject]@{
                ComputerName = $server.ComputerName
                InstanceName = $server.ServiceName
                SqlInstance  = $server.DomainInstanceName
                Data         = $dataPath
                Log          = $logPath
                Backup       = $server.BackupDirectory
                ErrorLog     = $server.ErrorlogPath
            }
        }
    }
}
tools\dbatools\functions\Get-DbaDependency.ps1
function Get-DbaDependency {
    <#
        .SYNOPSIS
            Finds object dependencies and their relevant creation scripts.

        .DESCRIPTION
            This function recursively finds all objects that depends on the input.
            It will then retrieve rich information from them, including their creation scripts and the order in which it should be applied.

            By using the 'Parents' switch, the function will instead retrieve all items that the input depends on (including their creation scripts).

            For more details on dependency, see:
            https://technet.microsoft.com/en-us/library/ms345449(v=sql.105).aspx

        .PARAMETER InputObject
            The SMO object to parse

        .PARAMETER AllowSystemObjects
            Normally, system objects are ignored by this function as dependencies.
            This switch overrides that behavior.

        .PARAMETER Parents
            Causes the function to retrieve all objects that the input depends on, rather than retrieving everything that depends on the input.

        .PARAMETER IncludeSelf
            Includes the object whose dependencies are retrieves itself.
            Useful when exporting an entire logic structure in order to recreate it in another database.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER IncludeScript
            Setting this switch will cause the function to also retrieve the creation script of the dependency.

        .NOTES
            Tags: Database, Dependent, Dependency, Object
            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaDependency

        .EXAMPLE
            $table = (Get-DbaDatabase -SqlInstance sql2012 -Database Northwind).tables | Where Name -eq Customers
            $table | Get-DbaDependency

            Returns everything that depends on the "Customers" table
    #>
    [CmdletBinding()]
    Param (
        [Parameter(ValueFromPipeline = $true)]
        $InputObject,

        [switch]
        $AllowSystemObjects,

        [switch]
        $Parents,

        [switch]
        $IncludeSelf,

        [switch]
        [Alias('Silent')]$EnableException
    )

    Begin {
        #region Utility functions
        function Get-DependencyTree {
            [CmdletBinding()]
            Param (
                $Object,

                $Server,

                [bool]
                $AllowSystemObjects,

                [bool]
                $EnumParents,

                [string]
                $FunctionName,

                [bool]
                $EnableException
            )

            $scripter = New-Object Microsoft.SqlServer.Management.Smo.Scripter
            $options = New-Object Microsoft.SqlServer.Management.Smo.ScriptingOptions
            $options.DriAll = $true
            $options.AllowSystemObjects = $AllowSystemObjects
            $options.WithDependencies = $true
            $scripter.Options = $options
            $scripter.Server = $Server

            $urnCollection = New-Object Microsoft.SqlServer.Management.Smo.UrnCollection

            Write-Message -EnableException $EnableException -Level 5 -Message "Adding $Object which is a $($Object.urn.Type)" -FunctionName $FunctionName
            $urnCollection.Add([Microsoft.SqlServer.Management.Sdk.Sfc.Urn]$Object.urn)

            #now we set up an event listnenr go get progress reports
            $progressReportEventHandler = [Microsoft.SqlServer.Management.Smo.ProgressReportEventHandler] {
                $name = $_.Current.GetAttribute('Name');
                Write-Message -EnableException $EnableException -Level 5 -Message "Analysed $name" -FunctionName $FunctionName
            }
            $scripter.add_DiscoveryProgress($progressReportEventHandler)

            return $scripter.DiscoverDependencies($urnCollection, $EnumParents)
        }

        function Read-DependencyTree {
            [CmdletBinding()]
            Param (
                [System.Object]
                $InputObject,

                [int]
                $Tier,

                [System.Object]
                $Parent,

                [bool]
                $EnumParents
            )

            Add-Member -Force -InputObject $InputObject -Name Parent -Value $Parent -MemberType NoteProperty
            if ($EnumParents) { Add-Member -Force -InputObject $InputObject -Name Tier -Value ($Tier * -1) -MemberType NoteProperty -PassThru }
            else { Add-Member -Force -InputObject $InputObject -Name Tier -Value $Tier -MemberType NoteProperty -PassThru }

            if ($InputObject.HasChildNodes) { Read-DependencyTree -InputObject $InputObject.FirstChild -Tier ($Tier + 1) -Parent $InputObject -EnumParents $EnumParents }
            if ($InputObject.NextSibling) { Read-DependencyTree -InputObject $InputObject.NextSibling -Tier $Tier -Parent $Parent -EnumParents $EnumParents }
        }

        function Get-DependencyTreeNodeDetail {
            [CmdletBinding()]
            Param (
                [Parameter(ValueFromPipeline = $true)]
                $SmoObject,

                $Server,

                $OriginalResource,

                [bool]
                $AllowSystemObjects
            )

            Begin {
                $scripter = New-Object Microsoft.SqlServer.Management.Smo.Scripter
                $options = New-Object Microsoft.SqlServer.Management.Smo.ScriptingOptions
                $options.DriAll = $true
                $options.AllowSystemObjects = $AllowSystemObjects
                $options.WithDependencies = $true
                $scripter.Options = $options
                $scripter.Server = $Server
            }

            process {
                foreach ($Item in $SmoObject) {
                    $richobject = $Server.GetSmoObject($Item.urn)
                    $parent = $Server.GetSmoObject($Item.Parent.Urn)

                    $NewObject = New-Object Sqlcollaborative.Dbatools.Database.Dependency
                    $NewObject.ComputerName = $server.ComputerName
                    $NewObject.ServiceName = $server.ServiceName
                    $NewObject.SqlInstance = $server.DomainInstanceName
                    $NewObject.Dependent = $richobject.Name
                    $NewObject.Type = $Item.Urn.Type
                    $NewObject.Owner = $richobject.Owner
                    $NewObject.IsSchemaBound = $Item.IsSchemaBound
                    $NewObject.Parent = $parent.Name
                    $NewObject.ParentType = $parent.Urn.Type
                    $NewObject.Tier = $Item.Tier
                    $NewObject.Object = $richobject
                    $NewObject.Urn = $richobject.Urn
                    $NewObject.OriginalResource = $OriginalResource

                    $SQLscript = $scripter.EnumScriptWithList($richobject)

                    # I can't remember how to remove these options and their syntax is breaking stuff
                    $SQLscript = $SQLscript -replace "SET ANSI_NULLS ON", ""
                    $SQLscript = $SQLscript -replace "SET QUOTED_IDENTIFIER ON", ""
                    $NewObject.Script = "$SQLscript `r`ngo"

                    $NewObject
                }
            }
        }

        function Select-DependencyPrecedence {
            [CmdletBinding()]
            Param (
                [Parameter(ValueFromPipeline = $true)]
                $Dependency
            )

            Begin {
                $list = @()
            }
            Process {
                foreach ($dep in $Dependency) {
                    # Killing the pipeline is generally a bad idea, but since we have to group and sort things, we have not really a choice
                    $list += $dep
                }
            }
            End {
                $list | Group-Object -Property Object | ForEach-Object { $_.Group | Sort-Object -Property Tier -Descending | Select-Object -First 1 } | Sort-Object Tier
            }
        }
        #endregion Utility functions
    }
    Process {
        foreach ($Item in $InputObject) {
            Write-Message -EnableException $EnableException -Level Verbose -Message "Processing: $Item"
            if ($null -eq $Item.urn) {
                Stop-Function -Message "$Item is not a valid SMO object" -EnableException $EnableException -Category InvalidData -Continue -Target $Item
            }

            # Find the server object to pass on to the function
            $parent = $Item.parent

            do { $parent = $parent.parent }
            until (($parent.urn.type -eq "Server") -or (-not $parent))

            if (-not $parent) {
                Stop-Function -Message "Failed to find valid server object in input: $Item" -EnableException $EnableException -Category InvalidData -Continue -Target $Item
            }

            $server = $parent

            $tree = Get-DependencyTree -Object $Item -AllowSystemObjects $false -Server $server -FunctionName (Get-PSCallStack)[0].COmmand -EnableException $EnableException -EnumParents $Parents
            $limitCount = 2
            if ($IncludeSelf) { $limitCount = 1 }
            if ($tree.Count -lt $limitCount) {
                Write-Message -Message "No dependencies detected for $($Item)" -Level Host
                continue
            }

            if ($IncludeSelf) { $resolved = Read-DependencyTree -InputObject $tree.FirstChild -Tier 0 -Parent $tree.FirstChild -EnumParents $Parents }
            else { $resolved = Read-DependencyTree -InputObject $tree.FirstChild.FirstChild -Tier 1 -Parent $tree.FirstChild -EnumParents $Parents }
            $resolved | Get-DependencyTreeNodeDetail -Server $server -OriginalResource $Item -AllowSystemObjects $AllowSystemObjects | Select-DependencyPrecedence
        }
    }
}
tools\dbatools\functions\Get-DbaDetachedDatabaseInfo.ps1
function Get-DbaDetachedDatabaseInfo {
    <#
        .SYNOPSIS
            Get detailed information about detached SQL Server database files.

        .DESCRIPTION
            Gathers the following information from detached database files: database name, SQL Server version (compatibility level), collation, and file structure.

            "Data files" and "Log file" report the structure of the data and log files as they were when the database was detached. "Database version" is the compatibility level.

            MDF files are most easily read by using a SQL Server to interpret them. Because of this, you must specify a SQL Server and the path must be relative to the SQL Server.

        .PARAMETER SqlInstance
            Source SQL Server. This instance must be online and is required to parse the information contained with in the detached database file.

            This function will not attach the database file, it will only use SQL Server to read its contents.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Path
            Specifies the path to the MDF file to be read. This path must be readable by the SQL Server service account. Ideally, the MDF will be located on the SQL Server itself, or on a network share to which the SQL Server service account has access.

        .NOTES
            Tags: Database, Detach

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaDetachedDatabaseInfo

        .EXAMPLE
            Get-DbaDetachedDatabaseInfo -SqlInstance sql2016 -Path M:\Archive\mydb.mdf

            Returns information about the detached database file M:\Archive\mydb.mdf using the SQL Server instance sql2016. The M drive is relative to the SQL Server instance.
     #>

    [CmdletBinding(DefaultParameterSetName = "Default")]
    Param (
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [parameter(Mandatory = $true)]
        [Alias("Mdf")]
        [string]$Path,
        [PSCredential]$SqlCredential
    )

    begin {
        function Get-MdfFileInfo {
            $datafiles = New-Object System.Collections.Specialized.StringCollection
            $logfiles = New-Object System.Collections.Specialized.StringCollection

            $servername = $server.name
            $serviceaccount = $server.ServiceAccount

            $exists = Test-DbaSqlPath -SqlInstance $server -Path $Path

            if ($exists -eq $false) {
                throw "$servername cannot access the file $path. Does the file exist and does the service account ($serviceaccount) have access to the path?"
            }

            try {
                $detachedDatabaseInfo = $server.DetachedDatabaseInfo($path)
                $dbname = ($detachedDatabaseInfo | Where-Object { $_.Property -eq "Database name" }).Value
                $exactdbversion = ($detachedDatabaseInfo | Where-Object { $_.Property -eq "Database version" }).Value
                $collationid = ($detachedDatabaseInfo | Where-Object { $_.Property -eq "Collation" }).Value
            }
            catch {
                throw "$servername cannot read the file $path. Is the database detached?"
            }

            switch ($exactdbversion) {
                852 { $dbversion = "SQL Server 2016" }
                829 { $dbversion = "SQL Server 2016 Prerelease" }
                782 { $dbversion = "SQL Server 2014" }
                706 { $dbversion = "SQL Server 2012" }
                684 { $dbversion = "SQL Server 2012 CTP1" }
                661 { $dbversion = "SQL Server 2008 R2" }
                660 { $dbversion = "SQL Server 2008 R2" }
                655 { $dbversion = "SQL Server 2008 SP2+" }
                612 { $dbversion = "SQL Server 2005" }
                611 { $dbversion = "SQL Server 2005" }
                539 { $dbversion = "SQL Server 2000" }
                515 { $dbversion = "SQL Server 7.0" }
                408 { $dbversion = "SQL Server 6.5" }
                default { $dbversion = "Unknown" }
            }

            $collationsql = "SELECT name FROM fn_helpcollations() where collationproperty(name, N'COLLATIONID')  = $collationid"

            try {
                $dataset = $server.databases['master'].ExecuteWithResults($collationsql)
                $collation = "$($dataset.Tables[0].Rows[0].Item(0))"
            }
            catch {
                $collation = $collationid
            }

            if ($collation.length -eq 0) { $collation = $collationid }

            try {
                foreach ($file in $server.EnumDetachedDatabaseFiles($path)) {
                    $datafiles += $file
                }

                foreach ($file in $server.EnumDetachedLogFiles($path)) {
                    $logfiles += $file
                }
            }
            catch {
                throw "$servername unable to enumerate database or log structure information for $path"
            }

            $mdfinfo = [pscustomobject]@{
                Name         = $dbname
                Version      = $dbversion
                ExactVersion = $exactdbversion
                Collation    = $collation
                DataFiles    = $datafiles
                LogFiles     = $logfiles
            }

            return $mdfinfo
        }
    }

    process {

        $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
        $mdfinfo = Get-MdfFileInfo $server $path

    }

    end {
        $server.ConnectionContext.Disconnect()
        return $mdfinfo
    }
}
tools\dbatools\functions\Get-DbaDiskSpace.ps1
#ValidationTags#CodeStyle,Messaging,FlowControl,Pipeline#
function Get-DbaDiskSpace {
    <#
        .SYNOPSIS
            Displays disk information for all local disk on a server.

        .DESCRIPTION
            Returns a custom object with server name, name of disk, label of disk, total size, free size, percent free, block size and filesystem.

            By default, this function only shows drives of types 2 and 3 (removable disk and local disk).

            Requires Windows administrator access on SQL Servers

        .PARAMETER ComputerName
            The target computer. Defaults to localhost.

        .PARAMETER Credential
            Credential object used to connect to the computer as a different user.

        .PARAMETER Unit
            This parameter has been deprecated and will be removed in 1.0.0
            All properties previously generated through this command are present at the same time, but hidden by default.

        .PARAMETER CheckForSql
            If this switch is enabled, disks will be checked for SQL Server data and log files. Windows Authentication is always used for this.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER ExcludeDrive
            Filter out drives - format is C:\

        .PARAMETER Detailed
            Output all properties, will be deprecated in 1.0.0 release. Use Force Instead

        .PARAMETER CheckFragmentation
            If this switch is enabled, fragmentation of all filesystems will be checked.

            This will increase the runtime of the function by seconds or even minutes per volume.

        .PARAMETER Force
            Enabling this switch will cause the command to include ALL drives.
            By default, only local disks and removable disks are shown, and hidden volumes are excluded.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .NOTES
            Tags: Storage, Disk
            Author: Chrissy LeMaire ([email protected]) & Jakob Bindslet ([email protected])

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaDiskSpace

        .EXAMPLE
            Get-DbaDiskSpace -ComputerName srv0042

            Get disk space for the server srv0042.

        .EXAMPLE
            Get-DbaDiskSpace -ComputerName srv0042 -Unit MB

            Get disk space for the server srv0042 and displays in megabytes (MB).

        .EXAMPLE
            Get-DbaDiskSpace -ComputerName srv0042, srv0007 -Unit TB

            Get disk space from two servers and displays in terabytes (TB).

        .EXAMPLE
            Get-DbaDiskSpace -ComputerName srv0042 -Force

            Get all disk and volume space information.

        .EXAMPLE
            Get-DbaDiskSpace -ComputerName srv0042 -ExcludeDrive 'C:\'

            Get all disk and volume space information.
    #>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Alias('ServerInstance', 'SqlInstance', 'SqlServer')]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [ValidateSet('Bytes', 'KB', 'MB', 'GB', 'TB', 'PB')]
        [string]$Unit = 'GB',
        [switch]$CheckForSql,
        [PSCredential]$SqlCredential,
        [string[]]$ExcludeDrive,
        [Alias('Detailed', 'AllDrives')]
        [switch]$CheckFragmentation,
        [switch]$Force,
        [switch][Alias('Silent')]
        $EnableException
    )

    begin {
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter Detailed
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter AllDrives
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter Unit

        $condition = " WHERE DriveType = 2 OR DriveType = 3"
        if (Test-Bound 'Force') {
            $condition = ""
        }

        # Keep track of what computer was already processed to avoid duplicates
        $processed = New-Object System.Collections.ArrayList

        <# In order to support properly identifying if a disk/volume is involved with ANY instance on a given computer #>
        $sqlDisks = New-Object System.Collections.ArrayList
    }

    process {
        foreach ($computer in $ComputerName) {
            if ($computer.ComputerName -notin $processed) {
                $null = $processed.Add($computer.ComputerName)
                Write-Message -Level VeryVerbose -Message "Connecting to $computer." -Target $computer.ComputerName
            }
            else {
                continue
            }

            try {
                $disks = Get-DbaCmObject -ComputerName $computer.ComputerName -Query "SELECT * FROM Win32_Volume$condition" -Credential $Credential -Namespace root\CIMv2 -ErrorAction Stop -WarningAction SilentlyContinue -EnableException
            }
            catch {
                Stop-Function -Message "Failed to connect to $computer." -EnableException $EnableException -ErrorRecord $_ -Target $computer.ComputerName -Continue
            }

            if ($CheckForSql) {
                try {
                    $sqlServices = Get-DbaSqlService -ComputerName $computer -Type Engine
                }
                catch {
                    Write-Message -Level Warning -Message "Failed to connect to $computer to gather SQL Server instances, will not be reporting SQL Information." -ErrorRecord $_ -OverrideExceptionMessage -Target $computer.ComputerName
                }

                Write-Message -Level Verbose -Message "Instances found on $($computer): $($sqlServices.InstanceName.Count)"
                if ($sqlServices.InstanceName.Count -gt 0) {
                    foreach ($sqlService in $sqlServices) {
                        if ($sqlService.InstanceName -eq "MSSQLSERVER") {
                            $instanceName = $sqlService.ComputerName
                        }
                        else {
                            $instanceName = "$($sqlService.ComputerName)\$($sqlService.InstanceName)"
                        }
                        Write-Message -Level VeryVerbose -Message "Processing instance $($instanceName)"
                        try {
                            $server = Connect-SqlInstance -SqlInstance $instanceName -SqlCredential $SqlCredential
                            if ($server.Version.Major -lt 9) {
                                $sql = "SELECT DISTINCT SUBSTRING(physical_name, 1, LEN(physical_name) - CHARINDEX('\', REVERSE(physical_name)) + 1) AS SqlDisk FROM sysaltfiles"
                            }
                            else {
                                $sql = "SELECT DISTINCT SUBSTRING(physical_name, 1, LEN(physical_name) - CHARINDEX('\', REVERSE(physical_name)) + 1) AS SqlDisk FROM sys.master_files"
                            }
                            $results = $server.Query($sql)
                            if ($results.SqlDisk.Count -gt 0) {
                                foreach ($sqlDisk in $results.SqlDisk) {
                                    if (-not $sqlDisks.Contains($sqlDisk)) {
                                        $null = $sqlDisks.Add($sqlDisk)
                                    }
                                }
                            }
                        }
                        catch {
                            Write-Message -Level Warning -Message "Failed to connect to $instanceName on $computer. SQL information may not be accurate or services have been stopped." -ErrorRecord $_ -OverrideExceptionMessage -Target $computer.ComputerName
                        }
                    }
                }
            }

            foreach ($disk in $disks) {
                if ($disk.Name -in $ExcludeDrive) {
                    continue
                }
                if ($disk.Name.StartsWith('\\') -and (-not $Force)) {
                    Write-Message -Level Verbose -Message "Skipping disk: $($disk.Name)" -Target $computer.ComputerName
                    continue
                }

                Write-Message -Level Verbose -Message "Processing disk: $($disk.Name)" -Target $computer.ComputerName

                $info = New-Object Sqlcollaborative.Dbatools.Computer.DiskSpace
                $info.ComputerName = $computer.ComputerName
                $info.Name = $disk.Name
                $info.Label = $disk.Label
                $info.Capacity = $disk.Capacity
                $info.Free = $disk.Freespace
                $info.BlockSize = $disk.BlockSize
                $info.FileSystem = $disk.FileSystem
                $info.Type = $disk.DriveType

                if ($CheckForSql) {
                    $drivePath = $disk.Name
                    $info.IsSqlDisk = $false
                    foreach ($sqlDisk in $sqlDisks) {
                        if ($sqlDisk -like ($drivePath + '*')) {
                            $info.IsSqlDisk = $true
                            break
                        }
                    }
                }
                $info
            }
        }
    }
}
tools\dbatools\functions\Get-DbaDistributor.ps1
function Get-DbaDistributor {
    <#
    .SYNOPSIS
        Gets the information about a replication distributor for a given SQL Server instance.

    .DESCRIPTION
        This function locates and enumerates distributor information for a given SQL Server instance.

    .PARAMETER SqlInstance
        Allows you to specify a comma separated list of servers to query.

    .PARAMETER SqlCredential
        Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Author: William Durkin, @sql_williamd
        Tags: Replication
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Get-DbaDistributor

    .EXAMPLE
        Get-DbaDistributor -SqlInstance sql2008, sqlserver2012
        Retrieve distributor information for servers sql2008 and sqlserver2012.
    #>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [parameter(Position = 1)]
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        if ($null -eq [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.RMO")) {
            Stop-Function -Message "Replication management objects not available. Please install SQL Server Management Studio."
        }
    }

    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            # connect to the instance
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            Write-Message -Level Verbose -Message "Attempting to retrieve distributor information from $instance"

            # Connect to the distributor of the instance
            try {
                $sourceSqlConn = $server.ConnectionContext.SqlConnectionObject
                $distributor = New-Object Microsoft.SqlServer.Replication.ReplicationServer $sourceSqlConn
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            Add-Member -Force -InputObject $distributor -MemberType NoteProperty -Name ComputerName -Value $server.ComputerName
            Add-Member -Force -InputObject $distributor -MemberType NoteProperty -Name InstanceName -Value $server.ServiceName
            Add-Member -Force -InputObject $distributor -MemberType NoteProperty -Name SqlInstance -Value $server.DomainInstanceName

            Select-DefaultView -InputObject $distributor -Property ComputerName, InstanceName, SqlInstance, IsPublisher, IsDistributor, DistributionServer, DistributionDatabase, DistributorInstalled, DistributorAvailable, HasRemotePublisher
        }
    }
}
tools\dbatools\functions\Get-DbaDump.ps1
function Get-DbaDump {
    <#
        .SYNOPSIS
            Locate a SQL Server that has generated any memory dump files.

        .DESCRIPTION
            The type of dump included in the search include minidump, all-thread dump, or a full dump.  The files have an extendion of .mdmp.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Engine, Corruption
            Author: Garry Bargsley (@gbargsley), http://blog.garrybargsley.com

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaDump

        .EXAMPLE
            Get-DbaDump -SqlInstance sql2016

            Shows the detailed information for memory dump(s) located on sql2016 instance


        .EXAMPLE
            Get-DbaDump -SqlInstance sql2016 -SqlCredential (Get-Credential sqladmin)

            Shows the detailed information for memory dump(s) located on sql2016 instance. Logs into the SQL Server using the SQL login 'sqladmin'

    #>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [switch]$EnableException
    )
    begin {
        $sql = "SELECT filename,  creation_time,  size_in_bytes FROM sys.dm_server_memory_dumps"
    }

    process {
        foreach ($instance in $SqlInstance) {
            $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential

            if ($server.versionMajor -lt 11 -and (-not ($server.versionMajor -eq 10 -and $server.versionMinor -eq 50))) {
                Stop-Function -Message "This function does not support versions lower than SQL Server 2008 R2 (v10.50). Skipping server '$instance'" -Continue
            }

            try {
                foreach ($result in $server.Query($sql)) {
                    [PSCustomObject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        FileName     = $result.filename
                        CreationTime = $result.creation_time
                        Size         = [dbasize]$result.size_in_bytes
                    }
                }
            }
            catch {
                Stop-Function -Message "Issue collecting data on $server" -Target $server -ErrorRecord $_ -Continue
            }
        }
    }
}
tools\dbatools\functions\Get-DbaEndpoint.ps1
#ValidationTags#Messaging,FlowControl,CodeStyle#
function Get-DbaEndpoint {
    <#
        .SYNOPSIS
            Gets SQL Endpoint(s) information for each instance(s) of SQL Server.

        .DESCRIPTION
            The Get-DbaEndpoint command gets SQL Endpoint(s) information for each instance(s) of SQL Server.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function
            to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Endpoint
            Author: Garry Bargsley (@gbargsley), http://blog.garrybargsley.com

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaEndpoint

        .EXAMPLE
            Get-DbaEndpoint -SqlInstance localhost

            Returns all Endpoint(s) on the local default SQL Server instance

        .EXAMPLE
            Get-DbaEndpoint -SqlInstance localhost, sql2016

            Returns all Endpoint(s) for the local and sql2016 SQL Server instances
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            foreach ($endpoint in $server.Endpoints) {
                Add-Member -Force -InputObject $endpoint -MemberType NoteProperty -Name ComputerName -value $endpoint.Parent.ComputerName
                Add-Member -Force -InputObject $endpoint -MemberType NoteProperty -Name InstanceName -value $endpoint.Parent.ServiceName
                Add-Member -Force -InputObject $endpoint -MemberType NoteProperty -Name SqlInstance -value $endpoint.Parent.DomainInstanceName

                Select-DefaultView -InputObject $endpoint -Property ComputerName, InstanceName, SqlInstance, ID, Name, EndpointType, Owner, IsAdminEndpoint, IsSystemObject
            }
        }
    }
}
tools\dbatools\functions\Get-DbaErrorLog.ps1
function Get-DbaErrorLog {
    <#
        .SYNOPSIS
            Gets the "SQL Error Log" of an instance

        .DESCRIPTION
            Gets the "SQL Error Log" of an instance. Returns all 10 error logs by default.

        .PARAMETER SqlInstance
            The SQL Server instance, or instances.

        .PARAMETER SqlCredential
            Allows you to login to servers using SQL Logins as opposed to Windows Auth/Integrated/Trusted.

        .PARAMETER LogNumber
            An Int32 value that specifies the index number of the error log required.
            Error logs are listed 0 through 99, where 0 is the current error log and 99 is potential oldest log file.

            SQL Server errorlog rollover defaults to 6, but can be increased to 99. https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/scm-services-configure-sql-server-error-logs

        .PARAMETER Source
            Filter results based on the Source of the error (e.g. Logon, Server, etc.)

        .PARAMETER Text
            Filter results based on a pattern of text (e.g. "login failed", "error: 12345").

        .PARAMETER After
            Filter the results based on datetime value.

        .PARAMETER Before
            Filter the results based on datetime value.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Instance, ErrorLog
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaErrorLog

        .EXAMPLE
            Get-DbaErrorLog -SqlInstance sql01\sharepoint

            Returns every log entry from sql01\sharepoint SQL Server instance.

        .EXAMPLE
            Get-DbaErrorLog -SqlInstance sql01\sharepoint -LogNumber 3, 6

            Returns all log entries for log number 3 and 6 on sql01\sharepoint SQL Server instance.

        .EXAMPLE
            Get-DbaErrorLog -SqlInstance sql01\sharepoint -Source Logon

            Returns every log entry, with a source of Logon, from sql01\sharepoint SQL Server instance.

        .EXAMPLE
            Get-DbaErrorLog -SqlInstance sql01\sharepoint -LogNumber 3 -Text "login failed"

            Returns every log entry for log number 3, with "login failed" in the text, from sql01\sharepoint SQL Server instance.

        .EXAMPLE
            $servers = "sql2014","sql2016", "sqlcluster\sharepoint"
            $servers | Get-DbaErrorLog -LogNumber 0

            Returns the most recent SQL Server error logs for "sql2014","sql2016" and "sqlcluster\sharepoint"

        .EXAMPLE
            Get-DbaErrorLog -SqlInstance sql01\sharepoint -After '11/14/2006 00:00'

            Returns every log entry found after the date 11/14/2006 00:00 from sql101\sharepoint SQL Server instance.

        .EXAMPLE
            Get-DbaErrorLog -SqlInstance sql01\sharepoint -Before '08/16/2016 00:00'

            Returns every log entry found before the date 08/16/2016 00:00 from sql101\sharepoint SQL Server instance.
        #>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [ValidateRange(0, 99)]
        [int[]]$LogNumber,
        [object[]]$Source,
        [string]$Text,
        [datetime]$After,
        [datetime]$Before,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Get-DbaSqlLog
    }
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($LogNumber) {
                foreach ($number in $lognumber) {
                    foreach ($object in $server.ReadErrorLog($number)) {
                        if ( ($Source -and $object.ProcessInfo -ne $Source) -or ($Text -and $object.Text -notlike "*$Text*") -or ($After -and $object.LogDate -lt $After) -or ($Before -and $object.LogDate -gt $Before) ) {
                            continue
                        }
                        Write-Message -Level Verbose -Message "Processing $object"
                        Add-Member -Force -InputObject $object -MemberType NoteProperty ComputerName -value $server.ComputerName
                        Add-Member -Force -InputObject $object -MemberType NoteProperty InstanceName -value $server.ServiceName
                        Add-Member -Force -InputObject $object -MemberType NoteProperty SqlInstance -value $server.DomainInstanceName

                        # Select all of the columns you'd like to show
                        Select-DefaultView -InputObject $object -Property ComputerName, InstanceName, SqlInstance, LogDate, 'ProcessInfo as Source', Text
                    }
                }
            }
            else {
                foreach ($object in $server.ReadErrorLog()) {
                    if ( ($Source -and $object.ProcessInfo -ne $Source) -or ($Text -and $object.Text -notlike "*$Text*") -or ($After -and $object.LogDate -lt $After) -or ($Before -and $object.LogDate -gt $Before) ) {
                        continue
                    }
                    Write-Message -Level Verbose -Message "Processing $object"
                    Add-Member -Force -InputObject $object -MemberType NoteProperty ComputerName -value $server.ComputerName
                    Add-Member -Force -InputObject $object -MemberType NoteProperty InstanceName -value $server.ServiceName
                    Add-Member -Force -InputObject $object -MemberType NoteProperty SqlInstance -value $server.DomainInstanceName

                    # Select all of the columns you'd like to show
                    Select-DefaultView -InputObject $object -Property ComputerName, InstanceName, SqlInstance, LogDate, 'ProcessInfo as Source', Text
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaErrorLogConfig.ps1
function Get-DbaErrorLogConfig {
    <#
        .SYNOPSIS
            Pulls the configuration for the ErrorLog on a given SQL Server instance
    
        .DESCRIPTION
            Pulls the configuration for the ErrorLog on a given SQL Server instance.

            Includes error log path, number of log files configured and size (SQL Server 2012+ only)

        .PARAMETER SqlInstance
            The target SQL Server instance(s)

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Instance, ErrorLog
            Author: Shawn Melton (@wsmelton)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaErrorLogConfig

       .EXAMPLE
            Get-DbaErrorLogConfig -SqlInstance server2017,server2014

            Returns error log configuration for server2017 and server2014
    #>
    [cmdletbinding()]
    param (
        [Parameter(ValueFromPipeline, Mandatory)]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $numLogs = $server.NumberOfLogFiles
            $logSize =
            if ($server.VersionMajor -ge 11) {
                [dbasize]($server.ErrorLogSizeKb * 1024)
            }
            else {
                $null
            }

            [PSCustomObject]@{
                ComputerName       = $server.ComputerName
                InstanceName       = $server.ServiceName
                SqlInstance        = $server.DomainInstanceName
                LogCount           = $numLogs
                LogSize            = $logSize
                LogPath            = $server.ErrorLogPath
            }
        }
    }
}
tools\dbatools\functions\Get-DbaEstimatedCompletionTime.ps1
function Get-DbaEstimatedCompletionTime {
    <#
.SYNOPSIS
Gets execution and estimated completion time information for queries

.DESCRIPTION
Gets execution and estimated completion time information for queries

Percent complete will show for the following commands

ALTER INDEX REORGANIZE
AUTO_SHRINK option with ALTER DATABASE
BACKUP DATABASE
DBCC CHECKDB
DBCC CHECKFILEGROUP
DBCC CHECKTABLE
DBCC INDEXDEFRAG
DBCC SHRINKDATABASE
DBCC SHRINKFILE
RECOVERY
RESTORE DATABASE
ROLLBACK
TDE ENCRYPTION

For additional information, check out https://blogs.sentryone.com/loriedwards/patience-dm-exec-requests/ and https://docs.microsoft.com/en-us/sql/relational-databases/system-dynamic-management-views/sys-dm-exec-requests-transact-sql

.PARAMETER SqlInstance
The SQL Server that you're connecting to.

.PARAMETER SqlCredential
SqlCredential object used to connect to the SQL Server as a different user.

.PARAMETER Database
The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

.PARAMETER ExcludeDatabase
The database(s) to exclude - this list is auto-populated from the server

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Database
Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Get-DbaEstimatedCompletionTime

.EXAMPLE
Get-DbaEstimatedCompletionTime -SqlInstance sql2016

Gets estimated completion times for queries performed against the entire server

.EXAMPLE
Get-DbaEstimatedCompletionTime -SqlInstance sql2016 | Select *

Gets estimated completion times for queries performed against the entire server PLUS the SQL query text of each command

.EXAMPLE
Get-DbaEstimatedCompletionTime -SqlInstance sql2016 | Where-Object { $_.Text -match 'somequerytext' }

Gets results for commands whose queries only match specific text (match is like LIKE but way more powerful)

.EXAMPLE
Get-DbaEstimatedCompletionTime -SqlInstance sql2016 -Database Northwind,pubs,Adventureworks2014

Gets estimated completion times for queries performed against the Northwind, pubs, and Adventureworks2014 databases

#>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $sql = "SELECT
                DB_NAME(r.database_id) as [Database],
                USER_NAME(r.user_id) as [Login],
                Command,
                start_time as StartTime,
                percent_complete as PercentComplete,

                  RIGHT('00000' + CAST(((DATEDIFF(s,start_time,GetDate()))/3600) as varchar),
                                CASE
                                    WHEN LEN(((DATEDIFF(s,start_time,GetDate()))/3600)) < 2 THEN 2
                                    ELSE LEN(((DATEDIFF(s,start_time,GetDate()))/3600))
                                 END)  + ':'
                + RIGHT('00' + CAST((DATEDIFF(s,start_time,GetDate())%3600)/60 as varchar), 2) + ':'
                + RIGHT('00' + CAST((DATEDIFF(s,start_time,GetDate())%60) as varchar), 2) as RunningTime,

                  RIGHT('00000' + CAST((estimated_completion_time/3600000) as varchar),
                        CASE
                                    WHEN LEN((estimated_completion_time/3600000)) < 2 THEN 2
                                    ELSE LEN((estimated_completion_time/3600000))
                         END)  + ':'
                + RIGHT('00' + CAST((estimated_completion_time %3600000)/60000 as varchar), 2) + ':'
                + RIGHT('00' + CAST((estimated_completion_time %60000)/1000 as varchar), 2) as EstimatedTimeToGo,
                dateadd(second,estimated_completion_time/1000, getdate()) as EstimatedCompletionTime,
                s.Text
             FROM sys.dm_exec_requests r
            CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) s"
    }

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential

            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($Database) {
                $includedatabases = $Database -join "','"
                $sql = "$sql WHERE DB_NAME(r.database_id) in ('$includedatabases')"
            }

            if ($ExcludeDatabase) {
                $excludedatabases = $ExcludeDatabase -join "','"
                $sql = "$sql WHERE DB_NAME(r.database_id) not in ('$excludedatabases')"
            }

            Write-Message -Level Debug -Message $sql
            foreach ($row in ($server.Query($sql))) {
                [pscustomobject]@{
                    ComputerName            = $server.ComputerName
                    InstanceName            = $server.ServiceName
                    SqlInstance             = $server.DomainInstanceName
                    Database                = $row.Database
                    Login                   = $row.Login
                    Command                 = $row.Command
                    PercentComplete         = $row.PercentComplete
                    StartTime               = $row.StartTime
                    RunningTime             = $row.RunningTime
                    EstimatedTimeToGo       = $row.EstimatedTimeToGo
                    EstimatedCompletionTime = $row.EstimatedCompletionTime
                    Text                    = $row.Text
                } | Select-DefaultView -ExcludeProperty Text
            }
        }
    }
}

tools\dbatools\functions\Get-DbaExecutionPlan.ps1
function Get-DbaExecutionPlan {
    <#
.SYNOPSIS
Gets execution plans and metadata

.DESCRIPTION
Gets execution plans and metadata. Can pipe to Export-DbaExecutionPlan :D

Thanks to
    https://www.simple-talk.com/sql/t-sql-programming/dmvs-for-query-plan-metadata/
    and
    http://www.scarydba.com/2017/02/13/export-plans-cache-sqlplan-file/
for the idea and query.

.PARAMETER SqlInstance
The SQL Server that you're connecting to.

.PARAMETER SqlCredential
Credential object used to connect to the SQL Server as a different user

.PARAMETER Database
Return restore information for only specific databases. These are only the databases that currently exist on the server.

.PARAMETER ExcludeDatabase
Return restore information for all but these specific databases

.PARAMETER SinceCreation
Datetime object used to narrow the results to a date

.PARAMETER SinceLastExecution
Datetime object used to narrow the results to a date

.PARAMETER ExcludeEmptyQueryPlan
Exclude results with empty query plan

.PARAMETER Force
Returns a ton of raw information about the execution plans

.PARAMETER EnableException
    By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
    Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.


.NOTES
Tags: Performance
dbatools PowerShell module (https://dbatools.io, [email protected])
Copyright (C) 2016 Chrissy LeMaire
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Get-DbaExecutionPlan

.EXAMPLE
Get-DbaExecutionPlan -SqlInstance sqlserver2014a

Gets all execution plans on  sqlserver2014a

.EXAMPLE
Get-DbaExecutionPlan -SqlInstance sqlserver2014a -Database db1, db2 -SinceLastExecution '7/1/2016 10:47:00'

Gets all execution plans for databases db1 and db2 on sqlserver2014a since July 1, 2016 at 10:47 AM.

.EXAMPLE
Get-DbaExecutionPlan -SqlInstance sqlserver2014a, sql2016 -Exclude db1 | Format-Table

Gets execution plan info for all databases except db1 on sqlserver2014a and sql2016 and makes the output pretty

.EXAMPLE
Get-DbaExecutionPlan -SqlInstance sql2014 -Database AdventureWorks2014, pubs -Force

Gets super detailed information for execution plans on only for AdventureWorks2014 and pubs

#>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [datetime]$SinceCreation,
        [datetime]$SinceLastExecution,
        [switch]$ExcludeEmptyQueryPlan,
        [switch]$Force,
        [switch]$EnableException
    )

    begin {

        if ($SinceCreation -ne $null) {
            $SinceCreation = $SinceCreation.ToString("yyyy-MM-dd HH:mm:ss")
        }

        if ($SinceLastExecution -ne $null) {
            $SinceLastExecution = $SinceLastExecution.ToString("yyyy-MM-dd HH:mm:ss")
        }
    }
    process {

        foreach ($instance in $sqlinstance) {
            try {
                try {
                    Write-Message -Level Verbose -Message "Connecting to $instance."
                    $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential -MinimumVersion 9
                }
                catch {
                    Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                }

                if ($force -eq $true) {
                    $select = "SELECT * "
                }
                else {
                    $select = "SELECT DB_NAME(deqp.dbid) as DatabaseName, OBJECT_NAME(deqp.objectid) as ObjectName,
                    detqp.query_plan AS SingleStatementPlan,
                    deqp.query_plan AS BatchQueryPlan,
                    ROW_NUMBER() OVER ( ORDER BY Statement_Start_offset ) AS QueryPosition,
                    sql_handle as SqlHandle,
                    plan_handle as PlanHandle,
                    creation_time as CreationTime,
                    last_execution_time as LastExecutionTime"
                }

                $from = " FROM sys.dm_exec_query_stats deqs
                        CROSS APPLY sys.dm_exec_text_query_plan(deqs.plan_handle,
                            deqs.statement_start_offset,
                            deqs.statement_end_offset) AS detqp
                        CROSS APPLY sys.dm_exec_query_plan(deqs.plan_handle) AS deqp
                        CROSS APPLY sys.dm_exec_sql_text(deqs.plan_handle) AS execText"

                if ($ExcludeDatabase -or $Database -or $SinceCreation.length -gt 0 -or $SinceLastExecution.length -gt 0 -or $ExcludeEmptyQueryPlan -eq $true) {
                    $where = " WHERE "
                }

                $wherearray = @()

                if ($Database) {
                    $dblist = $Database -join "','"
                    $wherearray += " DB_NAME(deqp.dbid) in ('$dblist') "
                }

                if ($null -ne $SinceCreation) {
                    Write-Message -Level Verbose -Message "Adding creation time"
                    $wherearray += " creation_time >= '$SinceCreation' "
                }

                if ($null -ne $SinceLastExecution) {
                    Write-Message -Level Verbose -Message "Adding last exectuion time"
                    $wherearray += " last_execution_time >= '$SinceLastExecution' "
                }

                if ($ExcludeDatabase) {
                    $dblist = $ExcludeDatabase -join "','"
                    $wherearray += " DB_NAME(deqp.dbid) not in ('$dblist') "
                }

                if ($ExcludeEmptyQueryPlan) {
                    $wherearray += " detqp.query_plan is not null"
                }

                if ($where.length -gt 0) {
                    $wherearray = $wherearray -join " and "
                    $where = "$where $wherearray"
                }

                $sql = "$select $from $where"
                Write-Message -Level Debug -Message $sql

                if ($Force -eq $true) {
                    $server.Query($sql)
                }
                else {
                    foreach ($row in $server.Query($sql)) {
                        $simple = ([xml]$row.SingleStatementPlan).ShowPlanXML.BatchSequence.Batch.Statements.StmtSimple
                        $sqlhandle = "0x"; $row.sqlhandle | ForEach-Object { $sqlhandle += ("{0:X}" -f $_).PadLeft(2, "0") }
                        $planhandle = "0x"; $row.planhandle | ForEach-Object { $planhandle += ("{0:X}" -f $_).PadLeft(2, "0") }
                        $planWarnings = $simple.QueryPlan.Warnings.PlanAffectingConvert;

                        [pscustomobject]@{
                            ComputerName                      = $server.ComputerName
                            InstanceName                      = $server.ServiceName
                            SqlInstance                       = $server.DomainInstanceName
                            DatabaseName                      = $row.DatabaseName
                            ObjectName                        = $row.ObjectName
                            QueryPosition                     = $row.QueryPosition
                            SqlHandle                         = $SqlHandle
                            PlanHandle                        = $PlanHandle
                            CreationTime                      = $row.CreationTime
                            LastExecutionTime                 = $row.LastExecutionTime
                            StatementCondition                = ([xml]$row.SingleStatementPlan).ShowPlanXML.BatchSequence.Batch.Statements.StmtCond
                            StatementSimple                   = $simple
                            StatementId                       = $simple.StatementId
                            StatementCompId                   = $simple.StatementCompId
                            StatementType                     = $simple.StatementType
                            RetrievedFromCache                = $simple.RetrievedFromCache
                            StatementSubTreeCost              = $simple.StatementSubTreeCost
                            StatementEstRows                  = $simple.StatementEstRows
                            SecurityPolicyApplied             = $simple.SecurityPolicyApplied
                            StatementOptmLevel                = $simple.StatementOptmLevel
                            QueryHash                         = $simple.QueryHash
                            QueryPlanHash                     = $simple.QueryPlanHash
                            StatementOptmEarlyAbortReason     = $simple.StatementOptmEarlyAbortReason
                            CardinalityEstimationModelVersion = $simple.CardinalityEstimationModelVersion

                            ParameterizedText                 = $simple.ParameterizedText
                            StatementSetOptions               = $simple.StatementSetOptions
                            QueryPlan                         = $simple.QueryPlan
                            BatchConditionXml                 = ([xml]$row.BatchQueryPlan).ShowPlanXML.BatchSequence.Batch.Statements.StmtCond
                            BatchSimpleXml                    = ([xml]$row.BatchQueryPlan).ShowPlanXML.BatchSequence.Batch.Statements.StmtSimple
                            BatchQueryPlanRaw                 = [xml]$row.BatchQueryPlan
                            SingleStatementPlanRaw            = [xml]$row.SingleStatementPlan
                            PlanWarnings                      = $planWarnings
                        } | Select-DefaultView -ExcludeProperty BatchQueryPlan, SingleStatementPlan, BatchConditionXmlRaw, BatchQueryPlanRaw, SingleStatementPlanRaw, PlanWarnings
                    }
                }
            }
            catch {
                Stop-Function -Message "Query Failure Failure" -ErrorRecord $_ -Target $instance -Continue
            }
        }
    }
}
tools\dbatools\functions\Get-DbaFile.ps1
function Get-DbaFile {
    <#
.SYNOPSIS
Get-DbaFile finds files in any directory specified on a remote SQL Server

.DESCRIPTION
This command searches all specified directories, allowing a DBA to see file information on a server without direct access

You can filter by extension using the -FileType parameter. By default, the default data directory will be returned. You can provide and additional paths to search using the -Path parameter.

Thanks to serg-52 for the query:  https://www.sqlservercentral.com/Forums/Topic1642213-391-1.aspx

.PARAMETER SqlInstance
The SQL Server instance.

.PARAMETER SqlCredential
Allows you to login to servers using alternative credentials

.PARAMETER Path
Used to specify extra directories to search in addition to the default data directory.

.PARAMETER FileType
Used to specify filter by filetype. No dot required, just pass the extension.

.PARAMETER Depth
Used to specify recursive folder depth.  Default is 1, non-recursive.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Discovery
Author: Brandon Abshire, netnerds.net

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Get-DbaFile

.EXAMPLE
Get-DbaFile -SqlInstance sqlserver2014a -Path E:\Dir1
Logs into the SQL Server "sqlserver2014a" using Windows credentials and searches E:\Dir for all files

.EXAMPLE
Get-DbaFile -SqlInstance sqlserver2014a -SqlCredential $cred -Path 'E:\sql files'
Logs into the SQL Server "sqlserver2014a" using alternative credentials and returns all files in 'E:\sql files'

.EXAMPLE
$all = Get-DbaDefaultPath -SqlInstance sql2014
Get-DbaFile -SqlInstance sql2014 -Path $all.Data, $all.Log, $all.Backup -Depth 3
Returns the files in the default data, log and backup directories on sql2014, 3 directories deep (recursively).

.EXAMPLE
Get-DbaFile -SqlInstance sql2014 -Path 'E:\Dir1', 'E:\Dir2'
Returns the files in "E:\Dir1" and "E:Dir2" on sql2014

.EXAMPLE
Get-DbaFile -SqlInstance -Path 'E:\Dir1' sql2014, sql2016 -FileType fsf, mld
Finds files in E:\Dir1 ending with ".fsf" and ".mld" for both the servers sql2014 and sql2016.

.EXAMPLE
Get-DbaFile -SqlInstance -Path 'E:\Dir1' sql2014, sql2016 -FileType fsf, mld
Finds files in E:\Dir1 ending with ".fsf" and ".mld" for both the servers sql2014 and sql2016.
#>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$Path,
        [string[]]$FileType,
        [int]$Depth = 1,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        $sql = ""

        function Get-SQLDirTreeQuery {
            param
            (
                $PathList
            )

            $q1 += "DECLARE @myPath nvarchar(4000);
                    DECLARE @depth SMALLINT = $Depth;

                    IF OBJECT_ID('tempdb..#DirectoryTree') IS NOT NULL
                    DROP TABLE #DirectoryTree;

                    CREATE TABLE #DirectoryTree (
                       id int IDENTITY(1,1)
                       ,subdirectory nvarchar(512)
                       ,depth int
                       ,isfile bit
                       , ParentDirectory int
                       ,flag tinyint default(0));"

            $q2 = "SET @myPath = 'dirname'
                    -- top level directory
                    INSERT #DirectoryTree (subdirectory,depth,isfile)
                       VALUES (@myPath,0,0);
                    -- all the rest under top level
                    INSERT #DirectoryTree (subdirectory,depth,isfile)
                       EXEC master.sys.xp_dirtree @myPath,@depth,1;


                    UPDATE #DirectoryTree
                       SET ParentDirectory = (
                          SELECT MAX(Id) FROM #DirectoryTree
                          WHERE Depth = d.Depth - 1 AND Id < d.Id   )
                    FROM #DirectoryTree d
                    WHERE ParentDirectory is NULL;"

            $query_files_sql = "-- SEE all with full paths
                    WITH dirs AS (
                        SELECT
                           Id,subdirectory,depth,isfile,ParentDirectory,flag
                           , CAST (null AS NVARCHAR(MAX)) AS container
                           , CAST([subdirectory] AS NVARCHAR(MAX)) AS dpath
                           FROM #DirectoryTree
                           WHERE ParentDirectory IS NULL
                        UNION ALL
                        SELECT
                           d.Id,d.subdirectory,d.depth,d.isfile,d.ParentDirectory,d.flag
                           , dpath as container
                           , dpath +'\'+d.[subdirectory]
                        FROM #DirectoryTree AS d
                        INNER JOIN dirs ON  d.ParentDirectory = dirs.id
                        WHERE dpath NOT LIKE '%RECYCLE.BIN%'
                    )
                    SELECT subdirectory as filename, container as filepath, isfile, dpath as fullpath FROM dirs
                    WHERE container IS NOT NULL
                    -- Dir style ordering
                    ORDER BY container, isfile, subdirectory"

            # build the query string based on how many directories they want to enumerate
            $sql = $q1
            $sql += $($PathList | Where-Object { $_ -ne '' } | ForEach-Object { "$([System.Environment]::Newline)$($q2 -Replace 'dirname', $_)" })
            $sql += $query_files_sql
            #Write-Message -Level Debug -Message $sql
            return $sql
        }

        function Format-Path {
            param ($path)
            $path = $path.Trim()
            #Thank you windows 2000
            $path = $path -replace '[^A-Za-z0-9 _\.\-\\:]', '__'
            return $path
        }

        if ($FileType) {
            $FileTypeComparison = $FileType | ForEach-Object { $_.ToLower() } | Where-Object { $_ } | Sort-Object | Get-Unique
        }
    }

    process {
        foreach ($instance in $SqlInstance) {

            $paths = @()
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            # Get the default data and log directories from the instance
            if (-not (Test-Bound -ParameterName Path)) { $Path = (Get-DbaDefaultPath -SqlInstance $server).Data }

            Write-Message -Level Verbose -Message "Adding paths"
            $sql = Get-SQLDirTreeQuery $Path
            Write-Message -Level Debug -Message $sql

            # This should remain as not .Query() to be compat with a PSProvider Chrissy is working on
            $datatable = $server.ConnectionContext.ExecuteWithResults($sql).Tables.Rows

            Write-Message -Level Verbose -Message "$($datatable.Rows.Count) files found."
            if ($FileTypeComparison) {
                foreach ($row in $datatable) {
                    foreach ($type in $FileTypeComparison) {
                        if ($row.filename.ToLower().EndsWith(".$type")) {
                            [pscustomobject]@{
                                ComputerName   = $server.ComputerName
                                InstanceName   = $server.ServiceName
                                SqlInstance    = $server.DomainInstanceName
                                Filename       = $row.fullpath
                                RemoteFilename = Join-AdminUnc -Servername $server.ComputerName -Filepath $row.fullpath
                            } | Select-DefaultView -ExcludeProperty ComputerName, InstanceName, RemoteFilename
                        }
                    }
                }
            }
            else {
                foreach ($row in $datatable) {
                    [pscustomobject]@{
                        ComputerName   = $server.ComputerName
                        InstanceName   = $server.ServiceName
                        SqlInstance    = $server.DomainInstanceName
                        Filename       = $row.fullpath
                        RemoteFilename = Join-AdminUnc -Servername $server.ComputerName -Filepath $row.fullpath
                    } | Select-DefaultView -ExcludeProperty ComputerName, InstanceName, RemoteFilename
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaForceNetworkEncryption.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#

function Get-DbaForceNetworkEncryption {
    <#
    .SYNOPSIS
        Gets Force Encryption settings for a SQL Server instance

    .DESCRIPTION
        Gets Force Encryption settings for a SQL Server instance. Note that this requires access to the Windows Server - not the SQL instance itself.

        This setting is found in Configuration Manager.

    .PARAMETER SqlInstance
        The target SQL Server - defaults to localhost.

    .PARAMETER Credential
        Allows you to login to the computer (not sql instance) using alternative Windows credentials

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .PARAMETER WhatIf
        Shows what would happen if the command were to run. No actions are actually performed

    .PARAMETER Confirm
        Prompts you for confirmation before executing any changing operations within the command

    .EXAMPLE
        Get-DbaForceNetworkEncryption

        Gets Force Encryption properties on the default (MSSQLSERVER) instance on localhost - requires (and checks for) RunAs admin.

    .EXAMPLE
        Get-DbaForceNetworkEncryption -SqlInstance sql01\SQL2008R2SP2

        Gets Force Network Encryption for the SQL2008R2SP2 on sql01. Uses Windows Credentials to both login and view the registry.

    .NOTES
        Tags: Certificate

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT
#>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer", "ComputerName")]
        [DbaInstanceParameter[]]
        $SqlInstance = $env:COMPUTERNAME,

        [PSCredential]

        $Credential,

        [switch]
        [Alias('Silent')]$EnableException
    )
    process {

        foreach ($instance in $SqlInstance) {
            Write-Message -Level VeryVerbose -Message "Processing $instance" -Target $instance
            $null = Test-ElevationRequirement -ComputerName $instance -Continue

            Write-Message -Level Verbose -Message "Resolving hostname"
            $resolved = $null
            $resolved = Resolve-DbaNetworkName -ComputerName $instance

            if ($null -eq $resolved) {
                Stop-Function -Message "Can't resolve $instance" -Target $instance -Continue -Category InvalidArgument
            }

            Write-Message -Level Verbose -Message "Connecting to SQL WMI on $($instance.ComputerName)"
            try {
                $sqlwmi = Invoke-ManagedComputerCommand -ComputerName $resolved.FullComputerName -ScriptBlock { $wmi.Services } -Credential $Credential -ErrorAction Stop | Where-Object DisplayName -eq "SQL Server ($($instance.InstanceName))"
            }
            catch {
                Stop-Function -Message "Failed to access $instance" -Target $instance -Continue -ErrorRecord $_
            }

            $regroot = ($sqlwmi.AdvancedProperties | Where-Object Name -eq REGROOT).Value
            $vsname = ($sqlwmi.AdvancedProperties | Where-Object Name -eq VSNAME).Value
            try {
                $instancename = $sqlwmi.DisplayName.Replace('SQL Server (', '').Replace(')', '') # Don't clown, I don't know regex :(
            }
            catch {
                # Probably because the instance name has been aliased or does not exist or samthin
            }
            $serviceaccount = $sqlwmi.ServiceAccount

            if ([System.String]::IsNullOrEmpty($regroot)) {
                $regroot = $sqlwmi.AdvancedProperties | Where-Object { $_ -match 'REGROOT' }
                $vsname = $sqlwmi.AdvancedProperties | Where-Object { $_ -match 'VSNAME' }

                if (![System.String]::IsNullOrEmpty($regroot)) {
                    $regroot = ($regroot -Split 'Value\=')[1]
                    $vsname = ($vsname -Split 'Value\=')[1]
                }
                else {
                    Stop-Function -Message "Can't find instance $vsname on $instance" -Continue -Category ObjectNotFound -Target $instance
                }
            }

            if ([System.String]::IsNullOrEmpty($vsname)) { $vsname = $instance }

            Write-Message -Level Verbose -Message "Regroot: $regroot" -Target $instance
            Write-Message -Level Verbose -Message "ServiceAcct: $serviceaccount" -Target $instance
            Write-Message -Level Verbose -Message "InstanceName: $instancename" -Target $instance
            Write-Message -Level Verbose -Message "VSNAME: $vsname" -Target $instance

            $scriptblock = {
                $regpath = "Registry::HKEY_LOCAL_MACHINE\$($args[0])\MSSQLServer\SuperSocketNetLib"
                $cert = (Get-ItemProperty -Path $regpath -Name Certificate).Certificate
                $forceencryption = (Get-ItemProperty -Path $regpath -Name ForceEncryption).ForceEncryption

                # [pscustomobject] doesn't always work, unsure why. so return hashtable then turn it into  pscustomobject on client
                @{
                    ComputerName          = $env:COMPUTERNAME
                    InstanceName          = $args[2]
                    SqlInstance           = $args[1]
                    ForceEncryption       = ($forceencryption -eq $true)
                    CertificateThumbprint = $cert
                }
            }

            if ($PScmdlet.ShouldProcess("local", "Connecting to $instance")) {
                try {
                    $results = Invoke-Command2 -ComputerName $resolved.FullComputerName -Credential $Credential -ArgumentList $regroot, $vsname, $instancename -ScriptBlock $scriptblock -ErrorAction Stop -Raw
                    foreach ($result in $results) {
                        [pscustomobject]$result
                    }
                }
                catch {
                    Stop-Function -Message "Failed to connect to $($resolved.FullComputerName) using PowerShell remoting!" -ErrorRecord $_ -Target $instance -Continue
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaHelpIndex.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaHelpIndex {
    <#
        .SYNOPSIS
            Returns size, row and configuration information for indexes in databases.

        .DESCRIPTION
            This function will return detailed information on indexes (and optionally statistics) for all indexes in a database, or a given index should one be passed along.
            As this uses SQL Server DMVs to access the data it will only work in 2005 and up (sorry folks still running SQL Server 2000).
            For performance reasons certain statistics information will not be returned from SQL Server 2005 if an ObjectName is not provided.

            The data includes:
                - ObjectName: the table containing the index
                - IndexType: clustered/non-clustered/columnstore and whether the index is unique/primary key
                - KeyColumns: the key columns of the index
                - IncludeColumns: any include columns in the index
                - FilterDefinition: any filter that may have been used in the index
                - DataCompression: row/page/none depending upon whether or not compression has been used
                - IndexReads: the number of reads of the index since last restart or index rebuild
                - IndexUpdates: the number of writes to the index since last restart or index rebuild
                - SizeKB: the size the index in KB
                - IndexRows: the number of the rows in the index (note filtered indexes will have fewer rows than exist in the table)
                - IndexLookups: the number of lookups that have been performed (only applicable for the heap or clustered index)
                - MostRecentlyUsed: when the index was most recently queried (default to 1900 for when never read)
                - StatsSampleRows: the number of rows queried when the statistics were built/rebuilt (not included in SQL Server 2005 unless ObjectName is specified)
                - StatsRowMods: the number of changes to the statistics since the last rebuild
                - HistogramSteps: the number of steps in the statistics histogram (not included in SQL Server 2005 unless ObjectName is specified)
                - StatsLastUpdated: when the statistics were last rebuilt (not included in SQL Server 2005 unless ObjectName is specified)

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process. This list is auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude. This list is auto-populated from the server.

        .PARAMETER ObjectName
            The name of a table for which you want to obtain the index information. If the two part naming convention for an object is not used it will use the default schema for the executing user. If not passed it will return data on all indexes in a given database.

        .PARAMETER IncludeStats
            If this switch is enabled, statistics as well as indexes will be returned in the output (statistics information such as the StatsRowMods will always be returned for indexes).

        .PARAMETER IncludeDataTypes
            If this switch is enabled, the output will include the data type of each column that makes up a part of the index definition (key and include columns).

        .PARAMETER IncludeFragmentation
            If this switch is enabled, the output will include fragmentation information.

        .PARAMETER InputObject
           Allows piping from Get-DbaDatabase
   
        .PARAMETER Raw
            If this switch is enabled, results may be less user-readable but more suitable for processing by other code.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Index
            Author: Nic Cain, https://sirsql.net/

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaHelpIndex

        .EXAMPLE
            Get-DbaHelpIndex -SqlInstance localhost -Database MyDB

            Returns information on all indexes on the MyDB database on the localhost.

        .EXAMPLE
            Get-DbaHelpIndex -SqlInstance localhost -Database MyDB,MyDB2

            Returns information on all indexes on the MyDB & MyDB2 databases.

        .EXAMPLE
            Get-DbaHelpIndex -SqlInstance localhost -Database MyDB -ObjectName dbo.Table1

            Returns index information on the object dbo.Table1 in the database MyDB.

        .EXAMPLE
            Get-DbaHelpIndex -SqlInstance localhost -Database MyDB -ObjectName dbo.Table1 -IncludeStats

            Returns information on the indexes and statistics for the table dbo.Table1 in the MyDB database.

        .EXAMPLE
            Get-DbaHelpIndex -SqlInstance localhost -Database MyDB -ObjectName dbo.Table1 -IncludeDataTypes

            Returns the index information for the table dbo.Table1 in the MyDB database, and includes the data types for the key and include columns.

        .EXAMPLE
            Get-DbaHelpIndex -SqlInstance localhost -Database MyDB -ObjectName dbo.Table1 -Raw

            Returns the index information for the table dbo.Table1 in the MyDB database, and returns the numerical data without localized separators.

        .EXAMPLE
            Get-DbaHelpIndex -SqlInstance localhost -Database MyDB -IncludeStats -Raw

            Returns the index information for all indexes in the MyDB database as well as their statistics, and formats the numerical data without localized separators.

        .EXAMPLE
            Get-DbaHelpIndex -SqlInstance localhost -Database MyDB -IncludeFragmentation

            Returns the index information for all indexes in the MyDB database as well as their fragmentation
    
        .EXAMPLE
            Get-DbaDatabase -SqlInstance sql2017 -Database MyDB | Get-DbaHelpIndex

            Returns the index information for all indexes in the MyDB database
  #>
    [CmdletBinding()]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Parameter(ValueFromPipeline)]
        [Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
        [string]$ObjectName,
        [switch]$IncludeStats,
        [switch]$IncludeDataTypes,
        [switch]$Raw,
        [switch]$IncludeFragmentation,
        [Alias('Silent')]
        [switch]$EnableException
    )
    
    begin {
        
        #Add the table predicate to the query
        if (!$ObjectName) {
            $TablePredicate = "DECLARE @TableName NVARCHAR(256);";
        }
        else {
            $TablePredicate = "DECLARE @TableName NVARCHAR(256); SET @TableName = '$ObjectName';";
        }
        
        #Add Fragmentation info if requested
        $FragSelectColumn = ", NULL as avg_fragmentation_in_percent"
        $FragJoin = ''
        $OutputProperties = 'DatabaseName,ObjectName,IndexName,IndexType,KeyColumns,IncludeColumns,FilterDefinition,DataCompression,IndexReads,IndexUpdates,SizeKB,IndexRows,IndexLookups,MostRecentlyUsed,StatsSampleRows,StatsRowMods,HistogramSteps,StatsLastUpdated'
        if ($IncludeFragmentation) {
            $FragSelectColumn = ', pstat.avg_fragmentation_in_percent'
            $FragJoin = "LEFT JOIN sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL , 'DETAILED') pstat
             ON pstat.database_id = ustat.database_id
             AND pstat.object_id = ustat.object_id
             AND pstat.index_id = ustat.index_id"
            $OutputProperties = 'DatabaseName,ObjectName,IndexName,IndexType,KeyColumns,IncludeColumns,FilterDefinition,DataCompression,IndexReads,IndexUpdates,SizeKB,IndexRows,IndexLookups,MostRecentlyUsed,StatsSampleRows,StatsRowMods,HistogramSteps,StatsLastUpdated,IndexFragInPercent'
        }
        $OutputProperties = $OutputProperties.Split(',')
        #Figure out if we are including stats in the results
        if ($IncludeStats) {
            $IncludeStatsPredicate = "";
        }
        else {
            $IncludeStatsPredicate = "WHERE IndexType != 'STATISTICS'";
        }
        
        #Data types being returns with the results?
        if ($IncludeDataTypes) {
            $IncludeDataTypesPredicate = 'DECLARE @IncludeDataTypes BIT; SET @IncludeDataTypes = 1';
        }
        else {
            $IncludeDataTypesPredicate = 'DECLARE @IncludeDataTypes BIT; SET @IncludeDataTypes = 0';
        }
        
        #region SizesQuery
        $SizesQuery = "
            SET NOCOUNT ON;
            SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

            $TablePredicate
            $IncludeDataTypesPredicate
            ;

        DECLARE @IndexUsageStats TABLE
            (
            object_id INT ,
            index_id INT ,
            user_scans BIGINT ,
            user_seeks BIGINT ,
            user_updates BIGINT ,
            user_lookups BIGINT ,
            last_user_lookup DATETIME2(0) ,
            last_user_scan DATETIME2(0) ,
            last_user_seek DATETIME2(0) ,
            avg_fragmentation_in_percent FLOAT
            );

        DECLARE @StatsInfo TABLE
            (
            object_id INT ,
            stats_id INT ,
            stats_column_name NVARCHAR(128) ,
            stats_column_id INT ,
            stats_name NVARCHAR(128) ,
            stats_last_updated DATETIME2(0) ,
            stats_sampled_rows BIGINT ,
            rowmods BIGINT ,
            histogramsteps INT ,
            StatsRows BIGINT ,
            FullObjectName NVARCHAR(256)
            );

        INSERT  INTO @IndexUsageStats
                ( object_id ,
                index_id ,
                user_scans ,
                user_seeks ,
                user_updates ,
                user_lookups ,
                last_user_lookup ,
                last_user_scan ,
                last_user_seek ,
                avg_fragmentation_in_percent
                )
                SELECT  ustat.object_id ,
                        ustat.index_id ,
                        ustat.user_scans ,
                        ustat.user_seeks ,
                        ustat.user_updates ,
                        ustat.user_lookups ,
                        ustat.last_user_lookup ,
                        ustat.last_user_scan ,
                        ustat.last_user_seek
                        $FragSelectColumn
                FROM    sys.dm_db_index_usage_stats ustat
                $FragJoin
                WHERE   ustat.database_id = DB_ID();

        INSERT  INTO @StatsInfo
                ( object_id ,
                stats_id ,
                stats_column_name ,
                stats_column_id ,
                stats_name ,
                stats_last_updated ,
                stats_sampled_rows ,
                rowmods ,
                histogramsteps ,
                StatsRows ,
                FullObjectName
                )
                SELECT  s.object_id ,
                        s.stats_id ,
                        c.name ,
                        sc.stats_column_id ,
                        s.name ,
                        sp.last_updated ,
                        sp.rows_sampled ,
                        sp.modification_counter ,
                        sp.steps ,
                        sp.rows ,
                        QUOTENAME(sch.name) + '.' + QUOTENAME(t.name) AS FullObjectName
                FROM    [sys].[stats] AS [s]
                        INNER JOIN sys.stats_columns sc ON s.stats_id = sc.stats_id
                                                        AND s.object_id = sc.object_id
                        INNER JOIN sys.columns c ON c.object_id = sc.object_id
                                                    AND c.column_id = sc.column_id
                        INNER JOIN sys.tables t ON c.object_id = t.object_id
                        INNER JOIN sys.schemas sch ON sch.schema_id = t.schema_id
                        OUTER APPLY sys.dm_db_stats_properties([s].[object_id],
                                                            [s].[stats_id]) AS [sp]
                WHERE   s.object_id = CASE WHEN @TableName IS NULL THEN s.object_id
                                        else OBJECT_ID(@TableName)
                                    END;


        ;
        WITH    cteStatsInfo
                AS ( SELECT   object_id ,
                                si.stats_id ,
                                si.stats_name ,
                                STUFF((SELECT   N', ' + stats_column_name
                                    FROM     @StatsInfo si2
                                    WHERE    si2.object_id = si.object_id
                                                AND si2.stats_id = si.stats_id
                                    ORDER BY si2.stats_column_id
                                FOR   XML PATH(N'') ,
                                        TYPE).value(N'.[1]', N'nvarchar(1000)'), 1,
                                    2, N'') AS StatsColumns ,
                                MAX(si.stats_sampled_rows) AS SampleRows ,
                                MAX(si.rowmods) AS RowMods ,
                                MAX(si.histogramsteps) AS HistogramSteps ,
                                MAX(si.stats_last_updated) AS StatsLastUpdated ,
                                MAX(si.StatsRows) AS StatsRows,
                                FullObjectName
                    FROM     @StatsInfo si
                    GROUP BY si.object_id ,
                                si.stats_id ,
                                si.stats_name ,
                                si.FullObjectName
                    ),
                cteIndexSizes
                AS ( SELECT   object_id ,
                                index_id ,
                                CASE WHEN index_id < 2
                                    THEN ( ( SUM(in_row_data_page_count
                                                + lob_used_page_count
                                                + row_overflow_used_page_count)
                                            * 8192 ) / 1024 )
                                    else ( ( SUM(used_page_count) * 8192 ) / 1024 )
                                END AS SizeKB
                    FROM     sys.dm_db_partition_stats
                    GROUP BY object_id ,
                                index_id
                    ),
                cteRows
                AS ( SELECT   object_id ,
                                index_id ,
                                SUM(rows) AS IndexRows
                    FROM     sys.partitions
                    GROUP BY object_id ,
                                index_id
                    ),
                cteIndex
                AS ( SELECT   OBJECT_NAME(c.object_id) AS ObjectName ,
                                c.object_id ,
                                c.index_id ,
                                i.name COLLATE SQL_Latin1_General_CP1_CI_AS AS name ,
                                c.index_column_id ,
                                c.column_id ,
                                c.is_included_column ,
                                CASE WHEN @IncludeDataTypes = 0
                                        AND c.is_descending_key = 1
                                    THEN sc.name + ' DESC'
                                    WHEN @IncludeDataTypes = 0
                                        AND c.is_descending_key = 0 THEN sc.name
                                    WHEN @IncludeDataTypes = 1
                                        AND c.is_descending_key = 1
                                        AND c.is_included_column = 0
                                    THEN sc.name + ' DESC (' + t.name + ') '
                                    WHEN @IncludeDataTypes = 1
                                        AND c.is_descending_key = 0
                                        AND c.is_included_column = 0
                                    THEN sc.name + ' (' + t.name + ')'
                                    else sc.name
                                END AS ColumnName ,
                                i.filter_definition ,
                                ISNULL(dd.user_scans, 0) AS user_scans ,
                                ISNULL(dd.user_seeks, 0) AS user_seeks ,
                                ISNULL(dd.user_updates, 0) AS user_updates ,
                                ISNULL(dd.user_lookups, 0) AS user_lookups ,
                                CONVERT(DATETIME2(0), ISNULL(dd.last_user_lookup,
                                                            '1901-01-01')) AS LastLookup ,
                                CONVERT(DATETIME2(0), ISNULL(dd.last_user_scan,
                                                            '1901-01-01')) AS LastScan ,
                                CONVERT(DATETIME2(0), ISNULL(dd.last_user_seek,
                                                            '1901-01-01')) AS LastSeek ,
                                i.fill_factor ,
                                c.is_descending_key ,
                                p.data_compression_desc ,
                                i.type_desc ,
                                i.is_unique ,
                                i.is_unique_constraint ,
                                i.is_primary_key ,
                                ci.SizeKB ,
                                cr.IndexRows ,
                                QUOTENAME(sch.name) + '.' + QUOTENAME(tbl.name) AS FullObjectName ,
                                ISNULL(dd.avg_fragmentation_in_percent, 0) as avg_fragmentation_in_percent
                    FROM     sys.indexes i
                                JOIN sys.index_columns c ON i.object_id = c.object_id
                                                            AND i.index_id = c.index_id
                                JOIN sys.columns sc ON c.object_id = sc.object_id
                                                    AND c.column_id = sc.column_id
                                INNER JOIN sys.tables tbl ON c.object_id = tbl.object_id
                                INNER JOIN sys.schemas sch ON sch.schema_id = tbl.schema_id
                                LEFT JOIN sys.types t ON sc.user_type_id = t.user_type_id
                                LEFT JOIN @IndexUsageStats dd ON i.object_id = dd.object_id
                                                                AND i.index_id = dd.index_id --and dd.database_id = db_id()
                                JOIN sys.partitions p ON i.object_id = p.object_id
                                                        AND i.index_id = p.index_id
                                JOIN cteIndexSizes ci ON i.object_id = ci.object_id
                                                        AND i.index_id = ci.index_id
                                JOIN cteRows cr ON i.object_id = cr.object_id
                                                AND i.index_id = cr.index_id
                    WHERE    i.object_id = CASE WHEN @TableName IS NULL
                                                THEN i.object_id
                                                else OBJECT_ID(@TableName)
                                            END
                    ),
                cteResults
                AS ( SELECT   ci.FullObjectName ,
                                ci.object_id ,
                                MAX(index_id) AS Index_Id ,
                                ci.type_desc
                                + CASE WHEN ci.is_primary_key = 1
                                    THEN ' (PRIMARY KEY)'
                                    WHEN ci.is_unique_constraint = 1
                                    THEN ' (UNIQUE CONSTRAINT)'
                                    WHEN ci.is_unique = 1 THEN ' (UNIQUE)'
                                    else ''
                                END AS IndexType ,
                                name AS IndexName ,
                                STUFF((SELECT   N', ' + ColumnName
                                    FROM     cteIndex ci2
                                    WHERE    ci2.name = ci.name
                                                AND ci2.is_included_column = 0
                                    GROUP BY ci2.index_column_id ,
                                                ci2.ColumnName
                                    ORDER BY ci2.index_column_id
                                FOR   XML PATH(N'') ,
                                        TYPE).value(N'.[1]', N'nvarchar(1000)'), 1,
                                    2, N'') AS KeyColumns ,
                                ISNULL(STUFF((SELECT    N',  ' + ColumnName
                                            FROM      cteIndex ci3
                                            WHERE     ci3.name = ci.name
                                                        AND ci3.is_included_column = 1
                                            GROUP BY  ci3.index_column_id ,
                                                        ci3.ColumnName
                                            ORDER BY  ci3.index_column_id
                                    FOR   XML PATH(N'') ,
                                                TYPE).value(N'.[1]',
                                                            N'nvarchar(1000)'), 1, 2,
                                            N''), '') AS IncludeColumns ,
                                ISNULL(filter_definition, '') AS FilterDefinition ,
                                ci.fill_factor ,
                                CASE WHEN ci.data_compression_desc = 'NONE' THEN ''
                                    else ci.data_compression_desc
                                END AS DataCompression ,
                                MAX(ci.user_seeks) + MAX(ci.user_scans)
                                + MAX(ci.user_lookups) AS IndexReads ,
                                MAX(ci.user_lookups) AS IndexLookups ,
                                ci.user_updates AS IndexUpdates ,
                                ci.SizeKB AS SizeKB ,
                                ci.IndexRows AS IndexRows ,
                                CASE WHEN LastScan > LastSeek
                                        AND LastScan > LastLookup THEN LastScan
                                    WHEN LastSeek > LastScan
                                        AND LastSeek > LastLookup THEN LastSeek
                                    WHEN LastLookup > LastScan
                                        AND LastLookup > LastSeek THEN LastLookup
                                    else ''
                                END AS MostRecentlyUsed ,
                                AVG(ci.avg_fragmentation_in_percent) as avg_fragmentation_in_percent
                    FROM     cteIndex ci
                    GROUP BY ci.ObjectName ,
                                ci.name ,
                                ci.filter_definition ,
                                ci.object_id ,
                                ci.LastLookup ,
                                ci.LastSeek ,
                                ci.LastScan ,
                                ci.user_updates ,
                                ci.fill_factor ,
                                ci.data_compression_desc ,
                                ci.type_desc ,
                                ci.is_primary_key ,
                                ci.is_unique ,
                                ci.is_unique_constraint ,
                                ci.SizeKB ,
                                ci.IndexRows ,
                                ci.FullObjectName
                    ),
                AllResults
                AS ( SELECT   c.FullObjectName ,
                                ISNULL(IndexType, 'STATISTICS') AS IndexType ,
                                ISNULL(IndexName, si.stats_name) AS IndexName ,
                                ISNULL(KeyColumns, si.StatsColumns) AS KeyColumns ,
                                ISNULL(IncludeColumns, '') AS IncludeColumns ,
                                FilterDefinition ,
                                fill_factor AS [FillFactor] ,
                                DataCompression ,
                                IndexReads ,
                                IndexUpdates ,
                                SizeKB ,
                                IndexRows ,
                                IndexLookups ,
                                MostRecentlyUsed ,
                                SampleRows AS StatsSampleRows ,
                                RowMods AS StatsRowMods ,
                                si.HistogramSteps ,
                                si.StatsLastUpdated ,
                                avg_fragmentation_in_percent AS IndexFragInPercent,
                                1 AS Ordering
                    FROM     cteResults c
                                INNER JOIN cteStatsInfo si ON si.object_id = c.object_id
                                                            AND si.stats_id = c.Index_Id
                    UNION
                    SELECT   QUOTENAME(sch.name) + '.' + QUOTENAME(tbl.name) AS FullObjectName ,
                                'STATISTICS' ,
                                stats_name ,
                                StatsColumns ,
                                '' ,
                                '' AS FilterDefinition ,
                                '' AS Fill_Factor ,
                                '' AS DataCompression ,
                                '' AS IndexReads ,
                                '' AS IndexUpdates ,
                                '' AS SizeKB ,
                                StatsRows AS IndexRows ,
                                '' AS IndexLookups ,
                                '' AS MostRecentlyUsed ,
                                SampleRows AS StatsSampleRows ,
                                RowMods AS StatsRowMods ,
                                csi.HistogramSteps ,
                                csi.StatsLastUpdated ,
                                '' AS IndexFragInPercent ,
                                2
                    FROM     cteStatsInfo csi
                    INNER JOIN sys.tables tbl ON csi.object_id = tbl.object_id
                                INNER JOIN sys.schemas sch ON sch.schema_id = tbl.schema_id
                    WHERE    stats_id NOT IN (
                                SELECT  stats_id
                                FROM    cteResults c
                                        INNER JOIN cteStatsInfo si ON si.object_id = c.object_id
                                                                    AND si.stats_id = c.Index_Id )
                    )
            SELECT  FullObjectName ,
                    ISNULL(IndexType, 'STATISTICS') AS IndexType ,
                    IndexName ,
                    KeyColumns ,
                    ISNULL(IncludeColumns, '') AS IncludeColumns ,
                    FilterDefinition ,
                    [FillFactor] AS [FillFactor] ,
                    DataCompression ,
                    IndexReads ,
                    IndexUpdates ,
                    SizeKB ,
                    IndexRows ,
                    IndexLookups ,
                    MostRecentlyUsed ,
                    StatsSampleRows ,
                    StatsRowMods ,
                    HistogramSteps ,
                    StatsLastUpdated ,
                    IndexFragInPercent
            FROM    AllResults
                    $IncludeStatsPredicate
        OPTION  ( RECOMPILE );
        "
        #endRegion SizesQuery
        
        
        #region sizesQuery2005
        $SizesQuery2005 = "
        SET NOCOUNT ON;
        SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

        $TablePredicate
        $IncludeDataTypesPredicate
        ;

        DECLARE @AllResults TABLE
            (
                RowNum INT ,
                FullObjectName	NVARCHAR(300) ,
                IndexType	NVARCHAR(256) ,
                IndexName	NVARCHAR(256) ,
                KeyColumns	NVARCHAR(2000) ,
                IncludeColumns	NVARCHAR(2000) ,
                FilterDefinition	NVARCHAR(100) ,
                [FillFactor]	TINYINT ,
                DataCompression	CHAR(4) ,
                IndexReads	BIGINT ,
                IndexUpdates	BIGINT ,
                SizeKB	BIGINT ,
                IndexRows	BIGINT ,
                IndexLookups	BIGINT ,
                MostRecentlyUsed	DATETIME ,
                StatsSampleRows	BIGINT ,
                StatsRowMods	BIGINT ,
                HistogramSteps	INT	,
                StatsLastUpdated	DATETIME ,
                object_id BIGINT ,
                index_id BIGINT
            );

        DECLARE @IndexUsageStats TABLE
            (
            object_id INT ,
            index_id INT ,
            user_scans BIGINT ,
            user_seeks BIGINT ,
            user_updates BIGINT ,
            user_lookups BIGINT ,
            last_user_lookup DATETIME ,
            last_user_scan DATETIME ,
            last_user_seek DATETIME ,
            avg_fragmentation_in_percent FLOAT
            );

        DECLARE @StatsInfo TABLE
            (
            object_id INT ,
            stats_id INT ,
            stats_column_name NVARCHAR(128) ,
            stats_column_id INT ,
            stats_name NVARCHAR(128) ,
            stats_last_updated DATETIME ,
            stats_sampled_rows BIGINT ,
            rowmods BIGINT ,
            histogramsteps INT ,
            StatsRows BIGINT ,
            FullObjectName NVARCHAR(256)
            );

        INSERT  INTO @IndexUsageStats
                ( object_id ,
                index_id ,
                user_scans ,
                user_seeks ,
                user_updates ,
                user_lookups ,
                last_user_lookup ,
                last_user_scan ,
                last_user_seek ,
                avg_fragmentation_in_percent
                )
                SELECT  ustat.object_id ,
                        ustat.index_id ,
                        ustat.user_scans ,
                        ustat.user_seeks ,
                        ustat.user_updates ,
                        ustat.user_lookups ,
                        ustat.last_user_lookup ,
                        ustat.last_user_scan ,
                        ustat.last_user_seek
                        $FragSelectColumn
                FROM    sys.dm_db_index_usage_stats ustat
                $FragJoin
                WHERE   database_id = DB_ID();


        INSERT  INTO @StatsInfo
                ( object_id ,
                stats_id ,
                stats_column_name ,
                stats_column_id ,
                stats_name ,
                stats_last_updated ,
                stats_sampled_rows ,
                rowmods ,
                histogramsteps ,
                StatsRows ,
                FullObjectName
                )
                SELECT  s.object_id ,
                        s.stats_id ,
                        c.name ,
                        sc.stats_column_id ,
                        s.name ,
                        NULL AS last_updated ,
                        NULL AS rows_sampled ,
                        NULL AS modification_counter ,
                        NULL AS steps ,
                        NULL AS rows ,
                        QUOTENAME(sch.name) + '.' + QUOTENAME(t.name) AS FullObjectName
                FROM    [sys].[stats] AS [s]
                        INNER JOIN sys.stats_columns sc ON s.stats_id = sc.stats_id
                                                        AND s.object_id = sc.object_id
                        INNER JOIN sys.columns c ON c.object_id = sc.object_id
                                                    AND c.column_id = sc.column_id
                        INNER JOIN sys.tables t ON c.object_id = t.object_id
                        INNER JOIN sys.schemas sch ON sch.schema_id = t.schema_id
                    --   OUTER APPLY sys.dm_db_stats_properties([s].[object_id],
                    --                                        [s].[stats_id]) AS [sp]
                WHERE   s.object_id = CASE WHEN @TableName IS NULL THEN s.object_id
                                        else OBJECT_ID(@TableName)
                                    END;


        ;
        WITH    cteStatsInfo
                AS ( SELECT   object_id ,
                                si.stats_id ,
                                si.stats_name ,
                                STUFF((SELECT   N', ' + stats_column_name
                                    FROM     @StatsInfo si2
                                    WHERE    si2.object_id = si.object_id
                                                AND si2.stats_id = si.stats_id
                                    ORDER BY si2.stats_column_id
                                FOR   XML PATH(N'') ,
                                        TYPE).value(N'.[1]', N'nvarchar(1000)'), 1,
                                    2, N'') AS StatsColumns ,
                                MAX(si.stats_sampled_rows) AS SampleRows ,
                                MAX(si.rowmods) AS RowMods ,
                                MAX(si.histogramsteps) AS HistogramSteps ,
                                MAX(si.stats_last_updated) AS StatsLastUpdated ,
                                MAX(si.StatsRows) AS StatsRows,
                                FullObjectName
                    FROM     @StatsInfo si
                    GROUP BY si.object_id ,
                                si.stats_id ,
                                si.stats_name ,
                                si.FullObjectName
                    ),
                cteIndexSizes
                AS ( SELECT   object_id ,
                                index_id ,
                                CASE WHEN index_id < 2
                                    THEN ( ( SUM(in_row_data_page_count
                                                + lob_used_page_count
                                                + row_overflow_used_page_count)
                                            * 8192 ) / 1024 )
                                    else ( ( SUM(used_page_count) * 8192 ) / 1024 )
                                END AS SizeKB
                    FROM     sys.dm_db_partition_stats
                    GROUP BY object_id ,
                                index_id
                    ),
                cteRows
                AS ( SELECT   object_id ,
                                index_id ,
                                SUM(rows) AS IndexRows
                    FROM     sys.partitions
                    GROUP BY object_id ,
                                index_id
                    ),
                cteIndex
                AS ( SELECT   OBJECT_NAME(c.object_id) AS ObjectName ,
                                c.object_id ,
                                c.index_id ,
                                i.name COLLATE SQL_Latin1_General_CP1_CI_AS AS name ,
                                c.index_column_id ,
                                c.column_id ,
                                c.is_included_column ,
                                CASE WHEN @IncludeDataTypes = 0
                                        AND c.is_descending_key = 1
                                    THEN sc.name + ' DESC'
                                    WHEN @IncludeDataTypes = 0
                                        AND c.is_descending_key = 0 THEN sc.name
                                    WHEN @IncludeDataTypes = 1
                                        AND c.is_descending_key = 1
                                        AND c.is_included_column = 0
                                    THEN sc.name + ' DESC (' + t.name + ') '
                                    WHEN @IncludeDataTypes = 1
                                        AND c.is_descending_key = 0
                                        AND c.is_included_column = 0
                                    THEN sc.name + ' (' + t.name + ')'
                                    else sc.name
                                END AS ColumnName ,
                                '' AS filter_definition ,
                                ISNULL(dd.user_scans, 0) AS user_scans ,
                                ISNULL(dd.user_seeks, 0) AS user_seeks ,
                                ISNULL(dd.user_updates, 0) AS user_updates ,
                                ISNULL(dd.user_lookups, 0) AS user_lookups ,
                                CONVERT(DATETIME, ISNULL(dd.last_user_lookup,
                                                            '1901-01-01')) AS LastLookup ,
                                CONVERT(DATETIME, ISNULL(dd.last_user_scan,
                                                            '1901-01-01')) AS LastScan ,
                                CONVERT(DATETIME, ISNULL(dd.last_user_seek,
                                                            '1901-01-01')) AS LastSeek ,
                                i.fill_factor ,
                                c.is_descending_key ,
                                'NONE' as data_compression_desc ,
                                i.type_desc ,
                                i.is_unique ,
                                i.is_unique_constraint ,
                                i.is_primary_key ,
                                ci.SizeKB ,
                                cr.IndexRows ,
                                QUOTENAME(sch.name) + '.' + QUOTENAME(tbl.name) AS FullObjectName ,
                                ISNULL(dd.avg_fragmentation_in_percent, 0) as avg_fragmentation_in_percent
                    FROM     sys.indexes i
                                JOIN sys.index_columns c ON i.object_id = c.object_id
                                                            AND i.index_id = c.index_id
                                JOIN sys.columns sc ON c.object_id = sc.object_id
                                                    AND c.column_id = sc.column_id
                                INNER JOIN sys.tables tbl ON c.object_id = tbl.object_id
                                INNER JOIN sys.schemas sch ON sch.schema_id = tbl.schema_id
                                LEFT JOIN sys.types t ON sc.user_type_id = t.user_type_id
                                LEFT JOIN @IndexUsageStats dd ON i.object_id = dd.object_id
                                                                AND i.index_id = dd.index_id --and dd.database_id = db_id()
                                JOIN sys.partitions p ON i.object_id = p.object_id
                                                        AND i.index_id = p.index_id
                                JOIN cteIndexSizes ci ON i.object_id = ci.object_id
                                                        AND i.index_id = ci.index_id
                                JOIN cteRows cr ON i.object_id = cr.object_id
                                                AND i.index_id = cr.index_id
                    WHERE    i.object_id = CASE WHEN @TableName IS NULL
                                                THEN i.object_id
                                                else OBJECT_ID(@TableName)
                                            END
                    ),
                cteResults
                AS ( SELECT   ci.FullObjectName ,
                                ci.object_id ,
                                MAX(index_id) AS Index_Id ,
                                ci.type_desc
                                + CASE WHEN ci.is_primary_key = 1
                                    THEN ' (PRIMARY KEY)'
                                    WHEN ci.is_unique_constraint = 1
                                    THEN ' (UNIQUE CONSTRAINT)'
                                    WHEN ci.is_unique = 1 THEN ' (UNIQUE)'
                                    else ''
                                END AS IndexType ,
                                name AS IndexName ,
                                STUFF((SELECT   N', ' + ColumnName
                                    FROM     cteIndex ci2
                                    WHERE    ci2.name = ci.name
                                                AND ci2.is_included_column = 0
                                    GROUP BY ci2.index_column_id ,
                                                ci2.ColumnName
                                    ORDER BY ci2.index_column_id
                                FOR   XML PATH(N'') ,
                                        TYPE).value(N'.[1]', N'nvarchar(1000)'), 1,
                                    2, N'') AS KeyColumns ,
                                ISNULL(STUFF((SELECT    N',  ' + ColumnName
                                            FROM      cteIndex ci3
                                            WHERE     ci3.name = ci.name
                                                        AND ci3.is_included_column = 1
                                            GROUP BY  ci3.index_column_id ,
                                                        ci3.ColumnName
                                            ORDER BY  ci3.index_column_id
                                    FOR   XML PATH(N'') ,
                                                TYPE).value(N'.[1]',
                                                            N'nvarchar(1000)'), 1, 2,
                                            N''), '') AS IncludeColumns ,
                                ISNULL(filter_definition, '') AS FilterDefinition ,
                                ci.fill_factor ,
                                CASE WHEN ci.data_compression_desc = 'NONE' THEN ''
                                    else ci.data_compression_desc
                                END AS DataCompression ,
                                MAX(ci.user_seeks) + MAX(ci.user_scans)
                                + MAX(ci.user_lookups) AS IndexReads ,
                                MAX(ci.user_lookups) AS IndexLookups ,
                                ci.user_updates AS IndexUpdates ,
                                ci.SizeKB AS SizeKB ,
                                ci.IndexRows AS IndexRows ,
                                CASE WHEN LastScan > LastSeek
                                        AND LastScan > LastLookup THEN LastScan
                                    WHEN LastSeek > LastScan
                                        AND LastSeek > LastLookup THEN LastSeek
                                    WHEN LastLookup > LastScan
                                        AND LastLookup > LastSeek THEN LastLookup
                                    else ''
                                END AS MostRecentlyUsed ,
                                AVG(ci.avg_fragmentation_in_percent) as avg_fragmentation_in_percent
                    FROM     cteIndex ci
                    GROUP BY ci.ObjectName ,
                                ci.name ,
                                ci.filter_definition ,
                                ci.object_id ,
                                ci.LastLookup ,
                                ci.LastSeek ,
                                ci.LastScan ,
                                ci.user_updates ,
                                ci.fill_factor ,
                                ci.data_compression_desc ,
                                ci.type_desc ,
                                ci.is_primary_key ,
                                ci.is_unique ,
                                ci.is_unique_constraint ,
                                ci.SizeKB ,
                                ci.IndexRows ,
                                ci.FullObjectName
                    ), AllResults AS
                        (		 SELECT   c.FullObjectName ,
                                ISNULL(IndexType, 'STATISTICS') AS IndexType ,
                                ISNULL(IndexName, '') AS IndexName ,
                                ISNULL(KeyColumns, '') AS KeyColumns ,
                                ISNULL(IncludeColumns, '') AS IncludeColumns ,
                                FilterDefinition ,
                                fill_factor AS [FillFactor] ,
                                DataCompression ,
                                IndexReads ,
                                IndexUpdates ,
                                SizeKB ,
                                IndexRows ,
                                IndexLookups ,
                                MostRecentlyUsed ,
                                NULL AS StatsSampleRows ,
                                NULL AS StatsRowMods ,
                                NULL AS HistogramSteps ,
                                NULL AS StatsLastUpdated ,
                                avg_fragmentation_in_percent as IndexFragInPercent,
                                1 AS Ordering ,
                                c.object_id ,
                                c.Index_Id
                    FROM     cteResults c
                                INNER JOIN cteStatsInfo si ON si.object_id = c.object_id
                                                            AND si.stats_id = c.Index_Id
                        UNION
                    SELECT   QUOTENAME(sch.name) + '.' + QUOTENAME(tbl.name) AS FullObjectName ,
                                'STATISTICS' ,
                                stats_name ,
                                StatsColumns ,
                                '' ,
                                '' AS FilterDefinition ,
                                '' AS Fill_Factor ,
                                '' AS DataCompression ,
                                '' AS IndexReads ,
                                '' AS IndexUpdates ,
                                '' AS SizeKB ,
                                StatsRows AS IndexRows ,
                                '' AS IndexLookups ,
                                '' AS MostRecentlyUsed ,
                                SampleRows AS StatsSampleRows ,
                                RowMods AS StatsRowMods ,
                                csi.HistogramSteps ,
                                csi.StatsLastUpdated ,
                                '' as IndexFragInPercent,
                                2 ,
                                csi.object_id ,
                                csi.stats_id
                    FROM     cteStatsInfo csi
                    INNER JOIN sys.tables tbl ON csi.object_id = tbl.object_id
                                INNER JOIN sys.schemas sch ON sch.schema_id = tbl.schema_id
                                LEFT JOIN (SELECT si.object_id, si.stats_id
                                            FROM    cteResults c
                                            INNER JOIN cteStatsInfo si ON si.object_id = c.object_id
                                                                    AND si.stats_id = c.Index_Id ) AS x on csi.object_id = x.object_id and csi.stats_id = x.stats_id
                        WHERE x.object_id is null
                    )
            INSERT INTO @AllResults
            SELECT  row_number() OVER (ORDER BY FullObjectName) AS RowNum ,
                    FullObjectName ,
                    ISNULL(IndexType, 'STATISTICS') AS IndexType ,
                    IndexName ,
                    KeyColumns ,
                    ISNULL(IncludeColumns, '') AS IncludeColumns ,
                    FilterDefinition ,
                    [FillFactor] AS [FillFactor] ,
                    DataCompression ,
                    IndexReads ,
                    IndexUpdates ,
                    SizeKB ,
                    IndexRows ,
                    IndexLookups ,
                    MostRecentlyUsed ,
                    StatsSampleRows ,
                    StatsRowMods ,
                    HistogramSteps ,
                    StatsLastUpdated ,
                    IndexFragInPercent ,
                    object_id ,
                    index_id
            FROM    AllResults
                    $IncludeStatsPredicate
        OPTION  ( RECOMPILE );

        /* Only update the stats data on 2005 for a single table, otherwise the run time for this is a potential problem for large table/index volumes */
        if @TableName IS NOT NULL
        BEGIN

            DECLARE @StatsInfo2005 TABLE (Name nvarchar(128), Updated DATETIME, Rows BIGINT, RowsSampled BIGINT, Steps INT, Density INT, AverageKeyLength INT, StringIndex NVARCHAR(20))

            DECLARE @SqlCall NVARCHAR(2000), @RowNum INT;
            SELECT @RowNum = min(RowNum) FROM @AllResults;
            WHILE @RowNum IS NOT NULL
            BEGIN
                SELECT @SqlCall = 'dbcc show_statistics('+FullObjectName+', '+IndexName+') with stat_header' FROM @AllResults WHERE RowNum = @RowNum;
                INSERT INTO @StatsInfo2005 exec (@SqlCall);
                UPDATE @AllResults
                    SET StatsSampleRows = RowsSampled,
                    HistogramSteps = Steps,
                    StatsLastUpdated = Updated
                    FROM @StatsInfo2005
                    WHERE RowNum = @RowNum;
                DELETE FROM @StatsInfo2005
                SELECT @RowNum = min(RowNum) FROM @AllResults WHERE RowNum > @RowNum;
            END;

        END;

        UPDATE a
        SET a.StatsRowMods = i.rowmodctr
        FROM @AllResults a
            JOIN sys.sysindexes i ON a.object_id = i.id AND a.index_id = i.indid;

        SELECT	FullObjectName ,
                IndexType ,
                IndexName ,
                KeyColumns ,
                IncludeColumns ,
                FilterDefinition ,
                [FillFactor] ,
                DataCompression ,
                IndexReads ,
                IndexUpdates ,
                SizeKB ,
                IndexRows ,
                IndexLookups ,
                MostRecentlyUsed ,
                StatsSampleRows ,
                StatsRowMods ,
                HistogramSteps ,
                StatsLastUpdated ,
                IndexFragInPercent
        FROM @AllResults;"
        
        #endregion sizesQuery2005
    }
    process {
        Write-Message -Level Debug -Message $SizesQuery
        Write-Message -Level Debug -Message $SizesQuery2005
        
        foreach ($instance in $SqlInstance) {
            
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            
            $InputObject += Get-DbaDatabase -SqlInstance $server -Database $Database -ExcludeDatabase $ExcludeDatabase
        }
        
        foreach ($db in $InputObject) {
            $server = $db.Parent
            
            #Need to check the version of SQL
            if ($server.versionMajor -ge 10) {
                $indexesQuery = $SizesQuery
            }
            else {
                $indexesQuery = $SizesQuery2005
            }
            
            if (!$db.IsAccessible) {
                Stop-Function -Message "$db is not accessible. Skipping." -Continue
            }
            
            Write-Message -Level Debug -Message "$indexesQuery"
            try {
                $IndexDetails = $db.Query($indexesQuery)
                
                if (!$Raw) {
                    foreach ($detail in $IndexDetails) {
                        $recentlyused = [datetime]$detail.MostRecentlyUsed
                        
                        if ($recentlyused.year -eq 1900) {
                            $recentlyused = $null
                        }
                        
                        [pscustomobject]@{
                            ComputerName  = $server.ComputerName
                            InstanceName  = $server.ServiceName
                            SqlInstance   = $server.DomainInstanceName
                            Database     = $db.Name
                            Object        = $detail.FullObjectName
                            Index         = $detail.IndexName
                            IndexType     = $detail.IndexType
                            KeyColumns    = $detail.KeyColumns
                            IncludeColumns = $detail.IncludeColumns
                            FilterDefinition = $detail.FilterDefinition
                            DataCompression = $detail.DataCompression
                            IndexReads    = "{0:N0}" -f $detail.IndexReads
                            IndexUpdates  = "{0:N0}" -f $detail.IndexUpdates
                            SizeKB        = "{0:N0}" -f $detail.SizeKB
                            IndexRows     = "{0:N0}" -f $detail.IndexRows
                            IndexLookups  = "{0:N0}" -f $detail.IndexLookups
                            MostRecentlyUsed = $recentlyused
                            StatsSampleRows = "{0:N0}" -f $detail.StatsSampleRows
                            StatsRowMods  = "{0:N0}" -f $detail.StatsRowMods
                            HistogramSteps = $detail.HistogramSteps
                            StatsLastUpdated = $detail.StatsLastUpdated
                            IndexFragInPercent = "{0:F2}" -f $detail.IndexFragInPercent
                        } | Select-DefaultView -Property $OutputProperties
                    }
                }
                
                else {
                    foreach ($detail in $IndexDetails) {
                        $recentlyused = [datetime]$detail.MostRecentlyUsed
                        
                        if ($recentlyused.year -eq 1900) {
                            $recentlyused = $null
                        }
                        
                        [pscustomobject]@{
                            ComputerName   = $server.ComputerName
                            InstanceName   = $server.ServiceName
                            SqlInstance    = $server.DomainInstanceName
                            Database       = $db.Name
                            Object         = $detail.FullObjectName
                            Index          = $detail.IndexName
                            IndexType      = $detail.IndexType
                            KeyColumns     = $detail.KeyColumns
                            IncludeColumns = $detail.IncludeColumns
                            FilterDefinition = $detail.FilterDefinition
                            DataCompression = $detail.DataCompression
                            IndexReads     = $detail.IndexReads
                            IndexUpdates   = $detail.IndexUpdates
                            SizeKB         = $detail.SizeKB
                            IndexRows      = $detail.IndexRows
                            IndexLookups   = $detail.IndexLookups
                            MostRecentlyUsed = $recentlyused
                            StatsSampleRows = $detail.StatsSampleRows
                            StatsRowMods   = $detail.StatsRowMods
                            HistogramSteps = $detail.HistogramSteps
                            StatsLastUpdated = $detail.StatsLastUpdated
                            IndexFragInPercent = $detail.IndexFragInPercent
                        } | Select-DefaultView -Property $OutputProperties
                    }
                }
            }
            catch {
                Stop-Function -Continue -ErrorRecord $_ -Message "Cannot process $db on $server"
            }
        }
    }
}
tools\dbatools\functions\Get-DbaLastBackup.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaLastBackup {
    <#
        .SYNOPSIS
            Get date/time for last known backups of databases.

        .DESCRIPTION
            Retrieves and compares the date/time for the last known backups, as well as the creation date/time for the database.

            Default output includes columns Server, Database, RecoveryModel, LastFullBackup, LastDiffBackup, LastLogBackup, SinceFull, SinceDiff, SinceLog, Status, DatabaseCreated, DaysSinceDbCreated.

        .PARAMETER SqlInstance
            The SQL Server instance to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies one or more database(s) to process. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies one or more database(s) to exclude from processing.

        .PARAMETER EnableException
            If this switch is enabled exceptions will be thrown to the caller, which will need to perform its own exception processing. Otherwise, the function will try to catch the exception, interpret it and provide a friendly error message.

        .NOTES
            Tags: DisasterRecovery, Backup
            Author: Klaas Vandenberghe ( @PowerDBAKlaas )

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaLastBackup

        .EXAMPLE
            Get-DbaLastBackup -SqlInstance ServerA\sql987

            Returns a custom object displaying Server, Database, RecoveryModel, LastFullBackup, LastDiffBackup, LastLogBackup, SinceFull, SinceDiff, SinceLog, Status, DatabaseCreated, DaysSinceDbCreated

        .EXAMPLE
            Get-DbaLastBackup -SqlInstance ServerA\sql987

            Returns a custom object with Server name, Database name, and the date the last time backups were performed.

        .EXAMPLE
            Get-DbaLastBackup -SqlInstance ServerA\sql987 | Select *

            Returns a custom object with Server name, Database name, and the date the last time backups were performed, and also recoverymodel and calculations on how long ago backups were taken and what the status is.

        .EXAMPLE
            Get-DbaLastBackup -SqlInstance ServerA\sql987 | Select * | Out-Gridview

            Returns a gridview displaying Server, Database, RecoveryModel, LastFullBackup, LastDiffBackup, LastLogBackup, SinceFull, SinceDiff, SinceLog, Status, DatabaseCreated, DaysSinceDbCreated.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        function Get-DbaDateOrNull ($TimeSpan) {
            if ($TimeSpan -eq 0) {
                return $null
            }
            return $TimeSpan
        }
        $StartOfTime = [DbaTimeSpan](New-TimeSpan -Start ([datetime]0))
    }
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            
            $dbs = $server.Databases | Where-Object { $_.name -ne 'tempdb' }

            if ($Database) {
                $dbs = $dbs | Where-Object Name -In $Database
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }
            # Get-DbaBackupHistory -Last would make the job in one query but SMO's (and this) report the last backup of this type irregardless of the chain
            $FullHistory = Get-DbaBackupHistory -SqlInstance $server -Database $dbs.Name -LastFull -IncludeCopyOnly -Raw
            $DiffHistory = Get-DbaBackupHistory -SqlInstance $server -Database $dbs.Name -LastDiff -IncludeCopyOnly -Raw
            $IncrHistory = Get-DbaBackupHistory -SqlInstance $server -Database $dbs.Name -LastLog -IncludeCopyOnly -Raw
            foreach ($db in $dbs) {
                Write-Message -Level Verbose -Message "Processing $db on $instance"

                if ($db.IsAccessible -eq $false) {
                    Write-Message -Level Warning -Message "The database $db on server $instance is not accessible. Skipping database."
                    Continue
                }
                $LastFullBackup = ($FullHistory | Where-Object Database -eq $db.Name | Sort-Object -Property End -Descending | Select-Object -First 1).End
                if ($null -ne $LastFullBackup) {
                    $SinceFull_ = [DbaTimeSpan](New-TimeSpan -Start $LastFullBackup)
                }
                else {
                    $SinceFull_ = $StartOfTime
                }

                $LastDiffBackup = ($DiffHistory | Where-Object Database -eq $db.Name | Sort-Object -Property End -Descending | Select-Object -First 1).End
                if ($null -ne $LastDiffBackup) {
                    $SinceDiff_ = [DbaTimeSpan](New-TimeSpan -Start $LastDiffBackup)
                }
                else {
                    $SinceDiff_ = $StartOfTime
                }

                $LastIncrBackup = ($IncrHistory | Where-Object Database -eq $db.Name | Sort-Object -Property End -Descending | Select-Object -First 1).End
                if ($null -ne $LastIncrBackup) {
                    $SinceLog_ = [DbaTimeSpan](New-TimeSpan -Start $LastIncrBackup)
                }
                else {
                    $SinceLog_ = $StartOfTime
                }

                $daysSinceDbCreated = (New-TimeSpan -Start $db.createDate).Days

                if ($daysSinceDbCreated -lt 1 -and $SinceFull_ -eq 0) {
                    $Status = 'New database, not backed up yet'
                }
                elseif ($SinceFull_.Days -gt 0 -and $SinceDiff_.Days -gt 0) {
                    $Status = 'No Full or Diff Back Up in the last day'
                }
                elseif ($db.RecoveryModel -eq "Full" -and $SinceLog_.Hours -gt 0) {
                    $Status = 'No Log Back Up in the last hour'
                }
                else {
                    $Status = 'OK'
                }

                $result = [PSCustomObject]@{
                    ComputerName       = $server.ComputerName
                    InstanceName       = $server.ServiceName
                    SqlInstance        = $server.DomainInstanceName
                    Database           = $db.Name
                    RecoveryModel      = $db.RecoveryModel
                    LastFullBackup     = [DbaDateTime]$LastFullBackup
                    LastDiffBackup     = [DbaDateTime]$LastDiffBackup
                    LastLogBackup      = [DbaDateTime]$LastIncrBackup
                    SinceFull          = Get-DbaDateOrNull -TimeSpan $SinceFull_
                    SinceDiff          = Get-DbaDateOrNull -TimeSpan $SinceDiff_
                    SinceLog           = Get-DbaDateOrNull -TimeSpan $SinceLog_
                    DatabaseCreated    = $db.createDate
                    DaysSinceDbCreated = $daysSinceDbCreated
                    Status             = $status
                }
                Select-DefaultView -InputObject $result -Property ComputerName, InstanceName, SqlInstance, Database, LastFullBackup, LastDiffBackup, LastLogBackup
            }
        }
    }
}
tools\dbatools\functions\Get-DbaLastGoodCheckDb.ps1
function Get-DbaLastGoodCheckDb {
    <#
        .SYNOPSIS
            Get date/time for last known good DBCC CHECKDB

        .DESCRIPTION
            Retrieves and compares the date/time for the last known good DBCC CHECKDB, as well as the creation date/time for the database.

            This function supports SQL Server 2005 and higher.

            Please note that this script uses the DBCC DBINFO() WITH TABLERESULTS. DBCC DBINFO has several known weak points, such as:
            - DBCC DBINFO is an undocumented feature/command.
            - The LastKnowGood timestamp is updated when a DBCC CHECKFILEGROUP is performed.
            - The LastKnowGood timestamp is updated when a DBCC CHECKDB WITH PHYSICAL_ONLY is performed.
            - The LastKnowGood timestamp does not get updated when a database in READ_ONLY.

            An empty ($null) LastGoodCheckDb result indicates that a good DBCC CHECKDB has never been performed.

            SQL Server 2008R2 has a "bug" that causes each databases to possess two dbi_dbccLastKnownGood fields, instead of the normal one.

            This script will only display this function to only display the newest timestamp. If -Verbose is specified, the function will announce every time more than one dbi_dbccLastKnownGood fields is encountered.

        .PARAMETER SqlInstance
            The SQL Server instance to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies one or more database(s) to process. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies one or more database(s) to exclude from processing.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: CHECKDB, Database
            Author: Jakob Bindslet ([email protected])

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            DBCC CHECKDB:
                https://msdn.microsoft.com/en-us/library/ms176064.aspx
                http://www.sqlcopilot.com/dbcc-checkdb.html
            Data Purity:
                http://www.sqlskills.com/blogs/paul/checkdb-from-every-angle-how-to-tell-if-data-purity-checks-will-be-run/
                https://www.mssqltips.com/sqlservertip/1988/ensure-sql-server-data-purity-checks-are-performed/

        .EXAMPLE
            Get-DbaLastGoodCheckDb -SqlInstance ServerA\sql987

            Returns a custom object displaying Server, Database, DatabaseCreated, LastGoodCheckDb, DaysSinceDbCreated, DaysSinceLastGoodCheckDb, Status and DataPurityEnabled

        .EXAMPLE
            Get-DbaLastGoodCheckDb -SqlInstance ServerA\sql987 -SqlCredential (Get-Credential sqladmin) | Format-Table -AutoSize

            Returns a formatted table displaying Server, Database, DatabaseCreated, LastGoodCheckDb, DaysSinceDbCreated, DaysSinceLastGoodCheckDb, Status and DataPurityEnabled. Authenticates using SQL Server authentication.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($server.versionMajor -lt 9) {
                Stop-Function -Message "Get-DbaLastGoodCheckDb is only supported on SQL Server 2005 and above. Skipping Instance." -Continue -Target $instance
            }

            $dbs = $server.Databases

            if ($Database) {
                $dbs = $dbs | Where-Object Name -In $Database
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $dbs) {
                Write-Message -Level Verbose -Message "Processing $db on $instances."

                if ($db.IsAccessible -eq $false) {
                    Stop-Function -Message "The database $db is not accessible. Skipping database." -Continue -Target $db
                }

                $sql = "DBCC DBINFO ([$($db.name)]) WITH TABLERESULTS"
                Write-Message -Level Debug -Message "T-SQL: $sql"

                $resultTable = $db.ExecuteWithResults($sql).Tables[0]
                [datetime[]]$lastKnownGoodArray = $resultTable | Where-Object Field -eq 'dbi_dbccLastKnownGood' | Select-Object -ExpandProperty Value

                ## look for databases with two or more occurrences of the field dbi_dbccLastKnownGood
                if ($lastKnownGoodArray.count -ge 2) {
                    Write-Message -Level Verbose -Message "The database $db has $($lastKnownGoodArray.count) dbi_dbccLastKnownGood fields. This script will only use the newest!"
                }
                [datetime]$lastKnownGood = $lastKnownGoodArray | Sort-Object -Descending | Select-Object -First 1

                [int]$createVersion = ($resultTable | Where-Object Field -eq 'dbi_createVersion').Value
                [int]$dbccFlags = ($resultTable | Where-Object Field -eq 'dbi_dbccFlags').Value

                if (($createVersion -lt 611) -and ($dbccFlags -eq 0)) {
                    $dataPurityEnabled = $false
                }
                else {
                    $dataPurityEnabled = $true
                }

                $daysSinceCheckDb = (New-TimeSpan -Start $lastKnownGood -End (Get-Date)).Days
                $daysSinceDbCreated = (New-TimeSpan -Start $db.createDate -End (Get-Date)).TotalDays

                if ($daysSinceCheckDb -lt 7) {
                    $Status = 'Ok'
                }
                elseif ($daysSinceDbCreated -lt 7) {
                    $Status = 'New database, not checked yet'
                }
                else {
                    $Status = 'CheckDB should be performed'
                }

                if ($lastKnownGood -eq '1/1/1900 12:00:00 AM') {
                    Remove-Variable -Name lastKnownGood, daysSinceCheckDb
                }

                [PSCustomObject]@{
                    ComputerName             = $server.ComputerName
                    InstanceName             = $server.ServiceName
                    SqlInstance              = $server.DomainInstanceName
                    Database                 = $db.name
                    DatabaseCreated          = $db.createDate
                    LastGoodCheckDb          = $lastKnownGood
                    DaysSinceDbCreated       = $daysSinceDbCreated
                    DaysSinceLastGoodCheckDb = $daysSinceCheckDb
                    Status                   = $status
                    DataPurityEnabled        = $dataPurityEnabled
                    CreateVersion            = $createVersion
                    DbccFlags                = $dbccFlags
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaLinkedServer.ps1
function Get-DbaLinkedServer {
    <#
        .SYNOPSIS
            Gets all linked servers and summary of information from the sql servers listed

        .DESCRIPTION
            Retrieves information about each linked server on the instance

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function
            to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER LinkedServer
            The linked server(s) to process - this list is auto-populated from the server. If unspecified, all linked servers will be processed.

        .PARAMETER ExcludeLinkedServer
            The linked server(s) to exclude - this list is auto-populated from the server

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: LinkedServer, Linked
            Author: Stephen Bennett ( https://sqlnotesfromtheunderground.wordpress.com/ )

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaLinkedServer

        .EXAMPLE
            Get-DbaLinkedServer -SqlInstance DEV01

            Returns all Linked Servers for the SQL Server instance DEV01
    #>
    [CmdletBinding(DefaultParameterSetName = 'Default')]
    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$LinkedServer,
        [object[]]$ExcludeLinkedServer,
        [Alias('Silent')]
        [switch]$EnableException
    )
    foreach ($Instance in $SqlInstance) {
        try {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
        }

        $lservers = $server.LinkedServers

        if ($LinkedServer) {
            $lservers = $lservers | Where-Object { $_.Name -in $LinkedServer }
        }
        if ($ExcludeLinkedServer) {
            $lservers = $lservers | Where-Object { $_.Name -notin $ExcludeLinkedServer }
        }

        foreach ($ls in $lservers) {
            Add-Member -Force -InputObject $ls -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
            Add-Member -Force -InputObject $ls -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
            Add-Member -Force -InputObject $ls -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
            Add-Member -Force -InputObject $ls -MemberType NoteProperty -Name Impersonate -value $ls.LinkedServerLogins.Impersonate
            Add-Member -Force -InputObject $ls -MemberType NoteProperty -Name RemoteUser -value $ls.LinkedServerLogins.RemoteUser

            Select-DefaultView -InputObject $ls -Property ComputerName, InstanceName, SqlInstance, Name, 'DataSource as RemoteServer', ProductName, Impersonate, RemoteUser, 'DistPublisher as Publisher', Distributor, DateLastModified
        }
    }
}
tools\dbatools\functions\Get-DbaLocaleSetting.ps1
function Get-DbaLocaleSetting {
    <#
      .SYNOPSIS
      Gets the Locale settings on a computer.

      .DESCRIPTION
      Gets the Locale settings on one or more computers.

      Requires Local Admin rights on destination computer(s).

      .PARAMETER ComputerName
      The SQL Server (or server in general) that you're connecting to. This command handles named instances.

      .PARAMETER Credential
      Credential object used to connect to the computer as a different user.

      .PARAMETER EnableException
      By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
      This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
      Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

      .NOTES
      Author: Klaas Vandenberghe ( @PowerDBAKlaas )
      Tags: OS
      dbatools PowerShell module (https://dbatools.io)
      Copyright (C) 2016 Chrissy LeMaire
      License: MIT https://opensource.org/licenses/MIT

      .LINK
      https://dbatools.io/Get-DbaLocaleSetting

      .EXAMPLE
      Get-DbaLocaleSetting -ComputerName sqlserver2014a

      Gets the Locale settings on computer sqlserver2014a.

      .EXAMPLE
      'sql1','sql2','sql3' | Get-DbaLocaleSetting

      Gets the Locale settings on computers sql1, sql2 and sql3.

      .EXAMPLE
      Get-DbaLocaleSetting -ComputerName sql1,sql2 | Out-Gridview

      Gets the Locale settings on computers sql1 and sql2, and shows them in a grid view.

  #>
    [CmdletBinding()]
    Param (
        [parameter(ValueFromPipeline)]
        [Alias("cn", "host", "Server")]
        [string[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential] $Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    BEGIN {
        $ComputerName = $ComputerName | ForEach-Object {$_.split("\")[0]} | Select-Object -Unique
        $sessionoption = New-CimSessionOption -Protocol DCom
        $keyname = "Control Panel\International"
        $NS = 'root\cimv2'
        $Reg = 'StdRegProv'
        [UInt32]$CIMHiveCU = 2147483649
    }
    PROCESS {
        foreach ($computer in $ComputerName) {
            $props = @{ "ComputerName" = $computer }
            $Server = Resolve-DbaNetworkName -ComputerName $Computer -Credential $credential
            if ( $Server.FullComputerName ) {
                $Computer = $server.FullComputerName
                Write-Message -Level Verbose -Message "Creating CIMSession on $computer over WSMan"
                $CIMsession = New-CimSession -ComputerName $Computer -ErrorAction SilentlyContinue -Credential $Credential
                if ( -not $CIMSession ) {
                    Write-Message -Level Verbose -Message "Creating CIMSession on $computer over WSMan failed. Creating CIMSession on $computer over DCom"
                    $CIMsession = New-CimSession -ComputerName $Computer -SessionOption $sessionoption -ErrorAction SilentlyContinue -Credential $Credential
                }
                if ( $CIMSession ) {
                    Write-Message -Level Verbose -Message "Getting properties from Registry Key"
                    $PropNames = Invoke-CimMethod -CimSession $CIMsession -Namespace $NS -ClassName $Reg -MethodName enumvalues -Arguments @{hDefKey = $CIMHiveCU; sSubKeyName = $keyname} |
                        Select-Object -ExpandProperty snames

                    foreach ($Name in $PropNames) {
                        $sValue = Invoke-CimMethod -CimSession $CIMsession -Namespace $NS -ClassName $Reg -MethodName GetSTRINGvalue -Arguments @{hDefKey = $CIMHiveCU; sSubKeyName = $keyname; sValueName = $Name} |
                            Select-Object -ExpandProperty svalue
                        $props.add($Name, $sValue)
                    }
                    [PSCustomObject]$props
                } #if CIMSession
                else {
                    Write-Message -Level Warning -Message "Can't create CIMSession on $computer"
                }
            } #if computername
            else {
                Write-Message -Level Warning -Message "Can't connect to $computer"
            }
        } #foreach computer
    } #PROCESS
} #function
tools\dbatools\functions\Get-DbaLogin.ps1
function Get-DbaLogin {
    <#
        .SYNOPSIS
            Function to get an SMO login object of the logins for a given SQL Instance. Takes a server object from the pipe

        .DESCRIPTION
            The Get-DbaLogin function returns an SMO Login object for the logins passed, if there are no users passed it will return all logins.

        .PARAMETER SqlInstance
            The SQL Server instance, or instances.You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SqlCredential
            Allows you to login to servers using SQL Logins as opposed to Windows Auth/Integrated/Trusted.

        .PARAMETER Login
            The login(s) to process - this list is auto-populated from the server. If unspecified, all logins will be processed.

        .PARAMETER ExcludeLogin
            The login(s) to exclude - this list is auto-populated from the server

        .PARAMETER IncludeFilter
            A list of logins to include - accepts wildcard patterns

        .PARAMETER ExcludeFilter
            A list of logins to exclude - accepts wildcard patterns

        .PARAMETER NoSystem
            A Switch to remove System Logins from the output.

        .PARAMETER SQLLogins
            A Switch to return Logins of type SQLLogin only.

        .PARAMETER WindowsLogins
            A Switch to return Logins of type Windows only.

        .PARAMETER Locked
            A Switch to return locked Logins.

        .PARAMETER Disabled
            A Switch to return disabled Logins.

        .PARAMETER HasAccess
            A Switch to return Logins that have access to the instance of SQL Server.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Login, Security
            Author: Mitchell Hamann (@SirCaptainMitch)
            Author: Klaas Vandenberghe (@powerdbaklaas)
            Author: Robert Corrigan (@rjcorrig)
            Author: Rob Sewell (@SQLDBaWithBeard)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaLogin

        .EXAMPLE
            Get-DbaLogin -SqlInstance sql2016

            Gets all the logins from server sql2016 using NT authentication and returns the SMO login objects

        .EXAMPLE
            Get-DbaLogin -SqlInstance sql2016 -SqlCredential $sqlcred

            Gets all the logins for a given SQL Server using a passed credential object and returns the SMO login objects

        .EXAMPLE
            Get-DbaLogin -SqlInstance sql2016 -SqlCredential $sqlcred -Login dbatoolsuser,TheCaptain

            Get specific logins from server sql2016 returned as SMO login objects.

        .EXAMPLE
            Get-DbaLogin -SqlInstance sql2016 -IncludeFilter '##*','NT *'

            Get all user objects from server sql2016 beginning with '##' or 'NT ', returned as SMO login objects.

        .EXAMPLE
            Get-DbaLogin -SqlInstance sql2016 -ExcludeLogin dbatoolsuser

            Get all user objects from server sql2016 except the login dbatoolsuser, returned as SMO login objects.

        .EXAMPLE
            Get-DbaLogin -SqlInstance sql2016 -WindowsLogins

            Get all user objects from server sql2016 that are Windows Logins

        .EXAMPLE
            Get-DbaLogin -SqlInstance sql2016 -WindowsLogins -IncludeFilter *Rob*

            Get all user objects from server sql2016 that are Windows Logins and have Rob in the name

        .EXAMPLE
            Get-DbaLogin -SqlInstance sql2016 -SQLLogins

            Get all user objects from server sql2016 that are SQLLogins

        .EXAMPLE
            Get-DbaLogin -SqlInstance sql2016 -SQLLogins -IncludeFilter *Rob*

            Get all user objects from server sql2016 that are SQLLogins  and have Rob in the name

        .EXAMPLE
            Get-DbaLogin -SqlInstance sql2016 -NoSystem

            Get all user objects from server sql2016 that are not system objects

        .EXAMPLE
            Get-DbaLogin -SqlInstance sql2016 -ExcludeFilter '##*','NT *'

            Get all user objects from server sql2016 except any beginning with '##' or 'NT ', returned as SMO login objects.

        .EXAMPLE
            'sql2016', 'sql2014' | Get-DbaLogin -SqlCredential $sqlcred

            Using Get-DbaLogin on the pipeline, you can also specify which names you would like with -Login.

        .EXAMPLE
            'sql2016', 'sql2014' | Get-DbaLogin -SqlCredential $sqlcred -Locked

            Using Get-DbaLogin on the pipeline to get all locked logins on servers sql2016 and sql2014.

        .EXAMPLE
            'sql2016', 'sql2014' | Get-DbaLogin -SqlCredential $sqlcred -HasAccess -Disabled

            Using Get-DbaLogin on the pipeline to get all Disabled logins that have access on servers sql2016 or sql2014.
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Login,
        [object[]]$IncludeFilter,
        [object[]]$ExcludeLogin,
        [object[]]$ExcludeFilter,
        [switch]$NoSystem,
        [switch]$SQLLogins,
        [switch]$WindowsLogins,
        [switch]$HasAccess,
        [switch]$Locked,
        [switch]$Disabled,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $serverLogins = $server.Logins

            if ($Login) {
                $serverLogins = $serverLogins | Where-Object Name -in $Login
            }

            if ($NoSystem) {
                $serverLogins = $serverLogins | Where-Object IsSystemObject -eq $false
            }

            if ($SQLLogins) {
                $serverLogins = $serverLogins | Where-Object LoginType -eq 'SqlLogin'
            }

            if ($WindowsLogins) {
                $serverLogins = $serverLogins | Where-Object LoginType -eq 'WindowsUser'
            }

            if ($IncludeFilter) {
                $serverLogins = $serverLogins | Where-Object {
                    foreach ($filter in $IncludeFilter) {
                        if ($_.Name -like $filter) {
                            return $true;
                        }
                    }
                }
            }

            if ($ExcludeLogin) {
                $serverLogins = $serverLogins | Where-Object Name -NotIn $ExcludeLogin
            }

            if ($ExcludeFilter) {
                foreach ($filter in $ExcludeFilter) {
                    $serverLogins = $serverLogins | Where-Object Name -NotLike $filter
                }
            }

            if ($HasAccess) {
                $serverLogins = $serverLogins | Where-Object HasAccess
            }

            if ($Locked) {
                $serverLogins = $serverLogins | Where-Object IsLocked
            }

            if ($Disabled) {
                $serverLogins = $serverLogins | Where-Object IsDisabled
            }

            foreach ($serverLogin in $serverlogins) {
                Write-Message -Level Verbose -Message "Processing $serverLogin on $instance"

                if ($server.VersionMajor -gt 9) {
                    # There's no reliable method to get last login time with SQL Server 2000, so only show on 2005+
                    Write-Message -Level Verbose -Message "Getting last login time"
                    $sql = "SELECT MAX(login_time) AS [login_time] FROM sys.dm_exec_sessions WHERE login_name = '$($serverLogin.name)'"
                    Add-Member -Force -InputObject $serverLogin -MemberType NoteProperty -Name LastLogin -Value $server.ConnectionContext.ExecuteScalar($sql)
                }
                else {
                    Add-Member -Force -InputObject $serverLogin -MemberType NoteProperty -Name LastLogin -Value $null
                }

                Add-Member -Force -InputObject $serverLogin -MemberType NoteProperty -Name ComputerName -Value $server.ComputerName
                Add-Member -Force -InputObject $serverLogin -MemberType NoteProperty -Name InstanceName -Value $server.ServiceName
                Add-Member -Force -InputObject $serverLogin -MemberType NoteProperty -Name SqlInstance -Value $server.DomainInstanceName

                Select-DefaultView -InputObject $serverLogin -Property ComputerName, InstanceName, SqlInstance, Name, LoginType, CreateDate, LastLogin, HasAccess, IsLocked, IsDisabled
            }
        }
    }
}
tools\dbatools\functions\Get-DbaLogShippingError.ps1
function Get-DbaLogShippingError {
    <#
        .SYNOPSIS
            Get-DbaLogShippingError returns all the log shipping errors that occurred

        .DESCRIPTION
            When your log shipping fails it's sometimes hard to see why is fails.
            Using this function you'll be able to find out what went wrong in a short amount of time.

        .PARAMETER SqlInstance
            SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Allows you to filter the results to only return the databases you're interested in. This can be one or more values separated by commas.
            This is not a wildcard and should be the exact database name. See examples for more info.

        .PARAMETER ExcludeDatabase
            Allows you to filter the results to only return the databases you're not interested in. This can be one or more values separated by commas.
            This is not a wildcard and should be the exact database name.

        .PARAMETER Action
            Filter to get the log shipping action that has occurred like Backup, Copy, Restore.
            By default all the actions are returned.

        .PARAMETER DateTimeFrom
            Filter the results based on the date starting from datetime X

        .PARAMETER DateTimeTo
            Filter the results based on the date ending with datetime X

        .PARAMETER Primary
            Allows to filter the results to only return values that apply to the primary instance.

        .PARAMETER Secondary
            Allows to filter the results to only return values that apply to the secondary instance.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: LogShipping
            Author: Sander Stad (@sqlstad, sqlstad.nl)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaLogShippingError

        .EXAMPLE
            Get-DbaLogShippingError -SqlInstance sql1

            Get all the log shipping errors that occurred

        .EXAMPLE
            Get-DbaLogShippingError -SqlInstance sql1 -Action Backup

            Get the errors that have something to do with the backup of the databases

        .EXAMPLE
            Get-DbaLogShippingError -SqlInstance sql1 -Secondary

            Get the errors that occurred on the secondary instance.
            This will return the copy of the restore actions because those only occur on the secondary instance

        .EXAMPLE
            Get-DbaLogShippingError -SqlInstance sql1 -DateTimeFrom "01/05/2018"

            Get the errors that have occurred from "01/05/2018". This can also be of format "yyyy-MM-dd"

        .EXAMPLE
            Get-DbaLogShippingError -SqlInstance sql1 -Secondary -DateTimeFrom "01/05/2018" -DateTimeTo "2018-01-07"

            Get the errors that have occurred between "01/05/2018" and "01/07/2018".
            See that is doesn't matter how the date is represented.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$Database,
        [string[]]$ExcludeDatabase,
        [ValidateSet("Backup", "Copy", "Restore")]
        [string[]]$Action,
        [datetime]$DateTimeFrom,
        [datetime]$DateTimeTo,
        [switch]$Primary,
        [switch]$Secondary,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {

        # Create array list to hold the results
        $collection = New-Object System.Collections.ArrayList

    }

    process {
        foreach ($instance in $sqlinstance) {
            # Try connecting to the instance
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($server.EngineEdition -match "Express") {
                Write-Message -Level Warning -Message "$instance is Express Edition which does not support Log Shipping"
                continue
            }

            $query = "
CREATE TABLE #DatabaseID
(
    DatabaseName VARCHAR(128),
    DatabaseID UNIQUEIDENTIFIER,
    Instance VARCHAR(20)
);

INSERT INTO #DatabaseID
(
    DatabaseName,
    DatabaseID,
    Instance
)
SELECT secondary_database,
        secondary_id,
        'Secondary'
FROM msdb.dbo.log_shipping_secondary_databases;


INSERT INTO #DatabaseID
(
    DatabaseName,
    DatabaseID,
    Instance
)
SELECT primary_database,
        primary_id,
        'Primary'
FROM msdb.dbo.log_shipping_primary_databases;


SELECT di.DatabaseName,
        di.Instance,
        CASE lsmed.[agent_type]
            WHEN 0 THEN
                'Backup'
            WHEN 1 THEN
                'Copy'
            WHEN 2 THEN
                'Restore'
            ELSE
                ''
        END AS [Action],
        lsmed.[session_id] AS SessionID,
        lsmed.[sequence_number] AS SequenceNumber,
        lsmed.[log_time] AS LogTime,
        lsmed.[message] AS [Message]
FROM msdb.dbo.log_shipping_monitor_error_detail AS lsmed
    INNER JOIN #DatabaseID AS di
        ON di.DatabaseID = lsmed.agent_id
ORDER BY lsmed.[log_time],
            lsmed.[database_name],
            lsmed.[agent_type],
            lsmed.[session_id],
            lsmed.[sequence_number];

DROP TABLE #DatabaseID;"

            # Get the log shipping errors
            $results = $server.Query($query)

            if ($results.Count -ge 1) {

                # Filter the results
                if ($Database) {
                    $results = $results | Where-Object { $_.DatabaseName -in $Database }
                }

                if ($Action) {
                    $results = $results | Where-Object { $_.Action -in $Action }
                }

                if ($DateTimeFrom) {
                    $results = $results | Where-Object {$_.Logtime -ge $DateTimeFrom}
                }

                if ($DateTimeTo) {
                    $results = $results | Where-Object {$_.Logtime -le $DateTimeTo}
                }

                if ($Primary) {
                    $results = $results | Where-Object {$_.Instance -eq 'Primary'}
                }

                if ($Secondary) {
                    $results = $results | Where-Object {$_.Instance -eq 'Secondary'}
                }

                # Loop through each of the results
                foreach ($result in $results) {
                    # Set up the custom object
                    $null = $collection.Add([PSCustomObject]@{
                            ComputerName   = $server.ComputerName
                            InstanceName   = $server.ServiceName
                            SqlInstance    = $server.DomainInstanceName
                            Database       = $result.DatabaseName
                            Instance       = $result.Instance
                            Action         = $result.Action
                            SessionID      = $result.SessionID
                            SequenceNumber = $result.SequenceNumber
                            LogTime        = $result.LogTime
                            Message        = $result.Message
                        })

                } # for each result
            }
            else {
                Write-Message -Message "No log shipping errors found" -Level Verbose
            }

        } # foreach instance

        return $collection

    } # end process

}

tools\dbatools\functions\Get-DbaMaintenanceSolutionLog.ps1
function Get-DbaMaintenanceSolutionLog {
    <#
        .SYNOPSIS
            Reads the log files generated by the IndexOptimize Agent Job from Ola Hallengren's MaintenanceSolution.

        .DESCRIPTION
            Ola wrote a .sql script to get the content from the commandLog table. However, if LogToTable='N', there will be no logging in that table. This function reads the text files that are written in the SQL Instance's Log directory.

        .PARAMETER SqlInstance
            The SQL Server instance.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER LogType
            Accepts 'IndexOptimize', 'DatabaseBackup', 'DatabaseIntegrityCheck'. ATM only IndexOptimize parsing is available

        .PARAMETER Since
            Consider only files generated since this date

        .PARAMETER Path
            Where to search for log files. By default it's the SQL instance errorlogpath path

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Ola, Maintenance
            Author: Klaas Vandenberghe ( @powerdbaklaas )
            Author: Simone Bizzotto ( @niphlod )

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaMaintenanceSolutionLog

        .EXAMPLE
            Get-DbaMaintenanceSolutionLog -SqlInstance sqlserver2014a

            Gets the outcome of the IndexOptimize job on sql instance sqlserver2014a.

        .EXAMPLE
            Get-DbaMaintenanceSolutionLog -SqlInstance sqlserver2014a -SqlCredential $credential

            Gets the outcome of the IndexOptimize job on sqlserver2014a, using SQL Authentication.

        .EXAMPLE
            'sqlserver2014a', 'sqlserver2020test' | Get-DbaMaintenanceSolutionLog

            Gets the outcome of the IndexOptimize job on sqlserver2014a and sqlserver2020test.

        .EXAMPLE
            Get-DbaMaintenanceSolutionLog -SqlInstance sqlserver2014a -Path 'D:\logs\maintenancesolution\'

            Gets the outcome of the IndexOptimize job on sqlserver2014a, reading the log files in their custom location.

        .EXAMPLE
            Get-DbaMaintenanceSolutionLog -SqlInstance sqlserver2014a -Since '2017-07-18'

            Gets the outcome of the IndexOptimize job on sqlserver2014a, starting from july 18, 2017.

        .EXAMPLE
        Get-DbaMaintenanceSolutionLog -SqlInstance sqlserver2014a -LogType IndexOptimize

        Gets the outcome of the IndexOptimize job on sqlserver2014a, the other options are not yet available! sorry
    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [ValidateSet('IndexOptimize', 'DatabaseBackup', 'DatabaseIntegrityCheck')]
        [string[]]$LogType = 'IndexOptimize',
        [datetime]$Since,
        [string]$Path,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        function process-block ($block) {
            $fresh = @{
                'ObjectType'     = $null
                'IndexType'      = $null
                'ImageText'      = $null
                'NewLOB'         = $null
                'FileStream'     = $null
                'ColumnStore'    = $null
                'AllowPageLocks' = $null
                'PageCount'      = $null
                'Fragmentation'  = $null
                'Error'          = $null
            }
            foreach ($l in $block) {
                $splitted = $l -split ': ', 2
                if (($splitted.Length -ne 2) -or ($splitted[0].length -gt 20)) {
                    if ($null -eq $fresh['Error']) {
                        $fresh['Error'] = New-Object System.Collections.ArrayList
                    }
                    $null = $fresh['Error'].Add($l)
                    continue
                }
                $k = $splitted[0]
                $v = $splitted[1]
                if ($k -eq 'Date and Time') {
                    # this is the end date, we already parsed the start date of the block
                    if ($fresh.ContainsKey($k)) {
                        continue
                    }
                }
                $fresh[$k] = $v
            }
            if ($fresh.ContainsKey('Command')) {
                if ($fresh['Command'] -match '(SET LOCK_TIMEOUT (?<timeout>\d+); )?ALTER INDEX \[(?<index>[^\]]+)\] ON \[(?<database>[^\]]+)\]\.\[(?<schema>[^]]+)\]\.\[(?<table>[^\]]+)\] (?<action>[^\ ]+)( PARTITION = (?<partition>\d+))? WITH \((?<options>[^\)]+)') {
                    $fresh['Index'] = $Matches.index
                    $fresh['Statistics'] = $null
                    $fresh['Schema'] = $Matches.Schema
                    $fresh['Table'] = $Matches.Table
                    $fresh['Action'] = $Matches.action
                    $fresh['Options'] = $Matches.options
                    $fresh['Timeout'] = $Matches.timeout
                    $fresh['Partition'] = $Matches.partition
                }
                elseif ($fresh['Command'] -match '(SET LOCK_TIMEOUT (?<timeout>\d+); )?UPDATE STATISTICS \[(?<database>[^\]]+)\]\.\[(?<schema>[^]]+)\]\.\[(?<table>[^\]]+)\] \[(?<stat>[^\]]+)\]') {
                    $fresh['Index'] = $null
                    $fresh['Statistics'] = $Matches.stat
                    $fresh['Schema'] = $Matches.Schema
                    $fresh['Table'] = $Matches.Table
                    $fresh['Action'] = $null
                    $fresh['Options'] = $null
                    $fresh['Timeout'] = $Matches.timeout
                    $fresh['Partition'] = $null
                }
            }
            if ($fresh.ContainsKey('Comment')) {
                $commentparts = $fresh['Comment'] -split ', '
                foreach ($part in $commentparts) {
                    $indkey, $indvalue = $part -split ': ', 2
                    if ($fresh.ContainsKey($indkey)) {
                        $fresh[$indkey] = $indvalue
                    }
                }
            }
            if ($null -ne $fresh['Error']) {
                $fresh['Error'] = $fresh['Error'] -join "`n"
            }

            return $fresh
        }
    }
    process {
        foreach ($instance in $sqlinstance) {
            $logdir = $logfiles = $null
            $computername = $instance.ComputerName
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Can't connect to $instance" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            if ($logtype -ne 'IndexOptimize') {
                Write-Message -Level Warning -Message "Parsing $logtype is not supported at the moment"
                Continue
            }
            if ($Path) {
                $logdir = Join-AdminUnc -Servername $server.ComputerName -Filepath $Path
            }
            else {
                $logdir = Join-AdminUnc -Servername $server.ComputerName -Filepath $server.errorlogpath # -replace '^(.):', "\\$computername\`$1$"
            }
            if (!$logdir) {
                Write-Message -Level Warning -Message "No log directory returned from $instance"
                Continue
            }

            Write-Message -Level Verbose -Message "Log directory on $computername is $logdir"
            if (! (Test-Path $logdir)) {
                Write-Message -Level Warning -Message "Directory $logdir is not accessible"
                continue
            }
            $logfiles = [System.IO.Directory]::EnumerateFiles("$logdir", "IndexOptimize_*.txt")
            if ($Since) {
                $filteredlogs = @()
                foreach ($l in $logfiles) {
                    $base = $($l.Substring($l.Length - 15, 15))
                    try {
                        $datefile = [DateTime]::ParseExact($base, 'yyyyMMdd_HHmmss', $null)
                    }
                    catch {
                        $datefile = Get-ItemProperty -Path $l | select -ExpandProperty CreationTime
                    }
                    if ($datefile -gt $since) {
                        $filteredlogs += $l
                    }
                }
                $logfiles = $filteredlogs
            }
            if (! $logfiles.count -ge 1) {
                Write-Message -Level Warning -Message "No log files returned from $computername"
                Continue
            }
            $instanceinfo = @{ }
            $instanceinfo['ComputerName'] = $server.ComputerName
            $instanceinfo['InstanceName'] = $server.ServiceName
            $instanceinfo['SqlInstance'] = $server.Name

            foreach ($File in $logfiles) {
                Write-Message -Level Verbose -Message "Reading $file"
                $text = New-Object System.IO.StreamReader -ArgumentList "$File"
                $block = New-Object System.Collections.ArrayList
                $remember = @{}
                while ($line = $text.ReadLine()) {

                    $real = $line.Trim()
                    if ($real.Length -eq 0) {
                        $processed = process-block $block
                        if ('Procedure' -in $processed.Keys) {
                            $block = New-Object System.Collections.ArrayList
                            continue
                        }
                        if ('Database' -in $processed.Keys) {
                            Write-Message -Level Verbose -Message "Index and Stats Optimizations on Database $($processed.Database) on $computername"
                            $processed.Remove('Is accessible')
                            $processed.Remove('User access')
                            $processed.Remove('Date and time')
                            $processed.Remove('Standby')
                            $processed.Remove('Recovery Model')
                            $processed.Remove('Updateability')
                            $processed['Database'] = $processed['Database'].Trim('[]')
                            $remember = $processed.Clone()
                        }
                        else {
                            foreach ($k in $processed.Keys) {
                                $remember[$k] = $processed[$k]
                            }
                            $remember.Remove('Command')
                            $remember['StartTime'] = [dbadatetime]([DateTime]::ParseExact($remember['Date and time'] , "yyyy-MM-dd HH:mm:ss", $null))
                            $remember.Remove('Date and time')
                            $remember['Duration'] = ($remember['Duration'] -as [timespan])
                            [pscustomobject]$remember
                        }
                        $block = New-Object System.Collections.ArrayList
                    }
                    else {
                        $null = $block.Add($real)
                    }
                }
                $text.close()
            }
        }
    }
}
tools\dbatools\functions\Get-DbaMaxMemory.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaMaxMemory {
    <#
        .SYNOPSIS
            Gets the 'Max Server Memory' configuration setting and the memory of the server.  Works on SQL Server 2000-2014.

        .DESCRIPTION
            This command retrieves the SQL Server 'Max Server Memory' configuration setting as well as the total  physical installed on the server.

        .PARAMETER SqlInstance
            Allows you to specify a comma separated list of servers to query.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: MaxMemory, Memory
            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaMaxMemory

        .EXAMPLE
            Get-DbaMaxMemory -SqlInstance sqlcluster,sqlserver2012

            Get memory settings for all servers within the SQL Server Central Management Server "sqlcluster".

        .EXAMPLE
            Get-DbaMaxMemory -SqlInstance sqlcluster | Where-Object { $_.SqlMaxMB -gt $_.TotalMB }

            Find all servers in Server Central Management Server that have 'Max Server Memory' set to higher than the total memory of the server (think 2147483647)
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $totalMemory = $server.PhysicalMemory

            # Some servers under-report by 1MB.
            if (($totalMemory % 1024) -ne 0) {
                $totalMemory = $totalMemory + 1
            }

            [pscustomobject]@{
                ComputerName = $server.ComputerName
                InstanceName = $server.ServiceName
                SqlInstance  = $server.DomainInstanceName
                TotalMB      = [int]$totalMemory
                SqlMaxMB     = [int]$server.Configuration.MaxServerMemory.ConfigValue
            } | Select-DefaultView -ExcludeProperty Server
        }
    }
}
tools\dbatools\functions\Get-DbaMemoryUsage.ps1
#ValidationTags#Messaging,CodeStyle#
function Get-DbaMemoryUsage {
    <#
        .SYNOPSIS
            Get amount of memory in use by *all* SQL Server components and instances

        .DESCRIPTION
            Retrieves the amount of memory per performance counter. Default output includes columns Server, counter instance, counter, number of pages, memory in KB, memory in MB
            SSAS and SSIS are included.

            SSRS does not have memory counters, only memory shrinks and memory pressure state.

            This function requires local admin role on the targeted computers.

        .PARAMETER ComputerName
            The Windows Server that you are connecting to. Note that this will return all instances, but Out-GridView makes it easy to filter to specific instances.

        .PARAMETER Credential
            Credential object used to connect to the SQL Server as a different user

        .PARAMETER Simple
            Shows concise information including Server name, Database name, and the date the last time backups were performed

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Memory
            Author: Klaas Vandenberghe ( @PowerDBAKlaas )

            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

            SSIS Counters: https://msdn.microsoft.com/en-us/library/ms137622.aspx

        .LINK
            https://dbatools.io/Get-DbaMemoryUsage

        .EXAMPLE
            Get-DbaMemoryUsage -ComputerName ServerA

            Returns a custom object displaying Server, counter instance, counter, number of pages, memory in KB, memory in MB

        .EXAMPLE
            Get-DbaMemoryUsage -ComputerName ServerA\sql987 -Simple

            Returns a custom object with Server, counter instance, counter, number of pages, memory in KB, memory in MB

        .EXAMPLE
            Get-DbaMemoryUsage -ComputerName ServerA\sql987 | Out-Gridview

            Returns a gridview displaying Server, counter instance, counter, number of pages, memory in KB, memory in MB
    #>
    [CmdletBinding()]
    param (
        [parameter(ValueFromPipeline)]
        [Alias("Host", "cn", "Server")]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [switch]$Simple,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        if ($Simple) {
            $Memcounters = '(Total Server Memory |Target Server Memory |Connection Memory |Lock Memory |SQL Cache Memory |Optimizer Memory |Granted Workspace Memory |Cursor memory usage|Maximum Workspace)'
            $Plancounters = 'total\)\\cache pages'
            $BufManpagecounters = 'Total pages'
            $SSAScounters = '(\\memory usage)'
            $SSIScounters = '(memory)'
        }
        else {
            $Memcounters = '(Total Server Memory |Target Server Memory |Connection Memory |Lock Memory |SQL Cache Memory |Optimizer Memory |Granted Workspace Memory |Cursor memory usage|Maximum Workspace)'
            $Plancounters = '(cache pages|procedure plan|ad hoc sql plan|prepared SQL Plan)'
            $BufManpagecounters = '(Free pages|Reserved pages|Stolen pages|Total pages|Database pages|target pages|extension .* pages)'
            $SSAScounters = '(\\memory )'
            $SSIScounters = '(memory)'
        }

        $scriptblock = {
            param ($Memcounters,
                $Plancounters,
                $BufManpagecounters,
                $SSAScounters,
                $SSIScounters)
            Write-Verbose "Searching for Memory Manager Counters on $Computer"
            try {
                $availablecounters = (Get-Counter -ListSet '*sql*:Memory Manager*' -ErrorAction SilentlyContinue).paths
                (Get-Counter -Counter $availablecounters -ErrorAction SilentlyContinue).countersamples |
                    Where-Object { $_.Path -match $Memcounters } |
                    ForEach-Object {
                    $instance = (($_.Path.split("\")[-2]).replace("mssql`$", "")).split(':')[0]
                    if ($instance -eq 'sqlserver') { $instance = 'mssqlserver' }
                    [PSCustomObject]@{
                        ComputerName    = $env:computername
                        SqlInstance     = $instance
                        CounterInstance = (($_.Path.split("\")[-2]).replace("mssql`$", "")).split(':')[1]
                        Counter         = $_.Path.split("\")[-1]
                        Pages           = $null
                        MemKB           = $_.cookedvalue
                        MemMB           = $_.cookedvalue / 1024
                    }
                }
            }
            catch {
                Write-Verbose "No Memory Manager Counters on $Computer"
            }

            Write-Verbose "Searching for Plan Cache Counters on $Computer"
            try {
                $availablecounters = (Get-Counter -ListSet '*sql*:Plan Cache*' -ErrorAction SilentlyContinue).paths
                (Get-Counter -Counter $availablecounters -ErrorAction SilentlyContinue).countersamples |
                    Where-Object { $_.Path -match $Plancounters } |
                    ForEach-Object {
                    $instance = (($_.Path.split("\")[-2]).replace("mssql`$", "")).split(':')[0]
                    if ($instance -eq 'sqlserver') { $instance = 'mssqlserver' }
                    [PSCustomObject]@{
                        ComputerName    = $env:computername
                        SqlInstance     = $instance
                        CounterInstance = (($_.Path.split("\")[-2]).replace("mssql`$", "")).split(':')[1]
                        Counter         = $_.Path.split("\")[-1]
                        Pages           = $_.cookedvalue
                        MemKB           = $_.cookedvalue * 8192 / 1024
                        MemMB           = $_.cookedvalue * 8192 / 1048576
                    }
                }
            }
            catch {
                Write-Verbose "No Plan Cache Counters on $Computer"
            }

            Write-Verbose "Searching for Buffer Manager Counters on $Computer"
            try {
                $availablecounters = (Get-Counter -ListSet "*Buffer Manager*" -ErrorAction SilentlyContinue).paths
                (Get-Counter -Counter $availablecounters -ErrorAction SilentlyContinue).countersamples |
                    Where-Object { $_.Path -match $BufManpagecounters } |
                    ForEach-Object {
                    $instance = (($_.Path.split("\")[-2]).replace("mssql`$", "")).split(':')[0]
                    if ($instance -eq 'sqlserver') { $instance = 'mssqlserver' }
                    [PSCustomObject]@{
                        ComputerName    = $env:computername
                        SqlInstance     = $instance
                        CounterInstance = (($_.Path.split("\")[-2]).replace("mssql`$", "")).split(':')[1]
                        Counter         = $_.Path.split("\")[-1]
                        Pages           = $_.cookedvalue
                        MemKB           = $_.cookedvalue * 8192 / 1024.0
                        MemMB           = $_.cookedvalue * 8192 / 1048576.0
                    }
                }
            }
            catch {
                Write-Verbose "No Buffer Manager Counters on $Computer"
            }

            Write-Verbose "Searching for SSAS Counters on $Computer"
            try {
                $availablecounters = (Get-Counter -ListSet "MSAS*:Memory" -ErrorAction SilentlyContinue).paths
                (Get-Counter -Counter $availablecounters -ErrorAction SilentlyContinue).countersamples |
                    Where-Object { $_.Path -match $SSAScounters } |
                    ForEach-Object {
                    $instance = (($_.Path.split("\")[-2]).replace("mssql`$", "")).split(':')[0]
                    if ($instance -eq 'sqlserver') { $instance = 'mssqlserver' }
                    [PSCustomObject]@{
                        ComputerName    = $env:COMPUTERNAME
                        SqlInstance     = $instance
                        CounterInstance = (($_.Path.split("\")[-2]).replace("mssql`$", "")).split(':')[1]
                        Counter         = $_.Path.split("\")[-1]
                        Pages           = $null
                        MemKB           = $_.cookedvalue
                        MemMB           = $_.cookedvalue / 1024
                    }
                }
            }
            catch {
                Write-Verbose "No SSAS Counters on $Computer"
            }

            Write-Verbose "Searching for SSIS Counters on $Computer"
            try {
                $availablecounters = (Get-Counter -ListSet "*SSIS*" -ErrorAction SilentlyContinue).paths
                (Get-Counter -Counter $availablecounters -ErrorAction SilentlyContinue).countersamples |
                    Where-Object { $_.Path -match $SSIScounters } |
                    ForEach-Object {
                    $instance = (($_.Path.split("\")[-2]).replace("mssql`$", "")).split(':')[0]
                    if ($instance -eq 'sqlserver') { $instance = 'mssqlserver' }
                    [PSCustomObject]@{
                        ComputerName    = $env:computername
                        SqlInstance     = $instance
                        CounterInstance = (($_.Path.split("\")[-2]).replace("mssql`$", "")).split(':')[1]
                        Counter         = $_.Path.split("\")[-1]
                        Pages           = $null
                        MemKB           = $_.cookedvalue / 1024
                        MemMB           = $_.cookedvalue / 1024 / 1024
                    }
                }
            }
            catch {
                Write-Verbose "No SSIS Counters on $Computer"
            }
        }
    }

    process {
        foreach ($Computer in $ComputerName.ComputerName) {
            $reply = Resolve-DbaNetworkName -ComputerName $computer -Credential $Credential -ErrorAction SilentlyContinue
            if ($reply.FullComputerName) {
                $Computer = $reply.FullComputerName
                try {
                    Write-Message -Level Verbose -Message "Connecting to $Computer"
                    Invoke-Command2 -ComputerName $Computer -Credential $Credential -ScriptBlock $scriptblock -argumentlist $Memcounters, $Plancounters, $BufManpagecounters, $SSAScounters, $SSIScounters
                }
                catch {
                    Stop-Function -Continue -Message "Failure" -ErrorRecord $_ -Target $computer -Continue
                }
            }
            else {
                Write-Message -Level Warning -Message "Can't resolve $Computer."
                Continue
            }
        }
    }
}
tools\dbatools\functions\Get-DbaMsdtc.ps1
#ValidationTags#Messaging#
function Get-DbaMsdtc {
    <#
        .SYNOPSIS
            Displays information about the Distributed Transaction Coordinator (MSDTC) on a server

        .DESCRIPTION
            Returns a custom object with Computer name, state of the MSDTC Service, security settings of MSDTC and CID's

            Requires: Windows administrator access on Servers

        .PARAMETER ComputerName
            The SQL Server (or server in general) that you're connecting to.

        .NOTES
            Tags: Msdtc, dtc
            Author: Klaas Vandenberghe ( powerdbaklaas )

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaMsdtc

        .EXAMPLE
            Get-DbaMsdtc -ComputerName srv0042

            Get DTC status for the server srv0042

        .EXAMPLE
            $Computers = (Get-Content D:\configfiles\SQL\MySQLInstances.txt | % {$_.split('\')[0]})
            $Computers | Get-DbaMsdtc

            Get DTC status for all the computers in a .txt file

        .EXAMPLE
            Get-DbaMsdtc -Computername $Computers | where { $_.dtcservicestate -ne 'running' }

            Get DTC status for all the computers where the MSDTC Service is not running

        .EXAMPLE
            Get-DbaMsdtc -ComputerName srv0042 | Out-Gridview

            Get DTC status for the computer srv0042 and show in a grid view
    #>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Alias('cn', 'host', 'Server')]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME
    )

    begin {
        $ComputerName = $ComputerName | ForEach-Object {$_.split("\")[0]} | Select-Object -Unique
        $query = "Select * FROM Win32_Service WHERE Name = 'MSDTC'"
        $dtcSecurity = {
            Get-ItemProperty -Path HKLM:\Software\Microsoft\MSDTC\Security |
                Select-Object PSPath, PSComputerName, AccountName, networkDTCAccess,
            networkDTCAccessAdmin, networkDTCAccessClients, networkDTCAccessInbound,
            networkDTCAccessOutBound, networkDTCAccessTip, networkDTCAccessTransactions, XATransactions
        }
        $dtcCids = {
            New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT | Out-Null
            Get-ItemProperty -Path HKCR:\CID\*\Description |
                Select-Object @{ l = 'Data'; e = { $_.'(default)' } }, @{ l = 'CID'; e = { $_.PSParentPath.split('\')[-1] } }
            Remove-PSDrive -Name HKCR | Out-Null
        }
    }
    process {
        foreach ($computer in $ComputerName) {
            $reg = $cids = $null
            $cidHash = @{}
            if ( Test-PSRemoting -ComputerName $computer ) {
                $dtcservice = $null
                Write-Message -Level Verbose -Message "Getting DTC on $computer via WSMan"
                $dtcservice = Get-Ciminstance -ComputerName $computer -Query $query
                if ( $null -eq $dtcservice ) {
                    Write-Warning "Can't connect to CIM on $computer via WSMan"
                }

                Write-Message -Level Verbose -Message "Getting MSDTC Security Registry Values on $computer"
                $reg = Invoke-Command -ComputerName $computer -ScriptBlock $dtcSecurity
                if ( $null -eq $reg ) {
                    Write-Message -Level Warning -Message "Can't connect to MSDTC Security registry on $computer"
                }
                Write-Message -Level Verbose -Message "Getting MSDTC CID Registry Values on $computer"
                $cids = Invoke-Command -ComputerName $computer -ScriptBlock $dtcCids
                if ( $null -ne $cids ) {
                    foreach ($key in $cids) {
                        $cidHash.Add($key.Data, $key.CID)
                    }
                }
                else {
                    Write-Message -Level Warning -Message "Can't connect to MSDTC CID registry on $computer"
                }
            }
            else {
                Write-Message -Level Verbose -Message "PSRemoting is not enabled on $computer"
                try {
                    Write-Message -Level Verbose -Message "Failed To get DTC via WinRM. Getting DTC on $computer via DCom"
                    $SessionParams = @{ }
                    $SessionParams.ComputerName = $Computer
                    $SessionParams.SessionOption = (New-CimSessionOption -Protocol Dcom)
                    $Session = New-CimSession @SessionParams
                    $dtcservice = Get-Ciminstance -CimSession $Session -Query $query
                }
                catch {
                    Stop-Function -Message "Can't connect to CIM on $computer via DCom" -Target $computer -ErrorRecord $_ -Continue
                }
            }
            if ( $dtcservice ) {
                [PSCustomObject]@{
                    ComputerName                 = $dtcservice.PSComputerName
                    DTCServiceName               = $dtcservice.DisplayName
                    DTCServiceState              = $dtcservice.State
                    DTCServiceStatus             = $dtcservice.Status
                    DTCServiceStartMode          = $dtcservice.StartMode
                    DTCServiceAccount            = $dtcservice.StartName
                    DTCCID_MSDTC                 = $cidHash['MSDTC']
                    DTCCID_MSDTCUIS              = $cidHash['MSDTCUIS']
                    DTCCID_MSDTCTIPGW            = $cidHash['MSDTCTIPGW']
                    DTCCID_MSDTCXATM             = $cidHash['MSDTCXATM']
                    networkDTCAccess             = $reg.networkDTCAccess
                    networkDTCAccessAdmin        = $reg.networkDTCAccessAdmin
                    networkDTCAccessClients      = $reg.networkDTCAccessClients
                    networkDTCAccessInbound      = $reg.networkDTCAccessInbound
                    networkDTCAccessOutBound     = $reg.networkDTCAccessOutBound
                    networkDTCAccessTip          = $reg.networkDTCAccessTip
                    networkDTCAccessTransactions = $reg.networkDTCAccessTransactions
                    XATransactions               = $reg.XATransactions
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaNetworkActivity.ps1
function Get-DbaNetworkActivity {
    <#
      .SYNOPSIS
      Gets the Current traffic on every Network Interface on a computer.

      .DESCRIPTION
      Gets the Current traffic on every Network Interface on a computer.
      See https://msdn.microsoft.com/en-us/library/aa394293(v=vs.85).aspx

      Requires Local Admin rights on destination computer(s).

      .PARAMETER ComputerName
      The SQL Server (or server in general) that you're connecting to. This command handles named instances.

      .PARAMETER Credential
      Credential object used to connect to the computer as a different user.

      .PARAMETER EnableException
      By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
      This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
      Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

      .NOTES
      Author: Klaas Vandenberghe ( @PowerDBAKlaas )
      Tags: Network
      dbatools PowerShell module (https://dbatools.io)
      Copyright (C) 2016 Chrissy LeMaire
      License: MIT https://opensource.org/licenses/MIT

      .LINK
      https://dbatools.io/Get-DbaNetworkActivity

      .EXAMPLE
      Get-DbaNetworkActivity -ComputerName sqlserver2014a

      Gets the Current traffic on every Network Interface on computer sqlserver2014a.

      .EXAMPLE
      'sql1','sql2','sql3' | Get-DbaNetworkActivity

      Gets the Current traffic on every Network Interface on computers sql1, sql2 and sql3.

      .EXAMPLE
      Get-DbaNetworkActivity -ComputerName sql1,sql2 | Out-Gridview

      Gets the Current traffic on every Network Interface on computers sql1 and sql2, and shows them in a grid view.

  #>
    [CmdletBinding()]
    Param (
        [parameter(ValueFromPipeline)]
        [Alias("cn", "host", "Server")]
        [string[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential] $Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    BEGIN {
        $ComputerName = $ComputerName | ForEach-Object {$_.split("\")[0]} | Select-Object -Unique
        $sessionoption = New-CimSessionOption -Protocol DCom
    }
    PROCESS {
        foreach ($computer in $ComputerName) {
            $Server = Resolve-DbaNetworkName -ComputerName $Computer -Credential $credential
            if ( $Server.FullComputerName ) {
                $Computer = $server.FullComputerName
                Write-Message -Level Verbose -Message "Creating CIMSession on $computer over WSMan"
                $CIMsession = New-CimSession -ComputerName $Computer -ErrorAction SilentlyContinue -Credential $Credential
                if ( -not $CIMSession ) {
                    Write-Message -Level Verbose -Message "Creating CIMSession on $computer over WSMan failed. Creating CIMSession on $computer over DCom"
                    $CIMsession = New-CimSession -ComputerName $Computer -SessionOption $sessionoption -ErrorAction SilentlyContinue -Credential $Credential
                }
                if ( $CIMSession ) {
                    Write-Message -Level Verbose -Message "Getting properties for Network Interfaces on $computer"
                    $NICs = Get-CimInstance -CimSession $CIMSession -ClassName Win32_PerfFormattedData_Tcpip_NetworkInterface
                    $NICs | Add-Member -Force -MemberType ScriptProperty -Name ComputerName -Value { $computer }
                    $NICs | Add-Member -Force -MemberType ScriptProperty -Name Bandwith -Value { switch ( $this.CurrentBandWidth ) { 10000000000 { '10Gb' } 1000000000 { '1Gb' } 100000000 { '100Mb' } 10000000 { '10Mb' } 1000000 { '1Mb' } 100000 { '100Kb' } default { 'Low' } } }
                    foreach ( $NIC in $NICs ) { Select-DefaultView -InputObject $NIC -Property 'ComputerName', 'Name as NIC', 'BytesReceivedPersec', 'BytesSentPersec', 'BytesTotalPersec', 'Bandwidth'}
                } #if CIMSession
                else {
                    Write-Message -Level Warning -Message "Can't create CIMSession on $computer"
                }
            } #if computername
            else {
                Write-Message -Level Warning -Message "can't connect to $computer"
            }
        } #foreach computer
    } #PROCESS
} #function
tools\dbatools\functions\Get-DbaNetworkCertificate.ps1
function Get-DbaNetworkCertificate {
    <#
.SYNOPSIS
Simplifies finding computer certificates that are candidates for using with SQL Server's network encryption

.DESCRIPTION
Gets computer certificates on localhost that are candidates for using with SQL Server's network encryption

.PARAMETER ComputerName
The target SQL Server - defaults to localhost. If target is a cluster, you must specify the distinct nodes.

.PARAMETER Credential
Allows you to login to $ComputerName using alternative credentials.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Certificate

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
Get-DbaNetworkCertificate
Gets computer certificates on localhost that are candidates for using with SQL Server's network encryption

.EXAMPLE
Get-DbaNetworkCertificate -ComputerName sql2016

Gets computer certificates on sql2016 that are being used for SQL Server network encryption

#>
    [CmdletBinding()]
    param (
        [parameter(ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer", "SqlInstance")]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($computer in $computername) {

            Write-Message -Level Verbose -Message "Connecting to SQL WMI on $($computer.ComputerName)"
            try {
                $sqlwmis = Invoke-ManagedComputerCommand -ComputerName $computer.ComputerName -ScriptBlock { $wmi.Services } -Credential $Credential -ErrorAction Stop | Where-Object DisplayName -match "SQL Server \("
            }
            catch {
                Stop-Function -Message $_ -Target $sqlwmi -Continue
            }

            foreach ($sqlwmi in $sqlwmis) {

                $regroot = ($sqlwmi.AdvancedProperties | Where-Object Name -eq REGROOT).Value
                $vsname = ($sqlwmi.AdvancedProperties | Where-Object Name -eq VSNAME).Value
                $instancename = $sqlwmi.DisplayName.Replace('SQL Server (', '').Replace(')', '') # Don't clown, I don't know regex :(
                $serviceaccount = $sqlwmi.ServiceAccount

                if ([System.String]::IsNullOrEmpty($regroot)) {
                    $regroot = $sqlwmi.AdvancedProperties | Where-Object { $_ -match 'REGROOT' }
                    $vsname = $sqlwmi.AdvancedProperties | Where-Object { $_ -match 'VSNAME' }

                    if (![System.String]::IsNullOrEmpty($regroot)) {
                        $regroot = ($regroot -Split 'Value\=')[1]
                        $vsname = ($vsname -Split 'Value\=')[1]
                    }
                    else {
                        Write-Message -Level Warning -Message "Can't find instance $vsname on $env:COMPUTERNAME"
                        return
                    }
                }

                if ([System.String]::IsNullOrEmpty($vsname)) { $vsname = $computer }

                Write-Message -Level Verbose -Message "Regroot: $regroot"
                Write-Message -Level Verbose -Message "ServiceAcct: $serviceaccount"
                Write-Message -Level Verbose -Message "InstanceName: $instancename"
                Write-Message -Level Verbose -Message "VSNAME: $vsname"

                $scriptblock = {
                    $regroot = $args[0]
                    $serviceaccount = $args[1]
                    $instancename = $args[2]
                    $vsname = $args[3]

                    $regpath = "Registry::HKEY_LOCAL_MACHINE\$regroot\MSSQLServer\SuperSocketNetLib"

                    $thumbprint = (Get-ItemProperty -Path $regpath -Name Certificate -ErrorAction SilentlyContinue).Certificate

                    try {
                        $cert = Get-ChildItem Cert:\LocalMachine -Recurse -ErrorAction Stop | Where-Object Thumbprint -eq $Thumbprint
                    }
                    catch {
                        # Don't care - sometimes there's errors that are thrown for apparent good reason
                    }

                    if (!$cert) { continue }

                    [pscustomobject]@{
                        ComputerName   = $env:COMPUTERNAME
                        InstanceName   = $instancename
                        SqlInstance    = $vsname
                        ServiceAccount = $serviceaccount
                        FriendlyName   = $cert.FriendlyName
                        DnsNameList    = $cert.DnsNameList
                        Thumbprint     = $cert.Thumbprint
                        Generated      = $cert.NotBefore
                        Expires        = $cert.NotAfter
                        IssuedTo       = $cert.Subject
                        IssuedBy       = $cert.Issuer
                        Certificate    = $cert
                    }
                }

                Write-Message -Level Verbose -Message "Connecting to $computer to get a list of certs"
                try {
                    Invoke-Command2 -ComputerName $computer.ComputerName -Credential $Credential -ArgumentList $regroot, $serviceaccount, $instancename, $vsname -ScriptBlock $scriptblock -ErrorAction Stop |
                        Select-DefaultView -ExcludeProperty Certificate
                }
                catch {
                    Stop-Function -Message $_ -ErrorRecord $_ -Target $ComputerName -Continue
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaOpenTransaction.ps1
function Get-DbaOpenTransaction {
    <#
        .SYNOPSIS
            Displays all open transactions.

        .DESCRIPTION
            This command is based on open transaction script published by Paul Randal.
            Reference: https://www.sqlskills.com/blogs/paul/script-open-transactions-with-text-and-plans/

        .PARAMETER SqlInstance
            The SQL Server instance

        .PARAMETER SqlCredential
            Connect using alternative credentials

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database, Process, Session, ActivityMonitor
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaOpenTransaction

        .EXAMPLE
            Get-DbaOpenTransaction -SqlInstance sqlserver2014a

            Returns open transactions for sqlserver2014a

        .EXAMPLE
            Get-DbaOpenTransaction -SqlInstance sqlserver2014a -SqlCredential (Get-Credential sqladmin)

            Logs into sqlserver2014a using the login "sqladmin"
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [switch]$EnableException
    )

    begin {
        $sql = "
            SELECT  SERVERPROPERTY('MachineName') AS ComputerName,
            ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
            SERVERPROPERTY('ServerName') AS SqlInstance,
            [s_tst].[session_id] as Spid,
            [s_es].[login_name] as Login,
            DB_NAME (s_tdt.database_id) AS [Database],
            [s_tdt].[database_transaction_begin_time] AS [BeginTime],
            [s_tdt].[database_transaction_log_bytes_used] AS [LogBytesUsed],
            [s_tdt].[database_transaction_log_bytes_reserved] AS [LogBytesReserved],
            [s_est].text AS [LastQuery],
            [s_eqp].[query_plan] AS [LastPlan]
            FROM
                sys.dm_tran_database_transactions [s_tdt]
            JOIN
                sys.dm_tran_session_transactions [s_tst]
            ON
                [s_tst].[transaction_id] = [s_tdt].[transaction_id]
            JOIN
                sys.[dm_exec_sessions] [s_es]
            ON
                [s_es].[session_id] = [s_tst].[session_id]
            JOIN
                sys.dm_exec_connections [s_ec]
            ON
                [s_ec].[session_id] = [s_tst].[session_id]
            LEFT OUTER JOIN
                sys.dm_exec_requests [s_er]
            ON
                [s_er].[session_id] = [s_tst].[session_id]
            CROSS APPLY
                sys.dm_exec_sql_text ([s_ec].[most_recent_sql_handle]) AS [s_est]
            OUTER APPLY
                sys.dm_exec_query_plan ([s_er].[plan_handle]) AS [s_eqp]
            ORDER BY
                [BeginTime] ASC"
    }
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $server.Query($sql)
        }
    }
}
tools\dbatools\functions\Get-DbaOperatingSystem.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaOperatingSystem {
    <#
        .SYNOPSIS
            Gets operating system information from the server.

        .DESCRIPTION
            Gets operating system information from the server and returns as an object.

        .PARAMETER ComputerName
            Target computer(s). If no computer name is specified, the local computer is targeted

        .PARAMETER Credential
            Alternate credential object to use for accessing the target computer(s).

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ServerInfo, OperatingSystem
            Author: Shawn Melton (@wsmelton | http://blog.wsmelton.info)

            Website: https: //dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaOperatingSystem

        .EXAMPLE
            Get-DbaOperatingSystem

            Returns information about the local computer's operating system

        .EXAMPLE
            Get-DbaOperatingSystem -ComputerName sql2016

            Returns information about the sql2016's operating system
    #>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Alias("cn", "host", "Server")]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($computer in $ComputerName) {
            Write-Message -Level Verbose -Message "Connecting to $computer"
            $server = Resolve-DbaNetworkName -ComputerName $computer.ComputerName -Credential $Credential

            $computerResolved = $server.FullComputerName

            if (!$computerResolved) {
                Write-Message -Level Warning -Message "Unable to resolve hostname of $computer. Skipping."
                continue
            }

            try {
                $psVersion = Invoke-Command2 -ComputerName $computerResolved -Credential $Credential -ScriptBlock { $PSVersionTable.PSVersion }
            }
            catch {
                Stop-Function -Message "Failure collecting PowerShell version on $computer" -Target $computer -ErrorRecord $_
                return
            }

            try {
                if (Test-Bound "Credential") {
                    $os = Get-DbaCmObject -ClassName Win32_OperatingSystem -ComputerName $computerResolved -Credential $Credential -EnableException
                }
                else {
                    $os = Get-DbaCmObject -ClassName Win32_OperatingSystem -ComputerName $computerResolved -EnableException
                }
            }
            catch {
                Stop-Function -Message "Failure collecting OS information on $computer" -Target $computer -ErrorRecord $_
                return
            }

            try {
                if (Test-Bound "Credential") {
                    $tz = Get-DbaCmObject -ClassName Win32_TimeZone -ComputerName $computerResolved -Credential $Credential -EnableException
                }
                else {
                    $tz = Get-DbaCmObject -ClassName Win32_TimeZone -ComputerName $computerResolved -EnableException
                }
            }
            catch {
                Stop-Function -Message "Failure collecting TimeZone information on $computer" -Target $computer -ErrorRecord $_
                return
            }

            try {
                if (Test-Bound "Credential") {
                    $powerPlan = Get-DbaCmObject -ClassName Win32_PowerPlan -Namespace "root\cimv2\power" -ComputerName $computerResolved -Credential $Credential -EnableException | Select-Object ElementName, InstanceId, IsActive
                }
                else {
                    $powerPlan = Get-DbaCmObject -ClassName Win32_PowerPlan -Namespace "root\cimv2\power" -ComputerName $computerResolved -EnableException | Select-Object ElementName, InstanceId, IsActive
                }
            }
            catch {
                Stop-Function -Message "Failure collecting PowerPlan information on $computer" -Target $computer -ErrorRecord $_
                return
            }

            $activePowerPlan = ($powerPlan | Where-Object IsActive).ElementName -join ','
            $language = Get-Language $os.OSLanguage

            [PSCustomObject]@{
                ComputerName             = $computerResolved
                Manufacturer             = $os.Manufacturer
                Organization             = $os.Organization
                Architecture             = $os.OSArchitecture
                Version                  = $os.Version
                Build                    = $os.BuildNumber
                Caption                  = $os.Caption
                InstallDate              = [DbaDateTime]$os.InstallDate
                LastBootTime             = [DbaDateTime]$os.LastBootUpTime
                LocalDateTime            = [DbaDateTime]$os.LocalDateTime
                PowerShellVersion        = "$($psVersion.Major).$($psVersion.Minor)"
                TimeZone                 = $tz.Caption
                TimeZoneStandard         = $tz.StandardName
                TimeZoneDaylight         = $tz.DaylightName
                BootDevice               = $os.BootDevice
                TotalVisibleMemory       = [DbaSize]($os.TotalVisibleMemorySize * 1024)
                FreePhysicalMemory       = [DbaSize]($os.FreePhysicalMemory * 1024)
                TotalVirtualMemory       = [DbaSize]($os.TotalVirtualMemorySize * 1024)
                FreeVirtualMemory        = [DbaSize]($os.FreeVirtualMemory * 1024)
                ActivePowerPlan          = $activePowerPlan
                Language                 = $language.Name
                LanguageId               = $language.LCID
                LanguageKeyboardLayoutId = $language.KeyboardLayoutId
                LanguageTwoLetter        = $language.TwoLetterISOLanguageName
                LanguageThreeLetter      = $language.ThreeLetterISOLanguageName
                LanguageAlias            = $language.DisplayName
                LanguageNative           = $language.NativeName
                CodeSet                  = $os.CodeSet
                CountryCode              = $os.CountryCode
                Locale                   = $os.Locale
            } | Select-DefaultView -Property ComputerName, Manufacturer, Organization, Architecture, Version, Caption, LastBootTime, LocalDateTime, PowerShellVersion, TimeZone, TotalVisibleMemory, ActivePowerPlan, LanguageNative
        }
    }
}
tools\dbatools\functions\Get-DbaOrphanUser.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaOrphanUser {
    <#
        .SYNOPSIS
            Get orphaned users.

        .DESCRIPTION
            An orphan user is defined by a user that does not have their matching login. (Login property = "").

        .PARAMETER SqlInstance
            The SQL Server Instance to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies the database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies the database(s) to exclude from processing. Options for this list are auto-populated from the server

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Orphan, Database, User, Security, Login
            Author: Claudio Silva (@ClaudioESSilva)
            Author: Garry Bargsley (@gbargsley)
            Editor: Simone Bizzotto (@niphlod)
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
        .LINK

            https://dbatools.io/Get-DbaOrphanUser

        .EXAMPLE
            Get-DbaOrphanUser -SqlInstance localhost\sql2016
            Finds all orphan users without matching Logins in all databases present on server 'localhost\sql2016'.

        .EXAMPLE
            Get-DbaOrphanUser -SqlInstance localhost\sql2016 -SqlCredential $cred
            Finds all orphan users without matching Logins in all databases present on server 'localhost\sql2016'. SQL Server authentication will be used in connecting to the server.

        .EXAMPLE
            Get-DbaOrphanUser -SqlInstance localhost\sql2016 -Database db1
            Finds orphan users without matching Logins in the db1 database present on server 'localhost\sql2016'.

    #>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Write-Message -Level Warning -Message "Failed to connect to: $instance."
                continue
            }
            $DatabaseCollection = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                $DatabaseCollection = $DatabaseCollection | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $DatabaseCollection = $DatabaseCollection | Where-Object Name -NotIn $ExcludeDatabase
            }

            if ($DatabaseCollection.Count -gt 0) {
                foreach ($db in $DatabaseCollection) {
                    try {
                        #if SQL 2012 or higher only validate databases with ContainmentType = NONE
                        if ($server.versionMajor -gt 10) {
                            if ($db.ContainmentType -ne [Microsoft.SqlServer.Management.Smo.ContainmentType]::None) {
                                Write-Message -Level Warning -Message "Database '$db' is a contained database. Contained databases can't have orphaned users. Skipping validation."
                                Continue
                            }
                        }
                        Write-Message -Level Verbose -Message "Validating users on database '$db'."
                        $UsersToWork = $db.Users | Where-Object { $_.Login -eq "" -and ($_.ID -gt 4) -and ($_.Sid.Length -gt 16 -and $_.LoginType -eq [Microsoft.SqlServer.Management.Smo.LoginType]::SqlLogin) -eq $false }

                        if ($UsersToWork.Count -gt 0) {
                            Write-Message -Level Verbose -Message "Orphan users found"
                            foreach ($user in $UsersToWork) {
                                [PSCustomObject]@{
                                    ComputerName = $server.ComputerName
                                    InstanceName = $server.ServiceName
                                    SqlInstance  = $server.DomainInstanceName
                                    DatabaseName = $db.Name
                                    User         = $user.Name
                                }
                            }
                        }
                        else {
                            Write-Message -Level Verbose -Message "No orphan users found on database '$db'."
                        }
                        #reset collection
                        $UsersToWork = $null
                    }
                    catch {
                        Stop-Function -Message $_ -Continue
                    }
                }
            }
            else {
                Write-Message -Level VeryVerbose -Message "There are no databases to analyse."
            }
        }
    }

}
tools\dbatools\functions\Get-DbaPageFileSetting.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#

function Get-DbaPageFileSetting {
<#
    .SYNOPSIS
        Returns information on the pagefile configuration of the target computer.
    
    .DESCRIPTION
        This command uses CIM (or other, related computer management tools) to detect the pagefile configuration of the target compuer(s).
        Note that this may require local administrator privileges for the relevant computers.
    
    .PARAMETER ComputerName
        The Server that you're connecting to.
        This can be the name of a computer, a SMO object, an IP address, an AD COmputer object, a connection string or a SQL Instance.
    
    .PARAMETER Credential
        Credential object used to connect to the Computer as a different user
    
    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
    
    .NOTES
        Tags: CIM
        Author: Klaas Vandenberghe ( @PowerDBAKlaas )
        
        dbatools PowerShell module (https://dbatools.io)
        Copyright (C) 2016 Chrissy LeMaire
        License: MIT https://opensource.org/licenses/MIT
    
    .EXAMPLE
        Get-DbaPageFileSetting -ComputerName ServerA,ServerB
        
        Returns a custom object displaying ComputerName, AutoPageFile, FileName, Status, LastModified, LastAccessed, AllocatedBaseSize, InitialSize, MaximumSize, PeakUsage, CurrentUsage  for ServerA and ServerB
    
    .EXAMPLE
        'ServerA' | Get-DbaPageFileSetting
        
        Returns a custom object displaying ComputerName, AutoPageFile, FileName, Status, LastModified, LastAccessed, AllocatedBaseSize, InitialSize, MaximumSize, PeakUsage, CurrentUsage  for ServerA
    
    .LINK
        https://dbatools.io/Get-DbaPageFileSetting
#>
    
    [CmdletBinding()]
    param (
        [Parameter(Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias("cn", "host", "ServerInstance", "Server", "SqlServer")]
        [DbaInstance]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($computer in $ComputerName) {
            Write-Message -Level VeryVerbose -Message "Connecting to $($computer.ComputerName)" -Target $computer
            $splatDbaCmObject = @{
                ComputerName   = $computer
                EnableException = $true
            }
            if ($Credential) { $splatDbaCmObject["Credential"] = $Credential }
            
            try {
                $compSys = Get-DbaCmObject @splatDbaCmObject -Query "SELECT * FROM win32_computersystem"
                if (-not $CompSys.automaticmanagedpagefile) {
                    $pagefiles = Get-DbaCmObject @splatDbaCmObject -Query "SELECT * FROM win32_pagefile"
                    $pagefileUsages = Get-DbaCmObject @splatDbaCmObject -Query "SELECT * FROM win32_pagefileUsage"
                    $pagefileSettings = Get-DbaCmObject @splatDbaCmObject -Query "SELECT * FROM win32_pagefileSetting"
                }
            }
            catch {
                Stop-Function -Message "Failed to retrieve information from $($computer.ComputerName)" -ErrorRecord $_ -Target $computer -Continue
            }
            
            if (-not $CompSys.automaticmanagedpagefile) {
                foreach ($file in $pagefiles) {
                    $settings = $pagefileSettings | Where-Object Name -EQ $file.Name
                    $usage = $pagefileUsages | Where-Object Name -EQ $file.Name
                    
                    # pagefile is not automatic managed, so return settings
                    New-Object Sqlcollaborative.Dbatools.Computer.PageFileSetting -Property @{
                        ComputerName          = $computer.ComputerName
                        AutoPageFile          = $CompSys.automaticmanagedpagefile
                        FileName              = $file.name
                        Status                = $file.status
                        SystemManaged         = ($settings.InitialSize -eq 0) -and ($settings.MaximumSize -eq 0)
                        LastModified          = $file.LastModified
                        LastAccessed          = $file.LastAccessed
                        AllocatedBaseSize     = $usage.AllocatedBaseSize # in MB, between Initial and Maximum Size
                        InitialSize           = $settings.InitialSize # in MB
                        MaximumSize           = $settings.MaximumSize # in MB
                        PeakUsage             = $usage.peakusage # in MB
                        CurrentUsage          = $usage.currentusage # in MB
                    }
                }
            }
            else {
                # pagefile is automatic managed, so there are no settings
                New-Object Sqlcollaborative.Dbatools.Computer.PageFileSetting -Property @{
                    ComputerName          = $computer
                    AutoPageFile          = $CompSys.automaticmanagedpagefile
                    FileName              = $null
                    Status                = $null
                    SystemManaged         = $null
                    LastModified          = $null
                    LastAccessed          = $null
                    AllocatedBaseSize     = $null
                    InitialSize           = $null
                    MaximumSize           = $null
                    PeakUsage             = $null
                    CurrentUsage          = $null
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaPermission.ps1
function Get-DbaPermission {
    <#
        .SYNOPSIS
            Get a list of Server and Database level permissions

        .DESCRIPTION
            Retrieves a list of permissions

            Permissions link principals to securables.
            Principals exist on Windows, Instance and Database level.
            Securables exist on Instance and Database level.
            A permission state can be GRANT, DENY or REVOKE.
            The permission type can be SELECT, CONNECT, EXECUTE and more.

            See https://msdn.microsoft.com/en-us/library/ms191291.aspx for more information

        .PARAMETER SqlInstance
            The SQL Server instance to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies one or more database(s) to process. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies one or more database(s) to exclude from processing.

        .PARAMETER IncludeServerLevel
            If this switch is enabled, information about Server Level Permissions will be output.

        .PARAMETER NoSystemObjects
            If this switch is enabled, permissions on system securables will be excluded.

        .PARAMETER EnableException
            If this switch is enabled exceptions will be thrown to the caller, which will need to perform its own exception processing. Otherwise, the function will try to catch the exception, interpret it and provide a friendly error message.

        .NOTES
            Tags: Permissions, Databases
            Author: Klaas Vandenberghe ( @PowerDBAKlaas )

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaPermission

        .EXAMPLE
            Get-DbaPermission -SqlInstance ServerA\sql987

            Returns a custom object with Server name, Database name, permission state, permission type, grantee and securable.

        .EXAMPLE
            Get-DbaPermission -SqlInstance ServerA\sql987 | Format-Table -AutoSize

            Returns a formatted table displaying Server, Database, permission state, permission type, grantee, granteetype, securable and securabletype.

        .EXAMPLE
            Get-DbaPermission -SqlInstance ServerA\sql987 -NoSystemObjects -IncludeServerLevel

            Returns a custom object with Server name, Database name, permission state, permission type, grantee and securable
            in all databases and on the server level, but not on system securables.

        .EXAMPLE
            Get-DbaPermission -SqlInstance sql2016 -Database master

            Returns a custom object with permissions for the master database.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$IncludeServerLevel,
        [switch]$NoSystemObjects,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        if ($NoSystemObjects) {
            $ExcludeSystemObjectssql = "WHERE major_id > 0 "
        }

        $ServPermsql = "SELECT SERVERPROPERTY('MachineName') AS ComputerName,
                       ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
                       SERVERPROPERTY('ServerName') AS SqlInstance
                        , [Database] = ''
                        , [PermState] = state_desc
                        , [PermissionName] = permission_name
                        , [SecurableType] = COALESCE(o.type_desc,sp.class_desc)
                        , [Securable] = CASE	WHEN class = 100 THEN @@SERVERNAME
                                                WHEN class = 105 THEN OBJECT_NAME(major_id)
                                                ELSE OBJECT_NAME(major_id)
                                                END
                        , [Grantee] = SUSER_NAME(grantee_principal_id)
                        , [GranteeType] = pr.type_desc
                        , [revokeStatement] = 'REVOKE ' + permission_name + ' ' + COALESCE(OBJECT_NAME(major_id),'') + ' FROM [' + SUSER_NAME(grantee_principal_id) + ']'
                        , [grantStatement] = 'GRANT ' + permission_name + ' ' + COALESCE(OBJECT_NAME(major_id),'') + ' TO [' + SUSER_NAME(grantee_principal_id) + ']'
                    FROM sys.server_permissions sp
                        JOIN sys.server_principals pr ON pr.principal_id = sp.grantee_principal_id
                        LEFT OUTER JOIN sys.all_objects o ON o.object_id = sp.major_id

                    $ExcludeSystemObjectssql

                    UNION ALL
                    SELECT	  SERVERPROPERTY('MachineName') AS ComputerName
                            , ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName
                            , SERVERPROPERTY('ServerName') AS SqlInstance
                            , [database] = ''
                            , [PermState] = 'GRANT'
                            , [PermissionName] = pb.[permission_name]
                            , [SecurableType] = pb.class_desc
                            , [Securable] = @@SERVERNAME
                            , [Grantee] = spr.name
                            , [GranteeType] = spr.type_desc
                            , [revokestatement] = ''
                            , [grantstatement] = ''
                    FROM sys.server_principals AS spr
                    INNER JOIN sys.fn_builtin_permissions('SERVER') AS pb ON
                        spr.[name]='bulkadmin' AND pb.[permission_name]='ADMINISTER BULK OPERATIONS'
                        OR
                        spr.[name]='dbcreator' AND pb.[permission_name]='CREATE ANY DATABASE'
                        OR
                        spr.[name]='diskadmin' AND pb.[permission_name]='ALTER RESOURCES'
                        OR
                        spr.[name]='processadmin' AND pb.[permission_name] IN ('ALTER ANY CONNECTION', 'ALTER SERVER STATE')
                        OR
                        spr.[name]='sysadmin' AND pb.[permission_name]='CONTROL SERVER'
                        OR
                        spr.[name]='securityadmin' AND pb.[permission_name]='ALTER ANY LOGIN'
                        OR
                        spr.[name]='serveradmin'  AND pb.[permission_name] IN ('ALTER ANY ENDPOINT', 'ALTER RESOURCES','ALTER SERVER STATE', 'ALTER SETTINGS','SHUTDOWN', 'VIEW SERVER STATE')
                        OR
                        spr.[name]='setupadmin' AND pb.[permission_name]='ALTER ANY LINKED SERVER'
                    WHERE spr.[type]='R'
                    ;"

        $DBPermsql = "SELECT SERVERPROPERTY('MachineName') AS ComputerName,
                    ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
                    SERVERPROPERTY('ServerName') AS SqlInstance
                    , [Database] = DB_NAME()
                    , [PermState] = state_desc
                    , [PermissionName] = permission_name
                    , [SecurableType] = COALESCE(o.type_desc,dp.class_desc)
                    , [Securable] = CASE	WHEN class = 0 THEN DB_NAME()
                                            WHEN class = 1 THEN ISNULL(s.name + '.','')+OBJECT_NAME(major_id)
                                            WHEN class = 3 THEN SCHEMA_NAME(major_id)
                                            WHEN class = 6 THEN SCHEMA_NAME(t.schema_id)+'.' + t.name
                                            END
                    , [Grantee] = USER_NAME(grantee_principal_id)
                    , [GranteeType] = pr.type_desc
                    , [revokeStatement] = 'REVOKE ' + permission_name + ' ON ' + isnull(schema_name(o.object_id)+'.','')+OBJECT_NAME(major_id)+ ' FROM [' + USER_NAME(grantee_principal_id) + ']'
                    , [grantStatement] = 'GRANT ' + permission_name + ' ON ' + isnull(schema_name(o.object_id)+'.','')+OBJECT_NAME(major_id)+ ' TO [' + USER_NAME(grantee_principal_id) + ']'
                FROM sys.database_permissions dp
                    JOIN sys.database_principals pr ON pr.principal_id = dp.grantee_principal_id
                    LEFT OUTER JOIN sys.all_objects o ON o.object_id = dp.major_id
                    LEFT OUTER JOIN sys.schemas s ON s.schema_id = o.schema_id
                    LEFT OUTER JOIN sys.types t on t.user_type_id = dp.major_id

                $ExcludeSystemObjectssql

                UNION ALL
                SELECT	  SERVERPROPERTY('MachineName') AS ComputerName
                        , ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName
                        , SERVERPROPERTY('ServerName') AS SqlInstance
                        , [database] = DB_NAME()
                        , [PermState] = ''
                        , [PermissionName] = p.[permission_name]
                        , [SecurableType] = p.class_desc
                        , [Securable] = DB_NAME()
                        , [Grantee] = dp.name
                        , [GranteeType] = dp.type_desc
                        , [revokestatement] = ''
                        , [grantstatement] = ''
                FROM sys.database_principals AS dp
                INNER JOIN sys.fn_builtin_permissions('DATABASE') AS p ON
                    dp.[name]='db_accessadmin' AND p.[permission_name] IN ('ALTER ANY USER', 'CREATE SCHEMA')
                    OR
                    dp.[name]='db_backupoperator' AND p.[permission_name] IN ('BACKUP DATABASE', 'BACKUP LOG', 'CHECKPOINT')
                    OR
                    dp.[name] IN ('db_datareader', 'db_denydatareader') AND p.[permission_name]='SELECT'
                    OR
                    dp.[name] IN ('db_datawriter', 'db_denydatawriter') AND p.[permission_name] IN ('INSERT', 'DELETE', 'UPDATE')
                    OR
                    dp.[name]='db_ddladmin' AND
                    p.[permission_name] IN ('ALTER ANY ASSEMBLY', 'ALTER ANY ASYMMETRIC KEY',
                                            'ALTER ANY CERTIFICATE', 'ALTER ANY CONTRACT',
                                            'ALTER ANY DATABASE DDL TRIGGER', 'ALTER ANY DATABASE EVENT',
                                            'NOTIFICATION', 'ALTER ANY DATASPACE', 'ALTER ANY FULLTEXT CATALOG',
                                            'ALTER ANY MESSAGE TYPE', 'ALTER ANY REMOTE SERVICE BINDING',
                                            'ALTER ANY ROUTE', 'ALTER ANY SCHEMA', 'ALTER ANY SERVICE',
                                            'ALTER ANY SYMMETRIC KEY', 'CHECKPOINT', 'CREATE AGGREGATE',
                                            'CREATE DEFAULT', 'CREATE FUNCTION', 'CREATE PROCEDURE',
                                            'CREATE QUEUE', 'CREATE RULE', 'CREATE SYNONYM', 'CREATE TABLE',
                                            'CREATE TYPE', 'CREATE VIEW', 'CREATE XML SCHEMA COLLECTION',
                                            'REFERENCES')
                    OR
                    dp.[name]='db_owner' AND p.[permission_name]='CONTROL'
                    OR
                    dp.[name]='db_securityadmin' AND p.[permission_name] IN ('ALTER ANY APPLICATION ROLE', 'ALTER ANY ROLE', 'CREATE SCHEMA', 'VIEW DEFINITION')

                WHERE dp.[type]='R'
                    AND dp.is_fixed_role=1
                ;"
    }

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance."

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($IncludeServerLevel) {
                Write-Message -Level Debug -Message "T-SQL: $ServPermsql"
                $server.Query($ServPermsql)
            }

            $dbs = $server.Databases

            if ($Database) {
                $dbs = $dbs | Where-Object Name -In $Database
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $dbs) {
                Write-Message -Level Verbose -Message "Processing $db on $instance."

                if ($db.IsAccessible -eq $false) {
                    Write-Warning "The database $db is not accessible. Skipping database."
                    Continue
                }

                Write-Message -Level Debug -Message "T-SQL: $DBPermsql"
                $db.ExecuteWithResults($DBPermsql).Tables.Rows
            }
        }
    }
}
tools\dbatools\functions\Get-DbaPfAvailableCounter.ps1
function Get-DbaPfAvailableCounter {
    <#
        .SYNOPSIS
            Gathers list of all available counters on local or remote machines.

        .DESCRIPTION
            Gathers list of all available counters on local or remote machines. Note, if you pass a credential object, it will be included in the output for easy reuse in your next piped command.

            Thanks to Daniel Streefkerk for this super fast way of counters
            https://daniel.streefkerkonline.com/2016/02/18/use-powershell-to-list-all-windows-performance-counters-and-their-numeric-ids

        .PARAMETER ComputerName
            The target computer. Defaults to localhost.

        .PARAMETER Credential
            Allows you to login to servers using alternative credentials. To use:

            $scred = Get-Credential, then pass $scred object to the -Credential parameter.

        .PARAMETER Pattern
            Specify a pattern for filtering.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Performance, DataCollector, PerfCounter
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaPfAvailableCounter

        .EXAMPLE
            Get-DbaPfAvailableCounter

            Gets all available counters on the local machine.

        .EXAMPLE
            Get-DbaPfAvailableCounter -Pattern *sql*

            Gets all counters matching sql on the local machine.

        .EXAMPLE
            Get-DbaPfAvailableCounter -ComputerName sql2017 -Pattern *sql*

            Gets all counters matching sql on the remote server sql2017.

        .EXAMPLE
            Get-DbaPfAvailableCounter -Pattern *sql*

            Gets all counters matching sql on the local machine.

        .EXAMPLE
            Get-DbaPfAvailableCounter -Pattern *sql* | Add-DbaPfDataCollectorCounter -CollectorSet 'Test Collector Set' -Collector DataCollector01

           Adds all counters matching "sql" to the DataCollector01 within the 'Test Collector Set' CollectorSet.
    #>
    [CmdletBinding()]
    param (
        [DbaInstance[]]$ComputerName = $env:ComputerName,
        [PSCredential]$Credential,
        [string]$Pattern,
        [switch]$EnableException
    )
    begin {
        $scriptblock = {
            $counters = Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\009' -Name 'counter' | Select-Object -ExpandProperty Counter |
                Where-Object { $_ -notmatch '[0-90000]' } | Sort-Object | Get-Unique

            foreach ($counter in $counters) {
                [pscustomobject]@{
                    ComputerName = $env:COMPUTERNAME
                    Name         = $counter
                    Credential   = $args
                }
            }
        }

        # In case people really want a "like" search, which is slower
        $Pattern = $Pattern.Replace("*", ".*").Replace("..*", ".*")
    }
    process {
        foreach ($computer in $ComputerName) {
            Write-Message -Level Verbose -Message "Connecting to $computer using Invoke-Command."

            try {
                if ($pattern) {
                    Invoke-Command2 -ComputerName $computer -Credential $Credential -ScriptBlock $scriptblock -ArgumentList $credential -ErrorAction Stop |
                        Where-Object Name -match $pattern | Select-DefaultView -ExcludeProperty Credential
                }
                else {
                    Invoke-Command2 -ComputerName $computer -Credential $Credential -ScriptBlock $scriptblock -ArgumentList $credential -ErrorAction Stop |
                        Select-DefaultView -ExcludeProperty Credential
                }
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_ -Target $computer -Continue
            }
        }
    }
}
tools\dbatools\functions\Get-DbaPfDataCollector.ps1
function Get-DbaPfDataCollector {
    <#
        .SYNOPSIS
            Gets Performance Monitor Data Collectors.

        .DESCRIPTION
           Gets Performance Monitor Data Collectors.

        .PARAMETER ComputerName
            The target computer. Defaults to localhost.

        .PARAMETER Credential
            Allows you to login to servers using alternative credentials. To use:

            $scred = Get-Credential, then pass $scred object to the -Credential parameter.

        .PARAMETER CollectorSet
            The Collector Set name.

        .PARAMETER Collector
            The Collector name.

        .PARAMETER InputObject
            Accepts the object output by Get-DbaPfDataCollectorSet via the pipeline.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: PerfMon

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaPfDataCollector

        .EXAMPLE
            Get-DbaPfDataCollector

            Gets all Collectors on localhost.

        .EXAMPLE
            Get-DbaPfDataCollector -ComputerName sql2017

            Gets all Collectors on sql2017.

        .EXAMPLE
            Get-DbaPfDataCollector -ComputerName sql2017, sql2016 -Credential (Get-Credential) -CollectorSet 'System Correlation'

            Gets all Collectors for the 'System Correlation' CollectorSet on sql2017 and sql2016 using alternative credentials.

        .EXAMPLE
            Get-DbaPfDataCollectorSet -CollectorSet 'System Correlation' | Get-DbaPfDataCollector

            Gets all Collectors for the 'System Correlation' CollectorSet.
    #>
    [CmdletBinding()]
    param (
        [DbaInstance[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias("DataCollectorSet")]
        [string[]]$CollectorSet,
        [Alias("DataCollector")]
        [string[]]$Collector,
        [parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [switch]$EnableException
    )
    begin {
        $columns = 'ComputerName', 'DataCollectorSet', 'Name', 'DataCollectorType', 'DataSourceName', 'FileName', 'FileNameFormat', 'FileNameFormatPattern', 'LatestOutputLocation', 'LogAppend', 'LogCircular', 'LogFileFormat', 'LogOverwrite', 'SampleInterval', 'SegmentMaxRecords', 'Counters'
    }
    process {
        if ($InputObject.Credential -and (Test-Bound -ParameterName Credential -Not)) {
            $Credential = $InputObject.Credential
        }

        if (-not $InputObject -or ($InputObject -and (Test-Bound -ParameterName ComputerName))) {
            foreach ($computer in $ComputerName) {
                $InputObject += Get-DbaPfDataCollectorSet -ComputerName $computer -Credential $Credential -CollectorSet $CollectorSet
            }
        }

        if ($InputObject) {
            if (-not $InputObject.DataCollectorSetObject) {
                Stop-Function -Message "InputObject is not of the right type. Please use Get-DbaPfDataCollectorSet."
                return
            }
        }

        foreach ($set in $InputObject) {
            $collectorxml = ([xml]$set.Xml).DataCollectorSet.PerformanceCounterDataCollector
            foreach ($col in $collectorxml) {
                if ($Collector -and $Collector -notcontains $col.Name) {
                    continue
                }

                $outputlocation = $col.LatestOutputLocation
                if ($outputlocation) {
                    $dir = ($outputlocation).Replace(':', '$')
                    $remote = "\\$($set.ComputerName)\$dir"
                }
                else {
                    $remote = $null
                }

                [pscustomobject]@{
                    ComputerName               = $set.ComputerName
                    DataCollectorSet           = $set.Name
                    Name                       = $col.Name
                    FileName                   = $col.FileName
                    DataCollectorType          = $col.DataCollectorType
                    FileNameFormat             = $col.FileNameFormat
                    FileNameFormatPattern      = $col.FileNameFormatPattern
                    LogAppend                  = $col.LogAppend
                    LogCircular                = $col.LogCircular
                    LogOverwrite               = $col.LogOverwrite
                    LatestOutputLocation       = $col.LatestOutputLocation
                    DataCollectorSetXml        = $set.Xml
                    RemoteLatestOutputLocation = $remote
                    DataSourceName             = $col.DataSourceName
                    SampleInterval             = $col.SampleInterval
                    SegmentMaxRecords          = $col.SegmentMaxRecords
                    LogFileFormat              = $col.LogFileFormat
                    Counters                   = $col.Counter
                    CounterDisplayNames        = $col.CounterDisplayName
                    CollectorXml               = $col
                    DataCollectorObject        = $true
                    Credential                 = $Credential
                } | Select-DefaultView -Property $columns
            }
        }
    }
}
tools\dbatools\functions\Get-DbaPfDataCollectorCounter.ps1
function Get-DbaPfDataCollectorCounter {
    <#
        .SYNOPSIS
            Gets Performance Counters.

        .DESCRIPTION
            Gets Performance Counters.

        .PARAMETER ComputerName
            The target computer. Defaults to localhost.

        .PARAMETER Credential
            Allows you to login to servers using alternative credentials. To use:

            $scred = Get-Credential, then pass $scred object to the -Credential parameter.

        .PARAMETER CollectorSet
            The Collector Set name.
  
        .PARAMETER Collector
            The Collector name.
   
        .PARAMETER Counter
            The Counter name to capture. This must be in the form of '\Processor(_Total)\% Processor Time'.
    
        .PARAMETER InputObject
            Accepts the object output by Get-DbaPfDataCollectorSet via the pipeline.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
    
        .NOTES
            Tags: PerfMon

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
    
        .LINK
            https://dbatools.io/Get-DbaPfDataCollectorCounter

        .EXAMPLE
            Get-DbaPfDataCollectorCounter
    
            Gets all counters for all Collector Sets on localhost.

        .EXAMPLE
            Get-DbaPfDataCollectorCounter -ComputerName sql2017
    
            Gets all counters for all Collector Sets on  on sql2017.
    
        .EXAMPLE
            Get-DbaPfDataCollectorCounter -ComputerName sql2017 -Counter '\Processor(_Total)\% Processor Time'

            Gets the '\Processor(_Total)\% Processor Time' counter on sql2017.
    
        .EXAMPLE
            Get-DbaPfDataCollectorCounter -ComputerName sql2017, sql2016 -Credential (Get-Credential) -CollectorSet 'System Correlation'
    
            Gets all counters for the 'System Correlation' CollectorSet on sql2017 and sql2016 using alternative credentials.
    
        .EXAMPLE
            Get-DbaPfDataCollectorSet -CollectorSet 'System Correlation' | Get-DbaPfDataCollector | Get-DbaPfDataCollectorCounter
    
            Gets all counters for the 'System Correlation' CollectorSet.
    #>
    [CmdletBinding()]
    param (
        [DbaInstance[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias("DataCollectorSet")]
        [string[]]$CollectorSet,
        [Alias("DataCollector")]
        [string[]]$Collector,
        [string[]]$Counter,
        [parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [switch]$EnableException
    )
    begin {
        $columns = 'ComputerName', 'Name', 'DataCollectorSet', 'Counters', 'DataCollectorType', 'DataSourceName', 'FileName', 'FileNameFormat', 'FileNameFormatPattern', 'LatestOutputLocation', 'LogAppend', 'LogCircular', 'LogFileFormat', 'LogOverwrite', 'SampleInterval', 'SegmentMaxRecords'
    }
    process {
        if ($InputObject.Credential -and (Test-Bound -ParameterName Credential -Not)) {
            $Credential = $InputObject.Credential
        }
        
        if (-not $InputObject -or ($InputObject -and (Test-Bound -ParameterName ComputerName))) {
            foreach ($computer in $ComputerName) {
                $InputObject += Get-DbaPfDataCollector -ComputerName $computer -Credential $Credential -CollectorSet $CollectorSet -Collector $Collector
            }
        }
        
        if ($InputObject) {
            if (-not $InputObject.DataCollectorObject) {
                Stop-Function -Message "InputObject is not of the right type. Please use Get-DbaPfDataCollector."
                return
            }
        }
        
        foreach ($counterobject in $InputObject) {
            foreach ($countername in $counterobject.Counters) {
                if ($Counter -and $Counter -notcontains $countername) { continue }
                [pscustomobject]@{
                    ComputerName        = $counterobject.ComputerName
                    DataCollectorSet    = $counterobject.DataCollectorSet
                    DataCollector       = $counterobject.Name
                    DataCollectorSetXml = $counterobject.DataCollectorSetXml
                    Name                = $countername
                    FileName            = $counterobject.FileName
                    CounterObject       = $true
                    Credential          = $Credential
                } | Select-DefaultView -ExcludeProperty DataCollectorObject, Credential, CounterObject, DataCollectorSetXml
            }
        }
    }
}
tools\dbatools\functions\Get-DbaPfDataCollectorCounterSample.ps1
function Get-DbaPfDataCollectorCounterSample {
    <#
        .SYNOPSIS
            Gets Performance Counter Samples.

        .DESCRIPTION
            Gets Performance Counter Samples.

        .PARAMETER ComputerName
            The target computer. Defaults to localhost.

        .PARAMETER Credential
            Allows you to login to servers using alternative credentials. To use:

            $scred = Get-Credential, then pass $scred object to the -Credential parameter.

        .PARAMETER CollectorSet
            The Collector Set name.

        .PARAMETER Collector
            The Collector name.

        .PARAMETER Counter
            The Counter name. This must be in the form of '\Processor(_Total)\% Processor Time'.

        .PARAMETER Continuous
           If this switch is enabled, samples will be retrieved continuously until you press CTRL+C. By default, this command gets only one counter sample. You can use the SampleInterval parameter to set the interval for continuous sampling.

        .PARAMETER ListSet
            Gets the specified performance counter sets on the computers. Enter the names of the counter sets. Wildcards are permitted.

        .PARAMETER MaxSamples
            Specifies the number of samples to get from each counter. The default is 1 sample. To get samples continuously (no maximum sample size), use the Continuous parameter.

            To collect a very large data set, consider running a Get-DbaPfDataCollectorCounterSample command as a Windows PowerShell background job.

        .PARAMETER SampleInterval
            Specifies the time between samples in seconds. The minimum value and the default value are 1 second.

        .PARAMETER InputObject
            Accepts the object output by Get-DbaPfDataCollectorCounter via the pipeline.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: PerfMon

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaPfDataCollectorCounterSample

        .EXAMPLE
            Get-DbaPfDataCollectorCounterSample

            Gets a single sample for all counters for all Collector Sets on localhost.

        .EXAMPLE
            Get-DbaPfDataCollectorCounterSample -Counter '\Processor(_Total)\% Processor Time'

            Gets a single sample for all counters for all Collector Sets on localhost.

        .EXAMPLE
            Get-DbaPfDataCollectorCounter -ComputerName sql2017, sql2016 | Out-GridView -PassThru | Get-DbaPfDataCollectorCounterSample -MaxSamples 10

            Gets 10 samples for all counters for all Collector Sets for servers sql2016 and sql2017.

        .EXAMPLE
            Get-DbaPfDataCollectorCounterSample -ComputerName sql2017

            Gets a single sample for all counters for all Collector Sets on sql2017.

        .EXAMPLE
            Get-DbaPfDataCollectorCounterSample -ComputerName sql2017, sql2016 -Credential (Get-Credential) -CollectorSet 'System Correlation'

            Gets a single sample for all counters for the 'System Correlation' CollectorSet on sql2017 and sql2016 using alternative credentials.

        .EXAMPLE
            Get-DbaPfDataCollectorCounterSample -CollectorSet 'System Correlation'

            Gets a single sample for all counters for the 'System Correlation' CollectorSet.
    #>
    [CmdletBinding()]
    param (
        [DbaInstance[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias("DataCollectorSet")]
        [string[]]$CollectorSet,
        [Alias("DataCollector")]
        [string[]]$Collector,
        [string[]]$Counter,
        [switch]$Continuous,
        [switch[]]$ListSet,
        [int]$MaxSamples,
        [int]$SampleInterval,
        [parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [switch]$EnableException
    )
    process {
        
        if ($InputObject.Credential -and (Test-Bound -ParameterName Credential -Not)) {
            $Credential = $InputObject.Credential
        }
        
        if ($InputObject.Counter -and (Test-Bound -ParameterName Counter -Not)) {
            $Counter = $InputObject.Counter
        }
        
        if (-not $InputObject -or ($InputObject -and (Test-Bound -ParameterName ComputerName))) {
            foreach ($computer in $ComputerName) {
                $InputObject += Get-DbaPfDataCollectorCounter -ComputerName $computer -Credential $Credential -CollectorSet $CollectorSet -Collector $Collector
            }
        }
        
        if ($InputObject) {
            if (-not $InputObject.CounterObject) {
                Stop-Function -Message "InputObject is not of the right type. Please use Get-DbaPfDataCollectorCounter."
                return
            }
        }
        
        foreach ($counterobject in $InputObject) {
            if ((Test-Bound -ParameterName Counter) -and ($Counter -notcontains $counterobject.Name)) { continue }
            $params = @{
                Counter = $counterobject.Name
            }
            
            if (-not ([dbainstance]$counterobject.ComputerName).IsLocalHost) {
                $params.Add("ComputerName", $counterobject.ComputerName)
            }
            
            if ($Credential) {
                $params.Add("Credential", $Credential)
            }
            
            if ($Continuous) {
                $params.Add("Continuous", $Continuous)
            }
            
            if ($ListSet) {
                $params.Add("ListSet", $ListSet)
            }
            
            if ($MaxSamples) {
                $params.Add("MaxSamples", $MaxSamples)
            }
            
            if ($SampleInterval) {
                $params.Add("SampleInterval", $SampleInterval)
            }
            
            if ($Continuous) {
                Get-Counter @params
            }
            else {
                try {
                    $pscounters = Get-Counter @params -ErrorAction Stop
                }
                catch {
                    Stop-Function -Message "Failure for $($counterobject.Name) on $($counterobject.ComputerName)." -ErrorRecord $_ -Continue
                }
                
                foreach ($pscounter in $pscounters) {
                    foreach ($sample in $pscounter.CounterSamples) {
                        [pscustomobject]@{
                            ComputerName           = $counterobject.ComputerName
                            DataCollectorSet       = $counterobject.DataCollectorSet
                            DataCollector          = $counterobject.DataCollector
                            Name                   = $counterobject.Name
                            Timestamp              = $pscounter.Timestamp
                            Path                   = $sample.Path
                            InstanceName           = $sample.InstanceName
                            CookedValue            = $sample.CookedValue
                            RawValue               = $sample.RawValue
                            SecondValue            = $sample.SecondValue
                            MultipleCount          = $sample.MultipleCount
                            CounterType            = $sample.CounterType
                            SampleTimestamp        = $sample.Timestamp
                            SampleTimestamp100NSec = $sample.Timestamp100NSec
                            Status                 = $sample.Status
                            DefaultScale           = $sample.DefaultScale
                            TimeBase               = $sample.TimeBase
                            Sample                 = $pscounter.CounterSamples
                            CounterSampleObject    = $true
                        } | Select-DefaultView -ExcludeProperty Sample, CounterSampleObject
                    }
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaPfDataCollectorSet.ps1
function Get-DbaPfDataCollectorSet {
    <#
        .SYNOPSIS
            Gets Performance Monitor Data Collector Set.

        .DESCRIPTION
            Gets Performance Monitor Data Collector Set.

        .PARAMETER ComputerName
            The target computer. Defaults to localhost.

        .PARAMETER Credential
            Allows you to login to servers using alternative credentials. To use:

            $scred = Get-Credential, then pass $scred object to the -Credential parameter.

        .PARAMETER CollectorSet
            The Collector set name.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: PerfMon

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaPfDataCollectorSet

        .EXAMPLE
            Get-DbaPfDataCollectorSet

            Gets all Collector Sets on localhost.

        .EXAMPLE
            Get-DbaPfDataCollectorSet -ComputerName sql2017

            Gets all Collector Sets on sql2017.

        .EXAMPLE
            Get-DbaPfDataCollectorSet -ComputerName sql2017 -Credential (Get-Credential) -CollectorSet 'System Correlation'

            Gets the 'System Correlation' CollectorSet on sql2017 using alternative credentials.

        .EXAMPLE
            Get-DbaPfDataCollectorSet | Select *

            Displays extra columns and also exposes the original COM object in DataCollectorSetObject.
    #>
    [CmdletBinding()]
    param (
        [parameter(ValueFromPipeline)]
        [DbaInstance[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias("DataCollectorSet")]
        [string[]]$CollectorSet,
        [switch]$EnableException
    )

    begin {
        $setscript = {
            # Get names / status info
            $schedule = New-Object -ComObject "Schedule.Service"
            $schedule.Connect()
            $folder = $schedule.GetFolder("Microsoft\Windows\PLA")
            $tasks = @()
            $tasknumber = 0
            $done = $false
            do {
                try {
                    $task = $folder.GetTasks($tasknumber)
                    $tasknumber++
                    if ($task) {
                        $tasks += $task
                    }
                }
                catch {
                    $done = $true
                }
            }
            while ($done -eq $false)
            $null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($schedule)

            if ($args[0]) {
                $tasks = $tasks | Where-Object Name -in $args[0]
            }

            $sets = New-Object -ComObject Pla.DataCollectorSet
            foreach ($task in $tasks) {
                $setname = $task.Name
                switch ($task.State) {
                    0 { $state = "Unknown" }
                    1 { $state = "Disabled" }
                    2 { $state = "Queued" }
                    3 { $state = "Ready" }
                    4 { $state = "Running" }
                }

                try {
                    # Query changes $sets so work from there
                    $sets.Query($setname, $null)
                    $set = $sets.PSObject.Copy()

                    $outputlocation = $set.OutputLocation
                    $latestoutputlocation = $set.LatestOutputLocation

                    if ($outputlocation) {
                        $dir = (Split-Path $outputlocation).Replace(':', '$')
                        $remote = "\\$env:COMPUTERNAME\$dir"
                    }
                    else {
                        $remote = $null
                    }

                    if ($latestoutputlocation) {
                        $dir = ($latestoutputlocation).Replace(':', '$')
                        $remotelatest = "\\$env:COMPUTERNAME\$dir"
                    }
                    else {
                        $remote = $null
                    }

                    [pscustomobject]@{
                        ComputerName               = $env:COMPUTERNAME
                        Name                       = $setname
                        LatestOutputLocation       = $set.LatestOutputLocation
                        OutputLocation             = $set.OutputLocation
                        RemoteOutputLocation       = $remote
                        RemoteLatestOutputLocation = $remotelatest
                        RootPath                   = $set.RootPath
                        Duration                   = $set.Duration
                        Description                = $set.Description
                        DescriptionUnresolved      = $set.DescriptionUnresolved
                        DisplayName                = $set.DisplayName
                        DisplayNameUnresolved      = $set.DisplayNameUnresolved
                        Keywords                   = $set.Keywords
                        Segment                    = $set.Segment
                        SegmentMaxDuration         = $set.SegmentMaxDuration
                        SegmentMaxSize             = $set.SegmentMaxSize
                        SerialNumber               = $set.SerialNumber
                        Server                     = $set.Server
                        Status                     = $set.Status
                        Subdirectory               = $set.Subdirectory
                        SubdirectoryFormat         = $set.SubdirectoryFormat
                        SubdirectoryFormatPattern  = $set.SubdirectoryFormatPattern
                        Task                       = $set.Task
                        TaskRunAsSelf              = $set.TaskRunAsSelf
                        TaskArguments              = $set.TaskArguments
                        TaskUserTextArguments      = $set.TaskUserTextArguments
                        Schedules                  = $set.Schedules
                        SchedulesEnabled           = $set.SchedulesEnabled
                        UserAccount                = $set.UserAccount
                        Xml                        = $set.Xml
                        Security                   = $set.Security
                        StopOnCompletion           = $set.StopOnCompletion
                        State                      = $state.Trim()
                        DataCollectorSetObject     = $true
                        TaskObject                 = $task
                        Credential                 = $args[1]
                    }
                }
                catch {
                    Write-Warning -Message "Issue with getting Collector Set $setname on $env:Computername : $_."
                    continue
                }
            }
        }

        $columns = 'ComputerName', 'Name', 'DisplayName', 'Description', 'State', 'Duration', 'OutputLocation', 'LatestOutputLocation',
        'RootPath', 'SchedulesEnabled', 'Segment', 'SegmentMaxDuration', 'SegmentMaxSize',
        'SerialNumber', 'Server', 'StopOnCompletion', 'Subdirectory', 'SubdirectoryFormat',
        'SubdirectoryFormatPattern', 'Task', 'TaskArguments', 'TaskRunAsSelf', 'TaskUserTextArguments', 'UserAccount'
    }
    process {
        foreach ($computer in $ComputerName.ComputerName) {
            Write-Message -Level Verbose -Message "Connecting to $computer using Invoke-Command."
            try {
                Invoke-Command2 -ComputerName $computer -Credential $Credential -ScriptBlock $setscript -ArgumentList $CollectorSet, $Credential -ErrorAction Stop | Select-DefaultView -Property $columns
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_ -Target $computer -Continue
            }
        }
    }
}
tools\dbatools\functions\Get-DbaPfDataCollectorSetTemplate.ps1
function Get-DbaPfDataCollectorSetTemplate {
    <#
        .SYNOPSIS
            Parses Perf Monitor templates. Defaults to parsing templates in the dbatools template repository (\bin\perfmontemplates\).

        .DESCRIPTION
            Parses Perf Monitor XML templates. Defaults to parsing templates in the dbatools template repository (\bin\perfmontemplates\).

        .PARAMETER Path
            The path to the template directory. Defaults to the dbatools template repository (\bin\perfmontemplates\).

        .PARAMETER Pattern
            Specify a pattern for filtering. Alternatively, you can use Out-GridView -Passthru to select objects and pipe them to Import-DbaPfDataCollectorSetTemplate.

        .PARAMETER Template
            Specifies one or more of the templates provided by dbatools. Press tab to cycle through the list to the options.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Performance, DataCollector, PerfCounter
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaPfDataCollectorSetTemplate

        .EXAMPLE
            Get-DbaPfDataCollectorSetTemplate

            Returns information about all the templates in the local dbatools repository.

        .EXAMPLE
            Get-DbaPfDataCollectorSetTemplate | Out-GridView -PassThru | Import-DbaPfDataCollectorSetTemplate -ComputerName sql2017 | Start-DbaPfDataCollectorSet

            Allows you to select a template, then deploys it to sql2017 and immediately starts the DataCollectorSet.

        .EXAMPLE
            Get-DbaPfDataCollectorSetTemplate | Select-Object *

            Returns more information about the template, including the full path/filename.
    #>
    [CmdletBinding()]
    param (
        [string[]]$Path = "$script:PSModuleRoot\bin\perfmontemplates\collectorsets",
        [string]$Pattern,
        [string[]]$Template,
        [switch]$EnableException
    )
    begin {
        $metadata = Import-Clixml "$script:PSModuleRoot\bin\perfmontemplates\collectorsets.xml"
        # In case people really want a "like" search, which is slower
        $Pattern = $Pattern.Replace("*", ".*").Replace("..*", ".*")
    }
    process {
        foreach ($directory in $Path) {
            $files = Get-ChildItem "$directory\*.xml"

            if ($Template) {
                $files = $files | Where-Object BaseName -in $Template
            }

            foreach ($file in $files) {
                try {
                    $xml = [xml](Get-Content $file)
                }
                catch {
                    Stop-Function -Message "Failure" -ErrorRecord $_ -Target $file -Continue
                }

                foreach ($dataset in $xml.DataCollectorSet) {
                    $meta = $metadata | Where-Object Name -eq $dataset.name
                    if ($Pattern) {
                        if (
                            ($dataset.Name -match $Pattern) -or
                            ($dataset.Description -match $Pattern)
                        ) {
                            [pscustomobject]@{
                                Name        = $dataset.name
                                Source      = $meta.Source
                                UserAccount = $dataset.useraccount
                                Description = $dataset.Description
                                Path        = $file
                                File        = $file.Name
                            } | Select-DefaultView -ExcludeProperty File, Path
                        }
                    }
                    else {
                        [pscustomobject]@{
                            Name        = $dataset.name
                            Source      = $meta.Source
                            UserAccount = $dataset.useraccount
                            Description = $dataset.Description
                            Path        = $file
                            File        = $file.Name
                        } | Select-DefaultView -ExcludeProperty File, Path
                    }
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaPlanCache.ps1
function Get-DbaPlanCache {
    <#
        .SYNOPSIS
            Provides information about adhoc and prepared plan cache usage

        .DESCRIPTION
            Checks ahoc and prepared plan cache for each database, if over 100 MBS you should consider you using Remove-DbaQueryPlan to clear the plan caches or turning on optimize for adhoc workloads configuration is running 2008 or later.

            References: https://www.sqlskills.com/blogs/kimberly/plan-cache-adhoc-workloads-and-clearing-the-single-use-plan-cache-bloat/

            Note: This command returns results from all SQL server instances on the destination server but the process column is specific to -SqlInstance passed.

        .PARAMETER SqlInstance
            The target SQL Server instance.

        .PARAMETER SqlCredential
           Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Memory
            Author: Tracy Boggiano, databasesuperhero.com

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: GNU GPL v3 https://opensource.org/licenses/GPL-3.0

        .LINK
            https://dbatools.io/Get-DbaPlanCache

        .EXAMPLE
            Get-DbaPlanCache -SqlInstance sql2017

            Returns the single use plan cashe usage information for SQL Server instance 2017

        .EXAMPLE
            Get-DbaPlanCache -SqlInstance sql2017

            Returns the single use plan cashe usage information for SQL Server instance 2017

        .EXAMPLE
            Get-DbaPlanCache -SqlInstance sql2017 -SqlCredential (Get-Credential sqladmin)

            Returns the single use plan cashe usage information for SQL Server instance 2017 using login 'sqladmin'
    #>
        [CmdletBinding()]
        Param (
            [parameter(Mandatory, ValueFromPipeline)]
            [Alias("ServerInstance", "SqlServer", "SqlServers")]
            [DbaInstanceParameter[]]$SqlInstance,
            [PSCredential]$SqlCredential,
            [switch]$EnableException
        )
        begin {
            $Sql = "SELECT SERVERPROPERTY('MachineName') AS ComputerName,
        ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
        SERVERPROPERTY('ServerName') AS SqlInstance, MB = sum(cast((CASE WHEN usecounts = 1 AND objtype IN ('Adhoc', 'Prepared') THEN size_in_bytes ELSE 0 END) as decimal(12, 2))) / 1024 / 1024,
        UseCount = sum(CASE WHEN usecounts = 1 AND objtype IN ('Adhoc', 'Prepared') THEN 1 ELSE 0 END)
        FROM sys.dm_exec_cached_plans;"
        }

        process {
            foreach ($instance in $SqlInstance) {
                try {
                    Write-Message -Level Verbose -Message "Connecting to $instance"
                    $server = Connect-DbaInstance -SqlInstance $instance -SqlCredential $sqlcredential
                }
                catch {
                    Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                }

                $results = $server.Query($sql)
                $size = [dbasize]($results.MB*1024*1024)
                Add-Member -Force -InputObject $results -MemberType NoteProperty -Name Size -Value $size

                Select-DefaultView -InputObject $results -Property ComputerName, InstanceName, SqlInstance, Size, UseCount
            }
        }
    }
tools\dbatools\functions\Get-DbaPolicy.ps1
function Get-DbaPolicy {
    <#
    .SYNOPSIS
    Returns polices from policy based management from an instance.

    .DESCRIPTION
    Returns details of policies with the option to filter on Category and SystemObjects.

    .PARAMETER SqlInstance
    SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

    .PARAMETER SqlCredential
    Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

    .PARAMETER Policy
    Filters results to only show specific policy

    .PARAMETER Category
    Filters results to only show policies in the category selected

    .PARAMETER IncludeSystemObject
    By default system objects are filtered out. Use this parameter to INCLUDE them .

    .PARAMETER EnableException
    By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
    Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
    Author: Stephen Bennett (https://sqlnotesfromtheunderground.wordpress.com/)
    Tags: Policy, PoilcyBasedManagement

    Website: https://dbatools.io
    Copyright: (C) Chrissy LeMaire, [email protected]
    License: MIT https://opensource.org/licenses/MIT

    .LINK
    https://dbatools.io/Get-DbaPolicy

    .EXAMPLE
    Get-DbaPolicy -SqlInstance sql2016

    Returns all policies from sql2016 server

    .EXAMPLE
    Get-DbaPolicy -SqlInstance sql2016 -SqlCredential $cred

    Uses a credential $cred to connect and return all policies from sql2016 instance

    .EXAMPLE
    Get-DbaPolicy -SqlInstance sql2016 -Category MorningCheck

    Returns all policies from sql2016 server that part of the PolicyCategory MorningCheck
#>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [string[]]$Policy,
        [string[]]$Category,
        [switch]$IncludeSystemObject,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            try {
                $sqlStoreConnection = New-Object Microsoft.SqlServer.Management.Sdk.Sfc.SqlStoreConnection $server.ConnectionContext.SqlConnectionObject
                # DMF is the Declarative Management Framework, Policy Based Management's old name
                $store = New-Object Microsoft.SqlServer.Management.DMF.PolicyStore $sqlStoreConnection
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $server -Continue
            }

            $allpolicies = $store.Policies

            if (-not $IncludeSystemObject) {
                $allpolicies = $allpolicies | Where-Object { $_.IsSystemObject -eq 0 }
            }

            if ($Category) {
                $allpolicies = $allpolicies | Where-Object { $_.PolicyCategory -in $Category }
            }

            if ($Policy) {
                $allpolicies = $allpolicies | Where-Object { $_.Name -in $Policy }
            }

            foreach ($currentpolicy in $allpolicies) {
                Write-Message -Level Verbose -Message "Processing $currentpolicy"
                Add-Member -Force -InputObject $currentpolicy -MemberType NoteProperty ComputerName -value $server.ComputerName
                Add-Member -Force -InputObject $currentpolicy -MemberType NoteProperty InstanceName -value $server.ServiceName
                Add-Member -Force -InputObject $currentpolicy -MemberType NoteProperty SqlInstance -value $server.DomainInstanceName

                Select-DefaultView -InputObject $currentpolicy -ExcludeProperty HelpText, HelpLink, Urn, Properties, Metadata, Parent, IdentityKey, HasScript, PolicyEvaluationStarted, ConnectionProcessingStarted, TargetProcessed, ConnectionProcessingFinished, PolicyEvaluationFinished, PropertyMetadataChanged, PropertyChanged
            }
        }
    }
}
tools\dbatools\functions\Get-DbaPrivilege.ps1
function Get-DbaPrivilege {
    <#
      .SYNOPSIS
      Gets the users with local privileges on one or more computers.

      .DESCRIPTION
      Gets the users with local privileges 'Lock Pages in Memory', 'Instant File Initialization', 'Logon as Batch' on one or more computers.

      Requires Local Admin rights on destination computer(s).

      .PARAMETER ComputerName
      The SQL Server (or server in general) that you're connecting to. This command handles named instances.

      .PARAMETER Credential
      Credential object used to connect to the computer as a different user.

      .PARAMETER EnableException
      By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
      This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
      Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

      .NOTES
      Author: Klaas Vandenberghe ( @PowerDBAKlaas )
      Tags: Privilege
      Website: https://dbatools.io
      Copyright: (C) Chrissy LeMaire, [email protected]
      License: MIT https://opensource.org/licenses/MIT

    .LINK
      https://dbatools.io/Get-DbaPrivilege

      .EXAMPLE
      Get-DbaPrivilege -ComputerName sqlserver2014a

      Gets the local privileges on computer sqlserver2014a.

      .EXAMPLE
      'sql1','sql2','sql3' | Get-DbaPrivilege

      Gets the local privileges on computers sql1, sql2 and sql3.

      .EXAMPLE
      Get-DbaPrivilege -ComputerName sql1,sql2 | Out-Gridview

      Gets the local privileges on computers sql1 and sql2, and shows them in a grid view.

  #>
    [CmdletBinding()]
    Param (
        [parameter(ValueFromPipeline)]
        [Alias("cn", "host", "Server")]
        [dbainstanceparameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [switch][Alias('Silent')]
        $EnableException
    )

    begin {
        $ResolveSID = @"
    function Convert-SIDToUserName ([string] `$SID ) {
      `$objSID = New-Object System.Security.Principal.SecurityIdentifier (`"`$SID`")
      `$objUser = `$objSID.Translate( [System.Security.Principal.NTAccount])
      `$objUser.Value
    }
"@
        $ComputerName = $ComputerName.ComputerName | Select-Object -Unique
    }
    process {
        foreach ($computer in $ComputerName) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $computer"
                if (Test-PSRemoting -ComputerName $Computer) {
                    Write-Message -Level Verbose -Message "Getting Privileges on $Computer"
                    $Priv = $null
                    $Priv = Invoke-Command2 -Raw -ComputerName $computer -Credential $Credential -ScriptBlock {
                        $temp = ([System.IO.Path]::GetTempPath()).TrimEnd(""); secedit /export /cfg $temp\secpolByDbatools.cfg > $NULL;
                        Get-Content $temp\secpolByDbatools.cfg | Where-Object { $_ -match "SeBatchLogonRight" -or $_ -match 'SeManageVolumePrivilege' -or $_ -match 'SeLockMemoryPrivilege' }
                    }

                    Write-Message -Level Verbose -Message "Getting Batch Logon Privileges on $Computer"
                    $BL = Invoke-Command2 -Raw -ComputerName $computer -Credential $Credential -ArgumentList $ResolveSID -ScriptBlock {
                        Param ($ResolveSID)
                        . ([ScriptBlock]::Create($ResolveSID))
                        $temp = ([System.IO.Path]::GetTempPath()).TrimEnd("");
                        (Get-Content $temp\secpolByDbatools.cfg | Where-Object { $_ -match "SeBatchLogonRight" }).substring(20).split(",").replace("`*", "") |
                        ForEach-Object { Convert-SIDToUserName -SID $_ }
                    } -ErrorAction SilentlyContinue
                    if ($BL.count -eq 0) {
                        Write-Message -Level Verbose -Message "No users with Batch Logon Rights on $computer"
                    }

                    Write-Message -Level Verbose -Message "Getting Instant File Initialization Privileges on $Computer"
                    $ifi = Invoke-Command2 -Raw -ComputerName $computer -Credential $Credential -ArgumentList $ResolveSID -ScriptBlock {
                        Param ($ResolveSID)
                        . ([ScriptBlock]::Create($ResolveSID))
                        $temp = ([System.IO.Path]::GetTempPath()).TrimEnd("");
                        (Get-Content $temp\secpolByDbatools.cfg | Where-Object { $_ -like 'SeManageVolumePrivilege*' }).substring(26).split(",").replace("`*", "") |
                        ForEach-Object { Convert-SIDToUserName -SID $_ }
                    } -ErrorAction SilentlyContinue
                    if ($ifi.count -eq 0) {
                        Write-Message -Level Verbose -Message "No users with Instant File Initialization Rights on $computer"
                    }

                    Write-Message -Level Verbose -Message "Getting Lock Pages in Memory Privileges on $Computer"
                    $lpim = Invoke-Command2 -Raw -ComputerName $computer -Credential $Credential -ArgumentList $ResolveSID -ScriptBlock {
                        Param ($ResolveSID)
                        . ([ScriptBlock]::Create($ResolveSID))
                        $temp = ([System.IO.Path]::GetTempPath()).TrimEnd("");
                        (Get-Content $temp\secpolByDbatools.cfg | Where-Object { $_ -like 'SeLockMemoryPrivilege*' }).substring(24).split(",").replace("`*", "") |
                        ForEach-Object { Convert-SIDToUserName -SID $_ }
                    } -ErrorAction SilentlyContinue

                    if ($lpim.count -eq 0) {
                        Write-Message -Level Verbose -Message "No users with Lock Pages in Memory Rights on $computer"
                    }
                    $users = @() + $BL + $ifi + $lpim | Select-Object -Unique
                    $users | ForEach-Object {
                        [PSCustomObject]@{
                            ComputerName                           = $computer
                            User                                   = $_
                            LogonAsBatchPrivilege                  = $BL -contains $_
                            InstantFileInitializationPrivilege     = $ifi -contains $_
                            LockPagesInMemoryPrivilege             = $lpim -contains $_
                        }
                    }
                    Write-Message -Level Verbose -Message "Removing secpol file on $computer"
                    Invoke-Command2 -Raw -ComputerName $computer -Credential $Credential -ScriptBlock { $temp = ([System.IO.Path]::GetTempPath()).TrimEnd(""); Remove-Item $temp\secpolByDbatools.cfg -Force > $NULL }
                }
                else {
                    Write-Message -Level Warning -Message "Failed to connect to $Computer"
                }
            }
            catch {
                Stop-Function -Continue -Message "Failure" -ErrorRecord $_ -Target $computer
            }
        }
    }
}
tools\dbatools\functions\Get-DbaProcess.ps1
function Get-DbaProcess {
    <#
        .SYNOPSIS
            This command displays SQL Server processes.

        .DESCRIPTION
            This command displays processes associated with a spid, login, host, program or database.

            Thanks to Michael J Swart at https://sqlperformance.com/2017/07/sql-performance/find-database-connection-leaks for the query to get the last executed SQL statement, minutesasleep and host process ID.

        .PARAMETER SqlInstance
            The SQL Server instance to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Spid
            Specifies one or more process IDs (Spid) to be displayed. Options for this parameter are auto-populated from the server.

        .PARAMETER Login
            Specifies one or more Login names with active processes to look for. Options for this parameter are auto-populated from the server.

        .PARAMETER Hostname
            Specifies one or more hostnames with active processes to look for. Options for this parameter are auto-populated from the server.

        .PARAMETER Program
            Specifies one or more program names with active processes to look for. Options for this parameter are auto-populated from the server.

        .PARAMETER Database
            Specifies one or more databases with active processes to look for. Options for this parameter are auto-populated from the server.

        .PARAMETER ExcludeSpid
            Specifies one ore more process IDs to exclude from display. Options for this parameter are auto-populated from the server.

            This is the last filter to run, so even if a Spid matches another filter, it will be excluded by this filter.

        .PARAMETER NoSystemSpid
            If this switch is enabled, system Spids will be ignored.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Process, Session, ActivityMonitor
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaProcess

        .EXAMPLE
            Get-DbaProcess -SqlInstance sqlserver2014a -Login base\ctrlb, sa

            Shows information about the processes for base\ctrlb and sa on sqlserver2014a. Windows Authentication is used in connecting to sqlserver2014a.

        .EXAMPLE
            Get-DbaProcess -SqlInstance sqlserver2014a -SqlCredential $credential -Spid 56, 77

            Shows information about the processes for spid 56 and 57. Uses alternative (SQL or Windows) credentials to authenticate to sqlserver2014a.

        .EXAMPLE
            Get-DbaProcess -SqlInstance sqlserver2014a -Program 'Microsoft SQL Server Management Studio'

            Shows information about the processes that were created in Microsoft SQL Server Management Studio.

        .EXAMPLE
            Get-DbaProcess -SqlInstance sqlserver2014a -Host workstationx, server100

            Shows information about the processes that were initiated by hosts (computers/clients) workstationx and server 1000.
    #>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [int[]]$Spid,
        [int[]]$ExcludeSpid,
        [string[]]$Database,
        [string[]]$Login,
        [string[]]$Hostname,
        [string[]]$Program,
        [switch]$NoSystemSpid,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $sqlinstance) {

            Write-Message -Message "Connecting to $instance." -Level Verbose
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Could not connect to Sql Server instance $instance : $_" -Target $instance -ErrorRecord $_ -Continue
            }

            $sql = "SELECT datediff(minute, s.last_request_end_time, getdate()) as MinutesAsleep, s.session_id as spid, s.host_process_id as HostProcessId, t.text as Query,
                    s.login_time as LoginTime,s.client_version as ClientVersion, s.last_request_start_time as LastRequestStartTime, s.last_request_end_time as LastRequestEndTime,
                    c.net_transport as NetTransport, c.encrypt_option as EncryptOption, c.auth_scheme as AuthScheme, c.net_packet_size as NetPacketSize, c.client_net_address as ClientNetAddress
                    FROM sys.dm_exec_connections c join sys.dm_exec_sessions s on c.session_id = s.session_id cross apply sys.dm_exec_sql_text(c.most_recent_sql_handle) t"

            if ($server.VersionMajor -gt 8) {
                $results = $server.Query($sql)
            }
            else {
                $results = $null
            }

            $allsessions = @()

            $processes = $server.EnumProcesses()

            if ($Login) {
                $allsessions += $processes | Where-Object { $_.Login -in $Login -and $_.Spid -notin $allsessions.Spid }
            }

            if ($Spid) {
                $allsessions += $processes | Where-Object { ($_.Spid -in $Spid -or $_.BlockingSpid -in $Spid) -and $_.Spid -notin $allsessions.Spid }
            }

            if ($Hostname) {
                $allsessions += $processes | Where-Object { $_.Host -in $Hostname -and $_.Spid -notin $allsessions.Spid }
            }

            if ($Program) {
                $allsessions += $processes | Where-Object { $_.Program -in $Program -and $_.Spid -notin $allsessions.Spid }
            }

            if ($Database) {
                $allsessions += $processes | Where-Object { $Database -contains $_.Database -and $_.Spid -notin $allsessions.Spid }
            }

            if (Test-Bound -not 'Login', 'Spid', 'ExcludeSpid', 'Hostname', 'Program', 'Database') {
                $allsessions = $processes
            }

            if ($NoSystemSpid -eq $true) {
                $allsessions = $allsessions | Where-Object { $_.Spid -gt 50 }
            }

            if ($Exclude) {
                $allsessions = $allsessions | Where-Object { $Exclude -notcontains $_.SPID -and $_.Spid -notin $allsessions.Spid }
            }

            foreach ($session in $allsessions) {

                if ($session.Status -eq "") {
                    $status = "sleeping"
                }
                else {
                    $status = $session.Status
                }

                if ($session.Command -eq "") {
                    $command = "AWAITING COMMAND"
                }
                else {
                    $command = $session.Command
                }

                $row = $results | Where-Object { $_.Spid -eq $session.Spid }

                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name Parent -value $server
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name Status -value $status
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name Command -value $command
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name HostProcessId -value $row.HostProcessId
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name MinutesAsleep -value $row.MinutesAsleep
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name LoginTime -value $row.LoginTime
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name ClientVersion -value $row.ClientVersion
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name LastRequestStartTime -value $row.LastRequestStartTime
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name LastRequestEndTime -value $row.LastRequestEndTime
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name NetTransport -value $row.NetTransport
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name EncryptOption -value $row.EncryptOption
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name AuthScheme -value $row.AuthScheme
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name NetPacketSize -value $row.NetPacketSize
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name ClientNetAddress -value $row.ClientNetAddress
                Add-Member -Force -InputObject $session -MemberType NoteProperty -Name LastQuery -value $row.Query

                Select-DefaultView -InputObject $session -Property ComputerName, InstanceName, SqlInstance, Spid, Login, LoginTime, Host, Database, BlockingSpid, Program, Status, Command, Cpu, MemUsage, LastRequestStartTime, LastRequestEndTime, MinutesAsleep, ClientNetAddress, NetTransport, EncryptOption, AuthScheme, NetPacketSize, ClientVersion, HostProcessId, IsSystem, LastQuery
            }
        }
    }
}
tools\dbatools\functions\Get-DbaQueryExecutionTime.ps1
function Get-DbaQueryExecutionTime {
    <#
.SYNOPSIS
Displays Stored Procedures and Ad hoc queries with the highest execution times.  Works on SQL Server 2008 and above.

.DESCRIPTION
Quickly find slow query executions within a database.  Results will include stored procedures and individual SQL statements.

.PARAMETER SqlInstance
Allows you to specify a comma separated list of servers to query.

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Database
The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

.PARAMETER ExcludeDatabase
The database(s) to exclude - this list is auto-populated from the server

.PARAMETER MaxResultsPerDb
Allows you to limit the number of results returned, as many systems can have very large amounts of query plans.  Default value is 100 results.

.PARAMETER MinExecs
Allows you to limit the scope to queries that have been executed a minimum number of time. Default value is 100 executions.

.PARAMETER MinExecMs
Allows you to limit the scope to queries with a specified average execution time.  Default value is 500 (ms).

.PARAMETER NoSystemDb
Allows you to suppress output on system databases

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Query, Performance
Author: Brandon Abshire, netnerds.net

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Get-DbaQueryExecutionTime

.EXAMPLE
Get-DbaQueryExecutionTime -SqlInstance sql2008, sqlserver2012

Return the top 100 slowest stored procedures or statements for servers sql2008 and sqlserver2012.

.EXAMPLE
Get-DbaQueryExecutionTime -SqlInstance sql2008 -Database TestDB

Return the top 100 slowest stored procedures or statements on server sql2008 for only the TestDB database.

.EXAMPLE
Get-DbaQueryExecutionTime -SqlInstance sql2008 -Database TestDB -MaxResultsPerDb 100 -MinExecs 200 -MinExecMs 1000

Return the top 100 slowest stored procedures or statements on server sql2008 for only the TestDB database,
limiting results to queries with more than 200 total executions and an execution time over 1000ms or higher.
#>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [parameter(Position = 1, Mandatory = $false)]
        [int]$MaxResultsPerDb = 100,
        [parameter(Position = 2, Mandatory = $false)]
        [int]$MinExecs = 100,
        [parameter(Position = 3, Mandatory = $false)]
        [int]$MinExecMs = 500,
        [parameter(Position = 4, Mandatory = $false)]
        [switch]$NoSystemDb,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $sql = ";With StatsCTE AS
            (
                SELECT
                    DB_NAME() as DatabaseName,
                    (total_worker_time / execution_count) / 1000 AS AvgExec_ms ,
                    execution_count ,
                    max_worker_time / 1000 AS MaxExec_ms ,
                    OBJECT_NAME(object_id) as ProcName,
                    object_id,
                    type_desc,
                    cached_time,
                    last_execution_time,
                    total_worker_time / 1000 as total_worker_time_ms,
                    total_elapsed_time / 1000 as total_elapsed_time_ms,
                    OBJECT_NAME(object_id) as SQLText,
                    OBJECT_NAME(object_id) as full_statement_text
                FROM    sys.dm_exec_procedure_stats
                WHERE   database_id = DB_ID()"

        if ($MinExecs) { $sql += "`n AND execution_count >= " + $MinExecs }
        if ($MinExecMs) { $sql += "`n AND (total_worker_time / execution_count) / 1000 >= " + $MinExecMs }

        $sql += "`n UNION
            SELECT
                DB_NAME() as DatabaseName,
                ( qs.total_worker_time / qs.execution_count ) / 1000 AS AvgExec_ms ,
                qs.execution_count ,
                qs.max_worker_time / 1000 AS MaxExec_ms ,
                OBJECT_NAME(st.objectid) as ProcName,
                   st.objectid as [object_id],
                   'STATEMENT' as type_desc,
                   '1901-01-01 00:00:00' as cached_time,
                    qs.last_execution_time,
                    qs.total_worker_time / 1000 as total_worker_time_ms,
                    qs.total_elapsed_time / 1000 as total_elapsed_time_ms,
                    SUBSTRING(st.text, (qs.statement_start_offset/2)+1, 50) + '...' AS SQLText,
                    SUBSTRING(st.text, (qs.statement_start_offset/2)+1,
                        ((CASE qs.statement_end_offset
                          WHEN -1 THEN DATALENGTH(st.text)
                         ELSE qs.statement_end_offset
                         END - qs.statement_start_offset)/2) + 1) AS full_statement_text
            FROM    sys.dm_exec_query_stats qs
            CROSS APPLY sys.dm_exec_plan_attributes(qs.plan_handle) as pa
            CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) as st
            WHERE st.dbid = DB_ID() OR (pa.attribute = 'dbid' and pa.value = DB_ID())"

        if ($MinExecs) { $sql += "`n AND execution_count >= " + $MinExecs }
        if ($MinExecMs) { $sql += "`n AND (total_worker_time / execution_count) / 1000 >= " + $MinExecMs }

        if ($MaxResultsPerDb) { $sql += ")`n SELECT TOP " + $MaxResultsPerDb }
        else {
            $sql += ")
                        SELECT "
        }

        $sql += "`n     DatabaseName,
                        AvgExec_ms,
                        execution_count,
                        MaxExec_ms,
                        ProcName,
                        object_id,
                        type_desc,
                        cached_time,
                        last_execution_time,
                        total_worker_time_ms,
                        total_elapsed_time_ms,
                        SQLText,
                        full_statement_text
                    FROM StatsCTE "

        if ($MinExecs -or $MinExecMs) {
            $sql += "`n WHERE `n"

            if ($MinExecs) {
                $sql += " execution_count >= " + $MinExecs
            }

            if ($MinExecMs -gt 0 -and $MinExecs) {
                $sql += "`n AND AvgExec_ms >= " + $MinExecMs
            }
            elseif ($MinExecMs) {
                $sql += "`n AvgExecs_ms >= " + $MinExecMs
            }
        }

        $sql += "`n ORDER BY AvgExec_ms DESC"
    }
    process {
        if (!$MaxResultsPerDb -and !$MinExecs -and !$MinExecMs) {
            Write-Message -Level Warning -Message "Results may take time, depending on system resources and size of buffer cache."
            Write-Message -Level Warning -Message "Consider limiting results using -MaxResultsPerDb, -MinExecs and -MinExecMs parameters."
        }

        foreach ($instance in $SqlInstance) {
            Write--Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $dbs = $server.Databases
            if ($Database) {
                $dbs = $dbs | Where-Object Name -In $Database
            }

            if ($NoSystemDb) {
                $dbs = $dbs | Where-Object { $_.IsSystemObject -eq $false }
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $dbs) {
                Write-Message -Level Verbose -Message "Processing $db on $instance"

                if ($db.IsAccessible -eq $false) {
                    Write-Message -Level Warning -Message "The database $db is not accessible. Skipping database."
                    continue
                }

                try {
                    foreach ($row in $db.ExecuteWithResults($sql).Tables.Rows) {
                        [PSCustomObject]@{
                            ComputerName       = $server.ComputerName
                            InstanceName       = $server.ServiceName
                            SqlInstance        = $server.DomainInstanceName
                            Database           = $row.DatabaseName
                            ProcName           = $row.ProcName
                            ObjectID           = $row.object_id
                            TypeDesc           = $row.type_desc
                            Executions         = $row.Execution_Count
                            AvgExecMs          = $row.AvgExec_ms
                            MaxExecMs          = $row.MaxExec_ms
                            CachedTime         = $row.cached_time
                            LastExecTime       = $row.last_execution_time
                            TotalWorkerTimeMs  = $row.total_worker_time_ms
                            TotalElapsedTimeMs = $row.total_elapsed_time_ms
                            SQLText            = $row.SQLText
                            FullStatementText  = $row.full_statement_text
                        } | Select-DefaultView -ExcludeProperty FullStatementText
                    }
                }
                catch {
                    Stop-Function -Message "Could not process $db on $instance" -Target $db -ErrorRecord $_ -Continue
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaRegisteredServer.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaRegisteredServer {
    <#
        .SYNOPSIS
            Gets list of SQL Server objects stored in SQL Server Central Management Server (CMS).

        .DESCRIPTION
            Returns an array of servers found in the CMS.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Name
            Specifies one or more names to include. Name is the visible name in SSMS CMS interface (labeled Registered Server Name)

        .PARAMETER ServerName
            Specifies one or more server names to include. Server Name is the actual instance name (labeled Server Name)

        .PARAMETER Group
            Specifies one or more groups to include from SQL Server Central Management Server.

        .PARAMETER ExcludeGroup
            Specifies one or more Central Management Server groups to exclude.

        .PARAMETER ExcludeCmsServer
            Deprecated, now follows the Microsoft convention of not including it by default. If you'd like to include the CMS Server, use -IncludeSelf

        .PARAMETER Id
            Get server by Id(s)

        .PARAMETER IncludeSelf
            If this switch is enabled, the CMS server itself will be included in the results, along with all other Registered Servers.

        .PARAMETER ResolveNetworkName
            If this switch is enabled, the NetBIOS name and IP address(es) of each server will be returned.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.

            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.

            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Bryan Hamby (@galador)
            Tags: RegisteredServer, CMS

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaRegisteredServer

        .EXAMPLE
            Get-DbaRegisteredServer -SqlInstance sqlserver2014a

            Gets a list of servers from the CMS on sqlserver2014a, using Windows Credentials.

        .EXAMPLE
            Get-DbaRegisteredServer -SqlInstance sqlserver2014a -IncludeSelf

            Gets a list of servers from the CMS on sqlserver2014a and includes sqlserver2014a in the output results.

        .EXAMPLE
            Get-DbaRegisteredServer -SqlInstance sqlserver2014a -SqlCredential $credential | Select-Object -Unique -ExpandProperty ServerName

            Returns only the server names from the CMS on sqlserver2014a, using SQL Authentication to authenticate to the server.

        .EXAMPLE
            Get-DbaRegisteredServer -SqlInstance sqlserver2014a -Group HR, Accounting

            Gets a list of servers in the HR and Accounting groups from the CMS on sqlserver2014a.

        .EXAMPLE
            Get-DbaRegisteredServer -SqlInstance sqlserver2014a -Group HR\Development

            Returns a list of servers in the HR and sub-group Development from the CMS on sqlserver2014a.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$Name,
        [string[]]$ServerName,
        [Alias("Groups")]
        [object[]]$Group,
        [object[]]$ExcludeGroup,
        [int[]]$Id,
        [switch]$IncludeSelf,
        [switch]$ExcludeCmsServer,
        [switch]$ResolveNetworkName,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        if ($ResolveNetworkName) {
            $defaults = 'ComputerName', 'FQDN', 'IPAddress', 'Name', 'ServerName', 'Group', 'Description'
        }
        $defaults = 'Name', 'ServerName', 'Group', 'Description'
    }
    process {
        $servers = @()
        foreach ($instance in $SqlInstance) {
            if ($Group) {
                Write-Message -Level Verbose -Message "Connecting to $instance to search for $group"
                $groupservers = Get-DbaRegisteredServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Group $Group -ExcludeGroup $ExcludeGroup
                if ($groupservers) {
                    $servers += $groupservers.GetDescendantRegisteredServers()
                }
            }
            else {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                try {
                    $serverstore = Get-DbaRegisteredServerStore -SqlInstance $instance -SqlCredential $SqlCredential -EnableException
                }
                catch {
                    Stop-Function -Message "Cannot access Central Management Server '$instance'." -ErrorRecord $_ -Continue
                }
                $servers += ($serverstore.DatabaseEngineServerGroup.GetDescendantRegisteredServers())
                $serverstore.ServerConnection.Disconnect()
            }
        }
        
        if ($Name) {
            Write-Message -Level Verbose -Message "Filtering by name for $name"
            $servers = $servers | Where-Object Name -in $Name
        }

        if ($ServerName) {
            Write-Message -Level Verbose -Message "Filtering by servername for $servername"
            $servers = $servers | Where-Object ServerName -in $ServerName
        }

        if ($Id) {
            Write-Message -Level Verbose -Message "Filtering by id for $Id (1 = default/root)"
            $servers = $servers | Where-Object Id -in $Id
        }

        if ($ExcludeGroup) {
            $excluded = Get-DbaRegisteredServer $serverstore.ServerConnection.SqlConnectionObject -Group $ExcludeGroup
            Write-Message -Level Verbose -Message "Excluding $ExcludeGroup"
            $servers = $servers | Where-Object { $_.Urn.Value -notin $excluded.Urn.Value }
        }

        foreach ($server in $servers) {
            $groupname = Get-RegServerGroupReverseParse $server
            if ($groupname -eq $server.Name) {
                $groupname = $null
            }
            else {
                $groupname = ($groupname).Split("\")
                $groupname = $groupname[0 .. ($groupname.Count - 2)]
                $groupname = ($groupname -join "\")
            }


            Add-Member -Force -InputObject $server -MemberType NoteProperty -Name ComputerName -value $serverstore.ComputerName
            Add-Member -Force -InputObject $server -MemberType NoteProperty -Name InstanceName -value $serverstore.InstanceName
            Add-Member -Force -InputObject $server -MemberType NoteProperty -Name SqlInstance -value $serverstore.SqlInstance
            Add-Member -Force -InputObject $server -MemberType NoteProperty -Name Group -value $groupname
            Add-Member -Force -InputObject $server -MemberType NoteProperty -Name FQDN -Value $null
            Add-Member -Force -InputObject $server -MemberType NoteProperty -Name IPAddress -Value $null

            if ($ResolveNetworkName) {
                try {
                    $lookup = Resolve-DbaNetworkName $server.ServerName -Turbo
                    $server.ComputerName = $lookup.ComputerName
                    $server.FQDN = $lookup.FQDN
                    $server.IPAddress = $lookup.IPAddress
                }
                catch {
                    try {
                        $lookup = Resolve-DbaNetworkName $server.ServerName
                        $server.ComputerName = $lookup.ComputerName
                        $server.FQDN = $lookup.FQDN
                        $server.IPAddress = $lookup.IPAddress
                    }
                    catch { }
                }
            }
            Add-Member -Force -InputObject $server -MemberType ScriptMethod -Name ToString -Value { $this.ServerName }
            Select-DefaultView -InputObject $server -Property $defaults
        }

        if ($IncludeSelf -and $servers) {
            Write-Message -Level Verbose -Message "Adding CMS instance"
            $self = $servers[0].PsObject.Copy()
            $self | Add-Member -MemberType NoteProperty -Name Name -Value "CMS Instance" -Force
            $self.ServerName = $instance
            $self.Description = $null
            $self.SecureConnectionString = $null
            Select-DefaultView -InputObject $self -Property $defaults
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Parameter ExcludeCmsServer
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Get-DbaRegisteredServerName
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Get-SqlRegisteredServerName
    }
}
tools\dbatools\functions\Get-DbaRegisteredServerGroup.ps1
function Get-DbaRegisteredServerGroup {
    <#
        .SYNOPSIS
            Gets list of Server Groups objects stored in SQL Server Central Management Server (CMS).

        .DESCRIPTION
            Returns an array of Server Groups found in the CMS.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Group
            Specifies one or more groups to include from SQL Server Central Management Server.

        .PARAMETER ExcludeGroup
            Specifies one or more Central Management Server groups to exclude.

        .PARAMETER Id
            Get group by Id(s). This parameter only works if the group has a registered server in it.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.

            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.

            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Tony Wilhelm (@tonywsql)
            Tags: RegisteredServer, CMS

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaRegisteredServerGroup

        .EXAMPLE
            Get-DbaRegisteredServerGroup -SqlInstance sqlserver2014a

            Gets the top level groups from the CMS on sqlserver2014a, using Windows Credentials.

        .EXAMPLE
            Get-DbaRegisteredServerGroup -SqlInstance sqlserver2014a -SqlCredential $credential

            Gets the top level groups from the CMS on sqlserver2014a, using alternative credentials to authenticate to the server.

        .EXAMPLE
            Get-DbaRegisteredServerGroup -SqlInstance sqlserver2014a -Group HR, Accounting

            Gets the HR and Accounting groups from the CMS on sqlserver2014a.

        .EXAMPLE
            Get-DbaRegisteredServerGroup -SqlInstance sqlserver2014a -Group HR\Development

            Returns the sub-group Development of the HR group from the CMS on sqlserver2014a.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Group,
        [object[]]$ExcludeGroup,
        [int[]]$Id,
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Get-DbaRegisteredServerStore -SqlInstance $instance -SqlCredential $SqlCredential -EnableException
            }
            catch {
                Stop-Function -Message "Cannot access Central Management Server '$instance'" -ErrorRecord $_ -Continue
            }

            $groups = @()

            if ($group) {
                foreach ($currentgroup in $Group) {
                    Write-Message -Level Verbose -Message "Processing $currentgroup"
                    if ($currentgroup -is [Microsoft.SqlServer.Management.RegisteredServers.ServerGroup]) {
                        $currentgroup = Get-RegServerGroupReverseParse -object $currentgroup
                    }
                    
                    if ($currentgroup -match 'DatabaseEngineServerGroup\\') {
                        $currentgroup = $currentgroup.Replace('DatabaseEngineServerGroup\', '')
                    }
                    
                    if ($currentgroup -match '\\') {
                        $split = $currentgroup.Split('\\')
                        $i = 0
                        $groupobject = $server.DatabaseEngineServerGroup
                        do {
                            if ($groupobject) {
                                $groupobject = $groupobject.ServerGroups[$split[$i]]
                                Write-Message -Level Verbose -Message "Parsed $($groupobject.Name)"
                            }
                        }
                        until ($i++ -eq $split.GetUpperBound(0))
                        if ($groupobject) {
                            $groups += $groupobject
                        }
                    }
                    else {
                        try {
                            $thisgroup = $server.DatabaseEngineServerGroup.ServerGroups[$currentgroup]
                            if ($thisgroup) {
                                Write-Message -Level Verbose -Message "Added $($thisgroup.Name)"
                                $groups += $thisgroup
                            }
                        }
                        catch { }
                    }
                }
            }
            else {
                Write-Message -Level Verbose -Message "Added all root server groups"
                $groups = $server.DatabaseEngineServerGroup.ServerGroups
            }

            if ($Group -eq 'DatabaseEngineServerGroup') {
                Write-Message -Level Verbose -Message "Added root group"
                $groups = $server.DatabaseEngineServerGroup
            }

            if ($ExcludeGroup) {
                $excluded = Get-DbaRegisteredServer $server -Group $ExcludeGroup
                Write-Message -Level Verbose -Message "Excluding $ExcludeGroup"
                $groups = $groups | Where-Object { $_.Urn.Value -notin $excluded.Urn.Value }
            }

            if ($Id) {
                Write-Message -Level Verbose -Message "Filtering for id $Id. Id 1 = default."
                if ($Id -eq 1) {
                    $groups = $server.DatabaseEngineServerGroup | Where-Object Id -in $Id
                }
                else {
                    $groups = $server.DatabaseEngineServerGroup.GetDescendantRegisteredServers().Parent | Where-Object Id -in $Id
                }
            }
            $server.ServerConnection.Disconnect()
            foreach ($groupobject in $groups) {
                Add-Member -Force -InputObject $groupobject -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                Add-Member -Force -InputObject $groupobject -MemberType NoteProperty -Name InstanceName -value $server.InstanceName
                Add-Member -Force -InputObject $groupobject -MemberType NoteProperty -Name SqlInstance -value $server.SqlInstance

                Select-DefaultView -InputObject $groupobject -Property ComputerName, InstanceName, SqlInstance, Name, DisplayName, Description, ServerGroups, RegisteredServers
            }
        }
    }
}
tools\dbatools\functions\Get-DbaRegisteredServerStore.ps1
function Get-DbaRegisteredServerStore {
    <#
        .SYNOPSIS
            Returns a SQL Server Registered Server Store Object

        .DESCRIPTION
            Returns a SQL Server Registered Server Store object - useful for working with Central Management Store

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function
            to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: RegisteredServer,CMS
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaRegisteredServerStore

        .EXAMPLE
            Get-DbaRegisteredServerStore -SqlInstance sqlserver2014a

            Returns a SQL Server Registered Server Store Object from sqlserver2014a

        .EXAMPLE
            Get-DbaRegisteredServerStore -SqlInstance sqlserver2014a -SqlCredential (Get-Credential sqladmin)

            Returns a SQL Server Registered Server Store Object from sqlserver2014a  by logging in with the sqladmin login
    #>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            try {
                $store = New-Object Microsoft.SqlServer.Management.RegisteredServers.RegisteredServersStore($server.ConnectionContext.SqlConnectionObject)
            }
            catch {
                Stop-Function -Message "Cannot access Central Management Server on $instance" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            Add-Member -Force -InputObject $store -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
            Add-Member -Force -InputObject $store -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
            Add-Member -Force -InputObject $store -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName

            Select-DefaultView -InputObject $store -ExcludeProperty ServerConnection, DomainInstanceName, DomainName, Urn, Properties, Metadata, Parent, ConnectionContext, PropertyMetadataChanged, PropertyChanged
        }
    }
}
tools\dbatools\functions\Get-DbaResourceGovernorClassifierFunction.ps1
function Get-DbaResourceGovernorClassifierFunction {
<#
.SYNOPSIS
Gets the Resource Governor custom classifier Function

.DESCRIPTION
Gets the Resource Governor custom classifier Function which is used for customize the workload groups usage

.PARAMETER SqlInstance
The target SQL Server instance(s)

.PARAMETER SqlCredential
Allows you to login to SQL Server using alternative credentials

.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Migration, ResourceGovernor
Author: Alessandro Alpi (@suxstellino), alessandroalpi.blog
Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Get-DbaResourceGovernorClassifierFunction

.EXAMPLE
Get-DbaResourceGovernorClassifierFunction -SqlInstance sql2016

Gets the classifier function object of the SqlInstance sql2016

.EXAMPLE
'Sql1','Sql2/sqlexpress' | Get-DbaResourceGovernorClassifierFunction

Gets the classifier function object on Sql1 and Sql2/sqlexpress instances

#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $classifierFunction = $null

            foreach ($currentFunction in $server.Databases["master"].UserDefinedFunctions)
            {
                $fullyQualifiedFunctionName = [string]::Format("[{0}].[{1}]", $currentFunction.Schema, $currentFunction.Name)
                if ($fullyQualifiedFunctionName -eq $server.ResourceGovernor.ClassifierFunction)
                {
                    $classifierFunction = $currentFunction
                }
            }

            if ($classifierFunction) {
                Add-Member -Force -InputObject $classifierFunction -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                Add-Member -Force -InputObject $classifierFunction -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                Add-Member -Force -InputObject $classifierFunction -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                Add-Member -Force -InputObject $classifierFunction -MemberType NoteProperty -Name Database -value 'master'
            }

            Select-DefaultView -InputObject $classifierFunction -Property ComputerName, InstanceName, SqlInstance, Database, Schema, CreateDate, DateLastModified, Name, DataType
        }
    }
}
tools\dbatools\functions\Get-DbaRestoreHistory.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaRestoreHistory {
    <#
        .SYNOPSIS
            Returns restore history details for databases on a SQL Server.

        .DESCRIPTION
            By default, this command will return the server name, database, username, restore type, date, from file and to files.

            Thanks to https://www.mssqltips.com/SqlInstancetip/1724/when-was-the-last-time-your-sql-server-database-was-restored/ for the query and https://sqlstudies.com/2016/07/27/when-was-this-database-restored/ for the idea.

        .PARAMETER SqlInstance
            Specifies the SQL Server instance(s) to operate on. Requires SQL Server 2005 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies the database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies the database(s) to exclude from processing. Options for this list are auto-populated from the server.

        .PARAMETER Since
            Specifies a datetime to use as the starting point for searching backup history.

        .PARAMETER Force
            Deprecated.

        .PARAMETER Last
            If this switch is enabled, the last restore action performed on each database is returned.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: DisasterRecovery, Backup, Restore

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaRestoreHistory

        .EXAMPLE
            Get-DbaRestoreHistory -SqlInstance sql2016

            Returns server name, database, username, restore type, date for all restored databases on sql2016.

        .EXAMPLE
            Get-DbaRestoreHistory -SqlInstance sql2016 -Database db1, db2 -Since '7/1/2016 10:47:00'

            Returns restore information only for databases db1 and db2 on sql2016 since July 1, 2016 at 10:47 AM.

        .EXAMPLE
            Get-DbaRestoreHistory -SqlInstance sql2014, sql2016 -Exclude db1

            Lots of detailed information for all databases except db1 on sql2014 and sql2016.

        .EXAMPLE
            Get-DbaRestoreHistory -SqlInstance sql2014 -Database AdventureWorks2014, pubs | Format-Table

            Adds From and To file information to output, returns information only for AdventureWorks2014 and pubs, and formats the data as a table.

        .EXAMPLE
            Get-DbaRegisteredServer -SqlInstance sql2016 | Get-DbaRestoreHistory

            Returns database restore information for every database on every server listed in the Central Management Server on sql2016.

    #>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [datetime]$Since,
        [switch]$Force,
        [switch]$Last,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Test-DbaDeprecation -DeprecatedOn "1.0.0.0" -EnableException:$false -Parameter 'Force'

        if ($Since -ne $null) {
            $Since = $Since.ToString("yyyy-MM-ddTHH:mm:ss")
        }
    }

    process {
        foreach ($instance in $SqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
                $computername = $server.ComputerName
                $instancename = $server.ServiceName
                $servername = $server.DomainInstanceName

                if ($force -eq $true) {
                    $select = "SELECT '$computername' AS [ComputerName],
                    '$instancename' AS [InstanceName],
                    '$servername' AS [SqlInstance], * "
                }
                else {
                    $select = "SELECT
                    '$computername' AS [ComputerName],
                    '$instancename' AS [InstanceName],
                    '$servername' AS [SqlInstance],
                     rsh.destination_database_name AS [Database],
                     --rsh.restore_history_id as RestoreHistoryID,
                     rsh.user_name AS [Username],
                     CASE
                         WHEN rsh.restore_type = 'D' THEN 'Database'
                         WHEN rsh.restore_type = 'F' THEN 'File'
                         WHEN rsh.restore_type = 'G' THEN 'Filegroup'
                         WHEN rsh.restore_type = 'I' THEN 'Differential'
                         WHEN rsh.restore_type = 'L' THEN 'Log'
                         WHEN rsh.restore_type = 'V' THEN 'Verifyonly'
                         WHEN rsh.restore_type = 'R' THEN 'Revert'
                         ELSE rsh.restore_type
                     END AS [RestoreType],
                     rsh.restore_date AS [Date],
                     ISNULL(STUFF((SELECT ', ' + bmf.physical_device_name
                                    FROM msdb.dbo.backupmediafamily bmf
                                   WHERE bmf.media_set_id = bs.media_set_id
                                 FOR XML PATH('')), 1, 2, ''), '') AS [From],
                     ISNULL(STUFF((SELECT ', ' + rf.destination_phys_name
                                    FROM msdb.dbo.restorefile rf
                                   WHERE rsh.restore_history_id = rf.restore_history_id
                                 FOR XML PATH('')), 1, 2, ''), '') AS [To],
                    bs.first_lsn,
                    bs.last_lsn,
                    bs.checkpoint_lsn,
                    bs.database_backup_lsn,
                    bs.backup_finish_date,
                    bs.backup_finish_date AS BackupFinishDate
                    "
                }

                $from = " FROM msdb.dbo.restorehistory rsh
                    INNER JOIN msdb.dbo.backupset bs ON rsh.backup_set_id = bs.backup_set_id"

                if ($ExcludeDatabase -or $Database -or $Since -or $last) {
                    $where = " WHERE "
                }

                $wherearray = @()

                if ($ExcludeDatabase) {
                    $dblist = $ExcludeDatabase -join "','"
                    $wherearray += " destination_database_name not in ('$dblist')"
                }

                if ($Database) {
                    $dblist = $Database -join "','"
                    $wherearray += "destination_database_name in ('$dblist')"
                }

                if ($null -ne $Since) {
                    $wherearray += "rsh.restore_date >= '$since'"
                }


                if ($last) {
                    $wherearray += "rsh.backup_set_id in
                        (select max(backup_set_id) from msdb.dbo.restorehistory
                        group by destination_database_name
                        )"
                }

                if ($where.length -gt 0) {
                    $wherearray = $wherearray -join " and "
                    $where = "$where $wherearray"
                }

                $sql = "$select $from $where"

                Write-Message -Level Debug -Message $sql

                $results = $server.ConnectionContext.ExecuteWithResults($sql).Tables.Rows
                if ($last) {
                    $ga = $results | Group-Object Database
                    $tmpres = @()
                    foreach($g in $ga) {
                        $tmpres += $g.Group | Sort-Object -Property Date -Descending | Select-Object -First 1
                    }
                    $results = $tmpres
                }
                $results | Select-DefaultView -ExcludeProperty first_lsn, last_lsn, checkpoint_lsn, database_backup_lsn, backup_finish_date
            }
            catch {
                Stop-Function -Message "Failure" -Target $SqlInstance -Error $_ -Exception $_.Exception.InnerException -Continue
            }
        }
    }
}
tools\dbatools\functions\Get-DbaRoleMember.ps1
function Get-DbaRoleMember {
    <#
.SYNOPSIS
Get members of all roles on a Sql instance.

.DESCRIPTION
Get members of all roles on a Sql instance.

Default output includes columns SQLServer, Database, Role, Member.

.PARAMETER SQLInstance
The SQL Server that you're connecting to.

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Database
The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

.PARAMETER ExcludeDatabase
The database(s) to exclude - this list is auto-populated from the server

.PARAMETER IncludeServerLevel
Shows also information on Server Level Permissions.

.PARAMETER NoFixedRole
Excludes all members of fixed roles.

.PARAMETER Credential
Credential object used to connect to the SQL Server as a different user.

.NOTES
Tags: Role, Database, Security, Login
Author: Klaas Vandenberghe ( @PowerDBAKlaas )

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
 https://dbatools.io/Get-DbaRoleMember

.EXAMPLE
Get-DbaRoleMember -SqlInstance ServerA

Returns a custom object displaying SQLServer, Database, Role, Member for all DatabaseRoles.

.EXAMPLE
Get-DbaRoleMember -SqlInstance sql2016 | Out-Gridview

Returns a gridview displaying SQLServer, Database, Role, Member for all DatabaseRoles.

.EXAMPLE
Get-DbaRoleMember -SqlInstance ServerA\sql987 -IncludeServerLevel

Returns a gridview displaying SQLServer, Database, Role, Member for both ServerRoles and DatabaseRoles.

#>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias('SqlServer', 'ServerInstance')]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$IncludeServerLevel,
        [switch]$NoFixedRole
    )

    process {

        foreach ($instance in $sqlinstance) {
            Write-Verbose "Connecting to $Instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Write-Warning "Failed to connect to $instance"
                continue
            }

            if ($IncludeServerLevel) {
                Write-Verbose "Server Role Members included"
                $instroles = $null
                Write-Verbose "Getting Server Roles on $instance"
                $instroles = $server.roles
                if ($NoFixedRole) {
                    $instroles = $instroles | Where-Object { $_.isfixedrole -eq $false }
                }
                ForEach ($instrole in $instroles) {
                    Write-Verbose "Getting Server Role Members for $instrole on $instance"
                    $irmembers = $null
                    $irmembers = $instrole.enumserverrolemembers()
                    ForEach ($irmem in $irmembers) {
                        [PSCustomObject]@{
                            SQLInstance = $instance
                            Database    = $null
                            Role        = $instrole.name
                            Member      = $irmem.tostring()
                        }
                    }
                }
            }

            $dbs = $server.Databases

            if ($Database) {
                $dbs = $dbs | Where-Object Name -In $Database
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $dbs) {
                Write-Verbose "Checking accessibility of $db on $instance"

                if ($db.IsAccessible -ne $true) {
                    Write-Warning "Database $db on $instance is not accessible"
                    continue
                }

                $dbroles = $db.roles
                Write-Verbose "Getting Database Roles for $db on $instance"

                if ($NoFixedRole) {
                    $dbroles = $dbroles | Where-Object { $_.isfixedrole -eq $false }
                }

                foreach ($dbrole in $dbroles) {
                    Write-Verbose "Getting Database Role Members for $dbrole in $db on $instance"
                    $dbmembers = $dbrole.enummembers()
                    ForEach ($dbmem in $dbmembers) {
                        [PSCustomObject]@{
                            SqlInstance = $instance
                            Database    = $db.name
                            Role        = $dbrole.name
                            Member      = $dbmem.tostring()
                        }
                    }
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaRunningJob.ps1
function Get-DbaRunningJob {
    <#
        .SYNOPSIS
            Returns all non-idle Agent jobs running on the server.

        .DESCRIPTION
            This function returns agent jobs that active on the SQL Server instance when calling the command. The information is gathered the SMO JobServer.jobs and be returned either in detailed or standard format.

        .PARAMETER SqlInstance
            The SQL Server instance to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Process, Session, ActivityMonitor, Agent, Job
            Author: Stephen Bennett, https://sqlnotesfromtheunderground.wordpress.com/
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaRunningJob

        .EXAMPLE
            Get-DbaRunningJob -SqlInstance localhost

            Returns any active jobs on localhost.

        .EXAMPLE
            Get-DbaRunningJob -SqlInstance localhost -Detailed

            Returns a detailed output of any active jobs on localhost.

        .EXAMPLE
            'localhost','localhost\namedinstance' | Get-DbaRunningJob

            Returns all active jobs on multiple instances piped into the function.
    #>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failed to connect to: $Server." -Target $server -ErrorRecord $_ -Continue
            }

            $jobs = $server.JobServer.jobs | Where-Object { $_.CurrentRunStatus -ne 'Idle' }

            if (!$jobs) {
                Write-Message -Level Verbose -Message "No Jobs are currently running on: $Server."
            }
            else {
                foreach ($job in $jobs) {
                    [pscustomobject]@{
                        ComputerName     = $server.ComputerName
                        InstanceName     = $server.ServiceName
                        SqlInstance      = $server.DomainInstanceName
                        Name             = $job.name
                        Category         = $job.Category
                        CurrentRunStatus = $job.CurrentRunStatus
                        CurrentRunStep   = $job.CurrentRunStep
                        HasSchedule      = $job.HasSchedule
                        LastRunDate      = $job.LastRunDate
                        LastRunOutcome   = $job.LastRunOutcome
                        JobStep          = $job.JobSteps
                    }
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaSchemaChangeHistory.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaSchemaChangeHistory {
    <#
    .SYNOPSIS
    Gets DDL changes logged in the system trace.

    .DESCRIPTION
    Queries the default system trace for any DDL changes in the specified timeframe
    Only works with SQL 2005 and later, as the system trace didn't exist before then

    .PARAMETER SqlInstance
    SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function
    to be executed against multiple SQL Server instances.

    .PARAMETER SqlCredential
    Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

    .PARAMETER Database
    The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

    .PARAMETER ExcludeDatabase
    The database(s) to exclude - this list is auto-populated from the server

    .PARAMETER Since
    A date from which DDL changes should be returned. Default is to start at the beggining of the current trace file

    .PARAMETER Object
    The name of a SQL Server object you want to look for changes on

    .PARAMETER EnableException
    By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
    Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
    Tags: Migration, Backup, Database
    Author: Stuart Moore (@napalmgram - http://stuart-moore.com)

    Website: https://dbatools.io
    Copyright: (C) Chrissy LeMaire, [email protected]
    License: MIT https://opensource.org/licenses/MIT

    .LINK
    https://dbatools.io/Get-DbaSchemaChangeHistory

    .EXAMPLE
    Get-DbaSchemaChangeHistory -SqlInstance localhost

    Returns all DDL changes made in all databases on the SQL Server instance localhost since the system trace began

    .EXAMPLE
    Get-DbaSchemaChangeHistory -SqlInstance localhost -Since (Get-Date).AddDays(-7)

    Returns all DDL changes made in all databases on the SQL Server instance localhost in the last 7 days

    .EXAMPLE
    Get-DbaSchemaChangeHistory -SqlInstance localhost -Database Finance, Prod -Since (Get-Date).AddDays(-7)

    Returns all DDL changes made in the Prod and Finance databases on the SQL Server instance localhost in the last 7 days

    .EXAMPLE
    Get-DbaSchemaChangeHistory -SqlInstance localhost -Database Finance -Object AccountsTable -Since (Get-Date).AddDays(-7)

    Returns all DDL changes made  to the AccountsTable object in the Finance database on the SQL Server instance localhost in the last 7 days

    #>

    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [DbaDateTime]$Since,
        [string[]]$Object,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            if ($Server.Version.Major -le 8) {
                Stop-Function -Message "This command doesn't support SQL Server 2000, sorry about that"
                return
            }
            $TraceFileQuery = "select path from sys.traces where is_default = 1"

            $TraceFile = $server.Query($TraceFileQuery) | Select-Object Path

            $Databases = $server.Databases

            if ($Database) { $Databases = $Databases | Where-Object Name -in $database }

            if ($ExcludeDatabase) { $Databases = $Databases | Where-Object Name -notin $ExcludeDatabase }

            foreach ($db in $Databases) {
                if ($db.IsAccessible -eq $false) {
                    Write-Message -Level Verbose -Message "$($db.name) is not accessible, skipping"
                }

                $sql = "select SERVERPROPERTY('MachineName') AS ComputerName,
                        ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
                        SERVERPROPERTY('ServerName') AS SqlInstance,
                        tt.databasename as 'DatabaseName',
                        starttime as 'DateModified',
                        Sessionloginname as 'LoginName',
                        NTusername as 'UserName',
                        applicationname as 'ApplicationName',
                        case eventclass
                            When '46' Then 'Create'
                            when '47' Then 'Drop'
                            when '164' then 'Alter'
                        end as 'DDLOperation',
                        s.name+'.'+o.name as 'Object',
                        o.type_desc as 'ObjectType'
                        from
                        sys.objects o  inner join
                        sys.schemas s on s.schema_id=o.schema_id
                        cross apply (select * from ::fn_trace_gettable('$($TraceFile.path)',default) where ObjectID=o.object_id ) tt
                        where tt.objecttype not in (21587)
                        and tt.DatabaseID=db_id()
                        and tt.EventSubClass=0"

                if ($null -ne $since) {
                    $sql = $sql + " and tt.StartTime>'$Since' "
                }
                if ($null -ne $object) {
                    $sql = $sql + " and o.name in ('$($object -join ''',''')') "
                }

                $sql = $sql + " order by tt.StartTime asc"
                Write-Message -Level Verbose -Message "Querying Database $db on $instance"
                Write-Message -Level Debug -Message "SQL: $sql"

                $db.Query($sql) | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, DatabaseName, DateModified, LoginName, UserName, ApplicationName, DDLOperation, Object, ObjectType
            }
        }
    }
}

tools\dbatools\functions\Get-DbaServerAudit.ps1
function Get-DbaServerAudit {
    <#
        .SYNOPSIS
            Gets SQL Security Audit information for each instance(s) of SQL Server.

        .DESCRIPTION
            The Get-DbaServerAudit command gets SQL Security Audit information for each instance(s) of SQL Server.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function
            to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Audit
            Return only specific audits

        .PARAMETER ExcludeAudit
            Exclude specific audits

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Audit, Security, SqlAudit
            Author: Garry Bargsley (@gbargsley), http://blog.garrybargsley.com

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaServerAudit

        .EXAMPLE
            Get-DbaServerAudit -SqlInstance localhost

            Returns all Security Audits on the local default SQL Server instance

        .EXAMPLE
            Get-DbaServerAudit -SqlInstance localhost, sql2016

            Returns all Security Audits for the local and sql2016 SQL Server instances
    #>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [string[]]$Audit,
        [string[]]$ExcludeAudit,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $audits = $server.Audits

            if (Test-Bound -ParameterName Audit) {
                $audits = $audits | Where-Object Name -in $Audit
            }
            if (Test-Bound -ParameterName ExcludeAudit) {
                $audits = $audits | Where-Object Name -notin $ExcludeAudit
            }

            foreach ($currentaudit in $audits) {
                $directory = $currentaudit.FilePath.TrimEnd("\")
                $filename = $currentaudit.FileName
                $fullname = "$directory\$filename"
                $remote = $fullname.Replace(":", "$")
                $remote = "\\$($currentaudit.Parent.ComputerName)\$remote"

                Add-Member -Force -InputObject $currentaudit -MemberType NoteProperty -Name ComputerName -value $currentaudit.Parent.ComputerName
                Add-Member -Force -InputObject $currentaudit -MemberType NoteProperty -Name InstanceName -value $currentaudit.Parent.ServiceName
                Add-Member -Force -InputObject $currentaudit -MemberType NoteProperty -Name SqlInstance -value $currentaudit.Parent.DomainInstanceName
                Add-Member -Force -InputObject $currentaudit -MemberType NoteProperty -Name FullName -value $fullname
                Add-Member -Force -InputObject $currentaudit -MemberType NoteProperty -Name RemoteFullName -value $remote

                Select-DefaultView -InputObject $currentaudit -Property ComputerName, InstanceName, SqlInstance, Name, 'Enabled as IsEnabled', FullName
            }
        }
    }
}
tools\dbatools\functions\Get-DbaServerAuditSpecification.ps1
function Get-DbaServerAuditSpecification {
    <#
        .SYNOPSIS
            Gets SQL Security Audit Specification information for each instance(s) of SQL Server.

        .DESCRIPTION
            The Get-DbaServerAuditSpecification command gets SQL Security Audit Specification information for each instance(s) of SQL Server.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function
            to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Audit, Security, SqlAudit
            Author: Garry Bargsley (@gbargsley), http://blog.garrybargsley.com

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaServerAuditSpecification

        .EXAMPLE
            Get-DbaServerAuditSpecification -SqlInstance localhost

            Returns all Security Audit Specifications on the local default SQL Server instance

        .EXAMPLE
            Get-DbaServerAuditSpecification -SqlInstance localhost, sql2016

            Returns all Security Audit Specifications for the local and sql2016 SQL Server instances
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Verbose "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($server.versionMajor -lt 10) {
                Write-Warning "Server Audits are only supported in SQL Server 2008 and above. Quitting."
                continue
            }

            foreach ($auditSpecification in $server.ServerAuditSpecifications) {
                Add-Member -Force -InputObject $auditSpecification -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                Add-Member -Force -InputObject $auditSpecification -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                Add-Member -Force -InputObject $auditSpecification -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName

                Select-DefaultView -InputObject $auditSpecification -Property ComputerName, InstanceName, SqlInstance, ID, Name, AuditName, Enabled, CreateDate, DateLastModified, Guid
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-SqlServerAuditSpecification
    }
}
tools\dbatools\functions\Get-DbaServerInstallDate.ps1
function Get-DbaServerInstallDate {
    <#
.SYNOPSIS
Returns the install date of a SQL Instance and Windows Server, depending on what is passed.

.DESCRIPTION
By default, this command returns for each SQL Instance instance passed in:
SQL Instance install date, formatted as a string
Hosting Windows server install date, formatted as a string

.PARAMETER SqlInstance
The SQL Server that you're connecting to.

.PARAMETER SqlCredential
Credential object used to connect to the SQL Server as a different user

.PARAMETER Credential
Credential object used to connect to the SQL Server as a different user

.PARAMETER IncludeWindows
Includes the Windows Server Install date information

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: CIM
Author: Mitchell Hamann (@SirCaptainMitch), mitchellhamann.com

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Get-DbaServerInstallDate

.EXAMPLE
Get-DbaServerInstallDate -SqlInstance SqlBox1\Instance2

Returns an object with SQL Instance Install date as a string and the Windows install date as string.

.EXAMPLE
Get-DbaServerInstallDate -SqlInstance winserver\sqlexpress, sql2016

Returns an object with SQL Instance Install date as a string and the Windows install date as a string for both SQLInstances that are passed to the cmdlet.

.EXAMPLE
Get-DbaServerInstallDate -SqlInstance sqlserver2014a, sql2016

Returns an object with only the SQL Server Install date as a string.

.EXAMPLE
Get-DbaServerInstallDate -SqlInstance sqlserver2014a, sql2016 -IncludeWindows

Returns an object with the Windows Install date and the SQL install date as a string.

.EXAMPLE
Get-DbaRegisteredServer -SqlInstance sql2014 | Get-DbaServerInstallDate

Returns an object with SQL Instance install date as a string for every server listed in the Central Management Server on sql2014

#>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer", "ComputerName")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]
        $SqlCredential,
        [PSCredential]
        $Credential,
        [Switch]$IncludeWindows,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level VeryVerbose -Message "Connecting to $instance" -Target $instance
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failed to process Instance $Instance" -ErrorRecord $_ -Target $instance -Continue
            }

            if ($server.VersionMajor -ge 9) {
                Write-Message -Level Verbose -Message "Getting Install Date for: $instance"
                $sql = "SELECT create_date FROM sys.server_principals WHERE sid = 0x010100000000000512000000"
                [DbaDateTime]$sqlInstallDate = $server.Query($sql, 'master', $true).create_date

            }
            else {
                Write-Message -Level Verbose -Message "Getting Install Date for: $instance"
                $sql = "SELECT schemadate FROM sysservers"
                [DbaDateTime]$sqlInstallDate = $server.Query($sql, 'master', $true).create_date
            }

            $WindowsServerName = $server.ComputerNamePhysicalNetBIOS

            if ($IncludeWindows) {
                try {
                    [DbaDateTime]$windowsInstallDate = (Get-DbaCmObject -ClassName win32_OperatingSystem -ComputerName $WindowsServerName -Credential $Credential -EnableException).InstallDate
                }
                catch {
                    Stop-Function -Message "Failed to connect to: $WindowsServerName" -Continue -Target $instance -ErrorRecord $_
                }
            }

            $object = [PSCustomObject]@{
                ComputerName       = $server.ComputerName
                InstanceName       = $server.ServiceName
                SqlInstance        = $server.DomainInstanceName
                SqlInstallDate     = $sqlInstallDate
                WindowsInstallDate = $windowsInstallDate
            }

            if ($IncludeWindows) {
                Select-DefaultView -InputObject $object -Property ComputerName, InstanceName, SqlInstance, SqlInstallDate, WindowsInstallDate
            }
            else {
                Select-DefaultView -InputObject $object -Property ComputerName, InstanceName, SqlInstance, SqlInstallDate
            }

        }
    }
}
tools\dbatools\functions\Get-DbaServerProtocol.ps1
function Get-DbaServerProtocol {
    <#
    .SYNOPSIS
    Gets the SQL Server related server protocols on a computer.

    .DESCRIPTION
    Gets the SQL Server related server protocols on one or more computers.

    Requires Local Admin rights on destination computer(s).
    The server protocols can be enabled and disabled when retrieved via WSMan.

    .PARAMETER ComputerName
    The SQL Server (or server in general) that you're connecting to. This command handles named instances.

    .PARAMETER Credential
    Credential object used to connect to the computer as a different user.

   .PARAMETER EnableException
   By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
   This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
   Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
    Author: Klaas Vandenberghe ( @PowerDBAKlaas )
    Tags: Protocol
    dbatools PowerShell module (https://dbatools.io)
    Copyright (C) 2016 Chrissy LeMaire
    License: MIT https://opensource.org/licenses/MIT

    .LINK
    https://dbatools.io/Get-DbaServerProtocol

    .EXAMPLE
    Get-DbaServerProtocol -ComputerName sqlserver2014a

    Gets the SQL Server related server protocols on computer sqlserver2014a.

    .EXAMPLE
    'sql1','sql2','sql3' | Get-DbaServerProtocol

    Gets the SQL Server related server protocols on computers sql1, sql2 and sql3.

    .EXAMPLE
    Get-DbaServerProtocol -ComputerName sql1,sql2 | Out-Gridview

    Gets the SQL Server related server protocols on computers sql1 and sql2, and shows them in a grid view.

    .EXAMPLE
    (Get-DbaServerProtocol -ComputerName sql1 | Where { $_.DisplayName = 'via' }).Disable()

    Disables the VIA ServerNetworkProtocol on computer sql1.
    If successful, returncode 0 is shown.

#>
    [CmdletBinding()]
    Param (
        [parameter(ValueFromPipeline)]
        [Alias("cn", "host", "Server")]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($Computer in $ComputerName.ComputerName) {
            $Server = Resolve-DbaNetworkName -ComputerName $Computer -Credential $credential
            if ($Server.FullComputerName) {
                $Computer = $server.FullComputerName
                Write-Message -Level Verbose -Message "Getting SQL Server namespace on $computer"
                $namespace = Get-DbaCmObject -ComputerName $Computer -NameSpace root\Microsoft\SQLServer -Query "Select * FROM __NAMESPACE WHERE Name Like 'ComputerManagement%'" -ErrorAction SilentlyContinue |
                    Where-Object { (Get-DbaCmObject -ComputerName $Computer -Namespace $("root\Microsoft\SQLServer\" + $_.Name) -ClassName ServerNetworkProtocol -ErrorAction SilentlyContinue).count -gt 0 } |
                    Sort-Object Name -Descending | Select-Object -First 1
                if ($namespace.Name) {
                    Write-Message -Level Verbose -Message "Getting Cim class ServerNetworkProtocol in Namespace $($namespace.Name) on $Computer"
                    try {
                        $prot = Get-DbaCmObject -ComputerName $Computer -Namespace $("root\Microsoft\SQLServer\" + $namespace.Name) -ClassName ServerNetworkProtocol -ErrorAction SilentlyContinue
                        $prot | Add-Member -Force -MemberType ScriptMethod -Name Enable -Value { Invoke-CimMethod -MethodName SetEnable -InputObject $this }
                        $prot | Add-Member -Force -MemberType ScriptMethod -Name Disable -Value { Invoke-CimMethod -MethodName SetDisable -InputObject $this }
                        foreach ($protocol in $prot) { Select-DefaultView -InputObject $protocol -Property 'PSComputerName as ComputerName', 'InstanceName', 'ProtocolDisplayName as DisplayName', 'ProtocolName as Name', 'MultiIpconfigurationSupport as MultiIP', 'Enabled as IsEnabled' }
                    }
                    catch {
                        Write-Message -Level Warning -Message "No Sql ServerNetworkProtocol found on $Computer"
                    }
                }
                else {
                    Write-Message -Level Warning -Message "No ComputerManagement Namespace on $Computer. Please note that this function is available from SQL 2005 up."
                }
            }
            else {
                Write-Message -Level Warning -Message "Failed to connect to $Computer"
            }
        }
    }
}
tools\dbatools\functions\Get-DbaServerRole.ps1
function Get-DbaServerRole {
    <#
        .SYNOPSIS
            Gets the list of server-level roles.

        .DESCRIPTION
            Gets the list of server-level roles for SQL Server instance.

        .PARAMETER SqlInstance
            The SQL Server instance. Server version must be SQL Server version 2005 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER ServerRole
            Server-Level role to filter results to that role only.

        .PARAMETER ExcludeServerRole
            Server-Level role to exclude from results.

        .PARAMETER ExcludeFixedRole
            Filter the fixed server-level roles. Only applies to SQL Server 2017 that supports creation of server-level roles.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message. This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting. Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ServerRole, Security
            Original Author: Shawn Melton (@wsmelton)

            Website: https: //dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaServerRole

        .EXAMPLE
            Get-DbaServerRole -SqlInstance sql2016a

            Outputs list of server-level roles for sql2016a instance.

        .EXAMPLE
            Get-DbaServerRole -SqlInstance sql2017a -ExcludeFixedRole

            Outputs the server-level role(s) that are not fixed roles on sql2017a instance.
    #>
    [CmdletBinding()]
    param (
        [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$ServerRole,
        [object[]]$ExcludeServerRole,
        [switch]$ExcludeFixedRole,
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $roles = $server.Roles

            if ($ServerRole) {
                $roles = $roles | Where-Object Name -In $ServerRole
            }
            if ($ExcludeServerRole) {
                $roles = $roles | Where-Object Name -NotIn $ExcludeServerRole
            }
            if ($ExcludeFixedRole) {
                $roles = $roles | Where-Object IsFixedRole -eq $false
            }

            foreach ($role in $roles) {
                $members = $role.EnumMemberNames()

                Add-Member -Force -InputObject $role -MemberType NoteProperty -Name Login -Value $members
                Add-Member -Force -InputObject $role -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                Add-Member -Force -InputObject $role -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                Add-Member -Force -InputObject $role -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName

                $default = 'ComputerName', 'InstanceName', 'SqlInstance', 'Name as Role', 'IsFixedRole', 'DateCreated', 'DateModified'
                Select-DefaultView -InputObject $role -Property $default
            }
        }
    }
}
tools\dbatools\functions\Get-DbaSpConfigure.ps1
function Get-DbaSpConfigure {
    <#
        .SYNOPSIS
            Returns all server level system configuration (sys.configuration/sp_configure) information

        .DESCRIPTION
            This function returns server level system configuration (sys.configuration/sp_configure) information. The information is gathered through SMO Configuration.Properties.
            The data includes the default value for each configuration, for quick identification of values that may have been changed.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a
            collection and receive pipeline input

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Name
            Return only specific configurations -- auto-populated from source server

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: SpConfig, Configure, Configuration
            Author: Nic Cain, https://sirsql.net/

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaSpConfigure

        .EXAMPLE
            Get-DbaSpConfigure -SqlInstance localhost

            Returns server level configuration data on the localhost (ServerName, Name, DisplayName, Description, IsAdvanced, IsDynamic, MinValue, MaxValue, ConfiguredValue, RunningValue, DefaultValue, IsRunningDefaultValue)

        .EXAMPLE
            'localhost','localhost\namedinstance' | Get-DbaSpConfigure

            Returns system configuration information on multiple instances piped into the function

        .EXAMPLE
            Get-DbaSpConfigure -SqlInstance localhost

            Returns server level configuration data on the localhost (ServerName, Name, DisplayName, Description, IsAdvanced, IsDynamic, MinValue, MaxValue, ConfiguredValue, RunningValue, DefaultValue, IsRunningDefaultValue)

        .EXAMPLE
            Get-DbaSpConfigure -SqlInstance sql2012 -Name MaxServerMemory

            Returns only the system configuration for MaxServerMemory. Configs is auto-populated for tabbing convenience.
        #>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Config", "ConfigName")]
        [string[]]$Name,
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Write-Warning "Failed to connect to: $instance"
                continue
            }

            #Get a list of the configuration property parents, and exclude the Parent, Properties values
            $proplist = Get-Member -InputObject $server.Configuration -MemberType Property -Force | Select-Object Name | Where-Object { $_.Name -ne "Parent" -and $_.Name -ne "Properties" }

            if ($Name) {
                $proplist = $proplist | Where-Object { $_.Name -in $Name }
            }

            #Grab the default sp_configure property values from the external function
            $defaultConfigs = (Get-SqlDefaultSpConfigure -SqlVersion $server.VersionMajor).psobject.properties;

            #Iterate through the properties to get the configuration settings
            foreach ($prop in $proplist) {
                $propInfo = $server.Configuration.$($prop.Name)
                $defaultConfig = $defaultConfigs | Where-Object { $_.Name -eq $propInfo.DisplayName };

                if ($defaultConfig.Value -eq $propInfo.RunValue) { $isDefault = $true }
                else { $isDefault = $false }

                #Ignores properties that are not valid on this version of SQL
                if (!([string]::IsNullOrEmpty($propInfo.RunValue))) {
                    # some displaynames were empty
                    $displayname = $propInfo.DisplayName
                    if ($displayname.Length -eq 0) { $displayname = $prop.Name }

                    [pscustomobject]@{
                        ServerName            = $server.Name
                        ComputerName          = $server.ComputerName
                        InstanceName          = $server.ServiceName
                        SqlInstance           = $server.DomainInstanceName
                        Name                  = $prop.Name
                        DisplayName           = $displayname
                        Description           = $propInfo.Description
                        IsAdvanced            = $propInfo.IsAdvanced
                        IsDynamic             = $propInfo.IsDynamic
                        MinValue              = $propInfo.Minimum
                        MaxValue              = $propInfo.Maximum
                        ConfiguredValue       = $propInfo.ConfigValue
                        RunningValue          = $propInfo.RunValue
                        DefaultValue          = $defaultConfig.Value
                        IsRunningDefaultValue = $isDefault
                        Parent                = $server
                        ConfigName            = $prop.Name
                    } | Select-DefaultView -ExcludeProperty ServerName, Parent, ConfigName
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaSpn.ps1
#ValidationTags#FlowControl,Pipeline#
function Get-DbaSpn {
    <#
        .SYNOPSIS
            Returns a list of set service principal names for a given computer/AD account

        .DESCRIPTION
            Get a list of set SPNs. SPNs are set at the AD account level. You can either retrieve set SPNs for a computer, or any SPNs set for
            a given active directory account. You can query one, or both. You'll get a list of every SPN found for either search term.

        .PARAMETER ComputerName
            The servers you want to return set SPNs for. This is defaulted automatically to localhost.

        .PARAMETER AccountName
            The accounts you want to retrieve set SPNs for.

        .PARAMETER Credential
            User credential to connect to the remote servers or active directory.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: SPN
            Author: Drew Furgiuele (@pittfurg), http://www.port1433.com

            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaSpn

        .EXAMPLE
            Get-DbaSpn -ServerName SQLSERVERA -Credential (Get-Credential)

            Returns a custom object with SearchTerm (ServerName) and the SPNs that were found

        .EXAMPLE
            Get-DbaSpn -AccountName domain\account -Credential (Get-Credential)

            Returns a custom object with SearchTerm (domain account) and the SPNs that were found

        .EXAMPLE
            Get-DbaSpn -ServerName SQLSERVERA,SQLSERVERB -Credential (Get-Credential)

            Returns a custom object with SearchTerm (ServerName) and the SPNs that were found for multiple computers
    #>
    [cmdletbinding()]
    param (
        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [string[]]$ComputerName,
        [Parameter(Mandatory = $false)]
        [string[]]$AccountName,
        [Parameter(Mandatory = $false)]
        [PSCredential]$Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        Function Process-Account ($AccountName) {

            ForEach ($account in $AccountName) {
                Write-Message -Message "Looking for account $account..." -Level Verbose
                $searchfor = 'User'
                if ($account.EndsWith('$')) {
                    $searchfor = 'Computer'
                }
                try {
                    $Result = Get-DbaADObject -ADObject $account -Type $searchfor -Credential $Credential -EnableException
                }
                catch {
                    Write-Message -Message "AD lookup failure. This may be because the domain cannot be resolved for the SQL Server service account ($Account)." -Level Warning
                    continue
                }
                if ($Result.Count -gt 0) {
                    try {
                        $results = $Result.GetUnderlyingObject()
                        $spns = $results.Properties.servicePrincipalName
                    }
                    catch {
                        Write-Message -Message "The SQL Service account ($Account) has been found, but you don't have enough permission to inspect its SPNs" -Level Warning
                        continue
                    }
                }
                else {
                    Write-Message -Message "The SQL Service account ($Account) has not been found" -Level Warning
                    continue
                }

                foreach ($spn in $spns) {
                    if ($spn -match "\:") {
                        try {
                            $port = [int]($spn -Split "\:")[1]
                        }
                        catch {
                            $port = $null
                        }
                        if ($spn -match "\/") {
                            $serviceclass = ($spn -Split "\/")[0]
                        }
                    }
                    [pscustomobject] @{
                        Input        = $Account
                        AccountName  = $Account
                        ServiceClass = "MSSQLSvc" # $serviceclass
                        Port         = $port
                        SPN          = $spn
                    }
                }
            }
        }
        if ($ComputerName.Count -eq 0 -and $AccountName.Count -eq 0) {
            $ComputerName = @($env:COMPUTERNAME)
        }
    }

    process {

        foreach ($computer in $ComputerName) {
            if ($computer) {
                if ($computer.EndsWith('$')) {
                    Write-Message -Message "$computer is an account name. Processing as account." -Level Verbose
                    Process-Account -AccountName $computer
                    continue
                }
            }

            Write-Message -Message "Getting SQL Server SPN for $computer" -Level Verbose
            $spns = Test-DbaSpn -ComputerName $computer -Credential $Credential

            $sqlspns = 0
            $spncount = $spns.count
            Write-Message -Message "Calculated $spncount SQL SPN entries that should exist for $computer" -Level Verbose
            foreach ($spn in $spns | Where-Object { $_.IsSet -eq $true }) {
                $sqlspns++

                if ($accountName) {
                    if ($accountName -eq $spn.InstanceServiceAccount) {
                        [pscustomobject] @{
                            Input        = $computer
                            AccountName  = $spn.InstanceServiceAccount
                            ServiceClass = "MSSQLSvc"
                            Port         = $spn.Port
                            SPN          = $spn.RequiredSPN
                        }
                    }
                }
                else {
                    [pscustomobject] @{
                        Input        = $computer
                        AccountName  = $spn.InstanceServiceAccount
                        ServiceClass = "MSSQLSvc"
                        Port         = $spn.Port
                        SPN          = $spn.RequiredSPN
                    }
                }
            }
            Write-Message -Message "Found $sqlspns set SQL SPN entries for $computer" -Level Verbose
        }

        if ($AccountName) {
            foreach ($account in $AccountName) {
                Process-Account -AccountName $account
            }
        }
    }
}
tools\dbatools\functions\Get-DbaSqlBuildReference.ps1
function Get-DbaSqlBuildReference {
    <#
    .SYNOPSIS
        Returns SQL Server Build infos on a SQL instance

    .DESCRIPTION
        Returns info about the specific build of a SQL instance, including the SP, the CU and the reference KB, wherever possible.
        It also includes End Of Support dates as specified on Microsoft Lifecycle Policy

    .PARAMETER Build
        Instead of connecting to a real instance, pass a string identifying the build to get the info back.

    .PARAMETER SqlInstance
        Target any number of instances, in order to return their build state.

    .PARAMETER SqlCredential
        When connecting to an instance, use the credentials specified.

    .PARAMETER Update
        Looks online for the most up to date reference, replacing the local one.

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .EXAMPLE
        Get-DbaSqlBuildReference -Build "12.00.4502"

        Returns information about a build identified by  "12.00.4502" (which is SQL 2014 with SP1 and CU11)

    .EXAMPLE
        Get-DbaSqlBuildReference -Build "12.00.4502" -Update

        Returns information about a build trying to fetch the most up to date index online. When the online version is newer, the local one gets overwritten

    .EXAMPLE
        Get-DbaSqlBuildReference -Build "12.0.4502","10.50.4260"

        Returns information builds identified by these versions strings

    .EXAMPLE
        Get-DbaRegisteredServer -SqlInstance sqlserver2014a | Get-DbaSqlBuildReference

        Integrate with other commandlets to have builds checked for all your registered servers on sqlserver2014a

    .NOTES
        Author: niphlod
        Editor: Fred
        Tags: SqlBuild

        dbatools PowerShell module (https://dbatools.io, [email protected])
        Copyright (C) 2016 Chrissy LeMaire
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Get-DbaSqlBuildReference
#>
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
    [CmdletBinding()]
    Param (
        [version[]]
        $Build,

        [parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]
        $SqlInstance,

        [Alias("Credential")]
        [PsCredential]
        $SqlCredential,

        [switch]
        $Update,

        [switch]
        [Alias('Silent')]$EnableException
    )

    begin {
        #region Helper functions
        function Get-DbaSqlBuildReferenceIndex {
            [CmdletBinding()]
            Param (
                [string]
                $Moduledirectory,

                [bool]
                $Update,

                [bool]
                $EnableException
            )

            $orig_idxfile = "$Moduledirectory\bin\dbatools-buildref-index.json"
            $DbatoolsData = Get-DbaConfigValue -Name 'Path.DbatoolsData'
            $writable_idxfile = Join-Path $DbatoolsData "dbatools-buildref-index.json"

            if (-not (Test-Path $orig_idxfile)) {
                Write-Message -Level Warning -Message "Unable to read local SQL build reference file. Check your module integrity!"
            }

            if ((-not (Test-Path $orig_idxfile)) -and (-not (Test-Path $writable_idxfile))) {
                throw "Build reference file not found, check module health!"
            }

            # If no writable copy exists, create one and return the module original
            if (-not (Test-Path $writable_idxfile)) {
                Copy-Item -Path $orig_idxfile -Destination $writable_idxfile -Force -ErrorAction Stop
                $result = Get-Content $orig_idxfile -Raw | ConvertFrom-Json
            }

            # Else, if both exist, update the writeable if necessary and return the current version
            elseif (Test-Path $orig_idxfile) {
                $module_content = Get-Content $orig_idxfile -Raw | ConvertFrom-Json
                $data_content = Get-Content $writable_idxfile -Raw | ConvertFrom-Json

                $module_time = Get-Date $module_content.LastUpdated
                $data_time = Get-Date $data_content.LastUpdated

                $offline_time = $module_time
                if ($module_time -gt $data_time) {
                    Copy-Item -Path $orig_idxfile -Destination $writable_idxfile -Force -ErrorAction Stop
                    $result = $module_content
                }
                else {
                    $result = $data_content
                    $offline_time = $data_time
                }
                # If Update is passed, try to fetch from online resource and store into the writeable
                if ($Update) {
                    $WebContent = Get-DbaSqlBuildReferenceIndexOnline -EnableException $EnableException
                    if ($null -ne $WebContent) {
                        $webdata_content = $WebContent.Content | ConvertFrom-Json
                        $webdata_time = Get-Date $webdata_content.LastUpdated
                        if ($webdata_time -gt $offline_time) {
                            Write-Message -Level Output -Message "Index updated correctly, last update on: $(Get-Date -Date $webdata_time -Format s), was $(Get-Date -Date $offline_time -Format s)"
                            $WebContent.Content | Out-File $writable_idxfile -Encoding utf8 -ErrorAction Stop
                            $result = Get-Content $writable_idxfile -Raw | ConvertFrom-Json
                        }
                    }
                }
            }

            # Else if the module version of the file no longer exists, but the writable version exists, return the writable version
            else {
                $result = Get-Content $writable_idxfile -Raw | ConvertFrom-Json
            }

            $LastUpdated = Get-Date -Date $result.LastUpdated
            if ($LastUpdated -lt (Get-Date).AddDays(-45)) {
                Write-Message -Level Warning -Message "Index is stale, last update on: $(Get-Date -Date $LastUpdated -Format s), try the -Update parameter to fetch the most up to date index"
            }

            $result.Data | Select-Object @{ Name = "VersionObject"; Expression = { [version]$_.Version } }, *
        }

        function Get-DbaSqlBuildReferenceIndexOnline {
            [CmdletBinding()]
            Param (
                [bool]
                $EnableException
            )
            $url = Get-DbaConfigValue -Name 'assets.sqlbuildreference'
            try {
                $WebContent = Invoke-WebRequest $url -ErrorAction Stop
            }
            catch {
                try {
                    Write-Message -Level Verbose -Message "Probably using a proxy for internet access, trying default proxy settings"
                    (New-Object System.Net.WebClient).Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
                    $WebContent = Invoke-WebRequest $url -ErrorAction Stop
                }
                catch {
                    Write-Message -Level Warning -Message "Couldn't download updated index from $url"
                    return
                }
            }
            return $WebContent
        }

        function Resolve-DbaSqlBuild {
            [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
            [CmdletBinding()]
            [OutputType([System.Collections.Hashtable])]
            Param (
                [version]
                $Build,

                $Data,

                [bool]
                $EnableException
            )

            Write-Message -Level Verbose -Message "Looking for $Build"

            $IdxVersion = $Data | Where-Object Version -like "$($Build.Major).$($Build.Minor).*"
            $Detected = @{ }
            $Detected.MatchType = 'Approximate'
            Write-Message -Level Verbose -Message "We have $($IdxVersion.Length) builds in store for this Release"
            If ($IdxVersion.Length -eq 0) {
                Write-Message -Level Warning -Message "No info in store for this Release"
                $Detected.Warning = "No info in store for this Release"
            }
            else {
                $LastVer = $IdxVersion[0]
            }
            foreach ($el in $IdxVersion) {
                if ($null -ne $el.Name) {
                    $Detected.Name = $el.Name
                }
                if ($el.VersionObject -gt $Build) {
                    $Detected.MatchType = 'Approximate'
                    $Detected.Warning = "$Build not found, closest build we have is $($LastVer.Version)"
                    break
                }
                $LastVer = $el
                if ($null -ne $el.SP) {
                    $Detected.SP = $el.SP
                    $Detected.CU = $null
                }
                if ($null -ne $el.CU) {
                    $Detected.CU = $el.CU
                }
                if ($null -ne $el.SupportedUntil) {
                    $Detected.SupportedUntil = (Get-Date -date $el.SupportedUntil)
                }
                $Detected.KB = $el.KBList
                if ($el.Version -eq $Build) {
                    $Detected.MatchType = 'Exact'
                    break
                }
            }
            return $Detected
        }
        #endregion Helper functions

        $moduledirectory = $MyInvocation.MyCommand.Module.ModuleBase

        try {
            $IdxRef = Get-DbaSqlBuildReferenceIndex -Moduledirectory $moduledirectory -Update $Update -EnableException $EnableException
        }
        catch {
            Stop-Function -Message "Error loading SQL build reference" -ErrorRecord $_
            return
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $SqlInstance) {
            #region Ensure the connection is established
            try {
                Write-Message -Level VeryVerbose -Message "Connecting to $instance" -Target $instance
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failed to process Instance $Instance" -ErrorRecord $_ -Target $instance -Continue
            }

            try {
                $null = $server.Version.ToString()
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            #endregion Ensure the connection is established

            $Detected = Resolve-DbaSqlBuild -Build $server.Version -Data $IdxRef -EnableException $EnableException

            [PSCustomObject]@{
                SqlInstance    = $server.DomainInstanceName
                Build          = $server.Version
                NameLevel      = $Detected.Name
                SPLevel        = $Detected.SP
                CULevel        = $Detected.CU
                KBLevel        = $Detected.KB
                SupportedUntil = $Detected.SupportedUntil
                MatchType      = $Detected.MatchType
                Warning        = $Detected.Warning
            }
        }

        foreach ($buildstr in $Build) {
            $Detected = Resolve-DbaSqlBuild -Build $buildstr -Data $IdxRef -EnableException $EnableException

            [PSCustomObject]@{
                SqlInstance    = $null
                Build          = $buildstr
                NameLevel      = $Detected.Name
                SPLevel        = $Detected.SP
                CULevel        = $Detected.CU
                KBLevel        = $Detected.KB
                SupportedUntil = $Detected.SupportedUntil
                MatchType      = $Detected.MatchType
                Warning        = $Detected.Warning
            } | Select-DefaultView -ExcludeProperty SqlInstance
        }
    }
}
tools\dbatools\functions\Get-DbaSqlFeature.ps1
#ValidationTags#CodeStyle,Messaging,FlowControl,Pipeline#
function Get-DbaSqlFeature {
    <#
        .SYNOPSIS
             Runs the SQL Server feature discovery report (setup.exe /Action=RunDiscovery)

        .DESCRIPTION
            Runs the SQL Server feature discovery report (setup.exe /Action=RunDiscovery)

            Inspired by Dave Mason's (@BeginTry) post at
            https://itsalljustelectrons.blogspot.be/2018/04/SQL-Server-Discovery-Report.html

            Assumptions:
            1. The sub-folder "Microsoft SQL Server" exists in $env:ProgramFiles,
                even if SQL was installed to a non-default path. This has been
                verified on SQL 2008R2 and SQL 2012. Further verification may be needed.
            2. The discovery report displays installed components for the version of SQL
                Server associated with setup.exe, along with installed components of all
                lesser versions of SQL Server that are installed.

        .PARAMETER ComputerName
            The target computer. If the target is not localhost, it must have PowerShell remoting enabled.

            Note that this is not the SqlInstance, but rather the ComputerName

        .PARAMETER Credential
            Allows you to login to servers using alternative credentials. To use:

            $cred = Get-Credential, then pass $cred object to the -Credential parameter.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Feature, Component
            Author: Chrissy LeMaire (@cl)
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaSqlFeature

        .EXAMPLE
            Get-DbaSqlFeature -ComputerName sql2017, sql2016, sql2005

            Gets all SQL Server features for all instances on sql2017, sql2016 and sql2005.

        .EXAMPLE
            Get-DbaSqlFeature -Verbose

            Gets all SQL Server features for all instances on localhost. Outputs to screen if no instances are found.

        .EXAMPLE
            Get-DbaSqlFeature -ComputerName sql2017 -Credential (Get-Credential ad\sqladmin)

            Gets all SQL Server features for all instances on sql2017 using the ad\sqladmin credential (which has access to the Windows Server).
    #>
    [CmdletBinding()]
    Param (
        [parameter(ValueFromPipeline)]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [switch]$EnableException
    )

    begin {
        $scriptblock = {
            $setup = Get-ChildItem -Recurse -Include setup.exe -Path "$env:ProgramFiles\Microsoft SQL Server" -ErrorAction SilentlyContinue |
                Where-Object { $_.FullName -match 'Setup Bootstrap\\SQL' -or $_.FullName -match 'Bootstrap\\Release\\Setup.exe' -or $_.FullName -match 'Bootstrap\\Setup.exe' } |
                Sort-Object FullName -Descending | Select-Object -First 1
            if ($setup) {
                $null = Start-Process -FilePath $setup.FullName -ArgumentList "/Action=RunDiscovery /q" -Wait
                $parent = Split-Path (Split-Path $setup.Fullname)
                $xmlfile = Get-ChildItem -Recurse -Include SqlDiscoveryReport.xml -Path $parent | Sort-Object LastWriteTime -Descending | Select-Object -First 1

                if ($xmlfile) {
                    $xml = [xml](Get-Content -Path $xmlfile)
                    $xml.ArrayOfDiscoveryInformation.DiscoveryInformation
                }
            }
        }
    }

    process {
        foreach ($computer in $ComputerName) {
            try {
                $results = Invoke-Command2 -ComputerName $Computer -ScriptBlock $scriptblock -Credential $Credential -Raw

                if (-not $results) {
                    Write-Message -Level Verbose -Message "No features found on $computer"
                }

                foreach ($result in $results) {
                    [pscustomobject]@{
                        ComputerName = $computer
                        Product      = $result.Product
                        Instance     = $result.Instance
                        InstanceID   = $result.InstanceID
                        Feature      = $result.Feature
                        Language     = $result.Language
                        Edition      = $result.Edition
                        Version      = $result.Version
                        Clustered    = $result.Clustered
                        Configured   = $result.Configured
                    }
                }
            }
            catch {
                Stop-Function -Continue -ErrorRecord $_ -Message "Failure"
            }
        }
    }
}
tools\dbatools\functions\Get-DbaSqlInstanceProperty.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaSqlInstanceProperty {
    <#
        .SYNOPSIS
            Gets SQL Server instance properties of one or more instance(s) of SQL Server.

        .DESCRIPTION
            The Get-DbaSqlInstanceProperty command gets SQL Server instance properties from the SMO object sqlserver.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER InstanceProperty
            SQL Server instance property(ies) to include.

        .PARAMETER ExcludeInstanceProperty
            SQL Server instance property(ies) to exclude.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Instance, Configure, Configuration
            Author: Klaas Vandenberghe (@powerdbaklaas)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaSqlInstanceProperty

        .EXAMPLE
            Get-DbaSqlInstanceProperty -SqlInstance localhost

            Returns SQL Server instance properties on the local default SQL Server instance

        .EXAMPLE
            Get-DbaSqlInstanceProperty -SqlInstance sql2, sql4\sqlexpress

            Returns SQL Server instance properties on default instance on sql2 and sqlexpress instance on sql4

        .EXAMPLE
            'sql2','sql4' | Get-DbaSqlInstanceProperty

            Returns SQL Server instance properties on sql2 and sql4

        .EXAMPLE
            Get-DbaSqlInstanceProperty -SqlInstance sql2,sql4 -InstanceProperty DefaultFile

            Returns SQL Server instance property DefaultFile on instance sql2 and sql4

        .EXAMPLE
            Get-DbaSqlInstanceProperty -SqlInstance sql2,sql4 -ExcludeInstanceProperty DefaultFile

            Returns all SQL Server instance properties except DefaultFile on instance sql2 and sql4

        .EXAMPLE
            $cred = Get-Credential sqladmin
            Get-DbaSqlInstanceProperty -SqlInstance sql2 -SqlCredential $cred

            Connects using sqladmin credential and returns SQL Server instance properties from sql2
    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$InstanceProperty,
        [object[]]$ExcludeInstanceProperty,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            try {
                $infoProperties = $server.Information.Properties

                if ($InstanceProperty) {
                    $infoProperties = $infoProperties | Where-Object Name -In $InstanceProperty
                }
                if ($ExcludeInstanceProperty) {
                    $infoProperties = $infoProperties | Where-Object Name -NotIn $ExcludeInstanceProperty
                }
                foreach ($prop in $infoProperties) {
                    Add-Member -Force -InputObject $prop -MemberType NoteProperty -Name ComputerName -Value $server.ComputerName
                    Add-Member -Force -InputObject $prop -MemberType NoteProperty -Name InstanceName -Value $server.ServiceName
                    Add-Member -Force -InputObject $prop -MemberType NoteProperty -Name SqlInstance -Value $server.DomainInstanceName
                    Add-Member -Force -InputObject $prop -MemberType NoteProperty -Name PropertyType -Value 'Information'
                    Select-DefaultView -InputObject $prop -Property ComputerName, InstanceName, SqlInstance, Name, Value, PropertyType
                }
            }
            catch {
                Stop-Function -Message "Issue gathering information properties for $instance." -Target $instance -ErrorRecord $_ -Continue
            }

            try {
                $userProperties = $server.UserOptions.Properties

                if ($InstanceProperty) {
                    $userProperties = $userProperties | Where-Object Name -In $InstanceProperty
                }
                if ($ExcludeInstanceProperty) {
                    $userProperties = $userProperties | Where-Object Name -NotIn $ExcludeInstanceProperty
                }
                foreach ($prop in $userProperties) {
                    Add-Member -Force -InputObject $prop -MemberType NoteProperty -Name ComputerName -Value $server.ComputerName
                    Add-Member -Force -InputObject $prop -MemberType NoteProperty -Name InstanceName -Value $server.ServiceName
                    Add-Member -Force -InputObject $prop -MemberType NoteProperty -Name SqlInstance -Value $server.DomainInstanceName
                    Add-Member -Force -InputObject $prop -MemberType NoteProperty -Name PropertyType -Value 'UserOption'
                    Select-DefaultView -InputObject $prop -Property ComputerName, InstanceName, SqlInstance, Name, Value, PropertyType
                }
            }
            catch {
                Stop-Function -Message "Issue gathering user options for $instance." -Target $instance -ErrorRecord $_ -Continue
            }

            try {
                $settingProperties = $server.Settings.Properties

                if ($InstanceProperty) {
                    $settingProperties = $settingProperties | Where-Object Name -In $InstanceProperty
                }
                if ($ExcludeInstanceProperty) {
                    $settingProperties = $settingProperties | Where-Object Name -NotIn $ExcludeInstanceProperty
                }
                foreach ($prop in $settingProperties) {
                    Add-Member -Force -InputObject $prop -MemberType NoteProperty -Name ComputerName -Value $server.ComputerName
                    Add-Member -Force -InputObject $prop -MemberType NoteProperty -Name InstanceName -Value $server.ServiceName
                    Add-Member -Force -InputObject $prop -MemberType NoteProperty -Name SqlInstance -Value $server.DomainInstanceName
                    Add-Member -Force -InputObject $prop -MemberType NoteProperty -Name PropertyType -Value 'Setting'
                    Select-DefaultView -InputObject $prop -Property ComputerName, InstanceName, SqlInstance, Name, Value, PropertyType
                }
            }
            catch {
                Stop-Function -Message "Issue gathering settings for $instance." -Target $instance -ErrorRecord $_ -Continue
            }
        }
    }
}
tools\dbatools\functions\Get-DbaSqlInstanceUserOption.ps1
function Get-DbaSqlInstanceUserOption {
    <#
        .SYNOPSIS
            Gets SQL Instance user options of one or more instance(s) of SQL Server.

        .DESCRIPTION
            The Get-DbaSqlInstanceUserOption command gets SQL Instance user options from the SMO object sqlserver.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to.
            This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Instance, Configure, UserOption
            Author: Klaas Vandenberghe (@powerdbaklaas)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaSqlInstanceUserOption

        .EXAMPLE
            Get-DbaSqlInstanceUserOption -SqlInstance localhost

            Returns SQL Instance user options on the local default SQL Server instance

        .EXAMPLE
            Get-DbaSqlInstanceUserOption -SqlInstance sql2, sql4\sqlexpress

            Returns SQL Instance user options on default instance on sql2 and sqlexpress instance on sql4

        .EXAMPLE
            'sql2','sql4' | Get-DbaSqlInstanceUserOption

            Returns SQL Instance user options on sql2 and sql4
    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            $props = $server.useroptions.properties
            foreach ($prop in $props) {
                Add-Member -Force -InputObject $prop -MemberType NoteProperty -Name ComputerName -Value $server.ComputerName
                Add-Member -Force -InputObject $prop -MemberType NoteProperty -Name InstanceName -Value $server.ServiceName
                Add-Member -Force -InputObject $prop -MemberType NoteProperty -Name SqlInstance -Value $server.DomainInstanceName
                Select-DefaultView -InputObject $prop -Property ComputerName, InstanceName, SqlInstance, Name, Value
            }
        }
    }
}
tools\dbatools\functions\Get-DbaSqlManagementObject.ps1
function Get-DbaSqlManagementObject {
    <#
        .SYNOPSIS
            Gets SQL Mangaement Object versions installed on the machine.

        .DESCRIPTION
            The Get-DbaSqlManagementObject returns an object with the Version and the
            Add-Type Load Template for each version on the server.

        .PARAMETER ComputerName
            The name of the target you would like to check

        .PARAMETER Credential
            This command uses Windows credentials. This parameter allows you to connect remotely as a different user.

        .PARAMETER VersionNumber
            This is the specific version number you are looking for. The function will look
            for that version only.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: SMO
            Author: Ben Miller (@DBAduck - http://dbaduck.com)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaSqlManagementObject

        .EXAMPLE
            Get-DbaSqlManagementObject

            Returns all versions of SMO on the computer

        .EXAMPLE
            Get-DbaSqlManagementObject -VersionNumber 13

            Returns just the version specified. If the version does not exist then it will return nothing.

    #>
    [CmdletBinding()]
    param (
        [parameter(ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer", "SqlInstance")]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]
        $Credential,
        [int]$VersionNumber,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        if (!$VersionNumber) {
            $VersionNumber = 0
        }
        $scriptblock = {
            $VersionNumber = [int]$args[0]

            Write-Verbose -Message "Checking currently loaded SMO version"
            $loadedversion = [AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.Fullname -like "Microsoft.SqlServer.SMO,*" }
            if ($loadedversion) {
                $loadedversion = $loadedversion | ForEach-Object {
                    if ($_.Location -match "__") {
                        ((Split-Path (Split-Path $_.Location) -Leaf) -split "__")[0]
                    }
                    else {
                        ((Get-ChildItem -Path $_.Location).VersionInfo.ProductVersion)
                    }
                }
            }

            Write-Verbose -Message "Looking for included smo library"
            $localversion = [version](Get-ChildItem -Path "$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.Smo.dll").VersionInfo.ProductVersion

            foreach ($version in $localversion) {
                if ($VersionNumber -eq 0) {
                    Write-Verbose -Message "Did not pass a version"
                    [PSCustomObject]@{
                        ComputerName = $env:COMPUTERNAME
                        Version      = $localversion
                        Loaded       = $loadedversion -contains $localversion
                        LoadTemplate = "Add-Type -Path $("$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.Smo.dll")"
                    }
                }
                else {
                    Write-Verbose -Message "Passed version $VersionNumber, looking for that specific version"
                    if ($localversion.ToString().StartsWith("$VersionNumber.")) {
                        Write-Verbose -Message "Found the Version $VersionNumber"
                        [PSCustomObject]@{
                            ComputerName = $env:COMPUTERNAME
                            Version      = $localversion
                            Loaded       = $loadedversion -contains $localversion
                            LoadTemplate = "Add-Type -Path $("$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.Smo.dll")"
                        }
                    }
                }
            }

            Write-Verbose -Message "Looking for SMO in the Global Assembly Cache"
            $smolist = (Get-ChildItem -Path "$env:SystemRoot\assembly\GAC_MSIL\Microsoft.SqlServer.Smo" | Sort-Object Name -Descending).Name

            foreach ($version in $smolist) {
                $array = $version.Split("__")
                if ($VersionNumber -eq 0) {
                    Write-Verbose -Message "Did not pass a version, looking for all versions"
                    $currentversion = $array[0]
                    [PSCustomObject]@{
                        ComputerName = $env:COMPUTERNAME
                        Version      = $currentversion
                        Loaded       = $loadedversion -contains $currentversion
                        LoadTemplate = "Add-Type -AssemblyName `"Microsoft.SqlServer.Smo, Version=$($array[0]), Culture=neutral, PublicKeyToken=89845dcd8080cc91`""
                    }
                }
                else {
                    Write-Verbose -Message "Passed version $VersionNumber, looking for that specific version"
                    if ($array[0].StartsWith("$VersionNumber.")) {
                        Write-Verbose -Message "Found the Version $VersionNumber"
                        $currentversion = $array[0]
                        [PSCustomObject]@{
                            ComputerName = $env:COMPUTERNAME
                            Version      = $currentversion
                            Loaded       = $loadedversion -contains $currentversion
                            LoadTemplate = "Add-Type -AssemblyName `"Microsoft.SqlServer.Smo, Version=$($array[0]), Culture=neutral, PublicKeyToken=89845dcd8080cc91`""
                        }
                    }
                }
            }
        }
    }

    process {
        foreach ($computer in $ComputerName.ComputerName) {
            try {
                Write-Message -Level Verbose -Message "Executing scriptblock against $computer"
                Invoke-Command2 -ComputerName $computer -ScriptBlock $scriptblock -Credential $Credential -ArgumentList $VersionNumber -ErrorAction Stop
            }
            catch {
                Stop-Function -Continue -Message "Failure" -ErrorRecord $_ -Target $ComputerName
            }
        }
    }
}
tools\dbatools\functions\Get-DbaSqlModule.ps1
function Get-DbaSqlModule {
    <#
    .SYNOPSIS
    Displays all objects in sys.sys_modules after specified modification date.  Works on SQL Server 2008 and above.

    .DESCRIPTION
    Quickly find modules (Stored Procs, Functions, Views, Constraints, Rules, Triggers, etc) that have been modified in a database, or across all databases.
    Results will exclude the module definition, but can be queried explicitly.

    .PARAMETER SqlInstance
    Allows you to specify a comma separated list of servers to query.

    .PARAMETER SqlCredential
    Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

    .PARAMETER Database
    The database(s) to process. If unspecified, all databases will be processed.

    .PARAMETER ExcludeDatabase
    The database(s) to exclude.

    .PARAMETER ModifiedSince
    DateTime value to use as minimum modified date of module.

    .PARAMETER Type
    Limit by specific type of module. Valid choices include: View, TableValuedFunction, DefaultConstraint, StoredProcedure, Rule, InlineTableValuedFunction, Trigger, ScalarFunction

    .PARAMETER NoSystemDb
    Allows you to suppress output on system databases

    .PARAMETER NoSystemObjects
    Allows you to suppress output on system objects

    .PARAMETER EnableException
    By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
    Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
    Author: Brandon Abshire, netnerds.net
    Tags: StoredProcedure, Trigger

    Website: https://dbatools.io
    Copyright: (C) Chrissy LeMaire, [email protected]
    License: MIT https://opensource.org/licenses/MIT

    .LINK
    https://dbatools.io/Get-DbaSqlModule

    .EXAMPLE
    Get-DbaSqlModule -SqlServer sql2008, sqlserver2012
    Return all modules for servers sql2008 and sqlserver2012 sorted by Database, Modify_Date ASC.

    .EXAMPLE
    Get-DbaSqlModule -SqlServer sql2008, sqlserver2012 | Select *
    Shows hidden definition column (informative wall of text).

    .EXAMPLE
    Get-DbaSqlModule -SqlServer sql2008 -Database TestDB -ModifiedSince "01/01/2017 10:00:00 AM"
    Return all modules on server sql2008 for only the TestDB database with a modified date after 01/01/2017 10:00:00 AM.

    .EXAMPLE
    Get-DbaSqlModule -SqlServer sql2008 -Type View, Trigger, ScalarFunction
    Return all modules on server sql2008 for all databases that are triggers, views or scalar functions.
#>
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [datetime]$ModifiedSince = "01/01/1900",
        [ValidateSet("View", "TableValuedFunction", "DefaultConstraint", "StoredProcedure", "Rule", "InlineTableValuedFunction", "Trigger", "ScalarFunction")]
        [string[]]$Type,
        [switch]$NoSystemDb,
        [switch]$NoSystemObjects,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {

        $types = @()

        foreach ($t in $type) {
            if ($t -eq "View") { $types += "VIEW" }
            if ($t -eq "TableValuedFunction") { $types += "SQL_TABLE_VALUED_FUNCTION" }
            if ($t -eq "DefaultConstraint") { $types += "DEFAULT_CONSTRAINT" }
            if ($t -eq "StoredProcedure") { $types += "SQL_STORED_PROCEDURE" }
            if ($t -eq "Rule") { $types += "RULE" }
            if ($t -eq "InlineTableValuedFunction") { $types += "SQL_INLINE_TABLE_VALUED_FUNCTION" }
            if ($t -eq "Trigger") { $types += "SQL_TRIGGER" }
            if ($t -eq "ScalarFunction") { $types += "SQL_SCALAR_FUNCTION" }
        }


        $sql = "SELECT  DB_NAME() AS DatabaseName,
        so.name AS ModuleName,
        so.object_id ,
        SCHEMA_NAME(so.schema_id) AS SchemaName ,
        so.parent_object_id ,
        so.type ,
        so.type_desc ,
        so.create_date ,
        so.modify_date ,
        so.is_ms_shipped ,
        sm.definition,
         OBJECTPROPERTY(so.object_id, 'ExecIsStartUp') as startup
        FROM sys.sql_modules sm
        LEFT JOIN sys.objects so ON sm.object_id = so.object_id
        WHERE so.modify_date >= '$($ModifiedSince)'"
        if ($NoSystemObjects) {
            $sql += "`n AND so.is_ms_shipped = 0"
        }
        if ($Type) {
            $sqltypes = $types -join "','"
            $sql += " AND type_desc in ('$sqltypes')"
        }
        $sql += "`n ORDER BY so.modify_date"
    }

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $databases = Get-DbaDatabase -SqlInstance $server

            if ($Database) {
                $databases = $databases | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }


            foreach ($db in $databases) {

                Write-Message -Level Verbose -Message "Processing $db on $instance"

                if ($db.IsAccessible -eq $false) {
                    Stop-Function -Message "The database $db is not accessible. Skipping database." -Target $db -Continue
                }

                foreach ($row in $server.Query($sql, $db.name)) {
                    [PSCustomObject]@{
                        ComputerName  = $server.ComputerName
                        InstanceName  = $server.ServiceName
                        SqlInstance   = $server.DomainInstanceName
                        Database      = $row.DatabaseName
                        Name          = $row.ModuleName
                        ObjectID      = $row.object_id
                        SchemaName    = $row.SchemaName
                        Type          = $row.type_desc
                        CreateDate    = $row.create_date
                        ModifyDate    = $row.modify_date
                        IsMsShipped   = $row.is_ms_shipped
                        ExecIsStartUp = $row.startup
                        Definition    = $row.definition
                    } | Select-DefaultView -ExcludeProperty Definition
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaSqlProductKey.ps1
function Get-DbaSqlProductKey {
    <#
.SYNOPSIS
Gets SQL Server Product Keys from local or destination SQL Servers. Works with SQL Server 2005-2016

.DESCRIPTION
Using a string of servers, a text file, or Central Management Server to provide a list of servers, this script will go to each server and get the product key for all installed instances. Clustered instances are supported as well. Requires regular user access to the SQL instances, SMO installed locally, Remote Registry enabled and accessible by the account running the script.

Uses key decoder by Jakob Bindslet (http://goo.gl/1jiwcB)

.PARAMETER SqlInstances
A comma separated list of servers. This can be the NetBIOS name, IP, or SQL instance name

.PARAMETER SqlCms
Compiles list of servers to inventory using all servers stored within a Central Management Server. Requires having SQL Management Studio installed.

.PARAMETER ServersFromFile
Uses a text file as input. The file must be formatted as such:
sqlserver1
sqlserver2

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.NOTES
Author: Chrissy LeMaire (@cl), netnerds.net
Tags: SQL, Product Key

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Get-DbaSqlProductKey

.EXAMPLE
Get-DbaSqlProductKey winxp, sqlservera, sqlserver2014a, win2k8
Gets SQL Server versions, editions and product keys for all instances within each server or workstation.

.EXAMPLE
Get-DbaSqlProductKey -SqlCms sqlserver01
Gets SQL Server versions, editions and product keys for all instances within sqlserver01's Central Management Server

.EXAMPLE
Get-DbaSqlProductKey -ServersFromFile C:\Scripts\servers.txt
Gets SQL Server versions, editions and product keys for all instances listed within C:\Scripts\servers.txt
#>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    Param (
        [parameter(Position = 0)]
        [Alias("ServerInstance", "SqlServer")]
        [string[]]$SqlInstances,
        # Central Management Server

        [string]$SqlCms,
        # File with one server per line

        [string]$ServersFromFile,
        [PSCredential]$SqlCredential
    )

    BEGIN {

        Function Unlock-SqlInstanceKey {
            [CmdletBinding()]
            param (
                [Parameter(Mandatory = $true)]
                [byte[]]$data,
                [int]$version
            )
            try {
                if ($version -ge 11) { $binArray = ($data)[0..66] }
                else { $binArray = ($data)[52..66] }
                $charsArray = "B", "C", "D", "F", "G", "H", "J", "K", "M", "P", "Q", "R", "T", "V", "W", "X", "Y", "2", "3", "4", "6", "7", "8", "9"
                for ($i = 24; $i -ge 0; $i--) {
                    $k = 0
                    for ($j = 14; $j -ge 0; $j--) {
                        $k = $k * 256 -bxor $binArray[$j]
                        $binArray[$j] = [math]::truncate($k / 24)
                        $k = $k % 24
                    }
                    $productKey = $charsArray[$k] + $productKey
                    if (($i % 5 -eq 0) -and ($i -ne 0)) {
                        $productKey = "-" + $productKey
                    }
                }
            }
            catch { $productkey = "Cannot decode product key." }
            return $productKey
        }
    }

    PROCESS {

        if ($SqlCms) {
            if ($null -eq [Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Management.RegisteredServers"))
            { throw "Can't load CMS assemblies. You must have SQL Server Management Studio installed to use the -SqlCms switch." }

            Write-Verbose "Gathering SQL Servers names from Central Management Server"
            $server = Connect-SqlInstance -SqlInstance $SqlCms -SqlCredential $SqlCredential
            $sqlconnection = $server.ConnectionContext.SqlConnectionObject

            try { $cmstore = New-Object Microsoft.SqlServer.Management.RegisteredServers.RegisteredServersStore($sqlconnection) }
            catch { throw "Cannot access Central Management Server" }
            $dbstore = $cmstore.DatabaseEngineServerGroup
            $SqlInstances = $dbstore.GetDescendantRegisteredServers().servername
            # Add the CM server itself, which can't be stored in the CM server.
            $servers += $SqlCms
            $basenames = @()
            foreach ($server in $SqlInstances) { $basenames += $server.Split("\")[0] }
            $SqlInstances = $basenames | Get-Unique
        }

        If ($ServersFromFile) {
            if ((Test-Path $ServersFromFile) -eq $false) { throw "Could not find file: $ServersFromFile" }
            $SqlInstances = Get-Content $ServersFromFile
        }

        if ([string]::IsNullOrEmpty($SqlInstances)) { $SqlInstances = $env:computername }

        $basepath = "SOFTWARE\Microsoft\Microsoft SQL Server"
        # Loop through each server
        $objectCollection = @()
        foreach ($servername in $SqlInstances) {
            $servername = $servername.Split("\")[0]

            if ($servername -eq "." -or $servername -eq "localhost" -or $servername -eq $env:computername) {
                $localmachine = [Microsoft.Win32.RegistryHive]::LocalMachine
                $defaultview = [Microsoft.Win32.RegistryView]::Default
                $reg = [Microsoft.Win32.RegistryKey]::OpenBaseKey($localmachine, $defaultview)
            }
            else {
                # Get IP for remote registry access. It's the most reliable.
                try { $ipaddr = ([System.Net.Dns]::GetHostAddresses($servername)).IPAddressToString }
                catch { Write-Warning "Can't resolve $servername. Skipping."; continue }

                try {
                    $reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("LocalMachine", $ipaddr)
                }
                catch { Write-Warning "Can't access registry for $servername. Is the Remote Registry service started?"; continue }
            }

            $instances = $reg.OpenSubKey("$basepath\Instance Names\SQL", $false)
            if ($instances -eq $null) { Write-Warning "No instances found on $servername. Skipping."; continue }
            # Get Product Keys for all instances on the server.
            foreach ($instance in $instances.GetValueNames()) {
                if ($instance -eq "MSSQLSERVER") { $SqlInstance = $servername }
                else { $SqlInstance = "$servername\$instance" }

                $subkeys = $reg.OpenSubKey("$basepath", $false)
                $instancekey = $subkeys.GetSubKeynames() | Where-Object { $_ -like "*.$instance" }
                if ($null -eq $instancekey) { $instancekey = $instance } # SQL 2k5

                # Cluster instance hostnames are required for SMO connection
                $cluster = $reg.OpenSubKey("$basepath\$instancekey\Cluster", $false)
                if ($cluster -ne $null) {
                    $clustername = $cluster.GetValue("ClusterName")
                    if ($instance -eq "MSSQLSERVER") { $SqlInstance = $clustername }
                    else { $SqlInstance = "$clustername\$instance" }
                }

                Write-Verbose "Connecting to $SqlInstance"
                try { $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential }
                catch { Write-Warning "Can't connect to $SqlInstance or access denied. Skipping."; continue }

                $servicePack = $server.ProductLevel
                Write-Debug "$servername $instance version is $($server.VersionMajor)"
                switch ($server.VersionMajor) {
                    9 {
                        $sqlversion = "SQL Server 2005 $servicePack"
                        $findkeys = $reg.OpenSubKey("$basepath\90\ProductID", $false)
                        foreach ($findkey in $findkeys.GetValueNames()) {
                            if ($findkey -like "DigitalProductID*") { $key = "$basepath\90\ProductID\$findkey" }
                        }
                    }
                    10 {
                        $sqlversion = "SQL Server 2008 $servicePack"
                        $key = "$basepath\MSSQL10"
                        if ($server.VersionMinor -eq 50) { $key += "_50"; $sqlversion = "SQL Server 2008 R2 $servicePack" }
                        $key += ".$instance\Setup\DigitalProductID"
                    }
                    11 { $key = "$basepath\110\Tools\Setup\DigitalProductID"; $sqlversion = "SQL Server 2012 $servicePack" }
                    12 { $key = "$basepath\120\Tools\Setup\DigitalProductID"; $sqlversion = "SQL Server 2014 $servicePack" }
                    13 { $key = "$basepath\130\Tools\Setup\DigitalProductID"; $sqlversion = "SQL Server 2016 $servicePack" }
                    default { Write-Warning "SQL version not currently supported."; continue }
                }
                if ($server.Edition -notlike "*Express*") {
                    try {
                        $subkey = Split-Path $key; $binaryvalue = Split-Path $key -leaf
                        $binarykey = $($reg.OpenSubKey($subkey)).GetValue($binaryvalue)
                    }
                    catch { $sqlkey = "Could not connect." }
                    try { $sqlkey = Unlock-SqlInstanceKey $binarykey $server.VersionMajor }
                    catch { }
                }
                else { $sqlkey = "SQL Server Express Edition" }
                $server.ConnectionContext.Disconnect()

                $object = New-Object PSObject -Property @{
                    "SQL Instance" = $SqlInstance
                    "SQL Version"  = $sqlversion
                    "SQL Edition"  = $server.Edition
                    "Product Key"  = $sqlkey
                }
                $objectCollection += $object
            }
            $reg.Close()
        }
        $objectCollection | Select "SQL Instance", "SQL Version", "SQL Edition", "Product Key"
    }

    END {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-SqlServerKey
    }
}
tools\dbatools\functions\Get-DbaSqlRegistryRoot.ps1
function Get-DbaSqlRegistryRoot {
    <#
.SYNOPSIS
Uses SQL WMI to find the Registry Root of each SQL Server instance on a computer

.DESCRIPTION
Uses SQL WMI to find the Registry Root of each SQL Server instance on a computer

.PARAMETER ComputerName
The target computer. This is not a SQL Server service, though if you pass a named SQL instance, it'll parse properly down to the computer name

.PARAMETER Credential
Allows you to login to $ComputerName using alternative Windows credentials

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Configuration, Registry

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
Get-DbaSqlRegistryRoot
Gets the registry root for all instances on localhost

.EXAMPLE
Get-DbaSqlRegistryRoot -ComputerName server1

Gets the registry root for all instances on server1

#>
    [CmdletBinding()]
    param (
        [parameter(ValueFromPipeline)]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($computer in $computername) {
            Write-Message -Level Verbose -Message "Connecting to SQL WMI on $($computer.ComputerName)"
            try {
                $sqlwmis = Invoke-ManagedComputerCommand -ComputerName $computer.ComputerName -ScriptBlock { $wmi.Services } -Credential $Credential -ErrorAction Stop | Where-Object DisplayName -match "SQL Server \("
            }
            catch {
                Stop-Function -Message $_ -Target $sqlwmi -Continue
            }

            foreach ($sqlwmi in $sqlwmis) {

                $regroot = ($sqlwmi.AdvancedProperties | Where-Object Name -eq REGROOT).Value
                $vsname = ($sqlwmi.AdvancedProperties | Where-Object Name -eq VSNAME).Value
                $instancename = $sqlwmi.DisplayName.Replace('SQL Server (', '').Replace(')', '') # Don't clown, I don't know regex :(

                if ([System.String]::IsNullOrEmpty($regroot)) {
                    $regroot = $sqlwmi.AdvancedProperties | Where-Object { $_ -match 'REGROOT' }
                    $vsname = $sqlwmi.AdvancedProperties | Where-Object { $_ -match 'VSNAME' }

                    if (![System.String]::IsNullOrEmpty($regroot)) {
                        $regroot = ($regroot -Split 'Value\=')[1]
                        $vsname = ($vsname -Split 'Value\=')[1]
                    }
                    else {
                        Write-Message -Level Warning -Message "Can't find instance $vsname on $env:COMPUTERNAME"
                        return
                    }
                }

                # vsname takes care of clusters
                if ([System.String]::IsNullOrEmpty($vsname)) {
                    $vsname = $computer
                    if ($instancename -ne "MSSQLSERVER") {
                        $vsname = "$computer\$instancename"
                    }
                }

                Write-Message -Level Verbose -Message "Regroot: $regroot"
                Write-Message -Level Verbose -Message "InstanceName: $instancename"
                Write-Message -Level Verbose -Message "VSNAME: $vsname"

                [pscustomobject]@{
                    ComputerName = $computer.ComputerName
                    InstanceName = $instancename
                    SqlInstance  = $vsname
                    Hive         = "HKLM"
                    Path         = $regroot
                    RegistryRoot = "HKLM:\$regroot"
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaSqlService.ps1
function Get-DbaSqlService {
    <#
        .SYNOPSIS
            Gets the SQL Server related services on a computer.

        .DESCRIPTION
            Gets the SQL Server related services on one or more computers.

        .PARAMETER ComputerName
            The SQL Server (or server in general) that you're connecting to. This command handles named instances.

        .PARAMETER InstanceName
            Only returns services that belong to the specific instances.

        .PARAMETER Credential
            Credential object used to connect to the computer as a different user.

        .PARAMETER Type
            Use -Type to collect only services of the desired SqlServiceType.
            Can be one of the following: "Agent","Browser","Engine","FullText","SSAS","SSIS","SSRS"

        .PARAMETER ServiceName
            Can be used to specify service names explicitly, without looking for service types/instances.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Service, SqlServer, Instance, Connect
            Author: Klaas Vandenberghe ( @PowerDBAKlaas )

            Requires Local Admin rights on destination computer(s).

            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaSqlService

        .EXAMPLE
            Get-DbaSqlService -ComputerName sqlserver2014a

            Gets the SQL Server related services on computer sqlserver2014a.

        .EXAMPLE
            'sql1','sql2','sql3' | Get-DbaSqlService

            Gets the SQL Server related services on computers sql1, sql2 and sql3.

        .EXAMPLE
            Get-DbaSqlService -ComputerName sql1,sql2 | Out-GridView

            Gets the SQL Server related services on computers sql1 and sql2, and shows them in a grid view.

        .EXAMPLE
            Get-DbaSqlService -ComputerName $MyServers -Type SSRS

            Gets the SQL Server related services of type "SSRS" (Reporting Services) on computers in the variable MyServers.

        .EXAMPLE
            $services = Get-DbaSqlService -ComputerName sql1 -Type Agent,Engine
            $services.ChangeStartMode('Manual')

            Gets the SQL Server related services of types Sql Agent and DB Engine on computer sql1 and changes their startup mode to 'Manual'.

        .EXAMPLE
            (Get-DbaSqlService sql1 -Type Engine).Restart($true)

            Calls a Restart method for each Engine service on computer sql1 with -Force option.
    #>
    [CmdletBinding(DefaultParameterSetName = "Search")]
    Param (
        [parameter(ValueFromPipeline = $true, Position = 1)]
        [Alias("cn", "host", "Server")]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [Parameter(ParameterSetName = "Search")]
        [Alias("Instance")]
        [string[]]$InstanceName,
        [PSCredential]$Credential,
        [Parameter(ParameterSetName = "Search")]
        [ValidateSet("Agent", "Browser", "Engine", "FullText", "SSAS", "SSIS", "SSRS")]
        [string[]]$Type,
        [Parameter(ParameterSetName = "ServiceName")]
        [string[]]$ServiceName,
        [Alias('Silent')]
        [switch]$EnableException
    )

    BEGIN {
        #Dictionary to transform service type IDs into the names from Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer.Services.Type
        $ServiceIdMap = @(
            @{ Name = "Engine"; Id = 1 },
            @{ Name = "Agent"; Id = 2 },
            @{ Name = "FullText"; Id = 3, 9 },
            @{ Name = "SSIS"; Id = 4 },
            @{ Name = "SSAS"; Id = 5 },
            @{ Name = "SSRS"; Id = 6 },
            @{ Name = "Browser"; Id = 7 },
            @{ Name = "Unknown"; Id = 8 }
        )
        if ($PsCmdlet.ParameterSetName -match 'Search') {
            if ($Type) {
                $searchClause = ""
                foreach ($itemType in $Type) {
                    foreach ($id in ($ServiceIdMap | Where-Object { $_.Name -eq $itemType }).Id) {
                        if ($searchClause) { $searchClause += ' OR ' }
                        $searchClause += "SQLServiceType = $id"
                    }
                }
            }
            else {
                $searchClause = "SQLServiceType > 0"
            }
        }
        elseif ($PsCmdlet.ParameterSetName -match 'ServiceName') {
            if ($ServiceName) {
                $searchClause = ""
                foreach ($sn in $ServiceName) {
                    if ($searchClause) { $searchClause += ' OR ' }
                    $searchClause += "ServiceName = '$sn'"
                }
            }
            else {
                $searchClause = "SQLServiceType > 0"
            }
        }
    }
    PROCESS {
        foreach ($Computer in $ComputerName.ComputerName) {
            $Server = Resolve-DbaNetworkName -ComputerName $Computer -Credential $credential
            if ($Server.FullComputerName) {
                $Computer = $server.FullComputerName
                Write-Message -Level VeryVerbose -Message "Getting SQL Server namespace on $Computer" -Target $Computer
                try { $namespaces = Get-DbaCmObject -ComputerName $Computer -NameSpace root\Microsoft\SQLServer -Query "Select Name FROM __NAMESPACE WHERE Name Like 'ComputerManagement%'" -EnableException -Credential $credential | Sort-Object Name -Descending }
                catch { }
                if ($namespaces) {
                    $servicesTemp = @()

                    ForEach ($namespace in $namespaces) {
                        try {
                            Write-Message -Level Verbose -Message "Getting Cim class SqlService in Namespace $($namespace.Name) on $Computer." -Target $Computer
                            foreach ($service in (Get-DbaCmObject -ComputerName $Computer -Namespace "root\Microsoft\SQLServer\$($namespace.Name)" -Query "SELECT * FROM SqlService WHERE $searchClause" -EnableException -Credential $credential)) {
                                $servicesTemp += New-Object PSObject -Property @{
                                    Name      = $service.ServiceName
                                    Namespace = $namespace.Name
                                    Service   = $service
                                }
                            }
                        }
                        catch {
                            Write-Message -Level Verbose -EnableException $EnableException.ToBool() -Message "Failed to acquire services from namespace $($namespace.Name)." -Target $Computer -ErrorRecord $_
                        }
                    }

                    $services = ($servicesTemp | Group-Object Name | ForEach-Object { $_.Group | Sort-Object Namespace -Descending | Select-Object -First 1 }).Service

                    if ($services) {
                        Write-Message -Level Verbose -Message "Creating output objects"
                        ForEach ($service in $services) {
                            Add-Member -Force -InputObject $service -MemberType NoteProperty -Name ComputerName -Value $service.HostName
                            Add-Member -Force -InputObject $service -MemberType NoteProperty -Name ServiceType -Value ($ServiceIdMap | Where-Object { $_.Id -contains $service.SQLServiceType }).Name
                            Add-Member -Force -InputObject $service -MemberType NoteProperty -Name State -Value $(switch ($service.State) { 1 { 'Stopped' } 2 { 'Start Pending' }  3 { 'Stop Pending' } 4 { 'Running' } })
                            Add-Member -Force -InputObject $service -MemberType NoteProperty -Name StartMode -Value $(switch ($service.StartMode) { 1 { 'Unknown' } 2 { 'Automatic' }  3 { 'Manual' } 4 { 'Disabled' } })

                            if ($service.ServiceName -in ("MSSQLSERVER", "SQLSERVERAGENT", "ReportServer", "MSSQLServerOLAPService")) {
                                $instance = "MSSQLSERVER"
                            }
                            else {
                                if ($service.ServiceType -in @("Agent", "Engine", "SSRS", "SSAS")) {
                                    if ($service.ServiceName.indexof('$') -ge 0) {
                                        $instance = $service.ServiceName.split('$')[1]
                                    }
                                    else {
                                        $instance = "Unknown"
                                    }
                                }
                                else {
                                    $instance = ""
                                }
                            }
                            $priority = switch ($service.ServiceType) {
                                "Engine" { 200 }
                                default { 100 }
                            }
                            #If only specific instances are selected
                            if (!$InstanceName -or $instance -in $InstanceName) {
                                #Add other properties and methods
                                Add-Member -Force -InputObject $service -NotePropertyName InstanceName -NotePropertyValue $instance
                                Add-Member -Force -InputObject $service -NotePropertyName ServicePriority -NotePropertyValue $priority
                                Add-Member -Force -InputObject $service -MemberType ScriptMethod -Name "Stop" -Value {
                                    Param ([bool]$Force = $false)
                                    Stop-DbaSqlService -InputObject $this -Force:$Force
                                }
                                Add-Member -Force -InputObject $service -MemberType ScriptMethod -Name "Start" -Value { Start-DbaSqlService -InputObject $this }
                                Add-Member -Force -InputObject $service -MemberType ScriptMethod -Name "Restart" -Value {
                                    Param ([bool]$Force = $false)
                                    Restart-DbaSqlService -InputObject $this -Force:$Force
                                }
                                Add-Member -Force -InputObject $service -MemberType ScriptMethod -Name "ChangeStartMode" -Value {
                                    Param (
                                        [parameter(Mandatory = $true)]
                                        [string]$Mode
                                    )
                                    $supportedModes = @("Automatic", "Manual", "Disabled")
                                    if ($Mode -notin $supportedModes) {
                                        Stop-Function -Message ("Incorrect mode '$Mode'. Use one of the following values: {0}" -f ($supportedModes -join ' | ')) -EnableException $false -FunctionName 'Get-DbaSqlService'
                                        Return
                                    }
                                    Set-ServiceStartMode -InputObject $this -Mode $Mode -ErrorAction Stop
                                    $this.StartMode = $Mode
                                }
                                Select-DefaultView -InputObject $service -Property ComputerName, ServiceName, ServiceType, InstanceName, DisplayName, StartName, State, StartMode -TypeName DbaSqlService
                            }
                        }
                    }
                    else {
                        Stop-Function -EnableException $EnableException -Message "No Sql Services found on $Computer" -Continue
                    }
                }
                else {
                    Stop-Function -EnableException $EnableException -Message "No ComputerManagement Namespace on $Computer. Please note that this function is available from SQL 2005 up." -Continue
                }
            }
            else {
                Stop-Function -EnableException $EnableException -Message "Failed to connect to $Computer" -Continue
            }
        }
    }
}
tools\dbatools\functions\Get-DbaSsisEnvironmentVariable.ps1
function Get-DbaSsisEnvironmentVariable {
    <#
        .SYNOPSIS
            This command gets specified SSIS Environment and all its variables

        .DESCRIPTION
            This command gets all variables from specified environment from SSIS Catalog. All sensitive values are decrypted.
            The function communicates directly with SSISDB database, "SQL Server Integration Services" service isn't queried there.
            Each parameter (besides SqlInstance and SqlCredential) acts as the filter to only include or exclude particular element

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to.
            This can be a collection and receive pipeline input to allow the function
            to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Environment
            The SSIS Environments names that we want to get variables from

        .PARAMETER EnvironmentExclude
            The SSIS Environments to exclude. Acts as a filter for environments, best used without 'Environment' parameter
            to get variables for all environments but excluded ones

        .PARAMETER Folder
            The Folders names that contain the environments

        .PARAMETER FolderExclude
            The Folders names to exclude. Acts as a filter for folders containing environments, best user without 'Folder' parameter
            to get variables for all folders but excluded ones

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: SSIS, SSISDB, Variable
            Author: Bartosz Ratajczyk ( @b_ratajczyk )

            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaSsisEnvironmentVariable

        .EXAMPLE
            Get-DbaSsisEnvironmentVariable -SqlInstance localhost -Environment DEV -Folder DWH_ETL

            Gets variables of 'DEV' environment located in 'DWH_ETL' folder on 'localhost' Server

        .EXAMPLE
            Get-DbaSsisEnvironmentVariable -SqlInstance localhost -Environment DEV -Folder DWH_ETL, DEV2, QA

            Gets variables of 'DEV' environment(s) located in folders 'DWH_ETL', 'DEV2' and 'QA' on 'localhost' server

        .EXAMPLE
            Get-DbaSsisEnvironmentVariable -SqlInstance localhost -Environment DEV -FolderExclude DWH_ETL, DEV2, QA

            Gets variables of 'DEV' environments located in folders other than 'DWH_ETL', 'DEV2' and 'QA' on 'localhost' server

        .EXAMPLE
            Get-DbaSsisEnvironmentVariable -SqlInstance localhost -Environment DEV, PROD -Folder DWH_ETL, DEV2, QA

            Gets variables of 'DEV' and 'PROD' environment(s) located in folders 'DWH_ETL', 'DEV2' and 'QA' on 'localhost' server

        .EXAMPLE
            Get-DbaSsisEnvironmentVariable -SqlInstance localhost -EnvironmentExclude DEV, PROD -Folder DWH_ETL, DEV2, QA

            Gets variables of environments other than 'DEV' and 'PROD' located in folders 'DWH_ETL', 'DEV2' and 'QA' on 'localhost' server

        .EXAMPLE
            Get-DbaSsisEnvironmentVariable -SqlInstance localhost -EnvironmentExclude DEV, PROD -FolderExclude DWH_ETL, DEV2, QA

            Gets variables of environments other than 'DEV' and 'PROD' located in folders other than 'DWH_ETL', 'DEV2' and 'QA' on 'localhost' server

        .EXAMPLE
            'localhost' | Get-DbaSsisEnvironmentVariable -EnvironmentExclude DEV, PROD

            Gets all SSIS environments except 'DEV' and 'PROD' from 'localhost' server. The server name comes from pipeline

        .EXAMPLE
            'SRV1', 'SRV3' | Get-DbaSsisEnvironmentVariable

            Gets all SSIS environments from 'SRV1' and 'SRV3' servers. The server's names come from pipeline

        .EXAMPLE
            'SRV1', 'SRV2' | Get-DbaSsisEnvironmentVariable DEV | Out-GridView

            Gets all variables from 'DEV' Environment(s) on servers 'SRV1' and 'SRV2' and outputs it as the GridView.
            The server names come from the pipeline.

        .EXAMPLE
            'localhost' | Get-DbaSsisEnvironmentVariable -EnvironmentExclude DEV, PROD | Select-Object -Property Name, Value | Where-Object {$_.Name -match '^a'} | Out-GridView

            Gets all variables from Environments other than 'DEV' and 'PROD' on 'localhost' server,
            selects Name and Value properties for variables that names start with letter 'a' and outputs it as the GridView
    #>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias('SqlServer', 'ServerInstance')]
        [DbaInstanceParameter[]]$SqlInstance,
        [Parameter(Mandatory = $false)]
        [PSCredential]$SqlCredential,
        [parameter(Mandatory = $false)]
        [object[]]$Environment,
        [parameter(Mandatory = $false)]
        [object[]]$EnvironmentExclude,
        [parameter(Mandatory = $false)]
        [object[]]$Folder,
        [parameter(Mandatory = $false)]
        [object[]]$FolderExclude,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Message "Connecting to $instance" -Level Verbose
                $server = Connect-SqlInstance -SqlInstance $instance -MinimumVersion 11
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            try {
                $ISNamespace = "Microsoft.SqlServer.Management.IntegrationServices"

                Write-Message -Message "Connecting to SSIS Catalog on $instance" -Level Verbose
                $SSIS = New-Object "$ISNamespace.IntegrationServices" $server
            }
            catch {
                Stop-Function -Message "Could not connect to SSIS Catalog on $instance or current SMO library does not support SSIS catalog"
                return
            }

            Write-Message -Message "Fetching SSIS Catalog and its folders" -Level Verbose
            $catalog = $SSIS.Catalogs | Where-Object { $_.Name -eq "SSISDB" }

            # get all folders names if none provided
            if ($null -eq $Folder) {
                $searchFolders = $catalog.Folders.Name
            }
            else {
                $searchFolders = $Folder
            }

            # filter unwanted folders
            if ($FolderExclude) {
                $searchFolders = $searchFolders | Where-Object { $_ -notin $FolderExclude }
            }

            if ($null -eq $searchFolders) {
                Write-Message -Message "Instance: $instance > -Folder and -FolderExclude filters return an empty collection. Skipping" -Level Warning
            }
            else {
                foreach ($f in $searchFolders) {
                    # get all environments names if none provided
                    if ($null -eq $Environment) {
                        $searchEnvironments = $catalog.Folders.Environments.Name
                    }
                    else {
                        $searchEnvironments = $Environment
                    }

                    #filter unwanted environments
                    if ($EnvironmentExclude) {
                        $searchEnvironments = $searchEnvironments | Where-Object { $_ -notin $EnvironmentExclude }
                    }

                    if ($null -eq $searchEnvironments) {
                        Write-Message -Message "Instance: $instance / Folder: $f > -Environment and -EnvironmentExclude filters return an empty collection. Skipping." -Level Warning
                    }
                    else {
                        $Environments = $catalog.Folders[$f].Environments | Where-Object { $_.Name -in $searchEnvironments }

                        foreach ($e in $Environments) {
                            #encryption handling
                            $encKey = 'MS_Enckey_Env_' + $e.EnvironmentId
                            $encCert = 'MS_Cert_Env_' + $e.EnvironmentId

                            <#
                            SMO does not return sensitive values (gets data from catalog.environment_variables)
                            We have to manually query internal.environment_variables instead and use symmetric keys
                            within T-SQL code
                            #>

                            $sql = @"
                            OPEN SYMMETRIC KEY $encKey DECRYPTION BY CERTIFICATE $encCert;

                            SELECT
                                ev.variable_id,
                                ev.name,
                                ev.description,
                                ev.type,
                                ev.sensitive,
                                value = ev.value,
                                ev.sensitive_value,
                                ev.base_data_type,
                                decrypted = decrypted.value
                            FROM internal.environment_variables ev

                                CROSS APPLY (
                                    SELECT
                                        value   = CASE base_data_type
                                                    WHEN 'nvarchar' THEN CONVERT(NVARCHAR(MAX), DECRYPTBYKEY(sensitive_value))
                                                    WHEN 'bit' THEN CONVERT(NVARCHAR(MAX), CONVERT(bit, DECRYPTBYKEY(sensitive_value)))
                                                    WHEN 'datetime' THEN CONVERT(NVARCHAR(MAX), CONVERT(datetime2(0), DECRYPTBYKEY(sensitive_value)))
                                                    WHEN 'single' THEN CONVERT(NVARCHAR(MAX), CONVERT(DECIMAL(38, 18), DECRYPTBYKEY(sensitive_value)))
                                                    WHEN 'float' THEN CONVERT(NVARCHAR(MAX), CONVERT(DECIMAL(38, 18), DECRYPTBYKEY(sensitive_value)))
                                                    WHEN 'decimal' THEN CONVERT(NVARCHAR(MAX), CONVERT(DECIMAL(38, 18), DECRYPTBYKEY(sensitive_value)))
                                                    WHEN 'tinyint' THEN CONVERT(NVARCHAR(MAX), CONVERT(tinyint, DECRYPTBYKEY(sensitive_value)))
                                                    WHEN 'smallint' THEN CONVERT(NVARCHAR(MAX), CONVERT(smallint, DECRYPTBYKEY(sensitive_value)))
                                                    WHEN 'int' THEN CONVERT(NVARCHAR(MAX), CONVERT(INT, DECRYPTBYKEY(sensitive_value)))
                                                    WHEN 'bigint' THEN CONVERT(NVARCHAR(MAX), CONVERT(bigint, DECRYPTBYKEY(sensitive_value)))
                                                END
                                ) decrypted
                            WHERE environment_id = $($e.EnvironmentId);
                            CLOSE SYMMETRIC KEY $encKey;
"@

                            $ssisVariables = $server.Query($sql, "SSISDB")

                            foreach ($variable in $ssisVariables) {
                                if ($variable.sensitive -eq $true) {
                                    $value = $variable.decrypted
                                }
                                else {
                                    $value = $variable.value
                                }

                                [PSCustomObject]@{
                                    ComputerName = $server.ComputerName
                                    InstanceName = $server.ServiceName
                                    SqlInstance  = $server.DomainInstanceName
                                    Folder       = $f
                                    Environment  = $e.Name
                                    Id           = $variable.variable_id
                                    Name         = $variable.Name
                                    Description  = $variable.description
                                    Type         = $variable.type
                                    IsSensitive  = $variable.sensitive
                                    BaseDataType = $variable.base_data_type
                                    Value        = $value
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaSsisExecutionHistory.ps1
#ValidationTags#Messaging#
function Get-DbaSsisExecutionHistory {
    <#
        .SYNOPSIS
           Get-DbaSsisHistory Retreives SSIS project and package execution History, and environments from one SQL Server to another.

        .DESCRIPTION
            This command gets execution history for SSIS executison given one or more instances and can be filtered by Project, Environment,Folder or Status.
        
        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to.
            This can be a collection and receive pipeline input to allow the function
            to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Project
            Specifies a filter by project

        .PARAMETER Folder
            Specifies a filter by folder
        
        .PARAMETER Environment
            Specifies a filter by environment

        .PARAMETER Status
            Specifies a filter by status (created,running,cancelled,failed,pending,halted,succeeded,stopping,completed)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration, SSIS
            Author: Chris Tucker (ChrisTucker, @ChrisTuc47368095)

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaSsisExecutionHistory

        .EXAMPLE
            Get-DbaSsisExecutionHistory -SqlInstance SMTQ01 -Folder SMTQ_PRC

            Get all history items for SMTQ01 in folder SMTQ_PRC.

        .EXAMPLE
            Get-DbaSsisExecutionHistory -SqlInstance SMTQ01 -Status Failed,Cancelled
            
            Gets all failed or canceled executions for SMTQ01.

        .EXAMPLE
            Get-DbaSsisExecutionHistory -SqlInstance SMTQ01,SMTQ02 -Status Failed,Cancelled -Whatif

            Shows what would happen if the command were executed and would return the SQL statement that would be executed per instance.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [ValidateSet("Created", "Running", "Cancelled", "Failed", "Pending", "Halted", "Succeeded", "Stopping", "Completed")]
        [String[]]$Status,
        [String[]]$Project,
        [String[]]$Folder,
        [String[]]$Environment,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        $statuses = @{
            'Created'   = 1
            'Running'   = 2
            'Cancelled' = 3
            'Failed'    = 4
            'Pending'   = 5
            'Halted'    = 6
            'Succeeded' = 7
            'Stopping'  = 8
            'Completed' = 9
        }
        if ($Status) {
            $csv = ($statuses[$Status] -join ',')
            $statusq = "AND e.[Status] in ($csv)"
        }
        else {
            $statusq = ''
        }
        
        if ($Project) {
            $csv = "`"" + ($Project -join '","') + "`""
            $projectq = "AND e.[ProjectName] in ($csv)"
        }
        else {
            $projectq = ''
        }
        
        if ($Folder) {
            $csv = "`'" + ($Folder -join "'", "'") + "`'"
            $folderq = "AND e.[FolderName] in ($csv)"
        }
        else {
            $folderq = ''
        }
        
        if ($Environment) {
            $csv = "`'" + ($Environment -join "'", "'") + "`'"
            $environmentq = "AND e.[Environment] in ($csv)"
        }
        else {
            $environmentq = ''
        }
        
        $sql = "
            WITH
            cteLoglevel as (
                SELECT
                    execution_id as ExecutionID,
                    cast(parameter_value AS INT) AS LoggingLevel
                FROM
                    [catalog].[execution_parameter_values]
                WHERE
                    parameter_name = 'LOGGING_LEVEL'
            )
            , cteStatus AS (
                SELECT
                     [key]
                    ,[code]
                FROM (
                    VALUES
                          ( 1,'Created'  )
                        , ( 2,'Running'  )
                        , ( 3,'Cancelled')
                        , ( 4,'Failed'   )
                        , ( 5,'Pending'  )
                        , ( 6,'Halted'   )
                        , ( 7,'Succeeded')
                        , ( 8,'Stopping' )
                        , ( 9,'Completed')
                ) codes([key],[code])
            )
            SELECT
                      e.execution_id as ExecutionID
                    , e.folder_name as FolderName
                    , e.project_name as ProjectName
                    , e.package_name as PackageName
                    , e.project_lsn as ProjectLsn
                    , Environment = isnull(e.environment_folder_name, '') + isnull('\' + e.environment_name,  '')
                    , s.code AS StatusCode
                    , start_time as StartTime
                    , end_time as EndTime
                    , ElapsedMinutes = DATEDIFF(ss, e.start_time, e.end_time)
                    , l.LoggingLevel
            FROM
                [catalog].executions e
                LEFT OUTER JOIN cteLoglevel l
                    ON e.execution_id = l.ExecutionID
                LEFT OUTER JOIN cteStatus s
                    ON s.[key] = e.status
            WHERE 1=1
                $statusq
                $projectq
                $folderq
                $environmentq
                OPTION  ( RECOMPILE );"
    }
    process {
        foreach ($instance in $SqlInstance) {
            $results = Invoke-DbaSqlQuery -SqlInstance $instance -Database SSISDB -Query $sql -as PSObject -SqlCredential $SqlCredential
            foreach ($row in $results) {
                $row.start_time = [dbadatetime]$row.StartTime.DateTime
                $row.end_time = [dbadatetime]$row.EndTime.DateTime
                $row
            }
        }
    }
}
tools\dbatools\functions\Get-DbaStartupParameter.ps1
function Get-DbaStartupParameter {
    <#
    .SYNOPSIS
        Displays values for a detailed list of SQL Server Startup Parameters.

    .DESCRIPTION
        Displays values for a detailed list of SQL Server Startup Parameters including Master Data Path, Master Log path, Error Log, Trace Flags, Parameter String and much more.

        This command relies on remote Windows Server (SQL WMI/WinRm) access. You can pass alternative Windows credentials by using the -Credential parameter.

        See https://msdn.microsoft.com/en-us/library/ms190737.aspx for more information.

    .PARAMETER SqlInstance
        The SQL Server instance to connect to.

    .PARAMETER Credential
        Allows you to login to servers using alternate Windows credentials.

        $scred = Get-Credential, then pass $scred object to the -Credential parameter.

    .PARAMETER Simple
        If this switch is enabled, simplified output will be produced including only Server, Master Data Path, Master Log path, ErrorLog, TraceFlags and ParameterString.

    .PARAMETER EnableException
        If this switch is enabled, exceptions will be thrown to the caller, which will need to perform its own exception processing. Otherwise, the function will try to catch the exception, interpret it and provide a friendly error message.

    .EXAMPLE
        Get-DbaStartupParameter -SqlInstance sql2014

        Logs into SQL WMI as the current user then displays the values for numerous startup parameters.

    .EXAMPLE
        $wincred = Get-Credential ad\sqladmin
        Get-DbaStartupParameter -SqlInstance sql2014 -Credential $wincred -Simple

        Logs in to WMI using the ad\sqladmin credential and gathers simplified information about the SQL Server Startup Parameters.

    .NOTES
        Tags: WSMan, SQLWMI, Memory
        dbatools PowerShell module (https://dbatools.io)
        Copyright (C) 2016 Chrissy LeMaire
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Get-DbaStartupParameter
#>
    [CmdletBinding()]
    param ([parameter(ValueFromPipeline, Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("SqlCredential")]
        [PSCredential]$Credential,
        [switch]$Simple,
        [switch]
        [Alias('Silent')]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                $computerName = $instance.ComputerName
                $instanceName = $instance.InstanceName
                $ogInstance = $instance.FullSmoName

                $computerName = (Resolve-DbaNetworkName -ComputerName $computerName).FullComputerName

                Write-Message -Level Verbose -message "Connecting to $computerName"

                if ($instanceName.Length -eq 0) { $instanceName = "MSSQLSERVER" }

                $displayname = "SQL Server ($instanceName)"

                $Scriptblock = {
                    $computerName = $args[0]
                    $displayname = $args[1]

                    $wmisvc = $wmi.Services | Where-Object DisplayName -eq $displayname

                    $params = $wmisvc.StartupParameters -split ';'

                    $masterdata = $params | Where-Object { $_.StartsWith('-d') }
                    $masterlog = $params | Where-Object { $_.StartsWith('-l') }
                    $errorlog = $params | Where-Object { $_.StartsWith('-e') }
                    $traceflags = $params | Where-Object { $_.StartsWith('-T') }

                    $debugflag = $params | Where-Object { $_.StartsWith('-t') }

                    if ($debugflag.length -ne 0) {
                        Write-Message -Level Warning "$instance is using the lowercase -t trace flag. This is for internal debugging only. Please ensure this was intentional."
                    }
                    #>

                    if ($traceflags.length -eq 0) {
                        $traceflags = "None"
                    }
                    else {
                        $traceflags = $traceflags.substring(2)
                    }

                    if ($Simple -eq $true) {
                        [PSCustomObject]@{
                            ComputerName    = $computerName
                            InstanceName    = $instanceName
                            SqlInstance     = $ogInstance
                            MasterData      = $masterdata.TrimStart('-d')
                            MasterLog       = $masterlog.TrimStart('-l')
                            ErrorLog        = $errorlog.TrimStart('-e')
                            TraceFlags      = $traceflags -join ','
                            ParameterString = $wmisvc.StartupParameters
                        }
                    }
                    else {
                        # From https://msdn.microsoft.com/en-us/library/ms190737.aspx

                        $commandpromptparm = $params | Where-Object { $_ -eq '-c' }
                        $minimalstartparm = $params | Where-Object { $_ -eq '-f' }
                        $memorytoreserve = $params | Where-Object { $_.StartsWith('-g') }
                        $noeventlogsparm = $params | Where-Object { $_ -eq '-n' }
                        $instancestartparm = $params | Where-Object { $_ -eq '-s' }
                        $disablemonitoringparm = $params | Where-Object { $_ -eq '-x' }
                        $increasedextentsparm = $params | Where-Object { $_ -ceq '-E' }

                        $minimalstart = $noeventlogs = $instancestart = $disablemonitoring = $false
                        $increasedextents = $commandprompt = $singleuser = $false

                        if ($null -ne $commandpromptparm) {
                            $commandprompt = $true
                        }
                        if ($null -ne $minimalstartparm) {
                            $minimalstart = $true
                        }
                        if ($null -eq $memorytoreserve) {
                            $memorytoreserve = 0
                        }
                        if ($null -ne $noeventlogsparm) {
                            $noeventlogs = $true
                        }
                        if ($null -ne $instancestartparm) {
                            $instancestart = $true
                        }
                        if ($null -ne $disablemonitoringparm) {
                            $disablemonitoring = $true
                        }
                        if ($null -ne $increasedextentsparm) {
                            $increasedextents = $true
                        }

                        $singleuserparm = $params | Where-Object { $_.StartsWith('-m') }

                        if ($singleuserparm.length -ne 0) {
                            $singleuser = $true
                            $singleuserdetails = $singleuserparm.TrimStart('-m')
                        }

                        [PSCustomObject]@{
                            ComputerName         = $computerName
                            InstanceName         = $instanceName
                            SqlInstance          = $ogInstance
                            MasterData           = $masterdata -replace '^-[dD]', ''
                            MasterLog            = $masterlog  -replace '^-[lL]', ''
                            ErrorLog             = $errorlog   -replace '^-[eE]', ''
                            TraceFlags           = $traceflags -join ','
                            CommandPromptStart   = $commandprompt
                            MinimalStart         = $minimalstart
                            MemoryToReserve      = $memorytoreserve
                            SingleUser           = $singleuser
                            SingleUserName       = $singleuserdetails
                            NoLoggingToWinEvents = $noeventlogs
                            StartAsNamedInstance = $instancestart
                            DisableMonitoring    = $disablemonitoring
                            IncreasedExtents     = $increasedextents
                            ParameterString      = $wmisvc.StartupParameters
                        }
                    }
                }

                # This command is in the internal function
                # It's sorta like Invoke-Command.
                if ($credential) {
                    Invoke-ManagedComputerCommand -Server $computerName -Credential $credential -ScriptBlock $Scriptblock -ArgumentList $computerName, $displayname
                }
                else {
                    Invoke-ManagedComputerCommand -Server $computerName -ScriptBlock $Scriptblock -ArgumentList $computerName, $displayname
                }
            }
            catch {
                Stop-Function -Message "$instance failed." -ErrorRecord $_ -Continue -Target $instance
            }
        }
    }
}
tools\dbatools\functions\Get-DbaSuspectPage.ps1
function Get-DbaSuspectPage {
    <#
        .SYNOPSIS
        Returns data that is stored in SQL for Suspect Pages on the specified SQL Server Instance

        .DESCRIPTION
        This function returns any records that were stored due to suspect pages in databases on a SQL Server Instance.

        .PARAMETER SqlInstance
        A SQL Server instance to connect to

        .PARAMETER SqlCredential
        A credential to use to connect to the SQL Instance rather than using Windows Authentication

        .PARAMETER Database
        The database to return. If unspecified, all records will be returned.

        .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
        Tags: Pages, DBCC
        Author: Garry Bargsley (@gbargsley), http://blog.garrybargsley.com

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
        Get-DbaSuspectPage -SqlInstance sql2016

        Retrieve any records stored for Suspect Pages on the sql2016 SQL Server.

        .EXAMPLE
        Get-DbaSuspectPage -SqlInstance sql2016 -Database Test

        Retrieve any records stored for Suspect Pages on the sql2016 SQL Server and the Test database only.

#>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [object]$Database,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {

        foreach ($instance in $sqlinstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                return
            }

            $sql = "Select
            DB_NAME(database_id) as DBName,
            file_id,
            page_id,
            CASE event_type
            WHEN 1 THEN '823 or 824 or Torn Page'
            WHEN 2 THEN 'Bad Checksum'
            WHEN 3 THEN 'Torn Page'
            WHEN 4 THEN 'Restored'
            WHEN 5 THEN 'Repaired (DBCC)'
            WHEN 7 THEN 'Deallocated (DBCC)'
            END as EventType,
            error_count,
            last_update_date
            from msdb.dbo.suspect_pages"

            try {
                $results = $server.Query($sql)
            }
            catch {
                Stop-Function -Message "Issue collecting data on $server" -Target $server -ErrorRecord $_ -Continue
            }

            if ($Database) {
                $results = $results | Where-Object DBName -EQ $Database
            }

        }
        foreach ($row in $results) {
            [PSCustomObject]@{
                ComputerName   = $server.ComputerName
                InstanceName   = $server.ServiceName
                SqlInstance    = $server.DomainInstanceName
                Database       = $row.DBName
                FileId         = $row.file_id
                PageId         = $row.page_id
                EventType      = $row.EventType
                ErrorCount     = $row.error_count
                LastUpdateDate = $row.last_update_date
            }
        }
    }
}
tools\dbatools\functions\Get-DbaTable.ps1
function Get-DbaTable {
    <#
.SYNOPSIS
Returns a summary of information on the tables

.DESCRIPTION
Shows table information around table row and data sizes and if it has any table type information.

.PARAMETER SqlInstance
SQL Server name or SMO object representing the SQL Server to connect to. This can be a
collection and receive pipeline input

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Database
The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

.PARAMETER ExcludeDatabase
The database(s) to exclude - this list is auto-populated from the server

.PARAMETER IncludeSystemDBs
Switch parameter that when used will display system database information

.PARAMETER Table
Define a specific table you would like to query. You can specify up to three-part name like db.sch.tbl.
If the object has special characters please wrap them in square brackets [ ].
This dbo.First.Table will try to find table named 'Table' on schema 'First' and database 'dbo'.
The correct way to find table named 'First.Table' on schema 'dbo' is passing dbo.[First.Table]

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Database, Tables
Author: Stephen Bennett, https://sqlnotesfromtheunderground.wordpress.com/

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Get-DbaTable

.EXAMPLE
Get-DbaTable -SqlInstance DEV01 -Database Test1
Return all tables in the Test1 database

.EXAMPLE
Get-DbaTable -SqlInstance DEV01 -Database MyDB -Table MyTable
Return only information on the table MyTable from the database MyDB

.EXAMPLE
Get-DbaTable -SqlInstance DEV01 -Table MyTable
Returns information on table called MyTable if it exists in any database on the server, under any schema

.EXAMPLE
Get-DbaTable -SqlInstance DEV01 -Table dbo.[First.Table]
Returns information on table called First.Table on schema dbo if it exists in any database on the server

.EXAMPLE
'localhost','localhost\namedinstance' | Get-DbaTable -Database DBA -Table Commandlog
Returns information on the CommandLog table in the DBA database on both instances localhost and the named instance localhost\namedinstance

#>
    [CmdletBinding()]
    param ([parameter(ValueFromPipeline, Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$IncludeSystemDBs,
        [string[]]$Table,
        [switch][Alias('Silent')]
        $EnableException
    )

    begin {
        if ($Table) {
            $fqtns = @()
            foreach ($t in $Table) {
                $splitName = [regex]::Matches($t, "(\[.+?\])|([^\.]+)").Value
                $dotcount = $splitName.Count

                $splitDb = $Schema = $null

                switch ($dotcount) {
                    1 {
                        $tbl = $t
                    }
                    2 {
                        $schema = $splitName[0]
                        $tbl = $splitName[1]
                    }
                    3 {
                        $splitDb = $splitName[0]
                        $schema = $splitName[1]
                        $tbl = $splitName[2]
                    }
                    default {
                        Write-Message -Level Warning -Message "Please make sure that you are using up to three-part names. If your search value contains '.' character you must use [ ] to wrap the name. The value $t is not a valid name."
                        Continue
                    }
                }

                if ($splitDb -like "[[]*[]]") {
                    $splitDb = $splitDb.Substring(1, ($splitDb.Length - 2))
                }

                if ($schema -like "[[]*[]]") {
                    $schema = $schema.Substring(1, ($schema.Length - 2))
                }

                if ($tbl -like "[[]*[]]") {
                    $tbl = $tbl.Substring(1, ($tbl.Length - 2))
                }

                $fqtns += [PSCustomObject] @{
                    Database = $splitDb
                    Schema   = $Schema
                    Table    = $tbl
                }
            }
        }
    }

    process {
        foreach ($instance in $sqlinstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            try {
                #only look at online databases (Status equal normal)
                $dbs = $server.Databases | Where-Object IsAccessible

                #If IncludeSystemDBs is false, exclude systemdbs
                if (!$IncludeSystemDBs -and !$Database) {
                    $dbs = $dbs | Where-Object { !$_.IsSystemObject }
                }

                if ($Database) {
                    $dbs = $dbs | Where-Object { $Database -contains $_.Name }
                }

                if ($ExcludeDatabase) {
                    $dbs = $dbs | Where-Object { $ExcludeDatabase -notcontains $_.Name }
                }
            }
            catch {
                Stop-Function -Message "Unable to gather dbs for $instance" -Target $instance -Continue -ErrorRecord $_
            }

            foreach ($db in $dbs) {
                Write-Message -Level Verbose -Message "Processing $db"

                if ($fqtns) {
                    $tables = @()
                    foreach ($fqtn in $fqtns) {
                        # If the user specified a database in a three-part name, and it's not the
                        # database currently being processed, skip this table.
                        if ($fqtn.Database) {
                            if ($fqtn.Database -ne $db.Name) {
                                continue
                            }
                        }

                        $tbl = $db.tables | Where-Object { $_.Name -in $fqtn.Table -and $fqtn.Schema -in ($_.Schema, $null) -and $fqtn.Database -in ($_.Parent.Name, $null) }

                        if (-not $tbl) {
                            Write-Message -Level Verbose -Message "Could not find table $($fqtn.Table) in $db on $server"
                        }
                        $tables += $tbl
                    }
                }
                else {
                    $tables = $db.Tables
                }

                foreach ($sqltable in $tables) {
                    $sqltable | Add-Member -Force -MemberType NoteProperty -Name ComputerName -Value $server.ComputerName
                    $sqltable | Add-Member -Force -MemberType NoteProperty -Name InstanceName -Value $server.ServiceName
                    $sqltable | Add-Member -Force -MemberType NoteProperty -Name SqlInstance -Value $server.DomainInstanceName
                    $sqltable | Add-Member -Force -MemberType NoteProperty -Name Database -Value $db.Name

                    $defaultprops = "ComputerName", "InstanceName", "SqlInstance", "Database", "Schema", "Name", "IndexSpaceUsed", "DataSpaceUsed", "RowCount", "HasClusteredIndex", "IsFileTable", "IsMemoryOptimized", "IsPartitioned", "FullTextIndex", "ChangeTrackingEnabled"

                    Select-DefaultView -InputObject $sqltable -Property $defaultprops
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaTcpPort.ps1
function Get-DbaTcpPort {
    <#
        .SYNOPSIS
            Returns the TCP port used by the specified SQL Server.

        .DESCRIPTION
            By default, this function returns just the TCP port used by the specified SQL Server.

            If -Detailed is specified, the server name, IPAddress (ipv4 and ipv6), port number and an indicator of whether or not the port assignment is static are returned.

            Remote sqlwmi is used by default. If this doesn't work, then remoting is used. If neither work, it defaults to T-SQL which can provide only the port.

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to.

        .PARAMETER SqlCredential
            Allows you to connect to servers using alternate Windows credentials

            $scred = Get-Credential, then pass $scred object to the -SqlCredential parameter.

        .PARAMETER Detailed
            If this switch is enabled, an object with server name, IPAddress (ipv4 and ipv6), port and static ($true/$false) for one or more SQL Servers is returned.

        .PARAMETER ExcludeIpv6
            If this switch is enabled, IPv6 information is excluded from detailed output.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: SQLWMI, tcp

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaTcpPort

        .EXAMPLE
            Get-DbaTcpPort -SqlInstance sqlserver2014a

            Returns just the port number for the default instance on sqlserver2014a.

        .EXAMPLE
            Get-DbaTcpPort -SqlInstance winserver\sqlexpress, sql2016

            Returns an object with server name and port number for the sqlexpress on winserver and the default instance on sql2016.

        .EXAMPLE
            Get-DbaTcpPort -SqlInstance sqlserver2014a, sql2016 -Detailed

            Returns an object with server name, IPAddress (ipv4 and ipv6), port and static ($true/$false) for sqlserver2014a and sql2016.

            Remote sqlwmi is used by default. If this doesn't work, then remoting is used. If neither work, it defaults to T-SQL which can provide only the port.

        .EXAMPLE
            Get-DbaRegisteredServer -SqlInstance sql2014 | Get-DbaTcpPort -ExcludeIpv6 -Detailed

            Returns an object with server name, IPAddress (just ipv4), port and static ($true/$false) for every server listed in the Central Management Server on sql2014.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [switch]$Detailed,
        [Alias("Ipv4")]
        [switch]$ExcludeIpv6,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            if ($detailed -eq $true) {
                try {
                    $scriptblock = {
                        $instance = $args[0]

                        Add-Type -AssemblyName Microsoft.VisualBasic

                        foreach ($servername in $wmi.ServerInstances) {
                            $instanceName = $servername.Name
                            $wmiinstance = $wmi.Services | Where-Object { $_.DisplayName -eq "SQL Server ($instanceName)" }
                            $vsname = ($wmiinstance.AdvancedProperties | Where-Object { $_ -match 'VSNAME' }).Value

                            if ($vsname.length -eq 0) {
                                $vsname = "$instance\$instanceName"
                            }

                            $vsname = $vsname.Replace("\MSSQLSERVER", "")

                            try {
                                $regroot = ($wmiinstance.AdvancedProperties | Where-Object { $_ -match 'REGROOT' }).Value
                                $dacport = (Get-ItemProperty "HKLM:\$regroot\MSSQLServer\SuperSocketNetLib\AdminConnection\Tcp").TcpDynamicPorts

                                [PsCustomObject]@{
                                    ComputerName = $instance
                                    InstanceName = $instanceName
                                    SqlInstance  = $vsname
                                    IPAddress    = "0.0.0.0"
                                    Port         = $dacport
                                    Static       = $false
                                    Type         = "DAC"
                                }
                            }
                            catch {
                                # it's just not our day
                            }

                            $tcp = $servername.ServerProtocols | Where-Object Name -eq Tcp
                            $ips = $tcp.IPAddresses

                            # This is a remote command so do not use Write-message
                            Write-Verbose "Parsing information for $($ips.count) IP addresses."
                            foreach ($ip in $ips) {
                                $props = $ip.IPAddressProperties | Where-Object { $_.Name -eq "TcpPort" -or $_.Name -eq "TcpDynamicPorts" }

                                foreach ($prop in $props) {
                                    if ([Microsoft.VisualBasic.Information]::IsNumeric($prop.value)) {
                                        $port = $prop.value
                                        if ($prop.name -eq 'TcpPort') {
                                            $static = $true
                                        }
                                        else {
                                            $static = $false
                                        }
                                        break
                                    }
                                }

                                [PsCustomObject]@{
                                    ComputerName = $instance
                                    InstanceName = $instanceName
                                    SqlInstance  = $vsname
                                    IPAddress    = $ip.IPAddress.IPAddressToString
                                    Port         = $port
                                    Static       = $static
                                    Type         = "Normal"
                                }
                            }
                        }
                    }

                    $computer = $instance.ComputerName
                    $resolved = Resolve-DbaNetworkName -ComputerName $instance -Verbose:$false
                    $computername = $resolved.FullComputerName

                    try {
                        Write-Message -Level Verbose -Message "Trying with ComputerName ($computer)."
                        $someIps = Invoke-ManagedComputerCommand -ComputerName $computer -ArgumentList $computer -ScriptBlock $scriptblock
                    }
                    catch {
                        Write-Message -Level Verbose -Message "Trying with FullComputerName because ComputerName failed."
                        $someIps = Invoke-ManagedComputerCommand -ComputerName $computername -ArgumentList $fqdn -ScriptBlock $scriptblock
                    }
                }
                catch {
                    Stop-Function -Message "Could not get detailed information." -Target $instance -ErrorRecord $_
                }

                $cleanedUp = $someIps | Sort-Object IPAddress

                if ($ExcludeIpv6) {
                    $octet = '(?:0?0?[0-9]|0?[1-9][0-9]|1[0-9]{2}|2[0-5][0-5]|2[0-4][0-9])'
                    [regex]$ipv4 = "^(?:$octet\.){3}$octet$"
                    $cleanedUp = $cleanedUp | Where-Object { $_.IPAddress -match $ipv4 }
                }

                $cleanedUp
            }

            if ($Detailed -eq $false -or ($Detailed -eq $true -and $null -eq $someIps)) {
                try {
                    $server = Connect-SqlInstance -SqlInstance "TCP:$instance" -SqlCredential $SqlCredential -MinimumVersion 9
                }
                catch {
                    Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $servername -Continue
                }

                # WmiComputer can be unreliable :( Use T-SQL
                $sql = "SELECT local_tcp_port FROM sys.dm_exec_connections WHERE session_id = @@SPID"
                $port = $server.Query($sql)

                [PSCustomObject]@{
                    ComputerName = $server.ComputerName
                    InstanceName = $server.ServiceName
                    SqlInstance  = $server.DomainInstanceName
                    Port         = $port.local_tcp_port
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaTempdbUsage.ps1
function Get-DbaTempdbUsage {
    <#
        .SYNOPSIS
        Gets Tempdb usage for running queries.

        .DESCRIPTION
        This function queries DMVs for running sessions using Tempdb and returns results if those sessions have user or internal space allocated or deallocated against them.

        .PARAMETER SqlInstance
        The SQL Instance you are querying against.

        .PARAMETER SqlCredential
        If you want to use alternative credentials to connect to the server.

        .PARAMETER WhatIf
        Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
        Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Tempdb, Space
            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaTempdbUsage

        .EXAMPLE
            Get-DbaTempdbUsage -SqlInstance localhost\SQLDEV2K14

            Gets tempdb usage for localhost\SQLDEV2K14
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($server.VersionMajor -le 9) {
                Stop-Function -Message "This function is only supported in SQL Server 2008 or higher." -Continue
            }

            $sql = "SELECT  SERVERPROPERTY('MachineName') AS ComputerName,
        ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
        SERVERPROPERTY('ServerName') AS SqlInstance,
        t.session_id AS Spid,
        r.command AS StatementCommand,
        SUBSTRING(   est.[text],
                     (r.statement_start_offset / 2) + 1,
                     ((CASE r.statement_end_offset
                            WHEN-1
                            THEN DATALENGTH(est.[text])
                            ELSE
                            r.statement_end_offset
                       END - r.statement_start_offset
                      ) / 2
                     ) + 1
                 ) AS QueryText,
        QUOTENAME(DB_NAME(r.database_id)) + N'.' + QUOTENAME(OBJECT_SCHEMA_NAME(est.objectid, est.dbid)) + N'.'
        + QUOTENAME(OBJECT_NAME(est.objectid, est.dbid)) AS ProcedureName,
        r.start_time AS StartTime,
        tdb.UserObjectAllocated * 8 AS CurrentUserAllocatedKB,
        (t.user_objects_alloc_page_count + tdb.UserObjectAllocated) * 8 AS TotalUserAllocatedKB,
        tdb.UserObjectDeallocated * 8 AS UserDeallocatedKB,
        (t.user_objects_dealloc_page_count + tdb.UserObjectDeallocated) * 8 AS TotalUserDeallocatedKB,
        tdb.InternalObjectAllocated * 8 AS InternalAllocatedKB,
        (t.internal_objects_alloc_page_count + tdb.InternalObjectAllocated) * 8 AS TotalInternalAllocatedKB,
        tdb.InternalObjectDeallocated * 8 AS InternalDeallocatedKB,
        (t.internal_objects_dealloc_page_count + tdb.InternalObjectDeallocated) * 8 AS TotalInternalDeallocatedKB,
        r.reads AS RequestedReads,
        r.writes AS RequestedWrites,
        r.logical_reads AS RequestedLogicalReads,
        r.cpu_time AS RequestedCPUTime,
        s.is_user_process AS IsUserProcess,
        s.[status] AS [Status],
        DB_NAME(r.database_id) AS [Database],
        s.login_name AS LoginName,
        s.original_login_name AS OriginalLoginName,
        s.nt_domain AS NTDomain,
        s.nt_user_name AS NTUserName,
        s.[host_name] AS HostName,
        s.[program_name] AS ProgramName,
        s.login_time AS LoginTime,
        s.last_request_start_time AS LastRequestedStartTime,
        s.last_request_end_time AS LastRequestedEndTime
FROM    sys.dm_db_session_space_usage AS t
INNER JOIN sys.dm_exec_sessions AS s
    ON s.session_id = t.session_id
LEFT JOIN sys.dm_exec_requests AS r
    ON r.session_id = s.session_id
LEFT JOIN
          (   SELECT    _tsu.session_id,
                        _tsu.request_id,
                        SUM(_tsu.user_objects_alloc_page_count)       AS UserObjectAllocated,
                        SUM(_tsu.user_objects_dealloc_page_count)     AS UserObjectDeallocated,
                        SUM(_tsu.internal_objects_alloc_page_count)   AS InternalObjectAllocated,
                        SUM(_tsu.internal_objects_dealloc_page_count) AS InternalObjectDeallocated
              FROM      tempdb.sys.dm_db_task_space_usage AS _tsu
              GROUP BY  _tsu.session_id,
                        _tsu.request_id
          ) AS tdb
    ON  tdb.session_id = r.session_id
   AND  tdb.request_id = r.request_id
OUTER APPLY sys.dm_exec_sql_text(r.[sql_handle]) AS est
WHERE   t.session_id != @@SPID
  AND   (tdb.UserObjectAllocated - tdb.UserObjectDeallocated + tdb.InternalObjectAllocated - tdb.InternalObjectDeallocated) != 0
OPTION (RECOMPILE);"

            $server.Query($sql)
        }
    }
}
tools\dbatools\functions\Get-DbatoolsLog.ps1
function Get-DbatoolsLog {
    
<#
    .SYNOPSIS
        Returns log entries for dbatools
    
    .DESCRIPTION
        Returns log entries for dbatools. Handy when debugging or developing a script using it.
    
    .PARAMETER FunctionName
        Default: "*"
        Only messages written by similar functions will be returned.
    
    .PARAMETER ModuleName
        Default: "*"
        Only messages written by commands from similar modules will be returned.
    
    .PARAMETER Target
        Only messags handling the specified target will be returned.
    
    .PARAMETER Tag
        Only messages containing one of these tags will be returned.
    
    .PARAMETER Last
        Only messages written by the last X executions will be returned.
        Uses Get-History to determine execution. Ignores Get-message commands.
        By default, this will also include messages from other runspaces. If your command executes in parallel, that's useful.
        If it doesn't and you were offloading executions to other runspaces, consider also filtering by runspace using '-Runspace'
    
    .PARAMETER Skip
        How many executions to skip when specifying '-Last'.
        Has no effect without the '-Last' parameter.
    
    .PARAMETER Runspace
        The guid of the runspace to return messages from.
        By default, messages from all runspaces are returned.
        Run the following line to see the list of guids:
        
        Get-Runspace | ft Id, Name, InstanceId -Autosize
    
    .PARAMETER Level
        Limit the message selection by level.
        Message levels have a numeric value, making it easier to select a range:
        
        -Level (1..6)
        
        Will select the first 6 levels (Critical - SomewhatVerbose).
    
    .PARAMETER Errors
        Instead of log entries, the error entries will be retrieved
    
    .EXAMPLE
        Get-DbatoolsLog
        
        Returns all log entries currently in memory.
    
    .EXAMPLE
        Get-DbatoolsLog -Target "a" -Last 1 -Skip 1
        
        Returns all log entries that targeted the object "a" in the second last execution sent.
    
    .EXAMPLE
        Get-DbatoolsLog -Tag "fail" -Last 5
        
        Returns all log entries within the last 5 executions that contained the tag "fail"
#>
    [CmdletBinding()]
    param (
        [string]
        $FunctionName = "*",
        
        [string]
        $ModuleName = "*",
        
        [AllowNull()]
        $Target,
        
        [string[]]
        $Tag,
        
        [int]
        $Last,
        
        [int]
        $Skip = 0,
        
        [guid]
        $Runspace,
        
        [Sqlcollaborative.Dbatools.Message.MessageLevel[]]
        $Level,
        
        [switch]
        $Errors
    )
    
    begin {
        
    }
    
    process {
        if ($Errors) { $messages = [Sqlcollaborative.Dbatools.Message.LogHost]::GetErrors() | Where-Object { ($_.FunctionName -like $FunctionName) -and ($_.ModuleName -like $ModuleName) } }
        else { $messages = [Sqlcollaborative.Dbatools.Message.LogHost]::GetLog() | Where-Object { ($_.FunctionName -like $FunctionName) -and ($_.ModuleName -like $ModuleName) } }
        
        if (Test-Bound -ParameterName Target) {
            $messages = $messages | Where-Object TargetObject -EQ $Target
        }
        
        if (Test-Bound -ParameterName Tag) {
            $messages = $messages | Where-Object { $_.Tags | Where-Object { $_ -in $Tag } }
        }
        
        if (Test-Bound -ParameterName Runspace) {
            $messages = $messages | Where-Object Runspace -EQ $Runspace
        }
        
        if (Test-Bound -ParameterName Last) {
            $history = Get-History | Where-Object CommandLine -NotLike "Get-DbatoolsLog*" | Select-Object -Last $Last -Skip $Skip
            $start = $history[0].StartExecutionTime
            $end = $history[-1].EndExecutionTime
            
            $messages = $messages | Where-Object { ($_.Timestamp -gt $start) -and ($_.Timestamp -lt $end) -and ($_.Runspace -eq ([System.Management.Automation.Runspaces.Runspace]::DefaultRunspace.InstanceId)) }
        }
        
        if (Test-Bound -ParameterName Level) {
            $messages = $messages | Where-Object Level -In $Level
        }
        
        return $messages
    }
    
    end {
        
    }
}
tools\dbatools\functions\Get-DbaTopResourceUsage.ps1
function Get-DbaTopResourceUsage {
    <#
    .SYNOPSIS
        Returns the top 20 resource consumers for cached queries based on four different metrics: duration, frequency, IO, and CPU.

    .DESCRIPTION
        Returns the top 20 resource consumers for cached queries based on four different metrics: duration, frequency, IO, and CPU.

        This command is based off of queries provided by Michael J. Swart at http://michaeljswart.com/go/Top20

        Per Michael: "I've posted queries like this before, and others have written many other versions of this query. All these queries are based on sys.dm_exec_query_stats."

    .PARAMETER SqlInstance
        Allows you to specify a comma separated list of servers to query.

    .PARAMETER SqlCredential
        Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

    .PARAMETER Database
        The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

    .PARAMETER ExcludeDatabase
        The database(s) to exclude - this list is auto-populated from the server

    .PARAMETER ExcludeSystem
        This will exclude system objects like replication procedures from being returned.

    .PARAMETER Type
        By default, all Types run but you can specify one or more of the following: Duration, Frequency, IO, or CPU

    .PARAMETER Limit
        By default, these query the Top 20 worst offenders (though more than 20 results can be returend if each of the top 20 have more than 1 subsequent result)

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Tags: Query, Performance
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Get-DbaTopResourceUsage

    .EXAMPLE
        Get-DbaTopResourceUsage -SqlInstance sql2008, sql2012
        Return the 80 (20 x 4 types) top usage results by duration, frequency, IO, and CPU servers for servers sql2008 and sql2012

    .EXAMPLE
        Get-DbaTopResourceUsage -SqlInstance sql2008 -Type Duration, Frequency -Database TestDB
        Return the highest usage by duration (top 20) and frequency (top 20) for the TestDB on sql2008

    .EXAMPLE
        Get-DbaTopResourceUsage -SqlInstance sql2016 -Limit 30
        Return the highest usage by duration (top 30) and frequency (top 30) for the TestDB on sql2016

    .EXAMPLE
    Get-DbaTopResourceUsage -SqlInstance sql2008, sql2012 -ExcludeSystem
        Return the 80 (20 x 4 types) top usage results by duration, frequency, IO, and CPU servers for servers sql2008 and sql2012 without any System Objects


    .EXAMPLE
        Get-DbaTopResourceUsage -SqlInstance sql2016| Select *
        Return all the columns plus the QueryPlan column
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [ValidateSet("All", "Duration", "Frequency", "IO", "CPU")]
        [string[]]$Type = "All",
        [int]$Limit = 20,
        [Alias('Silent')]
        [switch]$EnableException,
        [switch]$ExcludeSystem
    )

    begin {

        $instancecolumns = " SERVERPROPERTY('MachineName') AS ComputerName,
        ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
        SERVERPROPERTY('ServerName') AS SqlInstance, "

        if ($database) {
            $wheredb = " and coalesce(db_name(st.dbid), db_name(cast(pa.value AS INT)), 'Resource') in ('$($database -join '', '')')"
        }

        if ($ExcludeDatabase) {
            $wherenotdb = " and coalesce(db_name(st.dbid), db_name(cast(pa.value AS INT)), 'Resource') notin '$($excludedatabase -join '', '')'"
        }

        if ($ExcludeSystem) {
            $whereexcludesystem = " AND coalesce(object_name(st.objectid, st.dbid), '<none>') NOT LIKE 'sp_MS%' "
        }
        $duration = ";with long_queries as
                        (
                            select top $Limit
                                query_hash,
                                sum(total_elapsed_time) elapsed_time
                            from sys.dm_exec_query_stats
                            where query_hash <> 0x0
                            group by query_hash
                            order by sum(total_elapsed_time) desc
                        )
                        select $instancecolumns
                            coalesce(db_name(st.dbid), db_name(cast(pa.value AS INT)), 'Resource') AS [Database],
                            coalesce(object_name(st.objectid, st.dbid), '<none>') as ObjectName,
                            qs.query_hash as QueryHash,
                            qs.total_elapsed_time / 1000 as TotalElapsedTimeMs,
                            qs.execution_count as ExecutionCount,
                            cast((total_elapsed_time / 1000) / (execution_count + 0.0) as money) as AverageDurationMs,
                            lq.elapsed_time / 1000 as QueryTotalElapsedTimeMs,
                            SUBSTRING(st.TEXT,(qs.statement_start_offset + 2) / 2,
                                (CASE
                                    WHEN qs.statement_end_offset = -1  THEN LEN(CONVERT(NVARCHAR(MAX),st.text)) * 2
                                    ELSE qs.statement_end_offset
                                    END - qs.statement_start_offset) / 2) as QueryText,
                            qp.query_plan as QueryPlan
                        from sys.dm_exec_query_stats qs
                        join long_queries lq
                            on lq.query_hash = qs.query_hash
                        cross apply sys.dm_exec_sql_text(qs.sql_handle) st
                        cross apply sys.dm_exec_query_plan (qs.plan_handle) qp
                        outer apply sys.dm_exec_plan_attributes(qs.plan_handle) pa
                        where pa.attribute = 'dbid' $wheredb $wherenotdb $whereexcludesystem
                        order by lq.elapsed_time desc,
                            lq.query_hash,
                            qs.total_elapsed_time desc
                        option (recompile)"

        $frequency = ";with frequent_queries as
                        (
                            select top $Limit
                                query_hash,
                                sum(execution_count) executions
                            from sys.dm_exec_query_stats
                            where query_hash <> 0x0
                            group by query_hash
                            order by sum(execution_count) desc
                        )
                        select $instancecolumns
                            coalesce(db_name(st.dbid), db_name(cast(pa.value AS INT)), 'Resource') AS [Database],
                            coalesce(object_name(st.objectid, st.dbid), '<none>') as ObjectName,
                            qs.query_hash as QueryHash,
                            qs.execution_count as ExecutionCount,
                            executions as QueryTotalExecutions,
                            SUBSTRING(st.TEXT,(qs.statement_start_offset + 2) / 2,
                                (CASE
                                    WHEN qs.statement_end_offset = -1  THEN LEN(CONVERT(NVARCHAR(MAX),st.text)) * 2
                                    ELSE qs.statement_end_offset
                                    END - qs.statement_start_offset) / 2) as QueryText,
                            qp.query_plan as QueryPlan
                        from sys.dm_exec_query_stats qs
                        join frequent_queries fq
                            on fq.query_hash = qs.query_hash
                        cross apply sys.dm_exec_sql_text(qs.sql_handle) st
                        cross apply sys.dm_exec_query_plan (qs.plan_handle) qp
                        outer apply sys.dm_exec_plan_attributes(qs.plan_handle) pa
                        where pa.attribute = 'dbid'  $wheredb $wherenotdb $whereexcludesystem
                        order by fq.executions desc,
                            fq.query_hash,
                            qs.execution_count desc
                        option (recompile)"

        $io = ";with high_io_queries as
                (
                    select top $Limit
                        query_hash,
                        sum(total_logical_reads + total_logical_writes) io
                    from sys.dm_exec_query_stats
                    where query_hash <> 0x0
                    group by query_hash
                    order by sum(total_logical_reads + total_logical_writes) desc
                )
                select $instancecolumns
                    coalesce(db_name(st.dbid), db_name(cast(pa.value AS INT)), 'Resource') AS [Database],
                    coalesce(object_name(st.objectid, st.dbid), '<none>') as ObjectName,
                    qs.query_hash as QueryHash,
                    qs.total_logical_reads + total_logical_writes as TotalIO,
                    qs.execution_count as ExecutionCount,
                    cast((total_logical_reads + total_logical_writes) / (execution_count + 0.0) as money) as AverageIO,
                    io as QueryTotalIO,
                    SUBSTRING(st.TEXT,(qs.statement_start_offset + 2) / 2,
                        (CASE
                            WHEN qs.statement_end_offset = -1  THEN LEN(CONVERT(NVARCHAR(MAX),st.text)) * 2
                            ELSE qs.statement_end_offset
                            END - qs.statement_start_offset) / 2) as QueryText,
                    qp.query_plan as QueryPlan
                from sys.dm_exec_query_stats qs
                join high_io_queries fq
                    on fq.query_hash = qs.query_hash
                cross apply sys.dm_exec_sql_text(qs.sql_handle) st
                cross apply sys.dm_exec_query_plan (qs.plan_handle) qp
                outer apply sys.dm_exec_plan_attributes(qs.plan_handle) pa
                where pa.attribute = 'dbid' $wheredb $wherenotdb $whereexcludesystem
                order by fq.io desc,
                    fq.query_hash,
                    qs.total_logical_reads + total_logical_writes desc
                option (recompile)"

        $cpu = ";with high_cpu_queries as
                (
                    select top $Limit
                        query_hash,
                        sum(total_worker_time) cpuTime
                    from sys.dm_exec_query_stats
                    where query_hash <> 0x0
                    group by query_hash
                    order by sum(total_worker_time) desc
                )
                select $instancecolumns
                    coalesce(db_name(st.dbid), db_name(cast(pa.value AS INT)), 'Resource') AS [Database],
                    coalesce(object_name(st.objectid, st.dbid), '<none>') as ObjectName,
                    qs.query_hash as QueryHash,
                    qs.total_worker_time as CpuTime,
                    qs.execution_count as ExecutionCount,
                    cast(total_worker_time / (execution_count + 0.0) as money) as AverageCpuMs,
                    cpuTime as QueryTotalCpu,
                    SUBSTRING(st.TEXT,(qs.statement_start_offset + 2) / 2,
                        (CASE
                            WHEN qs.statement_end_offset = -1  THEN LEN(CONVERT(NVARCHAR(MAX),st.text)) * 2
                            ELSE qs.statement_end_offset
                            END - qs.statement_start_offset) / 2) as QueryText,
                    qp.query_plan as QueryPlan
                from sys.dm_exec_query_stats qs
                join high_cpu_queries hcq
                    on hcq.query_hash = qs.query_hash
                cross apply sys.dm_exec_sql_text(qs.sql_handle) st
                cross apply sys.dm_exec_query_plan (qs.plan_handle) qp
                outer apply sys.dm_exec_plan_attributes(qs.plan_handle) pa
                where pa.attribute = 'dbid' $wheredb $wherenotdb $whereexcludesystem
                order by hcq.cpuTime desc,
                    hcq.query_hash,
                    qs.total_worker_time desc
                option (recompile)"
    }

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            if ($server.ConnectionContext.StatementTimeout -ne 0) {
                $server.ConnectionContext.StatementTimeout = 0
            }

            if ($Type -in "All", "Duration") {
                try {
                    Write-Message -Level Debug -Message "Executing SQL: $duration"
                    $server.Query($duration) | Select-DefaultView -ExcludeProperty QueryPlan
                }
                catch {
                    Stop-Function -Message "Failure executing query for duration." -ErrorRecord $_ -Target $server -Continue
                }
            }

            if ($Type -in "All", "Frequency") {
                try {
                    Write-Message -Level Debug -Message "Executing SQL: $frequency"
                    $server.Query($frequency) | Select-DefaultView -ExcludeProperty QueryPlan
                }
                catch {
                    Stop-Function -Message "Failure executing query for frequency." -ErrorRecord $_ -Target $server -Continue
                }
            }

            if ($Type -in "All", "IO") {
                try {
                    Write-Message -Level Debug -Message "Executing SQL: $io"
                    $server.Query($io) | Select-DefaultView -ExcludeProperty QueryPlan
                }
                catch {
                    Stop-Function -Message "Failure executing query for IO." -ErrorRecord $_ -Target $server -Continue
                }
            }

            if ($Type -in "All", "CPU") {
                try {
                    Write-Message -Level Debug -Message "Executing SQL: $cpu"
                    $server.Query($cpu) | Select-DefaultView -ExcludeProperty QueryPlan
                }
                catch {
                    Stop-Function -Message "Failure executing query for CPU." -ErrorRecord $_ -Target $server -Continue
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaTrace.ps1
function Get-DbaTrace {
    <#
        .SYNOPSIS
        Gets a list of trace(s) from specified SQL Server Instance

        .DESCRIPTION
        This function returns a list of traces on a SQL Server instance and identifies the default trace file

        .PARAMETER SqlInstance
        A SQL Server instance to connect to

        .PARAMETER SqlCredential
        A credential to use to connect to the SQL Instance rather than using Windows Authentication

        .PARAMETER Id
        The id(s) of the Trace

        .PARAMETER Default
        Switch that will only return the information for the default system trace

        .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
        Tags: Security, Trace

        Author: Garry Bargsley (@gbargsley), http://blog.garrybargsley.com

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
        Get-DbaTrace -SqlInstance sql2016

        Lists all the tracefiles on the sql2016 SQL Server.

        .EXAMPLE
        Get-DbaTrace -SqlInstance sql2016 -Default

        Lists the default trace information on the sql2016 SQL Server.

#>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [int[]]$Id,
        [switch]$Default,
        [switch][Alias('Silent')]
        $EnableException
    )
    begin {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Get-DbaTraceFile

        # A Microsoft.SqlServer.Management.Trace.TraceServer class exists but is buggy
        # and requires x86 PowerShell. So we'll go with T-SQL.
        $sql = "SELECT id, status, path, max_size, stop_time, max_files, is_rowset, is_rollover, is_shutdown, is_default, buffer_count, buffer_size, file_position, reader_spid, start_time, last_event_time, event_count, dropped_event_count FROM sys.traces"

        if ($Id) {
            $idstring = $Id -join ","
            $sql = "$sql WHERE id in ($idstring)"
        }
    }
    process {
        foreach ($instance in $SqlInstance) {

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                return
            }

            try {
                $results = $server.Query($sql)
            }
            catch {
                Stop-Function -Message "Issue collecting trace data on $server" -Target $server -ErrorRecord $_
            }

            if ($Default) {
                $results = $results | Where-Object { $_.is_default }
            }

            foreach ($row in $results) {
                if ($row.Path.ToString().Length -gt 0) {
                    $remotefile = Join-AdminUnc -servername $server.ComputerName -filepath $row.path
                }
                else {
                    $remotefile = $null
                }

                [PSCustomObject]@{
                    ComputerName             = $server.ComputerName
                    InstanceName             = $server.ServiceName
                    SqlInstance              = $server.DomainInstanceName
                    Id                       = $row.id
                    Status                   = $row.status
                    IsRunning                = ($row.status -eq 1)
                    Path                     = $row.path
                    RemotePath               = $remotefile
                    MaxSize                  = $row.max_size
                    StopTime                 = $row.stop_time
                    MaxFiles                 = $row.max_files
                    IsRowset                 = $row.is_rowset
                    IsRollover               = $row.is_rollover
                    IsShutdown               = $row.is_shutdown
                    IsDefault                = $row.is_default
                    BufferCount              = $row.buffer_count
                    BufferSize               = $row.buffer_size
                    FilePosition             = $row.file_position
                    ReaderSpid               = $row.reader_spid
                    StartTime                = $row.start_time
                    LastEventTime            = $row.last_event_time
                    EventCount               = $row.event_count
                    DroppedEventCount        = $row.dropped_event_count
                    Parent                   = $server
                    SqlCredential            = $SqlCredential
                } | Select-DefaultView -ExcludeProperty Parent, RemotePath, RemoStatus, SqlCredential
            }
        }
    }
}
tools\dbatools\functions\Get-DbaTraceFlag.ps1
function Get-DbaTraceFlag {
    <#
        .SYNOPSIS
            Get global Trace Flag(s) information for each instance(s) of SQL Server.

        .DESCRIPTION
            Returns Trace Flags that are enabled globally on each instance(s) of SQL Server as an object.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER TraceFlag
            Use this switch to filter to a specific Trace Flag.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: TraceFlag
            Author: Kevin Bullen (@sqlpadawan)

            References:  https://docs.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-traceon-trace-flags-transact-sql

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaTraceFlag

        .EXAMPLE
            Get-DbaTraceFlag -SqlInstance localhost

            Returns all Trace Flag information on the local default SQL Server instance

        .EXAMPLE
            Get-DbaTraceFlag -SqlInstance localhost, sql2016

            Returns all Trace Flag(s) for the local and sql2016 SQL Server instances

        .EXAMPLE
            Get-DbaTraceFlag -SqlInstance localhost -TraceFlag 4199,3205

            Returns Trace Flag status for TF 4199 and 3205 for the local SQL Server instance if they are enabled.
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]
        $SqlCredential,
        [int[]]$TraceFlag,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $tflags = $server.EnumActiveGlobalTraceFlags()

            if ($tFlags.Rows.Count -eq 0) {
                Write-Message -Level Output -Message "No global trace flags enabled"
                return
            }

            if ($TraceFlag) {
                $tflags = $tflags | Where-Object TraceFlag -In $TraceFlag
            }

            foreach ($tflag in $tflags) {
                [pscustomobject]@{
                    ComputerName = $server.ComputerName
                    InstanceName = $server.ServiceName
                    SqlInstance  = $server.DomainInstanceName
                    TraceFlag    = $tflag.TraceFlag
                    Global       = $tflag.Global
                    Session      = $tflag.Session
                    Status       = $tflag.Status
                } | Select-DefaultView -ExcludeProperty 'Session'
            }
        }
    }
}
tools\dbatools\functions\Get-DbaTrigger.ps1
function Get-DbaTrigger {
    <#
.SYNOPSIS
Get all existing triggers on one or more SQL instances.

.DESCRIPTION
Get all existing triggers on one or more SQL instances.

Default output includes columns ComputerName, SqlInstance, Database, TriggerName, IsEnabled and DateLastModified.

.PARAMETER SqlInstance
The SQL Instance that you're connecting to.

.PARAMETER SqlCredential
SqlCredential object used to connect to the SQL Server as a different user.

.PARAMETER Database
The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

.PARAMETER ExcludeDatabase
The database(s) to exclude - this list is auto-populated from the server

.NOTES
Tags: Database, Triggers
Author: Klaas Vandenberghe ( @PowerDBAKlaas )

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
 https://dbatools.io/Get-DbaTrigger

.EXAMPLE
Get-DbaTrigger -SqlInstance ComputerA\sql987

Returns a custom object displaying ComputerName, SqlInstance, Database, TriggerName, IsEnabled and DateLastModified.

.EXAMPLE
Get-DbaTrigger -SqlInstance 'ComputerA\sql987','ComputerB'

Returns a custom object displaying ComputerName, SqlInstance, Database, TriggerName, IsEnabled and DateLastModified from two instances.

.EXAMPLE
Get-DbaTrigger -SqlInstance ComputerA\sql987 | Out-Gridview

Returns a gridview displaying ComputerName, SqlInstance, Database, TriggerName, IsEnabled and DateLastModified.

.EXAMPLE
'ComputerA\sql987','ComputerB' | Get-DbaTrigger | Out-Gridview

Returns a custom object displaying ComputerName, SqlInstance, Database, TriggerName, IsEnabled and DateLastModified from two instances.

#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer", "instance")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase
    )

    process {
        foreach ($Instance in $SqlInstance) {
            Write-Verbose "Connecting to $Instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $Instance -SqlCredential $SqlCredential -Erroraction SilentlyContinue
            }
            catch {
                Write-Warning "Can't connect to $Instance"
                continue
            }

            Write-Verbose "Getting Server Level Triggers on $Instance"
            $server.Triggers |
                ForEach-Object {
                [PSCustomObject]@{
                    ComputerName     = $server.ComputerName
                    InstanceName     = $server.ServiceName
                    SqlInstance      = $server.DomainInstanceName
                    TriggerLevel     = "Server"
                    Database         = $null
                    TriggerName      = $_.Name
                    Status           = switch ($_.IsEnabled) { $true { "Enabled" } $false { "Disabled" } }
                    DateLastModified = $_.DateLastModified
                }
            }

            Write-Verbose "Getting Database Level Triggers on $Instance"
            $dbs = $server.Databases | Where-Object { $_.status -eq 'Normal' }

            if ($Database) {
                $dbs = $dbs | Where-Object Name -in $Database
            }
            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -notin $ExcludeDatabase
            }

            $dbs |
                ForEach-Object {
                $DatabaseName = $_.Name
                Write-Verbose "Getting Database Level Triggers on Database $DatabaseName on $Instance"
                $_.Triggers |
                    ForEach-Object {
                    [PSCustomObject]@{
                        ComputerName     = $server.ComputerName
                        InstanceName     = $server.ServiceName
                        SqlInstance      = $server.DomainInstanceName
                        TriggerLevel     = "Database"
                        Database         = $DatabaseName
                        TriggerName      = $_.Name
                        Status           = switch ($_.IsEnabled) { $true { "Enabled" } $false { "Disabled" } }
                        DateLastModified = $_.DateLastModified
                    }
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaUptime.ps1
function Get-DbaUptime {
    <#
        .SYNOPSIS
            Returns the uptime of the SQL Server instance, and if required the hosting windows server

        .DESCRIPTION
            By default, this command returns for each SQL Server instance passed in:
            SQL Instance last startup time, Uptime as a PS TimeSpan, Uptime as a formatted string
            Hosting Windows server last startup time, Uptime as a PS TimeSpan, Uptime as a formatted string

        .PARAMETER SqlInstance
            The SQL Server instance that you're connecting to.

        .PARAMETER SqlCredential
            Allows you to login to servers using SQL Logins instead of Windows Authentication (AKA Integrated or Trusted). To use:

            $scred = Get-Credential, then pass $scred object to the -SqlCredential parameter.

            Windows Authentication will be used if SqlCredential is not specified. SQL Server does not accept Windows credentials being passed as credentials.

            To connect to SQL Server as a different Windows user, run PowerShell as that user.

        .PARAMETER Credential
            Allows you to login to the computer (not SQL Server instance) using alternative Windows credentials.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: CIM
            Author: Stuart Moore (@napalmgram), stuart-moore.com

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaUptime

        .EXAMPLE
            Get-DbaUptime -SqlInstance SqlBox1\Instance2

            Returns an object with SQL Server start time, uptime as TimeSpan object, uptime as a string, and Windows host boot time, host uptime as TimeSpan objects and host uptime as a string for the sqlexpress instance on winserver

        .EXAMPLE
            Get-DbaUptime -SqlInstance winserver\sqlexpress, sql2016

            Returns an object with SQL Server start time, uptime as TimeSpan object, uptime as a string, and Windows host boot time, host uptime as TimeSpan objects and host uptime as a string for the sqlexpress instance on host winserver  and the default instance on host sql2016

        .EXAMPLE
            Get-DbaRegisteredServer -SqlInstance sql2014 | Get-DbaUptime

            Returns an object with SQL Server start time, uptime as TimeSpan object, uptime as a string, and Windows host boot time, host uptime as TimeSpan objects and host uptime as a string for every server listed in the Central Management Server on sql2014

    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer", "ComputerName")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [PSCredential]$Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $nowutc = (Get-Date).ToUniversalTime()
    }
    process {
        foreach ($instance in $SqlInstance) {
            if ($instance.Gettype().FullName -eq [System.Management.Automation.PSCustomObject] ) {
                $servername = $instance.SqlInstance
            }
            elseif ($instance.Gettype().FullName -eq [Microsoft.SqlServer.Management.Smo.Server]) {
                $servername = $instance.ComputerName
            }
            else {
                $servername = $instance.ComputerName;
            }

            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            Write-Message -Level Verbose -Message "Getting start times for $servername"
            #Get tempdb creation date
            [dbadatetime]$SQLStartTime = $server.Databases["tempdb"].CreateDate
            $SQLUptime = New-TimeSpan -Start $SQLStartTime.ToUniversalTime() -End $nowutc
            $SQLUptimeString = "{0} days {1} hours {2} minutes {3} seconds" -f $($SQLUptime.Days), $($SQLUptime.Hours), $($SQLUptime.Minutes), $($SQLUptime.Seconds)

            $WindowsServerName = (Resolve-DbaNetworkName $servername -Credential $Credential).FullComputerName

            try {
                Write-Message -Level Verbose -Message "Getting WinBootTime via CimInstance for $servername"
                $WinBootTime = (Get-DbaOperatingSystem -ComputerName $windowsServerName -Credential $Credential -ErrorAction SilentlyContinue).LastBootTime
                $WindowsUptime = New-TimeSpan -start $WinBootTime.ToUniversalTime() -end $nowutc
                $WindowsUptimeString = "{0} days {1} hours {2} minutes {3} seconds" -f $($WindowsUptime.Days), $($WindowsUptime.Hours), $($WindowsUptime.Minutes), $($WindowsUptime.Seconds)
            }
            catch {
                try {
                    Write-Message -Level Verbose -Message "Getting WinBootTime via CimInstance DCOM"
                    $CimOption = New-CimSessionOption -Protocol DCOM
                    $CimSession = New-CimSession -Credential:$Credential -ComputerName $WindowsServerName -SessionOption $CimOption
                    [dbadatetime]$WinBootTime = ($CimSession | Get-CimInstance -ClassName Win32_OperatingSystem).LastBootUpTime
                    $WindowsUptime = New-TimeSpan -start $WinBootTime.ToUniversalTime() -end $nowutc
                    $WindowsUptimeString = "{0} days {1} hours {2} minutes {3} seconds" -f $($WindowsUptime.Days), $($WindowsUptime.Hours), $($WindowsUptime.Minutes), $($WindowsUptime.Seconds)
                }
                catch {
                    Stop-Function -Message "Failure getting WinBootTime" -ErrorRecord $_ -Target $instance -Continue
                }
            }

            [PSCustomObject]@{
                ComputerName     = $WindowsServerName
                InstanceName     = $server.ServiceName
                SqlServer        = $server.Name
                SqlUptime        = $SQLUptime
                WindowsUptime    = $WindowsUptime
                SqlStartTime     = $SQLStartTime
                WindowsBootTime  = $WinBootTime
                SinceSqlStart    = $SQLUptimeString
                SinceWindowsBoot = $WindowsUptimeString
            }
        }
    }
}
tools\dbatools\functions\Get-DbaUserLevelPermission.ps1
function Get-DbaUserLevelPermission {
    <#
    .SYNOPSIS
        Displays detailed permissions information for the server and database roles and securables.

    .DESCRIPTION
        This command will display all server logins, server level securable, database logins and database securables.

        DISA STIG implementators will find this command useful as it uses Permissions.sql provided by DISA.

        Note that if you Ctrl-C out of this command and end it prematurely, it will leave behind a STIG schema in tempdb.

    .PARAMETER SqlInstance
        Allows you to specify a comma separated list of servers to query.

    .PARAMETER SqlCredential
        Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

    .PARAMETER Database
        The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

    .PARAMETER ExcludeDatabase
        The database(s) to exclude - this list is auto-populated from the server

    .PARAMETER ExcludeSystemDatabase
        Allows you to suppress output on system databases

    .PARAMETER IncludePublicGuest
        Allows you to include output for public and guest grants.

    .PARAMETER IncludeSystemObjects
        Allows you to include output on sys schema objects.

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
    Tags: Discovery, Permissions, Security
    Author: Brandon Abshire, netnerds.net

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Get-DbaUserLevelPermission

    .EXAMPLE
        Get-DbaUserLevelPermission -SqlInstance sql2008, sqlserver2012
        Check server and database permissions for servers sql2008 and sqlserver2012.

    .EXAMPLE
        Get-DbaUserLevelPermission -SqlInstance sql2008 -Database TestDB
        Check server and database permissions on server sql2008 for only the TestDB database

    .EXAMPLE
        Get-DbaUserLevelPermission -SqlInstance sql2008 -Database TestDB -IncludePublicGuest -IncludeSystemObjects
        Check server and database permissions on server sql2008 for only the TestDB database,
        including public and guest grants, and sys schema objects.
    #>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [parameter(Position = 1, Mandatory = $false)]
        [switch]$ExcludeSystemDatabase,
        [switch]$IncludePublicGuest,
        [switch]$IncludeSystemObjects,
        [Alias('Silent')]
        [switch]$EnableException
    )

    BEGIN {

        $sql = [System.IO.File]::ReadAllText("$script:PSModuleRoot\bin\stig.sql")

        $endSQL = "	   BEGIN TRY DROP FUNCTION STIG.server_effective_permissions END TRY BEGIN CATCH END CATCH;
                       GO
                       BEGIN TRY DROP VIEW STIG.server_permissions END TRY BEGIN CATCH END CATCH;
                       GO
                       BEGIN TRY DROP FUNCTION STIG.members_of_server_role END TRY BEGIN CATCH END CATCH;
                       GO
                       BEGIN TRY DROP FUNCTION STIG.server_roles_of END TRY BEGIN CATCH END CATCH;
                       GO
                       BEGIN TRY DROP VIEW STIG.server_role_members END TRY BEGIN CATCH END CATCH;
                       GO
                       BEGIN TRY DROP FUNCTION STIG.database_effective_permissions END TRY BEGIN CATCH END CATCH;
                       GO
                       BEGIN TRY DROP VIEW STIG.database_permissions END TRY BEGIN CATCH END CATCH;
                       GO
                       BEGIN TRY DROP FUNCTION STIG.members_of_db_role END TRY BEGIN CATCH END CATCH;
                       GO
                       BEGIN TRY DROP FUNCTION STIG.database_roles_of END TRY BEGIN CATCH END CATCH;
                       GO
                       BEGIN TRY DROP VIEW STIG.database_role_members END TRY BEGIN CATCH END CATCH;
                       GO
                       BEGIN TRY DROP SCHEMA STIG END TRY BEGIN CATCH END CATCH;
                       GO"


        $serverSQL = "SELECT  'SERVER LOGINS' AS Type ,
                                    sl.name AS Member ,
                                    ISNULL(srm.role, 'None') AS [Role/Securable/Class] ,
                                    ' ' AS [Schema/Owner] ,
                                    ' ' AS [Securable] ,
                                    ' ' AS [Grantee Type] ,
                                    ' ' AS [Grantee] ,
                                    ' ' AS [Permission] ,
                                    ' ' AS [State] ,
                                    ' ' AS [Grantor] ,
                                    ' ' AS [Grantor Type] ,
                                    ' ' AS [Source View]
                            FROM    master.sys.syslogins sl
                                    LEFT JOIN tempdb.[STIG].[server_role_members] srm ON sl.name = srm.member
                            WHERE   sl.name NOT LIKE 'NT %'
                                    AND sl.name NOT LIKE '##%'
                            UNION
                            SELECT  'SERVER SECURABLES' AS Type ,
                                    sl.name ,
                                    sp.[Securable Class] COLLATE SQL_Latin1_General_CP1_CI_AS ,
                                    ' ' ,
                                    sp.[Securable] ,
                                    sp.[Grantee Type] COLLATE SQL_Latin1_General_CP1_CI_AS ,
                                    sp.Grantee ,
                                    sp.Permission COLLATE SQL_Latin1_General_CP1_CI_AS ,
                                    sp.State COLLATE SQL_Latin1_General_CP1_CI_AS ,
                                    sp.Grantor ,
                                    sp.[Grantor Type] COLLATE SQL_Latin1_General_CP1_CI_AS ,
                                    sp.[Source View]
                            FROM    master.sys.syslogins sl
                                    LEFT JOIN tempdb.[STIG].[server_permissions] sp ON sl.name = sp.Grantee
                            WHERE   sl.name NOT LIKE 'NT %'
                                    AND sl.name NOT LIKE '##%';"

        $dbSQL = "SELECT  'DB ROLE MEMBERS' AS type ,
                                Member ,
                                Role ,
                                ' ' AS [Schema/Owner] ,
                                ' ' AS [Securable] ,
                                ' ' AS [Grantee Type] ,
                                ' ' AS [Grantee] ,
                                ' ' AS [Permission] ,
                                ' ' AS [State] ,
                                ' ' AS [Grantor] ,
                                ' ' AS [Grantor Type] ,
                                ' ' AS [Source View]
                        FROM    tempdb.[STIG].[database_role_members]
                        UNION
                        SELECT DISTINCT
                                'DB SECURABLES' AS Type ,
                                drm.member ,
                                dp.[Securable Type or Class] COLLATE SQL_Latin1_General_CP1_CI_AS ,
                                dp.[Schema/Owner] ,
                                dp.Securable ,
                                dp.[Grantee Type] COLLATE SQL_Latin1_General_CP1_CI_AS ,
                                dp.Grantee ,
                                dp.Permission COLLATE SQL_Latin1_General_CP1_CI_AS ,
                                dp.State COLLATE SQL_Latin1_General_CP1_CI_AS ,
                                dp.Grantor ,
                                dp.[Grantor Type] COLLATE SQL_Latin1_General_CP1_CI_AS ,
                                dp.[Source View]
                        FROM    tempdb.[STIG].[database_role_members] drm
                                LEFT JOIN tempdb.[STIG].[database_permissions] dp ON ( drm.member = dp.grantee
                                                                                      OR drm.role = dp.grantee
                                                                                     )
                        WHERE	dp.Grantor IS NOT NULL
                                AND [Schema/Owner] <> 'sys'"

        if ($IncludePublicGuest) { $dbSQL = $dbSQL.Replace("LEFT JOIN", "FULL JOIN") }
        if ($IncludeSystemObjects) { $dbSQL = $dbSQL.Replace("AND [Schema/Owner] <> 'sys'", "") }

    }

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $dbs = $server.Databases

            if ($Database) {
                $dbs = $dbs | Where-Object { $Database -contains $_.Name }
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            if ($ExcludeSystemDatabase) {
                $dbs = $dbs | Where-Object IsSystemObject -eq $false
            }

            #reset $serverDT
            $serverDT = $null

            foreach ($db in $dbs) {
                Write-Message -Level Verbose -Message "Processing $db on $instance"

                if ($db.IsAccessible -eq $false) {
                    Stop-Function -Message "The database $db is not accessible" -Continue
                }

                $sql = $sql.Replace("<TARGETDB>", $db.Name)

                #Create objects in active database
                Write-Message -Level Verbose -Message "Creating objects"
                try { $db.ExecuteNonQuery($sql) } catch {} # sometimes it complains about not being able to drop the stig schema if the person Ctrl-C'd before.

                #Grab permissions data
                if (-not $serverDT) {
                    Write-Message -Level Verbose -Message "Building data table for server objects"

                    try { $serverDT = $db.Query($serverSQL) } catch { }

                    foreach ($row in $serverDT) {
                        [PSCustomObject]@{
                            ComputerName       = $server.ComputerName
                            InstanceName       = $server.ServiceName
                            SqlInstance        = $server.DomainInstanceName
                            Object             = 'SERVER'
                            Type               = $row.Type
                            Member             = $row.Member
                            RoleSecurableClass = $row.'Role/Securable/Class'
                            SchemaOwner        = $row.'Schema/Owner'
                            Securable          = $row.Securable
                            GranteeType        = $row.'Grantee Type'
                            Grantee            = $row.Grantee
                            Permission         = $row.Permission
                            State              = $row.State
                            Grantor            = $row.Grantor
                            GrantorType        = $row.'Grantor Type'
                            SourceView         = $row.'Source View'
                        }
                    }
                }

                Write-Message -Level Verbose -Message "Building data table for $db objects"
                try { $dbDT = $db.Query($dbSQL) } catch { }

                foreach ($row in $dbDT) {
                    [PSCustomObject]@{
                        ComputerName       = $server.ComputerName
                        InstanceName       = $server.ServiceName
                        SqlInstance        = $server.DomainInstanceName
                        Object             = $db.Name
                        Type               = $row.Type
                        Member             = $row.Member
                        RoleSecurableClass = $row.'Role/Securable/Class'
                        SchemaOwner        = $row.'Schema/Owner'
                        Securable          = $row.Securable
                        GranteeType        = $row.'Grantee Type'
                        Grantee            = $row.Grantee
                        Permission         = $row.Permission
                        State              = $row.State
                        Grantor            = $row.Grantor
                        GrantorType        = $row.'Grantor Type'
                        SourceView         = $row.'Source View'
                    }
                }

                #Delete objects
                Write-Message -Level Verbose -Message "Deleting objects"
                try { $db.ExecuteNonQuery($endSQL) } catch { }
                $sql = $sql.Replace($db.Name, "<TARGETDB>")

                #Sashay Away
            }
        }
    }
}
tools\dbatools\functions\Get-DbaWaitingTask.ps1
function Get-DbaWaitingTask {
    <#
        .SYNOPSIS
            Displays waiting task.

        .DESCRIPTION
            This command is based on waiting task T-SQL script published by Paul Randal.
            Reference: https://www.sqlskills.com/blogs/paul/updated-sys-dm_os_waiting_tasks-script-2/

        .PARAMETER SqlInstance
            The SQL Server instance. Server version must be SQL Server version XXXX or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Spid
            Find the waiting task of one or more specific process ids

        .PARAMETER IncludeSystemSpid
            If this switch is enabled, the output will include the system sessions.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Waits,Task,WaitTask
            Author: Shawn Melton (@wsmelton)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaWaitingTask

        .EXAMPLE
            Get-DbaWaitingTask -SqlInstance sqlserver2014a

            Returns the waiting task for all sessions on sqlserver2014a

        .EXAMPLE
            Get-DbaWaitingTask -SqlInstance sqlserver2014a -IncludeSystemSpid

            Returns the waiting task for all sessions (user and system) on sqlserver2014a
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(ValueFromPipelineByPropertyName = $true)]
        [object[]]$Spid,
        [switch]$IncludeSystemSpid,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $sql = "
            SELECT
                [owt].[session_id] AS [Spid],
                [owt].[exec_context_id] AS [Thread],
                [ot].[scheduler_id] AS [Scheduler],
                [owt].[wait_duration_ms] AS [WaitMs],
                [owt].[wait_type] AS [WaitType],
                [owt].[blocking_session_id] AS [BlockingSpid],
                [owt].[resource_description] AS [ResourceDesc],
                CASE [owt].[wait_type]
                    WHEN N'CXPACKET' THEN
                        RIGHT ([owt].[resource_description],
                            CHARINDEX (N'=', REVERSE ([owt].[resource_description])) - 1)
                    ELSE NULL
                END AS [NodeId],
                [eqmg].[dop] AS [Dop],
                [er].[database_id] AS [DbId],
                [est].text AS [SqlText],
                [eqp].[query_plan] AS [QueryPlan],
                CAST ('https://www.sqlskills.com/help/waits/' + [owt].[wait_type] as XML) AS [URL]
            FROM sys.dm_os_waiting_tasks [owt]
            INNER JOIN sys.dm_os_tasks [ot] ON
                [owt].[waiting_task_address] = [ot].[task_address]
            INNER JOIN sys.dm_exec_sessions [es] ON
                [owt].[session_id] = [es].[session_id]
            INNER JOIN sys.dm_exec_requests [er] ON
                [es].[session_id] = [er].[session_id]
            FULL JOIN sys.dm_exec_query_memory_grants [eqmg] ON
                [owt].[session_id] = [eqmg].[session_id]
            OUTER APPLY sys.dm_exec_sql_text ([er].[sql_handle]) [est]
            OUTER APPLY sys.dm_exec_query_plan ([er].[plan_handle]) [eqp]
            WHERE
                [es].[is_user_process] = $(if (Test-Bound 'IncludeSystemSpid') {0} else {1})
            ORDER BY
                [owt].[session_id],
                [owt].[exec_context_id]
            OPTION(RECOMPILE);"
    }
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $results = $server.Query($sql)
            foreach ($row in $results) {
                if (Test-Bound 'Spid') {
                    if ($row.Spid -notin $Spid) { continue }
                }

                [PSCustomObject]@{
                    ComputerName = $server.ComputerName
                    InstanceName = $server.ServiceName
                    SqlInstance  = $server.DomainInstanceName
                    Spid         = $row.Spid
                    Thread       = $row.Thread
                    Scheduler    = $row.Scheduler
                    WaitMs       = $row.WaitMs
                    WaitType     = $row.WaitType
                    BlockingSpid = $row.BlockingSpid
                    ResourceDesc = $row.ResourceDesc
                    NodeId       = $row.NodeId
                    Dop          = $row.Dop
                    DbId         = $row.DbId
                    SqlText      = $row.SqlText
                    QueryPlan    = $row.QueryPlan
                    InfoUrl      = $row.InfoUrl
                } | Select-DefaultView -ExcludeProperty 'SqlText', 'QueryPlan', 'InfoUrl'
            }
        }
    }
}
tools\dbatools\functions\Get-DbaWaitResource.ps1
function Get-DbaWaitResource {
    <#
    .SYNOPSIS
        Returns the resource being waited upon

    .DESCRIPTION
        Given a wait resource in the form of:
            'PAGE: 10:1:9180084 '
        returns the database, data file and the system object which is being waited up.
        Given a wait resource in the form of:
            'KEY: 7:35457594073541168 (de21f92a1572)'
        returns the database, object and index that is being waited on, With the -row switch the row data will also be returned.
    .PARAMETER SqlInstance
        The SQL Server instance to restore to.

    .PARAMETER SqlCredential
        Allows you to login to servers using SQL Logins as opposed to Windows Auth/Integrated/Trusted.

    .PARAMETER WaitResource
        The waitresource value as supplied in sys.dm_exec_requests

    .PARAMETER Row
        If this switch provided also returns the value of the row being waited on with KEY wait resources

    .PARAMETER EnableException
        Replaces user friendly yellow warnings with bloody red exceptions of doom!
        Use this if you want the function to throw terminating errors you want to catch.

    .EXAMPLE
        Get-DbaWaitResource -SqlInstance server1 -WaitResource 'PAGE: 10:1:9180084'

        Will return an object containing; database name, data file name, schema name and the object which owns the resource

    .EXAMPLE
        Get-DbaWaitResource -Sql Instance server2 -WaitResource 'KEY: 7:35457594073541168 (de21f92a1572)'

        Will return an object containing; database name, schema name and index name which is being waited on.

    .EXAMPLE
        Get-DbaWaitResource -Sql Instance server2 -WaitResource 'KEY: 7:35457594073541168 (de21f92a1572)' -row

        Will return an object containing; database name, schema name and index name which is being waited on, and in addition the contents of the locked row at the time the command is run.

    .NOTES
        Tags: Pages, DBCC
        Author: Stuart Moore (@napalmgram), stuart-moore.com

        dbatools PowerShell module (https://dbatools.io, [email protected])
        Copyright (C) 2016 Chrissy LeMaire
        License: MIT https://opensource.org/licenses/MIT
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance]$SqlInstance,
        [PsCredential]$SqlCredential,
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [string]$WaitResource,
        [switch]$Row,
        [switch]$EnableException
    )

    process {
        if ($WaitResource -notmatch '^PAGE: [0-9]*:[0-9]*:[0-9]*$' -and $WaitResource -notmatch '^KEY: [0-9]*:[0-9]* \([a-f0-9]*\)$'){
           Stop-Function -Message "Row input - $WaitResource - Improperly formatted"
           return
        }

        try {
            $server = Connect-SqlInstance -SqlInstance $sqlinstance -SqlCredential $SqlCredential
        }
        catch {
            Write-Message -Level Warning -Message "Cannot connect to $SqlInstance"
        }

        $null = $WaitResource -match '^(?<Type>[A-Z]*): (?<dbid>[0-9]*):*'
        $ResourceType = $matches.Type
        $DbId = $matches.DbId
        $DbName = ($server.Databases | Where-Object ID -eq $dbid).Name
        if ($null -eq $DbName){
            stop-function -Message "Database with id $dbid does not exist on $server"
            return
        }
        if ($ResourceType -eq 'PAGE'){
            $null = $WaitResource -match '^(?<Type>[A-Z]*): (?<dbid>[0-9]*):(?<FileID>[0-9]*):(?<PageID>[0-9]*)$'
            $DataFileSql = "select name, physical_name from sys.master_files where database_id=$DbID and file_ID=$($matches.FileID);"
            $DataFile = $server.query($DataFileSql)
            if ($null -eq $DataFile){
                Write-Message -Level Warning -Message "Datafile with id $($matches.FileID) for $dbname not found"
                return
            }
            $ObjectIdSQL = "dbcc traceon (3604); dbcc page ($dbid,$($matches.fileID),$($matches.PageID),2) with tableresults;"
            try {
                $ObjectID = ($server.databases[$dbname].Query($ObjectIdSQL) | Where-Object Field -eq 'Metadata: ObjectId').Value
            }
            catch {
                Stop-Function -Message "You've requested a page beyond the end of the database, exiting"
                return
            }
            if ($null -eq $ObjectID){
            Write-Message -Level Warning -Message "Object not found, could have been delete, or a transcription error when copying the Wait_resource to PowerShell"
            return
            }
            $ObjectSql = "select SCHEMA_NAME(schema_id) as SchemaName, name, type_desc from sys.all_objects where object_id=$objectID;"
            $Object = $server.databases[$dbname].query($ObjectSql)
            if ($null -eq $Object){
                Write-Message -Warning "Object could not be found. Could have been removed, or could be a transcription error copying the Wait_resource to sowerShell"
            }
            [PsCustomObject]@{
                DatabaseID = $DbId
                DatabaseName = $DbName
                DataFileName = $Datafile.name
                DataFilePath = $DataFile.physical_name
                ObjectID = $ObjectID
                ObjectName = $Object.Name
                ObjectSchema = $Object.SchemaName
                ObjectType = $Object.type_desc
            }
        }
        if ($ResourceType -eq 'KEY'){
            $null = $WaitResource -match '^(?<Type>[A-Z]*): (?<dbid>[0-9]*):(?<frodo>[0-9]*) (?<physloc>\(.*\))$'
            $IndexSql = "select
                            sp.object_id as ObjectID,
                            OBJECT_SCHEMA_NAME(sp.object_id) as SchemaName,
                            sao.name as ObjectName,
                            si.name as IndexName
                        from
                            sys.partitions sp inner join sys.indexes si on sp.index_id=si.index_id and sp.object_id=si.object_id
                                inner join sys.all_objects sao on sp.object_id=sao.object_id
                        where
                            hobt_id = $($matches.frodo);
                "
            $Index = $server.databases[$dbname].Query($IndexSql)
            if ($null -eq $Index){
                Write-Message -Level Warning -Message "Heap or B-Tree with ID $($matches.frodo) can not be found in $dbname on $server"
                return
            }
            $output = [PsCustomObject]@{
                DatabaseID = $DbId
                DatabaseName = $DbName
                SchemaName = $Index.SchemaName
                IndexName = $Index.IndexName
                ObjectID = $index.ObjectID
                Objectname = $index.ObjectName
                HobtID = $matches.frodo
            }
            if ($row -eq $True){
                $DataSql = "select * from $($Index.SchemaName).$($Index.ObjectName) with (NOLOCK) where %%lockres%% ='$($matches.physloc)'"
                $Data = $server.databases[$dbname].query($DataSql)
                if ($null -eq $data){
                    Write-Message -Level warning -Message "Could not retrieve the data. It may have been deleted or moved since the wait resource value was generated"
                }
                else{
                    $output | Add-Member -Type NoteProperty -Name ObjectData -Value $Data
                    $output | Select-Object * -ExpandProperty ObjectData
                }
            }
            else {
                $output
            }
        }
    }
}
tools\dbatools\functions\Get-DbaWaitStatistic.ps1
function Get-DbaWaitStatistic {
    <#
        .SYNOPSIS
            Displays wait statistics

        .DESCRIPTION
            This command is based off of Paul Randal's post "Wait statistics, or please tell me where it hurts"

            Returns:
                        WaitType
                        Category
                        WaitSeconds
                        ResourceSeconds
                        SignalSeconds
                        WaitCount
                        Percentage
                        AverageWaitSeconds
                        AverageResourceSeconds
                        AverageSignalSeconds
                        URL

            Reference: https://www.sqlskills.com/blogs/paul/wait-statistics-or-please-tell-me-where-it-hurts/

        .PARAMETER SqlInstance
            The SQL Server instance. Server version must be SQL Server version 2005 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Threshold
            Threshold, in percentage of all waits on the system. Default per Paul's post is 95%.

        .PARAMETER IncludeIgnorable
            Some waits are no big deal and can be safely ignored in most circumstances. If you've got weird issues with mirroring or AGs.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: WaitStatistic
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaWaitStatistic

        .EXAMPLE
            Get-DbaWaitStatistic -SqlInstance sql2008, sqlserver2012

            Check wait statistics for servers sql2008 and sqlserver2012

        .EXAMPLE
            Get-DbaWaitStatistic -SqlInstance sql2008 -Threshold 98 -IncludeIgnorable

            Check wait statistics on server sql2008 for thresholds above 98% and include wait stats that are most often, but not always, ignorable

        .EXAMPLE
            Get-DbaWaitStatistic -SqlInstance sql2008 | Select *

            Shows detailed notes, if available, from Paul's post

        .EXAMPLE
            $output = Get-DbaWaitStatistic -SqlInstance sql2008 -Threshold 100 -IncludeIgnorable | Select * | ConvertTo-DbaDataTable

            Collects all Wait Statistics (including ignorable waits) on server sql2008 into a Data Table.


        .EXAMPLE
            $output = Get-DbaWaitStatistic -SqlInstance sql2008
            $output
            foreach ($row in ($output | Sort-Object -Unique Url)) { Start-Process ($row).Url }

            Displays the output then loads the associated sqlskills website for each result. Opens one tab per unique URL.
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [int]$Threshold = 95,
        [switch]$IncludeIgnorable,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {

        $details = [pscustomobject]@{
            CXPACKET                         = "This indicates parallelism, not necessarily that there's a problem. The coordinator thread in a parallel query always accumulates these waits. If the parallel threads are not given equal amounts of work to do, or one thread blocks, the waiting threads will also accumulate CXPACKET waits, which will make them aggregate a lot faster - this is a problem. One thread may have a lot more to do than the others, and so the whole query is blocked while the long-running thread completes. If this is combined with a high number of PAGEIOLATCH_XX waits, it could be large parallel table scans going on because of incorrect non-clustered indexes, or a bad query plan. If neither of these are the issue, you might want to try setting MAXDOP to 4, 2, or 1 for the offending queries (or possibly the whole instance). Make sure that if you have a NUMA system that you try setting MAXDOP to the number of cores in a single NUMA node first to see if that helps the problem. You also need to consider the MAXDOP effect on a mixed-load system. Play with the cost threshold for parallelism setting (bump it up to, say, 25) before reducing the MAXDOP of the whole instance. And don't forget Resource Governor in Enterprise Edition of  SQL Server 2008 onward that allows DOP governing for a particular group of connections to the server."
            PAGEIOLATCH_XX                   = "This is where SQL Server is waiting for a data page to be read from disk into memory. It may indicate a bottleneck at the IO subsystem level (which is a common knee-jerk response to seeing these), but why is the I/O subsystem having to service so many reads? It could be buffer pool/memory pressure (i.e. not enough memory for the workload), a sudden change in query plans causing a large parallel scan instead of a seek, plan cache bloat, or a number of other things. Don't assume the root cause is the I/O subsystem."
            ASYNC_NETWORK_IO                 = "This is usually where SQL Server is waiting for a client to finish consuming data. It could be that the client has asked for a very large amount of data or just that it's consuming it reeeeeally slowly because of poor programming – I rarely see this being a network issue. Clients often process one row at a time – called RBAR or Row-By-Agonizing-Row – instead of caching the data on the client and acknowledging to SQL Server immediately."
            WRITELOG                         = "This is the log management system waiting for a log flush to disk. It commonly indicates that the I/O subsystem can't keep up with the log flush volume, but on very high-volume systems it could also be caused by internal log flush limits, that may mean you have to split your workload over multiple databases or even make your transactions a little longer to reduce log flushes. To be sure it is the I/O subsystem, use the DMV sys.dm_io_virtual_file_stats to examine the I/O latency for the log file and see if it correlates to the average WRITELOG time. If WRITELOG is longer, you've got internal contention and need to shard. If not, investigate why you're creating so much transaction log."
            BROKER_RECEIVE_WAITFOR           = "This is just Service Broker waiting around for new messages to receive. I would add this to the list of waits to filter out and re-run the wait stats query."
            MSQL_XP                          = "This is SQL Server waiting for an extended stored-proc to finish. This could indicate a problem in your XP code."
            OLEDB                            = "As its name suggests, this is a wait for something communicating using OLEDB – e.g. a linked server. However, OLEDB is also used by all DMVs and by DBCC CHECKDB, so don't assume linked servers are the problem – it could be a third-party monitoring tool making excessive DMV calls. If it *is* a linked server (wait times in the 10s or 100s of milliseconds), go to the linked server and do wait stats analysis there to figure out what the performance issue is there."
            BACKUPIO                         = "This can show up when you're backing up to a slow I/O subsystem, like directly to tape, which is slooooow, or over a network."
            LCK_M_XX                         = "This is simply the thread waiting for a lock to be granted and indicates blocking problems. These could be caused by unwanted lock escalation or bad programming, but could also be from I/Os taking a long time causing locks to be held for longer than usual. Look at the resource associated with the lock using the DMV sys.dm_os_waiting_tasks. Don't assume that locking is the root cause."
            ONDEMAND_TASK_QUEUE              = "This is normal and is part of the background task system (e.g. deferred drop, ghost cleanup).  I would add this to the list of waits to filter out and re-run the wait stats query."
            BACKUPBUFFER                     = "This commonly show up with BACKUPIO and is a backup thread waiting for a buffer to write backup data into."
            IO_COMPLETION                    = "This is SQL Server waiting for non-data page I/Os to complete and could be an indication that the I/O subsystem is overloaded if the latencies look high (see Are I/O latencies killing your performance?)"
            SOS_SCHEDULER_YIELD              = "This is code running that doesn't hit any resource waits."
            DBMIRROR_EVENTS_QUEUE            = "These two are database mirroring just sitting around waiting for something to do. I would add these to the list of waits to filter out and re-run the wait stats query."
            DBMIRRORING_CMD                  = "These two are database mirroring just sitting around waiting for something to do. I would add these to the list of waits to filter out and re-run the wait stats query."
            PAGELATCH_XX                     = "This is contention for access to in-memory copies of pages. The most well-known cases of these are the PFS and SGAM contention that can occur in tempdb under certain workloads. To find out what page the contention is on, you'll need to use the DMV sys.dm_os_waiting_tasks to figure out what page the latch is for. For tempdb issues, Robert Davis (blog | twitter) has a good post showing how to do this. Another common cause I've seen is an index hot-spot with concurrent inserts into an index with an identity value key."
            LATCH_XX                         = "This is contention for some non-page structure inside SQL Server – so not related to I/O or data at all. These can be hard to figure out and you're going to be using the DMV sys.dm_os_latch_stats. More on this in my Latches category."
            PREEMPTIVE_OS_PIPEOPS            = "This is SQL Server switching to preemptive scheduling mode to call out to Windows for something, and this particular wait is usually from using xp_cmdshell. These were added for 2008 and aren't documented anywhere except through the links to my waits library."
            THREADPOOL                       = "This says that there aren't enough worker threads on the system to satisfy demand. Commonly this is large numbers of high-DOP queries trying to execute and taking all the threads from the thread pool."
            BROKER_TRANSMITTER               = "This is just Service Broker waiting around for new messages to send. I would add this to the list of waits to filter out and re-run the wait stats query."
            SQLTRACE_WAIT_ENTRIES            = "Part of SQL Trace. I would add this to the list of waits to filter out and re-run the wait stats query."
            DBMIRROR_DBM_MUTEX               = "This one is undocumented and is contention for the send buffer that database mirroring shares between all the mirroring sessions on a server. It could indicate that you've got too many mirroring sessions."
            RESOURCE_SEMAPHORE               = "This is queries waiting for execution memory (the memory used to process the query operators – like a sort). This could be memory pressure or a very high concurrent workload."
            PREEMPTIVE_OS_AUTHENTICATIONOPS  = "These are SQL Server switching to preemptive scheduling mode to call out to Windows for something. These were added for 2008 and aren't documented anywhere except through the links to my waits library."
            PREEMPTIVE_OS_GENERICOPS         = "These are SQL Server switching to preemptive scheduling mode to call out to Windows for something. These were added for 2008 and aren't documented anywhere except through the links to my waits library."
            SLEEP_BPOOL_FLUSH                = "This is normal to see and indicates that checkpoint is throttling itself to avoid overloading the IO subsystem. I would add this to the list of waits to filter out and re-run the wait stats query."
            MSQL_DQ                          = "This is SQL Server waiting for a distributed query to finish. This could indicate a problem with the distributed query, or it could just be normal."
            RESOURCE_SEMAPHORE_QUERY_COMPILE = "When there are too many concurrent query compilations going on, SQL Server will throttle them. I don't remember the threshold, but this can indicate excessive recompilation, or maybe single-use plans."
            DAC_INIT                         = "This is the Dedicated Admin Connection initializing."
            MSSEARCH                         = "This is normal to see for full-text operations.  If this is the highest wait, it could mean your system is spending most of its time doing full-text queries. You might want to consider adding this to the filter list."
            PREEMPTIVE_OS_FILEOPS            = "These are SQL Server switching to preemptive scheduling mode to call out to Windows for something. These were added for 2008 and aren't documented anywhere except through the links to my waits library."
            PREEMPTIVE_OS_LIBRARYOPS         = "These are SQL Server switching to preemptive scheduling mode to call out to Windows for something. These were added for 2008 and aren't documented anywhere except through the links to my waits library."
            PREEMPTIVE_OS_LOOKUPACCOUNTSID   = "These are SQL Server switching to preemptive scheduling mode to call out to Windows for something. These were added for 2008 and aren't documented anywhere except through the links to my waits library."
            PREEMPTIVE_OS_QUERYREGISTRY      = "These are SQL Server switching to preemptive scheduling mode to call out to Windows for something. These were added for 2008 and aren't documented anywhere except through the links to my waits library."
            SQLTRACE_LOCK                    = "Part of SQL Trace. I would add this to the list of waits to filter out and re-run the wait stats query."
        }

        # Thanks Brentg Ozar via https://gist.github.com/BrentOzar/42e82ee0603a1917c17d74c3fca26d34
        # Thanks Marcin Gminski‏ via https://www.dropbox.com/s/x3zr7u18tc1ojey/WaitStats.sql?dl=0

        $category = [pscustomobject]@{
            ASYNC_IO_COMPLETION                             = 'Other Disk IO'
            ASYNC_NETWORK_IO                                = 'Network IO'
            BACKUPIO                                        = 'Other Disk IO'
            BROKER_CONNECTION_RECEIVE_TASK                  = 'Service Broker'
            BROKER_DISPATCHER                               = 'Service Broker'
            BROKER_ENDPOINT_STATE_MUTEX                     = 'Service Broker'
            BROKER_EVENTHANDLER                             = 'Service Broker'
            BROKER_FORWARDER                                = 'Service Broker'
            BROKER_INIT                                     = 'Service Broker'
            BROKER_MASTERSTART                              = 'Service Broker'
            BROKER_RECEIVE_WAITFOR                          = 'User Wait'
            BROKER_REGISTERALLENDPOINTS                     = 'Service Broker'
            BROKER_SERVICE                                  = 'Service Broker'
            BROKER_SHUTDOWN                                 = 'Service Broker'
            BROKER_START                                    = 'Service Broker'
            BROKER_TASK_SHUTDOWN                            = 'Service Broker'
            BROKER_TASK_STOP                                = 'Service Broker'
            BROKER_TASK_SUBMIT                              = 'Service Broker'
            BROKER_TO_FLUSH                                 = 'Service Broker'
            BROKER_TRANSMISSION_OBJECT                      = 'Service Broker'
            BROKER_TRANSMISSION_TABLE                       = 'Service Broker'
            BROKER_TRANSMISSION_WORK                        = 'Service Broker'
            BROKER_TRANSMITTER                              = 'Service Broker'
            CHECKPOINT_QUEUE                                = 'Idle'
            CHKPT                                           = 'Tran Log IO'
            CLR_AUTO_EVENT                                  = 'SQL CLR'
            CLR_CRST                                        = 'SQL CLR'
            CLR_JOIN                                        = 'SQL CLR'
            CLR_MANUAL_EVENT                                = 'SQL CLR'
            CLR_MEMORY_SPY                                  = 'SQL CLR'
            CLR_MONITOR                                     = 'SQL CLR'
            CLR_RWLOCK_READER                               = 'SQL CLR'
            CLR_RWLOCK_WRITER                               = 'SQL CLR'
            CLR_SEMAPHORE                                   = 'SQL CLR'
            CLR_TASK_START                                  = 'SQL CLR'
            CLRHOST_STATE_ACCESS                            = 'SQL CLR'
            CMEMPARTITIONED                                 = 'Memory'
            CMEMTHREAD                                      = 'Memory'
            CXPACKET                                        = 'Parallelism'
            DBMIRROR_DBM_EVENT                              = 'Mirroring'
            DBMIRROR_DBM_MUTEX                              = 'Mirroring'
            DBMIRROR_EVENTS_QUEUE                           = 'Mirroring'
            DBMIRROR_SEND                                   = 'Mirroring'
            DBMIRROR_WORKER_QUEUE                           = 'Mirroring'
            DBMIRRORING_CMD                                 = 'Mirroring'
            DTC                                             = 'Transaction'
            DTC_ABORT_REQUEST                               = 'Transaction'
            DTC_RESOLVE                                     = 'Transaction'
            DTC_STATE                                       = 'Transaction'
            DTC_TMDOWN_REQUEST                              = 'Transaction'
            DTC_WAITFOR_OUTCOME                             = 'Transaction'
            DTCNEW_ENLIST                                   = 'Transaction'
            DTCNEW_PREPARE                                  = 'Transaction'
            DTCNEW_RECOVERY                                 = 'Transaction'
            DTCNEW_TM                                       = 'Transaction'
            DTCNEW_TRANSACTION_ENLISTMENT                   = 'Transaction'
            DTCPNTSYNC                                      = 'Transaction'
            EE_PMOLOCK                                      = 'Memory'
            EXCHANGE                                        = 'Parallelism'
            EXTERNAL_SCRIPT_NETWORK_IOF                     = 'Network IO'
            FCB_REPLICA_READ                                = 'Replication'
            FCB_REPLICA_WRITE                               = 'Replication'
            FT_COMPROWSET_RWLOCK                            = 'Full Text Search'
            FT_IFTS_RWLOCK                                  = 'Full Text Search'
            FT_IFTS_SCHEDULER_IDLE_WAIT                     = 'Idle'
            FT_IFTSHC_MUTEX                                 = 'Full Text Search'
            FT_IFTSISM_MUTEX                                = 'Full Text Search'
            FT_MASTER_MERGE                                 = 'Full Text Search'
            FT_MASTER_MERGE_COORDINATOR                     = 'Full Text Search'
            FT_METADATA_MUTEX                               = 'Full Text Search'
            FT_PROPERTYLIST_CACHE                           = 'Full Text Search'
            FT_RESTART_CRAWL                                = 'Full Text Search'
            'FULLTEXT GATHERER'                             = 'Full Text Search'
            HADR_AG_MUTEX                                   = 'Replication'
            HADR_AR_CRITICAL_SECTION_ENTRY                  = 'Replication'
            HADR_AR_MANAGER_MUTEX                           = 'Replication'
            HADR_AR_UNLOAD_COMPLETED                        = 'Replication'
            HADR_ARCONTROLLER_NOTIFICATIONS_SUBSCRIBER_LIST = 'Replication'
            HADR_BACKUP_BULK_LOCK                           = 'Replication'
            HADR_BACKUP_QUEUE                               = 'Replication'
            HADR_CLUSAPI_CALL                               = 'Replication'
            HADR_COMPRESSED_CACHE_SYNC                      = 'Replication'
            HADR_CONNECTIVITY_INFO                          = 'Replication'
            HADR_DATABASE_FLOW_CONTROL                      = 'Replication'
            HADR_DATABASE_VERSIONING_STATE                  = 'Replication'
            HADR_DATABASE_WAIT_FOR_RECOVERY                 = 'Replication'
            HADR_DATABASE_WAIT_FOR_RESTART                  = 'Replication'
            HADR_DATABASE_WAIT_FOR_TRANSITION_TO_VERSIONING = 'Replication'
            HADR_DB_COMMAND                                 = 'Replication'
            HADR_DB_OP_COMPLETION_SYNC                      = 'Replication'
            HADR_DB_OP_START_SYNC                           = 'Replication'
            HADR_DBR_SUBSCRIBER                             = 'Replication'
            HADR_DBR_SUBSCRIBER_FILTER_LIST                 = 'Replication'
            HADR_DBSEEDING                                  = 'Replication'
            HADR_DBSEEDING_LIST                             = 'Replication'
            HADR_DBSTATECHANGE_SYNC                         = 'Replication'
            HADR_FABRIC_CALLBACK                            = 'Replication'
            HADR_FILESTREAM_BLOCK_FLUSH                     = 'Replication'
            HADR_FILESTREAM_FILE_CLOSE                      = 'Replication'
            HADR_FILESTREAM_FILE_REQUEST                    = 'Replication'
            HADR_FILESTREAM_IOMGR                           = 'Replication'
            HADR_FILESTREAM_IOMGR_IOCOMPLETION              = 'Replication'
            HADR_FILESTREAM_MANAGER                         = 'Replication'
            HADR_FILESTREAM_PREPROC                         = 'Replication'
            HADR_GROUP_COMMIT                               = 'Replication'
            HADR_LOGCAPTURE_SYNC                            = 'Replication'
            HADR_LOGCAPTURE_WAIT                            = 'Replication'
            HADR_LOGPROGRESS_SYNC                           = 'Replication'
            HADR_NOTIFICATION_DEQUEUE                       = 'Replication'
            HADR_NOTIFICATION_WORKER_EXCLUSIVE_ACCESS       = 'Replication'
            HADR_NOTIFICATION_WORKER_STARTUP_SYNC           = 'Replication'
            HADR_NOTIFICATION_WORKER_TERMINATION_SYNC       = 'Replication'
            HADR_PARTNER_SYNC                               = 'Replication'
            HADR_READ_ALL_NETWORKS                          = 'Replication'
            HADR_RECOVERY_WAIT_FOR_CONNECTION               = 'Replication'
            HADR_RECOVERY_WAIT_FOR_UNDO                     = 'Replication'
            HADR_REPLICAINFO_SYNC                           = 'Replication'
            HADR_SEEDING_CANCELLATION                       = 'Replication'
            HADR_SEEDING_FILE_LIST                          = 'Replication'
            HADR_SEEDING_LIMIT_BACKUPS                      = 'Replication'
            HADR_SEEDING_SYNC_COMPLETION                    = 'Replication'
            HADR_SEEDING_TIMEOUT_TASK                       = 'Replication'
            HADR_SEEDING_WAIT_FOR_COMPLETION                = 'Replication'
            HADR_SYNC_COMMIT                                = 'Replication'
            HADR_SYNCHRONIZING_THROTTLE                     = 'Replication'
            HADR_TDS_LISTENER_SYNC                          = 'Replication'
            HADR_TDS_LISTENER_SYNC_PROCESSING               = 'Replication'
            HADR_THROTTLE_LOG_RATE_GOVERNOR                 = 'Log Rate Governor'
            HADR_TIMER_TASK                                 = 'Replication'
            HADR_TRANSPORT_DBRLIST                          = 'Replication'
            HADR_TRANSPORT_FLOW_CONTROL                     = 'Replication'
            HADR_TRANSPORT_SESSION                          = 'Replication'
            HADR_WORK_POOL                                  = 'Replication'
            HADR_WORK_QUEUE                                 = 'Replication'
            HADR_XRF_STACK_ACCESS                           = 'Replication'
            INSTANCE_LOG_RATE_GOVERNOR                      = 'Log Rate Governor'
            IO_COMPLETION                                   = 'Other Disk IO'
            IO_QUEUE_LIMIT                                  = 'Other Disk IO'
            IO_RETRY                                        = 'Other Disk IO'
            LATCH_DT                                        = 'Latch'
            LATCH_EX                                        = 'Latch'
            LATCH_KP                                        = 'Latch'
            LATCH_NL                                        = 'Latch'
            LATCH_SH                                        = 'Latch'
            LATCH_UP                                        = 'Latch'
            LAZYWRITER_SLEEP                                = 'Idle'
            LCK_M_BU                                        = 'Lock'
            LCK_M_BU_ABORT_BLOCKERS                         = 'Lock'
            LCK_M_BU_LOW_PRIORITY                           = 'Lock'
            LCK_M_IS                                        = 'Lock'
            LCK_M_IS_ABORT_BLOCKERS                         = 'Lock'
            LCK_M_IS_LOW_PRIORITY                           = 'Lock'
            LCK_M_IU                                        = 'Lock'
            LCK_M_IU_ABORT_BLOCKERS                         = 'Lock'
            LCK_M_IU_LOW_PRIORITY                           = 'Lock'
            LCK_M_IX                                        = 'Lock'
            LCK_M_IX_ABORT_BLOCKERS                         = 'Lock'
            LCK_M_IX_LOW_PRIORITY                           = 'Lock'
            LCK_M_RIn_NL                                    = 'Lock'
            LCK_M_RIn_NL_ABORT_BLOCKERS                     = 'Lock'
            LCK_M_RIn_NL_LOW_PRIORITY                       = 'Lock'
            LCK_M_RIn_S                                     = 'Lock'
            LCK_M_RIn_S_ABORT_BLOCKERS                      = 'Lock'
            LCK_M_RIn_S_LOW_PRIORITY                        = 'Lock'
            LCK_M_RIn_U                                     = 'Lock'
            LCK_M_RIn_U_ABORT_BLOCKERS                      = 'Lock'
            LCK_M_RIn_U_LOW_PRIORITY                        = 'Lock'
            LCK_M_RIn_X                                     = 'Lock'
            LCK_M_RIn_X_ABORT_BLOCKERS                      = 'Lock'
            LCK_M_RIn_X_LOW_PRIORITY                        = 'Lock'
            LCK_M_RS_S                                      = 'Lock'
            LCK_M_RS_S_ABORT_BLOCKERS                       = 'Lock'
            LCK_M_RS_S_LOW_PRIORITY                         = 'Lock'
            LCK_M_RS_U                                      = 'Lock'
            LCK_M_RS_U_ABORT_BLOCKERS                       = 'Lock'
            LCK_M_RS_U_LOW_PRIORITY                         = 'Lock'
            LCK_M_RX_S                                      = 'Lock'
            LCK_M_RX_S_ABORT_BLOCKERS                       = 'Lock'
            LCK_M_RX_S_LOW_PRIORITY                         = 'Lock'
            LCK_M_RX_U                                      = 'Lock'
            LCK_M_RX_U_ABORT_BLOCKERS                       = 'Lock'
            LCK_M_RX_U_LOW_PRIORITY                         = 'Lock'
            LCK_M_RX_X                                      = 'Lock'
            LCK_M_RX_X_ABORT_BLOCKERS                       = 'Lock'
            LCK_M_RX_X_LOW_PRIORITY                         = 'Lock'
            LCK_M_S                                         = 'Lock'
            LCK_M_S_ABORT_BLOCKERS                          = 'Lock'
            LCK_M_S_LOW_PRIORITY                            = 'Lock'
            LCK_M_SCH_M                                     = 'Lock'
            LCK_M_SCH_M_ABORT_BLOCKERS                      = 'Lock'
            LCK_M_SCH_M_LOW_PRIORITY                        = 'Lock'
            LCK_M_SCH_S                                     = 'Lock'
            LCK_M_SCH_S_ABORT_BLOCKERS                      = 'Lock'
            LCK_M_SCH_S_LOW_PRIORITY                        = 'Lock'
            LCK_M_SIU                                       = 'Lock'
            LCK_M_SIU_ABORT_BLOCKERS                        = 'Lock'
            LCK_M_SIU_LOW_PRIORITY                          = 'Lock'
            LCK_M_SIX                                       = 'Lock'
            LCK_M_SIX_ABORT_BLOCKERS                        = 'Lock'
            LCK_M_SIX_LOW_PRIORITY                          = 'Lock'
            LCK_M_U                                         = 'Lock'
            LCK_M_U_ABORT_BLOCKERS                          = 'Lock'
            LCK_M_U_LOW_PRIORITY                            = 'Lock'
            LCK_M_UIX                                       = 'Lock'
            LCK_M_UIX_ABORT_BLOCKERS                        = 'Lock'
            LCK_M_UIX_LOW_PRIORITY                          = 'Lock'
            LCK_M_X                                         = 'Lock'
            LCK_M_X_ABORT_BLOCKERS                          = 'Lock'
            LCK_M_X_LOW_PRIORITY                            = 'Lock'
            LOGBUFFER                                       = 'Tran Log IO'
            LOGMGR                                          = 'Tran Log IO'
            LOGMGR_FLUSH                                    = 'Tran Log IO'
            LOGMGR_PMM_LOG                                  = 'Tran Log IO'
            LOGMGR_QUEUE                                    = 'Idle'
            LOGMGR_RESERVE_APPEND                           = 'Tran Log IO'
            MEMORY_ALLOCATION_EXT                           = 'Memory'
            MEMORY_GRANT_UPDATE                             = 'Memory'
            MSQL_XACT_MGR_MUTEX                             = 'Transaction'
            MSQL_XACT_MUTEX                                 = 'Transaction'
            MSSEARCH                                        = 'Full Text Search'
            NET_WAITFOR_PACKET                              = 'Network IO'
            ONDEMAND_TASK_QUEUE                             = 'Idle'
            PAGEIOLATCH_DT                                  = 'Buffer IO'
            PAGEIOLATCH_EX                                  = 'Buffer IO'
            PAGEIOLATCH_KP                                  = 'Buffer IO'
            PAGEIOLATCH_NL                                  = 'Buffer IO'
            PAGEIOLATCH_SH                                  = 'Buffer IO'
            PAGEIOLATCH_UP                                  = 'Buffer IO'
            PAGELATCH_DT                                    = 'Buffer Latch'
            PAGELATCH_EX                                    = 'Buffer Latch'
            PAGELATCH_KP                                    = 'Buffer Latch'
            PAGELATCH_NL                                    = 'Buffer Latch'
            PAGELATCH_SH                                    = 'Buffer Latch'
            PAGELATCH_UP                                    = 'Buffer Latch'
            POOL_LOG_RATE_GOVERNOR                          = 'Log Rate Governor'
            PREEMPTIVE_ABR                                  = 'Preemptive'
            PREEMPTIVE_CLOSEBACKUPMEDIA                     = 'Preemptive'
            PREEMPTIVE_CLOSEBACKUPTAPE                      = 'Preemptive'
            PREEMPTIVE_CLOSEBACKUPVDIDEVICE                 = 'Preemptive'
            PREEMPTIVE_CLUSAPI_CLUSTERRESOURCECONTROL       = 'Preemptive'
            PREEMPTIVE_COM_COCREATEINSTANCE                 = 'Preemptive'
            PREEMPTIVE_COM_COGETCLASSOBJECT                 = 'Preemptive'
            PREEMPTIVE_COM_CREATEACCESSOR                   = 'Preemptive'
            PREEMPTIVE_COM_DELETEROWS                       = 'Preemptive'
            PREEMPTIVE_COM_GETCOMMANDTEXT                   = 'Preemptive'
            PREEMPTIVE_COM_GETDATA                          = 'Preemptive'
            PREEMPTIVE_COM_GETNEXTROWS                      = 'Preemptive'
            PREEMPTIVE_COM_GETRESULT                        = 'Preemptive'
            PREEMPTIVE_COM_GETROWSBYBOOKMARK                = 'Preemptive'
            PREEMPTIVE_COM_LBFLUSH                          = 'Preemptive'
            PREEMPTIVE_COM_LBLOCKREGION                     = 'Preemptive'
            PREEMPTIVE_COM_LBREADAT                         = 'Preemptive'
            PREEMPTIVE_COM_LBSETSIZE                        = 'Preemptive'
            PREEMPTIVE_COM_LBSTAT                           = 'Preemptive'
            PREEMPTIVE_COM_LBUNLOCKREGION                   = 'Preemptive'
            PREEMPTIVE_COM_LBWRITEAT                        = 'Preemptive'
            PREEMPTIVE_COM_QUERYINTERFACE                   = 'Preemptive'
            PREEMPTIVE_COM_RELEASE                          = 'Preemptive'
            PREEMPTIVE_COM_RELEASEACCESSOR                  = 'Preemptive'
            PREEMPTIVE_COM_RELEASEROWS                      = 'Preemptive'
            PREEMPTIVE_COM_RELEASESESSION                   = 'Preemptive'
            PREEMPTIVE_COM_RESTARTPOSITION                  = 'Preemptive'
            PREEMPTIVE_COM_SEQSTRMREAD                      = 'Preemptive'
            PREEMPTIVE_COM_SEQSTRMREADANDWRITE              = 'Preemptive'
            PREEMPTIVE_COM_SETDATAFAILURE                   = 'Preemptive'
            PREEMPTIVE_COM_SETPARAMETERINFO                 = 'Preemptive'
            PREEMPTIVE_COM_SETPARAMETERPROPERTIES           = 'Preemptive'
            PREEMPTIVE_COM_STRMLOCKREGION                   = 'Preemptive'
            PREEMPTIVE_COM_STRMSEEKANDREAD                  = 'Preemptive'
            PREEMPTIVE_COM_STRMSEEKANDWRITE                 = 'Preemptive'
            PREEMPTIVE_COM_STRMSETSIZE                      = 'Preemptive'
            PREEMPTIVE_COM_STRMSTAT                         = 'Preemptive'
            PREEMPTIVE_COM_STRMUNLOCKREGION                 = 'Preemptive'
            PREEMPTIVE_CONSOLEWRITE                         = 'Preemptive'
            PREEMPTIVE_CREATEPARAM                          = 'Preemptive'
            PREEMPTIVE_DEBUG                                = 'Preemptive'
            PREEMPTIVE_DFSADDLINK                           = 'Preemptive'
            PREEMPTIVE_DFSLINKEXISTCHECK                    = 'Preemptive'
            PREEMPTIVE_DFSLINKHEALTHCHECK                   = 'Preemptive'
            PREEMPTIVE_DFSREMOVELINK                        = 'Preemptive'
            PREEMPTIVE_DFSREMOVEROOT                        = 'Preemptive'
            PREEMPTIVE_DFSROOTFOLDERCHECK                   = 'Preemptive'
            PREEMPTIVE_DFSROOTINIT                          = 'Preemptive'
            PREEMPTIVE_DFSROOTSHARECHECK                    = 'Preemptive'
            PREEMPTIVE_DTC_ABORT                            = 'Preemptive'
            PREEMPTIVE_DTC_ABORTREQUESTDONE                 = 'Preemptive'
            PREEMPTIVE_DTC_BEGINTRANSACTION                 = 'Preemptive'
            PREEMPTIVE_DTC_COMMITREQUESTDONE                = 'Preemptive'
            PREEMPTIVE_DTC_ENLIST                           = 'Preemptive'
            PREEMPTIVE_DTC_PREPAREREQUESTDONE               = 'Preemptive'
            PREEMPTIVE_FILESIZEGET                          = 'Preemptive'
            PREEMPTIVE_FSAOLEDB_ABORTTRANSACTION            = 'Preemptive'
            PREEMPTIVE_FSAOLEDB_COMMITTRANSACTION           = 'Preemptive'
            PREEMPTIVE_FSAOLEDB_STARTTRANSACTION            = 'Preemptive'
            PREEMPTIVE_FSRECOVER_UNCONDITIONALUNDO          = 'Preemptive'
            PREEMPTIVE_GETRMINFO                            = 'Preemptive'
            PREEMPTIVE_HADR_LEASE_MECHANISM                 = 'Preemptive'
            PREEMPTIVE_HTTP_EVENT_WAIT                      = 'Preemptive'
            PREEMPTIVE_HTTP_REQUEST                         = 'Preemptive'
            PREEMPTIVE_LOCKMONITOR                          = 'Preemptive'
            PREEMPTIVE_MSS_RELEASE                          = 'Preemptive'
            PREEMPTIVE_ODBCOPS                              = 'Preemptive'
            PREEMPTIVE_OLE_UNINIT                           = 'Preemptive'
            PREEMPTIVE_OLEDB_ABORTORCOMMITTRAN              = 'Preemptive'
            PREEMPTIVE_OLEDB_ABORTTRAN                      = 'Preemptive'
            PREEMPTIVE_OLEDB_GETDATASOURCE                  = 'Preemptive'
            PREEMPTIVE_OLEDB_GETLITERALINFO                 = 'Preemptive'
            PREEMPTIVE_OLEDB_GETPROPERTIES                  = 'Preemptive'
            PREEMPTIVE_OLEDB_GETPROPERTYINFO                = 'Preemptive'
            PREEMPTIVE_OLEDB_GETSCHEMALOCK                  = 'Preemptive'
            PREEMPTIVE_OLEDB_JOINTRANSACTION                = 'Preemptive'
            PREEMPTIVE_OLEDB_RELEASE                        = 'Preemptive'
            PREEMPTIVE_OLEDB_SETPROPERTIES                  = 'Preemptive'
            PREEMPTIVE_OLEDBOPS                             = 'Preemptive'
            PREEMPTIVE_OS_ACCEPTSECURITYCONTEXT             = 'Preemptive'
            PREEMPTIVE_OS_ACQUIRECREDENTIALSHANDLE          = 'Preemptive'
            PREEMPTIVE_OS_AUTHENTICATIONOPS                 = 'Preemptive'
            PREEMPTIVE_OS_AUTHORIZATIONOPS                  = 'Preemptive'
            PREEMPTIVE_OS_AUTHZGETINFORMATIONFROMCONTEXT    = 'Preemptive'
            PREEMPTIVE_OS_AUTHZINITIALIZECONTEXTFROMSID     = 'Preemptive'
            PREEMPTIVE_OS_AUTHZINITIALIZERESOURCEMANAGER    = 'Preemptive'
            PREEMPTIVE_OS_BACKUPREAD                        = 'Preemptive'
            PREEMPTIVE_OS_CLOSEHANDLE                       = 'Preemptive'
            PREEMPTIVE_OS_CLUSTEROPS                        = 'Preemptive'
            PREEMPTIVE_OS_COMOPS                            = 'Preemptive'
            PREEMPTIVE_OS_COMPLETEAUTHTOKEN                 = 'Preemptive'
            PREEMPTIVE_OS_COPYFILE                          = 'Preemptive'
            PREEMPTIVE_OS_CREATEDIRECTORY                   = 'Preemptive'
            PREEMPTIVE_OS_CREATEFILE                        = 'Preemptive'
            PREEMPTIVE_OS_CRYPTACQUIRECONTEXT               = 'Preemptive'
            PREEMPTIVE_OS_CRYPTIMPORTKEY                    = 'Preemptive'
            PREEMPTIVE_OS_CRYPTOPS                          = 'Preemptive'
            PREEMPTIVE_OS_DECRYPTMESSAGE                    = 'Preemptive'
            PREEMPTIVE_OS_DELETEFILE                        = 'Preemptive'
            PREEMPTIVE_OS_DELETESECURITYCONTEXT             = 'Preemptive'
            PREEMPTIVE_OS_DEVICEIOCONTROL                   = 'Preemptive'
            PREEMPTIVE_OS_DEVICEOPS                         = 'Preemptive'
            PREEMPTIVE_OS_DIRSVC_NETWORKOPS                 = 'Preemptive'
            PREEMPTIVE_OS_DISCONNECTNAMEDPIPE               = 'Preemptive'
            PREEMPTIVE_OS_DOMAINSERVICESOPS                 = 'Preemptive'
            PREEMPTIVE_OS_DSGETDCNAME                       = 'Preemptive'
            PREEMPTIVE_OS_DTCOPS                            = 'Preemptive'
            PREEMPTIVE_OS_ENCRYPTMESSAGE                    = 'Preemptive'
            PREEMPTIVE_OS_FILEOPS                           = 'Preemptive'
            PREEMPTIVE_OS_FINDFILE                          = 'Preemptive'
            PREEMPTIVE_OS_FLUSHFILEBUFFERS                  = 'Preemptive'
            PREEMPTIVE_OS_FORMATMESSAGE                     = 'Preemptive'
            PREEMPTIVE_OS_FREECREDENTIALSHANDLE             = 'Preemptive'
            PREEMPTIVE_OS_FREELIBRARY                       = 'Preemptive'
            PREEMPTIVE_OS_GENERICOPS                        = 'Preemptive'
            PREEMPTIVE_OS_GETADDRINFO                       = 'Preemptive'
            PREEMPTIVE_OS_GETCOMPRESSEDFILESIZE             = 'Preemptive'
            PREEMPTIVE_OS_GETDISKFREESPACE                  = 'Preemptive'
            PREEMPTIVE_OS_GETFILEATTRIBUTES                 = 'Preemptive'
            PREEMPTIVE_OS_GETFILESIZE                       = 'Preemptive'
            PREEMPTIVE_OS_GETFINALFILEPATHBYHANDLE          = 'Preemptive'
            PREEMPTIVE_OS_GETLONGPATHNAME                   = 'Preemptive'
            PREEMPTIVE_OS_GETPROCADDRESS                    = 'Preemptive'
            PREEMPTIVE_OS_GETVOLUMENAMEFORVOLUMEMOUNTPOINT  = 'Preemptive'
            PREEMPTIVE_OS_GETVOLUMEPATHNAME                 = 'Preemptive'
            PREEMPTIVE_OS_INITIALIZESECURITYCONTEXT         = 'Preemptive'
            PREEMPTIVE_OS_LIBRARYOPS                        = 'Preemptive'
            PREEMPTIVE_OS_LOADLIBRARY                       = 'Preemptive'
            PREEMPTIVE_OS_LOGONUSER                         = 'Preemptive'
            PREEMPTIVE_OS_LOOKUPACCOUNTSID                  = 'Preemptive'
            PREEMPTIVE_OS_MESSAGEQUEUEOPS                   = 'Preemptive'
            PREEMPTIVE_OS_MOVEFILE                          = 'Preemptive'
            PREEMPTIVE_OS_NETGROUPGETUSERS                  = 'Preemptive'
            PREEMPTIVE_OS_NETLOCALGROUPGETMEMBERS           = 'Preemptive'
            PREEMPTIVE_OS_NETUSERGETGROUPS                  = 'Preemptive'
            PREEMPTIVE_OS_NETUSERGETLOCALGROUPS             = 'Preemptive'
            PREEMPTIVE_OS_NETUSERMODALSGET                  = 'Preemptive'
            PREEMPTIVE_OS_NETVALIDATEPASSWORDPOLICY         = 'Preemptive'
            PREEMPTIVE_OS_NETVALIDATEPASSWORDPOLICYFREE     = 'Preemptive'
            PREEMPTIVE_OS_OPENDIRECTORY                     = 'Preemptive'
            PREEMPTIVE_OS_PDH_WMI_INIT                      = 'Preemptive'
            PREEMPTIVE_OS_PIPEOPS                           = 'Preemptive'
            PREEMPTIVE_OS_PROCESSOPS                        = 'Preemptive'
            PREEMPTIVE_OS_QUERYCONTEXTATTRIBUTES            = 'Preemptive'
            PREEMPTIVE_OS_QUERYREGISTRY                     = 'Preemptive'
            PREEMPTIVE_OS_QUERYSECURITYCONTEXTTOKEN         = 'Preemptive'
            PREEMPTIVE_OS_REMOVEDIRECTORY                   = 'Preemptive'
            PREEMPTIVE_OS_REPORTEVENT                       = 'Preemptive'
            PREEMPTIVE_OS_REVERTTOSELF                      = 'Preemptive'
            PREEMPTIVE_OS_RSFXDEVICEOPS                     = 'Preemptive'
            PREEMPTIVE_OS_SECURITYOPS                       = 'Preemptive'
            PREEMPTIVE_OS_SERVICEOPS                        = 'Preemptive'
            PREEMPTIVE_OS_SETENDOFFILE                      = 'Preemptive'
            PREEMPTIVE_OS_SETFILEPOINTER                    = 'Preemptive'
            PREEMPTIVE_OS_SETFILEVALIDDATA                  = 'Preemptive'
            PREEMPTIVE_OS_SETNAMEDSECURITYINFO              = 'Preemptive'
            PREEMPTIVE_OS_SQLCLROPS                         = 'Preemptive'
            PREEMPTIVE_OS_SQMLAUNCH                         = 'Preemptive'
            PREEMPTIVE_OS_VERIFYSIGNATURE                   = 'Preemptive'
            PREEMPTIVE_OS_VERIFYTRUST                       = 'Preemptive'
            PREEMPTIVE_OS_VSSOPS                            = 'Preemptive'
            PREEMPTIVE_OS_WAITFORSINGLEOBJECT               = 'Preemptive'
            PREEMPTIVE_OS_WINSOCKOPS                        = 'Preemptive'
            PREEMPTIVE_OS_WRITEFILE                         = 'Preemptive'
            PREEMPTIVE_OS_WRITEFILEGATHER                   = 'Preemptive'
            PREEMPTIVE_OS_WSASETLASTERROR                   = 'Preemptive'
            PREEMPTIVE_REENLIST                             = 'Preemptive'
            PREEMPTIVE_RESIZELOG                            = 'Preemptive'
            PREEMPTIVE_ROLLFORWARDREDO                      = 'Preemptive'
            PREEMPTIVE_ROLLFORWARDUNDO                      = 'Preemptive'
            PREEMPTIVE_SB_STOPENDPOINT                      = 'Preemptive'
            PREEMPTIVE_SERVER_STARTUP                       = 'Preemptive'
            PREEMPTIVE_SETRMINFO                            = 'Preemptive'
            PREEMPTIVE_SHAREDMEM_GETDATA                    = 'Preemptive'
            PREEMPTIVE_SNIOPEN                              = 'Preemptive'
            PREEMPTIVE_SOSHOST                              = 'Preemptive'
            PREEMPTIVE_SOSTESTING                           = 'Preemptive'
            PREEMPTIVE_SP_SERVER_DIAGNOSTICS                = 'Preemptive'
            PREEMPTIVE_STARTRM                              = 'Preemptive'
            PREEMPTIVE_STREAMFCB_CHECKPOINT                 = 'Preemptive'
            PREEMPTIVE_STREAMFCB_RECOVER                    = 'Preemptive'
            PREEMPTIVE_STRESSDRIVER                         = 'Preemptive'
            PREEMPTIVE_TESTING                              = 'Preemptive'
            PREEMPTIVE_TRANSIMPORT                          = 'Preemptive'
            PREEMPTIVE_UNMARSHALPROPAGATIONTOKEN            = 'Preemptive'
            PREEMPTIVE_VSS_CREATESNAPSHOT                   = 'Preemptive'
            PREEMPTIVE_VSS_CREATEVOLUMESNAPSHOT             = 'Preemptive'
            PREEMPTIVE_XE_CALLBACKEXECUTE                   = 'Preemptive'
            PREEMPTIVE_XE_CX_FILE_OPEN                      = 'Preemptive'
            PREEMPTIVE_XE_CX_HTTP_CALL                      = 'Preemptive'
            PREEMPTIVE_XE_DISPATCHER                        = 'Preemptive'
            PREEMPTIVE_XE_ENGINEINIT                        = 'Preemptive'
            PREEMPTIVE_XE_GETTARGETSTATE                    = 'Preemptive'
            PREEMPTIVE_XE_SESSIONCOMMIT                     = 'Preemptive'
            PREEMPTIVE_XE_TARGETFINALIZE                    = 'Preemptive'
            PREEMPTIVE_XE_TARGETINIT                        = 'Preemptive'
            PREEMPTIVE_XE_TIMERRUN                          = 'Preemptive'
            PREEMPTIVE_XETESTING                            = 'Preemptive'
            PWAIT_HADR_ACTION_COMPLETED                     = 'Replication'
            PWAIT_HADR_CHANGE_NOTIFIER_TERMINATION_SYNC     = 'Replication'
            PWAIT_HADR_CLUSTER_INTEGRATION                  = 'Replication'
            PWAIT_HADR_FAILOVER_COMPLETED                   = 'Replication'
            PWAIT_HADR_JOIN                                 = 'Replication'
            PWAIT_HADR_OFFLINE_COMPLETED                    = 'Replication'
            PWAIT_HADR_ONLINE_COMPLETED                     = 'Replication'
            PWAIT_HADR_POST_ONLINE_COMPLETED                = 'Replication'
            PWAIT_HADR_SERVER_READY_CONNECTIONS             = 'Replication'
            PWAIT_HADR_WORKITEM_COMPLETED                   = 'Replication'
            PWAIT_HADRSIM                                   = 'Replication'
            PWAIT_RESOURCE_SEMAPHORE_FT_PARALLEL_QUERY_SYNC = 'Full Text Search'
            QUERY_TRACEOUT                                  = 'Tracing'
            REPL_CACHE_ACCESS                               = 'Replication'
            REPL_HISTORYCACHE_ACCESS                        = 'Replication'
            REPL_SCHEMA_ACCESS                              = 'Replication'
            REPL_TRANFSINFO_ACCESS                          = 'Replication'
            REPL_TRANHASHTABLE_ACCESS                       = 'Replication'
            REPL_TRANTEXTINFO_ACCESS                        = 'Replication'
            REPLICA_WRITES                                  = 'Replication'
            REQUEST_FOR_DEADLOCK_SEARCH                     = 'Idle'
            RESERVED_MEMORY_ALLOCATION_EXT                  = 'Memory'
            RESOURCE_SEMAPHORE                              = 'Memory'
            RESOURCE_SEMAPHORE_QUERY_COMPILE                = 'Compilation'
            SLEEP_BPOOL_FLUSH                               = 'Idle'
            SLEEP_BUFFERPOOL_HELPLW                         = 'Idle'
            SLEEP_DBSTARTUP                                 = 'Idle'
            SLEEP_DCOMSTARTUP                               = 'Idle'
            SLEEP_MASTERDBREADY                             = 'Idle'
            SLEEP_MASTERMDREADY                             = 'Idle'
            SLEEP_MASTERUPGRADED                            = 'Idle'
            SLEEP_MEMORYPOOL_ALLOCATEPAGES                  = 'Idle'
            SLEEP_MSDBSTARTUP                               = 'Idle'
            SLEEP_RETRY_VIRTUALALLOC                        = 'Idle'
            SLEEP_SYSTEMTASK                                = 'Idle'
            SLEEP_TASK                                      = 'Idle'
            SLEEP_TEMPDBSTARTUP                             = 'Idle'
            SLEEP_WORKSPACE_ALLOCATEPAGE                    = 'Idle'
            SOS_SCHEDULER_YIELD                             = 'CPU'
            SQLCLR_APPDOMAIN                                = 'SQL CLR'
            SQLCLR_ASSEMBLY                                 = 'SQL CLR'
            SQLCLR_DEADLOCK_DETECTION                       = 'SQL CLR'
            SQLCLR_QUANTUM_PUNISHMENT                       = 'SQL CLR'
            SQLTRACE_BUFFER_FLUSH                           = 'Idle'
            SQLTRACE_FILE_BUFFER                            = 'Tracing'
            SQLTRACE_FILE_READ_IO_COMPLETION                = 'Tracing'
            SQLTRACE_FILE_WRITE_IO_COMPLETION               = 'Tracing'
            SQLTRACE_INCREMENTAL_FLUSH_SLEEP                = 'Idle'
            SQLTRACE_PENDING_BUFFER_WRITERS                 = 'Tracing'
            SQLTRACE_SHUTDOWN                               = 'Tracing'
            SQLTRACE_WAIT_ENTRIES                           = 'Idle'
            THREADPOOL                                      = 'Worker Thread'
            TRACE_EVTNOTIF                                  = 'Tracing'
            TRACEWRITE                                      = 'Tracing'
            TRAN_MARKLATCH_DT                               = 'Transaction'
            TRAN_MARKLATCH_EX                               = 'Transaction'
            TRAN_MARKLATCH_KP                               = 'Transaction'
            TRAN_MARKLATCH_NL                               = 'Transaction'
            TRAN_MARKLATCH_SH                               = 'Transaction'
            TRAN_MARKLATCH_UP                               = 'Transaction'
            TRANSACTION_MUTEX                               = 'Transaction'
            WAIT_FOR_RESULTS                                = 'User Wait'
            WAITFOR                                         = 'User Wait'
            WRITE_COMPLETION                                = 'Other Disk IO'
            WRITELOG                                        = 'Tran Log IO'
            XACT_OWN_TRANSACTION                            = 'Transaction'
            XACT_RECLAIM_SESSION                            = 'Transaction'
            XACTLOCKINFO                                    = 'Transaction'
            XACTWORKSPACE_MUTEX                             = 'Transaction'
            XE_DISPATCHER_WAIT                              = 'Idle'
            XE_TIMER_EVENT                                  = 'Idle'
            ABR                                             = 'Other'
            ASSEMBLY_LOAD                                   = 'SQLCLR'
            ASYNC_DISKPOOL_LOCK                             = 'Buffer I/O'
            BACKUP                                          = 'Backup'
            BACKUP_CLIENTLOCK                               = 'Backup'
            BACKUP_OPERATOR                                 = 'Backup'
            BACKUPBUFFER                                    = 'Backup'
            BACKUPTHREAD                                    = 'Backup'
            BAD_PAGE_PROCESS                                = 'Other'
            BUILTIN_HASHKEY_MUTEX                           = 'Other'
            CHECK_PRINT_RECORD                              = 'Other'
            CPU                                             = 'CPU'
            CURSOR                                          = 'Other'
            CURSOR_ASYNC                                    = 'Other'
            DAC_INIT                                        = 'Other'
            DBCC_COLUMN_TRANSLATION_CACHE                   = 'Other'
            DBTABLE                                         = 'Other'
            DEADLOCK_ENUM_MUTEX                             = 'Latch'
            DEADLOCK_TASK_SEARCH                            = 'Other'
            DEBUG                                           = 'Other'
            DISABLE_VERSIONING                              = 'Other'
            DISKIO_SUSPEND                                  = 'Backup'
            DLL_LOADING_MUTEX                               = 'Other'
            DROPTEMP                                        = 'Other'
            DUMP_LOG_COORDINATOR                            = 'Other'
            DUMP_LOG_COORDINATOR_QUEUE                      = 'Other'
            DUMPTRIGGER                                     = 'Other'
            EC                                              = 'Other'
            EE_SPECPROC_MAP_INIT                            = 'Other'
            ENABLE_VERSIONING                               = 'Other'
            ERROR_REPORTING_MANAGER                         = 'Other'
            EXECSYNC                                        = 'Parallelism'
            EXECUTION_PIPE_EVENT_INTERNAL                   = 'Other'
            FAILPOINT                                       = 'Other'
            FS_GARBAGE_COLLECTOR_SHUTDOWN                   = 'SQLCLR'
            FSAGENT                                         = 'Idle'
            FT_RESUME_CRAWL                                 = 'Other'
            GUARDIAN                                        = 'Other'
            HTTP_ENDPOINT_COLLCREATE                        = 'Other'
            HTTP_ENUMERATION                                = 'Other'
            HTTP_START                                      = 'Other'
            IMP_IMPORT_MUTEX                                = 'Other'
            IMPPROV_IOWAIT                                  = 'Other'
            INDEX_USAGE_STATS_MUTEX                         = 'Latch'
            INTERNAL_TESTING                                = 'Other'
            IO_AUDIT_MUTEX                                  = 'Other'
            KSOURCE_WAKEUP                                  = 'Idle'
            KTM_ENLISTMENT                                  = 'Other'
            KTM_RECOVERY_MANAGER                            = 'Other'
            KTM_RECOVERY_RESOLUTION                         = 'Other'
            LOWFAIL_MEMMGR_QUEUE                            = 'Memory'
            MIRROR_SEND_MESSAGE                             = 'Other'
            MISCELLANEOUS                                   = 'Other'
            MSQL_DQ                                         = 'Network I/O'
            MSQL_SYNC_PIPE                                  = 'Other'
            MSQL_XP                                         = 'Other'
            OLEDB                                           = 'Network I/O'
            PARALLEL_BACKUP_QUEUE                           = 'Other'
            PRINT_ROLLBACK_PROGRESS                         = 'Other'
            QNMANAGER_ACQUIRE                               = 'Other'
            QPJOB_KILL                                      = 'Other'
            QPJOB_WAITFOR_ABORT                             = 'Other'
            QRY_MEM_GRANT_INFO_MUTEX                        = 'Other'
            QUERY_ERRHDL_SERVICE_DONE                       = 'Other'
            QUERY_EXECUTION_INDEX_SORT_EVENT_OPEN           = 'Other'
            QUERY_NOTIFICATION_MGR_MUTEX                    = 'Other'
            QUERY_NOTIFICATION_SUBSCRIPTION_MUTEX           = 'Other'
            QUERY_NOTIFICATION_TABLE_MGR_MUTEX              = 'Other'
            QUERY_NOTIFICATION_UNITTEST_MUTEX               = 'Other'
            QUERY_OPTIMIZER_PRINT_MUTEX                     = 'Other'
            QUERY_REMOTE_BRICKS_DONE                        = 'Other'
            RECOVER_CHANGEDB                                = 'Other'
            REQUEST_DISPENSER_PAUSE                         = 'Other'
            RESOURCE_QUEUE                                  = 'Idle'
            RESOURCE_SEMAPHORE_MUTEX                        = 'Compilation'
            RESOURCE_SEMAPHORE_SMALL_QUERY                  = 'Compilation'
            SEC_DROP_TEMP_KEY                               = 'Other'
            SEQUENTIAL_GUID                                 = 'Other'
            SERVER_IDLE_CHECK                               = 'Idle'
            SHUTDOWN                                        = 'Other'
            SNI_CRITICAL_SECTION                            = 'Other'
            SNI_HTTP_ACCEPT                                 = 'Idle'
            SNI_HTTP_WAITFOR_0_DISCON                       = 'Other'
            SNI_LISTENER_ACCESS                             = 'Other'
            SNI_TASK_COMPLETION                             = 'Other'
            SOAP_READ                                       = 'Full Text Search'
            SOAP_WRITE                                      = 'Full Text Search'
            SOS_CALLBACK_REMOVAL                            = 'Other'
            SOS_DISPATCHER_MUTEX                            = 'Other'
            SOS_LOCALALLOCATORLIST                          = 'Other'
            SOS_OBJECT_STORE_DESTROY_MUTEX                  = 'Other'
            SOS_PROCESS_AFFINITY_MUTEX                      = 'Other'
            SOS_RESERVEDMEMBLOCKLIST                        = 'Memory'
            SOS_STACKSTORE_INIT_MUTEX                       = 'Other'
            SOS_SYNC_TASK_ENQUEUE_EVENT                     = 'Other'
            SOS_VIRTUALMEMORY_LOW                           = 'Memory'
            SOSHOST_EVENT                                   = 'Other'
            SOSHOST_INTERNAL                                = 'Other'
            SOSHOST_MUTEX                                   = 'Other'
            SOSHOST_RWLOCK                                  = 'Other'
            SOSHOST_SEMAPHORE                               = 'Other'
            SOSHOST_SLEEP                                   = 'Other'
            SOSHOST_TRACELOCK                               = 'Other'
            SOSHOST_WAITFORDONE                             = 'Other'
            SQLSORT_NORMMUTEX                               = 'Other'
            SQLSORT_SORTMUTEX                               = 'Other'
            SQLTRACE_LOCK                                   = 'Other'
            SRVPROC_SHUTDOWN                                = 'Other'
            TEMPOBJ                                         = 'Other'
            TIMEPRIV_TIMEPERIOD                             = 'Other'
            UTIL_PAGE_ALLOC                                 = 'Memory'
            VIA_ACCEPT                                      = 'Other'
            VIEW_DEFINITION_MUTEX                           = 'Latch'
            WAITFOR_TASKSHUTDOWN                            = 'Idle'
            WAITSTAT_MUTEX                                  = 'Other'
            WCC                                             = 'Other'
            WORKTBL_DROP                                    = 'Other'
            XE_BUFFERMGR_ALLPROCECESSED_EVENT               = 'Other'
            XE_BUFFERMGR_FREEBUF_EVENT                      = 'Other'
            XE_DISPATCHER_JOIN                              = 'Other'
            XE_MODULEMGR_SYNC                               = 'Other'
            XE_OLS_LOCK                                     = 'Other'
            XE_SERVICES_MUTEX                               = 'Other'
            XE_SESSION_CREATE_SYNC                          = 'Other'
            XE_SESSION_SYNC                                 = 'Other'
            XE_STM_CREATE                                   = 'Other'
            XE_TIMER_MUTEX                                  = 'Other'
            XE_TIMER_TASK_DONE                              = 'Other'
        }

        $ignorable = 'BROKER_EVENTHANDLER', 'BROKER_RECEIVE_WAITFOR', 'BROKER_TASK_STOP',
        'BROKER_TO_FLUSH', 'BROKER_TRANSMITTER', 'CHECKPOINT_QUEUE',
        'CHKPT', 'CLR_AUTO_EVENT', 'CLR_MANUAL_EVENT', 'CLR_SEMAPHORE', 'CXCONSUMER',
        'DBMIRROR_DBM_EVENT', 'DBMIRROR_EVENTS_QUEUE', 'DBMIRROR_WORKER_QUEUE',
        'DBMIRRORING_CMD', 'DIRTY_PAGE_POLL', 'DISPATCHER_QUEUE_SEMAPHORE',
        'EXECSYNC', 'FSAGENT', 'FT_IFTS_SCHEDULER_IDLE_WAIT', 'FT_IFTSHC_MUTEX',
        'HADR_CLUSAPI_CALL', 'HADR_FILESTREAM_IOMGR_IOCOMPLETION', 'HADR_LOGCAPTURE_WAIT',
        'HADR_NOTIFICATION_DEQUEUE', 'HADR_TIMER_TASK', 'HADR_WORK_QUEUE',
        'KSOURCE_WAKEUP', 'LAZYWRITER_SLEEP', 'LOGMGR_QUEUE',
        'MEMORY_ALLOCATION_EXT', 'ONDEMAND_TASK_QUEUE',
        'PARALLEL_REDO_DRAIN_WORKER', 'PARALLEL_REDO_LOG_CACHE', 'PARALLEL_REDO_TRAN_LIST', 'PARALLEL_REDO_WORKER_SYNC',
        'PREEMPTIVE_SP_SERVER_DIAGNOSTICS',
        'PARALLEL_REDO_WORKER_WAIT_WORK', 'PREEMPTIVE_HADR_LEASE_MECHANISM',
        'PREEMPTIVE_OS_LIBRARYOPS', 'PREEMPTIVE_OS_COMOPS', 'PREEMPTIVE_OS_CRYPTOPS',
        'PREEMPTIVE_OS_PIPEOPS', 'PREEMPTIVE_OS_AUTHENTICATIONOPS',
        'PREEMPTIVE_OS_GENERICOPS', 'PREEMPTIVE_OS_VERIFYTRUST',
        'PREEMPTIVE_OS_FILEOPS', 'PREEMPTIVE_OS_DEVICEOPS', 'PREEMPTIVE_OS_QUERYREGISTRY',
        'PREEMPTIVE_OS_WRITEFILE', 'PREEMPTIVE_XE_CALLBACKEXECUTE', 'PREEMPTIVE_XE_DISPATCHER',
        'PREEMPTIVE_XE_GETTARGETSTATE', 'PREEMPTIVE_XE_SESSIONCOMMIT',
        'PREEMPTIVE_XE_TARGETINIT', 'PREEMPTIVE_XE_TARGETFINALIZE',
        'PWAIT_ALL_COMPONENTS_INITIALIZED', 'PWAIT_DIRECTLOGCONSUMER_GETNEXT',
        'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP', 'QDS_ASYNC_QUEUE',
        'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP', 'REDO_THREAD_PENDING_WORK',
        'QDS_SHUTDOWN_QUEUE', 'REQUEST_FOR_DEADLOCK_SEARCH',
        'RESOURCE_QUEUE', 'SERVER_IDLE_CHECK', 'SLEEP_BPOOL_FLUSH', 'SLEEP_DBSTARTUP',
        'SLEEP_DCOMSTARTUP', 'SLEEP_MASTERDBREADY', 'SLEEP_MASTERMDREADY',
        'SLEEP_MASTERUPGRADED', 'SLEEP_MSDBSTARTUP', 'SLEEP_SYSTEMTASK', 'SLEEP_TASK',
        'SLEEP_TEMPDBSTARTUP', 'SNI_HTTP_ACCEPT', 'SP_SERVER_DIAGNOSTICS_SLEEP',
        'SQLTRACE_BUFFER_FLUSH', 'SQLTRACE_INCREMENTAL_FLUSH_SLEEP', 'SQLTRACE_WAIT_ENTRIES',
        'WAIT_FOR_RESULTS', 'WAITFOR', 'WAITFOR_TASKSHUTDOWN', 'WAIT_XTP_HOST_WAIT',
        'WAIT_XTP_OFFLINE_CKPT_NEW_LOG', 'WAIT_XTP_CKPT_CLOSE', 'WAIT_XTP_RECOVERY',
        'XE_BUFFERMGR_ALLPROCESSED_EVENT', 'XE_DISPATCHER_JOIN',
        'XE_DISPATCHER_WAIT', 'XE_LIVE_TARGET_TVF', 'XE_TIMER_EVENT'

        if ($IncludeIgnorable) {
            $sql = "WITH [Waits] AS
                (SELECT
                    [wait_type],
                    [wait_time_ms] / 1000.0 AS [WaitS],
                    ([wait_time_ms] - [signal_wait_time_ms]) / 1000.0 AS [ResourceS],
                    [signal_wait_time_ms] / 1000.0 AS [SignalS],
                    [waiting_tasks_count] AS [WaitCount],
                    100.0 * [wait_time_ms] / SUM ([wait_time_ms]) OVER() AS [Percentage],
                    ROW_NUMBER() OVER(ORDER BY [wait_time_ms] DESC) AS [RowNum]
                FROM sys.dm_os_wait_stats
                WHERE [waiting_tasks_count] > 0
                )
                SELECT
                    MAX ([W1].[wait_type]) AS [WaitType],
                    CAST (MAX ([W1].[WaitS]) AS DECIMAL (16,2)) AS [WaitSeconds],
                    CAST (MAX ([W1].[ResourceS]) AS DECIMAL (16,2)) AS [ResourceSeconds],
                    CAST (MAX ([W1].[SignalS]) AS DECIMAL (16,2)) AS [SignalSeconds],
                    MAX ([W1].[WaitCount]) AS [WaitCount],
                    CAST (MAX ([W1].[Percentage]) AS DECIMAL (5,2)) AS [Percentage],
                    CAST ((MAX ([W1].[WaitS]) / MAX ([W1].[WaitCount])) AS DECIMAL (16,4)) AS [AvgWaitSeconds],
                    CAST ((MAX ([W1].[ResourceS]) / MAX ([W1].[WaitCount])) AS DECIMAL (16,4)) AS [AvgResSeconds],
                    CAST ((MAX ([W1].[SignalS]) / MAX ([W1].[WaitCount])) AS DECIMAL (16,4)) AS [AvgSigSeconds],
                    CAST ('https://www.sqlskills.com/help/waits/' + MAX ([W1].[wait_type]) as XML) AS [URL]
                FROM [Waits] AS [W1]
                INNER JOIN [Waits] AS [W2]
                    ON [W2].[RowNum] <= [W1].[RowNum]
                GROUP BY [W1].[RowNum] HAVING SUM ([W2].[Percentage]) - MAX([W1].[Percentage]) < $Threshold"
            }
            else {
            $IgnorableList = "'$($ignorable -join "','")'"
            $sql = "WITH [Waits] AS
                (SELECT
                    [wait_type],
                    [wait_time_ms] / 1000.0 AS [WaitS],
                    ([wait_time_ms] - [signal_wait_time_ms]) / 1000.0 AS [ResourceS],
                    [signal_wait_time_ms] / 1000.0 AS [SignalS],
                    [waiting_tasks_count] AS [WaitCount],
                    100.0 * [wait_time_ms] / SUM ([wait_time_ms]) OVER() AS [Percentage],
                    ROW_NUMBER() OVER(ORDER BY [wait_time_ms] DESC) AS [RowNum]
                FROM sys.dm_os_wait_stats
                WHERE [waiting_tasks_count] > 0
                AND Cast([wait_type] as VARCHAR(60)) NOT IN ($IgnorableList)
                )
                SELECT
                    MAX ([W1].[wait_type]) AS [WaitType],
                    CAST (MAX ([W1].[WaitS]) AS DECIMAL (16,2)) AS [WaitSeconds],
                    CAST (MAX ([W1].[ResourceS]) AS DECIMAL (16,2)) AS [ResourceSeconds],
                    CAST (MAX ([W1].[SignalS]) AS DECIMAL (16,2)) AS [SignalSeconds],
                    MAX ([W1].[WaitCount]) AS [WaitCount],
                    CAST (MAX ([W1].[Percentage]) AS DECIMAL (5,2)) AS [Percentage],
                    CAST ((MAX ([W1].[WaitS]) / MAX ([W1].[WaitCount])) AS DECIMAL (16,4)) AS [AvgWaitSeconds],
                    CAST ((MAX ([W1].[ResourceS]) / MAX ([W1].[WaitCount])) AS DECIMAL (16,4)) AS [AvgResSeconds],
                    CAST ((MAX ([W1].[SignalS]) / MAX ([W1].[WaitCount])) AS DECIMAL (16,4)) AS [AvgSigSeconds],
                    CAST ('https://www.sqlskills.com/help/waits/' + MAX ([W1].[wait_type]) as XML) AS [URL]
                FROM [Waits] AS [W1]
                INNER JOIN [Waits] AS [W2]
                    ON [W2].[RowNum] <= [W1].[RowNum]
                GROUP BY [W1].[RowNum] HAVING SUM ([W2].[Percentage]) - MAX([W1].[Percentage]) < $Threshold"

            }
        Write-Message -Level Debug -Message $sql
    }
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            Write-Message -Level Verbose -Message "Connected to $instance"
            if ($IncludeIgnorable) {
                $excludeColumns = 'Notes'
            }
            else {
                $excludeColumns = 'Notes', 'Ignorable'
            }

            foreach ($row in $server.Query($sql)) {
                $waitType = $row.WaitType
                if (-not $IncludeIgnorable) {
                    if ($ignorable -contains $waitType) { continue }
                }

                [PSCustomObject]@{
                    ComputerName           = $server.ComputerName
                    InstanceName           = $server.ServiceName
                    SqlInstance            = $server.DomainInstanceName
                    WaitType               = $waitType
                    Category               = ($category).$waitType
                    WaitSeconds            = $row.WaitSeconds
                    ResourceSeconds        = $row.ResourceSeconds
                    SignalSeconds          = $row.SignalSeconds
                    WaitCount              = $row.WaitCount
                    Percentage             = $row.Percentage
                    AverageWaitSeconds     = $row.AvgWaitSeconds
                    AverageResourceSeconds = $row.AvgResSeconds
                    AverageSignalSeconds   = $row.AvgSigSeconds
                    Ignorable              = ($ignorable -contains $waitType)
                    URL                    = $row.URL
                    Notes                  = ($details).$waitType
                } | Select-DefaultView -ExcludeProperty $excludeColumns
            }
        }
    }
}
tools\dbatools\functions\Get-DbaWindowsLog.ps1
function Get-DbaWindowsLog {
    <#
    .SYNOPSIS
        Gets Windows Application events associated with an instance

    .DESCRIPTION
        Gets Windows Application events associated with an instance

    .PARAMETER SqlInstance
        The instance(s) to retrieve the event logs from

    .PARAMETER Start
        Default: 1970
        Retrieve all events starting from this timestamp.

    .PARAMETER End
        Default: Now
        Retrieve all events that happened before this timestamp

    .PARAMETER Credential
        Credential to be used to connect to the Server. Note this is a Windows credential, as this command requires we communicate with the computer and not with the SQL instance.

    .PARAMETER MaxThreads
        Default: Unlimited
        The maximum number of parallel threads used on the local computer.
        Given that those will mostly be waiting for the remote system, there is usually no need to limit this.

    .PARAMETER MaxRemoteThreads
        Default: 2
        The maximum number of parallel threads that are executed on the target sql server.
        These processes will cause considerable CPU load, so a low limit is advisable in most scenarios.
        Any value lower than 1 disables the limit

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Tags: Logging
        Author: Drew Furgiuele
        Editor: Friedrich "Fred" Weinmann
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Get-DbaWindowsLog

    .EXAMPLE
        $ErrorLogs = Get-DbaWindowsLog -SqlInstance sql01\sharepoint
        $ErrorLogs | Where-Object ErrorNumber -eq 18456

        Returns all lines in the errorlogs that have event number 18456 in them

#>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]
        $SqlInstance = $env:COMPUTERNAME,

        [DateTime]
        $Start = "1/1/1970 00:00:00",

        [DateTime]
        $End = (Get-Date),


        [System.Management.Automation.PSCredential]
        $Credential,

        [int]
        $MaxThreads = 0,

        [int]
        $MaxRemoteThreads = 2,

        [switch]
        [Alias('Silent')]$EnableException
    )

    begin {
        Write-Message -Level Debug -Message "Bound parameters: $($PSBoundParameters.Keys -join ", ")"

        #region Helper Functions
        function Start-Runspace {
            $Powershell = [PowerShell]::Create().AddScript($scriptBlock_ParallelRemoting).AddParameter("SqlInstance", $instance).AddParameter("Start", $Start).AddParameter("End", $End).AddParameter("Credential", $Credential).AddParameter("MaxRemoteThreads", $MaxRemoteThreads).AddParameter("ScriptBlock", $scriptBlock_RemoteExecution)
            $Powershell.RunspacePool = $RunspacePool
            Write-Message -Level Verbose -Message "Launching remote runspace against <c='green'>$instance</c>" -Target $instance
            $null = $RunspaceCollection.Add((New-Object -TypeName PSObject -Property @{ Runspace = $PowerShell.BeginInvoke(); PowerShell = $PowerShell; Instance = $instance.FullSmoName }))
        }

        function Receive-Runspace {
            [Parameter()]
            Param (
                [switch]
                $Wait
            )

            do {
                foreach ($Run in $RunspaceCollection.ToArray()) {
                    if ($Run.Runspace.IsCompleted) {
                        Write-Message -Level Verbose -Message "Receiving results from <c='green'>$($Run.Instance)</c>" -Target $Run.Instance
                        $Run.PowerShell.EndInvoke($Run.Runspace)
                        $Run.PowerShell.Dispose()
                        $RunspaceCollection.Remove($Run)
                    }
                }

                if ($Wait -and ($RunspaceCollection.Count -gt 0)) { Start-Sleep -Milliseconds 250 }
            }
            while ($Wait -and ($RunspaceCollection.Count -gt 0))
        }
        #endregion Helper Functions

        #region Scriptblocks
        $scriptBlock_RemoteExecution = {
            Param (
                [System.DateTime]
                $Start,

                [System.DateTime]
                $End,

                [string]
                $InstanceName,

                [int]
                $Throttle
            )

            #region Helper function
            function Convert-ErrorRecord {
                Param (
                    $Line
                )

                if (Get-Variable -Name codesAndStuff -Scope 1) {
                    $line2 = (Get-Variable -Name codesAndStuff -Scope 1).Value
                    Remove-Variable -Name codesAndStuff -Scope 1

                    $groups = [regex]::Matches($line2, '^([\d- :]+.\d\d) (\w+)[ ]+Error: (\d+), Severity: (\d+), State: (\d+)').Groups
                    $groups2 = [regex]::Matches($line, '^[\d- :]+.\d\d \w+[ ]+(.*)$').Groups

                    New-Object PSObject -Property @{
                        Timestamp   = [DateTime]::ParseExact($groups[1].Value, "yyyy-MM-dd HH:mm:ss.ff", $null)
                        Spid        = $groups[2].Value
                        Message     = $groups2[1].Value
                        ErrorNumber = [int]($groups[3].Value)
                        Severity    = [int]($groups[4].Value)
                        State       = [int]($groups[5].Value)
                    }
                }

                if ($Line -match '^\d{4}-\d\d-\d\d \d\d:\d\d:\d\d\.\d\d[\w ]+((\w+): (\d+)[,\.]\s?){3}') {
                    Set-Variable -Name codesAndStuff -Value $Line -Scope 1
                }
            }
            #endregion Helper function

            #region Script that processes an individual file
            $scriptBlock = {
                Param (
                    [System.IO.FileInfo]
                    $File
                )

                try {
                    $stream = New-Object System.IO.FileStream($File.FullName, "Open", "Read", "ReadWrite, Delete")
                    $reader = New-Object System.IO.StreamReader($stream)

                    while (-not $reader.EndOfStream) {
                        Convert-ErrorRecord -Line $reader.ReadLine()
                    }
                }
                catch { }
            }
            #endregion Script that processes an individual file

            #region Gather list of files to process
            $eventSource = "MSSQLSERVER"
            if ($InstanceName -notmatch "^DEFAULT$|^MSSQLSERVER$") {
                $eventSource = 'MSSQL$' + $InstanceName
            }

            $event = Get-WinEvent -FilterHashtable @{
                LogName      = "Application"
                ID           = 17111
                ProviderName = $eventSource
            } -MaxEvents 1 -ErrorAction SilentlyContinue

            if (-not $event) { return }

            $path = $event.Properties[0].Value
            $errorLogPath = Split-Path -Path $path
            $errorLogFileName = Split-Path -Path $path -Leaf
            $errorLogFiles = Get-ChildItem -Path $errorLogPath | Where-Object { ($_.Name -like "$errorLogFileName*") -and ($_.LastWriteTime -gt $Start) -and ($_.CreationTime -lt $End) }
            #endregion Gather list of files to process

            #region Prepare Runspaces
            [Collections.Arraylist]$RunspaceCollection = @()

            $InitialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
            $Command = Get-Item function:Convert-ErrorRecord
            $InitialSessionState.Commands.Add((New-Object System.Management.Automation.Runspaces.SessionStateFunctionEntry($command.Name, $command.Definition)))

            $RunspacePool = [RunspaceFactory]::CreateRunspacePool($InitialSessionState)
            $null = $RunspacePool.SetMinRunspaces(1)
            if ($Throttle -gt 0) { $null = $RunspacePool.SetMaxRunspaces($Throttle) }
            $RunspacePool.Open()
            #endregion Prepare Runspaces

            #region Process Error files
            $countDone = 0
            $countStarted = 0
            $countTotal = ($errorLogFiles | Measure-Object).Count

            while ($countDone -lt $countTotal) {
                while (($RunspacePool.GetAvailableRunspaces() -gt 0) -and ($countStarted -lt $countTotal)) {
                    $Powershell = [PowerShell]::Create().AddScript($scriptBlock).AddParameter("File", $errorLogFiles[$countStarted])
                    $Powershell.RunspacePool = $RunspacePool
                    $null = $RunspaceCollection.Add((New-Object -TypeName PSObject -Property @{ Runspace = $PowerShell.BeginInvoke(); PowerShell = $PowerShell }))
                    $countStarted++
                }

                foreach ($Run in $RunspaceCollection.ToArray()) {
                    if ($Run.Runspace.IsCompleted) {
                        $Run.PowerShell.EndInvoke($Run.Runspace) | Where-Object { ($_.Timestamp -gt $Start) -and ($_.Timestamp -lt $End) }
                        $Run.PowerShell.Dispose()
                        $RunspaceCollection.Remove($Run)
                        $countDone++
                    }
                }

                Start-Sleep -Milliseconds 250
            }
            $RunspacePool.Close()
            $RunspacePool.Dispose()
            #endregion Process Error files
        }

        $scriptBlock_ParallelRemoting = {
            Param (
                [DbaInstanceParameter]
                $SqlInstance,

                [DateTime]
                $Start,

                [DateTime]
                $End,

                [object]
                $Credential,

                [int]
                $MaxRemoteThreads,

                [System.Management.Automation.ScriptBlock]
                $ScriptBlock
            )

            $params = @{
                ArgumentList = $Start, $End, $SqlInstance.InstanceName, $MaxRemoteThreads
                ScriptBlock  = $ScriptBlock
            }
            if (-not $SqlInstance.IsLocalhost) { $params["ComputerName"] = $SqlInstance.ComputerName }
            if ($Credential) { $params["Credential"] = $Credential }

            Invoke-Command @params | Select-Object @{ n = "InstanceName"; e = { $SqlInstance.FullSmoName } }, Timestamp, Spid, Severity, ErrorNumber, State, Message
        }
        #endregion Scriptblocks

        #region Setup Runspace
        [Collections.Arraylist]$RunspaceCollection = @()
        $InitialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
        $RunspacePool = [RunspaceFactory]::CreateRunspacePool($InitialSessionState)
        $RunspacePool.SetMinRunspaces(1) | Out-Null
        if ($MaxThreads -gt 0) { $null = $RunspacePool.SetMaxRunspaces($MaxThreads) }
        $RunspacePool.Open()

        $countStarted = 0
        $countReceived = 0
        #endregion Setup Runspace
    }

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level VeryVerbose -Message "Processing <c='green'>$instance</c>" -Target $instance
            Start-Runspace
            Receive-Runspace
        }
    }

    end {
        Receive-Runspace -Wait
        $RunspacePool.Close()
        $RunspacePool.Dispose()
    }
}
tools\dbatools\functions\Get-DbaXEObject.ps1
function Get-DbaXEObject {
    <#
        .SYNOPSIS
            Gets a list of trace(s) from specified SQL Server instance(s).

        .DESCRIPTION
            This function returns a list of Traces on the specified SQL Server instance(s) and identifies the default Trace File

        .PARAMETER SqlInstance
            Target SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Type
            Used to specify the type. Valid types include:

                Action
                Event
                Map
                Message
                PredicateComparator
                PredicateSource
                Target
                Type

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message. This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting. Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            Get-DbaXEObject -SqlInstance sql2016

            Lists all the XE Objects on the sql2016 SQL Server.

        .EXAMPLE
            Get-DbaXEObject -SqlInstance sql2017 -Type Action, Event

            Lists all the XE Objects of type Action and Event on the sql2017 SQL Server.

    #>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [ValidateSet("Type", "Event", "Target", "Action", "Map", "Message", "PredicateComparator", "PredicateSource")]
        [string[]]$Type,
        [switch]$EnableException
    )
    begin {
        if ($Type) {
            $join = $Type -join "','"
            $where = "AND o.object_type in ('$join')"
            $where.Replace("PredicateComparator", "pred_compare")
            $where.Replace("PredicateSource", "pred_source")
        }
        $sql = "SELECT  SERVERPROPERTY('MachineName') AS ComputerName,
                ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
                SERVERPROPERTY('ServerName') AS SqlInstance,
                p.name AS PackageName,
                ObjectType =
                      CASE o.object_type
                         WHEN 'type' THEN 'Type'
                         WHEN 'event' THEN 'Event'
                         WHEN 'target' THEN 'Target'
                         WHEN 'pred_compare' THEN 'PredicateComparator'
                         WHEN 'pred_source' THEN 'PredicateSource'
                         WHEN 'action' THEN 'Action'
                         WHEN 'map' THEN 'Map'
                         WHEN 'message' THEN 'Message'
                         ELSE o.object_type
                      END,
                o.object_type as ObjectTypeRaw,
                o.name AS TargetName,
                o.description as Description
                FROM sys.dm_xe_packages AS p
                JOIN sys.dm_xe_objects AS o ON p.guid = o.package_guid
                WHERE (p.capabilities IS NULL OR p.capabilities & 1 = 0)
                $where
                AND (o.capabilities IS NULL OR o.capabilities & 1 = 0)
                ORDER BY o.object_type
                "
    }
    process {
        foreach ($instance in $SqlInstance) {

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                return
            }

            try {
                $server.Query($sql) | Select-DefaultView -ExcludeProperty ComputerName, InstanceName, ObjectTypeRaw
            }
            catch {
                Stop-Function -Message "Issue collecting trace data on $server." -Target $server -ErrorRecord $_
            }
        }
    }
}
tools\dbatools\functions\Get-DbaXESession.ps1
function Get-DbaXESession {
    <#
        .SYNOPSIS
            Gets a list of Extended Events Sessions from the specified SQL Server instance(s).

        .DESCRIPTION
            Retrieves a list of Extended Events Sessions present on the specified SQL Server instance(s).

        .PARAMETER SqlInstance
            Target SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Session
            Only return specific sessions. Options for this parameter are auto-populated from the server.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Author: Klaas Vandenberghe ( @PowerDBAKlaas )

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaXESession

        .EXAMPLE
            Get-DbaXESession -SqlInstance ServerA\sql987

            Returns a custom object with ComputerName, SQLInstance, Session, StartTime, Status and other properties.

        .EXAMPLE
            Get-DbaXESession -SqlInstance ServerA\sql987 | Format-Table ComputerName, SqlInstance, Session, Status -AutoSize

            Returns a formatted table displaying ComputerName, SqlInstance, Session, and Status.

        .EXAMPLE
            'ServerA\sql987','ServerB' | Get-DbaXESession

            Returns a custom object with ComputerName, SqlInstance, Session, StartTime, Status and other properties, from multiple SQL instances.

    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Sessions")]
        [object[]]$Session,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-DbaXEsSession
    }

    process {

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 11 -AzureUnsupported
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $SqlConn = $server.ConnectionContext.SqlConnectionObject
            $SqlStoreConnection = New-Object Microsoft.SqlServer.Management.Sdk.Sfc.SqlStoreConnection $SqlConn
            $XEStore = New-Object  Microsoft.SqlServer.Management.XEvent.XEStore $SqlStoreConnection
            Write-Message -Level Verbose -Message "Getting XEvents Sessions on $instance."

            $xesessions = $XEStore.sessions

            if ($Session) {
                $xesessions = $xesessions | Where-Object { $_.Name -in $Session }
            }

            foreach ($x in $xesessions) {
                $status = switch ($x.IsRunning) { $true { "Running" } $false { "Stopped" } }
                $files = $x.Targets.TargetFields | Where-Object Name -eq Filename | Select-Object -ExpandProperty Value

                $filecollection = $remotefile = @()

                if ($files) {
                    foreach ($file in $files) {
                        if ($file -notmatch ':\\' -and $file -notmatch '\\\\') {
                            $directory = $server.ErrorLogPath.TrimEnd("\")
                            $file = "$directory\$file"
                        }
                        $filecollection += $file
                        $remotefile += Join-AdminUnc -servername $server.ComputerName -filepath $file
                    }
                }

                Add-Member -Force -InputObject $x -MemberType NoteProperty -Name ComputerName -Value $server.ComputerName
                Add-Member -Force -InputObject $x -MemberType NoteProperty -Name InstanceName -Value $server.ServiceName
                Add-Member -Force -InputObject $x -MemberType NoteProperty -Name SqlInstance -Value $server.DomainInstanceName
                Add-Member -Force -InputObject $x -MemberType NoteProperty -Name Status -Value $status
                Add-Member -Force -InputObject $x -MemberType NoteProperty -Name Session -Value $x.Name
                Add-Member -Force -InputObject $x -MemberType NoteProperty -Name TargetFile -Value $filecollection
                Add-Member -Force -InputObject $x -MemberType NoteProperty -Name RemoteTargetFile -Value $remotefile
                Add-Member -Force -InputObject $x -MemberType NoteProperty -Name Parent -Value $server
                Add-Member -Force -InputObject $x -MemberType NoteProperty -Name Store -Value $XEStore
                Select-DefaultView -InputObject $x -Property ComputerName, InstanceName, SqlInstance, Name, Status, StartTime, AutoStart, State, Targets, TargetFile, Events, MaxMemory, MaxEventSize
            }
        }
    }
}
tools\dbatools\functions\Get-DbaXESessionTarget.ps1
function Get-DbaXESessionTarget {
    <#
        .SYNOPSIS
            Get a list of Extended Events Session Targets from the specified SQL Server instance(s).

        .DESCRIPTION
            Retrieves a list of Extended Events Session Targets from the specified SQL Server instance(s).

        .PARAMETER SqlInstance
            Target SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Session
            Only return a specific session. Options for this parameter are auto-populated from the server.

        .PARAMETER Target
            Only return a specific target.

        .PARAMETER InputObject
            Specifies an XE session returned by Get-DbaXESession to search.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaXESessionTarget

        .EXAMPLE
            Get-DbaXESessionTarget -SqlInstance ServerA\sql987 -Session system_health

            Shows targets for the system_health session on ServerA\sql987.

        .EXAMPLE
            Get-DbaXESession -SqlInstance sql2016 -Session system_health | Get-DbaXESessionTarget

            Returns the targets for the system_health session on sql2016.

        .EXAMPLE
            Get-DbaXESession -SqlInstance sql2016 -Session system_health | Get-DbaXESessionTarget -Target package0.event_file

            Return only the package0.event_file target for the system_health session on sql2016.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    param (
        [parameter(ValueFromPipeline, ParameterSetName = "instance", Mandatory)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$Session,
        [string[]]$Target,
        [parameter(ValueFromPipeline, ParameterSetName = "piped", Mandatory)]
        [Microsoft.SqlServer.Management.XEvent.Session[]]$InputObject,
        [switch][Alias('Silent')]
        $EnableException
    )

    begin {
        function Get-Target {
            [CmdletBinding()]
            param (
                $Sessions,
                $Session,
                $Server,
                $Target
            )

            foreach ($xsession in $Sessions) {

                if ($null -eq $server) {
                    $server = $xsession.Parent
                }

                if ($Session -and $xsession.Name -notin $Session) { continue }
                $status = switch ($xsession.IsRunning) { $true { "Running" } $false { "Stopped" } }
                $sessionname = $xsession.Name

                foreach ($xtarget in $xsession.Targets) {
                    if ($Target -and $xtarget.Name -notin $Target) { continue }

                    $files = $xtarget.TargetFields | Where-Object Name -eq Filename | Select-Object -ExpandProperty Value

                    $filecollection = $remotefile = @()

                    if ($files) {
                        foreach ($file in $files) {
                            if ($file -notmatch ':\\' -and $file -notmatch '\\\\') {
                                $directory = $server.ErrorLogPath.TrimEnd("\")
                                $file = "$directory\$file"
                            }
                            $filecollection += $file
                            $remotefile += Join-AdminUnc -servername $server.ComputerName -filepath $file
                        }
                    }

                    Add-Member -Force -InputObject $xtarget -MemberType NoteProperty -Name ComputerName -Value $server.ComputerName
                    Add-Member -Force -InputObject $xtarget -MemberType NoteProperty -Name InstanceName -Value $server.ServiceName
                    Add-Member -Force -InputObject $xtarget -MemberType NoteProperty -Name SqlInstance -Value $server.DomainInstanceName
                    Add-Member -Force -InputObject $xtarget -MemberType NoteProperty -Name Session -Value $sessionname
                    Add-Member -Force -InputObject $xtarget -MemberType NoteProperty -Name SessionStatus -Value $status
                    Add-Member -Force -InputObject $xtarget -MemberType NoteProperty -Name TargetFile -Value $filecollection
                    Add-Member -Force -InputObject $xtarget -MemberType NoteProperty -Name RemoteTargetFile -Value $remotefile

                    Select-DefaultView -InputObject $xtarget -Property ComputerName, InstanceName, SqlInstance, Session, SessionStatus, Name, ID, 'TargetFields as Field', PackageName, 'TargetFile as File', Description, ScriptName
                }
            }
        }
    }

    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $SqlInstance) {
            $InputObject += Get-DbaXESession -SqlInstance $instance -SqlCredential $SqlCredential -Session $Session
        }
        Get-Target -Sessions $InputObject -Session $Session -Target $Target
    }
}
tools\dbatools\functions\Get-DbaXESessionTargetFile.ps1
function Get-DbaXESessionTargetFile {
    <#
        .SYNOPSIS
            Get a file system object from the Extended Events Session Target Files.

        .DESCRIPTION
            Get a file system object from the Extended Events Session Target Files.

            Note: this performs a Get-ChildItem on remote servers if the specified target SQL Server is remote.

        .PARAMETER SqlInstance
            The target SQL Server

        .PARAMETER SqlCredential
            Login to SQL instnace with alternative credentials

        .PARAMETER Session
            Only return files from a specific session. Options for this parameter are auto-populated from the server.

        .PARAMETER Target
            Only return files from a specific target.

        .PARAMETER InputObject
            Allows results from piping in Get-DbaXESessionTarget.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaXESessionTargetFile

        .EXAMPLE
            Get-DbaXESessionTargetFile -SqlInstance sql2017 -Session 'Long Running Queries'

            Shows Target Files for the 'Long Running Queries' session on sql2017.

        .EXAMPLE
            Get-DbaXESession -SqlInstance sql2016 -Session 'Long Running Queries' | Get-DbaXESessionTarget | Get-DbaXESessionTargetFile

            Returns the Target Files for the system_health session on sql2016.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    param (
        [parameter(ValueFromPipeline, ParameterSetName = "instance", Mandatory)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$Session,
        [string[]]$Target,
        [parameter(ValueFromPipeline, ParameterSetName = "piped", Mandatory)]
        [Microsoft.SqlServer.Management.XEvent.Target[]]$InputObject,
        [switch]$EnableException
    )

    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $SqlInstance) {
            $InputObject += Get-DbaXESessionTarget -SqlInstance $instance -SqlCredential $SqlCredential -Session $Session -Target $Target | Where-Object File -ne $null
        }

        foreach ($object in $InputObject) {
            $computer = [dbainstance]$object.ComputerName
            try {
                if ($computer.IsLocal) {
                    $file = $object.TargetFile
                    Write-Message -Level Verbose -Message "Getting $file"
                    Get-ChildItem "$file*" -ErrorAction Stop
                }
                else {
                    $file = $object.RemoteTargetFile
                    Write-Message -Level Verbose -Message "Getting $file"
                    Get-ChildItem -Recurse "$file*" -ErrorAction Stop
                }
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_
            }
        }
    }
}
tools\dbatools\functions\Get-DbaXESessionTemplate.ps1
function Get-DbaXESessionTemplate {
    <#
        .SYNOPSIS
            Parses Extended Event XML templates. Defaults to parsing templates in the dbatools template repository (\bin\xetemplates\).

        .DESCRIPTION
            Parses Extended Event XML templates. Defaults to parsing templates in the dbatools template repository (\bin\xetemplates\).

            The default repository contains templates from:
                    Microsoft's Templates that come with SSMS
                    Jes Borland's "Everyday Extended Events" presentation and GitHub repository (https://github.com/grrlgeek/extended-events)
                    Christian Gräfe's XE Repo: https://github.com/chrgraefe/sqlscripts/blob/master/XE-Events/
                    Erin Stellato's Blog: https://www.sqlskills.com/blogs/erin/

            Some profile templates converted using:
                    sp_SQLskills_ConvertTraceToExtendedEvents.sql
                    Jonathan M. Kehayias, SQLskills.com
                    http://sqlskills.com/blogs/jonathan

        .PARAMETER Path
            The path to the template directory. Defaults to the dbatools template repository (\bin\xetemplates\).

        .PARAMETER Pattern
            Specify a pattern for filtering. Alternatively, you can use Out-GridView -Passthru to select objects and pipe them to Import-DbaXESessionTemplate

        .PARAMETER Template
            Specifies one or more of the templates provided by dbatools. Press tab to cycle through the list of options.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaXESessionTemplate

        .EXAMPLE
            Get-DbaXESessionTemplate

            Returns information about all the templates in the local dbatools repository.

        .EXAMPLE
            Get-DbaXESessionTemplate | Out-GridView -PassThru | Import-DbaXESessionTemplate -SqlInstance sql2017 | Start-DbaXESession

            Allows you to select a Session template, then import it to the specified instance and start the session.

        .EXAMPLE
            Get-DbaXESessionTemplate -Path "$home\Documents\SQL Server Management Studio\Templates\XEventTemplates"

            Returns information about all the templates in your local XEventTemplates repository.

        .EXAMPLE
            Get-DbaXESessionTemplate -Pattern duration

            Returns information about all the templates that match the word "duration" in the title, category or body.

        .EXAMPLE
            Get-DbaXESessionTemplate | Select-Object *

            Returns more information about the template, including the full path/filename.
        #>

    [CmdletBinding()]
    param (
        [string[]]$Path = "$script:PSModuleRoot\bin\xetemplates",
        [string]$Pattern,
        [string[]]$Template,
        [switch]$EnableException
    )
    begin {
        $metadata = Import-Clixml "$script:PSModuleRoot\bin\xetemplates-metadata.xml"
        # In case people really want a "like" search, which is slower
        $Pattern = $Pattern.Replace("*", ".*").Replace("..*", ".*")
    }
    process {
        foreach ($directory in $Path) {
            $files = Get-ChildItem "$directory\*.xml"

            if ($Template) {
                $files = $files | Where-Object BaseName -in $Template
            }

            foreach ($file in $files) {
                try {
                    $xml = [xml](Get-Content $file)
                }
                catch {
                    Stop-Function -Message "Failure" -ErrorRecord $_ -Target $file -Continue
                }

                foreach ($session in $xml.event_sessions) {
                    $meta = $metadata | Where-Object Name -eq $session.event_session.name
                    if ($Pattern) {
                        if (
                            # There's probably a better way to do this
                            ($session.event_session.name -match $Pattern) -or
                            ($session.event_session.TemplateCategory.'#text' -match $Pattern) -or
                            ($session.event_session.TemplateSource -match $Pattern) -or
                            ($session.event_session.TemplateDescription.'#text' -match $Pattern) -or
                            ($session.event_session.TemplateName.'#text' -match $Pattern) -or
                            ($meta.Source -match $Pattern)
                        ) {
                            [pscustomobject]@{
                                Name          = $session.event_session.name
                                Category      = $session.event_session.TemplateCategory.'#text'
                                Source        = $meta.Source
                                Compatibility = ("$($meta.Compatibility)").ToString().Replace(",", "")
                                Description   = $session.event_session.TemplateDescription.'#text'
                                TemplateName  = $session.event_session.TemplateName.'#text'
                                Path          = $file
                                File          = $file.Name
                            } | Select-DefaultView -ExcludeProperty File, TemplateName, Path
                        }
                    }
                    else {
                        [pscustomobject]@{
                            Name          = $session.event_session.name
                            Category      = $session.event_session.TemplateCategory.'#text'
                            Source        = $meta.Source
                            Compatibility = $meta.Compatibility.ToString().Replace(",", "")
                            Description   = $session.event_session.TemplateDescription.'#text'
                            TemplateName  = $session.event_session.TemplateName.'#text'
                            Path          = $file
                            File          = $file.Name
                        } | Select-DefaultView -ExcludeProperty File, TemplateName, Path
                    }
                }
            }
        }
    }
}
tools\dbatools\functions\Get-DbaXESmartTarget.ps1
function Get-DbaXESmartTarget {
    <#
        .SYNOPSIS
            Gets an XESmartTarget PowerShell Job created by Start-DbaXESmartTarget.

        .DESCRIPTION
            Gets an XESmartTarget PowerShell Job created by Start-DbaXESmartTarget.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
            SmartTarget: by Gianluca Sartori (@spaghettidba)

        .LINK
            https://dbatools.io/Get-DbaXESmartTarget
            https://github.com/spaghettidba/XESmartTarget/wiki

        .EXAMPLE
            Get-DbaXESmartTarget

            Gets an XESmartTarget PowerShell Job created by Start-DbaXESmartTarget.

    #>
    [CmdletBinding()]
    param (
        [switch]$EnableException
    )
    process {
        try {
            Get-Job | Where-Object Name -Match SmartTarget | Select-Object -Property ID, Name, State
        }
        catch {
            Stop-Function -Message "Failure" -ErrorRecord $_
        }
    }
}
tools\dbatools\functions\Get-DbaXEStore.ps1
function Get-DbaXEStore {
    <#
        .SYNOPSIS
            Get a Extended Events store

        .DESCRIPTION
            Get a Extended Events store

       .PARAMETER SqlInstance
            Target SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Get-DbaXEStore

        .EXAMPLE
            Get-DbaXEStore -SqlInstance ServerA\sql987

            Returns an XEvent Store.

    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 11
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $SqlConn = $server.ConnectionContext.SqlConnectionObject
            $SqlStoreConnection = New-Object Microsoft.SqlServer.Management.Sdk.Sfc.SqlStoreConnection $SqlConn
            $store = New-Object  Microsoft.SqlServer.Management.XEvent.XEStore $SqlStoreConnection

            Add-Member -Force -InputObject $store -MemberType NoteProperty -Name ComputerName -Value $server.ComputerName
            Add-Member -Force -InputObject $store -MemberType NoteProperty -Name InstanceName -Value $server.ServiceName
            Add-Member -Force -InputObject $store -MemberType NoteProperty -Name SqlInstance -Value $server.DomainInstanceName
            Select-DefaultView -InputObject $store -Property ComputerName, InstanceName, SqlInstance, ServerName, Sessions, Packages, RunningSessionCount
        }
    }
}
tools\dbatools\functions\Import-DbaCsvToSql.ps1
function Import-DbaCsvToSql {
    <#
        .SYNOPSIS
            Efficiently imports very large (and small) CSV files into SQL Server using only the .NET Framework and PowerShell.

        .DESCRIPTION
            Import-DbaCsvToSql takes advantage of .NET's super fast SqlBulkCopy class to import CSV files into SQL Server at up to 90,000 rows a second.

            The entire import is contained within a transaction, so if a failure occurs or the script is aborted, no changes will persist.

            If the table specified does not exist, it will be automatically created using best guessed data types. In addition, the destination table can be truncated prior to import.

            The Query parameter will be used to import only the data returned from a SQL Query executed against the CSV file(s). This function supports a number of bulk copy options. Please see parameter list for details.

        .PARAMETER CSV
            Specifies path to the CSV file(s) to be imported. Multiple files may be imported if they are formatted similarly.

            If no file is specified, a dialog box will appear to select your file(s).

        .PARAMETER FirstRowColumns
            If this switch is enabled, the first row in the file will be used as column names for the data being imported.

            If the first row does not contain column names and -Query is specified, use field names "column1, column2, column3" and so on.

        .PARAMETER Delimiter
            Specifies the delimiter used in the imported file(s). If no delimiter is specified, comma is assumed.

            Valid delimiters are '`t`, '|', ';',' ' and ',' (tab, pipe, semicolon, space, and comma).

        .PARAMETER SingleColumn
            Specifies that the file contains a single column of data

        .PARAMETER SqlInstance
            The SQL Server Instance to import data into.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies the name of the database the CSV will be imported into. Options for this this parameter are  auto-populated from the server.

        .PARAMETER Schema
            Specifies the schema in which the SQL table or view where CSV will be imported into resides. Default is dbo

            If a schema name is not specified, and a CSV name with multiple dots is specified (ie; something.data.csv) then this will be interpreted as a request to import into a table [data] in the schema [something].

            If a schema does not currently exist, it will be created, after a prompt to confirm this. Authorization will be set to dbo by default

        .PARAMETER Table
            Specifies the SQL table or view where CSV will be imported into.

            If a table name is not specified, the table name will be automatically determined from the filename, and a prompt will appear to confirm the table name.

            If a table does not currently exist, it will created.  SQL datatypes are determined from the first row of the CSV that contains data (skips first row if -FirstRowColumns is specified). Datatypes used are: bigint, numeric, datetime and varchar(MAX).

            If the automatically generated table datatypes do not work for you, please create the table prior to import.

        .PARAMETER Truncate
            If this switch is enabled, the destination table will be truncated prior to import.

        .PARAMETER Safe
            If this switch is enabled, OleDb is used to import the records. By default, Import-DbaCsvToSql uses StreamReader for imports. StreamReader is super fast, but may not properly parse some files.

            When using OleDb the import will be slower but more predictable when it comes to parsing CSV files. A schema.ini is automatically generated for best results. If schema.ini currently exists in the directory, it will be moved to a temporary location, then moved back.

            OleDB also enables the script to use the -Query parameter, which enables you to import specific subsets of data within a CSV file. OleDB imports at up to 21,000 rows/sec.

        .PARAMETER Turbo
            If this switch is enabled, a Table Lock will be created for the import to make the import run as fast as possible. Depending upon the number of columns and datatypes, this may be over 90,000 records per second.

            This switch cannot be used in conjunction with -Query.

            Remember the Turbo button? This one actually works. Turbo is mega fast, but may not handle some datatypes as well as other methods.

            If your CSV file is rather vanilla and doesn't have a ton of NULLs, Turbo may work well for you.

        .PARAMETER First
            Specifies the number of rows to import. If this parameter is omitted, the entire file is imported. Row counts start at the top of the file, but skip the first row if -FirstRowColumns is specified.

            Use -Query if you need advanced First (TOP) functionality.

        .PARAMETER Query
            Specifies a query to execute against the CSV data to select/modify the data being imported.

            To make command line queries easy, this module will convert the word "csv" to the actual CSV formatted table name. If the FirstRowColumns switch is not used, the query should use column1, column2, column3, etc.

            Cannot be used in conjunction with -Turbo or -First. When -Query is specified, the slower import method, OleDb, will be used.

        .PARAMETER NotifyAfter
            Specifies the import row count interval for reporting progress. A notification will be shown after each group of this many rows has been imported.

        .PARAMETER BatchSize
            Specifies the batch size for the import. Defaults to 50000.

        .PARAMETER TableLock
            If this switch is enabled, the SqlBulkCopy option to acquire a table lock will be used. This is automatically used if -Turbo is enabled.

            Per Microsoft "Obtain a bulk update lock for the duration of the bulk copy operation. When not
            specified, row locks are used."

        .PARAMETER CheckConstraints
            If this switch is enabled, the SqlBulkCopy option to check constraints will be used.

            Per Microsoft "Check constraints while data is being inserted. By default, constraints are not checked."

        .PARAMETER FireTriggers
            If this switch is enabled, the SqlBulkCopy option to allow insert triggers to be executed will be used.

            Per Microsoft "When specified, cause the server to fire the insert triggers for the rows being inserted into the database."

        .PARAMETER KeepIdentity
            If this switch is enabled, the SqlBulkCopy option to keep identity values from the source will be used.

            Per Microsoft "Preserve source identity values. When not specified, identity values are assigned by the destination."

        .PARAMETER KeepNulls
            If this switch is enabled, the SqlBulkCopy option to keep NULL values in the table will be used.

            Per Microsoft "Preserve null values in the destination table regardless of the settings for default values. When not specified, null values are replaced by default values where applicable."

        .NOTES
            Tags: Migration
            Author: Chrissy LeMaire (@cl), netnerds.net
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://blog.netnerds.net/2015/09/Import-DbaCsvtosql-super-fast-csv-to-sql-server-import-powershell-module/

        .EXAMPLE
            Import-DbaCsvToSql -Csv C:\temp\housing.csv -SqlInstance sql001 -Database markets

            Imports the entire comma-delimited housing.csv to the SQL "markets" database on a SQL Server named sql001.

            Since a table name was not specified, the table name is automatically determined from filename as "housing" and a prompt will appear to confirm table name.

            The first row is not skipped, as it does not contain column names.

        .EXAMPLE
            Import-DbaCsvToSql -Csv .\housing.csv -SqlInstance sql001 -Database markets -Table housing -First 100000 -Safe -Delimiter "`t" -FirstRowColumns

            Imports the first 100,000 rows of the tab delimited housing.csv file to the "housing" table in the "markets" database on a SQL Server named sql001. Since -Safe was specified, the OleDB method will be used for the bulk import. The first row is skipped, as it contains column names.

        .EXAMPLE
            Import-DbaCsvToSql -csv C:\temp\huge.txt -SqlInstance sqlcluster -Database locations -Table latitudes -Delimiter "|" -Turbo

            Imports all records from the pipe delimited huge.txt file using the fastest method possible into the latitudes table within the locations database. Obtains a table lock for the duration of the bulk copy operation. This specific command has been used
            to import over 10.5 million rows in 2 minutes.

        .EXAMPLE
            Import-DbaCsvToSql -Csv C:\temp\housing.csv, .\housing2.csv -SqlInstance sql001 -Database markets -Table housing -Delimiter "`t" -query "select top 100000 column1, column3 from csv" -Truncate

            Truncates the "housing" table, then imports columns 1 and 3 of the first 100000 rows of the tab-delimited housing.csv in the C:\temp directory, and housing2.csv in the current directory. Since the query is executed against both files, a total of 200,000 rows will be imported.

        .EXAMPLE
            Import-DbaCsvToSql -Csv C:\temp\housing.csv -SqlInstance sql001 -Database markets -Table housing -query "select address, zip from csv where state = 'Louisiana'" -FirstRowColumns -Truncate -FireTriggers

            Uses the first line to determine CSV column names. Truncates the "housing" table on the SQL Server, then imports the address and zip columns from all records in the housing.csv where the state equals Louisiana.

            Triggers are fired for all rows. Note that this does slightly slow down the import.

        .EXAMPLE
            Import-DbaCsvToSql -Csv c:\temp\SingleColumn.csv -SqlInstance sql001 -Database markets -Table TempTable -SingleColumn

            Upload the single column Csv SingleColumn.csv to Temptable which has just one column

        .EXAMPLE
            Import-DbaCsvToSql -Csv "\\FileServer\To Import\housing.csv" -SqlInstance sql001 -Database markets

            Imports the entire comma-delimited housing.csv located in the share named "To Import" on FileServer to the SQL "markets" database on a SQL Server named sql001.

        .EXAMPLE
            Import-DbaCsvToSql -Csv '\\FileServer\R$\To Import\housing.csv' -SqlInstance sql001 -Database markets

            Imports the entire comma-delimited housing.csv located in the directory R:\To Import on FileServer using the administrative share to the SQL "markets" database on a SQL Server named sql001.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    Param (
        [string[]]$Csv,
        [Parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [object]$SqlCredential,
        [string]$Table,
        [string]$Schema = "dbo",
        [switch]$Truncate,
        [ValidateSet("`t", "|", ";", " ", ",")]
        [string]$Delimiter = ",",
        [switch]$SingleColumn,
        [switch]$FirstRowColumns,
        [parameter(ParameterSetName = "reader")]
        [switch]$Turbo,
        [parameter(ParameterSetName = "ole")]
        [switch]$Safe,
        [int]$First = 0,
        [parameter(ParameterSetName = "ole")]
        [string]$Query = "select * from csv",
        [int]$BatchSize = 50000,
        [int]$NotifyAfter,
        [switch]$TableLock,
        [switch]$CheckConstraints,
        [switch]$FireTriggers,
        [switch]$KeepIdentity,
        [switch]$KeepNulls,
        #[Parameter(DontShow)]
        [switch]$shellswitch,
        #[Parameter(DontShow)]
        [string]$SqlCredentialPath
    )

    DynamicParam {

        if ($SqlInstance.length -gt 0) {
            # Auto populate database list from specified sqlserver
            $paramconn = New-Object System.Data.SqlClient.SqlConnection

            if ($SqlCredentialPath.length -gt 0) {
                $SqlCredential = Import-CliXml $SqlCredentialPath
            }

            if ($SqlCredential.count -eq 0 -or $null -eq $SqlCredential) {
                $paramconn.ConnectionString = "Data Source=$SqlInstance;Integrated Security=True;"
            }
            else {
                $paramconn.ConnectionString = "Data Source=$SqlInstance;User Id=$($SqlCredential.UserName); Password=$($SqlCredential.GetNetworkCredential().Password);"
            }

            try {
                $paramconn.Open()
                $sql = "select name from master.dbo.sysdatabases"
                $paramcmd = New-Object System.Data.SqlClient.SqlCommand($sql, $paramconn, $null)
                $paramdt = New-Object System.Data.DataTable
                $paramdt.Load($paramcmd.ExecuteReader())
                $databaselist = $paramdt.rows.name
                $null = $paramcmd.Dispose()
                $null = $paramconn.Close()
                $null = $paramconn.Dispose()
            }
            catch {
                # But if the routine fails, at least let them specify a database manually
                $databaselist = ""
            }

            # Reusable parameter setup
            $newparams = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
            $attributes = New-Object System.Management.Automation.ParameterAttribute
            $attributes.Mandatory = $false

            # Database list parameter setup
            $dbattributes = New-Object -Type System.Collections.ObjectModel.Collection[System.Attribute]
            $dbattributes.Add($attributes)
            # If a list of databases were returned, populate the parameter set
            if ($databaselist.length -gt 0) {
                $dbvalidationset = New-Object System.Management.Automation.ValidateSetAttribute -ArgumentList $databaselist
                $dbattributes.Add($dbvalidationset)
            }

            $Database = New-Object -Type System.Management.Automation.RuntimeDefinedParameter("Database", [String], $dbattributes)
            $newparams.Add("Database", $Database)
            return $newparams
        }
    }

    begin {
        function Get-Columns {
            <#
                .SYNOPSIS
                    TextFieldParser will be used instead of an OleDbConnection.
                    This is because the OleDbConnection driver may not exist on x64.

                .EXAMPLE
                    $columns = Get-Columns -Csv .\myfile.csv -Delimiter "," -FirstRowColumns $true

                .OUTPUTS
                    Array of column names
            #>

            param (
                [Parameter(Mandatory = $true)]
                [string[]]$Csv,
                [Parameter(Mandatory = $true)]
                [string]$Delimiter,
                [Parameter(Mandatory = $true)]
                [bool]$FirstRowColumns
            )

            $columnparser = New-Object Microsoft.VisualBasic.FileIO.TextFieldParser($csv[0])
            $columnparser.TextFieldType = "Delimited"
            $columnparser.SetDelimiters($Delimiter)
            $rawcolumns = $columnparser.ReadFields()

            if ($FirstRowColumns -eq $true) {
                $columns = ($rawcolumns | ForEach-Object { $_ -Replace '"' } | Select-Object -Property @{ Name = "name"; Expression = { "[$_]" } }).name
            }
            else {
                $columns = @()
                foreach ($number in 1..$rawcolumns.count) {
                    $columns += "[column$number]"
                }
            }

            $columnparser.Close()
            $columnparser.Dispose()
            return $columns
        }

        function Get-ColumnText {
            <#
                .SYNOPSIS
                    Returns an array of data, which can later be parsed for potential datatypes.

                .EXAMPLE
                    $columns = Get-Columns -Csv .\myfile.csv -Delimiter ","

                .OUTPUTS
                    Array of column data
             #>
            param (
                [Parameter(Mandatory = $true)]
                [string[]]$Csv,
                [Parameter(Mandatory = $true)]
                [string]$Delimiter
            )
            $columnparser = New-Object Microsoft.VisualBasic.FileIO.TextFieldParser($csv[0])
            $columnparser.TextFieldType = "Delimited"
            $columnparser.SetDelimiters($Delimiter)
            $line = $columnparser.ReadLine()
            # Skip a line, in case first line are column names
            $line = $columnparser.ReadLine()
            $datatext = $columnparser.ReadFields()
            $columnparser.Close()
            $columnparser.Dispose()
            return $datatext
        }

        function Write-Schemaini {
            <#
                .SYNOPSIS
                    Unfortunately, passing delimiter within the OleDBConnection connection string is unreliable, so we'll use schema.ini instead. The default delimiter in Windows changes depending on country, so we'll do this for every delimiter, even commas.

                    Get OLE datatypes based on best guess of column data within the -Columns parameter.

                    Sometimes SQL will accept a datetime that OLE won't, so Text will be used for datetime.

                .EXAMPLE
                    $columns = Get-Columns -Csv C:\temp\myfile.csv -Delimiter ","
                    $movedschemainis = Write-Schemaini -Csv  C:\temp\myfile.csv -Columns $columns -ColumnText $columntext -Delimiter "," -FirstRowColumns $true

                .OUTPUTS
                    Creates new schema files, that look something like this:

                    [housingdata.csv]
                    Format=Delimited(,)
                    ColNameHeader=True
                    Col1="House ID" Long
                    Col2="Description" Memo
                    Col3="Price" Double

                    Returns an array of existing schema files that have been moved, if any.
             #>
            param (
                [Parameter(Mandatory = $true)]
                [string[]]$Csv,
                [Parameter(Mandatory = $true)]
                [string[]]$Columns,
                [string[]]$ColumnText,
                [Parameter(Mandatory = $true)]
                [string]$Delimiter,
                [Parameter(Mandatory = $true)]
                [bool]$FirstRowColumns
            )

            $movedschemainis = @{ }
            foreach ($file in $csv) {
                $directory = Split-Path $file
                $schemaexists = Test-Path "$directory\schema.ini"
                if ($schemaexists -eq $true) {
                    $newschemaname = "$env:TEMP\$(Split-Path $file -leaf)-schema.ini"
                    $movedschemainis.Add($newschemaname, "$directory\schema.ini")
                    Move-Item "$directory\schema.ini" $newschemaname -Force
                }

                $filename = Split-Path $file -leaf; $directory = Split-Path $file
                Add-Content -Path "$directory\schema.ini" -Value "[$filename]"
                Add-Content -Path "$directory\schema.ini" -Value "Format=Delimited($InternalDelimiter)"
                Add-Content -Path "$directory\schema.ini" -Value "ColNameHeader=$FirstRowColumns"

                $index = 0
                $olecolumns = ($columns | ForEach-Object { $_ -Replace "\[|\]", '"' })

                foreach ($datatype in $columntext) {
                    $olecolumnname = $olecolumns[$index]
                    $index++

                    try {
                        [System.Guid]::Parse($datatype) | Out-Null; $isguid = $true
                    }
                    catch {
                        $isguid = $false
                    }

                    if ($isguid -eq $true) {
                        $oledatatype = "Text"
                    }
                    elseif ([int64]::TryParse($datatype, [ref]0) -eq $true) {
                        $oledatatype = "Long"
                    }
                    elseif ([double]::TryParse($datatype, [ref]0) -eq $true) {
                        $oledatatype = "Double"
                    }
                    elseif ([datetime]::TryParse($datatype, [ref]0) -eq $true) {
                        $oledatatype = "Text"
                    }
                    else {
                        $oledatatype = "Memo"
                    }

                    Add-Content -Path "$directory\schema.ini" -Value "Col$($index)`=$olecolumnname $oledatatype"
                }
            }
            return $movedschemainis
        }

        function New-SqlTable {
            <#
                .SYNOPSIS
                    Creates new Table using existing SqlCommand.

                    SQL datatypes based on best guess of column data within the -ColumnText parameter.
                    Columns parameter determine column names.

                .EXAMPLE
                    New-SqlTable -Csv $Csv -Delimiter $InternalDelimiter -Columns $columns -ColumnText $columntext -SqlConn $sqlconn -Transaction $transaction

                .OUTPUTS
                    Creates new table
            #>

            param (
                [Parameter(Mandatory = $true)]
                [string[]]$Csv,
                [Parameter(Mandatory = $true)]
                [string]$Delimiter,
                [string[]]$Columns,
                [string[]]$ColumnText,
                [System.Data.SqlClient.SqlConnection]$sqlconn,
                [System.Data.SqlClient.SqlTransaction]$transaction
            )
            # Get SQL datatypes by best guess on first data row
            $sqldatatypes = @(); $index = 0

            foreach ($column in $columntext) {
                $sqlcolumnname = $Columns[$index]
                $index++

                # bigint, float, and datetime are more accurate, but it didn't work
                # as often as it should have, so we'll just go for a smaller datatype
                if ([int64]::TryParse($column, [ref]0) -eq $true) {
                    $sqldatatype = "varchar(255)"
                }
                elseif ([double]::TryParse($column, [ref]0) -eq $true) {
                    $sqldatatype = "varchar(255)"
                }
                elseif ([datetime]::TryParse($column, [ref]0) -eq $true) {
                    $sqldatatype = "varchar(255)"
                }
                else {
                    $sqldatatype = "varchar(MAX)"
                }

                $sqldatatypes += "$sqlcolumnname $sqldatatype"
            }

            $sql = "BEGIN CREATE TABLE [$schema].[$table] ($($sqldatatypes -join ' NULL,')) END"
            $sqlcmd = New-Object System.Data.SqlClient.SqlCommand($sql, $sqlconn, $transaction)
            try {
                $null = $sqlcmd.ExecuteNonQuery()
            }
            catch {
                $errormessage = $_.Exception.Message.ToString()
                throw "Failed to execute $sql. `nDid you specify the proper delimiter? `n$errormessage"
            }

            Write-Output "[*] Successfully created table $schema.$table with the following column definitions:`n $($sqldatatypes -join "`n ")"
            # Write-Warning "All columns are created using a best guess, and use their maximum datatype."
            Write-Warning "This is inefficient but allows the script to import without issues."
            Write-Warning "Consider creating the table first using best practices if the data will be used in production."
        }


        if ($shellswitch -eq $false) { Write-Output "[*] Started at $(Get-Date)" }

        # Load the basics
        [void][Reflection.Assembly]::LoadWithPartialName("System.Data")
        [void][Reflection.Assembly]::LoadWithPartialName("Microsoft.VisualBasic")
        [void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

        # Getting the total rows copied is a challenge. Use SqlBulkCopyExtension.
        # http://stackoverflow.com/questions/1188384/sqlbulkcopy-row-count-when-complete

        $source = 'namespace System.Data.SqlClient
        {
            using Reflection;

            public static class SqlBulkCopyExtension
            {
                const String _rowsCopiedFieldName = "_rowsCopied";
                static FieldInfo _rowsCopiedField = null;

                public static int RowsCopiedCount(this SqlBulkCopy bulkCopy)
                {
                    if (_rowsCopiedField == null) _rowsCopiedField = typeof(SqlBulkCopy).GetField(_rowsCopiedFieldName, BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance);
                    return (int)_rowsCopiedField.GetValue(bulkCopy);
                }
            }
        }
    '
        Add-Type -ReferencedAssemblies 'System.Data.dll' -TypeDefinition $source -ErrorAction SilentlyContinue
    }

    process {
        # turbo mode requires a table lock, or it's just regular fast
        if ($turbo -eq $true) {
            $tablelock = $true
        }

        # Hack to get around the delimter parameter ValidateSet
        if ($SingleColumn -eq $true) {
            $InternalDelimiter = ''
        }
        else {
            $InternalDelimiter = $Delimiter
        }

        # The query parameter requires OleDB which is invoked by the "safe" variable
        # Actually, a select could be performed on the datatable used in StreamReader, too.
        # Maybe that will be done later.
        if ($query -ne "select * from csv") {
            $safe = $true
        }

        if ($first -gt 0 -and $query -ne "select * from csv") {
            throw "Cannot use both -Query and -First. If a query is necessary, use TOP $first within your SQL statement."
        }

        # In order to support -First in both Streamreader, and OleDb imports, the query must be modified slightly.
        if ($first -gt 0) {
            $query = "select top $first * from csv"
        }

        # If shell switch occured, and encrypted SQL credentials were written to disk, create $SqlCredential
        if ($SqlCredentialPath.length -gt 0) {
            $SqlCredential = Import-CliXml $SqlCredentialPath
        }

        # Get Database string from RuntimeDefinedParameter if required
        if ($database -isnot [string]) {
            $database = $PSBoundParameters.Database
        }
        if ($database.length -eq 0) {
            throw "You must specify a database."
        }

        # Check to ensure a Windows account wasn't used as a SQL Credential
        if ($SqlCredential.count -gt 0 -and $SqlCredential.UserName -like "*\*") {
            throw "Only SQL Logins can be used as a SqlCredential."
        }

        # If no CSV was specified, prompt the user to select one.
        if ($csv.length -eq 0) {
            $fd = New-Object System.Windows.Forms.OpenFileDialog
            $fd.InitialDirectory = [environment]::GetFolderPath("MyDocuments")
            $fd.Filter = "CSV Files (*.csv;*.tsv;*.txt)|*.csv;*.tsv;*.txt"
            $fd.Title = "Select one or more CSV files"
            $fd.MultiSelect = $true
            $null = $fd.showdialog()
            $csv = $fd.filenames
            if ($csv.length -eq 0) {
                throw "No CSV file selected."
            }
        }
        else {
            foreach ($file in $csv) {
                $exists = Test-Path $file
                if ($exists -eq $false) {
                    throw "$file does not exist"
                }
            }
        }

        # Resolve the full path of each CSV
        $resolvedcsv = @()
        foreach ($file in $csv) {
            $resolvedcsv += (Resolve-Path $file).ProviderPath
        }
        $csv = $resolvedcsv

        # UniqueIdentifier kills OLE DB / SqlBulkCopy imports. Check to see if destination table contains this datatype.
        if ($safe -eq $true) {
            $sqlcheckconn = New-Object System.Data.SqlClient.SqlConnection
            if ($SqlCredential.count -eq 0 -or $null -eq $SqlCredential) {
                $sqlcheckconn.ConnectionString = "Data Source=$SqlInstance;Integrated Security=True;Connection Timeout=3; Initial Catalog=master"
            }
            else {
                $username = ($SqlCredential.UserName).TrimStart("\")
                $sqlcheckconn.ConnectionString = "Data Source=$SqlInstance;User Id=$username; Password=$($SqlCredential.GetNetworkCredential().Password);Connection Timeout=3; Initial Catalog=master"
            }

            try {
                $sqlcheckconn.Open()
            }
            catch {
                throw $_.Exception
            }

            # Ensure database exists
            $sql = "select count(*) from master.dbo.sysdatabases where name = '$database'"
            $sqlcheckcmd = New-Object System.Data.SqlClient.SqlCommand($sql, $sqlcheckconn)
            $dbexists = $sqlcheckcmd.ExecuteScalar()
            if ($dbexists -eq $false) {
                throw "Database does not exist on $SqlInstance"
            }

            # Change database after the fact, because if db doesn't exist, the login would fail.
            $sqlcheckconn.ChangeDatabase($database)

            $sql = "SELECT t.name as datatype FROM sys.columns c
                JOIN sys.types t ON t.system_type_id = c.system_type_id
                WHERE c.object_id = object_id('$schema.$table') and t.name != 'sysname'"
            $sqlcheckcmd = New-Object System.Data.SqlClient.SqlCommand($sql, $sqlcheckconn)
            $sqlcolumns = New-Object System.Data.DataTable
            $sqlcolumns.load($sqlcheckcmd.ExecuteReader("CloseConnection"))
            $sqlcheckconn.Dispose()
            if ($sqlcolumns.datatype -contains "UniqueIdentifier") {
                throw "UniqueIdentifier not supported by OleDB/SqlBulkCopy. Query and Safe cannot be supported."
            }
        }

        if ($safe -eq $true) {
            # Check for drivers. First, ACE (Access) if file is smaller than 2GB, then JET
            # ACE doesn't handle files larger than 2gb. What gives?
            foreach ($file in $csv) {
                $filesize = (Get-ChildItem $file).Length / 1GB
                if ($filesize -gt 1.99) {
                    $jetonly = $true
                }
            }

            if ($jetonly -ne $true) {
                $provider = (New-Object System.Data.OleDb.OleDbEnumerator).GetElements() | Where-Object { $_.SOURCES_NAME -like "Microsoft.ACE.OLEDB.*" }
            }

            if ($null -eq $provider) {
                $provider = (New-Object System.Data.OleDb.OleDbEnumerator).GetElements() | Where-Object { $_.SOURCES_NAME -like "Microsoft.Jet.OLEDB.*" }
            }

            # If a suitable provider cannot be found (If x64 and Access hasn't been installed)
            # switch to x86, because it natively supports JET
            if ($null -ne $provider) {
                if ($provider -is [system.array]) {
                    $provider = $provider[$provider.GetUpperBound(0)].SOURCES_NAME
                }
                else {
                    $provider = $provider.SOURCES_NAME
                }
            }

            # If a provider doesn't exist, it is necessary to switch to x86 which natively supports JET.
            if ($null -eq $provider) {
                # While Install-Module takes care of installing modules to x86 and x64, Import-Module doesn't.
                # Because of this, the Module must be exported, written to file, and imported in the x86 shell.
                $definition = (Get-Command Import-DbaCsvToSql).Definition
                $function = "Function Import-DbaCsvToSql { $definition }"
                Set-Content "$env:TEMP\Import-DbaCsvToSql.psm1" $function

                # Encode the SQL string, since some characters may mess up after being passed a second time.
                $bytes = [System.Text.Encoding]::UTF8.GetBytes($query)
                $query = [System.Convert]::ToBase64String($bytes)

                # Put switches back into proper format
                $switches = @()
                $options = "TableLock", "CheckConstraints", "FireTriggers", "KeepIdentity", "KeepNulls", "Default", "Truncate", "FirstRowColumns", "Safe"
                foreach ($option in $options) {
                    $optionValue = Get-Variable $option -ValueOnly -ErrorAction SilentlyContinue
                    if ($optionValue -eq $true) {
                        $switches += "-$option"
                    }
                }

                # Perform the actual switch, which removes any registered Import-DbaCsvToSql modules
                # Then imports, and finally re-executes the command.
                $csv = $csv -join ","; $switches = $switches -join " "
                if ($SqlCredential.count -gt 0) {
                    $SqlCredentialPath = "$env:TEMP\sqlcredential.xml"
                    Export-CliXml -InputObject $SqlCredential $SqlCredentialPath
                }
                $command = "Import-DbaCsvToSql -Csv $csv -SqlInstance '$SqlInstance'-Database '$database' -Table '$table' -Delimiter '$InternalDelimiter' -First $First -Query '$query' -Batchsize $BatchSize -NotifyAfter $NotifyAfter $switches -shellswitch"

                if ($SqlCredentialPath.length -gt 0) {
                    $command += " -SqlCredentialPath $SqlCredentialPath"
                }
                Write-Verbose "Switching to x86 shell, then switching back."
                &"$env:windir\syswow64\windowspowershell\v1.0\powershell.exe" "$command"
                return
            }
        }

        # Do the first few lines contain the specified delimiter?
        foreach ($file in $csv) {
            try { $firstfewlines = Get-Content $file -First 3 -ErrorAction Stop }
            catch { throw "$file is in use." }
            if ($SingleColumn -ne $true ) {
                foreach ($line in $firstfewlines) {
                    if (($line -match $InternalDelimiter) -eq $false) {
                        throw "Delimiter $InternalDelimiter not found in first row of $file."
                    }
                }
            }
        }

        # If more than one csv specified, check to ensure number of columns match
        if ($csv -is [system.array]) {
            if ($SingleColumn -ne $true) {
                $numberofcolumns = ((Get-Content $csv[0] -First 1 -ErrorAction Stop) -Split $InternalDelimiter).Count

                foreach ($file in $csv) {
                    $firstline = Get-Content $file -First 1 -ErrorAction Stop
                    $newnumcolumns = ($firstline -Split $InternalDelimiter).Count
                    if ($newnumcolumns -ne $numberofcolumns) {
                        throw "Multiple csv file mismatch. Do both use the same delimiter and have the same number of columns?"
                    }
                }
            }
        }

        # Automatically generate Table name if not specified, then prompt user to confirm
        if ($table.length -eq 0) {
            $table = [IO.Path]::GetFileNameWithoutExtension($csv[0])

            #Count the dots in the file name.
            #1 dot, treat it as schema.table naming
            #2 or more dots, really should catch it as bad practice, but the rest of the script appears to let it pass
            if (($table.ToCharArray() | Where-Object {$_ -eq '.'} | Measure-Object).count -gt 0) {
                if (($schema -ne $table.Split('.')[0]) -and ($schema -ne 'dbo')) {
                    $title = "Conflicting schema names specified"
                    $message = "Please confirm which schema you want to use."
                    $schemaA = New-Object System.Management.Automation.Host.ChoiceDescription "&A - $schema", "Use schema name $schema for import."
                    $schemaB = New-Object System.Management.Automation.Host.ChoiceDescription "&B - $($table.Split('.')[0])", "Use schema name $($table.Split('.')[0]) for import."
                    $options = [System.Management.Automation.Host.ChoiceDescription[]]($schemaA, $schemaB)
                    $result = $host.ui.PromptForChoice($title, $message, $options, 0)
                    if ($result -eq 1) {
                        $schema = $table.Split('.')[0]
                        $tmparray = $table.split('.')
                        $table = $tmparray[1..$tmparray.Length] -join '.'
                    }
                }

            }
            else {
                $title = "Table name not specified."
                $message = "Would you like to use the automatically generated name: $table"
                $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Uses table name $table for import."
                $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Allows you to specify an alternative table name."
                $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
                $result = $host.ui.PromptForChoice($title, $message, $options, 0)
                if ($result -eq 1) {
                    do {
                        $table = Read-Host "Please enter a table name"
                    }
                    while ($table.Length -eq 0)
                }

            }
        }

        # If the shell has switched, decode the $query string.
        if ($shellswitch -eq $true) {
            $bytes = [System.Convert]::FromBase64String($Query)
            $query = [System.Text.Encoding]::UTF8.GetString($bytes)
            $csv = $csv -Split ","
        }

        # Create columns based on first data row of first csv.
        if ($SingleColumn -ne $true) {
            Write-Output "[*] Calculating column names and datatypes"
            $columns = Get-Columns -Csv $Csv -Delimiter $InternalDelimiter -FirstRowColumns $FirstRowColumns
            if ($columns.count -gt 255 -and $safe -eq $true) {
                throw "CSV must contain fewer than 256 columns."
            }
        }

        if ($SingleColumn -ne $true) {
            $columntext = Get-ColumnText -Csv $Csv -Delimiter $InternalDelimiter
        }

        # OLEDB method requires extra checks
        if ($safe -eq $true) {
            # Advanced SQL queries may not work (SqlBulkCopy likes a 1 to 1 mapping), so warn the user.
            if ($Query -match "GROUP BY" -or $Query -match "COUNT") {
                Write-Warning "Script doesn't really support the specified query. This probably won't work, but will be attempted anyway."
            }

            # Check for proper SQL syntax, which for the purposes of this module must include the word "table"
            if ($query.ToLower() -notmatch "\bcsv\b") {
                throw "SQL statement must contain the word 'csv'. Please see this module's documentation for more details."
            }

            # In order to ensure consistent results, a schema.ini file must be created.
            # If a schema.ini already exists, it will be moved to TEMP temporarily.
            Write-Verbose "Creating schema.ini"
            $movedschemainis = Write-Schemaini -Csv $Csv -Columns $columns -Delimiter "$InternalDelimiter" -FirstRowColumns $FirstRowColumns -ColumnText $columntext
        }

        # Display SQL Server Login info
        if ($sqlcredential.count -gt 0) {
            $username = "SQL login $($SqlCredential.UserName)"
        }
        else {
            $username = "Windows login $(whoami)"
        }
        # Open Connection to SQL Server
        Write-Output "[*] Logging into $SqlInstance as $username"
        $sqlconn = New-Object System.Data.SqlClient.SqlConnection
        if ($SqlCredential.count -eq 0) {
            $sqlconn.ConnectionString = "Data Source=$SqlInstance;Integrated Security=True;Connection Timeout=3; Initial Catalog=master"
        }
        else {
            $sqlconn.ConnectionString = "Data Source=$SqlInstance;User Id=$($SqlCredential.UserName); Password=$($SqlCredential.GetNetworkCredential().Password);Connection Timeout=3; Initial Catalog=master"
        }

        try {
            $sqlconn.Open()
        }
        catch {
            throw "Could not open SQL Server connection. Is $SqlInstance online?"
        }

        # Everything will be contained within 1 transaction, even creating a new table if required
        # and truncating the table, if specified.
        $transaction = $sqlconn.BeginTransaction()

        # Ensure database exists
        $sql = "select count(*) from master.dbo.sysdatabases where name = '$database'"
        $sqlcmd = New-Object System.Data.SqlClient.SqlCommand($sql, $sqlconn, $transaction)
        $dbexists = $sqlcmd.ExecuteScalar()
        if ($dbexists -eq $false) {
            throw "Database does not exist on $SqlInstance"
        }
        Write-Output "[*] Database exists"

        $sqlconn.ChangeDatabase($database)

        # Enure Schema exists
        $sql = "select count(*) from $database.sys.schemas where name='$schema'"
        $sqlcmd = New-Object System.Data.SqlClient.SqlCommand($sql, $sqlconn, $transaction)
        $schemaexists = $sqlcmd.ExecuteScalar()

        # If Schema doesn't exist create it
        # Defaulting to dbo.
        if ($schemaexists -eq $false) {
            Write-Output "[*] Creating schema $schema"
            $sql = "CREATE SCHEMA [$schema] AUTHORIZATION dbo"
            $sqlcmd = New-Object System.Data.SqlClient.SqlCommand($sql, $sqlconn, $transaction)
            try {
                $null = $sqlcmd.ExecuteNonQuery()
            }
            catch {
                Write-Warning "Could not create $schema"
            }

        }

        # Ensure table exists
        $sql = "select count(*) from $database.sys.tables where name = '$table' and schema_id=schema_id('$schema')"
        $sqlcmd = New-Object System.Data.SqlClient.SqlCommand($sql, $sqlconn, $transaction)
        $tablexists = $sqlcmd.ExecuteScalar()

        # Create the table if required. Remember, this will occur within a transaction, so if the script fails, the
        # new table will no longer exist.
        if ($tablexists -eq $false) {
            Write-Output "[*] Table does not exist"
            Write-Output "[*] Creating table"
            New-SqlTable -Csv $Csv -Delimiter $InternalDelimiter -Columns $columns -ColumnText $columntext -SqlConn $sqlconn -Transaction $transaction
        }
        else {
            Write-Output "[*] Table exists"
        }

        # Truncate if specified. Remember, this will occur within a transaction, so if the script fails, the
        # truncate will not be committed.
        if ($truncate -eq $true) {
            Write-Output "[*] Truncating table"
            $sql = "TRUNCATE TABLE [$schema].[$table]"
            $sqlcmd = New-Object System.Data.SqlClient.SqlCommand($sql, $sqlconn, $transaction)
            try {
                $null = $sqlcmd.ExecuteNonQuery()
            }
            catch {
                Write-Warning "Could not truncate $schema.$table"
            }
        }

        # Get columns for column mapping
        if ($null -eq $columnMappings) {
            $olecolumns = ($columns | ForEach-Object { $_ -Replace "\[|\]" })
            $sql = "select name from sys.columns where object_id = object_id('$schema.$table') order by column_id"
            $sqlcmd = New-Object System.Data.SqlClient.SqlCommand($sql, $sqlconn, $transaction)
            $sqlcolumns = New-Object System.Data.DataTable
            $sqlcolumns.Load($sqlcmd.ExecuteReader())
        }

        # Time to import!
        $elapsed = [System.Diagnostics.Stopwatch]::StartNew()

        # Process each CSV file specified
        foreach ($file in $csv) {

            # Dynamically set NotifyAfter if it wasn't specified
            if ($notifyAfter -eq 0) {
                if ($resultcount -is [int]) {
                    $notifyafter = $resultcount / 10
                }
                else {
                    $notifyafter = 50000
                }
            }

            # Setup bulk copy
            Write-Output "[*] Starting bulk copy for $(Split-Path $file -Leaf)"

            # Setup bulk copy options
            $bulkCopyOptions = @()
            $options = "TableLock", "CheckConstraints", "FireTriggers", "KeepIdentity", "KeepNulls", "Default", "Truncate"
            foreach ($option in $options) {
                $optionValue = Get-Variable $option -ValueOnly -ErrorAction SilentlyContinue
                if ($optionValue -eq $true) {
                    $bulkCopyOptions += "$option"
                }
            }
            $bulkCopyOptions = $bulkCopyOptions -join " & "

            # Create SqlBulkCopy using default options, or options specified in command line.
            if ($bulkCopyOptions.count -gt 1) {
                $bulkcopy = New-Object Data.SqlClient.SqlBulkCopy($oleconnstring, $bulkCopyOptions, $transaction)
            }
            else {
                $bulkcopy = New-Object Data.SqlClient.SqlBulkCopy($sqlconn, "Default", $transaction)
            }

            $bulkcopy.DestinationTableName = "[$schema].[$table]"
            $bulkcopy.bulkcopyTimeout = 0
            $bulkCopy.BatchSize = $BatchSize
            $bulkCopy.NotifyAfter = $NotifyAfter

            if ($safe -eq $true) {
                # Setup bulkcopy mappings
                for ($columnid = 0; $columnid -lt $sqlcolumns.rows.count; $columnid++) {
                    $null = $bulkCopy.ColumnMappings.Add($olecolumns[$columnid], $sqlcolumns.rows[$columnid].ItemArray[0])
                }

                # Setup the connection string. Data Source is the directory that contains the csv.
                # The file name is also the table name, but with a "#" instead of a "."
                $datasource = Split-Path $file
                $tablename = (Split-Path $file -leaf).Replace(".", "#")
                $oleconnstring = "Provider=$provider;Data Source=$datasource;Extended Properties='text';"

                # To make command line queries easier, let the user just specify "csv" instead of the
                # OleDbconnection formatted name (file.csv -> file#csv)
                $sql = $Query -replace "\bcsv\b", " [$tablename]"

                # Setup the OleDbconnection
                $oleconn = New-Object System.Data.OleDb.OleDbconnection
                $oleconn.ConnectionString = $oleconnstring

                # Setup the OleDBCommand
                $olecmd = New-Object System.Data.OleDB.OleDBCommand
                $olecmd.Connection = $oleconn
                $olecmd.CommandText = $sql

                try {
                    $oleconn.Open()
                }
                catch {
                    throw "Could not open OLEDB connection."
                }

                # Attempt to get the number of results so that a nice progress bar can be displayed.
                # This takes extra time, and files over 100MB take too long, so just skip them.
                if ($sql -match "GROUP BY") {
                    Write-Warning -Message "Query contains GROUP BY clause. Skipping result count."
                }
                else {
                    Write-Output "[*] Determining total rows to be copied. This may take a few seconds."
                }

                if ($sql -match "\bselect top\b") {
                    try {
                        $split = $sql -split "\bselect top \b"
                        $resultcount = [int]($split[1].Trim().Split()[0])
                        Write-Output "[*] Attempting to fetch $resultcount rows"
                    }
                    catch {
                        Write-Warning "Couldn't determine total rows to be copied."
                    }
                }
                elseif ($sql -notmatch "GROUP BY") {
                    $filesize = (Get-ChildItem $file).Length / 1MB
                    if ($filesize -lt 100) {
                        try {
                            $split = $sql -split "\bfrom\b"
                            $sqlcount = "select count(*) from $($split[1])"
                            # Setup the OleDBCommand
                            $olecmd = New-Object System.Data.OleDB.OleDBCommand
                            $olecmd.Connection = $oleconn
                            $olecmd.CommandText = $sqlcount
                            $resultcount = [int]($olecmd.ExecuteScalar())
                            Write-Output "[*] $resultcount rows will be copied"
                        }
                        catch {
                            Write-Warning "Couldn't determine total rows to be copied"
                        }
                    }
                    else {
                        Write-Output "[*] File is too large for efficient result count; progress bar will not be shown."
                    }
                }
            }

            # Write to server :D
            try {
                if ($safe -ne $true) {
                    # Check to ensure batchsize isn't equal to 0
                    if ($batchsize -eq 0) {
                        write-warning "Invalid batchsize for this operation. Increasing to 50k"
                        $batchsize = 50000
                    }

                    # Open the text file from disk
                    $reader = New-Object System.IO.StreamReader($file)
                    if ($FirstRowColumns -eq $true) {
                        $null = $reader.readLine()
                    }

                    # Create the reusable datatable. Columns will be genereated using info from SQL.
                    $datatable = New-Object System.Data.DataTable

                    # Get table column info from SQL Server
                    $sql = "SELECT c.name as colname, t.name as datatype, c.max_length, c.is_nullable FROM sys.columns c
                        JOIN sys.types t ON t.system_type_id = c.system_type_id
                        WHERE c.object_id = object_id('$schema.$table') and t.name != 'sysname'
                        order by c.column_id"
                    $sqlcmd = New-Object System.Data.SqlClient.SqlCommand($sql, $sqlconn, $transaction)
                    $sqlcolumns = New-Object System.Data.DataTable
                    $sqlcolumns.load($sqlcmd.ExecuteReader())

                    foreach ($sqlcolumn in $sqlcolumns) {
                        $datacolumn = $datatable.Columns.Add()
                        $colname = $sqlcolumn.colname
                        $datacolumn.AllowDBNull = $sqlcolumn.is_nullable
                        $datacolumn.ColumnName = $colname
                        $datacolumn.DefaultValue = [DBnull]::Value
                        $datacolumn.Datatype = [string]

                        # The following data types can sometimes cause issues when they are null
                        # so we will treat them differently
                        $convert = "bigint", "DateTimeOffset", "UniqueIdentifier", "smalldatetime", "datetime"
                        if ($convert -notcontains $sqlcolumn.datatype -and $turbo -ne $true) {
                            $null = $bulkCopy.ColumnMappings.Add($datacolumn.ColumnName, $sqlcolumn.colname)
                        }
                    }
                    # For the columns that cause trouble, we'll add an additional column to the datatable
                    # which will perform a conversion.
                    # Setting $column.datatype alone doesn't work as well as setting+converting.
                    if ($turbo -ne $true) {
                        $calcolumns = $sqlcolumns | Where-Object { $convert -contains $_.datatype }
                        foreach ($calcolumn in $calcolumns) {
                            $colname = $calcolumn.colname
                            $null = $newcolumn = $datatable.Columns.Add()
                            $null = $newcolumn.ColumnName = "computed$colname"
                            switch ($calcolumn.datatype) {
                                "bigint" {
                                    $netdatatype = "System.Int64";
                                    $newcolumn.Datatype = [int64]
                                }
                                "DateTimeOffset" {
                                    $netdatatype = "System.DateTimeOffset";
                                    $newcolumn.Datatype = [DateTimeOffset]
                                }
                                "UniqueIdentifier" {
                                    $netdatatype = "System.Guid";
                                    $newcolumn.Datatype = [Guid]
                                }
                                {"smalldatetime", "datetime" -contains $_ } {
                                    $netdatatype = "System.DateTime";
                                    $newcolumn.Datatype = [DateTime]
                                }
                            }
                            # Use a data column expression to facilitate actual conversion
                            $null = $newcolumn.Expression = "Convert($colname, $netdatatype)"
                            $null = $bulkCopy.ColumnMappings.Add($newcolumn.ColumnName, $calcolumn.colname)
                        }
                    }

                    # Check to see if file has quote identified data (ie. "first","second","third")
                    $quoted = $false
                    $checkline = Get-Content $file -Last 1
                    $checkcolumns = $checkline.Split($InternalDelimiter)
                    foreach ($checkcolumn in $checkcolumns) {
                        if ($checkcolumn.StartsWith('"') -and $checkcolumn.EndsWith('"')) {
                            $quoted = $true
                        }
                    }

                    if ($quoted -eq $true) {
                        Write-Warning "The CSV file appears to use quoted identifiers. This may take a little longer."
                        # Thanks for this, Chris! http://www.schiffhauer.com/c-split-csv-values-with-a-regular-expression/
                        $pattern = "((?<=`")[^`"]*(?=`"($InternalDelimiter|$)+)|(?<=$InternalDelimiter|^)[^$InternalDelimiter`"]*(?=$InternalDelimiter|$))"
                    }
                    if ($turbo -eq $true -and $first -eq 0) {
                        while ($null -ne ($line = $reader.ReadLine())) {
                            $i++
                            if ($quoted -eq $true) {
                                $null = $datatable.Rows.Add(($line.TrimStart('"').TrimEnd('"')) -Split "`"$InternalDelimiter`"")
                            }
                            else {
                                $row = $datatable.Rows.Add($line.Split($InternalDelimiter))
                            }

                            if (($i % $batchsize) -eq 0) {
                                $bulkcopy.WriteToServer($datatable)
                                Write-Output "[*] $i rows have been inserted in $([math]::Round($elapsed.Elapsed.TotalSeconds, 2)) seconds."
                                $datatable.Clear()
                            }
                        }
                    }
                    else {
                        if ($turbo -eq $true -and $first -gt 0) { Write-Warning -Message "Using -First makes turbo a little slower." }
                        # Start import!
                        while ($null -ne ($line = $reader.ReadLine())) {
                            $i++
                            try {
                                if ($quoted -eq $true) {
                                    $row = $datatable.Rows.Add(($line.TrimStart('"').TrimEnd('"')) -Split $pattern)
                                }
                                else {
                                    $row = $datatable.Rows.Add($line.Split($InternalDelimiter))
                                }
                            }
                            catch {
                                $row = $datatable.NewRow()
                                try {
                                    $tempcolumn = $line.Split($InternalDelimiter)
                                    $colnum = 0
                                    foreach ($column in $tempcolumn) {
                                        if ($column.length -ne 0) {
                                            $row.item($colnum) = $column
                                        }
                                        else {
                                            $row.item($colnum) = [DBnull]::Value
                                        }
                                        $colnum++
                                    }
                                    $newrow = $datatable.Rows.Add($row)
                                }
                                catch {
                                    Write-Warning "The following line ($i) is causing issues:"
                                    Write-Output $line.Replace($InternalDelimiter, "`n")

                                    if ($quoted -eq $true) {
                                        Write-Warning "The import has failed, likely because the quoted data was a little too inconsistent. Try using the -Safe parameter."
                                    }

                                    Write-Verbose "Column datatypes:"
                                    foreach ($c in $datatable.columns) {
                                        Write-Verbose "$($c.columnname) = $($c.datatype)"
                                    }
                                    Write-Error $_.Exception.Message
                                    break
                                }
                            }

                            if (($i % $batchsize) -eq 0 -or $i -eq $first) {
                                $bulkcopy.WriteToServer($datatable)
                                Write-Output "[*] $i rows have been inserted in $([math]::Round($elapsed.Elapsed.TotalSeconds, 2)) seconds."
                                $datatable.Clear()
                                if ($i -eq $first) {
                                    break
                                }
                            }
                        }
                    }
                    # Add in all the remaining rows since the last clear
                    if ($datatable.Rows.Count -gt 0) {
                        $bulkcopy.WriteToServer($datatable)
                        $datatable.Clear()
                    }
                }
                else {
                    # Add rowcount output
                    $bulkCopy.Add_SqlRowscopied( {
                            $script:totalrows = $args[1].RowsCopied
                            if ($resultcount -is [int]) {
                                $percent = [int](($script:totalrows / $resultcount) * 100)
                                $timetaken = [math]::Round($elapsed.Elapsed.TotalSeconds, 2)
                                Write-Progress -id 1 -activity "Inserting $resultcount rows" -percentcomplete $percent -status ([System.String]::Format("Progress: {0} rows ({1}%) in {2} seconds", $script:totalrows, $percent, $timetaken))
                            }
                            else {
                                Write-Host "$($script:totalrows) rows copied in $([math]::Round($elapsed.Elapsed.TotalSeconds, 2)) seconds."
                            }
                        })

                    $bulkCopy.WriteToServer($olecmd.ExecuteReader("SequentialAccess"))
                    if ($resultcount -is [int]) {
                        Write-Progress -id 1 -activity "Inserting $resultcount rows" -status "Complete" -Completed
                    }

                }
                $completed = $true
            }
            catch {
                # If possible, give more information about common errors.
                if ($resultcount -is [int]) { Write-Progress -id 1 -activity "Inserting $resultcount rows" -status "Failed" -Completed }
                $errormessage = $_.Exception.Message.ToString()
                $completed = $false
                if ($errormessage -like "*for one or more required parameters*") {

                    Write-Error -Message "Looks like your SQL syntax may be invalid. `nCheck the documentation for more information or start with a simple -Query 'select top 10 * from csv'."
                    Write-Error -Message "Valid CSV columns are $columns."

                }
                elseif ($errormessage -match "invalid column length") {

                    # Get more information about malformed CSV input
                    $pattern = @("\d+")
                    $match = [regex]::matches($errormessage, @("\d+"))
                    $index = [int]($match.groups[1].Value) - 1
                    $sql = "select name, max_length from sys.columns where object_id = object_id('$table') and column_id = $index"
                    $sqlcmd = New-Object System.Data.SqlClient.SqlCommand($sql, $sqlconn, $transaction)
                    $datatable = New-Object System.Data.DataTable
                    $datatable.load($sqlcmd.ExecuteReader())
                    $column = $datatable.name
                    $length = $datatable.max_length

                    if ($safe -eq $true) {
                        Write-Warning "Column $index ($column) contains data with a length greater than $length."
                        Write-Warning "SqlBulkCopy makes it pretty much impossible to know which row caused the issue, but it's somewhere after row $($script:totalrows)."
                    }
                }
                elseif ($errormessage -match "does not allow DBNull" -or $errormessage -match "The given value of type") {

                    if ($tablexists -eq $false) {
                        Write-Error "Looks like the datatype prediction didn't work out. Please create the table manually with proper datatypes then rerun the import script."
                    }
                    else {
                        $sql = "select name from sys.columns where object_id = object_id('$table') order by column_id"
                        $sqlcmd = New-Object System.Data.SqlClient.SqlCommand($sql, $sqlconn, $transaction)
                        $datatable = New-Object System.Data.DataTable
                        $datatable.Load($sqlcmd.ExecuteReader())
                        $olecolumns = ($columns | ForEach-Object { $_ -Replace "\[|\]" }) -join ', '
                        Write-Warning "Datatype mismatch."
                        Write-Output "[*] This is sometimes caused by null handling in SqlBulkCopy, quoted data, or the first row being column names and not data (-FirstRowColumns)."
                        Write-Output "[*] This could also be because the data types don't match or the order of the columns within the CSV/SQL statement "
                        Write-Output "[*] do not line up with the order of the table within the SQL Server.`n"
                        Write-Output "[*] CSV order: $olecolumns`n"
                        Write-Output "[*] SQL order: $($datatable.rows.name -join ', ')`n"
                        Write-Output "[*] If this is the case, you can reorder columns by using the -Query parameter or execute the import against a view.`n"
                        if ($safe -eq $false) {
                            Write-Output "[*] You can also try running this import using the -Safe parameter, which handles quoted text well.`n"
                        }
                        Write-Error "`n$errormessage"
                    }


                }
                elseif ($errormessage -match "Input string was not in a correct format" -or $errormessage -match "The given ColumnName") {
                    Write-Warning "CSV contents may be malformed."
                    Write-Error $errormessage
                }
                else { Write-Error $errormessage }
            }
        }

        if ($completed -eq $true) {
            # "Note: This count does not take into consideration the number of rows actually inserted when Ignore Duplicates is set to ON."
            $null = $transaction.Commit()

            if ($safe -eq $false) {
                Write-Output "[*] $i total rows copied"
            }
            else {
                $total = [System.Data.SqlClient.SqlBulkCopyExtension]::RowsCopiedCount($bulkcopy)
                Write-Output "[*] $total total rows copied"
            }
        }
        else {
            Write-Output "[*] Transaction rolled back."
            Write-Output "[*] (Was the proper parameter specified? Is the first row the column name?)."
        }

        # Script is finished. Show elapsed time.
        $totaltime = [math]::Round($elapsed.Elapsed.TotalSeconds, 2)
        Write-Output "[*] Total Elapsed Time for bulk insert: $totaltime seconds"
    }

    End {
        # Close everything just in case & ignore errors
        try {
            $null = $sqlconn.close(); $null = $sqlconn.Dispose(); $null = $oleconn.close;
            $null = $olecmd.Dispose(); $null = $oleconn.Dispose(); $null = $bulkCopy.close();
            $null = $bulkcopy.dispose(); $null = $reader.close; $null = $reader.dispose()
        }
        catch {

        }

        # Delete all the temp files
        if ($SqlCredentialPath.length -gt 0) {
            if ((Test-Path $SqlCredentialPath) -eq $true) {
                $null = cmd /c "del $SqlCredentialPath"
            }
        }

        if ($shellswitch -eq $false -and $safe -eq $true) {
            # Delete new schema files
            Write-Verbose "Removing automatically generated schema.ini."
            foreach ($file in $csv) {
                $directory = Split-Path $file
                $null = cmd /c "del $directory\schema.ini" | Out-Null
            }

            # If a shell switch occured, delete the temporary module file.
            if ((Test-Path "$env:TEMP\Import-DbaCsvToSql.psm1") -eq $true) {
                cmd /c "del $env:TEMP\Import-DbaCsvToSql.psm1" | Out-Null
            }

            # Move original schema.ini's back if they existed
            if ($movedschemainis.count -gt 0) {
                foreach ($item in $movedschemainis) {
                    Write-Verbose "Moving $($item.keys) back to $($item.values)."
                    $null = cmd /c "move $($item.keys) $($item.values)"
                }
            }
            Write-Output "[*] Finished at $(Get-Date)"
        }
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Import-CsvToSql
    }
}
tools\dbatools\functions\Import-DbaPfDataCollectorSetTemplate.ps1
function Import-DbaPfDataCollectorSetTemplate {
    <#
        .SYNOPSIS
            Imports a new Performance Monitor Data Collector Set Template either from the dbatools repository or a file you specify.

        .DESCRIPTION
            Imports a new Performance Monitor Data Collector Set Template either from the dbatools repository or a file you specify.
            When importing data collector sets from the local instance, Run As Admin is required.

            Note: The included counters will be added for all SQL instances on the machine by default.
            For specific instances in addition to the default, use -Instance.

            See https://msdn.microsoft.com/en-us/library/windows/desktop/aa371952 for more information

        .PARAMETER ComputerName
            The target computer. Defaults to localhost.

        .PARAMETER Credential
            Allows you to login to servers using alternative credentials. To use:

            $scred = Get-Credential, then pass $scred object to the -Credential parameter.

        .PARAMETER Path
            The path to the xml file or files.

        .PARAMETER Template
            From one or more of the templates from the dbatools repository. Press Tab to cycle through the available options.

        .PARAMETER RootPath
            Sets the base path where the subdirectories are created.

        .PARAMETER DisplayName
            Sets the display name of the data collector set.

        .PARAMETER SchedulesEnabled
            If this switch is enabled, sets a value that indicates whether the schedules are enabled.

        .PARAMETER Segment
            Sets a value that indicates whether PLA creates new logs if the maximum size or segment duration is reached before the data collector set is stopped.

        .PARAMETER SegmentMaxDuration
            Sets the duration that the data collector set can run before it begins writing to new log files.

        .PARAMETER SegmentMaxSize
            Sets the maximum size of any log file in the data collector set.

        .PARAMETER Subdirectory
            Sets a base subdirectory of the root path where the next instance of the data collector set will write its logs.

        .PARAMETER SubdirectoryFormat
            Sets flags that describe how to decorate the subdirectory name. PLA appends the decoration to the folder name. For example, if you specify plaMonthDayHour, PLA appends the current month, day, and hour values to the folder name. If the folder name is MyFile, the result could be MyFile110816.

        .PARAMETER SubdirectoryFormatPattern
            Sets a format pattern to use when decorating the folder name. Default is 'yyyyMMdd\-NNNNNN'.

        .PARAMETER Task
            Sets the name of a Task Scheduler job to start each time the data collector set stops, including between segments.

        .PARAMETER TaskRunAsSelf
            If this switch is enabled, sets a value that determines whether the task runs as the data collector set user or as the user specified in the task.

        .PARAMETER TaskArguments
            Sets the command-line arguments to pass to the Task Scheduler job specified in the IDataCollectorSet::Task property.
            See https://msdn.microsoft.com/en-us/library/windows/desktop/aa371992 for more information.

        .PARAMETER TaskUserTextArguments
            Sets the command-line arguments that are substituted for the {usertext} substitution variable in the IDataCollectorSet::TaskArguments property.
            See https://msdn.microsoft.com/en-us/library/windows/desktop/aa371993 for more information.

        .PARAMETER StopOnCompletion
            If this switch is enabled, sets a value that determines whether the data collector set stops when all the data collectors in the set are in a completed state.

        .PARAMETER Instance
            By default, the template will be applied to all instances. If you want to set specific ones in addition to the default, supply just the instance name.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Performance, DataCollector, PerfCounter
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Import-DbaPfDataCollectorSetTemplate

        .EXAMPLE
            Import-DbaPfDataCollectorSetTemplate -ComputerName sql2017 -Template 'Long Running Query'

            Creates a new data collector set named 'Long Running Query' from the dbatools repository on the SQL Server sql2017.

        .EXAMPLE
            Import-DbaPfDataCollectorSetTemplate -ComputerName sql2017 -Template 'Long Running Query' -DisplayName 'New Long running query' -Confirm

            Creates a new data collector set named "New Long Running Query" using the 'Long Running Query' template. Forces a confirmation if the template exists.

        .EXAMPLE
            Get-DbaPfDataCollectorSet -ComputerName sql2017 -Session db_ola_health | Remove-DbaPfDataCollectorSet
            Import-DbaPfDataCollectorSetTemplate -ComputerName sql2017 -Template db_ola_health | Start-DbaPfDataCollectorSet

            Imports a session if it exists, then recreates it using a template.

        .EXAMPLE
            Get-DbaPfDataCollectorSetTemplate | Out-GridView -PassThru | Import-DbaPfDataCollectorSetTemplate -ComputerName sql2017

            Allows you to select a Session template then import to an instance named sql2017.

        .EXAMPLE
            Import-DbaPfDataCollectorSetTemplate -ComputerName sql2017 -Template 'Long Running Query' -Instance SHAREPOINT

            Creates a new data collector set named 'Long Running Query' from the dbatools repository on the SQL Server sql2017 for both the default and the SHAREPOINT instance.

            If you'd like to remove counters for the default instance, use Remove-DbaPfDataCollectorCounter.
    #>
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Low")]
    param (
        [parameter(ValueFromPipeline)]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [string]$DisplayName,
        [switch]$SchedulesEnabled,
        [string]$RootPath,
        [switch]$Segment,
        [int]$SegmentMaxDuration,
        [int]$SegmentMaxSize,
        [string]$Subdirectory,
        [int]$SubdirectoryFormat = 3,
        [string]$SubdirectoryFormatPattern = 'yyyyMMdd\-NNNNNN',
        [string]$Task,
        [switch]$TaskRunAsSelf,
        [string]$TaskArguments,
        [string]$TaskUserTextArguments,
        [switch]$StopOnCompletion,
        [parameter(ValueFromPipelineByPropertyName)]
        [Alias("FullName")]
        [string[]]$Path,
        [string[]]$Template,
        [string[]]$Instance,
        [switch]$EnableException
    )
    begin {
        $metadata = Import-Clixml "$script:PSModuleRoot\bin\perfmontemplates\collectorsets.xml"

        $setscript = {
            $setname = $args[0]; $templatexml = $args[1]
            $collectorset = New-Object -ComObject Pla.DataCollectorSet
            $collectorset.SetXml($templatexml)
            $null = $collectorset.Commit($setname, $null, 0x0003) #add or modify.
            $null = $collectorset.Query($setname, $Null)
        }

        $instancescript = {
            $services = Get-Service -DisplayName *sql* | Select-Object -ExpandProperty DisplayName
            [regex]::matches($services, '(?<=\().+?(?=\))').Value | Where-Object { $PSItem -ne 'MSSQLSERVER' } | Select-Object -Unique
        }
    }
    process {
        if ((Test-Bound -ParameterName Path -Not) -and (Test-Bound -ParameterName Template -Not)) {
            Stop-Function -Message "You must specify Path or Template"
        }

        if (($Path.Count -gt 1 -or $Template.Count -gt 1) -and (Test-Bound -ParameterName Template)) {
            Stop-Function -Message "Name cannot be specified with multiple files or templates because the Session will already exist"
        }

        foreach ($computer in $ComputerName) {
            $null = Test-ElevationRequirement -ComputerName $computer -Continue

            foreach ($file in $template) {
                $templatepath = "$script:PSModuleRoot\bin\perfmontemplates\collectorsets\$file.xml"
                if ((Test-Path $templatepath)) {
                    $Path += $templatepath
                }
                else {
                    Stop-Function -Message "Invalid template ($templatepath does not exist)" -Continue
                }
            }

            foreach ($file in $Path) {

                if ((Test-Bound -ParameterName DisplayName -Not)) {
                    Set-Variable -Name DisplayName -Value (Get-ChildItem -Path $file).BaseName
                }

                $Name = $DisplayNameUnresolved = $DisplayName

                Write-Message -Level Verbose -Message "Processing $file for $computer"

                if ((Test-Bound -ParameterName RootPath -Not)) {
                    Set-Variable -Name RootName -Value "%systemdrive%\PerfLogs\Admin\$Name"
                }

                # Perform replace
                $temp = ([System.IO.Path]::GetTempPath()).TrimEnd("").TrimEnd("\")
                $tempfile = "$temp\import-dbatools-perftemplate.xml"

                try {
                    # Get content
                    $contents = Get-Content $file -ErrorAction Stop

                    # Replace content
                    $replacements = 'RootPath', 'DisplayName', 'SchedulesEnabled', 'Segment', 'SegmentMaxDuration', 'SegmentMaxSize', 'SubdirectoryFormat', 'SubdirectoryFormatPattern', 'Task', 'TaskRunAsSelf', 'TaskArguments', 'TaskUserTextArguments', 'StopOnCompletion', 'DisplayNameUnresolved'

                    foreach ($replacement in $replacements) {
                        $phrase = "<$replacement></$replacement>"
                        $value = (Get-Variable -Name $replacement).Value
                        if ($value -eq $false) {
                            $value = "0"
                        }
                        if ($value -eq $true) {
                            $value = "1"
                        }
                        $replacephrase = "<$replacement>$value</$replacement>"
                        $contents = $contents.Replace($phrase, $replacephrase)
                    }

                    # Set content
                    $null = Set-Content -Path $tempfile -Value $contents -Encoding Unicode
                    $xml = [xml](Get-Content $tempfile -ErrorAction Stop)
                    $plainxml = Get-Content $tempfile -ErrorAction Stop -Raw
                    $file = $tempfile
                }
                catch {
                    Stop-Function -Message "Failure" -ErrorRecord $_ -Target $file -Continue
                }
                if (-not $xml.DataCollectorSet) {
                    Stop-Function -Message "$file is not a valid Performance Monitor template document" -Continue
                }

                try {
                    Write-Message -Level Verbose -Message "Importing $file as $name "
                    Write-Message -Level Verbose -Message "Connecting to $computer using Invoke-Command"

                    if ($instance) {
                        $instances = $instance
                    }
                    else {
                        $instances = Invoke-Command2 -ComputerName $computer -Credential $Credential -ScriptBlock $instancescript -ErrorAction Stop -Raw
                    }

                    $scriptblock = {
                        try {
                            $results = Invoke-Command2 -ComputerName $computer -Credential $Credential -ScriptBlock $setscript -ArgumentList $Name, $plainxml -ErrorAction Stop
                            Write-Message -Level Verbose -Message " $results"
                        }
                        catch {
                            Stop-Function -Message "Failure starting $setname on $computer" -ErrorRecord $_ -Target $computer -Continue
                        }
                    }

                    if ((Get-DbaPfDataCollectorSet -ComputerName $computer -CollectorSet $Name)) {
                        if ($Pscmdlet.ShouldProcess($computer, "CollectorSet $Name already exists. Modify?")) {
                            Invoke-Command -Scriptblock $scriptblock
                            $output = Get-DbaPfDataCollectorSet -ComputerName $computer -CollectorSet $Name
                        }
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($computer, "Importing collector set $Name")) {
                            Invoke-Command -Scriptblock $scriptblock
                            $output = Get-DbaPfDataCollectorSet -ComputerName $computer -CollectorSet $Name
                        }
                    }

                    $newcollection = @()
                    foreach ($instance in $instances) {
                        $datacollector = Get-DbaPfDataCollectorSet -ComputerName $computer -CollectorSet $Name | Get-DbaPfDataCollector
                        $sqlcounters = $datacollector | Get-DbaPfDataCollectorCounter | Where-Object { $_.Name -match 'sql.*\:' -and $_.Name -notmatch 'sqlclient' } | Select-Object -ExpandProperty Name

                        foreach ($counter in $sqlcounters) {
                            $split = $counter.Split(":")
                            $firstpart = switch ($split[0]) {
                                'SQLServer' { 'MSSQL' }
                                '\SQLServer' { '\MSSQL' }
                                default { $split[0] }
                            }
                            $secondpart = $split[-1]
                            $finalcounter = "$firstpart`$$instance`:$secondpart"
                            $newcollection += $finalcounter
                        }
                    }

                    if ($newcollection.Count) {
                        if ($Pscmdlet.ShouldProcess($computer, "Adding $($newcollection.Count) additional counters")) {
                            $null = Add-DbaPfDataCollectorCounter -InputObject $datacollector -Counter $newcollection
                        }
                    }

                    Remove-Item $tempfile -ErrorAction SilentlyContinue
                    $output
                }
                catch {
                    Stop-Function -Message "Failure" -ErrorRecord $_ -Target $store -Continue
                }
            }
        }
    }
}
tools\dbatools\functions\Import-DbaRegisteredServer.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Import-DbaRegisteredServer {
    <#
        .SYNOPSIS
            Imports registered servers and registered server groups to SQL Server Central Management Server (CMS)

        .DESCRIPTION
            Imports registered servers and registered server groups to SQL Server Central Management Server (CMS)

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Group
            Imports to specific group

        .PARAMETER Path
            Optional path to exported reg server XML

        .PARAMETER InputObject
            Enables piping from Get-DbaRegisteredServer, Get-DbaRegisteredServerGroup, CSVs and other objects.

            If importing from CSV or other object, a column named ServerName is required. Optional columns include Name, Description and Group.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.

            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.

            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Chrissy LeMaire (@cl)
            Tags: RegisteredServer, CMS

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Import-DbaRegisteredServer

        .EXAMPLE
           Import-DbaRegisteredServer -SqlInstance sql2012 -Path C:\temp\corp-regservers.xml

           Imports C:\temp\corp-regservers.xml to the CMS on sql2012

        .EXAMPLE
           Import-DbaRegisteredServer -SqlInstance sql2008 -Group hr\Seattle -Path C:\temp\Seattle.xml

           Imports C:\temp\Seattle.xml to Seattle subgroup within the hr group on sql2008

        .EXAMPLE
           Get-DbaRegisteredServer -SqlInstance sql2008, sql2012 | Import-DbaRegisteredServer -SqlInstance sql2017

           Imports all registered servers from sql2008 and sql2012 to sql2017

        .EXAMPLE
           Get-DbaRegisteredServerGroup -SqlInstance sql2008 -Group hr\Seattle | Import-DbaRegisteredServer -SqlInstance sql2017 -Group Seattle

           Imports all registered servers from the hr\Seattle group on sql2008 to the Seattle group on sql2017

    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("FullName")]
        [string[]]$Path,
        [parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [object]$Group,
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            # Prep to import from file
            if ((Test-Bound -ParameterName Path)) {
                $InputObject += Get-ChildItem -Path $Path
            }
            if ((Test-Bound -ParameterName Group) -and (Test-Bound -Not -ParameterName Path)) {
                if ($Group -is [Microsoft.SqlServer.Management.RegisteredServers.ServerGroup]) {
                    $groupobject = $Group
                }
                else {
                    $groupobject = Get-DbaRegisteredServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Group $Group
                }
                if (-not $groupobject) {
                    Stop-Function -Message "Group $Group cannot be found on $instance" -Target $instance -Continue
                }
            }

            foreach ($object in $InputObject) {
                if ($object -is [Microsoft.SqlServer.Management.RegisteredServers.RegisteredServer]) {

                    $groupexists = Get-DbaRegisteredServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Group $object.Parent.Name
                    if (-not $groupexists) {
                        $groupexists = Add-DbaRegisteredServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Name $object.Parent.Name
                    }
                    Add-DbaRegisteredServer -SqlInstance $instance -SqlCredential $SqlCredential -Name $object.Name -ServerName $object.ServerName -Description $object.Description -Group $groupexists
                }
                elseif ($object -is [Microsoft.SqlServer.Management.RegisteredServers.ServerGroup]) {
                    foreach ($regserver in $object.RegisteredServers) {
                        $groupexists = Get-DbaRegisteredServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Group $regserver.Parent.Name
                        if (-not $groupexists) {
                            $groupexists = Add-DbaRegisteredServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Name $regserver.Parent.Name
                        }
                        Add-DbaRegisteredServer -SqlInstance $instance -SqlCredential $SqlCredential -Name $regserver.Name -ServerName $regserver.ServerName -Description $regserver.Description -Group $groupexists
                    }
                }
                elseif ($object -is [System.IO.FileInfo]) {
                    if ((Test-Bound -ParameterName Group)) {
                        if ($Group -is [Microsoft.SqlServer.Management.RegisteredServers.ServerGroup]) {
                            $reggroups = $Group
                        }
                        else {
                            $reggroups = Get-DbaRegisteredServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Group $Group
                        }
                    }
                    else {
                        $reggroups = Get-DbaRegisteredServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Id 1
                    }

                    foreach ($file in $object) {
                        if (-not (Test-Path -Path $file)) {
                            Stop-Function -Message "$file cannot be found" -Target $file -Continue
                        }

                        foreach ($reggroup in $reggroups) {
                            try {
                                Write-Message -Level Verbose -Message "Importing $file to $($reggroup.Name) on $instance"
                                $urnlist = $reggroup.RegisteredServers.Urn.Value
                                $reggroup.Import($file.FullName)
                                Get-DbaRegisteredServer -SqlInstance $instance -SqlCredential $SqlCredential | Where-Object { $_.Urn.Value -notin $urnlist }
                            }
                            catch {
                                Stop-Function -Message "Failure attempting to import $file to $instance" -ErrorRecord $_ -Continue
                            }
                        }
                    }
                }
                else {
                    if (-not $object.ServerName) {
                        Stop-Function -Message "Property 'ServerName' not found in InputObject. No servers added." -Continue
                    }
                    Add-DbaRegisteredServer -SqlInstance $instance -SqlCredential $SqlCredential -Name $object.Name -ServerName $object.ServerName -Description $object.Description -Group $groupobject
                }
            }
        }
    }
}
tools\dbatools\functions\Import-DbaSpConfigure.ps1
function Import-DbaSpConfigure {
    <#
        .SYNOPSIS
            Updates sp_configure settings on destination server.

        .DESCRIPTION
            Updates sp_configure settings on destination server.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER SqlInstance
            Specifies a SQL Server instance to set up sp_configure values on using a SQL file.

        .PARAMETER SqlCredential
            Use this SQL credential if you are setting up sp_configure values from a SQL file.

            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Path
            Specifies the path to a SQL script file holding sp_configure queries for each of the settings to be changed. Export-DbaSPConfigure creates a suitable file as its output.

        .PARAMETER Force
            If this switch is enabled, no version check between Source and Destination is performed. By default, the major and minor versions of Source and Destination must match when copying sp_configure settings.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .NOTES
            dbatools PowerShell module (https://dbatools.io, [email protected])
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            Import-DbaSpConfigure sqlserver sqlcluster $SourceSqlCredential $DestinationSqlCredential

            Imports the sp_configure settings from the source server sqlserver and sets them on the sqlcluster server
            using the SQL credentials stored in the variables

        .EXAMPLE
            Import-DbaSpConfigure -SqlInstance sqlserver -Path .\spconfig.sql -SqlCredential $SqlCredential

            Imports the sp_configure settings from the file .\spconfig.sql and sets them on the sqlcluster server
            using the SQL credential stored in the variables

        .OUTPUTS
            $true if success
            $false if failure

    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [Parameter(ParameterSetName = "ServerCopy")]
        [DbaInstanceParameter]$Source,
        [Parameter(ParameterSetName = "ServerCopy")]
        [DbaInstanceParameter]$Destination,
        [Parameter(ParameterSetName = "ServerCopy")]
        [PSCredential]$SourceSqlCredential,
        [Parameter(ParameterSetName = "ServerCopy")]
        [PSCredential]$DestinationSqlCredential,
        [Parameter(ParameterSetName = "FromFile")]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [Parameter(ParameterSetName = "FromFile")]
        [string]$Path,
        [Parameter(ParameterSetName = "FromFile")]
        [PSCredential]$SqlCredential,
        [switch]$Force

    )
    begin {

        if ($Path.length -eq 0) {
            $sourceserver = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
            $destserver = Connect-SqlInstance -SqlInstance $Destination -SqlCredential $DestinationSqlCredential

            $source = $sourceserver.DomainInstanceName
            $destination = $destserver.DomainInstanceName
        }
        else {
            $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
            if ((Test-Path $Path) -eq $false) {
                throw "File Not Found"
            }
        }

    }
    process {
        if ($Path.length -eq 0) {
            if ($Pscmdlet.ShouldProcess($destination, "Export sp_configure")) {
                $sqlfilename = Export-SqlSpConfigure $sourceserver
            }

            if ($sourceserver.versionMajor -ne $destserver.versionMajor -and $force -eq $false) {
                Write-Warning "Source SQL Server major version and Destination SQL Server major version must match for sp_configure migration. Use -Force to override this precaution or check the exported sql file, $sqlfilename, and run manually."
                return
            }

            If ($Pscmdlet.ShouldProcess($destination, "Execute sp_configure")) {
                $sourceserver.Configuration.ShowAdvancedOptions.ConfigValue = $true
                $sourceserver.Query("RECONFIGURE WITH OVERRIDE") | Out-Null
                $destserver.Configuration.ShowAdvancedOptions.ConfigValue = $true
                $destserver.Query("RECONFIGURE WITH OVERRIDE") | Out-Null

                $destprops = $destserver.Configuration.Properties

                foreach ($sourceprop in $sourceserver.Configuration.Properties) {
                    $displayname = $sourceprop.DisplayName

                    $destprop = $destprops | where-object { $_.Displayname -eq $displayname }
                    if ($null -ne $destprop) {
                        try {
                            $destprop.configvalue = $sourceprop.configvalue
                            $destserver.Query("RECONFIGURE WITH OVERRIDE") | Out-Null
                            Write-Output "updated $($destprop.displayname) to $($sourceprop.configvalue)."
                        }
                        catch {
                            Write-Error "Could not $($destprop.displayname) to $($sourceprop.configvalue). Feature may not be supported."
                        }
                    }
                }
                try {
                    $destserver.Configuration.Alter()
                }
                catch {
                    $needsrestart = $true
                }

                $sourceserver.Configuration.ShowAdvancedOptions.ConfigValue = $false
                $sourceserver.Query("RECONFIGURE WITH OVERRIDE") | Out-Null
                $destserver.Configuration.ShowAdvancedOptions.ConfigValue = $false
                $destserver.Query("RECONFIGURE WITH OVERRIDE") | Out-Null

                if ($needsrestart -eq $true) {
                    Write-Warning "Some configuration options will be updated once SQL Server is restarted."
                }
                else {
                    Write-Output "Configuration option has been updated."
                }
            }

            if ($Pscmdlet.ShouldProcess($destination, "Removing temp file")) {
                Remove-Item $sqlfilename -ErrorAction SilentlyContinue
            }

        }
        else {
            if ($Pscmdlet.ShouldProcess($destination, "Importing sp_configure from $Path")) {
                $server.Configuration.ShowAdvancedOptions.ConfigValue = $true
                $sql = Get-Content $Path
                foreach ($line in $sql) {
                    try {
                        $server.Query($line) | Out-Null
                        Write-Output "Successfully executed $line."
                    }
                    catch {
                        Write-Error "$line failed. Feature may not be supported."
                    }
                }
                $server.Configuration.ShowAdvancedOptions.ConfigValue = $false
                Write-Warning "Some configuration options will be updated once SQL Server is restarted."
            }
        }
    }
    end {
        if ($Path.length -gt 0) {
            $server.ConnectionContext.Disconnect()
        }
        else {
            $sourceserver.ConnectionContext.Disconnect()
            $destserver.ConnectionContext.Disconnect()
        }

        If ($Pscmdlet.ShouldProcess("console", "Showing finished message")) {
            Write-Output "SQL Server configuration options migration finished."
        }

        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Import-SqlSpConfigure
    }
}
tools\dbatools\functions\Import-DbaXESessionTemplate.ps1
function Import-DbaXESessionTemplate {
    <#
        .SYNOPSIS
            Imports a new XESession XML Template

        .DESCRIPTION
            Imports a new XESession XML Template either from the dbatools repository or a file you specify.

        .PARAMETER SqlInstance
            Target SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Name
            The Name of the session to create.

        .PARAMETER Path
            The path to the xml file or files for the session(s).

        .PARAMETER Template
            Specifies the name of one of the templates from the dbatools repository. Press tab to cycle through the provided templates.

        .PARAMETER TargetFilePath
            By default, files will be created in the default xel directory. Use TargetFilePath to change all instances of
            filename = "file.xel" to filename = "$TargetFilePath\file.xel". Only specify the directory, not the file itself.

            This path is relative to the destination directory

        .PARAMETER TargetFileMetadataPath
            By default, files will be created in the default xem directory. Use TargetFileMetadataPath to change all instances of
            filename = "file.xem" to filename = "$TargetFilePath\file.xem". Only specify the directory, not the file itself.

            This path is relative to the destination directory

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Import-DbaXESessionTemplate

        .EXAMPLE
            Import-DbaXESessionTemplate -SqlInstance sql2017 -Template db_query_wait_stats

            Creates a new XESession named db_query_wait_stats from the dbatools repository to the SQL Server sql2017.

        .EXAMPLE
            Import-DbaXESessionTemplate -SqlInstance sql2017 -Template db_query_wait_stats -Name "Query Wait Stats"

            Creates a new XESession named "Query Wait Stats" using the db_query_wait_stats template.

        .EXAMPLE
            Get-DbaXESession -SqlInstance sql2017 -Session db_ola_health | Remove-DbaXESession
            Import-DbaXESessionTemplate -SqlInstance sql2017 -Template db_ola_health | Start-DbaXESession

            Imports a session if it exists, then recreates it using a template.

        .EXAMPLE
            Get-DbaXESessionTemplate | Out-GridView -PassThru | Import-DbaXESessionTemplate -SqlInstance sql2017

            Allows you to select a Session template then import to an instance named sql2017.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string]$Name,
        [parameter(ValueFromPipelineByPropertyName)]
        [Alias("FullName")]
        [string[]]$Path,
        [string[]]$Template,
        [string]$TargetFilePath,
        [string]$TargetFileMetadataPath,
        [switch]$EnableException
    )
    begin {
        $metadata = Import-Clixml "$script:PSModuleRoot\bin\xetemplates-metadata.xml"
    }
    process {
        if ((Test-Bound -ParameterName Path -Not) -and (Test-Bound -ParameterName Template -Not)) {
            Stop-Function -Message "You must specify Path or Template."
        }

        if (($Path.Count -gt 1 -or $Template.Count -gt 1) -and (Test-Bound -ParameterName Template)) {
            Stop-Function -Message "Name cannot be specified with multiple files or templates because the Session will already exist."
        }

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 11
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $SqlConn = $server.ConnectionContext.SqlConnectionObject
            $SqlStoreConnection = New-Object Microsoft.SqlServer.Management.Sdk.Sfc.SqlStoreConnection $SqlConn
            $store = New-Object  Microsoft.SqlServer.Management.XEvent.XEStore $SqlStoreConnection

            foreach ($file in $template) {
                $templatepath = "$script:PSModuleRoot\bin\xetemplates\$file.xml"
                if ((Test-Path $templatepath)) {
                    $Path += $templatepath
                }
                else {
                    Stop-Function -Message "Invalid template ($templatepath does not exist)." -Continue
                }
            }

            foreach ($file in $Path) {

                if ((Test-Bound -Not -ParameterName TargetFilePath)) {
                    Write-Message -Level Verbose -Message "Importing $file to $instance"
                    try {
                        $xml = [xml](Get-Content $file -ErrorAction Stop)
                    }
                    catch {
                        Stop-Function -Message "Failure" -ErrorRecord $_ -Target $file -Continue
                    }
                }
                else {
                    Write-Message -Level Verbose -Message "TargetFilePath specified, changing all file locations in $file for $instance."
                    Write-Message -Level Verbose -Message "TargetFileMetadataPath specified, changing all metadata file locations in $file for $instance."

                    # Handle whatever people specify
                    $TargetFilePath = $TargetFilePath.TrimEnd("\")
                    $TargetFileMetadataPath = $TargetFileMetadataPath.TrimEnd("\")
                    $TargetFilePath = "$TargetFilePath\"
                    $TargetFileMetadataPath = "$TargetFileMetadataPath\"

                    # Perform replace
                    $xelphrase = 'name="filename" value="'
                    $xemphrase = 'name="metadatafile" value="'

                    try {
                        $basename = (Get-ChildItem $file).Basename
                        $contents = Get-Content $file -ErrorAction Stop
                        $contents = $contents.Replace($xelphrase, "$xelphrase$TargetFilePath")
                        $contents = $contents.Replace($xemphrase, "$xemphrase$TargetFileMetadataPath")
                        $temp = ([System.IO.Path]::GetTempPath()).TrimEnd("").TrimEnd("\")
                        $tempfile = "$temp\$basename"
                        $null = Set-Content -Path $tempfile -Value $contents -Encoding UTF8
                        $xml = [xml](Get-Content $tempfile -ErrorAction Stop)
                        $file = $tempfile
                    }
                    catch {
                        Stop-Function -Message "Failure" -ErrorRecord $_ -Target $file -Continue
                    }

                    Write-Message -Level Verbose -Message "$TargetFilePath does not exist on $server, creating now."
                    try {
                        if (-not (Test-DbaSqlPath -SqlInstance $server -Path $TargetFilePath)) {
                            $null = New-DbaSqlDirectory -SqlInstance $server -Path $TargetFilePath
                        }
                    }
                    catch {
                        Stop-Function -Message "Failure" -ErrorRecord $_ -Target $file -Continue
                    }
                }

                if (-not $xml.event_sessions) {
                    Stop-Function -Message "$file is not a valid XESession template document." -Continue
                }

                if ((Test-Bound -ParameterName Name -not)) {
                    $Name = (Get-ChildItem $file).BaseName
                }

                # This could be done better but not today
                $no2012 = ($metadata | Where-Object Compatibility -gt 2012).Name
                $no2014 = ($metadata | Where-Object Compatibility -gt 2014).Name

                if ($Name -in $no2012 -and $server.VersionMajor -eq 11) {
                    Stop-Function -Message "$Name is not supported in SQL Server 2012 ($server)" -Continue
                }

                if ($Name -in $no2014 -and $server.VersionMajor -eq 12) {
                    Stop-Function -Message "$Name is not supported in SQL Server 2014 ($server)" -Continue
                }

                if ((Get-DbaXESession -SqlInstance $server -Session $Name)) {
                    Stop-Function -Message "$Name already exists on $instance" -Continue
                }

                try {
                    Write-Message -Level Verbose -Message "Importing $file as $name "
                    $session = $store.CreateSessionFromTemplate($Name, $file)
                    $session.Create()
                    if ($file -eq $tempfile) {
                        Remove-Item $tempfile -ErrorAction SilentlyContinue
                    }
                    Get-DbaXESession -SqlInstance $server -Session $session.Name
                }
                catch {
                    Stop-Function -Message "Failure" -ErrorRecord $_ -Target $store -Continue
                }
            }
        }
    }
}
tools\dbatools\functions\Install-DbaFirstResponderKit.ps1
#ValidationTags#CodeStyle,Messaging,FlowControl,Pipeline#
function Install-DbaFirstResponderKit {
    <#
        .SYNOPSIS
            Installs or updates the First Responder Kit stored procedures.

        .DESCRIPTION
            Downloads, extracts and installs the First Responder Kit stored procedures:
            sp_Blitz, sp_BlitzWho, sp_BlitzFirst, sp_BlitzIndex, sp_BlitzCache and sp_BlitzTrace, etc.

            First Responder Kit links:
            http://FirstResponderKit.org
            https://github.com/BrentOzarULTD/SQL-Server-First-Responder-Kit

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies the database to instal the First Responder Kit stored procedures into

        .PARAMETER Branch
            Specifies an alternate branch of the First Responder Kit to install. (master or dev)

        .PARAMETER Confirm
            Prompts to confirm actions

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: BrentOzar, FRK, FirstResponderKit
            Author: Tara Kizer, Brent Ozar Unlimited (https://www.brentozar.com/)
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Install-DbaFirstResponderKit

        .EXAMPLE
            Install-DbaFirstResponderKit -SqlInstance server1 -Database master

            Logs into server1 with Windows authentication and then installs the FRK in the master database.

        .EXAMPLE
            Install-DbaFirstResponderKit -SqlInstance server1\instance1 -Database DBA

            Logs into server1\instance1 with Windows authentication and then installs the FRK in the DBA database.

        .EXAMPLE
            Install-DbaFirstResponderKit -SqlInstance server1\instance1 -Database master -SqlCredential $cred

            Logs into server1\instance1 with SQL authentication and then installs the FRK in the master database.

        .EXAMPLE
            Install-DbaFirstResponderKit -SqlInstance sql2016\standardrtm, sql2016\sqlexpress, sql2014

            Logs into sql2016\standardrtm, sql2016\sqlexpress and sql2014 with Windows authentication and then installs the FRK in the master database.

        .EXAMPLE
            $servers = "sql2016\standardrtm", "sql2016\sqlexpress", "sql2014"
            $servers | Install-DbaFirstResponderKit

            Logs into sql2016\standardrtm, sql2016\sqlexpress and sql2014 with Windows authentication and then installs the FRK in the master database.

        .EXAMPLE
            Install-DbaFirstResponderKit -SqlInstance sql2016 -Branch dev

            Installs the dev branch version of the FRK in the master database on sql2016 instance.
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [ValidateSet('master', 'dev')]
        [string]$Branch = "master",
        [object]$Database = "master",
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $url = "https://codeload.github.com/BrentOzarULTD/SQL-Server-First-Responder-Kit/zip/$Branch"

        $temp = ([System.IO.Path]::GetTempPath()).TrimEnd("\")
        $zipfile = "$temp\SQL-Server-First-Responder-Kit-$Branch.zip"
        $zipfolder = "$temp\SQL-Server-First-Responder-Kit-$Branch\"

        if ($zipfile | Test-Path) {
            Remove-Item -Path $zipfile -ErrorAction SilentlyContinue
        }

        if ($zipfolder | Test-Path) {
            Remove-Item -Path $zipfolder -Recurse -ErrorAction SilentlyContinue
        }

        $null = New-Item -ItemType Directory -Path $zipfolder -ErrorAction SilentlyContinue

        Write-Message -Level Verbose -Message "Downloading and unzipping the First Responder Kit zip file."

        try {
            $oldSslSettings = [System.Net.ServicePointManager]::SecurityProtocol
            [System.Net.ServicePointManager]::SecurityProtocol = "Tls12"
            try {
                $wc = New-Object System.Net.WebClient
                $wc.DownloadFile($url, $zipfile)
            }
            catch {
                # Try with default proxy and usersettings
                $wc = New-Object System.Net.WebClient
                $wc.Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
                $wc.DownloadFile($url, $zipfile)
            }
            [System.Net.ServicePointManager]::SecurityProtocol = $oldSslSettings

            # Unblock if there's a block
            Unblock-File $zipfile -ErrorAction SilentlyContinue

            Expand-Archive -Path $zipfile -DestinationPath $zipfolder -Force

            Remove-Item -Path $zipfile
        }
        catch {
            Stop-Function -Message "Couldn't download the First Responder Kit. Download and install manually from https://github.com/BrentOzarULTD/SQL-Server-First-Responder-Kit/archive/$Branch.zip." -ErrorRecord $_
            return
        }
    }

    process {
        if (Test-FunctionInterrupt) {
            return
        }

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure." -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            Write-Message -Level Verbose -Message "Starting installing/updating the First Responder Kit stored procedures in $database on $instance."
            $allprocedures_query = "select name from sys.procedures where is_ms_shipped = 0"
            $allprocedures = ($server.Query($allprocedures_query, $Database)).Name
            # Install/Update each FRK stored procedure
            foreach ($script in (Get-ChildItem $zipfolder -Recurse -Filter "sp_*.sql")) {
                $scriptname = $script.Name
                $scriptError = $false
                if ($scriptname -ne "sp_BlitzRS.sql") {

                    if ($scriptname -eq "sp_BlitzQueryStore.sql") {
                        if ($server.VersionMajor -lt 13) { continue }
                    }
                    if ($Pscmdlet.ShouldProcess($instance, "installing/updating $scriptname in $database.")) {
                        try {
                            Invoke-DbaSqlQuery -SqlInstance $server -Database $Database -File $script.FullName -EnableException -Verbose:$false
                        } catch {
                            Write-Message -Level Warning -Message "Could not execute at least one portion of $scriptname in $Database on $instance." -ErrorRecord $_
                            $scriptError = $true
                        }
                        $baseres = @{
                            ComputerName = $server.ComputerName
                            InstanceName = $server.ServiceName
                            SqlInstance  = $server.DomainInstanceName
                            Database     = $Database
                            Name         = $script.BaseName
                        }
                        if ($scriptError) {
                            $baseres['Status'] = 'Error'
                        } elseif ($script.BaseName -in $allprocedures) {
                            $baseres['Status'] = 'Updated'
                        }
                        else {
                            $baseres['Status'] = 'Installed'
                        }
                        [PSCustomObject]$baseres
                    }
                }
            }
            Write-Message -Level Verbose -Message "Finished installing/updating the First Responder Kit stored procedures in $database on $instance."
        }
    }
}
tools\dbatools\functions\Install-DbaMaintenanceSolution.ps1
function Install-DbaMaintenanceSolution {
    <#
        .SYNOPSIS
            Download and Install SQL Server Maintenance Solution created by Ola Hallengren (https://ola.hallengren.com)
        .DESCRIPTION
            This script will download and install the latest version of SQL Server Maintenance Solution created by Ola Hallengren

        .PARAMETER SqlInstance
            The target SQL Server instance onto which the Maintenance Solution will be installed.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database where Ola Hallengren's solution will be installed. Defaults to master.

        .PARAMETER BackupLocation
            Location of the backup root directory. If this is not supplied, the default backup directory will be used.

        .PARAMETER CleanupTime
            Time in hours, after which backup files are deleted.

        .PARAMETER OutputFileDirectory
            Specify the output file directory where the Maintenance Solution will write to.

        .PARAMETER ReplaceExisting
            If this switch is enabled, objects already present in the target database will be dropped and recreated.

        .PARAMETER LogToTable
            If this switch is enabled, the Maintenance Solution will be configured to log commands to a table.

        .PARAMETER Solution
            Specifies which portion of the Maintenance solution to install. Valid values are All (full solution), Backup, IntegrityCheck and IndexOptimize.

        .PARAMETER InstallJobs
            If this switch is enabled, the corresponding SQL Agent Jobs will be created.

       .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Ola, Maintenance
            Author: Viorel Ciucu, [email protected], cviorel.com

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            http://dbatools.io/Install-DbaMaintenanceSolution

        .EXAMPLE
            Install-DbaMaintenanceSolution -SqlInstance RES14224 -Database DBA -CleanupTime 72

            Installs Ola Hallengren's Solution objects on RES14224 in the DBA database.
            Backups will default to the default Backup Directory.
            If the Maintenance Solution already exists, the script will be halted.

        .EXAMPLE
            Install-DbaMaintenanceSolution -SqlInstance RES14224 -Database DBA -BackupLocation "Z:\SQLBackup" -CleanupTime 72

            This will create the Ola Hallengren's Solution objects. Existing objects are not affected in any way.

        .EXAMPLE
            Install-DbaMaintenanceSolution -SqlInstance RES14224 -Database DBA -BackupLocation "Z:\SQLBackup" -CleanupTime 72 -ReplaceExisting

            This will drop and then recreate the Ola Hallengren's Solution objects
            The cleanup script will drop and recreate:
                - TABLE [dbo].[CommandLog]
                - STORED PROCEDURE [dbo].[CommandExecute]
                - STORED PROCEDURE [dbo].[DatabaseBackup]
                - STORED PROCEDURE [dbo].[DatabaseIntegrityCheck]
                - STORED PROCEDURE [dbo].[IndexOptimize]

            The following SQL Agent jobs will be deleted:
                - 'Output File Cleanup'
                - 'IndexOptimize - USER_DATABASES'
                - 'sp_delete_backuphistory'
                - 'DatabaseBackup - USER_DATABASES - LOG'
                - 'DatabaseBackup - SYSTEM_DATABASES - FULL'
                - 'DatabaseBackup - USER_DATABASES - FULL'
                - 'sp_purge_jobhistory'
                - 'DatabaseIntegrityCheck - SYSTEM_DATABASES'
                - 'CommandLog Cleanup'
                - 'DatabaseIntegrityCheck - USER_DATABASES'
                - 'DatabaseBackup - USER_DATABASES - DIFF'
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias('ServerInstance', 'SqlServer')]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object]$Database = "master",
        [string]$BackupLocation,
        [int]$CleanupTime,
        [string]$OutputFileDirectory,
        [switch]$ReplaceExisting,
        [switch]$LogToTable,
        [ValidateSet('All', 'Backup', 'IntegrityCheck', 'IndexOptimize')]
        [string]$Solution = 'All',
        [switch]$InstallJobs,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -NonPooled
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ((Test-Bound -Parameter ReplaceExisting -Not)) {
                $procs = Get-DbaSqlModule -SqlInstance $server -Database $Database | Where-Object Name -in 'CommandExecute', 'DatabaseBackup', 'DatabaseIntegrityCheck', 'IndexOptimize'
                $table = Get-DbaTable -SqlInstance $server -Database $Database -Table CommandLog -IncludeSystemDBs  | Where-Object Database -eq $Database

                if ($null -ne $procs -or $null -ne $table) {
                    Stop-Function -Message "The Maintenance Solution already exists in $Database on $instance. Use -ReplaceExisting to automatically drop and recreate."
                    return
                }
            }

            if ((Test-Bound -Parameter BackupLocation -Not)) {
                $BackupLocation = (Get-DbaDefaultPath -SqlInstance $server).Backup
            }

            Write-Message -Level Output -Message "Ola Hallengren's solution will be installed on database $Database."

            $db = $server.Databases[$Database]

            if ($InstallJobs -and $Solution -ne 'All') {
                Stop-Function -Message "To create SQL Agent jobs you need to use '-Solution All' and '-InstallJobs Create'."
                return
            }

            if ($ReplaceExisting -eq $true) {
                Write-Message -Level Verbose -Message "If Ola Hallengren's scripts are found, we will drop and recreate them!"
            }

            if ($CleanupTime -ne 0 -and $InstallJobs -eq $false) {
                Write-Message -Level Output -Message "CleanupTime $CleanupTime value will be ignored because you chose not to create SQL Agent Jobs."
            }

            # Required
            $required = @('CommandExecute.sql')

            if ($LogToTable) {
                $required += 'CommandLog.sql'
            }

            if ($Solution -match 'Backup') {
                $required += 'DatabaseBackup.sql'
            }

            if ($Solution -match 'IntegrityCheck') {
                $required += 'DatabaseIntegrityCheck.sql'
            }

            if ($Solution -match 'IndexOptimize') {
                $required += 'IndexOptimize.sql'
            }

            if ($Solution -match 'All') {
                $required += 'MaintenanceSolution.sql'
            }

            $temp = ([System.IO.Path]::GetTempPath()).TrimEnd("\")
            $zipfile = "$temp\ola.zip"

            # Start the download
            $url = "https://github.com/olahallengren/sql-server-maintenance-solution/archive/master.zip"
            try {
                Start-BitsTransfer -Source $url -DisplayName 'Downloading SQL Server Maintenance Solution - https://ola.hallengren.com' -Destination $zipfile -ErrorAction Stop
            }
            catch {
                Stop-Function -Message "You need to re-run the script, there is a problem with the proxy or the download link has changed." -ErrorRecord $_
            }

            # Unblock if there's a block
            Unblock-File $zipfile -ErrorAction SilentlyContinue

            $path = "$temp\sql-server-maintenance-solution-master"

            # We don't like default parameters messed with so we start clean
            if ((Test-Path $path)) {
                Remove-Item -Path $temp\sql-server-maintenance-solution-master -Recurse -Force -ErrorAction SilentlyContinue
            }

            # internal if it doesn't exist
            Expand-Archive -Path $zipfile -DestinationPath $temp -Force
            Remove-Item -Path $zipfile

            $listOfFiles = Get-ChildItem -Filter "*.sql" -Path $path | Select-Object -ExpandProperty FullName

            # In which database we install
            if ($Database -ne 'master') {
                $findDB = 'USE [master]'
                $replaceDB = 'USE [' + $Database + ']'
                foreach ($file in $listOfFiles) {
                    (Get-Content -Path $file -Raw).Replace($findDB, $replaceDB) | Set-Content -Path $file
                }
            }

            # Backup location
            if ($BackupLocation) {
                $findBKP = 'SET @BackupDirectory     = NULL'
                $replaceBKP = 'SET @BackupDirectory     = N''' + $BackupLocation + ''''
                foreach ($file in $listOfFiles) {
                    (Get-Content -Path $file -Raw).Replace($findBKP, $replaceBKP) | Set-Content -Path $file
                }
            }

            # CleanupTime
            if ($CleanupTime -ne 0) {
                $findCleanupTime = 'SET @CleanupTime         = NULL'
                $replaceCleanupTime = 'SET @CleanupTime         = ' + $CleanupTime
                foreach ($file in $listOfFiles) {
                    (Get-Content -Path $file -Raw).Replace($findCleanupTime, $replaceCleanupTime) | Set-Content -Path $file
                }
            }

            # OutputFileDirectory
            if ($OutputFileDirectory.Length -gt 0) {
                $findOutputFileDirectory = 'SET @OutputFileDirectory = NULL'
                $replaceOutputFileDirectory = 'SET @OutputFileDirectory = N''' + $OutputFileDirectory + ''''
                foreach ($file in $listOfFiles) {
                    (Get-Content -Path $file -Raw).Replace($findOutputFileDirectory, $replaceOutputFileDirectory) | Set-Content -Path $file
                }

            }

            # LogToTable
            if (!$LogToTable) {
                $findLogToTable = "SET @LogToTable          = 'Y'"
                $replaceLogToTable = "SET @LogToTable          = 'N'"
                foreach ($file in $listOfFiles) {
                    (Get-Content -Path $file -Raw).Replace($findLogToTable, $replaceLogToTable) | Set-Content -Path $file
                }
            }

            # Create Jobs
            if ($InstallJobs -eq $false) {
                $findCreateJobs = "SET @CreateJobs          = 'Y'"
                $replaceCreateJobs = "SET @CreateJobs          = 'N'"
                foreach ($file in $listOfFiles) {
                    (Get-Content -Path $file -Raw).Replace($findCreateJobs, $replaceCreateJobs) | Set-Content -Path $file
                }
            }

            $CleanupQuery = $null
            if ($ReplaceExisting) {
                [string]$CleanupQuery = $("
                            IF OBJECT_ID('[dbo].[CommandLog]', 'U') IS NOT NULL
                                DROP TABLE [dbo].[CommandLog];
                            IF OBJECT_ID('[dbo].[CommandExecute]', 'P') IS NOT NULL
                                DROP PROCEDURE [dbo].[CommandExecute];
                            IF OBJECT_ID('[dbo].[DatabaseBackup]', 'P') IS NOT NULL
                                DROP PROCEDURE [dbo].[DatabaseBackup];
                            IF OBJECT_ID('[dbo].[DatabaseIntegrityCheck]', 'P') IS NOT NULL
                                DROP PROCEDURE [dbo].[DatabaseIntegrityCheck];
                            IF OBJECT_ID('[dbo].[IndexOptimize]', 'P') IS NOT NULL
                                DROP PROCEDURE [dbo].[IndexOptimize];
                            ")

                Write-Message -Level Output -Message "Dropping objects created by Ola's Maintenance Solution"
                $null = $db.Query($CleanupQuery)

                # Remove Ola's Jobs
                if ($InstallJobs -and $ReplaceExisting) {
                    Write-Message -Level Output -Message "Removing existing SQL Agent Jobs created by Ola's Maintenance Solution."
                    $jobs = Get-DbaAgentJob -SqlInstance $server | Where-Object Description -match "hallengren"
                    if ($jobs) {
                        $jobs | ForEach-Object { Remove-DbaAgentJob -SqlInstance $instance -Job $_.name }
                    }
                }
            }

            try {
                Write-Message -Level Output -Message "Installing on server $SqlInstance, database $Database."

                foreach ($file in $listOfFiles) {
                    $shortFileName = Split-Path $file -Leaf
                    if ($required.Contains($shortFileName)) {
                        Write-Message -Level Output -Message "Installing $file."
                        $sql = [IO.File]::ReadAllText($file)
                        try {
                            foreach ($query in ($sql -Split "\nGO\b")) {
                                $null = $db.Query($query)
                            }
                        }
                        catch {
                            Stop-Function -Message "Could not execute $file in $Database on $instance." -ErrorRecord $_ -Target $db -Continue
                        }
                    }
                }
            }
            catch {
                Stop-Function -Message "Could not execute $file in $Database on $instance." -ErrorRecord $_ -Target $db -Continue
            }
        }

        if ((Test-Path $path)) {
            Remove-Item -Path $temp\sql-server-maintenance-solution-master -Recurse -Force -ErrorAction SilentlyContinue
        }

        # Only here due to need for non-pooled connection in this command
        try {
            $server.ConnectionContext.Disconnect()
        }
        catch {
        }

        Write-Message -Level Output -Message "Installation complete."
    }
}
tools\dbatools\functions\Install-DbaWatchUpdate.ps1
function Install-DbaWatchUpdate {
    <#
        .SYNOPSIS
            Adds the scheduled task to support Watch-DbaUpdate.

        .DESCRIPTION
            Adds the scheduled task to support Watch-DbaUpdate.

        .PARAMETER TaskName
            Provide custom name for the Scheduled Task

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Module
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Install-DbaWatchUpdate

        .EXAMPLE
            Install-DbaWatchUpdate

            Adds the scheduled task needed by Watch-DbaUpdate

        .EXAMPLE
            Install-DbaWatchUpdate -TaskName MyScheduledTask

            Will create the scheduled task as the name MyScheduledTask
    #>
    [cmdletbinding(SupportsShouldProcess)]
    param(
        [string]$TaskName = 'dbatools version check',
        [switch]$EnableException
    )
    process {
        if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Validate Version of OS") ) {
            if (([Environment]::OSVersion).Version.Major -lt 10) {
                Stop-Function -Message "This command only supports Windows 10 and above"
            }
        }
        $script = {
            try {
                # create a task, check every 3 hours
                $action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument '-NoProfile -NoLogo -NonInteractive -WindowStyle Hidden Watch-DbaUpdate'
                $trigger = New-ScheduledTaskTrigger -Once -At (Get-Date).Date -RepetitionInterval (New-TimeSpan -Hours 1)
                $principal = New-ScheduledTaskPrincipal -LogonType S4U -UserId (whoami)
                $settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit ([timespan]::Zero) -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -RunOnlyIfNetworkAvailable -DontStopOnIdleEnd
                $task = Register-ScheduledTask -Principal $principal -TaskName 'dbatools version check' -Action $action -Trigger $trigger -Settings $settings -ErrorAction Stop
            }
            catch {
                # keep moving
            }
        }

        if ($null -eq (Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue)) {
            # Needs admin creds to setup the kind of PowerShell window that doesn't appear for a millisecond
            # which is a millisecond too long
            if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Validate running in RunAs mode")) {
                if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
                    Write-Message -Level Warning -Message "This command has to run using RunAs mode (privileged) to create the Scheduled Task. This will only happen once."
                    if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Starting process in RunAs mode") ) {
                        Start-Process powershell -Verb runAs -ArgumentList Install-DbaWatchUpdate -Wait
                    }
                }

            }
            if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Creating scheduled task $TaskName")) {
                try {
                    Invoke-Command -ScriptBlock $script -ErrorAction Stop

                    if ((Get-Location).Path -ne "$env:WINDIR\system32") {
                        Write-Message -Level Output -Message "Scheduled Task [$TaskName] created! A notification should appear momentarily. Here's something cute to look at in the interim."
                        Show-Notification -Title "dbatools wants you" -Text "come hang out at dbatools.io/slack"
                    }
                }
                catch {
                    Stop-Function -Message "Could not create scheduled task $TaskName" -Target $env:COMPUTERNAME -ErrorRecord $_
                }
            }
            if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Checking scheduled task was created")) {
                # double check
                if ($null -eq (Get-ScheduledTask -TaskName "dbatools version check" -ErrorAction SilentlyContinue)) {
                    Write-Message -Level Warning -Message "Scheduled Task was not created."
                }
            }
        }
        else {
            Write-Message -Level Output -Message "Scheduled Task $TaskName is already installed on this machine."
        }
    }
}
tools\dbatools\functions\Install-DbaWhoIsActive.ps1
function Install-DbaWhoIsActive {
    <#
        .SYNOPSIS
            Automatically installs or updates sp_WhoisActive by Adam Machanic.

        .DESCRIPTION
            This command downloads, extracts and installs sp_WhoisActive with Adam's permission. To read more about sp_WhoisActive, please visit http://whoisactive.com and http://sqlblog.com/blogs/adam_machanic/archive/tags/who+is+active/default.aspx

            Please consider donating to Adam if you find this stored procedure helpful: http://tinyurl.com/WhoIsActiveDonate

            Note that you will be prompted a bunch of times to confirm an action.

        .PARAMETER SqlInstance
            The SQL Server instance. Server version must be SQL Server version 2005 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database to install sp_WhoisActive into. This parameter is mandatory when executing this command unattended.

        .PARAMETER LocalFile
            Specifies the path to a local file to install sp_WhoisActive from. This can be either the zipfile as distributed by the website or the expanded SQL script. If this parameter is not specified, the latest version will be downloaded and installed from https://whoisactive.com/

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER Force
            If this switch is enabled, the sp_WhoisActive will be downloaded from the internet even if previously cached.

        .EXAMPLE
            Install-DbaWhoIsActive -SqlInstance sqlserver2014a -Database master

            Downloads sp_WhoisActive from the internet and installs to sqlserver2014a's master database. Connects to SQL Server using Windows Authentication.

        .EXAMPLE
            Install-DbaWhoIsActive -SqlInstance sqlserver2014a -SqlCredential $cred

            Pops up a dialog box asking which database on sqlserver2014a you want to install the procedure into. Connects to SQL Server using SQL Authentication.

        .EXAMPLE
            Install-DbaWhoIsActive -SqlInstance sqlserver2014a -Database master -LocalFile c:\SQLAdmin\whoisactive_install.sql

            Installs sp_WhoisActive to sqlserver2014a's master database from the local file whoisactive_install.sql

        .EXAMPLE
            $instances = Get-DbaRegisteredServer sqlserver
            Install-DbaWhoIsActive -SqlInstance $instances -Database master

        .NOTES
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Install-DbaWhoIsActive
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PsCredential]$SqlCredential,
        [parameter(Mandatory = $false)]
        [ValidateScript( { Test-Path -Path $_ -PathType Leaf })]
        [string]$LocalFile,
        [object]$Database,
        [switch][Alias('Silent')]
        $EnableException,
        [switch]$Force
    )

    begin {
        $DbatoolsData = Get-DbaConfigValue -FullName "Path.DbatoolsData"
        $temp = ([System.IO.Path]::GetTempPath()).TrimEnd("\")
        $zipfile = "$temp\spwhoisactive.zip"

        if ($LocalFile -eq $null -or $LocalFile.Length -eq 0) {
            $baseUrl = "http://whoisactive.com/downloads"
            $latest = ((Invoke-WebRequest -UseBasicParsing -uri http://whoisactive.com/downloads).Links | where-object { $PSItem.href -match "who_is_active" } | Select-Object href -First 1).href
            $LocalCachedCopy = Join-Path -Path $DbatoolsData -ChildPath $latest;

            if ((Test-Path -Path $LocalCachedCopy -PathType Leaf) -and (-not $Force)) {
                Write-Message -Level Verbose -Message "Locally-cached copy exists, skipping download."
                if ($PSCmdlet.ShouldProcess($env:computername, "Copying sp_WhoisActive from local cache for installation")) {
                    Copy-Item -Path $LocalCachedCopy -Destination $zipfile;
                }
            }
            else {
                if ($PSCmdlet.ShouldProcess($env:computername, "Downloading sp_WhoisActive")) {
                    try {
                        Write-Message -Level Verbose -Message "Downloading sp_WhoisActive zip file, unzipping and installing."
                        $url = $baseUrl + "/" + $latest
                        try {
                            Invoke-WebRequest $url -OutFile $zipfile -ErrorAction Stop -UseBasicParsing
                            Copy-Item -Path $zipfile -Destination $LocalCachedCopy
                        }
                        catch {
                            #try with default proxy and usersettings
                            (New-Object System.Net.WebClient).Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
                            Invoke-WebRequest $url -OutFile $zipfile -ErrorAction Stop -UseBasicParsing
                        }
                    }
                    catch {
                        Stop-Function -Message "Couldn't download sp_WhoisActive. Please download and install manually from $url." -ErrorRecord $_
                        return
                    }
                }
            }
        }
        else {
            # Look local
            if ($PSCmdlet.ShouldProcess($env:computername, "Copying local file to temp directory")) {

                if ($LocalFile.EndsWith("zip")) {
                    Copy-Item -Path $LocalFile -Destination $zipfile -Force
                }
                else {
                    Copy-Item -Path $LocalFile -Destination (Join-Path -path $temp -childpath "whoisactivelocal.sql")
                }
            }
        }
        if ($LocalFile -eq $null -or $LocalFile.Length -eq 0 -or $LocalFile.EndsWith("zip")) {
            # Unpack
            # Unblock if there's a block
            if ($PSCmdlet.ShouldProcess($env:computername, "Unpacking zipfile")) {

                Unblock-File $zipfile -ErrorAction SilentlyContinue

                if (Get-Command -ErrorAction SilentlyContinue -Name "Expand-Archive") {
                    try {
                        Expand-Archive -Path $zipfile -DestinationPath $temp -Force
                    }
                    catch {
                        Stop-Function -Message "Unable to extract $zipfile. Archive may not be valid." -ErrorRecord $_
                        return
                    }
                }
                else {
                    # Keep it backwards compatible
                    $shell = New-Object -ComObject Shell.Application
                    $zipPackage = $shell.NameSpace($zipfile)
                    $destinationFolder = $shell.NameSpace($temp)
                    Get-ChildItem "$temp\who*active*.sql" | Remove-Item
                    $destinationFolder.CopyHere($zipPackage.Items())
                }
                Remove-Item -Path $zipfile
            }
            $sqlfile = (Get-ChildItem "$temp\who*active*.sql" -ErrorAction SilentlyContinue | Select-Object -First 1).FullName
        }
        else {
            $sqlfile = $LocalFile
        }

        if ($PSCmdlet.ShouldProcess($env:computername, "Reading SQL file into memory")) {
            Write-Message -Level Verbose -Message "Using $sqlfile."

            $sql = [IO.File]::ReadAllText($sqlfile)
            $sql = $sql -replace 'USE master', ''
            $batches = $sql -split "GO\r\n"
        }
    }

    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if (-not $Database) {
                if ($PSCmdlet.ShouldProcess($instance, "Prompting with GUI list of databases")) {
                    $Database = Show-DbaDatabaseList -SqlInstance $server -Title "Install sp_WhoisActive" -Header "To deploy sp_WhoisActive, select a database or hit cancel to quit." -DefaultDb "master"

                    if (-not $Database) {
                        Stop-Function -Message "You must select a database to install the procedure." -Target $Database
                        return
                    }

                    if ($Database -ne 'master') {
                        Write-Message -Level Warning -Message "You have selected a database other than master. When you run Invoke-DbaWhoIsActive in the future, you must specify -Database $Database."
                    }
                }
            }
            if ($PSCmdlet.ShouldProcess($instance, "Installing sp_WhoisActive")) {
                try {
                    $ProcedureExists_Query = "select COUNT(*) [proc_count] from sys.procedures where is_ms_shipped = 0 and name like '%sp_WhoisActive%'"

                    if ($server.Databases[$Database]) {
                        $ProcedureExists = ($server.Query($ProcedureExists_Query, $Database)).proc_count
                        foreach ($batch in $batches) {
                            try {
                                $null = $server.databases[$Database].ExecuteNonQuery($batch)
                            }
                            catch {
                                Stop-Function -Message "Failed to install stored procedure." -ErrorRecord $_ -Continue -Target $instance
                            }
                        }

                        if ($ProcedureExists -gt 0) {
                            $status = 'Updated'
                        }
                        else {
                            $status = 'Installed'
                        }
                        [PSCustomObject]@{
                            ComputerName = $server.ComputerName
                            InstanceName = $server.ServiceName
                            SqlInstance  = $server.DomainInstanceName
                            Database     = $Database
                            Name         = 'sp_WhoisActive'
                            Status       = $status
                        }
                    }
                    else {
                        Stop-Function -Message "Failed to find database $Database on $instance or $Database is not writeable." -ErrorRecord $_ -Continue -Target $instance
                    }

                }
                catch {
                    Stop-Function -Message "Failed to install stored procedure." -ErrorRecord $_ -Continue -Target $instance
                }

            }
        }
    }
    end {
        if ($PSCmdlet.ShouldProcess($env:computername, "Post-install cleanup")) {
            Get-Item $sqlfile | Remove-Item
        }
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Install-SqlWhoIsActive
    }
}
tools\dbatools\functions\Invoke-DbaAdvancedRestore.ps1
function Invoke-DbaAdvancedRestore {
    <#
        .SYNOPSIS
            Allows the restore of modified BackupHistory Objects
            For 90% of users Restore-DbaDatabase should be your point of access to this function. The other 10% use it at their own risk

        .DESCRIPTION
            This is the final piece in the Restore-DbaDatabase Stack. Usually a BackupHistory object will arrive here from Restore-DbaDatabse via the following pipeline:
            Get-DbaBackupInformation  | Select-DbaBackupInformation | Format-DbaBackupInformation | Test-DbaBackupInformation | Invoke-DbaAdvancedRestore

            We have exposed these functions publicly to allow advanced users to perform operations that we don't support, or won't add as they would make things too complex for the majority of our users

            For example if you wanted to do some very complex redirection during a migration, then doing the rewrite of destinations may be better done with your own custom scripts rather than via Format-DbaBackupInformation

            We would recommend ALWAYS pushing your input through Test-DbaBackupInformation just to make sure that it makes sense to us.

        .PARAMETER BackupHistory
            The BackupHistory object to be restored.
            Can be passed in on the pipeline

        .PARAMETER SqlInstance
            The SqlInstance to which the backups should be restored

        .PARAMETER SqlCredential
            SqlCredential to be used to connect to the target SqlInstance

        .PARAMETER OutputScriptOnly
            If set, the restore will not be performed, but the T-SQL scripts to perform it will be returned

        .PARAMETER VerifyOnly
            If set, performs a Verify of the backups rather than a full restore

        .PARAMETER RestoreTime
            Point in Time to which the database should be restored.

            This should be the same value or earlier, as used in the previous pipeline stages

        .PARAMETER StandbyDirectory
            A folder path where a standby file should be created to put the recovered databases in a standby mode

        .PARAMETER NoRecovery
            Leave the database in a restoring state so that further restore may be made

        .PARAMETER MaxTransferSize
            Parameter to set the unit of transfer. Values must be a multiple by 64kb

        .PARAMETER Blocksize
            Specifies the block size to use. Must be one of 0.5kb,1kb,2kb,4kb,8kb,16kb,32kb or 64kb
            Can be specified in bytes
            Refer to https://msdn.microsoft.com/en-us/library/ms178615.aspx for more detail

        .PARAMETER BufferCount
            Number of I/O buffers to use to perform the operation.
            Refer to https://msdn.microsoft.com/en-us/library/ms178615.aspx for more detail

        .PARAMETER Continue
            Indicates that the restore is continuing a restore, so target database must be in Recovering or Standby states

        .PARAMETER AzureCredential
            AzureCredential required to connect to blob storage holding the backups

        .PARAMETER WithReplace
            Indicated that if the database already exists it should be replaced

        .PARAMETER KeepCDC
            Indicates whether CDC information should be restored as part of the database

        .PARAMETER PageRestore
            The output from Get-DbaSuspect page containing the suspect pages to be restored.

        .PARAMETER WhatIf
            Shows what would happen if the cmdlet runs. The cmdlet is not run.

        .PARAMETER Confirm
            Prompts you for confirmation before running the cmdlet.

        .PARAMETER EnableException
            Replaces user friendly yellow warnings with bloody red exceptions of doom!
            Use this if you want the function to throw terminating errors you want to catch.

        .NOTES
            Tags: Restore, Backup
            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Invoke-DbaAdvancedRestore

        .EXAMPLE
            $BackupHistory | Invoke-DbaAdvancedRestore -SqlInstance MyInstance

            Will restore all the backups in the BackupHistory object according to the transformations it contains

        .EXAMPLE
            $BackupHistory | Invoke-DbaAdvancedRestore -SqlInstance MyInstance -OutputScriptOnly
            $BackupHistory | Invoke-DbaAdvancedRestore -SqlInstance MyInstance

            First generates just the T-SQL restore scripts so they can be sanity checked, and then if they are good perform the full restore. By reusing the BackupHistory object there is no need to rescan all the backup files again
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Object[]]$BackupHistory,
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [switch]$OutputScriptOnly,
        [switch]$VerifyOnly,
        [datetime]$RestoreTime = (Get-Date).AddDays(2),
        [string]$StandbyDirectory,
        [switch]$NoRecovery,
        [int]$MaxTransferSize,
        [int]$BlockSize,
        [int]$BufferCount,
        [switch]$Continue,
        [string]$AzureCredential,
        [switch]$WithReplace,
        [switch]$KeepCDC,
        [object[]]$PageRestore,
        [switch]$EnableException
    )
    begin {
        try {
            $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            return
        }
        if ($KeepCDC -and ($NoRecovery -or ('' -ne $StandbyDirectory))) {
            Stop-Function -Category InvalidArgument -Message "KeepCDC cannot be specified with Norecovery or Standby as it needs recovery to work"
            return
        }

        if ($null -ne $PageRestore) {
            Write-Message -Message "Doing Page Recovery" -Level Verbose
            $tmpPages = @()
            foreach ($Page in $PageRestore) {
                $tmppages += "$($Page.FileId):$($Page.PageID)"
            }
            $NoRecovery = $True
            $Pages = $tmpPages -join ','
        }
        #$OutputScriptOnly  = $false
        $InternalHistory = @()
    }
    process {
        foreach ($bh in $BackupHistory) {
            $InternalHistory += $bh
        }
    }
    end {
        if (Test-FunctionInterrupt) { return }
        $Databases = $InternalHistory.Database | Select-Object -Unique
        foreach ($Database in $Databases) {
            $DatabaseRestoreStartTime = Get-Date
            if ($Database -in $Server.Databases.Name) {
                if (-not $OutputScriptOnly -and -not $VerifyOnly) {
                    if ($Pscmdlet.ShouldProcess("Killing processes in $Database on $SqlInstance as it exists and WithReplace specified  `n", "Cannot proceed if processes exist, ", "Database Exists and WithReplace specified, need to kill processes to restore")) {
                        try {
                            Write-Message -Level Verbose -Message "Killing processes on $Database"
                            $null = Stop-DbaProcess -SqlInstance $Server -Database $Database -WarningAction Silentlycontinue
                            $null = $server.Query("Alter database $Database set offline with rollback immediate; alter database $Database set restricted_user; Alter database $Database set online with rollback immediate", 'master')
                            $server.ConnectionContext.Connect()
                        }
                        catch {
                            Write-Message -Level Verbose -Message "No processes to kill in $Database"
                        }
                    }
                }
                elseif (-not $WithReplace -and (-not $VerifyOnly)) {
                    Stop-Function -Message "$Database exists and WithReplace not specified, stopping" -EnableException $EnableException
                    return
                }
            }
            Write-Message -Message "WithReplace  = $WithReplace" -Level Debug
            $backups = @($InternalHistory | Where-Object {$_.Database -eq $Database} | Sort-Object -Property Type, FirstLsn)
            $BackupCnt = 1
            foreach ($backup in $backups) {
                $FileRestoreStartTime = Get-Date
                $Restore = New-Object Microsoft.SqlServer.Management.Smo.Restore
                if (($backup -ne $backups[-1]) -or $true -eq $NoRecovery) {
                    $Restore.NoRecovery = $True
                }
                elseif ($backup -eq $backups[-1] -and '' -ne $StandbyDirectory) {
                    $Restore.StandbyFile = $StandByDirectory + "\" + $Database + (get-date -Format yyyMMddHHmmss) + ".bak"
                    Write-Message -Level Verbose -Message "Setting standby on last file $($Restore.StandbyFile)"
                }
                else {
                    $Restore.NoRecovery = $False
                }
                if ($restoretime -gt (Get-Date) -or $Restore.RestoreTime -gt (Get-Date) -or $backup.RecoveryModel -eq 'Simple') {
                    $Restore.ToPointInTime = $null
                }
                else {
                    if ($RestoreTime -ne $Restore.RestoreTime) {
                        $Restore.ToPointInTime = $backup.RestoreTime
                    }
                    else {
                        $Restore.ToPointInTime = $RestoreTime
                    }
                }
                $Restore.Database = $database
                $Restore.ReplaceDatabase = $WithReplace
                if ($MaxTransferSize) {
                    $Restore.MaxTransferSize = $MaxTransferSize
                }
                if ($BufferCount) {
                    $Restore.BufferCount = $BufferCount
                }
                if ($BlockSize) {
                    $Restore.Blocksize = $BlockSize
                }
                if ($true -ne $Continue -and ($null -eq $Pages)) {
                    foreach ($file in $backup.FileList) {
                        $MoveFile = New-Object Microsoft.SqlServer.Management.Smo.RelocateFile
                        $MoveFile.LogicalFileName = $File.LogicalName
                        $MoveFile.PhysicalFileName = $File.PhysicalName
                        $null = $Restore.RelocateFiles.Add($MoveFile)
                    }
                }
                $Action = switch ($backup.Type) {
                    '1' {'Database'}
                    '2' {'Log'}
                    '5' {'Database'}
                    'Transaction Log' {'Log'}
                    Default {'Database'}
                }

                Write-Message -Level Debug -Message "restore action = $Action"
                $Restore.Action = $Action
                foreach ($File in $backup.FullName) {
                    Write-Message -Message "Adding device $file" -Level Debug
                    $Device = New-Object -TypeName Microsoft.SqlServer.Management.Smo.BackupDeviceItem
                    $Device.Name = $file
                    if ($file.StartsWith("http")) {
                        $Device.devicetype = "URL"
                    }
                    else {
                        $Device.devicetype = "File"
                    }
                    
                    if ($AzureCredential) {
                        $Restore.CredentialName = $AzureCredential
                    }
                    
                    $Restore.FileNumber = $backup.Position
                    $Restore.Devices.Add($Device)
                }
                Write-Message -Level Verbose -Message "Performing restore action"
                $ConfirmMessage = "`n Restore Database $Database on $SqlInstance `n from files: $RestoreFileNames `n with these file moves: `n $LogicalFileMovesString `n $ConfirmPointInTime `n"
                if ($Pscmdlet.ShouldProcess("$Database on $SqlInstance `n `n", $ConfirmMessage)) {
                    try {
                        $RestoreComplete = $true
                        if ($KeepCDC -and $Restore.NoRecovery -eq $false) {
                            $script = $Restore.Script($server)
                            if ($script -like '*WITH*') {
                                $script = $script.TrimEnd() + ' , KEEP_CDC'
                            }
                            else {
                                $script = $script.TrimEnd() + ' WITH KEEP_CDC'
                            }
                            if ($true -ne $OutputScriptOnly) {
                                Write-Progress -id 2 -activity "Restoring $Database to $sqlinstance - Backup $BackupCnt of $($Backups.count)" -percentcomplete 0 -status ([System.String]::Format("Progress: {0} %", 0))
                                $null = $server.ConnectionContext.ExecuteNonQuery($script)
                                Write-Progress -id 2 -activity "Restoring $Database to $sqlinstance - Backup $BackupCnt of $($Backups.count)" -status "Complete" -Completed
                            }
                        }
                        elseif ($null -ne $Pages -and $Action -eq 'Database') {
                            $script = $Restore.Script($server)
                            $script = $script -replace "] FROM", "] PAGE='$pages' FROM"
                            if ($true -ne $OutputScriptOnly) {
                                Write-Progress -id 2 -activity "Restoring $Database to $sqlinstance - Backup $BackupCnt of $($Backups.count)" -percentcomplete 0 -status ([System.String]::Format("Progress: {0} %", 0))
                                $null = $server.ConnectionContext.ExecuteNonQuery($script)
                                Write-Progress -id 2 -activity "Restoring $Database to $sqlinstance - Backup $BackupCnt of $($Backups.count)" -status "Complete" -Completed
                            }
                        }
                        elseif ($OutputScriptOnly) {
                            $script = $Restore.Script($server)
                        }
                        elseif ($VerifyOnly) {
                            Write-Message -Message "VerifyOnly restore" -Level Verbose
                            Write-Progress -id 2 -activity "Verifying $Database backup file on $sqlinstance - Backup $BackupCnt of $($Backups.count)" -percentcomplete 0 -status ([System.String]::Format("Progress: {0} %", 0))
                            $Verify = $Restore.SqlVerify($server)
                            Write-Progress -id 2 -activity "Verifying $Database backup file on $sqlinstance - Backup $BackupCnt of $($Backups.count)" -status "Complete" -Completed
                            if ($verify -eq $true) {
                                Write-Message -Message "VerifyOnly restore Succeeded" -Level Verbose
                                return "Verify successful"
                            }
                            else {
                                Write-Message -Message "VerifyOnly restore Failed" -Level Verbose
                                return "Verify failed"
                            }
                        }
                        else {
                            $outerProgress = $BackupCnt/$Backups.Count*100
                            if ($BackupCnt -eq 1) {
                                Write-Progress -id 2 -ParentId 1 -Activity "Restoring $Database to $sqlinstance - Backup $BackupCnt of $($Backups.count)" -percentcomplete 0
                            }
                            Write-Progress -id 3 -ParentId 2 -Activity "Restore $($backup.FullName -Join ',')" -percentcomplete 0
                            $script = $Restore.Script($Server)
                            $percentcomplete = [Microsoft.SqlServer.Management.Smo.PercentCompleteEventHandler] {
                                Write-Progress -id 3 -ParentId 2 -Activity "Restore $($backup.FullName -Join ',')" -percentcomplete $_.Percent -status ([System.String]::Format("Progress: {0} %", $_.Percent))
                            }
                            $Restore.add_PercentComplete($percentcomplete)
                            $Restore.PercentCompleteNotification = 1
                            $Restore.SqlRestore($Server)
                            Write-Progress -id 3 -ParentId 2 -Activity "Restore $($backup.FullName -Join ',')" -Completed
                            Write-Progress -id 2 -ParentId 1 -Activity "Restoring $Database to $sqlinstance - Backup $BackupCnt of $($Backups.count)" -percentcomplete $outerProgress -status ([System.String]::Format("Progress: {0:N2} %", $outerProgress))
                        }
                    }
                    catch {
                        Write-Message -Level Verbose -Message "Failed, Closing Server connection"
                        $RestoreComplete = $False
                        $ExitError = $_.Exception.InnerException
                        Stop-Function -Message "Failed to restore db $Database, stopping" -ErrorRecord $_
                        return
                    }
                    finally {

                        if ($OutputScriptOnly -eq $false) {
                            [PSCustomObject]@{
                                SqlInstance            = $SqlInstance
                                DatabaseName           = $backup.Database
                                DatabaseOwner          = $server.ConnectionContext.TrueLogin
                                NoRecovery             = $Restore.NoRecovery
                                WithReplace            = $WithReplace
                                RestoreComplete        = $RestoreComplete
                                BackupFilesCount       = $backup.FullName.Count
                                RestoredFilesCount     = $backup.Filelist.PhysicalName.count
                                BackupSizeMB           = if ([bool]($backup.psobject.Properties.Name -contains 'TotalSize')) { [Math]::Round(($backup | Measure-Object -Property TotalSize -Sum).Sum / 1mb, 2) } else { $null }
                                CompressedBackupSizeMB = if ([bool]($backup.psobject.Properties.Name -contains 'CompressedBackupSize')) { [Math]::Round(($backup | Measure-Object -Property CompressedBackupSize -Sum).Sum / 1mb, 2) } else { $null }
                                BackupFile             = $backup.FullName -Join ','
                                RestoredFile           = $((Split-Path $backup.FileList.PhysicalName -Leaf) | Sort-Object -Unique) -Join ','
                                RestoredFileFull       = ($backup.Filelist.PhysicalName -Join ',')
                                RestoreDirectory       = ((Split-Path $backup.FileList.PhysicalName) | Sort-Object -Unique) -Join ','
                                BackupSize             = if ([bool]($backup.psobject.Properties.Name -contains 'TotalSize')) { ($backup | Measure-Object -Property TotalSize -Sum).Sum } else { $null }
                                CompressedBackupSize   = if ([bool]($backup.psobject.Properties.Name -contains 'CompressedBackupSize')) { ($backup | Measure-Object -Property CompressedBackupSize -Sum).Sum } else { $null }
                                Script                 = $script
                                BackupFileRaw          = ($backups.Fullname)
                                FileRestoreTime        = New-TimeSpan -Seconds ((Get-Date)-$FileRestoreStartTime).TotalSeconds
                                DatabaseRestoreTime    = New-TimeSpan -Seconds ((Get-Date)-$DatabaseRestoreStartTime).TotalSeconds
                                ExitError              = $ExitError
                            } | Select-DefaultView -ExcludeProperty BackupSize, CompressedBackupSize, ExitError, BackupFileRaw, RestoredFileFull
                        }
                        else {
                            $script
                        }
                        if ($Restore.Devices.Count -gt 0) {
                            $Restore.Devices.Clear()
                        }
                        Write-Message -Level Verbose -Message "Succeeded, Closing Server connection"
                        $server.ConnectionContext.Disconnect()
                    }
                }
                Write-Progress -id 1 -Activity "Restoring" -Completed
                Write-Progress -id 2 -Activity "Restoring" -Completed
                $BackupCnt++
            }
            if ($server.ConnectionContext.exists) {
                $server.ConnectionContext.Disconnect()
            }
        }
    }
}
tools\dbatools\functions\Invoke-DbaBalanceDataFiles.ps1
function Invoke-DbaBalanceDataFiles {
    <#
        .SYNOPSIS
            Re-balance data between data files

        .DESCRIPTION
            When you have a large database with a single data file and add another file, SQL Server will only use the new file until it's about the same size.
            You may want to balance the data between all the data files.

            The function will check the server version and edition to see if the it allows for online index rebuilds.
            If the server does support it, it will try to rebuild the index online.
            If the server doesn't support it, it will rebuild the index offline. Be carefull though, this can cause downtime

            The tables must have a clustered index to be able to balance out the data.
            The function does NOT yet support heaps.

            The function will also check if the file groups are subject to balance out.
            A file group whould have at least have 2 data files and should be writable.
            If a table is within such a file group it will be subject for processing. If not the table will be skipped.

        .PARAMETER SqlInstance
            The target SQL Server instance or instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process.

        .PARAMETER Table
            The tables(s) of the database to process. If unspecified, all tables will be processed.

        .PARAMETER RebuildOffline
            Will set all the indexes to rebuild offline.
            This option is also needed when the server version is below 2005.

        .PARAMETER WhatIf
            Shows what would happen if the command were to run

        .PARAMETER Confirm
            Prompts for confirmation of every step. For example:

            The server does not support online rebuilds of indexes.
            Do you want to rebuild the indexes offline?
            [Y] Yes  [N] No   [?] Help (default is "Y"):

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER Force
            This will disable the check for enough disk space for the action to be successful.
            Use this with caution!!

        .NOTES
            Tags: Database, FileManagement, File, Space
            Author: Sander Stad (@sqlstad, sqlstad.nl)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            Invoke-DbaBalanceDataFiles -SqlInstance sql1 -Database db1

            This command will distribute the data in database db1 on instance sql1

        .EXAMPLE
            Invoke-DbaBalanceDataFiles -SqlInstance sql1 -Database db1 | Select-Object -ExpandProperty DataFilesEnd

            This command will distribute the data in database db1 on instance sql1

        .EXAMPLE
            Invoke-DbaBalanceDataFiles -SqlInstance sql1 -Database db1 -Table table1,table2,table5

            This command will distribute the data for only the tables table1,table2 and table5

        .EXAMPLE
            Invoke-DbaBalanceDataFiles -SqlInstance sql1 -Database db1 -RebuildOffline

            This command will consider the fact that there might be a SQL Server edition that does not support online rebuilds of indexes.
            By supplying this parameter you give permission to do the rebuilds offline if the edition does not support it.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(ParameterSetName = "Pipe", Mandatory = $true)]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [Alias("Tables")]
        [object[]]$Table,
        [switch]$RebuildOffline,
        [switch]$EnableException,
        [switch]$Force
    )

    process {

        Write-Message -Message "Starting balancing out data files" -Level Verbose

        # Set the initial success flag
        [bool]$success = $true

        foreach ($instance in $sqlinstance) {
            # Try connecting to the instance
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $Server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            # Check the database parameter
            if ($Database) {
                if ($Database -notin $server.Databases.Name) {
                    Stop-Function -Message "One or more databases cannot be found on instance on instance $instance" -Target $instance -Continue
                }

                $DatabaseCollection = $server.Databases | Where-Object { $_.Name -in $Database }
            }
            else {
                Stop-Function -Message "Please supply a database to balance out" -Target $instance -Continue
            }

            # Get the server version
            $serverVersion = $server.Version.Major

            # Check edition of the sql instance
            if ($RebuildOffline) {
                Write-Message -Message "Continuing with offline rebuild." -Level Verbose
            }
            elseif (-not $RebuildOffline -and ($serverVersion -lt 9 -or (([string]$Server.Edition -notmatch "Developer") -and ($Server.Edition -notmatch "Enterprise")))) {
                # Set up the confirm part
                $message = "The server does not support online rebuilds of indexes. `nDo you want to rebuild the indexes offline?"
                $choiceYes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Answer Yes."
                $choiceNo = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Answer No."
                $options = [System.Management.Automation.Host.ChoiceDescription[]]($choiceYes, $choiceNo)
                $result = $host.ui.PromptForChoice($title, $message, $options, 0)

                # Check the result from the confirm
                switch ($result) {
                    # If yes
                    0 {
                        # Set the option to generate a full backup
                        Write-Message -Message "Continuing with offline rebuild." -Level Verbose

                        [bool]$supportOnlineRebuild = $false
                    }
                    1 {
                        Stop-Function -Message "You chose to not allow offline rebuilds of indexes. Use -RebuildOffline" -Target $instance
                        return
                    }
                } # switch
            }
            elseif ($serverVersion -ge 9 -and (([string]$Server.Edition -like "Developer*") -or ($Server.Edition -like "Enterprise*"))) {
                [bool]$supportOnlineRebuild = $true
            }

            # Loop through each of the databases
            foreach ($db in $DatabaseCollection) {
                $dataFilesStarting = Get-DbaDatabaseFile -SqlInstance $server -Database $db.Name | Where-Object { $_.TypeDescription -eq 'ROWS' } | Select-Object ID, LogicalName, PhysicalName, Size, UsedSpace, AvailableSpace | Sort-Object ID

                if (-not $Force) {
                    # Check the amount of disk space available
                    $query = "SELECT SUBSTRING(physical_name, 0, 4) AS 'Drive' ,
                                        SUM(( size * 8 ) / 1024) AS 'SizeMB'
                                FROM	sys.master_files
                                WHERE	DB_NAME(database_id) = '$($db.Name)'
                                GROUP BY SUBSTRING(physical_name, 0, 4)"
                    # Execute the query
                    $dbDiskUsage = $Server.Query($query)

                    # Get the free space for each drive
                    $result = $Server.Query("xp_fixeddrives")
                    $MbFreeColName = $result[0].psobject.Properties.Name[1]
                    $diskFreeSpace = $result | Select-Object Drive, @{ Name = 'FreeMB'; Expression = { $_.$MbFreeColName } }

                    # Loop through each of the drives to see if the size of files on that
                    # particular disk do not exceed the free space of that disk
                    foreach ($d in $dbDiskUsage) {
                        $freeSpace = $diskFreeSpace | Where-Object { $_.Drive -eq $d.Drive.Trim(':\') } | Select-Object FreeMB
                        if ($d.SizeMB -gt $freeSpace.FreeMB) {
                            # Set the success flag
                            $success = $false

                            Stop-Function -Message "The available space may not be sufficient to continue the process. Please use -Force to try anyway." -Target $instance -Continue
                            return
                        }
                    }
                }

                # Create the start time
                $start = Get-Date

                # Check if the function needs to continue
                if ($success) {

                    # Get the database files before all the alterations
                    Write-Message -Message "Retrieving data files before data move" -Level Verbose
                    Write-Message -Message "Processing database $db" -Level Verbose

                    # Check the datafiles of the database
                    $dataFiles = Get-DbaDatabaseFile -SqlInstance $instance -Database $db | Where-Object { $_.TypeDescription -eq 'ROWS' }
                    if ($dataFiles.Count -eq 1) {
                        # Set the success flag
                        $success = $false

                        Stop-Function -Message "Database $db only has one data file. Please add a data file to balance out the data" -Target $instance -Continue
                    }

                    # Check the tables parameter
                    if ($Table) {
                        if ($Table -notin $db.Table) {
                            # Set the success flag
                            $success = $false

                            Stop-Function -Message "One or more tables cannot be found in database $db on instance $instance" -Target $instance -Continue
                        }

                        $TableCollection = $db.Tables | Where-Object { $_.Name -in $Table }
                    }
                    else {
                        $TableCollection = $db.Tables
                    }

                    # Get the database file groups and check the aount of data files
                    Write-Message -Message "Retrieving file groups" -Level Verbose
                    $fileGroups = $Server.Databases[$db.Name].FileGroups

                    # ARray to hold the file groups with properties
                    $balanceableTables = @()

                    # Loop through each of the file groups

                    foreach ($fg in $fileGroups) {

                        # If there is less than 2 files balancing out data is not possible
                        if (($fg.Files.Count -ge 2) -and ($fg.Readonly -eq $false)) {
                            $balanceableTables += $fg.EnumObjects() | Where-Object { $_.GetType().Name -eq 'Table' }
                        }
                    }

                    $unsuccessfulTables = @()

                    # Loop through each of the tables
                    foreach ($tbl in $TableCollection) {

                        # Chck if the table balanceable
                        if ($tbl.Name -in $balanceableTables.Name) {

                            Write-Message -Message "Processing table $tbl" -Level Verbose

                            # Chck the tables and get the clustered indexes
                            if ($TableCollection.Indexes.Count -lt 1) {
                                # Set the success flag
                                $success = $false

                                Stop-Function -Message "Table $tbl does not contain any indexes" -Target $instance -Continue
                            }
                            else {

                                # Get all the clustered indexes for the table
                                $clusteredIndexes = $TableCollection.Indexes | Where-Object { $_.IndexType -eq 'ClusteredIndex' }

                                if ($clusteredIndexes.Count -lt 1) {
                                    # Set the success flag
                                    $success = $false

                                    Stop-Function -Message "No clustered indexes found in table $tbl" -Target $instance -Continue
                                }
                            }

                            # Loop through each of the clustered indexes and rebuild them
                            Write-Message -Message "$($clusteredIndexes.Count) clustered index(es) found for table $tbl" -Level Verbose
                            if ($PSCmdlet.ShouldProcess("Rebuilding indexes to balance data")) {
                                foreach ($ci in $clusteredIndexes) {

                                    Write-Message -Message "Rebuilding index $($ci.Name)" -Level Verbose

                                    # Get the original index operation
                                    [bool]$originalIndexOperation = $ci.OnlineIndexOperation

                                    # Set the rebuild option to be either offline or online
                                    if ($RebuildOffline) {
                                        $ci.OnlineIndexOperation = $false
                                    }
                                    elseif ($serverVersion -ge 9 -and $supportOnlineRebuild -and -not $RebuildOffline) {
                                        Write-Message -Message "Setting the index operation for index $($ci.Name) to online" -Level Verbose
                                        $ci.OnlineIndexOperation = $true
                                    }

                                    # Rebuild the index
                                    try {
                                        Write-Message -Message "Rebuilding index $($ci.Name)" -Level Verbose
                                        $ci.Rebuild()

                                        # Set the success flag
                                        $success = $true
                                    }
                                    catch {
                                        # Set the original index operation back for the index
                                        $ci.OnlineIndexOperation = $originalIndexOperation

                                        # Set the success flag
                                        $success = $false

                                        Stop-Function -Message "Something went wrong rebuilding index $($ci.Name). `n$($_.Exception.Message)" -ErrorRecord $_ -Target $instance -Continue
                                    }

                                    # Set the original index operation back for the index
                                    Write-Message -Message "Setting the index operation for index $($ci.Name) back to the original value" -Level Verbose
                                    $ci.OnlineIndexOperation = $originalIndexOperation

                                } # foreach index

                            } # if process

                        } # if table is balanceable
                        else {
                            # Add the table to the unsuccessful array
                            $unsuccessfulTables += $tbl.Name

                            # Set the success flag
                            $success = $false

                            Write-Message -Message "Table $tbl cannot be balanced out" -Level Verbose
                        }

                    } #foreach table
                }

                # Create the end time
                $end = Get-Date

                # Create the time span
                $timespan = New-TimeSpan -Start $start -End $end
                $ts = [timespan]::fromseconds($timespan.TotalSeconds)
                $elapsed = "{0:HH:mm:ss}" -f ([datetime]$ts.Ticks)

                # Get the database files after all the alterations
                Write-Message -Message "Retrieving data files after data move" -Level Verbose
                $dataFilesEnding = Get-DbaDatabaseFile -SqlInstance $server -Database $db.Name | Where-Object { $_.TypeDescription -eq 'ROWS' } | Select-Object ID, LogicalName, PhysicalName, Size, UsedSpace, AvailableSpace | Sort-Object ID

                [pscustomobject]@{
                    ComputerName   = $server.ComputerName
                    InstanceName   = $server.ServiceName
                    SqlInstance    = $server.DomainInstanceName
                    Database       = $db.Name
                    Start          = $start
                    End            = $end
                    Elapsed        = $elapsed
                    Success        = $success
                    Unsuccessful   = $unsuccessfulTables -join ","
                    DataFilesStart = $dataFilesStarting
                    DataFilesEnd   = $dataFilesEnding
                }

            } # foreach database

        } # end process
    }
}
tools\dbatools\functions\Invoke-DbaCycleErrorLog.ps1
function Invoke-DbaCycleErrorLog {
    <#
        .SYNOPSIS
            Cycles the current instance or agent log.

        .DESCRIPTION
            Cycles the current error log for the instance (SQL Server) and/or SQL Server Agent.

        .PARAMETER SqlInstance
            The SQL Server instance holding the databases to be removed.You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Type
            The log to cycle.
            Accepts: instance or agent.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Log, Cycle
            Author: Shawn Melton (@wsmelton | http://blog.wsmelton.info)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Invoke-DbaCycleLog

        .EXAMPLE
            Invoke-DbaCycleLog -SqlInstance sql2016 -Type agent

            Cycles the current error log for the SQL Server Agent on SQL Server instance sql2016

        .EXAMPLE
            Invoke-DbaCycleLog -SqlInstance sql2016 -Type instance

            Cycles the current error log for the SQL Server instance on SQL Server instance sql2016

        .EXAMPLE
            Invoke-DbaCycleLog -SqlInstance sql2016

            Cycles the current error log for both SQL Server instance and SQL Server Agent on SQL Server instance sql2016
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Low')]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [ValidateSet('instance', 'agent')]
        [string]$Type,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        if (Test-Bound 'Type') {
            if ($Type -notin 'instance', 'agent') {
                Stop-Function -Message "The type provided [$Type] for $SqlInstance is not an accepted value. Please use 'Instance' or 'Agent'"
                return
            }
        }
        $logToCycle = @()
        switch ($Type) {
            'agent' {
                $sql = "EXEC msdb.dbo.sp_cycle_agent_errorlog;"
                $logToCycle = $Type
            }
            'instance' {
                $sql = "EXEC master.dbo.sp_cycle_errorlog;"
                $logToCycle = $Type
            }
            default {
                $sql = "
                    EXEC master.dbo.sp_cycle_errorlog;
                    EXEC msdb.dbo.sp_cycle_agent_errorlog;"
                $logToCycle = 'instance', 'agent'
            }
        }

    }
    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            try {
                $logs = $logToCycle -join ','
                if ($Pscmdlet.ShouldProcess($server, "Cycle the log(s): $logs")) {
                    $null = $server.Query($sql)
                    [pscustomobject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        LogType      = $logToCycle
                        IsSuccessful = $true
                        Notes        = $null
                    }
                }
            }
            catch {
                [pscustomobject]@{
                    ComputerName = $server.ComputerName
                    InstanceName = $server.ServiceName
                    SqlInstance  = $server.DomainInstanceName
                    LogType      = $logToCycle
                    IsSuccessful = $false
                    Notes        = $_.Exception
                }
                Stop-Function -Message "Issue cycling $logs on $server" -Target $server -ErrorRecord $_ -Exception $_.Exception -Continue
            }
        }
    }
}
tools\dbatools\functions\Invoke-DbaDatabaseClone.ps1
function Invoke-DbaDatabaseClone {
    <#
    .SYNOPSIS
        Clones a database schema and statistics

    .DESCRIPTION
        Clones a database schema and statistics.

        This can be useful for testing query performance without requiring all the space needed for the data in the database.

        Read more at sqlperformance: https://sqlperformance.com/2016/08/sql-statistics/expanding-dbcc-clonedatabase

        Thanks to Microsoft Tiger Team for the code and idea https://github.com/Microsoft/tigertoolbox/

    .PARAMETER SqlInstance
        Allows you to specify a comma separated list of servers to query.

    .PARAMETER SqlCredential
        Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

    .PARAMETER Database
        The database to clone - this list is auto-populated from the server.

    .PARAMETER CloneDatabase
        The name(s) to clone to.

    .PARAMETER UpdateStatistics
        Update the statistics prior to cloning (per Microsoft Tiger Team formula)

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Tags: Statistics, Performance
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Invoke-DbaDatabaseClone

    .EXAMPLE
        Invoke-DbaDatabaseClone -SqlInstance sql2016 -Database mydb -CloneDatabase myclone
        Clones mydb to myclone on sql2016

    .EXAMPLE
        Invoke-DbaDatabaseClone -SqlInstance sql2016 -Database mydb -CloneDatabase myclone, myclone2 -UpdateStatistics
        Updates the statistics of mydb then clones to myclone and myclone2

    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory, ValueFromPipeline)]
        [object]$Database,
        [string[]]$CloneDatabase,
        [switch]$UpdateStatistics,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {

        if (-not $Database.Name -and -not $SqlInstance) {
            Stop-Function -Message "You must specify a server name if you did not pipe a database"
        }

        $updatestats = "declare @out table(id int identity(1,1),s sysname, o sysname, i sysname, stats_stream varbinary(max), rows bigint, pages bigint)
                    declare @dbcc table(stats_stream varbinary(max), rows bigint, pages bigint)
                    declare c cursor for
                           select object_schema_name(object_id) s, object_name(object_id) o, name i
                           from sys.indexes
                           where type_desc in ('CLUSTERED COLUMNSTORE', 'NONCLUSTERED COLUMNSTORE')
                    declare @s sysname, @o sysname, @i sysname
                    open c
                    fetch next from c into @s, @o, @i
                    while @@FETCH_STATUS = 0 begin
                           declare @showStats nvarchar(max) = N'DBCC SHOW_STATISTICS(""' + quotename(@s) + '.' + quotename(@o) + '"", ' + quotename(@i) + ') with stats_stream'
                           insert @dbcc exec sp_executesql @showStats
                           insert @out select @s, @o, @i, stats_stream, rows, pages from @dbcc
                           delete @dbcc
                           fetch next from c into @s, @o, @i
                    end
                    close c
                    deallocate c


                    declare @sql nvarchar(max);
                    declare @id int;

                    select top 1 @id=id,@sql=
                    'UPDATE STATISTICS ' + quotename(s) + '.' + quotename(o)  + '(' + quotename(i)
                    + ') with stats_stream = ' + convert(nvarchar(max), stats_stream, 1)
                    + ', rowcount = ' + convert(nvarchar(max), rows) + ', pagecount = '  + convert(nvarchar(max), pages)
                    from @out

                    WHILE (@@ROWCOUNT <> 0)
                    BEGIN
                        exec sp_executesql @sql
                        delete @out where id = @id
                        select top 1 @id=id,@sql=
                        'UPDATE STATISTICS ' + quotename(s) + '.' + quotename(o)  + '(' + quotename(i)
                        + ') with stats_stream = ' + convert(nvarchar(max), stats_stream, 1)
                        + ', rowcount = ' + convert(nvarchar(max), rows) + ', pagecount = '  + convert(nvarchar(max), pages)
                        from @out
                    END"

    }

    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 12
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $sql2012min = [version]"11.0.7001.0" # SQL 2012 SP4
            $sql2014min = [version]"12.0.5000.0" # SQL 2014 SP2
            $sql2016min = [version]"13.0.4001.0" # SQL 2016 SP1


            if ($server.VersionMajor -eq 11 -and $server.Version -lt $sql2012min) {
                Stop-Function -Message "Unsupported version for $instance. SQL Server 2012 SP4 and above required." -Target $server -Continue
            }

            if ($server.VersionMajor -eq 12 -and $server.Version -lt $sql2014min) {
                Stop-Function -Message "Unsupported version for $instance. SQL Server 2014 SP2 and above required." -Target $server -Continue
            }

            if ($server.VersionMajor -eq 13 -and $server.Version -lt $sql2016min) {
                Stop-Function -Message "Unsupported version for $instance. SQL Server 2016 SP1 and above required." -Target $server -Continue
            }

            if (-not $Database.Name) {
                [Microsoft.SqlServer.Management.Smo.Database]$database = $server.Databases[$database]
            }

            if ($Database.IsSystemObject) {
                Stop-Function -Message "Only user databases are supported" -Target $instance -Continue
            }

            if (-not $Database.name) {
                Stop-Function -Message "Database not found" -Target $instance -Continue
            }

            if ($UpdateStatistics) {
                try {
                    Write-Message -Level Verbose -Message "Updating statistics"
                    $null = $database.Query($updatestats)
                }
                catch {
                    Stop-Function -Message "Failure" -ErrorRecord $_ -Target $server -Continue
                }
            }

            $dbname = $database.Name

            foreach ($db in $CloneDatabase) {
                Write-Message -Level Verbose -Message "Cloning $db from $database"
                if ($server.Databases[$db]) {
                    Stop-Function -Message "Destination clone database $db already exists" -Target $instance -Continue
                }
                else {
                    try {
                        $sql = "dbcc clonedatabase('$dbname','$db')"
                        $null = $database.Query($sql)
                        $server.Databases.Refresh()
                        Get-DbaDatabase -SqlInstance $server -Database $db
                    }
                    catch {
                        Stop-Function -Message "Failure" -ErrorRecord $_ -Target $server -Continue
                    }
                }
            }
        }
    }
}
tools\dbatools\functions\Invoke-DbaDatabaseShrink.ps1
function Invoke-DbaDatabaseShrink {
    <#
        .SYNOPSIS
            Shrinks all files in a database. This is a command that should rarely be used.

            - Shrinks can cause severe index fragmentation (to the tune of 99%)
            - Shrinks can cause massive growth in the database's transaction log
            - Shrinks can require a lot of time and system resources to perform data movement

        .DESCRIPTION
            Shrinks all files in a database. Databases should be shrunk only when completely necessary.

            Many awesome SQL people have written about why you should not shrink your data files. Paul Randal and Kalen Delaney wrote great posts about this topic:

                http://www.sqlskills.com/blogs/paul/why-you-should-not-shrink-your-data-files
                http://sqlmag.com/sql-server/shrinking-data-files

            However, there are some cases where a database will need to be shrunk. In the event that you must shrink your database:

            1. Ensure you have plenty of space for your T-Log to grow
            2. Understand that shrinks require a lot of CPU and disk resources
            3. Consider running DBCC INDEXDEFRAG or ALTER INDEX ... REORGANIZE after the shrink is complete.

        .PARAMETER SqlInstance
            The target SQL Server instances

        .PARAMETER SqlCredential
            SqlCredential object used to connect to the SQL Server as a different user.

        .PARAMETER Database
            The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto-populated from the server

        .PARAMETER AllUserDatabases
            Run command against all user databases

        .PARAMETER PercentFreeSpace
            Specifies how much to reduce the database in percent, defaults to 0.

        .PARAMETER ShrinkMethod
            Specifies the method that is used to shrink the database
                Default
                    Data in pages located at the end of a file is moved to pages earlier in the file. Files are truncated to reflect allocated space.
                EmptyFile
                    Migrates all of the data from the referenced file to other files in the same filegroup. (DataFile and LogFile objects only).
                NoTruncate
                    Data in pages located at the end of a file is moved to pages earlier in the file.
                TruncateOnly
                    Data distribution is not affected. Files are truncated to reflect allocated space, recovering free space at the end of any file.

        .PARAMETER StatementTimeout
            Timeout in minutes. Defaults to infinity (shrinks can take a while.)

        .PARAMETER LogsOnly
            Deprecated. Use FileType instead

        .PARAMETER FileType
            Specifies the files types that will be shrunk
                All
                    All Data and Log files are shrunk, using database shrink (Default)
                Data
                    Just the Data files are shrunk using file shrink
                Log
                    Just the Log files are shrunk using file shrink

        .PARAMETER ExcludeIndexStats
            Exclude statistics about fragmentation

        .PARAMETER ExcludeUpdateUsage
            Exclude DBCC UPDATE USAGE for database

        .PARAMETER WhatIf
            Shows what would happen if the command were to run

        .PARAMETER Confirm
            Prompts for confirmation of every step. For example:

            Are you sure you want to perform this action?
            Performing the operation "Shrink database" on target "pubs on SQL2016\VNEXT".
            [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Shrink, Database

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Invoke-DbaDatabaseShrink

        .EXAMPLE
            Invoke-DbaDatabaseShrink -SqlInstance sql2016 -Database Northwind,pubs,Adventureworks2014

            Shrinks Northwind, pubs and Adventureworks2014 to have as little free space as possible.

        .EXAMPLE
            Invoke-DbaDatabaseShrink -SqlInstance sql2014 -Database AdventureWorks2014 -PercentFreeSpace 50

            Shrinks AdventureWorks2014 to have 50% free space. So let's say AdventureWorks2014 was 1GB and it's using 100MB space. The database free space would be reduced to 50MB.

        .EXAMPLE
            Invoke-DbaDatabaseShrink -SqlInstance sql2012 -AllUserDatabases

            Shrinks all databases on SQL2012 (not ideal for production)
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Low')]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$AllUserDatabases,
        [ValidateRange(0, 99)]
        [int]$PercentFreeSpace = 0,
        [ValidateSet('Default', 'EmptyFile', 'NoTruncate', 'TruncateOnly')]
        [string]$ShrinkMethod = "Default",
        [ValidateSet('All', 'Data', 'Log')]
        [string]$FileType = "All",
        [int]$StatementTimeout = 0,
        [switch]$LogsOnly,
        [switch]$ExcludeIndexStats,
        [switch]$ExcludeUpdateUsage,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        if ($LogsOnly) {
            Test-DbaDeprecation -DeprecatedOn "1.0.0" -Parameter "LogsOnly"
            $FileType = 'Log'
        }

        $StatementTimeoutSeconds = $StatementTimeout * 60

        $sql = "SELECT
                  avg(avg_fragmentation_in_percent) as [avg_fragmentation_in_percent]
                , max(avg_fragmentation_in_percent) as [max_fragmentation_in_percent]
                FROM sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL, NULL, NULL) AS indexstats
                WHERE indexstats.avg_fragmentation_in_percent > 0 AND indexstats.page_count > 100
                GROUP BY indexstats.database_id"
    }

    process {
        if (!$Database -and !$ExcludeDatabase -and !$AllUserDatabases) {
            Stop-Function -Message "You must specify databases to execute against using either -Databases, -Exclude or -AllUserDatabases" -Continue
        }

        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            # changing statement timeout to $StatementTimeout
            if ($StatementTimeout -eq 0) {
                Write-Message -Level Verbose -Message "Changing statement timeout to infinity"
            }
            else {
                Write-Message -Level Verbose -Message "Changing statement timeout to $StatementTimeout minutes"
            }
            $server.ConnectionContext.StatementTimeout = $StatementTimeoutSeconds

            $dbs = $server.Databases | Where-Object { $_.IsSystemObject -eq $false -and $_.IsAccessible }

            if ($Database) {
                $dbs = $dbs | Where-Object Name -In $Database
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $dbs) {
                Write-Message -Level Verbose -Message "Processing $db on $instance"

                if ($db.IsDatabaseSnapshot) {
                    Write-Message -Level Warning -Message "The database $db on server $instance is a snapshot and cannot be shrunk. Skipping database."
                    continue
                }

                $startingSize = $db.Size
                $spaceAvailableMB = $db.SpaceAvailable / 1024
                $spaceUsed = $startingSize - $spaceAvailableMB
                $desiredSpaceAvailable = ($PercentFreeSpace * $spaceUsed) / 100

                Write-Message -Level Verbose -Message "Starting Size (MB): $startingSize"
                Write-Message -Level Verbose -Message "Starting Freespace (MB): $([int]$spaceAvailableMB)"
                Write-Message -Level Verbose -Message "Desired Freespace (MB): $([int]$desiredSpaceAvailable)"

                if (($db.SpaceAvailable / 1024) -le $desiredSpaceAvailable) {
                    Write-Message -Level Warning -Message "Space Available ($spaceAvailableMB) is less than or equal to the desired outcome ($desiredSpaceAvailable)"
                }
                else {
                    if ($Pscmdlet.ShouldProcess("$db on $instance", "Shrinking from $([int]$spaceAvailableMB) MB space available to $([int]$desiredSpaceAvailable) MB space available")) {
                        if ($server.VersionMajor -gt 8 -and $ExcludeIndexStats -eq $false) {
                            Write-Message -Level Verbose -Message "Getting starting average fragmentation"
                            $dataRow = $server.Query($sql, $db.name)
                            $startingFrag = $dataRow.avg_fragmentation_in_percent
                            $startingTopFrag = $dataRow.max_fragmentation_in_percent
                        }
                        else {
                            $startingTopFrag = $startingFrag = $null
                        }

                        $start = Get-Date

                        switch ($FileType) {
                            'Log' {
                                try {
                                    Write-Message -Level Verbose -Message "Beginning shrink of log files"
                                    $db.LogFiles.Shrink($desiredSpaceAvailable, $ShrinkMethod)
                                    $db.Refresh()
                                    $success = $true
                                    $notes = $null
                                }
                                catch {
                                    $success = $false
                                    Stop-Function -message "Shrink Failed:  $($_.Exception.InnerException)"  -EnableException $EnableException -ErrorRecord $_ -Continue
                                    continue
                                }
                            }
                            'Data' {
                                try {
                                    Write-Message -Level Verbose -Message "Beginning shrink of data files"
                                    foreach ($fileGroup in $db.FileGroups) {
                                        foreach ($file in $fileGroup.Files) {
                                            Write-Message -Level Verbose -Message "Beginning shrink of $($file.Name)"
                                            $file.Shrink($desiredSpaceAvailable, $ShrinkMethod)
                                        }
                                    }
                                    $db.Refresh()
                                    Write-Message -Level Verbose -Message "Recalculating space usage"
                                    if (-not $ExcludeUpdateUsage) { $db.RecalculateSpaceUsage() }
                                    $success = $true
                                    $notes = $null
                                }
                                catch {
                                    $success = $false
                                    Stop-Function -message "Shrink Failed:  $($_.Exception.InnerException)" -EnableException $EnableException -ErrorRecord $_ -Continue
                                    continue
                                }
                            }
                            default {
                                try {
                                    Write-Message -Level Verbose -Message "Beginning shrink of entire database"
                                    $db.Shrink($desiredSpaceAvailable, $ShrinkMethod)
                                    $db.Refresh()
                                    Write-Message -Level Verbose -Message "Recalculating space usage"
                                    if (-not $ExcludeUpdateUsage) { $db.RecalculateSpaceUsage() }
                                    $success = $true
                                    $notes = $null
                                }
                                catch {
                                    $success = $false
                                    Stop-Function -message "Shrink Failed:  $($_.Exception.InnerException)" -EnableException $EnableException -ErrorRecord $_ -Continue
                                    continue
                                }
                            }
                        }

                        $end = Get-Date
                        $dbSize = $db.Size
                        $newSpaceAvailableMB = $db.SpaceAvailable / 1024

                        Write-Message -Level Verbose -Message "Final database size: $([int]$dbSize) MB"
                        Write-Message -Level Verbose -Message "Final space available: $([int]$newSpaceAvailableMB) MB"

                        if ($server.VersionMajor -gt 8 -and $ExcludeIndexStats -eq $false -and $success -and $FileType -ne 'Log') {
                            Write-Message -Level Verbose -Message "Getting ending average fragmentation"
                            $dataRow = $server.Query($sql, $db.name)
                            $endingDefrag = $dataRow.avg_fragmentation_in_percent
                            $endingTopDefrag = $dataRow.max_fragmentation_in_percent
                        }
                        else {
                            $endingTopDefrag = $endingDefrag = $null
                        }

                        $timSpan = New-TimeSpan -Start $start -End $end
                        $ts = [TimeSpan]::fromseconds($timSpan.TotalSeconds)
                        $elapsed = "{0:HH:mm:ss}" -f ([datetime]$ts.Ticks)
                    }
                }

                if ($Pscmdlet.ShouldProcess("$db on $instance", "Showing results")) {
                    if ($null -eq $notes -and $FileType -ne 'Log') {
                        $notes = "Database shrinks can cause massive index fragmentation and negatively impact performance. You should now run DBCC INDEXDEFRAG or ALTER INDEX ... REORGANIZE"
                    }
                    $object = [PSCustomObject]@{
                        ComputerName                  = $server.ComputerName
                        InstanceName                  = $server.ServiceName
                        SqlInstance                   = $server.DomainInstanceName
                        Database                      = $db.name
                        Start                         = $start
                        End                           = $end
                        Elapsed                       = $elapsed
                        Success                       = $success
                        StartingTotalSizeMB           = [math]::Round($startingSize, 2)
                        StartingUsedMB                = [math]::Round($spaceUsed, 2)
                        FinalTotalSizeMB              = [math]::Round($db.size, 2)
                        StartingAvailableMB           = [math]::Round($spaceAvailableMB, 2)
                        DesiredAvailableMB            = [math]::Round($desiredSpaceAvailable, 2)
                        FinalAvailableMB              = [math]::Round(($db.SpaceAvailable / 1024), 2)
                        StartingAvgIndexFragmentation = [math]::Round($startingFrag, 1)
                        EndingAvgIndexFragmentation   = [math]::Round($endingDefrag, 1)
                        StartingTopIndexFragmentation = [math]::Round($startingTopFrag, 1)
                        EndingTopIndexFragmentation   = [math]::Round($endingTopDefrag, 1)
                        Notes                         = $notes
                    }

                    if ($ExcludeIndexStats) {
                        Select-DefaultView -InputObject $object -ExcludeProperty StartingAvgIndexFragmentation, EndingAvgIndexFragmentation, StartingTopIndexFragmentation, EndingTopIndexFragmentation
                    }
                    else {
                        $object
                    }
                }
            }
        }
    }
}

tools\dbatools\functions\Invoke-DbaDatabaseUpgrade.ps1
function Invoke-DbaDatabaseUpgrade {
    <#
    .SYNOPSIS
    Take a database and upgrades it to compatibility of the SQL Instance its hosted on. Based on https://thomaslarock.com/2014/06/upgrading-to-sql-server-2014-a-dozen-things-to-check/

    .DESCRIPTION
    Updates compatibility level, then runs CHECKDB with data_purity, DBCC updateusage, sp_updatestats and finally sp_refreshview against all user views.

    .PARAMETER SqlInstance
    The SQL Server that you're connecting to.

    .PARAMETER SqlCredential
    SqlCredential object used to connect to the SQL Server as a different user.

    .PARAMETER Database
    The database(s) to process - this list is autopopulated from the server. If unspecified, all databases will be processed.

    .PARAMETER ExcludeDatabase
    The database(s) to exclude - this list is autopopulated from the server

    .PARAMETER AllUserDatabases
    Run command against all user databases

    .PARAMETER Force
    Don't skip over databases that are already at the same level the instance is

    .PARAMETER NoCheckDb
    Skip checkdb

    .PARAMETER NoUpdateUsage
    Skip usage update

    .PARAMETER NoUpdateStats
    Skip stats update

    .PARAMETER NoRefreshView
    Skip view update

    .PARAMETER InputObject
    A collection of databases (such as returned by Get-DbaDatabase)

    .PARAMETER WhatIf
    Shows what would happen if the command were to run

    .PARAMETER Confirm
    Prompts for confirmation of every step. For example:

    Are you sure you want to perform this action?
    Performing the operation "Update database" on target "pubs on SQL2016\VNEXT".
    [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):

    .PARAMETER EnableException
    By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
    Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Tags: Shrink, Database
        Author: Stephen Bennett, https://sqlnotesfromtheunderground.wordpress.com/

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT


    .LINK
        https://dbatools.io/Invoke-DbaDatabaseUpgrade

    .EXAMPLE
        Invoke-DbaDatabaseUpgrade -SqlInstance PRD-SQL-MSD01 -Database Test

        Runs the below processes against the databases
        -- Puts compatibility of database to level of SQL Instance
        -- Runs CHECKDB DATA_PURITY
        -- Runs DBCC UPDATESUSAGE
        -- Updates all users statistics
        -- Runs sp_refreshview against every view in the database

    .EXAMPLE
        Invoke-DbaDatabaseUpgrade -SqlInstance PRD-SQL-INT01 -Database Test -NoRefreshView

        Runs the upgrade command skipping the sp_refreshview update on all views

    .EXAMPLE
        Invoke-DbaDatabaseUpgrade -SqlInstance PRD-SQL-INT01 -Database Test -Force

        If database Test is already at the correct compatibility, runs every necessary step

    .EXAMPLE
        Get-DbaDatabase -SqlInstance sql2016 | Out-GridView -Passthru | Invoke-DbaDatabaseUpgrade

        Get only specific databases using GridView and pass those to Invoke-DbaDatabaseUpgrade
#>
    [CmdletBinding(SupportsShouldProcess)]
    Param (
        [parameter(Position = 0)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [System.Management.Automation.PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$NoCheckDb,
        [switch]$NoUpdateUsage,
        [switch]$NoUpdateStats,
        [switch]$NoRefreshView,
        [switch]$AllUserDatabases,
        [switch]$Force,
        [parameter(ValueFromPipeline)]
        [Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {

        if (Test-Bound -not 'SqlInstance', 'InputObject') {
            Write-Message -Level Warning -Message "You must specify either a SQL instance or pipe a database collection"
            continue
        }

        if (Test-Bound -not 'Database', 'InputObject', 'ExcludeDatabase', 'AllUserDatabases') {
            Write-Message -Level Warning -Message "You must explicitly specify a database. Use -Database, -ExcludeDatabase, -AllUserDatabases or pipe a database collection"
            continue
        }

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level VeryVerbose -Message "Connecting to <c='green'>$instance</c>" -Target $instance
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
                $server.ConnectionContext.StatementTimeout = [Int32]::MaxValue
            }
            catch {
                Stop-Function -Message "Failed to process Instance $Instance" -ErrorRecord $_ -Target $instance -Continue
            }
            $InputObject += $server.Databases | Where-Object IsAccessible
        }

        $InputObject = $InputObject | Where-Object { $_.IsSystemObject -eq $false }
        if ($Database) {
            $InputObject = $InputObject | Where-Object { $_.Name -contains $Database }
        }
        if ($ExcludeDatabase) {
            $InputObject = $InputObject | Where-Object { $_.Name -notcontains $ExcludeDatabase }
        }

        foreach ($db in $InputObject) {
            # create objects to use in updates
            $server = $db.Parent
            $ServerVersion = $server.VersionMajor
            Write-Message -Level Verbose -Message "SQL Server is using Version: $ServerVersion"

            $ogcompat = $db.CompatibilityLevel
            $dbname = $db.Name
            $dbversion = switch ($db.CompatibilityLevel) {
                "Version100" { 10 } # SQL Server 2008
                "Version110" { 11 } # SQL Server 2012
                "Version120" { 12 } # SQL Server 2014
                "Version130" { 13 } # SQL Server 2016
                "Version140" { 14 } # SQL Server 2017
                default { 9 } # SQL Server 2005
            }
            if (-not $Force) {
                # skip over databases at the correct level, unless -Force
                if ($dbversion -ge $ServerVersion) {
                    Write-Message -Level VeryVerbose -Message "Skipping $db because compatibility is at the correct level. Use -Force if you want to run all the additional steps"
                    continue
                }
            }
            Write-Message -Level Verbose -Message "Updating $db compatibility to SQL Instance level"
            if ($dbversion -lt $ServerVersion) {
                If ($Pscmdlet.ShouldProcess($server, "Updating $db version on $server from $dbversion to $ServerVersion")) {
                    $Comp = $ServerVersion * 10
                    $tsqlComp = "ALTER DATABASE $db SET COMPATIBILITY_LEVEL = $Comp"
                    try {
                        $db.ExecuteNonQuery($tsqlComp)
                        $comResult = $Comp
                    }
                    catch {
                        Write-Message -Level Warning -Message "Failed run Compatibility Upgrade" -ErrorRecord $_ -Target $instance
                        $comResult = "Fail"
                    }
                }
            }
            else {
                $comResult = "No change"
            }

            if (!($NoCheckDb)) {
                Write-Message -Level Verbose -Message "Updating $db with DBCC CHECKDB DATA_PURITY"
                If ($Pscmdlet.ShouldProcess($server, "Updating $db with DBCC CHECKDB DATA_PURITY")) {
                    $tsqlCheckDB = "DBCC CHECKDB ('$dbname') WITH DATA_PURITY, NO_INFOMSGS"
                    try {
                        $db.ExecuteNonQuery($tsqlCheckDB)
                        $DataPurityResult = "Success"
                    }
                    catch {
                        Write-Message -Level Warning -Message "Failed run DBCC CHECKDB with DATA_PURITY on $db" -ErrorRecord $_ -Target $instance
                        $DataPurityResult = "Fail"
                    }
                }
            }
            else {
                Write-Message -Level Verbose -Message "Ignoring CHECKDB DATA_PURITY"
            }

            if (!($NoUpdateUsage)) {
                Write-Message -Level Verbose -Message "Updating $db with DBCC UPDATEUSAGE"
                If ($Pscmdlet.ShouldProcess($server, "Updating $db with DBCC UPDATEUSAGE")) {
                    $tsqlUpdateUsage = "DBCC UPDATEUSAGE ($db) WITH NO_INFOMSGS;"
                    try {
                        $db.ExecuteNonQuery($tsqlUpdateUsage)
                        $UpdateUsageResult = "Success"
                    }
                    catch {
                        Write-Message -Level Warning -Message "Failed to run DBCC UPDATEUSAGE on $db" -ErrorRecord $_ -Target $instance
                        $UpdateUsageResult = "Fail"
                    }
                }
            }
            else {
                Write-Message -Level Verbose -Message "Ignore DBCC UPDATEUSAGE"
                $UpdateUsageResult = "Skipped"
            }

            if (!($NoUpdatestats)) {
                Write-Message -Level Verbose -Message "Updating $db statistics"
                If ($Pscmdlet.ShouldProcess($server, "Updating $db statistics")) {
                    $tsqlStats = "EXEC sp_updatestats;"
                    try {
                        $db.ExecuteNonQuery($tsqlStats)
                        $UpdateStatsResult = "Success"
                    }
                    catch {
                        Write-Message -Level Warning -Message "Failed to run sp_updatestats on $db" -ErrorRecord $_ -Target $instance
                        $UpdateStatsResult = "Fail"
                    }
                }
            }
            else {
                Write-Message -Level Verbose -Message "Ignoring sp_updatestats"
                $UpdateStatsResult = "Skipped"
            }

            if (!($NoRefreshView)) {
                Write-Message -Level Verbose -Message "Refreshing $db Views"
                $dbViews = $db.Views | Where-Object IsSystemObject -eq $false
                $RefreshViewResult = "Success"
                foreach ($dbview in $dbviews) {
                    $viewName = $dbView.Name
                    $viewSchema = $dbView.Schema
                    $fullName = $viewSchema + "." + $viewName

                    $tsqlupdateView = "EXECUTE sp_refreshview N'$fullName';  "

                    If ($Pscmdlet.ShouldProcess($server, "Refreshing view $fullName on $db")) {
                        try {
                            $db.ExecuteNonQuery($tsqlupdateView)
                        }
                        catch {
                            Write-Message -Level Warning -Message "Failed update view $fullName on $db" -ErrorRecord $_ -Target $instance
                            $RefreshViewResult = "Fail"
                        }
                    }
                }
            }
            else {
                Write-Message -Level Verbose -Message "Ignore View Refreshes"
                $RefreshViewResult = "Skipped"
            }

            If ($Pscmdlet.ShouldProcess("console", "Outputting object")) {
                $db.Refresh()

                [PSCustomObject]@{
                    ComputerName          = $server.ComputerName
                    InstanceName          = $server.ServiceName
                    SqlInstance           = $server.DomainInstanceName
                    Database              = $db.name
                    OriginalCompatibility = $ogcompat.ToString().Replace('Version', '')
                    CurrentCompatibility  = $db.CompatibilityLevel.ToString().Replace('Version', '')
                    Compatibility         = $comResult
                    DataPurity            = $DataPurityResult
                    UpdateUsage           = $UpdateUsageResult
                    UpdateStats           = $UpdateStatsResult
                    RefreshViews          = $RefreshViewResult
                }
            }
        }
    }
}
tools\dbatools\functions\Invoke-DbaDbDecryptObject.ps1
function Invoke-DbaDbDecryptObject {
    <#
        .SYNOPSIS
            Invoke-DbaDbDecryptObject returns the decrypted version of an object

        .DESCRIPTION
            When a procedure or a function is created with encryption and you lost the code you're in trouble.
            You cannot alter the object or view the definition.
            With this command you can search for the object and decrypt the it.

            The command will output the results to the console.
            There is an option to export all the results to a folder creating .sql files.

            Make sure the instance allowed dedicated administrator connections (DAC).
            The binary versions of the objects can only be retrieved using a DAC connection.
            You can check the DAC connection with:
            'Get-DbaSpConfigure -SqlInstance [yourinstance] -ConfigName RemoteDacConnectionsEnabled'
            It should say 1 in the ConfiguredValue.

            To change the configurations you can use the Set-DbaSpConfigure command:
            'Set-DbaSpConfigure -SqlInstance [yourinstance] -ConfigName RemoteDacConnectionsEnabled -Value 1'
            In some cases you may need to reboot the instance.

        .PARAMETER SqlInstance
            The target SQL Server instance

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Database to look through for the object.

        .PARAMETER ObjectName
            The name of the object to search for in the database.

        .PARAMETER EncodingType
            The encoding that's used to decrypt and encrypt values.

        .PARAMETER ExportDestination
            Used for exporting the results to.
            The destiation will use the instance name, database name and object type i.e.: C:\temp\decrypt\SQLDB1\DB1\StoredProcedure

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Encryption, Decrypt, Database
            Author: Sander Stad (@sqlstad, sqlstad.nl)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Invoke-DbaDbDecryptObject

        .EXAMPLE
            Invoke-DbaDbDecryptObject -SqlInstance SQLDB1 -Database DB1 -ObjectName Function1

            Decrypt object "Function1" in DB1 of instance SQLDB1 and output the data to the user.

        .EXAMPLE
            Invoke-DbaDbDecryptObject -SqlInstance SQLDB1 -Database DB1 -ObjectName Function1 -ExportDestination C:\temp\decrypt

            Decrypt object "Function1" in DB1 of instance SQLDB1 and output the data to the folder "C:\temp\decrypt".

        .EXAMPLE
            Invoke-DbaDbDecryptObject -SqlInstance SQLDB1 -Database DB1 -ExportDestination C:\temp\decrypt

            Decrypt all objects in DB1 of instance SQLDB1 and output the data to the folder "C:\temp\decrypt"

        .EXAMPLE
            Invoke-DbaDbDecryptObject -SqlInstance SQLDB1 -Database DB1 -ObjectName Function1, Function2

            Decrypt objects "Function1" and "Function2" and output the data to the user.

        .EXAMPLE
            "SQLDB1" | Invoke-DbaDbDecryptObject -Database DB1 -ObjectName Function1, Function2

            Decrypt objects "Function1" and "Function2" and output the data to the user using a pipeline for the instance.
    #>
   [CmdletBinding()]
    param(
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory = $true)]
        [object[]]$Database,
        [string[]]$ObjectName,
        [ValidateSet('ASCII', 'UTF8')]
        [string]$EncodingType = 'ASCII',
        [string]$ExportDestination,
        [switch]$EnableException
    )

    begin {

        function Invoke-DecryptData() {
            param(
                [parameter(Mandatory = $true)]
                [byte[]]$Secret,
                [parameter(Mandatory = $true)]
                [byte[]]$KnownPlain,
                [parameter(Mandatory = $true)]
                [byte[]]$KnownSecret
            )

            # Declare pointers
            [int]$i = 0

            # Loop through each of the characters and apply an XOR to decrypt the data
            $result = $(

                # Loop through the byte string
                while ($i -lt $Secret.Length) {

                    # Compare the byte string character to the key character using XOR
                    if ($i -lt $Secret.Length) {
                        $Secret[$i] -bxor $KnownPlain[$i] -bxor $KnownSecret[$i]
                    }

                    # Increment the byte string indicator
                    $i += 2

                } # end while loop

            ) # end data value

            # Get the string value from the data
            $decryptedData = $Encoding.GetString($result)

            # Return the decrypted data
            return $decryptedData
        }

        # Create array list to hold the results
        $objectCollection = New-Object System.Collections.ArrayList

        # Set the encoding
        if ($EncodingType -eq 'ASCII') {
            $encoding = [System.Text.Encoding]::ASCII
        }
        elseif ($EncodingType -eq 'UTF8') {
            $encoding = [System.Text.Encoding]::UTF8
        }

        # Check the export parameter
        if ($ExportDestination -and -not (Test-Path $ExportDestination)) {
            try {
                # Create the new destination
                New-Item -Path $ExportDestination -ItemType Directory -Force | Out-Null
            }
            catch {
                Stop-Function -Message "Couldn't create destination folder $ExportDestination" -ErrorRecord $_ -Target $instance -Continue
            }
        }

    }

    process {

        if (Test-FunctionInterrupt) { return }

        # Loop through all the instances
        foreach ($instance in $SqlInstance) {

            # Check the configuration of the intance to see if the DAC is enabled
            $config = Get-DbaSpConfigure -SqlInstance $instance -ConfigName RemoteDacConnectionsEnabled
            if ($config.ConfiguredValue -ne 1) {
                Stop-Function -Message "DAC is not enabled for instance $instance.`nPlease use 'Set-DbaSpConfigure -SqlInstance $instance -ConfigName RemoteDacConnectionsEnabled -Value 1' to configure the instance to allow DAC connections" -Target $instance -Continue
            }

            # Try to connect to instance
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = New-Object Microsoft.SqlServer.Management.Smo.Server "ADMIN:$instance"
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            # Get all the databases that compare to the database parameter
            $databaseCollection = $server.Databases | Where-Object {$_.Name -in $Database}

            # Loop through each of databases
            foreach ($db in $databaseCollection) {
                # Get the objects
                if($ObjectName){
                    $storedProcedures = @($db.StoredProcedures | Where-Object {$_.Name -in $ObjectName -and $_.IsEncrypted -eq $true} | Select-Object Name, Schema, @{N = "ObjectType"; E = {'StoredProcedure'}}, @{N = "SubType"; E = {''}})
                    $functions = @($db.UserDefinedFunctions | Where-Object {$_.Name -in $ObjectName -and $_.IsEncrypted -eq $true} | Select-Object Name, Schema, @{N = "ObjectType"; E = {"UserDefinedFunction"}}, @{N = "SubType"; E = {$_.FunctionType.ToString().Trim()}})
                    $views = @($db.Views | Where-Object {$_.Name -in $ObjectName -and $_.IsEncrypted -eq $true} | Select-Object Name, Schema, @{N = "ObjectType"; E = {'View'}}, @{N = "SubType"; E = {''}})
                }
                else{
                    # Get all encrypted objects
                    $storedProcedures = @($db.StoredProcedures | Where-Object {$_.IsEncrypted -eq $true} | Select-Object Name, Schema, @{N = "ObjectType"; E = {'StoredProcedure'}}, @{N = "SubType"; E = {''}})
                    $functions = @($db.UserDefinedFunctions | Where-Object {$_.IsEncrypted -eq $true} | Select-Object Name, Schema, @{N = "ObjectType"; E = {"UserDefinedFunction"}}, @{N = "SubType"; E = {$_.FunctionType.ToString().Trim()}})
                    $views = @($db.Views | Where-Object {$_.IsEncrypted -eq $true} | Select-Object Name, Schema, @{N = "ObjectType"; E = {'View'}}, @{N = "SubType"; E = {''}})
                }

                <# Get all the objects
                $storedProcedures = @($db.StoredProcedures | Where-Object {$_.Name -in $ObjectName -and $_.IsEncrypted -eq $true} | Select-Object Name, Schema, @{N = "ObjectType"; E = {'StoredProcedure'}}, @{N = "SubType"; E = {''}})
                $functions = @($db.UserDefinedFunctions | Where-Object {$_.Name -in $ObjectName -and $_.IsEncrypted -eq $true} | Select-Object Name, Schema, @{N = "ObjectType"; E = {"UserDefinedFunction"}}, @{N = "SubType"; E = {$_.FunctionType.ToString().Trim()}})
                $views = @($db.Views | Where-Object {$_.Name -in $ObjectName -and $_.IsEncrypted -eq $true} | Select-Object Name, Schema, @{N = "ObjectType"; E = {'View'}}, @{N = "SubType"; E = {''}})
                #>

                # Check if there are any objects
                if ($storedProcedures.Count -ge 1) {
                    $objectCollection += $storedProcedures
                }
                if ($functions.Count -ge 1) {
                    $objectCollection += $functions
                }
                if ($views.Count -ge 1) {
                    $objectCollection += $views
                }

                # Loop through all the objects
                foreach ($object in $objectCollection) {

                    # Setup the query to get the secret
                    $querySecret = "SELECT imageval AS Value FROM sys.sysobjvalues WHERE objid = OBJECT_ID('$($object.Name)')"

                    # Get the result of the secret query
                    try {
                        $secret = $server.Databases[$db.Name].Query($querySecret)
                    }
                    catch {
                        Stop-Function -Message "Couldn't retrieve secret from $instance" -ErrorRecord $_ -Target $instance -Continue
                    }

                    # Check if at least a value came back
                    if ($secret) {

                        # Setup a known plain command and get the binary version of it
                        switch ($object.ObjectType) {

                            'StoredProcedure' {
                                $queryKnownPlain = (" " * $secret.Value.Length) + "ALTER PROCEDURE $($object.Schema).$($object.Name) WITH ENCRYPTION AS RETURN 0;"
                            }
                            'UserDefinedFunction' {

                                switch ($object.SubType) {
                                    'Inline' {
                                        $queryKnownPlain = (" " * $secret.value.length) + "ALTER FUNCTION $($object.Schema).$($object.Name)() RETURNS TABLE WITH ENCRYPTION AS BEGIN RETURN SELECT 0 i END;"
                                    }
                                    'Scalar' {
                                        $queryKnownPlain = (" " * $secret.value.length) + "ALTER FUNCTION $($object.Schema).$($object.Name)() RETURNS INT WITH ENCRYPTION AS BEGIN RETURN 0 END;"
                                    }
                                    'Table' {
                                        $queryKnownPlain = (" " * $secret.value.length) + "ALTER FUNCTION $($object.Schema).$($object.Name)() RETURNS @r TABLE(i INT) WITH ENCRYPTION AS BEGIN RETURN END;"
                                    }
                                }
                            }
                            'View' {
                                $queryKnownPlain = (" " * $secret.Value.Length) + "ALTER VIEW $($object.Schema).$($object.Name) WITH ENCRYPTION AS SELECT NULL AS [Value];"
                            }
                        }

                        # Convert the known plain into binary
                        if ($queryKnownPlain) {
                            try {
                                $knownPlain = $encoding.GetBytes(($queryKnownPlain))
                            }
                            catch {
                                Stop-Function -Message "Couldn't convert the known plain to binary" -ErrorRecord $_ -Target $instance -Continue
                            }
                        }
                        else {
                            Stop-Function -Message "Something went wrong setting up the known plain" -ErrorRecord $_ -Target $instance -Continue
                        }

                        # Setup the query to change the object in SQL Server and roll it back getting the encrypted version
                        $queryKnownSecret = "
                            BEGIN TRANSACTION;
                                EXEC ('$queryKnownPlain');
                                SELECT imageval AS Value
                                FROM sys.sysobjvalues
                                WHERE objid = OBJECT_ID('$($object.Name)');
                            ROLLBACK;
                        "

                        # Get the result for the known encrypted
                        try {
                            $knownSecret = $server.Databases[$db.Name].Query($queryKnownSecret)
                        }
                        catch {
                            Stop-Function -Message "Couldn't retrieve known secret from $instance" -ErrorRecord $_ -Target $instance -Continue
                        }

                        # Get the result
                        $result = Invoke-DecryptData -Secret $secret.value -KnownPlain $knownPlain -KnownSecret $knownSecret.value

                        # Check if the results need to be exported
                        if ($ExportDestination) {
                            # make up the file name
                            $filename = "$($object.Schema).$($object.Name).sql"

                            # Check the export destination
                            if ($ExportDestination.EndsWith("\")) {
                                $destinationFolder = "$ExportDestination$instance\$($db.Name)\$($object.ObjectType)\"
                            }
                            else {
                                $destinationFolder = "$ExportDestination\$instance\$($db.Name)\$($object.ObjectType)\"
                            }

                            # Check if the destination folder exists
                            if (-not (Test-Path $destinationFolder)) {
                                try {
                                    # Create the new destination
                                    New-Item -Path $destinationFolder -ItemType Directory -Force:$Force | Out-Null
                                }
                                catch {
                                    Stop-Function -Message "Couldn't create destination folder $destinationFolder" -ErrorRecord $_ -Target $instance -Continue
                                }
                            }

                            # Combine the destination folder and the file name to get the path
                            $filePath = $destinationFolder + $filename

                            # Export the result
                            try {
                                $result | Out-File -FilePath $filePath -Force
                            }
                            catch {
                                Stop-Function -Message "Couldn't export the results of $($object.Name) to $filePath" -ErrorRecord $_ -Target $instance -Continue
                            }

                        }

                        # Add the results to the custom object
                        [PSCustomObject]@{
                                ComputerName    = $server.ComputerName
                                InstanceName    = $server.ServiceName
                                SqlInstance     = $server.DomainInstanceName
                                Database        = $db.Name
                                Type            = $object.ObjectType
                                Schema          = $object.Schema
                                Name            = $object.Name
                                FullName        = "$($object.Schema).$($object.Name)"
                                Script          = $result
                            }

                    } # end if secret

                } # end for each object

            } # end for each database

        } # end for each instance

    } # process

    end {
        if (Test-FunctionInterrupt) { return }

        Write-Message -Message "Finished decrypting data" -Level Verbose
    }
}
tools\dbatools\functions\Invoke-DbaDiagnosticQuery.ps1
function Invoke-DbaDiagnosticQuery {
    <#
    .SYNOPSIS
    Invoke-DbaDiagnosticQuery runs the scripts provided by Glenn Berry's DMV scripts on specified servers

    .DESCRIPTION
    This is the main function of the Sql Server Diagnostic Queries related functions in dbatools.
    The diagnostic queries are developed and maintained by Glenn Berry and they can be found here along with a lot of documentation:
    http://www.sqlskills.com/blogs/glenn/category/dmv-queries/

    The most recent version of the diagnostic queries are included in the dbatools module.
    But it is possible to download a newer set or a specific version to an alternative location and parse and run those scripts.
    It will run all or a selection of those scripts on one or multiple servers and return the result as a PowerShell Object

    .PARAMETER SqlInstance
    The target SQL Server. Can be either a string or SMO server

    .PARAMETER SqlCredential
    Allows alternative Windows or SQL login credentials to be used

    .PARAMETER Path
    Alternate path for the diagnostic scripts

    .PARAMETER Database
    The database(s) to process. If unspecified, all databases will be processed

    .PARAMETER ExcludeDatabase
    The database(s) to exclude

    .PARAMETER UseSelectionHelper
    Provides a gridview with all the queries to choose from and will run the selection made by the user on the Sql Server instance specified.

    .PARAMETER QueryName
    Only run specific query

    .PARAMETER InstanceOnly
    Run only instance level queries

    .PARAMETER DatabaseSpecific
    Run only database level queries

    .PARAMETER NoQueryTextColumn
    Use this switch to exclude the [Complete Query Text] column from relevant queries

    .PARAMETER NoPlanColumn
    Use this switch to exclude the [Query Plan] column from relevant queries

    .PARAMETER NoColumnParsing
    Does not parse the [Complete Query Text] and [Query Plan] columns and disregards the NoQueryTextColumn and NoColumnParsing switches

    .PARAMETER EnableException
    By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
    Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .PARAMETER Confirm
    Prompts to confirm certain actions

    .PARAMETER WhatIf
    Shows what would happen if the command would execute, but does not actually perform the command

    .PARAMETER OutputPath
    Directory to parsed diagnostict queries to. This will split them based on server, databasename, and query.

    .PARAMETER ExportQueries
    Use this switch to export the diagnostic queries to sql files. I
    nstead of running the queries, the server will be evaluated to find the appropriate queries to run based on SQL Version.
    These sql files will then be created in the OutputDirectory.



    .NOTES
    Tags: Database, DMV
    Author: André Kamman (@AndreKamman), http://clouddba.io
    Website: https://dbatools.io
    Copyright: (C) Chrissy LeMaire, [email protected]
    License: MIT https://opensource.org/licenses/MIT

    .LINK
    https://dbatools.io/Invoke-DbaDiagnosticQuery

    .EXAMPLE
    Invoke-DbaDiagnosticQuery -SqlInstance sql2016

    Run the selection made by the user on the Sql Server instance specified.

    .EXAMPLE
    Invoke-DbaDiagnosticQuery -SqlInstance sql2016 -UseSelectionHelper | Export-DbaDiagnosticQuery -Path C:\temp\gboutput

    Provides a gridview with all the queries to choose from and will run the selection made by the user on the SQL Server instance specified.
    Then it will export the results to Export-DbaDiagnosticQuery.

    .Example
    # Exporting Queries To SQL Files
    - Parse the appropriate diagnostic queries by connecting to server to get version matched queries
    - Instead of running the diagnostic queries, export them to SQL files

    # Export All Queries to Disk
    Invoke-DbaDiagnosticQuery -sqlinstance localhost -ExportQueries -outputpath "C:\temp\DiagnosticQueries"

    # Export Database Specific Queries for all User Dbs
    Invoke-DbaDiagnosticQuery -sqlinstance localhost -DatabaseSpecific -DatabaseName 'tempdb' -ExportQueries -outputpath "C:\temp\DiagnosticQueries"

    # Export Database Specific Queries For One Target Database
    Invoke-DbaDiagnosticQuery -sqlinstance localhost -DatabaseSpecific -DatabaseName 'tempdb' -ExportQueries -outputpath "C:\temp\DiagnosticQueries"

    # Export Database Specific Queries For One Target Database and One Specific Query
    Invoke-DbaDiagnosticQuery -sqlinstance localhost -DatabaseSpecific -DatabaseName 'tempdb' -ExportQueries -outputpath "C:\temp\DiagnosticQueries" -queryname 'Database-scoped Configurations'

    # Choose Queries To Export
    Invoke-DbaDiagnosticQuery -sqlinstance localhost -UseSelectionHelper

    This will export with SqlInstance, DatabaseName, and QueryName as appropriate based on query.

    .Example
    # Export Queries (not run) as returned object

    [System.Management.Automation.PSObject[]]$results = Invoke-DbaDiagnosticQuery -SqlInstance localhost -whatif

    Parse the appropriate diagnostic queries by connecting to server, and instead of running them, return as [pscustomobject[]] to work with further

    .Example
    # Database Specific Handling
    $results = Invoke-DbaDiagnosticQuery -SqlInstance $script:instance2 -DatabaseSpecific -queryname 'Database-scoped Configurations' -databasename $database

    Run diagnostic queries targeted at specific database, and only run database level queries against this database.

    #>

    [CmdletBinding(SupportsShouldProcess)]
    [outputtype([pscustomobject[]])]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,

        [Alias('DatabaseName')]
        [object[]]$Database,

        [object[]]$ExcludeDatabase,

        [Alias('Credential')]
        [PSCredential]$SqlCredential,
        [System.IO.FileInfo]$Path,
        [string[]]$QueryName,
        [switch]$UseSelectionHelper,
        [switch]$InstanceOnly,
        [switch]$DatabaseSpecific,
        [Switch]$NoQueryTextColumn,
        [Switch]$NoPlanColumn,
        [Switch]$NoColumnParsing,

        [string]$OutputPath,
        [switch]$ExportQueries,

        [switch][Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $ProgressId = Get-Random

        function Invoke-DiagnosticQuerySelectionHelper {
            [CmdletBinding()]
            Param (
                [parameter(Mandatory = $true)]
                $ParsedScript
            )

            $ParsedScript | Select-Object QueryNr, QueryName, DBSpecific, Description | Out-GridView -Title "Diagnostic Query Overview" -OutputMode Multiple | Sort-Object QueryNr | Select-Object -ExpandProperty QueryName

        }

        Write-Message -Level Verbose -Message "Interpreting DMV Script Collections"

        $module = Get-Module -Name dbatools
        $base = $module.ModuleBase

        if (!$Path) {
            $Path = "$base\bin\diagnosticquery"
        }

        $scriptversions = @()
        $scriptfiles = Get-ChildItem "$Path\SQLServerDiagnosticQueries_*_*.sql"

        if (!$scriptfiles) {
            Write-Message -Level Warning -Message "Diagnostic scripts not found in $Path. Using the ones within the module."

            $Path = "$base\bin\diagnosticquery"

            $scriptfiles = Get-ChildItem "$base\bin\diagnosticquery\SQLServerDiagnosticQueries_*_*.sql"
            if (!$scriptfiles) {
                Stop-Function -Message "Unable to download scripts, do you have an internet connection? $_" -ErrorRecord $_
                return
            }
        }

        [int[]]$filesort = $null

        foreach ($file in $scriptfiles) {
            $filesort += $file.BaseName.Split("_")[2]
        }

        $currentdate = $filesort | Sort-Object -Descending | Select-Object -First 1

        foreach ($file in $scriptfiles) {
            if ($file.BaseName.Split("_")[2] -eq $currentdate) {
                $parsedscript = Invoke-DbaDiagnosticQueryScriptParser -filename $file.fullname -NoQueryTextColumn:$NoQueryTextColumn -NoPlanColumn:$NoPlanColumn -NoColumnParsing:$NoColumnParsing

                $newscript = [pscustomobject]@{
                    Version = $file.Basename.Split("_")[1]
                    Script  = $parsedscript
                }
                $scriptversions += $newscript
            }
        }
    }

    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $SqlInstance) {
            $counter = 0
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            Write-Message -Level Verbose -Message "Collecting diagnostic query data from server: $instance"

            if ($server.VersionMinor -eq 50) {
                $version = "2008R2"
            }
            else {
                $version = switch ($server.VersionMajor) {
                    9 { "2005" }
                    10 { "2008" }
                    11 { "2012" }
                    12 { "2014" }
                    13 { "2016" }
                    14 { "2017" }
                }
            }

            if (!$instanceOnly) {
                if (-not $Database) {
                    $databases = (Get-DbaDatabase -SqlInstance $server -ExcludeAllSystemDb -ExcludeDatabase $ExcludeDatabase).Name
                }
                else {
                    $databases = (Get-DbaDatabase -SqlInstance $server -ExcludeAllSystemDb -Database $Database -ExcludeDatabase $ExcludeDatabase).Name
                }
            }

            $parsedscript = $scriptversions | Where-Object -Property Version -eq $version | Select-Object -ExpandProperty Script

            if ($null -eq $first) { $first = $true }
            if ($UseSelectionHelper -and $first) {
                $QueryName = Invoke-DiagnosticQuerySelectionHelper $parsedscript
                $first = $false
            }
            #since some database level queries can take longer (such as fragmentation) calculate progress with database specific queries * count of databases to run against into context
            $CountOfDatabases = ($databases).Count


            if ($QueryName.Count -ne 0) {
                #if running all queries, then calculate total to run by instance queries count + (db specific count * databases to run each against)
                $countDBSpecific = @($parsedscript | Where-Object {$_.QueryName -in $QueryName -and $_.DBSpecific -eq $true}).Count
                $countInstanceSpecific = @($parsedscript | Where-Object {$_.QueryName -in $QueryName -and $_.DBSpecific -eq $false}).Count
            }
            else {
                #if narrowing queries to database specific, calculate total to process based on instance queries count + (db specific count * databases to run each against)
                $countDBSpecific = @($parsedscript | Where-Object DBSpecific).Count
                $countInstanceSpecific = @($parsedscript | Where-Object DBSpecific -eq $false).Count

            }
            if (!$instanceonly -and !$DatabaseSpecific -and !$QueryName) {
                $scriptcount = $countInstanceSpecific + ($countDBSpecific * $CountOfDatabases )
            }
            elseif ($instanceOnly) {
                $scriptcount = $countInstanceSpecific
            }
            elseif ($DatabaseSpecific) {
                $scriptcount = $countDBSpecific * $CountOfDatabases
            }
            elseif ($QueryName.Count -ne 0) {
                $scriptcount = $countInstanceSpecific + ($countDBSpecific * $CountOfDatabases )


            }

            foreach ($scriptpart in $parsedscript) {

                if (($QueryName.Count -ne 0) -and ($QueryName -notcontains $scriptpart.QueryName)) { continue }
                if (!$scriptpart.DBSpecific -and !$DatabaseSpecific) {
                    if ($ExportQueries) {
                        $null = New-Item -Path $OutputPath -ItemType Directory -Force
                        $FileName = Remove-InvalidFileNameChars ('{0}.sql' -f $Scriptpart.QueryName)
                        $FullName = Join-Path $OutputPath $FileName
                        Write-Message -Level Verbose -Message  "Creating file: $FullName"
                        $scriptPart.Text | out-file -FilePath $FullName -Encoding UTF8 -force
                        continue
                    }

                    if ($PSCmdlet.ShouldProcess($instance, $scriptpart.QueryName)) {

                        if (-not $EnableException) {
                            $Counter++
                            Write-Progress -Id $ProgressId -ParentId 0 -Activity "Collecting diagnostic query data from $instance" -Status "Processing $counter of $scriptcount" -CurrentOperation $scriptpart.QueryName -PercentComplete (($counter / $scriptcount) * 100)
                        }

                        try {
                            $result = $server.Query($scriptpart.Text)
                            Write-Message -Level Verbose -Message "Processed $($scriptpart.QueryName) on $instance"
                            if (!$result) {
                                [pscustomobject]@{
                                    ComputerName     = $server.ComputerName
                                    InstanceName     = $server.ServiceName
                                    SqlInstance      = $server.DomainInstanceName
                                    Number           = $scriptpart.QueryNr
                                    Name             = $scriptpart.QueryName
                                    Description      = $scriptpart.Description
                                    DatabaseSpecific = $scriptpart.DBSpecific
                                    Database         = $null
                                    Notes            = "Empty Result for this Query"
                                    Result           = $null
                                }
                                Write-Message -Level Verbose -Message ("Empty result for Query {0} - {1} - {2}" -f $scriptpart.QueryNr, $scriptpart.QueryName, $scriptpart.Description)
                            }
                        }
                        catch {
                            Write-Message -Level Verbose -Message ('Some error has occured on Server: {0} - Script: {1}, result unavailable' -f $instance, $scriptpart.QueryName) -Target $instance -ErrorRecord $_
                        }
                        if ($result) {
                            [pscustomobject]@{
                                ComputerName     = $server.ComputerName
                                InstanceName     = $server.ServiceName
                                SqlInstance      = $server.DomainInstanceName
                                Number           = $scriptpart.QueryNr
                                Name             = $scriptpart.QueryName
                                Description      = $scriptpart.Description
                                DatabaseSpecific = $scriptpart.DBSpecific
                                Database         = $null
                                Notes            = $null
                                #Result           = Select-DefaultView -InputObject $result -Property *
                                #Not using Select-DefaultView because excluding the fields below doesn't seem to work
                                Result           = $result | Select-Object * -ExcludeProperty 'Item', 'RowError', 'RowState', 'Table', 'ItemArray', 'HasErrors'
                            }

                        }
                    }
                    else {
                        # if running WhatIf, then return the queries that would be run as an object, not just whatif output

                        [pscustomobject]@{
                            ComputerName     = $server.ComputerName
                            InstanceName     = $server.ServiceName
                            SqlInstance      = $server.DomainInstanceName
                            Number           = $scriptpart.QueryNr
                            Name             = $scriptpart.QueryName
                            Description      = $scriptpart.Description
                            DatabaseSpecific = $scriptpart.DBSpecific
                            Database         = $null
                            Notes            = "WhatIf - Bypassed Execution"
                            Result           = $null
                        }
                    }

                }
                elseif ($scriptpart.DBSpecific -and !$instanceOnly) {

                    foreach ($currentdb in $databases) {
                        if ($ExportQueries) {
                            $null = New-Item -Path $OutputPath -ItemType Directory -Force
                            $FileName = Remove-InvalidFileNameChars ('{0}-{1}-{2}.sql' -f $server.DomainInstanceName, $currentDb, $Scriptpart.QueryName)
                            $FullName = Join-Path $OutputPath $FileName
                            Write-Message -Level Verbose -Message  "Creating file: $FullName"
                            $scriptPart.Text | out-file -FilePath $FullName -encoding UTF8 -force
                            continue
                        }


                        if ($PSCmdlet.ShouldProcess(('{0} ({1})' -f $instance, $currentDb), $scriptpart.QueryName)) {

                            if (-not $EnableException) {
                                $Counter++
                                Write-Progress -Id $ProgressId -ParentId 0 -Activity "Collecting diagnostic query data from $($currentDb) on $instance" -Status ('Processing {0} of {1}' -f $counter, $scriptcount) -CurrentOperation $scriptpart.QueryName -PercentComplete (($Counter / $scriptcount) * 100)
                            }

                            Write-Message -Level Verbose -Message "Collecting diagnostic query data from $($currentDb) for $($scriptpart.QueryName) on $instance"
                            try {
                                $result = $server.Query($scriptpart.Text, $currentDb)
                                if (!$result) {
                                    [pscustomobject]@{
                                        ComputerName     = $server.ComputerName
                                        InstanceName     = $server.ServiceName
                                        SqlInstance      = $server.DomainInstanceName
                                        Number           = $scriptpart.QueryNr
                                        Name             = $scriptpart.QueryName
                                        Description      = $scriptpart.Description
                                        DatabaseSpecific = $scriptpart.DBSpecific
                                        Database         = $currentdb
                                        Notes            = "Empty Result for this Query"
                                        Result           = $null
                                    }
                                    Write-Message -Level Verbose -Message ("Empty result for Query {0} - {1} - {2}" -f $scriptpart.QueryNr, $scriptpart.QueryName, $scriptpart.Description) -Target $scriptpart -ErrorRecord $_
                                }
                            }
                            catch {
                                Write-Message -Level Verbose -Message ('Some error has occured on Server: {0} - Script: {1} - Database: {2}, result will not be saved' -f $instance, $scriptpart.QueryName, $currentDb) -Target $currentdb -ErrorRecord $_
                            }

                            if ($result){
                                [pscustomobject]@{
                                    ComputerName     = $server.ComputerName
                                    InstanceName     = $server.ServiceName
                                    SqlInstance      = $server.DomainInstanceName
                                    Number           = $scriptpart.QueryNr
                                    Name             = $scriptpart.QueryName
                                    Description      = $scriptpart.Description
                                    DatabaseSpecific = $scriptpart.DBSpecific
                                    Database         = $currentDb
                                    Notes            = $null
                                    #Result           = Select-DefaultView -InputObject $result -Property *
                                    #Not using Select-DefaultView because excluding the fields below doesn't seem to work
                                    Result           = $result | Select-Object * -ExcludeProperty 'Item', 'RowError', 'RowState', 'Table', 'ItemArray', 'HasErrors'
                                }
                            }
                        }
                        else {
                            # if running WhatIf, then return the queries that would be run as an object, not just whatif output

                            [pscustomobject]@{
                                ComputerName     = $server.ComputerName
                                InstanceName     = $server.ServiceName
                                SqlInstance      = $server.DomainInstanceName
                                Number           = $scriptpart.QueryNr
                                Name             = $scriptpart.QueryName
                                Description      = $scriptpart.Description
                                DatabaseSpecific = $scriptpart.DBSpecific
                                Database         = $null
                                Notes            = "WhatIf - Bypassed Execution"
                                Result           = $null
                            }
                        }
                    }
                }
            }
        }
    }
    end {
        Write-Progress -Id $ProgressId -Activity 'Invoke-DbaDiagnosticQuery' -Completed
    }
}
tools\dbatools\functions\Invoke-DbaLogShipping.ps1
function Invoke-DbaLogShipping {
    <#
        .SYNOPSIS
            Invoke-DbaLogShipping sets up log shipping for one or more databases

        .DESCRIPTION
            Invoke-DbaLogShipping helps to easily set up log shipping for one or more databases.

            This function will make a lot of decisions for you assuming you want default values like a daily interval for the schedules with a 15 minute interval on the day.
            There are some settings that cannot be made by the function and they need to be prepared before the function is executed.

            The following settings need to be made before log shipping can be initiated:
            - Backup destination (the folder and the privileges)
            - Copy destination (the folder and the privileges)

            * Privileges
            Make sure your agent service on both the primary and the secondary instance is an Active Directory account.
            Also have the credentials ready to set the folder permissions

            ** Network share
            The backup destination needs to be shared and have the share privileges of FULL CONTROL to Everyone.

            ** NTFS permissions
            The backup destination must have at least read/write permissions for the primary instance agent account.
            The backup destination must have at least read permissions for the secondary instance agent account.
            The copy destination must have at least read/write permission for the secondary instance agent acount.

        .PARAMETER SourceSqlInstance
            Source SQL Server instance which contains the databases to be log shipped.
            You must have sysadmin access and server version must be SQL Server version 2000 or greater.

        .PARAMETER DestinationSqlInstance
            Destination SQL Server instance which contains the databases to be log shipped.
            You must have sysadmin access and server version must be SQL Server version 2000 or greater.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER SourceCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER DestinationCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Database to set up log shipping for.

        .PARAMETER BackupNetworkPath
            The backup unc path to place the backup files. This is the root directory.
            A directory with the name of the database will be created in this path.

        .PARAMETER BackupLocalPath
            If the backup path is locally for the source server you can also set this value.

        .PARAMETER BackupJob
            Name of the backup that will be created in the SQL Server agent.
            The parameter works as a prefix where the name of the database will be added to the backup job name.
            The default is "LSBackup_[databasename]"

        .PARAMETER BackupRetention
            The backup retention period in minutes. Default is 4320 / 72 hours

        .PARAMETER BackupSchedule
            Name of the backup schedule created for the backup job.
            The parameter works as a prefix where the name of the database will be added to the backup job schedule name.
            Default is "LSBackupSchedule_[databasename]"

        .PARAMETER BackupScheduleDisabled
            Parameter to set the backup schedule to disabled upon creation.
            By default the schedule is enabled.

        .PARAMETER BackupScheduleFrequencyType
            A value indicating when a job is to be executed.
            Allowed values are "Daily", "AgentStart", "IdleComputer"

        .PARAMETER BackupScheduleFrequencyInterval
            The number of type periods to occur between each execution of the backup job.

        .PARAMETER BackupScheduleFrequencySubdayType
            Specifies the units for the sub-day FrequencyInterval.
            Allowed values are "Time", "Seconds", "Minutes", "Hours"

        .PARAMETER BackupScheduleFrequencySubdayInterval
            The number of sub-day type periods to occur between each execution of the backup job.

        .PARAMETER BackupScheduleFrequencyRelativeInterval
            A job's occurrence of FrequencyInterval in each month, if FrequencyInterval is 32 (monthlyrelative).

        .PARAMETER BackupScheduleFrequencyRecurrenceFactor
            The number of weeks or months between the scheduled execution of a job. FrequencyRecurrenceFactor is used only if FrequencyType is 8, "Weekly", 16, "Monthly", 32 or "MonthlyRelative".

        .PARAMETER BackupScheduleStartDate
            The date on which execution of a job can begin.

        .PARAMETER BackupScheduleEndDate
            The date on which execution of a job can stop.

        .PARAMETER BackupScheduleStartTime
            The time on any day to begin execution of a job. Format HHMMSS / 24 hour clock.
            Example: '010000' for 01:00:00 AM.
            Example: '140000' for 02:00:00 PM.

        .PARAMETER BackupScheduleEndTime
            The time on any day to end execution of a job. Format HHMMSS / 24 hour clock.
            Example: '010000' for 01:00:00 AM.
            Example: '140000' for 02:00:00 PM.

        .PARAMETER BackupThreshold
            Is the length of time, in minutes, after the last backup before a threshold alert error is raised.
            The default is 60.

        .PARAMETER CompressBackup
            Do the backups need to be compressed. By default the backups are not compressed.

        .PARAMETER CopyDestinationFolder
            The path to copy the transaction log backup files to. This is the root directory.
            A directory with the name of the database will be created in this path.

        .PARAMETER CopyJob
            Name of the copy job that will be created in the SQL Server agent.
            The parameter works as a prefix where the name of the database will be added to the copy job name.
            The default is "LSBackup_[databasename]"

        .PARAMETER CopyRetention
            The copy retention period in minutes. Default is 4320 / 72 hours

        .PARAMETER CopySchedule
            Name of the backup schedule created for the copy job.
            The parameter works as a prefix where the name of the database will be added to the copy job schedule name.
            Default is "LSCopy_[DestinationServerName]_[DatabaseName]"

        .PARAMETER CopyScheduleDisabled
            Parameter to set the copy schedule to disabled upon creation.
            By default the schedule is enabled.

        .PARAMETER CopyScheduleFrequencyType
            A value indicating when a job is to be executed.
            Allowed values are "Daily", "AgentStart", "IdleComputer"

        .PARAMETER CopyScheduleFrequencyInterval
            The number of type periods to occur between each execution of the copy job.

        .PARAMETER CopyScheduleFrequencySubdayType
            Specifies the units for the subday FrequencyInterval.
            Allowed values are "Time", "Seconds", "Minutes", "Hours"

        .PARAMETER CopyScheduleFrequencySubdayInterval
            The number of subday type periods to occur between each execution of the copy job.

        .PARAMETER CopyScheduleFrequencyRelativeInterval
            A job's occurrence of FrequencyInterval in each month, if FrequencyInterval is 32 (monthlyrelative).

        .PARAMETER CopyScheduleFrequencyRecurrenceFactor
            The number of weeks or months between the scheduled execution of a job. FrequencyRecurrenceFactor is used only if FrequencyType is 8, "Weekly", 16, "Monthly", 32 or "MonthlyRelative".

        .PARAMETER CopyScheduleStartDate
            The date on which execution of a job can begin.

        .PARAMETER CopyScheduleEndDate
            The date on which execution of a job can stop.

        .PARAMETER CopyScheduleStartTime
            The time on any day to begin execution of a job. Format HHMMSS / 24 hour clock.
            Example: '010000' for 01:00:00 AM.
            Example: '140000' for 02:00:00 PM.

        .PARAMETER CopyScheduleEndTime
            The time on any day to end execution of a job. Format HHMMSS / 24 hour clock.
            Example: '010000' for 01:00:00 AM.
            Example: '140000' for 02:00:00 PM.

        .PARAMETER DisconnectUsers
            If this parameter is set in combinations of standby the users will be disconnected during restore.

        .PARAMETER FullBackupPath
            Path to an existing full backup. Use this when an existing backup needs to used to initialize the database on the secondary instance.

        .PARAMETER GenerateFullBackup
            If the database is not initialized on the secondary instance it can be done by creating a new full backup and
            restore it for you.

        .PARAMETER HistoryRetention
            Is the length of time in minutes in which the history is retained.
            The default value is 14420

        .PARAMETER NoRecovery
            If this parameter is set the database will be in recovery mode. The database will not be readable.
            This setting is default.

        .PARAMETER NoInitialization
            If this parameter is set the secondary database will not be initialized.
            The database needs to be on the secondary instance in recovery mode.

        .PARAMETER PrimaryMonitorServer
            Is the name of the monitor server for the primary server.
            The default is the name of the primary sql server.

        .PARAMETER PrimaryMonitorCredential
            Allows you to login to enter a secure credential. Only needs to be used when the PrimaryMonitorServerSecurityMode is 0 or "sqlserver"
            To use: $scred = Get-Credential, then pass $scred object to the -PrimaryMonitorCredential parameter.

        .PARAMETER PrimaryMonitorServerSecurityMode
            The security mode used to connect to the monitor server for the primary server. Allowed values are 0, "sqlserver", 1, "windows"
            The default is 1 or Windows.

        .PARAMETER PrimaryThresholdAlertEnabled
            Enables the Threshold alert for the primary database

        .PARAMETER RestoreDataFolder
            Folder to be used to restore the database data files. Only used when parameter GenerateFullBackup or UseExistingFullBackup are set.
            If the parameter is not set the default data folder of the secondary instance will be used including the name of the database.
            If the folder is set but doesn't exist the default data folder of the secondary instance will be used including the name of the database.

        .PARAMETER RestoreLogFolder
            Folder to be used to restore the database log files. Only used when parameter GenerateFullBackup or UseExistingFullBackup are set.
            If the parameter is not set the default transaction log folder of the secondary instance will be used.
            If the folder is set but doesn't exist the default transaction log folder of the secondary instance will be used.

        .PARAMETER RestoreDelay
            In case a delay needs to be set for the restore.
            The default is 0.

        .PARAMETER RestoreAlertThreshold
            The amount of minutes after which an alert will be raised is no restore has taken place.
            The default is 45 minutes.

        .PARAMETER RestoreJob
            Name of the restore job that will be created in the SQL Server agent.
            The parameter works as a prefix where the name of the database will be added to the restore job name.
            The default is "LSRestore_[databasename]"

        .PARAMETER RestoreRetention
            The backup retention period in minutes. Default is 4320 / 72 hours

        .PARAMETER RestoreSchedule
            Name of the backup schedule created for the restore job.
            The parameter works as a prefix where the name of the database will be added to the restore job schedule name.
            Default is "LSRestore_[DestinationServerName]_[DatabaseName]"

        .PARAMETER RestoreScheduleDisabled
            Parameter to set the restore schedule to disabled upon creation.
            By default the schedule is enabled.

        .PARAMETER RestoreScheduleFrequencyType
            A value indicating when a job is to be executed.
            Allowed values are "Daily", "AgentStart", "IdleComputer"

        .PARAMETER RestoreScheduleFrequencyInterval
            The number of type periods to occur between each execution of the restore job.

        .PARAMETER RestoreScheduleFrequencySubdayType
            Specifies the units for the subday FrequencyInterval.
            Allowed values are "Time", "Seconds", "Minutes", "Hours"

        .PARAMETER RestoreScheduleFrequencySubdayInterval
            The number of subday type periods to occur between each execution of the restore job.

        .PARAMETER RestoreScheduleFrequencyRelativeInterval
            A job's occurrence of FrequencyInterval in each month, if FrequencyInterval is 32 (monthlyrelative).

        .PARAMETER RestoreScheduleFrequencyRecurrenceFactor
            The number of weeks or months between the scheduled execution of a job. FrequencyRecurrenceFactor is used only if FrequencyType is 8, "Weekly", 16, "Monthly", 32 or "MonthlyRelative".

        .PARAMETER RestoreScheduleStartDate
            The date on which execution of a job can begin.

        .PARAMETER RestoreScheduleEndDate
            The date on which execution of a job can stop.

        .PARAMETER RestoreScheduleStartTime
            The time on any day to begin execution of a job. Format HHMMSS / 24 hour clock.
            Example: '010000' for 01:00:00 AM.
            Example: '140000' for 02:00:00 PM.

        .PARAMETER RestoreScheduleEndTime
            The time on any day to end execution of a job. Format HHMMSS / 24 hour clock.
            Example: '010000' for 01:00:00 AM.
            Example: '140000' for 02:00:00 PM.

        .PARAMETER RestoreThreshold
            The number of minutes allowed to elapse between restore operations before an alert is generated.
            The default value = 0

        .PARAMETER SecondaryDatabasePrefix
            The secondary database can be renamed to include a prefix.

        .PARAMETER SecondaryDatabaseSuffix
            The secondary database can be renamed to include a suffix.

        .PARAMETER SecondaryMonitorServer
            Is the name of the monitor server for the secondary server.
            The default is the name of the secondary sql server.

        .PARAMETER SecondaryMonitorCredential
            Allows you to login to enter a secure credential. Only needs to be used when the SecondaryMonitorServerSecurityMode is 0 or "sqlserver"
            To use: $scred = Get-Credential, then pass $scred object to the -SecondaryMonitorCredential parameter.

        .PARAMETER SecondaryMonitorServerSecurityMode
            The security mode used to connect to the monitor server for the secondary server. Allowed values are 0, "sqlserver", 1, "windows"
            The default is 1 or Windows.

        .PARAMETER SecondaryThresholdAlertEnabled
            Enables the Threshold alert for the secondary database

        .PARAMETER Standby
            If this parameter is set the database will be set to standby mode making the database readable.
            If not set the database will be in recovery mode.

        .PARAMETER StandbyDirectory
            Directory to place the standby file(s) in

        .PARAMETER UseExistingFullBackup
            If the database is not initialized on the secondary instance it can be done by selecting an existing full backup
            and restore it for you.

        .PARAMETER UseBackupFolder
            This enables the user to specify a specific backup folder containing one or more backup files to initialize the database on the secondary instance.

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            Use this switch to disable any kind of verbose messages

        .PARAMETER Force
            The force parameter will ignore some errors in the parameters and assume defaults.
            It will also remove the any present schedules with the same name for the specific job.

        .NOTES
            Tags: LogShipping
            Author: Sander Stad (@sqlstad, sqlstad.nl)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Invoke-DbaLogShipping

        .EXAMPLE
            $params = @{
                SourceSqlInstance = 'sql1'
                DestinationSqlInstance = 'sql2'
                Database = 'db1'
                BackupNetworkPath= '\\sql1\logshipping'
                BackupLocalPath= 'D:\Data\logshipping'
                BackupScheduleFrequencyType = 'daily'
                BackupScheduleFrequencyInterval = 1
                CompressBackup = $true
                CopyScheduleFrequencyType = 'daily'
                CopyScheduleFrequencyInterval = 1
                GenerateFullBackup = $true
                RestoreScheduleFrequencyType = 'daily'
                RestoreScheduleFrequencyInterval = 1
                SecondaryDatabaseSuffix = 'DR'
                CopyDestinationFolder = '\\sql2\logshippingdest'
                Force = $true
            }

            Invoke-DbaLogShipping @params

            Sets up log shipping for database "db1" with the backup path to a network share allowing local backups.
            It creates daily schedules for the backup, copy and restore job with all the defaults to be executed every 15 minutes daily.
            The secondary database will be called "db1_LS".

        .EXAMPLE
            $params = @{
                SourceSqlInstance = 'sql1'
                DestinationSqlInstance = 'sql2'
                Database = 'db1'
                BackupNetworkPath= '\\sql1\logshipping'
                GenerateFullBackup = $true
                Force = $true
            }

            Invoke-DbaLogShipping @params

            Sets up log shipping with all defaults except that a backup file is generated.
            The script will show a message that the copy destination has not been supplied and asks if you want to use the default which would be the backup directory of the secondary server with the folder "logshipping" i.e. "D:\SQLBackup\Logshiping".
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]

    param(
        [parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [Alias("SourceServerInstance", "SourceSqlServerSqlServer", "Source")]
        [object]$SourceSqlInstance,

        [parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [Alias("DestinationServerInstance", "DestinationSqlServer", "Destination")]
        [object]$DestinationSqlInstance,

        [Parameter(Mandatory = $false)]
        [System.Management.Automation.PSCredential]
        $SourceSqlCredential,

        [Parameter(Mandatory = $false)]
        [System.Management.Automation.PSCredential]
        $SourceCredential,

        [Parameter(Mandatory = $false)]
        [System.Management.Automation.PSCredential]
        $DestinationSqlCredential,

        [Parameter(Mandatory = $false)]
        [System.Management.Automation.PSCredential]
        $DestinationCredential,

        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [object[]]$Database,

        [parameter(Mandatory = $true)]
        [string]$BackupNetworkPath,

        [parameter(Mandatory = $false)]
        [string]$BackupLocalPath,

        [parameter(Mandatory = $false)]
        [string]$BackupJob,

        [parameter(Mandatory = $false)]
        [int]$BackupRetention,

        [parameter(Mandatory = $false)]
        [string]$BackupSchedule,

        [parameter(Mandatory = $false)]
        [switch]$BackupScheduleDisabled,

        [parameter(Mandatory = $false)]
        [ValidateSet("Daily", "Weekly", "AgentStart", "IdleComputer")]
        [object]$BackupScheduleFrequencyType,

        [parameter(Mandatory = $false)]
        [object[]]$BackupScheduleFrequencyInterval,

        [parameter(Mandatory = $false)]
        [ValidateSet('Time', 'Seconds', 'Minutes', 'Hours')]
        [object]$BackupScheduleFrequencySubdayType,

        [parameter(Mandatory = $false)]
        [int]$BackupScheduleFrequencySubdayInterval,

        [Parameter(Mandatory = $false)]
        [ValidateSet('Unused', 'First', 'Second', 'Third', 'Fourth', 'Last')]
        [object]$BackupScheduleFrequencyRelativeInterval,

        [Parameter(Mandatory = $false)]
        [int]$BackupScheduleFrequencyRecurrenceFactor,

        [parameter(Mandatory = $false)]
        [string]$BackupScheduleStartDate,

        [parameter(Mandatory = $false)]
        [string]$BackupScheduleEndDate,

        [parameter(Mandatory = $false)]
        [string]$BackupScheduleStartTime,

        [parameter(Mandatory = $false)]
        [string]$BackupScheduleEndTime,

        [parameter(Mandatory = $false)]
        [int]$BackupThreshold,

        [parameter(Mandatory = $false)]
        [switch]$CompressBackup,

        [parameter(Mandatory = $false)]
        [string]$CopyDestinationFolder,

        [parameter(Mandatory = $false)]
        [string]$CopyJob,

        [parameter(Mandatory = $false)]
        [int]$CopyRetention,

        [parameter(Mandatory = $false)]
        [string]$CopySchedule,

        [parameter(Mandatory = $false)]
        [switch]$CopyScheduleDisabled,

        [parameter(Mandatory = $false)]
        [ValidateSet("Daily", "Weekly", "AgentStart", "IdleComputer")]
        [object]$CopyScheduleFrequencyType,

        [parameter(Mandatory = $false)]
        [object[]]$CopyScheduleFrequencyInterval,

        [parameter(Mandatory = $false)]
        [ValidateSet('Time', 'Seconds', 'Minutes', 'Hours')]
        [object]$CopyScheduleFrequencySubdayType,

        [parameter(Mandatory = $false)]
        [int]$CopyScheduleFrequencySubdayInterval,

        [Parameter(Mandatory = $false)]
        [ValidateSet('Unused', 'First', 'Second', 'Third', 'Fourth', 'Last')]
        [object]$CopyScheduleFrequencyRelativeInterval,

        [Parameter(Mandatory = $false)]
        [int]$CopyScheduleFrequencyRecurrenceFactor,

        [parameter(Mandatory = $false)]
        [string]$CopyScheduleStartDate,

        [parameter(Mandatory = $false)]
        [string]$CopyScheduleEndDate,

        [parameter(Mandatory = $false)]
        [string]$CopyScheduleStartTime,

        [parameter(Mandatory = $false)]
        [string]$CopyScheduleEndTime,

        [parameter(Mandatory = $false)]
        [switch]$DisconnectUsers,

        [parameter(Mandatory = $false)]
        [string]$FullBackupPath,

        [parameter(Mandatory = $false)]
        [switch]$GenerateFullBackup,

        [parameter(Mandatory = $false)]
        [int]$HistoryRetention,

        [parameter(Mandatory = $false)]
        [switch]$NoRecovery,

        [parameter(Mandatory = $false)]
        [switch]$NoInitialization,

        [Parameter(Mandatory = $false)]
        [string]$PrimaryMonitorServer,

        [Parameter(Mandatory = $false)]
        [System.Management.Automation.PSCredential]
        $PrimaryMonitorCredential,

        [Parameter(Mandatory = $false)]
        [ValidateSet(0, "sqlserver", 1, "windows")]
        [object]$PrimaryMonitorServerSecurityMode,

        [Parameter(Mandatory = $false)]
        [switch]$PrimaryThresholdAlertEnabled,

        [parameter(Mandatory = $false)]
        [string]$RestoreDataFolder,

        [parameter(Mandatory = $false)]
        [string]$RestoreLogFolder,

        [parameter(Mandatory = $false)]
        [int]$RestoreDelay,

        [parameter(Mandatory = $false)]
        [int]$RestoreAlertThreshold,

        [parameter(Mandatory = $false)]
        [string]$RestoreJob,

        [parameter(Mandatory = $false)]
        [int]$RestoreRetention,

        [parameter(Mandatory = $false)]
        [string]$RestoreSchedule,

        [parameter(Mandatory = $false)]
        [switch]$RestoreScheduleDisabled,

        [parameter(Mandatory = $false)]
        [ValidateSet("Daily", "Weekly", "AgentStart", "IdleComputer")]
        [object]$RestoreScheduleFrequencyType,

        [parameter(Mandatory = $false)]
        [object[]]$RestoreScheduleFrequencyInterval,

        [parameter(Mandatory = $false)]
        [ValidateSet('Time', 'Seconds', 'Minutes', 'Hours')]
        [object]$RestoreScheduleFrequencySubdayType,

        [parameter(Mandatory = $false)]
        [int]$RestoreScheduleFrequencySubdayInterval,

        [Parameter(Mandatory = $false)]
        [ValidateSet('Unused', 'First', 'Second', 'Third', 'Fourth', 'Last')]
        [object]$RestoreScheduleFrequencyRelativeInterval,

        [Parameter(Mandatory = $false)]
        [int]$RestoreScheduleFrequencyRecurrenceFactor,

        [parameter(Mandatory = $false)]
        [string]$RestoreScheduleStartDate,

        [parameter(Mandatory = $false)]
        [string]$RestoreScheduleEndDate,

        [parameter(Mandatory = $false)]
        [string]$RestoreScheduleStartTime,

        [parameter(Mandatory = $false)]
        [string]$RestoreScheduleEndTime,

        [parameter(Mandatory = $false)]
        [int]$RestoreThreshold,

        [parameter(Mandatory = $false)]
        [string]$SecondaryDatabasePrefix,

        [parameter(Mandatory = $false)]
        [string]$SecondaryDatabaseSuffix,

        [Parameter(Mandatory = $false)]
        [string]$SecondaryMonitorServer,

        [Parameter(Mandatory = $false)]
        [System.Management.Automation.PSCredential]
        $SecondaryMonitorCredential,

        [Parameter(Mandatory = $false)]
        [ValidateSet(0, "sqlserver", 1, "windows")]
        [object]$SecondaryMonitorServerSecurityMode,

        [Parameter(Mandatory = $false)]
        [switch]$SecondaryThresholdAlertEnabled,

        [parameter(Mandatory = $false)]
        [switch]$Standby,

        [parameter(Mandatory = $false)]
        [string]$StandbyDirectory,

        [parameter(Mandatory = $false)]
        [switch]$UseExistingFullBackup,

        [parameter(Mandatory = $false)]
        [string]$UseBackupFolder,

        [switch]$Force,

        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Write-Message -Message "Started log shipping for $SourceSqlInstance to $DestinationSqlInstance" -Level Output

        # Try connecting to the instance
        Write-Message -Message "Connecting to source Sql Server $SourceSqlInstance.." -Level Output
        try {
            $SourceServer = Connect-SqlInstance -SqlInstance $SourceSqlInstance -SqlCredential $SourceSqlCredential
        }
        catch {
            Stop-Function -Message "Could not connect to Sql Server instance $SourceSqlInstance" -ErrorRecord $_ -Target $SourceSqlInstance
            return
        }

        # Try connecting to the instance
        Write-Message -Message "Connecting to destination Sql Server $DestinationSqlInstance.." -Level Output
        try {
            $DestinationServer = Connect-SqlInstance -SqlInstance $DestinationSqlInstance -SqlCredential $DestinationSqlCredential
        }
        catch {
            Stop-Function -Message "Could not connect to Sql Server instance $DestinationSqlInstance" -ErrorRecord $_ -Target $DestinationSqlInstance
            return
        }

        # Check the instance if it is a named instance
        $SourceServerName, $SourceInstanceName = $SourceSqlInstance.Split("\")
        $DestinationServerName, $DestinationInstanceName = $DestinationSqlInstance.Split("\")

        if ($SourceInstanceName -eq $null) {
            $SourceInstanceName = "MSSQLSERVER"
        }

        if ($DestinationInstanceName -eq $null) {
            $DestinationInstanceName = "MSSQLSERVER"
        }

        $IsSourceLocal = $false
        $IsDestinationLocal = $false

        # Check if it's local or remote
        if ($SourceServerName -in ".", "localhost", $env:ServerName, "127.0.0.1") {
            $IsSourceLocal = $true
        }
        if ($DestinationServerName -in ".", "localhost", $env:ServerName, "127.0.0.1") {
            $IsDestinationLocal = $true
        }

        # Set up regex strings for several checks
        $RegexDate = '(?<!\d)(?:(?:(?:1[6-9]|[2-9]\d)?\d{2})(?:(?:(?:0[13578]|1[02])31)|(?:(?:0[1,3-9]|1[0-2])(?:29|30)))|(?:(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))0229)|(?:(?:1[6-9]|[2-9]\d)?\d{2})(?:(?:0?[1-9])|(?:1[0-2]))(?:0?[1-9]|1\d|2[0-8]))(?!\d)'
        $RegexTime = '^(?:(?:([01]?\d|2[0-3]))?([0-5]?\d))?([0-5]?\d)$'
        $RegexUnc = '^\\(?:\\[^<>:`"/\\|?*]+)+$'

        # Check the instance names and the database settings
        if (($SourceSqlInstance -eq $DestinationSqlInstance) -and (-not $SecondaryDatabasePrefix -or $SecondaryDatabaseSuffix)) {
            Stop-Function -Message "The destination database is the same as the source`nPlease enter a prefix or suffix using -SecondaryDatabasePrefix or -SecondaryDatabaseSuffix." -Target $SourceSqlInstance
            return
        }

        # Check the connection timeout
        if ($SourceServer.ConnectionContext.StatementTimeout -ne 0) {
            $SourceServer.ConnectionContext.StatementTimeout = 0
            Write-Message -Message "Connection timeout of $SourceServer is set to 0" -Level Verbose
        }

        if ($DestinationServer.ConnectionContext.StatementTimeout -ne 0) {
            $DestinationServer.ConnectionContext.StatementTimeout = 0
            Write-Message -Message "Connection timeout of $DestinationServer is set to 0" -Level Verbose
        }

        # Check the backup network path
        Write-Message -Message "Testing backup network path $BackupNetworkPath" -Level Verbose
        if ((Test-DbaSqlPath -Path $BackupNetworkPath -SqlInstance $SourceSqlInstance -SqlCredential $SourceCredential) -ne $true) {
            Stop-Function -Message "Backup network path $BackupNetworkPath is not valid or can't be reached." -Target $SourceSqlInstance
            return
        }
        elseif ($BackupNetworkPath -notmatch $RegexUnc) {
            Stop-Function -Message "Backup network path $BackupNetworkPath has to be in the form of \\server\share." -Target $SourceSqlInstance
            return
        }

        # Check the copy destination
        if (-not $CopyDestinationFolder) {
            # Make a default copy destination by retrieving the backup folder and adding a directory
            $CopyDestinationFolder = "$($DestinationServer.Settings.BackupDirectory)\Logshipping"

            # Check to see if the path already exists
            Write-Message -Message "Testing copy destination path $CopyDestinationFolder" -Level Verbose
            if (Test-DbaSqlPath -Path $CopyDestinationFolder -SqlInstance $DestinationSqlInstance -SqlCredential $DestinationCredential) {
                Write-Message -Message "Copy destination $CopyDestinationFolder already exists" -Level Verbose
            }
            else {
                # Check if force is being used
                if (-not $Force) {
                    # Set up the confirm part
                    $message = "The copy destination is missing. Do you want to use the default $($CopyDestinationFolder)?"
                    $choiceYes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Answer Yes."
                    $choiceNo = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Answer No."
                    $options = [System.Management.Automation.Host.ChoiceDescription[]]($choiceYes, $choiceNo)
                    $result = $host.ui.PromptForChoice($title, $message, $options, 0)

                    # Check the result from the confirm
                    switch ($result) {
                        # If yes
                        0 {
                            # Try to create the new directory
                            try {
                                # If the destination server is remote and the credential is set
                                if (-not $IsDestinationLocal -and $DestinationCredential) {
                                    Invoke-Command2 -ComputerName $DestinationServerName -Credential $DestinationCredential -ScriptBlock {
                                        Write-Message -Message "Creating copy destination folder $CopyDestinationFolder" -Level Verbose
                                        New-Item -Path $CopyDestinationFolder -ItemType Directory -Credential $DestinationCredential -Force:$Force | Out-Null
                                    }
                                }
                                # If the server is local and the credential is set
                                elseif ($DestinationCredential) {
                                    Invoke-Command2 -Credential $DestinationCredential -ScriptBlock {
                                        Write-Message -Message "Creating copy destination folder $CopyDestinationFolder" -Level Verbose
                                        New-Item -Path $CopyDestinationFolder -ItemType Directory -Credential $DestinationCredential -Force:$Force | Out-Null
                                    }
                                }
                                # If the server is local and the credential is not set
                                else {
                                    Write-Message -Message "Creating copy destination folder $CopyDestinationFolder" -Level Verbose
                                    New-Item -Path $CopyDestinationFolder -Force:$Force -ItemType Directory | Out-Null
                                }
                                Write-Message -Message "Copy destination $CopyDestinationFolder created." -Level Verbose
                            }
                            catch {
                                Stop-Function -Message "Something went wrong creating the copy destination folder $CopyDestinationFolder. `n$_" -Target $DestinationSqlInstance -ErrorRecord $_
                                return
                            }
                        }
                        1 {
                            Stop-Function -Message "Copy destination is a mandatory parameter. Please make sure the value is entered." -Target $DestinationSqlInstance
                            return
                        }
                    } # switch
                } # if not force
                else {
                    # Try to create the copy destination on the local server
                    try {
                        Write-Message -Message "Creating copy destination folder $CopyDestinationFolder" -Level Verbose
                        New-Item $CopyDestinationFolder -ItemType Directory -Credential $DestinationCredential -Force:$Force | Out-Null
                        Write-Message -Message "Copy destination $CopyDestinationFolder created." -Level Verbose
                    }
                    catch {
                        Stop-Function -Message "Something went wrong creating the copy destination folder $CopyDestinationFolder. `n$_" -Target $DestinationSqlInstance -ErrorRecord $_
                        return
                    }
                } # else not force
            } # if test path copy destination
        } # if not copy destination

        Write-Message -Message "Testing copy destination path $CopyDestinationFolder" -Level Verbose
        if ((Test-DbaSqlPath -Path $CopyDestinationFolder -SqlInstance $DestinationSqlInstance -SqlCredential $DestinationCredential) -ne $true) {
            Stop-Function -Message "Copy destination folder $CopyDestinationFolder is not valid or can't be reached." -Target $DestinationSqlInstance
            return
        }
        elseif ($CopyDestinationFolder.StartsWith("\\") -and $CopyDestinationFolder -notmatch $RegexUnc) {
            Stop-Function -Message "Copy destination folder $CopyDestinationFolder has to be in the form of \\server\share." -Target $DestinationSqlInstance
            return
        }

        # Check the backup compression
        if ($SourceServer.Version.Major -gt 9) {
            if ($CompressBackup) {
                Write-Message -Message "Setting backup compression to 1." -Level Verbose
                [bool]$BackupCompression = 1
            }
            else {
                $backupServerSetting = (Get-DbaSpConfigure -SqlInstance $SourceSqlInstance -ConfigName DefaultBackupCompression).ConfiguredValue
                Write-Message -Message "Setting backup compression to default server setting $backupServerSetting." -Level Verbose
                [bool]$BackupCompression = $backupServerSetting
            }
        }
        else {
            Write-Message -Message "Source server $SourceServer does not support backup compression" -Level Verbose
        }

        # Check the database parameter
        if ($Database) {
            foreach ($db in $Database) {
                if ($db -notin $SourceServer.Databases.Name) {
                    Stop-Function -Message "Database $db cannot be found on instance $SourceSqlInstance" -Target $SourceSqlInstance
                }

                $DatabaseCollection = $SourceServer.Databases | Where-Object { $_.Name -in $Database }
            }
        }
        else {
            Stop-Function -Message "Please supply a database to set up log shipping for" -Target $SourceSqlInstance -Continue
        }

        # Set the database mode
        if ($Standby) {
            $DatabaseStatus = 1
            Write-Message -Message "Destination database status set to STANDBY" -Level Verbose
        }
        else {
            $DatabaseStatus = 0
            Write-Message -Message "Destination database status set to NO RECOVERY" -Level Verbose
        }

        # Setting defaults
        if (-not $BackupRetention) {
            $BackupRetention = 4320
            Write-Message -Message "Backup retention set to $BackupRetention" -Level Verbose
        }
        if (-not $BackupThreshold) {
            $BackupThreshold = 60
            Write-Message -Message "Backup Threshold set to $BackupThreshold" -Level Verbose
        }
        if (-not $CopyRetention) {
            $CopyRetention = 4320
            Write-Message -Message "Copy retention set to $CopyRetention" -Level Verbose
        }
        if (-not $HistoryRetention) {
            $HistoryRetention = 14420
            Write-Message -Message "History retention set to $HistoryRetention" -Level Verbose
        }
        if (-not $RestoreAlertThreshold) {
            $RestoreAlertThreshold = 45
            Write-Message -Message "Restore alert Threshold set to $RestoreAlertThreshold" -Level Verbose
        }
        if (-not $RestoreDelay) {
            $RestoreDelay = 0
            Write-Message -Message "Restore delay set to $RestoreDelay" -Level Verbose
        }
        if (-not $RestoreRetention) {
            $RestoreRetention = 4320
            Write-Message -Message "Restore retention set to $RestoreRetention" -Level Verbose
        }
        if (-not $RestoreThreshold) {
            $RestoreThreshold = 0
            Write-Message -Message "Restore Threshold set to $RestoreThreshold" -Level Verbose
        }
        if (-not $PrimaryMonitorServerSecurityMode) {
            $PrimaryMonitorServerSecurityMode = 1
            Write-Message -Message "Primary monitor server security mode set to $PrimaryMonitorServerSecurityMode" -Level Verbose
        }
        if (-not $SecondaryMonitorServerSecurityMode) {
            $SecondaryMonitorServerSecurityMode = 1
            Write-Message -Message "Secondary monitor server security mode set to $SecondaryMonitorServerSecurityMode" -Level Verbose
        }
        if (-not $BackupScheduleFrequencyType) {
            $BackupScheduleFrequencyType = "Daily"
            Write-Message -Message "Backup frequency type set to $BackupScheduleFrequencyType" -Level Verbose
        }
        if (-not $BackupScheduleFrequencyInterval) {
            $BackupScheduleFrequencyInterval = "EveryDay"
            Write-Message -Message "Backup frequency interval set to $BackupScheduleFrequencyInterval" -Level Verbose
        }
        if (-not $BackupScheduleFrequencySubdayType) {
            $BackupScheduleFrequencySubdayType = "Minutes"
            Write-Message -Message "Backup frequency subday type set to $BackupScheduleFrequencySubdayType" -Level Verbose
        }
        if (-not $BackupScheduleFrequencySubdayInterval) {
            $BackupScheduleFrequencySubdayInterval = 15
            Write-Message -Message "Backup frequency subday interval set to $BackupScheduleFrequencySubdayInterval" -Level Verbose
        }
        if (-not $BackupScheduleFrequencyRelativeInterval) {
            $BackupScheduleFrequencyRelativeInterval = "Unused"
            Write-Message -Message "Backup frequency relative interval set to $BackupScheduleFrequencyRelativeInterval" -Level Verbose
        }
        if (-not $BackupScheduleFrequencyRecurrenceFactor) {
            $BackupScheduleFrequencyRecurrenceFactor = 0
            Write-Message -Message "Backup frequency recurrence factor set to $BackupScheduleFrequencyRecurrenceFactor" -Level Verbose
        }
        if (-not $CopyScheduleFrequencyType) {
            $CopyScheduleFrequencyType = "Daily"
            Write-Message -Message "Copy frequency type set to $CopyScheduleFrequencyType" -Level Verbose
        }
        if (-not $CopyScheduleFrequencyInterval) {
            $CopyScheduleFrequencyInterval = "EveryDay"
            Write-Message -Message "Copy frequency interval set to $CopyScheduleFrequencyInterval" -Level Verbose
        }
        if (-not $CopyScheduleFrequencySubdayType) {
            $CopyScheduleFrequencySubdayType = "Minutes"
            Write-Message -Message "Copy frequency subday type set to $CopyScheduleFrequencySubdayType" -Level Verbose
        }
        if (-not $CopyScheduleFrequencySubdayInterval) {
            $CopyScheduleFrequencySubdayInterval = 15
            Write-Message -Message "Copy frequency subday interval set to $CopyScheduleFrequencySubdayInterval" -Level Verbose
        }
        if (-not $CopyScheduleFrequencyRelativeInterval) {
            $CopyScheduleFrequencyRelativeInterval = "Unused"
            Write-Message -Message "Copy frequency relative interval set to $CopyScheduleFrequencyRelativeInterval" -Level Verbose
        }
        if (-not $CopyScheduleFrequencyRecurrenceFactor) {
            $CopyScheduleFrequencyRecurrenceFactor = 0
            Write-Message -Message "Copy frequency recurrence factor set to $CopyScheduleFrequencyRecurrenceFactor" -Level Verbose
        }
        if (-not $RestoreScheduleFrequencyType) {
            $RestoreScheduleFrequencyType = "Daily"
            Write-Message -Message "Restore frequency type set to $RestoreScheduleFrequencyType" -Level Verbose
        }
        if (-not $RestoreScheduleFrequencyInterval) {
            $RestoreScheduleFrequencyInterval = "EveryDay"
            Write-Message -Message "Restore frequency interval set to $RestoreScheduleFrequencyInterval" -Level Verbose
        }
        if (-not $RestoreScheduleFrequencySubdayType) {
            $RestoreScheduleFrequencySubdayType = "Minutes"
            Write-Message -Message "Restore frequency subday type set to $RestoreScheduleFrequencySubdayType" -Level Verbose
        }
        if (-not $RestoreScheduleFrequencySubdayInterval) {
            $RestoreScheduleFrequencySubdayInterval = 15
            Write-Message -Message "Restore frequency subday interval set to $RestoreScheduleFrequencySubdayInterval" -Level Verbose
        }
        if (-not $RestoreScheduleFrequencyRelativeInterval) {
            $RestoreScheduleFrequencyRelativeInterval = "Unused"
            Write-Message -Message "Restore frequency relative interval set to $RestoreScheduleFrequencyRelativeInterval" -Level Verbose
        }
        if (-not $RestoreScheduleFrequencyRecurrenceFactor) {
            $RestoreScheduleFrequencyRecurrenceFactor = 0
            Write-Message -Message "Restore frequency recurrence factor set to $RestoreScheduleFrequencyRecurrenceFactor" -Level Verbose
        }
        if (-not ($SecondaryDatabasePrefix -or $SecondaryDatabaseSuffix) -and ($SourceServer.Name -eq $DestinationServer.Name) -and ($SourceServer.InstanceName -eq $DestinationServer.InstanceName)) {
            if ($Force) {
                $SecondaryDatabaseSuffix = "_LS"
            }
            else {
                Stop-Function -Message "Destination database is the same as source database.`nPlease check the secondary server, database prefix or suffix or use -Force to set the secondary databse using a suffix." -Target $SourceSqlInstance
                return
            }
        }

        # Checking for contradicting variables
        if ($NoInitialization -and ($GenerateFullBackup -or $UseExistingFullBackup)) {
            Stop-Function -Message "Cannot use -NoInitialization with -GenerateFullBackup or -UseExistingFullBackup" -Target $DestinationSqlInstance
            return
        }

        if ($UseBackupFolder -and ($GenerateFullBackup -or $NoInitialization -or $UseExistingFullBackup)) {
            Stop-Function -Message "Cannot use -UseBackupFolder with -GenerateFullBackup, -NoInitialization or -UseExistingFullBackup" -Target $DestinationSqlInstance
            return
        }

        # Check the subday interval
        if (($BackupScheduleFrequencySubdayType -in 2, "Seconds", 4, "Minutes") -and (-not ($BackupScheduleFrequencySubdayInterval -ge 1 -or $BackupScheduleFrequencySubdayInterval -le 59))) {
            Stop-Function -Message "Backup subday interval $BackupScheduleFrequencySubdayInterval must be between 1 and 59 when subday type is 2, 'Seconds', 4 or 'Minutes'" -Target $SourceSqlInstance
            return
        }
        elseif (($BackupScheduleFrequencySubdayType -in 8, "Hours") -and (-not ($BackupScheduleFrequencySubdayInterval -ge 1 -and $BackupScheduleFrequencySubdayInterval -le 23))) {
            Stop-Function -Message "Backup Subday interval $BackupScheduleFrequencySubdayInterval must be between 1 and 23 when subday type is 8 or 'Hours" -Target $SourceSqlInstance
            return
        }

        # Check the subday interval
        if (($CopyScheduleFrequencySubdayType -in 2, "Seconds", 4, "Minutes") -and (-not ($CopyScheduleFrequencySubdayInterval -ge 1 -or $CopyScheduleFrequencySubdayInterval -le 59))) {
            Stop-Function -Message "Copy subday interval $CopyScheduleFrequencySubdayInterval must be between 1 and 59 when subday type is 2, 'Seconds', 4 or 'Minutes'" -Target $DestinationSqlInstance
            return
        }
        elseif (($CopyScheduleFrequencySubdayType -in 8, "Hours") -and (-not ($CopyScheduleFrequencySubdayInterval -ge 1 -and $CopyScheduleFrequencySubdayInterval -le 23))) {
            Stop-Function -Message "Copy subday interval $CopyScheduleFrequencySubdayInterval must be between 1 and 23 when subday type is 8 or 'Hours'" -Target $DestinationSqlInstance
            return
        }

        # Check the subday interval
        if (($RestoreScheduleFrequencySubdayType -in 2, "Seconds", 4, "Minutes") -and (-not ($RestoreScheduleFrequencySubdayInterval -ge 1 -or $RestoreScheduleFrequencySubdayInterval -le 59))) {
            Stop-Function -Message "Restore subday interval $RestoreScheduleFrequencySubdayInterval must be between 1 and 59 when subday type is 2, 'Seconds', 4 or 'Minutes'" -Target $DestinationSqlInstance
            return
        }
        elseif (($RestoreScheduleFrequencySubdayType -in 8, "Hours") -and (-not ($RestoreScheduleFrequencySubdayInterval -ge 1 -and $RestoreScheduleFrequencySubdayInterval -le 23))) {
            Stop-Function -Message "Restore subday interval $RestoreScheduleFrequencySubdayInterval must be between 1 and 23 when subday type is 8 or 'Hours" -Target $DestinationSqlInstance
            return
        }

        # Check the backup start date
        if (-not $BackupScheduleStartDate) {
            $BackupScheduleStartDate = (Get-Date -format "yyyyMMdd")
            Write-Message -Message "Backup start date set to $BackupScheduleStartDate" -Level Verbose
        }
        else {
            if ($BackupScheduleStartDate -notmatch $RegexDate) {
                Stop-Function -Message "Backup start date $BackupScheduleStartDate needs to be a valid date with format yyyyMMdd" -Target $SourceSqlInstance
                return
            }
        }

        # Check the back start time
        if (-not $BackupScheduleStartTime) {
            $BackupScheduleStartTime = '000000'
            Write-Message -Message "Backup start time set to $BackupScheduleStartTime" -Level Verbose
        }
        elseif ($BackupScheduleStartTime -notmatch $RegexTime) {
            Stop-Function -Message  "Backup start time $BackupScheduleStartTime needs to match between '000000' and '235959'" -Target $SourceSqlInstance
            return
        }

        # Check the back end time
        if (-not $BackupScheduleEndTime) {
            $BackupScheduleEndTime = '235959'
            Write-Message -Message "Backup end time set to $BackupScheduleEndTime" -Level Verbose
        }
        elseif ($BackupScheduleStartTime -notmatch $RegexTime) {
            Stop-Function -Message  "Backup end time $BackupScheduleStartTime needs to match between '000000' and '235959'" -Target $SourceSqlInstance
            return
        }

        # Check the backup end date
        if (-not $BackupScheduleEndDate) {
            $BackupScheduleEndDate = '99991231'
        }
        elseif ($BackupScheduleEndDate -notmatch $RegexDate) {
            Stop-Function -Message "Backup end date $BackupScheduleEndDate needs to be a valid date with format yyyyMMdd" -Target $SourceSqlInstance
            return
        }

        # Check the copy start date
        if (-not $CopyScheduleStartDate) {
            $CopyScheduleStartDate = (Get-Date -format "yyyyMMdd")
            Write-Message -Message "Copy start date set to $CopyScheduleStartDate" -Level Verbose
        }
        else {
            if ($CopyScheduleStartDate -notmatch $RegexDate) {
                Stop-Function -Message "Copy start date $CopyScheduleStartDate needs to be a valid date with format yyyyMMdd" -Target $SourceSqlInstance
                return
            }
        }

        # Check the copy end date
        if (-not $CopyScheduleEndDate) {
            $CopyScheduleEndDate = '99991231'
        }
        elseif ($CopyScheduleEndDate -notmatch $RegexDate) {
            Stop-Function -Message "Copy end date $CopyScheduleEndDate needs to be a valid date with format yyyyMMdd" -Target $SourceSqlInstance
            return
        }

        # Check the copy start time
        if (-not $CopyScheduleStartTime) {
            $CopyScheduleStartTime = '000000'
            Write-Message -Message "Copy start time set to $CopyScheduleStartTime" -Level Verbose
        }
        elseif ($CopyScheduleStartTime -notmatch $RegexTime) {
            Stop-Function -Message  "Copy start time $CopyScheduleStartTime needs to match between '000000' and '235959'" -Target $SourceSqlInstance
            return
        }

        # Check the copy end time
        if (-not $CopyScheduleEndTime) {
            $CopyScheduleEndTime = '235959'
            Write-Message -Message "Copy end time set to $CopyScheduleEndTime" -Level Verbose
        }
        elseif ($CopyScheduleEndTime -notmatch $RegexTime) {
            Stop-Function -Message  "Copy end time $CopyScheduleEndTime needs to match between '000000' and '235959'" -Target $SourceSqlInstance
            return
        }

        # Check the restore start date
        if (-not $RestoreScheduleStartDate) {
            $RestoreScheduleStartDate = (Get-Date -format "yyyyMMdd")
            Write-Message -Message "Restore start date set to $RestoreScheduleStartDate" -Level Verbose
        }
        else {
            if ($RestoreScheduleStartDate -notmatch $RegexDate) {
                Stop-Function -Message "Restore start date $RestoreScheduleStartDate needs to be a valid date with format yyyyMMdd" -Target $SourceSqlInstance
                return
            }
        }

        # Check the restore end date
        if (-not $RestoreScheduleEndDate) {
            $RestoreScheduleEndDate = '99991231'
        }
        elseif ($RestoreScheduleEndDate -notmatch $RegexDate) {
            Stop-Function -Message "Restore end date $RestoreScheduleEndDate needs to be a valid date with format yyyyMMdd" -Target $SourceSqlInstance
            return
        }

        # Check the restore start time
        if (-not $RestoreScheduleStartTime) {
            $RestoreScheduleStartTime = '000000'
            Write-Message -Message "Restore start time set to $RestoreScheduleStartTime" -Level Verbose
        }
        elseif ($RestoreScheduleStartTime -notmatch $RegexTime) {
            Stop-Function -Message  "Restore start time $RestoreScheduleStartTime needs to match between '000000' and '235959'" -Target $SourceSqlInstance
            return
        }

        # Check the restore end time
        if (-not $RestoreScheduleEndTime) {
            $RestoreScheduleEndTime = '235959'
            Write-Message -Message "Restore end time set to $RestoreScheduleEndTime" -Level Verbose
        }
        elseif ($RestoreScheduleEndTime -notmatch $RegexTime) {
            Stop-Function -Message  "Restore end time $RestoreScheduleEndTime needs to match between '000000' and '235959'" -Target $SourceSqlInstance
            return
        }

        # Check if standby is being used
        if ($Standby) {

            # Check the stand-by directory
            if ($StandbyDirectory) {
                # Check if the path is reachable for the destination server
                if ((Test-DbaSqlPath -Path $StandbyDirectory -SqlInstance $DestinationSqlInstance -SqlCredential $DestinationCredential) -ne $true) {
                    Stop-Function -Message "The directory $StandbyDirectory cannot be reached by the destination instance. Please check the permission and credentials." -Target $DestinationSqlInstance
                    return
                }
            }
            elseif (-not $StandbyDirectory -and $Force) {
                $StandbyDirectory = $DestinationSqlInstance.BackupDirectory
                Write-Message -Message "Stand-by directory was not set. Setting it to $StandbyDirectory" -Level Verbose
            }
            else {
                Stop-Function -Message "Please set the parameter -StandbyDirectory when using -Standby" -Target $SourceSqlInstance
                return
            }
        }
    } # begin

    process {

        if (Test-FunctionInterrupt) { return }

        # Loop through each of the databases
        foreach ($db in $DatabaseCollection) {

            # Check the status of the database
            if ($db.RecoveryModel -ne 'Full') {
                Stop-Function -Message  "Database $db is not in FULL recovery mode" -Target $SourceSqlInstance -Continue
            }

            # Set the intital destination database
            $SecondaryDatabase = $db.Name

            # Set the database prefix
            if ($SecondaryDatabasePrefix) {
                $SecondaryDatabase = "$SecondaryDatabasePrefix$($db.Name)"
            }

            # Set the database suffix
            if ($SecondaryDatabaseSuffix) {
                $SecondaryDatabase += $SecondaryDatabaseSuffix
            }

            # Check is the database is already initialized an check if the database exists on the secondary instance
            if ($NoInitialization -and ($DestinationServer.Databases.Name -notcontains $SecondaryDatabase)) {
                Stop-Function -Message "Database $SecondaryDatabase needs to be initialized before log shipping setting can continue." -Target $SourceSqlInstance -Continue
            }

            # Check the local backup path
            if ($BackupLocalPath) {
                if ($BackupLocalPath.EndsWith("\")) {
                    $DatabaseBackupLocalPath = "$BackupLocalPath$($db.Name)"
                }
                else {
                    $DatabaseBackupLocalPath = "$BackupLocalPath\$($db.Name)"
                }
            }
            else {
                $BackupLocalPath = $BackupNetworkPath

                if ($BackupLocalPath.EndsWith("\")) {
                    $DatabaseBackupLocalPath = "$BackupLocalPath$($db.Name)"
                }
                else {
                    $DatabaseBackupLocalPath = "$BackupLocalPath\$($db.Name)"
                }
            }
            Write-Message -Message "Backup local path set to $DatabaseBackupLocalPath." -Level Verbose

            # Setting the backup network path for the database
            if ($BackupNetworkPath.EndsWith("\")) {
                $DatabaseBackupNetworkPath = "$BackupNetworkPath$($db.Name)"
            }
            else {
                $DatabaseBackupNetworkPath = "$BackupNetworkPath\$($db.Name)"
            }
            Write-Message -Message "Backup network path set to $DatabaseBackupNetworkPath." -Level Verbose


            # Checking if the database network path exists
            Write-Message -Message "Testing database backup network path $DatabaseBackupNetworkPath" -Level Verbose
            if ((Test-DbaSqlPath -Path $DatabaseBackupNetworkPath -SqlInstance $SourceSqlInstance -SqlCredential $SourceCredential) -ne $true) {
                # To to create the backup directory for the database
                try {
                    Write-Message -Message "Database backup network path $DatabaseBackupNetworkPath not found. Trying to create it.." -Level Verbose

                    Invoke-Command2 -Credential $SourceCredential -ScriptBlock {
                        Write-Message -Message "Creating backup folder $DatabaseBackupNetworkPath" -Level Verbose
                        New-Item -Path $DatabaseBackupNetworkPath -ItemType Directory -Credential $SourceCredential -Force:$Force | Out-Null
                    }
                }
                catch {
                    Stop-Function -Message "Something went wrong creating the directory" -ErrorRecord $_ -Target $SourceSqlInstance -Continue
                }
            }

            # Check if the backup job name is set
            if ($BackupJob) {
                $DatabaseBackupJob = "$BackupJob_$($db.Name)"
            }
            else {
                $DatabaseBackupJob = "LSBackup_$($db.Name)"
            }
            Write-Message -Message "Backup job name set to $DatabaseBackupJob" -Level Verbose

            # Check if the backup job schedule name is set
            if ($BackupSchedule) {
                $DatabaseBackupSchedule = "$BackupSchedule_$($db.Name)"
            }
            else {
                $DatabaseBackupSchedule = "LSBackupSchedule_$($db.Name)"
            }
            Write-Message -Message "Backup job schedule name set to $DatabaseBackupSchedule" -Level Verbose

            # Check if secondary database is present on secondary instance
            if (-not $Force -and -not $NoInitialization -and ($DestinationServer.Databases[$SecondaryDatabase].Status -ne 'Restoring') -and ($DestinationServer.Databases.Name -contains $SecondaryDatabase)) {
                Stop-Function -Message "Secondary database already exists on instance $DestinationSqlInstance." -ErrorRecord $_ -Target $DestinationSqlInstance -Continue
            }

            # Check if the secondary database needs tobe initialized
            if (-not $NoInitialization) {
                # Check if the secondary database exists on the secondary instance
                if ($DestiationServer.Databases.Name -notcontains $SecondaryDatabase) {
                    # Check if force is being used and no option to generate the full backup is set
                    if ($Force -and -not ($GenerateFullBackup -or $UseExistingFullBackup)) {
                        # Set the option to generate a full backup
                        Write-Message -Message "Set option to initialize secondary database with full backup" -Level Verbose
                        $GenerateFullBackup = $true
                    }
                    elseif (-not $Force -and -not $GenerateFullBackup -and -not $UseExistingFullBackup -and -not $UseBackupFolder) {
                        # Set up the confirm part
                        $message = "The database $SecondaryDatabase does not exist on instance $DestinationSqlInstance. `nDo you want to initialize it by generating a full backup?"
                        $choiceYes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Answer Yes."
                        $choiceNo = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Answer No."
                        $options = [System.Management.Automation.Host.ChoiceDescription[]]($choiceYes, $choiceNo)
                        $result = $host.ui.PromptForChoice($title, $message, $options, 0)

                        # Check the result from the confirm
                        switch ($result) {
                            # If yes
                            0 {
                                # Set the option to generate a full backup
                                Write-Message -Message "Set option to initialize secondary database with full backup." -Level Verbose
                                $GenerateFullBackup = $true
                            }
                            1 {
                                Stop-Function -Message "The database is not initialized on the secondary instance. `nPlease initialize the database on the secondary instance, use -GenerateFullbackup or use -Force." -Target $DestinationSqlInstance
                                return
                            }
                        } # switch
                    }
                }
            }


            # Check the parameters for initialization of the secondary database
            if (-not $NoInitialization -and ($GenerateFullBackup -or $UseExistingFullBackup -or $UseBackupFolder)) {
                # Check if the restore data and log folder are set
                if (-not $RestoreDataFolder -or -not $RestoreLogFolder) {
                    Write-Message -Message "Restore data folder or restore log folder are not set. Using server defaults" -Level Verbose

                    # Get the default data folder
                    if (-not $RestoreDataFolder) {
                        $DatabaseRestoreDataFolder = $DestinationServer.DefaultFile
                    }
                    else {
                        # Set the restore data folder
                        if ($RestoreDataFolder.EndsWith("\")) {
                            $DatabaseRestoreDataFolder = "$RestoreDataFolder$($db.Name)"
                        }
                        else {
                            $DatabaseRestoreDataFolder = "$RestoreDataFolder\$($db.Name)"
                        }
                    }

                    Write-Message -Message "Restore data folder set to $DatabaseRestoreDataFolder" -Level Verbose

                    # Get the default log folder
                    if (-not $RestoreLogFolder) {
                        $DatabaseRestoreLogFolder = $DestinationServer.DefaultLog
                    }

                    Write-Message -Message "Restore log folder set to $DatabaseRestoreLogFolder" -Level Verbose

                    # Check if the restore data folder exists
                    Write-Message -Message "Testing database restore data path $DatabaseRestoreDataFolder" -Level Verbose
                    if ((Test-DbaSqlPath  -Path $DatabaseRestoreDataFolder -SqlInstance $DestinationSqlInstance -SqlCredential $DestinationCredential) -ne $true) {
                        if ($PSCmdlet.ShouldProcess($DestinationServerName, "Creating database restore data folder $DatabaseRestoreDataFolder on $DestinationServerName")) {
                            # Try creating the data folder
                            try {
                                Invoke-Command2 -Credential $DestinationCredential -ScriptBlock {
                                    Write-Message -Message "Creating data folder $DatabaseRestoreDataFolder" -Level Verbose
                                    New-Item -Path $DatabaseRestoreDataFolder -ItemType Directory -Credential $DestinationCredential -Force:$Force | Out-Null
                                }
                            }
                            catch {
                                Stop-Function -Message "Something went wrong creating the restore data directory" -ErrorRecord $_ -Target $SourceSqlInstance -Continue
                            }
                        }
                    }

                    # Check if the restore log folder exists
                    Write-Message -Message "Testing database restore log path $DatabaseRestoreLogFolder" -Level Verbose
                    if ((Test-DbaSqlPath  -Path $DatabaseRestoreLogFolder -SqlInstance $DestinationSqlInstance -SqlCredential $DestinationCredential) -ne $true) {
                        if ($PSCmdlet.ShouldProcess($DestinationServerName, "Creating database restore log folder $DatabaseRestoreLogFolder on $DestinationServerName")) {
                            # Try creating the log folder
                            try {
                                Write-Message -Message "Restore log folder $DatabaseRestoreLogFolder not found. Trying to create it.." -Level Verbose

                                Invoke-Command2 -Credential $DestinationCredential -ScriptBlock {
                                    Write-Message -Message "Restore log folder $DatabaseRestoreLogFolder not found. Trying to create it.." -Level Verbose
                                    New-Item -Path $DatabaseRestoreLogFolder -ItemType Directory -Credential $DestinationCredential -Force:$Force | Out-Null
                                }
                            }
                            catch {
                                Stop-Function -Message "Something went wrong creating the restore log directory" -ErrorRecord $_ -Target $SourceSqlInstance -Continue
                            }
                        }
                    }
                }

                # Chech if the full backup patk can be reached
                if ($FullBackupPath) {
                    Write-Message -Message "Testing full backup path $FullBackupPath" -Level Verbose
                    if ((Test-DbaSqlPath -Path $FullBackupPath -SqlInstance $DestinationSqlInstance -SqlCredential $DestinationCredential) -ne $true) {
                        Stop-Function -Message ("The path to the full backup could not be reached. Check the path and/or the crdential") -ErrorRecord $_ -Target $DestinationSqlInstance -Continue
                    }
                }
                elseif ($UseBackupFolder.Length -ge 1) {
                    Write-Message -Message "Testing backup folder $UseBackupFolder" -Level Verbose
                    if ((Test-DbaSqlPath -Path $UseBackupFolder -SqlInstance $DestinationSqlInstance -SqlCredential $DestinationCredential) -ne $true) {
                        Stop-Function -Message ("The path to the backup folder could not be reached. Check the path and/or the crdential") -ErrorRecord $_ -Target $DestinationSqlInstance -Continue
                    }

                    $BackupPath = $UseBackupFolder
                }
                elseif ($UseExistingFullBackup) {
                    Write-Message -Message "No path to the full backup is set. Trying to retrieve the last full backup for $db from $SourceSqlInstance" -Level Verbose

                    # Get the last full backup
                    $LastBackup = Get-DbaBackupHistory -SqlServer $SourceSqlInstance -Databases $($db.Name) -LastFull -Credential $SourceSqlCredential

                    # Check if there was a last backup
                    if ($LastBackup -ne $null) {
                        # Test the path to the backup
                        Write-Message -Message "Testing last backup path $(($LastBackup[-1]).Path[-1])" -Level Verbose
                        if ((Test-DbaSqlPath -Path ($LastBackup[-1]).Path[-1] -SqlInstance $SourceSqlInstance -SqlCredential $SourceCredential) -ne $true) {
                            Stop-Function -Message "The full backup could not be found on $($LastBackup.Path). Check path and/or credentials" -ErrorRecord $_ -Target $DestinationSqlInstance -Continue
                        }
                        # Check if the source for the last full backup is remote and the backup is on a shared location
                        elseif (($LastBackup.Computername -ne $SourceServerName) -and (($LastBackup[-1]).Path[-1].StartsWith('\\') -eq $false)) {
                            Stop-Function -Message "The last full backup is not located on shared location. `n$($_.Exception.Message)" -ErrorRecord $_ -Target $DestinationSqlInstance -Continue
                        }
                        else {
                            #$FullBackupPath = $LastBackup.Path
                            $BackupPath = $LastBackup.Path
                            Write-Message -Message "Full backup found for $db. Path $BackupPath" -Level Verbose
                        }
                    }
                    else {
                        Write-Message -Message "No Full backup found for $db." -Level Output
                    }
                }
            }

            # Set the copy destination folder to include the database name
            if ($CopyDestinationFolder.EndsWith("\")) {
                $DatabaseCopyDestinationFolder = "$CopyDestinationFolder$($db.Name)"
            }
            else {
                $DatabaseCopyDestinationFolder = "$CopyDestinationFolder\$($db.Name)"
            }
            Write-Message -Message "Copy destination folder set to $DatabaseCopyDestinationFolder." -Level Verbose

            # Check if the copy job name is set
            if ($CopyJob) {
                $DatabaseCopyJob = "$CopyJob_$SourceServerName_$($db.Name)"
            }
            else {
                $DatabaseCopyJob = "LSCopy_$SourceServerName_$($db.Name)"
            }
            Write-Message -Message "Copy job name set to $DatabaseCopyJob" -Level Verbose

            # Check if the copy job schedule name is set
            if ($CopySchedule) {
                $DatabaseCopySchedule = "$CopySchedule_$($db.Name)"
            }
            else {
                $DatabaseCopySchedule = "LSCopySchedule_$($db.Name)"
                Write-Message -Message "Copy job schedule name set to $DatabaseCopySchedule" -Level Verbose
            }

            # Check if the copy destination folder exists
            Write-Message -Message "Testing database copy destination path $DatabaseCopyDestinationFolder" -Level Verbose
            if ((Test-DbaSqlPath -Path $DatabaseCopyDestinationFolder -SqlInstance $DestinationSqlInstance -SqlCredential $DestinationCredential) -ne $true) {
                if ($PSCmdlet.ShouldProcess($DestinationServerName, "Creating copy destination folder on $DestinationServerName")) {
                    try {
                        Invoke-Command2 -Credential $DestinationCredential -ScriptBlock {
                            Write-Message -Message "Copy destination folder $DatabaseCopyDestinationFolder not found. Trying to create it.. ." -Level Verbose
                            New-Item -Path $DatabaseCopyDestinationFolder -ItemType Directory -Credential $DestinationCredential -Force:$Force | Out-Null
                        }
                    }
                    catch {
                        Stop-Function -Message "Something went wrong creating the database copy destination folder. `n$($_.Exception.Message)" -ErrorRecord $_ -Target $DestinationServerName -Continue
                    }
                }
            }

            # Check if the restore job name is set
            if ($RestoreJob) {
                $DatabaseRestoreJob = "$RestoreJob_$SourceServerName_$($db.Name)"
            }
            else {
                $DatabaseRestoreJob = "LSRestore_$DestinationServerName_$($db.Name)"
            }
            Write-Message -Message "Restore job name set to $DatabaseRestoreJob" -Level Verbose

            # Check if the restore job schedule name is set
            if ($RestoreSchedule) {
                $DatabaseRestoreSchedule = "$RestoreSchedule_$($db.Name)"
            }
            else {
                $DatabaseRestoreSchedule = "LSRestoreSchedule_$($db.Name)"
            }
            Write-Message -Message "Restore job schedule name set to $DatabaseRestoreSchedule" -Level Verbose

            # If the database needs to be backed up first
            if ($GenerateFullBackup) {
                if ($PSCmdlet.ShouldProcess($SourceSqlInstance, "Backing up database $db")) {

                    Write-Message -Message "Generating full backup." -Level Output
                    Write-Message -Message "Backing up database $db to $DatabaseBackupNetworkPath" -Level Output

                    try {
                        $Timestamp = Get-Date -format "yyyyMMddHHmmss"

                        $LastBackup = Backup-DbaDatabase -SqlInstance $SourceSqlInstance `
                            -SqlCredential $SourceSqlCredential `
                            -BackupDirectory $DatabaseBackupNetworkPath `
                            -BackupFileName "FullBackup_$($db.Name)_PreLogShipping_$Timestamp.bak" `
                            -Databases $($db.Name) `
                            -Type Full

                        Write-Message -Message "Backup completed." -Level Output

                        # Get the last full backup path
                        #$FullBackupPath = $LastBackup.BackupPath
                        $BackupPath = $LastBackup.BackupPath

                        Write-Message -Message "Backup is located at $BackupPath" -Level Verbose
                    }
                    catch {
                        Stop-Function -Message "Something went wrong generating the full backup" -ErrorRecord $_ -Target $DestinationServerName -Continue
                    }
                }
            }

            # Check of the MonitorServerSecurityMode value is of type string and set the integer value
            if ($PrimaryMonitorServerSecurityMode -notin 0, 1) {
                $PrimaryMonitorServerSecurityMode = switch ($PrimaryMonitorServerSecurityMode) {
                    "SQLSERVER" { 0 } "WINDOWS" { 1 } default { 1 }
                }
            }

            # Check the primary monitor server
            if ($Force -and (-not$PrimaryMonitorServer -or [string]$PrimaryMonitorServer -eq '' -or $PrimaryMonitorServer -eq $null)) {
                Write-Message -Message "Setting monitor server for primary server to $SourceSqlInstance." -Level Output
                $PrimaryMonitorServer = $SourceSqlInstance
            }

            # Check the PrimaryMonitorServerSecurityMode if it's SQL Server authentication
            if ($PrimaryMonitorServerSecurityMode -eq 0) {
                if ($PrimaryMonitorServerLogin) {
                    Stop-Function -Message "The PrimaryMonitorServerLogin cannot be empty when using SQL Server authentication." -Target $SourceSqlInstance -Continue
                }

                if ($PrimaryMonitorServerPassword) {
                    Stop-Function -Message "The PrimaryMonitorServerPassword cannot be empty when using SQL Server authentication." -Target $ -Continue
                }
            }

            # Check of the SecondaryMonitorServerSecurityMode value is of type string and set the integer value
            if ($SecondaryMonitorServerSecurityMode -notin 0, 1) {
                $SecondaryMonitorServerSecurityMode = switch ($SecondaryMonitorServerSecurityMode) {
                    "SQLSERVER" { 0 } "WINDOWS" { 1 } default { 1 }
                }
            }

            # Check the secondary monitor server
            if ($Force -and (-not $SecondaryMonitorServer -or [string]$SecondaryMonitorServer -eq '' -or $SecondaryMonitorServer -eq $null)) {
                Write-Message -Message "Setting secondary monitor server for $DestinationSqlInstance to $SourceSqlInstance." -Level Verbose
                $SecondaryMonitorServer = $SourceSqlInstance
            }

            # Check the MonitorServerSecurityMode if it's SQL Server authentication
            if ($SecondaryMonitorServerSecurityMode -eq 0) {
                if ($SecondaryMonitorServerLogin) {
                    Stop-Function -Message "The SecondaryMonitorServerLogin cannot be empty when using SQL Server authentication." -Target $SourceSqlInstance -Continue
                }

                if ($SecondaryMonitorServerPassword) {
                    Stop-Function -Message "The SecondaryMonitorServerPassword cannot be empty when using SQL Server authentication." -Target $SourceSqlInstance -Continue
                }
            }

            # Now that all the checks have been done we can start with the fun stuff !

            # Restore the full backup
            if ($PSCmdlet.ShouldProcess($DestinationSqlInstance, "Restoring database $db to $SecondaryDatabase on $DestinationSqlInstance")) {
                if ($GenerateFullBackup -or $UseExistingFullBackup -or $UseBackupFolder) {
                    try {
                        Write-Message -Message "Start database restore" -Level Output
                        if ($NoRecovery -or (-not $Standby)) {
                            if ($Force) {
                                Restore-DbaDatabase -SqlServer $DestinationSqlInstance `
                                    -SqlCredential $DestinationSqlCredential `
                                    -Path $BackupPath `
                                    -DestinationFilePrefix $SecondaryDatabasePrefix `
                                    -DestinationFileSuffix $SecondaryDatabaseSuffix `
                                    -DestinationDataDirectory $DatabaseRestoreDataFolder `
                                    -DestinationLogDirectory $DatabaseRestoreLogFolder `
                                    -DatabaseName $SecondaryDatabase `
                                    -DirectoryRecurse `
                                    -NoRecovery `
                                    -WithReplace | Out-Null
                            }
                            else {
                                Restore-DbaDatabase -SqlServer $DestinationSqlInstance `
                                    -SqlCredential $DestinationSqlCredential `
                                    -Path $BackupPath `
                                    -DestinationFilePrefix $SecondaryDatabasePrefix `
                                    -DestinationFileSuffix $SecondaryDatabaseSuffix `
                                    -DestinationDataDirectory $DatabaseRestoreDataFolder `
                                    -DestinationLogDirectory $DatabaseRestoreLogFolder `
                                    -DatabaseName $SecondaryDatabase `
                                    -DirectoryRecurse `
                                    -NoRecovery | Out-Null
                            }
                        }

                        # If the database needs to be in standby
                        if ($Standby) {
                            # Setup the path to the standby file
                            $StandbyDirectory = "$DatabaseCopyDestinationFolder"

                            # Check if credentials need to be used
                            if ($DestinationSqlCredential) {
                                Restore-DbaDatabase -ServerInstance $DestinationSqlInstance `
                                    -SqlCredential $DestinationSqlCredential `
                                    -Path $BackupPath `
                                    -DestinationFilePrefix $SecondaryDatabasePrefix `
                                    -DestinationFileSuffix $SecondaryDatabaseSuffix `
                                    -DestinationDataDirectory $DatabaseRestoreDataFolder `
                                    -DestinationLogDirectory $DatabaseRestoreLogFolder `
                                    -DatabaseName $SecondaryDatabase `
                                    -DirectoryRecurse `
                                    -StandbyDirectory $StandbyDirectory
                            }
                            else {
                                Restore-DbaDatabase -ServerInstance $DestinationSqlInstance `
                                    -Path $BackupPath `
                                    -DestinationFilePrefix $SecondaryDatabasePrefix `
                                    -DestinationFileSuffix $SecondaryDatabaseSuffix `
                                    -DestinationDataDirectory $DatabaseRestoreDataFolder `
                                    -DestinationLogDirectory $DatabaseRestoreLogFolder `
                                    -DatabaseName $SecondaryDatabase `
                                    -DirectoryRecurse `
                                    -StandbyDirectory $StandbyDirectory
                            }
                        }
                    }
                    catch {
                        Stop-Function -Message "Something went wrong restoring the secondary database" -ErrorRecord $_ -Target $SourceSqlInstance -Continue
                    }

                    Write-Message -Message "Restore completed." -Level Output
                }
            }

            #region Set up log shipping on the primary instance
            # Set up log shipping on the primary instance
            if ($PSCmdlet.ShouldProcess($SourceSqlInstance, "Configuring logshipping for primary database $db on $SourceSqlInstance")) {
                try {

                    Write-Message -Message "Configuring logshipping for primary database" -Level Output

                    New-DbaLogShippingPrimaryDatabase -SqlInstance $SourceSqlInstance `
                        -SqlCredential $SourceSqlCredential `
                        -Database $($db.Name) `
                        -BackupDirectory $DatabaseBackupLocalPath `
                        -BackupJob $DatabaseBackupJob `
                        -BackupRetention $BackupRetention `
                        -BackupShare $DatabaseBackupNetworkPath `
                        -BackupThreshold $BackupThreshold `
                        -CompressBackup:$BackupCompression `
                        -HistoryRetention $HistoryRetention `
                        -MonitorServer $PrimaryMonitorServer `
                        -MonitorServerSecurityMode $PrimaryMonitorServerSecurityMode `
                        -MonitorCredential $PrimaryMonitorCredential `
                        -ThresholdAlertEnabled:$PrimaryThresholdAlertEnabled `
                        -Force:$Force

                    # Check if the backup job needs to be enabled or disabled
                    if ($BackupScheduleDisabled) {
                        Set-DbaAgentJob -SqlInstance $SourceSqlInstance -SqlCredential $SourceSqlCredential -Job $DatabaseBackupJob -Disabled
                        Write-Message -Message "Disabling backup job $DatabaseBackupJob" -Level Output
                    }
                    else {
                        Set-DbaAgentJob -SqlInstance $SourceSqlInstance -SqlCredential $SourceSqlCredential -Job $DatabaseBackupJob -Enabled
                        Write-Message -Message "Enabling backup job $DatabaseBackupJob" -Level Output
                    }

                    Write-Message -Message "Create backup job schedule $DatabaseBackupSchedule" -Level Output

                    $BackupJobSchedule = New-DbaAgentSchedule -SqlInstance $SourceSqlInstance `
                        -SqlCredential $SourceSqlCredential `
                        -Job $DatabaseBackupJob `
                        -Schedule $DatabaseBackupSchedule `
                        -FrequencyType $BackupScheduleFrequencyType `
                        -FrequencyInterval $BackupScheduleFrequencyInterval `
                        -FrequencySubdayType $BackupScheduleFrequencySubdayType `
                        -FrequencySubdayInterval $BackupScheduleFrequencySubdayInterval `
                        -FrequencyRelativeInterval $BackupScheduleFrequencyRelativeInterval `
                        -FrequencyRecurrenceFactor $BackupScheduleFrequencyRecurrenceFactor `
                        -StartDate $BackupScheduleStartDate `
                        -EndDate $BackupScheduleEndDate `
                        -StartTime $BackupScheduleStartTime `
                        -EndTime $BackupScheduleEndTime `
                        -Force:$Force

                    Write-Message -Message "Configuring logshipping from primary to secondary database." -Level Output

                    New-DbaLogShippingPrimarySecondary -SqlInstance $SourceSqlInstance `
                        -SqlCredential $SourceSqlCredential `
                        -PrimaryDatabase $($db.Name) `
                        -SecondaryDatabase $SecondaryDatabase `
                        -SecondaryServer $DestinationSqlInstance `
                        -SecondarySqlCredential $DestinationSqlCredential
                }
                catch {
                    Stop-Function -Message "Something went wrong setting up log shipping for primary instance" -ErrorRecord $_ -Target $SourceSqlInstance -Continue
                }
            }
            #endregion Set up log shipping on the primary instance

            #region Set up log shipping on the secondary instance
            # Set up log shipping on the secondary instance
            if ($PSCmdlet.ShouldProcess($DestinationSqlInstance, "Configuring logshipping for secondary database $SecondaryDatabase on $DestinationSqlInstance")) {
                try {

                    Write-Message -Message "Configuring logshipping from secondary database $SecondaryDatabase to primary database $db." -Level Output

                    New-DbaLogShippingSecondaryPrimary -SqlInstance $DestinationSqlInstance `
                        -SqlCredential $DestinationSqlCredential `
                        -BackupSourceDirectory $DatabaseBackupNetworkPath `
                        -BackupDestinationDirectory $DatabaseCopyDestinationFolder `
                        -CopyJob $DatabaseCopyJob `
                        -FileRetentionPeriod $BackupRetention `
                        -MonitorServer $SecondaryMonitorServer `
                        -MonitorServerSecurityMode $SecondaryMonitorServerSecurityMode `
                        -MonitorCredential $SecondaryMonitorCredential `
                        -PrimaryServer $SourceSqlInstance `
                        -PrimaryDatabase $($db.Name) `
                        -RestoreJob $DatabaseRestoreJob `
                        -Force:$Force

                    Write-Message -Message "Create copy job schedule $DatabaseCopySchedule" -Level Output

                    $CopyJobSchedule = New-DbaAgentSchedule -SqlInstance $DestinationSqlInstance `
                        -SqlCredential $DestinationSqlCredential `
                        -Job $DatabaseCopyJob `
                        -Schedule $DatabaseCopySchedule `
                        -FrequencyType $CopyScheduleFrequencyType `
                        -FrequencyInterval $CopyScheduleFrequencyInterval `
                        -FrequencySubdayType $CopyScheduleFrequencySubdayType `
                        -FrequencySubdayInterval $CopyScheduleFrequencySubdayInterval `
                        -FrequencyRelativeInterval $CopyScheduleFrequencyRelativeInterval `
                        -FrequencyRecurrenceFactor $CopyScheduleFrequencyRecurrenceFactor `
                        -StartDate $CopyScheduleStartDate `
                        -EndDate $CopyScheduleEndDate `
                        -StartTime $CopyScheduleStartTime `
                        -EndTime $CopyScheduleEndTime `
                        -Force:$Force

                    Write-Message -Message "Create restore job schedule $DatabaseRestoreSchedule" -Level Output

                    $RestoreJobSchedule = New-DbaAgentSchedule -SqlInstance $DestinationSqlInstance `
                        -SqlCredential $DestinationSqlCredential `
                        -Job $DatabaseRestoreJob `
                        -Schedule $DatabaseRestoreSchedule `
                        -FrequencyType $RestoreScheduleFrequencyType `
                        -FrequencyInterval $RestoreScheduleFrequencyInterval `
                        -FrequencySubdayType $RestoreScheduleFrequencySubdayType `
                        -FrequencySubdayInterval $RestoreScheduleFrequencySubdayInterval `
                        -FrequencyRelativeInterval $RestoreScheduleFrequencyRelativeInterval `
                        -FrequencyRecurrenceFactor $RestoreScheduleFrequencyRecurrenceFactor `
                        -StartDate $RestoreScheduleStartDate `
                        -EndDate $RestoreScheduleEndDate `
                        -StartTime $RestoreScheduleStartTime `
                        -EndTime $RestoreScheduleEndTime `
                        -Force:$Force

                    Write-Message -Message "Configuring logshipping for secondary database." -Level Output

                    New-DbaLogShippingSecondaryDatabase -SqlInstance $DestinationSqlInstance `
                        -SqlCredential $DestinationSqlCredential `
                        -SecondaryDatabase $SecondaryDatabase `
                        -PrimaryServer $SourceSqlInstance `
                        -PrimaryDatabase $($db.Name) `
                        -RestoreDelay $RestoreDelay `
                        -RestoreMode $DatabaseStatus `
                        -DisconnectUsers:$DisconnectUsers `
                        -RestoreThreshold $RestoreThreshold `
                        -ThresholdAlertEnabled:$SecondaryThresholdAlertEnabled `
                        -HistoryRetention $HistoryRetention

                    # Check if the copy job needs to be enabled or disabled
                    if ($CopyScheduleDisabled) {
                        Set-DbaAgentJob -SqlInstance $DestinationSqlInstance -SqlCredential $DestinationSqlCredential -Job $DatabaseCopyJob -Disabled
                    }
                    else {
                        Set-DbaAgentJob -SqlInstance $DestinationSqlInstance -SqlCredential $DestinationSqlCredential -Job $DatabaseCopyJob -Enabled
                    }

                    # Check if the restore job needs to be enabled or disabled
                    if ($RestoreScheduleDisabled) {
                        Set-DbaAgentJob -SqlInstance $DestinationSqlInstance -SqlCredential $DestinationSqlCredential -Job $DatabaseRestoreJob -Disabled
                    }
                    else {
                        Set-DbaAgentJob -SqlInstance $DestinationSqlInstance -SqlCredential $DestinationSqlCredential -Job $DatabaseRestoreJob -Enabled
                    }

                }
                catch {
                    Stop-Function -Message "Something went wrong setting up log shipping for secondary instance.`n$($_.Exception.Message)" -ErrorRecord $_ -Target $DestinationSqlInstance -Continue
                }
            }
            #endregion Set up log shipping on the secondary instance

            Write-Message -Message "Completed configuring log shipping for database $db" -Level Output

        } # for each database
    } # end process

    end {
        Write-Message -Message "Finished setting up log shipping." -Level Verbose
    }
}
tools\dbatools\functions\Invoke-DbaLogShippingRecovery.ps1
function Invoke-DbaLogShippingRecovery {
    <#
        .SYNOPSIS
            Invoke-DbaLogShippingRecovery recovers log shipped databases to a normal state to act upon a migration or disaster.

        .DESCRIPTION
            By default all the databases for a particular instance are recovered.
            If the database is in the right state, either standby or recovering, the process will try to recover the database.

            At first the function will check if the backup source directory can still be reached.
            If so it will look up the last transaction log backup for the database. If that backup file is not the last copied file the log shipping copy job will be started.
            If the directory cannot be reached for the function will continue to the restoring process.
            After the copy job check is performed the job is disabled to prevent the job to run.

            For the restore the log shipping status is checked in the msdb database.
            If the last restored file is not the same as the last file name found, the log shipping restore job will be executed.
            After the restore job check is performed the job is disabled to prevent the job to run

            The last part is to set the database online by restoring the databases with recovery

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to

        .PARAMETER Database
            Database to perform the restore for. This value can also be piped enabling multiple databases to be recovered.
            If this value is not supplied all databases will be recovered.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER NoRecovery
            Allows you to choose to not restore the database to a functional state (Normal) in the final steps of the process.
            By default the database is restored to a functional state (Normal).

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER Force
            Use this parameter to force the function to continue and perform any adjusting actions to successfully execute

        .PARAMETER Delay
            Set the delay in seconds to wait for the copy and/or restore jobs.
            By default the delay is 5 seconds

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .NOTES
            Tags: LogShipping
            Author: Sander Stad (@sqlstad, sqlstad.nl)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Invoke-DbaLogShippingRecovery

        .EXAMPLE
            Invoke-DbaLogShippingRecovery -SqlServer server1

            Recovers all the databases on the instance that are enabled for log shipping

        .EXAMPLE
            Invoke-DbaLogShippingRecovery -SqlServer server1 -SqlCredential $cred -Verbose

            Recovers all the databases on the instance that are enabled for log shipping using a credential

        .EXAMPLE
            Invoke-DbaLogShippingRecovery -SqlServer server1 -database db_logship -Verbose

            Recovers the database "db_logship" to a normal status

        .EXAMPLE
            db1, db2, db3, db4 | Invoke-DbaLogShippingRecovery -SqlServer server1 -Verbose

            Recovers the database db1, db2, db3, db4 to a normal status

        .EXAMPLE
            Invoke-DbaLogShippingRecovery -SqlServer server1 -WhatIf

            Shows what would happen if the command were executed.
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param
    (
        [Parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [object]$SqlInstance,
        [Parameter(ValueFromPipeline = $true)]
        [object[]]$Database,
        [PSCredential]$SqlCredential,
        [switch]$NoRecovery,
        [Alias('Silent')]
        [switch]$EnableException,
        [switch]$Force,
        [int]$Delay = 5
    )

    begin {
        if (!$sqlinstance -and $database.Count -lt 1) {
            # You can prolly do this with
            Stop-Function -Message "You must pipe an SMO database object or specify SqlInstance"
            return
        }

        if ($sqlinstance) {
            # Check the instance if it is a named instance
            $servername, $instancename = $sqlinstance.Split("\")

            if ($null -eq $instancename) {
                $instancename = "MSSQLSERVER"
            }

            Write-Message -Message "Connecting to Sql Server" -Level Output
            try {
                $server = Connect-SqlInstance -SqlInstance $sqlinstance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance
            }

            if ($Force -and (!$database -or $database.Count -lt 1)) {
                $database = $server.databases
            }
            elseif (-not $Force -and (!$database -or $database.Count -lt 1)) {
                Stop-Function -Message "Please enter one or more databases to recover from log shipping" -Target $instance
            }
            else {
                $databases = $server.databases | Where-Object Name -in $database
            }
        }
    }

    process {
        # Try to get the agent service details
        try {
            # Start the service
            $agentservice = Get-DbaSqlService -ComputerName $servername | Where-Object {($_.ComputerName -eq $servername) -and ($_.DisplayName -eq "SQL Server Agent ($instancename)")}
        }
        catch {
            # Stop the function when the service was unable to start
            Stop-Function -Message "Unable to start SQL Server Agent Service" -ErrorRecord $_ -Target $sqlinstance
            return
        }

        # Check if the service is running
        if ($agentservice.State -ne 'Running') {

            if ($Force) {
                try {
                    Start-DbaSqlService -ComputerName $servername -InstanceName $instancename -Type Agent -Credential $SqlCredential
                }
                catch {
                    # Stop the function when the service was unable to start
                    Stop-Function -Message "Unable to start SQL Server Agent Service" -ErrorRecord $_ -Target $sqlinstance
                    return
                }
            }
            # If the force switch and the silent switch are not set
            elseif (!$Force -and !$EnableException) {
                # Set up the parts for the user choice
                $Title = "SQL Server Agent is not running"
                $Info = "Do you want to start the SQL Server Agent service?"

                $Options = [System.Management.Automation.Host.ChoiceDescription[]] @("&Start", "&Quit")
                [int]$Defaultchoice = 0
                $choice = $host.UI.PromptForChoice($Title, $Info, $Options, $Defaultchoice)

                # Check the given option
                if ($choice -eq 0) {
                    try {
                        # Start the service
                        Start-DbaSqlService -ComputerName $servername -InstanceName $instancename -Type Agent -Credential $SqlCredential
                    }
                    catch {
                        # Stop the function when the service was unable to start
                        Stop-Function -Message "Unable to start SQL Server Agent Service" -ErrorRecord $_ -Target $sqlinstance
                        return
                    }
                }
                else {
                    Stop-Function -Message "The SQL Server Agent service needs to be started to be able to recover the databases" -ErrorRecord $_ -Target $sqlinstance
                    return
                }
            }
            # If the force switch it not set and the silent switch is set
            elseif (!$Force -and $EnableException) {
                Stop-Function -Message "The SQL Server Agent service needs to be started to be able to recover the databases" -ErrorRecord $_ -Target $sqlinstance
                return
            }
            # If nothing else matches and the agent service is not started
            else {
                Stop-Function -Message "The SQL Server Agent service needs to be started to be able to recover the databases" -ErrorRecord $_ -Target $sqlinstance
                return
            }

        }

        Write-Message -Message "Started Log Shipping Recovery" -Level Output

        # Loop through all the databases
        foreach ($db in $databases) {
            # Query for retrieving the log shipping information
            $query = "SELECT lss.primary_server, lss.primary_database, lsd.secondary_database, lss.backup_source_directory,
            lss.backup_destination_directory, lss.last_copied_file, lss.last_copied_date,
            lsd.last_restored_file, sj1.name AS 'copyjob', sj2.name AS 'restorejob'
        FROM msdb.dbo.log_shipping_secondary AS lss
            INNER JOIN msdb.dbo.log_shipping_secondary_databases AS lsd ON lsd.secondary_id = lss.secondary_id
            INNER JOIN msdb.dbo.sysjobs AS sj1 ON sj1.job_id = lss.copy_job_id
            INNER JOIN msdb.dbo.sysjobs AS sj2 ON sj2.job_id = lss.restore_job_id
        WHERE lsd.secondary_database = '$($db.Name)'"

            # Retrieve the log shipping information from the secondary instance
            try {
                Write-Message -Message "Retrieving log shipping information from the secondary instance" -Level Verbose
                $logshipping_details = $server.Query($query)
            }
            catch {
                Stop-Function -Message "Error retrieving the log shipping details: $($_.Exception.Message)" -ErrorRecord $_ -Target $sqlinstance
                return
            }

            # Check if there are any databases to recover
            if ($null -eq $logshipping_details) {
                Stop-Function -Message "The database $db is not configured as a secondary database for log shipping." -Continue
            }
            else {
                # Loop through each of the log shipped databases
                foreach ($ls in $logshipping_details) {
                    $secondarydb = $ls.secondary_database

                    # Check if the database is in the right state
                    if ($server.Databases[$secondarydb].Status -notin ('Normal, Standby', 'Standby', 'Restoring')) {
                        Stop-Function -Message "The database $db doesn't have the right status to be recovered" -Continue
                    }
                    else {
                        Write-Message -Message "Started Recovery for $secondarydb" -Level Verbose

                        # Get the last file from the backup source directory
                        <# !!!! set credentials !!! #>
                        $latestBackupSource = Get-ChildItem -Path $ls.backup_source_directory -filter ("*" + $ls.primary_database + "*") | Where-Object { ($_.Extension -eq '.trn') } | Sort-Object LastWriteTime -Descending | Select-Object -First 1

                        # Get al the backup files from the destination directory
                        <# !!!! set credentials !!! #>
                        $latestBackupDest = Get-ChildItem -Path $ls.backup_destination_directory -filter ("*" + $ls.primary_database + "*") | Where-Object { ($_.Extension -eq '.trn') } | Sort-Object LastWriteTime -Descending | Select-Object -First 1

                        # Check if source and destination directory are in sync
                        if ($latestBackupSource.Name -ne $latestBackupDest.Name) {
                            # Check if the backup source directory can be reached
                            if (Test-DbaSqlPath -SqlInstance $SqlInstance -Path $ls.backup_source_directory -SqlCredential $SqlCredential) {

                                # Check if the latest file is also the latest copied file
                                if ($latestBackupSource.Name -ne ([string]$ls.last_copied_file).Split('\')[-1]) {
                                    Write-Message -Message "Backup destination is not up-to-date" -Level Verbose

                                    # Start the job to get the latest files
                                    if ($PSCmdlet.ShouldProcess($sqlinstance, ("Starting copy job $($ls.copyjob)"))) {
                                        Write-Message -Message "Starting copy job $($ls.copyjob)" -Level Verbose
                                        try {
                                            $server.JobServer.Jobs[$ls.copyjob].Start()
                                        }
                                        catch {
                                            Stop-Function -Message "Something went wrong starting the restore job.`n$($_)" -ErrorRecord $_ -Target $sqlinstance
                                        }

                                        Write-Message -Message "Copying files to $($ls.backup_destination_directory)" -Level Verbose

                                        # Check if the file has been copied
                                        $query = "SELECT last_copied_file FROM msdb.dbo.log_shipping_secondary WHERE primary_database = '$($ls.primary_database)' AND last_copied_file IS NOT NULL "
                                        $latestcopy = $server.Query($query)

                                        Write-Message -Message "Waiting for the copy action to complete.." -Level Verbose

                                        while (($latestBackupSource.Name -ne ([string]$latestcopy.last_copied_file).Split('\')[-1])) {
                                            # Sleep for while to let the files be copied
                                            Start-Sleep -Seconds $Delay

                                            # Again get the latest file to check if the process can continue
                                            $latestcopy = $server.Query($query)
                                        }

                                        # Again get the latest file to check if the process can continue
                                        $latestcopy = $server.Query($query)

                                        # Check the lat outcome of the job
                                        if ($server.JobServer.Jobs[$ls.copyjob].LastRunOutcome -eq 'Failed') {
                                            Stop-Function -Message "The copy job for database $db failed. Please check the error log." -Continue
                                        }

                                        Write-Message -Message "Copying of backup files finished" -Level Verbose
                                    } # if should process
                                } # if latest file name
                            } # if backup directory test
                            else {
                                Stop-Function -Message "Couldn't reach the backup source directory. Continuing..." -Continue
                            }
                        } # check latest backup file is already in directory


                        # Disable the log shipping copy job on the secondary instance
                        if ($PSCmdlet.ShouldProcess($sqlinstance, "Disabling copy job $($ls.copyjob)")) {
                            try {
                                Write-Message -Message "Disabling copy job $($ls.copyjob)" -Level Verbose
                                $server.JobServer.Jobs[$ls.copyjob].IsEnabled = $false
                                $server.JobServer.Jobs[$ls.copyjob].Alter()
                            }
                            catch {
                                Stop-Function -Message "Something went wrong disabling the copy job.`n$($_)" -ErrorRecord $_ -Target $sqlinstance
                            }
                        }

                        # Check if the file has been copied
                        $query = "SELECT last_restored_file FROM msdb.dbo.log_shipping_secondary_databases WHERE secondary_database = '$secondarydb' AND last_restored_file IS NOT NULL"
                        $latestrestore = $server.Query($query)

                        # Check if the last copied file is newer than the last restored file
                        if ((([string]$latestcopy.last_copied_file).Split('\')[-1] -ne ([string]$latestrestore.last_restored_file).Split('\')[-1]) -or ($null -eq ([string]$latestcopy.last_copied_file).Split('\')[-1])) {
                            Write-Message -Message "Restore is not up-to-date" -Level Verbose

                            # Start the restore job
                            if ($PSCmdlet.ShouldProcess($sqlinstance, ("Starting restore job " + $ls.restorejob))) {
                                Write-Message -Message "Starting restore job $($ls.restorejob)" -Level Verbose
                                try {
                                    $server.JobServer.Jobs[$ls.restorejob].Start()
                                }
                                catch {
                                    Stop-Function -Message "Something went wrong starting the restore job.`n$($_)" -ErrorRecord $_ -Target $sqlinstance
                                }

                                Write-Message -Message "Waiting for the restore action to complete.." -Level Verbose

                                while ($latestBackupSource.Name -ne [string]($latestrestore.last_restored_file).Split('\')[-1]) {
                                    # Sleep for while to let the files be copied
                                    Start-Sleep -Seconds $Delay

                                    # Again get the latest file to check if the process can continue
                                    $latestrestore = $server.Query($query)
                                }

                                # Again get the latest file to check if the process can continue
                                $latestrestore = $server.Query($query)

                                # Check the lat outcome of the job
                                if ($server.JobServer.Jobs[$ls.restorejob].LastRunOutcome -eq 'Failed') {
                                    Stop-Function -Message "The restore job for database $db failed. Please check the error log." -Continue
                                }
                            }
                        }

                        # Disable the log shipping restore job on the secondary instance
                        if ($PSCmdlet.ShouldProcess($sqlinstance, "Disabling restore job $($ls.restorejob)")) {
                            try {
                                Write-Message -Message ("Disabling restore job " + $ls.restorejob) -Level Verbose
                                $server.JobServer.Jobs[$ls.restorejob].IsEnabled = $false
                                $server.JobServer.Jobs[$ls.restorejob].Alter()
                            }
                            catch {
                                Stop-Function -Message "Something went wrong disabling the restore job.`n$($_)" -ErrorRecord $_ -Target $sqlinstance
                            }

                        }

                        # Check for the last time if everything is up-to-date
                        if ($latestBackupSource.Name -eq [string]($latestrestore.last_restored_file).Split('\')[-1]) {
                            # Check if the database needs to recovered to its normal state
                            if ($NoRecovery -eq $false) {
                                if ($PSCmdlet.ShouldProcess($secondarydb, "Restoring database with recovery")) {
                                    Write-Message -Message "Restoring the database to it's normal state" -Level Verbose
                                    $query = "RESTORE DATABASE [$secondarydb] WITH RECOVERY"
                                    $server.Query($query)
                                }
                            }
                            else {
                                Write-Message -Message "Skipping restore with recovery" -Level Output
                            }
                        }

                        Write-Message -Message ("Finished Recovery for $secondarydb") -Level Output

                        # Reset the log ship details
                        $logshipping_details = $null

                    } # database in restorable mode
                } # foreach ls details
            } # ls details are not null
        } # foreach database
    } # process
}
tools\dbatools\functions\Invoke-DbaPfRelog.ps1
function Invoke-DbaPfRelog {
    <#
        .SYNOPSIS
            Pipeline-compatible wrapper for the relog command which is available on modern Windows platforms.

        .DESCRIPTION
            Pipeline-compatible wrapper for the relog command. Relog is useful for converting Windows Perfmon.

            Extracts performance counters from performance counter logs into other formats,
            such as text-TSV (for tab-delimited text), text-CSV (for comma-delimited text), binary-BIN, or SQL.

            relog "C:\PerfLogs\Admin\System Correlation\WORKSTATIONX_20180112-000001\DataCollector01.blg" -o C:\temp\foo.csv -f tsv

            If you find any input hangs, please send us the output so we can accommodate for it then use -Raw for an immediate solution.

        .PARAMETER Path
            Specifies the pathname of an existing performance counter log or performance counter path. You can specify multiple input files.

        .PARAMETER Destination
            Specifies the pathname of the output file or SQL database where the counters will be written. Defaults to the same directory as the source.

        .PARAMETER Type
            The output format. Defaults to tsv. Options include tsv, csv, bin, and sql.

            For a SQL database, the output file specifies the DSN!counter_log. You can specify the database location by using the ODBC manager to configure the DSN (Database System Name).

            For more information, read here: https://technet.microsoft.com/en-us/library/bb490958.aspx

        .PARAMETER Append
            If this switch is enabled, output will be appended to the specified file instead of overwriting. This option does not apply to SQL format where the default is always to append.

        .PARAMETER AllowClobber
            If this switch is enabled, the destination file will be overwritten if it exists.

        .PARAMETER PerformanceCounter
            Specifies the performance counter path to log.

        .PARAMETER PerformanceCounterPath
            Specifies the pathname of the text file that lists the performance counters to be included in a relog file. Use this option to list counter paths in an input file, one per line. Default setting is all counters in the original log file are relogged.

        .PARAMETER Interval
            Specifies sample intervals in "n" records. Includes every nth data point in the relog file. Default is every data point.

        .PARAMETER BeginTime
            This is is Get-Date object and we format it for you.

        .PARAMETER EndTime
            Specifies end time for copying last record from the input file. This is is Get-Date object and we format it for you.

        .PARAMETER ConfigPath
            Specifies the pathname of the settings file that contains command-line parameters.

        .PARAMETER Summary
            If this switch is enabled, the performance counters and time ranges of log files specified in the input file will be displayed.

        .PARAMETER Multithread
            If this switch is enabled, processing will be done in parallel. This may speed up large batches or large files.

        .PARAMETER AllTime
            If this switch is enabled and a datacollector or datacollectorset is passed in via the pipeline, collects all logs, not just the latest.

        .PARAMETER Raw
            If this switch is enabled, the results of the DOS command instead of Get-ChildItem will be displayed. This does not run in parallel.

        .PARAMETER InputObject
            Accepts the output of Get-DbaPfDataCollector and Get-DbaPfDataCollectorSet as input via the pipeline.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Performance, DataCollector, PerfCounter, Relog
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Invoke-DbaPfRelog

        .EXAMPLE
            Invoke-DbaPfRelog -Path C:\temp\perfmon.blg

            Creates C:\temp\perfmon.tsv from C:\temp\perfmon.blg.

        .EXAMPLE
            Invoke-DbaPfRelog -Path C:\temp\perfmon.blg -Destination C:\temp\a\b\c

            Creates the temp, a, and b directories if needed, then generates c.tsv (tab separated) from C:\temp\perfmon.blg.

            Returns the newly created file as a file object.

        .EXAMPLE
            Get-DbaPfDataCollectorSet -ComputerName sql2016 | Get-DbaPfDataCollector | Invoke-DbaPfRelog -Destination C:\temp\perf

            Creates C:\temp\perf if needed, then generates computername-datacollectorname.tsv (tab separated) from the latest logs of all data collector sets on sql2016. This destination format was chosen to avoid naming conflicts with piped input.

        .EXAMPLE
            Invoke-DbaPfRelog -Path C:\temp\perfmon.blg -Destination C:\temp\a\b\c -Raw

            Creates the temp, a, and b directories if needed, then generates c.tsv (tab separated) from C:\temp\perfmon.blg then outputs the raw results of the relog command.

            [Invoke-DbaPfRelog][21:21:35] relog "C:\temp\perfmon.blg" -f csv -o C:\temp\a\b\c

            Input
            ----------------
            File(s):
                C:\temp\perfmon.blg (Binary)

            Begin:    1/13/2018 5:13:23
            End:      1/13/2018 14:29:55
            Samples:  2227

            100.00%

            Output
            ----------------
            File:     C:\temp\a\b\c.csv

            Begin:    1/13/2018 5:13:23
            End:      1/13/2018 14:29:55
            Samples:  2227

            The command completed successfully.

        .EXAMPLE
            Invoke-DbaPfRelog -Path 'C:\temp\perflog with spaces.blg' -Destination C:\temp\a\b\c -Type csv -BeginTime ((Get-Date).AddDays(-30)) -EndTime ((Get-Date).AddDays(-1))

            Creates the temp, a, and b directories if needed, then generates c.csv (comma separated) from C:\temp\perflog with spaces.blg', starts 30 days ago and ends one day ago.

        .EXAMPLE
            $servers | Get-DbaPfDataCollectorSet | Get-DbaPfDataCollector | Invoke-DbaPfRelog -Multithread -AllowClobber

            Relogs latest data files from all collectors within the servers listed in $servers.

        .EXAMPLE
            Get-DbaPfDataCollector -Collector DataCollector01 | Invoke-DbaPfRelog -AllowClobber -AllTime

            Relogs all the log files from the DataCollector01 on the local computer and allows overwrite.
    #>
    [CmdletBinding()]
    param (
        [parameter(ValueFromPipelineByPropertyName)]
        [Alias("FullName")]
        [string[]]$Path,
        [string]$Destination,
        [ValidateSet("tsv", "csv", "bin", "sql")]
        [string]$Type = "tsv",
        [switch]$Append,
        [switch]$AllowClobber,
        [string[]]$PerformanceCounter,
        [string]$PerformanceCounterPath,
        [int]$Interval,
        [datetime]$BeginTime,
        [datetime]$EndTime,
        [string]$ConfigPath,
        [switch]$Summary,
        [parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [switch]$Multithread,
        [switch]$AllTime,
        [switch]$Raw,
        [switch]$EnableException
    )
    begin {
        if (Test-Bound -ParameterName BeginTime) {
            $script:beginstring = ($BeginTime -f 'M/d/yyyy hh:mm:ss' | Out-String).Trim()
        }
        if (Test-Bound -ParameterName EndTime) {
            $script:endstring = ($EndTime -f 'M/d/yyyy hh:mm:ss' | Out-String).Trim()
        }

        $allpaths = @()
        $allpaths += $Path

        # to support multithreading
        if (Test-Bound -ParameterName Destination) {
            $script:destinationset = $true
            $originaldestination = $Destination
        }
        else {
            $script:destinationset = $false
        }
    }
    process {
        if ($Append -and $Type -ne "bin") {
            Stop-Function -Message "Append can only be used with -Type bin." -Target $Path
            return
        }

        if ($InputObject) {
            foreach ($object in $InputObject) {
                # DataCollectorSet
                if ($object.OutputLocation -and $object.RemoteOutputLocation) {
                    $instance = [dbainstance]$object.ComputerName

                    if (-not $AllTime) {
                        if ($instance.IsLocalHost) {
                            $allpaths += (Get-ChildItem -Recurse -Path $object.LatestOutputLocation -Include *.blg -ErrorAction SilentlyContinue).FullName
                        }
                        else {
                            $allpaths += (Get-ChildItem -Recurse -Path $object.RemoteLatestOutputLocation -Include *.blg -ErrorAction SilentlyContinue).FullName
                        }
                    }
                    else {
                        if ($instance.IsLocalHost) {
                            $allpaths += (Get-ChildItem -Recurse -Path $object.OutputLocation -Include *.blg -ErrorAction SilentlyContinue).FullName
                        }
                        else {
                            $allpaths += (Get-ChildItem -Recurse -Path $object.RemoteOutputLocation -Include *.blg -ErrorAction SilentlyContinue).FullName
                        }
                    }


                    $script:perfmonobject = $true
                }
                # DataCollector
                if ($object.LatestOutputLocation -and $object.RemoteLatestOutputLocation) {
                    $instance = [dbainstance]$object.ComputerName

                    if (-not $AllTime) {
                        if ($instance.IsLocalHost) {
                            $allpaths += (Get-ChildItem -Recurse -Path $object.LatestOutputLocation -Include *.blg -ErrorAction SilentlyContinue).FullName
                        }
                        else {
                            $allpaths += (Get-ChildItem -Recurse -Path $object.RemoteLatestOutputLocation -Include *.blg -ErrorAction SilentlyContinue).FullName
                        }
                    }
                    else {
                        if ($instance.IsLocalHost) {
                            $allpaths += (Get-ChildItem -Recurse -Path (Split-Path $object.LatestOutputLocation) -Include *.blg -ErrorAction SilentlyContinue).FullName
                        }
                        else {
                            $allpaths += (Get-ChildItem -Recurse -Path (Split-Path $object.RemoteLatestOutputLocation) -Include *.blg -ErrorAction SilentlyContinue).FullName
                        }
                    }
                    $script:perfmonobject = $true
                }
            }
        }
    }

    # Gotta collect all the paths first then process them otherwise there may be duplicates
    end {
        $allpaths = $allpaths | Where-Object { $_ -match '.blg' } | Select-Object -Unique

        if (-not $allpaths) {
            Stop-Function -Message "Could not find matching .blg files" -Target $file -Continue
            return
        }

        $scriptblock = {
            if ($args) {
                $file = $args
            }
            else {
                $file = $psitem
            }
            $item = Get-ChildItem -Path $file -ErrorAction SilentlyContinue

            if ($null -eq $item) {
                Stop-Function -Message "$file does not exist." -Target $file -Continue
                return
            }

            if (-not $script:destinationset -and $file -match "C\:\\.*Admin.*") {
                $null = Test-ElevationRequirement -ComputerName $env:COMPUTERNAME -Continue
            }

            if ($script:destinationset -eq $false -and -not $Append) {
                $Destination = Join-Path (Split-Path $file) $item.BaseName
            }

            if ($Destination -and $Destination -notmatch "\." -and -not $Append -and $script:perfmonobject) {
                # if destination is set, then it needs a different name
                if ($script:destinationset -eq $true) {
                    if ($file -match "\:") {
                        $computer = $env:COMPUTERNAME
                    }
                    else {
                        $computer = $file.Split("\")[2]
                    }
                    # Avoid naming conflicts
                    $timestamp = Get-Date -format yyyyMMddHHmmfff
                    $Destination = Join-Path $originaldestination "$computer - $($item.BaseName) - $timestamp"
                }
            }

            $params = @("`"$file`"")

            if ($Append) {
                $params += "-a"
            }

            if ($PerformanceCounter) {
                $parsedcounters = $PerformanceCounter -join " "
                $params += "-c `"$parsedcounters`""
            }

            if ($PerformanceCounterPath) {
                $params += "-cf `"$PerformanceCounterPath`""
            }

            $params += "-f $Type"

            if ($Interval) {
                $params += "-t $Interval"
            }

            if ($Destination) {
                $params += "-o `"$Destination`""
            }

            if ($script:beginstring) {
                $params += "-b $script:beginstring"
            }

            if ($script:endstring) {
                $params += "-e $script:endstring"
            }

            if ($ConfigPath) {
                $params += "-config $ConfigPath"
            }

            if ($Summary) {
                $params += "-q"
            }


            if (-not ($Destination.StartsWith("DSN"))) {
                $outputisfile = $true
            }
            else {
                $outputisfile = $false
            }

            if ($outputisfile) {
                if ($Destination) {
                    $dir = Split-Path $Destination
                    if (-not (Test-Path -Path $dir)) {
                        try {
                            $null = New-Item -ItemType Directory -Path $dir -ErrorAction Stop
                        }
                        catch {
                            Stop-Function -Message "Failure" -ErrorRecord $_ -Target $Destination -Continue
                        }
                    }

                    if ((Test-Path $Destination) -and -not $Append -and ((Get-Item $Destination) -isnot [System.IO.DirectoryInfo])) {
                        if ($AllowClobber) {
                            try {
                                Remove-Item -Path "$Destination" -ErrorAction Stop
                            }
                            catch {
                                Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
                            }
                        }
                        else {
                            if ($Type -eq "bin") {
                                Stop-Function -Message "$Destination exists. Use -AllowClobber to overwrite or -Append to append." -Continue
                            }
                            else {
                                Stop-Function -Message "$Destination exists. Use -AllowClobber to overwrite." -Continue
                            }
                        }
                    }

                    if ((Test-Path "$Destination.$type") -and -not $Append) {
                        if ($AllowClobber) {
                            try {
                                Remove-Item -Path "$Destination.$type" -ErrorAction Stop
                            }
                            catch {
                                Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
                            }
                        }
                        else {
                            if ($Type -eq "bin") {
                                Stop-Function -Message "$("$Destination.$type") exists. Use -AllowClobber to overwrite or -Append to append." -Continue
                            }
                            else {
                                Stop-Function -Message "$("$Destination.$type") exists. Use -AllowClobber to overwrite." -Continue
                            }
                        }
                    }
                }
            }

            $arguments = ($params -join " ")

            try {
                if ($Raw) {
                    Write-Message -Level Output -Message "relog $arguments"
                    cmd /c "relog $arguments"
                }
                else {
                    Write-Message -Level Verbose -Message "relog $arguments"
                    $scriptblock = {
                        $output = (cmd /c "relog $arguments" | Out-String).Trim()

                        if ($output -notmatch "Success") {
                            Stop-Function -Continue -Message $output.Trim("Input")
                        }
                        else {
                            Write-Message -Level Verbose -Message "$output"
                            $array = $output -Split [environment]::NewLine
                            $files = $array | Select-String "File:"

                            foreach ($rawfile in $files) {
                                $rawfile = $rawfile.ToString().Replace("File:", "").Trim()
                                $gcierror = $null
                                Get-ChildItem $rawfile -ErrorAction SilentlyContinue -ErrorVariable gcierror | Add-Member -MemberType NoteProperty -Name RelogFile -Value $true -PassThru -ErrorAction Ignore
                                if ($gcierror) {
                                    Write-Message -Level Verbose -Message "$gcierror"
                                }
                            }
                        }
                    }
                    Invoke-Command -ScriptBlock $scriptblock
                }
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_ -Target $path
            }
        }

        if ($Multithread) {
            $allpaths | Invoke-Parallel -ImportVariables -ImportModules -ScriptBlock $scriptblock -ErrorAction SilentlyContinue -ErrorVariable parallelerror
            if ($parallelerror) {
                Write-Message -Level Verbose -Message "$parallelerror"
            }
        }
        else {
            foreach ($file in $allpaths) { Invoke-Command -ScriptBlock $scriptblock -ArgumentList $file }
        }
    }
}
tools\dbatools\functions\Invoke-DbaSqlQuery.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Invoke-DbaSqlQuery {
    <#
        .SYNOPSIS
            A command to run explicit T-SQL commands or files.

        .DESCRIPTION
            This function is a wrapper command around Invoke-DbaSqlAsync, which in turn is based on Invoke-SqlCmd2.
            It was designed to be more convenient to use in a pipeline and to behave in a way consistent with the rest of our functions.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Credential object used to connect to the SQL Server Instance as a different user. This can be a Windows or SQL Server account. Windows users are determined by the existence of a backslash, so if you are intending to use an alternative Windows connection instead of a SQL login, ensure it contains a backslash.

        .PARAMETER Database
            The database to select before running the query. This list is auto-populated from the server.

        .PARAMETER Query
            Specifies one or more queries to be run. The queries can be Transact-SQL, XQuery statements, or sqlcmd commands. Multiple queries in a single batch may be separated by a semicolon or a GO

            Escape any double quotation marks included in the string.

            Consider using bracketed identifiers such as [MyTable] instead of quoted identifiers such as "MyTable".

        .PARAMETER QueryTimeout
            Specifies the number of seconds before the queries time out.

        .PARAMETER File
            Specifies the path to one or several files to be used as the query input.

        .PARAMETER SqlObject
            Specify on or multiple SQL objects. Those will be converted to script and their scripts run on the target system(s).

        .PARAMETER As
            Specifies output type. Valid options for this parameter are 'DataSet', 'DataTable', 'DataRow', 'PSObject', and 'SingleValue'

            PSObject output introduces overhead but adds flexibility for working with results: http://powershell.org/wp/forums/topic/dealing-with-dbnull/

        .PARAMETER SqlParameters
            Specifies a hashtable of parameters for parameterized SQL queries.  http://blog.codinghorror.com/give-me-parameterized-sql-or-give-me-death/

        .PARAMETER AppendServerInstance
            If this switch is enabled, the SQL Server instance will be appended to PSObject and DataRow output.

        .PARAMETER MessagesToOutput
            Use this switch to have on the output stream messages too (e.g. PRINT statements). Output will hold the resultset too. See examples for detail

        .PARAMETER InputObject
            A collection of databases (such as returned by Get-DbaDatabase)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database, Query
            Author: Fred Winmann (@FredWeinmann)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Invoke-DbaSqlQuery

        .EXAMPLE
            Invoke-DbaSqlQuery -SqlInstance server\instance -Query 'SELECT foo FROM bar'

            Runs the sql query 'SELECT foo FROM bar' against the instance 'server\instance'

        .EXAMPLE
            Get-DbaRegisteredServer -SqlInstance [SERVERNAME] -Group [GROUPNAME] | Invoke-DbaSqlQuery -Query 'SELECT foo FROM bar'

            Runs the sql query 'SELECT foo FROM bar' against all instances in the group [GROUPNAME] on the CMS [SERVERNAME]

        .EXAMPLE
            "server1", "server1\nordwind", "server2" | Invoke-DbaSqlQuery -File "C:\scripts\sql\rebuild.sql"

            Runs the sql commands stored in rebuild.sql against the instances "server1", "server1\nordwind" and "server2"

        .EXAMPLE
            Get-DbaDatabase -SqlInstance "server1", "server1\nordwind", "server2" | Invoke-DbaSqlQuery -File "C:\scripts\sql\rebuild.sql"

            Runs the sql commands stored in rebuild.sql against all accessible databases of the instances "server1", "server1\nordwind" and "server2"
    #>
    [CmdletBinding(DefaultParameterSetName = "Query")]
    Param (
        [parameter(ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance[]]
        $SqlInstance,

        [Alias("Credential")]
        [PsCredential]
        $SqlCredential,

        [object]$Database,

        [Parameter(Mandatory = $true, Position = 0, ParameterSetName = "Query")]
        [string]
        $Query,

        [Int32]
        $QueryTimeout = 600,

        [Parameter(Mandatory = $true, ParameterSetName = "File")]
        [object[]]
        $File,

        [Parameter(Mandatory = $true, ParameterSetName = "SMO")]
        [Microsoft.SqlServer.Management.Smo.SqlSmoObject[]]
        $SqlObject,

        [ValidateSet("DataSet", "DataTable", "DataRow", "PSObject", "SingleValue")]
        [string]
        $As = "DataRow",

        [System.Collections.IDictionary]
        $SqlParameters,

        [switch]
        $AppendServerInstance,

        [switch]
        $MessagesToOutput,

        [parameter(ValueFromPipeline = $true)]
        [Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,

        [Alias('Silent')]
        [switch]
        $EnableException

    )

    begin {
        Write-Message -Level Debug -Message "Bound parameters: $($PSBoundParameters.Keys -join ", ")"

        $splatInvokeDbaSqlAsync = @{
            As      = $As
        }

        if (Test-Bound -ParameterName "SqlParameters") {
            $splatInvokeDbaSqlAsync["SqlParameters"] = $SqlParameters
        }
        if (Test-Bound -ParameterName "AppendServerInstance") {
            $splatInvokeDbaSqlAsync["AppendServerInstance"] = $AppendServerInstance
        }
        if (Test-Bound -ParameterName "Query") {
            $splatInvokeDbaSqlAsync["Query"] = $Query
        }
        if (Test-Bound -ParameterName "QueryTimeout") {
            $splatInvokeDbaSqlAsync["QueryTimeout"] = $QueryTimeout
        }
        if (Test-Bound -ParameterName "MessagesToOutput") {
            $splatInvokeDbaSqlAsync["MessagesToOutput"] = $MessagesToOutput
        }
        if (Test-Bound -ParameterName "Verbose") {
            $splatInvokeDbaSqlAsync["Verbose"] = $Verbose
        }


        if (Test-Bound -ParameterName "File") {
            $files = @()
            $temporaryFiles = @()
            $temporaryFilesCount = 0
            $temporaryFilesPrefix = (97 .. 122 | Get-Random -Count 10 | ForEach-Object { [char]$_ }) -join ''

            foreach ($item in $File) {
                if ($null -eq $item) { continue }

                $type = $item.GetType().FullName

                switch ($type) {
                    "System.IO.DirectoryInfo" {
                        if (-not $item.Exists) {
                            Stop-Function -Message "Directory not found!" -Category ObjectNotFound
                            return
                        }
                        $files += ($item.GetFiles() | Where-Object Extension -EQ ".sql").FullName

                    }
                    "System.IO.FileInfo" {
                        if (-not $item.Exists) {
                            Stop-Function -Message "Directory not found!" -Category ObjectNotFound
                            return
                        }

                        $files += $item.FullName
                    }
                    "System.String" {
                        $uri = [uri]$item

                        switch -regex ($uri.Scheme) {
                            "http" {
                                $tempfile = "$env:TEMP\$temporaryFilesPrefix-$temporaryFilesCount.sql"
                                try {
                                    Invoke-WebRequest -Uri $item -OutFile $tempfile -ErrorAction Stop
                                    $files += $tempfile
                                    $temporaryFilesCount++
                                    $temporaryFiles += $tempfile
                                }
                                catch {
                                    Stop-Function -Message "Failed to download file $item" -ErrorRecord $_
                                    return
                                }
                            }
                            default {
                                try {
                                    $paths = Resolve-Path $item | Select-Object -ExpandProperty Path | Get-Item -ErrorAction Stop
                                }
                                catch {
                                    Stop-Function -Message "Failed to resolve path: $item" -ErrorRecord $_
                                    return
                                }

                                foreach ($path in $paths) {
                                    if (-not $path.PSIsContainer) {
                                        if (([uri]$path.FullName).Scheme -ne 'file') {
                                            Stop-Function -Message "Could not resolve path $path as filesystem object"
                                            return
                                        }
                                        $files += $path.FullName
                                    }
                                }
                            }
                        }
                    }
                    default {
                        Stop-Function -Message "Unkown input type: $type" -Category InvalidArgument
                        return
                    }
                }
            }
        }

        if (Test-Bound -ParameterName "SqlObject") {
            $files = @()
            $temporaryFiles = @()
            $temporaryFilesCount = 0
            $temporaryFilesPrefix = (97 .. 122 | Get-Random -Count 10 | ForEach-Object { [char]$_ }) -join ''

            foreach ($object in $SqlObject) {
                try { $code = Export-DbaScript -InputObject $object -Passthru -EnableException }
                catch {
                    Stop-Function -Message "Failed to generate script for object $object" -ErrorRecord $_
                    return
                }

                try {
                    $newfile = "$env:TEMP\$temporaryFilesPrefix-$temporaryFilesCount.sql"
                    Set-Content -Value $code -Path $newfile -Force -ErrorAction Stop -Encoding UTF8
                    $files += $newfile
                    $temporaryFilesCount++
                    $temporaryFiles += $newfile
                }
                catch {
                    Stop-Function -Message "Failed to write sql script to temp" -ErrorRecord $_
                    return
                }
            }
        }
    }

    process {
        if (Test-FunctionInterrupt) { return }
        if (Test-Bound -ParameterName "Database", "InputObject" -And) {
            Stop-Function -Category InvalidArgument -Message "You can't use -Database with piped databases"
            return
        }
        if (Test-Bound -ParameterName "SqlInstance", "InputObject" -And) {
            Stop-Function -Category InvalidArgument -Message "You can't use -SqlInstance with piped databases"
            return
        }

        foreach ($db in $InputObject) {
            if (!$db.IsAccessible) {
                Write-Message -Level Warning -Message "Database $db is not accessible. Skipping."
                continue
            }
            $server = $db.Parent
            $conncontext = $server.ConnectionContext
            if ($conncontext.DatabaseName -ne $db.Name) {
                $conncontext = $server.ConnectionContext.Copy()
                $conncontext.DatabaseName = $db.Name
            }
            try {
                if ($File -or $SqlObject) {
                    foreach ($item in $files) {
                        if ($null -eq $item) {continue}
                        $filePath = $(Resolve-Path -LiteralPath $item).ProviderPath
                        $QueryfromFile = [System.IO.File]::ReadAllText("$filePath")
                        Invoke-DbaSqlAsync -SQLConnection $conncontext @splatInvokeDbaSqlAsync -Query $QueryfromFile
                    }
                }
                else { Invoke-DbaSqlAsync -SQLConnection $conncontext @splatInvokeDbaSqlAsync }
            }
            catch {
                Stop-Function -Message "[$db] Failed during execution" -ErrorRecord $_ -Target $server -Continue
            }
        }
        foreach ($instance in $SqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_ -Target $instance -Continue
            }
            $conncontext = $server.ConnectionContext
            try {
                if ($Database -and $conncontext.DatabaseName -ne $Database) {
                    $conncontext = $server.ConnectionContext.Copy()
                    $conncontext.DatabaseName = $Database
                }
                if ($File -or $SqlObject) {
                    foreach ($item in $files) {
                        if ($null -eq $item) {continue}
                        $filePath = $(Resolve-Path -LiteralPath $item).ProviderPath
                        $QueryfromFile = [System.IO.File]::ReadAllText("$filePath")
                        Invoke-DbaSqlAsync -SQLConnection $conncontext @splatInvokeDbaSqlAsync -Query $QueryfromFile
                    }
                }
                else {
                    Invoke-DbaSqlAsync -SQLConnection $conncontext @splatInvokeDbaSqlAsync
                }
            }
            catch {
                Stop-Function -Message "[$instance] Failed during execution" -ErrorRecord $_ -Target $instance -Continue
            }
        }
    }

    end {
        # Execute end even when interrupting, as only used for cleanup

        if ($temporaryFiles) {
            # Clean up temporary files that were downloaded
            foreach ($item in $temporaryFiles) {
                Remove-Item -Path $item -ErrorAction Ignore
            }
        }
        Test-DbaDeprecation -DeprecatedOn '1.0.0' -Alias Invoke-DbaSqlCmd
    }
}
tools\dbatools\functions\Invoke-DbaWhoisActive.ps1
function Invoke-DbaWhoIsActive {
    <#
        .SYNOPSIS
            Outputs results of Adam Machanic's sp_WhoIsActive DataTable

        .DESCRIPTION
            Output results of Adam Machanic's sp_WhoIsActive

            This command was built with Adam's permission. To read more about sp_WhoIsActive, please visit:

            Updates: http://sqlblog.com/blogs/adam_machanic/archive/tags/who+is+active/default.aspx

            Also, consider donating to Adam if you find this stored procedure helpful: http://tinyurl.com/WhoIsActiveDonate

        .PARAMETER SqlInstance
            The SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER Database
            The database where sp_WhoIsActive is installed. Defaults to master. If the sp_WhoIsActive is not installed, the command will warn and exit.

        .PARAMETER Filter
            FiltersBoth inclusive and exclusive
            Set either filter to '' to disable
            Session is a session ID, and either 0 or '' can be used to indicate "all" sessions
            All other filter types support % or _ as wildcards

        .PARAMETER FilterType
            Valid filter types are: session, program, database, login, and host

        .PARAMETER NotFilter
            FiltersBoth inclusive and exclusive
            Set either filter to '' to disable
            Session is a session ID, and either 0 or '' can be used to indicate "all" sessions
            All other filter types support % or _ as wildcards

        .PARAMETER NotFilterType
            Valid filter types are: session, program, database, login, and host

        .PARAMETER ShowOwnSpid
            Retrieve data about the calling session?

        .PARAMETER ShowSystemSpids
            Retrieve data about system sessions?

        .PARAMETER ShowSleepingSpids
            Controls how sleeping SPIDs are handled, based on the idea of levels of interest
            0 does not pull any sleeping SPIDs
            1 pulls only those sleeping SPIDs that also have an open transaction
            2 pulls all sleeping SPIDs

        .PARAMETER GetFullInnerText
            If 1, gets the full stored procedure or running batch, when available
            If 0, gets only the actual statement that is currently running in the batch or procedure

        .PARAMETER GetPlans
            Get associated query plans for running tasks, if available
            If 1, gets the plan based on the request's statement offset
            If 2, gets the entire plan based on the request's plan_handle

        .PARAMETER GetOuterCommand
            Get the associated outer ad hoc query or stored procedure call, if available

        .PARAMETER GetTransactionInfo
            Enables pulling transaction log write info and transaction duration

        .PARAMETER GetTaskInfo
            Get information on active tasks, based on three interest levels
            Level 0 does not pull any task-related information
            Level 1 is a lightweight mode that pulls the top non-CXPACKET wait, giving preference to blockers
            Level 2 pulls all available task-based metrics, including:
            number of active tasks, current wait stats, physical I/O, context switches, and blocker information

        .PARAMETER GetLocks
            Gets associated locks for each request, aggregated in an XML format

        .PARAMETER GetAverageTime
            Get average time for past runs of an active query
            (based on the combination of plan handle, sql handle, and offset)

        .PARAMETER GetAdditonalInfo
            Get additional non-performance-related information about the session or request
            text_size, language, date_format, date_first, quoted_identifier, arithabort, ansi_null_dflt_on,
            ansi_defaults, ansi_warnings, ansi_padding, ansi_nulls, concat_null_yields_null,
            transaction_isolation_level, lock_timeout, deadlock_priority, row_count, command_type

            If a SQL Agent job is running, an subnode called agent_info will be populated with some or all of
            the following: job_id, job_name, step_id, step_name, msdb_query_error (in the event of an error)

            If @get_task_info is set to 2 and a lock wait is detected, a subnode called block_info will be
            populated with some or all of the following: lock_type, database_name, object_id, file_id, hobt_id,
            applock_hash, metadata_resource, metadata_class_id, object_name, schema_name

        .PARAMETER FindBlockLeaders
            Walk the blocking chain and count the number of
            total SPIDs blocked all the way down by a given session
            Also enables task_info Level 1, if @get_task_info is set to 0

        .PARAMETER DeltaInterval
            Pull deltas on various metrics
            Interval in seconds to wait before doing the second data pull

        .PARAMETER OutputColumnList
            List of desired output columns, in desired order
            Note that the final output will be the intersection of all enabled features and all
            columns in the list. Therefore, only columns associated with enabled features will
            actually appear in the output. Likewise, removing columns from this list may effectively
            disable features, even if they are turned on

            Each element in this list must be one of the valid output column names. Names must be
            delimited by square brackets. White space, formatting, and additional characters are
            allowed, as long as the list contains exact matches of delimited valid column names.

        .PARAMETER SortOrder
            Column(s) by which to sort output, optionally with sort directions.
            Valid column choices:
            session_id, physical_io, reads, physical_reads, writes, tempdb_allocations,
            tempdb_current, CPU, context_switches, used_memory, physical_io_delta,
            reads_delta, physical_reads_delta, writes_delta, tempdb_allocations_delta,
            tempdb_current_delta, CPU_delta, context_switches_delta, used_memory_delta,
            tasks, tran_start_time, open_tran_count, blocking_session_id, blocked_session_count,
            percent_complete, host_name, login_name, database_name, start_time, login_time

            Note that column names in the list must be bracket-delimited. Commas and/or white
            space are not required.

        .PARAMETER FormatOutput
            Formats some of the output columns in a more "human readable" form
            0 disables output format
            1 formats the output for variable-width fonts
            2 formats the output for fixed-width fonts

        .PARAMETER DestinationTable
            If set to a non-blank value, the script will attempt to insert into the specified destination table. Please note that the script will not verify that the table exists, or that it has the correct schema, before doing the insert. Table can be specified in one, two, or three-part format

        .PARAMETER ReturnSchema
            If set to 1, no data collection will happen and no result set will be returned; instead,
            a CREATE TABLE statement will be returned via the @schema parameter, which will match
            the schema of the result set that would be returned by using the same collection of the
            rest of the parameters. The CREATE TABLE statement will have a placeholder token of
            <table_name> in place of an actual table name.

        .PARAMETER Schema
            If set to 1, no data collection will happen and no result set will be returned; instead,
            a CREATE TABLE statement will be returned via the @schema parameter, which will match
            the schema of the result set that would be returned by using the same collection of the
            rest of the parameters. The CREATE TABLE statement will have a placeholder token of
            <table_name> in place of an actual table name.

        .PARAMETER Help
            Help! What do I do?

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: AdamMechanic, WhoIsActive, SpWhoIsActive
            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Invoke-DbaWhoIsActive

        .EXAMPLE
            Invoke-DbaWhoIsActive -SqlInstance sqlserver2014a

            Execute sp_whoisactive on sqlserver2014a. This command expects sp_WhoIsActive to be in the master database. Logs into the SQL Server with Windows credentials.

        .EXAMPLE
            Invoke-DbaWhoIsActive -SqlInstance sqlserver2014a -SqlCredential $credential -Database dbatools

            Execute sp_whoisactive on sqlserver2014a. This command expects sp_WhoIsActive to be in the dbatools database. Logs into the SQL Server with SQL Authentication.

        .EXAMPLE
            Invoke-DbaWhoIsActive -SqlInstance sqlserver2014a -GetAverageTime

            Similar to running sp_WhoIsActive @get_avg_time

        .EXAMPLE
            Invoke-DbaWhoIsActive -SqlInstance sqlserver2014a -GetOuterCommand -FindBlockLeaders

            Similar to running sp_WhoIsActive @get_outer_command = 1, @find_block_leaders = 1
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias('ServerInstance', 'SqlServer')]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]
        $SqlCredential,
        [object]$Database,
        [Alias('As')]
        [ValidateLength(0, 128)]
        [string]$Filter,
        [ValidateSet('Session', 'Program', 'Database', 'Login', 'Host')]
        [string]$FilterType = 'Session',
        [ValidateLength(0, 128)]
        [string]$NotFilter,
        [ValidateSet('Session', 'Program', 'Database', 'Login', 'Host')]
        [string]$NotFilterType = 'Session',
        [switch]$ShowOwnSpid,
        [switch]$ShowSystemSpids,
        [ValidateRange(0, 255)]
        [int]$ShowSleepingSpids,
        [switch]$GetFullInnerText,
        [ValidateRange(0, 255)]
        [int]$GetPlans,
        [switch]$GetOuterCommand,
        [switch]$GetTransactionInfo,
        [ValidateRange(0, 2)]
        [int]$GetTaskInfo,
        [switch]$GetLocks,
        [switch]$GetAverageTime,
        [switch]$GetAdditonalInfo,
        [switch]$FindBlockLeaders,
        [ValidateRange(0, 255)]
        [int]$DeltaInterval,
        [ValidateLength(0, 8000)]
        [string]$OutputColumnList = '[dd%][session_id][sql_text][sql_command][login_name][wait_info][tasks][tran_log%][cpu%][temp%][block%][reads%][writes%][context%][physical%][query_plan][locks][%]',
        [ValidateLength(0, 500)]
        [string]$SortOrder = '[start_time] ASC',
        [ValidateRange(0, 255)]
        [int]$FormatOutput = 1,
        [ValidateLength(0, 4000)]
        [string]$DestinationTable = '',
        [switch]$ReturnSchema,
        [string]$Schema,
        [switch]$Help,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $passedparams = $psboundparameters.Keys | Where-Object { 'Silent', 'SqlServer', 'SqlCredential', 'OutputAs', 'ServerInstance', 'SqlInstance', 'Database' -notcontains $_ }
        $localparams = $psboundparameters
    }

    process {

        foreach ($instance in $sqlinstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($server.VersionMajor -lt 9) {
                throw "sp_WhoIsActive is only supported in SQL Server 2005 and above"
            }

            $paramdictionary = @{
                Filter             = '@filter'
                FilterType         = '@filter_type'
                NotFilter          = 'not_filter'
                NotFilterType      = '@not_filter_type'
                ShowOwnSpid        = '@show_own_spid'
                ShowSystemSpids    = '@show_system_spids'
                ShowSleepingSpids  = '@show_sleeping_spids'
                GetFullInnerText   = '@get_full_inner_text'
                GetPlans           = '@get_plans'
                GetOuterCommand    = '@get_outer_command'
                GetTransactionInfo = '@get_transaction_info'
                GetTaskInfo        = '@get_task_info'
                GetLocks           = '@get_locks '
                GetAverageTime     = '@get_avg_time'
                GetAdditonalInfo   = '@get_additional_info'
                FindBlockLeaders   = '@find_block_leaders'
                DeltaInterval      = '@delta_interval'
                OutputColumnList   = '@output_column_list'
                SortOrder          = '@sort_order'
                FormatOutput       = '@format_output '
                DestinationTable   = '@destination_table '
                ReturnSchema       = '@return_schema'
                Schema             = '@schema'
                Help               = '@help'
            }

            Write-Message -Level Verbose -Message "Collecting sp_whoisactive data from server: $instance"

            try {
                $sqlconnection = New-Object System.Data.SqlClient.SqlConnection
                $sqlconnection.ConnectionString = $server.ConnectionContext.ConnectionString
                $sqlconnection.Open()

                if ($Database) {
                    # database is being returned as something weird. change it to string without using a method then trim.
                    $Database = "$Database"
                    $Database = $Database.Trim()
                    $sqlconnection.ChangeDatabase($Database)
                }

                $sqlcommand = New-Object System.Data.SqlClient.SqlCommand
                $sqlcommand.CommandType = "StoredProcedure"
                $sqlcommand.CommandText = "dbo.sp_WhoIsActive"
                $sqlcommand.Connection = $sqlconnection

                foreach ($param in $passedparams) {
                    Write-Message -Level Verbose -Message "Check parameter '$param'"

                    $sqlparam = $paramdictionary[$param]

                    if ($sqlparam) {

                        $value = $localparams[$param]

                        switch ($value) {
                            $true { $value = 1 }
                            $false { $value = 0 }
                        }
                        Write-Message -Level Verbose -Message "Adding parameter '$sqlparam' with value '$value'"
                        [Void]$sqlcommand.Parameters.AddWithValue($sqlparam, $value)
                    }
                }

                $datatable = New-Object system.Data.DataSet
                $dataadapter = New-Object system.Data.SqlClient.SqlDataAdapter($sqlcommand)
                $dataadapter.fill($datatable) | Out-Null
                $datatable.Tables.Rows
            }
            catch {
                if ($_.Exception.InnerException -Like "*Could not find*") {
                    Stop-Function -Message "sp_whoisactive not found, please install using Install-DbaWhoIsActive." -Continue
                }
                else {
                    Stop-Function -Message "Invalid query." -Continue
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Show-SqlWhoIsActive -CustomMessage "Show-SqlWhoIsActive is no longer supported. Use Invoke-DbaWhoIsActive | Out-GridView for similar results."
    }
}
tools\dbatools\functions\Invoke-DbaXEReplay.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Invoke-DbaXeReplay {
    <#
        .SYNOPSIS
            This command replays events from Read-DbaXEFile on one or more target servers

        .DESCRIPTION
            This command replays events from Read-DbaXEFile. It is simplistic in its approach.

            - Writes all queries to a temp sql file
            - Executes temp file using . $sqlcmd so that batches are executed properly
            - Deletes temp file

        .PARAMETER SqlInstance
            Target SQL Server(s)

        .PARAMETER SqlCredential
            Used to provide alternative credentials.

        .PARAMETER Database
            The initial starting database.

        .PARAMETER Event
            Each Response can be limited to processing specific events, while ignoring all the other ones. When this attribute is omitted, all events are processed.

        .PARAMETER Raw
            By dafault, the results of . $sqlcmd are collected, cleaned up and displayed. If you'd like to see all results immeidately, use Raw.

        .PARAMETER InputObject
            Accepts the object output of Read-DbaXESession.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            Read-DbaXEFile -Path C:\temp\sample.xel | Invoke-DbaXeReplay -SqlInstance sql2017

            Runs all batch_text for sql_batch_completed against tempdb on sql2017.

        .EXAMPLE
            Read-DbaXEFile -Path C:\temp\sample.xel | Invoke-DbaXeReplay -SqlInstance sql2017 -Database planning -Event sql_batch_completed

            Sets the *initial* database to planning then runs only sql_batch_completed against sql2017.

        .EXAMPLE
            Read-DbaXEFile -Path C:\temp\sample.xel | Invoke-DbaXeReplay -SqlInstance sql2017, sql2016

            Runs all batch_text for sql_batch_completed against tempdb on sql2017 and sql2016.

    #>
    Param (
        [Parameter(Mandatory)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance[]]$SqlInstance,
        [Alias("Credential")]
        [PsCredential]$SqlCredential,
        [string[]]$Database,
        [string[]]$Event = @('sql_batch_completed', 'rcp_completed'),
        [Parameter(Mandatory, ValueFromPipeline)]
        [object]$InputObject,
        [switch]$Raw,
        [switch]$EnableException
    )

    begin {
        $querycolumns = 'statement', 'batch_text'
        $timestamp = (Get-Date -Format yyyyMMddHHmm)
        $temp = ([System.IO.Path]::GetTempPath()).TrimEnd("\")
        $filename = "$temp\dbatools-replay-$timestamp.sql"
        Set-Content $filename -Value $null

        $sqlcmd = "$script:PSModuleRoot\bin\sqlcmd\sqlcmd.exe"
    }
    process {
        if (Test-FunctionInterrupt) { return }
        if ($InputObject.Name -notin $Event) {
            continue
        }

        if ($InputObject.statement) {
            if ($InputObject.statement -notmatch "ALTER EVENT SESSION") {
                Add-Content -Path $filename -Value $InputObject.statement
                Add-Content -Path $filename -Value "GO"
            }
        }
        else {
            if ($InputObject.batch_text -notmatch "ALTER EVENT SESSION") {
                Add-Content -Path $filename -Value $InputObject.batch_text
                Add-Content -Path $filename -Value "GO"
            }
        }
    }
    end {
        if (Test-FunctionInterrupt) { return }
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level VeryVerbose -Message "Connecting to $instance." -Target $instance
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_ -Target $instance -Continue
            }


            if ($Raw) {
                if (Test-Bound -ParameterName SqlCredential) {
                    . $sqlcmd -S $instance -i $filename -U $SqlCredential.Username -P $SqlCredential.GetNetworkCredential().Password
                    continue
                }
                else {
                    . $sqlcmd -S $instance -i $filename
                    continue
                }
            }

            if (Test-Bound -ParameterName SqlCredential) {
                $output = . $sqlcmd -S $instance -i $filename -U $SqlCredential.Username -P $SqlCredential.GetNetworkCredential().Password
            }
            else {
                $output = . $sqlcmd -S $instance -i $filename
            }

            foreach ($line in $output) {
                $newline = $line.Trim()
                if ($newline -and $newline -notmatch "------------------------------------------------------------------------------------") {
                    "$newline"
                }
            }
        }
        Remove-Item -Path $filename -ErrorAction Ignore
    }
}
tools\dbatools\functions\Invoke-SqlCmd2.ps1
function Invoke-Sqlcmd2 {
    <#
        .SYNOPSIS
            Runs a T-SQL script.

        .DESCRIPTION
            Runs a T-SQL script. Invoke-Sqlcmd2 runs the whole script and only captures the first selected result set, such as the output of PRINT statements when -verbose parameter is specified.
            Parameterized queries are supported.

            Help details below borrowed from Invoke-Sqlcmd

        .PARAMETER ServerInstance
            Specifies the SQL Server instance(s) to execute the query against.

        .PARAMETER Database
            Specifies the name of the database to execute the query against. If specified, this database will be used in the ConnectionString when establishing the connection to SQL Server.

            If a SQLConnection is provided, the default database for that connection is overridden with this database.

        .PARAMETER Query
            Specifies one or more queries to be run. The queries can be Transact-SQL, XQuery statements, or sqlcmd commands. Multiple queries in a single batch may be separated by a semicolon.

            Do not specify the sqlcmd GO separator (or, use the ParseGo parameter). Escape any double quotation marks included in the string.

            Consider using bracketed identifiers such as [MyTable] instead of quoted identifiers such as "MyTable".

        .PARAMETER InputFile
            Specifies the full path to a file to be used as the query input to Invoke-Sqlcmd2. The file can contain Transact-SQL statements, XQuery statements, sqlcmd commands and scripting variables.

        .PARAMETER Credential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

            SECURITY NOTE: If you use the -Debug switch, the connectionstring including plain text password will be sent to the debug stream.

        .PARAMETER Encrypt
            If this switch is enabled, the connection to SQL Server will be made using SSL.

            This requires that the SQL Server has been set up to accept SSL requests. For information regarding setting up SSL on SQL Server, see https://technet.microsoft.com/en-us/library/ms189067(v=sql.105).aspx

        .PARAMETER QueryTimeout
            Specifies the number of seconds before the queries time out.

        .PARAMETER ConnectionTimeout
            Specifies the number of seconds before Invoke-Sqlcmd2 times out if it cannot successfully connect to an instance of the Database Engine. The timeout value must be an integer between 0 and 65534. If 0 is specified, connection attempts do not time out.

        .PARAMETER As
            Specifies output type. Valid options for this parameter are 'DataSet', 'DataTable', 'DataRow', 'PSObject', and 'SingleValue'

            PSObject output introduces overhead but adds flexibility for working with results: http://powershell.org/wp/forums/topic/dealing-with-dbnull/

        .PARAMETER SqlParameters
            Specifies a hashtable of parameters for parameterized SQL queries.  http://blog.codinghorror.com/give-me-parameterized-sql-or-give-me-death/

            Example:

        .PARAMETER AppendServerInstance
            If this switch is enabled, the SQL Server instance will be appended to PSObject and DataRow output.

        .PARAMETER ParseGo
            If this switch is enabled, "GO" statements will be handled automatically.
            Every "GO" will effectively run in a separate query, like if you issued multiple Invoke-SqlCmd2 commands.
            "GO"s will be recognized if they are on a single line, as this covers
            the 95% of the cases "GO" parsing is needed
            Note:
                Queries will always target that database, e.g. if you have this Query:
                    USE DATABASE [dbname]
                    GO
                    SELECT * from sys.tables
                and you call it via
                    Invoke-SqlCmd2 -ServerInstance instance -Database msdb -Query ...
                you'll get back tables from msdb, not dbname.


        .PARAMETER SQLConnection
            Specifies an existing SQLConnection object to use in connecting to SQL Server. If the connection is closed, an attempt will be made to open it.

        .INPUTS
            None
                You cannot pipe objects to Invoke-Sqlcmd2

        .OUTPUTS
        As PSObject:     System.Management.Automation.PSCustomObject
        As DataRow:      System.Data.DataRow
        As DataTable:    System.Data.DataTable
        As DataSet:      System.Data.DataTableCollectionSystem.Data.DataSet
        As SingleValue:  Dependent on data type in first column.

        .EXAMPLE
            Invoke-Sqlcmd2 -ServerInstance "MyComputer\MyInstance" -Query "SELECT login_time AS 'StartTime' FROM sysprocesses WHERE spid = 1"

            Connects to a named instance of the Database Engine on a computer and runs a basic T-SQL query.

            StartTime
            -----------
            2010-08-12 21:21:03.593

        .EXAMPLE
            Invoke-Sqlcmd2 -ServerInstance "MyComputer\MyInstance" -InputFile "C:\MyFolder\tsqlscript.sql" | Out-File -filePath "C:\MyFolder\tsqlscript.rpt"

            Reads a file containing T-SQL statements, runs the file, and writes the output to another file.

        .EXAMPLE
            Invoke-Sqlcmd2  -ServerInstance "MyComputer\MyInstance" -Query "PRINT 'hello world'" -Verbose

            Uses the PowerShell -Verbose parameter to return the message output of the PRINT command.
            VERBOSE: hello world

        .EXAMPLE
            Invoke-Sqlcmd2 -ServerInstance MyServer\MyInstance -Query "SELECT ServerName, VCNumCPU FROM tblServerInfo" -as PSObject | ?{$_.VCNumCPU -gt 8}
            Invoke-Sqlcmd2 -ServerInstance MyServer\MyInstance -Query "SELECT ServerName, VCNumCPU FROM tblServerInfo" -as PSObject | ?{$_.VCNumCPU}

            This example uses the PSObject output type to allow more flexibility when working with results.

            If we used DataRow rather than PSObject, we would see the following behavior:
                Each row where VCNumCPU does not exist would produce an error in the first example
                Results would include rows where VCNumCPU has DBNull value in the second example

        .EXAMPLE
            'Instance1', 'Server1/Instance1', 'Server2' | Invoke-Sqlcmd2 -query "Sp_databases" -as psobject -AppendServerInstance

            This example lists databases for each instance.  It includes a column for the ServerInstance in question.
                DATABASE_NAME          DATABASE_SIZE REMARKS        ServerInstance
                -------------          ------------- -------        --------------
                REDACTED                       88320                Instance1
                master                         17920                Instance1
                ...
                msdb                          618112                Server1/Instance1
                tempdb                        563200                Server1/Instance1
                ...
                OperationsManager           20480000                Server2

        .EXAMPLE
            #Construct a query using SQL parameters
                $Query = "SELECT ServerName, VCServerClass, VCServerContact FROM tblServerInfo WHERE VCServerContact LIKE @VCServerContact AND VCServerClass LIKE @VCServerClass"

            #Run the query, specifying values for SQL parameters
                Invoke-Sqlcmd2 -ServerInstance SomeServer\NamedInstance -Database ServerDB -query $query -SqlParameters @{ VCServerContact="%cookiemonster%"; VCServerClass="Prod" }

                ServerName    VCServerClass VCServerContact
                ----------    ------------- ---------------
                SomeServer1   Prod          cookiemonster, blah
                SomeServer2   Prod          cookiemonster
                SomeServer3   Prod          blah, cookiemonster

        .EXAMPLE
            Invoke-Sqlcmd2 -SQLConnection $Conn -Query "SELECT login_time AS 'StartTime' FROM sysprocesses WHERE spid = 1"

            Uses an existing SQLConnection and runs a basic T-SQL query against it

            StartTime
            -----------
            2010-08-12 21:21:03.593

        .EXAMPLE
            Invoke-SqlCmd -SQLConnection $Conn -Query "SELECT ServerName FROM tblServerInfo WHERE ServerName LIKE @ServerName" -SqlParameters @{"ServerName = "c-is-hyperv-1"}

            Executes a parameterized query against the existing SQLConnection, with a collection of one parameter to be passed to the query when executed.

        .NOTES
            Changelog moved to CHANGELOG.md:

            https://github.com/sqlcollaborative/Invoke-SqlCmd2/blob/master/CHANGELOG.md

        .LINK
            https://github.com/sqlcollaborative/Invoke-SqlCmd2

        .LINK
            https://github.com/RamblingCookieMonster/PowerShell

        .FUNCTIONALITY
            SQL
    #>

    [CmdletBinding(DefaultParameterSetName = 'Ins-Que')]
    [OutputType([System.Management.Automation.PSCustomObject], [System.Data.DataRow], [System.Data.DataTable], [System.Data.DataTableCollection], [System.Data.DataSet])]
    param (
        [Parameter(ParameterSetName = 'Ins-Que',
            Position = 0,
            Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false,
            HelpMessage = 'SQL Server Instance required...')]
        [Parameter(ParameterSetName = 'Ins-Fil',
            Position = 0,
            Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false,
            HelpMessage = 'SQL Server Instance required...')]
        [Alias('Instance', 'Instances', 'ComputerName', 'Server', 'Servers', 'SqlInstance')]
        [ValidateNotNullOrEmpty()]
        [string[]]$ServerInstance,
        [Parameter(Position = 1,
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false)]
        [string]$Database,
        [Parameter(ParameterSetName = 'Ins-Que',
            Position = 2,
            Mandatory = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false)]
        [Parameter(ParameterSetName = 'Con-Que',
            Position = 2,
            Mandatory = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false)]
        [string]$Query,
        [Parameter(ParameterSetName = 'Ins-Fil',
            Position = 2,
            Mandatory = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false)]
        [Parameter(ParameterSetName = 'Con-Fil',
            Position = 2,
            Mandatory = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false)]
        [ValidateScript( { Test-Path -LiteralPath $_ })]
        [string]$InputFile,
        [Parameter(ParameterSetName = 'Ins-Que',
            Position = 3,
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false)]
        [Parameter(ParameterSetName = 'Ins-Fil',
            Position = 3,
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false)]
        [Alias('SqlCredential')]
        [System.Management.Automation.PSCredential]$Credential,
        [Parameter(ParameterSetName = 'Ins-Que',
            Position = 4,
            Mandatory = $false,
            ValueFromRemainingArguments = $false)]
        [Parameter(ParameterSetName = 'Ins-Fil',
            Position = 4,
            Mandatory = $false,
            ValueFromRemainingArguments = $false)]
        [switch]$Encrypt,
        [Parameter(Position = 5,
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false)]
        [Int32]$QueryTimeout = 600,
        [Parameter(ParameterSetName = 'Ins-Fil',
            Position = 6,
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false)]
        [Parameter(ParameterSetName = 'Ins-Que',
            Position = 6,
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false)]
        [Int32]$ConnectionTimeout = 15,
        [Parameter(Position = 7,
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false)]
        [ValidateSet("DataSet", "DataTable", "DataRow", "PSObject", "SingleValue")]
        [string]$As = "DataRow",
        [Parameter(Position = 8,
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false)]
        [System.Collections.IDictionary]$SqlParameters,
        [Parameter(Position = 9,
            Mandatory = $false)]
        [switch]$AppendServerInstance,
        [Parameter(Position = 10,
            Mandatory = $false)]
        [switch]$ParseGO,
        [Parameter(ParameterSetName = 'Con-Que',
            Position = 11,
            Mandatory = $false,
            ValueFromPipeline = $false,
            ValueFromPipelineByPropertyName = $false,
            ValueFromRemainingArguments = $false)]
        [Parameter(ParameterSetName = 'Con-Fil',
            Position = 11,
            Mandatory = $false,
            ValueFromPipeline = $false,
            ValueFromPipelineByPropertyName = $false,
            ValueFromRemainingArguments = $false)]
        [Alias('Connection', 'Conn')]
        [ValidateNotNullOrEmpty()]
        [System.Data.SqlClient.SQLConnection]$SQLConnection
    )

    begin {
        if ($InputFile) {
            $filePath = $(Resolve-Path -LiteralPath $InputFile).ProviderPath
            $Query = [System.IO.File]::ReadAllText("$filePath")
        }

        Write-Verbose "Running Invoke-Sqlcmd2 with ParameterSet '$($PSCmdlet.ParameterSetName)'.  Performing query '$Query'."

        if ($As -eq "PSObject") {
            #This code scrubs DBNulls.  Props to Dave Wyatt
            $cSharp = @'
                using System;
                using System.Data;
                using System.Management.Automation;

                public class DBNullScrubber
                {
                    public static PSObject DataRowToPSObject(DataRow row)
                    {
                        PSObject psObject = new PSObject();

                        if (row != null && (row.RowState & DataRowState.Detached) != DataRowState.Detached)
                        {
                            foreach (DataColumn column in row.Table.Columns)
                            {
                                Object value = null;
                                if (!row.IsNull(column))
                                {
                                    value = row[column];
                                }

                                psObject.Properties.Add(new PSNoteProperty(column.ColumnName, value));
                            }
                        }

                        return psObject;
                    }
                }
'@

            try {
                Add-Type -TypeDefinition $cSharp -ReferencedAssemblies 'System.Data', 'System.Xml' -ErrorAction stop
            }
            catch {
                if (-not $_.ToString() -like "*The type name 'DBNullScrubber' already exists*") {
                    Write-Warning "Could not load DBNullScrubber.  Defaulting to DataRow output: $_."
                    $As = "Datarow"
                }
            }
        }

        #Handle existing connections
        if ($PSBoundParameters.ContainsKey('SQLConnection')) {
            if ($SQLConnection.State -notlike "Open") {
                try {
                    Write-Verbose "Opening connection from '$($SQLConnection.State)' state."
                    $SQLConnection.Open()
                }
                catch {
                    throw $_
                }
            }

            if ($Database -and $SQLConnection.Database -notlike $Database) {
                try {
                    Write-Verbose "Changing SQLConnection database from '$($SQLConnection.Database)' to $Database."
                    $SQLConnection.ChangeDatabase($Database)
                }
                catch {
                    throw "Could not change Connection database '$($SQLConnection.Database)' to $Database`: $_"
                }
            }

            if ($SQLConnection.state -like "Open") {
                $ServerInstance = @($SQLConnection.DataSource)
            }
            else {
                throw "SQLConnection is not open"
            }
        }
        $GoSplitterRegex = [regex]'(?smi)^[\s]*GO[\s]*$'

    }
    process {
        foreach ($SQLInstance in $ServerInstance) {
            Write-Verbose "Querying ServerInstance '$SQLInstance'"

            if ($PSBoundParameters.Keys -contains "SQLConnection") {
                $Conn = $SQLConnection
            }
            else {
                $CSBuilder = New-Object -TypeName System.Data.SqlClient.SqlConnectionStringBuilder
                $CSBuilder["Server"] = $SQLInstance
                $CSBuilder["Database"] = $Database
                $CSBuilder["Connection Timeout"] = $ConnectionTimeout

                if ($Encrypt) {
                    $CSBuilder["Encrypt"] = $true
                }

                if ($Credential) {
                    $CSBuilder["Trusted_Connection"] = $false
                    $CSBuilder["User ID"] = $Credential.UserName
                    $CSBuilder["Password"] = $Credential.GetNetworkCredential().Password
                }
                else {
                    $CSBuilder["Integrated Security"] = $true
                }
                if ($ApplicationName) {
                    $CSBuilder["Application Name"] = $ApplicationName
                }
                else {
                    $ScriptName = (Get-PSCallStack)[-1].Command.ToString()
                    if ($ScriptName -ne "<ScriptBlock>") {
                        $CSBuilder["Application Name"] = $ScriptName
                    }
                }
                $conn = New-Object -TypeName System.Data.SqlClient.SQLConnection

                $ConnectionString = $CSBuilder.ToString()
                $conn.ConnectionString = $ConnectionString
                Write-Debug "ConnectionString $ConnectionString"

                try {
                    $conn.Open()
                }
                catch {
                    Write-Error $_
                    continue
                }
            }

            #Following EventHandler is used for PRINT and RAISERROR T-SQL statements. Executed when -Verbose parameter specified by caller
            if ($PSBoundParameters.Verbose) {
                $conn.FireInfoMessageEventOnUserErrors = $false # Shiyang, $true will change the SQL exception to information
                $handler = [System.Data.SqlClient.SqlInfoMessageEventHandler] { Write-Verbose "$($_)" }
                $conn.add_InfoMessage($handler)
            }
            if ($ParseGO) {
                Write-Verbose "Stripping GOs from source"
                $Pieces = $GoSplitterRegex.Split($Query)
            }
            else {
                $Pieces = , $Query
            }
            # Only execute non-empty statements
            $Pieces = $Pieces | Where-Object { $_.Trim().Length -gt 0 }
            foreach ($piece in $Pieces) {
                $cmd = New-Object system.Data.SqlClient.SqlCommand($piece, $conn)
                $cmd.CommandTimeout = $QueryTimeout

                if ($null -ne $SqlParameters) {
                    $SqlParameters.GetEnumerator() |
                        ForEach-Object {
                        if ($null -ne $_.Value) {
                            $cmd.Parameters.AddWithValue($_.Key, $_.Value)
                        }
                        else {
                            $cmd.Parameters.AddWithValue($_.Key, [DBNull]::Value)
                        }
                    } > $null
                }

                $ds = New-Object system.Data.DataSet
                $da = New-Object system.Data.SqlClient.SqlDataAdapter($cmd)

                try {
                    [void]$da.fill($ds)
                }
                catch [System.Data.SqlClient.SqlException] {
                    # For SQL exception

                    $Err = $_

                    Write-Verbose "Capture SQL Error"

                    if ($PSBoundParameters.Verbose) {
                        Write-Verbose "SQL Error:  $Err"
                    } #Shiyang, add the verbose output of exception

                    switch ($ErrorActionPreference.tostring()) {
                        { 'SilentlyContinue', 'Ignore' -contains $_ } {

                        }
                        'Stop' {
                            throw $Err
                        }
                        'Continue' {
                            throw $Err
                        }
                        Default {
                            Throw $Err
                        }
                    }
                }
                catch {
                    # For other exception
                    Write-Verbose "Capture Other Error"

                    $Err = $_

                    if ($PSBoundParameters.Verbose) {
                        Write-Verbose "Other Error:  $Err"
                    }

                    switch ($ErrorActionPreference.tostring()) {
                        { 'SilentlyContinue', 'Ignore' -contains $_ } {

                        }
                        'Stop' {
                            throw $Err
                        }
                        'Continue' {
                            throw $Err
                        }
                        Default {
                            throw $Err
                        }
                    }
                }
                finally {
                    #Close the connection
                    if (-not $PSBoundParameters.ContainsKey('SQLConnection')) {
                        $conn.Close()
                    }
                }

                if ($AppendServerInstance) {
                    #Basics from Chad Miller
                    $Column = New-Object Data.DataColumn
                    $Column.ColumnName = "ServerInstance"

                    if ($ds.Tables.Count -ne 0) {
                        $ds.Tables[0].Columns.Add($Column)
                        Foreach ($row in $ds.Tables[0]) {
                            $row.ServerInstance = $SQLInstance
                        }
                    }
                }

                switch ($As) {
                    'DataSet' {
                        $ds
                    }
                    'DataTable' {
                        $ds.Tables
                    }
                    'DataRow' {
                        if ($ds.Tables.Count -ne 0) {
                            $ds.Tables[0]
                        }
                    }
                    'PSObject' {
                        if ($ds.Tables.Count -ne 0) {
                            #Scrub DBNulls - Provides convenient results you can use comparisons with
                            #Introduces overhead (e.g. ~2000 rows w/ ~80 columns went from .15 Seconds to .65 Seconds - depending on your data could be much more!)
                            foreach ($row in $ds.Tables[0].Rows) {

                                [DBNullScrubber]::DataRowToPSObject($row)
                            }
                        }
                    }
                    'SingleValue' {
                        if ($ds.Tables.Count -ne 0) {
                            $ds.Tables[0] | Select-Object -ExpandProperty $ds.Tables[0].Columns[0].ColumnName
                        }
                    }
                }
            }
        }
    }
} #Invoke-Sqlcmd2
tools\dbatools\functions\Measure-DbaBackupThroughput.ps1
function Measure-DbaBackupThroughput {
    <#
        .SYNOPSIS
            Determines how quickly SQL Server is backing up databases to media.

        .DESCRIPTION
            Returns backup history details for one or more databases on a SQL Server.

            Output looks like this:
            SqlInstance     : sql2016
            Database        : SharePoint_Config
            AvgThroughputMB : 1.07
            AvgSizeMB       : 24.17
            AvgDuration     : 00:00:01.1000000
            MinThroughputMB : 0.02
            MaxThroughputMB : 2.26
            MinBackupDate   : 8/6/2015 10:22:01 PM
            MaxBackupDate   : 6/19/2016 12:57:45 PM
            BackupCount     : 10

        .PARAMETER SqlInstance
            The SQL Server instance.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude. Options for this list are auto-populated from the server.

        .PARAMETER Type
            By default, this command measures the speed of Full backups. Valid options are "Full", "Log" and "Differential".

        .PARAMETER Since
             All backups taken on or after the point in time represented by this datetime object will be processed.

        .PARAMETER Last
            If this switch is enabled, only the last backup will be measured.

        .PARAMETER DeviceType
            Specifies one or more DeviceTypes to use in filtering backup sets. Valid values are "Disk", "Permanent Disk Device", "Tape", "Permanent Tape Device", "Pipe", "Permanent Pipe Device" and "Virtual Device", as well as custom integers for your own DeviceTypes.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Backup, Database
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Measure-DbaBackupThroughput

        .EXAMPLE
            Measure-DbaBackupThroughput -SqlInstance sql2016

            Parses every backup in msdb's backuphistory for stats on all databases.

        .EXAMPLE
            Measure-DbaBackupThroughput -SqlInstance sql2016 -Database AdventureWorks2014

            Parses every backup in msdb's backuphistory for stats on AdventureWorks2014.

        .EXAMPLE
            Measure-DbaBackupThroughput -SqlInstance sql2005 -Last

            Processes the last full, diff and log backups every backup for all databases on sql2005.

        .EXAMPLE
            Measure-DbaBackupThroughput -SqlInstance sql2005 -Last -Type Log

            Processes the last log backups every backup for all databases on sql2005.

        .EXAMPLE
            Measure-DbaBackupThroughput -SqlInstance sql2016 -Since (Get-Date).AddDays(-7)

            Gets backup calculations for the last week.

        .EXAMPLE
            Measure-DbaBackupThroughput -SqlInstance sql2016 -Since (Get-Date).AddDays(-365) -Database bigoldb

            Gets backup calculations, limited to the last year and only the bigoldb database

    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "Instance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [datetime]$Since,
        [switch]$Last,
        [ValidateSet("Full", "Log", "Differential", "File", "Differential File", "Partial Full", "Partial Differential")]
        [string]$Type = "Full",
        [string[]]$DeviceType,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($Database) {
                $DatabaseCollection = $server.Databases | Where-Object Name -in $Database
            }
            else {
                $DatabaseCollection = $server.Databases
            }

            if ($ExcludeDatabase) {
                $DatabaseCollection = $DatabaseCollection | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $DatabaseCollection) {
                Write-Message -Level VeryVerbose -Message "Retrieving history for $db."
                $allhistory = @()

                # Splatting didn't work
                if ($since) {
                    $histories = Get-DbaBackupHistory -SqlInstance $server -Database $db.name -Since $since -DeviceType $DeviceType -Type $Type
                }
                else {
                    $histories = Get-DbaBackupHistory -SqlInstance $server -Database $db.name -Last:$last -DeviceType $DeviceType -Type $Type
                }

                foreach ($history in $histories) {
                    $timetaken = New-TimeSpan -Start $history.Start -End $history.End

                    if ($timetaken.TotalMilliseconds -eq 0) {
                        $throughput = $history.TotalSize.Megabyte
                    }
                    else {
                        $throughput = $history.TotalSize.Megabyte / $timetaken.TotalSeconds
                    }

                    Add-Member -Force -InputObject $history -MemberType Noteproperty -Name MBps -value $throughput

                    $allhistory += $history | Select-Object ComputerName, InstanceName, SqlInstance, Database, MBps, TotalSize, Start, End
                }

                Write-Message -Level VeryVerbose -Message "Calculating averages for $db."
                foreach ($db in ($allhistory | Sort-Object Database | Group-Object Database)) {

                    $measuremb = $db.Group.MBps | Measure-Object -Average -Minimum -Maximum
                    $measurestart = $db.Group.Start | Measure-Object -Minimum
                    $measureend = $db.Group.End | Measure-Object -Maximum
                    $measuresize = $db.Group.TotalSize.Megabyte | Measure-Object -Average
                    $avgduration = $db.Group | ForEach-Object { New-TimeSpan -Start $_.Start -End $_.End } | Measure-Object -Average TotalSeconds

                    [pscustomobject]@{
                        ComputerName    = $db.Group.ComputerName | Select-Object -First 1
                        InstanceName    = $db.Group.InstanceName | Select-Object -First 1
                        SqlInstance     = $db.Group.SqlInstance | Select-Object -First 1
                        Database        = $db.Name
                        AvgThroughputMB = [System.Math]::Round($measuremb.Average, 2)
                        AvgSizeMB       = [System.Math]::Round($measuresize.Average, 2)
                        AvgDuration     = [dbatimespan](New-TimeSpan -Seconds $avgduration.Average)
                        MinThroughputMB = [System.Math]::Round($measuremb.Minimum, 2)
                        MaxThroughputMB = [System.Math]::Round($measuremb.Maximum, 2)
                        MinBackupDate   = [dbadatetime]$measurestart.Minimum
                        MaxBackupDate   = [dbadatetime]$measureend.Maximum
                        BackupCount     = $db.Count
                    } | Select-DefaultView -ExcludeProperty ComputerName, InstanceName
                }
            }
        }
    }
}
tools\dbatools\functions\Measure-DbaDiskSpaceRequirement.ps1
function Measure-DbaDiskSpaceRequirement {
    <#
        .SYNOPSIS
            Calculate the space needed to copy and possibly replace a database from one SQL server to another.

        .DESCRIPTION
            Returns a file list from source and destination where source file may overwrite destination. Complex scenarios where a new file may exist is taken into account.
            This command will accept a hash object in pipeline with the following keys: Source, SourceDatabase, Destination. Using this command will provide a way to prepare before a complex migration with multiple databases from different sources and destinations.

        .PARAMETER Source
            Source SQL Server.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database to copy. It MUST exist.

        .PARAMETER Destination
            Destination SQL Server instance.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER DestinationDatabase
            The database name at destination.
            May or may not be present, if unspecified it will default to the database name provided in SourceDatabase.

        .PARAMETER Credential
            The credentials to use to connect via CIM/WMI/PowerShell remoting.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database, DiskSpace, Migration
            Author: Pollus Brodeur (@pollusb)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Measure-DbaDiskSpaceRequirement

        .EXAMPLE
            Measure-DbaDiskSpaceRequirement -Source INSTANCE1 -Database DB1 -Destination INSTANCE2

            Calculate space needed for a simple migration with one database with the same name at destination.

        .EXAMPLE
            @([PSCustomObject]@{Source='SQL1';Destination='SQL2';Database='DB1'},
              [PSCustomObject]@{Source='SQL1';Destination='SQL2';Database='DB2'}
            ) | Measure-DbaDiskSpaceRequirement

            Using a PSCustomObject with 2 databases to migrate on SQL2.

        .EXAMPLE
            Import-Csv -Path .\migration.csv -Delimiter "`t" | Measure-DbaDiskSpaceRequirement | Format-Table -AutoSize

            Using a CSV file. You will need to use this header line "Source<tab>Destination<tab>Database<tab>DestinationDatabase".

        .EXAMPLE
            Invoke-DbaSqlCmd -SqlInstance DBA -Database Migrations -Query 'select Source, Destination, Database from dbo.Migrations' `
                | Measure-DbaDiskSpaceRequirement

            Using a SQL table. We are DBA after all!
    #>
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [DbaInstanceParameter]$Source,
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [string]$Database,
        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)]
        [PSCredential]$SourceSqlCredential,
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [DbaInstanceParameter]$Destination,
        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)]
        [string]$DestinationDatabase,
        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)]
        [PSCredential]$DestinationSqlCredential,
        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)]
        [PSCredential]$Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        $local:cacheMP = @{}
        $local:cacheDP = @{}
        function Get-MountPoint {
            [CmdletBinding()]
            param(
                [Parameter(Mandatory = $true)]
                $computerName,
                [PSCredential]$credential
            )
            Get-DbaCmObject -Class Win32_MountPoint -ComputerName $computerName -Credential $credential |
                Select-Object @{n='Mountpoint';e={$_.Directory.split('=')[1].Replace('"','').Replace('\\','\')}}
        }
        function Get-MountPointFromPath {
            [CmdletBinding()]
            param(
                [Parameter(Mandatory = $true)]
                $path,
                [Parameter(Mandatory = $true)]
                $computerName,
                [PSCredential]$credential
            )
            if (!$cacheMP[$computerName]) {
                try {
                    $cacheMP.Add($computerName, (Get-MountPoint -computerName $computerName -credential $credential))
                    Write-Message -Level Verbose -Message "cacheMP[$computerName] is now cached"
                }
                catch {
                    # This way, I won't be asking again for this computer.
                    $cacheMP.Add($computerName, '?')
                    Stop-Function -Message "Can't connect to $computerName. cacheMP[$computerName] = ?" -ErrorRecord $_ -Target $computerName -Continue
                }
            }
            if ($cacheMP[$computerName] -eq '?') {
                return '?'
            }
            foreach ($m in ($cacheMP[$computerName] | Sort-Object -Property Mountpoint -Descending)) {
                if ($path -like "$($m.Mountpoint)*") {
                    return $m.Mountpoint
                }
            }
            Write-Message -Level Warning -Message "Path $path can't be found in any MountPoints of $computerName"
        }
        function Get-MountPointFromDefaultPath {
            [CmdletBinding()]
            param(
                [Parameter(Mandatory = $true)]
                [ValidateSet('Log', 'Data')]
                $DefaultPathType,
                [Parameter(Mandatory = $true)]
                $SqlInstance,
                [PSCredential]$SqlCredential,
                # Could probably use the computer defined in SqlInstance but info was already available from the caller
                $computerName,
                [PSCredential]$Credential
            )
            if (!$cacheDP[$SqlInstance]) {
                try {
                    $cacheDP.Add($SqlInstance, (Get-DbaDefaultPath -SqlInstance $SqlInstance -SqlCredential $SqlCredential -EnableException))
                    Write-Message -Level Verbose -Message "cacheDP[$SqlInstance] is now cached"
                }
                catch {
                    Stop-Function -Message "Can't connect to $SqlInstance" -Continue
                    $cacheDP.Add($SqlInstance, '?')
                    return '?'
                }
            }
            if ($cacheDP[$SqlInstance] -eq '?') {
                return '?'
            }
            if (!$computerName) {
                $computerName = $cacheDP[$SqlInstance].ComputerName
            }
            if (!$cacheMP[$computerName]) {
                try {
                    $cacheMP.Add($computerName, (Get-MountPoint -computerName $computerName -Credential $Credential))
                }
                catch {
                    Stop-Function -Message "Can't connect to $computerName." -Continue
                    $cacheMP.Add($computerName, '?')
                    return '?'
                }
            }
            if ($DefaultPathType -eq 'Log') {
                $path = $cacheDP[$SqlInstance].Log
            }
            else {
                $path = $cacheDP[$SqlInstance].Data
            }
            foreach ($m in ($cacheMP[$computerName] | Sort-Object -Property Mountpoint -Descending)) {
                if ($path -like "$($m.Mountpoint)*") {
                    return $m.Mountpoint
                }
            }
        }
    }
    process {
        Write-Message -Level Verbose -Message "Connecting to SQL Servers."
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source."
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
        }

        try {
            Write-Message -Level Verbose -Message "Connecting to $Destination."
            $destServer = Connect-SqlInstance -SqlInstance $Destination -SqlCredential $DestinationSqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Destination
        }

        if (Test-Bound 'DestinationDatabase' -not) {
            $DestinationDatabase = $Database
        }
        Write-Message -Level Verbose -Message "$Source.[$Database] -> $Destination.[$DestinationDatabase]"

        $sourceDb = Get-DbaDatabase -SqlInstance $sourceServer -Database $Database -SqlCredential $SourceSqlCredential
        if (Test-Bound 'Database' -not) {
            Stop-Function -Message "Database [$Database] MUST exist on Source Instance $Source." -ErrorRecord $_
        }
        $sourceFiles = @($sourceDb.FileGroups.Files | Select-Object Name, FileName, Size, @{n='Type'; e= {'Data'}})
        $sourceFiles += @($sourceDb.LogFiles        | Select-Object Name, FileName, Size, @{n='Type'; e= {'Log'}})

        if ($destDb = Get-DbaDatabase -SqlInstance $destServer -Database $DestinationDatabase -SqlCredential $DestinationSqlCredential) {
            $destFiles = @($destDb.FileGroups.Files | Select-Object Name, FileName, Size, @{n='Type'; e= {'Data'}})
            $destFiles += @($destDb.LogFiles        | Select-Object Name, FileName, Size, @{n='Type'; e= {'Log'}})
            $computerName = $destDb.ComputerName
        }
        else {
            Write-Message -Level Verbose -Message "Database [$DestinationDatabase] does not exist on Destination Instance $Destination."
            $computerName = $destServer.ComputerName
        }

        foreach ($sourceFile in $sourceFiles) {
            foreach ($destFile in $destFiles) {
                if ($found = ($sourceFile.Name -eq $destFile.Name)) {
                    # Files found on both sides
                    [PSCustomObject]@{
                            SourceComputerName      = $sourceServer.ComputerName
                            SourceInstance          = $sourceServer.ServiceName
                            SourceSqlInstance       = $sourceServer.DomainInstanceName
                            DestinationComputerName = $destServer.ComputerName
                            DestinationInstance     = $destServer.ServiceName
                            DestinationSqlInstance  = $destServer.DomainInstanceName
                            SourceDatabase          = $sourceDb.Name
                            SourceLogicalName       = $sourceFile.Name
                            SourceFileName          = $sourceFile.FileName
                            SourceFileSize          = [DbaSize]($sourceFile.Size * 1000)
                            DestinationDatabase     = $destDb.Name
                            DestinationLogicalName  = $destFile.Name
                            DestinationFileName     = $destFile.FileName
                            DestinationFileSize     = [DbaSize]($destFile.Size * 1000) * -1
                            DifferenceSize          = [DbaSize]( ($sourceFile.Size * 1000) - ($destFile.Size * 1000) )
                            MountPoint              = Get-MountPointFromPath -Path $destFile.Filename -ComputerName $computerName -Credential $Credential
                            FileLocation            = 'Source and Destination'
                        } | Select-DefaultView -ExcludeProperty SourceComputerName, SourceInstance, DestinationInstance, DestinationLogicalName
                    break
                }
            }
            if (!$found) {
                # Files on source but not on destination
                [PSCustomObject]@{
                        SourceComputerName      = $sourceServer.ComputerName
                        SourceInstance          = $sourceServer.ServiceName
                        SourceSqlInstance       = $sourceServer.DomainInstanceName
                        DestinationComputerName = $destServer.ComputerName
                        DestinationInstance     = $destServer.ServiceName
                        DestinationSqlInstance  = $destServer.DomainInstanceName
                        SourceDatabase          = $sourceDb.Name
                        SourceLogicalName       = $sourceFile.Name
                        SourceFileName          = $sourceFile.FileName
                        SourceFileSize          = [DbaSize]($sourceFile.Size * 1000)
                        DestinationDatabase     = $DestinationDatabase
                        DestinationLogicalName  = $null
                        DestinationFileName     = $null
                        DestinationFileSize     = [DbaSize]0
                        DifferenceSize          = [DbaSize]($sourceFile.Size * 1000)
                        MountPoint              = Get-MountPointFromDefaultPath -DefaultPathType $sourceFile.Type -SqlInstance $Destination `
                                                  -SqlCredential $DestinationSqlCredential -computerName $computerName -credential $Credential
                        FileLocation            = 'Only on Source'
                    } | Select-DefaultView -ExcludeProperty SourceComputerName, SourceInstance, DestinationInstance, DestinationLogicalName
            }
        }
        if ($destDb) {
            # Files on destination but not on source (strange scenario but possible)
            $destFilesNotSource = Compare-Object -ReferenceObject $destFiles -DifferenceObject $sourceFiles -Property Name -PassThru
            foreach ($destFileNotSource in $destFilesNotSource) {
                [PSCustomObject]@{
                        SourceComputerName      = $sourceServer.ComputerName
                        SourceInstance          = $sourceServer.ServiceName
                        SourceSqlInstance       = $sourceServer.DomainInstanceName
                        DestinationComputerName = $destServer.ComputerName
                        DestinationInstance     = $destServer.ServiceName
                        DestinationSqlInstance  = $destServer.DomainInstanceName
                        SourceDatabaseName      = $Database
                        SourceLogicalName       = $null
                        SourceFileName          = $null
                        SourceFileSize          = [DbaSize]0
                        DestinationDatabaseName = $destDb.Name
                        DestinationLogicalName  = $destFileNotSource.Name
                        DestinationFileName     = $destFile.FileName
                        DestinationFileSize     = [DbaSize]($destFileNotSource.Size * 1000) * -1
                        DifferenceSize          = [DbaSize]($destFileNotSource.Size * 1000) * -1
                        MountPoint              = Get-MountPointFromPath -Path $destFileNotSource.Filename -ComputerName $computerName -Credential $Credential
                        FileLocation            = 'Only on Destination'
                    } | Select-DefaultView -ExcludeProperty SourceComputerName, SourceInstance, DestinationInstance, DestinationLogicalName
            }
        }
        $DestinationDatabase = $null
    }
}
tools\dbatools\functions\Mount-DbaDatabase.ps1
function Mount-DbaDatabase {
    <#
        .SYNOPSIS
            Attach a SQL Server Database - aliased to Attach-DbaDatabase

        .DESCRIPTION
            This command will attach a SQL Server database.

        .PARAMETER SqlInstance
            The SQL Server instance.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to attach.

        .PARAMETER FileStructure
            A StringCollection object value that contains a list database files. If FileStructure is not specified, BackupHistory will be used to guess the structure.

        .PARAMETER DatabaseOwner
            Sets the database owner for the database. The sa account (or equivalent) will be used if DatabaseOwner is not specified.

        .PARAMETER AttachOption
            An AttachOptions object value that contains the attachment options. Valid options are "None", "RebuildLog", "EnableBroker", "NewBroker" and "ErrorBrokerConversations".

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Mount-DbaDatabase

        .EXAMPLE
            $fileStructure = New-Object System.Collections.Specialized.StringCollection
            $fileStructure.Add("E:\archive\example.mdf")
            $filestructure.Add("E:\archive\example.ldf")
            $filestructure.Add("E:\archive\example.ndf")
            Mount-DbaDatabase -SqlInstance sql2016 -Database example -FileStructure $fileStructure

            Attaches a database named "example" to sql2016 with the files "E:\archive\example.mdf", "E:\archive\example.ldf" and "E:\archive\example.ndf". The database owner will be set to sa and the attach option is None.

        .EXAMPLE
            Mount-DbaDatabase -SqlInstance sql2016 -Database example

            Since the FileStructure was not provided, this command will attempt to determine it based on backup history. If found, a database named example will be attached to sql2016.

        .EXAMPLE
            Mount-DbaDatabase -SqlInstance sql2016 -Database example -WhatIf

            Shows what would happen if the command were executed (without actually performing the command)
    #>
    [CmdletBinding(SupportsShouldProcess)]
    Param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]
        $SqlCredential,
        [parameter(Mandatory)]
        [string[]]$Database,
        [System.Collections.Specialized.StringCollection]$FileStructure,
        [string]$DatabaseOwner,
        [ValidateSet('None', 'RebuildLog', 'EnableBroker', 'NewBroker', 'ErrorBrokerConversations')]
        [string]$AttachOption = "None",
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if (-not $server.Logins.Item($DatabaseOwner)) {
                try {
                    $DatabaseOwner = ($server.Logins | Where-Object { $_.id -eq 1 }).Name
                }
                catch {
                    $DatabaseOwner = "sa"
                }
            }

            foreach ($db in $database) {

                if ($server.Databases[$db]) {
                    Stop-Function -Message "$db is already attached to $server." -Target $db -Continue
                }

                if ($server.Databases[$db].IsSystemObject) {
                    Stop-Function -Message "$db is a system database and cannot be attached using this method." -Target $db -Continue
                }

                if (-Not (Test-Bound -Parameter FileStructure)) {
                    $backuphistory = Get-DbaBackupHistory -SqlInstance $server -Database $db -Type Full | Sort-Object End -Descending | Select-Object -First 1

                    if (-not $backuphistory) {
                        $message = "Could not enumerate backup history to automatically build FileStructure. Rerun the command and provide the filestructure parameter."
                        Stop-Function -Message $message -Target $db -Continue
                    }

                    $backupfile = $backuphistory.Path[0]
                    $filepaths = (Read-DbaBackupHeader -SqlInstance $server -FileList -Path $backupfile).PhysicalName

                    $FileStructure = New-Object System.Collections.Specialized.StringCollection
                    foreach ($file in $filepaths) {
                        $exists = Test-DbaSqlpath -SqlInstance $server -Path $file
                        if (-not $exists) {
                            $message = "Could not find the files to build the FileStructure. Rerun the command and provide the FileStructure parameter."
                            Stop-Function -Message $message -Target $file -Continue
                        }

                        $null = $FileStructure.Add($file)
                    }
                }

                If ($Pscmdlet.ShouldProcess($server, "Attaching $Database with $DatabaseOwner as database owner and $AttachOption as attachoption")) {
                    try {
                        $server.AttachDatabase($db, $FileStructure, $DatabaseOwner, [Microsoft.SqlServer.Management.Smo.AttachOptions]::$AttachOption)

                        [pscustomobject]@{
                            ComputerName  = $server.ComputerName
                            InstanceName  = $server.ServiceName
                            SqlInstance   = $server.DomainInstanceName
                            Database      = $db
                            AttachResult  = "Success"
                            AttachOption  = $AttachOption
                            FileStructure = $FileStructure
                        }
                    }
                    catch {
                        Stop-Function -Message "Failure" -ErrorRecord $_ -Target $server
                    }
                }
            }
        }
    }
}
tools\dbatools\functions\Move-DbaRegisteredServer.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Move-DbaRegisteredServer {
    <#
        .SYNOPSIS
            Moves registered servers around SQL Server Central Management Server (CMS)

        .DESCRIPTION
            Moves registered servers around SQL Server Central Management Server (CMS)

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Name
            Specifies one or more reg servers to move. Name is the visible name in SSMS CMS interface (labeled Registered Server Name)

        .PARAMETER ServerName
            Specifies one or more reg servers to move. Server Name is the actual instance name (labeled Server Name)

        .PARAMETER NewGroup
            The new group. If no new group is specified, the default root will used

        .PARAMETER InputObject
            Allows results from Get-DbaRegisteredServer to be piped in

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.

            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.

            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Chrissy LeMaire (@cl)
            Tags: RegisteredServer, CMS

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Move-DbaRegisteredServer

        .EXAMPLE
            Move-DbaRegisteredServer -SqlInstance sql2012 -Name 'Web SQL Cluster' -NewGroup HR\Prod

            Moves the registered server on sql2012 titled 'Web SQL Cluster' to the Prod group within the HR group

        .EXAMPLE
            Move-DbaRegisteredServer -SqlInstance sql2012 -Group HR\Development -NewGroup HR\Prod

            Moves all servers from the HR and sub-group Development to HR Prod

        .EXAMPLE
            Get-DbaRegisteredServer -SqlInstance sql2017 -Name 'Web SQL Cluster' | Move-DbaRegisteredServer -NewGroup Web

            Moves the registered server 'Web SQL Cluster' on sql2017 to the Web group, also on sql2017
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$Name,
        [string[]]$ServerName,
        [string]$NewGroup,
        [parameter(ValueFromPipeline)]
        [Microsoft.SqlServer.Management.RegisteredServers.RegisteredServer[]]$InputObject,
        [switch]$EnableException
    )

    begin {
        if ((Test-Bound -ParameterName SqlInstance) -and (Test-Bound -Not -ParameterName Name) -and (Test-Bound -Not -ParameterName ServerName)) {
            Stop-Function -Message "Name or ServerName must be specified when using -SqlInstance"
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $SqlInstance) {
            $InputObject += Get-DbaRegisteredServer -SqlInstance $instance -SqlCredential $SqlCredential -Name $Name -ServerName $ServerName

        }

        foreach ($regserver in $InputObject) {
            $parentserver = Get-RegServerParent -InputObject $regserver

            if ($null -eq $parentserver) {
                Stop-Function -Message "Something went wrong and it's hard to explain, sorry. This basically shouldn't happen." -Continue
            }

            $server = $parentserver.ServerConnection.SqlConnectionObject

            if ((Test-Bound -ParameterName NewGroup)) {
                $group = Get-DbaRegisteredServerGroup -SqlInstance $server -Group $NewGroup

                if (-not $group) {
                    Stop-Function -Message "$NewGroup not found on $server" -Continue
                }
            }
            else {
                $group = Get-DbaRegisteredServerGroup -SqlInstance $server -Id 1
            }

            if ($Pscmdlet.ShouldProcess($regserver.SqlInstance, "Moving $($regserver.Name) to $group")) {
                try {
                    $null = $parentserver.ServerConnection.ExecuteNonQuery($regserver.ScriptMove($group).GetScript())
                    Get-DbaRegisteredServer -SqlInstance $server -Name $regserver.Name -ServerName $regserver.ServerName
                    $parentserver.ServerConnection.Disconnect()
                }
                catch {
                    Stop-Function -Message "Failed to move $($regserver.Name) to $NewGroup on $($regserver.SqlInstance)" -ErrorRecord $_ -Continue
                }
            }
        }
    }
}
tools\dbatools\functions\Move-DbaRegisteredServerGroup.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Move-DbaRegisteredServerGroup {
    <#
        .SYNOPSIS
             Moves registered server groups around SQL Server Central Management Server (CMS).

        .DESCRIPTION
            Moves registered server groups around SQL Server Central Management Server (CMS).

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Group
            Specifies one or more groups to include from SQL Server Central Management Server.

        .PARAMETER InputObject
            Allows results from Get-DbaRegisteredServerGroup to be piped in

        .PARAMETER Id
            Get group by Id(s)

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER NewGroup
            The new location.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.

            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.

            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Chrissy LeMaire (@cl)
            Tags: RegisteredServer, CMS

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Move-DbaRegisteredServerGroup

        .EXAMPLE
            Move-DbaRegisteredServerGroup -SqlInstance sql2012 -Group HR\Development -NewGroup AD\Prod

            Moves the Development group within HR to the Prod group within AD

        .EXAMPLE
            Get-DbaRegisteredServerGroup -SqlInstance sql2017 -Group HR\Development| Move-DbaRegisteredServer -NewGroup Web

            Moves the Development group within HR to the Web group
    #>
    [CmdletBinding(SupportsShouldProcess)]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$Group,
        [parameter(Mandatory)]
        [string]$NewGroup,
        [parameter(ValueFromPipeline)]
        [Microsoft.SqlServer.Management.RegisteredServers.ServerGroup[]]$InputObject,
        [switch]$EnableException
    )
    begin {
        if ((Test-Bound -ParameterName SqlInstance) -and (Test-Bound -Not -ParameterName Group)) {
            Stop-Function -Message "Group must be specified when using -SqlInstance"
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance to search for $group"
            $InputObject += Get-DbaRegisteredServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Group $Group
        }

        foreach ($regservergroup in $InputObject) {
            $parentserver = Get-RegServerParent -InputObject $regservergroup

            if ($null -eq $parentserver) {
                Stop-Function -Message "Something went wrong and it's hard to explain, sorry. This basically shouldn't happen." -Continue
            }

            $server = $parentserver.ServerConnection.SqlConnectionObject

            if ($NewGroup -eq 'Default') {
                $groupobject = Get-DbaRegisteredServerGroup -SqlInstance $server -Id 1
            }
            else {
                $groupobject = Get-DbaRegisteredServerGroup -SqlInstance $server -Group $NewGroup
            }

            Write-Message -Level Verbose -Message "Found $($groupobject.Name) on $($parentserver.ServerConnection.ServerName)"

            if (-not $groupobject) {
                Stop-Function -Message "Group '$NewGroup' not found on $server" -Continue
            }

            if ($Pscmdlet.ShouldProcess($regservergroup.SqlInstance, "Moving $($regservergroup.Name) to $($groupobject.Name)")) {
                try {
                    Write-Message -Level Verbose -Message "Parsing $groupobject"
                    $newname = Get-RegServerGroupReverseParse $groupobject
                    $newname = "$newname\$($regservergroup.Name)"
                    Write-Message -Level Verbose -Message "Executing $($regservergroup.ScriptMove($groupobject).GetScript())"
                    $null = $parentserver.ServerConnection.ExecuteNonQuery($regservergroup.ScriptMove($groupobject).GetScript())
                    Write-Message -Level Verbose -Message "Connecting to $instance to search for $newname"
                    Get-DbaRegisteredServerGroup -SqlInstance $server -Group $newname
                    $parentserver.ServerConnection.Disconnect()
                }
                catch {
                    Stop-Function -Message "Failed to move $($regserver.Name) to $NewGroup on $($regserver.SqlInstance)" -ErrorRecord $_ -Continue
                }
            }
        }
    }
}
tools\dbatools\functions\New-DbaAgentJob.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function New-DbaAgentJob {
    <#
.SYNOPSIS
New-DbaAgentJob creates a new job

.DESCRIPTION
New-DbaAgentJob makes is possible to create a job in the SQL Server Agent.
It returns an array of the job(s) created

.PARAMETER SqlInstance
SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Job
The name of the job. The name must be unique and cannot contain the percent (%) character.

.PARAMETER Schedule
Schedule to attach to job. This can be more than one schedule.

.PARAMETER ScheduleId
Schedule ID to attach to job. This can be more than one schedule ID.

.PARAMETER Disabled
Sets the status of the job to disabled. By default a job is enabled.

.PARAMETER Description
The description of the job.

.PARAMETER StartStepId
The identification number of the first step to execute for the job.

.PARAMETER Category
The category of the job.

.PARAMETER OwnerLogin
The name of the login that owns the job.

.PARAMETER EventLogLevel
Specifies when to place an entry in the Microsoft Windows application log for this job.
Allowed values 0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always"
The text value van either be lowercase, uppercase or something in between as long as the text is correct.

.PARAMETER EmailLevel
Specifies when to send an e-mail upon the completion of this job.
Allowed values 0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always"
The text value van either be lowercase, uppercase or something in between as long as the text is correct.

.PARAMETER NetsendLevel
Specifies when to send a network message upon the completion of this job.
Allowed values 0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always"
The text value van either be lowercase, uppercase or something in between as long as the text is correct.

.PARAMETER PageLevel
Specifies when to send a page upon the completion of this job.
Allowed values 0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always"
The text value van either be lowercase, uppercase or something in between as long as the text is correct.

.PARAMETER EmailOperator
The e-mail name of the operator to whom the e-mail is sent when EmailLevel is reached.

.PARAMETER NetsendOperator
The name of the operator to whom the network message is sent.

.PARAMETER PageOperator
The name of the operator to whom a page is sent.

.PARAMETER DeleteLevel
Specifies when to delete the job.
Allowed values 0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always"
The text value van either be lowercase, uppercase or something in between as long as the text is correct.

.PARAMETER Force
The force parameter will ignore some errors in the parameters and assume defaults.

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Author: Sander Stad (@sqlstad, sqlstad.nl)
Tags: Agent, Job, JobStep

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/New-DbaAgentJob

.EXAMPLE
New-DbaAgentJob -SqlInstance sql1 -Job 'Job One' -Description 'Just another job'
Creates a job with the name "Job1" and a small description

.EXAMPLE
New-DbaAgentJob -SqlInstance sql1 -Job 'Job One' -Disabled
Creates the job but sets it to disabled

.EXAMPLE
New-DbaAgentJob -SqlInstance sql1 -Job 'Job One' -EventLogLevel OnSuccess
Creates the job and sets the notification to write to the Windows Application event log on success

.EXAMPLE
New-DbaAgentJob -SqlInstance SSTAD-PC -Job 'Job One' -EmailLevel OnFailure -EmailOperator dba
Creates the job and sets the notification to send an e-mail to the e-mail operator

.EXAMPLE
New-DbaAgentJob -SqlInstance sql1 -Job 'Job One' -Description 'Just another job' -Whatif
Doesn't create the job but shows what would happen.

.EXAMPLE
New-DbaAgentJob -SqlInstance sql1, sql2, sql3 -Job 'Job One'
Creates a job with the name "Job One" on multiple servers

.EXAMPLE
"sql1", "sql2", "sql3" | New-DbaAgentJob -Job 'Job One'
Creates a job with the name "Job One" on multiple servers using the pipe line
#>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$Job,
        [object[]]$Schedule,
        [int[]]$ScheduleId,
        [switch]$Disabled,
        [string]$Description,
        [int]$StartStepId,
        [string]$Category,
        [string]$OwnerLogin,
        [ValidateSet(0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always")]
        [object]$EventLogLevel,
        [ValidateSet(0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always")]
        [object]$EmailLevel,
        [ValidateSet(0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always")]
        [Parameter()]
        [ValidateSet(0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always")]
        [object]$PageLevel,
        [string]$EmailOperator,
        [string]$NetsendOperator,
        [string]$PageOperator,
        [ValidateSet(0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always")]
        [object]$DeleteLevel,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {

        # Check of the event log level is of type string and set the integer value
        if ($EventLogLevel -notin 1, 2, 3) {
            $EventLogLevel = switch ($EventLogLevel) {
                "Never" { 0 } "OnSuccess" { 1 } "OnFailure" { 2 } "Always" { 3 }
                default { 0 }
            }
        }

        # Check of the email level is of type string and set the integer value
        if ($EmailLevel -notin 1, 2, 3) {
            $EmailLevel = switch ($EmailLevel) {
                "Never" { 0 } "OnSuccess" { 1 } "OnFailure" { 2 } "Always" { 3 }
                default { 0 }
            }
        }

        # Check of the net send level is of type string and set the integer value
        if ($NetsendLevel -notin 1, 2, 3) {
            $NetsendLevel = switch ($NetsendLevel) {
                "Never" { 0 } "OnSuccess" { 1 } "OnFailure" { 2 } "Always" { 3 }
                default { 0 }
            }
        }

        # Check of the page level is of type string and set the integer value
        if ($PageLevel -notin 1, 2, 3) {
            $PageLevel = switch ($PageLevel) {
                "Never" { 0 } "OnSuccess" { 1 } "OnFailure" { 2 } "Always" { 3 }
                default { 0 }
            }
        }

        # Check of the delete level is of type string and set the integer value
        if ($DeleteLevel -notin 1, 2, 3) {
            $DeleteLevel = switch ($DeleteLevel) {
                "Never" { 0 } "OnSuccess" { 1 } "OnFailure" { 2 } "Always" { 3 }
                default { 0 }
            }
        }

        # Check the e-mail operator name
        if (($EmailLevel -ge 1) -and (-not $EmailOperator)) {
            Stop-Function -Message "Please set the e-mail operator when the e-mail level parameter is set." -Target $sqlinstance
            return
        }

        # Check the e-mail operator name
        if (($NetsendLevel -ge 1) -and (-not $NetsendOperator)) {
            Stop-Function -Message "Please set the netsend operator when the netsend level parameter is set." -Target $sqlinstance
            return
        }

        # Check the e-mail operator name
        if (($PageLevel -ge 1) -and (-not $PageOperator)) {
            Stop-Function -Message "Please set the page operator when the page level parameter is set." -Target $sqlinstance
            return
        }
    }

    process {

        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $sqlinstance) {
            # Try connecting to the instance
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            # Check if the job already exists
            if (-not $Force -and ($server.JobServer.Jobs.Name -contains $Job)) {
                Stop-Function -Message "Job $Job already exists on $instance" -Target $instance -Continue
            }
            elseif ($Force -and ($server.JobServer.Jobs.Name -contains $Job)) {
                Write-Message -Message "Job $Job already exists on $instance. Removing.." -Level Verbose

                if ($PSCmdlet.ShouldProcess($instance, "Removing the job $Job on $instance")) {
                    try {
                        Remove-DbaAgentJob -SqlInstance $instance -Job $Job -EnableException
                    }
                    catch {
                        Stop-Function -Message "Couldn't remove job $Job from $instance" -Target $instance -Continue -ErrorRecord $_
                    }
                }

            }

            if ($PSCmdlet.ShouldProcess($instance, "Creating the job on $instance")) {
                # Create the job object
                try {
                    $currentjob = New-Object Microsoft.SqlServer.Management.Smo.Agent.Job($server.JobServer, $Job)
                }
                catch {
                    Stop-Function -Message "Something went wrong creating the job. `n" -Target $Job -Continue -ErrorRecord $_
                }

                #region job options
                # Settings the options for the job
                if ($Disabled) {
                    Write-Message -Message "Setting job to disabled" -Level Verbose
                    $currentjob.IsEnabled = $false
                }
                else {
                    Write-Message -Message "Setting job to enabled" -Level Verbose
                    $currentjob.IsEnabled = $true
                }

                if ($Description.Length -ge 1) {
                    Write-Message -Message "Setting job description" -Level Verbose
                    $currentjob.Description = $Description
                }

                if ($StartStepId -ge 1) {
                    Write-Message -Message "Setting job start step id" -Level Verbose
                    $currentjob.StartStepID = $StartStepId
                }

                if ($Category.Length -ge 1) {
                    # Check if the job category exists
                    if ($Category -notin $server.JobServer.JobCategories.Name) {
                        if ($Force) {
                            if ($PSCmdlet.ShouldProcess($instance, "Creating job category on $instance")) {
                                try {
                                    # Create the category
                                    New-DbaAgentJobCategory -SqlInstance $instance -Category $Category
                                }
                                catch {
                                    Stop-Function -Message "Couldn't create job category $Category from $instance" -Target $instance -Continue -ErrorRecord $_
                                }
                            }
                        }
                        else {
                            Stop-Function -Message "Job category $Category doesn't exist on $instance. Use -Force to create it." -Target $instance
                            return
                        }
                    }
                    else {
                        Write-Message -Message "Setting job category" -Level Verbose
                        $currentjob.Category = $Category
                    }
                }

                if ($OwnerLogin.Length -ge 1) {
                    # Check if the login name is present on the instance
                    if ($server.Logins.Name -contains $OwnerLogin) {
                        Write-Message -Message "Setting job owner login name to $OwnerLogin" -Level Verbose
                        $currentjob.OwnerLoginName = $OwnerLogin
                    }
                    else {
                        Stop-Function -Message "The owner $OwnerLogin does not exist on instance $instance" -Target $Job -Continue
                    }
                }

                if ($EventLogLevel -ge 0) {
                    Write-Message -Message "Setting job event log level" -Level Verbose
                    $currentjob.EventLogLevel = $EventLogLevel
                }

                if ($EmailOperator) {
                    if ($EmailLevel -ge 1) {
                        # Check if the operator name is present
                        if ($server.JobServer.Operators.Name -contains $EmailOperator) {
                            Write-Message -Message "Setting job e-mail level" -Level Verbose
                            $currentjob.EmailLevel = $EmailLevel

                            Write-Message -Message "Setting job e-mail operator" -Level Verbose
                            $currentjob.OperatorToEmail = $EmailOperator
                        }
                        else {
                            Stop-Function -Message "The e-mail operator name $EmailOperator does not exist on instance $instance. Exiting.." -Target $Job -Continue
                        }
                    }
                    else {
                        Stop-Function -Message "Invalid combination of e-mail operator name $EmailOperator and email level $EmailLevel. Not setting the notification." -Target $Job -Continue
                    }
                }

                if ($NetsendOperator) {
                    if ($NetsendLevel -ge 1) {
                        # Check if the operator name is present
                        if ($server.JobServer.Operators.Name -contains $NetsendOperator) {
                            Write-Message -Message "Setting job netsend level" -Level Verbose
                            $currentjob.NetSendLevel = $NetsendLevel

                            Write-Message -Message "Setting job netsend operator" -Level Verbose
                            $currentjob.OperatorToNetSend = $NetsendOperator
                        }
                        else {
                            Stop-Function -Message "The netsend operator name $NetsendOperator does not exist on instance $instance. Exiting.." -Target $Job -Continue
                        }
                    }
                    else {
                        Write-Message -Message "Invalid combination of netsend operator name $NetsendOperator and netsend level $NetsendLevel. Not setting the notification."
                    }
                }

                if ($PageOperator) {
                    if ($PageLevel -ge 1) {
                        # Check if the operator name is present
                        if ($server.JobServer.Operators.Name -contains $PageOperator) {
                            Write-Message -Message "Setting job pager level" -Level Verbose
                            $currentjob.PageLevel = $PageLevel

                            Write-Message -Message "Setting job pager operator" -Level Verbose
                            $currentjob.OperatorToPage = $PageOperator
                        }
                        else {
                            Stop-Function -Message "The page operator name $PageOperator does not exist on instance $instance. Exiting.." -Target $Job -Continue
                        }
                    }
                    else {
                        Write-Message -Message "Invalid combination of page operator name $PageOperator and page level $PageLevel. Not setting the notification." -Level Warning
                    }
                }

                if ($DeleteLevel -ge 0) {
                    Write-Message -Message "Setting job delete level" -Level Verbose
                    $currentjob.DeleteLevel = $DeleteLevel
                }
                #endregion job options

                try {
                    Write-Message -Message "Creating the job" -Level Verbose

                    # Create the job
                    $currentjob.Create()

                    Write-Message -Message "Job created with UID $($currentjob.JobID)" -Level Verbose

                    # Make sure the target is set for the job
                    Write-Message -Message "Applying the target (local) to job $Job" -Level Verbose
                    $currentjob.ApplyToTargetServer("(local)")

                    # If a schedule needs to be attached
                    if ($Schedule) {
                        Set-DbaAgentJob -SqlInstance $instance -Job $currentjob -Schedule $Schedule -SqlCredential $SqlCredential
                    }

                    if ($ScheduleId) {
                        Set-DbaAgentJob -SqlInstance $instance -Job $currentjob -ScheduleId $ScheduleId -SqlCredential $SqlCredential
                    }
                }
                catch {
                    Stop-Function -Message "Something went wrong creating the job" -Target $currentjob -ErrorRecord $_ -Continue
                }
            }

            # Return the job
            return $currentjob
        }
    }

    end {
        if (Test-FunctionInterrupt) { return }
        Write-Message -Message "Finished creating job(s)." -Level Verbose
    }

}
tools\dbatools\functions\New-DbaAgentJobCategory.ps1
function New-DbaAgentJobCategory {
    <#
.SYNOPSIS
New-DbaAgentJobCategory creates a new job category.

.DESCRIPTION
New-DbaAgentJobCategory makes it possible to create a job category that can be used with jobs.
It returns an array of the job(s) created .

.PARAMETER SqlInstance
SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Category
The name of the category

.PARAMETER CategoryType
The type of category. This can be "LocalJob", "MultiServerJob" or "None".
The default is "LocalJob" and will automatically be set when no option is chosen.

.PARAMETER Force
The force parameter will ignore some errors in the parameters and assume defaults.

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Author: Sander Stad (@sqlstad, sqlstad.nl)
Tags: Agent, Job, JobCategory

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/New-DbaAgentJobCategory

.EXAMPLE
New-DbaAgentJobCategory -SqlInstance sql1 -Category 'Category 1'

Creates a new job category with the name 'Category 1'.

.EXAMPLE
New-DbaAgentJobCategory -SqlInstance sql1 -Category 'Category 2' -CategoryType MultiServerJob

Creates a new job category with the name 'Category 2' and assign the category type for a multi server job.

#>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string[]]$Category,
        [ValidateSet("LocalJob", "MultiServerJob", "None")]
        [string]$CategoryType,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        # Check the category type
        if (-not $CategoryType) {
            # Setting category type to default
            Write-Message -Message "Setting the category type to 'LocalJob'" -Level Verbose
            $CategoryType = "LocalJob"
        }
    }

    process {

        foreach ($instance in $sqlinstance) {
            # Try connecting to the instance
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            foreach ($cat in $Category) {
                # Check if the category already exists
                if ($cat -in $server.JobServer.JobCategories.Name) {
                    Stop-Function -Message "Job category $cat already exists on $instance" -Target $instance -Continue
                }
                else {
                    if ($PSCmdlet.ShouldProcess($instance, "Adding the job category $cat")) {
                        try {
                            $jobcategory = New-Object Microsoft.SqlServer.Management.Smo.Agent.JobCategory($server.JobServer, $cat)
                            $jobcategory.CategoryType = $CategoryType

                            $jobcategory.Create()

                            $server.JobServer.Refresh()
                        }
                        catch {
                            Stop-Function -Message "Something went wrong creating the job category $cat on $instance" -Target $cat -Continue -ErrorRecord $_
                        }

                    } # if should process

                } # end else category exists

                # Return the job category
                Get-DbaAgentJobCategory -SqlInstance $instance -Category $cat

            } # for each category

        } # for each instance
    }

    end {
        if (Test-FunctionInterrupt) { return }
        Write-Message -Message "Finished creating job category." -Level Verbose
    }

}
tools\dbatools\functions\New-DbaAgentJobStep.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function New-DbaAgentJobStep {
    <#
.SYNOPSIS
New-DbaAgentJobStep creates a new job step for a job

.DESCRIPTION
New-DbaAgentJobStep creates a new job in the SQL Server Agent for a specific job

.PARAMETER SqlInstance
SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Job
The name of the job to which to add the step.

.PARAMETER StepId
The sequence identification number for the job step. Step identification numbers start at 1 and increment without gaps.

.PARAMETER StepName
The name of the step.

.PARAMETER SubSystem
The subsystem used by the SQL Server Agent service to execute command.
Allowed values 'ActiveScripting','AnalysisCommand','AnalysisQuery','CmdExec','Distribution','LogReader','Merge','PowerShell','QueueReader','Snapshot','Ssis','TransactSql'
The default is 'TransactSql'

.PARAMETER Command
The commands to be executed by SQLServerAgent service through subsystem.

.PARAMETER CmdExecSuccessCode
The value returned by a CmdExec subsystem command to indicate that command executed successfully.

.PARAMETER OnSuccessAction
The action to perform if the step succeeds.
Allowed values  "QuitWithSuccess" (default), "QuitWithFailure", "GoToNextStep", "GoToStep".
The text value van either be lowercase, uppercase or something in between as long as the text is correct.

.PARAMETER OnSuccessStepId
The ID of the step in this job to execute if the step succeeds and OnSuccessAction is "GoToStep".

.PARAMETER OnFailAction
The action to perform if the step fails.
Allowed values  "QuitWithSuccess" (default), "QuitWithFailure", "GoToNextStep", "GoToStep".
The text value van either be lowercase, uppercase or something in between as long as the text is correct.

.PARAMETER OnFailStepId
The ID of the step in this job to execute if the step fails and OnFailAction is "GoToNextStep".

.PARAMETER Database
The name of the database in which to execute a Transact-SQL step. The default is 'master'.

.PARAMETER DatabaseUser
The name of the user account to use when executing a Transact-SQL step.

.PARAMETER RetryAttempts
The number of retry attempts to use if this step fails. The default is 0.

.PARAMETER RetryInterval
The amount of time in minutes between retry attempts. The default is 0.

.PARAMETER OutputFileName
The name of the file in which the output of this step is saved.

.PARAMETER Flag
Sets the flag(s) for the job step.

Flag                                    Description
----------------------------------------------------------------------------
AppendAllCmdExecOutputToJobHistory      Job history, including command output, is appended to the job history file.
AppendToJobHistory                      Job history is appended to the job history file.
AppendToLogFile                         Job history is appended to the SQL Server log file.
AppendToTableLog                        Job history is appended to a log table.
LogToTableWithOverwrite                 Job history is written to a log table, overwriting previous contents.
None                                    Job history is not appended to a file.
ProvideStopProcessEvent                 Job processing is stopped.

.PARAMETER ProxyName
The name of the proxy that the job step runs as.

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER Force
The force parameter will ignore some errors in the parameters and assume defaults.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Agent, Job, JobStep
Author: Sander Stad (@sqlstad, sqlstad.nl)

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/New-DbaAgentJobStep

.EXAMPLE
New-DbaAgentJobStep -SqlInstance sql1 -Job Job1 -StepName Step1
Create a step in "Job1" with the name Step1 with the default subsystem TransactSql.

.EXAMPLE
New-DbaAgentJobStep -SqlInstance sql1 -Job Job1 -StepName Step1 -Database msdb
Create a step in "Job1" with the name Step1 where the database will the msdb

.EXAMPLE
New-DbaAgentJobStep -SqlInstance sql1, sql2, sql3 -Job Job1 -StepName Step1 -Database msdb
Create a step in "Job1" with the name Step1 where the database will the "msdb" for multiple servers

.EXAMPLE
New-DbaAgentJobStep -SqlInstance sql1, sql2, sql3 -Job Job1, Job2, 'Job Three' -StepName Step1 -Database msdb
Create a step in "Job1" with the name Step1 where the database will the "msdb" for multiple servers for multiple jobs

.EXAMPLE
sql1, sql2, sql3 | New-DbaAgentJobStep -Job Job1 -StepName Step1 -Database msdb
Create a step in "Job1" with the name Step1 where the database will the "msdb" for multiple servers using pipeline
#>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [object[]]$Job,
        [int]$StepId,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$StepName,
        [ValidateSet('ActiveScripting', 'AnalysisCommand', 'AnalysisQuery', 'CmdExec', 'Distribution', 'LogReader', 'Merge', 'PowerShell', 'QueueReader', 'Snapshot', 'Ssis', 'TransactSql')]
        [string]$Subsystem = 'TransactSql',
        [string]$Command,
        [int]$CmdExecSuccessCode,
        [ValidateSet('QuitWithSuccess', 'QuitWithFailure', 'GoToNextStep', 'GoToStep')]
        [string]$OnSuccessAction = 'QuitWithSuccess',
        [int]$OnSuccessStepId = 0,
        [ValidateSet('QuitWithSuccess', 'QuitWithFailure', 'GoToNextStep', 'GoToStep')]
        [string]$OnFailAction = 'QuitWithFailure',
        [int]$OnFailStepId,
        [object]$Database,
        [string]$DatabaseUser,
        [int]$RetryAttempts,
        [int]$RetryInterval,
        [string]$OutputFileName,
        [ValidateSet('AppendAllCmdExecOutputToJobHistory', 'AppendToJobHistory', 'AppendToLogFile', 'LogToTableWithOverwrite', 'None', 'ProvideStopProcessEvent')]
        [string[]]$Flag,
        [string]$ProxyName,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        # Check the parameter on success step id
        if (($OnSuccessAction -in 'GoToStep', 'GoToNextStep') -and ($OnSuccessStepId -ge 1)) {
            Stop-Function -Message "Parameter OnSuccessStepId can only be used with OnSuccessAction 'GoToStep'." -Target $SqlInstance
            return
        }

        # Check the parameter on success step id
        if (($OnFailAction -in 'GoToStep', 'GoToNextStep') -and ($OnFailStepId -ge 1)) {
            Stop-Function -Message "Parameter OnFailStepId can only be used with OnFailAction 'GoToStep'." -Target $SqlInstance
            return
        }
    }

    process {

        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $sqlinstance) {
            # Try connecting to the instance
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $Server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            foreach ($j in $Job) {

                # Check if the job exists
                if ($Server.JobServer.Jobs.Name -notcontains $j) {
                    Write-Message -Message "Job $j doesn't exists on $instance" -Warning
                }
                else {
                    # Create the job step object
                    try {
                        # Get the job
                        $currentjob = $Server.JobServer.Jobs[$j]

                        # Create the job step
                        $JobStep = New-Object Microsoft.SqlServer.Management.Smo.Agent.JobStep

                        # Set the job where the job steps belongs to
                        $JobStep.Parent = $currentjob
                    }
                    catch {
                        Stop-Function -Message "Something went wrong creating the job step" -Target $instance -ErrorRecord $_ -Continue
                    }

                    #region job step options
                    # Setting the options for the job step
                    if ($StepName) {
                        # Check if the step already exists
                        if ($Server.JobServer.Jobs[$j].JobSteps.Name -notcontains $StepName) {
                            $JobStep.Name = $StepName
                        }
                        elseif (($Server.JobServer.Jobs[$j].JobSteps.Name -contains $StepName) -and $Force) {
                            Write-Message -Message "Step $StepName already exists for job. Force is used. Removing existing step" -Level Verbose

                            # Remove the job step based on the name
                            Remove-DbaAgentJobStep -SqlInstance $instance -Job $currentjob -StepName $StepName

                            # Set the name job step object
                            $JobStep.Name = $StepName
                        }
                        else {
                            Stop-Function -Message "The step name $StepName already exists for job $j" -Target $instance -Continue
                        }
                    }

                    # If the step id need to be set
                    if ($StepId) {
                        # Check if the used step id is already in place
                        if ($Job.JobSteps.ID -notcontains $StepId) {
                            Write-Message -Message "Setting job step step id to $StepId" -Level Verbose
                            $JobStep.ID = $StepId
                        }
                        elseif ($Job.JobSteps.ID -contains $StepId) {
                            if($Force){
                                Write-Message -Message "Step ID $StepId already exists for job. Force is used. Removing existing step" -Level Verbose

                                # Remove the existing job step
                                $StepName = ($Server.JobServer.Jobs['Job2'].JobSteps | Where-Object {$_.ID -eq 1}).Name
                                Remove-DbaAgentJobStep -SqlInstance $instance -Job $currentjob -StepName $StepName
                            }

                            # Set the ID job step object
                            $JobStep.ID = $StepId
                        }
                        else {
                            Stop-Function -Message "The step id $StepId already exists for job $j" -Target $instance -Continue
                        }
                    }
                    else {
                        # Get the job step count
                        $JobStep.ID = $Job.JobSteps.Count + 1
                    }

                    if ($Subsystem) {
                        Write-Message -Message "Setting job step subsystem to $Subsystem" -Level Verbose
                        $JobStep.Subsystem = $Subsystem
                    }

                    if ($Command) {
                        Write-Message -Message "Setting job step command to $Command" -Level Verbose
                        $JobStep.Command = $Command
                    }

                    if ($CmdExecSuccessCode) {
                        Write-Message -Message "Setting job step command exec success code to $CmdExecSuccessCode" -Level Verbose
                        $JobStep.CommandExecutionSuccessCode = $CmdExecSuccessCode
                    }

                    if ($OnSuccessAction) {
                        Write-Message -Message "Setting job step success action to $OnSuccessAction" -Level Verbose
                        $JobStep.OnSuccessAction = $OnSuccessAction
                    }

                    if ($OnSuccessStepId) {
                        Write-Message -Message "Setting job step success step id to $OnSuccessStepId" -Level Verbose
                        $JobStep.OnSuccessStep = $OnSuccessStepId
                    }

                    if ($OnFailAction) {
                        Write-Message -Message "Setting job step fail action to $OnFailAction" -Level Verbose
                        $JobStep.OnFailAction = $OnFailAction
                    }

                    if ($OnFailStepId) {
                        Write-Message -Message "Setting job step fail step id to $OnFailStepId" -Level Verbose
                        $JobStep.OnFailStep = $OnFailStepId
                    }

                    if ($Database) {
                        # Check if the database is present on the server
                        if ($Server.Databases.Name -contains $Database) {
                            Write-Message -Message "Setting job step database name to $Database" -Level Verbose
                            $JobStep.DatabaseName = $Database
                        }
                        else {
                            Stop-Function -Message "The database is not present on instance $instance." -Target $instance -Continue
                        }
                    }

                    if ($DatabaseUser -and $DatabaseName) {
                        # Check if the username is present in the database
                        if ($Server.Databases[$DatabaseName].Users.Name -contains $DatabaseUser) {

                            Write-Message -Message "Setting job step database username to $DatabaseUser" -Level Verbose
                            $JobStep.DatabaseUserName = $DatabaseUser
                        }
                        else {
                            Stop-Function -Message "The database user is not present in the database $DatabaseName on instance $instance." -Target $instance -Continue
                        }
                    }

                    if ($RetryAttempts) {
                        Write-Message -Message "Setting job step retry attempts to $RetryAttempts" -Level Verbose
                        $JobStep.RetryAttempts = $RetryAttempts
                    }

                    if ($RetryInterval) {
                        Write-Message -Message "Setting job step retry interval to $RetryInterval" -Level Verbose
                        $JobStep.RetryInterval = $RetryInterval
                    }

                    if ($OutputFileName) {
                        Write-Message -Message "Setting job step output file name to $OutputFileName" -Level Verbose
                        $JobStep.OutputFileName = $OutputFileName
                    }

                    if ($ProxyName) {
                        # Check if the proxy exists
                        if ($Server.JobServer.ProxyAccounts.Name -contains $ProxyName) {
                            Write-Message -Message "Setting job step proxy name to $ProxyName" -Level Verbose
                            $JobStep.ProxyName = $ProxyName
                        }
                        else {
                            Stop-Function -Message "The proxy name $ProxyName doesn't exist on instance $instance." -Target $instance -Continue
                        }
                    }

                    if ($Flag.Count -ge 1) {
                        Write-Message -Message "Setting job step flag(s) to $($Flags -join ',')" -Level Verbose
                        $JobStep.JobStepFlags = $Flag
                    }
                    #endregion job step options

                    # Execute
                    if ($PSCmdlet.ShouldProcess($instance, "Creating the job step $StepName")) {
                        try {
                            Write-Message -Message "Creating the job step" -Level Verbose

                            # Create the job step
                            $JobStep.Create()
                            $currentjob.Alter()
                        }
                        catch {
                            Stop-Function -Message "Something went wrong creating the job step" -Target $instance -ErrorRecord $_ -Continue
                        }
                    }

                    # Return the job step
                    $JobStep
                }
            } # foreach object job
        } # foreach object instance
    } # process

    end {
        if (Test-FunctionInterrupt) { return }
        Write-Message -Message "Finished creating job step(s)" -Level Verbose
    }
}

tools\dbatools\functions\New-DbaAgentProxy.ps1
function New-DbaAgentProxy {
    <#
        .SYNOPSIS
        Adds one or more proxies to SQL Server Agent

        .DESCRIPTION
        Adds one or more proxies to SQL Server Agent

        .PARAMETER SqlInstance
        The SQL Server instance holding the databases to be removed.You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SqlCredential
        Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Name
        The name of the proxy or proxies you want to create

        .PARAMETER Credential
        The associated SQL Server Credential. The credential must be created prior to creating the Proxy.

        .PARAMETER SubSystem
        The associated subsystem or subsystems. Defaults to CmdExec.

        Valid options include:
        ActiveScripting
        AnalysisCommand
        AnalysisQuery
        CmdExec
        Distribution
        LogReader
        Merge
        PowerShell
        QueueReader
        Snapshot
        Ssis
        TransactSql

        .PARAMETER Description
        A description of the proxy

        .PARAMETER Login
        The SQL Server login or logins (known as proxy principals) to assign to the proxy

        .PARAMETER ServerRole
        The SQL Server role or roles (known as proxy principals) to assign to the proxy

        .PARAMETER MsdbRole
        The msdb role or roles (known as proxy principals) to assign to the proxy

        .PARAMETER Disabled
        Create the proxy as disabled

        .PARAMETER Force
        Drop and recreate the proxy if it already exists

        .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
        Tags: Agent, Proxy
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

        .LINK
        https://dbatools.io/New-DbaAgentProxy

        .EXAMPLE
        New-DbaAgentProxy -SqlInstance sql2016 -Name STIG -Credential 'PowerShell Proxy'

        Creates an Agent Proxy on sql2016 with the name STIG with the 'PowerShell Proxy' credential.
        The proxy is automatically added to the CmdExec subsystem.

        .EXAMPLE
        New-DbaAgentProxy -SqlInstance localhost\sql2016 -Name STIG -Credential 'PowerShell Proxy' -Description "Used for auditing purposes" -Login ad\sqlstig -SubSystem CmdExec, PowerShell -ServerRole securtyadmin -MsdbRole ServerGroupAdministratorRole

        Creates an Agent Proxy on sql2016 with the name STIG with the 'PowerShell Proxy' credential and the following principals:

        Login: ad\sqlstig
        ServerRole: securtyadmin
        MsdbRole: ServerGroupAdministratorRole

        By default, only sysadmins have access to create job steps with proxies. This will allow 3 additional principals access:
        The proxy is then added to the CmdExec and PowerShell subsystems

    #>
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
    param (
        [parameter(Mandatory, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory)]
        [string[]]$Name,
        [parameter(Mandatory)]
        [string[]]$Credential,
        [ValidateSet("ActiveScripting", "AnalysisCommand", "AnalysisQuery", "CmdExec", "Distribution", "LogReader", "Merge", "PowerShell", "QueueReader", "Snapshot", "Ssis", "TransactSql")]
        [string[]]$SubSystem = "CmdExec",
        [string]$Description,
        [string[]]$Login,
        [string[]]$ServerRole,
        [string[]]$MsdbRole,
        [switch]$Disabled,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            try {
                $jobServer = $server.JobServer
            }
            catch {
                Stop-Function -Message "Failure. Is SQL Agent started?" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            foreach ($proxyname in $name) {

                if ($jobServer.ProxyAccounts[$proxyName]) {
                    if ($force) {
                        if ($Pscmdlet.ShouldProcess($instance, "Dropping $proxyname")) {
                            $jobServer.ProxyAccounts[$proxyName].Drop()
                            $jobServer.ProxyAccounts.Refresh()
                        }
                    }
                    else {
                        Write-Message -Level Warning -Message "Proxy account $proxy already exists on $instance. Use -Force to drop and recreate."
                        continue
                    }
                }

                if (-not $server.Credentials[$Credential]) {
                    Write-Message -Level Warning -Message "Credential '$Credential' does not exist on $instance"
                    continue
                }

                if ($Pscmdlet.ShouldProcess($instance, "Adding $proxyname with the $Credential credential")) {
                    # the new-object is stubborn and $true/$false has to be forced in
                    $enabled = switch ($disabled) {
                        $false {
                            $proxy = New-Object Microsoft.SqlServer.Management.Smo.Agent.ProxyAccount -ArgumentList $jobServer, $ProxyName, $Credential, $true, $Description
                        }
                        $true {
                            $proxy = New-Object Microsoft.SqlServer.Management.Smo.Agent.ProxyAccount -ArgumentList $jobServer, $ProxyName, $Credential, $false, $Description
                        }
                    }

                    try {
                        $proxy.Create()
                    }
                    catch {
                        Stop-Function -Message "Could not create proxy account" -ErrorRecord $_ -Target $instance -Continue
                    }
                }

                foreach ($loginname in $login) {
                    if ($server.Logins[$loginname]) {
                        if ($Pscmdlet.ShouldProcess($instance, "Adding login $loginname to proxy")) {
                            $proxy.AddLogin($loginname)
                        }
                    }
                    else {
                        Write-Message -Level Warning -Message "Login '$loginname' does not exist on $instance"
                    }
                }

                foreach ($role in $ServerRole) {
                    if ($server.Roles[$role]) {
                        if ($Pscmdlet.ShouldProcess($instance, "Adding server role $role to proxy")) {
                            $proxy.AddServerRole($role)
                        }
                    }
                    else {
                        Write-Message -Level Warning -Message "Server Role '$role' does not exist on $instance"
                    }
                }

                foreach ($role in $MsdbRole) {
                    if ($server.Databases['msdb'].Roles[$role]) {
                        if ($Pscmdlet.ShouldProcess($instance, "Adding msdb role $role to proxy")) {
                            $proxy.AddMsdbRole($role)
                        }
                    }
                    else {
                        Write-Message -Level Warning -Message "msdb role '$role' does not exist on $instance"
                    }
                }

                foreach ($system in $SubSystem) {
                    if ($Pscmdlet.ShouldProcess($instance, "Adding subsystem $system to proxy")) {
                        $proxy.AddSubSystem($system)
                    }
                }

                if ($Pscmdlet.ShouldProcess("console", "Outputting Proxy object")) {
                    $proxy.Alter()
                    $proxy.Refresh()
                    Add-Member -Force -InputObject $proxy -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                    Add-Member -Force -InputObject $proxy -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                    Add-Member -Force -InputObject $proxy -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                    Add-Member -Force -InputObject $proxy -MemberType NoteProperty -Name Logins -value $proxy.EnumLogins()
                    Add-Member -Force -InputObject $proxy -MemberType NoteProperty -Name ServerRoles -value $proxy.EnumServerRoles()
                    Add-Member -Force -InputObject $proxy -MemberType NoteProperty -Name MsdbRoles -value $proxy.EnumMsdbRoles()
                    Add-Member -Force -InputObject $proxy -MemberType NoteProperty -Name Subsystems -value $proxy.EnumSubSystems()

                    Select-DefaultView -InputObject $proxy -Property ComputerName, InstanceName, SqlInstance, ID, Name, CredentialName, CredentialIdentity, Description, Logins, ServerRoles, MsdbRoles, SubSystems, IsEnabled
                }
            }
        }
    }
}
tools\dbatools\functions\New-DbaAgentSchedule.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function New-DbaAgentSchedule {
    <#
        .SYNOPSIS
            New-DbaAgentSchedule creates a new schedule in the msdb database.

        .DESCRIPTION
            New-DbaAgentSchedule will help create a new schedule for a job.
            If the job parameter is not supplied the schedule will not be attached to a job.

        .PARAMETER SqlInstance
            SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Job
            The name of the job that has the schedule.

        .PARAMETER Schedule
            The name of the schedule.

        .PARAMETER Disabled
            Set the schedule to disabled. Default is enabled

        .PARAMETER FrequencyType
            A value indicating when a job is to be executed.

            Allowed values: Once, Daily, Weekly, Monthly, MonthlyRelative, AgentStart or IdleComputer

            If force is used the default will be "Once".

        .PARAMETER FrequencyInterval
            The days that a job is executed

            Allowed values: Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Weekdays, Weekend or EveryDay.
            The other allowed values are the numbers 1 to 31 for each day of the month.

            If "Weekdays", "Weekend" or "EveryDay" is used it over writes any other value that has been passed before.

            If force is used the default will be 1.

        .PARAMETER FrequencySubdayType
            Specifies the units for the subday FrequencyInterval.

            Allowed values: Time, Seconds, Minutes, or Hours

        .PARAMETER FrequencySubdayInterval
            The number of subday type periods to occur between each execution of a job.

        .PARAMETER FrequencyRelativeInterval
            A job's occurrence of FrequencyInterval in each month, if FrequencyInterval is 32 (monthlyrelative).

            Allowed values: First, Second, Third, Fourth or Last

        .PARAMETER FrequencyRecurrenceFactor
            The number of weeks or months between the scheduled execution of a job.

            FrequencyRecurrenceFactor is used only if FrequencyType is "Weekly", "Monthly" or "MonthlyRelative".

        .PARAMETER StartDate
            The date on which execution of a job can begin.

            If force is used the start date will be the current day

        .PARAMETER EndDate
            The date on which execution of a job can stop.

            If force is used the end date will be '9999-12-31'

        .PARAMETER StartTime
            The time on any day to begin execution of a job. Format HHMMSS / 24 hour clock.
            Example: '010000' for 01:00:00 AM.
            Example: '140000' for 02:00:00 PM.

            If force is used the start time will be '00:00:00'

        .PARAMETER EndTime
            The time on any day to end execution of a job. Format HHMMSS / 24 hour clock.
            Example: '010000' for 01:00:00 AM.
            Example: '140000' for 02:00:00 PM.

            If force is used the start time will be '23:59:59'

        .PARAMETER Owner
            The name of the server principal that owns the schedule. If no value is given the schedule is owned by the creator.

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER Force
            The force parameter will ignore some errors in the parameters and assume defaults.
            It will also remove the any present schedules with the same name for the specific job.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Agent, Job, JobStep
            Author: Sander Stad (@sqlstad, sqlstad.nl)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/New-DbaAgentSchedule

        .EXAMPLE
            New-DbaAgentSchedule -SqlInstance localhost\SQL2016 -Schedule daily -FrequencyType Daily -FrequencyInterval Everyday -Force

            Creates a schedule with a daily frequency every day. It assumes default values for the start date, start time, end date and end time due to -Force.

        .EXAMPLE
            New-DbaAgentSchedule -SqlInstance sstad-pc -Schedule MonthlyTest -FrequencyType Monthly -FrequencyInterval 10 -FrequencyRecurrenceFactor 1 -Force

            Create a schedule with a monhtly frequency occuring every 10th of the month. It assumes default values for the start date, start time, end date and end time due to -Force.

    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [System.Management.Automation.PSCredential]
        $SqlCredential,
        [object[]]$Job,
        [object]$Schedule,
        [switch]$Disabled,
        [ValidateSet('Once', 'Daily', 'Weekly', 'Monthly', 'MonthlyRelative', 'AgentStart', 'IdleComputer')]
        [object]$FrequencyType,
        [ValidateSet('EveryDay', 'Weekdays', 'Weekend', 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31)]
        [object[]]$FrequencyInterval,
        [ValidateSet('Time', 'Seconds', 'Minutes', 'Hours')]
        [object]$FrequencySubdayType,
        [int]$FrequencySubdayInterval,
        [ValidateSet('Unused', 'First', 'Second', 'Third', 'Fourth', 'Last')]
        [object]$FrequencyRelativeInterval,
        [int]$FrequencyRecurrenceFactor,
        [string]$StartDate,
        [string]$EndDate,
        [string]$StartTime,
        [string]$EndTime,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        # if a Schedule is not provided there is no much point
        if (!$Schedule) {
            Stop-Function -Message "A schedule was not provided! Please provide a schedule name."
            return
        }

        [int]$Interval = 0

        # Translate FrequencyType value from string to the integer value
        if (!$FrequencyType -or $FrequencyType) {
            [int]$FrequencyType =
            switch ($FrequencyType) {
                "Once" { 1 }
                "Daily" { 4 }
                "Weekly" { 8 }
                "Monthly" { 16 }
                "MonthlyRelative" { 32 }
                "AgentStart" { 64 }
                "IdleComputer" { 128 }
                default { 1 }
            }
        }

        # Translate FrequencySubdayType value from string to the integer value
        if (!$FrequencySubdayType -or $FrequencySubdayType) {
            [int]$FrequencySubdayType =
            switch ($FrequencySubdayType) {
                "Time" { 1 }
                "Seconds" { 2 }
                "Minutes" { 4 }
                "Hours" { 8 }
                default { 1 }
            }
        }

        # Check of the relative FrequencyInterval value is of type string and set the integer value
        [int]$FrequencyRelativeInterval =
        switch ($FrequencyRelativeInterval) {
            "First" { 1 }
            "Second" { 2 }
            "Third" { 4 }
            "Fourth" { 8 }
            "Last" { 16 }
            "Unused" { 0 }
            default {0}
        }

        # Check if the interval is valid
        if (($FrequencyType -in 4, "Daily") -and (($FrequencyInterval -lt 1 -or $FrequencyInterval -ge 365) -and -not $FrequencyInterval -eq "EveryDay")) {
            Stop-Function -Message "The frequency interval $FrequencyInterval requires a frequency interval to be between 1 and 365." -Target $SqlInstance
            return
        }

        # Check if the recurrence factor is set for weekly or monthly interval
        if (($FrequencyType -in (16, 8)) -and $FrequencyRecurrenceFactor -lt 1) {
            if ($Force) {
                $FrequencyRecurrenceFactor = 1
                Write-Message -Message "Recurrence factor not set for weekly or monthly interval. Setting it to $FrequencyRecurrenceFactor." -Level Verbose
            }
            else {
                Stop-Function -Message "The recurrence factor $FrequencyRecurrenceFactor (parameter FrequencyRecurrenceFactor) needs to be at least one when using a weekly or monthly interval." -Target $SqlInstance
                return
            }
        }

        # Check the subday interval
        if (($FrequencySubdayType -in 2, "Seconds", 4, "Minutes") -and (-not ($FrequencySubdayInterval -ge 1 -or $FrequencySubdayInterval -le 59))) {
            Stop-Function -Message "Subday interval $FrequencySubdayInterval must be between 1 and 59 when subday type is 'Seconds' or 'Minutes'" -Target $SqlInstance
            return
        }
        elseif (($FrequencySubdayType -eq 8, "Hours") -and (-not ($FrequencySubdayInterval -ge 1 -and $FrequencySubdayInterval -le 23))) {
            Stop-Function -Message "Subday interval $FrequencySubdayInterval must be between 1 and 23 when subday type is 'Hours'" -Target $SqlInstance
            return
        }

        # If the FrequencyInterval is set for the daily FrequencyType
        if ($FrequencyType -in 4, 'Daily') {
            # Create the interval to hold the value(s)
            [int]$Interval = 0

            # Create the interval to hold the value(s)
            switch ($FrequencyInterval) {
                "EveryDay" { $Interval = 1}
                default {$Interval = 1 }
            }

        }

        # If the FrequencyInterval is set for the weekly FrequencyType
        if ($FrequencyType -in 8, 'Weekly') {
            # Create the interval to hold the value(s)
            [int]$Interval = 0

            # Loop through the array
            foreach ($Item in $FrequencyInterval) {

                switch ($Item) {
                    "Sunday" { $Interval += 1 }
                    "Monday" { $Interval += 2 }
                    "Tuesday" { $Interval += 4 }
                    "Wednesday" { $Interval += 8 }
                    "Thursday" { $Interval += 16 }
                    "Friday" { $Interval += 32 }
                    "Saturday" { $Interval += 64 }
                    "Weekdays" { $Interval = 62 }
                    "Weekend" { $Interval = 65 }
                    "EveryDay" {$Interval = 127 }
                    1 { $Interval += 1 }
                    2 { $Interval += 2 }
                    4 { $Interval += 4 }
                    8 { $Interval += 8 }
                    16 { $Interval += 16 }
                    32 { $Interval += 32 }
                    64 { $Interval += 64 }
                    62 { $Interval = 62 }
                    65 { $Interval = 65 }
                    127 {$Interval = 127 }
                    default { $Interval = 0 }
                }
            }
        }

        # If the FrequencyInterval is set for the monthly FrequencyInterval
        if ($FrequencyType -in 16, 'Monthly') {
            # Create the interval to hold the value(s)
            [int]$Interval = 0

            # Loop through the array
            foreach ($Item in $FrequencyInterval) {
                $FrequencyInterval
                switch ($Item) {
                    {[int]$_ -ge 1 -and [int]$_ -le 31} { $Interval = [int]$Item }
                }
            }


        }

        # If the FrequencyInterval is set for the relative monthly FrequencyInterval
        if ($FrequencyType -eq 32) {
            # Create the interval to hold the value(s)
            [int]$Interval = 0

            # Loop through the array
            foreach ($Item in $FrequencyInterval) {
                switch ($Item) {
                    "Sunday" { $Interval += 1 }
                    "Monday" { $Interval += 2 }
                    "Tuesday" { $Interval += 3 }
                    "Wednesday" { $Interval += 4 }
                    "Thursday" { $Interval += 5 }
                    "Friday" { $Interval += 6 }
                    "Saturday" { $Interval += 7 }
                    "Day" { $Interval += 8 }
                    "Weekday" { $Interval += 9 }
                    "WeekendDay" { $Interval += 10 }
                    1 { $Interval += 1 }
                    2 { $Interval += 2 }
                    3 { $Interval += 3 }
                    4 { $Interval += 4 }
                    5 { $Interval += 5 }
                    6 { $Interval += 6 }
                    7 { $Interval += 7 }
                    8 { $Interval += 8 }
                    9 { $Interval += 9 }
                    10 { $Interval += 10 }
                }
            }
        }

        # Check if the interval is valid for the frequency
        if ($FrequencyType -eq 0) {
            if ($Force) {
                Write-Message -Message "Parameter FrequencyType must be set to at least [Once]. Setting it to 'Once'." -Level Warning
                $FrequencyType = 1
            }
            else {
                Stop-Function -Message "Parameter FrequencyType must be set to at least [Once]" -Target $SqlInstance
                return
            }
        }

        # Check if the interval is valid for the frequency
        if (($FrequencyType -in 4, 8, 32) -and ($Interval -lt 1)) {
            if ($Force) {
                Write-Message -Message "Parameter FrequencyInterval must be provided for a recurring schedule. Setting it to first day of the week." -Level Warning
                $Interval = 1
            }
            else {
                Stop-Function -Message "Parameter FrequencyInterval must be provided for a recurring schedule." -Target $SqlInstance
                return
            }
        }

        # Setup the regex
        $RegexDate = '(?<!\d)(?:(?:(?:1[6-9]|[2-9]\d)?\d{2})(?:(?:(?:0[13578]|1[02])31)|(?:(?:0[1,3-9]|1[0-2])(?:29|30)))|(?:(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))0229)|(?:(?:1[6-9]|[2-9]\d)?\d{2})(?:(?:0?[1-9])|(?:1[0-2]))(?:0?[1-9]|1\d|2[0-8]))(?!\d)'
        $RegexTime = '^(?:(?:([01]?\d|2[0-3]))?([0-5]?\d))?([0-5]?\d)$'

        # Check the start date
        if (-not $StartDate -and $Force) {
            $StartDate = Get-Date -Format 'yyyyMMdd'
            Write-Message -Message "Start date was not set. Force is being used. Setting it to $StartDate" -Level Verbose
        }
        elseif (-not $StartDate) {
            Stop-Function -Message "Please enter a start date or use -Force to use defaults." -Target $SqlInstance
            return
        }
        elseif ($StartDate -notmatch $RegexDate) {
            Stop-Function -Message "Start date $StartDate needs to be a valid date with format yyyyMMdd" -Target $SqlInstance
            return
        }

        # Check the end date
        if (-not $EndDate -and $Force) {
            $EndDate = '99991231'
            Write-Message -Message "End date was not set. Force is being used. Setting it to $EndDate" -Level Verbose
        }
        elseif (-not $EndDate) {
            Stop-Function -Message "Please enter an end date or use -Force to use defaults." -Target $SqlInstance
            return
        }

        elseif ($EndDate -notmatch $RegexDate) {
            Stop-Function -Message "End date $EndDate needs to be a valid date with format yyyyMMdd" -Target $SqlInstance
            return
        }
        elseif ($EndDate -lt $StartDate) {
            Stop-Function -Message "End date $EndDate cannot be before start date $StartDate" -Target $SqlInstance
            return
        }

        # Check the start time
        if (-not $StartTime -and $Force) {
            $StartTime = '000000'
            Write-Message -Message "Start time was not set. Force is being used. Setting it to $StartTime" -Level Verbose
        }
        elseif (-not $StartTime) {
            Stop-Function -Message "Please enter a start time or use -Force to use defaults." -Target $SqlInstance
            return
        }
        elseif ($StartTime -notmatch $RegexTime) {
            Stop-Function -Message "Start time $StartTime needs to match between '000000' and '235959'" -Target $SqlInstance
            return
        }

        # Check the end time
        if (-not $EndTime -and $Force) {
            $EndTime = '235959'
            Write-Message -Message "End time was not set. Force is being used. Setting it to $EndTime" -Level Verbose
        }
        elseif (-not $EndTime) {
            Stop-Function -Message "Please enter an end time or use -Force to use defaults." -Target $SqlInstance
            return
        }
        elseif ($EndTime -notmatch $RegexTime) {
            Stop-Function -Message "End time $EndTime needs to match between '000000' and '235959'" -Target $SqlInstance
            return
        }
    }

    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $sqlinstance) {
            # Try connecting to the instance
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            # Check if the jobs parameter is set
            if ($Job) {
                # Loop through each of the jobs
                foreach ($j in $Job) {

                    # Check if the job exists
                    if ($Server.JobServer.Jobs.Name -notcontains $j) {
                        Write-Message -Message "Job $j doesn't exists on $instance" -Level Warning
                    }
                    else {
                        # Create the job schedule object
                        try {
                            # Get the job
                            $smoJob = $Server.JobServer.Jobs[$j]

                            # Check if schedule already exists with the same name
                            if ($Server.JobServer.JobSchedules.Name -contains $Schedule) {
                                # Check if force is set which will remove the other schedule
                                if ($Force) {
                                    if ($PSCmdlet.ShouldProcess($instance, "Removing the schedule $Schedule on $instance")) {
                                        # Removing schedule
                                        Remove-DbaAgentSchedule -SqlInstance $instance -SqlCredential $SqlCredential -Schedule $Schedule -Force:$Force
                                    }
                                }
                                else {
                                    Stop-Function -Message "Schedule $Schedule already exists for job $j on instance $instance" -Target $instance -ErrorRecord $_ -Continue
                                }
                            }

                            # Create the job schedule
                            $JobSchedule = New-Object Microsoft.SqlServer.Management.Smo.Agent.JobSchedule($smoJob, $Schedule)

                        }
                        catch {
                            Stop-Function -Message "Something went wrong creating the job schedule $Schedule for job $j." -Target $instance -ErrorRecord $_ -Continue
                        }

                        #region job schedule options
                        if ($Disabled) {
                            Write-Message -Message "Setting job schedule to disabled" -Level Verbose
                            $JobSchedule.IsEnabled = $false
                        }
                        else {
                            Write-Message -Message "Setting job schedule to enabled" -Level Verbose
                            $JobSchedule.IsEnabled = $true
                        }

                        if ($Interval -ge 0) {
                            Write-Message -Message "Setting job schedule frequency interval to $Interval" -Level Verbose
                            $JobSchedule.FrequencyInterval = $Interval
                        }

                        if ($FrequencyType -ge 1) {
                            Write-Message -Message "Setting job schedule frequency to $FrequencyType" -Level Verbose
                            $JobSchedule.FrequencyTypes = $FrequencyType
                        }

                        if ($FrequencySubdayType -ge 1) {
                            Write-Message -Message "Setting job schedule frequency subday type to $FrequencySubdayType" -Level Verbose
                            $JobSchedule.FrequencySubDayTypes = $FrequencySubdayType
                        }

                        if ($FrequencySubdayInterval -ge 1) {
                            Write-Message -Message "Setting job schedule frequency subday interval to $FrequencySubdayInterval" -Level Verbose
                            $JobSchedule.FrequencySubDayInterval = $FrequencySubdayInterval
                        }

                        if (($FrequencyRelativeInterval -ge 1) -and ($FrequencyType -eq 32)) {
                            Write-Message -Message "Setting job schedule frequency relative interval to $FrequencyRelativeInterval" -Level Verbose
                            $JobSchedule.FrequencyRelativeIntervals = $FrequencyRelativeInterval
                        }

                        if (($FrequencyRecurrenceFactor -ge 1) -and ($FrequencyType -in 8, 16, 32)) {
                            Write-Message -Message "Setting job schedule frequency recurrence factor to $FrequencyRecurrenceFactor" -Level Verbose
                            $JobSchedule.FrequencyRecurrenceFactor = $FrequencyRecurrenceFactor
                        }

                        if ($StartDate) {
                            $StartDate = $StartDate.Insert(6, '-').Insert(4, '-')
                            Write-Message -Message "Setting job schedule start date to $StartDate" -Level Verbose
                            $JobSchedule.ActiveStartDate = $StartDate
                        }

                        if ($EndDate) {
                            $EndDate = $EndDate.Insert(6, '-').Insert(4, '-')
                            Write-Message -Message "Setting job schedule end date to $EndDate" -Level Verbose
                            $JobSchedule.ActiveEndDate = $EndDate
                        }

                        if ($StartTime) {
                            $StartTime = $StartTime.Insert(4, ':').Insert(2, ':')
                            Write-Message -Message "Setting job schedule start time to $StartTime" -Level Verbose
                            $JobSchedule.ActiveStartTimeOfDay = $StartTime
                        }

                        if ($EndTime) {
                            $EndTime = $EndTime.Insert(4, ':').Insert(2, ':')
                            Write-Message -Message "Setting job schedule end time to $EndTime" -Level Verbose
                            $JobSchedule.ActiveEndTimeOfDay = $EndTime
                        }
                        #endregion job schedule options

                        # Create the schedule
                        if ($PSCmdlet.ShouldProcess($SqlInstance, "Adding the schedule $Schedule to job $j on $instance")) {
                            try {
                                Write-Message -Message "Adding the schedule $Schedule to job $j" -Level Verbose
                                #$JobSchedule
                                $JobSchedule.Create()

                                Write-Message -Message "Job schedule created with UID $($JobSchedule.ScheduleUid)" -Level Verbose
                            }
                            catch {
                                Stop-Function -Message "Something went wrong adding the schedule" -Target $instance -ErrorRecord $_ -Continue

                            }

                            # Output the job schedule
                            return $JobSchedule
                        }
                    }
                } # foreach object job
            } # end if job
            else {
                # Create the schedule
                $JobSchedule = New-Object Microsoft.SqlServer.Management.Smo.Agent.JobSchedule($Server.JobServer, $Schedule)

                #region job schedule options
                if ($Disabled) {
                    Write-Message -Message "Setting job schedule to disabled" -Level Verbose
                    $JobSchedule.IsEnabled = $false
                }
                else {
                    Write-Message -Message "Setting job schedule to enabled" -Level Verbose
                    $JobSchedule.IsEnabled = $true
                }

                if ($Interval -ge 1) {
                    Write-Message -Message "Setting job schedule frequency interval to $Interval" -Level Verbose
                    $JobSchedule.FrequencyInterval = $Interval
                }

                if ($FrequencyType -ge 1) {
                    Write-Message -Message "Setting job schedule frequency to $FrequencyType" -Level Verbose
                    $JobSchedule.FrequencyTypes = $FrequencyType
                }

                if ($FrequencySubdayType -ge 1) {
                    Write-Message -Message "Setting job schedule frequency subday type to $FrequencySubdayType" -Level Verbose
                    $JobSchedule.FrequencySubDayTypes = $FrequencySubdayType
                }

                if ($FrequencySubdayInterval -ge 1) {
                    Write-Message -Message "Setting job schedule frequency subday interval to $FrequencySubdayInterval" -Level Verbose
                    $JobSchedule.FrequencySubDayInterval = $FrequencySubdayInterval
                }

                if (($FrequencyRelativeInterval -ge 1) -and ($FrequencyType -eq 32)) {
                    Write-Message -Message "Setting job schedule frequency relative interval to $FrequencyRelativeInterval" -Level Verbose
                    $JobSchedule.FrequencyRelativeIntervals = $FrequencyRelativeInterval
                }

                if (($FrequencyRecurrenceFactor -ge 1) -and ($FrequencyType -in 8, 16, 32)) {
                    Write-Message -Message "Setting job schedule frequency recurrence factor to $FrequencyRecurrenceFactor" -Level Verbose
                    $JobSchedule.FrequencyRecurrenceFactor = $FrequencyRecurrenceFactor
                }

                if ($StartDate) {
                    $StartDate = $StartDate.Insert(6, '-').Insert(4, '-')
                    Write-Message -Message "Setting job schedule start date to $StartDate" -Level Verbose
                    $JobSchedule.ActiveStartDate = $StartDate
                }

                if ($EndDate) {
                    $EndDate = $EndDate.Insert(6, '-').Insert(4, '-')
                    Write-Message -Message "Setting job schedule end date to $EndDate" -Level Verbose
                    $JobSchedule.ActiveEndDate = $EndDate
                }

                if ($StartTime) {
                    $StartTime = $StartTime.Insert(4, ':').Insert(2, ':')
                    Write-Message -Message "Setting job schedule start time to $StartTime" -Level Verbose
                    $JobSchedule.ActiveStartTimeOfDay = $StartTime
                }

                if ($EndTime) {
                    $EndTime = $EndTime.Insert(4, ':').Insert(2, ':')
                    Write-Message -Message "Setting job schedule end time to $EndTime" -Level Verbose
                    $JobSchedule.ActiveEndTimeOfDay = $EndTime
                }

                # Create the schedule
                if ($PSCmdlet.ShouldProcess($SqlInstance, "Adding the schedule $schedule on $instance")) {
                    try {
                        Write-Message -Message "Adding the schedule $JobSchedule on instance $instance" -Level Verbose

                        $JobSchedule.Create()

                        Write-Message -Message "Job schedule created with UID $($JobSchedule.ScheduleUid)" -Level Verbose
                    }
                    catch {
                        Stop-Function -Message "Something went wrong adding the schedule." -Target $instance -ErrorRecord $_ -Continue
                    }

                    # Output the job schedule
                    return $JobSchedule
                }
            }
        } # foreach object instance
    } #process

    end {
        if (Test-FunctionInterrupt) { return }
        Write-Message -Message "Finished creating job schedule(s)." -Level Verbose
    }
}
tools\dbatools\functions\New-DbaClientAlias.ps1
function New-DbaClientAlias {
    <#
    .SYNOPSIS
    Creates/updates a sql alias for the specified server - mimics cliconfg.exe

    .DESCRIPTION
    Creates/updates a SQL Server alias by altering HKLM:\SOFTWARE\Microsoft\MSSQLServer\Client

    .PARAMETER ComputerName
    The target computer where the alias will be created

    .PARAMETER Credential
    Allows you to login to remote computers using alternative credentials

    .PARAMETER ServerName
    The target SQL Server

    .PARAMETER Alias
    The alias to be created

    .PARAMETER Protocol
    The protocol for the connection, either TCPIP or NetBIOS. Defaults to TCPIP.

    .PARAMETER EnableException
    By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
    Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
    Tags: Alias

    Website: https://dbatools.io
    Copyright: (C) Chrissy LeMaire, [email protected]
    License: MIT https://opensource.org/licenses/MIT

    .LINK
    https://dbatools.io/New-DbaClientAlias

    .EXAMPLE
    New-DbaClientAlias -ServerName sqlcluster\sharepoint -Alias sp
    Creates a new TCP alias on the local workstation called sp, which points sqlcluster\sharepoint


    .EXAMPLE
    New-DbaClientAlias -ServerName 'sqlcluster,14443' -Alias spinstance
    Creates a new TCP alias on the local workstation called spinstance, which points to sqlcluster, port 14443.

    .EXAMPLE
    New-DbaClientAlias -ServerName sqlcluster\sharepoint -Alias sp -Protocol NamedPipes
    Creates a new NamedPipes alias on the local workstation called sp, which points sqlcluster\sharepoint

#>
    [CmdletBinding()]
    Param (
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [parameter(Mandatory, ValueFromPipeline)]
        [DbaInstanceParameter[]]$ServerName,
        [parameter(Mandatory)]
        [string]$Alias,
        [ValidateSet("TCPIP", "NamedPipes")]
        [string]$Protocol = "TCPIP",
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        # This is a script block so cannot use messaging system
        $scriptblock = {
            $basekeys = "HKLM:\SOFTWARE\WOW6432Node\Microsoft\MSSQLServer", "HKLM:\SOFTWARE\Microsoft\MSSQLServer"
            $ServerName = $args[0]
            $Alias = $args[1]
            $serverstring = $args[2]

            if ($env:PROCESSOR_ARCHITECTURE -like "*64*") { $64bit = $true }

            foreach ($basekey in $basekeys) {
                if ($64bit -ne $true -and $basekey -like "*WOW64*") { continue }

                if ((Test-Path $basekey) -eq $false) {
                    throw "Base key ($basekey) does not exist. Quitting."
                }

                $client = "$basekey\Client"

                if ((Test-Path $client) -eq $false) {
                    # "Creating $client key"
                    $null = New-Item -Path $client -Force
                }

                $connect = "$client\ConnectTo"

                if ((Test-Path $connect) -eq $false) {
                    # "Creating $connect key"
                    $null = New-Item -Path $connect -Force
                }

                if ($basekey -like "*WOW64*") {
                    $architecture = "32-bit"
                }
                else {
                    $architecture = "64-bit"
                }

                # Write-Verbose "Creating/updating alias for $ComputerName for $architecture"
                $null = New-ItemProperty -Path $connect -Name $Alias -Value $serverstring -PropertyType String -Force
            }
        }
    }

    process {
        if ($protocol -eq "TCPIP") {
            $serverstring = "DBMSSOCN,$ServerName"
        }
        else {
            $serverstring = "DBNMPNTW,\\$ServerName\pipe\sql\query"
        }

        foreach ($computer in $ComputerName.ComputerName) {

            $null = Test-ElevationRequirement -ComputerName $computer -Continue

            if ($PScmdlet.ShouldProcess($computer, "Adding $alias")) {
                try {
                    Invoke-Command2 -ComputerName $computer -Credential $Credential -ScriptBlock $scriptblock -ErrorAction Stop -ArgumentList $ServerName, $Alias, $serverstring
                }
                catch {
                    Stop-Function -Message "Failure" -ErrorRecord $_ -Target $computer -Continue
                }
            }
        }

        Get-DbaClientAlias -ComputerName $computer -Credential $Credential | Where-Object AliasName -eq $Alias
    }
}
tools\dbatools\functions\New-DbaCmConnection.ps1
function New-DbaCmConnection {
    <#
        .SYNOPSIS
            Generates a connection object for use in remote computer management.

        .DESCRIPTION
            Generates a connection object for use in remote computer management.
            Those objects are used for the purpose of cim/wmi queries, caching which protocol worked, optimizing performance and minimizing authentication errors.

            New-DbaCmConnection will create a NEW object and overwrite any existing ones for the specified computer.
            Furthermore, information stored in the input beyond the computername will be discarded in favor of the new settings.

            Unless the connection cache has been disabled, all connections will automatically be registered in the cache, so no further action is necessary.
            The output is primarily for information purposes, however it may be used to pass objects and circumvent the cache with those.

            NOTE: Generally, this function need not be used, as a first connection to a computer using any connecting function such as "Get-DbaCmObject" will automatically register a new default connection for it.

            This function exists to be able to preconfigure connections.

        .PARAMETER ComputerName
            The computer to build the connection object for.

        .PARAMETER Credential
            The credential to register.

        .PARAMETER UseWindowsCredentials
            Whether using the default windows credentials is legit.
            Not setting this will not exclude using windows credentials, but only not pre-confirm them as working.

        .PARAMETER OverrideExplicitCredential
            Setting this will enable the credential override.
            The override will cause the system to ignore explicitly specified credentials, so long as known, good credentials are available.

        .PARAMETER DisabledConnectionTypes
            Exlicitly disable connection types.
            These types will then not be used for connecting to the computer.

        .PARAMETER DisableBadCredentialCache
            Will prevent the caching of credentials if set to true.

        .PARAMETER DisableCimPersistence
            Will prevent Cim-Sessions to be reused.

        .PARAMETER DisableCredentialAutoRegister
            Will prevent working credentials from being automatically cached

        .PARAMETER EnableCredentialFailover
            Will enable automatic failing over to known to work credentials, when using bad credentials.
            By default, passing bad credentials will cause the Computer Management functions to interrupt with a warning (Or exception if in silent mode).

        .PARAMETER WindowsCredentialsAreBad
            Will prevent the windows credentials of the currently logged on user from being used for the remote connection.

        .PARAMETER CimWinRMOptions
            Specify a set of options to use when connecting to the target computer using CIM over WinRM.
            Use 'New-CimSessionOption' to create such an object.

        .PARAMETER CimDCOMOptions
            Specify a set of options to use when connecting to the target computer using CIM over DCOM.
            Use 'New-CimSessionOption' to create such an object.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ComputerManagement, CIM
            Author: Fred Winmann (@FredWeinmann)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/New-DbaCmConnection

        .EXAMPLE
            New-DbaCmConnection -ComputerName sql2014 -UseWindowsCredentials -OverrideExplicitCredential -DisabledConnectionTypes CimRM

            Returns a new configuration object for connecting to the computer sql2014.
            - The current user credentials are set as valid
            - The connection is configured to ignore explicit credentials (so all connections use the windows credentials)
            - The connections will not try using CIM over WinRM

            Unless caching is globally disabled, this is automatically stored in the connection cache and will be applied automatically.
            In that (the default) case, the output is for information purposes only and need not be used.

        .EXAMPLE
            Get-Content computers.txt | New-DbaCmConnection -Credential $cred -CimWinRMOptions $options -DisableBadCredentialCache -OverrideExplicitCredential

            Gathers a list of computers from a text file, then creates and registers connections for each of them, setting them to ...
            - use the credentials stored in $cred
            - use the options stored in $options when connecting using CIM over WinRM
            - not store credentials that are known to not work
            - to ignore explicitly specified credentials

            Essentially, this configures all connections to those computers to prefer failure with the specified credentials over using alternative credentials.
    #>
    [CmdletBinding(DefaultParameterSetName = 'Credential')]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Sqlcollaborative.Dbatools.Parameter.DbaCmConnectionParameter[]]
        $ComputerName = $env:COMPUTERNAME,
        [Parameter(ParameterSetName = "Credential")]
        [PSCredential]
        $Credential,
        [Parameter(ParameterSetName = "Windows")]
        [switch]
        $UseWindowsCredentials,
        [switch]
        $OverrideExplicitCredential,
        [Sqlcollaborative.Dbatools.Connection.ManagementConnectionType]
        $DisabledConnectionTypes = 'None',
        [switch]
        $DisableBadCredentialCache,
        [switch]
        $DisableCimPersistence,
        [switch]
        $DisableCredentialAutoRegister,
        [switch]
        $EnableCredentialFailover,
        [Parameter(ParameterSetName = "Credential")]
        [switch]
        $WindowsCredentialsAreBad,
        [Microsoft.Management.Infrastructure.Options.WSManSessionOptions]
        $CimWinRMOptions,
        [Microsoft.Management.Infrastructure.Options.DComSessionOptions]
        $CimDCOMOptions,
        [switch]
        [Alias('Silent')]$EnableException
    )

    begin {
        Write-Message -Level InternalComment -Message "Starting execution"
        Write-Message -Level Verbose -Message "Bound parameters: $($PSBoundParameters.Keys -join ", ")"

        $disable_cache = Get-DbaConfigValue -Name 'ComputerManagement.Cache.Disable.All' -Fallback $false
    }
    process {
        foreach ($connectionObject in $ComputerName) {
            if (-not $connectionObject.Success) { Stop-Function -Message "Failed to interpret computername input: $($connectionObject.InputObject)" -Category InvalidArgument -Target $connectionObject.InputObject -Continue }
            Write-Message -Level VeryVerbose -Message "Processing computer: $($connectionObject.Connection.ComputerName)" -Target $connectionObject.Connection

            $connection = New-Object -TypeName Sqlcollaborative.Dbatools.Connection.ManagementConnection -ArgumentList $connectionObject.Connection.ComputerName
            if (Test-Bound "Credential") { $connection.Credentials = $Credential }
            if (Test-Bound "UseWindowsCredentials") {
                $connection.Credentials = $null
                $connection.UseWindowsCredentials = $UseWindowsCredentials
            }
            if (Test-Bound "OverrideExplicitCredential") { $connection.OverrideExplicitCredential = $OverrideExplicitCredential }
            if (Test-Bound "DisabledConnectionTypes") { $connection.DisabledConnectionTypes = $DisabledConnectionTypes }
            if (Test-Bound "DisableBadCredentialCache") { $connection.DisableBadCredentialCache = $DisableBadCredentialCache }
            if (Test-Bound "DisableCimPersistence") { $connection.DisableCimPersistence = $DisableCimPersistence }
            if (Test-Bound "DisableCredentialAutoRegister") { $connection.DisableCredentialAutoRegister = $DisableCredentialAutoRegister }
            if (Test-Bound "EnableCredentialFailover") { $connection.DisableCredentialAutoRegister = $EnableCredentialFailover }
            if (Test-Bound "WindowsCredentialsAreBad") { $connection.WindowsCredentialsAreBad = $WindowsCredentialsAreBad }
            if (Test-Bound "CimWinRMOptions") { $connection.CimWinRMOptions = $CimWinRMOptions }
            if (Test-Bound "CimDCOMOptions") { $connection.CimDCOMOptions = $CimDCOMOptions }

            if (-not $disable_cache) {
                Write-Message -Level Verbose -Message "Writing connection to cache"
                [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::Connections[$connectionObject.Connection.ComputerName] = $connection
            }
            else { Write-Message -Level Verbose -Message "Skipping writing to cache, since the cache has been disabled!" }
            $connection
        }
    }
    end {
        Write-Message -Level InternalComment -Message "Stopping execution"
    }
}
tools\dbatools\functions\New-DbaComputerCertificate.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function New-DbaComputerCertificate {
    <#
        .SYNOPSIS
            Creates a new computer certificate useful for Forcing Encryption

        .DESCRIPTION
            Creates a new computer certificate - self-signed or signed by an Active Directory CA, using the Web Server certificate.

            By default, a key with a length of 1024 and a friendly name of the machines FQDN is generated.

            This command was originally intended to help automate the process so that SSL certificates can be available for enforcing encryption on connections.

            It makes a lot of assumptions - namely, that your account is allowed to auto-enroll and that you have permission to do everything it needs to do ;)

            References:
            http://sqlmag.com/sql-server/7-steps-ssl-encryption
            https://azurebi.jppp.org/2016/01/23/using-lets-encrypt-certificates-for-secure-sql-server-connections/
            https://blogs.msdn.microsoft.com/sqlserverfaq/2016/09/26/creating-and-registering-ssl-certificates/

            The certificate is generated using AD's webserver SSL template on the client machine and pushed to the remote machine.

        .PARAMETER ComputerName
            The target SQL Server - defaults to localhost. If target is a cluster, you must also specify ClusterInstanceName (see below)

        .PARAMETER Credential
            Allows you to login to $ComputerName using alternative credentials.

        .PARAMETER CaServer
            Optional - the CA Server where the request will be sent to

        .PARAMETER CaName
            The properly formatted CA name of the corresponding CaServer

        .PARAMETER ClusterInstanceName
            When creating certs for a cluster, use this parameter to create the certificate for the cluster node name. Use ComputerName for each of the nodes.

        .PARAMETER Password
            Password to encrypt/decrypt private key for export to remote machine

        .PARAMETER FriendlyName
            The FriendlyName listed in the certificate. This defaults to the FQDN of the $ComputerName

        .PARAMETER CertificateTemplate
            The domain's Certificate Template - WebServer by default.

        .PARAMETER KeyLength
            The length of the key - defaults to 1024

        .PARAMETER Store
            Certificate store - defaults to LocalMachine

        .PARAMETER Folder
            Certificate folder - defaults to My (Personal)

        .PARAMETER Dns
            Specify the Dns entries listed in SAN. By default, it will be ComputerName + FQDN, or in the case of clusters, clustername + cluster FQDN.

        .PARAMETER SelfSigned
            Creates a self-signed certificate. All other parameters can still apply except CaServer and CaName because the command does not go and get the certificate signed.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .NOTES
            Tags: Certificate

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            New-DbaComputerCertificate

            Creates a computer certificate signed by the local domain CA for the local machine with the keylength of 1024.

        .EXAMPLE
            New-DbaComputerCertificate -ComputerName Server1

            Creates a computer certificate signed by the local domain CA _on the local machine_ for server1 with the keylength of 1024.

            The certificate is then copied to the new machine over WinRM and imported.

        .EXAMPLE
            New-DbaComputerCertificate -ComputerName sqla, sqlb -ClusterInstanceName sqlcluster -KeyLength 4096

            Creates a computer certificate for sqlcluster, signed by the local domain CA, with the keylength of 4096.

            The certificate is then copied to sqla _and_ sqlb over WinRM and imported.

        .EXAMPLE
            New-DbaComputerCertificate -ComputerName Server1 -WhatIf

            Shows what would happen if the command were run

        .EXAMPLE
            New-DbaComputerCertificate -SelfSigned

            Creates a self-signed certificate
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param (
        [parameter(ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer", "SqlInstance")]
        [DbaInstance[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [string]$CaServer,
        [string]$CaName,
        [string]$ClusterInstanceName,
        [securestring]$Password,
        [string]$FriendlyName = "SQL Server",
        [string]$CertificateTemplate = "WebServer",
        [int]$KeyLength = 1024,
        [string]$Store = "LocalMachine",
        [string]$Folder = "My",
        [string[]]$Dns,
        [switch]$SelfSigned,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        $englishCodes = 9, 1033, 2057, 3081, 4105, 5129, 6153, 7177, 8201, 9225
        if ($englishCodes -notcontains (Get-DbaCmObject Win32_OperatingSystem).OSLanguage) {
            Stop-Function -Message "Currently, this command is only supported in English OS locales. OS Locale detected: $([System.Globalization.CultureInfo]::GetCultureInfo([int](Get-DbaCmObject Win32_OperatingSystem).OSLanguage).DisplayName)`nWe apologize for the inconvenience and look into providing universal language support in future releases."
            return
        }

        if (-not (Test-ElevationRequirement -ComputerName $env:COMPUTERNAME)) {
            return
        }

        function GetHexLength {
            [cmdletbinding()]
            param(
                [int]$strLen
            )
            $hex = [String]::Format("{0:X2}", $strLen)

            if ($strLen -gt 127) { [String]::Format("{0:X2}", 128 + ($hex.Length / 2)) + $hex }
            else { $hex }
        }

        function Get-SanExt {
            [cmdletbinding()]
            param(
                [string[]]$hostName
            )
            # thanks to Lincoln of
            # https://social.technet.microsoft.com/Forums/windows/en-US/f568edfa-7f93-46a4-aab9-a06151592dd9/converting-ascii-to-asn1-der

            $temp = ''
            foreach ($fqdn in $hostName) {
                # convert each character of fqdn to hex
                $hexString = ($fqdn.ToCharArray() | ForEach-Object { [String]::Format("{0:X2}", [int]$_) }) -join ''

                # length of hex fqdn, in hex
                $hexLength = GetHexLength ($hexString.Length / 2)

                # concatenate special code 82, hex length, hex string
                $temp += "82${hexLength}${hexString}"
            }
            # calculate total length of concatenated string, in hex
            $totalHexLength = GetHexLength ($temp.Length / 2)
            # concatenate special code 30, hex length, hex string
            $temp = "30${totalHexLength}${temp}"
            # convert to binary
            $bytes = $(
                for ($i = 0; $i -lt $temp.Length; $i += 2) {
                    [byte]"0x$($temp.SubString($i, 2))"
                }
            )
            # convert to base 64
            $base64 = [Convert]::ToBase64String($bytes)
            # output in proper format
            for ($i = 0; $i -lt $base64.Length; $i += 64) {
                $line = $base64.SubString($i, [Math]::Min(64, $base64.Length - $i))
                if ($i -eq 0) { "2.5.29.17=$line" }
                else { "_continue_=$line" }
            }
        }

        if ((!$CaServer -or !$CaName) -and !$SelfSigned) {
            try {
                Write-Message -Level Verbose -Message "No CaServer or CaName specified. Performing lookup."
                # hat tip Vadims Podans
                $domain = ([System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()).Name
                $domain = "DC=" + $domain -replace '\.', ", DC="
                $pks = [ADSI]"LDAP://CN=Enrollment Services, CN=Public Key Services, CN=Services, CN=Configuration, $domain"
                $cas = $pks.psBase.Children

                $allCas = @()
                foreach ($ca in $cas) {
                    $allCas += [pscustomobject]@{
                        CA       = $ca | ForEach-Object { $_.Name }
                        Computer = $ca | ForEach-Object { $_.DNSHostName }
                    }
                }
            }
            catch {
                Stop-Function -Message "Cannot access Active Directory or find the Certificate Authority" -ErrorRecord $_
                return
            }

            if (!$CaServer) {
                $CaServer = ($allCas | Select-Object -First 1).Computer
                Write-Message -Level Verbose -Message "Root Server: $CaServer"
            }

            if (!$CaName) {
                $CaName = ($allCas | Select-Object -First 1).CA
                Write-Message -Level Verbose -Message "Root CA name: $CaName"
            }
        }

        $tempDir = ([System.IO.Path]::GetTempPath()).TrimEnd("\")
        $certTemplate = "CertificateTemplate:$CertificateTemplate"
    }

    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($computer in $ComputerName) {

            if (!$secondaryNode) {

                if ($ClusterInstanceName) {
                    if ($ClusterInstanceName -notmatch "\.") {
                        $fqdn = "$ClusterInstanceName.$env:USERDNSDOMAIN"
                    }
                    else {
                        $fqdn = $ClusterInstanceName
                    }
                }
                else {
                    $resolved = Resolve-DbaNetworkName -ComputerName $computer.ComputerName -WarningAction SilentlyContinue

                    if (!$resolved) {
                        $fqdn = "$ComputerName.$env:USERDNSDOMAIN"
                        Write-Message -Level Warning -Message "Server name cannot be resolved. Guessing it's $fqdn"
                    }
                    else {
                        $fqdn = $resolved.fqdn
                    }
                }

                $certDir = "$tempDir\$fqdn"
                $certCfg = "$certDir\request.inf"
                $certCsr = "$certDir\$fqdn.csr"
                $certCrt = "$certDir\$fqdn.crt"
                $certPfx = "$certDir\$fqdn.pfx"
                $tempPfx = "$certDir\temp-$fqdn.pfx"

                if (Test-Path($certDir)) {
                    Write-Message -Level Output -Message "Deleting files from $certDir"
                    $null = Remove-Item "$certDir\*.*"
                }
                else {
                    Write-Message -Level Output -Message "Creating $certDir"
                    $null = New-Item -Path $certDir -ItemType Directory -Force
                }

                # Make sure output is compat with clusters
                $shortName = $fqdn.Split(".")[0]

                if (!$dns) {
                    $dns = $shortName, $fqdn
                }

                $san = Get-SanExt $dns
                # Write config file
                Set-Content $certCfg "[Version]"
                Add-Content $certCfg 'Signature="$Windows NT$"'
                Add-Content $certCfg "[NewRequest]"
                Add-Content $certCfg "Subject = ""CN=$fqdn"""
                Add-Content $certCfg "KeySpec = 1"
                Add-Content $certCfg "KeyLength = $KeyLength"
                Add-Content $certCfg "Exportable = TRUE"
                Add-Content $certCfg "MachineKeySet = TRUE"
                Add-Content $certCfg "FriendlyName=""$FriendlyName"""
                Add-Content $certCfg "SMIME = False"
                Add-Content $certCfg "PrivateKeyArchive = FALSE"
                Add-Content $certCfg "UserProtected = FALSE"
                Add-Content $certCfg "UseExistingKeySet = FALSE"
                Add-Content $certCfg "ProviderName = ""Microsoft RSA SChannel Cryptographic Provider"""
                Add-Content $certCfg "ProviderType = 12"
                if ($SelfSigned) {
                    Add-Content $certCfg "RequestType = Cert"
                }
                else {
                    Add-Content $certCfg "RequestType = PKCS10"
                }
                Add-Content $certCfg "KeyUsage = 0xa0"
                Add-Content $certCfg "[EnhancedKeyUsageExtension]"
                Add-Content $certCfg "OID=1.3.6.1.5.5.7.3.1"
                Add-Content $certCfg "[Extensions]"
                Add-Content $certCfg $san
                Add-Content $certCfg "Critical=2.5.29.17"


                if ($PScmdlet.ShouldProcess("local", "Creating certificate for $computer")) {
                    Write-Message -Level Output -Message "Running: certreq -new $certCfg $certCsr"
                    $create = certreq -new $certCfg $certCsr
                }

                if ($SelfSigned) {
                    $serial = (($create -Split "Serial Number:" -Split "Subject")[2]).Trim() # D:
                    $storedCert = Get-ChildItem Cert:\LocalMachine\My -Recurse | Where-Object SerialNumber -eq $serial

                    if ($computer.IsLocalHost) {
                        $storedCert | Select-Object * | Select-DefaultView -Property FriendlyName, DnsNameList, Thumbprint, NotBefore, NotAfter, Subject, Issuer
                    }
                }
                else {
                    if ($PScmdlet.ShouldProcess("local", "Submitting certificate request for $computer to $CaServer\$CaName")) {
                        Write-Message -Level Output -Message "certreq -submit -config `"$CaServer\$CaName`" -attrib $certTemplate $certCsr $certCrt $certPfx"
                        $submit = certreq -submit -config ""$CaServer\$CaName"" -attrib $certTemplate $certCsr $certCrt $certPfx
                    }

                    if ($submit -match "ssued") {
                        Write-Message -Level Output -Message "certreq -accept -machine $certCrt"
                        $null = certreq -accept -machine $certCrt
                        $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
                        $cert.Import($certCrt, $null, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::DefaultKeySet)
                        $storedCert = Get-ChildItem "Cert:\$store\$folder" -Recurse | Where-Object { $_.Thumbprint -eq $cert.Thumbprint }
                    }
                    elseif ($submit) {
                        Write-Message -Level Warning -Message "Something went wrong"
                        Write-Message -Level Warning -Message "$create"
                        Write-Message -Level Warning -Message "$submit"
                        Stop-Function -Message "Failure when attempting to create the cert on $computer. Exception: $_" -ErrorRecord $_ -Target $computer -Continue
                    }

                    if ($Computer.IsLocalHost) {
                        $storedCert | Select-Object * | Select-DefaultView -Property FriendlyName, DnsNameList, Thumbprint, NotBefore, NotAfter, Subject, Issuer
                    }
                }
            }

            if (!$Computer.IsLocalHost) {

                if (!$secondaryNode) {
                    if ($PScmdlet.ShouldProcess("local", "Generating pfx and reading from disk")) {
                        Write-Message -Level Output -Message "Exporting PFX with password to $tempPfx"
                        $certdata = $storedCert.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::PFX, $password)
                    }

                    if ($PScmdlet.ShouldProcess("local", "Removing cert from disk but keeping it in memory")) {
                        $storedCert | Remove-Item
                    }

                    if ($ClusterInstanceName) { $secondaryNode = $true }
                }

                $scriptblock = {
                    $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
                    $cert.Import($args[0], $args[1], "Exportable,PersistKeySet")

                    $certstore = New-Object System.Security.Cryptography.X509Certificates.X509Store($args[3], $args[2])
                    $certstore.Open('ReadWrite')
                    $certstore.Add($cert)
                    $certstore.Close()
                    Get-ChildItem "Cert:\$($args[2])\$($args[3])" -Recurse | Where-Object { $_.Thumbprint -eq $cert.Thumbprint }
                }

                if ($PScmdlet.ShouldProcess("local", "Connecting to $computer to import new cert")) {
                    try {
                        Write-Message -Level Output -Message "Connecting to $computer"
                        Invoke-Command2 -ComputerName $computer -Credential $Credential -ArgumentList $certdata, $Password, $Store, $Folder -ScriptBlock $scriptblock -ErrorAction Stop |
                            Select-DefaultView -Property DnsNameList, Thumbprint, NotBefore, NotAfter, Subject, Issuer
                    }
                    catch {
                        Stop-Function -Message "Issue importing new cert on $computer" -ErrorRecord $_ -Target $computer -Continue
                    }
                }
            }
            if ($PScmdlet.ShouldProcess("local", "Removing all files from $certDir")) {
                try {
                    Remove-Item -Force -Recurse $certDir -ErrorAction SilentlyContinue
                }
                catch {
                    Stop-Function "Isue removing files from $certDir" -Target $certDir -ErrorRecord $_
                }
            }
        }
    }
}
tools\dbatools\functions\New-DbaCredential.ps1
function New-DbaCredential {
    <#
.SYNOPSIS
Creates a new SQL Server credential

.DESCRIPTION
Creates a new credential

.PARAMETER SqlInstance
The target SQL Server(s)

.PARAMETER SqlCredential
Allows you to login to SQL Server using alternative credentials

.PARAMETER Name
The Credential name

.PARAMETER Identity
The Credential Identity

.PARAMETER Password
Secure string used to authenticate the Credential Identity

.PARAMETER MappedClassType
Sets the class associated with the credential.

.PARAMETER ProviderName
Sets the name of the provider

.PARAMETER Force
If credential exists, drop and recreate

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Certificate

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
New-DbaCredential -SqlInstance Server1

You will be prompted to securely enter your password, then a credential will be created in the master database on server1 if it does not exist.

.EXAMPLE
New-DbaCredential -SqlInstance Server1 -Database db1 -Confirm:$false

Suppresses all prompts to install but prompts to securely enter your password and creates a credential in the 'db1' database

.EXAMPLE
New-DbaCredential -SqlInstance Server1 -Name AzureBackupBlobStore -Identity '<Azure Storage Account Name>' -Password (ConvertTo-SecureString '<Azure Storage Account Access Key>' -AsPlainText -Force)

Create credential on SQL Server 2012 CU2, SQL Server 2014 for use with BACKUP TO URL.
CredentialIdentity needs to be supplied with the Azure Storage Account Name.
Password needs to be one of the Access Keys for the account.

.EXAMPLE
New-DbaCredential -SqlInstance Server1 -Name 'https://<Azure Storage Account Name>.blob.core.windows.net/<Blob Store Container Name>' -Identity 'SHARED ACCESS SIGNATURE' -Password (ConvertTo-SecureString '<Shared Access Token>' -AsPlainText -Force)

Create Credential on SQL Server 2016 or higher for use with BACKUP TO URL.
Name has to be the full URL for the blob store container that will be the backup target.
Password needs to be passed the Shared Access Token (SAS Key).

#>
    [CmdletBinding(SupportsShouldProcess = $true)] #, ConfirmImpact = "High"
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Name = $Identity,
        [parameter(Mandatory)]
        [Alias("CredentialIdentity")]
        [string[]]$Identity,
        [Security.SecureString]$Password,
        [ValidateSet('CryptographicProvider', 'None')]
        [string]$MappedClassType = "None",
        [string]$ProviderName,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $mappedclass = switch ($MappedClassType) {
            "CryptographicProvider" { 1 }
            "None" { 0 }
        }
    }

    process {
        if (!$Password) {
            Read-Host -AsSecureString -Prompt "Enter the credential password"
        }

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            foreach ($cred in $Identity) {
                $currentcred = $server.Credentials[$name]

                if ($currentcred) {
                    if ($force) {
                        Write-Message -Level Verbose -Message "Dropping credential $name"
                        $currentcred.Drop()
                    }
                    else {
                        Stop-Function -Message "Credential exists and Force was not specified" -Target $name -Continue
                    }
                }


                if ($Pscmdlet.ShouldProcess($SqlInstance, "Creating credential for database '$cred' on $instance")) {
                    try {
                        $credential = New-Object Microsoft.SqlServer.Management.Smo.Credential -ArgumentList $server, $name
                        $credential.MappedClassType = $mappedclass
                        $credential.ProviderName = $ProviderName
                        $credential.Create($Identity, $Password)

                        Add-Member -Force -InputObject $credential -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                        Add-Member -Force -InputObject $credential -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                        Add-Member -Force -InputObject $credential -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName

                        Select-DefaultView -InputObject $credential -Property ComputerName, InstanceName, SqlInstance, Name, Identity, CreateDate, MappedClassType, ProviderName
                    }
                    catch {
                        Stop-Function -Message "Failed to create credential in $cred on $instance. Exception: $($_.Exception.InnerException)" -Target $credential -InnerErrorRecord $_ -Continue
                    }
                }
            }
        }
    }
}
tools\dbatools\functions\New-DbaDatabaseMasterKey.ps1
function New-DbaDatabaseMasterKey {
    <#
.SYNOPSIS
Creates a new database master key

.DESCRIPTION
Creates a new database master key. If no database is specified, the master key will be created in master.

.PARAMETER SqlInstance
The SQL Server to create the certificates on.

.PARAMETER SqlCredential
Allows you to login to SQL Server using alternative credentials.

.PARAMETER Database
The database where the master key will be created. Defaults to master.

.PARAMETER Password
Secure string used to create the key.

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Certificate

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
New-DbaDatabaseMasterKey -SqlInstance Server1

You will be prompted to securely enter your password, then a master key will be created in the master database on server1 if it does not exist.

.EXAMPLE
New-DbaDatabaseMasterKey -SqlInstance Server1 -Database db1 -Confirm:$false

Suppresses all prompts to install but prompts to securely enter your password and creates a master key in the 'db1' database


#>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High")]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Database = "master",
        [parameter(Mandatory)]
        [Security.SecureString]$Password = (Read-Host "Password" -AsSecureString),
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            foreach ($db in $Database) {
                $smodb = $server.Databases[$db]

                if ($null -eq $smodb) {
                    Stop-Function -Message "Database '$db' does not exist on $instance" -Target $smodb -Continue
                }

                if ($null -ne $smodb.MasterKey) {
                    Stop-Function -Message "Master key already exists in the $db database on $instance" -Target $smodb -Continue
                }

                if ($Pscmdlet.ShouldProcess($SqlInstance, "Creating master key for database '$db' on $instance")) {
                    try {
                        $masterkey = New-Object Microsoft.SqlServer.Management.Smo.MasterKey $smodb
                        $masterkey.Create(([System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($password))))

                        Add-Member -Force -InputObject $masterkey -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                        Add-Member -Force -InputObject $masterkey -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                        Add-Member -Force -InputObject $masterkey -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                        Add-Member -Force -InputObject $masterkey -MemberType NoteProperty -Name Database -value $smodb

                        Select-DefaultView -InputObject $masterkey -Property ComputerName, InstanceName, SqlInstance, Database, CreateDate, DateLastModified, IsEncryptedByServer
                    }
                    catch {
                        Stop-Function -Message "Failed to create master key in $db on $instance. Exception: $($_.Exception.InnerException)" -Target $masterkey -InnerErrorRecord $_ -Continue
                    }
                }
            }
        }
    }
}
tools\dbatools\functions\New-DbaDbCertificate.ps1
function New-DbaDbCertificate {
    <#
.SYNOPSIS
Creates a new database certificate

.DESCRIPTION
Creates a new database certificate. If no database is specified, the certificate will be created in master.

.PARAMETER SqlInstance
The SQL Server to create the certificates on.

.PARAMETER SqlCredential
Allows you to login to SQL Server using alternative credentials.

.PARAMETER Database
The database where the certificate will be created. Defaults to master.

.PARAMETER Name
Optional name to create the certificate. Defaults to database name.

.PARAMETER Subject
Optional subject to create the certificate.

.PARAMETER StartDate
Optional secure string used to create the certificate.

.PARAMETER ExpirationDate
Optional secure string used to create the certificate.

.PARAMETER ActiveForServiceBrokerDialog
Optional secure string used to create the certificate.

.PARAMETER Password
Optional password - if no password is supplied, the password will be protected by the master key

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Certificate

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
New-DbaDbCertificate -SqlInstance Server1

You will be prompted to securely enter your password, then a certificate will be created in the master database on server1 if it does not exist.

.EXAMPLE
New-DbaDbCertificate -SqlInstance Server1 -Database db1 -Confirm:$false

Suppresses all prompts to install but prompts to securely enter your password and creates a certificate in the 'db1' database
#>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$Name,
        [object[]]$Database = "master",
        [string[]]$Subject,
        [datetime]$StartDate = (Get-Date),
        [datetime]$ExpirationDate = $StartDate.AddYears(5),
        [switch]$ActiveForServiceBrokerDialog,
        [Security.SecureString]$Password,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias New-DbaDatabaseCertificate
    }
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            foreach ($db in $Database) {

                $currentdb = $server.Databases[$db] | Where-Object IsAccessible

                if ($null -eq $currentdb) {
                    Stop-Function -Message "Database '$db' does not exist on $instance" -Target $server -Continue
                }

                if ($null -eq $name) {
                    Write-Message -Level Verbose -Message "Name is NULL, setting it to '$db'"
                    $name = $db
                }
                if ($null -eq $subject) {
                    Write-Message -Level Verbose -Message "Subject is NULL, setting it to '$db Database Certificate'"
                    $subject = "$db Database Certificate"
                }

                foreach ($cert in $name) {
                    if ($null -ne $currentdb.Certificates[$cert]) {
                        Stop-Function -Message "Certificate '$cert' already exists in the $db database on $instance" -Target $currentdb -Continue
                    }

                    if ($Pscmdlet.ShouldProcess($SqlInstance, "Creating certificate for database '$db' on $instance")) {
                        try {
                            $smocert = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Certificate $currentdb, $cert

                            $smocert.StartDate = $StartDate
                            $smocert.Subject = $Subject
                            $smocert.ExpirationDate = $ExpirationDate
                            $smocert.ActiveForServiceBrokerDialog = $ActiveForServiceBrokerDialog

                            if ($password.Length -gt 0) {
                                $smocert.Create(([System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($password))))
                            }
                            else {
                                $smocert.Create()
                            }

                            Add-Member -Force -InputObject $smocert -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
                            Add-Member -Force -InputObject $smocert -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
                            Add-Member -Force -InputObject $smocert -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
                            Add-Member -Force -InputObject $smocert -MemberType NoteProperty -Name Database -value $currentdb.Name

                            Select-DefaultView -InputObject $smocert -Property ComputerName, InstanceName, SqlInstance, Database, Name, Subject, StartDate, ActiveForServiceBrokerDialog, ExpirationDate, Issuer, LastBackupDate, Owner, PrivateKeyEncryptionType, Serial
                        }
                        catch {
                            if ($_.Exception.InnerException) {
                                $exception = $_.Exception.InnerException.ToString() -Split "System.Data.SqlClient.SqlException: "
                                $exception = ($exception[1] -Split "at Microsoft.SqlServer.Management.Common.ConnectionManager")[0]
                            }
                            else {
                                $exception = $_.Exception
                            }

                            Stop-Function -Message "Failed to create certificate in $db on $instance. Exception: $exception" -Target $smocert -InnerErrorRecord $_ -Continue
                        }
                    }
                }
            }
        }
    }
}
tools\dbatools\functions\New-DbaDbSnapshot.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function New-DbaDbSnapshot {
    <#
    .SYNOPSIS
        Creates database snapshots

    .DESCRIPTION
        Creates database snapshots without hassles

    .PARAMETER SqlInstance
        The SQL Server that you're connecting to.

    .PARAMETER SqlCredential
        Credential object used to connect to the SQL Server as a different user

    .PARAMETER AllDatabases
        Creates snapshot for all eligible databases

    .PARAMETER Database
        The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

    .PARAMETER ExcludeDatabase
        The database(s) to exclude - this list is auto-populated from the server

    .PARAMETER WhatIf
        Shows what would happen if the command were to run

    .PARAMETER Confirm
        Prompts for confirmation of every step.

    .PARAMETER Name
        The specific snapshot name you want to create. Works only if you target a single database. If you need to create multiple snapshot,
        you must use the NameSuffix parameter

    .PARAMETER NameSuffix
        When you pass a simple string, it'll be appended to use it to build the name of the snapshot. By default snapshots are created with yyyyMMdd_HHmmss suffix
        You can also pass a standard placeholder, in which case it'll be interpolated (e.g. '{0}' gets replaced with the database name)

    .PARAMETER Path
        Snapshot files will be created here (by default the filestructure will be created in the same folder as the base db)

    .PARAMETER InputObject
       Allows Piping from Get-DbaDatabase

    .PARAMETER Force
        Databases with Filestream FG can be snapshotted, but the Filestream FG is marked offline
        in the snapshot. To create a "partial" snapshot, you need to pass -Force explicitely

        NB: You can't then restore the Database from the newly-created snapshot.
        For details, check https://msdn.microsoft.com/en-us/library/bb895334.aspx

    .PARAMETER EnableException
                By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
                This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
                Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Tags: Snapshot, Restore, Database
        Author: niphlod

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
         https://dbatools.io/New-DbaDbSnapshot

    .EXAMPLE
        New-DbaDbSnapshot -SqlInstance sqlserver2014a -Database HR, Accounting

        Creates snapshot for HR and Accounting, returning a custom object displaying Server, Database, DatabaseCreated, SnapshotOf, SizeMB, DatabaseCreated, PrimaryFilePath, Status, Notes

    .EXAMPLE
        New-DbaDbSnapshot -SqlInstance sqlserver2014a -Database HR -Name HR_snap

        Creates snapshot named "HR_snap" for HR

    .EXAMPLE
        New-DbaDbSnapshot -SqlInstance sqlserver2014a -Database HR -NameSuffix 'fool_{0}_snap'

        Creates snapshot named "fool_HR_snap" for HR

    .EXAMPLE
        New-DbaDbSnapshot -SqlInstance sqlserver2014a -Database HR, Accounting -Path F:\snapshotpath

        Creates snapshots for HR and Accounting databases, storing files under the F:\snapshotpath\ dir

    .EXAMPLE
        Get-DbaDatabase -SqlInstance sql2016 -Database df | New-DbaDbSnapshot

        Creates a snapshot for the database df on sql2016
#>

    [CmdletBinding(SupportsShouldProcess)]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$AllDatabases,
        [string]$Name,
        [string]$NameSuffix,
        [string]$Path,
        [switch]$Force,
        [parameter(ValueFromPipeline)]
        [Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {

        $NoSupportForSnap = @('model', 'master', 'tempdb')
        # Evaluate the default suffix here for naming consistency
        $DefaultSuffix = (Get-Date -Format "yyyyMMdd_HHmmss")
        if ($NameSuffix.Length -gt 0) {
            #Validate if Name can be interpolated
            try {
                $null = $NameSuffix -f 'some_string'
            }
            catch {
                Stop-Function -Message "NameSuffix parameter must be a template only containing one parameter {0}" -ErrorRecord $_
            }
        }

        function Resolve-SnapshotError($server) {
            $errhelp = ''
            $CurrentEdition = $server.Edition.toLower()
            $CurrentVersion = $server.Version.Major * 1000000 + $server.Version.Minor * 10000 + $server.Version.Build
            if ($server.Version.Major -lt 9) {
                $errhelp = 'Not supported before 2005'
            }
            if ($CurrentVersion -lt 12002000 -and $errhelp.Length -eq 0) {
                if ($CurrentEdition -notmatch '.*enterprise.*|.*developer.*|.*datacenter.*') {
                    $errhelp = 'Supported only for Enterprise, Developer or Datacenter editions'
                }
            }
            $message = ""
            if ($errhelp.Length -gt 0) {
                $message += "Please make sure your version supports snapshots : ($errhelp)"
            }
            else {
                $message += "This module can't tell you why the snapshot creation failed. Feel free to report back to dbatools what happened"
            }
            Write-Message -Level Warning -Message $message
        }
    }
    process {
        if (-not $InputObject -and -not $Database -and $AllDatabases -eq $false) {
            Stop-Function -Message "You must specify a -AllDatabases or -Database to continue" -EnableException $EnableException
            return
        }

        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            #Checks for path existence, left the length test because test-bound wasn't working for some reason
            if ($Path.Length -gt 0) {
                if (!(Test-DbaSqlPath -SqlInstance $instance -Path $Path)) {
                    Stop-Function -Message "$instance cannot access the directory $Path" -ErrorRecord $_ -Target $instance -Continue -EnableException $EnableException
                }
            }

            if ($AllDatabases) {
                $dbs = $server.Databases
            }

            if ($Database) {
                $dbs = $server.Databases | Where-Object { $Database -contains $_.Name }
            }

            if ($ExcludeDatabase) {
                $dbs = $server.Databases | Where-Object { $ExcludeDatabase -notcontains $_.Name }
            }

            ## double check for gotchas
            foreach ($db in $dbs) {
                if ($db.IsDatabaseSnapshot) {
                    Write-Message -Level Warning -Message "$($db.name) is a snapshot, skipping"
                }
                elseif ($db.name -in $NoSupportForSnap) {
                    Write-Message -Level Warning -Message "$($db.name) snapshots are prohibited"
                }
                elseif ($db.IsAccessible -ne $true) {
                    Write-Message -Level Verbose -Message "$($db.name) is not accessible, skipping"
                }
                else {
                    $InputObject += $db
                }
            }

            if ($InputObject.Length -gt 1 -and $Name) {
                Stop-Function -Message "You passed the Name parameter that is fixed but selected multiple databases to snapshot: use the NameSuffix parameter" -Continue -EnableException $EnableException
            }
        }

        foreach ($db in $InputObject) {
            $server = $db.Parent

            # In case stuff is piped in
            if ($server.VersionMajor -lt 9) {
                Stop-Function -Message "SQL Server version 9 required - $server not supported" -Continue
            }

            if ($NameSuffix.Length -gt 0) {
                $SnapName = $NameSuffix -f $db.Name
                if ($SnapName -eq $NameSuffix) {
                    #no interpolation, just append
                    $SnapName = '{0}{1}' -f $db.Name, $NameSuffix
                }
            }
            elseif ($Name.Length -gt 0) {
                $SnapName = $Name
            }
            else {
                $SnapName = "{0}_{1}" -f $db.Name, $DefaultSuffix
            }
            if ($SnapName -in $server.Databases.Name) {
                Write-Message -Level Warning -Message "A database named $Snapname already exists, skipping"
                continue
            }
            $all_FSD = $db.FileGroups | Where-Object FileGroupType -eq 'FileStreamDataFileGroup'
            $all_MMO = $db.FileGroups | Where-Object FileGroupType -eq 'MemoryOptimizedDataFileGroup'
            $has_FSD = $all_FSD.Count -gt 0
            $has_MMO = $all_MMO.Count -gt 0
            if ($has_MMO) {
                Write-Message -Level Warning -Message "MEMORY_OPTIMIZED_DATA detected, snapshots are not possible"
                continue
            }
            if ($has_FSD -and $Force -eq $false) {
                Write-Message -Level Warning -Message "Filestream detected, skipping. You need to specify -Force. See Get-Help for details"
                continue
            }
            $snaptype = "db snapshot"
            if ($has_FSD) {
                $snaptype = "partial db snapshot"
            }
            If ($Pscmdlet.ShouldProcess($server, "Create $snaptype $SnapName of $($db.Name)")) {
                $CustomFileStructure = @{ }
                $counter = 0
                foreach ($fg in $db.FileGroups) {
                    $CustomFileStructure[$fg.Name] = @()
                    if ($fg.FileGroupType -eq 'FileStreamDataFileGroup') {
                        Continue
                    }
                    foreach ($file in $fg.Files) {
                        $counter += 1
                        $basename = [IO.Path]::GetFileNameWithoutExtension($file.FileName)
                        $basepath = Split-Path $file.FileName -Parent
                        # change path if specified
                        if ($Path.Length -gt 0) {
                            $basepath = $Path
                        }
                        # we need to avoid cases where basename is the same for multiple FG
                        $fname = [IO.Path]::Combine($basepath, ("{0}_{1}_{2:0000}_{3:000}" -f $basename, $DefaultSuffix, (Get-Date).MilliSecond, $counter))
                        # fixed extension is hardcoded as "ss", which seems a "de-facto" standard
                        $fname = [IO.Path]::ChangeExtension($fname, "ss")
                        $CustomFileStructure[$fg.Name] += @{ 'name' = $file.name; 'filename' = $fname }
                    }
                }

                $SnapDB = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Database -ArgumentList $server, $Snapname
                $SnapDB.DatabaseSnapshotBaseName = $db.Name

                foreach ($fg in $CustomFileStructure.Keys) {
                    $SnapFG = New-Object -TypeName Microsoft.SqlServer.Management.Smo.FileGroup $SnapDB, $fg
                    $SnapDB.FileGroups.Add($SnapFG)
                    foreach ($file in $CustomFileStructure[$fg]) {
                        $SnapFile = New-Object -TypeName Microsoft.SqlServer.Management.Smo.DataFile $SnapFG, $file['name'], $file['filename']
                        $SnapDB.FileGroups[$fg].Files.Add($SnapFile)
                    }
                }

                # we're ready to issue a Create, but SMO is a little uncooperative here
                # there are cases we can manage and others we can't, and we need all the
                # info we can get both from testers and from users

                $sql = $SnapDB.Script()

                try {
                    $SnapDB.Create()
                    $server.Databases.Refresh()
                    Get-DbaDbSnapshot -SqlInstance $server -Snapshot $Snapname
                }
                catch {
                    try {
                        $server.Databases.Refresh()
                        if ($SnapName -notin $server.Databases.Name) {
                            # previous creation failed completely, snapshot is not there already
                            $null = $server.Query($sql[0])
                            $server.Databases.Refresh()
                            $SnapDB = Get-DbaDbSnapshot -SqlInstance $server -Snapshot $Snapname
                        }
                        else {
                            $SnapDB = Get-DbaDbSnapshot -SqlInstance $server -Snapshot $Snapname
                        }

                        $Notes = @()
                        if ($db.ReadOnly -eq $true) {
                            $Notes += 'SMO is probably trying to set a property on a read-only snapshot, run with -Debug to find out and report back'
                        }
                        if ($has_FSD) {
                            $Status = 'Partial'
                            $Notes += 'Filestream groups are not viable for snapshot'
                        }
                        $Notes = $Notes -Join ';'

                        $hints = @("Executing these commands led to a partial failure")
                        foreach ($stmt in $sql) {
                            $hints += $stmt
                        }

                        Write-Message -Level Debug -Message ($hints -Join "`n")

                        $SnapDB
                    }
                    catch {
                        # Resolve-SnapshotError $server
                        $hints = @("Executing these commands led to a failure")
                        foreach ($stmt in $sql) {
                            $hints += $stmt
                        }
                        Write-Message -Level Debug -Message ($hints -Join "`n")

                        Stop-Function -Message "Failure" -ErrorRecord $_ -Target $SnapDB -Continue
                    }
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias New-DbaDatabaseSnapshot
    }
}
tools\dbatools\functions\New-DbaDbUser.ps1
function New-DbaDbUser {
    <#
        .SYNOPSIS
            Creates a new user for the specified database.

        .DESCRIPTION
            Creates a new user for a specified database with provided specifications.

        .PARAMETER SqlInstance
            The target SQL Server instance. Defaults to the default instance on localhost.

        .PARAMETER SqlCredential
            Allows you to login to servers using SQL Logins instead of Windows Authentication (AKA Integrated or Trusted). To use:

            $scred = Get-Credential, then pass $scred object to the -SqlCredential parameter.

            Windows Authentication will be used if SqlCredential is not specified. SQL Server does not accept Windows credentials being passed as credentials.

            To connect to SQL Server as a different Windows user, run PowerShell as that user.

        .PARAMETER Database
            Specifies the database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies the database(s) to exclude from processing. Options for this list are auto-populated from the server. By default, system databases are excluded.

        .PARAMETER IncludeSystem
            If this switch is enabled, the user will be added to system databases.

        .PARAMETER Login
            When specified, the user will be associated to this SQL login and have the same name as the Login.

        .PARAMETER Username
            When specified, the user will have this name.

        .PARAMETER Force
            If user exists, drop and recreate.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database, User
            Author: Frank Henninger (@osiris687)
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/New-DbaDbUser

        .EXAMPLE
        New-DbaDbUser -SqlInstance sqlserver2014 -Database DB1 -Login user1

        Creates a new sql user with login named user1 in the specified database.

        .EXAMPLE
        New-DbaDbUser -SqlInstance sqlserver2014 -Database DB1 -Username user1

        Creates a new sql user without login named user1 in the specified database.

        .EXAMPLE
        New-DbaDbUser -SqlInstance sqlserver2014 -Database DB1 -Login Login1 -Username user1

        Creates a new sql user named user1 mapped to Login1 in the specified database.

        .EXAMPLE
        Get-DbaDatabaseUser -SqlInstance sqlserver1 -Database DB1 | New-DbaDbUser -SqlInstance sqlserver2 -Database DB1

        Copies users from sqlserver1.DB1 to sqlserver2.DB1. Does not copy permissions!
    #>
    [CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = "NoLogin")]
    param(
        [parameter(Mandatory, Position = 1)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$IncludeSystem,
        [parameter(ParameterSetName = "Login")]
        [string[]]$Login,
        [parameter(ParameterSetName = "NoLogin")]
        [parameter(ParameterSetName = "Login")]
        [string[]]$Username,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        function Test-SqlLoginInDatabase {
            param(
                [Microsoft.SqlServer.Management.Smo.Login]$Login,
                [Microsoft.SqlServer.Management.Smo.Database]$Database
            )

            # Does user exist with same login?
            if ( $existingUser = ( $Database.Users | Where-Object Login -eq $smoLogin ) ) {
                if (Test-Bound 'Force') {
                    if ($Pscmdlet.ShouldProcess($existingUser, "Dropping existing user $($existingUser.Name) because -Force was used")) {
                        try {
                            $existingUser.Drop()
                        }
                        catch {
                            Stop-Function -Message "Could not remove existing user $($existingUser.Name), skipping." -Target $existingUser -ErrorRecord $_ -Exception $_.Exception.InnerException.InnerException.InnerException -Continue
                        }
                    }
                }
                else {
                    Stop-Function -Message "User $($existingUser.Name) already exists and -Force was not specified" -Target $existingUser -Continue
                }
            }
        }

        function Test-SqlUserInDatabase {
            param(
                [string[]]$Username,
                [Microsoft.SqlServer.Management.Smo.Database]$Database
            )

            # Does user exist with same login?
            if ( $existingUser = ( $Database.Users | Where-Object Name -eq $Username ) ) {
                if (Test-Bound 'Force') {
                    if ($Pscmdlet.ShouldProcess($existingUser, "Dropping existing user $($existingUser.Name) because -Force was used")) {
                        try {
                            $existingUser.Drop()
                        }
                        catch {
                            Stop-Function -Message "Could not remove existing user $($existingUser.Name), skipping." -Target $existingUser -ErrorRecord $_ -Exception $_.Exception.InnerException.InnerException.InnerException -Continue
                        }
                    }
                }
                else {
                    Stop-Function -Message "User $($existingUser.Name) already exists and -Force was not specified" -Target $existingUser -Continue
                }
            }
        }
    }

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $databases = $server.Databases | Where-Object IsAccessible -eq $true

            if ($Database) {
                $databases = $databases | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }
            if (Test-Bound 'IncludeSystem' -Not) {
                $databases = $databases | Where-Object IsSystemObject -NE $true
            }

            foreach ($db in $databases) {
                Write-Message -Level Verbose -Message "Add users to Database $db on target $server"

                switch -Wildcard ($PSCmdlet.ParameterSetName) {
                    "Login*" {
                        # Creates a user with Login
                        Write-Message -Level VeryVerbose -Message "Using UserType: SqlLogin"

                        if ($PSBoundParameters.Keys -notcontains 'Login') {
                            Stop-Function -Message "Parameter -Login is required " -Target $instance
                        }
                        if ($Login.GetType().Name -eq 'Login') {
                            $smoLogin = $Login
                        }
                        else {
                            #get the login associated with the given name.
                            $smoLogin = $server.Logins | Where-Object Name -eq $Login
                            if ($smoLogin -eq $null) {
                                Stop-Function -Message "Invalid Login: $Login is not found on $Server" -Target $instance;
                                return
                            }
                        }

                        Test-SqlLoginInDatabase -Database $db -Login $smoLogin

                        if ( $PSCmdlet.ParameterSetName -eq "LoginWithNewUsername" ) {
                            $Name = $Username
                            Write-Message -Level Verbose -Message "Using UserName: $Username"
                        }
                        else {
                            $Name = $smoLogin.Name
                            Write-Message -Level Verbose -Message "Using LoginName: $Name"
                        }

                        $Login = $smoLogin
                        $UserType = [Microsoft.SqlServer.Management.Smo.UserType]::SqlLogin
                    }

                    "NoLogin" {
                        # Creates a user without login
                        Write-Message -Level Verbose -Message "Using UserType: NoLogin"
                        $UserType = [Microsoft.SqlServer.Management.Smo.UserType]::NoLogin
                        $Name = $Username
                    }
                } #switch

                # Does user exist with same name?
                Test-SqlUserInDatabase -Database $db -Username $Name

                if ($Pscmdlet.ShouldProcess($db, "Creating user $Name")) {
                    try {
                        $smoUser = New-Object Microsoft.SqlServer.Management.Smo.User
                        $smoUser.Parent = $db
                        $smoUser.Name = $Name

                        if ( $PSBoundParameters.Keys -contains 'Login' -and $Login.GetType().Name -eq 'Login' ) {
                            $smoUser.Login = Login
                        }
                        $smoUser.UserType = $UserType

                        $smoUser.Create()
                    }
                    catch {
                        Stop-Function -Message "Failed to add user $Name in $db to $instance"  -Category InvalidOperation -ErrorRecord $_ -Target $instance -Continue
                    }
                    $smoUser.Refresh()

                    if ( $PSBoundParameters.Keys -contains 'Username' -and $smoUser.Name -ne $Username ) {
                        $smoUser.Rename($Username)
                    }

                    Write-Message -Level Verbose -Message "Successfully added $smoUser in $db to $instance."
                }

                #Display Results
                Get-DbaDatabaseUser -SqlInstance $server.Name -Database $db.Name | Where-Object name -eq $smoUser.Name
            } #foreach ($db in $databases)
        } #foreach ($instance in $SqlInstance)
    }
}
tools\dbatools\functions\New-DbaLogin.ps1
function New-DbaLogin {
    <#
    .SYNOPSIS
    Creates a new SQL Server login

    .DESCRIPTION
    Creates a new SQL Server login with provided specifications

    .PARAMETER SqlInstance
    The target SQL Server(s)

    .PARAMETER SqlCredential
    Allows you to login to SQL Server using alternative credentials

    .PARAMETER Login
    The Login name(s)

    .PARAMETER Password
    Secure string used to authenticate the Login

    .PARAMETER HashedPassword
    Hashed password string used to authenticate the Login

    .PARAMETER InputObject
    Takes the parameters required from a Login object that has been piped into the command

    .PARAMETER LoginRenameHashtable
    Pass a hash table into this parameter to change login names when piping objects into the procedure

    .PARAMETER MapToCertificate
    Map the login to a certificate

    .PARAMETER MapToAsymmetricKey
    Map the login to an asymmetric key

    .PARAMETER MapToCredential
    Map the login to a credential

    .PARAMETER Sid
    Provide an explicit Sid that should be used when creating the account. Can be [byte[]] or hex [string] ('0xFFFF...')

    .PARAMETER DefaultDatabase
    Default database for the login

    .PARAMETER Language
    Login's default language

    .PARAMETER PasswordExpiration
    Enforces password expiration policy. Requires PasswordPolicy to be enabled. Can be $true or $false(default)

    .PARAMETER PasswordPolicy
    Enforces password complexity policy. Can be $true or $false(default)

    .PARAMETER Disabled
    Create the login in a disabled state

    .PARAMETER NewSid
    Ignore sids from the piped login object to generate new sids on the server. Useful when copying login onto the same server

    .PARAMETER Force
    If login exists, drop and recreate

    .PARAMETER WhatIf
    Shows what would happen if the command were to run. No actions are actually performed

    .PARAMETER Confirm
    Prompts you for confirmation before executing any changing operations within the command

    .PARAMETER EnableException
    By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
    Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
    Tags: Login
    Author: Kirill Kravtsov (@nvarscar)
    dbatools PowerShell module (https://dbatools.io, [email protected])
    Copyright (C) 2016 Chrissy LeMaire
    License: MIT https://opensource.org/licenses/MIT

    .LINK
    https://dbatools.io/New-DbaLogin

    .EXAMPLE
    New-DbaLogin -SqlInstance Server1,Server2 -Login Newlogin

    You will be prompted to securely enter the password for a login [Newlogin]. The login would be created on servers Server1 and Server2 with default parameters.

    .EXAMPLE
    $securePassword = Read-Host "Input password" -AsSecureString
    New-DbaLogin -SqlInstance Server1\sql1 -Login Newlogin -Password $securePassword -PasswordPolicy -PasswordExpiration

    Creates a login on Server1\sql1 with a predefined password. The login will have password and expiration policies enforced onto it.

    .EXAMPLE
    Get-DbaLogin -SqlInstance sql1 -Login Oldlogin | New-DbaLogin -SqlInstance sql1 -LoginRenameHashtable @{Oldlogin = 'Newlogin'} -Force -NewSid -Disabled:$false

    Copies a login [Oldlogin] to the same instance sql1 with the same parameters (including password). New login will have a new sid, a new name [Newlogin] and will not be disabled. Existing login [Newlogin] will be removed prior to creation.

    .EXAMPLE
    Get-DbaLogin -SqlInstance sql1 -Login Login1,Login2 | New-DbaLogin -SqlInstance sql2 -PasswordPolicy -PasswordExpiration -DefaultDatabase tempdb -Disabled

    Copies logins [Login1] and [Login2] from instance sql1 to instance sql2, but enforces password and expiration policies for the new logins. New logins will also have a default database set to [tempdb] and will be created in a disabled state.
#>
    [CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = "Password")]
    param (
        [parameter(Mandatory, Position = 1)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Name", "LoginName")]
        [parameter(ParameterSetName = "Password", Position = 2)]
        [parameter(ParameterSetName = "PasswordHash")]
        [parameter(ParameterSetName = "MapToCertificate")]
        [parameter(ParameterSetName = "MapToAsymmetricKey")]
        [string[]]$Login,
        [parameter(ValueFromPipeline = $true)]
        [parameter(ParameterSetName = "Password")]
        [parameter(ParameterSetName = "PasswordHash")]
        [parameter(ParameterSetName = "MapToCertificate")]
        [parameter(ParameterSetName = "MapToAsymmetricKey")]
        [object[]]$InputObject,
        [Alias("Rename")]
        [hashtable]$LoginRenameHashtable,
        [parameter(ParameterSetName = "Password", Position = 3)]
        [Security.SecureString]$Password,
        [Alias("Hash", "PasswordHash")]
        [parameter(ParameterSetName = "PasswordHash")]
        [string]$HashedPassword,
        [parameter(ParameterSetName = "MapToCertificate")]
        [string]$MapToCertificate,
        [parameter(ParameterSetName = "MapToAsymmetricKey")]
        [string]$MapToAsymmetricKey,
        [string]$MapToCredential,
        [object]$Sid,
        [Alias("DefaulDB")]
        [parameter(ParameterSetName = "Password")]
        [parameter(ParameterSetName = "PasswordHash")]
        [string]$DefaultDatabase,
        [parameter(ParameterSetName = "Password")]
        [parameter(ParameterSetName = "PasswordHash")]
        [string]$Language,
        [Alias("Expiration", "CheckExpiration")]
        [parameter(ParameterSetName = "Password")]
        [parameter(ParameterSetName = "PasswordHash")]
        [switch]$PasswordExpiration,
        [Alias("Policy", "CheckPolicy")]
        [parameter(ParameterSetName = "Password")]
        [parameter(ParameterSetName = "PasswordHash")]
        [switch]$PasswordPolicy,
        [Alias("Disable")]
        [switch]$Disabled,
        [switch]$NewSid,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        if ($Sid) {
            if ($Sid.GetType().Name -ne 'Byte[]') {
                foreach ($symbol in $Sid.TrimStart("0x").ToCharArray()) {
                    if ($symbol -notin "0123456789ABCDEF".ToCharArray()) {
                        Stop-Function -Message "Sid has invalid character '$symbol', cannot proceed." -Category InvalidArgument -EnableException $EnableException
                        return
                    }
                }
                $Sid = Convert-HexStringToByte $Sid
            }
        }

        if ($HashedPassword) {
            if ($HashedPassword.GetType().Name -eq 'Byte[]') {
                $HashedPassword = Convert-ByteToHexString $HashedPassword
            }
        }
    }

    process {
        #At least one of those should be specified
        if (!($Login -or $InputObject)) {
            Stop-Function -Message "No logins have been specified." -Category InvalidArgument -EnableException $EnableException
            Return
        }

        $loginCollection = @()
        if ($InputObject) {
            $loginCollection += $InputObject
            if ($Login) {
                Stop-Function -Message "Parameter -Login is not supported when processing objects from -InputObject. If you need to rename the logins, please use -LoginRenameHashtable." -Category InvalidArgument -EnableException $EnableException
                Return
            }
        }
        else {
            $loginCollection += $Login
            $Login | ForEach-Object {
                if ($_.IndexOf('\') -eq -1 -and $PsCmdlet.ParameterSetName -like "Password*" -and !($Password -or $HashedPassword)) {
                    $passwordNotSpecified = $true
                }
            }
        }
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            foreach ($loginItem in $loginCollection) {
                #check if $loginItem is an SMO Login object
                if ($loginItem.GetType().Name -eq 'Login') {
                    #Get all the necessary fields
                    $loginName = $loginItem.Name
                    $loginType = $loginItem.LoginType
                    $currentSid = $loginItem.Sid
                    $currentDefaultDatabase = $loginItem.DefaultDatabase
                    $currentLanguage = $loginItem.Language
                    $currentPasswordExpiration = $loginItem.PasswordExpiration
                    $currentPasswordPolicyEnforced = $loginItem.PasswordPolicyEnforced
                    $currentDisabled = $loginItem.IsDisabled

                    #Get previous password
                    if ($loginType -eq 'SqlLogin' -and !($Password -or $HashedPassword)) {
                        $sourceServer = $loginItem.Parent
                        switch ($sourceServer.versionMajor) {
                            0 { $sql = "SELECT CONVERT(VARBINARY(256),password) as hashedpass FROM master.dbo.syslogins WHERE loginname='$loginName'" }
                            8 { $sql = "SELECT CONVERT(VARBINARY(256),password) as hashedpass FROM dbo.syslogins WHERE name='$loginName'" }
                            9 { $sql = "SELECT CONVERT(VARBINARY(256),password_hash) as hashedpass FROM sys.sql_logins where name='$loginName'" }
                            default {
                                $sql = "SELECT CAST(CONVERT(VARCHAR(256), CAST(LOGINPROPERTY(name,'PasswordHash')
                                    AS VARBINARY(256)), 1) AS NVARCHAR(max)) AS hashedpass
                                    FROM sys.server_principals
                                    WHERE principal_id = $($loginItem.id)"
                            }
                        }

                        try {
                            $hashedPass = $sourceServer.ConnectionContext.ExecuteScalar($sql)
                        }
                        catch {
                            $hashedPassDt = $sourceServer.Databases['master'].ExecuteWithResults($sql)
                            $hashedPass = $hashedPassDt.Tables[0].Rows[0].Item(0)
                        }

                        if ($hashedPass.GetType().Name -ne "String") {
                            $hashedPass = Convert-ByteToHexString $hashedPass
                        }
                        $currentHashedPassword = $hashedPass
                    }

                    #Get cryptography and attached credentials
                    if ($loginType -eq 'AsymmetricKey') {
                        $currentAsymmetricKey = $loginItem.AsymmetricKey
                    }
                    if ($loginType -eq 'Certificate') {
                        $currentCertificate = $loginItem.Certificate
                    }
                    #This method or property is accessible only while working with SQL Server 2008 or later.
                    if ($sourceServer.versionMajor -gt 9) {
                        if ($loginItem.EnumCredentials()) {
                            $currentCredential = $loginItem.EnumCredentials()
                        }
                    }
                }
                else {
                    $loginName = $loginItem
                    $currentSid = $currentDefaultDatabase = $currentLanguage = $currentPasswordExpiration = $currentAsymmetricKey = $currentCertificate = $currentCredential = $currentDisabled = $currentPasswordPolicyEnforced = $null

                    if ($PsCmdlet.ParameterSetName -eq "MapToCertificate") { $loginType = 'Certificate' }
                    elseif ($PsCmdlet.ParameterSetName -eq "MapToAsymmetricKey") { $loginType = 'AsymmetricKey' }
                    elseif ($loginItem.IndexOf('\') -eq -1) {    $loginType = 'SqlLogin' }
                    else { $loginType = 'WindowsUser' }
                }

                if (($server.LoginMode -ne [Microsoft.SqlServer.Management.Smo.ServerLoginMode]::Mixed) -and ($loginType -eq 'SqlLogin')) {
                    Write-Message -Level Warning -Message "$instance does not have Mixed Mode enabled. [$loginName] is an SQL Login. Enable mixed mode authentication after the migration completes to use this type of login."
                }

                if ($Sid) {
                    $currentSid = $Sid
                }
                if ($DefaultDatabase) {
                    $currentDefaultDatabase = $DefaultDatabase
                }
                if ($Language) {
                    $currentLanguage = $Language
                }
                if ($PSBoundParameters.Keys -contains 'PasswordExpiration') {
                    $currentPasswordExpiration = $PasswordExpiration
                }
                if ($PSBoundParameters.Keys -contains 'PasswordPolicy') {
                    $currentPasswordPolicyEnforced = $PasswordPolicy
                }
                if ($PSBoundParameters.Keys -contains 'MapToAsymmetricKey') {
                    $currentAsymmetricKey = $MapToAsymmetricKey
                }
                if ($PSBoundParameters.Keys -contains 'MapToCertificate') {
                    $currentCertificate = $MapToCertificate
                }
                if ($PSBoundParameters.Keys -contains 'MapToCredential') {
                    $currentCredential = $MapToCredential
                }
                if ($PSBoundParameters.Keys -contains 'Disabled') {
                    $currentDisabled = $Disabled
                }

                #Apply renaming if necessary
                if ($LoginRenameHashtable.Keys -contains $loginName) {
                    $loginName = $LoginRenameHashtable[$loginName]
                }

                #Requesting password if required
                if ($loginItem.GetType().Name -ne 'Login' -and $loginType -eq 'SqlLogin' -and !($Password -or $HashedPassword)) {
                    $Password = Read-Host -AsSecureString -Prompt "Enter a new password for the SQL Server login(s)"
                }

                #verify if login exists on the server
                if ($existingLogin = $server.Logins[$loginName]) {
                    if ($force) {
                        if ($Pscmdlet.ShouldProcess($existingLogin, "Dropping existing login $loginName on $instance because -Force was used")) {
                            try {
                                $existingLogin.Drop()
                            }
                            catch {
                                Stop-Function -Message "Could not remove existing login $loginName on $instance, skipping." -Target $loginName -Continue
                            }
                        }
                    }
                    else {
                        Stop-Function -Message "Login $loginName already exists on $instance and -Force was not specified" -Target $loginName -Continue
                    }
                }


                if ($Pscmdlet.ShouldProcess($SqlInstance, "Creating login $loginName on $instance")) {
                    try {
                        $newLogin = New-Object Microsoft.SqlServer.Management.Smo.Login($server, $loginName)
                        $newLogin.LoginType = $loginType

                        $withParams = ""

                        if ($loginType -eq 'SqlLogin' -and $currentSid -and !$NewSid) {
                            Write-Message -Level Verbose -Message "Setting $loginName SID"
                            $withParams += ", SID = " + (Convert-ByteToHexString $currentSid)
                            $newLogin.Set_Sid($currentSid)
                        }

                        if ($loginType -in ("WindowsUser", "WindowsGroup", "SqlLogin")) {
                            if ($currentDefaultDatabase) {
                                Write-Message -Level Verbose -Message "Setting $loginName default database to $currentDefaultDatabase"
                                $withParams += ", DEFAULT_DATABASE = [$currentDefaultDatabase]"
                                $newLogin.DefaultDatabase = $currentDefaultDatabase
                            }

                            if ($currentLanguage) {
                                Write-Message -Level Verbose -Message "Setting $loginName language to $currentLanguage"
                                $withParams += ", DEFAULT_LANGUAGE = [$currentLanguage]"
                                $newLogin.Language = $currentLanguage
                            }

                            #CHECK_EXPIRATION: default - OFF
                            if ($currentPasswordExpiration) {
                                $withParams += ", CHECK_EXPIRATION = ON"
                                $newLogin.PasswordExpirationEnabled = $true
                            }
                            else {
                                $withParams += ", CHECK_EXPIRATION = OFF"
                                $newLogin.PasswordExpirationEnabled = $false
                            }

                            #CHECK_POLICY: default - ON
                            if ($currentPasswordPolicyEnforced) {
                                $withParams += ", CHECK_POLICY = ON"
                                $newLogin.PasswordPolicyEnforced = $true
                            }
                            else {
                                $withParams += ", CHECK_POLICY = OFF"
                                $newLogin.PasswordPolicyEnforced = $false
                            }

                            #Generate hashed password if necessary
                            if ($Password) {
                                $currentHashedPassword = Get-PasswordHash $Password $server.versionMajor
                            }
                            elseif ($HashedPassword) {
                                $currentHashedPassword = $HashedPassword
                            }
                        }
                        elseif ($loginType -eq 'AsymmetricKey') {
                            $newLogin.AsymmetricKey = $currentAsymmetricKey
                        }
                        elseif ($loginType -eq 'Certificate') {
                            $newLogin.Certificate = $currentCertificate
                        }

                        #Add credential
                        if ($currentCredential) {
                            $withParams += ", CREDENTIAL = [$currentCredential]"
                        }

                        Write-Message -Level Verbose -Message "Adding as login type $loginType"

                        # Attempt to add login using SMO, then T-SQL
                        try {
                            if ($loginType -in ("WindowsUser", "WindowsGroup", "AsymmetricKey", "Certificate")) {
                                if ($withParams) { $withParams = " WITH " + $withParams.TrimStart(',') }
                                $newLogin.Create()
                            }
                            elseif ($loginType -eq "SqlLogin") {
                                $newLogin.Create($currentHashedPassword, [Microsoft.SqlServer.Management.Smo.LoginCreateOptions]::IsHashed)
                            }
                            $newLogin.Refresh()

                            #Adding credential
                            if ($currentCredential) {
                                try {
                                    $newLogin.AddCredential($currentCredential)
                                }
                                catch {
                                    $newLogin.Drop()
                                    Stop-Function -Message "Failed to add $loginName to $instance." -Category InvalidOperation -ErrorRecord $_ -Target $instance -Continue
                                }
                            }
                            Write-Message -Level Verbose -Message "Successfully added $loginName to $instance."
                        }
                        catch {
                            Write-Message -Level Verbose -Message "Failed to create $loginName on $instance using SMO, trying T-SQL."
                            try {
                                if ($loginType -eq 'AsymmetricKey') { $sql = "CREATE LOGIN [$loginName] FROM ASYMMETRIC KEY [$currentAsymmetricKey]" }
                                elseif ($loginType -eq 'Certificate') { $sql = "CREATE LOGIN [$loginName] FROM CERTIFICATE [$currentCertificate]" }
                                elseif ($loginType -eq "SqlLogin") { $sql = "CREATE LOGIN [$loginName] WITH PASSWORD = $currentHashedPassword HASHED" + $withParams }
                                else { $sql = "CREATE LOGIN [$loginName] FROM WINDOWS" + $withParams }

                                $null = $server.Query($sql)
                                $newLogin = $server.logins[$loginName]
                                Write-Message -Level Verbose -Message "Successfully added $loginName to $instance."
                            }
                            catch {
                                Stop-Function -Message "Failed to add $loginName to $instance." -Category InvalidOperation -ErrorRecord $_ -Target $instance -Continue
                            }
                        }

                        #Process the Disabled property
                        if ($currentDisabled) {
                            try {
                                $newLogin.Disable()
                                Write-Message -Level Verbose -Message "Login $loginName has been disabled on $instance."
                            }
                            catch {
                                Write-Message -Level Verbose -Message "Failed to disable $loginName on $instance using SMO, trying T-SQL."
                                try {
                                    $sql = "ALTER LOGIN [$loginName] DISABLE"
                                    $null = $server.Query($sql)
                                    Write-Message -Level Verbose -Message "Login $loginName has been disabled on $instance."
                                }
                                catch {
                                    Stop-Function -Message "Failed to disable $loginName on $instance." -Category InvalidOperation -ErrorRecord $_ -Target $instance -Continue
                                }
                            }
                        }
                        #Display results
                        Get-DbaLogin -SqlInstance $server -Login $loginName
                    }
                    catch {
                        Stop-Function -Message "Failed to create login $loginName on $instance." -Target $credential -InnerErrorRecord $_ -Continue
                    }
                }
            }
        }
    }
}
tools\dbatools\functions\New-DbaPublishProfile.ps1
function New-DbaPublishProfile {
    <#
        .SYNOPSIS
            Creates a new Publish Profile.

        .DESCRIPTION
            The New-PublishProfile command generates a standard publish profile xml file that can be used by the DacFx (this and everything else) to control the deployment of your dacpac
            This generates a standard template XML which is enough to dpeloy a dacpac but it is highly recommended that you add additional options to the publish profile.
            If you use Visual Studio you can open a publish.xml file and use the ui to edit the file -
            To create a new file, right click on an SSDT project, choose "Publish" then "Load Profile" and load your profile or create a new one.
            Once you have loaded it in Visual Studio, clicking advanced shows you the list of options available to you.
            For a full list of options that you can add to the profile, google "sqlpackage.exe command line switches" or (https://msdn.microsoft.com/en-us/library/hh550080(v=vs.103).aspx)

        .PARAMETER SqlInstance
        SQL Server name or SMO object representing the SQL Server to connect to and publish to. Alternatively, you can provide a ConnectionString.

        .PARAMETER SqlCredential
        Allows you to login to servers using alternative logins instead Integrated, accepts Credential object created by Get-Credential

        .PARAMETER Database
            The database name you are targeting

        .PARAMETER ConnectionString
            The connection string to the database you are upgrading.

            Alternatively, you can provide a SqlInstance (and optionally SqlCredential) and the script will connect and generate the connectionstring.

        .PARAMETER Path
            The directory where you would like to save the profile xml file(s).

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Dacpac
            Author: Richie lee (@bzzzt_io)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
        .LINK
            https://dbatools.io/New-DbaPublishProfile

        .EXAMPLE
        New-DbaPublishProfile -SqlInstance sql2017 -SqlCredential (Get-Credential) -Database WorldWideImporters -Path C:\temp

        In this example, a prompt will appear for alternative credentials, tghen a connection will be made to sql2017. Using that connection,
        the ConnectionString will be extracted and used within the Publish Profile XML file which will be created at C:\temp\sql2017-WorldWideImporters-publish.xml

        .EXAMPLE
        New-DbaPublishProfile -Database WorldWideImporters -Path C:\temp -ConnectionString "SERVER=(localdb)\MSSQLLocalDB;Integrated Security=True;Database=master"

        In this example, no connections are made, and a Publish Profile XML would be created at C:\temp\localdb-MSSQLLocalDB-WorldWideImporters-publish.xml
    #>
    [CmdletBinding()]
    param (
        [parameter(ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Parameter(Mandatory)]
        [string[]]$Database,
        [string]$Path = "$home\Documents",
        [string[]]$ConnectionString,
        [switch]$EnableException
    )
    begin {
        if ((Test-Bound -Not -ParameterName SqlInstance) -and (Test-Bound -Not -ParameterName ConnectionString)) {
            Stop-Function -Message "You must specify either SqlInstance or ConnectionString"
        }

        if (-not (Test-Path $Path)) {
            Stop-Function -Message "$Path doesn't exist or access denied"
        }

        if ((Get-Item $path) -isnot [System.IO.DirectoryInfo]) {
            Stop-Function -Message "Path must be a directory"
        }

        function Get-Template ($db, $connstring) {
            "<?xml version=""1.0"" ?>
            <Project ToolsVersion=""14.0"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
              <PropertyGroup>
                <TargetDatabaseName>{0}</TargetDatabaseName>
                <TargetConnectionString>{1}</TargetConnectionString>
                <ProfileVersionNumber>1</ProfileVersionNumber>
              </PropertyGroup>
            </Project>" -f $db[0], $connstring
        }

        function Get-ServerName ($connstring) {
            $builder = New-Object System.Data.Common.DbConnectionStringBuilder
            $builder.set_ConnectionString($connstring)
            $instance = $builder['data source']

            if (-not $instance) {
                $instance = $builder['server']
            }

            return $instance.ToString().Replace('\', '--')
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $sqlinstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $ConnectionString += $server.ConnectionContext.ConnectionString.Replace(';Application Name="dbatools PowerShell module - dbatools.io"', '')

        }

        foreach ($connstring in $ConnectionString) {
            foreach ($db in $Database) {
                $profileTemplate = Get-Template $db, $connstring
                $instancename = Get-ServerName $connstring

                try {
                    $server = [DbaInstance]($instancename.ToString().Replace('--', '\'))
                    $PublishProfile = Join-Path $Path "$($instancename.Replace('--','-'))-$db-publish.xml" -ErrorAction Stop
                    Write-Message -Level Verbose -Message "Writing to $PublishProfile"
                    $profileTemplate | Out-File $PublishProfile -ErrorAction Stop
                    [pscustomobject]@{
                        ComputerName     = $server.ComputerName
                        InstanceName     = $server.InstanceName
                        SqlInstance      = $server.FullName
                        Database         = $db
                        FileName         = $PublishProfile
                        ConnectionString = $connstring
                        ProfileTemplate  = $profileTemplate
                    } | Select-DefaultView -ExcludeProperty ComputerName, InstanceName, ProfileTemplate
                }
                catch {
                    Stop-Function -ErrorRecord $_ -Message "Failure" -Target $instancename -Continue
                }
            }
        }
    }
}
tools\dbatools\functions\New-DbaScriptingOption.ps1
function New-DbaScriptingOption {
    <#
    .SYNOPSIS
    Creates a new Microsoft.SqlServer.Management.Smo.ScriptingOptions object

    .DESCRIPTION
    Creates a new Microsoft.SqlServer.Management.Smo.ScriptingOptions object. Basically saves you the time from remembering the SMO assembly name ;)

    See https://msdn.microsoft.com/en-us/library/microsoft.sqlserver.management.smo.scriptingoptions.aspx for more information

    .NOTES
    Tags: Migration, Backup, DR

    Website: https://dbatools.io
    Copyright: (C) Chrissy LeMaire, [email protected]
    License: MIT https://opensource.org/licenses/MIT

    .LINK
    https://dbatools.io/New-DbaScriptingOption
    https://msdn.microsoft.com/en-us/library/microsoft.sqlserver.management.smo.scriptingoptions.aspx

    .EXAMPLE
    $options = New-DbaScriptingOption
    $options.ScriptDrops = $false
    $options.WithDependencies = $true
    Get-DbaAgentJob -SqlInstance sql2016 | Export-DbaScript -ScriptingOptionObject $options

    Exports Agent Jobs with the Scripting Options ScriptDrops set to $false and WithDependencies set to true

    #>
    New-Object Microsoft.SqlServer.Management.Smo.ScriptingOptions
}
tools\dbatools\functions\New-DbaServiceMasterKey.ps1
function New-DbaServiceMasterKey {
    <#
.SYNOPSIS
Creates a new service master key

.DESCRIPTION
Creates a new service master key in the master database

.PARAMETER SqlInstance
The target SQL Server instances

.PARAMETER SqlCredential
Allows you to login to SQL Server using alternative credentials.

.PARAMETER Password
Secure string used to create the key.

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Certificate

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
New-DbaServiceMasterKey -SqlInstance Server1

You will be prompted to securely enter your Service Key Password twice, then a master key will be created in the master database on server1 if it does not exist.

#>
    [CmdletBinding(SupportsShouldProcess)]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Security.SecureString]$Password,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            if (Test-Bound -ParameterName Password -Not) {
                $password = Read-Host -AsSecureString -Prompt "You must enter Service Key password for $instance"
                $password2 = Read-Host -AsSecureString -Prompt "Type the password again"

                if (([System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($password))) -ne ([System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($password2)))) {
                    Stop-Function -Message "Passwords do not match" -Continue
                }
            }
            New-DbaDatabaseMasterKey -SqlInstance $instance -Database master -Password $password
        }
    }
}
tools\dbatools\functions\New-DbaSqlConnectionString.ps1
function New-DbaSqlConnectionString {
    <#
        .SYNOPSIS
            Builds or extracts a SQL Server Connection String

        .DESCRIPTION
            Builds or extracts a SQL Server Connection String

            See https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.connectionstring.aspx
            and https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnectionstringbuilder.aspx
            and https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.aspx

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to.

        .PARAMETER Credential
            Credential object used to connect to the SQL Server as a different user be it Windows or SQL Server. Windows users are determined by the existence of a backslash, so if you are intending to use an alternative Windows connection instead of a SQL login, ensure it contains a backslash.

        .PARAMETER AccessToken
            Gets or sets the access token for the connection.

        .PARAMETER AppendConnectionString
            Appends to the current connection string. Note that you cannot pass authentication information using this method. Use -SqlInstance and, optionally, -SqlCredential to set authentication information.

        .PARAMETER ApplicationIntent
            Declares the application workload type when connecting to a server. Possible values are ReadOnly and ReadWrite.

        .PARAMETER BatchSeparator
            By default, this is "GO"

        .PARAMETER ClientName
            By default, this command sets the client to "dbatools PowerShell module - dbatools.io - custom connection" if you're doing anything that requires profiling, you can look for this client name. Using -ClientName allows you to set your own custom client.

        .PARAMETER ConnectTimeout
            The length of time (in seconds) to wait for a connection to the server before terminating the attempt and generating an error.

            Valid values are greater than or equal to 0 and less than or equal to 2147483647.

            When opening a connection to a Azure SQL Database, set the connection timeout to 30 seconds.

        .PARAMETER EncryptConnection
            When true, SQL Server uses SSL encryption for all data sent between the client and server if the server has a certificate installed. Recognized values are true, false, yes, and no. For more information, see Connection String Syntax.

            Beginning in .NET Framework 4.5, when TrustServerCertificate is false and Encrypt is true, the server name (or IP address) in a SQL Server SSL certificate must exactly match the server name (or IP address) specified in the connection string. Otherwise, the connection attempt will fail. For information about support for certificates whose subject starts with a wildcard character (*), see Accepted wildcards used by server certificates for server authentication.

        .PARAMETER FailoverPartner
            The name of the failover partner server where database mirroring is configured.

            If the value of this key is "", then Initial Catalog must be present, and its value must not be "".

            The server name can be 128 characters or less.

            If you specify a failover partner but the failover partner server is not configured for database mirroring and the primary server (specified with the Server keyword) is not available, then the connection will fail.

            If you specify a failover partner and the primary server is not configured for database mirroring, the connection to the primary server (specified with the Server keyword) will succeed if the primary server is available.

        .PARAMETER IsActiveDirectoryUniversalAuth
            Azure related

        .PARAMETER LockTimeout
            Sets the time in seconds required for the connection to time out when the current transaction is locked.

        .PARAMETER MaxPoolSize
            Sets the maximum number of connections allowed in the connection pool for this specific connection string.

        .PARAMETER MinPoolSize
            Sets the minimum number of connections allowed in the connection pool for this specific connection string.

        .PARAMETER MultipleActiveResultSets
            When used, an application can maintain multiple active result sets (MARS). When false, an application must process or cancel all result sets from one batch before it can execute any other batch on that connection.

        .PARAMETER MultiSubnetFailover
            If your application is connecting to an AlwaysOn availability group (AG) on different subnets, setting MultiSubnetFailover provides faster detection of and connection to the (currently) active server. For more information about SqlClient support for Always On Availability Groups

        .PARAMETER NetworkProtocol
            Connect explicitly using 'TcpIp','NamedPipes','Multiprotocol','AppleTalk','BanyanVines','Via','SharedMemory' and 'NWLinkIpxSpx'

        .PARAMETER NonPooledConnection
            Request a non-pooled connection

        .PARAMETER PacketSize
            Sets the size in bytes of the network packets used to communicate with an instance of SQL Server. Must match at server.

        .PARAMETER PooledConnectionLifetime
            When a connection is returned to the pool, its creation time is compared with the current time, and the connection is destroyed if that time span (in seconds) exceeds the value specified by Connection Lifetime. This is useful in clustered configurations to force load balancing between a running server and a server just brought online.

            A value of zero (0) causes pooled connections to have the maximum connection timeout.

        .PARAMETER SqlExecutionModes
            The SqlExecutionModes enumeration contains values that are used to specify whether the commands sent to the referenced connection to the server are executed immediately or saved in a buffer.

            Valid values include CaptureSql, ExecuteAndCaptureSql and ExecuteSql.

        .PARAMETER StatementTimeout
            Sets the number of seconds a statement is given to run before failing with a time-out error.

        .PARAMETER TrustServerCertificate
            Sets a value that indicates whether the channel will be encrypted while bypassing walking the certificate chain to validate trust.

        .PARAMETER WorkstationId
            Sets the name of the workstation connecting to SQL Server.

        .NOTES
            Tags: Connection, Connect, ConnectionString
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/New-DbaSqlConnectionString

        .EXAMPLE
            New-DbaSqlConnectionString -SqlInstance sql2014

            Creates a connection string that connects using Windows Authentication

        .EXAMPLE
            Connect-DbaInstance -SqlInstance sql2016 | New-DbaSqlConnectionString

            Builds a connected SMO object using Connect-DbaInstance then extracts and displays the connection string

        .EXAMPLE
            $wincred = Get-Credential ad\sqladmin
            New-DbaSqlConnectionString -SqlInstance sql2014 -Credential $wincred

            Creates a connection string that connects using alternative Windows credentials

        .EXAMPLE
            $sqlcred = Get-Credential sqladmin
            $server = New-DbaSqlConnectionString -SqlInstance sql2014 -Credential $sqlcred

            Login to sql2014 as SQL login sqladmin.

        .EXAMPLE
            $server = New-DbaSqlConnectionString -SqlInstance sql2014 -ClientName "mah connection"

            Creates a connection string that connects using Windows Authentication and uses the client name "mah connection". So when you open up profiler or use extended events, you can search for "mah connection".

        .EXAMPLE
            $server = New-DbaSqlConnectionString -SqlInstance sql2014 -AppendConnectionString "Packet Size=4096;AttachDbFilename=C:\MyFolder\MyDataFile.mdf;User Instance=true;"

            Creates a connection string that connects to sql2014 using Windows Authentication, then it sets the packet size (this can also be done via -PacketSize) and other connection attributes.

        .EXAMPLE
            $server = New-DbaSqlConnectionString -SqlInstance sql2014 -NetworkProtocol TcpIp -MultiSubnetFailover

            Creates a connection string with Windows Authentication that uses TCPIP and has MultiSubnetFailover enabled.

        .EXAMPLE
            $connstring = New-DbaSqlConnectionString sql2016 -ApplicationIntent ReadOnly

            Creates a connection string with ReadOnly ApplicantionIntent.
    #>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("SqlCredential")]
        [PSCredential]$Credential,
        [string]$AccessToken,
        [ValidateSet('ReadOnly', 'ReadWrite')]
        [string]$ApplicationIntent,
        [string]$BatchSeparator,
        [string]$ClientName = "custom connection",
        [int]$ConnectTimeout,
        [switch]$EncryptConnection,
        [string]$FailoverPartner,
        [switch]$IsActiveDirectoryUniversalAuth,
        [int]$LockTimeout,
        [int]$MaxPoolSize,
        [int]$MinPoolSize,
        [switch]$MultipleActiveResultSets,
        [switch]$MultiSubnetFailover,
        [ValidateSet('TcpIp', 'NamedPipes', 'Multiprotocol', 'AppleTalk', 'BanyanVines', 'Via', 'SharedMemory', 'NWLinkIpxSpx')]
        [string]$NetworkProtocol,
        [switch]$NonPooledConnection,
        [int]$PacketSize,
        [int]$PooledConnectionLifetime,
        [ValidateSet('CaptureSql', 'ExecuteAndCaptureSql', 'ExecuteSql')]
        [string]$SqlExecutionModes,
        [int]$StatementTimeout,
        [switch]$TrustServerCertificate,
        [string]$WorkstationId,
        [string]$AppendConnectionString
    )

    process {
        foreach ($instance in $sqlinstance) {

            if ($instance.GetType() -eq [Microsoft.SqlServer.Management.Smo.Server]) {
                return $instance.ConnectionContext.ConnectionString
            }
            else {
                $guid = [System.Guid]::NewGuid()
                $server = New-Object Microsoft.SqlServer.Management.Smo.Server $guid

                if ($AppendConnectionString) {
                    $connstring = $server.ConnectionContext.ConnectionString
                    $server.ConnectionContext.ConnectionString = "$connstring;$appendconnectionstring"
                    $server.ConnectionContext.ConnectionString
                }
                else {

                    $server.ConnectionContext.ApplicationName = $clientname

                    if ($AccessToken) { $server.ConnectionContext.AccessToken = $AccessToken }
                    if ($BatchSeparator) { $server.ConnectionContext.BatchSeparator = $BatchSeparator }
                    if ($ConnectTimeout) { $server.ConnectionContext.ConnectTimeout = $ConnectTimeout }
                    if ($Database) { $server.ConnectionContext.DatabaseName = $Database }
                    if ($EncryptConnection) { $server.ConnectionContext.EncryptConnection = $true }
                    if ($IsActiveDirectoryUniversalAuth) { $server.ConnectionContext.IsActiveDirectoryUniversalAuth = $true }
                    if ($LockTimeout) { $server.ConnectionContext.LockTimeout = $LockTimeout }
                    if ($MaxPoolSize) { $server.ConnectionContext.MaxPoolSize = $MaxPoolSize }
                    if ($MinPoolSize) { $server.ConnectionContext.MinPoolSize = $MinPoolSize }
                    if ($MultipleActiveResultSets) { $server.ConnectionContext.MultipleActiveResultSets = $true }
                    if ($NetworkProtocol) { $server.ConnectionContext.NetworkProtocol = $NetworkProtocol }
                    if ($NonPooledConnection) { $server.ConnectionContext.NonPooledConnection = $true }
                    if ($PacketSize) { $server.ConnectionContext.PacketSize = $PacketSize }
                    if ($PooledConnectionLifetime) { $server.ConnectionContext.PooledConnectionLifetime = $PooledConnectionLifetime }
                    if ($StatementTimeout) { $server.ConnectionContext.StatementTimeout = $StatementTimeout }
                    if ($SqlExecutionModes) { $server.ConnectionContext.SqlExecutionModes = $SqlExecutionModes }
                    if ($TrustServerCertificate) { $server.ConnectionContext.TrustServerCertificate = $true }
                    if ($WorkstationId) { $server.ConnectionContext.WorkstationId = $WorkstationId }

                    $connstring = $server.ConnectionContext.ConnectionString
                    if ($MultiSubnetFailover) { $connstring = "$connstring;MultiSubnetFailover=True" }
                    if ($FailoverPartner) { $connstring = "$connstring;Failover Partner=$FailoverPartner" }
                    if ($ApplicationIntent) { $connstring = "$connstring;ApplicationIntent=$ApplicationIntent;" }

                    if ($connstring -ne $server.ConnectionContext.ConnectionString) {
                        $server.ConnectionContext.ConnectionString = $connstring
                    }
                    if ($null -ne $Credential.username) {
                        $username = ($Credential.username).TrimStart("\")

                        if ($username -like "*\*") {
                            $username = $username.Split("\")[1]
                            $authtype = "Windows Authentication with Credential"
                            $server.ConnectionContext.LoginSecure = $true
                            $server.ConnectionContext.ConnectAsUser = $true
                            $server.ConnectionContext.ConnectAsUserName = $username
                            $server.ConnectionContext.ConnectAsUserPassword = ($Credential).GetNetworkCredential().Password
                        }
                        else {
                            $authtype = "SQL Authentication"
                            $server.ConnectionContext.LoginSecure = $false
                            $server.ConnectionContext.set_Login($username)
                            $server.ConnectionContext.set_SecurePassword($Credential.Password)
                        }
                    }

                    ($server.ConnectionContext.ConnectionString).Replace($guid, $SqlInstance)
                }
            }
        }
    }
}
tools\dbatools\functions\New-DbaSqlConnectionStringBuilder.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function New-DbaSqlConnectionStringBuilder {
    <#
        .SYNOPSIS
            Returns a System.Data.SqlClient.SqlConnectionStringBuilder with the string specified

        .DESCRIPTION
            Creates a System.Data.SqlClient.SqlConnectionStringBuilder from a connection string.

        .PARAMETER ConnectionString
            A Connection String

        .PARAMETER ApplicationName
            The application name to tell SQL Server the connection is associated with.

        .PARAMETER DataSource
            The Sql Server to connect to.

        .PARAMETER InitialCatalog
            The initial database on the server to connect to.

        .PARAMETER IntegratedSecurity
            Set to true to use windows authentication.

        .PARAMETER UserName
            Sql User Name to connect with.

        .PARAMETER Password
            Password to use to connect with.

        .PARAMETER MultipleActiveResultSets
            Enable Multiple Active Result Sets.

        .PARAMETER ColumnEncryptionSetting
            Enable Always Encrypted.

        .PARAMETER WorkstationID
            Set the Workstation Id that is associated with the connection.

        .NOTES
            Author: zippy1981
            Tags: SqlBuild, ConnectionString, Connection

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2017 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/New-DbaSqlConnectionStringBuilder

        .EXAMPLE
            New-DbaSqlConnectionStringBuilder

            Returns an empty ConnectionStringBuilder

        .EXAMPLE
            "Data Source=localhost,1433;Initial Catalog=AlwaysEncryptedSample;UID=sa;PWD=alwaysB3Encrypt1ng;Application Name=Always Encrypted Sample MVC App;Column Encryption Setting=enabled" | New-DbaSqlConnectionStringBuilder

            Returns a connection string builder that can be used to connect to the local sql server instance on the default port.
    #>
    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "")]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingUserNameAndPassWordParams", "")]
    param (
        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [string[]]$ConnectionString = "",
        [Parameter(Mandatory = $false)]
        [string]$ApplicationName = "dbatools Powershell Module",
        [Parameter(Mandatory = $false)]
        [string]$DataSource = $null,
        [Parameter(Mandatory = $false)]
        [string]$InitialCatalog = $null,
        [Parameter(Mandatory = $false)]
        [Nullable[bool]]$IntegratedSecurity = $null,
        [Parameter(Mandatory = $false)]
        [string]$UserName = $null,
        # No point in securestring here, the memory is never stored securely in memory.
        [Parameter(Mandatory = $false)]
        [string]$Password = $null,
        [Alias('MARS')]
        [Parameter(Mandatory = $false)]
        [switch]$MultipleActiveResultSets,
        [Alias('AlwaysEncrypted')]
        [Parameter(Mandatory = $false)]
        [Data.SqlClient.SqlConnectionColumnEncryptionSetting]$ColumnEncryptionSetting =
        [Data.SqlClient.SqlConnectionColumnEncryptionSetting]::Enabled,
        [Parameter(Mandatory = $false)]
        [string]$WorkstationId = $env:COMPUTERNAME
    )
    process {
        foreach ($cs in $ConnectionString) {
            $builder = New-Object Data.SqlClient.SqlConnectionStringBuilder $cs
            if ($builder.ApplicationName -eq ".Net SqlClient Data Provider") {
                $builder['Application Name'] = $ApplicationName
            }
            if (![string]::IsNullOrWhiteSpace($DataSource)) {
                $builder['Data Source'] = $DataSource
            }
            if (![string]::IsNullOrWhiteSpace($InitialCatalog)) {
                $builder['Initial Catalog'] = $InitialCatalog
            }
            if (![string]::IsNullOrWhiteSpace($IntegratedSecurity)) {
                $builder['Integrated Security'] = $IntegratedSecurity
            }
            if (![string]::IsNullOrWhiteSpace($UserName)) {
                $builder["User ID"] = $UserName
            }
            if (![string]::IsNullOrWhiteSpace($Password)) {
                $builder['Password'] = $Password
            }
            if (![string]::IsNullOrWhiteSpace($WorkstationId)) {
                $builder['Workstation ID'] = $WorkstationId
            }
            if ($MultipleActiveResultSets -eq $true) {
                $builder['MultipleActiveResultSets'] = $true
            }
            if ($ColumnEncryptionSetting -eq [Data.SqlClient.SqlConnectionColumnEncryptionSetting]::Enabled) {
                $builder['Column Encryption Setting'] = [Data.SqlClient.SqlConnectionColumnEncryptionSetting]::Enabled
            }
            $builder
        }
    }
}
tools\dbatools\functions\New-DbaSqlDirectory.ps1
function New-DbaSqlDirectory {
    <#
        .SYNOPSIS
            Creates new path as specified by the path variable

        .DESCRIPTION
            Uses master.dbo.xp_create_subdir to create the path
            Returns $true if the path can be created, $false otherwise

        .PARAMETER SqlInstance
            The SQL Server you want to run the test on.

        .PARAMETER Path
            The Path to tests. Can be a file or directory.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Path, Directory, Folder
            Author: Stuart Moore

            Requires: Admin access to server (not SQL Services),
            Remoting must be enabled and accessible if $SqlInstance is not local

            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/New-DbaSqlDirectory

        .EXAMPLE
            New-DbaSqlDirectory -SqlInstance sqlcluster -Path L:\MSAS12.MSSQLSERVER\OLAP

            If the SQL Server instance sqlcluster can create the path L:\MSAS12.MSSQLSERVER\OLAP it will do and return $true, if not it will return $false.

        .EXAMPLE
            $credential = Get-Credential
            New-DbaSqlDirectory -SqlInstance sqlcluster -SqlCredential $credential -Path L:\MSAS12.MSSQLSERVER\OLAP

            If the SQL Server instance sqlcluster can create the path L:\MSAS12.MSSQLSERVER\OLAP it will do and return $true, if not it will return $false. Uses a SqlCredential to connect
    #>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Parameter(Mandatory = $true)]
        [string]$Path,
        [PSCredential]$SqlCredential,
        [switch]$EnableException
    )
    
    foreach ($instance in $SqlInstance) {
        try {
            Write-Message -Level Verbose -Message "Connecting to $instance."
            $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
        }
        
        $Path = $Path.Replace("'", "''")
        
        $exists = Test-DbaSqlPath -SqlInstance $sqlinstance -SqlCredential $SqlCredential -Path $Path
        
        if ($exists) {
            Stop-Function -Message "$Path already exists" -Target $server -Continue
        }
        
        $sql = "EXEC master.dbo.xp_create_subdir'$path'"
        Write-Message -Level Debug -Message $sql
        
        try {
            $query = $server.Query($sql)
            $Created = $true
        }
        catch {
            $Created = $false
            Stop-Function -Message "Failure" -ErrorRecord $_
        }
        
        [pscustomobject]@{
            Server  = $SqlInstance
            Path    = $Path
            Created = $Created
        }
    }
}
tools\dbatools\functions\New-DbaSsisCatalog.ps1
function New-DbaSsisCatalog {
    <#
        .SYNOPSIS
            Enables the SSIS Catalog on a SQL Server 2012+

        .DESCRIPTION
            After installing the SQL Server Engine and SSIS you still have to enable the SSIS Catalog. This function will enable the catalog and gives the option of supplying the password.

        .PARAMETER SqlInstance
            SQL Server you wish to run the function on.

        .PARAMETER SqlCredential
            Credentials used to connect to the SQL Server

        .PARAMETER Password
            Required password that will be used for the security key in SSISDB.

        .PARAMETER SsisCatalog
            SSIS catalog name. By default, this is SSISDB.

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: SSIS, SSISDB, Catalog
            Author: Stephen Bennett, https://sqlnotesfromtheunderground.wordpress.com/
            Tags:
            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/New-DbaSsisCatalog

        .EXAMPLE
            $password = ConvertTo-SecureString MyVisiblePassWord -AsPlainText -Force
            New-DbaSsisCatalog -SqlInstance sql2016 -Password $password

            Creates the SSIS Catalog on server DEV01 with the specified password.

        .EXAMPLE
            $password = Read-Host -AsSecureString -Prompt "Enter password"
            New-DbaSsisCatalog -SqlInstance DEV01 -Password $password

            Creates the SSIS Catalog on server DEV01 with the specified password.
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory = $true)]
        [Security.SecureString]$Password,
        [string]$SsisCatalog = "SSISDB",
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            ## check if SSIS and Engine running on box
            $services = Get-DbaSqlService -ComputerName $server.ComputerName

            $ssisservice = $Services | Where-Object { $_.ServiceType -eq "SSIS" -and $_.State -eq "Running" }

            if (-not $ssisservice) {
                Stop-Function -Message "SSIS is not running on $instance" -Continue -Target $instance
            }

            #if SQL 2012 or higher only validate databases with ContainmentType = NONE
            $clrenabled = Get-DbaSpConfigure -SqlInstance $server -Config IsSqlClrEnabled

            if (!$clrenabled.RunningValue) {
                Stop-Function -Message 'CLR Integration must be enabled.  You can enable it by running Set-DbaSpConfigure -SqlInstance sql2012 -Config IsSqlClrEnabled -Value $true' -Continue -Target $instance
            }

            try {
                $ssis = New-Object Microsoft.SqlServer.Management.IntegrationServices.IntegrationServices $server
            }
            catch {
                Stop-Function -Message "Can't load server" -Target $instance -ErrorRecord $_
                return
            }

            if ($ssis.Catalogs[$SsisCatalog]) {
                Stop-Function -Message "SSIS Catalog already exists" -Continue -Target $ssis.Catalogs[$SsisCatalog]
            }
            else {
                if ($Pscmdlet.ShouldProcess($server, "Creating SSIS catalog: $SsisCatalog")) {
                    try {
                        $ssisdb = New-Object Microsoft.SqlServer.Management.IntegrationServices.Catalog ($ssis, $SsisCatalog, $(([System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($password)))))
                        $ssisdb.Create()

                        [pscustomobject]@{
                            ComputerName = $server.ComputerName
                            InstanceName = $server.ServiceName
                            SqlInstance  = $server.DomainInstanceName
                            SsisCatalog  = $SsisCatalog
                            Created      = $true
                        }
                    }
                    catch {
                        Stop-Function -Message "Failed to create SSIS Catalog: $_" -Target $_ -Continue
                    }
                }
            }
        }
    }
}
tools\dbatools\functions\New-DbatoolsSupportPackage.ps1
function New-DbatoolsSupportPackage {
    <#
    .SYNOPSIS
    Creates a package of troubleshooting information that can be used by dbatools to help debug issues.

    .DESCRIPTION
    This function creates an extensive debugging package that can help with reproducing and fixing issues.

    The file will be created on the desktop by default and will contain quite a bit of information:
        - OS Information
        - Hardware Information (CPU, Ram, things like that)
        - .NET Information
        - PowerShell Information
        - Your input history
        - The In-Memory message log
        - The In-Memory error log
        - Screenshot of the console buffer (Basically, everything written in your current console, even if you have to scroll upwards to see it.

    .PARAMETER Path
    The folder where to place the output xml in.

    .PARAMETER Variables
    Name of additional variables to attach.
    This allows you to add the content of variables to the support package, if you believe them to be relevant to the case.

    .PARAMETER EnableException
    By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
    Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
    Author: Fred Weinmann (@FredWeinmann)
    Tags: Debug

    Website: https://dbatools.io
    Copyright: (C) Chrissy LeMaire, [email protected]
    License: MIT https://opensource.org/licenses/MIT

    .LINK
    https://dbatools.io/New-DbatoolsSupportPackage

    .EXAMPLE
    New-DbatoolsSupportPackage

    Creates a large support pack in order to help us troubleshoot stuff.
    #>
    [CmdletBinding()]
    param (
        [string]
        $Path = "$($env:USERPROFILE)\Desktop",

        [string[]]
        $Variables,

        [switch]
        [Alias('Silent')]$EnableException
    )

    BEGIN {
        Write-Message -Level InternalComment -Message "Starting"
        Write-Message -Level Verbose -Message "Bound parameters: $($PSBoundParameters.Keys -join ", ")"

        #region Helper functions
        function Get-ShellBuffer {
            [CmdletBinding()]
            Param ()

            try {
                # Define limits
                $rec = New-Object System.Management.Automation.Host.Rectangle
                $rec.Left = 0
                $rec.Right = $host.ui.rawui.BufferSize.Width - 1
                $rec.Top = 0
                $rec.Bottom = $host.ui.rawui.BufferSize.Height - 1

                # Load buffer
                $buffer = $host.ui.rawui.GetBufferContents($rec)

                # Convert Buffer to list of strings
                $int = 0
                $lines = @()
                while ($int -le $rec.Bottom) {
                    $n = 0
                    $line = ""
                    while ($n -le $rec.Right) {
                        $line += $buffer[$int, $n].Character
                        $n++
                    }
                    $line = $line.TrimEnd()
                    $lines += $line
                    $int++
                }

                # Measure empty lines at the beginning
                $int = 0
                $temp = $lines[$int]
                while ($temp -eq "") { $int++; $temp = $lines[$int] }

                # Measure empty lines at the end
                $z = $rec.Bottom
                $temp = $lines[$z]
                while ($temp -eq "") { $z--; $temp = $lines[$z] }

                # Skip the line launching this very function
                $z--

                # Measure empty lines at the end (continued)
                $temp = $lines[$z]
                while ($temp -eq "") { $z--; $temp = $lines[$z] }

                # Cut results to the limit and return them
                return $lines[$int .. $z]
            }
            catch { }
        }
        #endregion Helper functions
    }
    PROCESS {
        $filePathXml = "$($Path.Trim('\'))\dbatools_support_pack_$(Get-Date -Format "yyyy_MM_dd-HH_mm_ss").xml"
        $filePathZip = $filePathXml -replace "\.xml$", ".zip"

        Write-Message -Level Critical -Message @"
Gathering information...
Will write the final output to: $filePathZip

Please submit this file to the team, to help with troubleshooting whatever issue you encountered.
Be aware that this package contains a lot of information including your input history in the console.
Please make sure no sensitive data (such as passwords) can be caught this way.

Ideally start a new console, perform the minimal steps required to reproduce the issue, then run this command.
This will make it easier for us to troubleshoot and you won't be sending us the keys to your castle.
"@

        $hash = @{ }
        Write-Message -Level Output -Message "Collecting dbatools logged messages (Get-DbatoolsLog)"
        $hash["Messages"] = Get-DbatoolsLog
        Write-Message -Level Output -Message "Collecting dbatools logged errors (Get-DbatoolsLog -Errors)"
        $hash["Errors"] = Get-DbatoolsLog -Errors
        Write-Message -Level Output -Message "Collecting copy of console buffer (what you can see on your console)"
        $hash["ConsoleBuffer"] = Get-ShellBuffer
        Write-Message -Level Output -Message "Collecting Operating System information (Win32_OperatingSystem)"
        $hash["OperatingSystem"] = Get-DbaCmObject -ClassName Win32_OperatingSystem
        Write-Message -Level Output -Message "Collecting CPU information (Win32_Processor)"
        $hash["CPU"] = Get-DbaCmObject -ClassName Win32_Processor
        Write-Message -Level Output -Message "Collecting Ram information (Win32_PhysicalMemory)"
        $hash["Ram"] = Get-DbaCmObject -ClassName Win32_PhysicalMemory
        Write-Message -Level Output -Message "Collecting PowerShell & .NET Version (`$PSVersionTable)"
        $hash["PSVersion"] = $PSVersionTable
        Write-Message -Level Output -Message "Collecting Input history (Get-History)"
        $hash["History"] = Get-History
        Write-Message -Level Output -Message "Collecting list of loaded modules (Get-Module)"
        $hash["Modules"] = Get-Module
        Write-Message -Level Output -Message "Collecting list of loaded snapins (Get-PSSnapin)"
        $hash["SnapIns"] = Get-PSSnapin
        Write-Message -Level Output -Message "Collecting list of loaded assemblies (Name, Version, and Location)"
        $hash["Assemblies"] = [appdomain]::CurrentDomain.GetAssemblies() | Select-Object CodeBase, FullName, Location, ImageRuntimeVersion, GlobalAssemblyCache, IsDynamic

        if (Test-Bound "Variables") {
            Write-Message -Level Output -Message "Adding variables specified for export: $($Variables -join ", ")"
            $hash["Variables"] = $Variables | Get-Variable -ErrorAction Ignore
        }

        $data = [pscustomobject]$hash

        try { $data | Export-Clixml -Path $filePathXml -ErrorAction Stop }
        catch {
            Stop-Function -Message "Failed to export dump to file!" -ErrorRecord $_ -Target $filePathXml
            return
        }

        try { Compress-Archive -Path $filePathXml -DestinationPath $filePathZip -ErrorAction Stop }
        catch {
            Stop-Function -Message "Failed to pack dump-file into a zip archive. Please do so manually before submitting the results as the unpacked xml file will be rather large." -ErrorRecord $_ -Target $filePathZip
            return
        }

        Remove-Item -Path $filePathXml -ErrorAction Ignore
    }
    END {
        Write-Message -Level InternalComment -Message "Ending"
    }
}
tools\dbatools\functions\New-DbaXESession.ps1
function New-DbaXESession {
    <#
        .SYNOPSIS
            Creates a new XESession object - for the dogged.

        .DESCRIPTION
            Creates a new XESession object - for the dogged (very manual, Import-DbaXESession is recommended). See the following for more info:

            https://docs.microsoft.com/en-us/sql/relational-databases/extended-events/use-the-powershell-provider-for-extended-events

        .PARAMETER SqlInstance
            Target SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Name
            The Name of the session to be created.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/New-DbaXESession

        .EXAMPLE
            $session = New-DbaXESession -SqlInstance sql2017 -Name XeSession_Test
            $event = $session.AddEvent("sqlserver.file_written")
            $event.AddAction("package0.callstack")
            $session.Create()

            Returns a new XE Session object from sql2017 then adds an event, an action then creates it.

    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory)]
        [string]$Name,
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 11
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $SqlConn = $server.ConnectionContext.SqlConnectionObject
            $SqlStoreConnection = New-Object Microsoft.SqlServer.Management.Sdk.Sfc.SqlStoreConnection $SqlConn
            $store = New-Object  Microsoft.SqlServer.Management.XEvent.XEStore $SqlStoreConnection

            $store.CreateSession($Name)
        }
    }
}
tools\dbatools\functions\New-DbaXESmartCsvWriter.ps1
function New-DbaXESmartCsvWriter {
    <#
        .SYNOPSIS
            This Response type is used to write Extended Events to a CSV file.

        .DESCRIPTION
            This Response type is used to write Extended Events to a CSV file.

        .PARAMETER OutputFile
            Specifies the path to the output CSV file.

        .PARAMETER Overwrite
            Specifies whether any existiting file should be overwritten or not.

        .PARAMETER OutputColumn
            Specifies the list of columns to output from the events. XESmartTarget will capture in memory and write to the target table only the columns (fields or targets) that are present in this list.

            Fields and actions are matched in a case-sensitive manner.

            Expression columns are supported. Specify a column with ColumnName AS Expression to add an expression column (Example: Total AS Reads + Writes)

        .PARAMETER Event
            Specifies a list of events to be processed (with others being ignored. By default, all events are processed.

        .PARAMETER Filter
            Specifies a filter expression in the same form as you would use in the WHERE clause of a SQL query.

            Example: duration > 10000 AND cpu_time > 10000

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
            SmartTarget: by Gianluca Sartori (@spaghettidba)

        .LINK
            https://dbatools.io/New-DbaXESmartCsvWriter
            https://github.com/spaghettidba/XESmartTarget/wiki

        .EXAMPLE
            $columns = "cpu_time", "duration", "physical_reads", "logical_reads", "writes", "row_count"
            $response = New-DbaXESmartCsvWriter -OutputFile c:\temp\workload.csv -OutputColumn $columns -OverWrite -Event "sql_batch_completed"
            Start-DbaXESmartTarget -SqlInstance localhost\sql2017 -Session "Profiler Standard" -Responder $response

            Writes Extended Events to the file "C:\temp\workload.csv".
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory)]
        [string]$OutputFile,
        [switch]$Overwrite,
        [string[]]$Event,
        [string[]]$OutputColumn,
        [string]$Filter,
        [switch]$EnableException
    )

    begin {
        try {
            Add-Type -Path "$script:PSModuleRoot\bin\XESmartTarget\XESmartTarget.Core.dll" -ErrorAction Stop
        }
        catch {
            Stop-Function -Message "Could not load XESmartTarget.Core.dll" -ErrorRecord $_ -Target "XESmartTarget"
            return
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }

        try {
            $writer = New-Object -TypeName XESmartTarget.Core.Responses.CsvAppenderResponse
            $writer.OutputFile = $OutputFile
            $writer.OverWrite = $Overwrite
            if (Test-Bound -ParameterName "Event") {
                $writer.Events = $Event
            }
            if (Test-Bound -ParameterName "OutputColumn") {
                $writer.OutputColumns = $OutputColumn
            }
            if (Test-Bound -ParameterName "Filter") {
                $writer.Filter = $Filter
            }
            $writer
        }
        catch {
            Stop-Function -Message "Failure" -ErrorRecord $_ -Target "XESmartTarget" -Continue
        }
    }
}
tools\dbatools\functions\New-DbaXESmartEmail.ps1
function New-DbaXESmartEmail {
    <#
        .SYNOPSIS
            This Response type can be used to send an email each time an event is captured.

        .DESCRIPTION
            This Response type can be used to send an email each time an event is captured.

        .PARAMETER SmtpServer
            Address of the SMTP server for outgoing mail.

        .PARAMETER Sender
            Sender's email address.

        .PARAMETER To
            Address of the To recipient(s).

        .PARAMETER Cc
            Address of the Cc recipient(s).

        .PARAMETER Bcc
            Address of the Bcc recipient(s).

        .PARAMETER Credential
            Credential object containing username and password used to authenticate on the SMTP server. When blank, no authentication is performed.

        .PARAMETER Subject
            Subject of the mail message. Accepts placeholders in the text.

            Placeholders are in the form {PropertyName}, where PropertyName is one of the fields or actions available in the Event object.

            For instance, a valid Subject in a configuration file looks like this: "An event of name {Name} occurred at {collection_time}"

        .PARAMETER Body
            Body of the mail message. The body can be static text or any property taken from the underlying event. See Subject for a description of how placeholders work.

        .PARAMETER Attachment
            Data to attach to the email message. At this time, it can be any of the fields/actions of the underlying event. The data from the field/action is attached to the message as an ASCII stream. A single attachment is supported.

        .PARAMETER AttachmentFileName
            File name to assign to the attachment.

        .PARAMETER PlainText
            If this switch is enabled, the email will be sent in plain text. By default, HTML formatting is used.

        .PARAMETER Event
            Each Response can be limited to processing specific events, while ignoring all the other ones. When this attribute is omitted, all events are processed.

        .PARAMETER Filter
            You can specify a filter expression by using this attribute. The filter expression is in the same form that you would use in a SQL query. For example, a valid example looks like this: duration > 10000 AND cpu_time > 10000

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
            SmartTarget: by Gianluca Sartori (@spaghettidba)

        .LINK
            https://dbatools.io/New-DbaXESmartEmail
            https://github.com/spaghettidba/XESmartTarget/wiki

        .EXAMPLE
            $params = @{
                SmtpServer = "smtp.ad.local"
                To = "[email protected]"
                Sender = "[email protected]"
                Subject = "Query executed"
                Body = "Query executed at {collection_time}"
                Attachment = "batch_text"
                AttachmentFileName = "query.sql"
            }
            $emailresponse = New-DbaXESmartEmail @params
            Start-DbaXESmartTarget -SqlInstance sql2017 -Session querytracker -Responder $emailresponse

            Sends an email each time a querytracker event is captured.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory)]
        [string]$SmtpServer,
        [parameter(Mandatory)]
        [string]$Sender,
        [parameter(Mandatory)]
        [string[]]$To,
        [string[]]$Cc,
        [string[]]$Bcc,
        [pscredential]$Credential,
        [parameter(Mandatory)]
        [string]$Subject,
        [parameter(Mandatory)]
        [string]$Body,
        [string]$Attachment,
        [string]$AttachmentFileName,
        [string]$PlainText,
        [string[]]$Event,
        [string]$Filter,
        [switch]$EnableException
    )
    begin {
        try {
            Add-Type -Path "$script:PSModuleRoot\bin\XESmartTarget\XESmartTarget.Core.dll" -ErrorAction Stop
        }
        catch {
            Stop-Function -Message "Could not load XESmartTarget.Core.dll." -ErrorRecord $_ -Target "XESmartTarget"
            return
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }

        try {
            $email = New-Object -TypeName XESmartTarget.Core.Responses.EmailResponse
            $email.SmtpServer = $SmtpServer
            $email.Sender = $Sender
            $email.To = $To
            $email.Cc = $Cc
            $email.Bcc = $Bcc
            $email.Subject = $Subject
            $email.Body = $Body
            $email.Attachment = $Attachment
            $email.AttachmentFileName = $AttachmentFileName
            $email.HTMLFormat = ($PlainText -eq $false)
            if (Test-Bound -ParameterName "Event") {
                $email.Events = $Event
            }
            if (Test-Bound -ParameterName "Filter") {
                $email.Filter = $Filter
            }

            if ($Credential) {
                $email.UserName = $Credential.UserName
                $email.Password = $Credential.GetNetworkCredential().Password
            }

            $email
        }
        catch {
            Stop-Function -Message "Failure" -ErrorRecord $_ -Target "XESmartTarget" -Continue
        }
    }
}
tools\dbatools\functions\New-DbaXESmartQueryExec.ps1
function New-DbaXESmartQueryExec {
    <#
        .SYNOPSIS
            This Response type executes a T-SQL command against a target database whenever an event is recorded.

        .DESCRIPTION
            This Response type executes a T-SQL command against a target database whenever an event is recorded.

        .PARAMETER SqlInstance
            Target SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies the name of the database that contains the target table.

        .PARAMETER Query
            The T-SQL command to execute. This string can contain placeholders for properties taken from the events.

            Placeholders are in the form {PropertyName}, where PropertyName is one of the fields or actions available in the Event object.

        .PARAMETER Event
            Each Response can be limited to processing specific events, while ignoring all the other ones. When this attribute is omitted, all events are processed.

        .PARAMETER Filter
            You can specify a filter expression by using this attribute. The filter expression is in the same form that you would use in a SQL query. For example, a valid example looks like this: duration > 10000 AND cpu_time > 10000

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/New-DbaXESmartQueryExec
            https://github.com/spaghettidba/XESmartTarget/wiki

        .EXAMPLE
            $response = New-DbaXESmartQueryExec -SqlInstance sql2017 -Database dbadb -Query "update table set whatever = 1"
            Start-DbaXESmartTarget -SqlInstance sql2017 -Session deadlock_tracker -Responder $response

            Executes a T-SQL command against dbadb on sql2017 whenever a deadlock event is recorded.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string]$Database,
        [string]$Query,
        [switch]$EnableException,
        [string[]]$Event,
        [string]$Filter
    )
    begin {
        try {
            Add-Type -Path "$script:PSModuleRoot\XESmartTarget\XESmartTarget.Core.dll" -ErrorAction Stop
        }
        catch {
            Stop-Function -Message "Could not load XESmartTarget.Core.dll" -ErrorRecord $_ -Target "XESmartTarget"
            return
        }
    }
    process {
        if (Test-FunctionInterrupt) {
            return
        }

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 11
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $execute = New-Object -TypeName XESmartTarget.Core.Responses.ExecuteTSQLResponse
            $execute.ServerName = $server.Name
            $execute.DatabaseName = $Database
            $execute.TSQL = $Query

            if ($SqlCredential) {
                $execute.UserName = $SqlCredential.UserName
                $execute.Password = $SqlCredential.GetNetworkCredential().Password
            }

            if (Test-Bound -ParameterName "Event") {
                $execute.Events = $Event
            }
            if (Test-Bound -ParameterName "Filter") {
                $execute.Filter = $Filter
            }

            $execute
        }
    }
}
tools\dbatools\functions\New-DbaXESmartReplay.ps1
function New-DbaXESmartReplay {
    <#
        .SYNOPSIS
            This Response type can be used to replay execution related events to a target SQL Server instance.

        .DESCRIPTION
            This Response type can be used to replay execution related events to a target SQL Server instance. The events that you can replay are of the type sql_batch_completed and rpc_completed: all other events are ignored.

        .PARAMETER SqlInstance
            Target SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Name of the initial catalog to connect to. Statements will be replayed by changing database to the same database where the event was originally captured, so this property only controls the initial database to connect to.

        .PARAMETER Event
            Each Response can be limited to processing specific events, while ignoring all the other ones. When this attribute is omitted, all events are processed.

        .PARAMETER Filter
            Specifies a filter expression in the same form as you would use in the WHERE clause of a SQL query.

            Example: duration > 10000 AND cpu_time > 10000

        .PARAMETER DelaySeconds
            Specifies the duration of the delay in seconds.

        .PARAMETER ReplayIntervalSeconds
            Specifies the duration of the replay interval in seconds.

        .PARAMETER StopOnError
            If this switch is enabled, the replay will be stopped when the first error is encountered. By default, error messages are piped to the log and console output, and replay proceeds.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
            SmartTarget: by Gianluca Sartori (@spaghettidba)

        .LINK
            https://dbatools.io/New-DbaXESmartReplay
            https://github.com/spaghettidba/XESmartTarget/wiki

        .EXAMPLE
            $response = New-DbaXESmartReplay -SqlInstance sql2017 -Database planning
            Start-DbaXESmartTarget -SqlInstance sql2016 -Session loadrelay -Responder $response

            Replays events from sql2016 on sql2017 in the planning database. Returns a PowerShell job object.

            To see a list of all SmartTarget job objects, use Get-DbaXESmartTarget.

        .EXAMPLE
            $response = New-DbaXESmartReplay -SqlInstance sql2017 -Database planning
            Start-DbaXESmartTarget -SqlInstance sql2017 -Session 'Profiler Standard' -Responder $response -NotAsJob

            Replays events from the 'Profiler Standard' session on sql2016 to sql2017's planning database. Does not run as a job so you can see the raw output.

    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string]$Database,
        [string[]]$Event = "sql_batch_completed",
        [string]$Filter,
        [int]$DelaySeconds,
        [switch]$StopOnError,
        [int]$ReplayIntervalSeconds,
        [switch]$EnableException
    )
    begin {
        try {
            Add-Type -Path "$script:PSModuleRoot\bin\XESmartTarget\XESmartTarget.Core.dll" -ErrorAction Stop
        }
        catch {
            Stop-Function -Message "Could not load XESmartTarget.Core.dll" -ErrorRecord $_ -Target "XESmartTarget"
            return
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 11
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            try {
                $replay = New-Object -TypeName XESmartTarget.Core.Responses.ReplayResponse
                $replay.ServerName = $instance
                $replay.DatabaseName = $Database
                $replay.Events = $Event
                $replay.StopOnError = $StopOnError
                $replay.Filter = $Filter
                $replay.DelaySeconds = $DelaySeconds
                $replay.ReplayIntervalSeconds = $ReplayIntervalSeconds

                if ($SqlCredential) {
                    $replay.UserName = $SqlCredential.UserName
                    $replay.Password = $SqlCredential.GetNetworkCredential().Password
                }

                $replay
            }
            catch {
                $message = $_.Exception.InnerException.InnerException | Out-String
                Stop-Function -Message $message -Target "XESmartTarget" -Continue
            }
        }
    }
}
tools\dbatools\functions\New-DbaXESmartTableWriter.ps1
function New-DbaXESmartTableWriter {
    <#
        .SYNOPSIS
            This Response type is used to write Extended Events to a database table.

        .DESCRIPTION
            This Response type is used to write Extended Events to a database table. The events are temporarily stored in memory before being written to the database at regular intervals.

            The target table can be created manually upfront or you can let the TableAppenderResponse create a target table based on the fields and actions available in the events captured.

            The columns of the target table and the fields/actions of the events are mapped by name (case-sensitive).

       .PARAMETER SqlInstance
            Target SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies the name of the database that contains the target table.

        .PARAMETER Table
            Specifies the name of the target table.

        .PARAMETER AutoCreateTargetTable
            If this switch is enabled, XESmartTarget will infer the definition of the target table from the columns captured in the Extended Events session.

            If the target table already exists, it will not be recreated.

        .PARAMETER UploadIntervalSeconds
            Specifies the number of seconds XESmartTarget will keep the events in memory before dumping them to the target table. The default is 10 seconds.

        .PARAMETER OutputColumn
            Specifies the list of columns to output from the events. XESmartTarget will capture in memory and write to the target table only the columns (fields or targets) that are present in this list.

            Fields and actions are matched in a case-sensitive manner.

            Expression columns are supported. Specify a column with ColumnName AS Expression to add an expression column (Example: Total AS Reads + Writes)

        .PARAMETER Event
            Specifies a list of events to be processed (with others being ignored. By default, all events are processed.

        .PARAMETER Filter
            Specifies a filter expression in the same form as you would use in the WHERE clause of a SQL query.

            Example: duration > 10000 AND cpu_time > 10000

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
            SmartTarget: by Gianluca Sartori (@spaghettidba)

        .LINK
            https://dbatools.io/New-DbaXESmartTableWriter
            https://github.com/spaghettidba/XESmartTarget/wiki

        .EXAMPLE
            $columns = "cpu_time", "duration", "physical_reads", "logical_reads", "writes", "row_count", "batch_text"
            $response = New-DbaXESmartTableWriter -SqlInstance sql2017 -Database dbadb -Table deadlocktracker -OutputColumn $columns -Filter "duration > 10000"
            Start-DbaXESmartTarget -SqlInstance sql2017 -Session deadlock_tracker -Responder $response

            Writes Extended Events to the deadlocktracker table in dbadb on sql2017.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory)]
        [string]$Database,
        [parameter(Mandatory)]
        [string]$Table,
        [switch]$AutoCreateTargetTable,
        [int]$UploadIntervalSeconds = 10,
        [string[]]$Event,
        [string[]]$OutputColumn,
        [string]$Filter,
        [switch]$EnableException
    )
    begin {
        try {
            Add-Type -Path "$script:PSModuleRoot\bin\XESmartTarget\XESmartTarget.Core.dll" -ErrorAction Stop
        }
        catch {
            Stop-Function -Message "Could not load XESmartTarget.Core.dll" -ErrorRecord $_ -Target "XESmartTarget"
            return
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 11
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            try {
                $writer = New-Object -TypeName XESmartTarget.Core.Responses.TableAppenderResponse
                $writer.ServerName = $server.Name
                $writer.DatabaseName = $Database
                $writer.TableName = $Table
                $writer.AutoCreateTargetTable = $AutoCreateTargetTable
                $writer.UploadIntervalSeconds = $UploadIntervalSeconds
                if (Test-Bound -ParameterName "Event") {
                    $writer.Events = $Event
                }
                if (Test-Bound -ParameterName "OutputColumn") {
                    $writer.OutputColumns = $OutputColumn
                }
                if (Test-Bound -ParameterName "Filter") {
                    $writer.Filter = $Filter
                }
                $writer
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_ -Target "XESmartTarget" -Continue
            }
        }
    }
}
tools\dbatools\functions\Publish-DbaDacpac.ps1
function Publish-DbaDacpac {
    <#
        .SYNOPSIS
            The Publish-Database command takes a dacpac which is the output from an SSDT project and publishes it to a database. Changing the schema to match the dacpac and also to run any scripts in the dacpac (pre/post deploy scripts).

        .DESCRIPTION
            Deploying a dacpac uses the DacFx which historically needed to be installed on a machine prior to use. In 2016 the DacFx was supplied by Microsoft as a nuget package and this uses that nuget package.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Path
            Specifies the filesystem path to the DACPAC

        .PARAMETER PublishXml
            Specifies the publish profile which will include options and sqlCmdVariables.

        .PARAMETER Database
            Specifies the name of the database being published.

        .PARAMETER ConnectionString
            Specifies the connection string to the database you are upgrading. This is not required if SqlInstance is specified.

        .PARAMETER GenerateDeploymentScript
            If this switch is enabled, the publish script will be generated.

        .PARAMETER GenerateDeploymentReport
            If this switch is enabled, the publish XML report  will be generated.

        .PARAMETER OutputPath
            Specifies the filesystem path (directory) where output files will be generated.

        .PARAMETER ScriptOnly
            If this switch is enabled, only the change scripts will be generated.

        .PARAMETER IncludeSqlCmdVars
            If this switch is enabled, SqlCmdVars in publish.xml will have their values overwritten.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER DacFxPath
            Path to the dac dll. If this is ommited, then the version of dac dll which is packaged with dbatools is used.

        .NOTES
            Tags: Migration, Database, Dacpac
            Author: Richie lee (@bzzzt_io)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Publish-DbaDacpac

        .EXAMPLE
            Publish-DbaDacpac -SqlInstance sql2017 -Database WideWorldImporters -Path C:\temp\sql2016-WideWorldImporters.dacpac -PublishXml C:\temp\sql2016-WideWorldImporters-publish.xml

            Updates WideWorldImporters on sql2017 from the sql2016-WideWorldImporters.dacpac using the sql2016-WideWorldImporters-publish.xml publish profile

        .EXAMPLE
            New-DbaPublishProfile -SqlInstance sql2016 -Database db2 -Path C:\temp
            Export-DbaDacpac -SqlInstance sql2016 -Database db2 | Publish-DbaDacpac -PublishXml C:\temp\sql2016-db2-publish.xml -Database db1, db2 -SqlInstance sql2017

            Creates a publish profile at C:\temp\sql2016-db2-publish.xml, exports the .dacpac to $home\Documents\sql2016-db2.dacpac
            then publishes it to the sql2017 server database db2
        
        .EXAMPLE
        $loc = "C:\Users\bob\source\repos\Microsoft.Data.Tools.Msbuild\lib\net46\Microsoft.SqlServer.Dac.dll"
        Publish-DbaDacpac -SqlInstance "local" -Database WideWorldImporters -Path C:\temp\WideWorldImporters.dacpac -PublishXml C:\temp\WideWorldImporters.publish.xml -DacFxPath $loc
  #>
    [CmdletBinding()]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [string]$Path,
        [Parameter(Mandatory)]
        [string]$PublishXml,
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [string[]]$Database,
        [string[]]$ConnectionString,
        [switch]$GenerateDeploymentScript,
        [switch]$GenerateDeploymentReport,
        [Switch]$ScriptOnly,
        [string]$OutputPath = "$home\Documents",
        [switch]$IncludeSqlCmdVars,
        [switch]$EnableException,
        [String]$DacFxPath
    )

    begin {
        if ((Test-Bound -Not -ParameterName SqlInstance) -and (Test-Bound -Not -ParameterName ConnectionString)) {
            Stop-Function -Message "You must specify either SqlInstance or ConnectionString."
        }
        if ((Test-Bound -ParameterName GenerateDeploymentScript) -or (Test-Bound -ParameterName GenerateDeploymentReport)) {
            $defaultcolumns = 'ComputerName', 'InstanceName', 'SqlInstance', 'Database', 'Dacpac', 'PublishXml', 'Result', 'DatabaseScriptPath', 'MasterDbScriptPath', 'DeploymentReport', 'DeployOptions', 'SqlCmdVariableValues'
        }
        else {
            $defaultcolumns = 'ComputerName', 'InstanceName', 'SqlInstance', 'Database', 'Dacpac', 'PublishXml', 'Result', 'DeployOptions', 'SqlCmdVariableValues'
        }
        if ((Test-Bound -ParameterName ScriptOnly) -and (Test-Bound -Not -ParameterName GenerateDeploymentScript) -and (Test-Bound -Not -ParameterName GenerateDeploymentScript)) {
            Stop-Function -Message "You must at least one of GenerateDeploymentScript or GenerateDeploymentReport when using ScriptOnly"
        }

        function Get-ServerName ($connstring) {
            $builder = New-Object System.Data.Common.DbConnectionStringBuilder
            $builder.set_ConnectionString($connstring)
            $instance = $builder['data source']

            if (-not $instance) {
                $instance = $builder['server']
            }

            return $instance.ToString().Replace('\', '-').Replace('(','').Replace(')','')
        }
        if (Test-Bound -Not -ParameterName 'DacfxPath'){
            $dacfxPath = "$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.Dac.dll"
        }

        if ((Test-Path $dacfxPath) -eq $false) {
            Stop-Function -Message 'No usable version of Dac Fx found.' -EnableException $EnableException
        }
        else {
            try {
                Add-Type -Path $dacfxPath
                Write-Message -Level Verbose -Message "Dac Fx loaded."
            }
            catch {
                Stop-Function -Message 'No usable version of Dac Fx found.' -EnableException $EnableException -ErrorRecord $_
            }
        }
    }

    process {
        if (Test-FunctionInterrupt) { return }

        if (-not (Test-Path -Path $Path)) {
            Stop-Function -Message "$Path not found!"
        }

        if (-not (Test-Path -Path $PublishXml)) {
            Stop-Function -Message "$PublishXml not found!"
        }

        foreach ($instance in $sqlinstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure." -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $ConnectionString += $server.ConnectionContext.ConnectionString.Replace('"', "'")
        }

        try {
            $dacPackage = [Microsoft.SqlServer.Dac.DacPackage]::Load($Path)
        }
        catch {
            Stop-Function -Message "Could not load package." -ErrorRecord $_
        }

        try {
            $dacProfile = [Microsoft.SqlServer.Dac.DacProfile]::Load($PublishXml)
        }
        catch {
            Stop-Function -Message "Could not load profile." -ErrorRecord $_
        }

        if ($IncludeSqlCmdVars) {
            Get-SqlCmdVars -SqlCommandVariableValues $dacProfile.DeployOptions.SqlCommandVariableValues
        }

        foreach ($connstring in $ConnectionString) {
            $cleaninstance = Get-ServerName $connstring
            $instance = $cleaninstance.ToString().Replace('--', '\')

            foreach ($dbname in $database) {
                if ($GenerateDeploymentScript -or $GenerateDeploymentReport) {
                    $timeStamp = (Get-Date).ToString("yyMMdd_HHmmss_f")
                    $DatabaseScriptPath = Join-Path $OutputPath "$cleaninstance-$dbname`_DeployScript_$timeStamp.sql"
                    $MasterDbScriptPath = Join-Path $OutputPath "$cleaninstance-$dbname`_Master.DeployScript_$timeStamp.sql"
                    $DeploymentReport = Join-Path $OutputPath "$cleaninstance-$dbname`_Result.DeploymentReport_$timeStamp.xml"
                }

                if ($connstring -notmatch 'Database=') {
                    $connstring = "$connstring;Database=$dbname"
                }

                try {
                    $dacServices = New-Object Microsoft.SqlServer.Dac.DacServices $connstring
                }
                catch {
                    Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $server -Continue
                }

                $options = @{
                    GenerateDeploymentScript = $GenerateDeploymentScript
                    GenerateDeploymentReport = $GenerateDeploymentReport
                    DatabaseScriptPath       = $DatabaseScriptPath
                    MasterDbScriptPath       = $MasterDbScriptPath
                    DeployOptions            = $dacProfile.DeployOptions
                }

                try {
                    $global:output = @()
                    Register-ObjectEvent -InputObject $dacServices -EventName "Message" -SourceIdentifier "msg" -Action { $global:output += $EventArgs.Message.Message } | Out-Null
                    if ($ScriptOnly) {
                        Write-Message -Level Verbose -Message "Generating script."
                        $result = $dacServices.Script($dacPackage, $dbname, $options)
                    }
                    else {
                        Write-Message -Level Verbose -Message "Executing Deployment."
                        $result = $dacServices.Publish($dacPackage, $dbname, $options)
                    }
                }
                catch [Microsoft.SqlServer.Dac.DacServicesException] {
                        Stop-Function -Message "Deployment failed" -ErrorRecord $_ -EnableException $true
                }
                finally {
                    Unregister-Event -SourceIdentifier "msg"
                    if ($GenerateDeploymentReport) {
                        $result.DeploymentReport | Out-File $DeploymentReport
                        Write-Message -Level Verbose -Message "Deployment Report - $DeploymentReport."
                    }
                    if ($GenerateDeploymentScript) {
                        Write-Message -Level Verbose -Message "Database change script - $DatabaseScriptPath."
                        if ((Test-Path $MasterDbScriptPath)) {
                            Write-Message -Level Verbose -Message "Master database change script - $($result.MasterDbScript)."
                        }
                    }
                    $resultoutput = ($global:output -join "`r`n" | Out-String).Trim()
                    if ($resultoutput -match "Failed" -and ($GenerateDeploymentReport -or $GenerateDeploymentScript)) {
                        Write-Message -Level Warning -Message "Seems like the attempt to publish/script may have failed. If scripts have not generated load dacpac into Visual Studio to check SQL is valid."
                    }
                    $server = [dbainstance]$instance
                    $deployOptions = $dacProfile.DeployOptions | Select-Object -Property * -ExcludeProperty "SqlCommandVariableValues"
                    [pscustomobject]@{
                        ComputerName         = $server.ComputerName
                        InstanceName         = $server.InstanceName
                        SqlInstance          = $server.FullName
                        Database             = $dbname
                        Result               = $resultoutput
                        Dacpac               = $Path
                        PublishXml           = $PublishXml
                        ConnectionString     = $connstring
                        DatabaseScriptPath   = $DatabaseScriptPath
                        MasterDbScriptPath   = $MasterDbScriptPath
                        DeploymentReport     = $DeploymentReport
                        DeployOptions        = $deployOptions
                        SqlCmdVariableValues = $dacProfile.DeployOptions.SqlCommandVariableValues.Keys

                    } | Select-DefaultView -Property $defaultcolumns
                }
            }
        }
    }
}
tools\dbatools\functions\Read-DbaAuditFile.ps1
function Read-DbaAuditFile {
    <#
        .SYNOPSIS
            Read Audit details from a sqlaudit file.

        .DESCRIPTION
            Read Audit details from a sqlaudit file.

        .PARAMETER Path
            The path to the sqlaudit file. This is relative to the computer executing the command. UNC paths are supported.

        .PARAMETER Exact
            If this switch is enabled, only an exact search will be used for the Path. By default, this command will add a wildcard to the Path because Eventing uses the file name as a template and adds characters.

        .PARAMETER Raw
            If this switch is enabled, the Microsoft.SqlServer.XEvent.Linq.PublishedEvent enumeration object will be returned.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, Audit
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Read-DbaAuditFile

        .EXAMPLE
            Read-DbaAuditFile -Path C:\temp\logins.sqlaudit

            Returns events from C:\temp\logins.sqlaudit.

        .EXAMPLE
            Get-ChildItem C:\temp\audit\*.sqlaudit | Read-DbaAuditFile

            Returns events from all .sqlaudit files in C:\temp\audit.

        .EXAMPLE
            Get-DbaServerAudit -SqlInstance sql2014 -Audit LoginTracker | Read-DbaAuditFile

            Reads remote Audit details by accessing the file over the admin UNC share.

    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias('FullName')]
        [object[]]$Path,
        [switch]$Exact,
        [switch]$Raw,
        [switch]$EnableException
    )
    process {
        foreach ($file in $path) {
            # in order to ensure CSV gets all fields, all columns will be
            # collected and output in the first (all all subsequent) object
            $columns = @("name", "timestamp")

            if ($file -is [System.String]) {
                $currentfile = $file
                $manualadd = $true
            }
            elseif ($file -is [System.IO.FileInfo]) {
                $currentfile = $file.FullName
                $manualadd = $true
            }
            else {
                if ($file -isnot [Microsoft.SqlServer.Management.Smo.Audit]) {
                    Stop-Function -Message "Unsupported file type."
                    return
                }

                if ($file.FullName.Length -eq 0) {
                    Stop-Function -Message "This Audit does not have an associated file."
                    return
                }

                $instance = [dbainstance]$file.ComputerName

                if ($instance.IsLocalHost) {
                    $currentfile = $file.FullName
                }
                else {
                    $currentfile = $file.RemoteFullName
                }
            }

            if (-not $Exact) {
                $currentfile = $currentfile.Replace('.sqlaudit', '*.sqlaudit')

                if ($currentfile -notmatch "sqlaudit") {
                    $currentfile = "$currentfile*.sqlaudit"
                }
            }

            $accessible = Test-Path -Path $currentfile
            $whoami = whoami

            if (-not $accessible) {
                if ($file.Status -eq "Stopped") { continue }
                Stop-Function -Continue -Message "$currentfile cannot be accessed from $($env:COMPUTERNAME). Does $whoami have access?"
            }

            if ($raw) {
                return New-Object Microsoft.SqlServer.XEvent.Linq.QueryableXEventData($currentfile)
            }

            $enum = New-Object Microsoft.SqlServer.XEvent.Linq.QueryableXEventData($currentfile)
            $newcolumns = ($enum.Fields.Name | Select-Object -Unique)

            $actions = ($enum.Actions.Name | Select-Object -Unique)
            foreach ($action in $actions) {
                $newcolumns += ($action -Split '\.')[-1]
            }

            $newcolumns = $newcolumns | Sort-Object
            $columns = ($columns += $newcolumns) | Select-Object -Unique

            # Make it selectable, otherwise it's a weird enumeration
            foreach ($event in (New-Object Microsoft.SqlServer.XEvent.Linq.QueryableXEventData($currentfile))) {
                $hash = [ordered]@{ }

                foreach ($column in $columns) {
                    $null = $hash.Add($column, $event.$column)
                }

                foreach ($action in $event.Actions) {
                    $hash[$action.Name] = $action.Value
                }

                foreach ($field in $event.Fields) {
                    $hash[$field.Name] = $field.Value
                }

                [pscustomobject]$hash
            }
        }
    }
}
tools\dbatools\functions\Read-DbaBackupHeader.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#

function Read-DbaBackupHeader {
    <#
        .SYNOPSIS
            Reads and displays detailed information about a SQL Server backup.

        .DESCRIPTION
            Reads full, differential and transaction log backups. An online SQL Server is required to parse the backup files and the path specified must be relative to that SQL Server.

        .PARAMETER SqlInstance
            The SQL Server instance to use for parsing the backup files.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Path
            Path to SQL Server backup file. This can be a full, differential or log backup file. Accepts valid filesystem paths and URLs.

        .PARAMETER Simple
            If this switch is enabled, fewer columns are returned, giving an easy overview.

        .PARAMETER FileList
            If this switch is enabled, detailed information about the files within the backup is returned.

        .PARAMETER AzureCredential
            Name of the SQL Server credential that should be used for Azure storage access.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message. This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: DisasterRecovery, Backup, Restore
            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Read-DbaBackupHeader

        .EXAMPLE
            Read-DbaBackupHeader -SqlInstance sql2016 -Path S:\backups\mydb\mydb.bak

            Logs into sql2016 using Windows authentication and reads the local file on sql2016, S:\backups\mydb\mydb.bak.

            If you are running this command on a workstation and connecting remotely, remember that sql2016 cannot access files on your own workstation.

        .EXAMPLE
            Read-DbaBackupHeader -SqlInstance sql2016 -Path \\nas\sql\backups\mydb\mydb.bak, \\nas\sql\backups\otherdb\otherdb.bak

            Logs into sql2016 and reads two backup files - mydb.bak and otherdb.bak. The SQL Server service account must have rights to read this file.

        .EXAMPLE
            Read-DbaBackupHeader -SqlInstance . -Path C:\temp\myfile.bak -Simple

            Logs into the local workstation (or computer) and shows simplified output about C:\temp\myfile.bak. The SQL Server service account must have rights to read this file.

        .EXAMPLE
            $backupinfo = Read-DbaBackupHeader -SqlInstance . -Path C:\temp\myfile.bak
            $backupinfo.FileList

            Displays detailed information about each of the datafiles contained in the backupset.

        .EXAMPLE
            Read-DbaBackupHeader -SqlInstance . -Path C:\temp\myfile.bak -FileList

            Also returns detailed information about each of the datafiles contained in the backupset.

        .EXAMPLE
            "C:\temp\myfile.bak", "\backupserver\backups\myotherfile.bak" | Read-DbaBackupHeader -SqlInstance sql2016

            Similar to running Read-DbaBackupHeader -SqlInstance sql2016 -Path "C:\temp\myfile.bak", "\backupserver\backups\myotherfile.bak"

        .EXAMPLE
            Get-ChildItem \\nas\sql\*.bak | Read-DbaBackupHeader -SqlInstance sql2016

            Gets a list of all .bak files on the \\nas\sql share and reads the headers using the server named "sql2016". This means that the server, sql2016, must have read access to the \\nas\sql share.

        .EXAMPLE
            Read-DbaBackupHeader -Path https://dbatoolsaz.blob.core.windows.net/azbackups/restoretime/restoretime_201705131850.bak
            -AzureCredential AzureBackupUser

            Gets the backup header information from the SQL Server backup file stored at https://dbatoolsaz.blob.core.windows.net/azbackups/restoretime/restoretime_201705131850.bak on Azure
    #>
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", '')]
    <# AzureCredential is utilized in this command is not a formal Credential object. #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance]$SqlInstance,
        [PsCredential]$SqlCredential,
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [object[]]$Path,
        [switch]$Simple,
        [switch]$FileList,
        [string]$AzureCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        foreach($p in $path) {
            if ([System.IO.Path]::GetExtension($p).Length -eq 0) {
                Stop-Function -Message "Path ($p) should be a file, not a folder" -Category InvalidArgument
                return
            }
        }
        Write-Message -Level InternalComment -Message "Starting reading headers"
        try {
            $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            return
        }
        $getHeaderScript = {
            Param (
                $SqlInstance,
                $Path,
                $DeviceType,
                $AzureCredential
            )
            #Copy existing connection to create an independent TSQL session
            $server = New-Object Microsoft.SqlServer.Management.Smo.Server $SqlInstance.ConnectionContext.Copy()
            $restore = New-Object Microsoft.SqlServer.Management.Smo.Restore

            if ($DeviceType -eq 'URL') {
                $restore.CredentialName = $AzureCredential
            }

            $device = New-Object Microsoft.SqlServer.Management.Smo.BackupDeviceItem $Path, $DeviceType
            $restore.Devices.Add($device)
            $dataTable = $restore.ReadBackupHeader($server)

            $null = $dataTable.Columns.Add("FileList", [object])

            $mb = $dataTable.Columns.Add("BackupSizeMB", [int])
            $mb.Expression = "BackupSize / 1024 / 1024"
            $gb = $dataTable.Columns.Add("BackupSizeGB")
            $gb.Expression = "BackupSizeMB / 1024"

            if ($null -eq $dataTable.Columns['CompressedBackupSize']) {
                $formula = "0"
            }
            else {
                $formula = "CompressedBackupSize / 1024 / 1024"
            }

            $cmb = $dataTable.Columns.Add("CompressedBackupSizeMB", [int])
            $cmb.Expression = $formula
            $cgb = $dataTable.Columns.Add("CompressedBackupSizeGB")
            $cgb.Expression = "CompressedBackupSizeMB / 1024"

            $null = $dataTable.Columns.Add("SqlVersion")

            $null = $dataTable.Columns.Add("BackupPath")

            foreach ($row in $dataTable) {
                $row.BackupPath = $Path
                $restore.FileNumber = $row.Position
                <# Select-Object does a quick and dirty conversion from datatable to PS object #>
                $row.FileList = $restore.ReadFileList($server) | Select-Object *
            }
            $dataTable
        }
    }

    process {
        if (Test-FunctionInterrupt) { return }

        #Extract fullnames from the file system objects
        $pathStrings = @()
        foreach ($pathItem in $Path) {
            if ($null -ne $pathItem.FullName) {
                $pathStrings += $pathItem.FullName
            }
            else {
                $pathStrings += $pathItem
            }
        }
        #Group by filename
        $pathGroup = $pathStrings | Group-Object -NoElement | Select-Object -ExpandProperty Name

        $pathCount = ($pathGroup | Measure-Object).Count
        Write-Message -Level Verbose -Message "$pathCount unique files to scan."
        Write-Message -Level Verbose -Message "Checking accessibility for all the files."

        $testPath = Test-DbaSqlPath -SqlInstance $server -Path $pathGroup

        #Setup initial session state
        $InitialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
        #Create Runspace pool, min - 1, max - 10 sessions: there is internal SQL Server queue for the restore operations. 10 threads seem to perform best
        $runspacePool = [runspacefactory]::CreateRunspacePool(1, 10, $InitialSessionState, $Host)
        $runspacePool.Open()

        $threads = @()

        foreach ($file in $pathGroup) {
            if ($file -like 'http*') {
                $deviceType = 'URL'
            }
            else {
                $deviceType = 'FILE'
            }
            if ($pathCount -eq 1) {
                $fileExists = $testPath
            }
            else {
                $fileExists = ($testPath | Where-Object FilePath -eq $file).FileExists
            }
            if ($fileExists -or $deviceType -eq 'URL') {
                #Create parameters hashtable
                $argsRunPool = @{
                    SqlInstance     = $server
                    Path            = $file
                    AzureCredential = $AzureCredential
                    DeviceType      = $deviceType
                }
                Write-Message -Level Verbose -Message "Scanning file $file."
                #Create new runspace thread
                $thread = [powershell]::Create()
                $thread.RunspacePool = $runspacePool
                $thread.AddScript($getHeaderScript) | Out-Null
                $thread.AddParameters($argsRunPool) | Out-Null
                #Start the thread
                $handle = $thread.BeginInvoke()
                $threads += [pscustomobject]@{
                    handle      = $handle
                    thread      = $thread
                    file        = $file
                    deviceType  = $deviceType
                    isRetrieved = $false
                    started     = Get-Date
                }
            }
            else {
                Write-Message -Level Warning -Message "File $file does not exist or access denied. The SQL Server service account may not have access to the source directory."
            }
        }
        #receive runspaces
        while ($threads | Where-Object { $_.isRetrieved -eq $false }) {
            $totalThreads = ($threads | Measure-Object).Count
            $totalRetrievedThreads = ($threads | Where-Object { $_.isRetrieved -eq $true } | Measure-Object).Count
            Write-Progress -Id 1 -Activity Updating -Status 'Progress' -CurrentOperation "Scanning Restore headers: $totalRetrievedThreads/$totalThreads" -PercentComplete ($totalRetrievedThreads / $totalThreads * 100)
            foreach ($thread in ($threads | Where-Object { $_.isRetrieved -eq $false })) {
                if ($thread.Handle.IsCompleted) {
                    $dataTable = $thread.thread.EndInvoke($thread.handle)
                    $thread.isRetrieved = $true
                    #Check if thread had any errors
                    if ($thread.thread.HadErrors) {
                        if ($thread.deviceType -eq 'FILE') {
                            Stop-Function -Message "Problem found with $($thread.file)." -Target $thread.file -ErrorRecord $thread.thread.Streams.Error -Continue
                        }
                        else {
                            Stop-Function -Message "Unable to read $($thread.file), check credential $AzureCredential and network connectivity." -Target $thread.file -ErrorRecord $thread.thread.Streams.Error -Continue
                        }
                    }
                    #Process the result of this thread

                    $dbVersion = $dataTable[0].DatabaseVersion
                    $SqlVersion = (Convert-DbVersionToSqlVersion $dbVersion)
                    foreach ($row in $dataTable) {
                        $row.SqlVersion = $SqlVersion
                        if ($row.BackupName -eq "*** INCOMPLETE ***") {
                            Stop-Function -Message "$($thread.file) appears to be from a new version of SQL Server than $SqlInstance, skipping" -Target $thread.file -Continue
                        }
                    }
                    if ($Simple) {
                        $dataTable | Select-Object DatabaseName, BackupFinishDate, RecoveryModel, BackupSizeMB, CompressedBackupSizeMB, DatabaseCreationDate, UserName, ServerName, SqlVersion, BackupPath
                    }
                    elseif ($FileList) {
                        $dataTable.filelist
                    }
                    else {
                        $dataTable
                    }

                    $thread.thread.Dispose()
                }
            }
            Start-Sleep -Milliseconds 500
        }
        #Close the runspace pool
        $runspacePool.Close()
    }
}
tools\dbatools\functions\Read-DbaTraceFile.ps1
function Read-DbaTraceFile {
    <#
        .SYNOPSIS
        Reads SQL Server trace files

        .DESCRIPTION
        Using the fn_trace_gettable function, a trace file is read and returned as a PowerShell object

        This function returns the whole of the trace file. The information is presented in the format that the trace subsystem uses.

        .PARAMETER SqlInstance
        The target SQL Server instance

        .PARAMETER SqlCredential
        Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Path
        Path to the trace file. This path is relative to the SQL Server instance.

        .PARAMETER Database
        Search for results only with specific DatabaseName. Uses IN for comparisons.

        .PARAMETER Login
        Search for results only with specific Logins. Uses IN for comparisons.

        .PARAMETER Spid
        Search for results only with specific Spids. Uses IN for comparisons.

        .PARAMETER EventClass
        Search for results only with specific EventClasses. Uses IN for comparisons.

        .PARAMETER ObjectType
        Search for results only with specific ObjectTypes. Uses IN for comparisons.

        .PARAMETER Error
        Search for results only with specific Errors. Uses IN for comparisons.

        .PARAMETER EventSequence
        Search for results only with specific EventSequences. Uses IN for comparisons.

        .PARAMETER TextData
        Search for results only with specific TextData. Uses LIKE for comparisons.

        .PARAMETER ApplicationName
        Search for results only with specific ApplicationNames. Uses LIKE for comparisons.

        .PARAMETER ObjectName
        Search for results only with specific ObjectNames. Uses LIKE for comparisons.

        .PARAMETER Where
        Custom where clause - use without the word "WHERE". Here are the available columns:

        TextData
        BinaryData
        DatabaseID
        TransactionID
        LineNumber
        NTUserName
        NTDomainName
        HostName
        ClientProcessID
        ApplicationName
        LoginName
        SPID
        Duration
        StartTime
        EndTime
        Reads
        Writes
        CPU
        Permissions
        Severity
        EventSubClass
        ObjectID
        Success
        IndexID
        IntegerData
        ServerName
        EventClass
        ObjectType
        NestLevel
        State
        Error
        Mode
        Handle
        ObjectName
        DatabaseName
        FileName
        OwnerName
        RoleName
        TargetUserName
        DBUserName
        LoginSid
        TargetLoginName
        TargetLoginSid
        ColumnPermissions
        LinkedServerName
        ProviderName
        MethodName
        RowCounts
        RequestID
        XactSequence
        EventSequence
        BigintData1
        BigintData2
        GUID
        IntegerData2
        ObjectID2
        Type
        OwnerID
        ParentName
        IsSystem
        Offset
        SourceDatabaseID
        SqlHandle
        SessionLoginName
        PlanHandle
        GroupID

        .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
        Tags: Security, Trace
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
        Read-DbaTraceFile -SqlInstance sql2016 -Database master, tempdb -Path C:\traces\big.trc

        Reads the tracefile C:\traces\big.trc, stored on the sql2016 sql server. Filters only results that have master or tempdb as the DatabaseName.

        .EXAMPLE
        Read-DbaTraceFile -SqlInstance sql2016 -Database master, tempdb -Path C:\traces\big.trc -TextData 'EXEC SP_PROCOPTION'

        Reads the tracefile C:\traces\big.trc, stored on the sql2016 sql server.
        Filters only results that have master or tempdb as the DatabaseName and that have 'EXEC SP_PROCOPTION' somewhere in the text.

        .EXAMPLE
        Read-DbaTraceFile -SqlInstance sql2016 -Path C:\traces\big.trc -Where "LinkedServerName = 'myls' and StartTime > '5/30/2017 4:27:52 PM'"

        Reads the tracefile C:\traces\big.trc, stored on the sql2016 sql server.
        Filters only results where LinkServerName = myls and StartTime is greater than '5/30/2017 4:27:52 PM'.

        .EXAMPLE
        Get-DbaTrace -SqlInstance sql2014 | Read-DbaTraceFile

        Reads every trace file on sql2014

#>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory, ValueFromPipelineByPropertyName)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [parameter(ValueFromPipelineByPropertyName)]
        [PSCredential]$SqlCredential,
        [parameter(ValueFromPipelineByPropertyName)]
        [string[]]$Path,
        [string[]]$Database,
        [string[]]$Login,
        [int[]]$Spid,
        [string[]]$EventClass,
        [string[]]$ObjectType,
        [int[]]$Error,
        [int[]]$EventSequence,
        [string[]]$TextData,
        [string[]]$ApplicationName,
        [string[]]$ObjectName,
        [string]$Where,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        if ($where) {
            $Where = "where $where"
        }
        elseif ($Database -or $Login -or $Spid -or $ApplicationName -or $EventClass -or $ObjectName -or $ObjectType -or $EventSequence -or $Error) {

            $tempwhere = @()

            if ($Database) {
                $where = $database -join "','"
                $tempwhere += "databasename in ('$where')"
            }

            if ($Login) {
                $where = $Login -join "','"
                $tempwhere += "LoginName in ('$where')"
            }

            if ($Spid) {
                $where = $Spid -join ","
                $tempwhere += "Spid in ($where)"
            }

            if ($EventClass) {
                $where = $EventClass -join ","
                $tempwhere += "EventClass in ($where)"
            }

            if ($ObjectType) {
                $where = $ObjectType -join ","
                $tempwhere += "ObjectType in ($where)"
            }

            if ($Error) {
                $where = $Error -join ","
                $tempwhere += "Error in ($where)"
            }

            if ($EventSequence) {
                $where = $EventSequence -join ","
                $tempwhere += "EventSequence in ($where)"
            }

            if ($TextData) {
                $where = $TextData -join "%','%"
                $tempwhere += "TextData like ('%$where%')"
            }

            if ($ApplicationName) {
                $where = $ApplicationName -join "%','%"
                $tempwhere += "ApplicationName like ('%$where%')"
            }

            if ($ObjectName) {
                $where = $ObjectName -join "%','%"
                $tempwhere += "ObjectName like ('%$where%')"
            }

            $tempwhere = $tempwhere -join " and "
            $Where = "where $tempwhere"
        }
    }
    process {
        foreach ($instance in $sqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                return
            }

            if (Test-Bound -Parameter Path) {
                $currentpath = $path
            }
            else {
                $currentpath = $server.ConnectionContext.ExecuteScalar("Select path from sys.traces where is_default = 1")
            }

            foreach ($file in $currentpath) {
                Write-Message -Level Verbose -Message "Parsing $file"

                $exists = Test-DbaSqlPath -SqlInstance $server -Path $file

                if (!$exists) {
                    Write-Message -Level Warning -Message "Path does not exist" -Target $file
                    Continue
                }

                $sql = "select SERVERPROPERTY('MachineName') AS ComputerName,
                ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
                SERVERPROPERTY('ServerName') AS SqlInstance,
                 * FROM [fn_trace_gettable]('$file', DEFAULT) $Where"

                try {
                    $server.Query($sql)
                }
                catch {
                    Stop-Function -Message "Error returned from SQL Server: $_" -Target $server -InnerErrorRecord $_
                }
            }
        }
    }
}
tools\dbatools\functions\Read-DbaTransactionLog.ps1
function Read-DbaTransactionLog {
    <#
.SYNOPSIS
Reads the live Transaction log from specified SQL Server Database

.DESCRIPTION
Using the fn_dblog function, the live transaction log is read and returned as a PowerShell object

This function returns the whole of the log. The information is presented in the format that the logging subsystem uses.

A soft limit of 0.5GB of log as been implemented. This is based on testing. This limit can be overridden
at the users request, but please be aware that this may have an impact on your target databases and on the
system running this function

.PARAMETER SqlInstance
A SQL Server instance to connect to

.PARAMETER SqlCredential
A credential to use to connect to the SQL Instance rather than using Windows Authentication

.PARAMETER Database
Database to read the transaction log of

.PARAMETER IgnoreLimit
Switch to indicate that you wish to bypass the recommended limits of the function

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Database, Log, LogFile
Author: Stuart Moore (@napalmgram), stuart-moore.com

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
$Log = Read-DbaTransactionLog -SqlInstance sql2016 -Database MyDatabase

Will read the contents of the transaction log of MyDatabase on SQL Server Instance sql2016 into the local PowerShell object $Log

.EXAMPLE
$Log = Read-DbaTransactionLog -SqlInstance sql2016 -Database MyDatabase -IgnoreLimit

Will read the contents of the transaction log of MyDatabase on SQL Server Instance sql2016 into the local PowerShell object $Log, ignoring the recommnedation of not returning more that 0.5GB of log

#>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    Param (
        [parameter(Position = 0, Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory = $true)]
        [object]$Database,
        [Switch]$IgnoreLimit,
        [Alias('Silent')]
        [switch]$EnableException
    )

    try {
        $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
    }
    catch {
        Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
        return
    }

    if (-not $server.databases[$Database]) {
        Stop-Function -Message "$Database does not exist"
        return
    }

    if ($server.databases[$Database].Status -ne 'Normal') {
        Stop-Function -Message "$Database is not in a normal State, command will not run."
        return
    }

    if ($IgnoreLimit) {
        Write-Message -Level Verbose -Message "Please be aware that ignoring the recommended limits may impact on the performance of the SQL Server database and the calling system"
    }
    else {
        #Warn if more than 0.5GB of live log. Dodgy conversion as SMO returns the value in an unhelpful format :(
        $SqlSizeCheck = "select
                                sum(FileProperty(sf.name,'spaceused')*8/1024) as 'SizeMb'
                                from sys.sysfiles sf
                                where CONVERT(INT,sf.status & 0x40) / 64=1"
        $TransLogSize = $server.Query($SqlSizeCheck, $Database)
        if ($TransLogSize.SizeMb -ge 500) {
            Stop-Function -Message "$Database has more than 0.5 Gb of live log data, returning this may have an impact on the database and the calling system. If you wish to proceed please rerun with the -IgnoreLimit switch"
            return
        }
    }

    $sql = "select * from fn_dblog(NULL,NULL)"
    Write-Message -Level Debug -Message $sql
    Write-Message -Level Verbose -Message "Starting Log retrieval"
    $server.Query($sql, $Database)

}
tools\dbatools\functions\Read-DbaXEFile.ps1
function Read-DbaXEFile {
    <#
        .SYNOPSIS
            Read XEvents from a xel or xem file.

        .DESCRIPTION
            Read XEvents from a xel or xem file.

        .PARAMETER Path
            The path to the xel or xem file. This is relative to the computer executing the command. UNC paths are supported.

        .PARAMETER Exact
            If this switch is enabled, only an exact search will be used for the Path. By default, this command will add a wildcard to the Path because Eventing uses the file name as a template and adds characters.

        .PARAMETER Raw
            If this switch is enabled, the Microsoft.SqlServer.XEvent.Linq.PublishedEvent enumeration object will be returned.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Read-DbaXEFile

        .EXAMPLE
            Read-DbaXEFile -Path C:\temp\deadocks.xel

            Returns events from C:\temp\deadocks.xel.

        .EXAMPLE
            Get-ChildItem C:\temp\xe\*.xel | Read-DbaXEFile

            Returns events from all .xel files in C:\temp\xe.

        .EXAMPLE
            Get-DbaXESession -SqlInstance sql2014 -Session deadlocks | Read-DbaXEFile

            Reads remote XEvents by accessing the file over the admin UNC share.

    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias('FullName')]
        [object[]]$Path,
        [switch]$Exact,
        [switch]$Raw,
        [switch][Alias('Silent')]
        $EnableException
    )
    process {
        foreach ($file in $path) {
            # in order to ensure CSV gets all fields, all columns will be
            # collected and output in the first (all all subsequent) object
            $columns = @("name", "timestamp")

            if ($file -is [System.String]) {
                $currentfile = $file
                $manualadd = $true
            }
            elseif ($file -is [System.IO.FileInfo]) {
                $currentfile = $file.FullName
                $manualadd = $true
            }
            else {
                if ($file -isnot [Microsoft.SqlServer.Management.XEvent.Session]) {
                    Stop-Function -Message "Unsupported file type."
                    return
                }

                if ($file.TargetFile.Length -eq 0) {
                    Stop-Function -Message "This session does not have an associated Target File."
                    return
                }

                $instance = [dbainstance]$file.ComputerName

                if ($instance.IsLocalHost) {
                    $currentfile = $file.TargetFile
                }
                else {
                    $currentfile = $file.RemoteTargetFile
                }
            }

            if (-not $Exact) {
                $currentfile = $currentfile.Replace('.xel', '*.xel')
                $currentfile = $currentfile.Replace('.xem', '*.xem')

                if ($currentfile -notmatch "xel" -and $currentfile -notmatch "xem") {
                    $currentfile =  "$currentfile*.xel"
                }
            }

            $accessible = Test-Path -Path $currentfile
            $whoami = whoami

            if (-not $accessible) {
                if ($file.Status -eq "Stopped") { continue }
                Stop-Function -Continue -Message "$currentfile cannot be accessed from $($env:COMPUTERNAME). Does $whoami have access?"
            }

            if ($raw) {
                return New-Object Microsoft.SqlServer.XEvent.Linq.QueryableXEventData($currentfile)
            }

            $enum = New-Object Microsoft.SqlServer.XEvent.Linq.QueryableXEventData($currentfile)
            $newcolumns = ($enum.Fields.Name | Select-Object -Unique)

            $actions = ($enum.Actions.Name | Select-Object -Unique)
            foreach ($action in $actions) {
                $newcolumns += ($action -Split '\.')[-1]
            }

            $newcolumns = $newcolumns | Sort-Object
            $columns = ($columns += $newcolumns) | Select-Object -Unique

            # Make it selectable, otherwise it's a weird enumeration
            foreach ($event in (New-Object Microsoft.SqlServer.XEvent.Linq.QueryableXEventData($currentfile))) {
                $hash = [ordered]@{ }

                foreach ($column in $columns) {
                    $null = $hash.Add($column, $event.$column)
                }

                foreach ($action in $event.Actions) {
                    $hash[$action.Name] = $action.Value
                }

                foreach ($field in $event.Fields) {
                    $hash[$field.Name] = $field.Value
                }

                [pscustomobject]$hash
            }
        }
    }
}
tools\dbatools\functions\Register-DbaConfig.ps1
function Register-DbaConfig {
    <#
        .SYNOPSIS
            Registers an existing configuration object in registry.

        .DESCRIPTION
            Registers an existing configuration object in registry.
            This allows simple persisting of settings across powershell consoles.
            It also can be used to generate a registry template, which can then be used to create policies.

        .PARAMETER Config
            The configuration object to write to registry.
            Can be retrieved using Get-DbaConfig.

        .PARAMETER FullName
            The full name of the setting to be written to registry.

        .PARAMETER Module
            The name of the module, whose settings should be written to registry.

        .PARAMETER Name
            Default: "*"
            Used in conjunction with the -Module parameter to restrict the number of configuration items written to registry.

        .PARAMETER Scope
            Default: UserDefault
            Who will be affected by this export how? Current user or all? Default setting or enforced?
            Legal values: UserDefault, UserMandatory, SystemDefault, SystemMandatory

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Config, Module
            Author: Friedrich Weinmann

        .EXAMPLE
            PS C:\> Get-DbaConfig message.* | Register-DbaConfig

            Retrieves all configuration items that that start with message. and registers them in registry for the current user.

        .EXAMPLE
            PS C:\> Register-DbaConfig -FullName "developer.mode.enable" -Scope SystemDefault

            Retrieves the configuration item "developer.mode.enable" and registers it in registry as the default setting for all users on this machine.

        .EXAMPLE
            PS C:\> Register-DbaConfig -Module message -Scope SystemMandatory

            Retrieves all configuration items of the module MyModule, then registers them in registry to enforce them for all users on the current system.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    Param (
        [Parameter(ParameterSetName = "Default", Position = 0, ValueFromPipeline = $true)]
        [Sqlcollaborative.Dbatools.Configuration.Config[]]
        $Config,

        [Parameter(ParameterSetName = "Default", Position = 0, ValueFromPipeline = $true)]
        [string[]]
        $FullName,

        [Parameter(Mandatory = $true, ParameterSetName = "Name", Position = 0)]
        [string]
        $Module,

        [Parameter(ParameterSetName = "Name", Position = 1)]
        [string]
        $Name = "*",

        [Sqlcollaborative.Dbatools.Configuration.ConfigScope]
        $Scope = "UserDefault",

        [switch]
        $EnableException
    )

    begin {
        $parSet = $PSCmdlet.ParameterSetName

        function Write-Config {
            [CmdletBinding()]
            Param (
                [Sqlcollaborative.Dbatools.Configuration.Config]
                $Config,

                [Sqlcollaborative.Dbatools.Configuration.ConfigScope]
                $Scope,

                [bool]
                $EnableException,

                [string]
                $FunctionName = (Get-PSCallStack)[0].Command
            )

            if (-not $Config -or ($Config.RegistryData -eq "<type not supported>")) {
                Stop-Function -Message "Invalid Input, cannot export $($Config.FullName), type not supported" -EnableException $EnableException -Category InvalidArgument -Target $Config -FunctionName $FunctionName #-ModuleName "PSFramework" -Tag "config", "fail"
                return
            }

            try {
                Write-Message -Level Verbose -Message "Registering $($Config.FullName) for $Scope" -Target $Config -FunctionName $FunctionName #-ModuleName "PSFramework" -Tag "Config"
                #region User Default
                if (1 -band $Scope) {
                    Ensure-RegistryPath -Path "HKCU:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\Config\Default" -ErrorAction Stop
                    Set-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\Config\Default" -Name $Config.FullName -Value $Config.RegistryData -ErrorAction Stop
                }
                #endregion User Default

                #region User Mandatory
                if (2 -band $Scope) {
                    Ensure-RegistryPath -Path "HKCU:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\Config\Enforced" -ErrorAction Stop
                    Set-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\Config\Enforced" -Name $Config.FullName -Value $Config.RegistryData -ErrorAction Stop
                }
                #endregion User Mandatory

                #region System Default
                if (4 -band $Scope) {
                    Ensure-RegistryPath -Path "HKLM:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\Config\Default" -ErrorAction Stop
                    Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\Config\Default" -Name $Config.FullName -Value $Config.RegistryData -ErrorAction Stop
                }
                #endregion System Default

                #region System Mandatory
                if (8 -band $Scope) {
                    Ensure-RegistryPath -Path "HKLM:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\Config\Enforced" -ErrorAction Stop
                    Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\Config\Enforced" -Name $Config.FullName -Value $Config.RegistryData -ErrorAction Stop
                }
                #endregion System Mandatory
            }
            catch {
                Stop-Function -Message "Failed to export $($Config.FullName), to scope $Scope" -EnableException $EnableException -Target $Config -ErrorRecord $_ -FunctionName $FunctionName #-ModuleName "PSFramework" -Tag "config", "fail"
                return
            }
        }

        function Ensure-RegistryPath {
            [CmdletBinding()]
            Param (
                [string]
                $Path
            )

            if (-not (Test-Path $Path)) {
                $null = New-Item $Path -Force -ErrorAction Stop
            }
        }
    }
    process {
        switch ($parSet) {
            "Default" {
                foreach ($item in $Config) {
                    Write-Config -Config $item -Scope $Scope -EnableException $EnableException
                }

                foreach ($item in $FullName) {
                    if ([Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations.ContainsKey($item.ToLower())) {
                        Write-Config -Config ([Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations[$item.ToLower()]) -Scope $Scope -EnableException $EnableException
                    }
                }
            }
            "Name" {
                foreach ($item in ([Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations.Values | Where-Object Module -EQ $Module | Where-Object Name -Like $Name)) {
                    Write-Config -Config $item -Scope $Scope -EnableException $EnableException
                }
            }
        }
    }
    end {

    }
}
tools\dbatools\functions\Remove-DbaAgentJob.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Remove-DbaAgentJob {
    <#
        .SYNOPSIS
            Remove-DbaAgentJob removes a job.

        .DESCRIPTION
            Remove-DbaAgentJob removes a a job in the SQL Server Agent.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Job
            The name of the job. Can be null if the the job id is being used.

        .PARAMETER KeepHistory
            Specifies to keep the history for the job. By default history is deleted.

        .PARAMETER KeepUnusedSchedule
            Specifies to keep the schedules attached to this job if they are not attached to any other job.
            By default the unused schedule is deleted.

        .PARAMETER Mode
            Default: Strict
            How strict does the command take lesser issues?
            Strict: Interrupt if the job specified doesn't exist.
            Lazy:   Silently skip over jobs that don't exist.

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Sander Stad (@sqlstad, sqlstad.nl)
            Tags: Agent, Job

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Remove-DbaAgentJob

        .EXAMPLE
            Remove-DbaAgentJob -SqlInstance sql1 -Job Job1

            Removes the job from the instance with the name Job1

        .EXAMPLE
            Remove-DbaAgentJob -SqlInstance sql1 -Job Job1 -KeepHistory

            Removes the job but keeps the history

        .EXAMPLE
            Remove-DbaAgentJob -SqlInstance sql1 -Job Job1 -KeepUnusedSchedule

            Removes the job but keeps the unused schedules

        .EXAMPLE
            Remove-DbaAgentJob -SqlInstance sql1, sql2, sql3 -Job Job1

            Removes the job from multiple servers

        .EXAMPLE
            sql1, sql2, sql3 | Remove-DbaAgentJob -Job Job1

            Removes the job from multiple servers using pipe line

    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param(
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Parameter(Mandatory = $false)]
        [PSCredential]$SqlCredential,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [object[]]$Job,
        [Parameter(Mandatory = $false)]
        [switch]$KeepHistory,
        [Parameter(Mandatory = $false)]
        [switch]$KeepUnusedSchedule,
        [DbaMode]$Mode = (Get-DbaConfigValue -Name 'message.mode.default' -Fallback "Strict"),
        [Parameter(Mandatory = $false)]
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            foreach ($j in $Job) {
                Write-Message -Level Verbose -Message "Processing job $j"

                if ($Server.JobServer.Jobs.Name -notcontains $j) {
                    switch ($Mode) {
                        'Lazy' {
                            Write-Message -Level Verbose -Message "Job $j doesn't exists on $instance." -Target $instance
                        }
                        'Strict' {
                            Stop-Function -Message "Job $j doesn't exist on $instance." -Continue -ContinueLabel main -Target $instance -Category InvalidData
                        }
                    }
                }
                else {
                    if ($PSCmdlet.ShouldProcess($instance, "Removing the job $j")) {
                        try {
                            $currentJob = $Server.JobServer.Jobs[$j]
                            $dropHistory = 1
                            $dropSchedule = 1
                            if (Test-Bound -ParameterName KeepHistory) {
                                Write-Message -Level SomewhatVerbose -Message "Job history will be kept"
                                $dropHistory = 0
                            }
                            if (Test-Bound -ParameterName KeepUnusedSchedule) {
                                Write-Message -Level SomewhatVerbose -Message "Unused job schedules will be kept"
                                $dropSchedule = 0
                            }
                            Write-Message -Level SomewhatVerbose -Message "Removing job"
                            $dropJobQuery = ("EXEC dbo.sp_delete_job @job_name = '{0}', @delete_history = {1}, @delete_unused_schedule = {2}" -f $currentJob.Name, $dropHistory, $dropSchedule)
                            $server.Databases['msdb'].ExecuteNonQuery($dropJobQuery)
                        }
                        catch {
                            Stop-Function -Message  "Something went wrong removing the job" -Target $instance -ErrorRecord $_ -Continue
                        }
                    }
                }
            }
        }
    }
    end {
        Write-Message -Message "Finished removing jobs(s)." -Level Verbose
    }
}
tools\dbatools\functions\Remove-DbaAgentJobCategory.ps1
function Remove-DbaAgentJobCategory {

    <#
.SYNOPSIS
Remove-DbaAgentJobCategory removes a job category.

.DESCRIPTION
Remove-DbaAgentJobCategory makes it possible to remove a job category.
Be assured that the category you want to remove is not used with other jobs. If another job uses this category it will be get the category [Uncategorized (Local)].

.PARAMETER SqlInstance
SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Category
The name of the category

.PARAMETER Force
The force parameter will ignore some errors in the parameters and assume defaults.

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Author: Sander Stad (@sqlstad, sqlstad.nl)
Tags: Agent, Job, JobCategory

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Remove-DbaAgentJobCategory

.EXAMPLE
Remove-DbaAgentJobCategory -SqlInstance sql1 -Category 'Category 1'

Remove the job category Category 1 from the instance.

.EXAMPLE
Remove-DbaAgentJobCategory -SqlInstance sql1 -Category Category1, Category2, Category3

Remove multiple job categories from the instance.

.EXAMPLE
Remove-DbaAgentJobCategory -SqlInstance sql1, sql2, sql3 -Category Category1, Category2, Category3

Remove multiple job categories from the multiple instances.

#>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Parameter(Mandatory = $false)]
        [ValidateNotNullOrEmpty()]
        [string[]]$Category,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {

        foreach ($instance in $sqlinstance) {
            # Try connecting to the instance
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            # Loop through each of the categories
            foreach ($cat in $Category) {

                # Check if the job category exists
                if ($cat -notin $server.JobServer.JobCategories.Name) {
                    Stop-Function -Message "Job category $cat doesn't exist on $instance" -Target $instance -Continue
                }

                # Remove the category
                if ($PSCmdlet.ShouldProcess($instance, "Changing the job category $Category")) {
                    try {
                        # Get the category
                        $currentCategory = $server.JobServer.JobCategories[$cat]

                        Write-Message -Message "Removing job category $cat" -Level Verbose

                        $currentCategory.Drop()
                    }
                    catch {
                        Stop-Function -Message "Something went wrong removing the job category $cat on $instance" -Target $cat -Continue -ErrorRecord $_
                    }

                } #if should process

            } # for each category

        } # for each instance

    } # end process

    end {
        if (Test-FunctionInterrupt) { return }
        Write-Message -Message "Finished removing job category." -Level Verbose
    }

}
tools\dbatools\functions\Remove-DbaAgentJobStep.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Remove-DbaAgentJobStep {
    <#
        .SYNOPSIS
            Removes a step from the specified SQL Agent job.

        .DESCRIPTION
            Removes a job step from a SQL Server Agent job.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Job
            The name of the job.

        .PARAMETER StepName
            The name of the job step.

        .PARAMETER Mode
            Default: Strict
            How strict does the command take lesser issues?
            Strict: Interrupt if the configuration already has the same value as the one specified.
            Lazy:   Silently skip over instances that already have this configuration at the specified value.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Sander Stad (@sqlstad, sqlstad.nl)
            Tags: Agent, Job, JobStep

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Remove-DbaAgentJobStep

        .EXAMPLE
            Remove-DbaAgentJobStep -SqlInstance sql1 -Job Job1 -StepName Step1

            Remove 'Step1' from job 'Job1' on sql1.

        .EXAMPLE
            Remove-DbaAgentJobStep -SqlInstance sql1 -Job Job1, Job2, Job3 -StepName Step1

            Remove the job step from multiple jobs.

        .EXAMPLE
            Remove-DbaAgentJobStep -SqlInstance sql1, sql2, sql3 -Job Job1 -StepName Step1

            Remove the job step from the job on multiple servers.

        .EXAMPLE
            sql1, sql2, sql3 | Remove-DbaAgentJobStep -Job Job1 -StepName Step1

            Remove the job step from the job on multiple servers using pipeline.
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Parameter(Mandatory = $false)]
        [PSCredential]$SqlCredential,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [object[]]$Job,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$StepName,
        [DbaMode]$Mode = (Get-DbaConfigValue -Name 'message.mode.default' -Fallback "Strict"),
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            foreach ($j in $Job) {
                Write-Message -Level Verbose -Message "Processing job $j"
                # Check if the job exists
                if ($Server.JobServer.Jobs.Name -notcontains $j) {
                    switch ($Mode) {
                        'Lazy' {
                            Write-Message -Level Verbose -Message "Job $j doesn't exists on $instance." -Target $instance
                        }
                        'Strict' {
                            Stop-Function -Message "Job $j doesnn't exist on $instance." -Continue -ContinueLabel main -Target $instance -Category InvalidData
                        }
                    }
                }
                else {
                    # Check if the job step exists
                    if ($Server.JobServer.Jobs[$j].JobSteps.Name -notcontains $StepName) {
                        switch ($Mode) {
                            'Lazy' {
                                Write-Message -Level Verbose -Message "Step $StepName doesn't exist for $job on $instance." -Target $instance
                            }
                            'Strict' {
                                Stop-Function -Message "Step $StepName doesn't exist for $job on $instance." -Continue -ContinueLabel main -Target $instance -Category InvalidData
                            }
                        }
                    }
                    else {
                        # Execute
                        if ($PSCmdlet.ShouldProcess($instance, "Removing the job step $StepName for job $j")) {
                            try {
                                $JobStep = $Server.JobServer.Jobs[$j].JobSteps[$StepName]
                                Write-Message -Level SomewhatVerbose -Message "Removing the job step $StepName for job $j."
                                $JobStep.Drop()
                            }
                            catch {
                                Stop-Function -Message "Something went wrong removing the job step" -Target $JobStep -Continue -ErrorRecord $_
                                Write-Message -Level Verbose -Message "Could not remove the job step $StepName from $j"
                            }
                        }
                    }
                }
            }
        }
    }
    end {
        Write-Message -Message "Finished removing the jobs step(s)" -Level Verbose
    }
}
tools\dbatools\functions\Remove-DbaAgentSchedule.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Remove-DbaAgentSchedule {
    <#
.SYNOPSIS
Remove-DbaAgentJobSchedule removes a job schedule.

.DESCRIPTION
Remove-DbaAgentJobSchedule removes a a job in the SQL Server Agent.

.PARAMETER SqlInstance
SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Schedule
The name of the job schedule.

.PARAMETER InputObject
A collection of schedule (such as returned by Get-DbaAgentSchedule), to be removed.

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.PARAMETER Force
The force parameter will ignore some errors in the parameters and assume defaults.
It will also remove the any present schedules with the same name for the specific job.

.NOTES
Author: Sander Stad (@sqlstad, sqlstad.nl)
Tags: Agent, Job, Schedule

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Remove-DbaAgentJobSchedule

.EXAMPLE
Remove-DbaAgentSchedule -SqlInstance sql1 -Schedule weekly
Remove the schedule weekly

.EXAMPLE
Remove-DbaAgentSchedule -SqlInstance sql1 -Schedule weekly -Force
Remove the schedule weekly from the job even if the schedule is being used by another job.

.EXAMPLE
Remove-DbaAgentSchedule -SqlInstance sql1 -Schedule daily, weekly
Remove multiple schedule

.EXAMPLE
Remove-DbaAgentSchedule -SqlInstance sql1, sql2, sql3 -Schedule daily, weekly
Remove the schedule on multiple servers for multiple schedules

.EXAMPLE
sql1, sql2, sql3 | Remove-DbaAgentSchedule -Schedule daily, weekly
Remove the schedule on multiple servers using pipe line

.EXAMPLE
Get-DbaAgentSchedule -SqlInstance sql1 -Schedule sched1, sched2, sched3 | Remove-DbaAgentSchedule

Remove the schedules using a pipeline

#>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]

    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "instance")]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [System.Management.Automation.PSCredential]
        $SqlCredential,
        [Parameter(Mandatory = $true, ParameterSetName = "instance")]
        [ValidateNotNullOrEmpty()]
        [Alias("Schedules")]
        [object[]]$Schedule,
        [Parameter(ValueFromPipeline, Mandatory, ParameterSetName = "schedules")]
        [Microsoft.SqlServer.Management.Smo.Agent.ScheduleBase[]]$InputObject,
        [Alias('Silent')]
        [switch]$EnableException,
        [switch]$Force
    )

    process {

        foreach ($instance in $sqlinstance) {
            # Try connecting to the instance
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $InputObject += $server.JobServer.SharedSchedules | Where-Object { $_.Name -in $Schedule }

        } # foreach object instance

        foreach ($s in $InputObject) {

            if ($Server.JobServer.SharedSchedules.Name -contains $s.Name) {
                # Get job count
                $jobCount = $Server.JobServer.SharedSchedules[$s].JobCount

                # Check if the schedule is shared among other jobs
                if ($jobCount -ge 1 -and -not $Force) {
                    Stop-Function -Message "The schedule $s is shared connected to one or more jobs. If removal is neccesary use -Force." -Target $instance -Continue
                }

                # Remove the job schedule
                if ($PSCmdlet.ShouldProcess($instance, "Removing schedule $s on $instance")) {
                    # Loop through each of the schedules and drop them
                    Write-Message -Message "Removing schedule $s on $instance" -Level Verbose

                    #Check if jobs use the schedule
                    if ($jobCount -ge 1) {
                        # Get the job object
                        $smoSchedules = $server.JobServer.SharedSchedules | Where-Object {($_.Name -eq $s.Name)}

                        Write-Message -Message "Schedule $sched is used in one or more jobs. Removing it for each job." -Level Verbose

                        # Loop through each if the schedules
                        foreach ($smoSchedule in $smoSchedules) {

                            # Get the job ids
                            $jobGuids = $Server.JobServer.SharedSchedules[$smoSchedule].EnumJobReferences()

                            if (($jobCount -gt 1 -and $Force) -or $jobCount -eq 1) {

                                # Loop though each of the jobs
                                foreach ($guid in $jobGuids) {
                                    # Get the job object
                                    $smoJob = $Server.JobServer.GetJobByID($guid)

                                    # Get the job schedule
                                    $jobSchedules = $Server.JobServer.Jobs[$smoJob].JobSchedules | Where-Object {$_.Name -eq $smoSchedule}

                                    foreach ($jobSchedule in $jobSchedules) {
                                        try {
                                            Write-Message -Message "Removing the schedule $jobSchedule for job $smoJob" -Level Verbose

                                            $jobSchedule.Drop()
                                        }
                                        catch {
                                            Stop-Function -Message  "Something went wrong removing the job schedule" -Target $instance -ErrorRecord $_ -Continue
                                        }
                                    }
                                } # foreach guid
                            } # if jobcount

                        } # foreach smoschedule
                    } # if jobcount ge 1

                    Write-Message -Message "Removing schedules that are not being used by other jobs." -Level Verbose

                    # Get the schedules
                    $smoSchedules = $server.JobServer.SharedSchedules | Where-Object {($_.Name -eq $s.Name) -and ($_.JobCount -eq 0)}

                    # Remove the schedules that have no jobs
                    foreach ($smoSchedule in $smoSchedules) {
                        try {
                            $smoSchedule.Drop()
                        }
                        catch {
                            Stop-Function -Message  "Something went wrong removing the schedule" -Target $instance -ErrorRecord $_ -Continue
                        }
                    } # foreach schedule
                } # should process
            } # if contains schedule
            else {
                Stop-Function -Message "Schedule $s is not present on instance $instance" -Target $instance -Continue
            }
        } #foreach object schedule

    } # process

    end {
        Write-Message -Message "Finished removing jobs schedule(s)." -Level Verbose
    }
}
tools\dbatools\functions\Remove-DbaBackup.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Remove-DbaBackup {
    <#
        .SYNOPSIS
            Removes SQL Server backups from disk.

        .DESCRIPTION
            Provides all of the same functionality for removing SQL backups from disk as a standard maintenance plan would.

            As an addition you have the ability to check the Archive bit on files before deletion. This will allow you to ensure backups have been archived to your archive location before removal.

            Also included is the ability to remove empty folders as part of this cleanup activity.

        .PARAMETER Path
            Specifies the name of the base level folder to search for backup files. Deletion of backup files will be recursive from this location.

        .PARAMETER BackupFileExtension
            Specifies the filename extension of the backup files you wish to remove (typically 'bak', 'trn' or 'log'). Do not include the period.

        .PARAMETER RetentionPeriod
            Specifies the retention period for backup files. Correct format is ##U.

            ## is the retention value and must be an integer value
            U signifies the units where the valid units are:
            h = hours
            d = days
            w = weeks
            m = months

            Formatting Examples:
            '48h' = 48 hours
            '7d' = 7 days
            '4w' = 4 weeks
            '1m' = 1 month

        .PARAMETER CheckArchiveBit
            If this switch is enabled, the filesystem Archive bit is checked before deletion. If this bit is set (which translates to "it has not been backed up to another location yet", the file won't be deleted.

        .PARAMETER RemoveEmptyBackupFolder
            If this switch is enabled, empty folders will be removed after the cleanup process is complete.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

       .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.i

        .NOTES
            Tags: Storage, DisasterRecovery, Backup
            Author: Chris Sommer, @cjsommer, www.cjsommer.com

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Remove-DbaBackup

        .EXAMPLE
            Remove-DbaBackup -Path 'C:\MSSQL\SQL Backup\' -BackupFileExtension trn -RetentionPeriod 48h

            '*.trn' files in 'C:\MSSQL\SQL Backup\' and all subdirectories that are more than 48 hours old will be removed.

        .EXAMPLE
            Remove-DbaBackup -Path 'C:\MSSQL\SQL Backup\' -BackupFileExtension trn -RetentionPeriod 48h -WhatIf

            Same as example #1, but doesn't actually remove any files. The function will instead show you what would be done.
            This is useful when first experimenting with using the function.

        .EXAMPLE
            Remove-DbaBackup -Path 'C:\MSSQL\Backup\' -BackupFileExtension bak -RetentionPeriod 7d -CheckArchiveBit

            '*.bak' files in 'C:\MSSQL\Backup\' and all subdirectories that are more than 7 days old will be removed, but only if the files have been backed up to another location as verified by checking the Archive bit.

        .EXAMPLE
            Remove-DbaBackup -Path 'C:\MSSQL\Backup\' -BackupFileExtension bak -RetentionPeriod 1w -RemoveEmptyBackupFolder

            '*.bak' files in 'C:\MSSQL\Backup\' and all subdirectories that are more than 1 week old will be removed. Any folders left empty will be removed as well.
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true, HelpMessage = "Full path to the root level backup folder (ex. 'C:\SQL\Backups'")]
        [Alias("BackupFolder")]
        [string]$Path,
        [parameter(Mandatory = $true, HelpMessage = "Backup File extension to remove (ex. bak, trn, dif)")]
        [string]$BackupFileExtension ,
        [parameter(Mandatory = $true, HelpMessage = "Backup retention period. (ex. 24h, 7d, 4w, 6m)")]
        [string]$RetentionPeriod ,
        [parameter(Mandatory = $false)]
        [switch]$CheckArchiveBit = $false ,
        [parameter(Mandatory = $false)]
        [switch]$RemoveEmptyBackupFolder = $false,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        # Ensure BackupFileExtension does not begin with a .
        if ($BackupFileExtension -match "^[.]") {
            Write-Message -Level Warning -Message "Parameter -BackupFileExtension begins with a period '$BackupFileExtension'. A period is automatically prepended to -BackupFileExtension and need not be passed in."
        }
    }
    process {
        # Process stuff
        Write-Message -Message "Removing backups from $Path" -Level Verbose
        Find-DbaBackup -Path $Path -BackupFileExtension $BackupFileExtension -RetentionPeriod $RetentionPeriod -CheckArchiveBit:$CheckArchiveBit -EnableException |
            Foreach-Object {
            $file = $_
            if ($PSCmdlet.ShouldProcess($file.Directory.FullName, "Removing backup file $($file.Name)")) {
                try {
                    $file | Remove-Item -Force -EA Stop
                }
                catch {
                    Write-Message -Message "Failed to remove $file." -Level Warning -ErrorRecord $_
                }
            }
        }
        Write-Message -Message "File Cleaning ended." -Level Verbose
        # Cleanup empty backup folders.
        if ($RemoveEmptyBackupFolder) {
            Write-Message -Message "Removing empty folders." -Level Verbose
            (Get-ChildItem -Directory -Path $Path -Recurse -ErrorAction SilentlyContinue -ErrorVariable EnumErrors).FullName |
                Sort-Object -Descending |
                Foreach-Object {
                $OrigPath = $_
                try {
                    $Contents = @(Get-ChildItem -Force $OrigPath -ErrorAction Stop)
                }
                catch {
                    Write-Message -Message "Can't enumerate $OrigPath." -Level Warning -ErrorRecord $_
                }
                if ($Contents.Count -eq 0) {
                    return $_
                }
            } |
                Foreach-Object {
                $FolderPath = $_
                if ($PSCmdlet.ShouldProcess($Path, "Removing empty folder .$($FolderPath.Replace($Path, ''))")) {
                    try {
                        $FolderPath | Remove-Item -ErrorAction Stop
                    }
                    catch {
                        Write-Message -Message "Failed to remove $FolderPath." -Level Warning -ErrorRecord $_
                    }
                }
            }
            if ($EnumErrors) {
                Write-Message "Errors encountered enumerating folders." -Level Warning -ErrorRecord $EnumErrors
            }
            Write-Message -Message "Removed empty folders." -Level Verbose
        }
    }
}
tools\dbatools\functions\Remove-DbaClientAlias.ps1
function Remove-DbaClientAlias {
    <#
    .SYNOPSIS
    Removes a sql alias for the specified server - mimics cliconfg.exe

    .DESCRIPTION
    Removes a SQL Server alias by altering HKLM:\SOFTWARE\Microsoft\MSSQLServer\Client

    .PARAMETER ComputerName
    The target computer where the alias will be created

    .PARAMETER Credential
    Allows you to login to remote computers using alternative credentials

    .PARAMETER Alias
    The alias to be deleted

    .PARAMETER EnableException
    By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
    Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
    Tags: Alias

    Website: https://dbatools.io
    Copyright: (C) Chrissy LeMaire, [email protected]
    License: MIT https://opensource.org/licenses/MIT

    .LINK
    https://dbatools.io/Remove-DbaClientAlias

    .EXAMPLE
    Remove-DbaClientAlias -ComputerName workstationx -Alias sqlps
    Removes the sqlps SQL client alias on workstationx

    .EXAMPLE
    Get-DbaClientAlias | Remove-DbaClientAlias
    Removes all SQL Server client aliases on the local computer

#>
    [CmdletBinding()]
    Param (
        [parameter(ValueFromPipelineByPropertyName)]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [Alias('AliasName')]
        [string]$Alias,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {

        foreach ($computer in $ComputerName) {
            $null = Test-ElevationRequirement -ComputerName $computer -Continue

            $scriptblock = {
                $Alias = $args[0]
                function Get-ItemPropertyValue {
                    Param (
                        [parameter()]
                        [String]$Path,
                        [parameter()]
                        [String]$Name
                    )
                    Get-ItemProperty -LiteralPath $Path -Name $Name
                }

                $basekeys = "HKLM:\SOFTWARE\WOW6432Node\Microsoft\MSSQLServer", "HKLM:\SOFTWARE\Microsoft\MSSQLServer"

                foreach ($basekey in $basekeys) {

                    if ((Test-Path $basekey) -eq $false) {
                        Write-Warning "Base key ($basekey) does not exist. Quitting."
                        continue
                    }

                    $client = "$basekey\Client"

                    if ((Test-Path $client) -eq $false) {
                        continue
                    }

                    $connect = "$client\ConnectTo"

                    if ((Test-Path $connect) -eq $false) {
                        continue
                    }

                    if ($basekey -like "*WOW64*") {
                        $architecture = "32-bit"
                    }
                    else {
                        $architecture = "64-bit"
                    }


                    $all = Get-Item -Path $connect
                    foreach ($entry in $all) {

                        foreach ($en in $entry) {
                            $e = $entry.ToString().Replace('HKEY_LOCAL_MACHINE', 'HKLM:\')
                            if ($en.Property -contains $Alias) {
                                Remove-ItemProperty -Path $e -Name $Alias
                            }
                            else {
                                $en
                            }
                        }
                    }
                }
            }

            if ($PScmdlet.ShouldProcess($computer, "Getting aliases")) {
                try {
                    $null = Invoke-Command2 -ComputerName $computer -Credential $Credential -ScriptBlock $scriptblock -ErrorAction Stop -Verbose:$false -ArgumentList $Alias
                    Get-DbaClientAlias -ComputerName $computer

                }
                catch {
                    Stop-Function -Message "Failure" -ErrorRecord $_ -Target $computer -Continue
                }
            }
        }
    }
}
tools\dbatools\functions\Remove-DbaCmConnection.ps1
function Remove-DbaCmConnection {
    <#
        .SYNOPSIS
            Removes connection objects from the connection cache used for remote computer management.

        .DESCRIPTION
            Removes connection objects from the connection cache used for remote computer management.

        .PARAMETER ComputerName
            The computer whose connection to remove.
            Accepts both text as well as the output of Get-DbaCmConnection.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ComputerManagement, CIM
            Author: Fred Winmann (@FredWeinmann)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Remove-DbaCmConnection

        .EXAMPLE
            Remove-DbaCmConnection -ComputerName sql2014

            Removes the cached connection to the server sql2014 from the cache.

        .EXAMPLE
            Get-DbaCmConnection | Remove-DbaCmConnection

            Clears the entire connection cache.
    #>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true, Mandatory = $true)]
        [Sqlcollaborative.Dbatools.Parameter.DbaCmConnectionParameter[]]
        $ComputerName,

        [switch]
        [Alias('Silent')]$EnableException
    )

    BEGIN {
        Write-Message -Level InternalComment -Message "Starting"
        Write-Message -Level Verbose -Message "Bound parameters: $($PSBoundParameters.Keys -join ", ")"
    }
    PROCESS {
        foreach ($connectionObject in $ComputerName) {
            if (-not $connectionObject.Success) { Stop-Function -Message "Failed to interpret computername input: $($connectionObject.InputObject)" -Category InvalidArgument -Target $connectionObject.InputObject -Continue }
            Write-Message -Level VeryVerbose -Message "Removing from connection cache: $($connectionObject.Connection.ComputerName)" -Target $connectionObject.Connection.ComputerName
            if ([Sqlcollaborative.Dbatools.Connection.ConnectionHost]::Connections.ContainsKey($connectionObject.Connection.ComputerName)) {
                $null = [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::Connections.Remove($connectionObject.Connection.ComputerName)
                Write-Message -Level Verbose -Message "Successfully removed $($connectionObject.Connection.ComputerName)" -Target $connectionObject.Connection.ComputerName
            }
            else {
                Write-Message -Level Verbose -Message "Not found: $($connectionObject.Connection.ComputerName)" -Target $connectionObject.Connection.ComputerName
            }
        }
    }
    END {
        Write-Message -Level InternalComment -Message "Ending"
    }
}
tools\dbatools\functions\Remove-DbaComputerCertificate.ps1
function Remove-DbaComputerCertificate {
    <#
    .SYNOPSIS
        Removes a computer certificate - useful for removing easily certs from remote computers

    .DESCRIPTION
        Removes a computer certificate from a local or remote compuer

    .PARAMETER ComputerName
        The target computer - defaults to localhost

    .PARAMETER Credential
        Allows you to login to $ComputerName using alternative credentials

    .PARAMETER Thumbprint
        The thumbprint of the certificate object

    .PARAMETER Store
        Certificate store - defaults to LocalMachine (otherwise exceptions can be thrown on remote connections)

    .PARAMETER Folder
        Certificate folder

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .PARAMETER WhatIf
        Shows what would happen if the command were to run. No actions are actually performed.

    .PARAMETER Confirm
        Prompts you for confirmation before executing any changing operations within the command.

    .EXAMPLE
        Remove-DbaComputerCertificate -ComputerName Server1 -Thumbprint C2BBE81A94FEE7A26FFF86C2DFDAF6BFD28C6C94

        Removes certificate with thumbprint C2BBE81A94FEE7A26FFF86C2DFDAF6BFD28C6C94 in the LocalMachine store on Server1

    .EXAMPLE
        Get-DbaComputerCertificate | Where-Object Thumbprint -eq E0A071E387396723C45E92D42B2D497C6A182340 | Remove-DbaComputerCertificate

        Removes certificate using the pipeline

    .EXAMPLE
        Remove-DbaComputerCertificate -ComputerName Server1 -Thumbprint C2BBE81A94FEE7A26FFF86C2DFDAF6BFD28C6C94 -Store User -Folder My

        Removes certificate with thumbprint C2BBE81A94FEE7A26FFF86C2DFDAF6BFD28C6C94 in the User\My (Personal) store on Server1

    .NOTES
        Tags: Certificate

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT
#>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High")]
    param (
        [Alias("ServerInstance", "SqlServer", "SqlInstance")]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [parameter(ValueFromPipelineByPropertyName, Mandatory)]
        [string[]]$Thumbprint,
        [string]$Store = "LocalMachine",
        [string]$Folder = "My",
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        #region Scriptblock for remoting
        $scriptblock = {
            param (
                $Thumbprint,

                $Store,

                $Folder
            )
            Write-Verbose "Searching Cert:\$Store\$Folder for thumbprint: $thumbprint"
            $cert = Get-ChildItem "Cert:\$store\$folder" -Recurse | Where-Object { $_.Thumbprint -eq $Thumbprint }

            if ($cert) {
                $null = $cert | Remove-Item
                $status = "Removed"
            }
            else {
                $status = "Certificate not found in Cert:\$Store\$Folder"
            }

            [pscustomobject]@{
                ComputerName = $env:COMPUTERNAME
                Store        = $Store
                Folder       = $Folder
                Thumbprint   = $thumbprint
                Status       = $status
            }
        }
        #endregion Scriptblock for remoting
    }

    process {
        foreach ($computer in $computername) {
            foreach ($thumb in $Thumbprint) {
                if ($PScmdlet.ShouldProcess("local", "Connecting to $computer to remove cert from Cert:\$Store\$Folder")) {
                    try {
                        Invoke-Command2 -ComputerName $computer -Credential $Credential -ArgumentList $thumb, $Store, $Folder -ScriptBlock $scriptblock -ErrorAction Stop
                    }
                    catch {
                        Stop-Function -Message $_ -ErrorRecord $_ -Target $computer -Continue
                    }
                }
            }
        }
    }
}
tools\dbatools\functions\Remove-DbaDatabase.ps1
function Remove-DbaDatabase {
    <#
.SYNOPSIS
Drops a database, hopefully even the really stuck ones.

.DESCRIPTION
Tries a bunch of different ways to remove a database or two or more.

.PARAMETER SqlInstance
The SQL Server instance holding the databases to be removed.You must have sysadmin access and server version must be SQL Server version 2000 or higher.

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Database
The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

.PARAMETER InputObject
A collection of databases (such as returned by Get-DbaDatabase), to be removed.

.PARAMETER IncludeSystemDb
Use this switch to disable any kind of verbose messages

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Delete, Databases

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Remove-DbaDatabase

.EXAMPLE
Remove-DbaDatabase -SqlInstance sql2016 -Database containeddb

Prompts then removes the database containeddb on SQL Server sql2016

.EXAMPLE
Remove-DbaDatabase -SqlInstance sql2016 -Database containeddb, mydb

Prompts then removes the databases containeddb and mydb on SQL Server sql2016

.EXAMPLE
Remove-DbaDatabase -SqlInstance sql2016 -Database containeddb -Confirm:$false

Does not prompt and swiftly removes containeddb on SQL Server sql2016

.EXAMPLE
Get-DbaDatabase -SqlInstance server\instance -ExcludeAllSystemDb | Remove-DbaDatabase

Removes all the user databases from server\instance

.EXAMPLE
Get-DbaDatabase -SqlInstance server\instance -ExcludeAllSystemDb | Remove-DbaDatabase -Confirm:$false

Removes all the user databases from server\instance without any confirmation
#>
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High', DefaultParameterSetName = "Default")]
    Param (
        [parameter(Mandatory, ParameterSetName = "instance")]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [parameter(Mandatory = $false)]
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [parameter(Mandatory, ParameterSetName = "instance")]
        [Alias("Databases")]
        [object[]]$Database,
        [Parameter(ValueFromPipeline, Mandatory, ParameterSetName = "databases")]
        [Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
        [switch]$IncludeSystemDb,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            $InputObject += $server.Databases | Where-Object { $_.Name -in $Database }
        }

        $system_dbs = @( "master", "model", "tempdb", "resource", "msdb" )

        if (-not($IncludeSystemDb)) {
            $InputObject = $InputObject | Where-Object { $_.Name -notin $system_dbs}
        }

        foreach ($db in $InputObject) {
            try {
                $server = $db.Parent
                if ($Pscmdlet.ShouldProcess("$db on $server", "KillDatabase")) {
                    $server.KillDatabase($db.name)
                    $server.Refresh()

                    [pscustomobject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Database     = $db.name
                        Status       = "Dropped"
                    }
                }
            }
            catch {
                try {
                    if ($Pscmdlet.ShouldProcess("$db on $server", "alter db set single_user with rollback immediate then drop")) {
                        $null = $server.Query("if exists (select * from sys.databases where name = '$($db.name)' and state = 0) alter database $db set single_user with rollback immediate; drop database $db")

                        [pscustomobject]@{
                            ComputerName = $server.ComputerName
                            InstanceName = $server.ServiceName
                            SqlInstance  = $server.DomainInstanceName
                            Database     = $db.name
                            Status       = "Dropped"
                        }
                    }
                }
                catch {
                    try {
                        if ($Pscmdlet.ShouldProcess("$db on $server", "SMO drop")) {
                            $server.databases[$dbname].Drop()
                            $server.Refresh()

                            [pscustomobject]@{
                                ComputerName = $server.ComputerName
                                InstanceName = $server.ServiceName
                                SqlInstance  = $server.DomainInstanceName
                                Database     = $db.name
                                Status       = "Dropped"
                            }
                        }
                    }
                    catch {
                        Write-Message -Level Verbose -Message "Could not drop database $db on $server"

                        [pscustomobject]@{
                            ComputerName = $server.ComputerName
                            InstanceName = $server.ServiceName
                            SqlInstance  = $server.DomainInstanceName
                            Database     = $db.name
                            Status       = (Get-ErrorMessage -Record $_)
                        }
                    }
                }
            }
        }
    }
}
tools\dbatools\functions\Remove-DbaDatabaseMasterKey.ps1
function Remove-DbaDatabaseMasterKey {
    <#
    .SYNOPSIS
        Deletes specified database master key

    .DESCRIPTION
        Deletes specified database master key.

    .PARAMETER SqlInstance
        The target SQL Server instance.

    .PARAMETER SqlCredential
        Allows you to login to SQL Server using alternative credentials.

    .PARAMETER Database
        The database where the master key will be removed.

    .PARAMETER ExcludeDatabase
        List of databases to exclude from clearing all master keys

    .PARAMETER All
        Purge the master keys from all databases on an instance.

    .PARAMETER MasterKeyCollection
        Internal parameter to support pipeline input

    .PARAMETER Mode
        Controls how the function handles cases where it can't do anything due to missing database or key:
        Strict: Write a warning (default)
        Lazy:   Write a verbose message
        Report: Create a report object as part of the output
        The default action can be adjusted by using Set-DbaConfig to change the 'message.mode.default' configuration

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .PARAMETER WhatIf
        Shows what would happen if the command were to run. No actions are actually performed.

    .PARAMETER Confirm
        Prompts you for confirmation before executing any changing operations within the command.

    .EXAMPLE
        Remove-DbaDatabaseMasterKey -SqlInstance Server1

        The master key in the master database on server1 will be removed if it exists.

    .EXAMPLE
        Remove-DbaDatabaseMasterKey -SqlInstance Server1 -Database db1 -Confirm:$false

        Suppresses all prompts to remove the master key in the 'db1' database and drops the key.


    .NOTES
        Tags: Certificate

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT
#>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true, ConfirmImpact = "High")]
    param (
        [parameter(Mandatory, ParameterSetName = "instanceExplicit")]
        [parameter(Mandatory, ParameterSetName = "instanceAll")]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]
        $SqlInstance,

        [System.Management.Automation.PSCredential]
        $SqlCredential,

        [parameter(Mandatory, ParameterSetName = "instanceExplicit")]
        [object[]]
        $Database,

        [parameter(ParameterSetName = "instanceAll")]
        [object[]]
        $ExcludeDatabase,

        [parameter(Mandatory, ParameterSetName = "instanceAll")]
        [switch]
        $All,

        [parameter(ValueFromPipeline, ParameterSetName = "collection")]
        [Microsoft.SqlServer.Management.Smo.MasterKey[]]
        $MasterKeyCollection,

        [DbaMode]
        $Mode = (Get-DbaConfigValue -Name 'message.mode.default' -Fallback "Strict"),

        [switch]
        [Alias('Silent')]$EnableException
    )

    begin {
        function Drop-Masterkey {
            [CmdletBinding()]
            Param (
                $masterkey,

                $mode = $Mode,

                $EnableException = $EnableException
            )
            $server = $masterkey.Parent.Parent
            $instance = $server.DomainInstanceName
            $cert = $masterkey.Name
            $db = $masterkey.Parent

            if ($Pscmdlet.ShouldProcess($instance, "Dropping the master key for database '$db'")) {
                try {
                    $masterkey.Drop()
                    Write-Message -Level Verbose -Message "Successfully removed master key from the $db database on $instance"

                    [pscustomobject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Database     = $db.Name
                        Status       = "Success"
                    }
                }
                catch {
                    [pscustomobject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Database     = $db.Name
                        Status       = "Failure"
                    }
                    Stop-Function -Message "Failed to drop master key from $db on $instance." -Target $db -InnerErrorRecord $_ -Continue
                }
            }
        }
    }
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($All) {
                $Database = ($server.Databases | Where-Object Name -NotIn $ExcludeDatabase).Name
            }

            :Database foreach ($db in $Database) {
                $smodb = $server.Databases[$db]
                $masterkey = $smodb.MasterKey

                #region Case: Database Unknown
                if ($null -eq $smodb) {
                    switch ($Mode) {
                        [DbaMode]::Strict { Stop-Function -Message "Database '$db' does not exist on $instance" -Target $smodb -Continue -ContinueLabel database }
                        [DbaMode]::Lazy {
                            Write-Message -Level (Get-DbaConfigValue -Name 'message.mode.lazymessagelevel' -Fallback 4) -Message "Database '$db' does not exist on $instance" -Target $smodb
                            continue database
                        }
                        [DbaMode]::Report {
                            [pscustomobject]@{
                                ComputerName = $server.ComputerName
                                InstanceName = $server.ServiceName
                                SqlInstance  = $server.DomainInstanceName
                                Database     = $db
                                Status       = "Unknown Database"
                            }
                            continue Database
                        }
                    }
                }
                #endregion Case: Database Unknown

                #region Case: No Master Key
                if ($null -eq $masterkey) {
                    switch ($Mode.ToString()) {
                        "Strict" { Stop-Function -Message "No master key exists in the $db database on $instance" -Target $smodb -Continue -ContinueLabel database }
                        "Lazy" {
                            Write-Message -Level (Get-DbaConfigValue -Name 'message.mode.lazymessagelevel' -Fallback 4) -Message "No master key exists in the $db database on $instance" -Target $smodb
                            continue database
                        }
                        "Report" {
                            [pscustomobject]@{
                                ComputerName = $server.ComputerName
                                InstanceName = $server.ServiceName
                                SqlInstance  = $server.DomainInstanceName
                                Database     = $smodb.Name
                                Status       = "No Masterkey"
                            }
                            continue Database
                        }
                    }
                }
                #endregion Case: No Master Key

                Write-Message -Level Verbose -Message "Removing master key from $db"
                Drop-Masterkey -masterkey $masterkey
            }
        }

        foreach ($key in $MasterKeyCollection) {
            Write-Message -Level Verbose -Message "Removing master key: $key"
            Drop-Masterkey -masterkey $key
        }
    }
}
tools\dbatools\functions\Remove-DbaDatabaseSafely.ps1
function Remove-DbaDatabaseSafely {
    <#
        .SYNOPSIS
            Safely removes a SQL Database and creates an Agent Job to restore it.

        .DESCRIPTION
            Performs a DBCC CHECKDB on the database, backs up the database with Checksum and verify only to a final (golden) backup location, creates an Agent Job to restore from that backup, drops the database, runs the agent job to restore the database, performs a DBCC CHECKDB and drops the database.

            With huge thanks to Grant Fritchey and his verify your backups video. Take a look, it's only 3 minutes long. http://sqlps.io/backuprant

        .PARAMETER SqlInstance
            The SQL Server instance holding the databases to be removed. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            If specified, Agent jobs will be created on this server. By default, the jobs will be created on the server specified by SqlInstance. You must have sysadmin access and the server must be SQL Server 2000 or higher. The SQL Agent service will be started if it is not already running.

        .PARAMETER DestinationCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies one or more databases to remove.

        .PARAMETER NoDbccCheckDb
            If this switch is enabled, the initial DBCC CHECK DB will be skipped. This will make the process quicker but will also allow you to create an Agent job that restores a database backup containing a corrupt database.

            A second DBCC CHECKDB is performed on the restored database so you will still be notified BUT USE THIS WITH CARE.

        .PARAMETER BackupFolder
            Specifies the path to a folder where the final backups of the removed databases will be stored. If you are using separate source and destination servers, you must specify a UNC path such as  \\SERVER1\BACKUPSHARE\

        .PARAMETER JobOwner
            Specifies the name of the account which will own the Agent jobs. By default, sa is used.

        .PARAMETER UseDefaultFilePaths
            If this switch is enabled, the default file paths for the data and log files on the instance where the database is restored will be used. By default, the original file paths will be used.

        .PARAMETER CategoryName
            Specifies the Category Name for the Agent job that is created for restoring the database(s). By default, the name is "Rationalisation".

        .PARAMETER BackupCompression
            If this switch is enabled, compression will be used for the backup regardless of the SQL Server instance setting. By default, the SQL Server instance setting for backup compression is used.

        .PARAMETER AllDatabases
            If this switch is enabled, all user databases on the server will be removed. This is useful when decommissioning a server. You should use a DestinationServer with this switch.

        .PARAMETER ReuseSourceFolderStructure
            If this switch is enabled, the source folder structure will be used when restoring instead of using the destination instance default folder structure.

        .PARAMETER Force
            If this switch is enabled, all actions will be performed even if DBCC errors are detected. An Agent job will be created with 'DBCCERROR' in the name and the backup file will have 'DBCC' in its name.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database, Remove
            Author: Rob Sewell @SQLDBAWithBeard, sqldbawithabeard.com

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Remove-DbaDatabaseSafely

        .EXAMPLE
            Remove-DbaDatabaseSafely -SqlInstance 'Fade2Black' -Database RideTheLightning -BackupFolder 'C:\MSSQL\Backup\Rationalised - DO NOT DELETE'

            Performs a DBCC CHECKDB on database RideTheLightning on server Fade2Black. If there are no errors, the database is backup to the folder C:\MSSQL\Backup\Rationalised - DO NOT DELETE. Then, an Agent job to restore the database from that backup is created. The database is then dropped, the Agent job to restore it run, a DBCC CHECKDB run against the restored database, and then it is dropped again.

            Any DBCC errors will be written to your documents folder

        .EXAMPLE
            $Database = 'DemoNCIndex','RemoveTestDatabase'
            Remove-DbaDatabaseSafely -SqlInstance 'Fade2Black' -Database $Database -BackupFolder 'C:\MSSQL\Backup\Rationalised - DO NOT DELETE'

            Performs a DBCC CHECKDB on two databases, 'DemoNCIndex' and 'RemoveTestDatabase' on server Fade2Black. Then, an Agent job to restore each database from those backups is created. The databases are then dropped, the Agent jobs to restore them run, a DBCC CHECKDB run against the restored databases, and then they are dropped again.

            Any DBCC errors will be written to your documents folder

        .EXAMPLE
            Remove-DbaDatabaseSafely -SqlInstance 'Fade2Black' -DestinationServer JusticeForAll -Database RideTheLightning -BackupFolder '\\BACKUPSERVER\BACKUPSHARE\MSSQL\Rationalised - DO NOT DELETE'

            Performs a DBCC CHECKDB on database RideTheLightning on server Fade2Black. If there are no errors, the database is backup to the folder \\BACKUPSERVER\BACKUPSHARE\MSSQL\Rationalised - DO NOT DELETE . Then, an Agent job is created on server JusticeForAll to restore the database from that backup is created. The database is then dropped on Fade2Black, the Agent job to restore it on JusticeForAll is run, a DBCC CHECKDB run against the restored database, and then it is dropped from JusticeForAll.

            Any DBCC errors will be written to your documents folder

        .EXAMPLE
            Remove-DbaDatabaseSafely -SqlInstance IronMaiden -Database $Database -DestinationServer TheWildHearts -BackupFolder Z:\Backups -NoDbccCheckDb -UseDefaultFilePaths -JobOwner 'THEBEARD\Rob'

            For the databases $Database on the server IronMaiden a DBCC CHECKDB will not be performed before backing up the databases to the folder Z:\Backups. Then, an Agent job is created on server TheWildHearts with a Job Owner of THEBEARD\Rob to restore each database from that backup using the instance's default file paths. The database(s) is(are) then dropped on IronMaiden, the Agent job(s) run, a DBCC CHECKDB run on the restored database(s), and then the database(s) is(are) dropped.

        .EXAMPLE
            Remove-DbaDatabaseSafely -SqlInstance IronMaiden -Database $Database -DestinationServer TheWildHearts -BackupFolder Z:\Backups -UseDefaultFilePaths -ContinueAfterDbccError

            The databases $Database on the server IronMaiden will be backed up the to the folder Z:\Backups. Then, an Agent job is created on server TheWildHearts with a Job Owner of THEBEARD\Rob to restore each database from that backup using the instance's default file paths. The database(s) is(are) then dropped on IronMaiden, the Agent job(s) run, a DBCC CHECKDB run on the restored database(s), and then the database(s) is(are) dropped.

            If there is a DBCC Error, the function  will continue to perform rest of the actions and will create an Agent job with 'DBCCERROR' in the name and a Backup file with 'DBCCError' in the name.
    #>
    [CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = "Default")]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [parameter(Mandatory = $false)]
        [DbaInstanceParameter]$Destination = $sqlinstance,
        [PSCredential]
        $DestinationCredential,
        [parameter(Mandatory = $false)]
        [Alias("NoCheck")]
        [switch]$NoDbccCheckDb,
        [parameter(Mandatory = $true)]
        [string]$BackupFolder,
        [parameter(Mandatory = $false)]
        [string]$CategoryName = 'Rationalisation',
        [parameter(Mandatory = $false)]
        [string]$JobOwner,
        [parameter(Mandatory = $false)]
        [switch]$AllDatabases,
        [ValidateSet("Default", "On", "Of")]
        [string]$BackupCompression = 'Default',
        [switch]$ReuseSourceFolderStructure,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        if (!$AllDatabases -and !$Database) {
            Stop-Function -Message "You must specify at least one database. Use -Database or -AllDatabases." -ErrorRecord $_
            return
        }

        $sourceserver = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $sqlCredential -ParameterConnection

        if (-not $destination) {
            $destination = $sqlinstance
            $DestinationCredential = $SqlCredential
        }

        if ($sqlinstance -ne $destination) {

            $destserver = Connect-SqlInstance -SqlInstance $destination -SqlCredential $DestinationCredential

            $sourcenb = $instance.ComputerName
            $destnb = $instance.ComputerName

            if ($BackupFolder.StartsWith("\\") -eq $false -and $sourcenb -ne $destnb) {
                Stop-Function -Message "Backup folder must be a network share if the source and destination servers are not the same." -ErrorRecord $_ -Target $backupFolder
                return
            }
        }
        else {
            $destserver = $sourceserver
        }

        $source = $sourceserver.DomainInstanceName
        $destination = $destserver.DomainInstanceName

        if (!$jobowner) {
            $jobowner = Get-SqlSaLogin $destserver
        }

        if ($alldatabases -or !$Database) {
            $database = ($sourceserver.databases | Where-Object { $_.IsSystemObject -eq $false -and ($_.Status -match 'Offline') -eq $false }).Name
        }

        if (!(Test-DbaSqlPath -SqlInstance $destserver -Path $backupFolder)) {
            $serviceaccount = $destserver.ServiceAccount
            Stop-Function -Message "Can't access $backupFolder Please check if $serviceaccount has permissions." -ErrorRecord $_ -Target $backupFolder
        }

        $jobname = "Rationalised Final Database Restore for $dbname"
        $jobStepName = "Restore the $dbname database from Final Backup"

        if (!($destserver.Logins | Where-Object { $_.Name -eq $jobowner })) {
            Stop-Function -Message "$destination does not contain the login $jobowner - Please fix and try again - Aborting." -ErrorRecord $_ -Target $jobowner
        }

        function Start-SqlAgent {
            <#
                .SYNOPSIS
            #>
            [CmdletBinding(SupportsShouldProcess = $true)]
            param ()
            if ($destserver.VersionMajor -eq 8) {
                $serviceName = 'MSSQLSERVER'
            }
            else {
                $instance = $destserver.InstanceName
                if ($instance.length -eq 0) { $instance = "MSSQLSERVER" }
                $serviceName = "SQL Server Agent ($instance)"
            }

            if ($Pscmdlet.ShouldProcess($destination, "Starting Sql Agent")) {
                try {
                    $ipaddr = Resolve-SqlIpAddress $destserver
                    $agentservice = Get-Service -ComputerName $ipaddr -DisplayName $serviceName

                    if ($agentservice.Status -ne 'Running') {
                        $agentservice.Start()
                        $timeout = New-Timespan -seconds 60
                        $sw = [diagnostics.stopwatch]::StartNew()
                        $agentstatus = (Get-Service -ComputerName $ipaddr -DisplayName $serviceName).Status
                        while ($AgentStatus -ne 'Running' -and $sw.elapsed -lt $timeout) {
                            $agentStatus = (Get-Service -ComputerName $ipaddr -DisplayName $serviceName).Status
                        }
                    }
                }

                catch {
                    throw $_
                }

                if ($agentservice.Status -ne 'Running') {
                    throw "Cannot start Agent Service on $destination - Aborting."
                }
            }
        }

        function Start-DbccCheck {
            <#
            .SYNOPSIS

            #>

            [CmdletBinding(SupportsShouldProcess = $true)]
            param (
                [object]$server,
                [string]$dbname
            )

            $servername = $server.name
            $db = $server.databases[$dbname]

            if ($Pscmdlet.ShouldProcess($sourceserver, "Running dbcc check on $dbname on $servername")) {
                try {
                    $null = $db.CheckTables('None')
                    Write-Message -Level Verbose -Message "DBCC CHECKDB finished successfully for $dbname on $servername."
                }

                catch {
                    Write-Message -Level Warning -Message "DBCC CHECKDB failed."
                    Stop-Function -Message "Error occured: $_" -Target $agentservice -ErrorRecord $_ -Continue

                    if ($force) {
                        return $true
                    }
                    else {
                        return $false
                    }
                }
            }
        }

        function New-SqlAgentJobCategory {
            <#
                .SYNOPSIS

            #>
            [CmdletBinding(SupportsShouldProcess = $true)]
            param ([string]$categoryname,
                [object]$jobServer)

            if (!$jobServer.JobCategories[$categoryname]) {
                if ($Pscmdlet.ShouldProcess($sourceserver, "Running dbcc check on $dbname on $sourceserver")) {
                    try {
                        Write-Message -Level Verbose -Message "Creating Agent Job Category $categoryname."
                        $category = New-Object Microsoft.SqlServer.Management.Smo.Agent.JobCategory
                        $category.Parent = $jobServer
                        $category.Name = $categoryname
                        $category.Create()
                        Write-Message -Level Verbose -Message "Created Agent Job Category $categoryname."
                    }
                    catch {
                        Stop-Function -Message "FAILED : To Create Agent Job Category - $categoryname - Aborting." -Target $categoryname -ErrorRecord $_
                        return
                    }
                }
            }
        }

        function Restore-Database {
            <#
                .SYNOPSIS
                    Internal function. Restores .bak file to Sql database. Creates db if it doesn't exist. $filestructure is
                a custom object that contains logical and physical file locations.
            #>

            param (
                [Parameter(Mandatory = $true)]
                [Alias('ServerInstance', 'SqlInstance', 'SqlServer')]
                [object]$server,
                [Parameter(Mandatory = $true)]
                [ValidateNotNullOrEmpty()]
                [string]$dbname,
                [Parameter(Mandatory = $true)]
                [string]$backupfile,
                [string]$filetype = 'Database',
                [Parameter(Mandatory = $true)]
                [object]$filestructure,
                [switch]$norecovery,
                [PSCredential]$sqlCredential,
                [switch]$TSql = $false
            )

            $server = Connect-SqlInstance -SqlInstance $server -SqlCredential $sqlCredential
            $servername = $server.name
            $server.ConnectionContext.StatementTimeout = 0
            $restore = New-Object 'Microsoft.SqlServer.Management.Smo.Restore'
            $restore.ReplaceDatabase = $true

            foreach ($file in $filestructure.values) {
                $movefile = New-Object 'Microsoft.SqlServer.Management.Smo.RelocateFile'
                $movefile.LogicalFileName = $file.logical
                $movefile.PhysicalFileName = $file.physical
                $null = $restore.RelocateFiles.Add($movefile)
            }

            try {
                if ($TSql) {
                    $restore.PercentCompleteNotification = 1
                    $restore.add_Complete($complete)
                    $restore.ReplaceDatabase = $true
                    $restore.Database = $dbname
                    $restore.Action = $filetype
                    $restore.NoRecovery = $norecovery
                    $device = New-Object -TypeName Microsoft.SqlServer.Management.Smo.BackupDeviceItem
                    $device.name = $backupfile
                    $device.devicetype = 'File'
                    $restore.Devices.Add($device)
                    $restorescript = $restore.script($server)
                    return $restorescript
                }
                else {
                    $percent = [Microsoft.SqlServer.Management.Smo.PercentCompleteEventHandler] {
                        Write-Progress -id 1 -activity "Restoring $dbname to $servername" -percentcomplete $_.Percent -status ([System.String]::Format("Progress: {0} %", $_.Percent))
                    }
                    $restore.add_PercentComplete($percent)
                    $restore.PercentCompleteNotification = 1
                    $restore.add_Complete($complete)
                    $restore.ReplaceDatabase = $true
                    $restore.Database = $dbname
                    $restore.Action = $filetype
                    $restore.NoRecovery = $norecovery
                    $device = New-Object -TypeName Microsoft.SqlServer.Management.Smo.BackupDeviceItem
                    $device.name = $backupfile
                    $device.devicetype = 'File'
                    $restore.Devices.Add($device)

                    Write-Progress -id 1 -activity "Restoring $dbname to $servername" -percentcomplete 0 -status ([System.String]::Format("Progress: {0} %", 0))
                    $restore.sqlrestore($server)
                    Write-Progress -id 1 -activity "Restoring $dbname to $servername" -status 'Complete' -Completed

                    return $true
                }
            }
            catch {
                Stop-Function -Message "Restore failed" -ErrorRecord $_ -Target $dbname
                return $false
            }
        }

    }
    process {
        if (Test-FunctionInterrupt) {
            return
        }
        try {
            Start-SqlAgent
        }
        catch {
            Stop-Function -Message "Failure starting SQL Agent" -ErrorRecord $_
            return
        }

        $start = Get-Date
        Write-Message -Level Verbose -Message "Starting Rationalisation Script at $start."

        foreach ($dbname in $Database) {

            $db = $sourceserver.databases[$dbname]

            # The db check is needed when the number of databases exceeds 255, then it's no longer auto-populated
            if (!$db) {
                Stop-Function -Message "$dbname does not exist on $source. Aborting routine for this database." -Continue
            }

            $lastFullBckDuration = (Get-DbaBackupHistory -SqlInstance $sourceserver -Database $dbname -LastFull).Duration

            if (-NOT ([string]::IsNullOrEmpty($lastFullBckDuration))) {
                $lastFullBckDurationSec = $lastFullBckDuration.TotalSeconds
                $lastFullBckDurationMin = [Math]::Round($lastFullBckDuration.TotalMinutes, 2)

                Write-Message -Level Verbose -Message "From the backup history the last full backup took $lastFullBckDurationSec seconds ($lastFullBckDurationMin minutes)"
                if ($lastFullBckDurationSec -gt 600) {
                    Write-Message -Level Verbose -Message "Last full backup took more than 10 minutes. Do you want to continue?"

                    # Set up the parts for the user choice
                    $Title = "Backup duration"
                    $Info = "Last full backup took more than $lastFullBckDurationMin minutes. Do you want to continue?"

                    $Options = [System.Management.Automation.Host.ChoiceDescription[]] @("&Yes", "&No (Skip)")
                    [int]$Defaultchoice = 0
                    $choice = $host.UI.PromptForChoice($Title, $Info, $Options, $Defaultchoice)
                    # Check the given option
                    if ($choice -eq 1) {
                        Stop-Function -Message "You have chosen skipping the database $dbname because of last known backup time ($lastFullBckDurationMin minutes)." -ErrorRecord $_ -Target $dbname -Continue
                        Continue
                    }
                }
            }
            else {
                Write-Message -Level Verbose -Message "Couldn't find last full backup time for database $dbname using Get-DbaBackupHistory."
            }

            $jobname = "Rationalised Database Restore Script for $dbname"
            $jobStepName = "Restore the $dbname database from Final Backup"
            $jobServer = $destserver.JobServer

            if ($jobServer.Jobs[$jobname].count -gt 0) {
                if ($force -eq $false) {
                    Stop-Function -Message "FAILED: The Job $jobname already exists. Have you done this before? Rename the existing job and try again or use -Force to drop and recreate." -Continue
                }
                else {
                    if ($Pscmdlet.ShouldProcess($dbname, "Dropping $jobname on $source")) {
                        Write-Message -Level Verbose -Message "Dropping $jobname on $source."
                        $jobServer.Jobs[$jobname].Drop()
                        $jobServer.Jobs.Refresh()
                    }
                }
            }


            Write-Message -Level Verbose -Message "Starting Rationalisation of $dbname."
            ## if we want to Dbcc before to abort if we have a corrupt database to start with
            if ($NoDbccCheckDb -eq $false) {
                if ($Pscmdlet.ShouldProcess($dbname, "Running dbcc check on $dbname on $source")) {
                    Write-Message -Level Verbose -Message "Starting DBCC CHECKDB for $dbname on $source."
                    $dbccgood = Start-DbccCheck -Server $sourceserver -DBName $dbname

                    if ($dbccgood -eq $false) {
                        if ($force -eq $false) {
                            Write-Message -Level Verbose -Message "DBCC failed for $dbname (you should check that).  Aborting routine for this database."
                            continue
                        }
                        else {
                            Write-Message -Level Verbose -Message "DBCC failed, but Force specified. Continuing."
                        }
                    }
                }
            }

            if ($Pscmdlet.ShouldProcess($source, "Backing up $dbname")) {
                Write-Message -Level Verbose -Message "Starting Backup for $dbname on $source."
                ## Take a Backup
                try {
                    $timenow = [DateTime]::Now.ToString('yyyyMMdd_HHmmss')
                    $backup = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Backup
                    $backup.Action = [Microsoft.SqlServer.Management.SMO.BackupActionType]::Database
                    $backup.BackupSetDescription = "Final Full Backup of $dbname Prior to Dropping"
                    $backup.Database = $dbname
                    $backup.Checksum = $True
                    if ($sourceserver.versionMajor -gt 9) {
                        $backup.CompressionOption = $BackupCompression
                    }
                    if ($force -and $dbccgood -eq $false) {

                        $filename = "$backupFolder\$($dbname)_DBCCERROR_$timenow.bak"
                    }
                    else {
                        $filename = "$backupFolder\$($dbname)_Final_Before_Drop_$timenow.bak"
                    }

                    $devicetype = [Microsoft.SqlServer.Management.Smo.DeviceType]::File
                    $backupDevice = New-Object -TypeName Microsoft.SqlServer.Management.Smo.BackupDeviceItem($filename, $devicetype)

                    $backup.Devices.Add($backupDevice)
                    #Progress
                    $percent = [Microsoft.SqlServer.Management.Smo.PercentCompleteEventHandler] {
                        Write-Progress -id 1 -activity "Backing up database $dbname on $source to $filename" -percentcomplete $_.Percent -status ([System.String]::Format("Progress: {0} %", $_.Percent))
                    }
                    $backup.add_PercentComplete($percent)
                    $backup.add_Complete($complete)
                    Write-Progress -id 1 -activity "Backing up database $dbname on $source to $filename" -percentcomplete 0 -status ([System.String]::Format("Progress: {0} %", 0))
                    $backup.SqlBackup($sourceserver)
                    $null = $backup.Devices.Remove($backupDevice)
                    Write-Progress -id 1 -activity "Backing up database $dbname  on $source to $filename" -status "Complete" -Completed
                    Write-Message -Level Verbose -Message "Backup Completed for $dbname on $source."

                    Write-Message -Level Verbose -Message "Running Restore Verify only on Backup of $dbname on $source."
                    try {
                        $restoreverify = New-Object 'Microsoft.SqlServer.Management.Smo.Restore'
                        $restoreverify.Database = $dbname
                        $restoreverify.Devices.AddDevice($filename, $devicetype)
                        $result = $restoreverify.SqlVerify($sourceserver)

                        if ($result -eq $false) {
                            Write-Message -Level Warning -Message "FAILED : Restore Verify Only failed for $filename on $server - aborting routine for this database."
                            continue
                        }

                        Write-Message -Level Verbose -Message "Restore Verify Only for $filename succeeded."
                    }
                    catch {
                        Stop-Function -Message "FAILED : Restore Verify Only failed for $filename on $server - aborting routine for this database. Exception: $_" -Target $filename -ErrorRecord $_ -Continue
                    }
                }
                catch {
                    Stop-Function -Message "FAILED : Restore Verify Only failed for $filename on $server - aborting routine for this database. Exception: $_" -Target $filename -ErrorRecord $_ -Continue
                }
            }

            if ($Pscmdlet.ShouldProcess($destination, "Creating Automated Restore Job from Golden Backup for $dbname on $destination")) {
                Write-Message -Level Verbose -Message "Creating Automated Restore Job from Golden Backup for $dbname on $destination."
                try {
                    if ($force -eq $true -and $dbccgood -eq $false) {
                        $jobName = $jobname -replace "Rationalised", "DBCC ERROR"
                    }

                    ## Create an agent job to restore the database
                    $job = New-Object Microsoft.SqlServer.Management.Smo.Agent.Job $jobServer, $jobname
                    $job.Name = $jobname
                    $job.OwnerLoginName = $jobowner
                    $job.Description = "This job will restore the $dbname database using the final backup located at $filename."

                    ## Create a Job Category
                    if (!$jobServer.JobCategories[$categoryname]) {
                        New-SqlAgentJobCategory -JobServer $jobServer -categoryname $categoryname
                    }

                    $job.Category = $categoryname
                    try {
                        if ($Pscmdlet.ShouldProcess($destination, "Creating Agent Job on $destination")) {
                            Write-Message -Level Verbose -Message "Created Agent Job $jobname on $destination."
                            $job.Create()
                        }
                    }
                    catch {
                        Stop-Function -Message "FAILED : To Create Agent Job $jobname on $destination - aborting routine for this database." -Target $categoryname -ErrorRecord $_ -Continue
                    }

                    ## Create Job Step
                    ## Aaron's Suggestion: In the restore script, add a comment block that tells the last known size of each file in the database.
                    ## Suggestion check for disk space before restore
                    ## Create Restore Script
                    try {
                        $restore = New-Object Microsoft.SqlServer.Management.Smo.Restore
                        $device = New-Object -TypeName Microsoft.SqlServer.Management.Smo.BackupDeviceItem $filename, 'FILE'
                        $restore.Devices.Add($device)
                        try {
                            $filelist = $restore.ReadFileList($destserver)
                        }

                        catch {
                            throw 'File list could not be determined. This is likely due to connectivity issues or tiemouts with the Sql Server, the database version is incorrect, or the Sql Server service account does not have access to the file share. Script terminating.'
                        }

                        $filestructure = Get-OfflineSqlFileStructure $destserver $dbname $filelist $ReuseSourceFolderStructure

                        $jobStepCommand = Restore-Database $destserver $dbname $filename "Database" $filestructure -TSql -ErrorAction Stop
                        $jobStep = new-object Microsoft.SqlServer.Management.Smo.Agent.JobStep $job, $jobStepName
                        $jobStep.SubSystem = 'TransactSql' # 'PowerShell'
                        $jobStep.DatabaseName = 'master'
                        $jobStep.Command = $jobStepCommand
                        $jobStep.OnSuccessAction = 'QuitWithSuccess'
                        $jobStep.OnFailAction = 'QuitWithFailure'
                        if ($Pscmdlet.ShouldProcess($destination, "Creating Agent JobStep on $destination")) {
                            $null = $jobStep.Create()
                        }
                        $jobStartStepid = $jobStep.ID
                        Write-Message -Level Verbose -Message "Created Agent JobStep $jobStepName on $destination."
                    }
                    catch {
                        Stop-Function -Message "FAILED : To Create Agent JobStep $jobStepName on $destination - Aborting." -Target $jobStepName -ErrorRecord $_ -Continue
                    }
                    if ($Pscmdlet.ShouldProcess($destination, "Applying Agent Job $jobname to $destination")) {
                        $job.ApplyToTargetServer($destination)
                        $job.StartStepID = $jobStartStepid
                        $job.Alter()
                    }
                }
                catch {
                    Stop-Function -Message "FAILED : To Create Agent Job $jobname on $destination - aborting routine for $dbname. Exception: $_" -Target $jobname -ErrorRecord $_ -Continue
                }
            }

            if ($Pscmdlet.ShouldProcess($destination, "Dropping Database $dbname on $sourceserver")) {
                ## Drop the database
                try {
                    $null = Remove-DbaDatabase -SqlInstance $sourceserver -Database $dbname -Confirm:$false
                    Write-Message -Level Verbose -Message "Dropped $dbname Database on $source prior to running the Agent Job"
                }
                catch {
                    Stop-Function -Message "FAILED : To Drop database $dbname on $server - aborting routine for $dbname. Exception: $_" -Continue
                }
            }

            if ($Pscmdlet.ShouldProcess($destination, "Running Agent Job on $destination to restore $dbname")) {
                ## Run the restore job to restore it
                Write-Message -Level Verbose -Message "Starting $jobname on $destination."
                try {
                    $job = $destserver.JobServer.Jobs[$jobname]
                    $job.Start()
                    $job.Refresh()
                    $status = $job.CurrentRunStatus

                    while ($status -ne 'Idle') {
                        Write-Message -Level Verbose -Message "Restore Job for $dbname on $destination is $status."
                        Start-Sleep -Seconds 15
                        $job.Refresh()
                        $status = $job.CurrentRunStatus
                    }

                    Write-Message -Level Verbose -Message "Restore Job $jobname has completed on $destination."
                    Write-Message -Level Verbose -Message "Sleeping for a few seconds to ensure the next step (DBCC) succeeds."
                    Start-Sleep -Seconds 10 ## This is required to ensure the next DBCC Check succeeds
                }
                catch {
                    Stop-Function -Message "FAILED : Restore Job $jobname failed on $destination - aborting routine for $dbname. Exception: $_" -Continue
                }

                if ($job.LastRunOutcome -ne 'Succeeded') {
                    # LOL, love the plug.
                    Write-Message -Level Warning -Message "FAILED : Restore Job $jobname failed on $destination - aborting routine for $dbname."
                    Write-Message -Level Warning -Message "Check the Agent Job History on $destination - if you have SSMS2016 July release or later."
                    Write-Message -Level Warning -Message "Get-SqlAgentJobHistory -JobName '$jobname' -ServerInstance $destination -OutcomesType Failed."
                    continue
                }
            }

            $refreshRetries = 1

            while ($null -eq ($destserver.databases[$dbname]) -and $refreshRetries -lt 6) {
                Write-Message -Level verbose -Message "Database $dbname not found! Refreshing collection."

                #refresh database list, otherwise the next step (DBCC) can fail
                $destserver.Databases.Refresh()

                Start-Sleep -Seconds 1

                $refreshRetries += 1
            }


            ## Run a Dbcc No choice here
            if ($Pscmdlet.ShouldProcess($dbname, "Running Dbcc CHECKDB on $dbname on $destination")) {
                Write-Message -Level Verbose -Message "Starting Dbcc CHECKDB for $dbname on $destination."
                $null = Start-DbccCheck -Server $destserver -DbName $dbname
            }

            if ($Pscmdlet.ShouldProcess($dbname, "Dropping Database $dbname on $destination")) {
                ## Drop the database
                try {
                    $null = Remove-DbaDatabase -SqlInstance $sourceserver -Database $dbname -Confirm:$false
                    Write-Message -Level Verbose -Message "Dropped $dbname database on $destination."
                }
                catch {
                    Stop-Function -Message "FAILED : To Drop database $dbname on $destination - Aborting. Exception: $_" -Target $dbname -ErrorRecord $_ -Continue
                }
            }
            Write-Message -Level Verbose -Message "Rationalisation Finished for $dbname."

            [PSCustomObject]@{
                SqlInstance     = $source
                DatabaseName    = $dbname
                JobName         = $jobname
                TestingInstance = $destination
                BackupFolder    = $backupFolder
            }
        }
    }

    end {
        if (Test-FunctionInterrupt) {
            return
        }
        if ($Pscmdlet.ShouldProcess("console", "Showing final message")) {
            $End = Get-Date
            Write-Message -Level Verbose -Message "Finished at $End."
            $Duration = $End - $start
            Write-Message -Level Verbose -Message "Script Duration: $Duration."
        }

        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Remove-SqlDatabaseSafely
    }
}
tools\dbatools\functions\Remove-DbaDbCertificate.ps1
function Remove-DbaDbCertificate {
    <#
.SYNOPSIS
Deletes specified database certificate

.DESCRIPTION
Deletes specified database certificate

.PARAMETER SqlInstance
The SQL Server to create the certificates on.

.PARAMETER SqlCredential
Allows you to login to SQL Server using alternative credentials.

.PARAMETER Database
The database where the certificate will be removed.

.PARAMETER Certificate
The certificate that will be removed

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.PARAMETER InputObject
Piped certificate objects

.NOTES
Tags: Certificate
Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
Remove-DbaDbCertificate -SqlInstance Server1

The certificate in the master database on server1 will be removed if it exists.

.EXAMPLE
Remove-DbaDbCertificate -SqlInstance Server1 -Database db1 -Confirm:$false

Suppresses all prompts to remove the certificate in the 'db1' database and drops the key.


#>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true, ConfirmImpact = "High")]
    param (
        [parameter(Mandatory, ParameterSetName = "instance")]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory, ParameterSetName = "instance")]
        [object[]]$Database,
        [parameter(Mandatory, ParameterSetName = "instance")]
        [object[]]$Certificate,
        [parameter(ValueFromPipeline, ParameterSetName = "collection")]
        [Microsoft.SqlServer.Management.Smo.Certificate[]]$InputObject,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {

        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Remove-DbaDatabaseCertificate

        function drop-cert ($smocert) {
            $server = $smocert.Parent.Parent
            $instance = $server.DomainInstanceName
            $cert = $smocert.Name
            $db = $smocert.Parent.Name

            $output = [pscustomobject]@{
                ComputerName = $server.ComputerName
                InstanceName = $server.ServiceName
                SqlInstance  = $instance
                Database     = $db
                Certificate  = $cert
                Status       = $null
            }

            if ($Pscmdlet.ShouldProcess($instance, "Dropping the certificate named $cert for database '$db' on $server")) {
                try {
                    $smocert.Drop()
                    Write-Message -Level Verbose -Message "Successfully removed certificate named $cert from the $db database on $server"
                    $output.status = "Success"
                }
                catch {
                    $output.Status = "Failure"
                    Stop-Function -Message "Failed to drop certificate named $cert from $db on $server." -Target $smocert -InnerErrorRecord $_ -Continue
                }
                $output
            }
        }
    }
    process {

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            foreach ($db in $Database) {
                $currentdb = $server.Databases[$db]

                if ($null -eq $currentdb) {
                    Stop-Function -Message "Database '$db' does not exist on $server" -Target $currentdb -Continue
                }

                if (-not $currentdb.IsAccessible) {
                    Stop-Function -Message "Database '$db' is not accessible" -Target $currentdb -Continue
                }

                foreach ($cert in $certificate) {
                    $smocert = $currentdb.Certificates[$cert]

                    if ($null -eq $smocert) {
                        Stop-Function -Message "No certificate named $cert exists in the $db database on $server" -Target $currentdb.Certificates -Continue
                    }

                    Drop-Cert -smocert $smocert
                }
            }
        }

        foreach ($smocert in $InputObject) {
            Drop-Cert -smocert $smocert
        }
    }
}
tools\dbatools\functions\Remove-DbaDbSnapshot.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Remove-DbaDbSnapshot {
    <#
    .SYNOPSIS
        Removes database snapshots

    .DESCRIPTION
        Removes (drops) database snapshots from the server

    .PARAMETER SqlInstance
        The SQL Server that you're connecting to

    .PARAMETER SqlCredential
        Credential object used to connect to the SQL Server as a different user

    .PARAMETER Database
        Removes snapshots for only this specific base db

    .PARAMETER ExcludeDatabase
        Removes snapshots excluding this specific base dbs

    .PARAMETER Snapshot
        Restores databases from snapshot with this name only

    .PARAMETER AllSnapshots
        Specifies that you want to remove all snapshots from the server

    .PARAMETER Force
        Will forcibly kill all running queries that prevent the drop process.

    .PARAMETER WhatIf
        Shows what would happen if the command were to run

    .PARAMETER Confirm
        Prompts for confirmation of every step.

    .PARAMETER InputObject
        Enables input from Get-DbaDbSnapshot

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Tags: Snapshot, Database
        Author: niphlod

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
         https://dbatools.io/Remove-DbaDbSnapshot

    .EXAMPLE
        Remove-DbaDbSnapshot -SqlInstance sql2014 -Snapshot HR_snap_20161201, HR_snap_20161101

        Removes database snapshots named HR_snap_20161201 and HR_snap_20161101

    .EXAMPLE
        Remove-DbaDbSnapshot -SqlInstance sql2014 -Database HR, Accounting

        Removes all database snapshots having HR and Accounting as base dbs

    .EXAMPLE
        Get-DbaDbSnapshot -SqlInstance sql2014 -Database HR, Accounting | Remove-DbaDbSnapshot

        Removes all database snapshots having HR and Accounting as base dbs

    .EXAMPLE
        Remove-DbaDbSnapshot -SqlInstance sql2014 -Snapshot HR_snapshot, Accounting_snapshot

        Removes HR_snapshot and Accounting_snapshot

    .EXAMPLE
        Get-DbaDbSnapshot -SqlInstance sql2016 | Where SnapshotOf -like '*dumpsterfire*' | Remove-DbaDbSnapshot

        Removes all snapshots associated with databases that have dumpsterfire in the name

    .EXAMPLE
        Get-DbaDbSnapshot -SqlInstance sql2016 | Out-GridView -Passthru | Remove-DbaDbSnapshot

        Allows the selection of snapshots on sql2016 to remove

    .EXAMPLE
        Remove-DbaDbSnapshot -SqlInstance sql2014 -AllSnapshots

        Removes all database snapshots from sql2014

    .EXAMPLE
        Remove-DbaDbSnapshot -SqlInstance sql2014 -AllSnapshots -Confirm

        Removes all database snapshots from sql2014 and prompts for each database
#>
    [CmdletBinding(SupportsShouldProcess)]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [string[]]$Database,
        [string[]]$ExcludeDatabase,
        [string[]]$Snapshot,
        [parameter(ValueFromPipeline)]
        [Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
        [switch]$AllSnapshots,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        $defaultprops = 'ComputerName', 'InstanceName', 'SqlInstance', 'Database as Name', 'Status'
    }
    process {
        if (!$Snapshot -and !$Database -and !$AllSnapshots -and $null -eq $InputObject -and !$ExcludeDatabase) {
            Stop-Function -Message "You must pipe in a snapshot or specify -Snapshot, -Database, -ExcludeDatabase or -AllSnapshots"
            return
        }

        # if piped value either doesn't exist or is not the proper type
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $InputObject += Get-DbaDbSnapshot -SqlInstance $server -Database $Database -ExcludeDatabase $ExcludeDatabase -Snapshot $Snapshot
        }

        foreach ($db in $InputObject) {
            $server = $db.Parent

            if (-not $db.DatabaseSnapshotBaseName) {
                Stop-Function -Message "$db on $server is not a database snapshot" -Continue
            }

            if ($Force) {
                $db | Remove-DbaDatabase -Confirm:$false | Select-DefaultView -Property $defaultprops
            }
            else {
                try {
                    if ($Pscmdlet.ShouldProcess("$db on $server", "Drop snapshot")) {
                        $db.Drop()
                        $server.Refresh()

                        [pscustomobject]@{
                            ComputerName   = $server.ComputerName
                            InstanceName   = $server.ServiceName
                            SqlInstance    = $server.DomainInstanceName
                            Database       = $db.name
                            Status         = "Dropped"
                        } | Select-DefaultView -Property $defaultprops
                    }
                }
                catch {
                    Write-Message -Level Verbose -Message "Could not drop database $db on $server"

                    [pscustomobject]@{
                        ComputerName   = $server.ComputerName
                        InstanceName   = $server.ServiceName
                        SqlInstance    = $server.DomainInstanceName
                        Database       = $db.name
                        Status         = (Get-ErrorMessage -Record $_)
                    } | Select-DefaultView -Property $defaultprops
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Remove-DbaDatabaseSnapshot
    }
}
tools\dbatools\functions\Remove-DbaDbUser.ps1
function Remove-DbaDbUser {
    <#
    .SYNOPSIS
    Drop database user

    .DESCRIPTION
    If user is the owner of a schema with the same name and if if the schema does not have any underlying objects the schema will be
    dropped.  If user owns more than one schema, the owner of the schemas that does not have the same name as the user, will be
    changed to 'dbo'. If schemas have underlying objects, you must specify the -Force parameter so the user can be dropped.

    .PARAMETER SqlInstance
    The SQL Instances that you're connecting to.

    .PARAMETER SqlCredential
    Credential object used to connect to the SQL Server as a different user.

    .PARAMETER Database
    Specifies the database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

    .PARAMETER ExcludeDatabase
    Specifies the database(s) to exclude from processing. Options for this list are auto-populated from the server.

    .PARAMETER User
    Specifies the list of users to remove.

    .PARAMETER InputObject
    Support piping from Get-DbaDatabaseUser.

    .PARAMETER Force
    If enabled this will force the change of the owner to 'dbo' for any schema which owner is the User.

    .PARAMETER WhatIf
    Shows what would happen if the command were to run. No actions are actually performed.

    .PARAMETER Confirm
    If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

    .PARAMETER EnableException
    By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
    Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
    Tags: Database, User, Login, Security
    Author: Doug Meyers (@dgmyrs)

    Website: https://dbatools.io
    Copyright: (C) Chrissy LeMaire, [email protected]
    License: MIT https://opensource.org/licenses/MIT

    .LINK
    https://dbatools.io/Remove-DbaDbUser

    .EXAMPLE
    Remove-DbaDbUser -SqlInstance sqlserver2014 -User user1

    Drops user1 from all databases it exists in on server 'sqlserver2014'.

    .EXAMPLE
    Remove-DbaDbUser -SqlInstance sqlserver2014 -Database database1 -User user1

    Drops user1 from the database1 database on server 'sqlserver2014'.

    .EXAMPLE
    Remove-DbaDbUser -SqlInstance sqlserver2014 -ExcludeDatabase model -User user1

    Drops user1 from all databases it exists in on server 'sqlserver2014' except for the model database.

    .EXAMPLE
    Get-DbaDatabaseUser sqlserver2014 | Where-Object Name -In "user1" | Remove-DbaDbUser

    Drops user1 from all databases it exists in on server 'sqlserver2014'.

#>

    [CmdletBinding(DefaultParameterSetName = 'User', SupportsShouldProcess = $true)]
    Param (
        [parameter(Position = 1, Mandatory, ValueFromPipeline, ParameterSetName = 'User')]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,

        [parameter(ParameterSetName = 'User')]
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,

        [parameter(ParameterSetName = 'User')]
        [Alias("Databases")]
        [object[]]$Database,

        [parameter(ParameterSetName = 'User')]
        [object[]]$ExcludeDatabase,

        [parameter(Mandatory, ParameterSetName = 'User')]
        [object[]]$User,

        [parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Object')]
        [Microsoft.SqlServer.Management.Smo.User[]]$InputObject,

        [parameter(ParameterSetName = 'User')]
        [parameter(ParameterSetName = 'Object')]
        [switch]$Force,

        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        function Remove-DbUser {
            [CmdletBinding()]
            param ([Microsoft.SqlServer.Management.Smo.User[]]$users)

            foreach ($user in $users) {
                $db = $user.Parent
                $server = $db.Parent
                $ownedObjects = $false
                Write-Message -Level Verbose -Message "Removing User $user from Database $db on target $server"

                # Drop Schemas owned by the user before droping the user
                $schemaUrns = $user.EnumOwnedObjects() | Where-Object Type -EQ Schema
                if ($schemaUrns) {
                    Write-Message -Level Verbose -Message "User $user owns $($schemaUrns.Count) schema(s)."

                    # Need to gather up the schema changes so they can be done in a non-desctructive order
                    $alterSchemas = @()
                    $dropSchemas = @()

                    foreach ($schemaUrn in $schemaUrns) {
                        $schema = $server.GetSmoObject($schemaUrn)

                        # Drop any schema that is the same name as the user
                        if ($schema.Name -EQ $user.Name) {
                            # Check for owned objects early so we can exit before any changes are made
                            $ownedUrns = $schema.EnumOwnedObjects()
                            if (-Not $ownedUrns) {
                                $dropSchemas += $schema
                            }
                            else {
                                Write-Message -Level Warning -Message "User owns objects in the database and will not be removed."
                                foreach ($ownedUrn in $ownedUrns) {
                                    $obj = $server.GetSmoObject($ownedUrn)
                                    Write-Message -Level Warning -Message "User $user owns $($obj.GetType().Name) $obj"
                                }
                                $ownedObjects = $true
                            }
                        }

                        # Change the owner of any schema not the same name as the user
                        if ($schema.Name -NE $user.Name) {
                            # Check for owned objects early so we can exit before any changes are made
                            $ownedUrns = $schema.EnumOwnedObjects()
                            if (($ownedUrns -And $Force) -Or (-Not $ownedUrns)) {
                                $alterSchemas += $schema
                            }
                            else {
                                Write-Message -Level Warning -Message "User $user owns the Schema $schema, which owns $($ownedUrns.Count) Object(s).  If you want to change the schemas' owner to [dbo] and drop the user anyway, use -Force parameter.  User $user will not be removed."
                                $ownedObjects = $true
                            }
                        }
                    }
                }

                if (-Not $ownedObjects) {
                    try {
                        # Alter Schemas
                        foreach ($schema in $alterSchemas) {
                            Write-Message -Level Verbose -Message "Owner of Schema $schema will be changed to [dbo]."
                            if ($PSCmdlet.ShouldProcess($server, "Change the owner of Schema $schema to [dbo].")) {
                                $schema.Owner = "dbo"
                                $schema.Alter()
                            }
                        }

                        # Drop Schemas
                        foreach ($schema in $dropSchemas) {
                            if ($PSCmdlet.ShouldProcess($server, "Drop Schema $schema from Database $db.")) {
                                $schema.Drop()
                            }
                        }

                        # Finally, Drop user
                        if ($PSCmdlet.ShouldProcess($server, "Drop User $user from Database $db.")) {
                            $user.Drop()
                        }

                        $status = "Dropped"

                    }
                    catch {
                        Write-Error -Message "Could not drop $user from Database $db on target $server"
                        $status = "Not Dropped"
                    }

                    [pscustomobject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Database     = $db.name
                        User         = $user
                        Status       = $status
                    }
                }
            }
        }
    }

    process {
        if ($InputObject) {
            Remove-DbUser $InputObject
        }
        else {
            foreach ($instance in $SqlInstance) {
                try {
                    Write-Message -Level Verbose -Message "Connecting to $instance"
                    $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
                }
                catch {
                    Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                }

                $databases = $server.Databases | Where-Object IsAccessible

                if ($Database) {
                    $databases = $databases | Where-Object Name -In $Database
                }
                if ($ExcludeDatabase) {
                    $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
                }

                foreach ($db in $databases) {
                    Write-Message -Level Verbose -Message "Get users in Database $db on target $server"
                    $users = Get-DbaDatabaseUser -SqlInstance $server -Database $db.Name
                    $users = $users | Where-Object Name -In $User
                    Remove-DbUser $users
                }
            }
        }
    }

}
tools\dbatools\functions\Remove-DbaLogin.ps1
function Remove-DbaLogin {
    <#
.SYNOPSIS
Drops a Login

.DESCRIPTION
Tries a bunch of different ways to remove a Login or two or more.

.PARAMETER SqlInstance
The SQL Server instance holding the Logins to be removed.You must have sysadmin access and server version must be SQL Server version 2000 or higher.

.PARAMETER SqlCredential
Allows you to login to servers using alternative credentials.

.PARAMETER Login
The Login(s) to process - this list is auto-populated from the server. If unspecified, all Logins will be processed.

.PARAMETER InputObject
A collection of Logins (such as returned by Get-DbaLogin), to be removed.

.PARAMETER Force
Kills any sessions associated with the login prior to drop

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Delete, Logins

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Remove-DbaLogin

.EXAMPLE
Remove-DbaLogin -SqlInstance sql2016 -Login mylogin

Prompts then removes the Login mylogin on SQL Server sql2016

.EXAMPLE
Remove-DbaLogin -SqlInstance sql2016 -Login mylogin, yourlogin

Prompts then removes the Logins mylogin and yourlogin on SQL Server sql2016

.EXAMPLE
Remove-DbaLogin -SqlInstance sql2016 -Login mylogin -Confirm:$false

Does not prompt and swiftly removes mylogin on SQL Server sql2016

.EXAMPLE
Get-DbaLogin -SqlInstance server\instance -Login yourlogin | Remove-DbaLogin

removes mylogin on SQL Server server\instance

#>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High', DefaultParameterSetName = "Default")]
    Param (
        [parameter(Mandatory, ParameterSetName = "instance")]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [parameter(Mandatory = $false)]
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [parameter(Mandatory, ParameterSetName = "instance")]
        [string[]]$Login,
        [Parameter(ValueFromPipeline, Mandatory, ParameterSetName = "Logins")]
        [Microsoft.SqlServer.Management.Smo.Login[]]$InputObject,
        [switch]$Force,
        [switch]$EnableException
    )
    
    process {
        
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            $InputObject += $server.Logins | Where-Object { $_.Name -in $Login }
        }
        
        foreach ($currentlogin in $InputObject) {
            try {
                $server = $currentlogin.Parent
                if ($Pscmdlet.ShouldProcess("$currentlogin on $server", "KillLogin")) {
                    if ($force) {
                        $null = Stop-DbaProcess -SqlInstance $server -Login $currentlogin.name
                    }
                    
                    $currentlogin.Drop()
                    
                    [pscustomobject]@{
                        ComputerName  = $server.ComputerName
                        InstanceName  = $server.ServiceName
                        SqlInstance   = $server.DomainInstanceName
                        Login         = $currentlogin.name
                        Status        = "Dropped"
                    }
                }
            }
            catch {
                [pscustomobject]@{
                    ComputerName  = $server.ComputerName
                    InstanceName  = $server.ServiceName
                    SqlInstance   = $server.DomainInstanceName
                    Login         = $currentlogin.name
                    Status        = $_
                }
                Stop-Function -Message "Could not drop Login $currentlogin on $server" -ErrorRecord $_ -Target $currentlogin -Continue
            }
        }
    }
}
tools\dbatools\functions\Remove-DbaNetworkCertificate.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#

function Remove-DbaNetworkCertificate {
    <#
    .SYNOPSIS
        Removes the network certificate for SQL Server instance

    .DESCRIPTION
        Removes the network certificate for SQL Server instance. This setting is found in Configuration Manager.

    .PARAMETER SqlInstance
        The target SQL Server - defaults to localhost. If target is a cluster, you must also specify InstanceClusterName (see below)

    .PARAMETER Credential
        Allows you to login to the computer (not sql instance) using alternative credentials.

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .PARAMETER WhatIf
        Shows what would happen if the command were to run. No actions are actually performed.

    .PARAMETER Confirm
        Prompts you for confirmation before executing any changing operations within the command.

    .EXAMPLE
        Remove-DbaNetworkCertificate

        Removes the Network Certificate for the default instance (MSSQLSERVER) on localhost

    .EXAMPLE
        Remove-DbaNetworkCertificate -SqlInstance sql1\SQL2008R2SP2

        Removes the Network Certificate for the SQL2008R2SP2 instance on sql1

    .EXAMPLE
        Remove-DbaNetworkCertificate -SqlInstance localhost\SQL2008R2SP2 -WhatIf

        Shows what would happen if the command were run

    .NOTES
        Tags: Certificate

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT
#>
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Low", DefaultParameterSetName = 'Default')]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer", "ComputerName")]
        [DbaInstanceParameter[]]
        $SqlInstance = $env:COMPUTERNAME,

        [PSCredential]

        $Credential,

        [switch]
        [Alias('Silent')]$EnableException
    )
    process {
        foreach ($instance in $sqlinstance) {
            Write-Message -Level VeryVerbose -Message "Processing $instance" -Target $instance
            $null = Test-ElevationRequirement -ComputerName $instance -Continue

            Write-Message -Level Verbose -Message "Resolving hostname"
            $resolved = $null
            $resolved = Resolve-DbaNetworkName -ComputerName $instance -Turbo

            if ($null -eq $resolved) {
                Stop-Function -Message "Can't resolve $instance" -Target $instance -Continue -Category InvalidArgument
            }

            Write-Message -Level Output -Message "Connecting to SQL WMI on $($instance.ComputerName)"
            try {
                $sqlwmi = Invoke-ManagedComputerCommand -ComputerName $resolved.FQDN -ScriptBlock { $wmi.Services } -Credential $Credential -ErrorAction Stop | Where-Object DisplayName -eq "SQL Server ($($instance.InstanceName))"
            }
            catch {
                Stop-Function -Message "Failed to access $instance" -Target $instance -Continue -ErrorRecord $_
            }

            $regroot = ($sqlwmi.AdvancedProperties | Where-Object Name -eq REGROOT).Value
            $vsname = ($sqlwmi.AdvancedProperties | Where-Object Name -eq VSNAME).Value
            $instancename = $sqlwmi.DisplayName.Replace('SQL Server (', '').Replace(')', '') # Don't clown, I don't know regex :(
            $serviceaccount = $sqlwmi.ServiceAccount

            if ([System.String]::IsNullOrEmpty($regroot)) {
                $regroot = $sqlwmi.AdvancedProperties | Where-Object { $_ -match 'REGROOT' }
                $vsname = $sqlwmi.AdvancedProperties | Where-Object { $_ -match 'VSNAME' }

                if (![System.String]::IsNullOrEmpty($regroot)) {
                    $regroot = ($regroot -Split 'Value\=')[1]
                    $vsname = ($vsname -Split 'Value\=')[1]
                }
                else {
                    Stop-Function -Message "Can't find instance $vsname on $instance" -Continue -Category ObjectNotFound -Target $instance
                }
            }

            if ([System.String]::IsNullOrEmpty($vsname)) { $vsname = $instance }

            Write-Message -Level Output -Message "Regroot: $regroot" -Target $instance
            Write-Message -Level Output -Message "ServiceAcct: $serviceaccount" -Target $instance
            Write-Message -Level Output -Message "InstanceName: $instancename" -Target $instance
            Write-Message -Level Output -Message "VSNAME: $vsname" -Target $instance

            $scriptblock = {
                $regroot = $args[0]
                $serviceaccount = $args[1]
                $instancename = $args[2]
                $vsname = $args[3]

                $regpath = "Registry::HKEY_LOCAL_MACHINE\$($args[0])\MSSQLServer\SuperSocketNetLib"
                $cert = (Get-ItemProperty -Path $regpath -Name Certificate).Certificate
                Set-ItemProperty -Path $regpath -Name Certificate -Value $null

                [pscustomobject]@{
                    ComputerName      = $env:COMPUTERNAME
                    InstanceName      = $instancename
                    SqlInstance       = $vsname
                    ServiceAccount    = $serviceaccount
                    RemovedThumbprint = $cert.Thumbprint
                }
            }

            if ($PScmdlet.ShouldProcess("local", "Connecting to $ComputerName to remove the cert")) {
                try {
                    Invoke-Command2 -ComputerName $resolved.fqdn -Credential $Credential -ArgumentList $regroot, $serviceaccount, $instancename, $vsname -ScriptBlock $scriptblock -ErrorAction Stop
                }
                catch {
                    Stop-Function -Message "Failed to connect to $($resolved.fqdn) using PowerShell remoting!" -ErrorRecord $_ -Target $instance -Continue
                }
            }
        }
    }
}
tools\dbatools\functions\Remove-DbaOrphanUser.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Remove-DbaOrphanUser {
    <#
        .SYNOPSIS
            Drop orphan users with no existing login to map

        .DESCRIPTION
            An orphan user is defined by a user that does not have their matching login. (Login property = "").

            If user is the owner of the schema with the same name and if if the schema does not have any underlying objects the schema will be dropped.

            If user owns more than one schema, the owner of the schemas that does not have the same name as the user, will be changed to 'dbo'. If schemas have underlying objects, you must specify the -Force parameter so the user can be dropped.

            If exists a login to map the drop will not be performed unless you specify the -Force parameter (only when calling from Repair-DbaOrphanUser.

        .PARAMETER SqlInstance
            The SQL Server Instance to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies the database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies the database(s) to exclude from processing. Options for this list are auto-populated from the server

        .PARAMETER User
            Specifies the list of users to remove.

        .PARAMETER Force
            If this switch is enabled:
                If exists any schema which owner is the User, this will force the change of the owner to 'dbo'.
                If exists a login to map the drop will not be performed unless you specify this parameter.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Orphan, Database, Security, Login
            Author: Claudio Silva (@ClaudioESSilva)
            Editor: Simone Bizzotto (@niphlod)
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Remove-DbaOrphanUser

        .EXAMPLE
            Remove-DbaOrphanUser -SqlInstance sql2005

            Finds and drops all orphan users without matching Logins in all databases present on server 'sql2005'.

        .EXAMPLE
            Remove-DbaOrphanUser -SqlInstance sqlserver2014a -SqlCredential $cred

            Finds and drops all orphan users without matching Logins in all databases present on server 'sqlserver2014a'. SQL Server authentication will be used in connecting to the server.

        .EXAMPLE
            Remove-DbaOrphanUser -SqlInstance sqlserver2014a -Database db1, db2 -Force

            Finds and drops orphan users even if they have a matching Login on both db1 and db2 databases.

        .EXAMPLE
            Remove-DbaOrphanUser -SqlInstance sqlserver2014a -ExcludeDatabase db1, db2 -Force

            Finds and drops orphan users even if they have a matching Login from all databases except db1 and db2.

        .EXAMPLE
            Remove-DbaOrphanUser -SqlInstance sqlserver2014a -User OrphanUser

            Removes user OrphanUser from all databases only if there is no matching login.

        .EXAMPLE
            Remove-DbaOrphanUser -SqlInstance sqlserver2014a -User OrphanUser -Force

            Removes user OrphanUser from all databases even if they have a matching Login. Any schema that the user owns will change ownership to dbo.

    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [object[]]$User,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {

        foreach ($Instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $Instance."
            try {
                $server = Connect-SqlInstance -SqlInstance $Instance -SqlCredential $SqlCredential
            }
            catch {
                Write-Message -Level Warning -Message "Can't connect to $Instance or access denied. Skipping."
                continue
            }

            $DatabaseCollection = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                $DatabaseCollection = $DatabaseCollection | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $DatabaseCollection = $DatabaseCollection | Where-Object Name -NotIn $ExcludeDatabase
            }

            $CallStack = Get-PSCallStack | Select-Object -Property *
            if ($CallStack.Count -eq 1) {
                $StackSource = $CallStack[0].Command
            }
            else {
                #-2 because index base is 0 and we want the one before the last (the last is the actual command)
                $StackSource = $CallStack[($CallStack.Count - 2)].Command
            }

            if ($DatabaseCollection) {
                foreach ($db in $DatabaseCollection) {
                    try {
                        #if SQL 2012 or higher only validate databases with ContainmentType = NONE
                        if ($server.versionMajor -gt 10) {
                            if ($db.ContainmentType -ne [Microsoft.SqlServer.Management.Smo.ContainmentType]::None) {
                                Write-Message -Level Warning -Message "Database '$db' is a contained database. Contained databases can't have orphaned users. Skipping validation."
                                Continue
                            }
                        }

                        if ($StackSource -eq "Repair-DbaOrphanUser") {
                            Write-Message -Level Verbose -Message "Call origin: Repair-DbaOrphanUser."
                            #Will use collection from parameter ($User)
                        }
                        else {
                            Write-Message -Level Verbose -Message "Validating users on database $db."

                            if ($User.Count -eq 0) {
                                #the third validation will remove from list sql users without login. The rule here is Sid with length higher than 16
                                $User = $db.Users | Where-Object { $_.Login -eq "" -and ($_.ID -gt 4) -and (($_.Sid.Length -gt 16 -and $_.LoginType -eq [Microsoft.SqlServer.Management.Smo.LoginType]::SqlLogin) -eq $false) }
                            }
                            else {

                                #the fourth validation will remove from list sql users without login. The rule here is Sid with length higher than 16
                                $User = $db.Users | Where-Object { $_.Login -eq "" -and ($_.ID -gt 4) -and ($User -contains $_.Name) -and (($_.Sid.Length -gt 16 -and $_.LoginType -eq [Microsoft.SqlServer.Management.Smo.LoginType]::SqlLogin) -eq $false) }

                            }
                        }

                        if ($User.Count -gt 0) {
                            Write-Message -Level Verbose -Message "Orphan users found."
                            foreach ($dbuser in $User) {
                                $SkipUser = $false

                                $ExistLogin = $null

                                if ($StackSource -ne "Repair-DbaOrphanUser") {
                                    #Need to validate Existing Login because the call does not came from Repair-DbaOrphanUser
                                    $ExistLogin = $server.logins | Where-Object {
                                        $_.Isdisabled -eq $False -and
                                        $_.IsSystemObject -eq $False -and
                                        $_.IsLocked -eq $False -and
                                        $_.Name -eq $dbuser.Name
                                    }
                                }

                                #Schemas only appears on SQL Server 2005 (v9.0)
                                if ($server.versionMajor -gt 8) {

                                    #reset variables
                                    $AlterSchemaOwner = ""
                                    $DropSchema = ""

                                    #Validate if user owns any schema
                                    $Schemas = @()
                                    $Schemas = $db.Schemas | Where-Object Owner -eq $dbuser.Name

                                    if (@($Schemas).Count -gt 0) {
                                        Write-Message -Level Verbose -Message "User $dbuser owns one or more schemas."

                                        foreach ($sch in $Schemas) {
                                            <#
                                                On sql server 2008 or lower the EnumObjects method does not accept empty parameter.
                                                0x1FFFFFFF is the way we can say we want everything known by those versions

                                                When it is an higher version we can use empty to get all
                                            #>
                                            if ($server.versionMajor -lt 11) {
                                                $NumberObjects = ($db.EnumObjects(0x1FFFFFFF) | Where-Object { $_.Schema -eq $sch.Name } | Measure-Object).Count
                                            }
                                            else {
                                                $NumberObjects = ($db.EnumObjects() | Where-Object { $_.Schema -eq $sch.Name } | Measure-Object).Count
                                            }

                                            if ($NumberObjects -gt 0) {
                                                if ($Force) {
                                                    Write-Message -Level Verbose -Message "Parameter -Force was used! The schema '$($sch.Name)' have $NumberObjects underlying objects. We will change schema owner to 'dbo' and drop the user."

                                                    if ($Pscmdlet.ShouldProcess($db.Name, "Changing schema '$($sch.Name)' owner to 'dbo'. -Force used.")) {
                                                        $AlterSchemaOwner += "ALTER AUTHORIZATION ON SCHEMA::[$($sch.Name)] TO [dbo]`r`n"

                                                        [pscustomobject]@{
                                                            ComputerName      = $server.ComputerName
                                                            InstanceName      = $server.ServiceName
                                                            SqlInstance       = $server.DomainInstanceName
                                                            DatabaseName      = $db.Name
                                                            SchemaName        = $sch.Name
                                                            Action            = "ALTER OWNER"
                                                            SchemaOwnerBefore = $sch.Owner
                                                            SchemaOwnerAfter  = "dbo"
                                                        }
                                                    }
                                                }
                                                else {
                                                    Write-Message -Level Warning -Message "Schema '$($sch.Name)' owned by user $($dbuser.Name) have $NumberObjects underlying objects. If you want to change the schemas' owner to 'dbo' and drop the user anyway, use -Force parameter. Skipping user '$dbuser'."
                                                    $SkipUser = $true
                                                    break
                                                }
                                            }
                                            else {
                                                if ($sch.Name -eq $dbuser.Name) {
                                                    Write-Message -Level Verbose -Message "The schema '$($sch.Name)' have the same name as user $dbuser. Schema will be dropped."

                                                    if ($Pscmdlet.ShouldProcess($db.Name, "Dropping schema '$($sch.Name)'.")) {
                                                        $DropSchema += "DROP SCHEMA [$($sch.Name)]"

                                                        [pscustomobject]@{
                                                            ComputerName      = $server.ComputerName
                                                            InstanceName      = $server.ServiceName
                                                            SqlInstance       = $server.DomainInstanceName
                                                            DatabaseName      = $db.Name
                                                            SchemaName        = $sch.Name
                                                            Action            = "DROP"
                                                            SchemaOwnerBefore = $sch.Owner
                                                            SchemaOwnerAfter  = "N/A"
                                                        }
                                                    }
                                                }
                                                else {
                                                    Write-Message -Level Warning -Message "Schema '$($sch.Name)' does not have any underlying object. Ownership will be changed to 'dbo' so the user can be dropped. Remember to re-check permissions on this schema!"

                                                    if ($Pscmdlet.ShouldProcess($db.Name, "Changing schema '$($sch.Name)' owner to 'dbo'.")) {
                                                        $AlterSchemaOwner += "ALTER AUTHORIZATION ON SCHEMA::[$($sch.Name)] TO [dbo]`r`n"

                                                        [pscustomobject]@{
                                                            ComputerName      = $server.ComputerName
                                                            InstanceName      = $server.ServiceName
                                                            SqlInstance       = $server.DomainInstanceName
                                                            DatabaseName      = $db.Name
                                                            SchemaName        = $sch.Name
                                                            Action            = "ALTER OWNER"
                                                            SchemaOwnerBefore = $sch.Owner
                                                            SchemaOwnerAfter  = "dbo"
                                                        }
                                                    }
                                                }
                                            }
                                        }

                                    }
                                    else {
                                        Write-Message -Level Verbose -Message "User $dbuser does not own any schema. Will be dropped."
                                    }

                                    $query = "$AlterSchemaOwner `r`n$DropSchema `r`nDROP USER " + $dbuser

                                    Write-Message -Level Debug -Message $query
                                }
                                else {
                                    $query = "EXEC master.dbo.sp_droplogin @loginame = N'$($dbuser.name)'"
                                }

                                if ($ExistLogin) {
                                    if (-not $SkipUser) {
                                        if ($Force) {
                                            if ($Pscmdlet.ShouldProcess($db.Name, "Dropping user $dbuser using -Force")) {
                                                $server.Databases[$db.Name].ExecuteNonQuery($query) | Out-Null
                                                Write-Message -Level Verbose -Message "User $dbuser was dropped from $($db.Name). -Force parameter was used!"
                                            }
                                        }
                                        else {
                                            Write-Message -Level Warning -Message "Orphan user $($dbuser.Name) has a matching login. The user will not be dropped. If you want to drop anyway, use -Force parameter."
                                            Continue
                                        }
                                    }
                                }
                                else {
                                    if (-not $SkipUser) {
                                        if ($Pscmdlet.ShouldProcess($db.Name, "Dropping user $dbuser")) {
                                            $server.Databases[$db.Name].ExecuteNonQuery($query) | Out-Null
                                            Write-Message -Level Verbose -Message "User $dbuser was dropped from $($db.Name)."
                                        }
                                    }
                                }
                            }
                        }
                        else {
                            Write-Message -Level Verbose -Message "No orphan users found on database $db."
                        }
                        #reset collection
                        $User = $null
                    }
                    catch {
                        Stop-Function -Message "Failure" -ErrorRecord $_ -Target $db -Continue
                    }
                }
            }
            else {
                Write-Message -Level Verbose -Message "There are no databases to analyse."
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Remove-SqlOrphanUser
    }
}
tools\dbatools\functions\Remove-DbaPfDataCollectorCounter.ps1
function Remove-DbaPfDataCollectorCounter {
    <#
        .SYNOPSIS
            Removes a Performance Data Collector Counter.

        .DESCRIPTION
            Removes a Performance Data Collector Counter.

        .PARAMETER ComputerName
            The target computer. Defaults to localhost.

        .PARAMETER Credential
            Allows you to login to $ComputerName using alternative credentials. To use:

            $cred = Get-Credential, then pass $cred object to the -Credential parameter.

        .PARAMETER CollectorSet
            The name of the Collector Set to search.

        .PARAMETER Collector
            The name of the Collector to remove.
    
        .PARAMETER Counter
            The name of the Counter - in the form of '\Processor(_Total)\% Processor Time'.
    
        .PARAMETER InputObject
            Accepts the object output by Get-DbaPfDataCollectorSet via the pipeline.
    
        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
        
        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
    
        .NOTES
            Tags: PerfMon
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
    
        .LINK
            https://dbatools.io/Remove-DbaPfDataCollectorCounter

        .EXAMPLE
            Remove-DbaPfDataCollectorCounter -ComputerName sql2017 -CollectorSet 'System Correlation' -Collector DataCollector01  -Counter '\LogicalDisk(*)\Avg. Disk Queue Length'
    
            Prompts for confirmation then removes the '\LogicalDisk(*)\Avg. Disk Queue Length' counter within the DataCollector01 collector within the System Correlation collector set on sql2017.
    
        .EXAMPLE
            Get-DbaPfDataCollectorCounter | Out-GridView -PassThru | Remove-DbaPfDataCollectorCounter -Confirm:$false
    
            Allows you to select which counters you'd like on localhost and does not prompt for confirmation.

    #>
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "High")]
    param (
        [DbaInstance[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias("DataCollectorSet")]
        [string[]]$CollectorSet,
        [Alias("DataCollector")]
        [string[]]$Collector,
        [parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [Alias("Name")]
        [object[]]$Counter,
        [parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [switch]$EnableException
    )
    begin {
        $setscript = {
            $setname = $args[0]; $removexml = $args[1]
            $CollectorSet = New-Object -ComObject Pla.DataCollectorSet
            $CollectorSet.SetXml($removexml)
            $CollectorSet.Commit($setname, $null, 0x0003) #add or modify.
            $CollectorSet.Query($setname, $Null)
        }
    }
    process {
        if ($InputObject.Credential -and (Test-Bound -ParameterName Credential -Not)) {
            $Credential = $InputObject.Credential
        }
        
        if (-not $InputObject -or ($InputObject -and (Test-Bound -ParameterName ComputerName))) {
            foreach ($computer in $ComputerName) {
                $InputObject += Get-DbaPfDataCollectorCounter -ComputerName $computer -Credential $Credential -CollectorSet $CollectorSet -Collector $Collector -Counter $Counter
            }
        }
        
        if ($InputObject) {
            if (-not $InputObject.CounterObject) {
                Stop-Function -Message "InputObject is not of the right type. Please use Get-DbaPfDataCollectorCounter."
                return
            }
        }
        
        foreach ($object in $InputObject) {
            $computer = $InputObject.ComputerName
            $null = Test-ElevationRequirement -ComputerName $computer -Continue
            $setname = $InputObject.DataCollectorSet
            $collectorname = $InputObject.DataCollector
            
            $xml = [xml]($InputObject.DataCollectorSetXml)
            
            foreach ($countername in $counter) {
                $node = $xml.SelectSingleNode("//Name[.='$collectorname']").SelectSingleNode("//Counter[.='$countername']")
                $null = $node.ParentNode.RemoveChild($node)
                $node = $xml.SelectSingleNode("//Name[.='$collectorname']").SelectSingleNode("//CounterDisplayName[.='$countername']")
                $null = $node.ParentNode.RemoveChild($node)
            }
            
            $plainxml = $xml.OuterXml
            
            if ($Pscmdlet.ShouldProcess("$computer", "Remove $countername from $collectorname with the $setname collection set")) {
                try {
                    $results = Invoke-Command2 -ComputerName $computer -Credential $Credential -ScriptBlock $setscript -ArgumentList $setname, $plainxml -ErrorAction Stop -Raw
                    Write-Message -Level Verbose -Message " $results"
                    [pscustomobject]@{
                        ComputerName     = $computer
                        DataCollectorSet = $setname
                        DataCollector    = $collectorname
                        Name             = $counterName
                        Status           = "Removed"
                    }
                }
                catch {
                    Stop-Function -Message "Failure importing $Countername to $computer." -ErrorRecord $_ -Target $computer -Continue
                }
            }
        }
    }
}
tools\dbatools\functions\Remove-DbaPfDataCollectorSet.ps1
function Remove-DbaPfDataCollectorSet {
    <#
        .SYNOPSIS
            Removes a Performance Monitor Data Collector Set

        .DESCRIPTION
            Removes a Performance Monitor Data Collector Set. When removing data collector sets from the local instance, Run As Admin is required.

        .PARAMETER ComputerName
            The target computer. Defaults to localhost.

        .PARAMETER Credential
            Allows you to login to $ComputerName using alternative credentials. To use:

            $cred = Get-Credential, then pass $cred object to the -Credential parameter.

        .PARAMETER CollectorSet
            The name of the Collector Set to remove.

        .PARAMETER InputObject
            Accepts the object output by Get-DbaPfDataCollectorSet via the pipeline.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: PerfMon
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Remove-DbaPfDataCollectorSet

        .EXAMPLE
            Remove-DbaPfDataCollectorSet

            Prompts for confirmation then removes all ready Collectors on localhost.

        .EXAMPLE
            Remove-DbaPfDataCollectorSet -ComputerName sql2017 -Confirm:$false

            Attempts to remove all ready Collectors on localhost and does not prompt to confirm.

        .EXAMPLE
            Remove-DbaPfDataCollectorSet -ComputerName sql2017, sql2016 -Credential (Get-Credential) -CollectorSet 'System Correlation'

            Prompts for confirmation then removes the 'System Correlation' Collector on sql2017 and sql2016 using alternative credentials.

        .EXAMPLE
            Get-DbaPfDataCollectorSet -CollectorSet 'System Correlation' | Remove-DbaPfDataCollectorSet

            Removes the 'System Correlation' Collector.

        .EXAMPLE
            Get-DbaPfDataCollectorSet -CollectorSet 'System Correlation' | Stop-DbaPfDataCollectorSet | Remove-DbaPfDataCollectorSet

            Stops and removes the 'System Correlation' Collector.
    #>
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "High")]
    param (
        [DbaInstance[]]$ComputerName=$env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias("DataCollectorSet")]
        [string[]]$CollectorSet,
        [parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [switch]$EnableException
    )
    begin {
        $setscript = {
            $setname = $args
            $collectorset = New-Object -ComObject Pla.DataCollectorSet
            $collectorset.Query($setname, $null)
            if ($collectorset.name -eq $setname) {
                $null = $collectorset.Delete()
            }
            else {
                Write-Warning "Data Collector Set $setname does not exist on $env:COMPUTERNAME."
            }
        }
    }
    process {
        if (-not $InputObject -or ($InputObject -and (Test-Bound -ParameterName ComputerName))) {
            foreach ($computer in $ComputerName) {
                $InputObject += Get-DbaPfDataCollectorSet -ComputerName $computer -Credential $Credential -CollectorSet $CollectorSet
            }
        }

        if ($InputObject) {
            if (-not $InputObject.DataCollectorSetObject) {
                Stop-Function -Message "InputObject is not of the right type. Please use Get-DbaPfDataCollectorSet."
                return
            }
        }

        # Check to see if its running first
        foreach ($set in $InputObject) {
            $setname = $set.Name
            $computer = $set.ComputerName
            $status = $set.State

            $null = Test-ElevationRequirement -ComputerName $computer -Continue

            Write-Message -Level Verbose -Message "$setname on $ComputerName is $status."

            if ($status -eq "Running") {
                Stop-Function -Message "$setname on $computer is running. Use Stop-DbaPfDataCollectorSet to stop first." -Continue
            }

            if ($Pscmdlet.ShouldProcess("$computer", "Removing collector set $setname")) {
                Write-Message -Level Verbose -Message "Connecting to $computer using Invoke-Command."
                try {
                    Invoke-Command2 -ComputerName $computer -Credential $Credential -ScriptBlock $setscript -ArgumentList $setname -ErrorAction Stop
                    [pscustomobject]@{
                        ComputerName = $computer
                        Name         = $setname
                        Status       = "Removed"
                    }
                }
                catch {
                    Stop-Function -Message "Failure Removing $setname on $computer." -ErrorRecord $_ -Target $computer -Continue
                }
            }
        }
    }
}
tools\dbatools\functions\Remove-DbaRegisteredServer.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Remove-DbaRegisteredServer {
    <#
        .SYNOPSIS
            Removes registered servers found in SQL Server Central Management Server (CMS).

        .DESCRIPTION
            Removes registered servers found in SQL Server Central Management Server (CMS).

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Name
            Specifies one or more names to include. Name is the visible name in SSMS CMS interface (labeled Registered Server Name)

        .PARAMETER ServerName
            Specifies one or more server names to include. Server Name is the actual instance name (labeled Server Name)

        .PARAMETER Group
            Specifies one or more groups to include from SQL Server Central Management Server.

        .PARAMETER InputObject
            Allows results from Get-DbaRegisteredServer to be piped in

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.

            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.

            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Chrissy LeMaire (@cl)
            Tags: RegisteredServer, CMS

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Remove-DbaRegisteredServer

        .EXAMPLE
            Remove-DbaRegisteredServer -SqlInstance sql2012 -Group HR, Accounting

            Removes all servers from the HR and Accounting groups on sql2012

        .EXAMPLE
            Remove-DbaRegisteredServer -SqlInstance sql2012 -Group HR\Development

            Removes all servers from the HR and sub-group Development from the CMS on sql2012.

        .EXAMPLE
            Remove-DbaRegisteredServer -SqlInstance sql2012 -Confirm:$false

            Removes all registered servers on sql2012 and turns off all prompting
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$Name,
        [string[]]$ServerName,
        [string[]]$Group,
        [parameter(ValueFromPipeline)]
        [Microsoft.SqlServer.Management.RegisteredServers.RegisteredServer[]]$InputObject,
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            $InputObject += Get-DbaRegisteredServer -SqlInstance $instance -SqlCredential $SqlCredential -Group $Group -ExcludeGroup $ExcludeGroup -Name $Name -ServerName $ServerName
        }

        foreach ($regserver in $InputObject) {
            $server = $regserver.Parent
            
            if ($Pscmdlet.ShouldProcess($regserver.Parent, "Removing $regserver")) {
                $null = $regserver.Drop()
                Disconnect-RegServer -Server $server
                
                try {
                    [pscustomobject]@{
                        ComputerName = $regserver.ComputerName
                        InstanceName = $regserver.InstanceName
                        SqlInstance  = $regserver.SqlInstance
                        Name         = $regserver.Name
                        ServerName   = $regserver.ServerName
                        Status       = "Dropped"
                    }
                }
                catch {
                    Stop-Function -Message "Failed to drop $regserver on $server" -ErrorRecord $_ -Continue
                }
            }
        }
    }
}
tools\dbatools\functions\Remove-DbaRegisteredServerGroup.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Remove-DbaRegisteredServerGroup {
    <#
        .SYNOPSIS
            Gets list of Server Groups objects stored in SQL Server Central Management Server (CMS).

        .DESCRIPTION
            Returns an array of Server Groups found in the CMS.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Name
            Specifies one or more groups to include from SQL Server Central Management Server.

        .PARAMETER InputObject
            Allows results from Get-DbaRegisteredServerGroup to be piped in

        .PARAMETER Id
            Get group by Id(s)

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.

            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.

            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Chrissy LeMaire (@cl)
            Tags: RegisteredServer, CMS

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Remove-DbaRegisteredServerGroup

        .EXAMPLE
            Remove-DbaRegisteredServerGroup -SqlInstance sql2012 -Group HR, Accounting

            Removes the HR and Accounting groups on sql2012

        .EXAMPLE
            Remove-DbaRegisteredServerGroup -SqlInstance sql2012 -Group HR\Development -Confirm:$false

            Removes the Development subgroup within the HR group on sql2012 and turns off all prompting

    #>
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Group")]
        [string[]]$Name,
        [parameter(ValueFromPipeline)]
        [Microsoft.SqlServer.Management.RegisteredServers.ServerGroup[]]$InputObject,
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            $InputObject += Get-DbaRegisteredServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Group $Name
        }

        foreach ($regservergroup in $InputObject) {
            $parentserver = Get-RegServerParent -InputObject $regservergroup

            if ($null -eq $parentserver) {
                Stop-Function -Message "Something went wrong and it's hard to explain, sorry. This basically shouldn't happen." -Continue
            }

            if ($Pscmdlet.ShouldProcess($parentserver.DomainInstanceName, "Removing $($regservergroup.Name) CMS Group")) {
                $null = $parentserver.ServerConnection.ExecuteNonQuery($regservergroup.ScriptDrop().GetScript())
                $parentserver.ServerConnection.Disconnect()
                try {
                    [pscustomobject]@{
                        ComputerName            = $parentserver.ComputerName
                        InstanceName            = $parentserver.InstanceName
                        SqlInstance             = $parentserver.SqlInstance
                        Name                    = $regservergroup.Name
                        Status                  = "Dropped"
                    }
                }
                catch {
                    Stop-Function -Message "Failed to drop $regservergroup on $parentserver" -ErrorRecord $_ -Continue
                }
            }
        }
    }
}
tools\dbatools\functions\Remove-DbaSpn.ps1
#ValidationTags#FlowControl,Pipeline#
function Remove-DbaSpn {
    <#
.SYNOPSIS
Removes an SPN for a given service account in active directory and also removes delegation to the same SPN, if found

.DESCRIPTION
This function will connect to Active Directory and search for an account. If the account is found, it will attempt to remove the specified SPN. Once the SPN is removed, the function will also remove delegation to that service.

In order to run this function, the credential you provide must have write access to Active Directory.

Note: This function supports -WhatIf

.PARAMETER SPN
The SPN you want to remove

.PARAMETER ServiceAccount
The account you want the SPN remove from

.PARAMETER Credential
The credential you want to use to connect to Active Directory to make the changes

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.PARAMETER Confirm
Turns confirmations before changes on or off

.PARAMETER WhatIf
Shows what would happen if the command was executed

.NOTES
Tags: SPN
Author: Drew Furgiuele (@pittfurg), http://www.port1433.com

dbatools PowerShell module (https://dbatools.io)
Copyright (C) 2016 Chrissy LeMaire
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Remove-DbaSpn

.EXAMPLE
Remove-DbaSpn -SPN MSSQLSvc\SQLSERVERA.domain.something -ServiceAccount domain\account

Connects to Active Directory and removes a provided SPN from the given account (and also the relative delegation)

.EXAMPLE
Remove-DbaSpn -SPN MSSQLSvc\SQLSERVERA.domain.something -ServiceAccount domain\account -EnableException

Connects to Active Directory and removes a provided SPN from the given account, suppressing all error messages and throw exceptions that can be caught instead

.EXAMPLE
Remove-DbaSpn -SPN MSSQLSvc\SQLSERVERA.domain.something -ServiceAccount domain\account -Credential (Get-Credential)

Connects to Active Directory and removes a provided SPN to the given account. Uses alternative account to connect to AD.

.EXAMPLE
Test-DbaSpn -ComputerName sql2005 | Where { $_.isSet -eq $true } | Remove-DbaSpn -WhatIf

Shows what would happen trying to remove all set SPNs for sql2005 and the relative delegations

.EXAMPLE
Test-DbaSpn -ComputerName sql2005 | Where { $_.isSet -eq $true } | Remove-DbaSpn

Removes all set SPNs for sql2005 and the relative delegations


#>
    [cmdletbinding(SupportsShouldProcess = $true, DefaultParameterSetName = "Default")]
    param (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName)]
        [Alias("RequiredSPN")]
        [string]$SPN,
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName)]
        [Alias("InstanceServiceAccount", "AccountName")]
        [string]$ServiceAccount,
        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName)]
        [PSCredential]$Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        Write-Message -Message "Looking for account $ServiceAccount..." -Level Verbose
        $searchfor = 'User'
        if ($ServiceAccount.EndsWith('$')) {
            $searchfor = 'Computer'
        }
        try {
            $Result = Get-DbaADObject -ADObject $ServiceAccount -Type $searchfor -Credential $Credential -EnableException
        }
        catch {
            Stop-Function -Message "AD lookup failure. This may be because the domain cannot be resolved for the SQL Server service account ($ServiceAccount). $($_.Exception.Message)" -EnableException $EnableException -InnerErrorRecord $_ -Target $ServiceAccount
        }
        if ($Result.Count -gt 0) {
            try {
                $adentry = $Result.GetUnderlyingObject()
            }
            catch {
                Stop-Function -Message "The SQL Service account ($ServiceAccount) has been found, but you don't have enough permission to inspect its properties $($_.Exception.Message)" -EnableException $EnableException -InnerErrorRecord $_ -Target $ServiceAccount
            }
        }
        else {
            Stop-Function -Message "The SQL Service account ($ServiceAccount) has not been found" -EnableException $EnableException -Target $ServiceAccount
        }

        # Cool! Remove an SPN
        $delegate = $true
        $spnadobject = $adentry.Properties['servicePrincipalName']

        if ($spnadobject -notcontains $spn) {
            Write-Message -Level Warning -Message "SPN $SPN not found"
            $status = "SPN not found"
            $set = $false
        }

        if ($PSCmdlet.ShouldProcess("$spn", "Removing SPN for service account")) {
            try {
                if ($spnadobject -contains $spn) {
                    $null = $spnadobject.Remove($spn)
                    $adentry.CommitChanges()
                    Write-Message -Message "Remove SPN $spn for $serviceaccount" -Level Verbose
                    $set = $false
                    $status = "Successfully removed SPN"
                }
            }
            catch {
                Write-Message -Message "Could not remove SPN. $($_.Exception.Message)" -Level Warning -EnableException $EnableException.ToBool() -ErrorRecord $_ -Target $ServiceAccountWrite
                $set = $true
                $status = "Failed to remove SPN"
                $delegate = $false
            }

            [pscustomobject]@{
                Name           = $spn
                ServiceAccount = $ServiceAccount
                Property       = "servicePrincipalName"
                IsSet          = $set
                Notes          = $status
            }
        }
        # if we removed the SPN, we should clean up also the delegation
        if ($PSCmdlet.ShouldProcess("$spn", "Removing delegation for service account for SPN")) {
            # if we didn't remove the SPN we shouldn't do anything
            if ($delegate) {
                # even if we removed the SPN, delegation could have been not set at all. We should not raise an error
                if ($adentry.Properties['msDS-AllowedToDelegateTo'] -notcontains $spn) {
                    [pscustomobject]@{
                        Name           = $spn
                        ServiceAccount = $ServiceAccount
                        Property       = "msDS-AllowedToDelegateTo"
                        IsSet          = $false
                        Notes          = "Delegation not found"
                    }
                }
                else {
                    # we indeed need the cleanup
                    try {
                        $null = $adentry.Properties['msDS-AllowedToDelegateTo'].Remove($spn)
                        $adentry.CommitChanges()
                        Write-Message -Message "Removed kerberos delegation $spn for $ServiceAccount" -Level Verbose
                        $set = $false
                        $status = "Successfully removed delegation"
                    }
                    catch {
                        Write-Message -Message "Could not remove delegation. $($_.Exception.Message)" -Level Warning -EnableException $EnableException.ToBool() -ErrorRecord $_ -Target $ServiceAccount
                        $set = $true
                        $status = "Failed to remove delegation"
                    }

                    [pscustomobject]@{
                        Name           = $spn
                        ServiceAccount = $ServiceAccount
                        Property       = "msDS-AllowedToDelegateTo"
                        IsSet          = $set
                        Notes          = $status
                    }
                }
            }

        }
    }
}
tools\dbatools\functions\Remove-DbaTrace.ps1
function Remove-DbaTrace {
     <#
        .SYNOPSIS
        Stops and closes the specified trace and deletes its definition from the server.

        .DESCRIPTION
        Stops and closes the specified trace and deletes its definition from the server.

        .PARAMETER SqlInstance
        The target SQL Server instance

        .PARAMETER SqlCredential
        Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Id
        A list of trace ids

        .PARAMETER InputObject
        Internal parameter for piping

        .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
        Tags: Security, Trace
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

       .EXAMPLE
        Remove-DbaTrace -SqlInstance sql2008

        Stops and removes all traces on sql2008

        .EXAMPLE
        Remove-DbaTrace -SqlInstance sql2008 -Id 1

        Stops and removes all trace with ID 1 on sql2008

        .EXAMPLE
        Get-DbaTrace -SqlInstance sql2008 | Out-GridView -PassThru | Remove-DbaTrace

        Stops and removes selected traces on sql2008

#>
    [CmdletBinding()]
    Param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [int[]]$Id,
        [parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [switch]$EnableException
    )
    process {
        if (-not $InputObject -and $SqlInstance) {
            $InputObject = Get-DbaTrace -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Id $Id
        }

        foreach ($trace in $InputObject) {
            if (-not $trace.id -and -not $trace.Parent) {
                Stop-Function -Message "Input is of the wrong type. Use Get-DbaTrace." -Continue
                return
            }

            $server = $trace.Parent
            $traceid = $trace.id
            $default = Get-DbaTrace -SqlInstance $server -Default

            if ($default.id -eq $traceid) {
                Stop-Function -Message "The default trace on $server cannot be stopped. Use Set-DbaSpConfigure to turn it off." -Continue
            }

            $stopsql = "sp_trace_setstatus $traceid, 0"
            $removesql = "sp_trace_setstatus $traceid, 2"

            try {
                $server.Query($stopsql)
                if (Get-DbaTrace -SqlInstance $server -Id $traceid) {
                    $server.Query($removesql)
                }
                [pscustomobject]@{
                    ComputerName      = $server.ComputerName
                    InstanceName      = $server.ServiceName
                    SqlInstance       = $server.DomainInstanceName
                    Id                = $traceid
                    Status            = "Stopped, closed and deleted"
                }
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_ -Target $server -Continue
                return
            }
        }
    }
}
tools\dbatools\functions\Remove-DbaXESession.ps1
function Remove-DbaXESession {
    <#
        .SYNOPSIS
            Removes Extended Events sessions.

        .DESCRIPTION
            This script removes Extended Events sessions on a SQL Server instance.

        .PARAMETER SqlInstance
            Target SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Session
            Specifies a list of Extended Events sessions to remove.

        .PARAMETER AllSessions
            If this switch is enabled, all Extended Events sessions will be removed except the packaged sessions AlwaysOn_health, system_health, telemetry_xevents.

        .PARAMETER InputObject
            Accepts a collection of XEsession objects as output by Get-DbaXESession.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Remove-DbaXESession

        .EXAMPLE
            Remove-DbaXESession -SqlInstance sql2012 -AllSessions

            Removes all Extended Event Session on the sqlserver2014 instance.

        .EXAMPLE
            Remove-DbaXESession -SqlInstance sql2012 -Session xesession1,xesession2

            Removes the xesession1 and xesession2 Extended Event sessions.

        .EXAMPLE
            Get-DbaXESession -SqlInstance sql2017 | Remove-DbaXESession -Confirm:$false

            Removes all sessions from sql2017, bypassing prompts.

        .EXAMPLE
            Get-DbaXESession -SqlInstance sql2012 -Session xesession1 | Remove-DbaXESession

            Removes the sessions returned from the Get-DbaXESession function.
    #>
    [CmdletBinding(DefaultParameterSetName = 'Session', SupportsShouldProcess, ConfirmImpact = 'High')]
    param (
        [parameter(Position = 1, Mandatory, ParameterSetName = 'Session')]
        [parameter(Position = 1, Mandatory, ParameterSetName = 'All')]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [parameter(ParameterSetName = 'Session')]
        [parameter(ParameterSetName = 'All')]
        [PSCredential]$SqlCredential,
        [parameter(Mandatory, ParameterSetName = 'Session')]
        [Alias("Sessions")]
        [object[]]$Session,
        [parameter(Mandatory, ParameterSetName = 'All')]
        [switch]$AllSessions,
        [parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Object')]
        [Microsoft.SqlServer.Management.XEvent.Session[]]$InputObject,
        [switch]$EnableException
    )

    begin {
        # Remove each XESession
        function Remove-XESessions {
            [CmdletBinding()]
            param ([Microsoft.SqlServer.Management.XEvent.Session[]]$xeSessions)

            foreach ($xe in $xeSessions) {
                $instance = $xe.Parent.Name
                $session = $xe.Name

                if ($Pscmdlet.ShouldProcess("$instance", "Removing XEvent Session $session")) {
                    try {
                        $xe.Drop()
                        [pscustomobject]@{
                            ComputerName = $xe.Parent.ComputerName
                            InstanceName = $xe.Parent.ServiceName
                            SqlInstance  = $xe.Parent.DomainInstanceName
                            Session      = $session
                            Status       = "Removed"
                        }
                    }
                    catch {
                        Stop-Function -Message "Could not remove XEvent Session on $instance" -Target $session -ErrorRecord $_ -Continue
                    }
                }
            }
        }
    }

    process {
        if ($InputObject) {
            # avoid the collection issue
            $sessions = Get-DbaXESession -SqlInstance $InputObject.Parent -Session $InputObject.Name
            foreach ($item in $sessions) {
                Remove-XESessions $item
            }
        }
        else {
            foreach ($instance in $SqlInstance) {
                $xeSessions = Get-DbaXESession -SqlInstance $instance -SqlCredential $SqlCredential

                # Filter xeSessions based on parameters
                if ($Session) {
                    $xeSessions = $xeSessions | Where-Object { $_.Name -in $Session }
                }
                elseif ($AllSessions) {
                    $systemSessions = @('AlwaysOn_health', 'system_health', 'telemetry_xevents')
                    $xeSessions = $xeSessions | Where-Object { $_.Name -notin $systemSessions }
                }

                Remove-XESessions $xeSessions
            }
        }
    }
}
tools\dbatools\functions\Remove-DbaXESmartTarget.ps1
function Remove-DbaXESmartTarget {
    <#
        .SYNOPSIS
           Removes an XESmartTarget PowerShell Job.

        .DESCRIPTION
           Removes an XESmartTarget PowerShell Job.

        .PARAMETER InputObject
           Specifies one or more XESmartTarget job objects as output by Get-DbaXESmartTarget.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
            SmartTarget: by Gianluca Sartori (@spaghettidba)

        .LINK
            https://dbatools.io/Remove-DbaXESmartTarget
            https://github.com/spaghettidba/XESmartTarget/wiki

        .EXAMPLE
            Get-DbaXESmartTarget | Remove-DbaXESmartTarget

            Removes all XESmartTarget jobs.

        .EXAMPLE
            Get-DbaXESmartTarget | Where-Object Id -eq 2 | Remove-DbaXESmartTarget

            Removes a specific XESmartTarget job.
    #>
    [CmdletBinding(SupportsShouldProcess)]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [object[]]$InputObject,
        [switch]$EnableException
    )
    process {
        if ($Pscmdlet.ShouldProcess("localhost", "Removing job $id")) {
            try {
                $id = $InputObject.Id
                Write-Message -Level Output -Message "Removing job $id, this may take a couple minutes."
                Get-Job -ID $InputObject.Id | Remove-Job -Force
                Write-Message -Level Output -Message "Successfully removed $id."
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_
            }
        }
    }
}
tools\dbatools\functions\Rename-DbaDatabase.ps1
function Rename-DbaDatabase {
    <#
        .SYNOPSIS
            Changes database name, logical file names, file group names and physical file names (optionally handling the move). BETA VERSION.

        .DESCRIPTION
            Can change every database metadata that can be renamed.
            The ultimate goal is choosing to have a default template to enforce in your environment
            so your naming convention for every bit can be put in place in no time.
            The process is as follows (it follows the hierarchy of the entities):
                - database name is changed (optionally, forcing users out)
                - filegroup name(s) are changed accordingly
                - logical name(s) are changed accordingly
                - physical file(s) are changed accordingly
                    - if Move is specified, the database will be taken offline and the move will initiate, then it will be taken online
                    - if Move is not specified, the database remains online (unless SetOffline), and you are in charge of moving files
            If any of the above fails, the process stops.
            Please take a backup of your databases BEFORE using this, and remember to backup AFTER (also a FULL backup of master)

            It returns an object for each database with all the renames done, plus hidden properties showing a "human" representation of them.

            It's better you store the resulting object in a variable so you can inspect it in case of issues, e.g. "$result = Rename-DbaDatabase ....."

            To get a grasp without worrying of what would happen under the hood, use "Rename-DbaDatabase .... -Preview | Select-Object *"

        .PARAMETER SqlInstance
            Target any number of instances, in order to return their build state.

        .PARAMETER SqlCredential
            When connecting to an instance, use the credentials specified.

        .PARAMETER Database
            Targets only specified databases

        .PARAMETER ExcludeDatabase
            Excludes only specified databases

        .PARAMETER AllDatabases
            If you want to apply the naming convention system wide, you need to pass this parameter

        .PARAMETER DatabaseName
            Pass a template to rename the database name. Valid placeholders are:
                - <DBN> current database name
                - <DATE> date (yyyyMMdd)

        .PARAMETER FileGroupName
            Pass a template to rename file group name. Valid placeholders are:
                - <FGN> current filegroup name
                - <DBN> current database name
                - <DATE> date (yyyyMMdd)
            If distinct names cannot be generated, a counter will be appended (0001, 0002, 0003, etc)

        .PARAMETER LogicalName
            Pass a template to rename logical name. Valid placeholders are:
                - <FT> file type (ROWS, LOG)
                - <LGN> current logical name
                - <FGN> current filegroup name
                - <DBN> current database name
                - <DATE> date (yyyyMMdd)
            If distinct names cannot be generated, a counter will be appended (0001, 0002, 0003, etc)

        .PARAMETER FileName
            Pass a template to rename file name. Valid placeholders are:
                - <FNN> current file name (the basename, without directory nor extension)
                - <FT> file type (ROWS, LOG, MMO, FS)
                - <LGN> current logical name
                - <FGN> current filegroup name
                - <DBN> current database name
                - <DATE> date (yyyyMMdd)
            If distinct names cannot be generated, a counter will be appended (0001, 0002, 0003, etc)

        .PARAMETER ReplaceBefore
            If you pass this switch, all upper level "current names" will be inspected and replaced BEFORE doing the
            rename according to the template in the current level (remember the hierarchy):
            Let's say you have a database named "dbatools_HR", composed by 3 files
                - dbatools_HR_Data.mdf
                - dbatools_HR_Index.ndf
                - dbatools_HR_log.ldf
            Rename-DbaDatabase .... -Database "dbatools_HR" -DatabaseName "dbatools_HRARCHIVE" -FileName '<DBN><FNN>'
            would end up with this logic:
            - database --> no placeholders specified
                - dbatools_HR to dbatools_HRARCHIVE
                    - filenames placeholders specified
                        <DBN><FNN> --> current database name + current filename"
                            - dbatools_HR_Data.mdf to dbatools_HRARCHIVEdbatools_HR_Data.mdf
                            - dbatools_HR_Index.mdf to dbatools_HRARCHIVEdbatools_HR_Data.mdf
                            - dbatools_HR_log.ldf to dbatools_HRARCHIVEdbatools_HR_log.ldf
            Passing this switch, instead, e.g.
            Rename-DbaDatabase .... -Database "dbatools_HR" -DatabaseName "dbatools_HRARCHIVE" -FileName '<DBN><FNN>' -ReplaceBefore
            end up with this logic instead:
            - database --> no placeholders specified
                - dbatools_HR to dbatools_HRARCHIVE
                    - filenames placeholders specified,
                        <DBN><FNN>, plus -ReplaceBefore --> current database name + replace OLD "upper level" names inside the current filename
                        - dbatools_HR_Data.mdf to dbatools_HRARCHIVE_Data.mdf
                        - dbatools_HR_Index.mdf to dbatools_HRARCHIVE_Data.mdf
                        - dbatools_HR_log.ldf to dbatools_HRARCHIVE_log.ldf

        .PARAMETER Force
            Kills any open session to be able to do renames.

        .PARAMETER SetOffline
            Kills any open session and sets the database offline to be able to move files

        .PARAMETER Move
            If you want this function to move files, else you're the one in charge of it.
            This enables the same functionality as SetOffline, killing open transactions and putting the database
            offline, then do the actual rename and setting it online again afterwards

        .PARAMETER Preview
            Shows the renames without performing any operation (recommended to find your way around this function parameters ;-) )

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER InputObject
            Accepts piped database objects

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database, Rename
            Author: niphlod

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Rename-DbaDatabase

        .EXAMPLE
            Rename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -DatabaseName HR2 | select *

            Shows the detailed resultset you'll get renaming the HR database to HR2 without doing anything

        .EXAMPLE
            Rename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -DatabaseName HR2

            Renames the HR database to HR2

        .EXAMPLE
            Get-DbaDatabase -SqlInstance sqlserver2014a -Database HR | Rename-DbaDatabase -DatabaseName HR2

            Same as before, but with a piped database (renames the HR database to HR2)

        .EXAMPLE
            Rename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -DatabaseName "dbatools_<DBN>"

            Renames the HR database to dbatools_HR

        .EXAMPLE
            Rename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -DatabaseName "dbatools_<DBN>_<DATE>"

            Renames the HR database to dbatools_HR_20170807 (if today is 07th Aug 2017)

        .EXAMPLE
            Rename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -FileGroupName "dbatools_<FGN>"

            Renames every FileGroup within HR to "dbatools_[the original FileGroup name]"

        .EXAMPLE
            Rename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -DatabaseName "dbatools_<DBN>" -FileGroupName "<DBN>_<FGN>"

            Renames the HR database to "dbatools_HR", then renames every FileGroup within to "dbatools_HR_[the original FileGroup name]"
            Note the "default recursive behaviour" here: for all intents and purposes the result of the former can be obtained with two distinct calls:
            Rename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -FileGroupName "dbatools_<DBN>_<FGN>"
            Rename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -DatabaseName "dbatools_<DBN>"

        .EXAMPLE
            Rename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -DatabaseName "dbatools_<DBN>" -FileName "<DBN>_<FGN>_<FNN>"

            Renames the HR database to "dbatools_HR" and then all filenames as "dbatools_HR_[Name of the FileGroup]_[original_filename]"
            The db stays online (watch out!). You can then proceed manually to move/copy files by hand, set the db offline and then online again to finish the rename process

        .EXAMPLE
            Rename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -DatabaseName "dbatools_<DBN>" -FileName "<DBN>_<FGN>_<FNN>" -SetOffline

            Renames the HR database to "dbatools_HR" and then all filenames as "dbatools_HR_[Name of the FileGroup]_[original_filename]"
            The db is then set offline (watch out!). You can then proceed manually to move/copy files by hand and then set it online again to finish the rename process

        .EXAMPLE
            Rename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -DatabaseName "dbatools_<DBN>" -FileName "<DBN>_<FGN>_<FNN>" -Move

            Renames the HR database to "dbatools_HR" and then all filenames as "dbatools_HR_[Name of the FileGroup]_[original_filename]"
            The db is then set offline (watch out!). The function tries to do a simple rename and then sets the db online again to finish the rename process
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    Param (
        [parameter(Mandatory, ParameterSetName = "Server")]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [parameter(ParameterSetName = "Server")]
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$AllDatabases,
        [string]$DatabaseName,
        [string]$FileGroupName,
        [string]$LogicalName,
        [string]$FileName,
        [switch]$ReplaceBefore,
        [switch]$Force,
        [switch]$Move,
        [switch]$SetOffline,
        [switch]$Preview,
        [parameter(Mandatory, ValueFromPipeline, ParameterSetName = "Pipe")]
        [Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $CurrentDate = Get-Date -Format 'yyyyMMdd'

        function Get-DbaNameStructure($database) {
            $obj = @()
            # db name
            $obj += "- Database : $database"
            # FileGroups
            foreach ($fg in $database.FileGroups) {
                $obj += "  - FileGroup: $($fg.Name)"
                # LogicalNames
                foreach ($ln in $fg.Files) {
                    $obj += "    - Logical: $($ln.Name)"
                    $obj += "      - FileName: $($ln.FileName)"
                }
            }
            $obj += "  - Logfiles"
            foreach ($log in $database.LogFiles) {
                $obj += "    - Logical: $($log.Name)"
                $obj += "      - FileName: $($log.FileName)"
            }
            return $obj -Join "`n"
        }


        function Get-DbaKeyByValue($hashtable, $Value) {
            ($hashtable.GetEnumerator() | Where-Object Value -eq $Value).Name
        }

        if ((Test-Bound -ParameterName SetOffline) -and (-not(Test-Bound -ParameterName FileName))) {
            Stop-Function -Category InvalidArgument -Message "-SetOffline is only useful when -FileName is passed. Quitting."
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }
        if (!$Database -and !$AllDatabases -and !$InputObject -and !$ExcludeDatabase) {
            Stop-Function -Message "You must specify a -AllDatabases or -Database/ExcludeDatabase to continue"
            return
        }
        if (!$DatabaseName -and !$FileGroupName -and !$LogicalName -and !$FileName) {
            Stop-Function -Message "You must specify at least one of -DatabaseName,-FileGroupName,-LogicalName or -Filename to continue"
            return
        }
        $dbs = @()
        if ($InputObject) {
            if ($InputObject.Name) {
                # comes from Get-DbaDatabase
                $dbs += $InputObject
            }
        }
        else {
            foreach ($instance in $SqlInstance) {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                try {
                    $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlCredential
                }
                catch {
                    Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                }
                $all_dbs = $server.Databases | Where-Object IsAccessible
                $dbs += $all_dbs | Where-Object { @('master', 'model', 'msdb', 'tempdb', 'distribution') -notcontains $_.Name }
                if ($Database) {
                    $dbs = $dbs | Where-Object { $Database -contains $_.Name }
                }
                if ($ExcludeDatabase) {
                    $dbs = $dbs | Where-Object { $ExcludeDatabase -notcontains $_.Name }
                }
            }
        }

        # holds all dbs per instance to avoid naming clashes
        $InstanceDbs = @{}

        # holds all db file enumerations (used for -Move only)
        $InstanceFiles = @{}

        #region db loop
        foreach ($db in $dbs) {
            # used to stop futher operations on database
            $failed = $false

            # pending renames initialized at db level
            $Pending_Renames = @()

            $Entities_Before = @{}

            $server = $db.Parent
            if ($db.Name -in @('master', 'model', 'msdb', 'tempdb', 'distribution')) {
                Write-Message -Level Warning -Message "Database $($db.Name) is a system one, skipping..."
                continue
            }
            if (!$db.IsAccessible) {
                Write-Message -Level Warning -Message "Database $($db.Name) is not accessible, skipping..."
                continue
            }
            if ($db.IsMirroringEnabled -eq $true -or $db.AvailabilityGroupName.Length -gt 0) {
                Write-Message -Level Warning -Message "Database $($db.Name) is either mirrored or in an AG, skipping..."
                continue
            }
            $Server_Id = $server.DomainInstanceName
            if ( !$InstanceDbs.ContainsKey($Server_Id) ) {
                $InstanceDbs[$Server_Id] = @{}
                foreach ($dn in $server.Databases.Name) {
                    $InstanceDbs[$Server_Id][$dn] = 1
                }
            }

            $Entities_Before['DBN'] = @{}
            $Entities_Before['FGN'] = @{}
            $Entities_Before['LGN'] = @{}
            $Entities_Before['FNN'] = @{}
            $Entities_Before['DBN'][$db.Name] = $db.Name
            #region databasename
            if ($DatabaseName) {
                $Orig_DBName = $db.Name
                # fixed replacements
                $NewDBName = $DatabaseName.Replace('<DBN>', $Orig_DBName).Replace('<DATE>', $CurrentDate)
                if ($Orig_DBName -eq $NewDBName) {
                    Write-Message -Level VeryVerbose -Message "Database name unchanged, skipping"
                }
                else {
                    if ($InstanceDbs[$Server_Id].ContainsKey($NewDBName)) {
                        Write-Message -Level Warning -Message "Database $NewDBName exists already, skipping this rename"
                        $failed = $true
                    }
                    else {
                        if ($PSCmdlet.ShouldProcess($db, "Renaming Database $db to $NewDBName")) {
                            if ($Force) {
                                $server.KillAllProcesses($Orig_DBName)
                            }
                            try {
                                if (!$Preview) {
                                    $db.Rename($NewDBName)
                                }
                                $InstanceDbs[$Server_Id].Remove($Orig_DBName)
                                $InstanceDbs[$Server_Id][$NewDBName] = 1
                                $Entities_Before['DBN'][$Orig_DBName] = $NewDBName
                                #$db.Refresh()
                            }
                            catch {
                                Stop-Function -Message "Failed to rename Database : $($_.Exception.InnerException.InnerException.InnerException)" -ErrorRecord $_ -Target $server.DomainInstanceName -OverrideExceptionMessage
                                # stop any further renames
                                $failed = $true
                            }
                        }
                    }
                }
            }
            #endregion databasename
            #region filegroupname
            if ($ReplaceBefore) {
                #backfill PRIMARY
                $Entities_Before['FGN']['PRIMARY'] = 'PRIMARY'
                foreach ($fg in $db.FileGroups.Name) {
                    $Entities_Before['FGN'][$fg] = $fg
                }
            }

            if (!$failed -and $FileGroupName) {
                $Editable_FGs = $db.FileGroups | Where-Object Name -ne 'PRIMARY'
                $New_FGNames = @{}
                foreach ($fg in $db.FileGroups.Name) {
                    $New_FGNames[$fg] = 1
                }
                $FGCounter = 0
                foreach ($fg in $Editable_FGs) {
                    $Orig_FGName = $fg.Name
                    $Orig_Placeholder = $Orig_FGName
                    if ($ReplaceBefore) {
                        # at Filegroup level, we need to worry about database name
                        $Orig_Placeholder = $Orig_Placeholder.Replace($Entities_Before['DBN'][$Orig_DBName], '')
                    }
                    $NewFGName = $FileGroupName.Replace('<DBN>', $Entities_Before['DBN'][$db.Name]).Replace('<DATE>', $CurrentDate).Replace('<FGN>', $Orig_Placeholder)
                    $FinalFGName = $NewFGName
                    while ($fg.Name -ne $FinalFGName) {
                        if ($FinalFGName -in $New_FGNames.Keys) {
                            $FGCounter += 1
                            $FinalFGName = "$NewFGName$($FGCounter.ToString('000'))"
                        }
                        else {
                            break
                        }
                    }
                    if ($fg.Name -eq $FinalFGName) {
                        Write-Message -Level VeryVerbose -Message "No rename necessary for FileGroup $($fg.Name) (on $db)"
                        continue
                    }
                    if ($PSCmdlet.ShouldProcess($db, "Renaming FileGroup $($fg.Name) to $FinalFGName")) {
                        try {
                            if (!$Preview) {
                                $fg.Rename($FinalFGName)
                            }
                            $New_FGNames.Remove($Orig_FGName)
                            $New_FGNames[$FinalFGName] = 1
                            $Entities_Before['FGN'][$Orig_FGName] = $FinalFGName
                        }
                        catch {
                            Stop-Function -Message "Failed to rename FileGroup : $($_.Exception.InnerException.InnerException.InnerException)" -ErrorRecord $_ -Target $server.DomainInstanceName -OverrideExceptionMessage
                            # stop any further renames
                            $failed = $true
                            break
                        }
                    }
                }
                #$db.FileGroups.Refresh()
            }

            #endregion filegroupname
            #region logicalname
            if ($ReplaceBefore) {
                foreach ($fn in $db.FileGroups.Files.Name) {
                    $Entities_Before['LGN'][$fn] = $fn
                }
                foreach ($fn in $db.Logfiles.Name) {
                    $Entities_Before['LGN'][$fn] = $fn
                }
            }
            if (!$failed -and $LogicalName) {
                $New_LogicalNames = @{}
                foreach ($fn in $db.FileGroups.Files.Name) {
                    $New_LogicalNames[$fn] = 1
                }
                foreach ($fn in $db.Logfiles.Name) {
                    $New_LogicalNames[$fn] = 1
                }
                $LNCounter = 0
                foreach ($fg in $db.FileGroups) {
                    $logicalfiles = @($fg.Files)
                    for ($i = 0; $i -lt $logicalfiles.Count; $i++) {
                        $logical = $logicalfiles[$i]
                        $FileType = switch ($fg.FileGroupType) {
                            'RowsFileGroup' { 'ROWS' }
                            'MemoryOptimizedDataFileGroup' { 'MMO' }
                            'FileStreamDataFileGroup' { 'FS' }
                            default { 'STD' }
                        }
                        $Orig_LGName = $logical.Name
                        $Orig_Placeholder = $Orig_LGName
                        if ($ReplaceBefore) {
                            # at Logical Name level, we need to worry about database name and filegroup name
                            $Orig_Placeholder = $Orig_Placeholder.Replace((Get-DbaKeyByValue -HashTable $Entities_Before['DBN'] -Value $db.Name), '').Replace(
                                (Get-DbaKeyByValue -HashTable $Entities_Before['FGN'] -Value $fg.Name), '')
                        }
                        $NewLGName = $LogicalName.Replace('<DBN>', $db.Name).Replace('<DATE>', $CurrentDate).Replace('<FGN>', $fg.Name).Replace(
                            '<FT>', $FileType).Replace('<LGN>', $Orig_Placeholder)
                        $FinalLGName = $NewLGName
                        while ($logical.Name -ne $FinalLGName) {
                            if ($FinalLGName -in $New_LogicalNames.Keys) {
                                $LNCounter += 1
                                $FinalLGName = "$NewLGName$($LNCounter.ToString('000'))"
                            }
                            else {
                                break
                            }
                        }
                        if ($logical.Name -eq $FinalLGName) {
                            Write-Message -Level VeryVerbose -Message "No rename necessary for LogicalFile $($logical.Name) (on FileGroup $($fg.Name) (on $db))"
                            continue
                        }
                        if ($PSCmdlet.ShouldProcess($db, "Renaming LogicalFile $($logical.Name) to $FinalLGName (on FileGroup $($fg.Name))")) {
                            try {
                                if (!$Preview) {
                                    $logical.Rename($FinalLGName)
                                }
                                $New_LogicalNames.Remove($Orig_LGName)
                                $New_LogicalNames[$FinalLGName] = 1
                                $Entities_Before['LGN'][$Orig_LGName] = $FinalLGName
                            }
                            catch {
                                Stop-Function -Message "Failed to Rename Logical File : $($_.Exception.InnerException.InnerException.InnerException)" -ErrorRecord $_ -Target $server.DomainInstanceName -OverrideExceptionMessage
                                # stop any further renames
                                $failed = $true
                                break
                            }
                        }
                    }
                }
                #$fg.Files.Refresh()
                if (!$failed) {
                    $logfiles = @($db.LogFiles)
                    for ($i = 0; $i -lt $logfiles.Count; $i++) {
                        $logicallog = $logfiles[$i]
                        $Orig_LGName = $logicallog.Name
                        $Orig_Placeholder = $Orig_LGName
                        if ($ReplaceBefore) {
                            # at Logical Name level, we need to worry about database name and filegroup name, but for logfiles filegroup is not there
                            $Orig_Placeholder = $Orig_Placeholder.Replace((Get-DbaKeyByValue -HashTable $Entities_Before['DBN'] -Value $db.Name), '').Replace(
                                (Get-DbaKeyByValue -HashTable $Entities_Before['FGN'] -Value $fg.Name), '')
                        }
                        $NewLGName = $LogicalName.Replace('<DBN>', $db.Name).Replace('<DATE>', $CurrentDate).Replace('<FGN>', '').Replace(
                            '<FT>', 'LOG').Replace('<LGN>', $Orig_Placeholder)
                        $FinalLGName = $NewLGName
                        if ($FinalLGName.Length -eq 0) {
                            #someone passed in -LogicalName '<FGN>'.... but we don't have FGN here
                            $FinalLGName = $Orig_LGName
                        }
                        while ($logicallog.Name -ne $FinalLGName) {
                            if ($FinalLGName -in $New_LogicalNames.Keys) {
                                $LNCounter += 1
                                $FinalLGName = "$NewLGName$($LNCounter.ToString('000'))"
                            }
                            else {
                                break
                            }
                        }
                        if ($logicallog.Name -eq $FinalLGName) {
                            Write-Message -Level VeryVerbose -Message "No Rename necessary for LogicalFile log $($logicallog.Name) (LOG on (on $db))"
                            continue
                        }
                        if ($PSCmdlet.ShouldProcess($db, "Renaming LogicalFile log $($logicallog.Name) to $FinalLGName (LOG)")) {
                            try {
                                if (!$Preview) {
                                    $logicallog.Rename($FinalLGName)
                                }
                                $New_LogicalNames.Remove($Orig_LGName)
                                $New_LogicalNames[$FinalLGName] = 1
                                $Entities_Before['LGN'][$Orig_LGName] = $FinalLGName
                            }
                            catch {
                                Stop-Function -Message "Failed to Rename Logical File : $($_.Exception.InnerException.InnerException.InnerException)" -ErrorRecord $_ -Target $server.DomainInstanceName -OverrideExceptionMessage
                                # stop any further renames
                                $failed = $true
                                break
                            }
                        }
                    }
                    #$db.Logfiles.Refresh()
                }
            }
            #endregion logicalname
            #region filename
            if ($ReplaceBefore) {
                foreach ($fn in $db.FileGroups.Files.FileName) {
                    $Entities_Before['FNN'][$fn] = $fn
                }
                foreach ($fn in $db.Logfiles.FileName) {
                    $Entities_Before['FNN'][$fn] = $fn
                }
            }
            if (!$failed -and $FileName) {

                $New_FileNames = @{}
                foreach ($fn in $db.FileGroups.Files.FileName) {
                    $New_FileNames[$fn] = 1
                }
                foreach ($fn in $db.Logfiles.FileName) {
                    $New_FileNames[$fn] = 1
                }
                # we need to inspect what files are in the same directory
                # to avoid failing the process because the move won't work
                # here we have a dict keyed by instance and then keyed by path
                if ( !$InstanceFiles.ContainsKey($Server_Id) ) {
                    $InstanceFiles[$Server_Id] = @{}
                }
                foreach ($fn in $New_FileNames.Keys) {
                    $dirname = [IO.Path]::GetDirectoryName($fn)
                    if ( !$InstanceFiles[$Server_Id].ContainsKey($dirname) ) {
                        $InstanceFiles[$Server_Id][$dirname] = @{}
                        try {
                            $dirfiles = Get-DbaFile -SqlInstance $server -Path $dirname -EnableException
                        }
                        catch {
                            Write-Message -Level Warning -Message "Failed to enumerate existing files at $dirname, move could go wrong"
                        }
                        foreach ($f in $dirfiles) {
                            $InstanceFiles[$Server_Id][$dirname][$f.Filename] = 1
                        }
                    }
                }
                $FNCounter = 0
                foreach ($fg in $db.FileGroups) {
                    $FG_Files = @($fg.Files)
                    foreach ($logical in $FG_Files) {
                        $FileType = switch ($fg.FileGroupType) {
                            'RowsFileGroup' { 'ROWS' }
                            'MemoryOptimizedDataFileGroup' { 'MMO' }
                            'FileStreamDataFileGroup' { 'FS' }
                            default { 'STD' }
                        }
                        $FNName = $logical.FileName
                        $FNNameDir = [IO.Path]::GetDirectoryName($FNName)
                        $Orig_FNNameLeaf = [IO.Path]::GetFileNameWithoutExtension($logical.FileName)
                        $Orig_Placeholder = $Orig_FNNameLeaf
                        if ($ReplaceBefore) {
                            # at Filename level, we need to worry about database name, filegroup name and logical file name
                            $Orig_Placeholder = $Orig_Placeholder.Replace((Get-DbaKeyByValue -HashTable $Entities_Before['DBN'] -Value $db.Name), '').Replace(
                                (Get-DbaKeyByValue -HashTable $Entities_Before['FGN'] -Value $fg.Name), '').Replace(
                                (Get-DbaKeyByValue -HashTable $Entities_Before['LGN'] -Value $logical.Name), '')
                        }
                        $NewFNName = $FileName.Replace('<DBN>', $db.Name).Replace('<DATE>', $CurrentDate).Replace('<FGN>', $fg.Name).Replace(
                            '<FT>', $FileType).Replace('<LGN>', $logical.Name).Replace('<FNN>', $Orig_Placeholder)
                        $FinalFNName = [IO.Path]::Combine($FNNameDir, "$NewFNName$([IO.Path]::GetExtension($FNName))")

                        while ($logical.FileName -ne $FinalFNName) {
                            if ($InstanceFiles[$Server_Id][$FNNameDir].ContainsKey($FinalFNName)) {
                                $FNCounter += 1
                                $FinalFNName = [IO.Path]::Combine($FNNameDir, "$NewFNName$($FNCounter.ToString('000'))$([IO.Path]::GetExtension($FNName))"
                                )
                            }
                            else {
                                break
                            }
                        }
                        if ($logical.FileName -eq $FinalFNName) {
                            Write-Message -Level VeryVerbose -Message "No rename necessary (on FileGroup $($fg.Name) (on $db))"
                            continue
                        }
                        if ($PSCmdlet.ShouldProcess($db, "Renaming FileName $($logical.FileName) to $FinalFNName (on FileGroup $($fg.Name))")) {
                            try {
                                if (!$Preview) {
                                    $logical.FileName = $FinalFNName
                                    $db.Alter()
                                }
                                $InstanceFiles[$Server_Id][$FNNameDir].Remove($FNName)
                                $InstanceFiles[$Server_Id][$FNNameDir][$FinalFNName] = 1
                                $Entities_Before['FNN'][$FNName] = $FinalFNName
                                $Pending_Renames += [pscustomobject]@{
                                    Source      = $FNName
                                    Destination = $FinalFNName
                                }
                            }
                            catch {
                                Stop-Function -Message "Failed to Rename FileName : $($_.Exception.InnerException.InnerException.InnerException)" -ErrorRecord $_ -Target $server.DomainInstanceName -OverrideExceptionMessage
                                # stop any further renames
                                $failed = $true
                                break
                            }
                        }
                    }
                    if (!$failed) {
                        $FG_Files = @($db.Logfiles)
                        foreach ($logical in $FG_Files) {
                            $FNName = $logical.FileName
                            $FNNameDir = [IO.Path]::GetDirectoryName($FNName)
                            $Orig_FNNameLeaf = [IO.Path]::GetFileNameWithoutExtension($logical.FileName)
                            $Orig_Placeholder = $Orig_FNNameLeaf
                            if ($ReplaceBefore) {
                                # at Filename level, we need to worry about database name, filegroup name and logical file name
                                $Orig_Placeholder = $Orig_Placeholder.Replace((Get-DbaKeyByValue -HashTable $Entities_Before['DBN'] -Value $db.Name), '').Replace(
                                    (Get-DbaKeyByValue -HashTable $Entities_Before['FGN'] -Value $fg.Name), '').Replace(
                                    (Get-DbaKeyByValue -HashTable $Entities_Before['LGN'] -Value $logical.Name), '')
                            }
                            $NewFNName = $FileName.Replace('<DBN>', $db.Name).Replace('<DATE>', $CurrentDate).Replace('<FGN>', '').Replace(
                                '<FT>', 'LOG').Replace('<LGN>', $logical.Name).Replace('<FNN>', $Orig_Placeholder)
                            $FinalFNName = [IO.Path]::Combine($FNNameDir, "$NewFNName$([IO.Path]::GetExtension($FNName))")
                            while ($logical.FileName -ne $FinalFNName) {
                                if ($InstanceFiles[$Server_Id][$FNNameDir].ContainsKey($FinalFNName)) {
                                    $FNCounter += 1
                                    $FinalFNName = [IO.Path]::Combine($FNNameDir, "$NewFNName$($FNCounter.ToString('000'))$([IO.Path]::GetExtension($FNName))")
                                }
                                else {
                                    break
                                }
                            }
                            if ($logical.FileName -eq $FinalFNName) {
                                Write-Message -Level VeryVerbose -Message "No rename necessary for $($logical.FileName) (LOG on (on $db))"
                                continue
                            }

                            if ($PSCmdlet.ShouldProcess($db, "Renaming FileName $($logical.FileName) to $FinalFNName (LOG)")) {
                                try {
                                    if (!$Preview) {
                                        $logical.FileName = $FinalFNName
                                        $db.Alter()
                                    }
                                    $InstanceFiles[$Server_Id][$FNNameDir].Remove($FNName)
                                    $InstanceFiles[$Server_Id][$FNNameDir][$FinalFNName] = 1
                                    $Entities_Before['FNN'][$FNName] = $FinalFNName
                                    $Pending_Renames += [pscustomobject]@{
                                        Source      = $FNName
                                        Destination = $FinalFNName
                                    }
                                }
                                catch {
                                    Stop-Function -Message "Failed to Rename FileName : $($_.Exception.InnerException.InnerException.InnerException)" -ErrorRecord $_ -Target $server.DomainInstanceName -OverrideExceptionMessage
                                    # stop any further renames
                                    $failed = $true
                                    break
                                }
                            }
                        }
                    }

                }
                #endregion filename
                #region move
                $ComputerName = $null
                $Final_Renames = New-Object System.Collections.ArrayList
                if ([DbaValidate]::IsLocalhost($server.ComputerName)) {
                    # locally ran so we can just use rename-item
                    $ComputerName = $server.ComputerName
                }
                else {
                    # let's start checking if we can access .ComputerName
                    $testPS = $false
                    if ($SqlCredential) {
                        # why does Test-PSRemoting require a Credential param ? this is ugly...
                        $testPS = Test-PSRemoting -ComputerName $server.ComputerName -Credential $SqlCredential -ErrorAction Stop
                    }
                    else {
                        $testPS = Test-PSRemoting -ComputerName $server.ComputerName -ErrorAction Stop
                    }
                    if (!($testPS)) {
                        # let's try to resolve it to a more qualified name, without "cutting" knowledge about the domain (only $server.Name possibly holds the complete info)
                        $Resolved = (Resolve-DbaNetworkName -ComputerName $server.Name).FullComputerName
                        if ($SqlCredential) {
                            $testPS = Test-PSRemoting -ComputerName $Resolved -Credential $SqlCredential -ErrorAction Stop
                        }
                        else {
                            $testPS = Test-PSRemoting -ComputerName $Resolved -ErrorAction Stop
                        }
                        if ($testPS) {
                            $ComputerName = $Resolved
                        }
                    }
                    else {
                        $ComputerName = $server.ComputerName
                    }
                }
                foreach ($op in $pending_renames) {
                    if ([DbaValidate]::IsLocalhost($server.ComputerName)) {
                        $null = $Final_Renames.Add([pscustomobject]@{
                                Source       = $op.Source
                                Destination  = $op.Destination
                                ComputerName = $ComputerName
                            })
                    }
                    else {
                        if ($null -eq $ComputerName) {
                            # if we don't have remote access ($ComputerName is null) we can fallback to admin shares if they're available
                            if (Test-Path (Join-AdminUnc -ServerName $server.ComputerName -filepath $op.Source)) {
                                $null = $Final_Renames.Add([pscustomobject]@{
                                        Source       = Join-AdminUnc -ServerName $server.ComputerName -filepath $op.Source
                                        Destination  = Join-AdminUnc -ServerName $server.ComputerName -filepath $op.Destination
                                        ComputerName = $server.ComputerName
                                    })
                            }
                            else {
                                # flag the impossible rename ($ComputerName is $null)
                                $null = $Final_Renames.Add([pscustomobject]@{
                                        Source       = $op.Source
                                        Destination  = $op.Destination
                                        ComputerName = $ComputerName
                                    })
                            }
                        }
                        else {
                            # we can do renames in a remote pssession
                            $null = $Final_Renames.Add([pscustomobject]@{
                                    Source       = $op.Source
                                    Destination  = $op.Destination
                                    ComputerName = $ComputerName
                                })
                        }
                    }
                }
                $Status = 'FULL'
                if (!$failed -and ($SetOffline -or $Move) -and $Final_Renames) {
                    if (!$Move) {
                        Write-Message -Level VeryVerbose -Message "Setting the database offline. You are in charge of moving the files to the new location"
                        # because renames still need to be dealt with
                        $Status = 'PARTIAL'
                    }
                    else {
                        if ($PSCmdlet.ShouldProcess($db, "File Rename required, setting db offline")) {
                            $SetState = Set-DbaDatabaseState -SqlInstance $server -Database $db.Name -Offline -Force
                            if ($SetState.Status -ne 'OFFLINE') {
                                Write-Message -Level Warning -Message "Setting db offline failed, You are in charge of moving the files to the new location"
                                # because it was impossible to set the database offline
                                $Status = 'PARTIAL'
                            }
                            else {
                                try {
                                    while ($Final_Renames.Count -gt 0) {
                                        $op = $Final_Renames.Item(0)
                                        if ($null -eq $op.ComputerName) {
                                            Stop-Function -Message "No access to physical files for renames"
                                        }
                                        else {
                                            Write-Message -Level VeryVerbose -Message "Moving file $($op.Source) to $($op.Destination)"
                                            if (!$Preview) {
                                                $scriptblock = {
                                                    $op = $args[0]
                                                    Rename-Item -Path $op.Source -NewName $op.Destination
                                                }
                                                Invoke-Command2 -ComputerName $op.ComputerName -Credential $sqlCredential -ScriptBlock $scriptblock -ArgumentList $op
                                            }
                                        }
                                        $null = $Final_Renames.RemoveAt(0)
                                    }
                                }
                                catch {
                                    $failed = $true
                                    # because a rename operation failed
                                    $Status = 'PARTIAL'
                                    Stop-Function -Message "Failed to rename $($op.Source) to $($op.Destination), you are in charge of moving the files to the new location" -ErrorRecord $_ -Target $instance -Exception $_.Exception -Continue
                                }
                                if (!$failed) {
                                    if ($PSCmdlet.ShouldProcess($db, "Setting database online")) {
                                        $SetState = Set-DbaDatabaseState -SqlInstance $server -Database $db.Name -Online -Force
                                        if ($SetState.Status -ne 'ONLINE') {
                                            Write-Message -Level Warning -Message "Setting db online failed"
                                            # because renames were done, but the database didn't wake up
                                            $Status = 'PARTIAL'
                                        }
                                        else {
                                            $Status = 'FULL'
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                else {
                    # because of a previous error with renames to do
                    $Status = 'PARTIAL'
                }
            }
            else {
                if (!$failed) {
                    # because no previous error and not filename
                    $Status = 'FULL'
                }
                else {
                    # because previous errors and not filename
                    $Status = 'PARTIAL'
                }
            }
            #endregion move
            # remove entities that match for the output
            foreach ($k in $Entities_Before.Keys) {
                $ToRemove = $Entities_Before[$k].GetEnumerator() | Where-Object { $_.Name -eq $_.Value } | Select-Object -ExpandProperty Name
                foreach ($el in $ToRemove) {
                    $Entities_Before[$k].Remove($el)
                }
            }
            [pscustomobject]@{
                ComputerName       = $server.ComputerName
                InstanceName       = $server.ServiceName
                SqlInstance        = $server.DomainInstanceName
                Database           = $db
                DBN                = $Entities_Before['DBN']
                DatabaseRenames    = ($Entities_Before['DBN'].GetEnumerator() | Foreach-Object { "$($_.Name) --> $($_.Value)" }) -Join "`n"
                FGN                = $Entities_Before['FGN']
                FileGroupsRenames  = ($Entities_Before['FGN'].GetEnumerator() | Foreach-Object { "$($_.Name) --> $($_.Value)" }) -Join "`n"
                LGN                = $Entities_Before['LGN']
                LogicalNameRenames = ($Entities_Before['LGN'].GetEnumerator() | Foreach-Object { "$($_.Name) --> $($_.Value)"  }) -Join "`n"
                FNN                = $Entities_Before['FNN']
                FileNameRenames    = ($Entities_Before['FNN'].GetEnumerator() | Foreach-Object { "$($_.Name) --> $($_.Value)"  }) -Join "`n"
                PendingRenames     = $Final_Renames
                Status             = $Status
            } | Select-DefaultView -ExcludeProperty DatabaseRenames, FileGroupsRenames, LogicalNameRenames, FileNameRenames
        }
        #endregion db loop
    }
}
tools\dbatools\functions\Rename-DbaLogin.ps1
function Rename-DbaLogin {
    <#
.SYNOPSIS
Rename-DbaLogin will rename login and database mapping for a specified login.

.DESCRIPTION
There are times where you might want to rename a login that was copied down, or if the name is not descriptive for what it does.

It can be a pain to update all of the mappings for a specific user, this does it for you.

.PARAMETER SqlInstance
Source SQL Server.You must have sysadmin access and server version must be SQL Server version 2000 or greater.

.PARAMETER Destination
Destination Sql Server. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Login
The current Login on the server - this list is auto-populated from the server.

.PARAMETER NewLogin
The new Login that you wish to use. If it is a windows user login, then the SID must match.

.PARAMETER Confirm
Prompts to confirm actions

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.


.NOTES
Tags: Login
Author: Mitchell Hamann (@SirCaptainMitch)

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Rename-DbaLogin

.EXAMPLE
Rename-DbaLogin -SqlInstance localhost -Login DbaToolsUser -NewLogin captain

SQL Login Example

.EXAMPLE
Rename-DbaLogin -SqlInstance localhost -Login domain\oldname -NewLogin domain\newname

Change the windowsuser login name.

.EXAMPLE
Rename-DbaLogin -SqlInstance localhost -Login dbatoolsuser -NewLogin captain -WhatIf

WhatIf Example
#>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory = $true)]
        [string]$Login,
        [parameter(Mandatory = $true)]
        [string]$NewLogin,
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $Databases = $server.Databases | Where-Object IsAccessible
            $currentLogin = $server.Logins[$Login]

            if ($Pscmdlet.ShouldProcess($SqlInstance, "Changing Login name from  [$Login] to [$NewLogin]")) {
                try {
                    $dbenums = $currentLogin.EnumDatabaseMappings()
                    $currentLogin.rename($NewLogin)
                    [pscustomobject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Database     = $null
                        OldLogin     = $Login
                        NewLogin     = $NewLogin
                        Status       = "Successful"
                    }
                }
                catch {
                    $dbenums = $null
                    [pscustomobject]@{
                        ComputerName = $server.ComputerName
                        InstanceName = $server.ServiceName
                        SqlInstance  = $server.DomainInstanceName
                        Database     = $null
                        OldLogin     = $Login
                        NewLogin     = $NewLogin
                        Status       = "Failure"
                    }
                    Stop-Function -Message "Failure" -ErrorRecord $_ -Target $login
                }
            }

            foreach ($db in $dbenums) {
                $db = $databases[$db.DBName]
                $user = $db.Users[$Login]
                Write-Message -Level Verbose -Message "Starting update for $db"

                if ($Pscmdlet.ShouldProcess($SqlInstance, "Changing database $db user $user from [$Login] to [$NewLogin]")) {
                    try {
                        $oldname = $user.name
                        $user.Rename($NewLogin)
                        [pscustomobject]@{
                            ComputerName = $server.ComputerName
                            InstanceName = $server.ServiceName
                            SqlInstance  = $server.DomainInstanceName
                            Database     = $db.name
                            OldUser      = $oldname
                            NewUser      = $NewLogin
                            Status       = "Successful"
                        }

                    }
                    catch {
                        Write-Message -Level Warning -Message "Rolling back update to login: $Login"
                        $currentLogin.rename($Login)

                        [pscustomobject]@{
                            ComputerName = $server.ComputerName
                            InstanceName = $server.ServiceName
                            SqlInstance  = $server.DomainInstanceName
                            Database     = $db.name
                            OldUser      = $NewLogin
                            NewUser      = $oldname
                            Status       = "Failure to rename. Rolled back change."
                        }
                        Stop-Function -Message "Failure" -ErrorRecord $_ -Target $NewLogin
                    }
                }
            }
        }
    }
}
tools\dbatools\functions\Repair-DbaOrphanUser.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Repair-DbaOrphanUser {
    <#
        .SYNOPSIS
            Finds orphan users with existing login and remaps them.

        .DESCRIPTION
            An orphan user is defined by a user that does not have a matching login (Login property = "").

            If the matching login exists it must be:
                Enabled
                Not a system object
                Not locked
                Have the same name that user

            You can drop users that does not have their matching login by specifying the parameter -RemoveNotExisting.

        .PARAMETER SqlInstance
            The SQL Server Instance to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies the database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies the database(s) to exclude from processing. Options for this list are auto-populated from the server

        .PARAMETER Users
            Specifies the list of usernames to repair.

        .PARAMETER Force
        Forces alter schema to dbo owner so users can be dropped.

        .PARAMETER RemoveNotExisting
            If this switch is enabled, all users that do not have a matching login will be dropped from the database.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .EXAMPLE
            Repair-DbaOrphanUser -SqlInstance sql2005

            Finds and repairs all orphan users of all databases present on server 'sql2005'

        .EXAMPLE
            Repair-DbaOrphanUser -SqlInstance sqlserver2014a -SqlCredential $cred

            Finds and repair all orphan users in all databases present on server 'sqlserver2014a'. SQL credentials are used to authenticate to the server.

        .EXAMPLE
            Repair-DbaOrphanUser -SqlInstance sqlserver2014a -Database db1, db2

            Finds and repairs all orphan users in both db1 and db2 databases.

        .EXAMPLE
            Repair-DbaOrphanUser -SqlInstance sqlserver2014a -Database db1 -Users OrphanUser

            Finds and repairs user 'OrphanUser' in 'db1' database.

        .EXAMPLE
            Repair-DbaOrphanUser -SqlInstance sqlserver2014a -Users OrphanUser

            Finds and repairs user 'OrphanUser' on all databases

        .EXAMPLE
            Repair-DbaOrphanUser -SqlInstance sqlserver2014a -RemoveNotExisting

            Finds all orphan users of all databases present on server 'sqlserver2014a'. Removes all users that do not have  matching Logins.

        .NOTES
            Tags: Orphan
            Author: Claudio Silva (@ClaudioESSilva)
            Editor: Simone Bizzotto (@niphlod)
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Repair-DbaOrphanUser
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [object[]]$Users,
        [switch]$RemoveNotExisting,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {

        foreach ($instance in $SqlInstance) {

            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Write-Message -Level Warning -Message "Failed to connect to: $SqlInstance."
                continue
            }

            $DatabaseCollection = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                $DatabaseCollection = $DatabaseCollection | Where-Object Name -In $Database
            }
            if ($ExcludeDatabase) {
                $DatabaseCollection = $DatabaseCollection | Where-Object Name -NotIn $ExcludeDatabase
            }

            if ($DatabaseCollection.Count -gt 0) {
                foreach ($db in $DatabaseCollection) {
                    try {
                        #if SQL 2012 or higher only validate databases with ContainmentType = NONE
                        if ($server.versionMajor -gt 10) {
                            if ($db.ContainmentType -ne [Microsoft.SqlServer.Management.Smo.ContainmentType]::None) {
                                Write-Message -Level Warning -Message "Database '$db' is a contained database. Contained databases can't have orphaned users. Skipping validation."
                                Continue
                            }
                        }

                        Write-Message -Level Verbose -Message "Validating users on database '$db'."

                        if ($Users.Count -eq 0) {
                            #the third validation will remove from list sql users without login. The rule here is Sid with length higher than 16
                            $UsersToWork = $db.Users | Where-Object { $_.Login -eq "" -and ($_.ID -gt 4) -and ($_.Sid.Length -gt 16 -and $_.LoginType -eq [Microsoft.SqlServer.Management.Smo.LoginType]::SqlLogin) -eq $false }
                        }
                        else {

                            #the fourth validation will remove from list sql users without login. The rule here is Sid with length higher than 16
                            $UsersToWork = $db.Users | Where-Object { $_.Login -eq "" -and ($_.ID -gt 4) -and ($Users -contains $_.Name) -and (($_.Sid.Length -gt 16 -and $_.LoginType -eq [Microsoft.SqlServer.Management.Smo.LoginType]::SqlLogin) -eq $false) }

                        }

                        if ($UsersToWork.Count -gt 0) {
                            Write-Message -Level Verbose -Message "Orphan users found"
                            $UsersToRemove = @()
                            foreach ($User in $UsersToWork) {
                                $ExistLogin = $server.logins | Where-Object {
                                    $_.Isdisabled -eq $False -and
                                    $_.IsSystemObject -eq $False -and
                                    $_.IsLocked -eq $False -and
                                    $_.Name -eq $User.Name
                                }

                                if ($ExistLogin) {
                                    if ($server.versionMajor -gt 8) {
                                        $query = "ALTER USER " + $User + " WITH LOGIN = " + $User
                                    }
                                    else {
                                        $query = "exec sp_change_users_login 'update_one', '$User'"
                                    }

                                    if ($Pscmdlet.ShouldProcess($db.Name, "Mapping user '$($User.Name)'")) {
                                        $server.Databases[$db.Name].ExecuteNonQuery($query) | Out-Null
                                        Write-Message -Level Verbose -Message "User '$($User.Name)' mapped with their login."

                                        [PSCustomObject]@{
                                            ComputerName = $server.ComputerName
                                            InstanceName = $server.ServiceName
                                            SqlInstance  = $server.DomainInstanceName
                                            DatabaseName = $db.Name
                                            User         = $User.Name
                                            Status       = "Success"
                                        }
                                    }
                                }
                                else {
                                    if ($RemoveNotExisting) {
                                        #add user to collection
                                        $UsersToRemove += $User
                                    }
                                    else {
                                        Write-Message -Level Verbose -Message "Orphan user $($User.Name) does not have matching login."
                                        [PSCustomObject]@{
                                            ComputerName = $server.ComputerName
                                            InstanceName = $server.ServiceName
                                            SqlInstance  = $server.DomainInstanceName
                                            DatabaseName = $db.Name
                                            User         = $User.Name
                                            Status       = "No matching login"
                                        }
                                    }
                                }
                            }

                            #With the collection complete invoke remove.
                            if ($RemoveNotExisting) {
                                if ($Force) {
                                    if ($Pscmdlet.ShouldProcess($db.Name, "Remove-DbaOrphanUser")) {
                                        Write-Message -Level Verbose -Message "Calling 'Remove-DbaOrphanUser' with -Force."
                                        Remove-DbaOrphanUser -SqlInstance $server -Database $db.Name -User $UsersToRemove -Force
                                    }
                                }
                                else {
                                    if ($Pscmdlet.ShouldProcess($db.Name, "Remove-DbaOrphanUser")) {
                                        Write-Message -Level Verbose -Message "Calling 'Remove-DbaOrphanUser'."
                                        Remove-DbaOrphanUser -SqlInstance $server -Database $db.Name -User $UsersToRemove
                                    }
                                }
                            }
                        }
                        else {
                            Write-Message -Level Verbose -Message "No orphan users found on database '$db'."
                        }
                        #reset collection
                        $UsersToWork = $null
                    }
                    catch {
                        Stop-Function -Message $_ -Continue
                    }
                }
            }
            else {
                Write-Message -Level Verbose -Message "There are no databases to analyse."
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Repair-SqlOrphanUser
    }
}
tools\dbatools\functions\Repair-DbaServerName.ps1
function Repair-DbaServerName {
    <#
        .SYNOPSIS
            Renames @@SERVERNAME to match with the Windows name.

        .DESCRIPTION
            When a SQL Server's host OS is renamed, the SQL Server should be as well. This helps with Availability Groups and Kerberos.

            This command renames @@SERVERNAME to match with the Windows name. The new name is automatically determined. It does not matter if you use an alias to connect to the SQL instance.

            If the automatically determined new name matches the old name, the command will not run.

            https://www.mssqltips.com/sqlservertip/2525/steps-to-change-the-server-name-for-a-sql-server-machine/

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER AutoFix
            If this switch is enabled, the repair will be performed automatically.

        .PARAMETER Force
            If this switch is enabled, most confirmation prompts will be skipped.
        
        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.


        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .NOTES
            Tags: SPN
            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Repair-DbaServerName

        .EXAMPLE
            Repair-DbaServerName -SqlInstance sql2014

            Checks to see if the server name is updatable and changes the name with a number of prompts.

        .EXAMPLE
            Repair-DbaServerName -SqlInstance sql2014 -AutoFix

            Checks to see if the server name is updatable and automatically performs the change. Replication or mirroring will be broken if necessary.

        .EXAMPLE
            Repair-DbaServerName -SqlInstance sql2014 -AutoFix -Force

            Checks to see if the server name is updatable and automatically performs the change, bypassing most prompts and confirmations. Replication or mirroring will be broken if necessary.
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High")]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [switch]$AutoFix,
        [switch]$Force,
        [switch][Alias('Silent')]
        $EnableException
    )

    begin {
        if ($Force -eq $true) {
            $ConfirmPreference = "None"
        }
    }

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($server.isClustered) {
                Write-Message -Level Warning -Message "$instance is a cluster. Microsoft does not support renaming clusters."
                continue
            }


            # Check to see if we can easily proceed

            $nametest = Test-DbaServerName $server -EnableException | Select-Object *
            $oldserverinstancename = $nametest.ServerName
            $SqlInstancename = $nametest.SqlInstance

            if ($nametest.RenameRequired -eq $false) {
                Stop-Function -Continue -Message "Good news! $oldserverinstancename's @@SERVERNAME does not need to be changed. If you'd like to rename it, first rename the Windows server."
            }

            if (-not $nametest.updatable) {
                Write-Message -Level Output -Message "Test-DbaServerName reports that the rename cannot proceed with a rename in this $instance's current state."

                foreach ($nametesterror in $nametest.Blockers) {
                    if ($nametesterror -like '*replication*') {

                        if (-not $AutoFix) {
                            Stop-Function -Message "Cannot proceed because some databases are involved in replication. You can run exec sp_dropdistributor @no_checks = 1 but that may be pretty dangerous. Alternatively, you can run -AutoFix to automatically fix this issue. AutoFix will also break all database mirrors."
                            return
                        }
                        else {
                            if ($Pscmdlet.ShouldProcess("console", "Prompt will appear for confirmation to break replication.")) {
                                $title = "You have chosen to AutoFix the blocker: replication."
                                $message = "We can run sp_dropdistributor which will pretty much destroy replication on this server. Do you wish to continue? (Y/N)"
                                $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Will continue"
                                $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Will exit"
                                $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
                                $result = $host.ui.PromptForChoice($title, $message, $options, 1)

                                if ($result -eq 1) {
                                    Stop-Function -Message "Failure" -Target $server -ErrorRecord $_ -Continue
                                }
                                else {
                                    Write-Message -Level Output -Message "`nPerforming sp_dropdistributor @no_checks = 1."
                                    $sql = "sp_dropdistributor @no_checks = 1"
                                    Write-Message -Level Debug -Message $sql
                                    try {
                                        $null = $server.Query($sql)
                                    }
                                    catch {
                                        Stop-Function -Message "Failure" -Target $server -ErrorRecord $_ -Continue
                                    }
                                }
                            }
                        }
                    }
                    elseif ($Error -like '*mirror*') {
                        if ($AutoFix -eq $false) {
                            Stop-Function -Message "Cannot proceed because some databases are being mirrored. Stop mirroring to proceed. Alternatively, you can run -AutoFix to automatically fix this issue. AutoFix will also stop replication." -Continue
                        }
                        else {
                            if ($Pscmdlet.ShouldProcess("console", "Prompt will appear for confirmation to break replication.")) {
                                $title = "You have chosen to AutoFix the blocker: mirroring."
                                $message = "We can run sp_dropdistributor which will pretty much destroy replication on this server. Do you wish to continue? (Y/N)"
                                $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Will continue"
                                $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Will exit"
                                $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
                                $result = $host.ui.PromptForChoice($title, $message, $options, 1)

                                if ($result -eq 1) {
                                    Write-Message -Level Output -Message "Okay, moving on."
                                }
                                else {
                                    Write-Message -Level Verbose -Message "Removing Mirroring"

                                    foreach ($database in $server.Databases) {
                                        if ($database.IsMirroringEnabled) {
                                            $dbname = $database.name

                                            try {
                                                Write-Message -Level Verbose -Message "Breaking mirror for $dbname."
                                                $database.ChangeMirroringState([Microsoft.SqlServer.Management.Smo.MirroringOption]::Off)
                                                $database.Alter()
                                                $database.Refresh()
                                            }
                                            catch {
                                                Stop-Function -Message "Failure" -Target $server -ErrorRecord $_
                                                return
                                                #throw "Could not break mirror for $dbname. Skipping."
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            # ^ That's embarrassing

            $instancename = $server.InstanceName

            if (-not $instancename) {
                $instancename = "MSSQLSERVER"
            }

            try {
                $allsqlservices = Get-Service -ComputerName $instance.ComputerName -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -like "SQL*$instancename*" -and $_.Status -eq "Running" }
            }
            catch {
                Write-Message -Level Warning -Message "Can't contact $instance using Get-Service. This means the script will not be able to automatically restart SQL services."
            }

            if ($nametest.Warnings.length -gt 0) {
                $reportingservice = Get-Service -ComputerName $instance.ComputerName -DisplayName "SQL Server Reporting Services ($instancename)" -ErrorAction SilentlyContinue

                if ($reportingservice.Status -eq "Running") {
                    if ($Pscmdlet.ShouldProcess($server.name, "Reporting Services is running for this instance. Would you like to automatically stop this service?")) {
                        $reportingservice | Stop-Service
                        Write-Message -Level Warning -Message "You must reconfigure Reporting Services using Reporting Services Configuration Manager or PowerShell once the server has been successfully renamed."
                    }
                }
            }

            if ($Pscmdlet.ShouldProcess($server.name, "Performing sp_dropserver to remove the old server name, $oldserverinstancename, then sp_addserver to add $SqlInstancename")) {
                $sql = "sp_dropserver '$oldserverinstancename'"
                Write-Message -Level Debug -Message $sql
                try {
                    $null = $server.Query($sql)
                }
                catch {
                    Stop-Function -Message "Failure" -Target $server -ErrorRecord $_
                    return
                }

                $sql = "sp_addserver '$SqlInstancename', local"
                Write-Message -Level Debug -Message $sql

                try {
                    $null = $server.Query($sql)
                }
                catch {
                    Stop-Function -Message "Failure" -Target $server -ErrorRecord $_
                    return
                }
                $renamed = $true
            }

            if ($null -eq $allsqlservices) {
                Write-Message -Level Warning -Message "Could not contact $($instance.ComputerName) using Get-Service. You must manually restart the SQL Server instance."
                $needsrestart = $true
            }
            else {
                if ($Pscmdlet.ShouldProcess($instance.ComputerName, "Rename complete! The SQL Service must be restarted to commit the changes. Would you like to restart the $instancename instance now?")) {
                    try {
                        Write-Message -Level Verbose -Message "Stopping SQL Services for the $instancename instance"
                        $allsqlservices | Stop-Service -Force -WarningAction SilentlyContinue # because it reports the wrong name
                        Write-Message -Level Verbose -Message "Starting SQL Services for the $instancename instance."
                        $allsqlservices | Where-Object { $_.DisplayName -notlike "*reporting*" } | Start-Service -WarningAction SilentlyContinue # because it reports the wrong name
                    }
                    catch {
                        Stop-Function -Message "Failure" -Target $server -ErrorRecord $_ -Continue
                    }
                }
            }

            if ($renamed -eq $true) {
                Write-Message -Level Verbose -Message "$instance successfully renamed from $oldserverinstancename to $SqlInstancename."
                Test-DbaServerName -SqlInstance $server
            }

            if ($needsrestart -eq $true) {
                Write-Message -Level Warning -Message "SQL Service restart for $SqlInstancename still required."
            }
        }
    }
}
tools\dbatools\functions\Reset-DbaAdmin.ps1
function Reset-DbaAdmin {
    <#
        .SYNOPSIS
            This function allows administrators to regain access to SQL Servers in the event that passwords or access was lost.

            Supports SQL Server 2005 and above. Windows administrator access is required.

        .DESCRIPTION
            This function allows administrators to regain access to local or remote SQL Servers by either resetting the sa password, adding the sysadmin role to existing login, or adding a new login (SQL or Windows) and granting it sysadmin privileges.

            This is accomplished by stopping the SQL services or SQL Clustered Resource Group, then restarting SQL via the command-line using the /mReset-DbaAdmin parameter which starts the server in Single-User mode and only allows this script to connect.

            Once the service is restarted, the following tasks are performed:
            - Login is added if it doesn't exist
            - If login is a Windows User, an attempt is made to ensure it exists
            - If login is a SQL Login, password policy will be set to OFF when creating the login, and SQL Server authentication will be set to Mixed Mode.
            - Login will be enabled and unlocked
            - Login will be added to sysadmin role

            If failures occur at any point, a best attempt is made to restart the SQL Server.

            In order to make this script as portable as possible, System.Data.SqlClient and Get-WmiObject are used (as opposed to requiring the Failover Cluster Admin tools or SMO).

            If using this function against a remote SQL Server, ensure WinRM is configured and accessible. If this is not possible, run the script locally.

            Tested on Windows XP, 7, 8.1, Server 2012 and Windows Server Technical Preview 2.
            Tested on SQL Server 2005 SP4 through 2016 CTP2.

        .PARAMETER SqlInstance
            The SQL Server instance. SQL Server must be 2005 and above, and can be a clustered or stand-alone instance.

        .PARAMETER Login
            By default, the Login parameter is "sa" but any other SQL or Windows account can be specified. If a login does not currently exist, it will be added.

            When adding a Windows login to remote servers, ensure the SQL Server can add the login (ie, don't add WORKSTATION\Admin to remoteserver\instance. Domain users and Groups are valid input.

        .PARAMETER SecurePassword
            By default, if a SQL Login is detected, you will be prompted for a password. Use this to securely bypass the prompt.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER Force
            If this switch is enabled, the Login(s) will be dropped and recreated on Destination. Logins that own Agent jobs cannot be dropped at this time.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .EXAMPLE
            Reset-DbaAdmin -SqlInstance sqlcluster

            Prompts for password, then resets the "sa" account password on sqlcluster.

        .EXAMPLE
            Reset-DbaAdmin -SqlInstance sqlserver\sqlexpress -Login ad\administrator

            Prompts user to confirm that they understand the SQL Service will be restarted.

            Adds the domain account "ad\administrator" as a sysadmin to the SQL instance.
            If the account already exists, it will be added to the sysadmin role.

        .EXAMPLE
            Reset-DbaAdmin -SqlInstance sqlserver\sqlexpress -Login sqladmin -Force

            Skips restart confirmation, prompts for password, then adds a SQL Login "sqladmin" with sysadmin privileges.
            If the account already exists, it will be added to the sysadmin role and the password will be reset.

        .NOTES
            Tags: WSMan
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: Admin access to server (not SQL Services),
            Remoting must be enabled and accessible if $SqlInstance is not local

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire

        .LINK
            https://dbatools.io/Reset-DbaAdmin
#>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High")]
    param (
        [Parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]
        $SqlInstance,
        [string]$Login = "sa",
        [SecureString]$SecurePassword,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Reset-SqlAdmin

        #region Utility functions
        function ConvertTo-PlainText {
            <#
                .SYNOPSIS
                    Internal function.
             #>
            [CmdletBinding()]
            param (
                [Parameter(Mandatory = $true)]
                [Security.SecureString]
                $Password
            )

            $marshal = [Runtime.InteropServices.Marshal]
            $plaintext = $marshal::PtrToStringAuto($marshal::SecureStringToBSTR($Password))
            return $plaintext
        }

        function Invoke-ResetSqlCmd {
            <#
                .SYNOPSIS
                    Internal function. Executes a SQL statement against specified computer, and uses "Reset-DbaAdmin" as the Application Name.
            #>
            [OutputType([System.Boolean])]
            [CmdletBinding()]
            param (
                [Parameter(Mandatory = $true)]
                [Alias("ServerInstance", "SqlServer")]
                [DbaInstanceParameter]
                $SqlInstance,
                [string]$sql
            )
            try {
                $connstring = "Data Source=$SqlInstance;Integrated Security=True;Connect Timeout=2;Application Name=Reset-DbaAdmin"
                $conn = New-Object System.Data.SqlClient.SqlConnection $connstring
                $conn.Open()
                $cmd = New-Object system.data.sqlclient.sqlcommand($null, $conn)
                $cmd.CommandText = $sql
                $cmd.ExecuteNonQuery() | Out-Null
                $cmd.Dispose()
                $conn.Close()
                $conn.Dispose()
                return $true
            }
            catch {
                return $false
            }
        }
        #endregion Utility functions
    }

    process {
        if ($Force) {
            $ConfirmPreference = "none"
        }

        $baseaddress = $SqlInstance.ComputerName

        # Before we continue, we need confirmation.
        if ($pscmdlet.ShouldProcess($baseaddress, "Reset-DbaAdmin (SQL Server instance $SqlInstance will restart)")) {
            # Get hostname

            if ($baseaddress -eq "." -or $baseaddress -eq $env:COMPUTERNAME -or $baseaddress -eq "localhost") {
                $ipaddr = "."
                $hostname = $env:COMPUTERNAME
                $baseaddress = $env:COMPUTERNAME
            }

            # If server is not local, get IP address and NetBios name in case CNAME records were referenced in the SQL hostname
            if ($baseaddress -ne $env:COMPUTERNAME) {
                # Test for WinRM #Test-WinRM neh
                winrm id -r:$baseaddress 2>$null | Out-Null
                if ($LastExitCode -ne 0) {
                    throw "Remote PowerShell access not enabled on on $source or access denied. Quitting."
                }

                # Test Connection first using Test-Connection which requires ICMP access then failback to tcp if pings are blocked
                Write-Message -Level Verbose -Message "Testing connection to $baseaddress"
                $testconnect = Test-Connection -ComputerName $baseaddress -Count 1 -Quiet

                if ($testconnect -eq $false) {
                    Write-Message -Level Verbose -Message "First attempt using ICMP failed. Trying to connect using sockets. This may take up to 20 seconds."
                    $tcp = New-Object System.Net.Sockets.TcpClient
                    try {
                        $tcp.Connect($hostname, 135)
                        $tcp.Close()
                        $tcp.Dispose()
                    }
                    catch {
                        throw "Can't connect to $baseaddress either via ping or tcp (WMI port 135)"
                    }
                }
                Write-Message -Level Verbose -Message "Resolving IP address."
                try {
                    $hostentry = [System.Net.Dns]::GetHostEntry($baseaddress)
                    $ipaddr = ($hostentry.AddressList | Where-Object { $_ -notlike '169.*' } | Select-Object -First 1).IPAddressToString
                }
                catch {
                    throw "Could not resolve SqlServer IP or NetBIOS name"
                }

                Write-Message -Level Verbose -Message "Resolving NetBIOS name."
                try {
                    $hostname = (Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter IPEnabled=TRUE -ComputerName $ipaddr).PSComputerName
                    if ($null -eq $hostname) {
                        $hostname = (nbtstat -A $ipaddr | Where-Object { $_ -match '\<00\>  UNIQUE' } | ForEach-Object { $_.SubString(4, 14) }).Trim()
                    }
                }
                catch {
                    throw "Could not access remote WMI object. Check permissions and firewall."
                }
            }

            # Setup remote session if server is not local
            if ($hostname -ne $env:COMPUTERNAME) {
                try {
                    $session = New-PSSession -ComputerName $hostname
                }
                catch {
                    throw "Can't access $hostname using PSSession. Check your firewall settings and ensure Remoting is enabled or run the script locally."
                }
            }

            Write-Message -Level Verbose -Message "Detecting login type."
            # Is login a Windows login? If so, does it exist?
            if ($login -match "\\") {
                Write-Message -Level Verbose -Message "Windows login detected. Checking to ensure account is valid."
                $windowslogin = $true
                try {
                    if ($hostname -eq $env:COMPUTERNAME) {
                        $account = New-Object System.Security.Principal.NTAccount($args)
                        $sid = $account.Translate([System.Security.Principal.SecurityIdentifier])
                    }
                    else {
                        Invoke-Command -ErrorAction Stop -Session $session -ArgumentList $login -ScriptBlock {
                            $account = New-Object System.Security.Principal.NTAccount($args)
                            $sid = $account.Translate([System.Security.Principal.SecurityIdentifier])
                        }
                    }
                }
                catch {
                    Write-Message -Level Warning -Message "Cannot resolve Windows User or Group $login. Trying anyway."
                }
            }

            # If it's not a Windows login, it's a SQL login, so it needs a password.
            if ($windowslogin -ne $true -and (Test-Bound -Not -ParameterName SecurePassword)) {
                Write-Message -Level Verbose -Message "SQL login detected"
                do {
                    $Password = Read-Host -AsSecureString "Please enter a new password for $login"
                }
                while ($Password.Length -eq 0)
            }
            
            If ((Test-Bound -ParameterName SecurePassword)) {
                $Password = $SecurePassword
            }
            
            # Get instance and service display name, then get services
            $instance = $null
            $instance = $SqlInstance.InstanceName
            if (-not $instance) {
                $instance = "MSSQLSERVER"
            }
            $displayName = "SQL Server ($instance)"

            try {
                if ($hostname -eq $env:COMPUTERNAME) {
                    $instanceservices = Get-Service -ErrorAction Stop | Where-Object { $_.DisplayName -like "*($instance)*" -and $_.Status -eq "Running" }
                    $sqlservice = Get-Service -ErrorAction Stop | Where-Object DisplayName -eq "SQL Server ($instance)"
                }
                else {
                    $instanceservices = Get-Service -ComputerName $ipaddr -ErrorAction Stop | Where-Object { $_.DisplayName -like "*($instance)*" -and $_.Status -eq "Running" }
                    $sqlservice = Get-Service -ComputerName $ipaddr -ErrorAction Stop | Where-Object DisplayName -eq "SQL Server ($instance)"
                }
            }
            catch {
                Stop-Function -Message "Cannot connect to WMI on $hostname or SQL Service does not exist. Check permissions, firewall and SQL Server running status." -ErrorRecord $_ -Target $SqlInstance
                return
            }

            if (-not $instanceservices) {
                Stop-Function -Message "Couldn't find SQL Server instance. Check the spelling, ensure the service is running and try again." -Target $SqlInstance
                return
            }

            Write-Message -Level Verbose -Message "Attempting to stop SQL Services."

            # Check to see if service is clustered. Clusters don't support -m (since the cluster service
            # itself connects immediately) or -f, so they are handled differently.
            try {
                $checkcluster = Get-Service -ComputerName $ipaddr -ErrorAction Stop | Where-Object { $_.Name -eq "ClusSvc" -and $_.Status -eq "Running" }
            }
            catch {
                Stop-Function -Message "Can't check services." -Target $SqlInstance -ErrorRecord $_
                return
            }

            if ($null -ne $checkcluster) {
                $clusterResource = Get-DbaCmObject -ClassName "MSCluster_Resource" -Namespace "root\mscluster" -ComputerName $hostname |
                    Where-Object { $_.Name.StartsWith("SQL Server") -and $_.OwnerGroup -eq "SQL Server ($instance)" }
            }

            # Take SQL Server offline so that it can be started in single-user mode
            if ($clusterResource.count -gt 0) {
                $isclustered = $true
                try {
                    $clusterResource | Where-Object { $_.Name -eq "SQL Server" } | ForEach-Object { $_.TakeOffline(60) }
                }
                catch {
                    $clusterResource | Where-Object { $_.Name -eq "SQL Server" } | ForEach-Object { $_.BringOnline(60) }
                    $clusterResource | Where-Object { $_.Name -ne "SQL Server" } | ForEach-Object { $_.BringOnline(60) }
                    Stop-Function -Message "Could not stop the SQL Service. Restarted SQL Service and quit." -ErrorRecord $_ -Target $SqlInstance
                    return
                }
            }
            else {
                try {
                    Stop-Service -InputObject $sqlservice -Force -ErrorAction Stop
                    Write-Message -Level Verbose -Message "Successfully stopped SQL service."
                }
                catch {
                    Start-Service -InputObject $instanceservices -ErrorAction Stop
                    Stop-Function -Message "Could not stop the SQL Service. Restarted SQL service and quit." -ErrorRecord $_ -Target $SqlInstance
                    return
                }
            }

            # /mReset-DbaAdmin Starts an instance of SQL Server in single-user mode and only allows this script to connect.
            Write-Message -Level Verbose -Message "Starting SQL Service from command line."
            try {
                if ($hostname -eq $env:COMPUTERNAME) {
                    $netstart = net start ""$displayname"" /mReset-DbaAdmin 2>&1
                    if ("$netstart" -notmatch "success") {
                        throw
                    }
                }
                else {
                    $netstart = Invoke-Command -ErrorAction Stop -Session $session -ArgumentList $displayname -ScriptBlock { net start ""$args"" /mReset-DbaAdmin } 2>&1
                    foreach ($line in $netstart) {
                        if ($line.length -gt 0) { Write-Message -Level Verbose -Message $line }
                    }
                }
            }
            catch {
                Stop-Service -InputObject $sqlservice -Force -ErrorAction SilentlyContinue

                if ($isclustered) {
                    $clusterResource | Where-Object Name -eq "SQL Server" | ForEach-Object { $_.BringOnline(60) }
                    $clusterResource | Where-Object Name -ne "SQL Server" | ForEach-Object { $_.BringOnline(60) }
                }
                else {
                    Start-Service -InputObject $instanceservices -ErrorAction SilentlyContinue
                }
                Stop-Function -Message "Couldn't execute net start command. Restarted services and quit." -ErrorRecord $_
                return
            }

            Write-Message -Level Verbose -Message "Reconnecting to SQL instance."
            try {
                $null = Invoke-ResetSqlCmd -SqlInstance $sqlinstance -Sql "SELECT 1" -ErrorAction Stop
            }
            catch {
                try {
                    Start-Sleep 3
                    $null = Invoke-ResetSqlCmd -SqlInstance $sqlinstance -Sql "SELECT 1" -ErrorAction Stop
                }
                catch {
                    Stop-Service Input-Object $sqlservice -Force -ErrorAction SilentlyContinue
                    if ($isclustered) {
                        $clusterResource | Where-Object { $_.Name -eq "SQL Server" } | ForEach-Object { $_.BringOnline(60) }
                        $clusterResource | Where-Object { $_.Name -ne "SQL Server" } | ForEach-Object { $_.BringOnline(60) }
                    }
                    else {
                        Start-Service -InputObject $instanceservices -ErrorAction SilentlyContinue
                    }
                    Stop-Function -Message "Could not stop the SQL Service. Restarted SQL Service and quit." -ErrorRecord $_
                }
            }

            # Get login. If it doesn't exist, create it.
            Write-Message -Level Verbose -Message "Adding login $login if it doesn't exist."
            if ($windowslogin -eq $true) {
                $sql = "IF NOT EXISTS (SELECT name FROM master.sys.server_principals WHERE name = '$login')
                    BEGIN CREATE LOGIN [$login] FROM WINDOWS END"
                if ($(Invoke-ResetSqlCmd -SqlInstance $sqlinstance -Sql $sql) -eq $false) {
                    Write-Message -Level Warning -Message "Couldn't create login."
                }

            }
            elseif ($login -ne "sa") {
                # Create new sql user
                $sql = "IF NOT EXISTS (SELECT name FROM master.sys.server_principals WHERE name = '$login')
                    BEGIN CREATE LOGIN [$login] WITH PASSWORD = '$(ConvertTo-PlainText $Password)', CHECK_POLICY = OFF, CHECK_EXPIRATION = OFF END"
                if ($(Invoke-ResetSqlCmd -SqlInstance $sqlinstance -Sql $sql) -eq $false) {
                    Write-Message -Level Warning -Message "Couldn't create login."
                }
            }

            # If $login is a SQL Login, Mixed mode authentication is required.
            if ($windowslogin -ne $true) {
                Write-Message -Level Verbose -Message "Enabling mixed mode authentication."
                Write-Message -Level Verbose -Message "Ensuring account is unlocked."
                $sql = "EXEC xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'Software\Microsoft\MSSQLServer\MSSQLServer', N'LoginMode', REG_DWORD, 2"
                if ($(Invoke-ResetSqlCmd -SqlInstance $sqlinstance -Sql $sql) -eq $false) {
                    Write-Message -Level Warning -Message "Couldn't set to Mixed Mode."
                }

                $sql = "ALTER LOGIN [$login] WITH CHECK_POLICY = OFF
                    ALTER LOGIN [$login] WITH PASSWORD = '$(ConvertTo-PlainText $Password)' UNLOCK"
                if ($(Invoke-ResetSqlCmd -SqlInstance $sqlinstance -Sql $sql) -eq $false) {
                    Write-Message -Level Warning -Message "Couldn't unlock account."
                }
            }

            Write-Message -Level Verbose -Message "Ensuring login is enabled."
            $sql = "ALTER LOGIN [$login] ENABLE"
            if ($(Invoke-ResetSqlCmd -SqlInstance $sqlinstance -Sql $sql) -eq $false) {
                Write-Message -Level Warning -Message "Couldn't enable login."
            }

            if ($login -ne "sa") {
                Write-Message -Level Verbose -Message "Ensuring login exists within sysadmin role."
                $sql = "EXEC sp_addsrvrolemember '$login', 'sysadmin'"
                if ($(Invoke-ResetSqlCmd -SqlInstance $sqlinstance -Sql $sql) -eq $false) {
                    Write-Message -Level Warning -Message "Couldn't add to sysadmin role."
                }
            }

            Write-Message -Level Verbose -Message "Finished with login tasks."
            Write-Message -Level Verbose -Message "Restarting SQL Server."
            Stop-Service -InputObject $sqlservice -Force -ErrorAction SilentlyContinue
            if ($isclustered -eq $true) {
                $clusterResource | Where-Object Name -eq "SQL Server" | ForEach-Object { $_.BringOnline(60) }
                $clusterResource | Where-Object Name -ne "SQL Server" | ForEach-Object { $_.BringOnline(60) }
            }
            else {
                Start-Service -InputObject $instanceservices -ErrorAction SilentlyContinue
            }
        }
    }
    end {
        Write-Message -Level Verbose -Message "Script complete!"
    }
}
tools\dbatools\functions\Resolve-DbaNetworkName.ps1
function Resolve-DbaNetworkName {
    <#
        .SYNOPSIS
            Returns information about the network connection of the target computer including NetBIOS name, IP Address, domain name and fully qualified domain name (FQDN).

        .DESCRIPTION
            Retrieves the IPAddress, ComputerName from one computer.
            The object can be used to take action against its name or IPAddress.

            First ICMP is used to test the connection, and get the connected IPAddress.

            Multiple protocols (e.g. WMI, CIM, etc) are attempted before giving up.

            Important: Remember that FQDN doesn't always match "ComputerName dot Domain" as AD intends.
                There are network setup (google "disjoint domain") where AD and DNS do not match.
                "Full computer name" (as reported by sysdm.cpl) is the only match between the two,
                and it matches the "DNSHostName"  property of the computer object stored in AD.
                This means that the notation of FQDN that matches "ComputerName dot Domain" is incorrect
                in those scenarios.
                In other words, the "suffix" of the FQDN CAN be different from the AD Domain.

                This cmdlet has been providing good results since its inception but for lack of useful
                names some doubts may arise.
                Let this clear the doubts:
                - InputName: whatever has been passed in
                - ComputerName: hostname only
                - IPAddress: IP Address
                - DNSHostName: hostname only, coming strictly from DNS (as reported from the calling computer)
                - DNSDomain: domain only, coming strictly from DNS (as reported from the calling computer)
                - Domain: domain only, coming strictly from AD (i.e. the domain the ComputerName is joined to)
                - DNSHostEntry: Fully name as returned by DNS [System.Net.Dns]::GetHostEntry
                - FQDN: "legacy" notation of ComputerName "dot" Domain (coming from AD)
                - FullComputerName: Full name as configured from within the Computer (i.e. the only secure match between AD and DNS)

            So, if you need to use something, go with FullComputerName, always, as it is the most correct in every scenario.

        .PARAMETER ComputerName
            The Server that you're connecting to.
            This can be the name of a computer, a SMO object, an IP address or a SQL Instance.

        .PARAMETER Credential
            Credential object used to connect to the SQL Server as a different user

        .PARAMETER Turbo
            Resolves without accessing the server itself. Faster but may be less accurate because it relies on DNS only,
            so it may fail spectacularly for disjoin-domain setups. Also, everyone has its own DNS (i.e. results may vary
            changing the computer where the function runs)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Network, Resolve
            Author: Klaas Vandenberghe ( @PowerDBAKlaas )
            Editor: niphlod

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Resolve-DbaNetworkName

        .EXAMPLE
            Resolve-DbaNetworkName -ComputerName ServerA

            Returns a custom object displaying InputName, ComputerName, IPAddress, DNSHostName, DNSDomain, Domain, DNSHostEntry, FQDN, DNSHostEntry for ServerA

        .EXAMPLE
            Resolve-DbaNetworkName -SqlInstance sql2016\sqlexpress

            Returns a custom object displaying InputName, ComputerName, IPAddress, DNSHostName, DNSDomain, Domain, DNSHostEntry, FQDN, DNSHostEntry  for the SQL instance sql2016\sqlexpress

        .EXAMPLE
            Resolve-DbaNetworkName -SqlInstance sql2016\sqlexpress, sql2014

            Returns a custom object displaying InputName, ComputerName, IPAddress, DNSHostName, DNSDomain, Domain, DNSHostEntry, FQDN, DNSHostEntry  for the SQL instance sql2016\sqlexpress and sql2014

        .EXAMPLE
            Get-DbaRegisteredServer -SqlInstance sql2014 | Resolve-DbaNetworkName

            Returns a custom object displaying InputName, ComputerName, IPAddress, DNSHostName, Domain, FQDN for all SQL Servers returned by Get-DbaRegisteredServer
    #>
    [CmdletBinding()]
    param (
        [parameter(ValueFromPipeline)]
        [Alias('cn', 'host', 'ServerInstance', 'Server', 'SqlInstance')]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential] $Credential,
        [Alias('FastParrot')]
        [switch]$Turbo,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($Computer in $ComputerName) {
            $conn = $ipaddress = $null

            $OGComputer = $Computer

            if ($Computer.IsLocalhost) {
                $Computer = $env:COMPUTERNAME
            }
            else {
                $Computer = $Computer.ComputerName
            }

            if ($Turbo) {
                try {
                    Write-Message -Level VeryVerbose -Message "Resolving $Computer using .NET.Dns GetHostEntry"
                    $ipaddress = ([System.Net.Dns]::GetHostEntry($Computer)).AddressList[0].IPAddressToString
                    Write-Message -Level VeryVerbose -Message "Resolving $ipaddress using .NET.Dns GetHostByAddress"
                    $fqdn = [System.Net.Dns]::GetHostByAddress($ipaddress).HostName
                }
                catch {
                    try {
                        Write-Message -Level VeryVerbose -Message "Resolving $Computer and IP using .NET.Dns GetHostEntry"
                        $resolved = [System.Net.Dns]::GetHostEntry($Computer)
                        $ipaddress = $resolved.AddressList[0].IPAddressToString
                        $fqdn = $resolved.HostName
                    }
                    catch {
                        Stop-Function -Message "DNS name not found" -Continue -InnerErrorRecord $_
                    }
                }

                if ($fqdn -notmatch "\.") {
                    if ($computer.ComputerName -match "\.") {
                        $dnsdomain = $computer.ComputerName.Substring($computer.ComputerName.IndexOf(".") + 1)
                        $fqdn = "$resolved.$dnsdomain"
                    }
                    else {
                        $dnsdomain = "$env:USERDNSDOMAIN".ToLower()
                        if ($dnsdomain -match "\.") {
                            $fqdn = "$fqdn.$dnsdomain"
                        }
                    }
                }

                $hostname = $fqdn.Split(".")[0]

                [PSCustomObject]@{
                    InputName        = $OGComputer
                    ComputerName     = $hostname.ToUpper()
                    IPAddress        = $ipaddress
                    DNSHostname      = $hostname
                    DNSDomain        = $fqdn.Replace("$hostname.", "")
                    Domain           = $fqdn.Replace("$hostname.", "")
                    DNSHostEntry     = $fqdn
                    FQDN             = $fqdn
                    FullComputerName = $fqdn
                }

            }
            else {

                Write-Message -Level Verbose -Message "Connecting to $Computer"

                try {
                    $ipaddress = ((Test-Connection -ComputerName $Computer -Count 1 -ErrorAction Stop).Ipv4Address).IPAddressToString
                }
                catch {
                    try {
                        if ($env:USERDNSDOMAIN) {
                            $ipaddress = ((Test-Connection -ComputerName "$Computer.$env:USERDNSDOMAIN" -Count 1 -ErrorAction SilentlyContinue).Ipv4Address).IPAddressToString
                            $Computer = "$Computer.$env:USERDNSDOMAIN"
                        }
                    }
                    catch {
                        $Computer = $OGComputer
                        $ipaddress = ([System.Net.Dns]::GetHostEntry($Computer)).AddressList[0].IPAddressToString
                    }
                }

                if ($ipaddress) {
                    Write-Message -Level VeryVerbose -Message "IP Address from $Computer is $ipaddress"
                }
                else {
                    Write-Message -Level VeryVerbose -Message "No IP Address returned from $Computer"
                    Write-Message -Level VeryVerbose -Message "Using .NET.Dns to resolve IP Address"
                    return (Resolve-DbaNetworkName -ComputerName $Computer -Turbo)
                }

                if ($PSVersionTable.PSVersion.Major -gt 2) {
                    Write-Message -Level System -Message "Your PowerShell Version is $($PSVersionTable.PSVersion.Major)"
                    try {
                        try {
                            # if an alias (CNAME) is passed we should try to connect to the A name via CIM or WinRM
                            $ComputerNameIP = ([System.Net.Dns]::GetHostEntry($Computer)).AddressList[0].IPAddressToString
                            $RemoteComputer = [System.Net.Dns]::GetHostByAddress($ComputerNameIP).HostName
                        }
                        catch {
                            $RemoteComputer = $Computer
                        }
                        Write-Message -Level VeryVerbose -Message "Getting computer information from $RemoteComputer"
                        $ScBlock = {
                            $IPGProps = [System.Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties()
                            return [pscustomobject]@{
                                'DNSDomain' = $IPGProps.DomainName
                            }
                        }
                        if (Test-Bound "Credential") {
                            $conn = Get-DbaCmObject -ClassName win32_ComputerSystem -Computer $RemoteComputer -Credential $Credential -EnableException
                            $DNSSuffix = Invoke-Command2 -Computer $RemoteComputer -ScriptBlock $ScBlock -Credential $Credential -ErrorAction Stop
                        }
                        else {
                            $conn = Get-DbaCmObject -ClassName win32_ComputerSystem -Computer $RemoteComputer -EnableException
                            $DNSSuffix = Invoke-Command2 -Computer $RemoteComputer -ScriptBlock $ScBlock -ErrorAction Stop
                        }
                    }
                    catch {
                        Write-Message -Level Verbose -Message "Unable to get computer information from $Computer"
                    }

                    if (!$conn) {
                        Write-Message -Level Verbose -Message "No WMI/CIM from $Computer. Getting HostName via .NET.Dns"
                        try {
                            $fqdn = ([System.Net.Dns]::GetHostEntry($Computer)).HostName
                            $hostname = $fqdn.Split(".")[0]
                            $suffix = $fqdn.Replace("$hostname.", "")
                            if ($hostname -eq $fqdn) {
                                $suffix = ""
                            }
                            $conn = [PSCustomObject]@{
                                Name        = $Computer
                                DNSHostname = $hostname
                                Domain      = $suffix
                            }
                            $DNSSuffix = [PSCustomObject]@{
                                DNSDomain = $suffix
                            }
                        }
                        catch {
                            Stop-Function -Message "No .NET.Dns information from $Computer" -InnerErrorRecord $_ -Continue
                        }
                    }
                }
                if ($DNSSuffix.DNSDomain.Length -eq 0) {
                    $FullComputerName = $conn.DNSHostname
                }
                else {
                    $FullComputerName = $conn.DNSHostname + "." + $DNSSuffix.DNSDomain
                }
                try {
                    Write-Message -Level VeryVerbose -Message "Resolving $FullComputerName using .NET.Dns GetHostEntry"
                    $hostentry = ([System.Net.Dns]::GetHostEntry($FullComputerName)).HostName
                }
                catch {
                    Stop-Function -Message ".NET.Dns GetHostEntry failed for $FullComputerName" -InnerErrorRecord $_
                }

                $fqdn = "$($conn.DNSHostname).$($conn.Domain)"
                if ($fqdn -eq ".") {
                    Write-Message -Level VeryVerbose -Message "No full FQDN found. Setting to null"
                    $fqdn = $null
                }
                if ($FullComputerName -eq ".") {
                    Write-Message -Level VeryVerbose -Message "No DNS FQDN found. Setting to null"
                    $FullComputerName = $null
                }

                if ($FullComputerName -ne "." -and $FullComputerName -notmatch "\." -and $conn.Domain -match "\.") {
                    $d = $conn.Domain
                    $FullComputerName = "$FullComputerName.$d"
                }

                [PSCustomObject]@{
                    InputName        = $OGComputer
                    ComputerName     = $conn.Name
                    IPAddress        = $ipaddress
                    DNSHostName      = $conn.DNSHostname
                    DNSDomain        = $DNSSuffix.DNSDomain
                    Domain           = $conn.Domain
                    DNSHostEntry     = $hostentry
                    FQDN             = $fqdn.TrimEnd(".")
                    FullComputerName = $FullComputerName
                }
            }
        }
    }
}
tools\dbatools\functions\Restart-DbaSqlService.ps1
function Restart-DbaSqlService {
    <#
        .SYNOPSIS
            Restarts SQL Server services on a computer.

        .DESCRIPTION
            Restarts the SQL Server related services on one or more computers. Will follow SQL Server service dependencies.

            Requires Local Admin rights on destination computer(s).

        .PARAMETER ComputerName
            The SQL Server (or server in general) that you're connecting to. This command handles named instances.

        .PARAMETER InstanceName
            Only affects services that belong to the specific instances.

        .PARAMETER Credential
            Credential object used to connect to the computer as a different user.

        .PARAMETER Type
            Use -Type to collect only services of the desired SqlServiceType.
            Can be one of the following: "Agent","Browser","Engine","FullText","SSAS","SSIS","SSRS"

        .PARAMETER Timeout
            How long to wait for the start/stop request completion before moving on. Specify 0 to wait indefinitely.

        .PARAMETER InputObject
            A collection of services from Get-DbaSqlService

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER WhatIf
            Shows what would happen if the cmdlet runs. The cmdlet is not run.

        .PARAMETER Confirm
            Prompts you for confirmation before running the cmdlet.

        .PARAMETER Force
            Will stop dependent SQL Server agents when stopping Engine services.

        .NOTES
            Tags: Service, SqlServer, Instance, Connect
            Author: Kirill Kravtsov( @nvarscar )

            Requires Local Admin rights on destination computer(s).

            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2017 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Restart-DbaSqlService

        .EXAMPLE
            Restart-DbaSqlService -ComputerName sqlserver2014a

            Restarts the SQL Server related services on computer sqlserver2014a.

        .EXAMPLE
            'sql1','sql2','sql3'| Get-DbaSqlService | Restart-DbaSqlService

            Gets the SQL Server related services on computers sql1, sql2 and sql3 and restarts them.

        .EXAMPLE
            Restart-DbaSqlService -ComputerName sql1,sql2 -Instance MSSQLSERVER

            Restarts the SQL Server services related to the default instance MSSQLSERVER on computers sql1 and sql2.

        .EXAMPLE
            Restart-DbaSqlService -ComputerName $MyServers -Type SSRS

            Restarts the SQL Server related services of type "SSRS" (Reporting Services) on computers in the variable MyServers.

        .EXAMPLE
            Restart-DbaSqlService -ComputerName sql1 -Type Engine -Force

            Restarts SQL Server database engine services on sql1 forcing dependent SQL Server Agent services to restart as well.
    #>
    [CmdletBinding(DefaultParameterSetName = "Server", SupportsShouldProcess = $true)]
    Param (
        [Parameter(ParameterSetName = "Server", Position = 1)]
        [Alias("cn", "host", "Server")]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [Alias("Instance")]
        [string[]]$InstanceName,
        [ValidateSet("Agent", "Browser", "Engine", "FullText", "SSAS", "SSIS", "SSRS")]
        [string[]]$Type,
        [parameter(ValueFromPipeline = $true, Mandatory = $true, ParameterSetName = "Service")]
        [Alias("ServiceCollection")]
        [object[]]$InputObject,
        [int]$Timeout = 30,
        [PSCredential]$Credential,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        $processArray = @()
        if ($PsCmdlet.ParameterSetName -eq "Server") {
            $serviceParams = @{ ComputerName = $ComputerName }
            if ($InstanceName) { $serviceParams.InstanceName = $InstanceName }
            if ($Type) { $serviceParams.Type = $Type }
            if ($Credential) { $serviceParams.Credential = $Credential }
            if ($EnableException) { $serviceParams.Silent = $EnableException }
            $InputObject = Get-DbaSqlService @serviceParams
        }
    }
    process {
        #Get all the objects from the pipeline before proceeding
        $processArray += $InputObject
    }
    end {
        $processArray = [array]($processArray | Where-Object { (!$InstanceName -or $_.InstanceName -in $InstanceName) -and (!$Type -or $_.ServiceType -in $Type) })
        foreach ($service in $processArray) {
            if ($Force -and $service.ServiceType -eq 'Engine' -and !($processArray | Where-Object { $_.ServiceType -eq 'Agent' -and $_.InstanceName -eq $service.InstanceName -and $_.ComputerName -eq $service.ComputerName })) {
                Write-Message -Level Verbose -Message "Adding Agent service to the list for service $($service.ServiceName) on $($service.ComputerName), since -Force has been specified"
                #Construct parameters to call Get-DbaSqlService
                $serviceParams = @{
                    ComputerName = $service.ComputerName
                    InstanceName = $service.InstanceName
                    Type         = 'Agent'
                }
                if ($Credential) { $serviceParams.Credential = $Credential }
                if ($EnableException) { $serviceParams.Silent = $EnableException }
                $processArray += @(Get-DbaSqlService @serviceParams)
            }
        }
        if ($processArray) {
            $services = Update-ServiceStatus -InputObject $processArray -Action 'stop' -Timeout $Timeout -EnableException $EnableException
            foreach ($service in ($services | Where-Object { $_.Status -eq 'Failed'})) {
                $service
            }
            $services = $services | Where-Object { $_.Status -eq 'Successful'}
            if ($services) {
                Update-ServiceStatus -InputObject $services -Action 'restart' -Timeout $Timeout -EnableException $EnableException
            }
        }
        else { Stop-Function -EnableException $EnableException -Message "No SQL Server services found with current parameters." }
    }
}
tools\dbatools\functions\Restore-DbaBackupFromDirectory.ps1
function Restore-DbaBackupFromDirectory {
    <#
        .SYNOPSIS
            Restores SQL Server databases from the backup directory structure created by Ola Hallengren's database maintenance scripts. Different structures coming soon.

        .DESCRIPTION
            Many SQL Server database administrators use Ola Hallengren's SQL Server Maintenance Solution which can be found at http://ola.hallengren.com

            Hallengren uses a predictable backup structure which made it relatively easy to create a script that can restore an entire SQL Server database instance, down to the master database (next version), to a new server. This script is intended to be used in the event that the originating SQL Server becomes unavailable, thus rendering my other SQL restore script (http://goo.gl/QmfQ6s) ineffective.

        .PARAMETER SqlInstance
            The SQL Server instance to which you will be restoring the database.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Path
            Specifies the full path to the directory that contains the database backups. The SQL Server service must have read access to this path.

        .PARAMETER ReuseSourceFolderStructure
            If this switch is enabled, the folder structure used on the instance where the backup was made will be recreated. By default, the database files will be restored to the default data and log directories for the instance you're restoring onto.

        .PARAMETER NoRecovery
            If this switch is enabled, the database is left in the No Recovery state to enable further backups to be added.

        .PARAMETER Force
            If this switch is enabled, any existing database matching the name of a database being restored will be overwritten.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .NOTES
            Tags: DisasterRecovery, Backup, Restore
            Requires: sysadmin access on destination SQL Server.

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Restore-SqlBackupFromDirectory

        .EXAMPLE
            Restore-SqlBackupFromDirectory -SqlInstance sqlcluster -Path \\fileserver\share\sqlbackups\SQLSERVER2014A

            All user databases contained within \\fileserver\share\sqlbackups\SQLSERVERA will be restored to sqlcluster, down the most recent full/differential/logs.

    #>
    #Requires -Version 3.0
    [CmdletBinding()]
    Param (
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [parameter(Mandatory = $true)]
        [string]$Path,
        [switch]$NoRecovery,
        [Alias("ReuseFolderStructure")]
        [switch]$ReuseSourceFolderStructure,
        [PSCredential]$SqlCredential,
        [switch]$Force
    )

    Write-Warning "This command is no longer supported. Please use Get-ChildItem | Restore-DbaDatabase instead"
}
tools\dbatools\functions\Restore-DbaDatabase.ps1
function Restore-DbaDatabase {
    <#
    .SYNOPSIS
        Restores a SQL Server Database from a set of backupfiles

    .DESCRIPTION
        Upon being passed a list of potential backups files this command will scan the files, select those that contain SQL Server
        backup sets. It will then filter those files down to a set that can perform the requested restore, checking that we have a
        full restore chain to the point in time requested by the caller.

        The function defaults to working on a remote instance. This means that all paths passed in must be relative to the remote instance.
        XpDirTree will be used to perform the file scans


        Various means can be used to pass in a list of files to be considered. The default is to non recursively scan the folder
        passed in.

    .PARAMETER SqlInstance
        The SQL Server instance to restore to.

    .PARAMETER SqlCredential
        Allows you to login to servers using SQL Logins as opposed to Windows Auth/Integrated/Trusted.

    .PARAMETER Path
        Path to SQL Server backup files.

        Paths passed in as strings will be scanned using the desired method, default is a non recursive folder scan
        Accepts multiple paths separated by ','

        Or it can consist of FileInfo objects, such as the output of Get-ChildItem or Get-Item. This allows you to work with
        your own filestructures as needed

    .PARAMETER DatabaseName
        Name to restore the database under.
        Only works with a single database restore. If multiple database are found in the provided paths then we will exit

    .PARAMETER DestinationDataDirectory
        Path to restore the SQL Server backups to on the target instance.
        If only this parameter is specified, then all database files (data and log) will be restored to this location

    .PARAMETER DestinationLogDirectory
        Path to restore the database log files to.
        This parameter can only be specified alongside DestinationDataDirectory.

    .PARAMETER DestinationFileStreamDirectory
        Path to restore FileStream data to
        This parameter can only be specified alongside DestinationDataDirectory

    .PARAMETER RestoreTime
        Specify a DateTime object to which you want the database restored to. Default is to the latest point  available in the specified backups

    .PARAMETER NoRecovery
        Indicates if the databases should be recovered after last restore. Default is to recover

    .PARAMETER WithReplace
        Switch indicated is the restore is allowed to replace an existing database.

    .PARAMETER XpDirTree
        Switch that indicated file scanning should be performed by the SQL Server instance using xp_dirtree
        This will scan recursively from the passed in path
        You must have sysadmin role membership on the instance for this to work.

    .PARAMETER OutputScriptOnly
        Switch indicates that ONLY T-SQL scripts should be generated, no restore takes place

    .PARAMETER VerifyOnly
        Switch indicate that restore should be verified

    .PARAMETER MaintenanceSolutionBackup
        Switch to indicate the backup files are in a folder structure as created by Ola Hallengreen's maintenance scripts.
        This swith enables a faster check for suitable backups. Other options require all files to be read first to ensure we have an anchoring full backup. Because we can rely on specific locations for backups performed with OlaHallengren's backup solution, we can rely on file locations.

    .PARAMETER FileMapping
        A hashtable that can be used to move specific files to a location.
        $FileMapping = @{'DataFile1'='c:\restoredfiles\Datafile1.mdf';'DataFile3'='d:\DataFile3.mdf'}
        And files not specified in the mapping will be restored to their original location
        This Parameter is exclusive with DestinationDataDirectory

    .PARAMETER IgnoreLogBackup
        This switch tells the function to ignore transaction log backups. The process will restore to the latest full or differential backup point only

    .PARAMETER useDestinationDefaultDirectories
        Switch that tells the restore to use the default Data and Log locations on the target server. If they don't exist, the function will try to create them

    .PARAMETER ReuseSourceFolderStructure
        By default, databases will be migrated to the destination Sql Server's default data and log directories. You can override this by specifying -ReuseSourceFolderStructure.
        The same structure on the SOURCE will be kept exactly, so consider this if you're migrating between different versions and use part of Microsoft's default Sql structure (MSSql12.INSTANCE, etc)

        *Note, to reuse destination folder structure, specify -WithReplace

    .PARAMETER DestinationFilePrefix
        This value will be prefixed to ALL restored files (log and data). This is just a simple string prefix. If you want to perform more complex rename operations then please use the FileMapping parameter

        This will apply to all file move options, except for FileMapping

    .PARAMETER DestinationFileSuffix
        This value will be suffixed to ALL restored files (log and data). This is just a simple string suffix. If you want to perform more complex rename operations then please use the FileMapping parameter

        This will apply to all file move options, except for FileMapping

    .PARAMETER RestoredDatabaseNamePrefix
        A string which will be prefixed to the start of the restore Database's Name
        Useful if restoring a copy to the same sql server for testing.

    .PARAMETER TrustDbBackupHistory
        This switch can be used when piping the output of Get-DbaBackupHistory or Backup-DbaDatabase into this command.
        It allows the user to say that they trust that the output from those commands is correct, and skips the file header read portion of the process. This means a faster process, but at the risk of not knowing till halfway through the restore that something is wrong with a file.

    .PARAMETER MaxTransferSize
        Parameter to set the unit of transfer. Values must be a multiple by 64kb

    .PARAMETER Blocksize
        Specifies the block size to use. Must be one of 0.5kb,1kb,2kb,4kb,8kb,16kb,32kb or 64kb
        Can be specified in bytes
        Refer to https://msdn.microsoft.com/en-us/library/ms178615.aspx for more detail

    .PARAMETER BufferCount
        Number of I/O buffers to use to perform the operation.
        Refer to https://msdn.microsoft.com/en-us/library/ms178615.aspx for more detail

    .PARAMETER XpNoRecurse
        If specified, prevents the XpDirTree process from recursing (its default behaviour)

    .PARAMETER DirectoryRecurse
        If specified the specified directory will be recursed into

    .PARAMETER Continue
        If specified we will to attempt to recover more transaction log backups onto  database(s) in Recovering or Standby states

    .PARAMETER StandbyDirectory
        If a directory is specified the database(s) will be restored into a standby state, with the standby file placed into this directory (which must exist, and be writable by the target Sql Server instance)

    .PARAMETER AzureCredential
        The name of the SQL Server credential to be used if restoring from an Azure hosted backup

    .PARAMETER ReplaceDbNameInFile
        If switch set and occurence of the original database's name in a data or log file will be replace with the name specified in the Databasename parameter

    .PARAMETER Recover
        If set will perform recovery on the indicated database

    .PARAMETER AllowContinue
        By default, Restore-DbaDatabase will stop restoring any databases if it comes across an error.
        Use this switch to enable it to restore all databases without issues.

    .PARAMETER GetBackupInformation
        Passing a string value into this parameter will cause a global variable to be created holding the output of Get-DbaBackupInformation

    .PARAMETER SelectBackupInformation
        Passing a string value into this parameter will cause a global variable to be created holding the output of Select-DbaBackupInformation

    .PARAMETER FormatBackupInformation
        Passing a string value into this parameter will cause a global variable to be created holding the output of Format-DbaBackupInformation

    .PARAMETER TestBackupInformation
        Passing a string value into this parameter will cause a global variable to be created holding the output of Test-DbaBackupInformation

    .PARAMETER StopAfterGetBackupInformation
        Switch which will cause the function to exit after returning GetBackupInformation

    .PARAMETER StopAfterSelectBackupInformation
        Switch which will cause the function to exit after returning SelectBackupInformation

    .PARAMETER StopAfterFormatBackupInformation
         Switch which will cause the function to exit after returning FormatBackupInformation

    .PARAMETER StopAfterTestBackupInformation
         Switch which will cause the function to exit after returning TestBackupInformation

    .PARAMETER StatementTimeOut
        Timeout in minutes. Defaults to infinity (restores can take a while.)

    .PARAMETER KeepCDC
        Indicates whether CDC information should be restored as part of the database

    .PARAMETER PageRestore
        Passes in an object from Get-DbaSuspectPages containing suspect pages from a single database.
        Setting this Parameter will cause an Online Page restore if the target Instance is Enterprise Edition, or offline if not.
        This will involve taking a tail log backup, so you must check your restore chain once it has completed

    .PARAMETER PageRestoreTailFolder
        This parameter passes in a location for the tail log backup required for page level restore

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .PARAMETER Confirm
        Prompts to confirm certain actions

    .PARAMETER WhatIf
        Shows what would happen if the command would execute, but does not actually perform the command

    .EXAMPLE
        Restore-DbaDatabase -SqlInstance server1\instance1 -Path \\server2\backups

        Scans all the backup files in \\server2\backups, filters them and restores the database to server1\instance1

    .EXAMPLE
        Restore-DbaDatabase -SqlInstance server1\instance1 -Path \\server2\backups -MaintenanceSolutionBackup -DestinationDataDirectory c:\restores

        Scans all the backup files in \\server2\backups$ stored in an Ola Hallengren style folder structure,
        filters them and restores the database to the c:\restores folder on server1\instance1

    .EXAMPLE
        Get-ChildItem c:\SQLbackups1\, \\server\sqlbackups2 | Restore-DbaDatabase -SqlInstance server1\instance1

        Takes the provided files from multiple directories and restores them on  server1\instance1

    .EXAMPLE
        $RestoreTime = Get-Date('11:19 23/12/2016')
        Restore-DbaDatabase -SqlInstance server1\instance1 -Path \\server2\backups -MaintenanceSolutionBackup -DestinationDataDirectory c:\restores -RestoreTime $RestoreTime

        Scans all the backup files in \\server2\backups stored in an Ola Hallengren style folder structure,
        filters them and restores the database to the c:\restores folder on server1\instance1 up to 11:19 23/12/2016

    .EXAMPLE
        Restore-DbaDatabase -SqlInstance server1\instance1 -Path \\server2\backups -DestinationDataDirectory c:\restores -OutputScriptOnly | Select-Object -ExpandProperty Tsql | Out-File -Filepath c:\scripts\restore.sql

        Scans all the backup files in \\server2\backups stored in an Ola Hallengren style folder structure,
        filters them and generate the T-SQL Scripts to restore the database to the latest point in time,
        and then stores the output in a file for later retrieval

    .EXAMPLE
        Restore-DbaDatabase -SqlInstance server1\instance1 -Path c:\backups -DestinationDataDirectory c:\DataFiles -DestinationLogDirectory c:\LogFile

        Scans all the files in c:\backups and then restores them onto the SQL Server Instance server1\instance1, placing data files
        c:\DataFiles and all the log files into c:\LogFiles

    .EXAMPLE
        Restore-DbaDatabase -SqlInstance server1\instance1 -Path http://demo.blob.core.windows.net/backups/dbbackup.bak -AzureCredential MyAzureCredential

        Will restore the backup held at  http://demo.blob.core.windows.net/backups/dbbackup.bak to server1\instance1. The connection to Azure will be made using the
        credential MyAzureCredential held on instance Server1\instance1

    .EXAMPLE
        $File = Get-ChildItem c:\backups, \\server1\backups -recurse
        $File | Restore-DbaDatabase -SqlInstance Server1\Instance -useDestinationDefaultDirectories

        This will take all of the files found under the folders c:\backups and \\server1\backups, and pipeline them into
        Restore-DbaDatabase. Restore-DbaDatabase will then scan all of the files, and restore all of the databases included
        to the latest point in time covered by their backups. All data and log files will be moved to the default SQL Server
        folder for those file types as defined on the target instance.

    .EXAMPLE
        $files = Get-ChildItem C:\dbatools\db1

        #Restore database to a point in time
        $files | Restore-DbaDatabase -SqlInstance server\instance1 `
                    -DestinationFilePrefix prefix -DatabaseName Restored  `
                    -RestoreTime (get-date "14:58:30 22/05/2017") `
                    -NoRecovery -WithReplace -StandbyDirectory C:\dbatools\standby

        #It's in standby so we can peek at it
        Invoke-Sqlcmd2 -ServerInstance server\instance1 -Query "select top 1 * from Restored.dbo.steps order by dt desc"

        #Not quite there so let's roll on a bit:
        $files | Restore-DbaDatabase -SqlInstance server\instance1 `
                    -DestinationFilePrefix prefix -DatabaseName Restored `
                    -continue -WithReplace -RestoreTime (get-date "15:09:30 22/05/2017") `
                    -StandbyDirectory C:\dbatools\standby

        Invoke-Sqlcmd2 -ServerInstance server\instance1 -Query "select top 1 * from restored.dbo.steps order by dt desc"

        Restore-DbaDatabase -SqlInstance server\instance1 `
                    -DestinationFilePrefix prefix -DatabaseName Restored `
                    -continue -WithReplace

        In this example we step through the backup files held in c:\dbatools\db1 folder.
        First we restore the database to a point in time in standby mode. This means we can check some details in the databases
        We then roll it on a further 9 minutes to perform some more checks
        And finally we continue by rolling it all the way forward to the latest point in the backup.
        At each step, only the log files needed to roll the database forward are restored.

    .EXAMPLE
        Restore-DbaDatabase -SqlInstance server\instance1 -Path c:\backups -DatabaseName example1 -WithNoRecovery
        Restore-DbaDatabase -SqlInstance server\instance1 -Recover -DatabaseName example1

    .EXAMPLE
        $SuspectPage = Get-DbaSuspectPage -SqlInstance server\instance1 -Database ProdFinance
        Get-DbaBackupHistory - SqlInstance server\instance1 -Database -ProdFinance -Last | Restore-DbaDatabase -PageRestore $SuspectPage -PageRestoreTailFolder c:\temp -TrustDbBackupHistory -AllowContinues

        Gets a list of Suspect Pages using Get-DbaSuspectPage. The uses Get-DbaBackupHistory and Restore-DbaDatabase to perform a restore of the suspect pages and bring them up to date
        If server\instance1 is Enterprise edition this will be done online, if not it will be performed offline
        AllowContinue is required to make sure we cope with existing files

    .NOTES
        Tags: DisasterRecovery, Backup, Restore
        Author: Stuart Moore (@napalmgram), stuart-moore.com

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT
#>
    [CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = "Restore")]
    param (
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "Restore")]
        [parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "RestorePage")]
        [object[]]$Path,
        [parameter(ValueFromPipeline = $true)]
        [Alias("Name")]
        [object[]]$DatabaseName,
        [parameter(ParameterSetName = "Restore")]
        [String]$DestinationDataDirectory,
        [parameter(ParameterSetName = "Restore")]
        [String]$DestinationLogDirectory,
        [parameter(ParameterSetName = "Restore")]
        [String]$DestinationFileStreamDirectory,
        [parameter(ParameterSetName = "Restore")]
        [DateTime]$RestoreTime = (Get-Date).AddYears(1),
        [parameter(ParameterSetName = "Restore")]
        [switch]$NoRecovery,
        [parameter(ParameterSetName = "Restore")]
        [switch]$WithReplace,
        [parameter(ParameterSetName = "Restore")]
        [Switch]$XpDirTree,
        [switch]$OutputScriptOnly,
        [parameter(ParameterSetName = "Restore")]
        [switch]$VerifyOnly,
        [parameter(ParameterSetName = "Restore")]
        [switch]$MaintenanceSolutionBackup,
        [parameter(ParameterSetName = "Restore")]
        [hashtable]$FileMapping,
        [parameter(ParameterSetName = "Restore")]
        [switch]$IgnoreLogBackup,
        [parameter(ParameterSetName = "Restore")]
        [switch]$useDestinationDefaultDirectories,
        [parameter(ParameterSetName = "Restore")]
        [switch]$ReuseSourceFolderStructure,
        [parameter(ParameterSetName = "Restore")]
        [string]$DestinationFilePrefix = '',
        [parameter(ParameterSetName = "Restore")]
        [Alias("RestoredDatababaseNamePrefix")]
        [string]$RestoredDatabaseNamePrefix,
        [parameter(ParameterSetName = "Restore")]
        [parameter(ParameterSetName = "RestorePage")]
        [switch]$TrustDbBackupHistory,
        [parameter(ParameterSetName = "Restore")]
        [parameter(ParameterSetName = "RestorePage")]
        [int]$MaxTransferSize,
        [parameter(ParameterSetName = "Restore")]
        [parameter(ParameterSetName = "RestorePage")]
        [int]$BlockSize,
        [parameter(ParameterSetName = "Restore")]
        [parameter(ParameterSetName = "RestorePage")]
        [int]$BufferCount,
        [parameter(ParameterSetName = "Restore")]
        [switch]$DirectoryRecurse,
        [switch]$EnableException ,
        [parameter(ParameterSetName = "Restore")]
        [string]$StandbyDirectory,
        [parameter(ParameterSetName = "Restore")]
        [switch]$Continue,
        [string]$AzureCredential,
        [parameter(ParameterSetName = "Restore")]
        [switch]$ReplaceDbNameInFile,
        [parameter(ParameterSetName = "Restore")]
        [string]$DestinationFileSuffix,
        [parameter(ParameterSetName = "Recovery")]
        [switch]$Recover,
        [parameter(ParameterSetName = "Restore")]
        [switch]$KeepCDC,
        [switch]$AllowContinue,
        [string]$GetBackupInformation,
        [switch]$StopAfterGetBackupInformation,
        [string]$SelectBackupInformation,
        [switch]$StopAfterSelectBackupInformation,
        [string]$FormatBackupInformation,
        [switch]$StopAfterFormatBackupInformation,
        [string]$TestBackupInformation,
        [switch]$StopAfterTestBackupInformation,
        [parameter(Mandatory = $true, ParameterSetName = "RestorePage")]
        [object]$PageRestore,
        [parameter(Mandatory = $true, ParameterSetName = "RestorePage")]
        [string]$PageRestoreTailFolder,
        [int]$StatementTimeout = 0

    )
    begin {
        Write-Message -Level InternalComment -Message "Starting"
        Write-Message -Level Debug -Message "Parameters bound: $($PSBoundParameters.Keys -join ", ")"
        #[string]$DatabaseName = 'testparam'
        #region Validation
        try {
            $RestoreInstance = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            return
        }
        if ($PSCmdlet.ParameterSetName -eq "Restore") {
            $useDestinationDefaultDirectories = $true
            $paramCount = 0

            if (!(Test-Bound "AllowContinue") -and $true -ne $AllowContinue) {
                $AllowContinue = $false
            }
            if (Test-Bound "FileMapping") {
                $paramCount += 1
            }
            if (Test-Bound "ReuseSourceFolderStructure") {
                $paramCount += 1
            }
            if (Test-Bound "DestinationDataDirectory") {
                $paramCount += 1
            }
            if ($paramCount -gt 1) {
                Stop-Function -Category InvalidArgument -Message "You've specified incompatible Location parameters. Please only specify one of FileMapping, ReuseSourceFolderStructure or DestinationDataDirectory"
                return
            }
            if (($ReplaceDbNameInFile) -and !(Test-Bound "DatabaseName")) {
                Stop-Function -Category InvalidArgument -Message "To use ReplaceDbNameInFile you must specify DatabaseName"
                return
            }

            if ((Test-Bound "DestinationLogDirectory") -and (Test-Bound "ReuseSourceFolderStructure")) {
                Stop-Function -Category InvalidArgument -Message "The parameters DestinationLogDirectory and UseDestinationDefaultDirectories are mutually exclusive"
                return
            }
            if ((Test-Bound "DestinationLogDirectory") -and -not (Test-Bound "DestinationDataDirectory")) {
                Stop-Function -Category InvalidArgument -Message "The parameter DestinationLogDirectory can only be specified together with DestinationDataDirectory"
                return
            }
            if ((Test-Bound "DestinationFileStreamDirectory") -and (Test-Bound "ReuseSourceFolderStructure")) {
                Stop-Function -Category InvalidArgument -Message "The parameters DestinationFileStreamDirectory and UseDestinationDefaultDirectories are mutually exclusive"
                return
            }
            if ((Test-Bound "DestinationFileStreamDirectory") -and -not (Test-Bound "DestinationDataDirectory")) {
                Stop-Function -Category InvalidArgument -Message "The parameter DestinationFileStreamDirectory can only be specified together with DestinationDataDirectory"
                return
            }
            if (($null -ne $FileMapping) -or $ReuseSourceFolderStructure -or ($DestinationDataDirectory -ne '')) {
                $useDestinationDefaultDirectories = $false
            }
            if (($MaxTransferSize % 64kb) -ne 0 -or $MaxTransferSize -gt 4mb) {
                Stop-Function -Category InvalidArgument -Message "MaxTransferSize value must be a multiple of 64kb and no greater than 4MB"
                return
            }
            if ($BlockSize) {
                if ($BlockSize -notin (0.5kb, 1kb, 2kb, 4kb, 8kb, 16kb, 32kb, 64kb)) {
                    Stop-Function -Category InvalidArgument -Message "Block size must be one of 0.5kb,1kb,2kb,4kb,8kb,16kb,32kb,64kb"
                    return
                }
            }
            if ('' -ne $StandbyDirectory) {
                if (!(Test-DbaSqlPath -Path $StandbyDirectory -SqlInstance $RestoreInstance)) {
                    Stop-Function -Message "$SqlSever cannot see the specified Standby Directory $StandbyDirectory" -Target $SqlInstance
                    return
                }
            }
            if ($KeepCDC -and ($NoRecovery -or ('' -ne $StandbyDirectory))) {
                Stop-Function -Category InvalidArgument -Message "KeepCDC cannot be specified with Norecovery or Standby as it needs recovery to work"
                return
            }
            if ($Continue) {
                $ContinuePoints = Get-RestoreContinuableDatabase -SqlInstance $RestoreInstance
                #$WithReplace = $true
                #$ContinuePoints
            }
            if (!($PSBoundParameters.ContainsKey("DataBasename"))) {
                $PipeDatabaseName = $true
            }

        }



        # changing statement timeout to $StatementTimeout
        if ($StatementTimeout -eq 0) {
            Write-Message -Level Verbose -Message "Changing statement timeout to infinity"
        }
        else {
            Write-Message -Level Verbose -Message "Changing statement timeout to ($StatementTimeout) minutes"
        }
        $RestoreInstance.ConnectionContext.StatementTimeout = ($StatementTimeout * 60)
        #endregion Validation

        $isLocal = [dbavalidate]::IsLocalHost($SqlInstance.ComputerName)

        if ($useDestinationDefaultDirectories) {
            $DefaultPath = (Get-DbaDefaultPath -SqlInstance $RestoreInstance)
            $DestinationDataDirectory = $DefaultPath.Data
            $DestinationLogDirectory = $DefaultPath.Log
        }

        $BackupHistory = @()
        #$useDestinationDefaultDirectories = $true
    }
    process {
        if (Test-FunctionInterrupt) { return }
        if ($PSCmdlet.ParameterSetName -like "Restore*") {
            if ($PipeDatabaseName -eq $true) {$DatabaseName = ''}
            Write-Message -message "ParameterSet  = Restore" -Level Verbose
            if ($TrustDbBackupHistory -or $path[0].GetType().ToString() -eq 'Sqlcollaborative.Dbatools.Database.BackupHistory') {
                foreach ($f in $path) {
                    Write-Message -Level Verbose -Message "Trust Database Backup History Set"
                    if ("BackupPath" -notin $f.PSobject.Properties.name) {
                        Write-Message -Level Verbose -Message "adding BackupPath - $($_.Fullname)"
                        $f = $f | Select-Object *, @{ Name = "BackupPath"; Expression = { $_.FullName } }
                    }
                    if ("DatabaseName" -notin $f.PSobject.Properties.name) {
                        $f = $f | Select-Object *, @{ Name = "DatabaseName"; Expression = { $_.Database } }
                    }
                    if ("Database" -notin $f.PSobject.Properties.name) {
                        $f = $f | Select-Object *, @{ Name = "Database"; Expression = { $_.DatabaseName } }
                    }
                    if ("Type" -notin $f.PSobject.Properties.name) {
                        #$f = $f | Select-Object *,  @{Name="Type";Expression={"Full"}}
                    }
                    if ("BackupSetGUID" -notin $f.PSobject.Properties.name) {
                        #This line until Get-DbaBackupHistory gets fixed
                        #$f = $f | Select-Object *, @{ Name = "BackupSetGUID"; Expression = { $_.BackupSetupID } }
                        #This one once it's sorted:
                        $f = $f | Select-Object *, @{Name = "BackupSetGUID"; Expression = {$_.BackupSetID}}
                    }
                    if ($f.BackupPath -like 'http*' -and '' -eq $AzureCredential) {
                        Stop-Function -Message "At least one Azure backup passed in, and no Credential supplied. Stopping"
                        return
                    }

                    $BackupHistory += $F | Select-Object *, @{ Name = "ServerName"; Expression = { $_.SqlInstance } }, @{ Name = "BackupStartDate"; Expression = { $_.Start -as [DateTime] } }

                }
            }
            else {
                $files = @()
                foreach ($f in $Path) {
                    if ($f -is [System.IO.FileSystemInfo]) {
                        $files += $f.fullname
                    }
                    else {
                        $files += $f
                    }
                }
                Write-Message -Level Verbose -Message "Unverified input, full scans - $($files -join ';')"
                $BackupHistory += Get-DbaBackupInformation -SqlInstance $RestoreInstance -SqlCredential $SqlCredential -Path $files -DirectoryRecurse:$DirectoryRecurse -MaintenanceSolution:$MaintenanceSolutionBackup -IgnoreLogBackup:$IgnoreLogBackup -AzureCredential $AzureCredential
            }
            if ($PSCmdlet.ParameterSetName -eq "RestorePage") {
                if (-not (Test-DbaSqlPath -SqlInstance $RestoreInstance -Path $PageRestoreTailFolder)) {
                    Stop-Function -Message "Instance $RestoreInstance cannot read $PageRestoreTailFolder, cannot proceed" -Target $PageRestoreTailFolder
                    return
                }
                $WithReplace = $true
            }
        }
        elseif ($PSCmdlet.ParameterSetName -eq "Recovery") {
            Write-Message -Message "$($Database.count) databases to recover" -level Verbose
            ForEach ($DataBase in $DatabaseName) {
                if ($database -is [object]) {
                    #We've got an object, try the normal options Database, DatabaseName, Name
                    if ("Database" -in $Database.PSobject.Properties.name) {
                        [string]$DataBase = $database.Database
                    }
                    elseif ("DatabaseName" -in $Database.PSobject.Properties.name) {
                        [string]$DataBase = $database.DatabaseName
                    }
                    elseif ("Name" -in $Database.PSobject.Properties.name) {
                        [string]$DataBase = $database.name
                    }
                }
                Write-Verbose "existence - $($RestoreInstance.Databases[$DataBase].State)"
                if ($RestoreInstance.Databases[$DataBase].State -ne 'Existing') {
                    Write-Message -Message "$Database does not exist on $RestoreInstance" -level Warning
                    Continue

                }
                if ($RestoreInstance.Databases[$Database].Status -ne "Restoring") {
                    Write-Message -Message "$Database on $RestoreInstance is not in a Restoring State" -Level Warning
                    Continue

                }
                $RestoreComplete = $true
                $RecoverSql = "RESTORE DATABASE $Database WITH RECOVERY"
                Write-Message -Message "Recovery Sql Query - $RecoverSql" -level verbose
                Try {
                    $RestoreInstance.query($RecoverSql)
                }
                Catch {
                    $RestoreComplete = $False
                    $ExitError = $_.Exception.InnerException
                    Write-Message -Level Warning -Message "Failed to recover $Database on $RestoreInstance, `n $ExitError"
                }
                Finally {
                    [PSCustomObject]@{
                        SqlInstance     = $SqlInstance
                        DatabaseName    = $Database
                        RestoreComplete = $RestoreComplete
                        Scripts         = $RecoverSql
                    }
                }
            }
        }
    }
    end {
        if (Test-FunctionInterrupt) { return }
        if ($PSCmdlet.ParameterSetName -like "Restore*") {
            if ($BackupHistory.Count -eq 0) {
                Write-Message -Level Warning -Message "No backups passed through. `n This could mean the SQL instance cannot see the referenced files, the file's headers could not be read or some other issue"
                return
            }
            Write-Message -message "Processing DatabaseName - $DatabaseName" -Level Verbose
            $FilteredBackupHistory = @()
            if (Test-Bound -ParameterName GetBackupInformation) {
                Write-Message -Message "Setting $GetBackupInformation to BackupHistory" -Level Verbose
                Set-Variable -Name $GetBackupInformation -Value $BackupHistory -Scope Global
            }
            if ($StopAfterGetBackupInformation) {
                return
            }
            
            $FilteredBackupHistory = $BackupHistory | Select-DbaBackupInformation -RestoreTime $RestoreTime -IgnoreLogs:$IgnoreLogBackups -ContinuePoints $ContinuePoints
            
            if (Test-Bound -ParameterName SelectBackupInformation) {
                Write-Message -Message "Setting $SelectBackupInformation to FilteredBackupHistory" -Level Verbose
                Set-Variable -Name $SelectBackupInformation -Value $FilteredBackupHistory -Scope Global
                
            }
            if ($StopAfterSelectBackupInformation) {
                return
            }
            
            $null = $FilteredBackupHistory | Format-DbaBackupInformation -DataFileDirectory $DestinationDataDirectory -LogFileDirectory $DestinationLogDirectory -DestinationFileStreamDirectory $DestinationFileStreamDirectory -DatabaseFileSuffix $DestinationFileSuffix -DatabaseFilePrefix $DestinationFilePrefix -DatabaseNamePrefix $RestoredDatabaseNamePrefix -ReplaceDatabaseName $DatabaseName -Continue:$Continue -ReplaceDbNameInFile:$ReplaceDbNameInFile -FileMapping $FileMapping
            
            if (Test-Bound -ParameterName FormatBackupInformation) {
                Set-Variable -Name $FormatBackupInformation -Value $FilteredBackupHistory -Scope Global
            }
            if ($StopAfterFormatBackupInformation) {
                return
            }
            
            try {
                Write-Message -Level Verbose -Message "VerifyOnly = $VerifyOnly"
                $null = $FilteredBackupHistory | Test-DbaBackupInformation -SqlInstance $RestoreInstance -WithReplace:$WithReplace -Continue:$Continue -VerifyOnly:$VerifyOnly -EnableException:$true
            }
            catch {
                Stop-Function -ErrorRecord $_ -Message "Failure" -Continue
            }
            
            if (Test-Bound -ParameterName TestBackupInformation) {
                Set-Variable -Name $TestBackupInformation -Value $FilteredBackupHistory -Scope Global
            }
            if ($StopAfterTestBackupInformation) {
                return
            }
            $DbVerfied = ($FilteredBackupHistory | Where-Object { $_.IsVerified -eq $True } | Select-Object -Property Database -Unique).Database -join ','
            Write-Message -Message "$DbVerfied passed testing" -Level Verbose
            
            if (($FilteredBackupHistory | Where-Object { $_.IsVerified -eq $True }).count -lt $FilteredBackupHistory.count) {
                $DbUnVerified = ($FilteredBackupHistory | Where-Object { $_.IsVerified -eq $False } | Select-Object -Property Database -Unique).Database -join ','
                if ($AllowContinue) {
                    Write-Message -Message "$DbUnverified failed testing, AllowContinue set" -Level Verbose
                }
                else {
                    Stop-Function -Message "Database $DbUnverified failed testing, AllowContinue not set, exiting"
                    return
                }
            }
            
            If ($PSCmdlet.ParameterSetName -eq "RestorePage") {
                if (($FilteredBackupHistory.Database | select-Object -unique | Measure-Object).count -ne 1) {
                    Stop-Function -Message "Must only 1 database passed in for Page Restore. Sorry"
                    return
                }
                else {
                    $WithReplace = $false
                    $PageDb = ($FilteredBackupHistory.Database | select-Object -unique).Database
                }
            }
            Write-Message -Message "Passing in to restore" -Level Verbose
            if ($PSCmdlet.ParameterSetName -eq "RestorePage" -and $RestoreInstance.Edition -notlike '*Enterprise*') {
                Write-Message -Message "Taking Tail log backup for page restore for non-Enterprise" -Level Verbose
                $TailBackup = Backup-DbaDatabase -SqlInstance $RestoreInstance -Database $DatabaseName -Type Log -BackupDirectory $PageRestoreTailFolder -Norecovery -CopyOnly
            }
            try {
                $FilteredBackupHistory | Where-Object { $_.IsVerified -eq $true } | Invoke-DbaAdvancedRestore -SqlInstance $RestoreInstance -WithReplace:$WithReplace -RestoreTime $RestoreTime -StandbyDirectory $StandbyDirectory -NoRecovery:$NoRecovery -Continue:$Continue -OutputScriptOnly:$OutputScriptOnly -BlockSize $BlockSize -MaxTransferSize $MaxTransferSize -Buffercount $Buffercount -KeepCDC:$KeepCDC -VerifyOnly:$VerifyOnly -PageRestore $PageRestore -EnableException -AzureCredential $AzureCredential
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_ -Continue -Target $RestoreInstance
            }
            if ($PSCmdlet.ParameterSetName -eq "RestorePage" ) {
                if ($RestoreInstace.Edition -like '*Enterprise*') {
                    Write-Message -Message "Taking Tail log backup for page restore for Enterprise" -Level Verbose
                    $TailBackup = Backup-DbaDatabase -SqlInstance $RestoreInstance -Database $DatabaseName -Type Log -BackupDirectory $PageRestoreTailFolder -Norecovery -CopyOnly
                }
                Write-Message -Message "Restoring Tail log backup for page restore" -Level Verbose
                $TailBackup | Restore-DbaDatabase -SqlInstance $RestoreInstance -TrustDbBackupHistory -NoRecovery -OutputScriptOnly:$OutputScriptOnly -BlockSize $BlockSize -MaxTransferSize $MaxTransferSize -Buffercount $Buffercount -Continue
                Restore-DbaDatabase -SqlInstance $RestoreInstance -Recover -Database $DatabaseName -OutputScriptOnly:$OutputScriptOnly
            }

        }
    }
}
tools\dbatools\functions\Restore-DbaDbCertificate.ps1
function Restore-DbaDbCertificate {
    <#
.SYNOPSIS
Imports certificates from .cer files using SMO.

.DESCRIPTION
Imports certificates from.cer files using SMO.

.PARAMETER SqlInstance
The SQL Server to create the certificates on.

.PARAMETER Path
The Path the contains the certificate and private key files. The path can be a directory or a specific certificate.

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Password
Secure string used to decrypt the private key.

.PARAMETER Database
The database where the certificate imports into. Defaults to master.

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Tags: Migration, Certificate
Author: Jess Pomfret (@jpomfret)

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
Restore-DbaDbCertificate -SqlInstance Server1 -Path \\Server1\Certificates -password (ConvertTo-SecureString -force -AsPlainText GoodPass1234!!)
Imports all the certificates in the specified path.

#>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [object[]]$Path,
        [object]$Database = "master",
        [Security.SecureString]$Password = (Read-Host "Password" -AsSecureString),
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Retore-DbaDatabaseCertificate

        function new-smocert ($directory, $certname) {
            if ($Pscmdlet.ShouldProcess("$cert on $SqlInstance", "Importing Certificate")) {
                $smocert = New-Object Microsoft.SqlServer.Management.Smo.Certificate
                $smocert.Name = $certname
                $smocert.Parent = $server.Databases[$Database]
                Write-Message -Level Verbose -Message "Creating Certificate: $certname"
                try {
                    $fullcertname = "$directory\$certname.cer"
                    $privatekey = "$directory\$certname.pvk"
                    Write-Message -Level Verbose -Message "Full certificate path: $fullcertname"
                    Write-Message -Level Verbose -Message "Private key: $privatekey"
                    $fromfile = 1
                    $smocert.Create($fullcertname, $fromfile, $privatekey, [System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($password)), [System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($password)))
                    $smocert
                }
                catch {
                    Write-Message -Level Warning -Message $_ -ErrorRecord $_ -Target $instance
                }
            }
        }

        try {
            Write-Message -Level Verbose -Message "Connecting to $SqlInstance"
            $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $sqlcredential
        }
        catch {
            Stop-Function -Message "Failed to connect to: $SqlInstance" -Target $SqlInstance -InnerErrorRecord $_
            return
        }
    }

    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($fullname in $path) {

            if (-not $SqlInstance.IsLocalHost -and -not $fullname.StartsWith('\')) {
                Stop-Function -Message "Path ($fullname) must be a UNC share when SQL instance is not local." -Continue -Target $fullname
            }

            if (-not (Test-DbaSqlPath -SqlInstance $server -Path $fullname)) {
                Stop-Function -Message "$SqlInstance cannot access $fullname" -Continue -Target $fullname
            }

            $directory = Split-Path $fullname
            $filename = Split-Path $fullname -Leaf
            $basename = [io.path]::GetFileNameWithoutExtension($filename)
            $cert = new-smocert -directory $directory -certname $basename
            Get-DbaDbCertificate -SqlInstance $server -Database $Database -Certificate $cert.Name
        }
    }
}
tools\dbatools\functions\Restore-DbaDbSnapshot.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Restore-DbaDbSnapshot {
    <#
    .SYNOPSIS
        Restores databases from snapshots

    .DESCRIPTION
        Restores the database from the snapshot, discarding every modification made to the database
        NB: Restoring to a snapshot will result in every other snapshot of the same database to be dropped
        It also fixes some long-standing bugs in SQL Server when restoring from snapshots

    .PARAMETER SqlInstance
        The SQL Server that you're connecting to

    .PARAMETER SqlCredential
        Credential object used to connect to the SQL Server as a different user

    .PARAMETER Database
        Restores from the last snapshot databases with this names only. You can pass either Databases or Snapshots

    .PARAMETER ExcludeDatabase
        The database(s) to exclude - this list is auto-populated from the server

    .PARAMETER Snapshot
        Restores databases from snapshots with this names only. You can pass either Databases or Snapshots

    .PARAMETER InputObject
        Allows piping from other Snapshot commands

    .PARAMETER Force
        If restoring from a snapshot involves dropping any other shapshot, you need to explicitly
        use -Force to let this command delete the ones not involved in the restore process.
        Also, -Force will forcibly kill all running queries that prevent the restore process.

    .PARAMETER WhatIf
        Shows what would happen if the command were to run

    .PARAMETER Confirm
        Prompts for confirmation of every step.

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Tags: Snapshot, Backup, Restore, Database
        Author: niphlod

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Restore-DbaDbSnapshot

    .EXAMPLE
        Restore-DbaDbSnapshot -SqlInstance sql2014 -Database HR, Accounting

        Restores HR and Accounting databases using the latest snapshot available

    .EXAMPLE
        Restore-DbaDbSnapshot -SqlInstance sql2014 -Database HR -Force

        Restores HR database from latest snapshot and kills any active connections in the database on sql2014.

    .EXAMPLE
        Get-DbaDbSnapshot -SqlInstance sql2016 -Database HR | Restore-DbaDbSnapshot -Force

        Restores HR database from latest snapshot and kills any active connections in the database on sql2016.

    .EXAMPLE
        Get-DbaDbSnapshot -SqlInstance sql2016 | Out-GridView -Passthru | Restore-DbaDbSnapshot

        Allows the selection of snapshots on sql2016 to restore

    .EXAMPLE
        Restore-DbaDbSnapshot -SqlInstance sql2014 -Snapshot HR_snap_20161201, Accounting_snap_20161101

        Restores databases from snapshots named HR_snap_20161201 and Accounting_snap_20161101
    #>
    [CmdletBinding(SupportsShouldProcess)]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [object[]]$Snapshot,
        [Parameter(ValueFromPipeline)]
        [Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        if (-not $Snapshot -and -not $Database -and -not $ExcludeDatabase -and -not $InputObject) {
            Stop-Function -Message "You must specify either -Snapshot (to restore from) or -Database/-ExcludeDatabase (to restore to) or pipe in a snapshot"
            return
        }

        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $InputObject += Get-DbaDbSnapshot -SqlInstance $server -Database $Database -ExcludeDatabase $ExcludeDatabase -Snapshot $Snapshot | Sort-Object CreateDate -Descending

            if ($Snapshot) {
                # Restore databases from these snapshots
                Write-Message -Level Verbose -Message "Selected only snapshots"
                $dbs = $InputObject | Where-Object { $Snapshot -contains $_.Name }
                $baseDatabases = $dbs | Select-Object -ExpandProperty DatabaseSnapshotBaseName | Get-Unique
                if ($baseDatabases.Count -ne $Snapshot.Count -and $dbs.Count -ne 0) {
                    Stop-Function -Message "Failure. Multiple snapshots selected for the same database" -Continue
                }
            }
        }

        foreach ($snap in $InputObject) {
            # In the event someone passed -Database and it got all the snaps, most of which were dropped by the first
            if ($snap.Parent) {
                $server = $snap.Parent

                if (-not $snap.IsDatabaseSnapshot) {
                    Stop-Function -Continue -Message "$snap on $server is not a valid snapshot"
                }

                if (-not ($snap.IsAccessible)) {
                    Stop-Function -Message "Database $snap is not accessible on $($snap.Parent)." -Continue
                }

                $othersnaps = $server.Databases | Where-Object { $_.DatabaseSnapshotBaseName -eq $snap.DatabaseSnapshotBaseName -and $_.Name -ne $snap.Name }

                $db = $server.Databases | Where-Object Name -eq $snap.DatabaseSnapshotBaseName
                $loginfo = $db.LogFiles | Select-Object Id, Size, Growth, GrowthType

                if (($snap | Where-Object FileGroupType -eq 'FileStreamDataFileGroup')) {
                    Stop-Function -Message "Database $snap on $server has FileStream group(s). You cannot restore from snapshots" -Continue
                }

                if ($othersnaps -and -not $force) {
                    Stop-Function -Message "The restore process for $db from $snap needs to drop other snapshots on $db. Use -Force if you want to drop these snapshots" -Continue
                }

                if ($Pscmdlet.ShouldProcess($server, "Remove other db snapshots for $db")) {
                    try {
                        $null = $othersnaps | Remove-DbaDatabase -Confirm:$false -EnableException
                    }
                    catch {
                        Stop-Function -Message "Failed to remove other snapshots for $db on $server" -ErrorRecord $_ -Continue
                    }
                }

                # Need a proper restore now
                if ($Pscmdlet.ShouldProcess($server, "Restore db $db from $snap")) {
                    try {
                        if ($Force) {
                            $null = Stop-DbaProcess -SqlInstance $server -Database $db.Name, $snap.Name -WarningAction SilentlyContinue
                        }

                        $null = $server.Query("USE master; RESTORE DATABASE [$($db.Name)] FROM DATABASE_SNAPSHOT='$($snap.Name)'")
                    }
                    catch {
                        Stop-Function -Message "Failiure attempting to restore $db on $server" -ErrorRecord $_ -Continue
                    }
                }

                # Comparing sizes before and after, need to refresh to see if size
                foreach ($log in $db.LogFiles) {
                    $log.Refresh()
                }

                foreach ($log in $db.LogFiles) {
                    $matching = $loginfo | Where-Object ID -eq $log.ID
                    $changeflag = 0
                    foreach ($prop in @('Size', 'Growth', 'Growth', 'GrowthType')) {
                        if ($matching.$prop -ne $log.$prop) {
                            $changeflag = 1
                            $log.$prop = $matching.$prop
                        }
                    }
                    if ($changeflag -ne 0) {
                        Write-Message -Level Verbose -Message "Restoring original settings for log file"
                        $log.Alter()
                    }
                }

                Write-Message -Level Verbose -Message "Restored. Remember to take a backup now, and also to remove the snapshot if not needed."
                Get-DbaDatabase -SqlInstance $server -Database $db.Name
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Restore-DbaFromDatabaseSnapshot
    }
}
tools\dbatools\functions\Save-DbaDiagnosticQueryScript.ps1
function Save-DbaDiagnosticQueryScript {
    <#
.SYNOPSIS
Save-DbaDiagnosticQueryScript downloads the most recent version of all Glenn Berry DMV scripts

.DESCRIPTION
The dbatools module will have the diagnostic queries pre-installed. Use this only to update to a more recent version or specific versions.

This function is mainly used by Invoke-DbaDiagnosticQuery, but can also be used independently to download the Glenn Berry DMV scripts.

Use this function to pre-download the scripts from a device with an Internet connection.

The function Invoke-DbaDiagnosticQuery will try to download these scripts automatically, but it obviously needs an internet connection to do that.

.PARAMETER Path
Specifies the path to the output

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Author: André Kamman (@AndreKamman), http://clouddba.io
Tags: Diagnostic, DMV, Troubleshooting

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
Save-DbaDiagnosticQueryScript -Path c:\temp

Downloads the most recent version of all Glenn Berry DMV scripts to the specified location.
If Path is not specified, the "My Documents" location will be used.

#>
    [CmdletBinding()]
    param (
        [System.IO.FileInfo]$Path = [Environment]::GetFolderPath("mydocuments"),
        [Alias('Silent')]
        [switch]$EnableException
    )
    function Get-WebData {
        param ($uri)
        try {
            $data = (Invoke-WebRequest -uri $uri -ErrorAction Stop)
            return $data
        }
        catch {
            Stop-Function -Message "Invoke-WebRequest failed: $_" -Target $data -ErrorRecord $_
            return
        }
    }

    if (-not (Test-Path $Path)) {
        Stop-Function -Message "Path does not exist or access denied" -Target $path
        return
    }

    Add-Type -AssemblyName System.Web
    $glenberryrss = "http://www.sqlskills.com/blogs/glenn/feed/"
    $glenberrysql = @()

    Write-Message -Level Output -Message "Downloading RSS Feed"
    $rss = [xml](get-webdata -uri $glenberryrss)
    $Feed = $rss.rss.Channel

    $glenberrysql = @()
    $RssPostFilter = "SQL Server Diagnostic Information Queries for*"
    $DropboxLinkFilter = "*dropbox.com*"
    $LinkTitleFilter = "*Diagnostic*"

    foreach ($post in $Feed.item) {
        if ($post.title -like $RssPostFilter) {
            # We found the first post that matches it, lets go visit and scrape.
            $page = Get-WebData -uri $post.link
            $glenberrysql += ($page.Links | Where-Object { $_.href -like $DropboxLinkFilter -and $_.innerText -like $LinkTitleFilter } | ForEach-Object {
                    [pscustomobject]@{
                        URL        = $_.href
                        SQLVersion = $_.innerText -replace " Diagnostic Information Queries", "" -replace "SQL Server ", "" -replace ' ', ''
                        FileYear   = ($post.title -split " ")[-1]
                        FileMonth  = "{0:00}" -f [int]([CultureInfo]::InvariantCulture.DateTimeFormat.MonthNames.IndexOf(($post.title -split " ")[-2]))
                    }
                })
            break
        }
    }
    Write-Message -Level Output -Message "Found $($glenberrysql.Count) documents to download"
    foreach ($doc in $glenberrysql) {
        try {
            Write-Message -Level Output -Message "Downloading $($doc.URL)"
            $filename = "{0}\SQLServerDiagnosticQueries_{1}_{2}.sql" -f $Path, $doc.SQLVersion, "$($doc.FileYear)$($doc.FileMonth)"
            Invoke-WebRequest -Uri $doc.URL -OutFile $filename -ErrorAction Stop
        }
        catch {
            Stop-Function -Message "Requesting and writing file failed: $_" -Target $filename -ErrorRecord $_
            return
        }
    }
}
tools\dbatools\functions\Select-DbaBackupInformation.ps1
function Select-DbaBackupInformation {
    <#
        .SYNOPSIS
            Select a subset of backups from a dbatools backup history object

        .DESCRIPTION
        Select-DbaBackupInformation filters out a subset of backups from the dbatools backup history object with parameters supplied.

        .PARAMETER BackupHistory
            A dbatools.BackupHistory object containing backup history records

        .PARAMETER RestoreTime
            The point in time you want to restore to

        .PARAMETER IgnoreLogs
            This switch will cause Log Backups to be ignored. So will restore to the last Full or Diff backup only

        .PARAMETER IgnoreDiffs
            This switch will cause Differential backups to be ignored. Unless IgnoreLogs is specified, restore to point in time will still occur, just using all available log backups

        .PARAMETER DatabaseName
            A string array of Database Names that you want to filter to

        .PARAMETER ServerName
            A string array of Server Names that you want to filter

        .PARAMETER ContinuePoints
            The Output of Get-RestoreContinuableDatabase while provides 'Database',redo_start_lsn,'FirstRecoveryForkID' values. Used to filter backups to continue a restore on a database
            Sets IgnoreDiffs, and also filters databases to only those within the ContinuePoints object, or the ContinuePoints object AND DatabaseName if both specified

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Backup, Restore
            Author:Stuart Moore (@napalmgram stuart-moore.com )

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Select-DbaBackupInformation

        .EXAMPLE
            $Backups = Get-DbaBackupInformation -SqlInstance Server1 -Path \\server1\backups$
            $FilteredBackups = $Backups | Select-DbaBackupInformation -RestoreTime (Get-Date).AddHours(-1)

            Returns all backups needed to restore all the backups in \\server1\backups$ to 1 hour ago

        .EXAMPLE
            $Backups = Get-DbaBackupInformation -SqlInstance Server1 -Path \\server1\backups$
            $FilteredBackups = $Backups | Select-DbaBackupInformation -RestoreTime (Get-Date).AddHours(-1) -DatabaseName ProdFinance

            Returns all the backups needed to restore Database ProdFinance to an hour ago

        .EXAMPLE
            $Backups = Get-DbaBackupInformation -SqlInstance Server1 -Path \\server1\backups$
            $FilteredBackups = $Backups | Select-DbaBackupInformation -RestoreTime (Get-Date).AddHours(-1) -IgnoreLogs

            Returns all the backups in \\server1\backups$ to restore to as close prior to 1 hour ago as can be managed with only full and differential backups

        .EXAMPLE
            $Backups = Get-DbaBackupInformation -SqlInstance Server1 -Path \\server1\backups$
            $FilteredBackups = $Backups | Select-DbaBackupInformation -RestoreTime (Get-Date).AddHours(-1) -IgnoreDiffs

            Returns all the backups in \\server1\backups$ to restore to 1 hour ago using only Full and Diff backups.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [object]$BackupHistory,
        [DateTime]$RestoreTime = (get-date).addmonths(1),
        [switch]$IgnoreLogs,
        [switch]$IgnoreDiffs,
        [string[]]$DatabaseName,
        [string[]]$ServerName,
        [object]$ContinuePoints,
        [switch]$EnableException
    )
    begin {
        $InternalHistory = @()

        if ((Test-Bound -ParameterName ContinuePoints) -and $null -ne $ContinuePoints) {
            Write-Message -Message "ContinuePoints provided so setting up for a continue" -Level Verbose

            $IgnoreDiffs = $true
            $IgnoreFull = $true
            if (Test-Bound -ParameterName DatabaseName) {
                $DatabaseName = $DatabaseName | Where-Object {$_ -in ($ContinuePoints | Select-Object -Property Database).Database}

                $DroppedDatabases = $DatabaseName | Where-Object {$_ -notin ($ContinuePoints | Select-Object -Property Database).Database}
                if ($null -ne $DroppedDatabases) {
                    Write-Message -Message "$($DroppedDatabases.join(',')) filtered out as not in ContinuePoints" -Level Verbose
                }
            }
            else {
                $DatabaseName = ($ContinuePoints | Select-Object -Property Database).Database
            }
        }
    }
    process {
        $internalHistory += $BackupHistory
    }

    end {
        ForEach ($History in $InternalHistory) {
            if ("RestoreTime" -notin $History.PSobject.Properties.name) {
                $History | Add-Member -Name 'RestoreTime' -Type NoteProperty -Value $RestoreTime
            }
        }
        if ((Test-Bound -ParameterName DatabaseName) -and '' -ne $DatabaseName) {
            Write-Message -Message "Filtering by DatabaseName" -Level Verbose
            $InternalHistory = $InternalHistory | Where-Object {$_.Database -in $DatabaseName}
        }
        if (Test-Bound -ParameterName ServerName) {
            Write-Message -Message "Filtering by ServerName" -Level Verbose
            $InternalHistory = $InternalHistory | Where-Object {$_.InstanceName -in $servername}
        }

        $Databases = ($InternalHistory | Select-Object -Property Database -unique).Database
        ForEach ($Database in $Databases) {
            Write-Message -Message "Processing Db $Database" -Level Verbose
            $DatabaseHistory = $InternalHistory | Where-Object {$_.Database -eq $Database}

            $dbHistory = @()
            #Find the Last Full Backup before RestoreTime
            if ($true -ne $IgnoreFull) {
                $Full = $DatabaseHistory | Where-Object {$_.Type -in ('Full', 'Database') -and $_.Start -le $RestoreTime} | Sort-Object -Property LastLsn -Descending | Select-Object -First 1
                $full.Fullname = ($DatabaseHistory | Where-Object {$_.Type -in ('Full', 'Database') -and $_.BackupSetID -eq $Full.BackupSetID}).Fullname
                $dbHistory += $full
            }
            #Find the Last diff between Full and RestoreTime
            if ($true -ne $IgnoreDiffs) {
                $Diff = $DatabaseHistory | Where-Object {$_.Type -in ('Differential', 'Database Differential') -and $_.Start -le $RestoreTime -and $_.DatabaseBackupLSN -eq $Full.CheckpointLSN} | Sort-Object -Property LastLsn -Descending | Select-Object -First 1
                if ($null -ne $Diff) {
                    $Diff.FullName = ($DatabaseHistory | Where-Object {$_.Type -in ('Differential', 'Database Differential') -and $_.BackupSetID -eq $diff.BackupSetID}).Fullname
                    $dbhistory += $Diff
                }
            }
            #Get All t-logs up to restore time
            if ($IgnoreFull -eq $true) {
                [bigint]$LogBaseLsn = ($ContinuePoints | Where-Object {$_.Database -eq $Database}).redo_start_lsn
                $FirstRecoveryForkID = ($ContinuePoints | Where-Object {$_.Database -eq $Database}).FirstRecoveryForkID
                Write-Message -Message "Continuing, setting fake LastLsn - $LogBaseLSN" -Level Verbose
            }
            else {
                Write-Message -Message "Setting LogBaseLSN" -Level Verbose
                [bigint]$LogBaseLsn = ($dbHistory | Sort-Object -Property LastLsn -Descending | select-object -First 1).lastLsn.ToString()
                $FirstRecoveryForkID = $Full.FirstRecoveryForkID
            }

            if ($true -ne $IgnoreLogs) {
                $FilteredLogs = $DatabaseHistory | Where-Object {$_.Type -in ('Log', 'Transaction Log') -and $_.Start -le $RestoreTime -and $_.LastLSN.ToString() -ge $LogBaseLsn -and $_.FirstLSN -ne $_.LastLSN}  | Sort-Object -Property LastLsn, FirstLsn
                $GroupedLogs = $FilteredLogs | Group-Object -Property LastLSN, FirstLSN
                ForEach ($Group in $GroupedLogs) {
                    $Log = $DatabaseHistory | Where-Object {$_.BackupSetID -eq $Group.group[0].BackupSetID} | select-object -First 1
                    $Log.FullName = ($DatabaseHistory | Where-Object {$_.BackupSetID -eq $Group.group[0].BackupSetID}).Fullname
                    $dbhistory += $Log
                    #$dbhistory += $DatabaseHistory | Where-Object {$_.BackupSetID -eq $Group.group[0].BackupSetID}
                }
                # Get Last T-log
                $dbHistory += $DatabaseHistory | Where-Object {$_.Type -in ('Log', 'Transaction Log') -and $_.End -ge $RestoreTime -and $_.DatabaseBackupLSN -eq $Full.CheckpointLSN} | Sort-Object -Property LastLsn, FirstLsn  | Select-Object -First 1
            }

            $dbHistory  #| Group-Object -Property BackupSetId # -Unique
        }
    }
}
tools\dbatools\functions\Select-DbaObject.ps1
function Select-DbaObject {
<#
    .SYNOPSIS
        Wrapper around Select-Object, extends property parameter.
    
    .DESCRIPTION
        Wrapper around Select-Object, extends property parameter.
        
        This function allows specifying in-line transformation of the properties specified without needing to use complex hashtables.
        Without removing the ability to specify just those hashtables.
        
        See the description of the Property parameter for an exhaustive list of legal notations.
    
    .PARAMETER InputObject
        The object(s) to select from.
    
    .PARAMETER Property
        The properties to select.
        - Supports hashtables, which will be passed through to Select-Object.
        - Supports renaming as it is possible in SQL: "Length AS Size" will select the Length property but rename it to size.
        - Supports casting to a specified type: "Address to IPAddress" or "Length to int". Uses PowerShell type-conversion.
        - Supports parsing numbers to sizes: "Length size GB:2" Converts numeric input (presumed to be bytes) to gigabyte with two decimals.
        Also supports toggling on Unit descriptors by adding another element: "Length size GB:2:1"
        - Supports selecting properties from objects in other variables: "ComputerName from VarName" (Will insert the property 'ComputerName' from variable $VarName)
        - Supports filtering when selecting from outside objects: "ComputerName from VarName where ObjectId = Id" (Will insert the property 'ComputerName' from the object in variable $VarName, whose ObjectId property is equal to the inputs Id property)
        
        Important:
        When using this command from another module (not script-files, those are fine), you do not have access to the variables in the calling module.
        In order to select from other variables using the 'from' call, you need to declare the variable global.
    
    .PARAMETER ExcludeProperty
        Properties to not list.
    
    .PARAMETER ExpandProperty
        Properties to expand.
    
    .PARAMETER Unique
        Do not list multiples of the same value.
    
    .PARAMETER Last
        Select the last n items.
    
    .PARAMETER First
        Select the first n items.
    
    .PARAMETER Skip
        Skip the first (or last if used with -Last) n items.
    
    .PARAMETER SkipLast
        Skip the last n items.
    
    .PARAMETER Wait
        Indicates that the cmdlet turns off optimization.Windows PowerShell runs commands in the order that they appear in the command pipeline and lets them generate all objects. By default, if you include a Select-Object command with the First or Index parameters in a command pipeline, Windows PowerShell stops the command that generates the objects as soon as the selected number of objects is generated.
    
    .PARAMETER Index
        Specifies an array of objects based on their index values. Enter the indexes in a comma-separated list.
    
    .PARAMETER ShowProperty
        Only the specified properties will be shown by default.
        Supersedes ShowExcludeProperty.
    
    .PARAMETER ShowExcludeProperty
        Hides the specified properties from the default display style of the output object.
        Is ignored if used together with ShowProperty.
    
    .PARAMETER TypeName
        Adds a typename to the selected object.
        Will automatically prefix the module.
    
    .EXAMPLE
        PS C:\> Get-ChildItem | Select-DbaObject Name, "Length as Size"
        
        Selects the properties Name and Length, renaming Length to Size in the process.
    
    .EXAMPLE
        PS C:\> Import-Csv .\file.csv | Select-DbaObject Name, "Length as Size to DbaSize"
        
        Selects the properties Name and Length, renaming Length to Size and converting it to [DbaSize] (a userfriendly representation of size numbers)
    
    .EXAMPLE
        PS C:\> $obj = [PSCustomObject]@{ Name = "Foo" }
        PS C:\> Get-ChildItem | Select-DbaObject FullName, Length, "Name from obj"
        
        Selects the properties FullName and Length from the input and the Name property from the object stored in $obj
    
    .EXAMPLE
        PS C:\> $list = @()
        PS C:\> $list += [PSCustomObject]@{ Type = "Foo"; ID = 1 }
        PS C:\> $list += [PSCustomObject]@{ Type = "Bar"; ID = 2 }
        PS C:\> $obj | Select-DbaObject Name, "ID from list WHERE Type = Name"
        
        This allows you to LEFT JOIN contents of another variable.
        Note that it can only do simple property-matching at this point.
        
        It will select Name from the objects stored in $obj, and for each of those the ID Property on any object in $list that has a Type property of equal value as Name on the input.
#>
    [CmdletBinding(DefaultParameterSetName = 'DefaultParameter', RemotingCapability = 'None')]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [psobject]
        $InputObject,
        
        [Parameter(ParameterSetName = 'DefaultParameter', Position = 0)]
        [Parameter(ParameterSetName = 'SkipLastParameter', Position = 0)]
        [SqlCollaborative.Dbatools.Parameter.DbaSelectParameter[]]
        $Property,
        
        [Parameter(ParameterSetName = 'SkipLastParameter')]
        [Parameter(ParameterSetName = 'DefaultParameter')]
        [string[]]
        $ExcludeProperty,
        
        [Parameter(ParameterSetName = 'DefaultParameter')]
        [Parameter(ParameterSetName = 'SkipLastParameter')]
        [string]
        $ExpandProperty,
        
        [switch]
        $Unique,
        
        [Parameter(ParameterSetName = 'DefaultParameter')]
        [ValidateRange(0, 2147483647)]
        [int]
        $Last,
        
        [Parameter(ParameterSetName = 'DefaultParameter')]
        [ValidateRange(0, 2147483647)]
        [int]
        $First,
        
        [Parameter(ParameterSetName = 'DefaultParameter')]
        [ValidateRange(0, 2147483647)]
        [int]
        $Skip,
        
        [Parameter(ParameterSetName = 'SkipLastParameter')]
        [ValidateRange(0, 2147483647)]
        [int]
        $SkipLast,
        
        [Parameter(ParameterSetName = 'IndexParameter')]
        [Parameter(ParameterSetName = 'DefaultParameter')]
        [switch]
        $Wait,
        
        [Parameter(ParameterSetName = 'IndexParameter')]
        [ValidateRange(0, 2147483647)]
        [int[]]
        $Index,
        
        [string[]]
        $ShowProperty,
        
        [string[]]
        $ShowExcludeProperty,
        
        [string]
        $TypeName
    )
    
    begin {
        try {
            $outBuffer = $null
            if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) {
                $PSBoundParameters['OutBuffer'] = 1
            }
            
            $clonedParameters = @{ }
            foreach ($key in $PSBoundParameters.Keys) {
                if (($key -ne "Property") -and ($key -ne "ShowExcludeProperty") -and ($key -ne "ShowProperty") -and ($key -ne "TypeName")) {
                    $clonedParameters[$key] = $PSBoundParameters[$key]
                }
            }
            if (Test-Bound -ParameterName 'Property') {
                $clonedParameters['Property'] = $Property.Value
            }
            
            if (-not ($ShowExcludeProperty -or $ShowProperty -or $TypeName)) {
                $__noAdjustment = $true
            }
            else {
                $__noAdjustment = $false
                if ($ShowProperty) {
                    $__defaultset = New-Object System.Management.Automation.PSPropertySet('DefaultDisplayPropertySet', $ShowProperty)
                    $__standardmembers = [System.Management.Automation.PSMemberInfo[]]@($__defaultset)
                }
                if ($TypeName) {
                    $__callerModule = (Get-PSCallStack)[1].InvocationInfo.MyCommand.ModuleName
                    $__typeName = $TypeName
                    if ($__callerModule) { $__typeName = "$($__callerModule).$($TypeName)" }
                }
            }
            
            $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Microsoft.PowerShell.Utility\Select-Object', [System.Management.Automation.CommandTypes]::Cmdlet)
            $scriptCmd = { & $wrappedCmd @clonedParameters }
            $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
            # If no adjustment is necessary, run as integrated command (better performance)
            if ($__noAdjustment) { $steppablePipeline.Begin($PSCmdlet) }
            # If Adjustments are necessary, run as addon, allowing us to capture output
            else { $steppablePipeline.Begin($true) }
        }
        catch {
            throw
        }
    }
    
    process {
        try {
            if ($__noAdjustment) {
                $steppablePipeline.Process($InputObject)
            }
            else {
                $__item = $steppablePipeline.Process($InputObject)[0]
                if ($ShowProperty) {
                    $__item | Add-Member -Force -MemberType MemberSet -Name PSStandardMembers -Value $__standardmembers -ErrorAction SilentlyContinue
                }
                elseif ($ShowExcludeProperty) {
                    $__propertiesToShow = @()
                    foreach ($prop in $__item.PSObject.Properties.Name) {
                        if ($prop -notin $ShowExcludeProperty) {
                            $__propertiesToShow += $prop
                        }
                    }
                    $__defaultset = New-Object System.Management.Automation.PSPropertySet('DefaultDisplayPropertySet', $__propertiesToShow)
                    $__standardmembers = [System.Management.Automation.PSMemberInfo[]]@($__defaultset)
                    $__item | Add-Member -Force -MemberType MemberSet -Name PSStandardMembers -Value $__standardmembers -ErrorAction SilentlyContinue
                }
                if ($TypeName) {
                    $__item.PSObject.TypeNames.Insert(0, $__typeName)
                }
                $__item
            }
        }
        catch {
            throw
        }
    }
    
    end {
        try {
            $steppablePipeline.End()
        }
        catch {
            throw
        }
    }
}
tools\dbatools\functions\Set-DbaAgentAlert.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Set-DbaAgentAlert {
    <#
.SYNOPSIS
Set-DbaAgentAlert updates a the status of a SQL Agent Alert.

.DESCRIPTION
Set-DbaAgentAlert updates an alert in the SQL Server Agent with parameters supplied.

.PARAMETER SqlInstance
SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Alert
The name of the alert.

.PARAMETER NewName
The new name for the alert.

.PARAMETER Enabled
Enabled the alert.

.PARAMETER Disabled
Disabled the alert.

.PARAMETER Force
The force parameter will ignore some errors in the parameters and assume defaults.

.PARAMETER InputObject
Enables piping alert objects

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Author: Garry Bargsley (@gbargsley, garrybargsley.com)
Tags: Agent, Alert

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Set-DbaAgentAlert

.EXAMPLE
Set-DbaAgentAlert -SqlInstance sql1 -Alert 'Severity 025: Fatal Error' -Disabled
Changes the alert to disabled.

.EXAMPLE
Set-DbaAgentAlert -SqlInstance sql1 -Alert 'Severity 025: Fatal Error', 'Error Number 825', 'Error Number 824' -Enabled
Changes multiple alerts to enabled.

.EXAMPLE
Set-DbaAgentAlert -SqlInstance sql1, sql2, sql3 -Alert 'Severity 025: Fatal Error', 'Error Number 825', 'Error Number 824' -Enabled
Changes multiple alerts to enabled on multiple servers.

.EXAMPLE
Set-DbaAgentAlert -SqlInstance sql1 -Alert 'Severity 025: Fatal Error' -Disabled -Whatif
Doesn't Change the alert but shows what would happen.

#>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Alert,
        [string]$NewName,
        [switch]$Enabled,
        [switch]$Disabled,
        [switch]$Force,
        [parameter(ValueFromPipeline = $true)]
        [Microsoft.SqlServer.Management.Smo.Agent.Alert[]]$InputObject,
        [switch][Alias('Silent')]
        $EnableException
    )

    begin {
}
        process {

            if (Test-FunctionInterrupt) { return }

            if ((-not $InputObject) -and (-not $Alert)) {
                Stop-Function -Message "You must specify an alert name or pipe in results from another command" -Target $sqlinstance
                return
            }

            foreach ($instance in $sqlinstance) {
                # Try connecting to the instance
                Write-Message -Message "Connecting to $instance" -Level Verbose
                try {
                    $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
                }
                catch {
                    Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                }
            foreach ($a in $Alert) {
                    # Check if the alert exists
                    if ($server.JobServer.Alerts.Name -notcontains $a) {
                        Stop-Function -Message "Alert $a doesn't exists on $instance" -Target $instance
                    }
                    else {
                        # Get the alert
                        try {
                            $InputObject += $server.JobServer.Alerts[$a]

                            # Refresh the object
                            $InputObject.Refresh()
                        }
                        catch {
                            Stop-Function -Message "Something went wrong retrieving the alert" -Target $a -ErrorRecord $_ -Continue
                        }
                    }
                }
            }

            foreach ($currentalert in $InputObject) {
                $server = $currentalert.Parent.Parent

                #region alert options
                # Settings the options for the alert
                if ($NewName) {
                    Write-Message -Message "Setting alert name to $NewName" -Level Verbose
                    $currentalert.Rename($NewName)
                }

                if ($Enabled) {
                    Write-Message -Message "Setting alert to enabled" -Level Verbose
                    $currentalert.IsEnabled = $true
                }

                if ($Disabled) {
                    Write-Message -Message "Setting alert to disabled" -Level Verbose
                    $currentalert.IsEnabled = $false
                }

                #endregion alert options

                # Execute
                if ($PSCmdlet.ShouldProcess($SqlInstance, "Changing the alert $a")) {
                    try {
                        Write-Message -Message "Changing the alert" -Level Verbose

                        # Change the alert
                        $currentalert.Alter()
                    }
                    catch {
                        Stop-Function -Message "Something went wrong changing the alert" -ErrorRecord $_ -Target $instance -Continue
                    }
                    Get-DbaAgentAlert -SqlInstance $server | Where-Object Name -eq $currentalert.name
                }
            }
        }
}
        {
            Write-Message -Message "Finished changing alert(s)" -Level Verbose
        }
tools\dbatools\functions\Set-DbaAgentJob.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Set-DbaAgentJob {
    <#
.SYNOPSIS
Set-DbaAgentJob updates a job.

.DESCRIPTION
Set-DbaAgentJob updates a job in the SQL Server Agent with parameters supplied.

.PARAMETER SqlInstance
SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Job
The name of the job.

.PARAMETER Schedule
Schedule to attach to job. This can be more than one schedule.

.PARAMETER ScheduleId
Schedule ID to attach to job. This can be more than one schedule ID.

.PARAMETER NewName
The new name for the job.

.PARAMETER Enabled
Enabled the job.

.PARAMETER Disabled
Disabled the job

.PARAMETER Description
The description of the job.

.PARAMETER StartStepId
The identification number of the first step to execute for the job.

.PARAMETER Category
The category of the job.

.PARAMETER OwnerLogin
The name of the login that owns the job.

.PARAMETER EventlogLevel
Specifies when to place an entry in the Microsoft Windows application log for this job.
Allowed values 0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always"
The text value van either be lowercase, uppercase or something in between as long as the text is correct.

.PARAMETER EmailLevel
Specifies when to send an e-mail upon the completion of this job.
Allowed values 0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always"
The text value van either be lowercase, uppercase or something in between as long as the text is correct.

.PARAMETER NetsendLevel
Specifies when to send a network message upon the completion of this job.
Allowed values 0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always"
The text value van either be lowercase, uppercase or something in between as long as the text is correct.

.PARAMETER PageLevel
Specifies when to send a page upon the completion of this job.
Allowed values 0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always"
The text value van either be lowercase, uppercase or something in between as long as the text is correct.

.PARAMETER EmailOperator
The e-mail name of the operator to whom the e-mail is sent when EmailLevel is reached.

.PARAMETER NetsendOperator
The name of the operator to whom the network message is sent.

.PARAMETER PageOperator
The name of the operator to whom a page is sent.

.PARAMETER DeleteLevel
Specifies when to delete the job.
Allowed values 0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always"
The text value van either be lowercase, uppercase or something in between as long as the text is correct.

.PARAMETER Force
The force parameter will ignore some errors in the parameters and assume defaults.

.PARAMETER InputObject
Enables piping job objects

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Author: Sander Stad (@sqlstad, sqlstad.nl)
Tags: Agent, Job

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Set-DbaAgentJob

.EXAMPLE
Set-DbaAgentJob sql1 -Job Job1 -Disabled
Changes the job to disabled

.EXAMPLE
Set-DbaAgentJob sql1 -Job Job1 -OwnerLogin user1
Changes the owner of the job

.EXAMPLE
Set-DbaAgentJob -SqlInstance sql1 -Job Job1 -EventLogLevel OnSuccess
Changes the job and sets the notification to write to the Windows Application event log on success

.EXAMPLE
Set-DbaAgentJob -SqlInstance sql1 -Job Job1 -EmailLevel OnFailure -EmailOperator dba
Changes the job and sets the notification to send an e-mail to the e-mail operator

.EXAMPLE
Set-DbaAgentJob -SqlInstance sql1 -Job Job1, Job2, Job3 -Enabled
Changes multiple jobs to enabled

.EXAMPLE
Set-DbaAgentJob -SqlInstance sql1, sql2, sql3 -Job Job1, Job2, Job3 -Enabled
Changes multiple jobs to enabled on multiple servers

.EXAMPLE
Set-DbaAgentJob -SqlInstance sql1 -Job Job1 -Description 'Just another job' -Whatif
Doesn't Change the job but shows what would happen.

.EXAMPLE
Set-DbaAgentJob -SqlInstance sql1, sql2, sql3 -Job 'Job One' -Description 'Job One'
Changes a job with the name "Job1" on multiple servers to have another description

.EXAMPLE
sql1, sql2, sql3 | Set-DbaAgentJob -Job Job1 -Description 'Job One'
Changes a job with the name "Job1" on multiple servers to have another description using pipe line

#>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Job,
        [object[]]$Schedule,
        [int[]]$ScheduleId,
        [string]$NewName,
        [switch]$Enabled,
        [switch]$Disabled,
        [string]$Description,
        [int]$StartStepId,
        [string]$Category,
        [string]$OwnerLogin,
        [ValidateSet(0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always")]
        [object]$EventLogLevel,
        [ValidateSet(0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always")]
        [object]$EmailLevel,
        [ValidateSet(0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always")]
        [object]$NetsendLevel,
        [ValidateSet(0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always")]
        [object]$PageLevel,
        [string]$EmailOperator,
        [string]$NetsendOperator,
        [string]$PageOperator,
        [ValidateSet(0, "Never", 1, "OnSuccess", 2, "OnFailure", 3, "Always")]
        [object]$DeleteLevel,
        [switch]$Force,
        [parameter(ValueFromPipeline = $true)]
        [Microsoft.SqlServer.Management.Smo.Agent.Job[]]$InputObject,
        [switch][Alias('Silent')]
        $EnableException
    )

    begin {
        # Check of the event log level is of type string and set the integer value
        if (($EventLogLevel -notin 0, 1, 2, 3) -and ($null -ne $EventLogLevel)) {
            $EventLogLevel = switch ($EventLogLevel) { "Never" { 0 } "OnSuccess" { 1 } "OnFailure" { 2 } "Always" { 3 } }
        }

        # Check of the email level is of type string and set the integer value
        if (($EmailLevel -notin 0, 1, 2, 3) -and ($null -ne $EmailLevel)) {
            $EmailLevel = switch ($EmailLevel) { "Never" { 0 } "OnSuccess" { 1 } "OnFailure" { 2 } "Always" { 3 } }
        }

        # Check of the net send level is of type string and set the integer value
        if (($NetsendLevel -notin 0, 1, 2, 3) -and ($null -ne $NetsendLevel)) {
            $NetsendLevel = switch ($NetsendLevel) { "Never" { 0 } "OnSuccess" { 1 } "OnFailure" { 2 } "Always" { 3 } }
        }

        # Check of the page level is of type string and set the integer value
        if (($PageLevel -notin 0, 1, 2, 3) -and ($null -ne $PageLevel)) {
            $PageLevel = switch ($PageLevel) { "Never" { 0 } "OnSuccess" { 1 } "OnFailure" { 2 } "Always" { 3 } }
        }

        # Check of the delete level is of type string and set the integer value
        if (($DeleteLevel -notin 0, 1, 2, 3) -and ($null -ne $DeleteLevel)) {
            $DeleteLevel = switch ($DeleteLevel) { "Never" { 0 } "OnSuccess" { 1 } "OnFailure" { 2 } "Always" { 3 } }
        }

        # Check the e-mail operator name
        if (($EmailLevel -ge 1) -and (-not $EmailOperator)) {
            Stop-Function -Message "Please set the e-mail operator when the e-mail level parameter is set." -Target $sqlinstance
            return
        }

        # Check the e-mail operator name
        if (($NetsendLevel -ge 1) -and (-not $NetsendOperator)) {
            Stop-Function -Message "Please set the netsend operator when the netsend level parameter is set." -Target $sqlinstance
            return
        }

        # Check the e-mail operator name
        if (($PageLevel -ge 1) -and (-not $PageOperator)) {
            Stop-Function -Message "Please set the page operator when the page level parameter is set." -Target $sqlinstance
            return
        }
    }

    process {

        if (Test-FunctionInterrupt) { return }

        if ((-not $InputObject) -and (-not $Job)) {
            Stop-Function -Message "You must specify a job name or pipe in results from another command" -Target $sqlinstance
            return
        }

        foreach ($instance in $sqlinstance) {
            # Try connecting to the instance
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            foreach ($j in $Job) {

                # Check if the job exists
                if ($server.JobServer.Jobs.Name -notcontains $j) {
                    Stop-Function -Message "Job $j doesn't exists on $instance" -Target $instance
                }
                else {
                    # Get the job
                    try {
                        $InputObject += $server.JobServer.Jobs[$j]

                        # Refresh the object
                        $InputObject.Refresh()
                    }
                    catch {
                        Stop-Function -Message "Something went wrong retrieving the job" -Target $j -ErrorRecord $_ -Continue
                    }
                }
            }
        }

        foreach ($currentjob in $InputObject) {
            $server = $currentjob.Parent.Parent

            #region job options
            # Settings the options for the job
            if ($NewName) {
                Write-Message -Message "Setting job name to $NewName" -Level Verbose
                $currentjob.Rename($NewName)
            }

            if ($Schedule) {
                # Loop through each of the schedules
                foreach ($s in $Schedule) {
                    if ($server.JobServer.SharedSchedules.Name -contains $s) {
                        # Get the schedule ID
                        $sID = $server.JobServer.SharedSchedules[$s].ID

                        # Add schedule to job
                        Write-Message -Message "Adding schedule id $sID to job" -Level Verbose
                        $currentjob.AddSharedSchedule($sID)
                    }
                    else {
                        Stop-Function -Message "Schedule $s cannot be found on instance $instance" -Target $s -Continue
                    }

                }
            }

            if ($ScheduleId) {
                # Loop through each of the schedules IDs
                foreach ($sID in $ScheduleId) {
                    # Check if the schedule is
                    if ($server.JobServer.SharedSchedules.ID -contains $sID) {
                        # Add schedule to job
                        Write-Message -Message "Adding schedule id $sID to job" -Level Verbose
                        $currentjob.AddSharedSchedule($sID)

                    }
                    else {
                        Stop-Function -Message "Schedule ID $sID cannot be found on instance $instance" -Target $sID -Continue
                    }
                }
            }

            if ($Enabled) {
                Write-Message -Message "Setting job to enabled" -Level Verbose
                $currentjob.IsEnabled = $true
            }

            if ($Disabled) {
                Write-Message -Message "Setting job to disabled" -Level Verbose
                $currentjob.IsEnabled = $false
            }

            if ($Description) {
                Write-Message -Message "Setting job description to $Description" -Level Verbose
                $currentjob.Description = $Description
            }

            if ($Category) {
                # Check if the job category exists
                if ($Category -notin $server.JobServer.JobCategories.Name) {
                    if ($Force) {
                        if ($PSCmdlet.ShouldProcess($instance, "Creating job category on $instance")) {
                            try {
                                # Create the category
                                New-DbaAgentJobCategory -SqlInstance $instance -Category $Category

                                Write-Message -Message "Setting job category to $Category" -Level Verbose
                                $currentjob.Category = $Category
                            }
                            catch {
                                Stop-Function -Message "Couldn't create job category $Category from $instance" -Target $instance -ErrorRecord $_
                            }
                        }
                    }
                    else {
                        Stop-Function -Message "Job category $Category doesn't exist on $instance. Use -Force to create it." -Target $instance
                        return
                    }
                }
                else {
                    Write-Message -Message "Setting job category to $Category" -Level Verbose
                    $currentjob.Category = $Category
                }
            }

            if ($StartStepId) {
                # Get the job steps
                $currentjobSteps = $currentjob.JobSteps

                # Check if there are any job steps
                if ($currentjobSteps.Count -ge 1) {
                    # Check if the start step id value is one of the job steps in the job
                    if ($currentjobSteps.ID -contains $StartStepId) {
                        Write-Message -Message "Setting job start step id to $StartStepId" -Level Verbose
                        $currentjob.StartStepID = $StartStepId
                    }
                    else {
                        Write-Message -Message "The step id is not present in job $j on instance $instance" -Warning
                    }

                }
                else {
                    Stop-Function -Message "There are no job steps present for job $j on instance $instance" -Target $instance -Continue
                }

            }

            if ($OwnerLogin) {
                # Check if the login name is present on the instance
                if ($server.Logins.Name -contains $OwnerLogin) {
                    Write-Message -Message "Setting job owner login name to $OwnerLogin" -Level Verbose
                    $currentjob.OwnerLoginName = $OwnerLogin
                }
                else {
                    Stop-Function -Message "The given owner log in name $OwnerLogin does not exist on instance $instance" -Target $instance -Continue
                }
            }

            if ($EventLogLevel) {
                Write-Message -Message "Setting job event log level to $EventlogLevel" -Level Verbose
                $currentjob.EventLogLevel = $EventLogLevel
            }

            if ($EmailLevel) {
                # Check if the notifiction needs to be removed
                if ($EmailLevel -eq 0) {
                    # Remove the operator
                    $currentjob.OperatorToEmail = $null

                    # Remove the notification
                    $currentjob.EmailLevel = $EmailLevel
                }
                else {
                    # Check if either the operator e-mail parameter is set or the operator is set in the job
                    if ($EmailOperator -or $currentjob.OperatorToEmail) {
                        Write-Message -Message "Setting job e-mail level to $EmailLevel" -Level Verbose
                        $currentjob.EmailLevel = $EmailLevel
                    }
                    else {
                        Stop-Function -Message "Cannot set e-mail level $EmailLevel without a valid e-mail operator name" -Target $instance -Continue
                    }
                }
            }

            if ($NetsendLevel) {
                # Check if the notifiction needs to be removed
                if ($NetsendLevel -eq 0) {
                    # Remove the operator
                    $currentjob.OperatorToNetSend = $null

                    # Remove the notification
                    $currentjob.NetSendLevel = $NetsendLevel
                }
                else {
                    # Check if either the operator netsend parameter is set or the operator is set in the job
                    if ($NetsendOperator -or $currentjob.OperatorToNetSend) {
                        Write-Message -Message "Setting job netsend level to $NetsendLevel" -Level Verbose
                        $currentjob.NetSendLevel = $NetsendLevel
                    }
                    else {
                        Stop-Function -Message "Cannot set netsend level $NetsendLevel without a valid netsend operator name" -Target $instance -Continue
                    }
                }
            }

            if ($PageLevel) {
                # Check if the notifiction needs to be removed
                if ($PageLevel -eq 0) {
                    # Remove the operator
                    $currentjob.OperatorToPage = $null

                    # Remove the notification
                    $currentjob.PageLevel = $PageLevel
                }
                else {
                    # Check if either the operator pager parameter is set or the operator is set in the job
                    if ($PageOperator -or $currentjob.OperatorToPage) {
                        Write-Message -Message "Setting job pager level to $PageLevel" -Level Verbose
                        $currentjob.PageLevel = $PageLevel
                    }
                    else {
                        Stop-Function -Message "Cannot set page level $PageLevel without a valid netsend operator name" -Target $instance -Continue
                    }
                }
            }

            # Check the current setting of the job's email level
            if ($EmailOperator) {
                # Check if the operator name is present
                if ($server.JobServer.Operators.Name -contains $EmailOperator) {
                    Write-Message -Message "Setting job e-mail operator to $EmailOperator" -Level Verbose
                    $currentjob.OperatorToEmail = $EmailOperator
                }
                else {
                    Stop-Function -Message "The e-mail operator name $EmailOperator does not exist on instance $instance. Exiting.." -Target $j -Continue
                }
            }

            if ($NetsendOperator) {
                # Check if the operator name is present
                if ($server.JobServer.Operators.Name -contains $NetsendOperator) {
                    Write-Message -Message "Setting job netsend operator to $NetsendOperator" -Level Verbose
                    $currentjob.OperatorToNetSend = $NetsendOperator
                }
                else {
                    Stop-Function -Message "The netsend operator name $NetsendOperator does not exist on instance $instance. Exiting.." -Target $j -Continue
                }
            }

            if ($PageOperator) {
                # Check if the operator name is present
                if ($server.JobServer.Operators.Name -contains $PageOperator) {
                    Write-Message -Message "Setting job pager operator to $PageOperator" -Level Verbose
                    $currentjob.OperatorToPage = $PageOperator
                }
                else {
                    Stop-Function -Message "The page operator name $PageOperator does not exist on instance $instance. Exiting.." -Target $instance -Continue
                }
            }

            if ($DeleteLevel) {
                Write-Message -Message "Setting job delete level to $DeleteLevel" -Level Verbose
                $currentjob.DeleteLevel = $DeleteLevel
            }
            #endregion job options

            # Execute
            if ($PSCmdlet.ShouldProcess($SqlInstance, "Changing the job $j")) {
                try {
                    Write-Message -Message "Changing the job" -Level Verbose

                    # Change the job
                    $currentjob.Alter()
                }
                catch {
                    Stop-Function -Message "Something went wrong changing the job" -ErrorRecord $_ -Target $instance -Continue
                }
                Get-DbaAgentJob -SqlInstance $server | Where-Object Name -eq $currentjob.name
            }
        }
    }

    end {
        Write-Message -Message "Finished changing job(s)" -Level Verbose
    }
}
tools\dbatools\functions\Set-DbaAgentJobCategory.ps1
function Set-DbaAgentJobCategory {
    <#
.SYNOPSIS
Set-DbaAgentJobCategory changes a job category.

.DESCRIPTION
Set-DbaAgentJobCategory makes it possible to change a job category.

.PARAMETER SqlInstance
SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Category
The name of the category

.PARAMETER NewName
New name of the job category

.PARAMETER Force
The force parameter will ignore some errors in the parameters and assume defaults.

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.NOTES
Author: Sander Stad (@sqlstad, sqlstad.nl)
Tags: Agent, Job, JobCategory

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Set-DbaAgentJobCategory

.EXAMPLE
New-DbaAgentJobCategory -SqlInstance sql1 -Category 'Category 1' -NewName 'Category 2'

Change the name of the category from 'Category 1' to 'Category 2'.

.EXAMPLE
Set-DbaAgentJobCategory -SqlInstance sql1, sql2 -Category Category1, Category2 -NewName cat1, cat2

Rename multiple jobs in one go on multiple servers.

#>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Parameter(Mandatory = $false)]
        [ValidateNotNullOrEmpty()]
        [string[]]$Category,
        [string[]]$NewName,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        # Create array list to hold the results
        $collection = New-Object System.Collections.ArrayList

        # Check if multiple categories are being changed
        if ($Category.Count -gt 1 -and $NewName.Count -eq 1) {
            Stop-Function -Message "You cannot rename multiple jobs to the same name" -Target $instance
        }
    }

    process {

        foreach ($instance in $sqlinstance) {
            # Try connecting to the instance
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            # Loop through each of the categories
            foreach ($cat in $Category) {
                # Check if the category exists
                if ($cat -notin $server.JobServer.JobCategories.Name) {
                    Stop-Function -Message "Job category $cat doesn't exist on $instance" -Target $instance -Continue
                }

                # Check if the category already exists
                if ($NewName -and ($NewName -in $server.JobServer.JobCategories.Name)) {
                    Stop-Function -Message "Job category $NewName already exists on $instance" -Target $instance -Continue
                }

                if ($PSCmdlet.ShouldProcess($instance, "Changing the job category $Category")) {
                    try {
                        # Get the job category object
                        $currentCategory = $server.JobServer.JobCategories[$cat]

                        Write-Message -Message "Changing job category $cat" -Level Verbose

                        # Get and set the original and new values
                        $originalCategoryName = $currentCategory.Name
                        $newCategoryName = $null

                        # Check if the job category needs to be renamed
                        if ($NewName) {
                            $currentCategory.Rename($NewName[$Category.IndexOf($cat)])
                            $newCategoryName = $currentCategory.Name
                        }

                        # Set up the custom object
                        $null = $collection.Add([PSCustomObject]@{
                                ComputerName    = $server.ComputerName
                                InstanceName    = $server.ServiceName
                                SqlInstance     = $server.DomainInstanceName
                                CategoryName    = $originalCategoryName
                                NewCategoryName = $newCategoryName
                            })

                    }
                    catch {
                        Stop-Function -Message "Something went wrong changing the job category $cat on $instance" -Target $cat -Continue -ErrorRecord $_
                    }

                } # if should process

            } # for each category

        } # foreach instance

        # Return result
        return $collection

    } # end process

    end {
        if (Test-FunctionInterrupt) { return }
        Write-Message -Message "Finished changing job category." -Level Verbose
    }

}
tools\dbatools\functions\Set-DbaAgentJobOutputFile.ps1
function Set-DbaAgentJobOutputFile {
    <#
        .Synopsis
            Set the output file for a step within an Agent job.

        .DESCRIPTION
            Sets the Output File for a step of an agent job with the Job Names and steps provided dynamically if required

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to.

        .PARAMETER SQLCredential
            Credential object used to connect to the SQL Server as a different user be it Windows or SQL Server. Windows users are determined by the existence of a backslash, so if you are intending to use an alternative Windows connection instead of a SQL login, ensure it contains a backslash.

        .PARAMETER Job
            The job to process - this list is auto-populated from the server.

        .PARAMETER Step
            The Agent Job Step to provide Output File Path for. Also available dynamically

        .PARAMETER OutputFile
            The Full Path to the New Output file

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Agent, Job, SqlAgent
            Author: Rob Sewell (https://sqldbawithabeard.com)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

            # todo - allow piping and add -All

        .EXAMPLE
            Set-DbaAgentJobOutputFile -SqlInstance SERVERNAME -JobName 'The Agent Job' -OutPutFile E:\Logs\AgentJobStepOutput.txt

            Sets the Job step for The Agent job on SERVERNAME to E:\Logs\AgentJobStepOutput.txt
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [Parameter(Mandatory = $true, HelpMessage = 'The SQL Server Instance',
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false,
            Position = 0)]
        [ValidateNotNull()]
        [ValidateNotNullOrEmpty()]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Parameter(Mandatory = $false, HelpMessage = 'SQL Credential',
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false)]
        [PSCredential]$SqlCredential,
        [object[]]$Job,
        [Parameter(Mandatory = $false, HelpMessage = 'The Job Step name',
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNull()]
        [ValidateNotNullOrEmpty()]
        [object[]]$Step,
        [Parameter(Mandatory = $true, HelpMessage = 'The Full Output File Path',
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false)]
        [ValidateNotNull()]
        [ValidateNotNullOrEmpty()]
        [string]$OutputFile,
        [Alias('Silent')]
        [switch]$EnableException
    )

    foreach ($instance in $sqlinstance) {
        try {
            $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
        }
        catch {
            Write-Message -Level Warning -Message "Failed to connect to: $instance"
            continue
        }

        if (!$Job) {
            # This is because jobname isn't yet required
            Write-Message -Level Warning -Message "You must specify a job using the -Job parameter."
            return
        }

        foreach ($name in $Job) {
            $currentJob = $server.JobServer.Jobs[$name]

            if ($Step) {
                $steps = $currentJob.JobSteps | Where-Object Name -in $Step

                if (!$steps) {
                    Write-Message -Level Warning -Message "$Step didn't return any steps"
                    return
                }
            }
            else {
                if (($currentJob.JobSteps).Count -gt 1) {
                    Write-Message -Level Output -Message "Which Job Step do you wish to add output file to?"
                    $steps = $currentJob.JobSteps | Out-GridView -Title "Choose the Job Steps to add an output file to" -PassThru -Verbose
                }
                else {
                    $steps = $currentJob.JobSteps
                }
            }

            if (!$steps) {
                $steps = $currentJob.JobSteps
            }

            foreach ($jobstep in $steps) {
                $currentoutputfile = $jobstep.OutputFileName

                Write-Message -Level Verbose -Message "Current Output File for $currentJob is $currentoutputfile"
                Write-Message -Level Verbose -Message "Adding $OutputFile to $jobstep for $currentJob"

                try {
                    if ($Pscmdlet.ShouldProcess($jobstep, "Changing Output File from $currentoutputfile to $OutputFile")) {
                        $jobstep.OutputFileName = $OutputFile
                        $jobstep.Alter()
                        $jobstep.Refresh()

                        [pscustomobject]@{
                            ComputerName   = $server.ComputerName
                            InstanceName   = $server.ServiceName
                            SqlInstance    = $server.DomainInstanceName
                            Job            = $currentJob.Name
                            JobStep        = $jobstep.Name
                            OutputFileName = $currentoutputfile
                        }
                    }
                }
                catch {
                    Stop-Function -Message "Failed to add $OutputFile to $jobstep for $currentJob" -InnerErrorRecord $_ -Target $currentJob
                }
            }
        }
    }
}
tools\dbatools\functions\Set-DbaAgentJobStep.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Set-DbaAgentJobStep {
    <#
.SYNOPSIS
Set-DbaAgentJobStep updates a job step.

.DESCRIPTION
Set-DbaAgentJobStep updates a job step in the SQL Server Agent with parameters supplied.

.PARAMETER SqlInstance
SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Job
The name of the job. Can be null if the the job id is being used.

.PARAMETER StepName
The name of the step.

.PARAMETER NewName
The new name for the step in case it needs to be renamed.

.PARAMETER SubSystem
The subsystem used by the SQL Server Agent service to execute command.
Allowed values 'ActiveScripting','AnalysisCommand','AnalysisQuery','CmdExec','Distribution','LogReader','Merge','PowerShell','QueueReader','Snapshot','Ssis','TransactSql'

.PARAMETER Command
The commands to be executed by SQLServerAgent service through subsystem.

.PARAMETER CmdExecSuccessCode
The value returned by a CmdExec subsystem command to indicate that command executed successfully.

.PARAMETER OnSuccessAction
The action to perform if the step succeeds.
Allowed values  "QuitWithSuccess" (default), "QuitWithFailure", "GoToNextStep", "GoToStep".
The text value van either be lowercase, uppercase or something in between as long as the text is correct.

.PARAMETER OnSuccessStepId
The ID of the step in this job to execute if the step succeeds and OnSuccessAction is "GoToNextStep".

.PARAMETER OnFailAction
The action to perform if the step fails.
Allowed values  "QuitWithSuccess" (default), "QuitWithFailure", "GoToNextStep", "GoToStep".
The text value van either be lowercase, uppercase or something in between as long as the text is correct.

.PARAMETER OnFailStepId
The ID of the step in this job to execute if the step fails and OnFailAction is "GoToNextStep".

.PARAMETER Database
The name of the database in which to execute a Transact-SQL step. The default is 'master'.

.PARAMETER DatabaseUser
The name of the user account to use when executing a Transact-SQL step. The default is 'sa'.

.PARAMETER RetryAttempts
The number of retry attempts to use if this step fails. The default is 0.

.PARAMETER RetryInterval
The amount of time in minutes between retry attempts. The default is 0.

.PARAMETER OutputFileName
The name of the file in which the output of this step is saved.

.PARAMETER Flag
Sets the flag(s) for the job step.

Flag                                    Description
----------------------------------------------------------------------------
AppendAllCmdExecOutputToJobHistory      Job history, including command output, is appended to the job history file.
AppendToJobHistory                      Job history is appended to the job history file.
AppendToLogFile                         Job history is appended to the SQL Server log file.
AppendToTableLog                        Job history is appended to a log table.
LogToTableWithOverwrite                 Job history is written to a log table, overwriting previous contents.
None                                    Job history is not appended to a file.
ProvideStopProcessEvent                 Job processing is stopped.

.PARAMETER ProxyName
The name of the proxy that the job step runs as.

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.PARAMETER Force
The force parameter will ignore some errors in the parameters and assume defaults.

.NOTES
Author: Sander Stad (@sqlstad, sqlstad.nl)
Tags: Agent, Job, JobStep

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Set-DbaAgentJobStep

.EXAMPLE
Set-DbaAgentJobStep -SqlInstance sql1 -Job Job1 -StepName Step1 -NewName Step2
Changes the name of the step in "Job1" with the name Step1 to Step2

.EXAMPLE
Set-DbaAgentJobStep -SqlInstance sql1 -Job Job1 -StepName Step1 -Database msdb
Changes the database of the step in "Job1" with the name Step1 to msdb

.EXAMPLE
Set-DbaAgentJobStep -SqlInstance sql1 -Job Job1, Job2 -StepName Step1 -Database msdb
Changes job steps in multiple jobs with the name Step1 to msdb

.EXAMPLE
Set-DbaAgentJobStep -SqlInstance sql1, sql2, sql3 -Job Job1, Job2 -StepName Step1 -Database msdb
Changes job steps in multiple jobs on multiple servers with the name Step1 to msdb

.EXAMPLE
Set-DbaAgentJobStep -SqlInstance sql1, sql2, sql3 -Job Job1 -StepName Step1 -Database msdb
Changes the database of the step in "Job1" with the name Step1 to msdb for multiple servers

.EXAMPLE
sql1, sql2, sql3 | Set-DbaAgentJobStep -Job Job1 -StepName Step1 -Database msdb
Changes the database of the step in "Job1" with the name Step1 to msdb for multiple servers using pipeline

#>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Parameter(Mandatory = $false)]
        [PSCredential]$SqlCredential,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [object[]]$Job,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$StepName,
        [Parameter(Mandatory = $false)]
        [string]$NewName,
        [Parameter(Mandatory = $false)]
        [ValidateSet('ActiveScripting', 'AnalysisCommand', 'AnalysisQuery', 'CmdExec', 'Distribution', 'LogReader', 'Merge', 'PowerShell', 'QueueReader', 'Snapshot', 'Ssis', 'TransactSql')]
        [string]$Subsystem,
        [Parameter(Mandatory = $false)]
        [string]$Command,
        [Parameter(Mandatory = $false)]
        [int]$CmdExecSuccessCode,
        [Parameter(Mandatory = $false)]
        [ValidateSet('QuitWithSuccess', 'QuitWithFailure', 'GoToNextStep', 'GoToStep')]
        [string]$OnSuccessAction,
        [Parameter(Mandatory = $false)]
        [int]$OnSuccessStepId,
        [Parameter(Mandatory = $false)]
        [ValidateSet('QuitWithSuccess', 'QuitWithFailure', 'GoToNextStep', 'GoToStep')]
        [string]$OnFailAction,
        [Parameter(Mandatory = $false)]
        [int]$OnFailStepId,
        [Parameter(Mandatory = $false)]
        [string]$Database,
        [Parameter(Mandatory = $false)]
        [string]$DatabaseUser,
        [Parameter(Mandatory = $false)]
        [int]$RetryAttempts,
        [Parameter(Mandatory = $false)]
        [int]$RetryInterval,
        [Parameter(Mandatory = $false)]
        [string]$OutputFileName,
        [Parameter(Mandatory = $false)]
        [ValidateSet('AppendAllCmdExecOutputToJobHistory', 'AppendToJobHistory', 'AppendToLogFile', 'LogToTableWithOverwrite', 'None', 'ProvideStopProcessEvent')]
        [string[]]$Flag,
        [Parameter(Mandatory = $false)]
        [string]$ProxyName,
        [Parameter(Mandatory = $false)]
        [Alias('Silent')]
        [switch]$EnableException,
        [Parameter(Mandatory = $false)]
        [switch]$Force
    )

    begin {
        # Check the parameter on success step id
        if (($OnSuccessAction -ne 'GoToStep') -and ($OnSuccessStepId -ge 1)) {
            Stop-Function -Message "Parameter OnSuccessStepId can only be used with OnSuccessAction 'GoToStep'." -Target $SqlInstance
            return
        }

        # Check the parameter on success step id
        if (($OnFailAction -ne 'GoToStep') -and ($OnFailStepId -ge 1)) {
            Stop-Function -Message "Parameter OnFailStepId can only be used with OnFailAction 'GoToStep'." -Target $SqlInstance
            return
        }
    }

    process {

        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $sqlinstance) {

            # Try connecting to the instance
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $Server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            foreach ($j in $Job) {

                # Check if the job exists
                if ($Server.JobServer.Jobs.Name -notcontains $j) {
                    Stop-Function -Message "Job $j doesn't exists on $instance" -Target $instance -Continue
                }
                else {
                    # Check if the job step exists
                    if ($Server.JobServer.Jobs[$j].JobSteps.Name -notcontains $StepName) {
                        Stop-Function -Message "Step $StepName doesn't exists for job $j" -Target $instance -Continue
                    }
                    else {

                        # Get the job step
                        $JobStep = $Server.JobServer.Jobs[$j].JobSteps[$StepName]

                        Write-Message -Message "Modifying job $j on $instance" -Level Verbose

                        #region job step options
                        # Setting the options for the job step
                        if ($NewName) {
                            Write-Message -Message "Setting job step name to $NewName" -Level Verbose
                            $JobStep.Rename($NewName)
                        }

                        if ($Subsystem) {
                            Write-Message -Message "Setting job step subsystem to $Subsystem" -Level Verbose
                            $JobStep.Subsystem = $Subsystem
                        }

                        if ($Command) {
                            Write-Message -Message "Setting job step command to $Command" -Level Verbose
                            $JobStep.Command = $Command
                        }

                        if ($CmdExecSuccessCode) {
                            Write-Message -Message "Setting job step command exec success code to $CmdExecSuccessCode" -Level Verbose
                            $JobStep.CommandExecutionSuccessCode = $CmdExecSuccessCode
                        }

                        if ($OnSuccessAction) {
                            Write-Message -Message "Setting job step success action to $OnSuccessAction" -Level Verbose
                            $JobStep.OnSuccessAction = $OnSuccessAction
                        }

                        if ($OnSuccessStepId) {
                            Write-Message -Message "Setting job step success step id to $OnSuccessStepId" -Level Verbose
                            $JobStep.OnSuccessStep = $OnSuccessStepId
                        }

                        if ($OnFailAction) {
                            Write-Message -Message "Setting job step fail action to $OnFailAction" -Level Verbose
                            $JobStep.OnFailAction = $OnFailAction
                        }

                        if ($OnFailStepId) {
                            Write-Message -Message "Setting job step fail step id to $OnFailStepId" -Level Verbose
                            $JobStep.OnFailStep = $OnFailStepId
                        }

                        if ($Database) {
                            # Check if the database is present on the server
                            if ($Server.Databases.Name -contains $Database) {
                                Write-Message -Message "Setting job step database name to $Database" -Level Verbose
                                $JobStep.DatabaseName = $Database
                            }
                            else {
                                Stop-Function -Message "The database is not present on instance $instance." -Target $instance -Continue
                            }
                        }

                        if (($DatabaseUser) -and ($Database)) {
                            # Check if the username is present in the database
                            if ($Server.Databases[$Database].Users.Name -contains $DatabaseUser) {
                                Write-Message -Message "Setting job step database username to $DatabaseUser" -Level Verbose
                                $JobStep.DatabaseUserName = $DatabaseUser
                            }
                            else {
                                Stop-Function -Message "The database user is not present in the database $Database on instance $instance." -Target $instance -Continue
                            }
                        }

                        if ($RetryAttempts) {
                            Write-Message -Message "Setting job step retry attempts to $RetryAttempts" -Level Verbose
                            $JobStep.RetryAttempts = $RetryAttempts
                        }

                        if ($RetryInterval) {
                            Write-Message -Message "Setting job step retry interval to $RetryInterval" -Level Verbose
                            $JobStep.RetryInterval = $RetryInterval
                        }

                        if ($OutputFileName) {
                            Write-Message -Message "Setting job step output file name to $OutputFileName" -Level Verbose
                            $JobStep.OutputFileName = $OutputFileName
                        }

                        if ($ProxyName) {
                            # Check if the proxy exists
                            if ($Server.JobServer.ProxyAccounts.Name -contains $ProxyName) {
                                Write-Message -Message "Setting job step proxy name to $ProxyName" -Level Verbose
                                $JobStep.ProxyName = $ProxyName
                            }
                            else {
                                Stop-Function -Message "The proxy name $ProxyName doesn't exist on instance $instance." -Target $instance -Continue
                            }
                        }

                        if ($Flag.Count -ge 1) {
                            Write-Message -Message "Setting job step flag(s) to $($Flags -join ',')" -Level Verbose
                            $JobStep.JobStepFlags = $Flag
                        }
                        #region job step options

                        # Execute
                        if ($PSCmdlet.ShouldProcess($instance, "Changing the job step $StepName for job $j")) {
                            try {
                                Write-Message -Message "Changing the job step $StepName for job $j" -Level Verbose

                                # Change the job step
                                $JobStep.Alter()
                            }
                            catch {
                                Stop-Function -Message "Something went wrong changing the job step" -ErrorRecord $_ -Target $instance -Continue
                            }
                        }
                    }
                }

            } # foreach object job
        } # foreach object intance
    } # process

    end {
        if (Test-FunctionInterrupt) { return }
        Write-Message -Message "Finished changing job step(s)" -Level Verbose
    }
}
tools\dbatools\functions\Set-DbaAgentSchedule.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Set-DbaAgentSchedule {
    <#
.SYNOPSIS
Set-DbaAgentSchedule updates a schedule in the msdb database.

.DESCRIPTION
Set-DbaAgentSchedule will help update a schedule for a job. It does not attach the schedule to a job.

.PARAMETER SqlInstance
SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

.PARAMETER Job
The name of the job that has the schedule.

.PARAMETER ScheduleName
The name of the schedule.

.PARAMETER NewName
The new name for the schedule.

.PARAMETER Enabled
Set the schedule to enabled.

.PARAMETER Disabled
Set the schedule to disabled.

.PARAMETER FrequencyType
A value indicating when a job is to be executed.
Allowed values are 1, "Once", 4, "Daily", 8, "Weekly", 16, "Monthly", 32, "MonthlyRelative", 64, "AgentStart", 128 or "IdleComputer"

.PARAMETER FrequencyInterval
The days that a job is executed
Allowed values are 1, "Sunday", 2, "Monday", 4, "Tuesday", 8, "Wednesday", 16, "Thursday", 32, "Friday", 64, "Saturday", 62, "Weekdays", 65, "Weekend", 127, "EveryDay".
If 62, "Weekdays", 65, "Weekend", 127, "EveryDay" is used it overwwrites any other value that has been passed before.

.PARAMETER FrequencySubdayType
Specifies the units for the subday FrequencyInterval.
Allowed values are 1, "Time", 2, "Seconds", 4, "Minutes", 8 or "Hours"

.PARAMETER FrequencySubdayInterval
The number of subday type periods to occur between each execution of a job.

.PARAMETER FrequencySubdayInterval
The number of subday type periods to occur between each execution of a job.

.PARAMETER FrequencyRelativeInterval
A job's occurrence of FrequencyInterval in each month, if FrequencyInterval is 32 (monthlyrelative).

.PARAMETER FrequencyRecurrenceFactor
The number of weeks or months between the scheduled execution of a job. FrequencyRecurrenceFactor is used only if FrequencyType is 8, "Weekly", 16, "Monthly", 32 or "MonthlyRelative".

.PARAMETER StartDate
The date on which execution of a job can begin.

.PARAMETER EndDate
The date on which execution of a job can stop.

.PARAMETER StartTime
The time on any day to begin execution of a job. Format HHMMSS / 24 hour clock.
Example: '010000' for 01:00:00 AM.
Example: '140000' for 02:00:00 PM.

.PARAMETER EndTime
The time on any day to end execution of a job. Format HHMMSS / 24 hour clock.
Example: '010000' for 01:00:00 AM.
Example: '140000' for 02:00:00 PM.

.PARAMETER Owner
The name of the server principal that owns the schedule. If no value is given the schedule is owned by the creator.

.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.

.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.PARAMETER Force
The force parameter will ignore some errors in the parameters and assume defaults.
It will also remove the any present schedules with the same name for the specific job.

.NOTES
Author: Sander Stad (@sqlstad, sqlstad.nl)
Tags: Agent, Job, JobStep

Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Set-DbaAgentSchedule

.EXAMPLE
Set-DbaAgentSchedule -SqlInstance sql1 -Job Job1 -ScheduleName daily -Enabled
Changes the schedule for Job1 with the name 'daily' to enabled

.EXAMPLE
Set-DbaAgentSchedule -SqlInstance sql1 -Job Job1 -ScheduleName daily -NewName weekly -FrequencyType Weekly -FrequencyInterval Monday, Wednesday, Friday
Changes the schedule for Job1 with the name daily to have a new name weekly

.EXAMPLE
Set-DbaAgentSchedule -SqlInstance sql1 -Job Job1, Job2, Job3 -ScheduleName daily -StartTime '230000'
Changes the start time of the schedule for Job1 to 11 PM for multiple jobs

.EXAMPLE
Set-DbaAgentSchedule -SqlInstance sql1, sql2, sql3 -Job Job1 -ScheduleName daily -Enabled
Changes the schedule for Job1 with the name daily to enabled on multiple servers

.EXAMPLE
sql1, sql2, sql3 | Set-DbaAgentSchedule -Job Job1 -ScheduleName 'daily' -Enabled
Changes the schedule for Job1 with the name 'daily' to enabled on multiple servers using pipe line

#>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]

    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Parameter(Mandatory = $false)]
        [PSCredential]$SqlCredential,
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [ValidateNotNullOrEmpty()]
        [object[]]$Job,
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$ScheduleName,
        [Parameter(Mandatory = $false)]
        [string]$NewName,
        [Parameter(Mandatory = $false)]
        [switch]$Enabled,
        [Parameter(Mandatory = $false)]
        [switch]$Disabled,
        [ValidateSet(1, "Once", 4, "Daily", 8, "Weekly", 16, "Monthly", 32, "MonthlyRelative", 64, "AgentStart", 128, "IdleComputer")]
        [object]$FrequencyType,
        [Parameter(Mandatory = $false)]
        [object[]]$FrequencyInterval,
        [Parameter(Mandatory = $false)]
        [ValidateSet(1, "Time", 2, "Seconds", 4, "Minutes", 8, "Hours")]
        [object]$FrequencySubdayType,
        [Parameter(Mandatory = $false)]
        [int]$FrequencySubdayInterval,
        [Parameter(Mandatory = $false)]
        [ValidateSet('Unused', 'First', 'Second', 'Third', 'Fourth', 'Last')]
        [object]$FrequencyRelativeInterval,
        [Parameter(Mandatory = $false)]
        [int]$FrequencyRecurrenceFactor,
        [Parameter(Mandatory = $false)]
        [string]$StartDate,
        [Parameter(Mandatory = $false)]
        [string]$EndDate,
        [Parameter(Mandatory = $false)]
        [string]$StartTime,
        [Parameter(Mandatory = $false)]
        [string]$EndTime,
        [Parameter(Mandatory = $false)]
        [Alias('Silent')]
        [switch]$EnableException,
        [Parameter(Mandatory = $false)]
        [switch]$Force
    )

    begin {

        # Check of the FrequencyType value is of type string and set the integer value
        if ($FrequencyType -notin 0, 1, 4, 8, 16, 32, 64, 128) {
            [int]$FrequencyType = switch ($FrequencyType) { "Once" { 1 } "Daily" { 4 } "Weekly" { 8 } "Monthly" { 16 } "MonthlyRelative" { 32 } "AgentStart" { 64 } "IdleComputer" { 128 } }
        }

        # Check of the FrequencySubdayType value is of type string and set the integer value
        if ($FrequencySubdayType -notin 0, 1, 2, 4, 8) {
            [int]$FrequencySubdayType = switch ($FrequencySubdayType) { "Time" { 1 } "Seconds" { 2 } "Minutes" { 4 } "Hours" { 8 } default {0} }
        }

        # Check if the interval is valid
        if (($FrequencyType -eq 4) -and ($FrequencyInterval -lt 1 -or $FrequencyInterval -ge 365)) {
            Stop-Function -Message "The interval $FrequencyInterval needs to be higher than 1 and lower than 365 when using a daily frequency the interval." -Target $SqlInstance
            return
        }

        # Check if the recurrence factor is set for weekly or monthly interval
        if (($FrequencyType -in 8, 16) -and $FrequencyRecurrenceFactor -lt 1) {
            if ($Force) {
                $FrequencyRecurrenceFactor = 1
                Write-Message -Message "Recurrence factor not set for weekly or monthly interval. Setting it to $FrequencyRecurrenceFactor." -Level Verbose
            }
            else {
                Stop-Function -Message "The recurrence factor $FrequencyRecurrenceFactor needs to be at least on when using a weekly or monthly interval." -Target $SqlInstance
                return
            }
        }

        # Check the subday interval
        if (($FrequencySubdayType -in 2, 4) -and (-not ($FrequencySubdayInterval -ge 1 -or $FrequencySubdayInterval -le 59))) {
            Stop-Function -Message "Subday interval $FrequencySubdayInterval must be between 1 and 59 when subday type is 2, 'Seconds', 4 or 'Minutes'" -Target $SqlInstance
            return
        }
        elseif (($FrequencySubdayType -eq 8) -and (-not ($FrequencySubdayInterval -ge 1 -and $FrequencySubdayInterval -le 23))) {
            Stop-Function -Message "Subday interval $FrequencySubdayInterval must be between 1 and 23 when subday type is 8 or 'Hours" -Target $SqlInstance
            return
        }

        # Check of the FrequencyInterval value is of type string and set the integer value
        if (($null -ne $FrequencyType)) {
            # Create the interval to hold the value(s)
            [int]$Interval = 0

            # If the FrequencyInterval is set for the weekly FrequencyType
            if ($FrequencyType -eq 8) {
                # Loop through the array
                foreach ($Item in $FrequencyInterval) {
                    switch ($Item) {
                        "Sunday" { $Interval += 1 }
                        "Monday" { $Interval += 2 }
                        "Tuesday" { $Interval += 4 }
                        "Wednesday" { $Interval += 8 }
                        "Thursday" { $Interval += 16 }
                        "Friday" { $Interval += 32 }
                        "Saturday" { $Interval += 64 }
                        "Weekdays" { $Interval = 62 }
                        "Weekend" { $Interval = 65 }
                        "EveryDay" {$Interval = 127 }
                        1 { $Interval += 1 }
                        2 { $Interval += 2 }
                        4 { $Interval += 4 }
                        8 { $Interval += 8 }
                        16 { $Interval += 16 }
                        31 { $Interval += 32 }
                        64 { $Interval += 64 }
                        62 { $Interval = 62 }
                        65 { $Interval = 65 }
                        127 {$Interval = 127 }
                    }
                }
            }

            # If the FrequencyInterval is set for the relative monthly FrequencyInterval
            if ($FrequencyType -eq 32) {
                # Loop through the array
                foreach ($Item in $FrequencyInterval) {
                    switch ($Item) {
                        "Sunday" { $Interval += 1 }
                        "Monday" { $Interval += 2 }
                        "Tuesday" { $Interval += 3 }
                        "Wednesday" { $Interval += 4 }
                        "Thursday" { $Interval += 5 }
                        "Friday" { $Interval += 6 }
                        "Saturday" { $Interval += 7 }
                        "Day" { $Interval += 8 }
                        "Weekday" { $Interval += 9 }
                        "WeekendDay" { $Interval += 10 }
                        1 { $Interval += 1 }
                        2 { $Interval += 2 }
                        3 { $Interval += 3 }
                        4 { $Interval += 4 }
                        5 { $Interval += 5 }
                        6 { $Interval += 6 }
                        7 { $Interval += 7 }
                        8 { $Interval += 8 }
                        9 { $Interval += 9 }
                        10 { $Interval += 10 }
                    }
                }
            }
        }

        # Check of the relative FrequencyInterval value is of type string and set the integer value
        if (($FrequencyRelativeInterval -notin 1, 2, 4, 8, 16) -and $null -ne $FrequencyRelativeInterval) {
            [int]$FrequencyRelativeInterval = switch ($FrequencyRelativeInterval) { "First" { 1 } "Second" { 2 } "Third" { 4 } "Fourth" { 8 } "Last" { 16 } "Unused" { 0 } default { 0 }}
        }

        # Check if the interval is valid
        if (($FrequencyType -eq 4) -and ($FrequencyInterval -lt 1 -or $FrequencyInterval -ge 365)) {
            Stop-Function -Message "The interval $FrequencyInterval needs to be higher than 1 and lower than 365 when using a daily frequency the interval." -Target $SqlInstance
            return
        }

        # Setup the regex
        $RegexDate = '(?<!\d)(?:(?:(?:1[6-9]|[2-9]\d)?\d{2})(?:(?:(?:0[13578]|1[02])31)|(?:(?:0[1,3-9]|1[0-2])(?:29|30)))|(?:(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))0229)|(?:(?:1[6-9]|[2-9]\d)?\d{2})(?:(?:0?[1-9])|(?:1[0-2]))(?:0?[1-9]|1\d|2[0-8]))(?!\d)'
        $RegexTime = '^(?:(?:([01]?\d|2[0-3]))?([0-5]?\d))?([0-5]?\d)$'

        # Check the start date
        if ($StartDate -and ($StartDate -notmatch $RegexDate)) {
            Stop-Function -Message "Start date $StartDate needs to be a valid date with format yyyyMMdd" -Target $SqlInstance
            return
        }

        # Check the end date
        if ($EndDate -and ($EndDate -notmatch $RegexDate)) {
            Stop-Function -Message "End date $EndDate needs to be a valid date with format yyyyMMdd" -Target $SqlInstance
            return
        }
        elseif ($EndDate -lt $StartDate) {
            Stop-Function -Message "End date $EndDate cannot be before start date $StartDate" -Target $SqlInstance
            return
        }

        # Check the start time
        if ($StartTime -and ($StartTime -notmatch $RegexTime)) {
            Stop-Function -Message "Start time $StartTime needs to match between '000000' and '235959'" -Target $SqlInstance
            return
        }

        # Check the end time
        if ($EndTime -and ($EndTime -notmatch $RegexTime)) {
            Stop-Function -Message "End time $EndTime needs to match between '000000' and '235959'" -Target $SqlInstance
            return
        }
    }

    process {

        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $sqlinstance) {

            foreach ($j in $Job) {

                # Try connecting to the instance
                Write-Message -Message "Connecting to $instance" -Level Verbose
                try {
                    $Server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
                }
                catch {
                    Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                }

                # Check if the job exists
                if ($Server.JobServer.Jobs.Name -notcontains $j) {
                    Write-Message -Message "Job $j doesn't exists on $instance" -Level Warning
                }
                else {
                    # Check if the job schedule exists
                    if ($Server.JobServer.Jobs[$j].JobSchedules.Name -notcontains $ScheduleName) {
                        Stop-Function -Message "Schedule $ScheduleName doesn't exists for job $j on $instance" -Target $instance -Continue
                    }
                    else {
                        # Get the job schedule
                        # If for some reason the there are multiple schedules with the same name, the first on is chosen
                        $JobSchedule = $Server.JobServer.Jobs[$j].JobSchedules[$ScheduleName][0]

                        #region job step options
                        # Setting the options for the job schedule
                        if ($NewName) {
                            Write-Message -Message "Setting job schedule name to $NewName for schedule $ScheduleName" -Level Verbose
                            $JobSchedule.Rename($NewName)
                        }

                        if ($Enabled) {
                            Write-Message -Message "Setting job schedule to enabled for schedule $ScheduleName" -Level Verbose
                            $JobSchedule.IsEnabled = $true
                        }

                        if ($Disabled) {
                            Write-Message -Message "Setting job schedule to disabled for schedule $ScheduleName" -Level Verbose
                            $JobSchedule.IsEnabled = $false
                        }

                        if ($FrequencyType -ge 1) {
                            Write-Message -Message "Setting job schedule frequency to $FrequencyType for schedule $ScheduleName" -Level Verbose
                            $JobSchedule.FrequencyTypes = $FrequencyType
                        }

                        if ($Interval -ge 1) {
                            Write-Message -Message "Setting job schedule frequency interval to $Interval for schedule $ScheduleName" -Level Verbose
                            $JobSchedule.FrequencyInterval = $Interval
                        }

                        if ($FrequencySubdayType -ge 1) {
                            Write-Message -Message "Setting job schedule frequency subday type to $FrequencySubdayType for schedule $ScheduleName" -Level Verbose
                            $JobSchedule.FrequencySubDayTypes = $FrequencySubdayType
                        }

                        if ($FrequencySubdayInterval -ge 1) {
                            Write-Message -Message "Setting job schedule frequency subday interval to $FrequencySubdayInterval for schedule $ScheduleName" -Level Verbose
                            $JobSchedule.FrequencySubDayInterval = $FrequencySubdayInterval
                        }

                        if (($FrequencyRelativeInterval -ge 1) -and ($FrequencyType -eq 32)) {
                            Write-Message -Message "Setting job schedule frequency relative interval to $FrequencyRelativeInterval for schedule $ScheduleName" -Level Verbose
                            $JobSchedule.FrequencyRelativeIntervals = $FrequencyRelativeInterval
                        }

                        if (($FrequencyRecurrenceFactor -ge 1) -and ($FrequencyType -in 8, 16, 32)) {
                            Write-Message -Message "Setting job schedule frequency recurrence factor to $FrequencyRecurrenceFactor for schedule $ScheduleName" -Level Verbose
                            $JobSchedule.FrequencyRecurrenceFactor = $FrequencyRecurrenceFactor
                        }

                        if ($StartDate) {
                            $StartDate = $StartDate.Insert(6, '-').Insert(4, '-')
                            Write-Message -Message "Setting job schedule start date to $StartDate for schedule $ScheduleName" -Level Verbose
                            $JobSchedule.StartDate = $StartDate
                        }

                        if ($EndDate) {
                            $EndDate = $EndDate.Insert(6, '-').Insert(4, '-')
                            Write-Message -Message "Setting job schedule end date to $EndDate for schedule $ScheduleName" -Level Verbose
                            $JobSchedule.EndDate = $EndDate
                        }

                        if ($StartTime) {
                            $StartTime = $StartTime.Insert(4, ':').Insert(2, ':')
                            Write-Message -Message "Setting job schedule start time to $StartTime for schedule $ScheduleName" -Level Verbose
                            $JobSchedule.ActiveStartTimeOfDay = $StartTime
                        }

                        if ($EndTime) {
                            $EndTime = $EndTime.Insert(4, ':').Insert(2, ':')
                            Write-Message -Message "Setting job schedule end time to $EndTime for schedule $ScheduleName" -Level Verbose
                            $JobSchedule.ActiveStartTimeOfDay = $EndTime
                        }
                        #endregion job step options

                        # Execute the query
                        if ($PSCmdlet.ShouldProcess($instance, "Changing the schedule $ScheduleName for job $j on $instance")) {
                            try {
                                # Excute the query and save the result
                                Write-Message -Message "Changing the schedule $ScheduleName for job $j" -Level Verbose

                                $JobSchedule.Alter()

                            }
                            catch {
                                Stop-Function -Message "Something went wrong changing the schedule" -Target $instance -ErrorRecord $_ -Continue
                                return
                            }
                        }
                    }
                }
            } # foreach object job
        } # foreach object instance
    } # process

    end {
        if (Test-FunctionInterrupt) { return }
        Write-Message -Message "Finished changing the job schedule(s)" -Level Verbose
    }
}
tools\dbatools\functions\Set-DbaCmConnection.ps1
function Set-DbaCmConnection {
    <#
        .SYNOPSIS
            Configures a connection object for use in remote computer management.

        .DESCRIPTION
            Configures a connection object for use in remote computer management.
            This function will either create new records for computers that have no connection registered so far, or it will configure existing connections if already present.

            As such it can be handy in making bulk-edits on connections or manually adjusting some settings.

        .PARAMETER ComputerName
            The computer to build the connection object for.

        .PARAMETER Credential
            The credential to register.

        .PARAMETER UseWindowsCredentials
            Whether using the default windows credentials is legit.
            Not setting this will not exclude using windows credentials, but only not pre-confirm them as working.

        .PARAMETER OverrideExplicitCredential
            Setting this will enable the credential override.
            The override will cause the system to ignore explicitly specified credentials, so long as known, good credentials are available.

        .PARAMETER OverrideConnectionPolicy
            Setting this will configure the connection policy override.
            By default, global configurations enforce, which connection type is available at all and which is disabled.

        .PARAMETER DisabledConnectionTypes
            Exlicitly disable connection types.
            These types will then not be used for connecting to the computer.

        .PARAMETER DisableBadCredentialCache
            Will prevent the caching of credentials if set to true.

        .PARAMETER DisableCimPersistence
            Will prevent Cim-Sessions to be reused.

        .PARAMETER DisableCredentialAutoRegister
            Will prevent working credentials from being automatically cached

        .PARAMETER EnableCredentialFailover
            Will enable automatic failing over to known to work credentials, when using bad credentials.
            By default, passing bad credentials will cause the Computer Management functions to interrupt with a warning (Or exception if in silent mode).

        .PARAMETER WindowsCredentialsAreBad
            Will prevent the windows credentials of the currently logged on user from being used for the remote connection.

        .PARAMETER CimWinRMOptions
            Specify a set of options to use when connecting to the target computer using CIM over WinRM.
            Use 'New-CimSessionOption' to create such an object.

        .PARAMETER CimDCOMOptions
            Specify a set of options to use when connecting to the target computer using CIM over DCOM.
            Use 'New-CimSessionOption' to create such an object.

        .PARAMETER AddBadCredential
            Adds credentials to the bad credential cache.
            These credentials will not be used when connecting to the target remote computer.

        .PARAMETER RemoveBadCredential
            Removes credentials from the bad credential cache.

        .PARAMETER ClearBadCredential
            Clears the cache of credentials that didn't worked.
            Will be applied before adding entries to the credential cache.

        .PARAMETER ClearCredential
            Clears the cache of credentials that worked.
            Will be applied before adding entries to the credential cache.

        .PARAMETER ResetCredential
            Resets all credential-related caches:
            - Clears bad credential cache
            - Removes last working credential
            - Un-Confirms the windows credentials as working
            - Un-Confirms the windows credentials as not working

            Automatically implies the parameters -ClearCredential and -ClearBadCredential. Using them together is redundant.
            Will be applied before adding entries to the credential cache.

        .PARAMETER ResetConnectionStatus
            Restores all connection stati to default, as if no connection protocol had ever been tested.

        .PARAMETER ResetConfiguration
            Restores the configuration back to system default.
            Configuration elements are the basic behavior controlling settings, such as whether to cache bad credentials, etc.
            These can be configured globally using the dbatools configuration system and overridden locally on a per-connection basis.
            For a list of all available settings, use "Get-DbaConfig -Module ComputerManagement".

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Fred Winmann (@FredWeinmann)
            Tags: ComputerManagement, CIM

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/set-DbaCmConnection

        .EXAMPLE
            Get-DbaCmConnection sql2014 | Set-DbaCmConnection -ClearBadCredential -UseWindowsCredentials

            Retrieves the already existing connection to sql2014, removes the list of not working credentials and configures it to default to the credentials of the logged on user.

        .EXAMPLE
            Get-DbaCmConnection | Set-DbaCmConnection -RemoveBadCredential $cred
            Removes the credentials stored in $cred from all connections' list of "known to not work" credentials.
            Handy to update changes in privilege.

        .EXAMPLE
            Get-DbaCmConnection | Export-Clixml .\connections.xml
            Import-Clixml .\connections.xml | Set-DbaCmConnection -ResetConfiguration

            At first, the current cached connections are stored in an xml file. At a later time - possibly in the profile when starting the console again - those connections are imported again and applied again to the connection cache.

            In this example, the configuration settings will also be reset, since after reimport those will be set to explicit, rather than deriving them from the global settings.
            In many cases, using the default settings is desirable. For specific settings, use New-DbaCmConnection as part of the profile in order to explicitly configure a connection.
    #>
    [CmdletBinding(DefaultParameterSetName = 'Credential')]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Sqlcollaborative.Dbatools.Parameter.DbaCmConnectionParameter[]]
        $ComputerName = $env:COMPUTERNAME,

        [Parameter(ParameterSetName = "Credential")]
        [PSCredential]
        $Credential,

        [Parameter(ParameterSetName = "Windows")]
        [switch]
        $UseWindowsCredentials,

        [switch]
        $OverrideExplicitCredential,

        [switch]
        $OverrideConnectionPolicy,

        [Sqlcollaborative.Dbatools.Connection.ManagementConnectionType]
        $DisabledConnectionTypes = 'None',

        [switch]
        $DisableBadCredentialCache,

        [switch]
        $DisableCimPersistence,

        [switch]
        $DisableCredentialAutoRegister,

        [switch]
        $EnableCredentialFailover,

        [Parameter(ParameterSetName = "Credential")]
        [switch]
        $WindowsCredentialsAreBad,

        [Microsoft.Management.Infrastructure.Options.WSManSessionOptions]
        $CimWinRMOptions,

        [Microsoft.Management.Infrastructure.Options.DComSessionOptions]
        $CimDCOMOptions,

        [System.Management.Automation.PSCredential[]]
        $AddBadCredential,

        [System.Management.Automation.PSCredential[]]
        $RemoveBadCredential,

        [switch]
        $ClearBadCredential,

        [switch]
        $ClearCredential,

        [switch]
        $ResetCredential,

        [switch]
        $ResetConnectionStatus,

        [switch]
        $ResetConfiguration,

        [switch]
        [Alias('Silent')]$EnableException
    )

    BEGIN {
        Write-Message -Level InternalComment -Message "Starting execution"
        Write-Message -Level Verbose -Message "Bound parameters: $($PSBoundParameters.Keys -join ", ")"

        $disable_cache = Get-DbaConfigValue -Name 'ComputerManagement.Cache.Disable.All' -Fallback $false
    }
    PROCESS {
        foreach ($connectionObject in $ComputerName) {
            if (-not $connectionObject.Success) { Stop-Function -Message "Failed to interpret computername input: $($connectionObject.InputObject)" -Category InvalidArgument -Target $connectionObject.InputObject -Continue }
            Write-Message -Level VeryVerbose -Message "Processing computer: $($connectionObject.Connection.ComputerName)"

            $connection = $connectionObject.Connection

            if ($ResetConfiguration) {
                Write-Message -Level Verbose -Message "Resetting the configuration to system default"

                $connection.RestoreDefaultConfiguration()
            }

            if ($ResetConnectionStatus) {
                Write-Message -Level Verbose -Message "Resetting the connection status"

                $connection.CimRM = 'Unknown'
                $connection.CimDCOM = 'Unknown'
                $connection.Wmi = 'Unknown'
                $connection.PowerShellRemoting = 'Unknown'

                $connection.LastCimRM = New-Object System.DateTime(0)
                $connection.LastCimDCOM = New-Object System.DateTime(0)
                $connection.LastWmi = New-Object System.DateTime(0)
                $connection.LastPowerShellRemoting = New-Object System.DateTime(0)
            }

            if ($ResetCredential) {
                Write-Message -Level Verbose -Message "Resetting credentials"

                $connection.KnownBadCredentials.Clear()
                $connection.Credentials = $null
                $connection.UseWindowsCredentials = $false
                $connection.WindowsCredentialsAreBad = $false
            }
            else {
                if ($ClearBadCredential) {
                    Write-Message -Level Verbose -Message "Clearing bad credentials"

                    $connection.KnownBadCredentials.Clear()
                    $connection.WindowsCredentialsAreBad = $false
                }

                if ($ClearCredential) {
                    Write-Message -Level Verbose -Message "Clearing credentials"

                    $connection.Credentials = $null
                    $connection.UseWindowsCredentials = $false
                }
            }

            foreach ($badCred in $RemoveBadCredential) {
                $connection.RemoveBadCredential($badCred)
            }

            foreach ($badCred in $AddBadCredential) {
                $connection.AddBadCredential($badCred)
            }

            if (Test-Bound "Credential") { $connection.Credentials = $Credential }
            if ($UseWindowsCredentials) {
                $connection.Credentials = $null
                $connection.UseWindowsCredentials = $UseWindowsCredentials
            }
            if (Test-Bound "OverrideExplicitCredential") { $connection.OverrideExplicitCredential = $OverrideExplicitCredential }
            if (Test-Bound "DisabledConnectionTypes") { $connection.DisabledConnectionTypes = $DisabledConnectionTypes }
            if (Test-Bound "DisableBadCredentialCache") { $connection.DisableBadCredentialCache = $DisableBadCredentialCache }
            if (Test-Bound "DisableCimPersistence") { $connection.DisableCimPersistence = $DisableCimPersistence }
            if (Test-Bound "DisableCredentialAutoRegister") { $connection.DisableCredentialAutoRegister = $DisableCredentialAutoRegister }
            if (Test-Bound "EnableCredentialFailover") { $connection.DisableCredentialAutoRegister = $EnableCredentialFailover }
            if (Test-Bound "WindowsCredentialsAreBad") { $connection.WindowsCredentialsAreBad = $WindowsCredentialsAreBad }
            if (Test-Bound "CimWinRMOptions") { $connection.CimWinRMOptions = $CimWinRMOptions }
            if (Test-Bound "CimDCOMOptions") { $connection.CimDCOMOptions = $CimDCOMOptions }
            if (Test-Bound "OverrideConnectionPolicy") { $connection.OverrideConnectionPolicy = $OverrideConnectionPolicy }

            if (-not $disable_cache) {
                Write-Message -Level Verbose -Message "Writing connection to cache"
                [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::Connections[$connectionObject.Connection.ComputerName] = $connection
            }
            else { Write-Message -Level Verbose -Message "Skipping writing to cache, since the cache has been disabled!" }
            $connection
        }
    }
    END {
        Write-Message -Level InternalComment -Message "Stopping execution"
    }
}
tools\dbatools\functions\Set-DbaConfig.ps1
function Set-DbaConfig {
    <#
        .SYNOPSIS
            Sets configuration entries.

        .DESCRIPTION
            This function creates or changes configuration values.
            These are used in dbatools to provide dynamic configuration information outside the PowerShell variable system.

        .PARAMETER FullName
            The full name of a configuration element. Must be namespaced <Module>.<Name>.
            The name can have any number of sub-segments, in order to better group configurations thematically.

        .PARAMETER Name
            Name of the configuration entry. If an entry of exactly this non-casesensitive name already exists, its value will be overwritten.
            Duplicate names across different modules are possible and will be treated separately.
            If a name contains namespace notation and no module is set, the first namespace element will be used as module instead of name. Example:
            -Name "Nordwind.Server"
            Is Equivalent to
            -Name "Server" -Module "Nordwind"

        .PARAMETER Module
            This allows grouping configuration elements into groups based on the module/component they server.
            If this parameter is not set, the configuration element is stored under its name only, which increases the likelyhood of name conflicts in large environments.

        .PARAMETER Value
            The value to assign to the named configuration element.

        .PARAMETER Description
            Using this, the configuration setting is given a description, making it easier for a user to comprehend, what a specific setting is for.

        .PARAMETER Validation
            The name of the validation script used for input validation.
            These can be used to validate make sure that input is of the proper data type.
            New validation scripts can be registered using Register-DbaConfigValidation

        .PARAMETER Handler
            A scriptblock that is executed when a value is being set.
            Is only executed if the validation was successful (assuming there was a validation, of course)

        .PARAMETER Hidden
            Setting this parameter hides the configuration from casual discovery. Configurations with this set will only be returned by Get-Config, if the parameter "-Force" is used.
            This should be set for all system settings a user should have no business changing (e.g. for Infrastructure related settings such as mail server).

        .PARAMETER Default
            Setting this parameter causes the system to treat this configuration as a default setting. If the configuration already exists, no changes will be performed.
            Useful in scenarios where for some reason it is not practical to automatically set defaults before loading userprofiles.

        .PARAMETER Initialize
            Use this when setting configurations as part of module import.
            When initializing a configuration, it will only do a thing if the configuration hasn't already been initialized (So if you load the module multiple times or in multiple runspaces, it won't make a difference)
            Also, if there already was a non-initialized setting set for a given configuration, it will then try to set the old value again.
            This value will be processed by handlers, if any are set.

        .PARAMETER DisableValidation
            This parameters disables the input validation - if any - when processing a setting.
            Normally this shouldn't be circumvented, but just in case, it can be disabled.

        .PARAMETER DisableHandler
            Internal Use Only.
            This parameter disables the configuration handlers.
            Configuration handlers are designed to automatically validate and process input set to a config value, in addition to writing the value.
            In many cases, this is used to improve performance, by forking the value location also to a static C#-field, which is then used, rather than searching a Hashtable.
            Sometimes it may only be used to introduce input validation.
            During module import, some handlers are registered and many values written to configuration.
            However, some of those values actually are already set as default values within the library. Processing a handler will cost a few ms.
            Add up a couple dozen such events and the delay is very notable.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Config, Module
            Author: Friedrich Weinmann

        .EXAMPLE
            PS C:\> Set-DbaConfig -Name 'User' -Value "Friedrich" -Description "The user under which the show must go on."

            Creates a configuration entry named "User" with the value "Friedrich"

        .EXAMPLE
            PS C:\> Set-DbaConfig -Name 'mymodule.User' -Value "Friedrich" -Description "The user under which the show must go on." -Handler $scriptBlock -Initialize -Validation String

            Creates a configuration entry ...
            - Named "mymodule.user"
            - With the value "Friedrich"
            - It adds a description as noted
            - It registers the scriptblock stored in $scriptBlock as handler
            - It initializes the script. This block only executes the first time a it is run like this. Subsequent calls will be ignored.
            - It registers the basic string input type validator
            This is the default example for modules using the configuration system.
            Note: While the -Handler parameter is optional, it is important to add it at the initial initialize call, if you are planning to add it.
            Only then will the system validate previous settings (such as what a user might have placed in his user profile)

        .EXAMPLE
            PS C:\> Set-DbaConfig 'ConfigLink' 'https://www.example.com/config.xml' 'Company' -Hidden

            Creates a configuration entry named "ConfigLink" in the "Company" module with the value 'https://www.example.com/config.xml'.
            This entry is hidden from casual discovery using Get-Config.

        .EXAMPLE
            PS C:\> Set-DbaConfig 'Network.Firewall' '10.0.0.2' -Default

            Creates a configuration entry named "Firewall" in the "Network" module with the value '10.0.0.2'
            This is only set, if the setting does not exist yet. If it does, this command will apply no changes.
    #>
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
    [CmdletBinding(DefaultParameterSetName = "FullName")]
    Param (
        [Parameter(ParameterSetName = "FullName", Position = 0, Mandatory = $true)]
        [string]
        $FullName,

        [Parameter(ParameterSetName = "Module", Position = 1, Mandatory = $true)]
        [string]
        $Name,

        [Parameter(ParameterSetName = "Module", Position = 0)]
        [string]
        $Module,

        [Parameter(ParameterSetName = "FullName", Position = 1)]
        [Parameter(ParameterSetName = "Module", Position = 2)]
        [AllowNull()]
        [AllowEmptyCollection()]
        [AllowEmptyString()]
        $Value,

        [string]
        $Description,

        [string]
        $Validation,

        [System.Management.Automation.ScriptBlock]
        $Handler,

        [switch]
        $Hidden,

        [switch]
        $Default,

        [switch]
        $Initialize,

        [switch]
        $DisableValidation,

        [switch]
        $DisableHandler,

        [switch]
        $EnableException
    )

    #region Prepare Names
    if ($PSCmdlet.ParameterSetName -eq "FullName") {
        if (-not $FullName.Trim(".").Contains(".")) {
            Stop-Function -Message "Invalid Name: $FullName ! At least one '.' is required, to separate module from name" -EnableException $EnableException -Category InvalidArgument
            return
        }

        $Module = $FullName.Split(".")[0].ToLower().Trim(".")
        $Name = $FullName.Substring(($Module.Length + 1)).ToLower().Trim(".")
        $internalFullName = $FullName.ToLower().Trim(".")
    }
    else {
        $Name = $Name.ToLower().Trim(".")
        if ($Module) { $Module = $Module.ToLower().Trim(".") }

        if ((Test-Bound -ParameterName "Module" -Not) -and ($Name -match ".+\..+")) {
            $r = $Name | select-string "^(.+?)\..+" -AllMatches
            $Module = $r.Matches[0].Groups[1].Value
            $Name = $Name.Substring($Module.Length + 1)
        }
        elseif ((Test-Bound -ParameterName "Module" -Not) -and ($Name -notmatch ".+\..+")) {
            Stop-Function -Message "Invalid Name: $Name ! At least one '.' is required when not explicitly specifying a module name, to separate module from name" -EnableException $EnableException -Category InvalidArgument
            return
        }

        If ($Module) { $internalFullName = $Module, $Name -join "." }
        else { $internalFullName = $Name }
    }
    #endregion Prepare Names

    #region Prepare runtime and kill execution as needed
    if ([Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations.ContainsKey($internalFullName)) {
        $itExists = $true
        $itIsInitialized = [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations[$internalFullName].Initialized
        $itIsEnforced = [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations[$internalFullName].PolicyEnforced
    }
    else {
        $itExists = $false
        $itIsInitialized = $false
        $itIsEnforced = $false
    }

    if ($itExists -and $Default) { return }
    if ($itIsInitialized -and $Initialize) { return }
    if ($itIsEnforced -and (-not $Initialize)) {
        Stop-Function -Message "Could not update configuration due to policy settings: $internalFullName" -EnableException $EnableException -Category PermissionDenied
        return
    }

    if (Test-Bound -ParameterName "Validation") {
        if (-not ([Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Validation.Keys -contains $Validation.ToLower())) {
            Stop-Function -Message "Invalid validation name: $Validation. Supported validations: $([Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Validation.Keys -join ", ")" -Category InvalidArgument -Target $Name
            return
        }
    }
    #endregion Prepare runtime and kill execution as needed

    #region Initializing a configuration
    if ($Initialize) {
        if ($itExists) {
            $oldValue = [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations[$internalFullName].Value
            $cfg = [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations[$internalFullName]
        }
        else { $cfg = New-Object Sqlcollaborative.Dbatools.Configuration.Config }
        $cfg.Name = $Name
        $cfg.Module = $Module
        $cfg.Description = $Description
        $cfg.Value = $Value
        $cfg.Handler = $Handler
        if ($Validation) { $cfg.Validation = [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Validation[$Validation.ToLower()] }
        $cfg.Hidden = $Hidden
        $cfg.Initialized = $true
        [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations[$internalFullName] = $cfg

        if ($itExists) { Set-DbaConfig -Name $internalFullName -Value $oldValue }
    }
    #endregion Initializing a configuration

    #region Regular configuration update
    else {
        if (-not $itExists) {
            $cfg = New-Object Sqlcollaborative.Dbatools.Configuration.Config
            $cfg.Name = $Name
            $cfg.Module = $Module
            $cfg.Description = $Description
            $cfg.Handler = $Handler
            if ($Validation) { $cfg.Validation = [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Validation[$Validation.ToLower()] }
            $cfg.Hidden = $Hidden
            [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations[$internalFullName] = $cfg

            Set-DbaConfig -Name $internalFullName -Value $Value
            return
        }

        else {
            [Sqlcollaborative.Dbatools.Configuration.Config]$cfg = [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations[$internalFullName]
            if ((-not $DisableValidation) -and ($cfg.Validation) -and (Test-Bound -ParameterName "Value")) {
                $testResult = [scriptblock]::Create($cfg.Validation.ToString()).Invoke($Value)
                if (-not $TestResult.Success) {
                    Stop-Function -Message "Could not update configuration $internalFullName | Failed validation: $($testResult.Message)" -EnableException $EnableException -Category InvalidResult -Target $internalFullName
                    return
                }
                $Value = $testResult.Value
            }

            if (Test-Bound -ParameterName "Hidden") { [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations[$internalFullName].Hidden = $Hidden }
            if (Test-Bound -ParameterName "Value") { [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations[$internalFullName].Value = $Value }
            if (Test-Bound -ParameterName "Description") { [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations[$internalFullName].Description = $Description }
            if (Test-Bound -ParameterName "Handler") { [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations[$internalFullName].Handler = $Handler }
            if (Test-Bound -ParameterName "Validation") { [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations[$internalFullName].Validation = [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Validation[$Validation.ToLower()] }

            if ((-not $DisableHandler) -and ($cfg.Handler) -and (Test-Bound -ParameterName "Value")) {
                try { [scriptblock]::Create($cfg.Handler.ToString()).Invoke($Value) }
                catch {
                    Stop-Function -Message "Could not update configuration $internalFullName | Failed handling $_" -EnableException $EnableException -Category InvalidResult -Target $internalFullName
                    return
                }
            }
        }
    }
    #endregion Regular configuration update
}
tools\dbatools\functions\Set-DbaDatabaseOwner.ps1
function Set-DbaDatabaseOwner {
    <#
        .SYNOPSIS
            Sets database owners with a desired login if databases do not match that owner.

        .DESCRIPTION
            This function will alter database ownership to match a specified login if their current owner does not match the target login. By default, the target login will be 'sa', but the function will allow the user to specify a different login for  ownership. The user can also apply this to all databases or only to a select list of databases (passed as either a comma separated list or a string array).

            Best Practice reference: http://weblogs.sqlteam.com/dang/archive/2008/01/13/Database-Owner-Troubles.aspx

        .PARAMETER SqlInstance
            Specifies the SQL Server instance(s) to scan.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies the database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies the database(s) to exclude from processing. Options for this list are auto-populated from the server.

        .PARAMETER TargetLogin
            Specifies the login that you wish check for ownership. This defaults to 'sa' or the sysadmin name if sa was renamed. This must be a valid security principal which exists on the target server.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database, Owner, DbOwner
            Author: Michael Fal (@Mike_Fal), http://mikefal.net

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Set-DbaDatabaseOwner

        .EXAMPLE
            Set-DbaDatabaseOwner -SqlInstance localhost

            Sets database owner to 'sa' on all databases where the owner does not match 'sa'.

        .EXAMPLE
            Set-DbaDatabaseOwner -SqlInstance localhost -TargetLogin DOMAIN\account

            Sets the database owner to DOMAIN\account on all databases where the owner does not match DOMAIN\account.

        .EXAMPLE
            Set-DbaDatabaseOwner -SqlInstance sqlserver -Database db1, db2

            Sets database owner to 'sa' on the db1 and db2 databases if their current owner does not match 'sa'.
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Alias("Login")]
        [string]$TargetLogin,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance."
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure." -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            # dynamic sa name for orgs who have changed their sa name
            if (!$TargetLogin) {
                $TargetLogin = ($server.logins | Where-Object { $_.id -eq 1 }).Name
            }

            #Validate login
            if (($server.Logins.Name) -notcontains $TargetLogin) {
                Stop-Function -Message "$TargetLogin is not a valid login on $instance. Moving on." -Continue -EnableException $EnableException
            }

            #Owner cannot be a group
            $TargetLoginObject = $server.Logins | where-object {$PSItem.Name -eq $TargetLogin }| Select-Object -property  Name, LoginType
            if ($TargetLoginObject.LoginType -eq 'WindowsGroup') {
                Stop-Function -Message "$TargetLogin is a group, therefore can't be set as owner. Moving on." -Continue -EnableException $EnableException
            }

            #Get database list. If value for -Database is passed, massage to make it a string array.
            #Otherwise, use all databases on the instance where owner not equal to -TargetLogin
            #use where owner and target login do not match
            #exclude system dbs
            $dbs = $server.Databases | Where-Object { $_.IsAccessible -and $_.Owner -ne $TargetLogin -and @('master', 'model', 'msdb', 'tempdb', 'distribution') -notcontains $_.Name}

            #filter collection based on -Databases/-Exclude parameters
            if ($Database) {
                $dbs = $dbs | Where-Object { $Database -contains $_.Name }
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object { $ExcludeDatabase -notcontains $_.Name }
            }

            Write-Message -Level Verbose -Message "Updating $($dbs.Count) database(s)."
            foreach ($db in $dbs) {
                $dbname = $db.name
                if ($PSCmdlet.ShouldProcess($instance, "Setting database owner for $dbname to $TargetLogin")) {
                    try {
                        Write-Message -Level Verbose -Message "Setting database owner for $dbname to $TargetLogin on $instance."
                        # Set database owner to $TargetLogin (default 'sa')
                        # Ownership validations checks

                        #Database is online and accessible
                        if ($db.Status -notmatch 'Normal') {
                            Write-Message -Level Warning -Message "$dbname on $instance is in a  $($db.Status) state and can not be altered. It will be skipped."
                        }
                        #Database is updatable, not read-only
                        elseif ($db.IsUpdateable -eq $false) {
                            Write-Message -Level Warning -Message "$dbname on $instance is not in an updateable state and can not be altered. It will be skipped."
                        }
                        #Is the login mapped as a user? Logins already mapped in the database can not be the owner
                        elseif ($db.Users.name -contains $TargetLogin) {
                            Write-Message -Level Warning -Message "$dbname on $instance has $TargetLogin as a mapped user. Mapped users can not be database owners."
                        }
                        else {
                            $db.SetOwner($TargetLogin)
                            [PSCustomObject]@{
                                ComputerName = $server.ComputerName
                                InstanceName = $server.ServiceName
                                SqlInstance  = $server.DomainInstanceName
                                Database     = $db
                                Owner        = $TargetLogin
                            }
                        }
                    }
                    catch {
                        Stop-Function -Message "Failure updating owner." -ErrorRecord $_ -Target $instance -Continue
                    }
                }
            }
        }
    }
}

tools\dbatools\functions\Set-DbaDatabaseState.ps1
#ValidationTags#Messaging,FlowControl,Pipeline#
function Set-DbaDatabaseState {
    <#
        .SYNOPSIS
            Sets various options for databases, hereby called "states"

        .DESCRIPTION
            Sets some common "states" on databases:
            - "RW" options (ReadOnly, ReadWrite)
            - "Status" options (Online, Offline, Emergency, plus a special "Detached")
            - "Access" options (SingleUser, RestrictedUser, MultiUser)

            Returns an object with SqlInstance, Database, RW, Status, Access, Notes

            Notes gets filled when something went wrong setting the state

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to

        .PARAMETER SqlCredential
            Credential object used to connect to the SQL Server as a different user

        .PARAMETER Database
            The database(s) to process - this list is auto-populated from the server. if unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto-populated from the server

        .PARAMETER AllDatabases
            This is a parameter that was included for safety, so you don't accidentally set options on all databases without specifying

        .PARAMETER ReadOnly
            RW Option : Sets the database as READ_ONLY

        .PARAMETER ReadWrite
            RW Option : Sets the database as READ_WRITE

        .PARAMETER Online
            Status Option : Sets the database as ONLINE

        .PARAMETER Offline
            Status Option : Sets the database as OFFLINE

        .PARAMETER Emergency
            Status Option : Sets the database as EMERGENCY

        .PARAMETER Detached
            Status Option : Detaches the database

        .PARAMETER SingleUser
            Access Option : Sets the database as SINGLE_USER

        .PARAMETER RestrictedUser
            Access Option : Sets the database as RESTRICTED_USER

        .PARAMETER MultiUser
            Access Option : Sets the database as MULTI_USER

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER Force
            For most options, this translates to instantly rolling back any open transactions
            that may be stopping the process.
            For -Detached it is required to break mirroring and Availability Groups

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER InputObject
            Accepts piped database objects

        .NOTES
            Tags: Database, State
            Author: niphlod
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Set-DbaDatabaseState

        .EXAMPLE
            Set-DbaDatabaseState -SqlInstance sqlserver2014a -Database HR -Offline

            Sets the HR database as OFFLINE

        .EXAMPLE
            Set-DbaDatabaseState -SqlInstance sqlserver2014a -AllDatabases -Exclude HR -Readonly -Force

            Sets all databases of the sqlserver2014a instance, except for HR, as READ_ONLY

        .EXAMPLE
            Get-DbaDatabaseState -SqlInstance sql2016 | Where-Object Status -eq 'Offline' | Set-DbaDatabaseState -Online

            Finds all offline databases and sets them to online

        .EXAMPLE
            Set-DbaDatabaseState -SqlInstance sqlserver2014a -Database HR -SingleUser

            Sets the HR database as SINGLE_USER

        .EXAMPLE
            Set-DbaDatabaseState -SqlInstance sqlserver2014a -Database HR -SingleUser -Force

            Sets the HR database as SINGLE_USER, dropping all other connections (and rolling back open transactions)

        .EXAMPLE
            Get-DbaDatabase -SqlInstance sqlserver2014a -Database HR | Set-DbaDatabaseState -SingleUser -Force

            Gets the databases from Get-DbaDatabase, and sets them as SINGLE_USER, dropping all other connections (and rolling back open transactions)
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    Param (
        [parameter(Mandatory = $true, ValueFromPipelineByPropertyName, ParameterSetName = "Server")]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$AllDatabases,
        [switch]$ReadOnly,
        [switch]$ReadWrite,
        [switch]$Online,
        [switch]$Offline,
        [switch]$Emergency,
        [switch]$Detached,
        [switch]$SingleUser,
        [switch]$RestrictedUser,
        [switch]$MultiUser,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException,
        [parameter(Mandatory = $true, ValueFromPipeline, ParameterSetName = "Database")]
        [PsCustomObject[]]$InputObject
    )

    begin {
        function Get-WrongCombo($optset, $allparams) {
            $x = 0
            foreach ($opt in $optset) {
                if ($allparams.ContainsKey($opt)) { $x += 1 }
            }
            if ($x -gt 1) {
                $msg = $optset -Join ',-'
                $msg = "You can only specify one of: -" + $msg
                throw $msg
            }
        }

        function Edit-DatabaseState($sqlinstance, $dbname, $opt, $immediate = $false) {
            $warn = $null
            $sql = "ALTER DATABASE [$dbname] SET $opt"
            if ($immediate) {
                $sql += " WITH ROLLBACK IMMEDIATE"
            }
            else {
                $sql += " WITH NO_WAIT"
            }
            try {
                Write-Message -Level System -Message $sql
                if ($immediate) {
                    # this can be helpful only for SINGLE_USER databases
                    # but since $immediate is called, it does no more harm
                    # than the immediate rollback
                    $sqlinstance.KillAllProcesses($dbname)
                }
                $null = $sqlinstance.Query($sql)
            }
            catch {
                $warn = "Failed to set '$dbname' to $opt"
                Write-Message -Level Warning -Message $warn
            }
            return $warn
        }

        $StatusHash = @{
            'Offline'       = 'OFFLINE'
            'Normal'        = 'ONLINE'
            'EmergencyMode' = 'EMERGENCY'
        }

        function Get-DbState($databaseName, $dbStatuses) {
            $base = $dbStatuses | Where-Object DatabaseName -ceq $databaseName
            foreach ($status in $StatusHash.Keys) {
                if ($base.Status -match $status) {
                    $base.Status = $StatusHash[$status]
                    break
                }
            }
            return $base
        }

        $RWExclusive = @('ReadOnly', 'ReadWrite')
        $StatusExclusive = @('Online', 'Offline', 'Emergency', 'Detached')
        $AccessExclusive = @('SingleUser', 'RestrictedUser', 'MultiUser')
        $allparams = $PSBoundParameters
        try {
            Get-WrongCombo -optset $RWExclusive -allparams $allparams
        }
        catch {
            Stop-Function -Message $_
            return
        }
        try {
            Get-WrongCombo -optset $StatusExclusive -allparams $allparams
        }
        catch {
            Stop-Function -Message $_
            return
        }
        try {
            Get-WrongCombo -optset $AccessExclusive -allparams $allparams
        }
        catch {
            Stop-Function -Message $_
            return
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }
        $dbs = @()
        if (!$Database -and !$AllDatabases -and !$InputObject -and !$ExcludeDatabase) {
            Stop-Function -Message "You must specify a -AllDatabases or -Database to continue"
            return
        }

        if ($InputObject) {
            if ($InputObject.Database) {
                # comes from Get-DbaDatabaseState
                $dbs += $InputObject.Database
            }
            elseif ($InputObject.Name) {
                # comes from Get-DbaDatabase
                $dbs += $InputObject
            }
        }
        else {
            foreach ($instance in $SqlInstance) {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                try {
                    $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
                }
                catch {
                    Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                }
                $all_dbs = $server.Databases
                $dbs += $all_dbs | Where-Object { @('master', 'model', 'msdb', 'tempdb', 'distribution') -notcontains $_.Name }

                if ($database) {
                    $dbs = $dbs | Where-Object { $database -contains $_.Name }
                }
                if ($ExcludeDatabase) {
                    $dbs = $dbs | Where-Object { $ExcludeDatabase -notcontains $_.Name }
                }
            }
        }

        # need to pick up here
        foreach ($db in $dbs) {
            if ($db.Name -in @('master', 'model', 'msdb', 'tempdb', 'distribution')) {
                Write-Message -Level Warning -Message "Database $db is a system one, skipping"
                Continue
            }
            $dbStatuses = @{}
            $server = $db.Parent
            if ($server -notin $dbStatuses.Keys) {
                $dbStatuses[$server] = Get-DbaDatabaseState -SqlInstance $server
            }

            # normalizing properties returned by SMO to something more "fixed"
            $db_status = Get-DbState -DatabaseName $db.Name -dbStatuses $dbStatuses[$server]


            $warn = @()

            if ($db.DatabaseSnapshotBaseName.Length -gt 0) {
                Write-Message -Level Warning -Message "Database $db is a snapshot, skipping"
                Continue
            }

            if ($ReadOnly -eq $true) {
                if ($db_status.RW -eq 'READ_ONLY') {
                    Write-Message -Level VeryVerbose -Message "Database $db is already READ_ONLY"
                }
                else {
                    if ($Pscmdlet.ShouldProcess($server, "Set $db to READ_ONLY")) {
                        Write-Message -Level VeryVerbose -Message "Setting database $db to READ_ONLY"
                        $partial = Edit-DatabaseState -sqlinstance $server -dbname $db.Name -opt "READ_ONLY" -immediate $Force
                        $warn += $partial
                        if (!$partial) {
                            $db_status.RW = 'READ_ONLY'
                        }
                    }
                }
            }

            if ($ReadWrite -eq $true) {
                if ($db_status.RW -eq 'READ_WRITE') {
                    Write-Message -Level VeryVerbose -Message "Database $db is already READ_WRITE"
                }
                else {
                    if ($Pscmdlet.ShouldProcess($server, "Set $db to READ_WRITE")) {
                        Write-Message -Level VeryVerbose -Message "Setting database $db to READ_WRITE"
                        $partial = Edit-DatabaseState -sqlinstance $server -dbname $db.Name -opt "READ_WRITE" -immediate $Force
                        $warn += $partial
                        if (!$partial) {
                            $db_status.RW = 'READ_WRITE'
                        }
                    }
                }
            }

            if ($Online -eq $true) {
                if ($db_status.Status -eq 'ONLINE') {
                    Write-Message -Level VeryVerbose -Message "Database $db is already ONLINE"
                }
                else {
                    if ($Pscmdlet.ShouldProcess($server, "Set $db to ONLINE")) {
                        Write-Message -Level VeryVerbose -Message "Setting database $db to ONLINE"
                        $partial = Edit-DatabaseState -sqlinstance $server -dbname $db.Name -opt "ONLINE" -immediate $Force
                        $warn += $partial
                        if (!$partial) {
                            $db_status.Status = 'ONLINE'
                        }
                    }
                }
            }

            if ($Offline -eq $true) {
                if ($db_status.Status -eq 'OFFLINE') {
                    Write-Message -Level VeryVerbose -Message "Database $db is already OFFLINE"
                }
                else {
                    if ($Pscmdlet.ShouldProcess($server, "Set $db to OFFLINE")) {
                        Write-Message -Level VeryVerbose -Message "Setting database $db to OFFLINE"
                        $partial = Edit-DatabaseState -sqlinstance $server -dbname $db.Name -opt "OFFLINE" -immediate $Force
                        $warn += $partial
                        if (!$partial) {
                            $db_status.Status = 'OFFLINE'
                        }
                    }
                }
            }

            if ($Emergency -eq $true) {
                if ($db_status.Status -eq 'EMERGENCY') {
                    Write-Message -Level VeryVerbose -Message "Database $db is already EMERGENCY"
                }
                else {
                    if ($Pscmdlet.ShouldProcess($server, "Set $db to EMERGENCY")) {
                        Write-Message -Level VeryVerbose -Message "Setting database $db to EMERGENCY"
                        $partial = Edit-DatabaseState -sqlinstance $server -dbname $db.Name -opt "EMERGENCY" -immediate $Force
                        if (!$partial) {
                            $db_status.Status = 'EMERGENCY'
                        }
                    }
                }
            }

            if ($SingleUser -eq $true) {
                if ($db_status.Access -eq 'SINGLE_USER') {
                    Write-Message -Level VeryVerbose -Message "Database $db is already SINGLE_USER"
                }
                else {
                    if ($Pscmdlet.ShouldProcess($server, "Set $db to SINGLE_USER")) {
                        Write-Message -Level VeryVerbose -Message "Setting $db to SINGLE_USER"
                        $partial = Edit-DatabaseState -sqlinstance $server -dbname $db.Name -opt "SINGLE_USER" -immediate $Force
                        if (!$partial) {
                            $db_status.Access = 'SINGLE_USER'
                        }
                    }
                }
            }

            if ($RestrictedUser -eq $true) {
                if ($db_status.Access -eq 'RESTRICTED_USER') {
                    Write-Message -Level VeryVerbose -Message "Database $db is already RESTRICTED_USER"
                }
                else {
                    if ($Pscmdlet.ShouldProcess($server, "Set $db to RESTRICTED_USER")) {
                        Write-Message -Level VeryVerbose -Message "Setting $db to RESTRICTED_USER"
                        $partial = Edit-DatabaseState -sqlinstance $server -dbname $db.Name -opt "RESTRICTED_USER" -immediate $Force
                        if (!$partial) {
                            $db_status.Access = 'RESTRICTED_USER'
                        }
                    }
                }
            }

            if ($MultiUser -eq $true) {
                if ($db_status.Access -eq 'MULTI_USER') {
                    Write-Message -Level VeryVerbose -Message "Database $db is already MULTI_USER"
                }
                else {
                    if ($Pscmdlet.ShouldProcess($server, "Set $db to MULTI_USER")) {
                        Write-Message -Level VeryVerbose -Message "Setting $db to MULTI_USER"
                        $partial = Edit-DatabaseState -sqlinstance $server -dbname $db.Name -opt "MULTI_USER" -immediate $Force
                        if (!$partial) {
                            $db_status.Access = 'MULTI_USER'
                        }
                    }
                }
            }

            if ($Detached -eq $true) {
                # Refresh info about database state here (before detaching)
                $db.Refresh()
                # we need to see what snaps are on the server, as base databases cannot be dropped
                $snaps = $server.Databases | Where-Object { $_.DatabaseSnapshotBaseName.Length -gt 0 }
                $snaps = $snaps.DatabaseSnapshotBaseName | Get-Unique
                if ($db.Name -in $snaps) {
                    Write-Message -Level Warning -Message "Database $db has snapshots, you need to drop them before detaching, skipping..."
                    Continue
                }
                if ($db.IsMirroringEnabled -eq $true -or $db.AvailabilityGroupName.Length -gt 0) {
                    if ($Force -eq $false) {
                        Write-Message -Level Warning -Message "Needs -Force to detach $db, skipping"
                        Continue
                    }
                }

                if ($db.IsMirroringEnabled) {
                    if ($Pscmdlet.ShouldProcess($server, "Break mirroring for $db")) {
                        try {
                            $db.ChangeMirroringState([Microsoft.SqlServer.Management.Smo.MirroringOption]::Off)
                            $db.Alter()
                            $db.Refresh()
                            Write-Message -Level VeryVerbose -Message "Broke mirroring for $db"
                        }
                        catch {
                            Stop-Function -Message "Could not break mirror for $db. Skipping." -ErrorRecord $_ -Target $server -Continue
                        }
                    }
                }

                if ($db.AvailabilityGroupName) {
                    $agname = $db.AvailabilityGroupName
                    if ($Pscmdlet.ShouldProcess($server, "Removing $db from AG [$agname]")) {
                        try {
                            $server.AvailabilityGroups[$db.AvailabilityGroupName].AvailabilityDatabases[$db.Name].Drop()
                            Write-Message -Level VeryVerbose -Message "Successfully removed $db from AG [$agname] on $server"
                        }
                        catch {
                            Stop-Function -Message "Could not remove $db from AG [$agname] on $server" -ErrorRecord $_ -Target $server -Continue
                        }
                    }
                }

                # DBA 101 should encourage detaching just OFFLINE databases
                # we can do that here
                if ($Pscmdlet.ShouldProcess($server, "Detaching $db")) {
                    if ($db_status.Status -ne 'OFFLINE') {
                        $null = Edit-DatabaseState -sqlinstance $server -dbname $db.Name -opt "OFFLINE" -immediate $true
                    }
                    try {
                        $sql = "EXEC master.dbo.sp_detach_db N'$($db.Name)'"
                        Write-Message -Level System -Message $sql
                        $null = $server.Query($sql)
                        $db_status.Status = 'DETACHED'
                    }
                    catch {
                        Stop-Function -Message "Failed to detach $db" -ErrorRecord $_ -Target $server -Continue
                        $warn += "Failed to detach"
                    }

                }

            }
            if ($warn) {
                $warn = $warn | Get-Unique
                $warn = $warn -Join ';'
            }
            else {
                $warn = $null
            }
            if ($Detached -eq $true) {
                [PSCustomObject]@{
                    ComputerName = $server.ComputerName
                    InstanceName = $server.ServiceName
                    SqlInstance  = $server.DomainInstanceName
                    DatabaseName = $db.Name
                    RW           = $db_status.RW
                    Status       = $db_status.Status
                    Access       = $db_status.Access
                    Notes        = $warn
                    Database     = $db
                } | Select-DefaultView -ExcludeProperty Database
            }
            else {
                $db.Refresh()
                if ($null -eq $warn) {
                    # we avoid reenumerating properties
                    $newstate = $db_status
                }
                else {
                    $newstate = Get-DbState -databaseName $db.Name -dbStatuses $stateCache[$server]
                }

                [PSCustomObject]@{
                    ComputerName = $server.ComputerName
                    InstanceName = $server.ServiceName
                    SqlInstance  = $server.DomainInstanceName
                    DatabaseName = $db.Name
                    RW           = $newstate.RW
                    Status       = $newstate.Status
                    Access       = $newstate.Access
                    Notes        = $warn
                    Database     = $db
                } | Select-DefaultView -ExcludeProperty Database
            }
        }

    }

    end {

    }
}
tools\dbatools\functions\Set-DbaDbCompression.ps1
function Set-DbaDbCompression {
    <#
        .SYNOPSIS
            Sets tables and indexes with preferred compression setting.

        .DESCRIPTION
            This function sets the appropriate compression recommendation, determined either by using the Tiger Team's query or set to the CompressionType parameter.

            Remember Uptime is critical for the Tiger Team query, the longer uptime, the more accurate the analysis is.
            You would probably be best if you utilized Get-DbaUptime first, before running this command.

            Set-DbaDbCompression script derived from GitHub and the tigertoolbox
            (https://github.com/Microsoft/tigertoolbox/tree/master/Evaluate-Compression-Gains)

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process - this list is auto populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto populated from the server.

        .PARAMETER CompressionType
            Control the compression type applied. Default is 'Recommended' which uses the Tiger Team query to use the most appropriate setting per object. Other option is to compress all objects to either Row or Page.

        .PARAMETER MaxRunTime
            Will continue to alter tables and indexes for the given amount of minutes.

        .PARAMETER PercentCompression
            Will only work on the tables/indexes that have the calculated savings at and higher for the given number provided.

        .PARAMETER InputObject
            Takes the output of Test-DbaDbCompression as an object and applied compression based on those recommendations.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Jason Squires (@js_0505, [email protected])
            Tags: Compression, Table, Database
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Set-DbaDbCompression

        .EXAMPLE
            Set-DbaDbCompression -SqlInstance localhost -MaxRunTime 60 -PercentCompression 25

            Set the compression run time to 60 minutes and will start the compression of tables/indexes that have a difference of 25% or higher between current and recommended.

        .EXAMPLE
            Set-DbaDbCompression -SqlInstance ServerA -Database DBName -CompressionType Page

            Utilizes Page compression for all objects in DBName on ServerA with no time limit.

        .EXAMPLE
            Set-DbaDbCompression -SqlInstance ServerA -Database DBName -PercentCompression 25 | Out-GridView

            Will compress tables/indexes within the specified database that would show any % improvement with compression and with no time limit. The results will be piped into a nicely formated GridView.

        .EXAMPLE
            $testCompression = Test-DbaDbCompression -SqlInstance ServerA -Database DBName
            Set-DbaDbCompression -SqlInstance ServerA -Database DBName -InputObject $testCompression

            Gets the compression suggestions from Test-DbaDbCompression into a variable, this can then be reviewed and passed into Set-DbaDbCompression.

        .EXAMPLE
            $cred = Get-Credential sqladmin
            Set-DbaDbCompression -SqlInstance ServerA -ExcludeDatabase Database -SqlCredential $cred -MaxRunTime 60 -PercentCompression 25

            Set the compression run time to 60 minutes and will start the compression of tables/indexes for all databases except the specified excluded database. Only objects that have a difference of 25% or higher between current and recommended will be compressed.

        .EXAMPLE
            $servers = 'Server1','Server2'
            foreach ($svr in $servers)
            {
                Set-DbaDbCompression -SqlInstance $svr -MaxRunTime 60 -PercentCompression 25 | Export-Csv -Path C:\temp\CompressionAnalysisPAC.csv -Append
            }

            Set the compression run time to 60 minutes and will start the compression of tables/indexes across all listed servers that have a difference of 25% or higher between current and recommended. Output of command is exported to a csv.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess)]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [ValidateSet("Recommended", "Page", "Row", "None")]$CompressionType = "Recommended",
        [int]$MaxRunTime = 0,
        [int]$PercentCompression = 0,
        $InputObject,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        $starttime = Get-Date
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level VeryVerbose -Message "Connecting to $instance" -Target $instance
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failed to process Instance $instance" -ErrorRecord $_ -Target $instance -Continue
            }

            $Server.ConnectionContext.StatementTimeout = 0

            #The reason why we do this is because of SQL 2016 and they now allow for compression on standard edition.
            if ($server.EngineEdition -notmatch 'Enterprise' -and $server.VersionMajor -lt '13') {
                Stop-Function -Message "Only SQL Server Enterprise Edition supports compression on $server" -Target $server -Continue
            }
            try {
                $dbs = $server.Databases | Where-Object { $_.IsAccessible -and $_.IsSystemObject -eq 0}
                if ($Database) {
                    $dbs = $dbs | Where-Object { $_.Name -in $Database }
                }
                if ($ExcludeDatabase) {
                    $dbs = $dbs | Where-Object { $_.Name -NotIn $ExcludeDatabase }
                }
            }
            catch {
                Stop-Function -Message "Unable to gather list of databases for $instance" -Target $instance -ErrorRecord $_ -Continue
            }

            foreach ($db in $dbs) {
                try {
                    Write-Message -Level Verbose -Message "Querying $instance - $db"
                    if ($db.status -ne 'Normal' -or $db.IsAccessible -eq $false) {
                        Write-Message -Level Warning -Message "$db is not accessible" -Target $db
                        continue
                    }
                    if ($db.CompatibilityLevel -lt 'Version100') {
                        Stop-Function -Message "$db has a compatibility level lower than Version100 and will be skipped." -Target $db -Continue
                    }
                    if ($CompressionType -eq "Recommended") {
                        if (Test-Bound "InputObject") {
                            Write-Message -Level Verbose -Message "Using passed in compression suggestions"
                            $compressionSuggestion = $InputObject | Where-Object {$_.Database -eq $db.name}
                        }
                        else {
                            Write-Message -Level Verbose -Message "Testing database for compression suggestions for $instance.$db"
                            $compressionSuggestion = Test-DbaDbCompression -SqlInstance $server -Database $db.Name
                        }
                    }
                }
                catch {
                    Stop-Function -Message "Unable to query $instance - $db" -Target $db -ErrorRecord $_ -Continue
                }

                try {
                    if ($CompressionType -eq "Recommended") {
                        if ($Pscmdlet.ShouldProcess($db, "Applying suggested compression using results from Test-DbaDbCompression")) {
                            Write-Message -Level Verbose -Message "Applying suggested compression settings using Test-DbaDbCompression"
                            $results += $compressionSuggestion | Select-Object *, @{l = 'AlreadyProcesssed'; e = {"False"}}
                            foreach ($obj in ($results | Where-Object {$_.CompressionTypeRecommendation -ne 'NO_GAIN' -and $_.PercentCompression -ge $PercentCompression} | Sort-Object PercentCompression -Descending)) {
                                if ($MaxRunTime -ne 0 -and ($(get-date) - $starttime).TotalMinutes -ge $MaxRunTime) {
                                    Write-Message -Level Verbose -Message "Reached max run time of $MaxRunTime"
                                    break
                                }
                                if ($obj.indexId -le 1) {
                                    ##heaps and clustered indexes
                                    Write-Message -Level Verbose -Message "Applying $($obj.CompressionTypeRecommendation) compression to $($obj.Database).$($obj.Schema).$($obj.TableName)"
                                    $($server.Databases[$obj.Database].Tables[$obj.TableName, $obj.Schema].PhysicalPartitions | Where-Object {$_.PartitionNumber -eq $obj.Partition}).DataCompression = $($obj.CompressionTypeRecommendation)
                                    $server.Databases[$obj.Database].Tables[$obj.TableName, $($obj.Schema)].Rebuild()
                                    $obj.AlreadyProcesssed = "True"
                                }
                                else {
                                    ##nonclustered indexes
                                    Write-Message -Level Verbose -Message "Applying $($obj.CompressionTypeRecommendation) compression to $($obj.Database).$($obj.Schema).$($obj.TableName).$($obj.IndexName)"
                                    $($server.Databases[$obj.Database].Tables[$obj.TableName, $obj.Schema].Indexes[$obj.IndexName].PhysicalPartitions | Where-Object {$_.PartitionNumber -eq $obj.Partition}).DataCompression = $($obj.CompressionTypeRecommendation)
                                    $server.Databases[$obj.Database].Tables[$obj.TableName, $obj.Schema].Indexes[$obj.IndexName].Rebuild()
                                    $obj.AlreadyProcesssed = "True"
                                }
                                $obj
                            }
                        }
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($db, "Applying $CompressionType compression")) {
                            Write-Message -Level Verbose -Message "Applying $CompressionType compression to all objects in $($db.name)"
                            foreach ($obj in $server.Databases[$($db.name)].Tables | Where-Object {!$_.IsMemoryOptimized}) {
                                if ($MaxRunTime -ne 0 -and ($(get-date) - $starttime).TotalMinutes -ge $MaxRunTime) {
                                    Write-Message -Level Verbose -Message "Reached max run time of $MaxRunTime"
                                    break
                                }
                                foreach ($p in $($obj.PhysicalPartitions | Where-Object {$_.DataCompression -ne $CompressionType})) {
                                    Write-Message -Level Verbose -Message "Compressing table $($obj.Schema).$($obj.Name)"
                                    $($obj.PhysicalPartitions | Where-Object {$_.PartitionNumber -eq $P.PartitionNumber}).DataCompression = $CompressionType
                                    $obj.Rebuild()
                                    [pscustomobject]@{
                                        ComputerName                  = $server.ComputerName
                                        InstanceName                  = $server.ServiceName
                                        SqlInstance                   = $server.DomainInstanceName
                                        Database                      = $db.Name
                                        Schema                        = $obj.Schema
                                        TableName                     = $obj.Name
                                        IndexName                     = $null
                                        Partition                     = $p.PartitionNumber
                                        IndexID                       = 0
                                        IndexType                     = Switch ($obj.HasHeapIndex) {$false {"ClusteredIndex"} $true {"Heap"}}
                                        PercentScan                   = $null
                                        PercentUpdate                 = $null
                                        RowEstimatePercentOriginal    = $null
                                        PageEstimatePercentOriginal   = $null
                                        CompressionTypeRecommendation = $CompressionType.ToUpper()
                                        SizeCurrent                   = $null
                                        SizeRequested                 = $null
                                        PercentCompression            = $null
                                        AlreadyProcesssed             = "True"
                                    }
                                }

                                foreach ($index in $($obj.Indexes | Where-Object {!$_.IsMemoryOptimized})) {
                                    if ($MaxRunTime -ne 0 -and ($(get-date) - $starttime).TotalMinutes -ge $MaxRunTime) {
                                        Write-Message -Level Verbose -Message "Reached max run time of $MaxRunTime"
                                        break
                                    }
                                    foreach ($p in $($index.PhysicalPartitions | Where-Object {$_.DataCompression -ne $CompressionType})) {
                                        Write-Message -Level Verbose -Message "Compressing $($Index.IndexType) $($Index.Name) Partition $($p.PartitionNumber)"

                                        ## There is a bug in SMO where setting compression to None at the index level doesn't work
                                        ## Once this UserVoice item is fixed the workaround can be removed
                                        ## https://feedback.azure.com/forums/908035-sql-server/suggestions/34080112-data-compression-smo-bug
                                        if ($CompressionType -eq "None") {
                                            $query = "ALTER INDEX [$($index.Name)] ON $($index.Parent) REBUILD PARTITION = ALL WITH (DATA_COMPRESSION = $CompressionType)"
                                            $Server.Query($query, $db.Name)
                                        }
                                        else {
                                            $($Index.PhysicalPartitions | Where-Object {$_.PartitionNumber -eq $P.PartitionNumber}).DataCompression = $CompressionType
                                            $index.Rebuild()
                                        }

                                        [pscustomobject]@{
                                            ComputerName                  = $server.ComputerName
                                            InstanceName                  = $server.ServiceName
                                            SqlInstance                   = $server.DomainInstanceName
                                            Database                      = $db.Name
                                            Schema                        = $obj.Schema
                                            TableName                     = $obj.Name
                                            IndexName                     = $index.Name
                                            Partition                     = $p.PartitionNumber
                                            IndexID                       = $index.Id
                                            IndexType                     = $index.IndexType
                                            PercentScan                   = $null
                                            PercentUpdate                 = $null
                                            RowEstimatePercentOriginal    = $null
                                            PageEstimatePercentOriginal   = $null
                                            CompressionTypeRecommendation = $CompressionType.ToUpper()
                                            SizeCurrent                   = $null
                                            SizeRequested                 = $null
                                            PercentCompression            = $null
                                            AlreadyProcesssed             = "True"
                                        }
                                    }
                                }
                            }
                            foreach ($index in $($server.Databases[$($db.name)].Views | Where-Object {$_.Indexes}).Indexes) {
                                foreach ($p in $($index.PhysicalPartitions | Where-Object {$_.DataCompression -ne $CompressionType})) {
                                    Write-Message -Level Verbose -Message "Compressing $($index.IndexType) $($index.Name) Partition $($p.PartitionNumber)"

                                    ## There is a bug in SMO where setting compression to None at the index level doesn't work
                                    ## Once this UserVoice item is fixed the workaround can be removed
                                    ## https://feedback.azure.com/forums/908035-sql-server/suggestions/34080112-data-compression-smo-bug
                                    if ($CompressionType -eq "None") {
                                        $query = "ALTER INDEX [$($index.Name)] ON $($index.Parent) REBUILD PARTITION = ALL WITH (DATA_COMPRESSION = $CompressionType)"
                                        $query
                                        $Server.Query($query, $db.Name)
                                    }
                                    else {
                                        $($index.PhysicalPartitions | Where-Object {$_.PartitionNumber -eq $P.PartitionNumber}).DataCompression = $CompressionType
                                        $index.Rebuild()
                                    }

                                    [pscustomobject]@{
                                        ComputerName                  = $server.ComputerName
                                        InstanceName                  = $server.ServiceName
                                        SqlInstance                   = $server.DomainInstanceName
                                        Database                      = $db.Name
                                        Schema                        = $obj.Schema
                                        TableName                     = $obj.Name
                                        IndexName                     = $index.Name
                                        Partition                     = $p.PartitionNumber
                                        IndexID                       = $index.Id
                                        IndexType                     = $index.IndexType
                                        PercentScan                   = $null
                                        PercentUpdate                 = $null
                                        RowEstimatePercentOriginal    = $null
                                        PageEstimatePercentOriginal   = $null
                                        CompressionTypeRecommendation = $CompressionType.ToUpper()
                                        SizeCurrent                   = $null
                                        SizeRequested                 = $null
                                        PercentCompression            = $null
                                        AlreadyProcesssed             = "True"
                                    }
                                }
                            }
                        }
                    }
                }
                catch {
                    Stop-Function -Message "Compression failed for $instance - $db" -Target $db -ErrorRecord $_ -Continue
                }
            }
        }
    }
}
tools\dbatools\functions\Set-DbaDbQueryStoreOptions.ps1
#ValidationTags#CodeStyle,Messaging,FlowControl,Pipeline#
function Set-DbaDbQueryStoreOptions {
    <#
        .SYNOPSIS
            Configure Query Store settings for a specific or multiple databases.

        .DESCRIPTION
            Configure Query Store settings for a specific or multiple databases.

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to.

        .PARAMETER SqlCredential
            SqlCredential object used to connect to the SQL Server as a different user.

        .PARAMETER Database
            The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto-populated from the server

        .PARAMETER AllDatabases
            Run command against all user databases

        .PARAMETER State
            Set the state of the Query Store. Valid options are "ReadWrite", "ReadOnly" and "Off".

        .PARAMETER FlushInterval
            Set the flush to disk interval of the Query Store in seconds.

        .PARAMETER CollectionInterval
            Set the runtime statistics collection interval of the Query Store in minutes.

        .PARAMETER MaxSize
            Set the maximum size of the Query Store in MB.

        .PARAMETER CaptureMode
            Set the query capture mode of the Query Store. Valid options are "Auto" and "All".

        .PARAMETER CleanupMode
            Set the query cleanup mode policy. Valid options are "Auto" and "Off".

        .PARAMETER StaleQueryThreshold
            Set the stale query threshold in days.

        .PARAMETER WhatIf
            Shows what would happen if the command were to run

        .PARAMETER Confirm
            Prompts for confirmation of every step. For example:

            Are you sure you want to perform this action?
            Performing the operation "Changing Desired State" on target "pubs on SQL2016\VNEXT".
            [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: QueryStore
            Author: Enrico van de Laar ( @evdlaar )

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Set-DbaQueryStoreOptions

        .EXAMPLE
            Set-DbaDbQueryStoreOptions -SqlInstance ServerA\SQL -State ReadWrite -FlushInterval 600 -CollectionInterval 10 -MaxSize 100 -CaptureMode All -CleanupMode Auto -StaleQueryThreshold 100 -AllDatabases

            Configure the Query Store settings for all user databases in the ServerA\SQL Instance.

        .EXAMPLE
            Set-DbaDbQueryStoreOptions -SqlInstance ServerA\SQL -FlushInterval 600

            Only configure the FlushInterval setting for all Query Store databases in the ServerA\SQL Instance.

        .EXAMPLE
            Set-DbaDbQueryStoreOptions -SqlInstance ServerA\SQL -Database AdventureWorks -State ReadWrite -FlushInterval 600 -CollectionInterval 10 -MaxSize 100 -CaptureMode all -CleanupMode Auto -StaleQueryThreshold 100

            Configure the Query Store settings for the AdventureWorks database in the ServerA\SQL Instance.

        .EXAMPLE
            Set-DbaDbQueryStoreOptions -SqlInstance ServerA\SQL -Exclude AdventureWorks -State ReadWrite -FlushInterval 600 -CollectionInterval 10 -MaxSize 100 -CaptureMode all -CleanupMode Auto -StaleQueryThreshold 100

            Configure the Query Store settings for all user databases except the AdventureWorks database in the ServerA\SQL Instance.
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$AllDatabases,
        [ValidateSet('ReadWrite', 'ReadOnly', 'Off')]
        [string[]]$State,
        [int64]$FlushInterval,
        [int64]$CollectionInterval,
        [int64]$MaxSize,
        [ValidateSet('Auto', 'All')]
        [string[]]$CaptureMode,
        [ValidateSet('Auto', 'Off')]
        [string[]]$CleanupMode,
        [int64]$StaleQueryThreshold,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        $ExcludeDatabase += 'master', 'tempdb'
    }

    process {
        if (!$Database -and !$ExcludeDatabase -and !$AllDatabases) {
            Stop-Function -Message "You must specify a database(s) to execute against using either -Database, -ExcludeDatabase or -AllDatabases"
            return
        }

        if (!$State -and !$FlushInterval -and !$CollectionInterval -and !$MaxSize -and !$CaptureMode -and !$CleanupMode -and !$StaleQueryThreshold) {
            Stop-Function -Message "You must specify something to change."
            return
        }

        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 13

            }
            catch {
                Stop-Function -Message "Can't connect to $instance. Moving on." -Category InvalidOperation -InnerErrorRecord $_ -Target $instance -Continue
            }

            # We have to exclude all the system databases since they cannot have the Query Store feature enabled
            $dbs = Get-DbaDatabase -SqlInstance $server -ExcludeDatabase $ExcludeDatabase -Database $Database | Where-Object IsAccessible

            foreach ($db in $dbs) {
                Write-Message -Level Verbose -Message "Processing $($db.name) on $instance"

                if ($db.IsAccessible -eq $false) {
                    Write-Message -Level Warning -Message "The database $db on server $instance is not accessible. Skipping database."
                    continue
                }

                if ($State) {
                    if ($Pscmdlet.ShouldProcess("$db on $instance", "Changing DesiredState to $state")) {
                        $db.QueryStoreOptions.DesiredState = $State
                        $db.QueryStoreOptions.Alter()
                        $db.QueryStoreOptions.Refresh()
                    }
                }

                if ($db.QueryStoreOptions.DesiredState -eq "Off" -and (Test-Bound -Parameter State -Not)) {
                    Write-Message -Level Warning -Message "State is set to Off; cannot change values. Please update State to ReadOnly or ReadWrite."
                    continue
                }

                if ($FlushInterval) {
                    if ($Pscmdlet.ShouldProcess("$db on $instance", "Changing DataFlushIntervalInSeconds to $FlushInterval")) {
                        $db.QueryStoreOptions.DataFlushIntervalInSeconds = $FlushInterval
                    }
                }

                if ($CollectionInterval) {
                    if ($Pscmdlet.ShouldProcess("$db on $instance", "Changing StatisticsCollectionIntervalInMinutes to $CollectionInterval")) {
                        $db.QueryStoreOptions.StatisticsCollectionIntervalInMinutes = $CollectionInterval
                    }
                }

                if ($MaxSize) {
                    if ($Pscmdlet.ShouldProcess("$db on $instance", "Changing MaxStorageSizeInMB to $MaxSize")) {
                        $db.QueryStoreOptions.MaxStorageSizeInMB = $MaxSize
                    }
                }

                if ($CaptureMode) {
                    if ($Pscmdlet.ShouldProcess("$db on $instance", "Changing QueryCaptureMode to $CaptureMode")) {
                        $db.QueryStoreOptions.QueryCaptureMode = $CaptureMode
                    }
                }

                if ($CleanupMode) {
                    if ($Pscmdlet.ShouldProcess("$db on $instance", "Changing SizeBasedCleanupMode to $CleanupMode")) {
                        $db.QueryStoreOptions.SizeBasedCleanupMode = $CleanupMode
                    }
                }

                if ($StaleQueryThreshold) {
                    if ($Pscmdlet.ShouldProcess("$db on $instance", "Changing StaleQueryThresholdInDays to $StaleQueryThreshold")) {
                        $db.QueryStoreOptions.StaleQueryThresholdInDays = $StaleQueryThreshold
                    }
                }

                # Alter the Query Store Configuration
                if ($Pscmdlet.ShouldProcess("$db on $instance", "Altering Query Store configuration on database")) {
                    try {
                        $db.QueryStoreOptions.Alter()
                        $db.Alter()
                        $db.Refresh()
                    }
                    catch {
                        Stop-Function -Message "Could not modify configuration." -Category InvalidOperation -InnerErrorRecord $_ -Target $db -Continue
                    }
                }

                if ($Pscmdlet.ShouldProcess("$db on $instance", "Getting results from Get-DbaDbQueryStoreOptions")) {
                    # Display resulting changes
                    Get-DbaDbQueryStoreOptions -SqlInstance $server -Database $db.name -Verbose:$false
                }
            }
        }
    }
}

tools\dbatools\functions\Set-DbaDbRecoveryModel.ps1
function Set-DbaDbRecoveryModel {
    <#
        .SYNOPSIS
            Set-DbaDbRecoveryModel sets the Recovery Model.

        .DESCRIPTION
            Set-DbaDbRecoveryModel sets the Recovery Model for user databases.

        .PARAMETER SqlInstance
            The target SQL Server instance or instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process - this list is auto-populated from the server. if unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto-populated from the server

        .PARAMETER AllDatabases
            This is a parameter that was included for safety, so you don't accidentally set options on all databases without specifying

        .PARAMETER RecoveryModel
            Recovery Model to be set. Valid options are 'Simple', 'Full', 'BulkLogged'

            Details about the recovery models can be found here:
            https://docs.microsoft.com/en-us/sql/relational-databases/backup-restore/recovery-models-sql-server

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            Prompts for confirmation. For example:

            Are you sure you want to perform this action?
            Performing the operation "ALTER DATABASE [model] SET RECOVERY Full" on target "[model] on WERES14224".
            [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER InputObject
        A collection of databases (such as returned by Get-DbaDatabase)

        .NOTES
            Tags: Recovery, RecoveryModel, Simple, Full, Bulk, BulkLogged
            Author: Viorel Ciucu (@viorelciucu), https://www.cviorel.com

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Set-DbaDbRecoveryModel

        .EXAMPLE
            Set-DbaDbRecoveryModel -SqlInstance sql2014 -RecoveryModel BulkLogged -Database model -Confirm:$true -Verbose

            Sets the Recovery Model to BulkLogged for database [model] on SQL Server instance sql2014. User is requested to confirm the action.

        .EXAMPLE
            Get-DbaDatabase -SqlInstance sql2014 -Database TestDB | Set-DbaDbRecoveryModel -RecoveryModel Simple  -Confirm:$false

            Sets the Recovery Model to Simple for database [TestDB] on SQL Server instance sql2014. Confirmation is not required.

        .EXAMPLE
            Set-DbaDbRecoveryModel -SqlInstance sql2014 -RecoveryModel Simple -Database TestDB -Confirm:$false

            Sets the Recovery Model to Simple for database [TestDB] on SQL Server instance sql2014. Confirmation is not required.

        .EXAMPLE
            Set-DbaDbRecoveryModel -SqlInstance sql2014 -RecoveryModel Simple -AllDatabases -Confirm:$false

            Sets the Recovery Model to Simple for ALL uses databases MODEL database on SQL Server instance sql2014. Runs without asking for confirmation.

        .EXAMPLE
            Set-DbaDbRecoveryModel -SqlInstance sql2014 -RecoveryModel BulkLogged -Database TestDB1, TestDB2 -Confirm:$false -Verbose

            Sets the Recovery Model to BulkLogged for [TestDB1] and [TestDB2] databases on SQL Server instance sql2014. Runs without asking for confirmation.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess, ConfirmImpact = 'High')]
    param (
        [parameter(Mandatory, ParameterSetName = "Instance")]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory)]
        [ValidateSet('Simple', 'Full', 'BulkLogged')]
        [string]$RecoveryModel,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$AllDatabases,
        [switch]$EnableException,
        [parameter(Mandatory, ValueFromPipeline, ParameterSetName = "Pipeline")]
        [Microsoft.SqlServer.Management.Smo.Database[]]$InputObject
    )
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if (!$Database -and !$AllDatabases -and !$ExcludeDatabase) {
                Stop-Function -Message "You must specify -AllDatabases or -Database to continue"
                return
            }

            # We need to be able to change the RecoveryModel for model database
            $systemdbs = @("tempdb")
            $databases = $server.Databases | Where-Object { $systemdbs -notcontains $_.Name -and $_.IsAccessible }

            # filter collection based on -Database/-Exclude parameters
            if ($Database) {
                $databases = $databases | Where-Object Name -In $Database
            }

            if ($ExcludeDatabase) {
                $databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
            }

            if (!$databases) {
                Stop-Function -Message "The database(s) you specified do not exist on the instance $instance."
                return
            }

            $InputObject += $databases
        }

        foreach ($db in $InputObject) {
            if ($db.RecoveryModel -eq $RecoveryModel) {
                Stop-Function -Message "Recovery Model for database $db is already set to $RecoveryModel" -Category ConnectionError -Target $instance -Continue
            }
            else {
                $db.RecoveryModel = $RecoveryModel;
                if ($Pscmdlet.ShouldProcess("$db on $instance", "ALTER DATABASE $db SET RECOVERY $RecoveryModel")) {
                    $db.Alter()
                    Write-Message -Level Verbose -Message "Recovery Model set to $RecoveryModel for database $db"
                }
            }
            Get-DbaDbRecoveryModel -SqlInstance $db.Parent -Database $db.name
        }
    }
}
tools\dbatools\functions\Set-DbaErrorLogConfig.ps1
function Set-DbaErrorLogConfig {
    <#
        .SYNOPSIS
            Set the configuration for the ErrorLog on a given SQL Server instance

        .DESCRIPTION
            Sets the number of log files configured on all versions, and size in KB in SQL Server 2012+ and above.

            To set the Path to the ErrorLog, use Set-DbaStartupParameter -ErrorLog. Note that this command requires
            remote, administrative access to the Windows/WMI server, similar to SQL Configuration Manager.

        .PARAMETER SqlInstance
            The target SQL Server instance(s)

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER LogCount
            Integer value between 6 and 99 for setting the number of error log files to keep for SQL Server instance.

        .PARAMETER LogSize
            Integer value for the size in KB that you want the error log file to grow. This is feature only in SQL Server 2012 and higher. When the file reaches that limit SQL Server will roll the error log over.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Instance, ErrorLog
            Author: Shawn Melton (@wsmelton)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Set-DbaErrorLogConfig

       .EXAMPLE
            Set-DbaErrorLogConfig -SqlInstance sql2017,sql2014 -LogCount 25

            Sets the number of error log files to 25 on sql2017 and sql2014

        .EXAMPLE
            Set-DbaErrorLogConfig -SqlInstance sql2014 -LogSize 102400

            Sets the size of the error log file, before it rolls over, to 102400 KB (100 MB) on sql2014

        .EXAMPLE
            Set-DbaErrorLogConfig -SqlInstance sql2012 -LogCount 25 -LogSize 500

            Sets the number of error log files to 25 and size before it will roll over to 500 KB on sql2012
    #>
    [cmdletbinding(SupportsShouldProcess)]
    param(
        [Parameter(ValueFromPipelineByPropertyName, Mandatory)]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [ValidateRange(6, 99)]
        [int]$LogCount,
        [int]$LogSize,
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $currentNumLogs = $server.NumberOfLogFiles
            $currentLogSize = $server.ErrorLogSizeKb

            $collection = [PSCustomObject]@{
                ComputerName              = $server.ComputerName
                InstanceName              = $server.ServiceName
                SqlInstance               = $server.DomainInstanceName
                LogCount                  = $currentNumLogs
                LogSize                   = [dbasize]($currentLogSize * 1024)
            }
            if (Test-Bound -ParameterName 'LogSize') {
                if ($server.VersionMajor -lt 11) {
                    Stop-Function -Message "Size is cannot be set on $instance. SQL Server 2008 R2 and below not supported." -Continue
                }
                if ($LogSize -eq $currentLogSize) {
                    Write-Message -Level Warning -Message "The provided value for LogSize is already set to $LogSize KB on $instance"
                }
                else {
                    if ($PSCmdlet.ShouldProcess($server, "Updating log size from [$currentLogSize] to [$LogSize]")) {
                        try {
                            $server.ErrorLogSizeKb = $LogSize
                            $server.Alter()
                        }
                        catch {
                            Stop-Function -Message "Issue setting number of log files on $instance" -Target $instance -ErrorRecord $_ -Exception $_.Exception.InnerException.InnerException.InnerException -Continue
                        }
                    }
                    if ($PSCmdlet.ShouldProcess($server, "Output final results of setting error log size")) {
                        $server.Refresh()
                        $collection.LogSize = [dbasize]($server.ErrorLogSizeKb * 1024)
                    }
                }
            }

            if (Test-Bound -ParameterName 'LogCount') {
                if ($LogCount -eq $currentNumLogs) {
                    Write-Message -Level Warning -Message "The provided value for LogCount is already set to $LogCount on $instance"
                }
                else {
                    if ($PSCmdlet.ShouldProcess($server, "Setting number of logs from [$currentNumLogs] to [$LogCount]")) {
                        try {
                            $server.NumberOfLogFiles = $LogCount
                            $server.Alter()
                        }
                        catch {
                            Stop-Function -Message "Issue setting number of log files on $instance" -Target $instance -ErrorRecord $_ -Exception $_.Exception.InnerException.InnerException.InnerException -Continue
                        }
                    }
                    if ($PSCmdlet.ShouldProcess($server, "Output final results of setting number of log files")) {
                        $server.Refresh()
                        $collection.LogCount = $server.NumberOfLogFiles
                    }
                }
            }
            $collection
        }
    }
}
tools\dbatools\functions\Set-DbaJobOwner.ps1
function Set-DbaJobOwner {
    <#
        .SYNOPSIS
            Sets SQL Agent job owners with a desired login if jobs do not match that owner.

        .DESCRIPTION
            This function alters SQL Agent Job ownership to match a specified login if their current owner does not match the target login. By default, the target login will be 'sa', but the the user may specify a different login for ownership. This be applied to all jobs or only to a select collection of jobs.

            Best practice reference: http://sqlmag.com/blog/sql-server-tip-assign-ownership-jobs-sysadmin-account

        .NOTES
            Tags: Agent, Job
            Author: Michael Fal (@Mike_Fal), http://mikefal.net

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .PARAMETER SqlInstance
            Specifies the SQL Server instance(s) to scan.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Job
            Specifies the job(s) to process. Options for this list are auto-populated from the server. If unspecified, all jobs will be processed.

        .PARAMETER ExcludeJob
            Specifies the job(s) to exclude from processing. Options for this list are auto-populated from the server.

        .PARAMETER Login
            Specifies the login that you wish check for ownership. This defaults to 'sa' or the sysadmin name if sa was renamed. This must be a valid security principal which exists on the target server.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .LINK
            https://dbatools.io/Set-DbaJobOwner

        .EXAMPLE
            Set-DbaJobOwner -SqlInstance localhost

            Sets SQL Agent Job owner to sa on all jobs where the owner does not match sa.

        .EXAMPLE
            Set-DbaJobOwner -SqlInstance localhost -Login DOMAIN\account

            Sets SQL Agent Job owner to sa on all jobs where the owner does not match 'DOMAIN\account'. Note
            that Login must be a valid security principal that exists on the target server.

        .EXAMPLE
            Set-DbaJobOwner -SqlInstance localhost -Job job1, job2

            Sets SQL Agent Job owner to 'sa' on the job1 and job2 jobs if their current owner does not match 'sa'.

        .EXAMPLE
            'sqlserver','sql2016' | Set-DbaJobOwner

            Sets SQL Agent Job owner to sa on all jobs where the owner does not match sa on both sqlserver and sql2016.
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Jobs")]
        [object[]]$Job,
        [object[]]$ExcludeJob,
        [Alias("TargetLogin")]
        [string]$Login,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($servername in $SqlInstance) {
            #connect to the instance
            Write-Message -Level Verbose -Message "Connecting to $servername."
            $server = Connect-SqlInstance $servername -SqlCredential $SqlCredential

            # dynamic sa name for orgs who have changed their sa name
            if (!$Login) {
                $Login = ($server.logins | Where-Object { $_.id -eq 1 }).Name
            }

            #Validate login
            if (($server.Logins.Name) -notcontains $Login) {
                if ($SqlInstance.count -eq 1) {
                    throw -Message "Invalid login: $Login."
                }
                else {
                    Write-Message -Level Warning -Message "$Login is not a valid login on $servername. Moving on."
                    Continue
                }
            }

            if ($server.logins[$Login].LoginType -eq 'WindowsGroup') {
                throw "$Login is a Windows Group and can not be a job owner."
            }

            #Get database list. If value for -Job is passed, massage to make it a string array.
            #Otherwise, use all jobs on the instance where owner not equal to -TargetLogin
            Write-Message -Level Verbose -Message "Gathering jobs to update."

            if ($Job) {
                $jobcollection = $server.JobServer.Jobs | Where-Object { $_.OwnerLoginName -ne $Login -and $Job -contains $_.Name }
            }
            else {
                $jobcollection = $server.JobServer.Jobs | Where-Object { $_.OwnerLoginName -ne $Login }
            }

            if ($ExcludeJob) {
                $jobcollection = $jobcollection | Where-Object { $ExcludeJob -notcontains $_.Name }
            }

            Write-Message -Level Verbose -Message "Updating $($jobcollection.Count) job(s)."
            foreach ($j in $jobcollection) {
                $jobname = $j.name

                if ($PSCmdlet.ShouldProcess($servername, "Setting job owner for $jobname to $Login")) {
                    try {
                        Write-Message -Level Verbose -Message "Setting job owner for $jobname to $Login on $servername."
                        #Set job owner to $TargetLogin (default 'sa')
                        $j.OwnerLoginName = $Login
                        $j.Alter()
                    }
                    catch {
                        Stop-Function -Message "Issue setting job owner on $jobName." -Target $jobName -InnerErrorRecord $_ -Category InvalidOperation
                    }
                }
            }
        }
    }
}
tools\dbatools\functions\Set-DbaLogin.ps1
function Set-DbaLogin {

    <#
    .SYNOPSIS
    Set-DbaLogin makes it possible to make changes to one or more logins.

    .DESCRIPTION
    Set-DbaLogin will enable you to change the password, unlock, rename, disable or enable, deny or grant login privileges to the login.
    It's also possible to add or remove server roles from the login.

    .PARAMETER SqlInstance
    SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

    .PARAMETER SqlCredential
    Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

    .PARAMETER Login
    The login that needs to be changed

    .PARAMETER Password
    The new password for the login This can be either a credential or a secure string.

    .PARAMETER Unlock
    Switch to unlock an account. This will only be used in conjunction with the -Password parameter.
    The default is false.

    .PARAMETER MustChange
    Does the user need to change his/her password. This will only be used in conjunction with the -Password parameter.
    The default is false.

    .PARAMETER NewName
    The new name for the login.

    .PARAMETER Disable
    Disable the login

    .PARAMETER Enable
    Enable the login

    .PARAMETER DenyLogin
    Deny access to SQL Server

    .PARAMETER GrantLogin
    Grant access to SQL Server

    .PARAMETER PasswordPolicyEnforced
    Should the password policy be enforced.

    .PARAMETER AddRole
    Add one or more server roles to the login
    The following roles can be used "bulkadmin", "dbcreator", "diskadmin", "processadmin", "public", "securityadmin", "serveradmin", "setupadmin", "sysadmin".

    .PARAMETER RemoveRole
    Remove one or more server roles to the login
    The following roles can be used "bulkadmin", "dbcreator", "diskadmin", "processadmin", "public", "securityadmin", "serveradmin", "setupadmin", "sysadmin".

    .PARAMETER EnableException
    By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
    Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
    Original Author: Sander Stad (@sqlstad, sqlstad.nl)
    Tags: Login

    Website: https://dbatools.io
    Copyright: (C) Chrissy LeMaire, [email protected]
    License: MIT https://opensource.org/licenses/MIT

    .LINK
    https://dbatools.io/Set-DbaLogin

    .EXAMPLE
    $password = ConvertTo-SecureString "PlainTextPassword" -AsPlainText -Force
    $cred = New-Object System.Management.Automation.PSCredential ("username", $password)
    Set-DbaLogin -SqlInstance sql1 -Login login1 -Password $cred -Unlock -MustChange

    Set the new password for login1 using a credential, unlock the account and set the option
    that the usermust change password at next logon.

    .EXAMPLE
    Set-DbaLogin -SqlInstance sql1 -Login login1 -Enable

    Enable the login

    .EXAMPLE
    Set-DbaLogin -SqlInstance sql1 -Login login1, login2, login3, login4 -Enable

    Enable multiple logins

    .EXAMPLE
    Set-DbaLogin -SqlInstance sql1, sql2, sql3 -Login login1, login2, login3, login4 -Enable

    Enable multiple logins on multiple instances

    .EXAMPLE
    Set-DbaLogin -SqlInstance sql1 -Login login1 -Disable

    Disable the login

    .EXAMPLE
    Set-DbaLogin -SqlInstance sql1 -Login login1 -DenyLogin

    Deny the login to connect to the instance

    .EXAMPLE
    Set-DbaLogin -SqlInstance sql1 -Login login1 -GrantLogin

    Grant the login to connect to the instance

    .EXAMPLE
    Set-DbaLogin -SqlInstance sql1 -Login login1 -PasswordPolicyEnforced

    Enforces the password policy on a login

    .EXAMPLE
    Set-DbaLogin -SqlInstance sql1 -Login login1 -PasswordPolicyEnforced:$false

    Disables enforcement of the password policy on a login

    .EXAMPLE
    Set-DbaLogin -SqlInstance sql1 -Login test -AddRole serveradmin

    Add the server role "serveradmin" to the login

    .EXAMPLE
    Set-DbaLogin -SqlInstance sql1 -Login test -RemoveRole bulkadmin

    Remove the server role "bulkadmin" to the login

#>

    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [parameter(Mandatory = $true)]
        [string[]]$Login,
        [SecureString]$Password,
        [switch]$Unlock,
        [switch]$MustChange,
        [string]$NewName,
        [switch]$Disable,
        [switch]$Enable,
        [switch]$DenyLogin,
        [switch]$GrantLogin,
        [switch]$PasswordPolicyEnforced,
        [ValidateSet("bulkadmin", "dbcreator", "diskadmin", "processadmin", "public", "securityadmin", "serveradmin", "setupadmin", "sysadmin")]
        [string[]]$AddRole,
        [ValidateSet("bulkadmin", "dbcreator", "diskadmin", "processadmin", "public", "securityadmin", "serveradmin", "setupadmin", "sysadmin")]
        [string[]]$RemoveRole,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {

        # Check the parameters
        if ($Login -eq $NewName) {
            Stop-Function -Message "Login name is the same as the value in -NewName" -Target $Login -Continue
        }

        if ($Disable -and $Enable) {
            Stop-Function -Message "You cannot use both -Enable and -Disable together" -Target $Login -Continue
        }

        if ($GrantLogin -and $DenyLogin) {
            Stop-Function -Message "You cannot use both -GrantLogin and -DenyLogin together" -Target $Login -Continue
        }

        # Check the password
        if ($Password) {
            switch ($Password.GetType().Name) {
                "PSCredential" { $newPassword = $Password.Password}
                "SecureString" { $newPassword = $Password}
            }
        }
        else {
        }

    }

    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $sqlinstance) {
            # Try connecting to the instance
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            # Get all the logins
            $allLogins = $server.Logins | Where-Object {($_.IsSystemObject -eq $false) -and ($_.Name -notlike '##*')}
            $logins = $server.Logins | Where-Object {$Login -contains $_.Name}

            # Loop through all the logins
            foreach ($l in $logins) {

                # Create the notes
                $notes = @()

                # Change the name
                if ($NewName) {
                    # Check if the new name doesn't already exist
                    if ($allLogins.Name -notcontains $NewName) {
                        try {
                            $l.Rename($NewName)
                        }
                        catch {
                            $notes += "Couldn't rename login"
                            Stop-Function -Message "Something went wrong changing the name for $l" -Target $l -ErrorRecord $_ -Continue
                        }
                    }
                    else {
                        $notes += "New login name already exists"
                        Write-Message -Message "New login name $NewName already exists on $instance" -Level Verbose
                    }
                }

                # Change the password
                if ($Password) {
                    try {
                        $l.ChangePassword($newPassword, $Unlock, $MustChange)
                        $passwordChanged = $true
                    }
                    catch {
                        $notes += "Couldn't change password"
                        $passwordChanged = $false
                        Stop-Function -Message "Something went wrong changing the password for $l" -Target $l -ErrorRecord $_ -Continue
                    }
                }

                # Disable the login
                if ($Disable) {
                    if ($l.IsDisabled) {
                        Write-Message -Message "Login $l is already disabled" -Level Verbose
                    }
                    else {
                        try {
                            $l.Disable()
                        }
                        catch {
                            $notes += "Couldn't disable login"
                            Stop-Function -Message "Something went wrong disabling $l" -Target $l -ErrorRecord $_ -Continue
                        }
                    }
                }

                # Enable the login
                if ($Enable) {
                    if (-not $l.IsDisabled) {
                        Write-Message -Message "Login $l is already enabled" -Level Verbose
                    }
                    else {
                        try {
                            $l.Enable()
                        }
                        catch {
                            $notes += "Couldn't enable login"
                            Stop-Function -Message "Something went wrong enabling $l" -Target $l -ErrorRecord $_ -Continue
                        }
                    }
                }

                # Deny access
                if ($DenyLogin) {
                    if ($l.DenyWindowsLogin) {
                        Write-Message -Message "Login $l already has login access denied" -Level Verbose
                    }
                    else {
                        $l.DenyWindowsLogin = $true
                    }
                }

                # Grant access
                if ($GrantLogin) {
                    if (-not $l.DenyWindowsLogin) {
                        Write-Message -Message "Login $l already has login access granted" -Level Verbose
                    }
                    else {
                        $l.DenyWindowsLogin = $false
                    }
                }

                # Enforce password policy
                if (Test-Bound PasswordPolicyEnforced) {
                    if ($l.PasswordPolicyEnforced -eq $PasswordPolicyEnforced) {
                        Write-Message -Message ("Login $l password policy is already set to " + $l.PasswordPolicyEnforced) -Level Verbose
                    } else {
                        $l.PasswordPolicyEnforced = $PasswordPolicyEnforced
                    }
                }

                # Add server roles to login
                if ($AddRole) {
                    # Loop through each of the roles
                    foreach ($role in $AddRole) {
                        try {
                            $l.AddToRole($role)
                        }
                        catch {
                            $notes += "Couldn't add role $role"
                            Stop-Function -Message "Something went wrong adding role $role to $l" -Target $l -ErrorRecord $_ -Continue
                        }
                    }
                }

                # Remove server roles from login
                if ($RemoveRole) {
                    # Loop through each of the roles
                    foreach ($role in $RemoveRole) {
                        try {
                            $server.Roles[$role].DropMember($l.Name)
                        }
                        catch {
                            $notes += "Couldn't remove role $role"
                            Stop-Function -Message "Something went wrong removing role $role to $l" -Target $l -ErrorRecord $_ -Continue
                        }
                    }
                }

                # Alter the login to make the changes
                $l.Alter()

                # Retrieve the server roles for the login
                $roles = Get-DbaRoleMember -SqlInstance $instance -IncludeServerLevel | Where-Object {$null -eq $_.Database -and $_.Member -eq $l.Name}

                # Check if there were any notes to include in the results
                if ($notes) {
                    $notes = $notes | Get-Unique
                    $notes = $notes -Join ';'
                }
                else {
                    $notes = $null
                }

                # Return the results
                [PSCustomObject]@{
                    ComputerName           = $server.ComputerName
                    InstanceName           = $server.ServiceName
                    SqlInstance            = $server.DomainInstanceName
                    LoginName              = $l.Name
                    DenyLogin              = $l.DenyWindowsLogin
                    IsDisabled             = $l.IsDisabled
                    IsLocked               = $l.IsLocked
                    PasswordPolicyEnforced = $l.PasswordPolicyEnforced
                    MustChangePassword     = $l.MustChangePassword
                    PasswordChanged        = $passwordChanged
                    ServerRole             = $roles.Role -join ","
                    Notes                  = $notes
                } | Select-DefaultView -ExcludeProperty Login

            } # end for each login

        } # end for each instance

    } # end process

    end {
        if (Test-FunctionInterrupt) { return }
        Write-Message -Message "Finished changing login(s)" -Level Verbose
    }


}
tools\dbatools\functions\Set-DbaMaxDop.ps1
function Set-DbaMaxDop {
    <#
        .SYNOPSIS
            Sets SQL Server maximum degree of parallelism (Max DOP), then displays information relating to SQL Server Max DOP configuration settings. Works on SQL Server 2005 and higher.

        .DESCRIPTION
            Uses the Test-DbaMaxDop command to get the recommended value if -MaxDop parameter is not specified.

            These are just general recommendations for SQL Server and are a good starting point for setting the "max degree of parallelism" option.

            You can set MaxDop database scoped configurations if the server is version 2016 or higher

        .PARAMETER SqlInstance
            The SQL Server instance to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies one or more databases to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies one or more databases to exclude from processing. Options for this list are auto-populated from the server

        .PARAMETER MaxDop
            Specifies the Max DOP value to set.

        .PARAMETER AllDatabases
            If this switch is enabled, Max DOP will be set on all databases. This switch is only useful on SQL Server 2016 and higher.

        .PARAMETER Collection
            If Test-SQLMaxDop has been executed prior to this function, the results may be passed in via this parameter.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER WhatIf
            Shows what would happen if the cmdlet runs. The cmdlet is not run.

        .PARAMETER Confirm
            Prompts you for confirmation before running the cmdlet.

        .NOTES
            Tags: MaxDop, SpConfigure
            Author: Claudio Silva (@claudioessilva)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Set-DbaMaxDop

        .EXAMPLE
            Set-DbaMaxDop -SqlInstance sql2008, sql2012

            Sets Max DOP to the recommended value for servers sql2008 and sql2012.

        .EXAMPLE
            Set-DbaMaxDop -SqlInstance sql2014 -MaxDop 4

            Sets Max DOP to 4 for server sql2014.

        .EXAMPLE
            Test-DbaMaxDop -SqlInstance sql2008 | Set-DbaMaxDop

            Gets the recommended Max DOP from Test-DbaMaxDop and applies it to to sql2008.

        .EXAMPLE
            Set-DbaMaxDop -SqlInstance sql2016 -Database db1

            Set recommended Max DOP for database db1 on server sql2016.

        .EXAMPLE
            Set-DbaMaxDop -SqlInstance sql2016 -AllDatabases

            Set recommended Max DOP for all databases on server sql2016.

    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [int]$MaxDop = -1,
        [Parameter(ValueFromPipeline = $True)]
        [object]$Collection,
        [Alias("All")]
        [switch]$AllDatabases,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $processed = New-Object System.Collections.ArrayList
        $results = @()
    }
    process {
        if ((Test-Bound -Parameter Database) -and (Test-Bound -Parameter AllDatabases) -and (Test-Bound -Parameter ExcludeDatabase)) {
            Stop-Function -Category InvalidArgument -Message "-Database, -AllDatabases and -ExcludeDatabase are mutually exclusive. Please choose only one. Quitting."
            return
        }

        $dbscopedconfiguration = $false

        if ($MaxDop -eq -1) {
            $UseRecommended = $true
        }

        if ((Test-Bound -Not -Parameter Collection)) {
            $collection = Test-DbaMaxDop -SqlInstance $sqlinstance -SqlCredential $SqlCredential -Verbose:$false
        }
        elseif ($null -eq $collection.SqlInstance) {
            $collection = Test-DbaMaxDop -SqlInstance $sqlinstance -SqlCredential $SqlCredential -Verbose:$false
        }

        $collection | Add-Member -Force -NotePropertyName OldInstanceMaxDopValue -NotePropertyValue 0
        $collection | Add-Member -Force -NotePropertyName OldDatabaseMaxDopValue -NotePropertyValue 0

        #If we have servers 2016 or higher we will have a row per database plus the instance level, getting unique we only run one time per instance
        $servers = $collection | Select-Object SqlInstance -Unique

        foreach ($server in $servers) {
            $servername = $server.SqlInstance

            Write-Message -Level Verbose -Message "Connecting to $servername"
            try {
                $server = Connect-SqlInstance -SqlInstance $servername -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $servername -Continue
            }

            if (!(Test-SqlSa -SqlInstance $server)) {
                Stop-Function -Message "Not a sysadmin on $server. Skipping." -Category PermissionDenied -ErrorRecord $_ -Target $currentServer -Continue
            }

            if ($server.versionMajor -ge 13) {
                Write-Message -Level Verbose -Message "Server '$servername' supports Max DOP configuration per database."

                if ((Test-Bound -Not -Parameter Database) -and (Test-Bound -Not -Parameter ExcludeDatabase)) {
                    #Set at instance level
                    $collection = $collection | Where-Object { $_.DatabaseMaxDop -eq "N/A" }
                }
                else {
                    $dbscopedconfiguration = $true

                    if ((Test-Bound -Not -Parameter AllDatabases) -and (Test-Bound -Parameter Database)) {
                        $collection = $collection | Where-Object { $_.Database -in $Database }
                    }
                    elseif ((Test-Bound -Not -Parameter AllDatabases) -and (Test-Bound -Parameter ExcludeDatabase)) {
                        $collection = $collection | Where-Object { $_.Database -notin $ExcludeDatabase }
                    }
                    else {
                        if (Test-Bound -Parameter AllDatabases) {
                            $collection = $collection | Where-Object { $_.DatabaseMaxDop -ne "N/A" }
                        }
                        else {
                            $collection = $collection | Where-Object { $_.DatabaseMaxDop -eq "N/A" }
                            $dbscopedconfiguration = $false
                        }
                    }
                }
            }
            else {
                if ((Test-Bound -Parameter database) -or (Test-Bound -Parameter AllDatabases)) {
                    Write-Message -Level Warning -Message "Server '$servername' (v$($server.versionMajor)) does not support Max DOP configuration at the database level. Remember that this option is only available from SQL Server 2016 (v13). Run the command again without using database related parameters. Skipping."
                    Continue
                }
            }

            foreach ($row in $collection | Where-Object { $_.SqlInstance -eq $servername }) {
                if ($UseRecommended -and ($row.RecommendedMaxDop -eq $row.CurrentInstanceMaxDop) -and !($dbscopedconfiguration)) {
                    Write-Message -Level Verbose -Message "$servername is configured properly. No change required."
                    Continue
                }

                if ($UseRecommended -and ($row.RecommendedMaxDop -eq $row.DatabaseMaxDop) -and $dbscopedconfiguration) {
                    Write-Message -Level Verbose -Message "Database $($row.Database) on $servername is configured properly. No change required."
                    Continue
                }

                $row.OldInstanceMaxDopValue = $row.CurrentInstanceMaxDop

                try {
                    if ($UseRecommended) {
                        if ($dbscopedconfiguration) {
                            $row.OldDatabaseMaxDopValue = $row.DatabaseMaxDop

                            if ($resetDatabases) {
                                Write-Message -Level Verbose -Message "Changing $($row.Database) database max DOP to $($row.DatabaseMaxDop)."
                                $server.Databases["$($row.Database)"].MaxDop = $row.DatabaseMaxDop
                            }
                            else {
                                Write-Message -Level Verbose -Message "Changing $($row.Database) database max DOP from $($row.DatabaseMaxDop) to $($row.RecommendedMaxDop)."
                                $server.Databases["$($row.Database)"].MaxDop = $row.RecommendedMaxDop
                                $row.DatabaseMaxDop = $row.RecommendedMaxDop
                            }

                        }
                        else {
                            Write-Message -Level Verbose -Message "Changing $server SQL Server max DOP from $($row.CurrentInstanceMaxDop) to $($row.RecommendedMaxDop)."
                            $server.Configuration.MaxDegreeOfParallelism.ConfigValue = $row.RecommendedMaxDop
                            $row.CurrentInstanceMaxDop = $row.RecommendedMaxDop
                        }
                    }
                    else {
                        if ($dbscopedconfiguration) {
                            $row.OldDatabaseMaxDopValue = $row.DatabaseMaxDop

                            Write-Message -Level Verbose -Message "Changing $($row.Database) database max DOP from $($row.DatabaseMaxDop) to $MaxDop."
                            $server.Databases["$($row.Database)"].MaxDop = $MaxDop
                            $row.DatabaseMaxDop = $MaxDop
                        }
                        else {
                            Write-Message -Level Verbose -Message "Changing $servername SQL Server max DOP from $($row.CurrentInstanceMaxDop) to $MaxDop."
                            $server.Configuration.MaxDegreeOfParallelism.ConfigValue = $MaxDop
                            $row.CurrentInstanceMaxDop = $MaxDop
                        }
                    }

                    if ($dbscopedconfiguration) {
                        if ($Pscmdlet.ShouldProcess($row.Database, "Setting max dop on database")) {
                            $server.Databases["$($row.Database)"].Alter()
                        }
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess($servername, "Setting max dop on instance")) {
                            $server.Configuration.Alter()
                        }
                    }

                    $results += [pscustomobject]@{
                        ComputerName           = $server.ComputerName
                        InstanceName           = $server.ServiceName
                        SqlInstance            = $server.DomainInstanceName
                        InstanceVersion        = $row.InstanceVersion
                        Database               = $row.Database
                        DatabaseMaxDop         = $row.DatabaseMaxDop
                        CurrentInstanceMaxDop  = $row.CurrentInstanceMaxDop
                        RecommendedMaxDop      = $row.RecommendedMaxDop
                        OldDatabaseMaxDopValue = $row.OldDatabaseMaxDopValue
                        OldInstanceMaxDopValue = $row.OldInstanceMaxDopValue
                    }
                }
                catch {
                    Stop-Function -Message "Could not modify Max Degree of Parallelism for $server."  -ErrorRecord $_ -Target $server -Continue
                }
            }

            if ($dbscopedconfiguration) {
                Select-DefaultView -InputObject $results -Property InstanceName, Database, OldDatabaseMaxDopValue, @{ name = "CurrentDatabaseMaxDopValue"; expression = { $_.DatabaseMaxDop } }
            }
            else {
                Select-DefaultView -InputObject $results -Property InstanceName, OldInstanceMaxDopValue, CurrentInstanceMaxDop
            }
        }
    }
}
tools\dbatools\functions\Set-DbaMaxMemory.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#

function Set-DbaMaxMemory {
    <#
        .SYNOPSIS
            Sets SQL Server 'Max Server Memory' configuration setting to a new value then displays information this setting.

        .DESCRIPTION
            Sets SQL Server max memory then displays information relating to SQL Server Max Memory configuration settings.

            Inspired by Jonathan Kehayias's post about SQL Server Max memory (http://bit.ly/sqlmemcalc), this uses a formula to
            determine the default optimum RAM to use, then sets the SQL max value to that number.

            Jonathan notes that the formula used provides a *general recommendation* that doesn't account for everything that may
            be going on in your specific environment.

        .PARAMETER SqlInstance
            Allows you to specify a comma separated list of servers to query.

        .PARAMETER MaxMB
            Specifies the max megabytes

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER WhatIf
            Shows what would happen if the cmdlet runs. The cmdlet is not run.

        .PARAMETER Confirm
            Prompts you for confirmation before running the cmdlet.

        .NOTES
            Tags: MaxMemory, Memory
            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Set-DbaMaxMemory

        .EXAMPLE
            Set-DbaMaxMemory sqlserver1

            Set max memory to the recommended MB on just one server named "sqlserver1"

        .EXAMPLE
            Set-DbaMaxMemory -SqlInstance sqlserver1 -MaxMB 2048

            Explicitly max memory to 2048 MB on just one server, "sqlserver1"

        .EXAMPLE
            Get-DbaRegisteredServer -SqlInstance sqlserver | Test-DbaMaxMemory | Where-Object { $_.SqlMaxMB -gt $_.TotalMB } | Set-DbaMaxMemory

            Find all servers in SQL Server Central Management server that have Max SQL memory set to higher than the total memory
            of the server (think 2147483647), then pipe those to Set-DbaMaxMemory and use the default recommendation.
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param (
        [Parameter(Position = 0)]
        [Alias("ServerInstance", "SqlServer", "SqlServers", "ComputerName")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Parameter(Position = 1)]
        [int]$MaxMB,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        if ((Test-Bound -Not -Parameter SqlInstance) -and (Test-Bound -Not -Parameter Collection)) {
            Stop-Function -Category InvalidArgument -Message "You must specify a server list source using -SqlInstance or you can pipe results from Test-DbaMaxMemory"
            return
        }

        if ($MaxMB -eq 0) {
            $UseRecommended = $true
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if (!(Test-SqlSa -SqlInstance $server)) {
                Stop-Function -Message "Not a sysadmin on $server. Skipping." -Category PermissionDenied -ErrorRecord $_ -Target $server -Continue
            }

            try {
                $currentServer = Test-DbaMaxMemory -SqlInstance $server
                Add-Member -Force -InputObject $currentServer -NotePropertyName OldMaxValue -NotePropertyValue 0
                $currentServer.OldMaxValue = $currentServer.SqlMaxMB
            }
            catch {
                Stop-Function -Message "Issue collecting memory information on $server" -Target $server -ErrorRecord $_ -InnerException $_.Exception -Continue
            }

            try {
                if ($UseRecommended) {
                    Write-Message -Level Verbose -Message "Change $server SQL Server Max Memory from $($currentServer.SqlMaxMB) to $($currentServer.RecommendedMB) MB"

                    if ($currentServer.RecommendedMB -eq 0 -or $null -eq $currentServer.RecommendedMB) {
                        $maxMem = (Test-DbaMaxMemory -SqlInstance $server).RecommendedMB
                        Write-Message -Level VeryVerbose -Message "Max memory recommended: $maxMem"
                        $server.Configuration.MaxServerMemory.ConfigValue = $maxMem
                    }
                    else {
                        $server.Configuration.MaxServerMemory.ConfigValue = $currentServer.RecommendedMB
                    }
                }
                else {
                    Write-Message -Level Verbose -Message "Change $server SQL Server Max Memory from $($currentServer.SqlMaxMB) to $MaxMB MB"
                    $server.Configuration.MaxServerMemory.ConfigValue = $MaxMB
                }
                if ($PSCmdlet.ShouldProcess($server, "Change Max Memory from $($currentServer.OldMaxValue) to $($server.Configuration.MaxServerMemory.ConfigValue)")) {
                    try {
                        $server.Configuration.Alter()
                        $currentServer.SqlMaxMB = $server.Configuration.MaxServerMemory.ConfigValue
                    }
                    catch {
                        Stop-Function -Message "Failed to apply configuration change for $server" -ErrorRecord $_ -Target $server -Continue
                    }
                }
            }
            catch {
                Stop-Function -Message "Could not modify Max Server Memory for $server" -ErrorRecord $_ -Target $server -Continue
            }

            Add-Member -InputObject $currentServer -Force -MemberType NoteProperty -Name CurrentMaxValue -Value $currentServer.SqlMaxMB
            Select-DefaultView -InputObject $currentServer -Property ComputerName, InstanceName, SqlInstance, TotalMB, OldMaxValue, CurrentMaxValue
        }
    }
}
tools\dbatools\functions\Set-DbaNetworkCertificate.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#

function Set-DbaNetworkCertificate {
    <#
    .SYNOPSIS
        Sets the network certificate for SQL Server instance

    .DESCRIPTION
        Sets the network certificate for SQL Server instance. This setting is found in Configuration Manager.

        This command also grants read permissions for the service account on the certificate's private key.

        References:
        http://sqlmag.com/sql-server/7-steps-ssl-encryption
        https://azurebi.jppp.org/2016/01/23/using-lets-encrypt-certificates-for-secure-sql-server-connections/
        https://blogs.msdn.microsoft.com/sqlserverfaq/2016/09/26/creating-and-registering-ssl-certificates/

    .PARAMETER SqlInstance
        The target SQL Server - defaults to localhost.

    .PARAMETER Credential
        Allows you to login to the computer (not sql instance) using alternative credentials.

    .PARAMETER Certificate
        The target certificate object

    .PARAMETER Thumbprint
        The thumbprint of the target certificate

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .PARAMETER WhatIf
        Shows what would happen if the command were to run. No actions are actually performed.

    .PARAMETER Confirm
        Prompts you for confirmation before executing any changing operations within the command.

    .EXAMPLE
        New-DbaComputerCertificate | Set-DbaNetworkCertificate -SqlInstance localhost\SQL2008R2SP2

        Creates and imports a new certificate signed by an Active Directory CA on localhost then sets the network certificate for the SQL2008R2SP2 to that newly created certificate.

    .EXAMPLE
        Set-DbaNetworkCertificate -SqlInstance sql1\SQL2008R2SP2 -Thumbprint 1223FB1ACBCA44D3EE9640F81B6BA14A92F3D6E2

        Sets the network certificate for the SQL2008R2SP2 instance to the certificate with the thumbprint of 1223FB1ACBCA44D3EE9640F81B6BA14A92F3D6E2 in LocalMachine\My on sql1

    .NOTES
        Tags: Certificate

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT
#>
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Low", DefaultParameterSetName = 'Default')]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer", "ComputerName")]
        [DbaInstanceParameter[]]
        $SqlInstance = $env:COMPUTERNAME,

        [PSCredential]

        $Credential,

        [parameter(Mandatory, ParameterSetName = "Certificate", ValueFromPipeline)]
        [System.Security.Cryptography.X509Certificates.X509Certificate2]
        $Certificate,

        [parameter(Mandatory, ParameterSetName = "Thumbprint")]
        [string]
        $Thumbprint,

        [switch]
        [Alias('Silent')]$EnableException
    )

    process {
        if (Test-FunctionInterrupt) { return }
        $Certificate
        if (!$Certificate -and !$Thumbprint) {
            Stop-Function -Message "You must specify a certificate or thumbprint"
            return
        }

        if (!$Thumbprint) {
            Write-Message -Level SomewhatVerbose -Message "Getting thumbprint"
            $Thumbprint = $Certificate.Thumbprint
        }

        foreach ($instance in $sqlinstance) {
            Write-Message -Level VeryVerbose -Message "Processing $instance" -Target $instance
            $null = Test-ElevationRequirement -ComputerName $instance -Continue

            Write-Message -Level Verbose -Message "Resolving hostname"
            $resolved = $null
            $resolved = Resolve-DbaNetworkName -ComputerName $instance -Turbo

            if ($null -eq $resolved) {
                Stop-Function -Message "Can't resolve $instance" -Target $instance -Continue -Category InvalidArgument
            }

            $computername = $instance.ComputerName
            $instancename = $instance.instancename
            Write-Message -Level Output -Message "Connecting to SQL WMI on $computername"

            try {
                $sqlwmi = Invoke-ManagedComputerCommand -ComputerName $resolved.FQDN -ScriptBlock { $wmi.Services } -Credential $Credential -ErrorAction Stop | Where-Object DisplayName -eq "SQL Server ($instancename)"
            }
            catch {
                Stop-Function -Message "Failed to access $instance" -Target $instance -Continue -ErrorRecord $_
            }

            if (-not $sqlwmi) {
                Stop-Function -Message "Cannot find $instancename on $computerName" -Continue -Category ObjectNotFound -Target $instance
            }

            $regroot = ($sqlwmi.AdvancedProperties | Where-Object Name -eq REGROOT).Value
            $vsname = ($sqlwmi.AdvancedProperties | Where-Object Name -eq VSNAME).Value
            $instancename = $sqlwmi.DisplayName.Replace('SQL Server (', '').Replace(')', '') # Don't clown, I don't know regex :(
            $serviceaccount = $sqlwmi.ServiceAccount

            if ([System.String]::IsNullOrEmpty($regroot)) {
                $regroot = $sqlwmi.AdvancedProperties | Where-Object { $_ -match 'REGROOT' }
                $vsname = $sqlwmi.AdvancedProperties | Where-Object { $_ -match 'VSNAME' }

                if (![System.String]::IsNullOrEmpty($regroot)) {
                    $regroot = ($regroot -Split 'Value\=')[1]
                    $vsname = ($vsname -Split 'Value\=')[1]
                }
                else {
                    Stop-Function -Message "Can't find instance $vsname on $instance" -Continue -Category ObjectNotFound -Target $instance
                }
            }

            if ([System.String]::IsNullOrEmpty($vsname)) { $vsname = $instance }

            Write-Message -Level Output -Message "Regroot: $regroot" -Target $instance
            Write-Message -Level Output -Message "ServiceAcct: $serviceaccount" -Target $instance
            Write-Message -Level Output -Message "InstanceName: $instancename" -Target $instance
            Write-Message -Level Output -Message "VSNAME: $vsname" -Target $instance

            $scriptblock = {
                $regroot = $args[0]
                $serviceaccount = $args[1]
                $instancename = $args[2]
                $vsname = $args[3]
                $Thumbprint = $args[4]

                $regpath = "Registry::HKEY_LOCAL_MACHINE\$regroot\MSSQLServer\SuperSocketNetLib"

                $oldthumbprint = (Get-ItemProperty -Path $regpath -Name Certificate).Certificate

                $cert = Get-ChildItem Cert:\LocalMachine -Recurse -ErrorAction Stop | Where-Object { $_.Thumbprint -eq $Thumbprint }

                if ($null -eq $cert) {
                    Write-Warning "Certificate does not exist on $env:COMPUTERNAME"
                    return
                }

                $permission = $serviceaccount, "Read", "Allow"
                $accessRule = New-Object -TypeName System.Security.AccessControl.FileSystemAccessRule -ArgumentList $permission

                $keyPath = $env:ProgramData + "\Microsoft\Crypto\RSA\MachineKeys\"
                $keyName = $cert.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName
                $keyFullPath = $keyPath + $keyName

                $acl = Get-Acl -Path $keyFullPath
                $null = $acl.AddAccessRule($accessRule)
                Set-Acl -Path $keyFullPath -AclObject $acl

                if ($acl) {
                    Set-ItemProperty -Path $regpath -Name Certificate -Value $Thumbprint.ToString().ToLower() # to make it compat with SQL config
                }
                else {
                    Write-Warning "Read-only permissions could not be granted to certificate"
                    return
                }

                if (![System.String]::IsNullOrEmpty($oldthumbprint)) {
                    $notes = "Granted $serviceaccount read access to certificate private key. Replaced thumbprint: $oldthumbprint."
                }
                else {
                    $notes = "Granted $serviceaccount read access to certificate private key"
                }

                $newthumbprint = (Get-ItemProperty -Path $regpath -Name Certificate).Certificate

                [pscustomobject]@{
                    ComputerName          = $env:COMPUTERNAME
                    InstanceName          = $instancename
                    SqlInstance           = $vsname
                    ServiceAccount        = $serviceaccount
                    CertificateThumbprint = $newthumbprint
                    Notes                 = $notes
                }
            }

            if ($PScmdlet.ShouldProcess("local", "Connecting to $instanceName to import new cert")) {
                try {
                    Invoke-Command2 -Raw -ComputerName $resolved.fqdn -Credential $Credential -ArgumentList $regroot, $serviceaccount, $instancename, $vsname, $Thumbprint -ScriptBlock $scriptblock -ErrorAction Stop
                }
                catch {
                    Stop-Function -Message "Failed to connect to $($resolved.fqdn) using PowerShell remoting!" -ErrorRecord $_ -Target $instance -Continue
                }
            }
        }
    }
}
tools\dbatools\functions\Set-DbaPowerPlan.ps1
function Set-DbaPowerPlan {
    <#
        .SYNOPSIS
            Sets the SQL Server OS's Power Plan.

        .DESCRIPTION
            Sets the SQL Server OS's Power Plan. Defaults to High Performance which is best practice.

            If your organization uses a custom power plan that is considered best practice, specify -CustomPowerPlan.

            References:
            https://support.microsoft.com/en-us/kb/2207548
            http://www.sqlskills.com/blogs/glenn/windows-power-plan-effects-on-newer-intel-processors/

        .PARAMETER ComputerName
            The server(s) to set the Power Plan on.

        .PARAMETER PowerPlan
            Specifies the Power Plan that you wish to use. Valid options for this match the Windows default Power Plans of "Power Saver", "Balanced", and "High Performance".

        .PARAMETER CustomPowerPlan
            Specifies the name of a custom Power Plan to use.


        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .NOTES
            Tags: PowerPlan, OS, Configure
            Requires: WMI access to servers

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Set-DbaPowerPlan

        .EXAMPLE
            Set-DbaPowerPlan -ComputerName sqlserver2014a

            Sets the Power Plan to High Performance. Skips it if its already set.

        .EXAMPLE
            Set-DbaPowerPlan -ComputerName sqlcluster -CustomPowerPlan 'Maximum Performance'

            Sets the Power Plan to the custom power plan called "Maximum Performance". Skips it if its already set.

    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer", "SqlInstance")]
        [object[]]$ComputerName,
        [ValidateSet('High Performance', 'Balanced', 'Power saver')]
        [string]$PowerPlan = 'High Performance',
        [string]$CustomPowerPlan,
        [switch][Alias('Silent')]
        $EnableException
    )

    begin {
        if ($CustomPowerPlan.Length -gt 0) {
            $PowerPlan = $CustomPowerPlan
        }

        function Set-DbaPowerPlanInternal {
            param($server)

            try {
                Write-Message -Level Verbose -Message "Testing connection to $server and resolving IP address."
                $ipaddr = (Test-Connection $server -Count 1 -ErrorAction SilentlyContinue).Ipv4Address | Select-Object -First 1

            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $server
                return
            }

            try {
                Write-Message -Level Verbose -Message "Getting Power Plan information from $server."
                $query = "Select ElementName from Win32_PowerPlan WHERE IsActive = 'true'"
                $currentplan = Get-WmiObject -Namespace Root\CIMV2\Power -ComputerName $ipaddr -Query $query -ErrorAction SilentlyContinue
                $currentplan = $currentplan.ElementName
            }
            catch {
                Stop-Function -Message "Can't connect to WMI on $server." -Category ConnectionError -ErrorRecord $_ -Target $server
                return
            }

            if ($null -eq $currentplan) {
                # the try/catch above isn't working, so make it silent and handle it here.
                Stop-Function -Message "Cannot get Power Plan for $server." -Category ConnectionError -ErrorRecord $_ -Target $server
                return
            }

            $planinfo = [PSCustomObject]@{
                Server            = $server
                PreviousPowerPlan = $currentplan
                ActivePowerPlan   = $PowerPlan
            }

            if ($PowerPlan -ne $currentplan) {
                if ($Pscmdlet.ShouldProcess($server, "Changing Power Plan from $CurrentPlan to $PowerPlan")) {
                    try {
                        Write-Message -Level Verbose -Message "Setting Power Plan to $PowerPlan."
                        $null = (Get-WmiObject -Name root\cimv2\power -ComputerName $ipaddr -Class Win32_PowerPlan -Filter "ElementName='$PowerPlan'").Activate()
                    }
                    catch {
                        Stop-Function -Message "Couldn't set Power Plan on $server." -Category ConnectionError -ErrorRecord $_ -Target $server
                        return
                    }
                }
            }
            else {
                if ($Pscmdlet.ShouldProcess($server, "Stating power plan is already set to $PowerPlan, won't change.")) {
                    Write-Message -Level Verbose -Message "PowerPlan on $server is already set to $PowerPlan. Skipping."
                }
            }

            return $planinfo
        }


        $collection = New-Object System.Collections.ArrayList
        $processed = New-Object System.Collections.ArrayList
    }

    process {
        foreach ($server in $ComputerName) {
            if ($server -match 'Server\=') {
                Write-Message -Level Verbose -Message "Matched that value was piped from Test-DbaPowerPlan."
                # I couldn't properly unwrap the output from  Test-DbaPowerPlan so here goes.
                $lol = $server.Split("\;")[0]
                $lol = $lol.TrimEnd("\}")
                $lol = $lol.TrimStart("\@\{Server")
                # There was some kind of parsing bug here, don't clown
                $server = $lol.TrimStart("\=")
            }

            if ($server -match '\\') {
                $server = $server.Split('\\')[0]
            }

            if ($server -notin $processed) {
                $null = $processed.Add($server)
                Write-Message -Level Verbose -Message "Connecting to $server."
            }
            else {
                continue
            }

            $data = Set-DbaPowerPlanInternal $server

            if ($data.Count -gt 1) {
                $data.GetEnumerator() | ForEach-Object { $null = $collection.Add($_) }
            }
            else {
                $null = $collection.Add($data)
            }
        }
    }

    end {
        If ($Pscmdlet.ShouldProcess("console", "Showing results")) {
            return $collection
        }
    }
}
tools\dbatools\functions\Set-DbaPrivilege.ps1
function Set-DbaPrivilege {
    <#
      .SYNOPSIS
      Adds the SQL Service account to local privileges on one or more computers.

      .DESCRIPTION
      Adds the SQL Service account to local privileges 'Lock Pages in Memory', 'Instant File Initialization', 'Logon as Batch' on one or more computers.

      Requires Local Admin rights on destination computer(s).

      .PARAMETER ComputerName
      The SQL Server (or server in general) that you're connecting to. This command handles named instances.

      .PARAMETER Credential
      Credential object used to connect to the computer as a different user.

      .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

      .PARAMETER Type
      Use this to choose the privilege(s) to which you want to add the SQL Service account.
      Accepts 'IFI', 'LPIM' and/or 'BatchLogon' for local privileges 'Instant File Initialization', 'Lock Pages in Memory' and 'Logon as Batch'.

      .NOTES
      Author: Klaas Vandenberghe ( @PowerDBAKlaas )
      Tags: Privilege
      Website: https://dbatools.io
      Copyright: (C) Chrissy LeMaire, [email protected]
      License: MIT https://opensource.org/licenses/MIT

    .LINK
      https://dbatools.io/Set-DbaPrivilege

      .EXAMPLE
      Set-DbaPrivilege -ComputerName sqlserver2014a -Type LPIM,IFI

      Adds the SQL Service account(s) on computer sqlserver2014a to the local privileges 'SeManageVolumePrivilege' and 'SeLockMemoryPrivilege'.

      .EXAMPLE
      'sql1','sql2','sql3' | Set-DbaPrivilege -Type IFI

      Adds the SQL Service account(s) on computers sql1, sql2 and sql3 to the local privilege 'SeManageVolumePrivilege'.

  #>
    [CmdletBinding()]
    Param (
        [parameter(ValueFromPipeline)]
        [Alias("cn", "host", "Server")]
        [dbainstanceparameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Parameter(Mandatory = $true)]
        [ValidateSet('IFI', 'LPIM', 'BatchLogon')]
        [string[]]$Type,
        [switch][Alias('Silent')]
        $EnableException
    )
    
    begin {
        $ResolveAccountToSID = @"
function Convert-UserNameToSID ([string] `$Acc ) {
`$objUser = New-Object System.Security.Principal.NTAccount(`"`$Acc`")
`$strSID = `$objUser.Translate([System.Security.Principal.SecurityIdentifier])
`$strSID.Value
}
"@
        $ComputerName = $ComputerName.ComputerName | Select-Object -Unique
    }
    process {
        foreach ($computer in $ComputerName) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $computer"
                $null = Test-ElevationRequirement -ComputerName $Computer -Continue
                if (Test-PSRemoting -ComputerName $Computer) {
                    Write-Message -Level Verbose -Message "Exporting Privileges on $Computer"
                    Invoke-Command2 -Raw -ComputerName $computer -Credential $Credential -ScriptBlock {
                        $temp = ([System.IO.Path]::GetTempPath()).TrimEnd(""); secedit /export /cfg $temp\secpolByDbatools.cfg > $NULL;
                    }
                    Write-Message -Level Verbose -Message "Getting SQL Service Accounts on $computer"
                    $SQLServiceAccounts = (Get-DbaSqlService -ComputerName $computer -Type Engine).StartName
                    if ($SQLServiceAccounts.count -ge 1) {
                        Write-Message -Level Verbose -Message "Setting Privileges on $Computer"
                        Invoke-Command2 -Raw -ComputerName $computer -Credential $Credential -Verbose -ArgumentList $ResolveAccountToSID, $SQLServiceAccounts, $BatchLogon, $IFI, $LPIM -ScriptBlock {
                            [CmdletBinding()]
                            Param ($ResolveAccountToSID,
                                $SQLServiceAccounts,
                                $BatchLogon,
                                $IFI,
                                $LPIM)
                            . ([ScriptBlock]::Create($ResolveAccountToSID))
                            $temp = ([System.IO.Path]::GetTempPath()).TrimEnd("");
                            $tempfile = "$temp\secpolByDbatools.cfg"
                            if ('BatchLogon' -in $Type) {
                                $BLline = Get-Content $tempfile | Where-Object { $_ -match "SeBatchLogonRight" }
                                ForEach ($acc in $SQLServiceAccounts) {
                                    $SID = Convert-UserNameToSID -Acc $acc;
                                    if ($BLline -notmatch $SID) {
                                        (Get-Content $tempfile) -replace "SeBatchLogonRight = ", "SeBatchLogonRight = *$SID," |
                                        Set-Content $tempfile
                                        Write-Verbose "Added $acc to Batch Logon Privileges on $env:ComputerName"
                                    }
                                    else {
                                        Write-Warning "$acc already has Batch Logon Privilege on $env:ComputerName"
                                    }
                                }
                            }
                            if ('IFI' -in $Type) {
                                $IFIline = Get-Content $tempfile | Where-Object { $_ -match "SeManageVolumePrivilege" }
                                ForEach ($acc in $SQLServiceAccounts) {
                                    $SID = Convert-UserNameToSID -Acc $acc;
                                    if ($IFIline -notmatch $SID) {
                                        (Get-Content $tempfile) -replace "SeManageVolumePrivilege = ", "SeManageVolumePrivilege = *$SID," |
                                        Set-Content $tempfile
                                        Write-Verbose "Added $acc to Instant File Initialization Privileges on $env:ComputerName"
                                    }
                                    else {
                                        Write-Warning "$acc already has Instant File Initialization Privilege on $env:ComputerName"
                                    }
                                }
                            }
                            if ('LPIM' -in $Type) {
                                $LPIMline = Get-Content $tempfile | Where-Object { $_ -match "SeLockMemoryPrivilege" }
                                ForEach ($acc in $SQLServiceAccounts) {
                                    $SID = Convert-UserNameToSID -Acc $acc;
                                    if ($LPIMline -notmatch $SID) {
                                        (Get-Content $tempfile) -replace "SeLockMemoryPrivilege = ", "SeLockMemoryPrivilege = *$SID," |
                                        Set-Content $tempfile
                                        Write-Verbose "Added $acc to Lock Pages in Memory Privileges on $env:ComputerName"
                                    }
                                    else {
                                        Write-Warning "$acc already has Lock Pages in Memory Privilege on $env:ComputerName"
                                    }
                                }
                            }
                            $null = secedit /configure /cfg $tempfile /db secedit.sdb /areas USER_RIGHTS /overwrite /quiet
                        } -ErrorAction SilentlyContinue
                        Write-Message -Level Verbose -Message "Removing secpol file on $computer"
                        Invoke-Command2 -Raw -ComputerName $computer -Credential $Credential -ScriptBlock { $temp = ([System.IO.Path]::GetTempPath()).TrimEnd(""); Remove-Item $temp\secpolByDbatools.cfg -Force > $NULL }
                    }
                    else {
                        Write-Message -Level Warning -Message "No SQL Service Accounts found on $Computer"
                    }
                }
                else {
                    Write-Message -Level Warning -Message "Failed to connect to $Computer"
                }
            }
            catch {
                Stop-Function -Continue -Message "Failure" -ErrorRecord $_ -Target $computer -Continue
            }
        }
    }
}
tools\dbatools\functions\Set-DbaSpConfigure.ps1
function Set-DbaSpConfigure {
    <#
        .SYNOPSIS
            Changes the server level system configuration (sys.configuration/sp_configure) value for a given configuration

        .DESCRIPTION
            This function changes the configured value for sp_configure settings. If the setting is dynamic this setting will be used, otherwise the user will be warned that a restart of SQL is required.
            This is designed to be safe and will not allow for configurations to be set outside of the defined configuration min and max values.
            While it is possible to set below the min, or above the max this can cause serious problems with SQL Server (including startup failures), and so is not permitted.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a
            collection and receive pipeline input

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Name
            The name of the configuration to be set -- Configs is auto-populated for tabbing convenience.

        .PARAMETER Value
            The new value for the configuration

        .PARAMETER InputObject
            Piped objectgs from Get-DbaSpConfigure
    
        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .NOTES
            Tags: SpConfigure
            Author: Nic Cain, https://sirsql.net/

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Set-DbaSpConfigure

        .EXAMPLE
            Set-DbaSpConfigure -SqlInstance localhost -Name ScanForStartupProcedures -Value 1

            Adjusts the Scan for startup stored procedures configuration value to 1 and notifies the user that this requires a SQL restart to take effect

        .EXAMPLE
            Get-DbaSpConfigure -SqlInstance sql2017, sql2014 -Name XPCmdShellEnabled, IsSqlClrEnabled | Set-DbaSpConfigure -Value $false
            Sets the values for XPCmdShellEnabled and IsSqlClrEnabled on sql2017 and sql2014 to False
    
        .EXAMPLE
            Set-DbaSpConfigure -SqlInstance localhost -Name XPCmdShellEnabled -Value 1

            Adjusts the xp_cmdshell configuration value to 1.

        .EXAMPLE
            Set-DbaSpConfigure -SqlInstance localhost -Name XPCmdShellEnabled -Value 1 -WhatIf

            Returns information on the action that would be performed. No actual change will be made.
        #>
    [CmdletBinding(SupportsShouldProcess)]
    Param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [System.Management.Automation.PSCredential]$SqlCredential,
        [Alias("NewValue", "NewConfig")]
        [int]$Value,
        [Alias("Config", "ConfigName")]
        [string[]]$Name,
        [parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [switch][Alias('Silent')]
        $EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            $InputObject += Get-DbaSpConfigure -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Name $Name
        }
        
        foreach ($configobject in $InputObject) {
            $server = $InputObject.Parent
            $currentRunValue = $configobject.RunningValue
            $minValue = $configobject.MinValue
            $maxValue = $configobject.MaxValue
            $isDynamic = $configobject.IsDynamic
            $configuration = $configobject.Name
            
            #Let us not waste energy setting the value to itself
            if ($currentRunValue -eq $value) {
                Stop-Function -Message "Value to set is the same as the existing value. No work being performed." -Continue -Target $server -Category InvalidData
            }
            
            #Going outside the min/max boundary can be done, but it can break SQL, so I don't think allowing that is wise at this juncture
            if ($value -lt $minValue -or $value -gt $maxValue) {
                Stop-Function -Message "Value out of range for $configuration ($minValue <-> $maxValue)" -Continue -Category InvalidArgument
            }
            
            If ($Pscmdlet.ShouldProcess($SqlInstance, "Adjusting server configuration $configuration from $currentRunValue to $value.")) {
                try {
                    $server.Configuration.$configuration.ConfigValue = $value
                    $server.Configuration.Alter()
                    
                    [pscustomobject]@{
                        ComputerName           = $server.ComputerName
                        InstanceName           = $server.ServiceName
                        SqlInstance            = $server.DomainInstanceName
                        ConfigName             = $configuration
                        OldValue               = $currentRunValue
                        NewValue               = $value
                    }
                    
                    #If it's a dynamic setting we're all clear, otherwise let the user know that SQL needs to be restarted for the change to take
                    if ($isDynamic -eq $false) {
                        Write-Message -Level Warning -Message "Configuration setting $configuration has been set, but restart of SQL Server is required for the new value `"$value`" to be used (old value: `"$currentRunValue`")" -Target $Instance
                    }
                }
                catch {
                    Stop-Function -Message "Unable to change config setting" -Target $Instance -ErrorRecord $_ -Continue -ContinueLabel main
                }
            }
        }
    }
}
tools\dbatools\functions\Set-DbaSpn.ps1
#ValidationTags#FlowControl,Pipeline#
function Set-DbaSpn {
    <#
.SYNOPSIS
Sets an SPN for a given service account in active directory (and also enables delegation to the same SPN by default)

.DESCRIPTION
This function will connect to Active Directory and search for an account. If the account is found, it will attempt to add an SPN. Once the SPN
is added, the function will also set delegation to that service, unless -NoDelegation is specified. In order to run this function, the credential you provide must have write
access to Active Directory.

Note: This function supports -WhatIf

.PARAMETER SPN
The SPN you want to add

.PARAMETER ServiceAccount
The account you want the SPN added to

.PARAMETER Credential
The credential you want to use to connect to Active Directory to make the changes

.PARAMETER NoDelegation
Skips setting the delegation

.PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

.PARAMETER Confirm
Turns confirmations before changes on or off

.PARAMETER WhatIf
Shows what would happen if the command was executed

.NOTES
Tags: SPN
Author: Drew Furgiuele (@pittfurg), http://www.port1433.com

dbatools PowerShell module (https://dbatools.io)
Copyright (C) 2016 Chrissy LeMaire
License: MIT https://opensource.org/licenses/MIT

.LINK
https://dbatools.io/Set-DbaSpn

.EXAMPLE
Set-DbaSpn -SPN MSSQLSvc\SQLSERVERA.domain.something -ServiceAccount domain\account

Connects to Active Directory and adds a provided SPN to the given account.

Set-DbaSpn -SPN MSSQLSvc\SQLSERVERA.domain.something -ServiceAccount domain\account -EnableException

Connects to Active Directory and adds a provided SPN to the given account, suppressing all error messages and throw exceptions that can be caught instead

.EXAMPLE
Set-DbaSpn -SPN MSSQLSvc\SQLSERVERA.domain.something -ServiceAccount domain\account -Credential (Get-Credential)

Connects to Active Directory and adds a provided SPN to the given account. Uses alternative account to connect to AD.

.EXAMPLE
Set-DbaSpn -SPN MSSQLSvc\SQLSERVERA.domain.something -ServiceAccount domain\account -NoDelegation

Connects to Active Directory and adds a provided SPN to the given account, without the delegation.

.EXAMPLE
Test-DbaSpn -ComputerName sql2016 | Where { $_.isSet -eq $false } | Set-DbaSpn

Sets all missing SPNs for sql2016

.EXAMPLE
Test-DbaSpn -ComputerName sql2016 | Where { $_.isSet -eq $false } | Set-DbaSpn -WhatIf

Displays what would happen trying to set all missing SPNs for sql2016

#>
    [cmdletbinding(SupportsShouldProcess = $true, DefaultParameterSetName = "Default")]
    param (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName)]
        [Alias("RequiredSPN")]
        [string]$SPN,
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName)]
        [Alias("InstanceServiceAccount", "AccountName")]
        [string]$ServiceAccount,
        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName)]
        [PSCredential]$Credential,
        [switch]$NoDelegation,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        #did we find the server account?
        Write-Message -Message "Looking for account $ServiceAccount..." -Level Verbose
        $searchfor = 'User'
        if ($ServiceAccount.EndsWith('$')) {
            $searchfor = 'Computer'
        }
        try {
            $Result = Get-DbaADObject -ADObject $ServiceAccount -Type $searchfor -Credential $Credential -EnableException
        }
        catch {
            Stop-Function -Message "AD lookup failure. This may be because the domain cannot be resolved for the SQL Server service account ($ServiceAccount). $($_.Exception.Message)" -EnableException $EnableException -InnerErrorRecord $_ -Target $ServiceAccount
        }
        if ($Result.Count -gt 0) {
            try {
                $adentry = $Result.GetUnderlyingObject()
            }
            catch {
                Stop-Function -Message "The SQL Service account ($ServiceAccount) has been found, but you don't have enough permission to inspect its properties $($_.Exception.Message)" -EnableException $EnableException -InnerErrorRecord $_ -Target $ServiceAccount
            }
        }
        else {
            Stop-Function -Message "The SQL Service account ($ServiceAccount) has not been found" -EnableException $EnableException -Target $ServiceAccount
        }
        # Cool! Add an SPN
        $delegate = $true
        if ($PSCmdlet.ShouldProcess("$spn", "Adding SPN to service account")) {
            try {
                $null = $adentry.Properties['serviceprincipalname'].Add($spn)
                $status = "Successfully added SPN"
                $adentry.CommitChanges()
                Write-Message -Message "Added SPN $spn to $ServiceAccount" -Level Verbose
                $set = $true
            }
            catch {
                Write-Message -Message "Could not add SPN. $($_.Exception.Message)" -Level Warning -EnableException $EnableException.ToBool() -ErrorRecord $_ -Target $ServiceAccount
                $set = $false
                $status = "Failed to add SPN"
                $delegate = $false
            }

            [pscustomobject]@{
                Name           = $spn
                ServiceAccount = $ServiceAccount
                Property       = "servicePrincipalName"
                IsSet          = $set
                Notes          = $status
            }
        }

        #if we have the SPN set, we can add the delegation
        if ($delegate) {
            # but only if $NoDelegation is not passed
            if (!$NoDelegation) {
                if ($PSCmdlet.ShouldProcess("$spn", "Adding constrained delegation to service account for SPN")) {
                    try {
                        $null = $adentry.Properties['msDS-AllowedToDelegateTo'].Add($spn)
                        $adentry.CommitChanges()
                        Write-Message -Message "Added kerberos delegation to $spn for $ServiceAccount" -Level Verbose
                        $set = $true
                        $status = "Successfully added constrained delegation"
                    }
                    catch {
                        Write-Message -Message "Could not add delegation. $($_.Exception.Message)" -Level Warning -EnableException $EnableException.ToBool() -ErrorRecord $_ -Target $ServiceAccount
                        $set = $false
                        $status = "Failed to add constrained delegation"
                    }

                    [pscustomobject]@{
                        Name           = $spn
                        ServiceAccount = $ServiceAccount
                        Property       = "msDS-AllowedToDelegateTo"
                        IsSet          = $set
                        Notes          = $status
                    }
                }
            }
            else {
                Write-Message -Message "Skipping delegation as instructed" -Level Verbose
            }
        }
    }
}
tools\dbatools\functions\Set-DbaStartupParameter.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Set-DbaStartupParameter {
    <#
        .SYNOPSIS
            Sets the Startup Parameters for a SQL Server instance

        .DESCRIPTION
            Modifies the startup parameters for a specified SQL Server Instance

            For full details of what each parameter does, please refer to this MSDN article - https://msdn.microsoft.com/en-us/library/ms190737(v=sql.105).aspx

        .PARAMETER SqlInstance
            The SQL Server instance to be modified

            If the Sql Instance is offline path parameters will be ignored as we cannot test the instance's access to the path. If you want to force this to work then please use the Force switch

        .PARAMETER SqlCredential
            Windows or Sql Login Credential with permission to log into the SQL instance

        .PARAMETER Credential
            Windows Credential with permission to log on to the server running the SQL instance

        .PARAMETER MasterData
            Path to the data file for the Master database

            Will be ignored if SqlInstance is offline or the Offline switch is set. To override this behaviour use the Force switch. This is to ensure you understand the risk as we cannot validate the path if the instance is offline

        .PARAMETER MasterLog
            Path to the log file for the Master database

            Will be ignored if SqlInstance is offline or the Offline switch is set. To override this behaviour use the Force switch. This is to ensure you understand the risk as we cannot validate the path if the instance is offline

        .PARAMETER ErrorLog
            Path to the SQL Server error log file

            Will be ignored if SqlInstance is offline or the Offline switch is set. To override this behaviour use the Force switch. This is to ensure you understand the risk as we cannot validate the path if the instance is offline

        .PARAMETER TraceFlags
            A comma separated list of TraceFlags to be applied at SQL Server startup
            By default these will be appended to any existing trace flags set

        .PARAMETER CommandPromptStart
            Shortens startup time when starting SQL Server from the command prompt. Typically, the SQL Server Database Engine starts as a service by calling the Service Control Manager.
            Because the SQL Server Database Engine does not start as a service when starting from the command prompt

        .PARAMETER MinimalStart
            Starts an instance of SQL Server with minimal configuration. This is useful if the setting of a configuration value (for example, over-committing memory) has
            prevented the server from starting. Starting SQL Server in minimal configuration mode places SQL Server in single-user mode

        .PARAMETER MemoryToReserve
            Specifies an integer number of megabytes (MB) of memory that SQL Server will leave available for memory allocations within the SQL Server process,
            but outside the SQL Server memory pool. The memory outside of the memory pool is the area used by SQL Server for loading items such as extended procedure .dll files,
            the OLE DB providers referenced by distributed queries, and automation objects referenced in Transact-SQL statements. The default is 256 MB.

        .PARAMETER SingleUser
            Start Sql Server in single user mode

        .PARAMETER NoLoggingToWinEvents
            Don't use Windows Application events log

        .PARAMETER StartAsNamedInstance
            Allows you to start a named instance of SQL Server

        .PARAMETER DisableMonitoring
            Disables the following monitoring features:

            SQL Server performance monitor counters
            Keeping CPU time and cache-hit ratio statistics
            Collecting information for the DBCC SQLPERF command
            Collecting information for some dynamic management views
            Many extended-events event points

            ** Warning *\* When you use the -x startup option, the information that is available for you to diagnose performance and functional problems with SQL Server is greatly reduced.

        .PARAMETER SingleUserDetails
            The username for single user

        .PARAMETER IncreasedExtents
            Increases the number of extents that are allocated for each file in a filegroup.

        .PARAMETER TraceFlagsOverride
            Overrides the default behaviour and replaces any existing trace flags. If not trace flags specified will just remove existing ones

        .PARAMETER StartUpConfig
            Pass in a previously saved SQL Instance startup config
            using this parameter will set TraceFlagsOverride to true, so existing Trace Flags will be overridden

        .PARAMETER Offline
            Setting this switch will try perform the requested actions without connect to the SQL Server Instance, this will speed things up if you know the Instance is offline.

            When working offline, path inputs (MasterData, MasterLog and ErrorLog) will be ignored, unless Force is specified

        .PARAMETER Force
            By default we test the values passed in via MasterData, MasterLog, ErrorLog

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Service, Startup, Parameter, Configure
            Author: Stuart Moore (@napalmgram), stuart-moore.com

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            Set-DbaStartupParameter -SqlInstance server1\instance1 -SingleUser

            Will configure the SQL Instance server1\instance1 to startup up in Single User mode at next startup

        .EXAMPLE
            Set-DbaStartupParameter -SqlInstance sql2016 -IncreasedExtents

            Will configure the SQL Instance sql2016 to IncreasedExtents = True (-E)

        .EXAMPLE
            Set-DbaStartupParameter -SqlInstance sql2016  -IncreasedExtents:$false -WhatIf

            Shows what would happen if you attempted to configure the SQL Instance sql2016 to IncreasedExtents = False (no -E)

        .EXAMPLE
            Set-DbaStartupParameter -SqlInstance server1\instance1 -SingleUser -TraceFlags 8032,8048

            This will append Trace Flags 8032 and 8048 to the startup parameters

        .EXAMPLE
            Set-DbaStartupParameter -SqlInstance sql2016 -SingleUser:$false -TraceFlagsOverride

            This will remove all trace flags and set SinguleUser to false

        .EXAMPLE
            Set-DbaStartupParameter -SqlInstance server1\instance1 -SingleUser -TraceFlags 8032,8048 -TraceFlagsOverride

            This will set Trace Flags 8032 and 8048 to the startup parameters, removing any existing Trace Flags

        .EXAMPLE
            Set-DbaStartupParameter -SqlInstance sql2016 -SingleUser:$false -TraceFlagsOverride -Offline

            This will remove all trace flags and set SinguleUser to false from an offline instance

        .EXAMPLE
            Set-DbaStartupParameter -SqlInstance sql2016 -ErrorLog c:\Sql\ -Offline

            This will attempt to change the ErrorLog path to c:\sql\. However, with the offline switch this will not happen. To force it, use the -Force switch like so:

            Set-DbaStartupParameter -SqlInstance sql2016 -ErrorLog c:\Sql\ -Offline -Force

        .EXAMPLE
            $StartupConfig = Get-DbaStartupParameter -SqlInstance server1\instance1
            Set-DbaStartupParameter -SqlInstance server1\instance1 -SingleUser -NoLoggingToWinEvents
            #Restart your SQL instance with the tool of choice
            #Do Some work
            Set-DbaStartupParameter -SqlInstance server1\instance1 -StartUpConfig $StartUpConfig
            #Restart your SQL instance with the tool of choice and you're back to normal

            In this example we take a copy of the existing startup configuration of server1\instance1

            We then change the startup parameters ahead of some work

            After the work has been completed, we can push the original startup parameters back to server1\instance1 and resume normal operation
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High")]
    param ([parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [PSCredential]$Credential,
        [string]$MasterData,
        [string]$MasterLog,
        [string]$ErrorLog,
        [string[]]$TraceFlags,
        [switch]$CommandPromptStart,
        [switch]$MinimalStart,
        [int]$MemoryToReserve,
        [switch]$SingleUser,
        [string]$SingleUserDetails,
        [switch]$NoLoggingToWinEvents,
        [switch]$StartAsNamedInstance,
        [switch]$DisableMonitoring,
        [switch]$IncreasedExtents,
        [switch]$TraceFlagsOverride,
        [object]$StartUpConfig,
        [switch]$Offline,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {

        if (-not $Offline) {
            try {
                Write-Message -Level VeryVerbose -Message "Connecting to $SqlInstance" -Target $SqlInstance
                $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
            }
            catch {
                Write-Message -Level Warning -Message "Failed to connect to $SqlInstance, will try to work with just WMI. Path options will be ignored unless Force was indicated"
                $Server = $SqlInstance
                $Offline = $true
            }
        }
        else {
            Write-Message -Level Verbose -Message "Offline switch set, proceeding with just WMI"
            $Server = $SqlInstance
        }

        #Get Current parameters:
        $currentstartup = Get-DbaStartupParameter -SqlInstance $server -Credential $Credential
        $originalparamstring = $currentstartup.ParameterString

        Write-Message -Level Output -Message "Original startup parameter string: $originalparamstring"

        if ('startUpconfig' -in $PsBoundParameters.keys) {
            Write-Message -Level VeryVerbose -Message "StartupObject passed in"
            $newstartup = $StartUpConfig
            $TraceFlagsOverride = $true
        }
        else {
            Write-Message -Level VeryVerbose -Message "Parameters passed in"
            $newstartup = $currentstartup.PSObject.copy()
            foreach ($param in ($PsBoundParameters.keys | Where-Object { $_ -in ($newstartup.PSObject.Properties.name) })) {
                if ($PsBoundParameters.item($param) -ne $newstartup.$param) {
                    $newstartup.$param = $PsBoundParameters.item($param)
                }
            }
        }

        if (!($currentstartup.SingleUser)) {

            if ($newstartup.Masterdata.length -gt 0) {
                if ($Offline -and -not $Force) {
                    Write-Message -Level Warning -Message "Working offline, skipping untested MasterData path"
                    $ParameterString += "-d$($CurrentStartup.MasterData);"

                }
                else {
                    if ($Force) {
                        $ParameterString += "-d$($newstartup.MasterData);"
                    }
                    elseif (Test-DbaSqlPath -SqlInstance $server -SqlCredential $SqlCredential -Path (Split-Path $newstartup.MasterData -Parent)) {
                        $ParameterString += "-d$($newstartup.MasterData);"
                    }
                    else {
                        Stop-Function -Message "Specified folder for Master Data file is not reachable by instance $SqlInstance"
                        return
                    }
                }
            }
            else {
                Stop-Function -Message "MasterData value must be provided"
                return
            }

            if ($newstartup.ErrorLog.length -gt 0) {
                if ($Offline -and -not $Force) {
                    Write-Message -Level Warning -Message "Working offline, skipping untested ErrorLog path"
                    $ParameterString += "-e$($CurrentStartup.ErrorLog);"
                }
                else {
                    if ($Force) {
                        $ParameterString += "-e$($newstartup.ErrorLog);"
                    }
                    elseif (Test-DbaSqlPath -SqlInstance $server -SqlCredential $SqlCredential -Path (Split-Path $newstartup.ErrorLog -Parent)) {
                        $ParameterString += "-e$($newstartup.ErrorLog);"
                    }
                    else {
                        Stop-Function -Message "Specified folder for ErrorLog  file is not reachable by $SqlInstance"
                        return
                    }
                }
            }
            else {
                Stop-Function -Message "ErrorLog value must be provided"
                return
            }

            if ($newstartup.MasterLog.Length -gt 0) {
                if ($offline -and -not $Force) {
                    Write-Message -Level Warning -Message "Working offline, skipping untested MasterLog path"
                    $ParameterString += "-l$($CurrentStartup.MasterLog);"
                }
                else {
                    if ($Force) {
                        $ParameterString += "-l$($newstartup.MasterLog);"
                    }
                    elseif (Test-DbaSqlPath -SqlInstance $server -SqlCredential $SqlCredential -Path (Split-Path $newstartup.MasterLog -Parent)) {
                        $ParameterString += "-l$($newstartup.MasterLog);"
                    }
                    else {
                        Stop-Function -Message "Specified folder for Master Log  file is not reachable by $SqlInstance"
                        return
                    }
                }
            }
            else {
                Stop-Function -Message "MasterLog value must be provided."
                return
            }
        }
        else {

            Write-Message -Level Verbose -Message "Sql instance is presently configured for single user, skipping path validation"
            if ($newstartup.MasterData.Length -gt 0) {
                $ParameterString += "-d$($newstartup.MasterData);"
            }
            else {
                Stop-Function -Message "Must have a value for MasterData"
                return
            }
            if ($newstartup.ErrorLog.Length -gt 0) {
                $ParameterString += "-e$($newstartup.ErrorLog);"
            }
            else {
                Stop-Function -Message "Must have a value for Errorlog"
                return
            }
            if ($newstartup.MasterLog.Length -gt 0) {
                $ParameterString += "-l$($newstartup.MasterLog);"
            }
            else {
                Stop-Function -Message "Must have a value for MsterLog"
                return
            }
        }

        if ($newstartup.CommandPromptStart) {
            $ParameterString += "-c;"
        }
        if ($newstartup.MinimalStart) {
            $ParameterString += "-f;"
        }
        if ($newstartup.MemoryToReserve -notin ($null, 0)) {
            $ParameterString += "-g$($newstartup.MemoryToReserve)"
        }
        if ($newstartup.SingleUser) {
            if ($SingleUserDetails.length -gt 0) {
                if ($SingleUserDetails -match ' ') {
                    $SingleUserDetails = """$SingleUserDetails"""
                }
                $ParameterString += "-m$SingleUserDetails;"
            }
            else {
                $ParameterString += "-m;"
            }
        }
        if ($newstartup.NoLoggingToWinEvents) {
            $ParameterString += "-n;"
        }
        If ($newstartup.StartAsNamedInstance) {
            $ParameterString += "-s;"
        }
        if ($newstartup.DisableMonitoring) {
            $ParameterString += "-x;"
        }
        if ($newstartup.IncreasedExtents) {
            $ParameterString += "-E;"
        }
        if ($newstartup.TraceFlags -eq 'None') {
            $newstartup.TraceFlags = ''
        }
        if ($TraceFlagsOverride -and 'TraceFlags' -in $PsBoundParameters.keys) {
            if ($null -ne $TraceFlags -and '' -ne $TraceFlags) {
                $newstartup.TraceFlags = $TraceFlags -join ','
                $ParameterString += (($TraceFlags.split(',') | ForEach-Object { "-T$_" }) -join ';') + ";"
            }
        }
        else {
            if ('TraceFlags' -in $PsBoundParameters.keys) {
                if ($null -eq $TraceFlags) { $TraceFlags = '' }
                $oldflags = @($currentstartup.TraceFlags) -split ',' | Where-Object { $_ -ne 'None' }
                $newflags = $TraceFlags
                $newstartup.TraceFlags = (@($oldFlags) + @($newflags) | Sort-Object -Unique) -join ','
            }
            elseif ($TraceFlagsOverride) {
                $newstartup.TraceFlags = ''
            }
            else {
                $newstartup.TraceFlags = if ($currentstartup.TraceFlags -eq 'None') { }
                else { $currentstartup.TraceFlags -join ',' }
            }
            If ($newstartup.TraceFlags.Length -ne 0) {
                $ParameterString += (($newstartup.TraceFlags.split(',') | ForEach-Object { "-T$_" }) -join ';') + ";"
            }
        }

        $instance = $SqlInstance.ComputerName
        $instancename = $SqlInstance.InstanceName
        Write-Message -Level Verbose -Message "Connecting to $instancename on $instance"

        if ($instancename.Length -eq 0) { $instancename = "MSSQLSERVER" }

        $displayname = "SQL Server ($instancename)"

        if ($originalparamstring -eq "$ParameterString" -or "$originalparamstring;" -eq "$ParameterString") {
            Stop-Function -Message "New parameter string would be the same as the old parameter string. Nothing to do." -Target $ParameterString
            return
        }

        $Scriptblock = {
            $instance = $args[0]
            $displayname = $args[1]
            $ParameterString = $args[2]

            $wmisvc = $wmi.Services | Where-Object { $_.DisplayName -eq $displayname }
            $wmisvc.StartupParameters = $ParameterString
            $wmisvc.Alter()
            $wmisvc.Refresh()
            if ($wmisvc.StartupParameters -eq $ParameterString) {
                $true
            }
            else {
                $false
            }
        }

        if ($pscmdlet.ShouldProcess("Setting Sql Server start parameters on $SqlInstance to $ParameterString")) {
            try {
                if ($Credential) {
                    $response = Invoke-ManagedComputerCommand -ComputerName $instance -Credential $Credential -ScriptBlock $Scriptblock -ArgumentList $instance, $displayname, $ParameterString -EnableException
                    $output = Get-DbaStartupParameter -SqlInstance $server -Credential $Credential -EnableException
                    Add-Member -Force -InputObject $output -MemberType NoteProperty -Name OriginalStartupParameters -Value $originalparamstring
                }
                else {
                    $response = Invoke-ManagedComputerCommand -ComputerName $instance -ScriptBlock $Scriptblock -ArgumentList $instance, $displayname, $ParameterString -EnableException
                    $output = Get-DbaStartupParameter -SqlInstance $server -EnableException
                    Add-Member -Force -InputObject $output -MemberType NoteProperty -Name OriginalStartupParameters -Value $originalparamstring
                }

                $output

                Write-Message -Level Output -Message "Startup parameters changed on $SqlInstance. You must restart SQL Server for changes to take effect."
            }
            catch {
                Stop-Function -Message "Startup parameters failed to change on $SqlInstance. " -Target $SqlInstance -ErrorRecord $_
                return
            }
        }
    }
}
tools\dbatools\functions\Set-DbaTcpPort.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Set-DbaTcpPort {
    <#
        .SYNOPSIS
            Changes the TCP port used by the specified SQL Server.

        .DESCRIPTION
            This function changes the TCP port used by the specified SQL Server.

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to.

        .PARAMETER SqlCredential
            Credential object used to connect to the SQL Server instance as a different user

        .PARAMETER Credential
            Credential object used to connect to the Windows server itself as a different user

        .PARAMETER Port
            TCPPort that SQLService should listen on.

        .PARAMETER IpAddress
            Wich IpAddress should the portchange , if omitted allip (0.0.0.0) will be changed with the new portnumber.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .NOTES
            Tags: Service, Port, TCP, Configure
            Author: [email protected], @H0s0n77

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Set-DbaTcpPort

        .EXAMPLE
            Set-DbaTcpPort -SqlInstance SqlInstance2014a -Port 1433

            Sets the port number 1433 for allips on the default instance on SqlInstance2014a

        .EXAMPLE
            Set-DbaTcpPort -SqlInstance winserver\sqlexpress -IpAddress 192.168.1.22 -Port 1433

            Sets the port number 1433 for IP 192.168.1.22 on the sqlexpress instance on winserver

        .EXAMPLE
            Set-DbaTcpPort -SqlInstance 'SQLDB2014A' ,'SQLDB2016B' -port 1337

            Sets the port number 1337 for ALLIP's on SqlInstance SQLDB2014A and SQLDB2016B
    #>
    [CmdletBinding(ConfirmImpact = "High")]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [PSCredential]$Credential,
        [parameter(Mandatory = $true)]
        [ValidateRange(1, 65535)]
        [int]$Port,
        [IpAddress[]]$IpAddress,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {

        if ($IpAddress.Length -eq 0) {
            $IpAddress = '0.0.0.0'
        }
        else {
            if ($SqlInstance.count -gt 1) {
                Stop-Function -Message "-IpAddress switch cannot be used with a collection of serveraddresses" -Target $SqlInstance
                return
            }
        }
    }

    process {
        if (Test-FunctionInterrupt) { return }

        foreach ($instance in $SqlInstance) {

            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $wmiinstancename = $server.ServiceName


            if ($server.IsClustered) {
                Write-Message -Level Verbose -Message "Instance is clustered fetching nodes..."
                $clusternodes = (Get-DbaClusterNode -SqlInstance $server).ComputerName -join ", "

                Write-Message -Level Output -Message "$instance is a clustered instance, portchanges will be reflected on all nodes ($clusternodes) after a failover"
            }

            $scriptblock = {
                $instance = $args[0]
                $wmiinstancename = $args[1]
                $port = $args[2]
                $IpAddress = $args[3]
                $sqlinstanceName = $args[4]

                $wmi = New-Object Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer $instance
                $wmiinstance = $wmi.ServerInstances | Where-Object { $_.Name -eq $wmiinstancename }
                $tcp = $wmiinstance.ServerProtocols | Where-Object { $_.DisplayName -eq 'TCP/IP' }
                $IpAddress = $tcp.IpAddresses | where-object { $_.IpAddress -eq $IpAddress }
                $tcpport = $IpAddress.IpAddressProperties | Where-Object { $_.Name -eq 'TcpPort' }

                $oldport = $tcpport.Value
                try {
                    $tcpport.value = $port
                    $tcp.Alter()
                    [pscustomobject]@{
                        ComputerName  = $env:COMPUTERNAME
                        InstanceName  = $wmiinstancename
                        SqlInstance   = $sqlinstanceName
                        OldPortNumber = $oldport
                        PortNumber    = $Port
                        Status        = "Success"
                    }
                }
                catch {
                    [pscustomobject]@{
                        ComputerName  = $env:COMPUTERNAME
                        InstanceName  = $wmiinstancename
                        SqlInstance   = $sqlinstanceName
                        OldPortNumber = $oldport
                        PortNumber    = $Port
                        Status        = "Failed: $_"
                    }
                }
            }

            try {
                $computerName = $instance.ComputerName
                $resolved = Resolve-DbaNetworkName -ComputerName $computerName

                Write-Message -Level Verbose -Message "Writing TCPPort $port for $instance to $($resolved.FQDN)..."
                Invoke-ManagedComputerCommand -ComputerName $resolved.FQDN -ScriptBlock $scriptblock -ArgumentList $Server.ComputerName, $wmiinstancename, $port, $IpAddress, $server.DomainInstanceName -Credential $Credential

            }
            catch {
                Invoke-ManagedComputerCommand -ComputerName $instance.ComputerName -ScriptBlock $scriptblock -ArgumentList $Server.ComputerName, $wmiinstancename, $port, $IpAddress, $server.DomainInstanceName -Credential $Credential
            }
        }
    }
}
tools\dbatools\functions\Set-DbaTempDbConfiguration.ps1
function Set-DbaTempDbConfiguration {
    <#
        .SYNOPSIS
            Sets tempdb data and log files according to best practices.

        .DESCRIPTION
            Calculates tempdb size and file configurations based on passed parameters, calculated values, and Microsoft best practices. User must declare SQL Server to be configured and total data file size as mandatory values. Function then calculates the number of data files based on logical cores on the target host and create evenly sized data files based on the total data size declared by the user, with a log file 25% of the total data file size.

            Other parameters can adjust the settings as the user desires (such as different file paths, number of data files, and log file size). No functions that shrink or delete data files are performed. If you wish to do this, you will need to resize tempdb so that it is "smaller" than what the function will size it to before running the function.

        .PARAMETER SqlInstance
            The SQL Server Instance to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER DataFileCount
            Specifies the number of data files to create. If this number is not specified, the number of logical cores of the host will be used.

        .PARAMETER DataFileSizeMB
            Specifies the total data file size in megabytes. This is distributed across the total number of data files.

        .PARAMETER LogFileSizeMB
            Specifies the log file size in megabytes. If not specified, this will be set to 25% of total data file size.

        .PARAMETER DataFileGrowthMB
            Specifies the growth amount for the data file(s) in megabytes. The default is 512 MB.

        .PARAMETER LogFileGrowthMB
            Specifies the growth amount for the log file in megabytes. The default is 512 MB.

        .PARAMETER DataPath
            Specifies the filesystem path in which to create the tempdb data files. If not specified, current tempdb location will be used.

        .PARAMETER LogPath
            Specifies the filesystem path in which to create the tempdb log file. If not specified, current tempdb location will be used.

        .PARAMETER OutputScriptOnly
            If this switch is enabled, only the T-SQL script to change the tempdb configuration is created and output.

        .PARAMETER OutFile
            Specifies the filesystem path into which the generated T-SQL script will be saved.

        .PARAMETER DisableGrowth
            If this switch is enabled, the tempdb files will be configured to not grow. This overrides -DataFileGrowthMB and -LogFileGrowthMB.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Tempdb, Space, Configure, Configuration
            Author: Michael Fal (@Mike_Fal), http://mikefal.net

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Set-DbaTempDbConfiguration

        .EXAMPLE
            Set-DbaTempDbConfiguration -SqlInstance localhost -DataFileSizeMB 1000

            Creates tempdb with a number of data files equal to the logical cores where each file is equal to 1000MB divided by the number of logical cores, with a log file of 250MB.

        .EXAMPLE
            Set-DbaTempDbConfiguration -SqlInstance localhost -DataFileSizeMB 1000 -DataFileCount 8

            Creates tempdb with 8 data files, each one sized at 125MB, with a log file of 250MB.

        .EXAMPLE
            Set-DbaTempDbConfiguration -SqlInstance localhost -DataFileSizeMB 1000 -OutputScriptOnly

            Provides a SQL script output to configure tempdb according to the passed parameters.

        .EXAMPLE
            Set-DbaTempDbConfiguration -SqlInstance localhost -DataFileSizeMB 1000 -DisableGrowth

            Disables the growth for the data and log files.

        .EXAMPLE
            Set-DbaTempDbConfiguration -SqlInstance localhost -DataFileSizeMB 1000 -OutputScriptOnly

            Returns the T-SQL script representing tempdb configuration.
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [int]$DataFileCount,
        [Parameter(Mandatory = $true)]
        [int]$DataFileSizeMB,
        [int]$LogFileSizeMB,
        [int]$DataFileGrowthMB = 512,
        [int]$LogFileGrowthMB = 512,
        [string]$DataPath,
        [string]$LogPath,
        [string]$OutFile,
        [switch]$OutputScriptOnly,
        [switch]$DisableGrowth,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        $sql = @()
        Write-Message -Level Verbose -Message "Connecting to $SqlInstance"
        $server = Connect-SqlInstance $sqlinstance -SqlCredential $SqlCredential

        if ($server.VersionMajor -lt 9) {
            Stop-Function -Message "SQL Server 2000 is not supported"
            return
        }
    }

    process {

        if (Test-FunctionInterrupt) {
            return
        }

        $cores = $server.Processors
        if ($cores -gt 8) {
            $cores = 8
        }

        #Set DataFileCount if not specified. If specified, check against best practices.
        if (-not $DataFileCount) {
            $DataFileCount = $cores
            Write-Message -Message "Data file count set to number of cores: $DataFileCount" -Level Verbose
        }
        else {
            if ($DataFileCount -gt $cores) {
                Write-Message -Message "Data File Count of $DataFileCount exceeds the Logical Core Count of $cores. This is outside of best practices." -Level Warning
            }
            Write-Message -Message "Data file count set explicitly: $DataFileCount" -Level Verbose
        }

        $DataFilesizeSingleMB = $([Math]::Floor($DataFileSizeMB / $DataFileCount))
        Write-Message -Message "Single data file size (MB): $DataFilesizeSingleMB." -Level Verbose

        if ($DataPath) {
            if ((Test-DbaSqlPath -SqlInstance $server -Path $DataPath) -eq $false) {
                Stop-Function -Message "$datapath is an invalid path."
                return
            }
        }
        else {
            $Filepath = $server.Databases['tempdb'].ExecuteWithResults('SELECT physical_name as FileName FROM sys.database_files WHERE file_id = 1').Tables[0].Rows[0].FileName
            $DataPath = Split-Path $Filepath
        }

        Write-Message -Message "Using data path: $datapath." -Level Verbose

        if ($LogPath) {
            if ((Test-DbaSqlPath -SqlInstance $server -Path $LogPath) -eq $false) {
                Stop-Function -Message "$LogPath is an invalid path."
                return
            }
        }
        else {
            $Filepath = $server.Databases['tempdb'].ExecuteWithResults('SELECT physical_name as FileName FROM sys.database_files WHERE file_id = 2').Tables[0].Rows[0].FileName
            $LogPath = Split-Path $Filepath
        }
        Write-Message -Message "Using log path: $LogPath." -Level Verbose

        # Check if the file growth needs to be disabled
        if ($DisableGrowth) {
            $DataFileGrowthMB = 0
            $LogFileGrowthMB = 0
        }

        # Check current tempdb. Throw an error if current tempdb is larger than config.
        $CurrentFileCount = $server.Databases['tempdb'].ExecuteWithResults('SELECT count(1) as FileCount FROM sys.database_files WHERE type=0').Tables[0].Rows[0].FileCount
        $TooBigCount = $server.Databases['tempdb'].ExecuteWithResults("SELECT TOP 1 (size/128) as Size FROM sys.database_files WHERE size/128 > $DataFilesizeSingleMB AND type = 0").Tables[0].Rows[0].Size

        if ($CurrentFileCount -gt $DataFileCount) {
            Stop-Function -Message "Current tempdb not suitable to be reconfigured. The current tempdb has a greater number of files ($CurrentFileCount) than the calculated configuration ($DataFileCount)."
            return
        }

        if ($TooBigCount) {
            Stop-Function -Message "Current tempdb not suitable to be reconfigured. The current tempdb ($TooBigCount MB) is larger than the calculated individual file configuration ($DataFilesizeSingleMB MB)."
            return
        }

        $EqualCount = $server.Databases['tempdb'].ExecuteWithResults("SELECT count(1) as FileCount FROM sys.database_files WHERE size/128 = $DataFilesizeSingleMB AND type = 0").Tables[0].Rows[0].FileCount

        if ($EqualCount -gt 0) {
            Stop-Function -Message "Current tempdb not suitable to be reconfigured. The current tempdb is the same size as the specified DataFileSizeMB."
            return
        }

        Write-Message -Message "tempdb configuration validated." -Level Verbose

        $DataFiles = $server.Databases['tempdb'].ExecuteWithResults("select f.name as Name, f.physical_name as FileName from sys.filegroups fg join sys.database_files f on fg.data_space_id = f.data_space_id where fg.name = 'PRIMARY' and f.type_desc = 'ROWS'").Tables[0];

        #Checks passed, process reconfiguration
        for ($i = 0; $i -lt $DataFileCount; $i++) {
            $File = $DataFiles.Rows[$i]
            if ($File) {
                $Filename = Split-Path $File.FileName -Leaf
                $LogicalName = $File.Name
                $NewPath = "$datapath\$Filename"
                $sql += "ALTER DATABASE tempdb MODIFY FILE(name=$LogicalName,filename='$NewPath',size=$DataFilesizeSingleMB MB,filegrowth=$DataFileGrowthMB);"
            }
            else {
                $NewName = "tempdev$i.ndf"
                $NewPath = "$datapath\$NewName"
                $sql += "ALTER DATABASE tempdb ADD FILE(name=tempdev$i,filename='$NewPath',size=$DataFilesizeSingleMB MB,filegrowth=$DataFileGrowthMB);"
            }
        }

        if (-not $LogFileSizeMB) {
            $LogFileSizeMB = [Math]::Floor($DataFileSizeMB / 4)
        }

        $logfile = $server.Databases['tempdb'].ExecuteWithResults("SELECT name, physical_name as FileName FROM sys.database_files WHERE file_id = 2").Tables[0].Rows[0];
        $Filename = Split-Path $logfile.FileName -Leaf
        $LogicalName = $logfile.Name
        $NewPath = "$LogPath\$Filename"
        $sql += "ALTER DATABASE tempdb MODIFY FILE(name=$LogicalName,filename='$NewPath',size=$LogFileSizeMB MB,filegrowth=$LogFileGrowthMB);"

        Write-Message -Message "SQL Statement to resize tempdb." -Level Verbose
        Write-Message -Message ($sql -join "`n`n") -Level Verbose

        if ($OutputScriptOnly) {
            return $sql
        }
        elseif ($OutFile) {
            $sql | Set-Content -Path $OutFile
        }
        else {
            if ($Pscmdlet.ShouldProcess($SqlInstance, "Executing query and informing that a restart is required.")) {
                try {
                    $server.Databases['master'].ExecuteNonQuery($sql)
                    Write-Message -Level Verbose -Message "tempdb successfully reconfigured."

                    [PSCustomObject]@{
                        ComputerName         = $server.ComputerName
                        InstanceName         = $server.ServiceName
                        SqlInstance          = $server.DomainInstanceName
                        DataFileCount        = $DataFileCount
                        DataFileSizeMB       = $DataFileSizeMB
                        SingleDataFileSizeMB = $DataFilesizeSingleMB
                        LogSizeMB            = $LogFileSizeMB
                        DataPath             = $DataPath
                        LogPath              = $LogPath
                        DataFileGrowthMB     = $DataFileGrowthMB
                        LogFileGrowthMB      = $LogFileGrowthMB
                    }

                    Write-Message -Level Output -Message "tempdb reconfigured. You must restart the SQL Service for settings to take effect."
                }
                catch {
                    Stop-Function -Message "Unable to reconfigure tempdb. Exception: $_" -Target $sql -InnerErrorRecord $_
                    return
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Set-SqlTempDbConfiguration
    }
}
tools\dbatools\functions\Show-DbaDatabaseList.ps1
function Show-DbaDatabaseList {
    <#
        .SYNOPSIS
            Shows a list of databases in a GUI.

        .DESCRIPTION
            Shows a list of databases in a GUI. Returns a string holding the name of the selected database. Hitting cancel returns null.

        .PARAMETER SqlInstance
            The SQL Server Instance to connect to..

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Title
            Title of the window being displayed. Default is "Select Database".

        .PARAMETER Header
            Header text displayed above the database listing. Default is "Select the database:".

        .PARAMETER DefaultDb
            Specify a database to have selected when the window appears.

        .NOTES
            Tags: Database
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Show-DbaDatabaseList

        .EXAMPLE
            Show-DbaDatabaseList -SqlInstance sqlserver2014a

            Shows a GUI list of databases using Windows Authentication to connect to the SQL Server. Returns a string of the selected database.

        .EXAMPLE
            Show-DbaDatabaseList -Source sqlserver2014a -SqlCredential $cred

            Shows a GUI list of databases using SQL credentials to connect to the SQL Server. Returns a string of the selected database.
    #>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string]$Title = "Select Database",
        [string]$Header = "Select the database:",
        [string]$DefaultDb
    )

    begin {
        try {
            Add-Type -AssemblyName PresentationFramework
        }
        catch {
            throw "Windows Presentation Framework required but not installed"
        }

        function Add-TreeItem {
            Param (
                [string]$name,
                [object]$parent,
                [string]$tag
            )

            $childitem = New-Object System.Windows.Controls.TreeViewItem
            $textblock = New-Object System.Windows.Controls.TextBlock
            $textblock.Margin = "5,0"
            $stackpanel = New-Object System.Windows.Controls.StackPanel
            $stackpanel.Orientation = "Horizontal"
            $image = New-Object System.Windows.Controls.Image
            $image.Height = 20
            $image.Width = 20
            $image.Stretch = "Fill"
            $image.Source = $dbicon
            $textblock.Text = $name
            $childitem.Tag = $name

            if ($name -eq $DefaultDb) {
                $childitem.IsSelected = $true
                $script:selected = $name
            }

            [void]$stackpanel.Children.Add($image)
            [void]$stackpanel.Children.Add($textblock)

            $childitem.Header = $stackpanel
            [void]$parent.Items.Add($childitem)
        }

        function Convert-b64toimg {
            param ($base64)

            $bitmap = New-Object System.Windows.Media.Imaging.BitmapImage
            $bitmap.BeginInit()
            $bitmap.StreamSource = [System.IO.MemoryStream][System.Convert]::FromBase64String($base64)
            $bitmap.EndInit()
            $bitmap.Freeze()
            return $bitmap
        }

        $dbicon = Convert-b64toimg "iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAAFRSURBVDhPY/j//z9VMVZBSjCCgQZunFn6/8zenv+7llf83zA75/+6WTn/N80v+L93ddP/M/tnY2jAayDIoNvn5/5/cX/t/89vdv7/9fUQGIPYj2+t/H/xyJT/O1ZUoWjCaeCOxcX///48ShSeWhMC14jXwC9Xs/5/fzHr/6/PW+GaQS78/WH9/y+Pe8DyT3fYEmcgKJw+HHECawJp/vZ60f8v95v/fzgd8P/tVtn/L1cw/n+0iOH/7TlMxBkIigBiDewr9iVsICg2qWrg6qnpA2dgW5YrYQOX9icPAQPfU9PA2S2RRLuwMtaGOAOf73X+//FyGl4DL03jIM5AEFjdH/x//+Lo/1cOlP9/dnMq2MA3x/z/312l/P/4JNH/axoU/0/INUHRhNdAEDi+pQ1cZIFcDEpvoPCaVOTwf1Gjy/9ds5MxNGAYSC2MVZB8/J8BAGcHwqQBNWHRAAAAAElFTkSuQmCC"
        $foldericon = Convert-b64toimg "iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAAHaSURBVDhPY/j//z9VMVZBSjBWQUowVkFKMApnzZL+/+gYWZ4YDGeANL95sun/j3fbwPjbm5X/Pz+cRLKhcAayq2B45YKe/8vndoHx4lltYLxgajMKhumHYRQDf37Yh4J/fNry//fb1f9/v1n6/8/Tqf//3O/6/+dO9f9fV4v+fzmV/v/L0aj/lflJQO1YDAS5AmwI1MvfPyAZ9KgbYtDlvP/fzyT9/3w45P+HPT7/z8+UwG0gyDvIBmIYBnQVyDCQq0CGPV9p8v94P/f/rKQwoHYsBs4HhgfIQJjLfr+YjdOwt5tt/z9eov1/fxf3/+ggD6B2HAaCXQYKM6hhv+81oYQXzLCXq03/P5qn/H9LE/9/LycroHYsBs7oq4EYCDIM6FVshr3Z4gg2DOS6O9Nk/q+sFvlvZawD1I7FwKldleC0h2zY9wuZEMP2+aMYdn+W/P/rE0T/zy+T+q+jJg/UjsXASe1l/z/cX/T/1dn8/492ePy/vc7s/82VOv8vLVT9f3yGwv89ffL/1zXL/l9dJwF2GciwaYVy/xVlxIDasRjY31Lyv7Uy+39ZTvz/1JiA/8Hejv8dLA3+62sqgTWJC/HixDAzQBjOoBbGKkgJxipICcYqSD7+zwAAkIiWzSGuSg0AAAAASUVORK5CYII="
        $dbatoolsicon = Convert-b64toimg "iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAAO9SURBVEhL3VVdTFNXHO9MzPTF+OzDeBixFdTMINIWsAUK3AIVkFvAIQVFRLYZKR8Wi1IEKV9DYB8PGFAyEx8QScySabYY5+I2JvK18iWISKGk0JGhLzA3+e2c29uHtpcvH/0lv9yennN+v3vO/3fOFb2fCAg4vXWPNOmMRJ745TtTSskqeElviGXJ0XtkWvjJkyGLPoFAVQZoe/NkX/n6Mh/ysu4Qy7WZdJAutxRW6zT6LcNQaE4LiGgREH4cibpCMNqzCIk9hbScEoSSZ0zKOa7fRxG/k5d1h8ukvO4a5ubmMT1jw5E0vZcBZWzqOTS3dcB8tRXZeRX4/v5DZH5uIu0Wrn8NEzaNDjgYoUPd120oMjViX2iql8H6ZFd8DzE7eFl3iOWpuyQydlh44kbJroilSd8RuQ+cqh7wC9Z+JJaxY8KTN0gp+5Yk9DaREzYhb5FOBwZFZ6LlZifKa5ux//AxYTHCvSEp8A9O5n77B6dwqXS119guZ+GrGq9jfn4eM7ZZxB/PdxN2UfOpHq3kRWq/uoE8Yx3u/fQLzhSYUdN0g+tfN126z0oxNj6BJz0Dq0b4E2UawuJzuPhKyZmKYr/AocgMrk37VzWRBLGRdE/psuXqk9wkT/GNUCJLWqS3By/rDh9FxjaSrnahiZ7cq8wCUzKImLIJqC+Ngbk4gmjjIKKKB6Aq7l+OLBmfVF0YnlQZR1p4eSd2y5IiyEr+oyJ0CwIi0gUNKAOPmnG04Q0utf+DHweWkFjjQOyVWajLpsCUPkeUcRgqAzE09Dfz8k64aqI9YcDziUk87bMgOCZL0CQ0ux2J9UtIbXyFwall/PD0NeLKrU6DkhGymj8RXtRDjU7x8k64TKpJQmi6bLOzSEgv8DYhNWMujiK+9jU0VQs4Vm/H2MwSOh4vcP+rii2cQVh+F+IqbRJe3glyReuoSFBUJtpu3eWulv2h3ueE1iOu0g5N9QL3jLk8jerbdrz59y1yGoYQUdSLsII/CLscIsD9UPrLUz4myXhBhWjCPMVdPBBnhMbsIAZzSDDbcOvRIhyLy6i4+Qyq82QFxECR9xjK/K5OXtodNHo+CsW2tagunbxADbK+sXP16Bv/G7lNQ8hpHEX21UGoDb/j8NmfoSzoNvCymwdTPvMotsKGB32LaL1H0mS0oOHOFLpH/0L3iAOF3/YSk4dgTBMh/JTNgdVbtzNl1il12UuSpHE+SRayTb0IL3yCMP2vUJKtUuh/szNNK8Jfxw3BZNpiMoGjiKPJm54Ffw8gEv0PQRYX7wDAUKEAAAAASUVORK5CYII="

        $sourceserver = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
    }

    process {
        # Create XAML form in Visual Studio, ensuring the ListView looks chromeless
        [xml]$xaml = "<Window
        xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
        xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
        Title='$Title' SizeToContent='WidthAndHeight' Background='#F0F0F0'
        WindowStartupLocation='CenterScreen' MaxHeight='600'>
    <Grid>
        <TreeView Name='treeview' Height='Auto' Width='Auto' Background='#FFFFFF' BorderBrush='#FFFFFF' Foreground='#FFFFFF' Margin='11,36,11,79'/>
        <Label x:Name='label' Content='$header' HorizontalAlignment='Left' Margin='15,4,10,0' VerticalAlignment='Top'/>
        <StackPanel HorizontalAlignment='Right' Orientation='Horizontal' VerticalAlignment='Bottom' Margin='0,50,10,30'>
        <Button Name='okbutton' Content='OK'  Margin='0,0,0,0' Width='75'/>
        <Label Width='10'/>
        <Button Name='cancelbutton' Content='Cancel' Margin='0,0,0,0' Width='75'/>
    </StackPanel>
</Grid>
</Window>"
        #second pushes it down
        # Turn XAML into PowerShell objects
        $window = [Windows.Markup.XamlReader]::Load((New-Object System.Xml.XmlNodeReader $xaml))
        $window.icon = $dbatoolsicon

        $xaml.SelectNodes("//*[@Name]") | ForEach-Object { Set-Variable -Name ($_.Name) -Value $window.FindName($_.Name) -Scope Script }

        $childitem = New-Object System.Windows.Controls.TreeViewItem
        $textblock = New-Object System.Windows.Controls.TextBlock
        $textblock.Margin = "5,0"
        $stackpanel = New-Object System.Windows.Controls.StackPanel
        $stackpanel.Orientation = "Horizontal"
        $image = New-Object System.Windows.Controls.Image
        $image.Height = 20
        $image.Width = 20
        $image.Stretch = "Fill"
        $image.Source = $foldericon
        $textblock.Text = "Databases"
        $childitem.Tag = "Databases"
        $childitem.isExpanded = $true
        [void]$stackpanel.Children.Add($image)
        [void]$stackpanel.Children.Add($textblock)
        $childitem.Header = $stackpanel
        $databaseParent = $treeview.Items.Add($childitem)

        try {
            $databases = $sourceserver.databases.name
        }
        catch {
            return
        }

        foreach ($database in $databases) {
            Add-TreeItem -Name $database -Parent $childitem -Tag $nameSpace
        }

        $okbutton.Add_Click( {
                $window.Close()
                $script:okay = $true
            })

        $cancelbutton.Add_Click( {
                $script:selected = $null
                $window.Close()
            })

        $window.Add_SourceInitialized( {
                [System.Windows.RoutedEventHandler]$Event = {
                    if ($_.OriginalSource -is [System.Windows.Controls.TreeViewItem]) {
                        $script:selected = $_.OriginalSource.Tag
                    }
                }
                $treeview.AddHandler([System.Windows.Controls.TreeViewItem]::SelectedEvent, $Event)
            })

        $null = $window.ShowDialog()
    }

    end {
        if ($script:selected.length -gt 0 -and $script:okay -eq $true) {
            return $script:selected
        }

        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Show-SqlDatabaseList
    }
}
tools\dbatools\functions\Show-DbaServerFileSystem.ps1
function Show-DbaServerFileSystem {
    <#
        .SYNOPSIS
            Shows file system on remote SQL Server in a local GUI and returns the selected directory name

        .DESCRIPTION
            Similar to the remote file system popup you see when browsing a remote SQL Server in SQL Server Management Studio, this function allows you to traverse the remote SQL Server's file structure.

            Show-DbaServerFileSystem uses SQL Management Objects to browse the directories and what you see is limited to the permissions of the account running the command.

        .PARAMETER SqlInstance
            The SQL Server whose filesystem you want to view.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .NOTES
            Tags: Storage
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Show-DbaServerFileSystem

        .EXAMPLE
            Show-DbaServerFileSystem -SqlInstance sqlserver2014a

            Shows a list of databases using Windows Authentication to connect to the SQL Server. Returns a string of the selected path.

        .EXAMPLE
            Show-DbaServerFileSystem -Source sqlserver2014a -SqlCredential $cred

            Shows a list of databases using SQL credentials to connect to the SQL Server. Returns a string of the selected path.

    #>
    [CmdletBinding(SupportsShouldProcess)]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [object]$SqlCredential
    )

    begin {
        try {
            Add-Type -AssemblyName PresentationFramework
        }
        catch {
            throw "Windows Presentation Framework required but not installed."
        }

        function Add-TreeItem {
            param (
                [string]$name,
                [object]$parent,
                [string]$tag
            )

            $childitem = New-Object System.Windows.Controls.TreeViewItem

            $textblock = New-Object System.Windows.Controls.TextBlock
            $textblock.Margin = "5,0"

            $stackpanel = New-Object System.Windows.Controls.StackPanel
            $stackpanel.Orientation = "Horizontal"

            $image = New-Object System.Windows.Controls.Image
            $image.Height = 20
            $image.Width = 20
            $image.Stretch = "Fill"

            if ($name.length -eq 1) {
                $image.Source = $diskicon
                $textblock.Text = "$name`:"
                $childitem.Tag = "$name`:"

            }
            else {
                $image.Source = $foldericon
                $textblock.Text = $name
                $childitem.Tag = "$tag\$name"
            }

            [void]$stackpanel.Children.Add($image)
            [void]$stackpanel.Children.Add($textblock)

            $childitem.Header = $stackpanel

            [void]$childitem.Items.Add("*")
            [void]$parent.Items.Add($childitem)
        }

        function Get-SubDirectory {
            Param (
                [string]$nameSpace,
                [object]$treeviewItem
            )

            $textbox.Text = $nameSpace
            try {
                $dirs = $sourceserver.EnumDirectories($nameSpace)
            }
            catch {
                return
            }
            $subdirs = $dirs.Name

            foreach ($subdir in $subdirs) {
                if (!$subdir.StartsWith("$") -and $subdir -ne 'System Volume Information') {
                    Add-TreeItem -Name $subdir -Parent $treeviewItem -Tag $nameSpace
                }
            }
        }

        function Convert-b64toimg {
            param ($base64)

            $bitmap = New-Object System.Windows.Media.Imaging.BitmapImage
            $bitmap.BeginInit()
            $bitmap.StreamSource = [System.IO.MemoryStream][System.Convert]::FromBase64String($base64)
            $bitmap.EndInit()
            $bitmap.Freeze()
            return $bitmap
        }

        $diskicon = Convert-b64toimg "iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAACxEAAAsRAX9kX5EAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAAJtSURBVEhLtZJLa1NBGIa78ze4EZeu3bjS36BduVOsVCGmUqo1QlMaTV3E0oVugm0obdUQTZtYEnNvboTczjlN0ubaWE2aWGhuVQQXKbzOBM+BmokinA48nI+XmefjfDNDAE4dZig3zFBumKHcMEO5YYZywwwppVL5QrG4+217OweO30IiySPJCT1ozQsp7GTzoHvoXpZDpC/4Ut2/nc7sRIhYqO3Xuq1GA512C53WSY46bbSaTVQr1S5pLNAz9OyfPopUlMuf9KFAWO9yeit2uwtWiw1Ohwd+XwBBfxjBAIF+f9dkLzZ9QTg/umGzuuGwe+F0uivBQEhPXcwmJtM6HOSA2+VDOBRBaisNno4nwSOR4PqIx5LgyRhzuQK4NIdYPE7ORXsO6hK9FKkYHb0Po3ENGXIHzVabRP9ex13gsHkI7qcdobwTyUgapncWUBdZ/U3Gxx/j9aoJqVQGpd0KCsWvhPpAavXv8Ls5KCfGcMN7EcOay9CpX8D8/gOoS/RSTjQxLK6QlyRgt1xFvlAn1AZSq/yAZzOCW7pruHpwBlc056C+8xxr5o3BTRSKid6fZHM5VKoH2PvcIjQH0mwcwx/gcFN1HcOxs7ikPI+ZsTnyWHygLtFLkQq1ehZTUxpYrRvI58sQhAIhP5Bsbg9+Txzzcy+hddzDkwUVnk3PY1arA3WJXopUmEwWjIzcheqRGsa3ZjK65b+y8GoJy0tvyEWvY9W+CJvXhqczup6DukQvRSqi0QQMhhVMTk5DqXzYm+v/oFA8IJPQkhdqBnWJXopUnCbMUG6YodwwQ7lhhnLDDOWGGcoNM5QXDP0CA9dqCMSSjzkAAAAASUVORK5CYII="
        $foldericon = Convert-b64toimg "iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAACxEAAAsRAX9kX5EAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAAU0SURBVEhLtZXpU5NXFIf9q/qto9bRuhc3xqUiUK3KoLYq6ihu1VIU1DpjRZ3BHVR8i6hUkDUJASNL2EJIWLKvBLJAAmHRpwfb6ai10Q/2w29u3pP3/p57zrn3vnOA/13/CkwMlzLuv0rMfYaoM59R+1ki1lwiFtHgGcIDuYT6fiZoPsWI6SSBnmyGeo4yZDxN2KEQHXr54H3Pdx5mNe6/QnzkGtMxNVNjeqK+AkLWQ/iMWdg7sxnz3uZ1vIrJSBnx0O9MhBQmggqxkRJC9hsEzFc+Dol5cpkcFaNXHbyasTMVNzARqWXEeZv+lh+xG/OIRWrgtU5ebxG1yu9meb+JEct57C+2fwLEe04g95mOPxJV8JphCcPUuBOHfg/6qm/pa8klNvyA6fFqZqbqZaxiZqKWQF8uVm3qp0LuMTNZyfREuUx+JuEo8agLe1smHTXpGLXSB3O+lKqMV5N1UtZypqJ/SCwHS2P6J0Iis5BqgTyR3tyVcIj4mAtL807aq1IxqLJwdR6TJhfL/xUCK3kjn/Ekg9rvPg6JevL/hlQJ5KmU4qGEg9IXCwNN22ir3ES3ap9AjjLqvU48/FBgN0SFeAzZDDSk3nnf852HWY25zxKPFEut/4JMRmcX5mM8bKJPm0bLs/V01e/B2X6IsPMyscAtRt2XRZdwdx1kQPsJ5Rp1nRGIZCKQqZhs09FiCTuJhQyYNFtoLl9HZ20mjtb9BK0XJJsrhG3nZJvn4Wr/AUfbEc+I/fFsI//xfAcwq4gjR0pTxPRkhQAeMh66zavpHkYDWoz1m3j5ZDWd1duxN+9muO80IVuejCcImI7h0W/F2boLtyFfrBJAwtZTjAdvMTn+VFZ/j7HADSbGahh2FGGoWYeuLImOynRsL3bg7zksgGP4DPvwdu2W52wp4wH6NJvFKgEkaP2J6LA0dKxMxruM+q9LFnfxmH+hu3oVTY+S0FekyFbdhrdzr1wnWXg6MnC1phBwVuOxqelvSBOrBJCRgWOM+a9JmUqI+AoJea4SlMY6pamdlStoVJbTVr6BQemPq22ngDJwNm/B3rSKgFuPzz0gmXwEEug7TMTzm5TpDkHXZYbtFxm2XsTenkX7s2VoS76m9cla+lUbcbxMlQxmAclYNEsIuzSE3N0CSRWrBBC/6RBB50XC3kICtl/xD+bh78/DIveWvnwJDQ8W0iJ96atNxtYoIN0GrA1JWOoX4Ler8Dq7MKu3iFUCiK/ngKw8Xy7EAnwDZ6UXOXjkGh/QZdL2dDHq+1/RXLoSc80aMV+LTbsKm3opHvU8buoqKdC1YFKliFUCiKdrr6w8B7/lAu7eUzi7j+PoPCIH8XtaHi9CXTyPZmUJpqqVDKq/wapZgUO9mKB2LqmlVSQ9bKG3fqNYJYA4O3bhMZ2QDHJxdGVj0x/E2rafXnW6QBaiKZ7LS2URpucrGFQtF8hyHJqlDGkXcLz0OnuVUno/loldvwO37He3MQebXB3W1n1yMe7BKBNnIeqiL9GVLHwDsaiWYdMsw6pehl1A7bXraK7birlhh1glgLi6T2Br24tZmyqr34BJvZn+xu1y2r+l9fF86m5+gbZ4PsbnazDXJ9OvXv9mNNUl45LSDck3x91bKFYJIF7TJWVQl6mYNGn6fl0WpoYMeupSaK9Yje7RcjmMqbSWZ9gNz9crxvotSq86TTHWpiiGmk2KUZWuOAyXlBG3Snnb8x3A2wp6m3b6rE+wtp+nq2oDL0oX01CaQndjAVZjbdGH5vyXPhj83Ppg8POKOX8Cx4yjZbQFLr4AAAAASUVORK5CYII="
        $dbatoolsicon = Convert-b64toimg "iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAAO9SURBVEhL3VVdTFNXHO9MzPTF+OzDeBixFdTMINIWsAUK3AIVkFvAIQVFRLYZKR8Wi1IEKV9DYB8PGFAyEx8QScySabYY5+I2JvK18iWISKGk0JGhLzA3+e2c29uHtpcvH/0lv9yennN+v3vO/3fOFb2fCAg4vXWPNOmMRJ745TtTSskqeElviGXJ0XtkWvjJkyGLPoFAVQZoe/NkX/n6Mh/ysu4Qy7WZdJAutxRW6zT6LcNQaE4LiGgREH4cibpCMNqzCIk9hbScEoSSZ0zKOa7fRxG/k5d1h8ukvO4a5ubmMT1jw5E0vZcBZWzqOTS3dcB8tRXZeRX4/v5DZH5uIu0Wrn8NEzaNDjgYoUPd120oMjViX2iql8H6ZFd8DzE7eFl3iOWpuyQydlh44kbJroilSd8RuQ+cqh7wC9Z+JJaxY8KTN0gp+5Yk9DaREzYhb5FOBwZFZ6LlZifKa5ux//AxYTHCvSEp8A9O5n77B6dwqXS119guZ+GrGq9jfn4eM7ZZxB/PdxN2UfOpHq3kRWq/uoE8Yx3u/fQLzhSYUdN0g+tfN126z0oxNj6BJz0Dq0b4E2UawuJzuPhKyZmKYr/AocgMrk37VzWRBLGRdE/psuXqk9wkT/GNUCJLWqS3By/rDh9FxjaSrnahiZ7cq8wCUzKImLIJqC+Ngbk4gmjjIKKKB6Aq7l+OLBmfVF0YnlQZR1p4eSd2y5IiyEr+oyJ0CwIi0gUNKAOPmnG04Q0utf+DHweWkFjjQOyVWajLpsCUPkeUcRgqAzE09Dfz8k64aqI9YcDziUk87bMgOCZL0CQ0ux2J9UtIbXyFwall/PD0NeLKrU6DkhGymj8RXtRDjU7x8k64TKpJQmi6bLOzSEgv8DYhNWMujiK+9jU0VQs4Vm/H2MwSOh4vcP+rii2cQVh+F+IqbRJe3glyReuoSFBUJtpu3eWulv2h3ueE1iOu0g5N9QL3jLk8jerbdrz59y1yGoYQUdSLsII/CLscIsD9UPrLUz4myXhBhWjCPMVdPBBnhMbsIAZzSDDbcOvRIhyLy6i4+Qyq82QFxECR9xjK/K5OXtodNHo+CsW2tagunbxADbK+sXP16Bv/G7lNQ8hpHEX21UGoDb/j8NmfoSzoNvCymwdTPvMotsKGB32LaL1H0mS0oOHOFLpH/0L3iAOF3/YSk4dgTBMh/JTNgdVbtzNl1il12UuSpHE+SRayTb0IL3yCMP2vUJKtUuh/szNNK8Jfxw3BZNpiMoGjiKPJm54Ffw8gEv0PQRYX7wDAUKEAAAAASUVORK5CYII="

        $sourceserver = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SourceSqlCredential
    }

    process {
        # Create XAML form in Visual Studio, ensuring the ListView looks chromeless
        [xml]$xaml = '<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Locate Folder" Height="620" Width="440" Background="#F0F0F0"
        WindowStartupLocation="CenterScreen">
    <Grid>
        <TreeView Name="treeview" Height="462" Width="391" Background="#FFFFFF" BorderBrush="#FFFFFF" Foreground="#FFFFFF" Margin="11,36,11,79"/>
        <Label x:Name="label" Content="Select the folder:" HorizontalAlignment="Left" Margin="15,4,0,0" VerticalAlignment="Top"/>
        <Label x:Name="path" Content="Selected Path" HorizontalAlignment="Left" Margin="15,502,0,0" VerticalAlignment="Top"/>
        <TextBox Name="textbox" HorizontalAlignment="Left" Height="Auto" Margin="111,504,0,0" TextWrapping="NoWrap" Text="C:\" VerticalAlignment="Top" Width="292"/>
        <Button Name="okbutton" Content="OK" HorizontalAlignment="Left" Margin="241,540,0,0" VerticalAlignment="Top" Width="75"/>
        <Button Name="cancelbutton" Content="Cancel" HorizontalAlignment="Left" Margin="328.766,540,0,0" VerticalAlignment="Top" Width="75"/>
    </Grid>
</Window>
'
        # Turn XAML into PowerShell objects
        $window = [Windows.Markup.XamlReader]::Load((New-Object System.Xml.XmlNodeReader $xaml))
        $window.icon = $dbatoolsicon

        $xaml.SelectNodes("//*[@Name]") | ForEach-Object { Set-Variable -Name ($_.Name) -Value $window.FindName($_.Name) -Scope Script }

        try {
            $drives = ($sourceserver.EnumAvailableMedia()).Name
        }
        catch {
            throw "No access to remote SQL Server files."
        }

        foreach ($drive in $drives) {
            $drive = $drive.Replace(":", "")
            Add-TreeItem -Name $drive -Parent $treeview -Tag $drive
        }

        $window.Add_SourceInitialized( {
                [System.Windows.RoutedEventHandler]$Event = {
                    if ($_.OriginalSource -is [System.Windows.Controls.TreeViewItem]) {
                        $treeviewItem = $_.OriginalSource
                        $treeviewItem.items.clear()
                        Get-SubDirectory -NameSpace $treeviewItem.Tag -TreeViewItem $treeviewItem
                    }
                }
                $treeview.AddHandler([System.Windows.Controls.TreeViewItem]::ExpandedEvent, $Event)
                $treeview.AddHandler([System.Windows.Controls.TreeViewItem]::SelectedEvent, $Event)
            })

        $okbutton.Add_Click( {
                $window.Close()
            })

        $cancelbutton.Add_Click( {
                $textbox.Text = $null
                $window.Close()
            })

        $null = $window.ShowDialog()
    }

    end {

        if ($textbox.Text.length -gt 0) {
            $drive = $textbox.Text + '\'
            return $drive
        }

        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Show-SqlServerFileSystem
    }
}
tools\dbatools\functions\Start-DbaAgentJob.ps1
function Start-DbaAgentJob {
    <#
        .SYNOPSIS
            Starts a running SQL Server Agent Job.

        .DESCRIPTION
            This command starts a job then returns connected SMO object for SQL Agent Job information for each instance(s) of SQL Server.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Job
            The job(s) to process - this list is auto-populated from the server. If unspecified, all jobs will be processed.

        .PARAMETER ExcludeJob
            The job(s) to exclude - this list is auto-populated from the server.

        .PARAMETER AllJobs
            Retrieve all the jobs

        .PARAMETER Wait
            Wait for output until the job has started

        .PARAMETER WaitPeriod
            Wait period in seconds to use when -Wait is used

        .PARAMETER SleepPeriod
            Period in milliseconds to wait after a job has started

        .PARAMETER InputObject
            Internal parameter that enables piping

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Job, Agent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Start-DbaAgentJob

        .EXAMPLE
            Start-DbaAgentJob -SqlInstance localhost

            Starts all running SQL Agent Jobs on the local SQL Server instance

        .EXAMPLE
            Get-DbaAgentJob -SqlInstance sql2016 -Job cdc.DBWithCDC_capture | Start-DbaAgentJob

            Starts the cdc.DBWithCDC_capture SQL Agent Job on sql2016

        .EXAMPLE
            Start-DbaAgentJob -SqlInstance sql2016 -Job cdc.DBWithCDC_capture

            Starts the cdc.DBWithCDC_capture SQL Agent Job on sql2016

        .EXAMPLE
            $servers | Find-DbaAgentJob -IsFailed | Start-DbaAgentJob

            Restarts all failed jobs on all servers in the $servers collection

        .EXAMPLE
            Start-DbaAgentJob -SqlInstance sql2016 -AllJobs

            Start all the jobs

    #>
    [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = "Default")]
    param (
        [parameter(Mandatory, ParameterSetName = "Instance")]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$Job,
        [string[]]$ExcludeJob,
        [parameter(Mandatory, ValueFromPipeline, ParameterSetName = "Object")]
        [Microsoft.SqlServer.Management.Smo.Agent.Job[]]$InputObject,
        [switch]$AllJobs,
        [switch]$Wait,
        [int]$WaitPeriod = 3,
        [int]$SleepPeriod = 300,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        if ((Test-Bound -not -ParameterName AllJobs) -and (Test-Bound -not -ParameterName Job) -and (Test-Bound -not -ParameterName InputObject)) {
            Stop-Function -Message "Please use one of the job parameters, either -Job or -AllJobs. Or pipe in a list of jobs." -Target $instance
            return
        }
        # Loop through each of the instances
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            
            # Check if all the jobs need to included
            if ($AllJobs) {
                $InputObject += $server.JobServer.Jobs
            }
            
            # If a specific job needs to be added
            if (-not $AllJobs -and $Job) {
                $InputObject = $server.JobServer.Jobs | Where-Object Name -In $Job
            }
            
            # If a job needs to be excluded
            if ($ExcludeJob) {
                $InputObject = $InputObject | Where-Object Name -NotIn $ExcludeJob
            }
        }
        
        # Loop through each of the jobs
        foreach ($currentjob in $InputObject) {
            $server = $currentjob.Parent.Parent
            $status = $currentjob.CurrentRunStatus
            
            if ($status -ne 'Idle') {
                Stop-Function -Message "$currentjob on $server is not idle ($status)" -Target $currentjob -Continue
            }
            
            If ($Pscmdlet.ShouldProcess($server, "Starting job $currentjob")) {
                # Start the job
                $lastrun = $currentjob.LastRunDate
                Write-Message -Level Verbose -Message "Last run date was $lastrun"
                $null = $currentjob.Start()
                
                # Wait and refresh so that it has a chance to change status
                Start-Sleep -Milliseconds $SleepPeriod
                $currentjob.Refresh()
                
                $i = 0
                # Check if the status is Idle
                while (($currentjob.CurrentRunStatus -eq 'Idle' -and $i++ -lt 60)) {
                    Write-Message -Level Verbose -Message "Job $($currentjob.Name) status is $($currentjob.CurrentRunStatus)"
                    Write-Message -Level Verbose -Message "Job $($currentjob.Name) last run date is $($currentjob.LastRunDate)"
                    
                    Write-Message -Level Verbose -Message "Sleeping for $SleepPeriod ms and refreshing"
                    Start-Sleep -Milliseconds $SleepPeriod
                    $currentjob.Refresh()
                    
                    # If it failed fast, speed up output
                    if ($lastrun -ne $currentjob.LastRunDate) {
                        $i = 600
                    }
                }
                
                # Wait for the job
                if (Test-Bound -ParameterName Wait) {
                    while ($currentjob.CurrentRunStatus -ne 'Idle') {
                        Write-Message -Level Output -Message "$currentjob is $($currentjob.CurrentRunStatus)"
                        Start-Sleep -Seconds $WaitPeriod
                        $currentjob.Refresh()
                    }
                    Get-DbaAgentJob -SqlInstance $server -Job $currentjob.Name
                }
                else {
                    Get-DbaAgentJob -SqlInstance $server -Job $currentjob.Name
                }
            }
        }
    }
}
tools\dbatools\functions\Start-DbaMigration.ps1
function Start-DbaMigration {
    <#
        .SYNOPSIS
            Migrates SQL Server *ALL* databases, logins, database mail profiles/accounts, credentials, SQL Agent objects, linked servers,
            Central Management Server objects, server configuration settings (sp_configure), user objects in systems databases,
            system triggers and backup devices from one SQL Server to another.

            For more granular control, please use one of the -No parameters and use the other functions available within the dbatools module.

        .DESCRIPTION
            Start-DbaMigration consolidates most of the migration tools in dbatools into one command.  This is useful when you're looking to migrate entire instances. It less flexible than using the underlying functions. Think of it as an easy button. It migrates:

            All user databases to exclude support databases such as ReportServerTempDB (Use -IncludeSupportDbs for this). Use -NoDatabases to skip.
            All logins. Use -NoLogins to skip.
            All database mail objects. Use -NoDatabaseMail
            All credentials. Use -NoCredentials to skip.
            All objects within the Job Server (SQL Agent). Use -NoAgentServer to skip.
            All linked servers. Use -NoLinkedServers to skip.
            All groups and servers within Central Management Server. Use -NoCentralManagementServer to skip.
            All SQL Server configuration objects (everything in sp_configure). Use -NoSpConfigure to skip.
            All user objects in system databases. Use -NoSysDbUserObjects to skip.
            All system triggers. Use -NoSystemTriggers to skip.
            All system backup devices. Use -NoBackupDevices to skip.
            All Audits. Use -NoAudits to skip.
            All Endpoints. Use -NoEndpoints to skip.
            All Extended Events. Use -NoExtendedEvents to skip.
            All Policy Management objects. Use -NoPolicyManagement to skip.
            All Resource Governor objects. Use -NoResourceGovernor to skip.
            All Server Audit Specifications. Use -NoServerAuditSpecifications to skip.
            All Custom Errors (User Defined Messages). Use -NoCustomErrors to skip.
            Copies All Data Collector collection sets. Does not configure the server. Use -NoDataCollector to skip.

            This script provides the ability to migrate databases using detach/copy/attach or backup/restore. SQL Server logins, including passwords, SID and database/server roles can also be migrated. In addition, job server objects can be migrated and server configuration settings can be exported or migrated. This script works with named instances, clusters and SQL Express.

            By default, databases will be migrated to the destination SQL Server's default data and log directories. You can override this by specifying -ReuseSourceFolderStructure. Filestreams and filegroups are also migrated. Safety is emphasized.

        .PARAMETER Source
            Source SQL Server.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You may specify multiple servers.

            Note that when using -BackupRestore with multiple servers, the backup will only be performed once and backups will be deleted at the end (if you didn't specify -NoBackupCleanup).

            When using -DetachAttach with multiple servers, -Reattach must be specified.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER BackupRestore
            If this switch is enabled, the Copy-Only backup and restore method is used to perform database migrations. You must specify -NetworkShare with a valid UNC format as well (\\server\share).

        .PARAMETER NetworkShare
            Specifies the network location for the backup files. The SQL Server service accounts on both Source and Destination must have read/write permission to access this location.

        .PARAMETER WithReplace
            If this switch is enabled, databases are restored from backup using WITH REPLACE. This is useful if you want to stage some complex file paths.

        .PARAMETER ReuseSourceFolderStructure
            If this switch is enabled, the data and log directory structures on Source will be kept on Destination. Otherwise, databases will be migrated to Destination's default data and log directories.

            Consider this if you're migrating between different versions and use part of Microsoft's default SQL structure (MSSQL12.INSTANCE, etc.).

        .PARAMETER DetachAttach
            If this switch is enabled, the the detach/copy/attach method is used to perform database migrations. No files are deleted on Source. If the destination attachment fails, the source database will be reattached. File copies are performed over administrative shares (\\server\x$\mssql) using BITS. If a database is being mirrored, the mirror will be broken prior to migration.

        .PARAMETER Reattach
            If this switch is enabled, all databases are reattached to Source after a DetachAttach migration is complete.

            .PARAMETER NoRecovery
            If this switch is enabled, databases will be left in the No Recovery state to enable further backups to be added.

        .PARAMETER IncludeSupportDbs
            If this switch is enabled, the ReportServer, ReportServerTempDb, SSIDb, and distribution databases will be migrated if they exist. A logfile named $SOURCE-$DESTINATION-$date-Sqls.csv will be written to the current directory. Requires -BackupRestore or -DetachAttach.

        .PARAMETER SetSourceReadOnly
            If this switch is enabled, all migrated databases will be set to ReadOnly on the source instance prior to detach/attach & backup/restore. If -Reattach is specified, the database is set to read-only after reattaching.

        .PARAMETER NoDatabases
            If this switch is enabled, databases will not be migrated.

        .PARAMETER NoLogins
            If this switch is enabled, Logins will not be migrated.

        .PARAMETER NoAgentServer
            If this switch is enabled, SQL Agent jobs will not be migrated.

        .PARAMETER NoCredentials
            If this switch is enabled, Credentials will not be migrated.

        .PARAMETER NoLinkedServers
            If this switch is enabled, Linked Servers will not be migrated.

        .PARAMETER NoSpConfigure
            If this switch is enabled, options configured via sp_configure will not be migrated.

        .PARAMETER NoCentralManagementServer
            If this switch is enabled, Central Management Server will not be migrated.

        .PARAMETER NoDatabaseMail
            If this switch is enabled, Database Mail will not be migrated.

        .PARAMETER NoSysDbUserObjects
            If this switch is enabled, user objects found in the master, msdb and model databases will not be migrated.

        .PARAMETER NoSystemTriggers
            If this switch is enabled, System Triggers will not be migrated.

        .PARAMETER NoBackupDevices
            If this switch is enabled, Backup Devices will not be migrated.

        .PARAMETER NoAudits
            If this switch is enabled, Audits will not be migrated.

        .PARAMETER NoEndpoints
            If this switch is enabled, Endpoints will not be migrated.

        .PARAMETER NoExtendedEvents
            If this switch is enabled, Extended Events will not be migrated.

        .PARAMETER NoPolicyManagement
            If this switch is enabled, Policy-Based Management will not be migrated.

        .PARAMETER NoResourceGovernor
            If this switch is enabled, Resource Governor will not be migrated.

        .PARAMETER NoServerAuditSpecifications
            If this switch is enabled, the Server Audit Specification will not be migrated.

        .PARAMETER NoCustomErrors
            If this switch is enabled, Custom Errors (User Defined Messages) will not be migrated.

        .PARAMETER NoDataCollector
            If this switch is enabled, the Data Collector will not be migrated.

        .PARAMETER NoSaRename
            If this switch is enabled, the sa account will not be renamed on the destination instance to match the source.

        .PARAMETER DisableJobsOnDestination
            If this switch is enabled, migrated SQL Agent jobs will be disabled on the destination instance.

        .PARAMETER DisableJobsOnSource
            If this switch is enabled, SQL Agent jobs will be disabled on the source instance.

        .PARAMETER UseLastBackups
            Use the last full, diff and logs instead of performing backups. Note that the backups must exist in a location accessible by all destination servers, such a network share.

        .PARAMETER Force
            If migrating users, forces drop and recreate of SQL and Windows logins.
            If migrating databases, deletes existing databases with matching names.
            If using -DetachAttach, -Force will break mirrors and drop dbs from Availability Groups.

            For other migration objects, it will just drop existing items and readd, if -force is supported within the underlying function.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration
            Author: Chrissy LeMaire
            Limitations:     Doesn't cover what it doesn't cover (certificates, etc)
                            SQL Server 2000 login migrations have some limitations (server perms aren't migrated)
                            SQL Server 2000 databases cannot be directly migrated to SQL Server 2012 and above.
                            Logins within SQL Server 2012 and above logins cannot be migrated to SQL Server 2008 R2 and below.
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Start-DbaMigration

        .EXAMPLE
            Start-DbaMigration -Source sqlserver\instance -Destination sqlcluster -DetachAttach

            All databases, logins, job objects and sp_configure options will be migrated from sqlserver\instance to sqlcluster. Databases will be migrated using the detach/copy files/attach method. Dbowner will be updated. User passwords, SIDs, database roles and server roles will be migrated along with the login.

        .EXAMPLE
            Start-DbaMigration -Verbose -Source sqlcluster -Destination sql2016 -SourceSqlCredential $scred -ReuseSourceFolderStructure -DestinationSqlCredential $cred -Force -NetworkShare \\fileserver\share\sqlbackups\Migration -BackupRestore

            Migrate databases uses backup/restore. Also migrate logins, database mail, credentials, SQL Agent, Central Management Server, SQL global configuration.

        .EXAMPLE
            Start-DbaMigration -Verbose -Source sqlcluster -Destination sql2016 -NoDatabases -NoLogins

            Migrates everything but logins and databases.

        .EXAMPLE
            Start-DbaMigration -Verbose -Source sqlcluster -Destination sql2016 -DetachAttach -Reattach -SetSourceReadonly

            Migrate databases using detach/copy/attach. Reattach at source and set source databases read-only. Also migrates everything else.

    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true)]
    Param (
        [parameter(Position = 1, Mandatory = $true)]
        [DbaInstanceParameter]$Source,
        [parameter(Position = 2, Mandatory = $true)]
        [DbaInstanceParameter[]]$Destination,
        [parameter(Position = 3, Mandatory = $true, ParameterSetName = "DbAttachDetach")]
        [switch]$DetachAttach,
        [parameter(Position = 4, ParameterSetName = "DbAttachDetach")]
        [switch]$Reattach,
        [parameter(Position = 5, Mandatory = $true, ParameterSetName = "DbBackup")]
        [switch]$BackupRestore,
        [parameter(Position = 6, ParameterSetName = "DbBackup",
                   HelpMessage = "Specify a valid network share in the format \\server\share that can be accessed by your account and both Sql Server service accounts.")]
        [string]$NetworkShare,
        [parameter(Position = 7, ParameterSetName = "DbBackup")]
        [switch]$WithReplace,
        [parameter(Position = 8, ParameterSetName = "DbBackup")]
        [switch]$NoRecovery,
        [parameter(Position = 9, ParameterSetName = "DbBackup")]
        [parameter(Position = 10, ParameterSetName = "DbAttachDetach")]
        [switch]$SetSourceReadOnly,
        [Alias("ReuseFolderStructure")]
        [parameter(Position = 11, ParameterSetName = "DbBackup")]
        [parameter(Position = 12, ParameterSetName = "DbAttachDetach")]
        [switch]$ReuseSourceFolderStructure,
        [parameter(Position = 13, ParameterSetName = "DbBackup")]
        [parameter(Position = 14, ParameterSetName = "DbAttachDetach")]
        [switch]$IncludeSupportDbs,
        [parameter(Position = 15)]
        [PSCredential]$SourceSqlCredential,
        [parameter(Position = 16)]
        [PSCredential]$DestinationSqlCredential,
        [Alias("SkipDatabases")]
        [switch]$NoDatabases,
        [switch]$NoLogins,
        [Alias("SkipJobServer", "NoJobServer")]
        [switch]$NoAgentServer,
        [Alias("SkipCredentials")]
        [switch]$NoCredentials,
        [Alias("SkipLinkedServers")]
        [switch]$NoLinkedServers,
        [Alias("SkipSpConfigure")]
        [switch]$NoSpConfigure,
        [Alias("SkipCentralManagementServer")]
        [switch]$NoCentralManagementServer,
        [Alias("SkipDatabaseMail")]
        [switch]$NoDatabaseMail,
        [Alias("SkipSysDbUserObjects")]
        [switch]$NoSysDbUserObjects,
        [Alias("SkipSystemTriggers")]
        [switch]$NoSystemTriggers,
        [Alias("SkipBackupDevices")]
        [switch]$NoBackupDevices,
        [switch]$NoAudits,
        [switch]$NoEndpoints,
        [switch]$NoExtendedEvents,
        [switch]$NoPolicyManagement,
        [switch]$NoResourceGovernor,
        [switch]$NoServerAuditSpecifications,
        [switch]$NoCustomErrors,
        [switch]$NoDataCollector,
        [switch]$DisableJobsOnDestination,
        [switch]$DisableJobsOnSource,
        [switch]$NoSaRename,
        [switch]$UseLastBackups,
        [switch]$Force,
        [switch]$EnableException
    )

    begin {
        $elapsed = [System.Diagnostics.Stopwatch]::StartNew()
        $started = Get-Date
        $sourceserver = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential

        if ($BackupRestore -eq $false -and $DetachAttach -eq $false -and $NoDatabases -eq $false) {
            Stop-Function -Message "You must specify a database migration method (-BackupRestore or -DetachAttach) or -NoDatabases"
            return
        }
        if (-not $NoDatabases) {
            if (-not $DetachAttach -and !$BackupRestore) {
                Stop-Function -Message "You must specify a migration method using -BackupRestore or -DetachAttach."
                return
            }
        }
        if ($BackupRestore -and (-not $NetworkShare -and -not $UseLastBackups)) {
            Stop-Function -Message "When using -BackupRestore, you must specify -NetworkShare or -UseLastBackups"
            return
        }
        if ($NetworkShare -and $UseLastBackups) {
            Stop-Function -Message "-NetworkShare cannot be used with -UseLastBackups because the backup path is determined by the paths in the last backups"
            return
        }
        if ($DetachAttach -and -not $Reattach -and $Destination.Count -gt 1) {
            Stop-Function -Message "When using -DetachAttach with multiple servers, you must specify -Reattach to reattach database at source"
            return
        }
    }

    process {
        if (Test-FunctionInterrupt) { return }
        
        # testing twice for whatif reasons
        if ($BackupRestore -and (-not $NetworkShare -and -not $UseLastBackups)) {
            Stop-Function -Message "When using -BackupRestore, you must specify -NetworkShare or -UseLastBackups"
            return
        }
        if ($NetworkShare -and $UseLastBackups) {
            Stop-Function -Message "-NetworkShare cannot be used with -UseLastBackups because the backup path is determined by the paths in the last backups"
            return
        }
        if ($DetachAttach -and -not $Reattach -and $Destination.Count -gt 1) {
            Stop-Function -Message "When using -DetachAttach with multiple servers, you must specify -Reattach to reattach database at source"
            return
        }
        if (-not $NoSpConfigure) {
            Write-Message -Level Verbose -Message "Migrating SQL Server Configuration"
            Copy-DbaSpConfigure -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential
        }

        if (-not $NoCustomErrors) {
            Write-Message -Level Verbose -Message "Migrating custom errors (user defined messages)"
            Copy-DbaCustomError -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
        }

        if (-not $NoCredentials) {
            Write-Message -Level Verbose -Message "Migrating SQL credentials"
            Copy-DbaCredential -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
        }

        if (-not $NoDatabaseMail) {
            Write-Message -Level Verbose -Message "Migrating database mail"
            Copy-DbaDatabaseMail -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
        }

        if (-not $NoCentralManagementServer) {
            Write-Message -Level Verbose -Message "Migrating Central Management Server"
            Copy-DbaCentralManagementServer -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
        }

        if (-not $NoBackupDevices) {
            Write-Message -Level Verbose -Message "Migrating Backup Devices"
            Copy-DbaBackupDevice -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
        }

        if (-not $NoLinkedServers) {
            Write-Message -Level Verbose -Message "Migrating linked servers"
            Copy-DbaLinkedServer -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
        }

        if (-not $NoSystemTriggers) {
            Write-Message -Level Verbose -Message "Migrating System Triggers"
            Copy-DbaServerTrigger -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
        }

        if (-not $NoDatabases) {
            # Do it
            Write-Message -Level Verbose -Message "Migrating databases"
            if ($BackupRestore) {
                if ($UseLastBackups) {
                    Copy-DbaDatabase -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -AllDatabases -SetSourceReadOnly:$SetSourceReadOnly -ReuseSourceFolderStructure:$ReuseSourceFolderStructure -BackupRestore -Force:$Force -NoRecovery:$NoRecovery -WithReplace:$WithReplace -IncludeSupportDbs:$IncludeSupportDbs -UseLastBackups:$UseLastBackups
                }
                else {
                    Copy-DbaDatabase -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -AllDatabases -SetSourceReadOnly:$SetSourceReadOnly -ReuseSourceFolderStructure:$ReuseSourceFolderStructure -BackupRestore -NetworkShare $NetworkShare -Force:$Force -NoRecovery:$NoRecovery -WithReplace:$WithReplace -IncludeSupportDbs:$IncludeSupportDbs
                }
            }
            else {
                Copy-DbaDatabase -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -AllDatabases -SetSourceReadOnly:$SetSourceReadOnly -ReuseSourceFolderStructure:$ReuseSourceFolderStructure -DetachAttach:$DetachAttach -Reattach:$Reattach -Force:$Force -IncludeSupportDbs:$IncludeSupportDbs
            }
        }

        if (-not $NoLogins) {
            Write-Message -Level Verbose -Message "Migrating logins"
            $syncit = $NoSaRename -eq $false
            Copy-DbaLogin -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force -SyncSaName:$syncit
        }

        if (-not $NoLogins -and -not $NoDatabases -and -not $NoRecovery) {
            Write-Message -Level Verbose -Message "Updating database owners to match newly migrated logins"
            foreach ($dest in $Destination) {
                $null = Update-SqlDbOwner -Source $sourceserver -Destination $dest -DestinationSqlCredential $DestinationSqlCredential
            }
        }

        if (-not $NoDataCollector) {
            Write-Message -Level Verbose -Message "Migrating Data Collector collection sets"
            Copy-DbaSqlDataCollector -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
        }

        if (-not $NoAudits) {
            Write-Message -Level Verbose -Message "Migrating Audits"
            Copy-DbaServerAudit -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
        }

        if (-not $NoServerAuditSpecifications) {
            Write-Message -Level Verbose -Message "Migrating Server Audit Specifications"
            Copy-DbaServerAuditSpecification -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
        }

        if (-not $NoEndpoints) {
            Write-Message -Level Verbose -Message "Migrating Endpoints"
            Copy-DbaEndpoint -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
        }

        if (-not $NoPolicyManagement) {
            Write-Message -Level Verbose -Message "Migrating Policy Management"
            Copy-DbaSqlPolicyManagement -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
        }

        if (-not $NoResourceGovernor) {
            Write-Message -Level Verbose -Message "Migrating Resource Governor"
            Copy-DbaResourceGovernor -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
        }
        
        if (-not $NoSysDbUserObjects) {
            Write-Message -Level Verbose -Message "Migrating user objects in system databases (this can take a second)."
            If ($Pscmdlet.ShouldProcess($destination, "Copying user objects.")) {
                Copy-DbaSysDbUserObject -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$force
            }
        }
        
        if (-not $NoExtendedEvents) {
            Write-Message -Level Verbose -Message "Migrating Extended Events"
            Copy-DbaExtendedEvent -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
        }

        if (-not $NoAgentServer) {
            Write-Message -Level Verbose -Message "Migrating job server"
            Copy-DbaSqlServerAgent -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -DisableJobsOnDestination:$DisableJobsOnDestination -DisableJobsOnSource:$DisableJobsOnSource -Force:$Force
        }
    }

    end {
        if (Test-FunctionInterrupt) { return }
        $totaltime = ($elapsed.Elapsed.toString().Split(".")[0])
        Write-Message -Level Verbose -Message "SQL Server migration complete."
        Write-Message -Level Verbose -Message "Migration started: $started"
        Write-Message -Level Verbose -Message "Migration completed: $(Get-Date)"
        Write-Message -Level Verbose -Message "Total Elapsed time: $totaltime"
    }
}
tools\dbatools\functions\Start-DbaPfDataCollectorSet.ps1
function Start-DbaPfDataCollectorSet {
    <#
        .SYNOPSIS
            Starts Performance Monitor Data Collector Set.

        .DESCRIPTION
            Starts Performance Monitor Data Collector Set.

        .PARAMETER ComputerName
            The target computer. Defaults to localhost.

        .PARAMETER Credential
            Allows you to login to $ComputerName using alternative credentials. To use:

            $cred = Get-Credential, then pass $cred object to the -Credential parameter.

        .PARAMETER CollectorSet
            The name of the Collector Set to start.
    
        .PARAMETER NoWait
            If this switch is enabled, the collector is started and the results are returned immediately.
    
        .PARAMETER InputObject
            Accepts the object output by Get-DbaPfDataCollectorSet via the pipeline.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
    
        .NOTES
            Tags: PerfMon
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
    
        .LINK
            https://dbatools.io/Start-DbaPfDataCollectorSet

        .EXAMPLE
            Start-DbaPfDataCollectorSet
    
            Attempts to start all ready Collectors on localhost.

        .EXAMPLE
            Start-DbaPfDataCollectorSet -ComputerName sql2017
    
            Attempts to start all ready Collectors on localhost.
    
        .EXAMPLE
            Start-DbaPfDataCollectorSet -ComputerName sql2017, sql2016 -Credential (Get-Credential) -CollectorSet 'System Correlation'
    
            Starts the 'System Correlation' Collector on sql2017 and sql2016 using alternative credentials.
    
        .EXAMPLE
            Get-DbaPfDataCollectorSet -CollectorSet 'System Correlation' | Start-DbaPfDataCollectorSet
    
            Starts the 'System Correlation' Collector.
    #>
    [CmdletBinding()]
    param (
        [DbaInstance[]]$ComputerName=$env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias("DataCollectorSet")]
        [string[]]$CollectorSet,
        [parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [switch]$NoWait,
        [switch]$EnableException
    )
    begin {
        $wait = $NoWait -eq $false
        
        $setscript = {
            $setname = $args[0]; $wait = $args[1]
            $collectorset = New-Object -ComObject Pla.DataCollectorSet
            $collectorset.Query($setname, $null)
            $null = $collectorset.Start($wait)
        }
    }
    process {
        if (-not $InputObject -or ($InputObject -and (Test-Bound -ParameterName ComputerName))) {
            foreach ($computer in $ComputerName) {
                $InputObject += Get-DbaPfDataCollectorSet -ComputerName $computer -Credential $Credential -CollectorSet $CollectorSet
            }
        }
        
        if ($InputObject) {
            if (-not $InputObject.DataCollectorSetObject) {
                Stop-Function -Message "InputObject is not of the right type. Please use Get-DbaPfDataCollectorSet."
                return
            }
        }
        
        # Check to see if its running first
        foreach ($set in $InputObject) {
            $setname = $set.Name
            $computer = $set.ComputerName
            $status = $set.State
            Write-Message -Level Verbose -Message "$setname on $ComputerName is $status."
            if ($status -eq "Running") {
                Stop-Function -Message "$setname on $computer is already running." -Continue
            }
            if ($status -eq "Disabled") {
                Stop-Function -Message "$setname on $computer is disabled." -Continue
            }
            Write-Message -Level Verbose -Message "Connecting to $computer using Invoke-Command."
            try {
                Invoke-Command2 -ComputerName $computer -Credential $Credential -ScriptBlock $setscript -ArgumentList $setname, $wait -ErrorAction Stop
            }
            catch {
                Stop-Function -Message "Failure starting $setname on $computer." -ErrorRecord $_ -Target $computer -Continue
            }
            
            Get-DbaPfDataCollectorSet -ComputerName $computer -Credential $Credential -CollectorSet $setname
        }
    }
}
tools\dbatools\functions\Start-DbaPowerBi.ps1
function Start-DbaPowerBi {
    <#
        .SYNOPSIS
            Launches the PowerBi dashboard for dbatools

        .DESCRIPTION
            Launches the PowerBi dashboard for dbatools

        .PARAMETER Path
            The location of the pbix file. "$script:ModuleRoot\bin\pbix\dbatools.pbix" by default.

        .PARAMETER InputObject
            Enables piping.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .EXAMPLE
            Start-DbaPowerBi

            Launches PowerBi from "$script:ModuleRoot\bin\pbix\dbatools.pbix" using "C:\windows\Temp\dbatools\" (generated by Update-DbaPowerBiDataSource) as the datasource.

        .EXAMPLE
            Start-DbaPowerBi -Path \\nas\projects\dbatools.pbix

            Launches \\nas\projects\dbatools.pbix

    #>
    [CmdletBinding()]
    param (
        [string]$Path = "$script:ModuleRoot\bin\pbix\dbatools.pbix",
        [parameter(ValueFromPipeline)]
        [pscustomobject]$InputObject,
        [switch]$EnableException
    )

    process {
        if (-not (Test-Path -Path $Path)) {
            Stop-Function -Message "$Path does not exist"
            return
        }

        $association = Get-ItemProperty "Registry::HKEY_Classes_root\.pbix" -ErrorAction SilentlyContinue

        if (-not $association) {
            Stop-Function -Message ".pbix not associated with any program. Please (re)install Power BI"
            return
        }

        if ($Path -match "Program Files") {
            $newpath = "$script:localapp\dbatools.pbix"
            #if ((Test-Path -Path $newpath)) { # Would be nice if we could tell if it needed to be replaced or not
            #I suppose we could use dbatools versioning and wintemp?
            Copy-Item -Path $Path -Destination $newpath -Force -ErrorAction SilentlyContinue
            $Path = $newpath
        }

        try {
            Invoke-Item -Path $path
        }
        catch {
            Stop-Function -Message "Failure" -ErrorRecord $_
            return
        }
    }
}
tools\dbatools\functions\Start-DbaSqlService.ps1
function Start-DbaSqlService {
    <#
        .SYNOPSIS
            Starts SQL Server services on a computer.

        .DESCRIPTION
            Starts the SQL Server related services on one or more computers. Will follow SQL Server service dependencies.

            Requires Local Admin rights on destination computer(s).

        .PARAMETER ComputerName
            The SQL Server (or server in general) that you're connecting to. This command handles named instances.

        .PARAMETER InstanceName
            Only affects services that belong to the specific instances.

        .PARAMETER Credential
            Credential object used to connect to the computer as a different user.

        .PARAMETER Type
            Use -Type to collect only services of the desired SqlServiceType.
            Can be one of the following: "Agent","Browser","Engine","FullText","SSAS","SSIS","SSRS"

        .PARAMETER Timeout
            How long to wait for the start/stop request completion before moving on. Specify 0 to wait indefinitely.

        .PARAMETER InputObject
            A collection of services from Get-DbaSqlService

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER WhatIf
            Shows what would happen if the cmdlet runs. The cmdlet is not run.

        .PARAMETER Confirm
            Prompts you for confirmation before running the cmdlet.

        .NOTES
            Tags: Service, SqlServer, Instance, Connect
            Author: Kirill Kravtsov( @nvarscar )

            Requires Local Admin rights on destination computer(s).

            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2017 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Start-DbaSqlService

        .EXAMPLE
            Start-DbaSqlService -ComputerName sqlserver2014a

            Starts the SQL Server related services on computer sqlserver2014a.

        .EXAMPLE
            'sql1','sql2','sql3'| Get-DbaSqlService | Start-DbaSqlService

            Gets the SQL Server related services on computers sql1, sql2 and sql3 and starts them.

        .EXAMPLE
            Start-DbaSqlService -ComputerName sql1,sql2 -Instance MSSQLSERVER

            Starts the SQL Server services related to the default instance MSSQLSERVER on computers sql1 and sql2.

        .EXAMPLE
            Start-DbaSqlService -ComputerName $MyServers -Type SSRS

            Starts the SQL Server related services of type "SSRS" (Reporting Services) on computers in the variable MyServers.
    #>
    [CmdletBinding(DefaultParameterSetName = "Server", SupportsShouldProcess = $true)]
    Param (
        [Parameter(ParameterSetName = "Server", Position = 1)]
        [Alias("cn", "host", "Server")]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [Alias("Instance")]
        [string[]]$InstanceName,
        [ValidateSet("Agent", "Browser", "Engine", "FullText", "SSAS", "SSIS", "SSRS")]
        [string[]]$Type,
        [parameter(ValueFromPipeline = $true, Mandatory = $true, ParameterSetName = "Service")]
        [Alias("ServiceCollection")]
        [object[]]$InputObject,
        [int]$Timeout = 30,
        [PSCredential]$Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        $processArray = @()
        if ($PsCmdlet.ParameterSetName -eq "Server") {
            $serviceParams = @{ ComputerName = $ComputerName }
            if ($InstanceName) { $serviceParams.InstanceName = $InstanceName }
            if ($Type) { $serviceParams.Type = $Type }
            if ($Credential) { $serviceParams.Credential = $Credential }
            if ($EnableException) { $serviceParams.Silent = $EnableException }
            $InputObject = Get-DbaSqlService @serviceParams
        }
    }
    process {
        #Get all the objects from the pipeline before proceeding
        $processArray += $InputObject
    }
    end {
        $processArray = $processArray | Where-Object { (!$InstanceName -or $_.InstanceName -in $InstanceName) -and (!$Type -or $_.ServiceType -in $Type) }
        if ($processArray) {
            Update-ServiceStatus -InputObject $processArray -Action 'start' -Timeout $Timeout -EnableException $EnableException
        }
        else { Stop-Function -EnableException $EnableException -Message "No SQL Server services found with current parameters." -Category ObjectNotFound }
    }
}
tools\dbatools\functions\Start-DbaTrace.ps1
function Start-DbaTrace {
     <#
        .SYNOPSIS
        Starts SQL Server traces

        .DESCRIPTION
        Starts SQL Server traces

        .PARAMETER SqlInstance
        The target SQL Server instance

        .PARAMETER SqlCredential
        Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Id
        A list of trace ids

        .PARAMETER InputObject
        Internal parameter for piping

        .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
        Tags: Security, Trace
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

       .EXAMPLE
        Start-DbaTrace -SqlInstance sql2008

        Starts all traces on sql2008

        .EXAMPLE
        Start-DbaTrace -SqlInstance sql2008 -Id 1

        Starts all trace with ID 1 on sql2008

        .EXAMPLE
        Get-DbaTrace -SqlInstance sql2008 | Out-GridView -PassThru | Start-DbaTrace

        Starts selected traces on sql2008

#>
    [CmdletBinding()]
    Param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [int[]]$Id,
        [parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [switch]$EnableException
    )
    process {
        if (-not $InputObject -and $SqlInstance) {
            $InputObject = Get-DbaTrace -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Id $Id
        }

        foreach ($trace in $InputObject) {
            if (-not $trace.id -and -not $trace.Parent) {
                Stop-Function -Message "Input is of the wrong type. Use Get-DbaTrace." -Continue
                return
            }

            $server = $trace.Parent
            $traceid = $trace.id
            $default = Get-DbaTrace -SqlInstance $server -Default

            if ($default.id -eq $traceid) {
                Stop-Function -Message "The default trace on $server cannot be started. Use Set-DbaSpConfigure to turn it on." -Continue
            }

            $sql = "sp_trace_setstatus $traceid, 1"

            try {
                $server.Query($sql)
                Get-DbaTrace -SqlInstance $server -Id $traceid
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_ -Target $server -Continue
                return
            }
        }
    }
}
tools\dbatools\functions\Start-DbaXESession.ps1
function Start-DbaXESession {
    <#
        .SYNOPSIS
            Starts Extended Events sessions.

        .DESCRIPTION
            This script starts Extended Events sessions on a SQL Server instance.

        .PARAMETER SqlInstance
            Target SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Session
            Only start specific Extended Events sessions.

        .PARAMETER AllSessions
            Start all Extended Events sessions on an instance, ignoring the packaged sessions: AlwaysOn_health, system_health, telemetry_xevents.

        .PARAMETER InputObject
            Internal parameter to support piping from Get-DbaXESession

        .PARAMETER StopAt
            Specifies a datetime at which the session will be stopped. This is done via a self-deleting schedule.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Author: Doug Meyers
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Start-DbaXESession

        .EXAMPLE
            Start-DbaXESession -SqlInstance sqlserver2012 -AllSessions

            Starts all Extended Event Session on the sqlserver2014 instance.

        .EXAMPLE
            Start-DbaXESession -SqlInstance sqlserver2012 -Session xesession1,xesession2

            Starts the xesession1 and xesession2 Extended Event sessions.

        .EXAMPLE
            Start-DbaXESession -SqlInstance sqlserver2012 -Session xesession1,xesession2 -StopAt (Get-Date).AddMinutes(30)

            Starts the xesession1 and xesession2 Extended Event sessions and stops them in 30 minutes.

        .EXAMPLE
            Get-DbaXESession -SqlInstance sqlserver2012 -Session xesession1 | Start-DbaXESession

            Starts the sessions returned from the Get-DbaXESession function.

    #>
    [CmdletBinding(DefaultParameterSetName = 'Session')]
    param (
        [parameter(Position = 1, Mandatory, ParameterSetName = 'Session')]
        [parameter(Position = 1, Mandatory, ParameterSetName = 'All')]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [parameter(ParameterSetName = 'Session')]
        [parameter(ParameterSetName = 'All')]
        [PSCredential]$SqlCredential,
        [parameter(Mandatory, ParameterSetName = 'Session')]
        [Alias("Sessions")]
        [object[]]$Session,
        [datetime]$StopAt,
        [parameter(Mandatory, ParameterSetName = 'All')]
        [switch]$AllSessions,
        [parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Object')]
        [Microsoft.SqlServer.Management.XEvent.Session[]]$InputObject,
        [switch]$EnableException
    )

    begin {
        # Start each XESession
        function Start-XESessions {
            [CmdletBinding()]
            param ([Microsoft.SqlServer.Management.XEvent.Session[]]$xeSessions)

            foreach ($xe in $xeSessions) {
                $instance = $xe.Parent.Name
                $session = $xe.Name
                if (-Not $xe.isRunning) {
                    Write-Message -Level Verbose -Message "Starting XEvent Session $session on $instance."
                    try {
                        $xe.Start()
                    }
                    catch {
                        Stop-Function -Message "Could not start XEvent Session on $instance." -Target $session -ErrorRecord $_ -Continue
                    }
                }
                else {
                    Write-Message -Level Warning -Message "$session on $instance is already running."
                }
                Get-DbaXESession -SqlInstance $xe.Parent -Session $session
            }
        }

        function New-StopJob {
            [CmdletBinding()]
            param (
                [Microsoft.SqlServer.Management.XEvent.Session[]]$xeSessions,
                [datetime]$StopAt
            )

            foreach ($xe in $xeSessions) {
                $server = $xe.Parent
                $session = $xe.Name
                $name = "XE Session Stop - $session"

                # Setup the schedule time
                $time = ($StopAt).ToString("HHmmss")

                # Create the schedule
                $schedule = New-DbaAgentSchedule -SqlInstance $server -Schedule $name -FrequencyType Once -StartTime ($StopAt).ToString("HHmmss") -Force

                # Create the job and attach the schedule
                $job = New-DbaAgentJob -SqlInstance $server -Job $name -Schedule $schedule -DeleteLevel Always -Force

                # Create the job step
                $sql = "ALTER EVENT SESSION [$session] ON SERVER STATE = stop;"
                $jobstep = New-DbaAgentJobStep -SqlInstance $server -Job $job -StepName 'T-SQL Stop' -Subsystem TransactSql -Command $sql -Force
            }
        }
    }
    process {
        if ($InputObject) {
            Start-XESessions $InputObject
        }
        else {
            foreach ($instance in $SqlInstance) {
                $xeSessions = Get-DbaXESession -SqlInstance $instance -SqlCredential $SqlCredential

                # Filter xeSessions based on parameters
                if ($Session) {
                    $xeSessions = $xeSessions | Where-Object { $_.Name -in $Session }
                }
                elseif ($AllSessions) {
                    $systemSessions = @('AlwaysOn_health', 'system_health', 'telemetry_xevents')
                    $xeSessions = $xeSessions | Where-Object { $_.Name -notin $systemSessions }
                }

                Start-XESessions $xeSessions

                if ($StopAt) {
                    New-StopJob -xeSessions $xeSessions -StopAt $stopat
                }
            }
        }
    }
}
tools\dbatools\functions\Start-DbaXESmartTarget.ps1
function Start-DbaXESmartTarget {
    <#
        .SYNOPSIS
            XESmartTarget runs as a client application for an Extended Events session running on a SQL Server instance.

        .DESCRIPTION
            XESmartTarget offers the ability to set up complex actions in response to Extended Events captured in sessions, without writing a single line of code.

            See more at https://github.com/spaghettidba/XESmartTarget/wiki

        .PARAMETER SqlInstance
            Target SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Session
            Name of the Extended Events session to attach to.

            You can monitor a single session with an instance of XESmartTarget. In case you need to perform action on multiple sessions, run an additional instance of XESmartTarget, with its own configuration file.

        .PARAMETER Database
            Specifies the name of the database that contains the target table.

        .PARAMETER FailOnProcessingError
            If this switch is enabled, the a processing error will trigger a failure.

        .PARAMETER Responder
            The list of responses can include zero or more Response objects, each to be configured by specifying values for their public members.

        .PARAMETER Template
            Path to the dbatools built-in templates

        .PARAMETER NotAsJob
            If this switch is enabled, output will be sent to screen indefinitely. BY default, a job will be run in the background.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
            SmartTarget: by Gianluca Sartori (@spaghettidba)

        .LINK
            https://dbatools.io/Start-DbaXESmartTarget
            https://github.com/spaghettidba/XESmartTarget/wiki

        .EXAMPLE
            $response = New-DbaXESmartQueryExec -SqlInstance sql2017 -Database dbadb -Query "update table set whatever = 1"
            Start-DbaXESmartTarget -SqlInstance sql2017 -Session deadlock_tracker -Responder $response

            Executes a T-SQL command against dbadb on sql2017 whenever a deadlock event is recorded.

        .EXAMPLE
            $response = New-DbaXESmartQueryExec -SqlInstance sql2017 -Database dbadb -Query "update table set whatever = 1"
            $params = @{
                SmtpServer = "smtp.ad.local"
                To = "[email protected]"
                Sender = "[email protected]"
                Subject = "Query executed"
                Body = "Query executed at {collection_time}"
                Attachment = "batch_text"
                AttachmentFileName = "query.sql"
            }
            $emailresponse = New-DbaXESmartEmail @params
            Start-DbaXESmartTarget -SqlInstance sql2017 -Session querytracker -Responder $response, $emailresponse

            Executes a T-SQL command against dbadb on sql2017 and sends an email whenever a querytracker event is recorded.

        .EXAMPLE
            $columns = "cpu_time", "duration", "physical_reads", "logical_reads", "writes", "row_count", "batch_text"
            $response = New-DbaXESmartTableWriter -SqlInstance sql2017 -Database dbadb -Table deadlocktracker -OutputColumns $columns -Filter "duration > 10000"
            Start-DbaXESmartTarget -SqlInstance sql2017 -Session deadlock_tracker -Responder $response

            Writes Extended Events to the deadlocktracker table in dbadb on sql2017.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string]$Database,
        [parameter(Mandatory)]
        [string]$Session,
        [switch]$FailOnProcessingError,
        [object[]]$Responder,
        [string[]]$Template,
        [switch]$NotAsJob,
        [switch]$EnableException
    )
    begin {
        function Start-SmartFunction {
            [CmdletBinding()]
            param (
                [parameter(Mandatory, ValueFromPipeline)]
                [Alias("ServerInstance", "SqlServer")]
                [DbaInstanceParameter[]]$SqlInstance,
                [PSCredential]$SqlCredential,
                [string]$Database,
                [parameter(Mandatory)]
                [string]$Session,
                [switch]$FailOnProcessingError,
                [object[]]$Responder,
                [string[]]$Template,
                [switch]$NotAsJob,
                [switch]$EnableException
            )
            begin {
                try {
                    Add-Type -Path "$script:PSModuleRoot\bin\XESmartTarget\XESmartTarget.Core.dll" -ErrorAction Stop
                }
                catch {
                    Stop-Function -Message "Could not load XESmartTarget.Core.dll" -ErrorRecord $_ -Target "XESmartTarget"
                    return
                }
            }
            process {
                if (Test-FunctionInterrupt) { return }

                foreach ($instance in $SqlInstance) {
                    try {
                        Write-Message -Level Verbose -Message "Connecting to $instance."
                        $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 11
                    }
                    catch {
                        Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                    }

                    $target = New-Object -TypeName XESmartTarget.Core.Target
                    $target.ServerName = $instance
                    $target.SessionName = $Session
                    $target.FailOnProcessingError = $FailOnProcessingError

                    if ($SqlCredential) {
                        $target.UserName = $SqlCredential.UserName
                        $target.Password = $SqlCredential.GetNetworkCredential().Password
                    }

                    foreach ($response in $Responder) {
                        $target.Responses.Add($response)
                    }

                    try {
                        $target.Start()
                    }
                    catch {
                        $message = $_.Exception.InnerException.InnerException | Out-String

                        if ($message) {
                            Stop-Function -Message $message -Target "XESmartTarget" -Continue
                        }
                        else {
                            Stop-Function -Message "Failure" -Target "XESmartTarget" -ErrorRecord $_ -Continue
                        }
                    }
                }
            }
        }
    }
    process {
        foreach ($instance in $SqlInstance) {
            if (-not ($xesession = Get-DbaXESession -SqlInstance $instance -SqlCredential $SqlCredential -Session $Session)) {
                Stop-Function -Message "Session $Session does not exist on $instance."
                return
            }
            if ($xesession.Status -ne "Running") {
                Stop-Function -Message "Session $Session on $instance is not running."
                return
            }
        }

        if ($NotAsJob) {
            Start-SmartFunction @PSBoundParameters
        }
        else {
            $date = (Get-Date -UFormat "%H%M%S") #"%m%d%Y%H%M%S"
            Start-Job -Name "XESmartTarget-$session-$date" -ArgumentList $PSBoundParameters, $script:PSModuleRoot -ScriptBlock {
                param (
                    $Parameters,
                    $ModulePath
                )
                Import-Module "$ModulePath\dbatools.psd1"
                Add-Type -Path "$ModulePath\bin\XESmartTarget\XESmartTarget.Core.dll" -ErrorAction Stop
                $params = @{
                    SqlInstance    = $Parameters.SqlInstance.InputObject
                    Database       = $Parameters.Database
                    Session        = $Parameters.Session
                    Responder      = @()
                }
                if ($Parameters.SqlCredential) {
                    $params["SqlCredential"] = $Parameters.SqlCredential
                }
                foreach ($responder in $Parameters.Responder) {
                    $typename = $responder.PSObject.TypeNames[0] -replace "^Deserialized\.", ""
                    $newResponder = New-Object -TypeName $typename
                    foreach ($property in $responder.PSObject.Properties) {
                        if ($property.Value) {
                            $name = $property.Name
                            $newResponder.$name = $property.Value
                        }
                    }
                    $params["Responder"] += $newResponder
                }

                Start-DbaXESmartTarget @params -NotAsJob -FailOnProcessingError
            } | Select-Object -Property ID, Name, State
        }
    }
}
tools\dbatools\functions\Stop-DbaAgentJob.ps1
function Stop-DbaAgentJob {
    <#
        .SYNOPSIS
            Stops a running SQL Server Agent Job.

        .DESCRIPTION
            This command stops a job then returns connected SMO object for SQL Agent Job information for each instance(s) of SQL Server.

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Job
            The job(s) to process - this list is auto-populated from the server. If unspecified, all jobs will be processed.

        .PARAMETER ExcludeJob
            The job(s) to exclude - this list is auto-populated from the server.

        .PARAMETER Wait
            Wait for output until the job has completely stopped

        .PARAMETER InputObject
            Internal parameter that enables piping

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Job, Agent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Stop-DbaAgentJob

        .EXAMPLE
            Stop-DbaAgentJob -SqlInstance localhost

            Stops all running SQL Agent Jobs on the local SQL Server instance

        .EXAMPLE
            Get-DbaAgentJob -SqlInstance sql2016 -Job cdc.DBWithCDC_capture | Stop-DbaAgentJob

            Stops the cdc.DBWithCDC_capture SQL Agent Job on sql2016

        .EXAMPLE
            Stop-DbaAgentJob -SqlInstance sql2016 -Job cdc.DBWithCDC_capture

            Stops the cdc.DBWithCDC_capture SQL Agent Job on sql2016

    #>
    [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = "Default")]
    param (
        [parameter(Mandatory, ParameterSetName = "Instance")]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$Job,
        [string[]]$ExcludeJob,
        [parameter(Mandatory, ValueFromPipeline, ParameterSetName = "Object")]
        [Microsoft.SqlServer.Management.Smo.Agent.Job[]]$InputObject,
        [switch]$Wait,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Verbose "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $InputObject += $server.JobServer.Jobs

            if ($Job) {
                $InputObject = $InputObject | Where-Object Name -In $Job
            }
            if ($ExcludeJob) {
                $InputObject = $InputObject | Where-Object Name -NotIn $ExcludeJob
            }
        }

        foreach ($currentjob in $InputObject) {

            $server = $currentjob.Parent.Parent
            $status = $currentjob.CurrentRunStatus

            if ($status -eq 'Idle') {
                Stop-Function -Message "$currentjob on $server is idle ($status)" -Target $currentjob -Continue
            }

            If ($Pscmdlet.ShouldProcess($server, "Stopping job $currentjob")) {
                $null = $currentjob.Stop()
                Start-Sleep -Milliseconds 300
                $currentjob.Refresh()

                $waits = 0
                while ($currentjob.CurrentRunStatus -ne 'Idle' -and $waits++ -lt 10) {
                    Start-Sleep -Milliseconds 100
                    $currentjob.Refresh()
                }

                if ($wait) {
                    while ($currentjob.CurrentRunStatus -ne 'Idle') {
                        Write-Message -Level Output -Message "$currentjob is $($currentjob.CurrentRunStatus)"
                        Start-Sleep -Seconds 3
                        $currentjob.Refresh()
                    }
                    $currentjob
                }
                else {
                    $currentjob
                }
            }
        }
    }
}
tools\dbatools\functions\Stop-DbaPfDataCollectorSet.ps1
function Stop-DbaPfDataCollectorSet {
    <#
        .SYNOPSIS
            Stops Performance Monitor Data Collector Set.

        .DESCRIPTION
            Stops Performance Monitor Data Collector Set.

        .PARAMETER ComputerName
            The target computer. Defaults to localhost.

        .PARAMETER Credential
            Allows you to login to $ComputerName using alternative credentials. To use:

            $cred = Get-Credential, then pass $cred object to the -Credential parameter.

        .PARAMETER CollectorSet
            The name of the Collector Set to stop.
    
        .PARAMETER NoWait
            If this switch is enabled, the collector is stopped and the results are returned immediately.
    
        .PARAMETER InputObject
            Accepts the object output by Get-DbaPfDataCollectorSet via the pipeline.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
    
        .NOTES
            Tags: PerfMon
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
    
        .LINK
            https://dbatools.io/Stop-DbaPfDataCollectorSet

        .EXAMPLE
            Stop-DbaPfDataCollectorSet
    
            Attempts to stop all ready Collectors on localhost.

        .EXAMPLE
            Stop-DbaPfDataCollectorSet -ComputerName sql2017
    
            Attempts to stop all ready Collectors on localhost.
    
        .EXAMPLE
            Stop-DbaPfDataCollectorSet -ComputerName sql2017, sql2016 -Credential (Get-Credential) -CollectorSet 'System Correlation'
    
            Stops the 'System Correlation' Collector on sql2017 and sql2016 using alternative credentials.
    
        .EXAMPLE
            Get-DbaPfDataCollectorSet -CollectorSet 'System Correlation' | Stop-DbaPfDataCollectorSet
    
            Stops the 'System Correlation' Collector.
    #>
    [CmdletBinding()]
    param (
        [DbaInstance[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Alias("DataCollectorSet")]
        [string[]]$CollectorSet,
        [parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [switch]$NoWait,
        [switch]$EnableException
    )
    begin {
        $sets = @()
        $wait = $NoWait -eq $false
        
        $setscript = {
            $setname = $args[0]; $wait = $args[1]
            $collectorset = New-Object -ComObject Pla.DataCollectorSet
            $collectorset.Query($setname, $null)
            $null = $collectorset.Stop($wait)
        }
    }
    process {
        if (-not $InputObject -or ($InputObject -and (Test-Bound -ParameterName ComputerName))) {
            foreach ($computer in $ComputerName) {
                $InputObject += Get-DbaPfDataCollectorSet -ComputerName $computer -Credential $Credential -CollectorSet $CollectorSet
            }
        }
        
        if ($InputObject) {
            if (-not $InputObject.DataCollectorSetObject) {
                Stop-Function -Message "InputObject is not of the right type. Please use Get-DbaPfDataCollectorSet."
                return
            }
        }
        
        # Check to see if its running first
        foreach ($set in $InputObject) {
            $setname = $set.Name
            $computer = $set.ComputerName
            $status = $set.State
            
            Write-Message -Level Verbose -Message "$setname on $ComputerName is $status."
            if ($status -ne "Running") {
                Stop-Function -Message "$setname on $computer is already stopped." -Continue
            }
            Write-Message -Level Verbose -Message "Connecting to $computer using Invoke-Command."
            try {
                Invoke-Command2 -ComputerName $computer -Credential $Credential -ScriptBlock $setscript -ArgumentList $setname, $wait -ErrorAction Stop
            }
            catch {
                Stop-Function -Message "Failure stopping $setname on $computer." -ErrorRecord $_ -Target $computer -Continue
            }
            
            Get-DbaPfDataCollectorSet -ComputerName $computer -Credential $Credential -CollectorSet $setname
        }
    }
}
tools\dbatools\functions\Stop-DbaProcess.ps1
function Stop-DbaProcess {
    <#
        .SYNOPSIS
            This command finds and kills SQL Server processes.

        .DESCRIPTION
            This command kills all spids associated with a spid, login, host, program or database.

            If you are attempting to kill your own login sessions, the process performing the kills will be skipped.

        .PARAMETER SqlInstance
            The SQL Server instance.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Spid
            Specifies one or more spids to be killed. Options for this parameter are auto-populated from the server.

        .PARAMETER Login
            Specifies one or more login names whose processes will be killed. Options for this parameter are auto-populated from the server and only login names that have active processes are offered.

        .PARAMETER Hostname
            Specifies one or more client hostnames whose processes will be killed. Options for this parameter are auto-populated from the server and only hostnames that have active processes are offered.

        .PARAMETER Program
            Specifies one or more client programs whose processes will be killed. Options for this parameter are auto-populated from the server and only programs that have active processes are offered.

        .PARAMETER Database
            Specifies one or more databases whose processes will be killed. Options for this parameter are auto-populated from the server and only databases that have active processes are offered.

            This parameter is auto-populated from -SqlInstance and allows only database names that have active processes. You can specify one or more Databases whose processes will be killed.

        .PARAMETER ExcludeSpid
            Specifies one or more spids which will not be killed. Options for this parameter are auto-populated from the server.

            Exclude is the last filter to run, so even if a spid matches (for example) Hosts, if it's listed in Exclude it wil be excluded.

        .PARAMETER InputObject
            This is the process object passed by Get-DbaProcess if using a pipeline.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Processes
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Stop-DbaProcess

        .EXAMPLE
            Stop-DbaProcess -SqlInstance sqlserver2014a -Login base\ctrlb, sa

            Finds all processes for base\ctrlb and sa on sqlserver2014a, then kills them. Uses Windows Authentication to login to sqlserver2014a.

        .EXAMPLE
            Stop-DbaProcess -SqlInstance sqlserver2014a -SqlCredential $credential -Spids 56, 77

            Finds processes for spid 56 and 57, then kills them. Uses alternative (SQL or Windows) credentials to login to sqlserver2014a.

        .EXAMPLE
            Stop-DbaProcess -SqlInstance sqlserver2014a -Programs 'Microsoft SQL Server Management Studio'

            Finds processes that were created in Microsoft SQL Server Management Studio, then kills them.

        .EXAMPLE
            Stop-DbaProcess -SqlInstance sqlserver2014a -Hosts workstationx, server100

            Finds processes that were initiated by hosts (computers/clients) workstationx and server 1000, then kills them.

        .EXAMPLE
            Stop-DbaProcess -SqlInstance sqlserver2014  -Database tempdb -WhatIf

            Shows what would happen if the command were executed.

        .EXAMPLE
            Get-DbaProcess -SqlInstance sql2016 -Programs 'dbatools PowerShell module - dbatools.io' | Stop-DbaProcess

            Finds processes that were created with dbatools, then kills them.

    #>
    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess)]
    Param (
        [parameter(Mandatory, ParameterSetName = "Server")]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [int[]]$Spid,
        [int[]]$ExcludeSpid,
        [string[]]$Database,
        [string[]]$Login,
        [string[]]$Hostname,
        [string[]]$Program,
        [parameter(ValueFromPipeline = $true, Mandatory = $true, ParameterSetName = "Process")]
        [object[]]$InputObject,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        if (Test-FunctionInterrupt) { return }

        if (!$InputObject) {
            $InputObject = Get-DbaProcess @PSBoundParameters
        }

        foreach ($session in $InputObject) {
            $sourceserver = $session.Parent

            if (!$sourceserver) {
                Stop-Function -Message "Only process objects can be passed through the pipeline." -Category InvalidData -Target $session
                return
            }

            $currentspid = $session.spid

            if ($sourceserver.ConnectionContext.ProcessID -eq $currentspid) {
                Write-Message -Level Warning -Message "Skipping spid $currentspid because you cannot use KILL to kill your own process." -Target $session
                Continue
            }

            if ($Pscmdlet.ShouldProcess($sourceserver, "Killing spid $currentspid")) {
                try {
                    $sourceserver.KillProcess($currentspid)
                    [pscustomobject]@{
                        SqlInstance = $sourceserver.name
                        Spid        = $session.Spid
                        Login       = $session.Login
                        Host        = $session.Host
                        Database    = $session.Database
                        Program     = $session.Program
                        Status      = 'Killed'
                    }
                }
                catch {
                    Stop-Function -Message "Couldn't kill spid $currentspid." -Target $session -ErrorRecord $_ -Continue
                }
            }
        }
    }
}
tools\dbatools\functions\Stop-DbaSqlService.ps1
function Stop-DbaSqlService {
    <#
        .SYNOPSIS
            Stops SQL Server services on a computer.

        .DESCRIPTION
            Stops the SQL Server related services on one or more computers. Will follow SQL Server service dependencies.

            Requires Local Admin rights on destination computer(s).

        .PARAMETER ComputerName
            The SQL Server (or server in general) that you're connecting to. This command handles named instances.

        .PARAMETER InstanceName
            Only affects services that belong to the specific instances.

        .PARAMETER Credential
            Credential object used to connect to the computer as a different user.

        .PARAMETER Type
            Use -Type to collect only services of the desired SqlServiceType.
            Can be one of the following: "Agent","Browser","Engine","FullText","SSAS","SSIS","SSRS"

        .PARAMETER Timeout
            How long to wait for the start/stop request completion before moving on. Specify 0 to wait indefinitely.

        .PARAMETER InputObject
            A collection of services from Get-DbaSqlService

        .PARAMETER Force
            Use this switch to stop dependent services before proceeding with the specified service

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER WhatIf
            Shows what would happen if the cmdlet runs. The cmdlet is not run.

        .PARAMETER Confirm
            Prompts you for confirmation before running the cmdlet.

        .PARAMETER Force
            Will stop dependent SQL Server agents when stopping Engine services.

        .NOTES
            Tags: Service, SqlServer, Instance, Connect
            Author: Kirill Kravtsov( @nvarscar )

            Requires Local Admin rights on destination computer(s).

            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2017 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Stop-DbaSqlService

        .EXAMPLE
            Stop-DbaSqlService -ComputerName sqlserver2014a

            Stops the SQL Server related services on computer sqlserver2014a.

        .EXAMPLE
            'sql1','sql2','sql3'| Get-DbaSqlService | Stop-DbaSqlService

            Gets the SQL Server related services on computers sql1, sql2 and sql3 and stops them.

        .EXAMPLE
            Stop-DbaSqlService -ComputerName sql1,sql2 -Instance MSSQLSERVER

            Stops the SQL Server services related to the default instance MSSQLSERVER on computers sql1 and sql2.

        .EXAMPLE
            Stop-DbaSqlService -ComputerName $MyServers -Type SSRS

            Stops the SQL Server related services of type "SSRS" (Reporting Services) on computers in the variable MyServers.

        .EXAMPLE
            Stop-DbaSqlService -ComputerName sql1 -Type Engine -Force

            Stops SQL Server database engine services on sql1 forcing dependent SQL Server Agent services to stop as well.
    #>
    [CmdletBinding(DefaultParameterSetName = "Server", SupportsShouldProcess = $true)]
    Param (
        [Parameter(ParameterSetName = "Server", Position = 1)]
        [Alias("cn", "host", "Server")]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [Alias("Instance")]
        [string[]]$InstanceName,
        [ValidateSet("Agent", "Browser", "Engine", "FullText", "SSAS", "SSIS", "SSRS")]
        [string[]]$Type,
        [parameter(ValueFromPipeline = $true, Mandatory = $true, ParameterSetName = "Service")]
        [Alias("ServiceCollection")]
        [object[]]$InputObject,
        [int]$Timeout = 30,
        [PSCredential]$Credential,
        [switch]$Force,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        $processArray = @()
        if ($PsCmdlet.ParameterSetName -eq "Server") {
            $serviceParams = @{ ComputerName = $ComputerName }
            if ($InstanceName) { $serviceParams.InstanceName = $InstanceName }
            if ($Type) { $serviceParams.Type = $Type }
            if ($Credential) { $serviceParams.Credential = $Credential }
            if ($EnableException) { $serviceParams.Silent = $EnableException }
            $InputObject = Get-DbaSqlService @serviceParams
        }
    }
    process {
        #Get all the objects from the pipeline before proceeding
        $processArray += $InputObject
    }
    end {
        $processArray = [array]($processArray | Where-Object { (!$InstanceName -or $_.InstanceName -in $InstanceName) -and (!$Type -or $_.ServiceType -in $Type) })
        foreach ($service in $processArray) {
            if ($Force -and $service.ServiceType -eq 'Engine' -and !($processArray | Where-Object { $_.ServiceType -eq 'Agent' -and $_.InstanceName -eq $service.InstanceName -and $_.ComputerName -eq $service.ComputerName })) {
                #Construct parameters to call Get-DbaSqlService
                $serviceParams = @{
                    ComputerName = $service.ComputerName
                    InstanceName = $service.InstanceName
                    Type         = 'Agent'
                }
                if ($Credential) { $serviceParams.Credential = $Credential }
                if ($EnableException) { $serviceParams.Silent = $EnableException }
                $processArray += @(Get-DbaSqlService @serviceParams)
            }
        }
        if ($processArray) {
            Update-ServiceStatus -InputObject $processArray -Action 'stop' -Timeout $Timeout -EnableException $EnableException
        }
        else { Stop-Function -EnableException $EnableException -Message "No SQL Server services found with current parameters." -Category ObjectNotFound }
    }
}
tools\dbatools\functions\Stop-DbaTrace.ps1
function Stop-DbaTrace {
     <#
        .SYNOPSIS
        Stops SQL Server traces

        .DESCRIPTION
        Stops SQL Server traces

        .PARAMETER SqlInstance
        The target SQL Server instance

        .PARAMETER SqlCredential
        Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Id
        A list of trace ids

        .PARAMETER InputObject
        Internal parameter for piping

        .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
        Tags: Security, Trace
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

       .EXAMPLE
        Stop-DbaTrace -SqlInstance sql2008

        Stops all traces on sql2008

        .EXAMPLE
        Stop-DbaTrace -SqlInstance sql2008 -Id 1

        Stops all trace with ID 1 on sql2008

        .EXAMPLE
        Get-DbaTrace -SqlInstance sql2008 | Out-GridView -PassThru | Stop-DbaTrace

        Stops selected traces on sql2008

#>
    [CmdletBinding()]
    Param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [int[]]$Id,
        [parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [switch]$EnableException
    )
    process {
        if (-not $InputObject -and $SqlInstance) {
            $InputObject = Get-DbaTrace -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Id $Id
        }

        foreach ($trace in $InputObject) {
            if (-not $trace.id -and -not $trace.Parent) {
                Stop-Function -Message "Input is of the wrong type. Use Get-DbaTrace." -Continue
                return
            }

            $server = $trace.Parent
            $traceid = $trace.id
            $default = Get-DbaTrace -SqlInstance $server -Default

            if ($default.id -eq $traceid) {
                Stop-Function -Message "The default trace on $server cannot be stopped. Use Set-DbaSpConfigure to turn it off." -Continue
            }

            $sql = "sp_trace_setstatus $traceid, 0"

            try {
                $server.Query($sql)
                $output = Get-DbaTrace -SqlInstance $server -Id $traceid
                if (-not $output) {
                    $output = [PSCustomObject]@{
                        ComputerName            = $server.ComputerName
                        InstanceName            = $server.ServiceName
                        SqlInstance             = $server.DomainInstanceName
                        Id                      = $traceid
                        Status                  = $null
                        IsRunning               = $false
                        Path                    = $null
                        MaxSize                 = $null
                        StopTime                = $null
                        MaxFiles                = $null
                        IsRowset                = $null
                        IsRollover              = $null
                        IsShutdown              = $null
                        IsDefault               = $null
                        BufferCount             = $null
                        BufferSize              = $null
                        FilePosition            = $null
                        ReaderSpid              = $null
                        StartTime               = $null
                        LastEventTime           = $null
                        EventCount              = $null
                        DroppedEventCount       = $null
                        Parent                  = $server
                    } | Select-DefaultView -Property 'ComputerName', 'InstanceName', 'SqlInstance', 'Id', 'IsRunning'
                }
                $output
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_ -Target $server -Continue
                return
            }
        }
    }
}
tools\dbatools\functions\Stop-DbaXESession.ps1
function Stop-DbaXESession {
    <#
        .SYNOPSIS
            Stops Extended Events sessions.

        .DESCRIPTION
            This script stops Extended Events sessions on a SQL Server instance.

        .PARAMETER SqlInstance
            Target SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Session
            Specifies individual Extended Events sessions to stop.

        .PARAMETER AllSessions
            If this switch is enabled, all Extended Events sessions will be stopped except the packaged sessions AlwaysOn_health, system_health, telemetry_xevents.

        .PARAMETER InputObject
            Accepts the object output by Get-DbaXESession as the list of sessions to be stopped.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Author: Doug Meyers
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Stop-DbaXESession

        .EXAMPLE
            Stop-DbaXESession -SqlInstance sqlserver2012 -AllSessions

            Stops all Extended Event Session on the sqlserver2014 instance.

        .EXAMPLE
            Stop-DbaXESession -SqlInstance sqlserver2012 -Session xesession1,xesession2

            Stops the xesession1 and xesession2 Extended Event sessions.

        .EXAMPLE
            Get-DbaXESession -SqlInstance sqlserver2012 -Session xesession1 | Stop-DbaXESession

            Stops the sessions returned from the Get-DbaXESession function.
    #>
    [CmdletBinding(DefaultParameterSetName = 'Session')]
    param (
        [parameter(Position = 1, Mandatory, ParameterSetName = 'Session')]
        [parameter(Position = 1, Mandatory, ParameterSetName = 'All')]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,

        [parameter(ParameterSetName = 'Session')]
        [parameter(ParameterSetName = 'All')]
        [PSCredential]$SqlCredential,

        [parameter(Mandatory, ParameterSetName = 'Session')]
        [Alias("Sessions")]
        [object[]]$Session,

        [parameter(Mandatory, ParameterSetName = 'All')]
        [switch]$AllSessions,

        [parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Object')]
        [Microsoft.SqlServer.Management.XEvent.Session[]]$InputObject,
        [switch]$EnableException
    )

    begin {
        # Stop each XESession
        function Stop-XESessions {
            [CmdletBinding()]
            param ([Microsoft.SqlServer.Management.XEvent.Session[]]$xeSessions)

            foreach ($xe in $xeSessions) {
                $instance = $xe.Parent.Name
                $session = $xe.Name
                if ($xe.isRunning) {
                    Write-Message -Level Verbose -Message "Stopping XEvent Session $session on $instance."
                    try {
                        $xe.Stop()
                    }
                    catch {
                        Stop-Function -Message "Could not stop XEvent Session on $instance" -Target $session -ErrorRecord $_ -Continue
                    }
                }
                else {
                    Write-Message -Level Warning -Message "$session on $instance is already stopped"
                }
                Get-DbaXESession -SqlInstance $xe.Parent -Session $session
            }
        }
    }

    process {
        if ($InputObject) {
            Stop-XESessions $InputObject
        }
        else {
            foreach ($instance in $SqlInstance) {
                $xeSessions = Get-DbaXESession -SqlInstance $instance -SqlCredential $SqlCredential

                # Filter xesessions based on parameters
                if ($Session) {
                    $xeSessions = $xeSessions | Where-Object { $_.Name -in $Session }
                }
                elseif ($AllSessions) {
                    $systemSessions = @('AlwaysOn_health', 'system_health', 'telemetry_xevents')
                    $xeSessions = $xeSessions | Where-Object { $_.Name -notin $systemSessions }
                }

                Stop-XESessions $xeSessions
            }
        }
    }
}
tools\dbatools\functions\Stop-DbaXESmartTarget.ps1
function Stop-DbaXESmartTarget {
    <#
        .SYNOPSIS
            Stops an XESmartTarget PowerShell Job. Useful if you want to run a target, but not right now.

        .DESCRIPTION
            Stops an XESmartTarget PowerShell Job. Useful if you want to run a target, but not right now.

        .PARAMETER InputObject
            The XESmartTarget job object.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
            SmartTarget: by Gianluca Sartori (@spaghettidba)

        .LINK
            https://dbatools.io/Stop-DbaXESmartTarget
            https://github.com/spaghettidba/XESmartTarget/wiki

        .EXAMPLE
            Get-DbaXESmartTarget | Stop-DbaXESmartTarget

            Stops all XESmartTarget jobs.

        .EXAMPLE
            Get-DbaXESmartTarget | Where-Object Id -eq 2 | Stop-DbaXESmartTarget

            Stops a specific XESmartTarget job.
    #>
    [CmdletBinding(SupportsShouldProcess)]
    param (
        [parameter(Mandatory, ValueFromPipeline)]
        [object[]]$InputObject,
        [switch]$EnableException
    )
    process {
        if ($Pscmdlet.ShouldProcess("localhost", "Stopping job $id")) {
            try {
                $id = $InputObject.Id
                Write-Message -Level Output -Message "Stopping job $id, this may take a couple minutes."
                Get-Job -ID $InputObject.Id | Stop-Job
                Write-Message -Level Output -Message "Successfully Stopped $id. If you need to remove the job for good, use Remove-DbaXESmartTarget."
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_
            }
        }
    }
}
tools\dbatools\functions\Sync-DbaLoginPermission.ps1
function Sync-DbaLoginPermission {
    <#
        .SYNOPSIS
            Copies SQL login permissions from one server to another.

        .DESCRIPTION
            Syncs only SQL Server login permissions, roles, etc. Does not add or drop logins. If a matching login does not exist on the destination, the login will be skipped. Credential removal is not currently supported for this operation.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Login
            The login(s) to process. Options for this list are auto-populated from the server. If unspecified, all logins will be processed.

        .PARAMETER ExcludeLogin
            The login(s) to exclude. Options for this list are auto-populated from the server.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration, Login
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on SQL Servers
            Limitations: Does not support Application Roles yet

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Sync-DbaLoginPermission

        .EXAMPLE
            Sync-DbaLoginPermission -Source sqlserver2014a -Destination sqlcluster

            Syncs only SQL Server login permissions, roles, etc. Does not add or drop logins or users. To copy logins and their permissions, use Copy-SqlLogin.

        .EXAMPLE
            Sync-DbaLoginPermission -Source sqlserver2014a -Destination sqlcluster -Exclude realcajun -SourceSqlCredential $scred -DestinationSqlCredential $dcred

            Copies all login permissions except for realcajun using SQL Authentication to connect to each server. If a login already exists on the destination, the permissions will not be migrated.

        .EXAMPLE
            Sync-DbaLoginPermission -Source sqlserver2014a -Destination sqlcluster -Login realcajun, netnerds

            Copies permissions ONLY for logins netnerds and realcajun.
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [DbaInstanceParameter]$Source,
        [PSCredential]
        $SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstanceParameter]$Destination,
        [PSCredential]
        $DestinationSqlCredential,
        [object[]]$Login,
        [object[]]$ExcludeLogin,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        function Sync-Only {
            [CmdletBinding()]
            param (
                [Parameter(Mandatory = $true)]
                [ValidateNotNullOrEmpty()]
                [object]$sourceServer,
                [object]$destServer,
                [array]$Logins,
                [array]$Exclude
            )

            try {
                $sa = ($destServer.Logins | Where-Object { $_.id -eq 1 }).Name
            }
            catch {
                $sa = "sa"
            }

            foreach ($sourceLogin in $sourceServer.Logins) {

                $username = $sourceLogin.Name
                $currentLogin = $sourceServer.ConnectionContext.TrueLogin

                if (!$Login -and $currentLogin -eq $username) {
                    Write-Message -Level Warning -Message "Sync does not modify the permissions of the current user. Skipping."
                    continue
                }

                if ($null -ne $Logins -and $Logins -notcontains $username) {
                    continue
                }

                if ($Exclude -contains $username -or $username.StartsWith("##") -or $username -eq $sa) {
                    continue
                }

                $serverName = Resolve-NetBiosName $sourceServer
                $userBase = ($username.Split("\")[0]).ToLower()
                if ($serverName -eq $userBase -or $username.StartsWith("NT ")) {
                    continue
                }
                if ($null -eq ($destLogin = $destServer.Logins.Item($username))) {
                    continue
                }

                Update-SqlPermissions -SourceServer $sourceServer -SourceLogin $sourceLogin -DestServer $destServer -DestLogin $destLogin
            }
        }
    }
    process {

        if ($source -eq $destination) {
            Stop-Function -Message "Source and Destination SQL Servers are the same. Quitting."
            return
        }

        Write-Message -Level Verbose -Message "Connecting to SQL Servers."
        $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 8
        $destServer = Connect-SqlInstance -SqlInstance $Destination -SqlCredential $DestinationSqlCredential -MinimumVersion 8

        $source = $sourceServer.DomainInstanceName
        $destination = $destServer.DomainInstanceName

        if (!$Login) {
            $login = $sourceServer.Logins.Name
        }

        Sync-Only -SourceServer $sourceServer -DestServer $destServer -Logins $login -Exclude $ExcludeLogin
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Sync-SqlLoginPermissions
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Sync-SqlLoginPermission
    }
}
tools\dbatools\functions\Test-DbaBackupInformation.ps1
function Test-DbaBackupInformation {
    <#
        .SYNOPSIS
            Tests a dbatools backup history object is correct for restoring

        .DESCRIPTION
            Normally takes in a backup history object from Format-DbaBackupInformation

            This is then parse to check that it's valid for restore. Tests performed include:
                Checking unbroken LSN chain
                if the target database exists and WithReplace has been provided
                if any files already exist, but owned by other databases
                Creates any new folders required
                That the backup files exists at the location specified, and can be seen by the Sql Instance

            if no errors are found then the objects for that database will me marked as Verified.

        .PARAMETER BackupHistory
            dbatools BackupHistory object. Normally this will have been process with Select- and then Format-DbaBackupInformation

        .PARAMETER SqlInstance
            The Sql Server instance that wil be performing the restore

        .PARAMETER SqlCredential
            A Sql Credential to connect to $SqlInstance

        .PARAMETER WithReplace
            By default we won't overwrite an existing database, this switch tells us you want to

        .PARAMETER Continue
            Switch to indicate a continuing restore

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER VerifyOnly
            This switch indicates that you only wish to verify a restore, so runs a smaller number of tests as you won't be writing anything to the restore server

        .PARAMETER WhatIf
            Shows what would happen if the cmdlet runs. The cmdlet is not run.

        .PARAMETER Confirm
            Prompts you for confirmation before running the cmdlet.

        .NOTES
            Tags: Backup, Restore, DisasterRecovery
            Author: Stuart Moore (@napalmgram stuart-moore.com )

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaBackupInformation

        .EXAMPLE
            $BackupHistory | Test-DbaBackupInformation -SqlInstance MyInstance

            $PassedDbs = $BackupHistory | Where-Object {$_.IsVerified -eq $True}
            $FailedDbs = $BackupHistory | Where-Object {$_.IsVerified -ne $True}

            Pass in a BackupHistory object to be tested against MyInstance.

            Those records that pass are marked as verified. We can then use the IsVerified property to divide the failures and successes
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [object[]]$BackupHistory,
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [switch]$WithReplace,
        [switch]$Continue,
        [switch]$VerifyOnly,
        [switch]$EnableException
    )

    begin {
        try {
            $RestoreInstance = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            return
        }
        $InternalHistory = @()
    }
    process {
        foreach ($bh in $BackupHistory) {
            $InternalHistory += $bh
        }
    }
    end {
        $RegisteredFileCheck = Get-DbaDbPhysicalFile -SqlInstance $RestoreInstance

        $Databases = $InternalHistory.Database | Select-Object -Unique
        foreach ($Database in $Databases) {
            $VerificationErrors = 0
            Write-Message -Message "Testing restore for $Database" -Level Verbose
            #Test we're only restoring backups from one database, or hilarity will ensure
            $DbHistory = $InternalHistory | Where-Object {$_.Database -eq $Database}
            if (( $DbHistory | Select-Object -Property OriginalDatabase -Unique ).Count -gt 1) {
                Write-Message -Message "Trying to restore $Database from multiple sources databases" -Level Warning
                $VerificationErrors++
            }
            #Test Db Existance on destination
            $DbCheck = Get-DbaDatabase -SqlInstance $RestoreInstance -Database $Database
            # Only do file and db tests if we're not verifing
            Write-Message -Level Verbose -Message "VerifyOnly = $VerifyOnly"
            If ($VerifyOnly -ne $true) {
                if ($null -ne $DbCheck -and ($WithReplace -ne $true -and $Continue -ne $true)) {
                    Stop-Function -Message "Database $Database exists, so WithReplace must be specified" -Target $database
                    $VerificationErrors++
                }

                $DBFileCheck = ($RegisteredFileCheck | Where-Object Name -eq $Database).PhysicalName
                $OtherFileCheck = ($RegisteredFileCheck | Where-Object Name -ne $Database).PhysicalName
                $DBHistoryPhysicalPaths = ($DbHistory | Select-Object -ExpandProperty filelist | Select-Object PhysicalName -Unique).PhysicalName
                $DBHistoryPhysicalPathsTest = Test-DbaSqlPath -SqlInstance $RestoreInstance -Path $DBHistoryPhysicalPaths
                $DBHistoryPhysicalPathsExists = ($DBHistoryPhysicalPathsTest | Where-Object FileExists -eq $True).FilePath
                foreach ($path in $DBHistoryPhysicalPaths) {
                    if (($DBHistoryPhysicalPathsTest | Where-Object FilePath -eq $path).FileExists) {
                        if ($path -in $DBFileCheck) {
                            #If the Files are owned by the db we're restoring check for Continue or WithReplace. If not, then report error otherwise just carry on
                            if  ($WithReplace -ne $True -and $Continue -ne $True) {
                                Write-Message -Message "File $path already exists on $SqlInstance and WithReplace not specified, cannot restore" -Level Warning
                                $VerificationErrors++
                            }
                        }
                        elseif ($path -in $OtherFileCheck) {
                            Write-Message -Message "File $path already exists on $SqlInstance and owned by another database, cannot restore" -Level Warning
                            $VerificationErrors++
                        }
                        elseif ($path -in $DBHistoryPhysicalPathsExists) {
                                Write-Message -Message "File $path already exists on $($SqlInstance.ComputerName), not owned by any database in $SqlInstance, will not overwrite." -Level Warning
                                $VerificationErrors++
                        }
                    }
                    else {
                        $ParentPath = Split-Path $path -Parent
                        if (!(Test-DbaSqlPath -SqlInstance $RestoreInstance -Path $ParentPath) ) {
                            $ConfirmMessage = "`n Creating Folder $ParentPath on $SqlInstance `n"
                            if ($Pscmdlet.ShouldProcess("$Path on $SqlInstance `n `n", $ConfirmMessage)) {
                                if (New-DbaSqlDirectory -SqlInstance $RestoreInstance -Path $ParentPath) {
                                    Write-Message -Message "Created Folder $ParentPath on $SqlInstance" -Level Verbose
                                }
                                else {
                                    Write-Message -Message "Failed to create $ParentPath on $SqlInstance" -Level Warning
                                    $VerificationErrors++
                                }
                            }
                        }
                    }
                }
                #Easier to do FileStream checks out of the loop:
                if ('s' -in ($DbHistory | Select-Object -ExpandProperty filelist | Select-Object FileType -Unique).FileType) {
                    if ((Get-DbaSpConfigure -SqlInstance $RestoreInstance -ConfigName FilestreamAccessLevel).RunningValue -eq 0) {
                        Write-Message -Level Warning -Message "Database $Database contains FileStream data, and FileStream is not enable on the destination server"
                        $VerificationErrors++
                    }

                    $ExistingFS = Get-DbaFileStreamFolder -SqlInstance $SqlInstance
                    foreach ($FileStreamFolder in ($DbHistory | Select-Object -ExpandProperty filelist | Where-Object {$_.FileType -eq 's'} | Select-Object PhysicalName -unique).PhysicalName) {
                        if ($null -ne $ExistingFS) {
                            if ($null -ne ($ExistingFs | Where-Object {$_.Database -eq $Database}) -and $Withreplace -ne $True) {
                                Write-Message -Level Warning -Message "Folder $FileStreamFolder already in use for Filestream data on $SqlInstance and WithReplace not specified, cannot restore"
                                $VerificationErrors++
                            }
                            $OtherOwners = $ExistingFs | Where-Object {$_.FileStreamFolder -eq $FileStreamFolder -and $_.Database -ne $Database}
                            if ($null -ne $OtherOwners) {
                                Write-Message -Level Warning -Message "Folder $FileStreamFolder already in use for Filestream data by $($OtherOwners.Database) on $SqlInstance, cannot restore"
                                $VerificationErrors++
                            }
                        }
                    }
                }

            }

            #Test all backups readable
            $allpaths = $DbHistory | Select-Object -ExpandProperty FullName
            $allpaths_validity = Test-DbaSqlPath -SqlInstance $RestoreInstance -Path $allpaths
            foreach ($path in $allpaths_validity) {
                if ($path.FileExists -eq $false) {
                    Write-Message -Message "Backup File $($path.FilePath) cannot be read" -Level Warning
                    $VerificationErrors++
                }
            }
            #Test for LSN chain
            if ($true -ne $Continue) {
                if (!($DbHistory | Test-DbaLsnChain)) {
                    Write-Message -Message "LSN Check failed" -Level Verbose
                    $VerificationErrors++
                }
            }
            if ($VerificationErrors -eq 0) {
                Write-Message -Message "Marking $Database as verified" -Level Verbose
                $InternalHistory | Where-Object {$_.Database -eq $Database} | foreach-Object {$_.IsVerified = $True}
            }
            else {
                Write-Message -Message "Verification errors  = $VerificationErrors - Has not Passed" -Level Verbose
            }
        }
        $InternalHistory
    }
}
tools\dbatools\functions\Test-DbaCmConnection.ps1
function Test-DbaCmConnection {
    <#
        .SYNOPSIS
            Tests over which paths a computer can be managed.

        .DESCRIPTION
            Tests over which paths a computer can be managed.

            This function tries out the connectivity for:
                - Cim over WinRM
                - Cim over DCOM
                - Wmi
                - PowerShellRemoting
            Results will be written to the connectivity cache and will cause Get-DbaCmObject and Invoke-DbaCmMethod to connect using the way most likely to succeed. This way, it is likely the other commands will take less time to execute. These others too cache their results, in order to dynamically update connection statistics.

            This function ignores global configuration settings limiting which protocols may be used.

        .PARAMETER ComputerName
            The computer to test against.

        .PARAMETER Credential
            The credentials to use when running the test. Bad credentials are automatically cached as non-working. This behavior can be disabled by the 'Cache.Management.Disable.BadCredentialList' configuration.

        .PARAMETER Type
            The connection protocol types to test.
            By default, all types are tested.

            Note that this function will ignore global configurations limiting the types of connections available and test all connections specified here instead.

            Available connection protocol types: "CimRM", "CimDCOM", "Wmi", "PowerShellRemoting"

        .PARAMETER Force
            If this switch is enabled, the Alert will be dropped and recreated on Destination.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ComputerManagement, CIM
            Author: Fred Winmann (@FredWeinmann)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

            **This function should not be called from within dbatools. It is meant as a tool for users only.**

        .LINK
            https://dbatools.io/Test-DbaCmConnection

        .EXAMPLE
            Test-DbaCmConnection -ComputerName sql2014

            Performs a full-spectrum connection test against the computer sql2014. The results will be reported and registered. Future calls from Get-DbaCmObject will recognize the results and optimize the query.

        .EXAMPLE
            Test-DbaCmConnection -ComputerName sql2014 -Credential $null -Type CimDCOM, CimRM

            This test will run a connectivity test of CIM over DCOM and CIM over WinRM against the computer sql2014 using Windows Authentication.

            The results will be reported and registered. Future calls from Get-DbaCmObject will recognize the results and optimize the query.
        #>
    [CmdletBinding()]
    Param (
        [Parameter(ValueFromPipeline = $true)]
        [Sqlcollaborative.Dbatools.Parameter.DbaCmConnectionParameter[]]
        $ComputerName = $env:COMPUTERNAME,

        [System.Management.Automation.PSCredential]
        $Credential,

        [Sqlcollaborative.Dbatools.Connection.ManagementConnectionType[]]
        $Type = @("CimRM", "CimDCOM", "Wmi", "PowerShellRemoting"),

        [switch]
        $Force,

        [switch]
        [Alias('Silent')]$EnableException
    )

    Begin {
        #region Configuration Values
        $disable_cache = Get-DbaConfigValue -Name "ComputerManagement.Cache.Disable.All" -Fallback $false
        $disable_badcredentialcache = Get-DbaConfigValue -Name "ComputerManagement.Cache.Disable.BadCredentialList" -Fallback $false
        #endregion Configuration Values

        #region Helper Functions
        function Test-ConnectionCimRM {
            [CmdletBinding()]
            Param (
                [Sqlcollaborative.Dbatools.Parameter.DbaCmConnectionParameter]
                $ComputerName,

                [System.Management.Automation.PSCredential]
                $Credential
            )

            try {
                $os = $ComputerName.Connection.GetCimRMInstance($Credential, "Win32_OperatingSystem", "root\cimv2")

                New-Object PSObject -Property @{
                    Success       = "Success"
                    Timestamp     = Get-Date
                    Authenticated = $true
                }
            }
            catch {
                if (($_.Exception.InnerException -eq 0x8007052e) -or ($_.Exception.InnerException -eq 0x80070005)) {
                    New-Object PSObject -Property @{
                        Success       = "Error"
                        Timestamp     = Get-Date
                        Authenticated = $false
                    }
                }
                else {
                    New-Object PSObject -Property @{
                        Success       = "Error"
                        Timestamp     = Get-Date
                        Authenticated = $true
                    }
                }
            }
        }

        function Test-ConnectionCimDCOM {
            [CmdletBinding()]
            Param (
                [Sqlcollaborative.Dbatools.Parameter.DbaCmConnectionParameter]
                $ComputerName,

                [System.Management.Automation.PSCredential]
                $Credential
            )

            try {
                $os = $ComputerName.Connection.GetCimDComInstance($Credential, "Win32_OperatingSystem", "root\cimv2")

                New-Object PSObject -Property @{
                    Success       = "Success"
                    Timestamp     = Get-Date
                    Authenticated = $true
                }
            }
            catch {
                if (($_.Exception.InnerException -eq 0x8007052e) -or ($_.Exception.InnerException -eq 0x80070005)) {
                    New-Object PSObject -Property @{
                        Success       = "Error"
                        Timestamp     = Get-Date
                        Authenticated = $false
                    }
                }
                else {
                    New-Object PSObject -Property @{
                        Success       = "Error"
                        Timestamp     = Get-Date
                        Authenticated = $true
                    }
                }
            }
        }

        function Test-ConnectionWmi {
            [CmdletBinding()]
            Param (
                [string]
                $ComputerName,

                [System.Management.Automation.PSCredential]
                $Credential
            )

            try {
                $os = Get-WmiObject -ComputerName $ComputerName -Credential $Credential -Class Win32_OperatingSystem -ErrorAction Stop
                New-Object PSObject -Property @{
                    Success       = "Success"
                    Timestamp     = Get-Date
                    Authenticated = $true
                }
            }
            catch [System.UnauthorizedAccessException] {
                New-Object PSObject -Property @{
                    Success       = "Error"
                    Timestamp     = Get-Date
                    Authenticated = $false
                }
            }
            catch {
                New-Object PSObject -Property @{
                    Success       = "Error"
                    Timestamp     = Get-Date
                    Authenticated = $true
                }
            }
        }

        function Test-ConnectionPowerShellRemoting {
            [CmdletBinding()]
            Param (
                [string]
                $ComputerName,

                [System.Management.Automation.PSCredential]
                $Credential
            )

            try {
                $parameters = @{
                    ScriptBlock  = { Get-WmiObject -Class Win32_OperatingSystem -ErrorAction Stop }
                    ComputerName = $ComputerName
                    ErrorAction  = 'Stop'
                }
                if ($Credential) { $parameters["Credential"] = $Credential }
                $os = Invoke-Command @parameters

                New-Object PSObject -Property @{
                    Success       = "Success"
                    Timestamp     = Get-Date
                    Authenticated = $true
                }
            }
            catch {
                # Will always consider authenticated, since any call with credentials to a server that doesn't exist will also carry invalid credentials error.
                # There simply is no way to differentiate between actual authentication errors and server not reached
                New-Object PSObject -Property @{
                    Success       = "Error"
                    Timestamp     = Get-Date
                    Authenticated = $true
                }
            }
        }
        #endregion Helper Functions
    }
    Process {
        foreach ($ConnectionObject in $ComputerName) {
            if (-not $ConnectionObject.Success) { Stop-Function -Message "Failed to interpret input: $($ConnectionObject.Input)" -Category InvalidArgument -Target $ConnectionObject.Input -Continue}

            $Computer = $ConnectionObject.Connection.ComputerName.ToLower()
            Write-Message -Level VeryVerbose -Message "[$Computer] Testing management connection"

            #region Setup connection object
            $con = $ConnectionObject.Connection
            #endregion Setup connection object

            #region Handle credentials
            $BadCredentialsFound = $false
            if ($con.DisableBadCredentialCache) { $con.KnownBadCredentials.Clear() }
            elseif ($con.IsBadCredential($Credential) -and (-not $Force)) {
                Stop-Function -Message "[$Computer] The credentials supplied are on the list of known bad credentials, skipping. Use -Force to override this." -Continue -Category InvalidArgument -Target $Computer
            }
            elseif ($con.IsBadCredential($Credential) -and $Force) {
                $con.RemoveBadCredential($Credential)
            }
            #endregion Handle credentials

            #region Connectivity Tests
            :types foreach ($ConnectionType in $Type) {
                switch ($ConnectionType) {
                    #region CimRM
                    "CimRM" {
                        Write-Message -Level Verbose -Message "[$Computer] Testing management access using CIM over WinRM"
                        $res = Test-ConnectionCimRM -ComputerName $con -Credential $Credential
                        $con.LastCimRM = $res.Timestamp
                        $con.CimRM = $res.Success
                        Write-Message -Level VeryVerbose -Message "[$Computer] CIM over WinRM Results | Success: $($res.Success), Authentication: $($res.Authenticated)"

                        if (-not $res.Authenticated) {
                            Write-Message -Level Important -Message "[$Computer] The credentials supplied proved to be invalid. Skipping further tests"
                            $con.AddBadCredential($Credential)
                            break types
                        }
                    }
                    #endregion CimRM

                    #region CimDCOM
                    "CimDCOM" {
                        Write-Message -Level Verbose -Message "[$Computer] Testing management access using CIM over DCOM."
                        $res = Test-ConnectionCimDCOM -ComputerName $con -Credential $Credential
                        $con.LastCimDCOM = $res.Timestamp
                        $con.CimDCOM = $res.Success
                        Write-Message -Level VeryVerbose -Message "[$Computer] CIM over DCOM Results | Success: $($res.Success), Authentication: $($res.Authenticated)"

                        if (-not $res.Authenticated) {
                            Write-Message -Level Important -Message "[$Computer] The credentials supplied proved to be invalid. Skipping further tests."
                            $con.AddBadCredential($Credential)
                            break types
                        }
                    }
                    #endregion CimDCOM

                    #region Wmi
                    "Wmi" {
                        Write-Message -Level Verbose -Message "[$Computer] Testing management access using WMI."
                        $res = Test-ConnectionWmi -ComputerName $Computer -Credential $Credential
                        $con.LastWmi = $res.Timestamp
                        $con.Wmi = $res.Success
                        Write-Message -Level VeryVerbose -Message "[$Computer] WMI Results | Success: $($res.Success), Authentication: $($res.Authenticated)"

                        if (-not $res.Authenticated) {
                            Write-Message -Level Important -Message "[$Computer] The credentials supplied proved to be invalid. Skipping further tests"
                            $con.AddBadCredential($Credential)
                            break types
                        }
                    }
                    #endregion Wmi

                    #region PowerShell Remoting
                    "PowerShellRemoting" {
                        Write-Message -Level Verbose -Message "[$Computer] Testing management access using PowerShell Remoting."
                        $res = Test-ConnectionPowerShellRemoting -ComputerName $Computer -Credential $Credential
                        $con.LastPowerShellRemoting = $res.Timestamp
                        $con.PowerShellRemoting = $res.Success
                        Write-Message -Level VeryVerbose -Message "[$Computer] PowerShell Remoting Results | Success: $($res.Success)"
                    }
                    #endregion PowerShell Remoting
                }
            }
            #endregion Connectivity Tests

            if (-not $disable_cache) { [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::Connections[$Computer] = $con }
            $con
        }
    }
    End {

    }
}

tools\dbatools\functions\Test-DbaConnection.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#

function Test-DbaConnection {
    <#
        .SYNOPSIS
            Tests the connection to a single instance.

        .DESCRIPTION
            Tests the ability to connect to an SQL Server instance outputting information about the server and instance.

        .PARAMETER SqlInstance
            The SQL Server Instance to test connection

        .PARAMETER Credential
            Credential object used to connect to the Computer as a different user

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .EXAMPLE
            Test-DbaConnection SQL2016

            ComputerName         : SQL2016
            InstanceName         : MSSQLSERVER
            SqlInstance          : sql2016
            SqlVersion           : 13.0.4001
            ConnectingAsUser     : BASE\ctrlb
            ConnectSuccess       : True
            AuthType             : Windows Authentication
            AuthScheme           : KERBEROS
            TcpPort              : 1433
            IPAddress            : 10.2.1.5
            NetBiosName          : sql2016.base.local
            IsPingable           : True
            PSRemotingAccessible : True
            DomainName           : base.local
            LocalWindows         : 10.0.15063.0
            LocalPowerShell      : 5.1.15063.502
            LocalCLR             : 4.0.30319.42000
            LocalSMOVersion      : 13.0.0.0
            LocalDomainUser      : True
            LocalRunAsAdmin      : False

        .NOTES
            Tags: CIM, Test, Connection
            Author: Chrissy LeMaire

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
    #>
    [CmdletBinding()]
    param (
        [parameter(ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$Credential,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            # Get local environment
            Write-Message -Level Verbose -Message "Getting local environment information"
            $localInfo = [pscustomobject]@{
                Windows    = [environment]::OSVersion.Version.ToString()
                PowerShell = $PSVersionTable.PSversion.ToString()
                CLR        = $PSVersionTable.CLRVersion.ToString()
                SMO        = ((([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.Fullname -like "Microsoft.SqlServer.SMO,*" }).FullName -Split ", ")[1]).TrimStart("Version=")
                DomainUser = $env:computername -ne $env:USERDOMAIN
                RunAsAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
            }

            try {
                <# gather following properties #>
                <#
                        InputName        :
                        ComputerName     :
                        IPAddress        :
                        DNSHostName      :
                        DNSDomain        :
                        Domain           :
                        DNSHostEntry     :
                        FQDN             :
                        FullComputerName :
                     #>
                $resolved = Resolve-DbaNetworkName -ComputerName $instance.ComputerName -Credential $Credential
            }
            catch {
                Stop-Function -Message "Unable to resolve server information" -Category ConnectionError -Target $instance -ErrorRecord $_ -Continue
            }

            # Test for WinRM #Test-WinRM neh
            Write-Message -Level Verbose -Message "Checking remote acccess"
            try {
                $null = Invoke-Command2 -ComputerName $instance.ComputerName -Credential $Credential -ScriptBlock { Get-ChildItem } -ErrorAction Stop
                $remoting = $true
            }
            catch {
                $remoting = $_
            }

            # Test Connection first using Test-Connection which requires ICMP access then failback to tcp if pings are blocked
            Write-Message -Level Verbose -Message "Testing ping to $($instance.ComputerName)"
            $pingable = Test-Connection -ComputerName $instance.ComputerName -Count 1 -Quiet

            # SQL Server connection
            if ($instance.InstanceName -ne "MSSQLSERVER") {
                $sqlport = "N/A"
            }
            else {
                Write-Message -Level Verbose -Message "Testing raw socket connection to default SQL port"
                $tcp = New-Object System.Net.Sockets.TcpClient
                try {
                    $tcp.Connect($baseaddress, 1433)
                    $tcp.Close()
                    $tcp.Dispose()
                    $sqlport = $true
                }
                catch {
                    $sqlport = $false
                }
            }

            try {
                $server = Connect-SqlInstance -SqlInstance $instance.FullSmoName -SqlCredential $SqlCredential
                $connectSuccess = $true
            }
            catch {
                $connectSuccess = $false
                Stop-Function -Message "Issue connection to SQL Server on $instance" -Category ConnectionError -Target $instance -ErrorRecord $_ -Continue
            }

            $username = $server.ConnectionContext.TrueLogin
            if ($username -like "*\*") {
                $authType = "Windows Authentication"
            }
            else {
                $authType = "SQL Authentication"
            }

            # TCP Port
            try {
                $tcpport = (Get-DbaTcpPort -SqlInstance $server -EnableException).Port
            }
            catch {
                $tcpport = $_
            }

            # Auth Scheme
            try {
                $authscheme = (Test-DbaConnectionAuthScheme -SqlInstance $server -WarningVariable authwarning -WarningAction SilentlyContinue).AuthScheme
            }
            catch {
                $authscheme = $_
            }

            if ($authwarning) {
                $authscheme = "N/A"
            }

            [pscustomobject]@{
                ComputerName         = $resolved.ComputerName
                InstanceName         = $instance.InstanceName
                SqlInstance          = $instance.FullSmoName
                SqlVersion           = $server.Version
                ConnectingAsUser     = $username
                ConnectSuccess       = $connectSuccess
                AuthType             = $authType
                AuthScheme           = $authscheme
                TcpPort              = $tcpport
                IPAddress            = $resolved.IPAddress
                NetBiosName          = $resolved.FullComputerName
                IsPingable           = $pingable
                PSRemotingAccessible = $remoting
                DomainName           = $resolved.Domain
                LocalWindows         = $localInfo.Windows
                LocalPowerShell      = $localInfo.PowerShell
                LocalCLR             = $localInfo.CLR
                LocalSMOVersion      = $localInfo.SMO
                LocalDomainUser      = $localInfo.DomainUser
                LocalRunAsAdmin      = $localInfo.RunAsAdmin
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Test-SqlConnection
    }
}
tools\dbatools\functions\Test-DbaConnectionAuthScheme.ps1
function Test-DbaConnectionAuthScheme {
    <#
        .SYNOPSIS
            Returns the transport protocol and authentication scheme of the connection. This is useful to determine if your connection is using Kerberos.

        .DESCRIPTION
            By default, this command will return the ConnectName, ServerName, Transport and AuthScheme of the current connection.

            ConnectName is the name you used to connect. ServerName is the name that the SQL Server reports as its @@SERVERNAME which is used to register its SPN. If you were expecting a Kerberos connection and got NTLM instead, ensure ConnectName and ServerName match.

            If -Kerberos or -Ntlm is specified, the $true/$false results of the test will be returned. Returns $true or $false by default for one server. Returns Server name and Results for more than one server.

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to. Server(s) must be SQL Server 2005 or higher.

        .PARAMETER Kerberos
            If this switch is enabled, checks will be made for Kerberos authentication.

        .PARAMETER Ntlm
            If this switch is enabled, checks will be made for NTLM authentication.

        .PARAMETER Detailed
            Output all properties, will be deprecated in 1.0.0 release.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

            .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: SPN, Kerberos
            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaConnectionAuthScheme

        .EXAMPLE
            Test-DbaConnectionAuthScheme -SqlInstance sqlserver2014a, sql2016

            Returns ConnectName, ServerName, Transport and AuthScheme for sqlserver2014a and sql2016.

        .EXAMPLE
            Test-DbaConnectionAuthScheme -SqlInstance sqlserver2014a -Kerberos

            Returns $true or $false depending on if the connection is Kerberos or not.

        .EXAMPLE
            Test-DbaConnectionAuthScheme -SqlInstance sqlserver2014a | Select-Object *

            Returns the results of "SELECT * from sys.dm_exec_connections WHERE session_id = @@SPID"

    #>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential", "Cred")]
        [PSCredential]$SqlCredential,
        [switch]$Kerberos,
        [switch]$Ntlm,
        [switch]$Detailed,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter Detailed

        $sql = "SELECT  SERVERPROPERTY('MachineName') AS ComputerName,
                            ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
                            SERVERPROPERTY('ServerName') AS SqlInstance,
                            session_id as SessionId, most_recent_session_id as MostRecentSessionId, connect_time as ConnectTime,
                            net_transport as Transport, protocol_type as ProtocolType, protocol_version as ProtocolVersion,
                            endpoint_id as EndpointId, encrypt_option as EncryptOption, auth_scheme as AuthScheme, node_affinity as NodeAffinity,
                            num_reads as NumReads, num_writes as NumWrites, last_read as LastRead, last_write as LastWrite,
                            net_packet_size as PacketSize, client_net_address as ClientNetworkAddress, client_tcp_port as ClientTcpPort,
                            local_net_address as ServerNetworkAddress, local_tcp_port as ServerTcpPort, connection_id as ConnectionId,
                            parent_connection_id as ParentConnectionId, most_recent_sql_handle as MostRecentSqlHandle
                            FROM sys.dm_exec_connections WHERE session_id = @@SPID"
    }

    process {
        foreach ($instance in $SqlInstance) {

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            Write-Message -Level Verbose -Message "Getting results for the following query: $sql."
            try {
                $results = $server.Query($sql)
            }
            catch {
                Stop-Function -Message "Failure" -Target $server -Exception $_ -Continue
            }

            # sorry, standards!
            if ($Kerberos -or $Ntlm) {
                if ($Ntlm) {
                    $auth = 'NTLM'
                }
                else {
                    $auth = 'Kerberos'
                }
                [PSCustomObject]@{
                    ComputerName = $results.ComputerName
                    InstanceName = $results.InstanceName
                    SqlInstance  = $results.SqlInstance
                    Result       = ($server.AuthScheme -eq $auth)
                } | Select-DefaultView -Property SqlInstance, Result
            }
            else {
                Select-DefaultView -InputObject $results -Property ComputerName, InstanceName, SqlInstance, Transport, AuthScheme
            }
        }
    }
}
tools\dbatools\functions\Test-DbaDatabaseCollation.ps1
function Test-DbaDatabaseCollation {
    <#
        .SYNOPSIS
            Compares Database Collations to Server Collation

        .DESCRIPTION
            Compares Database Collations to Server Collation

        .PARAMETER SqlInstance
            The target SQL Server instance or instances.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies the database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies the database(s) to exclude from processing. Options for this list are auto-populated from the server.

        .PARAMETER Detailed
            Output all properties, will be deprecated in 1.0.0 release.

        .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database, Collation
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaDatabaseCollation

        .EXAMPLE
            Test-DbaDatabaseCollation -SqlInstance sqlserver2014a

            Returns server name, database name and true/false if the collations match for all databases on sqlserver2014a.

        .EXAMPLE
            Test-DbaDatabaseCollation -SqlInstance sqlserver2014a -Database db1, db2

            Returns inforamtion for the db1 and db2 databases on sqlserver2014a.

        .EXAMPLE
            Test-DbaDatabaseCollation -SqlInstance sqlserver2014a, sql2016 -Exclude db1

            Returns information for database and server collations for all databases except db1 on sqlserver2014a and sql2016.

        .EXAMPLE
            Get-DbaRegisteredServer -SqlInstance sql2016 | Test-DbaDatabaseCollation

            Returns db/server collation information for every database on every server listed in the Central Management Server on sql2016.
    #>
    [CmdletBinding()]
    Param (
        [parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$Detailed,
        [switch]$EnableException
    )
    begin {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Parameter "Detailed"
    }
    process {
        foreach ($instance in $sqlinstance) {
            # Try connecting to the instance
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $dbs = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                $dbs = $dbs | Where-Object { $Database -contains $_.Name }
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $dbs) {
                Write-Message -Level Verbose -Message "Processing $($db.name) on $servername."
                [PSCustomObject]@{
                    ComputerName      = $server.ComputerName
                    InstanceName      = $server.ServiceName
                    SqlInstance       = $server.DomainInstanceName
                    Database          = $db.name
                    ServerCollation   = $server.collation
                    DatabaseCollation = $db.collation
                    IsEqual           = $db.collation -eq $server.collation
                }
            }
        }
    }
}
tools\dbatools\functions\Test-DbaDatabaseCompatibility.ps1
function Test-DbaDatabaseCompatibility {
    <#
        .SYNOPSIS
            Compares Database Compatibility level to Server Compatibility

        .DESCRIPTION
            Compares Database Compatibility level to Server Compatibility

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to.

        .PARAMETER Credential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies the database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies the database(s) to exclude from processing. Options for this list are auto-populated from the server.

        .PARAMETER Detailed
            Will be deprecated in 1.0.0 release.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database, Compatibility
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaDatabaseCompatibility

        .EXAMPLE
            Test-DbaDatabaseCompatibility -SqlInstance sqlserver2014a

            Returns server name, database name and true/false if the compatibility level match for all databases on sqlserver2014a.

        .EXAMPLE
            Test-DbaDatabaseCompatibility -SqlInstance sqlserver2014a -Database db1, db2

            Returns detailed information for database and server compatibility level for the db1 and db2 databases on sqlserver2014a.

        .EXAMPLE
            Test-DbaDatabaseCompatibility -SqlInstance sqlserver2014a, sql2016 -Exclude db1

            Returns detailed information for database and server compatibility level for all databases except db1 on sqlserver2014a and sql2016.

        .EXAMPLE
            Get-DbaRegisteredServer -SqlInstance sql2014 | Test-DbaDatabaseCompatibility

            Returns db/server compatibility information for every database on every server listed in the Central Management Server on sql2016.
    #>
    [CmdletBinding()]
    [OutputType("System.Collections.ArrayList")]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$Credential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$Detailed,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Parameter "Detailed"
    }

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance."
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $serverversion = "Version$($server.VersionMajor)0"
            $dbs = $server.Databases | Where-Object IsAccessible

            if ($Database) {
                $dbs = $dbs | Where-Object { $Database -contains $_.Name }
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            foreach ($db in $dbs) {
                Write-Message -Level Verbose -Message "Processing $($db.name) on $instance."
                [PSCustomObject]@{
                    ComputerName          = $server.ComputerName
                    InstanceName          = $server.ServiceName
                    SqlInstance           = $server.DomainInstanceName
                    ServerLevel           = $serverversion
                    Database              = $db.name
                    DatabaseCompatibility = $db.CompatibilityLevel
                    IsEqual               = $db.CompatibilityLevel -eq $serverversion
                }
            }
        }
    }
}
tools\dbatools\functions\Test-DbaDatabaseOwner.ps1
function Test-DbaDatabaseOwner {
    <#
        .SYNOPSIS
            Checks database owners against a login to validate which databases do not match that owner.

        .DESCRIPTION
            This function will check all databases on an instance against a SQL login to validate if that
            login owns those databases or not. By default, the function will check against 'sa' for
            ownership, but the user can pass a specific login if they use something else.

            Best Practice reference: http://weblogs.sqlteam.com/dang/archive/2008/01/13/Database-Owner-Troubles.aspx

        .PARAMETER SqlInstance
            Specifies the SQL Server instance(s) to scan.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies the database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies the database(s) to exclude from processing. Options for this list are auto-populated from the server.

        .PARAMETER TargetLogin
            Specifies the login that you wish check for ownership. This defaults to 'sa' or the sysadmin name if sa was renamed. This must be a valid security principal which exists on the target server.

        .PARAMETER Detailed
            Will be deprecated in 1.0.0 release.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Database, Owner, DbOwner
            Author: Michael Fal (@Mike_Fal), http://mikefal.net

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaDatabaseOwner

        .EXAMPLE
            Test-DbaDatabaseOwner -SqlInstance localhost

            Returns all databases where the owner does not match 'sa'.

        .EXAMPLE
            Test-DbaDatabaseOwner -SqlInstance localhost -TargetLogin 'DOMAIN\account'

            Returns all databases where the owner does not match 'DOMAIN\account'.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [string]$TargetLogin ,
        [Switch]$Detailed,
        [Alias('Silent')]
        [Switch]$EnableException
    )

    begin {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Parameter "Detailed"
    }
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            # dynamic sa name for orgs who have changed their sa name
            if (Test-Bound -ParameterName TargetLogin -Not) {
                $TargetLogin = ($server.logins | Where-Object { $_.id -eq 1 }).Name
            }

            #Validate login
            if (($server.Logins.Name) -notmatch [Regex]::Escape($TargetLogin)) {
                Write-Message -Level Verbose -Message "$TargetLogin is not a login on $instance" -Target $instance
            }
        }
        #use online/available dbs
        $dbs = $server.Databases | Where-Object IsAccessible

        #filter database collection based on parameters
        if ($Database) {
            $dbs = $dbs | Where-Object { $Database -contains $_.Name }
        }

        if ($ExcludeDatabase) {
            $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
        }

        #for each database, create custom object for return set.
        foreach ($db in $dbs) {

            if ($db.IsAccessible -eq $false) {
                Stop-Function -Message "The database $db is not accessible. Skipping database." -Continue -Target $db
            }

            Write-Message -Level Verbose -Message "Checking $db"
            [pscustomobject]@{
                ComputerName = $server.ComputerName
                InstanceName = $server.ServiceName
                SqlInstance  = $server.DomainInstanceName
                Server       = $server.DomainInstanceName
                Database     = $db.Name
                DBState      = $db.Status
                CurrentOwner = $db.Owner
                TargetOwner  = $TargetLogin
                OwnerMatch   = ($db.owner -eq $TargetLogin)
            } | Select-DefaultView -ExcludeProperty Server
        }
    }
}
tools\dbatools\functions\Test-DbaDbCompression.ps1
function Test-DbaDbCompression {
    <#
    .SYNOPSIS
        Returns tables and indexes with preferred compression setting.
     .DESCRIPTION
        This function returns the results of a full table/index compression analysis.
        This function returns the best option to date for either NONE, Page, or Row Compression.
        Remember Uptime is critical, the longer uptime, the more accurate the analysis is.
        You would probably be best if you utilized Get-DbaUptime first, before running this command.

        Test-DbaCompression script derived from GitHub and the tigertoolbox
        (https://github.com/Microsoft/tigertoolbox/tree/master/Evaluate-Compression-Gains)
        In the output, you will find the following information:
        Column Percent_Update shows the percentage of update operations on a specific table, index, or partition,
        relative to total operations on that object. The lower the percentage of Updates
        (that is, the table, index, or partition is infrequently updated), the better candidate it is for page compression.
        Column Percent_Scan shows the percentage of scan operations on a table, index, or partition, relative to total
        operations on that object. The higher the value of Scan (that is, the table, index, or partition is mostly scanned),
        the better candidate it is for page compression.
        Column Compression_Type_Recommendation can have four possible outputs indicating where there is most gain,
        if any: 'PAGE', 'ROW', 'NO_GAIN' or '?'. When the output is '?' this approach could not give a recommendation,
        so as a rule of thumb I would lean to ROW if the object suffers mainly UPDATES, or PAGE if mainly INSERTS,
        but this is where knowing your workload is essential. When the output is 'NO_GAIN' well, that means that according
        to sp_estimate_data_compression_savings no space gains will be attained when compressing, as in the above output example,
        where compressing would grow the affected object.

        Note: Note that this script will execute on the context of the current database.
        Also be aware that this may take awhile to execute on large objects, because if the IS locks taken by the
        sp_estimate_data_compression_savings cannot be honored, the SP will be blocked.

    .PARAMETER SqlInstance
        SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

    .PARAMETER SqlCredential
        Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

    .PARAMETER Database
        The database(s) to process - this list is autopopulated from the server. If unspecified, all databases will be processed.

    .PARAMETER ExcludeDatabase
        The database(s) to exclude - this list is autopopulated from the server

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Author: Jason Squires (@js_0505, [email protected])
        Tags: Compression, Table, Database
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Test-DbaCompression

    .EXAMPLE
        Test-DbaCompression -SqlInstance localhost

        Returns all user database files and free space information for the local host

    .EXAMPLE
        Test-DbaCompression -SqlInstance ServerA -Database DBName | Out-GridView
        Returns results of all potential compression options for a single database
        with the recommendation of either Page or Row into and nicely formatted GridView

    .EXAMPLE
        Test-DbaCompression -SqlInstance ServerA
        Returns results of all potential compression options for all databases
        with the recommendation of either Page or Row
    .EXAMPLE
        $cred = Get-Credential sqladmin
        Test-DbaCompression -SqlInstance ServerA -ExcludeDatabase Database -SqlCredential $cred
        Returns results of all potential compression options for all databases
        with the recommendation of either Page or Row

    .EXAMPLE
        $servers = 'Server1','Server2'
        foreach ($svr in $servers)
        {
            Test-DbaCompression -SqlInstance $svr | Export-Csv -Path C:\temp\CompressionAnalysisPAC.csv -Append
        }

        This produces a full analysis of all your servers listed and is pushed to a csv for you to
        analyze.
#>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Write-Message -Level System -Message "Bound parameters: $($PSBoundParameters.Keys -join ", ")"
        $sql = "SET NOCOUNT ON;

IF OBJECT_ID('tempdb..##testdbacompression', 'U') IS NOT NULL
    DROP TABLE ##testdbacompression

IF OBJECT_ID('tempdb..##tmpEstimateRow', 'U') IS NOT NULL
    DROP TABLE ##tmpEstimateRow

IF OBJECT_ID('tempdb..##tmpEstimatePage', 'U') IS NOT NULL
    DROP TABLE ##tmpEstimatePage

CREATE TABLE ##testdbacompression (
    [Schema] SYSNAME
    ,[TableName] SYSNAME
    ,[IndexName] SYSNAME NULL
    ,[Partition] INT
    ,[IndexID] INT
    ,[IndexType] VARCHAR(25)
    ,[PercentScan] SMALLINT
    ,[PercentUpdate] SMALLINT
    ,[RowEstimatePercentOriginal] BIGINT
    ,[PageEstimatePercentOriginal] BIGINT
    ,[CompressionTypeRecommendation] VARCHAR(7)
    ,SizeCurrent BIGINT
    ,SizeRequested BIGINT
    ,PercentCompression NUMERIC(10, 2)
    );

CREATE TABLE ##tmpEstimateRow (
    objname SYSNAME
    ,schname SYSNAME
    ,indid INT
    ,partnr INT
    ,SizeCurrent BIGINT
    ,SizeRequested BIGINT
    ,SampleCurrent BIGINT
    ,SampleRequested BIGINT
    );

CREATE TABLE ##tmpEstimatePage (
    objname SYSNAME
    ,schname SYSNAME
    ,indid INT
    ,partnr INT
    ,SizeCurrent BIGINT
    ,SizeRequested BIGINT
    ,SampleCurrent BIGINT
    ,SampleRequested BIGINT
    );

INSERT INTO ##testdbacompression (
    [Schema]
    ,[TableName]
    ,[IndexName]
    ,[Partition]
    ,[IndexID]
    ,[IndexType]
    ,[PercentScan]
    ,[PercentUpdate]
    )
    SELECT s.NAME AS [Schema]
    ,t.NAME AS [TableName]
    ,x.NAME AS [IndexName]
    ,p.partition_number AS [Partition]
    ,x.Index_ID AS [IndexID]
    ,x.type_desc AS [IndexType]
    ,NULL AS [PercentScan]
    ,NULL AS [PercentUpdate]
FROM sys.tables t
INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
INNER JOIN sys.indexes x ON x.object_id = t.object_id
INNER JOIN sys.partitions p ON x.object_id = p.object_id
    AND x.Index_ID = p.Index_ID
WHERE objectproperty(t.object_id, 'IsUserTable') = 1
    AND p.data_compression_desc = 'NONE'
    AND p.rows > 0
ORDER BY [TableName] ASC;

DECLARE @sqlVersion int
SELECT @sqlVersion = substring(CONVERT(VARCHAR,SERVERPROPERTY('ProductVersion')),0,CHARINDEX('.',(CONVERT(VARCHAR,SERVERPROPERTY('ProductVersion')))))
IF @sqlVersion >= '12'
    BEGIN
        -- remove memory optimized tables
        DELETE tdc
        FROM ##testdbacompression tdc
        INNER JOIN sys.tables t
            ON SCHEMA_NAME(t.schema_id) = tdc.[Schema]
            AND t.name = tdc.TableName
        WHERE t.is_memory_optimized = 1
    END
IF @sqlVersion >= '13'
    BEGIN
        -- remove tables with encrypted columns
        DELETE tdc
        FROM ##testdbacompression tdc
        INNER JOIN sys.tables t
            ON SCHEMA_NAME(t.schema_id) = tdc.[Schema]
            AND t.name = tdc.TableName
        INNER JOIN sys.columns c
            ON t.object_id = c.object_id
        WHERE encryption_type IS NOT NULL
    END
IF @sqlVersion >= '14'
    BEGIN
        -- remove graph (node/edge) tables
        DELETE tdc
        FROM ##testdbacompression tdc
        INNER JOIN sys.tables t
            ON tdc.[Schema] = SCHEMA_NAME(t.schema_id)
            AND tdc.TableName = t.name
        WHERE (is_node = 1 OR is_edge = 1)
    END

DECLARE @schema SYSNAME
    ,@tbname SYSNAME
    ,@ixid INT

DECLARE cur CURSOR FAST_FORWARD
FOR
SELECT [Schema]
    ,[TableName]
    ,[IndexID]
FROM ##testdbacompression

OPEN cur

FETCH NEXT
FROM cur
INTO @schema
    ,@tbname
    ,@ixid

WHILE @@FETCH_STATUS = 0
BEGIN
    DECLARE @sqlcmd NVARCHAR(500)

    SET @sqlcmd = 'EXEC sp_estimate_data_compression_savings ''' + @schema + ''', ''' + @tbname + ''', ''' + cast(@ixid AS VARCHAR) + ''', NULL, ''ROW''';

    INSERT INTO ##tmpEstimateRow (
        objname
        ,schname
        ,indid
        ,partnr
        ,SizeCurrent
        ,SizeRequested
        ,SampleCurrent
        ,SampleRequested
        )
    EXECUTE sp_executesql @sqlcmd

    SET @sqlcmd = 'EXEC sp_estimate_data_compression_savings ''' + @schema + ''', ''' + @tbname + ''', ''' + cast(@ixid AS VARCHAR) + ''', NULL, ''PAGE''';

    INSERT INTO ##tmpEstimatePage (
        objname
        ,schname
        ,indid
        ,partnr
        ,SizeCurrent
        ,SizeRequested
        ,SampleCurrent
        ,SampleRequested
        )
    EXECUTE sp_executesql @sqlcmd

    FETCH NEXT
    FROM cur
    INTO @schema
        ,@tbname
        ,@ixid
END

CLOSE cur

DEALLOCATE cur;

--Update usage and partition_number - If database was restore the sys.dm_db_index_operational_stats will be empty until tables have accesses. Executing the sp_estimate_data_compression_savings first will make those entries appear
UPDATE ##testdbacompression
SET  [PercentScan] = i.range_scan_count * 100.0 / NULLIF((i.range_scan_count + i.leaf_insert_count + i.leaf_delete_count + i.leaf_update_count + i.leaf_page_merge_count + i.singleton_lookup_count), 0)
    ,[PercentUpdate] = i.leaf_update_count * 100.0 / NULLIF((i.range_scan_count + i.leaf_insert_count + i.leaf_delete_count + i.leaf_update_count + i.leaf_page_merge_count + i.singleton_lookup_count), 0)
FROM sys.dm_db_index_operational_stats(db_id(), NULL, NULL, NULL) i
INNER JOIN ##testdbacompression tmp ON OBJECT_ID(tmp.TableName) = i.[object_id]
    AND tmp.IndexID = i.index_id;

WITH tmp_cte (
    objname
    ,schname
    ,indid
    ,pct_of_orig_row
    ,pct_of_orig_page
    ,SizeCurrent
    ,SizeRequested
    )
AS (
    SELECT tr.objname
        ,tr.schname
        ,tr.indid
        ,(tr.SampleRequested * 100) / CASE
            WHEN tr.SampleCurrent = 0
                THEN 1
            ELSE tr.SampleCurrent
            END AS pct_of_orig_row
        ,(tp.SampleRequested * 100) / CASE
            WHEN tp.SampleCurrent = 0
                THEN 1
            ELSE tp.SampleCurrent
            END AS pct_of_orig_page
        ,tr.SizeCurrent
        ,tr.SizeRequested
    FROM ##tmpestimaterow tr
    INNER JOIN ##tmpestimatepage tp ON tr.objname = tp.objname
        AND tr.schname = tp.schname
        AND tr.indid = tp.indid
        AND tr.partnr = tp.partnr
    )
UPDATE ##testdbacompression
SET [RowEstimatePercentOriginal] = tcte.pct_of_orig_row
    ,[PageEstimatePercentOriginal] = tcte.pct_of_orig_page
    ,SizeCurrent = tcte.SizeCurrent
    ,SizeRequested = tcte.SizeRequested
    ,PercentCompression = 100 - (cast(tcte.[SizeRequested] AS NUMERIC(21, 2)) * 100 / (tcte.[SizeCurrent] - ABS(SIGN(tcte.[SizeCurrent])) + 1))
FROM tmp_cte tcte
    ,##testdbacompression tcomp
WHERE tcte.objname = tcomp.TableName
    AND tcte.schname = tcomp.[schema]
    AND tcte.indid = tcomp.IndexID;

WITH tmp_cte2 (
    TableName
    ,[schema]
    ,IndexID
    ,[CompressionTypeRecommendation]
    )
AS (
    SELECT TableName
        ,[schema]
        ,IndexID
        ,CASE
            WHEN [RowEstimatePercentOriginal] >= 100
                AND [PageEstimatePercentOriginal] >= 100
                THEN 'NO_GAIN'
            WHEN [PercentUpdate] >= 10
                THEN 'ROW'
            WHEN [PercentScan] <= 1
                AND [PercentUpdate] <= 1
                AND [RowEstimatePercentOriginal] < [PageEstimatePercentOriginal]
                THEN 'ROW'
            WHEN [PercentScan] <= 1
                AND [PercentUpdate] <= 1
                AND [RowEstimatePercentOriginal] > [PageEstimatePercentOriginal]
                THEN 'PAGE'
            WHEN [PercentScan] >= 60
                AND [PercentUpdate] <= 5
                THEN 'PAGE'
            WHEN [PercentScan] <= 35
                AND [PercentUpdate] <= 5
                THEN '?'
            ELSE 'ROW'
            END
    FROM ##testdbacompression
    )
UPDATE ##testdbacompression
SET [CompressionTypeRecommendation] = tcte2.[CompressionTypeRecommendation]
FROM tmp_cte2 tcte2
    ,##testdbacompression tcomp2
WHERE tcte2.TableName = tcomp2.TableName
    AND tcte2.[schema] = tcomp2.[schema]
    AND tcte2.IndexID = tcomp2.IndexID;

SET NOCOUNT ON;

SELECT DBName = DB_Name()
    ,[Schema]
    ,[TableName]
    ,[IndexName]
    ,[Partition]
    ,[IndexID]
    ,[IndexType]
    ,[PercentScan]
    ,[PercentUpdate]
    ,[RowEstimatePercentOriginal]
    ,[PageEstimatePercentOriginal]
    ,[CompressionTypeRecommendation]
    ,SizeCurrentKB = [SizeCurrent]
    ,SizeRequestedKB = [SizeRequested]
    ,PercentCompression
FROM ##testdbacompression;

IF OBJECT_ID('tempdb..##setdbacompression', 'U') IS NOT NULL
    DROP TABLE ##testdbacompression

IF OBJECT_ID('tempdb..##tmpEstimateRow', 'U') IS NOT NULL
    DROP TABLE ##tmpEstimateRow

IF OBJECT_ID('tempdb..##tmpEstimatePage', 'U') IS NOT NULL
    DROP TABLE ##tmpEstimatePage;

"
    }

    process {

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level VeryVerbose -Message "Connecting to $instance" -Target $instance
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SourceSqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failed to process Instance $Instance" -ErrorRecord $_ -Target $instance -Continue
            }

            $Server.ConnectionContext.StatementTimeout = 0

            [long]$instanceVersionNumber = $($server.VersionString).Replace(".", "")


            #If SQL Server 2016 SP1 (13.0.4001.0) or higher every version supports compression.
            if ($Server.EngineEdition -ne "EnterpriseOrDeveloper" -and $instanceVersionNumber -lt 13040010) {
                Stop-Function -Message "Compresison before SQLServer 2016 SP1 (13.0.4001.0) is only supported by enterprise, developer or evaluation edition. $Server has version $($server.VersionString) and edition is $($Server.EngineEdition)." -Target $db -Continue
            }
            #If IncludeSystemDBs is true, include systemdbs
            #look at all databases, online/offline/accessible/inaccessible and tell user if a db can't be queried.
            try {
                $dbs = $server.Databases | Where-Object IsAccessible

                if ($Database) {
                    $dbs = $dbs | Where-Object { $Database -contains $_.Name -and $_.IsSystemObject -eq 0 }
                }

                else {
                    $dbs = $dbs | Where-Object { $_.IsSystemObject -eq 0 }
                }

                if (Test-Bound "ExcludeDatabase") {
                    $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
                }
            }
            catch {
                Stop-Function -Message "Unable to gather list of databases for $instance" -Target $instance -ErrorRecord $_ -Continue
            }

            foreach ($db in $dbs) {
                try {
                    $dbCompatibilityLevel = [int]($db.CompatibilityLevel.ToString().Replace('Version', ''))

                    Write-Message -Level Verbose -Message "Querying $instance - $db"
                    if ($db.status -ne 'Normal' -or $db.IsAccessible -eq $false) {
                        Write-Message -Level Warning -Message "$db is not accessible." -Target $db
                        Continue
                    }

                    if ($dbCompatibilityLevel -lt 100) {
                        Stop-Function -Message "$db has a compatibility level lower than Version100 and will be skipped." -Target $db -Continue
                        Continue
                    }
                    #Execute query against individual database and add to output
                    foreach ($row in ($server.Query($sql, $db.Name))) {
                        [pscustomobject]@{
                            ComputerName                  = $server.ComputerName
                            InstanceName                  = $server.ServiceName
                            SqlInstance                   = $server.DomainInstanceName
                            Database                      = $row.DBName
                            Schema                        = $row.Schema
                            TableName                     = $row.TableName
                            IndexName                     = $row.IndexName
                            Partition                     = $row.Partition
                            IndexID                       = $row.IndexID
                            IndexType                     = $row.IndexType
                            PercentScan                   = $row.PercentScan
                            PercentUpdate                 = $row.PercentUpdate
                            RowEstimatePercentOriginal    = $row.RowEstimatePercentOriginal
                            PageEstimatePercentOriginal   = $row.PageEstimatePercentOriginal
                            CompressionTypeRecommendation = $row.CompressionTypeRecommendation
                            SizeCurrent                   = [dbasize]($row.SizeCurrentKB * 1024)
                            SizeRequested                 = [dbasize]($row.SizeRequestedKB * 1024)
                            PercentCompression            = $row.PercentCompression
                        }
                    }
                }
                catch {
                    Stop-Function -Message "Unable to query $instance - $db" -Target $db -ErrorRecord $_ -Continue
                }
            }
        }
    }
}
tools\dbatools\functions\Test-DbaDbVirtualLogFile.ps1
#ValidationTags#CodeStyle,Messaging,FlowControl,Pipeline#
function Test-DbaDbVirtualLogFile {
    <#
        .SYNOPSIS
            Returns calculations on the database virtual log files for database on a SQL instance.

        .DESCRIPTION
            Having a transaction log file with too many virtual log files (VLFs) can hurt database performance.

            Too many VLFs can cause transaction log backups to slow down and can also slow down database recovery and, in extreme cases, even affect insert/update/delete performance.

            References:
                http://www.sqlskills.com/blogs/kimberly/transaction-log-vlfs-too-many-or-too-few/
                http://blogs.msdn.com/b/saponsqlserver/archive/2012/02/22/too-many-virtual-log-files-vlfs-can-cause-slow-database-recovery.aspx

            If you've got a high number of VLFs, you can use Expand-SqlTLogResponsibly to reduce the number.

        .PARAMETER SqlInstance
            Specifies the SQL Server instance(s) to scan.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies the database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies the database(s) to exclude from processing. Options for this list are auto-populated from the server.

        .PARAMETER IncludeSystemDBs
            If this switch is enabled, system database information will be displayed.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: VLF, Database

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaDbVirtualLogFile

        .EXAMPLE
            Test-DbaDbVirtualLogFile -SqlInstance sqlcluster

            Returns all user database virtual log file counts for the sqlcluster instance.

        .EXAMPLE
            Test-DbaDbVirtualLogFile -SqlInstance sqlserver | Where-Object {$_.Count -ge 50}

            Returns user databases that have 50 or more VLFs.

        .EXAMPLE
            @('sqlserver','sqlcluster') | Test-DbaDbVirtualLogFile

            Returns all VLF information for the sqlserver and sqlcluster SQL Server instances. Processes data via the pipeline.

        .EXAMPLE
            Test-DbaDbVirtualLogFile -SqlInstance sqlcluster -Database db1, db2

            Returns VLF counts for the db1 and db2 databases on sqlcluster.
    #>
    [CmdletBinding()]
    [OutputType([System.Collections.ArrayList])]
    param ([parameter(ValueFromPipeline, Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$IncludeSystemDBs,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $dbs = $server.Databases
            if ($Database) {
                $dbs = $dbs | Where-Object Name -in $Database
            }
            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            if (!$IncludeSystemDBs) {
                $dbs = $dbs | Where-Object IsSystemObject -eq $false
            }

            foreach ($db in $dbs) {
                try {
                    $data = Get-DbaDbVirtualLogFile -SqlInstance $server -Database $db.Name
                    $logFile = Get-DbaDatabaseFile -SqlInstance $server -Database $db.Name | Where-Object Type -eq 1

                    $active = $data | Where-Object Status -eq 2
                    $inactive = $data | Where-Object Status -eq 0

                    [PSCustomObject]@{
                        ComputerName      = $server.ComputerName
                        InstanceName      = $server.ServiceName
                        SqlInstance       = $server.DomainInstanceName
                        Database          = $db.name
                        Total             = $data.Count
                        TotalCount        = $data.Count
                        Inactive          = if ($inactive -and $null -eq $inactive.Count) {1} else {$inactive.Count}
                        Active            = if ($active -and $null -eq $active.Count) {1} else {$active.Count}
                        LogFileName       = $logFile.LogicalName -join ","
                        LogFileGrowth     = $logFile.Growth -join ","
                        LogFileGrowthType = $logFile.GrowthType -join ","
                    } | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, Database, Total
                }
                catch {
                    Stop-Function -Message "Unable to query $($db.name) on $instance." -ErrorRecord $_ -Target $db -Continue
                }
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Test-DbaVirtualLogFile
    }
}
tools\dbatools\functions\Test-DbaDiskAlignment.ps1
function Test-DbaDiskAlignment {
    <#
        .SYNOPSIS
            Verifies that your non-dynamic disks are aligned according to physical constraints.

        .DESCRIPTION
            Returns $true or $false by default for one server. Returns Server name and IsBestPractice for more than one server.

            Please refer to your storage vendor best practices before following any advice below.

            By default issues with disk alignment should be resolved by a new installation of Windows Server 2008, Windows Vista, or later operating systems, but verifying disk alignment continues to be recommended as a best practice.
            While some versions of Windows use different starting alignments, if you are starting anew 1MB is generally the best practice offset for current operating systems (because it ensures that the partition offset % common stripe unit sizes == 0 )

            Caveats:
            * Dynamic drives (or those provisioned via third party software) may or may not have accurate results when polled by any of the built in tools, see your vendor for details.
            * Windows does not have a reliable way to determine stripe unit Sizes. These values are obtained from vendor disk management software or from your SAN administrator.
            * System drives in versions previous to Windows Server 2008 cannot be aligned, but it is generally not recommended to place SQL Server databases on system drives.

        .PARAMETER ComputerName
            The server(s) to check disk configuration on.

        .PARAMETER Detailed
            Output all properties, will be deprecated in 1.0.0 release.

        .PARAMETER Credential
            Specifies an alternate Windows account to use when enumerating drives on the server. May require Administrator privileges. To use:

            $cred = Get-Credential, then pass $cred object to the -Credential parameter.

        .PARAMETER SQLCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER NoSqlCheck
            If this switch is enabled, the disk(s) will not be checked for SQL Server data or log files.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .EXAMPLE
            Test-DbaDiskAlignment -ComputerName sqlserver2014a

            Tests the disk alignment of a single server named sqlserver2014a

        .EXAMPLE
            Test-DbaDiskAlignment -ComputerName sqlserver2014a, sqlserver2014b, sqlserver2014c

            Tests the disk alignment of multiple servers

        .NOTES
            Tags: Storage
            The preferred way to determine if your disks are aligned (or not) is to calculate:
            1. Partition offset - stripe unit size
            2. Stripe unit size - File allocation unit size

            References:
            Disk Partition Alignment Best Practices for SQL Server - https://technet.microsoft.com/en-us/library/dd758814(v=sql.100).aspx
            A great article and behind most of this code.

            Getting Partition Offset information with Powershell - http://sqlblog.com/blogs/jonathan_kehayias/archive/2010/03/01/getting-partition-Offset-information-with-powershell.aspx
            Thanks to Jonathan Kehayias!

            Decree: Set your partition Offset and block Size and make SQL Server faster - http://www.midnightdba.com/Jen/2014/04/decree-set-your-partition-Offset-and-block-Size-make-sql-server-faster/
            Thanks to Jen McCown!

            Disk Performance Hands On - http://www.kendalvandyke.com/2009/02/disk-performance-hands-on-series-recap.html
            Thanks to Kendal Van Dyke!

            Get WMI Disk Information - http://powershell.com/cs/media/p/7937.aspx
            Thanks to jbruns2010!

            Author: Constantine Kokkinos (https://constantinekokkinos.com, @mobileck)

            dbatools PowerShell module (https://dbatools.io, [email protected],)
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaDiskAlignment
    #>
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer", "SqlInstance")]
        [object[]]$ComputerName,
        [switch]$Detailed,
        [System.Management.Automation.PSCredential]$Credential,
        [System.Management.Automation.PSCredential]$SqlCredential,
        [switch]$NoSqlCheck,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -Parameter 'Detailed'

        $sessionoption = New-CimSessionOption -Protocol DCom

        function Get-DiskAlignment {
            [CmdletBinding()]
            param (
                $CimSession,
                [string]$FunctionName = (Get-PSCallStack)[0].Command,
                [bool]$NoSqlCheck,
                [string]$ComputerName,
                [System.Management.Automation.PSCredential]$SqlCredential,
                [bool]$EnableException = $EnableException
            )

            $SqlInstances = @()
            $offsets = @()

            #region Retrieving partition/disk Information
            try {
                Write-Message -Level Verbose -Message "Gathering information about first partition on each disk for $ComputerName." -FunctionName $FunctionName

                try {
                    $partitions = Get-CimInstance -CimSession $CimSession -ClassName Win32_DiskPartition -Namespace "root\cimv2" -ErrorAction Stop
                }
                catch {
                    if ($_.Exception -match "namespace") {
                        Stop-Function -Message "Can't get disk alignment info for $ComputerName. Unsupported operating system." -InnerErrorRecord $_ -Target $ComputerName -FunctionName $FunctionName
                        return
                    }
                    else {
                        Stop-Function -Message "Can't get disk alignment info for $ComputerName. Check logs for more details." -InnerErrorRecord $_ -Target $ComputerName -FunctionName $FunctionName
                        return
                    }
                }


                $disks = @()
                $disks += $($partitions | ForEach-Object {
                        Get-CimInstance -CimSession $CimSession -Query "ASSOCIATORS OF {Win32_DiskPartition.DeviceID=""$($_.DeviceID.Replace("\", "\\"))""} WHERE AssocClass = Win32_LogicalDiskToPartition" |
                            Add-Member -Force -MemberType noteproperty -Name BlockSize -Value $_.BlockSize -PassThru |
                            Add-Member -Force -MemberType noteproperty -Name BootPartition -Value $_.BootPartition -PassThru |
                            Add-Member -Force -MemberType noteproperty -Name DiskIndex -Value $_.DiskIndex -PassThru |
                            Add-Member -Force -MemberType noteproperty -Name Index -Value $_.Index -PassThru |
                            Add-Member -Force -MemberType noteproperty -Name NumberOfBlocks -Value $_.NumberOfBlocks -PassThru |
                            Add-Member -Force -MemberType noteproperty -Name StartingOffset -Value $_.StartingOffset -PassThru |
                            Add-Member -Force -MemberType noteproperty -Name Type -Value $_.Type -PassThru
                    } |
                        Select-Object BlockSize, BootPartition, Description, DiskIndex, Index, Name, NumberOfBlocks, Size, StartingOffset, Type
                )
                Write-Message -Level Verbose -Message "Gathered CIM information." -FunctionName $FunctionName
            }
            catch {
                Stop-Function -Message "Can't connect to CIM on $ComputerName." -FunctionName $FunctionName -InnerErrorRecord $_
                return
            }
            #endregion Retrieving partition Information

            #region Retrieving Instances
            if (-not $NoSqlCheck) {
                Write-Message -Level Verbose -Message "Checking for SQL Services." -FunctionName $FunctionName
                $sqlservices = Get-CimInstance -ClassName Win32_Service -CimSession $CimSession | Where-Object DisplayName -like 'SQL Server (*'
                foreach ($service in $sqlservices) {
                    $instance = $service.DisplayName.Replace('SQL Server (', '')
                    $instance = $instance.TrimEnd(')')

                    $instancename = $instance.Replace("MSSQLSERVER", "Default")
                    Write-Message -Level Verbose -Message "Found instance $instancename" -FunctionName $FunctionName
                    if ($instance -eq 'MSSQLSERVER') {
                        $SqlInstances += $ComputerName
                    }
                    else {
                        $SqlInstances += "$ComputerName\$instance"
                    }
                }
                $sqlcount = $SqlInstances.Count
                Write-Message -Level Verbose -Message "$sqlcount instance(s) found." -FunctionName $FunctionName
            }
            #endregion Retrieving Instances

            #region Offsets
            foreach ($disk in $disks) {
                if (!$disk.name.StartsWith("\\")) {
                    $diskname = $disk.Name
                    if ($NoSqlCheck -eq $false) {
                        $sqldisk = $false

                        foreach ($SqlInstance in $SqlInstances) {
                            Write-Message -Level Verbose -Message "Connecting to SQL instance ($SqlInstance)." -FunctionName $FunctionName
                            try {
                                if ($null -ne $SqlCredential) {
                                    $smoserver = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
                                }
                                else {
                                    $smoserver = Connect-SqlInstance -SqlInstance $SqlInstance # win auth
                                }
                                $sql = "Select count(*) as Count from sys.master_files where physical_name like '$diskname%'"
                                Write-Message -Level Verbose -Message "Query is: $sql" -FunctionName $FunctionName
                                Write-Message -Level Verbose -Message "SQL Server is: $SqlInstance." -FunctionName $FunctionName
                                $sqlcount = $smoserver.Databases['master'].ExecuteWithResults($sql).Tables[0].Count
                                if ($sqlcount -gt 0) {
                                    $sqldisk = $true
                                    break
                                }
                            }
                            catch {
                                Stop-Function -Message "Can't connect to $ComputerName ($SqlInstance)." -FunctionName $FunctionName -InnerErrorRecord $_
                                return
                            }
                        }
                    }

                    if ($NoSqlCheck -eq $false) {
                        if ($sqldisk -eq $true) {
                            $offsets += $disk
                        }
                    }
                    else {
                        $offsets += $disk
                    }
                }
            }
            #endregion Offsets

            #region Processing results
            Write-Message -Level Verbose -Message "Checking $($offsets.count) partitions." -FunctionName $FunctionName

            $allpartitions = @()
            foreach ($partition in $offsets) {
                # Unfortunately "Windows does not have a reliable way to determine stripe unit Sizes. These values are obtained from vendor disk management software or from your SAN administrator."
                # And this is the #1 most impactful issue with disk alignment :D
                # What we can do is test common stripe unit Sizes against the Offset we have and give advice if the Offset they chose would work in those scenarios
                $offset = $partition.StartingOffset / 1kb
                $type = $partition.Type
                $stripe_units = @(64, 128, 256, 512, 1024) # still wish I had a better way to verify this or someone to pat my back and say its alright.

                # testing dynamic disks, everyone states that info from dynamic disks is not to be trusted, so throw a warning.
                Write-Message -Level Verbose -Message "Testing for dynamic disks." -FunctionName $FunctionName
                if ($type -eq "Logical Disk Manager") {
                    $IsDynamicDisk = $true
                    Write-Message -Level Warning -Message "Disk is dynamic, all Offset calculations should be suspect, please refer to your vendor to determine actual Offset calculations." -FunctionName $FunctionName
                }
                else {
                    $IsDynamicDisk = $false
                }

                Write-Message -Level Verbose -Message "Checking for best practices offsets." -FunctionName $FunctionName

                if ($offset -ne 64 -and $offset -ne 128 -and $offset -ne 256 -and $offset -ne 512 -and $offset -ne 1024) {
                    $IsOffsetBestPractice = $false
                }
                else {
                    $IsOffsetBestPractice = $true
                }

                # as we cant tell the actual size of the file strip unit, just check all the sizes I know about
                foreach ($size in $stripe_units) {
                    if ($offset % $size -eq 0) {
                        # for proper alignment we really only need to know that your offset divided by your stripe unit size has a remainder of 0
                        $OffsetModuloKB = "$($offset % $size)"
                        $isBestPractice = $true
                    }
                    else {
                        $OffsetModuloKB = "$($offset % $size)"
                        $isBestPractice = $false
                    }

                    $output = [PSCustomObject]@{
                        Server                    = $ComputerName
                        Name                      = "$($partition.Name)"
                        PartitonSizeInMB          = $($partition.Size / 1MB)
                        PartitionType             = $partition.Type
                        TestingStripeSizeKB       = $size
                        OffsetModuluCalculationKB = $OffsetModuloKB
                        StartingOffsetKB          = $offset
                        IsOffsetBestPractice      = $IsOffsetBestPractice
                        IsBestPractice            = $isBestPractice
                        NumberOfBlocks            = $partition.NumberOfBlocks
                        BootPartition             = $partition.BootPartition
                        PartitionBlockSize        = $partition.BlockSize
                        IsDynamicDisk             = $IsDynamicDisk
                    }
                    $allpartitions += $output
                }
            }
            #endregion Processing results
            return $allpartitions
        }
    }

    process {
        foreach ($computer in $ComputerName) {
            Write-Message -Level VeryVerbose -Message "Processing: $computer."

            $computer = Resolve-DbaNetworkName -ComputerName $computer -Credential $credential
            $Computer = $computer.ComputerName

            if (!$Computer) {
                Stop-Function -Message "Couldn't resolve hostname. Skipping." -Continue
            }

            #region Connecting to server via Cim
            Write-Message -Level Verbose -Message "Creating CimSession on $computer over WSMan"

            if (!$Credential) {
                $cimsession = New-CimSession -ComputerName $Computer -ErrorAction Ignore
            }
            else {
                $cimsession = New-CimSession -ComputerName $Computer -ErrorAction Ignore -Credential $Credential
            }

            if ($null -eq $cimsession.id) {
                Write-Message -Level Verbose -Message "Creating CimSession on $computer over WSMan failed. Creating CimSession on $computer over DCOM."

                if (!$Credential) {
                    $cimsession = New-CimSession -ComputerName $Computer -SessionOption $sessionoption -ErrorAction Ignore -Credential $Credential
                }
                else {
                    $cimsession = New-CimSession -ComputerName $Computer -SessionOption $sessionoption -ErrorAction Ignore
                }
            }

            if ($null -eq $cimsession.id) {
                Stop-Function -Message "Can't create CimSession on $computer." -Target $Computer -Continue
            }
            #endregion Connecting to server via Cim

            Write-Message -Level Verbose -Message "Getting Power Plan information from $Computer."


            try {
                $data = Get-DiskAlignment -CimSession $cimsession -NoSqlCheck $NoSqlCheck -ComputerName $Computer -ErrorAction Stop
            }
            catch {
                Stop-Function -Message "Failed to process $($Computer): $($_.Exception.Message)" -Continue -InnerErrorRecord $_ -Target $Computer
            }

            if ($null -eq $data.Server) {
                Stop-Function -Message "CIM query to $Computer failed." -Continue -Target $computer
            }

            if ($data.Count -gt 1) {
                $data.GetEnumerator()
            }
            else {
                $data
            }
        }
    }
}
tools\dbatools\functions\Test-DbaDiskAllocation.ps1
function Test-DbaDiskAllocation {
    <#
        .SYNOPSIS
            Checks all disks on a computer to see if they are formatted with allocation units of 64KB.

        .DESCRIPTION
            Checks all disks on a computer for disk allocation units that match best practice recommendations. If one server is checked, only $true or $false is returned. If multiple servers are checked, each server's name and an IsBestPractice field are returned.

            Specify -Detailed for details.

            References:
            https://technet.microsoft.com/en-us/library/dd758814(v=sql.100).aspx - "The performance question here is usually not one of correlation per the formula, but whether the cluster size has been explicitly defined at 64 KB, which is a best practice for SQL Server."

            http://tk.azurewebsites.net/2012/08/

        .PARAMETER ComputerName
            The server(s) to check disk configuration on.

        .PARAMETER NoSqlCheck
            If this switch is enabled, the disk(s) will not be checked for SQL Server data or log files.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Detailed
            Output all properties, will be deprecated in 1.0.0 release.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: CIM, Storage
            Requires: Windows sysadmin access on SQL Servers

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaDiskAllocation

        .EXAMPLE
            Test-DbaDiskAllocation -ComputerName sqlserver2014a

            Scans all disks on server sqlserver2014a for best practice allocation unit size.

        .EXAMPLE
            Test-DbaDiskAllocation -ComputerName sqlserver2014 | Select-Output *

            Scans all disks on server sqlserver2014a for allocation unit size and returns detailed results for each.

        .EXAMPLE
            Test-DbaDiskAllocation -ComputerName sqlserver2014a -NoSqlCheck

            Scans all disks not hosting SQL Server data or log files on server sqlserver2014a for best practice allocation unit size.
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    [OutputType("System.Collections.ArrayList", "System.Boolean")]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer", "SqlInstance")]
        [object[]]$ComputerName,
        [switch]$NoSqlCheck,
        [object]$SqlCredential,
        [switch]$Detailed,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter Detailed

        $sessionoptions = New-CimSessionOption -Protocol DCOM

        function Get-AllDiskAllocation {
            $alldisks = @()
            $SqlInstances = @()

            try {
                Write-Message -Level Verbose -Message "Getting disk information from $computer."

                # $query = "Select Label, BlockSize, Name from Win32_Volume WHERE FileSystem='NTFS'"
                # $disks = Get-WmiObject -ComputerName $ipaddr -Query $query | Sort-Object -Property Name
                $disks = Get-CimInstance -CimSession $CIMsession -ClassName win32_volume -Filter "FileSystem='NTFS'" -ErrorAction Stop | Sort-Object -Property Name
            }
            catch {
                Stop-Function -Message "Can't connect to WMI on $computer."
                return
            }

            if ($NoSqlCheck -eq $false) {
                Write-Message -Level Verbose -Message "Checking for SQL Services"
                $sqlservices = Get-Service -ComputerName $ipaddr | Where-Object { $_.DisplayName -like 'SQL Server (*' }
                foreach ($service in $sqlservices) {
                    $instance = $service.DisplayName.Replace('SQL Server (', '')
                    $instance = $instance.TrimEnd(')')

                    $instancename = $instance.Replace("MSSQLSERVER", "Default")
                    Write-Message -Level Verbose -Message "Found instance $instancename."

                    if ($instance -eq 'MSSQLSERVER') {
                        $SqlInstances += $ipaddr
                    }
                    else {
                        $SqlInstances += "$ipaddr\$instance"
                    }
                }
                $sqlcount = $SqlInstances.Count

                Write-Message -Level Verbose -Message "$sqlcount instance(s) found."
            }

            foreach ($disk in $disks) {
                if (!$disk.name.StartsWith("\\")) {
                    $diskname = $disk.Name

                    if ($NoSqlCheck -eq $false) {
                        $sqldisk = $false

                        foreach ($SqlInstance in $SqlInstances) {
                            Write-Message -Level Verbose -Message "Connecting to SQL instance ($SqlInstance)."
                            try {
                                $smoserver = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
                                $sql = "Select count(*) as Count from sys.master_files where physical_name like '$diskname%'"
                                $sqlcount = $smoserver.Databases['master'].ExecuteWithResults($sql).Tables[0].Count
                                if ($sqlcount -gt 0) {
                                    $sqldisk = $true
                                    break
                                }
                            }
                            catch {
                                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                                continue
                            }
                        }
                    }

                    if ($disk.BlockSize -eq 65536) {
                        $IsBestPractice = $true
                    }
                    else {
                        $IsBestPractice = $false
                    }

                    $windowsdrive = "$env:SystemDrive\"

                    if ($diskname -eq $windowsdrive) {
                        $IsBestPractice = $false
                    }

                    if ($NoSqlCheck -eq $false) {
                        $alldisks += [PSCustomObject]@{
                            Server         = $computer
                            Name           = $diskname
                            Label          = $disk.Label
                            BlockSize      = $disk.BlockSize
                            IsSqlDisk      = $sqldisk
                            IsBestPractice = $IsBestPractice
                        }
                    }
                    else {
                        $alldisks += [PSCustomObject]@{
                            Server         = $computer
                            Name           = $diskname
                            Label          = $disk.Label
                            BlockSize      = $disk.BlockSize
                            IsBestPractice = $IsBestPractice
                        }
                    }
                }
            }
            return $alldisks
        }
    }

    process {
        foreach ($computer in $ComputerName) {

            $computer = Resolve-DbaNetworkName -ComputerName $computer -Credential $credential
            $ipaddr = $computer.IpAddress
            $Computer = $computer.ComputerName

            if (!$Computer) {
                Stop-Function -Message "Couldn't resolve hostname. Skipping." -Continue
            }

            Write-Message -Level Verbose -Message "Creating CimSession on $computer over WSMan."

            if (!$Credential) {
                $cimsession = New-CimSession -ComputerName $Computer -ErrorAction SilentlyContinue
            }
            else {
                $cimsession = New-CimSession -ComputerName $Computer -ErrorAction SilentlyContinue -Credential $Credential
            }

            if ($null -eq $cimsession.id) {
                Write-Message -Level Verbose -Message "Creating CimSession on $computer over WSMan failed. Creating CimSession on $computer over DCOM."

                if (!$Credential) {
                    $cimsession = New-CimSession -ComputerName $Computer -SessionOption $sessionoption -ErrorAction SilentlyContinue -Credential $Credential
                }
                else {
                    $cimsession = New-CimSession -ComputerName $Computer -SessionOption $sessionoption -ErrorAction SilentlyContinue
                }
            }

            if ($null -eq $cimsession.id) {
                Stop-Function -Message "Can't create CimSession on $computer" -Target $Computer
            }

            Write-Message -Level Verbose -Message "Getting Power Plan information from $Computer"

            $data = Get-AllDiskAllocation $computer

            if ($data.Count -gt 1) {
                $data.GetEnumerator()
            }
            else {
                $data
            }
        }
    }
}
tools\dbatools\functions\Test-DbaDiskSpeed.ps1
function Test-DbaDiskSpeed {
    <#
    .SYNOPSIS
        Tests how disks are performing.

    .DESCRIPTION
        Tests how disks are performing.

        This command uses a query from Rich Benner which was adapted from David Pless's article:
        https://blogs.msdn.microsoft.com/dpless/2010/12/01/leveraging-sys-dm_io_virtual_file_stats/
        https://github.com/RichBenner/PersonalCode/blob/master/Disk_Speed_Check.sql

    .PARAMETER SqlInstance
        Allows you to specify a comma separated list of servers to query.

    .PARAMETER SqlCredential
       Allows you to login to the SQL Server using alternative credentials.

    .PARAMETER Database
        The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

    .PARAMETER ExcludeDatabase
        The database(s) to exclude - this list is auto-populated from the server

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
        Author: Chrissy LeMaire
        Tags: Performance

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Test-DbaDiskSpeed

    .EXAMPLE
        Test-DbaDiskSpeed -SqlInstance sql2008, sqlserver2012
        Tests how disks are performing on sql2008 and sqlserver2012.

    .EXAMPLE
        Test-DbaDiskSpeed -SqlInstance sql2008 -Database tempdb
        Tests how disks storing tempdb files on sql2008 are performing.
    #>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [switch]$EnableException
    )

    begin {

        $sql = "SELECT  SERVERPROPERTY('MachineName') AS ComputerName,
        ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
        SERVERPROPERTY('ServerName') AS SqlInstance, db_name(a.database_id) AS [Database]
        , CAST(((a.size_on_disk_bytes/1024)/1024.0)/1024 AS DECIMAL(10,2)) AS [SizeGB]
        , RIGHT(b.physical_name, CHARINDEX('\', REVERSE(b.physical_name)) -1) AS [FileName]
        , a.file_id AS [FileID]
        , CASE WHEN a.file_id = 2 THEN 'Log' ELSE 'Data' END AS [FileType]
        , UPPER(SUBSTRING(b.physical_name, 1, 2)) AS [DiskLocation]
        , a.num_of_reads AS [Reads]
        , CASE WHEN a.num_of_reads < 1 THEN NULL ELSE CAST(a.io_stall_read_ms/(a.num_of_reads) AS INT) END AS [AverageReadStall]
        , CASE
            WHEN CASE WHEN a.num_of_reads < 1 THEN NULL ELSE CAST(a.io_stall_read_ms/(a.num_of_reads) AS INT) END < 10 THEN 'Very Good'
            WHEN CASE WHEN a.num_of_reads < 1 THEN NULL ELSE CAST(a.io_stall_read_ms/(a.num_of_reads) AS INT) END < 20 THEN 'OK'
            WHEN CASE WHEN a.num_of_reads < 1 THEN NULL ELSE CAST(a.io_stall_read_ms/(a.num_of_reads) AS INT) END < 50 THEN 'Slow, Needs Attention'
            WHEN CASE WHEN a.num_of_reads < 1 THEN NULL ELSE CAST(a.io_stall_read_ms/(a.num_of_reads) AS INT) END >= 50 THEN 'Serious I/O Bottleneck'
            END AS [ReadPerformance]
        , a.num_of_writes AS [Writes]
        , CASE WHEN a.num_of_writes < 1 THEN NULL ELSE CAST(a.io_stall_write_ms/a.num_of_writes AS INT) END AS [AverageWriteStall]
        , CASE
            WHEN CASE WHEN a.num_of_writes < 1 THEN NULL ELSE CAST(a.io_stall_write_ms/(a.num_of_writes) AS INT) END < 10 THEN 'Very Good'
            WHEN CASE WHEN a.num_of_writes < 1 THEN NULL ELSE CAST(a.io_stall_write_ms/(a.num_of_writes) AS INT) END < 20 THEN 'OK'
            WHEN CASE WHEN a.num_of_writes < 1 THEN NULL ELSE CAST(a.io_stall_write_ms/(a.num_of_writes) AS INT) END < 50 THEN 'Slow, Needs Attention'
            WHEN CASE WHEN a.num_of_writes < 1 THEN NULL ELSE CAST(a.io_stall_write_ms/(a.num_of_writes) AS INT) END >= 50 THEN 'Serious I/O Bottleneck'
            END AS [WritePerformance]
        FROM sys.dm_io_virtual_file_stats (NULL, NULL) a
        JOIN sys.master_files b
            ON a.file_id = b.file_id
            AND a.database_id = b.database_id"

        if ($Database -or $ExcludeDatabase) {
            if ($database) {
                $where = " where db_name(a.database_id) in ('$($Database -join "'")') "
            }
            if ($ExcludeDatabase) {
                $where = " where db_name(a.database_id) not in ('$($ExcludeDatabase -join "'")') "
            }
            $sql += $where
        }

        $sql += " ORDER BY (a.num_of_reads + a.num_of_writes) DESC"
    }

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            Write-Message -Level Debug -Message "Executing $sql"
            $server.Query("$sql")
        }
    }
}
tools\dbatools\functions\Test-DbaIdentityUsage.ps1
function Test-DbaIdentityUsage {
    <#
        .SYNOPSIS
            Displays information relating to IDENTITY seed usage.  Works on SQL Server 2008 and above.

        .DESCRIPTION
            IDENTITY seeds have max values based off of their data type.  This module will locate identity columns and report the seed usage.

        .PARAMETER SqlInstance
            Allows you to specify a comma separated list of servers to query.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude - this list is auto-populated from the server

        .PARAMETER Threshold
            Allows you to specify a minimum % of the seed range being utilized.  This can be used to ignore seeds that have only utilized a small fraction of the range.

        .PARAMETER ExcludeSystemDb
            Allows you to suppress output on system databases

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Brandon Abshire, netnerds.net
            Tags: Identity, Table, Column

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaIdentityUsage

        .EXAMPLE
            Test-DbaIdentityUsage -SqlInstance sql2008, sqlserver2012

            Check identity seeds for servers sql2008 and sqlserver2012.

        .EXAMPLE
            Test-DbaIdentityUsage -SqlInstance sql2008 -Database TestDB

            Check identity seeds on server sql2008 for only the TestDB database

        .EXAMPLE
            Test-DbaIdentityUsage -SqlInstance sql2008 -Database TestDB -Threshold 20

            Check identity seeds on server sql2008 for only the TestDB database, limiting results to 20% utilization of seed range or higher
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [parameter(Position = 1, Mandatory = $false)]
        [int]$Threshold = 0,
        [parameter(Position = 2, Mandatory = $false)]
        [Alias("NoSystemDb")]
        [switch]$ExcludeSystemDb,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter NoSystemDb

        $sql = ";WITH CT_DT AS
        (
            SELECT 'tinyint' AS DataType, 0 AS MinValue ,255 AS MaxValue UNION
            SELECT 'smallint' AS DataType, -32768 AS MinValue ,32767 AS MaxValue UNION
            SELECT 'int' AS DataType, -2147483648 AS MinValue ,2147483647 AS MaxValue UNION
            SELECT 'bigint' AS DataType, -9223372036854775808 AS MinValue ,9223372036854775807 AS MaxValue
        ), CTE_1
        AS
        (
          SELECT SCHEMA_NAME(o.schema_id) AS SchemaName,
                 OBJECT_NAME(a.Object_id) as TableName,
                 a.Name as ColumnName,
                 seed_value AS SeedValue,
                 CONVERT(bigint, increment_value) as IncrementValue,

                 CONVERT(bigint, ISNULL(a.last_value, seed_value)) AS LastValue,

                 (CASE
                        WHEN CONVERT(bigint, increment_value) < 0 THEN
                            (CONVERT(bigint, seed_value)
                            - CONVERT(bigint, ISNULL(last_value, seed_value))
                            + (CASE WHEN CONVERT(bigint, seed_value) <> 0 THEN ABS(CONVERT(bigint, increment_value)) ELSE 0 END))
                        ELSE
                            (CONVERT(bigint, ISNULL(last_value, seed_value))
                            - CONVERT(bigint, seed_value)
                            + (CASE WHEN CONVERT(bigint, seed_value) <> 0 THEN ABS(CONVERT(bigint, increment_value)) ELSE 0 END))
                    END) / ABS(CONVERT(bigint, increment_value))  AS NumberOfUses,

                  CAST (
                        (CASE
                            WHEN CONVERT(Numeric(20, 0), increment_value) < 0 THEN
                                ABS(CONVERT(Numeric(20, 0),dt.MinValue)
                                - CONVERT(Numeric(20, 0), seed_value)
                                - (CASE WHEN CONVERT(Numeric(20, 0), seed_value) <> 0 THEN ABS(CONVERT(Numeric(20, 0), increment_value)) ELSE 0 END))
                            ELSE
                                CONVERT(Numeric(20, 0),dt.MaxValue)
                                - CONVERT(Numeric(20, 0), seed_value)
                                + (CASE WHEN CONVERT(Numeric(20, 0), seed_value) <> 0 THEN ABS(CONVERT(Numeric(20, 0), increment_value)) ELSE 0 END)
                        END) / ABS(CONVERT(Numeric(20, 0), increment_value))
                    AS Numeric(20, 0)) AS MaxNumberRows

            FROM sys.identity_columns a
                INNER JOIN sys.objects o
                   ON a.object_id = o.object_id
                INNER JOIN sys.types As b
                     ON a.system_type_id = b.system_type_id
                INNER JOIN CT_DT dt
                     ON b.name = dt.DataType
          WHERE a.seed_value is not null
        ),
        CTE_2
        AS
        (
        SELECT SchemaName, TableName, ColumnName, CONVERT(BIGINT, SeedValue) AS SeedValue, CONVERT(BIGINT, IncrementValue) AS IncrementValue, LastValue, ABS(CONVERT(NUMERIC(20,0),MaxNumberRows)) AS MaxNumberRows, NumberOfUses,
               CONVERT(Numeric(18,2), ((CONVERT(Float, NumberOfUses) / ABS(CONVERT(Numeric(20, 0),MaxNumberRows)) * 100))) AS [PercentUsed]
          FROM CTE_1
        )
        SELECT DB_NAME() as DatabaseName, SchemaName, TableName, ColumnName, SeedValue, IncrementValue, LastValue, MaxNumberRows, NumberOfUses, [PercentUsed]
          FROM CTE_2"

        if ($Threshold -gt 0) {
            $sql += " WHERE [PercentUsed] >= " + $Threshold + " ORDER BY [PercentUsed] DESC"
        }
        else {
            $sql += " ORDER BY [PercentUsed] DESC"
        }
    }

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            $dbs = $server.Databases

            if ($Database) {
                $dbs = $dbs | Where-Object Name -In $Database
            }

            if ($ExcludeDatabase) {
                $dbs = $dbs | Where-Object Name -NotIn $ExcludeDatabase
            }

            if ($ExcludeSystemDb) {
                $dbs = $dbs | Where-Object IsSystemObject -EQ $false
            }

            foreach ($db in $dbs) {
                Write-Message -Level Verbose -Message "Processing $db on $instance"

                if ($db.IsAccessible -eq $false) {
                    Stop-Function -Message "The database $db is not accessible. Skipping." -Continue
                }

                try {
                    $results = $db.Query($sql)
                }
                catch {
                    Stop-Function -Message "Error capturing data on $db" -Target $instance -ErrorRecord $_ -Exception $_.Exception -Continue
                }

                foreach ($row in $results) {
                    if ($row.PercentUsed -eq [System.DBNull]::Value) {
                        continue
                    }

                    if ($row.PercentUsed -ge $threshold) {
                        [PSCustomObject]@{
                            ComputerName   = $server.ComputerName
                            InstanceName   = $server.ServiceName
                            SqlInstance    = $server.DomainInstanceName
                            Database       = $row.DatabaseName
                            Schema         = $row.SchemaName
                            Table          = $row.TableName
                            Column         = $row.ColumnName
                            SeedValue      = $row.SeedValue
                            IncrementValue = $row.IncrementValue
                            LastValue      = $row.LastValue
                            MaxNumberRows  = $row.MaxNumberRows
                            NumberOfUses   = $row.NumberOfUses
                            PercentUsed    = $row.PercentUsed
                        } | Select-DefaultView -Exclude MaxNumberRows, NumberOfUses
                    }
                }
            }
        }
    }
}

tools\dbatools\functions\Test-DbaJobOwner.ps1
function Test-DbaJobOwner {
    <#
        .SYNOPSIS
            Checks SQL Agent Job owners against a login to validate which jobs do not match that owner.

        .DESCRIPTION
            This function checks all SQL Agent Jobs on an instance against a SQL login to validate if that login owns those SQL Agent Jobs or not.

            By default, the function checks against 'sa' for ownership, but the user can pass a specific login if they use something else.

            Only SQL Agent Jobs that do not match this ownership will be displayed.

            Best practice reference: http://sqlmag.com/blog/sql-server-tip-assign-ownership-jobs-sysadmin-account

        .PARAMETER SqlInstance
            Specifies the SQL Server instance(s) to scan.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Job
            Specifies the job(s) to process. Options for this list are auto-populated from the server. If unspecified, all jobs will be processed.

        .PARAMETER ExcludeJob
            Specifies the job(s) to exclude from processing. Options for this list are auto-populated from the server.

        .PARAMETER Login
            Specifies the login that you wish check for ownership. This defaults to 'sa' or the sysadmin name if sa was renamed. This must be a valid security principal which exists on the target server.

        .PARAMETER Detailed
            Output all properties, will be deprecated in 1.0.0 release.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Agent, Job, Owner
            Author: Michael Fal (@Mike_Fal), http://mikefal.net

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaJobOwner

        .EXAMPLE
            Test-DbaJobOwner -SqlInstance localhost

            Returns all SQL Agent Jobs where the owner does not match 'sa'.

        .EXAMPLE
            Test-DbaJobOwner -SqlInstance localhost -ExcludeJob 'syspolicy_purge_history'

            Returns SQL Agent Jobs except for the syspolicy_purge_history job

        .EXAMPLE
            Test-DbaJobOwner -SqlInstance localhost -Login DOMAIN\account

            Returns all SQL Agent Jobs where the owner does not match DOMAIN\account. Note
            that Login must be a valid security principal that exists on the target server.
    #>
    [CmdletBinding()]
    [OutputType('System.Object[]')]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias("Jobs")]
        [object[]]$Job,
        [object[]]$ExcludeJob,
        [Alias("TargetLogin")]
        [string]$Login,
        [switch]$Detailed,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter Detailed
        #connect to the instance and set return array empty
        $return = @()
    }
    process {
        foreach ($servername in $SqlInstance) {
            #connect to the instance
            Write-Message -Level Verbose -Message "Connecting to $servername."
            $server = Connect-SqlInstance $servername -SqlCredential $SqlCredential

            #Validate login
            if ($Login -and ($server.Logins.Name) -notcontains $Login) {
                if ($SqlInstance.count -eq 1) {
                    Stop-Function -Message "Invalid login: $Login."
                    return
                }
                else {
                    Write-Message -Level Warning -Message "$Login is not a valid login on $servername. Moving on."
                    continue
                }
            }
            if ($Login -and $server.Logins[$Login].LoginType -eq 'WindowsGroup') {
                Stop-Function -Message "$Login is a Windows Group and can not be a job owner."
                return
            }

            #Sets the Default Login to sa if the Login Paramater is not set.
            if(!($PSBoundParameters.ContainsKey('Login'))){
                $Login = "sa"
            }
            #sql2000 id property is empty -force target login to 'sa' login
            if ($Login -and ( ($server.VersionMajor -lt 9) -and ([string]::IsNullOrEmpty($Login)) )) {
                $Login = "sa"
            }
            # dynamic sa name for orgs who have changed their sa name
            if ($Login -eq "sa") {
                $Login = ($server.Logins | Where-Object { $_.id -eq 1 }).Name
            }

            #Get database list. If value for -Job is passed, massage to make it a string array.
            #Otherwise, use all jobs on the instance where owner not equal to -TargetLogin
            Write-Message -Level Verbose -Message "Gathering jobs to check."
            if ($Job) {
                $jobCollection = $server.JobServer.Jobs | Where-Object { $Job -contains $_.Name }
            }
            elseif ($ExcludeJob) {
                $jobCollection = $server.JobServer.Jobs | Where-Object { $ExcludeJob -notcontains $_.Name }
            }
            else {
                $jobCollection = $server.JobServer.Jobs
            }

            #for each database, create custom object for return set.
            foreach ($j in $jobCollection) {
                Write-Message -Level Verbose -Message "Checking $j"
                $row = [ordered]@{
                    Server       = $server.Name
                    Job          = $j.Name
                    JobType      = if ($j.CategoryID -eq 1){ "Remote" } else { $j.JobType }
                    CurrentOwner = $j.OwnerLoginName
                    TargetOwner  = $Login
                    OwnerMatch   = if ($j.CategoryID -eq 1){ $true } else { $j.OwnerLoginName -eq $Login }

                }
                #add each custom object to the return array
                $return += New-Object PSObject -Property $row
            }
            if($Job){
                $results = $return
            }
            else{
                $results = $return | Where-Object {$_.OwnerMatch -eq $False}
            }
        }
    }
    end {
        #return results
            Select-DefaultView -InputObject $results -Property Server, Job, JobType, CurrentOwner, TargetOwner, OwnerMatch
    }

}
tools\dbatools\functions\Test-DbaLastBackup.ps1
function Test-DbaLastBackup {
    <#
        .SYNOPSIS
            Quickly and easily tests the last set of full backups for a server.

        .DESCRIPTION
            Restores all or some of the latest backups and performs a DBCC CHECKDB.

            1. Gathers information about the last full backups
            2. Restores the backups to the Destination with a new name. If no Destination is specified, the originating SqlServer wil be used.
            3. The database is restored as "dbatools-testrestore-$databaseName" by default, but you can change dbatools-testrestore to whatever you would like using -Prefix
            4. The internal file names are also renamed to prevent conflicts with original database
            5. A DBCC CHECKDB is then performed
            6. And the test database is finally dropped

        .PARAMETER SqlInstance
            The SQL Server to connect to. Unlike many of the other commands, you cannot specify more than one server.

        .PARAMETER Destination
            The destination server to use to test the restore. By default, the Destination will be set to the source server

            If a different Destination server is specified, you must ensure that the database backups are on a shared location

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER DestinationCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database backups to test. If -Database is not provided, all database backups will be tested.

        .PARAMETER ExcludeDatabase
            Exclude specific Database backups to test.

        .PARAMETER DataDirectory
            Specifies an alternative directory for mdfs, ndfs and so on. The command uses the SQL Server's default data directory for all restores.

        .PARAMETER LogDirectory
            Specifies an alternative directory for ldfs. The command uses the SQL Server's default log directory for all restores.

        .PARAMETER VerifyOnly
            If this switch is enabled, VERIFYONLY will be performed. An actual restore will not be executed.

        .PARAMETER NoCheck
            If this switch is enabled, DBCC CHECKDB will be skipped

        .PARAMETER NoDrop
            If this switch is enabled, the newly-created test database will not be dropped.

        .PARAMETER CopyFile
            If this switch is enabled, the backup file will be copied to the destination default backup location unless CopyPath is specified.

        .PARAMETER CopyPath
            Specifies a path relative to the SQL Server to copy backups when CopyFile is specified. If not specified will use destination default backup location. If destination SQL Server is not local, admin UNC paths will be utilized for the copy.

        .PARAMETER MaxMB
            Databases larger than this value will not be restored.

        .PARAMETER AzureCredential
            The name of the SQL Server credential on the destination instance that holds the key to the azure storage account.

        .PARAMETER IncludeCopyOnly
            If this switch is enabled, copy only backups will not be counted as a last backup.

        .PARAMETER IgnoreLogBackup
            If this switch is enabled, transaction log backups will be ignored. The restore will stop at the latest full or differential backup point.

        .PARAMETER Prefix
            The database is restored as "dbatools-testrestore-$databaseName" by default. You can change dbatools-testrestore to whatever you would like using this parameter.

       .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: DisasterRecovery, Backup, Restore

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaLastBackup

        .EXAMPLE
            Test-DbaLastBackup -SqlInstance sql2016

            Determines the last full backup for ALL databases, attempts to restore all databases (with a different name and file structure), then performs a DBCC CHECKDB.

            Once the test is complete, the test restore will be dropped.

        .EXAMPLE
            Test-DbaLastBackup -SqlInstance sql2016 -Database master

            Determines the last full backup for master, attempts to restore it, then performs a DBCC CHECKDB.

        .EXAMPLE
            Test-DbaLastBackup -SqlInstance sql2016 -Database model, master -VerifyOnly

        .EXAMPLE
            Test-DbaLastBackup -SqlInstance sql2016 -NoCheck -NoDrop

            Skips the DBCC CHECKDB check. This can help speed up the tests but makes it less tested. The test restores will remain on the server.

        .EXAMPLE
            Test-DbaLastBackup -SqlInstance sql2016 -DataDirectory E:\bigdrive -LogDirectory L:\bigdrive -MaxMB 10240

            Restores data and log files to alternative locations and only restores databases that are smaller than 10 GB.

        .EXAMPLE
            Test-DbaLastBackup -SqlInstance sql2014 -Destination sql2016 -CopyFile

            Copies the backup files for sql2014 databases to sql2016 default backup locations and then attempts restore from there.

        .EXAMPLE
            Test-DbaLastBackup -SqlInstance sql2014 -Destination sql2016 -CopyFile -CopyPath "\\BackupShare\TestRestore\"

            Copies the backup files for sql2014 databases to sql2016 default backup locations and then attempts restore from there.
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer", "Source")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [DbaInstanceParameter]$Destination,
        [object]$DestinationCredential,
        [string]$DataDirectory,
        [string]$LogDirectory,
        [string]$Prefix = "dbatools-testrestore-",
        [switch]$VerifyOnly,
        [switch]$NoCheck,
        [switch]$NoDrop,
        [switch]$CopyFile,
        [string]$CopyPath,
        [int]$MaxMB,
        [switch]$IncludeCopyOnly,
        [switch]$IgnoreLogBackup,
        [string]$AzureCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $sqlinstance) {

            if (-not $destination -or $nodestination) {
                $nodestination = $true
                $destination = $instance
                $DestinationCredential = $SqlCredential
            }

            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $sourceserver = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            try {
                Write-Message -Level Verbose -Message "Connecting to $destination."
                $destserver = Connect-SqlInstance -SqlInstance $destination -SqlCredential $DestinationCredential
            }
            catch {
                Stop-Function -Message "Failed to connect to: $destination." -Target $destination -Continue
            }

            if ($destserver.VersionMajor -lt $sourceserver.VersionMajor) {
                Stop-Function -Message "$Destination is a lower version than $instance. Backups would be incompatible." -Continue
            }

            if ($destserver.VersionMajor -eq $sourceserver.VersionMajor -and $destserver.VersionMinor -lt $sourceserver.VersionMinor) {
                Stop-Function -Message "$Destination is a lower version than $instance. Backups would be incompatible." -Continue
            }

            if ($CopyPath) {
                $testpath = Test-DbaSqlPath -SqlInstance $destserver -Path $CopyPath
                if (!$testpath) {
                    Stop-Function -Message "$destserver cannot access $CopyPath." -Continue
                }
            }
            else {
                # If not CopyPath is specified, use the destination server default backup directory
                $copyPath = $destserver.BackupDirectory
            }

            if ($instance -ne $destination -and !$CopyFile) {
                $sourcerealname = $sourceserver.ComputerNetBiosName
                $destrealname = $destserver.ComputerNetBiosName

                if ($BackupFolder) {
                    if ($BackupFolder.StartsWith("\\") -eq $false -and $sourcerealname -ne $destrealname) {
                        Stop-Function -Message "Backup folder must be a network share if the source and destination servers are not the same." -Continue
                    }
                }
            }

            $source = $sourceserver.DomainInstanceName
            $destination = $destserver.DomainInstanceName

            if ($datadirectory) {
                if (!(Test-DbaSqlPath -SqlInstance $destserver -Path $datadirectory)) {
                    $serviceaccount = $destserver.ServiceAccount
                    Stop-Function -Message "Can't access $datadirectory Please check if $serviceaccount has permissions." -Continue
                }
            }
            else {
                $datadirectory = Get-SqlDefaultPaths -SqlInstance $destserver -FileType mdf
            }

            if ($logdirectory) {
                if (!(Test-DbaSqlPath -SqlInstance $destserver -Path $logdirectory)) {
                    $serviceaccount = $destserver.ServiceAccount
                    Stop-Function -Message "$Destination can't access its local directory $logdirectory. Please check if $serviceaccount has permissions." -Continue
                }
            }
            else {
                $logdirectory = Get-SqlDefaultPaths -SqlInstance $destserver -FileType ldf
            }

            if ((Test-Bound "AzureCredential") -and (Test-Bound "CopyFile")) {
                Stop-Function -Message "Cannot use copyfile with Azure backups, set to false." -continue
                $CopyFile = $false
            }

            if (!$Database) {
                $database = $sourceserver.databases.Name | Where-Object Name -ne 'tempdb'
            }

            if ($ExcludeDatabase) {
                $database = $database | Where-Object { $_ -notin $ExcludeDatabase }
            }

            if ($Database -or $ExcludeDatabase) {
                $dblist = $database

                Write-Message -Level Verbose -Message "Getting recent backup history for $instance."

                foreach ($dbname in $dblist) {
                    if ($dbname -eq 'tempdb') {
                        Write-Message -Level Verbose -Message "Skipping tempdb."
                        continue
                    }

                    Write-Message -Level Verbose -Message "Processing $dbname."

                    $copysuccess = $true
                    $db = $sourceserver.databases[$dbname]

                    # The db check is needed when the number of databases exceeds 255, then it's no longer auto-populated
                    if (!$db) {
                        Stop-Function -Message "$dbname does not exist on $source." -Continue
                    }

                    if (Test-Bound "IgnoreLogBackup") {
                        Write-Message -Level Verbose -Message "Skipping Log backups as requested."
                        $lastbackup = @()
                        $lastbackup += $full = Get-DbaBackupHistory -SqlInstance $sourceserver -Database $dbname -IncludeCopyOnly:$IncludeCopyOnly -LastFull #-raw
                        $diff = Get-DbaBackupHistory -SqlInstance $sourceserver -Database $dbname -IncludeCopyOnly:$IncludeCopyOnly -LastDiff # -raw
                        if ($full.start -le $diff.start) {
                            $lastbackup += $diff
                        }
                    }
                    else {
                        $lastbackup = Get-DbaBackupHistory -SqlInstance $sourceserver -Database $dbname -IncludeCopyOnly:$IncludeCopyOnly -Last #-raw
                    }

                    if ($null -eq $lastbackup) {
                        Write-Message -Level Verbose -Message "No backups exist for this database."
                        $lastbackup = @{ Path = "No backups exist for this database" }
                        $fileexists = $false
                        $success = $restoreresult = $dbccresult = "Skipped"
                        continue
                    }

                    if ($CopyFile) {
                        try {
                            Write-Message -Level Verbose -Message "Gathering information for file copy."
                            $removearray = @()

                            foreach ($backup in $lastbackup) {
                                foreach ($file in $backup) {
                                    $filename = Split-Path -Path $file.FullName -Leaf
                                    Write-Message -Level Verbose -Message "Processing $filename."

                                    $sourcefile = Join-AdminUnc -servername $instance.ComputerName -filepath $file.Path

                                    if ($instance.IsLocalHost) {
                                        $remotedestdirectory = Join-AdminUnc -servername $instance.ComputerName -filepath $copyPath
                                    }
                                    else {
                                        $remotedestdirectory = $copyPath
                                    }

                                    $remotedestfile = "$remotedestdirectory\$filename"
                                    $localdestfile = "$copyPath\$filename"
                                    Write-Message -Level Verbose -Message "Destination directory is $destdirectory."
                                    Write-Message -Level Verbose -Message "Destination filename is $remotedestfile."

                                    try {
                                        Write-Message -Level Verbose -Message "Copying $sourcefile to $remotedestfile."
                                        Copy-Item -Path $sourcefile -Destination $remotedestfile -ErrorAction Stop
                                        $backup.Path = $localdestfile
                                        $backup.FullName = $localdestfile
                                        $removearray += $remotedestfile
                                    }
                                    catch {
                                        $backup.Path = $sourcefile
                                        $backup.FullName = $sourcefile
                                    }
                                }
                            }
                            $copysuccess = $true
                        }
                        catch {
                            Write-Message -Level Warning -Message "Failed to copy backups for $dbname on $instance to $destdirectory - $_."
                            $copysuccess = $false
                        }
                    }
                    if (!$copysuccess) {
                        Write-Message -Level Verbose -Message "Failed to copy backups."
                        $lastbackup = @{ Path = "Failed to copy backups" }
                        $fileexists = $false
                        $success = $restoreresult = $dbccresult = "Skipped"
                    }
                    elseif (!($lastbackup | Where-Object { $_.type -eq 'Full' })) {
                        Write-Message -Level Verbose -Message "No full backup returned from lastbackup."
                        $lastbackup = @{ Path = "Not found" }
                        $fileexists = $false
                        $success = $restoreresult = $dbccresult = "Skipped"
                    }
                    elseif ($source -ne $destination -and $lastbackup[0].Path.StartsWith('\\') -eq $false -and !$CopyFile) {
                        Write-Message -Level Verbose -Message "Path not UNC and source does not match destination. Use -CopyFile to move the backup file."
                        $fileexists = $dbccresult = "Skipped"
                        $success = $restoreresult = "Restore not located on shared location"
                    }
                    elseif (($lastbackup[0].Path | ForEach-Object { Test-DbaSqlPath -SqlInstance $destserver -Path $_ }) -eq $false) {
                        Write-Message -Level Verbose -Message "SQL Server cannot find backup."
                        $fileexists = $false
                        $success = $restoreresult = $dbccresult = "Skipped"
                    }
                    if ($restoreresult -ne "Skipped" -or $lastbackup[0].Path -like 'http*') {
                        Write-Message -Level Verbose -Message "Looking good!"

                        $fileexists = $true
                        $ogdbname = $dbname
                        $restorelist = Read-DbaBackupHeader -SqlInstance $destserver -Path $lastbackup[0].Path -AzureCredential $AzureCredential
                        $mb = $restorelist.BackupSizeMB

                        if ($MaxMB -gt 0 -and $MaxMB -lt $mb) {
                            $success = "The backup size for $dbname ($mb MB) exceeds the specified maximum size ($MaxMB MB)."
                            $dbccresult = "Skipped"
                        }
                        else {
                            $dbccElapsed = $restoreElapsed = $startRestore = $endRestore = $startDbcc = $endDbcc = $null

                            $dbname = "$prefix$dbname"
                            $destdb = $destserver.databases[$dbname]

                            if ($destdb) {
                                Stop-Function -Message "$dbname already exists on $destination - skipping." -Continue
                            }

                            if ($Pscmdlet.ShouldProcess($destination, "Restoring $ogdbname as $dbname.")) {
                                Write-Message -Level Verbose -Message "Performing restore."
                                $startRestore = Get-Date
                                if ($verifyonly) {
                                    $restoreresult = $lastbackup | Restore-DbaDatabase -SqlInstance $destserver -RestoredDatabaseNamePrefix $prefix -DestinationFilePrefix $Prefix -DestinationDataDirectory $datadirectory -DestinationLogDirectory $logdirectory -VerifyOnly:$VerifyOnly -IgnoreLogBackup:$IgnoreLogBackup -AzureCredential $AzureCredential -TrustDbBackupHistory
                                }
                                else {
                                    $restoreresult = $lastbackup | Restore-DbaDatabase -SqlInstance $destserver -RestoredDatabaseNamePrefix $prefix -DestinationFilePrefix $Prefix -DestinationDataDirectory $datadirectory -DestinationLogDirectory $logdirectory -IgnoreLogBackup:$IgnoreLogBackup -AzureCredential $AzureCredential -TrustDbBackupHistory
                                    Write-verbose " Restore-DbaDatabase -SqlInstance $destserver -RestoredDatabaseNamePrefix $prefix -DestinationFilePrefix $Prefix -DestinationDataDirectory $datadirectory -DestinationLogDirectory $logdirectory -IgnoreLogBackup:$IgnoreLogBackup -AzureCredential $AzureCredential -TrustDbBackupHistory"

                                }

                                $endRestore = Get-Date
                                $restorets = New-TimeSpan -Start $startRestore -End $endRestore
                                $ts = [timespan]::fromseconds($restorets.TotalSeconds)
                                $restoreElapsed = "{0:HH:mm:ss}" -f ([datetime]$ts.Ticks)

                                if ($restoreresult.RestoreComplete -eq $true) {
                                    $success = "Success"
                                }
                                else {
                                    $success = "Failure"
                                }
                            }

                            $destserver = Connect-SqlInstance -SqlInstance $destination -SqlCredential $DestinationCredential

                            if (!$NoCheck -and !$VerifyOnly) {
                                # shouldprocess is taken care of in Start-DbccCheck
                                if ($ogdbname -eq "master") {
                                    $dbccresult = "DBCC CHECKDB skipped for restored master ($dbname) database."
                                }
                                else {
                                    if ($success -eq "Success") {
                                        Write-Message -Level Verbose -Message "Starting DBCC."

                                        $startDbcc = Get-Date
                                        $dbccresult = Start-DbccCheck -Server $destserver -DbName $dbname 3>$null
                                        $endDbcc = Get-Date

                                        $dbccts = New-TimeSpan -Start $startDbcc -End $endDbcc
                                        $ts = [timespan]::fromseconds($dbccts.TotalSeconds)
                                        $dbccElapsed = "{0:HH:mm:ss}" -f ([datetime]$ts.Ticks)
                                    }
                                    else {
                                        $dbccresult = "Skipped"
                                    }
                                }
                            }

                            if ($VerifyOnly) {
                                $dbccresult = "Skipped"
                            }

                            if (!$NoDrop -and $null -ne $destserver.databases[$dbname]) {
                                if ($Pscmdlet.ShouldProcess($dbname, "Dropping Database $dbname on $destination")) {
                                    Write-Message -Level Verbose -Message "Dropping database."

                                    ## Drop the database
                                    try {
                                        $removeresult = Remove-DbaDatabase -SqlInstance $destserver -Database $dbname -Confirm:$false
                                        Write-Message -Level Verbose -Message "Dropped $dbname Database on $destination."
                                    }
                                    catch {
                                        $destserver.Databases.Refresh()
                                        if ($destserver.databases[$dbname]) {
                                            Write-Message -Level Warning -Message "Failed to Drop database $dbname on $destination."
                                        }
                                    }
                                }
                            }

                            #Cleanup BackupFiles if -CopyFile and backup was moved to destination
                            if ($CopyFile) {
                                Write-Message -Level Verbose -Message "Removing copied backup file from $destination."
                                try {
                                    $removearray | Remove-item -ErrorAction Stop
                                }
                                catch {
                                    Write-Message -Level Warning -Message $_ -ErrorRecord $_ -Target $instance
                                }
                            }

                            $destserver.Databases.Refresh()
                            if ($destserver.Databases[$dbname] -and !$NoDrop) {
                                Write-Message -Level Warning -Message "$dbname was not dropped."
                            }
                        }
                    }

                    if ($Pscmdlet.ShouldProcess("console", "Showing results")) {
                        [pscustomobject]@{
                            SourceServer   = $source
                            TestServer     = $destination
                            Database       = $db.name
                            FileExists     = $fileexists
                            Size           = [dbasize](($lastbackup.TotalSize | Measure-Object -Sum).Sum)
                            RestoreResult  = $success
                            DbccResult     = $dbccresult
                            RestoreStart   = [dbadatetime]$startRestore
                            RestoreEnd     = [dbadatetime]$endRestore
                            RestoreElapsed = $restoreElapsed
                            DbccStart      = [dbadatetime]$startDbcc
                            DbccEnd        = [dbadatetime]$endDbcc
                            DbccElapsed    = $dbccElapsed
                            BackupDate     = $lastbackup.Start
                            BackupFiles    = $lastbackup.FullName
                        }
                    }
                }
            }
        }
    }
}
tools\dbatools\functions\Test-DbaLinkedServerConnection.ps1
function Test-DbaLinkedServerConnection {
    <#
        .SYNOPSIS
            Test all linked servers from the sql servers passed

        .DESCRIPTION
            Test each linked server on the instance

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to.

        .PARAMETER SqlCredential
            Credential object used to connect to the SQL Server as a different user

        .PARAMETER EnableException
                By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.

                This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
                Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: LinkedServer
            Author: Thomas LaRock ( https://thomaslarock.com )

            dbatools PowerShell module (https://dbatools.io)
            Copyright (C) 2017 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaLinkedServerConnection

        .EXAMPLE
            Test-DbaLinkedServerConnection -SqlInstance DEV01

            Test all Linked Servers for the SQL Server instance DEV01

        .EXAMPLE
            Test-DbaLinkedServerConnection -SqlInstance sql2016 | Out-File C:\temp\results.txt

            Test all Linked Servers for the SQL Server instance sql2016 and output results to file

        .EXAMPLE
            Test-DbaLinkedServerConnection -SqlInstance sql2016, sql2014, sql2012

            Test all Linked Servers for the SQL Server instances sql2016, sql2014 and sql2012

        .EXAMPLE
            $servers = "sql2016","sql2014","sql2012"
            $servers | Test-DbaLinkedServerConnection -SqlCredential (Get-Credential sqladmin)

            Test all Linked Servers for the SQL Server instances sql2016, sql2014 and sql2012 using SQL login credentials

        .EXAMPLE
            $servers | Get-DbaLinkedServer | Test-DbaLinkedServerConnection

            Test all Linked Servers for the SQL Server instances sql2016, sql2014 and sql2012
    #>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            if ($instance.LinkedLive) {
                $linkedServerCollection = $instance.LinkedServer
            }
            else {
                try {
                    Write-Message -Level Verbose -Message "Connecting to $instance"
                    $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
                }
                catch {
                    Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                }
            }

            $linkedServerCollection = $server.LinkedServers

            foreach ($ls in $linkedServerCollection) {
                Write-Message -Level Verbose -Message "Testing linked server $($ls.name) on server $($ls.parent.name)"
                try {
                    $null = $ls.TestConnection()
                    $result = "Success"
                    $connectivity = $true
                }
                catch {
                    $result = $_.Exception.InnerException.InnerException.Message
                    $connectivity = $false
                }

                New-Object Sqlcollaborative.Dbatools.Validation.LinkedServerResult($ls.parent.ComputerName, $ls.parent.ServiceName, $ls.parent.DomainInstanceName, $ls.Name, $ls.DataSource, $connectivity, $result)
            }
        }
    }
}
tools\dbatools\functions\Test-DbaLoginPassword.ps1
function Test-DbaLoginPassword {
    <#
        .SYNOPSIS
            Test-DbaLoginPassword finds any logins on SQL instance that are SQL Logins and have a password that is either null or same as the login

        .DESCRIPTION
            The purpose of this function is to find SQL Server logins that have no password or the same password as login. You can add your own password to check for or add them to a csv file.
            By default it will test for empty password and the same password as username.

        .PARAMETER SqlInstance
            The SQL Server instance you're checking logins on. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SqlCredential
            Allows you to login to servers using SQL Logins instead of Windows Authentication (AKA Integrated or Trusted). To use:

            $scred = Get-Credential, then pass $scred object to the -SqlCredential parameter.

            Windows Authentication will be used if SqlCredential is not specified. SQL Server does not accept Windows credentials being passed as credentials.

            To connect as a different Windows user, run PowerShell as that user.

        .PARAMETER Dictionary
            Specifies a list of passwords to include in the test for weak passwords.

        .PARAMETER Login
            The login(s) to process.
    
        .PARAMETER InputObject
            Allows piping from Get-DbaLogin.
    
        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Peter Samuelsson
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaLoginPassword

        .EXAMPLE
            Test-DbaLoginPassword -SqlInstance Dev01

            Test all SQL logins that the password is null or same as username on SQL server instance Dev01

        .EXAMPLE
            Test-DbaLoginPassword -SqlInstance Dev01 -Login sqladmin

            Test the 'sqladmin' SQL login that the password is null or same as username on SQL server instance Dev01

        .EXAMPLE
            Test-DbaLoginPassword -SqlInstance Dev01 -Dictionary Test1,test2

            Test all SQL logins that the password is null, same as username or Test1,Test2 on SQL server instance Dev0

        .EXAMPLE
            Get-DbaLogin -SqlInstance "sql2017","sql2016" | Test-DbaLoginPassword

            Test all logins on sql2017 and sql2016

        .EXAMPLE
            $servers | Get-DbaLogin | Out-GridView -Passthru | Test-DbaLoginPassword

            Test selected logins on all servers in the $servers variable
    #>
    [CmdletBinding()]
    Param (
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [String[]]$Login,
        [String[]]$Dictionary,
        [Parameter(ValueFromPipeline)]
        [Microsoft.SqlServer.Management.Smo.Login[]]$InputObject,
        [switch]$EnableException
    )

    begin {
        $CheckPasses = "''", "'@@Name'"
        if ($Dictionary) {
            $Dictionary | ForEach-Object { $CheckPasses += "'" + $psitem + "'" }
        }

        foreach ($CheckPass in $CheckPasses) {
            if ($CheckPasses.IndexOf($CheckPass) -eq 0) {
                $checks = "SELECT " + $CheckPass
            }
            else {
                $checks += "
        UNION SELECT " + $CheckPass
            }
        }

        $sql = "DECLARE @WeakPwdList TABLE(WeakPwd NVARCHAR(255))
            --Define weak password list
            --Use @@Name if users password contain their name
            INSERT INTO @WeakPwdList(WeakPwd)
            $checks

            SELECT SERVERPROPERTY('MachineName') AS [ComputerName],
                ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
                SERVERPROPERTY('ServerName') AS [SqlInstance],
                SysLogins.name as SqlLogin,
                WeakPassword = 'True',
                REPLACE(WeakPassword.WeakPwd,'@@Name',SysLogins.name) As [Password],
                SysLogins.is_disabled as Disabled,
                SysLogins.create_date as CreatedDate,
                SysLogins.modify_date as ModifiedDate,
                SysLogins.default_database_name as DefaultDatabase
            FROM sys.sql_logins SysLogins
            INNER JOIN @WeakPwdList WeakPassword ON (PWDCOMPARE(WeakPassword.WeakPwd, password_hash) = 1
                OR PWDCOMPARE(REPLACE(WeakPassword.WeakPwd,'@@Name',SysLogins.name),password_hash) = 1)"
    }
    process {
        foreach ($instance in $SqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential -MinimumVersion 10
                Write-Message -Message "Connected to: $instance." -Level Verbose
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
            $InputObject += Get-DbaLogin -SqlInstance $server -Login $Login
        }

        $logins += $InputObject
    }
    end {
        $servers = $logins | Select-Object -Unique -ExpandProperty Parent
        $names = $logins | Select-Object -Unique -ExpandProperty Name

        foreach ($serverinstance in $servers) {
            Write-Message -Level Debug -Message "Executing $sql"
            Write-Message -Level Verbose -Message "Testing: same username as Password"
            Write-Message -Level Verbose -Message "Testing: the following Passwords $CheckPasses"
            try {
                $serverinstance.Query("$sql") | Where-Object SqlLogin -in $names
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_ -Target $serverinstance -Continue
            }
        }
    }
}
tools\dbatools\functions\Test-DbaLogShippingStatus.ps1
function Test-DbaLogShippingStatus {
    <#
        .SYNOPSIS
            Test-DbaLogShippingStatus returns the status of your log shipping databases

        .DESCRIPTION
            Most of the time your log shipping "just works".
            Checking your log shipping status can be done really easy with this function.

            Make sure you're connecting to the monitoring instance of your log shipping infrastructure.

            The function will return the status for a database. This can be one or more messages in a comma separated list.
            If everything is OK with the database than you should only see the message "All OK".

        .PARAMETER SqlInstance
            SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Allows you to filter the results to only return the databases you're interested in. This can be one or more values separated by commas.
            This is not a wildcard and should be the exact database name. See examples for more info.

        .PARAMETER ExcludeDatabase
            Allows you to filter the results to only return the databases you're not interested in. This can be one or more values separated by commas.
            This is not a wildcard and should be the exact database name.

        .PARAMETER Primary
            Allows to filter the results to only return values that apply to the primary instance.

        .PARAMETER Secondary
            Allows to filter the results to only return values that apply to the secondary instance.

        .PARAMETER Simple
            By default all the information will be returned.
            If this parameter is used you get an overview with the SQL Instance, Database, Instance Type and the status

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: LogShipping
            Author: Sander Stad (@sqlstad, sqlstad.nl)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaLogShippingStatus

        .EXAMPLE
            Test-DbaLogShippingStatus -SqlInstance sql1

            Retrieves the log ship information from sql1 and displays all the information present including the status.

        .EXAMPLE
            Test-DbaLogShippingStatus -SqlInstance sql1 -Database AdventureWorks2014

            Retrieves the log ship information for just the database AdventureWorks.

        .EXAMPLE
            Test-DbaLogShippingStatus -SqlInstance sql1 -Primary

            Retrieves the log ship information and only returns the information for the databases on the primary instance.

        .EXAMPLE
            Test-DbaLogShippingStatus -SqlInstance sql1 -Secondary

            Retrieves the log ship information and only returns the information for the databases on the secondary instance.

        .EXAMPLE
            Test-DbaLogShippingStatus -SqlInstance sql1 -Simple

            Retrieves the log ship information and only returns the columns SQL Instance, Database, Instance Type and Status
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$Database,
        [string[]]$ExcludeDatabase,
        [switch]$Simple,
        [switch]$Primary,
        [switch]$Secondary,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {

        # Create array list to hold the results
        $collection = New-Object System.Collections.ArrayList

        # Setup the query
        [string[]]$query = "
IF ( OBJECT_ID('tempdb..#logshippingstatus') ) IS NOT NULL
BEGIN
DROP TABLE #logshippingstatus;
END;

CREATE TABLE #logshippingstatus
(
    Status BIT ,
    IsPrimary BIT ,
    Server VARCHAR(100) ,
    DatabaseName VARCHAR(100) ,
    TimeSinceLastBackup INT ,
    LastBackupFile VARCHAR(255) ,
    BackupThresshold INT ,
    IsBackupAlertEnabled BIT ,
    TimeSinceLastCopy INT ,
    LastCopiedFile VARCHAR(255) ,
    TimeSinceLastRestore INT ,
    LastRestoredFile VARCHAR(255) ,
    LastRestoredLatency INT ,
    RestoreThresshold INT ,
    IsRestoreAlertEnabled BIT
);

INSERT INTO #logshippingstatus
(   Status ,
    IsPrimary ,
    Server ,
    DatabaseName ,
    TimeSinceLastBackup ,
    LastBackupFile ,
    BackupThresshold ,
    IsBackupAlertEnabled ,
    TimeSinceLastCopy ,
    LastCopiedFile ,
    TimeSinceLastRestore ,
    LastRestoredFile ,
    LastRestoredLatency ,
    RestoreThresshold ,
    IsRestoreAlertEnabled
)
EXEC master.sys.sp_help_log_shipping_monitor"

        $select = "SELECT * FROM #logshippingstatus"

        if ($Database -or $ExcludeDatabase) {

            if ($database) {
                $where += "DatabaseName IN ('$($Database -join ''',''')')"
            }
            elseif ($ExcludeDatabase) {
                $where += "DatabaseName NOT IN ('$($ExcludeDatabase -join ''',''')')"
            }

            $select = "$select WHERE $where"
        }

        $query += $select
        $query += "DROP TABLE #logshippingstatus"
        $sql = $query -join ";`n"
        Write-Message -level Debug -Message $sql
    }

    process {
        foreach ($instance in $sqlinstance) {
            # Try connecting to the instance
            Write-Message -Message "Connecting to $instance" -Level Verbose
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($server.EngineEdition -match "Express") {
                Write-Message -Level Warning -Message "$instance is Express Edition which does not support Log Shipping"
                continue
            }

            # Check the variables
            if ($Primary -and $Secondary) {
                Stop-Function -Message "Invalid parameter combination. Please enter either -Primary or -Secondary" -Target $instance -Continue
            }

            # Get the log shipped databases
            $results = $server.Query($sql)

            # Check if any rows were returned
            if ($results.Count -lt 1) {
                Stop-Function -Message "No information available about any log shipped databases for $instance. Please check the instance name." -Target $instance -Continue
            }

            # Filter the results
            if ($Primary) {
                $results = $results | Where-Object { $_.IsPrimary -eq $true }
            }

            if ($Secondary) {
                $results = $results | Where-Object { $_.IsPrimary -eq $false }
            }

            # Loop through each of the results
            foreach ($result in $results) {

                # Setup a variable to hold the errors
                $statusDetails = @()

                # Check if there are any results that need to be returned
                if ($result.Status -notin 0, 1) {
                    $statusDetails += "N/A"
                }
                else {
                    # Check the status of the row is true which indicates that something is wrong
                    if ($result.Status) {
                        # Check if the row is part of the primary or secondary instance
                        if ($result.IsPrimary) {
                            # Check the backup
                            if (-not $result.TimeSinceLastBackup) {
                                $statusDetails += "The backup has never been executed."
                            }
                            elseif ($result.TimeSinceLastBackup -ge $result.BackupThresshold) {
                                $statusDetails += "The backup has not been executed in the last $($result.BackupThresshold) minutes"
                            }
                        }
                        elseif (-not $result.IsPrimary) {
                            # Check the restore
                            if ($null -eq $result.TimeSinceLastRestore) {
                                $statusDetails += "The restore has never been executed."
                            }
                            elseif ($result.TimeSinceLastRestore -ge $result.RestoreThresshold) {
                                $statusDetails += "The restore has not been executed in the last $($result.RestoreThresshold) minutes"
                            }
                        }
                    }
                    else {
                        $statusDetails += "All OK"
                    }


                    # Check the time for the backup, copy and restore
                    if ($result.TimeSinceLastBackup -eq [DBNull]::Value) {
                        $lastBackup = "N/A"
                    }
                    else {
                        $lastBackup = (Get-Date).AddMinutes( - $result.TimeSinceLastBackup)
                    }

                    if ($result.TimeSinceLastCopy -eq [DBNull]::Value) {
                        $lastCopy = "N/A"
                    }
                    else {
                        $lastCopy = (Get-Date).AddMinutes( - $result.TimeSinceLastCopy)
                    }

                    if ($result.TimeSinceLastRestore -eq [DBNull]::Value) {
                        $lastRestore = "N/A"
                    }
                    else {
                        $lastRestore = (Get-Date).AddMinutes( - $result.TimeSinceLastRestore)
                    }
                }

                # Set up the custom object
                $null = $collection.Add([PSCustomObject]@{
                        ComputerName          = $server.ComputerName
                        InstanceName          = $server.ServiceName
                        SqlInstance           = $server.DomainInstanceName
                        Database              = $result.DatabaseName
                        InstanceType          = switch ($result.IsPrimary) { $true { "Primary Instance" } $false { "Secondary Instance" } }
                        TimeSinceLastBackup   = $lastBackup
                        LastBackupFile        = $result.LastBackupFile
                        BackupThresshold      = $result.BackupThresshold
                        IsBackupAlertEnabled  = $result.IsBackupAlertEnabled
                        TimeSinceLastCopy     = $lastCopy
                        LastCopiedFile        = $result.LastCopiedFile
                        TimeSinceLastRestore  = $lastRestore
                        LastRestoredFile      = $result.LastRestoredFile
                        LastRestoredLatency   = $result.LastRestoredLatency
                        RestoreThresshold     = $result.RestoreThresshold
                        IsRestoreAlertEnabled = $result.IsRestoreAlertEnabled
                        Status                = $statusDetails -join ","
                    })

            }

            if ($Simple) {
                return $collection | Select-Object SqlInstance, Database, InstanceType, Status
            }
            else {
                return $collection
            }
        }
    }
}
tools\dbatools\functions\Test-DbaMaxDop.ps1
function Test-DbaMaxDop {
    <#
        .SYNOPSIS
            Displays information relating to SQL Server Max Degree of Parallelism setting. Works on SQL Server 2005-2016.

        .DESCRIPTION
            Inspired by Sakthivel Chidambaram's post about SQL Server MAXDOP Calculator (https://blogs.msdn.microsoft.com/sqlsakthi/p/maxdop-calculator-SqlInstance/),
            this script displays a SQL Server's: max dop configured, and the calculated recommendation.

            For SQL Server 2016 shows:
                - Instance max dop configured and the calculated recommendation
                - max dop configured per database (new feature)

            More info:
                https://support.microsoft.com/en-us/kb/2806535
                https://blogs.msdn.microsoft.com/sqlsakthi/2012/05/23/wow-we-have-maxdop-calculator-for-sql-server-it-makes-my-job-easier/

            These are just general recommendations for SQL Server and are a good starting point for setting the "max degree of parallelism" option.

        .PARAMETER SqlInstance
            The SQL Server instance(s) to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Detailed
            Output all properties, will be deprecated in 1.0.0 release.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: MaxDop, SpConfigure
            Author  : Claudio Silva (@claudioessilva)
            Requires: sysadmin access on SQL Servers

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaMaxDop

        .EXAMPLE
            Test-DbaMaxDop -SqlInstance sql2008, sqlserver2012

            Get Max DOP setting for servers sql2008 and sqlserver2012 and also the recommended one.

        .EXAMPLE
            Test-DbaMaxDop -SqlInstance sql2014 | Select-Object *

            Shows Max DOP setting for server sql2014 with the recommended value. Piping the output to Select-Object * will also show the 'NUMANodes' and 'NumberOfCores' of each instance

        .EXAMPLE
            Test-DbaMaxDop -SqlInstance sqlserver2016 | Select-Object *

            Get Max DOP setting for servers sql2016 with the recommended value. Piping the output to Select-Object * will also show the 'NUMANodes' and 'NumberOfCores' of each instance. Because it is an 2016 instance will be shown 'InstanceVersion', 'Database' and 'DatabaseMaxDop' columns.
    #>
    [CmdletBinding()]
    [OutputType([System.Collections.ArrayList])]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [switch]$Detailed,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter Detailed

        $notesDopLT = "Before changing MaxDop, consider that the lower value may have been intentionally set."
        $notesDopGT = "Before changing MaxDop, consider that the higher value may have been intentionally set."
        $notesDopZero = "This is the default setting. Consider using the recommended value instead."
        $notesDopOne = "Some applications like SharePoint, Dynamics NAV, SAP, BizTalk has the need to use MAXDOP = 1. Please confirm that your instance is not supporting one of these applications prior to changing the MaxDop."
        $notesAsRecommended = "Configuration is as recommended."
    }

    process {
        $hasScopedConfig = $false

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance"
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            #Get current configured value
            $maxDop = $server.Configuration.MaxDegreeOfParallelism.ConfigValue

            try {
                #represents the Number of NUMA nodes
                $sql = "SELECT COUNT(DISTINCT memory_node_id) AS NUMA_Nodes FROM sys.dm_os_memory_clerks WHERE memory_node_id!=64"
                $numaNodes = $server.ConnectionContext.ExecuteScalar($sql)
            }
            catch {
                Stop-Function -Message "Failed to get Numa node count." -ErrorRecord $_ -Target $server -Continue
            }

            try {
                #represents the Number of Processor Cores
                $sql = "SELECT COUNT(scheduler_id) FROM sys.dm_os_schedulers WHERE status = 'VISIBLE ONLINE'"
                $numberOfCores = $server.ConnectionContext.ExecuteScalar($sql)
            }
            catch {
                Stop-Function -Message "Failed to get number of cores." -ErrorRecord $_ -Target $server -Continue
            }

            #Calculate Recommended Max Dop to instance
            #Server with single NUMA node
            if ($numaNodes -eq 1) {
                if ($numberOfCores -lt 8) {
                    #Less than 8 logical processors - Keep MAXDOP at or below # of logical processors
                    $recommendedMaxDop = $numberOfCores
                }
                else {
                    #Equal or greater than 8 logical processors - Keep MAXDOP at 8
                    $recommendedMaxDop = 8
                }
            }
            else {
                #Server with multiple NUMA nodes
                if (($numberOfCores / $numaNodes) -lt 8) {
                    # Less than 8 logical processors per NUMA node - Keep MAXDOP at or below # of logical processors per NUMA node
                    $recommendedMaxDop = [int]($numberOfCores / $numaNodes)
                }
                else {
                    # Greater than 8 logical processors per NUMA node - Keep MAXDOP at 8
                    $recommendedMaxDop = 8
                }
            }

            #Setting notes for instance max dop value
            $notes = $null
            if ($maxDop -eq 1) {
                $notes = $notesDopOne
            }
            else {
                if ($maxDop -ne 0 -and $maxDop -lt $recommendedMaxDop) {
                    $notes = $notesDopLT
                }
                else {
                    if ($maxDop -ne 0 -and $maxDop -gt $recommendedMaxDop) {
                        $notes = $notesDopGT
                    }
                    else {
                        if ($maxDop -eq 0) {
                            $notes = $notesDopZero
                        }
                        else {
                            $notes = $notesAsRecommended
                        }
                    }
                }
            }

            [pscustomobject]@{
                ComputerName          = $server.ComputerName
                InstanceName          = $server.ServiceName
                SqlInstance           = $server.DomainInstanceName
                InstanceVersion       = $server.Version
                Database              = "N/A"
                DatabaseMaxDop        = "N/A"
                CurrentInstanceMaxDop = $maxDop
                RecommendedMaxDop     = $recommendedMaxDop
                NUMANodes             = $numaNodes
                NumberOfCores         = $numberOfCores
                Notes                 = $notes
            } | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, Database, DatabaseMaxDop, CurrentInstanceMaxDop, RecommendedMaxDop, Notes

            # On SQL Server 2016 and higher, MaxDop can be set on a per-database level
            if ($server.VersionMajor -ge 13) {
                $hasScopedConfig = $true
                Write-Message -Level Verbose -Message "SQL Server 2016 or higher detected, checking each database's MaxDop."

                $databases = $server.Databases | where-object {$_.IsSystemObject -eq $false}

                foreach ($database in $databases) {
                    if ($database.IsAccessible -eq $false) {
                        Write-Message -Level Verbose -Message "Database $database is not accessible."
                        continue
                    }
                    Write-Message -Level Verbose -Message "Checking database '$($database.Name)'."

                    $dbmaxdop = $database.MaxDop

                    [pscustomobject]@{
                        ComputerName          = $server.ComputerName
                        InstanceName          = $server.ServiceName
                        SqlInstance           = $server.DomainInstanceName
                        InstanceVersion       = $server.Version
                        Database              = $database.Name
                        DatabaseMaxDop        = $dbmaxdop
                        CurrentInstanceMaxDop = $maxDop
                        RecommendedMaxDop     = $recommendedMaxDop
                        NUMANodes             = $numaNodes
                        NumberOfCores         = $numberOfCores
                        Notes                 = if ($dbmaxdop -eq 0) { "Will use CurrentInstanceMaxDop value" } else { "$notes" }
                    }  | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, Database, DatabaseMaxDop, CurrentInstanceMaxDop, RecommendedMaxDop, Notes
                }
            }
        }
    }
}
tools\dbatools\functions\Test-DbaMaxMemory.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Test-DbaMaxMemory {
    <#
        .SYNOPSIS
            Calculates the recommended value for SQL Server 'Max Server Memory' configuration setting. Works on SQL Server 2000-2014.

        .DESCRIPTION
            Inspired by Jonathan Kehayias's post about SQL Server Max memory (http://bit.ly/sqlmemcalc), this script displays a SQL Server's: total memory, currently configured SQL max memory, and the calculated recommendation.

            Jonathan notes that the formula used provides a *general recommendation* that doesn't account for everything that may be going on in your specific environment.

        .PARAMETER SqlInstance
            Allows you to specify a comma separated list of servers to query.

        .PARAMETER SqlCredential
            Windows or Sql Login Credential with permission to log into the SQL instance

        .PARAMETER Credential
            Windows Credential with permission to log on to the server running the SQL instance

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: MaxMemory, Memory
            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaMaxMemory

        .EXAMPLE
            Test-DbaMaxMemory -SqlInstance sqlcluster,sqlserver2012

            Calculate the 'Max Server Memory' settings for all servers within the SQL Server Central Management Server "sqlcluster"

        .EXAMPLE
            Test-DbaMaxMemory -SqlInstance sqlcluster | Where-Object { $_.SqlMaxMB -gt $_.TotalMB } | Set-DbaMaxMemory

            Find all servers in CMS that have Max SQL memory set to higher than the total memory of the server (think 2147483647) and set it to recommended value.
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [PSCredential]$Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level VeryVerbose -Message "Processing $instance" -Target $instance

            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            Write-Message -Level Verbose -Target $instance -Message "Retrieving maximum memory statistics from $instance"
            $serverMemory = Get-DbaMaxMemory -SqlInstance $server
            try {
                Write-Message -Level Verbose -Target $instance -Message "Retrieving number of instances from $($instance.ComputerName)"
                if ($Credential) {
                    $serverService = Get-DbaSqlService -ComputerName $instance -Credential $Credential -EnableException
                }
                else {
                    $serverService = Get-DbaSqlService -ComputerName $instance -EnableException
                }
                $instanceCount = ($serverService | Where-Object State -Like Running | Where-Object InstanceName | Group-Object InstanceName | Measure-Object Count).Count
            }
            catch {
                Write-Message -Level Warning -Message "Couldn't get accurate SQL Server instance count on $instance. Defaulting to 1." -Target $instance -ErrorRecord $_
                $instanceCount = 1
            }

            if ($null -eq $serverMemory) {
                continue
            }
            $reserve = 1

            $maxMemory = $serverMemory.SqlMaxMB
            $totalMemory = $serverMemory.TotalMB

            if ($totalMemory -ge 4096) {
                $currentCount = $totalMemory
                while ($currentCount / 4096 -gt 0) {
                    if ($currentCount -gt 16384) {
                        $reserve += 1
                        $currentCount += -8192
                    }
                    else {
                        $reserve += 1
                        $currentCount += -4096
                    }
                }
                $recommendedMax = [int]($totalMemory - ($reserve * 1024))
            }
            else {
                $recommendedMax = $totalMemory * .5
            }

            $recommendedMax = $recommendedMax / $instanceCount

            [pscustomobject]@{
                ComputerName  = $server.ComputerName
                InstanceName  = $server.ServiceName
                SqlInstance   = $server.DomainInstanceName
                InstanceCount = $instanceCount
                TotalMB       = [int]$totalMemory
                SqlMaxMB      = [int]$maxMemory
                RecommendedMB = [int]$recommendedMax
            } | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, InstanceCount, TotalMB, SqlMaxMB, RecommendedMB
        }
    }
}
tools\dbatools\functions\Test-DbaMigrationConstraint.ps1
function Test-DbaMigrationConstraint {
    <#
        .SYNOPSIS
            Show if you can migrate the database(s) between the servers.

        .DESCRIPTION
            When you want to migrate from a higher edition to a lower one there are some features that can't be used.
            This function will validate if you have any of this features in use and will report to you.
            The validation will be made ONLY on on SQL Server 2008 or higher using the 'sys.dm_db_persisted_sku_features' dmv.

            This function only validate SQL Server 2008 versions or higher.
            The editions supported by this function are:
                - Enterprise
                - Developer
                - Evaluation
                - Standard
                - Express

            Take into account the new features introduced on SQL Server 2016 SP1 for all versions. More information at https://blogs.msdn.microsoft.com/sqlreleaseservices/sql-server-2016-service-pack-1-sp1-released/

            The -Database parameter is auto-populated for command-line completion.

        .PARAMETER Source
            Source SQL Server. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SourceSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Destination
            Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.

        .PARAMETER DestinationSqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            The database(s) to exclude. Options for this list are auto-populated from the server.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Migration

            Author: Claudio Silva (@ClaudioESSilva)
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaMigrationConstraint

        .EXAMPLE
            Test-DbaMigrationConstraint -Source sqlserver2014a -Destination sqlcluster

            All databases on sqlserver2014a will be verified for features in use that can't be supported on sqlcluster.

        .EXAMPLE
            Test-DbaMigrationConstraint -Source sqlserver2014a -Destination sqlcluster -SqlCredential $cred

            All databases will be verified for features in use that can't be supported on the destination server. SQL credentials are used to authenticate against sqlserver2014 and Windows Authentication is used for sqlcluster.

        .EXAMPLE
            Test-DbaMigrationConstraint -Source sqlserver2014a -Destination sqlcluster -Database db1

            Only db1 database will be verified for features in use that can't be supported on the destination server.
    #>
    [CmdletBinding(DefaultParameterSetName = "DbMigration")]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $True)]
        [DbaInstance]$Source,
        [PSCredential]$SourceSqlCredential,
        [parameter(Mandatory = $true)]
        [DbaInstance]$Destination,
        [PSCredential]$DestinationSqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        <#
            1804890536 = Enterprise
            1872460670 = Enterprise Edition: Core-based Licensing
            610778273 = Enterprise Evaluation
            284895786 = Business Intelligence
            -2117995310 = Developer
            -1592396055 = Express
            -133711905= Express with Advanced Services
            -1534726760 = Standard
            1293598313 = Web
            1674378470 = SQL Database
        #>

        $editions = @{
            "Enterprise" = 10;
            "Developer"  = 10;
            "Evaluation" = 10;
            "Standard"   = 5;
            "Express"    = 1
        }
        $notesCanMigrate = "Database can be migrated."
        $notesCannotMigrate = "Database cannot be migrated."
    }
    process {
        try {
            Write-Message -Level Verbose -Message "Connecting to $Source."
            $sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source -Continue
        }

        try {
            Write-Message -Level Verbose -Message "Connecting to $Destination."
            $destServer = Connect-SqlInstance -SqlInstance $Destination -SqlCredential $DestinationSqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Destination -Continue
        }

        if (-Not $Database) {
            $Database = $sourceServer.Databases | Where-Object IsSystemObject -eq 0 | Select-Object Name, Status
        }

        if ($ExcludeDatabase) {
            $Database = $sourceServer.Databases | Where-Object Name -NotIn $ExcludeDatabase
        }

        if ($Database.Count -gt 0) {
            if ($Database -in @("master", "msdb", "tempdb")) {
                Stop-Function -Message "Migrating system databases is not currently supported."
                return
            }

            if ($sourceServer.VersionMajor -lt 9 -and $destServer.VersionMajor -gt 10) {
                Stop-Function -Message "Sql Server 2000 databases cannot be migrated to SQL Server version 2012 and above. Quitting."
                return
            }

            if ($sourceServer.Collation -ne $destServer.Collation) {
                Write-Message -Level Warning -Message "Collation on $Source, $($sourceServer.collation) differs from the $Destination, $($destServer.collation)."
            }

            if ($sourceServer.VersionMajor -gt $destServer.VersionMajor) {
                #indicate they must use 'Generate Scripts' and 'Export Data' options?
                Stop-Function -Message "You can't migrate databases from a higher version to a lower one. Quitting."
                return
            }

            if ($sourceServer.VersionMajor -lt 10) {
                Stop-Function -Message "This function does not support versions lower than SQL Server 2008 (v10)"
                return
            }

            #if editions differs, from higher to lower one, verify the sys.dm_db_persisted_sku_features - only available from SQL 2008 +
            if (($sourceServer.VersionMajor -ge 10 -and $destServer.VersionMajor -ge 10)) {
                foreach ($db in $Database) {
                    if ([string]::IsNullOrEmpty($db.Status)) {
                        $dbstatus = ($sourceServer.Databases | Where-Object Name -eq $db).Status.ToString()
                        $dbName = $db
                    }
                    else {
                        $dbstatus = $db.Status.ToString()
                        $dbName = $db.Name
                    }

                    Write-Message -Level Verbose -Message "Checking database '$dbName'."

                    if ($dbstatus.Contains("Offline") -eq $false -or $db.IsAccessible -eq $true) {

                        [long]$destVersionNumber = $($destServer.VersionString).Replace(".", "")
                        [string]$sourceVersion = "$($sourceServer.Edition) $($sourceServer.ProductLevel) ($($sourceServer.Version))"
                        [string]$destVersion = "$($destServer.Edition) $($destServer.ProductLevel) ($($destServer.Version))"
                        [string]$dbFeatures = ""

                        #Check if database has any FILESTREAM filegroup
                        Write-Message -Level Verbose -Message "Checking if FileStream is in use for database '$dbName'."
                        if ($sourceServer.Databases[$dbName].FileGroups | Where-Object FileGroupType -eq 'FileStreamDataFileGroup') {
                            Write-Message -Level Verbose -Message "Found FileStream filegroup and files."
                            $fileStreamSource = Get-DbaSpConfigure -SqlInstance $sourceServer -ConfigName FilestreamAccessLevel
                            $fileStreamDestination = Get-DbaSpConfigure -SqlInstance $destServer -ConfigName FilestreamAccessLevel

                            if ($fileStreamSource.RunningValue -ne $fileStreamDestination.RunningValue) {
                                [pscustomobject]@{
                                    SourceInstance      = $sourceServer.Name
                                    DestinationInstance = $destServer.Name
                                    SourceVersion       = $sourceVersion
                                    DestinationVersion  = $destVersion
                                    Database            = $dbName
                                    FeaturesInUse       = $dbFeatures
                                    IsMigratable        = $false
                                    Notes               = "$notesCannotMigrate. Destination server dones not have the 'FilestreamAccessLevel' configuration (RunningValue: $($fileStreamDestination.RunningValue)) equal to source server (RunningValue: $($fileStreamSource.RunningValue))."
                                }
                                Continue
                            }
                        }

                        try {
                            $sql = "SELECT feature_name FROM sys.dm_db_persisted_sku_features"

                            $skuFeatures = $sourceServer.Query($sql, $dbName)

                            Write-Message -Level Verbose -Message "Checking features in use..."

                            if (@($skuFeatures).Count -gt 0) {
                                foreach ($row in $skuFeatures) {
                                    $dbFeatures += ",$($row["feature_name"])"
                                }

                                $dbFeatures = $dbFeatures.TrimStart(",")
                            }
                        }
                        catch {
                            Stop-Function -Message "Issue collecting sku features." -ErrorRecord $_ -Target $sourceServer -Continue
                        }

                        #If SQL Server 2016 SP1 (13.0.4001.0) or higher
                        if ($destVersionNumber -ge 13040010) {
                            <#
                                Need to verify if Edition = EXPRESS and database uses 'Change Data Capture' (CDC)
                                This means that database cannot be migrated because Express edition doesn't have SQL Server Agent
                            #>
                            if ($editions.Item($destServer.Edition.ToString().Split(" ")[0]) -eq 1 -and $dbFeatures.Contains("ChangeCapture")) {
                                [pscustomobject]@{
                                    SourceInstance      = $sourceServer.Name
                                    DestinationInstance = $destServer.Name
                                    SourceVersion       = $sourceVersion
                                    DestinationVersion  = $destVersion
                                    Database            = $dbName
                                    FeaturesInUse       = $dbFeatures
                                    IsMigratable        = $false
                                    Notes               = "$notesCannotMigrate. Destination server edition is EXPRESS which does not support 'ChangeCapture' feature that is in use."
                                }
                            }
                            else {
                                [pscustomobject]@{
                                    SourceInstance      = $sourceServer.Name
                                    DestinationInstance = $destServer.Name
                                    SourceVersion       = $sourceVersion
                                    DestinationVersion  = $destVersion
                                    Database            = $dbName
                                    FeaturesInUse       = $dbFeatures
                                    IsMigratable        = $true
                                    Notes               = $notesCanMigrate
                                }
                            }
                        }
                        #Version is lower than SQL Server 2016 SP1
                        else {
                            Write-Message -Level Verbose -Message "Source Server Edition: $($sourceServer.Edition) (Weight: $($editions.Item($sourceServer.Edition.ToString().Split(" ")[0])))"
                            Write-Message -Level Verbose -Message "Destination Server Edition: $($destServer.Edition) (Weight: $($editions.Item($destServer.Edition.ToString().Split(" ")[0])))"

                            #Check for editions. If destination edition is lower than source edition and exists features in use
                            if (($editions.Item($destServer.Edition.ToString().Split(" ")[0]) -lt $editions.Item($sourceServer.Edition.ToString().Split(" ")[0])) -and (!([string]::IsNullOrEmpty($dbFeatures)))) {
                                [pscustomobject]@{
                                    SourceInstance      = $sourceServer.Name
                                    DestinationInstance = $destServer.Name
                                    SourceVersion       = $sourceVersion
                                    DestinationVersion  = $destVersion
                                    Database            = $dbName
                                    FeaturesInUse       = $dbFeatures
                                    IsMigratable        = $false
                                    Notes               = "$notesCannotMigrate There are features in use not available on destination instance."
                                }
                            }
                            #
                            else {
                                [pscustomobject]@{
                                    SourceInstance      = $sourceServer.Name
                                    DestinationInstance = $destServer.Name
                                    SourceVersion       = $sourceVersion
                                    DestinationVersion  = $destVersion
                                    Database            = $dbName
                                    FeaturesInUse       = $dbFeatures
                                    IsMigratable        = $true
                                    Notes               = $notesCanMigrate
                                }
                            }
                        }
                    }
                    else {
                        Write-Message -Level Warning -Message "Database '$dbName' is offline or not accessible. Bring database online and re-run the command."
                    }
                }
            }
            else {
                #SQL Server 2005 or under
                Write-Message -Level Warning -Message "This validation will not be made on versions lower than SQL Server 2008 (v10)."
                Write-Message -Level Verbose -Message "Source server version: $($sourceServer.VersionMajor)."
                Write-Message -Level Verbose -Message "Destination server version: $($destServer.VersionMajor)."
            }
        }
        else {
            Write-Message -Level Output -Message "There are no databases to validate."
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Test-SqlMigrationConstraint
    }
}
tools\dbatools\functions\Test-DbaNetworkLatency.ps1
function Test-DbaNetworkLatency {
    <#
        .SYNOPSIS
            Tests how long a query takes to return from SQL Server

        .DESCRIPTION
            This function is intended to help measure SQL Server network latency by establishing a connection and executing a simple query. This is a better than a simple ping because it actually creates the connection to the SQL Server and measures the time required for only the entire routine, but the duration of the query as well how long it takes for the results to be returned.

            By default, this command will execute "SELECT TOP 100 * FROM INFORMATION_SCHEMA.TABLES" three times.

            It will then output how long the entire connection and command took, as well as how long *only* the execution of the command took.

            This allows you to see if the issue is with the connection or the SQL Server itself.

        .PARAMETER SqlInstance
            The SQL Server you want to run the test on.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Query
            Specifies the query to be executed. By default, "SELECT TOP 100 * FROM INFORMATION_SCHEMA.TABLES" will be executed on master. To execute in other databases, use fully qualified object names.

        .PARAMETER Count
            Specifies how many times the query should be executed. By default, the query is executed three times.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Performance, Network
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaNetworkLatency

        .EXAMPLE
            Test-DbaNetworkLatency -SqlInstance sqlserver2014a, sqlcluster

            Tests the roundtrip return of "SELECT TOP 100 * FROM INFORMATION_SCHEMA.TABLES" on sqlserver2014a and sqlcluster using Windows credentials.

        .EXAMPLE
            Test-DbaNetworkLatency -SqlInstance sqlserver2014a -SqlCredential $cred

            Tests the execution results return of "SELECT TOP 100 * FROM INFORMATION_SCHEMA.TABLES" on sqlserver2014a using SQL credentials.

        .EXAMPLE
            Test-DbaNetworkLatency -SqlInstance sqlserver2014a, sqlcluster, sqlserver -Query "select top 10 * from otherdb.dbo.table" -Count 10

            Tests the execution results return of "select top 10 * from otherdb.dbo.table" 10 times on sqlserver2014a, sqlcluster, and sqlserver using Windows credentials.

    #>
    [CmdletBinding()]
    [OutputType([System.Object[]])]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string]$Query = "select top 100 * from INFORMATION_SCHEMA.TABLES",
        [int]$Count = 3,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            try {
                $start = [System.Diagnostics.Stopwatch]::StartNew()
                $currentCount = 0
                try {
                    Write-Message -Level Verbose -Message "Connecting to $instance."
                    $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
                }
                catch {
                    Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
                }

                do {
                    if (++$currentCount -eq 1) {
                        $first = [System.Diagnostics.Stopwatch]::StartNew()
                    }
                    $null = $server.Query($query)
                    if ($currentCount -eq $count) {
                        $last = $first.Elapsed
                    }
                }
                while ($currentCount -lt $count)

                $end = $start.Elapsed
                $totalTime = $end.TotalMilliseconds
                $average = $totalTime / $count

                $totalWarm = $last.TotalMilliseconds
                if ($Count -eq 1) {
                    $averageWarm = $totalWarm
                }
                else {
                    $averageWarm = $totalWarm / $count
                }

                [PSCustomObject]@{
                    ComputerName     = $server.ComputerName
                    InstanceName     = $server.ServiceName
                    SqlInstance      = $server.DomainInstanceName
                    Count            = $count
                    Total            = [prettytimespan]::FromMilliseconds($totalTime)
                    Avg              = [prettytimespan]::FromMilliseconds($average)
                    ExecuteOnlyTotal = [prettytimespan]::FromMilliseconds($totalWarm)
                    ExecuteOnlyAvg   = [prettytimespan]::FromMilliseconds($averageWarm)
                    NetworkOnlyTotal = [prettytimespan]::FromMilliseconds($totalTime - $totalWarm)
                } | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, 'Count as ExecutionCount', Total, 'Avg as Average', ExecuteOnlyTotal, 'ExecuteOnlyAvg as ExecuteOnlyAverage', NetworkOnlyTotal #backwards compat
            }
            catch {
                Stop-Function -Message "Error occurred testing dba network latency: $_" -ErrorRecord $_ -Continue -Target $instance
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Test-SqlNetworkLatency
    }
}
tools\dbatools\functions\Test-DbaOptimizeForAdHoc.ps1
function Test-DbaOptimizeForAdHoc {
    <#
        .SYNOPSIS
            Displays information relating to SQL Server Optimize for AdHoc Workloads setting.  Works on SQL Server 2008-2016.

        .DESCRIPTION
            When this option is set, plan cache size is further reduced for single-use ad hoc OLTP workload.

            More info: https://msdn.microsoft.com/en-us/library/cc645587.aspx
            http://www.sqlservercentral.com/blogs/glennberry/2011/02/25/some-suggested-sql-server-2008-r2-instance-configuration-settings/

            These are just general recommendations for SQL Server and are a good starting point for setting the "optimize for adhoc workloads" option.

        .PARAMETER SqlInstance
            A collection of one or more SQL Server instance names to query.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

    .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message. This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting. Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Configure, SPConfigure
            Author: Brandon Abshire, netnerds.net

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaOptimizeForAdHoc

        .EXAMPLE
            Test-DbaOptimizeForAdHoc -SqlInstance sql2008, sqlserver2012

            Validates whether Optimize for AdHoc Workloads setting is enabled for servers sql2008 and sqlserver2012.
    #>
    [CmdletBinding()]
    param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $True)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $notesAdHocZero = "Recommended configuration is 1 (enabled)."
        $notesAsRecommended = "Configuration is already set as recommended."
        $recommendedValue = 1
    }
    process {

        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential -MinimumVersion 10
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            #Get current configured value
            $optimizeAdHoc = $server.Configuration.OptimizeAdhocWorkloads.ConfigValue

            #Setting notes for optimize adhoc value
            if ($optimizeAdHoc -eq $recommendedValue) {
                $notes = $notesAsRecommended
            }
            else {
                $notes = $notesAdHocZero
            }

            [pscustomobject]@{
                ComputerName             = $server.ComputerName
                InstanceName             = $server.ServiceName
                SqlInstance              = $server.DomainInstanceName
                CurrentOptimizeAdHoc     = $optimizeAdHoc
                RecommendedOptimizeAdHoc = $recommendedValue
                Notes                    = $notes
            }
        }
    }
}
tools\dbatools\functions\Test-DbaPowerPlan.ps1
function Test-DbaPowerPlan {
    <#
        .SYNOPSIS
            Checks the Power Plan settings for compliance with best practices, which recommend High Performance for SQL Server.

        .DESCRIPTION
            Checks the Power Plan settings on a computer against best practices recommendations. If one server is checked, only $true or $false is returned. If multiple servers are checked, each server's name and an isBestPractice field are returned.

            References:
            https://support.microsoft.com/en-us/kb/2207548
            http://www.sqlskills.com/blogs/glenn/windows-power-plan-effects-on-newer-intel-processors/

        .PARAMETER ComputerName
            The server(s) to check Power Plan settings on.

        .PARAMETER Credential
            Specifies a PSCredential object to use in authenticating to the server(s), instead of the current user account.

        .PARAMETER CustomPowerPlan
            If your organization uses a custom power plan that's considered best practice, specify it here.

        .PARAMETER Detailed
             Output all properties, will be deprecated in 1.0.0 release.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: PowerPlan
            Requires: WMI access to servers

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaPowerPlan

        .EXAMPLE
            Test-DbaPowerPlan -ComputerName sqlserver2014a

            Checks the Power Plan settings for sqlserver2014a and indicates whether or not it complies with best practices.

        .EXAMPLE
            Test-DbaPowerPlan -ComputerName sqlserver2014a -CustomPowerPlan 'Maximum Performance'

            Checks the Power Plan settings for sqlserver2014a and indicates whether or not it is set to the custom plan "Maximum Performance".
    #>
    param (
        [parameter(ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer", "SqlInstance")]
        [DbaInstance[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [string]$CustomPowerPlan,
        [switch]$Detailed,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter Detailed

        $bpPowerPlan = [PSCustomObject]@{
            InstanceID  = '8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c'
            ElementName = $null
        }

        $sessionOption = New-CimSessionOption -Protocol DCom
    }

    process {
        foreach ($computer in $ComputerName) {
            $server = Resolve-DbaNetworkName -ComputerName $computer -Credential $credential

            $computerResolved = $server.FullComputerName

            if (!$computerResolved) {
                Stop-Function -Message "Couldn't resolve hostname. Skipping." -Continue
            }

            Write-Message -Level Verbose -Message "Creating CimSession on $computer over WSMan."

            if (!$Credential) {
                $cimSession = New-CimSession -ComputerName $computerResolved -ErrorAction SilentlyContinue
            }
            else {
                $cimSession = New-CimSession -ComputerName $computerResolved -ErrorAction SilentlyContinue -Credential $Credential
            }

            if ($null -eq $cimSession.id) {
                Write-Message -Level Verbose -Message "Creating CimSession on $computer over WSMan failed. Creating CimSession on $computer over DCOM."

                if (!$Credential) {
                    $cimSession = New-CimSession -ComputerName $computerResolved -SessionOption $sessionOption -ErrorAction SilentlyContinue -Credential $Credential
                }
                else {
                    $cimSession = New-CimSession -ComputerName $computerResolved -SessionOption $sessionOption -ErrorAction SilentlyContinue
                }
            }

            if ($null -eq $cimSession.id) {
                Stop-Function -Message "Can't create CimSession on $computer." -Target $computer
            }

            Write-Message -Level Verbose -Message "Getting Power Plan information from $computer."

            try {
                $powerPlans = Get-CimInstance -CimSession $cimSession -ClassName Win32_PowerPlan -Namespace "root\cimv2\power" -ErrorAction Stop | Select-Object ElementName, InstanceID, IsActive
            }
            catch {
                if ($_.Exception -match "namespace") {
                    Stop-Function -Message "Can't get Power Plan Info for $computer. Unsupported operating system." -Continue -ErrorRecord $_ -Target $computer
                }
                else {
                    Stop-Function -Message "Can't get Power Plan Info for $computer. Check logs for more details." -Continue -ErrorRecord $_ -Target $computer
                }
            }

            $powerPlan = $powerPlans | Where-Object IsActive -eq 'True' | Select-Object ElementName, InstanceID
            $powerPlan.InstanceID = $powerPlan.InstanceID.Split('{')[1].Split('}')[0]

            if ($CustomPowerPlan.Length -gt 0) {
                $bpPowerPlan.ElementName = $CustomPowerPlan
                $bpPowerPlan.InstanceID = $($powerPlans | Where-Object { $_.ElementName -eq $CustomPowerPlan }).InstanceID
            }
            else {
                $bpPowerPlan.ElementName = $($powerPlans | Where-Object { $_.InstanceID.Split('{')[1].Split('}')[0] -eq $bpPowerPlan.InstanceID }).ElementName
                if ($null -eq $bpPowerplan.ElementName) {
                    $bpPowerPlan.ElementName = "You do not have the high performance plan installed on this machine."
                }
            }

            Write-Message -Level Verbose -Message "Recommended GUID is $($bpPowerPlan.InstanceID) and you have $($powerPlan.InstanceID)."

            if ($null -eq $powerPlan.InstanceID) {
                $powerPlan.ElementName = "Unknown"
            }

            if ($powerPlan.InstanceID -eq $bpPowerPlan.InstanceID) {
                $isBestPractice = $true
            }
            else {
                $isBestPractice = $false
            }

            [PSCustomObject]@{
                ComputerName         = $computer
                ActivePowerPlan      = $powerPlan.ElementName
                RecommendedPowerPlan = $bpPowerPlan.ElementName
                isBestPractice       = $isBestPractice
            }
        }
    }
}
tools\dbatools\functions\Test-DbaRecoveryModel.ps1
function Test-DbaRecoveryModel {
    <#
        .SYNOPSIS
            Find if database is really a specific recovery model or not.

        .DESCRIPTION
            When you switch a database into FULL recovery model, it will behave like a SIMPLE recovery model until a full backup is taken in order to begin a log backup chain.

            However, you may also desire to validate if a database is SIMPLE or BULK LOGGED on an instance.

            Inspired by Paul Randal's post (http://www.sqlskills.com/blogs/paul/new-script-is-that-database-really-in-the-full-recovery-mode/)

        .PARAMETER SqlInstance
            The SQL Server instance to connect to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Specifies the database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.

        .PARAMETER ExcludeDatabase
            Specifies the database(s) to exclude from processing. Options for this list are auto-populated from the server.

        .PARAMETER RecoveryModel
            Specifies the type of recovery model you wish to test. By default it will test for FULL Recovery Model.

        .PARAMETER Detailed
            Output all properties, will be deprecated in 1.0.0 release.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: DisasterRecovery, Backup
            Author: Claudio Silva (@ClaudioESSilva)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: GNU GPL v3 https://opensource.org/licenses/GPL-3.0

        .LINK
            https://dbatools.io/Test-DbaRecoveryModel

        .EXAMPLE
            Test-DbaRecoveryModel -SqlInstance sql2005

            Shows all databases where the configured recovery model is FULL and indicates whether or not they are really in FULL recovery model.

        .EXAMPLE
            Test-DbaRecoveryModel -SqlInstance . | Where-Object {$_.ActualRecoveryModel -ne "FULL"}

            Only shows the databases that are functionally in 'simple' mode.

        .EXAMPLE
            Test-DbaRecoveryModel -SqlInstance sql2008 -RecoveryModel Bulk_Logged | Sort-Object Server  -Descending

            Shows all databases where the configured recovery model is BULK_LOGGED and sort them by server name descending

        .EXAMPLE
            Test-DbaRecoveryModel -SqlInstance localhost | Select-Object -Property *

            Shows all of the properties for the databases that have Full Recovery Model
    #>
    [CmdletBinding()]
    [OutputType("System.Collections.ArrayList")]
    Param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [Alias("Databases")]
        [object[]]$Database,
        [object[]]$ExcludeDatabase,
        [PSCredential]$SqlCredential,
        [validateSet("Full","Simple","Bulk_Logged")]
        [object]$RecoveryModel,
        [switch]$Detailed,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter Detailed
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Alias Test-DbaFullRecoveryModel

        if(Test-Bound -ParameterName RecoveryModel -Not){
            $RecoveryModel = "Full"
        }

        switch($RecoveryModel){
            "Full"          {$recoveryCode = 1}
            "Bulk_Logged"   {$recoveryCode = 2}
            "Simple"        {$recoveryCode = 3}
        }

        $sqlRecoveryModel = "SELECT  SERVERPROPERTY('MachineName') AS ComputerName,
                ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
                SERVERPROPERTY('ServerName') AS SqlInstance
                        , d.[name] AS [Database]
                        , d.recovery_model AS RecoveryModel
                        , d.recovery_model_desc AS RecoveryModelDesc
                        , CASE
                            WHEN d.recovery_model = 1 AND drs.last_log_backup_lsn IS NOT NULL THEN 1
                            ELSE 0
                           END AS IsReallyInFullRecoveryModel
                  FROM sys.databases AS D
                    INNER JOIN sys.database_recovery_status AS drs
                       ON D.database_id = drs.database_id
                  WHERE d.recovery_model = $recoveryCode"

        if ($Database) {
            $dblist = $Database -join "','"
            $databasefilter += "AND d.[name] in ('$dblist')"
        }
        if ($ExcludeDatabase) {
            $dblist = $ExcludeDatabase -join "','"
            $databasefilter += "AND d.[name] NOT IN ('$dblist')"
        }

        $sql = "$sqlRecoveryModel $databasefilter"

        Write-Message -Level Debug -Message $sql
    }
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level Verbose -Message "Connecting to $instance."
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            try {
                $results = $server.Query($sql)

                if (-not $results) {
                    Write-Message -Level Verbose -Message "Server '$instance' does not have any databases in the $RecoveryModel recovery model."
                }

                foreach ($row in $results) {
                    if (!([bool]$row.IsReallyInFullRecoveryModel) -and $RecoveryModel -eq 'Full') {
                        $ActualRecoveryModel = "SIMPLE"
                    }
                    else{
                        $ActualRecoveryModel = "$($RecoveryModel.ToString().ToUpper())"
                    }

                    [PSCustomObject]@{
                        ComputerName   = $row.ComputerName
                        InstanceName   = $row.InstanceName
                        SqlInstance    = $row.SqlInstance
                        Database       = $row.Database
                        ConfiguredRecoveryModel = $row.RecoveryModelDesc
                        ActualRecoveryModel = $ActualRecoveryModel
                    } | Select-DefaultView -Property ComputerName,InstanceName,SqlInstance,Database,ConfiguredRecoveryModel,ActualRecoveryModel
                }
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }
        }
    }
}
tools\dbatools\functions\Test-DbaServerName.ps1
function Test-DbaServerName {
    <#
        .SYNOPSIS
            Tests to see if it's possible to easily rename the server at the SQL Server instance level, or if it even needs to be changed.

        .DESCRIPTION
            When a SQL Server's host OS is renamed, the SQL Server should be as well. This helps with Availability Groups and Kerberos.

            This command helps determine if your OS and SQL Server names match, and whether a rename is required.

            It then checks conditions that would prevent a rename, such as database mirroring and replication.

            https://www.mssqltips.com/sqlservertip/2525/steps-to-change-the-server-name-for-a-sql-server-machine/

        .PARAMETER SqlInstance
            The SQL Server that you're connecting to.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Detailed
            Output all properties, will be deprecated in 1.0.0 release.

        .PARAMETER ExcludeSsrs
            If this switch is enabled, checking for SQL Server Reporting Services will be skipped.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: SPN, ServerName

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaServerName

        .EXAMPLE
            Test-DbaServerName -SqlInstance sqlserver2014a

            Returns ServerInstanceName, SqlServerName, IsEqual and RenameRequired for sqlserver2014a.

        .EXAMPLE
            Test-DbaServerName -SqlInstance sqlserver2014a, sql2016

            Returns ServerInstanceName, SqlServerName, IsEqual and RenameRequired for sqlserver2014a and sql2016.

        .EXAMPLE
            Test-DbaServerName -SqlInstance sqlserver2014a, sql2016 -ExcludeSsrs

            Returns ServerInstanceName, SqlServerName, IsEqual and RenameRequired for sqlserver2014a and sql2016, but skips validating if SSRS is installed on both instances.

        .EXAMPLE
            Test-DbaServerName -SqlInstance sqlserver2014a, sql2016 | Select-Object *

            Returns ServerInstanceName, SqlServerName, IsEqual and RenameRequired for sqlserver2014a and sql2016.

            If a Rename is required, it will also show Updatable, and Reasons if the servername is not updatable.
    #>
    [CmdletBinding()]
    [OutputType([System.Collections.ArrayList])]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [switch]$Detailed,
        [Alias("NoWarning")]
        [switch]$ExcludeSsrs,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter Detailed
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter NoWarning
    }
    process {

        foreach ($instance in $SqlInstance) {
            Write-Verbose "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if ($server.IsClustered) {
                Write-Message -Level Warning -Message "$instance is a cluster. Renaming clusters is not supported by Microsoft."
            }

            $sqlInstanceName = $server.Query("SELECT @@servername AS ServerName").ServerName
            $instance = $server.InstanceName

            if ($instance.Length -eq 0) {
                $serverInstanceName = $server.NetName
                $instance = "MSSQLSERVER"
            }
            else {
                $netname = $server.NetName
                $serverInstanceName = "$netname\$instance"
            }

            $serverInfo = [PSCustomObject]@{
                ComputerName = $server.NetName
                ServerName   = $sqlInstanceName
                InstanceName = $server.ServiceName
                SqlInstance  = $server.DomainInstanceName
                RenameRequired = $serverInstanceName -ne $sqlInstanceName
                Updatable    = "N/A"
                Warnings     = $null
                Blockers     = $null
            }

            $reasons = @()
            $ssrsService = "SQL Server Reporting Services ($instance)"

            Write-Message -Level Verbose -Message "Checking for $serverName on $netBiosName"
            $rs = $null
            if ($SkipSsrs -eq $false -or $NoWarning -eq $false) {
                try {
                    $rs = Get-DbaSqlService -ComputerName $instance.ComputerName -InstanceName $server.ServiceName -Type SSRS -EnableException -WarningAction Stop
                }
                catch {
                    Write-Message -Level Warning -Message "Unable to pull information on $ssrsService." -ErrorRecord $_ -Target $instance
                }
            }

            if ($null -ne $rs -or $rs.Count -gt 0) {
                if ($rs.State -eq 'Running') {
                    $rstext = "$ssrsService must be stopped and updated."
                }
                else {
                    $rstext = "$ssrsService exists. When it is started again, it must be updated."
                }
                $serverInfo.Warnings = $rstext
            }
            else {
                $serverInfo.Warnings = "N/A"
            }

            # check for mirroring
            $mirroredDb = $server.Databases | Where-Object { $_.IsMirroringEnabled -eq $true }

            Write-Message -Level Debug -Message "Found the following mirrored dbs: $($mirroredDb.Name)"

            if ($mirroredDb.Length -gt 0) {
                $dbs = $mirroredDb.Name -join ", "
                $reasons += "Databases are being mirrored: $dbs"
            }

            # check for replication
            $sql = "SELECT name FROM sys.databases WHERE is_published = 1 OR is_subscribed = 1 OR is_distributor = 1"
            Write-Message -Level Debug -Message "SQL Statement: $sql"
            $replicatedDb = $server.Query($sql)

            if ($replicatedDb.Count -gt 0) {
                $dbs = $replicatedDb.Name -join ", "
                $reasons += "Database(s) are involved in replication: $dbs"
            }

            # check for even more replication
            $sql = "SELECT srl.remote_name as RemoteLoginName FROM sys.remote_logins srl JOIN sys.sysservers sss ON srl.server_id = sss.srvid"
            Write-Message -Level Debug -Message "SQL Statement: $sql"
            $results = $server.Query($sql)

            if ($results.RemoteLoginName.Count -gt 0) {
                $remoteLogins = $results.RemoteLoginName -join ", "
                $reasons += "Remote logins still exist: $remoteLogins"
            }

            if ($reasons.Length -gt 0) {
                $serverInfo.Updatable = $false
                $serverInfo.Blockers = $reasons
            }
            else {
                $serverInfo.Updatable = $true
                $serverInfo.Blockers = "N/A"
            }

            $serverInfo | Select-DefaultView -ExcludeProperty InstanceName, SqlInstance
        }
    }
}
tools\dbatools\functions\Test-DbaSpn.ps1
#ValidationTags#FlowControl,Pipeline#
function Test-DbaSpn {
    <#
        .SYNOPSIS
            Test-DbaSpn will determine what SPNs *should* be set for a given server (and any instances of SQL running on it) and return
            whether the SPNs are set or not.

        .DESCRIPTION
            This function is designed to take in a server name(s) and attempt to determine required SPNs. It was initially written to mimic the (previously)
            broken functionality of the Microsoft Kerberos Configuration manager and SQL Server 2016. The functon will connect to a remote server and,
            through WMI, discover all running intances of SQL Server. For any instances with TCP/IP enabled, the script will determine which port(s)
            the instances are listening on and generate the required SPNs. For named instances NOT using dynamic ports, the script will generate a port-
            based SPN for those instances as well.  At a minimum, the script will test a base, port-less SPN for each instance discovered.

            Once the required SPNs are generated, the script will connect to Active Directory and search for any of the SPNs (if any) that are already
            set.

            The function will return a custom object(s) that contains the server name checked, the instance name discovered, the account the service is
            running under, and what the "required" SPN should be. It will also return a boolean property indicating if the SPN is set in Active Directory
            or not.

        .PARAMETER ComputerName
            The computer you want to discover any SQL Server instances on. This parameter is required.

        .PARAMETER Credential
            The credential you want to use to connect to the remote server and active directory.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: SPN
            Author: Drew Furgiuele (@pittfurg), http://www.port1433.com
            Editor: niphlod

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaSpn

        .EXAMPLE
            Test-DbaSpn -ComputerName SQLSERVERA -Credential (Get-Credential)

            Connects to a computer (SQLSERVERA) and queries WMI for all SQL instances and return "required" SPNs. It will then take each SPN it generates
            and query Active Directory to make sure the SPNs are set.

        .EXAMPLE
            Test-DbaSpn -ComputerName SQLSERVERA,SQLSERVERB -Credential (Get-Credential)

            Connects to multiple computers (SQLSERVERA, SQLSERVERB) and queries WMI for all SQL instances and return "required" SPNs.
            It will then take each SPN it generates and query Active Directory to make sure the SPNs are set.

        .EXAMPLE
            Test-DbaSpn -ComputerName SQLSERVERC -Credential (Get-Credential)

            Connects to a computer (SQLSERVERC) on a specified and queries WMI for all SQL instances and return "required" SPNs.
            It will then take each SPN it generates and query Active Directory to make sure the SPNs are set. Note that the credential you pass must have be a valid login with appropriate rights on the domain
    #>
    [cmdletbinding()]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [DbaInstance[]]$ComputerName,
        [PSCredential]$Credential,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        # spare the cmdlet to search for the same account over and over
        $resultCache = @{}
    }
    process {
        foreach ($computer in $ComputerName) {
            try {
                $resolved = Resolve-DbaNetworkName -ComputerName $computer.ComputerName -Credential $Credential -ErrorAction Stop
            }
            catch {
                $resolved = Resolve-DbaNetworkName -ComputerName $computer.ComputerName -Turbo
            }

            if ($null -eq $resolved.IPAddress) {
                Write-Message -Level Warning -Message "Cannot resolve IP address, moving on."
                continue
            }

            $hostEntry = $resolved.FullComputerName

            Write-Message -Message "Resolved ComputerName to FQDN: $hostEntry" -Level Verbose

            $Scriptblock = {

                function Convert-SqlVersion {
                    [cmdletbinding()]
                    param (
                        [version]$version
                    )

                    switch ($version.Major) {
                        9 { "SQL Server 2005" }
                        10 {
                            if ($version.Minor -eq 0) {
                                "SQL Server 2008"
                            }
                            else {
                                "SQL Server 2008 R2"
                            }
                        }
                        11 { "SQL Server 2012" }
                        12 { "SQL Server 2014" }
                        13 { "SQL Server 2016" }
                        14 { "SQL Server 2017" }
                        default { $version }
                    }
                }

                $spns = @()
                $servereName = $args[0]
                $hostEntry = $args[1]
                $instanceName = $args[2]
                $instanceCount = $wmi.ServerInstances.Count

                <# DO NOT use Write-Message as this is inside of a script block #>
                Write-Verbose "Found $instanceCount instances"

                foreach ($instance in $wmi.ServerInstances) {
                    $spn = [pscustomobject] @{
                        ComputerName           = $servereName
                        InstanceName           = $instanceName
                        #SKUNAME
                        SqlProduct             = $null
                        InstanceServiceAccount = $null
                        RequiredSPN            = $null
                        IsSet                  = $false
                        Cluster                = $false
                        TcpEnabled             = $false
                        Port                   = $null
                        DynamicPort            = $false
                        Warning                = "None"
                        Error                  = "None"
                        # for piping
                        Credential             = $Credential
                    }

                    $spn.InstanceName = $instance.Name
                    $instanceName = $spn.InstanceName

                    <# DO NOT use Write-Message as this is inside of a script block #>
                    Write-Verbose "Parsing $instanceName"

                    $services = $wmi.Services | Where-Object DisplayName -EQ "SQL Server ($instanceName)"
                    $spn.InstanceServiceAccount = $services.ServiceAccount
                    $spn.Cluster = ($services.advancedproperties | Where-Object Name -EQ 'Clustered').Value

                    if ($spn.Cluster) {
                        $hostEntry = ($services.advancedproperties | Where-Object Name -EQ 'VSNAME').Value.ToLower()
                        <# DO NOT use Write-Message as this is inside of a script block #>
                        Write-Verbose "Found cluster $hostEntry"
                        $hostEntry = ([System.Net.Dns]::GetHostEntry($hostEntry)).HostName
                        $spn.ComputerName = $hostEntry
                    }

                    $rawVersion = [version]($services.AdvancedProperties | Where-Object Name -EQ 'VERSION').Value

                    $version = Convert-SqlVersion $rawVersion
                    $skuName = ($services.AdvancedProperties | Where-Object Name -EQ 'SKUNAME').Value

                    $spn.SqlProduct = "$version $skuName"

                    #is tcp enabled on this instance? If not, we don't need an spn, son
                    if ((($instance.ServerProtocols | Where-Object { $_.Displayname -eq "TCP/IP" }).ProtocolProperties | Where-Object { $_.Name -eq "Enabled" }).Value -eq $true) {
                        <# DO NOT use Write-Message as this is inside of a script block #>
                        Write-Verbose "TCP is enabled, gathering SPN requirements"
                        $spn.TcpEnabled = $true
                        #Each instance has a default SPN of MSSQLSvc\<fqdn> or MSSSQLSvc\<fqdn>:Instance
                        if ($instance.Name -eq "MSSQLSERVER") {
                            $spn.RequiredSPN = "MSSQLSvc/$hostEntry"
                        }
                        else {
                            $spn.RequiredSPN = "MSSQLSvc/" + $hostEntry + ":" + $instance.Name
                        }
                    }

                    $spns += $spn
                }
                # Now, for each spn, do we need a port set? Only if TCP is enabled and NOT DYNAMIC!
                foreach ($spn in $spns) {
                    $ports = @()

                    $ips = (($wmi.ServerInstances | Where-Object { $_.Name -eq $spn.InstanceName }).ServerProtocols | Where-Object { $_.DisplayName -eq "TCP/IP" -and $_.IsEnabled -eq "True" }).IpAddresses
                    $ipAllPort = $null
                    foreach ($ip in $ips) {
                        if ($ip.Name -eq "IPAll") {
                            $ipAllPort = ($ip.IPAddressProperties | Where-Object { $_.Name -eq "TCPPort" }).Value
                            if (($ip.IpAddressProperties | Where-Object { $_.Name -eq "TcpDynamicPorts" }).Value -ne "") {
                                $ipAllPort = ($ip.IPAddressProperties | Where-Object { $_.Name -eq "TcpDynamicPorts" }).Value + "d"
                            }
                        }
                        else {
                            $enabled = ($ip.IPAddressProperties | Where-Object { $_.Name -eq "Enabled" }).Value
                            $active = ($ip.IPAddressProperties | Where-Object { $_.Name -eq "Active" }).Value
                            $tcpDynamicPorts = ($ip.IPAddressProperties | Where-Object { $_.Name -eq "TcpDynamicPorts" }).Value
                            if ($enabled -and $active -and $tcpDynamicPorts -eq "") {
                                $ports += ($ip.IPAddressProperties | Where-Object { $_.Name -eq "TCPPort" }).Value
                            }
                            elseif ($enabled -and $active -and $tcpDynamicPorts -ne "") {
                                $ports += $ipAllPort + "d"
                            }
                        }
                    }
                    if ($ipAllPort -ne "") {
                        #IPAll overrides any set ports. Not sure why that's the way it is?
                        $ports = $ipAllPort
                    }

                    $ports = $ports | Select-Object -Unique
                    foreach ($port in $ports) {
                        $newspn = $spn.PSObject.Copy()
                        if ($port -like "*d") {
                            $newspn.Port = ($port.replace("d", ""))
                            $newspn.RequiredSPN = $newspn.RequiredSPN.Replace($newSPN.InstanceName, $newspn.Port)
                            $newspn.DynamicPort = $true
                            $newspn.Warning = "Dynamic port is enabled"
                        }
                        else {
                            #If this is a named instance, replace the instance name with a port number (for non-dynamic ported named instances)
                            $newspn.Port = $port
                            $newspn.DynamicPort = $false

                            if ($newspn.InstanceName -eq "MSSQLSERVER") {
                                $newspn.RequiredSPN = $newspn.RequiredSPN + ":" + $port
                            }
                            else {
                                $newspn.RequiredSPN = $newspn.RequiredSPN.Replace($newSPN.InstanceName, $newspn.Port)
                            }
                        }
                        $spns += $newspn
                    }
                }
                $spns
            }

            Write-Message -Message "Connecting to SQL WMI on remote computer " -Level Verbose

            try {
                $spns = Invoke-ManagedComputerCommand -ComputerName $hostEntry -ScriptBlock $Scriptblock -ArgumentList $resolved.FullComputerName, $hostEntry, $computer.InstanceName -Credential $Credential -ErrorAction Stop
            }
            catch {
                Stop-Function -Message "Couldn't connect to $computer" -ErrorRecord $_ -Continue
            }

            #Now query AD for each required SPN
            foreach ($spn in $spns) {
                $searchfor = 'User'
                if ($spn.InstanceServiceAccount -eq 'LocalSystem' -or $spn.InstanceServiceAccount -like 'NT SERVICE\*') {
                    Write-Message -Level Verbose -Message "Virtual account detected, changing target registration to computername"
                    $spn.InstanceServiceAccount = "$($resolved.Domain)\$($resolved.ComputerName)$"
                    $searchfor = 'Computer'
                }
                elseif ($spn.InstanceServiceAccount -like '*\*$') {
                    Write-Message -Level Verbose -Message "Managed Service Account detected"
                    $searchfor = 'Computer'
                }

                $serviceAccount = $spn.InstanceServiceAccount
                # spare the cmdlet to search for the same account over and over
                if ($spn.InstanceServiceAccount -notin $resultCache.Keys) {
                    Write-Message -Message "Searching for $serviceAccount" -Level Verbose
                    try {
                        $result = Get-DbaADObject -ADObject $serviceAccount -Type $searchfor -Credential $Credential -EnableException
                        $resultCache[$spn.InstanceServiceAccount] = $result
                    }
                    catch {
                        if (![System.String]::IsNullOrEmpty($spn.InstanceServiceAccount)) {
                            Write-Message -Message "AD lookup failure. This may be because the domain cannot be resolved for the SQL Server service account ($serviceAccount)." -Level Warning
                        }
                    }
                }
                else {
                    $result = $resultCache[$spn.InstanceServiceAccount]
                }
                if ($result.Count -gt 0) {
                    try {
                        $results = $result.GetUnderlyingObject()
                        if ($results.Properties.servicePrincipalName -contains $spn.RequiredSPN) {
                            $spn.IsSet = $true
                        }
                    }
                    catch {
                        Write-Message -Message "The SQL Service account ($serviceAccount) has been found, but you don't have enough permission to inspect its SPNs" -Level Warning
                        continue
                    }
                }
                else {
                    Write-Message -Level Warning -Message "SQL Service account not found. Results may not be accurate."
                    $spn
                    continue
                }
                if (!$spn.IsSet -and $spn.TcpEnabled) {
                    $spn.Error = "SPN missing"
                }

                $spn | Select-DefaultView -ExcludeProperty Credential, DomainName
            }
        }
    }
}
tools\dbatools\functions\Test-DbaSqlBuild.ps1
function Test-DbaSqlBuild {
    <#
    .SYNOPSIS
        Returns SQL Server Build "compliance" level on a build

    .DESCRIPTION
        It answers the question "is this build up to date ?"
        Returns info about the specific build of a SQL instance, including the SP, the CU and the reference KB, End Of Support, wherever possible.
        It adds a Compliance property as true/false, and adds details about the "targeted compliance"

    .PARAMETER Build
        Instead of connecting to a real instance, pass a string identifying the build to get the info back.

    .PARAMETER MinimumBuild
        This is the build version to test "compliance" against. Anything below this is flagged as not compliant

    .PARAMETER MaxBehind
        Instead of using a specific MinimumBuild here you can pass "how many service packs and cu back" is the targeted compliance level
        You can use xxSP or xxCU or both, where xx is a number. See Examples for more informations

    .PARAMETER Latest
        Shortcut for specifying the very most up-to-date build available.

    .PARAMETER SqlInstance
        Target any number of instances, in order to return their compliance state.

    .PARAMETER SqlCredential
        When connecting to an instance, use the credentials specified.

    .PARAMETER Update
        Looks online for the most up to date reference, replacing the local one.

    .PARAMETER Quiet
        Makes the function just return $true/$false. It's useful if you use Test-DbaSqlBuild in your own scripts, like
            if (Test-DbaSqlBuild -Build "12.0.5540" -MaxBehind "0CU" -Quiet) {
                Do-Something
            }

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .EXAMPLE
        Test-DbaSqlBuild -Build "12.0.5540" -MinimumBuild "12.0.5557"

        Returns information about a build identified by "12.0.5540" (which is SQL 2014 with SP2 and CU4), which is not compliant as the minimum required
        build is "12.0.5557" (which is SQL 2014 with SP2 and CU8)

    .EXAMPLE
        Test-DbaSqlBuild -Build "12.0.5540" -MaxBehind "1SP"

        Returns information about a build identified by "12.0.5540", making sure it is AT MOST 1 Service Pack "behind". For that version,
        that identifies an SP2, means accepting as the lowest compliance version as "12.0.4110", that identifies 2014 with SP1

    .EXAMPLE
        Test-DbaSqlBuild -Build "12.0.5540" -MaxBehind "1SP 1CU"

        Returns information about a build identified by "12.0.5540", making sure it is AT MOST 1 Service Pack "behind", plus 1 CU "behind". For that version,
        that identifies an SP2 and CU, rolling back 1 SP brings you to "12.0.4110", but given the latest CU for SP1 is CU13, the target "compliant" build
        will be "12.0.4511", which is 2014 with SP1 and CU12

    .EXAMPLE
        Test-DbaSqlBuild -Build "12.0.5540" -MaxBehind "0CU"

        Returns information about a build identified by "12.0.5540", making sure it is the latest CU release.

    .EXAMPLE
        Test-DbaSqlBuild -Build "12.0.5540" -Latest

        Same as previous, returns information about a build identified by "12.0.5540", making sure it is the latest build available.

    .EXAMPLE
        Test-DbaSqlBuild -Build "12.00.4502" -MinimumBuild "12.0.4511" -Update

        Same as before, but tries to fetch the most up to date index online. When the online version is newer, the local one gets overwritten

    .EXAMPLE
        Test-DbaSqlBuild -Build "12.0.4502","10.50.4260" -MinimumBuild "12.0.4511"

        Returns information builds identified by these versions strings

    .EXAMPLE
        Get-DbaRegisteredServer -SqlInstance sqlserver2014a | Test-DbaSqlBuild -MinimumBuild "12.0.4511"

        Integrate with other commandlets to have builds checked for all your registered servers on sqlserver2014a

    .NOTES
        Author: niphlod
        Editor: Fred
        Tags: SqlBuild

        dbatools PowerShell module (https://dbatools.io, [email protected])
        Copyright (C) 2016 Chrissy LeMaire
        License: MIT https://opensource.org/licenses/MIT

    .LINK
        https://dbatools.io/Test-DbaSqlBuild
#>
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
    [CmdletBinding()]
    param (
        [version[]]
        $Build,

        [version]
        $MinimumBuild,

        [string]
        $MaxBehind,

        [switch]
        $Latest,

        [parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]
        $SqlInstance,

        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,

        [switch]
        $Update,

        [switch]
        $Quiet,

        [switch]
        [Alias('Silent')]$EnableException
    )

    begin {
        #region Helper functions
        function Get-DbaSqlBuildReferenceIndex {
            [CmdletBinding()]

            $DbatoolsData = Get-DbaConfigValue -Name 'Path.DbatoolsData'
            $writable_idxfile = Join-Path $DbatoolsData "dbatools-buildref-index.json"
            $result = Get-Content $writable_idxfile -Raw | ConvertFrom-Json
            $result.Data | Select-Object @{ Name = "VersionObject"; Expression = { [version]$_.Version } }, *
        }

        $ComplianceSpec = @()
        $ComplianceSpecExclusiveParams = @('MinimumBuild', 'MaxBehind', 'Latest')
        foreach ($exclParam in $ComplianceSpecExclusiveParams) {
            if (Test-Bound -Parameter $exclParam) { $ComplianceSpec += $exclParam }
        }
        if ($ComplianceSpec.Length -gt 1) {
            Stop-Function -Category InvalidArgument -Message "-MinimumBuild, -MaxBehind and -Latest are mutually exclusive. Please choose only one. Quitting."
            return
        }
        if ($ComplianceSpec.Length -eq 0) {
            Stop-Function -Category InvalidArgument -Message "You need to choose one from -MinimumBuild, -MaxBehind and -Latest. Quitting."
            return
        }
        try {
            # Empty call just to make sure the buildref is updated and on the right path
            Get-DbaSqlBuildReference -Update:$Update -EnableException:$true
            $IdxRef = Get-DbaSqlBuildReferenceIndex
        }
        catch {
            Stop-Function -Message "Error loading SQL build reference" -ErrorRecord $_
            return
        }
        if ($MaxBehind) {
            $MaxBehindValidator = [regex]'^(?<howmany>[\d]+)(?<what>SP|CU)$'
            $pieces = $MaxBehind.Split(' ')	| Where-Object { $_ }
            try {
                $ParsedMaxBehind = @{}
                foreach ($piece in $pieces) {
                    $pieceMatch = $MaxBehindValidator.Match($piece)
                    if ($pieceMatch.Success -ne $true) {
                        Stop-Function -Message "MaxBehind has an invalid syntax ('$piece' could not be parsed correctly)" -ErrorRecord $_
                        return
                    }
                    else {
                        $howmany = [int]$pieceMatch.Groups['howmany'].Value
                        $what = $pieceMatch.Groups['what'].Value
                        if ($ParsedMaxBehind.ContainsKey($what)) {
                            Stop-Function -Message "The specifier $what has been already passed" -ErrorRecord $_
                            return
                        }
                        else {
                            $ParsedMaxBehind[$what] = $howmany
                        }
                    }
                }
                if (-not $ParsedMaxBehind.ContainsKey('SP')) {
                    $ParsedMaxBehind['SP'] = 0
                }
            }
            catch {
                Stop-Function -Message "Error parsing MaxBehind" -ErrorRecord $_
                return
            }
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }
        $hiddenProps = @()
        if (-not $SqlInstance) {
            $hiddenProps += 'SqlInstance'
        }
        if ($MinimumBuild) {
            $hiddenProps += 'MaxBehind', 'SPTarget', 'CUTarget', 'BuildTarget'
        }
        elseif ($MaxBehind -or $Latest) {
            $hiddenProps += 'MinimumBuild'
        }
        $BuildVersions = Get-DbaSqlBuildReference -Build $Build -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Update:$Update -EnableException:$EnableException
        foreach ($BuildVersion in $BuildVersions) {
            $inputbuild = $BuildVersion.Build
            $compliant = $false
            $targetSPName = $null
            $targetCUName = $null
            if ($BuildVersion.MatchType -eq 'Approximate') {
                Stop-Function -Message "$($BuildVersion.Build) is not recognized as a correct version" -ErrorRecord $_ -Continue
            }
            if ($MinimumBuild) {
                Write-Message -Level Debug -Message "Comparing $MinimumBuild to $inputbuild"
                if ($inputbuild -ge $MinimumBuild) {
                    $compliant = $true
                }
            }
            elseif ($MaxBehind -or $Latest) {
                $IdxVersion = $IdxRef | Where-Object Version -like "$($inputbuild.Major).$($inputbuild.Minor).*"
                $lastsp = ''
                $SPsAndCUs = @()
                foreach ($el in $IdxVersion) {
                    if ($null -ne $el.SP) {
                        $lastsp = $el.SP | Where-Object { $_ -ne 'LATEST' }
                        $SPsAndCUs += @{
                            VersionObject = $el.VersionObject
                            SP            = $lastsp
                        }
                    }
                    if ($null -ne $el.CU) {
                        $SPsAndCUs += @{
                            VersionObject = $el.VersionObject
                            SP            = $lastsp
                            CU            = $el.CU
                        }
                    }
                }
                $targetedBuild = $SPsAndCUs[0]
                if ($Latest) {
                    $targetedBuild = $IdxVersion[$IdxVersion.Length - 1]
                }
                else {
                    if ($ParsedMaxBehind.ContainsKey('SP')) {
                        $AllSPs = $SPsAndCUs.SP | Select-Object -Unique
                        $targetSP = $AllSPs.Length - $ParsedMaxBehind['SP'] - 1
                        if ($targetSP -lt 0) {
                            $targetSP = 0
                        }
                        $targetSPName = $AllSPs[$targetSP]
                        Write-Message -Level Debug -Message "Target SP is $targetSPName - $targetSP on $($AllSPs.Length)"
                        $targetedBuild = $SPsAndCUs | Where-Object SP -eq $targetSPName | Select-Object -First 1
                    }
                    if ($ParsedMaxBehind.ContainsKey('CU')) {
                        $AllCUs = ($SPsAndCUs | Where-Object VersionObject -gt $targetedBuild.VersionObject).CU | Select-Object -Unique
                        if ($AllCUs.Length -gt 0) {
                            #CU after the targeted build available
                            $targetCU = $AllCUs.Length - $ParsedMaxBehind['CU'] - 1
                            if ($targetCU -lt 0) {
                                $targetCU = 0
                            }
                            $targetCUName = $AllCUs[$targetCU]
                            Write-Message -Level Debug -Message "Target CU is $targetCUName - $targetCU on $($AllCUs.Length)"
                            $targetedBuild = $SPsAndCUs | Where-Object VersionObject -gt $targetedBuild.VersionObject | Where-Object CU -eq $targetCUName | Select-Object -First 1
                        }
                    }
                }
                if ($inputbuild -ge $targetedBuild.VersionObject) {
                    $compliant = $true
                }
            }
            Add-Member -InputObject $BuildVersion -MemberType NoteProperty -Name Compliant -Value $compliant
            Add-Member -InputObject $BuildVersion -MemberType NoteProperty -Name MinimumBuild -Value $MinimumBuild
            Add-Member -InputObject $BuildVersion -MemberType NoteProperty -Name MaxBehind -Value $MaxBehind
            Add-Member -InputObject $BuildVersion -MemberType NoteProperty -Name SPTarget -Value $targetSPName
            Add-Member -InputObject $BuildVersion -MemberType NoteProperty -Name CUTarget -Value $targetCUName
            Add-Member -InputObject $BuildVersion -MemberType NoteProperty -Name BuildTarget -Value $targetedBuild.VersionObject
            if ($Quiet) {
                $BuildVersion.Compliant
            }
            else {
                $BuildVersion | Select-Object * | Select-DefaultView -ExcludeProperty $hiddenProps
            }
        }
    }
}
tools\dbatools\functions\Test-DbaSqlManagementObject.ps1
function Test-DbaSqlManagementObject {
    <#
        .SYNOPSIS
            Tests to see if the SMO version specified exists on the computer.

        .DESCRIPTION
            The Test-DbaSqlManagementObject returns True if the Version is on the computer, and False if it does not exist.

        .PARAMETER ComputerName
            The name of the target you would like to check

        .PARAMETER Credential
            This command uses Windows credentials. This parameter allows you to connect remotely as a different user.

        .PARAMETER VersionNumber
            This is the specific version number you are looking for and the return will be True.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: SMO
            Author: Ben Miller (@DBAduck - http://dbaduck.com)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaSqlManagementObject

        .EXAMPLE
            Test-DbaSqlManagementObject -VersionNumber 13

            Returns True if the version exists, if it does not exist it will return False
    #>
    [CmdletBinding()]
    param (
        [parameter(ValueFromPipeline)]
        [Alias("ServerInstance", "SqlServer", "SqlInstance")]
        [DbaInstance[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [Parameter(Mandatory)]
        [int[]]$VersionNumber,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        $scriptblock = {
            foreach ($number in $args) {
                $smoList = (Get-ChildItem -Path "$($env:SystemRoot)\assembly\GAC_MSIL\Microsoft.SqlServer.Smo" -Filter "$number.*" | Sort-Object Name -Descending).Name

                if ($smoList) {
                    [pscustomobject]@{
                        ComputerName = $env:COMPUTERNAME
                        Version      = $number
                        Exists       = $true
                    }
                }
                else {
                    [pscustomobject]@{
                        ComputerName = $env:COMPUTERNAME
                        Version      = $number
                        Exists       = $false
                    }
                }
            }
        }
    }
    process {
        foreach ($computer in $ComputerName.ComputerName) {
            try {
                Invoke-Command2 -ComputerName $computer -ScriptBlock $scriptblock -Credential $Credential -ArgumentList $VersionNumber -ErrorAction Stop
            }
            catch {
                Stop-Function -Continue -Message "Failure" -ErrorRecord $_ -Target $computer -Continue
            }
        }
    }
}
tools\dbatools\functions\Test-DbaSqlPath.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Test-DbaSqlPath {
    <#
        .SYNOPSIS
            Tests if file or directory exists from the perspective of the SQL Server service account.

        .DESCRIPTION
            Uses master.dbo.xp_fileexist to determine if a file or directory exists.

        .PARAMETER SqlInstance
            The SQL Server you want to run the test on.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Path
            The Path to test. This can be a file or directory

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.


        .NOTES
            Tags: Path, ServiceAccount
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: Admin access to server (not SQL Services),
            Remoting must be enabled and accessible if $SqlInstance is not local

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaSqlPath

        .EXAMPLE
            Test-DbaSqlPath -SqlInstance sqlcluster -Path L:\MSAS12.MSSQLSERVER\OLAP

            Tests whether the service account running the "sqlcluster" SQL Server instance can access L:\MSAS12.MSSQLSERVER\OLAP. Logs into sqlcluster using Windows credentials.

        .EXAMPLE
            $credential = Get-Credential
            Test-DbaSqlPath -SqlInstance sqlcluster -SqlCredential $credential -Path L:\MSAS12.MSSQLSERVER\OLAP

            Tests whether the service account running the "sqlcluster" SQL Server instance can access L:\MSAS12.MSSQLSERVER\OLAP. Logs into sqlcluster using SQL authentication.

    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Parameter(Mandatory = $true)]
        [object]$Path,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        foreach ($instance in $SqlInstance) {
            try {
                Write-Message -Level VeryVerbose -Message "Connecting to $instance." -Target $instance
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
            }
            catch {
                Stop-Function -Message "Failure" -ErrorRecord $_ -Target $instance -Continue
            }
            $counter = [pscustomobject] @{ Value = 0 }
            $groupSize = 100
            $RawPath = $Path
            $Path = [string[]]$Path
            $groups = $Path | Group-Object -Property { [math]::Floor($counter.Value++ / $groupSize) }
            foreach ($g in $groups) {
                $PathsBatch = $g.Group
                $query = @()
                foreach ($p in $PathsBatch) {
                    $query += "EXEC master.dbo.xp_fileexist '$p'"
                }
                $sql = $query -join ';'
                $batchresult = $server.ConnectionContext.ExecuteWithResults($sql)
                if ($Path.Count -eq 1 -and $SqlInstance.Count -eq 1 -and (-not($RawPath -is [array]))) {
                    if ($batchresult.Tables.rows[0] -eq $true -or $batchresult.Tables.rows[1] -eq $true) {
                        return $true
                    }
                    else {
                        return $false
                    }
                }
                else {
                    $i = 0
                    foreach ($r in $batchresult.tables.rows) {
                        $DoesPass = $r[0] -eq $true -or $r[1] -eq $true
                        [pscustomobject]@{
                            SqlInstance  = $server.Name
                            InstanceName = $server.ServiceName
                            ComputerName = $server.ComputerName
                            FilePath     = $PathsBatch[$i]
                            FileExists   = $DoesPass
                            IsContainer  = $r[1] -eq $true
                        }
                        $i += 1
                    }
                }
            }
        }

    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Test-SqlPath
    }
}
tools\dbatools\functions\Test-DbaTempDbConfiguration.ps1
function Test-DbaTempDbConfiguration {
    <#
        .SYNOPSIS
            Evaluates tempdb against several rules to match best practices.

        .DESCRIPTION
            Evaluates tempdb against a set of rules to match best practices. The rules are:

            * TF 1118 enabled - Is Trace Flag 1118 enabled (See KB328551).
            * File Count - Does the count of data files in tempdb match the number of logical cores, up to 8?
            * File Growth - Are any files set to have percentage growth? Best practice is all files have an explicit growth value.
            * File Location - Is tempdb located on the C:\? Best practice says to locate it elsewhere.
            * File MaxSize Set (optional) - Do any files have a max size value? Max size could cause tempdb problems if it isn't allowed to grow.

            Other rules can be added at a future date.

        .PARAMETER SqlInstance
            The SQL Server Instance to connect to. SQL Server 2005 and higher are supported.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Detailed
            Output all properties, will be depreciated in 1.0.0 release.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: tempdb, configuration
            Author: Michael Fal (@Mike_Fal), http://mikefal.net
            Based off of Amit Bannerjee's (@banerjeeamit) Get-TempDB function (https://github.com/amitmsft/SqlOnAzureVM/blob/master/Get-TempdbFiles.ps1)

            dbatools PowerShell module (https://dbatools.io, [email protected])
            Copyright (C) 2016 Chrissy LeMaire
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaTempDbConfiguration

        .EXAMPLE
            Test-DbaTempDbConfiguration -SqlInstance localhost

            Checks tempdb on the localhost machine.

        .EXAMPLE
            Test-DbaTempDbConfiguration -SqlInstance localhost | Select-Object *

            Checks tempdb on the localhost machine. All rest results are shown.
    #>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [switch]$Detailed,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter Detailed

        $result = @()
    }
    process {
        foreach ($instance in $SqlInstance) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            #test for TF 1118
            if ($server.VersionMajor -ge 13) {
                $notes = 'SQL Server 2016 has this functionality enabled by default'
                # DBA May have changed setting. May need to check.
                $value = [PSCustomObject]@{
                    ComputerName   = $server.ComputerName
                    InstanceName   = $server.ServiceName
                    SqlInstance    = $server.DomainInstanceName
                    Rule           = 'TF 1118 Enabled'
                    Recommended    = $true
                    CurrentSetting = $true
                }
            }
            else {
                $sql = "DBCC TRACEON (3604);DBCC TRACESTATUS(-1)"
                $tfCheck = $server.Databases['tempdb'].Query($sql)
                $notes = 'KB328551 describes how TF 1118 can benefit performance.'

                $value = [PSCustomObject]@{
                    ComputerName   = $server.ComputerName
                    InstanceName   = $server.ServiceName
                    SqlInstance    = $server.DomainInstanceName
                    Rule           = 'TF 1118 Enabled'
                    Recommended    = $true
                    CurrentSetting = ($tfCheck.TraceFlag -join ',').Contains('1118')
                }
            }

            if ($value.Recommended -ne $value.CurrentSetting -and $null -ne $value.Recommended) {
                $isBestPractice = $false
            }
            else {
                $isBestPractice = $true
            }

            Add-Member -Force -InputObject $value -MemberType NoteProperty -Name IsBestPractice -Value $isBestPractice
            Add-Member -Force -InputObject $value -MemberType NoteProperty -Name Notes -Value $notes
            $result += $value
            Write-Message -Level Verbose -Message "TF 1118 evaluated"

            #get files and log files
            $tempdbFiles = Get-DbaDatabaseFile -SqlInstance $server -Database tempdb
            [array]$dataFiles = $tempdbFiles | Where-Object Type -ne 1
            $logFiles = $tempdbFiles | Where-Object Type -eq 1
            Write-Message -Level Verbose -Message "TempDB file objects gathered"

            $value = [PSCustomObject]@{
                ComputerName   = $server.ComputerName
                InstanceName   = $server.ServiceName
                SqlInstance    = $server.DomainInstanceName
                Rule           = 'File Count'
                Recommended    = [Math]::Min(8, $server.Processors)
                CurrentSetting = $dataFiles.Count
            }

            if ($value.Recommended -ne $value.CurrentSetting -and $null -ne $value.Recommended) {
                $isBestPractice = $false
            }
            else {
                $isBestPractice = $true
            }

            Add-Member -Force -InputObject $value -MemberType NoteProperty -Name IsBestPractice -Value $isBestPractice
            Add-Member -Force -InputObject $value -MemberType NoteProperty -Name Notes -Value 'Microsoft recommends that the number of tempdb data files is equal to the number of logical cores up to 8.'
            $result += $value

            Write-Message -Level Verbose -Message "File counts evaluated."

            #test file growth
            $percData = $dataFiles | Where-Object GrowthType -ne 'KB' | Measure-Object
            $percLog = $logFiles  | Where-Object GrowthType -ne 'KB' | Measure-Object

            $totalCount = $percData.Count + $percLog.Count
            if ($totalCount -gt 0) {
                $totalCount = $true
            }
            else {
                $totalCount = $false
            }

            $value = [PSCustomObject]@{
                ComputerName   = $server.ComputerName
                InstanceName   = $server.ServiceName
                SqlInstance    = $server.DomainInstanceName
                Rule           = 'File Growth in Percent'
                Recommended    = $false
                CurrentSetting = $totalCount
            }

            if ($value.Recommended -ne $value.CurrentSetting -and $null -ne $value.Recommended) {
                $isBestPractice = $false
            }
            else {
                $isBestPractice = $true
            }

            Add-Member -Force -InputObject $value -MemberType NoteProperty -Name IsBestPractice -Value $isBestPractice
            Add-Member -Force -InputObject $value -MemberType NoteProperty -Name Notes -Value 'Set file growth to explicit values, not by percent.'
            $result += $value

            Write-Message -Level Verbose -Message "File growth settings evaluated."
            #test file Location

            $cdata = ($dataFiles | Where-Object PhysicalName -like 'C:*' | Measure-Object).Count + ($logFiles | Where-Object PhysicalName -like 'C:*' | Measure-Object).Count
            if ($cdata -gt 0) {
                $cdata = $true
            }
            else {
                $cdata = $false
            }

            $value = [PSCustomObject]@{
                ComputerName   = $server.ComputerName
                InstanceName   = $server.ServiceName
                SqlInstance    = $server.DomainInstanceName
                Rule           = 'File Location'
                Recommended    = $false
                CurrentSetting = $cdata
            }

            if ($value.Recommended -ne $value.CurrentSetting -and $null -ne $value.Recommended) {
                $isBestPractice = $false
            }
            else {
                $isBestPractice = $true
            }

            Add-Member -Force -InputObject $value -MemberType NoteProperty -Name IsBestPractice -Value $isBestPractice
            Add-Member -Force -InputObject $value -MemberType NoteProperty -Name Notes -Value "Do not place your tempdb files on C:\."
            $result += $value

            Write-Message -Level Verbose -Message "File locations evaluated."

            #Test growth limits
            $growthLimits = ($dataFiles | Where-Object MaxSize -gt 0 | Measure-Object).Count + ($logFiles | Where-Object MaxSize -gt 0 | Measure-Object).Count
            if ($growthLimits -gt 0) {
                $growthLimits = $true
            }
            else {
                $growthLimits = $false
            }

            $value = [PSCustomObject]@{
                ComputerName   = $server.ComputerName
                InstanceName   = $server.ServiceName
                SqlInstance    = $server.DomainInstanceName
                Rule           = 'File MaxSize Set'
                Recommended    = $false
                CurrentSetting = $growthLimits
            }

            if ($value.Recommended -ne $value.CurrentSetting -and $null -ne $value.Recommended) {
                $isBestPractice = $false
            }
            else {
                $isBestPractice = $true
            }

            Add-Member -Force -InputObject $value -MemberType NoteProperty -Name IsBestPractice -Value $isBestPractice
            Add-Member -Force -InputObject $value -MemberType NoteProperty -Name Notes -Value "Consider setting your tempdb files to unlimited growth."
            $result += $value

            Write-Message -Level Verbose -Message "MaxSize values evaluated."

            Select-DefaultView -InputObject $result -Property ComputerName, InstanceName, SqlInstance, Rule, Recommended, IsBestPractice
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Test-SqlTempDbConfiguration
    }
}
tools\dbatools\functions\Test-DbaWindowsLogin.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Test-DbaWindowsLogin {
    <#
        .SYNOPSIS
            Test-DbaWindowsLogin finds any logins on SQL instance that are AD logins with either disabled AD user accounts or ones that no longer exist

        .DESCRIPTION
            The purpose of this function is to find SQL Server logins that are used by active directory users that are either disabled or removed from the domain. It allows you to keep your logins accurate and up to date by removing accounts that are no longer needed.

        .PARAMETER SqlInstance
            The SQL Server instance you're checking logins on. You must have sysadmin access and server version must be SQL Server version 2000 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Login
            Specifies a list of logins to include in the results. Options for this list are auto-populated from the server.

        .PARAMETER ExcludeLogin
            Specifies a list of logins to exclude from the results. Options for this list are auto-populated from the server.

        .PARAMETER FilterBy
            Specifies the object types to return. By default, both Logins and Groups are returned. Valid options for this parameter are 'GroupsOnly' and 'LoginsOnly'.

        .PARAMETER IgnoreDomains
            Specifies a list of Active Directory domains to ignore. By default, all domains in the forest as well as all trusted domains are traversed.

        .PARAMETER Detailed
            Output all properties, will be depreciated in 1.0.0 release.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Login, Security
            Author: Stephen Bennett: https://sqlnotesfromtheunderground.wordpress.com/
            Author: Chrissy LeMaire (@cl), netnerds.net

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Test-DbaWindowsLogin

        .EXAMPLE
            Test-DbaWindowsLogin -SqlInstance Dev01

            Tests all logins in the current Active Directory domain that are either disabled or do not exist on the SQL Server instance Dev01

        .EXAMPLE
            Test-DbaWindowsLogin -SqlInstance Dev01 -FilterBy GroupsOnly | Select-Object -Property *

            Tests all Active Directory groups that have logins on Dev01, and shows all information for those logins

        .EXAMPLE
            Test-DbaWindowsLogin -SqlInstance Dev01 -IgnoreDomains testdomain

            Tests all Domain logins excluding any that are from the testdomain

    #>
    [CmdletBinding()]
    Param (
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer", "SqlServers")]
        [DbaInstanceParameter[]]$SqlInstance,
        [PSCredential]$SqlCredential,
        [object[]]$Login,
        [object[]]$ExcludeLogin,
        [ValidateSet("LoginsOnly", "GroupsOnly", "None")]
        [string]$FilterBy = "None",
        [string[]]$IgnoreDomains,
        [switch]$Detailed,
        [Alias('Silent')]
        [switch]$EnableException
    )

    begin {
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter Detailed

        if ($IgnoreDomains) {
            $IgnoreDomainsNormalized = $IgnoreDomains.ToUpper()
            Write-Message -Message ("Excluding logins for domains " + ($IgnoreDomains -join ',')) -Level Verbose
        }

        $mappingRaw = @{
            'SCRIPT'                                 = 1
            'ACCOUNTDISABLE'                         = 2
            'HOMEDIR_REQUIRED'                       = 8
            'LOCKOUT'                                = 16
            'PASSWD_NOTREQD'                         = 32
            'PASSWD_CANT_CHANGE'                     = 64
            'ENCRYPTED_TEXT_PASSWORD_ALLOWED'        = 128
            'TEMP_DUPLICATE_ACCOUNT'                 = 256
            'NORMAL_ACCOUNT'                         = 512
            'INTERDOMAIN_TRUST_ACCOUNT'              = 2048
            'WORKSTATION_TRUST_ACCOUNT'              = 4096
            'SERVER_TRUST_ACCOUNT'                   = 8192
            'DONT_EXPIRE_PASSWD'                     = 65536
            'MNS_LOGON_ACCOUNT'                      = 131072
            'SMARTCARD_REQUIRED'                     = 262144
            'TRUSTED_FOR_DELEGATION'                 = 524288
            'NOT_DELEGATED'                          = 1048576
            'USE_DES_KEY_ONLY'                       = 2097152
            'DONT_REQUIRE_PREAUTH'                   = 4194304
            'PASSWORD_EXPIRED'                       = 8388608
            'TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION' = 16777216
            'NO_AUTH_DATA_REQUIRED'                  = 33554432
            'PARTIAL_SECRETS_ACCOUNT'                = 67108864
        }
    }
    process {
        foreach ($instance in $SqlInstance) {
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
                Write-Message -Message "Connected to: $instance." -Level Verbose
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }


            # we can only validate AD logins
            $allWindowsLoginsGroups = $server.Logins | Where-Object { $_.LoginType -in ('WindowsUser', 'WindowsGroup') }

            # we cannot validate local users
            $allWindowsLoginsGroups = $allWindowsLoginsGroups | Where-Object { $_.Name.StartsWith("NT ") -eq $false -and $_.Name.StartsWith($server.ComputerName) -eq $false -and $_.Name.StartsWith("BUILTIN") -eq $false }
            if ($Login) {
                $allWindowsLoginsGroups = $allWindowsLoginsGroups | Where-Object Name -In $Login
            }
            if ($ExcludeLogin) {
                $allWindowsLoginsGroups = $allWindowsLoginsGroups | Where-Object Name -NotIn $ExcludeLogin
            }
            switch ($FilterBy) {
                "LoginsOnly" {
                    Write-Message -Message "Search restricted to logins." -Level Verbose
                    $windowsLogins = $allWindowsLoginsGroups | Where-Object LoginType -eq 'WindowsUser'
                }
                "GroupsOnly" {
                    Write-Message -Message "Search restricted to groups." -Level Verbose
                    $windowsGroups = $allWindowsLoginsGroups | Where-Object LoginType -eq 'WindowsGroup'
                }
                "None" {
                    Write-Message -Message "Search both logins and groups." -Level Verbose
                    $windowsLogins = $allWindowsLoginsGroups | Where-Object LoginType -eq 'WindowsUser'
                    $windowsGroups = $allWindowsLoginsGroups | Where-Object LoginType -eq 'WindowsGroup'
                }
            }
            foreach ($login in $windowsLogins) {
                $adLogin = $login.Name
                $loginSid = $login.Sid -join ''
                $domain, $username = $adLogin.Split("\")
                if ($domain.ToUpper() -in $IgnoreDomainsNormalized) {
                    Write-Message -Message "Skipping Login $adLogin." -Level Verbose
                    continue
                }
                Write-Message -Message "Parsing Login $adLogin." -Level Verbose
                $exists = $false
                try {
                    $u = Get-DbaADObject -ADObject $adLogin -Type User -EnableException
                    if ($null -eq $u -and $adLogin -like '*$'){
                        Write-Message -Message "Parsing Login as computer" -Level Verbose
                        $u = Get-DbaADObject -ADObject $adLogin -Type Computer -EnableException
                        $adType = 'Computer'
                    }
                    else {
                        $adType = 'User'
                    }
                    $foundUser = $u.GetUnderlyingObject()
                    $foundSid = $foundUser.ObjectSid.Value -join ''
                    if ($foundUser) {
                        $exists = $true
                    }
                    if ($foundSid -ne $loginSid) {
                        Write-Message -Message "SID mismatch detected for $adLogin." -Level Warning
                        Write-Message -Message "SID mismatch detected for $adLogin (MSSQL: $loginSid, AD: $foundSid)." -Level Debug
                        $exists = $false
                    }
                }
                catch {
                    Write-Message -Message "AD Searcher Error for $username." -Level Warning
                }

                $uac = $foundUser.Properties.UserAccountControl

                $additionalProps = @{
                    AccountNotDelegated               = $null
                    AllowReversiblePasswordEncryption = $null
                    CannotChangePassword              = $null
                    PasswordExpired                   = $null
                    LockedOut                         = $null
                    Enabled                           = $null
                    PasswordNeverExpires              = $null
                    PasswordNotRequired               = $null
                    SmartcardLogonRequired            = $null
                    TrustedForDelegation              = $null
                }
                if ($uac) {
                    $additionalProps = @{
                        AccountNotDelegated               = [bool]($uac.Value -band $mappingRaw['NOT_DELEGATED'])
                        AllowReversiblePasswordEncryption = [bool]($uac.Value -band $mappingRaw['ENCRYPTED_TEXT_PASSWORD_ALLOWED'])
                        CannotChangePassword              = [bool]($uac.Value -band $mappingRaw['PASSWD_CANT_CHANGE'])
                        PasswordExpired                   = [bool]($uac.Value -band $mappingRaw['PASSWORD_EXPIRED'])
                        LockedOut                         = [bool]($uac.Value -band $mappingRaw['LOCKOUT'])
                        Enabled                           = !($uac.Value -band $mappingRaw['ACCOUNTDISABLE'])
                        PasswordNeverExpires              = [bool]($uac.Value -band $mappingRaw['DONT_EXPIRE_PASSWD'])
                        PasswordNotRequired               = [bool]($uac.Value -band $mappingRaw['PASSWD_NOTREQD'])
                        SmartcardLogonRequired            = [bool]($uac.Value -band $mappingRaw['SMARTCARD_REQUIRED'])
                        TrustedForDelegation              = [bool]($uac.Value -band $mappingRaw['TRUSTED_FOR_DELEGATION'])
                        UserAccountControl                = $uac.Value
                    }
                }
                $rtn = [PSCustomObject]@{
                    Server                            = $server.DomainInstanceName
                    Domain                            = $domain
                    Login                             = $username
                    Type                              = $adType
                    Found                             = $exists
                    DisabledInSQLServer               = $login.IsDisabled
                    AccountNotDelegated               = $additionalProps.AccountNotDelegated
                    AllowReversiblePasswordEncryption = $additionalProps.AllowReversiblePasswordEncryption
                    CannotChangePassword              = $additionalProps.CannotChangePassword
                    PasswordExpired                   = $additionalProps.PasswordExpired
                    LockedOut                         = $additionalProps.LockedOut
                    Enabled                           = $additionalProps.Enabled
                    PasswordNeverExpires              = $additionalProps.PasswordNeverExpires
                    PasswordNotRequired               = $additionalProps.PasswordNotRequired
                    SmartcardLogonRequired            = $additionalProps.SmartcardLogonRequired
                    TrustedForDelegation              = $additionalProps.TrustedForDelegation
                    UserAccountControl                = $additionalProps.UserAccountControl
                }

                Select-DefaultView -InputObject $rtn -ExcludeProperty AccountNotDelegated, AllowReversiblePasswordEncryption, CannotChangePassword, PasswordNeverExpires, SmartcardLogonRequired, TrustedForDelegation, UserAccountControl

            }

            foreach ($login in $windowsGroups) {
                $adLogin = $login.Name
                $loginSid = $login.Sid -join ''
                $domain, $groupName = $adLogin.Split("\")
                if ($domain.ToUpper() -in $IgnoreDomainsNormalized) {
                    Write-Message -Message "Skipping Login $adLogin." -Level Verbose
                    continue
                }
                Write-Message -Message "Parsing Login $adLogin on $server." -Level Verbose
                $exists = $false
                try {
                    $u = Get-DbaADObject -ADObject $adLogin -Type Group -EnableException
                    $foundUser = $u.GetUnderlyingObject()
                    $foundSid = $foundUser.objectSid.Value -join ''
                    if ($foundUser) {
                        $exists = $true
                    }
                    if ($foundSid -ne $loginSid) {
                        Write-Message -Message "SID mismatch detected for $adLogin." -Level Warning
                        Write-Message -Message "SID mismatch detected for $adLogin (MSSQL: $loginSid, AD: $foundSid)." -Level Debug
                        $exists = $false
                    }
                }
                catch {
                    Write-Message -Message "AD Searcher Error for $groupName on $server" -Level Warning
                }
                $rtn = [PSCustomObject]@{
                    Server                            = $server.DomainInstanceName
                    Domain                            = $domain
                    Login                             = $groupName
                    Type                              = "Group"
                    Found                             = $exists
                    DisabledInSQLServer               = $login.IsDisabled
                    AccountNotDelegated               = $null
                    AllowReversiblePasswordEncryption = $null
                    CannotChangePassword              = $null
                    PasswordExpired                   = $null
                    LockedOut                         = $null
                    Enabled                           = $null
                    PasswordNeverExpires              = $null
                    PasswordNotRequired               = $null
                    SmartcardLogonRequired            = $null
                    TrustedForDelegation              = $null
                    UserAccountControl                = $null
                }

                Select-DefaultView -InputObject $rtn -ExcludeProperty AccountNotDelegated, AllowReversiblePasswordEncryption, CannotChangePassword, PasswordNeverExpires, SmartcardLogonRequired, TrustedForDelegation, UserAccountControl

            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Test-DbaValidLogin
    }
}
tools\dbatools\functions\Uninstall-DbaWatchUpdate.ps1
function Uninstall-DbaWatchUpdate {
    <#
        .SYNOPSIS
            Removes the scheduled task created for Watch-DbaUpdate by Install-DbaWatchUpdate so that notifications no longer pop up.

        .DESCRIPTION
            Removes the scheduled task created for Watch-DbaUpdate by Install-DbaWatchUpdate so that notifications no longer pop up.

        .NOTES
            Tags: JustForFun, Module
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Uninstall-DbaWatchUpdate

        .EXAMPLE
            Uninstall-DbaWatchUpdate

            Removes the scheduled task created by Install-DbaWatchUpdate.
    #>
    process {
        if (([Environment]::OSVersion).Version.Major -lt 10) {
            Write-Warning "This command only supports Windows 10 and higher."
            return
        }

        <# Does not utilize message system because of script block #>
        $script = {
            try {
                $task = Get-ScheduledTask -TaskName "dbatools version check" -ErrorAction SilentlyContinue

                if ($null -eq $task) {
                    Write-Warning "Task doesn't exist. Skipping removal."
                }
                else {
                    Write-Output "Removing watchupdate.xml."
                    $file = "$env:LOCALAPPDATA\dbatools\watchupdate.xml"
                    Remove-Item $file -ErrorAction SilentlyContinue

                    Write-Output "Removing Scheduled Task 'dbatools version check'."
                    $task | Unregister-ScheduledTask -Confirm:$false -ErrorAction Stop

                    Write-Output "Task removed"

                    Start-Sleep -Seconds 2
                }
            }
            catch {
                Write-Warning "Task could not be deleted. Please remove 'dbatools version check' manually."
            }
        }
        # Needs admin credentials to remove the task because of the way it was setup

        $task = Get-ScheduledTask -TaskName "dbatools version check" -ErrorAction SilentlyContinue

        if ($null -eq $task) {
            Write-Warning "dbatools update watcher is not installed."
            return
        }

        if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
            Write-Warning "Removal of this scheduled task requires elevated permissions."
            Start-Process powershell -Verb runAs -ArgumentList Uninstall-DbaWatchUpdate -Wait
        }
        else {
            Invoke-Command -ScriptBlock $script
        }

        Write-Output "All done!"
    }
}
tools\dbatools\functions\Update-DbaPowerBiDataSource.ps1
function Update-DbaPowerBiDataSource {
    <#
        .SYNOPSIS
            Converts the results of dbatools commands for our PowerBI Dashboard related commands. This command is specific to our toolset and not a general Power BI command.

        .DESCRIPTION
            Converts the results of dbatools commands for our PowerBI Dashboard related commands. This command is specific to our toolset and not a general Power BI command.

        .PARAMETER InputObject
            Enables piping

        .PARAMETER Path
            The directory to store your files. "C:\windows\temp\dbatools\" by default

        .PARAMETER Enviornment
            Tag your data with an enviornment. Defaults to "Default"

        .PARAMETER Append
            Don't delete previous default data sources.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .EXAMPLE
            Get-DbaPfDataCollectorSet -ComputerName sql2016 | Invoke-DbaPfRelog -AllowClobber | Update-DbaPowerBiDataSource | Start-DbaPowerBi

            Converts the results of the performance monitor data source and stores it in the appropriate directory then launches our Power BI dashboard

    #>
    [CmdletBinding()]
    param (
        [parameter(ValueFromPipeline, Mandatory)]
        [pscustomobject]$InputObject,
        [string]$Path = "$env:windir\temp\dbatools",
        [string]$Enviornment = "Default",
        [switch]$Append,
        [switch]$EnableException
    )
    begin {
        if ($Environment -ne "Default" -and -not $Append) {
            $null = Remove-Item "$Path\*Default*.*sv" -ErrorAction SilentlyContinue
        }
        $orginalpath = $Path
    }
    process {
        ++$i

        if ($InputObject.RelogFile) {
            $Path = "$orginalpath\perfmon"
        }
        else {
            $Path = "$orginalpath\xevents"
        }

        try {
            if (-not (Test-Path -Path $Path)) {
                $null = New-Item -ItemType Directory -Path $Path -ErrorAction Stop
            }
        }
        catch {
            Stop-Function -Message "Failure" -Exception $_
            return
        }

        $extension = $InputObject.Extension.TrimStart(".")
        $basename = "dbatools_$i"
        if ($InputObject.TagFilter) {
            $basename = "$basename`_$($InputObject.TagFilter -join "_")"
        }

        if ($Enviornment) {
            $basename = "$basename`_$Enviornment"
        }

        $filename = "$basename.$extension"

        try {
            Write-Message -Level Verbose -Message "Writing $filename to $path"
            $inputObject | Copy-Item -Destination "$path\$filename"
            Get-ChildItem "$path\$filename"
        }
        catch {
            Stop-Function -Message "Failure" -ErrorRecord $_
            return
        }
    }
    end {
        if ($InputObject -isnot [System.IO.FileInfo] -and $InputObject -isnot [System.IO.DirectoryInfo]) {
            Stop-Function -Message "Invalid input"
            return
        }
    }
}
tools\dbatools\functions\Update-DbaSqlServiceAccount.ps1
function Update-DbaSqlServiceAccount {
    <#
        .SYNOPSIS
            Changes service account (or just its password) of the SQL Server service.

        .DESCRIPTION
            Reconfigure the service account or update the password of the specified SQL Server service. The service will be restarted in the event of changing the account.

        .PARAMETER ComputerName
            The SQL Server (or server in general) that you're connecting to. This command handles named instances.

        .PARAMETER Credential
            Windows Credential with permission to log on to the server running the SQL instance

        .PARAMETER InputObject
            A collection of services. Basically, any object that has ComputerName and ServiceName properties. Can be piped from Get-DbaSqlService.

        .PARAMETER ServiceName
            A name of the service on which the action is performed. E.g. MSSQLSERVER or SqlAgent$INSTANCENAME

        .PARAMETER ServiceCredential
            Windows Credential object under which the service will be setup to run. Cannot be used with -Username. For local service accounts use one of the following usernames with empty password:
            LOCALSERVICE
            NETWORKSERVICE
            LOCALSYSTEM

        .PARAMETER OldPassword
            An old password of the service account. Optional when run under local admin privileges.

        .PARAMETER NewPassword
            New password of the service account. The function will ask for a password if not specified. MSAs and local system accounts will ignore the password.

        .PARAMETER Username
            Username of the service account. Cannot be used with -ServiceCredential. For local service accounts use one of the following usernames omitting the -Password parameter:
            LOCALSERVICE
            NETWORKSERVICE
            LOCALSYSTEM

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Service, SqlServer, Instance, Connect
            Author: Kirill Kravtsov (@nvarscar)

            Requires Local Admin rights on destination computer(s).

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            $NewPassword = ConvertTo-SecureString 'Qwerty1234' -AsPlainText -Force
            Update-DbaSqlServiceAccount -ComputerName sql1 -ServiceName 'MSSQL$MYINSTANCE' -Password $NewPassword

            Changes the current service account's password of the service MSSQL$MYINSTANCE to 'Qwerty1234'

        .EXAMPLE
            $cred = Get-Credential
            Get-DbaSqlService sql1 -Type Engine,Agent -Instance MYINSTANCE | Update-DbaSqlServiceAccount -ServiceCredential $cred

            Requests credentials from the user and configures them as a service account for the SQL Server engine and agent services of the instance sql1\MYINSTANCE

        .EXAMPLE
            Update-DbaSqlServiceAccount -ComputerName sql1,sql2 -ServiceName 'MSSQLSERVER','SQLSERVERAGENT' -Username NETWORKSERVICE

            Configures SQL Server engine and agent services on the machines sql1 and sql2 to run under Network Service system user.

        .EXAMPLE
            Get-DbaSqlService sql1 -Type Engine -Instance MSSQLSERVER | Update-DbaSqlServiceAccount -Username 'MyDomain\sqluser1'

            Configures SQL Server engine service on the machine sql1 to run under 'MyDomain\sqluser1'. Will request user to input the account password.
    #>
    [CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = "ServiceName" )]
    param (
        [parameter(ParameterSetName = "ServiceName")]
        [Alias("cn", "host", "Server")]
        [DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential,
        [parameter(ValueFromPipeline = $true, Mandatory = $true, ParameterSetName = "InputObject")]
        [Alias("ServiceCollection")]
        [object[]]$InputObject,
        [parameter(ParameterSetName = "ServiceName", Position = 1, Mandatory = $true)]
        [Alias("Name", "Service")]
        [string[]]$ServiceName,
        [Alias("User")]
        [string]$Username,
        [PSCredential]$ServiceCredential,
        [securestring]$OldPassword = (New-Object System.Security.SecureString),
        [Alias("Password")]
        [securestring]$NewPassword = (New-Object System.Security.SecureString),
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        $svcCollection = @()
        $scriptAccountChange = {
            $service = $wmi.Services[$args[0]]
            $service.SetServiceAccount($args[1], $args[2])
            $service.Alter()
        }
        $scriptPasswordChange = {
            $service = $wmi.Services[$args[0]]
            $service.ChangePassword($args[1], $args[2])
            $service.Alter()
        }
        #Check parameters
        if ($Username) {
            $actionType = 'Account'
            if ($ServiceCredential) {
                Stop-Function -EnableException $EnableException -Message "You cannot specify both -UserName and -ServiceCredential parameters" -Category InvalidArgument
                return
            }
            #System logins should not have a domain name, whitespaces or passwords
            $trimmedUsername = (Split-Path $Username -Leaf).Trim().Replace(' ', '')
            #Request password input if password was not specified and account is not MSA or system login
            if ($NewPassword.Length -eq 0 -and $PSBoundParameters.Keys -notcontains 'NewPassword' -and $trimmedUsername -notin 'NETWORKSERVICE', 'LOCALSYSTEM', 'LOCALSERVICE' -and $Username.EndsWith('$') -eq $false -and $Username.StartsWith('NT Service\') -eq $false) {
                $NewPassword = Read-Host -Prompt "Input new password for account $UserName" -AsSecureString
                $NewPassword2 = Read-Host -Prompt "Repeat password" -AsSecureString
                if ((New-Object System.Management.Automation.PSCredential ("user", $NewPassword)).GetNetworkCredential().Password -ne `
                    (New-Object System.Management.Automation.PSCredential ("user", $NewPassword2)).GetNetworkCredential().Password) {
                    Stop-Function -Message "Passwords do not match" -Category InvalidArgument -EnableException $EnableException
                    return
                }
            }
            $currentCredential = New-Object System.Management.Automation.PSCredential ($Username, $NewPassword)
        }
        elseif ($ServiceCredential) {
            $actionType = 'Account'
            $currentCredential = $ServiceCredential
        }
        else {
            $actionType = 'Password'
        }
        if ($actionType -eq 'Account') {
            #System logins should not have a domain name, whitespaces or passwords
            $credUserName = (Split-Path $currentCredential.UserName -Leaf).Trim().Replace(' ', '')
            #Check for system logins and replace the Credential object to simplify passing localsystem-like login names
            if ($credUserName -in 'NETWORKSERVICE', 'LOCALSYSTEM', 'LOCALSERVICE') {
                $currentCredential = New-Object System.Management.Automation.PSCredential ($credUserName, (New-Object System.Security.SecureString))
            }
        }
    }
    process {
        if ($PsCmdlet.ParameterSetName -match 'ServiceName') {
            foreach ($Computer in $ComputerName.ComputerName) {
                $Server = Resolve-DbaNetworkName -ComputerName $Computer -Credential $credential
                if ($Server.ComputerName) {
                    foreach ($service in $ServiceName) {
                        $svcCollection += [psobject]@{
                            ComputerName = $server.ComputerName
                            ServiceName  = $service
                        }
                    }
                }
                else {
                    Stop-Function -EnableException $EnableException -Message "Failed to connect to $Computer" -Continue
                }
            }
        }
        elseif ($PsCmdlet.ParameterSetName -match 'InputObject') {
            foreach ($service in $InputObject) {
                $Server = Resolve-DbaNetworkName -ComputerName $service.ComputerName -Credential $credential
                if ($Server.ComputerName) {
                    $svcCollection += [psobject]@{
                        ComputerName = $Server.ComputerName
                        ServiceName  = $service.ServiceName
                    }
                }
                else {
                    Stop-Function -EnableException $EnableException -Message "Failed to connect to $($service.ComputerName)" -Continue
                }
            }
        }

    }
    end {
        foreach ($svc in $svcCollection) {
            if ($serviceObject = Get-DbaSqlService -ComputerName $svc.ComputerName -ServiceName $svc.ServiceName -Credential $Credential -EnableException:$EnableException) {
                $outMessage = $outStatus = $agent = $null
                if ($actionType -eq 'Password' -and $NewPassword.Length -eq 0) {
                    $currentPassword = Read-Host -Prompt "New password for $($serviceObject.StartName) ($($svc.ServiceName) on $($svc.ComputerName))" -AsSecureString
                    $currentPassword2 = Read-Host -Prompt "Repeat password" -AsSecureString
                    if ((New-Object System.Management.Automation.PSCredential ("user", $currentPassword)).GetNetworkCredential().Password -ne `
                        (New-Object System.Management.Automation.PSCredential ("user", $currentPassword2)).GetNetworkCredential().Password) {
                        Stop-Function -Message "Passwords do not match. This service will not be updated" -Category InvalidArgument -EnableException $EnableException -Continue
                    }
                }
                else {
                    $currentPassword = $NewPassword
                }
                if ($serviceObject.ServiceType -eq 'Engine') {
                    #Get SQL Agent running status
                    $agent = Get-DbaSqlService -ComputerName $svc.ComputerName -Type Agent -InstanceName $serviceObject.InstanceName
                }
                if ($PsCmdlet.ShouldProcess($serviceObject, "Changing account information for service $($svc.ServiceName) on $($svc.ComputerName)")) {
                    try {
                        if ($actionType -eq 'Account') {
                            Write-Message -Level Verbose -Message "Attempting an account change for service $($svc.ServiceName) on $($svc.ComputerName)"
                            $null = Invoke-ManagedComputerCommand -ComputerName $svc.ComputerName -Credential $Credential -ScriptBlock $scriptAccountChange -ArgumentList @($svc.ServiceName, $currentCredential.UserName, $currentCredential.GetNetworkCredential().Password) -EnableException:$EnableException
                            $outMessage = "The login account for the service has been successfully set."
                        }
                        elseif ($actionType -eq 'Password') {
                            Write-Message -Level Verbose -Message "Attempting a password change for service $($svc.ServiceName) on $($svc.ComputerName)"
                            $null = Invoke-ManagedComputerCommand -ComputerName $svc.ComputerName -Credential $Credential -ScriptBlock $scriptPasswordChange -ArgumentList @($svc.ServiceName, (New-Object System.Management.Automation.PSCredential ("user", $OldPassword)).GetNetworkCredential().Password, (New-Object System.Management.Automation.PSCredential ("user", $currentPassword)).GetNetworkCredential().Password) -EnableException:$EnableException
                            $outMessage = "The password has been successfully changed."
                        }
                        $outStatus = 'Successful'
                    }
                    catch {
                        $outStatus = 'Failed'
                        $outMessage = $_.Exception.Message
                        Write-Message -Level Warning -Message $_.Exception.Message -EnableException $EnableException.ToBool()
                    }
                }
                else {
                    $outStatus = 'Successful'
                    $outMessage = 'No changes made - running in -WhatIf mode.'
                }
                if ($serviceObject.ServiceType -eq 'Engine' -and $actionType -eq 'Account' -and $outStatus -eq 'Successful' -and $agent.State -eq 'Running') {
                    #Restart SQL Agent after SQL Engine has been restarted
                    if ($PsCmdlet.ShouldProcess($serviceObject, "Starting SQL Agent after Engine account change on $($svc.ComputerName)")) {
                        $res = Start-DbaSqlService -ComputerName $svc.ComputerName -Type Agent -InstanceName $serviceObject.InstanceName
                        if ($res.Status -ne 'Successful') {
                            Write-Message -Level Warning -Message "Failed to restart SQL Agent after changing credentials. $($res.Message)"
                        }
                    }
                }
                $serviceObject = Get-DbaSqlService -ComputerName $svc.ComputerName -ServiceName $svc.ServiceName -Credential $Credential -EnableException:$EnableException
                Add-Member -Force -InputObject $serviceObject -NotePropertyName Message -NotePropertyValue $outMessage
                Add-Member -Force -InputObject $serviceObject -NotePropertyName Status -NotePropertyValue $outStatus
                Select-DefaultView -InputObject $serviceObject -Property ComputerName, ServiceName, State, StartName, Status, Message
            }
            Else {
                Stop-Function -Message "The service $($svc.ServiceName) has not been found on $($svc.ComputerName)" -EnableException $EnableException -Continue
            }
        }
    }
}
tools\dbatools\functions\Update-dbatools.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Update-Dbatools {
    <#
        .SYNOPSIS
            Exported function. Updates dbatools. Deletes current copy and replaces it with freshest copy.

        .DESCRIPTION
            Exported function. Updates dbatools. Deletes current copy and replaces it with freshest copy.

        .PARAMETER Development
            If this switch is enabled, the current development branch will be installed. By default, the latest official release is installed.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .NOTES
            Tags: Module
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Update-DbaTools

        .EXAMPLE
            Update-Dbatools

            Updates dbatools. Deletes current copy and replaces it with freshest copy.

        .EXAMPLE
            Update-Dbatools -dev

            Updates dbatools to the current development branch. Deletes current copy and replaces it with latest from github.
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param(
        [parameter(Mandatory = $false)]
        [Alias("dev", "devbranch")]
        [switch]$Development,
        [Alias('Silent')]
        [switch]$EnableException
    )
    $MyModuleBase = [SqlCollaborative.Dbatools.dbaSystem.SystemHost]::ModuleBase
    $InstallScript = join-path -path $MyModuleBase -ChildPath "install.ps1";
    if ($Development) {
        Write-Message -Level Verbose -Message "Installing dev/beta channel via $Installscript.";
        if ($PSCmdlet.ShouldProcess("development branch", "Updating dbatools")) {
            & $InstallScript -beta;
        }
    }
    else {
        Write-Message -Level Verbose -Message "Installing release version via $Installscript."
        if ($PSCmdlet.ShouldProcess("release branch", "Updating dbatools")) {
            & $InstallScript;
        }
    }
}
tools\dbatools\functions\Watch-DbaDbLogin.ps1
function Watch-DbaDbLogin {
    <#
        .SYNOPSIS
            Tracks SQL Server logins: which host they came from, what database they're using, and what program is being used to log in.

        .DESCRIPTION
            Watch-DbaDbLogin uses SQL Server DMVs to track logins into a SQL Server table. This is helpful when you need to migrate a SQL Server and update connection strings, but have inadequate documentation on which servers/applications are logging into your SQL instance.

            Running this script every 5 minutes for a week should give you a sufficient idea about database and login usage.

        .PARAMETER SqlInstance
            The SQL Server that stores the Watch database.

        .PARAMETER SqlCms
            Specifies a Central Management Server to query for a list of servers to watch.

        .PARAMETER ServersFromFile
            Specifies a file containing a list of servers to watch. This file must contain one server name per line.

        .PARAMETER Database
            The name of the Watch database.

        .PARAMETER Table
            The name of the Watch table. By default, this is DbaTools-WatchDbLogins.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: Login
            Author: Chrissy LeMaire (@cl), netnerds.net
            Requires: sysadmin access on all SQL Servers for the most accurate results

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Watch-DbaDbLogin

        .EXAMPLE
            Watch-DbaDbLogin -SqlInstance sqlserver -SqlCms SqlCms1

            A list of all database instances within the Central Management Server SqlCms1 is generated. Using this list, the script enumerates all the processes and gathers login information and saves it to the table Dblogins in the DatabaseLogins database on SQL Server sqlserver.

        .EXAMPLE
            Watch-DbaDbLogin -SqlInstance sqlcluster -Database CentralAudit -ServersFromFile .\sqlservers.txt

            A list of servers is gathered from the file sqlservers.txt in the current directory. Using this list, the script enumerates all the processes and gathers login information and saves it to the table Dblogins in the CentralAudit database on SQL Server sqlcluster.

        .EXAMPLE
            Watch-DbaDbLogin -SqlInstance sqlserver -SqlCms SqlCms1 -SqlCredential $cred

            A list of servers is generated using database instance names within the SQL2014Clusters group on the Central Management Server SqlCms1. Using this list, the script enumerates all the processes and gathers login information and saves it to the table Dblogins in the DatabaseLogins database on sqlserver.

    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    param (
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstance]$SqlInstance,
        [object]$Database,
        [string]$Table = "DbaTools-WatchDbLogins",
        [PSCredential]$SqlCredential,

        # Central Management Server
        [string]$SqlCms,

        # File with one server per line
        [string]$ServersFromFile,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        if (Test-Bound 'SqlCms', 'ServersFromFile' -Not) {
            Stop-Function -Message "You must specify a server list source using -SqlCms or -ServersFromFile"
            return
        }

        Write-Message -Level Verbose -Message "Connecting to $SqlInstance"
        try {
            $serverDest = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $SqlInstance -Continue
        }

        $systemdbs = "master", "msdb", "model", "tempdb"
        $excludedPrograms = "Microsoft SQL Server Management Studio - Query", "SQL Management"

        <#
            Get servers to query from Central Management Server or File
        #>
        if ($SqlCms) {
            try {
                $servers = Get-DbaRegisteredServerName -SqlInstance $SqlCms -SqlCredential $SqlCredential -EnableException
            }
            catch {
                Stop-Function -Message "The CMS server, $SqlCms, was not accessible." -Target $SqlCms -ErrorRecord $_
                return
            }
        }
        if (Test-Bound 'ServersFromFile') {
            if (Test-Path $ServersFromFile) {
                $servers = Get-Content $ServersFromFile
            }
            else {
                Stop-Function -Message "$ServersFromFile was not found." -Target $ServersFromFile
                return
            }
        }

        <#
            Process each server
        #>
        foreach ($instance in $servers) {
            Write-Message -Level Verbose -Message "Connecting to $instance"
            try {
                $server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
            }

            if (!(Test-SqlSa $server)) {
                Write-Warning "Not a sysadmin on $instance, resultset would be underwhelming. Skipping.";
                continue
            }

            $sql = "
            SELECT
                s.login_time AS [LoginTime]
                , s.login_name AS [Login]
                , ISNULL(s.host_name,N'') AS [Host]
                , ISNULL(s.program_name,N'') AS [Program]
                , ISNULL(r.database_id,N'') AS [DatabaseId]
                , ISNULL(DB_NAME(r.database_id),N'') AS [Database]
                , CAST(~s.is_user_process AS bit) AS [IsSystem]
                , CaptureTime = (SELECT GETDATE())
            FROM sys.dm_exec_sessions AS s
            LEFT OUTER JOIN sys.dm_exec_requests AS r
                ON r.session_id = s.session_id"
            Write-Message -Level Debug -Message $sql

            $procs = $server.Query($sql) | Where-Object { $_.Host -ne $instance.ComputerName -and ![string]::IsNullOrEmpty($_.Host) }
            $procs = $procs | Where-Object { $systemdbs -notcontains $_.Database -and $excludedPrograms -notcontains $_.Program }

            if ($procs.Count -gt 0) {
                $procs | Select-Object @{Label = "ComputerName"; Expression = {$server.ComputerName}}, @{Label = "InstanceName"; Expression = {$server.ServiceName}}, @{Label = "SqlInstance"; Expression = {$server.DomainInstanceName}}, LoginTime, Login, Host, Program, DatabaseId, Database, IsSystem, CaptureTime | ConvertTo-DbaDataTable | Write-DbaDataTable -SqlInstance $serverDest -Database $Database -Table $Table -AutoCreateTable

                Write-Output "Added process information for $instance to datatable."
            }
            else {
                Write-message -Level Verbose -Message "No data returned for $instance."
            }
        }
    }
    end {
        Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Watch-SqlDbLogin
    }
}
tools\dbatools\functions\Watch-DbaUpdate.ps1
function Watch-DbaUpdate {
    <#
        .SYNOPSIS
            Just for fun - checks the PowerShell Gallery every 1 hour for updates to dbatools. Notifies once per release.

        .DESCRIPTION
            Just for fun - checks the PowerShell Gallery every 1 hour for updates to dbatools. Notifies once max per release.

            Anyone know how to make it clickable so that it opens an URL?

        .NOTES
            Tags: JustForFun, Module
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Watch-DbaUpdate

        .EXAMPLE
            Watch-DbaUpdate

            Watches the gallery for updates to dbatools.
    #>
    [cmdletbinding()]
    param()
    process {
        if (([Environment]::OSVersion).Version.Major -lt 10) {
            Write-Warning "This command only supports Windows 10 and higher."
            return
        }

        if ($null -eq (Get-ScheduledTask -TaskName "dbatools version check" -ErrorAction SilentlyContinue)) {
            Install-DbaWatchUpdate
        }

        # leave this in for the scheduled task
        $module = Get-Module -Name dbatools

        if (-not $module) {
            Import-Module dbatools
            $module = Get-Module -Name dbatools
        }

        $galleryVersion = (Find-Module -Name dbatools -Repository PSGallery).Version
        $localVersion = $module.Version

        if ($galleryVersion -le $localVersion) { return }

        $file = "$env:LOCALAPPDATA\dbatools\watchupdate.xml"

        $new = [PSCustomObject]@{
            NotifyVersion = $galleryVersion
        }

        # now that notifications stay until they are checked, we just have to keep
        # track of the last version we notified about

        if (Test-Path $file) {
            $old = Import-Clixml -Path $file -ErrorAction SilentlyContinue

            if ($galleryVersion -gt $old.NotifyVersion) {
                Export-Clixml -InputObject $new -Path $file
                Show-Notification -GalleryVersion $galleryVersion
            }
        }
        else {
            $directory = Split-Path $file

            if (!(Test-Path $directory)) {
                $null = New-Item -ItemType Directory -Path $directory
            }

            Export-Clixml -InputObject $new -Path $file
            Show-Notification -GalleryVersion $galleryVersion
        }
    }
}
tools\dbatools\functions\Watch-DbaXESession.ps1
function Watch-DbaXESession {
    <#
        .SYNOPSIS
            Watch live XEvent Data as it happens

        .DESCRIPTION
            Watch live XEvent Data as it happens. This command runs until you stop the session, kill the PowerShell session, or Ctrl-C.

            Thanks to Dave Mason (@BeginTry) for some straightforward code samples https://itsalljustelectrons.blogspot.be/2017/01/SQL-Server-Extended-Event-Handling-Via-Powershell.html

        .PARAMETER SqlInstance
            Target SQL Server. You must have sysadmin access and server version must be SQL Server version 2008 or higher.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Session
            Only return a specific session. Options for this parameter are auto-populated from the server.

        .PARAMETER Raw
            If this switch is enabled, the Microsoft.SqlServer.XEvent.Linq.QueryableXEventData enumeration object is returned.

        .PARAMETER InputObject
            Accepts an XESession object returned by Get-DbaXESession.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Tags: ExtendedEvent, XE, XEvent
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Watch-DbaXESession

        .EXAMPLE
            Watch-DbaXESession -SqlInstance sql2017 -Session system_health

            Shows events for the system_health session as it happens.

        .EXAMPLE
            Watch-DbaXESession -SqlInstance sql2017 -Session system_health | Export-Csv -NoTypeInformation -Path C:\temp\system_health.csv

            Exports live events to CSV. Ctrl-C may not not cancel out of it - fastest way is to stop the session.

        .EXAMPLE
            Get-DbaXESession -SqlInstance sql2017 -Session system_health | Start-DbaXESession | Watch-DbaXESession | Export-Csv -NoTypeInformation -Path C:\temp\system_health.csv

            Exports live events to CSV. Ctrl-C may not not cancel out of this. The fastest way to do so is to stop the session.
    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    param (
        [parameter(ValueFromPipeline, ParameterSetName = "instance", Mandatory)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string]$Session,
        [parameter(ValueFromPipeline, ParameterSetName = "piped", Mandatory)]
        [Microsoft.SqlServer.Management.XEvent.Session]$InputObject,
        [switch]$Raw,
        [switch][Alias('Silent')]
        $EnableException
    )
    process {
        if (-not $SqlInstance) {
            $server = $InputObject.Parent
        }
        else {
            try {
                Write-Message -Level Verbose -Message "Connecting to $SqlInstance."
                $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential -MinimumVersion 11
            }
            catch {
                Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $SqlInstance -Continue
            }
            $SqlConn = $server.ConnectionContext.SqlConnectionObject
            $SqlStoreConnection = New-Object Microsoft.SqlServer.Management.Sdk.Sfc.SqlStoreConnection $SqlConn
            $XEStore = New-Object  Microsoft.SqlServer.Management.XEvent.XEStore $SqlStoreConnection
            Write-Message -Level Verbose -Message "Getting XEvents Sessions on $SqlInstance."
            $InputObject = $XEStore.sessions | Where-Object Name -eq $Session | Select-Object -First 1
        }

        if ($InputObject) {
            if (-Not $InputObject.IsRunning) {
                Stop-Function -Message "$($InputObject.Name) is in a $status state."
                return
            }

            # Setup all columns for csv but do it in an order
            $columns = @("name", "timestamp")
            $newcolumns = @()

            $fields = ($InputObject.Events.EventFields.Name | Select-Object -Unique)
            foreach ($column in $fields) {
                $newcolumns += $column.TrimStart("collect_")
            }

            $actions = ($InputObject.Events.Actions.Name | Select-Object -Unique)
            foreach ($action in $actions) {
                $newcolumns += ($action -Split '\.')[-1]
            }

            $newcolumns = $newcolumns | Sort-Object
            $columns = ($columns += $newcolumns) | Select-Object -Unique

            try {
                $xevent = New-Object -TypeName Microsoft.SqlServer.XEvent.Linq.QueryableXEventData(
                    ($server.ConnectionContext.ConnectionString),
                    ($InputObject.Name),
                    [Microsoft.SqlServer.XEvent.Linq.EventStreamSourceOptions]::EventStream,
                    [Microsoft.SqlServer.XEvent.Linq.EventStreamCacheOptions]::DoNotCache
                )

                if ($raw) {
                    return $xevent
                }

                # Format output
                foreach ($event in $xevent) {
                    $hash = [ordered]@{}

                    foreach ($column in $columns) {
                        $null = $hash.Add($column, $event.$column) # this basically adds name and timestamp then nulls
                    }

                    foreach ($action in $event.Actions) {
                        $hash[$action.Name] = $action.Value
                    }

                    foreach ($field in $event.Fields) {
                        $hash[$field.Name] = $field.Value
                    }

                    [pscustomobject]($hash)
                }
            }
            catch {
                Start-Sleep 1
                $status = Get-DbaXESession -SqlInstance $server -Session $Session
                if ($status.Status -ne "Running") {
                    Stop-Function -Message "$($InputObject.Name) was stopped."
                }
                else {
                    Stop-Function -Message "Failure" -ErrorRecord $_ -Target $session
                }
            }
            finally {
                if ($xevent -is [IDisposable]) {
                    $xevent.Dispose()
                }
            }
        }
        else {
            Stop-Function -Message "Session not found." -Target $session
        }
    }
}
tools\dbatools\functions\Write-DbaDataTable.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Write-DbaDataTable {
    <#
        .SYNOPSIS
            Writes data to a SQL Server Table.

        .DESCRIPTION
            Writes a .NET DataTable to a SQL Server table using SQL Bulk Copy.

        .PARAMETER SqlInstance
            The SQL Server instance.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            The database to import the table into.

        .PARAMETER InputObject
            This is the DataTable (or datarow) to import to SQL Server.

        .PARAMETER Table
            The table name to import data into. You can specify a one, two, or three part table name. If you specify a one or two part name, you must also use -Database.

            If the table does not exist, you can use -AutoCreateTable to automatically create the table with inefficient data types.

        .PARAMETER Schema
            Defaults to dbo if no schema is specified.

        .PARAMETER BatchSize
            The BatchSize for the import defaults to 5000.

        .PARAMETER NotifyAfter
            Sets the option to show the notification after so many rows of import

        .PARAMETER AutoCreateTable
            If this switch is enabled, the table will be created if it does not already exist. The table will be created with sub-optimal data types such as nvarchar(max)

        .PARAMETER NoTableLock
            If this switch is enabled, a table lock (TABLOCK) will not be placed on the destination table. By default, this operation will lock the destination table while running.

        .PARAMETER CheckConstraints
            If this switch is enabled, the SqlBulkCopy option to process check constraints will be enabled.

            Per Microsoft "Check constraints while data is being inserted. By default, constraints are not checked."

        .PARAMETER FireTriggers
            If this switch is enabled, the SqlBulkCopy option to fire insert triggers will be enabled.

            Per Microsoft "When specified, cause the server to fire the insert triggers for the rows being inserted into the Database."

        .PARAMETER KeepIdentity
            If this switch is enabled, the SqlBulkCopy option to preserve source identity values will be enabled.

            Per Microsoft "Preserve source identity values. When not specified, identity values are assigned by the destination."

        .PARAMETER KeepNulls
            If this switch is enabled, the SqlBulkCopy option to preserve NULL values will be enabled.

            Per Microsoft "Preserve null values in the destination table regardless of the settings for default values. When not specified, null values are replaced by default values where applicable."

        .PARAMETER Truncate
            If this switch is enabled, the destination table will be truncated after prompting for confirmation.

        .PARAMETER BulkCopyTimeOut
            Value in seconds for the BulkCopy operations timeout. The default is 30 seconds.

        .PARAMETER RegularUser
           Deprecated - now all connections are regular user (don't require admin)

        .PARAMETER WhatIf
            If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

        .PARAMETER Confirm
            If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER UseDynamicStringLength
            By default, all string columns will be NVARCHAR(MAX).
            If this switch is enabled, all columns will get the length specified by the column's MaxLength property (if specified)

        .NOTES
            Tags: DataTable, Insert
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/Write-DbaDataTable

        .EXAMPLE
            $DataTable = Import-Csv C:\temp\customers.csv | Out-DbaDataTable
            Write-DbaDataTable -SqlInstance sql2014 -InputObject $DataTable -Table mydb.dbo.customers

            Performs a bulk insert of all the data in customers.csv into database mydb, schema dbo, table customers. A progress bar will be shown as rows are inserted. If the destination table does not exist, the import will be halted.

        .EXAMPLE
            $DataTable = Import-Csv C:\temp\customers.csv | Out-DbaDataTable
            $DataTable | Write-DbaDataTable -SqlInstance sql2014 -Table mydb.dbo.customers

            Performs a row by row insert of the data in customers.csv. This is significantly slower than a bulk insert and will not show a progress bar.

            This method is not recommended. Use -InputObject instead.

        .EXAMPLE
            $DataTable = Import-Csv C:\temp\customers.csv | Out-DbaDataTable
            Write-DbaDataTable -SqlInstance sql2014 -InputObject $DataTable -Table mydb.dbo.customers -AutoCreateTable

            Performs a bulk insert of all the data in customers.csv. If mydb.dbo.customers does not exist, it will be created with inefficient but forgiving DataTypes.

        .EXAMPLE
            $DataTable = Import-Csv C:\temp\customers.csv | Out-DbaDataTable
            Write-DbaDataTable -SqlInstance sql2014 -InputObject $DataTable -Table mydb.dbo.customers -Truncate

            Performs a bulk insert of all the data in customers.csv. Prior to importing into mydb.dbo.customers, the user is informed that the table will be truncated and asks for confirmation. The user is prompted again to perform the import.

        .EXAMPLE
            $DataTable = Import-Csv C:\temp\customers.csv | Out-DbaDataTable
            Write-DbaDataTable -SqlInstance sql2014 -InputObject $DataTable -Database mydb -Table customers -KeepNulls

            Performs a bulk insert of all the data in customers.csv into mydb.dbo.customers. Because Schema was not specified, dbo was used. NULL values in the destination table will be preserved.

        .EXAMPLE
            $passwd = ConvertTo-SecureString "P@ssw0rd" -AsPlainText -Force
            $AzureCredential = Mew-Object System.Management.Automation.PSCredential("AzureAccount"),$passwd)
            $DataTable = Import-Csv C:\temp\customers.csv | Out-DbaDataTable
            Write-DbaDataTable -SqlInstance AzureDB.database.windows.net -InputObject $DataTable -Database mydb -Table customers -KeepNulls -Credential $AzureCredential -BulkCopyTimeOut 300

            This performs the same operation as the previous example, but against a SQL Azure Database instance using the required credentials.

        .EXAMPLE
            $process = Get-Process | Out-DbaDataTable
            Write-DbaDataTable -InputObject $process -SqlInstance sql2014 -Database mydb -Table myprocesses -AutoCreateTable

            Creates a table based on the Process object with over 60 columns, converted from PowerShell data types to SQL Server data types. After the table is created a bulk insert is performed to add process information into the table.

            This is an example of the type conversion in action. All process properties are converted, including special types like TimeSpan. Script properties are resolved before the type conversion starts thanks to Out-DbaDataTable.
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High")]
    param (
        [Parameter(Position = 0, Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [ValidateNotNull()]
        [DbaInstanceParameter]$SqlInstance,
        [Parameter(Position = 1)]
        [ValidateNotNull()]
        [Alias("Credential")]
        [PSCredential]$SqlCredential,
        [Parameter(Position = 2)]
        [object]$Database,
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("DataTable")]
        [ValidateNotNull()]
        [object]$InputObject,
        [Parameter(Position = 3, Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$Table,
        [Parameter(Position = 4)]
        [ValidateNotNullOrEmpty()]
        [string]$Schema = 'dbo',
        [ValidateNotNull()]
        [int]$BatchSize = 50000,
        [ValidateNotNull()]
        [int]$NotifyAfter = 5000,
        [switch]$AutoCreateTable,
        [switch]$NoTableLock,
        [switch]$CheckConstraints,
        [switch]$FireTriggers,
        [switch]$KeepIdentity,
        [switch]$KeepNulls,
        [switch]$Truncate,
        [ValidateNotNull()]
        [int]$bulkCopyTimeOut = 5000,
        [switch]$RegularUser,
        [Alias('Silent')]
        [switch]$EnableException,
        [switch]$UseDynamicStringLength
    )

    begin {
        # Null variable to make sure upper-scope variables don't interfere later
        $steppablePipeline = $null

        #region Utility Functions
        function Invoke-BulkCopy {
        <#
            .SYNOPSIS
                Copies a datatable in bulk over to a table.

            .DESCRIPTION
                Copies a datatable in bulk over to a table.

            .PARAMETER DataTable
                The datatable to copy.

            .PARAMETER SqlInstance
                Needs not be specified. The SqlInstance targeted. For message purposes only.

            .PARAMETER Fqtn
                Needs not be specified. The fqtn written to. For message purposes only.

            .PARAMETER BulkCopy
                Needs not be specified. The bulk copy object used to perform the copy operation.
        #>
            [CmdletBinding()]
            param (
                $DataTable,
                [DbaInstance]$SqlInstance = $SqlInstance,
                [string]$Fqtn = $fqtn,
                $BulkCopy = $bulkCopy
            )
            Write-Message -Level Verbose -Message "Importing in bulk to $fqtn"

            $rowCount = $DataTable.Rows.Count
            if ($rowCount -eq 0) {
                $rowCount = 1
            }

            if ($Pscmdlet.ShouldProcess($SqlInstance, "Writing $rowCount rows to $Fqtn")) {
                $bulkCopy.WriteToServer($DataTable)
                if ($rowCount -is [int]) {
                    Write-Progress -id 1 -activity "Inserting $rowCount rows" -status "Complete" -Completed
                }
            }
        }

        function New-Table {
        <#
            .SYNOPSIS
                Creates a table, based upon a DataTable.

            .DESCRIPTION
                Creates a table, based upon a DataTable.

            .PARAMETER DataTable
                The DataTable to base the table structure upon.

            .PARAMETER PStoSQLTypes
                Automatically inherits from parent.

            .PARAMETER SqlInstance
                Automatically inherits from parent.

            .PARAMETER Fqtn
                Automatically inherits from parent.

            .PARAMETER Server
                Automatically inherits from parent.

            .PARAMETER DatabaseName
                Automatically inherits from parent.

            .PARAMETER EnableException
                By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
                This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
                Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

            .PARAMETER UseDynamicStringLength
                Automatically inherits from parent.
        #>
            [CmdletBinding()]
            param (
                $DataTable,
                $PStoSQLTypes = $PStoSQLTypes,
                $SqlInstance = $SqlInstance,
                $Fqtn = $fqtn,
                $Server = $server,
                $DatabaseName = $databaseName,
                [switch]$EnableException
            )

            Write-Message -Level Verbose -Message "Creating table for $fqtn"

            # Get SQL datatypes by best guess on first data row
            $sqlDataTypes = @();
            $columns = $DataTable.Columns

            if ($null -eq $columns) {
                $columns = $DataTable.Table.Columns
            }

            foreach ($column in $columns) {
                $sqlColumnName = $column.ColumnName

                try {
                    $columnValue = $DataTable.Rows[0].$sqlColumnName
                }
                catch {
                    $columnValue = $DataTable.$sqlColumnName
                }

                if ($null -eq $columnValue) {
                    $columnValue = $DataTable.$sqlColumnName
                }

            <#
                PS to SQL type conversion
                If data type exists in hash table, use the corresponding SQL type
                Else, fallback to nvarchar.
                If UseDynamicStringLength is specified, the DataColumn MaxLength is used if specified
            #>
                if ($PStoSQLTypes.Keys -contains $column.DataType) {
                    $sqlDataType = $PStoSQLTypes[$($column.DataType.toString())]
                    if ($UseDynamicStringLength -and $column.MaxLength -gt 0 -and ($column.DataType -in ("String", "System.String"))) {
                        $sqlDataType = $sqlDataType.Replace("(MAX)", "($($column.MaxLength))")
                    }
                }
                else {
                    $sqlDataType = "nvarchar(MAX)"
                }

                $sqlDataTypes += "[$sqlColumnName] $sqlDataType"
            }

            $sql = "BEGIN CREATE TABLE $fqtn ($($sqlDataTypes -join ' NULL,')) END"

            Write-Message -Level Debug -Message $sql

            if ($Pscmdlet.ShouldProcess($SqlInstance, "Creating table $Fqtn")) {
                try {
                    $null = $Server.Databases[$DatabaseName].Query($sql)
                }
                catch {
                    Stop-Function -Message "The following query failed: $sql" -ErrorRecord $_
                    return
                }
            }
        }

        #endregion Utility Functions

        #region Prepare type for bulk copy
        if (-not $Truncate) { $ConfirmPreference = "None" }

        # Getting the total rows copied is a challenge. Use SqlBulkCopyExtension.
        # http://stackoverflow.com/questions/1188384/sqlbulkcopy-row-count-when-complete

        $source = 'namespace System.Data.SqlClient {
            using Reflection;

            public static class SqlBulkCopyExtension
            {
                const String _rowsCopiedFieldName = "_rowsCopied";
                static FieldInfo _rowsCopiedField = null;

                public static int RowsCopiedCount(this SqlBulkCopy bulkCopy)
                {
                    if (_rowsCopiedField == null) _rowsCopiedField = typeof(SqlBulkCopy).GetField(_rowsCopiedFieldName, BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance);
                    return (int)_rowsCopiedField.GetValue(bulkCopy);
                }
            }
        }'

        Add-Type -ReferencedAssemblies 'System.Data.dll' -TypeDefinition $source -ErrorAction SilentlyContinue
        #endregion Prepare type for bulk copy

        #region Resolve Full Qualified Table Name
        $dotCount = ([regex]::Matches($Table, "\.")).count

        if ($dotCount -lt 2 -and $null -eq $Database) {
            Stop-Function -Message "You must specify a database or fully qualified table name."
            return
        }

        if (Test-Bound -ParameterName Database) {
            $databaseName = "$Database"
        }

        $tableName = $Table
        $schemaName = $Schema

        if ($dotCount -eq 1) {
            $schemaName = $Table.Split(".")[0]
            $tableName = $Table.Split(".")[1]
        }

        if ($dotCount -eq 2) {
            $databaseName = $Table.Split(".")[0]
            $schemaName = $Table.Split(".")[1]
            $tableName = $Table.Split(".")[2]
        }

        if ($databaseName -match "\[.*\]") {
            $databaseName = ($databaseName -replace '\[', '') -replace '\]', ''
        }

        if ($schemaName -match "\[.*\]") {
            $schemaName = ($schemaName -replace '\[', '') -replace '\]', ''
        }

        if ($tableName -match "\[.*\]") {
            $tableName = ($tableName -replace '\[', '') -replace '\]', ''
        }

        $fqtn = "[$databaseName].[$schemaName].[$tableName]"
        Write-Message -Level SomewhatVerbose -Message "FQTN processed: $fqtn"
        #endregion Resolve Full Qualified Table Name

        #region Connect to server and get database
        Write-Message -Message "Connecting to $SqlInstance." -Level Verbose -Target $SqlInstance
        try {
            $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
        }
        catch {
            Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $SqlInstance
            return
        }

        if ($server.ServerType -eq 'SqlAzureDatabase') {
            <#
                For some reasons SMO wants an initial pull when talking to Azure Sql DB
                This will throw and be caught, and then we can continue as normal.
            #>
            try {
                $null = $server.Databases
            }
            catch {
                #do nothing
            }
        }
        $databaseObject = $server.Databases[$databaseName]
        #endregion Connect to server and get database

        #region Prepare database and bulk operations
        if ($null -eq $databaseObject) {
            Stop-Function -Message "$databaseName does not exist." -Target $SqlInstance
            return
        }

        $databaseObject.Tables.Refresh()
        if ($schemaName -notin $databaseObject.Schemas.Name) {
            Stop-Function -Message "Schema does not exist."
            return
        }

        $tableExists = ($tableName -in $databaseObject.Tables.Name) -and ($databaseObject.Tables.Schema -eq $schemaName)

        if ((-not $tableExists) -and (-not $AutoCreateTable)) {
            Stop-Function -Message "Table does not exist and automatic creation of the table has not been selected. Specify the '-AutoCreateTable'-parameter to generate a suitable table."
            return
        }

        $bulkCopyOptions = 0
        $options = "TableLock", "CheckConstraints", "FireTriggers", "KeepIdentity", "KeepNulls", "Default"

        foreach ($option in $options) {
            $optionValue = Get-Variable $option -ValueOnly -ErrorAction SilentlyContinue
            if ($option -eq "TableLock" -and (!$NoTableLock)) {
                $optionValue = $true
            }
            if ($optionValue -eq $true) {
                $bulkCopyOptions += $([Data.SqlClient.SqlBulkCopyOptions]::$option).value__
            }
        }

        if ($Truncate -eq $true) {
            if ($Pscmdlet.ShouldProcess($SqlInstance, "Truncating $fqtn")) {
                try {
                    Write-Message -Level Output -Message "Truncating $fqtn."
                    $null = $server.Databases[$databaseName].Query("TRUNCATE TABLE $fqtn")
                }
                catch {
                    Write-Message -Level Warning -Message "Could not truncate $fqtn. Table may not exist or may have key constraints." -ErrorRecord $_
                }
            }
        }

        $bulkCopy = New-Object Data.SqlClient.SqlBulkCopy("$($server.ConnectionContext.ConnectionString);Database=$databaseName", $bulkCopyOptions)
        $bulkCopy.DestinationTableName = $fqtn
        $bulkCopy.BatchSize = $BatchSize
        $bulkCopy.NotifyAfter = $NotifyAfter
        $bulkCopy.BulkCopyTimeOut = $BulkCopyTimeOut

        $elapsed = [System.Diagnostics.Stopwatch]::StartNew()
        # Add RowCount output
        $bulkCopy.Add_SqlRowsCopied({
                $script:totalRows = $args[1].RowsCopied
                $percent = [int](($script:totalRows / $rowCount) * 100)
                $timeTaken = [math]::Round($elapsed.Elapsed.TotalSeconds, 1)
                Write-Progress -id 1 -activity "Inserting $rowCount rows." -PercentComplete $percent -Status ([System.String]::Format("Progress: {0} rows ({1}%) in {2} seconds", $script:totalRows, $percent, $timeTaken))
            })

        $PStoSQLTypes = @{
            #PS datatype      = SQL data type
            'System.Int32'     = 'int';
            'System.UInt32'    = 'bigint';
            'System.Int16'     = 'smallint';
            'System.UInt16'    = 'int';
            'System.Int64'     = 'bigint';
            'System.UInt64'    = 'decimal(20,0)';
            'System.Decimal'   = 'decimal(20,5)';
            'System.Single'    = 'bigint';
            'System.Double'    = 'float';
            'System.Byte'      = 'tinyint';
            'System.SByte'     = 'smallint';
            'System.TimeSpan'  = 'nvarchar(30)';
            'System.String'    = 'nvarchar(MAX)';
            'System.Char'      = 'nvarchar(1)'
            'System.DateTime'  = 'datetime2';
            'System.Boolean'   = 'bit';
            'System.Guid'      = 'uniqueidentifier';
            'Int32'            = 'int';
            'UInt32'           = 'bigint';
            'Int16'            = 'smallint';
            'UInt16'           = 'int';
            'Int64'            = 'bigint';
            'UInt64'           = 'decimal(20,0)';
            'Decimal'          = 'decimal(20,5)';
            'Single'           = 'bigint';
            'Double'           = 'float';
            'Byte'             = 'tinyint';
            'SByte'            = 'smallint';
            'TimeSpan'         = 'nvarchar(30)';
            'String'           = 'nvarchar(MAX)';
            'Char'             = 'nvarchar(1)'
            'DateTime'         = 'datetime2';
            'Boolean'          = 'bit';
            'Bool'             = 'bit';
            'Guid'             = 'uniqueidentifier';
            'int'              = 'int';
            'long'             = 'bigint';
        }

        $validTypes = @([System.Data.DataSet], [System.Data.DataTable], [System.Data.DataRow], [System.Data.DataRow[]])
        #endregion Prepare database and bulk operations

        #region ConvertTo-DbaDataTable wrapper
        try {
            $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('ConvertTo-DbaDataTable', [System.Management.Automation.CommandTypes]::Function)
            $splatCDDT = @{
                TimeSpanType   = (Get-DbaConfigValue -FullName 'commands.write-dbadatatable.timespantype' -Fallback 'TotalMilliseconds')
                SizeType       = (Get-DbaConfigValue -FullName 'commands.write-dbadatatable.sizetype' -Fallback 'Int64')
                IgnoreNull     = (Get-DbaConfigValue -FullName 'commands.write-dbadatatable.ignorenull' -Fallback $false)
                Raw            = (Get-DbaConfigValue -FullName 'commands.write-dbadatatable.raw' -Fallback $false)
            }
            $scriptCmd = { & $wrappedCmd @splatCDDT }
            $steppablePipeline = $scriptCmd.GetSteppablePipeline()
            $steppablePipeline.Begin($true)
        }
        catch {
            Stop-Function -Message "Failed to initialize "
        }
        #endregion ConvertTo-DbaDataTable wrapper
    }
    process {
        if (Test-FunctionInterrupt) { return }

        if ($null -ne $InputObject) { $inputType = $InputObject.GetType() }
        else { $inputType = $null }

        if ($inputType -eq [System.Data.DataSet]) {
            $inputData = $InputObject.Tables
            $inputType = [System.Data.DataTable[]]
        }
        else {
            $inputData = $InputObject
        }

        #region Scenario 1: Single valid table
        if ($inputType -in $validTypes) {
            if (-not $tableExists) {
                try {
                    New-Table -DataTable $InputObject -EnableException
                    $tableExists = $true
                }
                catch {
                    Stop-Function -Message "Failed to create table $fqtn" -ErrorRecord $_ -Target $SqlInstance
                    return
                }
            }

            try { Invoke-BulkCopy -DataTable $InputObject }
            catch {
                Stop-Function -Message "Failed to bulk import to $fqtn" -ErrorRecord $_ -Target $SqlInstance
            }
            return
        }
        #endregion Scenario 1: Single valid table

        foreach ($object in $inputData) {
            #region Scenario 2: Multiple valid tables
            if ($object.GetType() -in $validTypes) {
                if (-not $tableExists) {
                    try {
                        New-Table -DataTable $object -EnableException
                        $tableExists = $true
                    }
                    catch {
                        Stop-Function -Message "Failed to create table $fqtn" -ErrorRecord $_ -Target $SqlInstance
                        return
                    }
                }

                try { Invoke-BulkCopy -DataTable $object }
                catch {
                    Stop-Function -Message "Failed to bulk import to $fqtn" -ErrorRecord $_ -Target $SqlInstance -Continue
                }
                continue
            }
            #endregion Scenario 2: Multiple valid tables

            #region Scenario 3: Invalid data types
            else {
                $null = $steppablePipeline.Process($object)
                continue
            }
            #endregion Scenario 3: Invalid data types
        }
    }
    end {
        #region ConvertTo-DbaDataTable wrapper
        if ($null -ne $steppablePipeline) {
            $dataTable = $steppablePipeline.End()

            if (-not $tableExists) {
                try {
                    New-Table -DataTable $dataTable[0] -EnableException
                    $tableExists = $true
                }
                catch {
                    Stop-Function -Message "Failed to create table $fqtn" -ErrorRecord $_ -Target $SqlInstance
                    return
                }
            }

            try { Invoke-BulkCopy -DataTable $dataTable[0] }
            catch {
                Stop-Function -Message "Failed to bulk import to $fqtn" -ErrorRecord $_ -Target $SqlInstance
            }
        }
        #endregion ConvertTo-DbaDataTable wrapper

        if ($bulkCopy) {
            $bulkCopy.Close()
            $bulkCopy.Dispose()
        }
        Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter RegularUser
    }
}
tools\dbatools\install.ps1
[CmdletBinding()]
param (
    [string]$Path,
    [switch]$Beta
)

function Write-LocalMessage {
    [CmdletBinding()]
    Param (
        [string]$Message
    )

    if (Test-Path function:Write-Message) { Write-Message -Level Output -Message $Message }
    else { Write-Host $Message }
}

try {
    Update-Module dbatools -Erroraction Stop
    Write-LocalMessage -Message "Updated using the PowerShell Gallery"
    return
}
catch {
    Write-LocalMessage -Message "dbatools was not installed by the PowerShell Gallery, continuing with web install."
}

$dbatools_copydllmode = $true
$module = Import-Module -Name dbatools -ErrorAction SilentlyContinue
$localpath = $module.ModuleBase

if ($null -eq $localpath) {
    $localpath = "$HOME\Documents\WindowsPowerShell\Modules\dbatools"
}
else {
    Write-LocalMessage -Message "Updating current install"
}

try {
    if (-not $path) {
        if ($PSCommandPath.Length -gt 0) {
            $path = Split-Path $PSCommandPath
            if ($path -match "github") {
                Write-LocalMessage -Message "Looks like this installer is run from your GitHub Repo, defaulting to psmodulepath"
                $path = $localpath
            }
        }
        else {
            $path = $localpath
        }
    }
}
catch {
    $path = $localpath
}

if (-not $path -or (Test-Path -Path "$path\.git")) {
    $path = $localpath
}

If ($lib = [appdomain]::CurrentDomain.GetAssemblies() | Where-Object FullName -like "dbatools, *") {
    if ($lib.Location -like "$Path\*") {
        Write-LocalMessage @"
We have detected dbatools to be already imported from
$path
In a manner that prevents us from updating it, since dll files have been locked.
In order to ensure a valid update, please:
- Close all consoles that have dbatools imported (Remove-Module dbatools is NOT enough)
- Start a new PowerShell console
- Run '`$dbatools_copydllmode = `$true' (without the single-quotes)
- Import dbatools and run Update-Dbatools
If done in this order, the binaries will be copied to another location before import, allowing for a save update.
"@
        return
    }
}

Write-LocalMessage -Message "Installing module to $path"

if (!(Test-Path -Path $path)) {
    try {
        Write-LocalMessage -Message "Creating directory: $path"
        New-Item -Path $path -ItemType Directory | Out-Null
    }
    catch {
        throw "Can't create $Path. You may need to Run as Administrator: $_"
    }
}

if ($beta) {
    $url = 'https://dbatools.io/devzip'
    $branch = "development"
}
else {
    $url = 'https://dbatools.io/zip'
    $branch = "master"
}

$temp = ([System.IO.Path]::GetTempPath()).TrimEnd("\")
$zipfile = "$temp\dbatools.zip"

Write-LocalMessage -Message "Downloading archive from github"
try {
    (New-Object System.Net.WebClient).DownloadFile($url, $zipfile)
}
catch {
    #try with default proxy and usersettings
    Write-LocalMessage -Message "Probably using a proxy for internet access, trying default proxy settings"
    $wc = (New-Object System.Net.WebClient).Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
    $wc.DownloadFile($url, $zipfile)
}

# Unblock if there's a block
Unblock-File $zipfile -ErrorAction SilentlyContinue

Write-LocalMessage -Message "Unzipping"

# Keep it backwards compatible
Remove-Item -ErrorAction SilentlyContinue "$temp\dbatools-$branch" -Recurse -Force
Remove-Item -ErrorAction SilentlyContinue "$temp\dbatools-old" -Recurse -Force
$null = New-Item "$temp\dbatools-old" -ItemType Directory
$shell = New-Object -ComObject Shell.Application
$zipPackage = $shell.NameSpace($zipfile)
$destinationFolder = $shell.NameSpace($temp)
$destinationFolder.CopyHere($zipPackage.Items())

Write-LocalMessage -Message "Applying Update"
Write-LocalMessage -Message "1) Backing up previous installation"
Copy-Item -Path "$Path\*" -Destination "$temp\dbatools-old" -ErrorAction Stop
try {
    Write-LocalMessage -Message "2) Cleaning up installation directory"
    Remove-Item "$Path\*" -Recurse -Force -ErrorAction Stop
}
catch {
    Write-LocalMessage -Message @"
Failed to clean up installation directory, rolling back update.
This usually has one of two causes:
- Insufficient privileges (need to run as admin)
- A file is locked - generally a dll file from having the module imported in some process.

You can run the following line before importing dbatools to prevent file locking:
`$dbatools_copydllmode = `$true
But it increases the time needed to import the module, so we only recommend using it for updates.

Exception:
$_
"@
    Copy-Item -Path "$temp\dbatools-old\*" -Destination $path -ErrorAction Ignore -Recurse
    Remove-Item "$temp\dbatools-old" -Recurse -Force
    return
}
Write-LocalMessage -Message "3) Setting up current version"
Move-Item -Path "$temp\dbatools-$branch\*" -Destination $path -ErrorAction SilentlyContinue -Force
Remove-Item -Path "$temp\dbatools-$branch" -Recurse -Force
Remove-Item "$temp\dbatools-old" -Recurse -Force
Remove-Item -Path $zipfile -Recurse -Force

Write-LocalMessage -Message "Done! Please report any bugs to dbatools.io/issues or [email protected]."
if (Get-Module dbatools) {
    Write-LocalMessage -Message @"

Please restart PowerShell before working with dbatools.
"@
}
else {
    Import-Module "$path\dbatools.psd1" -Force
    Write-LocalMessage @"

dbatools v $((Get-Module dbatools).Version)
# Commands available: $((Get-Command -Module dbatools -CommandType Function | Measure-Object).Count)

"@
}
Write-LocalMessage -Message "`n`nIf you experience any function missing errors after update, please restart PowerShell or reload your profile."


# SIG # Begin signature block
# MIIcYgYJKoZIhvcNAQcCoIIcUzCCHE8CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUhOFJ3NXmFl4u6JtbLG6lCFv+
# OuGggheRMIIFGjCCBAKgAwIBAgIQAsF1KHTVwoQxhSrYoGRpyjANBgkqhkiG9w0B
# AQsFADByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD
# VQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFz
# c3VyZWQgSUQgQ29kZSBTaWduaW5nIENBMB4XDTE3MDUwOTAwMDAwMFoXDTIwMDUx
# MzEyMDAwMFowVzELMAkGA1UEBhMCVVMxETAPBgNVBAgTCFZpcmdpbmlhMQ8wDQYD
# VQQHEwZWaWVubmExETAPBgNVBAoTCGRiYXRvb2xzMREwDwYDVQQDEwhkYmF0b29s
# czCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAI8ng7JxnekL0AO4qQgt
# Kr6p3q3SNOPh+SUZH+SyY8EA2I3wR7BMoT7rnZNolTwGjUXn7bRC6vISWg16N202
# 1RBWdTGW2rVPBVLF4HA46jle4hcpEVquXdj3yGYa99ko1w2FOWzLjKvtLqj4tzOh
# K7wa/Gbmv0Si/FU6oOmctzYMI0QXtEG7lR1HsJT5kywwmgcjyuiN28iBIhT6man0
# Ib6xKDv40PblKq5c9AFVldXUGVeBJbLhcEAA1nSPSLGdc7j4J2SulGISYY7ocuX3
# tkv01te72Mv2KkqqpfkLEAQjXgtM0hlgwuc8/A4if+I0YtboCMkVQuwBpbR9/6ys
# Z+sCAwEAAaOCAcUwggHBMB8GA1UdIwQYMBaAFFrEuXsqCqOl6nEDwGD5LfZldQ5Y
# MB0GA1UdDgQWBBRcxSkFqeA3vvHU0aq2mVpFRSOdmjAOBgNVHQ8BAf8EBAMCB4Aw
# EwYDVR0lBAwwCgYIKwYBBQUHAwMwdwYDVR0fBHAwbjA1oDOgMYYvaHR0cDovL2Ny
# bDMuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC1jcy1nMS5jcmwwNaAzoDGGL2h0
# dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zaGEyLWFzc3VyZWQtY3MtZzEuY3JsMEwG
# A1UdIARFMEMwNwYJYIZIAYb9bAMBMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3
# LmRpZ2ljZXJ0LmNvbS9DUFMwCAYGZ4EMAQQBMIGEBggrBgEFBQcBAQR4MHYwJAYI
# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBOBggrBgEFBQcwAoZC
# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0U0hBMkFzc3VyZWRJ
# RENvZGVTaWduaW5nQ0EuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQAD
# ggEBANuBGTbzCRhgG0Th09J0m/qDqohWMx6ZOFKhMoKl8f/l6IwyDrkG48JBkWOA
# QYXNAzvp3Ro7aGCNJKRAOcIjNKYef/PFRfFQvMe07nQIj78G8x0q44ZpOVCp9uVj
# sLmIvsmF1dcYhOWs9BOG/Zp9augJUtlYpo4JW+iuZHCqjhKzIc74rEEiZd0hSm8M
# asshvBUSB9e8do/7RhaKezvlciDaFBQvg5s0fICsEhULBRhoyVOiUKUcemprPiTD
# xh3buBLuN0bBayjWmOMlkG1Z6i8DUvWlPGz9jiBT3ONBqxXfghXLL6n8PhfppBhn
# daPQO8+SqF5rqrlyBPmRRaTz2GQwggUwMIIEGKADAgECAhAECRgbX9W7ZnVTQ7Vv
# lVAIMA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdp
# Q2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0Rp
# Z2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0xMzEwMjIxMjAwMDBaFw0yODEw
# MjIxMjAwMDBaMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx
# GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0IFNI
# QTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EwggEiMA0GCSqGSIb3DQEBAQUA
# A4IBDwAwggEKAoIBAQD407Mcfw4Rr2d3B9MLMUkZz9D7RZmxOttE9X/lqJ3bMtdx
# 6nadBS63j/qSQ8Cl+YnUNxnXtqrwnIal2CWsDnkoOn7p0WfTxvspJ8fTeyOU5JEj
# lpB3gvmhhCNmElQzUHSxKCa7JGnCwlLyFGeKiUXULaGj6YgsIJWuHEqHCN8M9eJN
# YBi+qsSyrnAxZjNxPqxwoqvOf+l8y5Kh5TsxHM/q8grkV7tKtel05iv+bMt+dDk2
# DZDv5LVOpKnqagqrhPOsZ061xPeM0SAlI+sIZD5SlsHyDxL0xY4PwaLoLFH3c7y9
# hbFig3NBggfkOItqcyDQD2RzPJ6fpjOp/RnfJZPRAgMBAAGjggHNMIIByTASBgNV
# HRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEF
# BQcDAzB5BggrBgEFBQcBAQRtMGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRp
# Z2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0cDovL2NhY2VydHMuZGlnaWNlcnQu
# Y29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNydDCBgQYDVR0fBHoweDA6oDig
# NoY0aHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9v
# dENBLmNybDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0
# QXNzdXJlZElEUm9vdENBLmNybDBPBgNVHSAESDBGMDgGCmCGSAGG/WwAAgQwKjAo
# BggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAKBghghkgB
# hv1sAzAdBgNVHQ4EFgQUWsS5eyoKo6XqcQPAYPkt9mV1DlgwHwYDVR0jBBgwFoAU
# Reuir/SSy4IxLVGLp6chnfNtyA8wDQYJKoZIhvcNAQELBQADggEBAD7sDVoks/Mi
# 0RXILHwlKXaoHV0cLToaxO8wYdd+C2D9wz0PxK+L/e8q3yBVN7Dh9tGSdQ9RtG6l
# jlriXiSBThCk7j9xjmMOE0ut119EefM2FAaK95xGTlz/kLEbBw6RFfu6r7VRwo0k
# riTGxycqoSkoGjpxKAI8LpGjwCUR4pwUR6F6aGivm6dcIFzZcbEMj7uo+MUSaJ/P
# QMtARKUT8OZkDCUIQjKyNookAv4vcn4c10lFluhZHen6dGRrsutmQ9qzsIzV6Q3d
# 9gEgzpkxYz0IGhizgZtPxpMQBvwHgfqL2vmCSfdibqFT+hKUGIUukpHqaGxEMrJm
# oecYpJpkUe8wggZqMIIFUqADAgECAhADAZoCOv9YsWvW1ermF/BmMA0GCSqGSIb3
# DQEBBQUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAX
# BgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3Vy
# ZWQgSUQgQ0EtMTAeFw0xNDEwMjIwMDAwMDBaFw0yNDEwMjIwMDAwMDBaMEcxCzAJ
# BgNVBAYTAlVTMREwDwYDVQQKEwhEaWdpQ2VydDElMCMGA1UEAxMcRGlnaUNlcnQg
# VGltZXN0YW1wIFJlc3BvbmRlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
# ggEBAKNkXfx8s+CCNeDg9sYq5kl1O8xu4FOpnx9kWeZ8a39rjJ1V+JLjntVaY1sC
# SVDZg85vZu7dy4XpX6X51Id0iEQ7Gcnl9ZGfxhQ5rCTqqEsskYnMXij0ZLZQt/US
# s3OWCmejvmGfrvP9Enh1DqZbFP1FI46GRFV9GIYFjFWHeUhG98oOjafeTl/iqLYt
# WQJhiGFyGGi5uHzu5uc0LzF3gTAfuzYBje8n4/ea8EwxZI3j6/oZh6h+z+yMDDZb
# esF6uHjHyQYuRhDIjegEYNu8c3T6Ttj+qkDxss5wRoPp2kChWTrZFQlXmVYwk/PJ
# YczQCMxr7GJCkawCwO+k8IkRj3cCAwEAAaOCAzUwggMxMA4GA1UdDwEB/wQEAwIH
# gDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMIIBvwYDVR0g
# BIIBtjCCAbIwggGhBglghkgBhv1sBwEwggGSMCgGCCsGAQUFBwIBFhxodHRwczov
# L3d3dy5kaWdpY2VydC5jb20vQ1BTMIIBZAYIKwYBBQUHAgIwggFWHoIBUgBBAG4A
# eQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQByAHQAaQBmAGkAYwBhAHQA
# ZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBjAGUAcAB0AGEAbgBjAGUA
# IABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAgAEMAUAAvAEMAUABTACAA
# YQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQAGEAcgB0AHkAIABBAGcA
# cgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBtAGkAdAAgAGwAaQBhAGIA
# aQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBjAG8AcgBwAG8AcgBhAHQA
# ZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBlAHIAZQBuAGMAZQAuMAsG
# CWCGSAGG/WwDFTAfBgNVHSMEGDAWgBQVABIrE5iymQftHt+ivlcNK2cCzTAdBgNV
# HQ4EFgQUYVpNJLZJMp1KKnkag0v0HonByn0wfQYDVR0fBHYwdDA4oDagNIYyaHR0
# cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEQ0EtMS5jcmww
# OKA2oDSGMmh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJ
# RENBLTEuY3JsMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29j
# c3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdp
# Y2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURDQS0xLmNydDANBgkqhkiG9w0BAQUF
# AAOCAQEAnSV+GzNNsiaBXJuGziMgD4CH5Yj//7HUaiwx7ToXGXEXzakbvFoWOQCd
# 42yE5FpA+94GAYw3+puxnSR+/iCkV61bt5qwYCbqaVchXTQvH3Gwg5QZBWs1kBCg
# e5fH9j/n4hFBpr1i2fAnPTgdKG86Ugnw7HBi02JLsOBzppLA044x2C/jbRcTBu7k
# A7YUq/OPQ6dxnSHdFMoVXZJB2vkPgdGZdA0mxA5/G7X1oPHGdwYoFenYk+VVFvC7
# Cqsc21xIJ2bIo4sKHOWV2q7ELlmgYd3a822iYemKC23sEhi991VUQAOSK2vCUcIK
# SK+w1G7g9BQKOhvjjz3Kr2qNe9zYRDCCBs0wggW1oAMCAQICEAb9+QOWA63qAArr
# Pye7uhswDQYJKoZIhvcNAQEFBQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERp
# Z2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMb
# RGlnaUNlcnQgQXNzdXJlZCBJRCBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTIx
# MTExMDAwMDAwMFowYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IElu
# YzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQg
# QXNzdXJlZCBJRCBDQS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
# 6IItmfnKwkKVpYBzQHDSnlZUXKnE0kEGj8kz/E1FkVyBn+0snPgWWd+etSQVwpi5
# tHdJ3InECtqvy15r7a2wcTHrzzpADEZNk+yLejYIA6sMNP4YSYL+x8cxSIB8HqIP
# kg5QycaH6zY/2DDD/6b3+6LNb3Mj/qxWBZDwMiEWicZwiPkFl32jx0PdAug7Pe2x
# QaPtP77blUjE7h6z8rwMK5nQxl0SQoHhg26Ccz8mSxSQrllmCsSNvtLOBq6thG9I
# hJtPQLnxTPKvmPv2zkBdXPao8S+v7Iki8msYZbHBc63X8djPHgp0XEK4aH631XcK
# J1Z8D2KkPzIUYJX9BwSiCQIDAQABo4IDejCCA3YwDgYDVR0PAQH/BAQDAgGGMDsG
# A1UdJQQ0MDIGCCsGAQUFBwMBBggrBgEFBQcDAgYIKwYBBQUHAwMGCCsGAQUFBwME
# BggrBgEFBQcDCDCCAdIGA1UdIASCAckwggHFMIIBtAYKYIZIAYb9bAABBDCCAaQw
# OgYIKwYBBQUHAgEWLmh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVw
# b3NpdG9yeS5odG0wggFkBggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUA
# IABvAGYAIAB0AGgAaQBzACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4A
# cwB0AGkAdAB1AHQAZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQA
# aABlACAARABpAGcAaQBDAGUAcgB0ACAAQwBQAC8AQwBQAFMAIABhAG4AZAAgAHQA
# aABlACAAUgBlAGwAeQBpAG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUA
# bgB0ACAAdwBoAGkAYwBoACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkA
# IABhAG4AZAAgAGEAcgBlACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUA
# cgBlAGkAbgAgAGIAeQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4wCwYJYIZIAYb9bAMV
# MBIGA1UdEwEB/wQIMAYBAf8CAQAweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzAB
# hhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9j
# YWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQw
# gYEGA1UdHwR6MHgwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdp
# Q2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwOqA4oDaGNGh0dHA6Ly9jcmw0LmRpZ2lj
# ZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwHQYDVR0OBBYEFBUA
# EisTmLKZB+0e36K+Vw0rZwLNMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3z
# bcgPMA0GCSqGSIb3DQEBBQUAA4IBAQBGUD7Jtygkpzgdtlspr1LPUukxR6tWXHvV
# DQtBs+/sdR90OPKyXGGinJXDUOSCuSPRujqGcq04eKx1XRcXNHJHhZRW0eu7NoR3
# zCSl8wQZVann4+erYs37iy2QwsDStZS9Xk+xBdIOPRqpFFumhjFiqKgz5Js5p8T1
# zh14dpQlc+Qqq8+cdkvtX8JLFuRLcEwAiR78xXm8TBJX/l/hHrwCXaj++wc4Tw3G
# XZG5D2dFzdaD7eeSDY2xaYxP+1ngIw/Sqq4AfO6cQg7PkdcntxbuD8O9fAqg7iwI
# VYUiuOsYGk38KiGtSTGDR5V3cdyxG0tLHBCcdxTBnU8vWpUIKRAmMYIEOzCCBDcC
# AQEwgYYwcjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcG
# A1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBB
# c3N1cmVkIElEIENvZGUgU2lnbmluZyBDQQIQAsF1KHTVwoQxhSrYoGRpyjAJBgUr
# DgMCGgUAoHgwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMx
# DAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkq
# hkiG9w0BCQQxFgQU5Du/GTW5pEjBXVetygAETjhOMCgwDQYJKoZIhvcNAQEBBQAE
# ggEADglQBYTjtCNTAtKs4hi9Kj74iKxNnhf9xSdqZZAUin++ysdxOZrrBHdbQNHk
# C7YXsKWqYEic8k9asjFa/iZKHbRTJ9JdykG1ljrcjf+z+7eNXsP2z3axkKRchHxX
# eMVsfvaTsKOhQoXm+MKbsnUGwvisA3cbUOZ8kM40IKAcg+ujEHxxT4RzqsGPmnHP
# 4W/N88x7Hrtykah7TJDmp0x2mVrNdr6iRsWPgmEZSj59d5lLKE+A7xkdvtYGZDEh
# DyPLbDhWWRDo40+D5NKZSRP7WwTY6RPtAl1obaxZolySdD17ZIaFazEg3b9/cpx3
# c3fUF6wcAareoqJVbgSAW0bxU6GCAg8wggILBgkqhkiG9w0BCQYxggH8MIIB+AIB
# ATB2MGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNV
# BAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3VyZWQg
# SUQgQ0EtMQIQAwGaAjr/WLFr1tXq5hfwZjAJBgUrDgMCGgUAoF0wGAYJKoZIhvcN
# AQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTgwNzIzMTk1NzIwWjAj
# BgkqhkiG9w0BCQQxFgQU2ergF7YU3jz7T4v+V2ggn9yhehowDQYJKoZIhvcNAQEB
# BQAEggEAQ06tCWWeHvsvl87J4yoNTue5StkgC/LZycKCEpP7W4wiBE/Hw6HiqHmx
# SXceiQuzY1Gh0TrM8/n6VTz5+q2O6KxFVkiMGwwW2+I+ATR2wMmBvm0Sd//yys28
# DrLvbSHIT99O0i/4LK+gsELcyI6/qLYl6Ihw9iRk0q4uZtfAPZNM97hNwKFGIvW5
# gtWr+5ayfhXLwJODIIAL3l+viyJBDQNmx3hpIWufDEMG+9tvnqXWVT8L9UQnsWuz
# u1T6+cMOm/QD19sexKW0IXw1KRWl7eoUND4AjxFciYhWx5VeO03l97yVNN9VpWEF
# Y4bRkYQ8JwuWaVua+q5UTj5BaoHbcw==
# SIG # End signature block
tools\dbatools\internal\configurations\configuration.ps1
<#

#-------------------------#
# Warning Warning Warning #
#-------------------------#

This is the global configuration management file.

DO NOT EDIT THIS FILE!!!!!
Disobedience shall be answered by the wrath of Fred.
You've been warned.
;)

The purpose of this file is to manage the configuration system.
That means, messing with this may mess with every function using this infrastructure.
Don't, unless you know what you do.

#---------------------------------------#
# Implementing the configuration system #
#---------------------------------------#

The configuration system is designed, to keep as much hard coded configuration out of the functions.
Instead we keep it in a central location: The Configuration store (= this folder).

In Order to put something here, either find a configuration file whose topic suits you and add configuration there,
or create your own file. The configuration system is loaded last during module import process, so you have access to all
that dbatools has to offer (Keep module load times in mind though).

Examples are better than a thousand words:

a) Setting the configuration value
# Put this in a configuration file in this folder
Set-DbaConfig -Name 'Path.DbatoolsLog' -Value "$($env:AppData)\PowerShell\dbatools" -Default

b) Retrieving the configuration value in your function
# Put this in the function that uses this setting
$path = Get-DbaConfigValue -Name 'Path.DbatoolsLog' -FallBack $env:temp

# Explanation #
#-------------#

In step a), which is run during module import, we assign the configuration of the name 'Path.DbatoolsLog' the value "$($env:AppData)\PowerShell\dbatools"
Unless there already IS a value set to this name (that's what the '-Default' switch is doing).
That means, that if a user had a different configuration value in his profile, that value will win. Userchoice over preset.
ALL configurations defined by the module should be 'default' values.

In step b), which will be run whenever the function is called within which it is written, we retrieve the value stored behind the name 'Path.DbatoolsLog'.
If there is nothing there (for example, if the user accidentally removed or nulled the configuration), then it will fall back to using "$($env:temp)\dbatools.log"

#--------------------#
# Architecture Notes #
#--------------------#

In order to reduce import times, this is executed in a separate runspace.

#>

$scriptBlock = {
    Param (
        $DbatoolsConfig
    )
    $ModuleRoot = [Sqlcollaborative.Dbatools.dbaSystem.SystemHost]::ModuleBase

    #region Helper functions
    # Empty dummy, should not have a cmdletbinding in order to avoid errors.
    function Stop-Function { }

    $ExecutionContext.InvokeCommand.InvokeScript($false, ([scriptblock]::Create([io.file]::ReadAllText("$ModuleRoot\internal\functions\Test-Bound.ps1"))), $null, $null)
    $ExecutionContext.InvokeCommand.InvokeScript($false, ([scriptblock]::Create([io.file]::ReadAllText("$ModuleRoot\internal\functions\Register-DbaConfigValidation.ps1"))), $null, $null)
    $ExecutionContext.InvokeCommand.InvokeScript($false, ([scriptblock]::Create([io.file]::ReadAllText("$ModuleRoot\functions\Register-DbaConfig.ps1"))), $null, $null)
    $ExecutionContext.InvokeCommand.InvokeScript($false, ([scriptblock]::Create([io.file]::ReadAllText("$ModuleRoot\functions\Set-DbaConfig.ps1"))), $null, $null)
    #endregion Helper functions



    $configpath = "$ModuleRoot\internal\configurations"

    # Import configuration validation
    foreach ($file in (Get-ChildItem -Path "$configpath\validation")) {
        if ($script:doDotSource) { . $file.FullName }
        else { . ([scriptblock]::Create([io.file]::ReadAllText($file.FullName))) }
    }

    # Import other configuration files
    foreach ($file in (Get-ChildItem -Path "$configpath\settings")) {
        if ($script:doDotSource) { . $file.FullName }
        else { . ([scriptblock]::Create([io.file]::ReadAllText($file.FullName))) }
    }

    #region Import settings from registry
    if (-not [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::ImportFromRegistryDone) {
        $common = 'PSPath', 'PSParentPath', 'PSChildName', 'PSDrive', 'PSProvider'

        function Convert-RegType {
            [CmdletBinding()]
            Param (
                [string]
                $Value
            )

            $index = $Value.IndexOf(":")
            if ($index -lt 1) { throw "No type identifier found!" }
            $type = $Value.Substring(0, $index).ToLower()
            $content = $Value.Substring($index + 1)

            switch ($type) {
                "bool" {
                    if ($content -eq "true") { return $true }
                    if ($content -eq "1") { return $true }
                    if ($content -eq "false") { return $false }
                    if ($content -eq "0") { return $false }
                    throw "Failed to interpret as bool: $content"
                }
                "int" { return ([int]$content) }
                "double" { return [double]$content }
                "long" { return [long]$content }
                "string" { return $content }
                "timespan" { return (New-Object System.TimeSpan($content)) }
                "datetime" { return (New-Object System.DateTime($content)) }
                "consolecolor" { return ([System.ConsoleColor]$content) }

                default { throw "Unknown type identifier" }
            }
        }

        #region Import from registry
        $config_hash = @{ }
        foreach ($item in ((Get-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\Config\Default" -ErrorAction Ignore).PSObject.Properties | Where-Object Name -NotIn $common)) {
            try {
                $config_hash[$item.Name.ToLower()] = New-Object PSObject -Property @{
                    Name     = $item.Name
                    Enforced = $false
                    Value    = Convert-RegType -Value $item.Value
                }
            }
            catch {
                Write-Message -Level Warning -Message "Failed to interpret configuration entry from registry: $($item.Name)" -ErrorRecord $_
            }
        }
        foreach ($item in ((Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\Config\Default" -ErrorAction Ignore).PSObject.Properties | Where-Object Name -NotIn $common)) {
            try {
                $config_hash[$item.Name.ToLower()] = New-Object PSObject -Property @{
                    Name     = $item.Name
                    Enforced = $false
                    Value    = Convert-RegType -Value $item.Value
                }
            }
            catch {
                Write-Message -Level Warning -Message "Failed to interpret configuration entry from registry: $($item.Name)" -ErrorRecord $_
            }
        }
        foreach ($item in ((Get-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\Config\Enforced" -ErrorAction Ignore).PSObject.Properties | Where-Object Name -NotIn $common)) {
            try {
                $config_hash[$item.Name.ToLower()] = New-Object PSObject -Property @{
                    Name     = $item.Name
                    Enforced = $true
                    Value    = Convert-RegType -Value $item.Value
                }
            }
            catch {
                Write-Message -Level Warning -Message "Failed to interpret configuration entry from registry: $($item.Name)" -ErrorRecord $_
            }
        }
        foreach ($item in ((Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\Config\Enforced" -ErrorAction Ignore).PSObject.Properties | Where-Object Name -NotIn $common)) {
            try {
                $config_hash[$item.Name.ToLower()] = New-Object PSObject -Property @{
                    Name     = $item.Name
                    Enforced = $true
                    Value    = Convert-RegType -Value $item.Value
                }
            }
            catch {
                Write-Message -Level Warning -Message "Failed to interpret configuration entry from registry: $($item.Name)" -ErrorRecord $_
            }
        }
        #endregion Import from registry

        foreach ($value in $config_hash.Values) {
            try {
                Set-DbaConfig -Name $value.Name -Value $value.Value -EnableException
                [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations[$value.Name.ToLower()].PolicySet = $true
                [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations[$value.Name.ToLower()].PolicyEnforced = $value.Enforced
            }
            catch { }
        }

        [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::ImportFromRegistryDone = $true
    }
    #endregion Import settings from registry

    #region Implement user profile
    if ($DbatoolsConfig -ne $null) {
        if ($DbatoolsConfig.GetType().FullName -eq "System.Management.Automation.ScriptBlock") {
            [System.Management.Automation.ScriptBlock]::Create($DbatoolsConfig.ToString()).Invoke()
        }
    }
    #endregion Implement user profile
}
if ($script:serialImport) {
    $scriptBlock.Invoke($global:dbatools_config)
}
else {
    $script:dbatoolsConfigRunspace = [System.Management.Automation.PowerShell]::Create()
    if ($script:dbatoolsConfigRunspace.Runspace.Name) {
        try { $script:dbatoolsConfigRunspace.Runspace.Name = "dbatools-import-config" }
        catch { }
    }
    $script:dbatoolsConfigRunspace.AddScript($scriptBlock).AddArgument($global:dbatools_config)
    $script:dbatoolsConfigRunspace.BeginInvoke()
}
tools\dbatools\internal\configurations\settings\assets.ps1
<#
This is designed for all online assets you need configurations for.
#>

# The default path where dbatools stores persistent data
Set-DbaConfig -FullName 'assets.sqlbuildreference' -Value 'https://sqlcollaborative.github.io/assets/dbatools-buildref-index.json' -Initialize -Validation string -Handler { } -Description "The url where dbatools fetches the up to date buildreference index (e.g. for Get-DbaSqlBuildReference)"
tools\dbatools\internal\configurations\settings\commands.ps1
# Write-DbaDataTable: Settings for ConvertTo-DbaDataTable
Set-DbaConfig -FullName 'commands.write-dbadatatable.timespantype' -Value 'TotalMilliseconds' -Initialize -Validation string -Description "When passing random objects at Write-DbaDataTable, it will convert them to a DataTable before writing it, using ConvertTo-DbaDataTable. This setting controls how Timespan objects are converted"
Set-DbaConfig -FullName 'commands.write-dbadatatable.sizetype' -Value 'Int64' -Initialize -Validation string -Description "When passing random objects at Write-DbaDataTable, it will convert them to a DataTable before writing it, using ConvertTo-DbaDataTable. This setting controls how Size objects are converted"
Set-DbaConfig -FullName 'commands.write-dbadatatable.ignorenull' -Value $false -Initialize -Validation bool -Description "When passing random objects at Write-DbaDataTable, it will convert them to a DataTable before writing it, using ConvertTo-DbaDataTable. This setting controls whether null objects will be ignored, rather than generating an empty row"
Set-DbaConfig -FullName 'commands.write-dbadatatable.raw' -Value $false -Initialize -Validation bool -Description "When passing random objects at Write-DbaDataTable, it will convert them to a DataTable before writing it, using ConvertTo-DbaDataTable. This setting controls whether all properties will be stored as string (`$true) or as much as possible in their native type (`$false)"
tools\dbatools\internal\configurations\settings\computermanagement.ps1
<#
This is designed for all things that control how anything that caches acts
#>

# Sets the default timeout on bad connections
Set-DbaConfig -FullName 'ComputerManagement.BadConnectionTimeout' -Value (New-TimeSpan -Minutes 15) -Initialize -Validation timespan -Handler { [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::BadConnectionTimeout = $args[0] } -Description 'The timeout used on bad computer management connections. When a connection using a protocol fails, it will not be reattempted for this timespan.'

# Disable the management cache entire
Set-DbaConfig -FullName 'ComputerManagement.Cache.Disable.All' -Value $false -Initialize -Validation bool -Handler { [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::DisableCache = $args[0] } -Description 'Globally disables all caching done by the Computer Management functions'

# Disables the caching of bad credentials, which is kept in order to avoid reusing them
Set-DbaConfig -FullName 'ComputerManagement.Cache.Disable.BadCredentialList' -Value $false -Initialize -Validation bool -Handler { [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::DisableBadCredentialCache = $args[0] } -Description 'Disables the caching of bad credentials. dbatools caches bad logon credentials for wmi/cim and will not reuse them.'

# Disables reuse of CIM Sessions
Set-DbaConfig -FullName 'ComputerManagement.Cache.Disable.CimPersistence' -Value $false -Initialize -Validation bool -Handler { [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::DisableCimPersistence = $args[0] } -Description 'Disables the reuse of Cim Sessions. Setting this config to "true" will hurt Computer Management Performance, but may be necessary in some rare cases'

# Disables automatic caching of working credentials
Set-DbaConfig -FullName 'ComputerManagement.Cache.Disable.CredentialAutoRegister' -Value $false -Initialize -Validation bool -Handler { [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::DisableCredentialAutoRegister = $args[0] } -Description 'Disables the automatic registration of working credentials. dbatools will caches the last working credential when connecting using wmi/cim and will use those rather than using known bad credentials'

# Enables automatic failover of credentials. If enabled, CM will use known-to-work credentials in case of non-working credentials
Set-DbaConfig -FullName 'ComputerManagement.Cache.Enable.CredentialFailover' -Value $false -Initialize -Validation bool -Handler { [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::EnableCredentialFailover = $args[0] } -Description 'Enables automatically failing over to known to work credentials, when specified credentials will not work.'

# Force-Overrides explicit credentials with cached-as-working credentials
Set-DbaConfig -FullName 'ComputerManagement.Cache.Force.OverrideExplicitCredential' -Value $false -Initialize -Validation bool -Handler { [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::OverrideExplicitCredential = $args[0] } -Description 'Enabling this will force the use of the last credentials known to work, rather than even trying explicit credentials.'

# Disables or enables globally which Remote Management channels can be used
Set-DbaConfig -FullName 'ComputerManagement.Type.Disable.CimRM' -Value $false -Initialize -Validation bool -Handler { [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::DisableConnectionCimRM = $args[0] } -Description 'Globally disables all connections using Cim over WinRM'
Set-DbaConfig -FullName 'ComputerManagement.Type.Disable.CimDCOM' -Value $false -Initialize -Validation bool -Handler { [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::DisableConnectionCimDCOM = $args[0] } -Description 'Globally disables all connections using Cim over DCOM'
Set-DbaConfig -FullName 'ComputerManagement.Type.Disable.WMI' -Value $true -Initialize -Validation bool -Handler { [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::DisableConnectionWMI = $args[0] } -Description 'Globally disables all connections using WMI'
Set-DbaConfig -FullName 'ComputerManagement.Type.Disable.PowerShellRemoting' -Value $true -Initialize -Validation bool -Handler { [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::DisableConnectionPowerShellRemoting = $args[0] } -Description 'Globally disables all connections using PowerShell Remoting'
#TODO: Implement Handler for type validation
tools\dbatools\internal\configurations\settings\formatting.ps1
# The default formatting style for dates
Set-DbaConfig -FullName 'Formatting.Date' -Value "dd MMM yyyy" -Initialize -Validation string -Handler { [Sqlcollaborative.Dbatools.Utility.UtilityHost]::FormatDate = $args[0] } -Description "The default formatting of Dates"

# The default formatting style for full datetime objects
Set-DbaConfig -FullName 'Formatting.DateTime' -Value "yyyy-MM-dd HH:mm:ss.fff" -Initialize -Validation string -Handler { [Sqlcollaborative.Dbatools.Utility.UtilityHost]::FormatDateTime = $args[0] } -Description "The default formatting style for full datetime objects"

# The default formatting style for time objects
Set-DbaConfig -FullName 'Formatting.Time' -Value "HH:mm:ss" -Initialize -Validation string -Handler { [Sqlcollaborative.Dbatools.Utility.UtilityHost]::FormatTime = $args[0] } -Description "The default formatting style for full datetime objects"

# Disable custom Datetime formats
Set-DbaConfig -FullName 'Formatting.Disable.CustomDateTime' -Value $false -Initialize -Validation bool -Handler { [Sqlcollaborative.Dbatools.Utility.UtilityHost]::DisableCustomDateTime = $args[0] } -Description "Controls whether custom DateTime formats are used or whether to default back to DateTime standard."

# Disable custom TimeSpan formats
Set-DbaConfig -FullName 'Formatting.Disable.CustomTimeSpan' -Value $false -Initialize -Validation bool -Handler { [Sqlcollaborative.Dbatools.Utility.UtilityHost]::DisableCustomTimeSpan = $args[0] } -Description "Controls whether custom TimeSpan formats are used or whether to default back to DateTime standard."

Set-DbaConfig -FullName 'Formatting.size.style' -Value ([Sqlcollaborative.Dbatools.Utility.SizeStyle]::Dynamic) -Initialize -Validation sizestyle -Handler { [Sqlcollaborative.Dbatools.Utility.UtilityHost]::SizeStyle = $args[0] } -Description "Controls how size objects are displayed by default. Generally, their string representation is calculated to be user friendly (dynamic), can be updated to 'plain' number or a specific size. Can be overriden on a per-object basis."
Set-DbaConfig -FullName 'Formatting.size.digits' -Value 2 -Initialize -Validation integer0to9 -Handler { [Sqlcollaborative.Dbatools.Utility.UtilityHost]::SizeDigits = $args[0] } -Description "How many digits are used when displaying a size object."
tools\dbatools\internal\configurations\settings\import.ps1
# Handle dotsourcing on import
Set-DbaConfig -Name 'Import.DoDotSource' -Value $false -Initialize -Validation bool -Handler {
    try {
        if (-not (Test-Path "HKCU:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\System")) { $null = New-Item "HKCU:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\System" -ItemType Container -Force -ErrorAction Stop }
        if ($args[0]) { $null = New-ItemProperty "HKCU:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\System" -Name DoDotSource -PropertyType DWORD -Value 1 -Force -ErrorAction Stop }
        else { $null = New-ItemProperty "HKCU:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\System" -Name DoDotSource -PropertyType DWORD -Value 0 -Force -ErrorAction Stop }
        # Scope Boundary exception: $cfg is defined in Set-DbaConfig
        Register-DbaConfig -Config $cfg
    }
    catch {
        Write-Message -Level Warning -Message "Failed to apply configuration 'Import.DoDotSource'" -ErrorRecord $_ -Target 'Import.DoDotSource'
    }
} -Description "Causes the module to be imported using dotsourcing. Security policy may require it, also useful for debugging. This configuration setting persists across all PowerShell consoles for this user!"

# Handle dotsourcing on import
Set-DbaConfig -Name 'Import.StrictSecurityMode' -Value $false -Initialize -Validation bool -Handler {
    try {
        if (-not (Test-Path "HKCU:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\System")) { $null = New-Item "HKCU:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\System" -ItemType Container -Force -ErrorAction Stop }
        if ($args[0]) { $null = New-ItemProperty "HKCU:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\System" -Name StrictSecurityMode -PropertyType DWORD -Value 1 -Force -ErrorAction Stop }
        else { $null = New-ItemProperty "HKCU:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\System" -Name StrictSecurityMode -PropertyType DWORD -Value 0 -Force -ErrorAction Stop }
        # Scope Boundary exception: $cfg is defined in Set-DbaConfig
        Register-DbaConfig -Config $cfg
    }
    catch {
        Write-Message -Level Warning -Message "Failed to apply configuration 'Import.StrictSecurityMode'" -ErrorRecord $_ -Target 'Import.StrictSecurityMode'
    }
} -Description "Causes the module to import its components only from the module directory. This makes it harder to update the module, but may be required by security policy. This configuration setting persists across all PowerShell consoles for this user!"

# Handle dotsourcing on import
Set-DbaConfig -Name 'Import.AlwaysBuildLibrary' -Value $false -Initialize -Validation bool -Handler {
    try {
        if (-not (Test-Path "HKCU:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\System")) { $null = New-Item "HKCU:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\System" -ItemType Container -Force -ErrorAction Stop }
        if ($args[0]) { $null = New-ItemProperty "HKCU:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\System" -Name AlwaysBuildLibrary -PropertyType DWORD -Value 1 -Force -ErrorAction Stop }
        else { $null = New-ItemProperty "HKCU:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\System" -Name AlwaysBuildLibrary -PropertyType DWORD -Value 0 -Force -ErrorAction Stop }
        # Scope Boundary exception: $cfg is defined in Set-DbaConfig
        Register-DbaConfig -Config $cfg
    }
    catch {
        Write-Message -Level Warning -Message "Failed to apply configuration 'Import.AlwaysBuildLibrary'" -ErrorRecord $_ -Target 'Import.AlwaysBuildLibrary'
    }
} -Description "Causes the module to compile the library from source on every import. Of interest for developers only, as this imposes a significant increase in import time. This configuration setting persists across all PowerShell consoles for this user!"

# Handle dotsourcing on import
Set-DbaConfig -Name 'Import.SerialImport' -Value $false -Initialize -Validation bool -Handler {
    try {
        if (-not (Test-Path "HKCU:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\System")) { $null = New-Item "HKCU:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\System" -ItemType Container -Force -ErrorAction Stop }
        if ($args[0]) { $null = New-ItemProperty "HKCU:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\System" -Name SerialImport -PropertyType DWORD -Value 1 -Force -ErrorAction Stop }
        else { $null = New-ItemProperty "HKCU:\SOFTWARE\Microsoft\WindowsPowerShell\dbatools\System" -Name SerialImport -PropertyType DWORD -Value 0 -Force -ErrorAction Stop }
        # Scope Boundary exception: $cfg is defined in Set-DbaConfig
        Register-DbaConfig -Config $cfg
    }
    catch {
        Write-Message -Level Warning -Message "Failed to apply configuration 'Import.SerialImport'" -ErrorRecord $_ -Target 'Import.SerialImport'
    }
} -Description "Enabling this will cause the module to perform import in a serial manner, not parallelizing anything. This will impose a significant delay on import, but reduces the CPU impact during import. Setting this for an unattended script may be useful to avoid resource alerts. Can be set on script level by placing the following code in the first line: '`$dbatools_serialimport = `$true'. This configuration setting persists across all PowerShell consoles for this user!"
tools\dbatools\internal\configurations\settings\logging.ps1
<#
This is for all configuration values regarding the logging system

NOTES:
- All these configurations should have a handler, as the logging system relies entirely on static fields for performance reasons
- If you want to change the default values, change them both here AND in the C# library
#>

Set-DbaConfig -FullName 'Logging.MaxErrorCount' -Value 128 -Initialize -Validation integerpositive -Handler { [Sqlcollaborative.Dbatools.Message.LogHost]::MaxErrorCount = $args[0] } -Description "The maximum number of error records maintained in-memory. This setting is on a per-Process basis. Runspaces share, jobs or other consoles counted separately."
Set-DbaConfig -FullName 'Logging.MaxMessageCount' -Value 1024 -Initialize -Validation integerpositive -Handler { [Sqlcollaborative.Dbatools.Message.LogHost]::MaxMessageCount = $args[0] } -Description "The maximum number of messages that can be maintained in the in-memory message queue. This setting is on a per-Process basis. Runspaces share, jobs or other consoles counted separately."
Set-DbaConfig -FullName 'Logging.MaxMessagefileBytes' -Value 5MB -Initialize -Validation integerpositive -Handler { [Sqlcollaborative.Dbatools.Message.LogHost]::MaxMessagefileBytes = $args[0] } -Description "The maximum size of a given logfile. When reaching this limit, the file will be abandoned and a new log created. Set to 0 to not limit the size. This setting is on a per-Process basis. Runspaces share, jobs or other consoles counted separately."
Set-DbaConfig -FullName 'Logging.MaxMessagefileCount' -Value 5 -Initialize -Validation integerpositive -Handler { [Sqlcollaborative.Dbatools.Message.LogHost]::MaxMessagefileCount = $args[0] } -Description "The maximum number of logfiles maintained at a time. Exceeding this number will cause the oldest to be culled. Set to 0 to disable the limit. This setting is on a per-Process basis. Runspaces share, jobs or other consoles counted separately."
Set-DbaConfig -FullName 'Logging.MaxErrorFileBytes' -Value 20MB -Initialize -Validation integerpositive -Handler { [Sqlcollaborative.Dbatools.Message.LogHost]::MaxErrorFileBytes = $args[0] } -Description "The maximum size all error files combined may have. When this number is exceeded, the oldest entry is culled. This setting is on a per-Process basis. Runspaces share, jobs or other consoles counted separately."
Set-DbaConfig -FullName 'Logging.MaxTotalFolderSize' -Value 100MB -Initialize -Validation integerpositive -Handler { [Sqlcollaborative.Dbatools.Message.LogHost]::MaxTotalFolderSize = $args[0] } -Description "This is the upper limit of length all items in the log folder may have combined across all processes."
Set-DbaConfig -FullName 'Logging.MaxLogFileAge' -Value (New-TimeSpan -Days 7) -Initialize -Validation timespan -Handler { [Sqlcollaborative.Dbatools.Message.LogHost]::MaxLogFileAge = $args[0] } -Description "Any logfile older than this will automatically be cleansed. This setting is global."
Set-DbaConfig -FullName 'Logging.MessageLogFileEnabled' -Value $true -Initialize -Validation bool -Handler { [Sqlcollaborative.Dbatools.Message.LogHost]::MessageLogFileEnabled = $args[0] } -Description "Governs, whether a log file for the system messages is written. This setting is on a per-Process basis. Runspaces share, jobs or other consoles counted separately."
Set-DbaConfig -FullName 'Logging.MessageLogEnabled' -Value $true -Initialize -Validation bool -Handler { [Sqlcollaborative.Dbatools.Message.LogHost]::MessageLogEnabled = $args[0] } -Description "Governs, whether a log of recent messages is kept in memory. This setting is on a per-Process basis. Runspaces share, jobs or other consoles counted separately."
Set-DbaConfig -FullName 'Logging.ErrorLogFileEnabled' -Value $true -Initialize -Validation bool -Handler { [Sqlcollaborative.Dbatools.Message.LogHost]::ErrorLogFileEnabled = $args[0] } -Description "Governs, whether log files for errors are written. This setting is on a per-Process basis. Runspaces share, jobs or other consoles counted separately."
Set-DbaConfig -FullName 'Logging.ErrorLogEnabled' -Value $true -Initialize -Validation bool -Handler { [Sqlcollaborative.Dbatools.Message.LogHost]::ErrorLogEnabled = $args[0] } -Description "Governs, whether a log of recent errors is kept in memory. This setting is on a per-Process basis. Runspaces share, jobs or other consoles counted separately."
tools\dbatools\internal\configurations\settings\paths.ps1
<#
This is designed for all paths you need configurations for.
#>

# The default path where dbatools stores persistent data
Set-DbaConfig -FullName 'Path.DbatoolsData' -Value "$env:AppData\PowerShell\dbatools" -Initialize -Validation string -Handler {  } -Description "The path where dbatools stores persistent data on a per user basis."

# The default path where dbatools stores temporary data
Set-DbaConfig -FullName 'Path.DbatoolsTemp' -Value ([System.IO.Path]::GetTempPath()).TrimEnd("\") -Initialize -Validation string -Handler { } -Description "The path where dbatools stores temporary data."

# The default path for writing logs
Set-DbaConfig -FullName 'Path.DbatoolsLogPath' -Value "$env:AppData\PowerShell\dbatools" -Initialize -Validation string -Handler { [Sqlcollaborative.Dbatools.Message.LogHost]::LoggingPath = $args[0] } -Description "The path where dbatools writes all its logs and debugging information."

# The default Path for where the tags Json is stored
Set-DbaConfig -FullName 'Path.TagCache' -Value "$ModuleRoot\bin\dbatools-index.json" -Initialize -Validation string -Handler { } -Description "The file in which dbatools stores the tag cache. That cache is used in Find-DbaCommand for more comfortable autocomplete"

# The default Path for the server list (Get-DbaServerList, etc)
Set-DbaConfig -FullName 'Path.Servers' -Value "$env:AppData\PowerShell\dbatools\servers.xml" -Initialize -Validation string -Handler { } -Description "The file in which dbatools stores the current user's server list, as managed by Get/Add/Update-DbaServerList"
tools\dbatools\internal\configurations\settings\remoting.ps1
# Handles PowerShell Session scrapping timeout
Set-DbaConfig -FullName 'PSRemoting.Sessions.ExpirationTimeout' -Value (New-TimeSpan -Minutes 5) -Initialize -Validation timespan -Handler { [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::PSSessionTimeout = $args[0] } -Description 'The timeout interval for PowerShell remote sessions. Dbatools will kill sessions that have been idle for this amount of time.'

# Disables session caching
Set-DbaConfig -FullName 'PSRemoting.Sessions.Enable' -Value $true -Initialize -Validation bool -Handler { [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::PSSessionCacheEnabled = $args[0] } -Description 'Globally enables session caching for PowerShell remoting'
tools\dbatools\internal\configurations\settings\sql.ps1
# Controls the timeout on sql connects
Set-DbaConfig -FullName 'sql.connection.timeout' -Value 15 -Initialize -Validation integerpositive -Handler { [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::SqlConnectionTimeout = $args[0] } -Description "The number of seconds before sql server connection attempts are aborted"
tools\dbatools\internal\configurations\settings\tabexpansion.ps1
# Sets the default interval and timeout for TEPP updates
Set-DbaConfig -FullName 'TabExpansion.UpdateInterval' -Value (New-TimeSpan -Minutes 3) -Initialize -Validation timespan -Handler { [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::TeppUpdateInterval = $args[0] } -Description 'The frequency in which TEPP tries to update each cache for autocompletion'
Set-DbaConfig -FullName 'TabExpansion.UpdateTimeout' -Value (New-TimeSpan -Seconds 30) -Initialize -Validation timespan -Handler { [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::TeppUpdateTimeout = $args[0] } -Description 'After this timespan has passed without connections to a server, the TEPP updater will no longer update the cache.'

# Disable the management cache entire
Set-DbaConfig -FullName 'TabExpansion.Disable' -Value $false -Initialize -Validation bool -Handler {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::TeppDisabled = $args[0]

    # Disable Async TEPP runspace if not needed
    if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::TeppAsyncDisabled -or [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::TeppDisabled) {
        [Sqlcollaborative.Dbatools.Runspace.RunspaceHost]::Runspaces["dbatools-teppasynccache"].Stop()
    }
    else {
        [Sqlcollaborative.Dbatools.Runspace.RunspaceHost]::Runspaces["dbatools-teppasynccache"].Start()
    }
} -Description 'Globally disables all TEPP functionality by dbatools'
Set-DbaConfig -FullName 'TabExpansion.Disable.Asynchronous' -Value $false -Initialize -Validation bool -Handler {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::TeppAsyncDisabled = $args[0]

    # Disable Async TEPP runspace if not needed
    if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::TeppAsyncDisabled -or [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::TeppDisabled) {
        [Sqlcollaborative.Dbatools.Runspace.RunspaceHost]::Runspaces["dbatools-teppasynccache"].Stop()
    }
    else {
        [Sqlcollaborative.Dbatools.Runspace.RunspaceHost]::Runspaces["dbatools-teppasynccache"].Start()
    }
} -Description 'Globally disables asynchronous TEPP updates in the background'
Set-DbaConfig -FullName 'TabExpansion.Disable.Synchronous' -Value $true -Initialize -Validation bool -Handler { [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::TeppSyncDisabled = $args[0] } -Description 'Globally disables synchronous TEPP updates, performed whenever connecting o the server. If this is not disabled, it will only perform updates that are fast to perform, in order to minimize performance impact. This may lead to some TEPP functionality loss if asynchronous updates are disabled.'
tools\dbatools\internal\configurations\settings\userinteraction.ps1
<#
This configuration file is for all settings of how dbatools interacts with users
#>

# Configure the message levels at which the function will write either an info, a verbose message or debug message
# Used by the internal "Write-Message" function
Set-DbaConfig -Name 'message.maximuminfo' -Value 3 -Initialize -Validation integer0to9 -Handler { [Sqlcollaborative.Dbatools.Message.MessageHost]::MaximumInformation = $args[0] } -Description "The maximum message level to still display to the user directly."
Set-DbaConfig -Name 'message.maximumverbose' -Value 6 -Initialize -Validation integer0to9 -Handler { [Sqlcollaborative.Dbatools.Message.MessageHost]::MaximumVerbose = $args[0] } -Description "The maxium message level where verbose information is still written."
Set-DbaConfig -Name 'message.maximumdebug' -Value 9 -Initialize -Validation integer0to9 -Handler { [Sqlcollaborative.Dbatools.Message.MessageHost]::MaximumDebug = $args[0] } -Description "The maximum message level where debug information is still written."
Set-DbaConfig -Name 'message.minimuminfo' -Value 1 -Initialize -Validation integer0to9 -Handler { [Sqlcollaborative.Dbatools.Message.MessageHost]::MinimumInformation = $args[0] } -Description "The minimum required message level for messages that will be shown to the user."
Set-DbaConfig -Name 'message.minimumverbose' -Value 4 -Initialize -Validation integer0to9 -Handler { [Sqlcollaborative.Dbatools.Message.MessageHost]::MinimumVerbose = $args[0] } -Description "The minimum required message level where verbose information is written."
Set-DbaConfig -Name 'message.minimumdebug' -Value 1 -Initialize -Validation integer0to9 -Handler { [Sqlcollaborative.Dbatools.Message.MessageHost]::MinimumDebug = $args[0] } -Description "The minimum required message level where debug information is written."

# Default color used by the "Write-Message" function in info mode
Set-DbaConfig -Name 'message.infocolor' -Value 'Cyan' -Initialize -Validation consolecolor -Handler { [Sqlcollaborative.Dbatools.Message.MessageHost]::InfoColor = $args[0] } -Description "The color to use when writing text to the screen on PowerShell."
Set-DbaConfig -Name 'message.developercolor' -Value 'Grey' -Initialize -Validation consolecolor -Handler { [Sqlcollaborative.Dbatools.Message.MessageHost]::DeveloperColor = $args[0] } -Description "The color to use when writing text with developer specific additional information to the screen on PowerShell."
Set-DbaConfig -Name 'message.info.color.emphasis' -Value 'green' -Initialize -Validation "consolecolor" -Handler { [Sqlcollaborative.Dbatools.Message.MessageHost]::InfoColorEmphasis = $args[0] } -Description "The color to use when emphasizing written text to the screen on PowerShell."
Set-DbaConfig -Name 'message.info.color.subtle' -Value 'gray' -Initialize -Validation "consolecolor" -Handler { [Sqlcollaborative.Dbatools.Message.MessageHost]::InfoColorSubtle = $args[0] } -Description "The color to use when making writing text to the screen on PowerShell appear subtle."
Set-DbaConfig -Name 'message.consoleoutput.disable' -Value $false -Initialize -Validation "bool" -Handler { [Sqlcollaborative.Dbatools.Message.MessageHost]::DisableVerbosity = $args[0] } -Description "Global toggle that allows disabling all regular messages to screen. Messages from '-Verbose' and '-Debug' are unaffected"
Set-DbaConfig -Name 'message.transform.errorqueuesize' -Value 512 -Initialize -Validation "integerpositive" -Handler { [Sqlcollaborative.Dbatools.Message.MessageHost]::TransformErrorQueueSize = $args[0] } -Description "The size of the queue for transformation errors. May be useful for advanced development, but can be ignored usually."
Set-DbaConfig -Name 'message.nestedlevel.decrement' -Value 0 -Initialize -Validation "integer0to9" -Handler { [Sqlcollaborative.Dbatools.Message.MessageHost]::NestedLevelDecrement = $args[0] } -Description "How many levels should be reduced per callstack depth. This makes commands less verbose, the more nested they are called"

# Messaging mode in non-critical terminations
Set-DbaConfig -Name 'message.mode.default' -Value ([DbaMode]::Strict) -Initialize -Validation string -Handler { } -Description "The mode controls how some functions handle non-critical terminations by default. Strict: Write a warning | Lazy: Write a message | Report: Generate a report object"
Set-DbaConfig -Name 'message.mode.lazymessagelevel' -Value 4 -Initialize -Validation integer0to9 -Handler { } -Description "At what level will the lazy message be written? (By default invisible to the user)"

# Enable Developer mode
Set-DbaConfig -Name 'developer.mode.enable' -Value $false -Initialize -Validation bool -Handler { [Sqlcollaborative.Dbatools.Message.MessageHost]::DeveloperMode = $args[0] } -Description "Developermode enables advanced logging and verbosity features. There is little benefit for enabling this as a regular user. but developers can use it to more easily troubleshoot issues."

# Message display style options
Set-DbaConfig -Name 'message.style.breadcrumbs' -Value $false -Initialize -Validation "bool" -Handler { [Sqlcollaborative.Dbatools.Message.MessageHost]::EnableMessageBreadcrumbs = $args[0] } -Description "Controls how messages are displayed. Enables Breadcrumb display, showing the entire callstack. Takes precedence over command name display."
Set-DbaConfig -Name 'message.style.functionname' -Value $true -Initialize -Validation "bool" -Handler { [Sqlcollaborative.Dbatools.Message.MessageHost]::EnableMessageDisplayCommand = $args[0] } -Description "Controls how messages are displayed. Enables command name, showing the name of the writing command. Is overwritten by enabling breadcrumbs."
Set-DbaConfig -Name 'message.style.timestamp' -Value $true -Initialize -Validation "bool" -Handler { [Sqlcollaborative.Dbatools.Message.MessageHost]::EnableMessageTimestamp = $args[0] } -Description "Controls how messages are displayed. Enables timestamp display, including a timestamp in each message."
tools\dbatools\internal\configurations\validation\bool.ps1
Register-DbaConfigValidation -Name "bool" -ScriptBlock {
    Param (
        $Value
    )

    $Result = New-Object PSObject -Property @{
        Success = $True
        Value   = $null
        Message = ""
    }
    try {
        if ($Value.GetType().FullName -ne "System.Boolean") {
            $Result.Message = "Not a boolean: $Value"
            $Result.Success = $False
            return $Result
        }
    }
    catch {
        $Result.Message = "Not a boolean: $Value"
        $Result.Success = $False
        return $Result
    }

    $Result.Value = $Value

    return $Result
}
tools\dbatools\internal\configurations\validation\consolecolor.ps1
Register-DbaConfigValidation -Name "consolecolor" -ScriptBlock {
    Param (
        $Value
    )

    $Result = New-Object PSObject -Property @{
        Success = $True
        Value   = $null
        Message = ""
    }

    try { [System.ConsoleColor]$color = $Value }
    catch {
        $Result.Message = "Not a console color: $Value"
        $Result.Success = $False
        return $Result
    }

    $Result.Value = $color

    return $Result
}
tools\dbatools\internal\configurations\validation\datetime.ps1
Register-DbaConfigValidation -Name "datetime" -ScriptBlock {
    Param (
        $Value
    )

    $Result = New-Object PSObject -Property @{
        Success = $True
        Value   = $null
        Message = ""
    }

    try { [DateTime]$DateTime = $Value }
    catch {
        $Result.Message = "Not a DateTime: $Value"
        $Result.Success = $False
        return $Result
    }

    $Result.Value = $DateTime

    return $Result
}
tools\dbatools\internal\configurations\validation\double.ps1
Register-DbaConfigValidation -Name "double" -ScriptBlock {
    Param (
        $Value
    )

    $Result = New-Object PSOBject -Property @{
        Success = $True
        Value   = $null
        Message = ""
    }

    try { [double]$number = $Value }
    catch {
        $Result.Message = "Not a double: $Value"
        $Result.Success = $False
        return $Result
    }

    $Result.Value = $number

    return $Result
}
tools\dbatools\internal\configurations\validation\integer.ps1
Register-DbaConfigValidation -Name "integer" -ScriptBlock {
    Param (
        $Value
    )

    $Result = New-Object PSOBject -Property @{
        Success = $True
        Value   = $null
        Message = ""
    }

    try { [int]$number = $Value }
    catch {
        $Result.Message = "Not an integer: $Value"
        $Result.Success = $False
        return $Result
    }

    $Result.Value = $number

    return $Result
}
tools\dbatools\internal\configurations\validation\integer0to9.ps1
Register-DbaConfigValidation -Name "integer0to9" -ScriptBlock {
    Param (
        $Value
    )

    $Result = New-Object PSOBject -Property @{
        Success = $True
        Value   = $null
        Message = ""
    }

    try { [int]$number = $Value }
    catch {
        $Result.Message = "Not an integer: $Value"
        $Result.Success = $False
        return $Result
    }

    if (($number -lt 0) -or ($number -gt 9)) {
        $Result.Message = "Out of range. Specify a number ranging from 0 to 9"
        $Result.Success = $False
        return $Result
    }

    $Result.Value = $Number

    return $Result
}
tools\dbatools\internal\configurations\validation\integerpositive.ps1
Register-DbaConfigValidation -Name "integerpositive" -ScriptBlock {
    Param (
        $Value
    )

    $Result = New-Object PSOBject -Property @{
        Success = $True
        Value   = $null
        Message = ""
    }

    try { [int]$number = $Value }
    catch {
        $Result.Message = "Not an integer: $Value"
        $Result.Success = $False
        return $Result
    }

    if ($number -lt 0) {
        $Result.Message = "Negative value: $Value"
        $Result.Success = $False
        return $Result
    }

    $Result.Value = $number

    return $Result
}
tools\dbatools\internal\configurations\validation\long.ps1
Register-DbaConfigValidation -Name "long" -ScriptBlock {
    Param (
        $Value
    )

    $Result = New-Object PSOBject -Property @{
        Success = $True
        Value   = $null
        Message = ""
    }

    try { [long]$number = $Value }
    catch {
        $Result.Message = "Not a long: $Value"
        $Result.Success = $False
        return $Result
    }

    $Result.Value = $number

    return $Result
}
tools\dbatools\internal\configurations\validation\sizestyle.ps1
Register-DbaConfigValidation -Name "sizestyle" -ScriptBlock {
    param (
        $Value
    )

    $Result = New-Object PSObject -Property @{
        Success  = $True
        Value    = $null
        Message  = ""
    }

    try { [Sqlcollaborative.Dbatools.Utility.SizeStyle]$style = $Value }
    catch
    {
        $Result.Message = "Not a size style: $Value"
        $Result.Success = $False
        return $Result
    }

    $Result.Value = $style

    return $Result
}
tools\dbatools\internal\configurations\validation\string.ps1
Register-DbaConfigValidation -Name "string" -ScriptBlock {
    Param (
        $Value
    )

    $Result = New-Object PSObject -Property @{
        Success = $True
        Value   = $null
        Message = ""
    }

    try {
        # Seriously, this should work for almost anybody and anything
        [string]$data = $Value
    }
    catch {
        $Result.Message = "Not a string: $Value"
        $Result.Success = $False
        return $Result
    }

    if ([string]::IsNullOrEmpty($data)) {
        $Result.Message = "Is an empty string: $Value"
        $Result.Success = $False
        return $Result
    }

    if ($data -eq $Value.GetType().FullName) {
        $Result.Message = "Is an object with no proper string representation: $Value"
        $Result.Success = $False
        return $Result
    }

    $Result.Value = $data

    return $Result
}
tools\dbatools\internal\configurations\validation\timespan.ps1
Register-DbaConfigValidation -Name "timespan" -ScriptBlock {
    Param (
        $Value
    )

    $Result = New-Object PSObject -Property @{
        Success = $True
        Value   = $null
        Message = ""
    }

    try { [timespan]$timespan = $Value }
    catch {
        $Result.Message = "Not a Timespan: $Value"
        $Result.Success = $False
        return $Result
    }

    $Result.Value = $timespan

    return $Result
}
tools\dbatools\internal\dynamicparams\alert.ps1
#region Initialize Cache
if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["alert"]) {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["alert"] = @{ }
}
#endregion Initialize Cache

#region Tepp Data return
$ScriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )


    $server = $fakeBoundParameter['SqlInstance']

    if (-not $server) {
        $server = $fakeBoundParameter['Source']
    }

    if (-not $server) {
        $server = $fakeBoundParameter['ComputerName']
    }

    if (-not $server) { return }

    try {
        [DbaInstanceParameter]$parServer = $server | Select-Object -First 1
    }
    catch {
        return
    }

    if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["alert"][$parServer.FullSmoName.ToLower()]) {
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["alert"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }

    try {
        $serverObject = Connect-SqlInstance -SqlInstance $parServer -SqlCredential $fakeBoundParameter['SqlCredential'] -ErrorAction Stop
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["alert"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }
    catch {
        return
    }
}

Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name alert
#endregion Tepp Data return

#region Update Cache
$ScriptBlock = {

    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["alert"][$FullSmoName] = $server.JobServer.Alerts.Name
}
Register-DbaTeppInstanceCacheBuilder -ScriptBlock $ScriptBlock
#endregion Update Cache
tools\dbatools\internal\dynamicparams\alertcategory.ps1
#region Initialize Cache
if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["alertcategory"]) {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["alertcategory"] = @{ }
}
#endregion Initialize Cache

#region Tepp Data return
$ScriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )


    $server = $fakeBoundParameter['SqlInstance']

    if (-not $server) {
        $server = $fakeBoundParameter['Source']
    }

    if (-not $server) {
        $server = $fakeBoundParameter['ComputerName']
    }

    if (-not $server) { return }

    try {
        [DbaInstanceParameter]$parServer = $server | Select-Object -First 1
    }
    catch {
        return
    }

    if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["alertcategory"][$parServer.FullSmoName.ToLower()]) {
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["alertcategory"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }

    try {
        $serverObject = Connect-SqlInstance -SqlInstance $parServer -SqlCredential $fakeBoundParameter['SqlCredential'] -ErrorAction Stop
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["alertcategory"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }
    catch {
        return
    }
}

Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name AlertCategory
#endregion Tepp Data return

#region Update Cache
$ScriptBlock = {

    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["alertcategory"][$FullSmoName] = $server.JobServer.AlertCategories.Name
}
Register-DbaTeppInstanceCacheBuilder -ScriptBlock $ScriptBlock
#endregion Update Cache
tools\dbatools\internal\dynamicparams\audit.ps1
#region Initialize Cache
if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["audit"]) {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["audit"] = @{ }
}
#endregion Initialize Cache

#region Tepp Data return
$ScriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )


    $server = $fakeBoundParameter['SqlInstance']

    if (-not $server) {
        $server = $fakeBoundParameter['Source']
    }

    if (-not $server) {
        $server = $fakeBoundParameter['ComputerName']
    }

    if (-not $server) { return }

    try {
        [DbaInstanceParameter]$parServer = $server | Select-Object -First 1
    }
    catch {
        return
    }

    if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["audit"][$parServer.FullSmoName.ToLower()]) {
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["audit"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }

    try {
        $serverObject = Connect-SqlInstance -SqlInstance $parServer -SqlCredential $fakeBoundParameter['SqlCredential'] -ErrorAction Stop
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["audit"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }
    catch {
        return
    }
}

Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name Audit
#endregion Tepp Data return

#region Update Cache
$ScriptBlock = {

    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["audit"][$FullSmoName] = $server.Audits.Name
}
Register-DbaTeppInstanceCacheBuilder -ScriptBlock $ScriptBlock
#endregion Update Cache
tools\dbatools\internal\dynamicparams\auditspecification.ps1
#region Initialize Cache
if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["auditspecification"]) {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["auditspecification"] = @{ }
}
#endregion Initialize Cache

#region Tepp Data return
$ScriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )


    $server = $fakeBoundParameter['SqlInstance']

    if (-not $server) {
        $server = $fakeBoundParameter['Source']
    }

    if (-not $server) {
        $server = $fakeBoundParameter['ComputerName']
    }

    if (-not $server) { return }

    try {
        [DbaInstanceParameter]$parServer = $server | Select-Object -First 1
    }
    catch {
        return
    }

    if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["auditspecification"][$parServer.FullSmoName.ToLower()]) {
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["auditspecification"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }

    try {
        $serverObject = Connect-SqlInstance -SqlInstance $parServer -SqlCredential $fakeBoundParameter['SqlCredential'] -ErrorAction Stop
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["auditspecification"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }
    catch {
        return
    }
}

Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name AuditSpecification
#endregion Tepp Data return

#region Update Cache
$ScriptBlock = {

    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["auditspecification"][$FullSmoName] = $server.AuditSpecifications.Name
}
Register-DbaTeppInstanceCacheBuilder -ScriptBlock $ScriptBlock
#endregion Update Cache
tools\dbatools\internal\dynamicparams\availabilitygroup.ps1
#region Initialize Cache
if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["availabilitygroup"]) {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["availabilitygroup"] = @{ }
}
#endregion Initialize Cache

#region Tepp Data return
$ScriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )


    $server = $fakeBoundParameter['SqlInstance']

    if (-not $server) {
        $server = $fakeBoundParameter['Source']
    }

    if (-not $server) {
        $server = $fakeBoundParameter['ComputerName']
    }

    if (-not $server) { return }

    try {
        [DbaInstanceParameter]$parServer = $server | Select-Object -First 1
    }
    catch {
        return
    }

    if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["availabilitygroup"][$parServer.FullSmoName.ToLower()]) {
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["availabilitygroup"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }

    try {
        $serverObject = Connect-SqlInstance -SqlInstance $parServer -SqlCredential $fakeBoundParameter['SqlCredential'] -ErrorAction Stop
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["availabilitygroup"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }
    catch {
        return
    }
}

Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name AvailabilityGroup
#endregion Tepp Data return

#region Update Cache
$ScriptBlock = {

    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["availabilitygroup"][$FullSmoName] = $server.AvailabilityGroups.Name
}
Register-DbaTeppInstanceCacheBuilder -ScriptBlock $ScriptBlock
#endregion Update Cache
tools\dbatools\internal\dynamicparams\backupdevice.ps1
#region Initialize Cache
if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["backupdevice"]) {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["backupdevice"] = @{ }
}
#endregion Initialize Cache

#region Tepp Data return
$ScriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )


    $server = $fakeBoundParameter['SqlInstance']

    if (-not $server) {
        $server = $fakeBoundParameter['Source']
    }

    if (-not $server) {
        $server = $fakeBoundParameter['ComputerName']
    }

    if (-not $server) { return }

    try {
        [DbaInstanceParameter]$parServer = $server | Select-Object -First 1
    }
    catch {
        return
    }

    if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["backupdevice"][$parServer.FullSmoName.ToLower()]) {
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["backupdevice"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }

    try {
        $serverObject = Connect-SqlInstance -SqlInstance $parServer -SqlCredential $fakeBoundParameter['SqlCredential'] -ErrorAction Stop
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["backupdevice"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }
    catch {
        return
    }
}

Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name BackupDevice
#endregion Tepp Data return

#region Update Cache
$ScriptBlock = {

    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["backupdevice"][$FullSmoName] = $server.BackupDevices.Name
}
Register-DbaTeppInstanceCacheBuilder -ScriptBlock $ScriptBlock
#endregion Update Cache
tools\dbatools\internal\dynamicparams\config.ps1
#region Tepp Data return: FullName
$ScriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )


    foreach ($name in ([Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations.Values | Where-Object { -not $_.Hidden -and ($_.FullName -Like "$wordToComplete*") } | Select-Object -ExpandProperty FullName | Sort-Object)) {
        New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
    }
}

Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name config
#endregion Tepp Data return: FullName

#region Tepp Data return: Name
$ScriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )


    $moduleName = "*"
    if ($fakeBoundParameter.Module) { $moduleName = $fakeBoundParameter.Module }

    foreach ($name in ([Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations.Values | Where-Object { (-not $_.Hidden) -and ($_.Name -Like "$wordToComplete*") -and ($_.Module -like $moduleName) } | Select-Object -ExpandProperty Name | Sort-Object)) {
        New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
    }
}

Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name config_name
#endregion Tepp Data return: Name

#region Tepp Data return: Module
$ScriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )


    foreach ($name in ([Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations.Values.Module | Select-Object -Unique | Where-DbaObject -Like "$wordToComplete*" | Sort-Object )) {
        New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
    }
}

Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name configmodule
#endregion Tepp Data return: Module
tools\dbatools\internal\dynamicparams\configname.ps1
#region Initialize Cache
if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["configname"]) {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["configname"] = @{ }
}
#endregion Initialize Cache

#region Tepp Data return
$ScriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )


    $server = $fakeBoundParameter['SqlInstance']

    if (-not $server) {
        $server = $fakeBoundParameter['Source']
    }

    if (-not $server) {
        $server = $fakeBoundParameter['ComputerName']
    }

    if (-not $server) { return }

    try {
        [DbaInstanceParameter]$parServer = $server | Select-Object -First 1
    }
    catch {
        return
    }

    if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["configname"][$parServer.FullSmoName.ToLower()]) {
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["configname"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }

    try {
        $serverObject = Connect-SqlInstance -SqlInstance $parServer -SqlCredential $fakeBoundParameter['SqlCredential'] -ErrorAction Stop
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["configname"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }
    catch {
        return
    }
}

Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name ConfigName
#endregion Tepp Data return

#region Update Cache
$ScriptBlock = {

    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["configname"][$FullSmoName] = (Get-Member -InputObject $server.Configuration -MemberType Property -Force | Where-Object Name -NotIn 'Parent',
        'Properties').Name
}
Register-DbaTeppInstanceCacheBuilder -ScriptBlock $ScriptBlock
#endregion Update Cache
tools\dbatools\internal\dynamicparams\credential.ps1
#region Initialize Cache
if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["credential"]) {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["credential"] = @{ }
}
#endregion Initialize Cache

#region Tepp Data return
$ScriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )


    $server = $fakeBoundParameter['SqlInstance']

    if (-not $server) {
        $server = $fakeBoundParameter['Source']
    }

    if (-not $server) {
        $server = $fakeBoundParameter['ComputerName']
    }

    if (-not $server) { return }

    try {
        [DbaInstanceParameter]$parServer = $server | Select-Object -First 1
    }
    catch {
        return
    }

    if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["credential"][$parServer.FullSmoName.ToLower()]) {
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["credential"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }

    try {
        $serverObject = Connect-SqlInstance -SqlInstance $parServer -SqlCredential $fakeBoundParameter['SqlCredential'] -ErrorAction Stop
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["credential"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }
    catch {
        return
    }
}

Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name Credential
#endregion Tepp Data return

#region Update Cache
$ScriptBlock = {

    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["credential"][$FullSmoName] = $server.Credentials.Name
}
Register-DbaTeppInstanceCacheBuilder -ScriptBlock $ScriptBlock
#endregion Update Cache
tools\dbatools\internal\dynamicparams\customerror.ps1
#region Initialize Cache
if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["customerror"]) {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["customerror"] = @{ }
}
#endregion Initialize Cache

#region Tepp Data return
$ScriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )


    $server = $fakeBoundParameter['SqlInstance']

    if (-not $server) {
        $server = $fakeBoundParameter['Source']
    }

    if (-not $server) {
        $server = $fakeBoundParameter['ComputerName']
    }

    if (-not $server) { return }

    try {
        [DbaInstanceParameter]$parServer = $server | Select-Object -First 1
    }
    catch {
        return
    }

    if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["customerror"][$parServer.FullSmoName.ToLower()]) {
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["customerror"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }

    try {
        $serverObject = Connect-SqlInstance -SqlInstance $parServer -SqlCredential $fakeBoundParameter['SqlCredential'] -ErrorAction Stop
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["customerror"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }
    catch {
        return
    }
}

Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name CustomError
#endregion Tepp Data return

#region Update Cache
$ScriptBlock = {

    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["customerror"][$FullSmoName] = $server.UserDefinedMessages.ID
}
Register-DbaTeppInstanceCacheBuilder -ScriptBlock $ScriptBlock
#endregion Update Cache
tools\dbatools\internal\dynamicparams\database.ps1
#region Initialize Cache
if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["database"]) {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["database"] = @{ }
}
#endregion Initialize Cache

#region Tepp Data return
$ScriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )


    $server = $fakeBoundParameter['SqlInstance']

    if (-not $server) {
        $server = $fakeBoundParameter['Source']
    }

    if (-not $server) {
        $server = $fakeBoundParameter['ComputerName']
    }

    if (-not $server) { return }

    try {
        [DbaInstanceParameter]$parServer = $server | Select-Object -First 1
    }
    catch {
        return
    }

    if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["database"][$parServer.FullSmoName.ToLower()]) {
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["database"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }

    try {
        $serverObject = Connect-SqlInstance -SqlInstance $parServer -SqlCredential $fakeBoundParameter['SqlCredential'] -ErrorAction Stop
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["database"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }
    catch {
        return
    }
}

Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name Database
#endregion Tepp Data return

#region Update Cache
$ScriptBlock = {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["database"][$FullSmoName] = $server.Databases.Name
}
Register-DbaTeppInstanceCacheBuilder -ScriptBlock $ScriptBlock
#endregion Update Cache
tools\dbatools\internal\dynamicparams\endpoint.ps1
#region Initialize Cache
if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["endpoint"]) {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["endpoint"] = @{ }
}
#endregion Initialize Cache

#region Tepp Data return
$ScriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )


    $server = $fakeBoundParameter['SqlInstance']

    if (-not $server) {
        $server = $fakeBoundParameter['Source']
    }

    if (-not $server) {
        $server = $fakeBoundParameter['ComputerName']
    }

    if (-not $server) { return }

    try {
        [DbaInstanceParameter]$parServer = $server | Select-Object -First 1
    }
    catch {
        return
    }

    if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["endpoint"][$parServer.FullSmoName.ToLower()]) {
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["endpoint"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }

    try {
        $serverObject = Connect-SqlInstance -SqlInstance $parServer -SqlCredential $fakeBoundParameter['SqlCredential'] -ErrorAction Stop
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["endpoint"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }
    catch {
        return
    }
}

Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name Endpoint
#endregion Tepp Data return

#region Update Cache
$ScriptBlock = {

    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["endpoint"][$FullSmoName] = ($server.Endpoints | Where-Object IsSystemObject -eq $false).Name
}
Register-DbaTeppInstanceCacheBuilder -ScriptBlock $ScriptBlock
#endregion Update Cache
tools\dbatools\internal\dynamicparams\group.ps1
#region Initialize Cache
if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["group"]) {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["group"] = @{ }
}
#endregion Initialize Cache

#region Tepp Data return
$ScriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )


    $server = $fakeBoundParameter['SqlInstance']

    if (-not $server) {
        $server = $fakeBoundParameter['Source']
    }

    if (-not $server) {
        $server = $fakeBoundParameter['ComputerName']
    }

    if (-not $server) { return }

    try {
        [DbaInstanceParameter]$parServer = $server | Select-Object -First 1
    }
    catch {
        return
    }

    if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["group"][$parServer.FullSmoName.ToLower()]) {
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["group"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }

    try {
        $serverObject = Connect-SqlInstance -SqlInstance $parServer -SqlCredential $fakeBoundParameter['SqlCredential'] -ErrorAction Stop
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["group"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }
    catch {
        return
    }
}

Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name Group
#endregion Tepp Data return

#region Update Cache
$ScriptBlock = {

    $cms = New-Object Microsoft.SqlServer.Management.RegisteredServers.RegisteredServersStore($server.ConnectionContext.SqlConnectionObject)
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["group"][$FullSmoName] = $cms.DatabaseEngineServerGroup.ServerGroups.Name
}
Register-DbaTeppInstanceCacheBuilder -ScriptBlock $ScriptBlock
#endregion Update Cache
tools\dbatools\internal\dynamicparams\instanceproperty.ps1
#region Initialize Cache
if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["instanceproperty"]) {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["instanceproperty"] = @{ }
}
#endregion Initialize Cache

#region Tepp Data return
$ScriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )


    $server = $fakeBoundParameter['SqlInstance']

    if (-not $server) {
        $server = $fakeBoundParameter['Source']
    }

    if (-not $server) {
        $server = $fakeBoundParameter['ComputerName']
    }

    if (-not $server) { return }

    try {
        [DbaInstanceParameter]$parServer = $server | Select-Object -First 1
    }
    catch {
        return
    }

    if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["instanceproperty"][$parServer.FullSmoName.ToLower()]) {
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["instanceproperty"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }

    try {
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["instanceproperty"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }
    catch {
        return
    }
}

Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name InstanceProperty
#endregion Tepp Data return

#region Update Cache
$ScriptBlock = {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["instanceproperty"][$FullSmoName] = $server.Information.Properties.Name + $server.UserOptions.Properties.Name + $server.Settings.Properties.Name
}
Register-DbaTeppInstanceCacheBuilder -ScriptBlock $ScriptBlock
#endregion Update Cache
tools\dbatools\internal\dynamicparams\job.ps1
#region Initialize Cache
if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["job"]) {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["job"] = @{ }
}
#endregion Initialize Cache

#region Tepp Data return
$ScriptBlock = {
    param (
        $commandName,
        $parameterName,
        $wordToComplete,
        $commandAst,
        $fakeBoundParameter
    )


    $server = $fakeBoundParameter['SqlInstance']

    if (-not $server) {
        $server = $fakeBoundParameter['Source']
    }

    if (-not $server) {
        $server = $fakeBoundParameter['ComputerName']
    }

    if (-not $server) { return }

    try {
        [DbaInstanceParameter]$parServer = $server | Select-Object -First 1
    }
    catch {
        return
    }

    if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["job"][$parServer.FullSmoName.ToLower()]) {
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["job"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }

    try {
        $serverObject = Connect-SqlInstance -SqlInstance $parServer -SqlCredential $fakeBoundParameter['SqlCredential'] -ErrorAction Stop
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["job"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }
    catch {
        return
    }
}

Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name Job
#endregion Tepp Data return

#region Update Cache
$ScriptBlock = {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["job"][$FullSmoName] = $server.JobServer.Jobs.Name
}
Register-DbaTeppInstanceCacheBuilder -ScriptBlock $ScriptBlock
#endregion Update Cache
tools\dbatools\internal\dynamicparams\jobcategory.ps1
#region Initialize Cache
if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["jobcategory"]) {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["jobcategory"] = @{ }
}
#endregion Initialize Cache

#region Tepp Data return
$ScriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )


    $server = $fakeBoundParameter['SqlInstance']

    if (-not $server) {
        $server = $fakeBoundParameter['Source']
    }

    if (-not $server) {
        $server = $fakeBoundParameter['ComputerName']
    }

    if (-not $server) { return }

    try {
        [DbaInstanceParameter]$parServer = $server | Select-Object -First 1
    }
    catch {
        return
    }

    if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["jobcategory"][$parServer.FullSmoName.ToLower()]) {
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["jobcategory"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }

    try {
        $serverObject = Connect-SqlInstance -SqlInstance $parServer -SqlCredential $fakeBoundParameter['SqlCredential'] -ErrorAction Stop
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["jobcategory"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }
    catch {
        return
    }
}

Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name JobCategory
#endregion Tepp Data return

#region Update Cache
$ScriptBlock = {

    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["jobcategory"][$FullSmoName] = $server.JobServer.JobCategories.Name
}
Register-DbaTeppInstanceCacheBuilder -ScriptBlock $ScriptBlock
#endregion Update Cache
tools\dbatools\internal\dynamicparams\linkedserver.ps1
#region Initialize Cache
if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["linkedserver"]) {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["linkedserver"] = @{ }
}
#endregion Initialize Cache

#region Tepp Data return
$ScriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )


    $server = $fakeBoundParameter['SqlInstance']

    if (-not $server) {
        $server = $fakeBoundParameter['Source']
    }

    if (-not $server) {
        $server = $fakeBoundParameter['ComputerName']
    }

    if (-not $server) { return }

    try {
        [DbaInstanceParameter]$parServer = $server | Select-Object -First 1
    }
    catch {
        return
    }

    if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["linkedserver"][$parServer.FullSmoName.ToLower()]) {
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["linkedserver"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }

    try {
        $serverObject = Connect-SqlInstance -SqlInstance $parServer -SqlCredential $fakeBoundParameter['SqlCredential'] -ErrorAction Stop
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["linkedserver"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }
    catch {
        return
    }
}

Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name LinkedServer
#endregion Tepp Data return

#region Update Cache
$ScriptBlock = {

    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["linkedserver"][$FullSmoName] = $server.LinkedServers.Name
}
Register-DbaTeppInstanceCacheBuilder -ScriptBlock $ScriptBlock
#endregion Update Cache
tools\dbatools\internal\dynamicparams\login.ps1
tools\dbatools\internal\dynamicparams\mailaccount.ps1
#region Initialize Cache
if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["mailaccount"]) {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["mailaccount"] = @{ }
}
#endregion Initialize Cache

#region Tepp Data return
$ScriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )


    $server = $fakeBoundParameter['SqlInstance']

    if (-not $server) {
        $server = $fakeBoundParameter['Source']
    }

    if (-not $server) {
        $server = $fakeBoundParameter['ComputerName']
    }

    if (-not $server) { return }

    try {
        [DbaInstanceParameter]$parServer = $server | Select-Object -First 1
    }
    catch {
        return
    }

    if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["mailaccount"][$parServer.FullSmoName.ToLower()]) {
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["mailaccount"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }

    try {
        $serverObject = Connect-SqlInstance -SqlInstance $parServer -SqlCredential $fakeBoundParameter['SqlCredential'] -ErrorAction Stop
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["mailaccount"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }
    catch {
        return
    }
}

Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name MailAccount
#endregion Tepp Data return

#region Update Cache
$ScriptBlock = {

    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["mailaccount"][$FullSmoName] = $server.Mail.Accounts.Name
}
Register-DbaTeppInstanceCacheBuilder -ScriptBlock $ScriptBlock
#endregion Update Cache
tools\dbatools\internal\dynamicparams\mailprofile.ps1
#region Initialize Cache
if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["mailprofile"]) {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["mailprofile"] = @{ }
}
#endregion Initialize Cache

#region Tepp Data return
$ScriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )


    $server = $fakeBoundParameter['SqlInstance']

    if (-not $server) {
        $server = $fakeBoundParameter['Source']
    }

    if (-not $server) {
        $server = $fakeBoundParameter['ComputerName']
    }

    if (-not $server) { return }

    try {
        [DbaInstanceParameter]$parServer = $server | Select-Object -First 1
    }
    catch {
        return
    }

    if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["mailprofile"][$parServer.FullSmoName.ToLower()]) {
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["mailprofile"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }

    try {
        $serverObject = Connect-SqlInstance -SqlInstance $parServer -SqlCredential $fakeBoundParameter['SqlCredential'] -ErrorAction Stop
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["mailprofile"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }
    catch {
        return
    }
}

Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name MailProfile
#endregion Tepp Data return

#region Update Cache
$ScriptBlock = {

    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["mailprofile"][$FullSmoName] = $server.Mail.Profiles.Name
}
Register-DbaTeppInstanceCacheBuilder -ScriptBlock $ScriptBlock
#endregion Update Cache
tools\dbatools\internal\dynamicparams\mailserver.ps1
#region Initialize Cache
if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["mailserver"]) {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["mailserver"] = @{ }
}
#endregion Initialize Cache

#region Tepp Data return
$ScriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )


    $server = $fakeBoundParameter['SqlInstance']

    if (-not $server) {
        $server = $fakeBoundParameter['Source']
    }

    if (-not $server) {
        $server = $fakeBoundParameter['ComputerName']
    }

    if (-not $server) { return }

    try {
        [DbaInstanceParameter]$parServer = $server | Select-Object -First 1
    }
    catch {
        return
    }

    if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["mailserver"][$parServer.FullSmoName.ToLower()]) {
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["mailserver"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }

    try {
        $serverObject = Connect-SqlInstance -SqlInstance $parServer -SqlCredential $fakeBoundParameter['SqlCredential'] -ErrorAction Stop
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["mailserver"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }
    catch {
        return
    }
}

Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name MailServer
#endregion Tepp Data return

#region Update Cache
$ScriptBlock = {

    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["mailserver"][$FullSmoName] = $server.Mail.Accounts.MailServers.Name
}
Register-DbaTeppInstanceCacheBuilder -ScriptBlock $ScriptBlock
#endregion Update Cache
tools\dbatools\internal\dynamicparams\operator.ps1
#region Initialize Cache
if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["operator"]) {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["operator"] = @{ }
}
#endregion Initialize Cache

#region Tepp Data return
$ScriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )


    $server = $fakeBoundParameter['SqlInstance']

    if (-not $server) {
        $server = $fakeBoundParameter['Source']
    }

    if (-not $server) {
        $server = $fakeBoundParameter['ComputerName']
    }

    if (-not $server) { return }

    try {
        [DbaInstanceParameter]$parServer = $server | Select-Object -First 1
    }
    catch {
        return
    }

    if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["operator"][$parServer.FullSmoName.ToLower()]) {
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["operator"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }

    try {
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["operator"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }
    catch {
        return
    }
}

Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name Operator
#endregion Tepp Data return

#region Update Cache
$ScriptBlock = {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["operator"][$FullSmoName] = $server.JobServer.Operators.Name
}
Register-DbaTeppInstanceCacheBuilder -ScriptBlock $ScriptBlock
#endregion Update Cache
tools\dbatools\internal\dynamicparams\operatorcategory.ps1
#region Initialize Cache
if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["operatorcategory"]) {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["operatorcategory"] = @{ }
}
#endregion Initialize Cache

#region Tepp Data return
$ScriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )


    $server = $fakeBoundParameter['SqlInstance']

    if (-not $server) {
        $server = $fakeBoundParameter['Source']
    }

    if (-not $server) {
        $server = $fakeBoundParameter['ComputerName']
    }

    if (-not $server) { return }

    try {
        [DbaInstanceParameter]$parServer = $server | Select-Object -First 1
    }
    catch {
        return
    }

    if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["operatorcategory"][$parServer.FullSmoName.ToLower()]) {
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["operatorcategory"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }

    try {
        $serverObject = Connect-SqlInstance -SqlInstance $parServer -SqlCredential $fakeBoundParameter['SqlCredential'] -ErrorAction Stop
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["operatorcategory"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }
    catch {
        return
    }
}

Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name OperatorCategory
#endregion Tepp Data return

#region Update Cache
$ScriptBlock = {

    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["operatorcategory"][$FullSmoName] = $server.JobServer.OperatorCategories.Name
}
Register-DbaTeppInstanceCacheBuilder -ScriptBlock $ScriptBlock
#endregion Update Cache
tools\dbatools\internal\dynamicparams\perfmontemplate.ps1
#region Initialize Cache
if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["perfmontemplate"]) {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["perfmontemplate"] = @{ }
}
#endregion Initialize Cache

#region Tepp Data return
$ScriptBlock = {
    param (
        $commandName,
        $parameterName,
        $wordToComplete,
        $commandAst,
        $fakeBoundParameter
    )

    $files = (Get-ChildItem "$script:PSModuleRoot\bin\perfmontemplates\collectorsets\*.xml").BaseName
    foreach ($file in $files) {
        "'$file'"
    }
}

Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name perfmontemplate
#endregion Tepp Data return
tools\dbatools\internal\dynamicparams\processHostname.ps1
$scriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )


    $server = $fakeBoundParameter['SqlInstance']
    if (-not $server) {
        return
    }
    $sqlCredential = $fakeBoundParameter['SqlCredential']

    try {
        if ($sqlCredential) { $instance = Connect-SqlInstance -SqlInstance $server -ErrorAction Stop }
        else { $instance = Connect-SqlInstance -SqlInstance $server -ErrorAction Stop }

        $instance.EnumProcesses().Host | Select-Object -Unique | Where-DbaObject -Like "$wordToComplete*" | ForEach-Object {
            if (-not ([string]::IsNullOrWhiteSpace($_))) { New-DbaTeppCompletionResult -CompletionText $_ -ToolTip $_ }
        }
    }
    catch {
        return
    }
    finally {
    }
}

Register-DbaTeppScriptblock -ScriptBlock $scriptBlock -Name processhostname
tools\dbatools\internal\dynamicparams\processProgram.ps1
$scriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )


    $server = $fakeBoundParameter['SqlInstance']
    if (-not $server) {
        return
    }
    $sqlCredential = $fakeBoundParameter['SqlCredential']

    try {
        if ($sqlCredential) { $instance = Connect-SqlInstance -SqlInstance $server -ErrorAction Stop  }
        else { $instance = Connect-SqlInstance -SqlInstance $server -ErrorAction Stop }

        $instance.EnumProcesses().Program | Select-Object -Unique | Where-DbaObject -Like "$wordToComplete*" | ForEach-Object {
            if (-not ([string]::IsNullOrWhiteSpace($_))) { New-DbaTeppCompletionResult -CompletionText $_ -ToolTip $_ }
        }
    }
    catch {
        return
    }
    finally {
    }
}

Register-DbaTeppScriptblock -ScriptBlock $scriptBlock -Name processprogram
tools\dbatools\internal\dynamicparams\processSpid.ps1
$scriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )


    $server = $fakeBoundParameter['SqlInstance']
    if (-not $server) {
        return
    }
    $sqlCredential = $fakeBoundParameter['SqlCredential']

    try {
        if ($sqlCredential) { $instance = Connect-SqlInstance -SqlInstance $server -ErrorAction Stop }
        else { $instance = Connect-SqlInstance -SqlInstance $server -ErrorAction Stop }

        $instance.EnumProcesses().Spid | Select-Object -Unique | Where-DbaObject -Like "$wordToComplete*" | ForEach-Object {
            if (-not ([string]::IsNullOrWhiteSpace($_))) { New-DbaTeppCompletionResult -CompletionText $_ -ToolTip $_ }
        }
    }
    catch {
        return
    }
    finally {
    }
}

Register-DbaTeppScriptblock -ScriptBlock $scriptBlock -Name processspid
tools\dbatools\internal\dynamicparams\proxyaccount.ps1
#region Initialize Cache
if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["proxyaccount"]) {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["proxyaccount"] = @{ }
}
#endregion Initialize Cache

#region Tepp Data return
$ScriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )


    $server = $fakeBoundParameter['SqlInstance']

    if (-not $server) {
        $server = $fakeBoundParameter['Source']
    }

    if (-not $server) {
        $server = $fakeBoundParameter['ComputerName']
    }

    if (-not $server) { return }

    try {
        [DbaInstanceParameter]$parServer = $server | Select-Object -First 1
    }
    catch {
        return
    }

    if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["proxyaccount"][$parServer.FullSmoName.ToLower()]) {
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["proxyaccount"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }

    try {
        $serverObject = Connect-SqlInstance -SqlInstance $parServer -SqlCredential $fakeBoundParameter['SqlCredential'] -ErrorAction Stop
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["proxyaccount"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }
    catch {
        return
    }
}

Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name ProxyAccount
#endregion Tepp Data return

#region Update Cache
$ScriptBlock = {

    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["proxyaccount"][$FullSmoName] = $server.JobServer.ProxyAccounts.Name
}
Register-DbaTeppInstanceCacheBuilder -ScriptBlock $ScriptBlock
#endregion Update Cache
tools\dbatools\internal\dynamicparams\resourcepool.ps1
#region Initialize Cache
if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["resourcepool"]) {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["resourcepool"] = @{ }
}
#endregion Initialize Cache

#region Tepp Data return
$ScriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )


    $server = $fakeBoundParameter['SqlInstance']

    if (-not $server) {
        $server = $fakeBoundParameter['Source']
    }

    if (-not $server) {
        $server = $fakeBoundParameter['ComputerName']
    }

    if (-not $server) { return }

    try {
        [DbaInstanceParameter]$parServer = $server | Select-Object -First 1
    }
    catch {
        return
    }

    if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["resourcepool"][$parServer.FullSmoName.ToLower()]) {
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["resourcepool"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }

    try {
        $serverObject = Connect-SqlInstance -SqlInstance $parServer -SqlCredential $fakeBoundParameter['SqlCredential'] -ErrorAction Stop
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["resourcepool"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }
    catch {
        return
    }
}

Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name ResourcePool
#endregion Tepp Data return

#region Update Cache
$ScriptBlock = {

    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["resourcepool"][$FullSmoName] = ($server.ResourceGovernor.ResourcePools | Where-Object Name -NotIn 'internal', 'default').Name
}
Register-DbaTeppInstanceCacheBuilder -ScriptBlock $ScriptBlock
#endregion Update Cache
tools\dbatools\internal\dynamicparams\schedule.ps1
#region Initialize Cache
if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["schedule"]) {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["schedule"] = @{ }
}
#endregion Initialize Cache

#region Tepp Data return
$ScriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )


    $server = $fakeBoundParameter['SqlInstance']

    if (-not $server) {
        $server = $fakeBoundParameter['Source']
    }

    if (-not $server) {
        $server = $fakeBoundParameter['ComputerName']
    }

    if (-not $server) { return }

    try {
        [DbaInstanceParameter]$parServer = $server | Select-Object -First 1
    }
    catch {
        return
    }

    if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["schedule"][$parServer.FullSmoName.ToLower()]) {
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["schedule"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }

    try {
        $serverObject = Connect-SqlInstance -SqlInstance $parServer -SqlCredential $fakeBoundParameter['SqlCredential'] -ErrorAction Stop
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["schedule"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }
    catch {
        return
    }
}

Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name Schedule
#endregion Tepp Data return

#region Update Cache
$ScriptBlock = {

    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["schedule"][$FullSmoName] = $server.JobServer.SharedSchedules.Name
}
Register-DbaTeppInstanceCacheBuilder -ScriptBlock $ScriptBlock
#endregion Update Cache
tools\dbatools\internal\dynamicparams\servertrigger.ps1
#region Initialize Cache
if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["servertrigger"]) {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["servertrigger"] = @{ }
}
#endregion Initialize Cache

#region Tepp Data return
$ScriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )


    $server = $fakeBoundParameter['SqlInstance']

    if (-not $server) {
        $server = $fakeBoundParameter['Source']
    }

    if (-not $server) {
        $server = $fakeBoundParameter['ComputerName']
    }

    if (-not $server) { return }

    try {
        [DbaInstanceParameter]$parServer = $server | Select-Object -First 1
    }
    catch {
        return
    }

    if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["servertrigger"][$parServer.FullSmoName.ToLower()]) {
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["servertrigger"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }

    try {
        $serverObject = Connect-SqlInstance -SqlInstance $parServer -SqlCredential $fakeBoundParameter['SqlCredential'] -ErrorAction Stop
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["servertrigger"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }
    catch {
        return
    }
}

Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name ServerTrigger
#endregion Tepp Data return

#region Update Cache
$ScriptBlock = {

    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["servertrigger"][$FullSmoName] = $server.Triggers.Name
}
Register-DbaTeppInstanceCacheBuilder -ScriptBlock $ScriptBlock
#endregion Update Cache
tools\dbatools\internal\dynamicparams\session.ps1
#region Initialize Cache
if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["session"]) {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["session"] = @{ }
}
#endregion Initialize Cache

#region Tepp Data return
$ScriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )


    $server = $fakeBoundParameter['SqlInstance']

    if (-not $server) {
        $server = $fakeBoundParameter['Source']
    }

    if (-not $server) {
        $server = $fakeBoundParameter['ComputerName']
    }

    if (-not $server) { return }

    try {
        [DbaInstanceParameter]$parServer = $server | Select-Object -First 1
    }
    catch {
        return
    }

    if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["session"][$parServer.FullSmoName.ToLower()]) {
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["session"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }

    try {
        $serverObject = Connect-SqlInstance -SqlInstance $parServer -SqlCredential $fakeBoundParameter['SqlCredential'] -ErrorAction Stop
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["session"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }
    catch {
        return
    }
}

Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name session
#endregion Tepp Data return

#region Update Cache
$ScriptBlock = {

    $SqlConn = $server.ConnectionContext.SqlConnectionObject
    $SqlStoreConnection = New-Object Microsoft.SqlServer.Management.Sdk.Sfc.SqlStoreConnection $SqlConn
    $XEStore = New-Object  Microsoft.SqlServer.Management.XEvent.XEStore $SqlStoreConnection
    $xesessions = $XEStore.sessions
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["session"][$FullSmoName] = $xesessions.Name
}
Register-DbaTeppInstanceCacheBuilder -ScriptBlock $ScriptBlock
#endregion Update Cache
tools\dbatools\internal\dynamicparams\snapshot.ps1
#region Initialize Cache
if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["snapshot"]) {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["snapshot"] = @{ }
}
#endregion Initialize Cache

#region Tepp Data return
$ScriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )


    $server = $fakeBoundParameter['SqlInstance']

    if (-not $server) {
        $server = $fakeBoundParameter['Source']
    }

    if (-not $server) {
        $server = $fakeBoundParameter['ComputerName']
    }

    if (-not $server) { return }

    try {
        [DbaInstanceParameter]$parServer = $server | Select-Object -First 1
    }
    catch {
        return
    }

    if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["snapshot"][$parServer.FullSmoName.ToLower()]) {
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["snapshot"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }

    try {
        foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["snapshot"][$parServer.FullSmoName.ToLower()] | Where-DbaObject -Like "$wordToComplete*")) {
            New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
        }
        return
    }
    catch {
        return
    }
}

Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name snapshot
#endregion Tepp Data return

#region Update Cache
$ScriptBlock = {
    if ($PSVersionTable.PSVersion.Major -ge 4) { [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["snapshot"][$FullSmoName] = $server.Databases.Where( { $_.IsDatabaseSnapShot }).Name }
    else { [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["snapshot"][$FullSmoName] = ($server.Databases | Where-Object IsDatabaseSnapShot).Name }
}
Register-DbaTeppInstanceCacheBuilder -ScriptBlock $ScriptBlock
#endregion Update Cache
tools\dbatools\internal\dynamicparams\sqlinstance.ps1
#region Initialize Cache
if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["sqlinstance"]) {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["sqlinstance"] = @()
}
#endregion Initialize Cache

#region Tepp Data return
$ScriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )


    foreach ($name in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["sqlinstance"] | Where-DbaObject -Like "$wordToComplete*")) {
        New-DbaTeppCompletionResult -CompletionText $name -ToolTip $name
    }
}
Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name "sqlinstance"
#endregion Tepp Data return
tools\dbatools\internal\dynamicparams\tag.ps1
$ScriptBlock = {
    param (
        $commandName,

        $parameterName,

        $wordToComplete,

        $commandAst,

        $fakeBoundParameter
    )

    # Hack till we get this working
    function New-CompletionResult {
        param ([Parameter(Position = 0, ValueFromPipelineByPropertyName, Mandatory, ValueFromPipeline)]
            [ValidateNotNullOrEmpty()]
            [string]
            $CompletionText,

            [Parameter(Position = 1, ValueFromPipelineByPropertyName)]
            [string]
            $ToolTip,

            [Parameter(Position = 2, ValueFromPipelineByPropertyName)]
            [string]
            $ListItemText,

            [System.Management.Automation.CompletionResultType]
            $CompletionResultType = [System.Management.Automation.CompletionResultType]::ParameterValue,

            [Parameter(Mandatory = $false)]
            [switch]
            $NoQuotes = $false
        )

        process {
            $toolTipToUse = if ($ToolTip -eq '') { $CompletionText }
            else { $ToolTip }
            $listItemToUse = if ($ListItemText -eq '') { $CompletionText }
            else { $ListItemText }

            # If the caller explicitly requests that quotes
            # not be included, via the -NoQuotes parameter,
            # then skip adding quotes.

            if ($CompletionResultType -eq [System.Management.Automation.CompletionResultType]::ParameterValue -and -not $NoQuotes) {
                # Add single quotes for the caller in case they are needed.
                # We use the parser to robustly determine how it will treat
                # the argument.  If we end up with too many tokens, or if
                # the parser found something expandable in the results, we
                # know quotes are needed.

                $tokens = $null
                $null = [System.Management.Automation.Language.Parser]::ParseInput("echo $CompletionText", [ref]$tokens, [ref]$null)
                if ($tokens.Length -ne 3 -or
                    ($tokens[1] -is [System.Management.Automation.Language.StringExpandableToken] -and
                        $tokens[1].Kind -eq [System.Management.Automation.Language.TokenKind]::Generic)) {
                    $CompletionText = "'$CompletionText'"
                }
            }
            return New-Object System.Management.Automation.CompletionResult `
            ($CompletionText, $listItemToUse, $CompletionResultType, $toolTipToUse.Trim())
        }

    }

    $moduledirectory = (Get-Module -Name dbatools).ModuleBase
    $idxfile = "$moduledirectory\bin\dbatools-index.json"
    $json = Get-Content $idxfile | ConvertFrom-Json
    $cleantags = @()
    $tags = $json.Tags

    foreach ($tag in $tags) {
        if ($null -ne $tag) {
            $cleantags += $tag.Trim()
        }
    }

    $collection = $cleantags | Select-Object -Unique

    if ($collection) {
        foreach ($item in $collection) {
            New-CompletionResult -CompletionText $item -ToolTip $item
        }
    }
}

Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name "tag"
tools\dbatools\internal\dynamicparams\xesessiontemplate.ps1
#region Initialize Cache
if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["xesessiontemplate"]) {
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["xesessiontemplate"] = @{ }
}
#endregion Initialize Cache

#region Tepp Data return
$ScriptBlock = {
    param (
        $commandName,
        $parameterName,
        $wordToComplete,
        $commandAst,
        $fakeBoundParameter
    )

    $files = (Get-ChildItem "$script:PSModuleRoot\bin\xetemplates\*.xml").BaseName
    foreach ($file in $files) {
        "'$file'"
    }
}

Register-DbaTeppScriptblock -ScriptBlock $ScriptBlock -Name xesessiontemplate
#endregion Tepp Data return
tools\dbatools\internal\functions\Connect-AsServer.ps1
function Connect-AsServer {
    <#
.SYNOPSIS
Internal function that creates SMO server object.

.DESCRIPTION
Internal function that creates SMO server object.

.PARAMETER AsServer
Analysis Server

.PARAMETER ParameterConnection
Shorten the timeout

.NOTES
Website: https://dbatools.io
Copyright: (C) Chrissy LeMaire, [email protected]
License: MIT https://opensource.org/licenses/MIT

.EXAMPLE
Connect-AsServer -AsServer localhost
Connects to SSAS on the local server

#>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [object]$AsServer,
        [switch]$ParameterConnection
    )

    if ($AsServer.GetType() -eq [Microsoft.AnalysisServices.Server]) {

        if ($ParameterConnection) {
            $paramserver = New-Object Microsoft.AnalysisServices.Server
            $paramserver.Connect("Data Source=$($AsServer.Name);Connect Timeout=2")
            return $paramserver
        }

        if ($AsServer.Connected -eq $false) { $AsServer.Connect("Data Source=$($AsServer.Name);Connect Timeout=3") }
        return $AsServer
    }

    $server = New-Object Microsoft.AnalysisServices.Server

    try {
        if ($ParameterConnection) {
            $server.Connect("Data Source=$AsServer;Connect Timeout=2")
        }
        else { $server.Connect("Data Source=$AsServer;Connect Timeout=3") }
    }
    catch {
        $message = $_.Exception.InnerException
        $message = $message.ToString()
        $message = ($message -Split '-->')[0]
        $message = ($message -Split 'at System.Data.SqlClient')[0]
        $message = ($message -Split 'at System.Data.ProviderBase')[0]
        throw "Can't connect to $asserver`: $message "
    }

    return $server
}
tools\dbatools\internal\functions\Connect-SqlInstance.ps1
function Connect-SqlInstance {
    <#
        .SYNOPSIS
            Internal function to establish smo connections.

        .DESCRIPTION
            Internal function to establish smo connections.

            Can interpret any of the following types of information:
            - String
            - Smo Server objects
            - Smo Linked Server objects

        .PARAMETER SqlInstance
            The SQL Server instance to restore to.

        .PARAMETER SqlCredential
            Allows you to login to servers using SQL Logins as opposed to Windows Auth/Integrated/Trusted.

        .PARAMETER ParameterConnection
            This call is for dynamic parameters only and is no longer used, actually.

        .PARAMETER AzureUnsupported
            Throw if Azure is detected but not supported

        .PARAMETER RegularUser
            The connection doesn't require SA privileges.
            By default, the assumption is that SA is no longer required.

        .PARAMETER MinimumVersion
           The minimum version that the calling command will support

        .EXAMPLE
            Connect-SqlInstance -SqlInstance sql2014

            Connect to the Server sql2014 with native credentials.
    #>
    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingEmptyCatchBlock", "")]
    param (
        [Parameter(Mandatory = $true)]
        [object]$SqlInstance,
        [object]$SqlCredential,
        [switch]$ParameterConnection,
        [switch]$RegularUser = $true,
        [int]$MinimumVersion,
        [switch]$AzureUnsupported,
        [switch]$NonPooled
    )

    #region Utility functions
    function Invoke-TEPPCacheUpdate {
        [CmdletBinding()]
        param (
            [System.Management.Automation.ScriptBlock]$ScriptBlock
        )

        try {
            [ScriptBlock]::Create($scriptBlock).Invoke()
        }
        catch {
            # If the SQL Server version doesn't support the feature, we ignore it and silently continue
            if ($_.Exception.InnerException.InnerException.GetType().FullName -eq "Microsoft.SqlServer.Management.Sdk.Sfc.InvalidVersionEnumeratorException") {
                return
            }

            if ($ENV:APPVEYOR_BUILD_FOLDER -or ([Sqlcollaborative.Dbatools.Message.MEssageHost]::DeveloperMode)) { throw }
            else {
                Write-Message -Level Warning -Message "Failed TEPP Caching: $($scriptBlock.ToString() | Select-String '"(.*?)"' | ForEach-Object { $_.Matches[0].Groups[1].Value })" -ErrorRecord $_ 3>$null
            }
        }
    }
    #endregion Utility functions

    #region Ensure Credential integrity
    <#
    Usually, the parameter type should have been not object but off the PSCredential type.
    When binding null to a PSCredential type parameter on PS3-4, it'd then show a prompt, asking for username and password.

    In order to avoid that and having to refactor lots of functions (and to avoid making regular scripts harder to read), we created this workaround.
    #>
    if ($SqlCredential) {
        if ($SqlCredential.GetType() -ne [System.Management.Automation.PSCredential]) {
            throw "The credential parameter was of a non-supported type! Only specify PSCredentials such as generated from Get-Credential. Input was of type $($SqlCredential.GetType().FullName)"
        }
    }
    #endregion Ensure Credential integrity

    #region Safely convert input into instance parameters
    <#
    This is a bit ugly, but:
    In some cases functions would directly pass their own input through when the parameter on the calling function was typed as [object[]].
    This would break the base parameter class, as it'd automatically be an array and the parameterclass is not designed to handle arrays (Shouldn't have to).

    Note: Multiple servers in one call were never supported, those old functions were liable to break anyway and should be fixed soonest.
    #>
    if ($SqlInstance.GetType() -eq [Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter]) {
        [DbaInstanceParameter]$ConvertedSqlInstance = $SqlInstance
        if ($ConvertedSqlInstance.Type -like "SqlConnection") {
            [DbaInstanceParameter]$ConvertedSqlInstance = New-Object Microsoft.SqlServer.Management.Smo.Server($ConvertedSqlInstance.InputObject)
        }
    }
    else {
        [DbaInstanceParameter]$ConvertedSqlInstance = [DbaInstanceParameter]($SqlInstance | Select-Object -First 1)

        if ($SqlInstance.Count -gt 1) {
            Write-Message -Level Warning -EnableException $true -Message "More than on server was specified when calling Connect-SqlInstance from $((Get-PSCallStack)[1].Command)"
        }
    }
    #endregion Safely convert input into instance parameters

    #region Input Object was a server object
    if ($ConvertedSqlInstance.InputObject.GetType() -eq [Microsoft.SqlServer.Management.Smo.Server]) {
        $server = $ConvertedSqlInstance.InputObject
        if ($server.ConnectionContext.IsOpen -eq $false) {
            if ($NonPooled) {
                $server.ConnectionContext.Connect()
            }
            elseif ($authtype -eq "Windows Authentication with Credential") {
                # Make it connect in a natural way, hard to explain.
                $null = $server.IsMemberOfWsfcCluster
            }
            else {
                $server.ConnectionContext.SqlConnectionObject.Open()
            }
        }

        # Register the connected instance, so that the TEPP updater knows it's been connected to and starts building the cache
        [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::SetInstance($ConvertedSqlInstance.FullSmoName.ToLower(), $server.ConnectionContext.Copy(), ($server.ConnectionContext.FixedServerRoles -match "SysAdmin"))

        # Update cache for instance names
        if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["sqlinstance"] -notcontains $ConvertedSqlInstance.FullSmoName.ToLower()) {
            [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["sqlinstance"] += $ConvertedSqlInstance.FullSmoName.ToLower()
        }

        # Update lots of registered stuff
        if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::TeppSyncDisabled) {
            $FullSmoName = $ConvertedSqlInstance.FullSmoName.ToLower()
            foreach ($scriptBlock in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::TeppGatherScriptsFast)) {
                Invoke-TEPPCacheUpdate -ScriptBlock $scriptBlock
            }
        }

        if (-not $server.ComputerName) {
            $parsedcomputername = $server.NetName
            if (-not $parsedcomputername) {
                $parsedcomputername = ([dbainstance]$SqlInstance).ComputerName
            }
            Add-Member -InputObject $server -NotePropertyName ComputerName -NotePropertyValue $parsedcomputername -Force
        }
        return $server
    }
    #endregion Input Object was a server object

    #region Input Object was anything else

    $server = New-Object Microsoft.SqlServer.Management.Smo.Server $ConvertedSqlInstance.FullSmoName
    $server.ConnectionContext.ApplicationName = "dbatools PowerShell module - dbatools.io"
    if ($ConvertedSqlInstance.IsConnectionString) { $server.ConnectionContext.ConnectionString = $ConvertedSqlInstance.InputObject }

    try {
        $server.ConnectionContext.ConnectTimeout = [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::SqlConnectionTimeout

        if ($null -ne $SqlCredential.Username) {
            $username = ($SqlCredential.Username).TrimStart("\")

            if ($username -like "*\*") {
                $username = $username.Split("\")[1]
                $authtype = "Windows Authentication with Credential"
                $server.ConnectionContext.LoginSecure = $true
                $server.ConnectionContext.ConnectAsUser = $true
                $server.ConnectionContext.ConnectAsUserName = $username
                $server.ConnectionContext.ConnectAsUserPassword = ($SqlCredential).GetNetworkCredential().Password
            }
            else {
                $authtype = "SQL Authentication"
                $server.ConnectionContext.LoginSecure = $false
                $server.ConnectionContext.set_Login($username)
                $server.ConnectionContext.set_SecurePassword($SqlCredential.Password)
            }
        }
    }
    catch { }

    try {
        if ($NonPooled) {
            $server.ConnectionContext.Connect()
        }
        elseif ($authtype -eq "Windows Authentication with Credential") {
            # Make it connect in a natural way, hard to explain.
            $null = $server.IsMemberOfWsfcCluster
        }
        else {
            $server.ConnectionContext.SqlConnectionObject.Open()
        }
    }
    catch {
        $message = $_.Exception.InnerException.InnerException
        if ($message) {
            $message = $message.ToString()
            $message = ($message -Split '-->')[0]
            $message = ($message -Split 'at System.Data.SqlClient')[0]
            $message = ($message -Split 'at System.Data.ProviderBase')[0]

            if ($message -match "network path was not found") {
                $message = "Can't connect to $sqlinstance`: System.Data.SqlClient.SqlException (0x80131904): A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections."
            }

            throw "Can't connect to $ConvertedSqlInstance`: $message "
        }
        else {
            throw $_
        }
    }

    if ($MinimumVersion -and $server.VersionMajor) {
        if ($server.versionMajor -lt $MinimumVersion) {
            throw "SQL Server version $MinimumVersion required - $server not supported."
        }
    }

    if ($AzureUnsupported -and $server.DatabaseEngineType -eq "SqlAzureDatabase") {
        throw "Azure SQL Database not supported"
    }

    if (-not $RegularUser) {
        if ($server.ConnectionContext.FixedServerRoles -notmatch "SysAdmin") {
            throw "Not a sysadmin on $ConvertedSqlInstance. Quitting."
        }
    }
    #'PrimaryFilePath' seems the culprit for slow SMO on databases
    $Fields2000_Db = 'Collation', 'CompatibilityLevel', 'CreateDate', 'ID', 'IsAccessible', 'IsFullTextEnabled', 'IsSystemObject', 'IsUpdateable', 'LastBackupDate', 'LastDifferentialBackupDate', 'LastLogBackupDate', 'Name', 'Owner', 'ReadOnly', 'RecoveryModel', 'ReplicationOptions', 'Status', 'Version'
    $Fields200x_Db = $Fields2000_Db + @('BrokerEnabled', 'DatabaseSnapshotBaseName', 'IsMirroringEnabled', 'Trustworthy')
    $Fields201x_Db = $Fields200x_Db + @('ActiveConnections', 'AvailabilityDatabaseSynchronizationState', 'AvailabilityGroupName', 'ContainmentType', 'EncryptionEnabled')

    $Fields2000_Login = 'CreateDate', 'DateLastModified', 'DefaultDatabase', 'DenyWindowsLogin', 'IsSystemObject', 'Language', 'LanguageAlias', 'LoginType', 'Name', 'Sid', 'WindowsLoginAccessType'
    $Fields200x_Login = $Fields2000_Login + @('AsymmetricKey', 'Certificate', 'Credential', 'ID', 'IsDisabled', 'IsLocked', 'IsPasswordExpired', 'MustChangePassword', 'PasswordExpirationEnabled', 'PasswordPolicyEnforced')
    $Fields201x_Login = $Fields200x_Login + @('PasswordHashAlgorithm')

    try {
        if ($Server.ServerType -ne 'SqlAzureDatabase') {
            $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Trigger], 'IsSystemObject')
            $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Schema], 'IsSystemObject')
            $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.SqlAssembly], 'IsSystemObject')
            $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Table], 'IsSystemObject')
            $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.View], 'IsSystemObject')
            $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.StoredProcedure], 'IsSystemObject')
            $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.UserDefinedFunction], 'IsSystemObject')

            if ($server.VersionMajor -eq 8) {
                # 2000
                $initFieldsDb = New-Object System.Collections.Specialized.StringCollection
                [void]$initFieldsDb.AddRange($Fields2000_Db)
                $initFieldsLogin = New-Object System.Collections.Specialized.StringCollection
                [void]$initFieldsLogin.AddRange($Fields2000_Login)
                $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Database], $initFieldsDb)
                $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Login], $initFieldsLogin)
            }
            elseif ($server.VersionMajor -eq 9 -or $server.VersionMajor -eq 10) {
                # 2005 and 2008
                $initFieldsDb = New-Object System.Collections.Specialized.StringCollection
                [void]$initFieldsDb.AddRange($Fields200x_Db)
                $initFieldsLogin = New-Object System.Collections.Specialized.StringCollection
                [void]$initFieldsLogin.AddRange($Fields200x_Login)
                $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Database], $initFieldsDb)
                $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Login], $initFieldsLogin)
            }
            else {
                # 2012 and above
                $initFieldsDb = New-Object System.Collections.Specialized.StringCollection
                [void]$initFieldsDb.AddRange($Fields201x_Db)
                $initFieldsLogin = New-Object System.Collections.Specialized.StringCollection
                [void]$initFieldsLogin.AddRange($Fields201x_Login)
                $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Database], $initFieldsDb)
                $server.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Login], $initFieldsLogin)
            }
        }
    }
    catch {
        # perhaps a DLL issue, continue going
    }

    # Register the connected instance, so that the TEPP updater knows it's been connected to and starts building the cache
    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::SetInstance($ConvertedSqlInstance.FullSmoName.ToLower(), $server.ConnectionContext.Copy(), ($server.ConnectionContext.FixedServerRoles -match "SysAdmin"))

    # Update cache for instance names
    if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["sqlinstance"] -notcontains $ConvertedSqlInstance.FullSmoName.ToLower()) {
        [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Cache["sqlinstance"] += $ConvertedSqlInstance.FullSmoName.ToLower()
    }

    # Update lots of registered stuff
    if (-not [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::TeppSyncDisabled) {
        $FullSmoName = $ConvertedSqlInstance.FullSmoName.ToLower()
        foreach ($scriptBlock in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::TeppGatherScriptsFast)) {
            Invoke-TEPPCacheUpdate -ScriptBlock $scriptBlock
        }
    }

    if (-not $server.ComputerName) {
        $parsedcomputername = $server.NetName
        if (-not $parsedcomputername) {
            $parsedcomputername = ([dbainstance]$SqlInstance).ComputerName
        }
        Add-Member -InputObject $server -NotePropertyName ComputerName -NotePropertyValue $parsedcomputername -Force
    }
    return $server
    #endregion Input Object was anything else
}
tools\dbatools\internal\functions\Convert-ByteToHexString.ps1
function Convert-ByteToHexString {
    <#
    .SYNOPSIS
    Converts byte object into hex string

    .DESCRIPTION
    Converts byte object ([byte[]]@(1,100,23,54)) into the hex string (e.g. '0x01641736')
    Used when working with SMO logins and their byte parameters: sids and hashed passwords

    .PARAMETER InputObject
    Input byte[] object (e.g. [byte[]]@(18,52))

    .NOTES
    Tags: Login, Internal
    Author: Kirill Kravtsov (@nvarscar)
    dbatools PowerShell module (https://dbatools.io, [email protected])
    Copyright (C) 2016 Chrissy LeMaire
    License: MIT https://opensource.org/licenses/MIT

    .EXAMPLE
    Convert-ByteToHexString ([byte[]]@(1,100,23,54))

    Returns hex string '0x01641736'

    .EXAMPLE
    Convert-ByteToHexString 18,52

    Returns hex string '0x1234'
#>
    param ([byte[]]$InputObject)
    $outString = "0x"
    $InputObject | ForEach-Object { $outString += ("{0:X}" -f $_).PadLeft(2, "0") }
    $outString
}
tools\dbatools\internal\functions\Convert-DbaMessageException.ps1
function Convert-DbaMessageException {
<#
    .SYNOPSIS
        Transforms the Exception input to the message system.
    
    .DESCRIPTION
        Transforms the Exception input to the message system.
        
        If there is an exception running a transformation scriptblock, it will log the error in the transform error queue and return the original object instead.
    
    .PARAMETER Exception
        The input Exception object, that might have to be transformed (may not either)
    
    .PARAMETER FunctionName
        The function writing the message
    
    .PARAMETER ModuleName
        The module, that the function writing the message is part of
    
    .EXAMPLE
        PS C:\> Convert-DbaMessageException -Exception $Exception -FunctionName 'Get-Test' -ModuleName 'MyModule'
        
        Checks internal storage for definitions that require a Exception transform, and either returns the original object or the transformed object.
#>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        $Exception,
        
        [Parameter(Mandatory = $true)]
        [string]
        $FunctionName,
        
        [Parameter(Mandatory = $true)]
        [string]
        $ModuleName
    )
    
    if ($null -eq $Exception) { return }
    
    $typeName = $Exception.GetType().FullName.ToLower()
    
    if ([Sqlcollaborative.Dbatools.Message.MessageHost]::ExceptionTransforms.ContainsKey($typeName)) {
        $scriptBlock = [Sqlcollaborative.Dbatools.Message.MessageHost]::ExceptionTransforms[$typeName]
        try {
            $tempException = $ExecutionContext.InvokeCommand.InvokeScript($false, ([scriptblock]::Create($scriptBlock.ToString())), $null, $Exception)
            return $tempException
        }
        catch {
            [Sqlcollaborative.Dbatools.Message.MessageHost]::WriteTransformError($_, $FunctionName, $ModuleName, $Exception, "Exception", ([System.Management.Automation.Runspaces.Runspace]::DefaultRunspace.InstanceId))
            return $Exception
        }
    }
    
    if ($transform = [Sqlcollaborative.Dbatools.Message.MessageHost]::ExceptionTransformList.Get($typeName, $ModuleName, $FunctionName)) {
        try {
            $tempException = $ExecutionContext.InvokeCommand.InvokeScript($false, ([scriptblock]::Create($transform.ScriptBlock.ToString())), $null, $Exception)
            return $tempException
        }
        catch {
            [Sqlcollaborative.Dbatools.Message.MessageHost]::WriteTransformError($_, $FunctionName, $ModuleName, $Exception, "Target", ([System.Management.Automation.Runspaces.Runspace]::DefaultRunspace.InstanceId))
            return $Exception
        }
    }
    
    return $Exception
}
tools\dbatools\internal\functions\Convert-DbaMessageLevel.ps1
function Convert-DbaMessageLevel {
<#
    .SYNOPSIS
        Processes the effective message level of a message
    
    .DESCRIPTION
        Processes the effective message level of a message
        - Applies level decrements
        - Applies message level modifiers
    
    .PARAMETER OriginalLevel
        The level the message was originally written to
    
    .PARAMETER FromStopFunction
        Whether the message was passed through Stop-PSFFunction first.
        This is used to increment the automatic message level decrement counter by 1 (so it ignores the fact, that it was passed through Stop-PSFFunction).
        The automatic message level decrement functionality allows users to make nested commands' messages be less verbose.
    
    .PARAMETER Tags
        The tags that were added to the message
    
    .PARAMETER FunctionName
        The function that wrote the message.
    
    .PARAMETER ModuleName
        The module the function writing the message comes from.
    
    .EXAMPLE
        Convert-DbaMessageLevel -OriginalLevel $Level -FromStopFunction $fromStopFunction -Tags $Tag -FunctionName $FunctionName -ModuleName $ModuleName
        
        This will convert the original level of $Level based on the transformation rules for levels.
#>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Sqlcollaborative.Dbatools.Message.MessageLevel]
        $OriginalLevel,
        
        [Parameter(Mandatory = $true)]
        [bool]
        $FromStopFunction,
        
        [Parameter(Mandatory = $true)]
        [AllowNull()]
        [string[]]
        $Tags,
        
        [Parameter(Mandatory = $true)]
        [string]
        $FunctionName,
        
        [Parameter(Mandatory = $true)]
        [string]
        $ModuleName
    )
    
    $number = $OriginalLevel.value__
    
    if ([Sqlcollaborative.Dbatools.Message.MessageHost]::NestedLevelDecrement -gt 0) {
        $depth = (Get-PSCallStack).Count - 3
        if ($FromStopFunction) { $depth = $depth - 1 }
        $number = $number + $depth * ([Sqlcollaborative.Dbatools.Message.MessageHost]::NestedLevelDecrement)
    }
    
    foreach ($modifier in [Sqlcollaborative.Dbatools.Message.MessageHost]::MessageLevelModifiers.Values) {
        if ($modifier.AppliesTo($FunctionName, $ModuleName, $Tags)) {
            $number = $number + $modifier.Modifier
        }
    }
    
    # Finalize number and return
    if ($number -lt 1) { $number = 1 }
    if ($number -gt 9) { $number = 9 }
    return ([Sqlcollaborative.Dbatools.Message.MessageLevel]$number)
}
tools\dbatools\internal\functions\Convert-DbaMessageTarget.ps1
function Convert-DbaMessageTarget {
<#
    .SYNOPSIS
        Transforms the target input to the message system.
    
    .DESCRIPTION
        Transforms the target input to the message system.
        
        If there is an exception running a transformation scriptblock, it will log the error in the transform error queue and return the original object instead.
    
    .PARAMETER Target
        The input target object, that might have to be transformed (may not either)
    
    .PARAMETER FunctionName
        The function writing the message
    
    .PARAMETER ModuleName
        The module, that the function writing the message is part of
    
    .EXAMPLE
        PS C:\> Convert-DbaMessageTarget -Target $Target -FunctionName 'Get-Test' -ModuleName 'MyModule'
        
        Checks internal storage for definitions that require a target transform, and either returns the original object or the transformed object.
#>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        $Target,
        
        [Parameter(Mandatory = $true)]
        [string]
        $FunctionName,
        
        [Parameter(Mandatory = $true)]
        [string]
        $ModuleName
    )
    
    if ($null -eq $Target) { return }
    
    $typeName = $Target.GetType().FullName.ToLower()
    
    if ([Sqlcollaborative.Dbatools.Message.MessageHost]::TargetTransforms.ContainsKey($typeName)) {
        $scriptBlock = [Sqlcollaborative.Dbatools.Message.MessageHost]::TargetTransforms[$typeName]
        try {
            $tempTarget = $ExecutionContext.InvokeCommand.InvokeScript($false, ([scriptblock]::Create($scriptBlock.ToString())), $null, $Target)
            return $tempTarget
        }
        catch {
            [Sqlcollaborative.Dbatools.Message.MessageHost]::WriteTransformError($_, $FunctionName, $ModuleName, $Target, "Target", ([System.Management.Automation.Runspaces.Runspace]::DefaultRunspace.InstanceId))
            return $Target
        }
    }
    
    if ($transform = [Sqlcollaborative.Dbatools.Message.MessageHost]::TargetTransformlist.Get($typeName, $ModuleName, $FunctionName)) {
        try {
            $tempTarget = $ExecutionContext.InvokeCommand.InvokeScript($false, ([scriptblock]::Create($transform.ScriptBlock.ToString())), $null, $Target)
            return $tempTarget
        }
        catch {
            [Sqlcollaborative.Dbatools.Message.MessageHost]::WriteTransformError($_, $FunctionName, $ModuleName, $Target, "Target", ([System.Management.Automation.Runspaces.Runspace]::DefaultRunspace.InstanceId))
            return $Target
        }
    }
    
    return $Target
}
tools\dbatools\internal\functions\Convert-DbVersionToSqlVersion.ps1
function Convert-DbVersionToSqlVersion {
    <#
.SYNOPSIS
Internal function that makes db versions human readable

.DESCRIPTION
Internal function that makes db versions human readable

.PARAMETER dbversion
Analysis Server

.EXAMPLE
Convert-DbVersionToSqlVersion -dbversion 856

Returns "SQL Server vNext CTP1"

#>
    param (
        [string]$dbversion
    )

    $dbversion = switch ($dbversion) {
        869 { "SQL Server 2017"}
        856 { "SQL Server vNext CTP1" }
        852 { "SQL Server 2016" }
        829 { "SQL Server 2016 Prerelease" }
        782 { "SQL Server 2014" }
        706 { "SQL Server 2012" }
        684 { "SQL Server 2012 CTP1" }
        661 { "SQL Server 2008 R2" }
        660 { "SQL Server 2008 R2" }
        655 { "SQL Server 2008 SP2+" }
        612 { "SQL Server 2005" }
        611 { "SQL Server 2005" }
        539 { "SQL Server 2000" }
        515 { "SQL Server 7.0" }
        408 { "SQL Server 6.5" }
        default { $dbversion }
    }

    return $dbversion
}
tools\dbatools\internal\functions\Convert-HexStringToByte.ps1
function Convert-HexStringToByte {
    <#
    .SYNOPSIS
    Converts hex string into byte object

    .DESCRIPTION
    Converts hex string (e.g. '0x01641736') into the byte object ([byte[]]@(1,100,23,54))
    Used when working with SMO logins and their byte parameters: sids and hashed passwords

    .PARAMETER InputObject
    Input hex string (e.g. '0x1234' or 'DBA2FF')

    .NOTES
    Tags: Login, Internal
    Author: Kirill Kravtsov (@nvarscar)
    dbatools PowerShell module (https://dbatools.io, [email protected])
    Copyright (C) 2016 Chrissy LeMaire
    License: MIT https://opensource.org/licenses/MIT

    .EXAMPLE
    Convert-HexStringToByte '0x01641736'

    Returns byte[] object [byte[]]@(1,100,23,54)

    .EXAMPLE
    Convert-HexStringToByte '1234'

    Returns byte[] object [byte[]]@(18,52)
#>
    param (
        [string]$InputObject
    )
    $hexString = $InputObject.TrimStart("0x")
    if ($hexString.Length % 2 -eq 1) { $hexString = '0' + $hexString }
    [byte[]]$outByte = $null; $outByte += 0 .. (($hexString.Length) / 2 - 1) | ForEach-Object { [Int16]::Parse($hexString.Substring($_ * 2, 2), 'HexNumber') }
    Return $outByte
}
tools\dbatools\internal\functions\Disconnect-Regserver.ps1
function Disconnect-Regserver ($Server) {
    $i = 0
    do { $server = $server.Parent }
    until ($null -ne $server.ServerConnection -or $i++ -gt 20)
    if ($server.ServerConnection) {
        $server.ServerConnection.Disconnect()
    }
}
tools\dbatools\internal\functions\Get-BackupAncientHistory.ps1
function Get-BackupAncientHistory {
    <#
        .SYNOPSIS
            Returns details of the last full backup of a SQL Server 2000 database

        .DESCRIPTION
            Backup History command to pull limited history from a SQL 2000 instance. If not using SQL 2000, please use Get-DbaBackupHistory which pulls more infomation, and has more options. This is just here to cope with 2k and copy-DbaDatabase issues

        .PARAMETER SqlInstance
            SQL Server name or SMO object representing the SQL Server to connect to. This can be a collection and receive pipeline input to allow the function to be executed against multiple SQL Server instances.

        .PARAMETER Credential
            Credential object used to connect to the SQL Server Instance as a different user. This can be a Windows or SQL Server account. Windows users are determined by the existence of a backslash, so if you are intending to use an alternative Windows connection instead of a SQL login, ensure it contains a backslash.

        .PARAMETER Database
            Specifies one or more database(s) to process. If unspecified, all databases will be processed.

        .NOTES
        Author: Stuart Moore (@napalmgram), stuart-moore.com

        dbatools PowerShell module (https://dbatools.io, [email protected])
        Copyright (C) 2016 Chrissy LeMaire
        License: MIT https://opensource.org/licenses/MIT

    #>
    [CmdletBinding(DefaultParameterSetName = "Default")]
    Param (
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [Alias("Credential")]
        [PsCredential]$SqlCredential,
        [Alias("Databases")]
        [object[]]$Database,
        [string]$FileNameStub,
        [Alias('Silent')]
        [switch]$EnableException
    )
    BEGIN {
        try {
            Write-Message -Level VeryVerbose -Message "Connecting to $SqlInstance." -Target $SqlInstance
            $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
        }
        catch {
            Stop-Function -Message "Failed to process Instance $SqlInstance." -InnerErrorRecord $_ -Target $SqlInstance -Continue
        }
        if ($server.SoftwareVersionMajor -gt 8) {
            Write-Message -Level Warning -Message "This is not the function you're looking for. This is for SQL 2000 only, please use Get-DbaBackupHistory instead. It's much nicer"
        }

        $databases = @()
        if ($null -ne $Database) {
            ForEach ($db in $Database) {
                $databases += [PScustomObject]@{name = $db}
            }
        }
        else {
            $databases = $server.Databases
        }
    }

    PROCESS {
        foreach ($db in $Database) {
            Write-Message -Level Verbose -Message "Processing database $db"
            $sql = "
            SELECT
            a.Server,
             a.[Database],
             a.Username,
             a.Start,
             a.[End],
             a.Duration,
             a.[Path],
             a.Type,
            NULL as TotalSize,
             a.MediaSetId,
             a.BackupSetID,
             a.Software,
              a.position,
              a.first_lsn,
              a.database_backup_lsn,
              a.checkpoint_lsn,
              a.last_lsn,
             a.first_lsn as 'FirstLSN',
              a.database_backup_lsn as 'DatabaseBackupLsn',
              a.checkpoint_lsn as 'CheckpointLsn',
              a.last_lsn as 'Lastlsn',
              a.software_major_version,
             a.DeviceType,
                NULL as is_copy_only,
            NULL as last_recovery_fork_guid
            FROM (
            SELECT
              backupset.database_name AS [Database],
              backupset.user_name AS Username,
              backupset.backup_start_date AS Start,
              backupset.server_name as [Server],
              backupset.backup_finish_date AS [End],
              DATEDIFF(SECOND, backupset.backup_start_date, backupset.backup_finish_date) AS Duration,
              mediafamily.physical_device_name AS Path,
              CASE backupset.type
             WHEN 'L' THEN 'Log'
             WHEN 'D' THEN 'Full'
             WHEN 'F' THEN 'File'
             WHEN 'I' THEN 'Differential'
             WHEN 'G' THEN 'Differential File'
             WHEN 'P' THEN 'Partial Full'
             WHEN 'Q' THEN 'Partial Differential'
             ELSE NULL
              END AS Type,
              backupset.media_set_id AS MediaSetId,
              mediafamily.media_family_id as mediafamilyid,
              backupset.backup_set_id as BackupSetID,
              CASE mediafamily.device_type
             WHEN 2 THEN 'Disk'
             WHEN 102 THEN 'Permanent Disk Device'
             WHEN 5 THEN 'Tape'
             WHEN 105 THEN 'Permanent Tape Device'
             WHEN 6 THEN 'Pipe'
             WHEN 106 THEN 'Permanent Pipe Device'
             WHEN 7 THEN 'Virtual Device'
             ELSE 'Unknown'
             END AS DeviceType,
              backupset.position,
              backupset.first_lsn,
              backupset.database_backup_lsn,
              backupset.checkpoint_lsn,
              backupset.last_lsn,
              backupset.software_major_version,
              mediaset.software_name AS Software
            FROM msdb..backupmediafamily AS mediafamily
            JOIN msdb..backupmediaset AS mediaset
              ON mediafamily.media_set_id = mediaset.media_set_id
            JOIN msdb..backupset AS backupset
              ON backupset.media_set_id = mediaset.media_set_id
            WHERE backupset.database_name = '$db'
                    ) AS a
            where  a.backupsetid in (Select max(backup_set_id) from msdb..backupset where database_name='$db')"
            Write-Message -Level Debug -Message $sql
            $results = $server.ConnectionContext.ExecuteWithResults($sql).Tables.Rows | Select-Object * -ExcludeProperty BackupSetRank, RowError, Rowstate, table, itemarray, haserrors
            Write-Message -Level SomewhatVerbose -Message "Processing as grouped output."
            $GroupedResults = $results | Group-Object -Property backupsetid
            Write-Message -Level SomewhatVerbose -Message "$($GroupedResults.Count) result-groups found."
            $groupResults = @()
            foreach ($group in $GroupedResults) {

                $fileSql = "select file_type as FileType, logical_name as LogicalName, physical_name as PhysicalName
                            from msdb.dbo.backupfile where backup_set_id='$($Group.group[0].BackupSetID)'"

                Write-Message -Level Debug -Message "FileSQL: $fileSql"

                $historyObject = New-Object Sqlcollaborative.Dbatools.Database.BackupHistory
                $historyObject.ComputerName = $server.ComputerName
                $historyObject.InstanceName = $server.ServiceName
                $historyObject.SqlInstance = $server.DomainInstanceName
                $historyObject.Database = $group.Group[0].Database
                $historyObject.UserName = $group.Group[0].UserName
                $historyObject.Start = ($group.Group.Start | Measure-Object -Minimum).Minimum
                $historyObject.End = ($group.Group.End | Measure-Object -Maximum).Maximum
                $historyObject.Duration = New-TimeSpan -Seconds ($group.Group.Duration | Measure-Object -Maximum).Maximum
                $historyObject.Path = $group.Group.Path
                $historyObject.TotalSize = $NULL
                $historyObject.Type = $group.Group[0].Type
                $historyObject.BackupSetId = $group.Group[0].BackupSetId
                $historyObject.DeviceType = $group.Group[0].DeviceType
                $historyObject.Software = $group.Group[0].Software
                $historyObject.FullName = $group.Group.Path
                $historyObject.FileList = $server.ConnectionContext.ExecuteWithResults($fileSql).Tables.Rows
                $historyObject.Position = $group.Group[0].Position
                $historyObject.FirstLsn = $group.Group[0].First_LSN
                $historyObject.DatabaseBackupLsn = $group.Group[0].database_backup_lsn
                $historyObject.CheckpointLsn = $group.Group[0].checkpoint_lsn
                $historyObject.LastLsn = $group.Group[0].Last_Lsn
                $historyObject.SoftwareVersionMajor = $group.Group[0].Software_Major_Version
                $historyObject.IsCopyOnly = if ($group.Group[0].is_copy_only -eq 1) {
                    $true
                }
                else {
                    $false
                }
                $groupResults += $historyObject
            }
            $groupResults | Sort-Object -Property LastLsn, Type
        }

    }

    END {}
}
tools\dbatools\internal\functions\Get-CodePage.ps1
function Get-CodePage {
    <#
        .SYNOPSIS
            Converts Microsoft's code page ID to human readable format

        .DESCRIPTION
            Converts Microsoft's code page ID to human readable format

        .PARAMETER Id
            The code page ID

        .EXAMPLE
            Get-CodePage 1252

            Returns a pscustomobject with id, alias and name
    #>
    [CmdletBinding()]
    param (
        [int]$id
    )
    process {
        $encoding = [System.Text.Encoding]::GetEncoding($id)
        $IncludeProps = 'CodePage', 'BodyName', 'EncodingName', 'HeaderName', 'WebName', 'IsSingleByte'
        Select-DefaultView -InputObject $encoding -Property $IncludeProps
    }
}
tools\dbatools\internal\functions\Get-DbaADObject.ps1
#ValidationTags#FlowControl,Pipeline#
function Get-DbaADObject {
    <#
    .SYNOPSIS
    Get-DbaADObject tries to facilitate searching AD with dbatools, which ATM can't require AD cmdlets.

    .DESCRIPTION
    As working with multiple domains, forests, ldap filters, partitions, etc is quite hard to grasp, let's try to do "the right thing" here and
    facilitate everybody's work with it. It either returns the exact matched result or None if it isn't found. You can inspect the raw object
    calling GetUnderlyingObject() on the returned object.

    .PARAMETER ADObject
    Pass in both the domain and the login name in Domain\sAMAccountName format (the one everybody is accustomed to)
    You can also pass a UserPrincipalName format (with the correct IdentityType, either with Domain\UserPrincipalName or UserPrincipalName@Domain)
    Beware: the "Domain" part of the UPN *can* be different from the real domain, see "UPN suffixes" (https://msdn.microsoft.com/en-us/library/windows/desktop/aa380525(v=vs.85).aspx)
    It's always best to pass the real domain name in (see the examples)
    For any other format, please beware that the domain part must always be specified (again, for the best result, before the slash)

    .PARAMETER Type
    You *should* always know what you are asking for. Please pass in Computer,Group or User to help speeding up the search

    .PARAMETER IdentityType
    By default objects are searched using sAMAccountName format, here you can pass different representation that need to match the passed in ADObject

    .PARAMETER Credential
    Use this credential to connect to the domain and search for the needed ADObject. If not passed, uses the current process' one.

    .PARAMETER SearchAllDomains
    Search for the object in all domains connected to the current one. If you are unsure what domain the object is coming from,
    using this switch will search through all domains in your forest and also in the ones that are trusted. This is HEAVY, but it can save
    some headaches.

    .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .NOTES
    Author: Niphlod, https://github.com/niphlod
    Tags:
    dbatools PowerShell module (https://dbatools.io, [email protected])
    Copyright (C) 2016 Chrissy LeMaire
    License: MIT https://opensource.org/licenses/MIT

    .EXAMPLE
    Get-DbaADObject -ADObject "contoso\ctrlb" -Type User

    Searches in the contoso domain for a ctrlb user

    .EXAMPLE
    Get-DbaADObject -ADObject "[email protected]" -Type User -IdentityType UserPrincipalName

    Searches in the contoso domain for a ctrlb user using the UserPrincipalName format. Again, beware of the UPN suffixes in elaborate AD structures!

    .EXAMPLE
    Get-DbaADObject -ADObject "contoso\[email protected]" -Type User -IdentityType UserPrincipalName

    Searches in the contoso domain for a [email protected] user using the UserPrincipalName format. This kind of search is better than the previous one
    because it takes into account possible UPN suffixes

    .EXAMPLE
    Get-DbaADObject -ADObject "[email protected]" -Type User -IdentityType UserPrincipalName -SearchAllDomains

    As a last resort, searches in all the current forest for a [email protected] user using the UserPrincipalName format

    .EXAMPLE
    Get-DbaADObject -ADObject "contoso\sqlcollaborative" -Type Group

    Searches in the contoso domain for a sqlcollaborative group

    .EXAMPLE
    Get-DbaADObject -ADObject "contoso\SqlInstance2014$" -Type Group

    Searches in the contoso domain for a SqlInstance2014 computer (remember the ending $ for computer objects)

    .EXAMPLE
    Get-DbaADObject -ADObject "contoso\ctrlb" -Type User -EnableException

    Searches in the contoso domain for a ctrlb user, suppressing all error messages and throw exceptions that can be caught instead

#>
    [CmdletBinding()]
    param (
        [string[]]$ADObject,
        [ValidateSet("User", "Group", "Computer")]
        [string]$Type,

        [ValidateSet("DistinguishedName", "Guid", "Name", "SamAccountName", "Sid", "UserPrincipalName")]
        [string]$IdentityType = "SamAccountName",

        [PSCredential]$Credential,
        [switch]$SearchAllDomains,
        [Alias('Silent')]
        [switch]$EnableException
    )
    begin {
        try {
            Add-Type -AssemblyName System.DirectoryServices.AccountManagement
        }
        catch {
            Stop-Function -Message "Failed to load the required module $($_.Exception.Message)" -EnableException $EnableException -InnerErrorRecord $_
            return
        }
        switch ($Type) {
            "User" {
                $searchClass = [System.DirectoryServices.AccountManagement.UserPrincipal]
            }
            "Group" {
                $searchClass = [System.DirectoryServices.AccountManagement.GroupPrincipal]
            }
            "Computer" {
                $searchClass = [System.DirectoryServices.AccountManagement.ComputerPrincipal]
            }
            default {
                $searchClass = [System.DirectoryServices.AccountManagement.Principal]
            }
        }

        function Get-DbaADObjectInternal($Domain, $IdentityType, $obj, $EnableException) {
            try {
                # can we simply resolve the passed domain ? This has the benefit of raising almost instantly if the domain is not valid
                $Context = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Domain', $Domain)
                $null = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($Context)
                if ($Credential) {
                    $ctx = New-Object System.DirectoryServices.AccountManagement.PrincipalContext('Domain', $Domain, $Credential.UserName, $Credential.GetNetworkCredential().Password)
                }
                else {
                    $ctx = New-Object System.DirectoryServices.AccountManagement.PrincipalContext('Domain', $Domain)
                }
                $found = $searchClass::FindByIdentity($ctx, $IdentityType, $obj)
                $found
            }
            catch {
                Stop-Function -Message "Errors trying to connect to the domain $Domain $($_.Exception.Message)" -EnableException $EnableException -InnerErrorRecord $_ -Target $ADObj
            }
        }
    }
    process {
        if (Test-FunctionInterrupt) { return }
        foreach ($ADObj in $ADObject) {
            # passing the domain as the first part before the \ wins always in defining the domain to search into
            $Splitted = $ADObj.Split("\")
            if ($Splitted.Length -ne 2) {
                # we can also take the object@domain format
                $Splitted = $ADObj.Split("@")
                if ($Splitted.Length -ne 2) {
                    Stop-Function -Message "You need to pass ADObject either DOMAIN\object or object@domain format" -Continue -EnableException $EnableException
                }
                else {
                    if ($IdentityType -ne 'UserPrincipalName') {
                        $obj, $Domain = $Splitted
                    }
                    else {
                        # if searching for a UserPrincipalName format without a specific domain passed in before the slash,
                        # we can assume there are no custom UPN suffixes in place
                        $obj, $Domain = $AdObj, $Splitted[1]
                    }
                }
            }
            else {
                $Domain, $obj = $Splitted
            }
            if ($SearchAllDomains) {
                Write-Message -Message "Searching for $obj under all domains in $IdentityType format" -Level VeryVerbose
                # if we're lucky, we can resolve the domain right away
                try {
                    Get-DbaADObjectInternal -Domain $Domain -IdentityType $IdentityType -obj $obj -EnableException $true
                }
                catch {
                    # if not, let's build up all domains
                    $ForestObject = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
                    $AllDomains = $ForestObject.Domains.Name
                    foreach ($ForestDomain in $AllDomains) {
                        Write-Message -Message "Searching for $obj under domain $ForestDomain in $IdentityType format" -Level VeryVerbose
                        $found = Get-DbaADObjectInternal -Domain $ForestDomain -IdentityType $IdentityType -obj $obj
                        if ($found) {
                            $found
                            break
                        }
                    }
                    # we are very unlucky, let's search also in all trusted domains
                    $AllTrusted = ($ForestObject.GetAllTrustRelationships().TopLevelNames | Where-Object Status -eq 'Enabled').Name
                    foreach ($ForestDomain in $AllTrusted) {
                        Write-Message -Message "Searching for $obj under domain $ForestDomain in $IdentityType format" -Level VeryVerbose
                        $found = Get-DbaADObjectInternal -Domain $ForestDomain -IdentityType $IdentityType -obj $obj
                        if ($found) {
                            $found
                            break
                        }
                    }
                }
            }
            else {
                Write-Message -Message "Searching for $obj under domain $domain in $IdentityType format" -Level VeryVerbose
                Get-DbaADObjectInternal -Domain $Domain -IdentityType $IdentityType -obj $obj
            }
        }
    }
}

tools\dbatools\internal\functions\Get-DbaDbPhysicalFile.ps1
function Get-DbaDbPhysicalFile {
    <#
    .SYNOPSIS
    Gets raw information about physical files linked to databases

    .DESCRIPTION
    Fastest way to fetch just the paths of the physical files for every database on the instance, also for offline databases.
    Incidentally, it also fetches the paths for MMO and FS filegroups.
    This is partly already in Get-DbaDatabaseFile, but this internal needs to stay lean and fast, as it's heavily used in top-level functions

    .PARAMETER SqlInstance
    SMO object representing the SQL Server to connect to.

    .EXAMPLE
    Get-DbaDbPhysicalFile -SqlInstance server1\instance2

    .NOTES
        Author: Simone Bizzotto

        dbatools PowerShell module (https://dbatools.io, [email protected])
        Copyright (C) 2016 Chrissy LeMaire
        License: MIT https://opensource.org/licenses/MIT
    #>
    [CmdletBinding()]
    param(
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential
    )
    try {
        Write-Message -Level Verbose -Message "Connecting to $SqlInstance"
        $Server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
    }
    catch {
        Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $SqlInstance
        return
    }
    if ($Server.versionMajor -le 8) {
        $sql = "SELECT DB_NAME(db_id) AS Name, filename AS PhysicalName FROM sysaltfiles"
    }
    else {
        $sql = "SELECT DB_NAME(database_id) AS Name, physical_name AS PhysicalName FROM sys.master_files"
    }
    Write-Message -Level Debug -Message "$sql"
    try {
        $Server.Query($sql)
    }
 catch {
        throw "Error enumerating files"
    }
}
tools\dbatools\internal\functions\Get-DbaFileStreamFolder.ps1
function Get-DbaFileStreamFolder {
    <#

    .SYNOPSIS
        Returns basic information about Filestream folders from a Sql Instance

    .DESCRIPTION
        Given a SQL Instance, and an optional list of databases returns the FileStream containing folders on that Instance. Without the Database parameter, all dbs with FileStream are returned

    .PARAMETER SqlInstance
        The Sql Server instance to be queries

    .PARAMETER SqlCredential
        A Sql Credential to connect to $SqlInstance

    .PARAMETER Database
        Database to be tested, multiple databases may be specified as a comma seperated list.

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .EXAMPLE
        Get-DbaFileStreamFolder -SqlInstance server1\instance2

        Returns all FileStream folders from server1\instance2

    .EXAMPLE
        Get-DbaFileStreamFolder -SqlInstance server1\instance2 -Database Archive

        Returns any FileStream folders from the Archive database on server1\instance2

    .NOTES
    Author:Stuart Moore (@napalmgram stuart-moore.com )


    Website: https://dbatools.io
    Copyright: (C) Chrissy LeMaire, [email protected]
    License: MIT https://opensource.org/licenses/MIT
    #>
    param (
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$Database,
        [switch]$EnableException
    )

    BEGIN {
        try {
            Write-Message -Level VeryVerbose -Message "Connecting to $SqlInstance." -Target $SqlInstance
            $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
        }
        catch {
            Stop-Function -Message "Failed to process Instance $SqlInstance." -InnerErrorRecord $_ -Target $SqlInstance -Continue
        }
    }

    PROCESS {
        $sql = "select d.name as 'dbname', mf.Physical_Name from sys.master_files mf inner join sys.databases d on mf.database_id = d.database_id
        where mf.type=2"
        $databases = @()
        if ($null -ne $Database) {
            ForEach ($db in $Database) {
                $databases += "'$db'"
            }
            $sql = $sql + " and d.name in ( $($databases -join ',') )"
        }

        $results = $server.ConnectionContext.ExecuteWithResults($sql).Tables.Rows | Select-Object * -ExcludeProperty  RowError, Rowstate, table, itemarray, haserrors
        foreach ($result in $results) {
            [PsCustomObject]@{
                ServerInstance   = $SqlInstance
                Database         = $result.dbname
                FileStreamFolder = $result.Physical_Name
            }
        }


    }

    END {}
}
tools\dbatools\internal\functions\Get-DbaMessageLevelModifier.ps1
function Get-DbaMessageLevelModifier {
<#
    .SYNOPSIS
        Returns all registered message level modifiers with similar name.
    
    .DESCRIPTION
        Returns all registered message level modifiers with similar name.
        
        Message level modifiers are created using New-DbaMessageLevelModifier and allow dynamically modifying the actual message level written by commands.
    
    .PARAMETER Name
        Default: "*"
        A name filter - only commands that are similar to the filter will be returned.
    
    .EXAMPLE
        PS C:\> Get-DbaMessageLevelModifier
        
        Returns all message level filters
    
    .EXAMPLE
        PS C:\> Get-DbaMessageLevelModifier -Name "mymodule.*"
        
        Returns all message level filters that start with "mymodule."
#>
    [CmdletBinding()]
    param (
        [string]
        $Name = "*"
    )
    
    ([Sqlcollaborative.Dbatools.Message.MessageHost]::MessageLevelModifiers.Values) | Where-Object Name -Like $Name
}
tools\dbatools\internal\functions\Get-DbaRunspace.ps1
function Get-DbaRunspace {
    <#
    .SYNOPSIS
        Returns registered runspaces.

    .DESCRIPTION
        Returns a list of runspaces that have been registered with dbatools

    .PARAMETER Name
        Default: "*"
        Only registered runspaces of similar names are returned.

    .EXAMPLE
        PS C:\> Get-DbaRunspace

        Returns all registered runspaces

    .EXAMPLE
        PS C:\> Get-DbaRunspace -Name 'mymodule.maintenance'

        Returns the runspace registered under the name 'mymodule.maintenance'
#>
    [CmdletBinding()]
    param (
        [string]
        $Name = "*"
    )

    [Sqlcollaborative.Dbatools.Runspace.RunspaceHost]::Runspaces.Values | Where-Object Name -Like $Name
}
tools\dbatools\internal\functions\Get-DbaService.ps1
function Get-DbaService {
    <#
    .SYNOPSIS
        Uses WMI/CIM to scan for the existance of a specific windows services.

    .DESCRIPTION
        Uses WMI/CIM to scan for the existance of a specific windows services.

        Use Get-DbaSqlService if you are interested in scanning for sql server services exclusively.

    .PARAMETER ComputerName
        The computer to target. Uses localhost by default.

    .PARAMETER Name
        The name of the service to search for.

    .PARAMETER DisplayName
        The display-name of the service to search for.

    .PARAMETER Credential
        The credentials to use when connecting to the computer.

    .PARAMETER DoNotUse
        Connection Protocols that should not be used when retrieving the information.

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .EXAMPLE
        Get-DbaService -Name LanmanServer

        Returns information on the LanmanServer service from localhost.

    .EXAMPLE
        Get-ADComputer -Filter * | Get-DbaService -Name Browser

        First retrieves all computer accounts from active directory, then scans all of those computers for the browser service.
        Note: THis may take seriously long time, you may also want to filter out computers that are offline before scanning for services.

    .EXAMPLE
        Get-DbaService -ComputerName "server1","server2","server3" -Name Lanman%

        Scans the servers server1, server2 and server3 for all services whose name starts with 'lanman'
#>
    [CmdletBinding()]
    param (
        [string[]]
        $Name,

        [string[]]
        $DisplayName,

        [Parameter(ValueFromPipeline = $true)]
        [Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter[]]
        $ComputerName = $env:COMPUTERNAME,

        [System.Management.Automation.PSCredential]
        $Credential,

        [Sqlcollaborative.Dbatools.Connection.ManagementConnectionType[]]
        $DoNotUse,

        [switch]
        [Alias('Silent')]$EnableException
    )

    begin {
        Write-Message -Level InternalComment -Message "Starting"
        Write-Message -Level System -Message "Bound parameters: $($PSBoundParameters.Keys -join ", ")"

        if (-not (Test-Bound "Name") -and -not (Test-Bound "DisplayName")) {
            $Name = "%"
        }
    }
    process {
        :main foreach ($computer in $ComputerName) {
            Write-Message -Level VeryVerbose -Message "Processing queries to $($computer.ComputerName)" -Target $computer.ComputerName
            foreach ($serviceName in $Name) {
                Write-Message -Level Verbose -Message "Searching for services with name: $serviceName" -Target $computer.ComputerName
                try {
                    if (Test-Bound "Credential") { Get-DbaCmObject -Query "SELECT * FROM Win32_Service WHERE Name LIKE '$serviceName'" -ComputerName $computer.ComputerName -Credential $Credential -EnableException -DoNotUse $DoNotUse }
                    else { Get-DbaCmObject -Query "SELECT * FROM Win32_Service WHERE Name LIKE '$serviceName'" -ComputerName $computer.ComputerName -EnableException -DoNotUse $DoNotUse }
                }
                catch {
                    if ($_.CategoryInfo.Category -eq "OpenError") {
                        Stop-Function -Message "Failed to access computer $($computer.ComputerName)" -ErrorRecord $_ -Target $computer.ComputerName -Continue -ContinueLabel main
                    }
                    else {
                        Stop-Function -Message "Failed to retrieve service" -ErrorRecord $_ -Target $computer.ComputerName -Continue
                    }
                }
            }

            foreach ($serviceDisplayName in $DisplayName) {
                Write-Message -Level Verbose -Message "Searching for services with display name: $serviceDisplayName" -Target $computer.ComputerName
                try {
                    if (Test-Bound "Credential") { Get-DbaCmObject -Query "SELECT * FROM Win32_Service WHERE DisplayName LIKE '$serviceDisplayName'" -ComputerName $computer.ComputerName -Credential $Credential -EnableException -DoNotUse $DoNotUse }
                    else { Get-DbaCmObject -Query "SELECT * FROM Win32_Service WHERE DisplayName LIKE '$serviceDisplayName'" -ComputerName $computer.ComputerName -EnableException -DoNotUse $DoNotUse }
                }
                catch {
                    if ($_.CategoryInfo.Category -eq "OpenError") {
                        Stop-Function -Message "Failed to access computer $($computer.ComputerName)" -ErrorRecord $_ -Target $computer.ComputerName -Continue -ContinueLabel main
                    }
                    else {
                        Stop-Function -Message "Failed to retrieve service" -ErrorRecord $_ -Target $computer.ComputerName -Continue
                    }
                }
            }
        }
    }
    end {
        Write-Message -Level InternalComment -Message "Ending"
    }
}
tools\dbatools\internal\functions\Get-DBASQLServiceErrorMessage.ps1
function Get-DBASQLServiceErrorMessage {
    <#
    .SYNOPSIS
    Internal function. Returns the list of error code messages for Windows service management.

#>
    param(
        [parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 1)]
        [int]$ErrorNumber
    )
    $returnCodes = @("The request was accepted.",
        "The request is not supported.",
        "The user did not have the necessary access.",
        "The service cannot be stopped because other services that are running are dependent on it.",
        "The requested control code is not valid, or it is unacceptable to the service.",
        "The requested control code cannot be sent to the service because the state of the service (Win32_BaseService.State property) is equal to 0, 1, or 2.",
        "The service has not been started.",
        "The service did not respond to the start request in a timely fashion.",
        "Unknown failure when starting the service.",
        "The directory path to the service executable file was not found.",
        "The service is already running.",
        "The database to add a new service is locked.",
        "A dependency this service relies on has been removed from the system.",
        "The service failed to find the service needed from a dependent service.",
        "The service has been disabled from the system.",
        "The service does not have the correct authentication to run on the system.",
        "This service is being removed from the system.",
        "The service has no execution thread.",
        "The service has circular dependencies when it starts.",
        "A service is running under the same name.",
        "The service name has invalid characters.",
        "Invalid parameters have been passed to the service.",
        "The account under which this service runs is either invalid or lacks the permissions to run the service.",
        "The service exists in the database of services available from the system.",
        "The service is currently paused in the system.")
    if ($ErrorNumber -in 0..($returnCodes.Length - 1)) { Return $returnCodes[$ErrorNumber] }
    else { Return "Unknown error." }
}
tools\dbatools\internal\functions\Get-DecryptedObject.ps1
function Get-DecryptedObject {
            <#
            .SYNOPSIS
                Internal function.

                This function is heavily based on Antti Rantasaari's script at http://goo.gl/wpqSib
                Antti Rantasaari 2014, NetSPI
                License: BSD 3-Clause http://opensource.org/licenses/BSD-3-Clause
            #>
    param (
        [Parameter(Mandatory)]
        [Microsoft.SqlServer.Management.Smo.Server]$SqlInstance,
        [Parameter(Mandatory)]
        [ValidateSet("LinkedServer", "Credential")]
        [string]$Type,
        [switch]$EnableException
    )
    
    $server = $SqlInstance
    $sourceName = $server.Name
    
    # Query Service Master Key from the database - remove padding from the key
    # key_id 102 eq service master key, thumbprint 3 means encrypted with machinekey
    $sql = "SELECT substring(crypt_property,9,len(crypt_property)-8) as smk FROM sys.key_encryptions WHERE key_id=102 and (thumbprint=0x03 or thumbprint=0x0300000001)"
    try {
        $smkbytes = $server.Query($sql).smk
    }
    catch {
        Stop-Function -Message "Can't execute query on $sourcename" -Target $server -ErrorRecord $_
        return
    }
    
    $sourceNetBios = Resolve-NetBiosName $server
    $instance = $server.InstanceName
    $serviceInstanceId = $server.ServiceInstanceId
    
    # Get entropy from the registry - hopefully finds the right SQL server instance
    try {
        [byte[]]$entropy = Invoke-Command2 -Raw -Credential $Credential -ComputerName $sourceNetBios -argumentlist $serviceInstanceId {
            $serviceInstanceId = $args[0]
            $entropy = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$serviceInstanceId\Security\").Entropy
            return $entropy
        }
    }
    catch {
       Stop-Function -Message "Can't access registry keys on $sourceName. Do you have administrative access to the Windows registry on $sourcename? Otherwise, we're out of ideas." -Target $source
        return
    }
    
    # Decrypt the service master key
    try {
        $serviceKey = Invoke-Command2 -Raw -Credential $Credential -ComputerName $sourceNetBios -ArgumentList $smkbytes, $Entropy {
            Add-Type -AssemblyName System.Security
            Add-Type -AssemblyName System.Core
            $smkbytes = $args[0]; $Entropy = $args[1]
            $serviceKey = [System.Security.Cryptography.ProtectedData]::Unprotect($smkbytes, $Entropy, 'LocalMachine')
            return $serviceKey
        }
    }
    catch {
        Stop-Function -Message "Can't unprotect registry data on $sourcename. Do you have administrative access to the Windows registry on $sourcename? Otherwise, we're out of ideas." -Target $source
        return
    }
    
    # Choose the encryption algorithm based on the SMK length - 3DES for 2008, AES for 2012
    # Choose IV length based on the algorithm
    if (($serviceKey.Length -ne 16) -and ($serviceKey.Length -ne 32)) {
        Write-Message -Level Verbose -Message "ServiceKey found: $serviceKey.Length"
        Stop-Function -Message "Unknown key size. Do you have administrative access to the Windows registry on $sourcename? Otherwise, we're out of ideas." -Target $source
        return
    }
    
    if ($serviceKey.Length -eq 16) {
        $decryptor = New-Object System.Security.Cryptography.TripleDESCryptoServiceProvider
        $ivlen = 8
    }
    elseif ($serviceKey.Length -eq 32) {
        $decryptor = New-Object System.Security.Cryptography.AESCryptoServiceProvider
        $ivlen = 16
    }
    
            <#
                Query link server password information from the Db.
                Remove header from pwdhash, extract IV (as iv) and ciphertext (as pass)
                Ignore links with blank credentials (integrated auth ?)
            #>
    try {
        if (-not $server.IsClustered) {
            $connString = "Server=ADMIN:$sourceNetBios\$instance;Trusted_Connection=True"
        }
        else {
            $dacEnabled = $server.Configuration.RemoteDacConnectionsEnabled.ConfigValue
            
            if ($dacEnabled -eq $false) {
                If ($Pscmdlet.ShouldProcess($server.Name, "Enabling DAC on clustered instance.")) {
                    Write-Message -Level Verbose -Message "DAC must be enabled for clusters, even when accessed from active node. Enabling."
                    $server.Configuration.RemoteDacConnectionsEnabled.ConfigValue = $true
                    $server.Configuration.Alter()
                }
            }
            
            $connString = "Server=ADMIN:$sourceName;Trusted_Connection=True"
        }
    }
    catch {
        Stop-Function -Message "Failure enabling DAC on $sourcename" -Target $source -ErrorRecord $_
    }
    
    <# NOTE: This query is accessing syslnklgns table. Can only be done via the DAC connection #>
    
    $sql = switch ($Type) {
        "LinkedServer" {
            "SELECT sysservers.srvname,
                    syslnklgns.Name,
                    substring(syslnklgns.pwdhash,5,$ivlen) iv,
                    substring(syslnklgns.pwdhash,$($ivlen + 5),
                    len(syslnklgns.pwdhash)-$($ivlen + 4)) pass
                FROM master.sys.syslnklgns
                    inner join master.sys.sysservers
                    on syslnklgns.srvid=sysservers.srvid
                WHERE len(pwdhash) > 0"
        }
        "Credential" {
            "SELECT QUOTENAME(name) AS name,credential_identity,substring(imageval,5,$ivlen) iv, substring(imageval,$($ivlen + 5),len(imageval)-$($ivlen + 4)) pass from sys.Credentials cred inner join sys.sysobjvalues obj on cred.credential_id = obj.objid where valclass=28 and valnum=2"
        }
    }
    
    Write-Message -Level Debug -Message $sql
    # Get entropy from the registry
    try {
        $results = Invoke-Command2 -Raw -Credential $Credential -ComputerName $sourceNetBios -ArgumentList $connString, $sql {
            $connString = $args[0]; $sql = $args[1]
            $conn = New-Object System.Data.SqlClient.SQLConnection($connString)
            $conn.open()
            $cmd = New-Object System.Data.SqlClient.SqlCommand($sql, $conn);
            $dt = New-Object System.Data.DataTable
            $dt.Load($cmd.ExecuteReader())
            $conn.Close()
            $conn.Dispose()
            return $dt
        }
    }
    catch {
        Stop-Function -Message "Can't establish local DAC connection on $sourcename." -Target $server -ErrorRecord $_
        return
    }
    
    if ($server.IsClustered -and $dacEnabled -eq $false) {
        If ($Pscmdlet.ShouldProcess($server.Name, "Disabling DAC on clustered instance.")) {
            try {
                Write-Message -Level Verbose -Message "Setting DAC config back to 0."
                $server.Configuration.RemoteDacConnectionsEnabled.ConfigValue = $false
                $server.Configuration.Alter()
            }
            catch {
                Stop-Function -Message "Can't establish local DAC connection on $sourcename" -Target $server -ErrorRecord $_
                return
            }
        }
    }
    
    # Go through each row in results
    foreach ($result in $results) {
        # decrypt the password using the service master key and the extracted IV
        $decryptor.Padding = "None"
        $decrypt = $decryptor.Createdecryptor($serviceKey, $result.iv)
        $stream = New-Object System.IO.MemoryStream ( , $result.pass)
        $crypto = New-Object System.Security.Cryptography.CryptoStream $stream, $decrypt, "Write"
        
        $crypto.Write($result.pass, 0, $result.pass.Length)
        [byte[]]$decrypted = $stream.ToArray()
        
        # convert decrypted password to unicode
        $encode = New-Object System.Text.UnicodeEncoding
        
        # Print results - removing the weird padding (8 bytes in the front, some bytes at the end)...
        # Might cause problems but so far seems to work.. may be dependant on SQL server version...
        # If problems arise remove the next three lines..
        $i = 8; foreach ($b in $decrypted) { if ($decrypted[$i] -ne 0 -and $decrypted[$i + 1] -ne 0 -or $i -eq $decrypted.Length) { $i -= 1; break; }; $i += 1; }
        $decrypted = $decrypted[8 .. $i]
        
        if ($Type -eq "LinkedServer") {
            $name = $result.srvname
            $identity = $result.Name
        }
        else {
            $name = $result.name
            $identity = $result.credential_identity
        }
        [pscustomobject]@{
            Name = $name
            Identity = $identity
            Password = $encode.GetString($decrypted)
        }
    }
}
tools\dbatools\internal\functions\Get-DirectoryRestoreFile.ps1
function Get-DirectoryRestoreFile {
    <#
.SYNOPSIS
Internal Function to get SQL Server backfiles from a specified folder

.DESCRIPTION
Takes path, checks for validity. Scans for usual backup file
#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [string]$Path,
        [switch]$Recurse,
        [Alias('Silent')]
        [switch]$EnableException
    )

    Write-Message -Level Verbose -Message "Starting"
    Write-Message -Level Verbose -Message "Checking Path"
    if ((Test-Path $Path) -ne $true) {
        Stop-Function -Message "$Path is not reachable"
        return
    }
    #Path needs to end \* to use includes, which is faster than Where-Object
    $PathCheckArray = $path.ToCharArray()
    if ($PathCheckArray[-2] -eq '\' -and $PathCheckArray[-1] -eq '*') {
        #We're good
    }
    elseif ($PathCheckArray[-2] -ne '\' -and $PathCheckArray[-1] -eq '*') {
        $Path = ($PathCheckArray[0..(($PathCheckArray.length) - 2)] -join ('')) + "\*"
    }
    elseif ($PathCheckArray[-2] -eq '\' -and $PathCheckArray[-1] -ne '*') {
        #Append a * to the end
        $Path = "$Path*"
    }
    elseif ($PathCheckArray[-2] -ne '\' -and $PathCheckArray[-1] -ne '*') {
        #Append a \* to the end
        $Path = "$Path\*"
    }
    Write-Message -Level Verbose -Message "Scanning $path"
    $Results = Get-ChildItem -path $Path -Recurse:$Recurse | Where-Object {$_.PsIsContainer -eq $false}
    return $Results
}
tools\dbatools\internal\functions\Get-ErrorMessage.ps1
function Get-ErrorMessage {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [System.Management.Automation.ErrorRecord]$Record
    )
    process {
        $innermessage = $Record.Exception.InnerException.InnerException.InnerException.InnerException.InnerException.Message
        if (-not $innermessage) { $innermessage = $Record.Exception.InnerException.InnerException.InnerException.InnerException.Message }
        if (-not $innermessage) { $innermessage = $Record.Exception.InnerException.InnerException.InnerException.Message }
        if (-not $innermessage) { $innermessage = $Record.Exception.InnerException.InnerException.Message }
        if (-not $innermessage) { $innermessage = $Record.Exception.InnerException.Message }
        if (-not $innermessage) { $innermessage = $Record.Exception.Message }
        return $innermessage
    }
}
tools\dbatools\internal\functions\Get-JobList.ps1
function Get-JobList {
    <#
    .SYNOPSIS
        Helper function to get SQL Agent jobs.
    .DESCRIPTION
        Helper function to get all SQL Agent jobs or provide filter
    .PARAMETER SqlInstance
        SQL Server instance
    .PARAMETER SqlCredential
        Credential to use if SqlInstance did not include it.
    .PARAMETER JobFilter
        Object of jobs to filter on, also supports wildcard patterns
    .PARAMETER StepFilter
        Object of job steps to filter on, also supports wildcard patterns
    .PARAMETER Not
        Reverse results where object returned excludes filtered content.
    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .EXAMPLE
        Get-JobList -SqlInstance sql2016

        Returns the full JobServer.Jobs object found on sql2016
    .EXAMPLE
        Get-JobList -SqlInstance sql2016 -JobFilter '*job*'

        Returns the Job object for each job name found to have "job" in the name on sql2016
    .EXAMPLE
        Get-JobList -SqlInstance sql2016 -JobFilter '*job*' -Not

        Returns any Job object that does not have "job" in the name on sql2016
    .EXAMPLE
        Get-JobList -SqlInstance YourServer -JobFilter 'JobName'

        Returns the Job object where the job name is 'JobName' on sql2016
    .EXAMPLE
        Get-JobList -SqlInstance YourServer -JobFilter 'JobName' -Not

        Returns any Job object where the job name is not 'JobName' on sql2016
    .EXAMPLE
        Get-JobList -SqlInstance YourServer -JobFilter job_3_upload, job_3_download

        Returns the Job object for where job is job_3_upload or job_3_download on sql2016
    .EXAMPLE
        Get-JobList -SqlInstance YourServer -JobFilter job_3_upload, job_3_download -Not

        Returns any Job object where job is not job_3_upload or job_3_download on sql2016
    .NOTES
        Author: Shawn Melton (@wsmelton)

        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT
    #>
    [cmdletbinding()]
    param(
        [Parameter(ValueFromPipeline = $true)]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string[]]$JobFilter,
        [string[]]$StepFilter,
        [switch]$Not,
        [Alias('Silent')]
        [switch]$EnableException
    )
    process {
        $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential

        $jobs = $server.JobServer.Jobs
        if ( (Test-Bound 'JobFilter') -or (Test-Bound 'StepFilter') ) {
            if ($JobFilter.Count -gt 1) {
                if ($Not) {
                    $jobs | Where-Object Name -NotIn $JobFilter
                }
                else {
                    $jobs | Where-Object Name -In $JobFilter
                }
            }
            else {
                foreach ($job in $jobs) {
                    if ($JobFilter -match '`*') {
                        if ($Not) {
                            $job | Where-Object Name -NotLike $JobFilter
                        }
                        else {
                            $job | Where-Object Name -Like $JobFilter
                        }
                    }
                    else {
                        if ($Not) {
                            $job | Where-Object Name -NE $JobFilter
                        }
                        else {
                            $job | Where-Object Name -EQ $JobFilter
                        }
                    }
                    if ($StepFilter -match '`*') {
                        if ($Not) {
                            $stepFound = $job.JobSteps | Where-Object Name -NotLike $StepFilter
                            if ($stepFound.Count -gt 0) {
                                $job
                            }
                        }
                        else {
                            $stepFound = $job.JobSteps | Where-Object Name -Like $StepFilter
                            if ($stepFound.Count -gt 0) {
                                $job
                            }
                        }
                    }
                    elseif ($StepName.Count -gt 1) {
                        if ($Not) {
                            $stepFound = $job.JobSteps | Where-Object Name -NotIn $StepName
                            if ($stepFound.Count -gt 0) {
                                $job
                            }
                        }
                        else {
                            $stepFound = $job.JobSteps | Where-Object Name -In $StepName
                            if ($stepFound.Count -gt 0) {
                                $job
                            }
                        }
                    }
                    else {
                        if ($Not) {
                            $stepFound = $job.JobSteps | Where-Object Name -NE $StepName
                            if ($stepFound.Count -gt 0) {
                                $job
                            }
                        }
                        else {
                            $stepFound = $job.JobSteps | Where-Object Name -EQ $StepName
                            if ($stepFound.Count -gt 0) {
                                $job
                            }
                        }
                    }
                }
            }
        }
        else {
            $jobs
        }
    }
}
tools\dbatools\internal\functions\Get-Language.ps1
function Get-Language {
    <#
        .SYNOPSIS
            Converts Microsoft's language ID to human readable format

        .DESCRIPTION
            Converts Microsoft's language ID to human readable format

        .PARAMETER Id
            The language ID

        .EXAMPLE
            Get-Language 1033

            Returns a pscustomobject with id, alias and name
    #>
    [CmdletBinding()]
    param (
        [int]$id
    )
    process {

        $culture = [System.Globalization.CultureInfo]::GetCultureInfo($id)

        $excludeProps = 'Parent', 'IetfLanguageTag', 'CompareInfo', 'TextInfo', 'IsNeutralCulture', 'NumberFormat', 'DateTimeFormat', 'Calendar'
        , 'OptionalCalendars', 'UseUserOverride', 'IsReadOnly'
        Select-DefaultView -InputObject $culture -ExcludeProperty $excludeProps
    }
}
tools\dbatools\internal\functions\Get-OfflineSqlFileStructure.ps1
function Get-OfflineSqlFileStructure {
    <#
.SYNOPSIS
Internal function. Returns dictionary object that contains file structures for SQL databases.

#>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [object]$SqlInstance,
        [Parameter(Mandatory = $true, Position = 1)]
        [string]$dbname,
        [Parameter(Mandatory = $true, Position = 2)]
        [object]$filelist,
        [Parameter(Mandatory = $false, Position = 3)]
        [bool]$ReuseSourceFolderStructure,
        [PSCredential]$SqlCredential
    )

    $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential

    $destinationfiles = @{ };
    $logfiles = $filelist | Where-Object { $_.Type -eq "L" }
    $datafiles = $filelist | Where-Object { $_.Type -ne "L" }
    $filestream = $filelist | Where-Object { $_.Type -eq "S" }

    if ($filestream) {
        $sql = "select coalesce(SERVERPROPERTY('FilestreamConfiguredLevel'),0) as fs"
        $fscheck = $server.databases['master'].ExecuteWithResults($sql)
        if ($fscheck.tables.fs -eq 0) { return $false }
    }

    # Data Files
    foreach ($file in $datafiles) {
        # Destination File Structure
        $d = @{ }
        if ($ReuseSourceFolderStructure -eq $true) {
            $d.physical = $file.PhysicalName
        }
        else {
            $directory = Get-SqlDefaultPaths $server data
            $filename = Split-Path $($file.PhysicalName) -leaf
            $d.physical = "$directory\$filename"
        }

        $d.logical = $file.LogicalName
        $destinationfiles.add($file.LogicalName, $d)
    }

    # Log Files
    foreach ($file in $logfiles) {
        $d = @{ }
        if ($ReuseSourceFolderStructure) {
            $d.physical = $file.PhysicalName
        }
        else {
            $directory = Get-SqlDefaultPaths $server log
            $filename = Split-Path $($file.PhysicalName) -leaf
            $d.physical = "$directory\$filename"
        }

        $d.logical = $file.LogicalName
        $destinationfiles.add($file.LogicalName, $d)
    }

    return $destinationfiles
}
tools\dbatools\internal\functions\Get-PasswordHash.ps1
function Get-PasswordHash {
    <#
    .SYNOPSIS
    Generates a password hash for SQL Server login

    .DESCRIPTION
    Generates a hash string based on the plaintext or securestring password and a SQL Server version. Salt is optional

    .PARAMETER Password
    Either plain text or Securestring password

    .PARAMETER SqlMajorVersion
    Major version of the SQL Server. Defines the hash algorithm.

    .PARAMETER byteSalt
    Optional. Inserts custom salt into the hash instead of randomly generating new salt

    .NOTES
    Tags: Login, Internal
    Author: Kirill Kravtsov (@nvarscar)
    dbatools PowerShell module (https://dbatools.io, [email protected])
    Copyright (C) 2016 Chrissy LeMaire
    License: MIT https://opensource.org/licenses/MIT

    .EXAMPLE
    Get-PasswordHash $securePassword 11

    Generates password hash for SQL 2012

    .EXAMPLE
    Get-PasswordHash $securePassword 9 $byte

    Generates password hash for SQL 2005 using custom salt from the $byte variable

#>
    param (
        [object]$Password,
        $SqlMajorVersion,
        [byte[]]$byteSalt
    )
    #Choose hash algorithm
    if ($SqlMajorVersion -lt 11) {
        $algorithm = 'SHA1'
        $hashVersion = '0100'
    }
    else {
        $algorithm = 'SHA512'
        $hashVersion = '0200'
    }

    #Generate salt
    if (!$byteSalt) {
        0 .. 3 | ForEach-Object { $byteSalt += Get-Random -Minimum 0 -Maximum 255 }
    }

    #Convert salt to a hex string
    [string]$stringSalt = ""
    $byteSalt | ForEach-Object { $stringSalt += ("{0:X}" -f $_).PadLeft(2, "0") }

    #Extract password
    if ($Password.GetType().Name -eq 'SecureString') {
        $cred = New-Object System.Management.Automation.PSCredential -ArgumentList 'foo', $Password
        $plainPassword = $cred.GetNetworkCredential().Password
    }
    else {
        $plainPassword = $Password
    }
    #Get byte representation of the password string
    $enc = [system.Text.Encoding]::Unicode
    $data = $enc.GetBytes($plainPassword)
    #Run hash algorithm
    $hash = [Security.Cryptography.HashAlgorithm]::Create($algorithm)
    $bytes = $hash.ComputeHash($data + $byteSalt)
    #Construct hex string
    $hashString = "0x$hashVersion$stringSalt"
    $bytes | ForEach-Object { $hashString += ("{0:X2}" -f $_).PadLeft(2, "0") }
    #Add UPPERCASE hash for SQL 2000 and lower
    if ($SqlMajorVersion -lt 9) {
        $data = $enc.GetBytes($plainPassword.ToUpper())
        $bytes = $hash.ComputeHash($data + $byteSalt)
        $bytes | ForEach-Object { $hashString += ("{0:X2}" -f $_).PadLeft(2, "0") }
    }
    return $hashString
}
tools\dbatools\internal\functions\Get-RegServerGroupReverseParse.ps1
function Get-RegServerGroupReverseParse ($object) {
    if ($object.Name -eq 'DatabaseEngineServerGroup') {
        $object.Name
    }
    else {
        $name = @()
        do {
            $name += $object.Name.Split("\")[0]
            $object = $object.Parent
        }
        until ($object.Name -eq 'DatabaseEngineServerGroup')
        
        [array]::Reverse($name)
        $name -join '\'
    }
}
tools\dbatools\internal\functions\Get-RegServerParent.ps1
function Get-RegServerParent {
    [cmdletbinding()]
    param (
        [object]$InputObject
    )
    process {
        $parentcount = 0
        do {
            if ($null -ne $InputObject.Parent) {
                $InputObject = $InputObject.Parent
            }
        }
        until ($null -ne $InputObject.ServerConnection -or $parentcount++ -gt 10)
        
        
        if ($parentcount -lt 10) {
            $InputObject
        }
    }
}
tools\dbatools\internal\functions\Get-RestoreContinuableDatabase.ps1
function Get-RestoreContinuableDatabase {
    <#
    .SYNOPSIS
    Gets a list of databases from a SQL instance that are in a state for further restores

    .DESCRIPTION
    Takes a SQL instance and checks for databases with a redo_start_lsn value, and returns the database name and that value
    -gt SQl 2005 it comes from master.sys.master_files
    -eq SQL 2000 DBCC DBINFO
#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [object]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    try {
        $Server = Connect-SqlInstance -Sqlinstance $SqlInstance -SqlCredential $SqlCredential
    }
    catch {
        Write-Message -Level Warning -Message "Cannot connect to $SqlInstance"
        break
    }
    if ($Server.VersionMajor -ge 9) {
        $sql = "select distinct db_name(database_id) as 'Database', redo_start_lsn, redo_start_fork_guid as 'FirstRecoveryForkID' from master.sys.master_files where redo_start_lsn is not NULL"
    }
    else {
        $sql = "
              CREATE TABLE #db_info
                (
                ParentObject NVARCHAR(128) COLLATE database_default ,
                Object       NVARCHAR(128) COLLATE database_default,
                Field        NVARCHAR(128) COLLATE database_default,
                Value        SQL_VARIANT
                )"
    }
    $server.ConnectionContext.ExecuteWithResults($sql).Tables.Rows
}
tools\dbatools\internal\functions\Get-SaLoginName.ps1
function Get-SaLoginName {
    <#
    .SYNOPSIS
    Gets the login matching the standard "sa" user

    .DESCRIPTION
    Gets the login matching the standard "sa" user, useful in case of renames

    .PARAMETER SqlInstance
    The SQL Server instance.

    .PARAMETER SqlCredential
    Allows you to login to servers using SQL Logins instead of Windows Authentication (AKA Integrated or Trusted).

    .EXAMPLE
    Get-SaLoginName -SqlInstance base\sql2016

    .NOTES
        Website: https://dbatools.io
        Copyright: (C) Chrissy LeMaire, [email protected]
        License: MIT https://opensource.org/licenses/MIT
    #>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [object]$SqlInstance,
        [PSCredential]$SqlCredential
    )

    $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
    $saname = ($server.logins | Where-Object { $_.id -eq 1 }).Name

    return $saname
}
tools\dbatools\internal\functions\Get-SmoServerForDynamicParams.ps1
function Get-SmoServerForDynamicParams {
    ##############################
    # THIS DOES NOT SEEM TO BE USED
    ##############################
    if ($fakeBoundParameter.length -eq 0) { return }

    $SqlInstance = $fakeBoundParameter['SqlInstance']
    $sqlcredential = $fakeBoundParameter['SqlCredential']

    if ($null -eq $SqlInstance) {
        $SqlInstance = $fakeBoundParameter['sqlinstance']
    }
    if ($null -eq $SqlInstance) {
        $SqlInstance = $fakeBoundParameter['source']
    }
    if ($null -eq $sqlcredential) {
        $sqlcredential = $fakeBoundParameter['Credential']
    }

    if ($SqlInstance) {
        Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential -ParameterConnection
    }
}
tools\dbatools\internal\functions\Get-SqlCmdVars.ps1
function Get-SqlCmdVars {
    <#
        .SYNOPSIS
            Retrieves the values of PowerShell parameters and updates values of SqlmdVars listed in the publish.xml.

        .DESCRIPTION
            Attempt to resolve SQLCmd variables via matching powershell variables explicitly defined in the current context.
            To try and avoid 'bad' default values getting deployed, block a deployment if we have SqlCmd variables that aren't defined in current context.
            Function has one reference and is executed when the "getSqlCmdVars" switch is included.
        .PARAMETER SqlCommandVariableValues
            Mandatory. The SqlCommandVariableValues from the DeployOptions property in the Microsoft.SqlServer.Dac.DacProfile
        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Richie lee (@bzzzt_io)

            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT
        .LINK
            https://dbatools.io/Test-Noun

        .EXAMPLE
        Imagine content of MyDbProject.publish.xml is as follows -
        <?xml version="1.0" encoding="utf-8"?>
        <Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
        <PropertyGroup>
            <IncludeCompositeObjects>True</IncludeCompositeObjects>
            <TargetDatabaseName>MyDbProject</TargetDatabaseName>
            <DeployScriptFileName>MyDbProject.sql</DeployScriptFileName>
            <TargetConnectionString>Data Source=.;Integrated Security=True;Persist Security Info=False;Pooling=False;MultipleActiveResultSets=False;Connect Timeout=60;Encrypt=False;TrustServerCertificate=True</TargetConnectionString>
            <BlockOnPossibleDataLoss>True</BlockOnPossibleDataLoss>
            <CreateNewDatabase>False</CreateNewDatabase>
            <ProfileVersionNumber>1</ProfileVersionNumber>
        </PropertyGroup>
        <ItemGroup>
            <SqlCmdVariable Include="DeployTag">
            <Value>OldValue</Value>
            </SqlCmdVariable>
        </ItemGroup>
        </Project>
        We will need one PowerShell parameter named $DeployTag to update the value

        The following scenario will fail as no $deployTag -
        "
            $publishXml =  "C:\MyDbProject\bin\Debug\MyDbProject.publish.xml"
            $dacProfile = [Microsoft.SqlServer.Dac.DacProfile]::Load($publishXml)
            Get-SqlCmdVars $dacProfile.DeployOptions.SqlCommandVariableValues -EnableException
        "
        This scenario will pass.
        "
            $deployTag = "NewValue"
            $publishXml =  "C:\MyDbProject\bin\Debug\MyDbProject.publish.xml"
            $dacProfile = [Microsoft.SqlServer.Dac.DacProfile]::Load($publishXml)
            Get-SqlCmdVars $dacProfile.DeployOptions.SqlCommandVariableValues -EnableException
        "
    #>
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        $SqlCommandVariableValues,
        [switch]$EnableException
    )
    $missingVariables = @()
    $keys = $($SqlCommandVariableValues.Keys)
    foreach ($var in $keys) {
        if (Test-Path variable:$var) {
            $value = Get-Variable $var -ValueOnly
            $SqlCommandVariableValues[$var] = $value
        }
        else {
            $missingVariables += $var
        }
    }
    if ($missingVariables.Count -gt 0) {
        $errorMsg = 'The following SqlCmd variables are not defined in the session (but are defined in the publish profile): {0}' -f ($missingVariables -join " `n")
        Stop-Function -Message $errorMsg -EnableException $EnableException
    }
}
tools\dbatools\internal\functions\Get-SqlDefaultPaths.ps1
function Get-SqlDefaultPaths {
    <#
    .SYNOPSIS
        Internal function. Returns the default data and log paths for SQL Server. Needed because SMO's server.defaultpath is sometimes null.
#>
    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "")]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [Alias("ServerInstance", "SqlServer")]
        [object]$SqlInstance,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$filetype,
        [PSCredential]$SqlCredential
    )

    try {
        if ($SqlInstance -isnot [Microsoft.SqlServer.Management.Smo.SqlSmoObject]) {
            $Server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
        }
        else {
            $server = $SqlInstance
        }
    }
    catch {
        Write-Message -Lvel Warning -Message "Cannot connect to $SqlInstance"
        break
    }
    switch ($filetype) { "mdf" { $filetype = "data" } "ldf" { $filetype = "log" } }

    if ($filetype -eq "log") {
        # First attempt
        $filepath = $server.DefaultLog
        # Second attempt
        if ($filepath.Length -eq 0) { $filepath = $server.Information.MasterDbLogPath }
        # Third attempt
        if ($filepath.Length -eq 0) {
            $sql = "select SERVERPROPERTY('InstanceDefaultLogPath') as physical_name"
            $filepath = $server.ConnectionContext.ExecuteScalar($sql)
        }
    }
    else {
        # First attempt
        $filepath = $server.DefaultFile
        # Second attempt
        if ($filepath.Length -eq 0) { $filepath = $server.Information.MasterDbPath }
        # Third attempt
        if ($filepath.Length -eq 0) {
            $sql = "select SERVERPROPERTY('InstanceDefaultDataPath') as physical_name"
            $filepath = $server.ConnectionContext.ExecuteScalar($sql)
        }
    }

    if ($filepath.Length -eq 0) { throw "Cannot determine the required directory path" }
    $filepath = $filepath.TrimEnd("\")
    return $filepath
}
tools\dbatools\internal\functions\Get-SqlDefaultSpConfigure.ps1
function Get-SqlDefaultSpConfigure {
    <#
        .SYNOPSIS
        Internal function. Returns the default sp_configure options for a given version of SQL Server.

        .NOTES
        Server Configuration Options BOL (links subject to change):
        SQL Server 2017 - https://technet.microsoft.com/en-us/library/ms189631(v=sql.140).aspx
        SQL Server 2016 - https://technet.microsoft.com/en-us/library/ms189631(v=sql.130).aspx
        SQL Server 2014 - http://technet.microsoft.com/en-us/library/ms189631(v=sql.120).aspx
        SQL Server 2012 - http://technet.microsoft.com/en-us/library/ms189631(v=sql.110).aspx
        SQL Server 2008 R2 - http://technet.microsoft.com/en-us/library/ms189631(v=sql.105).aspx
        SQL Server 2008 - http://technet.microsoft.com/en-us/library/ms189631(v=sql.100).aspx
        SQL Server 2005 - http://technet.microsoft.com/en-us/library/ms189631(v=sql.90).aspx
        SQL Server 2000 - http://technet.microsoft.com/en-us/library/aa196706(v=sql.80).aspx (requires PDF download)

        .EXAMPLE
        Get-SqlDefaultSpConfigure -SqlVersion 11
        Returns a list of sp_configure (sys.configurations) items for SQL 2012.

#>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [Alias("Version")]
        [object]$SqlVersion
    )

    switch ($SqlVersion) {

        #region SQL2000
        8 {
            [pscustomobject]@{
                "affinity mask"                  = 0
                "allow updates"                  = 0
                "aweenabled"                     = 0
                "c2 audit mode"                  = 0
                "cost threshold for parallelism" = 5
                "Cross DB Ownership Chaining"    = 0
                "cursor threshold"               = -1
                "default full-text language"     = 1033
                "default language"               = 0
                "fill factor (%)"                = 0
                "index create memory (KB)"       = 0
                "lightweight pooling"            = 0
                "locks"                          = 0
                "max degree of parallelism"      = 0
                "max server memory (MB)"         = 2147483647
                "max text repl size (B)"         = 65536
                "max worker threads"             = 255
                "media retention"                = 0
                "min memory per query (KB)"      = 1024
                "min server memory (MB)"         = 0
                "Using Nested Triggers"          = 1
                "network packet size (B)"        = 4096
                "open objects"                   = 0
                "priority boost"                 = 0
                "query governor cost limit"      = 0
                "query wait (s)"                 = -1
                "recovery interval (min)"        = 0
                "remoteaccess"                   = 1
                "remotelogin timeout"            = 20
                "remote proc trans"              = 0
                "remote query timeout (s)"       = 600
                "scan for startup procs"         = 0
                "set working set size"           = 0
                "show advanced options"          = 0
                "two digityear cutoff"           = 2049
                "user connections"               = 0
                "user options"                   = 0
            }
        }
        #endregion SQL2000

        #region SQL2005
        9 {
            [pscustomobject]@{
                "Ad Hoc Distributed Queries"         = 0
                "affinity I/O mask"                  = 0
                "affinity64 I/O mask"                = 0
                "affinity mask"                      = 0
                "affinity64 mask"                    = 0
                "Agent XPs"                          = 0
                "allow updates"                      = 0
                "awe enabled"                        = 0
                "blocked process threshold (s)"      = 0
                "c2 audit mode"                      = 0
                "clr enabled"                        = 0
                "common criteria compliance enabled" = 0
                "cost threshold for parallelism"     = 5
                "cross db ownership chaining"        = 0
                "cursor threshold"                   = -1
                "Database Mail XPs"                  = 0
                "default full-text language"         = 1033
                "default language"                   = 0
                "default trace enabled"              = 1
                "disallow results from triggers"     = 0
                "fill factor (%)"                    = 0
                "ft crawl bandwidth (max)"           = 100
                "ft crawl bandwidth (min)"           = 0
                "ft notify bandwidth (max)"          = 100
                "ft notify bandwidth (min)"          = 0
                "index create memory (KB)"           = 0
                "in-doubt xact resolution"           = 0
                "lightweight pooling"                = 0
                "locks"                              = 0
                "max degree of parallelism"          = 0
                "max full-text crawl range"          = 4
                "max server memory (MB)"             = 2147483647
                "max text repl size (B)"             = 65536
                "max worker threads"                 = 0
                "media retention"                    = 0
                "min memory per query (KB)"          = 1024
                "min server memory (MB)"             = 8
                "nested triggers"                    = 1
                "network packet size (B)"            = 4096
                "Ole Automation Procedures"          = 0
                "open objects"                       = 0
                "PH timeout (s)"                     = 60
                "precompute rank"                    = 0
                "priority boost"                     = 0
                "query governor cost limit"          = 0
                "query wait (s)"                     = -1
                "recovery interval (min)"            = 0
                "remote access"                      = 1
                "remote admin connections"           = 0
                "remote login timeout (s)"           = 20
                "remote proc trans"                  = 0
                "remote query timeout (s)"           = 600
                "Replication XPs"                    = 0
                "scan for startup procs"             = 0
                "server trigger recursion"           = 1
                "set working set size"               = 0
                "show advanced options"              = 0
                "SMO and DMO XPs"                    = 1
                "SQL Mail XPs"                       = 0
                "transform noise words"              = 0
                "two digit year cutoff"              = 2049
                "user connections"                   = 0
                "User Instance Timeout"              = 60
                "user instances enabled"             = 0
                "user options"                       = 0
                "Web Assistant Procedures"           = 0
                "xp_cmdshell"                        = 0
            }
        }

        #endregion SQL2005

        #region SQL2008&2008R2
        10 {
            [pscustomobject]@{
                "access check cache bucket count"    = 0
                "access check cache quota"           = 0
                "ad hoc distributed queries"         = 0
                "affinity I/O mask"                  = 0
                "affinity64 I/O mask"                = 0
                "affinity mask"                      = 0
                "affinity64 mask"                    = 0
                "Agent XPs"                          = 0
                "allow updates"                      = 0
                "awe enabled"                        = 0
                "backup compression default"         = 0
                "blocked process threshold (s)"      = 0
                "c2 audit mode"                      = 0
                "clr enabled"                        = 0
                "common criteria compliance enabled" = 0
                "cost threshold for parallelism"     = 5
                "cross db ownership chaining"        = 0
                "cursor threshold"                   = -1
                "Database Mail XPs"                  = 0
                "default full-text language"         = 1033
                "default language"                   = 0
                "default trace enabled"              = 1
                "disallow results from triggers"     = 0
                "EKM provider enabled"               = 0
                "filestream access level"            = 0
                "fill factor (%)"                    = 0
                "ft crawl bandwidth (max)"           = 100
                "ft crawl bandwidth (min)"           = 0
                "ft notify bandwidth (max)"          = 100
                "ft notify bandwidth (min)"          = 0
                "index create memory (KB)"           = 0
                "in-doubt xact resolution"           = 0
                "lightweight pooling"                = 0
                "locks"                              = 0
                "max degree of parallelism"          = 0
                "max full-text crawl range"          = 4
                "max server memory (MB)"             = 2147483647
                "max text repl size (B)"             = 65536
                "max worker threads"                 = 0
                "media retention"                    = 0
                "min memory per query (KB)"          = 1024
                "min server memory (MB)"             = 0
                "nested triggers"                    = 1
                "network packet size (B)"            = 4096
                "Ole Automation Procedures"          = 0
                "open objects"                       = 0
                "optimize for ad hoc workloads"      = 0
                "PH timeout (s)"                     = 60
                "precompute rank"                    = 0
                "priority boost"                     = 0
                "query governor cost limit"          = 0
                "query wait (s)"                     = -1
                "recovery interval (min)"            = 0
                "remote access"                      = 1
                "remote admin connections"           = 0
                "remote login timeout (s)"           = 20
                "remote proc trans"                  = 0
                "remote query timeout (s)"           = 600
                "Replication XPs"                    = 0
                "scan for startup procs"             = 0
                "server trigger recursion"           = 1
                "set working set size"               = 0
                "show advanced options"              = 0
                "SMO and DMO XPs"                    = 1
                "SQL Mail XPs"                       = 0
                "transform noise words"              = 0
                "two digit year cutoff"              = 2049
                "user connections"                   = 0
                "User Instance Timeout"              = 60
                "user instances enabled"             = 0
                "user options"                       = 0
                "xp_cmdshell"                        = 0
            }
        }
        #endregion SQL2008&2008R2

        #region SQL2012
        11 {
            [pscustomobject]@{
                "access check cache bucket count"    = 0
                "access check cache quota"           = 0
                "ad hoc distributed queries"         = 0
                "affinity I/O mask"                  = 0
                "affinity64 I/O mask"                = 0
                "affinity mask"                      = 0
                "affinity64 mask"                    = 0
                "Agent XPs"                          = 0
                "allow updates"                      = 0
                "backup compression default"         = 0
                "blocked process threshold (s)"      = 0
                "c2 audit mode"                      = 0
                "clr enabled"                        = 0
                "common criteria compliance enabled" = 0
                "contained database authentication"  = 0
                "cost threshold for parallelism"     = 5
                "cross db ownership chaining"        = 0
                "cursor threshold"                   = -1
                "Database Mail XPs"                  = 0
                "default full-text language"         = 1033
                "default language"                   = 0
                "default trace enabled"              = 1
                "disallow results from triggers"     = 0
                "EKM provider enabled"               = 0
                "filestream access level"            = 0
                "fill factor (%)"                    = 0
                "ft crawl bandwidth (max)"           = 100
                "ft crawl bandwidth (min)"           = 0
                "ft notify bandwidth (max)"          = 100
                "ft notify bandwidth (min)"          = 0
                "index create memory (KB)"           = 0
                "in-doubt xact resolution"           = 0
                "lightweight pooling"                = 0
                "locks"                              = 0
                "max degree of parallelism"          = 0
                "max full-text crawl range"          = 4
                "max server memory (MB)"             = 2147483647
                "max text repl size (B)"             = 65536
                "max worker threads"                 = 0
                "media retention"                    = 0
                "min memory per query (KB)"          = 1024
                "min server memory (MB)"             = 0
                "nested triggers"                    = 1
                "network packet size (B)"            = 4096
                "Ole Automation Procedures"          = 0
                "open objects"                       = 0
                "optimize for ad hoc workloads"      = 0
                "PH_timeou"                          = 60
                "precompute rank"                    = 0
                "priority boost"                     = 0
                "query governor cost limit"          = 0
                "query wait (s)"                     = -1
                "recovery interval (min)"            = 0
                "remote access"                      = 1
                "remote admin connections"           = 0
                "remote login timeout (s)"           = 10
                "remote proc trans"                  = 0
                "remote query timeout (s)"           = 600
                "Replication XPs"                    = 0
                "scan for startup procs"             = 0
                "server trigger recursion"           = 1
                "set working set size"               = 0
                "show advanced options"              = 0
                "SMO and DMO XPs"                    = 1
                "transform noise words"              = 0
                "two digit year cutoff"              = 2049
                "user connections"                   = 0
                "user options"                       = 0
                "xp_cmdshell"                        = 0
            }
        }
        #endregion SQL2012

        #region SQL2014
        12 {
            [pscustomobject]@{
                "access check cache bucket count"    = 0
                "access check cache quota"           = 0
                "ad hoc distributed queries"         = 0
                "affinity I/O mask"                  = 0
                "affinity64 I/O mask"                = 0
                "affinity mask"                      = 0
                "affinity64 mask"                    = 0
                "Agent XPs"                          = 0
                "allow updates"                      = 0
                "backup checksum default"            = 0
                "backup compression default"         = 0
                "blocked process threshold (s)"      = 0
                "c2 audit mode"                      = 0
                "clr enabled"                        = 0
                "common criteria compliance enabled" = 0
                "contained database authentication"  = 0
                "cost threshold for parallelism"     = 5
                "cross db ownership chaining"        = 0
                "cursor threshold"                   = -1
                "Database Mail XPs"                  = 0
                "default full-text language"         = 1033
                "default language"                   = 0
                "default trace enabled"              = 1
                "disallow results from triggers"     = 0
                "EKM provider enabled"               = 0
                "filestream access level"            = 0
                "fill factor (%)"                    = 0
                "ft crawl bandwidth (max)"           = 100
                "ft crawl bandwidth (min)"           = 0
                "ft notify bandwidth (max)"          = 100
                "ft notify bandwidth (min)"          = 0
                "index create memory (KB)"           = 0
                "in-doubt xact resolution"           = 0
                "lightweight pooling"                = 0
                "locks"                              = 0
                "max degree of parallelism"          = 0
                "max full-text crawl range"          = 4
                "max server memory (MB)"             = 2147483647
                "max text repl size (B)"             = 65536
                "max worker threads"                 = 0
                "media retention"                    = 0
                "min memory per query (KB)"          = 1024
                "min server memory (MB)"             = 0
                "nested triggers"                    = 1
                "network packet size (B)"            = 4096
                "Ole Automation Procedures"          = 0
                "open objects"                       = 0
                "optimize for ad hoc workloads"      = 0
                "PH timeout (s)"                     = 60
                "precompute rank"                    = 0
                "priority boost"                     = 0
                "query governor cost limit"          = 0
                "query wait (s)"                     = -1
                "recovery interval (min)"            = 0
                "remote access"                      = 1
                "remote admin connections"           = 0
                "remote login timeout (s)"           = 10
                "remote proc trans"                  = 0
                "remote query timeout (s)"           = 600
                "Replication XPs"                    = 0
                "scan for startup procs"             = 0
                "server trigger recursion"           = 1
                "set working set size"               = 0
                "show advanced options"              = 0
                "SMO and DMO XPs"                    = 1
                "transform noise words"              = 0
                "two digit year cutoff"              = 2049
                "user connections"                   = 0
                "user options"                       = 0
                "xp_cmdshell"                        = 0
            }
        }
        #endregion SQL2014

        #region SQL2016
        13 {
            [pscustomobject]@{
                "access check cache bucket count"        = 0
                "access check cache quota"               = 0
                "ad hoc distributed queries"             = 0
                "affinity I/O mask"                      = 0
                "affinity64 I/O mask"                    = 0
                "affinity mask"                          = 0
                "affinity64 mask"                        = 0
                "Agent XPs"                              = 0
                "allow updates"                          = 0
                "automatic soft-NUMA disabled"           = 0
                "backup checksum default"                = 0
                "backup compression default"             = 0
                "blocked process threshold (s)"          = 0
                "c2 audit mode"                          = 0
                "clr enabled"                            = 0
                "common criteria compliance enabled"     = 0
                "contained database authentication"      = 0
                "cost threshold for parallelism"         = 5
                "cross db ownership chaining"            = 0
                "cursor threshold"                       = -1
                "Database Mail XPs"                      = 0
                "default full-text language"             = 1033
                "default language"                       = 0
                "default trace enabled"                  = 1
                "disallow results from triggers"         = 0
                "EKM provider enabled"                   = 0
                "external scripts enabled"               = 0
                "filestream access level"                = 0
                "fill factor (%)"                        = 0
                "ft crawl bandwidth (max)"               = 100
                "ft crawl bandwidth (min)"               = 0
                "ft notify bandwidth (max)"              = 100
                "ft notify bandwidth (min)"              = 0
                "index create memory (KB)"               = 0
                "in-doubt xact resolution"               = 0
                "lightweight pooling"                    = 0
                "locks"                                  = 0
                "max degree of parallelism"              = 0
                "max full-text crawl range"              = 4
                "max server memory (MB)"                 = 2147483647
                "max text repl size (B)"                 = 65536
                "max worker threads"                     = 0
                "media retention"                        = 0
                "min memory per query (KB)"              = 1024
                "min server memory (MB)"                 = 0
                "nested triggers"                        = 1
                "network packet size (B)"                = 4096
                "Ole Automation Procedures"              = 0
                "open objects"                           = 0
                "optimize for ad hoc workloads"          = 0
                "PH timeout (s)"                         = 60
                "PolyBase Hadoop and Azure blob storage" = 0
                "precompute rank"                        = 0
                "priority boost"                         = 0
                "query governor cost limit"              = 0
                "query wait (s)"                         = -1
                "recovery interval (min)"                = 0
                "remote access"                          = 1
                "remote admin connections"               = 0
                "remote data archive"                    = 0
                "remote login timeout (s)"               = 10
                "remote proc trans"                      = 0
                "remote query timeout (s)"               = 0
                "Replication XPs"                        = 0
                "scan for startup procs"                 = 0
                "server trigger recursion"               = 1
                "set working set size"                   = 0
                "show advanced options"                  = 0
                "SMO and DMO XPs"                        = 1
                "transform noise words"                  = 0
                "two digit year cutoff"                  = 2049
                "user connections"                       = 0
                "user options"                           = 0
                "xp_cmdshell"                            = 0
            }
        }
        #endregion SQL2016

        #region SQL2017
        14 {
            [pscustomobject]@{
                "access check cache bucket count"    = 0
                "access check cache quota"           = 0
                "Ad Hoc Distributed Queries"         = 0
                "affinity I / O mask"                = 0
                "affinity mask"                      = 0
                "affinity64 I / O mask"              = 0
                "affinity64 mask"                    = 0
                "Agent XPs"                          = 0
                "allow polybase export"              = 0
                "allow updates"                      = 0
                "automatic soft-NUMA disabled"       = 0
                "backup checksum default"            = 0
                "backup compression default"         = 0
                "blocked process threshold (s)"      = 0
                "c2 audit mode"                      = 0
                "clr enabled"                        = 0
                "clr strict security"                = 1
                "common criteria compliance enabled" = 0
                "contained database authentication"  = 0
                "cost threshold for parallelism"     = 5
                "cross db ownership chaining"        = 0
                "cursor threshold"                   = -1
                "Database Mail XPs"                  = 0
                "default full-text language"         = 1033
                "default language"                   = 0
                "default trace enabled"              = 1
                "disallow results from triggers"     = 0
                "EKM provider enabled"               = 0
                "external scripts enabled"           = 0
                "filestream access level"            = 0
                "fill factor ( % )"                  = 0
                "ft crawl bandwidth (max)"           = 100
                "ft crawl bandwidth (min)"           = 0
                "ft notify bandwidth (max)"          = 100
                "ft notify bandwidth (min)"          = 0
                "hadoop connectivity"                = 0
                "index create memory (KB)"           = 0
                "in-doubt xact resolution"           = 0
                "lightweight pooling"                = 0
                "locks"                              = 0
                "max degree of parallelism"          = 0
                "max full-text crawl range"          = 4
                "max server memory (MB)"             = 2147483647
                "max text repl size (B)"             = 65536
                "max worker threads"                 = 0
                "media retention"                    = 0
                "min memory per query (KB)"          = 1024
                "min server memory (MB)"             = 0
                "nested triggers"                    = 1
                "network packet size (B)"            = 4096
                "Ole Automation Procedures"          = 0
                "open objects"                       = 0
                "optimize for ad hoc workloads"      = 0
                "PH timeout (s)"                     = 60
                "polybase network encryption"        = 1
                "precompute rank"                    = 0
                "priority boost"                     = 0
                "query governor cost limit"          = 0
                "query wait (s)"                     = -1
                "recovery interval (min)"            = 0
                "remote access"                      = 1
                "remote admin connections"           = 0
                "remote data archive"                = 0
                "remote login timeout (s)"           = 10
                "remote proc trans"                  = 0
                "remote query timeout (s)"           = 600
                "Replication XPs"                    = 0
                "scan for startup procs"             = 0
                "server trigger recursion"           = 1
                "set working set size"               = 0
                "show advanced options"              = 0
                "SMO and DMO XPs"                    = 1
                "transform noise words"              = 0
                "two digit year cutoff"              = 2049
                "user connections"                   = 0
                "user options"                       = 0
                "xp_cmdshell"                        = 0

            }
        }
        #endregion SQL2017


    }

}
tools\dbatools\internal\functions\Get-SqlFileStructure.ps1
function Get-SqlFileStructure {
    <#
    .SYNOPSIS
    Internal function. Returns custom object that contains file structures on destination paths (\\SqlInstance\m$\mssql\etc\etc\file.mdf) for
    source and destination servers.
#>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [object]$source,
        [Parameter(Mandatory = $true, Position = 1)]
        [ValidateNotNullOrEmpty()]
        [object]$destination,
        [Parameter(Mandatory = $false, Position = 2)]
        [bool]$ReuseSourceFolderStructure,
        [PSCredential]$SourceSqlCredential,
        [PSCredential]$DestinationSqlCredential
    )

    $sourceserver = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
    $source = $sourceserver.DomainInstanceName
    $destserver = Connect-SqlInstance -SqlInstance $Destination -SqlCredential $DestinationSqlCredential
    $destination = $destserver.DomainInstanceName

    $sourcenetbios = Resolve-NetBiosName $sourceserver
    $destnetbios = Resolve-NetBiosName $destserver

    $dbcollection = @{ };

    foreach ($db in $sourceserver.databases) {
        $dbstatus = $db.status.toString()
        if ($dbstatus.StartsWith("Normal") -eq $false) { continue }
        $destinationfiles = @{ }; $sourcefiles = @{ }

        # Data Files
        foreach ($filegroup in $db.filegroups) {
            foreach ($file in $filegroup.files) {
                # Destination File Structure
                $d = @{ }
                if ($ReuseSourceFolderStructure) {
                    $d.physical = $file.filename
                }
                else {
                    $directory = Get-SqlDefaultPaths $destserver data
                    $filename = Split-Path $($file.filename) -leaf
                    $d.physical = "$directory\$filename"
                }
                $d.logical = $file.name
                $d.remotefilename = Join-AdminUnc $destnetbios $d.physical
                $destinationfiles.add($file.name, $d)

                # Source File Structure
                $s = @{ }
                $s.logical = $file.name
                $s.physical = $file.filename
                $s.remotefilename = Join-AdminUnc $sourcenetbios $s.physical
                $sourcefiles.add($file.name, $s)
            }
        }

        # Add support for Full Text Catalogs in SQL Server 2005 and below
        if ($sourceserver.VersionMajor -lt 10) {
            foreach ($ftc in $db.FullTextCatalogs) {
                # Destination File Structure
                $d = @{ }
                $pre = "sysft_"
                $name = $ftc.name
                $physical = $ftc.RootPath
                $logical = "$pre$name"
                if ($ReuseSourceFolderStructure) {
                    $d.physical = $physical
                }
                else {
                    $directory = Get-SqlDefaultPaths $destserver data
                    if ($destserver.VersionMajor -lt 10) { $directory = "$directory\FTDATA" }
                    $filename = Split-Path($physical) -leaf
                    $d.physical = "$directory\$filename"
                }
                $d.logical = $logical
                $d.remotefilename = Join-AdminUnc $destnetbios $d.physical
                $destinationfiles.add($logical, $d)

                # Source File Structure
                $s = @{ }
                $pre = "sysft_"
                $name = $ftc.name
                $physical = $ftc.RootPath
                $logical = "$pre$name"

                $s.logical = $logical
                $s.physical = $physical
                $s.remotefilename = Join-AdminUnc $sourcenetbios $s.physical
                $sourcefiles.add($logical, $s)
            }
        }

        # Log Files
        foreach ($file in $db.logfiles) {
            $d = @{ }
            if ($ReuseSourceFolderStructure) {
                $d.physical = $file.filename
            }
            else {
                $directory = Get-SqlDefaultPaths $destserver log
                $filename = Split-Path $($file.filename) -leaf
                $d.physical = "$directory\$filename"
            }
            $d.logical = $file.name
            $d.remotefilename = Join-AdminUnc $destnetbios $d.physical
            $destinationfiles.add($file.name, $d)

            $s = @{ }
            $s.logical = $file.name
            $s.physical = $file.filename
            $s.remotefilename = Join-AdminUnc $sourcenetbios $s.physical
            $sourcefiles.add($file.name, $s)
        }

        $location = @{ }
        $location.add("Destination", $destinationfiles)
        $location.add("Source", $sourcefiles)
        $dbcollection.Add($($db.name), $location)
    }

    $filestructure = [pscustomobject]@{ "databases" = $dbcollection }
    return $filestructure
}
tools\dbatools\internal\functions\Get-SqlSaLogin.ps1
function Get-SqlSaLogin {
    <#
        .SYNOPSIS
            Internal function. Gets the name of the sa login in case someone changed it.
        .PARAMETER SqlInstance
            The SQL Server instance.
        .PARAMETER SqlCredential
            Allows you to login to servers using SQL Logins instead of Windows Authentication (AKA Integrated or Trusted).
    #>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [object]$SqlInstance,
        [PSCredential]$SqlCredential
    )
    $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
    $sa = $server.Logins | Where-Object Id -eq 1
    return $sa.Name
}
tools\dbatools\internal\functions\Get-XpDirTreeRestoreFile.ps1
function Get-XpDirTreeRestoreFile {
    <#
    .SYNOPSIS
        Internal Function to get SQL Server backfiles from a specified folder using xp_dirtree

    .DESCRIPTION
        Takes path, checks for validity. Scans for usual backup file

    .PARAMETER Path
        The path to retrieve the restore for.

    .PARAMETER SqlInstance
        The SQL Server that you're connecting to.

    .PARAMETER SqlCredential
        Credential object used to connect to the SQL Server as a different user

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .EXAMPLE
        PS C:\> Get-XpDirTreeRestoreFile -Path '\\foo\bar\' -SqlInstance $SqlInstance

        Tests whether the instance $SqlInstance has access to the path \\foo\bar\
#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [string]$Path,
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [System.Management.Automation.PSCredential]$SqlCredential,
        [bool][Alias('Silent')]$EnableException = $false,
        [switch]$NoRecurse
    )

    Write-Message -Level InternalComment -Message "Starting"

    Write-Message -Level Verbose -Message "Connecting to $SqlInstance"
    $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential

    if (($path -like '*.bak') -or ($path -like '*trn')) {

    }
    elseif ($Path[-1] -ne "\") {
        $Path = $Path + "\"
    }

    if (!(Test-DbaSqlPath -SqlInstance $server -path $path)) {
        Stop-Function -Message "SqlInstance $SqlInstance cannot access $path" -EnableException $true
    }
    if ($server.VersionMajor -lt 9) {
        $sql = "EXEC master..xp_dirtree '$Path',1,1;"
    }
    else {
        $sql = "EXEC master.sys.xp_dirtree '$Path',1,1;"
    }
    #$queryResult = Invoke-Sqlcmd2 -ServerInstance $SqlInstance -Credential $SqlCredential -Database tempdb -Query $query
    $queryResult = $server.Query($sql)
    Write-Message -Level Debug -Message $sql
    $dirs = $queryResult | where-object file -eq 0
    $Results = @()
    $Results += $queryResult | where-object file -eq 1 | Select-Object @{ Name = "FullName"; Expression = { $path + $_."Subdirectory" } }

    if ($True -ne $NoRecurse) {
        foreach ($d in $dirs) {
            $fullpath = "$path$($d.Subdirectory)"
            Write-Message -Level Verbose -Message "Enumerating subdirectory '$fullpath'"
            $Results += Get-XpDirTreeRestoreFile -path $fullpath -SqlInstance $server
        }
    }
    return $Results
}
tools\dbatools\internal\functions\Import-DbaCmdlet.ps1
function Import-DbaCmdlet {
<#
    .SYNOPSIS
        Loads a cmdlet into the current context.
    
    .DESCRIPTION
        Loads a cmdlet into the current context.
        This can be used to register a cmdlet during module import, making it easy to have hybrid modules publishing both cmdlets and functions.
        Can also be used to register cmdlets written in PowerShell classes.
    
    .PARAMETER Name
        The name of the cmdlet to register.
    
    .PARAMETER Type
        The type of the class implementing the cmdlet.
    
    .PARAMETER HelpFile
        Path to the help XML containing the help for the cmdlet.
    
    .PARAMETER Module
        Module to inject the cmdlet into.
    
    .EXAMPLE
        PS C:\> Import-DbaCmdlet -Name Get-Something -Type ([GetSomethingCommand])
        
        Imports the Get-Something cmdlet into the current context.
    
    .EXAMPLE
        PS C:\> Import-DbaCmdlet -Name Get-Something -Type ([GetSomethingCommand]) -Module (Get-Module PSReadline)
        
        Imports the Get-Something cmdlet into the PSReadline module.
    
    .NOTES
        Original Author: Chris Dent
        Link: https://www.indented.co.uk/cmdlets-without-a-dll/
#>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [String]
        $Name,
        
        [Parameter(Mandatory = $true)]
        [Type]
        $Type,
        
        [string]
        $HelpFile,
        
        [System.Management.Automation.PSModuleInfo]
        $Module
    )
    
    begin {
        $scriptBlock = {
            param (
                [String]
                $Name,
                
                [Type]
                $Type,
                
                [string]
                $HelpFile
            )
            
            $sessionStateCmdletEntry = New-Object System.Management.Automation.Runspaces.SessionStateCmdletEntry(
                $Name,
                $Type,
                $HelpFile
            )
            
            # System.Management.Automation.Runspaces.LocalPipeline will let us get at ExecutionContext.
            # Note: $ExecutionContext is *not* an instance of this object.
            $pipelineType = [PowerShell].Assembly.GetType('System.Management.Automation.Runspaces.LocalPipeline')
            $method = $pipelineType.GetMethod(
                'GetExecutionContextFromTLS',
                [System.Reflection.BindingFlags]'Static,NonPublic'
            )
            
            # Invoke the method to get an instance of ExecutionContext.
            $context = $method.Invoke(
                $null,
                [System.Reflection.BindingFlags]'Static,NonPublic',
                $null,
                $null,
                (Get-Culture)
            )
            
            # Get the SessionStateInternal type
            $internalType = [PowerShell].Assembly.GetType('System.Management.Automation.SessionStateInternal')
            
            # Get a valid constructor which accepts a param of type ExecutionContext
            $constructor = $internalType.GetConstructor(
                [System.Reflection.BindingFlags]'Instance,NonPublic',
                $null,
                $context.GetType(),
                $null
            )
            
            # Get the SessionStateInternal for this execution context
            $sessionStateInternal = $constructor.Invoke($context)
            
            # Get the method which allows Cmdlets to be added to the session
            $method = $internalType.GetMethod(
                'AddSessionStateEntry',
                [System.Reflection.BindingFlags]'Instance,NonPublic',
                $null,
                $sessionStateCmdletEntry.GetType(),
                $null
            )
            # Invoke the method.
            $method.Invoke($sessionStateInternal, $sessionStateCmdletEntry)
        }
    }
    
    process {
        if (-not $Module) { $scriptBlock.Invoke($Name, $Type, $HelpFile) }
        else { $Module.Invoke($scriptBlock, @($Name, $Type, $HelpFile)) }
    }
}
tools\dbatools\internal\functions\Invoke-Command2.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Invoke-Command2 {
    <#
        .SYNOPSIS
            Wrapper function that calls Invoke-Command and gracefully handles credentials.

        .DESCRIPTION
            Wrapper function that calls Invoke-Command and gracefully handles credentials.

        .PARAMETER ComputerName
            Default: $env:COMPUTERNAME
            The computer to invoke the scriptblock on.

        .PARAMETER Credential
            The credentials to use.
            Can accept $null on older PowerShell versions, since it expects type object, not PSCredential

        .PARAMETER ScriptBlock
            The code to run on the targeted system

        .PARAMETER ArgumentList
            Any arguments to pass to the scriptblock being run

        .PARAMETER Raw
            Passes through the raw return data, rather than prettifying stuff.

        .EXAMPLE
            PS C:\> Invoke-Command2 -ComputerName sql2014 -Credential $Credential -ScriptBlock { dir }

            Executes the scriptblock '{ dir }' on the computer sql2014 using the credentials stored in $Credential.
            If $Credential is null, no harm done.
    #>
    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUsePSCredentialType", "")]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "")]
    param (
        [DbaInstanceParameter]$ComputerName = $env:COMPUTERNAME,
        [object]$Credential,
        [scriptblock]$ScriptBlock,
        [object[]]$ArgumentList,
        [switch]$Raw
    )
    <# Note: Credential stays as an object type for legacy reasons. #>

    $InvokeCommandSplat = @{
        ScriptBlock = $ScriptBlock
    }
    if ($ArgumentList) {
        $InvokeCommandSplat["ArgumentList"] = $ArgumentList
    }
    if (-not $ComputerName.IsLocalHost) {
        $runspaceId = [System.Management.Automation.Runspaces.Runspace]::DefaultRunspace.InstanceId
        $sessionName = "dbatools_$runspaceId"

        # Retrieve a session from the session cache, if available (it's unique per runspace)
        if (-not ($currentSession = [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::PSSessionGet($runspaceId, $ComputerName.ComputerName) | Where-Object State -Match "Opened|Disconnected")) {
            $timeout = New-PSSessionOption -IdleTimeout (New-TimeSpan -Minutes 10).TotalMilliSeconds
            if ($Credential) {
                $InvokeCommandSplat["Session"] = (New-PSSession -ComputerName $ComputerName.ComputerName -Name $sessionName -SessionOption $timeout -Credential $Credential -ErrorAction Stop)
            }
            else {
                $InvokeCommandSplat["Session"] = (New-PSSession -ComputerName $ComputerName.ComputerName -Name $sessionName -SessionOption $timeout -ErrorAction Stop)
            }
            $currentSession = $InvokeCommandSplat["Session"]
        }
        else {
            if ($currentSession.State -eq "Disconnected") {
                $null = $currentSession | Connect-PSSession -ErrorAction Stop
            }
            $InvokeCommandSplat["Session"] = $currentSession

            # Refresh the session registration if registered, to reset countdown until purge
            [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::PSSessionSet($runspaceId, $ComputerName.ComputerName, $currentSession)
        }
    }

    if ($Raw) {
        Invoke-Command @InvokeCommandSplat
    }
    else {
        Invoke-Command @InvokeCommandSplat | Select-Object -Property * -ExcludeProperty PSComputerName, RunspaceId, PSShowComputerName
    }

    if (-not $ComputerName.IsLocalhost) {
        # Tell the system to clean up if the session expires
        [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::PSSessionSet($runspaceId, $ComputerName.ComputerName, $currentSession)

        if (-not (Get-DbaConfigValue -FullName 'PSRemoting.Sessions.Enable' -Fallback $true)) {
            $currentSession | Remove-PSSession
        }
    }
}
tools\dbatools\internal\functions\Invoke-DbaDatabaseCorruption.ps1
function Invoke-DbaDatabaseCorruption {
    <#
      .SYNOPSIS
      Utilizes the DBCC WRITEPAGE functionality  to corrupt a specific database table for testing.  In no uncertain terms, this is a non-production command.
      This will absolutely break your databases and that is its only purpose.
      Using DBCC WritePage will definitely void any support options for your database.

      .DESCRIPTION
      This command can be used to verify your tests for corruption are successful, and to demo various scenarios for corrupting page data.
      This command will take an instance and database (and optionally a table) and set the database to single user mode, corrupt either the specified table or the first table it finds, and returns it to multi-user.

      .PARAMETER SqlInstance
      The SQL Server instance holding the databases to be removed.You must have sysadmin access and Server version must be SQL Server version 2000 or higher.

      .PARAMETER SqlCredential
      Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

      .PARAMETER Database
      The single database you would like to corrupt, this command does not support multiple databases (on purpose.)

      .PARAMETER Table
      The specific table you want corrupted, if you do not choose one, the first user table (alphabetically) will be chosen for corruption.

      .PARAMETER WhatIf
      If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.

      .PARAMETER Confirm
      If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.

      .PARAMETER EnableException
      By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
      This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
      Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

      .NOTES
      Tags: Corruption, Testing
      Author: Constantine Kokkinos (@mobileck https://constantinekokkinos.com)
      Reference: https://www.sqlskills.com/blogs/paul/dbcc-writepage/
      Website: https://dbatools.io
      Copyright: (C) Chrissy LeMaire, [email protected]
      License: MIT https://opensource.org/licenses/MIT

      .LINK
      https://dbatools.io/Invoke-DbaDatabaseCorruption

      .EXAMPLE
      Invoke-DbaDatabaseCorruption -SqlInstance sql2016 -Database containeddb
      Prompts for confirmation then selects the first table in database containeddb and corrupts it (by putting database into single user mode, writing to garbage to its first non-iam page, and returning it to multi-user.)

      .EXAMPLE
      Invoke-DbaDatabaseCorruption -SqlInstance sql2016 -Database containeddb -Table Customers -Confirm:$false
      Does not prompt and immediately corrupts table customers in database containeddb on the sql2016 instance (by putting database into single user mode, writing to garbage to its first non-iam page, and returning it to multi-user.)
  #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [parameter(Mandatory = $false)]
        [Alias("Credential")]
        [PSCredential]
        $SqlCredential,
        [parameter(Mandatory)]
        [string]$Database,
        [string]$Table,
        [Alias('Silent')]
        [switch]$EnableException
    )
    # For later if we want to do bit flipping.
    # function Dbcc-ReadPage {
    #   param (
    #     $SqlInstance,
    #     $Database,
    #     $TableName,
    #     $IndexID = 1
    #   )
    #   $DbccPage = "DBCC PAGE (N'$Database',N'$($TableName)',$IndexID)"
    #   Write-Message -Level Verbose -Message "$DbccPage"
    #   $pages = $SqlInstance.Query($DbccPage) | Where-Object { $_.IAMFID -ne [DBNull]::Value }
    #   return $Pages
    # }

    function Dbcc-Index {
        [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseApprovedVerbs", "")]
        [CmdletBinding()]
        param (
            $SqlInstance,
            $Database,
            $TableName,
            $IndexID = 1
        )
        $DbccInd = "DBCC IND (N'$Database',N'$($TableName)',$IndexID)"
        Write-Message -Level Verbose -Message "$DbccInd"
        $pages = $SqlInstance.Query($DbccInd) | Where-Object { $_.IAMFID -ne [DBNull]::Value }
        return $Pages
    }
    function Dbcc-WritePage {
        [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseApprovedVerbs", "")]
        [CmdletBinding()]
        param (
            $SqlInstance,
            $Database,
            $FileId = 1,
            $PageId,
            $Offset = 4000,
            $NumberOfBytesToChange = 1,
            $HexString = '0x45',
            $bypassbufferpool = 1
        )
        $DbccWritePage = "DBCC WRITEPAGE (N'$Database', $FileId, $PageId, $Offset, $NumberOfBytesToChange, $HexString, $bypassbufferpool);"
        Write-Message -Level Verbose -Message "$DbccWritePage"
        $WriteInfo = $SqlInstance.Databases[$Database].Query($DbccWritePage)
        return $WriteInfo
    }

    if ("master", "tempdb", "model", "msdb" -contains $Database) {
        Stop-Function -Message "You may not corrupt system databases."
        return
    }

    try {
        Write-Message -Level Verbose -Message "Connecting to $SqlInstance"
        $Server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential -MinimumVersion 9
    }
    catch {
        Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $SqlInstance
        return
    }

    $db = $Server.Databases | Where-Object { $_.Name -eq $Database }
    if (!$db) {
        Stop-Function -Message "The database specified does not exist."
        return
    }
    if ($Table) {
        $tb = $db.Tables | Where-Object Name -eq $Table
    }
    else {
        $tb = $db.Tables | Select-Object -First 1
    }

    if (-not $tb) {
        Stop-Function -Message "There are no accessible tables in $Database on $SqlInstance." -Target $Database
        return
    }

    $RowCount = $db.Query("select top 1 * from $($tb.name)")
    if ($RowCount.count -eq 0) {
        Stop-Function -Message "The table $tb has no rows" -Target $table
        return
    }

    if ($Pscmdlet.ShouldProcess("$db on $SqlInstance", "Corrupt $tb in $Database")) {
        $pages = Dbcc-Index -SqlInstance $Server -Database $Database -TableName $tb.Name | Select-Object -First 1
        #Dbcc-ReadPage -SqlInstance $Server -Database $Database -PageId $pages.PagePID -FileId $pages.PageFID
        Write-Message -Level Verbose -Message "Setting single-user mode."
        $null = Stop-DbaProcess -SqlInstance $Server -Database $Database
        $null = Set-DbaDatabaseState -SqlServer $Server -Database $Database -SingleUser -Force

        try {
            Write-Message -Level Verbose -Message "Stopping processes in target database."
            $null = Stop-DbaProcess -SqlInstance $Server -Database $Database
            Write-Message -Level Verbose -Message "Corrupting data."
            Dbcc-WritePage -SqlInstance $Server -Database $Database -PageId $pages.PagePID -FileId $pages.PageFID
        }
        catch {
            $Server.ConnectionContext.Disconnect()
            $Server.ConnectionContext.Connect()
            $null = Set-DbaDatabaseState -SqlServer $Server -Database $Database -MultiUser -Force
            Stop-Function -Message "Failed to write page" -Category WriteError -ErrorRecord $_ -Target $instance
            return
        }

        Write-Message -Level Verbose -Message "Setting database into multi-user mode."
        # If you do not disconnect and reconnect, multiuser fails.
        $Server.ConnectionContext.Disconnect()
        $Server.ConnectionContext.Connect()
        $null = Set-DbaDatabaseState -SqlServer $Server -Database $Database -MultiUser -Force

        [pscustomobject]@{
            ComputerName = $Server.ComputerName
            InstanceName = $Server.ServiceName
            SqlInstance  = $Server.DomainInstanceName
            Database     = $db.Name
            Table        = $tb.Name
            Status       = "Corrupted"
        }
    }
}
tools\dbatools\internal\functions\Invoke-DbaDiagnosticQueryScriptParser.ps1
function Invoke-DbaDiagnosticQueryScriptParser {
    [CmdletBinding(DefaultParameterSetName = "Default")]

    Param(
        [parameter(Mandatory = $true)]
        [ValidateScript( {Test-Path $_})]
        [System.IO.FileInfo]$filename,
        [Switch]$NoQueryTextColumn,
        [Switch]$NoPlanColumn,
        [Switch]$NoColumnParsing
    )

    $out = "Parsing file {0}" -f $filename
    write-verbose -Message $out

    $ParsedScript = @()
    [string]$scriptpart = ""

    $fullscript = Get-Content -Path $filename

    $start = $false
    $querynr = 0
    $DBSpecific = $false

    if ($NoQueryTextColumn) {$QueryTextColumn = ""}  else {$QueryTextColumn = ", t.[text] AS [Complete Query Text]"}
    if ($NoPlanColumn) {$PlanTextColumn = ""} else {$PlanTextColumn = ", qp.query_plan AS [Query Plan]"}

    foreach ($line in $fullscript) {
        if ($start -eq $false) {
            if ($line -match "You have the correct major version of SQL Server for this diagnostic information script") {
                $start = $true
            }
            continue
        }

        if ($line.StartsWith("-- Database specific queries ***") -or ($line.StartsWith("-- Switch to user database **"))) {
            $DBSpecific = $true
        }

        if (!$NoColumnParsing) {
            if (($line -match "-- uncomment out these columns if not copying results to Excel") -or ($line -match "-- comment out this column if copying results to Excel")) {
                $line = $QueryTextColumn + $PlanTextColumn
            }
        }

        if ($line -match "-{2,}\s{1,}(.*) \(Query (\d*)\) \((\D*)\)") {
            $prev_querydescription = $Matches[1]
            $prev_querynr = $Matches[2]
            $prev_queryname = $Matches[3]

            if ($querynr -gt 0) {
                $properties = @{QueryNr = $querynr; QueryName = $queryname; DBSpecific = $DBSpecific; Description = $queryDescription; Text = $scriptpart}
                $newscript = New-Object -TypeName PSObject -Property $properties
                $ParsedScript += $newscript
                $scriptpart = ""
            }

            $querydescription = $prev_querydescription
            $querynr = $prev_querynr
            $queryname = $prev_queryname
        }
        else {
            if (!$line.startswith("--") -and ($line.trim() -ne "") -and ($null -ne $line) -and ($line -ne "\n")) {
                $scriptpart += $line + "`n"
            }
        }
    }

    $properties = @{QueryNr = $querynr; QueryName = $queryname; DBSpecific = $DBSpecific; Description = $queryDescription; Text = $scriptpart}
    $newscript = New-Object -TypeName PSObject -Property $properties
    $ParsedScript += $newscript
    $ParsedScript
}
tools\dbatools\internal\functions\Invoke-DbaSqlAsync.ps1
function Invoke-DbaSqlAsync {
    <#
        .SYNOPSIS
            Runs a T-SQL script.

        .DESCRIPTION
            Runs a T-SQL script. It's a stripped down version of https://github.com/sqlcollaborative/Invoke-SqlCmd2 and adapted to use dbatools' facilities.
            If you're looking for a public usable function, see Invoke-DbaSqlQuery

        .PARAMETER SQLConnection
            Specifies an existing SQLConnection object to use in connecting to SQL Server.

        .PARAMETER Query
            Specifies one or more queries to be run. The queries can be Transact-SQL, XQuery statements, or sqlcmd commands. Multiple queries in a single batch may be separated by a semicolon.

            Do not specify the sqlcmd GO separator (or, use the ParseGo parameter). Escape any double quotation marks included in the string.

            Consider using bracketed identifiers such as [MyTable] instead of quoted identifiers such as "MyTable".

        .PARAMETER QueryTimeout
            Specifies the number of seconds before the queries time out.

        .PARAMETER As
            Specifies output type. Valid options for this parameter are 'DataSet', 'DataTable', 'DataRow', 'PSObject', and 'SingleValue'

            PSObject output introduces overhead but adds flexibility for working with results: http://powershell.org/wp/forums/topic/dealing-with-dbnull/

        .PARAMETER SqlParameters
            Specifies a hashtable of parameters for parameterized SQL queries.  http://blog.codinghorror.com/give-me-parameterized-sql-or-give-me-death/

            Example:

        .PARAMETER AppendServerInstance
            If this switch is enabled, the SQL Server instance will be appended to PSObject and DataRow output.


        .PARAMETER MessagesToOutput
            Use this switch to have on the output stream messages too (e.g. PRINT statements). Output will hold the resultset too. See examples for detail

        .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
    #>

    param (
        [Alias('Connection', 'Conn')]
        [ValidateNotNullOrEmpty()]
        [Microsoft.SqlServer.Management.Common.ServerConnection]$SQLConnection,

        [Parameter(Mandatory = $true, Position = 0, ParameterSetName = "Query")]
        [string]
        $Query,

        [ValidateSet("DataSet", "DataTable", "DataRow", "PSObject", "SingleValue")]
        [string]
        $As = "DataRow",

        [System.Collections.IDictionary]
        $SqlParameters,

        [switch]
        $AppendServerInstance,

        [Int32]$QueryTimeout = 600,

        [switch]
        $MessagesToOutput,

        [switch]
        $EnableException
    )

    begin {
        function Resolve-SqlError {
            param($Err)
            if ($Err) {
                if ($Err.Exception.GetType().Name -eq 'SqlException') {
                    # For SQL exception
                    #$Err = $_
                    Write-Message -Level Debug -Message "Capture SQL Error"
                    if ($PSBoundParameters.Verbose) {
                        Write-Message -Level Verbose -Message "SQL Error:  $Err"
                    } #Shiyang, add the verbose output of exception
                    switch ($ErrorActionPreference.ToString()) {
                        { 'SilentlyContinue', 'Ignore' -contains $_ } {   }
                        'Stop' { throw $Err }
                        'Continue' { throw $Err }
                        Default { Throw $Err }
                    }
                }
                else {
                    # For other exception
                    Write-Message -Level Debug -Message "Capture Other Error"
                    if ($PSBoundParameters.Verbose) {
                        Write-Message -Level Verbose -Message "Other Error:  $Err"
                    }
                    switch ($ErrorActionPreference.ToString()) {
                        { 'SilentlyContinue', 'Ignore' -contains $_ } { }
                        'Stop' { throw $Err }
                        'Continue' { throw $Err }
                        Default { throw $Err }
                    }
                }
            }

        }

        if ($As -eq "PSObject") {
            #This code scrubs DBNulls.  Props to Dave Wyatt
            $cSharp = @'
                using System;
                using System.Data;
                using System.Management.Automation;

                public class DBNullScrubber
                {
                    public static PSObject DataRowToPSObject(DataRow row)
                    {
                        PSObject psObject = new PSObject();

                        if (row != null && (row.RowState & DataRowState.Detached) != DataRowState.Detached)
                        {
                            foreach (DataColumn column in row.Table.Columns)
                            {
                                Object value = null;
                                if (!row.IsNull(column))
                                {
                                    value = row[column];
                                }

                                psObject.Properties.Add(new PSNoteProperty(column.ColumnName, value));
                            }
                        }

                        return psObject;
                    }
                }
'@

            try {
                Add-Type -TypeDefinition $cSharp -ReferencedAssemblies 'System.Data', 'System.Xml' -ErrorAction stop
            }
            catch {
                if (-not $_.ToString() -like "*The type name 'DBNullScrubber' already exists*") {
                    Write-Warning "Could not load DBNullScrubber.  Defaulting to DataRow output: $_."
                    $As = "Datarow"
                }
            }
        }

        $GoSplitterRegex = [regex]'(?smi)^[\s]*GO[\s]*$'

    }
    process {
        $Conn = $SQLConnection.SqlConnectionObject


        Write-Message -Level Debug -Message "Stripping GOs from source"
        $Pieces = $GoSplitterRegex.Split($Query)

        # Only execute non-empty statements
        $Pieces = $Pieces | Where-Object { $_.Trim().Length -gt 0 }
        foreach ($piece in $Pieces) {
            $cmd = New-Object system.Data.SqlClient.SqlCommand($piece, $conn)
            $cmd.CommandTimeout = $QueryTimeout

            if ($null -ne $SqlParameters) {
                $SqlParameters.GetEnumerator() |
                    ForEach-Object {
                    if ($null -ne $_.Value) {
                        $cmd.Parameters.AddWithValue($_.Key, $_.Value)
                    }
                    else {
                        $cmd.Parameters.AddWithValue($_.Key, [DBNull]::Value)
                    }
                } > $null
            }

            $ds = New-Object system.Data.DataSet
            $da = New-Object system.Data.SqlClient.SqlDataAdapter($cmd)

            if ($MessagesToOutput) {
                $pool = [RunspaceFactory]::CreateRunspacePool(1, [int]$env:NUMBER_OF_PROCESSORS + 1)
                $pool.ApartmentState = "MTA"
                $pool.Open()
                $runspaces = @()
                $scriptblock = {
                    Param ($da, $ds, $conn, $queue )
                    $conn.FireInfoMessageEventOnUserErrors = $false
                    $handler = [System.Data.SqlClient.SqlInfoMessageEventHandler] { $queue.Enqueue($_) }
                    $conn.add_InfoMessage($handler)
                    $Err = $null
                    try {
                        [void]$da.fill($ds)
                    }
                    catch {
                        $Err = $_
                    }
                    finally {
                        $conn.remove_InfoMessage($handler)
                    }
                    return $Err
                }
                $queue = New-Object System.Collections.Concurrent.ConcurrentQueue[string]
                $runspace = [PowerShell]::Create()
                $null = $runspace.AddScript($scriptblock)
                $null = $runspace.AddArgument($da)
                $null = $runspace.AddArgument($ds)
                $null = $runspace.AddArgument($Conn)
                $null = $runspace.AddArgument($queue)
                $runspace.RunspacePool = $pool
                $runspaces += [PSCustomObject]@{ Pipe = $runspace; Status = $runspace.BeginInvoke() }
                # While streaming ...
                while ($runspaces.Status.IsCompleted -notcontains $true) {
                    $item = $null
                    if ($queue.TryDequeue([ref]$item)) {
                        "$item"
                    }
                }
                # Drain the stream as the runspace is closed, just to be safe
                if ($queue.IsEmpty -ne $true) {
                    $item = $null
                    while ($queue.TryDequeue([ref]$item)) {
                        "$item"
                    }
                }
                foreach ($runspace in $runspaces) {
                    $results = $runspace.Pipe.EndInvoke($runspace.Status)
                    $runspace.Pipe.Dispose()
                    if ($null -ne $results) {
                        Resolve-SqlError $results[0]
                    }
                }
                $pool.Close()
                $pool.Dispose()
            }
            else {
                #Following EventHandler is used for PRINT and RAISERROR T-SQL statements. Executed when -Verbose parameter specified by caller and no -MessageToOutput
                if ($PSBoundParameters.Verbose) {
                    $conn.FireInfoMessageEventOnUserErrors = $false
                    $handler = [System.Data.SqlClient.SqlInfoMessageEventHandler] { Write-Verbose -Message "$($_)" }
                    $conn.add_InfoMessage($handler)
                }
                try {
                    [void]$da.fill($ds)
                }
                catch {
                    $Err = $_
                }
                finally {
                    if ($PSBoundParameters.Verbose) {
                        $conn.remove_InfoMessage($handler)
                    }
                }
                Resolve-SqlError $Err
            }
            if ($AppendServerInstance) {
                #Basics from Chad Miller
                $Column = New-Object Data.DataColumn
                $Column.ColumnName = "ServerInstance"
                
                if ($ds.Tables.Count -ne 0) {
                    $ds.Tables[0].Columns.Add($Column)
                    Foreach ($row in $ds.Tables[0]) {
                        $row.ServerInstance = $SQLConnection.ServerInstance
                    }
                }
            }

            switch ($As) {
                'DataSet' {
                    $ds
                }
                'DataTable' {
                    $ds.Tables
                }
                'DataRow' {
                    if ($ds.Tables.Count -ne 0) {
                        $ds.Tables[0]
                    }
                }
                'PSObject' {
                    if ($ds.Tables.Count -ne 0) {
                        #Scrub DBNulls - Provides convenient results you can use comparisons with
                        #Introduces overhead (e.g. ~2000 rows w/ ~80 columns went from .15 Seconds to .65 Seconds - depending on your data could be much more!)
                        foreach ($row in $ds.Tables[0].Rows) {
                            [DBNullScrubber]::DataRowToPSObject($row)
                        }
                    }
                }
                'SingleValue' {
                    if ($ds.Tables.Count -ne 0) {
                        $ds.Tables[0] | Select-Object -ExpandProperty $ds.Tables[0].Columns[0].ColumnName
                    }
                }
            }
        } #foreach ($piece in $Pieces)

    }
}
tools\dbatools\internal\functions\Invoke-ManagedComputerCommand.ps1
function Invoke-ManagedComputerCommand {
    <#
        .SYNOPSIS
            Runs wmi commands against a target system.

        .DESCRIPTION
            Runs wmi commands against a target system.
            Either directly or over PowerShell remoting.

        .PARAMETER ComputerName
            The target to run against. Must be resolvable.

        .PARAMETER Credential
            Credentials to use when using PowerShell remoting.

        .PARAMETER ScriptBlock
            The scriptblock to execute.
            Use $wmi to access the smo wmi object.
            Must not include a param block!

        .PARAMETER ArgumentList
            The arguments to pass to your scriptblock.
            Access them within the scriptblock using the automatic variable $args

        .PARAMETER EnableException
            Left in for legacy reasons. This command will throw no matter what
    #>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Alias("Server")]
        [dbainstanceparameter]$ComputerName,
        [PSCredential]$Credential,
        [Parameter(Mandatory = $true)]
        [scriptblock]$ScriptBlock,
        [string[]]$ArgumentList,
        [switch][Alias('Silent')]
        $EnableException # Left in for legacy but this command needs to throw
    )
    
    $computer = $ComputerName.ComputerName
    
    $null = Test-ElevationRequirement -ComputerName $computer -EnableException $true
    
    $resolved = Resolve-DbaNetworkName -ComputerName $computer -Turbo
    $ipaddr = $resolved.IpAddress
    $ArgumentList += $ipaddr
    
    [scriptblock]$setupScriptBlock = {
        $ipaddr = $args[$args.GetUpperBound(0)]
        
        # Just in case we go remote, ensure the assembly is loaded
        [void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SqlWmiManagement')
        $wmi = New-Object Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer $ipaddr
        $null = $wmi.Initialize()
    }
    
    $prescriptblock = $setupScriptBlock.ToString()
    $postscriptblock = $ScriptBlock.ToString()
    
    $scriptblock = [ScriptBlock]::Create("$prescriptblock  $postscriptblock")
    
    try {
        Invoke-Command2 -ScriptBlock $ScriptBlock -ArgumentList $ArgumentList -Credential $Credential -ErrorAction Stop
    }
    catch {
        Write-Message -Level Verbose -Message "Local connection attempt to $computer failed. Connecting remotely."
        
        # For surely resolve stuff, and going by default with kerberos, this needs to match FullComputerName
        $hostname = $resolved.FullComputerName
        
        Invoke-Command2 -ScriptBlock $ScriptBlock -ArgumentList $ArgumentList -ComputerName $hostname -ErrorAction Stop
    }
}
tools\dbatools\internal\functions\Invoke-Parallel.ps1
function Invoke-Parallel {
    <#
    .SYNOPSIS
        Function to control parallel processing using runspaces

    .DESCRIPTION
        Function to control parallel processing using runspaces

            Note that each runspace will not have access to variables and commands loaded in your session or in other runspaces by default.
            This behaviour can be changed with parameters.

    .PARAMETER ScriptFile
        File to run against all input objects.  Must include parameter to take in the input object, or use $args.  Optionally, include parameter to take in parameter.  Example: C:\script.ps1

    .PARAMETER ScriptBlock
        Scriptblock to run against all computers.

        You may use $Using:<Variable> language in PowerShell 3 and later.

            The parameter block is added for you, allowing behaviour similar to foreach-object:
                Refer to the input object as $_.
                Refer to the parameter parameter as $parameter

    .PARAMETER InputObject
        Run script against these specified objects.

    .PARAMETER Parameter
        This object is passed to every script block.  You can use it to pass information to the script block; for example, the path to a logging folder

            Reference this object as $parameter if using the scriptblock parameterset.

    .PARAMETER ImportVariables
        If specified, get user session variables and add them to the initial session state

    .PARAMETER ImportModules
        If specified, get loaded modules and pssnapins, add them to the initial session state

    .PARAMETER Throttle
        Maximum number of threads to run at a single time.

    .PARAMETER SleepTimer
        Milliseconds to sleep after checking for completed runspaces and in a few other spots.  I would not recommend dropping below 200 or increasing above 500

    .PARAMETER RunspaceTimeout
        Maximum time in seconds a single thread can run.  If execution of your code takes longer than this, it is disposed.  Default: 0 (seconds)

        WARNING:  Using this parameter requires that maxQueue be set to throttle (it will be by default) for accurate timing.  Details here:
        http://gallery.technet.microsoft.com/Run-Parallel-Parallel-377fd430

    .PARAMETER NoCloseOnTimeout
        Do not dispose of timed out tasks or attempt to close the runspace if threads have timed out. This will prevent the script from hanging in certain situations where threads become non-responsive, at the expense of leaking memory within the PowerShell host.

    .PARAMETER MaxQueue
        Maximum number of powershell instances to add to runspace pool.  If this is higher than $throttle, $timeout will be inaccurate

        If this is equal or less than throttle, there will be a performance impact

        The default value is $throttle times 3, if $runspaceTimeout is not specified
        The default value is $throttle, if $runspaceTimeout is specified

    .PARAMETER LogFile
        Path to a file where we can log results, including run time for each thread, whether it completes, completes with errors, or times out.

    .PARAMETER AppendLog
        Append to existing log

    .PARAMETER Quiet
        Disable progress bar

    .EXAMPLE
        Each example uses Test-ForPacs.ps1 which includes the following code:
            param($computer)

            if(test-connection $computer -count 1 -quiet -BufferSize 16){
                $object = [pscustomobject] @{
                    Computer=$computer;
                    Available=1;
                    Kodak=$(
                        if((test-path "\\$computer\c$\users\public\desktop\Kodak Direct View Pacs.url") -or (test-path "\\$computer\c$\documents and settings\all users\desktop\Kodak Direct View Pacs.url") ){"1"}else{"0"}
                    )
                }
            }
            else{
                $object = [pscustomobject] @{
                    Computer=$computer;
                    Available=0;
                    Kodak="NA"
                }
            }

            $object

    .EXAMPLE
        Invoke-Parallel -scriptfile C:\public\Test-ForPacs.ps1 -inputobject $(get-content C:\pcs.txt) -runspaceTimeout 10 -throttle 10

            Pulls list of PCs from C:\pcs.txt,
            Runs Test-ForPacs against each
            If any query takes longer than 10 seconds, it is disposed
            Only run 10 threads at a time

    .EXAMPLE
        Invoke-Parallel -scriptfile C:\public\Test-ForPacs.ps1 -inputobject c-is-ts-91, c-is-ts-95

            Runs against c-is-ts-91, c-is-ts-95 (-computername)
            Runs Test-ForPacs against each

    .EXAMPLE
        $stuff = [pscustomobject] @{
            ContentFile = "windows\system32\drivers\etc\hosts"
            Logfile = "C:\temp\log.txt"
        }

        $computers | Invoke-Parallel -parameter $stuff {
            $contentFile = join-path "\\$_\c$" $parameter.contentfile
            Get-Content $contentFile |
                set-content $parameter.logfile
        }

        This example uses the parameter argument.  This parameter is a single object.  To pass multiple items into the script block, we create a custom object (using a PowerShell v3 language) with properties we want to pass in.

        Inside the script block, $parameter is used to reference this parameter object.  This example sets a content file, gets content from that file, and sets it to a predefined log file.

    .EXAMPLE
        $test = 5
        1..2 | Invoke-Parallel -ImportVariables {$_ * $test}

        Add variables from the current session to the session state.  Without -ImportVariables $Test would not be accessible

    .EXAMPLE
        $test = 5
        1..2 | Invoke-Parallel {$_ * $Using:test}

        Reference a variable from the current session with the $Using:<Variable> syntax.  Requires PowerShell 3 or later. Note that -ImportVariables parameter is no longer necessary.

    .FUNCTIONALITY
        PowerShell Language

    .NOTES
        Credit to Boe Prox for the base runspace code and $Using implementation
            http://learn-powershell.net/2012/05/10/speedy-network-information-query-using-powershell/
            http://gallery.technet.microsoft.com/scriptcenter/Speedy-Network-Information-5b1406fb#content
            https://github.com/proxb/PoshRSJob/

        Credit to T Bryce Yehl for the Quiet and NoCloseOnTimeout implementations

        Credit to Sergei Vorobev for the many ideas and contributions that have improved functionality, reliability, and ease of use

    .LINK
        https://github.com/RamblingCookieMonster/Invoke-Parallel
    #>
    [cmdletbinding(DefaultParameterSetName='ScriptBlock')]
    Param (
        [Parameter(Mandatory=$false,position=0,ParameterSetName='ScriptBlock')]
        [System.Management.Automation.ScriptBlock]$ScriptBlock,

        [Parameter(Mandatory=$false,ParameterSetName='ScriptFile')]
        [ValidateScript({Test-Path $_ -pathtype leaf})]
        $ScriptFile,

        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [Alias('CN','__Server','IPAddress','Server','ComputerName')]
        [PSObject]$InputObject,

        [PSObject]$Parameter,

        [switch]$ImportVariables,
        [switch]$ImportModules,
        [switch]$ImportFunctions,

        [int]$Throttle = 20,
        [int]$SleepTimer = 200,
        [int]$RunspaceTimeout = 0,
        [switch]$NoCloseOnTimeout = $false,
        [int]$MaxQueue,

        [validatescript({Test-Path (Split-Path $_ -parent)})]
        [switch] $AppendLog = $false,
        [string]$LogFile,

        [switch] $Quiet = $false
    )
    begin {
        #No max queue specified?  Estimate one.
        #We use the script scope to resolve an odd PowerShell 2 issue where MaxQueue isn't seen later in the function
        if( -not $PSBoundParameters.ContainsKey('MaxQueue') ) {
            if($RunspaceTimeout -ne 0){ $script:MaxQueue = $Throttle }
            else{ $script:MaxQueue = $Throttle * 3 }
        }
        else {
            $script:MaxQueue = $MaxQueue
        }
        $ProgressId = Get-Random
        Write-Verbose "Throttle: '$throttle' SleepTimer '$sleepTimer' runSpaceTimeout '$runspaceTimeout' maxQueue '$maxQueue' logFile '$logFile'"

        #If they want to import variables or modules, create a clean runspace, get loaded items, use those to exclude items
        if ($ImportVariables -or $ImportModules -or $ImportFunctions) {
            $StandardUserEnv = [powershell]::Create().addscript({

                #Get modules, snapins, functions in this clean runspace
                $Modules = Get-Module | Select-Object -ExpandProperty Name
                $Snapins = Get-PSSnapin | Select-Object -ExpandProperty Name
                $Functions = Get-ChildItem function:\ | Select-Object -ExpandProperty Name

                #Get variables in this clean runspace
                #Called last to get vars like $? into session
                $Variables = Get-Variable | Select-Object -ExpandProperty Name

                #Return a hashtable where we can access each.
                @{
                    Variables   = $Variables
                    Modules     = $Modules
                    Snapins     = $Snapins
                    Functions   = $Functions
                }
            }).invoke()[0]

            if ($ImportVariables) {
                #Exclude common parameters, bound parameters, and automatic variables
                Function _temp {[cmdletbinding(SupportsShouldProcess=$True)] param() }
                $VariablesToExclude = @( (Get-Command _temp | Select-Object -ExpandProperty parameters).Keys + $PSBoundParameters.Keys + $StandardUserEnv.Variables )
                Write-Verbose "Excluding variables $( ($VariablesToExclude | Sort-Object ) -join ", ")"

                # we don't use 'Get-Variable -Exclude', because it uses regexps.
                # One of the veriables that we pass is '$?'.
                # There could be other variables with such problems.
                # Scope 2 required if we move to a real module
                $UserVariables = @( Get-Variable | Where-Object { -not ($VariablesToExclude -contains $_.Name) } )
                Write-Verbose "Found variables to import: $( ($UserVariables | Select-Object -expandproperty Name | Sort-Object ) -join ", " | Out-String).`n"
            }
            if ($ImportModules) {
                $UserModules = @( Get-Module | Where-Object {$StandardUserEnv.Modules -notcontains $_.Name -and (Test-Path $_.Path -ErrorAction SilentlyContinue)} | Select-Object -ExpandProperty Path )
                $UserSnapins = @( Get-PSSnapin | Select-Object -ExpandProperty Name | Where-Object {$StandardUserEnv.Snapins -notcontains $_ } )
            }
            if($ImportFunctions) {
                $UserFunctions = @( Get-ChildItem function:\ | Where-Object { $StandardUserEnv.Functions -notcontains $_.Name } )
            }
        }

        #region functions
            Function Get-RunspaceData {
                [cmdletbinding()]
                param( [switch]$Wait )
                #loop through runspaces
                #if $wait is specified, keep looping until all complete
                Do {
                    #set more to false for tracking completion
                    $more = $false

                    #Progress bar if we have inputobject count (bound parameter)
                    if (-not $Quiet) {
                        Write-Progress -Id $ProgressId -Activity "Running Query" -Status "Starting threads"`
                            -CurrentOperation "$startedCount threads defined - $totalCount input objects - $script:completedCount input objects processed"`
                            -PercentComplete $( Try { $script:completedCount / $totalCount * 100 } Catch {0} )
                    }

                    #run through each runspace.
                    Foreach($runspace in $runspaces) {

                        #get the duration - inaccurate
                        $currentdate = Get-Date
                        $runtime = $currentdate - $runspace.startTime
                        $runMin = [math]::Round( $runtime.totalminutes ,2 )

                        #set up log object
                        $log = "" | Select-Object Date, Action, Runtime, Status, Details
                        $log.Action = "Removing:'$($runspace.object)'"
                        $log.Date = $currentdate
                        $log.Runtime = "$runMin minutes"

                        #If runspace completed, end invoke, dispose, recycle, counter++
                        If ($runspace.Runspace.isCompleted) {

                            $script:completedCount++

                            #check if there were errors
                            if($runspace.powershell.Streams.Error.Count -gt 0) {
                                #set the logging info and move the file to completed
                                $log.status = "CompletedWithErrors"
                                Write-Verbose ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1]
                                foreach($ErrorRecord in $runspace.powershell.Streams.Error) {
                                    Write-Error -ErrorRecord $ErrorRecord
                                }
                            }
                            else {
                                #add logging details and cleanup
                                $log.status = "Completed"
                                Write-Verbose ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1]
                            }

                            #everything is logged, clean up the runspace
                            $runspace.powershell.EndInvoke($runspace.Runspace)
                            $runspace.powershell.dispose()
                            $runspace.Runspace = $null
                            $runspace.powershell = $null
                        }
                        #If runtime exceeds max, dispose the runspace
                        ElseIf ( $runspaceTimeout -ne 0 -and $runtime.totalseconds -gt $runspaceTimeout) {
                            $script:completedCount++
                            $timedOutTasks = $true

                            #add logging details and cleanup
                            $log.status = "TimedOut"
                            Write-Verbose ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1]
                            Write-Error "Runspace timed out at $($runtime.totalseconds) seconds for the object:`n$($runspace.object | out-string)"

                            #Depending on how it hangs, we could still get stuck here as dispose calls a synchronous method on the powershell instance
                            if (!$noCloseOnTimeout) { $runspace.powershell.dispose() }
                            $runspace.Runspace = $null
                            $runspace.powershell = $null
                            $completedCount++
                        }

                        #If runspace isn't null set more to true
                        ElseIf ($runspace.Runspace -ne $null ) {
                            $log = $null
                            $more = $true
                        }

                        #log the results if a log file was indicated
                        if($logFile -and $log) {
                            ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1] | out-file $LogFile -append
                        }
                    }

                    #Clean out unused runspace jobs
                    $temphash = $runspaces.clone()
                    $temphash | Where-Object { $_.runspace -eq $Null } | ForEach-Object {
                        $Runspaces.remove($_)
                    }

                    #sleep for a bit if we will loop again
                    if($PSBoundParameters['Wait']){ Start-Sleep -milliseconds $SleepTimer }

                #Loop again only if -wait parameter and there are more runspaces to process
                } while ($more -and $PSBoundParameters['Wait'])

            #End of runspace function
            }
        #endregion functions

        #region Init

            if($PSCmdlet.ParameterSetName -eq 'ScriptFile') {
                $ScriptBlock = [scriptblock]::Create( $(Get-Content $ScriptFile | out-string) )
            }
            elseif($PSCmdlet.ParameterSetName -eq 'ScriptBlock') {
                #Start building parameter names for the param block
                [string[]]$ParamsToAdd = '$_'
                if( $PSBoundParameters.ContainsKey('Parameter') ) {
                    $ParamsToAdd += '$Parameter'
                }

                $UsingVariableData = $Null

                # This code enables $Using support through the AST.
                # This is entirely from  Boe Prox, and his https://github.com/proxb/PoshRSJob module; all credit to Boe!

                if($PSVersionTable.PSVersion.Major -gt 2) {
                    #Extract using references
                    $UsingVariables = $ScriptBlock.ast.FindAll({$args[0] -is [System.Management.Automation.Language.UsingExpressionAst]},$True)

                    If ($UsingVariables) {
                        $List = New-Object 'System.Collections.Generic.List`1[System.Management.Automation.Language.VariableExpressionAst]'
                        ForEach ($Ast in $UsingVariables) {
                            [void]$list.Add($Ast.SubExpression)
                        }

                        $UsingVar = $UsingVariables | Group-Object -Property SubExpression | ForEach-Object {$_.Group | Select-Object -First 1}

                        #Extract the name, value, and create replacements for each
                        $UsingVariableData = ForEach ($Var in $UsingVar) {
                            try {
                                $Value = Get-Variable -Name $Var.SubExpression.VariablePath.UserPath -ErrorAction Stop
                                [pscustomobject]@{
                                    Name = $Var.SubExpression.Extent.Text
                                    Value = $Value.Value
                                    NewName = ('$__using_{0}' -f $Var.SubExpression.VariablePath.UserPath)
                                    NewVarName = ('__using_{0}' -f $Var.SubExpression.VariablePath.UserPath)
                                }
                            }
                            catch {
                                Write-Error "$($Var.SubExpression.Extent.Text) is not a valid Using: variable!"
                            }
                        }
                        $ParamsToAdd += $UsingVariableData | Select-Object -ExpandProperty NewName -Unique

                        $NewParams = $UsingVariableData.NewName -join ', '
                        $Tuple = [Tuple]::Create($list, $NewParams)
                        $bindingFlags = [Reflection.BindingFlags]"Default,NonPublic,Instance"
                        $GetWithInputHandlingForInvokeCommandImpl = ($ScriptBlock.ast.gettype().GetMethod('GetWithInputHandlingForInvokeCommandImpl',$bindingFlags))

                        $StringScriptBlock = $GetWithInputHandlingForInvokeCommandImpl.Invoke($ScriptBlock.ast,@($Tuple))

                        $ScriptBlock = [scriptblock]::Create($StringScriptBlock)

                        Write-Verbose $StringScriptBlock
                    }
                }

                $ScriptBlock = $ExecutionContext.InvokeCommand.NewScriptBlock("param($($ParamsToAdd -Join ", "))`r`n" + $Scriptblock.ToString())
            }
            else {
                Throw "Must provide ScriptBlock or ScriptFile"; Break
            }

            Write-Debug "`$ScriptBlock: $($ScriptBlock | Out-String)"
            Write-Verbose "Creating runspace pool and session states"

            #If specified, add variables and modules/snapins to session state
            $sessionstate = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
            if($ImportVariables -and $UserVariables.count -gt 0) {
                foreach($Variable in $UserVariables) {
                    $sessionstate.Variables.Add((New-Object -TypeName System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList $Variable.Name, $Variable.Value, $null) )
                }
            }
            if ($ImportModules) {
                if($UserModules.count -gt 0) {
                    foreach($ModulePath in $UserModules) {
                        $sessionstate.ImportPSModule($ModulePath)
                    }
                }
                if($UserSnapins.count -gt 0) {
                    foreach($PSSnapin in $UserSnapins) {
                        [void]$sessionstate.ImportPSSnapIn($PSSnapin, [ref]$null)
                    }
                }
            }
            if($ImportFunctions -and $UserFunctions.count -gt 0) {
                foreach ($FunctionDef in $UserFunctions) {
                    $sessionstate.Commands.Add((New-Object System.Management.Automation.Runspaces.SessionStateFunctionEntry -ArgumentList $FunctionDef.Name,$FunctionDef.ScriptBlock))
                }
            }

            #Create runspace pool
            $runspacepool = [runspacefactory]::CreateRunspacePool(1, $Throttle, $sessionstate, $Host)
            $runspacepool.Open()

            Write-Verbose "Creating empty collection to hold runspace jobs"
            $Script:runspaces = New-Object System.Collections.ArrayList

            #If inputObject is bound get a total count and set bound to true
            $bound = $PSBoundParameters.keys -contains "InputObject"
            if(-not $bound) {
                [System.Collections.ArrayList]$allObjects = @()
            }

            #Set up log file if specified
            if( $LogFile -and (-not (Test-Path $LogFile) -or $AppendLog -eq $false)){
                New-Item -ItemType file -Path $logFile -Force | Out-Null
                ("" | Select-Object -Property Date, Action, Runtime, Status, Details | ConvertTo-Csv -NoTypeInformation -Delimiter ";")[0] | Out-File $LogFile
            }

            #write initial log entry
            $log = "" | Select-Object -Property Date, Action, Runtime, Status, Details
                $log.Date = Get-Date
                $log.Action = "Batch processing started"
                $log.Runtime = $null
                $log.Status = "Started"
                $log.Details = $null
                if($logFile) {
                    ($log | convertto-csv -Delimiter ";" -NoTypeInformation)[1] | Out-File $LogFile -Append
                }
            $timedOutTasks = $false
        #endregion INIT
    }
    process {
        #add piped objects to all objects or set all objects to bound input object parameter
        if($bound) {
            $allObjects = $InputObject
        }
        else {
            [void]$allObjects.add( $InputObject )
        }
    }
    end {
        #Use Try/Finally to catch Ctrl+C and clean up.
        try {
            #counts for progress
            $totalCount = $allObjects.count
            $script:completedCount = 0
            $startedCount = 0
            foreach($object in $allObjects) {
                #region add scripts to runspace pool
                    #Create the powershell instance, set verbose if needed, supply the scriptblock and parameters
                    $powershell = [powershell]::Create()

                    if ($VerbosePreference -eq 'Continue') {
                        [void]$PowerShell.AddScript({$VerbosePreference = 'Continue'})
                    }

                    [void]$PowerShell.AddScript($ScriptBlock).AddArgument($object)

                    if ($parameter) {
                        [void]$PowerShell.AddArgument($parameter)
                    }

                    # $Using support from Boe Prox
                    if ($UsingVariableData) {
                        Foreach($UsingVariable in $UsingVariableData) {
                            Write-Verbose "Adding $($UsingVariable.Name) with value: $($UsingVariable.Value)"
                            [void]$PowerShell.AddArgument($UsingVariable.Value)
                        }
                    }

                    #Add the runspace into the powershell instance
                    $powershell.RunspacePool = $runspacepool

                    #Create a temporary collection for each runspace
                    $temp = "" | Select-Object PowerShell, StartTime, object, Runspace
                    $temp.PowerShell = $powershell
                    $temp.StartTime = Get-Date
                    $temp.object = $object

                    #Save the handle output when calling BeginInvoke() that will be used later to end the runspace
                    $temp.Runspace = $powershell.BeginInvoke()
                    $startedCount++

                    #Add the temp tracking info to $runspaces collection
                    Write-Verbose ( "Adding {0} to collection at {1}" -f $temp.object, $temp.starttime.tostring() )
                    $runspaces.Add($temp) | Out-Null

                    #loop through existing runspaces one time
                    Get-RunspaceData

                    #If we have more running than max queue (used to control timeout accuracy)
                    #Script scope resolves odd PowerShell 2 issue
                    $firstRun = $true
                    while ($runspaces.count -ge $Script:MaxQueue) {
                        #give verbose output
                        if($firstRun) {
                            Write-Verbose "$($runspaces.count) items running - exceeded $Script:MaxQueue limit."
                        }
                        $firstRun = $false

                        #run get-runspace data and sleep for a short while
                        Get-RunspaceData
                        Start-Sleep -Milliseconds $sleepTimer
                    }
                #endregion add scripts to runspace pool
            }
            Write-Verbose ( "Finish processing the remaining runspace jobs: {0}" -f ( @($runspaces | Where-Object {$_.Runspace -ne $Null}).Count) )

            Get-RunspaceData -wait
            if (-not $quiet) {
                Write-Progress -Id $ProgressId -Activity "Running Query" -Status "Starting threads" -Completed
            }
        }
        finally {
            #Close the runspace pool, unless we specified no close on timeout and something timed out
            if ( ($timedOutTasks -eq $false) -or ( ($timedOutTasks -eq $true) -and ($noCloseOnTimeout -eq $false) ) ) {
                Write-Verbose "Closing the runspace pool"
                $runspacepool.close()
            }
            #collect garbage
            [gc]::Collect()
        }
    }
}
tools\dbatools\internal\functions\Invoke-SmoCheck.ps1
function Invoke-SmoCheck {
    <#
    .SYNOPSIS
    Checks for PowerShell SMO version vs SQL Server's SMO version.

#>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [object]$SqlInstance
    )

    if ($script:smocheck -ne $true) {
        $script:smocheck = $true
        $smo = (([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.Fullname -like "Microsoft.SqlServer.SMO,*" }).FullName -Split ", ")[1]
        $smo = ([version]$smo.TrimStart("Version=")).Major
        $serverversion = $SqlInstance.version.major

        if ($serverversion - $smo -gt 1) {
            Write-Warning "Your version of SMO is $smo, which is significantly older than $($SqlInstance.name)'s version $($SqlInstance.version.major)."
            Write-Warning "This may present an issue when migrating certain portions of SQL Server."
            Write-Warning "If you encounter issues, consider upgrading SMO."
        }
    }
}
tools\dbatools\internal\functions\Invoke-SteppablePipeline.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Invoke-SteppablePipeline
{
<#
    .SYNOPSIS
        Allows using steppable pipelines on the pipeline.
    
    .DESCRIPTION
        Allows using steppable pipelines on the pipeline.
        
    .PARAMETER InputObject
        The object(s) to process
        Should only receive input from the pipeline!
    
    .PARAMETER Pipeline
        The pipeline to execute
    
    .EXAMPLE
        PS C:\> Get-ChildItem | Invoke-SteppablePipeline -Pipeline $steppablePipeline
    
        Processes the object returned by Get-ChildItem in the pipeline defined
#>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        $InputObject,
        
        [Parameter(Mandatory = $true)]
        $Pipeline
    )
    
    process
    {
        $Pipeline.Process($InputObject)
    }
}
tools\dbatools\internal\functions\Invoke-TagCommand.ps1
function Invoke-TagCommand ([string]$Tag, [string]$Keyword) {
    <#
.SYNOPSIS
    An internal command, feel free to ignore.

.EXAMPLE
    Tag-Command -Tag Restore -Keyword Restore
    Tag-Command -Tag Backup -Keyword Backup
    Tag-Command -Tag Orphan -Keyword Orphan
    Tag-Command -Tag DisasterRecovery -Keyword Attach
    Tag-Command -Tag DisasterRecovery -Keyword Detach
    Tag-Command -Tag Snapshot -Keyword Snapshot
    Tag-Command -Tag Memory -Keyword Memory
    Tag-Command -Tag DisasterRecovery -Keyword Restore
    Tag-Command -Tag DisasterRecovery -Keyword Backup
    Tag-Command -Tag Storage -Keyword disk
    Tag-Command -Tag Storage -Keyword storage
    Tag-Command -Tag Migration -Keyword "Copy-"
    Tag-Command -Tag SPN -Keyword Kerberos
    Tag-Command -Tag SPN -Keyword SPN
    Tag-Command -Tag CIM -Keyword CimSession
    Tag-Command -Tag SQLWMI -Keyword Invoke-ManagedComputerCommand
    Tag-Command -Tag WSMan -Keyword Invoke-Command

#>

    $tagsRex = ([regex]'(?m)^[\s]{0,15}Tags:(.*)$')
    $modulepath = (Get-Module -Name dbatools).Path
    $directory = Split-Path $modulepath
    $basedir = "$directory\functions\"
    Import-Module $modulepath -force
    $allfiles = Get-ChildItem $basedir
    foreach ($f in $allfiles) {
        if ($f -eq "Find-DbaCommand.ps1") { continue }

        $content = Get-Content $f.fullname
        if ($content -like "*$keyword*") {
            Write-Message -Level Warning -Message "$f needs a tag tag"
            $cmdname = $f.name.replace('.ps1', '')

            $fullhelp = get-help $cmdname -full

            $as = $fullhelp.alertset | out-string

            $tags = $tagsrex.Match($as).Groups[1].Value

            if ($tags) {
                $tags = $tags.ToString().split(',').Trim()
                Write-Message -Level Warning -Message "adding tags to existing ones"
                if ($tag -in $tags) {
                    Write-Message -Level Warning -Message "tag $tag is already present"
                    continue
                }
                $out = @()
                foreach ($line in $content) {
                    if ($line.trim().startsWith('Tags:')) {
                        $out += "$line, $tag"
                    }
                    else {
                        $out += $line
                    }
                }
                Write-Message -Level Warning -Message "replacing content into $($f.fullname)"
                $out -join "`r`n" | Set-Content $f.fullname -Encoding UTF8

            }
            else {
                Write-Message -Level Warning -Message "need to add tags"
                $out = @()
                foreach ($line in $content) {
                    if ($line.startsWith('.NOTES')) {
                        $out += '.NOTES'
                        $out += "Tags: $tag"
                    }
                    else {
                        $out += $line
                    }
                }
                Write-Message -Level Warning -Message "replacing content into $($f.fullname)"
                $out -join "`r`n" | Set-Content $f.fullname -Encoding UTF8
            }
        }
    }
}
tools\dbatools\internal\functions\Join-AdminUnc.ps1
function Join-AdminUnc {
    <#
    .SYNOPSIS
    Internal function. Parses a path to make it an admin UNC.
#>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$servername,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$filepath

    )

    if (!$filepath) { return }
    if ($filepath.StartsWith("\\")) { return $filepath }

    $servername = $servername.Split("\")[0]

    if ($filepath.length -gt 0 -and $filepath -ne [System.DbNull]::Value) {
        $newpath = Join-Path "\\$servername\" $filepath.replace(':', '$')
        return $newpath
    }
    else { return }
}
tools\dbatools\internal\functions\New-DbaLogShippingPrimaryDatabase.ps1
function New-DbaLogShippingPrimaryDatabase {
    <#
        .SYNOPSIS
            New-DbaLogShippingPrimaryDatabase add the primary database to log shipping

        .DESCRIPTION
            New-DbaLogShippingPrimaryDatabase will add the primary database to log shipping.
            This is executed on the primary server.

        .PARAMETER SqlInstance
            SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER Database
            Database to set up log shipping for.

        .PARAMETER BackupDirectory
            Is the path to the backup folder on the primary server.

        .PARAMETER BackupJob
            Is the name of the SQL Server Agent job on the primary server that copies the backup into the backup folder.

        .PARAMETER BackupJobID
            The SQL Server Agent job ID associated with the backup job on the primary server.

        .PARAMETER BackupRetention
            Is the length of time, in minutes, to retain the log backup file in the backup directory on the primary server.

        .PARAMETER BackupShare
            Is the network path to the backup directory on the primary server.

        .PARAMETER BackupThreshold
            Is the length of time, in minutes, after the last backup before a threshold_alert error is raised.
            The default is 60.

        .PARAMETER CompressBackup
            Enables the use of backup compression

        .PARAMETER ThressAlert
            Is the length of time, in minutes, when the alert is to be raised when the backup threshold is exceeded.
            The default is 14,420.

        .PARAMETER HistoryRetention
            Is the length of time in minutes in which the history will be retained.
            The default is 14420.

        .PARAMETER MonitorServer
            Is the name of the monitor server.
            The default is the name of the primary server.

        .PARAMETER MonitorCredential
            Allows you to login to enter a secure credential.
            This is only needed in combination with MonitorServerSecurityMode having either a 0 or 'sqlserver' value.
            To use: $scred = Get-Credential, then pass $scred object to the -MonitorCredential parameter.

        .PARAMETER MonitorServerSecurityMode
            The security mode used to connect to the monitor server. Allowed values are 0, "sqlserver", 1, "windows"
            The default is 1 or Windows.

        .PARAMETER ThresholdAlertEnabled
            Specifies whether an alert will be raised when backup threshold is exceeded.
            The default is 0.

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER Force
            The force parameter will ignore some errors in the parameters and assume defaults.
            It will also remove the any present schedules with the same name for the specific job.

        .NOTES
            Author: Sander Stad (@sqlstad, sqlstad.nl)
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            New-DbaLogShippingPrimaryDatabase -SqlInstance sql1 -Database DB1 -BackupDirectory D:\data\logshipping -BackupJob LSBackup_DB1 -BackupRetention 4320 -BackupShare "\\sql1\logshipping" -BackupThreshold 60 -CompressBackup -HistoryRetention 14420 -MonitorServer sql1 -ThresholdAlertEnabled

    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]

    param (
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [object]$SqlInstance,

        [System.Management.Automation.PSCredential]
        $SqlCredential,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [object]$Database,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$BackupDirectory,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$BackupJob,

        [Parameter(Mandatory = $true)]
        [int]$BackupRetention,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$BackupShare,

        [int]$BackupThreshold = 60,

        [switch]$CompressBackup,

        [int]$ThressAlert = 14420,

        [int]$HistoryRetention = 14420,

        [string]$MonitorServer,

        [ValidateSet(0, "sqlserver", 1, "windows")]
        [object]$MonitorServerSecurityMode = 1,

        [System.Management.Automation.PSCredential]
        $MonitorCredential,

        [switch]$ThresholdAlertEnabled,

        [Alias('Silent')]
        [switch]$EnableException,

        [switch]$Force
    )

    # Try connecting to the instance
    Write-Message -Message "Connecting to $SqlInstance" -Level Verbose
    try {
        $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
    }
    catch {
        Stop-Function -Message "Could not connect to Sql Server instance" -Target $SqlInstance -Continue
    }

    # Check if the backup UNC path is correct and reachable
    if ([bool]([uri]$BackupShare).IsUnc -and $BackupShare -notmatch '^\\(?:\\[^<>:`"/\\|?*]+)+$') {
        Stop-Function -Message "The backup share path $BackupShare should be formatted in the form \\server\share." -Target $SqlInstance
        return
    }
    else {
        if (-not ((Test-Path $BackupShare -PathType Container -IsValid) -and ((Get-Item $BackupShare).PSProvider.Name -eq 'FileSystem'))) {
            Stop-Function -Message "The backup share path $BackupShare is not valid or can't be reached." -Target $SqlInstance
            return
        }
    }

    # Check the backup compression
    if ($CompressBackup -eq $true) {
        Write-Message -Message "Setting backup compression to 1." -Level Verbose
        $BackupCompression = 1
    }
    elseif ($CompressBackup -eq $false) {
        Write-Message -Message "Setting backup compression to 0." -Level Verbose
        $BackupCompression = 0
    }
    elseif (-not $CompressBackup) {
        $defaultCompression = (Get-DbaSpConfigure -SqlInstance $SqlInstance -ConfigName DefaultBackupCompression).ConfiguredValue
        Write-Message -Message "Setting backup compression to default value $defaultCompression." -Level Verbose
        $BackupCompression = $defaultCompression

    }

    # Check of the MonitorServerSecurityMode value is of type string and set the integer value
    if ($MonitorServerSecurityMode -notin 0, 1) {
        $MonitorServerSecurityMode = switch ($MonitorServerSecurityMode) {"WINDOWS" { 1 } "SQLSERVER" { 0 } }
        Write-Message -Message "Setting monitor server security mode to $MonitorServerSecurityMode." -Level Verbose
    }

    # Check the MonitorServer
    if ($Force -and -not $MonitorServer) {
        $MonitorServer = $SqlInstance
        Write-Message -Message "Setting monitor server to $MonitorServer." -Level Verbose
    }

    # Check the MonitorServerSecurityMode if it's SQL Server authentication
    if ($MonitorServerSecurityMode -eq 0 -and -not $MonitorCredential) {
        Stop-Function -Message "The MonitorServerCredential cannot be empty when using SQL Server authentication." -Target $SqlInstance
        return
    }
    elseif ($MonitorServerSecurityMode -eq 0 -and $MonitorCredential) {
        # Get the username and password from the credential
        $MonitorLogin = $MonitorCredential.UserName
        $MonitorPassword = $MonitorCredential.GetNetworkCredential().Password

        # Check if the user is in the database
        if ($server.Databases['master'].Users.Name -notcontains $MonitorLogin) {
            Stop-Function -Message "User $MonitorLogin for monitor login must be in the master database." -Target $SqlInstance
            return
        }
    }

    # Check if the database is present on the source sql server
    if ($server.Databases.Name -notcontains $Database) {
        Stop-Function -Message "Database $Database is not available on instance $SqlInstance" -Target $SqlInstance
        return
    }

    # Check the if Threshold alert needs to be enabled
    if ($ThresholdAlertEnabled) {
        [int]$ThresholdAlertEnabled = 1
        Write-Message -Message "Setting Threshold alert to $ThresholdAlertEnabled." -Level Verbose
    }
    else {
        [int]$ThresholdAlertEnabled = 0
        Write-Message -Message "Setting Threshold alert to $ThresholdAlertEnabled." -Level Verbose
    }

    # Set the log shipping primary
    $Query = "
        DECLARE @LS_BackupJobId AS uniqueidentifier;
        DECLARE @LS_PrimaryId AS uniqueidentifier;
        EXEC master.sys.sp_add_log_shipping_primary_database
            @database = N'$Database'
            ,@backup_directory = N'$BackupDirectory'
            ,@backup_share = N'$BackupShare'
            ,@backup_job_name = N'$BackupJob'
            ,@backup_retention_period = $BackupRetention"

    if ($SqlInstance.Version.Major -gt 9) {
        $Query += ",@backup_compression = $BackupCompression"
    }

    if ($MonitorServer) {
        $Query += ",@monitor_server = N'$MonitorServer'
            ,@monitor_server_security_mode = $MonitorServerSecurityMode
            ,@threshold_alert = $ThressAlert
            ,@threshold_alert_enabled = $ThresholdAlertEnabled"
    }

    $Query += ",@backup_threshold = $BackupThreshold
            ,@history_retention_period = $HistoryRetention
            ,@backup_job_id = @LS_BackupJobId OUTPUT
            ,@primary_id = @LS_PrimaryId OUTPUT "

    # Check the MonitorServerSecurityMode if it's SQL Server authentication
    if ($MonitorServer -and $MonitorServerSecurityMode -eq 0 ) {
        $Query += ",@monitor_server_login = N'$MonitorLogin'
            ,@monitor_server_password = N'$MonitorPassword' "
    }

    if ($server.Version.Major -gt 9) {
        $Query += ",@overwrite = 1;"
    }
    else {
        $Query += ";"
    }

    # Execute the query to add the log shipping primary
    if ($PSCmdlet.ShouldProcess($SqlServer, ("Configuring logshipping for primary database $Database on $SqlInstance"))) {
        try {
            Write-Message -Message "Configuring logshipping for primary database $Database." -Level Output
            Write-Message -Message "Executing query:`n$Query" -Level Verbose
            $server.Query($Query)
        }
        catch {
            Write-Message -Message "$($_.Exception.InnerException.InnerException.InnerException.InnerException.Message)" -Level Warning
            Stop-Function -Message "Error executing the query.`n$($_.Exception.Message)`n$($Query)" -ErrorRecord $_ -Target $SqlInstance -Continue
        }
    }

    Write-Message -Message "Finished adding the primary database $Database to log shipping." -Level Output

}
tools\dbatools\internal\functions\New-DbaLogShippingPrimarySecondary.ps1
function New-DbaLogShippingPrimarySecondary {
    <#
        .SYNOPSIS
            New-DbaLogShippingPrimarySecondary adds an entry for a secondary database.

        .DESCRIPTION
            New-DbaLogShippingPrimarySecondary adds an entry for a secondary database.
            This is executed on the primary server.

        .PARAMETER SqlInstance
            SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER PrimaryDatabase
            Is the name of the database on the primary server.

        .PARAMETER SecondaryDatabase
            Is the name of the secondary database.

        .PARAMETER SecondaryServer
            Is the name of the secondary server.

        .PARAMETER SecondarySqlCredential
            Allows you to login to servers using SQL Logins as opposed to Windows Auth/Integrated/Trusted. To use:
            $scred = Get-Credential, then pass $scred object to the -SecondarySqlCredential parameter.

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .NOTES
            Author: Sander Stad (@sqlstad, sqlstad.nl)
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/New-DbaLogShippingPrimarySecondary

        .EXAMPLE
            New-DbaLogShippingPrimarySecondary -SqlInstance sql1 -PrimaryDatabase DB1 -SecondaryServer sql2 -SecondaryDatabase DB1_DR
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param (
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [object]$PrimaryDatabase,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [object]$SecondaryDatabase,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [DBAInstanceParameter]$SecondaryServer,
        [PSCredential]$SecondarySqlCredential,
        [Alias('Silent')]
        [switch]$EnableException
    )

    # Try connecting to the instance
    Write-Message -Message "Connecting to $SqlInstance" -Level Verbose
    try {
        $ServerPrimary = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
    }
    catch {
        Stop-Function -Message "Could not connect to Sql Server instance" -Target $SqlInstance -Continue
    }

    # Try connecting to the instance
    Write-Message -Message "Connecting to $SecondaryServer" -Level Verbose
    try {
        $ServerSecondary = Connect-SqlInstance -SqlInstance $SecondaryServer -SqlCredential $SecondarySqlCredential
    }
    catch {
        Stop-Function -Message "Could not connect to Sql Server instance" -Target $SecondaryServer -Continue
    }

    # Check if the database is present on the source sql server
    if ($ServerPrimary.Databases.Name -notcontains $PrimaryDatabase) {
        Stop-Function -Message "Database $PrimaryDatabase is not available on instance $SqlInstance" -ErrorRecord $_ -Target $SqlInstance -Continue
    }

    # Check if the database is present on the destination sql server
    if ($ServerSecondary.Databases.Name -notcontains $SecondaryDatabase) {
        Stop-Function -Message "Database $SecondaryDatabase is not available on instance $SecondaryServer" -ErrorRecord $_ -Target $SecondaryServer -Continue
    }

    $Query = "SELECT primary_database FROM msdb.dbo.log_shipping_primary_databases WHERE primary_database = '$PrimaryDatabase'"

    try {
        Write-Message -Message "Executing query:`n$Query" -Level Verbose
        $Result = $ServerPrimary.Query($Query)
        if ($Result.Count -eq 0 -or $Result[0] -ne $PrimaryDatabase) {
            Stop-Function -Message "Database $PrimaryDatabase does not exist as log shipping primary.`nPlease run New-DbaLogShippingPrimaryDatabase first."  -ErrorRecord $_ -Target $SqlInstance -Continue
        }
    }
    catch {
        Stop-Function -Message "Error executing the query.`n$($_.Exception.Message)`n$Query" -ErrorRecord $_ -Target $SqlInstance -Continue
    }

    # Set the query for the log shipping primary and secondary
    $Query = "EXEC master.sys.sp_add_log_shipping_primary_secondary
        @primary_database = N'$PrimaryDatabase'
        ,@secondary_server = N'$SecondaryServer'
        ,@secondary_database = N'$SecondaryDatabase' "

    if ($ServerPrimary.Version.Major -gt 9) {
        $Query += ",@overwrite = 1;"
    }
    else {
        $Query += ";"
    }

    # Execute the query to add the log shipping primary
    if ($PSCmdlet.ShouldProcess($SqlInstance, ("Configuring logshipping connecting the primary database $PrimaryDatabase to secondary database $SecondaryDatabase on $SqlInstance"))) {
        try {
            Write-Message -Message "Configuring logshipping connecting the primary database $PrimaryDatabase to secondary database $SecondaryDatabase on $SqlInstance." -Level Output
            Write-Message -Message "Executing query:`n$Query" -Level Verbose
            $ServerPrimary.Query($Query)
        }
        catch {
            Write-Message -Message "$($_.Exception.InnerException.InnerException.InnerException.InnerException.Message)" -Level Warning
            Stop-Function -Message "Error executing the query.`n$($_.Exception.Message)`n$Query" -ErrorRecord $_ -Target $SqlInstance -Continue
        }
    }

    Write-Message -Message "Finished configuring of primary database $PrimaryDatabase to secondary database $SecondaryDatabase." -Level Output

}
tools\dbatools\internal\functions\New-DbaLogShippingSecondaryDatabase.ps1
function New-DbaLogShippingSecondaryDatabase {
    <#
        .SYNOPSIS
            New-DbaLogShippingSecondaryDatabase sets up a secondary databases for log shipping.

        .DESCRIPTION
            New-DbaLogShippingSecondaryDatabase sets up a secondary databases for log shipping.
            This is executed on the secondary server.

        .PARAMETER SqlInstance
            SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER BufferCount
            The total number of buffers used by the backup or restore operation.
            The default is -1.

        .PARAMETER BlockSize
            The size, in bytes, that is used as the block size for the backup device.
            The default is -1.

        .PARAMETER DisconnectUsers
            If set to 1, users are disconnected from the secondary database when a restore operation is performed.
            Te default is 0.

        .PARAMETER HistoryRetention
            Is the length of time in minutes in which the history is retained.
            The default is 14420.

        .PARAMETER MaxTransferSize
            The size, in bytes, of the maximum input or output request which is issued by SQL Server to the backup device.

        .PARAMETER PrimaryServer
            The name of the primary instance of the Microsoft SQL Server Database Engine in the log shipping configuration.

        .PARAMETER PrimaryDatabase
            Is the name of the database on the primary server.

        .PARAMETER RestoreAll
            If set to 1, the secondary server restores all available transaction log backups when the restore job runs.
            The default is 1.

        .PARAMETER RestoreDelay
            The amount of time, in minutes, that the secondary server waits before restoring a given backup file.
            The default is 0.

        .PARAMETER RestoreMode
            The restore mode for the secondary database. The default is 0.
            0 = Restore log with NORECOVERY.
            1 = Restore log with STANDBY.

        .PARAMETER RestoreThreshold
            The number of minutes allowed to elapse between restore operations before an alert is generated.

        .PARAMETER SecondaryDatabase
            Is the name of the secondary database.

        .PARAMETER ThresholdAlert
            Is the alert to be raised when the backup threshold is exceeded.
            The default is 14420.

        .PARAMETER ThresholdAlertEnabled
            Specifies whether an alert is raised when backup_threshold is exceeded.

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER Force
            The force parameter will ignore some errors in the parameters and assume defaults.
            It will also remove the any present schedules with the same name for the specific job.

        .NOTES
            Author: Sander Stad (@sqlstad, sqlstad.nl)
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
            New-DbaLogShippingSecondaryDatabase -SqlInstance sql2 -SecondaryDatabase DB1_DR -PrimaryServer sql1 -PrimaryDatabase DB1 -RestoreDelay 0 -RestoreMode standby -DisconnectUsers -RestoreThreshold 45 -ThresholdAlertEnabled -HistoryRetention 14420
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]

    param (
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [DbaInstanceParameter]$SqlInstance,
        [PSCredential]$SqlCredential,
        [int]$BufferCount = -1,
        [int]$BlockSize = -1,
        [switch]$DisconnectUsers,
        [int]$HistoryRetention = 14420,
        [int]$MaxTransferSize,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [DbaInstanceParameter]$PrimaryServer,
        [PSCredential]$PrimarySqlCredential,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [object]$PrimaryDatabase,
        [int]$RestoreAll = 1,
        [int]$RestoreDelay = 0,
        [ValidateSet(0, 'NoRecovery', 1, 'Standby')]
        [object]$RestoreMode = 0,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [int]$RestoreThreshold,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [object]$SecondaryDatabase,
        [int]$ThresholdAlert = 14420,
        [switch]$ThresholdAlertEnabled,
        [Alias('Silent')]
        [switch]$EnableException,
        [switch]$Force
    )

    # Try connecting to the instance
    Write-Message -Message "Connecting to $SqlInstance" -Level Verbose
    try {
        $ServerSecondary = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
    }
    catch {
        Stop-Function -Message "Failure" -Category ConnectionError -Target $SqlInstance -ErrorRecord $_ -Continue
    }

    # Try connecting to the instance
    Write-Message -Message "Connecting to $PrimaryServer" -Level Verbose
    try {
        $ServerPrimary = Connect-SqlInstance -SqlInstance $PrimaryServer -SqlCredential $PrimarySqlCredential
    }
    catch {
        Stop-Function -Message "Failure" -Category ConnectionError -Target $PrimaryServer -ErrorRecord $_ -Continue
    }

    # Check if the database is present on the primary sql server
    if ($ServerPrimary.Databases.Name -notcontains $PrimaryDatabase) {
        Stop-Function -Message "Database $PrimaryDatabase is not available on instance $PrimaryServer" -Target $PrimaryServer -Continue
    }

    # Check if the database is present on the primary sql server
    if ($ServerSecondary.Databases.Name -notcontains $SecondaryDatabase) {
        Stop-Function -Message "Database $SecondaryDatabase is not available on instance $ServerSecondary" -Target $SqlInstance -Continue
    }

    # Check the restore mode
    if ($RestoreMode -notin 0, 1) {
        $RestoreMode = switch ($RestoreMode) { "NoRecovery" { 0}  "Standby" { 1 } }
        Write-Message -Message "Setting restore mode to $RestoreMode." -Level Verbose
    }

    # Check the if Threshold alert needs to be enabled
    if ($ThresholdAlertEnabled) {
        [int]$ThresholdAlertEnabled = 1
        Write-Message -Message "Setting Threshold alert to $ThresholdAlertEnabled." -Level Verbose
    }
    else {
        [int]$ThresholdAlertEnabled = 0
        Write-Message -Message "Setting Threshold alert to $ThresholdAlertEnabled." -Level Verbose
    }

    # Checking the option to disconnect users
    if ($DisconnectUsers) {
        [int]$DisconnectUsers = 1
        Write-Message -Message "Setting disconnect users to $DisconnectUsers." -Level Verbose
    }
    else {
        [int]$DisconnectUsers = 0
        Write-Message -Message "Setting disconnect users to $DisconnectUsers." -Level Verbose
    }

    # Check hte combination of the restore mode with the option to disconnect users
    if ($RestoreMode -eq 0 -and $DisconnectUsers -ne 0) {
        if ($Force) {
            [int]$DisconnectUsers = 0
            Write-Message -Message "Illegal combination of database restore mode $RestoreMode and disconnect users $DisconnectUsers. Setting it to $DisconnectUsers." -Level Warning
        }
        else {
            Stop-Function -Message "Illegal combination of database restore mode $RestoreMode and disconnect users $DisconnectUsers." -Target $SqlInstance -Continue
        }
    }

    # Set up the query
    $Query = "EXEC master.sys.sp_add_log_shipping_secondary_database
        @secondary_database = '$SecondaryDatabase'
        ,@primary_server = '$PrimaryServer'
        ,@primary_database = '$PrimaryDatabase'
        ,@restore_delay = $RestoreDelay
        ,@restore_all = $RestoreAll
        ,@restore_mode = $RestoreMode
        ,@disconnect_users = $DisconnectUsers
        ,@restore_threshold = $RestoreThreshold
        ,@threshold_alert = $ThresholdAlert
        ,@threshold_alert_enabled = $ThresholdAlertEnabled
        ,@history_retention_period = $HistoryRetention "

    # Addinf extra options to the query when needed
    if ($BlockSize -ne -1) {
        $Query += ",@block_size = $BlockSize"
    }

    if ($BufferCount -ne -1) {
        $Query += ",@buffer_count = $BufferCount"
    }

    if ($MaxTransferSize -ge 1) {
        $Query += ",@max_transfer_size = $MaxTransferSize"
    }

    if ($ServerSecondary.Version.Major -gt 9) {
        $Query += ",@overwrite = 1;"
    }
    else {
        $Query += ";"
    }

    # Execute the query to add the log shipping primary
    if ($PSCmdlet.ShouldProcess($SqlServer, ("Configuring logshipping for secondary database $SecondaryDatabase on $SqlInstance"))) {
        try {
            Write-Message -Message "Configuring logshipping for secondary database $SecondaryDatabase on $SqlInstance." -Level Output
            Write-Message -Message "Executing query:`n$Query" -Level Verbose
            $ServerSecondary.Query($Query)
        }
        catch {
            Write-Message -Message "$($_.Exception.InnerException.InnerException.InnerException.InnerException.Message)" -Level Warning
            Stop-Function -Message "Error executing the query.`n$($_.Exception.Message)`n$Query"  -ErrorRecord $_ -Target $SqlInstance -Continue
        }
    }

    Write-Message -Message "Finished adding the secondary database $SecondaryDatabase to log shipping." -Level Output

}
tools\dbatools\internal\functions\New-DbaLogShippingSecondaryPrimary.ps1
function New-DbaLogShippingSecondaryPrimary {
    <#
        .SYNOPSIS
            New-DbaLogShippingPrimarySecondary sets up the primary information for the primary database.

        .DESCRIPTION
            New-DbaLogShippingPrimarySecondary sets up the primary information, adds local and remote monitor links,
            and creates copy and restore jobs for the specified primary database.
            This is executed on the secondary server.

        .PARAMETER SqlInstance
            SQL Server instance. You must have sysadmin access and server version must be SQL Server version 2000 or greater.

        .PARAMETER SqlCredential
            Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)

        .PARAMETER BackupSourceDirectory
            The directory where transaction log backup files from the primary server are stored.

        .PARAMETER BackupDestinationDirectory
            The directory on the secondary server where backup files are copied to.

        .PARAMETER CopyJob
            The name to use for the SQL Server Agent job being created to copy transaction log backups to the secondary server.

        .PARAMETER CopyJobID
            The UID associated with the copy job on the secondary server.

        .PARAMETER FileRetentionPeriod
            The length of time, in minutes, that a backup file is retained on the secondary server in the path specified by the BackupDestinationDirectory parameter before being deleted.
            The default is 14420.

        .PARAMETER MonitorServer
            Is the name of the monitor server. The default is the secondary server.

        .PARAMETER MonitorServerLogin
            Is the username of the account used to access the monitor server.

        .PARAMETER MonitorServerPassword
            Is the password of the account used to access the monitor server.

        .PARAMETER MonitorServerSecurityMode
            The security mode used to connect to the monitor server. Allowed values are 0, "sqlserver", 1, "windows"
            The default is 1 or Windows.

        .PARAMETER PrimaryServer
            The name of the primary instance of the Microsoft SQL Server Database Engine in the log shipping configuration.

        .PARAMETER PrimaryDatabase
            Is the name of the database on the primary server.

        .PARAMETER RestoreJob
            Is the name of the SQL Server Agent job on the secondary server that restores the backups to the secondary database.

        .PARAMETER RestoreJobID
            The UID associated with the restore job on the secondary server.

        .PARAMETER WhatIf
            Shows what would happen if the command were to run. No actions are actually performed.

        .PARAMETER Confirm
            Prompts you for confirmation before executing any changing operations within the command.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .PARAMETER Force
            The force parameter will ignore some errors in the parameters and assume defaults.
            It will also remove the any present schedules with the same name for the specific job.

        .NOTES
            Author: Sander Stad (@sqlstad, sqlstad.nl)
            Website: https://dbatools.io
            Copyright: (C) Chrissy LeMaire, [email protected]
            License: MIT https://opensource.org/licenses/MIT

        .LINK
            https://dbatools.io/New-DbaLogShippingPrimarySecondary

        .EXAMPLE
            New-DbaLogShippingSecondaryPrimary -SqlInstance sql2 -BackupSourceDirectory "\\sql1\logshipping\DB1" -BackupDestinationDirectory D:\Data\logshippingdestination\DB1_DR -CopyJob LSCopy_sql2_DB1_DR -FileRetentionPeriod 4320 -MonitorServer sql2 -MonitorServerSecurityMode 'Windows' -PrimaryServer sql1 -PrimaryDatabase DB1 -RestoreJob LSRestore_sql2_DB1_DR
    #>
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param (
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [object]$SqlInstance,
        [PSCredential]$SqlCredential,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$BackupSourceDirectory,
        [Parameter(Mandatory = $false)]
        [string]$BackupDestinationDirectory,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$CopyJob,
        [int]$FileRetentionPeriod = 14420,
        [string]$MonitorServer,
        [PSCredential]$MonitorCredential,
        [Parameter(Mandatory = $true)]
        [ValidateSet(0, "sqlserver", 1, "windows")]
        [object]$MonitorServerSecurityMode = 1,
        [object]$PrimaryServer,
        [PSCredential]$PrimarySqlCredential,
        [object]$PrimaryDatabase,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$RestoreJob,
        [Alias('Silent')]
        [switch]$EnableException,
        [switch]$Force
    )

    # Try connecting to the instance
    Write-Message -Message "Connecting to $SqlInstance" -Level Verbose
    try {
        $ServerSecondary = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
    }
    catch {
        Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $SqlInstance -Continue
    }

    # Try connecting to the instance
    Write-Message -Message "Connecting to $PrimaryServer" -Level Verbose
    try {
        $ServerPrimary = Connect-SqlInstance -SqlInstance $PrimaryServer -SqlCredential $PrimarySqlCredential
    }
    catch {
        Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $PrimaryServer -Continue
    }

    # Check if the backup UNC path is correct and reachable
    if ([bool]([uri]$BackupDestinationDirectory).IsUnc -and $BackupDestinationDirectory -notmatch '^\\(?:\\[^<>:`"/\\|?*]+)+$') {
        Stop-Function -Message "The backup destination path should be formatted in the form \\server\share." -Target $SqlInstance
        return
    }
    else {
        if (-not ((Test-Path $BackupDestinationDirectory -PathType Container -IsValid) -and ((Get-Item $BackupDestinationDirectory).PSProvider.Name -eq 'FileSystem'))) {
            Stop-Function -Message "The backup destination path is not valid or can't be reached." -Target $SqlInstance
            return
        }
    }

    # Check the MonitorServer
    if ($Force -and -not $MonitorServer) {
        $MonitorServer = $SqlInstance
        Write-Message -Message "Setting monitor server to $MonitorServer." -Level Verbose
    }

    # Check of the MonitorServerSecurityMode value is of type string and set the integer value
    if ($MonitorServerSecurityMode -notin 0, 1) {
        $MonitorServerSecurityMode = switch ($MonitorServerSecurityMode) {"WINDOWS" { 1 } "SQLSERVER" { 0 } }
        Write-Message -Message "Setting monitor server security mode to $MonitorServerSecurityMode." -Level Verbose
    }

    # Check the MonitorServerSecurityMode if it's SQL Server authentication
    if ($MonitorServerSecurityMode -eq 0 -and -not $MonitorCredential) {
        Stop-Function -Message "The MonitorServerCredential cannot be empty when using SQL Server authentication." -Target $SqlInstance -Continue
        return
    }
    elseif ($MonitorServerSecurityMode -eq 0 -and $MonitorCredential) {
        # Get the username and password from the credential
        $MonitorLogin = $MonitorCredential.UserName
        $MonitorPassword = $MonitorCredential.GetNetworkCredential().Password

        # Check if the user is in the database
        if ($ServerSecondary.Databases['master'].Users.Name -notcontains $MonitorLogin) {
            Stop-Function -Message "User $MonitorLogin for monitor login must be in the master database." -Target $SqlInstance -Continue
            return
        }
    }

    # Check if the database is present on the primary sql server
    if ($ServerPrimary.Databases.Name -notcontains $PrimaryDatabase) {
        Stop-Function -Message "Database $PrimaryDatabase is not available on instance $PrimaryServer" -Target $PrimaryServer -Continue
        return
    }

    # Set up the query
    $Query = "
        DECLARE @LS_Secondary__CopyJobId AS uniqueidentifier
        DECLARE @LS_Secondary__RestoreJobId	AS uniqueidentifier
        DECLARE @LS_Secondary__SecondaryId AS uniqueidentifier
        EXEC master.sys.sp_add_log_shipping_secondary_primary
                @primary_server = N'$PrimaryServer'
                ,@primary_database = N'$PrimaryDatabase'
                ,@backup_source_directory = N'$BackupSourceDirectory'
                ,@backup_destination_directory = N'$BackupDestinationDirectory'
                ,@copy_job_name = N'$CopyJob'
                ,@restore_job_name = N'$RestoreJob'
                ,@file_retention_period = $FileRetentionPeriod
                ,@copy_job_id = @LS_Secondary__CopyJobId OUTPUT
                ,@restore_job_id = @LS_Secondary__RestoreJobId OUTPUT
                ,@secondary_id = @LS_Secondary__SecondaryId OUTPUT "

    if ($MonitorServer) {
        $Query += ",@monitor_server = N'$MonitorServer'
                ,@monitor_server_security_mode = $($MonitorServerSecurityMode) "
    }


    # Check the MonitorServerSecurityMode if it's SQL Server authentication
    if ($MonitorServerSecurityMode -eq 0 -and $MonitorServer) {
        $Query += ",@monitor_server_login = N'$MonitorLogin'
            ,@monitor_server_password = N'$MonitorPassword' "
    }

    if ($ServerSecondary.Version.Major -gt 9) {
        $Query += ",@overwrite = 1;"
    }
    else {
        $Query += ";"
    }

    # Execute the query to add the log shipping primary
    if ($PSCmdlet.ShouldProcess($SqlServer, ("Configuring logshipping making settings for the primary database to secondary database on $SqlInstance"))) {
        try {
            Write-Message -Message "Configuring logshipping making settings for the primary database." -Level Output
            Write-Message -Message "Executing query:`n$Query" -Level Verbose
            $ServerSecondary.Query($Query)
        }
        catch {
            Write-Message -Message "$($_.Exception.InnerException.InnerException.InnerException.InnerException.Message)" -Level Warning
            Stop-Function -Message "Error executing the query.`n$($_.Exception.Message)"  -ErrorRecord $_ -Target $SqlInstance -Continue
        }
    }

    Write-Message -Message "Finished configuring of secondary database to primary database $PrimaryDatabase." -Level Output
}
tools\dbatools\internal\functions\New-DbaMessageLevelModifier.ps1
function New-DbaMessageLevelModifier {
<#
    .SYNOPSIS
        Allows modifying message levels by powerful filters.
    
    .DESCRIPTION
        Allows modifying message levels by powerful filters.
        
        This is designed to allow a developer to have more control over what is written how during the development process.
        It also allows a debug user to fine tune what he is shown.
        
        This functionality is NOT designed for default implementation within a module.
        Instead, set healthy message levels for your own messages and leave others to tend to their own levels.
        
        Note:
        Adding too many level modifiers may impact performance, use with discretion.
    
    .PARAMETER Name
        The name of the level modifier.
        Can be arbitrary, but must be unique. Not case sensitive.
    
    .PARAMETER Modifier
        The level modifier to apply.
        - Use a negative value to make a message more relevant
        - Use a positive value to make a message less relevant
        While not limited to this range, the original levels range from 1 through 9:
        - 1-3 : Written to host and debug by default
        - 4-6 : Written to verbose and debug by default
        - 7-9 : Internas, written only to debug
    
    .PARAMETER IncludeFunctionName
        Only messages from functions with one of these exact names will be considered.
    
    .PARAMETER ExcludeFunctionName
        Messages from functions with one of these exact names will be ignored.
    
    .PARAMETER IncludeModuleName
        Only messages from modules with one of these exact names will be considered.
    
    .PARAMETER ExcludeModuleName
        Messages from module with one of these exact names will be ignored.
    
    .PARAMETER IncludeTags
        Only messages that contain one of these tags will be considered.
    
    .PARAMETER ExcludeTags
        Messages that contain one of these tags will be ignored.
    
    .PARAMETER EnableException
        This parameters disables user-friendly warnings and enables the throwing of exceptions.
        This is less user friendly, but allows catching exceptions in calling scripts.
    
    .EXAMPLE
        PS C:\> New-DbaMessageLevelModifier -Name 'MyModule-Include' -Modifier -9 -IncludeModuleName MyModule
        PS C:\> New-DbaMessageLevelModifier -Name 'MyModule-Exclude' -Modifier 9 -ExcludeModuleName MyModule
        
        These settings will cause all messages from the module 'MyModule' to be highly prioritized and almost certainly written to host.
        It will also make it highly unlikely, that messages from other modules will even be considered for anything but the lowest level.
        
        This is useful when prioritizing your own module during development.
#>
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]
        $Name,
        
        [Parameter(Mandatory = $true)]
        [int]
        $Modifier,
        
        [string]
        $IncludeFunctionName,
        
        [string]
        $ExcludeFunctionName,
        
        [string]
        $IncludeModuleName,
        
        [string]
        $ExcludeModuleName,
        
        [string[]]
        $IncludeTags,
        
        [string[]]
        $ExcludeTags,
        
        [switch]
        $EnableException
    )
    
    if (Test-Bound -ParameterName IncludeFunctionName, ExcludeFunctionName, IncludeModuleName, ExcludeModuleName, IncludeTags, ExcludeTags -Not) {
        Stop-Function -Message "Must specify at least one condition in order to apply message level modifier!" -EnableException $EnableException -Category InvalidArgument
        return
    }
    
    $levelModifier = New-Object Sqlcollaborative.Dbatools.Message.MessageLevelModifier
    $levelModifier.Name = $Name.ToLower()
    $levelModifier.Modifier = $Modifier
    
    if (Test-Bound -ParameterName IncludeFunctionName) {
        $levelModifier.IncludeFunctionName = $IncludeFunctionName
    }
    
    if (Test-Bound -ParameterName ExcludeFunctionName) {
        $levelModifier.ExcludeFunctionName = $ExcludeFunctionName
    }
    
    if (Test-Bound -ParameterName IncludeModuleName) {
        $levelModifier.IncludeModuleName = $IncludeModuleName
    }
    
    if (Test-Bound -ParameterName ExcludeModuleName) {
        $levelModifier.ExcludeModuleName = $ExcludeModuleName
    }
    
    if (Test-Bound -ParameterName IncludeTags) {
        $levelModifier.IncludeTags = $IncludeTags
    }
    
    if (Test-Bound -ParameterName ExcludeTags) {
        $levelModifier.ExcludeTags = $ExcludeTags
    }
    
    [Sqlcollaborative.Dbatools.Message.MessageHost]::MessageLevelModifiers[$levelModifier.Name] = $levelModifier
    
    $levelModifier
}
tools\dbatools\internal\functions\New-DbaTeppCompletionResult.ps1
function global:New-DbaTeppCompletionResult {
    <#
        .SYNOPSIS
            Generates a completion result for dbatools internal tab completion.

        .DESCRIPTION
            Generates a completion result for dbatools internal tab completion.

        .PARAMETER CompletionText
            The text to propose.

        .PARAMETER ToolTip
            The tooltip to show in tooltip-aware hosts (ISE, mostly)

        .PARAMETER ListItemText
            ???

        .PARAMETER CompletionResultType
            The type of object that is being completed.
            By default it generates one of type paramter value.

        .PARAMETER NoQuotes
            Whether to put the result in quotes or not.

        .EXAMPLE
            New-DbaTeppCompletionResult -CompletionText 'master' -ToolTip 'master'

            Returns a CompletionResult with the text and tooltip 'master'
    #>
    param (
        [Parameter(Position = 0, ValueFromPipelineByPropertyName = $true, Mandatory = $true, ValueFromPipeline = $true)]
        [ValidateNotNullOrEmpty()]
        [string]
        $CompletionText,

        [Parameter(Position = 1, ValueFromPipelineByPropertyName = $true)]
        [string]
        $ToolTip,

        [Parameter(Position = 2, ValueFromPipelineByPropertyName = $true)]
        [string]
        $ListItemText,

        [System.Management.Automation.CompletionResultType]
        $CompletionResultType = [System.Management.Automation.CompletionResultType]::ParameterValue,

        [Parameter(Mandatory = $false)]
        [switch]
        $NoQuotes = $false
    )

    process {
        $toolTipToUse = if ($ToolTip -eq '') { $CompletionText }
        else { $ToolTip }
        $listItemToUse = if ($ListItemText -eq '') { $CompletionText }
        else { $ListItemText }

        # If the caller explicitly requests that quotes
        # not be included, via the -NoQuotes parameter,
        # then skip adding quotes.

        if ($CompletionResultType -eq [System.Management.Automation.CompletionResultType]::ParameterValue -and -not $NoQuotes) {
            # Add single quotes for the caller in case they are needed.
            # We use the parser to robustly determine how it will treat
            # the argument.  If we end up with too many tokens, or if
            # the parser found something expandable in the results, we
            # know quotes are needed.

            $tokens = $null
            $null = [System.Management.Automation.Language.Parser]::ParseInput("echo $CompletionText", [ref]$tokens, [ref]$null)
            if ($tokens.Length -ne 3 -or ($tokens[1] -is [System.Management.Automation.Language.StringExpandableToken] -and $tokens[1].Kind -eq [System.Management.Automation.Language.TokenKind]::Generic)) {
                $CompletionText = "'$CompletionText'"
            }
        }
        return New-Object System.Management.Automation.CompletionResult($CompletionText, $listItemToUse, $CompletionResultType, $toolTipToUse.Trim())
    }
}

(Get-Item Function:\New-DbaTeppCompletionResult).Visibility = "Private"
tools\dbatools\internal\functions\Register-DbaConfigValidation.ps1
function Register-DbaConfigValidation {
    <#
        .SYNOPSIS
            Registers a validation scriptblock for use with the configuration system.

        .DESCRIPTION
            Registers a validation scriptblock for use with the configuration system.

            The scriptblock must be designed according to a few guidelines:
            - It must not throw exceptions
            - It must accept a single parameter (the value to be tested)
            - It must return an object with three properties: 'Message', 'Value' and 'Success'.
            The Success property should be boolean and indicate whether the value is valid.
            The Value property contains the validated input. The scriptblock may legally convert the input (For example from string to int in case of integer validation)
            The message contains a string that will be passed along to an exception in case the input is NOT valid.

        .PARAMETER Name
            The name under which to register the validation scriptblock

        .PARAMETER ScriptBlock
            The scriptblock to register

        .EXAMPLE
            PS C:\> Register-DbaConfigValidation -Name IntPositive -ScriptBlock $scriptblock

            Registers the scriptblock stored in $scriptblock as validation with the name IntPositive
    #>
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $true)]
        [string]
        $Name,

        [Parameter(Mandatory = $true)]
        [ScriptBlock]
        $ScriptBlock
    )

    [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Validation[$Name.ToLower()] = $ScriptBlock
}
tools\dbatools\internal\functions\Register-DbaMaintenanceTask.ps1
function Register-DbaMaintenanceTask {
    <#
        .SYNOPSIS
            Allows scheduling maintenance tasks, that are perfomed in the background.

        .DESCRIPTION
            Allows scheduling maintenance tasks, that are perfomed in the background.

            All scriptblocks scheduled like this will be performed on a separate runspace and have access to all internal dbatools commands.
            None of the scriptblocks will affect the main session (so you cannot manipulate variables, etc.)

        .PARAMETER Name
            The name of the task.
            Must be unique, otherwise it will update the existing task.

        .PARAMETER ScriptBlock
            The task/scriptblock that should be performed as part of the maintenance.
            It will have all of dbatools including internal commands available.

        .PARAMETER Once
            Whether the interval should be performed only once.

        .PARAMETER Interval
            The interval at which the task should be repeated.

        .PARAMETER Delay
            How far after the initial registration should the maintenance script wait before processing this.
            This can be used to delay background stuff that should not content with items that would be good to have as part of the module import.
            Some library specific items can be moved to maintenance if their processing would take too much time on original import, even if it is desirable to have them available as soon as possible.

        .PARAMETER Priority
            How important is this task?
            If multiple tasks are due at the same maintenance cycle, the more critical one will be processed first.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .EXAMPLE
            PS C:\> Register-DbaMaintenanceTask -Name 'value1' -ScriptBlock $ScriptBlock -Once
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]
        $Name,

        [Parameter(Mandatory = $true)]
        [System.Management.Automation.ScriptBlock]
        $ScriptBlock,

        [Parameter(Mandatory = $true, ParameterSetName = "Once")]
        [switch]
        $Once,

        [Parameter(Mandatory = $true, ParameterSetName = "Repeating")]
        [System.TimeSpan]
        $Interval,

        [System.TimeSpan]
        $Delay,

        [Sqlcollaborative.Dbatools.Maintenance.MaintenancePriority]
        $Priority = "Medium",

        [switch]
        [Alias('Silent')]$EnableException
    )

    #region Case: Task already registered
    if ([Sqlcollaborative.Dbatools.Maintenance.MaintenanceHost]::Tasks.ContainsKey($Name.ToLower())) {
        $task = [Sqlcollaborative.Dbatools.Maintenance.MaintenanceHost]::Tasks[$Name.ToLower()]
        if ($task.ScriptBlock -ne $ScriptBlock) { $task.ScriptBlock = $ScriptBlock }
        if (Test-Bound -ParameterName Once) { $task.Once = $Once }
        if (Test-Bound -ParameterName Interval) {
            $task.Once = $false
            $task.Interval = $Interval
        }
        if (Test-Bound -ParameterName Delay) { $task.Delay = $Delay }
        if (Test-Bound -ParameterName Priority) { $task.Priority = $Priority }
    }
    #endregion Case: Task already registered

    #region New Task
    else {
        $task = New-Object Sqlcollaborative.Dbatools.Maintenance.MaintenanceTask
        $task.Name = $Name.ToLower()
        $task.ScriptBlock = $ScriptBlock
        if (Test-Bound -ParameterName Once) { $task.Once = $true }
        if (Test-Bound -ParameterName Interval) {
            if ($Interval.Ticks -le 0) {
                Stop-Function -Message "Failed to register task: $Name - Interval cannot be 0 or less" -Category InvalidArgument
                return
            }
            else { $task.Interval = $Interval }
        }
        if (Test-Bound -ParameterName Delay) { $task.Delay = $Delay }
        $task.Priority = $Priority
        $task.Registered = Get-Date
        [Sqlcollaborative.Dbatools.Maintenance.MaintenanceHost]::Tasks[$Name.ToLower()] = $task
    }
    #endregion New Task
}
tools\dbatools\internal\functions\Register-DbaMessageEvent.ps1
function Register-DbaMessageEvent {
<#
    .SYNOPSIS
        Registers an event to when a message is written.
    
    .DESCRIPTION
        Registers an event to when a message is written.
        These events will fire whenever the written message fulfills the specified filter criteria.
        
        This allows integrating direct alerts and reactions to messages as they occur.
        
        Warnings:
        - Adding many subscriptions can impact overall performance, even without triggering.
        - Events are executed synchronously. executing complex operations may introduce a significant delay to the command execution.
        
        It is recommended to push processing that involves outside resources to a separate runspace, then use the event to pass the object as trigger.
        The TaskEngine component may prove to be just what is needed to accomplish this.
    
    .PARAMETER Name
        The name of the subscription.
        Each subscription must have a name, subscriptions of equal name will overwrite each other.
        This is in order to avoid having runspace uses explode the number of subscriptions on each invocation.
    
    .PARAMETER ScriptBlock
        The scriptblock to execute.
        It will receive the message entry (as returned by Get-DbatoolsLog) as its sole argument.
    
    .PARAMETER MessageFilter
        Filter by message content. Understands wildcards, but not regex.
    
    .PARAMETER ModuleNameFilter
        Filter by Name of the module, from which the message comes. Understands wildcards, but not regex.
    
    .PARAMETER FunctionNameFilter
        Filter by Name of the function, from which the message comes. Understands wildcards, but not regex.
    
    .PARAMETER TargetFilter
        Filter by target object. Performs equality comparison on an object level.
    
    .PARAMETER LevelFilter
        Include only messages of the specified levels.
    
    .PARAMETER TagFilter
        Only include messages with any of the specified tags.
    
    .PARAMETER RunspaceFilter
        Only include messages which were written by the specified runspace.
        You can find out the current runspace ID by running this:
        [System.Management.Automation.Runspaces.Runspace]::DefaultRunspace.InstanceId
        You can retrieve the primary runspace - the Guid used by the runspace the user sees - by running this:
        [Sqlcollaborative.Dbatools.Utility.UtilityHost]::PrimaryRunspace
    
    .EXAMPLE
        PS C:\> Register-DbaMessageEvent -Name 'Mymodule.OffloadTrigger' -ScriptBlock $ScriptBlock -Tag 'engine' -Module 'MyModule' -Level Warning
        
        Registers an event subscription ...
        - Under the name 'Mymodule.OffloadTrigger' ...
        - To execute $ScriptBlock ...
        - Whenever a message is written with the tag 'engine' by the module 'MyModule' at the level 'Warning'
#>
    [CmdletBinding(PositionalBinding = $false)]
    param (
        [Parameter(Mandatory = $true)]
        [string]
        $Name,
        
        [Parameter(Mandatory = $true)]
        [System.Management.Automation.ScriptBlock]
        $ScriptBlock,
        
        [string]
        $MessageFilter,
        
        [string]
        $ModuleNameFilter,
        
        [string]
        $FunctionNameFilter,
        
        $TargetFilter,
        
        [Sqlcollaborative.Dbatools.Message.MessageLevel[]]
        $LevelFilter,
        
        [string[]]
        $TagFilter,
        
        [System.Guid]
        $RunspaceFilter
    )
    
    $newName = $Name.ToLower()
    $eventSubscription = New-Object Sqlcollaborative.Dbatools.Message.MessageEventSubscription
    $eventSubscription.Name = $newName
    $eventSubscription.ScriptBlock = $ScriptBlock
    
    if (Test-Bound -ParameterName MessageFilter) {
        $eventSubscription.MessageFilter = $MessageFilter
    }
    
    if (Test-Bound -ParameterName ModuleNameFilter) {
        $eventSubscription.ModuleNameFilter = $ModuleNameFilter
    }
    
    if (Test-Bound -ParameterName FunctionNameFilter) {
        $eventSubscription.FunctionNameFilter = $FunctionNameFilter
    }
    
    if (Test-Bound -ParameterName TargetFilter) {
        $eventSubscription.TargetFilter = $TargetFilter
    }
    
    if (Test-Bound -ParameterName LevelFilter) {
        $eventSubscription.LevelFilter = $LevelFilter
    }
    
    if (Test-Bound -ParameterName TagFilter) {
        $eventSubscription.TagFilter = $TagFilter
    }
    
    if (Test-Bound -ParameterName RunspaceFilter) {
        $eventSubscription.RunspaceFilter = $RunspaceFilter
    }
    
    [Sqlcollaborative.Dbatools.Message.MessageHost]::Events[$newName] = $eventSubscription
}
tools\dbatools\internal\functions\Register-DbaMessageTransform.ps1
function Register-DbaMessageTransform {
<#
    .SYNOPSIS
        Registers a scriptblock that can transform message content.
    
    .DESCRIPTION
        Registers a scriptblock that can transform message content.
        This can be used to convert some kinds of input. Specifically:
        
        Target:
        When specifying a target, this target may require some conversion.
        For example, an object containing a live connection may need to have a static copy stored instead,
        as otherwise its export on a different runspace may cause access violations.
        
        Exceptions:
        Some exceptions may need transforming.
        For example some APIs might wrap the actual exception into a common wrapper.
        In this scenario you may want the actual exception in order to provide more specific information.
        
        In all instances, the scriptblock will be called, receiving only the relevant object as its sole input.
        
        Note: This transformation is performed synchronously on the active runspace. Complex scriptblocks may delay execution times when a matching object is passed.
    
    .PARAMETER TargetType
        The full typename of the target object to apply the scriptblock to.
        All objects of that typename will be processed through that scriptblock.
    
    .PARAMETER ExceptionType
        The full typename of the exception object to apply the scriptblock to.
        All objects of that typename will be processed through that scriptblock.
        Note: In case of error records, the type of the Exception Property is inspected. The error record as a whole will not be touched, except for having its exception exchanged.
    
    .PARAMETER ScriptBlock
        The scriptblock that performs the transformation.
    
    .PARAMETER TargetTypeFilter
        A filter for the typename of the target object to transform.
        Supports wildcards, but not regex.
        WARNING: Adding too many filter-type transforms may impact overall performance, try to avoid using them!
    
    .PARAMETER ExceptionTypeFilter
        A filter for the typename of the exception object to transform.
        Supports wildcards, but not regex.
        WARNING: Adding too many filter-type transforms may impact overall performance, try to avoid using them!
    
    .PARAMETER FunctionNameFilter
        Default: "*"
        Allows filtering by function name, in order to consider whether the function is affected.
        Supports wildcards, but not regex.
        WARNING: Adding too many filter-type transforms may impact overall performance, try to avoid using them!
    
    .PARAMETER ModuleNameFilter
        Default: "*"
        Allows filtering by module name, in order to consider whether the function is affected.
        Supports wildcards, but not regex.
        WARNING: Adding too many filter-type transforms may impact overall performance, try to avoid using them!
    
    .EXAMPLE
        PS C:\> Register-DbaMessageTransform -TargetType 'mymodule.category.classname' -ScriptBlock $ScriptBlock
        
        Whenever a target object of type 'mymodule.category.classname' is specified, invoke $ScriptBlock (with the object as sole argument) and store the result as target instead.
    
    .EXAMPLE
        PS C:\> Register-DbaMessageTransform -ExceptionType 'mymodule.category.exceptionname' -ScriptBlock $ScriptBlock
        
        Whenever an exception or error record of type 'mymodule.category.classname' is specified, invoke $ScriptBlock (with the object as sole argument) and store the result as exception instead.
        If the full error record is specified, only the updated exception will be inserted
    
    .EXAMPLE
        PS C:\> Register-DbaMessageTransform -TargetTypeFilter 'mymodule.category.*' -ScriptBlock $ScriptBlock
        
        Adds a transform for all target objects that are of a type whose full name starts with 'mymodule.category.'
        All target objects matching that typename will be run through the specified scriptblock, which in return generates the new target object.
#>
    [CmdletBinding(PositionalBinding = $false)]
    param (
        [Parameter(Mandatory = $true, ParameterSetName = "Target")]
        [string]
        $TargetType,
        
        [Parameter(Mandatory = $true, ParameterSetName = "Exception")]
        [string]
        $ExceptionType,
        
        [Parameter(Mandatory = $true)]
        [ScriptBlock]
        $ScriptBlock,
        
        [Parameter(Mandatory = $true, ParameterSetName = "TargetFilter")]
        [string]
        $TargetTypeFilter,
        
        [Parameter(Mandatory = $true, ParameterSetName = "ExceptionFilter")]
        [string]
        $ExceptionTypeFilter,
        
        [Parameter(ParameterSetName = "TargetFilter")]
        [Parameter(ParameterSetName = "ExceptionFilter")]
        $FunctionNameFilter = "*",
        
        [Parameter(ParameterSetName = "TargetFilter")]
        [Parameter(ParameterSetName = "ExceptionFilter")]
        $ModuleNameFilter = "*"
    )
    
    process {
        if ($TargetType) { [Sqlcollaborative.Dbatools.Message.MessageHost]::TargetTransforms[$TargetType.ToLower()] = $ScriptBlock }
        if ($ExceptionType) { [Sqlcollaborative.Dbatools.Message.MessageHost]::ExceptionTransforms[$ExceptionType.ToLower()] = $ScriptBlock }
        
        if ($TargetTypeFilter) {
            $condition = New-Object Sqlcollaborative.Dbatools.Message.TransformCondition($TargetTypeFilter, $ModuleNameFilter, $FunctionNameFilter, $ScriptBlock, "Target")
            [Sqlcollaborative.Dbatools.Message.MessageHost]::TargetTransformList.Add($condition)
        }
        
        if ($ExceptionTypeFilter) {
            $condition = New-Object Sqlcollaborative.Dbatools.Message.TransformCondition($ExceptionTypeFilter, $ModuleNameFilter, $FunctionNameFilter, $ScriptBlock, "Exception")
            [Sqlcollaborative.Dbatools.Message.MessageHost]::ExceptionTransformList.Add($condition)
        }
    }
}
tools\dbatools\internal\functions\Register-DbaRunspace.ps1
function Register-DbaRunspace {
    <#
    .SYNOPSIS
        Registers a scriptblock to run in the background.

    .DESCRIPTION
        This function registers a scriptblock to run in separate runspace.
        This is different from most runspace solutions, in that it is designed for permanent background tasks that need to be done.
        It guarantees a single copy of the task to run within the powershell process, even when running the same module in many runspaces in parallel.

        Updating:
        If this function is called multiple times, targeting the same name, it will update the scriptblock.
        - If that scriptblock is the same as the previous scriptblock, nothing changes
        - If that scriptblock is different from the previous ones, it will be registered, but will not be executed right away!
          Only after stopping and starting the runspace will it operate under the new scriptblock.

    .PARAMETER ScriptBlock
        The scriptblock to run in a dedicated runspace

    .PARAMETER Name
        The name to register the scriptblock under.

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .EXAMPLE
        PS C:\> Register-DbaRunspace -ScriptBlock $scriptBlock -Name 'mymodule.maintenance'

        Registers the script defined in $scriptBlock under the name 'mymodule.maintenance'
        It does not start the runspace yet. If it already exists, it will overwrite the scriptblock without affecting the running script.

    .EXAMPLE
        PS C:\> Register-DbaRunspace -ScriptBlock $scriptBlock -Name 'mymodule.maintenance'
        PS C:\> Start-DbaRunspace -Name 'mymodule.maintenance'

        Registers the script defined in $scriptBlock under the name 'mymodule.maintenance'
        Then it starts the runspace, running the registered $scriptBlock
#>
    [CmdletBinding(PositionalBinding = $false)]
    param
    (
        [Parameter(Mandatory = $true)]
        [Scriptblock]
        $ScriptBlock,

        [Parameter(Mandatory = $true)]
        [String]
        $Name,

        [switch]
        [Alias('Silent')]
        $EnableException
    )

    if ([Sqlcollaborative.Dbatools.Runspace.RunspaceHost]::Runspaces.ContainsKey($Name.ToLower())) {
        Write-Message -Level Verbose -Message "Updating runspace: $($Name.ToLower())" -Target $Name.ToLower()
        [Sqlcollaborative.Dbatools.Runspace.RunspaceHost]::Runspaces[$Name.ToLower()].SetScript($ScriptBlock)
    }
    else {
        Write-Message -Level Verbose -Message "Registering runspace: $($Name.ToLower())" -Target $Name.ToLower()
        [Sqlcollaborative.Dbatools.Runspace.RunspaceHost]::Runspaces[$Name.ToLower()] = New-Object Sqlcollaborative.Dbatools.Runspace.RunspaceContainer($Name.ToLower(), $ScriptBlock)
    }
}
tools\dbatools\internal\functions\Register-DbaTeppArgumentCompleter.ps1
function Register-DbaTeppArgumentCompleter {
    <#
        .SYNOPSIS
            Registers a parameter for a prestored Tepp.

        .DESCRIPTION
            Registers a parameter for a prestored Tepp.
            This function allows easily registering a function's parameter for Tepp in the function-file, rather than in a centralized location.

        .PARAMETER Command
            Name of the command whose parameter should receive Tepp.
            Supports multiple commands at the same time in order to optimize performance.

        .PARAMETER Parameter
            Name of the parameter that should be Tepp'ed.

        .PARAMETER Name
            Name of the Tepp Completioner to use.
            Defaults to the parameter name.
            Best practice requires a Completioner to be named the same as the completed parameter, in which case this parameter needs not be specified.
            However sometimes that may not be universally possible, which is when this parameter comes in.

        .PARAMETER All
            Whether this TEPP applies to all commands in dbatools that have the specified parameter.

        .EXAMPLE
            Register-DbaTeppArgumentCompleter -Command Get-DbaBackupHistory -Parameter Database

            Registers the "Database" parameter of the Get-DbaBackupHistory to receive Database-Tepp
    #>
    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingEmptyCatchBlock", "")]
    param (
        [string[]]$Command,
        [string[]]$Parameter,
        [string]$Name,
        [switch]$All
    )

    #region ScriptBlock
    $scriptBlock = {
        param (
            $commandName,
            $parameterName,
            $wordToComplete,
            $commandAst,
            $fakeBoundParameter
        )

        if ($teppScript = [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::GetTeppScript($commandName, $parameterName)) {
            $start = Get-Date
            $teppScript.LastExecution = $start
            $teppScript.LastDuration = New-Object System.TimeSpan(-1) # Null it, just in case. It's a new start.

            try { $ExecutionContext.InvokeCommand.InvokeScript($true, ([System.Management.Automation.ScriptBlock]::Create($teppScript.ScriptBlock.ToString())), $null, @($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)) }
            catch { }

            $teppScript.LastDuration = (Get-Date) - $start
        }
    }
    #endregion ScriptBlock

    foreach ($p in $Parameter) {
        $lowername = $PSBoundParameters.Name

        if ($null -eq $lowername) {
            $lowername = $p.ToLower()
        }
        else {
            $lowername = $lowername.ToLower()
        }

        if ($All) { [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::AddTabCompletionSet("*", $p, $lowername) }
        else {
            foreach ($c in $Command) {
                [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::AddTabCompletionSet($c, $p, $lowername)
            }
        }

        if ($script:TEPP) {
            TabExpansionPlusPlus\Register-ArgumentCompleter -CommandName $Command -ParameterName $p -ScriptBlock $scriptBlock
        }
        else {
            Register-ArgumentCompleter -CommandName $Command -ParameterName $p -ScriptBlock $scriptBlock
        }
    }
}
tools\dbatools\internal\functions\Register-DbaTeppInstanceCacheBuilder.ps1
function Register-DbaTeppInstanceCacheBuilder {
    <#
        .SYNOPSIS
            Registers a scriptblock used to build the TEPP cache from an instance connection.

        .DESCRIPTION
            Registers a scriptblock used to build the TEPP cache from an instance connection.
            Used only on import of the module.

        .PARAMETER ScriptBlock
            The ScriptBlock used to build the cache.

            The ScriptBlock may assume the following two variables to exist:
            - $FullSmoName (A string containing the full SMO name as presented by the DbaInstanceParameter class-interpreted input)
            - $server (An SMO connection object)

        .PARAMETER Slow
            This switch implies a gathering process that takes too much time to be performed synchronously.
            Basically, when retrieving the information takes more than 25ms on an average server (on top of establishing the original connection), this switch should be set.

        .EXAMPLE
            Register-DbaTeppInstanceCacheBuilder -ScriptBlock $ScriptBlock

            Registers the scriptblock stored in the aptly named variable $ScriptBlock as a fest cache building scriptblock.
            Note: The scriptblock must execute swiftly! (less than 25ms)

        .EXAMPLE
            Register-DbaTeppInstanceCacheBuilder -ScriptBlock $ScriptBlock -Slow

            Registers the scriptblock stored in the aptly named variable $ScriptBlock as a slow cache building scriptblock.
            This is suitable for cache building scriptblocks that take a while to execute.

        .NOTES
            Additional information about the function.
    #>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [System.Management.Automation.ScriptBlock]
        $ScriptBlock,

        [switch]
        $Slow
    )

    if ($Slow -and ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::TeppGatherScriptsSlow -notcontains $ScriptBlock)) {
        [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::TeppGatherScriptsSlow.Add($ScriptBlock)
    }
    elseif ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::TeppGatherScriptsFast -notcontains $ScriptBlock) {
        [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::TeppGatherScriptsFast.Add($ScriptBlock)
    }
}
tools\dbatools\internal\functions\Register-DbaTeppScriptBlock.ps1
function Register-DbaTeppScriptblock {
    <#
        .SYNOPSIS
            Registers a scriptblock under name, to later be available for TabExpansion.

        .DESCRIPTION
            Registers a scriptblock under name, to later be available for TabExpansion.

        .PARAMETER ScriptBlock
            The scriptblock to register.

        .PARAMETER Name
            The name under which the scriptblock should be registered.

        .EXAMPLE
            Register-DbaTeppScriptblock -ScriptBlock $scriptBlock -Name MyFirstTeppScriptBlock

            Stores the scriptblock stored in $scriptBlock under the name "MyFirstTeppScriptBlock"
    #>
    [CmdletBinding()]
    param (
        [System.Management.Automation.ScriptBlock]
        $ScriptBlock,

        [string]
        $Name
    )

    $scp = New-Object Sqlcollaborative.Dbatools.TabExpansion.ScriptContainer
    $scp.Name = $Name.ToLower()
    $scp.ScriptBlock = $ScriptBlock
    $scp.LastDuration = New-TimeSpan -Seconds -1

    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::Scripts[$Name.ToLower()] = $scp
}
tools\dbatools\internal\functions\Remove-DbaMessageLevelModifier.ps1
function Remove-DbaMessageLevelModifier {
<#
    .SYNOPSIS
        Removes a message level modifier.
    
    .DESCRIPTION
        Removes a message level modifier.
        
        Message Level Modifiers can be created by using New-DbaMessageLevelModifier.
        They are used to emphasize or deemphasize messages, in order to help with debugging.
    
    .PARAMETER Name
        Name of the message level modifier to remove.
    
    .PARAMETER Modifier
        The actual modifier to remove, as returned by Get-DbaMessageLevelModifier.
    
    .PARAMETER EnableException
        This parameters disables user-friendly warnings and enables the throwing of exceptions.
        This is less user friendly, but allows catching exceptions in calling scripts.
    
    .EXAMPLE
        PS C:\> Get-DbaMessageLevelModifier | Remove-DbaMessageLevelModifier
        
        Removes all message level modifiers, restoring everything to their default levels.
    
    .EXAMPLE
        PS C:\> Remove-DbaMessageLevelModifier -Name "mymodule.foo"
        
        Removes the message level modifier named "mymodule.foo"
#>
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [string[]]
        $Name,
        
        [Parameter(ValueFromPipeline = $true)]
        [Sqlcollaborative.Dbatools.Message.MessageLevelModifier[]]
        $Modifier,
        
        [switch]
        $EnableException
    )
    
    process {
        foreach ($item in $Name) {
            if ($item -eq "Sqlcollaborative.Dbatools.Message.MessageLevelModifier") { continue }
            
            if ([Sqlcollaborative.Dbatools.Message.MessageHost]::MessageLevelModifiers.ContainsKey($item.ToLower())) {
                [Sqlcollaborative.Dbatools.Message.MessageHost]::MessageLevelModifiers.Remove($item.ToLower())
            }
            else {
                Stop-Function -Message "No message level modifier of name $item found!" -EnableException $EnableException -Category InvalidArgument -Continue
            }
        }
        foreach ($item in $Modifier) {
            if ([Sqlcollaborative.Dbatools.Message.MessageHost]::MessageLevelModifiers.ContainsKey($item.Name)) {
                [Sqlcollaborative.Dbatools.Message.MessageHost]::MessageLevelModifiers.Remove($item.Name)
            }
            else {
                Stop-Function -Message "No message level modifier of name $($item.Name) found!" -EnableException $EnableException -Category InvalidArgument -Continue
            }
        }
    }
}
tools\dbatools\internal\functions\Remove-InvalidFileNameChars.ps1
Function Remove-InvalidFileNameChars {
  param(
    [Parameter(Mandatory=$true,
      Position=0,
      ValueFromPipeline=$true,
      ValueFromPipelineByPropertyName=$true)]
    [String]$Name
  )

  $invalidChars = [IO.Path]::GetInvalidFileNameChars() -join ''
  $re = "[{0}]" -f [RegEx]::Escape($invalidChars)
  return ($Name -replace $re)
}
tools\dbatools\internal\functions\Resolve-IpAddress.ps1
function Resolve-IpAddress {
    # Uses the Beard's method to resolve IPs
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlInstance", "ComputerName", "SqlServer")]
        [object]$Server
    )

    if ($Server.GetType() -eq [Microsoft.SqlServer.Management.Smo.Server]) {
        return $ipaddress = ((Test-Connection $Server.ComputerName -Count 1 -ErrorAction SilentlyContinue).Ipv4Address).IPAddressToString
    }
    else {
        return $ipaddress = ((Test-Connection $server.Split('\')[0] -Count 1 -ErrorAction SilentlyContinue).Ipv4Address).IPAddressToString
    }
}
tools\dbatools\internal\functions\Resolve-NetBiosName.ps1
function Resolve-NetBiosName {
    <#
.SYNOPSIS
Internal function. Takes a best guess at the NetBIOS name of a server.
 #>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [object]$SqlInstance,
        [PSCredential]$SqlCredential
    )
    $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
    $server.ComputerName
}
tools\dbatools\internal\functions\Resolve-SqlIpAddress.ps1
function Resolve-SqlIpAddress {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [object]$SqlInstance,
        [PSCredential]$SqlCredential
    )

    $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
    $servernetbios = $server.ComputerNamePhysicalNetBIOS
    $ipaddr = (Test-Connection $servernetbios -count 1).Ipv4Address
    return $ipaddr
}
tools\dbatools\internal\functions\Select-DefaultView.ps1
function Select-DefaultView {
    <#

    This command enables us to send full on objects to the pipeline without the user seeing it

    See it in action in Get-DbaDbSnapshot and Remove-DbaDbSnapshot

    a lot of this is from boe, thanks boe!
    https://learn-powershell.net/2013/08/03/quick-hits-set-the-default-property-display-in-powershell-on-custom-objects/

    TypeName creates a new type so that we can use ps1xml to modify the output
    #>

    [CmdletBinding()]
    param (
        [parameter(ValueFromPipeline = $true)]
        [object]$InputObject,
        [string[]]$Property,
        [string[]]$ExcludeProperty,
        [string]$TypeName
    )
    process {

        if ($null -eq $InputObject) { return }

        if ($TypeName) {
            $InputObject.PSObject.TypeNames.Insert(0, "dbatools.$TypeName")
        }

        if ($ExcludeProperty) {
            if ($InputObject.GetType().Name.ToString() -eq 'DataRow') {
                $ExcludeProperty += 'Item', 'RowError', 'RowState', 'Table', 'ItemArray', 'HasErrors'
            }
            
            $props = ($InputObject | Get-Member | Where-Object MemberType -in 'Property', 'NoteProperty', 'AliasProperty' | Where-Object { $_.Name -notin $ExcludeProperty }).Name
            $defaultset = New-Object System.Management.Automation.PSPropertySet('DefaultDisplayPropertySet', [string[]]$props)
        }
        else {
            # property needs to be string
            if ("$property" -like "* as *") {
                $newproperty = @()
                foreach ($p in $property) {
                    if ($p -like "* as *") {
                        $old, $new = $p -isplit " as "
                        # Do not be tempted to not pipe here
                        $inputobject | Add-Member -Force -MemberType AliasProperty -Name $new -Value $old -ErrorAction SilentlyContinue
                        $newproperty += $new
                    }
                    else {
                        $newproperty += $p
                    }
                }
                $property = $newproperty
            }
            $defaultset = New-Object System.Management.Automation.PSPropertySet('DefaultDisplayPropertySet', [string[]]$Property)
        }

        $standardmembers = [System.Management.Automation.PSMemberInfo[]]@($defaultset)

        # Do not be tempted to not pipe here
        $inputobject | Add-Member -Force -MemberType MemberSet -Name PSStandardMembers -Value $standardmembers -ErrorAction SilentlyContinue

        $inputobject
    }
}
tools\dbatools\internal\functions\Set-ServiceStartMode.ps1
function Set-ServiceStartMode {
    <#
        .SYNOPSIS
        Internal function. Implements the method that changes startup mode of the SQL Server service.

        .DESCRIPTION
        Accepts objects from Get-DbaSqlService and performs a corresponding action.

        .PARAMETER InputObject
        A collection of services from Get-DbaSqlService.

        .PARAMETER Mode
        Startup mode of the service: Automatic, Manual or Disabled.

        .PARAMETER WhatIf
        Shows what would happen if the cmdlet runs. The cmdlet is not run.

        .PARAMETER Confirm
        Prompts you for confirmation before running the cmdlet.

        .NOTES
        Author: Kirill Kravtsov ( @nvarscar )

        dbatools PowerShell module (https://dbatools.io)
        Copyright (C) 2017 Chrissy LeMaire
        License: MIT https://opensource.org/licenses/MIT

        .EXAMPLE
        Get-DbaSqlService -ComputerName sql1 | Set-ServiceStartMode -Mode 'Manual'

        Sets all SQL services on sql1 to Manual startup.

        .EXAMPLE
        $services = Get-DbaSqlService -ComputerName sql1
        Set-ServiceStartMode -InputObject $services -Mode 'Automatic'

        Sets all SQL services on sql1 to Automatic startup.

#>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [string]$Mode,
        [parameter(ValueFromPipeline = $true, Mandatory = $true)]
        [object[]]$InputObject
    )
    begin {
        $callStack = Get-PSCallStack
        if ($callStack.Length -gt 1) {
            $callerName = $callStack[1].Command
        }
        else {
            $callerName = $callStack[0].Command
        }
        $ProcessArray = @()
    }
    process {
        #Get all the objects from the pipeline before proceeding
        $ProcessArray += $InputObject
    }
    end {
        $ProcessArray = $ProcessArray | Where-Object { (!$InstanceName -or $_.InstanceName -in $InstanceName) -and (!$Type -or $_.type -in $Type) }
        foreach ($service in $ProcessArray) {
            #Get WMI object
            $Wmi = Get-WmiObject Win32_Service -ComputerName $service.ComputerName -filter "name='$($service.ServiceName)'"
            if ($Pscmdlet.ShouldProcess($Wmi, "Changing the Start Mode to $Mode")) {
                $x = $Wmi.ChangeStartMode($Mode)
                if ($x.ReturnValue -ne 0) {
                    Write-Message -Level Warning -FunctionName $callerName -Message ("The attempt to $action the service $($job.ServiceName) on $($job.ComputerName) returned the following message: " + (Get-DbaSQLServiceErrorMessage $x.ReturnValue))
                }
            }
        }
    }
}
tools\dbatools\internal\functions\Show-Notification.ps1
function Show-Notification {
    param(
        $GalleryVersion,
        $Title = "dbatools update",
        $Text = "Version $GalleryVersion is now available"
    )
    # ensure the dbatools 'app' exists in registry so that it doesn't immediately disappear from Action Center
    $regPath = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Notifications\Settings'
    $appId = "{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\WindowsPowerShell\v1.0\powershell.exe"

    if (!(Test-Path -Path "$regPath\$appId")) {
        Write-Verbose "Adding required registry entry at $("$regPath\$appId")"
        $null = New-Item -Path "$regPath\$appId" -Force
        $null = New-ItemProperty -Path "$regPath\$appId" -Name 'ShowInActionCenter' -Value 1 -PropertyType 'DWORD' -Force
    }
    $null = [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime]
    $template = [Windows.UI.Notifications.ToastTemplateType]::ToastImageAndText02
    [xml]$toastTemplate = ([Windows.UI.Notifications.ToastNotificationManager]::GetTemplateContent($template).GetXml())

    [xml]$toastTemplate = "
    <toast launch=`"app-defined-string`">
        <visual>
            <binding template=`"ToastGeneric`">
                <text>`"$Title`"</text>
                <text>`"$Text`"</text>
            </binding>
        </visual>
        <actions>
            <action activationType=`"background`" content=`"OK`" arguments=`"later`"/>
        </actions>
    </toast>"

    $toastXml = New-Object -TypeName Windows.Data.Xml.Dom.XmlDocument
    $toastXml.LoadXml($toastTemplate.OuterXml)

    $notify = [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier($appId)
    $notify.Show($toastXml)
}
tools\dbatools\internal\functions\Start-DbaRunspace.ps1
function Start-DbaRunspace {
    <#
    .SYNOPSIS
        Starts a managed runspace

    .DESCRIPTION
        Starts a runspace that was registered to dbatools
        Simply registering does not automatically start a given runspace. Only by executing this function will it take effect.

    .PARAMETER Name
        The name of the registered runspace to launch

    .PARAMETER Runspace
        The runspace to launch. Returned by Get-DbaRunspace

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .EXAMPLE
        PS C:\> Start-DbaRunspace -Name 'mymodule.maintenance'

        Starts the runspace registered under the name 'mymodule.maintenance'
#>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [string[]]
        $Name,

        [Parameter(ValueFromPipeline = $true)]
        [Sqlcollaborative.Dbatools.Runspace.RunspaceContainer[]]
        $Runspace,

        [switch]
        [Alias('Silent')]$EnableException
    )

    process {
        foreach ($item in $Name) {
            # Ignore all output from Get-DbaRunspace - it'll be handled by the second loop
            if ($item -eq "Sqlcollaborative.Dbatools.Runspace.runspacecontainer") { continue }

            if ([Sqlcollaborative.Dbatools.Runspace.RunspaceHost]::Runspaces.ContainsKey($item.ToLower())) {
                try {
                    Write-Message -Level Verbose -Message "Starting runspace: $($item.ToLower())" -Target $item.ToLower()
                    [Sqlcollaborative.Dbatools.Runspace.RunspaceHost]::Runspaces[$item.ToLower()].Start()
                }
                catch {
                    Stop-Function -Message "Failed to start runspace: $($item.ToLower())" -EnableException $EnableException -Target $item.ToLower() -Continue
                }
            }
            else {
                Stop-Function -Message "Failed to start runspace: $($item.ToLower()) | No runspace registered under this name!" -EnableException $EnableException -Category InvalidArgument -Tag "fail", "argument", "runspace", "start" -Target $item.ToLower() -Continue
            }
        }

        foreach ($item in $Runspace) {
            try {
                Write-Message -Level Verbose -Message "Starting runspace: $($item.Name.ToLower())" -Target $item
                $item.Start()
            }
            catch {
                Stop-Function -Message "Failed to start runspace: $($item.Name.ToLower())" -EnableException $EnableException -Target $item -Continue
            }
        }
    }
}
tools\dbatools\internal\functions\Start-DbccCheck.ps1
function Start-DbccCheck {
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [object]$server,
        [string]$dbname,
        [switch]$table
    )

    $servername = $server.name

    if ($Pscmdlet.ShouldProcess($sourceserver, "Running dbcc check on $dbname on $servername")) {
        if ($server.ConnectionContext.StatementTimeout = 0 -ne 0) {
            $server.ConnectionContext.StatementTimeout = 0
        }

        try {
            if ($table) {
                $null = $server.databases[$dbname].CheckTables('None')
                Write-Verbose "Dbcc CheckTables finished successfully for $dbname on $servername"
            }
            else {
                $null = $server.Query("DBCC CHECKDB ([$dbname])")
                Write-Verbose "Dbcc CHECKDB finished successfully for $dbname on $servername"
            }
            return "Success"
        }
        catch {
            $message = $_.Exception
            if ($null -ne $_.Exception.InnerException) { $message = $_.Exception.InnerException }

            # english cleanup only sorry
            try {
                $newmessage = ($message -split "at Microsoft.SqlServer.Management.Common.ConnectionManager.ExecuteTSql")[0]
                $newmessage = ($newmessage -split "Microsoft.SqlServer.Management.Common.ExecutionFailureException:")[1]
                $newmessage = ($newmessage -replace "An exception occurred while executing a Transact-SQL statement or batch. ---> System.Data.SqlClient.SqlException:").Trim()
                $message = $newmessage
            }
            catch {
                $null
            }
            return $message.Trim()
        }
    }
}
tools\dbatools\internal\functions\Stop-DbaRunspace.ps1
function Stop-DbaRunspace {
    <#
    .SYNOPSIS
        Stops a managed runspace

    .DESCRIPTION
        Stops a runspace that was registered to dbatools.
        Will not cause errors if the runspace is already halted.

        Runspaces may not automatically terminate immediately when calling this function.
        Depending on the implementation of the scriptblock, this may in fact take a little time.
        If the scriptblock hasn't finished and terminated the runspace in a seemingly time, it will be killed by the system.
        This timeout is by default 30 seconds, but can be altered by using the Configuration System.
        For example, this line will increase the timeout to 60 seconds:
        Set-DbaConfig Runspace.StopTimeout 60

    .PARAMETER Name
        The name of the registered runspace to stop

    .PARAMETER Runspace
        The runspace to stop. Returned by Get-DbaRunspace

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .EXAMPLE
        PS C:\> Stop-DbaRunspace -Name 'mymodule.maintenance'

        Stops the runspace registered under the name 'mymodule.maintenance'
#>
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [string[]]
        $Name,

        [Parameter(ValueFromPipeline = $true)]
        [Sqlcollaborative.Dbatools.Runspace.RunspaceContainer[]]
        $Runspace,

        [switch]
        [Alias('Silent')]$EnableException
    )

    process {
        foreach ($item in $Name) {
            # Ignore all output from Get-DbaRunspace - it'll be handled by the second loop
            if ($item -eq "Sqlcollaborative.Dbatools.Runspace.runspacecontainer") { continue }

            if ([Sqlcollaborative.Dbatools.Runspace.RunspaceHost]::Runspaces.ContainsKey($item.ToLower())) {
                try {
                    Write-Message -Level Verbose -Message "Stopping runspace: $($item.ToLower())" -Target $item.ToLower()
                    [Sqlcollaborative.Dbatools.Runspace.RunspaceHost]::Runspaces[$item.ToLower()].Stop()
                }
                catch {
                    Stop-Function -Message "Failed to stop runspace: $($item.ToLower())" -EnableException $EnableException -Target $item.ToLower() -Continue
                }
            }
            else {
                Stop-Function -Message "Failed to stop runspace: $($item.ToLower()) | No runspace registered under this name!" -EnableException $EnableException -Category InvalidArgument -Target $item.ToLower() -Continue
            }
        }

        foreach ($item in $Runspace) {
            try {
                Write-Message -Level Verbose -Message "Stopping runspace: $($item.Name.ToLower())" -Target $item
                $item.Stop()
            }
            catch {
                Stop-Function -Message "Failed to stop runspace: $($item.Name.ToLower())" -EnableException $EnableException -Target $item -Continue
            }
        }
    }
}
tools\dbatools\internal\functions\Stop-Function.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#

function Stop-Function {
<#
    .SYNOPSIS
        Function that interrupts a function.
    
    .DESCRIPTION
        Function that interrupts a function.
        
        This function is a utility function used by other functions to reduce error catching overhead.
        It is designed to allow gracefully terminating a function with a warning by default and also allow opt-in into terminating errors.
        It also allows simple integration into loops.
        
        Note:
        When calling this function with the intent to terminate the calling function in non-EnableException mode too, you need to add a return below the call.
    
    .PARAMETER Message
        A message to pass along, explaining just what the error was.
    
    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
    
    .PARAMETER Category
        What category does this termination belong to?
        Mandatory so long as no inner exception is passed.
    
    .PARAMETER ErrorRecord
        An option to include an inner exception in the error record (and in the exception thrown, if one is thrown).
        Use this, whenever you call Stop-Function in a catch block.
        
        Note:
        Pass the full error record, not just the exception.
    
    .PARAMETER Tag
        Tags to add to the message written.
        This allows filtering and grouping by category of message, targeting specific messages.
    
    .PARAMETER FunctionName
        The name of the function to crash.
        This parameter is very optional, since it automatically selects the name of the calling function.
        The function name is used as part of the errorid.
        That in turn allows easily figuring out, which exception belonged to which function when checking out the $error variable.
    
    .PARAMETER File
        The file in which Stop-PSFFunction was called.
        Will be automatically set, but can be overridden when necessary.
    
    .PARAMETER Line
        The line on which Stop-PSFFunction was called.
        Will be automatically set, but can be overridden when necessary.
    
    .PARAMETER Target
        The object that was processed when the error was thrown.
        For example, if you were trying to process a Database Server object when the processing failed, add the object here.
        This object will be in the error record (which will be written, even in non-EnableException mode, just won't show it).
        If you specify such an object, it becomes simple to actually figure out, just where things failed at.
    
    .PARAMETER Exception
        Allows specifying an inner exception as input object. This will be passed on to the logging and used for messages.
        When specifying both ErrorRecord AND Exception, Exception wins, but ErrorRecord is still used for record metadata.
    
    .PARAMETER OverrideExceptionMessage
        Disables automatic appending of exception messages.
        Use in cases where you already have a speaking message interpretation and do not need the original message.
    
    .PARAMETER Continue
        This will cause the function to call continue while not running silently.
        Useful when mass-processing items where an error shouldn't break the loop.
    
    .PARAMETER SilentlyContinue
        This will cause the function to call continue while running silently.
        Useful when mass-processing items where an error shouldn't break the loop.
    
    .PARAMETER ContinueLabel
        When specifying a label in combination with "-Continue" or "-SilentlyContinue", this function will call continue with this specified label.
        Helpful when trying to continue on an upper level named loop.
    
    .EXAMPLE
        Stop-Function -Message "Foo failed bar!" -EnableException $EnableException -ErrorRecord $_
        return
        
        Depending on whether $EnableException is true or false it will:
        - Throw a bloody terminating error. Game over.
        - Write a nice warning about how Foo failed bar, then terminate the function. The return on the next line will then end the calling function.
    
    .EXAMPLE
        Stop-Function -Message "Foo failed bar!" -EnableException $EnableException -Category InvalidOperation -Target $foo -Continue
        
        Depending on whether $silent is true or false it will:
        - Throw a bloody terminating error. Game over.
        - Write a nice warning about how Foo failed bar, then call continue to process the next item in the loop.
        In both cases, the error record added to $error will have the content of $foo added, the better to figure out what went wrong.
#>
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
    [CmdletBinding(DefaultParameterSetName = 'Plain')]
    param (
        [Parameter(Mandatory = $true)]
        [string]
        $Message,

        [bool]
        [Alias('Silent')]
        $EnableException = $EnableException,

        [Parameter(ParameterSetName = 'Plain')]
        [Parameter(ParameterSetName = 'Exception')]
        [System.Management.Automation.ErrorCategory]
        $Category = ([System.Management.Automation.ErrorCategory]::NotSpecified),

        [Parameter(ParameterSetName = 'Exception')]
        [Alias('InnerErrorRecord')]
        [System.Management.Automation.ErrorRecord[]]
        $ErrorRecord,
        
        [string[]]
        $Tag,

        [string]
        $FunctionName = ((Get-PSCallStack)[0].Command),
        
        [string]
        $File,
        
        [int]
        $Line,

        [object]
        $Target,

        [System.Exception]
        $Exception,

        [switch]
        $OverrideExceptionMessage,

        [switch]
        $Continue,

        [switch]
        $SilentlyContinue,

        [string]
        $ContinueLabel
    )
    
    #region Initialize information on the calling command
    $callStack = (Get-PSCallStack)[1]
    if (-not $FunctionName) { $FunctionName = $callStack.Command }
    $ModuleName = "dbatools"
    if (-not $File) { $File = $callStack.Position.File }
    if (-not $Line) { $Line = $callStack.Position.StartLineNumber }
    #endregion Initialize information on the calling command
    
    #region Apply Transforms
    #region Target Transform
    if ($null -ne $Target) {
        $Target = Convert-DbaMessageTarget -Target $Target -FunctionName $FunctionName -ModuleName $ModuleName
    }
    #endregion Target Transform
    
    #region Exception Transforms
    if ($Exception) {
        $Exception = Convert-DbaMessageException -Exception $Exception -FunctionName $FunctionName -ModuleName $ModuleName
    }
    elseif ($ErrorRecord) {
        $int = 0
        while ($int -lt $ErrorRecord.Length) {
            $tempException = Convert-DbaMessageException -Exception $ErrorRecord[$int].Exception -FunctionName $FunctionName -ModuleName $ModuleName
            if ($tempException -ne $ErrorRecord[$int].Exception) {
                $ErrorRecord[$int] = New-Object System.Management.Automation.ErrorRecord($tempException, $ErrorRecord[$int].FullyQualifiedErrorId, $ErrorRecord[$int].CategoryInfo.Category, $ErrorRecord[$int].TargetObject)
            }
            
            $int++
        }
    }
    #endregion Exception Transforms
    #endregion Apply Transforms

    #region Message Handling
    $records = @()

    if ($ErrorRecord -or $Exception) {
        if ($ErrorRecord) {
            foreach ($record in $ErrorRecord) {
                if (-not $Exception) { $newException = New-Object System.Exception($record.Exception.Message, $record.Exception) }
                else { $newException = $Exception }
                if ($record.CategoryInfo.Category) { $Category = $record.CategoryInfo.Category }
                $records += New-Object System.Management.Automation.ErrorRecord($newException, "$($ModuleName)_$FunctionName", $Category, $Target)
            }
        }
        else {
            $records += New-Object System.Management.Automation.ErrorRecord($Exception, "$($ModuleName)_$FunctionName", $Category, $Target)
        }
        
        # Manage Debugging
        if ($EnableException) { Write-Message -Level Warning -Message $Message -EnableException $EnableException -FunctionName $FunctionName -Target $Target -ErrorRecord $records -Tag $Tag -ModuleName $ModuleName -OverrideExceptionMessage:$OverrideExceptionMessage -File $File -Line $Line 3>$null }
        else { Write-Message -Level Warning -Message $Message -EnableException $EnableException -FunctionName $FunctionName -Target $Target -ErrorRecord $records -Tag $Tag -ModuleName $ModuleName -OverrideExceptionMessage:$OverrideExceptionMessage -File $File -Line $Line }
    }
    else {
        $exception = New-Object System.Exception($Message)
        $records += New-Object System.Management.Automation.ErrorRecord($Exception, "dbatools_$FunctionName", $Category, $Target)
        
        # Manage Debugging
        if ($EnableException) { Write-Message -Level Warning -Message $Message -EnableException $EnableException -FunctionName $FunctionName -Target $Target -ErrorRecord $records -Tag $Tag -ModuleName $ModuleName -OverrideExceptionMessage:$true -File $File -Line $Line 3>$null}
        else { Write-Message -Level Warning -Message $Message -EnableException $EnableException -FunctionName $FunctionName -Target $Target -ErrorRecord $records -Tag $Tag -ModuleName $ModuleName -OverrideExceptionMessage:$true -File $File -Line $Line }
    }
    #endregion Message Handling



    #region EnableException Mode
    if ($EnableException) {
        if ($SilentlyContinue) {
            foreach ($record in $records) { Write-Error -Message $record -Category $Category -TargetObject $Target -Exception $record.Exception -ErrorId "dbatools_$FunctionName" -ErrorAction Continue }
            if ($ContinueLabel) { continue $ContinueLabel }
            else { Continue }
        }

        # Extra insurance that it'll stop
        Set-Variable -Name "__dbatools_interrupt_function_78Q9VPrM6999g6zo24Qn83m09XF56InEn4hFrA8Fwhu5xJrs6r" -Scope 1 -Value $true

        throw $records[0]
    }
    #endregion EnableException Mode

    #region Non-EnableException Mode
    else {
        # This ensures that the error is stored in the $error variable AND has its Stacktrace (simply adding the record would lack the stacktrace)
        foreach ($record in $records) {
            $null = Write-Error -Message $record -Category $Category -TargetObject $Target -Exception $record.Exception -ErrorId "dbatools_$FunctionName" -ErrorAction Continue 2>&1
        }

        if ($Continue) {
            if ($ContinueLabel) { continue $ContinueLabel }
            else { Continue }
        }
        else {
            # Make sure the function knows it should be stopping
            Set-Variable -Name "__dbatools_interrupt_function_78Q9VPrM6999g6zo24Qn83m09XF56InEn4hFrA8Fwhu5xJrs6r" -Scope 1 -Value $true

            return
        }
    }
    #endregion Non-EnableException Mode
}
tools\dbatools\internal\functions\Test-Bound.ps1
function Test-Bound {
    <#
        .SYNOPSIS
            Helperfunction that tests, whether a parameter was bound.

        .DESCRIPTION
            Helperfunction that tests, whether a parameter was bound.

        .PARAMETER ParameterName
            The name(s) of the parameter that is tested for being bound.
            By default, the check is true when AT LEAST one was bound.

        .PARAMETER Not
            Reverses the result. Returns true if NOT bound and false if bound.

        .PARAMETER And
            All specified parameters must be present, rather than at least one of them.

        .PARAMETER BoundParameters
            The hashtable of bound parameters. Is automatically inherited from the calling function via default value. Needs not be bound explicitly.

        .EXAMPLE
            if (Test-Bound "Day")
            {

            }

            Snippet as part of a function. Will check whether the parameter "Day" was bound. If yes, whatever logic is in the conditional will be executed.

        .EXAMPLE
            Test-Bound -Not 'Login', 'Spid', 'ExcludeSpid', 'Host', 'Program', 'Database'

            Returns whether none of the parameters above were specified.

        .EXAMPLE
            Test-Bound -And 'Login', 'Spid', 'ExcludeSpid', 'Host', 'Program', 'Database'

            Returns whether any of the specified parameters was not bound
    #>
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $true, Position = 0)]
        [string[]]
        $ParameterName,

        [Alias('Reverse')]
        [switch]
        $Not,

        [switch]
        $And,

        [object]
        $BoundParameters = (Get-PSCallStack)[0].InvocationInfo.BoundParameters
    )

    if ($And) {
        $test = $true
    }
    else {
        $test = $false
    }

    foreach ($name in $ParameterName) {
        if ($And) {
            if (-not $BoundParameters.ContainsKey($name)) { $test = $false }
        }
        else {
            if ($BoundParameters.ContainsKey($name)) { $test = $true }
        }
    }

    return ((-not $Not) -eq $test)
}
tools\dbatools\internal\functions\Test-ComputerTarget.ps1
function Test-ComputerTarget {
    <#
    .SYNOPSIS
        Validates wheher the input string can be legally used to target a computer.

    .DESCRIPTION
        Validates whether the input string can be legally used to target a computer.
        It will consider:
        - Names (NETBIOS/dns)
        - IPv4 Addresses
        - IPv6 Addresses
        It will resolve idn names into default ascii names according to the official rules, before rendering judgement.

    .PARAMETER ComputerName
        The name to verify

    .EXAMPLE
        PS C:\> Test-ComputerTarget -ComputerName 'server1'

        Will test whether 'server1' is a legal computername (hint: it is)

    .EXAMPLE
        PS C:\> "foo", "bar", "foo bar" | Test-ComputerTarget

        Will test, whether the names passed to it are legal targets.
        - The first two will pass, the last one will fail
        - Note that it will only return boolean values, so the order needs to be remembered (due to this, using it by pipeline on more than one object is not really recommended).
#>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true, Mandatory = $true)]
        [string[]]
        $ComputerName
    )

    process {
        foreach ($Computer in $ComputerName) {
            [Sqlcollaborative.Dbatools.Utility.Validation]::IsValidComputerTarget($ComputerName)
        }
    }
}
tools\dbatools\internal\functions\Test-DbaDeprecation.ps1
function Test-DbaDeprecation {
    <#
        .SYNOPSIS
            Tests whether a function or one of its parameters was called by a bad name.

        .DESCRIPTION
            Tests whether a function or one of its parameters was called by a bad name.
            This allows giving deprecation warnings - once per session - whenever a user uses something we are planning on removing.

            For example, when renaming a function, we give a grace period by adding an Alias for that function with its old name.
            However, we do not want to carry along this alias forever, so we give warning ahead of time using this function.
            When reaching the specified version, we then can safely remove the alias.

            Furthermore, this function is used for testing, whether such a removal was properly done.

        .PARAMETER DeprecatedOn
            The version this parameter or alias will be removed in.
            Generally, deprecated parameters and aliases should only be removed on major releases.

        .PARAMETER FunctionName
            Automatically filled with the calling function.
            The name of the function that contains either a deprecated alias or parameter.

        .PARAMETER Call
            The InvocationInfo of the calling function.
            Automatically filled.

        .PARAMETER Parameter
            The parameter that has become deprecated.
            On renamed parameters, keep a parameter-alias. This function will notice, when the alias is used.

        .PARAMETER Alias
            The alias of the command that will be deprecated.

        .PARAMETER CustomMessage
            This function will generate a default message. However, this may not always be appropriate.
            Use CustomMessage to tailor a response to the necessity of the moment.

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .EXAMPLE
            PS C:\> Test-DbaDeprecation -DeprecatedOn "1.0.0.0" -Parameter 'Details'

            Will - once per session - complain if the parameter 'Details' is used.
            Will cause tests to fail, if it's still in the code after release 1.0.0.0.

        .EXAMPLE
            PS C:\> Test-DbaDeprecation -DeprecatedOn "1.0.0.0" -Alias Copy-SqlDatabase

            Will - once per session - complain if the alias 'Copy-SqlDatabase' is used.
            Will cause tests to fail, if it's still in the code after release 1.0.0.0.
    #>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Version]
        $DeprecatedOn,

        [string]
        $FunctionName = (Get-PSCallStack)[0].Command,

        [object]
        $Call = (Get-PSCallStack)[0].InvocationInfo,

        [Parameter(ParameterSetName = "Param", Mandatory = $true)]
        [string]
        $Parameter,

        [Parameter(ParameterSetName = "Alias", Mandatory = $true)]
        [string]
        $Alias,

        [string]
        $CustomMessage,

        [bool]
        [Alias('Silent')]
        $EnableException = $EnableException
    )

    switch ($PSCmdlet.ParameterSetName) {
        "Param" {
            $ast = [System.Management.Automation.Language.Parser]::ParseInput($Call.Line, [ref]$null, [ref]$null)
            $objects = $ast.FindAll( { $args[0] -is [System.Management.Automation.Language.CommandAst] }, $true)
            $sub = $objects | Where-Object Parent -Like "$($Call.InvocationName)*" | Select-Object -First 1

            if ($sub.CommandElements | Where-Object ParameterName -eq $Parameter) {
                if ($CustomMessage) { $Message = $CustomMessage }
                else { $Message = "Using the parameter $Parameter is deprecated. This parameter will be removed in version $DeprecatedOn, check in the documentation what parameter to use instead" }

                Write-Message -Message $Message -Level Warning -FunctionName $FunctionName -Once "Deprecated.Alias.$Alias"
            }
        }

        "Alias" {
            if ($Alias -eq $Call.InvocationName) {
                if ($CustomMessage) { $Message = $CustomMessage }
                else { $Message = "Using the alias $Alias is deprecated. This alias will be removed in version $DeprecatedOn, use $FunctionName instead" }

                Write-Message -Message $Message -Level Warning -FunctionName $FunctionName -Once "Deprecated.Alias.$Alias"
            }
        }
    }
}
tools\dbatools\internal\functions\Test-DbaLsnChain.ps1
function Test-DbaLsnChain {
    <#
    .SYNOPSIS
        Checks that a filtered array from Get-FilteredRestore contains a restorabel chain of LSNs

    .DESCRIPTION
        Finds the anchoring Full backup (or multiple if it's a striped set).
        Then filters to ensure that all the backups are from that anchor point (LastLSN) and that they're all on the same RecoveryForkID
        Then checks that we have either enough Diffs and T-log backups to get to where we want to go. And checks that there is no break between
        LastLSN and FirstLSN in sequential files

    .PARAMETER FilteredRestoreFiles
        This is just an object consisting of the output from Read-DbaBackupHeader. Normally this will have been filtered down to a restorable chain
        before arriving here. (ie; only 1 anchoring Full backup)

    .NOTES
        Author: Stuart Moore (@napalmgram), stuart-moore.com
        Tags:
        dbatools PowerShell module (https://dbatools.io, [email protected])
        Copyright (C) 2016 Chrissy LeMaire
        License: MIT https://opensource.org/licenses/MIT

    .EXAMPLE
        Test-DbaLsnChain -FilteredRestoreFiles $FilteredFiles

        Checks that the Restore chain in $FilteredFiles is complete and can be fully restored

#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [object[]]$FilteredRestoreFiles,
        [switch]$Continue,
        [switch]$EnableException
    )


    begin {
        #Need to anchor  with full backup:
        $TestHistory = @()
    }
    process {
        foreach ($bh in $FilteredRestoreFiles) {
            $TestHistory += $bh
        }
    }
    end {
        if ($continue) {
            return $true
        }
        Write-Message -Level Verbose -Message "Testing LSN Chain"
        if ($null -eq $TestHistory[0].BackupTypeDescription) {
            $TypeName = 'Type'
        }
        else {
            $TypeName = "BackupTypeDescription"
        }
        Write-Message -Level VeryVerbose -Message "Testing LSN Chain - Type $typename"
        $FullDBAnchor = $TestHistory | Where-Object {$_.$TypeName -in ('Database', 'Full') }

        if (($FullDBAnchor | Group-Object -Property FirstLSN | Measure-Object).Count -ne 1) {
            $cnt = ($FullDBAnchor | Group-Object -Property FirstLSN | Measure-Object).Count
            foreach ($tFile in $FullDBAnchor) {
                Write-Message -Level Debug -Message "$($tfile.FirstLsn) - $($tfile.TypeName)"
            }
            Write-Message -Level Verbose -Message "db count = $cnt"
            Write-Message -Level Warning -Message "More than 1 full backup from a different LSN, or less than 1, neither supported"

            return $false
            break;
        }

        #Via LSN chain:
        [BigInt]$CheckPointLSN = ($FullDBAnchor | Select-Object -First 1).CheckPointLSN.ToString()
        [BigInt]$FullDBLastLSN = ($FullDBAnchor | Select-Object -First 1).LastLSN.ToString()
        $BackupWrongLSN = $FilteredRestoreFiles | Where-Object {$_.DatabaseBackupLSN -ne $CheckPointLSN}
        #Should be 0 in there, if not, lets check that they're from during the full backup
        if ($BackupWrongLSN.count -gt 0 ) {
            if (($BackupWrongLSN | Where-Object {[BigInt]$_.LastLSN.ToString() -lt $FullDBLastLSN}).count -gt 0) {
                Write-Message -Level Warning -Message "We have non matching LSNs - not supported"
                return $false
                break;
            }
        }
        $DiffAnchor = $TestHistory | Where-Object {$_.$TypeName -in ('Database Differential', 'Differential')}
        #Check for no more than a single Differential backup
        if (($DiffAnchor.FirstLSN | Select-Object -unique | Measure-Object).count -gt 1) {
            Write-Message -Level Warning -Message "More than 1 differential backup, not supported"
            return $false
            break;
        }
        elseif (($DiffAnchor | Measure-Object).Count -eq 1) {
            Write-Message -Level VeryVerbose -Message "Found a diff file, setting Log Anchor"
            $TlogAnchor = $DiffAnchor
        }
        else {
            $TlogAnchor = $FullDBAnchor
        }


        #Check T-log LSNs form a chain.
        $TranLogBackups = $TestHistory | Where-Object {$_.$TypeName -in ('Transaction Log', 'Log') -and $_.DatabaseBackupLSN -eq $FullDBAnchor.CheckPointLSN} | Sort-Object -Property LastLSN, FirstLsn
        for ($i = 0; $i -lt ($TranLogBackups.count)) {
            Write-Message -Level Debug -Message "looping t logs"
            if ($i -eq 0) {
                if ($TranLogBackups[$i].FirstLSN -gt $TlogAnchor.LastLSN) {
                    Write-Message -Level Warning -Message "Break in LSN Chain between $($TlogAnchor.FullName) and $($TranLogBackups[($i)].FullName) "
                    Write-Message -Level Verbose -Message "Anchor $($TlogAnchor.LastLSN) - FirstLSN $($TranLogBackups[$i].FirstLSN)"
                    return $false
                    break
                }
            }
            else {
                if ($TranLogBackups[($i - 1)].LastLsn -ne $TranLogBackups[($i)].FirstLSN -and ($TranLogBackups[($i)] -ne $TranLogBackups[($i - 1)])) {
                    Write-Message -Level Warning -Message "Break in transaction log between $($TranLogBackups[($i-1)].FullName) and $($TranLogBackups[($i)].FullName) "
                    return $false
                    break
                }
            }
            $i++

        }
        Write-Message -Level VeryVerbose -Message "Passed LSN Chain checks"
        return $true
    }
}
tools\dbatools\internal\functions\Test-DbaRestoreVersion.ps1
function Test-DbaRestoreVersion {
    <#
    .SYNOPSIS
        Checks that the restore files are from a version of SQL Server that can be restored on the target version

    .DESCRIPTION
        Finds the anchoring Full backup (or multiple if it's a striped set).
        Then filters to ensure that all the backups are from that anchor point (LastLSN) and that they're all on the same RecoveryForkID
        Then checks that we have either enough Diffs and T-log backups to get to where we want to go. And checks that there is no break between
        LastLSN and FirstLSN in sequential files

    .PARAMETER FilteredRestoreFiles
        This is just an object consisting of the output from Read-DbaBackupHeader. Normally this will have been filtered down to a restorable chain
        before arriving here. (ie; only 1 anchoring Full backup)

    .PARAMETER SqlInstance
        Sql Server Instance against which the restore is going to be performed

    .PARAMETER SqlCredential
        Credential for connecting to SqlInstance

    .PARAMETER SystemDatabaseRestore
        Switch when restoring system databases

    .NOTES
        Author: Stuart Moore (@napalmgram), stuart-moore.com
        Tags:
        dbatools PowerShell module (https://dbatools.io, [email protected])
        Copyright (C) 2016 Chrissy LeMaire
        License: MIT https://opensource.org/licenses/MIT

    .EXAMPLE
        Test-DbaRestoreVersion -FilteredRestoreFiles $FilteredFiles -SqlInstance server1\instance1

        Checks that the Restore chain in $FilteredFiles is compatible with the SQL Server version of server1\instance1

#>
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true)]
        [Alias("ServerInstance", "SqlServer")]
        [object]$SqlInstance,
        [parameter(Mandatory = $true)]
        [object[]]$FilteredRestoreFiles,
        [PSCredential]$SqlCredential,
        [switch]$SystemDatabaseRestore
    )
    $RestoreVersion = ($FilteredRestoreFiles.SoftwareVersionMajor | Measure-Object -average).average
    Write-Message -Level Verbose -Message "RestoreVersion is $RestoreVersion"
    #Test to make sure we don't have an upgrade mid backup chain, there's a reason I'm paranoid..
    if ([int]$RestoreVersion -ne $RestoreVersion) {
        Write-Message -Level Warning -Message "Version number change during backups - $RestoreVersion"
        return $false
        break
    }
    #Can't restore backwards
    try {
        if ($SqlInstance -isnot [Microsoft.SqlServer.Management.Smo.SqlSmoObject]) {
            $Newconnection = $true
            $Server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
        }
        else {
            $server = $SqlInstance
        }
    }
    catch {
        Write-Message -Level Warning -Message "Cannot connect to $SqlInstance"
        break
    }

    if ($SystemDatabaseRestore) {
        if ($RestoreVersion -ne $Server.VersionMajor) {
            Write-Message -Level Warning -Message "For System Database restore versions must match)"
            return $false
            break
        }
    }
    else {
        if ($RestoreVersion -gt $Server.VersionMajor) {
            Write-Message -Level Warning -Message "Backups are from a newer version of SQL Server than $($Server.Name)"
            return $false
            break
        }

        if (($Server.VersionMajor -gt 10 -and $RestoreVersion -lt 9)  ) {
            Write-Message -Level Warning -Message "This version - $RestoreVersion - too old to restore on to $($Server.Name)"
            return $false
            break
        }
    }
    if ($Newconnection) {
        $server.ConnectionContext.Disconnect()
    }
    return $True
}

tools\dbatools\internal\functions\Test-ElevationRequirement.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#

function Test-ElevationRequirement {
    <#
        .SYNOPSIS
            Command that tests, whether the process runs elevated and has to run as such.

        .DESCRIPTION
            Command that tests, whether the process runs elevated and has to run as such.
            Some commands require to be run elevated, when executed against localhost, but not when run against a remote computer.
            This command handles that test and manages the reaction to it.

        .PARAMETER ComputerName
            The computer that is being targeted by the calling command.
            This must be a localhost variety, for it to be able to fail.

        .PARAMETER Continue
            When using the native capability to terminate on fail, this will call continue in non-EnableException mode.

        .PARAMETER ContinueLabel
            When using the native capability to terminate on fail, and using a continue mode, the continue will continue with this label.

        .PARAMETER SilentlyContinue
            When using the native capability to terminate on fail, this will call continue in EnableException mode.

        .PARAMETER NoStop
            Does not call stop-function when the test fails, rather only returns $false instead

        .PARAMETER EnableException
            By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
            This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
            Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

        .EXAMPLE
            $null = Test-ElevationRequirement -ComputerName $instance -Continue

            This will test whether the currently processed instance is localhost and the process is running elevated.
            If it should have elevation but is not running with elevation:
            - In silent mode it will termiante with an exception
            - In default mode, it will continue with the next instance

        .EXAMPLE
            if (-not ( Test-ElevationRequirement -ComputerName $instance -NoStop)) {
                # Do whatever
            }

        This will test whether the currently processed instance is localhost and the process is running elevated.
        If it isn't running elevated but should be, the overall condition will be met and the if-block is executed.
    #>
    [CmdletBinding(DefaultParameterSetName = 'Stop')]
    param (
        [DbaInstanceParameter]
        $ComputerName,

        [Parameter(ParameterSetName = 'Stop')]
        [switch]
        $Continue,

        [Parameter(ParameterSetName = 'Stop')]
        [string]
        $ContinueLabel,

        [Parameter(ParameterSetName = 'Stop')]
        [switch]
        $SilentlyContinue,

        [Parameter(ParameterSetName = 'NoStop')]
        [switch]
        $NoStop,

        [bool]
        [Alias('Silent')]
        $EnableException = $EnableException
    )

    $isElevated = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
    $testResult = $true
    if ($ComputerName.IsLocalHost -and (-not $isElevated)) { $testResult = $false }

    if ($PSCmdlet.ParameterSetName -like "NoStop") {
        return $testResult
    }
    elseif ($PSCmdlet.ParameterSetName -like "Stop") {
        if ($testResult) { return $testResult }

        $splatStopFunction = @{
            Message = "Console not elevated, but elevation is required to perform some actions on localhost for this command."
        }

        if (Test-Bound "Continue") { $splatStopFunction["Continue"] = $Continue }
        if (Test-Bound "ContinueLabel") { $splatStopFunction["ContinueLabel"] = $ContinueLabel }
        if (Test-Bound "SilentlyContinue") { $splatStopFunction["SilentlyContinue"] = $SilentlyContinue }

        . Stop-Function @splatStopFunction -FunctionName (Get-PSCallStack)[1].Command
        return $testResult
    }
}
tools\dbatools\internal\functions\Test-FunctionInterrupt.ps1
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#

function Test-FunctionInterrupt {
    <#
        .SYNOPSIS
            Internal tool, used to gracefully interrupt a function.

        .DESCRIPTION
            This helper function is designed to work in tandem with Stop-Function.
            When gracefully terminating a function, there is a major issue:
            "Return" will only stop the current one of the three blocks (Begin, Process, End).
            All other statements have side effects or produce lots of red text.

            So, Stop-Function writes a variable into the parent scope, that signals the function should cease.
            This function then checks for that very variable and returns true if it is set.

            This avoids having to handle odd variables in the parent function and causes the least impact on contributors.

        .EXAMPLE
            if (Test-FunctionInterrupt) { return }

            The calling function will stop if this function returns true.
    #>
    [CmdletBinding()]
    param (

    )

    $var = Get-Variable -Name "__dbatools_interrupt_function_78Q9VPrM6999g6zo24Qn83m09XF56InEn4hFrA8Fwhu5xJrs6r" -Scope 1 -ErrorAction Ignore
    if ($var.Value) { return $true }

    return $false
}
tools\dbatools\internal\functions\Test-HostOSLinux.ps1
function Test-HostOSLinux {
    param (
        [object]$SqlInstance,
        [object]$sqlcredential
    )

    $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $sqlcredential
    $server.ConnectionContext.ExecuteScalar("SELECT @@VERSION") -match "Linux"
}
tools\dbatools\internal\functions\Test-PSRemoting.ps1
#requires -version 3.0

function Test-PSRemoting {
    <#
    Jeff Hicks
    https://www.petri.com/test-network-connectivity-powershell-test-connection-cmdlet
#>
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUsePSCredentialType", "")]
    [Cmdletbinding()]
    param(
        [Parameter(Position = 0, Mandatory, ValueFromPipeline)]
        [DbaInstance]$ComputerName,
        $Credential = [System.Management.Automation.PSCredential]::Empty,
        [Alias('Silent')]
        [switch]$EnableException
    )

    process {
        Write-Message -Level VeryVerbose -Message "Testing $($ComputerName.Computername)"
        try {
            $null = Test-WSMan -ComputerName $ComputerName.ComputerName -Credential $Credential -Authentication Default -ErrorAction Stop
            $true
        }
        catch {
            Write-Message -Level Verbose -Message "Testing $($ComputerName.Computername)" -Target $ComputerName -ErrorRecord $_
            $false
        }

    } #process

} #close function
tools\dbatools\internal\functions\Test-SqlAgent.ps1
function Test-SqlAgent {
    <#
    .SYNOPSIS
        Internal function. Checks to see if SQL Server Agent is running on a server.
#>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [Alias("ServerInstance", "SqlServer")]
        [object]$SqlInstance,
        [PSCredential]$SqlCredential
    )

    if ($SqlInstance.GetType() -ne [Microsoft.SqlServer.Management.Smo.Server]) {
        $SqlInstance = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
    }

    if ($null -eq $SqlInstance.JobServer) { return $false }
    try { $null = $SqlInstance.JobServer.script(); return $true }
    catch { return $false }
}
tools\dbatools\internal\functions\Test-SqlLoginAccess.ps1
function Test-SqlLoginAccess {
    <#
    .SYNOPSIS
        Internal function. Ensures login has access on SQL Server.
#>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [Alias("ServerInstance", "SqlServer")]
        [object]$SqlInstance,
        [PSCredential]$SqlCredential,
        [string]$Login
        #[switch]$Detailed - can return if its a login or just has access
    )

    if ($SqlInstance.GetType() -ne [Microsoft.SqlServer.Management.Smo.Server]) {
        $SqlInstance = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
    }

    if (($SqlInstance.Logins.Name) -notcontains $Login) {
        try {
            $rows = $SqlInstance.ConnectionContext.ExecuteScalar("EXEC xp_logininfo '$Login'")

            if (($rows | Measure-Object).Count -eq 0) {
                return $false
            }
        }
        catch {
            return $false
        }
    }
    return $true
}
tools\dbatools\internal\functions\Test-SqlQueryComplete.ps1
function Test-SqlQueryComplete {
    param (
        [Alias("SqlInstance", "SqlServer")]
        [object]$server,
        [string]$sql,
        [switch]$checkpid
    )

    if ($checkpid) {
        $sqlpid = " and session_id = $sqlpid"
    }

    $sqlpid = $server.ConnectionContext.ProcessID
    $sqlpid = " and session_id = $sqlpid"
    $sql = $sql.Replace("'", "''")
    $testsql = "select sqltext.text FROM sys.dm_exec_requests req CROSS APPLY sys.dm_exec_sql_text(sql_handle) AS sqltext where text = '$sql' $sqlpid"

    if ($server.ConnectionContext.ExecuteScalar($testsql) -ne $null) {
        return $false
    }
    else {
        return $true
    }
}
tools\dbatools\internal\functions\Test-SqlSa.ps1
function Test-SqlSa {
    <#
    .SYNOPSIS
        Internal function. Ensures sysadmin account access on SQL Server.
#>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [Alias("ServerInstance", "SqlServer")]
        [object]$SqlInstance,
        [PSCredential]$SqlCredential
    )

    try {

        if ($SqlInstance.GetType() -eq [Microsoft.SqlServer.Management.Smo.Server]) {
            return ($SqlInstance.ConnectionContext.FixedServerRoles -match "SysAdmin")
        }

        $server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
        return ($server.ConnectionContext.FixedServerRoles -match "SysAdmin")
    }
    catch { return $false }
}
tools\dbatools\internal\functions\Update-ServiceStatus.ps1
function Update-ServiceStatus {
    <#
    .SYNOPSIS
        Internal function. Sends start/stop request to a SQL Server service and wait for the result.

    .DESCRIPTION
        Accepts objects from Get-DbaSqlService and performs a corresponding action.

    .PARAMETER Credential
        Credential object used to connect to the computer as a different user.

    .PARAMETER Timeout
        How long to wait for the start/stop request completion before moving on.

    .PARAMETER InputObject
        A collection of services from Get-DbaSqlService

    .PARAMETER Action
        Start or stop.

    .PARAMETER EnableException
        By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
        This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
        Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.

    .PARAMETER WhatIf
        Shows what would happen if the cmdlet runs. The cmdlet is not run.

    .PARAMETER Confirm
        Prompts you for confirmation before running the cmdlet.

    .NOTES
        Author: Kirill Kravtsov ( @nvarscar )
        Tags:
        dbatools PowerShell module (https://dbatools.io)
        Copyright (C) 2016 Chrissy LeMaire
        License: MIT https://opensource.org/licenses/MIT

    .EXAMPLE
        $InputObject = Get-DbaSqlService -ComputerName sql1
        Update-ServiceStatus -InputObject $InputObject -Action 'stop' -Timeout 30
        Update-ServiceStatus -InputObject $InputObject -Action 'start' -Timeout 30

        Restarts SQL services on sql1

    .EXAMPLE
        $InputObject = Get-DbaSqlService -ComputerName sql1
        $credential = Get-Credential
        Update-ServiceStatus -InputObject $InputObject -Action 'stop' -Timeout 0 -Credential $credential

        Stops SQL services on sql1 and waits indefinitely for them to stop. Uses $credential to authorize on the server.
#>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param(
        [parameter(ValueFromPipeline = $true, Mandatory = $true)]
        [object[]]$InputObject,
        [parameter(Mandatory = $true)]
        [string[]]$Action,
        [int]$Timeout = 30,
        [PSCredential] $Credential,
        [bool][Alias('Silent')]$EnableException
    )
    begin {
        $callStack = Get-PSCallStack
        if ($callStack.Length -gt 1) {
            $callerName = $callStack[1].Command
        }
        else {
            $callerName = $callStack[0].Command
        }
        #Prepare the service control script block
        $svcControlBlock = {
            param (
                $server,
                $service,
                $action,
                $timeout,
                [System.Management.Automation.PSCredential]
                $credential
            )

            #Perform $action
            if ($action -in 'start', 'restart') {
                $methodName = 'StartService'
                $desiredState = 'Running'
                $undesiredState = 'Stopped'
            }
            elseif ($action -eq 'stop') {
                $methodName = 'StopService'
                $desiredState = 'Stopped'
                $undesiredState = 'Running'
            }
            #Get CIM object
            try {
                $svc = Get-DbaCmObject -ComputerName $server -Namespace "root\cimv2" -query "SELECT * FROM Win32_Service WHERE name = '$service'" -Credential $credential
            }
            catch {
                throw $_
                break
            }
            #Invoke corresponding CIM method
            $x = Invoke-CimMethod -InputObject $svc -MethodName $methodName

            $result = [psobject](@{} | Select-Object ExitCode, ServiceState)
            #If command was not accepted
            if ($x.ReturnValue -ne 0) {
                $result.ExitCode = $x.ReturnValue
                $result.ServiceState = $svc.State
            }
            else {
                $startTime = Get-Date
                #Wait for the service to complete the action until timeout
                while ($true) {
                    try {
                        $svc = Get-DbaCmObject -ComputerName $server -Namespace "root\cimv2" -query "SELECT State FROM Win32_Service WHERE name = '$service'" -Credential $credential
                    }
                    catch {
                        throw $_
                        break
                    }
                    $result.ServiceState = $svc.State
                    #Succeeded
                    if ($svc.State -eq $desiredState) { $result.ExitCode = 0; break }
                    #Failed after being in the Pending state
                    if ($pending -and $svc.State -eq $undesiredState) { $result.ExitCode = -2; break }
                    #Timed out
                    if ($timeout -gt 0 -and ((Get-Date) - $startTime).TotalSeconds -gt $timeout) { $result.ExitCode = -1; break}
                    #Still pending
                    if ($svc.State -like '*Pending') { $pending = $true }
                    Start-Sleep -Milliseconds 100
                }
            }
            $result
        }

        $actionText = switch ($action) { stop { 'stopped' }; start { 'started' }; restart { 'restarted' } }
        #Setup initial session state
        $InitialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
        $InitialSessionState.ImportPSModule((get-module dbatools).modulebase + '\dbatools.psd1')
        #Create Runspace pool, min - 1, max - 50 sessions
        $runspacePool = [runspacefactory]::CreateRunspacePool(1, 50, $InitialSessionState, $Host)
        $runspacePool.Open()
    }

    process {
        $threads = @()

        #Get priorities on which the service startup/shutdown order is based
        $servicePriorityCollection = $InputObject.ServicePriority | Select-Object -unique | Sort-Object -Property @{ Expression = { [int]$_ }; Descending = $action -ne 'stop' }
        foreach ($priority in $servicePriorityCollection) {
            foreach ($service in ($InputObject | Where-Object { $_.ServicePriority -eq $priority })) {
                if ('dbatools.DbaSqlService' -in $service.PSObject.TypeNames) {
                    if (($service.State -eq 'Running' -and $action -eq 'start') -or ($service.State -eq 'Stopped' -and $action -eq 'stop')) {
                        Add-Member -Force -InputObject $service -NotePropertyName Status -NotePropertyValue 'Successful'
                        Add-Member -Force -InputObject $service -NotePropertyName Message -NotePropertyValue "The service is already $actionText, no action required"
                        Select-DefaultView -InputObject $service -Property ComputerName, ServiceName, State, Status, Message
                    }
                    elseif ($service.StartMode -eq 'Disabled' -and $action -in 'start', 'restart') {
                        Add-Member -Force -InputObject $service -NotePropertyName Status -NotePropertyValue 'Failed'
                        Add-Member -Force -InputObject $service -NotePropertyName Message -NotePropertyValue "The service is disabled and cannot be $actionText"
                        Select-DefaultView -InputObject $service -Property ComputerName, ServiceName, State, Status, Message
                    }
                    else {
                        if ($Pscmdlet.ShouldProcess("Sending $action request to service $($service.ServiceName) on $($service.ComputerName)")) {
                            #Create parameters hashtable
                            $argsRunPool = @{
                                server     = $service.computerName
                                service    = $service.ServiceName
                                action     = $action
                                timeout    = $Timeout
                                credential = $Credential
                            }
                            Write-Message -Level Verbose -Message "Sending $action request to service $($service.ServiceName) on $($service.ComputerName) with timeout $Timeout"
                            #Create new runspace thread
                            $thread = [powershell]::Create()
                            $thread.RunspacePool = $runspacePool
                            $thread.AddScript($svcControlBlock) | Out-Null
                            $thread.AddParameters($argsRunPool) | Out-Null
                            #Start the thread
                            $handle = $thread.BeginInvoke()
                            $threads += [pscustomobject]@{
                                handle       = $handle
                                thread       = $thread
                                serviceName  = $service.ServiceName
                                computerName = $service.ComputerName
                                isRetrieved  = $false
                                started      = Get-Date
                            }
                        }
                    }
                }
                else {
                    Stop-Function -FunctionName $callerName -Message "Unknown object in pipeline - make sure to use Get-DbaSqlService cmdlet" -EnableException $EnableException
                    Return
                }
            }
            if ($Pscmdlet.ShouldProcess("Waiting for the services to $action")) {
                #Get job execution results
                while ($threads | Where-Object { $_.isRetrieved -eq $false }) {
                    foreach ($thread in ($threads | Where-Object { $_.isRetrieved -eq $false })) {
                        if ($thread.Handle.IsCompleted -eq $true) {
                            Write-Message -Level Verbose -Message "Processing runspace thread results from service $($thread.ServiceName) on $($thread.ComputerName)"
                            $jobResult = $null
                            try {
                                $jobResult = $thread.thread.EndInvoke($thread.handle)
                            }
                            catch {
                                $jobError = $_
                                Write-Message -Level Verbose -Message ("Could not return data from the runspace thread: " + $_.Exception.Message)
                            }
                            $thread.isRetrieved = $true
                            if ($thread.thread.HadErrors) {
                                if (!$jobError) { $jobError = $thread.thread.Streams.Error }
                                Stop-Function -EnableException $EnableException -FunctionName $callerName -Message ("The attempt to $action the service $($thread.ServiceName) on $($thread.ComputerName) returned the following error: " + ($jobError.Exception.Message -join ' ')) -Category ConnectionError -ErrorRecord $thread.thread.Streams.Error -Target $thread -Continue
                            }
                            elseif (!$jobResult) {
                                Stop-Function -EnableException $EnableException -FunctionName $callerName -Message ("The attempt to $action the service $($thread.ServiceName) on $($thread.ComputerName) did not return any results") -Category ConnectionError -ErrorRecord $_ -Target $thread -Continue
                            }
                            #Find a corresponding service object
                            $outObject = $InputObject | Where-Object { $_.ServiceName -eq $thread.serviceName -and $_.ComputerName -eq $thread.computerName }
                            #Set additional properties
                            $status = switch ($jobResult.ExitCode) {
                                0 { 'Successful' }
                                10 { 'Successful '} #Already running - FullText service is started automatically
                                default { 'Failed' }
                            }
                            Add-Member -Force -InputObject $outObject -NotePropertyName Status -NotePropertyValue $status
                            $message = switch ($jobResult.ExitCode) {
                                -2 { "The service failed to $action." }
                                -1 { "The attempt to $action the service has timed out." }
                                0 { "Service was successfully $actionText." }
                                default { "The attempt to $action the service returned the following error: " + (Get-DBASQLServiceErrorMessage $jobResult.ExitCode) }
                            }
                            Add-Member -Force -InputObject $outObject -NotePropertyName Message -NotePropertyValue $message
                            if ($jobResult.ServiceState) { $outObject.State = $jobResult.ServiceState }
                            #Dispose of the thread
                            $thread.thread.Dispose()

                            Select-DefaultView -InputObject $outObject -Property ComputerName, ServiceName, State, Status, Message
                        }
                        elseif ($Timeout -gt 0 -and ((Get-Date) - $thread.started).TotalSeconds -gt $Timeout) {
                            #Session has timed out - return failure and stop the thread

                            $thread.isRetrieved = $true
                            $outObject = $InputObject | Where-Object { $_.ServiceName -eq $thread.serviceName -and $_.ComputerName -eq $thread.computerName }
                            #Set additional properties
                            Add-Member -Force -InputObject $outObject -NotePropertyName Status -NotePropertyValue 'Failed'
                            Add-Member -Force -InputObject $outObject -NotePropertyName Message -NotePropertyValue "The attempt to $action the service has timed out."
                            $outObject.State = 'Unknown'
                            #Stop and dispose of the thread
                            $thread.thread.Stop()
                            $thread.thread.Dispose()

                            Select-DefaultView -InputObject $outObject -Property ComputerName, ServiceName, State, Status, Message
                        }
                    }
                    Start-Sleep -Milliseconds 50
                }
            }
        }
    }
    end {
        #Close the runspace pool
        $runspacePool.Close()
    }
}
tools\dbatools\internal\functions\Update-SqlDbOwner.ps1
function Update-SqlDbOwner {
    <#
    .SYNOPSIS
        Internal function. Updates specified database dbowner.
#>
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [object]$source,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [object]$destination,
        [string]$dbname,
        [PSCredential]$SourceSqlCredential,
        [PSCredential]$DestinationSqlCredential
    )

    $sourceserver = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
    try {
        if ($Destination -isnot [Microsoft.SqlServer.Management.Smo.SqlSmoObject]) {
            $destserver = Connect-SqlInstance -SqlInstance $Destination -SqlCredential $SqlCredential
        }
        else {
            $destserver = $Destination
        }
    }
    catch {
        Write-Message -Level Warning "Cannot connect to $SqlInstance"
        break
    }

    $source = $sourceserver.DomainInstanceName
    $destination = $destserver.DomainInstanceName

    if ($dbname.length -eq 0) {
        $databases = ($sourceserver.Databases | Where-Object { $destserver.databases.name -contains $_.name -and $_.IsSystemObject -eq $false }).Name
    }
    else { $databases = $dbname }

    foreach ($dbname in $databases) {
        $destdb = $destserver.databases[$dbname]
        $dbowner = $sourceserver.databases[$dbname].owner

        if ($destdb.owner -ne $dbowner) {
            if ($destdb.Status -ne 'Normal') { Write-Output "Database status not normal. Skipping dbowner update."; continue }

            if ($null -eq $dbowner -or $null -eq $destserver.logins[$dbowner]) {
                try {
                    $dbowner = ($destserver.logins | Where-Object { $_.id -eq 1 }).Name
                }
                catch {
                    $dbowner = "sa"
                }
            }

            try {
                if ($destdb.ReadOnly -eq $true) {
                    $changeroback = $true
                    Update-SqlDbReadOnly $destserver $dbname $false
                }

                $destdb.SetOwner($dbowner)
                Write-Output "Changed $dbname owner to $dbowner"

                if ($changeroback) {
                    Update-SqlDbReadOnly $destserver $dbname $true
                    $changeroback = $null
                }
            }
            catch {
                Write-Error "Failed to update $dbname owner to $dbowner."
            }
        }
        else { Write-Output "Proper owner already set on $dbname" }
    }
}
tools\dbatools\internal\functions\Update-SqlDbReadOnly.ps1
function Update-SqlDbReadOnly {
    <#
    .SYNOPSIS
        Internal function. Updates specified database to read-only or read-write. Necessary because SMO doesn't appear to support NO_WAIT.
#>
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [Alias("ServerInstance", "SqlServer")]
        [object]$SqlInstance,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$dbname,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [bool]$readonly
    )

    if ($readonly) {
        Stop-DbaProcess -SqlInstance $SqlInstance -Database $dbname
        $sql = "ALTER DATABASE [$dbname] SET READ_ONLY WITH NO_WAIT"
    }
    else {
        $sql = "ALTER DATABASE [$dbname] SET READ_WRITE WITH NO_WAIT"
    }

    try {
        $server = Connect-SqlInstance -SqlInstance $SqlInstance
        $null = $server.Query($sql)
        Write-Message -Level Verbose -Message "Changed ReadOnly status to $readonly for $dbname on $($server.name)"
        return $true
    }
    catch {
        Write-Message -Level Warning "Could not change readonly status for $dbname on $($server.name)"
        return $false
    }
}
tools\dbatools\internal\functions\Update-SqlPermissions.ps1
function Update-SqlPermissions {
    <#
        .SYNOPSIS
            Internal function. Updates permission sets, roles, database mappings on server and databases
        .PARAMETER SourceServer
            Source Server
        .PARAMETER SourceLogin
            Source login
        .PARAMETER DestServer
            Destination Server
        .PARAMETER DestLogin
            Destination Login
        .PARAMETER EnableException
            Use this switch to disable any kind of verbose messages
    #>
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "")]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [object]$SourceServer,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [object]$SourceLogin,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [object]$DestServer,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [object]$DestLogin,
        [Alias('Silent')]
        [switch]$EnableException
    )

    $destination = $DestServer.DomainInstanceName
    $source = $SourceServer.DomainInstanceName
    $userName = $SourceLogin.Name

    # Server Roles: sysadmin, bulklogin, etc
    foreach ($role in $SourceServer.Roles) {
        $roleName = $role.Name
        $destRole = $DestServer.Roles[$roleName]

        if ($null -ne $destRole) {
            try {
                $destRoleMembers = $destRole.EnumMemberNames()
            }
            catch {
                $destRoleMembers = $destRole.EnumServerRoleMembers()
            }
        }

        try {
            $roleMembers = $role.EnumMemberNames()
        }
        catch {
            $roleMembers = $role.EnumServerRoleMembers()
        }

        if ($roleMembers -contains $userName) {
            if ($null -ne $destRole) {
                if ($Pscmdlet.ShouldProcess($destination, "Adding $userName to $roleName server role.")) {
                    try {
                        $destRole.AddMember($userName)
                        Write-Message -Level Verbose -Message "Adding $userName to $roleName server role on $destination successfully performed."
                    }
                    catch {
                        Stop-Function -Message "Failed to add $userName to $roleName server role on $destination." -Target $role -ErrorRecord $_
                    }
                }
            }
        }

        # Remove for Syncs
        if ($roleMembers -notcontains $userName -and $destRoleMembers -contains $userName -and $null -ne $destRole) {
            if ($Pscmdlet.ShouldProcess($destination, "Adding $userName to $roleName server role.")) {
                try {
                    $destRole.DropMember($userName)
                    Write-Message -Level Verbose -Message "Removing $userName from $destRoleName server role on $destination successfully performed."
                }
                catch {
                    Stop-Function -Message "Failed to remove $userName from $destRoleName server role on $destination." -Target $role -ErrorRecord $_
                }
            }
        }
    }

    $ownedJobs = $SourceServer.JobServer.Jobs | Where-Object OwnerLoginName -eq $userName
    foreach ($ownedJob in $ownedJobs) {
        if ($null -ne $DestServer.JobServer.Jobs[$ownedJob.Name]) {
            if ($Pscmdlet.ShouldProcess($destination, "Changing of job owner to $userName for $($ownedJob.Name).")) {
                try {
                    $destOwnedJob = $DestServer.JobServer.Jobs | Where-Object { $_.Name -eq $ownedJobs.Name }
                    $destOwnedJob.Set_OwnerLoginName($userName)
                    $destOwnedJob.Alter()
                    Write-Message -Level Verbose -Message "Changing job owner to $userName for $($ownedJob.Name) on $destination successfully performed."
                }
                catch {
                    Stop-Function -Message "Failed to change job owner for $($ownedJob.Name) on $destination." -Target $ownedJob -ErrorRecord $_
                }
            }
        }
    }

    if ($SourceServer.VersionMajor -ge 9 -and $DestServer.VersionMajor -ge 9) {
        <#
            These operations are only supported by SQL Server 2005 and above.
            Securables: Connect SQL, View any database, Administer Bulk Operations, etc.
        #>

        $perms = $SourceServer.EnumServerPermissions($userName)
        foreach ($perm in $perms) {
            $permState = $perm.PermissionState
            if ($permState -eq "GrantWithGrant") {
                $grantWithGrant = $true;
                $permState = "grant"
            }
            else {
                $grantWithGrant = $false
            }

            $permSet = New-Object Microsoft.SqlServer.Management.Smo.ServerPermissionSet($perm.PermissionType)
            if ($Pscmdlet.ShouldProcess($destination, "$permState on $($perm.PermissionType) for $userName.")) {
                try {
                    $DestServer.PSObject.Methods[$permState].Invoke($permSet, $userName, $grantWithGrant)
                    Write-Message -Level Verbose -Message "$permState $($perm.PermissionType) to $userName on $destination successfully performed."
                }
                catch {
                    Stop-Function -Message "Failed to $permState $($perm.PermissionType) to $userName on $destination." -Target $perm -ErrorRecord $_
                }
            }

            # for Syncs
            $destPerms = $DestServer.EnumServerPermissions($userName)
            foreach ($perm in $destPerms) {
                $permState = $perm.PermissionState
                $sourcePerm = $perms | Where-Object { $_.PermissionType -eq $perm.PermissionType -and $_.PermissionState -eq $permState }

                if ($null -eq $sourcePerm) {
                    if ($Pscmdlet.ShouldProcess($destination, "Revoking $($perm.PermissionType) for $userName.")) {
                        try {
                            $permSet = New-Object Microsoft.SqlServer.Management.Smo.ServerPermissionSet($perm.PermissionType)

                            if ($permState -eq "GrantWithGrant") {
                                $grantWithGrant = $true;
                                $permState = "grant"
                            }
                            else {
                                $grantWithGrant = $false
                            }

                            $DestServer.PSObject.Methods["Revoke"].Invoke($permSet, $userName, $false, $grantWithGrant)
                            Write-Message -Level Verbose -Message "Revoking $($perm.PermissionType) for $userName on $destination successfully performed."
                        }
                        catch {
                            Stop-Function -Message "Failed to revoke $($perm.PermissionType) from $userName on $destination." -Target $perm -ErrorRecord $_
                        }
                    }
                }
            }
        }

        # Credential mapping. Credential removal not currently supported for Syncs.
        $loginCredentials = $SourceServer.Credentials | Where-Object { $_.Identity -eq $SourceLogin.Name }
        foreach ($credential in $loginCredentials) {
            if ($null -eq $DestServer.Credentials[$credential.Name]) {
                if ($Pscmdlet.ShouldProcess($destination, "Creating credential $($credential.Name) for $userName.")) {
                    try {
                        $newCred = New-Object Microsoft.SqlServer.Management.Smo.Credential($DestServer, $credential.Name)
                        $newCred.Identity = $SourceLogin.Name
                        $newCred.Create()
                        Write-Message -Level Verbose -Message "Creating credential $($credential.Name) for $userName on $destination successfully performed."
                    }
                    catch {
                        Stop-Function -Message "Failed to create credential $($credential.Name) for $userName on $destination." -Target $credential -ErrorRecord $_
                    }
                }
            }
        }
    }

    if ($DestServer.VersionMajor -lt 9) {
        Write-Message -Level Warning -Message "SQL Server 2005 or greater required for database mappings.";
        continue
    }

    # For Sync, if info doesn't exist in EnumDatabaseMappings, then no big deal.
    foreach ($db in $DestLogin.EnumDatabaseMappings()) {
        $dbName = $db.DbName
        $destDb = $DestServer.Databases[$dbName]
        $sourceDb = $SourceServer.Databases[$dbName]
        $dbUsername = $db.Username;
        $dbLogin = $db.LoginName

        if ($null -ne $sourceDb) {
            if (!$sourceDb.IsAccessible) {
                Write-Message -Level Verbose -Message "Database [$($sourceDb.Name)] is not accessible on $source. Skipping."
                continue
            }
            if ($null -eq $sourceDb.Users[$dbUsername] -and $null -eq $destDb.Users[$dbUsername]) {
                if ($Pscmdlet.ShouldProcess($destination, "Dropping user $dbUsername from $dbName.")) {
                    try {
                        $destDb.Users[$dbUsername].Drop()
                        Write-Message -Level Verbose -Message "Dropping user $dbUsername (login: $dbLogin) from $dbName on destination successfully performed."
                        Write-Message -Level Verbose -Message "Any schema in $dbaName owned by $dbUsername may still exist."
                    }
                    catch {
                        Stop-Function -Message "Failed to drop $dbUsername (login: $dbLogin) from $dbName on destination." -Target $db -ErrorRecord $_
                    }
                }
            }

            # Remove user from role. Role removal not currently supported for Syncs.
            # TODO: reassign if dbo, application roles
            foreach ($destRole in $destDb.Roles) {
                $destRoleName = $destRole.Name
                $sourceRole = $sourceDb.Roles[$destRoleName]
                if ($null -eq $sourceRole) {
                    if ($sourceRole.EnumMembers() -notcontains $dbUsername -and $destRole.EnumMembers() -contains $dbUsername) {
                        if ($dbUsername -ne "dbo") {
                            if ($Pscmdlet.ShouldProcess($destination, "Dropping user $userName from $destRoleName database role in $dbName.")) {
                                try {
                                    $destRole.DropMember($dbUsername)
                                    $destDb.Alter()
                                    Write-Message -Level Verbose -Message "Dropping user $dbUsername (login: $dbLogin) from $destRoleName database role in $dbName on $destination successfully performed."
                                }
                                catch {
                                    Stop-Function -Message "Failed to remove $dbUsername (login: $dbLogin) from $destRoleName database role in $dbName on $destination." -Target $destRole -ErrorRecord $_
                                }
                            }
                        }
                    }
                }
            }

            # Remove Connect, Alter Any Assembly, etc
            $destPerms = $destDb.EnumDatabasePermissions($userName)
            $perms = $sourceDb.EnumDatabasePermissions($userName)
            # for Syncs
            foreach ($perm in $destPerms) {
                $permState = $perm.PermissionState
                $sourcePerm = $perms | Where-Object { $_.PermissionType -eq $perm.PermissionType -and $_.PermissionState -eq $permState }
                if ($null -eq $sourcePerm) {
                    if ($Pscmdlet.ShouldProcess($destination, "Revoking $($perm.PermissionType) from $userName in $dbName.")) {
                        try {
                            $permSet = New-Object Microsoft.SqlServer.Management.Smo.DatabasePermissionSet($perm.PermissionType)

                            if ($permState -eq "GrantWithGrant") {
                                $grantWithGrant = $true;
                                $permState = "grant"
                            }
                            else {
                                $grantWithGrant = $false
                            }

                            $destDb.PSObject.Methods["Revoke"].Invoke($permSet, $userName, $false, $grantWithGrant)
                            Write-Message -Level Verbose -Message "Revoking $($perm.PermissionType) from $userName in $dbName on $destination successfully performed."
                        }
                        catch {
                            Stop-Function -Message "Failed to revoke $($perm.PermissionType) from $userName in $dbName on $destination." -Target $perm -ErrorRecord $_
                        }
                    }
                }
            }
        }
    }

    # Adding database mappings and securables
    foreach ($db in $SourceLogin.EnumDatabaseMappings()) {
        $dbName = $db.DbName
        $destDb = $DestServer.Databases[$dbName]
        $sourceDb = $SourceServer.Databases[$dbName]
        $dbUsername = $db.Username;
        $dbLogin = $db.LoginName

        if ($null -ne $destDb) {
            if (!$destDb.IsAccessible) {
                Write-Message -Level Verbose -Message "Database [$dbName] is not accessible. Skipping."
                continue
            }
            if ($null -eq $destDb.Users[$dbUsername]) {
                if ($Pscmdlet.ShouldProcess($destination, "Adding $dbUsername to $dbName.")) {
                    $sql = $SourceServer.Databases[$dbName].Users[$dbUsername].Script() | Out-String
                    try {
                        $destDb.ExecuteNonQuery($sql)
                        Write-Message -Level Verbose -Message "Adding user $dbUsername (login: $dbLogin) to $dbName successfully performed."
                    }
                    catch {
                        Stop-Function -Message "Failed to add $dbUsername (login: $dbLogin) to $dbName on $destination." -Target $db -ErrorRecord $_
                    }
                }
            }

            # Db owner
            if ($sourceDb.Owner -eq $userName) {
                if ($Pscmdlet.ShouldProcess($destination, "Changing $dbName dbowner to $userName.")) {
                    try {
                        $result = Update-SqlDbOwner $SourceServer $DestServer -DbName $dbName
                        if ($result -eq $true) {
                            Write-Message -Level Verbose -Message "Changed $($destDb.Name) owner to $($sourceDb.owner)."
                        }
                        else {
                            Write-Message -Level Warning -Message "Failed to update $($destDb.Name) owner to $($sourceDb.owner)."
                        }
                    }
                    catch {
                        Write-Message -Level Warning -Message "Failed to update $($destDb.Name) owner to $($sourceDb.owner)."
                    }
                }
            }

            # Database Roles: db_owner, db_datareader, etc
            foreach ($role in $sourceDb.Roles) {
                if ($role.EnumMembers() -contains $userName) {
                    $roleName = $role.Name
                    $destDbRole = $destDb.Roles[$roleName]

                    if ($null -ne $destDbRole -and $dbUsername -ne "dbo" -and $destDbRole.EnumMembers() -notcontains $userName) {
                        if ($Pscmdlet.ShouldProcess($destination, "Adding $userName to $roleName database role in $dbName.")) {
                            try {
                                $destDbRole.AddMember($userName)
                                $destDb.Alter()
                                Write-Message -Level Verbose -Message "Adding $userName to $roleName database role in $dbName on $destination successfully performed."
                            }
                            catch {
                                Stop-Function -Message "Failed to add $userName to $roleName database role in $dbName on $destination." -Target $role -ErrorRecord $_
                            }
                        }
                    }
                }
            }

            # Connect, Alter Any Assembly, etc
            $perms = $sourceDb.EnumDatabasePermissions($userName)
            foreach ($perm in $perms) {
                $permState = $perm.PermissionState
                if ($permState -eq "GrantWithGrant") {
                    $grantWithGrant = $true;
                    $permState = "grant"
                }
                else {
                    $grantWithGrant = $false
                }
                $permSet = New-Object Microsoft.SqlServer.Management.Smo.DatabasePermissionSet($perm.PermissionType)

                if ($Pscmdlet.ShouldProcess($destination, "$permState on $($perm.PermissionType) for $userName on $dbName")) {
                    try {
                        $destDb.PSObject.Methods[$permState].Invoke($permSet, $userName, $grantWithGrant)
                        Write-Message -Level Verbose -Message "$permState on $($perm.PermissionType) to $userName on $dbName on $destination successfully performed."
                    }
                    catch {
                        Stop-Function -Message "Failed to perform $permState on $($perm.PermissionType) to $userName on $dbName on $destination." -Target $perm -ErrorRecord $_
                    }
                }
            }
        }
    }
}
tools\dbatools\internal\functions\Where-DbaObject.ps1
function global:Where-DbaObject {
    <#
        .SYNOPSIS
            A slightly more efficient filter function than Where-Object.

        .DESCRIPTION
            A slightly more efficient filter function than Where-Object.
            In case multiple filters are set, any one hit will work.

        .PARAMETER InputObject
            The object to process.

        .PARAMETER PropertyName
            Whether a property should be tested, rather than the input object itself.

        .PARAMETER Equals
            Tests for equality.

        .PARAMETER NotEquals
            Tests for inequality.

        .PARAMETER Like
            Tests for similarity.

        .PARAMETER NotLike
            Tests for non-similarity.

        .PARAMETER In
            Tests, whether the input is contained in a specified list.

        .PARAMETER NotIn
            Tests, whether the input is not contained in a specified list.

        .PARAMETER Match
            Tests for regex match.

        .PARAMETER NotMatch
            Tests for regex non-match.

        .EXAMPLE
            dir | Where-DbaObject Length -gt 1024

            Scans the current folder and filters out all files smaller then 1024 bytes

        .EXAMPLE
            "foo","bar" | Where-DbaObject -match "o"

            Filters out all strings that don't contain the letter "o"
    #>
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true, Mandatory = $true)]
        [object]
        $InputObject,

        [Parameter(Position = 0)]
        [Alias('Property')]
        [string]
        $PropertyName,

        [Alias('Eq')]
        [object]
        $Equals,

        [Alias('Ne')]
        [object]
        $NotEquals,

        [object]
        $Like,

        [object]
        $NotLike,

        [object]
        $In,

        [object]
        $NotIn,

        [object]
        $Match,

        [object]
        $NotMatch
    )

    begin {
        $TestEquals = Test-Bound -ParameterName Equals
        $TestNotEquals = Test-Bound -ParameterName NotEquals
        $TestLike = Test-Bound -ParameterName Like
        $TestNotLike = Test-Bound -ParameterName NotLike
        $TestIn = Test-Bound -ParameterName In
        $TestNotIn = Test-Bound -ParameterName NotIn
        $TestMatch = Test-Bound -ParameterName Match
        $TestNotMatch = Test-Bound -ParameterName NotMatch

        $TestObject = -not ($TestEquals -or $TestNotEquals -or $TestLike -or $TestNotLike -or $TestIn -or $TestNotIn -or $TestMatch -or $TestNotMatch)

        $TestProperty = Test-Bound -ParameterName PropertyName
    }
    process {
        foreach ($item in $InputObject) {
            #region Test Property
            if ($TestProperty) {
                if ($TestObject -and $item.$PropertyName) { return $item }

                if ($TestEquals -and ($item.$PropertyName -eq $Equals)) { return $item }
                if ($TestNotEquals -and ($item.$PropertyName -ne $NotEquals)) { return $item }
                if ($TestLike -and ($item.$PropertyName -like $Like)) { return $item }
                if ($TestNotLike -and ($item.$PropertyName -notlike $NotLike)) { return $item }
                if ($TestIn -and ($item.$PropertyName -In $In)) { return $item }
                if ($TestNotIn -and ($item.$PropertyName -NotIn $NotIn)) { return $item }
                if ($TestMatch -and ($item.$PropertyName -Match $Match)) { return $item }
                if ($TestNotMatch -and ($item.$PropertyName -NotMatch $NotMatch)) { return $item }
            }
            #endregion Test Property
            #region Test Object
            else {
                if ($TestObject -and $item) { return $item }

                if ($TestEquals -and ($item -eq $Equals)) { return $item }
                if ($TestNotEquals -and ($item -ne $NotEquals)) { return $item }
                if ($TestLike -and ($item -like $Like)) { return $item }
                if ($TestNotLike -and ($item -notlike $NotLike)) { return $item }
                if ($TestIn -and ($item -In $In)) { return $item }
                if ($TestNotIn -and ($item -NotIn $NotIn)) { return $item }
                if ($TestMatch -and ($item -Match $Match)) { return $item }
                if ($TestNotMatch -and ($item -NotMatch $NotMatch)) { return $item }
            }
            #endregion Test Object
        }
    }
    end {

    }
}

(Get-Item Function:\Where-DbaObject).Visibility = "Private"
tools\dbatools\internal\functions\Write-HostColor.ps1
function Write-HostColor {
    <#
    .SYNOPSIS
        Function that recognizes html-style tags to insert color into printed text.

    .DESCRIPTION
        Function that recognizes html-style tags to insert color into printed text.

        Color tags should be designed to look like this:
        <c="<console color>">Text</c>
        For example this would be a valid string:
        "This message should <c="red">partially be painted in red</c>!"

        This allows specifying color within strings and avoids having to piece together colored text in multiple calls to Write-Host.
        Only colors that are part of the ConsoleColor enumeration can be used. Bad colors will be ignored in favor of the default color.

    .PARAMETER String
        The message to write to host.

    .PARAMETER DefaultColor
        Default: (Get-DbaConfigValue -Name "message.infocolor")
        The color to write stuff to host in when no (or bad) color-code was specified.

    .EXAMPLE
        Write-HostColor -String 'This is going to be <c="red">bloody red</c> text! And this is <c="green">green stuff</c> for extra color'

        Will print the specified line in multiple colors

    .EXAMPLE
        $string1 = 'This is going to be <c="red">bloody red</c> text! And this is <c="green">green stuff</c> for extra color'
        $string2 = '<c="red">bloody red</c> text! And this is <c="green">green stuff</c> for extra color'
        $string3 = 'This is going to be <c="red">bloody red</c> text! And this is <c="green">green stuff</c>'
        $string1, $string2, $string3 | Write-HostColor -DefaultColor "Magenta"

        Will print all three lines, respecting the color-codes, but use the color "Magenta" as default color.

    .EXAMPLE
        $stringLong = @"
        Dear <c="red">Sirs</c><c="green"> and</c> <c="blue">Madams</c>,

        it has come to our attention that you are not sufficiently <c="darkblue">awesome!</c>
        Kindly improve your <c="yellow">AP</c> (<c="magenta">awesome-ness points</c>) by at least 50% to maintain you membership in Awesome Inc!

        You have <c="green">27 3/4</c> days time to meet this deadline. <c="darkyellow">After this we will unfortunately be forced to rend you assunder and sacrifice your remains to the devil</c>.

        Best regards,
        <c="red">Luzifer</c>
        "@
        Write-HostColor -String $stringLong

        Will print a long multiline text in its entirety while still respecting the colorcodes
#>
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingWriteHost", "")]
    [CmdletBinding()]
    Param (
        [Parameter(ValueFromPipeline = $true)]
        [string[]]
        $String,

        [ConsoleColor]
        $DefaultColor = (Get-DbaConfigValue -Name "message.infocolor")
    )
    process {
        foreach ($line in $String) {
            foreach ($row in $line.Split("`n").Split([environment]::NewLine)) {
                if ($row -notlike '*<c=["'']*["'']>*</c>*') { Write-Host -Object $row -ForegroundColor $DefaultColor }
                else {
                    $match = ($row | Select-String '<c=["''](.*?)["'']>(.*?)</c>' -AllMatches).Matches
                    $index = 0
                    $count = 0

                    while ($count -le $match.Count) {
                        if ($count -lt $Match.Count) {
                            Write-Host -Object $row.SubString($index, ($match[$count].Index - $Index)) -ForegroundColor $DefaultColor -NoNewline
                            try { Write-Host -Object $match[$count].Groups[2].Value -ForegroundColor $match[$count].Groups[1].Value -NoNewline -ErrorAction Stop }
                            catch { Write-Host -Object $match[$count].Groups[2].Value -ForegroundColor $DefaultColor -NoNewline -ErrorAction Stop }

                            $index = $match[$count].Index + $match[$count].Length
                            $count++
                        }
                        else {
                            Write-Host -Object $row.SubString($index) -ForegroundColor $DefaultColor
                            $count++
                        }
                    }
                }
            }
        }
    }
}
tools\dbatools\internal\maintenance\PSSession-Cleanup.ps1
$scriptBlock = {
    while ([Sqlcollaborative.Dbatools.Connection.ConnectionHost]::PSSessionCountExpired -gt 0) {
        $session = $null
        $session = [Sqlcollaborative.Dbatools.Connection.ConnectionHost]::PSSessionPurgeExpired()
        if ($null -ne $session) { $session | Remove-PSSession }
    }
}
Register-DbaMaintenanceTask -Name "pssession_cleanup" -ScriptBlock $scriptBlock -Delay (New-TimeSpan -Minutes 1) -Priority Low -Interval (New-TimeSpan -Minutes 1)

# Cleans up local references in the current runspace. All actual termination logic is handled by the task above
$script:pssession_cleanup_timer = New-Object System.Timers.TImer
$script:pssession_cleanup_timer.Interval = 60000
$null = Register-ObjectEvent -InputObject $script:pssession_cleanup_timer -EventName elapsed -SourceIdentifier dbatools_Timer -Action { Get-PSSession | Where-Object State -Like Closed | Remove-PSSession } -ErrorAction Ignore
$script:pssession_cleanup_timer.Start()
tools\dbatools\internal\maintenance\tempcleanup.ps1
$scriptBlock = {
    Get-ChildItem -Path $env:TEMP -Filter dbatools* | Remove-Item -ErrorAction Ignore -Recurse
}
Register-DbaMaintenanceTask -Name "tempcleanup" -ScriptBlock $scriptBlock -Once -Delay (New-TimeSpan -Minutes 1) -Priority Low
tools\dbatools\internal\maintenance\teppInsertTask.ps1
$scriptBlock = {
    $ModuleRoot = [Sqlcollaborative.Dbatools.dbaSystem.SystemHost]::ModuleBase

    $ExecutionContext.InvokeCommand.InvokeScript($false, ([scriptblock]::Create([io.file]::ReadAllText("$ModuleRoot\internal\functions\Register-DbaTeppScriptblock.ps1"))), $null, $null)
    $ExecutionContext.InvokeCommand.InvokeScript($false, ([scriptblock]::Create([io.file]::ReadAllText("$ModuleRoot\internal\functions\Register-DbaTeppInstanceCacheBuilder.ps1"))), $null, $null)

    foreach ($file in (Get-ChildItem "$ModuleRoot\internal\dynamicparams\*.ps1")) {
        $ExecutionContext.InvokeCommand.InvokeScript($false, ([scriptblock]::Create([io.file]::ReadAllText($file.FullName))), $null, $null)
    }

    [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::CalculateTabExpansion()
}
Register-DbaMaintenanceTask -Name "teppInsertTask" -ScriptBlock $scriptBlock -Once
tools\dbatools\internal\scripts\cmdlets.ps1
<#
Registers the cmdlets published by this module.
Necessary for full hybrid module support.
#>
$commonParam = @{
    HelpFile = "$($PSModuleRoot)\en-us\dbatools.dll-Help.xml"
    Module   = $ExecutionContext.SessionState.Module
}

Import-DbaCmdlet @commonParam -Name Write-Message -Type ([Sqlcollaborative.Dbatools.Commands.WriteMessageCommand])
tools\dbatools\internal\scripts\dbatools-maintenance.ps1
foreach ($item in (Get-ChildItem "$script:PSModuleRoot\internal\maintenance" -Filter *.ps1)) {
    if ($script:doDotSource) { . $item.FullName }
    else { $ExecutionContext.InvokeCommand.InvokeScript($false, ([scriptblock]::Create([io.file]::ReadAllText($item.FullName))), $null, $null) }
}

$scriptBlock = {
    $script:___ScriptName = 'dbatools-maintenance'

    # Import module in a way where internals are available
    $dbatools_disableTimeMeasurements = $true
    Import-Module "$([Sqlcollaborative.Dbatools.dbaSystem.SystemHost]::ModuleBase)\dbatools.psm1"

    try {
        #region Main Execution
        while ($true) {
            # This portion is critical to gracefully closing the script
            if ([Sqlcollaborative.Dbatools.Runspace.RunspaceHost]::Runspaces[$___ScriptName.ToLower()].State -notlike "Running") {
                break
            }

            $task = $null
            $tasksDone = @()
            while ($task = [Sqlcollaborative.Dbatools.Maintenance.MaintenanceHost]::GetNextTask($tasksDone)) {
                try { ([ScriptBlock]::Create($task.ScriptBlock.ToString())).Invoke() }
                catch { Write-Message -EnableException $false -Level Verbose -Message "[Maintenance] Task '$($task.Name)' failed to execute: $_" -ErrorRecord $_ -FunctionName "task:Maintenance" -Target $task }
                $task.LastExecution = Get-Date
                $tasksDone += $task.Name
            }

            Start-Sleep -Seconds 5
        }
        #endregion Main Execution
    }
    catch {  }
    finally {
        [Sqlcollaborative.Dbatools.Runspace.RunspaceHost]::Runspaces[$___ScriptName.ToLower()].SignalStopped()
    }
}

Register-DbaRunspace -ScriptBlock $scriptBlock -Name "dbatools-maintenance"
Start-DbaRunspace -Name "dbatools-maintenance"
tools\dbatools\internal\scripts\insertTepp.ps1
if (Get-Command TabExpansionPlusPlus\Register-ArgumentCompleter -ErrorAction Ignore) {
    $script:TEPP = $true
}
else {
    $script:TEPP = $false
}

$functions = Get-ChildItem function:\*-Dba*
[Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::DbatoolsCommands = $functions
$names = $functions.Name

#region Automatic TEPP by parameter name
Register-DbaTeppArgumentCompleter -Command $names -Parameter Alert -Name Alert -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter AlertCategory -Name AlertCategory -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter Audit -Name Audit -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter AuditSpecification -Name AuditSpecification -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter AvailabilityGroup -Name AvailabilityGroup -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter BackupDevice -Name BackupDevice -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter ConfigName -Name ConfigName -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter Credential -Name Credential -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter CredentialIdentity -Name Credential -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter CustomError -Name CustomError -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter Database -Name Database -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter Endpoint -Name Endpoint -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter ExcludeAlert -Name Alert -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter ExcludeAlertCategory -Name AlertCategory -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter ExcludeAudit -Name Audit -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter ExcludeAuditSpecification -Name AuditSpecification -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter ExcludeAvailabilityGroup -Name AvailabilityGroup -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter ExcludeBackupDevice -Name BackupDevice -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter ExcludeConfigName -Name ConfigName -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter ExcludeCredential -Name Credential -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter ExcludeCredentialIdentity -Name Credential -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter ExcludeCustomError -Name CustomError -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter ExcludeDatabase -Name Database -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter ExcludeEndpoint -Name Endpoint -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter ExcludeGroup -Name Group -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter ExcludeJob -Name Job -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter ExcludeJobCategory -Name JobCategory -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter ExcludeLinkedServer -Name LinkedServer -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter ExcludeLogin -Name Login -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter ExcludeMailAccount -Name MailAccount -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter ExcludeMailProfile -Name MailProfile -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter ExcludeMailServer -Name MailServer -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter ExcludeOperator -Name Operator -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter ExcludeProxyAccount -Name ProxyAccount -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter ExcludeResourcePool -Name ResourcePool -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter ExcludeSchedule -Name Schedule -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter ExcludeServerTrigger -Name ServerTrigger -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter ExcludeSession -Name Session -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter ExcludeSnapshot -Name Snapshot -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter Group -Name Group -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter Job -Name Job -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter JobCategory -Name JobCategory -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter LinkedServer -Name LinkedServer -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter Login -Name Login -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter MailAccount -Name MailAccount -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter MailProfile -Name MailProfile -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter MailServer -Name MailServer -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter Operator -Name Operator -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter ProxyAccount -Name ProxyAccount -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter ResourcePool -Name ResourcePool -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter Schedule -Name Schedule -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter ServerTrigger -Name ServerTrigger -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter Session -Name Session -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter Snapshot -Name Snapshot -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter SqlInstance -Name SqlInstance -All
Register-DbaTeppArgumentCompleter -Command $names -Parameter InstanceProperty -Name InstanceProperty -All
#endregion Automatic TEPP by parameter name

#region Explicit TEPP
Register-DbaTeppArgumentCompleter -Command "Find-DbaCommand" -Parameter Tag -Name tag
Register-DbaTeppArgumentCompleter -Command "Get-DbaConfig", "Get-DbaConfigValue", "Register-DbaConfig", "Set-DbaConfig" -Parameter FullName -Name config
Register-DbaTeppArgumentCompleter -Command "Get-DbaConfig", "Register-DbaConfig", "Set-DbaConfig" -Parameter Module -Name configmodule
Register-DbaTeppArgumentCompleter -Command "Get-DbaConfig", "Register-DbaConfig", "Set-DbaConfig" -Parameter Name -Name config_name
Register-DbaTeppArgumentCompleter -Command "Get-DbaProcess", "Stop-DbaProcess" -Parameter ExcludeSpid -Name processSpid
Register-DbaTeppArgumentCompleter -Command "Get-DbaProcess", "Stop-DbaProcess" -Parameter Hostname -Name processHostname
Register-DbaTeppArgumentCompleter -Command "Get-DbaProcess", "Stop-DbaProcess" -Parameter Program -Name processProgram
Register-DbaTeppArgumentCompleter -Command "Get-DbaProcess", "Stop-DbaProcess" -Parameter Spid -Name processSpid
Register-DbaTeppArgumentCompleter -Command "Import-DbaXESessionTemplate", "Get-DbaXESessionTemplate", "Export-DbaXESessionTemplate" -Parameter Template -Name xesessiontemplate
Register-DbaTeppArgumentCompleter -Command "Import-DbaPfDataCollectorSetTemplate", "Get-DbaPfDataCollectorSetTemplate", "Export-DbaPfDataCollectorSetTemplate"  -Parameter Template -Name perfmontemplate
#endregion Explicit TEPP
tools\dbatools\internal\scripts\logfilescript.ps1
$scriptBlock = {
    $script:___ScriptName = 'dbatools-logging'

    #region Helper Functions
    function Clean-ErrorXml {
        [CmdletBinding()]
        Param (
            $Path
        )

        $totalLength = $Null
        $files = Get-ChildItem -Path $Path.FullName -Filter "dbatools_$($pid)_error_*.xml" | Sort-Object LastWriteTime
        $totalLength = $files | Measure-Object Length -Sum | Select-Object -ExpandProperty Sum
        if (([Sqlcollaborative.Dbatools.Message.LogHost]::MaxErrorFileBytes) -gt $totalLength) { return }

        $removed = 0
        foreach ($file in $files) {
            $removed += $file.Length
            Remove-Item -Path $file.FullName -Force -Confirm:$false

            if (($totalLength - $removed) -lt ([Sqlcollaborative.Dbatools.Message.LogHost]::MaxErrorFileBytes)) { break }
        }
    }

    function Clean-MessageLog {
        [CmdletBinding()]
        Param (
            $Path
        )

        if ([Sqlcollaborative.Dbatools.Message.LogHost]::MaxMessagefileCount -eq 0) { return }

        $files = Get-ChildItem -Path $Path.FullName -Filter "dbatools_$($pid)_message_*.log" | Sort-Object LastWriteTime
        if (([Sqlcollaborative.Dbatools.Message.LogHost]::MaxMessagefileCount) -ge $files.Count) { return }

        $removed = 0
        foreach ($file in $files) {
            $removed++
            Remove-Item -Path $file.FullName -Force -Confirm:$false

            if (($files.Count - $removed) -le ([Sqlcollaborative.Dbatools.Message.LogHost]::MaxMessagefileCount)) { break }
        }
    }

    function Clean-GlobalLog {
        [CmdletBinding()]
        Param (
            $Path
        )

        # Kill too old files
        Get-ChildItem -Path "$($Path.FullName)\*" -Include "*.xml", "*.log" -Filter "*" | Where-Object LastWriteTime -LT ((Get-Date) - ([Sqlcollaborative.Dbatools.Message.LogHost]::MaxLogFileAge)) |Remove-Item -Force -Confirm:$false

        # Handle the global overcrowding
        $files = Get-ChildItem -Path "$($Path.FullName)\*" -Include "*.xml", "*.log" -Filter "*" | Sort-Object LastWriteTime
        if (-not ($files)) { return }
        $totalLength = $files | Measure-Object Length -Sum | Select-Object -ExpandProperty Sum

        if (([Sqlcollaborative.Dbatools.Message.LogHost]::MaxTotalFolderSize) -gt $totalLength) { return }

        $removed = 0
        foreach ($file in $files) {
            $removed += $file.Length
            Remove-Item -Path $file.FullName -Force -Confirm:$false

            if (($totalLength - $removed) -lt ([Sqlcollaborative.Dbatools.Message.LogHost]::MaxTotalFolderSize)) { break }
        }
    }
    #endregion Helper Functions

    try {
        while ($true) {
            # This portion is critical to gracefully closing the script
            if ([Sqlcollaborative.Dbatools.Runspace.RunspaceHost]::Runspaces[$___ScriptName.ToLower()].State -notlike "Running") {
                break
            }

            $path = [Sqlcollaborative.Dbatools.Message.LogHost]::LoggingPath
            if (-not (Test-Path $path)) {
                $root = New-Item $path -ItemType Directory -Force -ErrorAction Stop
            }
            else { $root = Get-Item -Path $path }

            try { [int]$num_Error = (Get-ChildItem -Path $root.FullName -Filter "dbatools_$($pid)_error_*.xml" | Sort-Object LastWriteTime -Descending | Select-Object -First 1 -ExpandProperty Name | Select-String -Pattern "(\d+)" -AllMatches).Matches[1].Value }
            catch { }
            try { [int]$num_Message = (Get-ChildItem -Path $root.FullName -Filter "dbatools_$($pid)_message_*.log" | Sort-Object LastWriteTime -Descending | Select-Object -First 1 -ExpandProperty Name | Select-String -Pattern "(\d+)" -AllMatches).Matches[1].Value }
            catch { }
            if (-not ($num_Error)) { $num_Error = 0 }
            if (-not ($num_Message)) { $num_Message = 0 }

            #region Process Errors
            while ([Sqlcollaborative.Dbatools.Message.LogHost]::OutQueueError.Count -gt 0) {
                $num_Error++

                $Record = $null
                [Sqlcollaborative.Dbatools.Message.LogHost]::OutQueueError.TryDequeue([ref]$Record)

                if ($Record) {
                    $Record | Export-Clixml -Path "$($root.FullName)\dbatools_$($pid)_error_$($num_Error).xml" -Depth 3
                }

                Clean-ErrorXml -Path $root
            }
            #endregion Process Errors

            #region Process Logs
            while ([Sqlcollaborative.Dbatools.Message.LogHost]::OutQueueLog.Count -gt 0) {
                $CurrentFile = "$($root.FullName)\dbatools_$($pid)_message_$($num_Message).log"
                if (Test-Path $CurrentFile) {
                    $item = Get-Item $CurrentFile
                    if ($item.Length -gt ([Sqlcollaborative.Dbatools.Message.LogHost]::MaxMessagefileBytes)) {
                        $num_Message++
                        $CurrentFile = "$($root.FullName)\dbatools_$($pid)_message_$($num_Message).log"
                    }
                }

                $Entry = $null
                [Sqlcollaborative.Dbatools.Message.LogHost]::OutQueueLog.TryDequeue([ref]$Entry)
                if ($Entry) {
                    Add-Content -Path $CurrentFile -Value (ConvertTo-Csv -InputObject $Entry -NoTypeInformation)[1]
                }
            }
            #endregion Process Logs

            Clean-MessageLog -Path $root
            Clean-GlobalLog -Path $root

            Start-Sleep -Seconds 5
        }
    }
    catch { }
    finally {
        [Sqlcollaborative.Dbatools.Runspace.RunspaceHost]::Runspaces[$___ScriptName.ToLower()].SignalStopped()
    }
}

Register-DbaRunspace -ScriptBlock $scriptBlock -Name "dbatools-logging"
Start-DbaRunspace -Name "dbatools-logging"
tools\dbatools\internal\scripts\message-transforms.ps1
Register-DbaMessageTransform -TargetType 'Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter' -ScriptBlock {
    $args[0].FullSmoName
}
Register-DbaMessageTransform -TargetType 'Microsoft.SqlServer.Management.Smo.Server' -ScriptBlock {
    ([Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter]$args[0]).FullSmoName
}

Register-DbaMessageTransform -ExceptionTypeFilter '*' -ScriptBlock {
    if ($args[0] -is [System.Data.SqlClient.SqlException]) { return $args[0] }
    
    $item = $args[0]
    while ($item.InnerException) {
        $item = $item.InnerException
        if ($item -is [System.Data.SqlClient.SqlException]) { return $item }
    }
    
    return $args[0]
}
tools\dbatools\internal\scripts\smoLibraryImport.ps1
$scriptBlock = {
    Param (
        $ModuleRoot,

        $DllRoot,

        $DoCopy
    )

    function Copy-Assembly {
        [CmdletBinding()]
        Param (
            [string]$ModuleRoot,
            [string]$DllRoot,
            [bool]$DoCopy,
            [string]$Name
        )

        if (-not $DoCopy) {
            return
        }
        if ("$ModuleRoot\bin\smo" -eq $DllRoot) {
            return
        }

        if (-not (Test-Path $DllRoot)) {
            $null = New-Item -Path $DllRoot -ItemType Directory -ErrorAction Ignore
        }

        Copy-Item -Path "$ModuleRoot\bin\smo\$Name.dll" -Destination $DllRoot
    }

    #region Names
    $names = @(
        'Microsoft.SqlServer.Smo',
        'Microsoft.SqlServer.Dmf',
        'Microsoft.SqlServer.SqlWmiManagement',
        'Microsoft.SqlServer.ConnectionInfo',
        'Microsoft.SqlServer.SmoExtended',
        'Microsoft.SqlServer.Management.RegisteredServers',
        'Microsoft.SqlServer.Management.Sdk.Sfc',
        'Microsoft.SqlServer.SqlEnum',
        'Microsoft.SqlServer.RegSvrEnum',
        'Microsoft.SqlServer.WmiEnum',
        'Microsoft.SqlServer.ServiceBrokerEnum',
        'Microsoft.SqlServer.Management.Collector',
        'Microsoft.SqlServer.Management.CollectorEnum',
        'Microsoft.SqlServer.Management.Utility',
        'Microsoft.SqlServer.Management.UtilityEnum',
        'Microsoft.SqlServer.Management.HadrDMF',
        'Microsoft.SqlServer.VulnerabilityAssessment.Model',

        'Microsoft.SqlServer.BatchParser',
        'Microsoft.SqlServer.BatchParserClient',
        'Microsoft.SqlServer.BulkInsertTaskConnections',
        'Microsoft.SqlServer.DTSRuntimeWrap',
        'Microsoft.SqlServer.DtsServer.Interop',
        'Microsoft.SqlServer.DTSUtilities',
        'Microsoft.SqlServer.ForEachFileEnumeratorWrap',
        'Microsoft.SqlServer.ManagedDTS',
        'Microsoft.SqlServer.IntegrationServices.ODataConnectionManager',
        'Microsoft.SqlServer.IntegrationServices.ODataSrc',
        'Microsoft.SqlServer.PipelineHost',
        'Microsoft.SqlServer.PackageFormatUpdate',
        'Microsoft.SqlServer.Replication',
        'Microsoft.SqlServer.SqlCEDest',
        'Microsoft.SqlServer.SQLTask',
        'Microsoft.SqlServer.TxScript',
        'Microsoft.SqlServer.XE.Core',
        'Microsoft.SqlServer.XEvent.Configuration',
        'Microsoft.SqlServer.XEvent',
        'Microsoft.SqlServer.XEvent.Linq',
        'Microsoft.SqlServer.XmlSrc',
        'Microsoft.SqlServer.Rmo',
        'Microsoft.SqlServer.DTSPipelineWrap',
        'Microsoft.SqlServer.ScriptTask',

        'Accessibility',
        'EnvDTE',
        'Microsoft.AnalysisServices.AppLocal.Core',
        'Microsoft.AnalysisServices.AppLocal',
        'Microsoft.Azure.KeyVault.Core',
        'Microsoft.Data.Edm',
        'Microsoft.Data.OData',
        'Microsoft.Practices.TransientFaultHandling.Core',
        'Microsoft.DataTransfer.Common.Utils',
        'Microsoft.SqlServer.ASTasks',
        'Microsoft.SqlServer.ConnectionInfoExtended',
        'Microsoft.SqlServer.DataProfiler',
        'Microsoft.SqlServer.DataProfilingTask',
        'Microsoft.SqlServer.Diagnostics.STrace',
        'Microsoft.SqlServer.Dmf.Common',

        'Microsoft.SqlServer.DMQueryTask',
        'Microsoft.SqlServer.DTEnum',
        'Microsoft.SqlServer.Dts.Design',
        'Microsoft.SqlServer.Dts.DtsClient',
        'Microsoft.SqlServer.DtsMsg',
        'Microsoft.SqlServer.Edition',
        'Microsoft.SqlServer.ExecProcTask',
        'Microsoft.SqlServer.ExpressionTask',
        'Microsoft.SqlServer.FileSystemTask',
        'Microsoft.SqlServer.ForEachADOEnumerator',
        'Microsoft.SqlServer.ForEachFromVarEnumerator',
        'Microsoft.SqlServer.ForEachNodeListEnumerator',
        'Microsoft.SqlServer.ForEachSMOEnumerator',
        'Microsoft.SqlServer.FtpTask',
        'Microsoft.SqlServer.GridControl',
        'Microsoft.SqlServer.Instapi',
        'Microsoft.SqlServer.IntegrationServices.ClusterManagement',
        'Microsoft.SqlServer.IntegrationServices.Common.ObjectModel',
        'Microsoft.SqlServer.IntegrationServices.ISServerDBUpgrade',
        'Microsoft.SqlServer.IntegrationServices.Server.Common',
        'Microsoft.SqlServer.IntegrationServices.Server',
        'Microsoft.SqlServer.IntegrationServices.Server.IPC',
        'Microsoft.SqlServer.IntegrationServices.server.shared',
        'Microsoft.SqlServer.IntegrationServices.TaskScheduler',
        'Microsoft.SqlServer.ManagedConnections',
        'Microsoft.SqlServer.Management.CollectorTasks',
        'Microsoft.SqlServer.Management.HelpViewer',
        'Microsoft.SqlServer.Management.IntegrationServices',
        'Microsoft.SqlServer.Management.IntegrationServicesEnum',
        'Microsoft.SqlServer.Management.Sdk.Scripting',
        'Microsoft.SqlServer.Management.Sdk.SqlStudio',
        'Microsoft.SqlServer.Management.SmartAdminPolicies',
        'Microsoft.SqlServer.Management.SqlParser',
        'Microsoft.SqlServer.Management.SystemMetadataProvider',
        'Microsoft.SqlServer.Management.XEvent',
        'Microsoft.SqlServer.Management.XEventDbScoped',
        'Microsoft.SqlServer.Management.XEventDbScopedEnum',
        'Microsoft.SqlServer.Management.XEventEnum',
        'Microsoft.SqlServer.MSMQTask',
        'Microsoft.SqlServer.PipelineXML',
        'Microsoft.SqlServer.PolicyEnum',
        'Microsoft.SqlServer.Replication.BusinessLogicSupport',
        'Microsoft.SqlServer.SendMailTask',
        'Microsoft.SqlServer.SqlClrProvider',
        'Microsoft.SqlServer.SQLTaskConnectionsWrap',
        'Microsoft.SqlServer.SqlTDiagm',
        'Microsoft.SqlServer.SString',
        'Microsoft.SqlServer.TransferDatabasesTask',
        'Microsoft.SqlServer.TransferErrorMessagesTask',
        'Microsoft.SqlServer.TransferJobsTask',
        'Microsoft.SqlServer.TransferLoginsTask',
        'Microsoft.SqlServer.TransferObjectsTask',
        'Microsoft.SqlServer.TransferSqlServerObjectsTask',
        'Microsoft.SqlServer.TransferStoredProceduresTask',
        'Microsoft.SqlServer.Types',
        'Microsoft.SqlServer.Types.resources',
        'Microsoft.SqlServer.VSTAScriptingLib',
        'Microsoft.SqlServer.WebServiceTask',
        'Microsoft.SqlServer.WMIDRTask',
        'Microsoft.SqlServer.WMIEWTask',
        'Microsoft.SqlServer.XMLTask',

        'Microsoft.SqlServer.Dmf.Adapters',
        'Microsoft.SqlServer.DmfSqlClrWrapper'
    )
    #endregion Names

    foreach ($name in $names) {
        Copy-Assembly -ModuleRoot $ModuleRoot -DllRoot $DllRoot -DoCopy $DoCopy -Name $name
    }
    foreach ($name in $names) {
        Add-Type -Path "$DllRoot\$name.dll"
    }

    <#
Likely don't need yet
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.WizardFramework.dll"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.WizardFrameworkLite.dll"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.IntegrationServices.WorkerAgent.dll"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.IntegrationServices.SqlTaskScheduler.dll"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.CustomControls.dll"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.NetEnterpriseServers.ExceptionMessageBox.dll"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.IntegrationServices.MasterService.dll"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.IntegrationServices.MasterServiceClient.dll"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.Practices.TransientFaultHandling.Core.dll"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.IntegrationServices.Scale.ResourceProvider.dll"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.IntegrationServices.Scale.ScaleoutContract.dll"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.IntegrationServices.ScaleOut.Telemetry.dll"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.IntegrationServices.ScaleOut.Utilities.dll"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.IntegrationService.Hadoop.Common.dll"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.IntegrationService.HadoopComponents.dll"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.IntegrationService.HadoopConnections.dll"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.IntegrationService.HadoopEnumerators.dll"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.IntegrationService.HadoopTasks.dll"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.ExceptionMessageBox.dll"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.SqlTools.Telemetry.Interop.dll"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.Ssdqs.Component.DataCorrection.dll"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.Ssdqs.Component.DataQualityConnectionManager.dll"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.WindowsAzure.Configuration.dll"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.WindowsAzure.Storage.dll"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.WindowsAzure.StorageClient.dll"

# Throws exceptions but likes to be added
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.Data.Services.Client.dll" -ErrorAction Stop
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.DataTransfer.Common.dll" -ErrorAction Stop
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.DataTransfer.DataContracts.dll" -ErrorAction Stop
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.AnalysisServices.AppLocal.Tabular.dll" -ErrorAction Stop
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.Management.SmoMetadataProvider.dll"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.Hadoop.Avro.dll"

# Can't load, won't load
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.Data.DataFeedClient.dll"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.DataTransfer.ClientLibrary.dll"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.ADONETDest.dll"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.ADONETSrc.dllv"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.BulkInsertTask.dll"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.DataReaderDest.dll"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.DataStreaming.Dest.dll"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.DTSPipelineWrap.dll"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.IntegrationServices.RuntimeTelemetry.dll"
Add-Type -Path "$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.MaintenancePlanTasks.dll"
#>
}

if ($script:serialImport) {
    $scriptBlock.Invoke($script:PSModuleRoot, "$script:DllRoot\smo", (-not $script:strictSecurityMode))
}
else {
    $script:smoRunspace = [System.Management.Automation.PowerShell]::Create()
    if ($script:smoRunspace.Runspace.Name) {
        try { $script:smoRunspace.Runspace.Name = "dbatools-import-smo" }
        catch { }
    }
    $script:smoRunspace.AddScript($scriptBlock).AddArgument($script:PSModuleRoot).AddArgument("$script:DllRoot\smo").AddArgument((-not $script:strictSecurityMode))
    $script:smoRunspace.BeginInvoke()
}
tools\dbatools\internal\scripts\updateTeppAsync.ps1
$scriptBlock = {
    $script:___ScriptName = 'dbatools-teppasynccache'

    #region Utility Functions
    function Get-PriorityServer {
        [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::InstanceAccess.Values | Where-Object -Property LastUpdate -LT (New-Object System.DateTime(1, 1, 1, 1, 1, 1))
    }

    function Get-ActionableServer {
        [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::InstanceAccess.Values | Where-Object -Property LastUpdate -LT ((Get-Date) - ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::TeppUpdateInterval)) | Where-Object -Property LastUpdate -GT ((Get-Date) - ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::TeppUpdateTimeout))
    }

    function Update-TeppCache {
        [CmdletBinding()]
        Param (
            [Parameter(ValueFromPipeline = $true)]
            $ServerAccess
        )

        begin {

        }
        Process {
            if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::TeppUdaterStopper) { break }

            foreach ($instance in $ServerAccess) {
                if ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::TeppUdaterStopper) { break }
                $server = New-Object Microsoft.SqlServer.Management.Smo.Server($instance.ConnectionObject)
                try {
                    $server.ConnectionContext.Connect()
                }
                catch {
                    continue
                }

                $FullSmoName = ([Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter]$server).FullSmoName.ToLower()

                foreach ($scriptBlock in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::TeppGatherScriptsFast)) {
                    # Workaround to avoid stupid issue with scriptblock from different runspace
                    [ScriptBlock]::Create($scriptBlock).Invoke()
                }

                foreach ($scriptBlock in ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::TeppGatherScriptsSlow)) {
                    # Workaround to avoid stupid issue with scriptblock from different runspace
                    [ScriptBlock]::Create($scriptBlock).Invoke()
                }

                $server.ConnectionContext.Disconnect()

                $instance.LastUpdate = Get-Date
            }
        }
        end {

        }
    }
    #endregion Utility Functions

    try {
        #region Main Execution
        while ($true) {
            # This portion is critical to gracefully closing the script
            if ([Sqlcollaborative.Dbatools.Runspace.RunspaceHost]::Runspaces[$___ScriptName.ToLower()].State -notlike "Running") {
                break
            }

            Get-PriorityServer | Update-TeppCache

            Get-ActionableServer | Update-TeppCache

            Start-Sleep -Seconds 5
        }
        #endregion Main Execution
    }
    catch { }
    finally {
        [Sqlcollaborative.Dbatools.Runspace.RunspaceHost]::Runspaces[$___ScriptName.ToLower()].SignalStopped()
    }
}

Register-DbaRunspace -ScriptBlock $scriptBlock -Name "dbatools-teppasynccache"
if (-not ([Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::TeppAsyncDisabled -or [Sqlcollaborative.Dbatools.TabExpansion.TabExpansionHost]::TeppDisabled)) {
    Start-DbaRunspace -Name "dbatools-teppasynccache"
}
tools\dbatools\LICENSE
 
tools\dbatools\msbuild.log
 
tools\dbatools\optional\Compress-Archive.ps1
if ($PSVersionTable.PSVersion.Major -lt 5) {

<#
Copied from the Microsoft Module: Microsoft.PowerShell.Archive
Which ships with PowerShell Version 5 but will run under v3.
#>



    function Compress-Archive
    {
        <#
            .SYNOPSIS
                Creates an archive, or zipped file, from specified files and folders.

            .DESCRIPTION
                The Compress-Archive cmdlet creates a zipped (or compressed) archive file from one or more specified files or folders. An archive file allows multiple files to be packaged, and optionally compressed, into a single zipped file for easier distribution and storage. An archive file can be compressed by using the compression algorithm specified by the CompressionLevel parameter.

                Because Compress-Archive relies upon the Microsoft .NET Framework API System.IO.Compression.ZipArchive to compress files, the maximum file size that you can compress by using Compress-Archive is currently 2 GB. This is a limitation of the underlying API.

            .PARAMETER Path
                Specifies the path or paths to the files that you want to add to the archive zipped file. This parameter can accept wildcard characters. Wildcard characters allow you to add all files in a folder to your zipped archive file. To specify multiple paths, and include files in multiple locations in your output zipped file, use commas to separate the paths.

            .PARAMETER LiteralPath
                Specifies the path or paths to the files that you want to add to the archive zipped file. Unlike the Path parameter, the value of LiteralPath is used exactly as it is typed. No characters are interpreted as wildcards. If the path includes escape characters, enclose each escape character in single quotation marks, to instruct Windows PowerShell not to interpret any characters as escape sequences. To specify multiple paths, and include files in multiple locations in your output zipped file, use commas to separate the paths.

            .PARAMETER DestinationPath
                Specifies the path to the archive output file. This parameter is required. The specified DestinationPath value should include the desired name of the output zipped file; it specifies either the absolute or relative path to the zipped file. If the file name specified in DestinationPath does not have a .zip file name extension, the cmdlet adds a .zip file name extension.

            .PARAMETER CompressionLevel
                Specifies how much compression to apply when you are creating the archive file. Faster compression requires less time to create the file, but can result in larger file sizes. The acceptable values for this parameter are:

                - Fastest. Use the fastest compression method available to decrease processing time; this can result in larger file sizes.
                - NoCompression. Do not compress the source files.
                - Optimal. Processing time is dependent on file size.

                If this parameter is not specified, the command uses the default value, Optimal.

            .PARAMETER Update
                Updates the specified archive by replacing older versions of files in the archive with newer versions of files that have the same names. You can also add this parameter to add files to an existing archive.

            .PARAMETER Force
                @{Text=}

            .PARAMETER Confirm
                Prompts you for confirmation before running the cmdlet.

            .PARAMETER WhatIf
                Shows what would happen if the cmdlet runs. The cmdlet is not run.

            .EXAMPLE
                Example 1: Create an archive file

                PS C:\>Compress-Archive -LiteralPath C:\Reference\Draftdoc.docx, C:\Reference\Images\diagram2.vsd -CompressionLevel Optimal -DestinationPath C:\Archives\Draft.Zip

                This command creates a new archive file, Draft.zip, by compressing two files, Draftdoc.docx and diagram2.vsd, specified by the LiteralPath parameter. The compression level specified for this operation is Optimal.

            .EXAMPLE
                Example 2: Create an archive with wildcard characters

                PS C:\>Compress-Archive -Path C:\Reference\* -CompressionLevel Fastest -DestinationPath C:\Archives\Draft

                This command creates a new archive file, Draft.zip, in the C:\Archives folder. Note that though the file name extension .zip was not added to the value of the DestinationPath parameter, Windows PowerShell appends this to the specified archive file name automatically. The new archive file contains every file in the C:\Reference folder, because a wildcard character was used in place of specific file names in the Path parameter. The specified compression level is Fastest, which might result in a larger output file, but compresses a large number of files faster.

            .EXAMPLE
                Example 3: Update an existing archive file

                PS C:\>Compress-Archive -Path C:\Reference\* -Update -DestinationPath C:\Archives\Draft.Zip

                This command updates an existing archive file, Draft.Zip, in the C:\Archives folder. The command is run to update Draft.Zip with newer versions of existing files that came from the C:\Reference folder, and also to add new files that have been added to C:\Reference since Draft.Zip was initially created.

            .EXAMPLE
                Example 4: Create an archive from an entire folder

                PS C:\>Compress-Archive -Path C:\Reference -DestinationPath C:\Archives\Draft

                This command creates an archive from an entire folder, C:\Reference. Note that though the file name extension .zip was not added to the value of the DestinationPath parameter, Windows PowerShell appends this to the specified archive file name automatically.
        #>
        [CmdletBinding(DefaultParameterSetName = "Path", SupportsShouldProcess = $true, HelpUri = "http://go.microsoft.com/fwlink/?LinkID=393252")]
        param
        (
            [parameter (mandatory = $true, Position = 0, ParameterSetName = "Path", ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
            [parameter (mandatory = $true, Position = 0, ParameterSetName = "PathWithForce", ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
            [parameter (mandatory = $true, Position = 0, ParameterSetName = "PathWithUpdate", ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
            [ValidateNotNullOrEmpty()]
            [string[]]
            $Path,

            [parameter (mandatory = $true, ParameterSetName = "LiteralPath", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $true)]
            [parameter (mandatory = $true, ParameterSetName = "LiteralPathWithForce", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $true)]
            [parameter (mandatory = $true, ParameterSetName = "LiteralPathWithUpdate", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $true)]
            [ValidateNotNullOrEmpty()]
            [Alias("PSPath")]
            [string[]]
            $LiteralPath,

            [parameter (mandatory = $true,
                        Position = 1,
                        ValueFromPipeline = $false,
                        ValueFromPipelineByPropertyName = $false)]
            [ValidateNotNullOrEmpty()]
            [string]
            $DestinationPath,

            [parameter (
                        mandatory = $false,
                        ValueFromPipeline = $false,
                        ValueFromPipelineByPropertyName = $false)]
            [ValidateSet("Optimal", "NoCompression", "Fastest")]
            [string]
            $CompressionLevel = "Optimal",

            [parameter(mandatory = $true, ParameterSetName = "PathWithUpdate", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)]
            [parameter(mandatory = $true, ParameterSetName = "LiteralPathWithUpdate", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)]
            [switch]
            $Update = $false,

            [parameter(mandatory = $true, ParameterSetName = "PathWithForce", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)]
            [parameter(mandatory = $true, ParameterSetName = "LiteralPathWithForce", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)]
            [switch]
            $Force = $false
        )

        BEGIN
        {
            Add-Type -AssemblyName System.IO.Compression -ErrorAction Ignore
            Add-Type -AssemblyName System.IO.Compression.FileSystem -ErrorAction Ignore

            $zipFileExtension = ".zip"

            $LocalizedData = ConvertFrom-StringData @'
    PathNotFoundError=The path '{0}' either does not exist or is not a valid file system path.
    ExpandArchiveInValidDestinationPath=The path '{0}' is not a valid file system directory path.
    InvalidZipFileExtensionError={0} is not a supported archive file format. {1} is the only supported archive file format.
    ArchiveFileIsReadOnly=The attributes of the archive file {0} is set to 'ReadOnly' hence it cannot be updated. If you intend to update the existing archive file, remove the 'ReadOnly' attribute on the archive file else use -Force parameter to override and create a new archive file.
    ZipFileExistError=The archive file {0} already exists. Use the -Update parameter to update the existing archive file or use the -Force parameter to overwrite the existing archive file.
    DuplicatePathFoundError=The input to {0} parameter contains a duplicate path '{1}'. Provide a unique set of paths as input to {2} parameter.
    ArchiveFileIsEmpty=The archive file {0} is empty.
    CompressProgressBarText=The archive file '{0}' creation is in progress...
    ExpandProgressBarText=The archive file '{0}' expansion is in progress...
    AppendArchiveFileExtensionMessage=The archive file path '{0}' supplied to the DestinationPath patameter does not include .zip extension. Hence .zip is appended to the supplied DestinationPath path and the archive file would be created at '{1}'.
    AddItemtoArchiveFile=Adding '{0}'.
    CreateFileAtExpandedPath=Created '{0}'.
    InvalidArchiveFilePathError=The archive file path '{0}' specified as input to the {1} parameter is resolving to multiple file system paths. Provide a unique path to the {2} parameter where the archive file has to be created.
    InvalidExpandedDirPathError=The directory path '{0}' specified as input to the DestinationPath parameter is resolving to multiple file system paths. Provide a unique path to the Destination parameter where the archive file contents have to be expanded.
    FileExistsError=Failed to create file '{0}' while expanding the archive file '{1}' contents as the file '{2}' already exists. Use the -Force parameter if you want to overwrite the existing directory '{3}' contents when expanding the archive file.
    DeleteArchiveFile=The partially created archive file '{0}' is deleted as it is not usable.
    InvalidDestinationPath=The destination path '{0}' does not contain a valid archive file name.
    PreparingToCompressVerboseMessage=Preparing to compress...
    PreparingToExpandVerboseMessage=Preparing to expand...
'@

            #region Utility Functions
            function GetResolvedPathHelper
            {
                param
                (
                    [string[]]
                    $path,

                    [boolean]
                    $isLiteralPath,

                    [System.Management.Automation.PSCmdlet]
                    $callerPSCmdlet
                )

                $resolvedPaths = @()

                # null and empty check are are already done on Path parameter at the cmdlet layer.
                foreach ($currentPath in $path)
                {
                    try
                    {
                        if ($isLiteralPath)
                        {
                            $currentResolvedPaths = Resolve-Path -LiteralPath $currentPath -ErrorAction Stop
                        }
                        else
                        {
                            $currentResolvedPaths = Resolve-Path -Path $currentPath -ErrorAction Stop
                        }
                    }
                    catch
                    {
                        $errorMessage = ($LocalizedData.PathNotFoundError -f $currentPath)
                        $exception = New-Object System.InvalidOperationException $errorMessage, $_.Exception
                        $errorRecord = CreateErrorRecordHelper "ArchiveCmdletPathNotFound" $null ([System.Management.Automation.ErrorCategory]::InvalidArgument) $exception $currentPath
                        $callerPSCmdlet.ThrowTerminatingError($errorRecord)
                    }

                    foreach ($currentResolvedPath in $currentResolvedPaths)
                    {
                        $resolvedPaths += $currentResolvedPath.ProviderPath
                    }
                }

                $resolvedPaths
            }

            function Add-CompressionAssemblies
            {

                if ($PSEdition -eq "Desktop")
                {
                    Add-Type -AssemblyName System.IO.Compression
                    Add-Type -AssemblyName System.IO.Compression.FileSystem
                }
            }

            function IsValidFileSystemPath
            {
                param
                (
                    [string[]]
                    $path
                )

                $result = $true;

                # null and empty check are are already done on Path parameter at the cmdlet layer.
                foreach ($currentPath in $path)
                {
                    if (!([System.IO.File]::Exists($currentPath) -or [System.IO.Directory]::Exists($currentPath)))
                    {
                        $errorMessage = ($LocalizedData.PathNotFoundError -f $currentPath)
                        ThrowTerminatingErrorHelper "PathNotFound" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidArgument) $currentPath
                    }
                }

                return $result;
            }


            function ValidateDuplicateFileSystemPath
            {
                param
                (
                    [string]
                    $inputParameter,

                    [string[]]
                    $path
                )

                $uniqueInputPaths = @()

                # null and empty check are are already done on Path parameter at the cmdlet layer.
                foreach ($currentPath in $path)
                {
                    $currentInputPath = $currentPath.ToUpper()
                    if ($uniqueInputPaths.Contains($currentInputPath))
                    {
                        $errorMessage = ($LocalizedData.DuplicatePathFoundError -f $inputParameter, $currentPath, $inputParameter)
                        ThrowTerminatingErrorHelper "DuplicatePathFound" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidArgument) $currentPath
                    }
                    else
                    {
                        $uniqueInputPaths += $currentInputPath
                    }
                }
            }

            function CompressionLevelMapper
            {
                param
                (
                    [string]
                    $compressionLevel
                )

                $compressionLevelFormat = [System.IO.Compression.CompressionLevel]::Optimal

                # CompressionLevel format is already validated at the cmdlet layer.
                switch ($compressionLevel.ToString())
                {
                    "Fastest"
                    {
                        $compressionLevelFormat = [System.IO.Compression.CompressionLevel]::Fastest
                    }
                    "NoCompression"
                    {
                        $compressionLevelFormat = [System.IO.Compression.CompressionLevel]::NoCompression
                    }
                }

                return $compressionLevelFormat
            }

            function CompressArchiveHelper
            {
                param
                (
                    [string[]]
                    $sourcePath,

                    [string]
                    $destinationPath,

                    [string]
                    $compressionLevel,

                    [bool]
                    $isUpdateMode
                )

                $numberOfItemsArchived = 0
                $sourceFilePaths = @()
                $sourceDirPaths = @()

                foreach ($currentPath in $sourcePath)
                {
                    $result = Test-Path -LiteralPath $currentPath -PathType Leaf
                    if ($result -eq $true)
                    {
                        $sourceFilePaths += $currentPath
                    }
                    else
                    {
                        $sourceDirPaths += $currentPath
                    }
                }

                # The Soure Path contains one or more directory (this directory can have files under it) and no files to be compressed.
                if ($sourceFilePaths.Count -eq 0 -and $sourceDirPaths.Count -gt 0)
                {
                    $currentSegmentWeight = 100/[double]$sourceDirPaths.Count
                    $previousSegmentWeight = 0
                    foreach ($currentSourceDirPath in $sourceDirPaths)
                    {
                        $count = CompressSingleDirHelper $currentSourceDirPath $destinationPath $compressionLevel $true $isUpdateMode $previousSegmentWeight $currentSegmentWeight
                        $numberOfItemsArchived += $count
                        $previousSegmentWeight += $currentSegmentWeight
                    }
                }

                # The Soure Path contains only files to be compressed.
                elseIf ($sourceFilePaths.Count -gt 0 -and $sourceDirPaths.Count -eq 0)
                {
                    # $previousSegmentWeight is equal to 0 as there are no prior segments.
                    # $currentSegmentWeight is set to 100 as all files have equal weightage.
                    $previousSegmentWeight = 0
                    $currentSegmentWeight = 100

                    $numberOfItemsArchived = CompressFilesHelper $sourceFilePaths $destinationPath $compressionLevel $isUpdateMode $previousSegmentWeight $currentSegmentWeight
                }
                # The Soure Path contains one or more files and one or more directories (this directory can have files under it) to be compressed.
                elseif ($sourceFilePaths.Count -gt 0 -and $sourceDirPaths.Count -gt 0)
                {
                    # each directory is considered as an individual segments & all the individual files are clubed in to a separate sgemnet.
                    $currentSegmentWeight = 100/[double]($sourceDirPaths.Count + 1)
                    $previousSegmentWeight = 0

                    foreach ($currentSourceDirPath in $sourceDirPaths)
                    {
                        $count = CompressSingleDirHelper $currentSourceDirPath $destinationPath $compressionLevel $true $isUpdateMode $previousSegmentWeight $currentSegmentWeight
                        $numberOfItemsArchived += $count
                        $previousSegmentWeight += $currentSegmentWeight
                    }

                    $count = CompressFilesHelper $sourceFilePaths $destinationPath $compressionLevel $isUpdateMode $previousSegmentWeight $currentSegmentWeight
                    $numberOfItemsArchived += $count
                }

                return $numberOfItemsArchived
            }

            function CompressFilesHelper
            {
                param
                (
                    [string[]]
                    $sourceFilePaths,

                    [string]
                    $destinationPath,

                    [string]
                    $compressionLevel,

                    [bool]
                    $isUpdateMode,

                    [double]
                    $previousSegmentWeight,

                    [double]
                    $currentSegmentWeight
                )

                $numberOfItemsArchived = ZipArchiveHelper $sourceFilePaths $destinationPath $compressionLevel $isUpdateMode $null $previousSegmentWeight $currentSegmentWeight

                return $numberOfItemsArchived
            }

            function CompressSingleDirHelper
            {
                param
                (
                    [string]
                    $sourceDirPath,

                    [string]
                    $destinationPath,

                    [string]
                    $compressionLevel,

                    [bool]
                    $useParentDirAsRoot,

                    [bool]
                    $isUpdateMode,

                    [double]
                    $previousSegmentWeight,

                    [double]
                    $currentSegmentWeight
                )

                [System.Collections.Generic.List[System.String]]$subDirFiles = @()

                if ($useParentDirAsRoot)
                {
                    $sourceDirInfo = New-Object -TypeName System.IO.DirectoryInfo -ArgumentList $sourceDirPath
                    $sourceDirFullName = $sourceDirInfo.Parent.FullName

                    # If the directory is present at the drive level the DirectoryInfo.Parent include '\' example: C:\
                    # On the other hand if the directory exists at a deper level then DirectoryInfo.Parent
                    # has just the path (without an ending '\'). example C:\source
                    if ($sourceDirFullName.Length -eq 3)
                    {
                        $modifiedSourceDirFullName = $sourceDirFullName
                    }
                    else
                    {
                        $modifiedSourceDirFullName = $sourceDirFullName + "\"
                    }
                }
                else
                {
                    $sourceDirFullName = $sourceDirPath
                    $modifiedSourceDirFullName = $sourceDirFullName + "\"
                }

                $dirContents = Get-ChildItem -LiteralPath $sourceDirPath -Recurse
                foreach ($currentContent in $dirContents)
                {
                    $isContainer = $currentContent -is [System.IO.DirectoryInfo]
                    if (!$isContainer)
                    {
                        $subDirFiles.Add($currentContent.FullName)
                    }
                    else
                    {
                        # The currentContent points to a directory.
                        # We need to check if the directory is an empty directory, if so such a
                        # directory has to be explictly added to the archive file.
                        # if there are no files in the directory the GetFiles() API returns an empty array.
                        $files = $currentContent.GetFiles()
                        if ($files.Count -eq 0)
                        {
                            $subDirFiles.Add($currentContent.FullName + "\")
                        }
                    }
                }

                $numberOfItemsArchived = ZipArchiveHelper $subDirFiles.ToArray() $destinationPath $compressionLevel $isUpdateMode $modifiedSourceDirFullName $previousSegmentWeight $currentSegmentWeight

                return $numberOfItemsArchived
            }

            function ZipArchiveHelper
            {
                param
                (
                    [System.Collections.Generic.List[System.String]]
                    $sourcePaths,

                    [string]
                    $destinationPath,

                    [string]
                    $compressionLevel,

                    [bool]
                    $isUpdateMode,

                    [string]
                    $modifiedSourceDirFullName,

                    [double]
                    $previousSegmentWeight,

                    [double]
                    $currentSegmentWeight
                )

                $numberOfItemsArchived = 0
                $fileMode = [System.IO.FileMode]::Create
                $result = Test-Path -LiteralPath $DestinationPath -PathType Leaf
                if ($result -eq $true)
                {
                    $fileMode = [System.IO.FileMode]::Open
                }

                Add-CompressionAssemblies

                try
                {
                    # At this point we are sure that the archive file has write access.
                    $archiveFileStreamArgs = @($destinationPath, $fileMode)
                    $archiveFileStream = New-Object -TypeName System.IO.FileStream -ArgumentList $archiveFileStreamArgs

                    $zipArchiveArgs = @($archiveFileStream, [System.IO.Compression.ZipArchiveMode]::Update, $false)
                    $zipArchive = New-Object -TypeName System.IO.Compression.ZipArchive -ArgumentList $zipArchiveArgs

                    $currentEntryCount = 0
                    $progressBarStatus = ($LocalizedData.CompressProgressBarText -f $destinationPath)
                    $bufferSize = 4kb
                    $buffer = New-Object Byte[] $bufferSize

                    foreach ($currentFilePath in $sourcePaths)
                    {
                        if ($modifiedSourceDirFullName -ne $null -and $modifiedSourceDirFullName.Length -gt 0)
                        {
                            $index = $currentFilePath.IndexOf($modifiedSourceDirFullName, [System.StringComparison]::OrdinalIgnoreCase)
                            $currentFilePathSubString = $currentFilePath.Substring($index, $modifiedSourceDirFullName.Length)
                            $relativeFilePath = $currentFilePath.Replace($currentFilePathSubString, "").Trim()
                        }
                        else
                        {
                            $relativeFilePath = [System.IO.Path]::GetFileName($currentFilePath)
                        }

                        # Update mode is selected.
                        # Check to see if archive file already contains one or more zip files in it.
                        if ($isUpdateMode -eq $true -and $zipArchive.Entries.Count -gt 0)
                        {
                            $entryToBeUpdated = $null

                            # Check if the file already exists in the archive file.
                            # If so replace it with new file from the input source.
                            # If the file does not exist in the archive file then default to
                            # create mode and create the entry in the archive file.

                            foreach ($currentArchiveEntry in $zipArchive.Entries)
                            {
                                if ($currentArchiveEntry.FullName -eq $relativeFilePath)
                                {
                                    $entryToBeUpdated = $currentArchiveEntry
                                    break
                                }
                            }

                            if ($entryToBeUpdated -ne $null)
                            {
                                $addItemtoArchiveFileMessage = ($LocalizedData.AddItemtoArchiveFile -f $currentFilePath)
                                $entryToBeUpdated.Delete()
                            }
                        }

                        $compression = CompressionLevelMapper $compressionLevel

                        # If a directory needs to be added to an archive file,
                        # by convention the .Net API's expect the path of the diretcory
                        # to end with '\' to detect the path as an directory.
                        if (!$relativeFilePath.EndsWith("\", [StringComparison]::OrdinalIgnoreCase))
                        {
                            try
                            {
                                try
                                {
                                    $currentFileStream = [System.IO.File]::Open($currentFilePath, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read)
                                }
                                catch
                                {
                                    # Failed to access the file. Write a non terminating error to the pipeline
                                    # and move on with the remaining files.
                                    $exception = $_.Exception
                                    if ($null -ne $_.Exception -and
                                        $null -ne $_.Exception.InnerException)
                                    {
                                        $exception = $_.Exception.InnerException
                                    }
                                    $errorRecord = CreateErrorRecordHelper "CompressArchiveUnauthorizedAccessError" $null ([System.Management.Automation.ErrorCategory]::PermissionDenied) $exception $currentFilePath
                                    Write-Error -ErrorRecord $errorRecord
                                }

                                if ($null -ne $currentFileStream)
                                {
                                    $srcStream = New-Object System.IO.BinaryReader $currentFileStream

                                    $currentArchiveEntry = $zipArchive.CreateEntry($relativeFilePath, $compression)

                                    # Updating  the File Creation time so that the same timestamp would be retained after expanding the compressed file.
                                    # At this point we are sure that Get-ChildItem would succeed.
                                    $currentArchiveEntry.LastWriteTime = (Get-Item -LiteralPath $currentFilePath).LastWriteTime

                                    $destStream = New-Object System.IO.BinaryWriter $currentArchiveEntry.Open()

                                    while ($numberOfBytesRead = $srcStream.Read($buffer, 0, $bufferSize))
                                    {
                                        $destStream.Write($buffer, 0, $numberOfBytesRead)
                                        $destStream.Flush()
                                    }

                                    $numberOfItemsArchived += 1
                                    $addItemtoArchiveFileMessage = ($LocalizedData.AddItemtoArchiveFile -f $currentFilePath)
                                }
                            }
                            finally
                            {
                                If ($null -ne $currentFileStream)
                                {
                                    $currentFileStream.Dispose()
                                }
                                If ($null -ne $srcStream)
                                {
                                    $srcStream.Dispose()
                                }
                                If ($null -ne $destStream)
                                {
                                    $destStream.Dispose()
                                }
                            }
                        }
                        else
                        {
                            $currentArchiveEntry = $zipArchive.CreateEntry("$relativeFilePath", $compression)
                            $numberOfItemsArchived += 1
                            $addItemtoArchiveFileMessage = ($LocalizedData.AddItemtoArchiveFile -f $currentFilePath)
                        }

                        if ($null -ne $addItemtoArchiveFileMessage)
                        {
                            Write-Verbose $addItemtoArchiveFileMessage
                        }

                        $currentEntryCount += 1
                        ProgressBarHelper "Compress-Archive" $progressBarStatus $previousSegmentWeight $currentSegmentWeight $sourcePaths.Count  $currentEntryCount
                    }
                }
                finally
                {
                    If ($null -ne $zipArchive)
                    {
                        $zipArchive.Dispose()
                    }

                    If ($null -ne $archiveFileStream)
                    {
                        $archiveFileStream.Dispose()
                    }

                    # Complete writing progress.
                    Write-Progress -Activity "Compress-Archive" -Completed
                }

                return $numberOfItemsArchived
            }

<############################################################################################
# ValidateArchivePathHelper: This is a helper function used to validate the archive file
# path & its file format. The only supported archive file format is .zip
############################################################################################>
            function ValidateArchivePathHelper
            {
                param
                (
                    [string]
                    $archiveFile
                )

                if ([System.IO.File]::Exists($archiveFile))
                {
                    $extension = [system.IO.Path]::GetExtension($archiveFile)

                    # Invalid file extension is specifed for the zip file.
                    if ($extension -ne $zipFileExtension)
                    {
                        $errorMessage = ($LocalizedData.InvalidZipFileExtensionError -f $extension, $zipFileExtension)
                        ThrowTerminatingErrorHelper "NotSupportedArchiveFileExtension" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidArgument) $extension
                    }
                }
                else
                {
                    $errorMessage = ($LocalizedData.PathNotFoundError -f $archiveFile)
                    ThrowTerminatingErrorHelper "PathNotFound" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidArgument) $archiveFile
                }
            }

<############################################################################################
# ExpandArchiveHelper: This is a helper function used to expand the archive file contents
# to the specified directory.
############################################################################################>
            function ExpandArchiveHelper
            {
                param
                (
                    [string]
                    $archiveFile,

                    [string]
                    $expandedDir,

                    [ref]
                    $expandedItems,

                    [boolean]
                    $force,

                    [boolean]
                    $isVerbose,

                    [boolean]
                    $isConfirm
                )

                Add-CompressionAssemblies

                try
                {
                    # The existance of archive file has already been validated by ValidateArchivePathHelper
                    # before calling this helper function.
                    $archiveFileStreamArgs = @($archiveFile, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read)
                    $archiveFileStream = New-Object -TypeName System.IO.FileStream -ArgumentList $archiveFileStreamArgs

                    $zipArchiveArgs = @($archiveFileStream, [System.IO.Compression.ZipArchiveMode]::Read, $false)
                    $zipArchive = New-Object -TypeName System.IO.Compression.ZipArchive -ArgumentList $zipArchiveArgs

                    if ($zipArchive.Entries.Count -eq 0)
                    {
                        $archiveFileIsEmpty = ($LocalizedData.ArchiveFileIsEmpty -f $archiveFile)
                        Write-Verbose $archiveFileIsEmpty
                        return
                    }

                    $currentEntryCount = 0
                    $progressBarStatus = ($LocalizedData.ExpandProgressBarText -f $archiveFile)

                    # The archive entries can either be empty directories or files.
                    foreach ($currentArchiveEntry in $zipArchive.Entries)
                    {
                        $currentArchiveEntryPath = Join-Path -Path $expandedDir -ChildPath $currentArchiveEntry.FullName
                        $extension = [system.IO.Path]::GetExtension($currentArchiveEntryPath)

                        # The current archive entry is an empty directory
                        # The FullName of the Archive Entry representing a directory would end with a trailing '\'.
                        if ($extension -eq [string]::Empty -and
                            $currentArchiveEntryPath.EndsWith("\", [StringComparison]::OrdinalIgnoreCase))
                        {
                            $pathExists = Test-Path -LiteralPath $currentArchiveEntryPath

                            # The current archive entry expects an empty directory.
                            # Check if the existing directory is empty. If its not empty
                            # then it means that user has added this directory by other means.
                            if ($pathExists -eq $false)
                            {
                                New-Item $currentArchiveEntryPath -ItemType Directory -Confirm:$isConfirm | Out-Null

                                if (Test-Path -LiteralPath $currentArchiveEntryPath -PathType Container)
                                {
                                    $addEmptyDirectorytoExpandedPathMessage = ($LocalizedData.AddItemtoArchiveFile -f $currentArchiveEntryPath)
                                    Write-Verbose $addEmptyDirectorytoExpandedPathMessage

                                    $expandedItems.Value += $currentArchiveEntryPath
                                }
                            }
                        }
                        else
                        {
                            try
                            {
                                $currentArchiveEntryFileInfo = New-Object -TypeName System.IO.FileInfo -ArgumentList $currentArchiveEntryPath
                                $parentDirExists = Test-Path -LiteralPath $currentArchiveEntryFileInfo.DirectoryName -PathType Container

                                # If the Parent directory of the current entry in the archive file does not exist, then create it.
                                if ($parentDirExists -eq $false)
                                {
                                    New-Item $currentArchiveEntryFileInfo.DirectoryName -ItemType Directory -Confirm:$isConfirm | Out-Null

                                    if (!(Test-Path -LiteralPath $currentArchiveEntryFileInfo.DirectoryName -PathType Container))
                                    {
                                        # The directory referred by $currentArchiveEntryFileInfo.DirectoryName was not successfully created.
                                        # This could be because the user has specified -Confirm paramter when Expand-Archive was invoked
                                        # and authorization was not provided when confirmation was prompted. In such a scenario,
                                        # we skip the current file in the archive and continue with the remaining archive file contents.
                                        Continue
                                    }

                                    $expandedItems.Value += $currentArchiveEntryFileInfo.DirectoryName
                                }

                                $hasNonTerminatingError = $false

                                # Check if the file in to which the current archive entry contents
                                # would be expanded already exists.
                                if ($currentArchiveEntryFileInfo.Exists)
                                {
                                    if ($force)
                                    {
                                        Remove-Item -LiteralPath $currentArchiveEntryFileInfo.FullName -Force -ErrorVariable ev -Verbose:$isVerbose -Confirm:$isConfirm
                                        if ($ev -ne $null)
                                        {
                                            $hasNonTerminatingError = $true
                                        }

                                        if (Test-Path -LiteralPath $currentArchiveEntryFileInfo.FullName -PathType Leaf)
                                        {
                                            # The file referred by $currentArchiveEntryFileInfo.FullName was not successfully removed.
                                            # This could be because the user has specified -Confirm paramter when Expand-Archive was invoked
                                            # and authorization was not provided when confirmation was prompted. In such a scenario,
                                            # we skip the current file in the archive and continue with the remaining archive file contents.
                                            Continue
                                        }
                                    }
                                    else
                                    {
                                        # Write non-terminating error to the pipeline.
                                        $errorMessage = ($LocalizedData.FileExistsError -f $currentArchiveEntryFileInfo.FullName, $archiveFile, $currentArchiveEntryFileInfo.FullName, $currentArchiveEntryFileInfo.FullName)
                                        $errorRecord = CreateErrorRecordHelper "ExpandArchiveFileExists" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidOperation) $null $currentArchiveEntryFileInfo.FullName
                                        Write-Error -ErrorRecord $errorRecord
                                        $hasNonTerminatingError = $true
                                    }
                                }

                                if (!$hasNonTerminatingError)
                                {
                                    [System.IO.Compression.ZipFileExtensions]::ExtractToFile($currentArchiveEntry, $currentArchiveEntryPath, $false)

                                    # Add the expanded file path to the $expandedItems array,
                                    # to keep track of all the expanded files created while expanding the archive file.
                                    # If user enters CTRL + C then at that point of time, all these expanded files
                                    # would be deleted as part of the clean up process.
                                    $expandedItems.Value += $currentArchiveEntryPath

                                    $addFiletoExpandedPathMessage = ($LocalizedData.CreateFileAtExpandedPath -f $currentArchiveEntryPath)
                                    Write-Verbose $addFiletoExpandedPathMessage
                                }
                            }
                            finally
                            {
                                If ($null -ne $destStream)
                                {
                                    $destStream.Dispose()
                                }

                                If ($null -ne $srcStream)
                                {
                                    $srcStream.Dispose()
                                }
                            }
                        }

                        $currentEntryCount += 1
                        # $currentSegmentWeight is Set to 100 giving equal weightage to each file that is getting expanded.
                        # $previousSegmentWeight is set to 0 as there are no prior segments.
                        $previousSegmentWeight = 0
                        $currentSegmentWeight = 100
                        ProgressBarHelper "Expand-Archive" $progressBarStatus $previousSegmentWeight $currentSegmentWeight $zipArchive.Entries.Count  $currentEntryCount
                    }
                }
                finally
                {
                    If ($null -ne $zipArchive)
                    {
                        $zipArchive.Dispose()
                    }

                    If ($null -ne $archiveFileStream)
                    {
                        $archiveFileStream.Dispose()
                    }

                    # Complete writing progress.
                    Write-Progress -Activity "Expand-Archive" -Completed
                }
            }

<############################################################################################
# ProgressBarHelper: This is a helper function used to display progress message.
# This function is used by both Compress-Archive & Expand-Archive to display archive file
# creation/expansion progress.
############################################################################################>
            function ProgressBarHelper
            {
                param
                (
                    [string]
                    $cmdletName,

                    [string]
                    $status,

                    [double]
                    $previousSegmentWeight,

                    [double]
                    $currentSegmentWeight,

                    [int]
                    $totalNumberofEntries,

                    [int]
                    $currentEntryCount
                )

                if ($currentEntryCount -gt 0 -and
                    $totalNumberofEntries -gt 0 -and
                    $previousSegmentWeight -ge 0 -and
                    $currentSegmentWeight -gt 0)
                {
                    $entryDefaultWeight = $currentSegmentWeight/[double]$totalNumberofEntries

                    $percentComplete = $previousSegmentWeight + ($entryDefaultWeight * $currentEntryCount)
                    Write-Progress -Activity $cmdletName -Status $status -PercentComplete $percentComplete
                }
            }

<############################################################################################
# CSVHelper: This is a helper function used to append comma after each path specifid by
# the SourcePath array. This helper function is used to display all the user supplied paths
# in the WhatIf message.
############################################################################################>
            function CSVHelper
            {
                param
                (
                    [string[]]
                    $sourcePath
                )

                # SourcePath has already been validated by the calling funcation.
                if ($sourcePath.Count -gt 1)
                {
                    $sourcePathInCsvFormat = "`n"
                    for ($currentIndex = 0; $currentIndex -lt $sourcePath.Count; $currentIndex++)
                    {
                        if ($currentIndex -eq $sourcePath.Count - 1)
                        {
                            $sourcePathInCsvFormat += $sourcePath[$currentIndex]
                        }
                        else
                        {
                            $sourcePathInCsvFormat += $sourcePath[$currentIndex] + "`n"
                        }
                    }
                }
                else
                {
                    $sourcePathInCsvFormat = $sourcePath
                }

                return $sourcePathInCsvFormat
            }

<############################################################################################
# ThrowTerminatingErrorHelper: This is a helper function used to throw terminating error.
############################################################################################>
            function ThrowTerminatingErrorHelper
            {
                param
                (
                    [string]
                    $errorId,

                    [string]
                    $errorMessage,

                    [System.Management.Automation.ErrorCategory]
                    $errorCategory,

                    [object]
                    $targetObject,

                    [Exception]
                    $innerException
                )

                if ($innerException -eq $null)
                {
                    $exception = New-object System.IO.IOException $errorMessage
                }
                else
                {
                    $exception = New-Object System.IO.IOException $errorMessage, $innerException
                }

                $exception = New-Object System.IO.IOException $errorMessage
                $errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $targetObject
                $PSCmdlet.ThrowTerminatingError($errorRecord)
            }

<############################################################################################
# CreateErrorRecordHelper: This is a helper function used to create an ErrorRecord
############################################################################################>
            function CreateErrorRecordHelper
            {
                param
                (
                    [string]
                    $errorId,

                    [string]
                    $errorMessage,

                    [System.Management.Automation.ErrorCategory]
                    $errorCategory,

                    [Exception]
                    $exception,

                    [object]
                    $targetObject
                )

                if ($null -eq $exception)
                {
                    $exception = New-Object System.IO.IOException $errorMessage
                }

                $errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $targetObject
                return $errorRecord
            }
            #endregion Utility Functions

            $inputPaths = @()
            $destinationParentDir = [system.IO.Path]::GetDirectoryName($DestinationPath)
            if ($null -eq $destinationParentDir)
            {
                $errorMessage = ($LocalizedData.InvalidDestinationPath -f $DestinationPath)
                ThrowTerminatingErrorHelper "InvalidArchiveFilePath" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidArgument) $DestinationPath
            }

            if ($destinationParentDir -eq [string]::Empty)
            {
                $destinationParentDir = '.'
            }

            $achiveFileName = [system.IO.Path]::GetFileName($DestinationPath)
            $destinationParentDir = GetResolvedPathHelper $destinationParentDir $false $PSCmdlet

            if ($destinationParentDir.Count -gt 1)
            {
                $errorMessage = ($LocalizedData.InvalidArchiveFilePathError -f $DestinationPath, "DestinationPath", "DestinationPath")
                ThrowTerminatingErrorHelper "InvalidArchiveFilePath" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidArgument) $DestinationPath
            }

            IsValidFileSystemPath $destinationParentDir | Out-Null
            $DestinationPath = Join-Path -Path $destinationParentDir -ChildPath $achiveFileName

            # GetExtension API does not validate for the actual existance of the path.
            $extension = [system.IO.Path]::GetExtension($DestinationPath)

            # If user does not specify .Zip extension, we append it.
            If ($extension -eq [string]::Empty)
            {
                $DestinationPathWithOutExtension = $DestinationPath
                $DestinationPath = $DestinationPathWithOutExtension + $zipFileExtension
                $appendArchiveFileExtensionMessage = ($LocalizedData.AppendArchiveFileExtensionMessage -f $DestinationPathWithOutExtension, $DestinationPath)
                Write-Verbose $appendArchiveFileExtensionMessage
            }
            else
            {
                # Invalid file extension is specified for the zip file to be created.
                if ($extension -ne $zipFileExtension)
                {
                    $errorMessage = ($LocalizedData.InvalidZipFileExtensionError -f $extension, $zipFileExtension)
                    ThrowTerminatingErrorHelper "NotSupportedArchiveFileExtension" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidArgument) $extension
                }
            }

            $archiveFileExist = Test-Path -LiteralPath $DestinationPath -PathType Leaf

            if ($archiveFileExist -and ($Update -eq $false -and $Force -eq $false))
            {
                $errorMessage = ($LocalizedData.ZipFileExistError -f $DestinationPath)
                ThrowTerminatingErrorHelper "ArchiveFileExists" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidArgument) $DestinationPath
            }

            # If archive file already exists and if -Update is specified, then we check to see
            # if we have write access permission to update the existing archive file.
            if ($archiveFileExist -and $Update -eq $true)
            {
                $item = Get-Item -Path $DestinationPath
                if ($item.Attributes.ToString().Contains("ReadOnly"))
                {
                    $errorMessage = ($LocalizedData.ArchiveFileIsReadOnly -f $DestinationPath)
                    ThrowTerminatingErrorHelper "ArchiveFileIsReadOnly" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidOperation) $DestinationPath
                }
            }

            $isWhatIf = $psboundparameters.ContainsKey("WhatIf")
            if (!$isWhatIf)
            {
                $preparingToCompressVerboseMessage = ($LocalizedData.PreparingToCompressVerboseMessage)
                Write-Verbose $preparingToCompressVerboseMessage

                $progressBarStatus = ($LocalizedData.CompressProgressBarText -f $DestinationPath)
                ProgressBarHelper "Compress-Archive" $progressBarStatus 0 100 100 1
            }
        }
        PROCESS
        {
            if ($PsCmdlet.ParameterSetName -eq "Path" -or
                $PsCmdlet.ParameterSetName -eq "PathWithForce" -or
                $PsCmdlet.ParameterSetName -eq "PathWithUpdate")
            {
                $inputPaths += $Path
            }

            if ($PsCmdlet.ParameterSetName -eq "LiteralPath" -or
                $PsCmdlet.ParameterSetName -eq "LiteralPathWithForce" -or
                $PsCmdlet.ParameterSetName -eq "LiteralPathWithUpdate")
            {
                $inputPaths += $LiteralPath
            }
        }
        END
        {
            # If archive file already exists and if -Force is specified, we delete the
            # existing artchive file and create a brand new one.
            if (($PsCmdlet.ParameterSetName -eq "PathWithForce" -or
                    $PsCmdlet.ParameterSetName -eq "LiteralPathWithForce") -and $archiveFileExist)
            {
                Remove-Item -Path $DestinationPath -Force -ErrorAction Stop
            }

            # Validate Source Path depeding on parameter set being used.
            # The specified source path conatins one or more files or directories that needs
            # to be compressed.
            $isLiteralPathUsed = $false
            if ($PsCmdlet.ParameterSetName -eq "LiteralPath" -or
                $PsCmdlet.ParameterSetName -eq "LiteralPathWithForce" -or
                $PsCmdlet.ParameterSetName -eq "LiteralPathWithUpdate")
            {
                $isLiteralPathUsed = $true
            }

            ValidateDuplicateFileSystemPath $PsCmdlet.ParameterSetName $inputPaths
            $resolvedPaths = GetResolvedPathHelper $inputPaths $isLiteralPathUsed $PSCmdlet
            IsValidFileSystemPath $resolvedPaths | Out-Null

            $sourcePath = $resolvedPaths;

            # CSVHelper: This is a helper function used to append comma after each path specifid by
            # the $sourcePath array. The comma saperated paths are displayed in the -WhatIf message.
            $sourcePathInCsvFormat = CSVHelper $sourcePath
            if ($pscmdlet.ShouldProcess($sourcePathInCsvFormat))
            {
                try
                {
                    # StopProcessing is not avaliable in Script cmdlets. However the pipleline execution
                    # is terminated when ever 'CTRL + C' is entered by user to terminate the cmdlet execution.
                    # The finally block is executed whenever pipleline is terminated.
                    # $isArchiveFileProcessingComplete variable is used to track if 'CTRL + C' is entered by the
                    # user.
                    $isArchiveFileProcessingComplete = $false

                    $numberOfItemsArchived = CompressArchiveHelper $sourcePath $DestinationPath $CompressionLevel $Update

                    $isArchiveFileProcessingComplete = $true
                }
                finally
                {
                    # The $isArchiveFileProcessingComplete would be set to $false if user has typed 'CTRL + C' to
                    # terminate the cmdlet execution or if an unhandled exception is thrown.
                    # $numberOfItemsArchived contains the count of number of files or directories add to the archive file.
                    # If the newly created archive file is empty then we delete it as its not usable.
                    if (($isArchiveFileProcessingComplete -eq $false) -or
                        ($numberOfItemsArchived -eq 0))
                    {
                        $DeleteArchiveFileMessage = ($LocalizedData.DeleteArchiveFile -f $DestinationPath)
                        Write-Verbose $DeleteArchiveFileMessage

                        # delete the partial archive file created.
                        if (Test-Path $DestinationPath)
                        {
                            Remove-Item -LiteralPath $DestinationPath -Force -Recurse -ErrorAction SilentlyContinue
                        }
                    }
                }
            }
        }
    }
}
tools\dbatools\optional\Expand-Archive.ps1
if ($PSVersionTable.PSVersion.Major -lt 5)
{


<#
Copied from the Microsoft Module: Microsoft.PowerShell.Archive
Which ships with PowerShell Version 5 but will run under v3.
#>



    function Expand-Archive
    {
        <#
            .SYNOPSIS
                Extracts files from a specified archive (zipped) file.

            .DESCRIPTION
                The Expand-Archive cmdlet extracts files from a specified zipped archive file to a specified destination folder. An archive file allows multiple files to be packaged, and optionally compressed, into a single zipped file for easier distribution and storage.

            .PARAMETER Path
                Specifies the path to the archive file.

            .PARAMETER LiteralPath
                Specifies the path to an archive file. Unlike the Path parameter, the value of LiteralPath is used exactly as it is typed. Wildcard characters are not supported. If the path includes escape characters, enclose each escape character in single quotation marks, to instruct Windows PowerShell not to interpret any characters as escape sequences.

            .PARAMETER DestinationPath
                Specifies the path to the folder in which you want the command to save extracted files. Enter the path to a folder, but do not specify a file name or file name extension. This parameter is required.

            .PARAMETER Force
                Forces the command to run without asking for user confirmation.

            .PARAMETER Confirm
                Prompts you for confirmation before running the cmdlet.

            .PARAMETER WhatIf
                Shows what would happen if the cmdlet runs. The cmdlet is not run.

            .EXAMPLE
                Example 1: Extract the contents of an archive

                PS C:\>Expand-Archive -LiteralPath C:\Archives\Draft.Zip -DestinationPath C:\Reference

                This command extracts the contents of an existing archive file, Draft.zip, into the folder specified by the DestinationPath parameter, C:\Reference.

            .EXAMPLE
                Example 2: Extract the contents of an archive in the current folder

                PS C:\>Expand-Archive -Path Draft.Zip -DestinationPath C:\Reference

                This command extracts the contents of an existing archive file in the current folder, Draft.zip, into the folder specified by the DestinationPath parameter, C:\Reference.
        #>
        [CmdletBinding(
                       DefaultParameterSetName = "Path",
                       SupportsShouldProcess = $true,
                       HelpUri = "http://go.microsoft.com/fwlink/?LinkID=393253")]
        param
        (
            [parameter (
                        mandatory = $true,
                        Position = 0,
                        ParameterSetName = "Path",
                        ValueFromPipeline = $true,
                        ValueFromPipelineByPropertyName = $true)]
            [ValidateNotNullOrEmpty()]
            [string]
            $Path,

            [parameter (
                        mandatory = $true,
                        ParameterSetName = "LiteralPath",
                        ValueFromPipelineByPropertyName = $true)]
            [ValidateNotNullOrEmpty()]
            [Alias("PSPath")]
            [string]
            $LiteralPath,

            [parameter (mandatory = $false,
                        Position = 1,
                        ValueFromPipeline = $false,
                        ValueFromPipelineByPropertyName = $false)]
            [ValidateNotNullOrEmpty()]
            [string]
            $DestinationPath,

            [parameter (mandatory = $false,
                        ValueFromPipeline = $false,
                        ValueFromPipelineByPropertyName = $false)]
            [switch]
            $Force
        )

        BEGIN
        {
            Add-Type -AssemblyName System.IO.Compression -ErrorAction Ignore
            Add-Type -AssemblyName System.IO.Compression.FileSystem -ErrorAction Ignore

            $zipFileExtension = ".zip"

            $LocalizedData = ConvertFrom-StringData @'
    PathNotFoundError=The path '{0}' either does not exist or is not a valid file system path.
    ExpandArchiveInValidDestinationPath=The path '{0}' is not a valid file system directory path.
    InvalidZipFileExtensionError={0} is not a supported archive file format. {1} is the only supported archive file format.
    ArchiveFileIsReadOnly=The attributes of the archive file {0} is set to 'ReadOnly' hence it cannot be updated. If you intend to update the existing archive file, remove the 'ReadOnly' attribute on the archive file else use -Force parameter to override and create a new archive file.
    ZipFileExistError=The archive file {0} already exists. Use the -Update parameter to update the existing archive file or use the -Force parameter to overwrite the existing archive file.
    DuplicatePathFoundError=The input to {0} parameter contains a duplicate path '{1}'. Provide a unique set of paths as input to {2} parameter.
    ArchiveFileIsEmpty=The archive file {0} is empty.
    CompressProgressBarText=The archive file '{0}' creation is in progress...
    ExpandProgressBarText=The archive file '{0}' expansion is in progress...
    AppendArchiveFileExtensionMessage=The archive file path '{0}' supplied to the DestinationPath patameter does not include .zip extension. Hence .zip is appended to the supplied DestinationPath path and the archive file would be created at '{1}'.
    AddItemtoArchiveFile=Adding '{0}'.
    CreateFileAtExpandedPath=Created '{0}'.
    InvalidArchiveFilePathError=The archive file path '{0}' specified as input to the {1} parameter is resolving to multiple file system paths. Provide a unique path to the {2} parameter where the archive file has to be created.
    InvalidExpandedDirPathError=The directory path '{0}' specified as input to the DestinationPath parameter is resolving to multiple file system paths. Provide a unique path to the Destination parameter where the archive file contents have to be expanded.
    FileExistsError=Failed to create file '{0}' while expanding the archive file '{1}' contents as the file '{2}' already exists. Use the -Force parameter if you want to overwrite the existing directory '{3}' contents when expanding the archive file.
    DeleteArchiveFile=The partially created archive file '{0}' is deleted as it is not usable.
    InvalidDestinationPath=The destination path '{0}' does not contain a valid archive file name.
    PreparingToCompressVerboseMessage=Preparing to compress...
    PreparingToExpandVerboseMessage=Preparing to expand...
'@

            #region Utility Functions
            function GetResolvedPathHelper
            {
                param
                (
                    [string[]]
                    $path,

                    [boolean]
                    $isLiteralPath,

                    [System.Management.Automation.PSCmdlet]
                    $callerPSCmdlet
                )

                $resolvedPaths = @()

                # null and empty check are are already done on Path parameter at the cmdlet layer.
                foreach ($currentPath in $path)
                {
                    try
                    {
                        if ($isLiteralPath)
                        {
                            $currentResolvedPaths = Resolve-Path -LiteralPath $currentPath -ErrorAction Stop
                        }
                        else
                        {
                            $currentResolvedPaths = Resolve-Path -Path $currentPath -ErrorAction Stop
                        }
                    }
                    catch
                    {
                        $errorMessage = ($LocalizedData.PathNotFoundError -f $currentPath)
                        $exception = New-Object System.InvalidOperationException $errorMessage, $_.Exception
                        $errorRecord = CreateErrorRecordHelper "ArchiveCmdletPathNotFound" $null ([System.Management.Automation.ErrorCategory]::InvalidArgument) $exception $currentPath
                        $callerPSCmdlet.ThrowTerminatingError($errorRecord)
                    }

                    foreach ($currentResolvedPath in $currentResolvedPaths)
                    {
                        $resolvedPaths += $currentResolvedPath.ProviderPath
                    }
                }

                $resolvedPaths
            }

            function Add-CompressionAssemblies
            {

                if ($PSEdition -eq "Desktop")
                {
                    Add-Type -AssemblyName System.IO.Compression
                    Add-Type -AssemblyName System.IO.Compression.FileSystem
                }
            }

            function IsValidFileSystemPath
            {
                param
                (
                    [string[]]
                    $path
                )

                $result = $true;

                # null and empty check are are already done on Path parameter at the cmdlet layer.
                foreach ($currentPath in $path)
                {
                    if (!([System.IO.File]::Exists($currentPath) -or [System.IO.Directory]::Exists($currentPath)))
                    {
                        $errorMessage = ($LocalizedData.PathNotFoundError -f $currentPath)
                        ThrowTerminatingErrorHelper "PathNotFound" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidArgument) $currentPath
                    }
                }

                return $result;
            }


            function ValidateDuplicateFileSystemPath
            {
                param
                (
                    [string]
                    $inputParameter,

                    [string[]]
                    $path
                )

                $uniqueInputPaths = @()

                # null and empty check are are already done on Path parameter at the cmdlet layer.
                foreach ($currentPath in $path)
                {
                    $currentInputPath = $currentPath.ToUpper()
                    if ($uniqueInputPaths.Contains($currentInputPath))
                    {
                        $errorMessage = ($LocalizedData.DuplicatePathFoundError -f $inputParameter, $currentPath, $inputParameter)
                        ThrowTerminatingErrorHelper "DuplicatePathFound" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidArgument) $currentPath
                    }
                    else
                    {
                        $uniqueInputPaths += $currentInputPath
                    }
                }
            }

            function CompressionLevelMapper
            {
                param
                (
                    [string]
                    $compressionLevel
                )

                $compressionLevelFormat = [System.IO.Compression.CompressionLevel]::Optimal

                # CompressionLevel format is already validated at the cmdlet layer.
                switch ($compressionLevel.ToString())
                {
                    "Fastest"
                    {
                        $compressionLevelFormat = [System.IO.Compression.CompressionLevel]::Fastest
                    }
                    "NoCompression"
                    {
                        $compressionLevelFormat = [System.IO.Compression.CompressionLevel]::NoCompression
                    }
                }

                return $compressionLevelFormat
            }

            function CompressArchiveHelper
            {
                param
                (
                    [string[]]
                    $sourcePath,

                    [string]
                    $destinationPath,

                    [string]
                    $compressionLevel,

                    [bool]
                    $isUpdateMode
                )

                $numberOfItemsArchived = 0
                $sourceFilePaths = @()
                $sourceDirPaths = @()

                foreach ($currentPath in $sourcePath)
                {
                    $result = Test-Path -LiteralPath $currentPath -PathType Leaf
                    if ($result -eq $true)
                    {
                        $sourceFilePaths += $currentPath
                    }
                    else
                    {
                        $sourceDirPaths += $currentPath
                    }
                }

                # The Soure Path contains one or more directory (this directory can have files under it) and no files to be compressed.
                if ($sourceFilePaths.Count -eq 0 -and $sourceDirPaths.Count -gt 0)
                {
                    $currentSegmentWeight = 100/[double]$sourceDirPaths.Count
                    $previousSegmentWeight = 0
                    foreach ($currentSourceDirPath in $sourceDirPaths)
                    {
                        $count = CompressSingleDirHelper $currentSourceDirPath $destinationPath $compressionLevel $true $isUpdateMode $previousSegmentWeight $currentSegmentWeight
                        $numberOfItemsArchived += $count
                        $previousSegmentWeight += $currentSegmentWeight
                    }
                }

                # The Soure Path contains only files to be compressed.
                elseIf ($sourceFilePaths.Count -gt 0 -and $sourceDirPaths.Count -eq 0)
                {
                    # $previousSegmentWeight is equal to 0 as there are no prior segments.
                    # $currentSegmentWeight is set to 100 as all files have equal weightage.
                    $previousSegmentWeight = 0
                    $currentSegmentWeight = 100

                    $numberOfItemsArchived = CompressFilesHelper $sourceFilePaths $destinationPath $compressionLevel $isUpdateMode $previousSegmentWeight $currentSegmentWeight
                }
                # The Soure Path contains one or more files and one or more directories (this directory can have files under it) to be compressed.
                elseif ($sourceFilePaths.Count -gt 0 -and $sourceDirPaths.Count -gt 0)
                {
                    # each directory is considered as an individual segments & all the individual files are clubed in to a separate sgemnet.
                    $currentSegmentWeight = 100/[double]($sourceDirPaths.Count + 1)
                    $previousSegmentWeight = 0

                    foreach ($currentSourceDirPath in $sourceDirPaths)
                    {
                        $count = CompressSingleDirHelper $currentSourceDirPath $destinationPath $compressionLevel $true $isUpdateMode $previousSegmentWeight $currentSegmentWeight
                        $numberOfItemsArchived += $count
                        $previousSegmentWeight += $currentSegmentWeight
                    }

                    $count = CompressFilesHelper $sourceFilePaths $destinationPath $compressionLevel $isUpdateMode $previousSegmentWeight $currentSegmentWeight
                    $numberOfItemsArchived += $count
                }

                return $numberOfItemsArchived
            }

            function CompressFilesHelper
            {
                param
                (
                    [string[]]
                    $sourceFilePaths,

                    [string]
                    $destinationPath,

                    [string]
                    $compressionLevel,

                    [bool]
                    $isUpdateMode,

                    [double]
                    $previousSegmentWeight,

                    [double]
                    $currentSegmentWeight
                )

                $numberOfItemsArchived = ZipArchiveHelper $sourceFilePaths $destinationPath $compressionLevel $isUpdateMode $null $previousSegmentWeight $currentSegmentWeight

                return $numberOfItemsArchived
            }

            function CompressSingleDirHelper
            {
                param
                (
                    [string]
                    $sourceDirPath,

                    [string]
                    $destinationPath,

                    [string]
                    $compressionLevel,

                    [bool]
                    $useParentDirAsRoot,

                    [bool]
                    $isUpdateMode,

                    [double]
                    $previousSegmentWeight,

                    [double]
                    $currentSegmentWeight
                )

                [System.Collections.Generic.List[System.String]]$subDirFiles = @()

                if ($useParentDirAsRoot)
                {
                    $sourceDirInfo = New-Object -TypeName System.IO.DirectoryInfo -ArgumentList $sourceDirPath
                    $sourceDirFullName = $sourceDirInfo.Parent.FullName

                    # If the directory is present at the drive level the DirectoryInfo.Parent include '\' example: C:\
                    # On the other hand if the directory exists at a deper level then DirectoryInfo.Parent
                    # has just the path (without an ending '\'). example C:\source
                    if ($sourceDirFullName.Length -eq 3)
                    {
                        $modifiedSourceDirFullName = $sourceDirFullName
                    }
                    else
                    {
                        $modifiedSourceDirFullName = $sourceDirFullName + "\"
                    }
                }
                else
                {
                    $sourceDirFullName = $sourceDirPath
                    $modifiedSourceDirFullName = $sourceDirFullName + "\"
                }

                $dirContents = Get-ChildItem -LiteralPath $sourceDirPath -Recurse
                foreach ($currentContent in $dirContents)
                {
                    $isContainer = $currentContent -is [System.IO.DirectoryInfo]
                    if (!$isContainer)
                    {
                        $subDirFiles.Add($currentContent.FullName)
                    }
                    else
                    {
                        # The currentContent points to a directory.
                        # We need to check if the directory is an empty directory, if so such a
                        # directory has to be explictly added to the archive file.
                        # if there are no files in the directory the GetFiles() API returns an empty array.
                        $files = $currentContent.GetFiles()
                        if ($files.Count -eq 0)
                        {
                            $subDirFiles.Add($currentContent.FullName + "\")
                        }
                    }
                }

                $numberOfItemsArchived = ZipArchiveHelper $subDirFiles.ToArray() $destinationPath $compressionLevel $isUpdateMode $modifiedSourceDirFullName $previousSegmentWeight $currentSegmentWeight

                return $numberOfItemsArchived
            }

            function ZipArchiveHelper
            {
                param
                (
                    [System.Collections.Generic.List[System.String]]
                    $sourcePaths,

                    [string]
                    $destinationPath,

                    [string]
                    $compressionLevel,

                    [bool]
                    $isUpdateMode,

                    [string]
                    $modifiedSourceDirFullName,

                    [double]
                    $previousSegmentWeight,

                    [double]
                    $currentSegmentWeight
                )

                $numberOfItemsArchived = 0
                $fileMode = [System.IO.FileMode]::Create
                $result = Test-Path -LiteralPath $DestinationPath -PathType Leaf
                if ($result -eq $true)
                {
                    $fileMode = [System.IO.FileMode]::Open
                }

                Add-CompressionAssemblies

                try
                {
                    # At this point we are sure that the archive file has write access.
                    $archiveFileStreamArgs = @($destinationPath, $fileMode)
                    $archiveFileStream = New-Object -TypeName System.IO.FileStream -ArgumentList $archiveFileStreamArgs

                    $zipArchiveArgs = @($archiveFileStream, [System.IO.Compression.ZipArchiveMode]::Update, $false)
                    $zipArchive = New-Object -TypeName System.IO.Compression.ZipArchive -ArgumentList $zipArchiveArgs

                    $currentEntryCount = 0
                    $progressBarStatus = ($LocalizedData.CompressProgressBarText -f $destinationPath)
                    $bufferSize = 4kb
                    $buffer = New-Object Byte[] $bufferSize

                    foreach ($currentFilePath in $sourcePaths)
                    {
                        if ($modifiedSourceDirFullName -ne $null -and $modifiedSourceDirFullName.Length -gt 0)
                        {
                            $index = $currentFilePath.IndexOf($modifiedSourceDirFullName, [System.StringComparison]::OrdinalIgnoreCase)
                            $currentFilePathSubString = $currentFilePath.Substring($index, $modifiedSourceDirFullName.Length)
                            $relativeFilePath = $currentFilePath.Replace($currentFilePathSubString, "").Trim()
                        }
                        else
                        {
                            $relativeFilePath = [System.IO.Path]::GetFileName($currentFilePath)
                        }

                        # Update mode is selected.
                        # Check to see if archive file already contains one or more zip files in it.
                        if ($isUpdateMode -eq $true -and $zipArchive.Entries.Count -gt 0)
                        {
                            $entryToBeUpdated = $null

                            # Check if the file already exists in the archive file.
                            # If so replace it with new file from the input source.
                            # If the file does not exist in the archive file then default to
                            # create mode and create the entry in the archive file.

                            foreach ($currentArchiveEntry in $zipArchive.Entries)
                            {
                                if ($currentArchiveEntry.FullName -eq $relativeFilePath)
                                {
                                    $entryToBeUpdated = $currentArchiveEntry
                                    break
                                }
                            }

                            if ($entryToBeUpdated -ne $null)
                            {
                                $addItemtoArchiveFileMessage = ($LocalizedData.AddItemtoArchiveFile -f $currentFilePath)
                                $entryToBeUpdated.Delete()
                            }
                        }

                        $compression = CompressionLevelMapper $compressionLevel

                        # If a directory needs to be added to an archive file,
                        # by convention the .Net API's expect the path of the diretcory
                        # to end with '\' to detect the path as an directory.
                        if (!$relativeFilePath.EndsWith("\", [StringComparison]::OrdinalIgnoreCase))
                        {
                            try
                            {
                                try
                                {
                                    $currentFileStream = [System.IO.File]::Open($currentFilePath, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read)
                                }
                                catch
                                {
                                    # Failed to access the file. Write a non terminating error to the pipeline
                                    # and move on with the remaining files.
                                    $exception = $_.Exception
                                    if ($null -ne $_.Exception -and
                                        $null -ne $_.Exception.InnerException)
                                    {
                                        $exception = $_.Exception.InnerException
                                    }
                                    $errorRecord = CreateErrorRecordHelper "CompressArchiveUnauthorizedAccessError" $null ([System.Management.Automation.ErrorCategory]::PermissionDenied) $exception $currentFilePath
                                    Write-Error -ErrorRecord $errorRecord
                                }

                                if ($null -ne $currentFileStream)
                                {
                                    $srcStream = New-Object System.IO.BinaryReader $currentFileStream

                                    $currentArchiveEntry = $zipArchive.CreateEntry($relativeFilePath, $compression)

                                    # Updating  the File Creation time so that the same timestamp would be retained after expanding the compressed file.
                                    # At this point we are sure that Get-ChildItem would succeed.
                                    $currentArchiveEntry.LastWriteTime = (Get-Item -LiteralPath $currentFilePath).LastWriteTime

                                    $destStream = New-Object System.IO.BinaryWriter $currentArchiveEntry.Open()

                                    while ($numberOfBytesRead = $srcStream.Read($buffer, 0, $bufferSize))
                                    {
                                        $destStream.Write($buffer, 0, $numberOfBytesRead)
                                        $destStream.Flush()
                                    }

                                    $numberOfItemsArchived += 1
                                    $addItemtoArchiveFileMessage = ($LocalizedData.AddItemtoArchiveFile -f $currentFilePath)
                                }
                            }
                            finally
                            {
                                If ($null -ne $currentFileStream)
                                {
                                    $currentFileStream.Dispose()
                                }
                                If ($null -ne $srcStream)
                                {
                                    $srcStream.Dispose()
                                }
                                If ($null -ne $destStream)
                                {
                                    $destStream.Dispose()
                                }
                            }
                        }
                        else
                        {
                            $currentArchiveEntry = $zipArchive.CreateEntry("$relativeFilePath", $compression)
                            $numberOfItemsArchived += 1
                            $addItemtoArchiveFileMessage = ($LocalizedData.AddItemtoArchiveFile -f $currentFilePath)
                        }

                        if ($null -ne $addItemtoArchiveFileMessage)
                        {
                            Write-Verbose $addItemtoArchiveFileMessage
                        }

                        $currentEntryCount += 1
                        ProgressBarHelper "Compress-Archive" $progressBarStatus $previousSegmentWeight $currentSegmentWeight $sourcePaths.Count  $currentEntryCount
                    }
                }
                finally
                {
                    If ($null -ne $zipArchive)
                    {
                        $zipArchive.Dispose()
                    }

                    If ($null -ne $archiveFileStream)
                    {
                        $archiveFileStream.Dispose()
                    }

                    # Complete writing progress.
                    Write-Progress -Activity "Compress-Archive" -Completed
                }

                return $numberOfItemsArchived
            }

<############################################################################################
# ValidateArchivePathHelper: This is a helper function used to validate the archive file
# path & its file format. The only supported archive file format is .zip
############################################################################################>
            function ValidateArchivePathHelper
            {
                param
                (
                    [string]
                    $archiveFile
                )

                if ([System.IO.File]::Exists($archiveFile))
                {
                    $extension = [system.IO.Path]::GetExtension($archiveFile)

                    # Invalid file extension is specifed for the zip file.
                    if ($extension -ne $zipFileExtension)
                    {
                        $errorMessage = ($LocalizedData.InvalidZipFileExtensionError -f $extension, $zipFileExtension)
                        ThrowTerminatingErrorHelper "NotSupportedArchiveFileExtension" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidArgument) $extension
                    }
                }
                else
                {
                    $errorMessage = ($LocalizedData.PathNotFoundError -f $archiveFile)
                    ThrowTerminatingErrorHelper "PathNotFound" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidArgument) $archiveFile
                }
            }

<############################################################################################
# ExpandArchiveHelper: This is a helper function used to expand the archive file contents
# to the specified directory.
############################################################################################>
            function ExpandArchiveHelper
            {
                param
                (
                    [string]
                    $archiveFile,

                    [string]
                    $expandedDir,

                    [ref]
                    $expandedItems,

                    [boolean]
                    $force,

                    [boolean]
                    $isVerbose,

                    [boolean]
                    $isConfirm
                )

                Add-CompressionAssemblies

                try
                {
                    # The existance of archive file has already been validated by ValidateArchivePathHelper
                    # before calling this helper function.
                    $archiveFileStreamArgs = @($archiveFile, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read)
                    $archiveFileStream = New-Object -TypeName System.IO.FileStream -ArgumentList $archiveFileStreamArgs

                    $zipArchiveArgs = @($archiveFileStream, [System.IO.Compression.ZipArchiveMode]::Read, $false)
                    $zipArchive = New-Object -TypeName System.IO.Compression.ZipArchive -ArgumentList $zipArchiveArgs

                    if ($zipArchive.Entries.Count -eq 0)
                    {
                        $archiveFileIsEmpty = ($LocalizedData.ArchiveFileIsEmpty -f $archiveFile)
                        Write-Verbose $archiveFileIsEmpty
                        return
                    }

                    $currentEntryCount = 0
                    $progressBarStatus = ($LocalizedData.ExpandProgressBarText -f $archiveFile)

                    # The archive entries can either be empty directories or files.
                    foreach ($currentArchiveEntry in $zipArchive.Entries)
                    {
                        $currentArchiveEntryPath = Join-Path -Path $expandedDir -ChildPath $currentArchiveEntry.FullName
                        $extension = [system.IO.Path]::GetExtension($currentArchiveEntryPath)

                        # The current archive entry is an empty directory
                        # The FullName of the Archive Entry representing a directory would end with a trailing '\'.
                        if ($extension -eq [string]::Empty -and
                            $currentArchiveEntryPath.EndsWith("\", [StringComparison]::OrdinalIgnoreCase))
                        {
                            $pathExists = Test-Path -LiteralPath $currentArchiveEntryPath

                            # The current archive entry expects an empty directory.
                            # Check if the existing directory is empty. If its not empty
                            # then it means that user has added this directory by other means.
                            if ($pathExists -eq $false)
                            {
                                New-Item $currentArchiveEntryPath -ItemType Directory -Confirm:$isConfirm | Out-Null

                                if (Test-Path -LiteralPath $currentArchiveEntryPath -PathType Container)
                                {
                                    $addEmptyDirectorytoExpandedPathMessage = ($LocalizedData.AddItemtoArchiveFile -f $currentArchiveEntryPath)
                                    Write-Verbose $addEmptyDirectorytoExpandedPathMessage

                                    $expandedItems.Value += $currentArchiveEntryPath
                                }
                            }
                        }
                        else
                        {
                            try
                            {
                                $currentArchiveEntryFileInfo = New-Object -TypeName System.IO.FileInfo -ArgumentList $currentArchiveEntryPath
                                $parentDirExists = Test-Path -LiteralPath $currentArchiveEntryFileInfo.DirectoryName -PathType Container

                                # If the Parent directory of the current entry in the archive file does not exist, then create it.
                                if ($parentDirExists -eq $false)
                                {
                                    New-Item $currentArchiveEntryFileInfo.DirectoryName -ItemType Directory -Confirm:$isConfirm | Out-Null

                                    if (!(Test-Path -LiteralPath $currentArchiveEntryFileInfo.DirectoryName -PathType Container))
                                    {
                                        # The directory referred by $currentArchiveEntryFileInfo.DirectoryName was not successfully created.
                                        # This could be because the user has specified -Confirm paramter when Expand-Archive was invoked
                                        # and authorization was not provided when confirmation was prompted. In such a scenario,
                                        # we skip the current file in the archive and continue with the remaining archive file contents.
                                        Continue
                                    }

                                    $expandedItems.Value += $currentArchiveEntryFileInfo.DirectoryName
                                }

                                $hasNonTerminatingError = $false

                                # Check if the file in to which the current archive entry contents
                                # would be expanded already exists.
                                if ($currentArchiveEntryFileInfo.Exists)
                                {
                                    if ($force)
                                    {
                                        Remove-Item -LiteralPath $currentArchiveEntryFileInfo.FullName -Force -ErrorVariable ev -Verbose:$isVerbose -Confirm:$isConfirm
                                        if ($ev -ne $null)
                                        {
                                            $hasNonTerminatingError = $true
                                        }

                                        if (Test-Path -LiteralPath $currentArchiveEntryFileInfo.FullName -PathType Leaf)
                                        {
                                            # The file referred by $currentArchiveEntryFileInfo.FullName was not successfully removed.
                                            # This could be because the user has specified -Confirm paramter when Expand-Archive was invoked
                                            # and authorization was not provided when confirmation was prompted. In such a scenario,
                                            # we skip the current file in the archive and continue with the remaining archive file contents.
                                            Continue
                                        }
                                    }
                                    else
                                    {
                                        # Write non-terminating error to the pipeline.
                                        $errorMessage = ($LocalizedData.FileExistsError -f $currentArchiveEntryFileInfo.FullName, $archiveFile, $currentArchiveEntryFileInfo.FullName, $currentArchiveEntryFileInfo.FullName)
                                        $errorRecord = CreateErrorRecordHelper "ExpandArchiveFileExists" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidOperation) $null $currentArchiveEntryFileInfo.FullName
                                        Write-Error -ErrorRecord $errorRecord
                                        $hasNonTerminatingError = $true
                                    }
                                }

                                if (!$hasNonTerminatingError)
                                {
                                    [System.IO.Compression.ZipFileExtensions]::ExtractToFile($currentArchiveEntry, $currentArchiveEntryPath, $false)

                                    # Add the expanded file path to the $expandedItems array,
                                    # to keep track of all the expanded files created while expanding the archive file.
                                    # If user enters CTRL + C then at that point of time, all these expanded files
                                    # would be deleted as part of the clean up process.
                                    $expandedItems.Value += $currentArchiveEntryPath

                                    $addFiletoExpandedPathMessage = ($LocalizedData.CreateFileAtExpandedPath -f $currentArchiveEntryPath)
                                    Write-Verbose $addFiletoExpandedPathMessage
                                }
                            }
                            finally
                            {
                                If ($null -ne $destStream)
                                {
                                    $destStream.Dispose()
                                }

                                If ($null -ne $srcStream)
                                {
                                    $srcStream.Dispose()
                                }
                            }
                        }

                        $currentEntryCount += 1
                        # $currentSegmentWeight is Set to 100 giving equal weightage to each file that is getting expanded.
                        # $previousSegmentWeight is set to 0 as there are no prior segments.
                        $previousSegmentWeight = 0
                        $currentSegmentWeight = 100
                        ProgressBarHelper "Expand-Archive" $progressBarStatus $previousSegmentWeight $currentSegmentWeight $zipArchive.Entries.Count  $currentEntryCount
                    }
                }
                finally
                {
                    If ($null -ne $zipArchive)
                    {
                        $zipArchive.Dispose()
                    }

                    If ($null -ne $archiveFileStream)
                    {
                        $archiveFileStream.Dispose()
                    }

                    # Complete writing progress.
                    Write-Progress -Activity "Expand-Archive" -Completed
                }
            }

<############################################################################################
# ProgressBarHelper: This is a helper function used to display progress message.
# This function is used by both Compress-Archive & Expand-Archive to display archive file
# creation/expansion progress.
############################################################################################>
            function ProgressBarHelper
            {
                param
                (
                    [string]
                    $cmdletName,

                    [string]
                    $status,

                    [double]
                    $previousSegmentWeight,

                    [double]
                    $currentSegmentWeight,

                    [int]
                    $totalNumberofEntries,

                    [int]
                    $currentEntryCount
                )

                if ($currentEntryCount -gt 0 -and
                    $totalNumberofEntries -gt 0 -and
                    $previousSegmentWeight -ge 0 -and
                    $currentSegmentWeight -gt 0)
                {
                    $entryDefaultWeight = $currentSegmentWeight/[double]$totalNumberofEntries

                    $percentComplete = $previousSegmentWeight + ($entryDefaultWeight * $currentEntryCount)
                    Write-Progress -Activity $cmdletName -Status $status -PercentComplete $percentComplete
                }
            }

<############################################################################################
# CSVHelper: This is a helper function used to append comma after each path specifid by
# the SourcePath array. This helper function is used to display all the user supplied paths
# in the WhatIf message.
############################################################################################>
            function CSVHelper
            {
                param
                (
                    [string[]]
                    $sourcePath
                )

                # SourcePath has already been validated by the calling funcation.
                if ($sourcePath.Count -gt 1)
                {
                    $sourcePathInCsvFormat = "`n"
                    for ($currentIndex = 0; $currentIndex -lt $sourcePath.Count; $currentIndex++)
                    {
                        if ($currentIndex -eq $sourcePath.Count - 1)
                        {
                            $sourcePathInCsvFormat += $sourcePath[$currentIndex]
                        }
                        else
                        {
                            $sourcePathInCsvFormat += $sourcePath[$currentIndex] + "`n"
                        }
                    }
                }
                else
                {
                    $sourcePathInCsvFormat = $sourcePath
                }

                return $sourcePathInCsvFormat
            }

<############################################################################################
# ThrowTerminatingErrorHelper: This is a helper function used to throw terminating error.
############################################################################################>
            function ThrowTerminatingErrorHelper
            {
                param
                (
                    [string]
                    $errorId,

                    [string]
                    $errorMessage,

                    [System.Management.Automation.ErrorCategory]
                    $errorCategory,

                    [object]
                    $targetObject,

                    [Exception]
                    $innerException
                )

                if ($innerException -eq $null)
                {
                    $exception = New-object System.IO.IOException $errorMessage
                }
                else
                {
                    $exception = New-Object System.IO.IOException $errorMessage, $innerException
                }

                $exception = New-Object System.IO.IOException $errorMessage
                $errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $targetObject
                $PSCmdlet.ThrowTerminatingError($errorRecord)
            }

<############################################################################################
# CreateErrorRecordHelper: This is a helper function used to create an ErrorRecord
############################################################################################>
            function CreateErrorRecordHelper
            {
                param
                (
                    [string]
                    $errorId,

                    [string]
                    $errorMessage,

                    [System.Management.Automation.ErrorCategory]
                    $errorCategory,

                    [Exception]
                    $exception,

                    [object]
                    $targetObject
                )

                if ($null -eq $exception)
                {
                    $exception = New-Object System.IO.IOException $errorMessage
                }

                $errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $targetObject
                return $errorRecord
            }
            #endregion Utility Functions

            $isVerbose = $psboundparameters.ContainsKey("Verbose")
            $isConfirm = $psboundparameters.ContainsKey("Confirm")

            $isDestinationPathProvided = $true
            if ($DestinationPath -eq [string]::Empty)
            {
                $resolvedDestinationPath = $pwd
                $isDestinationPathProvided = $false
            }
            else
            {
                $destinationPathExists = Test-Path -Path $DestinationPath -PathType Container
                if ($destinationPathExists)
                {
                    $resolvedDestinationPath = GetResolvedPathHelper $DestinationPath $false $PSCmdlet
                    if ($resolvedDestinationPath.Count -gt 1)
                    {
                        $errorMessage = ($LocalizedData.InvalidExpandedDirPathError -f $DestinationPath)
                        ThrowTerminatingErrorHelper "InvalidDestinationPath" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidArgument) $DestinationPath
                    }

                    # At this point we are sure that the provided path resolves to a valid single path.
                    # Calling Resolve-Path again to get the underlying provider name.
                    $suppliedDestinationPath = Resolve-Path -Path $DestinationPath
                    if ($suppliedDestinationPath.Provider.Name -ne "FileSystem")
                    {
                        $errorMessage = ($LocalizedData.ExpandArchiveInValidDestinationPath -f $DestinationPath)
                        ThrowTerminatingErrorHelper "InvalidDirectoryPath" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidArgument) $DestinationPath
                    }
                }
                else
                {
                    $createdItem = New-Item -Path $DestinationPath -ItemType Directory -Confirm:$isConfirm -Verbose:$isVerbose -ErrorAction Stop
                    if ($createdItem -ne $null -and $createdItem.PSProvider.Name -ne "FileSystem")
                    {
                        Remove-Item "$DestinationPath" -Force -Recurse -ErrorAction SilentlyContinue
                        $errorMessage = ($LocalizedData.ExpandArchiveInValidDestinationPath -f $DestinationPath)
                        ThrowTerminatingErrorHelper "InvalidDirectoryPath" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidArgument) $DestinationPath
                    }

                    $resolvedDestinationPath = GetResolvedPathHelper $DestinationPath $true $PSCmdlet
                }
            }

            $isWhatIf = $psboundparameters.ContainsKey("WhatIf")
            if (!$isWhatIf)
            {
                $preparingToExpandVerboseMessage = ($LocalizedData.PreparingToExpandVerboseMessage)
                Write-Verbose $preparingToExpandVerboseMessage

                $progressBarStatus = ($LocalizedData.ExpandProgressBarText -f $DestinationPath)
                ProgressBarHelper "Expand-Archive" $progressBarStatus 0 100 100 1
            }
        }
        PROCESS
        {
            switch ($PsCmdlet.ParameterSetName)
            {
                "Path"
                {
                    $resolvedSourcePaths = GetResolvedPathHelper $Path $false $PSCmdlet

                    if ($resolvedSourcePaths.Count -gt 1)
                    {
                        $errorMessage = ($LocalizedData.InvalidArchiveFilePathError -f $Path, $PsCmdlet.ParameterSetName, $PsCmdlet.ParameterSetName)
                        ThrowTerminatingErrorHelper "InvalidArchiveFilePath" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidArgument) $Path
                    }
                }
                "LiteralPath"
                {
                    $resolvedSourcePaths = GetResolvedPathHelper $LiteralPath $true $PSCmdlet

                    if ($resolvedSourcePaths.Count -gt 1)
                    {
                        $errorMessage = ($LocalizedData.InvalidArchiveFilePathError -f $LiteralPath, $PsCmdlet.ParameterSetName, $PsCmdlet.ParameterSetName)
                        ThrowTerminatingErrorHelper "InvalidArchiveFilePath" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidArgument) $LiteralPath
                    }
                }
            }

            ValidateArchivePathHelper $resolvedSourcePaths

            if ($pscmdlet.ShouldProcess($resolvedSourcePaths))
            {
                $expandedItems = @()

                try
                {
                    # StopProcessing is not avaliable in Script cmdlets. However the pipleline execution
                    # is terminated when ever 'CTRL + C' is entered by user to terminate the cmdlet execution.
                    # The finally block is executed whenever pipleline is terminated.
                    # $isArchiveFileProcessingComplete variable is used to track if 'CTRL + C' is entered by the
                    # user.
                    $isArchiveFileProcessingComplete = $false

                    # The User has not provided a destination path, hence we use '$pwd\ArchiveFileName' as the directory where the
                    # archive file contents would be expanded. If the path '$pwd\ArchiveFileName' already exists then we use the
                    # Windows default mechanism of appending a counter value at the end of the directory name where the contents
                    # would be expanded.
                    if (!$isDestinationPathProvided)
                    {
                        $archiveFile = New-Object System.IO.FileInfo $resolvedSourcePaths
                        $resolvedDestinationPath = Join-Path -Path $resolvedDestinationPath -ChildPath $archiveFile.BaseName
                        $destinationPathExists = Test-Path -LiteralPath $resolvedDestinationPath -PathType Container

                        if (!$destinationPathExists)
                        {
                            New-Item -Path $resolvedDestinationPath -ItemType Directory -Confirm:$isConfirm -Verbose:$isVerbose -ErrorAction Stop | Out-Null
                        }
                    }

                    ExpandArchiveHelper $resolvedSourcePaths $resolvedDestinationPath ([ref]$expandedItems) $Force $isVerbose $isConfirm

                    $isArchiveFileProcessingComplete = $true
                }
                finally
                {
                    # The $isArchiveFileProcessingComplete would be set to $false if user has typed 'CTRL + C' to
                    # terminate the cmdlet execution or if an unhandled exception is thrown.
                    if ($isArchiveFileProcessingComplete -eq $false)
                    {
                        if ($expandedItems.Count -gt 0)
                        {
                            # delete the expanded file/directory as the archive
                            # file was not completly expanded.
                            $expandedItems | ForEach-Object { Remove-Item $_ -Force -Recurse }
                        }
                    }
                }
            }
        }
    }
}
tools\dbatools\optional\Get-GenericArgumentCompleter.ps1
if (-not (Get-Command -Name Register-ArgumentCompleter -ErrorAction Ignore)) {
    Function Get-GenericArgumentCompleter {
        param (
            [string]$name,
            [object]$collection
        )

        Register-ArgumentCompleter -ParameterName $name -ScriptBlock {
            param (
                $commandName,
                $parameterName,
                $wordToComplete,
                $commandAst,
                $fakeBoundParameter
            )

            if ($collection) {
                foreach ($item in $collection) {
                    New-CompletionResult -CompletionText $item -ToolTip $item
                }
            }
        }
    }
}
tools\dbatools\optional\TabExpansionPlusPlus.ps1
if (-not (Get-Command -Name Register-ArgumentCompleter -ErrorAction Ignore))
{

    #############################################################################
    #
    # TabExpansionPlusPlus
    #
    #

<#
Copyright (c) 2013, Jason Shirk
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#>

    # Save off the previous tab completion so it can be restored if this module
    # is removed.
    $oldTabExpansion = $function:TabExpansion
    $oldTabExpansion2 = $function:TabExpansion2

    [bool]$updatedTypeData = $false


    #region Exported utility functions for completers

    #############################################################################
    #
    # Helper function to create a new completion results
    #
    function New-CompletionResult
    {
        param ([Parameter(Position = 0, ValueFromPipelineByPropertyName, Mandatory, ValueFromPipeline)]
            [ValidateNotNullOrEmpty()]
            [string]
            $CompletionText,

            [Parameter(Position = 1, ValueFromPipelineByPropertyName)]
            [string]
            $ToolTip,

            [Parameter(Position = 2, ValueFromPipelineByPropertyName)]
            [string]
            $ListItemText,

            [System.Management.Automation.CompletionResultType]
            $CompletionResultType = [System.Management.Automation.CompletionResultType]::ParameterValue,

            [Parameter(Mandatory = $false)]
            [switch]
            $NoQuotes = $false
        )

        process
        {
            $toolTipToUse = if ($ToolTip -eq '') { $CompletionText }
            else { $ToolTip }
            $listItemToUse = if ($ListItemText -eq '') { $CompletionText }
            else { $ListItemText }

            # If the caller explicitly requests that quotes
            # not be included, via the -NoQuotes parameter,
            # then skip adding quotes.

            if ($CompletionResultType -eq [System.Management.Automation.CompletionResultType]::ParameterValue -and -not $NoQuotes)
            {
                # Add single quotes for the caller in case they are needed.
                # We use the parser to robustly determine how it will treat
                # the argument.  If we end up with too many tokens, or if
                # the parser found something expandable in the results, we
                # know quotes are needed.

                $tokens = $null
                $null = [System.Management.Automation.Language.Parser]::ParseInput("echo $CompletionText", [ref]$tokens, [ref]$null)
                if ($tokens.Length -ne 3 -or
                    ($tokens[1] -is [System.Management.Automation.Language.StringExpandableToken] -and
                        $tokens[1].Kind -eq [System.Management.Automation.Language.TokenKind]::Generic))
                {
                    $CompletionText = "'$CompletionText'"
                }
            }
            return New-Object System.Management.Automation.CompletionResult `
            ($CompletionText, $listItemToUse, $CompletionResultType, $toolTipToUse.Trim())
        }

    }

    #############################################################################
    #
    # .SYNOPSIS
    #
    #     This is a simple wrapper of Get-Command gets commands with a given
    #     parameter ignoring commands that use the parameter name as an alias.
    #
    function Get-CommandWithParameter
    {
        [CmdletBinding(DefaultParameterSetName = 'AllCommandSet')]
        param (
            [Parameter(ParameterSetName = 'AllCommandSet', Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName)]
            [ValidateNotNullOrEmpty()]
            [string[]]
            ${Name},

            [Parameter(ParameterSetName = 'CmdletSet', ValueFromPipelineByPropertyName)]
            [string[]]
            ${Verb},

            [Parameter(ParameterSetName = 'CmdletSet', ValueFromPipelineByPropertyName)]
            [string[]]
            ${Noun},

            [Parameter(ValueFromPipelineByPropertyName)]
            [string[]]
            ${Module},

            [ValidateNotNullOrEmpty()]
            [Parameter(Mandatory)]
            [string]
            ${ParameterName})

        begin
        {
            $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Get-Command', [System.Management.Automation.CommandTypes]::Cmdlet)
            $scriptCmd = { & $wrappedCmd @PSBoundParameters | Where-Object { $_.Parameters[$ParameterName] -ne $null } }
            $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
            $steppablePipeline.Begin($PSCmdlet)
        }
        process
        {
            $steppablePipeline.Process($_)
        }
        end
        {
            $steppablePipeline.End()
        }
    }

    #############################################################################
    #
    function Set-CompletionPrivateData
    {
        param (
            [ValidateNotNullOrEmpty()]
            [string]
            $Key,

            [object]
            $Value,

            [ValidateNotNullOrEmpty()]
            [int]
            $ExpirationSeconds = 604800
        )

        $Cache = [PSCustomObject]@{
            Value = $Value
            ExpirationTime = (Get-Date).AddSeconds($ExpirationSeconds)
        }
        $completionPrivateData[$key] = $Cache
    }

    #############################################################################
    #
    function Get-CompletionPrivateData
    {
        param (
            [ValidateNotNullOrEmpty()]
            [string]
            $Key)

        if (!$Key)
        { return $completionPrivateData }

        $cacheValue = $completionPrivateData[$key]
        if ((Get-Date) -lt $cacheValue.ExpirationTime)
        {
            return $cacheValue.Value
        }
    }

    #############################################################################
    #
    function Get-CompletionWithExtension
    {
        param ([string]
            $lastWord,

            [string[]]
            $extensions)

        [System.Management.Automation.CompletionCompleters]::CompleteFilename($lastWord) |
        Where-Object {
            # Use ListItemText because it won't be quoted, CompletionText might be
            [System.IO.Path]::GetExtension($_.ListItemText) -in $extensions
        }
    }

    #############################################################################
    #
    function New-CommandTree
    {
        [CmdletBinding(DefaultParameterSetName = 'Default')]
        param (
            [Parameter(Position = 0, Mandatory, ParameterSetName = 'Default')]
            [Parameter(Position = 0, Mandatory, ParameterSetName = 'Argument')]
            [ValidateNotNullOrEmpty()]
            [string]
            $Completion,

            [Parameter(Position = 1, Mandatory, ParameterSetName = 'Default')]
            [Parameter(Position = 1, Mandatory, ParameterSetName = 'Argument')]
            [string]
            $Tooltip,

            [Parameter(ParameterSetName = 'Argument')]
            [switch]
            $Argument,

            [Parameter(Position = 2, ParameterSetName = 'Default')]
            [Parameter(Position = 1, ParameterSetName = 'ScriptBlockSet')]
            [scriptblock]
            $SubCommands,

            [Parameter(Position = 0, Mandatory, ParameterSetName = 'ScriptBlockSet')]
            [scriptblock]
            $CompletionGenerator
        )

        $actualSubCommands = $null
        if ($null -ne $SubCommands)
        {
            $actualSubCommands = [NativeCommandTreeNode[]](& $SubCommands)
        }

        switch ($PSCmdlet.ParameterSetName)
        {
            'Default' {
                New-Object NativeCommandTreeNode $Completion, $Tooltip, $actualSubCommands
                break
            }
            'Argument' {
                New-Object NativeCommandTreeNode $Completion, $Tooltip, $true
            }
            'ScriptBlockSet' {
                New-Object NativeCommandTreeNode $CompletionGenerator, $actualSubCommands
                break
            }
        }
    }

    #############################################################################
    #
    function Get-CommandTreeCompletion
    {
        param ($wordToComplete,

            $commandAst,

            [NativeCommandTreeNode[]]
            $CommandTree)

        $commandElements = $commandAst.CommandElements

        # Skip the first command element - it's the command name
        # Iterate through the remaining elements, stopping early
        # if we find the element that matches $wordToComplete.
        for ($i = 1; $i -lt $commandElements.Count; $i++)
        {
            if (!($commandElements[$i] -is [System.Management.Automation.Language.StringConstantExpressionAst]))
            {
                # Ignore arguments that are expressions.  In some rare cases this
                # could cause strange completions because the context is incorrect, e.g.:
                #    $c = 'advfirewall'
                #    netsh $c firewall
                # Here we would be in advfirewall firewall context, but we'd complete as
                # though we were in firewall context.
                continue
            }

            if ($commandElements[$i].Value -eq $wordToComplete)
            {
                $CommandTree = $CommandTree |
                Where-Object { $_.Command -like "$wordToComplete*" -or $_.CompletionGenerator -ne $null }
                break
            }

            foreach ($subCommand in $CommandTree)
            {
                if ($subCommand.Command -eq $commandElements[$i].Value)
                {
                    if (!$subCommand.Argument)
                    {
                        $CommandTree = $subCommand.SubCommands
                    }
                    break
                }
            }
        }

        if ($null -ne $CommandTree)
        {
            $CommandTree | ForEach-Object {
                if ($_.Command)
                {
                    $toolTip = if ($_.Tooltip) { $_.Tooltip }
                    else { $_.Command }
                    New-CompletionResult -CompletionText $_.Command -ToolTip $toolTip
                }
                else
                {
                    & $_.CompletionGenerator $wordToComplete $commandAst
                }
            }
        }
    }

    #endregion Exported utility functions for completers

    #region Exported functions

    #############################################################################
    #
    # .SYNOPSIS
    #     Register a ScriptBlock to perform argument completion for a
    #     given command or parameter.
    #
    # .DESCRIPTION
    #     Argument completion can be extended without needing to do any
    #     parsing in many cases. By registering a handler for specific
    #     commands and/or parameters, PowerShell will call the handler
    #     when appropriate.
    #
    #     There are 2 kinds of extensions - native and PowerShell. Native
    #     refers to commands external to PowerShell, e.g. net.exe. PowerShell
    #     completion covers any functions, scripts, or cmdlets where PowerShell
    #     can determine the correct parameter being completed.
    #
    #     When registering a native handler, you must specify the CommandName
    #     parameter. The CommandName is typically specified without any path
    #     or extension. If specifying a path and/or an extension, completion
    #     will only work when the command is specified that way when requesting
    #     completion.
    #
    #     When registering a PowerShell handler, you must specify the
    #     ParameterName parameter. The CommandName is optional - PowerShell will
    #     first try to find a handler based on the command and parameter, but
    #     if none is found, then it will try just the parameter name. This way,
    #     you could specify a handler for all commands that have a specific
    #     parameter.
    #
    #     A handler needs to return instances of
    #     System.Management.Automation.CompletionResult.
    #
    #     A native handler is passed 2 parameters:
    #
    #         param($wordToComplete, $commandAst)
    #
    #     $wordToComplete  - The argument being completed, possibly an empty string
    #     $commandAst      - The ast of the command being completed.
    #
    #     A PowerShell handler is passed 5 parameters:
    #
    #         param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)
    #
    #     $commandName        - The command name
    #     $parameterName      - The parameter name
    #     $wordToComplete     - The argument being completed, possibly an empty string
    #     $commandAst         - The parsed representation of the command being completed.
    #     $fakeBoundParameter - Like $PSBoundParameters, contains values for some of the parameters.
    #                           Certain values are not included, this does not mean a parameter was
    #                           not specified, just that getting the value could have had unintended
    #                           side effects, so no value was computed.
    #
    # .PARAMETER ParameterName
    #     The name of the parameter that the Completion parameter supports.
    #     This parameter is not supported for native completion and is
    #     mandatory for script completion.
    #
    # .PARAMETER CommandName
    #     The name of the command that the Completion parameter supports.
    #     This parameter is mandatory for native completion and is optional
    #     for script completion.
    #
    # .PARAMETER Completion
    #     A ScriptBlock that returns instances of CompletionResult. For
    #     native completion, the script block parameters are
    #
    #         param($wordToComplete, $commandAst)
    #
    #     For script completion, the parameters are:
    #
    #         param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)
    #
    # .PARAMETER Description
    #     A description of how the completion can be used.
    #
    function Register-ArgumentCompleter
    {
        [CmdletBinding(DefaultParameterSetName = "PowerShellSet")]
        param (
            [Parameter(ParameterSetName = "NativeSet", Mandatory)]
            [Parameter(ParameterSetName = "PowerShellSet")]
            [string[]]
            $CommandName = "",

            [Parameter(ParameterSetName = "PowerShellSet", Mandatory)]
            [string]
            $ParameterName = "",

            [Parameter(Mandatory)]
            [scriptblock]
            $ScriptBlock,

            [string]
            $Description,

            [Parameter(ParameterSetName = "NativeSet")]
            [switch]
            $Native)

        $fnDefn = $ScriptBlock.Ast -as [System.Management.Automation.Language.FunctionDefinitionAst]
        if (!$Description)
        {
            # See if the script block is really a function, if so, use the function name.
            $Description = if ($fnDefn -ne $null) { $fnDefn.Name }
            else { "" }
        }

        if ($MyInvocation.ScriptName -ne (& { $MyInvocation.ScriptName }))
        {
            # Make an unbound copy of the script block so it has access to TabExpansionPlusPlus when invoked.
            # We can skip this step if we created the script block (Register-ArgumentCompleter was
            # called internally).
            if ($fnDefn -ne $null)
            {
                $ScriptBlock = $ScriptBlock.Ast.Body.GetScriptBlock() # Don't reparse, just get a new ScriptBlock.
            }
            else
            {
                $ScriptBlock = $ScriptBlock.Ast.GetScriptBlock() # Don't reparse, just get a new ScriptBlock.
            }
        }

        foreach ($command in $CommandName)
        {
            if ($command -and $ParameterName)
            {
                $command += ":"
            }

            $key = if ($Native) { 'NativeArgumentCompleters' }
            else { 'CustomArgumentCompleters' }
            $tabExpansionOptions[$key]["${command}${ParameterName}"] = $ScriptBlock

            $tabExpansionDescriptions["${command}${ParameterName}$Native"] = $Description
        }
    }

    #############################################################################
    #
    # .SYNOPSIS
    #     Tests the registered argument completer
    #
    # .DESCRIPTION
    #     Invokes the registered parameteter completer for a specified command to make it easier to test
    #     a completer
    #
    # .EXAMPLE
    #  Test-ArgumentCompleter -CommandName Get-Verb -ParameterName Verb -WordToComplete Sta
    #
    # Test what would be completed if Get-Verb -Verb Sta<Tab> was typed at the prompt
    #
    # .EXAMPLE
    #  Test-ArgumentCompleter -NativeCommand Robocopy -WordToComplete /
    #
    # Test what would be completed if Robocopy /<Tab> was typed at the prompt
    #
    function Test-ArgumentCompleter
    {
        [CmdletBinding(DefaultParametersetName = 'PS')]
        param
        (
            [Parameter(Mandatory, Position = 1, ParameterSetName = 'PS')]
            [string]
            $CommandName
             ,

            [Parameter(Mandatory, Position = 2, ParameterSetName = 'PS')]
            [string]
            $ParameterName
             ,

            [Parameter(ParameterSetName = 'PS')]
            [System.Management.Automation.Language.CommandAst]
            $commandAst
             ,

            [Parameter(ParameterSetName = 'PS')]
            [Hashtable]
            $FakeBoundParameters = @{ }
             ,

            [Parameter(Mandatory, Position = 1, ParameterSetName = 'NativeCommand')]
            [string]
            $NativeCommand
             ,

            [Parameter(Position = 2, ParameterSetName = 'NativeCommand')]
            [Parameter(Position = 3, ParameterSetName = 'PS')]
            [string]
            $WordToComplete = ''

        )

        if ($PSCmdlet.ParameterSetName -eq 'NativeCommand')
        {
            $Tokens = $null
            $Errors = $null
            $ast = [System.Management.Automation.Language.Parser]::ParseInput($NativeCommand, [ref]$Tokens, [ref]$Errors)
            $commandAst = $ast.EndBlock.Statements[0].PipelineElements[0]
            $command = $commandAst.GetCommandName()
            $completer = $tabExpansionOptions.NativeArgumentCompleters[$command]
            if (-not $Completer)
            {
                throw "No argument completer registered for command '$Command' (from $NativeCommand)"
            }
            & $completer $WordToComplete $commandAst
        }
        else
        {
            $completer = $tabExpansionOptions.CustomArgumentCompleters["${CommandName}:$ParameterName"]
            if (-not $Completer)
            {
                throw "No argument completer registered for '${CommandName}:$ParameterName'"
            }
            & $completer $CommandName $ParameterName $WordToComplete $commandAst $FakeBoundParameters
        }
    }

    #############################################################################
    #
    # .SYNOPSIS
    # Retrieves a list of argument completers that have been loaded into the
    # PowerShell session.
    #
    # .PARAMETER Name
    # The name of the argument complete to retrieve. This parameter supports
    # wildcards (asterisk).
    #
    # .EXAMPLE
    # Get-ArgumentCompleter -Name *Azure*;
    function Get-ArgumentCompleter
    {
        [CmdletBinding()]
        param ([string[]]
            $Name = '*')

        if (!$updatedTypeData)
        {
            # Define the default display properties for the objects returned by Get-ArgumentCompleter
            [string[]]$properties = "Command", "Parameter"
            Update-TypeData -TypeName 'TabExpansionPlusPlus.ArgumentCompleter' -DefaultDisplayPropertySet $properties -Force
            $updatedTypeData = $true
        }

        function WriteCompleters
        {
            function WriteCompleter($command, $parameter, $native, $scriptblock)
            {
                foreach ($n in $Name)
                {
                    if ($command -like $n)
                    {
                        $c = $command
                        if ($command -and $parameter) { $c += ':' }
                        $description = $tabExpansionDescriptions["${c}${parameter}${native}"]
                        $completer = [pscustomobject]@{
                            Command = $command
                            Parameter = $parameter
                            Native = $native
                            Description = $description
                            ScriptBlock = $scriptblock
                            File = if ($scriptblock.File) { Split-Path -Leaf -Path $scriptblock.File }
                        }

                        $completer.PSTypeNames.Add('TabExpansionPlusPlus.ArgumentCompleter')
                        Write-Output $completer

                        break
                    }
                }
            }

            foreach ($pair in $tabExpansionOptions.CustomArgumentCompleters.GetEnumerator())
            {
                if ($pair.Key -match '^(.*):(.*)$')
                {
                    $command = $matches[1]
                    $parameter = $matches[2]
                }
                else
                {
                    $parameter = $pair.Key
                    $command = ""
                }

                WriteCompleter $command $parameter $false $pair.Value
            }

            foreach ($pair in $tabExpansionOptions.NativeArgumentCompleters.GetEnumerator())
            {
                WriteCompleter $pair.Key '' $true $pair.Value
            }
        }

        WriteCompleters | Sort-Object -Property Native, Command, Parameter
    }

    #############################################################################
    #
    # .SYNOPSIS
    #     Register a ScriptBlock to perform argument completion for a
    #     given command or parameter.
    #
    # .DESCRIPTION
    #
    # .PARAMETER Option
    #
    #     The name of the option.
    #
    # .PARAMETER Value
    #
    #     The value to set for Option. Typically this will be $true.
    #
    function Set-TabExpansionOption
    {
        param (
            [ValidateSet('ExcludeHiddenFiles',
                        'RelativePaths',
                        'LiteralPaths',
                        'IgnoreHiddenShares',
                        'AppendBackslash')]
            [string]
            $Option,

            [object]
            $Value = $true)

        $tabExpansionOptions[$option] = $value
    }

    #endregion Exported functions

    #region Internal utility functions

    #############################################################################
    #
    # This function checks if an attribute argument's name can be completed.
    # For example:
    #     [Parameter(<TAB>
    #     [Parameter(Po<TAB>
    #     [CmdletBinding(DefaultPa<TAB>
    #
    function TryAttributeArgumentCompletion
    {
        param (
            [System.Management.Automation.Language.Ast]
            $ast,

            [int]
            $offset
        )

        $results = @()
        $matchIndex = -1

        try
        {
            # We want to find any NamedAttributeArgumentAst objects where the Ast extent includes $offset
            $offsetInExtentPredicate = {
                param ($ast)
                return $offset -gt $ast.Extent.StartOffset -and
                $offset -le $ast.Extent.EndOffset
            }
            $asts = $ast.FindAll($offsetInExtentPredicate, $true)

            $attributeType = $null
            $attributeArgumentName = ""
            $replacementIndex = $offset
            $replacementLength = 0

            $attributeArg = $asts | Where-Object { $_ -is [System.Management.Automation.Language.NamedAttributeArgumentAst] } | Select-Object -First 1
            if ($null -ne $attributeArg)
            {
                $attributeAst = [System.Management.Automation.Language.AttributeAst]$attributeArg.Parent
                $attributeType = $attributeAst.TypeName.GetReflectionAttributeType()
                $attributeArgumentName = $attributeArg.ArgumentName
                $replacementIndex = $attributeArg.Extent.StartOffset
                $replacementLength = $attributeArg.ArgumentName.Length
            }
            else
            {
                $attributeAst = $asts | Where-Object { $_ -is [System.Management.Automation.Language.AttributeAst] } | Select-Object -First 1
                if ($null -ne $attributeAst)
                {
                    $attributeType = $attributeAst.TypeName.GetReflectionAttributeType()
                }
            }

            if ($null -ne $attributeType)
            {
                $results = $attributeType.GetProperties('Public,Instance') |
                Where-Object {
                    # Ignore TypeId (all attributes inherit it)
                    $_.Name -like "$attributeArgumentName*" -and $_.Name -ne 'TypeId'
                } |
                Sort-Object -Property Name |
                ForEach-Object {
                    $propType = [Microsoft.PowerShell.ToStringCodeMethods]::Type($_.PropertyType)
                    $propName = $_.Name
                    New-CompletionResult $propName -ToolTip "$propType $propName" -CompletionResultType Property
                }

                return [PSCustomObject]@{
                    Results = $results
                    ReplacementIndex = $replacementIndex
                    ReplacementLength = $replacementLength
                }
            }
        }
        catch { }
    }

    #############################################################################
    #
    # This function completes native commands options starting with - or --
    # works around a bug in PowerShell that causes it to not complete
    # native command options starting with - or --
    #
    function TryNativeCommandOptionCompletion
    {
        param (
            [System.Management.Automation.Language.Ast]
            $ast,

            [int]
            $offset
        )

        $results = @()
        $replacementIndex = $offset
        $replacementLength = 0
        try
        {
            # We want to find any Command element objects where the Ast extent includes $offset
            $offsetInOptionExtentPredicate = {
                param ($ast)
                return $offset -gt $ast.Extent.StartOffset -and
                $offset -le $ast.Extent.EndOffset -and
                $ast.Extent.Text.StartsWith('-')
            }
            $option = $ast.Find($offsetInOptionExtentPredicate, $true)
            if ($option -ne $null)
            {
                $command = $option.Parent -as [System.Management.Automation.Language.CommandAst]
                if ($command -ne $null)
                {
                    $nativeCommand = [System.IO.Path]::GetFileNameWithoutExtension($command.CommandElements[0].Value)
                    $nativeCompleter = $tabExpansionOptions.NativeArgumentCompleters[$nativeCommand]

                    if ($nativeCompleter)
                    {
                        $results = @(& $nativeCompleter $option.ToString() $command)
                        if ($results.Count -gt 0)
                        {
                            $replacementIndex = $option.Extent.StartOffset
                            $replacementLength = $option.Extent.Text.Length
                        }
                    }
                }
            }
        }
        catch { }

        return [PSCustomObject]@{
            Results = $results
            ReplacementIndex = $replacementIndex
            ReplacementLength = $replacementLength
        }
    }


    #endregion Internal utility functions

    #############################################################################
    #
    # This function is partly a copy of the V3 TabExpansion2, adding a few
    # capabilities such as completing attribute arguments and excluding hidden
    # files from results.
    #
    function global:TabExpansion2
    {
        [CmdletBinding(DefaultParameterSetName = 'ScriptInputSet')]
        Param (
            [Parameter(ParameterSetName = 'ScriptInputSet', Mandatory, Position = 0)]
            [string]
            $inputScript,

            [Parameter(ParameterSetName = 'ScriptInputSet', Mandatory, Position = 1)]
            [int]
            $cursorColumn,

            [Parameter(ParameterSetName = 'AstInputSet', Mandatory, Position = 0)]
            [System.Management.Automation.Language.Ast]
            $ast,

            [Parameter(ParameterSetName = 'AstInputSet', Mandatory, Position = 1)]
            [System.Management.Automation.Language.Token[]]
            $tokens,

            [Parameter(ParameterSetName = 'AstInputSet', Mandatory, Position = 2)]
            [System.Management.Automation.Language.IScriptPosition]
            $positionOfCursor,

            [Parameter(ParameterSetName = 'ScriptInputSet', Position = 2)]
            [Parameter(ParameterSetName = 'AstInputSet', Position = 3)]
            [Hashtable]
            $options = $null
        )

        if ($null -ne $options)
        {
            $options += $tabExpansionOptions
        }
        else
        {
            $options = $tabExpansionOptions
        }

        if ($psCmdlet.ParameterSetName -eq 'ScriptInputSet')
        {
            $results = [System.Management.Automation.CommandCompletion]::CompleteInput(
            <#inputScript#>                $inputScript,
            <#cursorColumn#>                $cursorColumn,
            <#options#>                $options)
        }
        else
        {
            $results = [System.Management.Automation.CommandCompletion]::CompleteInput(
            <#ast#>                $ast,
            <#tokens#>                $tokens,
            <#positionOfCursor#>                $positionOfCursor,
            <#options#>                $options)
        }

        if ($results.CompletionMatches.Count -eq 0)
        {
            # Built-in didn't succeed, try our own completions here.
            if ($psCmdlet.ParameterSetName -eq 'ScriptInputSet')
            {
                $ast = [System.Management.Automation.Language.Parser]::ParseInput($inputScript, [ref]$tokens, [ref]$null)
            }
            else
            {
                $cursorColumn = $positionOfCursor.Offset
            }

            # workaround PowerShell bug that case it to not invoking native completers for - or --
            # making it hard to complete options for many commands
            $nativeCommandResults = TryNativeCommandOptionCompletion -ast $ast -offset $cursorColumn
            if ($null -ne $nativeCommandResults)
            {
                $results.ReplacementIndex = $nativeCommandResults.ReplacementIndex
                $results.ReplacementLength = $nativeCommandResults.ReplacementLength
                if ($results.CompletionMatches.IsReadOnly)
                {
                    # Workaround where PowerShell returns a readonly collection that we need to add to.
                    $collection = new-object System.Collections.ObjectModel.Collection[System.Management.Automation.CompletionResult]
                    $results.GetType().GetProperty('CompletionMatches').SetValue($results, $collection)
                }
                $nativeCommandResults.Results | ForEach-Object {
                    $results.CompletionMatches.Add($_)
                }
            }

            $attributeResults = TryAttributeArgumentCompletion $ast $cursorColumn
            if ($null -ne $attributeResults)
            {
                $results.ReplacementIndex = $attributeResults.ReplacementIndex
                $results.ReplacementLength = $attributeResults.ReplacementLength
                if ($results.CompletionMatches.IsReadOnly)
                {
                    # Workaround where PowerShell returns a readonly collection that we need to add to.
                    $collection = new-object System.Collections.ObjectModel.Collection[System.Management.Automation.CompletionResult]
                    $results.GetType().GetProperty('CompletionMatches').SetValue($results, $collection)
                }
                $attributeResults.Results | ForEach-Object {
                    $results.CompletionMatches.Add($_)
                }
            }
        }

        if ($options.ExcludeHiddenFiles)
        {
            foreach ($result in @($results.CompletionMatches))
            {
                if ($result.ResultType -eq [System.Management.Automation.CompletionResultType]::ProviderItem -or
                    $result.ResultType -eq [System.Management.Automation.CompletionResultType]::ProviderContainer)
                {
                    try
                    {
                        $item = Get-Item -LiteralPath $result.CompletionText -ErrorAction Stop
                    }
                    catch
                    {
                        # If Get-Item w/o -Force fails, it is probably hidden, so exclude the result
                        $null = $results.CompletionMatches.Remove($result)
                    }
                }
            }
        }
        if ($options.AppendBackslash -and
            $results.CompletionMatches.ResultType -contains [System.Management.Automation.CompletionResultType]::ProviderContainer)
        {
            foreach ($result in @($results.CompletionMatches))
            {
                if ($result.ResultType -eq [System.Management.Automation.CompletionResultType]::ProviderContainer)
                {
                    $completionText = $result.CompletionText
                    $lastChar = $completionText[-1]
                    $lastIsQuote = ($lastChar -eq '"' -or $lastChar -eq "'")
                    if ($lastIsQuote)
                    {
                        $lastChar = $completionText[-2]
                    }

                    if ($lastChar -ne '\')
                    {
                        $null = $results.CompletionMatches.Remove($result)

                        if ($lastIsQuote)
                        {
                            $completionText =
                            $completionText.Substring(0, $completionText.Length - 1) +
                            '\' + $completionText[-1]
                        }
                        else
                        {
                            $completionText = $completionText + '\'
                        }

                        $updatedResult = New-Object System.Management.Automation.CompletionResult `
                        ($completionText, $result.ListItemText, $result.ResultType, $result.ToolTip)
                        $results.CompletionMatches.Add($updatedResult)
                    }
                }
            }
        }

        if ($results.CompletionMatches.Count -eq 0)
        {
            # No results, if this module has overridden another TabExpansion2 function, call it
            # but only if it's not the built-in function (which we assume if function isn't
            # defined in a file.
            if ($oldTabExpansion2 -ne $null -and $oldTabExpansion2.File -ne $null)
            {
                return (& $oldTabExpansion2 @PSBoundParameters)
            }
        }

        return $results
    }


    #############################################################################
    #
    # Main
    #

    Add-Type @"
using System;
using System.Management.Automation;

public class NativeCommandTreeNode
{
    private NativeCommandTreeNode(NativeCommandTreeNode[] subCommands)
    {
        SubCommands = subCommands;
    }

    public NativeCommandTreeNode(string command, NativeCommandTreeNode[] subCommands)
        : this(command, null, subCommands)
    {
    }

    public NativeCommandTreeNode(string command, string tooltip, NativeCommandTreeNode[] subCommands)
        : this(subCommands)
    {
        this.Command = command;
        this.Tooltip = tooltip;
    }

    public NativeCommandTreeNode(string command, string tooltip, bool argument)
        : this(null)
    {
        this.Command = command;
        this.Tooltip = tooltip;
        this.Argument = true;
    }

    public NativeCommandTreeNode(ScriptBlock completionGenerator, NativeCommandTreeNode[] subCommands)
        : this(subCommands)
    {
        this.CompletionGenerator = completionGenerator;
    }

    public string Command { get; private set; }
    public string Tooltip { get; private set; }
    public bool Argument { get; private set; }
    public ScriptBlock CompletionGenerator { get; private set; }
    public NativeCommandTreeNode[] SubCommands { get; private set; }
}
"@

    # Custom completions are saved in this hashtable
    $tabExpansionOptions = @{
        CustomArgumentCompleters = @{ }
        NativeArgumentCompleters = @{ }
    }
    # Descriptions for the above completions saved in this hashtable
    $tabExpansionDescriptions = @{ }
    # And private data for the above completions cached in this hashtable
    $completionPrivateData = @{ }
}
tools\dbatools\PSGetModuleInfo.xml
<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
  <Obj RefId="0">
    <TN RefId="0">
      <T>Microsoft.PowerShell.Commands.PSRepositoryItemInfo</T>
      <T>System.Management.Automation.PSCustomObject</T>
      <T>System.Object</T>
    </TN>
    <MS>
      <S N="Name">dbatools</S>
      <Version N="Version">0.9.380</Version>
      <S N="Type">Module</S>
      <S N="Description">The community module that enables SQL Server Pros to automate database development and server administration</S>
      <S N="Author">Chrissy LeMaire</S>
      <Obj N="CompanyName" RefId="1">
        <TN RefId="1">
          <T>System.Object[]</T>
          <T>System.Array</T>
          <T>System.Object</T>
        </TN>
        <LST>
          <S>chrissylemaire</S>
          <S>FWN</S>
          <S>SQLDBAWithABeard</S>
          <S>wsmelton</S>
        </LST>
      </Obj>
      <S N="Copyright">2018 Chrissy LeMaire</S>
      <DT N="PublishedDate">2018-07-23T19:59:10+00:00</DT>
      <Nil N="InstalledDate" />
      <Nil N="UpdatedDate" />
      <URI N="LicenseUri">https://opensource.org/licenses/MIT</URI>
      <URI N="ProjectUri">https://dbatools.io/</URI>
      <URI N="IconUri">https://dbatools.io/logo.png</URI>
      <Obj N="Tags" RefId="2">
        <TNRef RefId="1" />
        <LST>
          <S>sqlserver</S>
          <S>migrations</S>
          <S>sql</S>
          <S>dba</S>
          <S>databases</S>
          <S>PSModule</S>
        </LST>
      </Obj>
      <Obj N="Includes" RefId="3">
        <TN RefId="2">
          <T>System.Collections.Hashtable</T>
          <T>System.Object</T>
        </TN>
        <DCT>
          <En>
            <S N="Key">Function</S>
            <Obj N="Value" RefId="4">
              <TNRef RefId="1" />
              <LST>
                <S>Start-DbaMigration</S>
                <S>Copy-DbaDatabase</S>
                <S>Copy-DbaLogin</S>
                <S>Copy-DbaSqlServerAgent</S>
                <S>Copy-DbaSpConfigure</S>
                <S>Copy-DbaLinkedServer</S>
                <S>Copy-DbaDatabaseMail</S>
                <S>Copy-DbaDatabaseAssembly</S>
                <S>Copy-DbaSqlPolicyManagement</S>
                <S>Copy-DbaAgentSharedSchedule</S>
                <S>Copy-DbaAgentOperator</S>
                <S>Copy-DbaAgentJob</S>
                <S>Copy-DbaSqlDataCollector</S>
                <S>Copy-DbaCustomError</S>
                <S>Copy-DbaServerAuditSpecification</S>
                <S>Copy-DbaEndpoint</S>
                <S>Copy-DbaServerAudit</S>
                <S>Copy-DbaServerRole</S>
                <S>Copy-DbaResourceGovernor</S>
                <S>Copy-DbaExtendedEvent</S>
                <S>Copy-DbaBackupDevice</S>
                <S>Copy-DbaServerTrigger</S>
                <S>Copy-DbaCredential</S>
                <S>Copy-DbaCentralManagementServer</S>
                <S>Copy-DbaSysDbUserObject</S>
                <S>Copy-DbaAgentProxyAccount</S>
                <S>Copy-DbaAgentAlert</S>
                <S>Import-DbaSpConfigure</S>
                <S>Export-DbaSpConfigure</S>
                <S>Get-DbaDetachedDatabaseInfo</S>
                <S>Restore-DbaBackupFromDirectory</S>
                <S>Test-DbaConnection</S>
                <S>Import-DbaCsvToSql</S>
                <S>Copy-DbaAgentCategory</S>
                <S>Update-Dbatools</S>
                <S>Test-DbaSqlPath</S>
                <S>Export-DbaLogin</S>
                <S>Reset-DbaAdmin</S>
                <S>Watch-DbaDbLogin</S>
                <S>Expand-DbaTLogResponsibly</S>
                <S>Test-DbaMigrationConstraint</S>
                <S>Get-DbaRegisteredServer</S>
                <S>Test-DbaNetworkLatency</S>
                <S>Find-DbaDuplicateIndex</S>
                <S>Show-DbaServerFileSystem</S>
                <S>Get-DbaDiskSpace</S>
                <S>Remove-DbaDatabaseSafely</S>
                <S>Show-DbaDatabaseList</S>
                <S>Set-DbaTempDbConfiguration</S>
                <S>Test-DbaTempDbConfiguration</S>
                <S>Repair-DbaOrphanUser</S>
                <S>Remove-DbaOrphanUser</S>
                <S>Find-DbaUnusedIndex</S>
                <S>Test-DbaDiskAllocation</S>
                <S>Test-DbaPowerPlan</S>
                <S>Set-DbaPowerPlan</S>
                <S>Test-DbaDiskAlignment</S>
                <S>Get-DbaDatabaseSpace</S>
                <S>Get-DbaClusterNode</S>
                <S>Test-DbaDatabaseOwner</S>
                <S>Set-DbaDatabaseOwner</S>
                <S>Test-DbaJobOwner</S>
                <S>Set-DbaJobOwner</S>
                <S>Test-DbaDbVirtualLogFile</S>
                <S>Get-DbaRestoreHistory</S>
                <S>Get-DbaTcpPort</S>
                <S>Test-DbaDatabaseCompatibility</S>
                <S>Test-DbaDatabaseCollation</S>
                <S>Test-DbaConnectionAuthScheme</S>
                <S>Test-DbaServerName</S>
                <S>Repair-DbaServerName</S>
                <S>Stop-DbaProcess</S>
                <S>Copy-DbaSsisCatalog</S>
                <S>Find-DbaOrphanedFile</S>
                <S>Get-DbaAvailabilityGroup</S>
                <S>Get-DbaLastGoodCheckDb</S>
                <S>Get-DbaProcess</S>
                <S>Get-DbaRunningJob</S>
                <S>Set-DbaMaxDop</S>
                <S>Test-DbaRecoveryModel</S>
                <S>Test-DbaMaxDop</S>
                <S>Remove-DbaBackup</S>
                <S>Get-DbaPermission</S>
                <S>Get-DbaLastBackup</S>
                <S>Connect-DbaInstance</S>
                <S>Get-DbaStartupParameter</S>
                <S>Get-DbaBackupHistory</S>
                <S>Read-DbaBackupHeader</S>
                <S>Test-DbaLastBackup</S>
                <S>Get-DbaMaxMemory</S>
                <S>Set-DbaMaxMemory</S>
                <S>Test-DbaMaxMemory</S>
                <S>Get-DbaDbSnapshot</S>
                <S>Remove-DbaDbSnapshot</S>
                <S>Get-DbaRoleMember</S>
                <S>Resolve-DbaNetworkName</S>
                <S>Test-DbaWindowsLogin</S>
                <S>Get-DbaMemoryUsage</S>
                <S>Export-DbaAvailabilityGroup</S>
                <S>Write-DbaDataTable</S>
                <S>New-DbaDbSnapshot</S>
                <S>Restore-DbaDbSnapshot</S>
                <S>Get-DbaTrigger</S>
                <S>Export-DbaUser</S>
                <S>Get-DbaDatabaseState</S>
                <S>Set-DbaDatabaseState</S>
                <S>Get-DbaHelpIndex</S>
                <S>Get-DbaAgentAlert</S>
                <S>Get-DbaAgentOperator</S>
                <S>Get-DbaPageFileSetting</S>
                <S>Get-DbaSpConfigure</S>
                <S>Rename-DbaLogin</S>
                <S>Find-DbaAgentJob</S>
                <S>Find-DbaDatabase</S>
                <S>Get-DbaMsdtc</S>
                <S>Get-DbaUptime</S>
                <S>Get-DbaXESession</S>
                <S>Test-DbaOptimizeForAdHoc</S>
                <S>Find-DbaStoredProcedure</S>
                <S>Measure-DbaBackupThroughput</S>
                <S>Find-DbaLoginInGroup</S>
                <S>Get-DbaSpn</S>
                <S>Test-DbaSpn</S>
                <S>Set-DbaSpn</S>
                <S>Remove-DbaSpn</S>
                <S>Get-DbaDatabase</S>
                <S>Find-DbaUserObject</S>
                <S>Get-DbaSqlService</S>
                <S>Get-DbaDependency</S>
                <S>Clear-DbaSqlConnectionPool</S>
                <S>Find-DbaCommand</S>
                <S>Get-DbaConfig</S>
                <S>Get-DbaConfigValue</S>
                <S>Set-DbaConfig</S>
                <S>Get-DbaClientProtocol</S>
                <S>Backup-DbaDatabase</S>
                <S>New-DbaSqlDirectory</S>
                <S>Get-DbaPrivilege</S>
                <S>Install-DbaWatchUpdate</S>
                <S>Watch-DbaUpdate</S>
                <S>Uninstall-DbaWatchUpdate</S>
                <S>Get-DbaDbQueryStoreOptions</S>
                <S>Set-DbaDbQueryStoreOptions</S>
                <S>Restore-DbaDatabase</S>
                <S>Copy-DbaQueryStoreConfig</S>
                <S>Get-DbaExecutionPlan</S>
                <S>Export-DbaExecutionPlan</S>
                <S>Get-DbaServerProtocol</S>
                <S>Get-DbaLocaleSetting</S>
                <S>Get-DbaSqlBuildReference</S>
                <S>Set-DbaSpConfigure</S>
                <S>Test-DbaIdentityUsage</S>
                <S>Get-DbaDatabaseAssembly</S>
                <S>Get-DbaAgentJob</S>
                <S>Get-DbaCustomError</S>
                <S>Get-DbaCredential</S>
                <S>Get-DbaBackupDevice</S>
                <S>Get-DbaAgentProxy</S>
                <S>Get-DbaDatabaseEncryption</S>
                <S>New-DbaSsisCatalog</S>
                <S>Remove-DbaDatabase</S>
                <S>Get-DbaQueryExecutionTime</S>
                <S>Get-DbaTempdbUsage</S>
                <S>Find-DbaDbGrowthEvent</S>
                <S>Get-DbaNetworkActivity</S>
                <S>Get-DbaAgentJobOutputFile</S>
                <S>Set-DbaAgentJobOutputFile</S>
                <S>Test-DbaLinkedServerConnection</S>
                <S>Get-DbaDatabaseFile</S>
                <S>Read-DbaTransactionLog</S>
                <S>Get-DbaTable</S>
                <S>Invoke-DbaDatabaseShrink</S>
                <S>Get-DbaEstimatedCompletionTime</S>
                <S>Get-DbaLinkedServer</S>
                <S>Set-DbaStartupParameter</S>
                <S>New-DbaAgentJob</S>
                <S>Export-DbaScript</S>
                <S>Get-DbaLogin</S>
                <S>New-DbaScriptingOption</S>
                <S>Save-DbaDiagnosticQueryScript</S>
                <S>Invoke-DbaDiagnosticQuery</S>
                <S>Export-DbaDiagnosticQuery</S>
                <S>Invoke-DbaWhoIsActive</S>
                <S>Install-DbaWhoIsActive</S>
                <S>Set-DbaAgentJob</S>
                <S>Remove-DbaAgentJob</S>
                <S>New-DbaAgentJobStep</S>
                <S>Set-DbaAgentJobStep</S>
                <S>Remove-DbaAgentJobStep</S>
                <S>New-DbaAgentSchedule</S>
                <S>Set-DbaAgentSchedule</S>
                <S>Remove-DbaAgentSchedule</S>
                <S>Backup-DbaDbCertificate</S>
                <S>Get-DbaDbCertificate</S>
                <S>Get-DbaCmConnection</S>
                <S>Get-DbaCmObject</S>
                <S>Get-DbaEndpoint</S>
                <S>Get-DbaDatabaseMasterKey</S>
                <S>Get-DbaSchemaChangeHistory</S>
                <S>Get-DbaServerAudit</S>
                <S>Get-DbaServerAuditSpecification</S>
                <S>Get-DbaSqlProductKey</S>
                <S>Get-DbatoolsLog</S>
                <S>Restore-DbaDbCertificate</S>
                <S>New-DbaDbCertificate</S>
                <S>New-DbaCmConnection</S>
                <S>New-DbaDatabaseMasterKey</S>
                <S>New-DbaServiceMasterKey</S>
                <S>New-DbatoolsSupportPackage</S>
                <S>Remove-DbaDbCertificate</S>
                <S>Remove-DbaCmConnection</S>
                <S>Remove-DbaDatabaseMasterKey</S>
                <S>Set-DbaCmConnection</S>
                <S>Set-DbaTcpPort</S>
                <S>Test-DbaCmConnection</S>
                <S>New-DbaSqlConnectionStringBuilder</S>
                <S>Get-DbaSqlInstanceProperty</S>
                <S>Get-DbaSqlInstanceUserOption</S>
                <S>New-DbaSqlConnectionString</S>
                <S>Get-DbaAgentSchedule</S>
                <S>Invoke-DbaLogShipping</S>
                <S>Read-DbaTraceFile</S>
                <S>New-DbaComputerCertificate</S>
                <S>Get-DbaComputerCertificate</S>
                <S>Add-DbaComputerCertificate</S>
                <S>Get-DbaNetworkCertificate</S>
                <S>Set-DbaNetworkCertificate</S>
                <S>Remove-DbaNetworkCertificate</S>
                <S>Enable-DbaForceNetworkEncryption</S>
                <S>Disable-DbaForceNetworkEncryption</S>
                <S>Get-DbaForceNetworkEncryption</S>
                <S>Remove-DbaComputerCertificate</S>
                <S>Get-DbaServerInstallDate</S>
                <S>Install-DbaFirstResponderKit</S>
                <S>Backup-DbaDatabaseMasterKey</S>
                <S>Get-DbaAgentJobHistory</S>
                <S>Get-DbaSsisEnvironmentVariable</S>
                <S>Get-DbaSqlManagementObject</S>
                <S>Test-DbaSqlManagementObject</S>
                <S>Get-DbaMaintenanceSolutionLog</S>
                <S>Invoke-DbaLogShippingRecovery</S>
                <S>Find-DbaTrigger</S>
                <S>Find-DbaView</S>
                <S>Invoke-DbaDatabaseUpgrade</S>
                <S>Get-DbaDatabaseUser</S>
                <S>Get-DbaWindowsLog</S>
                <S>Get-DbaErrorLog</S>
                <S>Get-DbaAgentLog</S>
                <S>Get-DbaDbMailLog</S>
                <S>Get-DbaDbMailHistory</S>
                <S>Get-DbaDatabaseView</S>
                <S>Get-DbaDatabaseUdf</S>
                <S>Get-DbaDatabasePartitionFunction</S>
                <S>Get-DbaDatabasePartitionScheme</S>
                <S>Get-DbaDefaultPath</S>
                <S>Get-DbaDbStoredProcedure</S>
                <S>Test-DbaDbCompression</S>
                <S>Mount-DbaDatabase</S>
                <S>Dismount-DbaDatabase</S>
                <S>Set-DbaPrivilege</S>
                <S>Get-DbaAgReplica</S>
                <S>Get-DbaAgDatabase</S>
                <S>Get-DbaSqlModule</S>
                <S>Get-DbaRegisteredServerStore</S>
                <S>Sync-DbaLoginPermission</S>
                <S>Invoke-Sqlcmd2</S>
                <S>New-DbaCredential</S>
                <S>Get-DbaFile</S>
                <S>Set-DbaDbCompression</S>
                <S>New-DbaClientAlias</S>
                <S>Get-DbaClientAlias</S>
                <S>Get-DbaOperatingSystem</S>
                <S>Install-DbaMaintenanceSolution</S>
                <S>Get-DbaComputerSystem</S>
                <S>Get-DbaTraceFlag</S>
                <S>Stop-DbaSqlService</S>
                <S>Start-DbaSqlService</S>
                <S>Restart-DbaSqlService</S>
                <S>Invoke-DbaCycleErrorLog</S>
                <S>Get-DbaSqlRegistryRoot</S>
                <S>Get-DbaAvailableCollation</S>
                <S>Get-DbaUserLevelPermission</S>
                <S>Get-DbaAgHadr</S>
                <S>Get-DbaPolicy</S>
                <S>Find-DbaSimilarTable</S>
                <S>Disable-DbaAgHadr</S>
                <S>Enable-DbaAgHadr</S>
                <S>Get-DbaTrace</S>
                <S>Get-DbaSuspectPage</S>
                <S>Get-DbaWaitStatistic</S>
                <S>Clear-DbaWaitStatistics</S>
                <S>Get-DbaTopResourceUsage</S>
                <S>New-DbaLogin</S>
                <S>Get-DbaAgListener</S>
                <S>Invoke-DbaDatabaseClone</S>
                <S>Read-DbaXEFile</S>
                <S>Get-DbaDistributor</S>
                <S>Update-DbaSqlServiceAccount</S>
                <S>Watch-DbaXESession</S>
                <S>Disable-DbaTraceFlag</S>
                <S>Enable-DbaTraceFlag</S>
                <S>Start-DbaAgentJob</S>
                <S>Stop-DbaAgentJob</S>
                <S>Remove-DbaClientAlias</S>
                <S>New-DbaAgentProxy</S>
                <S>Test-DbaLogShippingStatus</S>
                <S>Get-DbaXESessionTarget</S>
                <S>New-DbaXESmartTargetResponse</S>
                <S>New-DbaXESmartTarget</S>
                <S>Get-DbaDbVirtualLogFile</S>
                <S>Register-DbaConfig</S>
                <S>Get-DbaBackupInformation</S>
                <S>Start-DbaXESession</S>
                <S>Stop-DbaXESession</S>
                <S>Set-DbaDbRecoveryModel</S>
                <S>Get-DbaDbRecoveryModel</S>
                <S>Get-DbaWaitingTask</S>
                <S>Remove-DbaDbUser</S>
                <S>Get-DbaDump</S>
                <S>Invoke-DbaAdvancedRestore</S>
                <S>Format-DbaBackupInformation</S>
                <S>Get-DbaAgentJobStep</S>
                <S>Test-DbaBackupInformation</S>
                <S>Invoke-DbaBalanceDataFiles</S>
                <S>Select-DbaBackupInformation</S>
                <S>Rename-DbaDatabase</S>
                <S>New-DbaPublishProfile</S>
                <S>Publish-DbaDacpac</S>
                <S>Export-DbaDacpac</S>
                <S>Copy-DbaTableData</S>
                <S>Invoke-DbaSqlQuery</S>
                <S>Remove-DbaLogin</S>
                <S>Get-DbaFileStream</S>
                <S>Set-DbaFileStream</S>
                <S>Get-DbaAgentJobCategory</S>
                <S>New-DbaAgentJobCategory</S>
                <S>Remove-DbaAgentJobCategory</S>
                <S>Set-DbaAgentJobCategory</S>
                <S>Get-DbaDbRole</S>
                <S>Get-DbaServerRole</S>
                <S>Find-DbaBackup</S>
                <S>Get-DbaCpuUsage</S>
                <S>Remove-DbaXESession</S>
                <S>New-DbaXESession</S>
                <S>Import-DbaXESessionTemplate</S>
                <S>Get-DbaXEStore</S>
                <S>Export-DbaXESessionTemplate</S>
                <S>New-DbaXESmartTableWriter</S>
                <S>New-DbaXESmartReplay</S>
                <S>New-DbaXESmartEmail</S>
                <S>New-DbaXESmartQueryExec</S>
                <S>Start-DbaXESmartTarget</S>
                <S>Get-DbaOrphanUser</S>
                <S>Get-DbaOpenTransaction</S>
                <S>Get-DbaLogShippingError</S>
                <S>Test-DbaSqlBuild</S>
                <S>Get-DbaXESessionTemplate</S>
                <S>ConvertTo-DbaXESession</S>
                <S>Start-DbaTrace</S>
                <S>Stop-DbaTrace</S>
                <S>Remove-DbaTrace</S>
                <S>Set-DbaLogin</S>
                <S>Copy-DbaXESessionTemplate</S>
                <S>Get-DbaXEObject</S>
                <S>ConvertTo-DbaDataTable</S>
                <S>Find-DbaDisabledIndex</S>
                <S>Invoke-DbaPfRelog</S>
                <S>Get-DbaPfDataCollectorCounter</S>
                <S>Get-DbaPfDataCollectorCounterSample</S>
                <S>Get-DbaPfDataCollector</S>
                <S>Get-DbaPfDataCollectorSet</S>
                <S>Start-DbaPfDataCollectorSet</S>
                <S>Stop-DbaPfDataCollectorSet</S>
                <S>Export-DbaPfDataCollectorSetTemplate</S>
                <S>Get-DbaPfDataCollectorSetTemplate</S>
                <S>Import-DbaPfDataCollectorSetTemplate</S>
                <S>Remove-DbaPfDataCollectorSet</S>
                <S>Add-DbaPfDataCollectorCounter</S>
                <S>Remove-DbaPfDataCollectorCounter</S>
                <S>Get-DbaPfAvailableCounter</S>
                <S>Get-DbaXESmartTarget</S>
                <S>Remove-DbaXESmartTarget</S>
                <S>Stop-DbaXESmartTarget</S>
                <S>Get-DbaRegisteredServerGroup</S>
                <S>New-DbaDbUser</S>
                <S>Measure-DbaDiskSpaceRequirement</S>
                <S>New-DbaXESmartCsvWriter</S>
                <S>Export-DbaXECsv</S>
                <S>Invoke-DbaXeReplay</S>
                <S>Find-DbaInstance</S>
                <S>Test-DbaDiskSpeed</S>
                <S>Get-DbaDbExtentDiff</S>
                <S>Read-DbaAuditFile</S>
                <S>Get-DbaDbCompression</S>
                <S>Invoke-DbaDbDecryptObject</S>
                <S>Get-DbaDbForeignKey</S>
                <S>Get-DbaDbCheckConstraint</S>
                <S>Set-DbaAgentAlert</S>
                <S>Get-DbaSqlFeature</S>
                <S>Get-DbaWaitResource</S>
                <S>Get-DbaDbPageInfo</S>
                <S>Get-DbaConnection</S>
                <S>Test-DbaLoginPassword</S>
                <S>Get-DbaResourceGovernorClassifierFunction</S>
                <S>Get-DbaErrorLogConfig</S>
                <S>Set-DbaErrorLogConfig</S>
                <S>Select-DbaObject</S>
                <S>Add-DbaRegisteredServer</S>
                <S>Add-DbaRegisteredServerGroup</S>
                <S>Export-DbaRegisteredServer</S>
                <S>Import-DbaRegisteredServer</S>
                <S>Move-DbaRegisteredServer</S>
                <S>Move-DbaRegisteredServerGroup</S>
                <S>Remove-DbaRegisteredServer</S>
                <S>Remove-DbaRegisteredServerGroup</S>
                <S>Get-DbaPlanCache</S>
                <S>Clear-DbaPlanCache</S>
                <S>Get-DbaSsisExecutionHistory</S>
              </LST>
            </Obj>
          </En>
          <En>
            <S N="Key">RoleCapability</S>
            <Obj N="Value" RefId="5">
              <TNRef RefId="1" />
              <LST />
            </Obj>
          </En>
          <En>
            <S N="Key">Command</S>
            <Obj N="Value" RefId="6">
              <TNRef RefId="1" />
              <LST>
                <S>Start-DbaMigration</S>
                <S>Copy-DbaDatabase</S>
                <S>Copy-DbaLogin</S>
                <S>Copy-DbaSqlServerAgent</S>
                <S>Copy-DbaSpConfigure</S>
                <S>Copy-DbaLinkedServer</S>
                <S>Copy-DbaDatabaseMail</S>
                <S>Copy-DbaDatabaseAssembly</S>
                <S>Copy-DbaSqlPolicyManagement</S>
                <S>Copy-DbaAgentSharedSchedule</S>
                <S>Copy-DbaAgentOperator</S>
                <S>Copy-DbaAgentJob</S>
                <S>Copy-DbaSqlDataCollector</S>
                <S>Copy-DbaCustomError</S>
                <S>Copy-DbaServerAuditSpecification</S>
                <S>Copy-DbaEndpoint</S>
                <S>Copy-DbaServerAudit</S>
                <S>Copy-DbaServerRole</S>
                <S>Copy-DbaResourceGovernor</S>
                <S>Copy-DbaExtendedEvent</S>
                <S>Copy-DbaBackupDevice</S>
                <S>Copy-DbaServerTrigger</S>
                <S>Copy-DbaCredential</S>
                <S>Copy-DbaCentralManagementServer</S>
                <S>Copy-DbaSysDbUserObject</S>
                <S>Copy-DbaAgentProxyAccount</S>
                <S>Copy-DbaAgentAlert</S>
                <S>Import-DbaSpConfigure</S>
                <S>Export-DbaSpConfigure</S>
                <S>Get-DbaDetachedDatabaseInfo</S>
                <S>Restore-DbaBackupFromDirectory</S>
                <S>Test-DbaConnection</S>
                <S>Import-DbaCsvToSql</S>
                <S>Copy-DbaAgentCategory</S>
                <S>Update-Dbatools</S>
                <S>Test-DbaSqlPath</S>
                <S>Export-DbaLogin</S>
                <S>Reset-DbaAdmin</S>
                <S>Watch-DbaDbLogin</S>
                <S>Expand-DbaTLogResponsibly</S>
                <S>Test-DbaMigrationConstraint</S>
                <S>Get-DbaRegisteredServer</S>
                <S>Test-DbaNetworkLatency</S>
                <S>Find-DbaDuplicateIndex</S>
                <S>Show-DbaServerFileSystem</S>
                <S>Get-DbaDiskSpace</S>
                <S>Remove-DbaDatabaseSafely</S>
                <S>Show-DbaDatabaseList</S>
                <S>Set-DbaTempDbConfiguration</S>
                <S>Test-DbaTempDbConfiguration</S>
                <S>Repair-DbaOrphanUser</S>
                <S>Remove-DbaOrphanUser</S>
                <S>Find-DbaUnusedIndex</S>
                <S>Test-DbaDiskAllocation</S>
                <S>Test-DbaPowerPlan</S>
                <S>Set-DbaPowerPlan</S>
                <S>Test-DbaDiskAlignment</S>
                <S>Get-DbaDatabaseSpace</S>
                <S>Get-DbaClusterNode</S>
                <S>Test-DbaDatabaseOwner</S>
                <S>Set-DbaDatabaseOwner</S>
                <S>Test-DbaJobOwner</S>
                <S>Set-DbaJobOwner</S>
                <S>Test-DbaDbVirtualLogFile</S>
                <S>Get-DbaRestoreHistory</S>
                <S>Get-DbaTcpPort</S>
                <S>Test-DbaDatabaseCompatibility</S>
                <S>Test-DbaDatabaseCollation</S>
                <S>Test-DbaConnectionAuthScheme</S>
                <S>Test-DbaServerName</S>
                <S>Repair-DbaServerName</S>
                <S>Stop-DbaProcess</S>
                <S>Copy-DbaSsisCatalog</S>
                <S>Find-DbaOrphanedFile</S>
                <S>Get-DbaAvailabilityGroup</S>
                <S>Get-DbaLastGoodCheckDb</S>
                <S>Get-DbaProcess</S>
                <S>Get-DbaRunningJob</S>
                <S>Set-DbaMaxDop</S>
                <S>Test-DbaRecoveryModel</S>
                <S>Test-DbaMaxDop</S>
                <S>Remove-DbaBackup</S>
                <S>Get-DbaPermission</S>
                <S>Get-DbaLastBackup</S>
                <S>Connect-DbaInstance</S>
                <S>Get-DbaStartupParameter</S>
                <S>Get-DbaBackupHistory</S>
                <S>Read-DbaBackupHeader</S>
                <S>Test-DbaLastBackup</S>
                <S>Get-DbaMaxMemory</S>
                <S>Set-DbaMaxMemory</S>
                <S>Test-DbaMaxMemory</S>
                <S>Get-DbaDbSnapshot</S>
                <S>Remove-DbaDbSnapshot</S>
                <S>Get-DbaRoleMember</S>
                <S>Resolve-DbaNetworkName</S>
                <S>Test-DbaWindowsLogin</S>
                <S>Get-DbaMemoryUsage</S>
                <S>Export-DbaAvailabilityGroup</S>
                <S>Write-DbaDataTable</S>
                <S>New-DbaDbSnapshot</S>
                <S>Restore-DbaDbSnapshot</S>
                <S>Get-DbaTrigger</S>
                <S>Export-DbaUser</S>
                <S>Get-DbaDatabaseState</S>
                <S>Set-DbaDatabaseState</S>
                <S>Get-DbaHelpIndex</S>
                <S>Get-DbaAgentAlert</S>
                <S>Get-DbaAgentOperator</S>
                <S>Get-DbaPageFileSetting</S>
                <S>Get-DbaSpConfigure</S>
                <S>Rename-DbaLogin</S>
                <S>Find-DbaAgentJob</S>
                <S>Find-DbaDatabase</S>
                <S>Get-DbaMsdtc</S>
                <S>Get-DbaUptime</S>
                <S>Get-DbaXESession</S>
                <S>Test-DbaOptimizeForAdHoc</S>
                <S>Find-DbaStoredProcedure</S>
                <S>Measure-DbaBackupThroughput</S>
                <S>Find-DbaLoginInGroup</S>
                <S>Get-DbaSpn</S>
                <S>Test-DbaSpn</S>
                <S>Set-DbaSpn</S>
                <S>Remove-DbaSpn</S>
                <S>Get-DbaDatabase</S>
                <S>Find-DbaUserObject</S>
                <S>Get-DbaSqlService</S>
                <S>Get-DbaDependency</S>
                <S>Clear-DbaSqlConnectionPool</S>
                <S>Find-DbaCommand</S>
                <S>Get-DbaConfig</S>
                <S>Get-DbaConfigValue</S>
                <S>Set-DbaConfig</S>
                <S>Get-DbaClientProtocol</S>
                <S>Backup-DbaDatabase</S>
                <S>New-DbaSqlDirectory</S>
                <S>Get-DbaPrivilege</S>
                <S>Install-DbaWatchUpdate</S>
                <S>Watch-DbaUpdate</S>
                <S>Uninstall-DbaWatchUpdate</S>
                <S>Get-DbaDbQueryStoreOptions</S>
                <S>Set-DbaDbQueryStoreOptions</S>
                <S>Restore-DbaDatabase</S>
                <S>Copy-DbaQueryStoreConfig</S>
                <S>Get-DbaExecutionPlan</S>
                <S>Export-DbaExecutionPlan</S>
                <S>Get-DbaServerProtocol</S>
                <S>Get-DbaLocaleSetting</S>
                <S>Get-DbaSqlBuildReference</S>
                <S>Set-DbaSpConfigure</S>
                <S>Test-DbaIdentityUsage</S>
                <S>Get-DbaDatabaseAssembly</S>
                <S>Get-DbaAgentJob</S>
                <S>Get-DbaCustomError</S>
                <S>Get-DbaCredential</S>
                <S>Get-DbaBackupDevice</S>
                <S>Get-DbaAgentProxy</S>
                <S>Get-DbaDatabaseEncryption</S>
                <S>New-DbaSsisCatalog</S>
                <S>Remove-DbaDatabase</S>
                <S>Get-DbaQueryExecutionTime</S>
                <S>Get-DbaTempdbUsage</S>
                <S>Find-DbaDbGrowthEvent</S>
                <S>Get-DbaNetworkActivity</S>
                <S>Get-DbaAgentJobOutputFile</S>
                <S>Set-DbaAgentJobOutputFile</S>
                <S>Test-DbaLinkedServerConnection</S>
                <S>Get-DbaDatabaseFile</S>
                <S>Read-DbaTransactionLog</S>
                <S>Get-DbaTable</S>
                <S>Invoke-DbaDatabaseShrink</S>
                <S>Get-DbaEstimatedCompletionTime</S>
                <S>Get-DbaLinkedServer</S>
                <S>Set-DbaStartupParameter</S>
                <S>New-DbaAgentJob</S>
                <S>Export-DbaScript</S>
                <S>Get-DbaLogin</S>
                <S>New-DbaScriptingOption</S>
                <S>Save-DbaDiagnosticQueryScript</S>
                <S>Invoke-DbaDiagnosticQuery</S>
                <S>Export-DbaDiagnosticQuery</S>
                <S>Invoke-DbaWhoIsActive</S>
                <S>Install-DbaWhoIsActive</S>
                <S>Set-DbaAgentJob</S>
                <S>Remove-DbaAgentJob</S>
                <S>New-DbaAgentJobStep</S>
                <S>Set-DbaAgentJobStep</S>
                <S>Remove-DbaAgentJobStep</S>
                <S>New-DbaAgentSchedule</S>
                <S>Set-DbaAgentSchedule</S>
                <S>Remove-DbaAgentSchedule</S>
                <S>Backup-DbaDbCertificate</S>
                <S>Get-DbaDbCertificate</S>
                <S>Get-DbaCmConnection</S>
                <S>Get-DbaCmObject</S>
                <S>Get-DbaEndpoint</S>
                <S>Get-DbaDatabaseMasterKey</S>
                <S>Get-DbaSchemaChangeHistory</S>
                <S>Get-DbaServerAudit</S>
                <S>Get-DbaServerAuditSpecification</S>
                <S>Get-DbaSqlProductKey</S>
                <S>Get-DbatoolsLog</S>
                <S>Restore-DbaDbCertificate</S>
                <S>New-DbaDbCertificate</S>
                <S>New-DbaCmConnection</S>
                <S>New-DbaDatabaseMasterKey</S>
                <S>New-DbaServiceMasterKey</S>
                <S>New-DbatoolsSupportPackage</S>
                <S>Remove-DbaDbCertificate</S>
                <S>Remove-DbaCmConnection</S>
                <S>Remove-DbaDatabaseMasterKey</S>
                <S>Set-DbaCmConnection</S>
                <S>Set-DbaTcpPort</S>
                <S>Test-DbaCmConnection</S>
                <S>New-DbaSqlConnectionStringBuilder</S>
                <S>Get-DbaSqlInstanceProperty</S>
                <S>Get-DbaSqlInstanceUserOption</S>
                <S>New-DbaSqlConnectionString</S>
                <S>Get-DbaAgentSchedule</S>
                <S>Invoke-DbaLogShipping</S>
                <S>Read-DbaTraceFile</S>
                <S>New-DbaComputerCertificate</S>
                <S>Get-DbaComputerCertificate</S>
                <S>Add-DbaComputerCertificate</S>
                <S>Get-DbaNetworkCertificate</S>
                <S>Set-DbaNetworkCertificate</S>
                <S>Remove-DbaNetworkCertificate</S>
                <S>Enable-DbaForceNetworkEncryption</S>
                <S>Disable-DbaForceNetworkEncryption</S>
                <S>Get-DbaForceNetworkEncryption</S>
                <S>Remove-DbaComputerCertificate</S>
                <S>Get-DbaServerInstallDate</S>
                <S>Install-DbaFirstResponderKit</S>
                <S>Backup-DbaDatabaseMasterKey</S>
                <S>Get-DbaAgentJobHistory</S>
                <S>Get-DbaSsisEnvironmentVariable</S>
                <S>Get-DbaSqlManagementObject</S>
                <S>Test-DbaSqlManagementObject</S>
                <S>Get-DbaMaintenanceSolutionLog</S>
                <S>Invoke-DbaLogShippingRecovery</S>
                <S>Find-DbaTrigger</S>
                <S>Find-DbaView</S>
                <S>Invoke-DbaDatabaseUpgrade</S>
                <S>Get-DbaDatabaseUser</S>
                <S>Get-DbaWindowsLog</S>
                <S>Get-DbaErrorLog</S>
                <S>Get-DbaAgentLog</S>
                <S>Get-DbaDbMailLog</S>
                <S>Get-DbaDbMailHistory</S>
                <S>Get-DbaDatabaseView</S>
                <S>Get-DbaDatabaseUdf</S>
                <S>Get-DbaDatabasePartitionFunction</S>
                <S>Get-DbaDatabasePartitionScheme</S>
                <S>Get-DbaDefaultPath</S>
                <S>Get-DbaDbStoredProcedure</S>
                <S>Test-DbaDbCompression</S>
                <S>Mount-DbaDatabase</S>
                <S>Dismount-DbaDatabase</S>
                <S>Set-DbaPrivilege</S>
                <S>Get-DbaAgReplica</S>
                <S>Get-DbaAgDatabase</S>
                <S>Get-DbaSqlModule</S>
                <S>Get-DbaRegisteredServerStore</S>
                <S>Sync-DbaLoginPermission</S>
                <S>Invoke-Sqlcmd2</S>
                <S>New-DbaCredential</S>
                <S>Get-DbaFile</S>
                <S>Set-DbaDbCompression</S>
                <S>New-DbaClientAlias</S>
                <S>Get-DbaClientAlias</S>
                <S>Get-DbaOperatingSystem</S>
                <S>Install-DbaMaintenanceSolution</S>
                <S>Get-DbaComputerSystem</S>
                <S>Get-DbaTraceFlag</S>
                <S>Stop-DbaSqlService</S>
                <S>Start-DbaSqlService</S>
                <S>Restart-DbaSqlService</S>
                <S>Invoke-DbaCycleErrorLog</S>
                <S>Get-DbaSqlRegistryRoot</S>
                <S>Get-DbaAvailableCollation</S>
                <S>Get-DbaUserLevelPermission</S>
                <S>Get-DbaAgHadr</S>
                <S>Get-DbaPolicy</S>
                <S>Find-DbaSimilarTable</S>
                <S>Disable-DbaAgHadr</S>
                <S>Enable-DbaAgHadr</S>
                <S>Get-DbaTrace</S>
                <S>Get-DbaSuspectPage</S>
                <S>Get-DbaWaitStatistic</S>
                <S>Clear-DbaWaitStatistics</S>
                <S>Get-DbaTopResourceUsage</S>
                <S>New-DbaLogin</S>
                <S>Get-DbaAgListener</S>
                <S>Invoke-DbaDatabaseClone</S>
                <S>Read-DbaXEFile</S>
                <S>Get-DbaDistributor</S>
                <S>Update-DbaSqlServiceAccount</S>
                <S>Watch-DbaXESession</S>
                <S>Disable-DbaTraceFlag</S>
                <S>Enable-DbaTraceFlag</S>
                <S>Start-DbaAgentJob</S>
                <S>Stop-DbaAgentJob</S>
                <S>Remove-DbaClientAlias</S>
                <S>New-DbaAgentProxy</S>
                <S>Test-DbaLogShippingStatus</S>
                <S>Get-DbaXESessionTarget</S>
                <S>New-DbaXESmartTargetResponse</S>
                <S>New-DbaXESmartTarget</S>
                <S>Get-DbaDbVirtualLogFile</S>
                <S>Register-DbaConfig</S>
                <S>Get-DbaBackupInformation</S>
                <S>Start-DbaXESession</S>
                <S>Stop-DbaXESession</S>
                <S>Set-DbaDbRecoveryModel</S>
                <S>Get-DbaDbRecoveryModel</S>
                <S>Get-DbaWaitingTask</S>
                <S>Remove-DbaDbUser</S>
                <S>Get-DbaDump</S>
                <S>Invoke-DbaAdvancedRestore</S>
                <S>Format-DbaBackupInformation</S>
                <S>Get-DbaAgentJobStep</S>
                <S>Test-DbaBackupInformation</S>
                <S>Invoke-DbaBalanceDataFiles</S>
                <S>Select-DbaBackupInformation</S>
                <S>Rename-DbaDatabase</S>
                <S>New-DbaPublishProfile</S>
                <S>Publish-DbaDacpac</S>
                <S>Export-DbaDacpac</S>
                <S>Copy-DbaTableData</S>
                <S>Invoke-DbaSqlQuery</S>
                <S>Remove-DbaLogin</S>
                <S>Get-DbaFileStream</S>
                <S>Set-DbaFileStream</S>
                <S>Get-DbaAgentJobCategory</S>
                <S>New-DbaAgentJobCategory</S>
                <S>Remove-DbaAgentJobCategory</S>
                <S>Set-DbaAgentJobCategory</S>
                <S>Get-DbaDbRole</S>
                <S>Get-DbaServerRole</S>
                <S>Find-DbaBackup</S>
                <S>Get-DbaCpuUsage</S>
                <S>Remove-DbaXESession</S>
                <S>New-DbaXESession</S>
                <S>Import-DbaXESessionTemplate</S>
                <S>Get-DbaXEStore</S>
                <S>Export-DbaXESessionTemplate</S>
                <S>New-DbaXESmartTableWriter</S>
                <S>New-DbaXESmartReplay</S>
                <S>New-DbaXESmartEmail</S>
                <S>New-DbaXESmartQueryExec</S>
                <S>Start-DbaXESmartTarget</S>
                <S>Get-DbaOrphanUser</S>
                <S>Get-DbaOpenTransaction</S>
                <S>Get-DbaLogShippingError</S>
                <S>Test-DbaSqlBuild</S>
                <S>Get-DbaXESessionTemplate</S>
                <S>ConvertTo-DbaXESession</S>
                <S>Start-DbaTrace</S>
                <S>Stop-DbaTrace</S>
                <S>Remove-DbaTrace</S>
                <S>Set-DbaLogin</S>
                <S>Copy-DbaXESessionTemplate</S>
                <S>Get-DbaXEObject</S>
                <S>ConvertTo-DbaDataTable</S>
                <S>Find-DbaDisabledIndex</S>
                <S>Invoke-DbaPfRelog</S>
                <S>Get-DbaPfDataCollectorCounter</S>
                <S>Get-DbaPfDataCollectorCounterSample</S>
                <S>Get-DbaPfDataCollector</S>
                <S>Get-DbaPfDataCollectorSet</S>
                <S>Start-DbaPfDataCollectorSet</S>
                <S>Stop-DbaPfDataCollectorSet</S>
                <S>Export-DbaPfDataCollectorSetTemplate</S>
                <S>Get-DbaPfDataCollectorSetTemplate</S>
                <S>Import-DbaPfDataCollectorSetTemplate</S>
                <S>Remove-DbaPfDataCollectorSet</S>
                <S>Add-DbaPfDataCollectorCounter</S>
                <S>Remove-DbaPfDataCollectorCounter</S>
                <S>Get-DbaPfAvailableCounter</S>
                <S>Get-DbaXESmartTarget</S>
                <S>Remove-DbaXESmartTarget</S>
                <S>Stop-DbaXESmartTarget</S>
                <S>Get-DbaRegisteredServerGroup</S>
                <S>New-DbaDbUser</S>
                <S>Measure-DbaDiskSpaceRequirement</S>
                <S>New-DbaXESmartCsvWriter</S>
                <S>Export-DbaXECsv</S>
                <S>Invoke-DbaXeReplay</S>
                <S>Find-DbaInstance</S>
                <S>Test-DbaDiskSpeed</S>
                <S>Get-DbaDbExtentDiff</S>
                <S>Read-DbaAuditFile</S>
                <S>Get-DbaDbCompression</S>
                <S>Invoke-DbaDbDecryptObject</S>
                <S>Get-DbaDbForeignKey</S>
                <S>Get-DbaDbCheckConstraint</S>
                <S>Set-DbaAgentAlert</S>
                <S>Get-DbaSqlFeature</S>
                <S>Get-DbaWaitResource</S>
                <S>Get-DbaDbPageInfo</S>
                <S>Get-DbaConnection</S>
                <S>Test-DbaLoginPassword</S>
                <S>Get-DbaResourceGovernorClassifierFunction</S>
                <S>Get-DbaErrorLogConfig</S>
                <S>Set-DbaErrorLogConfig</S>
                <S>Select-DbaObject</S>
                <S>Add-DbaRegisteredServer</S>
                <S>Add-DbaRegisteredServerGroup</S>
                <S>Export-DbaRegisteredServer</S>
                <S>Import-DbaRegisteredServer</S>
                <S>Move-DbaRegisteredServer</S>
                <S>Move-DbaRegisteredServerGroup</S>
                <S>Remove-DbaRegisteredServer</S>
                <S>Remove-DbaRegisteredServerGroup</S>
                <S>Get-DbaPlanCache</S>
                <S>Clear-DbaPlanCache</S>
                <S>Get-DbaSsisExecutionHistory</S>
              </LST>
            </Obj>
          </En>
          <En>
            <S N="Key">DscResource</S>
            <Obj N="Value" RefId="7">
              <TNRef RefId="1" />
              <LST />
            </Obj>
          </En>
          <En>
            <S N="Key">Workflow</S>
            <Obj N="Value" RefId="8">
              <TNRef RefId="1" />
              <LST />
            </Obj>
          </En>
          <En>
            <S N="Key">Cmdlet</S>
            <Obj N="Value" RefId="9">
              <TNRef RefId="1" />
              <LST />
            </Obj>
          </En>
        </DCT>
      </Obj>
      <Nil N="PowerShellGetFormatVersion" />
      <S N="ReleaseNotes">https://dbatools.io/releases</S>
      <Obj N="Dependencies" RefId="10">
        <TNRef RefId="1" />
        <LST />
      </Obj>
      <S N="RepositorySourceLocation">https://www.powershellgallery.com/api/v2/</S>
      <S N="Repository">PSGallery</S>
      <S N="PackageManagementProvider">NuGet</S>
      <Obj N="AdditionalMetadata" RefId="11">
        <TNRef RefId="2" />
        <DCT>
          <En>
            <S N="Key">releaseNotes</S>
            <S N="Value">https://dbatools.io/releases</S>
          </En>
          <En>
            <S N="Key">versionDownloadCount</S>
            <S N="Value">48</S>
          </En>
          <En>
            <S N="Key">ItemType</S>
            <S N="Value">Module</S>
          </En>
          <En>
            <S N="Key">copyright</S>
            <S N="Value">2018 Chrissy LeMaire</S>
          </En>
          <En>
            <S N="Key">CompanyName</S>
            <S N="Value">dbatools.io</S>
          </En>
          <En>
            <S N="Key">tags</S>
            <S N="Value">sqlserver migrations sql dba databases PSModule PSFunction_Start-DbaMigration PSCommand_Start-DbaMigration PSFunction_Copy-DbaDatabase PSCommand_Copy-DbaDatabase PSFunction_Copy-DbaLogin PSCommand_Copy-DbaLogin PSFunction_Copy-DbaSqlServerAgent PSCommand_Copy-DbaSqlServerAgent PSFunction_Copy-DbaSpConfigure PSCommand_Copy-DbaSpConfigure PSFunction_Copy-DbaLinkedServer PSCommand_Copy-DbaLinkedServer PSFunction_Copy-DbaDatabaseMail PSCommand_Copy-DbaDatabaseMail PSFunction_Copy-DbaDatabaseAssembly PSCommand_Copy-DbaDatabaseAssembly PSFunction_Copy-DbaSqlPolicyManagement PSCommand_Copy-DbaSqlPolicyManagement PSFunction_Copy-DbaAgentSharedSchedule PSCommand_Copy-DbaAgentSharedSchedule PSFunction_Copy-DbaAgentOperator PSCommand_Copy-DbaAgentOperator PSFunction_Copy-DbaAgentJob PSCommand_Copy-DbaAgentJob PSFunction_Copy-DbaSqlDataCollector PSCommand_Copy-DbaSqlDataCollector PSFunction_Copy-DbaCustomError PSCommand_Copy-DbaCustomError PSFunction_Copy-DbaServerAuditSpecification PSCommand_Copy-DbaServerAuditSpecification PSFunction_Copy-DbaEndpoint PSCommand_Copy-DbaEndpoint PSFunction_Copy-DbaServerAudit PSCommand_Copy-DbaServerAudit PSFunction_Copy-DbaServerRole PSCommand_Copy-DbaServerRole PSFunction_Copy-DbaResourceGovernor PSCommand_Copy-DbaResourceGovernor PSFunction_Copy-DbaExtendedEvent PSCommand_Copy-DbaExtendedEvent PSFunction_Copy-DbaBackupDevice PSCommand_Copy-DbaBackupDevice PSFunction_Copy-DbaServerTrigger PSCommand_Copy-DbaServerTrigger PSFunction_Copy-DbaCredential PSCommand_Copy-DbaCredential PSFunction_Copy-DbaCentralManagementServer PSCommand_Copy-DbaCentralManagementServer PSFunction_Copy-DbaSysDbUserObject PSCommand_Copy-DbaSysDbUserObject PSFunction_Copy-DbaAgentProxyAccount PSCommand_Copy-DbaAgentProxyAccount PSFunction_Copy-DbaAgentAlert PSCommand_Copy-DbaAgentAlert PSFunction_Import-DbaSpConfigure PSCommand_Import-DbaSpConfigure PSFunction_Export-DbaSpConfigure PSCommand_Export-DbaSpConfigure PSFunction_Get-DbaDetachedDatabaseInfo PSCommand_Get-DbaDetachedDatabaseInfo PSFunction_Restore-DbaBackupFromDirectory PSCommand_Restore-DbaBackupFromDirectory PSFunction_Test-DbaConnection PSCommand_Test-DbaConnection PSFunction_Import-DbaCsvToSql PSCommand_Import-DbaCsvToSql PSFunction_Copy-DbaAgentCategory PSCommand_Copy-DbaAgentCategory PSFunction_Update-Dbatools PSCommand_Update-Dbatools PSFunction_Test-DbaSqlPath PSCommand_Test-DbaSqlPath PSFunction_Export-DbaLogin PSCommand_Export-DbaLogin PSFunction_Reset-DbaAdmin PSCommand_Reset-DbaAdmin PSFunction_Watch-DbaDbLogin PSCommand_Watch-DbaDbLogin PSFunction_Expand-DbaTLogResponsibly PSCommand_Expand-DbaTLogResponsibly PSFunction_Test-DbaMigrationConstraint PSCommand_Test-DbaMigrationConstraint PSFunction_Get-DbaRegisteredServer PSCommand_Get-DbaRegisteredServer PSFunction_Test-DbaNetworkLatency PSCommand_Test-DbaNetworkLatency PSFunction_Find-DbaDuplicateIndex PSCommand_Find-DbaDuplicateIndex PSFunction_Show-DbaServerFileSystem PSCommand_Show-DbaServerFileSystem PSFunction_Get-DbaDiskSpace PSCommand_Get-DbaDiskSpace PSFunction_Remove-DbaDatabaseSafely PSCommand_Remove-DbaDatabaseSafely PSFunction_Show-DbaDatabaseList PSCommand_Show-DbaDatabaseList PSFunction_Set-DbaTempDbConfiguration PSCommand_Set-DbaTempDbConfiguration PSFunction_Test-DbaTempDbConfiguration PSCommand_Test-DbaTempDbConfiguration PSFunction_Repair-DbaOrphanUser PSCommand_Repair-DbaOrphanUser PSFunction_Remove-DbaOrphanUser PSCommand_Remove-DbaOrphanUser PSFunction_Find-DbaUnusedIndex PSCommand_Find-DbaUnusedIndex PSFunction_Test-DbaDiskAllocation PSCommand_Test-DbaDiskAllocation PSFunction_Test-DbaPowerPlan PSCommand_Test-DbaPowerPlan PSFunction_Set-DbaPowerPlan PSCommand_Set-DbaPowerPlan PSFunction_Test-DbaDiskAlignment PSCommand_Test-DbaDiskAlignment PSFunction_Get-DbaDatabaseSpace PSCommand_Get-DbaDatabaseSpace PSFunction_Get-DbaClusterNode PSCommand_Get-DbaClusterNode PSFunction_Test-DbaDatabaseOwner PSCommand_Test-DbaDatabaseOwner PSFunction_Set-DbaDatabaseOwner PSCommand_Set-DbaDatabaseOwner PSFunction_Test-DbaJobOwner PSCommand_Test-DbaJobOwner PSFunction_Set-DbaJobOwner PSCommand_Set-DbaJobOwner PSFunction_Test-DbaDbVirtualLogFile PSCommand_Test-DbaDbVirtualLogFile PSFunction_Get-DbaRestoreHistory PSCommand_Get-DbaRestoreHistory PSFunction_Get-DbaTcpPort PSCommand_Get-DbaTcpPort PSFunction_Test-DbaDatabaseCompatibility PSCommand_Test-DbaDatabaseCompatibility PSFunction_Test-DbaDatabaseCollation PSCommand_Test-DbaDatabaseCollation PSFunction_Test-DbaConnectionAuthScheme PSCommand_Test-DbaConnectionAuthScheme PSFunction_Test-DbaServerName PSCommand_Test-DbaServerName PSFunction_Repair-DbaServerName PSCommand_Repair-DbaServerName PSFunction_Stop-DbaProcess PSCommand_Stop-DbaProcess PSFunction_Copy-DbaSsisCatalog PSCommand_Copy-DbaSsisCatalog PSFunction_Find-DbaOrphanedFile PSCommand_Find-DbaOrphanedFile PSFunction_Get-DbaAvailabilityGroup PSCommand_Get-DbaAvailabilityGroup PSFunction_Get-DbaLastGoodCheckDb PSCommand_Get-DbaLastGoodCheckDb PSFunction_Get-DbaProcess PSCommand_Get-DbaProcess PSFunction_Get-DbaRunningJob PSCommand_Get-DbaRunningJob PSFunction_Set-DbaMaxDop PSCommand_Set-DbaMaxDop PSFunction_Test-DbaRecoveryModel PSCommand_Test-DbaRecoveryModel PSFunction_Test-DbaMaxDop PSCommand_Test-DbaMaxDop PSFunction_Remove-DbaBackup PSCommand_Remove-DbaBackup PSFunction_Get-DbaPermission PSCommand_Get-DbaPermission PSFunction_Get-DbaLastBackup PSCommand_Get-DbaLastBackup PSFunction_Connect-DbaInstance PSCommand_Connect-DbaInstance PSFunction_Get-DbaStartupParameter PSCommand_Get-DbaStartupParameter PSFunction_Get-DbaBackupHistory PSCommand_Get-DbaBackupHistory PSFunction_Read-DbaBackupHeader PSCommand_Read-DbaBackupHeader PSFunction_Test-DbaLastBackup PSCommand_Test-DbaLastBackup PSFunction_Get-DbaMaxMemory PSCommand_Get-DbaMaxMemory PSFunction_Set-DbaMaxMemory PSCommand_Set-DbaMaxMemory PSFunction_Test-DbaMaxMemory PSCommand_Test-DbaMaxMemory PSFunction_Get-DbaDbSnapshot PSCommand_Get-DbaDbSnapshot PSFunction_Remove-DbaDbSnapshot PSCommand_Remove-DbaDbSnapshot PSFunction_Get-DbaRoleMember PSCommand_Get-DbaRoleMember PSFunction_Resolve-DbaNetworkName PSCommand_Resolve-DbaNetworkName PSFunction_Test-DbaWindowsLogin PSCommand_Test-DbaWindowsLogin PSFunction_Get-DbaMemoryUsage PSCommand_Get-DbaMemoryUsage PSFunction_Export-DbaAvailabilityGroup PSCommand_Export-DbaAvailabilityGroup PSFunction_Write-DbaDataTable PSCommand_Write-DbaDataTable PSFunction_New-DbaDbSnapshot PSCommand_New-DbaDbSnapshot PSFunction_Restore-DbaDbSnapshot PSCommand_Restore-DbaDbSnapshot PSFunction_Get-DbaTrigger PSCommand_Get-DbaTrigger PSFunction_Export-DbaUser PSCommand_Export-DbaUser PSFunction_Get-DbaDatabaseState PSCommand_Get-DbaDatabaseState PSFunction_Set-DbaDatabaseState PSCommand_Set-DbaDatabaseState PSFunction_Get-DbaHelpIndex PSCommand_Get-DbaHelpIndex PSFunction_Get-DbaAgentAlert PSCommand_Get-DbaAgentAlert PSFunction_Get-DbaAgentOperator PSCommand_Get-DbaAgentOperator PSFunction_Get-DbaPageFileSetting PSCommand_Get-DbaPageFileSetting PSFunction_Get-DbaSpConfigure PSCommand_Get-DbaSpConfigure PSFunction_Rename-DbaLogin PSCommand_Rename-DbaLogin PSFunction_Find-DbaAgentJob PSCommand_Find-DbaAgentJob PSFunction_Find-DbaDatabase PSCommand_Find-DbaDatabase PSFunction_Get-DbaMsdtc PSCommand_Get-DbaMsdtc PSFunction_Get-DbaUptime PSCommand_Get-DbaUptime PSFunction_Get-DbaXESession PSCommand_Get-DbaXESession PSFunction_Test-DbaOptimizeForAdHoc PSCommand_Test-DbaOptimizeForAdHoc PSFunction_Find-DbaStoredProcedure PSCommand_Find-DbaStoredProcedure PSFunction_Measure-DbaBackupThroughput PSCommand_Measure-DbaBackupThroughput PSFunction_Find-DbaLoginInGroup PSCommand_Find-DbaLoginInGroup PSFunction_Get-DbaSpn PSCommand_Get-DbaSpn PSFunction_Test-DbaSpn PSCommand_Test-DbaSpn PSFunction_Set-DbaSpn PSCommand_Set-DbaSpn PSFunction_Remove-DbaSpn PSCommand_Remove-DbaSpn PSFunction_Get-DbaDatabase PSCommand_Get-DbaDatabase PSFunction_Find-DbaUserObject PSCommand_Find-DbaUserObject PSFunction_Get-DbaSqlService PSCommand_Get-DbaSqlService PSFunction_Get-DbaDependency PSCommand_Get-DbaDependency PSFunction_Clear-DbaSqlConnectionPool PSCommand_Clear-DbaSqlConnectionPool PSFunction_Find-DbaCommand PSCommand_Find-DbaCommand PSFunction_Get-DbaConfig PSCommand_Get-DbaConfig PSFunction_Get-DbaConfigValue PSCommand_Get-DbaConfigValue PSFunction_Set-DbaConfig PSCommand_Set-DbaConfig PSFunction_Get-DbaClientProtocol PSCommand_Get-DbaClientProtocol PSFunction_Backup-DbaDatabase PSCommand_Backup-DbaDatabase PSFunction_New-DbaSqlDirectory PSCommand_New-DbaSqlDirectory PSFunction_Get-DbaPrivilege PSCommand_Get-DbaPrivilege PSFunction_Install-DbaWatchUpdate PSCommand_Install-DbaWatchUpdate PSFunction_Watch-DbaUpdate PSCommand_Watch-DbaUpdate PSFunction_Uninstall-DbaWatchUpdate PSCommand_Uninstall-DbaWatchUpdate PSFunction_Get-DbaDbQueryStoreOptions PSCommand_Get-DbaDbQueryStoreOptions PSFunction_Set-DbaDbQueryStoreOptions PSCommand_Set-DbaDbQueryStoreOptions PSFunction_Restore-DbaDatabase PSCommand_Restore-DbaDatabase PSFunction_Copy-DbaQueryStoreConfig PSCommand_Copy-DbaQueryStoreConfig PSFunction_Get-DbaExecutionPlan PSCommand_Get-DbaExecutionPlan PSFunction_Export-DbaExecutionPlan PSCommand_Export-DbaExecutionPlan PSFunction_Get-DbaServerProtocol PSCommand_Get-DbaServerProtocol PSFunction_Get-DbaLocaleSetting PSCommand_Get-DbaLocaleSetting PSFunction_Get-DbaSqlBuildReference PSCommand_Get-DbaSqlBuildReference PSFunction_Set-DbaSpConfigure PSCommand_Set-DbaSpConfigure PSFunction_Test-DbaIdentityUsage PSCommand_Test-DbaIdentityUsage PSFunction_Get-DbaDatabaseAssembly PSCommand_Get-DbaDatabaseAssembly PSFunction_Get-DbaAgentJob PSCommand_Get-DbaAgentJob PSFunction_Get-DbaCustomError PSCommand_Get-DbaCustomError PSFunction_Get-DbaCredential PSCommand_Get-DbaCredential PSFunction_Get-DbaBackupDevice PSCommand_Get-DbaBackupDevice PSFunction_Get-DbaAgentProxy PSCommand_Get-DbaAgentProxy PSFunction_Get-DbaDatabaseEncryption PSCommand_Get-DbaDatabaseEncryption PSFunction_New-DbaSsisCatalog PSCommand_New-DbaSsisCatalog PSFunction_Remove-DbaDatabase PSCommand_Remove-DbaDatabase PSFunction_Get-DbaQueryExecutionTime PSCommand_Get-DbaQueryExecutionTime PSFunction_Get-DbaTempdbUsage PSCommand_Get-DbaTempdbUsage PSFunction_Find-DbaDbGrowthEvent PSCommand_Find-DbaDbGrowthEvent PSFunction_Get-DbaNetworkActivity PSCommand_Get-DbaNetworkActivity PSFunction_Get-DbaAgentJobOutputFile PSCommand_Get-DbaAgentJobOutputFile PSFunction_Set-DbaAgentJobOutputFile PSCommand_Set-DbaAgentJobOutputFile PSFunction_Test-DbaLinkedServerConnection PSCommand_Test-DbaLinkedServerConnection PSFunction_Get-DbaDatabaseFile PSCommand_Get-DbaDatabaseFile PSFunction_Read-DbaTransactionLog PSCommand_Read-DbaTransactionLog PSFunction_Get-DbaTable PSCommand_Get-DbaTable PSFunction_Invoke-DbaDatabaseShrink PSCommand_Invoke-DbaDatabaseShrink PSFunction_Get-DbaEstimatedCompletionTime PSCommand_Get-DbaEstimatedCompletionTime PSFunction_Get-DbaLinkedServer PSCommand_Get-DbaLinkedServer PSFunction_Set-DbaStartupParameter PSCommand_Set-DbaStartupParameter PSFunction_New-DbaAgentJob PSCommand_New-DbaAgentJob PSFunction_Export-DbaScript PSCommand_Export-DbaScript PSFunction_Get-DbaLogin PSCommand_Get-DbaLogin PSFunction_New-DbaScriptingOption PSCommand_New-DbaScriptingOption PSFunction_Save-DbaDiagnosticQueryScript PSCommand_Save-DbaDiagnosticQueryScript PSFunction_Invoke-DbaDiagnosticQuery PSCommand_Invoke-DbaDiagnosticQuery PSFunction_Export-DbaDiagnosticQuery PSCommand_Export-DbaDiagnosticQuery PSFunction_Invoke-DbaWhoIsActive PSCommand_Invoke-DbaWhoIsActive PSFunction_Install-DbaWhoIsActive PSCommand_Install-DbaWhoIsActive PSFunction_Set-DbaAgentJob PSCommand_Set-DbaAgentJob PSFunction_Remove-DbaAgentJob PSCommand_Remove-DbaAgentJob PSFunction_New-DbaAgentJobStep PSCommand_New-DbaAgentJobStep PSFunction_Set-DbaAgentJobStep PSCommand_Set-DbaAgentJobStep PSFunction_Remove-DbaAgentJobStep PSCommand_Remove-DbaAgentJobStep PSFunction_New-DbaAgentSchedule PSCommand_New-DbaAgentSchedule PSFunction_Set-DbaAgentSchedule PSCommand_Set-DbaAgentSchedule PSFunction_Remove-DbaAgentSchedule PSCommand_Remove-DbaAgentSchedule PSFunction_Backup-DbaDbCertificate PSCommand_Backup-DbaDbCertificate PSFunction_Get-DbaDbCertificate PSCommand_Get-DbaDbCertificate PSFunction_Get-DbaCmConnection PSCommand_Get-DbaCmConnection PSFunction_Get-DbaCmObject PSCommand_Get-DbaCmObject PSFunction_Get-DbaEndpoint PSCommand_Get-DbaEndpoint PSFunction_Get-DbaDatabaseMasterKey PSCommand_Get-DbaDatabaseMasterKey PSFunction_Get-DbaSchemaChangeHistory PSCommand_Get-DbaSchemaChangeHistory PSFunction_Get-DbaServerAudit PSCommand_Get-DbaServerAudit PSFunction_Get-DbaServerAuditSpecification PSCommand_Get-DbaServerAuditSpecification PSFunction_Get-DbaSqlProductKey PSCommand_Get-DbaSqlProductKey PSFunction_Get-DbatoolsLog PSCommand_Get-DbatoolsLog PSFunction_Restore-DbaDbCertificate PSCommand_Restore-DbaDbCertificate PSFunction_New-DbaDbCertificate PSCommand_New-DbaDbCertificate PSFunction_New-DbaCmConnection PSCommand_New-DbaCmConnection PSFunction_New-DbaDatabaseMasterKey PSCommand_New-DbaDatabaseMasterKey PSFunction_New-DbaServiceMasterKey PSCommand_New-DbaServiceMasterKey PSFunction_New-DbatoolsSupportPackage PSCommand_New-DbatoolsSupportPackage PSFunction_Remove-DbaDbCertificate PSCommand_Remove-DbaDbCertificate PSFunction_Remove-DbaCmConnection PSCommand_Remove-DbaCmConnection PSFunction_Remove-DbaDatabaseMasterKey PSCommand_Remove-DbaDatabaseMasterKey PSFunction_Set-DbaCmConnection PSCommand_Set-DbaCmConnection PSFunction_Set-DbaTcpPort PSCommand_Set-DbaTcpPort PSFunction_Test-DbaCmConnection PSCommand_Test-DbaCmConnection PSFunction_New-DbaSqlConnectionStringBuilder PSCommand_New-DbaSqlConnectionStringBuilder PSFunction_Get-DbaSqlInstanceProperty PSCommand_Get-DbaSqlInstanceProperty PSFunction_Get-DbaSqlInstanceUserOption PSCommand_Get-DbaSqlInstanceUserOption PSFunction_New-DbaSqlConnectionString PSCommand_New-DbaSqlConnectionString PSFunction_Get-DbaAgentSchedule PSCommand_Get-DbaAgentSchedule PSFunction_Invoke-DbaLogShipping PSCommand_Invoke-DbaLogShipping PSFunction_Read-DbaTraceFile PSCommand_Read-DbaTraceFile PSFunction_New-DbaComputerCertificate PSCommand_New-DbaComputerCertificate PSFunction_Get-DbaComputerCertificate PSCommand_Get-DbaComputerCertificate PSFunction_Add-DbaComputerCertificate PSCommand_Add-DbaComputerCertificate PSFunction_Get-DbaNetworkCertificate PSCommand_Get-DbaNetworkCertificate PSFunction_Set-DbaNetworkCertificate PSCommand_Set-DbaNetworkCertificate PSFunction_Remove-DbaNetworkCertificate PSCommand_Remove-DbaNetworkCertificate PSFunction_Enable-DbaForceNetworkEncryption PSCommand_Enable-DbaForceNetworkEncryption PSFunction_Disable-DbaForceNetworkEncryption PSCommand_Disable-DbaForceNetworkEncryption PSFunction_Get-DbaForceNetworkEncryption PSCommand_Get-DbaForceNetworkEncryption PSFunction_Remove-DbaComputerCertificate PSCommand_Remove-DbaComputerCertificate PSFunction_Get-DbaServerInstallDate PSCommand_Get-DbaServerInstallDate PSFunction_Install-DbaFirstResponderKit PSCommand_Install-DbaFirstResponderKit PSFunction_Backup-DbaDatabaseMasterKey PSCommand_Backup-DbaDatabaseMasterKey PSFunction_Get-DbaAgentJobHistory PSCommand_Get-DbaAgentJobHistory PSFunction_Get-DbaSsisEnvironmentVariable PSCommand_Get-DbaSsisEnvironmentVariable PSFunction_Get-DbaSqlManagementObject PSCommand_Get-DbaSqlManagementObject PSFunction_Test-DbaSqlManagementObject PSCommand_Test-DbaSqlManagementObject PSFunction_Get-DbaMaintenanceSolutionLog PSCommand_Get-DbaMaintenanceSolutionLog PSFunction_Invoke-DbaLogShippingRecovery PSCommand_Invoke-DbaLogShippingRecovery PSFunction_Find-DbaTrigger PSCommand_Find-DbaTrigger PSFunction_Find-DbaView PSCommand_Find-DbaView PSFunction_Invoke-DbaDatabaseUpgrade PSCommand_Invoke-DbaDatabaseUpgrade PSFunction_Get-DbaDatabaseUser PSCommand_Get-DbaDatabaseUser PSFunction_Get-DbaWindowsLog PSCommand_Get-DbaWindowsLog PSFunction_Get-DbaErrorLog PSCommand_Get-DbaErrorLog PSFunction_Get-DbaAgentLog PSCommand_Get-DbaAgentLog PSFunction_Get-DbaDbMailLog PSCommand_Get-DbaDbMailLog PSFunction_Get-DbaDbMailHistory PSCommand_Get-DbaDbMailHistory PSFunction_Get-DbaDatabaseView PSCommand_Get-DbaDatabaseView PSFunction_Get-DbaDatabaseUdf PSCommand_Get-DbaDatabaseUdf PSFunction_Get-DbaDatabasePartitionFunction PSCommand_Get-DbaDatabasePartitionFunction PSFunction_Get-DbaDatabasePartitionScheme PSCommand_Get-DbaDatabasePartitionScheme PSFunction_Get-DbaDefaultPath PSCommand_Get-DbaDefaultPath PSFunction_Get-DbaDbStoredProcedure PSCommand_Get-DbaDbStoredProcedure PSFunction_Test-DbaDbCompression PSCommand_Test-DbaDbCompression PSFunction_Mount-DbaDatabase PSCommand_Mount-DbaDatabase PSFunction_Dismount-DbaDatabase PSCommand_Dismount-DbaDatabase PSFunction_Set-DbaPrivilege PSCommand_Set-DbaPrivilege PSFunction_Get-DbaAgReplica PSCommand_Get-DbaAgReplica PSFunction_Get-DbaAgDatabase PSCommand_Get-DbaAgDatabase PSFunction_Get-DbaSqlModule PSCommand_Get-DbaSqlModule PSFunction_Get-DbaRegisteredServerStore PSCommand_Get-DbaRegisteredServerStore PSFunction_Sync-DbaLoginPermission PSCommand_Sync-DbaLoginPermission PSFunction_Invoke-Sqlcmd2 PSCommand_Invoke-Sqlcmd2 PSFunction_New-DbaCredential PSCommand_New-DbaCredential PSFunction_Get-DbaFile PSCommand_Get-DbaFile PSFunction_Set-DbaDbCompression PSCommand_Set-DbaDbCompression PSFunction_New-DbaClientAlias PSCommand_New-DbaClientAlias PSFunction_Get-DbaClientAlias PSCommand_Get-DbaClientAlias PSFunction_Get-DbaOperatingSystem PSCommand_Get-DbaOperatingSystem PSFunction_Install-DbaMaintenanceSolution PSCommand_Install-DbaMaintenanceSolution PSFunction_Get-DbaComputerSystem PSCommand_Get-DbaComputerSystem PSFunction_Get-DbaTraceFlag PSCommand_Get-DbaTraceFlag PSFunction_Stop-DbaSqlService PSCommand_Stop-DbaSqlService PSFunction_Start-DbaSqlService PSCommand_Start-DbaSqlService PSFunction_Restart-DbaSqlService PSCommand_Restart-DbaSqlService PSFunction_Invoke-DbaCycleErrorLog PSCommand_Invoke-DbaCycleErrorLog PSFunction_Get-DbaSqlRegistryRoot PSCommand_Get-DbaSqlRegistryRoot PSFunction_Get-DbaAvailableCollation PSCommand_Get-DbaAvailableCollation PSFunction_Get-DbaUserLevelPermission PSCommand_Get-DbaUserLevelPermission PSFunction_Get-DbaAgHadr PSCommand_Get-DbaAgHadr PSFunction_Get-DbaPolicy PSCommand_Get-DbaPolicy PSFunction_Find-DbaSimilarTable PSCommand_Find-DbaSimilarTable PSFunction_Disable-DbaAgHadr PSCommand_Disable-DbaAgHadr PSFunction_Enable-DbaAgHadr PSCommand_Enable-DbaAgHadr PSFunction_Get-DbaTrace PSCommand_Get-DbaTrace PSFunction_Get-DbaSuspectPage PSCommand_Get-DbaSuspectPage PSFunction_Get-DbaWaitStatistic PSCommand_Get-DbaWaitStatistic PSFunction_Clear-DbaWaitStatistics PSCommand_Clear-DbaWaitStatistics PSFunction_Get-DbaTopResourceUsage PSCommand_Get-DbaTopResourceUsage PSFunction_New-DbaLogin PSCommand_New-DbaLogin PSFunction_Get-DbaAgListener PSCommand_Get-DbaAgListener PSFunction_Invoke-DbaDatabaseClone PSCommand_Invoke-DbaDatabaseClone PSFunction_Read-DbaXEFile PSCommand_Read-DbaXEFile PSFunction_Get-DbaDistributor PSCommand_Get-DbaDistributor PSFunction_Update-DbaSqlServiceAccount PSCommand_Update-DbaSqlServiceAccount PSFunction_Watch-DbaXESession PSCommand_Watch-DbaXESession PSFunction_Disable-DbaTraceFlag PSCommand_Disable-DbaTraceFlag PSFunction_Enable-DbaTraceFlag PSCommand_Enable-DbaTraceFlag PSFunction_Start-DbaAgentJob PSCommand_Start-DbaAgentJob PSFunction_Stop-DbaAgentJob PSCommand_Stop-DbaAgentJob PSFunction_Remove-DbaClientAlias PSCommand_Remove-DbaClientAlias PSFunction_New-DbaAgentProxy PSCommand_New-DbaAgentProxy PSFunction_Test-DbaLogShippingStatus PSCommand_Test-DbaLogShippingStatus PSFunction_Get-DbaXESessionTarget PSCommand_Get-DbaXESessionTarget PSFunction_New-DbaXESmartTargetResponse PSCommand_New-DbaXESmartTargetResponse PSFunction_New-DbaXESmartTarget PSCommand_New-DbaXESmartTarget PSFunction_Get-DbaDbVirtualLogFile PSCommand_Get-DbaDbVirtualLogFile PSFunction_Register-DbaConfig PSCommand_Register-DbaConfig PSFunction_Get-DbaBackupInformation PSCommand_Get-DbaBackupInformation PSFunction_Start-DbaXESession PSCommand_Start-DbaXESession PSFunction_Stop-DbaXESession PSCommand_Stop-DbaXESession PSFunction_Set-DbaDbRecoveryModel PSCommand_Set-DbaDbRecoveryModel PSFunction_Get-DbaDbRecoveryModel PSCommand_Get-DbaDbRecoveryModel PSFunction_Get-DbaWaitingTask PSCommand_Get-DbaWaitingTask PSFunction_Remove-DbaDbUser PSCommand_Remove-DbaDbUser PSFunction_Get-DbaDump PSCommand_Get-DbaDump PSFunction_Invoke-DbaAdvancedRestore PSCommand_Invoke-DbaAdvancedRestore PSFunction_Format-DbaBackupInformation PSCommand_Format-DbaBackupInformation PSFunction_Get-DbaAgentJobStep PSCommand_Get-DbaAgentJobStep PSFunction_Test-DbaBackupInformation PSCommand_Test-DbaBackupInformation PSFunction_Invoke-DbaBalanceDataFiles PSCommand_Invoke-DbaBalanceDataFiles PSFunction_Select-DbaBackupInformation PSCommand_Select-DbaBackupInformation PSFunction_Rename-DbaDatabase PSCommand_Rename-DbaDatabase PSFunction_New-DbaPublishProfile PSCommand_New-DbaPublishProfile PSFunction_Publish-DbaDacpac PSCommand_Publish-DbaDacpac PSFunction_Export-DbaDacpac PSCommand_Export-DbaDacpac PSFunction_Copy-DbaTableData PSCommand_Copy-DbaTableData PSFunction_Invoke-DbaSqlQuery PSCommand_Invoke-DbaSqlQuery PSFunction_Remove-DbaLogin PSCommand_Remove-DbaLogin PSFunction_Get-DbaFileStream PSCommand_Get-DbaFileStream PSFunction_Set-DbaFileStream PSCommand_Set-DbaFileStream PSFunction_Get-DbaAgentJobCategory PSCommand_Get-DbaAgentJobCategory PSFunction_New-DbaAgentJobCategory PSCommand_New-DbaAgentJobCategory PSFunction_Remove-DbaAgentJobCategory PSCommand_Remove-DbaAgentJobCategory PSFunction_Set-DbaAgentJobCategory PSCommand_Set-DbaAgentJobCategory PSFunction_Get-DbaDbRole PSCommand_Get-DbaDbRole PSFunction_Get-DbaServerRole PSCommand_Get-DbaServerRole PSFunction_Find-DbaBackup PSCommand_Find-DbaBackup PSFunction_Get-DbaCpuUsage PSCommand_Get-DbaCpuUsage PSFunction_Remove-DbaXESession PSCommand_Remove-DbaXESession PSFunction_New-DbaXESession PSCommand_New-DbaXESession PSFunction_Import-DbaXESessionTemplate PSCommand_Import-DbaXESessionTemplate PSFunction_Get-DbaXEStore PSCommand_Get-DbaXEStore PSFunction_Export-DbaXESessionTemplate PSCommand_Export-DbaXESessionTemplate PSFunction_New-DbaXESmartTableWriter PSCommand_New-DbaXESmartTableWriter PSFunction_New-DbaXESmartReplay PSCommand_New-DbaXESmartReplay PSFunction_New-DbaXESmartEmail PSCommand_New-DbaXESmartEmail PSFunction_New-DbaXESmartQueryExec PSCommand_New-DbaXESmartQueryExec PSFunction_Start-DbaXESmartTarget PSCommand_Start-DbaXESmartTarget PSFunction_Get-DbaOrphanUser PSCommand_Get-DbaOrphanUser PSFunction_Get-DbaOpenTransaction PSCommand_Get-DbaOpenTransaction PSFunction_Get-DbaLogShippingError PSCommand_Get-DbaLogShippingError PSFunction_Test-DbaSqlBuild PSCommand_Test-DbaSqlBuild PSFunction_Get-DbaXESessionTemplate PSCommand_Get-DbaXESessionTemplate PSFunction_ConvertTo-DbaXESession PSCommand_ConvertTo-DbaXESession PSFunction_Start-DbaTrace PSCommand_Start-DbaTrace PSFunction_Stop-DbaTrace PSCommand_Stop-DbaTrace PSFunction_Remove-DbaTrace PSCommand_Remove-DbaTrace PSFunction_Set-DbaLogin PSCommand_Set-DbaLogin PSFunction_Copy-DbaXESessionTemplate PSCommand_Copy-DbaXESessionTemplate PSFunction_Get-DbaXEObject PSCommand_Get-DbaXEObject PSFunction_ConvertTo-DbaDataTable PSCommand_ConvertTo-DbaDataTable PSFunction_Find-DbaDisabledIndex PSCommand_Find-DbaDisabledIndex PSFunction_Invoke-DbaPfRelog PSCommand_Invoke-DbaPfRelog PSFunction_Get-DbaPfDataCollectorCounter PSCommand_Get-DbaPfDataCollectorCounter PSFunction_Get-DbaPfDataCollectorCounterSample PSCommand_Get-DbaPfDataCollectorCounterSample PSFunction_Get-DbaPfDataCollector PSCommand_Get-DbaPfDataCollector PSFunction_Get-DbaPfDataCollectorSet PSCommand_Get-DbaPfDataCollectorSet PSFunction_Start-DbaPfDataCollectorSet PSCommand_Start-DbaPfDataCollectorSet PSFunction_Stop-DbaPfDataCollectorSet PSCommand_Stop-DbaPfDataCollectorSet PSFunction_Export-DbaPfDataCollectorSetTemplate PSCommand_Export-DbaPfDataCollectorSetTemplate PSFunction_Get-DbaPfDataCollectorSetTemplate PSCommand_Get-DbaPfDataCollectorSetTemplate PSFunction_Import-DbaPfDataCollectorSetTemplate PSCommand_Import-DbaPfDataCollectorSetTemplate PSFunction_Remove-DbaPfDataCollectorSet PSCommand_Remove-DbaPfDataCollectorSet PSFunction_Add-DbaPfDataCollectorCounter PSCommand_Add-DbaPfDataCollectorCounter PSFunction_Remove-DbaPfDataCollectorCounter PSCommand_Remove-DbaPfDataCollectorCounter PSFunction_Get-DbaPfAvailableCounter PSCommand_Get-DbaPfAvailableCounter PSFunction_Get-DbaXESmartTarget PSCommand_Get-DbaXESmartTarget PSFunction_Remove-DbaXESmartTarget PSCommand_Remove-DbaXESmartTarget PSFunction_Stop-DbaXESmartTarget PSCommand_Stop-DbaXESmartTarget PSFunction_Get-DbaRegisteredServerGroup PSCommand_Get-DbaRegisteredServerGroup PSFunction_New-DbaDbUser PSCommand_New-DbaDbUser PSFunction_Measure-DbaDiskSpaceRequirement PSCommand_Measure-DbaDiskSpaceRequirement PSFunction_New-DbaXESmartCsvWriter PSCommand_New-DbaXESmartCsvWriter PSFunction_Export-DbaXECsv PSCommand_Export-DbaXECsv PSFunction_Invoke-DbaXeReplay PSCommand_Invoke-DbaXeReplay PSFunction_Find-DbaInstance PSCommand_Find-DbaInstance PSFunction_Test-DbaDiskSpeed PSCommand_Test-DbaDiskSpeed PSFunction_Get-DbaDbExtentDiff PSCommand_Get-DbaDbExtentDiff PSFunction_Read-DbaAuditFile PSCommand_Read-DbaAuditFile PSFunction_Get-DbaDbCompression PSCommand_Get-DbaDbCompression PSFunction_Invoke-DbaDbDecryptObject PSCommand_Invoke-DbaDbDecryptObject PSFunction_Get-DbaDbForeignKey PSCommand_Get-DbaDbForeignKey PSFunction_Get-DbaDbCheckConstraint PSCommand_Get-DbaDbCheckConstraint PSFunction_Set-DbaAgentAlert PSCommand_Set-DbaAgentAlert PSFunction_Get-DbaSqlFeature PSCommand_Get-DbaSqlFeature PSFunction_Get-DbaWaitResource PSCommand_Get-DbaWaitResource PSFunction_Get-DbaDbPageInfo PSCommand_Get-DbaDbPageInfo PSFunction_Get-DbaConnection PSCommand_Get-DbaConnection PSFunction_Test-DbaLoginPassword PSCommand_Test-DbaLoginPassword PSFunction_Get-DbaResourceGovernorClassifierFunction PSCommand_Get-DbaResourceGovernorClassifierFunction PSFunction_Get-DbaErrorLogConfig PSCommand_Get-DbaErrorLogConfig PSFunction_Set-DbaErrorLogConfig PSCommand_Set-DbaErrorLogConfig PSFunction_Select-DbaObject PSCommand_Select-DbaObject PSFunction_Add-DbaRegisteredServer PSCommand_Add-DbaRegisteredServer PSFunction_Add-DbaRegisteredServerGroup PSCommand_Add-DbaRegisteredServerGroup PSFunction_Export-DbaRegisteredServer PSCommand_Export-DbaRegisteredServer PSFunction_Import-DbaRegisteredServer PSCommand_Import-DbaRegisteredServer PSFunction_Move-DbaRegisteredServer PSCommand_Move-DbaRegisteredServer PSFunction_Move-DbaRegisteredServerGroup PSCommand_Move-DbaRegisteredServerGroup PSFunction_Remove-DbaRegisteredServer PSCommand_Remove-DbaRegisteredServer PSFunction_Remove-DbaRegisteredServerGroup PSCommand_Remove-DbaRegisteredServerGroup PSFunction_Get-DbaPlanCache PSCommand_Get-DbaPlanCache PSFunction_Clear-DbaPlanCache PSCommand_Clear-DbaPlanCache PSFunction_Get-DbaSsisExecutionHistory PSCommand_Get-DbaSsisExecutionHistory PSIncludes_Function</S>
          </En>
          <En>
            <S N="Key">created</S>
            <S N="Value">7/23/2018 7:59:10 PM +00:00</S>
          </En>
          <En>
            <S N="Key">description</S>
            <S N="Value">The community module that enables SQL Server Pros to automate database development and server administration</S>
          </En>
          <En>
            <S N="Key">published</S>
            <S N="Value">7/23/2018 7:59:10 PM +00:00</S>
          </En>
          <En>
            <S N="Key">developmentDependency</S>
            <S N="Value">False</S>
          </En>
          <En>
            <S N="Key">NormalizedVersion</S>
            <S N="Value">0.9.380</S>
          </En>
          <En>
            <S N="Key">downloadCount</S>
            <S N="Value">66093</S>
          </En>
          <En>
            <S N="Key">GUID</S>
            <S N="Value">9d139310-ce45-41ce-8e8b-d76335aa1789</S>
          </En>
          <En>
            <S N="Key">PowerShellVersion</S>
            <S N="Value">3.0</S>
          </En>
          <En>
            <S N="Key">updated</S>
            <S N="Value">2018-07-24T00:00:50Z</S>
          </En>
          <En>
            <S N="Key">isLatestVersion</S>
            <S N="Value">True</S>
          </En>
          <En>
            <S N="Key">IsPrerelease</S>
            <S N="Value">false</S>
          </En>
          <En>
            <S N="Key">isAbsoluteLatestVersion</S>
            <S N="Value">True</S>
          </En>
          <En>
            <S N="Key">packageSize</S>
            <S N="Value">20070729</S>
          </En>
          <En>
            <S N="Key">FileList</S>
            <S N="Value">dbatools.nuspec|allcommands.ps1|appveyor.yml|codecov.yml|contributing.md|dbatools.psd1|dbatools.psm1|dbatools.psproj|dbatools.psprojs|install.ps1|LICENSE|msbuild.log|readme.md|bin\build-project.ps1|bin\dbatools-buildref-index.json|bin\dbatools-index.json|bin\dbatools.dll|bin\dbatools.pdb|bin\dbatools.xml|bin\library.ps1|bin\LumenWorks.Framework.IO.dll|bin\PSScriptAnalyzerRules.psd1|bin\sp_SQLskills_ConvertTraceToEEs.sql|bin\stig.sql|bin\thor.png|bin\type-extensions.ps1|bin\typealiases.ps1|bin\xetemplates-metadata.xml|bin\bcp\bcp.exe|bin\bcp\Resources\1033\bcp.rll|bin\build\vsts-build.ps1|bin\build\vsts-prebuild.ps1|bin\diagnosticquery\SQLServerDiagnosticQueries_2005_201806.sql|bin\diagnosticquery\SQLServerDiagnosticQueries_2008R2_201806.sql|bin\diagnosticquery\SQLServerDiagnosticQueries_2008_201806.sql|bin\diagnosticquery\SQLServerDiagnosticQueries_2012_201806.sql|bin\diagnosticquery\SQLServerDiagnosticQueries_2014_201806.sql|bin\diagnosticquery\SQLServerDiagnosticQueries_2016SP2_201806.sql|bin\diagnosticquery\SQLServerDiagnosticQueries_2016_201806.sql|bin\diagnosticquery\SQLServerDiagnosticQueries_2017_201806.sql|bin\diagnosticquery\SQLServerDiagnosticQueries_AzureSQLDatabase_201806.sql|bin\perfmontemplates\collectorsets.xml|bin\perfmontemplates\collectorsets\Long%20Running%20Queries.xml|bin\perfmontemplates\collectorsets\PAL%20-%20SQL%20Server%202005.xml|bin\perfmontemplates\collectorsets\PAL%20-%20SQL%20Server%202008%20and%20R2.xml|bin\perfmontemplates\collectorsets\PAL%20-%20SQL%20Server%202012.xml|bin\perfmontemplates\collectorsets\PAL%20-%20SQL%20Server%202014%20and%20Up.xml|bin\projects\dbatools\dbatools.sln|bin\projects\dbatools\msbuild.rsp|bin\projects\dbatools\dbatools\dbatools.csproj|bin\projects\dbatools\dbatools\Commands\WriteMessageCommand.cs|bin\projects\dbatools\dbatools\Computer\DiskSpace.cs|bin\projects\dbatools\dbatools\Computer\DriveType.cs|bin\projects\dbatools\dbatools\Computer\PageFileSetting.cs|bin\projects\dbatools\dbatools\Configuration\Config.cs|bin\projects\dbatools\dbatools\Configuration\ConfigScope.cs|bin\projects\dbatools\dbatools\Configuration\ConfigurationHost.cs|bin\projects\dbatools\dbatools\Connection\ConnectionHost.cs|bin\projects\dbatools\dbatools\Connection\ManagementConnection.cs|bin\projects\dbatools\dbatools\Connection\ManagementConnectionProtocolState.cs|bin\projects\dbatools\dbatools\Connection\ManagementConnectionType.cs|bin\projects\dbatools\dbatools\Connection\PSSessionContainer.cs|bin\projects\dbatools\dbatools\Connection\SqlConnectionProtocol.cs|bin\projects\dbatools\dbatools\Database\BackupHistory.cs|bin\projects\dbatools\dbatools\Database\Dependency.cs|bin\projects\dbatools\dbatools\dbaSystem\DbaErrorRecord.cs|bin\projects\dbatools\dbatools\dbaSystem\DebugHost.cs|bin\projects\dbatools\dbatools\dbaSystem\StartTimeEntry.cs|bin\projects\dbatools\dbatools\dbaSystem\StartTimeResult.cs|bin\projects\dbatools\dbatools\dbaSystem\SystemHost.cs|bin\projects\dbatools\dbatools\Discovery\DbaBrowserReply.cs|bin\projects\dbatools\dbatools\Discovery\DbaInstanceAvailability.cs|bin\projects\dbatools\dbatools\Discovery\DbaInstanceConfidenceLevel.cs|bin\projects\dbatools\dbatools\Discovery\DbaInstanceDiscoveryType.cs|bin\projects\dbatools\dbatools\Discovery\DbaInstanceReport.cs|bin\projects\dbatools\dbatools\Discovery\DbaInstanceScanType.cs|bin\projects\dbatools\dbatools\Discovery\DbaPortReport.cs|bin\projects\dbatools\dbatools\Exceptions\BloodyHellGiveMeSomethingToWorkWithException.cs|bin\projects\dbatools\dbatools\General\ExecutionMode.cs|bin\projects\dbatools\dbatools\Maintenance\MaintenanceHost.cs|bin\projects\dbatools\dbatools\Maintenance\MaintenancePriority.cs|bin\projects\dbatools\dbatools\Maintenance\MaintenanceTask.cs|bin\projects\dbatools\dbatools\Message\DbatoolsException.cs|bin\projects\dbatools\dbatools\Message\DbatoolsExceptionRecord.cs|bin\projects\dbatools\dbatools\Message\LogEntry.cs|bin\projects\dbatools\dbatools\Message\LogEntryType.cs|bin\projects\dbatools\dbatools\Message\LogHost.cs|bin\projects\dbatools\dbatools\Message\MessageEventSubscription.cs|bin\projects\dbatools\dbatools\Message\MessageHost.cs|bin\projects\dbatools\dbatools\Message\MessageLevel.cs|bin\projects\dbatools\dbatools\Message\MessageLevelModifier.cs|bin\projects\dbatools\dbatools\Message\TransformCondition.cs|bin\projects\dbatools\dbatools\Message\TransformError.cs|bin\projects\dbatools\dbatools\Message\TransformList.cs|bin\projects\dbatools\dbatools\Message\TransformType.cs|bin\projects\dbatools\dbatools\obj\Release\dbatools.csproj.FileListAbsolute.txt|bin\projects\dbatools\dbatools\obj\Release\dbatools.csprojResolveAssemblyReference.cache|bin\projects\dbatools\dbatools\obj\Release\dbatools.dll|bin\projects\dbatools\dbatools\obj\Release\dbatools.pdb|bin\projects\dbatools\dbatools\Parameter\DbaCmConnectionParameter.cs|bin\projects\dbatools\dbatools\Parameter\DbaCredentialParameter.cs|bin\projects\dbatools\dbatools\Parameter\DbaDatabaseParameter.cs|bin\projects\dbatools\dbatools\Parameter\DbaDatabaseSmoParameter.cs|bin\projects\dbatools\dbatools\Parameter\DbaInstanceInputType.cs|bin\projects\dbatools\dbatools\Parameter\DbaInstanceParameter.cs|bin\projects\dbatools\dbatools\Parameter\DbaSelectParameter.cs|bin\projects\dbatools\dbatools\Parameter\ParameterContractAttribute.cs|bin\projects\dbatools\dbatools\Parameter\ParameterContractBehavior.cs|bin\projects\dbatools\dbatools\Parameter\ParameterContractType.cs|bin\projects\dbatools\dbatools\Properties\AssemblyInfo.cs|bin\projects\dbatools\dbatools\Runspace\DbaRunspaceState.cs|bin\projects\dbatools\dbatools\Runspace\RunspaceContainer.cs|bin\projects\dbatools\dbatools\Runspace\RunspaceHost.cs|bin\projects\dbatools\dbatools\TabExpansion\InstanceAccess.cs|bin\projects\dbatools\dbatools\TabExpansion\ScriptContainer.cs|bin\projects\dbatools\dbatools\TabExpansion\TabCompletionSet.cs|bin\projects\dbatools\dbatools\TabExpansion\TabExpansionHost.cs|bin\projects\dbatools\dbatools\TypeConversion\DbaCredentialParameterConverter.cs|bin\projects\dbatools\dbatools\Utility\CredentialPrompt.cs|bin\projects\dbatools\dbatools\Utility\DateTimeExtension.cs|bin\projects\dbatools\dbatools\Utility\DbaDate.cs|bin\projects\dbatools\dbatools\Utility\DbaDateTime.cs|bin\projects\dbatools\dbatools\Utility\DbaDateTimeBase.cs|bin\projects\dbatools\dbatools\Utility\DbaTime.cs|bin\projects\dbatools\dbatools\Utility\DbaTimeSpan.cs|bin\projects\dbatools\dbatools\Utility\DbaTimeSpanPretty.cs|bin\projects\dbatools\dbatools\Utility\DbaValidatePatternAttribute.cs|bin\projects\dbatools\dbatools\Utility\DbaValidateScriptAttribute.cs|bin\projects\dbatools\dbatools\Utility\RegexHelper.cs|bin\projects\dbatools\dbatools\Utility\Size.cs|bin\projects\dbatools\dbatools\Utility\SizeStyle.cs|bin\projects\dbatools\dbatools\Utility\UtilityHost.cs|bin\projects\dbatools\dbatools\Utility\Validation.cs|bin\projects\dbatools\dbatools\Validation\LinkedServerResult.cs|bin\projects\dbatools\dbatools.Tests\dbatools.Tests.csproj|bin\projects\dbatools\dbatools.Tests\packages.config|bin\projects\dbatools\dbatools.Tests\Connection\ManagementConnectionTest.cs|bin\projects\dbatools\dbatools.Tests\Parameter\DbaInstanceParamaterTest.cs|bin\projects\dbatools\dbatools.Tests\Properties\AssemblyInfo.cs|bin\projects\dbatools\dbatools.Tests\Utility\SizeTest.cs|bin\smo\Accessibility.dll|bin\smo\EnvDTE.dll|bin\smo\Microsoft.AnalysisServices.AppLocal.Core.dll|bin\smo\Microsoft.AnalysisServices.AppLocal.dll|bin\smo\Microsoft.Azure.KeyVault.Core.dll|bin\smo\Microsoft.Data.Edm.dll|bin\smo\Microsoft.Data.OData.dll|bin\smo\Microsoft.Data.Tools.Components.dll|bin\smo\Microsoft.Data.Tools.Contracts.dll|bin\smo\Microsoft.Data.Tools.Schema.Sql.dll|bin\smo\Microsoft.Data.Tools.Schema.SqlTasks.targets|bin\smo\Microsoft.Data.Tools.Schema.Tasks.Sql.dll|bin\smo\Microsoft.Data.Tools.Schema.Utilities.Sql.dll|bin\smo\Microsoft.Data.Tools.Utilities.dll|bin\smo\Microsoft.DataTransfer.Common.Utils.dll|bin\smo\Microsoft.Practices.TransientFaultHandling.Core.dll|bin\smo\Microsoft.SqlServer.ADONETSrc.dll|bin\smo\Microsoft.SqlServer.ASTasks.dll|bin\smo\Microsoft.SqlServer.BatchParser.dll|bin\smo\Microsoft.SqlServer.BatchParserClient.dll|bin\smo\Microsoft.SqlServer.BulkInsertTaskConnections.dll|bin\smo\Microsoft.SqlServer.ConnectionInfo.dll|bin\smo\Microsoft.SqlServer.ConnectionInfoExtended.dll|bin\smo\Microsoft.SqlServer.CustomControls.dll|bin\smo\Microsoft.SqlServer.Dac.dll|bin\smo\Microsoft.SqlServer.Dac.Extensions.dll|bin\smo\Microsoft.SqlServer.Dac.Extensions.xml|bin\smo\Microsoft.SqlServer.Dac.xml|bin\smo\Microsoft.SqlServer.DataProfiler.dll|bin\smo\Microsoft.SqlServer.DataProfilingTask.dll|bin\smo\Microsoft.SqlServer.Diagnostics.Strace.dll|bin\smo\Microsoft.SqlServer.Dmf.Adapters.dll|bin\smo\Microsoft.SqlServer.Dmf.Common.dll|bin\smo\Microsoft.SqlServer.Dmf.dll|bin\smo\Microsoft.SqlServer.DmfSqlClrWrapper.dll|bin\smo\Microsoft.SqlServer.DMQueryTask.dll|bin\smo\Microsoft.SqlServer.DTEnum.dll|bin\smo\Microsoft.SqlServer.Dts.Design.dll|bin\smo\Microsoft.SqlServer.Dts.DtsClient.dll|bin\smo\Microsoft.SqlServer.DtsMsg.dll|bin\smo\Microsoft.SqlServer.DTSPipelineWrap.dll|bin\smo\Microsoft.SQLServer.DTSRuntimeWrap.dll|bin\smo\Microsoft.SqlServer.DtsServer.Interop.dll|bin\smo\Microsoft.SqlServer.DTSUtilities.dll|bin\smo\Microsoft.SqlServer.Edition.dll|bin\smo\Microsoft.SqlServer.ExecProcTask.dll|bin\smo\Microsoft.SqlServer.ExpressionTask.dll|bin\smo\Microsoft.SqlServer.FileSystemTask.dll|bin\smo\Microsoft.SqlServer.ForEachADOEnumerator.dll|bin\smo\Microsoft.SqlServer.ForEachFileEnumeratorWrap.dll|bin\smo\Microsoft.SqlServer.ForEachFromVarEnumerator.dll|bin\smo\Microsoft.SqlServer.ForEachNodeListEnumerator.dll|bin\smo\Microsoft.SqlServer.ForEachSMOEnumerator.dll|bin\smo\Microsoft.SqlServer.FtpTask.dll|bin\smo\Microsoft.SqlServer.GridControl.dll|bin\smo\Microsoft.SqlServer.Instapi.dll|bin\smo\Microsoft.SqlServer.IntegrationServices.ClusterManagement.dll|bin\smo\Microsoft.SqlServer.IntegrationServices.Common.ObjectModel.dll|bin\smo\Microsoft.SqlServer.IntegrationServices.ISServerDBUpgrade.dll|bin\smo\Microsoft.SqlServer.IntegrationServices.ODataConnectionManager.dll|bin\smo\Microsoft.SqlServer.IntegrationServices.ODataSrc.dll|bin\smo\Microsoft.SqlServer.IntegrationServices.Server.Common.dll|bin\smo\Microsoft.SqlServer.IntegrationServices.Server.dll|bin\smo\Microsoft.SqlServer.IntegrationServices.Server.IPC.dll|bin\smo\Microsoft.SqlServer.IntegrationServices.server.shared.dll|bin\smo\Microsoft.SqlServer.IntegrationServices.SqlTaskScheduler.dll|bin\smo\Microsoft.SqlServer.IntegrationServices.TaskScheduler.dll|bin\smo\Microsoft.SqlServer.IntegrationServices.WorkerAgent.dll|bin\smo\Microsoft.SqlServer.ManagedConnections.dll|bin\smo\Microsoft.SqlServer.ManagedDTS.dll|bin\smo\Microsoft.SqlServer.Management.AlwaysEncrypted.AzureKeyVaultProvider.dll|bin\smo\Microsoft.SqlServer.Management.AlwaysEncrypted.Management.dll|bin\smo\Microsoft.SqlServer.Management.AlwaysEncrypted.Types.dll|bin\smo\Microsoft.SqlServer.Management.AzureAuthenticationManagement.dll|bin\smo\Microsoft.SqlServer.Management.CloudAdapter.Client.dll|bin\smo\Microsoft.SqlServer.Management.CloudAdapter.Data.dll|bin\smo\Microsoft.SqlServer.Management.Collector.dll|bin\smo\Microsoft.SqlServer.Management.CollectorEnum.dll|bin\smo\Microsoft.SqlServer.Management.CollectorTasks.dll|bin\smo\Microsoft.SqlServer.Management.Dac.UniversalAuthProvider.dll|bin\smo\Microsoft.SqlServer.Management.HadrDmf.dll|bin\smo\Microsoft.SqlServer.Management.HelpViewer.dll|bin\smo\Microsoft.SqlServer.Management.InMemoryOLTPMigrationAdvisor.dll|bin\smo\Microsoft.SqlServer.Management.IntegrationServices.dll|bin\smo\Microsoft.SqlServer.Management.IntegrationServicesEnum.dll|bin\smo\Microsoft.SqlServer.Management.RegisteredServers.dll|bin\smo\Microsoft.SqlServer.Management.Sdk.Scripting.dll|bin\smo\Microsoft.SqlServer.Management.Sdk.Sfc.dll|bin\smo\Microsoft.SqlServer.Management.SDK.SqlStudio.dll|bin\smo\Microsoft.SqlServer.Management.SmartAdminPolicies.dll|bin\smo\Microsoft.SqlServer.Management.SqlParser.dll|bin\smo\Microsoft.SqlServer.Management.SystemMetadataProvider.dll|bin\smo\Microsoft.SqlServer.Management.Utility.dll|bin\smo\Microsoft.SqlServer.Management.UtilityEnum.dll|bin\smo\Microsoft.SqlServer.Management.XEvent.dll|bin\smo\Microsoft.SqlServer.Management.XEventDbScoped.dll|bin\smo\Microsoft.SqlServer.Management.XEventDbScopedEnum.dll|bin\smo\Microsoft.SqlServer.Management.XEventEnum.dll|bin\smo\Microsoft.SqlServer.MSMQTask.dll|bin\smo\Microsoft.SqlServer.OlapEnum.dll|bin\smo\Microsoft.SqlServer.PackageFormatUpdate.dll|bin\smo\Microsoft.SqlServer.PipelineHost.dll|bin\smo\Microsoft.SqlServer.PipelineXML.dll|bin\smo\Microsoft.SqlServer.PolicyEnum.dll|bin\smo\Microsoft.SqlServer.RegSvrEnum.dll|bin\smo\Microsoft.SqlServer.ReplEnum.dll|bin\smo\Microsoft.SqlServer.Replication.BusinessLogicSupport.dll|bin\smo\Microsoft.SqlServer.Replication.dll|bin\smo\Microsoft.SqlServer.Rmo.dll|bin\smo\Microsoft.SqlServer.ScriptTask.dll|bin\smo\Microsoft.SqlServer.SendMailTask.dll|bin\smo\Microsoft.SqlServer.ServiceBrokerEnum.dll|bin\smo\Microsoft.SqlServer.Smo.dll|bin\smo\Microsoft.SqlServer.SmoExtended.dll|bin\smo\Microsoft.SqlServer.SqlCEDest.dll|bin\smo\Microsoft.SqlServer.SqlClrProvider.dll|bin\smo\Microsoft.SqlServer.SqlEnum.dll|bin\smo\Microsoft.SqlServer.SQLTask.dll|bin\smo\Microsoft.SqlServer.SQLTaskConnectionsWrap.dll|bin\smo\Microsoft.SqlServer.SqlTDiagm.dll|bin\smo\Microsoft.SqlServer.SqlWmiManagement.dll|bin\smo\Microsoft.SqlServer.SString.dll|bin\smo\Microsoft.SqlServer.TransactSql.ScriptDom.dll|bin\smo\Microsoft.SqlServer.TransferDatabasesTask.dll|bin\smo\Microsoft.SqlServer.TransferErrorMessagesTask.dll|bin\smo\Microsoft.SqlServer.TransferJobsTask.dll|bin\smo\Microsoft.SqlServer.TransferLoginsTask.dll|bin\smo\Microsoft.SqlServer.TransferObjectsTask.dll|bin\smo\Microsoft.SqlServer.TransferSqlServerObjectsTask.dll|bin\smo\Microsoft.SqlServer.TransferStoredProceduresTask.dll|bin\smo\Microsoft.SqlServer.TxScript.dll|bin\smo\Microsoft.SqlServer.Types.dll|bin\smo\Microsoft.SqlServer.Types.resources.dll|bin\smo\Microsoft.SqlServer.VSTAScriptingLib.dll|bin\smo\Microsoft.SqlServer.VulnerabilityAssessment.Model.dll|bin\smo\Microsoft.SqlServer.WebServiceTask.dll|bin\smo\Microsoft.SqlServer.WMIDRTask.dll|bin\smo\Microsoft.SqlServer.WmiEnum.dll|bin\smo\Microsoft.SqlServer.WMIEWTask.dll|bin\smo\Microsoft.SqlServer.XE.Core.dll|bin\smo\Microsoft.SqlServer.XEvent.Configuration.dll|bin\smo\Microsoft.SqlServer.XEvent.dll|bin\smo\Microsoft.SqlServer.XEvent.dll.xoeg8du.partial|bin\smo\Microsoft.SqlServer.XEvent.Linq.dll|bin\smo\Microsoft.SqlServer.XEvent.Linq.dll.oc2ihg1.partial|bin\smo\Microsoft.SqlServer.XmlSrc.dll|bin\smo\Microsoft.SqlServer.XMLTask.dll|bin\smo\smo-deps.txt|bin\smo\sqlpackage.exe|bin\smo\sqlpackage.exe.config|bin\sqlcmd\SQLCMD.EXE|bin\sqlcmd\Resources\1033\license_SQLCMD.txt|bin\sqlcmd\Resources\1033\SQLCMD.rll|bin\third-party-licenses\sql-server-data-tools-license-terms-vs2017.md|bin\third-party-licenses\sql-server-data-tools-license-terms.md|bin\third-party-licenses\ThirdPartyNoticesDacFramework.rtf|bin\XESmartTarget\CommandLine.dll|bin\XESmartTarget\CsvHelper.dll|bin\XESmartTarget\DouglasCrockford.JsMin.dll|bin\XESmartTarget\NLog.dll|bin\XESmartTarget\NLog.dll.nlog|bin\XESmartTarget\SmartFormat.dll|bin\XESmartTarget\XESmartTarget.Core.dll|bin\XEtemplates\15%20Second%20IO%20Error.xml|bin\XEtemplates\Activity%20Detail%20Tracking.xml|bin\XEtemplates\Activity%20Tracking.xml|bin\XEtemplates\AlwaysOn%20Health%20Enhanced.xml|bin\XEtemplates\Blocked%20Process%20Report.xml|bin\XEtemplates\Connection%20Detail%20Tracking.xml|bin\XEtemplates\Connection%20Tracking.xml|bin\XEtemplates\Count%20Query%20Locks.xml|bin\XEtemplates\Database%20File%20IO.xml|bin\XEtemplates\Database%20Health%202012.xml|bin\XEtemplates\Database%20Health%202014.xml|bin\XEtemplates\Database%20Health%202016%20and%20Above.xml|bin\XEtemplates\Deadlock%20Graphs.xml|bin\XEtemplates\Default%20Profiler%20Trace.xml|bin\XEtemplates\Deprecated%20Feature%20Usage.xml|bin\XEtemplates\Function%20Executions.xml|bin\XEtemplates\Index%20Page%20Splits.xml|bin\XEtemplates\Log%20File%20IO%20Detail%20Tracking.xml|bin\XEtemplates\Log%20File%20IO%20Tracking.xml|bin\XEtemplates\Login%20Tracker.xml|bin\XEtemplates\Long%20Running%20Queries.xml|bin\XEtemplates\Overly%20Complex%20Queries.xml|bin\XEtemplates\Profiler%20SP%20Counts.xml|bin\XEtemplates\Profiler%20Standard.xml|bin\XEtemplates\Profiler%20TSQL%20Duration.xml|bin\XEtemplates\Profiler%20TSQL%20Locks.xml|bin\XEtemplates\Profiler%20TSQL%20Replay.xml|bin\XEtemplates\Profiler%20TSQL%20SPs.xml|bin\XEtemplates\Profiler%20TSQL.xml|bin\XEtemplates\Profiler%20Tuning.xml|bin\XEtemplates\Queries%20and%20Resources.xml|bin\XEtemplates\Query%20Batch%20Detail%20Sampling.xml|bin\XEtemplates\Query%20Batch%20Sampling.xml|bin\XEtemplates\Query%20Batch%20Tracking.xml|bin\XEtemplates\Query%20Detail%20Sampling.xml|bin\XEtemplates\Query%20Detail%20Tracking.xml|bin\XEtemplates\Query%20Timeouts.xml|bin\XEtemplates\Query%20Wait%20Statistics%20Detail.xml|bin\XEtemplates\Query%20Wait%20Statistics.xml|bin\XEtemplates\Stored%20Procedure%20Parameters.xml|en-us\about_dbatools.help.txt|en-us\about_dbatools_importoptions.help.txt|en-us\about_dbatools_support.help.txt|functions\Add-DbaComputerCertificate.ps1|functions\Add-DbaPfDataCollectorCounter.ps1|functions\Add-DbaRegisteredServer.ps1|functions\Add-DbaRegisteredServerGroup.ps1|functions\Backup-DbaDatabase.ps1|functions\Backup-DbaDatabaseMasterKey.ps1|functions\Backup-DbaDbCertificate.ps1|functions\Clear-DbaPlanCache.ps1|functions\Clear-DbaSqlConnectionPool.ps1|functions\Clear-DbaWaitStatistics.ps1|functions\Connect-DbaInstance.ps1|functions\ConvertTo-DbaDataTable.ps1|functions\ConvertTo-DbaXESession.ps1|functions\Copy-DbaAgentAlert.ps1|functions\Copy-DbaAgentCategory.ps1|functions\Copy-DbaAgentJob.ps1|functions\Copy-DbaAgentOperator.ps1|functions\Copy-DbaAgentProxyAccount.ps1|functions\Copy-DbaAgentSharedSchedule.ps1|functions\Copy-DbaBackupDevice.ps1|functions\Copy-DbaCentralManagementServer.ps1|functions\Copy-DbaCredential.ps1|functions\Copy-DbaCustomError.ps1|functions\Copy-DbaDatabase.ps1|functions\Copy-DbaDatabaseAssembly.ps1|functions\Copy-DbaDatabaseMail.ps1|functions\Copy-DbaEndpoint.ps1|functions\Copy-DbaExtendedEvent.ps1|functions\Copy-DbaLinkedServer.ps1|functions\Copy-DbaLogin.ps1|functions\Copy-DbaQueryStoreConfig.ps1|functions\Copy-DbaResourceGovernor.ps1|functions\Copy-DbaServerAudit.ps1|functions\Copy-DbaServerAuditSpecification.ps1|functions\Copy-DbaServerTrigger.ps1|functions\Copy-DbaSpConfigure.ps1|functions\Copy-DbaSqlDataCollector.ps1|functions\Copy-DbaSqlPolicyManagement.ps1|functions\Copy-DbaSqlServerAgent.ps1|functions\Copy-DbaSsisCatalog.ps1|functions\Copy-DbaSysDbUserObject.ps1|functions\Copy-DbaTableData.ps1|functions\Copy-DbaXESessionTemplate.ps1|functions\Disable-DbaAgHadr.ps1|functions\Disable-DbaForceNetworkEncryption.ps1|functions\Disable-DbaTraceFlag.ps1|functions\Dismount-DbaDatabase.ps1|functions\Enable-DbaAgHadr.ps1|functions\Enable-DbaForceNetworkEncryption.ps1|functions\Enable-DbaTraceFlag.ps1|functions\Expand-DbaTLogResponsibly.ps1|functions\Export-DbaAvailabilityGroup.ps1|functions\Export-DbaDacpac.ps1|functions\Export-DbaDiagnosticQuery.ps1|functions\Export-DbaExecutionPlan.ps1|functions\Export-DbaLogin.ps1|functions\Export-DbaPfDataCollectorSetTemplate.ps1|functions\Export-DbaRegisteredServer.ps1|functions\Export-DbaScript.ps1|functions\Export-DbaSpConfigure.ps1|functions\Export-DbaUser.ps1|functions\Export-DbaXECsv.ps1|functions\Export-DbaXESessionTemplate.ps1|functions\Find-DbaAgentJob.ps1|functions\Find-DbaBackup.ps1|functions\Find-DbaCommand.ps1|functions\Find-DbaDatabase.ps1|functions\Find-DbaDbGrowthEvent.ps1|functions\Find-DbaDisabledIndex.ps1|functions\Find-DbaDuplicateIndex.ps1|functions\Find-DbaInstance.ps1|functions\Find-DbaLoginInGroup.ps1|functions\Find-DbaOrphanedFile.ps1|functions\Find-DbaSimilarTable.ps1|functions\Find-DbaStoredProcedure.ps1|functions\Find-DbaTrigger.ps1|functions\Find-DbaUnusedIndex.ps1|functions\Find-DbaUserObject.ps1|functions\Find-DbaView.ps1|functions\Format-DbaBackupInformation.ps1|functions\Get-DbaAgDatabase.ps1|functions\Get-DbaAgentAlert.ps1|functions\Get-DbaAgentJob.ps1|functions\Get-DbaAgentJobCategory.ps1|functions\Get-DbaAgentJobHistory.ps1|functions\Get-DbaAgentJobOutputFile.ps1|functions\Get-DbaAgentJobStep.ps1|functions\Get-DbaAgentLog.ps1|functions\Get-DbaAgentOperator.ps1|functions\Get-DbaAgentProxy.ps1|functions\Get-DbaAgentSchedule.ps1|functions\Get-DbaAgHadr.ps1|functions\Get-DbaAgListener.ps1|functions\Get-DbaAgReplica.ps1|functions\Get-DbaAvailabilityGroup.ps1|functions\Get-DbaAvailableCollation.ps1|functions\Get-DbaBackupDevice.ps1|functions\Get-DbaBackupHistory.ps1|functions\Get-DbaBackupInformation.ps1|functions\Get-DbaClientAlias.ps1|functions\Get-DbaClientProtocol.ps1|functions\Get-DbaClusterNode.ps1|functions\Get-DbaCmConnection.ps1|functions\Get-DbaCmObject.ps1|functions\Get-DbaComputerCertificate.ps1|functions\Get-DbaComputerSystem.ps1|functions\Get-DbaConfig.ps1|functions\Get-DbaConfigValue.ps1|functions\Get-DbaConnection.ps1|functions\Get-DbaCpuUsage.ps1|functions\Get-DbaCredential.ps1|functions\Get-DbaCustomError.ps1|functions\Get-DbaDatabase.ps1|functions\Get-DbaDatabaseAssembly.ps1|functions\Get-DbaDatabaseEncryption.ps1|functions\Get-DbaDatabaseFile.ps1|functions\Get-DbaDatabaseMasterKey.ps1|functions\Get-DbaDatabasePartitionFunction.ps1|functions\Get-DbaDatabasePartitionScheme.ps1|functions\Get-DbaDatabaseSpace.ps1|functions\Get-DbaDatabaseState.ps1|functions\Get-DbaDatabaseUdf.ps1|functions\Get-DbaDatabaseUser.ps1|functions\Get-DbaDatabaseView.ps1|functions\Get-DbaDbCertificate.ps1|functions\Get-DbaDbCheckConstraint.ps1|functions\Get-DbaDbCompression.ps1|functions\Get-DbaDbExtentDiff.ps1|functions\Get-DbaDbForeignKey.ps1|functions\Get-DbaDbMailHistory.ps1|functions\Get-DbaDbMailLog.ps1|functions\Get-DbaDbPageInfo.ps1|functions\Get-DbaDbQueryStoreOptions.ps1|functions\Get-DbaDbRecoveryModel.ps1|functions\Get-DbaDbRole.ps1|functions\Get-DbaDbSnapshot.ps1|functions\Get-DbaDbStoredProcedure.ps1|functions\Get-DbaDbVirtualLogFile.ps1|functions\Get-DbaDefaultPath.ps1|functions\Get-DbaDependency.ps1|functions\Get-DbaDetachedDatabaseInfo.ps1|functions\Get-DbaDiskSpace.ps1|functions\Get-DbaDistributor.ps1|functions\Get-DbaDump.ps1|functions\Get-DbaEndpoint.ps1|functions\Get-DbaErrorLog.ps1|functions\Get-DbaErrorLogConfig.ps1|functions\Get-DbaEstimatedCompletionTime.ps1|functions\Get-DbaExecutionPlan.ps1|functions\Get-DbaFile.ps1|functions\Get-DbaForceNetworkEncryption.ps1|functions\Get-DbaHelpIndex.ps1|functions\Get-DbaLastBackup.ps1|functions\Get-DbaLastGoodCheckDb.ps1|functions\Get-DbaLinkedServer.ps1|functions\Get-DbaLocaleSetting.ps1|functions\Get-DbaLogin.ps1|functions\Get-DbaLogShippingError.ps1|functions\Get-DbaMaintenanceSolutionLog.ps1|functions\Get-DbaMaxMemory.ps1|functions\Get-DbaMemoryUsage.ps1|functions\Get-DbaMsdtc.ps1|functions\Get-DbaNetworkActivity.ps1|functions\Get-DbaNetworkCertificate.ps1|functions\Get-DbaOpenTransaction.ps1|functions\Get-DbaOperatingSystem.ps1|functions\Get-DbaOrphanUser.ps1|functions\Get-DbaPageFileSetting.ps1|functions\Get-DbaPermission.ps1|functions\Get-DbaPfAvailableCounter.ps1|functions\Get-DbaPfDataCollector.ps1|functions\Get-DbaPfDataCollectorCounter.ps1|functions\Get-DbaPfDataCollectorCounterSample.ps1|functions\Get-DbaPfDataCollectorSet.ps1|functions\Get-DbaPfDataCollectorSetTemplate.ps1|functions\Get-DbaPlanCache.ps1|functions\Get-DbaPolicy.ps1|functions\Get-DbaPrivilege.ps1|functions\Get-DbaProcess.ps1|functions\Get-DbaQueryExecutionTime.ps1|functions\Get-DbaRegisteredServer.ps1|functions\Get-DbaRegisteredServerGroup.ps1|functions\Get-DbaRegisteredServerStore.ps1|functions\Get-DbaResourceGovernorClassifierFunction.ps1|functions\Get-DbaRestoreHistory.ps1|functions\Get-DbaRoleMember.ps1|functions\Get-DbaRunningJob.ps1|functions\Get-DbaSchemaChangeHistory.ps1|functions\Get-DbaServerAudit.ps1|functions\Get-DbaServerAuditSpecification.ps1|functions\Get-DbaServerInstallDate.ps1|functions\Get-DbaServerProtocol.ps1|functions\Get-DbaServerRole.ps1|functions\Get-DbaSpConfigure.ps1|functions\Get-DbaSpn.ps1|functions\Get-DbaSqlBuildReference.ps1|functions\Get-DbaSqlFeature.ps1|functions\Get-DbaSqlInstanceProperty.ps1|functions\Get-DbaSqlInstanceUserOption.ps1|functions\Get-DbaSqlManagementObject.ps1|functions\Get-DbaSqlModule.ps1|functions\Get-DbaSqlProductKey.ps1|functions\Get-DbaSqlRegistryRoot.ps1|functions\Get-DbaSqlService.ps1|functions\Get-DbaSsisEnvironmentVariable.ps1|functions\Get-DbaSsisExecutionHistory.ps1|functions\Get-DbaStartupParameter.ps1|functions\Get-DbaSuspectPage.ps1|functions\Get-DbaTable.ps1|functions\Get-DbaTcpPort.ps1|functions\Get-DbaTempdbUsage.ps1|functions\Get-DbatoolsLog.ps1|functions\Get-DbaTopResourceUsage.ps1|functions\Get-DbaTrace.ps1|functions\Get-DbaTraceFlag.ps1|functions\Get-DbaTrigger.ps1|functions\Get-DbaUptime.ps1|functions\Get-DbaUserLevelPermission.ps1|functions\Get-DbaWaitingTask.ps1|functions\Get-DbaWaitResource.ps1|functions\Get-DbaWaitStatistic.ps1|functions\Get-DbaWindowsLog.ps1|functions\Get-DbaXEObject.ps1|functions\Get-DbaXESession.ps1|functions\Get-DbaXESessionTarget.ps1|functions\Get-DbaXESessionTargetFile.ps1|functions\Get-DbaXESessionTemplate.ps1|functions\Get-DbaXESmartTarget.ps1|functions\Get-DbaXEStore.ps1|functions\Import-DbaCsvToSql.ps1|functions\Import-DbaPfDataCollectorSetTemplate.ps1|functions\Import-DbaRegisteredServer.ps1|functions\Import-DbaSpConfigure.ps1|functions\Import-DbaXESessionTemplate.ps1|functions\Install-DbaFirstResponderKit.ps1|functions\Install-DbaMaintenanceSolution.ps1|functions\Install-DbaWatchUpdate.ps1|functions\Install-DbaWhoIsActive.ps1|functions\Invoke-DbaAdvancedRestore.ps1|functions\Invoke-DbaBalanceDataFiles.ps1|functions\Invoke-DbaCycleErrorLog.ps1|functions\Invoke-DbaDatabaseClone.ps1|functions\Invoke-DbaDatabaseShrink.ps1|functions\Invoke-DbaDatabaseUpgrade.ps1|functions\Invoke-DbaDbDecryptObject.ps1|functions\Invoke-DbaDiagnosticQuery.ps1|functions\Invoke-DbaLogShipping.ps1|functions\Invoke-DbaLogShippingRecovery.ps1|functions\Invoke-DbaPfRelog.ps1|functions\Invoke-DbaSqlQuery.ps1|functions\Invoke-DbaWhoisActive.ps1|functions\Invoke-DbaXEReplay.ps1|functions\Invoke-SqlCmd2.ps1|functions\Measure-DbaBackupThroughput.ps1|functions\Measure-DbaDiskSpaceRequirement.ps1|functions\Mount-DbaDatabase.ps1|functions\Move-DbaRegisteredServer.ps1|functions\Move-DbaRegisteredServerGroup.ps1|functions\New-DbaAgentJob.ps1|functions\New-DbaAgentJobCategory.ps1|functions\New-DbaAgentJobStep.ps1|functions\New-DbaAgentProxy.ps1|functions\New-DbaAgentSchedule.ps1|functions\New-DbaClientAlias.ps1|functions\New-DbaCmConnection.ps1|functions\New-DbaComputerCertificate.ps1|functions\New-DbaCredential.ps1|functions\New-DbaDatabaseMasterKey.ps1|functions\New-DbaDbCertificate.ps1|functions\New-DbaDbSnapshot.ps1|functions\New-DbaDbUser.ps1|functions\New-DbaLogin.ps1|functions\New-DbaPublishProfile.ps1|functions\New-DbaScriptingOption.ps1|functions\New-DbaServiceMasterKey.ps1|functions\New-DbaSqlConnectionString.ps1|functions\New-DbaSqlConnectionStringBuilder.ps1|functions\New-DbaSqlDirectory.ps1|functions\New-DbaSsisCatalog.ps1|functions\New-DbatoolsSupportPackage.ps1|functions\New-DbaXESession.ps1|functions\New-DbaXESmartCsvWriter.ps1|functions\New-DbaXESmartEmail.ps1|functions\New-DbaXESmartQueryExec.ps1|functions\New-DbaXESmartReplay.ps1|functions\New-DbaXESmartTableWriter.ps1|functions\Publish-DbaDacpac.ps1|functions\Read-DbaAuditFile.ps1|functions\Read-DbaBackupHeader.ps1|functions\Read-DbaTraceFile.ps1|functions\Read-DbaTransactionLog.ps1|functions\Read-DbaXEFile.ps1|functions\Register-DbaConfig.ps1|functions\Remove-DbaAgentJob.ps1|functions\Remove-DbaAgentJobCategory.ps1|functions\Remove-DbaAgentJobStep.ps1|functions\Remove-DbaAgentSchedule.ps1|functions\Remove-DbaBackup.ps1|functions\Remove-DbaClientAlias.ps1|functions\Remove-DbaCmConnection.ps1|functions\Remove-DbaComputerCertificate.ps1|functions\Remove-DbaDatabase.ps1|functions\Remove-DbaDatabaseMasterKey.ps1|functions\Remove-DbaDatabaseSafely.ps1|functions\Remove-DbaDbCertificate.ps1|functions\Remove-DbaDbSnapshot.ps1|functions\Remove-DbaDbUser.ps1|functions\Remove-DbaLogin.ps1|functions\Remove-DbaNetworkCertificate.ps1|functions\Remove-DbaOrphanUser.ps1|functions\Remove-DbaPfDataCollectorCounter.ps1|functions\Remove-DbaPfDataCollectorSet.ps1|functions\Remove-DbaRegisteredServer.ps1|functions\Remove-DbaRegisteredServerGroup.ps1|functions\Remove-DbaSpn.ps1|functions\Remove-DbaTrace.ps1|functions\Remove-DbaXESession.ps1|functions\Remove-DbaXESmartTarget.ps1|functions\Rename-DbaDatabase.ps1|functions\Rename-DbaLogin.ps1|functions\Repair-DbaOrphanUser.ps1|functions\Repair-DbaServerName.ps1|functions\Reset-DbaAdmin.ps1|functions\Resolve-DbaNetworkName.ps1|functions\Restart-DbaSqlService.ps1|functions\Restore-DbaBackupFromDirectory.ps1|functions\Restore-DbaDatabase.ps1|functions\Restore-DbaDbCertificate.ps1|functions\Restore-DbaDbSnapshot.ps1|functions\Save-DbaDiagnosticQueryScript.ps1|functions\Select-DbaBackupInformation.ps1|functions\Select-DbaObject.ps1|functions\Set-DbaAgentAlert.ps1|functions\Set-DbaAgentJob.ps1|functions\Set-DbaAgentJobCategory.ps1|functions\Set-DbaAgentJobOutputFile.ps1|functions\Set-DbaAgentJobStep.ps1|functions\Set-DbaAgentSchedule.ps1|functions\Set-DbaCmConnection.ps1|functions\Set-DbaConfig.ps1|functions\Set-DbaDatabaseOwner.ps1|functions\Set-DbaDatabaseState.ps1|functions\Set-DbaDbCompression.ps1|functions\Set-DbaDbQueryStoreOptions.ps1|functions\Set-DbaDbRecoveryModel.ps1|functions\Set-DbaErrorLogConfig.ps1|functions\Set-DbaJobOwner.ps1|functions\Set-DbaLogin.ps1|functions\Set-DbaMaxDop.ps1|functions\Set-DbaMaxMemory.ps1|functions\Set-DbaNetworkCertificate.ps1|functions\Set-DbaPowerPlan.ps1|functions\Set-DbaPrivilege.ps1|functions\Set-DbaSpConfigure.ps1|functions\Set-DbaSpn.ps1|functions\Set-DbaStartupParameter.ps1|functions\Set-DbaTcpPort.ps1|functions\Set-DbaTempDbConfiguration.ps1|functions\Show-DbaDatabaseList.ps1|functions\Show-DbaServerFileSystem.ps1|functions\Start-DbaAgentJob.ps1|functions\Start-DbaMigration.ps1|functions\Start-DbaPfDataCollectorSet.ps1|functions\Start-DbaPowerBi.ps1|functions\Start-DbaSqlService.ps1|functions\Start-DbaTrace.ps1|functions\Start-DbaXESession.ps1|functions\Start-DbaXESmartTarget.ps1|functions\Stop-DbaAgentJob.ps1|functions\Stop-DbaPfDataCollectorSet.ps1|functions\Stop-DbaProcess.ps1|functions\Stop-DbaSqlService.ps1|functions\Stop-DbaTrace.ps1|functions\Stop-DbaXESession.ps1|functions\Stop-DbaXESmartTarget.ps1|functions\Sync-DbaLoginPermission.ps1|functions\Test-DbaBackupInformation.ps1|functions\Test-DbaCmConnection.ps1|functions\Test-DbaConnection.ps1|functions\Test-DbaConnectionAuthScheme.ps1|functions\Test-DbaDatabaseCollation.ps1|functions\Test-DbaDatabaseCompatibility.ps1|functions\Test-DbaDatabaseOwner.ps1|functions\Test-DbaDbCompression.ps1|functions\Test-DbaDbVirtualLogFile.ps1|functions\Test-DbaDiskAlignment.ps1|functions\Test-DbaDiskAllocation.ps1|functions\Test-DbaDiskSpeed.ps1|functions\Test-DbaIdentityUsage.ps1|functions\Test-DbaJobOwner.ps1|functions\Test-DbaLastBackup.ps1|functions\Test-DbaLinkedServerConnection.ps1|functions\Test-DbaLoginPassword.ps1|functions\Test-DbaLogShippingStatus.ps1|functions\Test-DbaMaxDop.ps1|functions\Test-DbaMaxMemory.ps1|functions\Test-DbaMigrationConstraint.ps1|functions\Test-DbaNetworkLatency.ps1|functions\Test-DbaOptimizeForAdHoc.ps1|functions\Test-DbaPowerPlan.ps1|functions\Test-DbaRecoveryModel.ps1|functions\Test-DbaServerName.ps1|functions\Test-DbaSpn.ps1|functions\Test-DbaSqlBuild.ps1|functions\Test-DbaSqlManagementObject.ps1|functions\Test-DbaSqlPath.ps1|functions\Test-DbaTempDbConfiguration.ps1|functions\Test-DbaWindowsLogin.ps1|functions\Uninstall-DbaWatchUpdate.ps1|functions\Update-DbaPowerBiDataSource.ps1|functions\Update-DbaSqlServiceAccount.ps1|functions\Update-dbatools.ps1|functions\Watch-DbaDbLogin.ps1|functions\Watch-DbaUpdate.ps1|functions\Watch-DbaXESession.ps1|functions\Write-DbaDataTable.ps1|internal\configurations\configuration.ps1|internal\configurations\settings\assets.ps1|internal\configurations\settings\commands.ps1|internal\configurations\settings\computermanagement.ps1|internal\configurations\settings\formatting.ps1|internal\configurations\settings\import.ps1|internal\configurations\settings\logging.ps1|internal\configurations\settings\paths.ps1|internal\configurations\settings\remoting.ps1|internal\configurations\settings\sql.ps1|internal\configurations\settings\tabexpansion.ps1|internal\configurations\settings\userinteraction.ps1|internal\configurations\validation\bool.ps1|internal\configurations\validation\consolecolor.ps1|internal\configurations\validation\datetime.ps1|internal\configurations\validation\double.ps1|internal\configurations\validation\integer.ps1|internal\configurations\validation\integer0to9.ps1|internal\configurations\validation\integerpositive.ps1|internal\configurations\validation\long.ps1|internal\configurations\validation\sizestyle.ps1|internal\configurations\validation\string.ps1|internal\configurations\validation\timespan.ps1|internal\dynamicparams\alert.ps1|internal\dynamicparams\alertcategory.ps1|internal\dynamicparams\audit.ps1|internal\dynamicparams\auditspecification.ps1|internal\dynamicparams\availabilitygroup.ps1|internal\dynamicparams\backupdevice.ps1|internal\dynamicparams\config.ps1|internal\dynamicparams\configname.ps1|internal\dynamicparams\credential.ps1|internal\dynamicparams\customerror.ps1|internal\dynamicparams\database.ps1|internal\dynamicparams\endpoint.ps1|internal\dynamicparams\group.ps1|internal\dynamicparams\instanceproperty.ps1|internal\dynamicparams\job.ps1|internal\dynamicparams\jobcategory.ps1|internal\dynamicparams\linkedserver.ps1|internal\dynamicparams\login.ps1|internal\dynamicparams\mailaccount.ps1|internal\dynamicparams\mailprofile.ps1|internal\dynamicparams\mailserver.ps1|internal\dynamicparams\operator.ps1|internal\dynamicparams\operatorcategory.ps1|internal\dynamicparams\perfmontemplate.ps1|internal\dynamicparams\processHostname.ps1|internal\dynamicparams\processProgram.ps1|internal\dynamicparams\processSpid.ps1|internal\dynamicparams\proxyaccount.ps1|internal\dynamicparams\resourcepool.ps1|internal\dynamicparams\schedule.ps1|internal\dynamicparams\servertrigger.ps1|internal\dynamicparams\session.ps1|internal\dynamicparams\snapshot.ps1|internal\dynamicparams\sqlinstance.ps1|internal\dynamicparams\tag.ps1|internal\dynamicparams\xesessiontemplate.ps1|internal\functions\Connect-AsServer.ps1|internal\functions\Connect-SqlInstance.ps1|internal\functions\Convert-ByteToHexString.ps1|internal\functions\Convert-DbaMessageException.ps1|internal\functions\Convert-DbaMessageLevel.ps1|internal\functions\Convert-DbaMessageTarget.ps1|internal\functions\Convert-DbVersionToSqlVersion.ps1|internal\functions\Convert-HexStringToByte.ps1|internal\functions\Disconnect-Regserver.ps1|internal\functions\Get-BackupAncientHistory.ps1|internal\functions\Get-CodePage.ps1|internal\functions\Get-DbaADObject.ps1|internal\functions\Get-DbaDbPhysicalFile.ps1|internal\functions\Get-DbaFileStreamFolder.ps1|internal\functions\Get-DbaMessageLevelModifier.ps1|internal\functions\Get-DbaRunspace.ps1|internal\functions\Get-DbaService.ps1|internal\functions\Get-DBASQLServiceErrorMessage.ps1|internal\functions\Get-DecryptedObject.ps1|internal\functions\Get-DirectoryRestoreFile.ps1|internal\functions\Get-ErrorMessage.ps1|internal\functions\Get-JobList.ps1|internal\functions\Get-Language.ps1|internal\functions\Get-OfflineSqlFileStructure.ps1|internal\functions\Get-PasswordHash.ps1|internal\functions\Get-RegServerGroupReverseParse.ps1|internal\functions\Get-RegServerParent.ps1|internal\functions\Get-RestoreContinuableDatabase.ps1|internal\functions\Get-SaLoginName.ps1|internal\functions\Get-SmoServerForDynamicParams.ps1|internal\functions\Get-SqlCmdVars.ps1|internal\functions\Get-SqlDefaultPaths.ps1|internal\functions\Get-SqlDefaultSpConfigure.ps1|internal\functions\Get-SqlFileStructure.ps1|internal\functions\Get-SqlSaLogin.ps1|internal\functions\Get-XpDirTreeRestoreFile.ps1|internal\functions\Import-DbaCmdlet.ps1|internal\functions\Invoke-Command2.ps1|internal\functions\Invoke-DbaDatabaseCorruption.ps1|internal\functions\Invoke-DbaDiagnosticQueryScriptParser.ps1|internal\functions\Invoke-DbaSqlAsync.ps1|internal\functions\Invoke-ManagedComputerCommand.ps1|internal\functions\Invoke-Parallel.ps1|internal\functions\Invoke-SmoCheck.ps1|internal\functions\Invoke-SteppablePipeline.ps1|internal\functions\Invoke-TagCommand.ps1|internal\functions\Join-AdminUnc.ps1|internal\functions\New-DbaLogShippingPrimaryDatabase.ps1|internal\functions\New-DbaLogShippingPrimarySecondary.ps1|internal\functions\New-DbaLogShippingSecondaryDatabase.ps1|internal\functions\New-DbaLogShippingSecondaryPrimary.ps1|internal\functions\New-DbaMessageLevelModifier.ps1|internal\functions\New-DbaTeppCompletionResult.ps1|internal\functions\Register-DbaConfigValidation.ps1|internal\functions\Register-DbaMaintenanceTask.ps1|internal\functions\Register-DbaMessageEvent.ps1|internal\functions\Register-DbaMessageTransform.ps1|internal\functions\Register-DbaRunspace.ps1|internal\functions\Register-DbaTeppArgumentCompleter.ps1|internal\functions\Register-DbaTeppInstanceCacheBuilder.ps1|internal\functions\Register-DbaTeppScriptBlock.ps1|internal\functions\Remove-DbaMessageLevelModifier.ps1|internal\functions\Remove-InvalidFileNameChars.ps1|internal\functions\Resolve-IpAddress.ps1|internal\functions\Resolve-NetBiosName.ps1|internal\functions\Resolve-SqlIpAddress.ps1|internal\functions\Select-DefaultView.ps1|internal\functions\Set-ServiceStartMode.ps1|internal\functions\Show-Notification.ps1|internal\functions\Start-DbaRunspace.ps1|internal\functions\Start-DbccCheck.ps1|internal\functions\Stop-DbaRunspace.ps1|internal\functions\Stop-Function.ps1|internal\functions\Test-Bound.ps1|internal\functions\Test-ComputerTarget.ps1|internal\functions\Test-DbaDeprecation.ps1|internal\functions\Test-DbaLsnChain.ps1|internal\functions\Test-DbaRestoreVersion.ps1|internal\functions\Test-ElevationRequirement.ps1|internal\functions\Test-FunctionInterrupt.ps1|internal\functions\Test-HostOSLinux.ps1|internal\functions\Test-PSRemoting.ps1|internal\functions\Test-SqlAgent.ps1|internal\functions\Test-SqlLoginAccess.ps1|internal\functions\Test-SqlQueryComplete.ps1|internal\functions\Test-SqlSa.ps1|internal\functions\Update-ServiceStatus.ps1|internal\functions\Update-SqlDbOwner.ps1|internal\functions\Update-SqlDbReadOnly.ps1|internal\functions\Update-SqlPermissions.ps1|internal\functions\Where-DbaObject.ps1|internal\functions\Write-HostColor.ps1|internal\maintenance\PSSession-Cleanup.ps1|internal\maintenance\tempcleanup.ps1|internal\maintenance\teppInsertTask.ps1|internal\scripts\cmdlets.ps1|internal\scripts\dbatools-maintenance.ps1|internal\scripts\insertTepp.ps1|internal\scripts\logfilescript.ps1|internal\scripts\message-transforms.ps1|internal\scripts\smoLibraryImport.ps1|internal\scripts\updateTeppAsync.ps1|optional\Compress-Archive.ps1|optional\Expand-Archive.ps1|optional\Get-GenericArgumentCompleter.ps1|optional\TabExpansionPlusPlus.ps1|tests\Add-DbaComputerCertificate.Tests.ps1|tests\Add-DbaPfDataCollectorCounter.Tests.ps1|tests\Add-DbaRegisteredServer.Tests.ps1|tests\Add-DbaRegisteredServerGroup.Tests.ps1|tests\appveyor.pester.ps1|tests\appveyor.post.ps1|tests\appveyor.prep.ps1|tests\appveyor.SQL2008R2SP2.ps1|tests\appveyor.SQL2016.ps1|tests\appveyor.SQL2017.ps1|tests\appveyor.sqlserver.ps1|tests\Backup-DbaDatabase.Tests.ps1|tests\Backup-DbaDatabaseMasterKey.Tests.ps1|tests\Backup-DbaDbCertificate.Tests.ps1|tests\Clear-DbaPlanCache.Tests.ps1|tests\Clear-DbaSqlConnectionPool.Tests.ps1|tests\Clear-DbaWaitStatistics.Tests.ps1|tests\Connect-DbaInstance.Tests.ps1|tests\Connect-SqlInstance.Tests.ps1|tests\constants.ps1|tests\ConvertTo-DbaDataTable.Tests.ps1|tests\ConvertTo-DbaXESession.Tests.ps1|tests\Copy-DbaAgentAlert.Tests.ps1|tests\Copy-DbaAgentCategory.Tests.ps1|tests\Copy-DbaAgentJob.Tests.ps1|tests\Copy-DbaAgentOperator.Tests.ps1|tests\Copy-DbaBackupDevice.Tests.ps1|tests\Copy-DbaCentralManagementServer.Tests.ps1|tests\Copy-DbaCredential.Tests.ps1|tests\Copy-DbaCustomError.Tests.ps1|tests\Copy-DbaDatabase.Tests.ps1|tests\Copy-DbaDatabaseAssembly.Tests.ps1|tests\Copy-DbaLinkedServer.Tests.ps1|tests\Copy-DbaLogin.Tests.ps1|tests\Copy-DbaResourceGovernor.Tests.ps1|tests\Copy-DbaServerTrigger.Tests.ps1|tests\Copy-DbaSpConfigure.Tests.ps1|tests\Copy-DbaTableData.Tests.ps1|tests\Copy-DbaXESessionTemplate.Tests.ps1|tests\dbatools.Tests.ps1|tests\Disable-DbaAgHadr.Tests.ps1|tests\Disable-DbaForceNetworkEncryption.Tests.ps1|tests\Disable-DbaTraceFlag.Tests.ps1|tests\Dismount-DbaDatabase.Tests.ps1|tests\Enable-DbaAgHadr.Tests.ps1|tests\Enable-DbaForceNetworkEncryption.Tests.ps1|tests\Enable-DbaTraceFlag.Tests.ps1|tests\Expand-DbaTLogResponsibly.Tests.ps1|tests\Export-DbaAvailabilityGroup.Tests.ps1|tests\Export-DbaDacpac.Tests.ps1|tests\Export-DbaDiagnosticQuery.Tests.ps1|tests\Export-DbaLogin.Tests.ps1|tests\Export-DbaPfDataCollectorSetTemplate.Tests.ps1|tests\Export-DbaRegisteredServer.Tests.ps1|tests\Export-DbaUser.Tests.ps1|tests\Export-DbaXESessionTemplate.Tests.ps1|tests\Find-DbaBackup.Tests.ps1|tests\Find-DbaDatabase.Tests.ps1|tests\Find-DbaDisabledIndex.Tests.ps1|tests\Find-DbaDuplicateIndex.Tests.ps1|tests\Find-DbaInstance.Tests.ps1|tests\Find-DbaOrphanedFile.Tests.ps1|tests\Find-DbaSimilarTable.Tests.ps1|tests\Format-DbaBackupInformation.Tests.ps1|tests\Get-DbaAgDatabase.Tests.ps1|tests\Get-DbaAgentAlert.Tests.ps1|tests\Get-DbaAgentJob.Tests.ps1|tests\Get-DbaAgentJobCategory.Tests.ps1|tests\Get-DbaAgentJobHistory.Tests.ps1|tests\Get-DbaAgentJobOutputFile.Tests.ps1|tests\Get-DbaAgentJobStep.Tests.ps1|tests\Get-DbaAgentLog.Tests.ps1|tests\Get-DbaAgentOperator.Tests.ps1|tests\Get-DbaAgHadr.Tests.ps1|tests\Get-DbaAgListener.Tests.ps1|tests\Get-DbaAgReplica.Tests.ps1|tests\Get-DbaAvailabilityGroup.Tests.ps1|tests\Get-DbaAvailableCollation.Tests.ps1|tests\Get-DbaBackupHistory.Tests.ps1|tests\Get-DbaBackupInformation.Tests.ps1|tests\Get-DbaClientAlias.Tests.ps1|tests\Get-DbaClientProtocol.Tests.ps1|tests\Get-DbaClusterNode.Tests.ps1|tests\Get-DbaCmObject.Tests.ps1|tests\Get-DbaComputerCertificate.Tests.ps1|tests\Get-DbaComputerSystem.Tests.ps1|tests\Get-DbaConfig.Tests.ps1|tests\Get-DbaConnection.Tests.ps1|tests\Get-DbaCredential.Tests.ps1|tests\Get-DbaDatabase.Tests.ps1|tests\Get-DbaDatabaseEncryption.Tests.ps1|tests\Get-DbaDatabaseFile.Tests.ps1|tests\Get-DbaDatabaseState.Tests.ps1|tests\Get-DbaDatabaseView.Tests.ps1|tests\Get-DbaDbCertificate.Tests.ps1|tests\Get-DbaDbCheckConstraint.Tests.ps1|tests\Get-DbaDbCompression.Tests.ps1|tests\Get-DbaDbForeignKey.Tests.ps1|tests\Get-DbaDbPageInfo.Tests.ps1|tests\Get-DbaDbQueryStoreOptions.Tests.ps1|tests\Get-DbaDbRecoveryModel.Tests.ps1|tests\Get-DbaDbRole.Tests.ps1|tests\Get-DbaDbSnapshot.Tests.ps1|tests\Get-DbaDbStoredProcedure.Tests.ps1|tests\Get-DbaDbVirtualLogFile.Tests.ps1|tests\Get-DbaDefaultPath.Tests.ps1|tests\Get-DbaDiskSpace.Tests.ps1|tests\Get-DbaDistributor.Tests.ps1|tests\Get-DbaDump.Tests.ps1|tests\Get-DbaErrorLog.Tests.ps1|tests\Get-DbaErrorLogConfig.Tests.ps1|tests\Get-DbaFile.Tests.ps1|tests\Get-DbaForceNetworkEncryption.Tests.ps1|tests\Get-DbaLastBackup.Tests.ps1|tests\Get-DbaLastGoodCheckDb.Tests.ps1|tests\Get-DbaLogin.Tests.ps1|tests\Get-DbaLogShippingError.Tests.ps1|tests\Get-DbaMaxMemory.Tests.ps1|tests\Get-DbaOpenTransaction.Tests.ps1|tests\Get-DbaOperatingSystem.Tests.ps1|tests\Get-DbaOrphanUser.Tests.ps1|tests\Get-DbaPermission.Tests.ps1|tests\Get-DbaPfAvailableCounter.Tests.ps1|tests\Get-DbaPfDataCollector.Tests.ps1|tests\Get-DbaPfDataCollectorCounter.Tests.ps1|tests\Get-DbaPfDataCollectorCounterSample.Tests.ps1|tests\Get-DbaPfDataCollectorSet.Tests.ps1|tests\Get-DbaPfDataCollectorSetTemplate.Tests.ps1|tests\Get-DbaPlanCache.Tests.ps1|tests\Get-DbaPolicy.Tests.ps1|tests\Get-DbaProcess.Tests.ps1|tests\Get-DbaRegisteredServer.Tests.ps1|tests\Get-DbaRegisteredServerGroup.Tests.ps1|tests\Get-DbaRegisteredServerStore.Tests.ps1|tests\Get-DbaResourceGovernorClassifierFunction.Tests.ps1|tests\Get-DbaRestoreHistory.Tests.ps1|tests\Get-DbaSchemaChangeHistory.Tests.ps1|tests\Get-DbaServerAudit.Tests.ps1|tests\Get-DbaServerProtocol.Tests.ps1|tests\Get-DbaServerRole.Tests.ps1|tests\Get-DbaSpConfigure.Tests.ps1|tests\Get-DbaSqlBuildReference.Tests.ps1|tests\Get-DbaSqlFeature.Tests.ps1|tests\Get-DbaSqlInstanceProperty.Tests.ps1|tests\Get-DbaSqlModule.Tests.ps1|tests\Get-DbaSqlRegistryRoot.Tests.ps1|tests\Get-DbaSqlService.Tests.ps1|tests\Get-DbaSuspectPage.Tests.ps1|tests\Get-DbaTopResourceUsage.Tests.ps1|tests\Get-DbaTrace.Tests.ps1|tests\Get-DbaTraceFlag.Tests.ps1|tests\Get-DbaUpTime.Tests.ps1|tests\Get-DbaUserLevelPermission.Tests.ps1|tests\Get-DbaWaitingTask.Tests.ps1|tests\Get-DbaWaitResource.Tests.ps1|tests\Get-DbaWaitStatistic.Tests.ps1|tests\Get-DbaXESession.Tests.ps1|tests\Get-DbaXESessionTarget.Tests.ps1|tests\Get-DbaXESessionTemplate.Tests.ps1|tests\Get-DirectoryRestoreFile.Tests.ps1|tests\Get-XpDirTreeRestoreFile.Tests.ps1|tests\Import-DbaPfDataCollectorSetTemplate.Tests.ps1|tests\Import-DbaRegisteredServer.Tests.ps1|tests\Import-DbaXESessionTemplate.Tests.ps1|tests\InModule.Help.Exceptions.ps1|tests\InModule.Help.Tests.ps1|tests\Install-DbaFirstResponderKit.Tests.ps1|tests\Install-DbaMaintenanceSolution.Tests.ps1|tests\Invoke-DbaBalanceDataFiles.Tests.ps1|tests\Invoke-DbaCycleErrorLog.Tests.ps1|tests\Invoke-DbaDatabaseClone.Tests.ps1|tests\Invoke-DbaDatabaseCorruption.Tests.ps1|tests\Invoke-DbaDbDecryptObject.Tests.ps1|tests\Invoke-DbaDiagnosticQuery.Tests.ps1|tests\Invoke-DbaLogShipping.Tests.ps1|tests\Invoke-DbaSqlQuery.Tests.ps1|tests\Invoke-SqlCmd2.Tests.ps1|tests\manual.pester.ps1|tests\Measure-DbaBackupThroughput.Tests.ps1|tests\Mount-DbaDatabase.Tests.ps1|tests\Move-DbaRegisteredServer.Tests.ps1|tests\Move-DbaRegisteredServerGroup.Tests.ps1|tests\New-DbaAgentJob.Tests.ps1|tests\New-DbaAgentJobCategory.Tests.ps1|tests\New-DbaAgentJobStep.Tests.ps1|tests\New-DbaAgentProxy.Tests.ps1|tests\New-DbaClientAlias.Tests.ps1|tests\New-DbaComputerCertificate.Tests.ps1|tests\New-DbaCredential.Tests.ps1|tests\New-DbaDbCertificate.Tests.ps1|tests\New-DbaDbSnapshot.Tests.ps1|tests\New-DbaLogin.Tests.ps1|tests\New-DbaPublishProfile.Tests.ps1|tests\New-DbaSqlConnectionStringBuilder.Tests.ps1|tests\New-DbaSsisCatalog.Tests.ps1|tests\pester.groups.ps1|tests\Publish-DbaDacpac.Tests.ps1|tests\Read-DbaAuditFile.Tests.ps1|tests\Read-DbaTraceFile.Tests.ps1|tests\Read-DbaXEFile.Tests.ps1|tests\Remove-DbaAgentJob.Tests.ps1|tests\Remove-DbaAgentJobCategory.Tests.ps1|tests\Remove-DbaBackup.Tests.ps1|tests\Remove-DbaClientAlias.Tests.ps1|tests\Remove-DbaComputerCertificate.Tests.ps1|tests\Remove-DbaDatabase.Tests.ps1|tests\Remove-DbaDatabaseSafely.Tests.ps1|tests\Remove-DbaDbCertificate.Tests.ps1|tests\Remove-DbaDbSnapshot.Tests.ps1|tests\Remove-DbaDbUser.Tests.ps1|tests\Remove-DbaLogin.Tests.ps1|tests\Remove-DbaPfDataCollectorCounter.Tests.ps1|tests\Remove-DbaPfDataCollectorSet.Tests.ps1|tests\Remove-DbaRegisteredServer.Tests.ps1|tests\Remove-DbaRegisteredServerGroup.Tests.ps1|tests\Remove-DbaTrace.Tests.ps1|tests\Remove-DbaXESession.Tests.ps1|tests\Rename-DbaLogin.Tests.ps1|tests\Repair-DbaOrphanUser.Tests.ps1|tests\Reset-DbaAdmin.Tests.ps1|tests\Restart-DbaSqlService.Tests.ps1|tests\Restore-DbaDatabase.Tests.ps1|tests\Restore-DbaDbCertificate.Tests.ps1|tests\Restore-DbaDbSnapshot.Tests.ps1|tests\Select-DbaBackupInformation.Tests.ps1|tests\Select-DbaObject.Tests.ps1|tests\Set-DbaAgentAlert.Tests.ps1|tests\Set-DbaAgentJobCategory.Tests.ps1|tests\Set-DbaDatabaseState.Tests.ps1|tests\Set-DbaDbCompression.Tests.ps1|tests\Set-DbaDbQueryStoreOptions.Tests.ps1|tests\Set-DbaDbRecoveryModel.Tests.ps1|tests\Set-DbaErrorLogConfig.Tests.ps1|tests\Set-DbaLogin.Tests.ps1|tests\Set-DbaMaxDop.Tests.ps1|tests\Set-DbaMaxMemory.Tests.ps1|tests\Set-DbaSpConfigure.Tests.ps1|tests\Start-DbaAgentJob.Tests.ps1|tests\Start-DbaPfDataCollectorSet.Tests.ps1|tests\Start-DbaSqlService.Tests.ps1|tests\Start-DbaTrace.Tests.ps1|tests\Start-DbaXESession.Tests.ps1|tests\Stop-DbaAgentJob.Tests.ps1|tests\Stop-DbaPfDataCollectorSet.Tests.ps1|tests\Stop-DbaProcess.Tests.ps1|tests\Stop-DbaSqlService.Tests.ps1|tests\Stop-DbaTrace.Tests.ps1|tests\Stop-DbaXESession.Tests.ps1|tests\Stop-Function.Tests.ps1|tests\Test-DbaBackupInformation.Tests.ps1|tests\Test-DbaCmConnection.Tests.ps1|tests\Test-DbaConnection.Tests.ps1|tests\Test-DbaConnectionAuthScheme.Tests.ps1|tests\Test-DbaDatabaseCollation.Tests.ps1|tests\Test-DbaDatabaseOwner.Tests.ps1|tests\Test-DbaDbCompression.Tests.ps1|tests\Test-DbaDbVirtualLogFile.Tests.ps1|tests\Test-DbaDiskAlignment.Tests.ps1|tests\Test-DbaDiskSpeed.Tests.ps1|tests\Test-DbaIdentityUsage.Tests.ps1|tests\Test-DbaJobOwner.Tests.ps1|tests\Test-DbaLastBackup.Tests.ps1|tests\Test-DbaLinkedServerConnection.Tests.ps1|tests\Test-DbaLoginPassword.test.ps1|tests\Test-DbaLogShippingStatus.Tests.ps1|tests\Test-DbaLsnChain.Tests.ps1|tests\Test-DbaMaxDop.Tests.ps1|tests\Test-DbaMaxMemory.Tests.ps1|tests\Test-DbaMigrationConstraint.Tests.ps1|tests\Test-DbaNetworkLatency.Tests.ps1|tests\Test-DbaOptimizeForAdHoc.Tests.ps1|tests\Test-DbaPowerPlan.Tests.ps1|tests\Test-DbaRecoveryModel.Tests.ps1|tests\Test-DbaServerName.Tests.ps1|tests\Test-DbaSpn.Tests.ps1|tests\Test-DbaSqlManagementObject.Tests.ps1|tests\Test-DbaSqlPath.Tests.ps1|tests\Test-DbaTempDbConfiguration.Tests.ps1|tests\Test-DbaWindowsLogin.Tests.ps1|tests\Test-PSRemoting.Tests.ps1|tests\Update-DbaSqlServiceAccount.Tests.ps1|tests\Watch-DbaDbLogin.Tests.ps1|tests\Watch-DbaXESession.Tests.ps1|tests\Write-DbaDataTable.Tests.ps1|tests\ObjectDefinitions\BackupRestore\RawInput\broken_chain.json|tests\ObjectDefinitions\BackupRestore\RawInput\chkptLSN-ne-firstLSN.json|tests\ObjectDefinitions\BackupRestore\RawInput\CleanFormatDbaInformation.xml|tests\ObjectDefinitions\BackupRestore\RawInput\ContinuePointTest.xml|tests\ObjectDefinitions\BackupRestore\RawInput\DiffIssues.json|tests\ObjectDefinitions\BackupRestore\RawInput\DiffRestore.json|tests\ObjectDefinitions\BackupRestore\RawInput\EmptyTLogData.json|tests\ObjectDefinitions\BackupRestore\RawInput\RestoreCommaIssues.json|tests\ObjectDefinitions\BackupRestore\RawInput\RestoreTimeClean.xml|tests\ObjectDefinitions\BackupRestore\RawInput\TLogBWFirstLastLsn.json|xml\dbatools.Format.ps1xml|xml\dbatools.Types.ps1xml</S>
          </En>
          <En>
            <S N="Key">requireLicenseAcceptance</S>
            <S N="Value">True</S>
          </En>
        </DCT>
      </Obj>
      <S N="InstalledLocation">C:\Users\appveyor\AppData\Local\Temp\1\73bb355a-e5a4-4426-b1a5-223ae3fab1a5\dbatools\0.9.380</S>
    </MS>
  </Obj>
</Objs>
tools\dbatools\readme.md
# dbatools

> PowerShell Core (aka PowerShell 6+) went GA (generally available) and supported in January of 2018. Please be aware at this time we do not support this version of PowerShell. It is on the roadmap but at this time there is no estimated time we will be supporting it.

<img align="left" src=https://blog.netnerds.net/wp-content/uploads/2016/05/dbatools.png alt="dbatools logo">  dbatools is sort of like a command-line SQL Server Management Studio. The project initially started out as Start-SqlMigration.ps1, but has now grown into a collection of [over 400 commands](https://dbatools.io/commands) that help automate SQL Server tasks and encourage best practices.

Got ideas for new commands? Please propose them as [issues](https://dbatools.io/issues) and let us know what you'd like to see. Bug reports should also be filed under this repository's [issues](https://github.com/sqlcollaborative/dbatools/issues) section.

There's also over 1400 of us on the [SQL Server Community Slack](https://sqlcommunity.slack.com) in the #dbatools channel. Need an invite? Check out the [self-invite page](https://dbatools.io/slack/). Drop by if you'd like to chat about dbatools or even [join the team](https://dbatools.io/team)!

![PowerShell Gallery](https://img.shields.io/powershellgallery/dt/dbatools.svg?style=plastic) [![AppVeyor](https://img.shields.io/appveyor/tests/sqlcollaborative/dbatools/master.svg)](https://ci.appveyor.com/project/sqlcollaborative/dbatools/history) [![GitHub forks](https://img.shields.io/github/forks/badges/shields.svg?style=plastic&label=Forks)](https://github.com/sqlcollaborative/dbatools/network/members) ![GitHub issues](https://img.shields.io/github/issues-raw/sqlcollaborative/dbatools.svg?style=plastic) ![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/sqlcollaborative/dbatools.svg?style=plastic)

## Installer
This module is now in the PowerShell Gallery. Run the following from an administrative prompt to install:
```powershell
Install-Module dbatools
```

Or if you don't have a version of PowerShell that supports the Gallery, you can install it manually:
```powershell
Invoke-Expression (Invoke-WebRequest https://dbatools.io/in)
```

Note: please only use `Invoke-Expression (Invoke-WebRequest..)` from sources you trust, like us 👍

## Usage scenarios

In addition to the simple things you can do in SSMS (like starting a job), we've also read a whole bunch of docs and came up with commands that do nifty things quickly.

* Lost sysadmin access and need to regain entry to your SQL Server? Use [Reset-DbaAdmin](http://dbatools.io/Reset-DbaAdmin).
* Need to easily test your backups? Use [Test-DbaLastBackup](http://dbatools.io/Test-DbaLastBackup).
* SPN management got you down? Use [our suite of SPN commands](http://dbatools.io/schwifty) to find which SPNs are missing and easily add them.
* Got so many databases you can't keep track? Congrats on your big ol' environment! Use [Find-DbaDatabase](http://dbatools.io/Find-DbaDatabase) to easily find your database.

## Usage examples

As previously mentioned, dbatools now offers [over 400 commands](https://dbatools.io/commands)! [Here are some of the ones we highlight at conferences](https://gist.github.com/potatoqualitee/e8932b64aeb6ef404e252d656b6318a2) - PowerShell v3 and above required. (See below for important information about alternative logins and specifying SQL Server ports).

```powershell
# Set some vars
$new = "localhost\sql2016"
$old = $instance = "localhost"
$allservers = $old, $new

# Alternatively, use Registered Servers 
$allservers = Get-DbaRegisteredServer -SqlInstance $instance

# Need to restore a database? It can be as simple as this:
Restore-DbaDatabase -SqlInstance $instance -Path "C:\temp\AdventureWorks2012-Full Database Backup.bak"

# Use Ola Hallengren's backup script? We can restore an *ENTIRE INSTANCE* with just one line
Get-ChildItem -Directory \\workstation\backups\sql2012 | Restore-DbaDatabase -SqlInstance $new

# What about if you need to make a backup? And you are logging in with alternative credentials?
Get-DbaDatabase -SqlInstance $new -SqlCredential (Get-Credential sa) | Backup-DbaDatabase

# Testing your backups is crazy easy! 
Start-Process https://dbatools.io/Test-DbaLastBackup
Test-DbaLastBackup -SqlInstance $old | Out-GridView

# But what if you want to test your backups on a different server?
Test-DbaLastBackup -SqlInstance $old -Destination $new | Out-GridView

# Nowadays, we don't just backup databases. Now, we're backing up logins
Export-DbaLogin -SqlInstance $instance -Path C:\temp\logins.sql
Invoke-Item C:\temp\logins.sql

# And Agent Jobs
Get-DbaAgentJob -SqlInstance $old | Export-DbaScript -Path C:\temp\jobs.sql

# What if you just want to script out your restore?
Get-ChildItem -Directory \\workstation\backups\subset\ | Restore-DbaDatabase -SqlInstance $new -OutputScriptOnly -WithReplace | Out-File -Filepath c:\temp\restore.sql
Invoke-Item c:\temp\restore.sql

# You've probably heard about how easy migrations can be with dbatools. Here's an example 
$startDbaMigrationSplat = @{
    Source = $old
    Destination = $new
    BackupRestore = $true
    NetworkShare = 'C:\temp'
    NoSysDbUserObjects = $true
    NoCredentials = $true
    NoBackupDevices = $true
    NoEndPoints = $true
}
		
Start-DbaMigration @startDbaMigrationSplat -Force | Select * | Out-GridView

# Know how snapshots used to be a PITA? Now they're super easy
New-DbaDatabaseSnapshot -SqlInstance $new -Database db1 -Name db1_snapshot
Get-DbaDatabaseSnapshot -SqlInstance $new
Get-DbaProcess -SqlInstance $new -Database db1 | Stop-DbaProcess
Restore-DbaFromDatabaseSnapshot -SqlInstance $new -Database db1 -Snapshot db1_snapshot
Remove-DbaDatabaseSnapshot -SqlInstance $new -Snapshot db1_snapshot # or -Database db1

# Have you tested your last good DBCC CHECKDB? We've got a command for that
$old | Get-DbaLastGoodCheckDb | Out-GridView

# Here's how you can find your integrity jobs and easily start them. Then, you can watch them run, and finally check your newest DBCC CHECKDB results
$old | Get-DbaAgentJob | Where Name -match integrity | Start-DbaAgentJob
$old | Get-DbaRunningJob
$old | Get-DbaLastGoodCheckDb | Out-GridView

# Our new build website is super useful!
Start-Process https://dbatools.io/builds

# You can use the same JSON the website uses to check the status of your own environment
$allservers | Get-DbaSqlBuildReference

# We evaluated 37,545 SQL Server stored procedures on 9 servers in 8.67 seconds!
$new | Find-DbaStoredProcedure -Pattern dbatools

# Have an employee who is leaving? Find all of their objects.
$allservers | Find-DbaUserObject -Pattern ad\jdoe | Out-GridView
 
# Find detached databases, by example
Detach-DbaDatabase -SqlInstance $instance -Database AdventureWorks2012
Find-DbaOrphanedFile -SqlInstance $instance | Out-GridView

# Check out how complete our sp_configure command is
Get-DbaSpConfigure -SqlInstance $new | Out-GridView

# Easily update configuration values
Set-DbaSpConfigure -SqlInstance $new -ConfigName XPCmdShellEnabled -Value $true

# DB Cloning too!
Invoke-DbaDatabaseClone -SqlInstance $new -Database db1 -CloneDatabase db1_clone | Out-GridView

# Read and watch XEvents
Get-DbaXEventSession -SqlInstance $new -Session system_health | Read-DbaXEventFile
Get-DbaXEventSession -SqlInstance $new -Session system_health | Read-DbaXEventFile | Select -ExpandProperty Fields | Out-GridView

# Reset-DbaAdmin
Reset-DbaAdmin -SqlInstance $instance -Login sqladmin -Verbose
Get-DbaDatabase -SqlInstance $instance -SqlCredential (Get-Credential sqladmin)

# sp_whoisactive
Install-DbaWhoIsActive -SqlInstance $instance -Database master
Invoke-DbaWhoIsActive -SqlInstance $instance -ShowOwnSpid -ShowSystemSpids

# Diagnostic query!
$instance | Invoke-DbaDiagnosticQuery -UseSelectionHelper | Export-DbaDiagnosticQuery -Path $home
Invoke-Item $home

# Ola, yall
$instance | Install-DbaMaintenanceSolution -ReplaceExisting -BackupLocation C:\temp -InstallJobs

# Startup parameters
Get-DbaStartupParameter -SqlInstance $instance
Set-DbaStartupParameter -SqlInstance $instance -SingleUser -WhatIf

# Database clone
Invoke-DbaDatabaseClone -SqlInstance $new -Database dbwithsprocs -CloneDatabase dbwithsprocs_clone

# Schema change and Pester tests
Get-DbaSchemaChangeHistory -SqlInstance $new -Database tempdb

# Get Db Free Space AND write it to table
Get-DbaDatabaseSpace -SqlInstance $instance | Out-GridView
Get-DbaDatabaseSpace -SqlInstance $instance -IncludeSystemDB | Out-DbaDataTable | Write-DbaDataTable -SqlInstance $instance -Database tempdb -Table DiskSpaceExample -AutoCreateTable
Invoke-Sqlcmd2 -ServerInstance $instance -Database tempdb -Query 'SELECT * FROM dbo.DiskSpaceExample' | Out-GridView

# History
Get-Command -Module dbatools *history*

# Identity usage
Test-DbaIdentityUsage -SqlInstance $instance | Out-GridView

# Test/Set SQL max memory
$allservers | Get-DbaMaxMemory
$allservers | Test-DbaMaxMemory | Format-Table
$allservers | Test-DbaMaxMemory | Where-Object { $_.SqlMaxMB -gt $_.TotalMB } | Set-DbaMaxMemory -WhatIf
Set-DbaMaxMemory -SqlInstance $instance -MaxMb 1023

# Testing sql server linked server connections
Test-DbaLinkedServerConnection -SqlInstance $instance

# See protocols
Get-DbaServerProtocol -ComputerName $instance | Out-GridView

# Reads trace files - default trace by default
Read-DbaTraceFile -SqlInstance $instance | Out-GridView

# don't have remoting access? Explore the filesystem. Uses master.sys.xp_dirtree
Get-DbaFile -SqlInstance $instance

# Test your SPNs and see what'd happen if you'd set them
$servers | Test-DbaSpn | Out-GridView
$servers | Test-DbaSpn | Out-GridView -PassThru | Set-DbaSpn -WhatIf

# Get Virtual Log File information
Get-DbaDbVirtualLogFile -SqlInstance $new -Database db1
Get-DbaDbVirtualLogFile -SqlInstance $new -Database db1 | Measure-Object

```

## Important Note

#### Alternative SQL Credentials

By default, all SQL-based commands will login to SQL Server using Trusted/Windows Authentication. To use alternative credentials, including SQL Logins or alternative Windows credentials, use the `-SqlCredential`. This parameter accepts the results of `Get-Credential` which generates a [PSCredential](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.security/get-credential?view=powershell-5.1) object.

```powershell
Get-DbaDatabase -SqlInstance sql2017 -SqlCredential (Get-Credential sqladmin)
```

<a href="https://dbatools.io/wp-content/uploads/2016/05/cred.jpg"><img class="aligncenter size-full wp-image-6897" src="https://dbatools.io/wp-content/uploads/2016/05/cred.jpg" alt="" width="322" height="261" /></a>

A few (or maybe just one - [Restore-DbaDatabase](/Restore-DbaDatabase)), you can also use `-AzureCredential`.

#### Alternative Windows Credentials

For commands that access Windows such as [Get-DbaDiskSpace](/Get-DbaDiskSpace), you will pass the `-Credential` parameter.

```powershell
$cred = Get-Credential ad\winadmin
Get-DbaDiskSpace -ComputerName sql2017 -Credential $cred
```

To store credentials to disk, please read more at [Jaap Brasser's blog](https://www.jaapbrasser.com/quickly-and-securely-storing-your-credentials-powershell/).

#### Servers with custom ports

If you use non-default ports and SQL Browser is disabled, you can access servers using a semicolon (functionality we've added) or a comma (the way Microsoft does it).

```powershell
-SqlInstance sql2017:55559
-SqlInstance 'sql2017,55559'
```

Note that PowerShell sees commas as arrays, so you must surround the host name with quotes.

## Support

dbatools aims to support as many configurations as possible, including

* PowerShell v3 and above
* SQL Server 2000 - 2017
* Express - Datacenter Edition
* Clustered and stand-alone instances
* Windows and SQL authentication
* Default and named instances
* Multiple instances on one server
* Auto-populated parameters for command-line completion (think -Database and -Login)

Read more at our website at [dbatools.io](https://dbatools.io)

## Contributing

Want to contribute to the project? We'd love to have you! Visit our [contributing.md](https://github.com/sqlcollaborative/dbatools/blob/master/contributing.md) for a jump start.
tools\dbatools\tests\Add-DbaComputerCertificate.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Certificate is added properly" {
        $results = Add-DbaComputerCertificate -Path $script:appveyorlabrepo\certificates\localhost.crt -Confirm:$false

        It "Should show the proper thumbprint has been added" {
            $results.Thumbprint | Should Be "29C469578D6C6211076A09CEE5C5797EEA0C2713"
        }

        It "Should be in LocalMachine\My Cert Store" {
            $results.PSParentPath | Should Be "Microsoft.PowerShell.Security\Certificate::LocalMachine\My"
        }

        Remove-DbaComputerCertificate -Thumbprint 29C469578D6C6211076A09CEE5C5797EEA0C2713 -Confirm:$false
    }
}
tools\dbatools\tests\Add-DbaPfDataCollectorCounter.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    BeforeEach {
        $null = Get-DbaPfDataCollectorSetTemplate -Template 'Long Running Queries' | Import-DbaPfDataCollectorSetTemplate |
        Get-DbaPfDataCollector | Get-DbaPfDataCollectorCounter -Counter '\LogicalDisk(*)\Avg. Disk Queue Length' | Remove-DbaPfDataCollectorCounter -Confirm:$false
    }
    AfterAll {
        $null = Get-DbaPfDataCollectorSet -CollectorSet 'Long Running Queries' | Remove-DbaPfDataCollectorSet -Confirm:$false
    }
    Context "Verifying command returns all the required results" {
        It "returns the correct values" {
            $results = Get-DbaPfDataCollectorSet -CollectorSet 'Long Running Queries' | Get-DbaPfDataCollector | Add-DbaPfDataCollectorCounter -Counter '\LogicalDisk(*)\Avg. Disk Queue Length'
            $results.DataCollectorSet | Should Be 'Long Running Queries'
            $results.Name | Should Be '\LogicalDisk(*)\Avg. Disk Queue Length'
        }
    }
}
tools\dbatools\tests\Add-DbaRegisteredServer.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tag "IntegrationTests" {
    Context "Setup" {
        BeforeAll {
            $srvName = "dbatoolsci-server1"
            $group = "dbatoolsci-group1"
            $regSrvName = "dbatoolsci-server12"
            $regSrvDesc = "dbatoolsci-server123"
            $groupobject = Add-DbaRegisteredServerGroup -SqlInstance $script:instance1 -Name $group
        }
        AfterAll {
            Get-DbaRegisteredServer -SqlInstance $script:instance1, $script:instance2 | Where-Object Name -match dbatoolsci | Remove-DbaRegisteredServer -Confirm:$false
            Get-DbaRegisteredServerGroup -SqlInstance $script:instance1, $script:instance2 | Where-Object Name -match dbatoolsci | Remove-DbaRegisteredServerGroup -Confirm:$false
        }

        It "adds a registered server" {
            $results1 = Add-DbaRegisteredServer -SqlInstance $script:instance1 -ServerName $srvName
            $results1.Name | Should -Be $srvName
            $results1.ServerName | Should -Be $srvName
            $results1.SqlInstance | Should -Not -Be $null
        }
        It "adds a registered server with extended properties" {
            $results2 = Add-DbaRegisteredServer -SqlInstance $script:instance1 -ServerName $RegsrvName -Name $srvName -Group $groupobject -Description $regSrvDesc
            $results2.ServerName | Should -Be $regSrvName
            $results2.Description | Should -Be $regSrvDesc
            $results2.Name | Should -Be $srvName
            $results2.SqlInstance | Should -Not -Be $null
        }
    }
}
tools\dbatools\tests\Add-DbaRegisteredServerGroup.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tag "IntegrationTests" {
    Context "Setup" {
        BeforeAll {
            $group = "dbatoolsci-group1"
            $group2 = "dbatoolsci-group2"
            $description = "group description"
        }
        AfterAll {
            Get-DbaRegisteredServerGroup -SqlInstance $script:instance1 | Where-Object Name -match dbatoolsci | Remove-DbaRegisteredServerGroup -Confirm:$false
        }
        
        It "adds a registered server group" {
            $results = Add-DbaRegisteredServerGroup -SqlInstance $script:instance1 -Name $group
            $results.Name | Should -Be $group
            $results.SqlInstance | Should -Not -Be $null
        }
        It "adds a registered server group with extended properties" {
            $results = Add-DbaRegisteredServerGroup -SqlInstance $script:instance1 -Name $group2 -Description $description
            $results.Name | Should -Be $group2
            $results.Description | Should -Be $description
            $results.SqlInstance | Should -Not -Be $null
        }
        It "supports hella pipe" {
            $results = Get-DbaRegisteredServerGroup -SqlInstance $script:instance1 -Id 1 | Add-DbaRegisteredServerGroup -Name dbatoolsci-first | Add-DbaRegisteredServerGroup -Name dbatoolsci-second | Add-DbaRegisteredServerGroup -Name dbatoolsci-third | Add-DbaRegisteredServer -ServerName dbatoolsci-test -Description ridiculous
            $results.Group | Should -Be 'dbatoolsci-first\dbatoolsci-second\dbatoolsci-third'
        }
    }
}
tools\dbatools\tests\appveyor.pester.ps1
<#
.SYNOPSIS
This script will invoke Pester tests, then serialize XML results and pull them in appveyor.yml

.DESCRIPTION
Internal function that creates SMO server object.

.PARAMETER Finalize
If Finalize is specified, we collect XML output, upload tests, and indicate build errors

.PARAMETER PSVersion
The version of PS

.PARAMETER TestFile
The output file

.PARAMETER ProjectRoot
The appveyor project root

.PARAMETER ModuleBase
The location of the module

.PARAMETER IncludeCoverage
Calculates coverage and sends it to codecov.io

.EXAMPLE
.\appveyor.pester.ps1
Executes the test

.EXAMPLE
.\appveyor.pester.ps1 -Finalize
Finalizes the tests
#>
param (
    [switch]$Finalize,
    $PSVersion = $PSVersionTable.PSVersion.Major,
    $TestFile = "TestResultsPS$PSVersion.xml",
    $ProjectRoot = $ENV:APPVEYOR_BUILD_FOLDER,
    $ModuleBase = $ProjectRoot,
    [switch]$IncludeCoverage
)

# Move to the project root
Set-Location $ModuleBase
# required to calculate coverage
$global:dbatools_dotsourcemodule = $true
$dbatools_serialimport = $true
#removes previously imported dbatools, if any
Remove-Module dbatools -ErrorAction Ignore
#imports the psm1 to be able to use internal functions in tests
Import-Module "$ModuleBase\dbatools.psm1"
#imports the module making sure DLL is loaded ok
Import-Module "$ModuleBase\dbatools.psd1"

Update-TypeData -AppendPath "$ModuleBase\xml\dbatools.types.ps1xml"
Start-Sleep 5

function Get-CoverageIndications($Path, $ModuleBase) {
    # takes a test file path and figures out what to analyze for coverage (i.e. dependencies)
    $CBHRex = [regex]'(?smi)<#(.*)#>'
    $everything = (Get-Module dbatools).ExportedCommands.Values
    $everyfunction = $everything.Name
    $funcs = @()
    $leaf = Split-Path $path -Leaf
    # assuming Get-DbaFoo.Tests.ps1 wants coverage for "Get-DbaFoo"
    # but allowing also Get-DbaFoo.one.Tests.ps1 and Get-DbaFoo.two.Tests.ps1
    $func_name += ($leaf -replace '^([^.]+)(.+)?.Tests.ps1', '$1')
    if ($func_name -in $everyfunction) {
        $funcs += $func_name
        $f = $everything | Where-Object Name -eq $func_name
        $source = $f.Definition
        $CBH = $CBHRex.match($source).Value
        $cmdonly = $source.Replace($CBH, '')
        foreach ($e in $everyfunction) {
            # hacky, I know, but every occurrence of any function plus a space kinda denotes usage !?
            $searchme = "$e "
            if ($cmdonly.contains($searchme)) {
                $funcs += $e
            }
        }
    }
    $testpaths = @()
    $allfiles = Get-ChildItem -File -Path "$ModuleBase\internal\functions", "$ModuleBase\functions" -Filter '*.ps1'
    foreach ($f in $funcs) {
        # exclude always used functions ?!
        if ($f -in ('Connect-SqlInstance', 'Select-DefaultView', 'Stop-Function', 'Write-Message')) { continue }
        # can I find a correspondence to a physical file (again, on the convenience of having Get-DbaFoo.ps1 actually defining Get-DbaFoo)?
        $res = $allfiles | Where-Object { $_.Name.Replace('.ps1', '') -eq $f }
        if ($res.count -gt 0) {
            $testpaths += $res.FullName
        }
    }
    return @() + ($testpaths | Select-Object -Unique)
}

function Get-CodecovReport($Results, $ModuleBase) {
    #handle coverage https://docs.codecov.io/reference#upload
    $report = @{'coverage' = @{}}
    #needs correct casing to do the replace
    $ModuleBase = (Resolve-Path $ModuleBase).Path
    # things we wanna a report for (and later backfill if not tested)
    $allfiles = Get-ChildItem -File -Path "$ModuleBase\internal\functions", "$ModuleBase\functions" -Filter '*.ps1'

    $missed = $results.CodeCoverage | Select-Object -ExpandProperty MissedCommands | Sort-Object -Property File, Line -Unique
    $hits = $results.CodeCoverage | Select-Object -ExpandProperty HitCommands | Sort-Object -Property File, Line -Unique
    $LineCount = @{}
    $hits | ForEach-Object {
        $filename = $_.File.Replace("$ModuleBase\", '').Replace('\', '/')
        if ($filename -notin $report['coverage'].Keys) {
            $report['coverage'][$filename] = @{}
            $LineCount[$filename] = (Get-Content $_.File -Raw | Measure-Object -Line).Lines
        }
        $report['coverage'][$filename][$_.Line] = 1
    }

    $missed | ForEach-Object {
        $filename = $_.File.Replace("$ModuleBase\", '').Replace('\', '/')
        if ($filename -notin $report['coverage'].Keys) {
            $report['coverage'][$filename] = @{}
            $LineCount[$filename] = (Get-Content $_.File | Measure-Object -Line).Lines
        }
        if ($_.Line -notin $report['coverage'][$filename].Keys) {
            #miss only if not already covered
            $report['coverage'][$filename][$_.Line] = 0
        }
    }

    $newreport = @{'coverage' = [ordered]@{}}
    foreach ($fname in $report['coverage'].Keys) {
        $Linecoverage = [ordered]@{}
        for ($i = 1; $i -le $LineCount[$fname]; $i++) {
            if ($i -in $report['coverage'][$fname].Keys) {
                $Linecoverage["$i"] = $report['coverage'][$fname][$i]
            }
        }
        $newreport['coverage'][$fname] = $Linecoverage
    }

    #backfill it
    foreach ($target in $allfiles) {
        $target_relative = $target.FullName.Replace("$ModuleBase\", '').Replace('\', '/')
        if ($target_relative -notin $newreport['coverage'].Keys) {
            $newreport['coverage'][$target_relative] = @{"1" = $null}
        }
    }
    $newreport
}

function Send-CodecovReport($CodecovReport) {
    $params = @{}
    $params['branch'] = $env:APPVEYOR_REPO_BRANCH
    $params['service'] = "appveyor"
    $params['job'] = $env:APPVEYOR_ACCOUNT_NAME
    if ($params['job']) { $params['job'] += '/' + $env:APPVEYOR_PROJECT_SLUG }
    if ($params['job']) { $params['job'] += '/' + $env:APPVEYOR_BUILD_VERSION }
    $params['build'] = $env:APPVEYOR_JOB_ID
    $params['pr'] = $env:APPVEYOR_PULL_REQUEST_NUMBER
    $params['slug'] = $env:APPVEYOR_REPO_NAME
    $params['commit'] = $env:APPVEYOR_REPO_COMMIT
    Add-Type -AssemblyName System.Web
    $CodeCovParams = [System.Web.HttpUtility]::ParseQueryString([String]::Empty)
    $params.GetEnumerator() | Where-Object Value | ForEach-Object { $CodeCovParams.Add($_.Name, $_.Value) }
    $Request = [System.UriBuilder]('https://codecov.io/upload/v2')
    $Request.Query = $CodeCovParams.ToString()
    Invoke-RestMethod -Uri $Request.Uri -Method Post -InFile $CodecovReport -ContentType 'multipart/form-data'
}

function Get-TestsForScenario {
    param($Scenario, $AllTest)

    # does this scenario run an 'autodetect' ?
    if ($TestsRunGroups[$Scenario].StartsWith('autodetect_')[0]) {
        # exclude any test specifically tied to a non-autodetect scenario
        $TiedFunctions = ($TestsRunGroups.GetEnumerator() | Where-Object { $_.Value -notlike 'autodetect_*' }).Value
        $RemainingTests = $AllTests | Where-Object { ($_.Name -replace '^([^.]+)(.+)?.Tests.ps1', '$1') -notin $TiedFunctions }
        # and now scan for the instance string
        $ScanFor = $TestsRunGroups[$Scenario].Replace('autodetect_', '')
        #if ScanFor holds an array, search it in *and*
        $ScanForAll = $ScanFor.Split(',')
        # and exclude other instances in autodetect
        $ExcludeScanForRaw = @() + ($TestsRunGroups.GetEnumerator() | Where-Object { ($_.Name -ne $Scenario) -and ($_.Value -like 'autodetect_*') }).Value.Replace('autodetect_', '')
        $ScanTests = @()
        foreach ($test in $RemainingTests) {
            $testcontent = Get-Content $test -Raw
            $IncludeFlag = 0
            foreach ($piece in $ScanForAll) {
                if ($testcontent -like "*$piece*") {
                    $IncludeFlag += 1
                }
            }
            if ($IncludeFlag -eq $ScanForAll.Length) {
                #matched all pieces
                $ExcludeAll = 0
                foreach ($otherenv in $ExcludeScanForRaw) {
                    $ExcludeFlag = 0
                    $ExcludeScanForAll_ = $otherenv.split(',')
                    #honor includes before excludes
                    $ExcludeScanForAll = @()
                    foreach ($piece in $ExcludeScanForAll_) {
                        if ($piece -notin $ScanForAll) {
                            $ExcludeScanForAll += $piece
                        }
                    }
                    if ($ExcludeScanForAll.Length -eq 0) {
                        $ExcludeAll = 0
                        continue
                    }
                    foreach ($piece in $ExcludeScanForAll) {
                        if ($testContent -like "*$piece*") {
                            $ExcludeFlag += 1
                        }
                    }
                    if ($ExcludeFlag -eq $ExcludeScanForAll.Length) {
                        $ExcludeAll += 1
                    }
                }
                if ($ExcludeAll -eq 0) {
                    $ScanTests += $test
                }
            }
        }
        $AllScenarioTests = $ScanTests
    }
    else {
        $AllScenarioTests = $AllTests | Where-Object { ($_.Name -replace '\.Tests\.ps1$', '') -in $TestsRunGroups[$Scenario] }
    }
    return $AllScenarioTests
}


if (-not $Finalize) {
    # Invoke pester.groups.ps1 to know which tests to run
    . "$ModuleBase\tests\pester.groups.ps1"
    # retrieve all .Tests.
    $AllDbatoolsTests = Get-ChildItem -File -Path "$ModuleBase\tests\*.Tests.ps1"
    # exclude "disabled"
    $AllTests = $AllDbatoolsTests | Where-Object { ($_.Name -replace '^([^.]+)(.+)?.Tests.ps1', '$1') -notin $TestsRunGroups['disabled'] }
    # only in appveyor, disable uncooperative tests
    $AllTests = $AllTests | Where-Object { ($_.Name -replace '^([^.]+)(.+)?.Tests.ps1', '$1') -notin $TestsRunGroups['appveyor_disabled'] }

    # Inspect special words
    $TestsToRunMessage = "$($env:APPVEYOR_REPO_COMMIT_MESSAGE) $($env:APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED)"
    $TestsToRunRegex = [regex] '(?smi)\(do (?<do>[^)]+)\)'
    $TestsToRunMatch = $TestsToRunRegex.Match($TestsToRunMessage).Groups['do'].Value
    if ($TestsToRunMatch.Length -gt 0) {
        $TestsToRun = "*$TestsToRunMatch*"
        $AllTests = $AllTests | Where-Object { ($_.Name -replace '\.Tests\.ps1$', '') -like $TestsToRun }
        Write-Host -ForegroundColor DarkGreen "Commit message: Reduced to $($AllTests.Count) out of $($AllDbatoolsTests.Count) tests"
        if ($AllTests.Count -eq 0) {
            throw "something went wrong, nothing to test"
        }
    }
    else {
        $TestsToRun = "*.Tests.*"
    }

    # do we have a scenario ?
    if ($env:SCENARIO) {
        # if so, do we have a group with tests to run ?
        if ($env:SCENARIO -in $TestsRunGroups.Keys) {
            $AllScenarioTests = Get-TestsForScenario -scenario $env:SCENARIO -AllTest $AllTests
        }
        else {
            $AllTestsToExclude = @()
            $validScenarios = $TestsRunGroups.Keys | Where-Object { $_ -notin @('disabled', 'appveyor_disabled') }
            foreach ($k in $validScenarios) {
                $AllTestsToExclude += Get-TestsForScenario -scenario $k -AllTest $AllTests
            }
            $AllScenarioTests = $AllTests | Where-Object { $_ -notin $AllTestsToExclude }
        }
    }
    else {
        $AllScenarioTests = $AllTests
    }

    Write-Host -ForegroundColor DarkGreen "Test Groups   : Reduced to $($AllScenarioTests.Count) out of $($AllDbatoolsTests.Count) tests"
    if ($AllTests.Count -eq 0 -and $AllScenarioTests.Count -eq 0) {
        throw "something went wrong, nothing to test"
    }
}

#Run a test with the current version of PowerShell
#Make things faster by removing most output
if (-not $Finalize) {
    Import-Module Pester
    Set-Variable ProgressPreference -Value SilentlyContinue
    if ($AllScenarioTests.Count -eq 0) {
        Write-Host -ForegroundColor DarkGreen "Nothing to do in this scenario"
        return
    }
    # invoking a single invoke-pester consumes too much memory, let's go file by file
    $AllTestsWithinScenario = Get-ChildItem -File -Path $AllScenarioTests
    $Counter = 0
    foreach ($f in $AllTestsWithinScenario) {
        $Counter += 1
        $PesterSplat = @{
            'Script'   = $f.FullName
            'Show'     = 'None'
            'PassThru' = $true
        }
        #opt-in
        if ($IncludeCoverage) {
            $CoverFiles = Get-CoverageIndications -Path $f -ModuleBase $ModuleBase
            $PesterSplat['CodeCoverage'] = $CoverFiles
            $PesterSplat['CodeCoverageOutputFile'] = "$ModuleBase\PesterCoverage$Counter.xml"
        }
        # Pester 4.0 outputs already what file is being ran. If we remove write-host from every test, we can time
        # executions for each test script (i.e. Executing Get-DbaFoo .... Done (40 seconds))
        Add-AppveyorTest -Name $f.Name -Framework NUnit -FileName $f.FullName -Outcome Running
        $PesterRun = Invoke-Pester @PesterSplat
        $PesterRun | Export-Clixml -Path "$ModuleBase\PesterResults$PSVersion$Counter.xml"
        Update-AppveyorTest -Name $f.Name -Framework NUnit -FileName $f.FullName -Outcome Passed -Duration $PesterRun.Time.TotalMilliseconds
    }
}
else {
    # Unsure why we're uploading so I removed it for now
    <#
    #If finalize is specified, check for failures and  show status
    $allfiles = Get-ChildItem -Path $ModuleBase\*Results*.xml | Select-Object -ExpandProperty FullName
    Write-Output "Finalizing results and collating the following files:"
    Write-Output ($allfiles | Out-String)
    #Upload results for test page
    Get-ChildItem -Path "$ModuleBase\TestResultsPS*.xml" | Foreach-Object {
        $Address = "https://ci.appveyor.com/api/testresults/nunit/$($env:APPVEYOR_JOB_ID)"
        $Source = $_.FullName
        Write-Output "Uploading files: $Address $Source"
        (New-Object System.Net.WebClient).UploadFile($Address, $Source)
        Write-Output "You can download it from https://ci.appveyor.com/api/buildjobs/$($env:APPVEYOR_JOB_ID)/tests"
    }
    #>
    #What failed? How many tests did we run ?
    $results = @(Get-ChildItem -Path "$ModuleBase\PesterResults*.xml" | Import-Clixml)
    #$totalcount = $results | Select-Object -ExpandProperty TotalCount | Measure-Object -Sum | Select-Object -ExpandProperty Sum
    $failedcount = $results | Select-Object -ExpandProperty FailedCount | Measure-Object -Sum | Select-Object -ExpandProperty Sum
    if ($failedcount -gt 0) {
        $faileditems = $results | Select-Object -ExpandProperty TestResult | Where-Object { $_.Passed -notlike $True }
        if ($faileditems) {
            Write-Warning "Failed tests summary:"
            $faileditems | ForEach-Object {
                $name = $_.Name
                [pscustomobject]@{
                    Describe = $_.Describe
                    Context  = $_.Context
                    Name     = "It $name"
                    Result   = $_.Result
                    Message  = $_.FailureMessage
                }
            } | Sort-Object Describe, Context, Name, Result, Message | Format-List
            throw "$failedcount tests failed."
        }
    }
    #opt-in
    if ($IncludeCoverage) {
        $CodecovReport = Get-CodecovReport -Results $results -ModuleBase $ModuleBase
        $CodecovReport | ConvertTo-Json -Depth 4 -Compress | Out-File -FilePath "$ModuleBase\PesterResultsCoverage.json" -Encoding utf8
    }
}
tools\dbatools\tests\appveyor.post.ps1
Add-AppveyorTest -Name "appveyor.post" -Framework NUnit -FileName "appveyor.post.ps1" -Outcome Running
$sw = [system.diagnostics.stopwatch]::startNew()
Write-Host -Object "appveyor.post: Sending coverage data" -ForeGroundColor DarkGreen
Push-AppveyorArtifact PesterResultsCoverage.json -FileName "PesterResultsCoverage"
codecov -f PesterResultsCoverage.json --flag "ps,$($env:SCENARIO.toLower())" | Out-Null
# DLL unittests only in default scenario
if($env:SCENARIO -eq 'default') {
  Write-Host -Object "appveyor.post: DLL unittests"  -ForeGroundColor DarkGreen
  OpenCover.Console.exe `
    -register:user `
    -target:"vstest.console.exe" `
    -targetargs:"/logger:Appveyor bin\projects\dbatools\dbatools.Tests\bin\Debug\dbatools.Tests.dll" `
    -output:"coverage.xml" `
    -filter:"+[dbatools]*" `
    -returntargetcode
  Push-AppveyorArtifact coverage.xml -FileName "OpenCover C# Report"
  codecov -f "coverage.xml" --flag "dll,$($env:SCENARIO.toLower())" | Out-Null
}
$sw.Stop()
Update-AppveyorTest -Name "appveyor.post" -Framework NUnit -FileName "appveyor.post.ps1" -Outcome Passed -Duration $sw.ElapsedMilliseconds
tools\dbatools\tests\appveyor.prep.ps1
Add-AppveyorTest -Name "appveyor.prep" -Framework NUnit -FileName "appveyor.prep.ps1" -Outcome Running
$sw = [system.diagnostics.stopwatch]::startNew()
Write-Host -Object "appveyor.prep: Cloning lab materials"  -ForegroundColor DarkGreen
git clone -q --branch=master --depth=1 https://github.com/sqlcollaborative/appveyor-lab.git C:\github\appveyor-lab

#Get codecov (to upload coverage results)
Write-Host -Object "appveyor.prep: Install codecov" -ForegroundColor DarkGreen
choco install codecov | Out-Null

#Get PSScriptAnalyzer (to check warnings)
Write-Host -Object "appveyor.prep: Install PSScriptAnalyzer" -ForegroundColor DarkGreen
Install-Module -Name PSScriptAnalyzer -Force -SkipPublisherCheck | Out-Null

#Get Pester (to run tests)
Write-Host -Object "appveyor.prep: Install Pester" -ForegroundColor DarkGreen
choco install pester | Out-Null

#Get opencover.portable (to run DLL tests)
Write-Host -Object "appveyor.prep: Install opencover.portable" -ForegroundColor DarkGreen
choco install opencover.portable | Out-Null

$sw.Stop()
Update-AppveyorTest -Name "appveyor.prep" -Framework NUnit -FileName "appveyor.prep.ps1" -Outcome Passed -Duration $sw.ElapsedMilliseconds

tools\dbatools\tests\appveyor.SQL2008R2SP2.ps1
$indent = '...'
Write-Host -Object "$indent Running $PSCommandpath" -ForegroundColor DarkGreen
$dbatools_serialimport = $true
Import-Module C:\github\dbatools\dbatools.psd1
Start-Sleep 5

# This script spins up the 2008R2SP2 instance and the relative setup

$sqlinstance = "localhost\SQL2008R2SP2"
$instance = "SQL2008R2SP2"
$port = "1433"

Write-Host -Object "$indent Setting up AppVeyor Services" -ForegroundColor DarkGreen
Set-Service -Name SQLBrowser -StartupType Automatic -WarningAction SilentlyContinue
Start-Service SQLBrowser -ErrorAction SilentlyContinue -WarningAction SilentlyContinue


Write-Host -Object "$indent Changing the port on $instance to $port" -ForegroundColor DarkGreen
$wmi = New-Object Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer
$uri = "ManagedComputer[@Name='$env:COMPUTERNAME']/ ServerInstance[@Name='$instance']/ServerProtocol[@Name='Tcp']"
$Tcp = $wmi.GetSmoObject($uri)
foreach ($ipAddress in $Tcp.IPAddresses) {
    $ipAddress.IPAddressProperties["TcpDynamicPorts"].Value = ""
    $ipAddress.IPAddressProperties["TcpPort"].Value = $port
}
$Tcp.Alter()
Write-Host -Object "$indent Starting $instance" -ForegroundColor DarkGreen
Restart-Service "MSSQL`$$instance" -WarningAction SilentlyContinue -Force
$server = Connect-DbaInstance -SqlInstance $sqlinstance
$server.Configuration.RemoteDacConnectionsEnabled.ConfigValue = $true
$server.Configuration.Alter()
$null = Set-DbaStartupParameter -SqlInstance $sqlinstance -TraceFlagsOverride -TraceFlags 7806 -Confirm:$false -ErrorAction SilentlyContinue -EnableException
Restart-Service "MSSQL`$SQL2008R2SP2" -WarningAction SilentlyContinue -Force
$server = Connect-DbaInstance -SqlInstance $sqlinstance
$server.Configuration.RemoteDacConnectionsEnabled.ConfigValue = $true
$server.Configuration.Alter()

do {
    Start-Sleep 1
    $null = (& sqlcmd -S "$sqlinstance" -b -Q "select 1" -d master)
}
while ($lastexitcode -ne 0 -and $t++ -lt 10)

Write-Host -Object "$indent Executing startup scripts for SQL Server 2008" -ForegroundColor DarkGreen
# Add some jobs to the sql2008r2sp2 instance (1433 = default)
foreach ($file in (Get-ChildItem C:\github\appveyor-lab\sql2008-startup\*.sql -Recurse -ErrorAction SilentlyContinue)) {
    Invoke-Sqlcmd2 -ServerInstance $sqlinstance -InputFile $file
}
tools\dbatools\tests\appveyor.SQL2016.ps1
$indent = '...'
Write-Host -Object "Running $PSCommandpath" -ForegroundColor DarkGreen
$dbatools_serialimport = $true
Import-Module C:\github\dbatools\dbatools.psd1
Start-Sleep 5
# This script spins up the 2016 instance and the relative setup

$sqlinstance = "localhost\SQL2016"
$instance = "SQL2016"
$port = "14333"

Write-Host -Object "$indent Setting up AppVeyor Services" -ForegroundColor DarkGreen
Set-Service -Name SQLBrowser -StartupType Automatic -WarningAction SilentlyContinue
Set-Service -Name "SQLAgent`$$instance" -StartupType Automatic -WarningAction SilentlyContinue
Start-Service SQLBrowser -ErrorAction SilentlyContinue -WarningAction SilentlyContinue


Write-Host -Object "$indent Changing the port on $instance to $port" -ForegroundColor DarkGreen
$wmi = New-Object Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer
$uri = "ManagedComputer[@Name='$env:COMPUTERNAME']/ ServerInstance[@Name='$instance']/ServerProtocol[@Name='Tcp']"
$Tcp = $wmi.GetSmoObject($uri)
foreach ($ipAddress in $Tcp.IPAddresses) {
    $ipAddress.IPAddressProperties["TcpDynamicPorts"].Value = ""
    $ipAddress.IPAddressProperties["TcpPort"].Value = $port
}
$Tcp.Alter()
Write-Host -Object "$indent Starting $instance" -ForegroundColor DarkGreen
Restart-Service "MSSQL`$$instance" -WarningAction SilentlyContinue -Force
Restart-Service "SQLAgent`$$instance" -WarningAction SilentlyContinue -Force

do {
    Start-Sleep 1
    $null = (& sqlcmd -S "$sqlinstance" -b -Q "select 1" -d master)
}
while ($lastexitcode -ne 0 -and $t++ -lt 10)

# Agent sometimes takes a moment to start
do {
    Write-Host -Object "$indent Waiting for SQL Agent to start" -ForegroundColor DarkGreen
    Start-Sleep 1
}
while ((Get-Service "SQLAgent`$$instance").Status -ne 'Running' -and $z++ -lt 10)

# Whatever, just sleep an extra 5
Start-Sleep 5

# this needs to be moved out. Tests that require these things need to run this in a BeforeAll stanza and remove the cruft in an AfterAll one
# so everybody can run tests without needing this too (which should be used strictly as appveyor-setup-related activities)
# when this fails for resource contention, the whole build stops for no reason. At most, it should fail only tests that are in the need of the reqs
Write-Host -Object "$indent Executing startup scripts for SQL Server 2016" -ForegroundColor DarkGreen
$sql2016Startup = 0
foreach ($file in (Get-ChildItem C:\github\appveyor-lab\sql2016-startup\*.sql -Recurse -ErrorAction SilentlyContinue)) {
    try {
        Invoke-Sqlcmd2 -ServerInstance $sqlinstance -InputFile $file -ErrorAction Stop
    } catch {
        $sql2016Startup = 1
    }
}
if ($sql2016Startup -eq 1) {
    Write-Host -Object "$indent something went wrong with startup scripts" -ForegroundColor DarkGreen
}
tools\dbatools\tests\appveyor.SQL2017.ps1
$indent = '...'
Write-Host -Object "Running $PSCommandpath" -ForegroundColor DarkGreen
$dbatools_serialimport = $true
Import-Module C:\github\dbatools\dbatools.psd1
Start-Sleep 5
# This script spins up the 2016 instance and the relative setup

$sqlinstance = "localhost\SQL2017"
$instance = "SQL2017"
$port = "14334"

Write-Host -Object "$indent Setting up AppVeyor Services" -ForegroundColor DarkGreen
Set-Service -Name SQLBrowser -StartupType Automatic -WarningAction SilentlyContinue
Set-Service -Name "SQLAgent`$$instance" -StartupType Automatic -WarningAction SilentlyContinue
Start-Service SQLBrowser -ErrorAction SilentlyContinue -WarningAction SilentlyContinue

Write-Host -Object "$indent Changing the port on $instance to $port" -ForegroundColor DarkGreen
$wmi = New-Object Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer
$uri = "ManagedComputer[@Name='$env:COMPUTERNAME']/ ServerInstance[@Name='$instance']/ServerProtocol[@Name='Tcp']"
$Tcp = $wmi.GetSmoObject($uri)
foreach ($ipAddress in $Tcp.IPAddresses) {
    $ipAddress.IPAddressProperties["TcpDynamicPorts"].Value = ""
    $ipAddress.IPAddressProperties["TcpPort"].Value = $port
}
$Tcp.Alter()
Write-Host -Object "$indent Starting $instance" -ForegroundColor DarkGreen
#Restart-Service "MSSQL`$$instance" -WarningAction SilentlyContinue -Force
#Restart-Service "SQLAgent`$$instance" -WarningAction SilentlyContinue -Force

$null = Enable-DbaAgHadr -SqlInstance $sqlinstance -Confirm:$false -Force
Restart-Service "SQLAgent`$$instance" -WarningAction SilentlyContinue -Force

do {
    Start-Sleep 1
    $null = (& sqlcmd -S "$sqlinstance" -b -Q "select 1" -d master)
}
while ($lastexitcode -ne 0 -and $t++ -lt 10)

# Agent sometimes takes a moment to start
do {
    Write-Host -Object "$indent Waiting for SQL Agent to start" -ForegroundColor DarkGreen
    Start-Sleep 1
}
while ((Get-Service "SQLAgent`$$instance").Status -ne 'Running' -and $z++ -lt 10)


$server = Connect-DbaInstance -SqlInstance $sqlinstance
$computername = $server.NetName
$servicename = $server.ServiceName
if ($servicename -eq 'MSSQLSERVER') {
    $instancename = "$computername"
}
else {
    $instancename = "$computername\$servicename"
}

$null = Get-DbaProcess -SqlInstance $sqlinstance -Program 'dbatools PowerShell module - dbatools.io' | Stop-DbaProcess -WarningAction SilentlyContinue
$server = Connect-DbaInstance -SqlInstance $sqlinstance
$dbname = "dbatoolsci_agroupdb"
$server.Query("create database $dbname")
$backup = Get-DbaDatabase -SqlInstance $sqlinstance -Database $dbname | Backup-DbaDatabase
$server.Query("IF NOT EXISTS (select * from sys.symmetric_keys where name like '%DatabaseMasterKey%') CREATE MASTER KEY ENCRYPTION BY PASSWORD = '<StrongPassword>'")
$server.Query("IF EXISTS ( SELECT * FROM sys.tcp_endpoints WHERE name = 'End_Mirroring') DROP ENDPOINT endpoint_mirroring")
$server.Query("CREATE CERTIFICATE dbatoolsci_AGCert WITH SUBJECT = 'AG Certificate'")
$server.Query("CREATE ENDPOINT dbatoolsci_AGEndpoint
                            STATE = STARTED
                            AS TCP (LISTENER_PORT = 5022,LISTENER_IP = ALL)
                            FOR DATABASE_MIRRORING (AUTHENTICATION = CERTIFICATE dbatoolsci_AGCert,ROLE = ALL)")
$server.Query("CREATE AVAILABILITY GROUP dbatoolsci_agroup
                            WITH (DB_FAILOVER = OFF, DTC_SUPPORT = NONE, CLUSTER_TYPE = NONE)
                            FOR DATABASE $dbname REPLICA ON N'$instancename'
                            WITH (ENDPOINT_URL = N'TCP://$computername`:5022', FAILOVER_MODE = MANUAL, AVAILABILITY_MODE = SYNCHRONOUS_COMMIT)")
tools\dbatools\tests\appveyor.sqlserver.ps1
Write-Host -Object "Creating migration & backup directories" -ForegroundColor DarkGreen
New-Item -Path C:\temp -ItemType Directory -ErrorAction SilentlyContinue | Out-Null
New-Item -Path C:\temp\migration -ItemType Directory -ErrorAction SilentlyContinue | Out-Null
New-Item -Path C:\temp\backups -ItemType Directory -ErrorAction SilentlyContinue | Out-Null
New-Item -Path C:\github\dbatools\.git -ItemType Directory -ErrorAction SilentlyContinue | Out-Null

if ($env:SCENARIO) {
    Write-Host -Object "Scenario $($env:scenario)" -ForegroundColor DarkGreen
    #Write-Host -Object "Main instance $($env:MAIN_INSTANCE)" -ForegroundColor DarkGreen
    #Write-Host -Object "Setup scripts $($env:SETUP_SCRIPTS)" -ForegroundColor DarkGreen
    $Setup_Scripts = $env:SETUP_SCRIPTS.split(',').Trim()
    foreach ($Setup_Script in $Setup_Scripts) {
        $SetupScriptPath = Join-Path $env:APPVEYOR_BUILD_FOLDER $Setup_Script
        Add-AppveyorTest -Name $Setup_Script -Framework NUnit -FileName $Setup_Script -Outcome Running
        $sw = [system.diagnostics.stopwatch]::startNew()
        . $SetupScriptPath
        $sw.Stop()
        Update-AppveyorTest -Name $Setup_Script -Framework NUnit -FileName $Setup_Script -Outcome Passed -Duration $sw.ElapsedMilliseconds
    }
}
tools\dbatools\tests\Backup-DbaDatabase.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    <#
    Context "Properly restores a database on the local drive using Path" {
        $results = Backup-DbaDatabase -SqlInstance $script:instance1 -BackupDirectory C:\temp\backups
        It "Should return a database name, specifically master" {
            ($results.DatabaseName -contains 'master') | Should -Be $true
        }
        It "Should return successful restore" {
            $results.ForEach{ $_.BackupComplete | Should -Be $true }
        }
    }
    #>
    BeforeAll {
        $DestBackupDir = 'C:\Temp\backups'
        $random = Get-Random
        $DestDbRandom = "dbatools_ci_backupdbadatabase$random"
        if (-Not(Test-Path $DestBackupDir)) {
            New-Item -Type Container -Path $DestBackupDir
        }
        Get-DbaDatabase -SqlInstance $script:instance1 -Database "dbatoolsci_singlerestore" | Remove-DbaDatabase -Confirm:$false
        Get-DbaDatabase -SqlInstance $script:instance2 -Database $DestDbRandom | Remove-DbaDatabase -Confirm:$false
    }
    AfterAll {
        Get-DbaDatabase -SqlInstance $script:instance1 -Database "dbatoolsci_singlerestore" | Remove-DbaDatabase -Confirm:$false
        Get-DbaDatabase -SqlInstance $script:instance2 -Database $DestDbRandom | Remove-DbaDatabase -Confirm:$false
        if (Test-Path $DestBackupDir) {
            Remove-Item "$DestBackupDir\*" -Force -Recurse
        }
    }
    Context "Should not backup if database and exclude match" {
        $results = Backup-DbaDatabase -SqlInstance $script:instance1 -BackupDirectory $DestBackupDir -Database master -Exclude master
        It "Should not return object" {
            $results | Should -Be $null
        }
    }

    Context "Database should backup 1 database" {
        $results = Backup-DbaDatabase -SqlInstance $script:instance1 -BackupDirectory $DestBackupDir -Database master
        It "Database backup object count Should Be 1" {
            $results.DatabaseName.Count | Should -Be 1
            $results.BackupComplete | Should -Be $true
        }
    }

    Context "Database should backup 2 databases" {
        $results = Backup-DbaDatabase -SqlInstance $script:instance1 -BackupDirectory $DestBackupDir -Database master, msdb
        It "Database backup object count Should Be 2" {
            $results.DatabaseName.Count | Should -Be 2
            $results.BackupComplete | Should -Be @($true, $true)
        }
    }

    Context "Should take path and filename" {
        $results = Backup-DbaDatabase -SqlInstance $script:instance1 -BackupDirectory $DestBackupDir -Database master -BackupFileName 'PesterTest.bak'
        It "Should report it has backed up to the path with the correct name"{
            $results.Fullname | Should -BeLike "$DestBackupDir*PesterTest.bak"
        }
        It "Should have backed up to the path with the correct name"{
            Test-Path "$DestBackupDir\PesterTest.bak" | Should -Be $true
        }
    }

    Context "Handling backup paths that don't exist" {
        $MissingPathTrailing = "$DestBackupDir\Missing1\Awol2\"
        $MissingPath = "$DestBackupDir\Missing1\Awol2"
        $null = Backup-DbaDatabase -SqlInstance $script:instance1 -Database master -BackupDirectory $MissingPath -WarningVariable warnvar *>$null
        It "Should warn and fail if path doesn't exist and BuildPath not set" {
            $warnvar | Should -BeLike "*$MissingPath*"
        }
        # $MissingPathTrailing has a trailing slash but we normalize the path before doing the actual backup
        $results = Backup-DbaDatabase -SqlInstance $script:instance1 -Database master -BackupDirectory $MissingPathTrailing -WarningVariable warnvar -BuildPath
        It "Should have backed up to $MissingPath" {
            $results.BackupFolder | Should -Be "$MissingPath"

            $results.Path | Should -Not -BeLike '*\\*'
        }
    }

    Context "CreateFolder switch should append the databasename to the backup path" {
        $results = Backup-DbaDatabase -SqlInstance $script:instance1 -Database master -BackupDirectory $DestBackupDir -CreateFolder
        It "Should have appended master to the backup path" {
            $results.BackupFolder | Should -Be "$DestBackupDir\master"
        }
    }

    Context "CreateFolder switch should append the databasename to the backup path even when striping" {
        $backupPaths = "$DestBackupDir\stripewithdb1", "$DestBackupDir\stripewithdb2"
        $results = Backup-DbaDatabase -SqlInstance $script:instance1 -Database master -BackupDirectory $backupPaths -CreateFolder
        It "Should have appended master to all backup paths" {
            foreach($path in $results.BackupFolder) {
                ($results.BackupFolder | Sort-Object) | Should -Be ($backupPaths | Sort-Object | ForEach-Object { [IO.Path]::Combine($_, 'master') })
            }
        }
    }


    Context "A fully qualified path should override a backupfolder" {
        $results = Backup-DbaDatabase -SqlInstance $script:instance1 -Database master -BackupDirectory c:\temp -BackupFileName "$DestBackupDir\PesterTest2.bak"
        It "Should report backed up to $DestBackupDir"  {
            $results.FullName | Should -BeLike "$DestBackupDir\PesterTest2.bak"
            $results.BackupFolder | Should Not Be 'c:\temp'
        }
        It "Should have backuped up to $DestBackupDir\PesterTest2.bak" {
            Test-Path "$DestBackupDir\PesterTest2.bak" | Should -Be $true
        }
    }

    Context "Should stripe if multiple backupfolders specified" {
        $backupPaths = "$DestBackupDir\stripe1", "$DestBackupDir\stripe2", "$DestBackupDir\stripe3"
        $null = New-item -Path $backupPaths -ItemType Directory


        $results = Backup-DbaDatabase -SqlInstance $script:instance1 -Database master -BackupDirectory $backupPaths
        It "Should have created 3 backups" {
            $results.BackupFilesCount | Should -Be 3
        }
        It "Should have written to all 3 folders" {
            $backupPaths | ForEach-Object {
                $_ | Should -BeIn ($results.BackupFolder)
            }
        }
        It "Should have written files with extensions" {
            foreach($path in $results.BackupFile) {
                [IO.Path]::GetExtension($path) | Should -Be '.bak'
            }
        }
        # Assure that striping logic favours -BackupDirectory and not -Filecount
        $results = Backup-DbaDatabase -SqlInstance $script:instance1 -Database master -BackupDirectory $backupPaths -FileCount 2
        It "Should have created 3 backups, even when FileCount is different" {
            $results.BackupFilesCount | Should -Be 3
        }
    }

    Context "Should stripe on filecount > 1" {
        $results = Backup-DbaDatabase -SqlInstance $script:instance1 -Database master -BackupDirectory $DestBackupDir -FileCount 3
        It "Should have created 3 backups" {
            $results.BackupFilesCount | Should -Be 3
        }
    }

    It "Should have 1 period in file extension" {
        foreach($path in $results.BackupFile) {
            [IO.Path]::GetExtension($path) | Should -Not -BeLike '*..*'
        }
    }

    Context "Should Backup to default path if none specified" {
        $results = Backup-DbaDatabase -SqlInstance $script:instance1 -Database master -BackupFileName 'PesterTest.bak'
        $DefaultPath = (Get-DbaDefaultPath -SqlInstance $script:instance1).Backup
        It "Should report it has backed up to the path with the corrrect name"{
            $results.Fullname | Should -BeLike "$DefaultPath*PesterTest.bak"
        }
        It "Should have backed up to the path with the corrrect name"{
            Test-Path "$DefaultPath\PesterTest.bak" | Should -Be $true
        }
    }

    Context "Backup can pipe to restore" {
        $null = Restore-DbaDatabase -SqlServer $script:instance1 -Path $script:appveyorlabrepo\singlerestore\singlerestore.bak -DatabaseName "dbatoolsci_singlerestore"
        $results = Backup-DbaDatabase -SqlInstance $script:instance1 -BackupDirectory $DestBackupDir -Database "dbatoolsci_singlerestore" | Restore-DbaDatabase -SqlInstance $script:instance2 -DatabaseName $DestDbRandom -TrustDbBackupHistory -ReplaceDbNameInFile
        It "Should return successful restore" {
            $results.RestoreComplete | Should -Be $true
        }
    }

    Context "Should handle NUL as an input path" {
        $results = Backup-DbaDatabase -SqlInstance $script:instance1 -Database master -BackupFileName NUL
        It "Should return succesful backup" {
            $results.BackupComplete | Should -Be $true
        }
        It "Should have backed up to NUL:" {
            $results.FullName[0] | Should -Be 'NUL:'
        }
    }

    Context "Should only output a T-SQL String if OutputScriptOnly specified" {
        $results = Backup-DbaDatabase -SqlInstance $script:instance1 -Database master -BackupFileName c:\notexists\file.bak -OutputScriptOnly
        It "Should return a string" {
            $results.GetType().ToString() | Should -Be 'System.String'
        }
        It "Should return BACKUP DATABASE [master] TO  DISK = N'c:\notexists\file.bak' WITH NOFORMAT, NOINIT, NOSKIP, REWIND, NOUNLOAD,  STATS = 1" {
            $results | Should -Be "BACKUP DATABASE [master] TO  DISK = N'c:\notexists\file.bak' WITH NOFORMAT, NOINIT, NOSKIP, REWIND, NOUNLOAD,  STATS = 1"
        }
    }
    if ($env:azurepasswd) {
        Context "Azure works" {
            BeforeAll {
                $server = Connect-DbaInstance -SqlInstance $script:instance2
                $sql = "CREATE CREDENTIAL [https://dbatools.blob.core.windows.net/sql] WITH IDENTITY = N'SHARED ACCESS SIGNATURE', SECRET = N'$env:azurepasswd'"
                $server.Query($sql)
                $server.Query("CREATE DATABASE dbatoolsci_azure")
                $sql = "CREATE CREDENTIAL [dbatools_ci] WITH IDENTITY = N'dbatools', SECRET = N'$env:azurelegacypasswd'"
                $server.Query($sql)
            }
            AfterAll {
                Get-DbaDatabase -SqlInstance $script:instance2 -Database "dbatoolsci_azure" | Remove-DbaDatabase -Confirm:$false
                $server.Query("DROP CREDENTIAL [https://dbatools.blob.core.windows.net/sql]")
                $server.Query("DROP CREDENTIAL dbatools_ci")
            }
            It "backs up to Azure properly using SHARED ACCESS SIGNATURE" {
                $results = Backup-DbaDatabase -SqlInstance $script:instance2 -AzureBaseUrl https://dbatools.blob.core.windows.net/sql -Database dbatoolsci_azure -BackupFileName dbatoolsci_azure.bak -WithFormat
                $results.Database | Should -Be 'dbatoolsci_azure'
                $results.DeviceType | Should -Be 'URL'
                $results.BackupFile | Should -Be 'dbatoolsci_azure.bak'
            }
            It "backs up to Azure properly using legacy credential" {
                $results = Backup-DbaDatabase -SqlInstance $script:instance2 -AzureBaseUrl https://dbatools.blob.core.windows.net/sql -Database dbatoolsci_azure -BackupFileName dbatoolsci_azure.bak -WithFormat -AzureCredential dbatools_ci
                $results.Database | Should -Be 'dbatoolsci_azure'
                $results.DeviceType | Should -Be 'URL'
                $results.BackupFile | Should -Be 'dbatoolsci_azure.bak'
            }
        }
    }
}
tools\dbatools\tests\Backup-DbaDatabaseMasterKey.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Can create a database certificate" {
        BeforeAll {
            if (-not (Get-DbaDatabaseMasterKey -SqlInstance $script:instance1 -Database tempdb)) {
                $masterkey = New-DbaDatabaseMasterKey -SqlInstance $script:instance1 -Database tempdb -Password $(ConvertTo-SecureString -String "GoodPass1234!" -AsPlainText -Force) -Confirm:$false
            }
        }
        AfterAll {
            (Get-DbaDatabaseMasterKey -SqlInstance $script:instance1 -Database tempdb) | Remove-DbaDatabaseMasterKey -Confirm:$false
        }

        $results = Backup-DbaDatabaseMasterKey -SqlInstance $script:instance1 -Database tempdb -Password $(ConvertTo-SecureString -String "GoodPass1234!" -AsPlainText -Force)
        $null = Remove-Item -Path $results.Path -ErrorAction SilentlyContinue -Confirm:$false

        It "backs up the db cert" {
            $results.Database -eq 'tempdb'
            $results.Status -eq "Success"
        }
    }
}
tools\dbatools\tests\Backup-DbaDbCertificate.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Can create a database certificate" {
        BeforeAll {
            if (-not (Get-DbaDatabaseMasterKey -SqlInstance $script:instance1 -Database tempdb)) {
                $masterkey = New-DbaDatabaseMasterKey -SqlInstance $script:instance1 -Database tempdb -Password $(ConvertTo-SecureString -String "GoodPass1234!" -AsPlainText -Force) -Confirm:$false
            }
        }
        AfterAll {
            (Get-DbaDbCertificate -SqlInstance $script:instance1 -Database tempdb) | Remove-DbaDbCertificate -Confirm:$false
            (Get-DbaDatabaseMasterKey -SqlInstance $script:instance1 -Database tempdb) | Remove-DbaDatabaseMasterKey -Confirm:$false
        }

        $cert = New-DbaDbCertificate -SqlInstance $script:instance1 -Database tempdb
        $results = Backup-DbaDbCertificate -SqlInstance $script:instance1 -Certificate $cert.Name -Database tempdb
        $null = Remove-Item -Path $results.Path -ErrorAction SilentlyContinue -Confirm:$false

        It "backs up the db cert" {
            $results.Certificate -match $certificateName1
            $results.Status -match "Success"
        }
    }
}
tools\dbatools\tests\Clear-DbaPlanCache.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    Context "doesn't clear plan cache" {
        It "returns correct datatypes" {
            # Make plan cache way higher than likely for a test rig
            $results = Clear-DbaPlanCache -SqlInstance $script:instance1 -Threshold 10240
            $results.Size -is [dbasize] | Should -Be $true
            $results.Status -match 'below' | Should -Be $true
        }
        It "supports piping" {
            # Make plan cache way higher than likely for a test rig
            $results = Get-DbaPlanCache -SqlInstance $script:instance1 | Clear-DbaPlanCache -Threshold 10240
            $results.Size -is [dbasize] | Should -Be $true
            $results.Status -match 'below' | Should -Be $true
        }
    }
}
tools\dbatools\tests\Clear-DbaSqlConnectionPool.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    It "doesn't throw" {
        { Clear-DbaSqlConnectionPool }  | Should Not Throw
    }
}
tools\dbatools\tests\Clear-DbaWaitStatistics.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Command executes properly and returns proper info" {
        $results = Clear-DbaWaitStatistics -SqlInstance $script:instance1 -Confirm:$false

        It "returns success" {
            $results.Status -eq 'Success' | Should Be $true
        }
    }
}
tools\dbatools\tests\Connect-DbaInstance.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "connection is properly made" {
        $server = Connect-DbaInstance -SqlInstance $script:instance1 -ApplicationIntent ReadOnly

        It "returns the proper name" {
            $server.Name -eq $script:instance1 | Should Be $true
        }

        It "returns more than one database" {
            $server.Databases.Name.Count -gt 0 | Should Be $true
        }

        It "returns the connection with ApplicationIntent of ReadOnly" {
            $server.ConnectionContext.ConnectionString -match "ApplicationIntent=ReadOnly" | Should Be $true
        }

        It "sets StatementTimeout to 0" {
            $server = Connect-DbaInstance -SqlInstance $script:instance1 -StatementTimeout 0

            $server.ConnectionContext.StatementTimeout | Should Be 0
        }

        It "sets connectioncontext parameters that are provided" {
            $params = @{
                'BatchSeparator' = 'GO'
                'ConnectTimeout' = 1
                'Database' = 'master'
                'LockTimeout' = 1
                'MaxPoolSize' = 20
                'MinPoolSize' = 1
                'NetworkProtocol' = 'TcpIp'
                'PacketSize' = 4096
                'PooledConnectionLifetime' = 600
                'WorkstationId' = 'MadeUpServer'
                'SqlExecutionModes' = 'ExecuteSql'
                'StatementTimeout' = 0
            }

            $server = Connect-DbaInstance -SqlInstance $script:instance1 @params

            foreach ($param in $params.GetEnumerator()) {
                if ($param.Key -eq 'Database') {
                    $propName = 'DatabaseName'
                } else {
                    $propName = $param.Key
                }

                $server.ConnectionContext.$propName | Should Be $param.Value
            }
        }
    }
}
tools\dbatools\tests\Connect-SqlInstance.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"
. "$PSScriptRoot\..\internal\functions\Connect-SqlInstance.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    $password = 'MyV3ry$ecur3P@ssw0rd'
    $securePassword = ConvertTo-SecureString $password -AsPlainText -Force
    $server = Connect-SqlInstance -SqlInstance $script:instance1
    $login = "csitester"

    #Cleanup

    $results = Invoke-Sqlcmd2 -ServerInstance $script:instance1 -Query "IF EXISTS (SELECT * FROM sys.server_principals WHERE name = '$login') EXEC sp_who '$login'"
    foreach ($spid in $results.spid) {
        Invoke-Sqlcmd2 -ServerInstance $script:instance1 -Query "kill $spid"
    }

    if ($l = $server.logins[$login]) {
        if ($c = $l.EnumCredentials()) {
            $l.DropCredential($c)
        }
        $l.Drop()
    }

    #Create login
    $newLogin = New-Object Microsoft.SqlServer.Management.Smo.Login($server, $login)
    $newLogin.LoginType = "SqlLogin"
    $newLogin.Create($password)

    Context "Connect with a new login" {
        It "Should login with newly created Sql Login (also tests credential login) and get instance name" {
            $cred = New-Object System.Management.Automation.PSCredential ($login, $securePassword)
            $s = Connect-SqlInstance -SqlInstance $script:instance1 -SqlCredential $cred
            $s.Name | Should Be $script:instance1
        }
        It "Should return existing process running under the new login and kill it" {
            $results = Invoke-Sqlcmd2 -ServerInstance $script:instance1 -Query "IF EXISTS (SELECT * FROM sys.server_principals WHERE name = '$login') EXEC sp_who '$login'"
            $results | Should Not BeNullOrEmpty
            foreach ($spid in $results.spid) {
                { Invoke-Sqlcmd2 -ServerInstance $script:instance1 -Query "kill $spid" -ErrorAction Stop} | Should Not Throw
            }
        }
    }

    #Cleanup
    if ($l = $server.logins[$login]) {
        if ($c = $l.EnumCredentials()) {
            $l.DropCredential($c)
        }
        $l.Drop()
    }
}
tools\dbatools\tests\constants.ps1
# constants
if (Test-Path C:\temp\constants.ps1) {
    Write-Verbose "C:\temp\constants.ps1 found."
    . C:\temp\constants.ps1
}
elseif (Test-Path "$PSScriptRoot\constants.local.ps1") {
    Write-Verbose "tests\constants.local.ps1 found."
    . "$PSScriptRoot\constants.local.ps1"
}
else {
    $script:instance1 = "localhost\sql2008r2sp2"
    $script:instance2 = "localhost\sql2016"
    $script:instance3 = "localhost\sql2017"
    $script:instance1_detailed = "localhost,1433\sql2008r2sp2" #Just to make sure things parse a port properly
    $script:appveyorlabrepo = "C:\github\appveyor-lab"
    $instances = @($script:instance1, $script:instance2)
    $ssisserver = "localhost\sql2016"
}
tools\dbatools\tests\ConvertTo-DbaDataTable.Tests.ps1
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
Describe "Testing data table output when using a complex object" {
    $obj = New-Object -TypeName psobject -Property @{
        guid     = [system.guid]'32ccd4c4-282a-4c0d-997c-7b5deb97f9e0'
        timespan = New-TimeSpan -Start 2016-10-30 -End 2017-04-30
        datetime = Get-Date -Year 2016 -Month 10 -Day 30 -Hour 5 -Minute 52 -Second 0 -Millisecond 0
        char     = [System.Char]'T'
        true     = $true
        false    = $false
        null     = [bool]$null
        string   = "it's a boy!"
        UInt64   = [System.UInt64]123456
    }

    $innedobj = New-Object -TypeName psobject -Property @{
        Mission = 'Keep Hank alive'
    }

    Add-Member -Force -InputObject $obj -MemberType NoteProperty -Name myobject -Value $innedobj
    $result = ConvertTo-DbaDataTable -InputObject $obj

    Context "Property: guid" {
        It 'Has a column called "guid"' {
            $result.Columns.ColumnName.Contains('guid') | Should Be $true
        }
        It 'Has a [guid] data type on the column "guid"' {
            $result.Columns | Where-Object -Property 'ColumnName' -eq 'guid' | Select-Object -ExpandProperty 'DataType' | Select-Object -ExpandProperty Name | Should Be 'guid'
        }
        It 'Has the following guid: "32ccd4c4-282a-4c0d-997c-7b5deb97f9e0"' {
            $result.guid | Should Be '32ccd4c4-282a-4c0d-997c-7b5deb97f9e0'
        }
    }

    Context "Property: timespan" {
        It 'Has a column called "timespan"' {
            $result.Columns.ColumnName.Contains('timespan') | Should Be $true
        }
        It 'Has a [long] data type on the column "timespan"' {
            $result.Columns | Where-Object -Property 'ColumnName' -eq 'timespan' | Select-Object -ExpandProperty 'DataType' | Select-Object -ExpandProperty Name | Should Be 'Int64'
        }
        It "Has the following timespan: 15724800000" {
            $result.timespan | Should Be 15724800000
        }
    }

    Context "Property: datetime" {
        It 'Has a column called "datetime"' {
            $result.Columns.ColumnName.Contains('datetime') | Should Be $true
        }
        It 'Has a [datetime] data type on the column "datetime"' {
            $result.Columns | Where-Object -Property 'ColumnName' -eq 'datetime' | Select-Object -ExpandProperty 'DataType' | Select-Object -ExpandProperty Name | Should Be 'datetime'
        }
        It "Has the following datetime: 2016-10-30 05:52:00.000" {
            $date = Get-Date -Year 2016 -Month 10 -Day 30 -Hour 5 -Minute 52 -Second 0 -Millisecond 0
            $result.datetime -eq $date | Should Be $true
        }
    }

    Context "Property: char" {
        It 'Has a column called "char"' {
            $result.Columns.ColumnName.Contains('char') | Should Be $true
        }
        It 'Has a [char] data type on the column "char"' {
            $result.Columns | Where-Object -Property 'ColumnName' -eq 'char' | Select-Object -ExpandProperty 'DataType' | Select-Object -ExpandProperty Name | Should Be 'char'
        }
        It "Has the following char: T" {
            $result.char | Should Be "T"
        }
    }

    Context "Property: true" {
        It 'Has a column called "true"' {
            $result.Columns.ColumnName.Contains('true') | Should Be $true
        }
        It 'Has a [bool] data type on the column "true"' {
            $result.Columns | Where-Object -Property 'ColumnName' -eq 'true' | Select-Object -ExpandProperty 'DataType' | Select-Object -ExpandProperty Name | Should Be 'boolean'
        }
        It "Has the following bool: true" {
            $result.true | Should Be $true
        }
    }

    Context "Property: false" {
        It 'Has a column called "false"' {
            $result.Columns.ColumnName.Contains('false') | Should Be $true
        }
        It 'Has a [bool] data type on the column "false"' {
            $result.Columns | Where-Object -Property 'ColumnName' -eq 'false' | Select-Object -ExpandProperty 'DataType' | Select-Object -ExpandProperty Name | Should Be 'boolean'
        }
        It "Has the following bool: false" {
            $result.false | Should Be $false
        }
    }

    Context "Property: null" {
        It 'Has a column called "null"' {
            $result.Columns.ColumnName.Contains('null') | Should Be $true
        }
        It 'Has a [bool] data type on the column "null"' {
            $result.Columns | Where-Object -Property 'ColumnName' -eq 'null' | Select-Object -ExpandProperty 'DataType' | Select-Object -ExpandProperty Name | Should Be 'boolean'
        }
        It "Has the following bool: false" {
            $result.null | Should Be $false #should actually be $null but its hard to compare :)
        }
    }

    Context "Property: string" {
        It 'Has a column called "string"' {
            $result.Columns.ColumnName.Contains('string') | Should Be $true
        }
        It 'Has a [string] data type on the column "string"' {
            $result.Columns | Where-Object -Property 'ColumnName' -eq 'string' | Select-Object -ExpandProperty 'DataType' | Select-Object -ExpandProperty Name | Should Be 'string'
        }
        It "Has the following string: it's a boy!" {
            $result.string | Should Be "it's a boy!"
        }
    }

    Context "Property: UInt64" {
        It 'Has a column called "UInt64"' {
            $result.Columns.ColumnName.Contains('UInt64') | Should Be $true
        }
        It 'Has a [UInt64] data type on the column "UInt64"' {
            $result.Columns | Where-Object -Property 'ColumnName' -eq 'UInt64' | Select-Object -ExpandProperty 'DataType' | Select-Object -ExpandProperty Name | Should Be 'UInt64'
        }
        It "Has the following number: 123456" {
            $result.UInt64 | Should Be 123456
        }
    }

    Context "Property: myobject" {
        It 'Has a column called "myobject"' {
            $result.Columns.ColumnName.Contains('myobject') | Should Be $true
        }
        It 'Has a [string] data type on the column "myobject"' {
            $result.Columns | Where-Object -Property 'ColumnName' -eq 'myobject' | Select-Object -ExpandProperty 'DataType' | Select-Object -ExpandProperty Name | Should Be 'String'
        }
        It "Has no value" {
            # not sure if this is a feaure. Should probably be changed in the future
            $result.myobject.GetType().FullName | Should Be "System.DBNull"
        }
    }

}

Describe "Testing input parameters" {
    $obj = New-Object -TypeName psobject -Property @{
        timespan = New-TimeSpan -Start 2017-01-01 -End 2017-01-02
    }

    Context "Verifying TimeSpanType" {
        It "Should return '1.00:00:00' when String is used" {
            (ConvertTo-DbaDataTable -InputObject $obj -TimeSpanType String).Timespan | Should Be '1.00:00:00'
        }
        It "Should return 864000000000 when Ticks is used" {
            (ConvertTo-DbaDataTable -InputObject $obj -TimeSpanType Ticks).Timespan | Should Be 864000000000
        }
        It "Should return 1 when TotalDays is used" {
            (ConvertTo-DbaDataTable -InputObject $obj -TimeSpanType TotalDays).Timespan | Should Be 1
        }
        It "Should return 24 when TotalHours is used" {
            (ConvertTo-DbaDataTable -InputObject $obj -TimeSpanType TotalHours).Timespan | Should Be 24
        }
        It "Should return 86400000 when TotalMilliseconds is used" {
            (ConvertTo-DbaDataTable -InputObject $obj -TimeSpanType TotalMilliseconds).Timespan | Should Be 86400000
        }
        It "Should return 1440 when TotalMinutes is used" {
            (ConvertTo-DbaDataTable -InputObject $obj -TimeSpanType TotalMinutes).Timespan | Should Be 1440
        }
        It "Should return 86400 when TotalSeconds is used" {
            (ConvertTo-DbaDataTable -InputObject $obj -TimeSpanType TotalSeconds).Timespan | Should Be 86400
        }
    }

    Context "Verifying IgnoreNull" {
        # To be able to force null
        function returnnull {
            [CmdletBinding()]
            param ()
            New-Object -TypeName psobject -Property @{ Name = [int]1 }
            $null
            New-Object -TypeName psobject -Property @{ Name = [int]3 }
        }

        function returnOnlynull {
            [CmdletBinding()]
            param ()
            $null
        }

        It "Does not create row if null is in array when IgnoreNull is set" {
            $result = ConvertTo-DbaDataTable -InputObject (returnnull) -IgnoreNull -WarningAction SilentlyContinue
            $result.Rows.Count | Should Be 2
        }

        It "Does not create row if null is in pipeline when IgnoreNull is set" {
            $result = returnnull | ConvertTo-DbaDataTable -IgnoreNull -WarningAction SilentlyContinue
            $result.Rows.Count | Should Be 2
        }

        It "Returns empty row when null value is provided (without IgnoreNull)" {
            $result = ConvertTo-DbaDataTable -InputObject (returnnull)
            $result.Name[0] | Should Be 1
            $result.Name[1].GetType().FullName | Should Be 'System.DBNull'
            $result.Name[2] | Should Be 3
        }

        It "Returns empty row when null value is passed in pipe (without IgnoreNull)" {
            $result = returnnull | ConvertTo-DbaDataTable
            $result.Name[0] | Should Be 1
            $result.Name[1].GetType().FullName | Should Be 'System.DBNull'
            $result.Name[2] | Should Be 3
        }
    }

    Context "Verifying Silent" {
        # To be able to force null
        function returnnull {
            New-Object -TypeName psobject -Property @{ Name = 1 }
            $null
            New-Object -TypeName psobject -Property @{ Name = 3 }
        }

        It "Suppresses warning messages when Silent is used" {
            $null = ConvertTo-DbaDataTable -InputObject (returnnull) -IgnoreNull -EnableException -WarningVariable warn -WarningAction SilentlyContinue
            $warn.message -eq $null | Should Be $true
        }
    }

    Context "Verifying script properties returning null" {

        It "Returns string column if a script property returns null" {
            $myobj = New-Object -TypeName psobject -Property @{ Name = 'Test' }
            $myobj | Add-Member -Force -MemberType ScriptProperty -Name ScriptNothing -Value { $null }
            $r = ConvertTo-DbaDataTable -InputObject $myobj
            ($r.Columns | Where-Object ColumnName -eq ScriptNothing | Select-Object -ExpandProperty DataType).ToString() | Should Be 'System.String'

        }
    }

    Context "Verifying a datatable gets cloned when passed in" {
        $obj = New-Object -TypeName psobject -Property @{
            col1 = 'col1'
            col2 = 'col2'
        }
        $first = $obj | ConvertTo-DbaDataTable
        $second = $first | ConvertTo-DbaDataTable
        It "Should have the same columns" {
            # does not add ugly RowError,RowState Table, ItemArray, HasErrors
            $firstColumns = ($first.Columns.ColumnName | Sort-Object) -Join ','
            $secondColumns = ($second.Columns.ColumnName | Sort-Object) -Join ','
            $firstColumns | Should -Be $secondColumns
        }
    }
}
tools\dbatools\tests\ConvertTo-DbaXESession.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $sql = "-- Create a Queue
                declare @rc int
                declare @TraceID int
                declare @maxfilesize bigint
                set @maxfilesize = 5
                exec @rc = sp_trace_create @TraceID output, 0, N'C:\windows\temp\temptrace', @maxfilesize, NULL

                -- Set the events
                declare @on bit
                set @on = 1
                exec sp_trace_setevent @TraceID, 14, 1, @on
                exec sp_trace_setevent @TraceID, 14, 9, @on
                exec sp_trace_setevent @TraceID, 14, 10, @on
                exec sp_trace_setevent @TraceID, 14, 11, @on
                exec sp_trace_setevent @TraceID, 14, 6, @on
                exec sp_trace_setevent @TraceID, 14, 12, @on
                exec sp_trace_setevent @TraceID, 14, 14, @on
                exec sp_trace_setevent @TraceID, 15, 11, @on
                exec sp_trace_setevent @TraceID, 15, 6, @on
                exec sp_trace_setevent @TraceID, 15, 9, @on
                exec sp_trace_setevent @TraceID, 15, 10, @on
                exec sp_trace_setevent @TraceID, 15, 12, @on
                exec sp_trace_setevent @TraceID, 15, 13, @on
                exec sp_trace_setevent @TraceID, 15, 14, @on
                exec sp_trace_setevent @TraceID, 15, 15, @on
                exec sp_trace_setevent @TraceID, 15, 16, @on
                exec sp_trace_setevent @TraceID, 15, 17, @on
                exec sp_trace_setevent @TraceID, 15, 18, @on
                exec sp_trace_setevent @TraceID, 17, 1, @on
                exec sp_trace_setevent @TraceID, 17, 9, @on
                exec sp_trace_setevent @TraceID, 17, 10, @on
                exec sp_trace_setevent @TraceID, 17, 11, @on
                exec sp_trace_setevent @TraceID, 17, 6, @on
                exec sp_trace_setevent @TraceID, 17, 12, @on
                exec sp_trace_setevent @TraceID, 17, 14, @on
                exec sp_trace_setevent @TraceID, 10, 9, @on
                exec sp_trace_setevent @TraceID, 10, 2, @on
                exec sp_trace_setevent @TraceID, 10, 10, @on
                exec sp_trace_setevent @TraceID, 10, 6, @on
                exec sp_trace_setevent @TraceID, 10, 11, @on
                exec sp_trace_setevent @TraceID, 10, 12, @on
                exec sp_trace_setevent @TraceID, 10, 13, @on
                exec sp_trace_setevent @TraceID, 10, 14, @on
                exec sp_trace_setevent @TraceID, 10, 15, @on
                exec sp_trace_setevent @TraceID, 10, 16, @on
                exec sp_trace_setevent @TraceID, 10, 17, @on
                exec sp_trace_setevent @TraceID, 10, 18, @on
                exec sp_trace_setevent @TraceID, 12, 1, @on
                exec sp_trace_setevent @TraceID, 12, 9, @on
                exec sp_trace_setevent @TraceID, 12, 11, @on
                exec sp_trace_setevent @TraceID, 12, 6, @on
                exec sp_trace_setevent @TraceID, 12, 10, @on
                exec sp_trace_setevent @TraceID, 12, 12, @on
                exec sp_trace_setevent @TraceID, 12, 13, @on
                exec sp_trace_setevent @TraceID, 12, 14, @on
                exec sp_trace_setevent @TraceID, 12, 15, @on
                exec sp_trace_setevent @TraceID, 12, 16, @on
                exec sp_trace_setevent @TraceID, 12, 17, @on
                exec sp_trace_setevent @TraceID, 12, 18, @on
                exec sp_trace_setevent @TraceID, 13, 1, @on
                exec sp_trace_setevent @TraceID, 13, 9, @on
                exec sp_trace_setevent @TraceID, 13, 11, @on
                exec sp_trace_setevent @TraceID, 13, 6, @on
                exec sp_trace_setevent @TraceID, 13, 10, @on
                exec sp_trace_setevent @TraceID, 13, 12, @on
                exec sp_trace_setevent @TraceID, 13, 14, @on

                -- Set the Filters
                declare @intfilter int
                declare @bigintfilter bigint

                exec sp_trace_setfilter @TraceID, 10, 0, 7, N'SQL Server Profiler - 934a8575-0dc1-4937-bde1-edac1cb9691f'
                -- Set the trace status to start
                exec sp_trace_setstatus @TraceID, 1

                -- display trace id for future references
                select TraceID=@TraceID"
        $server = Connect-DbaInstance -SqlInstance $script:instance2
        $traceid = ($server.Query($sql)).TraceID
        $script:name = "dbatoolsci-session"
    }
    AfterAll {
        $null = Remove-DbaXESession -SqlInstance $script:instance2 -Session $script:name
        $null = Remove-DbaTrace -SqlInstance $script:instance2 -Id $traceid
        Remove-Item C:\windows\temp\temptrace.trc -ErrorAction SilentlyContinue
    }
    Context "Test Trace Conversion" {
        $results = Get-DbaTrace -SqlInstance $script:instance2 -Id $traceid | ConvertTo-DbaXESession -Name $script:name | Start-DbaXESession
        It "returns the right results" {
            $results.Name | Should Be $script:name
            $results.Status | Should Be "Running"
            $results.Targets.Name | Should Be "package0.event_file"
        }
    }
}
tools\dbatools\tests\Copy-DbaAgentAlert.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"
Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        $server = Connect-DbaInstance -SqlInstance $script:instance2 -Database master
        $server.Query("EXEC msdb.dbo.sp_add_alert @name=N'dbatoolsci test alert',
        @message_id=0,
        @severity=6,
        @enabled=1,
        @delay_between_responses=0,
        @include_event_description_in=0,
        @category_name=N'[Uncategorized]',
        @job_id=N'00000000-0000-0000-0000-000000000000'")
    }
    AfterAll {
        $server = Connect-DbaInstance -SqlInstance $script:instance2 -Database master
        $server.Query("EXEC msdb.dbo.sp_delete_alert @name=N'dbatoolsci test alert'")
        $server = Connect-DbaInstance -SqlInstance $script:instance3 -Database master
        $server.Query("EXEC msdb.dbo.sp_delete_alert @name=N'dbatoolsci test alert'")
    }
    
    It "copies the sample alert" {
        $results = Copy-DbaAgentAlert -Source $script:instance2 -Destination $script:instance3 -Alert 'dbatoolsci test alert'
        $results.Name -eq 'dbatoolsci test alert', 'dbatoolsci test alert'
        $results.Status -eq 'Successful', 'Successful'
    }
    
    It "doesn't overwrite existing alerts" {
        $results = Copy-DbaAgentAlert -Source $script:instance2 -Destination $script:instance3 -Alert 'dbatoolsci test alert'
        $results.Name -eq 'dbatoolsci test alert'
        $results.Status -eq 'Skipped'
    }
    
    It "the newly copied alert exists" {
        $results = Get-DbaAgentAlert -SqlInstance $script:instance2
        $results.Name -contains 'dbatoolsci test alert'
    }
}
tools\dbatools\tests\Copy-DbaAgentCategory.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        $null = New-DbaAgentJobCategory -SqlInstance $script:instance2 -Category 'dbatoolsci test category'
    }
    AfterAll {
        $null = Remove-DbaAgentJobCategory -SqlInstance $script:instance2 -Category 'dbatoolsci test category'
    }
    
    Context "Command copies jobs properly" {
        It "returns one success" {
            $results = Copy-DbaAgentCategory -Source $script:instance2 -Destination $script:instance3 -JobCategory 'dbatoolsci test category'
            $results.Name -eq "dbatoolsci test category"
            $results.Status -eq "Successful"
        }

        It "does not overwrite" {
            $results = Copy-DbaAgentCategory -Source $script:instance2 -Destination $script:instance3 -JobCategory 'dbatoolsci test category'
            $results.Name -eq "dbatoolsci test category"
            $results.Status -eq "Skipped"
        }
    }
}
tools\dbatools\tests\Copy-DbaAgentJob.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        $null = New-DbaAgentJob -SqlInstance $script:instance2 -Job dbatoolsci_copyjob
        $null = New-DbaAgentJob -SqlInstance $script:instance2 -Job dbatoolsci_copyjob_disabled
        $sourcejobs = Get-DbaAgentJob -SqlInstance $script:instance2
        $destjobs = Get-DbaAgentJob -SqlInstance $script:instance3
    }
    AfterAll {
        $null = Remove-DbaAgentJob -SqlInstance $script:instance2, $script:instance3 -Job dbatoolsci_copyjob, dbatoolsci_copyjob_disabled
    }
    
    Context "Command copies jobs properly" {
        $results = Copy-DbaAgentJob -Source $script:instance2 -Destination $script:instance3 -Job dbatoolsci_copyjob
        
        It "returns one success" {
            $results.Name -eq "dbatoolsci_copyjob"
            $results.Status -eq "Successful"
        }
        
        It "did not copy dbatoolsci_copyjob_disabled" {
            Get-DbaAgentJob -SqlInstance $script:instance3 -Job dbatoolsci_copyjob_disabled | Should -Be $null
        }
        
        It "disables jobs when requested" {
            (Get-DbaAgentJob -SqlInstance $script:instance2 -Job dbatoolsci_copyjob_disabled).Enabled
            $results = Copy-DbaAgentJob -Source $script:instance2 -Destination $script:instance3 -Job dbatoolsci_copyjob_disabled -DisableOnSource -DisableOnDestination
            (Get-DbaAgentJob -SqlInstance $script:instance2 -Job dbatoolsci_copyjob_disabled).Enabled | Should -Be $false
            (Get-DbaAgentJob -SqlInstance $script:instance3 -Job dbatoolsci_copyjob_disabled).Enabled | Should -Be $false
        }
    }
}
tools\dbatools\tests\Copy-DbaAgentOperator.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        $server = Connect-DbaInstance -SqlInstance $script:instance2
        $sql = "EXEC msdb.dbo.sp_add_operator @name=N'dbatoolsci_operator', @enabled=1, @pager_days=0"
        $server.Query($sql)
        $sql = "EXEC msdb.dbo.sp_add_operator @name=N'dbatoolsci_operator2', @enabled=1, @pager_days=0"
        $server.Query($sql)
    }
    AfterAll {
        $server = Connect-DbaInstance -SqlInstance $script:instance2
        $sql = "EXEC msdb.dbo.sp_delete_operator @name=N'dbatoolsci_operator'"
        $server.Query($sql)
        $sql = "EXEC msdb.dbo.sp_delete_operator @name=N'dbatoolsci_operator2'"
        $server.Query($sql)
        
        $server = Connect-DbaInstance -SqlInstance $script:instance3
        $sql = "EXEC msdb.dbo.sp_delete_operator @name=N'dbatoolsci_operator'"
        $server.Query($sql)
        $sql = "EXEC msdb.dbo.sp_delete_operator @name=N'dbatoolsci_operator2'"
        $server.Query($sql)
    }
    
    Context "Copies operators" {
        $results = Copy-DbaAgentOperator -Source $script:instance2 -Destination $script:instance3 -Operator dbatoolsci_operator, dbatoolsci_operator2
        
        It "returns two results" {
            $results.Count -eq 2
            $results.Status -eq "Successful", "Successful"
        }
        
        It "return one result that's skipped" {
            $results = Copy-DbaAgentOperator -Source $script:instance2 -Destination $script:instance3 -Operator dbatoolsci_operator
            $results.Status -eq "Skipped"
        }
    }
}
tools\dbatools\tests\Copy-DbaBackupDevice.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

if (-not $env:appveyor) {
    Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
        Context "Setup" {
            BeforeAll {
                $devicename = "dbatoolsci-backupdevice"
                $backupdir = (Get-DbaDefaultPath -SqlInstance $script:instance1).Backup
                $backupfilename = "$backupdir\$devicename.bak"
                $server = Connect-DbaInstance -SqlInstance $script:instance1
                $server.Query("EXEC master.dbo.sp_addumpdevice  @devtype = N'disk', @logicalname = N'$devicename',@physicalname = N'$backupfilename'")
                $server.Query("BACKUP DATABASE master TO DISK = '$backupfilename'")
            }
            AfterAll {
                $server.Query("EXEC master.dbo.sp_dropdevice @logicalname = N'$devicename'")
                $server1 = Connect-DbaInstance -SqlInstance $script:instance2
                try {
                    $server1.Query("EXEC master.dbo.sp_dropdevice @logicalname = N'$devicename'")
                }
                catch {
                    # dont care
                }
            }

            $results = Copy-DbaBackupDevice -Source $script:instance1 -Destination $script:instance2 -WarningVariable warn -WarningAction SilentlyContinue
            if ($warn) {
                It "warns if it has a problem moving (issue for local to local)" {
                    $warn -match "backup device to destination" | Should Be $true
                }
            }
            else {
                It "should report success" {
                    $results.Status | Should Be "Successful"
                }
            }

            $results = Copy-DbaBackupDevice -Source $script:instance1 -Destination $script:instance2
            It "Should say skipped" {
                $results.Status -ne "Successful" | Should be $true
            }
        }
    }
}
tools\dbatools\tests\Copy-DbaCentralManagementServer.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Setup" {
        BeforeAll {
            $server = Connect-DbaInstance $script:instance2
            $regstore = New-Object Microsoft.SqlServer.Management.RegisteredServers.RegisteredServersStore($server.ConnectionContext.SqlConnectionObject)
            $dbstore = $regstore.DatabaseEngineServerGroup

            $servername = "dbatoolsci-server1"
            $group = "dbatoolsci-group1"
            $regservername = "dbatoolsci-server12"
            $regserverdescription = "dbatoolsci-server123"

            $newgroup = New-Object Microsoft.SqlServer.Management.RegisteredServers.ServerGroup($dbstore, $group)
            $newgroup.Create()
            $dbstore.Refresh()

            $groupstore = $dbstore.ServerGroups[$group]
            $newserver = New-Object Microsoft.SqlServer.Management.RegisteredServers.RegisteredServer($groupstore, $regservername)
            $newserver.ServerName = $servername
            $newserver.Description = $regserverdescription
            $newserver.Create()
        }
        AfterAll {
            $newgroup.Drop()
            $server = Connect-DbaInstance $script:instance1
            $regstore = New-Object Microsoft.SqlServer.Management.RegisteredServers.RegisteredServersStore($server.ConnectionContext.SqlConnectionObject)
            $dbstore = $regstore.DatabaseEngineServerGroup
            $groupstore = $dbstore.ServerGroups[$group]
            $groupstore.Drop()
        }

        $results = Copy-DbaCentralManagementServer -Source $script:instance2 -Destination $script:instance1 -WarningVariable warn -WarningAction SilentlyContinue -CMSGroup $group

        It "should report success" {
            $results.Status | Should Be "Successful", "Successful"
        }

        # Property Comparisons will come later when we have the commands
    }
}
tools\dbatools\tests\Copy-DbaCredential.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"
. "$PSScriptRoot\..\internal\functions\Invoke-Command2.ps1"

Describe "$CommandName Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        $logins = "dbatoolsci_thor", "dbatoolsci_thorsmomma"
        $plaintext = "BigOlPassword!"
        $password = ConvertTo-SecureString $plaintext -AsPlainText -Force
        
        # Add user
        foreach ($login in $logins) {
            $null = Invoke-Command2 -ScriptBlock { net user $args[0] $args[1] /add *>&1 } -ArgumentList $login, $plaintext -ComputerName $script:instance2
            $null = Invoke-Command2 -ScriptBlock { net user $args[0] $args[1] /add *>&1 } -ArgumentList $login, $plaintext -ComputerName $script:instance3
        }
    }
    AfterAll {
        (Get-DbaCredential -SqlInstance $script:instance2 -Identity dbatoolsci_thor, dbatoolsci_thorsmomma -ErrorAction Stop -WarningAction SilentlyContinue).Drop()
        (Get-DbaCredential -SqlInstance $script:instance3 -Identity dbatoolsci_thor, dbatoolsci_thorsmomma -ErrorAction Stop -WarningAction SilentlyContinue).Drop()

        foreach ($login in $logins) {
            $null = Invoke-Command2 -ScriptBlock { net user $args /delete *>&1 } -ArgumentList $login -ComputerName $script:instance2
            $null = Invoke-Command2 -ScriptBlock { net user $args /delete *>&1 } -ArgumentList $login -ComputerName $script:instance3
        }
    }
    
    Context "Create new credential" {
        It "Should create new credentials with the proper properties" {
            $results = New-DbaCredential -SqlInstance $script:instance2 -Name dbatoolsci_thorcred -Identity dbatoolsci_thor -Password $password
            $results.Name | Should Be "dbatoolsci_thorcred"
            $results.Identity | Should Be "dbatoolsci_thor"
            
            $results = New-DbaCredential -SqlInstance $script:instance2 -Identity dbatoolsci_thorsmomma -Password $password
            $results.Name | Should Be "dbatoolsci_thorsmomma"
            $results.Identity | Should Be "dbatoolsci_thorsmomma"
        }
    }
    
    Context "Copy Credential with the same properties." {
        It "Should copy successfully" {
            $results = Copy-DbaCredential -Source $script:instance2 -Destination $script:instance3 -Name dbatoolsci_thorcred
            $results.Status | Should Be "Successful"
        }
        
        It "Should retain its same properties" {
            $Credential1 = Get-DbaCredential -SqlInstance $script:instance2 -Name dbatoolsci_thor -ErrorAction SilentlyContinue -WarningAction SilentlyContinue
            $Credential2 = Get-DbaCredential -SqlInstance $script:instance3 -Name dbatoolsci_thor -ErrorAction SilentlyContinue -WarningAction SilentlyContinue
            
            # Compare its value
            $Credential1.Name | Should Be $Credential2.Name
            $Credential1.Identity | Should Be $Credential2.Identity
        }
    }
    
    Context "No overwrite" {
        It "does not overwrite without force" {
            $results = Copy-DbaCredential -Source $script:instance2 -Destination $script:instance3 -Name dbatoolsci_thorcred
            $results.Status | Should Be "Skipping"
        }
    }
}
tools\dbatools\tests\Copy-DbaCustomError.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"
Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        $server = Connect-DbaInstance -SqlInstance $script:instance2 -Database master
        $server.Query("EXEC sp_addmessage @msgnum = 60000, @severity = 16,@msgtext = N'The item named %s already exists in %s.',@lang = 'us_english';")
        $server.Query("EXEC sp_addmessage @msgnum = 60000, @severity = 16, @msgtext = N'L''élément nommé %1! existe déjà dans %2!',@lang = 'French';")
    }
    AfterAll {
        $server = Connect-DbaInstance -SqlInstance $script:instance2 -Database master
        $server.Query("EXEC sp_dropmessage @msgnum = 60000, @lang = 'all';")
        $server = Connect-DbaInstance -SqlInstance $script:instance3 -Database master
        $server.Query("EXEC sp_dropmessage @msgnum = 60000, @lang = 'all';")
    }
    
    It "copies the sample custom errror" {
        $results = Copy-DbaCustomError -Source $script:instance2 -Destination $script:instance3 -CustomError 60000
        $results.Name -eq "60000:'us_english'", "60000:'Français'"
        $results.Status -eq 'Successful', 'Successful'
    }
    
    It "doesn't overwrite existing custom errors" {
        $results = Copy-DbaCustomError -Source $script:instance2 -Destination $script:instance3 -CustomError 60000
        $results.Name -eq "60000:'us_english'", "60000:'Français'"
        $results.Status -eq 'Skipped', 'Skipped'
    }
    
    It "the newly copied custom error exists" {
        $results = Get-DbaCustomError -SqlInstance $script:instance2
        $results.ID -contains 60000
    }
}
tools\dbatools\tests\Copy-DbaDatabase.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        $NetworkPath = "C:\temp"
        $random = Get-Random
        $backuprestoredb = "dbatoolsci_backuprestore$random"
        $backuprestoredb2 = "dbatoolsci_backuprestoreother$random"
        $detachattachdb = "dbatoolsci_detachattach$random"
        Remove-DbaDatabase -Confirm:$false -SqlInstance $script:instance2, $script:instance3 -Database $backuprestoredb, $detachattachdb
        
        $server = Connect-DbaInstance -SqlInstance $script:instance3
        $server.Query("CREATE DATABASE $backuprestoredb2; ALTER DATABASE $backuprestoredb2 SET AUTO_CLOSE OFF WITH ROLLBACK IMMEDIATE")
        
        $server = Connect-DbaInstance -SqlInstance $script:instance2
        $server.Query("CREATE DATABASE $backuprestoredb; ALTER DATABASE $backuprestoredb SET AUTO_CLOSE OFF WITH ROLLBACK IMMEDIATE")
        $server.Query("CREATE DATABASE $detachattachdb; ALTER DATABASE $detachattachdb SET AUTO_CLOSE OFF WITH ROLLBACK IMMEDIATE")
        $server.Query("CREATE DATABASE $backuprestoredb2; ALTER DATABASE $backuprestoredb2 SET AUTO_CLOSE OFF WITH ROLLBACK IMMEDIATE")
        $null = Set-DbaDatabaseOwner -SqlInstance $script:instance2 -Database $backuprestoredb, $detachattachdb -TargetLogin sa
    }
    AfterAll {
        Remove-DbaDatabase -Confirm:$false -SqlInstance $script:instance2, $script:instance3 -Database $backuprestoredb, $detachattachdb, $backuprestoredb2
    }
    
    # if failed Disable-NetFirewallRule -DisplayName 'Core Networking - Group Policy (TCP-Out)'
    Context "Detach Attach" {
        It "Should be success" {
            $results = Copy-DbaDatabase -Source $script:instance2 -Destination $script:instance3 -Database $detachattachdb -DetachAttach -Reattach -Force #-WarningAction SilentlyContinue
            $results.Status | Should Be "Successful"
        }
        
        $db1 = Get-DbaDatabase -SqlInstance $script:instance2 -Database $detachattachdb
        $db2 = Get-DbaDatabase -SqlInstance $script:instance3 -Database $detachattachdb
        
        It "should not be null"  {
            $db1.Name | Should Be $detachattachdb
            $db2.Name | Should Be $detachattachdb
        }
        
        It "Name, recovery model, and status should match" {
            # Compare its variable
            $db1.Name | Should -Be $db2.Name
            $db1.RecoveryModel | Should -Be $db2.RecoveryModel
            $db1.Status | Should -Be $db2.Status
            $db1.Owner | Should -Be $db2.Owner
        }
        
        It "Should say skipped" {
            $results = Copy-DbaDatabase -Source $script:instance2 -Destination $script:instance3 -Database $detachattachdb -DetachAttach -Reattach
            $results.Status | Should be "Skipped"
            $results.Notes | Should be "Already exists"
        }
    }
    
    Context "Backup restore" {
        Get-DbaProcess -SqlInstance $script:instance2, $script:instance3 -Program 'dbatools PowerShell module - dbatools.io' | Stop-DbaProcess -WarningAction SilentlyContinue
        $results = Copy-DbaDatabase -Source $script:instance2 -Destination $script:instance3 -Database $backuprestoredb -BackupRestore -NetworkShare $NetworkPath 3>$null
        
        It "copies a database successfully" {
            $results.Name -eq $backuprestoredb
            $results.Status -eq "Successful"
        }
        
        It "retains its name, recovery model, and status." {
            $dbs = Get-DbaDatabase -SqlInstance $script:instance2, $script:instance3 -Database $backuprestoredb
            $dbs[0].Name -ne $null
            # Compare its variables
            $dbs[0].Name -eq $dbs[1].Name
            $dbs[0].RecoveryModel -eq $dbs[1].RecoveryModel
            $dbs[0].Status -eq $dbs[1].Status
            $dbs[0].Owner -eq $dbs[1].Owner
        }
        
        # needs regr test that uses $backuprestoredb once #3377 is fixed
        It  "Should say skipped" {
            $result = Copy-DbaDatabase -Source $script:instance2 -Destination $script:instance3 -Database $backuprestoredb2 -BackupRestore -NetworkShare $NetworkPath 3>$null
            $result.Status | Should be "Skipped"
            $result.Notes | Should be "Already exists"
        }
        
        # needs regr test once #3377 is fixed
        if (-not $env:appveyor) {
            It "Should overwrite when forced to" {
                #regr test for #3358
                $result = Copy-DbaDatabase -Source $script:instance2 -Destination $script:instance3 -Database $backuprestoredb2 -BackupRestore -NetworkShare $NetworkPath -Force
                $result.Status | Should be "Successful"
            }
        }
    }
}
tools\dbatools\tests\Copy-DbaDatabaseAssembly.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"
Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        Get-DbaProcess -SqlInstance $script:instance2, $script:instance3 -Program 'dbatools PowerShell module - dbatools.io' | Stop-DbaProcess -WarningAction SilentlyContinue
        $server = Connect-DbaInstance -SqlInstance $script:instance3
        $server.Query("CREATE DATABASE dbclrassembly")
        $server.Query("EXEC sp_configure 'CLR ENABLED' , '1'")
        $server.Query("RECONFIGURE")
        $server = Connect-DbaInstance -SqlInstance $script:instance2
        $server.Query("CREATE DATABASE dbclrassembly")
        $server.Query("EXEC sp_configure 'CLR ENABLED' , '1'")
        $server.Query("RECONFIGURE")
        $server.Query("ALTER DATABASE dbclrassembly SET TRUSTWORTHY ON")
        $db = Get-Dbadatabase -SqlInstance $script:instance2 -Database dbclrassembly
        $db.Query("CREATE ASSEMBLY [resolveDNS] AUTHORIZATION [dbo] FROM 0x4D5A90000300000004000000FFFF0000B800000000000000400000000000000000000000000000000000000000000000000000000000000000000000800000000E1FBA0E00B409CD21B8014CCD21546869732070726F6772616D2063616E6E6F742062652072756E20696E20444F53206D6F64652E0D0D0A2400000000000000504500004C010300457830570000000000000000E00002210B010B000008000000060000000000002E260000002000000040000000000010002000000002000004000000000000000400000000000000008000000002000000000000030040850000100000100000000010000010000000000000100000000000000000000000E02500004B00000000400000B002000000000000000000000000000000000000006000000C000000A82400001C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000080000000000000000000000082000004800000000000000000000002E7465787400000034060000002000000008000000020000000000000000000000000000200000602E72737263000000B00200000040000000040000000A0000000000000000000000000000400000402E72656C6F6300000C0000000060000000020000000E0000000000000000000000000000400000420000000000000000000000000000000010260000000000004800000002000500A42000000404000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001B3001002F000000010000110000026F0500000A280600000A6F0700000A6F0800000A0A06730900000A0BDE0B260002730900000A0BDE0000072A0001100000000001002021000B010000011E02280A00000A2A42534A4201000100000000000C00000076322E302E35303732370000000005006C00000070010000237E0000DC010000A401000023537472696E67730000000080030000080000002355530088030000100000002347554944000000980300006C00000023426C6F620000000000000002000001471502000900000000FA253300160000010000000A0000000200000002000000010000000A0000000400000001000000010000000300000000000A0001000000000006003E0037000A006600510006009D008A000F00B10000000600E000C00006000001C0000A00440129010600590137000E00700165010E007401650100000000010000000000010001000100100019000000050001000100502000000000960070000A0001009C200000000086187D001000020000000100830019007D00140029007D001A0031007D00100039007D00100041006001240049008001280051008D01240009009A01240011007D002E0009007D001000200023001F002E000B0039002E00130042002E001B004B0033000480000000000000000000000000000000001E01000002000000000000000000000001002E00000000000200000000000000000000000100450000000000020000000000000000000000010037000000000000000000003C4D6F64756C653E007265736F6C7665444E532E646C6C0055736572446566696E656446756E6374696F6E73006D73636F726C69620053797374656D004F626A6563740053797374656D2E446174610053797374656D2E446174612E53716C54797065730053716C537472696E67004950746F486F73744E616D65002E63746F72006970616464720053797374656D2E446961676E6F73746963730044656275676761626C6541747472696275746500446562756767696E674D6F6465730053797374656D2E52756E74696D652E436F6D70696C6572536572766963657300436F6D70696C6174696F6E52656C61786174696F6E734174747269627574650052756E74696D65436F6D7061746962696C697479417474726962757465007265736F6C7665444E53004D6963726F736F66742E53716C5365727665722E5365727665720053716C46756E6374696F6E41747472696275746500537472696E67005472696D0053797374656D2E4E657400446E73004950486F7374456E74727900476574486F7374456E747279006765745F486F73744E616D6500546F537472696E6700000003200000000000BBBB2D2F51E12E4791398BFA79459ABA0008B77A5C561934E08905000111090E03200001052001011111042001010804010000000320000E05000112290E042001010E0507020E11090801000701000000000801000800000000001E01000100540216577261704E6F6E457863657074696F6E5468726F7773010000000000004578305700000000020000001C010000C4240000C40600005253445357549849C5462E43AD588F97CA53634201000000633A5C74656D705C4461746162617365315C4461746162617365315C6F626A5C44656275675C7265736F6C7665444E532E706462000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000826000000000000000000001E260000002000000000000000000000000000000000000000000000102600000000000000005F436F72446C6C4D61696E006D73636F7265652E646C6C0000000000FF25002000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100100000001800008000000000000000000000000000000100010000003000008000000000000000000000000000000100000000004800000058400000540200000000000000000000540234000000560053005F00560045005200530049004F004E005F0049004E0046004F0000000000BD04EFFE00000100000000000000000000000000000000003F000000000000000400000002000000000000000000000000000000440000000100560061007200460069006C00650049006E0066006F00000000002400040000005400720061006E0073006C006100740069006F006E00000000000000B004B4010000010053007400720069006E006700460069006C00650049006E0066006F0000009001000001003000300030003000300034006200300000002C0002000100460069006C0065004400650073006300720069007000740069006F006E000000000020000000300008000100460069006C006500560065007200730069006F006E000000000030002E0030002E0030002E003000000040000F00010049006E007400650072006E0061006C004E0061006D00650000007200650073006F006C007600650044004E0053002E0064006C006C00000000002800020001004C006500670061006C0043006F00700079007200690067006800740000002000000048000F0001004F0072006900670069006E0061006C00460069006C0065006E0061006D00650000007200650073006F006C007600650044004E0053002E0064006C006C0000000000340008000100500072006F006400750063007400560065007200730069006F006E00000030002E0030002E0030002E003000000038000800010041007300730065006D0062006C0079002000560065007200730069006F006E00000030002E0030002E0030002E003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000C000000303600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
    }
    AfterAll {
        Get-Dbadatabase -SqlInstance $script:instance2, $script:instance3 -Database dbclrassembly | Remove-DbaDatabase -Confirm:$false
    }
    
    It "copies the sample database assembly" {
        $results = Copy-DbaDatabaseAssembly -Source $script:instance2 -Destination $script:instance3 -Assembly resolveDNS
        $results.Name -eq "60000:'us_english'", "60000:'Français'"
        $results.Status -eq 'Successful', 'Successful'
    }
    
    It "doesn't overwrite existing custom errors" {
        $results = Copy-DbaCustomError -Source $script:instance2 -Destination $script:instance3 -CustomError 60000
        $results.Name -eq "60000:'us_english'", "60000:'Français'"
        $results.Status -eq 'Skipped', 'Skipped'
    }
    
    It "the newly copied custom error exists" {
        $results = Get-DbaCustomError -SqlInstance $script:instance2
        $results.ID -contains 60000
    }
}
tools\dbatools\tests\Copy-DbaLinkedServer.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $createsql = "EXEC master.dbo.sp_addlinkedserver @server = N'dbatoolsci_localhost', @srvproduct=N'SQL Server';
        EXEC master.dbo.sp_addlinkedsrvlogin @rmtsrvname=N'dbatoolsci_localhost',@useself=N'False',@locallogin=NULL,@rmtuser=N'testuser1',@rmtpassword='supfool';
        EXEC master.dbo.sp_addlinkedserver @server = N'dbatoolsci_localhost2', @srvproduct=N'', @provider=N'SQLNCLI10';
        EXEC master.dbo.sp_addlinkedsrvlogin @rmtsrvname=N'dbatoolsci_localhost2',@useself=N'False',@locallogin=NULL,@rmtuser=N'testuser1',@rmtpassword='supfool';"
        
        $server1 = Connect-DbaInstance -SqlInstance $script:instance2
        $server2 = Connect-DbaInstance -SqlInstance $script:instance3
        $server1.Query($createsql)
    }
    AfterAll {
        $dropsql = "EXEC master.dbo.sp_dropserver @server=N'dbatoolsci_localhost', @droplogins='droplogins';
        EXEC master.dbo.sp_dropserver @server=N'dbatoolsci_localhost2', @droplogins='droplogins'"

        try {
            $server1.Query($dropsql)
            $server2.Query($dropsql)
        }
        catch {}
    }

    Context "Copy linked server with the same properties" {
        It "copies successfully" {
            $result = Copy-DbaLinkedServer -Source $server1 -Destination $server2 -LinkedServer dbatoolsci_localhost -WarningAction SilentlyContinue
            $result | Select-Object -ExpandProperty Name -Unique | Should Be "dbatoolsci_localhost"
            $result | Select-Object -ExpandProperty Status -Unique | Should Be "Successful"
        }

        It "retains the same properties" {
            $LinkedServer1 = Get-DbaLinkedServer -SqlInstance $server1 -LinkedServer dbatoolsci_localhost -WarningAction SilentlyContinue
            $LinkedServer2 = Get-DbaLinkedServer -SqlInstance $server2 -LinkedServer dbatoolsci_localhost -WarningAction SilentlyContinue

            # Compare its value
            $LinkedServer1.Name | Should Be $LinkedServer2.Name
            $LinkedServer1.LinkedServer | Should Be $LinkedServer2.LinkedServer
        }

        It "skips existing linked servers" {
            $results = Copy-DbaLinkedServer -Source $server1 -Destination $server2 -LinkedServer dbatoolsci_localhost -WarningAction SilentlyContinue
            $results.Status | Should Be "Skipped"
        }

        It "upgrades SQLNCLI provider based on what is registered" {
            $result = Copy-DbaLinkedServer -Source $server1 -Destination $server2 -LinkedServer dbatoolsci_localhost2 -UpgradeSqlClient
            $server1.LinkedServers.Script() -match 'SQLNCLI10' | Should -Not -BeNullOrEmpty
            $server2.LinkedServers.Script() -match 'SQLNCLI11' | Should -Not -BeNullOrEmpty
        }
    }
}
tools\dbatools\tests\Copy-DbaLogin.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    $logins = "claudio", "port", "tester"

    foreach ($instance in $instances) {
        foreach ($login in $logins) {
            if ($l = Get-DbaLogin -SqlInstance $instance -Login $login) {
                Get-DbaProcess -SqlInstance $instance -Login $login | Stop-DbaProcess
                $l.Drop()
            }
        }
    }

    $null = Invoke-Sqlcmd2 -ServerInstance $script:instance1 -InputFile $script:appveyorlabrepo\sql2008-scripts\logins.sql

    Context "Copy login with the same properties." {
        It "Should copy successfully" {
            $results = Copy-DbaLogin -Source $script:instance1 -Destination $script:instance2 -Login Tester
            $results.Status | Should Be "Successful"
        }

        It "Should retain its same properties" {

            $login1 = Get-Dbalogin -SqlInstance $script:instance1 -login Tester
            $login2 = Get-Dbalogin -SqlInstance $script:instance2 -login Tester

            $login2 | Should Not BeNullOrEmpty

            # Compare its value
            $login1.Name | Should Be $login2.Name
            $login1.Language | Should Be $login2.Language
            $login1.Credential | Should be $login2.Credential
            $login1.DefaultDatabase | Should be $login2.DefaultDatabase
            $login1.IsDisabled | Should be $login2.IsDisabled
            $login1.IsLocked | Should be $login2.IsLocked
            $login1.IsPasswordExpired | Should be $login2.IsPasswordExpired
            $login1.PasswordExpirationEnabled | Should be $login2.PasswordExpirationEnabled
            $login1.PasswordPolicyEnforced | Should be $login2.PasswordPolicyEnforced
            $login1.Sid | Should be $login2.Sid
            $login1.Status | Should be $login2.Status
        }

        It "Should login with newly created Sql Login (also tests credential login) and gets name" {
            $password = ConvertTo-SecureString -Force -AsPlainText tester1
            $cred = New-Object System.Management.Automation.PSCredential ("tester", $password)
            $s = Connect-DbaInstance -SqlInstance $script:instance1 -Credential $cred
            $s.Name | Should Be $script:instance1
        }
    }

    Context "No overwrite" {
        $results = Copy-DbaLogin -Source $script:instance1 -Destination $script:instance2 -Login tester
        It "Should say skipped" {
            $results.Status | Should be "Skipped"
            $results.Notes | Should be "Already exists"
        }
    }

    Context "ExcludeSystemLogin Parameter" {
        $results = Copy-DbaLogin -Source $script:instance1 -Destination $script:instance2 -ExcludeSystemLogin
        It "Should say skipped" {
            $results.Status.Contains('Skipped') | Should Be $true
            $results.Notes.Contains('System login') | Should Be $true
        }
    }
    
    Context "Supports pipe" {
        $results = Get-DbaLogin -SqlInstance $script:instance1 -Login tester | Copy-DbaLogin -Destination $script:instance2 -Force
        It "migrates the one tester login" {
            $results.Name | Should be "tester"
            $results.Status | Should Be "Successful"
        }
    }
}
tools\dbatools\tests\Copy-DbaResourceGovernor.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"
Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        $sql = "CREATE RESOURCE POOL dbatoolsci_prod
                WITH
                (
                     MAX_CPU_PERCENT = 100,
                     MIN_CPU_PERCENT = 50
                )"
        Invoke-DbaSqlQuery -WarningAction SilentlyContinue -SqlInstance $script:instance2 -Query $sql
        $sql = "CREATE WORKLOAD GROUP dbatoolsci_prodprocessing
                WITH
                (
                     IMPORTANCE = MEDIUM
                ) USING dbatoolsci_prod"
        Invoke-DbaSqlQuery -WarningAction SilentlyContinue -SqlInstance $script:instance2 -Query $sql
        $sql = "CREATE RESOURCE POOL dbatoolsci_offhoursprocessing
                WITH
                (
                     MAX_CPU_PERCENT = 50,
                     MIN_CPU_PERCENT = 0
                )"
        Invoke-DbaSqlQuery -WarningAction SilentlyContinue -SqlInstance $script:instance2 -Query $sql
        $sql = "CREATE WORKLOAD GROUP dbatoolsci_goffhoursprocessing
                WITH
                (
                     IMPORTANCE = LOW
                )
                USING dbatoolsci_offhoursprocessing"
        Invoke-DbaSqlQuery -WarningAction SilentlyContinue -SqlInstance $script:instance2 -Query $sql
        $sql = "ALTER RESOURCE GOVERNOR RECONFIGURE"
        Invoke-DbaSqlQuery -WarningAction SilentlyContinue -SqlInstance $script:instance2 -Query $sql
        $sql = "CREATE FUNCTION dbatoolsci_fnRG()
                RETURNS sysname
                WITH SCHEMABINDING
                AS
                BEGIN
                     RETURN N'dbatoolsci_goffhoursprocessing'
                END"
        Invoke-DbaSqlQuery -WarningAction SilentlyContinue -SqlInstance $script:instance2 -Query $sql
        $sql = "ALTER RESOURCE GOVERNOR with (CLASSIFIER_FUNCTION = dbo.dbatoolsci_fnRG); ALTER RESOURCE GOVERNOR RECONFIGURE;"
        Invoke-DbaSqlQuery -WarningAction SilentlyContinue -SqlInstance $script:instance2 -Query $sql
    }
    AfterAll {
        Get-DbaProcess -SqlInstance $script:instance2, $script:instance3 |  Stop-DbaProcess -WarningAction SilentlyContinue
        Invoke-DbaSqlQuery -WarningAction SilentlyContinue -SqlInstance $script:instance2, $script:instance3 -Query "ALTER RESOURCE GOVERNOR WITH (CLASSIFIER_FUNCTION = NULL); ALTER RESOURCE GOVERNOR RECONFIGURE"
        Invoke-DbaSqlQuery -WarningAction SilentlyContinue -SqlInstance $script:instance2, $script:instance3 -Query "DROP FUNCTION [dbo].[dbatoolsci_fnRG];ALTER RESOURCE GOVERNOR RECONFIGURE"
        Invoke-DbaSqlQuery -WarningAction SilentlyContinue -SqlInstance $script:instance2, $script:instance3 -Query "DROP WORKLOAD GROUP [dbatoolsci_prodprocessing];ALTER RESOURCE GOVERNOR RECONFIGURE"
        Invoke-DbaSqlQuery -WarningAction SilentlyContinue -SqlInstance $script:instance2, $script:instance3 -Query "DROP WORKLOAD GROUP [dbatoolsci_goffhoursprocessing];ALTER RESOURCE GOVERNOR RECONFIGURE"
        Invoke-DbaSqlQuery -WarningAction SilentlyContinue -SqlInstance $script:instance2, $script:instance3 -Query "DROP RESOURCE POOL [dbatoolsci_offhoursprocessing];ALTER RESOURCE GOVERNOR RECONFIGURE"
        Invoke-DbaSqlQuery -WarningAction SilentlyContinue -SqlInstance $script:instance2, $script:instance3 -Query "DROP RESOURCE POOL [dbatoolsci_prod];ALTER RESOURCE GOVERNOR RECONFIGURE"
    }

    Context "Command works" {
        It "copies the resource governor successfully" {
            $results = Copy-DbaResourceGovernor -Source $script:instance2 -Destination $script:instance3 -Force -WarningAction SilentlyContinue
            $results.Status.Count | Should -BeGreaterThan 3
            $results.Name | Should -Contain 'dbatoolsci_prod'
        }
        It "returns the proper classifier function" {
            $results = Get-DbaResourceGovernorClassifierFunction -SqlInstance $script:instance3
            $results.Name | Should -Be 'dbatoolsci_fnRG'
        }
    }
}
tools\dbatools\tests\Copy-DbaServerTrigger.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Setup" {
        BeforeAll {
            $triggername = "dbatoolsci-trigger"
            $sql = "CREATE TRIGGER [$triggername] -- Trigger name
                    ON ALL SERVER FOR LOGON -- Tells you it's a logon trigger
                    AS
                    PRINT 'hello'"
            $server = Connect-DbaInstance -SqlInstance $script:instance1
            $server.Query($sql)
        }
        AfterAll {
            $server.Query("DROP TRIGGER [$triggername] ON ALL SERVER")

            try {
                $server1 = Connect-DbaInstance -SqlInstance $script:instance2
                $server1.Query("DROP TRIGGER [$triggername] ON ALL SERVER")
            }
            catch {
                # dont care
            }
        }

        $results = Copy-DbaServerTrigger -Source $script:instance1 -Destination $script:instance2 -WarningVariable warn -WarningAction SilentlyContinue # -ServerTrigger $triggername

        It "should report success" {
            $results.Status | Should Be "Successful"
        }

        # same properties need to be added
    }
}
tools\dbatools\tests\Copy-DbaSpConfigure.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Copy config with the same properties." {
        BeforeAll {
            $sourceconfig = (Get-DbaSpConfigure -SqlInstance $script:instance1 -ConfigName RemoteQueryTimeout).ConfiguredValue
            $destconfig = (Get-DbaSpConfigure -SqlInstance $script:instance2 -ConfigName RemoteQueryTimeout).ConfiguredValue
            # Set it so they don't match
            if ($sourceconfig -and $destconfig) {
                $newvalue = $sourceconfig + $destconfig
                $null = Set-DbaSpConfigure -SqlInstance $script:instance2 -ConfigName RemoteQueryTimeout -Value $newvalue
            }
        }
        AfterAll {
            if ($destconfig -and $destconfig -ne $sourceconfig) {
                $null = Set-DbaSpConfigure -SqlInstance $script:instance2 -ConfigName RemoteQueryTimeout -Value $destconfig
            }
        }

        It "starts with different values" {
            $config1 = Get-DbaSpConfigure -SqlInstance $script:instance1 -ConfigName RemoteQueryTimeout
            $config2 = Get-DbaSpConfigure -SqlInstance $script:instance2 -ConfigName RemoteQueryTimeout
            $config1.ConfiguredValue -ne $config2.ConfiguredValue | Should be $true
        }

        It "copied successfully" {
            $results = Copy-DbaSpConfigure -Source $script:instance1 -Destination $script:instance2 -ConfigName RemoteQueryTimeout
            $results.Status | Should Be "Successful"
        }

        It "retains the same properties" {
            $config1 = Get-DbaSpConfigure -SqlInstance $script:instance1 -ConfigName RemoteQueryTimeout
            $config2 = Get-DbaSpConfigure -SqlInstance $script:instance2 -ConfigName RemoteQueryTimeout
            $config1.ConfiguredValue | Should be $config2.ConfiguredValue
        }

        It "didn't modify the source" {
            $newconfig = (Get-DbaSpConfigure -SqlInstance $script:instance1 -ConfigName RemoteQueryTimeout).ConfiguredValue
            $newconfig -eq $sourceconfig | Should Be $true
        }
    }
}
tools\dbatools\tests\Copy-DbaTableData.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $db = Get-DbaDatabase -SqlInstance $script:instance1 -Database tempdb
        $db2 = Get-DbaDatabase -SqlInstance $script:instance2 -Database tempdb
        $null = $db.Query("CREATE TABLE dbo.dbatoolsci_example (id int);
            INSERT dbo.dbatoolsci_example
            SELECT top 10 1
            FROM sys.objects")
        $null = $db.Query("CREATE TABLE dbo.dbatoolsci_example2 (id int)")
        $null = $db2.Query("CREATE TABLE dbo.dbatoolsci_example3 (id int)")
        $null = $db.Query("CREATE TABLE dbo.dbatoolsci_example4 (id int);
            INSERT dbo.dbatoolsci_example4
            SELECT top 13 1
            FROM sys.objects")
        $null = $db2.Query("CREATE TABLE dbo.dbatoolsci_example (id int)")
        $null = $db2.Query("CREATE TABLE dbo.dbatoolsci_example4 (id int);
            INSERT dbo.dbatoolsci_example4
            SELECT top 13 2
            FROM sys.objects")
    }
    AfterAll {
        $null = $db.Query("DROP TABLE dbo.dbatoolsci_example")
        $null = $db.Query("DROP TABLE dbo.dbatoolsci_example2")
        $null = $db2.Query("DROP TABLE dbo.dbatoolsci_example3")
        $null = $db.Query("DROP TABLE dbo.dbatoolsci_example4")
        $null = $db2.Query("DROP TABLE dbo.dbatoolsci_example4")
        $null = $db2.Query("DROP TABLE dbo.dbatoolsci_example")
    }
    
    It "copies the table data" {
        $null = Copy-DbaTableData -SqlInstance $script:instance1 -Database tempdb -Table dbatoolsci_example -DestinationTable dbatoolsci_example2
        $table1count = $db.Query("select id from dbo.dbatoolsci_example")
        $table2count = $db.Query("select id from dbo.dbatoolsci_example2")
        $table1count.Count | Should -Be $table2count.Count
    }
    
    It "copies the table data to another instance" {
        $null = Copy-DbaTableData -SqlInstance $script:instance1 -Destination $script:instance2 -Database tempdb -Table dbatoolsci_example -DestinationTable dbatoolsci_example3
        $table1count = $db.Query("select id from dbo.dbatoolsci_example")
        $table2count = $db2.Query("select id from dbo.dbatoolsci_example3")
        $table1count.Count | Should -Be $table2count.Count
    }
    
    It "supports piping" {
        $null = Get-DbaTable -SqlInstance $script:instance1 -Database tempdb -Table dbatoolsci_example | Copy-DbaTableData -DestinationTable dbatoolsci_example2 -Truncate
        $table1count = $db.Query("select id from dbo.dbatoolsci_example")
        $table2count = $db.Query("select id from dbo.dbatoolsci_example2")
        $table1count.Count | Should -Be $table2count.Count
    }
    
    It "supports piping more than one table" {
        $results = Get-DbaTable -SqlInstance $script:instance1 -Database tempdb -Table dbatoolsci_example2, dbatoolsci_example | Copy-DbaTableData -DestinationTable dbatoolsci_example2
        $results.Count | Should -Be 2
    }
    
    It "opens and closes connections properly" {
        #regression test, see #3468
        $results = Get-DbaTable -SqlInstance $script:instance1 -Database tempdb -Table 'dbo.dbatoolsci_example', 'dbo.dbatoolsci_example4' | Copy-DbaTableData -Destination $script:instance2 -DestinationDatabase tempdb -KeepIdentity -KeepNulls -BatchSize 5000 -Truncate
        $results.Count | Should -Be 2
        $table1dbcount = $db.Query("select id from dbo.dbatoolsci_example")
        $table4dbcount = $db2.Query("select id from dbo.dbatoolsci_example4")
        $table1db2count = $db.Query("select id from dbo.dbatoolsci_example")
        $table4db2count = $db2.Query("select id from dbo.dbatoolsci_example4")
        $table1dbcount.Count | Should -Be $table1db2count.Count
        $table4dbcount.Count | Should -Be $table4db2count.Count
        $results[0].RowsCopied | Should -Be 10
        $results[1].RowsCopied | Should -Be 13
        $table4db2check = $db2.Query("select id from dbo.dbatoolsci_example4 where id = 1")
        $table4db2check.Count | Should -Be 13
    }
}
tools\dbatools\tests\Copy-DbaXESessionTemplate.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Get Template Index" {
        $null = Copy-DbaXESessionTemplate *>1
        $source = ((Get-DbaXESessionTemplate -Path $Path | Where-Object Source -ne Microsoft).Path | Select-Object -First 1).Name
        It "copies the files properly" {
            Get-ChildItem "$home\Documents\SQL Server Management Studio\Templates\XEventTemplates" | Where-Object Name -eq $source | Should Not Be Null
        }
    }
}
tools\dbatools\tests\dbatools.Tests.ps1
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
$Path = Split-Path -Parent $MyInvocation.MyCommand.Path
$ModulePath = (Get-Item $Path).Parent.FullName
$ModuleName = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -Replace ".Tests.ps1"
#$ManifestPath = "$ModulePath\$ModuleName.psd1"

Describe "$ModuleName Aliases" -tag Build , Aliases {
    ## Get the Aliases that should be set from the psm1 file

    $psm1 = Get-Content $ModulePath\$ModuleName.psm1 -Verbose
    $Matches = [regex]::Matches($psm1, "AliasName`"\s=\s`"(\w*-\w*)`"")
    $Aliases = $Matches.ForEach{$_.Groups[1].Value}

    foreach ($Alias in $Aliases) {
        Context "Testing $Alias Alias" {
            $Definition = (Get-Alias $Alias).Definition
            It "$Alias Alias should exist" {
                Get-Alias $Alias| Should Not BeNullOrEmpty
            }
            It "$Alias Aliased Command $Definition Should Exist" {
                Get-Command $Definition -ErrorAction SilentlyContinue | Should Not BeNullOrEmpty
            }
        }
    }
}

Describe "$ModuleName indentation" -Tag 'Compliance' {
    $AllFiles = Get-ChildItem -Path $ModulePath -File -Recurse  -Filter '*.ps*1'

    foreach ($f in $AllFiles) {
        $LeadingTabs = Select-String -Path $f -Pattern '^[\t]+'
        if ($LeadingTabs.Count -gt 0) {
            It "$f is not indented with tabs (line(s) $($LeadingTabs.LineNumber -join ','))" {
                $LeadingTabs.Count | Should Be 0
            }
        }
        $TrailingSpaces = Select-String -Path $f -Pattern '([^ \t\r\n])[ \t]+$'
        if ($TrailingSpaces.Count -gt 0) {
            It "$f has no trailing spaces (line(s) $($TrailingSpaces.LineNumber -join ','))" {
                $TrailingSpaces.Count | Should Be 0
            }
        }
    }
}

Describe "$ModuleName ScriptAnalyzerErrors" -Tag 'Compliance' {
    $ScriptAnalyzerErrors = @()
    $ScriptAnalyzerErrors += Invoke-ScriptAnalyzer -Path "$ModuleBase\functions" -Severity Error
    $ScriptAnalyzerErrors += Invoke-ScriptAnalyzer -Path "$ModuleBase\internal\functions" -Severity Error
    if ($ScriptAnalyzerErrors.Count -gt 0) {
        foreach($err in $ScriptAnalyzerErrors) {
            It "$($err.scriptName) has Error(s) : $($err.RuleName)" {
                $err.Message | Should Be $null
            }
        }
    }
}

# test the module manifest - exports the right functions, processes the right formats, and is generally correct
<#
Describe "Manifest" {

    $Manifest = $null

    It "has a valid manifest" {

        {

            $Script:Manifest = Test-ModuleManifest -Path $ManifestPath -ErrorAction Stop -WarningAction SilentlyContinue

        } | Should Not Throw

    }
## Should be fixed now - Until the issue with requiring full paths for required assemblies is resolved need to keep this commented out RMS 01112016

$Script:Manifest = Test-ModuleManifest -Path $ManifestPath -ErrorAction SilentlyContinue
    It "has a valid name" {

        $Script:Manifest.Name | Should Be $ModuleName

    }



    It "has a valid root module" {

        $Script:Manifest.RootModule | Should Be "$ModuleName.psm1"

    }



    It "has a valid Description" {

        $Script:Manifest.Description | Should Be 'Provides extra functionality for SQL Server Database admins and enables SQL Server instance migrations.'

    }

    It "has a valid Author" {
        $Script:Manifest.Author | Should Be 'Chrissy LeMaire'
    }

    It "has a valid Company Name" {
        $Script:Manifest.CompanyName | Should Be 'dbatools.io'
    }
    It "has a valid guid" {

        $Script:Manifest.Guid | Should Be '9d139310-ce45-41ce-8e8b-d76335aa1789'

    }
    It "has valid PowerShell version" {
        $Script:Manifest.PowerShellVersion | Should Be '3.0'
    }

    It "has valid  required assemblies" {
        {$Script:Manifest.RequiredAssemblies -eq @()} | Should Be $true
    }

    It "has a valid copyright" {

        $Script:Manifest.CopyRight | Should BeLike '* Chrissy LeMaire'

    }



 # Don't want this just yet

    It 'exports all public functions' {

        $FunctionFiles = Get-ChildItem "$ModulePath\functions" -Filter *.ps1 | Select-Object -ExpandProperty BaseName

        $FunctionNames = $FunctionFiles

        $ExFunctions = $Script:Manifest.ExportedFunctions.Values.Name
        $ExFunctions
        foreach ($FunctionName in $FunctionNames)

        {

            $ExFunctions -contains $FunctionName | Should Be $true

        }

    }
}
#>
tools\dbatools\tests\Disable-DbaAgHadr.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag "UnitTests" {
    Context "Validate parameters" {
        $paramCount = 4
        <#
            Get commands, Default count = 11
            Commands with SupportShouldProcess = 13
        #>
        $defaultParamCount = 13
        [object[]]$params = (Get-ChildItem function:\Disable-DbaAgHadr).Parameters.Keys
        $knownParameters = 'SqlInstance', 'Credential', 'Force', 'EnableException'
        It "Should contian our specifc parameters" {
            ((Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}

Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        Enable-DbaAgHadr -SqlInstance $script:instance3 -Confirm:$false -Force
    }
    
    $results = Disable-DbaAgHadr -SqlInstance $script:instance3 -Confirm:$false -Force
    
    It "disables hadr" {
        $results.IsHadrEnabled | Should -Be $false
    }
}
tools\dbatools\tests\Disable-DbaForceNetworkEncryption.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    $results = Disable-DbaForceNetworkEncryption $script:instance1 -EnableException

    It "returns false" {
        $results.ForceEncryption -eq $false
    }
}
tools\dbatools\tests\Disable-DbaTraceFlag.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    Context "Verifying TraceFlag output" {
        BeforeAll {
            $server = Connect-DbaInstance -SqlInstance $script:instance1
            $startingtfs = Get-DbaTraceFlag -SqlInstance $server
            $safetraceflag = 3226

            if ($startingtfs.TraceFlag -notcontains $safetraceflag) {
                $null = $server.Query("DBCC TRACEON($safetraceflag,-1)")
            }

        }
        AfterAll {
            if ($startingtfs.TraceFlag -contains $safetraceflag) {
                $server.Query("DBCC TRACEON($safetraceflag,-1)  WITH NO_INFOMSGS")
            }
        }

        $results = Disable-DbaTraceFlag -SqlInstance $server -TraceFlag $safetraceflag

        It "Return $safetraceflag as disabled" {
            $results.TraceFlag -contains $safetraceflag | Should Be $true
        }
    }
}
tools\dbatools\tests\Dismount-DbaDatabase.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    
    # Setting up the environment we need to test the cmdlet
    BeforeAll {
        # Everything in here gets executed before anything else in this context
        Get-DbaProcess -SqlInstance $script:instance3 -Program 'dbatools PowerShell module - dbatools.io' | Stop-DbaProcess -WarningAction SilentlyContinue
        # Setting up variables names. If you want them to persist between all of the pester blocks, they can be moved outside
        $dbname = "dbatoolsci_detachattach"
        # making room in the remote case a db with the same name exists
        $null = Get-DbaDatabase -SqlInstance $script:instance3 -Database $dbname | Remove-DbaDatabase -Confirm:$false
        
        $server = Connect-DbaInstance -SqlInstance $script:instance3
        $db1 = "dbatoolsci_dbsetstate_online"
        $server.Query("CREATE DATABASE $dbname")
        
        # memorizing $fileStructure for a later test
        $fileStructure = New-Object System.Collections.Specialized.StringCollection
        
        foreach ($file in (Get-DbaDatabaseFile -SqlInstance $script:instance3 -Database $dbname).PhysicalName) {
            $null = $fileStructure.Add($file)
        }
    }
    
    # Everything we create/touch/mess with should be reverted to a "clean" state whenever possible
    AfterAll {
        # this gets executed always (think "finally" in try/catch/finally) and it's the best place for final cleanups
        $null = Mount-DbaDatabase -SqlInstance $script:instance3 -Database $dbname -FileStructure $script:fileStructure
        $null = Get-DbaDatabase -SqlInstance $script:instance3 -Database $dbname | Remove-DbaDatabase -Confirm:$false
    }
    
    # Actual tests
    Context "Detaches a single database and tests to ensure the alias still exists" {
        $results = Dismount-DbaDatabase -SqlInstance $script:instance3 -Database $dbname -Force
        
        It "was successfull" {
            $results.DetachResult | Should Be "Success"
        }
        
        It "removed just one database" {
            $results.Database | Should Be $dbname
        }
        
        It "has the correct properties" {
            $ExpectedProps = 'ComputerName,InstanceName,SqlInstance,Database,DetachResult'.Split(',')
            ($results.PsObject.Properties.Name | Sort-Object) | Should Be ($ExpectedProps | Sort-Object)
        }
    }
    Context "Database Detachment" {
        BeforeAll {
            Get-DbaProcess -SqlInstance $script:instance3 -Program 'dbatools PowerShell module - dbatools.io' | Stop-DbaProcess -WarningAction SilentlyContinue
            $server = Connect-DbaInstance -SqlInstance $script:instance3
            $db1 = "dbatoolsci_dbsetstate_detached"
            $server.Query("CREATE DATABASE $db1")
            Get-DbaProcess -SqlInstance $script:instance3 -Program 'dbatools PowerShell module - dbatools.io' | Stop-DbaProcess -WarningAction SilentlyContinue
            $server = Connect-DbaInstance -SqlInstance $script:instance3
            $db2 = "dbatoolsci_dbsetstate_detached_withSnap"
            
            $server.Query("CREATE DATABASE $db2")
            $null = New-DbaDbSnapshot -SqlInstance $script:instance3 -Database $db2
            $fileStructure = New-Object System.Collections.Specialized.StringCollection
            foreach ($file in (Get-DbaDatabaseFile -SqlInstance $script:instance3 -Database $db1).PhysicalName) {
                $null = $fileStructure.Add($file)
            }
            Stop-DbaProcess -SqlInstance $script:instance3 -Database $db1
        }
        AfterAll {
            $null = Remove-DbaDbSnapshot -SqlInstance $script:instance3 -Database $db2 -Force
            $null = Mount-DbaDatabase -SqlInstance $script:instance3 -Database $db1 -FileStructure $fileStructure
            $null = Get-DbaDatabase -SqlInstance $script:instance3 -Database $db1, $db2 | Remove-DbaDatabase -Confirm:$false
        }
        
        It "Skips detachment if database is snapshotted" {
            $result = Dismount-DbaDatabase -SqlInstance $script:instance3 -Database $db2 -Force -WarningAction SilentlyContinue -WarningVariable warn
            $result | Should Be $null
            $warn -match "snapshot" | Should Be $true
            $result = Get-DbaDatabase -SqlInstance $script:instance3 -Database $db2
            $result | Should Not Be $null
        }
        $null = Stop-DbaProcess -SqlInstance $script:instance3 -Database $db1
        $result = Dismount-DbaDatabase -SqlInstance $script:instance3 -Database $db1
        It "Detaches the database correctly" {
            $result = Get-DbaDatabase -SqlInstance $script:instance3 -Database $db1
            $result | Should Be $null
        }
    }
}
#$script:instance2 - to make it show up in appveyor, long story
tools\dbatools\tests\Enable-DbaAgHadr.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag "UnitTests" {
    Context "Validate parameters" {
        $paramCount = 4
        <#
            Get commands, Default count = 11
            Commands with SupportShouldProcess = 13
        #>
        $defaultParamCount = 13
        [object[]]$params = (Get-ChildItem function:\Enable-DbaAgHadr).Parameters.Keys
        $knownParameters = 'SqlInstance', 'Credential', 'Force', 'EnableException'
        it "Should contian our specifc parameters" {
            ((Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}

Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        $current = Get-DbaAgHadr -SqlInstance $script:instance3 # for appveyor $script:instance2
        if ($current.IsHadrEnabled) {
            Disable-DbaAgHadr -SqlInstance $script:instance3 -Confirm:$false -WarningAction SilentlyContinue -Force
        }
    }
    AfterAll {
        if (-not $current.IsHadrEnabled) {
            Disable-DbaAgHadr -SqlInstance $script:instance3 -Confirm:$false -WarningAction SilentlyContinue -Force
        }
    }
    
    $results = Enable-DbaAgHadr -SqlInstance $script:instance3 -Confirm:$false -Force
    
    It "enables hadr" {
        $results.IsHadrEnabled | Should -Be $true
    }
}
tools\dbatools\tests\Enable-DbaForceNetworkEncryption.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    $results = Enable-DbaForceNetworkEncryption $script:instance1 -EnableException

    It "returns true" {
        $results.ForceEncryption -eq $true
    }
}
tools\dbatools\tests\Enable-DbaTraceFlag.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    Context "Verifying TraceFlag output" {
        BeforeAll {
            $server = Connect-DbaInstance -SqlInstance $script:instance2
            $startingtfs = Get-DbaTraceFlag -SqlInstance $script:instance2
            $safetraceflag = 3226

            if ($startingtfs.TraceFlag -contains $safetraceflag) {
                $server.Query("DBCC TRACEOFF($safetraceflag,-1)")
            }
        }
        AfterAll {
            if ($startingtfs.TraceFlag -notcontains $safetraceflag) {
                $server.Query("DBCC TRACEOFF($safetraceflag,-1)")
            }
        }

        $results = Enable-DbaTraceFlag -SqlInstance $server -TraceFlag $safetraceflag

        It "Return $safetraceflag as enabled" {
            $results.TraceFlag -contains $safetraceflag | Should Be $true
        }
    }
}
tools\dbatools\tests\Expand-DbaTLogResponsibly.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $server = Connect-DbaInstance -SqlInstance $script:instance1
        $db1 = "dbatoolsci_expand"
        $server.Query("CREATE DATABASE $db1")
    }
    AfterAll {
        Remove-DbaDatabase -Confirm:$false -SqlInstance $script:instance1 -Database $db1
    }

    $results = Expand-DbaTLogResponsibly -SqlInstance $script:instance1 -Database $db1 -TargetLogSizeMB 128

    It -Skip "Should have correct properties" {
        $ExpectedProps = 'ComputerName,InstanceName,SqlInstance,Database,ID,Name,LogFileCount,InitialSize,CurrentSize,InitialVLFCount,CurrentVLFCount'.Split(',')
        ($results[0].PsObject.Properties.Name | Sort-Object) | Should Be ($ExpectedProps | Sort-Object)
    }

    It "Should have database name of $db1" {
        foreach ($result in $results) {
            $result.InitialSize -gt $result.CurrentSize
        }
    }

    It "Should have grown the log file" {
        foreach ($result in $results) {
            $result.InitialSize -gt $result.CurrentSize
        }
    }
}
tools\dbatools\tests\Export-DbaAvailabilityGroup.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    $dbname = "dbatoolsci_agroupdb"
    if (-not $env:appveyor) {
        BeforeAll {
            # $script:instance2 - to make it appear in the proper place on appveyor
            Get-DbaProcess -SqlInstance $script:instance3 -Program 'dbatools PowerShell module - dbatools.io' | Stop-DbaProcess -WarningAction SilentlyContinue
            $server = Connect-DbaInstance -SqlInstance $script:instance3
            $computername = $server.NetName
            $servicename = $server.ServiceName
            if ($servicename -eq 'MSSQLSERVER') {
                $instancename = "$computername"
            }
            else {
                $instancename = "$computername\$servicename"
            }
            $server.Query("create database $dbname")
            $backup = Get-DbaDatabase -SqlInstance $script:instance3 -Database $dbname | Backup-DbaDatabase
            $server.Query("IF NOT EXISTS (select * from sys.symmetric_keys where name like '%DatabaseMasterKey%') CREATE MASTER KEY ENCRYPTION BY PASSWORD = '<StrongPassword>'")
            $server.Query("IF EXISTS ( SELECT * FROM sys.tcp_endpoints WHERE name = 'End_Mirroring') DROP ENDPOINT endpoint_mirroring")
            $server.Query("CREATE CERTIFICATE dbatoolsci_AGCert WITH SUBJECT = 'AG Certificate'")
            $server.Query("CREATE ENDPOINT dbatoolsci_AGEndpoint
                            STATE = STARTED
                            AS TCP (LISTENER_PORT = 5022,LISTENER_IP = ALL)
                            FOR DATABASE_MIRRORING (AUTHENTICATION = CERTIFICATE dbatoolsci_AGCert,ROLE = ALL)")
            $server.Query("CREATE AVAILABILITY GROUP dbatoolsci_agroup
                            WITH (DB_FAILOVER = OFF, DTC_SUPPORT = NONE, CLUSTER_TYPE = NONE)
                            FOR DATABASE $dbname REPLICA ON N'$instancename'
                            WITH (ENDPOINT_URL = N'TCP://$computername`:5022', FAILOVER_MODE = MANUAL, AVAILABILITY_MODE = SYNCHRONOUS_COMMIT)")
        }
        AfterAll {
            try {
                if ($backup.BackupPath) { Remove-Item -Path $backup.BackupPath -ErrorAction SilentlyContinue }
                $server.Query("DROP AVAILABILITY GROUP dbatoolsci_agroup")
                Get-DbaDatabase -SqlInstance $script:instance3 -Database $dbname | Remove-DbaDatabase -Confirm:$false
                $server.Query("DROP ENDPOINT dbatoolsci_AGEndpoint")
                $server.Query("DROP CERTIFICATE dbatoolsci_AGCert")
            }
            catch {
                # dont care
            }
        }
    }
    Context "exports ags" {
        $results = Export-DbaAvailabilityGroup -SqlInstance $script:instance3
        It "returns file objects and one should be the name of the availability group" {
            $results.BaseName | Should -Contain 'dbatoolsci_agroup'
        }
        It "the files it returns should contain the term 'CREATE AVAILABILITY GROUP'" {
            $results | Select-String 'CREATE AVAILABILITY GROUP' | Should -Not -Be $null
        }
        $results | Remove-Item -ErrorAction SilentlyContinue
        $results = Export-DbaAvailabilityGroup -SqlInstance $script:instance3 -AvailabilityGroup dbatoolsci_agroup -FilePath C:\temp
        It "returns a single result" {
            $results.BaseName | Should -Be 'dbatoolsci_agroup'
        }
        It "the file it returns should contain the term 'CREATE AVAILABILITY GROUP'" {
            $results | Select-String 'CREATE AVAILABILITY GROUP' | Should -Not -Be $null
        }
        It "the file's path should match C:\temp" {
            $results.FullName -match 'C:\\temp' | Should -Be $true
        }
        $results | Remove-Item -ErrorAction SilentlyContinue
    }
}
# $script:instance2 - to make it appear in the proper place on appveyor
tools\dbatools\tests\Export-DbaDacpac.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        try {
            $dbname = "dbatoolsci_exportdacpac"
            $server = Connect-DbaInstance -SqlInstance $script:instance1
            $null = $server.Query("Create Database [$dbname]")
            $db = Get-DbaDatabase -SqlInstance $script:instance1 -Database $dbname
            $null = $db.Query("CREATE TABLE dbo.example (id int);
            INSERT dbo.example
            SELECT top 100 1
            FROM sys.objects")
        }
        catch { } # No idea why appveyor can't handle this
    }
    AfterAll {
        Remove-DbaDatabase -SqlInstance $script:instance1 -Database $dbname -Confirm:$false
    }

    if ((Get-DbaTable -SqlInstance $script:instance1 -Database $dbname -Table example)) {
        # Sometimes appveyor bombs
        It "exports a dacpac" {
            $results = Export-DbaDacpac -SqlInstance $script:instance1 -Database $dbname
            if (($results).Path) {
                Remove-Item -Confirm:$false -Path ($results).Path -ErrorAction SilentlyContinue
            }
        }
    }
}
tools\dbatools\tests\Export-DbaDiagnosticQuery.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    AfterAll {
        (Get-ChildItem "$env:temp\dbatoolsci") | Remove-Item
    }
    Context "Verifying output" {
        It "exports results to one file and creates directory if required" {
            $results = Invoke-DbaDiagnosticQuery -SqlInstance $script:instance2 -QueryName 'Memory Clerk Usage' | Export-DbaDiagnosticQuery -Path "$env:temp\dbatoolsci"
            (Get-ChildItem "$env:temp\dbatoolsci").Count | Should Be 1
        }
    }
}
tools\dbatools\tests\Export-DbaLogin.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

$outputFile = "dbatoolsci_exportdbalogin.sql"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        try {
            $random = Get-Random
            $dbname1 = "dbatoolsci_exportdbalogin1$random"
            $login1 = "dbatoolsci_exportdbalogin_login1$random"
            $user1 = "dbatoolsci_exportdbalogin_user1$random"
            $server = Connect-DbaInstance -SqlInstance $script:instance1
            $null = $server.Query("CREATE DATABASE [$dbname1]")
            $null = $server.Query("CREATE LOGIN [$login1] WITH PASSWORD = 'GoodPass1234!'")
            $server.Databases[$dbname1].ExecuteNonQuery("CREATE USER [$user1] FOR LOGIN [$login1]")

            $dbname2 = "dbatoolsci_exportdbalogin2$random"
            $login2 = "dbatoolsci_exportdbalogin_login2$random"
            $user2 = "dbatoolsci_exportdbalogin_user2$random"
            $server = Connect-DbaInstance -SqlInstance $script:instance1
            $null = $server.Query("CREATE DATABASE [$dbname2]")
            $null = $server.Query("CREATE LOGIN [$login2] WITH PASSWORD = 'GoodPass1234!'")
            $null = $server.Query("ALTER LOGIN [$login2] DISABLE")
            $null = $server.Query("DENY CONNECT SQL TO [$login2]")

            if ($server.VersionMajor -lt 11) {
                $null = $server.Query("EXEC sys.sp_addsrvrolemember @rolename=N'dbcreator', @loginame=N'$login2'")
            } else {
                $null = $server.Query("ALTER SERVER ROLE [dbcreator] ADD MEMBER [$login2]")
            }
            $null = $server.Query("GRANT SELECT ON sys.databases TO [$login2] WITH GRANT OPTION")
            $server.Databases[$dbname2].ExecuteNonQuery("CREATE USER [$user2] FOR LOGIN [$login2]")
        }
        catch { } # No idea why appveyor can't handle this
    }
    AfterAll {
        Remove-DbaDatabase -SqlInstance $script:instance1 -Database $dbname1 -Confirm:$false
        Remove-DbaLogin -SqlInstance $script:instance1 -Login $login1 -Confirm:$false

        Remove-DbaDatabase -SqlInstance $script:instance1 -Database $dbname2 -Confirm:$false
        Remove-DbaLogin -SqlInstance $script:instance1 -Login $login2 -Confirm:$false

        Remove-Item -Path $outputFile
    }

    It "Filters to specific databases" {
        $output = Export-DbaLogin -SqlInstance $script:instance1 -Database $dbname1 -WarningAction SilentlyContinue

        ([regex]::matches($output, 'USE \[.*?\]').Value | Select-Object -Unique).Count | Should Be 1
    }

    It "Doesn't include database details when using NoDatabase" {
        $output = Export-DbaLogin -SqlInstance $script:instance1 -NoDatabases -WarningAction SilentlyContinue

        ([regex]::matches($output, 'USE \[.*?\]')).Count | Should Be 0
    }

    $output = Export-DbaLogin -SqlInstance $script:instance1 -WarningAction SilentlyContinue
    It "Doesn't filter specific databases" {
        ([regex]::matches($output, 'USE \[.*?\]').Value | Select-Object -Unique).Count | Should BeGreaterThan 1
    }

    It "Exports disabled logins" {
        [regex]::matches($output, "ALTER LOGIN \[.*?\] DISABLE").Count | Should BeGreaterThan 0
    }

    It "Exports deny connects" {
        [regex]::matches($output, "DENY CONNECT SQL TO \[.*?\]").Count | Should BeGreaterThan 0
    }

    It "Exports system role memberships" {
        if ($server.VersionMajor -lt 11) {
            [regex]::matches($output, "EXEC sys.sp_addsrvrolemember @rolename=N'dbcreator', @loginame=N'$login2'").Count | Should BeGreaterThan 0
        } else {
            [regex]::matches($output, "ALTER SERVER ROLE \[.*?\] ADD MEMBER \[.*?\]").Count | Should BeGreaterThan 0
        }
    }

    It "Exports to the specified file" {
        Export-DbaLogin -SqlInstance $script:instance1 -FilePath $outputFile -WarningAction SilentlyContinue

        Test-Path -Path $outputFile | Should Be $true
    }
}
tools\dbatools\tests\Export-DbaPfDataCollectorSetTemplate.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    BeforeEach {
        $null = Get-DbaPfDataCollectorSetTemplate -Template 'Long Running Queries' | Import-DbaPfDataCollectorSetTemplate
    }
    AfterAll {
        $null = Get-DbaPfDataCollectorSet -CollectorSet 'Long Running Queries' | Remove-DbaPfDataCollectorSet -Confirm:$false
    }
    Context "Verifying command returns all the required results" {
        It "returns a file system object" {
            $results = Get-DbaPfDataCollectorSet -CollectorSet 'Long Running Queries' | Export-DbaPfDataCollectorSetTemplate
            $results.BaseName | Should Be 'Long Running Queries'
        }
        It "returns a file system object" {
            $results = Export-DbaPfDataCollectorSetTemplate -CollectorSet 'Long Running Queries'
            $results.BaseName | Should Be 'Long Running Queries'
        }
    }
}
tools\dbatools\tests\Export-DbaRegisteredServer.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        $srvName = "dbatoolsci-server1"
        $group = "dbatoolsci-group1"
        $regSrvName = "dbatoolsci-server12"
        $regSrvDesc = "dbatoolsci-server123"
        
        $newGroup = Add-DbaRegisteredServerGroup -SqlInstance $script:instance1 -Name $group
        $newServer = Add-DbaRegisteredServer -SqlInstance $script:instance1 -ServerName $srvName -Name $regSrvName -Description $regSrvDesc
        
        $srvName2 = "dbatoolsci-server2"
        $group2 = "dbatoolsci-group1a"
        $regSrvName2 = "dbatoolsci-server21"
        $regSrvDesc2 = "dbatoolsci-server321"
        
        $newGroup2 = Add-DbaRegisteredServerGroup -SqlInstance $script:instance1 -Name $group2
        $newServer2 = Add-DbaRegisteredServer -SqlInstance $script:instance1 -ServerName $srvName2 -Name $regSrvName2 -Description $regSrvDesc2
        
        $regSrvName3 = "dbatoolsci-server3"
        $srvName3 = "dbatoolsci-server3"
        $regSrvDesc3 = "dbatoolsci-server3desc"
        
        $newServer3 = Add-DbaRegisteredServer -SqlInstance $script:instance1 -ServerName $srvName3 -Name $regSrvName3 -Description $regSrvDesc3
    }
    AfterAll {
        Get-DbaRegisteredServer -SqlInstance $script:instance1, $script:instance2 | Where-Object Name -match dbatoolsci | Remove-DbaRegisteredServer -Confirm:$false
        Get-DbaRegisteredServerGroup -SqlInstance $script:instance1, $script:instance2 | Where-Object Name -match dbatoolsci | Remove-DbaRegisteredServerGroup -Confirm:$false
        $results, $results2, $results3 | Remove-Item -ErrorAction Ignore
    }
    
    It -Skip "should create an xml file" {
        $results = $newServer | Export-DbaRegisteredServer
        $results -is [System.IO.FileInfo] | Should -Be $true
        $results.Extension -eq '.xml' | Should -Be $true
    }
    
    It "should create a specific xml file when using Path" {
        $results2 = $newGroup2 | Export-DbaRegisteredServer -Path C:\temp\dbatoolsci_regserverexport.xml
        $results2 -is [System.IO.FileInfo] | Should -Be $true
        $results2.FullName | Should -Be 'C:\temp\dbatoolsci_regserverexport.xml'
        Get-Content -Path $results2 -Raw | Should -Match dbatoolsci-group1a
    }
    
    It "creates an importable xml file" {
        $results3 = $newServer3 | Export-DbaRegisteredServer -Path C:\temp\dbatoolsci_regserverexport.xml
        $results4 = Import-DbaRegisteredServer -SqlInstance $script:instance2 -Path $results3
        $results4.ServerName | Should -Be $newServer3.ServerName
        $results4.Description | Should -Be $newServer3.Description
    }
}
tools\dbatools\tests\Export-DbaUser.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

$outputFile = "$env:temp\dbatoolsci_user.sql"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        try {
            $dbname = "dbatoolsci_exportdbauser"
            $login = "dbatoolsci_exportdbauser_login"
            $user = "dbatoolsci_exportdbauser_user"
            $server = Connect-DbaInstance -SqlInstance $script:instance1
            $null = $server.Query("CREATE DATABASE [$dbname]")

            $securePassword = $(ConvertTo-SecureString -String "GoodPass1234!" -AsPlainText -Force)
            $null = New-DbaLogin -SqlInstance $script:instance1 -Login $login -Password $securePassword

            $db = Get-DbaDatabase -SqlInstance $script:instance1 -Database $dbname
            $null = $db.Query("CREATE USER [$user] FOR LOGIN [$login]")
        }
        catch { } # No idea why appveyor can't handle this
    }
    AfterAll {
        Remove-DbaDatabase -SqlInstance $script:instance1 -Database $dbname -Confirm:$false
        Remove-DbaLogin -SqlInstance $script:instance1 -Login $login -Confirm:$false
        (Get-ChildItem $outputFile -ErrorAction SilentlyContinue) | Remove-Item -ErrorAction SilentlyContinue
    }

    Context "Check if output file was created" {
        if (Get-DbaDatabaseUser -SqlInstance $script:instance1 -Database $dbname | Where-Object Name -eq $user) {
            $results = Export-DbaUser -SqlInstance $script:instance1 -Database $dbname -User $user -FilePath $outputFile
            It "Exports results to one sql file" {
                (Get-ChildItem $outputFile).Count | Should Be 1
            }
            It "Exported file is bigger than 0" {
                (Get-ChildItem $outputFile).Length | Should BeGreaterThan 0
            }
        }
    }
}
tools\dbatools\tests\Export-DbaXESessionTemplate.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    AfterAll {
        $null = Get-DbaXESession -SqlInstance $script:instance2 -Session db_ola_health | Remove-DbaXESession
        Remove-Item -Path 'C:\windows\temp\Profiler TSQL Duration.xml' -ErrorAction SilentlyContinue
    }
    Context "Test Importing Session Template" {
        $session = Import-DbaXESessionTemplate -SqlInstance $script:instance2 -Template 'Profiler TSQL Duration'
        $results = $session | Export-DbaXESessionTemplate -Path C:\windows\temp
        It "session exports to disk" {
            $results.Name | Should Be 'Profiler TSQL Duration.xml'
        }
    }
}
tools\dbatools\tests\Find-DbaBackup.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        $paramCount = 5
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Find-DbaBackup).Parameters.Keys
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    $testPath = "TestDrive:\sqlbackups"
    if (!(Test-Path $testPath)) {
        New-Item -Path $testPath -ItemType Container
    }
    Context "Path validation" {
        { Find-DbaBackup -Path 'funnypath' -BackupFileExtension 'bak' -RetentionPeriod '0d' -EnableException } | Should Throw "not found"
    }
    Context "RetentionPeriod validation" {
        { Find-DbaBackup -Path $testPath -BackupFileExtension 'bak' -RetentionPeriod 'ad' -EnableException } | Should Throw "format invalid"
        { Find-DbaBackup -Path $testPath -BackupFileExtension 'bak' -RetentionPeriod '11y' -EnableException } | Should Throw "units invalid"
    }
    Context "BackupFileExtension validation" {
        { Find-DbaBackup -Path $testPath -BackupFileExtension '.bak' -RetentionPeriod '0d' -EnableException -WarningAction SilentlyContinue } | Should Not Throw
    }
    Context "BackupFileExtension message validation" {
        $warnmessage = Find-DbaBackup -Path $testPath -BackupFileExtension '.bak' -RetentionPeriod '0d' 3>&1
        $warnmessage | Should BeLike '*period*'
    }
    Context "Files found match the proper retention" {
        for ($i = 1; $i -le 5; $i++) {
            $filepath = Join-Path $testPath "dbatoolsci_$($i)_backup_hours.bak"
            Set-Content $filepath -value "."
            (Get-ChildItem $filepath).LastWriteTime = (Get-Date).AddHours(-10)
        }
        for ($i = 1; $i -le 5; $i++) {
            $filepath = Join-Path $testPath "dbatoolsci_$($i)_backup_days.bak"
            Set-Content $filepath -value "."
            (Get-ChildItem $filepath).LastWriteTime = (Get-Date).AddDays(-5)
        }
        for ($i = 1; $i -le 5; $i++) {
            $filepath = Join-Path $testPath "dbatoolsci_$($i)_backup_weeks.bak"
            Set-Content $filepath -value "."
            (Get-ChildItem $filepath).LastWriteTime = (Get-Date).AddDays(-5 * 7)
        }
        for ($i = 1; $i -le 5; $i++) {
            $filepath = Join-Path $testPath "dbatoolsci_$($i)_backup_months.bak"
            Set-Content $filepath -value "."
            (Get-ChildItem $filepath).LastWriteTime = (Get-Date).AddDays(-5 * 30)
        }
        It "Should find all files with retention 0d" {
            $results = Find-DbaBackup -Path $testPath -BackupFileExtension 'bak' -RetentionPeriod '0d'
            $results.Length | Should Be 20
        }
        It "Should find no files '*hours*' with retention 11h" {
            $results = Find-DbaBackup -Path $testPath -BackupFileExtension 'bak' -RetentionPeriod '11h'
            $results.Length | Should Be 15
            ($results | Where-Object FullName -Like '*hours*').Count | Should Be 0
        }
        It "Should find no files '*days*' with retention 6d" {
            $results = Find-DbaBackup -Path $testPath -BackupFileExtension 'bak' -RetentionPeriod '6d'
            $results.Length | Should Be 10
            ($results | Where-Object FullName -Like '*hours*').Count | Should Be 0
            ($results | Where-Object FullName -Like '*days*').Count | Should Be 0
        }
        It "Should find no files '*weeks*' with retention 6w" {
            $results = Find-DbaBackup -Path $testPath -BackupFileExtension 'bak' -RetentionPeriod '6w'
            $results.Length | Should Be 5
            ($results | Where-Object FullName -Like '*hours*').Count | Should Be 0
            ($results | Where-Object FullName -Like '*days*').Count | Should Be 0
            ($results | Where-Object FullName -Like '*weeks*').Count | Should Be 0
        }
        It "Should find no files '*months*' with retention 6m" {
            $results = Find-DbaBackup -Path $testPath -BackupFileExtension 'bak' -RetentionPeriod '6m'
            $results.Length | Should Be 0
            ($results | Where-Object FullName -Like '*hours*').Count | Should Be 0
            ($results | Where-Object FullName -Like '*days*').Count | Should Be 0
            ($results | Where-Object FullName -Like '*weeks*').Count | Should Be 0
            ($results | Where-Object FullName -Like '*weeks*').Count | Should Be 0
        }
    }
    Context "Files found match the proper archive bit" {
        for ($i = 1; $i -le 5; $i++) {
            $filepath = Join-Path $testPath "dbatoolsci_$($i)_backup_notarchive.bak"
            Set-Content $filepath -value "."
            (Get-ChildItem $filepath).LastWriteTime = (Get-Date).AddDays(-5)
            (Get-ChildItem $filepath).Attributes = "Normal"
        }
        for ($i = 1; $i -le 5; $i++) {
            $filepath = Join-Path $testPath "dbatoolsci_$($i)_backup_archive.bak"
            Set-Content $filepath -value "."
            (Get-ChildItem $filepath).LastWriteTime = (Get-Date).AddDays(-5)
            (Get-ChildItem $filepath).Attributes = "Archive"
        }
        It "Should find all files with retention 0d" {
            $results = Find-DbaBackup -Path $testPath -BackupFileExtension 'bak' -RetentionPeriod '0d'
            $results.Length | Should Be 10
        }
        It "Should find only files with the archive bit not set" {
            $results = Find-DbaBackup -Path $testPath -BackupFileExtension 'bak' -RetentionPeriod '0d' -CheckArchiveBit
            $results.Length | Should Be 5
            ($results | Where-Object FullName -Like '*_notarchive*').Count | Should Be 5
            ($results | Where-Object FullName -Like '*_archive*').Count | Should Be 0
        }
    }
    Context "Files found match the proper extension" {
        for ($i = 1; $i -le 5; $i++) {
            $filepath = Join-Path $testPath "dbatoolsci_$($i)_backup.trn"
            Set-Content $filepath -value "."
            (Get-ChildItem $filepath).LastWriteTime = (Get-Date).AddDays(-5)
        }
        for ($i = 1; $i -le 5; $i++) {
            $filepath = Join-Path $testPath "dbatoolsci_$($i)_backup.bak"
            Set-Content $filepath -value "."
            (Get-ChildItem $filepath).LastWriteTime = (Get-Date).AddDays(-5)
        }
        It "Should find 5 files with extension trn" {
            $results = Find-DbaBackup -Path $testPath -BackupFileExtension 'trn' -RetentionPeriod '0d'
            $results.Length | Should Be 5
        }
        It "Should find 5 files with extension bak" {
            $results = Find-DbaBackup -Path $testPath -BackupFileExtension 'bak' -RetentionPeriod '0d'
            $results.Length | Should Be 5
        }
    }
}
tools\dbatools\tests\Find-DbaDatabase.Tests.ps1
<#
    The below statement stays in for every test you build.
#>
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

<#
    Unit test is required for any command added
#>
Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        <#
            The $paramCount is adjusted based on the parameters your command will have.

            The $defaultParamCount is adjusted based on what type of command you are writing the test for:
                - Commands that *do not* include SupportShouldProcess, set defaultParamCount    = 11
                - Commands that *do* include SupportShouldProcess, set defaultParamCount        = 13
        #>
        $paramCount = 7
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\$CommandName).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'Property', 'Pattern', 'Exact', 'EnableException', 'Detailed'
        It "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}
Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    Context "Command actually works" {
        $results = Find-DbaDatabase -SqlInstance $script:instance2 -Pattern Master
        It "Should return correct properties" {
            $ExpectedProps = 'ComputerName,InstanceName,SqlInstance,Name,SizeMB,Owner,CreateDate,ServiceBrokerGuid,Tables,StoredProcedures,Views,ExtendedProperties,Database'.Split(',')
            ($results[0].PsObject.Properties.Name | Sort-Object) | Should Be ($ExpectedProps | Sort-Object)
        }


        $results = Find-DbaDatabase -SqlInstance $script:instance2 -Pattern Master
        It "Should return true if Database Master is Found" {
            ($results | Where-Object Name -match 'Master' ) | Should Be $true
        }
        It "Should return true if Creation Date of Master is '4/8/2003 9:13:36 AM'" {
            $($results.CreateDate.ToFileTimeutc()[0]) -eq 126942668163900000  | Should Be $true
        }

        $results = Find-DbaDatabase -SqlInstance $script:instance1, $script:instance2 -Pattern Master
        It "Should return true if Executed Against 2 instances: $script:instance1 and $script:instance2" {
            ($results.InstanceName | Select-Object -Unique).count -eq 2 | Should Be $true
        }
        $results = Find-DbaDatabase -SqlInstance $script:instance2 -Property ServiceBrokerGuid -Pattern -0000-0000-000000000000
        It "Should return true if Database Found via Property Filter" {
            $results.ServiceBrokerGuid | Should BeLike '*-0000-0000-000000000000'
        }
    }
}
tools\dbatools\tests\Find-DbaDisabledIndex.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        $paramCount = 7
        $defaultParamCount = 13
        [object[]]$params = (Get-ChildItem function:\Find-DbaDisabledIndex).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'Database', 'ExcludeDatabase', 'NoClobber', 'Append','EnableException'
        It "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    Context "Command actually works" {
        BeforeAll {
            $server = Connect-DbaInstance -SqlInstance $script:instance1
            $random = Get-Random
            $indexName = "dbatoolsci_index_$random"
            $tableName = "dbatoolsci_table_$random"
            $sql = "create table $tableName (col1 int)
                    create index $indexName on $tableName (col1)
                    ALTER INDEX $indexName ON $tableName DISABLE;"
            $null = $server.Query($sql,'tempdb')
        }
        AfterAll {
           $sql = "drop table $tableName;"
           $null = $server.Query($sql,'tempdb')
        }

        It "Should find disabled index: $indexName" {
            $results = Find-DbadisabledIndex -SqlInstance $script:instance1
            $results.IndexName -contains $indexName | Should Be $true
        }
        It "Should find disabled index: $indexName for specific database" {
            $results = Find-DbadisabledIndex -SqlInstance $script:instance1 -Database tempdb
            $results.IndexName -contains $indexName | Should Be $true
        }
        It "Should exclude specific database" {
            $results = Find-DbadisabledIndex -SqlInstance $script:instance1 -ExcludeDatabase tempdb
            $results.DatabaseName -contains 'tempdb' | Should Be $false
        }
    }
}
tools\dbatools\tests\Find-DbaDuplicateIndex.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $server = Connect-DbaInstance -SqlInstance $script:instance1
        $sql = "create database [dbatools_dupeindex]"
        $server.Query($sql)
        $sql = "CREATE TABLE [dbatools_dupeindex].[dbo].[WABehaviorEvent](
                [BehaviorEventId] [smallint] NOT NULL,
                [ClickType] [nvarchar](50) NOT NULL,
                [Description] [nvarchar](512) NOT NULL,
                [BehaviorClassId] [tinyint] NOT NULL,
             CONSTRAINT [PK_WABehaviorEvent_BehaviorEventId] PRIMARY KEY CLUSTERED
            (
                [BehaviorEventId] ASC
            )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY],
            UNIQUE NONCLUSTERED
            (
                [ClickType] ASC
            )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
            ) ON [PRIMARY]


            CREATE UNIQUE NONCLUSTERED INDEX [IX_WABehaviorEvent_ClickType] ON [dbatools_dupeindex].[dbo].[WABehaviorEvent]
            (
                [ClickType] ASC
            )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

            ALTER TABLE [dbatools_dupeindex].[dbo].[WABehaviorEvent] ADD UNIQUE NONCLUSTERED
            (
                [ClickType] ASC
            )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
            "
        $server.Query($sql)
    }
    AfterAll {
        Remove-DbaDatabase -SqlInstance $script:instance1 -Database dbatools_dupeindex -Confirm:$false
    }

    Context "Gets back some results" {
        $results = Find-DbaDuplicateIndex -SqlInstance $script:instance1 -Database dbatools_dupeindex
        It "return at least two results" {
            $results.Count -ge 2 | Should Be $true
        }
    }
}
tools\dbatools\tests\Find-DbaInstance.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    Context "Command finds appveyor instances" {
        $results = Find-DbaInstance -ComputerName $env:COMPUTERNAME
        It "finds more than one SQL instance" {
            $results.count -gt 1
        }
        It "finds the SQL2008R2SP2 instance" {
            $results.InstanceName -contains 'SQL2008R2SP2' | Should -Be $true
        }
        It "finds the SQL2016 instance" {
            $results.InstanceName -contains 'SQL2016' | Should -Be $true
        }
        It "finds the SQL2017 instance" {
            $results.InstanceName -contains 'SQL2017' | Should -Be $true
        }
    }
}
tools\dbatools\tests\Find-DbaOrphanedFile.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    Context "Orphaned files are correctly identified" {
        BeforeAll {
            $dbname = "dbatoolsci_orphanedfile"
            $server = Connect-DbaInstance -SqlInstance $script:instance2
            $null = $server.Query("CREATE DATABASE $dbname")
            $result = Get-DbaDatabase -SqlInstance $script:instance2 -Database $dbname
            if ($result.count -eq 0) {
                it "has failed setup" {
                    Set-TestInconclusive -message "Setup failed"
                }
                throw "has failed setup"
            }
        }
        AfterAll {
            Get-DbaDatabase -SqlInstance $script:instance2 -Database $dbname | Remove-DbaDatabase -Confirm:$false
        }
        $null = Detach-DbaDatabase -SqlInstance $script:instance2 -Database $dbname -Force
        $results = Find-DbaOrphanedFile -SqlInstance $script:instance2

        It "Has the correct default properties" {
            $ExpectedStdProps = 'ComputerName,InstanceName,SqlInstance,Filename,RemoteFilename'.Split(',')
            ($results[0].PSStandardMembers.DefaultDisplayPropertySet.ReferencedPropertyNames | Sort-Object) | Should Be ($ExpectedStdProps | Sort-Object)
        }
        It "Has the correct properties" {
            $ExpectedProps = 'ComputerName,InstanceName,SqlInstance,Filename,RemoteFilename,Server'.Split(',')
            ($results[0].PsObject.Properties.Name | Sort-Object) | Should Be ($ExpectedProps | Sort-Object)
        }

        It "Finds two files" {
            $results.Count | Should Be 2
        }

        $results.FileName | Remove-Item

        $results = Find-DbaOrphanedFile -SqlInstance $script:instance2
        It "Finds zero files after cleaning up" {
            $results.Count | Should Be 0
        }
    }
}

Describe "$CommandName Unit Tests" -Tags 'UnitTests' {
    Context "Validate parameters" {
        <#
            The $paramCount is adjusted based on the parameters your command will have.
            The $defaultParamCount is adjusted based on what type of command you are writing the test for:
                - Commands that *do not* include SupportShouldProcess, set defaultParamCount    = 11
                - Commands that *do* include SupportShouldProcess, set defaultParamCount        = 13
        #>
        $paramCount = 7
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Find-DbaOrphanedFile).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'Path', 'FileType', 'LocalOnly', 'RemoteOnly', 'EnableException'
        It "Should contain our specific parameters" {
            ((Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}
tools\dbatools\tests\Find-DbaSimilarTable.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Testing if similar tables are discovered" {
        BeforeAll {
            $db = Get-DbaDatabase -SqlInstance $script:instance1 -Database tempdb
            $db.Query("CREATE TABLE dbatoolsci_table1 (id int identity, fname varchar(20), lname char(5), lol bigint, whatever datetime)")
            $db.Query("CREATE TABLE dbatoolsci_table2 (id int identity, fname varchar(20), lname char(5), lol bigint, whatever datetime)")
        }
        AfterAll {
            $db.Query("DROP TABLE dbatoolsci_table1")
            $db.Query("DROP TABLE dbatoolsci_table2")
        }

        $results = Find-DbaSimilarTable -SqlInstance $script:instance1 -Database tempdb | Where-Object Table -Match dbatoolsci

        It "returns at least two rows" { # not an exact count because who knows
            $results.Count -ge 2 | Should Be $true
        }

        foreach ($result in $results) {
            It "matches 100% for the test tables" {
                $result.MatchPercent -eq 100 | Should Be $true
            }
        }
    }
}
tools\dbatools\tests\Format-DbaBackupInformation.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "UnitTests" {

    Context "Rename a Database" {
        $History = Get-DbaBackupInformation -Import -Path $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\ContinuePointTest.xml
        $output = $history | Format-DbaBackupInformation -ReplaceDatabaseName 'Pester'
        It "Should have a database name of Pester" {
            ($output | Where-Object {$_.Database -ne 'Pester'}).count | Should be 0
        }
        It "Should have renamed datafiles as well" {
            ($output | Select-Object -ExpandProperty filelist | Where-Object {$_.PhysicalName -like '*ContinuePointTest*'}).count
        }

    }

    Context "Test it works as a parameter as well" {
        $History = Get-DbaBackupInformation -Import -Path $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\ContinuePointTest.xml
        $output = Format-DbaBackupInformation -BackupHistory $History -ReplaceDatabaseName 'Pester'
        It "Should have a database name of Pester" {
            ($output | Where-Object {$_.Database -ne 'Pester'}).count | Should be 0
        }
        It "Should have renamed datafiles as well" {
            ($out | Select-Object -ExpandProperty filelist | Where-Object {$_.PhysicalName -like 'ContinuePointTest'}).count | Should Be 0
        }
    }

    Context "Rename 2 dbs using a hash" {
        $History = Get-DbaBackupInformation -Import -Path $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\ContinuePointTest.xml
        $History += Get-DbaBackupInformation -Import -Path $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\RestoreTimeClean.xml
        $output = Format-DbaBackupInformation -BackupHistory $History -ReplaceDatabaseName @{'ContinuePointTest' = 'Spiggy'; 'RestoreTimeClean' = 'Eldritch'}
        It "Should have no databases other than spiggy and eldritch" {
            ($output | Where-Object {$_.Database -notin ('Spiggy', 'Eldritch')}).count | Should be 0
        }
        It "Should have renamed all RestoreTimeCleans to Eldritch" {
            ($Output | Where-Object {$_.OriginalDatabase -eq 'RestoreTimeClean'} | Where-Object {$_.Database -ne 'Eldritch'}).count | Should be 0
        }
        It "Should have renamed all the RestoreTimeClean files to Eldritch" {
            ($out | Where-Object {$_.OriginalDatabase -eq 'RestoreTimeClean'} | Select-Object -ExpandProperty filelist | Where-Object {$_.PhysicalName -like 'RestoreTimeClean'}).count | Should Be 0
            ($out | Where-Object {$_.OriginalDatabase -eq 'RestoreTimeClean'} | Select-Object -ExpandProperty filelist | Where-Object {$_.PhysicalName -like 'eldritch'}).count | Should Be ($out | Where-Object {$_.OriginalDatabase -eq 'ContinuePointTest'} | Select-Object -ExpandProperty filelist).count

        }
        It "Should have renamed all ContinuePointTest to Spiggy" {
            ($Output | Where-Object {$_.OriginalDatabase -eq 'ContinuePointTest'} | Where-Object {$_.Database -ne 'Spiggy'}).count | Should be 0
        }
        It "Should have renamed all the ContinuePointTest files to Spiggy" {
            ($out | Where-Object {$_.OriginalDatabase -eq 'ContinuePointTest'} | Select-Object -ExpandProperty filelist | Where-Object {$_.PhysicalName -like 'ContinuePointTest'}).count | Should Be 0
            ($out | Where-Object {$_.OriginalDatabase -eq 'ContinuePointTest'} | Select-Object -ExpandProperty filelist | Where-Object {$_.PhysicalName -like 'spiggy'}).count | Should Be ($out | Where-Object {$_.OriginalDatabase -eq 'ContinuePointTest'} | Select-Object -ExpandProperty filelist).count

        }
    }

    Context "Rename 1 dbs using a hash" {
        $History = Get-DbaBackupInformation -Import -Path $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\ContinuePointTest.xml
        $History += Get-DbaBackupInformation -Import -Path $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\RestoreTimeClean.xml
        $output = Format-DbaBackupInformation -BackupHistory $History -ReplaceDatabaseName @{'ContinuePointTest' = 'Alice'}
        It "Should have no databases other than spiggy and eldritch" {
            ($output | Where-Object {$_.Database -notin ('RestoreTimeClean', 'Alice')}).count | Should be 0
        }
        It "Should have left RestoreTimeClean alone" {
            ($Output | Where-Object {$_.OriginalDatabase -eq 'RestoreTimeClean'} | Where-Object {$_.Database -ne 'RestoreTimeClean'}).count | Should be 0
        }
        It "Should have renamed all ContinuePointTest to Alice" {
            ($Output | Where-Object {$_.OriginalDatabase -eq 'ContinuePointTest'} | Where-Object {$_.Database -ne 'Alice'}).count | Should be 0
        }
        It "Should have renamed all the ContinuePointTest files to Alice" {
            ($Output | Where-Object {$_.OriginalDatabase -eq 'ContinuePointTest'} | Select-Object -ExpandProperty filelist | Where-Object {$_.PhysicalName -like 'ContinuePointTest'}).count | Should Be 0
            ($Output | Where-Object {$_.OriginalDatabase -eq 'ContinuePointTest'} | Select-Object -ExpandProperty filelist | Where-Object {$_.PhysicalName -like 'alice'}).count | Should Be ($out | Where-Object {$_.OriginalDatabase -eq 'ContinuePointTest'} | Select-Object -ExpandProperty filelist).count
        }
    }

    Context "Check DB Name prefix and suffix" {
        $History = Get-DbaBackupInformation -Import -Path $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\ContinuePointTest.xml
        $output = $history | Format-DbaBackupInformation -DatabaseNamePrefix PREFIX
        It "Should have prefixed all db names" {
            ($Output | Where-Object {$_.Database -like 'PREFIX*'}).count | Should be $output.count
        }

    }

    Context "Check DataFileDirectory moves all files" {
        $History = Get-DbaBackupInformation -Import -Path $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\ContinuePointTest.xml
        $History += Get-DbaBackupInformation -Import -Path $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\RestoreTimeClean.xml
        $output = Format-DbaBackupInformation -BackupHistory $History -DataFileDirectory c:\restores

        It "Should have move ALL files to c:\restores\" {
            (($Output | Select-Object -ExpandProperty Filelist).PhysicalName | split-path | Where-Object {$_ -ne 'c:\restores'}).count | Should Be 0
        }
    }

    Context "Check DataFileDirectory and LogFileDirectory work independently" {
        $History = Get-DbaBackupInformation -Import -Path $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\ContinuePointTest.xml
        $History += Get-DbaBackupInformation -Import -Path $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\RestoreTimeClean.xml
        $output = Format-DbaBackupInformation -BackupHistory $History -DataFileDirectory c:\restores\ -LogFileDirectory c:\logs

        It "Should  have moved all data files to c:\restores\" {
            (($Output | Select-Object -ExpandProperty Filelist | Where-Object {$_.Type -eq 'D'}).PhysicalName | split-path | Where-Object {$_ -ne 'c:\restores'}).count | Should Be 0
        }
        It "Should have moved all log files to c:\logs\" {
            (($Output | Select-Object -ExpandProperty Filelist | Where-Object {$_.Type -eq 'L'}).PhysicalName | split-path | Where-Object {$_ -ne 'c:\logs'}).count | Should Be 0
        }
    }

    Context "Check LogFileDirectory works for just logfiles" {
        $History = Get-DbaBackupInformation -Import -Path $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\ContinuePointTest.xml
        $History += Get-DbaBackupInformation -Import -Path $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\RestoreTimeClean.xml
        $Output = Format-DbaBackupInformation -BackupHistory $History -DataFileDirectory c:\restores\ -LogFileDirectory c:\logs

        It "Should not have moved all data files to c:\restores\" {
            (($Output | Select-Object -ExpandProperty Filelist | Where-Object {$_.Type -eq 'D'}).PhysicalName | split-path | Where-Object {$_ -eq 'c:\logs'}).count | Should Be 0
        }
        It "Should have moved all log files to c:\logs\" {
            (($Output | Select-Object -ExpandProperty Filelist | Where-Object {$_.Type -eq 'L'}).PhysicalName | split-path | Where-Object {$_ -ne 'c:\logs'}).count | Should Be 0
        }
    }

    Context "Test RebaseBackupFolder" {
        $History = Get-DbaBackupInformation -Import -Path $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\ContinuePointTest.xml
        $History += Get-DbaBackupInformation -Import -Path $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\RestoreTimeClean.xml
        $Output = Format-DbaBackupInformation -BackupHistory $History -RebaseBackupFolder c:\backups\

        It "Should not have moved all backup files to c:\backups" {
            ($Output | Select-Object -ExpandProperty FullName | split-path | Where-Object {$_ -eq 'c:\backups'}).count | Should Be 0
        }

    }

    Context "Test everything all at once" {
        $History = Get-DbaBackupInformation -Import -Path $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\ContinuePointTest.xml
        $output = $history | Format-DbaBackupInformation -ReplaceDatabaseName 'Pester' -DataFileDirectory c:\restores -LogFileDirectory c:\logs\ -RebaseBackupFolder c:\backups\
        It "Should have a database name of Pester" {
            ($output | Where-Object {$_.Database -ne 'Pester'}).count | Should be 0
        }
        It "Should have renamed datafiles as well" {
            ($output | Select-Object -ExpandProperty filelist | Where-Object {$_.PhysicalName -like '*ContinuePointTest*'}).count
        }
        It "Should  have moved all data files to c:\restores\" {
            (($Output | Select-Object -ExpandProperty Filelist | Where-Object {$_.Type -eq 'D'}).PhysicalName | split-path | Where-Object {$_ -ne 'c:\restores'}).count | Should Be 0
        }
        It "Should have moved all log files to c:\logs\" {
            (($Output | Select-Object -ExpandProperty Filelist | Where-Object {$_.Type -eq 'L'}).PhysicalName | split-path | Where-Object {$_ -ne 'c:\logs'}).count | Should Be 0
        }
        It "Should not have moved all backup files to c:\backups" {
            ($Output | Select-Object -ExpandProperty FullName | split-path | Where-Object {$_ -eq 'c:\backups'}).count | Should Be 0
        }

    }
}
tools\dbatools\tests\Get-DbaAgDatabase.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        $paramCount = 5
        <#
            Get commands, Default count = 11
            Commands with SupportShouldProcess = 13
        #>
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Get-DbaAgDatabase).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'AvailabilityGroup', 'Database', 'EnableException'
        it "Should contain our specific parameters" {
            ((Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count) | Should Be $paramCount
        }
        it "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}

InModuleScope dbatools {
    . "$PSScriptRoot\constants.ps1"
    $CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
    Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
        Mock Connect-SqlInstance {
            Import-Clixml $script:appveyorlabrepo\agserver.xml
        }
        Context "gets ag databases" {
            $results = Get-DbaAgDatabase -SqlInstance sql2016c
            foreach ($result in $results) {
                It "returns results with proper data" {
                    $result.Replica | Should -Be 'SQL2016C'
                    $result.SynchronizationState | Should -Be 'NotSynchronizing'
                }
            }
            $results = Get-DbaAgDatabase -SqlInstance sql2016c -Database WSS_Content
            It "returns results with proper data for one database" {
                $results.Replica | Should -Be 'SQL2016C'
                $results.SynchronizationState | Should -Be 'NotSynchronizing'
                $results.DatabaseName | Should -Be 'WSS_Content'
            }
        }
    }
}
tools\dbatools\tests\Get-DbaAgentAlert.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"
Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        $server = Connect-DbaInstance -SqlInstance $script:instance2 -Database master
        $server.Query("EXEC msdb.dbo.sp_add_alert @name=N'dbatoolsci test alert',@message_id=0,@severity=6,@enabled=1,@delay_between_responses=0,@include_event_description_in=0,@category_name=N'[Uncategorized]',@job_id=N'00000000-0000-0000-0000-000000000000'")
    }
    AfterAll {
        $server = Connect-DbaInstance -SqlInstance $script:instance2 -Database master
        $server.Query("EXEC msdb.dbo.sp_delete_alert @name=N'dbatoolsci test alert'")
    }
    
    $results = Get-DbaAgentAlert -SqlInstance $script:instance2
    It "gets the newly created alert" {
        $results.Name -contains 'dbatoolsci test alert'
    }
}
tools\dbatools\tests\Get-DbaAgentJob.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        $paramCount = 6
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Get-DbaAgentJob).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'Job', 'ExcludeJob', 'NoDisabledJobs', 'EnableException'
        It "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Command gets jobs" {
        BeforeAll {
            $null = New-DbaAgentJob -SqlInstance $script:instance2 -Job dbatoolsci_testjob
            $null = New-DbaAgentJob -SqlInstance $script:instance2 -Job dbatoolsci_testjob_disabled -Disabled
        }
        AfterAll {
            $null = Remove-DbaAgentJob -SqlInstance $script:instance2 -Job dbatoolsci_testjob, dbatoolsci_testjob_disabled
        }
        $results = Get-DbaAgentJob -SqlInstance $script:instance2 | Where-Object {$_.Name -match "dbatoolsci"}
        It "Should get 2 dbatoolsci jobs" {
            $results.count | Should Be 2
        }
        $results = Get-DbaAgentJob -SqlInstance $script:instance2 -Job dbatoolsci_testjob
        It "Should get a specific job" {
            $results.name | Should Be "dbatoolsci_testjob"
        }
        
    }
    Context "Command gets no disabled jobs" {
        BeforeAll {
            $null = New-DbaAgentJob -SqlInstance $script:instance2 -Job dbatoolsci_testjob
            $null = New-DbaAgentJob -SqlInstance $script:instance2 -Job dbatoolsci_testjob_disabled -Disabled
        }
        AfterAll {
            $null = Remove-DbaAgentJob -SqlInstance $script:instance2 -Job dbatoolsci_testjob, dbatoolsci_testjob_disabled
        }
        $results = Get-DbaAgentJob -SqlInstance $script:instance2 -NoDisabledJobs | Where-Object {$_.Name -match "dbatoolsci"}
        It "Should return only enabled jobs" {
            $results.enabled -contains $False | Should Be $False
        }
    }
    Context "Command doesn't get excluded job" {
        BeforeAll {
            $null = New-DbaAgentJob -SqlInstance $script:instance2 -Job dbatoolsci_testjob
            $null = New-DbaAgentJob -SqlInstance $script:instance2 -Job dbatoolsci_testjob_disabled -Disabled
        }
        AfterAll {
            $null = Remove-DbaAgentJob -SqlInstance $script:instance2 -Job dbatoolsci_testjob, dbatoolsci_testjob_disabled
        }
        $results = Get-DbaAgentJob -SqlInstance $script:instance2 -ExcludeJob dbatoolsci_testjob  | Where-Object {$_.Name -match "dbatoolsci"}
        It "Should not return excluded job" {
            $results.name -contains "dbatoolsci_testjob" | Should Be $False
        }
    }
}
tools\dbatools\tests\Get-DbaAgentJobCategory.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        $paramCount = 6
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Get-DbaAgentJobCategory).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'Category', 'CategoryType', 'Force', 'EnableException'
        It "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Command gets job categories" {
        BeforeAll {
            $null = New-DbaAgentJobCategory -SqlInstance $script:instance2 -Category dbatoolsci_testcategory, dbatoolsci_testcategory2
        }
        AfterAll {
            $null = Remove-DbaAgentJobCategory -SqlInstance $script:instance2 -Category dbatoolsci_testcategory, dbatoolsci_testcategory2
        }
        $results = Get-DbaAgentJobCategory -SqlInstance $script:instance2 | Where-Object {$_.Name -match "dbatoolsci"}
        It "Should get at least 2 categories" {
            $results.count | Should BeGreaterThan 1
        }
        $results = Get-DbaAgentJobCategory -SqlInstance $script:instance2 -Category dbatoolsci_testcategory | Where-Object {$_.Name -match "dbatoolsci"}
        It "Should get the dbatoolsci_testcategory category" {
            $results.count | Should Be 1
        }
        $results = Get-DbaAgentJobCategory -SqlInstance $script:instance2 -CategoryType LocalJob | Where-Object {$_.Name -match "dbatoolsci"}
        It "Should get at least 1 LocalJob" {
            $results.count | Should BeGreaterThan 1
        }
    }
}
tools\dbatools\tests\Get-DbaAgentJobHistory.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        $paramCount = 10
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Get-DbaAgentJobHistory).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'Job', 'ExcludeJob', 'StartDate', 'EndDate', 'NoJobSteps', 'WithOutputFile', 'JobCollection', 'EnableException'
        It "Contains our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        It "Contains $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}


Describe "$CommandName Unittests" -Tag 'UnitTests' {
    InModuleScope 'dbatools' {
        Mock Connect-SQLInstance -MockWith {
            # Thanks @Fred
            $obj = [PSCustomObject]@{
                Name                 = 'BASEName'
                ComputerName              = 'BASEComputerName'
                InstanceName         = 'BASEInstanceName'
                DomainInstanceName   = 'BASEDomainInstanceName'
                InstallDataDirectory = 'BASEInstallDataDirectory'
                ErrorLogPath         = 'BASEErrorLog_{0}_{1}_{2}_Path' -f "'", '"', ']'
                ServiceName          = 'BASEServiceName'
                JobServer            = New-Object PSObject
                ConnectionContext    = New-Object PSObject
            }
            Add-Member -InputObject $obj.ConnectionContext -Name ConnectionString  -MemberType NoteProperty -Value 'put=an=equal=in=it'
            Add-Member -InputObject $obj.JobServer -Name EnumJobHistory -MemberType ScriptMethod -Value {
                param ($filter)
                return @(
                    @{
                        JobName     = 'Job1'
                        JobID       = [guid]'E7718A84-8B43-46D0-8F8D-4FC4464F9FC5'
                        StepID      = 0
                        StepName    = '(Job outcome)'
                        RunDate     = [DateTime]::Parse('2017-09-26T13:00:00')
                        RunDuration = 112
                        RunStatus   = 0
                    },
                    @{
                        JobName     = 'Job1'
                        JobID       = [guid]'E7718A84-8B43-46D0-8F8D-4FC4464F9FC5'
                        StepID      = 1
                        StepName    = 'Job1Step1'
                        RunDate     = [DateTime]::Parse('2017-09-26T13:00:00')
                        RunDuration = 1
                        RunStatus   = 0
                    },
                    @{
                        JobName     = 'Job1'
                        JobID       = [guid]'E7718A84-8B43-46D0-8F8D-4FC4464F9FC5'
                        StepID      = 2
                        StepName    = 'Job1Step2'
                        RunDate     = [DateTime]::Parse('2017-09-26T13:00:01')
                        RunDuration = 1
                        RunStatus   = 0
                    },
                    @{
                        JobName     = 'Job2'
                        JobID       = [guid]'9C9A6819-58CE-451A-8DD7-6D17593F0DFA'
                        StepID      = 0
                        StepName    = '(Job outcome)'
                        RunDate     = [DateTime]::Parse('2017-09-26T01:00:00')
                        RunDuration = 2
                        RunStatus   = 0
                    },
                    @{
                        JobName     = 'Job2'
                        JobID       = [guid]'9C9A6819-58CE-451A-8DD7-6D17593F0DFA'
                        StepID      = 1
                        StepName    = 'Job2Step1'
                        RunDate     = [DateTime]::Parse('2017-09-26T01:00:00')
                        RunDuration = 1
                        RunStatus   = 0
                    },
                    @{
                        JobName     = 'Job2'
                        JobID       = [guid]'9C9A6819-58CE-451A-8DD7-6D17593F0DFA'
                        StepID      = 2
                        StepName    = 'Job2Step2'
                        RunDate     = [DateTime]::Parse('2017-09-26T01:00:01')
                        RunDuration = 1
                        RunStatus   = 0
                    }
                )
            }
            $obj.PSObject.TypeNames.Clear()
            $obj.PSObject.TypeNames.Add("Microsoft.SqlServer.Management.Smo.Server")
            return $obj
        } #mock connect-sqlserver
        Context "Return values" {

            Mock Get-DbaAgentJobOutputFile -MockWith {
                @(
                    @{
                        Job            = 'Job1'
                        StepId         = 1
                        OutputFileName = 'Job1Output1'
                    },
                    @{
                        Job            = 'Job1'
                        StepId         = 2
                        OutputFileName = 'Job1Output2'
                    },
                    @{
                        Job            = 'Job2'
                        StepId         = 2
                        OutputFileName = 'Job2Output1'
                    }
                )
            }
            It "Throws when NoJobSteps and WithOutputFile" {
                { Get-DbaAgentJobHistory -SqlInstance 'SQLServerName' -NoJobSteps -WithOutputFile -EnableException } | Should Throw
            }
            It "Returns full history by default" {
                $Results = @()
                $Results += Get-DbaAgentJobHistory -SqlInstance 'SQLServerName'
                $Results.Length | Should Be 6
            }
            It "Returns only runs with no steps with NoJobSteps" {
                $Results = @()
                $Results += Get-DbaAgentJobHistory -SqlInstance 'SQLServerName' -NoJobSteps
                $Results.Length | Should Be 2
            }
            It 'Returns our own "augmented" properties, too' {
                $Results = @()
                $Results += Get-DbaAgentJobHistory -SqlInstance 'SQLServerName' -NoJobSteps
                $Results[0].psobject.properties.Name | Should -Contain 'StartDate'
                $Results[0].psobject.properties.Name | Should -Contain 'EndDate'
                $Results[0].psobject.properties.Name | Should -Contain 'Duration'
            }
            It 'Returns "augmented" properties that are correct' {
                $Results = @()
                $Results += Get-DbaAgentJobHistory -SqlInstance 'SQLServerName' -NoJobSteps
                $Results[0].StartDate | Should -Be $Results[0].RunDate
                $Results[0].RunDuration | Should -Be 112
                $Results[0].Duration.TotalSeconds | Should -Be 72
                $Results[0].EndDate | Should -Be ($Results[0].StartDate.AddSeconds($Results[0].Duration.TotalSeconds))
            }
            It "Figures out plain outputfiles" {
                $Results = @()
                $Results += Get-DbaAgentJobHistory -SqlInstance 'SQLServerName' -WithOutputFile
                # no output for outcomes
                ($Results | Where-Object StepID -eq 0).Length | Should Be 2
                ($Results | Where-Object StepID -eq 0).OutputFileName -Join '' | Should Be ''
                # correct output for job1
                ($Results | Where-Object StepID -ne 0 | Where-Object JobName -eq 'Job1').OutputFileName | Should Match 'Job1Output[12]'
                # correct output for job2
                ($Results | Where-Object StepID -eq 2 | Where-Object JobName -eq 'Job2').OutputFileName | Should Match 'Job2Output1'
                ($Results | Where-Object StepID -eq 1 | Where-Object JobName -eq 'Job2').OutputFileName | Should Be ''
            }
        }
        Context "SQL Agent Tokens" {
            It "Handles INST" {
                Mock Get-DbaAgentJobOutputFile -MockWith {
                    @(
                        @{
                            Job            = 'Job1'
                            StepId         = 1
                            OutputFileName = '$(INST)__Job1Output1'
                        }
                    )
                }
                $Results = @()
                $Results += Get-DbaAgentJobHistory -SqlInstance 'SQLServerName' -WithOutputFile

                ($Results | Where-Object StepID -eq 1 | Where-Object JobName -eq 'Job1').OutputFileName | Should Be 'BASEServiceName__Job1Output1'

            }
            It "Handles MACH" {
                Mock Get-DbaAgentJobOutputFile -MockWith {
                    @(
                        @{
                            Job            = 'Job1'
                            StepId         = 1
                            OutputFileName = '$(MACH)__Job1Output1'
                        }
                    )
                }
                $Results = @()
                $Results += Get-DbaAgentJobHistory -SqlInstance 'SQLServerName' -WithOutputFile

                ($Results | Where-Object StepID -eq 1 | Where-Object JobName -eq 'Job1').OutputFileName | Should Be 'BASEComputerName__Job1Output1'

            }
            It "Handles SQLDIR" {
                Mock Get-DbaAgentJobOutputFile -MockWith {
                    @(
                        @{
                            Job            = 'Job1'
                            StepId         = 1
                            OutputFileName = '$(SQLDIR)__Job1Output1'
                        }
                    )
                }
                $Results = @()
                $Results += Get-DbaAgentJobHistory -SqlInstance 'SQLServerName' -WithOutputFile

                ($Results | Where-Object StepID -eq 1 | Where-Object JobName -eq 'Job1').OutputFileName | Should Be 'BASEInstallDataDirectory__Job1Output1'

            }
            It "Handles SQLLOGDIR" {
                Mock Get-DbaAgentJobOutputFile -MockWith {
                    @(
                        @{
                            Job            = 'Job1'
                            StepId         = 1
                            OutputFileName = '$(SQLLOGDIR)__Job1Output1'
                        }
                    )
                }
                $Results = @()
                $Results += Get-DbaAgentJobHistory -SqlInstance 'SQLServerName' -WithOutputFile

                ($Results | Where-Object StepID -eq 1 | Where-Object JobName -eq 'Job1').OutputFileName | Should Be 'BASEErrorLog_''_"_]_Path__Job1Output1'

            }
            It "Handles SRVR" {
                Mock Get-DbaAgentJobOutputFile -MockWith {
                    @(
                        @{
                            Job            = 'Job1'
                            StepId         = 1
                            OutputFileName = '$(SRVR)__Job1Output1'
                        }
                    )
                }
                $Results = @()
                $Results += Get-DbaAgentJobHistory -SqlInstance 'SQLServerName' -WithOutputFile

                ($Results | Where-Object StepID -eq 1 | Where-Object JobName -eq 'Job1').OutputFileName | Should Be 'BASEDomainInstanceName__Job1Output1'

            }

            It "Handles STEPID" {
                Mock Get-DbaAgentJobOutputFile -MockWith {
                    @(
                        @{
                            Job            = 'Job1'
                            StepId         = 1
                            OutputFileName = '$(STEPID)__Job1Output1'
                        }
                    )
                }
                $Results = @()
                $Results += Get-DbaAgentJobHistory -SqlInstance 'SQLServerName' -WithOutputFile
                ($Results | Where-Object StepID -eq 1 | Where-Object JobName -eq 'Job1').OutputFileName | Should Be '1__Job1Output1'

            }
            It "Handles JOBID" {
                Mock Get-DbaAgentJobOutputFile -MockWith {
                    @(
                        @{
                            Job            = 'Job1'
                            StepId         = 1
                            OutputFileName = '$(JOBID)__Job1Output1'
                        }
                    )
                }
                $Results = @()
                $Results += Get-DbaAgentJobHistory -SqlInstance 'SQLServerName' -WithOutputFile

                ($Results | Where-Object StepID -eq 1 | Where-Object JobName -eq 'Job1').OutputFileName | Should Be '0x848A71E7438BD0468F8D4FC4464F9FC5__Job1Output1'

            }


            It "Handles STRTDT" {
                Mock Get-DbaAgentJobOutputFile -MockWith {
                    @(
                        @{
                            Job            = 'Job1'
                            StepId         = 1
                            OutputFileName = '$(STRTDT)__Job1Output1'
                        }
                    )
                }
                $Results = @()
                $Results += Get-DbaAgentJobHistory -SqlInstance 'SQLServerName' -WithOutputFile
                ($Results | Where-Object StepID -eq 1 | Where-Object JobName -eq 'Job1').OutputFileName | Should Be '20170926__Job1Output1'
            }
            It "Handles STRTTM" {
                Mock Get-DbaAgentJobOutputFile -MockWith {
                    @(
                        @{
                            Job            = 'Job1'
                            StepId         = 1
                            OutputFileName = '$(STRTTM)__Job1Output1'
                        }
                    )
                }
                $Results = @()
                $Results += Get-DbaAgentJobHistory -SqlInstance 'SQLServerName' -WithOutputFile
                ($Results | Where-Object StepID -eq 1 | Where-Object JobName -eq 'Job1').OutputFileName | Should Be '130000__Job1Output1'
            }
            It "Handles DATE" {
                Mock Get-DbaAgentJobOutputFile -MockWith {
                    @(
                        @{
                            Job            = 'Job1'
                            StepId         = 1
                            OutputFileName = '$(DATE)__Job1Output1'
                        }
                    )
                }
                $Results = @()
                $Results += Get-DbaAgentJobHistory -SqlInstance 'SQLServerName' -WithOutputFile
                ($Results | Where-Object StepID -eq 1 | Where-Object JobName -eq 'Job1').OutputFileName | Should Be '20170926__Job1Output1'
            }

            It "Handles TIME" {
                Mock Get-DbaAgentJobOutputFile -MockWith {
                    @(
                        @{
                            Job            = 'Job1'
                            StepId         = 2
                            OutputFileName = '$(TIME)__Job1Output1'
                        }
                    )
                }
                $Results = @()
                $Results += Get-DbaAgentJobHistory -SqlInstance 'SQLServerName' -WithOutputFile
                ($Results | Where-Object StepID -eq 2 | Where-Object JobName -eq 'Job1').OutputFileName | Should Be '130001__Job1Output1'

            }
        }
        Context "SQL Agent escape sequences" {
            It "Handles ESCAPE_NONE" {
                Mock Get-DbaAgentJobOutputFile -MockWith {
                    @(
                        @{
                            Job            = 'Job1'
                            StepId         = 1
                            OutputFileName = '$(ESCAPE_NONE(SQLLOGDIR))__Job1Output1'
                        }
                    )
                }
                $Results = @()
                $Results += Get-DbaAgentJobHistory -SqlInstance 'SQLServerName' -WithOutputFile

                ($Results | Where-Object StepID -eq 1 | Where-Object JobName -eq 'Job1').OutputFileName | Should Be 'BASEErrorLog_''_"_]_Path__Job1Output1'

            }
            It "Handles ESCAPE_SQUOTE" {
                Mock Get-DbaAgentJobOutputFile -MockWith {
                    @(
                        @{
                            Job            = 'Job1'
                            StepId         = 1
                            OutputFileName = '$(ESCAPE_SQUOTE(SQLLOGDIR))__Job1Output1'
                        }
                    )
                }
                $Results = @()
                $Results += Get-DbaAgentJobHistory -SqlInstance 'SQLServerName' -WithOutputFile

                ($Results | Where-Object StepID -eq 1 | Where-Object JobName -eq 'Job1').OutputFileName | Should Be 'BASEErrorLog_''''_"_]_Path__Job1Output1'

            }
            It "Handles ESCAPE_DQUOTE" {
                Mock Get-DbaAgentJobOutputFile -MockWith {
                    @(
                        @{
                            Job            = 'Job1'
                            StepId         = 1
                            OutputFileName = '$(ESCAPE_DQUOTE(SQLLOGDIR))__Job1Output1'
                        }
                    )
                }
                $Results = @()
                $Results += Get-DbaAgentJobHistory -SqlInstance 'SQLServerName' -WithOutputFile

                ($Results | Where-Object StepID -eq 1 | Where-Object JobName -eq 'Job1').OutputFileName | Should Be 'BASEErrorLog_''_""_]_Path__Job1Output1'

            }
            It "Handles ESCAPE_RBRACKET" {
                Mock Get-DbaAgentJobOutputFile -MockWith {
                    @(
                        @{
                            Job            = 'Job1'
                            StepId         = 1
                            OutputFileName = '$(ESCAPE_RBRACKET(SQLLOGDIR))__Job1Output1'
                        }
                    )
                }
                $Results = @()
                $Results += Get-DbaAgentJobHistory -SqlInstance 'SQLServerName' -WithOutputFile

                ($Results | Where-Object StepID -eq 1 | Where-Object JobName -eq 'Job1').OutputFileName | Should Be 'BASEErrorLog_''_"_]]_Path__Job1Output1'

            }
        }
    }
}
tools\dbatools\tests\Get-DbaAgentJobOutputFile.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        $paramCount = 5
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Get-DbaAgentJobOutputFile).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'Job', 'ExcludeJob', 'EnableException'
        It "Contains our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        It "Contains $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}

Describe "$CommandName Unittests" -Tag 'UnitTests' {
    InModuleScope 'dbatools' {
        Context "Return values" {
            Mock Connect-SQLInstance -MockWith {
                [object]@{
                    Name      = 'SQLServerName'
                    ComputerName   = 'SQLServerName'
                    JobServer = @{
                        Jobs = @(
                            @{
                                Name     = 'Job1'
                                JobSteps = @(
                                    @{
                                        Id             = 1
                                        Name           = 'Job1Step1'
                                        OutputFileName = 'Job1Output1'
                                    },
                                    @{
                                        Id             = 2
                                        Name           = 'Job1Step2'
                                        OutputFileName = 'Job1Output2'
                                    }
                                )
                            },
                            @{
                                Name     = 'Job2'
                                JobSteps = @(
                                    @{
                                        Id             = 1
                                        Name           = 'Job2Step1'
                                        OutputFileName = 'Job2Output1'
                                    },
                                    @{
                                        Id   = 2
                                        Name = 'Job2Step2'
                                    }
                                )
                            },
                            @{
                                Name     = 'Job3'
                                JobSteps = @(
                                    @{
                                        Id   = 1
                                        Name = 'Job3Step1'
                                    },
                                    @{
                                        Id   = 2
                                        Name = 'Job3Step2'
                                    }
                                )
                            }
                        )
                    }
                } #object
            } #mock connect-sqlserver
            It "Gets only steps with output files" {
                $Results = @()
                $Results += Get-DbaAgentJobOutputFile -SqlInstance 'SQLServerName'
                $Results.Length | Should Be 3
                $Results.Job | Should Match 'Job[12]'
                $Results.JobStep | Should Match 'Job[12]Step[12]'
                $Results.OutputFileName | Should Match 'Job[12]Output[12]'
                $Results.RemoteOutputFileName | Should Match '\\\\SQLServerName\\Job[12]Output[12]'
            }
            It "Honors the Job parameter" {
                $Results = @()
                $Results += Get-DbaAgentJobOutputFile -SqlInstance 'SQLServerName' -Job 'Job1'
                $Results.Job | Should Match 'Job1'
                $Results.JobStep | Should Match 'Job1Step[12]'
                $Results.OutputFileName | Should Match 'Job1Output[12]'
            }
            It "Honors the ExcludeJob parameter" {
                $Results = @()
                $Results += Get-DbaAgentJobOutputFile -SqlInstance 'SQLServerName' -ExcludeJob 'Job1'
                $Results.Length | Should Be 1
                $Results.Job | Should Match 'Job2'
                $Results.OutputFileName | Should Be 'Job2Output1'
                $Results.StepId | Should Be 1
            }
            It "Does not return even with a specific job without outputfiles" {
                $Results = @()
                $Results += Get-DbaAgentJobOutputFile -SqlInstance 'SQLServerName' -Job 'Job3'
                $Results.Length | Should Be 0
            }
        }
    }
}

tools\dbatools\tests\Get-DbaAgentJobStep.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        $paramCount = 5
        [object[]]$params = (Get-ChildItem function:\Get-DbaAgentJobStep).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'Job', 'ExcludeJob', 'EnableException'
        It "Contains our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
    }
}

Describe "$CommandName Unittests" -Tag 'UnitTests' {
    InModuleScope 'dbatools' {
        Context "Return values" {
            Mock Connect-SQLInstance -MockWith {
                [object]@{
                    Name      = 'SQLServerName'
                    NetName   = 'SQLServerName'
                    JobServer = @{
                        Jobs = @(
                            @{
                                Name     = 'Job1'
                                JobSteps = @(
                                    @{
                                        Id   = 1
                                        Name = 'Job1Step1'
                                    },
                                    @{
                                        Id   = 2
                                        Name = 'Job1Step2'
                                    }
                                )
                            },
                            @{
                                Name     = 'Job2'
                                JobSteps = @(
                                    @{
                                        Id   = 1
                                        Name = 'Job2Step1'
                                    },
                                    @{
                                        Id   = 2
                                        Name = 'Job2Step2'
                                    }
                                )
                            },
                            @{
                                Name     = 'Job3'
                                JobSteps = @(
                                    @{
                                        Id   = 1
                                        Name = 'Job3Step1'
                                    },
                                    @{
                                        Id   = 2
                                        Name = 'Job3Step2'
                                    }
                                )
                            }
                        )
                    }
                } #object
            } #mock connect-sqlserver

            It "Honors the Job parameter" {
                $Results = @()
                $Results += Get-DbaAgentJobStep -SqlInstance 'SQLServerName' -Job 'Job1'
                $Results.Length | Should Be 2
                $Results.Name | Should Match 'Job1'
                $Results.Name | Should Match 'Job1Step[12]'
            }
            It "Honors the ExcludeJob parameter" {
                $Results = @()
                $Results += Get-DbaAgentJobStep -SqlInstance 'SQLServerName' -ExcludeJob 'Job1'
                $Results.Length | Should Be 4
                $Results.Name | Should Match 'Job[23]Step[12]'
            }
        }
    }
}

tools\dbatools\tests\Get-DbaAgentLog.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        $paramCount = 4
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Get-DbaAgentLog).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'LogNumber', 'EnableException'
        It "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Command gets agent log" {
        $results = Get-DbaAgentLog -SqlInstance $script:instance2
        It "Results are not empty" {
            $results | Should Not Be $Null
        }
        It "Results contain SQLServerAgent version" {
            $results.text -like '`[100`] Microsoft SQLServerAgent version*' | Should Be $true
        }
        It "LogDate is a DateTime type" {
            $($results | Select-Object -first 1).LogDate | Should BeOfType DateTime
        }
    }
    Context "Command gets current agent log using LogNumber parameter" {
        $results = Get-DbaAgentLog -SqlInstance $script:instance2 -LogNumber 0
        It "Results are not empty" {
            $results | Should Not Be $Null
        }
    }
}
tools\dbatools\tests\Get-DbaAgentOperator.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $server = Connect-DbaInstance -SqlInstance $script:instance2
        $sql = "EXEC msdb.dbo.sp_add_operator @name=N'dbatoolsci_operator', @enabled=1, @pager_days=0"
        $server.Query($sql)
        $sql = "EXEC msdb.dbo.sp_add_operator @name=N'dbatoolsci_operator2', @enabled=1, @pager_days=0"
        $server.Query($sql)
    }
    AfterAll {
        $sql = "EXEC msdb.dbo.sp_delete_operator @name=N'dbatoolsci_operator'"
        $server.Query($sql)
        $sql = "EXEC msdb.dbo.sp_delete_operator @name=N'dbatoolsci_operator2'"
        $server.Query($sql)
    }
    Context "Get back some operators" {
        $results = Get-DbaAgentOperator -SqlInstance $script:instance2
        It "return at least two results" {
            $results.Count -ge 2 | Should Be $true
        }
        $results = Get-DbaAgentOperator -SqlInstance $script:instance2 -Operator dbatoolsci_operator
        It "return one result" {
            $results.Count | Should Be 1
        }
    }
}
tools\dbatools\tests\Get-DbaAgHadr.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag "UnitTests" {
    Context "Validate parameters" {
        $paramCount = 3
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Get-DbaAgHadr).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'EnableException'
        It "Should contian our specifc parameters" {
            ((Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}

Describe "$CommandName Integration Test" -Tag "IntegrationTests" {
    $results = Get-DbaAgHadr -SqlInstance $script:instance2
    Context "Validate output" {
        It "returns the correct properties" {
            $results.IsHadrEnabled | Should -Not -Be $null
        }
    }
}
tools\dbatools\tests\Get-DbaAgListener.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        $paramCount = 5
        <#
            Get commands, Default count = 11
            Commands with SupportShouldProcess = 13
        #>
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Get-DbaAgDatabase).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'AvailabilityGroup', 'Database', 'EnableException'
        it "Should contain our specific parameters" {
            ((Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count) | Should Be $paramCount
        }
        it "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}

InModuleScope dbatools {
    . "$PSScriptRoot\constants.ps1"
    $CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
    Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
        Mock Connect-SqlInstance {
            Import-CliXml $script:appveyorlabrepo\agserver.xml
        }
        Context "gets ag databases" {
            $results = Get-DbaAgListener -SqlInstance sql2016c
            foreach ($result in $results) {
                It "returns results with the right listener information" {
                    $result.Name | Should -Be 'splistener'
                    $result.PortNumber | Should -Be '20200'
                }
            }
            $results = Get-DbaAgListener -SqlInstance sql2016c -Listener splistener
            foreach ($result in $results) {
                It "returns results with the right listener information for a single listener" {
                    $result.Name | Should -Be 'splistener'
                    $result.PortNumber | Should -Be '20200'
                }
            }
            $results = Get-DbaAgListener -SqlInstance sql2016c -Listener doesntexist
            It "does not return a non existent listener" {
            $results | Should -Be $null
            }
        }
    }
}
tools\dbatools\tests\Get-DbaAgReplica.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        $paramCount = 5
        <#
            Get commands, Default count = 11
            Commands with SupportShouldProcess = 13
        #>
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Get-DbaAgReplica).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'AvailabilityGroup', 'Replica', 'EnableException'
        it "Should contain our specific parameters" {
            ((Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count) | Should Be $paramCount
        }
        it "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}

InModuleScope dbatools {
    . "$PSScriptRoot\constants.ps1"
    Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
        Mock Connect-SqlInstance {
            Import-Clixml $script:appveyorlabrepo\agserver.xml
        }
        Context "gets ag replicas" {
            $results = Get-DbaAgReplica -SqlInstance sql2016c
            It "returns results with proper data" {
                $results.ConnectionState | Should -Be 'Unknown', 'Unknown', 'Disconnected'
                $results.EndPointUrl -contains 'TCP://sql2016c.base.local:5022'| Should -Be $true
            }
        }
    }
}
tools\dbatools\tests\Get-DbaAvailabilityGroup.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        $paramCount = 5
        <#
            Get commands, Default count = 11
            Commands with SupportShouldProcess = 13
        #>
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Get-DbaAvailabilityGroup).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'AvailabilityGroup', 'IsPrimary', 'EnableException'
        it "Should contain our specific parameters" {
            ((Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count) | Should Be $paramCount
        }
        it "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}

Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    $dbname = "dbatoolsci_agroupdb"
    Context "gets ags" {
        $results = Get-DbaAvailabilityGroup -SqlInstance $script:instance3
        It "returns results with proper data" {
            $results.AvailabilityGroup | Should -Contain 'dbatoolsci_agroup'
            $results.AvailabilityDatabases.Name | Should -Contain $dbname
        }
        $results = Get-DbaAvailabilityGroup -SqlInstance $script:instance3 -AvailabilityGroup dbatoolsci_agroup
        It "returns a single result" {
            $results.AvailabilityGroup | Should -Be 'dbatoolsci_agroup'
            $results.AvailabilityDatabases.Name | Should -Be $dbname
        }
    }
} #$script:instance2 for appveyor
tools\dbatools\tests\Get-DbaAvailableCollation.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Available Collations" {
        $results = Get-DbaAvailableCollation -SqlInstance $script:instance2
        It "finds a collation that matches Slovenian" {
            ($results.Name -match 'Slovenian').Count -gt 10 | Should Be $true
        }
    }
}
tools\dbatools\tests\Get-DbaBackupHistory.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        $DestBackupDir = 'C:\Temp\backups'
        if (-Not (Test-Path $DestBackupDir)) {
            New-Item -ItemType Container -Path $DestBackupDir
        }
        $random = Get-Random
        $dbname = "dbatoolsci_history_$random"
        $null = Get-DbaDatabase -SqlInstance $script:instance1 -Database $dbname | Remove-DbaDatabase -Confirm:$false
        $null = Restore-DbaDatabase -SqlInstance $script:instance1 -Path $script:appveyorlabrepo\singlerestore\singlerestore.bak -DatabaseName $dbname -DestinationFilePrefix $dbname
        $db = Get-DbaDatabase -SqlInstance $script:instance1 -Database $dbname
        $db | Backup-DbaDatabase -Type Full -BackupDirectory $DestBackupDir
        $db | Backup-DbaDatabase -Type Differential -BackupDirectory $DestBackupDir
        $db | Backup-DbaDatabase -Type Log -BackupDirectory $DestBackupDir
        $db | Backup-DbaDatabase -Type Log -BackupDirectory $DestBackupDir
        $null = Get-DbaDatabase -SqlInstance $script:instance1 -Database master | Backup-DbaDatabase -Type Full
        $db | Backup-DbaDatabase -Type Full -BackupDirectory $DestBackupDir -BackupFileName CopyOnly.bak -CopyOnly
    }

    AfterAll {
        $null = Get-DbaDatabase -SqlInstance $script:instance1 -Database $dbname | Remove-DbaDatabase -Confirm:$false
    }

    Context "Get last history for single database" {
        $results = Get-DbaBackupHistory -SqlInstance $script:instance1 -Database $dbname -Last
        It "Should be 4 backups returned" {
            $results.count | Should Be 4
        }
        It "First backup should be a Full Backup" {
            $results[0].Type | Should be "Full"
        }
        It "Duration should be meaningful" {
            ($results[0].end - $results[0].start).TotalSeconds | Should Be $results[0].Duration.TotalSeconds
        }
        It "Last Backup Should be a log backup" {
            $results[-1].Type | Should Be "Log"
        }
    }

    Context "Get last history for all databases" {
        $results = Get-DbaBackupHistory -SqlInstance $script:instance1
        It "Should be more than one database" {
            ($results | Where-Object Database -match "master").Count | Should BeGreaterThan 0
        }
    }

    Context "LastFull should work with multiple databases" {
        $results = Get-DbaBackupHistory -SqlInstance $script:instance1 -Database $dbname, master -lastfull
        It "Should return 2 records" {
            $results.count | Should Be 2
        }
    }

    Context "Testing IncludeCopyOnly with LastFull" {
        $results = Get-DbaBackupHistory -SqlInstance $script:instance1 -LastFull -Database $dbname
        $resultsCo = Get-DbaBackupHistory -SqlInstance $script:instance1 -LastFull -IncludeCopyOnly -Database $dbname
        It "Should return the CopyOnly Backup" {
            ($resultsCo.BackupSetID -ne $Results.BackupSetID) | Should Be $True
        }
    }

    Context "Testing IncludeCopyOnly with Last" {
        $resultsCo = Get-DbaBackupHistory -SqlInstance $script:instance1 -Last -IncludeCopyOnly -Database $dbname
        It "Should return just the CopyOnly Full Backup" {
            ($resultsCo | Measure-Object).count | Should Be 1
        }
    }

    Context "Testing TotalSize regression test for #3517" {
        It "supports large numbers" {
            $historyObject = New-Object Sqlcollaborative.Dbatools.Database.BackupHistory
            $server = connect-dbainstance $script:instance1
            $cast = $server.Query('select cast(1000000000000000 as numeric(20,0)) AS TotalSize')
            $historyObject.TotalSize = $cast.TotalSize
            ($historyObject.TotalSize.Byte)| Should -Be 1000000000000000
        }
    }
}
tools\dbatools\tests\Get-DbaBackupInformation.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {

    BeforeAll {
        $DestBackupDir = 'C:\Temp\GetBackups'
        if (-Not(Test-Path $DestBackupDir)) {
            New-Item -Type Container -Path $DestBackupDir
        }
        else {
            Remove-Item $DestBackupDir\*
        }
        $random = Get-Random
        $dbname = "dbatoolsci_Backuphistory_$random"
        $null = Get-DbaDatabase -SqlInstance $script:instance1 -Database $dbname | Remove-DbaDatabase -Confirm:$false
        $null = Restore-DbaDatabase -SqlInstance $script:instance1 -Path $script:appveyorlabrepo\singlerestore\singlerestore.bak -DatabaseName $dbname -DestinationFilePrefix $dbname
        $db = Get-DbaDatabase -SqlInstance $script:instance1 -Database $dbname
        $db | Backup-DbaDatabase -Type Full -BackupDirectory $DestBackupDir
        $db | Backup-DbaDatabase -Type Differential -BackupDirectory $DestBackupDir
        $db | Backup-DbaDatabase -Type Log -BackupDirectory $DestBackupDir

        $dbname2 = "dbatoolsci_Backuphistory2_$random"
        $null = Get-DbaDatabase -SqlInstance $script:instance1 -Database $dbname2 | Remove-DbaDatabase -Confirm:$false
        $null = Restore-DbaDatabase -SqlInstance $script:instance1 -Path $script:appveyorlabrepo\singlerestore\singlerestore.bak -DatabaseName $dbname2 -DestinationFilePrefix $dbname2
        $db2 = Get-DbaDatabase -SqlInstance $script:instance1 -Database $dbname2
        $db2 | Backup-DbaDatabase -Type Full -BackupDirectory $DestBackupDir
        $db2 | Backup-DbaDatabase -Type Differential -BackupDirectory $DestBackupDir
        $db2 | Backup-DbaDatabase -Type Log -BackupDirectory $DestBackupDir

        $DestBackupDirOla = 'C:\Temp\GetBackupsOla'
        if (-Not(Test-Path $DestBackupDirOla)) {
            New-Item -Type Container -Path $DestBackupDirOla
            New-Item -Type Container -Path $DestBackupDirOla\FULL
            New-Item -Type Container -Path $DestBackupDirOla\DIFF
            New-Item -Type Container -Path $DestBackupDirOla\LOG
        }
        else {
            Remove-Item $DestBackupDirOla\FULL\*
            Remove-Item $DestBackupDirOla\DIFF\*
            Remove-Item $DestBackupDirOla\LOG\*
        }

        $dbname3 = "dbatoolsci_BackuphistoryOla_$random"
        $null = Get-DbaDatabase -SqlInstance $script:instance1 -Database $dbname3 | Remove-DbaDatabase -Confirm:$false
        $null = Restore-DbaDatabase -SqlInstance $script:instance1 -Path $script:appveyorlabrepo\singlerestore\singlerestore.bak -DatabaseName $dbname3 -DestinationFilePrefix $dbname3
        $db3 = Get-DbaDatabase -SqlInstance $script:instance1 -Database $dbname3
        $db3 | Backup-DbaDatabase -Type Full -BackupDirectory "$DestBackupDirOla\FULL"
        $db3 | Backup-DbaDatabase -Type Differential -BackupDirectory "$DestBackupDirOla\Diff"
        $db3 | Backup-DbaDatabase -Type Log -BackupDirectory "$DestBackupDirOla\LOG"
    }

    AfterAll {
        $null = Get-DbaDatabase -SqlInstance $script:instance1 -Database $dbname | Remove-DbaDatabase -Confirm:$false
        $null = Get-DbaDatabase -SqlInstance $script:instance1 -Database $dbname2 | Remove-DbaDatabase -Confirm:$false
    }

    Context "Get history for all database" {
        $results = Get-DbaBackupInformation -SqlInstance $script:instance1 -Path $DestBackupDir
        It "Should be 6 backups returned" {
            $results.count | Should Be 6
        }
        It "Should return 2 full backups" {
            ($results | Where-Object {$_.Type -eq 'Database'}).count | Should be 2
        }
        It "Should return 2 log backups" {
            ($results | Where-Object {$_.Type -eq 'Transaction Log'}).count | Should be 2
        }
    }

    Context "Get history for one database" {
        $results = Get-DbaBackupInformation -SqlInstance $script:instance1 -Path $DestBackupDir -DatabaseName $dbname2
        It "Should be 3 backups returned" {
            $results.count | Should Be 3
        }
        It "Should Be 1 full backup" {
            ($results | Where-Object {$_.Type -eq 'Database'}).count | Should be 1
        }
        It "Should be 1 log backups" {
            ($results | Where-Object {$_.Type -eq 'Transaction Log'}).count | Should be 1
        }
        It "Should only be backups of $dbname2" {
            ($results | Where-Object {$_.Database -ne $dbname2 }).count | Should Be 0
        }
    }

    Context "Check the export/import of backup history" {
        # This one used to cause all sorts of red
        $results = Get-DbaBackupInformation -SqlInstance $script:instance1 -Path $DestBackupDir -DatabaseName $dbname2 -ExportPath "$DestBackupDir\history.xml"

        # the command below returns just a warning
        # Get-DbaBackupInformation -Import -Path "$DestBackupDir\history.xml" | Restore-DbaDatabase -SqlInstance $script:instance1 -DestinationFilePrefix hist -RestoredDatabaseNamePrefix hist -TrustDbBackupHistory

        It "Should restore cleanly" {
            ($results | Where-Object {$_.RestoreComplete -eq $false}).count | Should be 0
        }
    }

    Context "Test Maintenance solution options" {
        $results = Get-DbaBackupInformation -SqlInstance $script:instance1 -Path $DestBackupDirOla -MaintenanceSolution
        It "Should be 3 backups returned" {
            $results.count | Should Be 3
        }
        It "Should Be 1 full backup" {
            ($results | Where-Object {$_.Type -eq 'Database'}).count | Should be 1
        }
        It "Should be 1 log backups" {
            ($results | Where-Object {$_.Type -eq 'Transaction Log'}).count | Should be 1
        }
        It "Should only be backups of $dbname3" {
            ($results | Where-Object {$_.Database -ne $dbname3 }).count | Should Be 0
        }
        $ResultsSanLog = Get-DbaBackupInformation -SqlInstance $script:instance1 -Path $DestBackupDirOla -MaintenanceSolution -IgnoreLogBackup
        It "Should be 2 backups returned" {
            $ResultsSanLog.count | Should Be 2
        }
        It "Should Be 1 full backup" {
            ($ResultsSanLog | Where-Object {$_.Type -eq 'Database'}).count | Should be 1
        }
        It "Should be 0 log backups" {
            ($resultsSanLog | Where-Object {$_.Type -eq 'Transaction Log'}).count | Should be 0
        }
        $ResultsSanLog = Get-DbaBackupInformation -SqlInstance $script:instance1 -Path $DestBackupDirOla -IgnoreLogBackup -WarningVariable warnvar -WarningAction SilentlyContinue
        It "Should Warn if IgnoreLogBackup without MaintenanceSolution" {
            ($WarnVar -match "IgnoreLogBackup can only by used with MaintenanceSolution. Will not be used") | Should Be $True
        }
        It "Should ignore IgnoreLogBackup and return 3 backups" {
            $resultsSanLog.count | Should Be 3
        }

    }

}
tools\dbatools\tests\Get-DbaClientAlias.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $newalias = New-DbaClientAlias -ServerName sql2016 -Alias dbatoolscialias -Verbose:$false
    }
    AfterAll {
        $newalias | Remove-DbaClientAlias
    }

    Context "gets the alias" {
        $results = Get-DbaClientAlias
        It "returns accurate information" {
            $results.AliasName -contains 'dbatoolscialias' | Should Be $true
        }
    }
}
tools\dbatools\tests\Get-DbaClientProtocol.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Get some client protocols" {
        $results = Get-DbaClientProtocol
        It "Should return some protocols" {
            $results.Count | Should BeGreaterThan 1
            $results | Where-Object { $_.ProtocolDisplayName -eq 'TCP/IP' } | Should Not Be $null
        }
    }
}
tools\dbatools\tests\Get-DbaClusterNode.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        <#
            The $paramCount is adjusted based on the parameters your command will have.

            The $defaultParamCount is adjusted based on what type of command you are writing the test for:
                - Commands that *do not* include SupportShouldProcess, set defaultParamCount    = 11
                - Commands that *do* include SupportShouldProcess, set defaultParamCount        = 13
        #>
        $paramCount = 5
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Get-DbaClusterNode).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'ActiveNode', 'EnableException', 'Detailed'
        It "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}
tools\dbatools\tests\Get-DbaCmObject.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    Context "returns proper information" {
        It "returns a bias that's an int" {
            (Get-DbaCmObject -ClassName Win32_TimeZone).Bias -is [int]
        }
    }
}
tools\dbatools\tests\Get-DbaComputerCertificate.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Can get a certificate" {
        BeforeAll {
            $null = Add-DbaComputerCertificate -Path $script:appveyorlabrepo\certificates\localhost.crt -Confirm:$false
            $thumbprint = "29C469578D6C6211076A09CEE5C5797EEA0C2713"
        }
        AfterAll {
            Remove-DbaComputerCertificate -Thumbprint $thumbprint -Confirm:$false
        }

        $cert = Get-DbaComputerCertificate -Thumbprint $thumbprint

        It "returns a single certificate with a specific thumbprint" {
            $cert.Thumbprint | Should Be $thumbprint
        }

        $cert = Get-DbaComputerCertificate

        It "returns all certificates and at least one has the specified thumbprint" {
            "$($cert.Thumbprint)" -match $thumbprint | Should Be $true
        }
        It "returns all certificates and at least one has the specified EnhancedKeyUsageList" {
            "$($cert.EnhancedKeyUsageList)" -match '1\.3\.6\.1\.5\.5\.7\.3\.1' | Should Be $true
        }
    }
}
tools\dbatools\tests\Get-DbaComputerSystem.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"
Describe "Get-DbaComputerSystem Unit Tests" -Tag "UnitTests" {
    InModuleScope dbatools {
        Context "Validate parameters" {
            $params = (Get-ChildItem function:\Get-DbaComputerSystem).Parameters
            it "should have a parameter named ComputerName" {
                $params.ContainsKey("ComputerName") | Should Be $true
            }
            it "should have a parameter named Credential" {
                $params.ContainsKey("Credential") | Should Be $true
            }
            it "should have a parameter named EnableException" {
                $params.ContainsKey("EnableException") | Should Be $true
            }
        }
        Context "Validate input" {
            it "Cannot resolve hostname of computer" {
                mock Resolve-DbaNetworkName {$null}
                {Get-DbaComputerSystem -ComputerName 'DoesNotExist142' -WarningAction Stop 3> $null} | Should Throw
            }
        }
    }
}
Describe "Get-DbaComputerSystem Integration Test" -Tag "IntegrationTests" {
    $result = Get-DbaComputerSystem -ComputerName $script:instance1

    $props = 'ComputerName', 'Domain', 'IsDaylightSavingsTime', 'Manufacturer', 'Model', 'NumberLogicalProcessors'
    , 'NumberProcessors', 'IsHyperThreading', 'SystemFamily', 'SystemSkuNumber', 'SystemType', 'IsSystemManagedPageFile', 'TotalPhysicalMemory'

    Context "Validate output" {
        foreach ($prop in $props) {
            $p = $result.PSObject.Properties[$prop]
            it "Should return property: $prop" {
                $p.Name | Should Be $prop
            }
        }
        it "Should return nothing if unable to connect to server" {
            $result = Get-DbaComputerSystem -ComputerName 'Melton5312' -WarningAction SilentlyContinue
            $result | Should Be $null
        }
    }
}
tools\dbatools\tests\Get-DbaConfig.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    Context "returns proper information" {
        $results = Get-DbaConfig -FullName sql.connection.timeout
        It "returns a value that is an int" {
            $results.Value -is [int]
        }
    }
}
tools\dbatools\tests\Get-DbaConnection.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    Context "returns the proper transport" {
        $results = Get-DbaConnection -SqlInstance $script:instance1
        foreach ($result in $results) {
            It "returns an scheme" {
                $result.AuthScheme -eq 'ntlm' -or $result.AuthScheme -eq 'Kerberos' | Should -Be $true
            }
        }
    }
}
tools\dbatools\tests\Get-DbaCredential.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"
. "$PSScriptRoot\..\internal\functions\Invoke-Command2.ps1"

Describe "$CommandName Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        $logins = "dbatoolsci_thor", "dbatoolsci_thorsmomma"
        $plaintext = "BigOlPassword!"
        $password = ConvertTo-SecureString $plaintext -AsPlainText -Force
        
        # Add user
        foreach ($login in $logins) {
            $null = Invoke-Command2 -ScriptBlock { net user $args[0] $args[1] /add *>&1 } -ArgumentList $login, $plaintext -ComputerName $script:instance2
        }
        
        $results = New-DbaCredential -SqlInstance $script:instance2 -Name dbatoolsci_thorcred -Identity dbatoolsci_thor -Password $password
        $results = New-DbaCredential -SqlInstance $script:instance2 -Identity dbatoolsci_thorsmomma -Password $password
    }
    AfterAll {
        try {
            (Get-DbaCredential -SqlInstance $script:instance2 -Identity dbatoolsci_thor, dbatoolsci_thorsmomma -ErrorAction Stop -WarningAction SilentlyContinue).Drop()
        }
        catch { }
        
        foreach ($login in $logins) {
            $null = Invoke-Command2 -ScriptBlock { net user $args /delete *>&1 } -ArgumentList $login -ComputerName $script:instance2
            $null = Invoke-Command2 -ScriptBlock { net user $args /delete *>&1 } -ArgumentList $login -ComputerName $script:instance2
        }
    }
    
    Context "Get credentials" {
        It "Should get just one credential with the proper properties when using Identity" {
            $results = Get-DbaCredential -SqlInstance $script:instance2 -Identity dbatoolsci_thorsmomma
            $results.Name | Should Be "dbatoolsci_thorsmomma"
            $results.Identity | Should Be "dbatoolsci_thorsmomma"
        }
        It "Should get just one credential with the proper properties when using Name" {
            $results = Get-DbaCredential -SqlInstance $script:instance2 -Name dbatoolsci_thorsmomma
            $results.Name | Should Be "dbatoolsci_thorsmomma"
            $results.Identity | Should Be "dbatoolsci_thorsmomma"
        }
        It "gets more than one credential" {
            $results = Get-DbaCredential -SqlInstance $script:instance2 -Identity dbatoolsci_thor, dbatoolsci_thorsmomma
            $results.count -gt 1
        }
    }
}
tools\dbatools\tests\Get-DbaDatabase.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {

    Context "Count system databases on localhost" {
        $results = Get-DbaDatabase -SqlInstance $script:instance1 -ExcludeAllUserDb
        It "reports the right number of databases" {
            $results.Count | Should Be 4
        }
    }

    Context "Check that temppb database is in Simple recovery mode" {
        $results = Get-DbaDatabase -SqlInstance $script:instance1 -Database tempdb
        It "tempdb's recovery mode is Simple" {
            $results.RecoveryModel | Should Be "Simple"
        }
    }

    Context "Check that master database is accessible" {
        $results = Get-DbaDatabase -SqlInstance $script:instance1 -Database master
        It "master is accessible" {
            $results.IsAccessible | Should Be $true
        }
    }
}

Describe "$commandname Unit Tests" -Tags "UnitTests", Get-DBADatabase {
    BeforeAll {
        ## Ensure it is the module that is being coded that is in the session when running just this Pester test
        #  Remove-Module dbatools -Force -ErrorAction SilentlyContinue
        #  $Base = Split-Path -parent $PSCommandPath
        #  Import-Module $Base\..\dbatools.psd1
    }
    Context "Input validation" {
        BeforeAll {
            Mock Stop-Function { } -ModuleName dbatools
            Mock Test-FunctionInterrupt { } -ModuleName dbatools
        }
        Mock Connect-SQLInstance -MockWith {
            [object]@{
                Name      = 'SQLServerName';
                Databases = [object]@(
                    @{
                        Name           = 'db1'
                        Status         = 'Normal'
                        ReadOnly       = 'false'
                        IsSystemObject = 'false'
                        RecoveryModel  = 'Full'
                        Owner          = 'sa'
                    }
                ); #databases
            } #object
        } -ModuleName dbatools #mock connect-sqlserver
        function Invoke-QueryRawDatabases { }
        Mock Invoke-QueryRawDatabases -MockWith {
            [object]@(
                @{
                    name     = 'db1'
                    state = 0
                    Owner = 'sa'
                }
            )
        } -ModuleName dbatools
        It "Should Call Stop-Function if NoUserDbs and NoSystemDbs are specified" {
            Get-DbaDatabase -SqlInstance Dummy -ExcludeAllSystemDb -ExcludeAllUserDb -ErrorAction SilentlyContinue | Should Be
        }
        It "Validates that Stop Function Mock has been called" {
            $assertMockParams = @{
                'CommandName' = 'Stop-Function'
                'Times'       = 1
                'Exactly'     = $true
                'Module'      = 'dbatools'
            }
            Assert-MockCalled @assertMockParams
        }
        It "Validates that Test-FunctionInterrupt Mock has been called" {
            $assertMockParams = @{
                'CommandName' = 'Test-FunctionInterrupt'
                'Times'       = 1
                'Exactly'     = $true
                'Module'      = 'dbatools'
            }
            Assert-MockCalled @assertMockParams
        }
    }
    Context "Output" {
        It "Should have Last Read and Last Write Property when IncludeLastUsed switch is added" {
            Mock Connect-SQLInstance -MockWith {
                [object]@{
                    Name      = 'SQLServerName'
                    Databases = [object]@{
                            'db1' = @{
                                Name           = 'db1'
                                Status         = 'Normal'
                                ReadOnly       = 'false'
                                IsSystemObject = 'false'
                                RecoveryModel  = 'Full'
                                Owner          = 'sa'
                                IsAccessible   = $true
                            }
                    }
                } #object
            } -ModuleName dbatools #mock connect-sqlserver
            function Invoke-QueryDBlastUsed { }
            Mock Invoke-QueryDBlastUsed -MockWith {
                [object]
                @{
                    dbname     = 'db1'
                    last_read  = (Get-Date).AddHours(-1)
                    last_write = (Get-Date).AddHours(-1)
                }
            } -ModuleName dbatools
            function Invoke-QueryRawDatabases { }
            Mock Invoke-QueryRawDatabases -MockWith {
                [object]@(
                    @{
                        name  = 'db1'
                        state = 0
                        Owner = 'sa'
                    }
                )
            } -ModuleName dbatools
            (Get-DbaDatabase -SqlInstance SQLServerName -IncludeLastUsed).LastRead -ne $null | Should Be $true
            (Get-DbaDatabase -SqlInstance SQLServerName -IncludeLastUsed).LastWrite -ne $null | Should Be $true
        }
        It "Validates that Connect-SqlInstance Mock has been called" {
            $assertMockParams = @{
                'CommandName' = 'Connect-SqlInstance'
                'Times'       = 2
                'Exactly'     = $true
                'Module'      = 'dbatools'
            }
            Assert-MockCalled @assertMockParams
        }
        It "Validates that Invoke-QueryDBlastUsed Mock has been called" {
            $assertMockParams = @{
                'CommandName' = 'Invoke-QueryDBlastUsed'
                'Times'       = 2
                'Exactly'     = $true
                'Module'      = 'dbatools'
            }
            Assert-MockCalled @assertMockParams
        }
    }
}
tools\dbatools\tests\Get-DbaDatabaseEncryption.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Test Retriving Certificate" {
        BeforeAll {
            $random = Get-Random
            $cert = "dbatoolsci_getcert$random"
            $password = ConvertTo-SecureString -String Get-Random -AsPlainText -Force
            New-DbaDbCertificate -SqlInstance $script:instance1 -Name $cert -password $password
        }
        AfterAll {
            Get-DbaDbCertificate -SqlInstance $script:instance1 -Certificate $cert | Remove-DbaDbCertificate -confirm:$false
        }
        $results = Get-DbaDatabaseEncryption -SqlInstance $script:instance1
        It "Should find a certificate named $cert" {
            ($results.Name -match 'dbatoolsci').Count -gt 0 | Should Be $true
        }
    }
}
tools\dbatools\tests\Get-DbaDatabaseFile.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Unit Tests" -Tag "Unit" {
    Context "Ensure array" {
        $results = Get-Command -Name Get-DbaDatabaseFile | Select-Object -ExpandProperty ScriptBlock
        It "returns disks as an array" {
            $results -match '\$disks \= \@\(' | Should -Be $true
        }
    }
}

Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    Context "Count system databases on localhost" {
        $results = Get-DbaDatabaseFile -SqlInstance $script:instance1
        It "returns information about tempdb" {
            $results.Database -contains "tempdb" | Should -Be $true
        }
    }
    
    Context "Check that temppb database is in Simple recovery mode" {
        $results = Get-DbaDatabaseFile -SqlInstance $script:instance1 -Database tempdb
        foreach ($result in $results) {
            It "returns only information about tempdb" {
                $result.Database | Should -Be "tempdb"
            }
        }
    }
    
    Context "Physical name is populated" {
        $results = Get-DbaDatabaseFile -SqlInstance $script:instance1 -Database master
        It "master returns proper results" {
            $result = $results | Where-Object LogicalName -eq 'master'
            $result.PhysicalName -match 'master.mdf' | Should -Be $true
            $result = $results | Where-Object LogicalName -eq 'mastlog'
            $result.PhysicalName -match 'mastlog.ldf' | Should -Be $true
        }
    }
}
tools\dbatools\tests\Get-DbaDatabaseState.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {

    Context "Reading db statuses" {
        BeforeAll {
            $server = Connect-DbaInstance -SqlInstance $script:instance2
            $db1 = "dbatoolsci_dbstate_online"
            $db2 = "dbatoolsci_dbstate_offline"
            $db3 = "dbatoolsci_dbstate_emergency"
            $db4 = "dbatoolsci_dbstate_single"
            $db5 = "dbatoolsci_dbstate_restricted"
            $db6 = "dbatoolsci_dbstate_multi"
            $db7 = "dbatoolsci_dbstate_rw"
            $db8 = "dbatoolsci_dbstate_ro"
            Get-DbaDatabase -SqlInstance $script:instance2 -Database $db1, $db2, $db3, $db4, $db5, $db6, $db7, $db8 | Remove-DbaDatabase -Confirm:$false
            $server.Query("CREATE DATABASE $db1")
            $server.Query("CREATE DATABASE $db2; ALTER DATABASE $db2 SET OFFLINE WITH ROLLBACK IMMEDIATE")
            $server.Query("CREATE DATABASE $db3; ALTER DATABASE $db3 SET EMERGENCY WITH ROLLBACK IMMEDIATE")
            $server.Query("CREATE DATABASE $db4; ALTER DATABASE $db4 SET SINGLE_USER WITH ROLLBACK IMMEDIATE")
            $server.Query("CREATE DATABASE $db5; ALTER DATABASE $db5 SET RESTRICTED_USER WITH ROLLBACK IMMEDIATE")
            $server.Query("CREATE DATABASE $db6; ALTER DATABASE $db6 SET MULTI_USER WITH ROLLBACK IMMEDIATE")
            $server.Query("CREATE DATABASE $db7; ALTER DATABASE $db7 SET READ_WRITE WITH ROLLBACK IMMEDIATE")
            $server.Query("CREATE DATABASE $db8; ALTER DATABASE $db8 SET READ_ONLY WITH ROLLBACK IMMEDIATE")
            $setupright = $true
            $needed_ = $server.Query("select name from sys.databases")
            $needed = $needed_ | Where-Object name -in $db1, $db2, $db3, $db4, $db5, $db6, $db7, $db8
            if ($needed.Count -ne 8) {
                $setupright = $false
                It "has failed setup" {
                    Set-TestInconclusive -Message "Setup failed"
                }
            }
        }
        AfterAll {
            $null = Set-DbaDatabaseState -Sqlinstance $script:instance2 -Database $db2, $db3, $db4, $db5, $db7 -Online -ReadWrite -MultiUser -Force
            Remove-DbaDatabase -Confirm:$false -SqlInstance $script:instance2 -Database $db1, $db2, $db3, $db4, $db5, $db6, $db7, $db8
        }
        if ($setupright) {
            # just to have a correct report on how much time BeforeAll takes
            It "Waits for BeforeAll to finish" {
                $true | Should Be $true
            }
            It "Honors the Database parameter" {
                $result = Get-DbaDatabaseState -SqlInstance $script:instance2 -Database $db2
                $result.DatabaseName | Should be $db2
                $results = Get-DbaDatabaseState -SqlInstance $script:instance2 -Database $db1, $db2
                $results.Count | Should be 2
            }
            It "Honors the ExcludeDatabase parameter" {
                $alldbs_ = $server.Query("select name from sys.databases")
                $alldbs = ($alldbs_ | Where-Object Name -notin @($db1, $db2, $db3, $db4, $db5, $db6, $db7, $db8)).name
                $results = Get-DbaDatabaseState -SqlInstance $script:instance2 -ExcludeDatabase $alldbs
                $comparison = Compare-Object -ReferenceObject ($results.DatabaseName) -DifferenceObject (@($db1, $db2, $db3, $db4, $db5, $db6, $db7, $db8))
                $comparison.Count | Should Be 0
            }
            It "Identifies online database" {
                $result = Get-DbaDatabaseState -SqlInstance $script:instance2 -Database $db1
                $result.DatabaseName | Should Be $db1
                $result.Status | Should Be "ONLINE"
            }
            It "Identifies offline database" {
                $result = Get-DbaDatabaseState -SqlInstance $script:instance2 -Database $db2
                $result.DatabaseName | Should Be $db2
                $result.Status | Should Be "OFFLINE"
            }
            It "Identifies emergency database" {
                $result = Get-DbaDatabaseState -SqlInstance $script:instance2 -Database $db3
                $result.DatabaseName | Should Be $db3
                $result.Status | Should Be "EMERGENCY"
            }
            It "Identifies single_user database" {
                $result = Get-DbaDatabaseState -SqlInstance $script:instance2 -Database $db4
                $result.DatabaseName | Should Be $db4
                $result.Access | Should Be "SINGLE_USER"
            }
            It "Identifies restricted_user database" {
                $result = Get-DbaDatabaseState -SqlInstance $script:instance2 -Database $db5
                $result.DatabaseName | Should Be $db5
                $result.Access | Should Be "RESTRICTED_USER"
            }
            It "Identifies multi_user database" {
                $result = Get-DbaDatabaseState -SqlInstance $script:instance2 -Database $db6
                $result.DatabaseName | Should Be $db6
                $result.Access | Should Be "MULTI_USER"
            }
            It "Identifies read_write database" {
                $result = Get-DbaDatabaseState -SqlInstance $script:instance2 -Database $db7
                $result.DatabaseName | Should Be $db7
                $result.RW | Should Be "READ_WRITE"
            }
            It "Identifies read_only database" {
                $result = Get-DbaDatabaseState -SqlInstance $script:instance2 -Database $db8
                $result.DatabaseName | Should Be $db8
                $result.RW | Should Be "READ_ONLY"
            }

            $result = Get-DbaDatabaseState -SqlInstance $script:instance2 -Database $db1
            It "Has the correct properties" {
                $ExpectedProps = 'SqlInstance,InstanceName,ComputerName,DatabaseName,RW,Status,Access,Database'.Split(',')
                ($result.PsObject.Properties.Name | Sort-Object) | Should Be ($ExpectedProps | Sort-Object)
            }

            It "Has the correct default properties" {
                $ExpectedPropsDefault = 'SqlInstance,InstanceName,ComputerName,DatabaseName,RW,Status,Access'.Split(',')
                ($result.PSStandardMembers.DefaultDisplayPropertySet.ReferencedPropertyNames | Sort-Object) | Should Be ($ExpectedPropsDefault | Sort-Object)
            }
        }
    }
}

tools\dbatools\tests\Get-DbaDatabaseView.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {

        $paramCount = 6
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Get-DbaDatabaseView).Parameters.Keys
        $knownParameters = 'SqlInstance','SqlCredential','Database','ExcludeDatabase','ExcludeSystemView','EnableException'
        it "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        it "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $server = Connect-DbaInstance -SqlInstance $script:instance2
        $viewName = ("dbatoolsci_{0}" -f $(Get-Random))
        $server.Query("CREATE VIEW $viewName AS (SELECT 1 as col1)", 'tempdb')
    }
    AfterAll {
        $null = $server.Query("DROP VIEW $viewName", 'tempdb')
    }

    Context "Command actually works" {
        $results = Get-DbaDatabaseView -SqlInstance $script:instance2 -Database tempdb | Select-Object Name, IsSystemObject
        It "Should get test view: $viewName" {
            ($results | Where-Object Name -eq $viewName).Name | Should Be $true
        }
        It "Should include system views" {
            $results.IsSystemObject | Should Contain $true
        }
    }

    Context "Exclusions work correctly" {
        It "Should contain no views from master database" {
            $results = Get-DbaDatabaseView -SqlInstance $script:instance2 -ExcludeDatabase master
            $results.Database | Should Not Contain 'master'
        }
        It "Should exclude system views" {
            $results = Get-DbaDatabaseView -SqlInstance $script:instance2 -ExcludeSystemView | Select-Object Name, IsSystemObject
             $results.IsSystemObject | Should Not Contain $true
        }
    }
}
tools\dbatools\tests\Get-DbaDbCertificate.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Can get a database certificate" {
        BeforeAll {
            if (-not (Get-DbaDatabaseMasterKey -SqlInstance $script:instance1 -Database master)) {
                $masterkey = New-DbaDatabaseMasterKey -SqlInstance $script:instance1 -Database master -Password $(ConvertTo-SecureString -String "GoodPass1234!" -AsPlainText -Force) -Confirm:$false
            }

            $tempdbmasterkey = New-DbaDatabasemasterKey -SqlInstance $script:instance1 -Database tempdb -Password $(ConvertTo-SecureString -String "GoodPass1234!" -AsPlainText -Force) -Confirm:$false
            $certificateName1 = "Cert_$(Get-random)"
            $certificateName2 = "Cert_$(Get-random)"
            $cert1 = New-DbaDbCertificate -SqlInstance $script:instance1 -Name $certificateName1
            $cert2 = New-DbaDbCertificate -SqlInstance $script:instance1 -Name $certificateName2 -Database "tempdb"
        }
        AfterAll {
            $null = $cert1 | Remove-DbaDbCertificate -Confirm:$false
            $null = $cert2 | Remove-DbaDbCertificate -Confirm:$false
            if ($tempdbmasterkey) { $tempdbmasterkey | Remove-DbaDatabaseMasterKey -Confirm:$false }
            if ($masterKey) { $masterkey | Remove-DbaDatabasemasterKey -Confirm:$false }
        }

        $cert = Get-DbaDbCertificate -SqlInstance $script:instance1 -Certificate $certificateName1
        It "returns database certificate created in default, master database" {
            "$($cert.Database)" -match 'master' | Should Be $true
        }

        $cert = Get-DbaDbCertificate -SqlInstance $script:instance1 -Database tempdb
        It "returns database certificate created in tempdb database, looked up by certificate name" {
            "$($cert.Name)" -match $certificateName2 | Should Be $true
        }

        $cert = Get-DbaDbCertificate -SqlInstance $script:instance1 -ExcludeDatabase master
        It "returns database certificates excluding those in the master database" {
            "$($cert.Database)" -notmatch 'master' | Should Be $true
        }

    }
}
tools\dbatools\tests\Get-DbaDbCheckConstraint.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tags 'UnitTests' {
    Context "Validate parameters" {
        <#
            The $paramCount is adjusted based on the parameters your command will have.
            The $defaultParamCount is adjusted based on what type of command you are writing the test for:
                - Commands that *do not* include SupportShouldProcess, set defaultParamCount    = 11
                - Commands that *do* include SupportShouldProcess, set defaultParamCount        = 13
        #>
        $paramCount = 6
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Get-DbaDbCheckConstraint).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'EnableException', 'Database', 'ExcludeDatabase', 'ExcludeSystemTable'
        It "Should contain our specific parameters" {
            ((Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}

Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        $server = Connect-DbaInstance -SqlInstance $script:instance2
        $random = Get-Random
        $tableName = "dbatools_getdbtbl1"
        $tableName2 = "dbatools_getdbtbl2"
        $ckName = "dbatools_getdbck"
        $dbname = "dbatoolsci_getdbfk$random"
        $server.Query("CREATE DATABASE $dbname")
        $server.Query("CREATE TABLE $tableName (idTbl1 INT PRIMARY KEY)", $dbname)
        $server.Query("CREATE TABLE $tableName2 (idTbl2 INT, idTbl1 INT, id3 INT)", $dbname)
        $server.Query("ALTER TABLE $tableName2 ADD CONSTRAINT $ckName CHECK (id3 > 10)", $dbname)
    }

    AfterAll {
        $null = Get-DbaDatabase -SqlInstance $script:instance2 -Database $dbname | Remove-DbaDatabase -Confirm:$false
    }

    Context "Command actually works" {
        It "returns no check constraints from excluded DB with -ExcludeDatabase" {
            $results = Get-DbaDbCheckConstraint -SqlInstance $script:instance2 -ExcludeDatabase master
            $results.where( {$_.Database -eq 'master'}).count | Should Be 0
        }
        It "returns only check constraints from selected DB with -Database" {
            $results = Get-DbaDbCheckConstraint -SqlInstance $script:instance2 -Database $dbname
            $results.where( {$_.Database -ne 'master'}).count | Should Be 1
        }
        It "Should include test check constraint: $ckName" {
            $results = Get-DbaDbCheckConstraint -SqlInstance $script:instance2 -Database $dbname -ExcludeSystemTable
            ($results | Where-Object Name -eq $ckName).Name | Should Be $ckName
        }
        It "Should exclude system tables" {
            $results = Get-DbaDbCheckConstraint -SqlInstance $script:instance2 -Database master -ExcludeSystemTable
            ($results | Where-Object Name -eq 'spt_fallback_db') | Should Be $null
        }
    }
}
tools\dbatools\tests\Get-DbaDbCompression.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        $paramCount = 5
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Get-DbaDbCompression).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential','Database','ExcludeDatabase','EnableException'
        It "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $dbname = "dbatoolsci_test_$(get-random)"
        $server = Connect-DbaInstance -SqlInstance $script:instance2
        $null = $server.Query("Create Database [$dbname]")
        $null = $server.Query("select * into syscols from sys.all_columns
                                select * into sysallparams from sys.all_parameters
                                create clustered index CL_sysallparams on sysallparams (object_id)
                                create nonclustered index NC_syscols on syscols (precision) include (collation_name)",$dbname)
       }
    AfterAll {
        Get-DbaProcess -SqlInstance $script:instance2 -Database $dbname | Stop-DbaProcess -WarningAction SilentlyContinue
        Remove-DbaDatabase -SqlInstance $script:instance2 -Database $dbname -Confirm:$false
    }
    $results = Get-DbaDbCompression -SqlInstance $script:instance2 -Database $dbname

    Context "Command handles heaps and clustered indexes" {
        It "Gets results" {
            $results | Should Not Be $null
        }
        Foreach ($row in $results | Where-Object {$_.IndexId -le 1}) {
            It "Should return compression level for object $($row.TableName)" {
                $row.DataCompression | Should BeIn ('None','Row','Page')
            }
        }
    }
    Context "Command handles nonclustered indexes" {
        It "Gets results" {
            $results | Should Not Be $null
        }
        Foreach ($row in $results | Where-Object {$_.IndexId -gt 1}) {
            It "Should return compression level for nonclustered index $($row.IndexName)" {
                $row.DataCompression | Should BeIn ('None','Row','Page')
            }
        }
    }

    Context "Command excludes results for specified database" {
        It "Shouldn't get any results for $dbname" {
            $(Get-DbaDbCompression -SqlInstance $script:instance2 -Database $dbname -ExcludeDatabase $dbname) | Should not Match $dbname
        }
    }
}
tools\dbatools\tests\Get-DbaDbForeignKey.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tags 'UnitTests' {
    Context "Validate parameters" {
        <#
            The $paramCount is adjusted based on the parameters your command will have.
            The $defaultParamCount is adjusted based on what type of command you are writing the test for:
                - Commands that *do not* include SupportShouldProcess, set defaultParamCount    = 11
                - Commands that *do* include SupportShouldProcess, set defaultParamCount        = 13
        #>
        $paramCount = 6
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Get-DbaDbForeignKey).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'EnableException', 'Database', 'ExcludeDatabase', 'ExcludeSystemTable'
        It "Should contain our specific parameters" {
            ((Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}

Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        $server = Connect-DbaInstance -SqlInstance $script:instance2
        $random = Get-Random
        $tableName = "dbatools_getdbtbl1"
        $tableName2 = "dbatools_getdbtbl2"
        $fkName = "dbatools_getdbfk"
        $dbname = "dbatoolsci_getdbfk$random"
        $server.Query("CREATE DATABASE $dbname")
        $server.Query("CREATE TABLE $tableName (idTbl1 INT PRIMARY KEY)", $dbname)
        $server.Query("CREATE TABLE $tableName2 (idTbl2 INT, idTbl1 INT)", $dbname)
        $server.Query("ALTER TABLE $tableName2 ADD CONSTRAINT $fkName FOREIGN KEY (idTbl1) REFERENCES $tableName (idTbl1) ON UPDATE NO ACTION ON DELETE NO ACTION ", $dbname)
    }

    AfterAll {
        $null = Get-DbaDatabase -SqlInstance $script:instance2 -Database $dbname | Remove-DbaDatabase -Confirm:$false
    }

    Context "Command actually works" {
        It "returns no foreign keys from excluded DB with -ExcludeDatabase" {
            $results = Get-DbaDbForeignKey -SqlInstance $script:instance2 -ExcludeDatabase master
            $results.where( {$_.Database -eq 'master'}).count | Should Be 0
        }
        It "returns only foreign keys from selected DB with -Database" {
            $results = Get-DbaDbForeignKey -SqlInstance $script:instance2 -Database $dbname
            $results.where( {$_.Database -ne 'master'}).count | Should Be 1
        }
        It "Should include test foreign keys: $ckName" {
            $results = Get-DbaDbForeignKey -SqlInstance $script:instance2 -Database $dbname -ExcludeSystemTable
            ($results | Where-Object Name -eq $ckName).Name | Should Be $ckName
        }
        It "Should exclude system tables" {
            $results = Get-DbaDbForeignKey -SqlInstance $script:instance2 -Database master -ExcludeSystemTable
            ($results | Where-Object Name -eq 'spt_fallback_db') | Should Be $null
        }
    }
}
tools\dbatools\tests\Get-DbaDbPageInfo.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Unit Tests" -Tag "UnitTests" {
    BeforeAll {
        $random = Get-Random
        $dbname = "dbatoolsci_pageinfo_$random"
        Get-DbaProcess -SqlInstance $script:instance2 -Program 'dbatools PowerShell module - dbatools.io' | Stop-DbaProcess -WarningAction SilentlyContinue
        $server = Connect-DbaInstance -SqlInstance $script:instance2
        $server.Query("CREATE DATABASE $dbname;")
        $server.Databases[$dbname].Query('CREATE TABLE [dbo].[TestTable](TestText VARCHAR(MAX) NOT NULL)')
        $query = "
                INSERT INTO dbo.TestTable
                (
                    TestText
                )
                VALUES
                ('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA')"
        
        # Generate a bunch of extra inserts to create enough pages
        1..100 | ForEach-Object {
            $query += ",('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA')"
        }
        $server.Databases[$dbname].Query($query)
    }
    AfterAll {
        Remove-DbaDatabase -SqlInstance $script:instance2 -Database $dbname -Confirm:$false
    }
    Context "Count Pages" {
        $result = Get-DbaDbPageInfo -SqlInstance $script:instance2 -Database $dbname
        It "returns the proper results" {
            ($result).Count | Should -Be 9
            ($result | Where-Object { $_.IsAllocated -eq $false }).Count | Should -Be 5
            ($result | Where-Object { $_.IsAllocated -eq $true }).Count | Should -Be 4
        }
    }
}
tools\dbatools\tests\Get-DbaDbQueryStoreOptions.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        Get-DbaDatabase -SqlInstance $script:instance1, $script:instance2 | Where-Object Name -Match 'dbatoolsci' | Remove-DbaDatabase -Confirm:$false
    }
    Context "Get some client protocols" {
        foreach ($instance in ($script:instance1, $script:instance2)) {
            $server = Connect-DbaInstance -SqlInstance $instance
            $results = Get-DbaDbQueryStoreOptions -SqlInstance $instance -WarningVariable warning  3>&1

            if ($server.VersionMajor -lt 13) {
                It "should warn" {
                    $warning | Should Not Be $null
                }
            }
            else {
                It "should return some valid results" {
                    $result = $results | Where-Object Database -eq msdb
                    $result.ActualState | Should Be 'Off'
                }

                It "should only get one database" {
                    $results = Get-DbaDbQueryStoreOptions -SqlInstance $instance -Database model
                    $results.Count | Should Be 1
                    $results.Database | Should Be 'model'
                }

                It "should not get this one database" {
                    $results = Get-DbaDbQueryStoreOptions -SqlInstance $instance -ExcludeDatabase model
                    $result = $results | Where-Object Database -eq model
                    $result.Count | Should Be 0
                }
            }
        }
    }
}
tools\dbatools\tests\Get-DbaDbRecoveryModel.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Recovery model is correctly identified" {
        $results = Get-DbaDbRecoveryModel -SqlInstance $script:instance2 -Database master

        It "returns a single database" {
            $results.Count | Should Be 1
        }

        It "returns the correct recovery model" {
            $results.RecoveryModel -eq 'Simple' | Should Be $true
        }

        $results = Get-DbaDbRecoveryModel -SqlInstance $script:instance2

        It "returns accurate number of results" {
            $results.Count -ge 4 | Should Be $true
        }
    }
    Context "RecoveryModel parameter works" {
        BeforeAll {
            $server = Connect-DbaInstance -SqlInstance $script:instance2
            $dbname = "dbatoolsci_getrecoverymodel"
            Get-DbaDatabase -SqlInstance $server -Database $dbname | Remove-DbaDatabase -Confirm:$false
            $server.Query("CREATE DATABASE $dbname; ALTER DATABASE $dbname SET RECOVERY BULK_LOGGED WITH NO_WAIT;")
        }
        AfterAll {
            Get-DbaDatabase -SqlInstance $script:instance2 -Database $dbname | Remove-DbaDatabase -Confirm:$false
        }

        It "gets the newly created database with the correct recovery model" {
            $results = Get-DbaDbRecoveryModel -SqlInstance $script:instance2 -Database $dbname
            $results.RecoveryModel -eq 'BulkLogged' | Should Be $true
        }
        It "honors the RecoveryModel parameter filter" {
            $results = Get-DbaDbRecoveryModel -SqlInstance $script:instance2 -RecoveryModel BulkLogged
            $results.Name -contains $dbname | Should Be $true
        }
    }
}
tools\dbatools\tests\Get-DbaDbRole.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tags 'UnitTests' {
    Context "Validate parameters" {
        <#
            The $paramCount is adjusted based on the parameters your command will have.
            The $defaultParamCount is adjusted based on what type of command you are writing the test for:
                - Commands that *do not* include SupportShouldProcess, set defaultParamCount    = 11
                - Commands that *do* include SupportShouldProcess, set defaultParamCount        = 13
        #>
        $paramCount = 6
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Get-DbaDbRole).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'EnableException', 'Database', 'ExcludeDatabase', 'ExcludeFixedRole'
        It "Should contain our specific parameters" {
            ((Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}

Describe "Get-DbaDbRole Integration Tests" -Tag "IntegrationTests" {
    Context "parameters work" {
        It "returns no roles from excluded DB with -ExcludeDatabase" {
            $results = Get-DbaDbRole -SqlInstance $script:instance2 -ExcludeDatabase master
            $results.where( {$_.Database -eq 'master'}).count | Should Be 0
        }
        It "returns only roles from selected DB with -Database" {
            $results = Get-DbaDbRole -SqlInstance $script:instance2 -Database master
            $results.where( {$_.Database -ne 'master'}).count | Should Be 0
        }
        It "returns no fixed roles with -ExcludeFixedRole" {
            $results = Get-DbaDbRole -SqlInstance $script:instance2 -ExcludeFixedRole
            $results.where( {$_.name -match 'db_datareader|db_datawriter|db_ddladmin'}).count | Should Be 0
        }
        It "returns fixed roles without -ExcludeFixedRole" {
            $results = Get-DbaDbRole -SqlInstance $script:instance2
            $results.where( {$_.name -match 'db_datareader|db_datawriter|db_ddladmin'}).count | Should BeGreaterThan 0
        }
    }
}
tools\dbatools\tests\Get-DbaDbSnapshot.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

# Targets only instance2 because it's the only one where Snapshots can happen
Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    Context "Operations on snapshots" {
        BeforeAll {
            Get-DbaProcess -SqlInstance $script:instance2 | Where-Object Program -match dbatools | Stop-DbaProcess -Confirm:$false -WarningAction SilentlyContinue
            $server = Connect-DbaInstance -SqlInstance $script:instance2
            $db1 = "dbatoolsci_GetSnap"
            $db1_snap1 = "dbatoolsci_GetSnap_snapshotted1"
            $db1_snap2 = "dbatoolsci_GetSnap_snapshotted2"
            $db2 = "dbatoolsci_GetSnap2"
            $db2_snap1 = "dbatoolsci_GetSnap2_snapshotted"
            Remove-DbaDbSnapshot -SqlInstance $script:instance2 -Database $db1, $db2 -Confirm:$false
            Get-DbaDatabase -SqlInstance $script:instance2 -Database $db1, $db2 | Remove-DbaDatabase -Confirm:$false
            $server.Query("CREATE DATABASE $db1")
            $server.Query("CREATE DATABASE $db2")
            $null = New-DbaDbSnapshot -SqlInstance $script:instance2 -Database $db1 -Name $db1_snap1 -WarningAction SilentlyContinue
            $null = New-DbaDbSnapshot -SqlInstance $script:instance2 -Database $db1 -Name $db1_snap2 -WarningAction SilentlyContinue
            $null = New-DbaDbSnapshot -SqlInstance $script:instance2 -Database $db2 -Name $db2_snap1 -WarningAction SilentlyContinue
        }
        AfterAll {
            Remove-DbaDbSnapshot -SqlInstance $script:instance2 -Database $db1, $db2 -ErrorAction SilentlyContinue -Confirm:$false
            Remove-DbaDatabase -Confirm:$false -SqlInstance $script:instance2 -Database $db1, $db2 -ErrorAction SilentlyContinue
        }

        It "Gets all snapshots by default" {
            $results = Get-DbaDbSnapshot -SqlInstance $script:instance2
            ($results | Where-Object Name -Like 'dbatoolsci_GetSnap*').Count | Should Be 3
        }
        It "Honors the Database parameter, returning only snapshots of that database" {
            $results = Get-DbaDbSnapshot -SqlInstance $script:instance2 -Database $db1
            $results.Count | Should Be 2
            $result = Get-DbaDbSnapshot -SqlInstance $script:instance2 -Database $db2
            $result.SnapshotOf | Should Be $db2
        }
        It "Honors the ExcludeDatabase parameter, returning relevant snapshots" {
            $alldbs = (Get-DbaDatabase -SqlInstance $script:instance2 | Where-Object IsDatabaseSnapShot -eq $false | Where-Object Name -notin @($db1, $db2)).Name
            $results = Get-DbaDbSnapshot -SqlInstance $script:instance2 -ExcludeDatabase $alldbs
            $results.Count | Should Be 3
        }
        It "Honors the Snapshot parameter" {
            $result = Get-DbaDbSnapshot -SqlInstance $script:instance2 -Snapshot $db1_snap1
            $result.Name | Should Be $db1_snap1
            $result.SnapshotOf | Should Be $db1
        }
        It "Honors the ExcludeSnapshot parameter" {
            $result = Get-DbaDbSnapshot -SqlInstance $script:instance2 -ExcludeSnapshot $db1_snap1 -Database $db1
            $result.Name | Should Be $db1_snap2
        }
        It "has the correct default properties" {
            $result = Get-DbaDbSnapshot -SqlInstance $script:instance2 -Database $db2
            $ExpectedPropsDefault = 'ComputerName', 'CreateDate', 'InstanceName', 'Name', 'SnapshotOf', 'SqlInstance', 'DiskUsage'
            ($result.PSStandardMembers.DefaultDisplayPropertySet.ReferencedPropertyNames | Sort-Object) | Should Be ($ExpectedPropsDefault | Sort-Object)
        }
    }
}
tools\dbatools\tests\Get-DbaDbStoredProcedure.Tests.ps1
<#
    The below statement stays in for every test you build.
#>
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

<#
    Unit test is required for any command added
#>
Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        <#
            The $paramCount is adjusted based on the parameters your command will have.

            The $defaultParamCount is adjusted based on what type of command you are writing the test for:
                - Commands that *do not* include SupportShouldProcess, set defaultParamCount    = 11
                - Commands that *do* include SupportShouldProcess, set defaultParamCount        = 13
        #>
        $paramCount = 6
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Get-DbaDbStoredProcedure).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'Database', 'ExcludeDatabase', 'ExcludeSystemSp', 'EnableException'
        it "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        it "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}
# Get-DbaNoun
Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $server = Connect-DbaInstance -SqlInstance $script:instance2
        $random = Get-Random
        $procName = "dbatools_getdbsp"
        $dbname = "dbatoolsci_getdbsp$random"
        $server.Query("CREATE DATABASE $dbname")
        $server.Query("CREATE PROCEDURE $procName AS SELECT 1", $dbname)
    }

    AfterAll {
        $null = Get-DbaDatabase -SqlInstance $script:instance2 -Database $dbname | Remove-DbaDatabase -Confirm:$false
    }

    Context "Command actually works" {
        $results = Get-DbaDbStoredProcedure -SqlInstance $script:instance2 -Database $dbname -ExcludeSystemSp
        it "Should have standard properties" {
            $ExpectedProps = 'ComputerName,InstanceName,SqlInstance'.Split(',')
            ($results[0].PsObject.Properties.Name | Where-Object {$_ -in $ExpectedProps} | Sort-Object) | Should Be ($ExpectedProps | Sort-Object)
        }

        It "Should include test procedure: $procName" {
            ($results | Where-Object Name -eq $procName).Name | Should Be $procName
        }
        It "Should exclude system procedures" {
            ($results | Where-Object Name -eq 'sp_helpdb') | Should Be $null
        }
    }
}
tools\dbatools\tests\Get-DbaDbVirtualLogFile.Tests.ps1
<#
    The below statement stays in for every test you build.
#>
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

<#
    Unit test is required for any command added
#>
Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        <#
            The $paramCount is adjusted based on the parameters your command will have.

            The $defaultParamCount is adjusted based on what type of command you are writing the test for:
                - Commands that *do not* include SupportShouldProcess, set defaultParamCount    = 11
                - Commands that *do* include SupportShouldProcess, set defaultParamCount        = 13
        #>
        $paramCount = 6
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Get-DbaDbVirtualLogFile).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'Database', 'ExcludeDatabase', 'IncludeSystemDbs', 'EnableException'
        it "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        it "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}
# Get-DbaNoun
Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $server = Connect-DbaInstance -SqlInstance $script:instance2
        $db1 = "dbatoolsci_getvlf"
        $server.Query("CREATE DATABASE $db1")
        $needed = Get-DbaDatabase -SqlInstance $script:instance2 -Database $db1
        $setupright = $true
        if ($needed.Count -ne 1) {
            $setupright = $false
            it "has failed setup" {
                Set-TestInconclusive -message "Setup failed"
            }
        }
    }
    AfterAll {
        Remove-DbaDatabase -Confirm:$false -SqlInstance $script:instance2 -Database $db1
    }
    Context "Command actually works" {
        $results = Get-DbaDbVirtualLogFile -SqlInstance $script:instance2 -Database $db1
        It "Should have correct properties" {
            $ExpectedProps = 'ComputerName,InstanceName,SqlInstance,Database,RecoveryUnitId,FileId,FileSize,StartOffset,FSeqNo,Status,Parity,CreateLSN'.Split(',')
            ($results[0].PsObject.Properties.Name | Sort-Object) | Should Be ($ExpectedProps | Sort-Object)
        }

        It "Should have database name of $db1" {
            foreach ($result in $results) {
                $result.Database | Should Be $db1
            }
        }
    }
}
tools\dbatools\tests\Get-DbaDefaultPath.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    Context "returns proper information" {
        $results = Get-DbaDefaultPath -SqlInstance $script:instance1
        It "Data returns a value that contains :\" {
            $results.Data -match "\:\\"
        }
        It "Log returns a value that contains :\" {
            $results.Log -match "\:\\"
        }
        It "Backup returns a value that contains :\" {
            $results.Backup -match "\:\\"
        }
        It "ErrorLog returns a value that contains :\" {
            $results.ErrorLog -match "\:\\"
        }
    }
}
tools\dbatools\tests\Get-DbaDiskSpace.Tests.ps1
$commandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandName Integration Tests" -Tags "IntegrationTests" {
    Context "Disks are properly retrieved" {
        $results = Get-DbaDiskSpace -ComputerName $env:COMPUTERNAME
        It "returns at least the system drive" {
            $results.Name -contains "$env:SystemDrive\" | Should Be $true
        }

        $results = Get-DbaDiskSpace -ComputerName $env:COMPUTERNAME | Where-Object Name -eq "$env:SystemDrive\"
        It "has some valid properties" {
            $results.BlockSize -gt 0 | Should Be $true
            $results.SizeInGB -gt 0 | Should Be $true
        }
    }

    Context "CheckForSql works with mount points" {
        Mock -ModuleName 'dbatools' -CommandName 'Connect-SqlInstance' -ParameterFilter { $SqlInstance -in ('MadeUpServer', 'MadeUpServer\MadeUpInstance') } -MockWith {
            $object = [PSCustomObject] @{
                Version = @{
                    Major = 13
                }
            }
            $object | Add-Member -Name 'Query' -MemberType ScriptMethod -Value {
                return @{
                    SqlDisk = @('D:\Data\')
                }
            }
            return $object
        }

        Mock -ModuleName 'dbatools' -CommandName 'Get-DbaSqlService' -ParameterFilter { $ComputerName.ComputerName -eq 'MadeUpServer' } -MockWith {
            return @(
                @{
                    ComputerName = 'MadeUpServer'
                    ServiceName = 'MSSQLSERVER'
                    ServiceType = 'Engine'
                    InstanceName = 'MSSQLSERVER'
                    DisplayName = 'SQL Server (MSSQLSERVER)'
                    StartName = 'FAKEDOMAIN\FAKEUSER'
                    State = 'Running'
                    StartMode = 'Automatic'
                },
                @{
                    ComputerName = 'MadeUpServer'
                    ServiceName = 'MSSQLSERVER$MADEUPINSTANCE'
                    ServiceType = 'Engine'
                    InstanceName = 'MadeUpInstance'
                    DisplayName = 'SQL Server (MSSQLSERVER)'
                    StartName = 'FAKEDOMAIN\FAKEUSER'
                    State = 'Running'
                    StartMode = 'Automatic'
                }
            )
        }

        Mock -ModuleName 'dbatools' -CommandName 'Get-DbaCmObject' -ParameterFilter { $ComputerName::InputObject -eq 'MadeUpServer' -and $Query -like '*Win32_Volume*' } -MockWith {
            return @(
                @{
                    Name = 'D:\Data\'
                    Label = 'Log'
                    Capacity = 32209043456
                    Freespace = 11653545984
                    BlockSize = 65536
                    FileSystem = 'NTFS'
                    DriveType = 3
                    DriveLetter = ''
                },
                @{
                    Name = 'C:\'
                    Label = 'OS'
                    Capacity = 32209043456
                    Freespace = 11653545984
                    BlockSize = 4096
                    FileSystem = 'NTFS'
                    DriveType = 2
                    DriveLetter = 'C:'
                }
            )
        }
        
        It -Skip "SQL Server drive is found in there somewhere" {
            $results = Get-DbaDiskSpace -ComputerName 'MadeUpServer' -CheckForSql -EnableException
            $true | Should -BeIn $results.IsSqlDisk
        }

        Assert-MockCalled -ModuleName 'dbatools' -CommandName 'Get-DbaCmObject' -Times 0
        Assert-MockCalled -ModuleName 'dbatools' -CommandName 'Get-DbaSqlService' -Times 0
        Assert-MockCalled -ModuleName 'dbatools' -CommandName 'Connect-SqlInstance' -Times 0
    }

    Context "CheckForSql returns IsSqlDisk property with a value (likely false)" {
        It -Skip "SQL Server drive is not found in there somewhere" {
            $results = Get-DbaDiskSpace -ComputerName $env:COMPUTERNAME -CheckForSql -WarningAction SilentlyContinue
            $false | Should BeIn $results.IsSqlDisk
        }
    }
}
tools\dbatools\tests\Get-DbaDistributor.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "ensuring accuracy of results" {
        $results = Get-DbaDistributor -SqlInstance $script:instance1
        It "accurately reports that the distributor is not installed" {
            $results.DistributorInstalled | Should Be $false
        }
    }
}
tools\dbatools\tests\Get-DbaDump.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

# Not sure what is up with appveyor but it does not support this at all
if (-not $env:appveyor) {
    Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
        Context "Testing if memory dump is present" {
            BeforeAll {
                $server = Connect-DbaInstance -SqlInstance $script:instance1
                $server.Query("DBCC STACKDUMP")
            }

            $results = Get-DbaDump -SqlInstance $script:instance1
            It "finds least one dump" {
                ($results).Count -ge 1 | Should Be $true
            }
        }
    }
}
tools\dbatools\tests\Get-DbaErrorLog.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    Context "Correctly gets error log messages" {
        $sourceFilter = "Logon"
        $textFilter = "All rights reserved"
        BeforeAll {
            $login = 'DaperDan'
            $l = Get-DbaLogin -SqlInstance $script:instance1 -Login $login
            if ($l) {
                Get-DbaProcess -SqlInstance $instance -Login $login | Stop-DbaProcess
                $l.Drop()
            }
            # (1) Cycle errorlog message: The error log has been reinitialized
            $sql = "EXEC sp_cycle_errorlog;"
            $server = Connect-DbaInstance -SqlInstance $script:instance1
            $null = $server.Query($sql)

            # (2) Need a login failure, source would be Logon
            $pwd = "p0w3rsh3llrules" | ConvertTo-SecureString -Force -AsPlainText
            $sqlCred = New-Object System.Management.Automation.PSCredential($login, $pwd)
            try {
                Connect-DbaInstance -SqlInstance $script:instance1 -Credential $sqlCred -ErrorVariable $whatever
            }
            catch {}
        }
        It "Has the correct default properties" {
            $expectedProps = 'ComputerName,InstanceName,SqlInstance,LogDate,Source,Text'.Split(',')
            $results = Get-DbaErrorLog -SqlInstance $script:instance1 -LogNumber 0
            ($results[0].PSStandardMembers.DefaultDisplayPropertySet.ReferencedPropertyNames | Sort-Object) | Should Be ($expectedProps | Sort-Object)
        }
        It "Returns filtered results for [Source = $sourceFilter]" {
            $results = Get-DbaErrorLog -SqlInstance $script:instance1 -Source $sourceFilter
            $results[0].Source | Should Be $sourceFilter
        }
        It "Returns filtered result for [LogNumber = 0] and [Source = $sourceFilter]" {
            $results = Get-DbaErrorLog -SqlInstance $script:instance1 -LogNumber 0 -Source $sourceFilter
            $results[0].Source | Should Be $sourceFilter
        }
        It "Returns filtered results for [Text = $textFilter]" {
            $results = Get-DbaErrorLog -SqlInstance $script:instance1 -Text $textFilter
            {$results[0].Text -like "*$textFilter*"} | Should Be $true
        }
        It "Returns filtered result for [LogNumber = 0] and [Text = $textFilter]" {
            $results = Get-DbaErrorLog -SqlInstance $script:instance1 -LogNumber 0 -Text $textFilter
            {$results[0].Text -like "*$textFilter"} | Should Be $true
        }
        $after = Get-DbaErrorLog -SqlInstance $script:instance1 -LogNumber 1 | Select-Object -First 1
        $before = Get-DbaErrorLog -SqlInstance $script:instance1 -LogNumber 1 | Select-Object -Last 1

        $afterFilter = $after.LogDate.AddMinutes(+1)
        It "Returns filtered results for [After = $afterFilter" {
            $results = Get-DbaErrorLog -SqlInstance $script:instance1 -After $afterFilter
            {$results[0].LogDate -ge $afterFilter} | Should Be $true
        }
        It "Returns filtered results for [LogNumber = 1] and [After = $afterFilter" {
            $results = Get-DbaErrorLog -SqlInstance $script:instance1 -LogNumber 1 -After $afterFilter
            {$results[0].LogDate -ge $afterFilter} | Should Be $true
        }
        $beforeFilter = $before.LogDate.AddMinutes(-1)
        It "Returns filtered result for [Before = $beforeFilter]" {
            $results = Get-DbaErrorLog -SqlInstance $script:instance1 -Before $beforeFilter
            {$results[-1].LogDate -le $beforeFilter} | Should Be $true
        }
        It "Returns filtered result for [LogNumber = 1] and [Before = $beforeFilter]" {
            $results = Get-DbaErrorLog -SqlInstance $script:instance1 -LogNumber 1 -Before $beforeFilter
            {$results[-1].LogDate -le $beforeFilter} | Should Be $true
        }
    }
}
tools\dbatools\tests\Get-DbaErrorLogConfig.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag "UnitTests" {
    Context "Validate parameters" {
        $paramCount = 3
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Get-DbaErrorLogConfig).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'EnableException'
        It "Should contain our specific parameters" {
            ((Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count) | Should -Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should -Be $paramCount
        }
    }
}

Describe "$CommandName Integration Tests" -Tag "IntegrationTests" {
    Context "Get NumberErrorLog for multiple instances" {
        $results = Get-DbaErrorLogConfig -SqlInstance $script:instance3, $script:instance2
        foreach ($result in $results) {
            It 'returns 3 values' {
                $result.LogCount | Should -Not -Be $null
                $result.LogSize | Should -Not -Be $null
                $result.LogPath | Should -Not -Be $null
            }
        }
    }
}
tools\dbatools\tests\Get-DbaFile.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Returns some files" {
        BeforeAll {
            $server = Connect-DbaInstance -SqlInstance $script:instance2
            $random = Get-Random
            $db = "dbatoolsci_getfile$random"
            $server.Query("CREATE DATABASE $db")
        }
        AfterAll {
            $null = Get-DbaDatabase -SqlInstance $script:instance2 -Database $db | Remove-DbaDatabase -Confirm:$false
        }

        $results = Get-DbaFile -SqlInstance $script:instance2
        It "Should find the new database file" {
            ($results.Filename -match 'dbatoolsci').Count -gt 0 | Should Be $true
        }

        $results = Get-DbaFile -SqlInstance $script:instance2 -Path (Get-DbaDefaultPath -SqlInstance $script:instance2).Log
        It "Should find the new database log file" {
            ($results.Filename -like '*dbatoolsci*ldf').Count -gt 0 | Should Be $true
        }

        $masterpath = $server.MasterDBPath
        $results = Get-DbaFile -SqlInstance $script:instance2 -Path $masterpath
        It "Should find the master database file" {
            $results.Filename -match 'master.mdf' | Should Be $true
        }
    }
}
tools\dbatools\tests\Get-DbaForceNetworkEncryption.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

if (-not $env:appveyor) {
    Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
        $results = Get-DbaForceNetworkEncryption $script:instance1 -EnableException

        It "returns true or false" {
            $results.ForceEncryption -ne $null
        }
    }
}
tools\dbatools\tests\Get-DbaLastBackup.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    
    BeforeAll {
        $server = Connect-DbaInstance -SqlInstance $script:instance2
        $random = Get-Random
        $dbname = "dbatoolsci_getlastbackup$random"
        $server.Query("CREATE DATABASE $dbname")
        $server.Query("ALTER DATABASE $dbname SET RECOVERY FULL WITH NO_WAIT")
        $backupdir = Join-Path $server.BackupDirectory $dbname
        if (-not (Test-Path $backupdir -PathType Container)) {
            $null = New-Item -Path $backupdir -ItemType Container
        }
    }
    
    AfterAll {
        $null = Get-DbaDatabase -SqlInstance $script:instance2 -Database $dbname | Remove-DbaDatabase -Confirm:$false
        Remove-Item -Path $backupdir -Recurse -Force -ErrorAction SilentlyContinue
    }
    
    Context "Get null history for database" {
        $results = Get-DbaLastBackup -SqlInstance $script:instance2 -Database $dbname
        It "doesn't have any values for last backups because none exist yet" {
            $results.LastFullBackup | Should Be $null
            $results.LastDiffBackup | Should Be $null
            $results.LastLogBackup | Should Be $null
        }
    }
    
    $yesterday = (Get-Date).AddDays(-1)
    $null = Get-DbaDatabase -SqlInstance $script:instance2 -Database $dbname | Backup-DbaDatabase -BackupDirectory $backupdir
    $null = Get-DbaDatabase -SqlInstance $script:instance2 -Database $dbname | Backup-DbaDatabase -BackupDirectory $backupdir -Type Differential
    $null = Get-DbaDatabase -SqlInstance $script:instance2 -Database $dbname | Backup-DbaDatabase -BackupDirectory $backupdir -Type Log
    
    Context "Get last history for single database" {
        $results = Get-DbaLastBackup -SqlInstance $script:instance2 -Database $dbname
        It "returns a date within the proper range" {
            [datetime]$results.LastFullBackup -gt $yesterday | Should Be $true
            [datetime]$results.LastDiffBackup -gt $yesterday | Should Be $true
            [datetime]$results.LastLogBackup -gt $yesterday | Should Be $true
        }
    }
    
    Context "Get last history for all databases" {
        $results = Get-DbaLastBackup -SqlInstance $script:instance2
        It "returns more than 3 databases" {
            $results.count -gt 3 | Should Be $true
        }
    }
    
    Context "Get last history for one split database" {
        It "supports multi-file backups" {
            $null = Backup-DbaDatabase -SqlInstance $script:instance2 -Database $dbname -FileCount 4
            $results = Get-DbaLastBackup -SqlInstance $script:instance2 -Database $dbname | Select-Object -First 1
            $results.LastFullBackup.GetType().Name | Should be "DbaDateTime"
        }
    }
}
tools\dbatools\tests\Get-DbaLastGoodCheckDb.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        $server = Connect-DbaInstance -SqlInstance $script:instance1 -Database master
        $server.Query("DBCC CHECKDB")
    }
    
    $results = Get-DbaLastGoodCheckDb -SqlInstance $script:instance1 -Database master
    It "LastGoodCheckDb is a valid date" {
        $results.LastGoodCheckDb -ne $null
        $results.LastGoodCheckDb -is [datetime]
    }
    
    $results = Get-DbaLastGoodCheckDb -SqlInstance $script:instance1 -WarningAction SilentlyContinue
    It "returns more than 3 results" {
        ($results).Count -gt 3
    }
}
tools\dbatools\tests\Get-DbaLogin.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag UnitTests, Get-DbaLogin {
    Context "$Command Name Input" {
        $Params = (Get-Command Get-DbaLogin).Parameters
        It "Should have a mandatory parameter SQLInstance" {
            $Params['SQLInstance'].Attributes.Mandatory | Should be $true
        }
        It "Should have Alias of ServerInstance and SqlServer for Parameter SQLInstance" {
            $params['SQLInstance'].Aliases | Should Be @('ServerInstance', 'SqlServer')
        }
        It "Should have a parameter SqlCredential" {
            $Params['SqlCredential'].Count | Should Be 1
        }
        It "Should have a parameter Login" {
            $Params['Login'].Count | Should Be 1
        }
        It "Should have a parameter IncludeFilter" {
            $Params['IncludeFilter'].Count | Should Be 1
        }
        It "Should have a parameter ExcludeLogin" {
            $Params['ExcludeLogin'].Count | Should Be 1
        }
        It "Should have a parameter ExcludeFilter" {
            $Params['ExcludeFilter'].Count | Should Be 1
        }
        It "Should have a parameter NoSystem" {
            $Params['NoSystem'].Count | Should Be 1
        }
        It "Should have a parameter SQLLogins" {
            $Params['SQLLogins'].Count | Should Be 1
        }
        It "Should have a parameter WindowsLogins" {
            $Params['WindowsLogins'].Count | Should Be 1
        }
        It "Should have a parameter HasAccess" {
            $Params['HasAccess'].Count | Should Be 1
        }
        It "Should have a parameter Locked" {
            $Params['Locked'].Count | Should Be 1
        }
        It "Should have a parameter Disabled" {
            $Params['Disabled'].Count | Should Be 1
        }
        It "Should have a parameter EnableException" {
            $Params['EnableException'].Count | Should Be 1
        }
        It "Should have a silent alias for parameter EnableException" {
            $Params['EnableException'].Aliases | Should Be 'Silent'
        }
    }
}

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Does sql instance have a SA account" {
        $results = Get-DbaLogin -SqlInstance $script:instance1 -Login sa
        It "Should report that one account named SA exists" {
            $results.Count | Should Be 1
        }
    }

    Context "Check that SA account is enabled" {
        $results = Get-DbaLogin -SqlInstance $script:instance1 -Login sa
        It "Should say the SA account is disabled FALSE" {
            $results.IsDisabled | Should Be "False"
        }
    }
}
tools\dbatools\tests\Get-DbaLogShippingError.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        $paramCount = 5
        [object[]]$params = (Get-ChildItem function:\Get-DbaAgentJobStep).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'Job', 'ExcludeJob', 'EnableException'
        It "Contains our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
    }
}

Describe "$CommandName Unittests" -Tag 'UnitTests' {
    Context "Return values" {
        It "Get the log shipping errors" {
            $Results = @()
            $Results += Get-DbaLogShippingError -SqlInstance $script:instance2
            $Results.Count | Should Be 0
        }
    }
}
tools\dbatools\tests\Get-DbaMaxMemory.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Connects to multiple instances" {
        It 'Returns multiple objects' {
            $results = Get-DbaMaxMemory -SqlInstance $script:instance1, $script:instance2
            $results.Count | Should BeGreaterThan 1 # and ultimately not throw an exception
        }
        It 'Returns the right amount of MB' {
            $null = Set-DbaMaxMemory -SqlInstance $script:instance1, $script:instance2 -MaxMB 1024
            $results = Get-DbaMaxMemory -SqlInstance $script:instance1
            $results.SqlMaxMB | Should Be 1024
        }
    }
}

Describe "$commandname Unit Test" -Tags Unittest {
    InModuleScope dbatools {
        Context 'Validate input arguments' {
            It 'SqlInstance parameter is empty' {
                Mock Connect-SqlInstance { throw System.Data.SqlClient.SqlException }
                { Get-DbaMaxMemory -SqlInstance '' -WarningAction Stop 3> $null } | Should Throw
            }

            It 'SqlInstance parameter host cannot be found' {
                Mock Connect-SqlInstance { throw System.Data.SqlClient.SqlException }
                { Get-DbaMaxMemory -SqlInstance 'ABC' -WarningAction Stop 3> $null } | Should Throw
            }
        }

        Context 'Validate functionality ' {
            It 'Server SqlInstance reported correctly' {
                Mock Connect-SqlInstance {
                    return @{
                        DomainInstanceName = 'ABC'
                    }
                }

                (Get-DbaMaxMemory -SqlInstance 'ABC').SqlInstance | Should be 'ABC'
            }

            It 'Server under-report by 1MB the memory installed on the host' {
                Mock Connect-SqlInstance {
                    return @{
                        PhysicalMemory = 1023
                    }
                }

                (Get-DbaMaxMemory -SqlInstance 'ABC').TotalMB | Should be 1024
            }

            It 'Server reports correctly the memory installed on the host' {
                Mock Connect-SqlInstance {
                    return @{
                        PhysicalMemory = 1024
                    }
                }

                (Get-DbaMaxMemory -SqlInstance 'ABC').TotalMB | Should be 1024
            }

            It 'Memory allocated to SQL Server instance reported' {
                Mock Connect-SqlInstance {
                    return @{
                        Configuration = @{
                            MaxServerMemory = @{
                                ConfigValue = 2147483647
                            }
                        }
                    }
                }

                (Get-DbaMaxMemory -SqlInstance 'ABC').SqlMaxMB | Should be 2147483647
            }
        }
    }
}
tools\dbatools\tests\Get-DbaOpenTransaction.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    It "doesn't throw" {
        { Get-DbaOpenTransaction -SqlInstance $script:instance1 } | Should Not Throw
    }
}
tools\dbatools\tests\Get-DbaOperatingSystem.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"
Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        $paramCount = 3
        $commonParamCount = ([System.Management.Automation.PSCmdlet]::CommonParameters).Count
        [object[]]$params = (Get-ChildItem function:\Get-DbaOperatingSystem).Parameters.Keys
        $knownParameters = 'ComputerName', 'Credential', 'EnableException'
        It "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $commonParamCount | Should Be $paramCount
        }
    }
    Context "Validate input" {
        It "Cannot resolve hostname of computer" {
            mock Resolve-DbaNetworkName {$null}
            {Get-DbaOperatingSystem -ComputerName 'DoesNotExist142' -WarningAction Stop 3> $null} | Should Throw
        }
    }
}
Describe "Get-DbaOperatingSystem Integration Test" -Tag "IntegrationTests" {
    $result = Get-DbaOperatingSystem -ComputerName $script:instance1

    $props = 'ComputerName', 'Manufacturer', 'Organization',
    'Architecture', 'Build', 'Version', 'InstallDate', 'LastBootTime', 'LocalDateTime',
    'BootDevice', 'TimeZone', 'TimeZoneDaylight', 'TimeZoneStandard', 'TotalVisibleMemory'
    <#
        FreePhysicalMemory: units = KB
        FreeVirtualMemory: units = KB
        TimeZoneStandard: StandardName from win32_timezone
        TimeZoneDaylight: DaylightName from win32_timezone
        TimeZone: Caption from win32_timezone
    #>
    Context "Validate output" {
        foreach ($prop in $props) {
            $p = $result.PSObject.Properties[$prop]
            It "Should return property: $prop" {
                $p.Name | Should Be $prop
            }
        }
        It "Should return nothing if unable to connect to server" {
            $result = Get-DbaOperatingSystem -ComputerName 'Melton5312' -WarningAction SilentlyContinue
            $result | Should Be $null
        }
    }
}
tools\dbatools\tests\Get-DbaOrphanUser.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        $paramCount = 5
        $commonParamCount = ([System.Management.Automation.PSCmdlet]::CommonParameters).Count
        [object[]]$params = (Get-ChildItem function:\Get-DbaOrphanUser).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'Database', 'ExcludeDatabase', 'EnableException'
        It "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $commonParamCount | Should Be $paramCount
        }
    }
}


Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        $loginsq = @'
CREATE LOGIN [dbatoolsci_orphan1] WITH PASSWORD = N'password1', CHECK_EXPIRATION = OFF, CHECK_POLICY = OFF;
CREATE LOGIN [dbatoolsci_orphan2] WITH PASSWORD = N'password2', CHECK_EXPIRATION = OFF, CHECK_POLICY = OFF;
CREATE LOGIN [dbatoolsci_orphan3] WITH PASSWORD = N'password3', CHECK_EXPIRATION = OFF, CHECK_POLICY = OFF;
CREATE DATABASE dbatoolsci_orphan;
'@
        $server = Connect-DbaInstance -SqlInstance $script:instance1
        $null = Remove-DbaLogin -SqlInstance $server -Login dbatoolsci_orphan1, dbatoolsci_orphan2, dbatoolsci_orphan3 -Force -Confirm:$false
        $null = Remove-DbaDatabase -SqlInstance $server -Database dbatoolsci_orphan -Confirm:$false
        $null = Invoke-DbaSqlQuery -SqlInstance $server -Query $loginsq
        $usersq = @'
CREATE USER [dbatoolsci_orphan1] FROM LOGIN [dbatoolsci_orphan1];
CREATE USER [dbatoolsci_orphan2] FROM LOGIN [dbatoolsci_orphan2];
CREATE USER [dbatoolsci_orphan3] FROM LOGIN [dbatoolsci_orphan3];
'@
        Invoke-DbaSqlQuery -SqlInstance $server -Query $usersq -Database dbatoolsci_orphan
        $dropOrphan = "DROP LOGIN [dbatoolsci_orphan1];DROP LOGIN [dbatoolsci_orphan2];"
        Invoke-DbaSqlQuery -SqlInstance $server -Query $dropOrphan
    }
    AfterAll {
        $server = Connect-DbaInstance -SqlInstance $script:instance1
        $null = Remove-DbaLogin -SqlInstance $server -Login dbatoolsci_orphan1, dbatoolsci_orphan2, dbatoolsci_orphan3 -Force -Confirm:$false
        $null = Remove-DbaDatabase -SqlInstance $server -Database dbatoolsci_orphan -Confirm:$false
    }
    It "shows time taken for preparation" {
        1 | Should -Be 1
    }
    $results = Get-DbaOrphanUser -SqlInstance $script:instance1 -Database dbatoolsci_orphan
    It "Finds two orphans" {
        $results.Count | Should -Be 2
        foreach ($user in $Users) {
            $user.User | Should -BeIn @('dbatoolsci_orphan1', 'dbatoolsci_orphan2')
            $user.DatabaseName | Should -Be 'dbatoolsci_orphan'
        }
    }
    It "has the correct properties" {
        $result = $results[0]
        $ExpectedProps = 'ComputerName,InstanceName,SqlInstance,DatabaseName,User'.Split(',')
        ($result.PsObject.Properties.Name | Sort-Object) | Should Be ($ExpectedProps | Sort-Object)
    }
}

tools\dbatools\tests\Get-DbaPermission.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "Get-DbaPermission Unit Tests" -Tags "UnitTests" {
    InModuleScope dbatools {
        Context "Validate parameters" {
            $params = (Get-ChildItem function:\Get-DbaPermission).Parameters
            it "should have a parameter named SqlInstance" {
                $params.ContainsKey("SqlInstance") | Should Be $true
            }
            it "should have a parameter named SqlCredential" {
                $params.ContainsKey("SqlCredential") | Should Be $true
            }
            it "should have a parameter named EnableException" {
                $params.ContainsKey("EnableException") | Should Be $true
            }
        }
    }
}

Describe "Get-DbaPermission Integration Tests" -Tag "IntegrationTests" {
    Context "parameters work" {
        it "returns server level permissions with -IncludeServerLevel" {
            $results = Get-DbaPermission -SqlInstance $script:instance2 -IncludeServerLevel
            $results.where( {$_.Database -eq ''}).count | Should BeGreaterThan 0
        }
        it "returns no server level permissions without -IncludeServerLevel" {
            $results = Get-DbaPermission -SqlInstance $script:instance2
            $results.where( {$_.Database -eq ''}).count | Should Be 0
        }
        it "returns no system object permissions with -NoSystemObjects" {
            $results = Get-DbaPermission -SqlInstance $script:instance2 -NoSystemObjects
            $results.where( {$_.securable -like 'sys.*'}).count | Should Be 0
        }
        it "returns system object permissions without -NoSystemObjects" {
            $results = Get-DbaPermission -SqlInstance $script:instance2
            $results.where( {$_.securable -like 'sys.*'}).count | Should BeGreaterThan 0
        }
    }
    Context "Validate input" {
        it "Cannot resolve hostname of computer" {
            mock Resolve-DbaNetworkName {$null}
            {Get-DbaComputerSystem -ComputerName 'DoesNotExist142' -WarningAction Stop 3> $null} | Should Throw
        }
    }
}
tools\dbatools\tests\Get-DbaPfAvailableCounter.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    BeforeEach {
        $null = Get-DbaPfDataCollectorSetTemplate -Template 'Long Running Queries' | Import-DbaPfDataCollectorSetTemplate
    }
    AfterAll {
        $null = Get-DbaPfDataCollectorSet -CollectorSet 'Long Running Queries' | Remove-DbaPfDataCollectorSet -Confirm:$false
    }
    Context "Verifying command returns all the required results" {
        It "returns the correct values" {
            $results = Get-DbaPfAvailableCounter
            $results.Count -gt 1000 | Should Be $true
        }
        It "returns are pipable into Add-DbaPfDataCollectorCounter" {
            $results = Get-DbaPfAvailableCounter -Pattern *sql* | Select-Object -First 3 | Add-DbaPfDataCollectorCounter -CollectorSet 'Long Running Queries' -Collector DataCollector01 -WarningAction SilentlyContinue
            foreach ($result in $results) {
                $result.Name -match "sql" | Should Be $true
            }
        }
    }
}
tools\dbatools\tests\Get-DbaPfDataCollector.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    Context "Verifying command works" {
        It "returns a result with the right computername and name is not null" {
            $results = Get-DbaPfDataCollector | Select-Object -First 1
            $results.ComputerName | Should Be $env:COMPUTERNAME
            $results.Name | Should Not Be $null
        }
    }
}
tools\dbatools\tests\Get-DbaPfDataCollectorCounter.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    Context "Verifying command works" {
        It "returns a result with the right computername and name is not null" {
            $results = Get-DbaPfDataCollectorCounter | Select-Object -First 1
            $results.ComputerName | Should Be $env:COMPUTERNAME
            $results.Name | Should Not Be $null
        }
    }
}
tools\dbatools\tests\Get-DbaPfDataCollectorCounterSample.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    Context "Verifying command works" {
        It "returns a result with the right computername and name is not null" {
            $results = Get-DbaPfDataCollectorCounterSample | Select-Object -First 1
            $results.ComputerName | Should Be $env:COMPUTERNAME
            $results.Name | Should Not Be $null
        }
    }
}
tools\dbatools\tests\Get-DbaPfDataCollectorSet.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    Context "Verifying command works" {
        It "returns a result with the right computername and name is not null" {
            $results = Get-DbaPfDataCollectorSet | Select-Object -First 1
            $results.ComputerName | Should Be $env:COMPUTERNAME
            $results.Name | Should Not Be $null
        }
    }
}
tools\dbatools\tests\Get-DbaPfDataCollectorSetTemplate.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    Context "Verifying command returns all the required results" {
        It "returns not null values for required fields" {
            $results = Get-DbaPfDataCollectorSetTemplate
            foreach ($result in $results) {
                $result.Name | Should Not Be $null
                $result.Source | Should Not Be $null
                $result.Description | Should Not Be $null
            }
        }
        
        It "returns only one (and the proper) template" {
            $results = Get-DbaPfDataCollectorSetTemplate -Template 'Long Running Queries'
            $results.Name | Should Be 'Long Running Queries'
        }
    }
}
tools\dbatools\tests\Get-DbaPlanCache.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    Context "returns proper information" {
        It "returns correct datatypes" {
            $results = Get-DbaPlanCache -SqlInstance $script:instance1 | Clear-DbaPlanCache -Threshold 1024
            $results.Size -is [dbasize] | Should -Be $true
        }
    }
}
tools\dbatools\tests\Get-DbaPolicy.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Setup" {
        BeforeAll {
            $sqlconditionid = "DECLARE @condition_id INT
                EXEC msdb.dbo.sp_syspolicy_add_condition @name=N'dbatoolsci_Condition', @description=N'', @facet=N'ApplicationRole', @expression=N'<Operator>
                  <TypeClass>Bool</TypeClass>
                  <OpType>EQ</OpType>
                  <Count>2</Count>
                  <Attribute>
                    <TypeClass>DateTime</TypeClass>
                    <Name>DateLastModified</Name>
                  </Attribute>
                  <Function>
                    <TypeClass>DateTime</TypeClass>
                    <FunctionType>DateTime</FunctionType>
                    <ReturnType>DateTime</ReturnType>
                    <Count>1</Count>
                    <Constant>
                      <TypeClass>String</TypeClass>
                      <ObjType>System.String</ObjType>
                      <Value>2016-05-03T00:00:00.0000000</Value>
                    </Constant>
                  </Function>
                </Operator>', @is_name_condition=0, @obj_name=N'', @condition_id=@condition_id OUTPUT
                SELECT @condition_id"

            $sqlobjectsetid = "DECLARE @object_set_id INT
            EXEC msdb.dbo.sp_syspolicy_add_object_set @object_set_name=N'dbatoolsci_TestPolicy_ObjectSet', @facet=N'ApplicationRole', @object_set_id=@object_set_id OUTPUT
            SELECT @object_set_id"

            $sqlpolicyid = "DECLARE @policy_id INT
            EXEC msdb.dbo.sp_syspolicy_add_policy @name=N'dbatoolsci_TestPolicy', @condition_name=N'dbatoolsci_Condition', @policy_category=N'', @description=N'', @help_text=N'', @help_link=N'', @schedule_uid=N'00000000-0000-0000-0000-000000000000', @execution_mode=2, @is_enabled=True, @policy_id=@policy_id OUTPUT, @root_condition_name=N'', @object_set=N'dbatoolsci_TestPolicy_ObjectSet'
            SELECT @policy_id"

            $server = Connect-DbaInstance -SqlInstance $script:instance2
            $conditionid = $server.ConnectionContext.ExecuteScalar($sqlconditionid)
            $objectsetid = $server.ConnectionContext.ExecuteScalar($sqlobjectsetid)
            $policyid = $server.ConnectionContext.ExecuteScalar($sqlpolicyid)

        }
        AfterAll {
            $server.Query("EXEC msdb.dbo.sp_syspolicy_delete_policy @policy_id=$policyid")
            $server.Query("EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_id=$objectsetid")
            $server.Query("EXEC msdb.dbo.sp_syspolicy_delete_condition @condition_id=$conditionid")
        }

        $results = Get-DbaPolicy -SqlInstance $script:instance2

        It "returns the test policy" {
            $results.Name -contains 'dbatoolsci_TestPolicy' | Should Be $true
        }

        $results = Get-DbaPolicy -SqlInstance $script:instance2 -Policy dbatoolsci_TestPolicy

        It "returns only the test policy named dbatoolsci_TestPolicy" {
            $results.Name -eq 'dbatoolsci_TestPolicy' | Should Be $true
        }

        It "returns a policy with a condition named dbatoolsci_Condition" {
            $results.Condition -eq 'dbatoolsci_Condition' | Should Be $true
        }
    }
}
tools\dbatools\tests\Get-DbaProcess.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Testing Get-DbaProcess results" {
        $results = Get-DbaProcess -SqlInstance $script:instance1

        It "matches self as a login at least once" {
            $matching = $results | Where-Object Login -match $env:username
            $matching.Length | Should BeGreaterThan 0
        }

        $results = Get-DbaProcess -SqlInstance $script:instance1 -Program 'dbatools PowerShell module - dbatools.io'

        foreach ($result in $results) {
            It "returns only dbatools processes" {
                $result.Program -eq 'dbatools PowerShell module - dbatools.io' | Should Be $true
            }
        }

        $results = Get-DbaProcess -SqlInstance $script:instance1 -Database master

        foreach ($result in $results) {
            It "returns only processes from master database" {
                $result.Database -eq 'master' | Should Be $true
            }
        }
    }
}
tools\dbatools\tests\Get-DbaRegisteredServer.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tag "IntegrationTests" {
    Context "Setup" {
        BeforeAll {
            $server = Connect-DbaInstance $script:instance1
            $regStore = New-Object Microsoft.SqlServer.Management.RegisteredServers.RegisteredServersStore($server.ConnectionContext.SqlConnectionObject)
            $dbStore = $regStore.DatabaseEngineServerGroup
            
            $srvName = "dbatoolsci-server1"
            $group = "dbatoolsci-group1"
            $regSrvName = "dbatoolsci-server12"
            $regSrvDesc = "dbatoolsci-server123"
            
            <# Create that first group #>
            $newGroup = New-Object Microsoft.SqlServer.Management.RegisteredServers.ServerGroup($dbStore, $group)
            $newGroup.Create()
            $dbStore.Refresh()
            
            $groupStore = $dbStore.ServerGroups[$group]
            $newServer = New-Object Microsoft.SqlServer.Management.RegisteredServers.RegisteredServer($groupStore, $regSrvName)
            $newServer.ServerName = $srvName
            $newServer.Description = $regSrvDesc
            $newServer.Create()
            
            <# Create the sub-group #>
            $srvName2 = "dbatoolsci-server2"
            $group2 = "dbatoolsci-group1a"
            $regSrvName2 = "dbatoolsci-server21"
            $regSrvDesc2 = "dbatoolsci-server321"
            
            $newGroup2 = New-Object Microsoft.SqlServer.Management.RegisteredServers.ServerGroup($groupStore, $group2)
            $newGroup2.Create()
            $dbStore.Refresh()
            
            $groupStore2 = $dbStore.ServerGroups[$group].ServerGroups[$group2]
            $newServer2 = New-Object Microsoft.SqlServer.Management.RegisteredServers.RegisteredServer($groupStore2, $regSrvName2)
            $newServer2.ServerName = $srvName2
            $newServer2.Description = $regSrvDesc2
            $newServer2.Create()
            
            $regSrvName3 = "dbatoolsci-server3"
            $srvName3 = "dbatoolsci-server3"
            $regSrvDesc3 = "dbatoolsci-server3desc"
            $newServer3 = New-Object Microsoft.SqlServer.Management.RegisteredServers.RegisteredServer($dbStore, $regSrvName3)
            $newServer3.ServerName = $srvName3
            $newServer3.Description = $regSrvDesc3
            $newServer3.Create()
        }
        AfterAll {
            Get-DbaRegisteredServer -SqlInstance $script:instance1 | Where-Object Name -match dbatoolsci | Remove-DbaRegisteredServer -Confirm:$false
            Get-DbaRegisteredServerGroup -SqlInstance $script:instance1 | Where-Object Name -match dbatoolsci | Remove-DbaRegisteredServerGroup -Confirm:$false
        }
        
        It "Should return multiple objects" {
            $results = Get-DbaRegisteredServer -SqlInstance $script:instance1 -Group $group
            $results.Count | Should Be 2
        }
        It "Should allow searching subgroups" {
            $results = Get-DbaRegisteredServer -SqlInstance $script:instance1 -Group "$group\$group2"
            $results.Count | Should Be 1
        }
        It "Should return the root server when excluding (see #3529)" {
            $results = Get-DbaRegisteredServer -SqlInstance $script:instance1 -ExcludeGroup "$group\$group2"
            @($results | Where-Object Name -eq $srvName3).Count | Should -Be 1
        }
        
        # Property Comparisons will come later when we have the commands
    }
}
tools\dbatools\tests\Get-DbaRegisteredServerGroup.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    Context "Setup" {
        BeforeAll {
            $server = Connect-DbaInstance $script:instance1
            $regStore = New-Object Microsoft.SqlServer.Management.RegisteredServers.RegisteredServersStore($server.ConnectionContext.SqlConnectionObject)
            $dbStore = $regStore.DatabaseEngineServerGroup
            
            $srvName = "dbatoolsci-server1"
            $group = "dbatoolsci-group1"
            $regSrvName = "dbatoolsci-server12"
            $regSrvDesc = "dbatoolsci-server123"
            
            <# Create that first group #>
            $newGroup = New-Object Microsoft.SqlServer.Management.RegisteredServers.ServerGroup($dbStore, $group)
            $newGroup.Create()
            $dbStore.Refresh()
            
            $groupStore = $dbStore.ServerGroups[$group]
            $newServer = New-Object Microsoft.SqlServer.Management.RegisteredServers.RegisteredServer($groupStore, $regSrvName)
            $newServer.ServerName = $srvName
            $newServer.Description = $regSrvDesc
            $newServer.Create()
            
            <# Create the sub-group #>
            $srvName2 = "dbatoolsci-server2"
            $group2 = "dbatoolsci-group1a"
            $regSrvName2 = "dbatoolsci-server21"
            $regSrvDesc2 = "dbatoolsci-server321"
            
            $newGroup2 = New-Object Microsoft.SqlServer.Management.RegisteredServers.ServerGroup($groupStore, $group2)
            $newGroup2.Create()
            $dbStore.Refresh()
            
            $groupStore2 = $dbStore.ServerGroups[$group].ServerGroups[$group2]
            $newServer2 = New-Object Microsoft.SqlServer.Management.RegisteredServers.RegisteredServer($groupStore2, $regSrvName2)
            $newServer2.ServerName = $srvName2
            $newServer2.Description = $regSrvDesc2
            $newServer2.Create()
        }
        AfterAll {
            Get-DbaRegisteredServer -SqlInstance $script:instance1 | Where-Object Name -match dbatoolsci | Remove-DbaRegisteredServer -Confirm:$false
            Get-DbaRegisteredServerGroup -SqlInstance $script:instance1 | Where-Object Name -match dbatoolsci | Remove-DbaRegisteredServerGroup -Confirm:$false
        }
        
        It "Should return one group" {
            $results = Get-DbaRegisteredServerGroup -SqlInstance $script:instance1 -Group $group
            $results.Count | Should Be 1
        }
        It "Should allow searching subgroups" {
            $results = Get-DbaRegisteredServerGroup -SqlInstance $script:instance1 -Group "$group\$group2"
            $results.Count | Should Be 1
        }
        
        # Property Comparisons will come later when we have the commands
    }
}
tools\dbatools\tests\Get-DbaRegisteredServerStore.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Components are properly retreived" {
        It "Should return the right values" {
            $results = Get-DbaRegisteredServerStore -SqlInstance $script:instance2
            $results.InstanceName | Should -Not -Be $null
            $results.DisplayName | Should -Be "Central Management Servers"
        }
    }
}
tools\dbatools\tests\Get-DbaResourceGovernorClassifierFunction.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        $sql = "CREATE FUNCTION dbatoolsci_fnRG()
                RETURNS sysname
                WITH SCHEMABINDING
                AS
                BEGIN
                     RETURN N'gOffHoursProcessing'
                END"

        Invoke-DbaSqlQuery -SqlInstance $script:instance2 -Query $sql
        Invoke-DbaSqlQuery -SqlInstance $script:instance2 -Query "ALTER RESOURCE GOVERNOR with (CLASSIFIER_FUNCTION = dbo.dbatoolsci_fnRG); ALTER RESOURCE GOVERNOR RECONFIGURE"
    }
    AfterAll {
        Invoke-DbaSqlQuery -SqlInstance $script:instance2 -Query "ALTER RESOURCE GOVERNOR WITH (CLASSIFIER_FUNCTION = NULL); ALTER RESOURCE GOVERNOR RECONFIGURE"
        Invoke-DbaSqlQuery -SqlInstance $script:instance2 -Query "DROP FUNCTION [dbo].[dbatoolsci_fnRG]"
    }

    Context "Command works" {
        It "returns the proper classifier function" {
            $results = Get-DbaResourceGovernorClassifierFunction -SqlInstance $script:instance2
            $results.Name | Should -Be 'dbatoolsci_fnRG'
        }
    }
}
tools\dbatools\tests\Get-DbaRestoreHistory.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        $paramCount = 8
        $commonParamCount = ([System.Management.Automation.PSCmdlet]::CommonParameters).Count
        [object[]]$params = (Get-ChildItem function:\Get-DbaRestoreHistory).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'Database', 'ExcludeDatabase', 'Since', 'Last', 'Force', 'EnableException'
        It "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $commonParamCount | Should Be $paramCount
        }
    }

}
Describe "$commandname Integration Tests" -Tags "IntegrationTests" {

    BeforeAll {
        $DestBackupDir = 'C:\Temp\backups'
        if (-Not(Test-Path $DestBackupDir)) {
            New-Item -Type Container -Path $DestBackupDir
        }
        $random = Get-Random
        $dbname1 = "dbatoolsci_restorehistory1_$random"
        $dbname2 = "dbatoolsci_restorehistory2_$random"
        $null = Get-DbaDatabase -SqlInstance $script:instance2 -Database $dbname1, $dbname2 | Remove-DbaDatabase -Confirm:$false
        $null = Restore-DbaDatabase -SqlInstance $script:instance2 -Path $script:appveyorlabrepo\singlerestore\singlerestore.bak -DatabaseName $dbname1 -DestinationFilePrefix $dbname1
        $null = Restore-DbaDatabase -SqlInstance $script:instance2 -Path $script:appveyorlabrepo\singlerestore\singlerestore.bak -DatabaseName $dbname2 -DestinationFilePrefix $dbname2
        $null = Restore-DbaDatabase -SqlInstance $script:instance2 -Path $script:appveyorlabrepo\singlerestore\singlerestore.bak -DatabaseName $dbname2 -DestinationFilePrefix "rsh_pre_$dbname2" -WithReplace
    }

    AfterAll {
        $null = Get-DbaDatabase -SqlInstance $script:instance1 -Database $dbname1, $dbname2 | Remove-DbaDatabase -Confirm:$false
    }
    Context "Preparation" {
        It "Should have prepared" {
            1 | Should -Be 1
        }
    }
    Context "Get last restore history for single database" {
        $results = @(Get-DbaRestoreHistory -SqlInstance $script:instance2 -Database $dbname2 -Last)
        It "Results holds 1 object" {
            $results.count | Should -Be 1
        }
        It "Should return the full restore with the correct properties" {
            $results[0].RestoreType | Should -Be "Database"
            $results[0].From | Should -Be $script:appveyorlabrepo\singlerestore\singlerestore.bak
            $results[0].To | Should -Match "\\rsh_pre_$dbname2"
        }
    }
    Context "Get last restore history for multiple database" {
        $results = @(Get-DbaRestoreHistory -SqlInstance $script:instance2 -Database $dbname1, $dbname2 -Last)
        It "Results holds 2 objects" {
            $results.count | Should -Be 2
        }
        It "Should return the full restore with the correct properties" {
            $results[0].RestoreType | Should -Be "Database"
            $results[1].RestoreType | Should -Be "Database"
            $results[0].From | Should -Be $script:appveyorlabrepo\singlerestore\singlerestore.bak
            $results[1].From | Should -Be $script:appveyorlabrepo\singlerestore\singlerestore.bak
            ($results | Where-Object Database -eq $dbname1).To | Should -Match "\\$dbname1"
            ($results | Where-Object Database -eq $dbname2).To | Should -Match "\\rsh_pre_$dbname2"
        }
    }
    Context "Get complete restore history for multiple database" {
        $results = @(Get-DbaRestoreHistory -SqlInstance $script:instance2 -Database $dbname1, $dbname2)
        It "Results holds 3 objects" {
            $results.Count | Should -Be 3
        }
        It "Should return the full restore with the correct properties" {
            @($results | Where-Object Database -eq $dbname1).Count | Should -Be 1
            @($results | Where-Object Database -eq $dbname2).Count | Should -Be 2
        }
    }
    Context "return object properties" {
        It "has the correct properties" {
            $results = Get-DbaRestoreHistory -SqlInstance $script:instance2 -Database $dbname1, $dbname2
            $result = $results[0]
            $ExpectedProps = 'ComputerName,InstanceName,SqlInstance,Database,Username,RestoreType,Date,From,To,first_lsn,last_lsn,checkpoint_lsn,database_backup_lsn,backup_finish_date,BackupFinishDate,RowError,RowState,Table,ItemArray,HasErrors'.Split(',')
            ($result.PsObject.Properties.Name | Sort-Object) | Should Be ($ExpectedProps | Sort-Object)
            $ExpectedPropsDefault = 'ComputerName,InstanceName,SqlInstance,Database,Username,RestoreType,Date,From,To,BackupFinishDate'.Split(',')
            ($result.PSStandardMembers.DefaultDisplayPropertySet.ReferencedPropertyNames | Sort-Object) | Should Be ($ExpectedPropsDefault | Sort-Object)
        }
    }
}
tools\dbatools\tests\Get-DbaSchemaChangeHistory.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Testing if schema changes are discovered" {
        BeforeAll {
            $db = Get-DbaDatabase -SqlInstance $script:instance1 -Database tempdb
            $db.Query("CREATE TABLE dbatoolsci_schemachange (id int identity)")
            $db.Query("EXEC sp_rename 'dbatoolsci_schemachange', 'dbatoolsci_schemachange1'")
        }
        AfterAll {
            $db.Query("DROP TABLE dbo.dbatoolsci_schemachange1")
        }

        $results = Get-DbaSchemaChangeHistory -SqlInstance $script:instance1 -Database tempdb

        It "notices dbatoolsci_schemachange changed" {
            $results.Object -match 'dbatoolsci_schemachange' | Should Be $true
        }
    }
}
tools\dbatools\tests\Get-DbaServerAudit.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        $server = Connect-DbaInstance -SqlInstance $script:instance2
        $sql = "CREATE SERVER AUDIT LoginAudit
                TO FILE (FILEPATH = N'C:\temp',MAXSIZE = 10 MB,MAX_ROLLOVER_FILES = 1,RESERVE_DISK_SPACE = OFF)
                WITH (QUEUE_DELAY = 1000, ON_FAILURE = CONTINUE)

                CREATE SERVER AUDIT SPECIFICATION TrackAllLogins
                FOR SERVER AUDIT LoginAudit ADD (SUCCESSFUL_LOGIN_GROUP) WITH (STATE = ON)

                ALTER SERVER AUDIT LoginAudit WITH (STATE = ON)"
        $server.Query($sql)
    }
    AfterAll {
        $sql = "ALTER SERVER AUDIT SPECIFICATION TrackAllLogins WITH (STATE = OFF)
                ALTER SERVER AUDIT LoginAudit WITH (STATE = OFF)
                DROP SERVER AUDIT SPECIFICATION TrackAllLogins
                DROP SERVER AUDIT LoginAudit"
        $server.Query($sql)
    }
    Context "Verifying command output" {
        It "returns some results" {
            $results = Get-DbaServerAudit -SqlInstance $script:instance2
            $results | Should -Not -Be $null
        }
        It "returns some results" {
            $results = Get-DbaServerAudit -SqlInstance $script:instance2 -Audit LoginAudit
            $results.Name | Should -Be 'LoginAudit'
            $results.Enabled | Should -Be $true
        }
    }
}
tools\dbatools\tests\Get-DbaServerProtocol.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {

    Context "Command actually works" {
        $results = Get-DbaServerProtocol -ComputerName $script:instance1, $script:instance2

        It "shows some services" {
            $results.DisplayName | Should Not Be $null
        }

        $results = $results | Where-Object Name -eq Tcp
        It "can get TCPIP" {
            foreach ($result in $results) {
                $result.Name -eq "Tcp" | Should Be $true
            }
        }
    }
}
tools\dbatools\tests\Get-DbaServerRole.Tests.ps1
<#
    The below statement stays in for every test you build.
#>
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

<#
    Unit test is required for any command added
#>
Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        <#
            The $paramCount is adjusted based on the parameters your command will have.

            The $defaultParamCount is adjusted based on what type of command you are writing the test for:
                - Commands that *do not* include SupportShouldProcess, set defaultParamCount    = 11
                - Commands that *do* include SupportShouldProcess, set defaultParamCount        = 13
        #>
        $paramCount = 6
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Get-DbaServerRole).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'EnableException', 'ServerRole', 'ExcludeServerRole', 'ExcludeFixedRole'
        It "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
    Context "Input validation" {
        BeforeAll {
            Mock Stop-Function { } -ModuleName dbatools
        }
        It "Should Call Stop-Function if instance does not exist or connection failure" {
            Set-DbaConfig -FullName sql.connection.timeout -Value 1
            Get-DbaServerRole -SqlInstance Dummy | Should Be
        }
        It "Validates that Stop Function Mock has been called" {
            $assertMockParams = @{
                'CommandName' = 'Stop-Function'
                'Times'       = 1
                'Exactly'     = $true
                'Module'      = 'dbatools'
            }
            Assert-MockCalled @assertMockParams
        }
    }
}

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    Context "Command actually works" {
        $results = Get-DbaServerRole -SqlInstance $script:instance2
        It "Should have correct properties" {
            $ExpectedProps = 'ComputerName,InstanceName,SqlInstance,Id,Role,IsFixedRole,Owner,DateCreated,DateModified,Login,DatabaseEngineEdition,DatabaseEngineType,Events,ExecutionManager,Name,Parent,Properties,State,Urn,UserData'.Split(',')
            ($results[0].PsObject.Properties.Name | Sort-Object) | Should Be ($ExpectedProps | Sort-Object)
        }

        It "Shows only one value with ServerRole parameter" {
            $results = Get-DbaServerRole -SqlInstance $script:instance2 -ServerRole sysadmin
            $results[0].Role | Should Be "sysadmin"
        }

        It "Should exclude sysadmin from output" {
            $results = Get-DbaServerRole -SqlInstance $script:instance2 -ExcludeServerRole sysadmin
            'sysadmin' -NotIn $results.Role | Should Be $true
        }

        It "Should exclude fixed server-level roles" {
            $results = Get-DbaServerRole -SqlInstance $script:instance2 -ExcludeFixedRole
            'sysadmin' -NotIn $results.Role | Should Be $true
        }

    }
}
tools\dbatools\tests\Get-DbaSpConfigure.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Get configuration" {
        $server = Connect-DbaInstance -SqlInstance $script:instance1
        $configs = $server.Query("sp_configure")
        $remotequerytimeout = $configs | Where-Object name -match 'remote query timeout'

        It "returns equal to or more results than the straight T-SQL query" {
            $results = Get-DbaSpConfigure -SqlInstance $script:instance1
            $results.count -ge $configs.count
        }

        It "returns two results" {
            $results = Get-DbaSpConfigure -SqlInstance $script:instance1 -ConfigName RemoteQueryTimeout, AllowUpdates
            $results.Count | Should Be 2
        }

        It "matches the output of sp_configure " {
            $results = Get-DbaSpConfigure -SqlInstance $script:instance1 -ConfigName RemoteQueryTimeout
            $results.ConfiguredValue -eq $remotequerytimeout.config_value | Should Be $true
            $results.RunningValue -eq $remotequerytimeout.run_value | Should Be $true
        }
    }
}
tools\dbatools\tests\Get-DbaSqlBuildReference.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Unit Test" -Tags Unittest {
    $ModuleBase = (Get-Module -Name dbatools).ModuleBase
    $idxfile = "$ModuleBase\bin\dbatools-buildref-index.json"
    Context 'Validate data in json is correct' {
        It "the json file is there" {
            $result = Test-Path $idxfile
            $result | Should Be $true
        }
        It "the json can be parsed" {
            $IdxRef = Get-Content $idxfile -raw | ConvertFrom-Json
            $IdxRef | Should BeOfType System.Object
        }
    }
    Context 'Validate LastUpdated property' {
        $IdxRef = Get-Content $idxfile -raw | ConvertFrom-Json
        It "Has a proper LastUpdated property" {
            $lastupdate = Get-Date -Date $IdxRef.LastUpdated
            $lastupdate | Should BeOfType System.DateTime
        }
        It "LastUpdated is updated regularly (keeps everybody on their toes)" {
            $lastupdate = Get-Date -Date $IdxRef.LastUpdated
            $lastupdate | Should BeGreaterThan (Get-Date).AddDays(-90)
        }
        It "LastUpdated is not in the future" {
            $lastupdate = Get-Date -Date $IdxRef.LastUpdated
            $lastupdate | Should BeLessThan (Get-Date)
        }
    }
    Context 'Validate Data property' {
        $IdxRef = Get-Content $idxfile -raw | ConvertFrom-Json
        It "Data is a proper array" {
            $IdxRef.Data.Length | Should BeGreaterThan 100
        }
        It "Each Datum has a Version property" {
            $DataLength = $IdxRef.Data.Length
            $DataWithVersion = ($IdxRef.Data.Version | Where-Object { $_ }).Length
            $DataLength | Should Be $DataWithVersion
        }
        It "Each version is correctly parsable" {
            $Versions = $IdxRef.Data.Version | Where-Object { $_ }
            foreach ($ver in $Versions) {
                $splitted = $ver.split('.')
                $dots = $ver.split('.').Length - 1
                if ($dots -ne 2) {
                    $dots | Should Be 2
                }
                try {
                    $splitted | Foreach-Object { [convert]::ToInt32($_) }
                }
                catch {
                    # I know. But someone can find a method to output a custom message ?
                    $splitted -join '.' | Should Be "Composed by integers"
                }
            }
        }
        It "Versions are ordered, the way versions are ordered" {
            $Versions = $IdxRef.Data.Version | Where-Object { $_ }
            $Naturalized = $Versions | Foreach-Object {
                $splitted = $_.split('.') | Foreach-Object { [convert]::ToInt32($_) }
                "$($splitted[0].toString('00'))$($splitted[1].toString('00'))$($splitted[2].toString('0000'))"
            }
            $SortedVersions = $Naturalized | Sort-Object
            ($SortedVersions -join ",") | Should Be ($Naturalized -join ",")
        }
        It "Names are at least 8" {
            $Names = $IdxRef.Data.Name | Where-Object { $_ }
            $Names.Length | Should BeGreaterThan 7
        }
    }
    # These are groups by major release (aka "Name")
    $IdxRef = Get-Content $idxfile -raw | ConvertFrom-Json
    $Groups = @{ }
    $OrderedKeys = @()
    foreach ($el in $IdxRef.Data) {
        $ver = $el.Version.split('.')[0 .. 1] -join '.'
        if (!($Groups.ContainsKey($ver))) {
            $Groups[$ver] = New-Object System.Collections.ArrayList
            $OrderedKeys += $ver
        }
        $null = $Groups[$ver].Add($el)
    }
    foreach ($g in $OrderedKeys) {
        $Versions = $Groups[$g]
        Context "Properties Check, for major release $g" {
            It "has the first element with a Name" {
                $Versions[0].Name | Should BeLike "20*"
            }
            It "No multiple Names around" {
                ($Versions.Name | Where-Object { $_ }).Count | Should Be 1
            }
            It "has a single version tagged as RTM" {
                ($Versions.SP -eq 'RTM').Count | Should Be 1
            }
            It "has a single version tagged as LATEST" {
                ($Versions.SP -eq 'LATEST').Count | Should Be 1
            }
            It "SP Property is formatted correctly" {
                $Versions.SP | Where-Object { $_ } | Should Match '^RTM$|^LATEST$|^SP[\d]+$'
            }
            It "CU Property is formatted correctly" {
                $CUMatch = $Versions.CU | Where-Object { $_ }
                if ($CUMatch) {
                    $CUMatch | Should Match '^CU[\d]+$'
                }
            }
            It "SPs are ordered correctly" {
                $SPs = $Versions.SP | Where-Object { $_ }
                $SPs[0] | Should Be 'RTM'
                $SPs[-1] | Should Be 'LATEST'
                $ActualSPs = $SPs | Where-Object { $_ -match '^SP[\d]+$' }
                $OrderedActualSPs = $ActualSPs | Sort-Object
                ($ActualSPs -join ',') | Should Be ($OrderedActualSPs -join ',')
            }
            It "LATEST is on PAR with a SP" {
                $LATEST = $Versions | Where-Object SP -contains "LATEST"
                $LATEST.SP.Count | Should Be 2
            }
            # see https://github.com/sqlcollaborative/dbatools/pull/2466
            It "KBList has only numbers on it" {
                $NotNumbers = $Versions.KBList | Where-Object { $_ } | Where-Object { $_ -notmatch '^[\d]+$' }
                if ($NotNumbers.Count -ne 0) {
                    foreach ($Nn in $NotNumbers) {
                        $Nn | Should Be "Composed by integers"
                    }
                }
            }
        }
    }
}

Describe "$commandname Integration Tests" -Tags IntegrationTests {
    Context "Test retrieving version from instances" {
        $results = Get-DbaSqlBuildReference -SqlInstance $script:instance1, $script:instance2
        It "Should return an exact match" {
            foreach ($r in $results) {
                $r.MatchType | Should Be "Exact"
            }
        }
    }
}
tools\dbatools\tests\Get-DbaSqlFeature.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tag "IntegrationTests" {
    Context "Verifying command works" {
        It "returns a result with the right computername and name is not null" {
            $results = Get-DbaSqlFeature | Select-Object -First 1
            $results.ComputerName | Should Be $env:COMPUTERNAME
        }
    }
}
tools\dbatools\tests\Get-DbaSqlInstanceProperty.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        $paramCount = 5
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Get-DbaSqlInstanceProperty).Parameters.Keys
        $knownParameters = 'Computer', 'SqlInstance', 'SqlCredential', 'InstanceProperty', 'ExcludeInstanceProperty', 'Credential', 'EnableException'
        It "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    Context "Command actually works" {
        $results = Get-DbaSqlInstanceProperty -SqlInstance $script:instance2
        It "Should have correct properties" {
            $ExpectedProps = 'ComputerName,InstanceName,PropertyType,SqlInstance'.Split(',')
            (($results | Get-Member -MemberType NoteProperty).name | Sort-Object) | Should Be ($ExpectedProps | Sort-Object)
        }
        It "Should return that returns a valid build" {
            $(Get-DbaSqlBuildReference -Build ($results | Where-Object {$_.name -eq 'ResourceVersionString'}).Value).MatchType | Should Be "Exact"
        }
        It "Should have DisableDefaultConstraintCheck set false" {
            ($results | Where-Object {$_.name -eq 'DisableDefaultConstraintCheck'}).Value | Should Be $False
        }
        It "Should get the correct DefaultFile location" {
            $defaultFiles = Get-DbaDefaultPath -SqlInstance $script:instance2
            ($results | Where-Object {$_.name -eq 'DefaultFile'}).Value | Should BeLike "$($defaultFiles.Data)*"
        }
    }
    Context "Property filters work" {
        $resultInclude = Get-DbaSqlInstanceProperty -SqlInstance $script:instance2 -InstanceProperty DefaultFile
        $resultExclude = Get-DbaSqlInstanceProperty -SqlInstance $script:instance2 -ExcludeInstanceProperty DefaultFile
        It "Should only return DefaultFile property" {
            $resultInclude.Name | Should Contain 'DefaultFile'
        }
        It "Should not contain DefaultFile property" {
            $resultExclude.Name | Should Not Contain ([regex]::Escape("DefaultFile"))
        }
    }
    Context "Command can handle multiple instances" {
        It "Should have results for 2 instances" {
            $(Get-DbaSqlInstanceProperty -SqlInstance $script:instance1, $script:instance2 | Select-Object -unique SqlInstance).count | Should Be 2
        }
    }
}
tools\dbatools\tests\Get-DbaSqlModule.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Modules are properly retreived" {

        # SQL2008R2SP2 returns around 600 of these in freshly installed instance. 100 is a good enough number.
        It "Should have a high count" {
            $results = Get-DbaSqlModule -SqlInstance $script:instance1 | Select-Object -First 101
            $results.Count | Should BeGreaterThan 100
        }

        # SQL2008R2SP2 will return a number of modules from the msdb database so it is a good candidate to test
        $results = Get-DbaSqlModule -SqlInstance $script:instance1 -Type View -Database msdb
        It "Should only have one type of object" {
            ($results | Select -Unique Database | Measure-Object).Count | Should Be 1
        }

        It "Should only have one database" {
            ($results | Select -Unique Type | Measure-Object).Count | Should Be 1
        }
    }
}
tools\dbatools\tests\Get-DbaSqlRegistryRoot.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Command returns proper info" {
        $results = Get-DbaSqlRegistryRoot
        $regexpath = "Software\\Microsoft\\Microsoft SQL Server"

        if ($results.count -gt 1) {
            It "returns at least one named instance if more than one result is returned" {
                $named = $results | Where-Object SqlInstance -match '\\'
                $named.SqlInstance.Count -gt 0 | Should Be $true
            }
        }

        foreach ($result in $results) {
            It "returns non-null values" {
                $result.Hive | Should Not Be $null
                $result.SqlInstance | Should Not Be $null
            }

            It "matches Software\Microsoft\Microsoft SQL Server" {
                $result.RegistryRoot -match $regexpath | Should Be $true
            }
        }
    }
}
tools\dbatools\tests\Get-DbaSqlService.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"
. "$PSScriptRoot\..\internal\functions\Connect-SqlInstance.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {

    Context "Command actually works" {
        $instanceName = (Connect-SqlInstance -SqlInstance $script:instance2).ServiceName

        $results = Get-DbaSqlService -ComputerName $script:instance2

        It "shows some services" {
            $results.DisplayName | Should Not Be $null
        }

        $results = Get-DbaSqlService -ComputerName $script:instance2 -Type Agent

        It "shows only one service type" {
            foreach ($result in $results) {
                $result.DisplayName -match "Agent" | Should Be $true
            }
        }


        $results = Get-DbaSqlService -ComputerName $script:instance2 -InstanceName $instanceName -Type Agent

        It "shows a service from a specific instance" {
            $results.ServiceType| Should Be "Agent"
        }

        $service = Get-DbaSqlService -ComputerName $script:instance2 -Type Agent -InstanceName $instanceName

        It "sets startup mode of the service to 'Manual'" {
            { $service.ChangeStartMode('Manual') } | Should Not Throw
        }

        $results = Get-DbaSqlService -ComputerName $script:instance2 -Type Agent -InstanceName $instanceName

        It "verifies that startup mode of the service is 'Manual'" {
            $results.StartMode | Should Be 'Manual'
        }

        $service = Get-DbaSqlService -ComputerName $script:instance2 -Type Agent -InstanceName $instanceName

        It "sets startup mode of the service to 'Automatic'" {
            { $service.ChangeStartMode('Automatic') } | Should Not Throw
        }

        $results = Get-DbaSqlService -ComputerName $script:instance2 -Type Agent -InstanceName $instanceName

        It "verifies that startup mode of the service is 'Automatic'" {
            $results.StartMode | Should Be 'Automatic'
        }
    }
}
tools\dbatools\tests\Get-DbaSuspectPage.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Testing if suspect pages are present" {
        BeforeAll {
            $dbname = "dbatoolsci_GetSuspectPage"
            $Server = Connect-DbaInstance -SqlInstance $script:instance2
            $null = $Server.Query("Create Database [$dbname]")
            $db = Get-DbaDatabase -SqlInstance $Server -Database $dbname
        }
        AfterAll {
            Remove-DbaDatabase -SqlInstance $Server -Database $dbname -Confirm:$false
        }

        $null = $db.Query("
        CREATE TABLE dbo.[Example] (id int);
        INSERT dbo.[Example]
        SELECT top 1000 1
        FROM sys.objects")

        # make darn sure suspect pages show up, run twice
        try {
            $null = Invoke-DbaDatabaseCorruption -SqlInstance $script:instance2 -Database $dbname -Confirm:$false
            $null = $db.Query("select top 100 from example")
            $null = $server.Query("ALTER DATABASE $dbname SET PAGE_VERIFY CHECKSUM  WITH NO_WAIT")
            $null = Start-DbccCheck -Server $Server -dbname $dbname -WarningAction SilentlyContinue
        }
        catch {} # should fail

        try {
            $null = Invoke-DbaDatabaseCorruption -SqlInstance $script:instance2 -Database $dbname -Confirm:$false
            $null = $db.Query("select top 100 from example")
            $null = $server.Query("ALTER DATABASE $dbname SET PAGE_VERIFY CHECKSUM  WITH NO_WAIT")
            $null = Start-DbccCheck -Server $Server -dbname $dbname -WarningAction SilentlyContinue
        }
        catch { } # should fail

        $results = Get-DbaSuspectPage -SqlInstance $server
        It "function should find at least one record in suspect_pages table" {
            $results.Database -contains $dbname | Should Be $true
        }
    }
}
tools\dbatools\tests\Get-DbaTopResourceUsage.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    $results = Get-DbaTopResourceUsage -SqlInstance $instances -Type Duration -Database master

    Context "Command returns proper info" {
        It "returns results" {
            $results.Count -gt 0 | Should Be $true
        }

        foreach ($result in $results) {
            It "only returns results from master" {
                $result.Database -eq 'master' | Should Be $true
            }
        }

        # Each of the 4 -Types return slightly different information so this way, we can check to ensure only duration was returned
        It "Should have correct properties for Duration" {
            $ExpectedProps = 'ComputerName,InstanceName,SqlInstance,Database,ObjectName,QueryHash,TotalElapsedTimeMs,ExecutionCount,AverageDurationMs,QueryTotalElapsedTimeMs,QueryText'.Split(',')
            ($results[0].PSStandardMembers.DefaultDisplayPropertySet.ReferencedPropertyNames | Sort-Object) | Should Be ($ExpectedProps | Sort-Object)
        }
    }
}
tools\dbatools\tests\Get-DbaTrace.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $traceconfig = Get-DbaSpConfigure -SqlInstance $script:instance2 -ConfigName DefaultTraceEnabled

        if ($traceconfig.RunningValue -eq $false) {
            $server = Connect-DbaInstance -SqlInstance $script:instance2
            $server.Query("EXEC sp_configure 'show advanced options', 1;")
            $server.Query("RECONFIGURE WITH OVERRIDE")
            $server.Query("EXEC sp_configure 'default trace enabled', 1;")
            $server.Query("RECONFIGURE WITH OVERRIDE")
            $server.Query("EXEC sp_configure 'show advanced options', 0;")
            $server.Query("RECONFIGURE WITH OVERRIDE")
        }
    }

    AfterAll {
        if ($traceconfig.RunningValue -eq $false) {
            $server.Query("EXEC sp_configure 'show advanced options', 1;")
            $server.Query("RECONFIGURE WITH OVERRIDE")
            $server.Query("EXEC sp_configure 'default trace enabled', 0;")
            $server.Query("RECONFIGURE WITH OVERRIDE")
            $server.Query("EXEC sp_configure 'show advanced options', 0;")
            $server.Query("RECONFIGURE WITH OVERRIDE")
            #$null = Set-DbaSpConfigure -SqlInstance $script:instance2 -ConfigName DefaultTraceEnabled -Value $false
        }
    }
    Context "Test Check Default Trace" {
        $results = Get-DbaTrace -SqlInstance $script:instance2
        It "Should find at least one trace file" {
            $results.Id.Count -gt 0 | Should Be $true
        }
    }
}
tools\dbatools\tests\Get-DbaTraceFlag.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    Context "Verifying TraceFlag output" {
        BeforeAll {
            $safetraceflag = 3226
            $server = Connect-DbaInstance -SqlInstance $script:instance2
            $startingtfs = $server.Query("DBCC TRACESTATUS(-1)")
            $startingtfscount = $startingtfs.Count

            if ($startingtfs.TraceFlag -notcontains $safetraceflag) {
                $server.Query("DBCC TRACEON($safetraceflag,-1)  WITH NO_INFOMSGS")
                $startingtfscount++
            }
        }
        AfterAll {
            if ($startingtfs.TraceFlag -notcontains $safetraceflag) {
                $server.Query("DBCC TRACEOFF($safetraceflag,-1)")
            }
        }

        It "Has the right default properties" {
            $expectedProps = 'ComputerName,InstanceName,SqlInstance,TraceFlag,Global,Status'.Split(',')
            $results = Get-DbaTraceFlag -SqlInstance $script:instance2
            ($results[0].PSStandardMembers.DefaultDisplayPropertySet.ReferencedPropertyNames | Sort-Object) | Should Be ($expectedProps | Sort-Object)
        }

        It "Returns filtered results" {
            $results = Get-DbaTraceFlag -SqlInstance $script:instance2 -TraceFlag $safetraceflag
            $results.TraceFlag.Count | Should Be 1
        }
        It "Returns following number of TFs: $startingtfscount" {
            $results = Get-DbaTraceFlag -SqlInstance $script:instance2
            $results.TraceFlag.Count | Should Be $startingtfscount
        }
    }
}
tools\dbatools\tests\Get-DbaUpTime.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        $paramCount = 4
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Get-DbaUptime).Parameters.Keys
        $knownParameters = 'Computer', 'SqlInstance', 'SqlCredential', 'Credential', 'EnableException'
        It "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    Context "Command actually works" {
        $results = Get-DbaUptime -SqlInstance $script:instance1
        It "Should have correct properties" {
            $ExpectedProps = 'ComputerName,InstanceName,SqlServer,SqlUptime,WindowsUptime,SqlStartTime,WindowsBootTime,SinceSqlStart,SinceWindowsBoot'.Split(',')
            ($results.PsObject.Properties.Name | Sort-Object) | Should Be ($ExpectedProps | Sort-Object)
        }
    }
    Context "Command can handle multiple SqlInstances" {
        $results = Get-DbaUptime -SqlInstance $script:instance1,$script:instance2
            It "Command resultset could contain 2 results" {
                $results.count | Should Be 2
            }
        foreach ($result in $results) {
            It "Windows up time should be more than SQL Uptime" {
                $result.SqlUptime | Should BeLessThan $result.WindowsUpTime
            }
        }
    }
    Context "Properties should return expected types" {
        $results = Get-DbaUptime -SqlInstance $script:instance1
        foreach ($result in $results) {
            It "SqlStartTime should be a DbaDateTime" {
                $result.SqlStartTime  | Should BeOfType DbaDateTime
            }
            It "WindowsBootTime should be a DbaDateTime" {
                $result.WindowsBootTime  | Should BeOfType DbaDateTime
            }
        }
    }
}
tools\dbatools\tests\Get-DbaUserLevelPermission.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Command returns proper info" {
        $results = Get-DbaUserLevelPermission -SqlInstance $script:instance1 -Database tempdb

        It "returns results" {
            $results.Count -gt 0 | Should Be $true
        }

        foreach ($result in $results) {
            It "returns only tempdb or server results" {
                $result.Object -in 'tempdb', 'SERVER' | Should Be $true
            }
        }
    }
}
tools\dbatools\tests\Get-DbaWaitingTask.Tests.ps1
<#
    The below statement stays in for every test you build.
#>
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

<#
    Unit test is required for any command added
#>
Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {

        $paramCount = 5
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Get-DbaWaitingTask).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'EnableException', 'Spid', 'IncludeSystemSpid'
        It "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}
<#
    Integration test are custom to the command you are writing it for,
        but something similar to below should be included if applicable.

    The below examples are by no means set in stone and there are already
        a number of test that you can pull examples from in how they are done.
#>

# Get-DbaNoun
Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {

    $flag = "dbatools_$(Get-Random)"
    $time = '00:15:00'
    $sql = "SELECT '$flag'; WAITFOR DELAY '$time'"
    $instance = $script:instance2

    $modulePath = 'C:\Github\dbatools\dbatools.psm1'
    $job = 'YouHaveBeenFoundWaiting'

    Start-Job -Name $job -ScriptBlock {
        Import-Module $args[0];
        (Connect-DbaInstance -SqlInstance $args[1] -ClientName dbatools-waiting).Query($args[2])
    } -ArgumentList $modulePath, $instance, $sql

    <#
        **This has to sleep as it can take a couple seconds for the job to start**
        Setting it lower will cause issues, you have to consider the Start-Job has to load the module which takes on average 3-4 seconds itself before it executes the command.

        If someone knows a cleaner method by all means adjust this test.
    #>
    Start-Sleep -Seconds 8

    $process = Get-DbaProcess -SqlInstance $instance | Where-Object Program -eq 'dbatools-waiting' | Select-Object -ExpandProperty Spid

    if ($process -ne $null) {
        Context "Command actually works" {
            $results = Get-DbaWaitingTask -SqlInstance $instance -Spid $process
            It "Should have correct properties" {
                $ExpectedProps = 'ComputerName,InstanceName,SqlInstance,Spid,Thread,Scheduler,WaitMs,WaitType,BlockingSpid,ResourceDesc,NodeId,Dop,DbId,InfoUrl,QueryPlan,SqlText'.Split(',')
                ($results.PsObject.Properties.Name | Sort-Object) | Should Be ($ExpectedProps | Sort-Object)
            }
            It "Should have command of 'WAITFOR'" {
                $results.WaitType | Should BeLike "*WAITFOR*"
            }
        }

        $isProcess = Get-DbaProcess -SqlInstance $instance -Spid $process
        if ($isProcess) {
            Stop-DbaProcess -SqlInstance $instance -Spid $process

            # I've had a few cases where first run didn't actually kill the process
            $isProcess = Get-DbaProcess -SqlInstance $instance -Spid $process
            if ($isProcess) {
                Stop-DbaProcess -SqlInstance $instance -Spid $process -ErrorAction SilentlyContinue
            }
        }
        Get-Job -Name $job | Remove-Job -Force -ErrorAction SilentlyContinue
    }
}
tools\dbatools\tests\Get-DbaWaitResource.Tests.ps1
<#
    The below statement stays in for every test you build.
#>
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {

    BeforeAll {

        $random = Get-Random
        $WaitResourceDB = "WaitResource$random"
        Restore-DbaDatabase -SqlInstance $script:instance1 -DatabaseName $WaitResourceDB -ReplaceDbNameInFile -Path $script:appveyorlabrepo\singlerestore\singlerestore.bak
        $sql = "
                create table waittest (
                col1 int,
                col2 varchar(5)
                )
                go
                insert into waittest values (1,'hello')
                go
            "
        
        Invoke-DbaSqlQuery -SqlInstance $script:instance1 -Database $WaitResourceDB -Query $sql
    }
    AfterAll {
        Get-DbaDatabase -SqlInstance $script:instance1 -Database $WaitResourceDB | Remove-DbaDatabase -Confirm:$false

    }

    Context "Test getting a Page resource"{
        $PageSql = "
            Create table #TmpIndex(
                PageFiD int,
                PagePid int,
                IAMFID int,
                IAMPid int,
                ObjectID int,
                IndexID int,
                PartitionNumber bigint,
                ParitionId bigint,
                iam_chain_type varchar(50),
                PageType int,
                IndexLevel int,
                NextPageFID int,
                NextPagePID int,
                prevPageFid int,
                PrevPagePID int
            );

            insert #TmpIndex exec ('dbcc ind($WaitResourceDb,waittest,-1)')

            declare @pageid int
            select @pageid=PagePid from #TmpIndex where PageType=10
            select 'PAGE: '+convert(varchar(3),DB_ID())+':1:'+convert(varchar(15),@pageid)
        "
       $page =  (Invoke-DbaSqlQuery -SqlInstance $script:instance1 -Database $WaitResourceDB -Query $Pagesql).Column1
       $file = Get-DbaDatabaseFile -SqlInstance $script:instance1 -Database $WaitResourceDB | Where-Object TypeDescription -eq 'ROWS'
       $results = Get-DbaWaitResource -SqlInstance $script:instance1 -WaitResource $page
       It "Should return databasename $WaitResourceDB" {
           $results.DatabaseName | Should Be $WaitResourceDB
       }
       
       It "Should return physical filename" {
           $results.DataFilePath | Should Be $file.PhysicalName
       }
       It "Should return the correct filename" {
           $results.DatafileName | Should Be $file.LogicalName
       }
       It "Should return ObjectName waittest" {
           $results.ObjectName | Should be 'waittest'
       }
       It "Should return the correct object type" {
           $Results.ObjectType | Should Be 'USER_TABLE'
       }
    }

    Context "Deciphering a KEY WaitResource" {
        $SqlKey = "
            create table keytest(
                col1 int,
                col2 varchar(5)
            )

            create clustered index idx_pester on keytest (col1)

            insert into keytest values (1,'bilbo')

            declare @hobt_id bigint
            select @hobt_id = hobt_id from sys.partitions where object_id=object_id('dbo.keytest')

            select 'KEY: '+convert(varchar(3),db_id())+':'+convert(varchar(30),@hobt_id)+' '+ %%lockres%% from keytest  where col1=1
        "
        $key = (Invoke-DbaSqlQuery -SqlInstance $script:instance1 -Database $WaitResourceDB -Query $SqlKey).Column1
        $resultskey = Get-DbaWaitResource -SqlInstance $script:instance1 -WaitResource $key -row
        It "Should Return DatabaseName $WaitResourceDB" {
            $results
        }
        It "Should return databasename $WaitResourceDB" {
            $resultskey.DatabaseName | Should Be $WaitResourceDB
        }
        It "Should return SchemaName dbo" {
            $resultskey.SchemaName | Should Be 'dbo'
        }
        It "Should return indexname is idx_pester" {
            $resultskey.IndexName | Should Be 'idx_pester'
        }
        It "Should return ObjectName keytest"{
            $resultskey.ObjectName | Should Be 'Keytest'
        }
        It "SHould return col1 is 1" {
            $resultskey.ObjectData.col1 | Should Be 1
        }
        It "Should return col1 is bilbo" {
            $resultskey.ObjectData.col2 | Should Be 'bilbo'
        }
    }
}
tools\dbatools\tests\Get-DbaWaitStatistic.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Command returns proper info" {
        $results = Get-DbaWaitStatistic -SqlInstance $script:instance2 -Threshold 100

        It "returns results" {
            $results.Count -gt 0 | Should Be $true
        }

        foreach ($result in $results) {
            It "returns a hyperlink" {
                $result.URL -match 'sqlskills.com' | Should Be $true
            }
        }
    }

    Context "Command returns proper info when using parameter IncludeIgnorable" {
        $results = Get-DbaWaitStatistic -SqlInstance $script:instance2 -Threshold 100 -IncludeIgnorable | Where-Object {
                $_.WaitType -eq 'SLEEP_MASTERDBREADY'
            }

        It "returns results" {
            $results | Should -Not -BeNullOrEmpty
        }

        It "results includes ignorable column" {
            $results.PSObject.Properties.Name.Contains('Ignorable') | Should Be $true
        }

        foreach ($result in $results) {
            It "returns a hyperlink" {
                $result.URL -match 'sqlskills.com' | Should Be $true
            }
        }
    }
}
tools\dbatools\tests\Get-DbaXESession.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    Context "Verifying command output" {
        It "returns some results" {
            $results = Get-DbaXESession -SqlInstance $script:instance2
            $results.Count -gt 1 | Should Be $true
        }

        It "returns only the system_health session" {
            $results = Get-DbaXESession -SqlInstance $script:instance2 -Session system_health
            $results.Name -eq 'system_health' | Should Be $true
        }
    }
}
tools\dbatools\tests\Get-DbaXESessionTarget.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    Context "Verifying command output" {

        It "returns only the system_health session" {
            $results = Get-DbaXESessionTarget -SqlInstance $script:instance2 -Target package0.event_file
            foreach ($result in $results) {
                $result.Name -eq 'package0.event_file' | Should Be $true
            }
        }

        It "supports the pipeline" {
            $results = Get-DbaXESession -SqlInstance $script:instance2 -Session system_health | Get-DbaXESessionTarget -Target package0.event_file
            $results.Count -gt 0 | Should Be $true
        }
    }
}
tools\dbatools\tests\Get-DbaXESessionTemplate.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Get Template Index" {
        $results = Get-DbaXESessionTemplate
        It "returns good results with no missing information" {
            $results | Where-Object Name -eq $null | Should Be $null
            $results | Where-Object TemplateName -eq $null | Should Be $null
            $results | Where-Object Description -eq $null | Should Be $null
            $results | Where-Object Category -eq $null | Should Be $null
        }
    }
}
tools\dbatools\tests\Get-DirectoryRestoreFile.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Unit Tests" -Tag 'UnitTests' {
    Context "Test Path handling" {
        It "Should throw on an invalid Path" {
            { Get-DirectoryRestoreFile -Path TestDrive:\foo\bar\does\not\exist\ -EnableException } | Should Throw
        }
    }
    Context "Returning Files from one folder" {
        New-item "TestDrive:\backups\" -ItemType directory
        New-item "TestDrive:\backups\full.bak" -ItemType File
        New-item "TestDrive:\backups\log1.trn" -ItemType File
        New-item "TestDrive:\backups\log2.trn" -ItemType File
        New-item "TestDrive:\backups\b\" -ItemType directory
        New-item "TestDrive:\backups\b\log2b.trn" -ItemType File
        $results = Get-DirectoryRestoreFile -Path TestDrive:\backups
        It "Should Return an array of FileInfo" {
            $results | Should BeOfType System.IO.FileSystemInfo
        }
        It "Should Return 3 files" {
            $results.count | Should Be 3
        }
        It "Should return 1 bak file" {
            ($results | Where-Object { $_.Fullname -like '*\backups\Full.bak' }).count | Should be 1
        }
        It "Should return 2 trn files" {
            ($results | Where-Object { $_.Fullname -like '*\backups\*.trn' }).count | Should be 2
        }
        It "Should not contain log2b.trn" {
            ($results | Where-Object { $_.Fullname -like '*\backups\*log2b.trn' }).count | Should be 0
        }
    }
    Context "Returning Files from folders with recursion" {
        New-item "TestDrive:\backups\" -ItemType directory
        New-item "TestDrive:\backups\full.bak" -ItemType File
        New-item "TestDrive:\backups\log1.trn" -ItemType File
        New-item "TestDrive:\backups\log2.trn" -ItemType File
        New-item "TestDrive:\backups\b\" -ItemType directory
        New-item "TestDrive:\backups\b\log2b.trn" -ItemType File
        $results2 = Get-DirectoryRestoreFile -Path TestDrive:\backups -recurse
        It "Should Return an array of FileInfo" {
            $results2 | Should BeOfType System.IO.FileSystemInfo
        }
        It "Should Return 4 files" {
            $results2.count | Should Be 4
        }
        It "Should return 1 bak file" {
            ($results2 | Where-Object {$_.Fullname -like '*\backups\Full.bak'}).count | Should be 1
        }
        It "Should return 3 trn files" {
            ($results2 | Where-Object {$_.Fullname -like '*\backups\*.trn'}).count | Should be 3
        }
        It "Should  contain log2b.trn" {
            ($results2 | Where-Object {$_.Fullname -like '*\backups\*log2b.trn'}).count | Should be 1
        }
    }
}
tools\dbatools\tests\Get-XpDirTreeRestoreFile.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"
. "$PSScriptRoot\..\internal\functions\Connect-SqlInstance.ps1"

Describe "$commandname Unit Tests" -Tag 'UnitTests' {
    InModuleScope dbatools {
        #mock Connect-SqlInstance { $true }
        mock Test-DbaSqlPath { $true }

        Context "Test Connection and User Rights" {
            It "Should throw on an invalid SQL Connection" {
                #mock Test-SQLConnection {(1..12) | %{[System.Collections.ArrayList]$t += @{ConnectSuccess = $false}}}
                Mock Connect-SqlInstance { throw }
                { Get-XpDirTreeRestoreFile -path c:\dummy -SqlInstance bad\bad -EnableException $true } | Should Throw
            }
            It "Should throw if SQL Server can't see the path" {
                Mock Test-DbaSqlPath { $false }
                Mock Connect-SqlInstance { [DbaInstanceParameter]"bad\bad" }
                { Get-XpDirTreeRestoreFile -path c:\dummy -SqlInstance bad\bad -EnableException $true } | Should Throw
            }
        }
        Context "Non recursive filestructure" {
            $array = (@{ subdirectory = 'full.bak'; depth = 1; file = 1 },
                @{ subdirectory = 'full2.bak'; depth = 1; file = 1 })
            Mock Connect-SqlInstance -MockWith {
                $obj = [PSCustomObject]@{
                    Name                 = 'BASEName'
                    NetName              = 'BASENetName'
                    InstanceName         = 'BASEInstanceName'
                    DomainInstanceName   = 'BASEDomainInstanceName'
                    InstallDataDirectory = 'BASEInstallDataDirectory'
                    ErrorLogPath         = 'BASEErrorLog_{0}_{1}_{2}_Path' -f "'", '"', ']'
                    ServiceName          = 'BASEServiceName'
                    VersionMajor         = 9
                    ConnectionContext    = New-Object PSObject
                }
                Add-Member -InputObject $obj.ConnectionContext -Name ConnectionString  -MemberType NoteProperty -Value 'put=an=equal=in=it'
                Add-Member -InputObject $obj -Name Query -MemberType ScriptMethod -Value {
                    param($query)
                    if ($query -eq "EXEC master.sys.xp_dirtree 'c:\temp\',1,1;") {
                        return $array
                    }
                }
                $obj.PSObject.TypeNames.Clear()
                $obj.PSObject.TypeNames.Add("Microsoft.SqlServer.Management.Smo.Server")
                return $obj
            }
            $results = Get-XpDirTreeRestoreFile -path c:\temp -SqlInstance bad\bad -EnableException $true
            It "Should return an array of 2 files" {
                $results.count | Should Be 2
            }
            It "Should return a file in c:\temp" {
                $results[0].Fullname | Should BeLike 'c:\temp\*bak'
            }
            It "Should return another file in C:\temp" {
                $results[1].Fullname | Should BeLike 'c:\temp\*bak'
            }
        }
        Context "Recursive Filestructure" {
            $array = (@{ subdirectory = 'full.bak'; depth = 1; file = 1 },
                @{ subdirectory = 'full2.bak'; depth = 1; file = 1 },
                @{ subdirectory = 'recurse'; depth = 1; file = 0 })
            $array2 = (@{ subdirectory = 'fulllow.bak'; depth = 1; file = 1 },
                @{ subdirectory = 'full2low.bak'; depth = 1; file = 1 })
            Mock Connect-SqlInstance -MockWith {
                $obj = [PSCustomObject]@{
                    Name                 = 'BASEName'
                    NetName              = 'BASENetName'
                    InstanceName         = 'BASEInstanceName'
                    DomainInstanceName   = 'BASEDomainInstanceName'
                    InstallDataDirectory = 'BASEInstallDataDirectory'
                    ErrorLogPath         = 'BASEErrorLog_{0}_{1}_{2}_Path' -f "'", '"', ']'
                    ServiceName          = 'BASEServiceName'
                    VersionMajor         = 9
                    ConnectionContext    = New-Object PSObject
                }
                Add-Member -InputObject $obj.ConnectionContext -Name ConnectionString  -MemberType NoteProperty -Value 'put=an=equal=in=it'
                Add-Member -InputObject $obj -Name Query -MemberType ScriptMethod -Value {
                    param($query)
                    if ($query -eq "EXEC master.sys.xp_dirtree 'c:\temp\recurse\',1,1;") {
                        return $array2
                    }
                    if ($query -eq "EXEC master.sys.xp_dirtree 'c:\temp\',1,1;") {
                        return $array
                    }
                }
                $obj.PSObject.TypeNames.Clear()
                $obj.PSObject.TypeNames.Add("Microsoft.SqlServer.Management.Smo.Server")
                return $obj
            }


            $results = Get-XpDirTreeRestoreFile -path c:\temp -SqlInstance bad\bad -EnableException $true
            It "Should return array of 4 files - recursion" {
                $results.count | Should Be 4
            }
            It "Should return C:\temp\recurse\fulllow.bak" {
                ($results | Where-Object { $_.Fullname -eq 'C:\temp\recurse\fulllow.bak' } | measure-Object).count | Should be 1
            }
            It "Should return C:\temp\recurse\fulllow.bak" {
                ($results | Where-Object { $_.Fullname -eq 'C:\temp\recurse\full2low.bak' } | measure-Object).count | Should be 1
            }
            It "Should return C:\temp\recurse\fulllow.bak" {
                ($results | Where-Object { $_.Fullname -eq 'C:\temp\full.bak' } | measure-Object).count | Should be 1
            }
            It "Should return C:\temp\recurse\fulllow.bak" {
                ($results | Where-Object { $_.Fullname -eq 'C:\temp\full2.bak' } | measure-Object).count | Should be 1
            }
        }
    }
}
tools\dbatools\tests\Import-DbaPfDataCollectorSetTemplate.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    BeforeEach {
        $null = Get-DbaPfDataCollectorSet -CollectorSet 'Long Running Queries' | Remove-DbaPfDataCollectorSet -Confirm:$false
    }
    AfterAll {
        $null = Get-DbaPfDataCollectorSet -CollectorSet 'Long Running Queries' | Remove-DbaPfDataCollectorSet -Confirm:$false
    }
    Context "Verifying command returns all the required results with pipe" {
        It "returns only one (and the proper) template" {
            $results = Get-DbaPfDataCollectorSetTemplate -Template 'Long Running Queries' | Import-DbaPfDataCollectorSetTemplate
            $results.Name | Should Be 'Long Running Queries'
            $results.ComputerName | Should Be $env:COMPUTERNAME
        }
        It "returns only one (and the proper) template without pipe" {
            $results = Import-DbaPfDataCollectorSetTemplate -Template 'Long Running Queries'
            $results.Name | Should Be 'Long Running Queries'
            $results.ComputerName | Should Be $env:COMPUTERNAME
        }
    }
}
tools\dbatools\tests\Import-DbaRegisteredServer.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tag "IntegrationTests" {
    Context "Setup" {
        BeforeAll {
            $srvName = "dbatoolsci-server1"
            $group = "dbatoolsci-group1"
            $regSrvName = "dbatoolsci-server12"
            $regSrvDesc = "dbatoolsci-server123"
            
            $newGroup = Add-DbaRegisteredServerGroup -SqlInstance $script:instance1 -Name $group
            $newServer = Add-DbaRegisteredServer -SqlInstance $script:instance1 -ServerName $srvName -Name $regSrvName -Description $regSrvDesc -Group $newGroup.Name
            
            $srvName2 = "dbatoolsci-server2"
            $group2 = "dbatoolsci-group1a"
            $regSrvName2 = "dbatoolsci-server21"
            $regSrvDesc2 = "dbatoolsci-server321"
            
            $newGroup2 = Add-DbaRegisteredServerGroup -SqlInstance $script:instance1 -Name $group2
            $newServer2 = Add-DbaRegisteredServer -SqlInstance $script:instance1 -ServerName $srvName2 -Name $regSrvName2 -Description $regSrvDesc2
            
            $regSrvName3 = "dbatoolsci-server3"
            $srvName3 = "dbatoolsci-server3"
            $regSrvDesc3 = "dbatoolsci-server3desc"
            
            $newServer3 = Add-DbaRegisteredServer -SqlInstance $script:instance1 -ServerName $srvName3 -Name $regSrvName3 -Description $regSrvDesc3
        }
        AfterAll {
            Get-DbaRegisteredServer -SqlInstance $script:instance1, $script:instance2 | Where-Object Name -match dbatoolsci | Remove-DbaRegisteredServer -Confirm:$false
            Get-DbaRegisteredServerGroup -SqlInstance $script:instance1, $script:instance2 | Where-Object Name -match dbatoolsci | Remove-DbaRegisteredServerGroup -Confirm:$false
        }
        
        It "imports group objects" {
            $results = $newServer.Parent | Import-DbaRegisteredServer -SqlInstance $script:instance2
            $results.Description | Should -Be $regSrvDesc
            $results.ServerName | Should -Be $srvName
            $results.Parent.Name | Should -Be $group
        }
        
        It "imports registered server objects" {
            $results2 = $newServer2 | Import-DbaRegisteredServer -SqlInstance $script:instance2
            $results2.ServerName | Should -Be $newServer2.ServerName
            $results2.Parent.Name | Should -Be $newServer2.Parent.Name
        }
        
        It "imports a file from Export-DbaRegisteredServer" {
            $results3 = $newServer3 | Export-DbaRegisteredServer -Path C:\temp\dbatoolsci_regserverexport.xml
            $results4 = Import-DbaRegisteredServer -SqlInstance $script:instance2 -Path $results3
            $results4.ServerName | Should -Be @('dbatoolsci-server3', 'dbatoolsci-server1')
            $results4.Description | Should -Be @('dbatoolsci-server3desc', 'dbatoolsci-server123')
        }
        It "imports from a random object so long as it has ServerName" {
            $object = [pscustomobject]@{
                ServerName = 'dbatoolsci-randobject'
            }
            $results = $object | Import-DbaRegisteredServer -SqlInstance $script:instance2
            $results.ServerName | Should -Be 'dbatoolsci-randobject'
            $results.Name | Should -Be 'dbatoolsci-randobject'
        }
        It "does not import object if ServerName does not exist" {
            $object = [pscustomobject]@{
                Name = 'dbatoolsci-randobject'
            }
            $results = $object | Import-DbaRegisteredServer -SqlInstance $script:instance2 -WarningAction SilentlyContinue -WarningVariable warn
            $results | Should -Be $null
            $warn | Should -Match 'No servers added'
        }
    }
}
tools\dbatools\tests\Import-DbaXESessionTemplate.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    AfterAll {
        $null = Get-DbaXESession -SqlInstance $script:instance2 -Session 'Overly Complex Queries' | Remove-DbaXESession
    }
    Context "Test Importing Session Template" {
        It -Skip "session imports with proper name and non-default target file location" {
            $result = Import-DbaXESessionTemplate -SqlInstance $script:instance2 -Template 'Overly Complex Queries' -TargetFilePath C:\temp
            $result.Name | Should Be "Overly Complex Queries"
            $result.TargetFile -match 'C\:\\temp' | Should Be $true
        }
    }
}
tools\dbatools\tests\InModule.Help.Exceptions.ps1
$global:FunctionHelpTestExceptions = @(
    "TabExpansion2"
)

$global:HelpTestEnumeratedArrays = @(
    "Sqlcollaborative.Dbatools.Connection.ManagementConnectionType[]"
    "Sqlcollaborative.Dbatools.Message.MessageLevel[]"
)

$global:HelpTestSkipParameterType = @{
    "Get-DbaCmObject"      = @("DoNotUse")
    "Test-DbaCmConnection" = @("Type")
    "Get-DbaService"       = @("DoNotUse")
}
tools\dbatools\tests\InModule.Help.Tests.ps1
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
<#
    .NOTES
        ===========================================================================
        Created with:    SAPIEN Technologies, Inc., PowerShell Studio 2016 v5.2.119
        Created on:      4/12/2016 1:11 PM
        Created by:      June Blender
        Organization:    SAPIEN Technologies, Inc
        Filename:        *.Help.Tests.ps1
        ===========================================================================
    .DESCRIPTION
    To test help for the commands in a module, place this file in the module folder.
    To test any module from any path, use https://github.com/juneb/PesterTDD/Module.Help.Tests.ps1
#>
if ($SkipHelpTest) { return }
. "$PSScriptRoot\InModule.Help.Exceptions.ps1"

$includedNames = (Get-ChildItem "$PSScriptRoot\..\functions" | Where-Object Name -like "*.ps1" ).BaseName
$commands = Get-Command -Module (Get-Module dbatools) -CommandType Cmdlet, Function, Workflow | Where-Object Name -in $includedNames

## When testing help, remember that help is cached at the beginning of each session.
## To test, restart session.


foreach ($command in $commands) {
    $commandName = $command.Name

    # Skip all functions that are on the exclusions list
    if ($global:FunctionHelpTestExceptions -contains $commandName) { continue }

    # The module-qualified command fails on Microsoft.PowerShell.Archive cmdlets
    $Help = Get-Help $commandName -ErrorAction SilentlyContinue
    $testhelperrors = 0
    $testhelpall = 0
    Describe "Test help for $commandName" {

        $testhelpall += 1
        if ($Help.Synopsis -like '*`[`<CommonParameters`>`]*') {
            # If help is not found, synopsis in auto-generated help is the syntax diagram
            It "should not be auto-generated" {
                $Help.Synopsis | Should Not BeLike '*`[`<CommonParameters`>`]*'
            }
            $testhelperrors += 1
        }

        $testhelpall += 1
        if ([String]::IsNullOrEmpty($Help.Description.Text)) {
            # Should be a description for every function
            It "gets description for $commandName" {
                $Help.Description | Should Not BeNullOrEmpty
            }
            $testhelperrors += 1
        }

        $testhelpall += 1
        if ([String]::IsNullOrEmpty(($Help.Examples.Example | Select-Object -First 1).Code)) {
            # Should be at least one example
            It "gets example code from $commandName" {
                ($Help.Examples.Example | Select-Object -First 1).Code | Should Not BeNullOrEmpty
            }
            $testhelperrors += 1
        }

        $testhelpall += 1
        if ([String]::IsNullOrEmpty(($Help.Examples.Example.Remarks | Select-Object -First 1).Text)) {
            # Should be at least one example description
            It "gets example help from $commandName" {
                ($Help.Examples.Example.Remarks | Select-Object -First 1).Text | Should Not BeNullOrEmpty
            }
            $testhelperrors += 1
        }

        if ($testhelperrors -eq 0) {
            It "Ran silently $testhelpall tests" {
                $testhelperrors | Should be 0
            }
        }

        $testparamsall = 0
        $testparamserrors = 0
        Context "Test parameter help for $commandName" {

            $Common = 'Debug', 'ErrorAction', 'ErrorVariable', 'InformationAction', 'InformationVariable', 'OutBuffer', 'OutVariable',
            'PipelineVariable', 'Verbose', 'WarningAction', 'WarningVariable'

            $parameters = $command.ParameterSets.Parameters | Sort-Object -Property Name -Unique | Where-Object Name -notin $common
            $parameterNames = $parameters.Name
            $HelpParameterNames = $Help.Parameters.Parameter.Name | Sort-Object -Unique
            foreach ($parameter in $parameters) {
                $parameterName = $parameter.Name
                $parameterHelp = $Help.parameters.parameter | Where-Object Name -EQ $parameterName

                $testparamsall += 1
                if ([String]::IsNullOrEmpty($parameterHelp.Description.Text)) {
                    # Should be a description for every parameter
                    It "gets help for parameter: $parameterName : in $commandName" {
                        $parameterHelp.Description.Text | Should Not BeNullOrEmpty
                    }
                    $testparamserrors += 1
                }

                $testparamsall += 1
                $codeMandatory = $parameter.IsMandatory.toString()
                if ($parameterHelp.Required -ne $codeMandatory) {
                    # Required value in Help should match IsMandatory property of parameter
                    It "help for $parameterName parameter in $commandName has correct Mandatory value" {
                        $parameterHelp.Required | Should Be $codeMandatory
                    }
                    $testparamserrors += 1
                }

                if ($HelpTestSkipParameterType[$commandName] -contains $parameterName) { continue }

                $codeType = $parameter.ParameterType.Name

                $testparamsall += 1
                if ($parameter.ParameterType.IsEnum) {
                    # Enumerations often have issues with the typename not being reliably available
                    $names = $parameter.ParameterType::GetNames($parameter.ParameterType)
                    if ($parameterHelp.parameterValueGroup.parameterValue -ne $names) {
                        # Parameter type in Help should match code
                        It "help for $commandName has correct parameter type for $parameterName" {
                            $parameterHelp.parameterValueGroup.parameterValue | Should be $names
                        }
                        $testparamserrors += 1
                    }
                }
                elseif ($parameter.ParameterType.FullName -in $HelpTestEnumeratedArrays) {
                    # Enumerations often have issues with the typename not being reliably available
                    $names = [Enum]::GetNames($parameter.ParameterType.DeclaredMembers[0].ReturnType)
                    if ($parameterHelp.parameterValueGroup.parameterValue -ne $names) {
                        # Parameter type in Help should match code
                        It "help for $commandName has correct parameter type for $parameterName" {
                            $parameterHelp.parameterValueGroup.parameterValue | Should be $names
                        }
                        $testparamserrors += 1
                    }
                }
                else {
                    # To avoid calling Trim method on a null object.
                    $helpType = if ($parameterHelp.parameterValue) { $parameterHelp.parameterValue.Trim() }
                    if ($helpType -ne $codeType ) {
                        # Parameter type in Help should match code
                        It "help for $commandName has correct parameter type for $parameterName" {
                            $helpType | Should be $codeType
                        }
                        $testparamserrors += 1
                    }
                }
            }
            foreach ($helpParm in $HelpParameterNames) {
                $testparamsall += 1
                if ($helpParm -notin $parameterNames) {
                    # Shouldn't find extra parameters in help.
                    It "finds help parameter in code: $helpParm" {
                        $helpParm -in $parameterNames | Should Be $true
                    }
                    $testparamserrors += 1
                }
            }
            if ($testparamserrors -eq 0) {
                It "Ran silently $testparamsall tests" {
                    $testparamserrors | Should be 0
                }
            }
        }
    }
}
tools\dbatools\tests\Install-DbaFirstResponderKit.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        $paramCount = 5
        $commonParamCount = ([System.Management.Automation.PSCmdlet]::CommonParameters).Count + 2
        [object[]]$params = (Get-ChildItem function:\Install-DbaFirstResponderKit).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'Branch', 'Database', 'EnableException'
        It "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $commonParamCount | Should Be $paramCount
        }
    }
}

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    Context "Testing First Responder Kit installer" {
        BeforeAll {
            $database = "dbatoolsci_frk_$(Get-Random)"
            $server = Connect-DbaInstance -SqlInstance $script:instance2
            $server.Query("CREATE DATABASE $database")
        }
        AfterAll {
            Remove-DbaDatabase -SqlInstance $script:instance2 -Database $database -Confirm:$false
        }

        $results = Install-DbaFirstResponderKit -SqlInstance $script:instance2 -Database $database -Branch master

        It "Installs to specified database: $database" {
            $results[0].Database -eq $database | Should Be $true
        }
        It "Shows status of Installed" {
            $results[0].Status -eq "Installed" | Should Be $true
        }
        It "At least installed sp_Blitz and sp_BlitzIndex" {
            'sp_Blitz', 'sp_BlitzIndex' | Should BeIn $results.Name
        }
        It "has the correct properties" {
            $result = $results[0]
            $ExpectedProps = 'SqlInstance,InstanceName,ComputerName,Name,Status,Database'.Split(',')
            ($result.PsObject.Properties.Name | Sort-Object) | Should Be ($ExpectedProps | Sort-Object)
        }
    }
}
tools\dbatools\tests\Install-DbaMaintenanceSolution.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    Context "Limited testing of Maintenance Solution installer" {
        BeforeAll {
            $server = Connect-DbaInstance -SqlInstance $script:instance2
            $server.Databases['tempdb'].Query("CREATE TABLE CommandLog (id int)")
        }
        AfterAll {
            $server.Databases['tempdb'].Query("DROP TABLE CommandLog")
        }
        It "does not overwrite existing " {
            $results = Install-DbaMaintenanceSolution -SqlInstance $script:instance2 -Database tempdb -WarningVariable warn -WarningAction SilentlyContinue
            $warn -match "already exists" | Should Be $true
        }
    }
}
tools\dbatools\tests\Invoke-DbaBalanceDataFiles.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {

    BeforeAll {
        # Create the server object
        $server = Connect-DbaInstance -SqlInstance $script:instance2

        # Get the default data directory to create the additional data file
        $defaultdata = (Get-DbaDefaultPath -SqlInstance $server).Data

        # Set the database name
        $dbname = "dbatoolscsi_balance"

        # Create the databse
        $server.Query("CREATE DATABASE [$dbname]")

        # Refresh the database to get all the latest changes
        $server.Databases.Refresh()

        # retrieve the database object for later
        $db = Get-DbaDatabase -SqlInstance $server -Database $dbname

        # Create the tables
        $db.Query("CREATE TABLE table1 (ID1 INT IDENTITY PRIMARY KEY, Name1 varchar(100))")
        $db.Query("CREATE TABLE table2 (ID1 INT IDENTITY PRIMARY KEY, Name2 varchar(100))")

        # Generate the values
        $sqlvalues = New-Object System.Collections.ArrayList
        1 .. 1000 | ForEach-Object { $null = $sqlvalues.Add("('some value to test the balance command $_')") }

        $db.Query("insert into table1 (Name1) Values $($sqlvalues -join ',')")
        $db.Query("insert into table1 (Name1) Values $($sqlvalues -join ',')")
        $db.Query("insert into table1 (Name1) Values $($sqlvalues -join ',')")
        $db.Query("insert into table1 (Name1) Values $($sqlvalues -join ',')")
        $db.Query("insert into table1 (Name1) Values $($sqlvalues -join ',')")
        $db.Query("insert into table2 (Name2) Values $($sqlvalues -join ',')")
        $db.Query("insert into table2 (Name2) Values $($sqlvalues -join ',')")
        $db.Query("insert into table2 (Name2) Values $($sqlvalues -join ',')")
        $db.Query("insert into table2 (Name2) Values $($sqlvalues -join ',')")
        $db.Query("insert into table2 (Name2) Values $($sqlvalues -join ',')")

        $db.Query("ALTER DATABASE $dbname ADD FILE (NAME = secondfile, FILENAME = '$defaultdata\$dbname-secondaryfg.ndf') TO FILEGROUP [PRIMARY]")

    }
    AfterAll {
        Remove-DbaDatabase -SqlInstance $server -Database $dbname -Confirm:$false
    }

    Context "Data is balanced among data files" {

        $results = Invoke-DbaBalanceDataFiles -SqlInstance $server -Database $dbname -RebuildOffline -Force

        It "Result returns success" {
            $results.Success | Should -Be $true
        }

        $sizeUsedBefore = $results.DataFilesStart[0].UsedSpace.Kilobyte
        $sizeUsedAfter = $results.DataFilesEnd[0].UsedSpace.Kilobyte

        It "New used space should be less" {

            $sizeUsedAfter | Should -BeLessThan $sizeUsedBefore
        }


    }
}
tools\dbatools\tests\Invoke-DbaCycleErrorLog.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag "UnitTests" {
    Context "Validate parameters" {
        $paramCount = 4
        $defaultParamCount = 13
        [object[]]$params = (Get-ChildItem function:\Invoke-DbaCycleErrorLog).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'Type', 'EnableException'
        It "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}

Describe "$CommandName Integration Test" -Tag "IntegrationTests" {
    $results = Invoke-DbaCycleErrorLog -SqlInstance $script:instance1 -Type instance

    Context "Validate output" {
        It "Should have correct properties" {
            $ExpectedProps = 'ComputerName,InstanceName,SqlInstance,LogType,IsSuccessful,Notes'.Split(',')
            ($results.PsObject.Properties.Name | Sort-Object) | Should Be ($ExpectedProps | Sort-Object)
        }
        It "Should cycle instance error log" {
            $results.LogType | Should Be "instance"
        }
    }
}
tools\dbatools\tests\Invoke-DbaDatabaseClone.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    $dbname = "dbatoolsci_clonetest"
    $clonedb = "dbatoolsci_clonetest_CLONE"
    $clonedb2 = "dbatoolsci_clonetest_CLONE2"
    Context "Command functions as expected" {
        BeforeAll {
            $server = Connect-DbaInstance -SqlInstance $script:instance2
            $server.Query("CREATE DATABASE $dbname")
        }

        AfterAll {
            Get-DbaDatabase -SqlInstance $server -Database $dbname, $clonedb, $clonedb2 | Remove-DbaDatabase -Confirm:$false
        }

        It "warns if SQL instance version is not supported" {
            $results = Invoke-DbaDatabaseClone -SqlInstance $script:instance1 -Database $dbname -CloneDatabase $clonedb -WarningAction SilentlyContinue -WarningVariable versionwarn
            $versionwarn = $versionwarn | Out-String
            $versionwarn -match "required"
        }

        It "warns if destination database already exists" {
            $results = Invoke-DbaDatabaseClone -SqlInstance $script:instance2 -Database $dbname -CloneDatabase tempdb -WarningAction SilentlyContinue -WarningVariable dbwarn
            $dbwarn = $dbwarn | Out-String
            $dbwarn -match "exists"
        }

        It "warns if a system db is specified to clone" {
            $results = Invoke-DbaDatabaseClone -SqlInstance $script:instance2 -Database master -CloneDatabase $clonedb -WarningAction SilentlyContinue -WarningVariable systemwarn
            $systemwarn = $systemwarn | Out-String
            $systemwarn -match "user database"
        }

        $results = Invoke-DbaDatabaseClone -SqlInstance $script:instance2 -Database $dbname -CloneDatabase $clonedb -WarningAction SilentlyContinue

        It "returns 1 result" {
            ($results).Count -eq 1
        }

        foreach ($result in $results) {
            It "returns a rich database object with the correct name" {
                $result.Name -in $clonedb, $clonedb2
            }
        }
    }
}
tools\dbatools\tests\Invoke-DbaDatabaseCorruption.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Unit Tests" -Tags "UnitTests" {
    Context "Validating Database Input" {
        Invoke-DbaDatabaseCorruption -SqlInstance $script:instance2 -Database "master" -WarningAction SilentlyContinue -WarningVariable systemwarn
        It "Should not allow you to corrupt system databases." {
            $systemwarn -match 'may not corrupt system databases' | Should Be $true
        }
        It "Should fail if more than one database is specified" {
            { Invoke-DbaDatabaseCorruption -SqlInstance $script:instance2 -Database "Database1", "Database2" -EnableException } | Should Throw
        }
    }

    Context "It's Confirm impact should be high" {
        $command = Get-Command Invoke-DbaDatabaseCorruption
        $metadata = [System.Management.Automation.CommandMetadata]$command
        $metadata.ConfirmImpact | Should Be 'High'
    }
}

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $dbname = "dbatoolsci_InvokeDbaDatabaseCorruptionTest"
        $Server = Connect-DbaInstance -SqlInstance $script:instance2
        $TableName = "Example"
        # Need a clean empty database
        $null = $Server.Query("Create Database [$dbname]")
        $db = Get-DbaDatabase -SqlInstance $Server -Database $dbname
    }

    AfterAll {
        # Cleanup
        Remove-DbaDatabase -SqlInstance $Server -Database $dbname -Confirm:$false
    }

    It "Require at least a single table in the database specified" {
        { Invoke-DbaDatabaseCorruption -SqlInstance $server -Database $dbname -EnableException } | Should Throw
    }

    # Creating a table to make sure these are failing for different reasons
    It "Fail if the specified table does not exist" {
        { Invoke-DbaDatabaseCorruption -SqlInstance $server -Database $dbname -Table "DoesntExist$(New-Guid)" -EnableException } | Should Throw
    }

    $null = $db.Query("
        CREATE TABLE dbo.[$TableName] (id int);
        INSERT dbo.[Example]
        SELECT top 1000 1
        FROM sys.objects")

    It "Corrupt a single database" {
        Invoke-DbaDatabaseCorruption -SqlInstance $script:instance2 -Database $dbname -Confirm:$false | Select-Object -ExpandProperty Status | Should be "Corrupted"
    }

    It "Causes DBCC CHECKDB to fail" {
        $result = Start-DbccCheck -Server $Server -dbname $dbname
        $result | Should Not Be 'Success'
    }
}
tools\dbatools\tests\Invoke-DbaDbDecryptObject.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "UnitTests" {

    BeforeAll {
        # Get a random value for the database name
        $random = Get-Random

        # Setup the database name
        $dbname = "dbatoolsci_decrypt_$random"

        # Remove the database if it exists
        Remove-DbaDatabase -SqlInstance $script:instance1 -Database $dbname -Confirm:$false

        # Get a server object
        $server = Connect-DbaInstance -SqlInstance $script:instance1

        # Create the database
        $server = Connect-DbaInstance -SqlInstance $script:instance1
        $server.Query("CREATE DATABASE $dbname;")

        # Setup the code for the encrypted function
        $queryFunction = "
-- =============================================
-- Author:        Sander Stad
-- Description:   Dummy encrypted function to test the command
-- =============================================
CREATE FUNCTION DummyEncryptedFunction
(
    @param1 varchar(100)
)
RETURNS VARCHAR
WITH ENCRYPTION
AS
BEGIN
    -- Declare the return variable here
    DECLARE @ResultVar VARCHAR(100)

    -- Add the T-SQL statements to compute the return value here
    SELECT @ResultVar = 'Hello this is a test function' + @param1

    -- Return the result of the function
    RETURN @ResultVar

END
        "
        # Create the encrypted function
        $server.Databases[$dbname].Query($queryFunction)

        # Setup the query for the encrypted stored procedure
        $queryStoredProcedure = "
-- =============================================
-- Author:        Sander Stad
-- Description:   Dummy encrypted stored procedure to test the command
-- =============================================
CREATE PROCEDURE DummyEncryptedFunctionStoredProcedure
    @param1 VARCHAR(100)
WITH ENCRYPTION
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    -- Insert statements for procedure here
    SELECT @param1
END
        "

        # Create the encrypted stored procedure
        $server.Databases[$dbname].Query($queryStoredProcedure)

        # Check if DAC is enabled
        $config = Get-DbaSpConfigure -SqlInstance $script:instance1 -ConfigName RemoteDacConnectionsEnabled
        if ($config.ConfiguredValue -ne 1) {
            Set-DbaSpConfigure -SqlInstance $script:instance1 -ConfigName RemoteDacConnectionsEnabled -Value $true
        }
    }

    AfterAll {
        # Remove the database if it exists
        Remove-DbaDatabase -SqlInstance $script:instance1 -Database $dbname -Confirm:$false

        # Set the original configuration
        Set-DbaSpConfigure -SqlInstance $script:instance1 -ConfigName RemoteDacConnectionsEnabled -Value $config.ConfiguredValue -WarningAction SilentlyContinue
    }

    Context "DAC enabled" {
        # too much messing around punts appveyor
        It -Skip "Should throw error" {
            Set-DbaSpConfigure -SqlInstance $script:instance1 -Name RemoteDacConnectionsEnabled -Value $false
            Invoke-DbaDbDecryptObject -SqlInstance $script:instance1 -Database $dbname -ObjectName DummyEncryptedFunctionStoredProcedure -WarningVariable warn -WarningAction SilentlyContinue
            $error[0].Exception | Should -BeLike "*DAC is not enabled for instance*"
            Set-DbaSpConfigure -SqlInstance $script:instance1 -Name RemoteDacConnectionsEnabled -Value $true -WarningAction SilentlyContinue
        }
    }

    Context "Decrypt Function" {
        It -Skip "Should be successful" {
            $result = Invoke-DbaDbDecryptObject -SqlInstance $script:instance1 -Database $dbname -ObjectName DummyEncryptedFunction
            $result.Script | Should -Be $queryFunction

        }
    }

    Context "Decrypt Stored Procedure" {
        It -Skip "Should be successful" {
            $result = Invoke-DbaDbDecryptObject -SqlInstance $script:instance1 -Database $dbname -ObjectName DummyEncryptedFunctionStoredProcedure
            $result.Script | Should -Be $queryStoredProcedure

        }
    }
}
tools\dbatools\tests\Invoke-DbaDiagnosticQuery.Tests.ps1
# test ouput directory to confirm creation of test files
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $script:PesterOutputPath = "TestDrive:$commandName"
        $database = "dbatoolsci_frk_$(Get-Random)"
        $database2 = "dbatoolsci_frk_$(Get-Random)"
        $database3 = "dbatoolsci_frk_$(Get-Random)"
        $server = Connect-DbaInstance -SqlInstance $script:instance2
        $server.Query("CREATE DATABASE [$database]")
        $server.Query("CREATE DATABASE [$database2]")
        $server.Query("CREATE DATABASE [$database3]")
    }
    AfterAll {
        @($database, $database2, $database3) | Foreach-Object {
            $db = $_
            $server.Query("IF DB_ID('$db') IS NOT NULL
                begin
                    print 'Dropping $db'
                    ALTER DATABASE [$db] SET SINGLE_USER WITH ROLLBACK immediate;
                    DROP DATABASE [$db];
                end")
        }

        Remove-Item $script:PesterOutputPath -Recurse -ErrorAction SilentlyContinue
    }
    AfterEach {
        Remove-Item $script:PesterOutputPath -Recurse -ErrorAction SilentlyContinue
    }

    Context "verifying output when running queries" {
        It "runs a specific query" {
            $results = Invoke-DbaDiagnosticQuery -SqlInstance $script:instance2 -QueryName 'Memory Clerk Usage'
            @($results).Count | Should -Be 1
        }
        It "works with DatabaseSpecific" {
            $results = Invoke-DbaDiagnosticQuery -SqlInstance $script:instance2 -DatabaseSpecific
            @($results).Count | Should -BeGreaterThan 10
        }
        It "works with specific database provided" {
            $results = Invoke-DbaDiagnosticQuery -SqlInstance $script:instance2 -QueryName 'File Sizes and Space', 'Log Space Usage' -Database $database2, $database3
            @($results | Where-Object {$_.Database -eq $Database}).Count | Should -Be 0
            @($results | Where-Object {$_.Database -eq $Database2}).Count | Should -Be 2
            @($results | Where-Object {$_.Database -eq $Database3}).Count | Should -Be 2
        }
        It "works with Exclude Databases provided" {
            $results = Invoke-DbaDiagnosticQuery -SqlInstance $script:instance2 -DatabaseSpecific -ExcludeDatabase $database2
            @($results | Where-Object {$_.Database -eq $Database}).Count | Should -BeGreaterThan 1
            @($results | Where-Object {$_.Database -eq $Database2}).Count | Should -Be 0
        }

        $columnnames = 'Item', 'RowError', 'RowState', 'Table', 'ItemArray', 'HasErrors'
        $TestCases = @()
        $columnnames.ForEach{$TestCases += @{columnname = $PSItem}}
        $results = Invoke-DbaDiagnosticQuery -SqlInstance $script:instance2 -QueryName 'Memory Clerk Usage'
        It "correctly excludes default column name <columnname>" -TestCases $TestCases {
            Param($columnname)
            @($results.Result | Get-Member | Where-Object Name -eq $columnname).Count | Should be 0
        }
    }

    context "verifying output when exporting queries as files instead of running" {

        It "exports queries to sql files without running" {
            $null = Invoke-DbaDiagnosticQuery -SqlInstance $script:instance2 -ExportQueries -QueryName 'Memory Clerk Usage' -OutputPath $script:PesterOutputPath
            @(Get-ChildItem -path $script:PesterOutputPath -filter *.sql).Count | Should -Be 1
        }

        It "exports single database specific query against single database" {
            $null = Invoke-DbaDiagnosticQuery -SqlInstance $script:instance2  -ExportQueries  -DatabaseSpecific -QueryName 'Database-scoped Configurations' -Database $database -OutputPath $script:PesterOutputPath
            @(Get-ChildItem -path $script:PesterOutputPath -filter *.sql | Where-Object {$_.FullName -match "($database)"}).Count | Should -Be 1
        }

        It "exports a database specific query foreach specific database provided" {
            $null = Invoke-DbaDiagnosticQuery -SqlInstance $script:instance2  -ExportQueries  -DatabaseSpecific -QueryName 'Database-scoped Configurations' -Database @($database, $database2) -OutputPath $script:PesterOutputPath
            @(Get-ChildItem -path $script:PesterOutputPath -filter *.sql | Where-Object {$_.FullName -match "($database)|($database2)"}).Count | Should -Be 2
        }

        It "exports database specific query when multiple specific databases are referenced" {
            $null = Invoke-DbaDiagnosticQuery -SqlInstance $script:instance2 -ExportQueries -DatabaseSpecific -QueryName 'Database-scoped Configurations' -Database @($database, $database2) -OutputPath $script:PesterOutputPath
            @(Get-ChildItem -path $script:PesterOutputPath -filter *.sql | Where-Object {$_.FullName -match "($database)|($database2)"}).Count | Should -Be 2
        }

    }

    context "verifying output when running database specific queries" {
        It "runs database specific queries against single database only when providing database name" {
            $results = Invoke-DbaDiagnosticQuery -SqlInstance $script:instance2 -DatabaseSpecific -QueryName 'Database-scoped Configurations' -Database $database
            @($results).Count | Should -Be 1
        }

        It "runs database specific queries against set of databases when provided with multiple database names" {
            $results = Invoke-DbaDiagnosticQuery -SqlInstance $script:instance2 -DatabaseSpecific -QueryName 'Database-scoped Configurations' -Database @($database, $database2)
            @($results).Count |  Should -Be 2
        }
    }
}
tools\dbatools\tests\Invoke-DbaLogShipping.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    # This is a placeholder until we decide on sql2016/sql2017
    BeforeAll {
        $dbname = "dbatoolsci_logshipping"
    }

    It -Skip "returns success" {
        $results = Invoke-DbaLogShipping -SourceSqlInstance $script:instance2 -DestinationSqlInstance $script:instance -Database $dbname -BackupNetworkPath C:\temp -BackupLocalPath "C:\temp\logshipping\backup" -GenerateFullBackup -CompressBackup -SecondaryDatabaseSuffix "_LS" -Force
        $results.Status -eq 'Success' | Should Be $true
    }
}
tools\dbatools\tests\Invoke-DbaSqlQuery.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    It "supports pipable instances" {
        $results = $script:instance1, $script:instance2 | Invoke-DbaSqlQuery -Database tempdb -Query "Select 'hello' as TestColumn"
        foreach ($result in $results) {
            $result.TestColumn | Should -Be 'hello'
        }
    }
    It "supports parameters" {
        $sqlParams = @{testvalue = 'hello'}
        $results = $script:instance1 | Invoke-DbaSqlQuery -Database tempdb -Query "Select @testvalue as TestColumn" -SqlParameters $sqlParams
        foreach ($result in $results) {
            $result.TestColumn | Should -Be 'hello'
        }
    }
    It "supports AppendServerInstance" {
        $conn1 = Connect-DbaInstance $script:instance1
        $conn2 = Connect-DbaInstance $script:instance2
        $serverInstances = $conn1.Name, $conn2.Name
        $results = $script:instance1, $script:instance2 | Invoke-DbaSqlQuery -Database tempdb -Query "Select 'hello' as TestColumn" -AppendServerInstance
        foreach ($result in $results) {
            $result.ServerInstance | Should -Not -Be Null
            $result.ServerInstance | Should -BeIn $serverInstances
        }
    }
    It "supports pipable databases" {
        $dbs = Get-DbaDatabase -SqlInstance $script:instance1, $script:instance2
        $results = $dbs | Invoke-DbaSqlQuery -Query "Select 'hello' as TestColumn, DB_NAME() as dbname"
        foreach ($result in $results) {
            $result.TestColumn | Should -Be 'hello'
        }
        'tempdb' | Should -Bein $results.dbname
    }
    It "stops when piped databases and -Database" {
        $dbs = Get-DbaDatabase -SqlInstance $script:instance1, $script:instance2
        { $dbs | Invoke-DbaSqlQuery -Query "Select 'hello' as TestColumn, DB_NAME() as dbname" -Database tempdb -EnableException } | Should Throw "You can't"
    }
    It "supports reading files" {
        $testPath = "TestDrive:\dbasqlquerytest.txt"
        Set-Content $testPath -value "Select 'hello' as TestColumn, DB_NAME() as dbname"
        $results = Invoke-DbaSqlQuery -SqlInstance $script:instance1 -Database tempdb -File $testPath
        foreach ($result in $results) {
            $result.TestColumn | Should -Be 'hello'
        }
        'tempdb' | Should -Bein $results.dbname
    }
    It "supports reading entire directories, just *.sql" {
        $testPath = "TestDrive:\"
        Set-Content "$testPath\dbasqlquerytest.sql" -value "Select 'hello' as TestColumn, DB_NAME() as dbname"
        Set-Content "$testPath\dbasqlquerytest2.sql" -value "Select 'hello2' as TestColumn, DB_NAME() as dbname"
        Set-Content "$testPath\dbasqlquerytest2.txt" -value "Select 'hello3' as TestColumn, DB_NAME() as dbname"
        $pathinfo = Get-Item $testpath
        $results = Invoke-DbaSqlQuery -SqlInstance $script:instance1 -Database tempdb -File $pathinfo
        'hello' | Should -Bein $results.TestColumn
        'hello2' | Should -Bein $results.TestColumn
        'hello3' | Should -Not -Bein $results.TestColumn
        'tempdb' | Should -Bein $results.dbname

    }
    It "supports http files" {
        $cleanup = "IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[CommandLog]') AND type in (N'U')) DROP TABLE [dbo].[CommandLog]"
        $null = Invoke-DbaSqlQuery -SqlInstance $script:instance1 -Database tempdb -Query $cleanup
        $CloudQuery = 'https://raw.githubusercontent.com/sqlcollaborative/appveyor-lab/master/sql2016-startup/ola/CommandLog.sql'
        $null = Invoke-DbaSqlQuery -SqlInstance $script:instance1 -Database tempdb -File $CloudQuery
        $check = "SELECT name FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[CommandLog]') AND type in (N'U')"
        $results = Invoke-DbaSqlQuery -SqlInstance $script:instance1 -Database tempdb -Query $check
        $results.Name | Should -Be 'CommandLog'
        $null = Invoke-DbaSqlQuery -SqlInstance $script:instance1 -Database tempdb -Query $cleanup
    }
    It "supports smo objects" {
        $cleanup = "IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[CommandLog]') AND type in (N'U')) DROP TABLE [dbo].[CommandLog]"
        $null = Invoke-DbaSqlQuery -SqlInstance $script:instance1, $script:instance2 -Database tempdb -Query $cleanup
        $CloudQuery = 'https://raw.githubusercontent.com/sqlcollaborative/appveyor-lab/master/sql2016-startup/ola/CommandLog.sql'
        $null = Invoke-DbaSqlQuery -SqlInstance $script:instance1 -Database tempdb -File $CloudQuery
        $smoobj = Get-Dbatable -SqlInstance $script:instance1 -Database tempdb  | Where-Object Name -eq 'CommandLog'
        $null = Invoke-DbaSqlQuery -SqlInstance $script:instance2 -Database tempdb -SqlObject $smoobj
        $check = "SELECT name FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[CommandLog]') AND type in (N'U')"
        $results = Invoke-DbaSqlQuery -SqlInstance $script:instance2 -Database tempdb -Query $check
        $results.Name | Should Be 'CommandLog'
        $null = Invoke-DbaSqlQuery -SqlInstance $script:instance1, $script:instance2 -Database tempdb -Query $cleanup
    }
    <#
    It "supports loose objects (with SqlInstance and database props)" {
        $dbs = Get-DbaDatabaseState -SqlInstance $script:instance1, $script:instance2
        $results = $dbs | Invoke-DbaSqlQuery -Query "Select 'hello' as TestColumn, DB_NAME() as dbname"
        foreach ($result in $results) {
            $result.TestColumn | Should -Be 'hello'
        }
    }#>
    It "supports queries with GO statements" {
        $Query = @'
SELECT DB_NAME() as dbname
GO
SELECT @@servername as dbname
'@
        $results = $script:instance1, $script:instance2 | Invoke-DbaSqlQuery -Database tempdb -Query $Query
        $results.dbname -contains 'tempdb' | Should -Be $true
    }
    It "streams correctly 'messages' with Verbose" {
        $query = @'
        DECLARE @time char(19)
        PRINT 'stmt_1|PRINT start|' + CONVERT(VARCHAR(19), GETUTCDATE(), 126)
        SET @time= CONVERT(VARCHAR(19), GETUTCDATE(), 126)
        RAISERROR ('stmt_2|RAISERROR before WITHOUT NOWAIT|%s', 0, 1, @time)
        WAITFOR DELAY '00:00:03'
        PRINT 'stmt_3|PRINT after the first delay|' + CONVERT(VARCHAR(19), GETUTCDATE(), 126)
        SET @time= CONVERT(VARCHAR(19), GETUTCDATE(), 126)
        RAISERROR ('stmt_4|RAISERROR with NOWAIT|%s', 0, 1, @time) WITH NOWAIT
        WAITFOR DELAY '00:00:03'
        PRINT 'stmt_5|PRINT after the second delay|' + CONVERT(VARCHAR(19), GETUTCDATE(), 126)
        SELECT 'hello' AS TestColumn
        WAITFOR DELAY '00:00:03'
        PRINT 'stmt_6|PRINT end|' + CONVERT(VARCHAR(19), GETUTCDATE(), 126)
'@
        $results = @()
        Invoke-DbaSqlQuery -SqlInstance $script:instance1 -Database tempdb -Query $query -Verbose 4>&1 | ForEach-Object {
            $results += [pscustomobject]@{
                FiredAt = (Get-Date).ToUniversalTime()
                Out = $_
            }
        }
        $results.Length | Should -Be 7  # 6 'messages' plus the actual resultset
        ($results  | ForEach-Object { Get-Date -Date $_.FiredAt -f s } | Get-Unique).Count  | Should -Not -Be 1 # the first WITH NOWAIT (stmt_4) and after
        #($results[0..3]  | ForEach-Object { Get-Date -Date $_.FiredAt -f s } | Get-Unique).Count | Should -Be 1 # everything before stmt_4 is fired at the same time
        #$parsedstmt_1 = Get-Date -Date $results[0].Out.Message.split('|')[2]
        #(Get-Date -Date (Get-Date -Date $parsedstmt_1).AddSeconds(3) -f s) | Should -Be (Get-Date -Date $results[0].FiredAt -f s) # stmt_1 is fired 3 seconds after the logged date
        #$parsedstmt_4 = Get-Date -Date $results[3].Out.Message.split('|')[2]
        #(Get-Date -Date (Get-Date -Date $parsedstmt_4) -f s) | Should -Be (Get-Date -Date $results[0].FiredAt -f s) # stmt_4 is fired at the same time the logged date is
    }
    It "streams correctly 'messages' with MessagesToOutput" {
        $query = @'
        DECLARE @time char(19)
        PRINT 'stmt_1|PRINT start|' + CONVERT(VARCHAR(19), GETUTCDATE(), 126)
        SET @time= CONVERT(VARCHAR(19), GETUTCDATE(), 126)
        RAISERROR ('stmt_2|RAISERROR before WITHOUT NOWAIT|%s', 0, 1, @time)
        WAITFOR DELAY '00:00:03'
        PRINT 'stmt_3|PRINT after the first delay|' + CONVERT(VARCHAR(19), GETUTCDATE(), 126)
        SET @time= CONVERT(VARCHAR(19), GETUTCDATE(), 126)
        RAISERROR ('stmt_4|RAISERROR with NOWAIT|%s', 0, 1, @time) WITH NOWAIT
        WAITFOR DELAY '00:00:03'
        PRINT 'stmt_5|PRINT after the second delay|' + CONVERT(VARCHAR(19), GETUTCDATE(), 126)
        SELECT 'hello' AS TestColumn
        WAITFOR DELAY '00:00:03'
        PRINT 'stmt_6|PRINT end|' + CONVERT(VARCHAR(19), GETUTCDATE(), 126)
'@
        $results = @()
        Invoke-DbaSqlQuery -SqlInstance $script:instance1 -Database tempdb -Query $query -MessagesToOutput | ForEach-Object {
            $results += [pscustomobject]@{
                FiredAt = (Get-Date).ToUniversalTime()
                Out = $_
            }
        }
        $results.Length | Should -Be 7  # 6 'messages' plus the actual resultset
        ($results  | ForEach-Object { Get-Date -Date $_.FiredAt -f s } | Get-Unique).Count  | Should -Not -Be 1 # the first WITH NOWAIT (stmt_4) and after
    }
}
tools\dbatools\tests\Invoke-SqlCmd2.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    $results = Invoke-SqlCmd2 -ServerInstance $script:instance1 -Database tempdb -Query "Select 'hello' as TestColumn"
    It "returns a datatable" {
        $results.GetType().Name -eq "DataRow" | Should Be $true
    }

    It "returns the proper result" {
        $results.TestColumn -eq 'hello' | Should Be $true
    }

    $results = Invoke-SqlCmd2 -SqlInstance $script:instance1 -Database tempdb -Query "Select 'hello' as TestColumn"
    It "supports SQL instance param" {
        $results.TestColumn -eq 'hello' | Should Be $true
    }
}
tools\dbatools\tests\manual.pester.ps1
<#
    .SYNOPSIS
        Runs dbatools tests.

    .DESCRIPTION
        This is an helper to automate running tests locally

    .PARAMETER Path
        The Path to the test files to run. It accepts multiple test file paths passed in (e.g. .\Find-DbaOrphanedFile.Tests.ps1) as well
        as simple strings (e.g. "orphaned" will run all files matching .\*orphaned*.Tests.ps1)

    .PARAMETER Show
        Gets passed down to Pester's -Show parameter (useful if you want to reduce verbosity)

    .PARAMETER PassThru
        Gets passed down to Pester's -PassThru parameter (useful if you want to return an object to analyze)

    .PARAMETER TestIntegration
        dbatools's suite has unittests and integrationtests. This switch enables IntegrationTests, which need live instances
        see constants.ps1 for customizations

    .PARAMETER Coverage
        Enables measuring code coverage on the tested function

    .PARAMETER DependencyCoverage
        Enables measuring code coverage also of "lower level" (i.e. called) functions

    .PARAMETER ScriptAnalyzer
        Enables checking the called function's code with Invoke-ScriptAnalyzer, with dbatools's profile

    .EXAMPLE
        .\manual.pester.ps1 -Path Find-DbaOrphanedFile.Tests.ps1 -TestIntegration -Coverage -DependencyCoverage -ScriptAnalyzer

        The most complete number of checks:
          - Runs both unittests and integrationtests
          - Gathers and shows code coverage measurement for Find-DbaOrphanedFile and all its dependencies
          - Checks Find-DbaOrphanedFile with Invoke-ScriptAnalyzer

    .EXAMPLE
        .\manual.pester.ps1 -Path Find-DbaOrphanedFile.Tests.ps1

        Runs unittests stored in Find-DbaOrphanedFile.Tests.ps1

    .EXAMPLE
        .\manual.pester.ps1 -Path Find-DbaOrphanedFile.Tests.ps1 -PassThru

        Runs unittests stored in Find-DbaOrphanedFile.Tests.ps1 and returns an object that can be analyzed

    .EXAMPLE
        .\manual.pester.ps1 -Path orphan

        Runs unittests for all tests matching in `*orphan*.Tests.ps1

    .EXAMPLE
        .\manual.pester.ps1 -Path Find-DbaOrphanedFile.Tests.ps1 -Show Default

        Runs unittests stored in Find-DbaOrphanedFile.Tests.ps1, with reduced verbosity

    .EXAMPLE
        .\manual.pester.ps1 -Path Find-DbaOrphanedFile.Tests.ps1 -TestIntegration

        Runs both unittests and integrationtests stored in Find-DbaOrphanedFile.Tests.ps1

    .EXAMPLE
        .\manual.pester.ps1 -Path Find-DbaOrphanedFile.Tests.ps1 -TestIntegration -Coverage

        Gathers and shows code coverage measurement for Find-DbaOrphanedFile

    .EXAMPLE
        .\manual.pester.ps1 -Path Find-DbaOrphanedFile.Tests.ps1 -TestIntegration -Coverage -DependencyCoverage

        Gathers and shows code coverage measurement for Find-DbaOrphanedFile and all its dependencies

#>

[CmdletBinding()]
param (
    [string[]]
    $Path,

    [ValidateSet('None', 'Default', 'Passed', 'Failed', 'Pending', 'Skipped', 'Inconclusive', 'Describe', 'Context', 'Summary', 'Header', 'All', 'Fails')]
    [string]
    $Show = "All",

    [switch]
    $PassThru,

    [switch]
    $TestIntegration,

    [switch]
    $Coverage,

    [switch]
    $DependencyCoverage,

    [switch]
    $ScriptAnalyzer
)

$HasScriptAnalyzer = $null -ne (Get-Command Invoke-ScriptAnalyzer -ErrorAction SilentlyContinue).Version
$MinimumPesterVersion = [Version] '3.4.5.0' # Because this is when -Show was introduced
$PesterVersion = (Get-Command Invoke-Pester -ErrorAction SilentlyContinue).Version
$HasPester = $null -ne $PesterVersion

if (!($HasScriptAnalyzer)) {
    Write-Warning "Please install PSScriptAnalyzer"
    Write-Warning "     Install-Module -Name PSScriptAnalyzer"
    Write-Warning "     or go to https://github.com/PowerShell/PSScriptAnalyzer"
}
if (!($HasPester)) {
    Write-Warning "Please install Pester"
    Write-Warning "     Install-Module -Name Pester -Force -SkipPublisherCheck"
    Write-Warning "     or go to https://github.com/pester/Pester"
}
if ($PesterVersion -lt $MinimumPesterVersion) {
    Write-Warning "Please update Pester to at least 3.4.5"
    Write-Warning "     Install-Module -Name Pester -Force -SkipPublisherCheck"
    Write-Warning "     or go to https://github.com/pester/Pester"
}

if (($HasPester -and $HasScriptAnalyzer -and ($PesterVersion -ge $MinimumPesterVersion)) -eq $false) {
    Write-Warning "Exiting..."
    return
}

$ModuleBase = Split-Path -Path $PSScriptRoot -Parent

if (-not(Test-Path "$ModuleBase\.git" -Type Container)) {
    New-Item -Type Container -Path "$ModuleBase\.git"
}

#removes previously imported dbatools, if any
Remove-Module dbatools -ErrorAction Ignore
#imports the module making sure DLL is loaded ok
Import-Module "$ModuleBase\dbatools.psd1" -DisableNameChecking
#imports the psm1 to be able to use internal functions in tests
Import-Module "$ModuleBase\dbatools.psm1" -DisableNameChecking

$ScriptAnalyzerRulesExclude = @('PSUseOutputTypeCorrectly', 'PSAvoidUsingPlainTextForPassword', 'PSUseBOMForUnicodeEncodedFile')

$testInt = $false
if ($config_TestIntegration) {
    $testInt = $true
}
if ($TestIntegration) {
    $testInt = $true
}

function Get-CoverageIndications($Path, $ModuleBase) {
    # takes a test file path and figures out what to analyze for coverage (i.e. dependencies)
    $CBHRex = [regex]'(?smi)<#(.*)#>'
    $everything = (Get-Module dbatools).ExportedCommands.Values
    $everyfunction = $everything.Name
    $funcs = @()
    $leaf = Split-Path $path -Leaf
    # assuming Get-DbaFoo.Tests.ps1 wants coverage for "Get-DbaFoo"
    # but allowing also Get-DbaFoo.one.Tests.ps1 and Get-DbaFoo.two.Tests.ps1
    $func_name += ($leaf -replace '^([^.]+)(.+)?.Tests.ps1', '$1')
    if ($func_name -in $everyfunction) {
        $funcs += $func_name
        $f = $everything | Where-Object Name -eq $func_name
        $source = $f.Definition
        $CBH = $CBHRex.match($source).Value
        $cmdonly = $source.Replace($CBH, '')
        foreach ($e in $everyfunction) {
            # hacky, I know, but every occurrence of any function plus a space kinda denotes usage !?
            $searchme = "$e "
            if ($cmdonly.contains($searchme)) {
                $funcs += $e
            }
        }
    }
    $testpaths = @()
    $allfiles = Get-ChildItem -File -Path "$ModuleBase\internal\functions", "$ModuleBase\functions" -Filter '*.ps1'
    foreach ($f in $funcs) {
        # exclude always used functions ?!
        if ($f -in ('Connect-SqlInstance', 'Select-DefaultView', 'Stop-Function', 'Write-Message')) { continue }
        # can I find a correspondence to a physical file (again, on the convenience of having Get-DbaFoo.ps1 actually defining Get-DbaFoo)?
        $res = $allfiles | Where-Object { $_.Name.Replace('.ps1', '') -eq $f }
        if ($res.count -gt 0) {
            $testpaths += $res.FullName
        }
    }
    return @() + ($testpaths | Select-Object -Unique)
}

$files = @()

if ($Path) {
    foreach ($item in $path) {
        if (Test-Path $item) {
            $files += Get-ChildItem -Path $item
        }
        else {
            $files += Get-ChildItem -Path "$ModuleBase\tests\*$item*.Tests.ps1"
        }
    }
}

if ($files.Length -eq 0) {
    Write-Warning "No tests to be run"
}

$AllTestsWithinScenario = $files

foreach ($f in $AllTestsWithinScenario) {
    $PesterSplat = @{
        'Script'   = $f.FullName
        'Show'     = $show
        'PassThru' = $passThru
    }
    #opt-in
    $HeadFunctionPath = $f.FullName

    if ($Coverage -or $ScriptAnalyzer) {
        $CoverFiles = Get-CoverageIndications -Path $f -ModuleBase $ModuleBase
        $HeadFunctionPath = $CoverFiles | Select-Object -First 1
    }
    if ($Coverage) {
        if ($DependencyCoverage) {
            $CoverFilesPester = $CoverFiles
        }
        else {
            $CoverFilesPester = $HeadFunctionPath
        }
        $PesterSplat['CodeCoverage'] = $CoverFilesPester
    }
    if (!($testInt)) {
        $PesterSplat['ExcludeTag'] = "IntegrationTests"
    }
    Invoke-Pester @PesterSplat
    if ($ScriptAnalyzer) {
        if ($Show -ne "None") {
            Write-Host -ForegroundColor green -Object "ScriptAnalyzer check for $HeadFunctionPath"
        }
        Invoke-ScriptAnalyzer -Path $HeadFunctionPath -ExcludeRule $ScriptAnalyzerRulesExclude
    }
}
tools\dbatools\tests\Measure-DbaBackupThroughput.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    Context "Returns output for single database" {
        BeforeAll {
            $server = Connect-DbaInstance -SqlInstance $script:instance2
            $random = Get-Random
            $db = "dbatoolsci_measurethruput$random"
            $server.Query("CREATE DATABASE $db")
            $null = Get-DbaDatabase -SqlInstance $server -Database $db | Backup-DbaDatabase
        }
        AfterAll {
            $null = Get-DbaDatabase -SqlInstance $server -Database $db | Remove-DbaDatabase -Confirm:$false
        }

        $results = Measure-DbaBackupThroughput -SqlInstance $server -Database $db
        It "Should return just one backup" {
            $results.Database.Count -eq 1 | Should Be $true
        }
    }
}
tools\dbatools\tests\Mount-DbaDatabase.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {

    Context "Setup removes, restores and backups on the local drive for Mount-DbaDatabase" {
        $null = Get-DbaDatabase -SqlInstance $script:instance1 -Database detachattach | Remove-DbaDatabase -Confirm:$false
        $null = Restore-DbaDatabase -SqlInstance $script:instance1 -Path $script:appveyorlabrepo\detachattach\detachattach.bak -WithReplace
        $null = Get-DbaDatabase -SqlInstance $script:instance1 -Database detachattach | Backup-DbaDatabase -Type Full
        $null = Detach-DbaDatabase -SqlInstance $script:instance1 -Database detachattach -Force
    }

    Context "Attaches a single database and tests to ensure the alias still exists" {
        $results = Attach-DbaDatabase -SqlInstance $script:instance1 -Database detachattach

        It "Should return success" {
            $results.AttachResult | Should Be "Success"
        }

        It "Should return that the database is only Database" {
            $results.Database | Should Be "detachattach"
        }

        It "Should return that the AttachOption default is None" {
            $results.AttachOption | Should Be "None"
        }
    }

    $null = Get-DbaDatabase -SqlInstance $script:instance1 -Database detachattach | Remove-DbaDatabase -Confirm:$false
}
tools\dbatools\tests\Move-DbaRegisteredServer.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tag "IntegrationTests" {
    Context "Setup" {
        BeforeAll {
            $srvName = "dbatoolsci-server1"
            $group = "dbatoolsci-group1"
            $regSrvName = "dbatoolsci-server12"
            $regSrvDesc = "dbatoolsci-server123"
            
            $newGroup = Add-DbaRegisteredServerGroup -SqlInstance $script:instance1 -Name $group
            $newServer = Add-DbaRegisteredServer -SqlInstance $script:instance1 -ServerName $srvName -Name $regSrvName -Description $regSrvDesc -Group $newGroup.Name
            
            $srvName2 = "dbatoolsci-server2"
            $group2 = "dbatoolsci-group1a"
            $regSrvName2 = "dbatoolsci-server21"
            $regSrvDesc2 = "dbatoolsci-server321"
            
            $newGroup2 = Add-DbaRegisteredServerGroup -SqlInstance $script:instance1 -Name $group2
            $newServer2 = Add-DbaRegisteredServer -SqlInstance $script:instance1 -ServerName $srvName2 -Name $regSrvName2 -Description $regSrvDesc2
            
            $regSrvName3 = "dbatoolsci-server3"
            $srvName3 = "dbatoolsci-server3"
            $regSrvDesc3 = "dbatoolsci-server3desc"
            
            $newServer3 = Add-DbaRegisteredServer -SqlInstance $script:instance1 -ServerName $srvName3 -Name $regSrvName3 -Description $regSrvDesc3
        }
        AfterAll {
            Get-DbaRegisteredServer -SqlInstance $script:instance1 -Name $regSrvName, $regSrvName2, $regSrvName3 | Remove-DbaRegisteredServer -Confirm:$false
            Get-DbaRegisteredServerGroup -SqlInstance $script:instance1 -Group $group, $group2 | Remove-DbaRegisteredServerGroup -Confirm:$false
        }
        
        It "moves a piped server" {
            $results = $newServer2 | Move-DbaRegisteredServer -NewGroup $newGroup.Name
            $results.Parent.Name | Should -Be $newGroup.Name
            $results.Name | Should -Be $regSrvName2
        }
        
        It "moves a manually specified server" {
            $results = Move-DbaRegisteredServer -SqlInstance $script:instance1 -ServerName $srvName3 -NewGroup $newGroup2.Name
            $results.Parent.Name | Should -Be $newGroup2.Name
            $results.Description | Should -Be $regSrvDesc3
        }
    }
}
tools\dbatools\tests\Move-DbaRegisteredServerGroup.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tag "IntegrationTests" {
    Context "Setup" {
        BeforeAll {
            $srvName = "dbatoolsci-server1"
            $group = "dbatoolsci-group1"
            $regSrvName = "dbatoolsci-server12"
            $regSrvDesc = "dbatoolsci-server123"
            
            $newGroup = Add-DbaRegisteredServerGroup -SqlInstance $script:instance1 -Name $group
            $newServer = Add-DbaRegisteredServer -SqlInstance $script:instance1 -ServerName $srvName -Name $regSrvName -Description $regSrvDesc -Group $newGroup.Name
            
            $group2 = "dbatoolsci-group1a"
            $newGroup2 = Add-DbaRegisteredServerGroup -SqlInstance $script:instance1 -Name $group2
            
            $group3 = "dbatoolsci-group1b"
            $newGroup3 = Add-DbaRegisteredServerGroup -SqlInstance $script:instance1 -Name $group3
          }
        AfterAll {
            Get-DbaRegisteredServer -SqlInstance $script:instance1 -Name $regSrvName  | Remove-DbaRegisteredServer -Confirm:$false
            Get-DbaRegisteredServerGroup -SqlInstance $script:instance1 -Group $group, $group2, $group3 | Remove-DbaRegisteredServerGroup -Confirm:$false
        }
        
        It "moves a piped group" {
            $results = $newGroup2, $newGroup3 | Move-DbaRegisteredServerGroup -NewGroup $newGroup.Name
            $results.Parent.Name | Should -Be $newGroup.Name, $newGroup.Name
        }
        
        It "moves a manually specified group" {
            $results = Move-DbaRegisteredServerGroup -SqlInstance $script:instance1 -Group "$group\$group3" -NewGroup Default
            $results.Parent.Name | Should -Be 'DatabaseEngineServerGroup'
        }
    }
}
tools\dbatools\tests\New-DbaAgentJob.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    Context "New Agent JOb is added properly" {

        It "Should have the right name and description" {
            $results = New-DbaAgentJob -SqlInstance $script:instance2 -Job "Job One" -Description "Just another job"
            $results.Name | Should Be "Job One"
            $results.Description | Should Be "Just another job"
        }

        It "Should actually for sure exist" {
            $newresults = Get-DbaAgentJob -SqlInstance $script:instance2 -Job "Job One"
            $newresults.Name | Should Be "Job One"
            $newresults.Description | Should Be "Just another job"
        }

        It "Should not write over existing jobs" {
            $results = New-DbaAgentJob -SqlInstance $script:instance2 -Job "Job One" -Description "Just another job" -WarningAction SilentlyContinue -WarningVariable warn
            $warn -match "already exists" | Should Be $true
        }

        # Cleanup and ignore all output
        Remove-DbaAgentJob -SqlInstance $script:instance2 -Job "Job One" *> $null
    }
}
tools\dbatools\tests\New-DbaAgentJobCategory.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    Context "New Agent Job Category is added properly" {

        It "Should have the right name and category type" {
            $results = New-DbaAgentJobCategory -SqlInstance $script:instance2 -Category CategoryTest1
            $results.Name | Should Be "CategoryTest1"
            $results.CategoryType | Should Be "LocalJob"
        }

        It "Should have the right name and category type" {
            $results = New-DbaAgentJobCategory -SqlInstance $script:instance2 -Category CategoryTest2 -CategoryType MultiServerJob
            $results.Name | Should Be "CategoryTest2"
            $results.CategoryType | Should Be "MultiServerJob"
        }

        It "Should actually for sure exist" {
            $newresults = Get-DbaAgentJobCategory -SqlInstance $script:instance2 -Category CategoryTest1, CategoryTest2
            $newresults[0].Name | Should Be "CategoryTest1"
            $newresults[0].CategoryType | Should Be "LocalJob"
            $newresults[1].Name | Should Be "CategoryTest2"
            $newresults[1].CategoryType | Should Be "MultiServerJob"
        }

        It "Should not write over existing job categories" {
            $results = New-DbaAgentJobCategory -SqlInstance $script:instance2 -Category CategoryTest1 -WarningAction SilentlyContinue -WarningVariable warn
            $warn -match "already exists" | Should Be $true
        }

        # Cleanup and ignore all output
        Remove-DbaAgentJobCategory -SqlInstance $script:instance2 -Category CategoryTest1, CategoryTest2 *> $null
    }
}
tools\dbatools\tests\New-DbaAgentJobStep.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    Context "New Agent Job Step is added properly" {

        # Create job to add step to
        $job = New-DbaAgentJob -SqlInstance $script:instance2 -Job "Job One" -Description "Just another job"

        It "Should have the right name and description" {
            $results = New-DbaAgentJobStep -SqlInstance $script:instance2 -Job $job -StepName "Step One"
            $results.Name | Should Be "Step One"
        }

        It "Should actually for sure exist" {
            $newresults = Get-DbaAgentJob -SqlInstance $script:instance2 -Job "Job One"
            $newresults.JobSteps.Name | Should Be "Step One"
        }

        It "Should not write over existing job steps" {
            New-DbaAgentJobStep -SqlInstance $script:instance2 -Job "Job One" -StepName "Step One" -WarningAction SilentlyContinue -WarningVariable warn
            $warn -match "already exists" | Should Be $true
        }

        # Cleanup and ignore all output
        Remove-DbaAgentJob -SqlInstance $script:instance2 -Job "Job One" *> $null
    }
}
tools\dbatools\tests\New-DbaAgentProxy.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

# This is quite light of a test but setting up a proxy requires a lot of setup and I don't have time today
Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "does not try to add without" {
        $results = New-DbaAgentProxy -SqlInstance $script:instance2 -Name STIG -Credential 'dbatoolsci_proxytest' -WarningAction SilentlyContinue -WarningVariable warn
        It "does not try to add the proxy without a valid credential" {
            $warn -match 'does not exist' | Should Be $true
        }
    }
}
tools\dbatools\tests\New-DbaClientAlias.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {

    Context "adds the alias" {
        $results = New-DbaClientAlias -ServerName sql2016 -Alias dbatoolscialias-new -Verbose:$false
        It "returns accurate information" {
            $results.AliasName | Should Be dbatoolscialias-new, dbatoolscialias-new
        }
        $results | Remove-DbaClientAlias
    }
}
tools\dbatools\tests\New-DbaComputerCertificate.Tests.ps1
if (-not $env:appveyor) {
    $CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
    Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
    . "$PSScriptRoot\constants.ps1"

    Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
        Context "Can generate a new certificate" {
            BeforeAll {
                $cert = New-DbaComputerCertificate -SelfSigned -EnableException
            }
            AfterAll {
                Remove-DbaComputerCertificate -Thumbprint $cert.Thumbprint -Confirm:$false
            }
            It "returns the right EnhancedKeyUsageList" {
                "$($cert.EnhancedKeyUsageList)" -match '1\.3\.6\.1\.5\.5\.7\.3\.1' | Should Be $true
            }
            It "returns the right FriendlyName" {
                "$($cert.FriendlyName)" -match 'SQL Server' | Should Be $true
            }
        }
    }
}
tools\dbatools\tests\New-DbaCredential.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"
. "$PSScriptRoot\..\internal\functions\Invoke-Command2.ps1"

Describe "$CommandName Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        $logins = "dbatoolsci_thor", "dbatoolsci_thorsmomma"
        $plaintext = "BigOlPassword!"
        $password = ConvertTo-SecureString $plaintext -AsPlainText -Force
        
        # Add user
        foreach ($login in $logins) {
            $null = Invoke-Command2 -ScriptBlock { net user $args[0] $args[1] /add *>&1 } -ArgumentList $login, $plaintext -ComputerName $script:instance2
        }
    }
    AfterAll {
        try {
            (Get-DbaCredential -SqlInstance $script:instance2 -Identity dbatoolsci_thor, dbatoolsci_thorsmomma -ErrorAction Stop -WarningAction SilentlyContinue).Drop()
        }
        catch { }
        
        foreach ($login in $logins) {
            $null = Invoke-Command2 -ScriptBlock { net user $args /delete *>&1 } -ArgumentList $login -ComputerName $script:instance2
            $null = Invoke-Command2 -ScriptBlock { net user $args /delete *>&1 } -ArgumentList $login -ComputerName $script:instance2
        }
    }
    
    Context "Create a new credential" {
        It "Should create new credentials with the proper properties" {
            $results = New-DbaCredential -SqlInstance $script:instance2 -Name dbatoolsci_thorcred -Identity dbatoolsci_thor -Password $password
            $results.Name | Should Be "dbatoolsci_thorcred"
            $results.Identity | Should Be "dbatoolsci_thor"

            $results = New-DbaCredential -SqlInstance $script:instance2 -Identity dbatoolsci_thorsmomma -Password $password
            $results | Should Not Be $null
        }
        It "Gets the newly created credential" {
            $results = Get-DbaCredential -SqlInstance $script:instance2 -Identity dbatoolsci_thorsmomma
            $results.Name | Should Be "dbatoolsci_thorsmomma"
            $results.Identity | Should Be "dbatoolsci_thorsmomma"
        }
    }
}
tools\dbatools\tests\New-DbaDbCertificate.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Can create a database certificate" {
        BeforeAll {
            if (-not (Get-DbaDatabaseMasterKey -SqlInstance $script:instance1 -Database master)) {
                $masterkey = New-DbaDatabaseMasterKey -SqlInstance $script:instance1 -Database master -Password $(ConvertTo-SecureString -String "GoodPass1234!" -AsPlainText -Force) -Confirm:$false
            }

            $tempdbmasterkey = New-DbaDatabasemasterKey -SqlInstance $script:instance1 -Database tempdb -Password $(ConvertTo-SecureString -String "GoodPass1234!" -AsPlainText -Force) -Confirm:$false
            $certificateName1 = "Cert_$(Get-random)"
            $certificateName2 = "Cert_$(Get-random)"
        }
        AfterAll {
            if ($tempdbmasterkey) { $tempdbmasterkey | Remove-DbaDatabaseMasterKey -Confirm:$false }
            if ($masterKey) { $masterkey | Remove-DbaDatabasemasterKey -Confirm:$false }
        }

        $cert1 = New-DbaDbCertificate -SqlInstance $script:instance1 -Name $certificateName1
        It "Successfully creates a new database certificate in default, master database" {
            "$($cert1.name)" -match $certificateName1 | Should Be $true
        }

        $cert2 = New-DbaDbCertificate -SqlInstance $script:instance1 -Name $certificateName2 -Database tempdb
        It "Successfully creates a new database certificate in the tempdb database" {
            "$($cert2.Database)" -match "tempdb" | Should Be $true
        }

        $null = $cert1 | Remove-DbaDbCertificate -Confirm:$false
        $null = $cert2 | Remove-DbaDbCertificate -Confirm:$false
    }
}
tools\dbatools\tests\New-DbaDbSnapshot.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

# Targets only instance2 because it's the only one where Snapshots can happen
Describe "$CommandName Integration Tests" -Tag "IntegrationTests" {
    Context "Parameter validation" {
        It "Stops if no Database or AllDatabases" {
            { New-DbaDbSnapshot -SqlInstance $script:instance2 -EnableException -WarningAction SilentlyContinue } | Should Throw "You must specify"
        }
        It "Is nice by default" {
            { New-DbaDbSnapshot -SqlInstance $script:instance2 *> $null -WarningAction SilentlyContinue } | Should Not Throw "You must specify"
        }
    }
    
    Context "Operations on not supported databases" {
        It "Doesn't support model, master or tempdb" {
            $result = New-DbaDbSnapshot -SqlInstance $script:instance2 -EnableException -Database model, master, tempdb -WarningAction SilentlyContinue
            $result | Should Be $null
        }
    }
    
    Context "Operations on databases" {
        BeforeAll {
            $server = Connect-DbaInstance -SqlInstance $script:instance2
            Get-DbaProcess -SqlInstance $script:instance2 | Where-Object Program -match dbatools | Stop-DbaProcess -Confirm:$false -WarningAction SilentlyContinue
            $db1 = "dbatoolsci_SnapMe"
            $db2 = "dbatoolsci_SnapMe2"
            $db3 = "dbatoolsci_SnapMe3_Offline"
            $server.Query("CREATE DATABASE $db1")
            $server.Query("CREATE DATABASE $db2")
            $server.Query("CREATE DATABASE $db3")
        }
        AfterAll {
            Remove-DbaDbSnapshot -SqlInstance $script:instance2 -Database $db1, $db2, $db3 -Confirm:$false
            Remove-DbaDatabase -Confirm:$false -SqlInstance $script:instance2 -Database $db1, $db2, $db3
        }
        
        It "Skips over offline databases nicely" {
            $server.Query("ALTER DATABASE $db3 SET OFFLINE WITH ROLLBACK IMMEDIATE")
            $result = New-DbaDbSnapshot -SqlInstance $script:instance2 -EnableException -Database $db3
            $result | Should Be $null
            $server.Query("ALTER DATABASE $db3 SET ONLINE WITH ROLLBACK IMMEDIATE")
        }
        
        It "Refuses to accept multiple source databases with a single name target" {
            { New-DbaDbSnapshot -SqlInstance $script:instance2 -EnableException -Database $db1, $db2 -Name "dbatools_Snapped" -WarningAction SilentlyContinue } | Should Throw
        }
        
        It "Halts when path is not accessible" {
            { New-DbaDbSnapshot -SqlInstance $script:instance2 -Database $db1 -Path B:\Funnydbatoolspath -EnableException -WarningAction SilentlyContinue } | Should Throw
        }
        
        It "Creates snaps for multiple dbs by default" {
            $results = New-DbaDbSnapshot -SqlInstance $script:instance2 -EnableException -Database $db1, $db2
            $results | Should Not Be $null
            foreach ($result in $results) {
                $result.SnapshotOf -in @($db1, $db2) | Should Be $true
            }
        }
        
        It "Creates snap with the correct name" {
            $result = New-DbaDbSnapshot -SqlInstance $script:instance2 -EnableException -Database $db1 -Name "dbatools_SnapMe_right"
            $result | Should Not Be $null
            $result.SnapshotOf | Should Be $db1
            $result.Name | Should Be "dbatools_SnapMe_right"
        }
        
        It "Creates snap with the correct name template" {
            $result = New-DbaDbSnapshot -SqlInstance $script:instance2 -EnableException -Database $db2 -NameSuffix "dbatools_SnapMe_{0}_funny"
            $result | Should Not Be $null
            $result.SnapshotOf | Should Be $db2
            $result.Name | Should Be ("dbatools_SnapMe_{0}_funny" -f $db2)
        }
        
        It "has the correct default properties" {
            $result = Get-DbaDbSnapshot -SqlInstance $script:instance2 -Database $db2 | Select-Object -First 1
            $ExpectedPropsDefault = 'ComputerName', 'CreateDate', 'InstanceName', 'Name', 'SnapshotOf', 'SqlInstance','DiskUsage'
            ($result.PSStandardMembers.DefaultDisplayPropertySet.ReferencedPropertyNames | Sort-Object) | Should Be ($ExpectedPropsDefault | Sort-Object)
        }
    }
}
tools\dbatools\tests\New-DbaLogin.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"
. "$PSScriptRoot\..\internal\functions\Connect-SqlInstance.ps1"
. "$PSScriptRoot\..\internal\functions\Get-PasswordHash.ps1"
. "$PSScriptRoot\..\internal\functions\Convert-HexStringToByte.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {

    $credLogin = 'credologino'
    $certificateName = 'DBAToolsPesterlogincertificate'
    $password = 'MyV3ry$ecur3P@ssw0rd'
    $securePassword = ConvertTo-SecureString $password -AsPlainText -Force
    $sid = '0xDBA700131337C0D30123456789ABCDEF'
    $server1 = Connect-SqlInstance -SqlInstance $script:instance1
    $server2 = Connect-SqlInstance -SqlInstance $script:instance2
    $servers = @($server1, $server2)
    $computerName = $server1.NetName
    $winLogin = "$computerName\$credLogin"
    $logins = "claudio", "port", "tester", "certifico", $winLogin
    
    #cleanup
    try {
        foreach ($instance in $servers) {
            foreach ($login in $logins) {
                if ($l = Get-DbaLogin -SqlInstance $instance -Login $login) {
                    $results = $instance.Query("IF EXISTS (SELECT * FROM sys.server_principals WHERE name = '$login') EXEC sp_who '$login'")
                    foreach ($spid in $results.spid) {
                        $null = $instance.Query("kill $spid")
                    }
                    if ($c = $l.EnumCredentials()) {
                        $l.DropCredential($c)
                    }
                    $l.Drop()
                }
            }
        }
    }
    catch {<#nbd#> }
    
    #create Windows login
    $computer = [ADSI]"WinNT://$computerName"
    try {
        $user = [ADSI]"WinNT://$computerName/$credLogin,user"
        if ($user.Name -eq $credLogin) {
            $computer.Delete('User', $credLogin)
        }
    }
    catch {<#User does not exist#>}

    $user = $computer.Create("user", $credLogin)
    $user.SetPassword($password)
    $user.SetInfo()

    #create credential
    $null = New-DbaCredential -SqlInstance $server1 -Name $credLogin -CredentialIdentity $credLogin -Password $securePassword -Force

    #create master key if not exists
    if (!($mkey = Get-DbaDatabaseMasterKey -SqlInstance $server1 -Database master)) {
        $null = New-DbaDatabaseMasterKey -SqlInstance $server1 -Database master -Password $securePassword -Confirm:$false
    }
    
    try {
        #create certificate
        if ($crt = $server1.Databases['master'].Certificates[$certificateName]) {
            $crt.Drop()
        }
    }
    catch {<#nbd#> }
    $null = New-DbaDbCertificate $server1 -Name $certificateName -Password $null

    Context "Create new logins" {
        It "Should be created successfully - Hashed password" {
            $results = New-DbaLogin -SqlInstance $server1 -Login tester -HashedPassword (Get-PasswordHash $securePassword $server1.VersionMajor) -Force
            $results.Name | Should Be "tester"
            $results.DefaultDatabase | Should be 'master'
            $results.IsDisabled | Should be $false
            $results.PasswordExpirationEnabled | Should be $false
            $results.PasswordPolicyEnforced | Should be $false
            $results.LoginType | Should be 'SqlLogin'
        }
        It "Should be created successfully - password, credential and a custom sid " {
            $results = New-DbaLogin -SqlInstance $server1 -Login claudio -Password $securePassword -Sid $sid -MapToCredential $credLogin
            $results.Name | Should Be "claudio"
            $results.EnumCredentials() | Should be $credLogin
            $results.DefaultDatabase | Should be 'master'
            $results.IsDisabled | Should be $false
            $results.PasswordExpirationEnabled | Should be $false
            $results.PasswordPolicyEnforced | Should be $false
            $results.Sid | Should be (Convert-HexStringToByte $sid)
            $results.LoginType | Should be 'SqlLogin'
        }
        It "Should be created successfully - password and all the flags" {
            $results = New-DbaLogin -SqlInstance $server1 -Login port -Password $securePassword -PasswordPolicy -PasswordExpiration -DefaultDatabase tempdb -Disabled -Language Nederlands
            $results.Name | Should Be "port"
            $results.Language | Should Be 'Nederlands'
            $results.EnumCredentials() | Should be $null
            $results.DefaultDatabase | Should be 'tempdb'
            $results.IsDisabled | Should be $true
            $results.PasswordExpirationEnabled | Should be $true
            $results.PasswordPolicyEnforced | Should be $true
            $results.LoginType | Should be 'SqlLogin'
        }
        It "Should be created successfully - Windows login" {
            $results = New-DbaLogin -SqlInstance $server1 -Login $winLogin
            $results.Name | Should Be "$winLogin"
            $results.DefaultDatabase | Should be 'master'
            $results.IsDisabled | Should be $false
            $results.LoginType | Should be 'WindowsUser'
        }
        It "Should be created successfully - certificate" {
            $results = New-DbaLogin -SqlInstance $server1 -Login certifico -MapToCertificate $certificateName
            $results.Name | Should Be "certifico"
            $results.DefaultDatabase | Should be 'master'
            $results.IsDisabled | Should be $false
            $results.LoginType | Should be 'Certificate'
        }

        It "Should be copied successfully" {
            $results = Get-DbaLogin -SqlInstance $server1 -Login tester | New-DbaLogin -SqlInstance $server2 -Disabled:$false -Force
            $results.Name | Should Be "tester"

            $results = Get-DbaLogin -SqlInstance $server1 -Login claudio, port | New-DbaLogin -SqlInstance $server2 -Force -PasswordPolicy -PasswordExpiration -DefaultDatabase tempdb -Disabled -Language Nederlands -NewSid -LoginRenameHashtable @{claudio = 'port'; port = 'claudio'} -MapToCredential $null
            $results.Name | Should Be @("port", "claudio")

            $results = Get-DbaLogin -SqlInstance $server1 -Login tester | New-DbaLogin -SqlInstance $server1 -LoginRenameHashtable @{tester = 'port'} -Force -NewSid
            $results.Name | Should Be "port"
        }

        It "Should retain its same properties" {

            $login1 = Get-Dbalogin -SqlInstance $script:instance1 -login tester
            $login2 = Get-Dbalogin -SqlInstance $script:instance2 -login tester

            $login2 | Should Not BeNullOrEmpty

            # Compare values
            $login1.Name | Should Be $login2.Name
            $login1.Language | Should Be $login2.Language
            $login1.EnumCredentials() | Should be $login2.EnumCredentials()
            $login1.DefaultDatabase | Should be $login2.DefaultDatabase
            $login1.IsDisabled | Should be $login2.IsDisabled
            $login1.PasswordExpirationEnabled | Should be $login2.PasswordExpirationEnabled
            $login1.PasswordPolicyEnforced | Should be $login2.PasswordPolicyEnforced
            $login1.Sid | Should be $login2.Sid
        }

        It "Should not have same properties because of the overrides" {

            $login1 = Get-Dbalogin -SqlInstance $script:instance1 -login claudio
            $login2 = Get-Dbalogin -SqlInstance $script:instance2 -login port

            $login2 | Should Not BeNullOrEmpty

            # Compare values
            $login1.Language | Should Not Be $login2.Language
            $login1.EnumCredentials() | Should Not Be $login2.EnumCredentials()
            $login1.DefaultDatabase | Should Not be $login2.DefaultDatabase
            $login1.IsDisabled | Should Not be $login2.IsDisabled
            $login1.PasswordExpirationEnabled | Should Not be $login2.PasswordExpirationEnabled
            $login1.PasswordPolicyEnforced | Should Not be $login2.PasswordPolicyEnforced
            $login1.Sid | Should Not be $login2.Sid
        }
    }
    
    if ((Connect-DbaInstance -SqlInstance $script:instance1).LoginMode -eq "Mixed") {
        Context "Connect with a new login" {
            It "Should login with newly created Sql Login, get instance name and kill the process" {
                $cred = New-Object System.Management.Automation.PSCredential ("tester", $securePassword)
                $s = Connect-DbaInstance -SqlInstance $script:instance1 -Credential $cred
                $s.Name | Should Be $script:instance1
                Stop-DbaProcess -SqlInstance $script:instance1 -Login tester
            }
        }
    }
    
    Context "No overwrite" {
        $null = Get-DbaLogin -SqlInstance $server1 -Login tester | New-DbaLogin -SqlInstance $server2 -WarningAction SilentlyContinue -WarningVariable warning 3>&1
        It "Should not attempt overwrite" {
            $warning | Should Match "Login tester already exists"
        }
    }
    
    try {
        foreach ($instance in $servers) {
            foreach ($login in $logins) {
                if ($l = Get-DbaLogin -SqlInstance $instance -Login $login) {
                    $results = $instance.Query("IF EXISTS (SELECT * FROM sys.server_principals WHERE name = '$login') EXEC sp_who '$login'")
                    foreach ($spid in $results.spid) {
                        $null = $instance.Query("kill $spid")
                    }
                    if ($c = $l.EnumCredentials()) {
                        $l.DropCredential($c)
                    }
                    $l.Drop()
                }
            }
        }
        
        $computer.Delete('User', $credLogin)
        $server1.Credentials[$credLogin].Drop()
        $server1.Databases['master'].Certificates[$certificateName].Drop()
        if (!$mkey) {
            $null = Remove-DbaDatabaseMasterKey -SqlInstance $script:instance1 -Database master -Confirm:$false
        }
    }
    catch {<#nbd#> }
}
tools\dbatools\tests\New-DbaPublishProfile.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $dbname = "dbatoolsci_publishprofile"
        $server = Connect-DbaInstance -SqlInstance $script:instance1
        $null = $server.Query("Create Database [$dbname]")
        $db = Get-DbaDatabase -SqlInstance $script:instance1 -Database $dbname
        $null = $db.Query("CREATE TABLE dbo.example (id int);
            INSERT dbo.example
            SELECT top 100 1
            FROM sys.objects")
    }
    AfterAll {
        Remove-DbaDatabase -SqlInstance $script:instance1 -Database $dbname -Confirm:$false
    }

    It "returns the right results" {
        $publishprofile = New-DbaPublishProfile -SqlInstance $script:instance1 -Database $dbname
        $publishprofile.FileName -match 'publish.xml' | Should Be $true
        Remove-Item -Confirm:$false -Path $publishprofile.FileName -ErrorAction SilentlyContinue
    }
}
tools\dbatools\tests\New-DbaSqlConnectionStringBuilder.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Get a ConnectionStringBuilder and assert its values" {
        $results = New-DbaSqlConnectionStringBuilder "Data Source=localhost,1433;Initial Catalog=AlwaysEncryptedSample;UID=sa;PWD=alwaysB3Encrypt1ng;Column Encryption Setting=enabled"
        It "Should be a connection string builder" {
            $results.GetType() | Should Be System.Data.SqlClient.SqlConnectionStringBuilder
        }
        It "Should enable Always Encrypted" {
            $results.ColumnEncryptionSetting | Should Be Enabled
        }
        It "Should have a user name of sa" {
            $results.UserID  | Should Be "sa"
        }
        It "Should have an Application name of `"dbatools Powershell Module`"" {
            $results.ApplicationName  | Should Be "dbatools Powershell Module"
        }
        It "Should have an Workstation ID of `"${env:COMPUTERNAME}`"" {
            $results.WorkstationID  | Should Be $env:COMPUTERNAME
        }
        It "Should have a null MultipeActiveRcordSets" {
            $results.MultipeActiveRcordSets  | Should Be $null
        }
    }
    Context "Assert that the default Application name is preserved" {
        $results = New-DbaSqlConnectionStringBuilder "Data Source=localhost,1433;Initial Catalog=AlwaysEncryptedSample;UID=sa;PWD=alwaysB3Encrypt1ng;Application Name=Always Encrypted MvcString;Column Encryption Setting=enabled"
        It "Should have the Application name of `"Always Encrypted MvcString`"" {
            $results.ApplicationName  | Should Be "Always Encrypted MvcString"
        }
    }
    Context "Build a ConnectionStringBuilder by parameters" {
        $results = New-DbaSqlConnectionStringBuilder `
            -DataSource "localhost,1433" `
            -InitialCatalog "AlwaysEncryptedSample" `
            -UserName "sa" `
            -Password "alwaysB3Encrypt1ng"
        It "Should be a connection string builder" {
            $results.GetType() | Should Be System.Data.SqlClient.SqlConnectionStringBuilder
        }
        It "Should have a user name of sa" {
            $results.UserID | Should Be "sa"
        }
        It "Should have a password of alwaysB3Encrypt1ng" {
            $results.Password | Should Be "alwaysB3Encrypt1ng"
        }
        It "Should have a WorkstationID of {$env:COMPUTERNAME}" {
            $results.WorkstationID | Should Be $env:COMPUTERNAME
        }
        It "Should have an Application name of `"dbatools Powershell Module`"" {
            $results.ApplicationName  | Should Be "dbatools Powershell Module"
        }
        It "Should have an Workstation ID of `"${env:COMPUTERNAME}`"" {
            $results.WorkstationID  | Should Be ${env:COMPUTERNAME}
        }
    }
    Context "Explicitly set MARS to false" {
        $results = New-DbaSqlConnectionStringBuilder `
            -MultipleActiveResultSets:$false
        It "Should not enable Multipe Active Record Sets" {
            $results.MultipleActiveResultSets | Should Be $false
        }
    }
    Context "Set MARS via alias" {
        $results = New-DbaSqlConnectionStringBuilder -MARS
        It "Should have a MultipeActiveResultSets value of true" {
            $results.MultipleActiveResultSets | Should Be $true
        }
    }
    Context "Set AlwaysEncrypted" {
        $results = New-DbaSqlConnectionStringBuilder -AlwaysEncrypted "Enabled"
        It "Should have a `"Column Encryption Setting`" value of `"Enabled`"" {
            $results.ColumnEncryptionSetting | Should Be 'Enabled'
        }
    }
    Context "Set IntegratedSecurity" {
        $results = New-DbaSqlConnectionStringBuilder -IntegratedSecurity $True
        It "Should have a `"Integrated Security Setting`" value of `"True`"" {
            $results.IntegratedSecurity | Should Be $True
        }
    }
}
tools\dbatools\tests\New-DbaSsisCatalog.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Catalog is added properly" {
        # database name is currently fixed
        $database = "SSISDB"
        $db = Get-DbaDatabase -SqlInstance $ssisserver -Database $database

        if (-not $db) {
            $password = ConvertTo-SecureString MyVisiblePassWord -AsPlainText -Force
            $results = New-DbaSsisCatalog -SqlInstance $ssisserver -Password $password -WarningAction SilentlyContinue -WarningVariable warn

            # Run the tests only if it worked (this could be more accurate but w/e, it's hard to test on appveyor)
            if ($warn -match "not running") {
                if (-not $env:APPVEYOR_REPO_BRANCH) {
                    Write-Warning "$warn"
                }
            }
            else {
                It "uses the specified database" {
                    $results.SsisCatalog | Should Be $database
                }

                It "creates the catalog" {
                    $results.Created | Should Be $true
                }
                Remove-DbaDatabase -Confirm:$false -SqlInstance $ssisserver -Database $database
            }
        }
    }
}
tools\dbatools\tests\ObjectDefinitions\BackupRestore\RawInput\broken_chain.json
[
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000000314200037,
        "LastLSN":  17126658000000314300037,
        "CheckpointLSN":  17126658000000314200037,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  17126658000000314600038,
        "BackupStartDate":  "\/Date(1500224400000)\/",
        "BackupFinishDate":  "\/Date(1500224500000)\/",
        "BackupTypeDescription":  "Database",
        "BackupSetGUID":  "8ccfd67c-0969-4d74-89ef-0d2efc9d0284",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Database",
        "End":  "\/Date(1500224500000)\/",
        "Start":  "\/Date(1500224400000)\/",
        "BackupSetId":  "8ccfd67c-0969-4d74-89ef-0d2efc9d0284",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000000313600037,
        "LastLSN":  17126658000000314600037,
        "CheckpointLSN":  17126658000000314600037,
        "DatabaseBackupLSN": 17126658000000314200037,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500226500000)\/",
        "BackupFinishDate":  "\/Date(1500227000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b756c794-790e-4a37-b3cf-1ab15ac26247",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500227000000)\/",
        "Start":  "\/Date(1500226500000)\/",
        "BackupSetId":  "b756c794-790e-4a37-b3cf-1ab15ac26247",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000000314600037,
        "LastLSN":  17126658000000315600037,
        "CheckpointLSN":  17126658000000314600037,
        "DatabaseBackupLSN":  17126658000000314200037,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500227500000)\/",
        "BackupFinishDate":  "\/Date(1500228000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8a14e614-1ee7-4b74-92c0-71c3a045fef9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500228000000)\/",
        "Start":  "\/Date(1500227500000)\/",
        "BackupSetId":  "8a14e614-1ee7-4b74-92c0-71c3a045fef9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000000315600037,
        "LastLSN":  17126658000000315600037,
        "CheckpointLSN":  17126657000008405600001,
        "DatabaseBackupLSN":  17126658000000314200037,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500228500000)\/",
        "BackupFinishDate":  "\/Date(1500229000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b102f57c-0830-4745-8f96-25872336018d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500229000000)\/",
        "Start":  "\/Date(1500228500000)\/",
        "BackupSetId":  "b102f57c-0830-4745-8f96-25872336018d",
        "Database":  "testdb"
    }
]
tools\dbatools\tests\ObjectDefinitions\BackupRestore\RawInput\chkptLSN-ne-firstLSN.json
[
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000010739200127,
        "LastLSN":  17126681000011151300001,
        "CheckpointLSN":  17126681000010739200127,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  17126658000000314600038,
        "BackupStartDate":  "\/Date(1500224400000)\/",
        "BackupFinishDate":  "\/Date(1500224501000)\/",
        "BackupTypeDescription":  "Database Differential",
        "BackupSetGUID":  "8ccfd67c-0969-4d74-89ef-0d2efc9d0284",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Database Differential",
        "End":  "\/Date(1500224501000)\/",
        "Start":  "\/Date(1500224400000)\/",
        "BackupSetId":  "8ccfd67c-0969-4d74-89ef-0d2efc9d0284",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126786000011858200224,
        "LastLSN":  17126787000007305800001,
        "CheckpointLSN":  17126786000011858200224,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  17126658000000314600038,
        "BackupStartDate":  "\/Date(1500310800000)\/",
        "BackupFinishDate":  "\/Date(1500311003000)\/",
        "BackupTypeDescription":  "Database Differential",
        "BackupSetGUID":  "b756c794-790e-4a37-b3cf-1ab15ac26247",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Database Differential",
        "End":  "\/Date(1500311003000)\/",
        "Start":  "\/Date(1500310800000)\/",
        "BackupSetId":  "b756c794-790e-4a37-b3cf-1ab15ac26247",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000000314600037,
        "LastLSN":  17126658000004509500001,
        "CheckpointLSN":  17126658000000314600038,
        "DatabaseBackupLSN":  17126050000002195100134,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500138000000)\/",
        "BackupFinishDate":  "\/Date(1500139557000)\/",
        "BackupTypeDescription":  "Database",
        "BackupSetGUID":  "8a14e614-1ee7-4b74-92c0-71c3a045fef9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Database",
        "End":  "\/Date(1500139557000)\/",
        "Start":  "\/Date(1500138000000)\/",
        "BackupSetId":  "8a14e614-1ee7-4b74-92c0-71c3a045fef9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126657000007603200001,
        "LastLSN":  17126657000008405600001,
        "CheckpointLSN":  17126657000006446100001,
        "DatabaseBackupLSN":  17126050000002195100134,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500136201000)\/",
        "BackupFinishDate":  "\/Date(1500136201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b102f57c-0830-4745-8f96-25872336018d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500136201000)\/",
        "Start":  "\/Date(1500136201000)\/",
        "BackupSetId":  "b102f57c-0830-4745-8f96-25872336018d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126657000008405600001,
        "LastLSN":  17126657000009239000001,
        "CheckpointLSN":  17126657000008515000001,
        "DatabaseBackupLSN":  17126050000002195100134,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500136500000)\/",
        "BackupFinishDate":  "\/Date(1500136500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2f2bbd3e-38e5-4a10-b5da-6dbf7abcc0c8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500136500000)\/",
        "Start":  "\/Date(1500136500000)\/",
        "BackupSetId":  "2f2bbd3e-38e5-4a10-b5da-6dbf7abcc0c8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126657000009239000001,
        "LastLSN":  17126657000010031800001,
        "CheckpointLSN":  17126657000008515000001,
        "DatabaseBackupLSN":  17126050000002195100134,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500136800000)\/",
        "BackupFinishDate":  "\/Date(1500136800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e47b6df9-1bcb-4b12-b00b-c657788a1684",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500136800000)\/",
        "Start":  "\/Date(1500136800000)\/",
        "BackupSetId":  "e47b6df9-1bcb-4b12-b00b-c657788a1684",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126657000010031800001,
        "LastLSN":  17126657000010852900001,
        "CheckpointLSN":  17126657000010586000001,
        "DatabaseBackupLSN":  17126050000002195100134,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500137100000)\/",
        "BackupFinishDate":  "\/Date(1500137100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e1059b75-c092-4bc2-99b3-212760dbf6a1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500137100000)\/",
        "Start":  "\/Date(1500137100000)\/",
        "BackupSetId":  "e1059b75-c092-4bc2-99b3-212760dbf6a1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126657000010852900001,
        "LastLSN":  17126657000011643300001,
        "CheckpointLSN":  17126657000010586000001,
        "DatabaseBackupLSN":  17126050000002195100134,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500137401000)\/",
        "BackupFinishDate":  "\/Date(1500137401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "df01f6ad-3414-4f7e-82d0-349cb8ab0392",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500137401000)\/",
        "Start":  "\/Date(1500137401000)\/",
        "BackupSetId":  "df01f6ad-3414-4f7e-82d0-349cb8ab0392",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126657000011643300001,
        "LastLSN":  17126657000012449000001,
        "CheckpointLSN":  17126657000010586000001,
        "DatabaseBackupLSN":  17126050000002195100134,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500137700000)\/",
        "BackupFinishDate":  "\/Date(1500137700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "38a3ce29-be3e-4e98-b6a4-ee6167cfff61",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500137700000)\/",
        "Start":  "\/Date(1500137700000)\/",
        "BackupSetId":  "38a3ce29-be3e-4e98-b6a4-ee6167cfff61",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126657000012449000001,
        "LastLSN":  17126658000000181500001,
        "CheckpointLSN":  17126657000012653200001,
        "DatabaseBackupLSN":  17126050000002195100134,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500138000000)\/",
        "BackupFinishDate":  "\/Date(1500138000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fefea32b-9398-48f0-b5fc-0676f57ddfdb",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500138000000)\/",
        "Start":  "\/Date(1500138000000)\/",
        "BackupSetId":  "fefea32b-9398-48f0-b5fc-0676f57ddfdb",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000000181500001,
        "LastLSN":  17126658000001119000001,
        "CheckpointLSN":  17126658000000314600038,
        "DatabaseBackupLSN":  17126050000002195100134,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500138300000)\/",
        "BackupFinishDate":  "\/Date(1500138300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d64aea20-a6fd-4271-88b5-e7dbab63dc80",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500138300000)\/",
        "Start":  "\/Date(1500138300000)\/",
        "BackupSetId":  "d64aea20-a6fd-4271-88b5-e7dbab63dc80",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000001119000001,
        "LastLSN":  17126658000001923900001,
        "CheckpointLSN":  17126658000000314600038,
        "DatabaseBackupLSN":  17126050000002195100134,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500138601000)\/",
        "BackupFinishDate":  "\/Date(1500138601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d85266f8-9332-4140-8100-bcf5144603e8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500138601000)\/",
        "Start":  "\/Date(1500138601000)\/",
        "BackupSetId":  "d85266f8-9332-4140-8100-bcf5144603e8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000001923900001,
        "LastLSN":  17126658000002735600001,
        "CheckpointLSN":  17126658000002375600001,
        "DatabaseBackupLSN":  17126050000002195100134,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500138901000)\/",
        "BackupFinishDate":  "\/Date(1500138901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7e30983e-a392-4860-9294-61aa012a64f6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500138901000)\/",
        "Start":  "\/Date(1500138901000)\/",
        "BackupSetId":  "7e30983e-a392-4860-9294-61aa012a64f6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000002735600001,
        "LastLSN":  17126658000003541500001,
        "CheckpointLSN":  17126658000002375600001,
        "DatabaseBackupLSN":  17126050000002195100134,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500139201000)\/",
        "BackupFinishDate":  "\/Date(1500139201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5a878541-29bc-4c3f-9e93-b46a25a156b1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500139201000)\/",
        "Start":  "\/Date(1500139201000)\/",
        "BackupSetId":  "5a878541-29bc-4c3f-9e93-b46a25a156b1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000003541500001,
        "LastLSN":  17126658000004334900001,
        "CheckpointLSN":  17126658000002375600001,
        "DatabaseBackupLSN":  17126050000002195100134,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500139501000)\/",
        "BackupFinishDate":  "\/Date(1500139501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7b32e695-d6a2-45e3-ad37-92427310cd0a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500139501000)\/",
        "Start":  "\/Date(1500139501000)\/",
        "BackupSetId":  "7b32e695-d6a2-45e3-ad37-92427310cd0a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000004334900001,
        "LastLSN":  17126658000005147400001,
        "CheckpointLSN":  17126658000004445000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500139801000)\/",
        "BackupFinishDate":  "\/Date(1500139801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "823cea78-03d6-4328-a3e9-77e77073bb15",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500139801000)\/",
        "Start":  "\/Date(1500139801000)\/",
        "BackupSetId":  "823cea78-03d6-4328-a3e9-77e77073bb15",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000005147400001,
        "LastLSN":  17126658000005940600001,
        "CheckpointLSN":  17126658000004445000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500140101000)\/",
        "BackupFinishDate":  "\/Date(1500140101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "956a6e34-fc5f-49dc-9cd5-70ee7ab48ab3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500140101000)\/",
        "Start":  "\/Date(1500140101000)\/",
        "BackupSetId":  "956a6e34-fc5f-49dc-9cd5-70ee7ab48ab3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000005940600001,
        "LastLSN":  17126658000006794100001,
        "CheckpointLSN":  17126658000006512600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500140401000)\/",
        "BackupFinishDate":  "\/Date(1500140401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "24360dd6-280b-4625-adb2-82586b3d1ff3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500140401000)\/",
        "Start":  "\/Date(1500140401000)\/",
        "BackupSetId":  "24360dd6-280b-4625-adb2-82586b3d1ff3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000006794100001,
        "LastLSN":  17126658000007617100001,
        "CheckpointLSN":  17126658000006512600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500140700000)\/",
        "BackupFinishDate":  "\/Date(1500140700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "32fa2f83-c532-4d3d-a396-29d01b955bcf",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500140700000)\/",
        "Start":  "\/Date(1500140700000)\/",
        "BackupSetId":  "32fa2f83-c532-4d3d-a396-29d01b955bcf",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000007617100001,
        "LastLSN":  17126658000008421500001,
        "CheckpointLSN":  17126658000006512600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500141001000)\/",
        "BackupFinishDate":  "\/Date(1500141001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "70d61bfa-9a60-4506-aef5-def0d90a3e90",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500141001000)\/",
        "Start":  "\/Date(1500141001000)\/",
        "BackupSetId":  "70d61bfa-9a60-4506-aef5-def0d90a3e90",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000008421500001,
        "LastLSN":  17126658000009259200001,
        "CheckpointLSN":  17126658000008581300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500141300000)\/",
        "BackupFinishDate":  "\/Date(1500141300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "55065563-c0bd-408b-974e-d7fcc779a20b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500141300000)\/",
        "Start":  "\/Date(1500141300000)\/",
        "BackupSetId":  "55065563-c0bd-408b-974e-d7fcc779a20b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000009259200001,
        "LastLSN":  17126658000010064800001,
        "CheckpointLSN":  17126658000008581300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500141600000)\/",
        "BackupFinishDate":  "\/Date(1500141600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c5e5803a-106f-48e8-8d61-14cf878e3d4c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500141600000)\/",
        "Start":  "\/Date(1500141600000)\/",
        "BackupSetId":  "c5e5803a-106f-48e8-8d61-14cf878e3d4c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000010064800001,
        "LastLSN":  17126658000010899700001,
        "CheckpointLSN":  17126658000010656400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500141900000)\/",
        "BackupFinishDate":  "\/Date(1500141900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ca157657-13c1-4aba-8464-8c826c84e4a2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500141900000)\/",
        "Start":  "\/Date(1500141900000)\/",
        "BackupSetId":  "ca157657-13c1-4aba-8464-8c826c84e4a2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000010899700001,
        "LastLSN":  17126658000011702700001,
        "CheckpointLSN":  17126658000010656400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500142200000)\/",
        "BackupFinishDate":  "\/Date(1500142201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f7f076a8-909c-4af6-8b1a-72c016d9cab0",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500142201000)\/",
        "Start":  "\/Date(1500142200000)\/",
        "BackupSetId":  "f7f076a8-909c-4af6-8b1a-72c016d9cab0",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000011702700001,
        "LastLSN":  17126658000012516800001,
        "CheckpointLSN":  17126658000010656400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500142500000)\/",
        "BackupFinishDate":  "\/Date(1500142500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9a74f843-3186-4ace-a092-b6f43007b281",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500142500000)\/",
        "Start":  "\/Date(1500142500000)\/",
        "BackupSetId":  "9a74f843-3186-4ace-a092-b6f43007b281",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000012516800001,
        "LastLSN":  17126659000000216700001,
        "CheckpointLSN":  17126658000012725700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500142800000)\/",
        "BackupFinishDate":  "\/Date(1500142800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8b3dfc41-c3f1-4248-ae74-f4304eac2524",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500142800000)\/",
        "Start":  "\/Date(1500142800000)\/",
        "BackupSetId":  "8b3dfc41-c3f1-4248-ae74-f4304eac2524",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126659000000216700001,
        "LastLSN":  17126659000001049300001,
        "CheckpointLSN":  17126659000000216700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500143100000)\/",
        "BackupFinishDate":  "\/Date(1500143100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "557d112b-5dcf-4c64-a330-1be79dc6da90",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500143100000)\/",
        "Start":  "\/Date(1500143100000)\/",
        "BackupSetId":  "557d112b-5dcf-4c64-a330-1be79dc6da90",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126659000001049300001,
        "LastLSN":  17126659000001853300001,
        "CheckpointLSN":  17126659000000216700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500143400000)\/",
        "BackupFinishDate":  "\/Date(1500143400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6208f520-0fb5-48bb-8ba4-f21d85bb1737",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500143400000)\/",
        "Start":  "\/Date(1500143400000)\/",
        "BackupSetId":  "6208f520-0fb5-48bb-8ba4-f21d85bb1737",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126659000001853300001,
        "LastLSN":  17126659000002688100001,
        "CheckpointLSN":  17126659000002280700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500143701000)\/",
        "BackupFinishDate":  "\/Date(1500143701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8a57d3d3-7507-449f-afed-5aa1c9f7d68f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500143701000)\/",
        "Start":  "\/Date(1500143701000)\/",
        "BackupSetId":  "8a57d3d3-7507-449f-afed-5aa1c9f7d68f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126659000002688100001,
        "LastLSN":  17126659000003480900001,
        "CheckpointLSN":  17126659000002280700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500144000000)\/",
        "BackupFinishDate":  "\/Date(1500144000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fe0f5db4-8014-435c-9f01-72527caffcf5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500144000000)\/",
        "Start":  "\/Date(1500144000000)\/",
        "BackupSetId":  "fe0f5db4-8014-435c-9f01-72527caffcf5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126659000003480900001,
        "LastLSN":  17126659000004283600001,
        "CheckpointLSN":  17126659000002280700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500144300000)\/",
        "BackupFinishDate":  "\/Date(1500144300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "afc843b0-f4b6-4ae9-acc5-7fca112c8c6c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500144300000)\/",
        "Start":  "\/Date(1500144300000)\/",
        "BackupSetId":  "afc843b0-f4b6-4ae9-acc5-7fca112c8c6c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126659000004283600001,
        "LastLSN":  17126659000005092200001,
        "CheckpointLSN":  17126659000004369700029,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500144600000)\/",
        "BackupFinishDate":  "\/Date(1500144600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "17a8897e-54f3-447e-aa7e-51545e0b9abc",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500144600000)\/",
        "Start":  "\/Date(1500144600000)\/",
        "BackupSetId":  "17a8897e-54f3-447e-aa7e-51545e0b9abc",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126659000005092200001,
        "LastLSN":  17126659000005897800001,
        "CheckpointLSN":  17126659000004369700029,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500144900000)\/",
        "BackupFinishDate":  "\/Date(1500144901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "bdaeebf1-26f7-4273-b718-1fd200403851",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500144901000)\/",
        "Start":  "\/Date(1500144900000)\/",
        "BackupSetId":  "bdaeebf1-26f7-4273-b718-1fd200403851",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126659000005897800001,
        "LastLSN":  17126659000006733600001,
        "CheckpointLSN":  17126659000006480800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500145200000)\/",
        "BackupFinishDate":  "\/Date(1500145200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3b8f6454-7928-43d8-a9dd-e29ab3133c3c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500145200000)\/",
        "Start":  "\/Date(1500145200000)\/",
        "BackupSetId":  "3b8f6454-7928-43d8-a9dd-e29ab3133c3c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126659000006733600001,
        "LastLSN":  17126659000007522100001,
        "CheckpointLSN":  17126659000006480800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500145500000)\/",
        "BackupFinishDate":  "\/Date(1500145500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b90fb423-f3b3-47d3-a58c-efab54ed085a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500145500000)\/",
        "Start":  "\/Date(1500145500000)\/",
        "BackupSetId":  "b90fb423-f3b3-47d3-a58c-efab54ed085a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126659000007522100001,
        "LastLSN":  17126659000008327300001,
        "CheckpointLSN":  17126659000006480800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500145800000)\/",
        "BackupFinishDate":  "\/Date(1500145800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "cefa4790-fd56-47ba-a04e-daf100b1cab5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500145800000)\/",
        "Start":  "\/Date(1500145800000)\/",
        "BackupSetId":  "cefa4790-fd56-47ba-a04e-daf100b1cab5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126659000008327300001,
        "LastLSN":  17126659000009133000001,
        "CheckpointLSN":  17126659000008552500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500146100000)\/",
        "BackupFinishDate":  "\/Date(1500146100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4e9e5a3b-0b58-49b8-b92a-89703af99600",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500146100000)\/",
        "Start":  "\/Date(1500146100000)\/",
        "BackupSetId":  "4e9e5a3b-0b58-49b8-b92a-89703af99600",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126659000009133000001,
        "LastLSN":  17126659000009935800001,
        "CheckpointLSN":  17126659000008552500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500146400000)\/",
        "BackupFinishDate":  "\/Date(1500146400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "77437117-7e97-4294-a5f8-c46655a7649e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500146400000)\/",
        "Start":  "\/Date(1500146400000)\/",
        "BackupSetId":  "77437117-7e97-4294-a5f8-c46655a7649e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126659000009935800001,
        "LastLSN":  17126659000010746900001,
        "CheckpointLSN":  17126659000010636900031,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500146700000)\/",
        "BackupFinishDate":  "\/Date(1500146700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "420c6440-8f63-4f3f-9236-482dbdf0c5e2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500146700000)\/",
        "Start":  "\/Date(1500146700000)\/",
        "BackupSetId":  "420c6440-8f63-4f3f-9236-482dbdf0c5e2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126659000010746900001,
        "LastLSN":  17126659000011537000001,
        "CheckpointLSN":  17126659000010636900031,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500147000000)\/",
        "BackupFinishDate":  "\/Date(1500147000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b7fe7c92-785c-4b08-963e-05fc295709d4",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500147000000)\/",
        "Start":  "\/Date(1500147000000)\/",
        "BackupSetId":  "b7fe7c92-785c-4b08-963e-05fc295709d4",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126659000011537000001,
        "LastLSN":  17126659000012353200001,
        "CheckpointLSN":  17126659000010636900031,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500147301000)\/",
        "BackupFinishDate":  "\/Date(1500147301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e055914d-4608-44ab-88f7-46806dc8732b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500147301000)\/",
        "Start":  "\/Date(1500147301000)\/",
        "BackupSetId":  "e055914d-4608-44ab-88f7-46806dc8732b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126659000012353200001,
        "LastLSN":  17126660000000093400001,
        "CheckpointLSN":  17126659000012783100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500147600000)\/",
        "BackupFinishDate":  "\/Date(1500147600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a5acc570-eb24-4412-a684-42c3fdb3fe44",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500147600000)\/",
        "Start":  "\/Date(1500147600000)\/",
        "BackupSetId":  "a5acc570-eb24-4412-a684-42c3fdb3fe44",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126660000000093400001,
        "LastLSN":  17126660000000938600001,
        "CheckpointLSN":  17126660000000093400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500147900000)\/",
        "BackupFinishDate":  "\/Date(1500147900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "791ee6e2-f9ea-4920-a5d4-388dab35e9a9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500147900000)\/",
        "Start":  "\/Date(1500147900000)\/",
        "BackupSetId":  "791ee6e2-f9ea-4920-a5d4-388dab35e9a9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126660000000938600001,
        "LastLSN":  17126660000001743000001,
        "CheckpointLSN":  17126660000000093400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500148200000)\/",
        "BackupFinishDate":  "\/Date(1500148200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "95af85e5-25fb-4d6c-a116-6ac6f60a32fb",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500148200000)\/",
        "Start":  "\/Date(1500148200000)\/",
        "BackupSetId":  "95af85e5-25fb-4d6c-a116-6ac6f60a32fb",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126660000001743000001,
        "LastLSN":  17126660000002581700001,
        "CheckpointLSN":  17126660000002159100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500148500000)\/",
        "BackupFinishDate":  "\/Date(1500148500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c84a2b73-1fe9-43be-8d57-1f7264d1740c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500148500000)\/",
        "Start":  "\/Date(1500148500000)\/",
        "BackupSetId":  "c84a2b73-1fe9-43be-8d57-1f7264d1740c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126660000002581700001,
        "LastLSN":  17126660000003385400001,
        "CheckpointLSN":  17126660000002159100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500148800000)\/",
        "BackupFinishDate":  "\/Date(1500148800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "db59d738-51c0-40e7-8352-a5351412f89c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500148800000)\/",
        "Start":  "\/Date(1500148800000)\/",
        "BackupSetId":  "db59d738-51c0-40e7-8352-a5351412f89c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126660000003385400001,
        "LastLSN":  17126660000004285700001,
        "CheckpointLSN":  17126660000004236300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500149100000)\/",
        "BackupFinishDate":  "\/Date(1500149100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f850ccf6-fa68-4fe9-aa71-50bff9483de8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500149100000)\/",
        "Start":  "\/Date(1500149100000)\/",
        "BackupSetId":  "f850ccf6-fa68-4fe9-aa71-50bff9483de8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126660000004285700001,
        "LastLSN":  17126660000005090300001,
        "CheckpointLSN":  17126660000004236300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500149400000)\/",
        "BackupFinishDate":  "\/Date(1500149401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "430fe02a-3770-49d4-bfe9-5d811fcb8449",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500149401000)\/",
        "Start":  "\/Date(1500149400000)\/",
        "BackupSetId":  "430fe02a-3770-49d4-bfe9-5d811fcb8449",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126660000005090300001,
        "LastLSN":  17126660000005905900001,
        "CheckpointLSN":  17126660000004236300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500149701000)\/",
        "BackupFinishDate":  "\/Date(1500149701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8538bee9-4d9f-402f-9f6e-2a6f21fb1697",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500149701000)\/",
        "Start":  "\/Date(1500149701000)\/",
        "BackupSetId":  "8538bee9-4d9f-402f-9f6e-2a6f21fb1697",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126660000005905900001,
        "LastLSN":  17126660000006714900001,
        "CheckpointLSN":  17126660000006312800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500150000000)\/",
        "BackupFinishDate":  "\/Date(1500150000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "cfbddc3d-8d42-444e-a4d8-0ac24186a86a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500150000000)\/",
        "Start":  "\/Date(1500150000000)\/",
        "BackupSetId":  "cfbddc3d-8d42-444e-a4d8-0ac24186a86a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126660000006714900001,
        "LastLSN":  17126660000007536300001,
        "CheckpointLSN":  17126660000006312800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500150300000)\/",
        "BackupFinishDate":  "\/Date(1500150300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b473add3-eed5-4b26-acc1-d9c252556730",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500150300000)\/",
        "Start":  "\/Date(1500150300000)\/",
        "BackupSetId":  "b473add3-eed5-4b26-acc1-d9c252556730",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126660000007536300001,
        "LastLSN":  17126660000008329200001,
        "CheckpointLSN":  17126660000006312800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500150600000)\/",
        "BackupFinishDate":  "\/Date(1500150600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fa0163e2-de31-480a-92ab-16495a25bb35",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500150600000)\/",
        "Start":  "\/Date(1500150600000)\/",
        "BackupSetId":  "fa0163e2-de31-480a-92ab-16495a25bb35",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126660000008329200001,
        "LastLSN":  17126660000009166900001,
        "CheckpointLSN":  17126660000008382400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500150901000)\/",
        "BackupFinishDate":  "\/Date(1500150901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "af0bc4e1-7e9e-4c9a-9494-cf81213d3af5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500150901000)\/",
        "Start":  "\/Date(1500150901000)\/",
        "BackupSetId":  "af0bc4e1-7e9e-4c9a-9494-cf81213d3af5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126660000009166900001,
        "LastLSN":  17126660000009959700001,
        "CheckpointLSN":  17126660000008382400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500151200000)\/",
        "BackupFinishDate":  "\/Date(1500151200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fca9bf01-c3b6-4372-b7bb-9e5116199c74",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500151200000)\/",
        "Start":  "\/Date(1500151200000)\/",
        "BackupSetId":  "fca9bf01-c3b6-4372-b7bb-9e5116199c74",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126660000009959700001,
        "LastLSN":  17126660000010777600001,
        "CheckpointLSN":  17126660000010462900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500151500000)\/",
        "BackupFinishDate":  "\/Date(1500151500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "da3c3063-a343-41a0-8016-77bca6f2f9fc",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500151500000)\/",
        "Start":  "\/Date(1500151500000)\/",
        "BackupSetId":  "da3c3063-a343-41a0-8016-77bca6f2f9fc",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126660000010777600001,
        "LastLSN":  17126660000011568300001,
        "CheckpointLSN":  17126660000010462900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500151800000)\/",
        "BackupFinishDate":  "\/Date(1500151800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1aab51df-0c93-4d6d-a5b7-a72864441c4a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500151800000)\/",
        "Start":  "\/Date(1500151800000)\/",
        "BackupSetId":  "1aab51df-0c93-4d6d-a5b7-a72864441c4a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126660000011568300001,
        "LastLSN":  17126660000012371700001,
        "CheckpointLSN":  17126660000010462900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500152100000)\/",
        "BackupFinishDate":  "\/Date(1500152101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4a8a6f72-e93c-4aed-832a-e253df31c284",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500152101000)\/",
        "Start":  "\/Date(1500152100000)\/",
        "BackupSetId":  "4a8a6f72-e93c-4aed-832a-e253df31c284",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126660000012371700001,
        "LastLSN":  17126661000000105600001,
        "CheckpointLSN":  17126660000012545900004,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500152401000)\/",
        "BackupFinishDate":  "\/Date(1500152401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "63f3fd30-023f-4fe3-9172-fac735abc995",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500152401000)\/",
        "Start":  "\/Date(1500152401000)\/",
        "BackupSetId":  "63f3fd30-023f-4fe3-9172-fac735abc995",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000000105600001,
        "LastLSN":  17126661000000917100001,
        "CheckpointLSN":  17126661000000105600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500152701000)\/",
        "BackupFinishDate":  "\/Date(1500152701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b9c56fd4-e340-495f-b49f-2c07099d70fa",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500152701000)\/",
        "Start":  "\/Date(1500152701000)\/",
        "BackupSetId":  "b9c56fd4-e340-495f-b49f-2c07099d70fa",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000000917100001,
        "LastLSN":  17126661000001719300001,
        "CheckpointLSN":  17126661000000105600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500153000000)\/",
        "BackupFinishDate":  "\/Date(1500153000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "02643266-0df5-4ac9-a5af-387c75bb283d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500153000000)\/",
        "Start":  "\/Date(1500153000000)\/",
        "BackupSetId":  "02643266-0df5-4ac9-a5af-387c75bb283d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000001719300001,
        "LastLSN":  17126661000002525600001,
        "CheckpointLSN":  17126661000002176300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500153300000)\/",
        "BackupFinishDate":  "\/Date(1500153300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "468736c3-c396-43c7-b173-bf5ed6701840",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500153300000)\/",
        "Start":  "\/Date(1500153300000)\/",
        "BackupSetId":  "468736c3-c396-43c7-b173-bf5ed6701840",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000002525600001,
        "LastLSN":  17126661000003328800001,
        "CheckpointLSN":  17126661000002176300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500153601000)\/",
        "BackupFinishDate":  "\/Date(1500153601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "61d64fca-7869-4a13-a97a-0f0a7dd799fd",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500153601000)\/",
        "Start":  "\/Date(1500153601000)\/",
        "BackupSetId":  "61d64fca-7869-4a13-a97a-0f0a7dd799fd",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000003328800001,
        "LastLSN":  17126661000003733400001,
        "CheckpointLSN":  17126661000002176300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500153900000)\/",
        "BackupFinishDate":  "\/Date(1500153900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "37038563-9c5c-44a6-8e80-47aa1b3eae86",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500153900000)\/",
        "Start":  "\/Date(1500153900000)\/",
        "BackupSetId":  "37038563-9c5c-44a6-8e80-47aa1b3eae86",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000003733400001,
        "LastLSN":  17126661000004161000001,
        "CheckpointLSN":  17126661000002176300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500154203000)\/",
        "BackupFinishDate":  "\/Date(1500154203000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8c3f94ac-4972-406a-beaf-31961b8767cd",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500154203000)\/",
        "Start":  "\/Date(1500154203000)\/",
        "BackupSetId":  "8c3f94ac-4972-406a-beaf-31961b8767cd",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000004161000001,
        "LastLSN":  17126661000004617200001,
        "CheckpointLSN":  17126661000004258900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500154503000)\/",
        "BackupFinishDate":  "\/Date(1500154503000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8be53728-8c53-4ca1-8d7d-0a0ebff69bcb",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500154503000)\/",
        "Start":  "\/Date(1500154503000)\/",
        "BackupSetId":  "8be53728-8c53-4ca1-8d7d-0a0ebff69bcb",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000004617200001,
        "LastLSN":  17126661000005179900001,
        "CheckpointLSN":  17126661000004258900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500154801000)\/",
        "BackupFinishDate":  "\/Date(1500154801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3dcdcb4f-2937-4bb7-ba79-f95c6a3c87e5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500154801000)\/",
        "Start":  "\/Date(1500154801000)\/",
        "BackupSetId":  "3dcdcb4f-2937-4bb7-ba79-f95c6a3c87e5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000005179900001,
        "LastLSN":  17126661000005981000001,
        "CheckpointLSN":  17126661000004258900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500155101000)\/",
        "BackupFinishDate":  "\/Date(1500155101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e218fb19-1447-4a4e-810e-399f6e0e7df0",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500155101000)\/",
        "Start":  "\/Date(1500155101000)\/",
        "BackupSetId":  "e218fb19-1447-4a4e-810e-399f6e0e7df0",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000005981000001,
        "LastLSN":  17126661000006803200001,
        "CheckpointLSN":  17126661000006534000028,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500155401000)\/",
        "BackupFinishDate":  "\/Date(1500155401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "69a831a2-f9c6-4e61-9cf8-42650ecdac1a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500155401000)\/",
        "Start":  "\/Date(1500155401000)\/",
        "BackupSetId":  "69a831a2-f9c6-4e61-9cf8-42650ecdac1a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000006803200001,
        "LastLSN":  17126661000007620600001,
        "CheckpointLSN":  17126661000006534000028,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500155700000)\/",
        "BackupFinishDate":  "\/Date(1500155700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f5ff034f-2ff9-4520-8c11-cb67e1ac43d3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500155700000)\/",
        "Start":  "\/Date(1500155700000)\/",
        "BackupSetId":  "f5ff034f-2ff9-4520-8c11-cb67e1ac43d3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000007620600001,
        "LastLSN":  17126661000008422500001,
        "CheckpointLSN":  17126661000006534000028,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500156000000)\/",
        "BackupFinishDate":  "\/Date(1500156000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "546afe4a-c2aa-4ca7-844b-928ce5df6efe",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500156000000)\/",
        "Start":  "\/Date(1500156000000)\/",
        "BackupSetId":  "546afe4a-c2aa-4ca7-844b-928ce5df6efe",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000008422500001,
        "LastLSN":  17126661000009256200001,
        "CheckpointLSN":  17126661000008610500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500156300000)\/",
        "BackupFinishDate":  "\/Date(1500156300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8afd1eb2-fc0b-416b-ae5f-66b99506c049",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500156300000)\/",
        "Start":  "\/Date(1500156300000)\/",
        "BackupSetId":  "8afd1eb2-fc0b-416b-ae5f-66b99506c049",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000009256200001,
        "LastLSN":  17126661000010059500001,
        "CheckpointLSN":  17126661000008610500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500156600000)\/",
        "BackupFinishDate":  "\/Date(1500156600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "be7011af-866c-41e8-bdf0-0c9637089b4d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500156600000)\/",
        "Start":  "\/Date(1500156600000)\/",
        "BackupSetId":  "be7011af-866c-41e8-bdf0-0c9637089b4d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000010059500001,
        "LastLSN":  17126661000010895200001,
        "CheckpointLSN":  17126661000010688800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500156901000)\/",
        "BackupFinishDate":  "\/Date(1500156901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d59f69c1-8654-4158-8afb-2fc5a30dea38",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500156901000)\/",
        "Start":  "\/Date(1500156901000)\/",
        "BackupSetId":  "d59f69c1-8654-4158-8afb-2fc5a30dea38",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000010895200001,
        "LastLSN":  17126661000011689200001,
        "CheckpointLSN":  17126661000010688800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500157201000)\/",
        "BackupFinishDate":  "\/Date(1500157201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f31f21c8-c168-42d6-9770-b6ee0d1f2a23",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500157201000)\/",
        "Start":  "\/Date(1500157201000)\/",
        "BackupSetId":  "f31f21c8-c168-42d6-9770-b6ee0d1f2a23",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000011689200001,
        "LastLSN":  17126661000012503200001,
        "CheckpointLSN":  17126661000010688800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500157501000)\/",
        "BackupFinishDate":  "\/Date(1500157501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3d0ee49f-6fa3-4a89-8d96-c14a4f3e1dc5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500157501000)\/",
        "Start":  "\/Date(1500157501000)\/",
        "BackupSetId":  "3d0ee49f-6fa3-4a89-8d96-c14a4f3e1dc5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000012503200001,
        "LastLSN":  17126662000000220800001,
        "CheckpointLSN":  17126661000012789500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500157800000)\/",
        "BackupFinishDate":  "\/Date(1500157800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c1f34952-6b9d-4e18-94a0-a9069f53074d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500157800000)\/",
        "Start":  "\/Date(1500157800000)\/",
        "BackupSetId":  "c1f34952-6b9d-4e18-94a0-a9069f53074d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126662000000220800001,
        "LastLSN":  17126662000001055800001,
        "CheckpointLSN":  17126662000000220800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500158100000)\/",
        "BackupFinishDate":  "\/Date(1500158100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6174c748-72ed-4891-ab0f-d65ce7100c00",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500158100000)\/",
        "Start":  "\/Date(1500158100000)\/",
        "BackupSetId":  "6174c748-72ed-4891-ab0f-d65ce7100c00",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126662000001055800001,
        "LastLSN":  17126662000001847500001,
        "CheckpointLSN":  17126662000000220800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500158400000)\/",
        "BackupFinishDate":  "\/Date(1500158400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f249f25b-268c-40d8-b94a-acab3a8dc973",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500158400000)\/",
        "Start":  "\/Date(1500158400000)\/",
        "BackupSetId":  "f249f25b-268c-40d8-b94a-acab3a8dc973",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126662000001847500001,
        "LastLSN":  17126662000002669200001,
        "CheckpointLSN":  17126662000002288300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500158700000)\/",
        "BackupFinishDate":  "\/Date(1500158700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4d89fc7c-694b-4b5c-80eb-aa2e56d1e478",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500158700000)\/",
        "Start":  "\/Date(1500158700000)\/",
        "BackupSetId":  "4d89fc7c-694b-4b5c-80eb-aa2e56d1e478",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126662000002669200001,
        "LastLSN":  17126662000003461300001,
        "CheckpointLSN":  17126662000002288300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500159000000)\/",
        "BackupFinishDate":  "\/Date(1500159000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "da2be1d7-43a9-4863-98f8-d94b35420fdf",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500159000000)\/",
        "Start":  "\/Date(1500159000000)\/",
        "BackupSetId":  "da2be1d7-43a9-4863-98f8-d94b35420fdf",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126662000003461300001,
        "LastLSN":  17126662000004266500001,
        "CheckpointLSN":  17126662000002288300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500159301000)\/",
        "BackupFinishDate":  "\/Date(1500159301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "360ed6f2-3344-410a-abc4-6467bf06dc8f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500159301000)\/",
        "Start":  "\/Date(1500159301000)\/",
        "BackupSetId":  "360ed6f2-3344-410a-abc4-6467bf06dc8f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126662000004266500001,
        "LastLSN":  17126662000005101200001,
        "CheckpointLSN":  17126662000004356900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500159601000)\/",
        "BackupFinishDate":  "\/Date(1500159601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0c5ea9e3-7c5e-499f-bdfc-8b8cb536f83f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500159601000)\/",
        "Start":  "\/Date(1500159601000)\/",
        "BackupSetId":  "0c5ea9e3-7c5e-499f-bdfc-8b8cb536f83f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126662000005101200001,
        "LastLSN":  17126662000011164400001,
        "CheckpointLSN":  17126662000010456500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500159900000)\/",
        "BackupFinishDate":  "\/Date(1500159900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1b13ffe8-13af-480d-af09-669e938b5488",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500159900000)\/",
        "Start":  "\/Date(1500159900000)\/",
        "BackupSetId":  "1b13ffe8-13af-480d-af09-669e938b5488",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126662000011164400001,
        "LastLSN":  17126662000011967700001,
        "CheckpointLSN":  17126662000010456500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500160200000)\/",
        "BackupFinishDate":  "\/Date(1500160200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e575d400-be12-4981-b5a1-1598d59c18ef",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500160200000)\/",
        "Start":  "\/Date(1500160200000)\/",
        "BackupSetId":  "e575d400-be12-4981-b5a1-1598d59c18ef",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126662000011967700001,
        "LastLSN":  17126662000012779700001,
        "CheckpointLSN":  17126662000012523800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500160500000)\/",
        "BackupFinishDate":  "\/Date(1500160500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8fda1df6-fdd6-42c3-99a9-d5b087be0d13",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500160500000)\/",
        "Start":  "\/Date(1500160500000)\/",
        "BackupSetId":  "8fda1df6-fdd6-42c3-99a9-d5b087be0d13",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126662000012779700001,
        "LastLSN":  17126663000000478700001,
        "CheckpointLSN":  17126662000012523800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500160800000)\/",
        "BackupFinishDate":  "\/Date(1500160800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "898643a5-c36c-4894-bb9e-d2bad9effcf3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500160800000)\/",
        "Start":  "\/Date(1500160800000)\/",
        "BackupSetId":  "898643a5-c36c-4894-bb9e-d2bad9effcf3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126663000000478700001,
        "LastLSN":  17126663000001289100001,
        "CheckpointLSN":  17126663000000478700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500161101000)\/",
        "BackupFinishDate":  "\/Date(1500161101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8b9ea978-7803-44be-af28-91158c5d303d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500161101000)\/",
        "Start":  "\/Date(1500161101000)\/",
        "BackupSetId":  "8b9ea978-7803-44be-af28-91158c5d303d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126663000001289100001,
        "LastLSN":  17126663000002079700001,
        "CheckpointLSN":  17126663000000478700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500161400000)\/",
        "BackupFinishDate":  "\/Date(1500161400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "58ecd0ff-e967-47e7-a12c-cd11138f5a6c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500161400000)\/",
        "Start":  "\/Date(1500161400000)\/",
        "BackupSetId":  "58ecd0ff-e967-47e7-a12c-cd11138f5a6c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126663000002079700001,
        "LastLSN":  17126664000000776300001,
        "CheckpointLSN":  17126664000000260100005,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500161700000)\/",
        "BackupFinishDate":  "\/Date(1500161701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "44288c06-dc71-4131-bded-800c7d11a893",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500161701000)\/",
        "Start":  "\/Date(1500161700000)\/",
        "BackupSetId":  "44288c06-dc71-4131-bded-800c7d11a893",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126664000000776300001,
        "LastLSN":  17126665000001904300001,
        "CheckpointLSN":  17126665000001742200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500162000000)\/",
        "BackupFinishDate":  "\/Date(1500162001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fff80027-431f-469e-894c-2ea22b04984b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500162001000)\/",
        "Start":  "\/Date(1500162000000)\/",
        "BackupSetId":  "fff80027-431f-469e-894c-2ea22b04984b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126665000001904300001,
        "LastLSN":  17126666000003628600001,
        "CheckpointLSN":  17126666000000786700002,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500162300000)\/",
        "BackupFinishDate":  "\/Date(1500162301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "feabb09e-01be-41a8-b543-11159dadb85e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500162301000)\/",
        "Start":  "\/Date(1500162300000)\/",
        "BackupSetId":  "feabb09e-01be-41a8-b543-11159dadb85e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126666000003628600001,
        "LastLSN":  17126669000001952600001,
        "CheckpointLSN":  17126669000000629200002,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500162601000)\/",
        "BackupFinishDate":  "\/Date(1500162601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5d82631a-c50f-4d21-8b88-66d1212594d6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500162601000)\/",
        "Start":  "\/Date(1500162601000)\/",
        "BackupSetId":  "5d82631a-c50f-4d21-8b88-66d1212594d6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126669000001952600001,
        "LastLSN":  17126669000002772300001,
        "CheckpointLSN":  17126669000000629200002,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500162900000)\/",
        "BackupFinishDate":  "\/Date(1500162900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "bc61f0bc-66e8-4ce4-985a-b25a9ca2173f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500162900000)\/",
        "Start":  "\/Date(1500162900000)\/",
        "BackupSetId":  "bc61f0bc-66e8-4ce4-985a-b25a9ca2173f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126669000002772300001,
        "LastLSN":  17126669000003610400001,
        "CheckpointLSN":  17126669000002788000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500163200000)\/",
        "BackupFinishDate":  "\/Date(1500163200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e0df9982-b6d6-4824-9305-cfce51101acb",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500163200000)\/",
        "Start":  "\/Date(1500163200000)\/",
        "BackupSetId":  "e0df9982-b6d6-4824-9305-cfce51101acb",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126669000003610400001,
        "LastLSN":  17126669000004427300001,
        "CheckpointLSN":  17126669000002788000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500163500000)\/",
        "BackupFinishDate":  "\/Date(1500163500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9abdb23c-ee24-49e2-bd06-88ae1cbd9f19",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500163500000)\/",
        "Start":  "\/Date(1500163500000)\/",
        "BackupSetId":  "9abdb23c-ee24-49e2-bd06-88ae1cbd9f19",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126669000004427300001,
        "LastLSN":  17126669000005250700001,
        "CheckpointLSN":  17126669000004885200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500163800000)\/",
        "BackupFinishDate":  "\/Date(1500163800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2ea2ed1a-a789-411f-aa4c-615119294db4",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500163800000)\/",
        "Start":  "\/Date(1500163800000)\/",
        "BackupSetId":  "2ea2ed1a-a789-411f-aa4c-615119294db4",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126669000005250700001,
        "LastLSN":  17126669000006068900001,
        "CheckpointLSN":  17126669000004885200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500164100000)\/",
        "BackupFinishDate":  "\/Date(1500164101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "169e9131-ffe3-4c91-8cf9-0463ffe3b8b7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500164101000)\/",
        "Start":  "\/Date(1500164100000)\/",
        "BackupSetId":  "169e9131-ffe3-4c91-8cf9-0463ffe3b8b7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126669000006068900001,
        "LastLSN":  17126669000006861300001,
        "CheckpointLSN":  17126669000004885200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500164400000)\/",
        "BackupFinishDate":  "\/Date(1500164400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9d72fd8a-717b-4520-b643-edb01d8f8339",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500164400000)\/",
        "Start":  "\/Date(1500164400000)\/",
        "BackupSetId":  "9d72fd8a-717b-4520-b643-edb01d8f8339",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126669000006861300001,
        "LastLSN":  17126669000007699200001,
        "CheckpointLSN":  17126669000006999600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500164700000)\/",
        "BackupFinishDate":  "\/Date(1500164700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a5d3f401-9583-48b8-8657-60e557f6391c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500164700000)\/",
        "Start":  "\/Date(1500164700000)\/",
        "BackupSetId":  "a5d3f401-9583-48b8-8657-60e557f6391c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126669000007699200001,
        "LastLSN":  17126669000008506200001,
        "CheckpointLSN":  17126669000006999600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500165000000)\/",
        "BackupFinishDate":  "\/Date(1500165000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a5f29546-a881-47c5-9f05-dc6d2e61e4d6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500165000000)\/",
        "Start":  "\/Date(1500165000000)\/",
        "BackupSetId":  "a5f29546-a881-47c5-9f05-dc6d2e61e4d6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126669000008506200001,
        "LastLSN":  17126669000009342200001,
        "CheckpointLSN":  17126669000009069000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500165300000)\/",
        "BackupFinishDate":  "\/Date(1500165300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "43540116-96fc-4f4d-a305-9ec311bcb20f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500165300000)\/",
        "Start":  "\/Date(1500165300000)\/",
        "BackupSetId":  "43540116-96fc-4f4d-a305-9ec311bcb20f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126669000009342200001,
        "LastLSN":  17126669000010137500001,
        "CheckpointLSN":  17126669000009069000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500165600000)\/",
        "BackupFinishDate":  "\/Date(1500165601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e95383af-86f5-4afa-951f-a491f6acb4b3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500165601000)\/",
        "Start":  "\/Date(1500165600000)\/",
        "BackupSetId":  "e95383af-86f5-4afa-951f-a491f6acb4b3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126669000010137500001,
        "LastLSN":  17126669000010938800001,
        "CheckpointLSN":  17126669000009069000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500165900000)\/",
        "BackupFinishDate":  "\/Date(1500165900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "984dad94-24c1-4db8-932a-f17a004f9eb5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500165900000)\/",
        "Start":  "\/Date(1500165900000)\/",
        "BackupSetId":  "984dad94-24c1-4db8-932a-f17a004f9eb5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126669000010938800001,
        "LastLSN":  17126669000011750200001,
        "CheckpointLSN":  17126669000011136700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500166200000)\/",
        "BackupFinishDate":  "\/Date(1500166200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7c18e157-f418-4406-a8e3-1f0a16aa8ce2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500166200000)\/",
        "Start":  "\/Date(1500166200000)\/",
        "BackupSetId":  "7c18e157-f418-4406-a8e3-1f0a16aa8ce2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126669000011750200001,
        "LastLSN":  17126669000012557300001,
        "CheckpointLSN":  17126669000011136700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500166500000)\/",
        "BackupFinishDate":  "\/Date(1500166500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "74e04bab-d592-439d-b9be-6d3074897980",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500166500000)\/",
        "Start":  "\/Date(1500166500000)\/",
        "BackupSetId":  "74e04bab-d592-439d-b9be-6d3074897980",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126669000012557300001,
        "LastLSN":  17126670000000286800001,
        "CheckpointLSN":  17126670000000105700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500166800000)\/",
        "BackupFinishDate":  "\/Date(1500166801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ed20b933-1f60-41ec-b6f9-5ee00e366f89",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500166801000)\/",
        "Start":  "\/Date(1500166800000)\/",
        "BackupSetId":  "ed20b933-1f60-41ec-b6f9-5ee00e366f89",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126670000000286800001,
        "LastLSN":  17126670000001080100001,
        "CheckpointLSN":  17126670000000105700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500167101000)\/",
        "BackupFinishDate":  "\/Date(1500167101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a0e0e24a-235c-4147-bd9e-9c2859912f8b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500167101000)\/",
        "Start":  "\/Date(1500167101000)\/",
        "BackupSetId":  "a0e0e24a-235c-4147-bd9e-9c2859912f8b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126670000001080100001,
        "LastLSN":  17126670000001881600001,
        "CheckpointLSN":  17126670000000105700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500167400000)\/",
        "BackupFinishDate":  "\/Date(1500167400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "17dbe046-7403-4bf4-a02f-fa1de48fa0a1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500167400000)\/",
        "Start":  "\/Date(1500167400000)\/",
        "BackupSetId":  "17dbe046-7403-4bf4-a02f-fa1de48fa0a1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126670000001881600001,
        "LastLSN":  17126670000002693200001,
        "CheckpointLSN":  17126670000002193200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500167700000)\/",
        "BackupFinishDate":  "\/Date(1500167700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fa1e3e4e-d4ba-4f11-9dc7-a0e47373d684",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500167700000)\/",
        "Start":  "\/Date(1500167700000)\/",
        "BackupSetId":  "fa1e3e4e-d4ba-4f11-9dc7-a0e47373d684",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126670000002693200001,
        "LastLSN":  17126670000003497400001,
        "CheckpointLSN":  17126670000002193200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500168000000)\/",
        "BackupFinishDate":  "\/Date(1500168001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "256d838e-d5ea-490e-83d3-6c13135bafb4",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500168001000)\/",
        "Start":  "\/Date(1500168000000)\/",
        "BackupSetId":  "256d838e-d5ea-490e-83d3-6c13135bafb4",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126670000003497400001,
        "LastLSN":  17126670000004307300001,
        "CheckpointLSN":  17126670000004263000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500168300000)\/",
        "BackupFinishDate":  "\/Date(1500168300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "356a5869-dc46-418b-adb8-dd8aad353d06",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500168300000)\/",
        "Start":  "\/Date(1500168300000)\/",
        "BackupSetId":  "356a5869-dc46-418b-adb8-dd8aad353d06",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126670000004307300001,
        "LastLSN":  17126670000005099100001,
        "CheckpointLSN":  17126670000004263000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500168601000)\/",
        "BackupFinishDate":  "\/Date(1500168601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "915e8418-b4d1-4312-b82e-37e4682ef3e6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500168601000)\/",
        "Start":  "\/Date(1500168601000)\/",
        "BackupSetId":  "915e8418-b4d1-4312-b82e-37e4682ef3e6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126670000005099100001,
        "LastLSN":  17126670000005914800001,
        "CheckpointLSN":  17126670000004263000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500168900000)\/",
        "BackupFinishDate":  "\/Date(1500168900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "665dcbe5-1225-4bea-a5d4-757faf210b12",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500168900000)\/",
        "Start":  "\/Date(1500168900000)\/",
        "BackupSetId":  "665dcbe5-1225-4bea-a5d4-757faf210b12",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126670000005914800001,
        "LastLSN":  17126670000006764000001,
        "CheckpointLSN":  17126670000006331400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500169200000)\/",
        "BackupFinishDate":  "\/Date(1500169200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "60e5c305-6d05-4c7e-9ac1-d41d19b8b121",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500169200000)\/",
        "Start":  "\/Date(1500169200000)\/",
        "BackupSetId":  "60e5c305-6d05-4c7e-9ac1-d41d19b8b121",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126670000006764000001,
        "LastLSN":  17126670000007592600001,
        "CheckpointLSN":  17126670000006331400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500169500000)\/",
        "BackupFinishDate":  "\/Date(1500169500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "bcd4c2f5-2e36-4ee9-9365-b7532cdd6352",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500169500000)\/",
        "Start":  "\/Date(1500169500000)\/",
        "BackupSetId":  "bcd4c2f5-2e36-4ee9-9365-b7532cdd6352",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126670000007592600001,
        "LastLSN":  17126670000008398300001,
        "CheckpointLSN":  17126670000006331400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500169801000)\/",
        "BackupFinishDate":  "\/Date(1500169801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "316f894b-f248-4dff-83c4-6f25c024947d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500169801000)\/",
        "Start":  "\/Date(1500169801000)\/",
        "BackupSetId":  "316f894b-f248-4dff-83c4-6f25c024947d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126670000008398300001,
        "LastLSN":  17126670000009236200001,
        "CheckpointLSN":  17126670000008400200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500170100000)\/",
        "BackupFinishDate":  "\/Date(1500170100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7b75dcc9-ccdf-4046-a940-1dc642d49859",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500170100000)\/",
        "Start":  "\/Date(1500170100000)\/",
        "BackupSetId":  "7b75dcc9-ccdf-4046-a940-1dc642d49859",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126670000009236200001,
        "LastLSN":  17126670000010042300001,
        "CheckpointLSN":  17126670000008400200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500170400000)\/",
        "BackupFinishDate":  "\/Date(1500170400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e6ce917d-3522-4299-b2b0-dd82a60e5be4",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500170400000)\/",
        "Start":  "\/Date(1500170400000)\/",
        "BackupSetId":  "e6ce917d-3522-4299-b2b0-dd82a60e5be4",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126670000010042300001,
        "LastLSN":  17126670000010878100001,
        "CheckpointLSN":  17126670000010467400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500170700000)\/",
        "BackupFinishDate":  "\/Date(1500170700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "077b964b-ff34-4cb2-abc4-58e86bcf15c5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500170700000)\/",
        "Start":  "\/Date(1500170700000)\/",
        "BackupSetId":  "077b964b-ff34-4cb2-abc4-58e86bcf15c5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126670000010878100001,
        "LastLSN":  17126670000011681900001,
        "CheckpointLSN":  17126670000010467400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500171000000)\/",
        "BackupFinishDate":  "\/Date(1500171001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "890b8b69-ac64-461b-bde1-441b84f0e956",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500171001000)\/",
        "Start":  "\/Date(1500171000000)\/",
        "BackupSetId":  "890b8b69-ac64-461b-bde1-441b84f0e956",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126670000011681900001,
        "LastLSN":  17126670000012497500001,
        "CheckpointLSN":  17126670000010467400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500171301000)\/",
        "BackupFinishDate":  "\/Date(1500171301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e70bfd6c-441e-4d0d-8e2f-41f14affe464",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500171301000)\/",
        "Start":  "\/Date(1500171301000)\/",
        "BackupSetId":  "e70bfd6c-441e-4d0d-8e2f-41f14affe464",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126670000012497500001,
        "LastLSN":  17126671000000202900001,
        "CheckpointLSN":  17126670000012562200004,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500171600000)\/",
        "BackupFinishDate":  "\/Date(1500171600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "40411649-d419-4be5-998a-45058666e8ab",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500171600000)\/",
        "Start":  "\/Date(1500171600000)\/",
        "BackupSetId":  "40411649-d419-4be5-998a-45058666e8ab",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126671000000202900001,
        "LastLSN":  17126671000001034700001,
        "CheckpointLSN":  17126671000000203300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500171900000)\/",
        "BackupFinishDate":  "\/Date(1500171900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "48608d55-efa7-40a1-9e81-21ca010facfa",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500171900000)\/",
        "Start":  "\/Date(1500171900000)\/",
        "BackupSetId":  "48608d55-efa7-40a1-9e81-21ca010facfa",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126671000001034700001,
        "LastLSN":  17126671000001838200001,
        "CheckpointLSN":  17126671000000203300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500172200000)\/",
        "BackupFinishDate":  "\/Date(1500172200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3c5dd58f-b5d2-4cc1-aa99-38224ea74e2b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500172200000)\/",
        "Start":  "\/Date(1500172200000)\/",
        "BackupSetId":  "3c5dd58f-b5d2-4cc1-aa99-38224ea74e2b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126671000001838200001,
        "LastLSN":  17126671000002673400001,
        "CheckpointLSN":  17126671000002278100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500172501000)\/",
        "BackupFinishDate":  "\/Date(1500172501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a9465409-7b55-4d27-9df6-bd0bea11578c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500172501000)\/",
        "Start":  "\/Date(1500172501000)\/",
        "BackupSetId":  "a9465409-7b55-4d27-9df6-bd0bea11578c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126671000002673400001,
        "LastLSN":  17126671000003449200001,
        "CheckpointLSN":  17126671000002278100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500172800000)\/",
        "BackupFinishDate":  "\/Date(1500172800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "52345a04-15d7-4513-b541-cf41e708a6df",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500172800000)\/",
        "Start":  "\/Date(1500172800000)\/",
        "BackupSetId":  "52345a04-15d7-4513-b541-cf41e708a6df",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126671000003449200001,
        "LastLSN":  17126671000004251000001,
        "CheckpointLSN":  17126671000002278100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500173100000)\/",
        "BackupFinishDate":  "\/Date(1500173100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "512ff79a-0a97-4976-ae7d-20f119ada4e1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500173100000)\/",
        "Start":  "\/Date(1500173100000)\/",
        "BackupSetId":  "512ff79a-0a97-4976-ae7d-20f119ada4e1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126671000004251000001,
        "LastLSN":  17126671000005060400001,
        "CheckpointLSN":  17126671000004345900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500173400000)\/",
        "BackupFinishDate":  "\/Date(1500173400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "af4d5a8e-3386-40fd-b657-9cde922287f5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500173400000)\/",
        "Start":  "\/Date(1500173400000)\/",
        "BackupSetId":  "af4d5a8e-3386-40fd-b657-9cde922287f5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126671000005060400001,
        "LastLSN":  17126671000005867100001,
        "CheckpointLSN":  17126671000004345900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500173700000)\/",
        "BackupFinishDate":  "\/Date(1500173701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ca768efb-11d3-475e-8508-7f8ca35cc1cb",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500173701000)\/",
        "Start":  "\/Date(1500173700000)\/",
        "BackupSetId":  "ca768efb-11d3-475e-8508-7f8ca35cc1cb",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126671000005867100001,
        "LastLSN":  17126671000006705600001,
        "CheckpointLSN":  17126671000006431200023,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500174000000)\/",
        "BackupFinishDate":  "\/Date(1500174000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9d5a68b7-2b45-4338-a929-cf558926949a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500174000000)\/",
        "Start":  "\/Date(1500174000000)\/",
        "BackupSetId":  "9d5a68b7-2b45-4338-a929-cf558926949a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126671000006705600001,
        "LastLSN":  17126671000007496500001,
        "CheckpointLSN":  17126671000006431200023,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500174300000)\/",
        "BackupFinishDate":  "\/Date(1500174300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fa863c75-da1f-4f0d-af64-d9bcd90db809",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500174300000)\/",
        "Start":  "\/Date(1500174300000)\/",
        "BackupSetId":  "fa863c75-da1f-4f0d-af64-d9bcd90db809",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126671000007496500001,
        "LastLSN":  17126671000008303500001,
        "CheckpointLSN":  17126671000006431200023,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500174600000)\/",
        "BackupFinishDate":  "\/Date(1500174600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "30da268e-a28d-4b2d-a88c-9b1bcdf35937",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500174600000)\/",
        "Start":  "\/Date(1500174600000)\/",
        "BackupSetId":  "30da268e-a28d-4b2d-a88c-9b1bcdf35937",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126671000008303500001,
        "LastLSN":  17126671000009114600001,
        "CheckpointLSN":  17126671000008557900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500174900000)\/",
        "BackupFinishDate":  "\/Date(1500174900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5c173e09-7c83-4440-ab61-242243d9c5a1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500174900000)\/",
        "Start":  "\/Date(1500174900000)\/",
        "BackupSetId":  "5c173e09-7c83-4440-ab61-242243d9c5a1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126671000009114600001,
        "LastLSN":  17126671000009918300001,
        "CheckpointLSN":  17126671000008557900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500175201000)\/",
        "BackupFinishDate":  "\/Date(1500175201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "66ebd5b8-d484-44bc-9ecd-6be0c480ee37",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500175201000)\/",
        "Start":  "\/Date(1500175201000)\/",
        "BackupSetId":  "66ebd5b8-d484-44bc-9ecd-6be0c480ee37",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126671000009918300001,
        "LastLSN":  17126671000010710900001,
        "CheckpointLSN":  17126671000010627900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500175500000)\/",
        "BackupFinishDate":  "\/Date(1500175500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "620eb2be-b435-47ec-9df6-9b006b299ae9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500175500000)\/",
        "Start":  "\/Date(1500175500000)\/",
        "BackupSetId":  "620eb2be-b435-47ec-9df6-9b006b299ae9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126671000010710900001,
        "LastLSN":  17126671000011502300001,
        "CheckpointLSN":  17126671000010627900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500175800000)\/",
        "BackupFinishDate":  "\/Date(1500175800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "53c90d72-1a48-49bb-b246-e84617c8c2b2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500175800000)\/",
        "Start":  "\/Date(1500175800000)\/",
        "BackupSetId":  "53c90d72-1a48-49bb-b246-e84617c8c2b2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126671000011502300001,
        "LastLSN":  17126671000012318700001,
        "CheckpointLSN":  17126671000010627900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500176100000)\/",
        "BackupFinishDate":  "\/Date(1500176100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "09109be2-5104-4ec0-920d-05b21e661d91",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500176100000)\/",
        "Start":  "\/Date(1500176100000)\/",
        "BackupSetId":  "09109be2-5104-4ec0-920d-05b21e661d91",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126671000012318700001,
        "LastLSN":  17126672000000061800001,
        "CheckpointLSN":  17126671000012697200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500176400000)\/",
        "BackupFinishDate":  "\/Date(1500176400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8726c270-6597-4275-84df-86bad4e1d739",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500176400000)\/",
        "Start":  "\/Date(1500176400000)\/",
        "BackupSetId":  "8726c270-6597-4275-84df-86bad4e1d739",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126672000000061800001,
        "LastLSN":  17126672000000910400001,
        "CheckpointLSN":  17126672000000061800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500176700000)\/",
        "BackupFinishDate":  "\/Date(1500176700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2238100a-dd03-4f13-814d-598fa88265ef",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500176700000)\/",
        "Start":  "\/Date(1500176700000)\/",
        "BackupSetId":  "2238100a-dd03-4f13-814d-598fa88265ef",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126672000000910400001,
        "LastLSN":  17126672000001713000001,
        "CheckpointLSN":  17126672000000061800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500177000000)\/",
        "BackupFinishDate":  "\/Date(1500177000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2bf48f8a-b439-4495-980b-07b5b141ef7f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500177000000)\/",
        "Start":  "\/Date(1500177000000)\/",
        "BackupSetId":  "2bf48f8a-b439-4495-980b-07b5b141ef7f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126672000001713000001,
        "LastLSN":  17126672000002552200001,
        "CheckpointLSN":  17126672000002128900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500177300000)\/",
        "BackupFinishDate":  "\/Date(1500177300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b794d0c3-11fb-44e5-9ae6-9aca4aa40263",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500177300000)\/",
        "Start":  "\/Date(1500177300000)\/",
        "BackupSetId":  "b794d0c3-11fb-44e5-9ae6-9aca4aa40263",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126672000002552200001,
        "LastLSN":  17126672000003358300001,
        "CheckpointLSN":  17126672000002128900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500177601000)\/",
        "BackupFinishDate":  "\/Date(1500177601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0d8e9ae9-a6e8-480e-a36c-cd2ee37d790e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500177601000)\/",
        "Start":  "\/Date(1500177601000)\/",
        "BackupSetId":  "0d8e9ae9-a6e8-480e-a36c-cd2ee37d790e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126672000003358300001,
        "LastLSN":  17126672000004174300001,
        "CheckpointLSN":  17126672000002128900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500177900000)\/",
        "BackupFinishDate":  "\/Date(1500177900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e82e2742-b476-40e3-a01f-0db39de1bd93",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500177900000)\/",
        "Start":  "\/Date(1500177900000)\/",
        "BackupSetId":  "e82e2742-b476-40e3-a01f-0db39de1bd93",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126672000004174300001,
        "LastLSN":  17126672000004999000001,
        "CheckpointLSN":  17126672000004196800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500178200000)\/",
        "BackupFinishDate":  "\/Date(1500178200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9d1e5fa6-b36e-4917-9cbd-c8ed01f5c696",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500178200000)\/",
        "Start":  "\/Date(1500178200000)\/",
        "BackupSetId":  "9d1e5fa6-b36e-4917-9cbd-c8ed01f5c696",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126672000004999000001,
        "LastLSN":  17126672000005817200001,
        "CheckpointLSN":  17126672000004196800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500178500000)\/",
        "BackupFinishDate":  "\/Date(1500178500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ee805c96-2032-42f7-8864-aefb99c13945",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500178500000)\/",
        "Start":  "\/Date(1500178500000)\/",
        "BackupSetId":  "ee805c96-2032-42f7-8864-aefb99c13945",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126672000005817200001,
        "LastLSN":  17126672000006625700001,
        "CheckpointLSN":  17126672000006297500021,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500178800000)\/",
        "BackupFinishDate":  "\/Date(1500178801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "835e7217-1a89-4441-9681-876d7ccca10c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500178801000)\/",
        "Start":  "\/Date(1500178800000)\/",
        "BackupSetId":  "835e7217-1a89-4441-9681-876d7ccca10c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126672000006625700001,
        "LastLSN":  17126672000007442400001,
        "CheckpointLSN":  17126672000006297500021,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500179100000)\/",
        "BackupFinishDate":  "\/Date(1500179100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5c3032f5-698f-476b-bcd4-0a75d484776d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500179100000)\/",
        "Start":  "\/Date(1500179100000)\/",
        "BackupSetId":  "5c3032f5-698f-476b-bcd4-0a75d484776d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126672000007442400001,
        "LastLSN":  17126672000008248100001,
        "CheckpointLSN":  17126672000006297500021,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500179400000)\/",
        "BackupFinishDate":  "\/Date(1500179400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "74f32ffc-448e-4ab6-b8ef-dd42c0b10593",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500179400000)\/",
        "Start":  "\/Date(1500179400000)\/",
        "BackupSetId":  "74f32ffc-448e-4ab6-b8ef-dd42c0b10593",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126672000008248100001,
        "LastLSN":  17126672000009082300001,
        "CheckpointLSN":  17126672000008366500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500179700000)\/",
        "BackupFinishDate":  "\/Date(1500179700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "317b3328-29f0-4d66-8dca-fede8eb9e086",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500179700000)\/",
        "Start":  "\/Date(1500179700000)\/",
        "BackupSetId":  "317b3328-29f0-4d66-8dca-fede8eb9e086",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126672000009082300001,
        "LastLSN":  17126672000009873200001,
        "CheckpointLSN":  17126672000008366500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500180000000)\/",
        "BackupFinishDate":  "\/Date(1500180000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e862a5a9-46f5-49c9-b3f9-a981eca01f7d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500180000000)\/",
        "Start":  "\/Date(1500180000000)\/",
        "BackupSetId":  "e862a5a9-46f5-49c9-b3f9-a981eca01f7d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126672000009873200001,
        "LastLSN":  17126672000010695100001,
        "CheckpointLSN":  17126672000010449400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500180300000)\/",
        "BackupFinishDate":  "\/Date(1500180300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "97c9247c-6b90-4cc2-bc1b-524e2555cb50",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500180300000)\/",
        "Start":  "\/Date(1500180300000)\/",
        "BackupSetId":  "97c9247c-6b90-4cc2-bc1b-524e2555cb50",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126672000010695100001,
        "LastLSN":  17126672000011486300001,
        "CheckpointLSN":  17126672000010449400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500180601000)\/",
        "BackupFinishDate":  "\/Date(1500180601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e99e4a34-e970-4294-879e-88d8c84fdd8c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500180601000)\/",
        "Start":  "\/Date(1500180601000)\/",
        "BackupSetId":  "e99e4a34-e970-4294-879e-88d8c84fdd8c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126672000011486300001,
        "LastLSN":  17126672000012293700001,
        "CheckpointLSN":  17126672000010449400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500180900000)\/",
        "BackupFinishDate":  "\/Date(1500180900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0b0048fe-2727-45ca-84d3-3920c44a579c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500180900000)\/",
        "Start":  "\/Date(1500180900000)\/",
        "BackupSetId":  "0b0048fe-2727-45ca-84d3-3920c44a579c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126672000012293700001,
        "LastLSN":  17126673000000025400001,
        "CheckpointLSN":  17126672000012568800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500181200000)\/",
        "BackupFinishDate":  "\/Date(1500181200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6641cf06-9266-486e-b91d-57189d288531",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500181200000)\/",
        "Start":  "\/Date(1500181200000)\/",
        "BackupSetId":  "6641cf06-9266-486e-b91d-57189d288531",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126673000000025400001,
        "LastLSN":  17126673000003570500001,
        "CheckpointLSN":  17126673000002098400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500181500000)\/",
        "BackupFinishDate":  "\/Date(1500181500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "cf889cf0-6690-410e-90fd-f1bcff71a437",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500181500000)\/",
        "Start":  "\/Date(1500181500000)\/",
        "BackupSetId":  "cf889cf0-6690-410e-90fd-f1bcff71a437",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126673000003570500001,
        "LastLSN":  17126673000004400300001,
        "CheckpointLSN":  17126673000004202700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500181800000)\/",
        "BackupFinishDate":  "\/Date(1500181800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5988fcc4-3216-4dfd-8fd9-c54c378aff36",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500181800000)\/",
        "Start":  "\/Date(1500181800000)\/",
        "BackupSetId":  "5988fcc4-3216-4dfd-8fd9-c54c378aff36",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126673000004400300001,
        "LastLSN":  17126673000005189900001,
        "CheckpointLSN":  17126673000004202700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500182101000)\/",
        "BackupFinishDate":  "\/Date(1500182101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "42cefd12-ef9e-4e05-ae1e-92c3a41cae5e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500182101000)\/",
        "Start":  "\/Date(1500182101000)\/",
        "BackupSetId":  "42cefd12-ef9e-4e05-ae1e-92c3a41cae5e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126673000005189900001,
        "LastLSN":  17126673000005992800001,
        "CheckpointLSN":  17126673000004202700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500182400000)\/",
        "BackupFinishDate":  "\/Date(1500182400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "bafcb75b-bc77-4b7a-9352-e1bb9770f9cd",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500182400000)\/",
        "Start":  "\/Date(1500182400000)\/",
        "BackupSetId":  "bafcb75b-bc77-4b7a-9352-e1bb9770f9cd",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126673000005992800001,
        "LastLSN":  17126673000006801400001,
        "CheckpointLSN":  17126673000006278200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500182700000)\/",
        "BackupFinishDate":  "\/Date(1500182700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7c92d710-412d-421c-987e-95e38e80d714",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500182700000)\/",
        "Start":  "\/Date(1500182700000)\/",
        "BackupSetId":  "7c92d710-412d-421c-987e-95e38e80d714",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126673000006801400001,
        "LastLSN":  17126673000007592900001,
        "CheckpointLSN":  17126673000006278200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500183000000)\/",
        "BackupFinishDate":  "\/Date(1500183000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "153138bc-7d4f-4bf4-8d1b-9c2cd3022520",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500183000000)\/",
        "Start":  "\/Date(1500183000000)\/",
        "BackupSetId":  "153138bc-7d4f-4bf4-8d1b-9c2cd3022520",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126673000007592900001,
        "LastLSN":  17126673000008429800001,
        "CheckpointLSN":  17126673000008359300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500183300000)\/",
        "BackupFinishDate":  "\/Date(1500183300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a950d546-5203-4cb4-abf6-ddc7a0bd9dba",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500183300000)\/",
        "Start":  "\/Date(1500183300000)\/",
        "BackupSetId":  "a950d546-5203-4cb4-abf6-ddc7a0bd9dba",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126673000008429800001,
        "LastLSN":  17126673000009260100001,
        "CheckpointLSN":  17126673000008359300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500183600000)\/",
        "BackupFinishDate":  "\/Date(1500183600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "30ac2de7-665e-4f08-afc5-e6c441931e1e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500183600000)\/",
        "Start":  "\/Date(1500183600000)\/",
        "BackupSetId":  "30ac2de7-665e-4f08-afc5-e6c441931e1e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126673000009260100001,
        "LastLSN":  17126673000010089500001,
        "CheckpointLSN":  17126673000008359300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500183900000)\/",
        "BackupFinishDate":  "\/Date(1500183900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b2f8d031-c7a8-4a72-9075-198fc12a6f23",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500183900000)\/",
        "Start":  "\/Date(1500183900000)\/",
        "BackupSetId":  "b2f8d031-c7a8-4a72-9075-198fc12a6f23",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126673000010089500001,
        "LastLSN":  17126673000010912200001,
        "CheckpointLSN":  17126673000010441400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500184200000)\/",
        "BackupFinishDate":  "\/Date(1500184200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5f532efa-14ce-4330-8fa8-5e9767ee6503",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500184200000)\/",
        "Start":  "\/Date(1500184200000)\/",
        "BackupSetId":  "5f532efa-14ce-4330-8fa8-5e9767ee6503",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126673000010912200001,
        "LastLSN":  17126673000011731100001,
        "CheckpointLSN":  17126673000010441400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500184500000)\/",
        "BackupFinishDate":  "\/Date(1500184500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1c3aeeb9-f2e7-498d-8555-e5e10b4f656d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500184500000)\/",
        "Start":  "\/Date(1500184500000)\/",
        "BackupSetId":  "1c3aeeb9-f2e7-498d-8555-e5e10b4f656d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126673000011731100001,
        "LastLSN":  17126673000012535000001,
        "CheckpointLSN":  17126673000010441400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500184801000)\/",
        "BackupFinishDate":  "\/Date(1500184801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f791dc87-280a-4291-bbf7-2b622f44b07e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500184801000)\/",
        "Start":  "\/Date(1500184801000)\/",
        "BackupSetId":  "f791dc87-280a-4291-bbf7-2b622f44b07e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126673000012535000001,
        "LastLSN":  17126674000000263600001,
        "CheckpointLSN":  17126673000012544700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500185100000)\/",
        "BackupFinishDate":  "\/Date(1500185100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "dffc204a-b30a-4e06-9689-ea5c2af1d516",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500185100000)\/",
        "Start":  "\/Date(1500185100000)\/",
        "BackupSetId":  "dffc204a-b30a-4e06-9689-ea5c2af1d516",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126674000000263600001,
        "LastLSN":  17126674000001088000001,
        "CheckpointLSN":  17126674000000263600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500185400000)\/",
        "BackupFinishDate":  "\/Date(1500185400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a35cee8f-d6fb-4497-9e1f-d7bb0186a8d1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500185400000)\/",
        "Start":  "\/Date(1500185400000)\/",
        "BackupSetId":  "a35cee8f-d6fb-4497-9e1f-d7bb0186a8d1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126674000001088000001,
        "LastLSN":  17126674000001903600001,
        "CheckpointLSN":  17126674000000263600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500185700000)\/",
        "BackupFinishDate":  "\/Date(1500185700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "250cfd25-476f-425e-8a7d-8433a3f725e1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500185700000)\/",
        "Start":  "\/Date(1500185700000)\/",
        "BackupSetId":  "250cfd25-476f-425e-8a7d-8433a3f725e1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126674000001903600001,
        "LastLSN":  17126674000002715300001,
        "CheckpointLSN":  17126674000002330900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500186000000)\/",
        "BackupFinishDate":  "\/Date(1500186000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "92eaf48b-59cc-404e-808b-ab3652e8d1c7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500186000000)\/",
        "Start":  "\/Date(1500186000000)\/",
        "BackupSetId":  "92eaf48b-59cc-404e-808b-ab3652e8d1c7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126674000002715300001,
        "LastLSN":  17126674000003530700001,
        "CheckpointLSN":  17126674000002330900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500186300000)\/",
        "BackupFinishDate":  "\/Date(1500186300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "19234118-65f5-48bc-8555-8aa17dac51a5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500186300000)\/",
        "Start":  "\/Date(1500186300000)\/",
        "BackupSetId":  "19234118-65f5-48bc-8555-8aa17dac51a5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126674000003530700001,
        "LastLSN":  17126674000004334000001,
        "CheckpointLSN":  17126674000002330900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500186600000)\/",
        "BackupFinishDate":  "\/Date(1500186600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4b877d13-7a00-43d1-9da4-77d15549c49b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500186600000)\/",
        "Start":  "\/Date(1500186600000)\/",
        "BackupSetId":  "4b877d13-7a00-43d1-9da4-77d15549c49b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126674000004334000001,
        "LastLSN":  17126674000005171100001,
        "CheckpointLSN":  17126674000004399000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500186900000)\/",
        "BackupFinishDate":  "\/Date(1500186900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "93c56816-f247-480a-a055-d56b33763e73",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500186900000)\/",
        "Start":  "\/Date(1500186900000)\/",
        "BackupSetId":  "93c56816-f247-480a-a055-d56b33763e73",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126674000005171100001,
        "LastLSN":  17126674000005963600001,
        "CheckpointLSN":  17126674000004399000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500187200000)\/",
        "BackupFinishDate":  "\/Date(1500187200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0f5f969b-f5f6-477b-bc2b-96e6dc2f83ba",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500187200000)\/",
        "Start":  "\/Date(1500187200000)\/",
        "BackupSetId":  "0f5f969b-f5f6-477b-bc2b-96e6dc2f83ba",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126674000005963600001,
        "LastLSN":  17126674000006785200001,
        "CheckpointLSN":  17126674000006468800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500187500000)\/",
        "BackupFinishDate":  "\/Date(1500187500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ff535a10-d45e-454f-90ba-b53ff37a8f56",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500187500000)\/",
        "Start":  "\/Date(1500187500000)\/",
        "BackupSetId":  "ff535a10-d45e-454f-90ba-b53ff37a8f56",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126674000006785200001,
        "LastLSN":  17126674000007577900001,
        "CheckpointLSN":  17126674000006468800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500187800000)\/",
        "BackupFinishDate":  "\/Date(1500187800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c17f556d-1e1a-42b0-9b25-ff330a5290f8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500187800000)\/",
        "Start":  "\/Date(1500187800000)\/",
        "BackupSetId":  "c17f556d-1e1a-42b0-9b25-ff330a5290f8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126674000007577900001,
        "LastLSN":  17126674000008385600001,
        "CheckpointLSN":  17126674000006468800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500188100000)\/",
        "BackupFinishDate":  "\/Date(1500188101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a58e991e-4d4e-462a-a99b-21081a747c81",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500188101000)\/",
        "Start":  "\/Date(1500188100000)\/",
        "BackupSetId":  "a58e991e-4d4e-462a-a99b-21081a747c81",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126674000008385600001,
        "LastLSN":  17126674000009222400001,
        "CheckpointLSN":  17126674000008536400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500188400000)\/",
        "BackupFinishDate":  "\/Date(1500188400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6cca14be-f085-4108-9efd-4a67b0cdfc1d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500188400000)\/",
        "Start":  "\/Date(1500188400000)\/",
        "BackupSetId":  "6cca14be-f085-4108-9efd-4a67b0cdfc1d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126674000009222400001,
        "LastLSN":  17126674000010012400001,
        "CheckpointLSN":  17126674000008536400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500188700000)\/",
        "BackupFinishDate":  "\/Date(1500188700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d8112172-c97c-412a-96bc-9b161a8740db",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500188700000)\/",
        "Start":  "\/Date(1500188700000)\/",
        "BackupSetId":  "d8112172-c97c-412a-96bc-9b161a8740db",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126674000010012400001,
        "LastLSN":  17126674000010835400001,
        "CheckpointLSN":  17126674000010624200040,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500189000000)\/",
        "BackupFinishDate":  "\/Date(1500189000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e0bc3070-e569-46fa-8fe3-8f281f0305c3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500189000000)\/",
        "Start":  "\/Date(1500189000000)\/",
        "BackupSetId":  "e0bc3070-e569-46fa-8fe3-8f281f0305c3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126674000010835400001,
        "LastLSN":  17126674000011626900001,
        "CheckpointLSN":  17126674000010624200040,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500189300000)\/",
        "BackupFinishDate":  "\/Date(1500189300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5a8710af-fd8a-4600-a9a0-4597d151829a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500189300000)\/",
        "Start":  "\/Date(1500189300000)\/",
        "BackupSetId":  "5a8710af-fd8a-4600-a9a0-4597d151829a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126674000011626900001,
        "LastLSN":  17126674000012429900001,
        "CheckpointLSN":  17126674000010624200040,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500189601000)\/",
        "BackupFinishDate":  "\/Date(1500189601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "05becbbb-cf5c-4607-b72d-3696f55e67a4",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500189601000)\/",
        "Start":  "\/Date(1500189601000)\/",
        "BackupSetId":  "05becbbb-cf5c-4607-b72d-3696f55e67a4",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126674000012429900001,
        "LastLSN":  17126675000000133100001,
        "CheckpointLSN":  17126674000012722300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500189900000)\/",
        "BackupFinishDate":  "\/Date(1500189900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "522bd44b-dcad-4a2f-9c02-f03eabd5f18b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500189900000)\/",
        "Start":  "\/Date(1500189900000)\/",
        "BackupSetId":  "522bd44b-dcad-4a2f-9c02-f03eabd5f18b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126675000000133100001,
        "LastLSN":  17126675000000940900001,
        "CheckpointLSN":  17126675000000133100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500190200000)\/",
        "BackupFinishDate":  "\/Date(1500190200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e14b353b-7be1-4b76-8683-6946cfefa441",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500190200000)\/",
        "Start":  "\/Date(1500190200000)\/",
        "BackupSetId":  "e14b353b-7be1-4b76-8683-6946cfefa441",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126675000000940900001,
        "LastLSN":  17126675000001759200001,
        "CheckpointLSN":  17126675000000133100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500190500000)\/",
        "BackupFinishDate":  "\/Date(1500190500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "eea08527-9829-43d0-91d5-cba5ebb651ca",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500190500000)\/",
        "Start":  "\/Date(1500190500000)\/",
        "BackupSetId":  "eea08527-9829-43d0-91d5-cba5ebb651ca",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126675000001759200001,
        "LastLSN":  17126675000002608800001,
        "CheckpointLSN":  17126675000002198300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500190800000)\/",
        "BackupFinishDate":  "\/Date(1500190800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d447b0a5-bb17-41f3-a065-4f1f5dfad0d7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500190800000)\/",
        "Start":  "\/Date(1500190800000)\/",
        "BackupSetId":  "d447b0a5-bb17-41f3-a065-4f1f5dfad0d7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126675000002608800001,
        "LastLSN":  17126675000003438200001,
        "CheckpointLSN":  17126675000002198300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500191100000)\/",
        "BackupFinishDate":  "\/Date(1500191101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c79de9d9-229c-4c2f-963d-f4f5ca6827dd",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500191101000)\/",
        "Start":  "\/Date(1500191100000)\/",
        "BackupSetId":  "c79de9d9-229c-4c2f-963d-f4f5ca6827dd",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126675000003438200001,
        "LastLSN":  17126675000004241200001,
        "CheckpointLSN":  17126675000002198300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500191400000)\/",
        "BackupFinishDate":  "\/Date(1500191400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "77c1e56b-21bd-4547-8b06-36726a68d45b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500191400000)\/",
        "Start":  "\/Date(1500191400000)\/",
        "BackupSetId":  "77c1e56b-21bd-4547-8b06-36726a68d45b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126675000004241200001,
        "LastLSN":  17126675000005080500001,
        "CheckpointLSN":  17126675000004270900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500191700000)\/",
        "BackupFinishDate":  "\/Date(1500191700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ca8b7e0e-1382-4dd0-8ed3-253e8db36e63",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500191700000)\/",
        "Start":  "\/Date(1500191700000)\/",
        "BackupSetId":  "ca8b7e0e-1382-4dd0-8ed3-253e8db36e63",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126675000005080500001,
        "LastLSN":  17126675000005882900001,
        "CheckpointLSN":  17126675000004270900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500192000000)\/",
        "BackupFinishDate":  "\/Date(1500192000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d090ebb1-fd9c-4cdb-a671-0e4615f6be16",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500192000000)\/",
        "Start":  "\/Date(1500192000000)\/",
        "BackupSetId":  "d090ebb1-fd9c-4cdb-a671-0e4615f6be16",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126675000005882900001,
        "LastLSN":  17126675000006718900001,
        "CheckpointLSN":  17126675000006339000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500192300000)\/",
        "BackupFinishDate":  "\/Date(1500192300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "909df065-ffae-4da5-84e6-f22eed5a5097",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500192300000)\/",
        "Start":  "\/Date(1500192300000)\/",
        "BackupSetId":  "909df065-ffae-4da5-84e6-f22eed5a5097",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126675000006718900001,
        "LastLSN":  17126675000007520700001,
        "CheckpointLSN":  17126675000006339000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500192601000)\/",
        "BackupFinishDate":  "\/Date(1500192601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d4cd1168-5a0a-4a12-b341-25ccda1cf472",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500192601000)\/",
        "Start":  "\/Date(1500192601000)\/",
        "BackupSetId":  "d4cd1168-5a0a-4a12-b341-25ccda1cf472",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126675000007520700001,
        "LastLSN":  17126675000008337800001,
        "CheckpointLSN":  17126675000006339000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500192900000)\/",
        "BackupFinishDate":  "\/Date(1500192900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d34c2c86-b80b-4f48-9be5-49065e5a6eba",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500192900000)\/",
        "Start":  "\/Date(1500192900000)\/",
        "BackupSetId":  "d34c2c86-b80b-4f48-9be5-49065e5a6eba",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126675000008337800001,
        "LastLSN":  17126675000009146400001,
        "CheckpointLSN":  17126675000008407400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500193200000)\/",
        "BackupFinishDate":  "\/Date(1500193200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "cd1ab380-864d-4c73-b53e-7cb1a14ea180",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500193200000)\/",
        "Start":  "\/Date(1500193200000)\/",
        "BackupSetId":  "cd1ab380-864d-4c73-b53e-7cb1a14ea180",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126675000009146400001,
        "LastLSN":  17126675000009964300001,
        "CheckpointLSN":  17126675000008407400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500193500000)\/",
        "BackupFinishDate":  "\/Date(1500193500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d73196b1-3a65-40a5-8c9d-fc5a1ebea39e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500193500000)\/",
        "Start":  "\/Date(1500193500000)\/",
        "BackupSetId":  "d73196b1-3a65-40a5-8c9d-fc5a1ebea39e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126675000009964300001,
        "LastLSN":  17126675000010788900001,
        "CheckpointLSN":  17126675000010476200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500193800000)\/",
        "BackupFinishDate":  "\/Date(1500193801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "bdd81806-e4af-49fe-9f91-abd3226250d8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500193801000)\/",
        "Start":  "\/Date(1500193800000)\/",
        "BackupSetId":  "bdd81806-e4af-49fe-9f91-abd3226250d8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126675000010788900001,
        "LastLSN":  17126675000011607300001,
        "CheckpointLSN":  17126675000010476200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500194100000)\/",
        "BackupFinishDate":  "\/Date(1500194100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "63bf7d27-7f06-4391-bca6-746d2f7d11ac",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500194100000)\/",
        "Start":  "\/Date(1500194100000)\/",
        "BackupSetId":  "63bf7d27-7f06-4391-bca6-746d2f7d11ac",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126675000011607300001,
        "LastLSN":  17126675000012402000001,
        "CheckpointLSN":  17126675000010476200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500194400000)\/",
        "BackupFinishDate":  "\/Date(1500194400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d44af580-8d1d-46b5-99db-99bbd278f0e6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500194400000)\/",
        "Start":  "\/Date(1500194400000)\/",
        "BackupSetId":  "d44af580-8d1d-46b5-99db-99bbd278f0e6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126675000012402000001,
        "LastLSN":  17126676000000118900001,
        "CheckpointLSN":  17126675000012545000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500194700000)\/",
        "BackupFinishDate":  "\/Date(1500194700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e477a0a0-0ec9-4ca8-aa64-9c0c3527f334",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500194700000)\/",
        "Start":  "\/Date(1500194700000)\/",
        "BackupSetId":  "e477a0a0-0ec9-4ca8-aa64-9c0c3527f334",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126676000000118900001,
        "LastLSN":  17126676000000925300001,
        "CheckpointLSN":  17126676000000119300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500195000000)\/",
        "BackupFinishDate":  "\/Date(1500195000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3360a528-a523-408e-b9c1-11abd589463c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500195000000)\/",
        "Start":  "\/Date(1500195000000)\/",
        "BackupSetId":  "3360a528-a523-408e-b9c1-11abd589463c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126676000000925300001,
        "LastLSN":  17126676000001732300001,
        "CheckpointLSN":  17126676000000119300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500195300000)\/",
        "BackupFinishDate":  "\/Date(1500195301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "59799c78-938f-4bc1-baec-57759b4372e5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500195301000)\/",
        "Start":  "\/Date(1500195300000)\/",
        "BackupSetId":  "59799c78-938f-4bc1-baec-57759b4372e5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126676000001732300001,
        "LastLSN":  17126676000002567200001,
        "CheckpointLSN":  17126676000002182900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500195600000)\/",
        "BackupFinishDate":  "\/Date(1500195600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b8976932-960d-4f0e-bd4c-866b158f646d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500195600000)\/",
        "Start":  "\/Date(1500195600000)\/",
        "BackupSetId":  "b8976932-960d-4f0e-bd4c-866b158f646d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126676000002567200001,
        "LastLSN":  17126676000003359000001,
        "CheckpointLSN":  17126676000002182900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500195900000)\/",
        "BackupFinishDate":  "\/Date(1500195900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1e5caf1d-1b53-480a-98ed-9ecf676f4fd8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500195900000)\/",
        "Start":  "\/Date(1500195900000)\/",
        "BackupSetId":  "1e5caf1d-1b53-480a-98ed-9ecf676f4fd8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126676000003359000001,
        "LastLSN":  17126676000004162700001,
        "CheckpointLSN":  17126676000002182900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500196200000)\/",
        "BackupFinishDate":  "\/Date(1500196200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e4b1ea0d-ce13-45b9-879e-fa51f6dd4101",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500196200000)\/",
        "Start":  "\/Date(1500196200000)\/",
        "BackupSetId":  "e4b1ea0d-ce13-45b9-879e-fa51f6dd4101",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126676000004162700001,
        "LastLSN":  17126676000004972300001,
        "CheckpointLSN":  17126676000004248600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500196500000)\/",
        "BackupFinishDate":  "\/Date(1500196500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "57aa3495-c28d-4f6f-a63c-9c14314cd0e3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500196500000)\/",
        "Start":  "\/Date(1500196500000)\/",
        "BackupSetId":  "57aa3495-c28d-4f6f-a63c-9c14314cd0e3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126676000004972300001,
        "LastLSN":  17126676000005778200001,
        "CheckpointLSN":  17126676000004248600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500196800000)\/",
        "BackupFinishDate":  "\/Date(1500196800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c5a51cff-9d5c-4485-a6bf-b5e81729128b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500196800000)\/",
        "Start":  "\/Date(1500196800000)\/",
        "BackupSetId":  "c5a51cff-9d5c-4485-a6bf-b5e81729128b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126676000005778200001,
        "LastLSN":  17126676000006590000001,
        "CheckpointLSN":  17126676000006333200021,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500197101000)\/",
        "BackupFinishDate":  "\/Date(1500197101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c121cb29-5fa0-4ee1-8dc4-432856b5efaa",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500197101000)\/",
        "Start":  "\/Date(1500197101000)\/",
        "BackupSetId":  "c121cb29-5fa0-4ee1-8dc4-432856b5efaa",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126676000006590000001,
        "LastLSN":  17126676000007364400001,
        "CheckpointLSN":  17126676000006333200021,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500197400000)\/",
        "BackupFinishDate":  "\/Date(1500197400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ff24c957-d0cd-46c7-a5c0-d563299c89e6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500197400000)\/",
        "Start":  "\/Date(1500197400000)\/",
        "BackupSetId":  "ff24c957-d0cd-46c7-a5c0-d563299c89e6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126676000007364400001,
        "LastLSN":  17126676000008180100001,
        "CheckpointLSN":  17126676000006333200021,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500197700000)\/",
        "BackupFinishDate":  "\/Date(1500197700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "306439cf-1205-49ae-ade3-c8e1e3b23eac",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500197700000)\/",
        "Start":  "\/Date(1500197700000)\/",
        "BackupSetId":  "306439cf-1205-49ae-ade3-c8e1e3b23eac",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126676000008180100001,
        "LastLSN":  17126676000009030800001,
        "CheckpointLSN":  17126676000008485800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500198000000)\/",
        "BackupFinishDate":  "\/Date(1500198000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5b902f4a-d1dd-429c-b894-6fc527bb8a4b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500198000000)\/",
        "Start":  "\/Date(1500198000000)\/",
        "BackupSetId":  "5b902f4a-d1dd-429c-b894-6fc527bb8a4b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126676000009030800001,
        "LastLSN":  17126676000009861600001,
        "CheckpointLSN":  17126676000008485800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500198300000)\/",
        "BackupFinishDate":  "\/Date(1500198301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "47fa9874-f26e-4cea-a6a4-2a5db1892a4a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500198301000)\/",
        "Start":  "\/Date(1500198300000)\/",
        "BackupSetId":  "47fa9874-f26e-4cea-a6a4-2a5db1892a4a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126676000009861600001,
        "LastLSN":  17126676000010667100001,
        "CheckpointLSN":  17126676000010552200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500198600000)\/",
        "BackupFinishDate":  "\/Date(1500198600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ac9f9ab4-d93c-4efc-b071-790b03990c5e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500198600000)\/",
        "Start":  "\/Date(1500198600000)\/",
        "BackupSetId":  "ac9f9ab4-d93c-4efc-b071-790b03990c5e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126676000010667100001,
        "LastLSN":  17126676000011487700001,
        "CheckpointLSN":  17126676000010552200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500198900000)\/",
        "BackupFinishDate":  "\/Date(1500198900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "64224497-7ae0-4648-a574-d72624cc8a60",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500198900000)\/",
        "Start":  "\/Date(1500198900000)\/",
        "BackupSetId":  "64224497-7ae0-4648-a574-d72624cc8a60",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126676000011487700001,
        "LastLSN":  17126676000012297200001,
        "CheckpointLSN":  17126676000010552200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500199200000)\/",
        "BackupFinishDate":  "\/Date(1500199200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ceae87e7-9195-4bc6-b383-975e96f81b35",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500199200000)\/",
        "Start":  "\/Date(1500199200000)\/",
        "BackupSetId":  "ceae87e7-9195-4bc6-b383-975e96f81b35",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126676000012297200001,
        "LastLSN":  17126677000000020200001,
        "CheckpointLSN":  17126676000012622400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500199500000)\/",
        "BackupFinishDate":  "\/Date(1500199501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9d365e8a-38b9-4248-a2e7-8c3d9e785216",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500199501000)\/",
        "Start":  "\/Date(1500199500000)\/",
        "BackupSetId":  "9d365e8a-38b9-4248-a2e7-8c3d9e785216",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000000020200001,
        "LastLSN":  17126677000000809800001,
        "CheckpointLSN":  17126677000000022400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500199800000)\/",
        "BackupFinishDate":  "\/Date(1500199800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "413b4754-7a64-466e-a8d8-bf7e087bda0a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500199800000)\/",
        "Start":  "\/Date(1500199800000)\/",
        "BackupSetId":  "413b4754-7a64-466e-a8d8-bf7e087bda0a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000000809800001,
        "LastLSN":  17126677000001628000001,
        "CheckpointLSN":  17126677000000022400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500200100000)\/",
        "BackupFinishDate":  "\/Date(1500200100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2904d291-1d04-46f7-9867-16739fbfc7b1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500200100000)\/",
        "Start":  "\/Date(1500200100000)\/",
        "BackupSetId":  "2904d291-1d04-46f7-9867-16739fbfc7b1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000001628000001,
        "LastLSN":  17126677000002438500001,
        "CheckpointLSN":  17126677000002107200003,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500200400000)\/",
        "BackupFinishDate":  "\/Date(1500200400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "146b3217-d461-4dd3-b84b-c2c3c38cbc99",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500200400000)\/",
        "Start":  "\/Date(1500200400000)\/",
        "BackupSetId":  "146b3217-d461-4dd3-b84b-c2c3c38cbc99",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000002438500001,
        "LastLSN":  17126677000003255000001,
        "CheckpointLSN":  17126677000002107200003,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500200700000)\/",
        "BackupFinishDate":  "\/Date(1500200700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8e2f3276-6468-4ad7-91ee-b3dff4ba9dd6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500200700000)\/",
        "Start":  "\/Date(1500200700000)\/",
        "BackupSetId":  "8e2f3276-6468-4ad7-91ee-b3dff4ba9dd6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000003255000001,
        "LastLSN":  17126677000004061700001,
        "CheckpointLSN":  17126677000002107200003,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500201001000)\/",
        "BackupFinishDate":  "\/Date(1500201001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d668f317-6c2f-4541-8aea-7ea63ae244e9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500201001000)\/",
        "Start":  "\/Date(1500201001000)\/",
        "BackupSetId":  "d668f317-6c2f-4541-8aea-7ea63ae244e9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000004061700001,
        "LastLSN":  17126677000004861600001,
        "CheckpointLSN":  17126677000004173100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500201300000)\/",
        "BackupFinishDate":  "\/Date(1500201300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "94cdbfbb-ffda-4567-8b7d-5c86de8951c6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500201300000)\/",
        "Start":  "\/Date(1500201300000)\/",
        "BackupSetId":  "94cdbfbb-ffda-4567-8b7d-5c86de8951c6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000004861600001,
        "LastLSN":  17126677000005654300001,
        "CheckpointLSN":  17126677000004173100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500201600000)\/",
        "BackupFinishDate":  "\/Date(1500201600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a89e08e2-9e9f-4e0e-95d8-f639be38b4cd",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500201600000)\/",
        "Start":  "\/Date(1500201600000)\/",
        "BackupSetId":  "a89e08e2-9e9f-4e0e-95d8-f639be38b4cd",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000005654300001,
        "LastLSN":  17126677000006477600001,
        "CheckpointLSN":  17126677000006254600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500201900000)\/",
        "BackupFinishDate":  "\/Date(1500201900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d667ed28-a3ff-4b58-958c-6d586218ed04",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500201900000)\/",
        "Start":  "\/Date(1500201900000)\/",
        "BackupSetId":  "d667ed28-a3ff-4b58-958c-6d586218ed04",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000006477600001,
        "LastLSN":  17126677000007271200001,
        "CheckpointLSN":  17126677000006254600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500202200000)\/",
        "BackupFinishDate":  "\/Date(1500202200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "730bc922-a979-41f2-a7ad-778af044b52e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500202200000)\/",
        "Start":  "\/Date(1500202200000)\/",
        "BackupSetId":  "730bc922-a979-41f2-a7ad-778af044b52e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000007271200001,
        "LastLSN":  17126677000008079100001,
        "CheckpointLSN":  17126677000006254600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500202500000)\/",
        "BackupFinishDate":  "\/Date(1500202500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "91cd0658-d309-480b-b0e6-c7cbca3b52f9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500202500000)\/",
        "Start":  "\/Date(1500202500000)\/",
        "BackupSetId":  "91cd0658-d309-480b-b0e6-c7cbca3b52f9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000008079100001,
        "LastLSN":  17126677000008913500001,
        "CheckpointLSN":  17126677000008320300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500202801000)\/",
        "BackupFinishDate":  "\/Date(1500202801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0ac06cd1-30c7-4878-85fe-367e9f6f0f28",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500202801000)\/",
        "Start":  "\/Date(1500202801000)\/",
        "BackupSetId":  "0ac06cd1-30c7-4878-85fe-367e9f6f0f28",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000008913500001,
        "LastLSN":  17126677000009705800001,
        "CheckpointLSN":  17126677000008320300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500203100000)\/",
        "BackupFinishDate":  "\/Date(1500203100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c6ee9ad5-12d4-4856-8b49-3bf3c9eb4c3f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500203100000)\/",
        "Start":  "\/Date(1500203100000)\/",
        "BackupSetId":  "c6ee9ad5-12d4-4856-8b49-3bf3c9eb4c3f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000009705800001,
        "LastLSN":  17126677000010528100001,
        "CheckpointLSN":  17126677000010399600023,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500203400000)\/",
        "BackupFinishDate":  "\/Date(1500203400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f95de4b6-c580-4f87-b059-23110e2a0b2d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500203400000)\/",
        "Start":  "\/Date(1500203400000)\/",
        "BackupSetId":  "f95de4b6-c580-4f87-b059-23110e2a0b2d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000010528100001,
        "LastLSN":  17126677000011320700001,
        "CheckpointLSN":  17126677000010399600023,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500203700000)\/",
        "BackupFinishDate":  "\/Date(1500203700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1acacccc-3fd0-4e93-9424-ce24f4568914",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500203700000)\/",
        "Start":  "\/Date(1500203700000)\/",
        "BackupSetId":  "1acacccc-3fd0-4e93-9424-ce24f4568914",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000011320700001,
        "LastLSN":  17126677000012123700001,
        "CheckpointLSN":  17126677000010399600023,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500204001000)\/",
        "BackupFinishDate":  "\/Date(1500204001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "87a80fc3-e4db-4002-9224-6ab65df3b951",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500204001000)\/",
        "Start":  "\/Date(1500204001000)\/",
        "BackupSetId":  "87a80fc3-e4db-4002-9224-6ab65df3b951",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000012123700001,
        "LastLSN":  17126677000012934400001,
        "CheckpointLSN":  17126677000012541000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500204300000)\/",
        "BackupFinishDate":  "\/Date(1500204300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b20c784e-f362-425a-8fc9-4411228185d1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500204300000)\/",
        "Start":  "\/Date(1500204300000)\/",
        "BackupSetId":  "b20c784e-f362-425a-8fc9-4411228185d1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000012934400001,
        "LastLSN":  17126678000000621600001,
        "CheckpointLSN":  17126677000012541000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500204600000)\/",
        "BackupFinishDate":  "\/Date(1500204600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e936eabd-c0e2-43df-902c-4ee42656ec34",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500204600000)\/",
        "Start":  "\/Date(1500204600000)\/",
        "BackupSetId":  "e936eabd-c0e2-43df-902c-4ee42656ec34",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126678000000621600001,
        "LastLSN":  17126678000001456500001,
        "CheckpointLSN":  17126678000000621600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500204900000)\/",
        "BackupFinishDate":  "\/Date(1500204900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "60fa3bba-66b1-4d5a-951a-8bd87a5041a8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500204900000)\/",
        "Start":  "\/Date(1500204900000)\/",
        "BackupSetId":  "60fa3bba-66b1-4d5a-951a-8bd87a5041a8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126678000001456500001,
        "LastLSN":  17126678000002291700001,
        "CheckpointLSN":  17126678000000621600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500205200000)\/",
        "BackupFinishDate":  "\/Date(1500205200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d31aaa76-2e32-48b9-a610-acf4dc3ae2a0",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500205200000)\/",
        "Start":  "\/Date(1500205200000)\/",
        "BackupSetId":  "d31aaa76-2e32-48b9-a610-acf4dc3ae2a0",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126678000002291700001,
        "LastLSN":  17126678000003138900001,
        "CheckpointLSN":  17126678000002685200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500205501000)\/",
        "BackupFinishDate":  "\/Date(1500205501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "73c3fa86-e8f5-403f-a6fe-86767cb4bc96",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500205501000)\/",
        "Start":  "\/Date(1500205501000)\/",
        "BackupSetId":  "73c3fa86-e8f5-403f-a6fe-86767cb4bc96",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126678000003138900001,
        "LastLSN":  17126678000003944400001,
        "CheckpointLSN":  17126678000002685200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500205800000)\/",
        "BackupFinishDate":  "\/Date(1500205800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "74815d45-6cc4-4441-80be-d644b78509d3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500205800000)\/",
        "Start":  "\/Date(1500205800000)\/",
        "BackupSetId":  "74815d45-6cc4-4441-80be-d644b78509d3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126678000003944400001,
        "LastLSN":  17126678000004782400001,
        "CheckpointLSN":  17126678000004753000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500206100000)\/",
        "BackupFinishDate":  "\/Date(1500206100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "869f8aa0-ef73-480a-b020-72fe9b569890",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500206100000)\/",
        "Start":  "\/Date(1500206100000)\/",
        "BackupSetId":  "869f8aa0-ef73-480a-b020-72fe9b569890",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126678000004782400001,
        "LastLSN":  17126678000005586800001,
        "CheckpointLSN":  17126678000004753000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500206400000)\/",
        "BackupFinishDate":  "\/Date(1500206400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "969637e9-9441-4b1c-83b0-22df942cc687",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500206400000)\/",
        "Start":  "\/Date(1500206400000)\/",
        "BackupSetId":  "969637e9-9441-4b1c-83b0-22df942cc687",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126678000005586800001,
        "LastLSN":  17126678000006407300001,
        "CheckpointLSN":  17126678000004753000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500206701000)\/",
        "BackupFinishDate":  "\/Date(1500206701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8e7d1ea0-2460-4414-b65e-4f98cbbd304a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500206701000)\/",
        "Start":  "\/Date(1500206701000)\/",
        "BackupSetId":  "8e7d1ea0-2460-4414-b65e-4f98cbbd304a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126678000006407300001,
        "LastLSN":  17126678000007229100001,
        "CheckpointLSN":  17126678000006818700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500207000000)\/",
        "BackupFinishDate":  "\/Date(1500207000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6ffd1007-3982-4642-956c-5895d1bb53e5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500207000000)\/",
        "Start":  "\/Date(1500207000000)\/",
        "BackupSetId":  "6ffd1007-3982-4642-956c-5895d1bb53e5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126678000007229100001,
        "LastLSN":  17126678000008048200001,
        "CheckpointLSN":  17126678000006818700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500207300000)\/",
        "BackupFinishDate":  "\/Date(1500207300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c705c557-2d95-4dc3-859a-fe01e65e7f53",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500207300000)\/",
        "Start":  "\/Date(1500207300000)\/",
        "BackupSetId":  "c705c557-2d95-4dc3-859a-fe01e65e7f53",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126678000008048200001,
        "LastLSN":  17126678000008841600001,
        "CheckpointLSN":  17126678000006818700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500207600000)\/",
        "BackupFinishDate":  "\/Date(1500207601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "38a1de6b-a700-4687-a6d3-6c99545a0c0f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500207601000)\/",
        "Start":  "\/Date(1500207600000)\/",
        "BackupSetId":  "38a1de6b-a700-4687-a6d3-6c99545a0c0f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126678000008841600001,
        "LastLSN":  17126678000009647800001,
        "CheckpointLSN":  17126678000008897300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500207901000)\/",
        "BackupFinishDate":  "\/Date(1500207901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "383b7be7-0745-4d02-86a8-d03e9f8ed168",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500207901000)\/",
        "Start":  "\/Date(1500207901000)\/",
        "BackupSetId":  "383b7be7-0745-4d02-86a8-d03e9f8ed168",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126678000009647800001,
        "LastLSN":  17126678000010362600001,
        "CheckpointLSN":  17126678000008897300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500208200000)\/",
        "BackupFinishDate":  "\/Date(1500208200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f0e26e0c-0061-4f4d-9673-7893f5d64481",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500208200000)\/",
        "Start":  "\/Date(1500208200000)\/",
        "BackupSetId":  "f0e26e0c-0061-4f4d-9673-7893f5d64481",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126678000010362600001,
        "LastLSN":  17126678000011109000001,
        "CheckpointLSN":  17126678000010987200023,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500208500000)\/",
        "BackupFinishDate":  "\/Date(1500208500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "74232158-89a5-4d40-afc8-3999d3fd0625",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500208500000)\/",
        "Start":  "\/Date(1500208500000)\/",
        "BackupSetId":  "74232158-89a5-4d40-afc8-3999d3fd0625",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126678000011109000001,
        "LastLSN":  17126678000011813400001,
        "CheckpointLSN":  17126678000010987200023,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500208800000)\/",
        "BackupFinishDate":  "\/Date(1500208800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fb5bcc27-b2e0-4cae-98c9-868023acaed0",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500208800000)\/",
        "Start":  "\/Date(1500208800000)\/",
        "BackupSetId":  "fb5bcc27-b2e0-4cae-98c9-868023acaed0",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126678000011813400001,
        "LastLSN":  17126678000012528800001,
        "CheckpointLSN":  17126678000010987200023,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500209100000)\/",
        "BackupFinishDate":  "\/Date(1500209100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3179ccfa-7637-4f53-9636-99ea6bee64ac",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500209100000)\/",
        "Start":  "\/Date(1500209100000)\/",
        "BackupSetId":  "3179ccfa-7637-4f53-9636-99ea6bee64ac",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126678000012528800001,
        "LastLSN":  17126679000000142600001,
        "CheckpointLSN":  17126679000000045100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500209400000)\/",
        "BackupFinishDate":  "\/Date(1500209400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c617b60c-397a-4a3e-9d94-fde9b5603058",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500209400000)\/",
        "Start":  "\/Date(1500209400000)\/",
        "BackupSetId":  "c617b60c-397a-4a3e-9d94-fde9b5603058",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000000142600001,
        "LastLSN":  17126679000000857500001,
        "CheckpointLSN":  17126679000000045100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500209700000)\/",
        "BackupFinishDate":  "\/Date(1500209700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "44e6a1f5-86cf-41e8-85b0-ff67ddaede0f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500209700000)\/",
        "Start":  "\/Date(1500209700000)\/",
        "BackupSetId":  "44e6a1f5-86cf-41e8-85b0-ff67ddaede0f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000000857500001,
        "LastLSN":  17126679000001584300001,
        "CheckpointLSN":  17126679000000045100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500210000000)\/",
        "BackupFinishDate":  "\/Date(1500210000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b79e599f-1ee8-477b-b012-47ca24778988",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500210000000)\/",
        "Start":  "\/Date(1500210000000)\/",
        "BackupSetId":  "b79e599f-1ee8-477b-b012-47ca24778988",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000001584300001,
        "LastLSN":  17126679000002303200001,
        "CheckpointLSN":  17126679000002195800023,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500210300000)\/",
        "BackupFinishDate":  "\/Date(1500210300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c11dab05-9f6e-4f9d-832d-a57634975679",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500210300000)\/",
        "Start":  "\/Date(1500210300000)\/",
        "BackupSetId":  "c11dab05-9f6e-4f9d-832d-a57634975679",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000002303200001,
        "LastLSN":  17126679000003020500001,
        "CheckpointLSN":  17126679000002195800023,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500210600000)\/",
        "BackupFinishDate":  "\/Date(1500210600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "471d3251-5248-4125-9737-9c61098b6c02",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500210600000)\/",
        "Start":  "\/Date(1500210600000)\/",
        "BackupSetId":  "471d3251-5248-4125-9737-9c61098b6c02",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000003020500001,
        "LastLSN":  17126679000003721800001,
        "CheckpointLSN":  17126679000002195800023,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500210901000)\/",
        "BackupFinishDate":  "\/Date(1500210901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c6493046-5314-465a-a0a5-03c3dea8808f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500210901000)\/",
        "Start":  "\/Date(1500210901000)\/",
        "BackupSetId":  "c6493046-5314-465a-a0a5-03c3dea8808f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000003721800001,
        "LastLSN":  17126679000004452800001,
        "CheckpointLSN":  17126679000004347100020,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500211200000)\/",
        "BackupFinishDate":  "\/Date(1500211200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "44c0fc49-a585-4690-8389-485eb74a5b7b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500211200000)\/",
        "Start":  "\/Date(1500211200000)\/",
        "BackupSetId":  "44c0fc49-a585-4690-8389-485eb74a5b7b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000004452800001,
        "LastLSN":  17126679000005155200001,
        "CheckpointLSN":  17126679000004347100020,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500211500000)\/",
        "BackupFinishDate":  "\/Date(1500211500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e2ec47fb-954c-41ae-80c0-dd32aef7fba0",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500211500000)\/",
        "Start":  "\/Date(1500211500000)\/",
        "BackupSetId":  "e2ec47fb-954c-41ae-80c0-dd32aef7fba0",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000005155200001,
        "LastLSN":  17126679000005856100001,
        "CheckpointLSN":  17126679000004347100020,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500211800000)\/",
        "BackupFinishDate":  "\/Date(1500211800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "731950a1-daf7-42aa-81f1-0c31fcdd97a0",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500211800000)\/",
        "Start":  "\/Date(1500211800000)\/",
        "BackupSetId":  "731950a1-daf7-42aa-81f1-0c31fcdd97a0",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000005856100001,
        "LastLSN":  17126679000006600400001,
        "CheckpointLSN":  17126679000006493300008,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500212100000)\/",
        "BackupFinishDate":  "\/Date(1500212100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "76619470-8829-47bd-b94d-88c5eb2ad9ad",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500212100000)\/",
        "Start":  "\/Date(1500212100000)\/",
        "BackupSetId":  "76619470-8829-47bd-b94d-88c5eb2ad9ad",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000006600400001,
        "LastLSN":  17126679000007343800001,
        "CheckpointLSN":  17126679000006493300008,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500212401000)\/",
        "BackupFinishDate":  "\/Date(1500212401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c870fc80-33de-4bd3-a675-6f25c96f0467",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500212401000)\/",
        "Start":  "\/Date(1500212401000)\/",
        "BackupSetId":  "c870fc80-33de-4bd3-a675-6f25c96f0467",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000007343800001,
        "LastLSN":  17126679000008081800001,
        "CheckpointLSN":  17126679000006493300008,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500212700000)\/",
        "BackupFinishDate":  "\/Date(1500212700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0dff8253-aecd-4050-83c4-659ab7cfff35",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500212700000)\/",
        "Start":  "\/Date(1500212700000)\/",
        "BackupSetId":  "0dff8253-aecd-4050-83c4-659ab7cfff35",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000008081800001,
        "LastLSN":  17126679000008816700001,
        "CheckpointLSN":  17126679000008631400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500213000000)\/",
        "BackupFinishDate":  "\/Date(1500213000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9751b4ca-5837-4bf4-ab9c-0e9d5eb7b774",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500213000000)\/",
        "Start":  "\/Date(1500213000000)\/",
        "BackupSetId":  "9751b4ca-5837-4bf4-ab9c-0e9d5eb7b774",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000008816700001,
        "LastLSN":  17126679000009546700001,
        "CheckpointLSN":  17126679000008631400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500213300000)\/",
        "BackupFinishDate":  "\/Date(1500213300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b5be0e6c-3e04-431c-8e5a-07970770d457",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500213300000)\/",
        "Start":  "\/Date(1500213300000)\/",
        "BackupSetId":  "b5be0e6c-3e04-431c-8e5a-07970770d457",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000009546700001,
        "LastLSN":  17126679000010262700001,
        "CheckpointLSN":  17126679000008631400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500213600000)\/",
        "BackupFinishDate":  "\/Date(1500213600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4bf7d7da-7ac8-4079-b424-b2bf91aece41",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500213600000)\/",
        "Start":  "\/Date(1500213600000)\/",
        "BackupSetId":  "4bf7d7da-7ac8-4079-b424-b2bf91aece41",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000010262700001,
        "LastLSN":  17126679000011007300001,
        "CheckpointLSN":  17126679000010697500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500213900000)\/",
        "BackupFinishDate":  "\/Date(1500213901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "52ab811e-8fc6-4623-8788-e07717e70ce6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500213901000)\/",
        "Start":  "\/Date(1500213900000)\/",
        "BackupSetId":  "52ab811e-8fc6-4623-8788-e07717e70ce6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000011007300001,
        "LastLSN":  17126679000011722700001,
        "CheckpointLSN":  17126679000010697500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500214200000)\/",
        "BackupFinishDate":  "\/Date(1500214200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "de4a62e9-d888-4542-8bda-011fc343daab",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500214200000)\/",
        "Start":  "\/Date(1500214200000)\/",
        "BackupSetId":  "de4a62e9-d888-4542-8bda-011fc343daab",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000011722700001,
        "LastLSN":  17126679000012449700001,
        "CheckpointLSN":  17126679000010697500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500214500000)\/",
        "BackupFinishDate":  "\/Date(1500214500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8c60a5db-b4da-40fd-9f6a-886e1735c204",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500214500000)\/",
        "Start":  "\/Date(1500214500000)\/",
        "BackupSetId":  "8c60a5db-b4da-40fd-9f6a-886e1735c204",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000012449700001,
        "LastLSN":  17126680000000063800001,
        "CheckpointLSN":  17126679000012765500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500214800000)\/",
        "BackupFinishDate":  "\/Date(1500214800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "271e29b0-413c-4a9b-aae6-c9a196956e51",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500214800000)\/",
        "Start":  "\/Date(1500214800000)\/",
        "BackupSetId":  "271e29b0-413c-4a9b-aae6-c9a196956e51",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000000063800001,
        "LastLSN":  17126680000000805400001,
        "CheckpointLSN":  17126680000000063800030,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500215100000)\/",
        "BackupFinishDate":  "\/Date(1500215100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "08c0e74f-9fc1-482a-996f-6314bfc50a4e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500215100000)\/",
        "Start":  "\/Date(1500215100000)\/",
        "BackupSetId":  "08c0e74f-9fc1-482a-996f-6314bfc50a4e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000000805400001,
        "LastLSN":  17126680000001522700001,
        "CheckpointLSN":  17126680000000063800030,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500215400000)\/",
        "BackupFinishDate":  "\/Date(1500215401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6dceb2e8-8fb2-46d0-af53-76f97cbbd69a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500215401000)\/",
        "Start":  "\/Date(1500215400000)\/",
        "BackupSetId":  "6dceb2e8-8fb2-46d0-af53-76f97cbbd69a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000001522700001,
        "LastLSN":  17126680000002268200001,
        "CheckpointLSN":  17126680000002131600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500215701000)\/",
        "BackupFinishDate":  "\/Date(1500215701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "851d8df0-aae4-4d89-92cd-ff4759811ea8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500215701000)\/",
        "Start":  "\/Date(1500215701000)\/",
        "BackupSetId":  "851d8df0-aae4-4d89-92cd-ff4759811ea8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000002268200001,
        "LastLSN":  17126680000002974200001,
        "CheckpointLSN":  17126680000002131600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500216000000)\/",
        "BackupFinishDate":  "\/Date(1500216000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "18839b34-298f-434e-aee8-fe5bd8efb4be",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500216000000)\/",
        "Start":  "\/Date(1500216000000)\/",
        "BackupSetId":  "18839b34-298f-434e-aee8-fe5bd8efb4be",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000002974200001,
        "LastLSN":  17126680000003692400001,
        "CheckpointLSN":  17126680000002131600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500216300000)\/",
        "BackupFinishDate":  "\/Date(1500216300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7dba5e97-937c-473d-a237-602bf6c14c3b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500216300000)\/",
        "Start":  "\/Date(1500216300000)\/",
        "BackupSetId":  "7dba5e97-937c-473d-a237-602bf6c14c3b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000003692400001,
        "LastLSN":  17126680000004413600001,
        "CheckpointLSN":  17126680000004243700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500216600000)\/",
        "BackupFinishDate":  "\/Date(1500216600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0be95ed7-8b55-401e-80b6-bb1ab5066dbe",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500216600000)\/",
        "Start":  "\/Date(1500216600000)\/",
        "BackupSetId":  "0be95ed7-8b55-401e-80b6-bb1ab5066dbe",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000004413600001,
        "LastLSN":  17126680000005134100001,
        "CheckpointLSN":  17126680000004243700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500216901000)\/",
        "BackupFinishDate":  "\/Date(1500216901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "862e467d-1548-4fd5-a5c4-20ebe715c8cc",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500216901000)\/",
        "Start":  "\/Date(1500216901000)\/",
        "BackupSetId":  "862e467d-1548-4fd5-a5c4-20ebe715c8cc",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000005134100001,
        "LastLSN":  17126680000005864000001,
        "CheckpointLSN":  17126680000004243700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500217200000)\/",
        "BackupFinishDate":  "\/Date(1500217200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "891f5484-c248-4e75-9e8a-3bd846b3f26d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500217200000)\/",
        "Start":  "\/Date(1500217200000)\/",
        "BackupSetId":  "891f5484-c248-4e75-9e8a-3bd846b3f26d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000005864000001,
        "LastLSN":  17126680000006584000001,
        "CheckpointLSN":  17126680000006310800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500217500000)\/",
        "BackupFinishDate":  "\/Date(1500217500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0928f3d2-a45f-48e2-9230-6b6789ca6417",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500217500000)\/",
        "Start":  "\/Date(1500217500000)\/",
        "BackupSetId":  "0928f3d2-a45f-48e2-9230-6b6789ca6417",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000006584000001,
        "LastLSN":  17126680000007298600001,
        "CheckpointLSN":  17126680000006310800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500217800000)\/",
        "BackupFinishDate":  "\/Date(1500217800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "20153807-99d2-482f-93dc-a224fe72f02e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500217800000)\/",
        "Start":  "\/Date(1500217800000)\/",
        "BackupSetId":  "20153807-99d2-482f-93dc-a224fe72f02e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000007298600001,
        "LastLSN":  17126680000008002200001,
        "CheckpointLSN":  17126680000006310800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500218101000)\/",
        "BackupFinishDate":  "\/Date(1500218101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "903df7a9-4592-47c2-8368-17a858556cf1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500218101000)\/",
        "Start":  "\/Date(1500218101000)\/",
        "BackupSetId":  "903df7a9-4592-47c2-8368-17a858556cf1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000008002200001,
        "LastLSN":  17126680000008729800001,
        "CheckpointLSN":  17126680000008376600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500218400000)\/",
        "BackupFinishDate":  "\/Date(1500218400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8d0e6e57-e3f8-4216-af60-80aa64d08b15",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500218400000)\/",
        "Start":  "\/Date(1500218400000)\/",
        "BackupSetId":  "8d0e6e57-e3f8-4216-af60-80aa64d08b15",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000008729800001,
        "LastLSN":  17126680000009438300001,
        "CheckpointLSN":  17126680000008376600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500218700000)\/",
        "BackupFinishDate":  "\/Date(1500218700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0fbc90bf-1d05-464f-b8b6-1a25ce7f240c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500218700000)\/",
        "Start":  "\/Date(1500218700000)\/",
        "BackupSetId":  "0fbc90bf-1d05-464f-b8b6-1a25ce7f240c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000009438300001,
        "LastLSN":  17126680000010140500001,
        "CheckpointLSN":  17126680000008376600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500219000000)\/",
        "BackupFinishDate":  "\/Date(1500219000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2c98f2c5-c780-46b1-9c2f-f54e318a6e30",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500219000000)\/",
        "Start":  "\/Date(1500219000000)\/",
        "BackupSetId":  "2c98f2c5-c780-46b1-9c2f-f54e318a6e30",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000010140500001,
        "LastLSN":  17126680000010890200001,
        "CheckpointLSN":  17126680000010444000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500219300000)\/",
        "BackupFinishDate":  "\/Date(1500219300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "18acba9e-3fd2-4286-9637-a60d19804f2f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500219300000)\/",
        "Start":  "\/Date(1500219300000)\/",
        "BackupSetId":  "18acba9e-3fd2-4286-9637-a60d19804f2f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000010890200001,
        "LastLSN":  17126680000012325500001,
        "CheckpointLSN":  17126680000010444000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500219601000)\/",
        "BackupFinishDate":  "\/Date(1500219601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e318c099-ed1e-4b0b-9ae1-31cfd1eb1575",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500219601000)\/",
        "Start":  "\/Date(1500219601000)\/",
        "BackupSetId":  "e318c099-ed1e-4b0b-9ae1-31cfd1eb1575",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000012325500001,
        "LastLSN":  17126680000013083200001,
        "CheckpointLSN":  17126680000012514000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500219900000)\/",
        "BackupFinishDate":  "\/Date(1500219900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ec2c7b0f-6d35-4d1e-b6cf-f90193e63387",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500219900000)\/",
        "Start":  "\/Date(1500219900000)\/",
        "BackupSetId":  "ec2c7b0f-6d35-4d1e-b6cf-f90193e63387",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000013083200001,
        "LastLSN":  17126681000000702400001,
        "CheckpointLSN":  17126680000012514000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500220200000)\/",
        "BackupFinishDate":  "\/Date(1500220200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "cdf94a75-4f41-4eaf-bada-82d150cb6b81",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500220200000)\/",
        "Start":  "\/Date(1500220200000)\/",
        "BackupSetId":  "cdf94a75-4f41-4eaf-bada-82d150cb6b81",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000000702400001,
        "LastLSN":  17126681000001451300001,
        "CheckpointLSN":  17126681000000702400032,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500220500000)\/",
        "BackupFinishDate":  "\/Date(1500220500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c10d9242-7806-4861-b58e-782f01c0faac",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500220500000)\/",
        "Start":  "\/Date(1500220500000)\/",
        "BackupSetId":  "c10d9242-7806-4861-b58e-782f01c0faac",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000001451300001,
        "LastLSN":  17126681000002166200001,
        "CheckpointLSN":  17126681000000702400032,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500220800000)\/",
        "BackupFinishDate":  "\/Date(1500220801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "48b9fdb6-7770-40dd-9b05-40a6418320e6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500220801000)\/",
        "Start":  "\/Date(1500220800000)\/",
        "BackupSetId":  "48b9fdb6-7770-40dd-9b05-40a6418320e6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000002166200001,
        "LastLSN":  17126681000002765900001,
        "CheckpointLSN":  17126681000000702400032,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500221100000)\/",
        "BackupFinishDate":  "\/Date(1500221100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8673dd0a-946e-48a6-b68f-4861090530dd",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500221100000)\/",
        "Start":  "\/Date(1500221100000)\/",
        "BackupSetId":  "8673dd0a-946e-48a6-b68f-4861090530dd",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000002765900001,
        "LastLSN":  17126681000003499800001,
        "CheckpointLSN":  17126681000002782900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500221400000)\/",
        "BackupFinishDate":  "\/Date(1500221400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7db09a5a-0c4b-419a-a2f5-d11d05fd9428",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500221400000)\/",
        "Start":  "\/Date(1500221400000)\/",
        "BackupSetId":  "7db09a5a-0c4b-419a-a2f5-d11d05fd9428",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000003499800001,
        "LastLSN":  17126681000004229400001,
        "CheckpointLSN":  17126681000002782900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500221700000)\/",
        "BackupFinishDate":  "\/Date(1500221700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "15cb41c2-3987-46a5-919c-239773bb7278",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500221700000)\/",
        "Start":  "\/Date(1500221700000)\/",
        "BackupSetId":  "15cb41c2-3987-46a5-919c-239773bb7278",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000004229400001,
        "LastLSN":  17126681000004952100001,
        "CheckpointLSN":  17126681000004909800027,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500222000000)\/",
        "BackupFinishDate":  "\/Date(1500222000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "237f52ff-eca9-4a53-8b7c-e2139d791e48",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500222000000)\/",
        "Start":  "\/Date(1500222000000)\/",
        "BackupSetId":  "237f52ff-eca9-4a53-8b7c-e2139d791e48",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000004952100001,
        "LastLSN":  17126681000005680900001,
        "CheckpointLSN":  17126681000004909800027,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500222301000)\/",
        "BackupFinishDate":  "\/Date(1500222301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c17e9cdb-963b-41f4-8df4-90bbc5e9d48e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500222301000)\/",
        "Start":  "\/Date(1500222301000)\/",
        "BackupSetId":  "c17e9cdb-963b-41f4-8df4-90bbc5e9d48e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000005680900001,
        "LastLSN":  17126681000006395700001,
        "CheckpointLSN":  17126681000004909800027,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500222600000)\/",
        "BackupFinishDate":  "\/Date(1500222600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "bdaf5521-d683-45b3-8cb3-b4fe8760446e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500222600000)\/",
        "Start":  "\/Date(1500222600000)\/",
        "BackupSetId":  "bdaf5521-d683-45b3-8cb3-b4fe8760446e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000006395700001,
        "LastLSN":  17126681000007139900001,
        "CheckpointLSN":  17126681000006976900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500222900000)\/",
        "BackupFinishDate":  "\/Date(1500222900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "38b68220-944d-4665-a498-2d8f1b8d5ab8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500222900000)\/",
        "Start":  "\/Date(1500222900000)\/",
        "BackupSetId":  "38b68220-944d-4665-a498-2d8f1b8d5ab8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000007139900001,
        "LastLSN":  17126681000007845400001,
        "CheckpointLSN":  17126681000006976900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500223200000)\/",
        "BackupFinishDate":  "\/Date(1500223200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e7b09873-d9a0-4486-b0ce-b33584c792a7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500223200000)\/",
        "Start":  "\/Date(1500223200000)\/",
        "BackupSetId":  "e7b09873-d9a0-4486-b0ce-b33584c792a7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000007845400001,
        "LastLSN":  17126681000008560100001,
        "CheckpointLSN":  17126681000006976900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500223500000)\/",
        "BackupFinishDate":  "\/Date(1500223500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "255e1f9d-5e4a-441b-8a8b-4ad911de09f0",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500223500000)\/",
        "Start":  "\/Date(1500223500000)\/",
        "BackupSetId":  "255e1f9d-5e4a-441b-8a8b-4ad911de09f0",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000008560100001,
        "LastLSN":  17126681000009279900001,
        "CheckpointLSN":  17126681000009045600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500223800000)\/",
        "BackupFinishDate":  "\/Date(1500223800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "79f42353-649b-41fe-a174-e9078e0b1ccb",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500223800000)\/",
        "Start":  "\/Date(1500223800000)\/",
        "BackupSetId":  "79f42353-649b-41fe-a174-e9078e0b1ccb",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000009279900001,
        "LastLSN":  17126681000010000200001,
        "CheckpointLSN":  17126681000009045600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500224100000)\/",
        "BackupFinishDate":  "\/Date(1500224100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1c078fff-5906-471e-888b-f83a3e89340c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500224100000)\/",
        "Start":  "\/Date(1500224100000)\/",
        "BackupSetId":  "1c078fff-5906-471e-888b-f83a3e89340c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000010000200001,
        "LastLSN":  17126681000010727200001,
        "CheckpointLSN":  17126681000009045600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500224400000)\/",
        "BackupFinishDate":  "\/Date(1500224400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "753c5f71-b26e-4296-99da-a876390d5bdf",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500224400000)\/",
        "Start":  "\/Date(1500224400000)\/",
        "BackupSetId":  "753c5f71-b26e-4296-99da-a876390d5bdf",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000010727200001,
        "LastLSN":  17126681000011444900001,
        "CheckpointLSN":  17126681000010739200127,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500224700000)\/",
        "BackupFinishDate":  "\/Date(1500224701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "dddfa0f2-ce99-4d09-8f8f-2786f6ccc6c3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500224701000)\/",
        "Start":  "\/Date(1500224700000)\/",
        "BackupSetId":  "dddfa0f2-ce99-4d09-8f8f-2786f6ccc6c3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000011444900001,
        "LastLSN":  17126681000012159400001,
        "CheckpointLSN":  17126681000010739200127,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500225001000)\/",
        "BackupFinishDate":  "\/Date(1500225001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b20e822e-e77f-4e2f-af4e-f57c7209456b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500225001000)\/",
        "Start":  "\/Date(1500225001000)\/",
        "BackupSetId":  "b20e822e-e77f-4e2f-af4e-f57c7209456b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000012159400001,
        "LastLSN":  17126681000012878100001,
        "CheckpointLSN":  17126681000012792900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500225300000)\/",
        "BackupFinishDate":  "\/Date(1500225300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "66b1c85d-9c92-4f3e-8370-baa4a7cc2d06",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500225300000)\/",
        "Start":  "\/Date(1500225300000)\/",
        "BackupSetId":  "66b1c85d-9c92-4f3e-8370-baa4a7cc2d06",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000012878100001,
        "LastLSN":  17126682000000487900001,
        "CheckpointLSN":  17126681000012792900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500225600000)\/",
        "BackupFinishDate":  "\/Date(1500225600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a0543d6a-9334-4540-9e4c-5a9e400fd4e3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500225600000)\/",
        "Start":  "\/Date(1500225600000)\/",
        "BackupSetId":  "a0543d6a-9334-4540-9e4c-5a9e400fd4e3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000000487900001,
        "LastLSN":  17126682000001211100001,
        "CheckpointLSN":  17126682000000487900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500225900000)\/",
        "BackupFinishDate":  "\/Date(1500225900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "28b4b6b1-e482-415c-b5c5-38796be84858",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500225900000)\/",
        "Start":  "\/Date(1500225900000)\/",
        "BackupSetId":  "28b4b6b1-e482-415c-b5c5-38796be84858",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000001211100001,
        "LastLSN":  17126682000001914300001,
        "CheckpointLSN":  17126682000000487900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500226200000)\/",
        "BackupFinishDate":  "\/Date(1500226200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3982c28b-5e3b-4c77-aa51-70bc361de2f5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500226200000)\/",
        "Start":  "\/Date(1500226200000)\/",
        "BackupSetId":  "3982c28b-5e3b-4c77-aa51-70bc361de2f5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000001914300001,
        "LastLSN":  17126682000002659900001,
        "CheckpointLSN":  17126682000002553900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500226500000)\/",
        "BackupFinishDate":  "\/Date(1500226501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3156ea6a-4aaf-457f-bcbf-184697e673a6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500226501000)\/",
        "Start":  "\/Date(1500226500000)\/",
        "BackupSetId":  "3156ea6a-4aaf-457f-bcbf-184697e673a6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000002659900001,
        "LastLSN":  17126682000003403700001,
        "CheckpointLSN":  17126682000002553900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500226800000)\/",
        "BackupFinishDate":  "\/Date(1500226800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "33b8c6e4-6709-4e1e-b3e4-30b8a08f22e5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500226800000)\/",
        "Start":  "\/Date(1500226800000)\/",
        "BackupSetId":  "33b8c6e4-6709-4e1e-b3e4-30b8a08f22e5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000003403700001,
        "LastLSN":  17126682000004146900001,
        "CheckpointLSN":  17126682000002553900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500227100000)\/",
        "BackupFinishDate":  "\/Date(1500227100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "de6cc3d1-1526-4183-9700-9cdd078fca96",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500227100000)\/",
        "Start":  "\/Date(1500227100000)\/",
        "BackupSetId":  "de6cc3d1-1526-4183-9700-9cdd078fca96",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000004146900001,
        "LastLSN":  17126682000004881300001,
        "CheckpointLSN":  17126682000004624800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500227400000)\/",
        "BackupFinishDate":  "\/Date(1500227400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9fddcd40-310f-47b4-b707-2285f5d9bd77",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500227400000)\/",
        "Start":  "\/Date(1500227400000)\/",
        "BackupSetId":  "9fddcd40-310f-47b4-b707-2285f5d9bd77",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000004881300001,
        "LastLSN":  17126682000005611300001,
        "CheckpointLSN":  17126682000004624800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500227701000)\/",
        "BackupFinishDate":  "\/Date(1500227701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a7898fe5-40d7-4b67-a3d3-8f595684277a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500227701000)\/",
        "Start":  "\/Date(1500227701000)\/",
        "BackupSetId":  "a7898fe5-40d7-4b67-a3d3-8f595684277a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000005611300001,
        "LastLSN":  17126682000006326400001,
        "CheckpointLSN":  17126682000004624800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500228000000)\/",
        "BackupFinishDate":  "\/Date(1500228000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0ddd4cd8-0e3f-478d-b248-4b027c936a6d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500228000000)\/",
        "Start":  "\/Date(1500228000000)\/",
        "BackupSetId":  "0ddd4cd8-0e3f-478d-b248-4b027c936a6d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000006326400001,
        "LastLSN":  17126682000007074400001,
        "CheckpointLSN":  17126682000006691600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500228300000)\/",
        "BackupFinishDate":  "\/Date(1500228300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "abd61b4f-79cf-460c-ae4b-3448ddfd0b96",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500228300000)\/",
        "Start":  "\/Date(1500228300000)\/",
        "BackupSetId":  "abd61b4f-79cf-460c-ae4b-3448ddfd0b96",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000007074400001,
        "LastLSN":  17126682000007791400001,
        "CheckpointLSN":  17126682000006691600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500228600000)\/",
        "BackupFinishDate":  "\/Date(1500228600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ce54ab35-1116-40ac-82ec-69b0cd314828",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500228600000)\/",
        "Start":  "\/Date(1500228600000)\/",
        "BackupSetId":  "ce54ab35-1116-40ac-82ec-69b0cd314828",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000007791400001,
        "LastLSN":  17126682000008519100001,
        "CheckpointLSN":  17126682000006691600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500228900000)\/",
        "BackupFinishDate":  "\/Date(1500228900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "96ff25bf-817b-4b66-b910-04ee551a76d5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500228900000)\/",
        "Start":  "\/Date(1500228900000)\/",
        "BackupSetId":  "96ff25bf-817b-4b66-b910-04ee551a76d5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000008519100001,
        "LastLSN":  17126682000009242700001,
        "CheckpointLSN":  17126682000008764200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500229201000)\/",
        "BackupFinishDate":  "\/Date(1500229201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "35f8dcd4-76d3-49e3-a1e1-b4502e135731",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500229201000)\/",
        "Start":  "\/Date(1500229201000)\/",
        "BackupSetId":  "35f8dcd4-76d3-49e3-a1e1-b4502e135731",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000009242700001,
        "LastLSN":  17126682000009970700001,
        "CheckpointLSN":  17126682000008764200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500229500000)\/",
        "BackupFinishDate":  "\/Date(1500229500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ac379ce4-cef7-41ff-a322-125350220d20",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500229500000)\/",
        "Start":  "\/Date(1500229500000)\/",
        "BackupSetId":  "ac379ce4-cef7-41ff-a322-125350220d20",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000009970700001,
        "LastLSN":  17126682000010686000001,
        "CheckpointLSN":  17126682000008764200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500229800000)\/",
        "BackupFinishDate":  "\/Date(1500229800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "da9a3dd8-7add-4208-bb33-b8edf9321b91",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500229800000)\/",
        "Start":  "\/Date(1500229800000)\/",
        "BackupSetId":  "da9a3dd8-7add-4208-bb33-b8edf9321b91",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000010686000001,
        "LastLSN":  17126682000011432000001,
        "CheckpointLSN":  17126682000010830700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500230100000)\/",
        "BackupFinishDate":  "\/Date(1500230100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fe0e3763-312a-474d-b01f-c432388a0133",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500230100000)\/",
        "Start":  "\/Date(1500230100000)\/",
        "BackupSetId":  "fe0e3763-312a-474d-b01f-c432388a0133",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000011432000001,
        "LastLSN":  17126682000012133800001,
        "CheckpointLSN":  17126682000010830700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500230401000)\/",
        "BackupFinishDate":  "\/Date(1500230401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ebe6a308-8279-4039-bf0d-447c1a235a46",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500230401000)\/",
        "Start":  "\/Date(1500230401000)\/",
        "BackupSetId":  "ebe6a308-8279-4039-bf0d-447c1a235a46",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000012133800001,
        "LastLSN":  17126682000012850000001,
        "CheckpointLSN":  17126682000010830700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500230700000)\/",
        "BackupFinishDate":  "\/Date(1500230700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b981a1ea-70f9-42db-9014-a145b8361dcb",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500230700000)\/",
        "Start":  "\/Date(1500230700000)\/",
        "BackupSetId":  "b981a1ea-70f9-42db-9014-a145b8361dcb",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000012850000001,
        "LastLSN":  17126683000000470400001,
        "CheckpointLSN":  17126682000012915200020,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500231000000)\/",
        "BackupFinishDate":  "\/Date(1500231000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5675acc2-185a-48a4-a963-eb292be45af8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500231000000)\/",
        "Start":  "\/Date(1500231000000)\/",
        "BackupSetId":  "5675acc2-185a-48a4-a963-eb292be45af8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000000470400001,
        "LastLSN":  17126683000001205800001,
        "CheckpointLSN":  17126683000000470500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500231300000)\/",
        "BackupFinishDate":  "\/Date(1500231300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5e9fc50f-379d-43f6-9a0e-31ab460e6311",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500231300000)\/",
        "Start":  "\/Date(1500231300000)\/",
        "BackupSetId":  "5e9fc50f-379d-43f6-9a0e-31ab460e6311",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000001205800001,
        "LastLSN":  17126683000001934100001,
        "CheckpointLSN":  17126683000000470500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500231601000)\/",
        "BackupFinishDate":  "\/Date(1500231601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b99b6e07-8b64-4a54-9549-a66c1ef6757b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500231601000)\/",
        "Start":  "\/Date(1500231601000)\/",
        "BackupSetId":  "b99b6e07-8b64-4a54-9549-a66c1ef6757b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000001934100001,
        "LastLSN":  17126683000003017500001,
        "CheckpointLSN":  17126683000002534300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500231900000)\/",
        "BackupFinishDate":  "\/Date(1500231900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a8d6e797-b163-430e-b741-71c045d1565e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500231900000)\/",
        "Start":  "\/Date(1500231900000)\/",
        "BackupSetId":  "a8d6e797-b163-430e-b741-71c045d1565e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000003017500001,
        "LastLSN":  17126683000003904900001,
        "CheckpointLSN":  17126683000002534300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500232200000)\/",
        "BackupFinishDate":  "\/Date(1500232201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "28960343-6340-48b5-a557-470e658a3269",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500232201000)\/",
        "Start":  "\/Date(1500232200000)\/",
        "BackupSetId":  "28960343-6340-48b5-a557-470e658a3269",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000003904900001,
        "LastLSN":  17126683000004771000001,
        "CheckpointLSN":  17126683000004614100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500232500000)\/",
        "BackupFinishDate":  "\/Date(1500232500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ce7faf45-8273-4cdb-b38e-bcbb245b7c6e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500232500000)\/",
        "Start":  "\/Date(1500232500000)\/",
        "BackupSetId":  "ce7faf45-8273-4cdb-b38e-bcbb245b7c6e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000004771000001,
        "LastLSN":  17126683000005488900001,
        "CheckpointLSN":  17126683000004614100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500232801000)\/",
        "BackupFinishDate":  "\/Date(1500232801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f4fa7ac6-74aa-4438-b458-ab7369d266da",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500232801000)\/",
        "Start":  "\/Date(1500232801000)\/",
        "BackupSetId":  "f4fa7ac6-74aa-4438-b458-ab7369d266da",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000005488900001,
        "LastLSN":  17126683000006189900001,
        "CheckpointLSN":  17126683000004614100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500233100000)\/",
        "BackupFinishDate":  "\/Date(1500233100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "654035aa-5f05-40aa-9e25-8591182ecbbc",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500233100000)\/",
        "Start":  "\/Date(1500233100000)\/",
        "BackupSetId":  "654035aa-5f05-40aa-9e25-8591182ecbbc",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000006189900001,
        "LastLSN":  17126683000006908300001,
        "CheckpointLSN":  17126683000006679800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500233400000)\/",
        "BackupFinishDate":  "\/Date(1500233400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3782fbfe-9ee6-4b39-8f05-4f0db92d473b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500233400000)\/",
        "Start":  "\/Date(1500233400000)\/",
        "BackupSetId":  "3782fbfe-9ee6-4b39-8f05-4f0db92d473b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000006908300001,
        "LastLSN":  17126683000007636900001,
        "CheckpointLSN":  17126683000006679800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500233700000)\/",
        "BackupFinishDate":  "\/Date(1500233701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "68f4ee67-1ef8-4e5c-b694-b74cf5c452d2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500233701000)\/",
        "Start":  "\/Date(1500233700000)\/",
        "BackupSetId":  "68f4ee67-1ef8-4e5c-b694-b74cf5c452d2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000007636900001,
        "LastLSN":  17126683000008377600001,
        "CheckpointLSN":  17126683000006679800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500234000000)\/",
        "BackupFinishDate":  "\/Date(1500234000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f3649c2a-391e-453d-8073-4cdce21d638a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500234000000)\/",
        "Start":  "\/Date(1500234000000)\/",
        "BackupSetId":  "f3649c2a-391e-453d-8073-4cdce21d638a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000008377600001,
        "LastLSN":  17126683000009132500001,
        "CheckpointLSN":  17126683000008745700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500234300000)\/",
        "BackupFinishDate":  "\/Date(1500234300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "012bc43c-b554-4887-a6fd-d8da7f6e2e4a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500234300000)\/",
        "Start":  "\/Date(1500234300000)\/",
        "BackupSetId":  "012bc43c-b554-4887-a6fd-d8da7f6e2e4a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000009132500001,
        "LastLSN":  17126683000009847200001,
        "CheckpointLSN":  17126683000008745700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500234600000)\/",
        "BackupFinishDate":  "\/Date(1500234600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e037904c-0e04-47bb-a9c4-12aa085f0454",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500234600000)\/",
        "Start":  "\/Date(1500234600000)\/",
        "BackupSetId":  "e037904c-0e04-47bb-a9c4-12aa085f0454",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000009847200001,
        "LastLSN":  17126683000010578500001,
        "CheckpointLSN":  17126683000008745700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500234900000)\/",
        "BackupFinishDate":  "\/Date(1500234900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2344c6db-3994-4e95-9437-00bb3d846339",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500234900000)\/",
        "Start":  "\/Date(1500234900000)\/",
        "BackupSetId":  "2344c6db-3994-4e95-9437-00bb3d846339",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000010578500001,
        "LastLSN":  17126683000011311300001,
        "CheckpointLSN":  17126683000010814600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500235200000)\/",
        "BackupFinishDate":  "\/Date(1500235201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "93b6f4f5-d941-48a3-9cf1-4a057b560f6b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500235201000)\/",
        "Start":  "\/Date(1500235200000)\/",
        "BackupSetId":  "93b6f4f5-d941-48a3-9cf1-4a057b560f6b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000011311300001,
        "LastLSN":  17126683000012101400001,
        "CheckpointLSN":  17126683000010814600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500235500000)\/",
        "BackupFinishDate":  "\/Date(1500235501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d4ddd413-7f61-46ed-8fed-7b42b3c64fac",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500235501000)\/",
        "Start":  "\/Date(1500235500000)\/",
        "BackupSetId":  "d4ddd413-7f61-46ed-8fed-7b42b3c64fac",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000012101400001,
        "LastLSN":  17126683000012814300001,
        "CheckpointLSN":  17126683000010814600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500235800000)\/",
        "BackupFinishDate":  "\/Date(1500235800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b8601787-062e-42c1-9da7-0fdc5ef34f01",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500235800000)\/",
        "Start":  "\/Date(1500235800000)\/",
        "BackupSetId":  "b8601787-062e-42c1-9da7-0fdc5ef34f01",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000012814300001,
        "LastLSN":  17126684000000465300001,
        "CheckpointLSN":  17126683000012901100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500236100000)\/",
        "BackupFinishDate":  "\/Date(1500236100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "83abeb49-abc9-4408-947a-abd769145eba",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500236100000)\/",
        "Start":  "\/Date(1500236100000)\/",
        "BackupSetId":  "83abeb49-abc9-4408-947a-abd769145eba",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000000465300001,
        "LastLSN":  17126684000001183800001,
        "CheckpointLSN":  17126684000000465300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500236400000)\/",
        "BackupFinishDate":  "\/Date(1500236400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b8a9d594-ef73-4e38-bb82-acf49a4adeda",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500236400000)\/",
        "Start":  "\/Date(1500236400000)\/",
        "BackupSetId":  "b8a9d594-ef73-4e38-bb82-acf49a4adeda",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000001183800001,
        "LastLSN":  17126684000001912500001,
        "CheckpointLSN":  17126684000000465300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500236700000)\/",
        "BackupFinishDate":  "\/Date(1500236700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8405ffd7-f6f2-4ca2-a3e7-19c02b1b7a5f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500236700000)\/",
        "Start":  "\/Date(1500236700000)\/",
        "BackupSetId":  "8405ffd7-f6f2-4ca2-a3e7-19c02b1b7a5f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000001912500001,
        "LastLSN":  17126684000002644100001,
        "CheckpointLSN":  17126684000002530900004,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500237001000)\/",
        "BackupFinishDate":  "\/Date(1500237001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b5686649-0a07-4a26-9454-eb0056fe83af",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500237001000)\/",
        "Start":  "\/Date(1500237001000)\/",
        "BackupSetId":  "b5686649-0a07-4a26-9454-eb0056fe83af",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000002644100001,
        "LastLSN":  17126684000003344900001,
        "CheckpointLSN":  17126684000002530900004,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500237300000)\/",
        "BackupFinishDate":  "\/Date(1500237300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "504353e3-0034-4aa7-8743-cb620f75e107",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500237300000)\/",
        "Start":  "\/Date(1500237300000)\/",
        "BackupSetId":  "504353e3-0034-4aa7-8743-cb620f75e107",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000003344900001,
        "LastLSN":  17126684000004031300001,
        "CheckpointLSN":  17126684000002530900004,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500237600000)\/",
        "BackupFinishDate":  "\/Date(1500237600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "68d00574-4d9a-432d-b830-f74792d22d28",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500237600000)\/",
        "Start":  "\/Date(1500237600000)\/",
        "BackupSetId":  "68d00574-4d9a-432d-b830-f74792d22d28",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000004031300001,
        "LastLSN":  17126684000004762900001,
        "CheckpointLSN":  17126684000004597700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500237900000)\/",
        "BackupFinishDate":  "\/Date(1500237900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6ab4e7a1-c887-4e17-bb79-a729014e75a1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500237900000)\/",
        "Start":  "\/Date(1500237900000)\/",
        "BackupSetId":  "6ab4e7a1-c887-4e17-bb79-a729014e75a1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000004762900001,
        "LastLSN":  17126684000005463800001,
        "CheckpointLSN":  17126684000004597700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500238201000)\/",
        "BackupFinishDate":  "\/Date(1500238201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f812e7d1-33c9-4870-bae6-9709f01e7eb3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500238201000)\/",
        "Start":  "\/Date(1500238201000)\/",
        "BackupSetId":  "f812e7d1-33c9-4870-bae6-9709f01e7eb3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000005463800001,
        "LastLSN":  17126684000006180400001,
        "CheckpointLSN":  17126684000004597700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500238500000)\/",
        "BackupFinishDate":  "\/Date(1500238500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "31b044a1-60ba-4773-b413-0ae1c3deaf70",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500238500000)\/",
        "Start":  "\/Date(1500238500000)\/",
        "BackupSetId":  "31b044a1-60ba-4773-b413-0ae1c3deaf70",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000006180400001,
        "LastLSN":  17126684000006925200001,
        "CheckpointLSN":  17126684000006664600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500238800000)\/",
        "BackupFinishDate":  "\/Date(1500238800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "56b853f5-d622-4672-bfa9-a2001335c596",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500238800000)\/",
        "Start":  "\/Date(1500238800000)\/",
        "BackupSetId":  "56b853f5-d622-4672-bfa9-a2001335c596",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000006925200001,
        "LastLSN":  17126684000007627800001,
        "CheckpointLSN":  17126684000006664600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500239100000)\/",
        "BackupFinishDate":  "\/Date(1500239100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "42585c63-ad28-4d02-952f-fd444eabc4d8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500239100000)\/",
        "Start":  "\/Date(1500239100000)\/",
        "BackupSetId":  "42585c63-ad28-4d02-952f-fd444eabc4d8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000007627800001,
        "LastLSN":  17126684000008342100001,
        "CheckpointLSN":  17126684000006664600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500239400000)\/",
        "BackupFinishDate":  "\/Date(1500239400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3af0f859-cc4b-4927-8fbf-fc8109d954e5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500239400000)\/",
        "Start":  "\/Date(1500239400000)\/",
        "BackupSetId":  "3af0f859-cc4b-4927-8fbf-fc8109d954e5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000008342100001,
        "LastLSN":  17126684000009061800001,
        "CheckpointLSN":  17126684000008730200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500239701000)\/",
        "BackupFinishDate":  "\/Date(1500239701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9640df99-a515-4445-90c3-68caf91f0232",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500239701000)\/",
        "Start":  "\/Date(1500239701000)\/",
        "BackupSetId":  "9640df99-a515-4445-90c3-68caf91f0232",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000009061800001,
        "LastLSN":  17126684000009777600001,
        "CheckpointLSN":  17126684000008730200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500240000000)\/",
        "BackupFinishDate":  "\/Date(1500240000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0057b8f9-6d60-4525-9fd3-c60bec5b1eb6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500240000000)\/",
        "Start":  "\/Date(1500240000000)\/",
        "BackupSetId":  "0057b8f9-6d60-4525-9fd3-c60bec5b1eb6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000009777600001,
        "LastLSN":  17126684000010478200001,
        "CheckpointLSN":  17126684000008730200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500240300000)\/",
        "BackupFinishDate":  "\/Date(1500240300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "603a1400-45ab-4cce-8273-0d6eee141082",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500240300000)\/",
        "Start":  "\/Date(1500240300000)\/",
        "BackupSetId":  "603a1400-45ab-4cce-8273-0d6eee141082",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000010478200001,
        "LastLSN":  17126684000011197700001,
        "CheckpointLSN":  17126684000010800900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500240600000)\/",
        "BackupFinishDate":  "\/Date(1500240600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e659924f-adf8-45b1-8d8c-2a9ce0116568",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500240600000)\/",
        "Start":  "\/Date(1500240600000)\/",
        "BackupSetId":  "e659924f-adf8-45b1-8d8c-2a9ce0116568",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000011197700001,
        "LastLSN":  17126684000011925700001,
        "CheckpointLSN":  17126684000010800900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500240901000)\/",
        "BackupFinishDate":  "\/Date(1500240901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fc81ea12-d814-4366-a595-028f8198b2ec",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500240901000)\/",
        "Start":  "\/Date(1500240901000)\/",
        "BackupSetId":  "fc81ea12-d814-4366-a595-028f8198b2ec",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000011925700001,
        "LastLSN":  17126684000012668100001,
        "CheckpointLSN":  17126684000010800900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500241200000)\/",
        "BackupFinishDate":  "\/Date(1500241200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c13b0050-0834-4dce-8439-92ba251e84f7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500241200000)\/",
        "Start":  "\/Date(1500241200000)\/",
        "BackupSetId":  "c13b0050-0834-4dce-8439-92ba251e84f7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000012668100001,
        "LastLSN":  17126687000000215000001,
        "CheckpointLSN":  17126684000012871700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500241500000)\/",
        "BackupFinishDate":  "\/Date(1500241500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4da030e1-a61f-4e23-b6b1-ff4b21aeb0dc",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500241500000)\/",
        "Start":  "\/Date(1500241500000)\/",
        "BackupSetId":  "4da030e1-a61f-4e23-b6b1-ff4b21aeb0dc",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126687000000215000001,
        "LastLSN":  17126687000000948600001,
        "CheckpointLSN":  17126687000000215100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500241800000)\/",
        "BackupFinishDate":  "\/Date(1500241800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4097a22b-a79c-4978-a5ad-88aec254dcd3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500241800000)\/",
        "Start":  "\/Date(1500241800000)\/",
        "BackupSetId":  "4097a22b-a79c-4978-a5ad-88aec254dcd3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126687000000948600001,
        "LastLSN":  17126687000001681600001,
        "CheckpointLSN":  17126687000000215100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500242100000)\/",
        "BackupFinishDate":  "\/Date(1500242101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9882ac97-c42d-408a-ad17-6534d437e6ab",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500242101000)\/",
        "Start":  "\/Date(1500242100000)\/",
        "BackupSetId":  "9882ac97-c42d-408a-ad17-6534d437e6ab",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126687000001681600001,
        "LastLSN":  17126687000002414600001,
        "CheckpointLSN":  17126687000002280700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500242401000)\/",
        "BackupFinishDate":  "\/Date(1500242401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8b26e506-6641-4719-8952-4f07fc725186",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500242401000)\/",
        "Start":  "\/Date(1500242401000)\/",
        "BackupSetId":  "8b26e506-6641-4719-8952-4f07fc725186",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126687000002414600001,
        "LastLSN":  17126687000003143700001,
        "CheckpointLSN":  17126687000002280700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500242700000)\/",
        "BackupFinishDate":  "\/Date(1500242700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a6e4b7e5-2e96-4ab4-95a5-dd4938b2135b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500242700000)\/",
        "Start":  "\/Date(1500242700000)\/",
        "BackupSetId":  "a6e4b7e5-2e96-4ab4-95a5-dd4938b2135b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126687000003143700001,
        "LastLSN":  17126687000003860400001,
        "CheckpointLSN":  17126687000002280700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500243000000)\/",
        "BackupFinishDate":  "\/Date(1500243000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8c8f942c-53ec-44af-8936-a34b58600ac9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500243000000)\/",
        "Start":  "\/Date(1500243000000)\/",
        "BackupSetId":  "8c8f942c-53ec-44af-8936-a34b58600ac9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126687000003860400001,
        "LastLSN":  17126687000004606600001,
        "CheckpointLSN":  17126687000004347500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500243300000)\/",
        "BackupFinishDate":  "\/Date(1500243300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0afa1fc6-8bbc-4b21-9105-c909a92f6f45",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500243300000)\/",
        "Start":  "\/Date(1500243300000)\/",
        "BackupSetId":  "0afa1fc6-8bbc-4b21-9105-c909a92f6f45",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126687000004606600001,
        "LastLSN":  17126687000005307200001,
        "CheckpointLSN":  17126687000004347500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500243600000)\/",
        "BackupFinishDate":  "\/Date(1500243601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "98df7cf6-818a-4c01-a617-b29e350dc0a0",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500243601000)\/",
        "Start":  "\/Date(1500243600000)\/",
        "BackupSetId":  "98df7cf6-818a-4c01-a617-b29e350dc0a0",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126687000005307200001,
        "LastLSN":  17126687000006035500001,
        "CheckpointLSN":  17126687000004347500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500243900000)\/",
        "BackupFinishDate":  "\/Date(1500243900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "42dc5510-7c3d-4c7c-8cb1-64bdf03824f9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500243900000)\/",
        "Start":  "\/Date(1500243900000)\/",
        "BackupSetId":  "42dc5510-7c3d-4c7c-8cb1-64bdf03824f9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126687000006035500001,
        "LastLSN":  17126687000006768500001,
        "CheckpointLSN":  17126687000006425200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500244200000)\/",
        "BackupFinishDate":  "\/Date(1500244200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1bfffeaf-2e97-4b21-8c37-39272562c5b4",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500244200000)\/",
        "Start":  "\/Date(1500244200000)\/",
        "BackupSetId":  "1bfffeaf-2e97-4b21-8c37-39272562c5b4",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126687000006768500001,
        "LastLSN":  17126687000007495600001,
        "CheckpointLSN":  17126687000006425200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500244500000)\/",
        "BackupFinishDate":  "\/Date(1500244500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c55be641-c225-4ef9-9040-5aba1f016dfc",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500244500000)\/",
        "Start":  "\/Date(1500244500000)\/",
        "BackupSetId":  "c55be641-c225-4ef9-9040-5aba1f016dfc",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126687000007495600001,
        "LastLSN":  17126687000008200500001,
        "CheckpointLSN":  17126687000006425200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500244800000)\/",
        "BackupFinishDate":  "\/Date(1500244800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "01450ac4-d686-4dfb-8082-7dd10c56e2ff",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500244800000)\/",
        "Start":  "\/Date(1500244800000)\/",
        "BackupSetId":  "01450ac4-d686-4dfb-8082-7dd10c56e2ff",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126687000008200500001,
        "LastLSN":  17126687000008934000001,
        "CheckpointLSN":  17126687000008494600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500245100000)\/",
        "BackupFinishDate":  "\/Date(1500245100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "775ea127-677e-4173-91b2-daf49a1c7a43",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500245100000)\/",
        "Start":  "\/Date(1500245100000)\/",
        "BackupSetId":  "775ea127-677e-4173-91b2-daf49a1c7a43",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126687000008934000001,
        "LastLSN":  17126687000009636300001,
        "CheckpointLSN":  17126687000008494600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500245400000)\/",
        "BackupFinishDate":  "\/Date(1500245400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b48182e4-4c5d-47b1-8d3e-d9b82773e78e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500245400000)\/",
        "Start":  "\/Date(1500245400000)\/",
        "BackupSetId":  "b48182e4-4c5d-47b1-8d3e-d9b82773e78e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126687000009636300001,
        "LastLSN":  17126687000010355200001,
        "CheckpointLSN":  17126687000008494600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500245700000)\/",
        "BackupFinishDate":  "\/Date(1500245700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9d20a7cb-6595-41c6-b6c4-bcee1fa43292",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500245700000)\/",
        "Start":  "\/Date(1500245700000)\/",
        "BackupSetId":  "9d20a7cb-6595-41c6-b6c4-bcee1fa43292",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126687000010355200001,
        "LastLSN":  17126687000011101300001,
        "CheckpointLSN":  17126687000010561700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500246000000)\/",
        "BackupFinishDate":  "\/Date(1500246000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2b0fb94e-2865-4c80-a389-30df74273792",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500246000000)\/",
        "Start":  "\/Date(1500246000000)\/",
        "BackupSetId":  "2b0fb94e-2865-4c80-a389-30df74273792",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126687000011101300001,
        "LastLSN":  17126688000005292100001,
        "CheckpointLSN":  17126688000004924900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500246300000)\/",
        "BackupFinishDate":  "\/Date(1500246301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "45300aa9-6702-4506-aaa5-6693cb69326a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500246301000)\/",
        "Start":  "\/Date(1500246300000)\/",
        "BackupSetId":  "45300aa9-6702-4506-aaa5-6693cb69326a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126688000005292100001,
        "LastLSN":  17126688000006006500001,
        "CheckpointLSN":  17126688000004924900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500246600000)\/",
        "BackupFinishDate":  "\/Date(1500246600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4df27c61-e3cf-48b8-bd64-2ad8c8059236",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500246600000)\/",
        "Start":  "\/Date(1500246600000)\/",
        "BackupSetId":  "4df27c61-e3cf-48b8-bd64-2ad8c8059236",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126688000006006500001,
        "LastLSN":  17126688000006708300001,
        "CheckpointLSN":  17126688000004924900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500246900000)\/",
        "BackupFinishDate":  "\/Date(1500246900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "90bdb2af-cae2-4b7c-bae5-45e79017bc40",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500246900000)\/",
        "Start":  "\/Date(1500246900000)\/",
        "BackupSetId":  "90bdb2af-cae2-4b7c-bae5-45e79017bc40",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126688000006708300001,
        "LastLSN":  17126688000007442900001,
        "CheckpointLSN":  17126688000006991000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500247200000)\/",
        "BackupFinishDate":  "\/Date(1500247200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9fd10ed0-d6d0-4369-a12a-815bd60907ed",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500247200000)\/",
        "Start":  "\/Date(1500247200000)\/",
        "BackupSetId":  "9fd10ed0-d6d0-4369-a12a-815bd60907ed",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126688000007442900001,
        "LastLSN":  17126688000008145900001,
        "CheckpointLSN":  17126688000006991000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500247500000)\/",
        "BackupFinishDate":  "\/Date(1500247500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f50b1c44-c467-4661-aed0-c5d6944d5af2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500247500000)\/",
        "Start":  "\/Date(1500247500000)\/",
        "BackupSetId":  "f50b1c44-c467-4661-aed0-c5d6944d5af2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126688000008145900001,
        "LastLSN":  17126688000008846700001,
        "CheckpointLSN":  17126688000006991000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500247800000)\/",
        "BackupFinishDate":  "\/Date(1500247800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b18cca4c-3823-4bb5-8d41-404eb2e8434d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500247800000)\/",
        "Start":  "\/Date(1500247800000)\/",
        "BackupSetId":  "b18cca4c-3823-4bb5-8d41-404eb2e8434d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126688000008846700001,
        "LastLSN":  17126689000008054800001,
        "CheckpointLSN":  17126689000006141400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500248101000)\/",
        "BackupFinishDate":  "\/Date(1500248101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1702b488-96ce-41ab-98ac-c13e1945eb1e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500248101000)\/",
        "Start":  "\/Date(1500248101000)\/",
        "BackupSetId":  "1702b488-96ce-41ab-98ac-c13e1945eb1e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126689000008054800001,
        "LastLSN":  17126690000009720900001,
        "CheckpointLSN":  17126690000008855400003,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500248400000)\/",
        "BackupFinishDate":  "\/Date(1500248400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "bba380d5-114a-4409-ac7d-a3b2b22c7637",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500248400000)\/",
        "Start":  "\/Date(1500248400000)\/",
        "BackupSetId":  "bba380d5-114a-4409-ac7d-a3b2b22c7637",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126690000009720900001,
        "LastLSN":  17126691000011881700001,
        "CheckpointLSN":  17126691000011431000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500248700000)\/",
        "BackupFinishDate":  "\/Date(1500248700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6024ff27-c2ab-4ee5-945d-34b31f8215e3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500248700000)\/",
        "Start":  "\/Date(1500248700000)\/",
        "BackupSetId":  "6024ff27-c2ab-4ee5-945d-34b31f8215e3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126691000011881700001,
        "LastLSN":  17126693000003831600001,
        "CheckpointLSN":  17126693000003759700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500249000000)\/",
        "BackupFinishDate":  "\/Date(1500249001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2f61d707-44e9-4ca0-88e1-0c85a8b2e353",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500249001000)\/",
        "Start":  "\/Date(1500249000000)\/",
        "BackupSetId":  "2f61d707-44e9-4ca0-88e1-0c85a8b2e353",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126693000003831600001,
        "LastLSN":  17126694000011369200001,
        "CheckpointLSN":  17126694000007839400009,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500249300000)\/",
        "BackupFinishDate":  "\/Date(1500249301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c41828ec-4149-456f-81ef-2779ca82ac40",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500249301000)\/",
        "Start":  "\/Date(1500249300000)\/",
        "BackupSetId":  "c41828ec-4149-456f-81ef-2779ca82ac40",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126694000011369200001,
        "LastLSN":  17126696000005429500001,
        "CheckpointLSN":  17126696000002669000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500249601000)\/",
        "BackupFinishDate":  "\/Date(1500249602000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8d2370cb-fccd-47d1-8e84-83c839ce9ada",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500249602000)\/",
        "Start":  "\/Date(1500249601000)\/",
        "BackupSetId":  "8d2370cb-fccd-47d1-8e84-83c839ce9ada",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126696000005429500001,
        "LastLSN":  17126697000002299300001,
        "CheckpointLSN":  17126697000002057900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500249900000)\/",
        "BackupFinishDate":  "\/Date(1500249901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a8ef71fe-e48b-4cb6-8beb-99d4557398a5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500249901000)\/",
        "Start":  "\/Date(1500249900000)\/",
        "BackupSetId":  "a8ef71fe-e48b-4cb6-8beb-99d4557398a5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126697000002299300001,
        "LastLSN":  17126697000003013100001,
        "CheckpointLSN":  17126697000002057900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500250201000)\/",
        "BackupFinishDate":  "\/Date(1500250201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "52e2e7fd-bb18-44ac-bbdf-585131b6b4d9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500250201000)\/",
        "Start":  "\/Date(1500250201000)\/",
        "BackupSetId":  "52e2e7fd-bb18-44ac-bbdf-585131b6b4d9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126697000003013100001,
        "LastLSN":  17126697000003738100001,
        "CheckpointLSN":  17126697000002057900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500250500000)\/",
        "BackupFinishDate":  "\/Date(1500250500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e6285c0a-6d69-4334-b6ff-628647024238",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500250500000)\/",
        "Start":  "\/Date(1500250500000)\/",
        "BackupSetId":  "e6285c0a-6d69-4334-b6ff-628647024238",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126697000003738100001,
        "LastLSN":  17126697000004457100001,
        "CheckpointLSN":  17126697000004175300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500250800000)\/",
        "BackupFinishDate":  "\/Date(1500250800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ace942e3-ee77-4947-8789-935ec066e6b6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500250800000)\/",
        "Start":  "\/Date(1500250800000)\/",
        "BackupSetId":  "ace942e3-ee77-4947-8789-935ec066e6b6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126697000004457100001,
        "LastLSN":  17126697000005185000001,
        "CheckpointLSN":  17126697000004175300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500251101000)\/",
        "BackupFinishDate":  "\/Date(1500251101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "540e22b1-ee98-41b2-8bcd-b3a3a34c6942",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500251101000)\/",
        "Start":  "\/Date(1500251101000)\/",
        "BackupSetId":  "540e22b1-ee98-41b2-8bcd-b3a3a34c6942",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126697000005185000001,
        "LastLSN":  17126697000005902200001,
        "CheckpointLSN":  17126697000004175300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500251401000)\/",
        "BackupFinishDate":  "\/Date(1500251401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5a53147a-01ba-4b64-b7cc-2afdf29640a5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500251401000)\/",
        "Start":  "\/Date(1500251401000)\/",
        "BackupSetId":  "5a53147a-01ba-4b64-b7cc-2afdf29640a5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126697000005902200001,
        "LastLSN":  17126697000006649100001,
        "CheckpointLSN":  17126697000006242100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500251701000)\/",
        "BackupFinishDate":  "\/Date(1500251701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1eaa54f9-9a44-4b10-95e0-4161d6e44226",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500251701000)\/",
        "Start":  "\/Date(1500251701000)\/",
        "BackupSetId":  "1eaa54f9-9a44-4b10-95e0-4161d6e44226",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126697000006649100001,
        "LastLSN":  17126697000007354500001,
        "CheckpointLSN":  17126697000006242100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500252000000)\/",
        "BackupFinishDate":  "\/Date(1500252000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c48cd96d-b82b-468e-a36d-12b58db68013",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500252000000)\/",
        "Start":  "\/Date(1500252000000)\/",
        "BackupSetId":  "c48cd96d-b82b-468e-a36d-12b58db68013",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126697000007354500001,
        "LastLSN":  17126697000008071000001,
        "CheckpointLSN":  17126697000006242100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500252300000)\/",
        "BackupFinishDate":  "\/Date(1500252300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c376c204-ab49-40c6-8ed8-9f54c4e0e295",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500252300000)\/",
        "Start":  "\/Date(1500252300000)\/",
        "BackupSetId":  "c376c204-ab49-40c6-8ed8-9f54c4e0e295",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126697000008071000001,
        "LastLSN":  17126697000008790100001,
        "CheckpointLSN":  17126697000008320700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500252600000)\/",
        "BackupFinishDate":  "\/Date(1500252600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1bf44aba-cdd7-4db5-9729-c5548bcc1a85",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500252600000)\/",
        "Start":  "\/Date(1500252600000)\/",
        "BackupSetId":  "1bf44aba-cdd7-4db5-9729-c5548bcc1a85",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126697000008790100001,
        "LastLSN":  17126697000009509900001,
        "CheckpointLSN":  17126697000008320700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500252900000)\/",
        "BackupFinishDate":  "\/Date(1500252900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "bebcc39f-d3ae-450d-bedd-cd50c6dab78d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500252900000)\/",
        "Start":  "\/Date(1500252900000)\/",
        "BackupSetId":  "bebcc39f-d3ae-450d-bedd-cd50c6dab78d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126697000009509900001,
        "LastLSN":  17126697000010235800001,
        "CheckpointLSN":  17126697000008320700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500253201000)\/",
        "BackupFinishDate":  "\/Date(1500253201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "98df9d17-541d-4238-b466-cb39729264a2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500253201000)\/",
        "Start":  "\/Date(1500253201000)\/",
        "BackupSetId":  "98df9d17-541d-4238-b466-cb39729264a2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126697000010235800001,
        "LastLSN":  17126697000010955300001,
        "CheckpointLSN":  17126697000010397200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500253500000)\/",
        "BackupFinishDate":  "\/Date(1500253500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "57d04681-6fe1-49c2-b1e3-cafb65c46ba2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500253500000)\/",
        "Start":  "\/Date(1500253500000)\/",
        "BackupSetId":  "57d04681-6fe1-49c2-b1e3-cafb65c46ba2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126697000010955300001,
        "LastLSN":  17126697000011669800001,
        "CheckpointLSN":  17126697000010397200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500253800000)\/",
        "BackupFinishDate":  "\/Date(1500253800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f05c0151-d2ec-4a7e-b8ed-cc4cc970916e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500253800000)\/",
        "Start":  "\/Date(1500253800000)\/",
        "BackupSetId":  "f05c0151-d2ec-4a7e-b8ed-cc4cc970916e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126697000011669800001,
        "LastLSN":  17126697000012372000001,
        "CheckpointLSN":  17126697000010397200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500254100000)\/",
        "BackupFinishDate":  "\/Date(1500254100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "95444bca-e7d9-4be6-8b44-5e91988bbda5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500254100000)\/",
        "Start":  "\/Date(1500254100000)\/",
        "BackupSetId":  "95444bca-e7d9-4be6-8b44-5e91988bbda5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126697000012372000001,
        "LastLSN":  17126697000013101600001,
        "CheckpointLSN":  17126697000012533300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500254400000)\/",
        "BackupFinishDate":  "\/Date(1500254400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7585f2e6-33f5-44ba-a9b7-5423ed94bf85",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500254400000)\/",
        "Start":  "\/Date(1500254400000)\/",
        "BackupSetId":  "7585f2e6-33f5-44ba-a9b7-5423ed94bf85",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126697000013101600001,
        "LastLSN":  17126698000000694300001,
        "CheckpointLSN":  17126697000012533300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500254700000)\/",
        "BackupFinishDate":  "\/Date(1500254700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3b68377d-d26a-46c7-a13d-a067a566213e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500254700000)\/",
        "Start":  "\/Date(1500254700000)\/",
        "BackupSetId":  "3b68377d-d26a-46c7-a13d-a067a566213e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000000694300001,
        "LastLSN":  17126698000001414800001,
        "CheckpointLSN":  17126698000000694500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500255000000)\/",
        "BackupFinishDate":  "\/Date(1500255000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "644376d9-beb9-4d2e-a317-fa87eaac7df9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500255000000)\/",
        "Start":  "\/Date(1500255000000)\/",
        "BackupSetId":  "644376d9-beb9-4d2e-a317-fa87eaac7df9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000001414800001,
        "LastLSN":  17126698000002142800001,
        "CheckpointLSN":  17126698000000694500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500255300000)\/",
        "BackupFinishDate":  "\/Date(1500255300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ec9a8d07-1206-4ddd-ba68-aaf4bf64e3ac",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500255300000)\/",
        "Start":  "\/Date(1500255300000)\/",
        "BackupSetId":  "ec9a8d07-1206-4ddd-ba68-aaf4bf64e3ac",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000002142800001,
        "LastLSN":  17126698000002903500001,
        "CheckpointLSN":  17126698000002760800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500255600000)\/",
        "BackupFinishDate":  "\/Date(1500255600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ec7fe888-6685-4baf-b406-7eac4ee96331",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500255600000)\/",
        "Start":  "\/Date(1500255600000)\/",
        "BackupSetId":  "ec7fe888-6685-4baf-b406-7eac4ee96331",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000002903500001,
        "LastLSN":  17126698000003642300001,
        "CheckpointLSN":  17126698000002760800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500255901000)\/",
        "BackupFinishDate":  "\/Date(1500255901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "29b70dc4-d1c3-45e2-b543-7ce2e5f7689f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500255901000)\/",
        "Start":  "\/Date(1500255901000)\/",
        "BackupSetId":  "29b70dc4-d1c3-45e2-b543-7ce2e5f7689f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000003642300001,
        "LastLSN":  17126698000004356200001,
        "CheckpointLSN":  17126698000002760800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500256200000)\/",
        "BackupFinishDate":  "\/Date(1500256200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e8313c1f-f706-40c1-bd28-e813a75bc672",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500256200000)\/",
        "Start":  "\/Date(1500256200000)\/",
        "BackupSetId":  "e8313c1f-f706-40c1-bd28-e813a75bc672",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000004356200001,
        "LastLSN":  17126698000005103600001,
        "CheckpointLSN":  17126698000004827600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500256500000)\/",
        "BackupFinishDate":  "\/Date(1500256500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2e859051-ed18-490f-b05c-de10a0b0708f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500256500000)\/",
        "Start":  "\/Date(1500256500000)\/",
        "BackupSetId":  "2e859051-ed18-490f-b05c-de10a0b0708f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000005103600001,
        "LastLSN":  17126698000005818900001,
        "CheckpointLSN":  17126698000004827600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500256801000)\/",
        "BackupFinishDate":  "\/Date(1500256801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "86b975d0-e902-4789-aed1-9b844caa56b3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500256801000)\/",
        "Start":  "\/Date(1500256801000)\/",
        "BackupSetId":  "86b975d0-e902-4789-aed1-9b844caa56b3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000005818900001,
        "LastLSN":  17126698000006547300001,
        "CheckpointLSN":  17126698000004827600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500257100000)\/",
        "BackupFinishDate":  "\/Date(1500257100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4c79988d-8760-4380-b02d-64819dbaec32",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500257100000)\/",
        "Start":  "\/Date(1500257100000)\/",
        "BackupSetId":  "4c79988d-8760-4380-b02d-64819dbaec32",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000006547300001,
        "LastLSN":  17126698000007280100001,
        "CheckpointLSN":  17126698000006893700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500257400000)\/",
        "BackupFinishDate":  "\/Date(1500257400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "26bd67c9-368b-4725-95f9-313f72c26fe2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500257400000)\/",
        "Start":  "\/Date(1500257400000)\/",
        "BackupSetId":  "26bd67c9-368b-4725-95f9-313f72c26fe2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000007280100001,
        "LastLSN":  17126698000008008200001,
        "CheckpointLSN":  17126698000006893700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500257700000)\/",
        "BackupFinishDate":  "\/Date(1500257700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5859afcb-6871-4f92-9061-b89e47b441c2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500257700000)\/",
        "Start":  "\/Date(1500257700000)\/",
        "BackupSetId":  "5859afcb-6871-4f92-9061-b89e47b441c2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000008008200001,
        "LastLSN":  17126698000008710700001,
        "CheckpointLSN":  17126698000006893700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500258000000)\/",
        "BackupFinishDate":  "\/Date(1500258000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fa663310-8e04-46d1-94da-54c8b5cf22df",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500258000000)\/",
        "Start":  "\/Date(1500258000000)\/",
        "BackupSetId":  "fa663310-8e04-46d1-94da-54c8b5cf22df",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000008710700001,
        "LastLSN":  17126698000009456000001,
        "CheckpointLSN":  17126698000008964900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500258301000)\/",
        "BackupFinishDate":  "\/Date(1500258301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "14b36756-9b23-4c5b-97a9-ee0023266ba5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500258301000)\/",
        "Start":  "\/Date(1500258301000)\/",
        "BackupSetId":  "14b36756-9b23-4c5b-97a9-ee0023266ba5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000009456000001,
        "LastLSN":  17126698000010169000001,
        "CheckpointLSN":  17126698000008964900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500258600000)\/",
        "BackupFinishDate":  "\/Date(1500258600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3e9a99a5-1578-4fe3-9500-cdb90c5f720b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500258600000)\/",
        "Start":  "\/Date(1500258600000)\/",
        "BackupSetId":  "3e9a99a5-1578-4fe3-9500-cdb90c5f720b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000010169000001,
        "LastLSN":  17126698000010893400001,
        "CheckpointLSN":  17126698000008964900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500258900000)\/",
        "BackupFinishDate":  "\/Date(1500258900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a8123b4a-2ab9-4d7d-b663-5d911d3a4fa4",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500258900000)\/",
        "Start":  "\/Date(1500258900000)\/",
        "BackupSetId":  "a8123b4a-2ab9-4d7d-b663-5d911d3a4fa4",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000010893400001,
        "LastLSN":  17126698000011613500001,
        "CheckpointLSN":  17126698000011051600007,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500259200000)\/",
        "BackupFinishDate":  "\/Date(1500259200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "896d800a-6d26-46b2-af7b-9e9972e9e134",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500259200000)\/",
        "Start":  "\/Date(1500259200000)\/",
        "BackupSetId":  "896d800a-6d26-46b2-af7b-9e9972e9e134",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000011613500001,
        "LastLSN":  17126698000012326300001,
        "CheckpointLSN":  17126698000011051600007,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500259500000)\/",
        "BackupFinishDate":  "\/Date(1500259500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "cca604c4-d944-448f-8cce-c0790aea94cf",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500259500000)\/",
        "Start":  "\/Date(1500259500000)\/",
        "BackupSetId":  "cca604c4-d944-448f-8cce-c0790aea94cf",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000012326300001,
        "LastLSN":  17126698000013027500001,
        "CheckpointLSN":  17126698000011051600007,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500259800000)\/",
        "BackupFinishDate":  "\/Date(1500259800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "17b023dc-1690-4bcc-aedf-933d274f93a6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500259800000)\/",
        "Start":  "\/Date(1500259800000)\/",
        "BackupSetId":  "17b023dc-1690-4bcc-aedf-933d274f93a6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000013027500001,
        "LastLSN":  17126699000000658200001,
        "CheckpointLSN":  17126699000000023100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500260100000)\/",
        "BackupFinishDate":  "\/Date(1500260100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a7d5d2d8-6e2c-48c2-abd6-469291793c00",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500260100000)\/",
        "Start":  "\/Date(1500260100000)\/",
        "BackupSetId":  "a7d5d2d8-6e2c-48c2-abd6-469291793c00",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000000658200001,
        "LastLSN":  17126699000001384300001,
        "CheckpointLSN":  17126699000000023100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500260400000)\/",
        "BackupFinishDate":  "\/Date(1500260400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "50e3f599-e347-4dfe-bdec-ef8d54788d47",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500260400000)\/",
        "Start":  "\/Date(1500260400000)\/",
        "BackupSetId":  "50e3f599-e347-4dfe-bdec-ef8d54788d47",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000001384300001,
        "LastLSN":  17126699000002085900001,
        "CheckpointLSN":  17126699000000023100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500260700000)\/",
        "BackupFinishDate":  "\/Date(1500260700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0ead9533-e15f-447e-b26b-914c6b8e0101",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500260700000)\/",
        "Start":  "\/Date(1500260700000)\/",
        "BackupSetId":  "0ead9533-e15f-447e-b26b-914c6b8e0101",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000002085900001,
        "LastLSN":  17126699000002819700001,
        "CheckpointLSN":  17126699000002091000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500261001000)\/",
        "BackupFinishDate":  "\/Date(1500261001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f4e6116b-1bf3-491c-acfc-87b72ca312ce",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500261001000)\/",
        "Start":  "\/Date(1500261001000)\/",
        "BackupSetId":  "f4e6116b-1bf3-491c-acfc-87b72ca312ce",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000002819700001,
        "LastLSN":  17126699000003519300001,
        "CheckpointLSN":  17126699000002091000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500261300000)\/",
        "BackupFinishDate":  "\/Date(1500261300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "dc8915ff-1c84-4643-b2c8-56bad47f93a9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500261300000)\/",
        "Start":  "\/Date(1500261300000)\/",
        "BackupSetId":  "dc8915ff-1c84-4643-b2c8-56bad47f93a9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000003519300001,
        "LastLSN":  17126699000004255300001,
        "CheckpointLSN":  17126699000004158600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500261600000)\/",
        "BackupFinishDate":  "\/Date(1500261600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b2013af9-1a0b-4540-bb17-316ebf7b11f8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500261600000)\/",
        "Start":  "\/Date(1500261600000)\/",
        "BackupSetId":  "b2013af9-1a0b-4540-bb17-316ebf7b11f8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000004255300001,
        "LastLSN":  17126699000004956300001,
        "CheckpointLSN":  17126699000004158600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500261900000)\/",
        "BackupFinishDate":  "\/Date(1500261900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4c71862a-f460-4a25-8819-42413b87f977",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500261900000)\/",
        "Start":  "\/Date(1500261900000)\/",
        "BackupSetId":  "4c71862a-f460-4a25-8819-42413b87f977",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000004956300001,
        "LastLSN":  17126699000005657700001,
        "CheckpointLSN":  17126699000004158600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500262201000)\/",
        "BackupFinishDate":  "\/Date(1500262201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1a51d11d-3273-4a1c-ae84-936c6634115e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500262201000)\/",
        "Start":  "\/Date(1500262201000)\/",
        "BackupSetId":  "1a51d11d-3273-4a1c-ae84-936c6634115e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000005657700001,
        "LastLSN":  17126699000006404600001,
        "CheckpointLSN":  17126699000006228500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500262500000)\/",
        "BackupFinishDate":  "\/Date(1500262500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f5da5162-7ba9-4a21-9a9d-154d30cb4fb8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500262500000)\/",
        "Start":  "\/Date(1500262500000)\/",
        "BackupSetId":  "f5da5162-7ba9-4a21-9a9d-154d30cb4fb8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000006404600001,
        "LastLSN":  17126699000007144200001,
        "CheckpointLSN":  17126699000006228500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500262800000)\/",
        "BackupFinishDate":  "\/Date(1500262800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d102c256-87b9-4209-b269-db46e0a740c6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500262800000)\/",
        "Start":  "\/Date(1500262800000)\/",
        "BackupSetId":  "d102c256-87b9-4209-b269-db46e0a740c6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000007144200001,
        "LastLSN":  17126699000007884800001,
        "CheckpointLSN":  17126699000006228500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500263100000)\/",
        "BackupFinishDate":  "\/Date(1500263100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "60b736e2-ce1c-4bb2-a828-680e808a2ffd",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500263100000)\/",
        "Start":  "\/Date(1500263100000)\/",
        "BackupSetId":  "60b736e2-ce1c-4bb2-a828-680e808a2ffd",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000007884800001,
        "LastLSN":  17126699000008619000001,
        "CheckpointLSN":  17126699000008298300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500263400000)\/",
        "BackupFinishDate":  "\/Date(1500263401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a19bfc9a-a3fc-4df0-9458-99ede57dcff0",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500263401000)\/",
        "Start":  "\/Date(1500263400000)\/",
        "BackupSetId":  "a19bfc9a-a3fc-4df0-9458-99ede57dcff0",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000008619000001,
        "LastLSN":  17126699000009350600001,
        "CheckpointLSN":  17126699000008298300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500263701000)\/",
        "BackupFinishDate":  "\/Date(1500263701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "644a2215-9789-4284-be6d-ba7fb90a03f6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500263701000)\/",
        "Start":  "\/Date(1500263701000)\/",
        "BackupSetId":  "644a2215-9789-4284-be6d-ba7fb90a03f6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000009350600001,
        "LastLSN":  17126699000010065600001,
        "CheckpointLSN":  17126699000008298300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500264000000)\/",
        "BackupFinishDate":  "\/Date(1500264000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d4c529b6-48da-40ae-bee0-6cd65d55f048",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500264000000)\/",
        "Start":  "\/Date(1500264000000)\/",
        "BackupSetId":  "d4c529b6-48da-40ae-bee0-6cd65d55f048",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000010065600001,
        "LastLSN":  17126699000010811000001,
        "CheckpointLSN":  17126699000010368800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500264300000)\/",
        "BackupFinishDate":  "\/Date(1500264300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "347ac8b2-809a-41c1-952c-46ee666630da",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500264300000)\/",
        "Start":  "\/Date(1500264300000)\/",
        "BackupSetId":  "347ac8b2-809a-41c1-952c-46ee666630da",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000010811000001,
        "LastLSN":  17126699000011527200001,
        "CheckpointLSN":  17126699000010368800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500264600000)\/",
        "BackupFinishDate":  "\/Date(1500264600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "74d8d621-9588-4fb0-a029-2b520504a1a1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500264600000)\/",
        "Start":  "\/Date(1500264600000)\/",
        "BackupSetId":  "74d8d621-9588-4fb0-a029-2b520504a1a1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000011527200001,
        "LastLSN":  17126699000012256200001,
        "CheckpointLSN":  17126699000010368800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500264900000)\/",
        "BackupFinishDate":  "\/Date(1500264900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f535f808-be3e-4d2e-9cab-7ecfb31d0331",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500264900000)\/",
        "Start":  "\/Date(1500264900000)\/",
        "BackupSetId":  "f535f808-be3e-4d2e-9cab-7ecfb31d0331",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000012256200001,
        "LastLSN":  17126699000012976500001,
        "CheckpointLSN":  17126699000012459400059,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500265200000)\/",
        "BackupFinishDate":  "\/Date(1500265201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "536da3db-8998-488d-a8c6-a2119e95ce6f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500265201000)\/",
        "Start":  "\/Date(1500265200000)\/",
        "BackupSetId":  "536da3db-8998-488d-a8c6-a2119e95ce6f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000012976500001,
        "LastLSN":  17126700000000599500001,
        "CheckpointLSN":  17126699000012459400059,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500265500000)\/",
        "BackupFinishDate":  "\/Date(1500265500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "cef03a15-6726-4d42-a857-85f98ecf4780",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500265500000)\/",
        "Start":  "\/Date(1500265500000)\/",
        "BackupSetId":  "cef03a15-6726-4d42-a857-85f98ecf4780",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126700000000599500001,
        "LastLSN":  17126700000001331400001,
        "CheckpointLSN":  17126700000000599600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500265800000)\/",
        "BackupFinishDate":  "\/Date(1500265800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "bf8d020a-ecf6-4b70-a9cc-4deddaf8d664",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500265800000)\/",
        "Start":  "\/Date(1500265800000)\/",
        "BackupSetId":  "bf8d020a-ecf6-4b70-a9cc-4deddaf8d664",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126700000001331400001,
        "LastLSN":  17126700000002058800001,
        "CheckpointLSN":  17126700000000599600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500266100000)\/",
        "BackupFinishDate":  "\/Date(1500266100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ad79dd5c-92ac-46be-8e2a-2988ac71b7c4",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500266100000)\/",
        "Start":  "\/Date(1500266100000)\/",
        "BackupSetId":  "ad79dd5c-92ac-46be-8e2a-2988ac71b7c4",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126700000002058800001,
        "LastLSN":  17126700000002782600001,
        "CheckpointLSN":  17126700000002665300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500266400000)\/",
        "BackupFinishDate":  "\/Date(1500266400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "27449349-1290-4edc-a940-dc5645c5e2b9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500266400000)\/",
        "Start":  "\/Date(1500266400000)\/",
        "BackupSetId":  "27449349-1290-4edc-a940-dc5645c5e2b9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126700000002782600001,
        "LastLSN":  17126700000003495600001,
        "CheckpointLSN":  17126700000002665300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500266701000)\/",
        "BackupFinishDate":  "\/Date(1500266701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a094ad2e-fca0-406a-9709-1d0d35bd9219",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500266701000)\/",
        "Start":  "\/Date(1500266701000)\/",
        "BackupSetId":  "a094ad2e-fca0-406a-9709-1d0d35bd9219",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126700000003495600001,
        "LastLSN":  17126700000004196400001,
        "CheckpointLSN":  17126700000002665300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500267000000)\/",
        "BackupFinishDate":  "\/Date(1500267000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "459bfb0e-61a3-4839-8e0b-eb2ce9611446",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500267000000)\/",
        "Start":  "\/Date(1500267000000)\/",
        "BackupSetId":  "459bfb0e-61a3-4839-8e0b-eb2ce9611446",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126700000004196400001,
        "LastLSN":  17126700000004931800001,
        "CheckpointLSN":  17126700000004737400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500267300000)\/",
        "BackupFinishDate":  "\/Date(1500267300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "034aa99b-fd50-46ed-a691-b681fc1b649a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500267300000)\/",
        "Start":  "\/Date(1500267300000)\/",
        "BackupSetId":  "034aa99b-fd50-46ed-a691-b681fc1b649a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126700000004931800001,
        "LastLSN":  17126700000005664300001,
        "CheckpointLSN":  17126700000004737400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500267601000)\/",
        "BackupFinishDate":  "\/Date(1500267601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "05ac3cfe-9264-4955-928c-97a7a24038f8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500267601000)\/",
        "Start":  "\/Date(1500267601000)\/",
        "BackupSetId":  "05ac3cfe-9264-4955-928c-97a7a24038f8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126700000005664300001,
        "LastLSN":  17126700000009094200001,
        "CheckpointLSN":  17126700000008897500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500267900000)\/",
        "BackupFinishDate":  "\/Date(1500267901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "577ab783-a88a-4e73-a21f-b64d76815d99",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500267901000)\/",
        "Start":  "\/Date(1500267900000)\/",
        "BackupSetId":  "577ab783-a88a-4e73-a21f-b64d76815d99",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126700000009094200001,
        "LastLSN":  17126700000009807000001,
        "CheckpointLSN":  17126700000008897500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500268200000)\/",
        "BackupFinishDate":  "\/Date(1500268200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b963fedc-b8f7-49f6-b7e1-1b8c4ca1a8d6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500268200000)\/",
        "Start":  "\/Date(1500268200000)\/",
        "BackupSetId":  "b963fedc-b8f7-49f6-b7e1-1b8c4ca1a8d6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126700000009807000001,
        "LastLSN":  17126700000010508900001,
        "CheckpointLSN":  17126700000008897500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500268500000)\/",
        "BackupFinishDate":  "\/Date(1500268500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3e812b2c-9a6d-4dfe-8e41-843b32a9ab3b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500268500000)\/",
        "Start":  "\/Date(1500268500000)\/",
        "BackupSetId":  "3e812b2c-9a6d-4dfe-8e41-843b32a9ab3b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126700000010508900001,
        "LastLSN":  17126700000011241400001,
        "CheckpointLSN":  17126700000010971200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500268800000)\/",
        "BackupFinishDate":  "\/Date(1500268800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "14872544-d36d-4209-99e3-f5c4249fe12b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500268800000)\/",
        "Start":  "\/Date(1500268800000)\/",
        "BackupSetId":  "14872544-d36d-4209-99e3-f5c4249fe12b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126700000011241400001,
        "LastLSN":  17126700000011949300001,
        "CheckpointLSN":  17126700000010971200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500269100000)\/",
        "BackupFinishDate":  "\/Date(1500269100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c5cab591-278b-48c8-9a1c-c6b1d579a5dc",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500269100000)\/",
        "Start":  "\/Date(1500269100000)\/",
        "BackupSetId":  "c5cab591-278b-48c8-9a1c-c6b1d579a5dc",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126700000011949300001,
        "LastLSN":  17126701000000226500001,
        "CheckpointLSN":  17126700000013038300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500269401000)\/",
        "BackupFinishDate":  "\/Date(1500269401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "563e8f15-3feb-42a2-871b-7aa0545d8959",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500269401000)\/",
        "Start":  "\/Date(1500269401000)\/",
        "BackupSetId":  "563e8f15-3feb-42a2-871b-7aa0545d8959",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126701000000226500001,
        "LastLSN":  17126701000010334700001,
        "CheckpointLSN":  17126701000008264300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500269700000)\/",
        "BackupFinishDate":  "\/Date(1500269700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8923699b-2451-4c87-a9ac-ab2a53bbb41d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500269700000)\/",
        "Start":  "\/Date(1500269700000)\/",
        "BackupSetId":  "8923699b-2451-4c87-a9ac-ab2a53bbb41d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126701000010334700001,
        "LastLSN":  17126702000006262100001,
        "CheckpointLSN":  17126702000005302000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500270000000)\/",
        "BackupFinishDate":  "\/Date(1500270000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2de72270-b7b5-4a01-95fb-139dc3e88856",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500270000000)\/",
        "Start":  "\/Date(1500270000000)\/",
        "BackupSetId":  "2de72270-b7b5-4a01-95fb-139dc3e88856",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126702000006262100001,
        "LastLSN":  17126702000010625200001,
        "CheckpointLSN":  17126702000009475700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500270300000)\/",
        "BackupFinishDate":  "\/Date(1500270300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "425191a0-8a43-429d-a31a-e9f64a1a6413",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500270300000)\/",
        "Start":  "\/Date(1500270300000)\/",
        "BackupSetId":  "425191a0-8a43-429d-a31a-e9f64a1a6413",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126702000010625200001,
        "LastLSN":  17126702000011592600001,
        "CheckpointLSN":  17126702000011542200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500270600000)\/",
        "BackupFinishDate":  "\/Date(1500270600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "469d59fb-41cb-4d59-9af4-dac146f9ab01",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500270600000)\/",
        "Start":  "\/Date(1500270600000)\/",
        "BackupSetId":  "469d59fb-41cb-4d59-9af4-dac146f9ab01",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126702000011592600001,
        "LastLSN":  17126702000012321300001,
        "CheckpointLSN":  17126702000011542200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500270900000)\/",
        "BackupFinishDate":  "\/Date(1500270900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "84d0791d-8988-4f3f-941a-47b6edfb33d0",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500270900000)\/",
        "Start":  "\/Date(1500270900000)\/",
        "BackupSetId":  "84d0791d-8988-4f3f-941a-47b6edfb33d0",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126702000012321300001,
        "LastLSN":  17126705000000438400001,
        "CheckpointLSN":  17126705000000398200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500271200000)\/",
        "BackupFinishDate":  "\/Date(1500271200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6bb8dd74-63aa-4be9-b404-5a635e13f6da",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500271200000)\/",
        "Start":  "\/Date(1500271200000)\/",
        "BackupSetId":  "6bb8dd74-63aa-4be9-b404-5a635e13f6da",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126705000000438400001,
        "LastLSN":  17126705000010433900001,
        "CheckpointLSN":  17126705000008624400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500271500000)\/",
        "BackupFinishDate":  "\/Date(1500271501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ee457ba4-a49e-4ea2-b4b3-accff7ee144e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500271501000)\/",
        "Start":  "\/Date(1500271500000)\/",
        "BackupSetId":  "ee457ba4-a49e-4ea2-b4b3-accff7ee144e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126705000010433900001,
        "LastLSN":  17126706000004172600001,
        "CheckpointLSN":  17126706000001755500033,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500271800000)\/",
        "BackupFinishDate":  "\/Date(1500271800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "147919d1-01fb-49a1-af04-0b64951f1360",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500271800000)\/",
        "Start":  "\/Date(1500271800000)\/",
        "BackupSetId":  "147919d1-01fb-49a1-af04-0b64951f1360",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126706000004172600001,
        "LastLSN":  17126706000005136700001,
        "CheckpointLSN":  17126706000001755500033,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500272100000)\/",
        "BackupFinishDate":  "\/Date(1500272100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ba30775e-50fc-408b-a57d-cafe734becb7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500272100000)\/",
        "Start":  "\/Date(1500272100000)\/",
        "BackupSetId":  "ba30775e-50fc-408b-a57d-cafe734becb7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126706000005136700001,
        "LastLSN":  17126706000006063400001,
        "CheckpointLSN":  17126706000005413900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500272400000)\/",
        "BackupFinishDate":  "\/Date(1500272400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "76afd155-de94-4a2c-8a4a-9654682cd70e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500272400000)\/",
        "Start":  "\/Date(1500272400000)\/",
        "BackupSetId":  "76afd155-de94-4a2c-8a4a-9654682cd70e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126706000006063400001,
        "LastLSN":  17126706000006866800001,
        "CheckpointLSN":  17126706000005413900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500272700000)\/",
        "BackupFinishDate":  "\/Date(1500272700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8ed83789-d65c-408f-a17f-ef2d09732570",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500272700000)\/",
        "Start":  "\/Date(1500272700000)\/",
        "BackupSetId":  "8ed83789-d65c-408f-a17f-ef2d09732570",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126706000006866800001,
        "LastLSN":  17126706000007651500001,
        "CheckpointLSN":  17126706000007485400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500273001000)\/",
        "BackupFinishDate":  "\/Date(1500273001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "25ac1c84-7c3d-4ef6-ab63-53025fc35435",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500273001000)\/",
        "Start":  "\/Date(1500273001000)\/",
        "BackupSetId":  "25ac1c84-7c3d-4ef6-ab63-53025fc35435",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126706000007651500001,
        "LastLSN":  17126706000008425100001,
        "CheckpointLSN":  17126706000007485400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500273300000)\/",
        "BackupFinishDate":  "\/Date(1500273300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "bacf6c8e-68d0-4224-95b7-6b1bc2156349",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500273300000)\/",
        "Start":  "\/Date(1500273300000)\/",
        "BackupSetId":  "bacf6c8e-68d0-4224-95b7-6b1bc2156349",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126706000008425100001,
        "LastLSN":  17126706000009762500001,
        "CheckpointLSN":  17126706000009568900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500273600000)\/",
        "BackupFinishDate":  "\/Date(1500273600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4ba1caa7-0a76-41fd-94b9-d31638ffa130",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500273600000)\/",
        "Start":  "\/Date(1500273600000)\/",
        "BackupSetId":  "4ba1caa7-0a76-41fd-94b9-d31638ffa130",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126706000009762500001,
        "LastLSN":  17126706000010929100001,
        "CheckpointLSN":  17126706000009568900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500273900000)\/",
        "BackupFinishDate":  "\/Date(1500273900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1d95aae5-cbf5-4146-8239-e17061ea04f3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500273900000)\/",
        "Start":  "\/Date(1500273900000)\/",
        "BackupSetId":  "1d95aae5-cbf5-4146-8239-e17061ea04f3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126706000010929100001,
        "LastLSN":  17126707000003905600001,
        "CheckpointLSN":  17126707000000637600008,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500274200000)\/",
        "BackupFinishDate":  "\/Date(1500274201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "90620cc6-76d1-4795-a0f3-788157b3054c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500274201000)\/",
        "Start":  "\/Date(1500274200000)\/",
        "BackupSetId":  "90620cc6-76d1-4795-a0f3-788157b3054c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126707000003905600001,
        "LastLSN":  17126707000012950800001,
        "CheckpointLSN":  17126707000012508600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500274501000)\/",
        "BackupFinishDate":  "\/Date(1500274501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "cda21b51-5aca-46bf-a1d3-c567e06612d1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500274501000)\/",
        "Start":  "\/Date(1500274501000)\/",
        "BackupSetId":  "cda21b51-5aca-46bf-a1d3-c567e06612d1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126707000012950800001,
        "LastLSN":  17126708000004222700001,
        "CheckpointLSN":  17126708000003616100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500274800000)\/",
        "BackupFinishDate":  "\/Date(1500274800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "eb550d9c-d132-4c61-944e-2ce6db5de8da",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500274800000)\/",
        "Start":  "\/Date(1500274800000)\/",
        "BackupSetId":  "eb550d9c-d132-4c61-944e-2ce6db5de8da",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126708000004222700001,
        "LastLSN":  17126708000005608800001,
        "CheckpointLSN":  17126708000003616100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500275100000)\/",
        "BackupFinishDate":  "\/Date(1500275100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "206c6805-0847-4c13-bdd6-6cd0f190dc55",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500275100000)\/",
        "Start":  "\/Date(1500275100000)\/",
        "BackupSetId":  "206c6805-0847-4c13-bdd6-6cd0f190dc55",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126708000005608800001,
        "LastLSN":  17126708000008208300001,
        "CheckpointLSN":  17126708000007799700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500275400000)\/",
        "BackupFinishDate":  "\/Date(1500275401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ab2be580-42c9-4113-98e6-ca02ff0b1a7b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500275401000)\/",
        "Start":  "\/Date(1500275400000)\/",
        "BackupSetId":  "ab2be580-42c9-4113-98e6-ca02ff0b1a7b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126708000008208300001,
        "LastLSN":  17126708000011340700001,
        "CheckpointLSN":  17126708000009892100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500275701000)\/",
        "BackupFinishDate":  "\/Date(1500275701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "da15e5d1-7bf1-48d8-a1f5-8d1cb3cb1557",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500275701000)\/",
        "Start":  "\/Date(1500275701000)\/",
        "BackupSetId":  "da15e5d1-7bf1-48d8-a1f5-8d1cb3cb1557",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126708000011340700001,
        "LastLSN":  17126709000001895500001,
        "CheckpointLSN":  17126709000000941300014,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500276000000)\/",
        "BackupFinishDate":  "\/Date(1500276000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c8d69fd4-c913-4e6b-ac80-e9a0073bc07d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500276000000)\/",
        "Start":  "\/Date(1500276000000)\/",
        "BackupSetId":  "c8d69fd4-c913-4e6b-ac80-e9a0073bc07d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126709000001895500001,
        "LastLSN":  17126710000002368400001,
        "CheckpointLSN":  17126710000002262900009,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500276300000)\/",
        "BackupFinishDate":  "\/Date(1500276300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4e23ac24-fada-4e59-b423-0c53ddad9520",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500276300000)\/",
        "Start":  "\/Date(1500276300000)\/",
        "BackupSetId":  "4e23ac24-fada-4e59-b423-0c53ddad9520",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126710000002368400001,
        "LastLSN":  17126712000008541300001,
        "CheckpointLSN":  17126712000004575500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500276600000)\/",
        "BackupFinishDate":  "\/Date(1500276602000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6badf75e-ef48-4da6-a1eb-5fd9a80de48b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500276602000)\/",
        "Start":  "\/Date(1500276600000)\/",
        "BackupSetId":  "6badf75e-ef48-4da6-a1eb-5fd9a80de48b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126712000008541300001,
        "LastLSN":  17126713000008587000001,
        "CheckpointLSN":  17126713000008019700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500276900000)\/",
        "BackupFinishDate":  "\/Date(1500276901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "83c197ad-04a8-4152-a510-5dea58bc2a88",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500276901000)\/",
        "Start":  "\/Date(1500276900000)\/",
        "BackupSetId":  "83c197ad-04a8-4152-a510-5dea58bc2a88",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126713000008587000001,
        "LastLSN":  17126713000009533600001,
        "CheckpointLSN":  17126713000008019700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500277200000)\/",
        "BackupFinishDate":  "\/Date(1500277200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ba7e9011-1ad4-43b6-9a3a-05a873df0c42",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500277200000)\/",
        "Start":  "\/Date(1500277200000)\/",
        "BackupSetId":  "ba7e9011-1ad4-43b6-9a3a-05a873df0c42",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126713000009533600001,
        "LastLSN":  17126716000002041800001,
        "CheckpointLSN":  17126716000001614700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500277500000)\/",
        "BackupFinishDate":  "\/Date(1500277501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d00d0ed4-2f25-40b0-b054-1d7ab821215a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500277501000)\/",
        "Start":  "\/Date(1500277500000)\/",
        "BackupSetId":  "d00d0ed4-2f25-40b0-b054-1d7ab821215a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126716000002041800001,
        "LastLSN":  17126718000002541400001,
        "CheckpointLSN":  17126718000002509700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500277800000)\/",
        "BackupFinishDate":  "\/Date(1500277801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3ef0378d-26ce-4adc-9da5-ab9a341baaf8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500277801000)\/",
        "Start":  "\/Date(1500277800000)\/",
        "BackupSetId":  "3ef0378d-26ce-4adc-9da5-ab9a341baaf8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126718000002541400001,
        "LastLSN":  17126718000009557300001,
        "CheckpointLSN":  17126718000007631900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500278100000)\/",
        "BackupFinishDate":  "\/Date(1500278101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "cb262a50-b3c5-404e-97c3-a3497cc1ff12",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500278101000)\/",
        "Start":  "\/Date(1500278100000)\/",
        "BackupSetId":  "cb262a50-b3c5-404e-97c3-a3497cc1ff12",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126718000009557300001,
        "LastLSN":  17126719000007606800001,
        "CheckpointLSN":  17126719000007379900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500278401000)\/",
        "BackupFinishDate":  "\/Date(1500278401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "40035edd-7f75-44d0-a1f7-e2a76476029e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500278401000)\/",
        "Start":  "\/Date(1500278401000)\/",
        "BackupSetId":  "40035edd-7f75-44d0-a1f7-e2a76476029e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126719000007606800001,
        "LastLSN":  17126720000000489600001,
        "CheckpointLSN":  17126719000011629900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500278700000)\/",
        "BackupFinishDate":  "\/Date(1500278700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3bc8f9d5-9574-4cc3-851b-623cc3b60f8a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500278700000)\/",
        "Start":  "\/Date(1500278700000)\/",
        "BackupSetId":  "3bc8f9d5-9574-4cc3-851b-623cc3b60f8a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126720000000489600001,
        "LastLSN":  17126723000005599500001,
        "CheckpointLSN":  17126723000005521400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500279000000)\/",
        "BackupFinishDate":  "\/Date(1500279001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "01b0bfa4-6f2f-43b7-9eeb-65f346b02dc8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500279001000)\/",
        "Start":  "\/Date(1500279000000)\/",
        "BackupSetId":  "01b0bfa4-6f2f-43b7-9eeb-65f346b02dc8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126723000005599500001,
        "LastLSN":  17126725000007625400001,
        "CheckpointLSN":  17126725000007320300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500279300000)\/",
        "BackupFinishDate":  "\/Date(1500279301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1e4249c0-a609-42df-bac1-26f83b85b92e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500279301000)\/",
        "Start":  "\/Date(1500279300000)\/",
        "BackupSetId":  "1e4249c0-a609-42df-bac1-26f83b85b92e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126725000007625400001,
        "LastLSN":  17126726000000470700001,
        "CheckpointLSN":  17126726000000274200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500279600000)\/",
        "BackupFinishDate":  "\/Date(1500279601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "24e79a8f-64e8-4093-8d59-63631a853176",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500279601000)\/",
        "Start":  "\/Date(1500279600000)\/",
        "BackupSetId":  "24e79a8f-64e8-4093-8d59-63631a853176",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126726000000470700001,
        "LastLSN":  17126727000007729200001,
        "CheckpointLSN":  17126727000004068000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500279901000)\/",
        "BackupFinishDate":  "\/Date(1500279902000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "419ec4fc-becf-416d-96ad-b1d3647b7e69",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500279902000)\/",
        "Start":  "\/Date(1500279901000)\/",
        "BackupSetId":  "419ec4fc-becf-416d-96ad-b1d3647b7e69",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126727000007729200001,
        "LastLSN":  17126730000002553300001,
        "CheckpointLSN":  17126730000002380700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500280200000)\/",
        "BackupFinishDate":  "\/Date(1500280202000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "66f73880-849a-4ed0-b48a-ef6d7b9ae5f1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500280202000)\/",
        "Start":  "\/Date(1500280200000)\/",
        "BackupSetId":  "66f73880-849a-4ed0-b48a-ef6d7b9ae5f1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126730000002553300001,
        "LastLSN":  17126733000011493000001,
        "CheckpointLSN":  17126733000011432400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500280500000)\/",
        "BackupFinishDate":  "\/Date(1500280502000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4f3c893f-7b13-4973-abbd-8cf6a68f02bc",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500280502000)\/",
        "Start":  "\/Date(1500280500000)\/",
        "BackupSetId":  "4f3c893f-7b13-4973-abbd-8cf6a68f02bc",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126733000011493000001,
        "LastLSN":  17126735000001923200001,
        "CheckpointLSN":  17126734000005021300030,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500280800000)\/",
        "BackupFinishDate":  "\/Date(1500280801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4562d610-ac10-4853-b49d-92b63f4128c9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500280801000)\/",
        "Start":  "\/Date(1500280800000)\/",
        "BackupSetId":  "4562d610-ac10-4853-b49d-92b63f4128c9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126735000001923200001,
        "LastLSN":  17126736000012961600001,
        "CheckpointLSN":  17126736000012791100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500281100000)\/",
        "BackupFinishDate":  "\/Date(1500281102000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a8190ac3-86b7-45e2-8764-8d6f7d494568",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500281102000)\/",
        "Start":  "\/Date(1500281100000)\/",
        "BackupSetId":  "a8190ac3-86b7-45e2-8764-8d6f7d494568",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126736000012961600001,
        "LastLSN":  17126741000003983800001,
        "CheckpointLSN":  17126741000002378000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500281400000)\/",
        "BackupFinishDate":  "\/Date(1500281401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "50d07a3b-32a1-4c89-bf51-8b9f33ba0039",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500281401000)\/",
        "Start":  "\/Date(1500281400000)\/",
        "BackupSetId":  "50d07a3b-32a1-4c89-bf51-8b9f33ba0039",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126741000003983800001,
        "LastLSN":  17126741000012053600001,
        "CheckpointLSN":  17126741000010858400010,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500281700000)\/",
        "BackupFinishDate":  "\/Date(1500281700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7232f045-b04d-4f3c-ac59-afdace0c50c5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500281700000)\/",
        "Start":  "\/Date(1500281700000)\/",
        "BackupSetId":  "7232f045-b04d-4f3c-ac59-afdace0c50c5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126741000012053600001,
        "LastLSN":  17126743000003341600001,
        "CheckpointLSN":  17126743000001957500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500282000000)\/",
        "BackupFinishDate":  "\/Date(1500282001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "22c1c5b4-6791-4e88-99f1-5b74a4e5a23e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500282001000)\/",
        "Start":  "\/Date(1500282000000)\/",
        "BackupSetId":  "22c1c5b4-6791-4e88-99f1-5b74a4e5a23e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126743000003341600001,
        "LastLSN":  17126743000010911300001,
        "CheckpointLSN":  17126743000010805400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500282300000)\/",
        "BackupFinishDate":  "\/Date(1500282301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f56fbee4-0670-4ce1-98e8-bdfd079ada72",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500282301000)\/",
        "Start":  "\/Date(1500282300000)\/",
        "BackupSetId":  "f56fbee4-0670-4ce1-98e8-bdfd079ada72",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126743000010911300001,
        "LastLSN":  17126744000003412000001,
        "CheckpointLSN":  17126744000002218700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500282600000)\/",
        "BackupFinishDate":  "\/Date(1500282601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c6d617df-7d3f-4f6a-abdc-ca31ae579004",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500282601000)\/",
        "Start":  "\/Date(1500282600000)\/",
        "BackupSetId":  "c6d617df-7d3f-4f6a-abdc-ca31ae579004",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126744000003412000001,
        "LastLSN":  17126744000007380700001,
        "CheckpointLSN":  17126744000006421800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500282901000)\/",
        "BackupFinishDate":  "\/Date(1500282901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6b9e3a06-edb6-4242-a2cb-3b3eef90c064",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500282901000)\/",
        "Start":  "\/Date(1500282901000)\/",
        "BackupSetId":  "6b9e3a06-edb6-4242-a2cb-3b3eef90c064",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126744000007380700001,
        "LastLSN":  17126745000003806200001,
        "CheckpointLSN":  17126745000002160200017,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500283200000)\/",
        "BackupFinishDate":  "\/Date(1500283201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "dade97f4-5291-452d-bf4a-20b1645e1706",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500283201000)\/",
        "Start":  "\/Date(1500283200000)\/",
        "BackupSetId":  "dade97f4-5291-452d-bf4a-20b1645e1706",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126745000003806200001,
        "LastLSN":  17126745000010781900001,
        "CheckpointLSN":  17126745000009743200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500283500000)\/",
        "BackupFinishDate":  "\/Date(1500283500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d22a4b51-21b5-4171-b8bf-2b1ea837abd5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500283500000)\/",
        "Start":  "\/Date(1500283500000)\/",
        "BackupSetId":  "d22a4b51-21b5-4171-b8bf-2b1ea837abd5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126745000010781900001,
        "LastLSN":  17126746000007456300001,
        "CheckpointLSN":  17126746000006772000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500283800000)\/",
        "BackupFinishDate":  "\/Date(1500283801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2250a249-3c70-46fc-9389-188df8c15e36",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500283801000)\/",
        "Start":  "\/Date(1500283800000)\/",
        "BackupSetId":  "2250a249-3c70-46fc-9389-188df8c15e36",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126746000007456300001,
        "LastLSN":  17126747000004278900001,
        "CheckpointLSN":  17126747000003860800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500284100000)\/",
        "BackupFinishDate":  "\/Date(1500284101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ac0cd0c9-e896-4502-b0e8-d1b7a2dfe2ec",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500284101000)\/",
        "Start":  "\/Date(1500284100000)\/",
        "BackupSetId":  "ac0cd0c9-e896-4502-b0e8-d1b7a2dfe2ec",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126747000004278900001,
        "LastLSN":  17126748000001476800001,
        "CheckpointLSN":  17126748000000828300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500284401000)\/",
        "BackupFinishDate":  "\/Date(1500284401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4019d957-4390-42c7-812a-ecd3bc4b0355",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500284401000)\/",
        "Start":  "\/Date(1500284401000)\/",
        "BackupSetId":  "4019d957-4390-42c7-812a-ecd3bc4b0355",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126748000001476800001,
        "LastLSN":  17126749000002053400001,
        "CheckpointLSN":  17126749000000739500014,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500284700000)\/",
        "BackupFinishDate":  "\/Date(1500284700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "95de62c3-0434-4021-8ae5-7825ae30567c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500284700000)\/",
        "Start":  "\/Date(1500284700000)\/",
        "BackupSetId":  "95de62c3-0434-4021-8ae5-7825ae30567c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126749000002053400001,
        "LastLSN":  17126749000013025600001,
        "CheckpointLSN":  17126749000012397400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500285000000)\/",
        "BackupFinishDate":  "\/Date(1500285000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fc9c0f0a-46fc-4344-a22b-f7d513350d48",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500285000000)\/",
        "Start":  "\/Date(1500285000000)\/",
        "BackupSetId":  "fc9c0f0a-46fc-4344-a22b-f7d513350d48",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126749000013025600001,
        "LastLSN":  17126750000010362200001,
        "CheckpointLSN":  17126750000009396000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500285300000)\/",
        "BackupFinishDate":  "\/Date(1500285300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6a62c97a-88f8-4bde-8824-fe8e1f4ac87b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500285300000)\/",
        "Start":  "\/Date(1500285300000)\/",
        "BackupSetId":  "6a62c97a-88f8-4bde-8824-fe8e1f4ac87b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126750000010362200001,
        "LastLSN":  17126751000011703800001,
        "CheckpointLSN":  17126751000008199400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500285600000)\/",
        "BackupFinishDate":  "\/Date(1500285601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b909e8c6-8fc9-428f-80eb-8aa30e41629f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500285601000)\/",
        "Start":  "\/Date(1500285600000)\/",
        "BackupSetId":  "b909e8c6-8fc9-428f-80eb-8aa30e41629f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126751000011703800001,
        "LastLSN":  17126753000003318200001,
        "CheckpointLSN":  17126753000002501500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500285900000)\/",
        "BackupFinishDate":  "\/Date(1500285901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b6493786-baf5-4f93-bf34-658a5f67490f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500285901000)\/",
        "Start":  "\/Date(1500285900000)\/",
        "BackupSetId":  "b6493786-baf5-4f93-bf34-658a5f67490f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126753000003318200001,
        "LastLSN":  17126754000008863900001,
        "CheckpointLSN":  17126754000006844400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500286201000)\/",
        "BackupFinishDate":  "\/Date(1500286202000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9d0fcc82-7ce6-40b4-b3e4-0eb1ca6ee911",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500286202000)\/",
        "Start":  "\/Date(1500286201000)\/",
        "BackupSetId":  "9d0fcc82-7ce6-40b4-b3e4-0eb1ca6ee911",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126754000008863900001,
        "LastLSN":  17126759000003670200001,
        "CheckpointLSN":  17126759000003375900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500286501000)\/",
        "BackupFinishDate":  "\/Date(1500286502000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4cfc47c0-2a7c-47e0-8806-2bdb9195e72f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500286502000)\/",
        "Start":  "\/Date(1500286501000)\/",
        "BackupSetId":  "4cfc47c0-2a7c-47e0-8806-2bdb9195e72f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126759000003670200001,
        "LastLSN":  17126761000008817200001,
        "CheckpointLSN":  17126761000007590400023,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500286800000)\/",
        "BackupFinishDate":  "\/Date(1500286801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5d5854ff-c79b-4393-b870-9fdc3068ac93",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500286801000)\/",
        "Start":  "\/Date(1500286800000)\/",
        "BackupSetId":  "5d5854ff-c79b-4393-b870-9fdc3068ac93",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126761000008817200001,
        "LastLSN":  17126762000011823300001,
        "CheckpointLSN":  17126762000011774500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500287100000)\/",
        "BackupFinishDate":  "\/Date(1500287101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "cb0f6323-f091-4d6c-ba84-02f8fafcac93",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500287101000)\/",
        "Start":  "\/Date(1500287100000)\/",
        "BackupSetId":  "cb0f6323-f091-4d6c-ba84-02f8fafcac93",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126762000011823300001,
        "LastLSN":  17126763000005679700001,
        "CheckpointLSN":  17126763000005234400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500287401000)\/",
        "BackupFinishDate":  "\/Date(1500287401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "efbe13f0-05e0-4029-bc98-d9bf904ea2c1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500287401000)\/",
        "Start":  "\/Date(1500287401000)\/",
        "BackupSetId":  "efbe13f0-05e0-4029-bc98-d9bf904ea2c1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126763000005679700001,
        "LastLSN":  17126763000012080600001,
        "CheckpointLSN":  17126763000011641100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500287701000)\/",
        "BackupFinishDate":  "\/Date(1500287701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c39c7126-a90d-4527-8f40-5c644f7e4afa",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500287701000)\/",
        "Start":  "\/Date(1500287701000)\/",
        "BackupSetId":  "c39c7126-a90d-4527-8f40-5c644f7e4afa",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126763000012080600001,
        "LastLSN":  17126764000005193900001,
        "CheckpointLSN":  17126764000004786900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500288000000)\/",
        "BackupFinishDate":  "\/Date(1500288000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "33bdaebd-823e-48a6-ad3f-9b9fcdc53d31",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500288000000)\/",
        "Start":  "\/Date(1500288000000)\/",
        "BackupSetId":  "33bdaebd-823e-48a6-ad3f-9b9fcdc53d31",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126764000005193900001,
        "LastLSN":  17126764000011387100001,
        "CheckpointLSN":  17126764000010991400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500288300000)\/",
        "BackupFinishDate":  "\/Date(1500288300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8a7538f1-9906-48aa-8f72-53fe24fdcb3c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500288300000)\/",
        "Start":  "\/Date(1500288300000)\/",
        "BackupSetId":  "8a7538f1-9906-48aa-8f72-53fe24fdcb3c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126764000011387100001,
        "LastLSN":  17126765000010602100001,
        "CheckpointLSN":  17126765000010029500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500288600000)\/",
        "BackupFinishDate":  "\/Date(1500288601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "229332d4-44d3-40c8-a7bd-b1b1bf88c22c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500288601000)\/",
        "Start":  "\/Date(1500288600000)\/",
        "BackupSetId":  "229332d4-44d3-40c8-a7bd-b1b1bf88c22c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126765000010602100001,
        "LastLSN":  17126766000007348100001,
        "CheckpointLSN":  17126766000006462500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500288901000)\/",
        "BackupFinishDate":  "\/Date(1500288901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b1ffda08-4d53-492f-876c-1c3793dbf41b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500288901000)\/",
        "Start":  "\/Date(1500288901000)\/",
        "BackupSetId":  "b1ffda08-4d53-492f-876c-1c3793dbf41b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126766000007348100001,
        "LastLSN":  17126767000001039900001,
        "CheckpointLSN":  17126767000000580000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500289200000)\/",
        "BackupFinishDate":  "\/Date(1500289200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b34fa9a7-a565-4365-b125-7fe845a773f5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500289200000)\/",
        "Start":  "\/Date(1500289200000)\/",
        "BackupSetId":  "b34fa9a7-a565-4365-b125-7fe845a773f5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126767000001039900001,
        "LastLSN":  17126767000007460900001,
        "CheckpointLSN":  17126767000007035800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500289500000)\/",
        "BackupFinishDate":  "\/Date(1500289500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a9b51572-f246-4c95-9da0-685ef39b5b92",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500289500000)\/",
        "Start":  "\/Date(1500289500000)\/",
        "BackupSetId":  "a9b51572-f246-4c95-9da0-685ef39b5b92",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126767000007460900001,
        "LastLSN":  17126768000000779800001,
        "CheckpointLSN":  17126768000000349200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500289800000)\/",
        "BackupFinishDate":  "\/Date(1500289800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d3c3ec44-9bd5-4860-893a-6a888845e1da",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500289800000)\/",
        "Start":  "\/Date(1500289800000)\/",
        "BackupSetId":  "d3c3ec44-9bd5-4860-893a-6a888845e1da",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126768000000779800001,
        "LastLSN":  17126768000007256800001,
        "CheckpointLSN":  17126768000006834400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500290100000)\/",
        "BackupFinishDate":  "\/Date(1500290101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "810fea30-a598-4888-99a0-9705d22a658e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500290101000)\/",
        "Start":  "\/Date(1500290100000)\/",
        "BackupSetId":  "810fea30-a598-4888-99a0-9705d22a658e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126768000007256800001,
        "LastLSN":  17126769000005497500001,
        "CheckpointLSN":  17126769000005149200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500290400000)\/",
        "BackupFinishDate":  "\/Date(1500290400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c1ce98b7-f3e1-4e12-9baf-9e838b08613c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500290400000)\/",
        "Start":  "\/Date(1500290400000)\/",
        "BackupSetId":  "c1ce98b7-f3e1-4e12-9baf-9e838b08613c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126769000005497500001,
        "LastLSN":  17126769000010193000001,
        "CheckpointLSN":  17126769000009331900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500290700000)\/",
        "BackupFinishDate":  "\/Date(1500290700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b69144c9-6ad9-4ebd-a0b3-f18454230105",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500290700000)\/",
        "Start":  "\/Date(1500290700000)\/",
        "BackupSetId":  "b69144c9-6ad9-4ebd-a0b3-f18454230105",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126769000010193000001,
        "LastLSN":  17126769000011671200001,
        "CheckpointLSN":  17126769000011418800011,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500291000000)\/",
        "BackupFinishDate":  "\/Date(1500291000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7626874d-be1c-4206-b69f-200264192cd4",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500291000000)\/",
        "Start":  "\/Date(1500291000000)\/",
        "BackupSetId":  "7626874d-be1c-4206-b69f-200264192cd4",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126769000011671200001,
        "LastLSN":  17126769000012913600001,
        "CheckpointLSN":  17126769000011418800011,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500291300000)\/",
        "BackupFinishDate":  "\/Date(1500291300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c9f7af16-d57b-418a-a18f-c2d4f525e224",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500291300000)\/",
        "Start":  "\/Date(1500291300000)\/",
        "BackupSetId":  "c9f7af16-d57b-418a-a18f-c2d4f525e224",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126769000012913600001,
        "LastLSN":  17126770000001083100001,
        "CheckpointLSN":  17126770000000413000011,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500291600000)\/",
        "BackupFinishDate":  "\/Date(1500291601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d75ae974-2efa-493a-b28a-ff7f250ce1f6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500291601000)\/",
        "Start":  "\/Date(1500291600000)\/",
        "BackupSetId":  "d75ae974-2efa-493a-b28a-ff7f250ce1f6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126770000001083100001,
        "LastLSN":  17126770000002357900001,
        "CheckpointLSN":  17126770000000413000011,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500291900000)\/",
        "BackupFinishDate":  "\/Date(1500291900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "07f3868d-2928-414a-955f-b62ae1e00aff",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500291900000)\/",
        "Start":  "\/Date(1500291900000)\/",
        "BackupSetId":  "07f3868d-2928-414a-955f-b62ae1e00aff",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126770000002357900001,
        "LastLSN":  17126770000007457400001,
        "CheckpointLSN":  17126770000006959000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500292200000)\/",
        "BackupFinishDate":  "\/Date(1500292200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4dfed5ef-7809-4e69-bdaf-40b5fe64096d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500292200000)\/",
        "Start":  "\/Date(1500292200000)\/",
        "BackupSetId":  "4dfed5ef-7809-4e69-bdaf-40b5fe64096d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126770000007457400001,
        "LastLSN":  17126770000010477000001,
        "CheckpointLSN":  17126770000009061600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500292500000)\/",
        "BackupFinishDate":  "\/Date(1500292500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "341898cd-8ec5-4ac1-a4cc-02d967f96284",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500292500000)\/",
        "Start":  "\/Date(1500292500000)\/",
        "BackupSetId":  "341898cd-8ec5-4ac1-a4cc-02d967f96284",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126770000010477000001,
        "LastLSN":  17126770000012142600001,
        "CheckpointLSN":  17126770000011146000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500292801000)\/",
        "BackupFinishDate":  "\/Date(1500292801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b84e61ca-3000-4556-b2f0-e377d62c4c43",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500292801000)\/",
        "Start":  "\/Date(1500292801000)\/",
        "BackupSetId":  "b84e61ca-3000-4556-b2f0-e377d62c4c43",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126770000012142600001,
        "LastLSN":  17126771000001282200001,
        "CheckpointLSN":  17126771000000141500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500293100000)\/",
        "BackupFinishDate":  "\/Date(1500293100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6e6aeb6b-c296-4fd0-998f-10f4723a2820",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500293100000)\/",
        "Start":  "\/Date(1500293100000)\/",
        "BackupSetId":  "6e6aeb6b-c296-4fd0-998f-10f4723a2820",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126771000001282200001,
        "LastLSN":  17126771000002618700001,
        "CheckpointLSN":  17126771000002233100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500293400000)\/",
        "BackupFinishDate":  "\/Date(1500293400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2a6a24cb-8327-4fec-9ee6-0ff9f303b58d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500293400000)\/",
        "Start":  "\/Date(1500293400000)\/",
        "BackupSetId":  "2a6a24cb-8327-4fec-9ee6-0ff9f303b58d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126771000002618700001,
        "LastLSN":  17126771000005599100001,
        "CheckpointLSN":  17126771000004348400016,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500293700000)\/",
        "BackupFinishDate":  "\/Date(1500293700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "94af23e3-d660-4e93-a158-da35872c8f5f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500293700000)\/",
        "Start":  "\/Date(1500293700000)\/",
        "BackupSetId":  "94af23e3-d660-4e93-a158-da35872c8f5f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126771000005599100001,
        "LastLSN":  17126771000007157100001,
        "CheckpointLSN":  17126771000006809600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500294000000)\/",
        "BackupFinishDate":  "\/Date(1500294001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b512ed21-e9fe-47fc-b558-a9c37ed69cec",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500294001000)\/",
        "Start":  "\/Date(1500294000000)\/",
        "BackupSetId":  "b512ed21-e9fe-47fc-b558-a9c37ed69cec",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126771000007157100001,
        "LastLSN":  17126771000008810100001,
        "CheckpointLSN":  17126771000006809600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500294301000)\/",
        "BackupFinishDate":  "\/Date(1500294301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2efbc472-83cd-413f-800c-a7836d4cd91c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500294301000)\/",
        "Start":  "\/Date(1500294301000)\/",
        "BackupSetId":  "2efbc472-83cd-413f-800c-a7836d4cd91c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126771000008810100001,
        "LastLSN":  17126771000010016100001,
        "CheckpointLSN":  17126771000008901300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500294600000)\/",
        "BackupFinishDate":  "\/Date(1500294600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "eb911dc9-e1fb-4f11-9013-144b938f9d89",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500294600000)\/",
        "Start":  "\/Date(1500294600000)\/",
        "BackupSetId":  "eb911dc9-e1fb-4f11-9013-144b938f9d89",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126771000010016100001,
        "LastLSN":  17126773000000178000001,
        "CheckpointLSN":  17126772000010626500021,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500294900000)\/",
        "BackupFinishDate":  "\/Date(1500294901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "dedf7209-9510-4ec2-abf4-ad5bf4854ad2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500294901000)\/",
        "Start":  "\/Date(1500294900000)\/",
        "BackupSetId":  "dedf7209-9510-4ec2-abf4-ad5bf4854ad2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126773000000178000001,
        "LastLSN":  17126773000002010100001,
        "CheckpointLSN":  17126773000000178500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500295200000)\/",
        "BackupFinishDate":  "\/Date(1500295200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6d4a8b85-b8a5-4497-9741-27811db8ac86",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500295200000)\/",
        "Start":  "\/Date(1500295200000)\/",
        "BackupSetId":  "6d4a8b85-b8a5-4497-9741-27811db8ac86",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126773000002010100001,
        "LastLSN":  17126773000003503800001,
        "CheckpointLSN":  17126773000002261000037,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500295500000)\/",
        "BackupFinishDate":  "\/Date(1500295501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "123be27a-dc47-4cf4-8166-36f4183c1401",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500295501000)\/",
        "Start":  "\/Date(1500295500000)\/",
        "BackupSetId":  "123be27a-dc47-4cf4-8166-36f4183c1401",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126773000003503800001,
        "LastLSN":  17126773000004968500001,
        "CheckpointLSN":  17126773000004363500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500295800000)\/",
        "BackupFinishDate":  "\/Date(1500295800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0237d9df-8eaf-4c3f-8258-2b01d083a76d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500295800000)\/",
        "Start":  "\/Date(1500295800000)\/",
        "BackupSetId":  "0237d9df-8eaf-4c3f-8258-2b01d083a76d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126773000004968500001,
        "LastLSN":  17126773000006835600001,
        "CheckpointLSN":  17126773000006453900014,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500296100000)\/",
        "BackupFinishDate":  "\/Date(1500296100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e7c0c765-1f74-45ea-be91-321257ce9ae5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500296100000)\/",
        "Start":  "\/Date(1500296100000)\/",
        "BackupSetId":  "e7c0c765-1f74-45ea-be91-321257ce9ae5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126773000006835600001,
        "LastLSN":  17126774000000125600001,
        "CheckpointLSN":  17126773000011195400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500296400000)\/",
        "BackupFinishDate":  "\/Date(1500296400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4330bde6-bbb8-4b42-b0fb-83e47911e6af",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500296400000)\/",
        "Start":  "\/Date(1500296400000)\/",
        "BackupSetId":  "4330bde6-bbb8-4b42-b0fb-83e47911e6af",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126774000000125600001,
        "LastLSN":  17126774000003548800001,
        "CheckpointLSN":  17126774000002244200026,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500296700000)\/",
        "BackupFinishDate":  "\/Date(1500296701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1e9b8c9d-0a26-46de-9f5d-b0d76bff0da8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500296701000)\/",
        "Start":  "\/Date(1500296700000)\/",
        "BackupSetId":  "1e9b8c9d-0a26-46de-9f5d-b0d76bff0da8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126774000003548800001,
        "LastLSN":  17126774000010613100001,
        "CheckpointLSN":  17126774000009373500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500297001000)\/",
        "BackupFinishDate":  "\/Date(1500297001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "12b8522c-0156-4142-8ba9-45313ead74e1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500297001000)\/",
        "Start":  "\/Date(1500297001000)\/",
        "BackupSetId":  "12b8522c-0156-4142-8ba9-45313ead74e1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126774000010613100001,
        "LastLSN":  17126777000002520900001,
        "CheckpointLSN":  17126777000000748800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500297300000)\/",
        "BackupFinishDate":  "\/Date(1500297300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a4b3a60f-47c7-4f8d-9a89-ce206c25393b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500297300000)\/",
        "Start":  "\/Date(1500297300000)\/",
        "BackupSetId":  "a4b3a60f-47c7-4f8d-9a89-ce206c25393b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126777000002520900001,
        "LastLSN":  17126777000004313900001,
        "CheckpointLSN":  17126777000002832300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500297600000)\/",
        "BackupFinishDate":  "\/Date(1500297600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ca3bce0b-2617-4e8f-a356-d69573d411a8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500297600000)\/",
        "Start":  "\/Date(1500297600000)\/",
        "BackupSetId":  "ca3bce0b-2617-4e8f-a356-d69573d411a8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126777000004313900001,
        "LastLSN":  17126777000006093700001,
        "CheckpointLSN":  17126777000004915000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500297900000)\/",
        "BackupFinishDate":  "\/Date(1500297900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a21045a6-2154-4978-964c-f641a66c7560",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500297900000)\/",
        "Start":  "\/Date(1500297900000)\/",
        "BackupSetId":  "a21045a6-2154-4978-964c-f641a66c7560",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126777000006093700001,
        "LastLSN":  17126777000008065100001,
        "CheckpointLSN":  17126777000006989400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500298200000)\/",
        "BackupFinishDate":  "\/Date(1500298200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "730892d2-2ab7-4509-a474-d057797ea3f3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500298200000)\/",
        "Start":  "\/Date(1500298200000)\/",
        "BackupSetId":  "730892d2-2ab7-4509-a474-d057797ea3f3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126777000008065100001,
        "LastLSN":  17126777000010590300001,
        "CheckpointLSN":  17126777000009089300021,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500298500000)\/",
        "BackupFinishDate":  "\/Date(1500298501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "89f62dec-7431-40d4-a84e-c70ef479b348",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500298501000)\/",
        "Start":  "\/Date(1500298500000)\/",
        "BackupSetId":  "89f62dec-7431-40d4-a84e-c70ef479b348",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126777000010590300001,
        "LastLSN":  17126778000002117600001,
        "CheckpointLSN":  17126778000000271200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500298801000)\/",
        "BackupFinishDate":  "\/Date(1500298801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b5988b14-0dfb-490c-a6fc-883aeea0eedd",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500298801000)\/",
        "Start":  "\/Date(1500298801000)\/",
        "BackupSetId":  "b5988b14-0dfb-490c-a6fc-883aeea0eedd",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126778000002117600001,
        "LastLSN":  17126778000003921600001,
        "CheckpointLSN":  17126778000002352400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500299100000)\/",
        "BackupFinishDate":  "\/Date(1500299100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3bceadf0-e7b9-4213-a51b-d53e1d5f7487",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500299100000)\/",
        "Start":  "\/Date(1500299100000)\/",
        "BackupSetId":  "3bceadf0-e7b9-4213-a51b-d53e1d5f7487",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126778000003921600001,
        "LastLSN":  17126778000006263200001,
        "CheckpointLSN":  17126778000004435600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500299400000)\/",
        "BackupFinishDate":  "\/Date(1500299400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "61fd0a07-3844-43bd-b3b6-2c040c564d58",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500299400000)\/",
        "Start":  "\/Date(1500299400000)\/",
        "BackupSetId":  "61fd0a07-3844-43bd-b3b6-2c040c564d58",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126778000006263200001,
        "LastLSN":  17126778000008246700001,
        "CheckpointLSN":  17126778000006522900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500299700000)\/",
        "BackupFinishDate":  "\/Date(1500299700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7e4e821e-cf7a-4608-af25-268c4c5d3c8e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500299700000)\/",
        "Start":  "\/Date(1500299700000)\/",
        "BackupSetId":  "7e4e821e-cf7a-4608-af25-268c4c5d3c8e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126778000008246700001,
        "LastLSN":  17126778000010081000001,
        "CheckpointLSN":  17126778000008606700003,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500300001000)\/",
        "BackupFinishDate":  "\/Date(1500300001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d1699682-4059-4480-9e2c-0800a085789e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500300001000)\/",
        "Start":  "\/Date(1500300001000)\/",
        "BackupSetId":  "d1699682-4059-4480-9e2c-0800a085789e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126778000010081000001,
        "LastLSN":  17126778000011836900001,
        "CheckpointLSN":  17126778000010687500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500300300000)\/",
        "BackupFinishDate":  "\/Date(1500300300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "85d85017-6a09-48ff-9acd-ada7359e953d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500300300000)\/",
        "Start":  "\/Date(1500300300000)\/",
        "BackupSetId":  "85d85017-6a09-48ff-9acd-ada7359e953d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126778000011836900001,
        "LastLSN":  17126779000000810100001,
        "CheckpointLSN":  17126778000012797400011,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500300600000)\/",
        "BackupFinishDate":  "\/Date(1500300600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ed3937d8-b02d-40e2-9889-ae8b3af4429f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500300600000)\/",
        "Start":  "\/Date(1500300600000)\/",
        "BackupSetId":  "ed3937d8-b02d-40e2-9889-ae8b3af4429f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126779000000810100001,
        "LastLSN":  17126779000004732900001,
        "CheckpointLSN":  17126779000002926400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500300900000)\/",
        "BackupFinishDate":  "\/Date(1500300900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "36f8b3f4-5f1a-4027-afde-b35af46389d5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500300900000)\/",
        "Start":  "\/Date(1500300900000)\/",
        "BackupSetId":  "36f8b3f4-5f1a-4027-afde-b35af46389d5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126779000004732900001,
        "LastLSN":  17126779000008977700001,
        "CheckpointLSN":  17126779000007086000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500301200000)\/",
        "BackupFinishDate":  "\/Date(1500301201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "13cae25a-7d70-4895-9339-1143a3e46c04",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500301201000)\/",
        "Start":  "\/Date(1500301200000)\/",
        "BackupSetId":  "13cae25a-7d70-4895-9339-1143a3e46c04",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126779000008977700001,
        "LastLSN":  17126780000000122300001,
        "CheckpointLSN":  17126779000011324900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500301500000)\/",
        "BackupFinishDate":  "\/Date(1500301501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ab91028a-56a2-43db-8847-7759a842fb88",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500301501000)\/",
        "Start":  "\/Date(1500301500000)\/",
        "BackupSetId":  "ab91028a-56a2-43db-8847-7759a842fb88",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126780000000122300001,
        "LastLSN":  17126780000006889000001,
        "CheckpointLSN":  17126780000004272600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500301800000)\/",
        "BackupFinishDate":  "\/Date(1500301800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "199c3e8f-3f7f-4fa5-94b8-11c7cf861bfa",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500301800000)\/",
        "Start":  "\/Date(1500301800000)\/",
        "BackupSetId":  "199c3e8f-3f7f-4fa5-94b8-11c7cf861bfa",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126780000006889000001,
        "LastLSN":  17126781000000129200001,
        "CheckpointLSN":  17126780000011125600020,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500302100000)\/",
        "BackupFinishDate":  "\/Date(1500302100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6172e35b-9c3a-4b09-a157-8495f8151fbf",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500302100000)\/",
        "Start":  "\/Date(1500302100000)\/",
        "BackupSetId":  "6172e35b-9c3a-4b09-a157-8495f8151fbf",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126781000000129200001,
        "LastLSN":  17126781000007955900001,
        "CheckpointLSN":  17126781000006067200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500302400000)\/",
        "BackupFinishDate":  "\/Date(1500302400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5e54dbe6-9c82-4123-b2bb-c88373031c02",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500302400000)\/",
        "Start":  "\/Date(1500302400000)\/",
        "BackupSetId":  "5e54dbe6-9c82-4123-b2bb-c88373031c02",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126781000007955900001,
        "LastLSN":  17126782000000191800001,
        "CheckpointLSN":  17126781000012584300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500302700000)\/",
        "BackupFinishDate":  "\/Date(1500302700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1b63b665-eb4c-4906-a003-57bb5de0dd13",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500302700000)\/",
        "Start":  "\/Date(1500302700000)\/",
        "BackupSetId":  "1b63b665-eb4c-4906-a003-57bb5de0dd13",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126782000000191800001,
        "LastLSN":  17126782000003846200001,
        "CheckpointLSN":  17126782000002271400021,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500303000000)\/",
        "BackupFinishDate":  "\/Date(1500303000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9e1a787e-ca4a-4800-90e9-62fe9da9e61c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500303000000)\/",
        "Start":  "\/Date(1500303000000)\/",
        "BackupSetId":  "9e1a787e-ca4a-4800-90e9-62fe9da9e61c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126782000003846200001,
        "LastLSN":  17126782000007532100001,
        "CheckpointLSN":  17126782000006493500033,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500303300000)\/",
        "BackupFinishDate":  "\/Date(1500303301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "22cd95f2-6237-437b-b9c4-68d8a91b03b2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500303301000)\/",
        "Start":  "\/Date(1500303300000)\/",
        "BackupSetId":  "22cd95f2-6237-437b-b9c4-68d8a91b03b2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126782000007532100001,
        "LastLSN":  17126782000011336100001,
        "CheckpointLSN":  17126782000011301400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500303600000)\/",
        "BackupFinishDate":  "\/Date(1500303600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b58cfb81-6a1c-41eb-a89f-5a6c2d13765f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500303600000)\/",
        "Start":  "\/Date(1500303600000)\/",
        "BackupSetId":  "b58cfb81-6a1c-41eb-a89f-5a6c2d13765f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126782000011336100001,
        "LastLSN":  17126783000003856900001,
        "CheckpointLSN":  17126783000002377900010,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500303900000)\/",
        "BackupFinishDate":  "\/Date(1500303900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "804850b6-1f91-4897-95b9-cf29522025c7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500303900000)\/",
        "Start":  "\/Date(1500303900000)\/",
        "BackupSetId":  "804850b6-1f91-4897-95b9-cf29522025c7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126783000003856900001,
        "LastLSN":  17126783000007774900001,
        "CheckpointLSN":  17126783000006572700012,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500304200000)\/",
        "BackupFinishDate":  "\/Date(1500304200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "97093961-a8e1-4a1d-b51f-2641e5963deb",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500304200000)\/",
        "Start":  "\/Date(1500304200000)\/",
        "BackupSetId":  "97093961-a8e1-4a1d-b51f-2641e5963deb",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126783000007774900001,
        "LastLSN":  17126783000009876500001,
        "CheckpointLSN":  17126783000008783800010,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500304500000)\/",
        "BackupFinishDate":  "\/Date(1500304500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "81b96f14-ab1a-4ccf-b74b-8ac9265ea8e2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500304500000)\/",
        "Start":  "\/Date(1500304500000)\/",
        "BackupSetId":  "81b96f14-ab1a-4ccf-b74b-8ac9265ea8e2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126783000009876500001,
        "LastLSN":  17126783000011970900001,
        "CheckpointLSN":  17126783000010917100020,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500304800000)\/",
        "BackupFinishDate":  "\/Date(1500304801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f4109d94-3369-4fbd-af37-a845e30527cd",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500304801000)\/",
        "Start":  "\/Date(1500304800000)\/",
        "BackupSetId":  "f4109d94-3369-4fbd-af37-a845e30527cd",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126783000011970900001,
        "LastLSN":  17126784000000917400001,
        "CheckpointLSN":  17126783000013021300012,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500305101000)\/",
        "BackupFinishDate":  "\/Date(1500305101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9b753932-0740-4f60-a9b9-53480db2a860",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500305101000)\/",
        "Start":  "\/Date(1500305101000)\/",
        "BackupSetId":  "9b753932-0740-4f60-a9b9-53480db2a860",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126784000000917400001,
        "LastLSN":  17126784000002971800001,
        "CheckpointLSN":  17126784000000917400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500305400000)\/",
        "BackupFinishDate":  "\/Date(1500305400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "930bec35-8193-4e23-8708-0ecf42a3d1ee",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500305400000)\/",
        "Start":  "\/Date(1500305400000)\/",
        "BackupSetId":  "930bec35-8193-4e23-8708-0ecf42a3d1ee",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126784000002971800001,
        "LastLSN":  17126784000005112100001,
        "CheckpointLSN":  17126784000005045900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500305700000)\/",
        "BackupFinishDate":  "\/Date(1500305700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "61df2d80-752a-4b5a-9010-9d414be9ea43",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500305700000)\/",
        "Start":  "\/Date(1500305700000)\/",
        "BackupSetId":  "61df2d80-752a-4b5a-9010-9d414be9ea43",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126784000005112100001,
        "LastLSN":  17126784000009792700001,
        "CheckpointLSN":  17126784000009487000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500306000000)\/",
        "BackupFinishDate":  "\/Date(1500306000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "111d878a-6fff-4923-a43c-5c69411e0439",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500306000000)\/",
        "Start":  "\/Date(1500306000000)\/",
        "BackupSetId":  "111d878a-6fff-4923-a43c-5c69411e0439",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126784000009792700001,
        "LastLSN":  17126785000003622200001,
        "CheckpointLSN":  17126785000003095000013,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500306300000)\/",
        "BackupFinishDate":  "\/Date(1500306301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0a59e961-41fc-4064-9ae7-8117dcf74d65",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500306301000)\/",
        "Start":  "\/Date(1500306300000)\/",
        "BackupSetId":  "0a59e961-41fc-4064-9ae7-8117dcf74d65",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126785000003622200001,
        "LastLSN":  17126785000006995200001,
        "CheckpointLSN":  17126785000005295800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500306600000)\/",
        "BackupFinishDate":  "\/Date(1500306600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ef28a791-4910-4776-b427-3dcad88030fe",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500306600000)\/",
        "Start":  "\/Date(1500306600000)\/",
        "BackupSetId":  "ef28a791-4910-4776-b427-3dcad88030fe",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126785000006995200001,
        "LastLSN":  17126785000009533100001,
        "CheckpointLSN":  17126785000009451100017,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500306900000)\/",
        "BackupFinishDate":  "\/Date(1500306900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ee4ab663-6d30-4d2f-8758-e722c7d8b7db",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500306900000)\/",
        "Start":  "\/Date(1500306900000)\/",
        "BackupSetId":  "ee4ab663-6d30-4d2f-8758-e722c7d8b7db",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126785000009533100001,
        "LastLSN":  17126785000010803400001,
        "CheckpointLSN":  17126785000009451100017,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500307200000)\/",
        "BackupFinishDate":  "\/Date(1500307200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8b9235ab-650d-41d4-a2a7-ac7807da7eec",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500307200000)\/",
        "Start":  "\/Date(1500307200000)\/",
        "BackupSetId":  "8b9235ab-650d-41d4-a2a7-ac7807da7eec",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126785000010803400001,
        "LastLSN":  17126785000012009100001,
        "CheckpointLSN":  17126785000011552000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500307500000)\/",
        "BackupFinishDate":  "\/Date(1500307500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9f86cd81-7997-4c7e-b131-4e9d8b263f93",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500307500000)\/",
        "Start":  "\/Date(1500307500000)\/",
        "BackupSetId":  "9f86cd81-7997-4c7e-b131-4e9d8b263f93",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126785000012009100001,
        "LastLSN":  17126786000000075800001,
        "CheckpointLSN":  17126785000011552000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500307800000)\/",
        "BackupFinishDate":  "\/Date(1500307800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "92c64cba-3cd4-423e-855d-9a168bef3431",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500307800000)\/",
        "Start":  "\/Date(1500307800000)\/",
        "BackupSetId":  "92c64cba-3cd4-423e-855d-9a168bef3431",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126786000000075800001,
        "LastLSN":  17126786000001283700001,
        "CheckpointLSN":  17126786000000075800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500308100000)\/",
        "BackupFinishDate":  "\/Date(1500308100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c5aeff97-bfdd-4892-9cbb-ee37a8ca9599",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500308100000)\/",
        "Start":  "\/Date(1500308100000)\/",
        "BackupSetId":  "c5aeff97-bfdd-4892-9cbb-ee37a8ca9599",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126786000001283700001,
        "LastLSN":  17126786000002464700001,
        "CheckpointLSN":  17126786000002158000018,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500308400000)\/",
        "BackupFinishDate":  "\/Date(1500308400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "65f4baf9-bfe4-41d1-842f-1b4265f781f1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500308400000)\/",
        "Start":  "\/Date(1500308400000)\/",
        "BackupSetId":  "65f4baf9-bfe4-41d1-842f-1b4265f781f1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126786000002464700001,
        "LastLSN":  17126786000003657100001,
        "CheckpointLSN":  17126786000002158000018,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500308700000)\/",
        "BackupFinishDate":  "\/Date(1500308700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "83043ee6-68a2-44ea-9157-dda30714019c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500308700000)\/",
        "Start":  "\/Date(1500308700000)\/",
        "BackupSetId":  "83043ee6-68a2-44ea-9157-dda30714019c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126786000003657100001,
        "LastLSN":  17126786000004852900001,
        "CheckpointLSN":  17126786000004262600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500309001000)\/",
        "BackupFinishDate":  "\/Date(1500309001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "26b98811-378c-444b-a818-76a4375dfdbf",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500309001000)\/",
        "Start":  "\/Date(1500309001000)\/",
        "BackupSetId":  "26b98811-378c-444b-a818-76a4375dfdbf",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126786000004852900001,
        "LastLSN":  17126786000006060600001,
        "CheckpointLSN":  17126786000004262600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500309300000)\/",
        "BackupFinishDate":  "\/Date(1500309300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ddbd7bb1-67f7-478f-8a34-7b91ad163cf7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500309300000)\/",
        "Start":  "\/Date(1500309300000)\/",
        "BackupSetId":  "ddbd7bb1-67f7-478f-8a34-7b91ad163cf7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126786000006060600001,
        "LastLSN":  17126786000007231300001,
        "CheckpointLSN":  17126786000006350500025,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500309600000)\/",
        "BackupFinishDate":  "\/Date(1500309600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f5bac5f6-6dc4-48c4-be02-e3109ef21bab",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500309600000)\/",
        "Start":  "\/Date(1500309600000)\/",
        "BackupSetId":  "f5bac5f6-6dc4-48c4-be02-e3109ef21bab",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126786000007231300001,
        "LastLSN":  17126786000008387300001,
        "CheckpointLSN":  17126786000006350500025,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500309900000)\/",
        "BackupFinishDate":  "\/Date(1500309900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "19cdc4ee-1c30-4754-a8bb-efac97be38d6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500309900000)\/",
        "Start":  "\/Date(1500309900000)\/",
        "BackupSetId":  "19cdc4ee-1c30-4754-a8bb-efac97be38d6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126786000008387300001,
        "LastLSN":  17126786000009546900001,
        "CheckpointLSN":  17126786000008421600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500310200000)\/",
        "BackupFinishDate":  "\/Date(1500310200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f208e5b6-b897-4769-a745-29a362d3c0da",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500310200000)\/",
        "Start":  "\/Date(1500310200000)\/",
        "BackupSetId":  "f208e5b6-b897-4769-a745-29a362d3c0da",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126786000009546900001,
        "LastLSN":  17126786000010698900001,
        "CheckpointLSN":  17126786000010503000015,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500310500000)\/",
        "BackupFinishDate":  "\/Date(1500310500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "19b76ecf-21eb-4cb5-91d8-5f41c46f536b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500310500000)\/",
        "Start":  "\/Date(1500310500000)\/",
        "BackupSetId":  "19b76ecf-21eb-4cb5-91d8-5f41c46f536b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126786000010698900001,
        "LastLSN":  17126786000011867500001,
        "CheckpointLSN":  17126786000011858200224,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500310801000)\/",
        "BackupFinishDate":  "\/Date(1500310801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "96c4f67a-bc49-443e-9a3a-6ea789dbccba",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500310801000)\/",
        "Start":  "\/Date(1500310801000)\/",
        "BackupSetId":  "96c4f67a-bc49-443e-9a3a-6ea789dbccba",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126786000011867500001,
        "LastLSN":  17126787000009695300001,
        "CheckpointLSN":  17126787000008980800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500311100000)\/",
        "BackupFinishDate":  "\/Date(1500311101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "69fd6bfd-9799-4b9f-b361-a2e7f378a4e9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500311101000)\/",
        "Start":  "\/Date(1500311100000)\/",
        "BackupSetId":  "69fd6bfd-9799-4b9f-b361-a2e7f378a4e9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126787000009695300001,
        "LastLSN":  17126788000000905000001,
        "CheckpointLSN":  17126788000000012500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500311400000)\/",
        "BackupFinishDate":  "\/Date(1500311400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1e257e33-4a25-4cb0-a931-9c24beb9eb74",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500311400000)\/",
        "Start":  "\/Date(1500311400000)\/",
        "BackupSetId":  "1e257e33-4a25-4cb0-a931-9c24beb9eb74",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126788000000905000001,
        "LastLSN":  17126788000002017500001,
        "CheckpointLSN":  17126788000000012500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500311700000)\/",
        "BackupFinishDate":  "\/Date(1500311700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0db20a5e-da84-4bd2-8089-e4a15af46fdb",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500311700000)\/",
        "Start":  "\/Date(1500311700000)\/",
        "BackupSetId":  "0db20a5e-da84-4bd2-8089-e4a15af46fdb",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126788000002017500001,
        "LastLSN":  17126788000003236700001,
        "CheckpointLSN":  17126788000002076700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500312000000)\/",
        "BackupFinishDate":  "\/Date(1500312000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "aefcf752-99f7-40db-8a39-109cd7e5f3c3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500312000000)\/",
        "Start":  "\/Date(1500312000000)\/",
        "BackupSetId":  "aefcf752-99f7-40db-8a39-109cd7e5f3c3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126788000003236700001,
        "LastLSN":  17126788000004427900001,
        "CheckpointLSN":  17126788000004155300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500312300000)\/",
        "BackupFinishDate":  "\/Date(1500312301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d4c37d96-ef83-49f4-8a37-c88e58fcc64e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500312301000)\/",
        "Start":  "\/Date(1500312300000)\/",
        "BackupSetId":  "d4c37d96-ef83-49f4-8a37-c88e58fcc64e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126788000004427900001,
        "LastLSN":  17126788000005594500001,
        "CheckpointLSN":  17126788000004155300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500312600000)\/",
        "BackupFinishDate":  "\/Date(1500312600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0da97386-ed6f-40ea-bca3-821cce95d411",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500312600000)\/",
        "Start":  "\/Date(1500312600000)\/",
        "BackupSetId":  "0da97386-ed6f-40ea-bca3-821cce95d411",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126788000005594500001,
        "LastLSN":  17126788000006802800001,
        "CheckpointLSN":  17126788000006230200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500312900000)\/",
        "BackupFinishDate":  "\/Date(1500312900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fbbd0454-1665-4688-8f49-9716c73ac406",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500312900000)\/",
        "Start":  "\/Date(1500312900000)\/",
        "BackupSetId":  "fbbd0454-1665-4688-8f49-9716c73ac406",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126788000006802800001,
        "LastLSN":  17126788000008012800001,
        "CheckpointLSN":  17126788000006230200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500313200000)\/",
        "BackupFinishDate":  "\/Date(1500313200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a17ca55b-b711-4e7c-9b52-c9feb7a4b01f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500313200000)\/",
        "Start":  "\/Date(1500313200000)\/",
        "BackupSetId":  "a17ca55b-b711-4e7c-9b52-c9feb7a4b01f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126788000008012800001,
        "LastLSN":  17126788000009239900001,
        "CheckpointLSN":  17126788000008319400021,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500313500000)\/",
        "BackupFinishDate":  "\/Date(1500313500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d1333f54-d262-4c65-b8a2-08e24342f01d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500313500000)\/",
        "Start":  "\/Date(1500313500000)\/",
        "BackupSetId":  "d1333f54-d262-4c65-b8a2-08e24342f01d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126788000009239900001,
        "LastLSN":  17126788000010434800001,
        "CheckpointLSN":  17126788000010390000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500313800000)\/",
        "BackupFinishDate":  "\/Date(1500313800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "450e3a77-22cd-4721-9e8b-4908ca88e1f8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500313800000)\/",
        "Start":  "\/Date(1500313800000)\/",
        "BackupSetId":  "450e3a77-22cd-4721-9e8b-4908ca88e1f8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126788000010434800001,
        "LastLSN":  17126788000011630800001,
        "CheckpointLSN":  17126788000010390000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500314100000)\/",
        "BackupFinishDate":  "\/Date(1500314100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ed74e744-d90f-4608-a2bc-b0f309cce63e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500314100000)\/",
        "Start":  "\/Date(1500314100000)\/",
        "BackupSetId":  "ed74e744-d90f-4608-a2bc-b0f309cce63e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126788000011630800001,
        "LastLSN":  17126788000012836800001,
        "CheckpointLSN":  17126788000012478400019,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500314400000)\/",
        "BackupFinishDate":  "\/Date(1500314400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3f54addf-32db-478e-935f-3fd8248531b2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500314400000)\/",
        "Start":  "\/Date(1500314400000)\/",
        "BackupSetId":  "3f54addf-32db-478e-935f-3fd8248531b2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126788000012836800001,
        "LastLSN":  17126789000000926900001,
        "CheckpointLSN":  17126788000012478400019,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500314700000)\/",
        "BackupFinishDate":  "\/Date(1500314700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "bcaf896e-bac4-41da-b075-de9ee17104dd",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500314700000)\/",
        "Start":  "\/Date(1500314700000)\/",
        "BackupSetId":  "bcaf896e-bac4-41da-b075-de9ee17104dd",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126789000000926900001,
        "LastLSN":  17126789000002123200001,
        "CheckpointLSN":  17126789000000926900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500315000000)\/",
        "BackupFinishDate":  "\/Date(1500315000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "929003ea-be9f-446b-bc1e-72d53c20a51f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500315000000)\/",
        "Start":  "\/Date(1500315000000)\/",
        "BackupSetId":  "929003ea-be9f-446b-bc1e-72d53c20a51f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126789000002123200001,
        "LastLSN":  17126789000003334600001,
        "CheckpointLSN":  17126789000003013500028,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500315300000)\/",
        "BackupFinishDate":  "\/Date(1500315300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "03da547b-31a1-4bf6-902b-5fd073294502",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500315300000)\/",
        "Start":  "\/Date(1500315300000)\/",
        "BackupSetId":  "03da547b-31a1-4bf6-902b-5fd073294502",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126789000003334600001,
        "LastLSN":  17126789000004497500001,
        "CheckpointLSN":  17126789000003013500028,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500315600000)\/",
        "BackupFinishDate":  "\/Date(1500315600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "01ee91bb-3361-4fdf-9d9a-7915bc961aef",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500315600000)\/",
        "Start":  "\/Date(1500315600000)\/",
        "BackupSetId":  "01ee91bb-3361-4fdf-9d9a-7915bc961aef",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126789000004497500001,
        "LastLSN":  17126789000005711100001,
        "CheckpointLSN":  17126789000005126100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500315900000)\/",
        "BackupFinishDate":  "\/Date(1500315900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1af11de0-cd53-4654-b1fc-2acb27a37667",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500315900000)\/",
        "Start":  "\/Date(1500315900000)\/",
        "BackupSetId":  "1af11de0-cd53-4654-b1fc-2acb27a37667",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126789000005711100001,
        "LastLSN":  17126789000006890700001,
        "CheckpointLSN":  17126789000005126100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500316200000)\/",
        "BackupFinishDate":  "\/Date(1500316200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c3594826-a7d7-400b-a31f-6415ab5db356",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500316200000)\/",
        "Start":  "\/Date(1500316200000)\/",
        "BackupSetId":  "c3594826-a7d7-400b-a31f-6415ab5db356",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126789000006890700001,
        "LastLSN":  17126789000008104100001,
        "CheckpointLSN":  17126789000007196100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500316500000)\/",
        "BackupFinishDate":  "\/Date(1500316500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c58fb382-2fac-4441-ac41-249ce0aec21d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500316500000)\/",
        "Start":  "\/Date(1500316500000)\/",
        "BackupSetId":  "c58fb382-2fac-4441-ac41-249ce0aec21d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126789000008104100001,
        "LastLSN":  17126789000009290400001,
        "CheckpointLSN":  17126789000009264800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500316800000)\/",
        "BackupFinishDate":  "\/Date(1500316800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b93dbe64-b2fc-4dca-be13-9c67bfde22e4",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500316800000)\/",
        "Start":  "\/Date(1500316800000)\/",
        "BackupSetId":  "b93dbe64-b2fc-4dca-be13-9c67bfde22e4",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126789000009290400001,
        "LastLSN":  17126789000010467300001,
        "CheckpointLSN":  17126789000009264800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500317100000)\/",
        "BackupFinishDate":  "\/Date(1500317101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "830b5a49-5bbf-48eb-958c-2649de4d38cf",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500317101000)\/",
        "Start":  "\/Date(1500317100000)\/",
        "BackupSetId":  "830b5a49-5bbf-48eb-958c-2649de4d38cf",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126789000010467300001,
        "LastLSN":  17126789000011655000001,
        "CheckpointLSN":  17126789000011355400018,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500317400000)\/",
        "BackupFinishDate":  "\/Date(1500317400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7754519e-9d85-4330-b411-13f9dec08ddb",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500317400000)\/",
        "Start":  "\/Date(1500317400000)\/",
        "BackupSetId":  "7754519e-9d85-4330-b411-13f9dec08ddb",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126789000011655000001,
        "LastLSN":  17126789000012838400001,
        "CheckpointLSN":  17126789000011355400018,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500317700000)\/",
        "BackupFinishDate":  "\/Date(1500317700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c09b4498-633d-4878-9275-ace3c14f13de",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500317700000)\/",
        "Start":  "\/Date(1500317700000)\/",
        "BackupSetId":  "c09b4498-633d-4878-9275-ace3c14f13de",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126789000012838400001,
        "LastLSN":  17126790000000941500001,
        "CheckpointLSN":  17126790000000364500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500318000000)\/",
        "BackupFinishDate":  "\/Date(1500318000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f410cf53-1fc3-4d33-8704-315ba49a2b5e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500318000000)\/",
        "Start":  "\/Date(1500318000000)\/",
        "BackupSetId":  "f410cf53-1fc3-4d33-8704-315ba49a2b5e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126790000000941500001,
        "LastLSN":  17126790000002240600001,
        "CheckpointLSN":  17126790000000364500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500318301000)\/",
        "BackupFinishDate":  "\/Date(1500318301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0b28ee12-0115-4dcc-a155-48a2b2635372",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500318301000)\/",
        "Start":  "\/Date(1500318301000)\/",
        "BackupSetId":  "0b28ee12-0115-4dcc-a155-48a2b2635372",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126790000002240600001,
        "LastLSN":  17126790000003876300001,
        "CheckpointLSN":  17126790000002434300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500318600000)\/",
        "BackupFinishDate":  "\/Date(1500318600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "94d028ab-6264-4c2c-a39c-b2f2382b2d61",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500318600000)\/",
        "Start":  "\/Date(1500318600000)\/",
        "BackupSetId":  "94d028ab-6264-4c2c-a39c-b2f2382b2d61",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126790000003876300001,
        "LastLSN":  17126790000005588800001,
        "CheckpointLSN":  17126790000004507300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500318900000)\/",
        "BackupFinishDate":  "\/Date(1500318900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "801e8890-30d1-42af-97d4-6d50480cfeb2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500318900000)\/",
        "Start":  "\/Date(1500318900000)\/",
        "BackupSetId":  "801e8890-30d1-42af-97d4-6d50480cfeb2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126790000005588800001,
        "LastLSN":  17126790000012694800001,
        "CheckpointLSN":  17126790000011694400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500319200000)\/",
        "BackupFinishDate":  "\/Date(1500319201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "387bd64c-9dd3-4d80-a834-91d1b240d22a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500319201000)\/",
        "Start":  "\/Date(1500319200000)\/",
        "BackupSetId":  "387bd64c-9dd3-4d80-a834-91d1b240d22a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126790000012694800001,
        "LastLSN":  17126795000003531600001,
        "CheckpointLSN":  17126795000003138100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500319500000)\/",
        "BackupFinishDate":  "\/Date(1500319501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c2757c70-94aa-4669-9a25-ca8c4f978bb8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500319501000)\/",
        "Start":  "\/Date(1500319500000)\/",
        "BackupSetId":  "c2757c70-94aa-4669-9a25-ca8c4f978bb8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126795000003531600001,
        "LastLSN":  17126848000002843800001,
        "CheckpointLSN":  17126840000008736200036,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500319800000)\/",
        "BackupFinishDate":  "\/Date(1500319821000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a1d080d6-1487-4dba-8aa4-ca8d4010dc95",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500319821000)\/",
        "Start":  "\/Date(1500319800000)\/",
        "BackupSetId":  "a1d080d6-1487-4dba-8aa4-ca8d4010dc95",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126848000002843800001,
        "LastLSN":  17126848000004172700001,
        "CheckpointLSN":  17126848000002870200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500320101000)\/",
        "BackupFinishDate":  "\/Date(1500320101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "22e9e106-8ddd-42d8-98ae-5d778cd8c2ef",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500320101000)\/",
        "Start":  "\/Date(1500320101000)\/",
        "BackupSetId":  "22e9e106-8ddd-42d8-98ae-5d778cd8c2ef",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126848000004172700001,
        "LastLSN":  17126848000005396500001,
        "CheckpointLSN":  17126848000004934500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500320400000)\/",
        "BackupFinishDate":  "\/Date(1500320400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "10115706-e329-46d9-812b-03271af0e193",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500320400000)\/",
        "Start":  "\/Date(1500320400000)\/",
        "BackupSetId":  "10115706-e329-46d9-812b-03271af0e193",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126848000005396500001,
        "LastLSN":  17126848000006603200001,
        "CheckpointLSN":  17126848000004934500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500320700000)\/",
        "BackupFinishDate":  "\/Date(1500320700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "646fe6c1-9e63-4d6e-b85d-3a4753ddbcba",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500320700000)\/",
        "Start":  "\/Date(1500320700000)\/",
        "BackupSetId":  "646fe6c1-9e63-4d6e-b85d-3a4753ddbcba",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126848000006603200001,
        "LastLSN":  17126848000007803200001,
        "CheckpointLSN":  17126848000007008000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500321000000)\/",
        "BackupFinishDate":  "\/Date(1500321000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "06e17623-8252-4383-be66-e021c039216d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500321000000)\/",
        "Start":  "\/Date(1500321000000)\/",
        "BackupSetId":  "06e17623-8252-4383-be66-e021c039216d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126848000007803200001,
        "LastLSN":  17126848000009001600001,
        "CheckpointLSN":  17126848000007008000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500321300000)\/",
        "BackupFinishDate":  "\/Date(1500321300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "db351aee-6d6d-429c-a9e0-bde4d54b8c3e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500321300000)\/",
        "Start":  "\/Date(1500321300000)\/",
        "BackupSetId":  "db351aee-6d6d-429c-a9e0-bde4d54b8c3e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126848000009001600001,
        "LastLSN":  17126848000010197800001,
        "CheckpointLSN":  17126848000009078700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500321600000)\/",
        "BackupFinishDate":  "\/Date(1500321600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ecafd19b-1fb1-41f0-afa6-adf449bda841",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500321600000)\/",
        "Start":  "\/Date(1500321600000)\/",
        "BackupSetId":  "ecafd19b-1fb1-41f0-afa6-adf449bda841",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126848000010197800001,
        "LastLSN":  17126848000011480700001,
        "CheckpointLSN":  17126848000011165700080,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500321900000)\/",
        "BackupFinishDate":  "\/Date(1500321900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f71f7b17-ada2-44c8-a411-67d52a381e2c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500321900000)\/",
        "Start":  "\/Date(1500321900000)\/",
        "BackupSetId":  "f71f7b17-ada2-44c8-a411-67d52a381e2c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126848000011480700001,
        "LastLSN":  17126848000012660000001,
        "CheckpointLSN":  17126848000011165700080,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500322200000)\/",
        "BackupFinishDate":  "\/Date(1500322200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "aa3dea0c-9816-4140-9969-c79258595a51",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500322200000)\/",
        "Start":  "\/Date(1500322200000)\/",
        "BackupSetId":  "aa3dea0c-9816-4140-9969-c79258595a51",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126848000012660000001,
        "LastLSN":  17126849000000746500001,
        "CheckpointLSN":  17126849000000195400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500322500000)\/",
        "BackupFinishDate":  "\/Date(1500322501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fc5cd42b-ce88-43dd-83ec-928893a8c103",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500322501000)\/",
        "Start":  "\/Date(1500322500000)\/",
        "BackupSetId":  "fc5cd42b-ce88-43dd-83ec-928893a8c103",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126849000000746500001,
        "LastLSN":  17126849000001913300001,
        "CheckpointLSN":  17126849000000195400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500322800000)\/",
        "BackupFinishDate":  "\/Date(1500322800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3428d0b5-b8bf-435a-8d61-0c58c6566af4",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500322800000)\/",
        "Start":  "\/Date(1500322800000)\/",
        "BackupSetId":  "3428d0b5-b8bf-435a-8d61-0c58c6566af4",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126849000001913300001,
        "LastLSN":  17126849000003125200001,
        "CheckpointLSN":  17126849000002265400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500323100000)\/",
        "BackupFinishDate":  "\/Date(1500323100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "75f1be35-8fb8-43aa-80e5-bff41f6216a7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500323100000)\/",
        "Start":  "\/Date(1500323100000)\/",
        "BackupSetId":  "75f1be35-8fb8-43aa-80e5-bff41f6216a7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126849000003125200001,
        "LastLSN":  17126849000004305900001,
        "CheckpointLSN":  17126849000002265400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500323400000)\/",
        "BackupFinishDate":  "\/Date(1500323400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "10cc640f-4c2a-41e5-bec8-728f69d10929",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500323400000)\/",
        "Start":  "\/Date(1500323400000)\/",
        "BackupSetId":  "10cc640f-4c2a-41e5-bec8-728f69d10929",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126849000004305900001,
        "LastLSN":  17126849000005523700001,
        "CheckpointLSN":  17126849000004341300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500323700000)\/",
        "BackupFinishDate":  "\/Date(1500323700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d886afa4-4dc3-42e3-9c98-6cc867e364cb",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500323700000)\/",
        "Start":  "\/Date(1500323700000)\/",
        "BackupSetId":  "d886afa4-4dc3-42e3-9c98-6cc867e364cb",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126849000005523700001,
        "LastLSN":  17126849000006710400001,
        "CheckpointLSN":  17126849000006427300051,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500324001000)\/",
        "BackupFinishDate":  "\/Date(1500324001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "595ebcc5-8492-4fbf-9766-b059f38995f0",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500324001000)\/",
        "Start":  "\/Date(1500324001000)\/",
        "BackupSetId":  "595ebcc5-8492-4fbf-9766-b059f38995f0",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126849000006710400001,
        "LastLSN":  17126849000007887500001,
        "CheckpointLSN":  17126849000006427300051,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500324300000)\/",
        "BackupFinishDate":  "\/Date(1500324300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e3a3801c-df3a-4043-a797-8aa80b36724d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500324300000)\/",
        "Start":  "\/Date(1500324300000)\/",
        "BackupSetId":  "e3a3801c-df3a-4043-a797-8aa80b36724d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126849000007887500001,
        "LastLSN":  17126849000009079800001,
        "CheckpointLSN":  17126849000008551800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500324600000)\/",
        "BackupFinishDate":  "\/Date(1500324600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1577db61-1e85-450a-a1fe-e57017cb51ef",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500324600000)\/",
        "Start":  "\/Date(1500324600000)\/",
        "BackupSetId":  "1577db61-1e85-450a-a1fe-e57017cb51ef",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126849000009079800001,
        "LastLSN":  17126849000010260700001,
        "CheckpointLSN":  17126849000008551800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500324900000)\/",
        "BackupFinishDate":  "\/Date(1500324900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b406618c-ae24-4989-974a-1ad01497cb19",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500324900000)\/",
        "Start":  "\/Date(1500324900000)\/",
        "BackupSetId":  "b406618c-ae24-4989-974a-1ad01497cb19",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126849000010260700001,
        "LastLSN":  17126849000011476200001,
        "CheckpointLSN":  17126849000010623300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500325200000)\/",
        "BackupFinishDate":  "\/Date(1500325200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0dd6b853-377c-4e97-a7ae-6e2f261b8a6d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500325200000)\/",
        "Start":  "\/Date(1500325200000)\/",
        "BackupSetId":  "0dd6b853-377c-4e97-a7ae-6e2f261b8a6d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126849000011476200001,
        "LastLSN":  17126849000012642500001,
        "CheckpointLSN":  17126849000010623300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500325501000)\/",
        "BackupFinishDate":  "\/Date(1500325501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "150b886a-9921-4d7a-805a-80ad0c7a85d7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500325501000)\/",
        "Start":  "\/Date(1500325501000)\/",
        "BackupSetId":  "150b886a-9921-4d7a-805a-80ad0c7a85d7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126849000012642500001,
        "LastLSN":  17126850000000739600001,
        "CheckpointLSN":  17126849000012700100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500325800000)\/",
        "BackupFinishDate":  "\/Date(1500325800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "890d8f2c-a0d6-4bdd-80bc-811d500f910f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500325800000)\/",
        "Start":  "\/Date(1500325800000)\/",
        "BackupSetId":  "890d8f2c-a0d6-4bdd-80bc-811d500f910f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126850000000739600001,
        "LastLSN":  17126850000001930200001,
        "CheckpointLSN":  17126850000000739600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500326100000)\/",
        "BackupFinishDate":  "\/Date(1500326100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ff160963-4a54-4592-a1a9-29000be31344",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500326100000)\/",
        "Start":  "\/Date(1500326100000)\/",
        "BackupSetId":  "ff160963-4a54-4592-a1a9-29000be31344",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126850000001930200001,
        "LastLSN":  17126850000003133300001,
        "CheckpointLSN":  17126850000002828400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500326400000)\/",
        "BackupFinishDate":  "\/Date(1500326400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c58d23e7-16a0-4240-82de-0724583a5791",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500326400000)\/",
        "Start":  "\/Date(1500326400000)\/",
        "BackupSetId":  "c58d23e7-16a0-4240-82de-0724583a5791",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126850000003133300001,
        "LastLSN":  17126850000004305200001,
        "CheckpointLSN":  17126850000002828400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500326700000)\/",
        "BackupFinishDate":  "\/Date(1500326700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "01871022-45b6-4945-8c65-5220e13dca9e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500326700000)\/",
        "Start":  "\/Date(1500326700000)\/",
        "BackupSetId":  "01871022-45b6-4945-8c65-5220e13dca9e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126850000004305200001,
        "LastLSN":  17126850000005491700001,
        "CheckpointLSN":  17126850000004977600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500327001000)\/",
        "BackupFinishDate":  "\/Date(1500327001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8fde00c7-3298-4a8d-9d66-369d149ed34a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500327001000)\/",
        "Start":  "\/Date(1500327001000)\/",
        "BackupSetId":  "8fde00c7-3298-4a8d-9d66-369d149ed34a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126850000005491700001,
        "LastLSN":  17126850000006512400001,
        "CheckpointLSN":  17126850000004977600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500327300000)\/",
        "BackupFinishDate":  "\/Date(1500327300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "494454d0-6a24-4d9e-827a-d203db649127",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500327300000)\/",
        "Start":  "\/Date(1500327300000)\/",
        "BackupSetId":  "494454d0-6a24-4d9e-827a-d203db649127",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126850000006512400001,
        "LastLSN":  17126850000007738800001,
        "CheckpointLSN":  17126850000007045000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500327600000)\/",
        "BackupFinishDate":  "\/Date(1500327600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4d5d4d27-b0fb-4a17-827c-1703d7204df1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500327600000)\/",
        "Start":  "\/Date(1500327600000)\/",
        "BackupSetId":  "4d5d4d27-b0fb-4a17-827c-1703d7204df1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126850000007738800001,
        "LastLSN":  17126850000008950100001,
        "CheckpointLSN":  17126850000007045000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500327900000)\/",
        "BackupFinishDate":  "\/Date(1500327900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "bcd56797-7262-4af2-8de4-1669d477779d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500327900000)\/",
        "Start":  "\/Date(1500327900000)\/",
        "BackupSetId":  "bcd56797-7262-4af2-8de4-1669d477779d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126850000008950100001,
        "LastLSN":  17126850000010154300001,
        "CheckpointLSN":  17126850000009120600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500328200000)\/",
        "BackupFinishDate":  "\/Date(1500328200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4f695068-8fc0-4fd1-848e-cf418d37399d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500328200000)\/",
        "Start":  "\/Date(1500328200000)\/",
        "BackupSetId":  "4f695068-8fc0-4fd1-848e-cf418d37399d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126850000010154300001,
        "LastLSN":  17126850000011364200001,
        "CheckpointLSN":  17126850000011212800011,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500328501000)\/",
        "BackupFinishDate":  "\/Date(1500328501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1fcef890-2bd1-4386-9723-f77416dbec03",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500328501000)\/",
        "Start":  "\/Date(1500328501000)\/",
        "BackupSetId":  "1fcef890-2bd1-4386-9723-f77416dbec03",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126850000011364200001,
        "LastLSN":  17126850000012543500001,
        "CheckpointLSN":  17126850000011212800011,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500328800000)\/",
        "BackupFinishDate":  "\/Date(1500328800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ac61e18d-a542-4307-85ff-b38580c277b1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500328800000)\/",
        "Start":  "\/Date(1500328800000)\/",
        "BackupSetId":  "ac61e18d-a542-4307-85ff-b38580c277b1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126850000012543500001,
        "LastLSN":  17126851000000649300001,
        "CheckpointLSN":  17126851000000277500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500329100000)\/",
        "BackupFinishDate":  "\/Date(1500329100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "233368ad-ea60-410f-8e59-94a33dfce4ea",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500329100000)\/",
        "Start":  "\/Date(1500329100000)\/",
        "BackupSetId":  "233368ad-ea60-410f-8e59-94a33dfce4ea",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126851000000649300001,
        "LastLSN":  17126851000001829800001,
        "CheckpointLSN":  17126851000000277500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500329400000)\/",
        "BackupFinishDate":  "\/Date(1500329400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "50c6c412-50e5-4d74-8762-175fafc34275",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500329400000)\/",
        "Start":  "\/Date(1500329400000)\/",
        "BackupSetId":  "50c6c412-50e5-4d74-8762-175fafc34275",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126851000001829800001,
        "LastLSN":  17126851000003040000001,
        "CheckpointLSN":  17126851000002352400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500329700000)\/",
        "BackupFinishDate":  "\/Date(1500329701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "66174307-c6e0-489d-a51c-f436da6efbec",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500329701000)\/",
        "Start":  "\/Date(1500329700000)\/",
        "BackupSetId":  "66174307-c6e0-489d-a51c-f436da6efbec",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126851000003040000001,
        "LastLSN":  17126851000004203300001,
        "CheckpointLSN":  17126851000002352400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500330001000)\/",
        "BackupFinishDate":  "\/Date(1500330001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "aab849b4-7c81-46e1-b656-454399fbb455",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500330001000)\/",
        "Start":  "\/Date(1500330001000)\/",
        "BackupSetId":  "aab849b4-7c81-46e1-b656-454399fbb455",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126851000004203300001,
        "LastLSN":  17126851000005393900001,
        "CheckpointLSN":  17126851000004420900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500330300000)\/",
        "BackupFinishDate":  "\/Date(1500330300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "175e729c-6f7b-4191-9ee3-28fdcd38117d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500330300000)\/",
        "Start":  "\/Date(1500330300000)\/",
        "BackupSetId":  "175e729c-6f7b-4191-9ee3-28fdcd38117d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126851000005393900001,
        "LastLSN":  17126851000006598600001,
        "CheckpointLSN":  17126851000006503100024,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500330600000)\/",
        "BackupFinishDate":  "\/Date(1500330600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f8f6fbd6-333b-4968-8537-94e90739c205",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500330600000)\/",
        "Start":  "\/Date(1500330600000)\/",
        "BackupSetId":  "f8f6fbd6-333b-4968-8537-94e90739c205",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126851000006598600001,
        "LastLSN":  17126851000007789800001,
        "CheckpointLSN":  17126851000006503100024,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500330900000)\/",
        "BackupFinishDate":  "\/Date(1500330900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "73b03202-3b87-4885-8f41-1b462555c27d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500330900000)\/",
        "Start":  "\/Date(1500330900000)\/",
        "BackupSetId":  "73b03202-3b87-4885-8f41-1b462555c27d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126851000007789800001,
        "LastLSN":  17126851000008973800001,
        "CheckpointLSN":  17126851000008641500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500331200000)\/",
        "BackupFinishDate":  "\/Date(1500331201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d9e44c8a-04f3-4621-b35a-e588724f6b5a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500331201000)\/",
        "Start":  "\/Date(1500331200000)\/",
        "BackupSetId":  "d9e44c8a-04f3-4621-b35a-e588724f6b5a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126851000008973800001,
        "LastLSN":  17126851000010154200001,
        "CheckpointLSN":  17126851000008641500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500331501000)\/",
        "BackupFinishDate":  "\/Date(1500331501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8d927337-da17-4698-9f34-e50c239de051",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500331501000)\/",
        "Start":  "\/Date(1500331501000)\/",
        "BackupSetId":  "8d927337-da17-4698-9f34-e50c239de051",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126851000010154200001,
        "LastLSN":  17126851000011348400001,
        "CheckpointLSN":  17126851000010711500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500331800000)\/",
        "BackupFinishDate":  "\/Date(1500331800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "de358622-cf2f-457c-bb04-2345008fb794",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500331800000)\/",
        "Start":  "\/Date(1500331800000)\/",
        "BackupSetId":  "de358622-cf2f-457c-bb04-2345008fb794",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126851000011348400001,
        "LastLSN":  17126851000012533800001,
        "CheckpointLSN":  17126851000010711500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500332100000)\/",
        "BackupFinishDate":  "\/Date(1500332100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1469aa5d-171c-4c53-aace-742c946a07b7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500332100000)\/",
        "Start":  "\/Date(1500332100000)\/",
        "BackupSetId":  "1469aa5d-171c-4c53-aace-742c946a07b7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126851000012533800001,
        "LastLSN":  17126852000000641200001,
        "CheckpointLSN":  17126851000012784400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500332401000)\/",
        "BackupFinishDate":  "\/Date(1500332402000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "af89f882-aad0-4eb3-b499-c07d73123c6a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500332402000)\/",
        "Start":  "\/Date(1500332401000)\/",
        "BackupSetId":  "af89f882-aad0-4eb3-b499-c07d73123c6a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126852000000641200001,
        "LastLSN":  17126857000002747100001,
        "CheckpointLSN":  17126857000001983800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500332701000)\/",
        "BackupFinishDate":  "\/Date(1500332702000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c2f4e6bd-ad6c-409a-979e-e0a52489ea0c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500332702000)\/",
        "Start":  "\/Date(1500332701000)\/",
        "BackupSetId":  "c2f4e6bd-ad6c-409a-979e-e0a52489ea0c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126857000002747100001,
        "LastLSN":  17126857000003926300001,
        "CheckpointLSN":  17126857000001983800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500333000000)\/",
        "BackupFinishDate":  "\/Date(1500333000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e15c039a-1f59-4f78-bde5-83fc16a8af6d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500333000000)\/",
        "Start":  "\/Date(1500333000000)\/",
        "BackupSetId":  "e15c039a-1f59-4f78-bde5-83fc16a8af6d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126857000003926300001,
        "LastLSN":  17126857000005115300001,
        "CheckpointLSN":  17126857000004067200066,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500333300000)\/",
        "BackupFinishDate":  "\/Date(1500333300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0598ddf9-dd4f-4539-94d4-c9bdeb4cd93a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500333300000)\/",
        "Start":  "\/Date(1500333300000)\/",
        "BackupSetId":  "0598ddf9-dd4f-4539-94d4-c9bdeb4cd93a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126857000005115300001,
        "LastLSN":  17126857000006319800001,
        "CheckpointLSN":  17126857000006215500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500333600000)\/",
        "BackupFinishDate":  "\/Date(1500333600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "51e0a0c5-0d35-42bd-acb8-6a943d58b0bc",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500333600000)\/",
        "Start":  "\/Date(1500333600000)\/",
        "BackupSetId":  "51e0a0c5-0d35-42bd-acb8-6a943d58b0bc",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126857000006319800001,
        "LastLSN":  17126857000007488300001,
        "CheckpointLSN":  17126857000006215500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500333900000)\/",
        "BackupFinishDate":  "\/Date(1500333901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "bc34d0cc-c331-4ecc-88af-8c7c5b979670",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500333901000)\/",
        "Start":  "\/Date(1500333900000)\/",
        "BackupSetId":  "bc34d0cc-c331-4ecc-88af-8c7c5b979670",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126857000007488300001,
        "LastLSN":  17126857000008671500001,
        "CheckpointLSN":  17126857000008304100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500334200000)\/",
        "BackupFinishDate":  "\/Date(1500334200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f2ea5b8e-9c98-47e2-ba07-93c426464cd4",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500334200000)\/",
        "Start":  "\/Date(1500334200000)\/",
        "BackupSetId":  "f2ea5b8e-9c98-47e2-ba07-93c426464cd4",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126857000008671500001,
        "LastLSN":  17126858000008379100001,
        "CheckpointLSN":  17126858000005205500004,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500334500000)\/",
        "BackupFinishDate":  "\/Date(1500334501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "58d046ce-5c9f-42f6-a77c-c7e4b8ae24e7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500334501000)\/",
        "Start":  "\/Date(1500334500000)\/",
        "BackupSetId":  "58d046ce-5c9f-42f6-a77c-c7e4b8ae24e7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126858000008379100001,
        "LastLSN":  17126859000010072600001,
        "CheckpointLSN":  17126859000007895800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500334800000)\/",
        "BackupFinishDate":  "\/Date(1500334801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0c832de2-04f9-4046-a7ca-c358009fc03f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500334801000)\/",
        "Start":  "\/Date(1500334800000)\/",
        "BackupSetId":  "0c832de2-04f9-4046-a7ca-c358009fc03f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126859000010072600001,
        "LastLSN":  17126860000012621900001,
        "CheckpointLSN":  17126860000010735200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500335100000)\/",
        "BackupFinishDate":  "\/Date(1500335101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "98cb76c1-c545-4637-b497-d8cce8e7eb7e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500335101000)\/",
        "Start":  "\/Date(1500335100000)\/",
        "BackupSetId":  "98cb76c1-c545-4637-b497-d8cce8e7eb7e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126860000012621900001,
        "LastLSN":  17126862000003493200001,
        "CheckpointLSN":  17126862000002389800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500335401000)\/",
        "BackupFinishDate":  "\/Date(1500335401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3c9a3df0-c605-461a-84ff-daabdaa1a17c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500335401000)\/",
        "Start":  "\/Date(1500335401000)\/",
        "BackupSetId":  "3c9a3df0-c605-461a-84ff-daabdaa1a17c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126862000003493200001,
        "LastLSN":  17126862000011152200001,
        "CheckpointLSN":  17126862000009251500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500335700000)\/",
        "BackupFinishDate":  "\/Date(1500335700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9c4d7227-db94-4dbf-b826-ee62412cfad9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500335700000)\/",
        "Start":  "\/Date(1500335700000)\/",
        "BackupSetId":  "9c4d7227-db94-4dbf-b826-ee62412cfad9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126862000011152200001,
        "LastLSN":  17126862000012370500001,
        "CheckpointLSN":  17126862000011349100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500336000000)\/",
        "BackupFinishDate":  "\/Date(1500336000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f1fb9ebe-e900-4901-9b58-a299eb10b9a1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500336000000)\/",
        "Start":  "\/Date(1500336000000)\/",
        "BackupSetId":  "f1fb9ebe-e900-4901-9b58-a299eb10b9a1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126862000012370500001,
        "LastLSN":  17126863000000509600001,
        "CheckpointLSN":  17126863000000330700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500336300000)\/",
        "BackupFinishDate":  "\/Date(1500336300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8ccfc393-c788-49d2-b8cc-243aff04d1e4",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500336300000)\/",
        "Start":  "\/Date(1500336300000)\/",
        "BackupSetId":  "8ccfc393-c788-49d2-b8cc-243aff04d1e4",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126863000000509600001,
        "LastLSN":  17126863000001691600001,
        "CheckpointLSN":  17126863000000330700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500336601000)\/",
        "BackupFinishDate":  "\/Date(1500336601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "29c4f820-2527-4a73-8d71-909f5c5402c4",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500336601000)\/",
        "Start":  "\/Date(1500336601000)\/",
        "BackupSetId":  "29c4f820-2527-4a73-8d71-909f5c5402c4",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126863000001691600001,
        "LastLSN":  17126863000002906300001,
        "CheckpointLSN":  17126863000002422500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500336900000)\/",
        "BackupFinishDate":  "\/Date(1500336900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e652a672-b83b-4f49-990e-89c15c239932",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500336900000)\/",
        "Start":  "\/Date(1500336900000)\/",
        "BackupSetId":  "e652a672-b83b-4f49-990e-89c15c239932",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126863000002906300001,
        "LastLSN":  17126863000004075700001,
        "CheckpointLSN":  17126863000002422500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500337200000)\/",
        "BackupFinishDate":  "\/Date(1500337200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ef67e2da-9ca6-40ba-8d7c-aa88625cedbe",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500337200000)\/",
        "Start":  "\/Date(1500337200000)\/",
        "BackupSetId":  "ef67e2da-9ca6-40ba-8d7c-aa88625cedbe",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126863000004075700001,
        "LastLSN":  17126863000005292900001,
        "CheckpointLSN":  17126863000004492100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500337500000)\/",
        "BackupFinishDate":  "\/Date(1500337500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3be33864-6ec3-498a-b8b3-28dc4adc91e6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500337500000)\/",
        "Start":  "\/Date(1500337500000)\/",
        "BackupSetId":  "3be33864-6ec3-498a-b8b3-28dc4adc91e6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126863000005292900001,
        "LastLSN":  17126863000006480700001,
        "CheckpointLSN":  17126863000004492100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500337801000)\/",
        "BackupFinishDate":  "\/Date(1500337801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2a149c14-de99-49be-8a7c-4996688956f2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500337801000)\/",
        "Start":  "\/Date(1500337801000)\/",
        "BackupSetId":  "2a149c14-de99-49be-8a7c-4996688956f2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126863000006480700001,
        "LastLSN":  17126863000007698300001,
        "CheckpointLSN":  17126863000006573300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500338101000)\/",
        "BackupFinishDate":  "\/Date(1500338101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "eca4c3ba-d7c7-430b-8adb-42cd02464719",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500338101000)\/",
        "Start":  "\/Date(1500338101000)\/",
        "BackupSetId":  "eca4c3ba-d7c7-430b-8adb-42cd02464719",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126863000007698300001,
        "LastLSN":  17126863000008889900001,
        "CheckpointLSN":  17126863000008723500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500338400000)\/",
        "BackupFinishDate":  "\/Date(1500338400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7087f6ee-be57-4557-87ed-9342ff781c6c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500338400000)\/",
        "Start":  "\/Date(1500338400000)\/",
        "BackupSetId":  "7087f6ee-be57-4557-87ed-9342ff781c6c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126863000008889900001,
        "LastLSN":  17126863000010077100001,
        "CheckpointLSN":  17126863000008723500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500338700000)\/",
        "BackupFinishDate":  "\/Date(1500338700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d4a6ab8c-af48-46d3-a36c-9ec9a4dc2632",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500338700000)\/",
        "Start":  "\/Date(1500338700000)\/",
        "BackupSetId":  "d4a6ab8c-af48-46d3-a36c-9ec9a4dc2632",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126863000010077100001,
        "LastLSN":  17126863000011267100001,
        "CheckpointLSN":  17126863000010797700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500339000000)\/",
        "BackupFinishDate":  "\/Date(1500339000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b2cbac93-0cd3-43c5-a4fa-6bbeadf6a5c2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500339000000)\/",
        "Start":  "\/Date(1500339000000)\/",
        "BackupSetId":  "b2cbac93-0cd3-43c5-a4fa-6bbeadf6a5c2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126863000011267100001,
        "LastLSN":  17126863000012544900001,
        "CheckpointLSN":  17126863000010797700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500339300000)\/",
        "BackupFinishDate":  "\/Date(1500339300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a44c60cc-f541-423c-9eaf-7a3dd14bb93e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500339300000)\/",
        "Start":  "\/Date(1500339300000)\/",
        "BackupSetId":  "a44c60cc-f541-423c-9eaf-7a3dd14bb93e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126863000012544900001,
        "LastLSN":  17126864000000655200001,
        "CheckpointLSN":  17126863000012884500028,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500339601000)\/",
        "BackupFinishDate":  "\/Date(1500339601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1ad57804-d2ec-47b3-a2f9-c4499a955d23",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500339601000)\/",
        "Start":  "\/Date(1500339601000)\/",
        "BackupSetId":  "1ad57804-d2ec-47b3-a2f9-c4499a955d23",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126864000000655200001,
        "LastLSN":  17126864000001846500001,
        "CheckpointLSN":  17126864000000655200009,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500339900000)\/",
        "BackupFinishDate":  "\/Date(1500339900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a24fe4c3-6456-4e5b-ae58-248dea915e39",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500339900000)\/",
        "Start":  "\/Date(1500339900000)\/",
        "BackupSetId":  "a24fe4c3-6456-4e5b-ae58-248dea915e39",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126864000001846500001,
        "LastLSN":  17126864000003049600001,
        "CheckpointLSN":  17126864000002726800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500340200000)\/",
        "BackupFinishDate":  "\/Date(1500340200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e30b77a2-70fe-4d88-8b1b-0dbf3ae4e475",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500340200000)\/",
        "Start":  "\/Date(1500340200000)\/",
        "BackupSetId":  "e30b77a2-70fe-4d88-8b1b-0dbf3ae4e475",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126864000003049600001,
        "LastLSN":  17126864000004218700001,
        "CheckpointLSN":  17126864000002726800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500340500000)\/",
        "BackupFinishDate":  "\/Date(1500340500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fc24e571-be90-4263-838f-85bf972b8d22",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500340500000)\/",
        "Start":  "\/Date(1500340500000)\/",
        "BackupSetId":  "fc24e571-be90-4263-838f-85bf972b8d22",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126864000004218700001,
        "LastLSN":  17126864000005422300001,
        "CheckpointLSN":  17126864000004803300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500340800000)\/",
        "BackupFinishDate":  "\/Date(1500340800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9b22102e-1350-435c-9f74-1fc19d368df9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500340800000)\/",
        "Start":  "\/Date(1500340800000)\/",
        "BackupSetId":  "9b22102e-1350-435c-9f74-1fc19d368df9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126864000005422300001,
        "LastLSN":  17126864000006589100001,
        "CheckpointLSN":  17126864000004803300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500341100000)\/",
        "BackupFinishDate":  "\/Date(1500341100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "96a35891-7e94-4ff2-8af7-8e885c9f261e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500341100000)\/",
        "Start":  "\/Date(1500341100000)\/",
        "BackupSetId":  "96a35891-7e94-4ff2-8af7-8e885c9f261e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126864000006589100001,
        "LastLSN":  17126864000007781100001,
        "CheckpointLSN":  17126864000006887400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500341400000)\/",
        "BackupFinishDate":  "\/Date(1500341400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b34572b0-73d3-4124-afbb-5322ed17bd3a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500341400000)\/",
        "Start":  "\/Date(1500341400000)\/",
        "BackupSetId":  "b34572b0-73d3-4124-afbb-5322ed17bd3a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126864000007781100001,
        "LastLSN":  17126864000008973500001,
        "CheckpointLSN":  17126864000006887400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500341700000)\/",
        "BackupFinishDate":  "\/Date(1500341700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e449eb09-2cab-4f1e-b6d8-9c6322870bab",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500341700000)\/",
        "Start":  "\/Date(1500341700000)\/",
        "BackupSetId":  "e449eb09-2cab-4f1e-b6d8-9c6322870bab",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126864000008973500001,
        "LastLSN":  17126864000010200800001,
        "CheckpointLSN":  17126864000009050500020,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500342000000)\/",
        "BackupFinishDate":  "\/Date(1500342000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "36105646-8ba9-4b82-becd-8fcc1101ec8a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500342000000)\/",
        "Start":  "\/Date(1500342000000)\/",
        "BackupSetId":  "36105646-8ba9-4b82-becd-8fcc1101ec8a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126864000010200800001,
        "LastLSN":  17126864000011430900001,
        "CheckpointLSN":  17126864000011227000005,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500342301000)\/",
        "BackupFinishDate":  "\/Date(1500342301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6ba1247f-8e10-46cd-957f-ebb480a15fc3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500342301000)\/",
        "Start":  "\/Date(1500342301000)\/",
        "BackupSetId":  "6ba1247f-8e10-46cd-957f-ebb480a15fc3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126864000011430900001,
        "LastLSN":  17126864000012614000001,
        "CheckpointLSN":  17126864000011227000005,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500342600000)\/",
        "BackupFinishDate":  "\/Date(1500342600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "41f089c8-fedf-4e7d-baef-e1e1a58726f9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500342600000)\/",
        "Start":  "\/Date(1500342600000)\/",
        "BackupSetId":  "41f089c8-fedf-4e7d-baef-e1e1a58726f9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126864000012614000001,
        "LastLSN":  17126867000000713400001,
        "CheckpointLSN":  17126867000000098800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500342900000)\/",
        "BackupFinishDate":  "\/Date(1500342900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "79f62d1f-9cd4-4183-a3dd-8254c5214d78",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500342900000)\/",
        "Start":  "\/Date(1500342900000)\/",
        "BackupSetId":  "79f62d1f-9cd4-4183-a3dd-8254c5214d78",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126867000000713400001,
        "LastLSN":  17126867000001889200001,
        "CheckpointLSN":  17126867000000098800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500343201000)\/",
        "BackupFinishDate":  "\/Date(1500343201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b943443a-42a9-4937-9912-99245a310664",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500343201000)\/",
        "Start":  "\/Date(1500343201000)\/",
        "BackupSetId":  "b943443a-42a9-4937-9912-99245a310664",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126867000001889200001,
        "LastLSN":  17126867000003104000001,
        "CheckpointLSN":  17126867000002188300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500343500000)\/",
        "BackupFinishDate":  "\/Date(1500343501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c3aa0dd8-8aae-4eb1-af0c-94e39c255562",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500343501000)\/",
        "Start":  "\/Date(1500343500000)\/",
        "BackupSetId":  "c3aa0dd8-8aae-4eb1-af0c-94e39c255562",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126867000003104000001,
        "LastLSN":  17126867000004282600001,
        "CheckpointLSN":  17126867000002188300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500343800000)\/",
        "BackupFinishDate":  "\/Date(1500343800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7a13c982-895b-4401-b26c-5dcbfdea3c1d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500343800000)\/",
        "Start":  "\/Date(1500343800000)\/",
        "BackupSetId":  "7a13c982-895b-4401-b26c-5dcbfdea3c1d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126867000004282600001,
        "LastLSN":  17126867000005502800001,
        "CheckpointLSN":  17126867000004350600024,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500344100000)\/",
        "BackupFinishDate":  "\/Date(1500344100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "49a58acc-e8ea-432d-acce-b847e8b16158",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500344100000)\/",
        "Start":  "\/Date(1500344100000)\/",
        "BackupSetId":  "49a58acc-e8ea-432d-acce-b847e8b16158",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126867000005502800001,
        "LastLSN":  17126867000006690100001,
        "CheckpointLSN":  17126867000006454800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500344400000)\/",
        "BackupFinishDate":  "\/Date(1500344400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5de40e0d-bd3c-49e7-908e-ec56a494ab23",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500344400000)\/",
        "Start":  "\/Date(1500344400000)\/",
        "BackupSetId":  "5de40e0d-bd3c-49e7-908e-ec56a494ab23",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126867000006690100001,
        "LastLSN":  17126867000007885200001,
        "CheckpointLSN":  17126867000006454800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500344700000)\/",
        "BackupFinishDate":  "\/Date(1500344700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "89629b9d-6f2c-4011-a293-ac3f2cefb6db",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500344700000)\/",
        "Start":  "\/Date(1500344700000)\/",
        "BackupSetId":  "89629b9d-6f2c-4011-a293-ac3f2cefb6db",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126867000007885200001,
        "LastLSN":  17126867000009084700001,
        "CheckpointLSN":  17126867000008547000021,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500345001000)\/",
        "BackupFinishDate":  "\/Date(1500345001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "bccb7d12-c500-44cc-b12c-25194548c61c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500345001000)\/",
        "Start":  "\/Date(1500345001000)\/",
        "BackupSetId":  "bccb7d12-c500-44cc-b12c-25194548c61c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126867000009084700001,
        "LastLSN":  17126867000010273700001,
        "CheckpointLSN":  17126867000008547000021,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500345300000)\/",
        "BackupFinishDate":  "\/Date(1500345300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e184a9a1-dbab-4aed-99e7-45cae7ebed13",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500345300000)\/",
        "Start":  "\/Date(1500345300000)\/",
        "BackupSetId":  "e184a9a1-dbab-4aed-99e7-45cae7ebed13",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126867000010273700001,
        "LastLSN":  17126867000011463700001,
        "CheckpointLSN":  17126867000010691600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500345600000)\/",
        "BackupFinishDate":  "\/Date(1500345600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d99cbd0b-58c0-41db-9e4a-010016e85a9a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500345600000)\/",
        "Start":  "\/Date(1500345600000)\/",
        "BackupSetId":  "d99cbd0b-58c0-41db-9e4a-010016e85a9a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126867000011463700001,
        "LastLSN":  17126867000012644400001,
        "CheckpointLSN":  17126867000010691600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500345900000)\/",
        "BackupFinishDate":  "\/Date(1500345900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "35fe03ee-79d2-4218-aebc-bf98d15aef3d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500345900000)\/",
        "Start":  "\/Date(1500345900000)\/",
        "BackupSetId":  "35fe03ee-79d2-4218-aebc-bf98d15aef3d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126867000012644400001,
        "LastLSN":  17126868000000726300001,
        "CheckpointLSN":  17126867000012775000040,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500346200000)\/",
        "BackupFinishDate":  "\/Date(1500346201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3678a4dd-701d-4b11-ab67-383a270b2812",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500346201000)\/",
        "Start":  "\/Date(1500346200000)\/",
        "BackupSetId":  "3678a4dd-701d-4b11-ab67-383a270b2812",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126868000000726300001,
        "LastLSN":  17126868000002023300001,
        "CheckpointLSN":  17126868000000726300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500346500000)\/",
        "BackupFinishDate":  "\/Date(1500346500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "19afde50-c33e-4dd3-aacc-bfc0628e2d7f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500346500000)\/",
        "Start":  "\/Date(1500346500000)\/",
        "BackupSetId":  "19afde50-c33e-4dd3-aacc-bfc0628e2d7f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126868000002023300001,
        "LastLSN":  17126868000003242200001,
        "CheckpointLSN":  17126868000002794600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500346800000)\/",
        "BackupFinishDate":  "\/Date(1500346800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4e530977-6466-401c-9e7a-fc0f47dc4315",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500346800000)\/",
        "Start":  "\/Date(1500346800000)\/",
        "BackupSetId":  "4e530977-6466-401c-9e7a-fc0f47dc4315",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126868000003242200001,
        "LastLSN":  17126868000004413900001,
        "CheckpointLSN":  17126868000002794600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500347100000)\/",
        "BackupFinishDate":  "\/Date(1500347100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b3b88b7f-ff4d-4f98-8a2f-88adefcff1cf",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500347100000)\/",
        "Start":  "\/Date(1500347100000)\/",
        "BackupSetId":  "b3b88b7f-ff4d-4f98-8a2f-88adefcff1cf",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126868000004413900001,
        "LastLSN":  17126868000005617400001,
        "CheckpointLSN":  17126868000004873300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500347401000)\/",
        "BackupFinishDate":  "\/Date(1500347401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6b3ebbfc-f6a0-41c7-9db6-558b1fc0d5e2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500347401000)\/",
        "Start":  "\/Date(1500347401000)\/",
        "BackupSetId":  "6b3ebbfc-f6a0-41c7-9db6-558b1fc0d5e2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126868000005617400001,
        "LastLSN":  17126868000006786600001,
        "CheckpointLSN":  17126868000004873300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500347700000)\/",
        "BackupFinishDate":  "\/Date(1500347700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "47784e33-7b71-4059-856b-fc432fc72c29",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500347700000)\/",
        "Start":  "\/Date(1500347700000)\/",
        "BackupSetId":  "47784e33-7b71-4059-856b-fc432fc72c29",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126868000006786600001,
        "LastLSN":  17126868000007988200001,
        "CheckpointLSN":  17126868000006955900010,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500348000000)\/",
        "BackupFinishDate":  "\/Date(1500348000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "860b3d75-dfd3-4fe1-af80-aea949d455b0",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500348000000)\/",
        "Start":  "\/Date(1500348000000)\/",
        "BackupSetId":  "860b3d75-dfd3-4fe1-af80-aea949d455b0",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126868000007988200001,
        "LastLSN":  17126868000009173200001,
        "CheckpointLSN":  17126868000009048300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500348300000)\/",
        "BackupFinishDate":  "\/Date(1500348300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3c31755b-b5db-42a0-9a59-04edbcd53319",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500348300000)\/",
        "Start":  "\/Date(1500348300000)\/",
        "BackupSetId":  "3c31755b-b5db-42a0-9a59-04edbcd53319",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126868000009173200001,
        "LastLSN":  17126868000010337800001,
        "CheckpointLSN":  17126868000009048300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500348600000)\/",
        "BackupFinishDate":  "\/Date(1500348600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3e44e943-afab-4a2b-ad12-10c6783f0cc9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500348600000)\/",
        "Start":  "\/Date(1500348600000)\/",
        "BackupSetId":  "3e44e943-afab-4a2b-ad12-10c6783f0cc9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126868000010337800001,
        "LastLSN":  17126868000011550300001,
        "CheckpointLSN":  17126868000011117400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500348900000)\/",
        "BackupFinishDate":  "\/Date(1500348900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f8868efd-a0f7-4b48-8b16-60de3b2804e7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500348900000)\/",
        "Start":  "\/Date(1500348900000)\/",
        "BackupSetId":  "f8868efd-a0f7-4b48-8b16-60de3b2804e7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126868000011550300001,
        "LastLSN":  17126868000012758900001,
        "CheckpointLSN":  17126868000011117400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500349200000)\/",
        "BackupFinishDate":  "\/Date(1500349200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "bf503bf6-ad63-47a6-8b42-f1a095321afd",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500349200000)\/",
        "Start":  "\/Date(1500349200000)\/",
        "BackupSetId":  "bf503bf6-ad63-47a6-8b42-f1a095321afd",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126868000012758900001,
        "LastLSN":  17126869000000879700001,
        "CheckpointLSN":  17126869000000082400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500349500000)\/",
        "BackupFinishDate":  "\/Date(1500349501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "cf5ae0df-0d4c-44eb-a539-37d94e3d2dfa",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500349501000)\/",
        "Start":  "\/Date(1500349500000)\/",
        "BackupSetId":  "cf5ae0df-0d4c-44eb-a539-37d94e3d2dfa",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126869000000879700001,
        "LastLSN":  17126869000002062500001,
        "CheckpointLSN":  17126869000000082400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500349800000)\/",
        "BackupFinishDate":  "\/Date(1500349800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "85fb8cc9-67bf-4e02-93bd-f3145d2791a2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500349800000)\/",
        "Start":  "\/Date(1500349800000)\/",
        "BackupSetId":  "85fb8cc9-67bf-4e02-93bd-f3145d2791a2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126869000002062500001,
        "LastLSN":  17126869000003371900001,
        "CheckpointLSN":  17126869000002189000047,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500350100000)\/",
        "BackupFinishDate":  "\/Date(1500350100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1eaa3707-5190-4a6b-a4a0-852c729f5cdb",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500350100000)\/",
        "Start":  "\/Date(1500350100000)\/",
        "BackupSetId":  "1eaa3707-5190-4a6b-a4a0-852c729f5cdb",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126869000003371900001,
        "LastLSN":  17126869000004574900001,
        "CheckpointLSN":  17126869000004337800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500350400000)\/",
        "BackupFinishDate":  "\/Date(1500350401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a255ebbd-09ec-4806-818e-b84bbc0c18b7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500350401000)\/",
        "Start":  "\/Date(1500350400000)\/",
        "BackupSetId":  "a255ebbd-09ec-4806-818e-b84bbc0c18b7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126869000004574900001,
        "LastLSN":  17126869000005773100001,
        "CheckpointLSN":  17126869000004337800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500350701000)\/",
        "BackupFinishDate":  "\/Date(1500350701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e1c1e30f-52a8-4877-a1ff-60816700c5fc",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500350701000)\/",
        "Start":  "\/Date(1500350701000)\/",
        "BackupSetId":  "e1c1e30f-52a8-4877-a1ff-60816700c5fc",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126869000005773100001,
        "LastLSN":  17126869000006971800001,
        "CheckpointLSN":  17126869000006420400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500351000000)\/",
        "BackupFinishDate":  "\/Date(1500351000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b9560ac4-2134-4a00-9eca-dd778767f7af",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500351000000)\/",
        "Start":  "\/Date(1500351000000)\/",
        "BackupSetId":  "b9560ac4-2134-4a00-9eca-dd778767f7af",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126869000006971800001,
        "LastLSN":  17126869000008166900001,
        "CheckpointLSN":  17126869000006420400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500351300000)\/",
        "BackupFinishDate":  "\/Date(1500351300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "342e88ba-1adc-441e-bb96-2f59e38ce662",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500351300000)\/",
        "Start":  "\/Date(1500351300000)\/",
        "BackupSetId":  "342e88ba-1adc-441e-bb96-2f59e38ce662",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126869000008166900001,
        "LastLSN":  17126869000009356400001,
        "CheckpointLSN":  17126869000008509600040,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500351600000)\/",
        "BackupFinishDate":  "\/Date(1500351600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "83468b03-134f-42f8-8f6a-285265889405",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500351600000)\/",
        "Start":  "\/Date(1500351600000)\/",
        "BackupSetId":  "83468b03-134f-42f8-8f6a-285265889405",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126869000009356400001,
        "LastLSN":  17126869000010547700001,
        "CheckpointLSN":  17126869000008509600040,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500351900000)\/",
        "BackupFinishDate":  "\/Date(1500351901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "de9f7e2c-f476-4581-89dd-35ca65e2f0d6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500351901000)\/",
        "Start":  "\/Date(1500351900000)\/",
        "BackupSetId":  "de9f7e2c-f476-4581-89dd-35ca65e2f0d6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126869000010547700001,
        "LastLSN":  17126869000011751200001,
        "CheckpointLSN":  17126869000010662900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500352200000)\/",
        "BackupFinishDate":  "\/Date(1500352200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "163d3d1b-759a-4089-b4c5-7cfc723d62cf",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500352200000)\/",
        "Start":  "\/Date(1500352200000)\/",
        "BackupSetId":  "163d3d1b-759a-4089-b4c5-7cfc723d62cf",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126869000011751200001,
        "LastLSN":  17126869000012968700001,
        "CheckpointLSN":  17126869000012816200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500352500000)\/",
        "BackupFinishDate":  "\/Date(1500352500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d48b036d-1a8f-40d5-a3e1-0b2eea786fa9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500352500000)\/",
        "Start":  "\/Date(1500352500000)\/",
        "BackupSetId":  "d48b036d-1a8f-40d5-a3e1-0b2eea786fa9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126869000012968700001,
        "LastLSN":  17126870000001031900001,
        "CheckpointLSN":  17126869000012816200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500352800000)\/",
        "BackupFinishDate":  "\/Date(1500352800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "cfabf3a4-b3e2-47d4-89cb-471894040193",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500352800000)\/",
        "Start":  "\/Date(1500352800000)\/",
        "BackupSetId":  "cfabf3a4-b3e2-47d4-89cb-471894040193",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126870000001031900001,
        "LastLSN":  17126870000002234600001,
        "CheckpointLSN":  17126870000001032000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500353100000)\/",
        "BackupFinishDate":  "\/Date(1500353100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5cb1d297-4d00-4eda-956a-50848b45c03d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500353100000)\/",
        "Start":  "\/Date(1500353100000)\/",
        "BackupSetId":  "5cb1d297-4d00-4eda-956a-50848b45c03d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126870000002234600001,
        "LastLSN":  17126870000003421400001,
        "CheckpointLSN":  17126870000003105800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500353400000)\/",
        "BackupFinishDate":  "\/Date(1500353400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4b46e1a2-34bc-41fd-ab97-4fdfc3f64cf7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500353400000)\/",
        "Start":  "\/Date(1500353400000)\/",
        "BackupSetId":  "4b46e1a2-34bc-41fd-ab97-4fdfc3f64cf7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126870000003421400001,
        "LastLSN":  17126870000004698000001,
        "CheckpointLSN":  17126870000003105800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500353700000)\/",
        "BackupFinishDate":  "\/Date(1500353700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fb6d2b23-588c-4cea-bf1e-df8575114efc",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500353700000)\/",
        "Start":  "\/Date(1500353700000)\/",
        "BackupSetId":  "fb6d2b23-588c-4cea-bf1e-df8575114efc",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126870000004698000001,
        "LastLSN":  17126870000005917600001,
        "CheckpointLSN":  17126870000005176800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500354000000)\/",
        "BackupFinishDate":  "\/Date(1500354000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c5552d0a-cf67-41fa-9c26-549bdb46a5db",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500354000000)\/",
        "Start":  "\/Date(1500354000000)\/",
        "BackupSetId":  "c5552d0a-cf67-41fa-9c26-549bdb46a5db",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126870000005917600001,
        "LastLSN":  17126870000008737700001,
        "CheckpointLSN":  17126870000007248700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500354300000)\/",
        "BackupFinishDate":  "\/Date(1500354301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7905f569-c829-4a1b-bb05-b5bb88d2444c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500354301000)\/",
        "Start":  "\/Date(1500354300000)\/",
        "BackupSetId":  "7905f569-c829-4a1b-bb05-b5bb88d2444c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126870000008737700001,
        "LastLSN":  17126870000009951200001,
        "CheckpointLSN":  17126870000009344300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500354600000)\/",
        "BackupFinishDate":  "\/Date(1500354600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6970a96e-55df-4eb6-9f06-ffd99f04f658",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500354600000)\/",
        "Start":  "\/Date(1500354600000)\/",
        "BackupSetId":  "6970a96e-55df-4eb6-9f06-ffd99f04f658",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126870000009951200001,
        "LastLSN":  17126870000011117600001,
        "CheckpointLSN":  17126870000009344300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500354900000)\/",
        "BackupFinishDate":  "\/Date(1500354900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7da6973d-e1fc-438b-a714-7a5e0619d39a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500354900000)\/",
        "Start":  "\/Date(1500354900000)\/",
        "BackupSetId":  "7da6973d-e1fc-438b-a714-7a5e0619d39a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126870000011117600001,
        "LastLSN":  17126870000012318700001,
        "CheckpointLSN":  17126870000011440200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500355200000)\/",
        "BackupFinishDate":  "\/Date(1500355200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2f014732-38b2-4310-a4d7-0fb1539f4c10",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500355200000)\/",
        "Start":  "\/Date(1500355200000)\/",
        "BackupSetId":  "2f014732-38b2-4310-a4d7-0fb1539f4c10",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126870000012318700001,
        "LastLSN":  17126871000000378200001,
        "CheckpointLSN":  17126870000011440200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500355500000)\/",
        "BackupFinishDate":  "\/Date(1500355500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "61eb6e2e-6097-4464-b875-0944a96b30ed",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500355500000)\/",
        "Start":  "\/Date(1500355500000)\/",
        "BackupSetId":  "61eb6e2e-6097-4464-b875-0944a96b30ed",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126871000000378200001,
        "LastLSN":  17126871000001565900001,
        "CheckpointLSN":  17126871000000378200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500355800000)\/",
        "BackupFinishDate":  "\/Date(1500355800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d892fcb1-6ac9-460a-bf62-c678dcc09d88",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500355800000)\/",
        "Start":  "\/Date(1500355800000)\/",
        "BackupSetId":  "d892fcb1-6ac9-460a-bf62-c678dcc09d88",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126871000001565900001,
        "LastLSN":  17126871000002785100001,
        "CheckpointLSN":  17126871000002448200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500356100000)\/",
        "BackupFinishDate":  "\/Date(1500356100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7891ce32-3ec6-4cfe-8445-7ecfc9258c51",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500356100000)\/",
        "Start":  "\/Date(1500356100000)\/",
        "BackupSetId":  "7891ce32-3ec6-4cfe-8445-7ecfc9258c51",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126871000002785100001,
        "LastLSN":  17126871000004028400001,
        "CheckpointLSN":  17126871000002448200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500356400000)\/",
        "BackupFinishDate":  "\/Date(1500356400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "81913f11-47a9-4d43-8c85-6b9c230033be",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500356400000)\/",
        "Start":  "\/Date(1500356400000)\/",
        "BackupSetId":  "81913f11-47a9-4d43-8c85-6b9c230033be",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126871000004028400001,
        "LastLSN":  17126871000005205200001,
        "CheckpointLSN":  17126871000004517700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500356700000)\/",
        "BackupFinishDate":  "\/Date(1500356700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b7a68861-6714-42ef-9f26-75dbc0dc14db",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500356700000)\/",
        "Start":  "\/Date(1500356700000)\/",
        "BackupSetId":  "b7a68861-6714-42ef-9f26-75dbc0dc14db",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126871000005205200001,
        "LastLSN":  17126871000009851900001,
        "CheckpointLSN":  17126871000009332000013,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500357000000)\/",
        "BackupFinishDate":  "\/Date(1500357000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "36b71b5b-b2d6-4258-b464-586342d3a817",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500357000000)\/",
        "Start":  "\/Date(1500357000000)\/",
        "BackupSetId":  "36b71b5b-b2d6-4258-b464-586342d3a817",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126871000009851900001,
        "LastLSN":  17126871000011000900001,
        "CheckpointLSN":  17126871000009332000013,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500357300000)\/",
        "BackupFinishDate":  "\/Date(1500357300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "93bfd71b-f199-432b-8286-f0c9f524d4b7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500357300000)\/",
        "Start":  "\/Date(1500357300000)\/",
        "BackupSetId":  "93bfd71b-f199-432b-8286-f0c9f524d4b7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126871000011000900001,
        "LastLSN":  17126871000012156700001,
        "CheckpointLSN":  17126871000011428900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500357600000)\/",
        "BackupFinishDate":  "\/Date(1500357600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0afb9ee4-a4c9-48ef-abb8-2ddb631653d6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500357600000)\/",
        "Start":  "\/Date(1500357600000)\/",
        "BackupSetId":  "0afb9ee4-a4c9-48ef-abb8-2ddb631653d6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126871000012156700001,
        "LastLSN":  17126872000000196900001,
        "CheckpointLSN":  17126871000011428900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500357900000)\/",
        "BackupFinishDate":  "\/Date(1500357901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2c6f565a-43a8-4bb1-aae6-13a2c1b9e9ba",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500357901000)\/",
        "Start":  "\/Date(1500357900000)\/",
        "BackupSetId":  "2c6f565a-43a8-4bb1-aae6-13a2c1b9e9ba",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126872000000196900001,
        "LastLSN":  17126872000001348300001,
        "CheckpointLSN":  17126872000000196900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500358200000)\/",
        "BackupFinishDate":  "\/Date(1500358200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3c020dfe-48b3-4ad9-93e3-036fdb220cd5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500358200000)\/",
        "Start":  "\/Date(1500358200000)\/",
        "BackupSetId":  "3c020dfe-48b3-4ad9-93e3-036fdb220cd5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126872000001348300001,
        "LastLSN":  17126872000002508300001,
        "CheckpointLSN":  17126872000002263900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500358500000)\/",
        "BackupFinishDate":  "\/Date(1500358500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "155b3751-4464-48a9-a68c-5b3fc9fad130",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500358500000)\/",
        "Start":  "\/Date(1500358500000)\/",
        "BackupSetId":  "155b3751-4464-48a9-a68c-5b3fc9fad130",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126872000002508300001,
        "LastLSN":  17126872000004522900001,
        "CheckpointLSN":  17126872000004332000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500358800000)\/",
        "BackupFinishDate":  "\/Date(1500358800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7c3de697-0a5e-4be4-b4e4-24a548627079",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500358800000)\/",
        "Start":  "\/Date(1500358800000)\/",
        "BackupSetId":  "7c3de697-0a5e-4be4-b4e4-24a548627079",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126872000004522900001,
        "LastLSN":  17126872000009167800001,
        "CheckpointLSN":  17126872000008513600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500359101000)\/",
        "BackupFinishDate":  "\/Date(1500359101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9e432f12-bbf9-46d1-b629-ae66adec9350",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500359101000)\/",
        "Start":  "\/Date(1500359101000)\/",
        "BackupSetId":  "9e432f12-bbf9-46d1-b629-ae66adec9350",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126872000009167800001,
        "LastLSN":  17126872000010465000001,
        "CheckpointLSN":  17126872000008513600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500359400000)\/",
        "BackupFinishDate":  "\/Date(1500359400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f726fcb8-d701-44cc-9553-27124768e950",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500359400000)\/",
        "Start":  "\/Date(1500359400000)\/",
        "BackupSetId":  "f726fcb8-d701-44cc-9553-27124768e950",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126872000010465000001,
        "LastLSN":  17126872000011866800001,
        "CheckpointLSN":  17126872000010599800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500359700000)\/",
        "BackupFinishDate":  "\/Date(1500359700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a9bbbf73-b3b0-4bb3-9aa9-7088f48502e7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500359700000)\/",
        "Start":  "\/Date(1500359700000)\/",
        "BackupSetId":  "a9bbbf73-b3b0-4bb3-9aa9-7088f48502e7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126872000011866800001,
        "LastLSN":  17126873000002164700001,
        "CheckpointLSN":  17126873000001757000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500360000000)\/",
        "BackupFinishDate":  "\/Date(1500360000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fac68e59-74ab-42da-84f9-c4d413d5154b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500360000000)\/",
        "Start":  "\/Date(1500360000000)\/",
        "BackupSetId":  "fac68e59-74ab-42da-84f9-c4d413d5154b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126873000002164700001,
        "LastLSN":  17126873000007518500001,
        "CheckpointLSN":  17126873000006352900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500360300000)\/",
        "BackupFinishDate":  "\/Date(1500360300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6afc679a-7823-4d0a-bd6f-175ffd70634f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500360300000)\/",
        "Start":  "\/Date(1500360300000)\/",
        "BackupSetId":  "6afc679a-7823-4d0a-bd6f-175ffd70634f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126873000007518500001,
        "LastLSN":  17126873000009190200001,
        "CheckpointLSN":  17126873000008427500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500360600000)\/",
        "BackupFinishDate":  "\/Date(1500360601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3477008d-d3bf-4f9e-a925-4cbb5bae4e93",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500360601000)\/",
        "Start":  "\/Date(1500360600000)\/",
        "BackupSetId":  "3477008d-d3bf-4f9e-a925-4cbb5bae4e93",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126873000009190200001,
        "LastLSN":  17126873000010665000001,
        "CheckpointLSN":  17126873000010499300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500360901000)\/",
        "BackupFinishDate":  "\/Date(1500360901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "dc2b3a02-3b5d-4539-ad47-2f354aeff254",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500360901000)\/",
        "Start":  "\/Date(1500360901000)\/",
        "BackupSetId":  "dc2b3a02-3b5d-4539-ad47-2f354aeff254",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126873000010665000001,
        "LastLSN":  17126874000006566100001,
        "CheckpointLSN":  17126874000006039300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500361200000)\/",
        "BackupFinishDate":  "\/Date(1500361200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c8bdc950-3c4b-4485-9aa3-78c485c1c395",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500361200000)\/",
        "Start":  "\/Date(1500361200000)\/",
        "BackupSetId":  "c8bdc950-3c4b-4485-9aa3-78c485c1c395",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126874000006566100001,
        "LastLSN":  17126875000009651500001,
        "CheckpointLSN":  17126875000009531200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500361500000)\/",
        "BackupFinishDate":  "\/Date(1500361500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8b79613d-1da1-4ebf-88c1-8f8fa1c5860a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500361500000)\/",
        "Start":  "\/Date(1500361500000)\/",
        "BackupSetId":  "8b79613d-1da1-4ebf-88c1-8f8fa1c5860a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126875000009651500001,
        "LastLSN":  17126877000001129500001,
        "CheckpointLSN":  17126877000001079100005,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500361800000)\/",
        "BackupFinishDate":  "\/Date(1500361801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4e2b4164-ede3-43e0-a855-a7be9910381f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500361801000)\/",
        "Start":  "\/Date(1500361800000)\/",
        "BackupSetId":  "4e2b4164-ede3-43e0-a855-a7be9910381f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126877000001129500001,
        "LastLSN":  17126878000001254100001,
        "CheckpointLSN":  17126877000012531300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500362100000)\/",
        "BackupFinishDate":  "\/Date(1500362101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "055e2ecb-e076-4934-a764-ee949f1e8331",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500362101000)\/",
        "Start":  "\/Date(1500362100000)\/",
        "BackupSetId":  "055e2ecb-e076-4934-a764-ee949f1e8331",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126878000001254100001,
        "LastLSN":  17126878000008324500001,
        "CheckpointLSN":  17126878000007543800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500362401000)\/",
        "BackupFinishDate":  "\/Date(1500362401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f96d0ae7-9c0d-4ecc-b8c1-de9a4589f9fd",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500362401000)\/",
        "Start":  "\/Date(1500362401000)\/",
        "BackupSetId":  "f96d0ae7-9c0d-4ecc-b8c1-de9a4589f9fd",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126878000008324500001,
        "LastLSN":  17126879000005426500001,
        "CheckpointLSN":  17126879000003856700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500362701000)\/",
        "BackupFinishDate":  "\/Date(1500362701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "eb1957b8-37e7-4431-9dce-7a5cffa5cf6b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500362701000)\/",
        "Start":  "\/Date(1500362701000)\/",
        "BackupSetId":  "eb1957b8-37e7-4431-9dce-7a5cffa5cf6b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126879000005426500001,
        "LastLSN":  17126880000000251900001,
        "CheckpointLSN":  17126879000012839400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500363000000)\/",
        "BackupFinishDate":  "\/Date(1500363000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "af1bb19a-cafd-4da3-900c-3d289a5e849a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500363000000)\/",
        "Start":  "\/Date(1500363000000)\/",
        "BackupSetId":  "af1bb19a-cafd-4da3-900c-3d289a5e849a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126880000000251900001,
        "LastLSN":  17126880000004591500001,
        "CheckpointLSN":  17126880000004435100044,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500363300000)\/",
        "BackupFinishDate":  "\/Date(1500363300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "bef5fe43-4327-4f1e-8e44-68e6a48e368a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500363300000)\/",
        "Start":  "\/Date(1500363300000)\/",
        "BackupSetId":  "bef5fe43-4327-4f1e-8e44-68e6a48e368a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126880000004591500001,
        "LastLSN":  17126880000010221200001,
        "CheckpointLSN":  17126880000008836100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500363600000)\/",
        "BackupFinishDate":  "\/Date(1500363600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d150f208-c47f-426b-8bf8-87f3a009cd2d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500363600000)\/",
        "Start":  "\/Date(1500363600000)\/",
        "BackupSetId":  "d150f208-c47f-426b-8bf8-87f3a009cd2d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126880000010221200001,
        "LastLSN":  17126881000003709800001,
        "CheckpointLSN":  17126881000003016100026,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500363900000)\/",
        "BackupFinishDate":  "\/Date(1500363900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d17e3f4c-a012-4e97-ac76-e0021a8c9926",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500363900000)\/",
        "Start":  "\/Date(1500363900000)\/",
        "BackupSetId":  "d17e3f4c-a012-4e97-ac76-e0021a8c9926",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126881000003709800001,
        "LastLSN":  17126882000002880400001,
        "CheckpointLSN":  17126882000001158100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500364200000)\/",
        "BackupFinishDate":  "\/Date(1500364201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "aa55f2e1-2945-48b9-a7fb-ae9225b21c74",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500364201000)\/",
        "Start":  "\/Date(1500364200000)\/",
        "BackupSetId":  "aa55f2e1-2945-48b9-a7fb-ae9225b21c74",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126882000002880400001,
        "LastLSN":  17126885000002910700001,
        "CheckpointLSN":  17126885000001518800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500364500000)\/",
        "BackupFinishDate":  "\/Date(1500364501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f3097802-3895-48ee-bbab-bb340e2b1864",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500364501000)\/",
        "Start":  "\/Date(1500364500000)\/",
        "BackupSetId":  "f3097802-3895-48ee-bbab-bb340e2b1864",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126885000002910700001,
        "LastLSN":  17126885000012446100001,
        "CheckpointLSN":  17126885000012397000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500364801000)\/",
        "BackupFinishDate":  "\/Date(1500364801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6d343efe-fcf5-4b61-ab59-9e63793f1c21",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500364801000)\/",
        "Start":  "\/Date(1500364801000)\/",
        "BackupSetId":  "6d343efe-fcf5-4b61-ab59-9e63793f1c21",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126885000012446100001,
        "LastLSN":  17126886000006098700001,
        "CheckpointLSN":  17126886000005556300004,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500365100000)\/",
        "BackupFinishDate":  "\/Date(1500365100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "00de6130-8b73-4723-9717-53bbb5980809",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500365100000)\/",
        "Start":  "\/Date(1500365100000)\/",
        "BackupSetId":  "00de6130-8b73-4723-9717-53bbb5980809",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126886000006098700001,
        "LastLSN":  17126886000008372000001,
        "CheckpointLSN":  17126886000007692600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500365400000)\/",
        "BackupFinishDate":  "\/Date(1500365400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "93c25af6-e78c-4093-bec9-fa4a000a0e1d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500365400000)\/",
        "Start":  "\/Date(1500365400000)\/",
        "BackupSetId":  "93c25af6-e78c-4093-bec9-fa4a000a0e1d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126886000008372000001,
        "LastLSN":  17126886000009731900001,
        "CheckpointLSN":  17126886000007692600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500365700000)\/",
        "BackupFinishDate":  "\/Date(1500365701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5817e7d4-fb36-4574-a16f-81746bbefdd2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500365701000)\/",
        "Start":  "\/Date(1500365700000)\/",
        "BackupSetId":  "5817e7d4-fb36-4574-a16f-81746bbefdd2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126886000009731900001,
        "LastLSN":  17126886000012009900001,
        "CheckpointLSN":  17126886000011886300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500366000000)\/",
        "BackupFinishDate":  "\/Date(1500366000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fd6efedb-49ff-4747-aadc-d1afcd63a79e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500366000000)\/",
        "Start":  "\/Date(1500366000000)\/",
        "BackupSetId":  "fd6efedb-49ff-4747-aadc-d1afcd63a79e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126886000012009900001,
        "LastLSN":  17126887000001819300001,
        "CheckpointLSN":  17126887000000883800033,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500366300000)\/",
        "BackupFinishDate":  "\/Date(1500366300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "58c1ae74-46e5-4d23-add8-bbd8ceca7872",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500366300000)\/",
        "Start":  "\/Date(1500366300000)\/",
        "BackupSetId":  "58c1ae74-46e5-4d23-add8-bbd8ceca7872",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126887000001819300001,
        "LastLSN":  17126887000004629100001,
        "CheckpointLSN":  17126887000003078000027,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500366600000)\/",
        "BackupFinishDate":  "\/Date(1500366600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "cde67a18-9d64-4c5a-a8b4-736bc22011c0",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500366600000)\/",
        "Start":  "\/Date(1500366600000)\/",
        "BackupSetId":  "cde67a18-9d64-4c5a-a8b4-736bc22011c0",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126887000004629100001,
        "LastLSN":  17126887000007492200001,
        "CheckpointLSN":  17126887000007463100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500366900000)\/",
        "BackupFinishDate":  "\/Date(1500366900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a352d833-494e-4ad1-93f3-38e96e77230f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500366900000)\/",
        "Start":  "\/Date(1500366900000)\/",
        "BackupSetId":  "a352d833-494e-4ad1-93f3-38e96e77230f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126887000007492200001,
        "LastLSN":  17126888000001473200001,
        "CheckpointLSN":  17126887000012639900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500367200000)\/",
        "BackupFinishDate":  "\/Date(1500367201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2afd92ef-c314-48aa-89d2-4bba8f2913be",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500367201000)\/",
        "Start":  "\/Date(1500367200000)\/",
        "BackupSetId":  "2afd92ef-c314-48aa-89d2-4bba8f2913be",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126888000001473200001,
        "LastLSN":  17126888000007702900001,
        "CheckpointLSN":  17126888000006332400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500367500000)\/",
        "BackupFinishDate":  "\/Date(1500367500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "094a53f2-00f4-4178-ab1a-0af54a698cfd",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500367500000)\/",
        "Start":  "\/Date(1500367500000)\/",
        "BackupSetId":  "094a53f2-00f4-4178-ab1a-0af54a698cfd",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126888000007702900001,
        "LastLSN":  17126888000012832900001,
        "CheckpointLSN":  17126888000010667400007,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500367800000)\/",
        "BackupFinishDate":  "\/Date(1500367800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "42d36e76-5b5b-46c1-b21c-706b6961a694",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500367800000)\/",
        "Start":  "\/Date(1500367800000)\/",
        "BackupSetId":  "42d36e76-5b5b-46c1-b21c-706b6961a694",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126888000012832900001,
        "LastLSN":  17126889000004147900001,
        "CheckpointLSN":  17126889000002242400030,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500368100000)\/",
        "BackupFinishDate":  "\/Date(1500368100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "feda57fc-cd7a-407c-8c01-68804a7d03bd",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500368100000)\/",
        "Start":  "\/Date(1500368100000)\/",
        "BackupSetId":  "feda57fc-cd7a-407c-8c01-68804a7d03bd",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126889000004147900001,
        "LastLSN":  17126889000008328700001,
        "CheckpointLSN":  17126889000006512000046,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500368401000)\/",
        "BackupFinishDate":  "\/Date(1500368402000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "403c379f-92f3-4d5f-9c3d-fe3dc391387d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500368402000)\/",
        "Start":  "\/Date(1500368401000)\/",
        "BackupSetId":  "403c379f-92f3-4d5f-9c3d-fe3dc391387d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126889000008328700001,
        "LastLSN":  17126889000011872200001,
        "CheckpointLSN":  17126889000011097500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500368700000)\/",
        "BackupFinishDate":  "\/Date(1500368700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0c4bce56-1f86-48b7-a4bd-c510a059e235",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500368700000)\/",
        "Start":  "\/Date(1500368700000)\/",
        "BackupSetId":  "0c4bce56-1f86-48b7-a4bd-c510a059e235",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126889000011872200001,
        "LastLSN":  17126890000007715700001,
        "CheckpointLSN":  17126890000005513400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500369000000)\/",
        "BackupFinishDate":  "\/Date(1500369000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e5e28fed-19f0-4e91-8368-cb6856385ce9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500369000000)\/",
        "Start":  "\/Date(1500369000000)\/",
        "BackupSetId":  "e5e28fed-19f0-4e91-8368-cb6856385ce9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126890000007715700001,
        "LastLSN":  17126891000001634500001,
        "CheckpointLSN":  17126890000012226400010,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500369300000)\/",
        "BackupFinishDate":  "\/Date(1500369300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8d47c5ef-065f-4d26-bdac-72a28f099e4a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500369300000)\/",
        "Start":  "\/Date(1500369300000)\/",
        "BackupSetId":  "8d47c5ef-065f-4d26-bdac-72a28f099e4a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126891000001634500001,
        "LastLSN":  17126891000006141500001,
        "CheckpointLSN":  17126891000005984300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500369600000)\/",
        "BackupFinishDate":  "\/Date(1500369600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0928531e-88b9-469c-83ab-8c0e113f7e11",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500369600000)\/",
        "Start":  "\/Date(1500369600000)\/",
        "BackupSetId":  "0928531e-88b9-469c-83ab-8c0e113f7e11",
        "Database":  "testdb"
    }
]
tools\dbatools\tests\ObjectDefinitions\BackupRestore\RawInput\CleanFormatDbaInformation.xml
<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
  <Obj RefId="0">
    <TN RefId="0">
      <T>Sqlcollaborative.Dbatools.Database.BackupHistory</T>
      <T>System.Object</T>
    </TN>
    <ToString>Sqlcollaborative.Dbatools.Database.BackupHistory</ToString>
    <Props>
      <S N="ComputerName">CGE100LAP-42</S>
      <S N="InstanceName"></S>
      <S N="SqlInstance">CGE100LAP-42\SQLEXPRESS2016</S>
      <S N="Database">RestoreTimeClean</S>
      <S N="UserName">ADS\COM3MOORESM</S>
      <DT N="Start">2017-08-01T11:09:25</DT>
      <DT N="End">2017-08-01T11:09:25</DT>
      <Obj N="Duration" RefId="1">
        <TN RefId="1">
          <T>Sqlcollaborative.Dbatools.Utility.DbaTimeSpan</T>
          <T>System.Object</T>
        </TN>
        <ToString>00:00:00</ToString>
        <Props>
          <I32 N="Days">0</I32>
          <I32 N="Hours">0</I32>
          <I32 N="Milliseconds">0</I32>
          <I32 N="Minutes">0</I32>
          <I32 N="Seconds">0</I32>
          <I64 N="Ticks">0</I64>
          <Db N="TotalDays">0</Db>
          <Db N="TotalHours">0</Db>
          <Db N="TotalMilliseconds">0</Db>
          <Db N="TotalMinutes">0</Db>
          <Db N="TotalSeconds">0</Db>
        </Props>
      </Obj>
      <Obj N="Path" RefId="2">
        <TN RefId="2">
          <T>System.String[]</T>
          <T>System.Array</T>
          <T>System.Object</T>
        </TN>
        <LST>
          <S>C:\dbatools\RestoreTimeClean\RestoreTimeClean.bak</S>
        </LST>
      </Obj>
      <Nil N="TotalSize" />
      <S N="Type">Database</S>
      <S N="BackupSetId">95383afa-d1dc-4a95-92c2-06b2ba1277db</S>
      <S N="DeviceType">Disk</S>
      <Nil N="Software" />
      <Obj N="FullName" RefId="3">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\RestoreTimeClean\RestoreTimeClean.bak</S>
        </LST>
      </Obj>
      <Obj N="FileList" RefId="4">
        <TN RefId="3">
          <T>System.Object[]</T>
          <T>System.Array</T>
          <T>System.Object</T>
        </TN>
        <LST>
          <Obj RefId="5">
            <TN RefId="4">
              <T>Selected.System.Management.Automation.PSCustomObject</T>
              <T>System.Management.Automation.PSCustomObject</T>
              <T>System.Object</T>
            </TN>
            <MS>
              <S N="Type">D</S>
              <S N="LogicalName">RestoreTimeClean</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\DATA\RestoreTimeClean.mdf</S>
            </MS>
          </Obj>
          <Obj RefId="6">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">L</S>
              <S N="LogicalName">RestoreTimeClean_log</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\data\RestoreTimeClean_log.ldf</S>
            </MS>
          </Obj>
        </LST>
      </Obj>
      <I32 N="Position">1</I32>
      <Obj N="FirstLsn" RefId="7">
        <TN RefId="5">
          <T>System.Numerics.BigInteger</T>
          <T>System.ValueType</T>
          <T>System.Object</T>
        </TN>
        <ToString>34000000006900179</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="DatabaseBackupLsn" RefId="8">
        <TNRef RefId="5" />
        <ToString>0</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">true</B>
          <B N="IsOne">false</B>
          <B N="IsEven">true</B>
          <I32 N="Sign">0</I32>
        </Props>
      </Obj>
      <Obj N="CheckpointLsn" RefId="9">
        <TNRef RefId="5" />
        <ToString>34000000006900179</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="LastLsn" RefId="10">
        <TNRef RefId="5" />
        <ToString>34000000014400001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <I32 N="SoftwareVersionMajor">13</I32>
      <B N="IsCopyOnly">false</B>
      <G N="LastRecoveryForkGUID">00000000-0000-0000-0000-000000000000</G>
    </Props>
    <MS>
      <S N="OriginalDatabase">RestoreTimeClean</S>
      <Ref N="OriginalFileList" RefId="4" />
      <Ref N="OriginalFullName" RefId="3" />
      <B N="IsVerified">false</B>
    </MS>
  </Obj>
  <Obj RefId="11">
    <TNRef RefId="0" />
    <ToString>Sqlcollaborative.Dbatools.Database.BackupHistory</ToString>
    <Props>
      <S N="ComputerName">CGE100LAP-42</S>
      <S N="InstanceName"></S>
      <S N="SqlInstance">CGE100LAP-42\SQLEXPRESS2016</S>
      <S N="Database">RestoreTimeClean</S>
      <S N="UserName">ADS\COM3MOORESM</S>
      <DT N="Start">2017-08-01T11:10:15</DT>
      <DT N="End">2017-08-01T11:10:15</DT>
      <Obj N="Duration" RefId="12">
        <TNRef RefId="1" />
        <ToString>00:00:00</ToString>
        <Props>
          <I32 N="Days">0</I32>
          <I32 N="Hours">0</I32>
          <I32 N="Milliseconds">0</I32>
          <I32 N="Minutes">0</I32>
          <I32 N="Seconds">0</I32>
          <I64 N="Ticks">0</I64>
          <Db N="TotalDays">0</Db>
          <Db N="TotalHours">0</Db>
          <Db N="TotalMilliseconds">0</Db>
          <Db N="TotalMinutes">0</Db>
          <Db N="TotalSeconds">0</Db>
        </Props>
      </Obj>
      <Obj N="Path" RefId="13">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\RestoreTimeClean\RestoreTimeClean_1.trn</S>
        </LST>
      </Obj>
      <Nil N="TotalSize" />
      <S N="Type">Transaction Log</S>
      <S N="BackupSetId">293a6268-a873-4eea-8b03-e9d002459d91</S>
      <S N="DeviceType">Disk</S>
      <Nil N="Software" />
      <Obj N="FullName" RefId="14">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\RestoreTimeClean\RestoreTimeClean_1.trn</S>
        </LST>
      </Obj>
      <Obj N="FileList" RefId="15">
        <TNRef RefId="3" />
        <LST>
          <Obj RefId="16">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">D</S>
              <S N="LogicalName">RestoreTimeClean</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\DATA\RestoreTimeClean.mdf</S>
            </MS>
          </Obj>
          <Obj RefId="17">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">L</S>
              <S N="LogicalName">RestoreTimeClean_log</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\data\RestoreTimeClean_log.ldf</S>
            </MS>
          </Obj>
        </LST>
      </Obj>
      <I32 N="Position">1</I32>
      <Obj N="FirstLsn" RefId="18">
        <TNRef RefId="5" />
        <ToString>34000000006900179</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="DatabaseBackupLsn" RefId="19">
        <TNRef RefId="5" />
        <ToString>34000000006900179</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="CheckpointLsn" RefId="20">
        <TNRef RefId="5" />
        <ToString>34000000006900179</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="LastLsn" RefId="21">
        <TNRef RefId="5" />
        <ToString>34000000016800001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <I32 N="SoftwareVersionMajor">13</I32>
      <B N="IsCopyOnly">false</B>
      <G N="LastRecoveryForkGUID">00000000-0000-0000-0000-000000000000</G>
    </Props>
    <MS>
      <S N="OriginalDatabase">RestoreTimeClean</S>
      <Ref N="OriginalFileList" RefId="15" />
      <Ref N="OriginalFullName" RefId="14" />
      <B N="IsVerified">false</B>
    </MS>
  </Obj>
  <Obj RefId="22">
    <TNRef RefId="0" />
    <ToString>Sqlcollaborative.Dbatools.Database.BackupHistory</ToString>
    <Props>
      <S N="ComputerName">CGE100LAP-42</S>
      <S N="InstanceName"></S>
      <S N="SqlInstance">CGE100LAP-42\SQLEXPRESS2016</S>
      <S N="Database">RestoreTimeClean</S>
      <S N="UserName">ADS\COM3MOORESM</S>
      <DT N="Start">2017-08-01T11:11:05</DT>
      <DT N="End">2017-08-01T11:11:05</DT>
      <Obj N="Duration" RefId="23">
        <TNRef RefId="1" />
        <ToString>00:00:00</ToString>
        <Props>
          <I32 N="Days">0</I32>
          <I32 N="Hours">0</I32>
          <I32 N="Milliseconds">0</I32>
          <I32 N="Minutes">0</I32>
          <I32 N="Seconds">0</I32>
          <I64 N="Ticks">0</I64>
          <Db N="TotalDays">0</Db>
          <Db N="TotalHours">0</Db>
          <Db N="TotalMilliseconds">0</Db>
          <Db N="TotalMinutes">0</Db>
          <Db N="TotalSeconds">0</Db>
        </Props>
      </Obj>
      <Obj N="Path" RefId="24">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\RestoreTimeClean\RestoreTimeClean_2.trn</S>
        </LST>
      </Obj>
      <Nil N="TotalSize" />
      <S N="Type">Transaction Log</S>
      <S N="BackupSetId">0c0e1500-f405-46a1-bda5-701d400fe690</S>
      <S N="DeviceType">Disk</S>
      <Nil N="Software" />
      <Obj N="FullName" RefId="25">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\RestoreTimeClean\RestoreTimeClean_2.trn</S>
        </LST>
      </Obj>
      <Obj N="FileList" RefId="26">
        <TNRef RefId="3" />
        <LST>
          <Obj RefId="27">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">D</S>
              <S N="LogicalName">RestoreTimeClean</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\DATA\RestoreTimeClean.mdf</S>
            </MS>
          </Obj>
          <Obj RefId="28">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">L</S>
              <S N="LogicalName">RestoreTimeClean_log</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\data\RestoreTimeClean_log.ldf</S>
            </MS>
          </Obj>
        </LST>
      </Obj>
      <I32 N="Position">1</I32>
      <Obj N="FirstLsn" RefId="29">
        <TNRef RefId="5" />
        <ToString>34000000016800001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="DatabaseBackupLsn" RefId="30">
        <TNRef RefId="5" />
        <ToString>34000000006900179</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="CheckpointLsn" RefId="31">
        <TNRef RefId="5" />
        <ToString>34000000017000002</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">true</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="LastLsn" RefId="32">
        <TNRef RefId="5" />
        <ToString>34000000019700001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <I32 N="SoftwareVersionMajor">13</I32>
      <B N="IsCopyOnly">false</B>
      <G N="LastRecoveryForkGUID">00000000-0000-0000-0000-000000000000</G>
    </Props>
    <MS>
      <S N="OriginalDatabase">RestoreTimeClean</S>
      <Ref N="OriginalFileList" RefId="26" />
      <Ref N="OriginalFullName" RefId="25" />
      <B N="IsVerified">false</B>
    </MS>
  </Obj>
  <Obj RefId="33">
    <TNRef RefId="0" />
    <ToString>Sqlcollaborative.Dbatools.Database.BackupHistory</ToString>
    <Props>
      <S N="ComputerName">CGE100LAP-42</S>
      <S N="InstanceName"></S>
      <S N="SqlInstance">CGE100LAP-42\SQLEXPRESS2016</S>
      <S N="Database">RestoreTimeClean</S>
      <S N="UserName">ADS\COM3MOORESM</S>
      <DT N="Start">2017-08-01T11:11:55</DT>
      <DT N="End">2017-08-01T11:11:55</DT>
      <Obj N="Duration" RefId="34">
        <TNRef RefId="1" />
        <ToString>00:00:00</ToString>
        <Props>
          <I32 N="Days">0</I32>
          <I32 N="Hours">0</I32>
          <I32 N="Milliseconds">0</I32>
          <I32 N="Minutes">0</I32>
          <I32 N="Seconds">0</I32>
          <I64 N="Ticks">0</I64>
          <Db N="TotalDays">0</Db>
          <Db N="TotalHours">0</Db>
          <Db N="TotalMilliseconds">0</Db>
          <Db N="TotalMinutes">0</Db>
          <Db N="TotalSeconds">0</Db>
        </Props>
      </Obj>
      <Obj N="Path" RefId="35">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\RestoreTimeClean\RestoreTimeClean_3.trn</S>
        </LST>
      </Obj>
      <Nil N="TotalSize" />
      <S N="Type">Transaction Log</S>
      <S N="BackupSetId">ba177689-2085-4955-ac54-5631ee943f22</S>
      <S N="DeviceType">Disk</S>
      <Nil N="Software" />
      <Obj N="FullName" RefId="36">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\RestoreTimeClean\RestoreTimeClean_3.trn</S>
        </LST>
      </Obj>
      <Obj N="FileList" RefId="37">
        <TNRef RefId="3" />
        <LST>
          <Obj RefId="38">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">D</S>
              <S N="LogicalName">RestoreTimeClean</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\DATA\RestoreTimeClean.mdf</S>
            </MS>
          </Obj>
          <Obj RefId="39">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">L</S>
              <S N="LogicalName">RestoreTimeClean_log</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\data\RestoreTimeClean_log.ldf</S>
            </MS>
          </Obj>
        </LST>
      </Obj>
      <I32 N="Position">1</I32>
      <Obj N="FirstLsn" RefId="40">
        <TNRef RefId="5" />
        <ToString>34000000019700001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="DatabaseBackupLsn" RefId="41">
        <TNRef RefId="5" />
        <ToString>34000000006900179</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="CheckpointLsn" RefId="42">
        <TNRef RefId="5" />
        <ToString>34000000019900002</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">true</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="LastLsn" RefId="43">
        <TNRef RefId="5" />
        <ToString>34000000021300001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <I32 N="SoftwareVersionMajor">13</I32>
      <B N="IsCopyOnly">false</B>
      <G N="LastRecoveryForkGUID">00000000-0000-0000-0000-000000000000</G>
    </Props>
    <MS>
      <S N="OriginalDatabase">RestoreTimeClean</S>
      <Ref N="OriginalFileList" RefId="37" />
      <Ref N="OriginalFullName" RefId="36" />
      <B N="IsVerified">false</B>
    </MS>
  </Obj>
  <Obj RefId="44">
    <TNRef RefId="0" />
    <ToString>Sqlcollaborative.Dbatools.Database.BackupHistory</ToString>
    <Props>
      <S N="ComputerName">CGE100LAP-42</S>
      <S N="InstanceName"></S>
      <S N="SqlInstance">CGE100LAP-42\SQLEXPRESS2016</S>
      <S N="Database">RestoreTimeClean</S>
      <S N="UserName">ADS\COM3MOORESM</S>
      <DT N="Start">2017-08-01T11:12:45</DT>
      <DT N="End">2017-08-01T11:12:45</DT>
      <Obj N="Duration" RefId="45">
        <TNRef RefId="1" />
        <ToString>00:00:00</ToString>
        <Props>
          <I32 N="Days">0</I32>
          <I32 N="Hours">0</I32>
          <I32 N="Milliseconds">0</I32>
          <I32 N="Minutes">0</I32>
          <I32 N="Seconds">0</I32>
          <I64 N="Ticks">0</I64>
          <Db N="TotalDays">0</Db>
          <Db N="TotalHours">0</Db>
          <Db N="TotalMilliseconds">0</Db>
          <Db N="TotalMinutes">0</Db>
          <Db N="TotalSeconds">0</Db>
        </Props>
      </Obj>
      <Obj N="Path" RefId="46">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\RestoreTimeClean\RestoreTimeClean_21.trn</S>
        </LST>
      </Obj>
      <Nil N="TotalSize" />
      <S N="Type">Transaction Log</S>
      <S N="BackupSetId">16237419-48a6-47e0-920e-290325c1e9d4</S>
      <S N="DeviceType">Disk</S>
      <Nil N="Software" />
      <Obj N="FullName" RefId="47">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\RestoreTimeClean\RestoreTimeClean_21.trn</S>
        </LST>
      </Obj>
      <Obj N="FileList" RefId="48">
        <TNRef RefId="3" />
        <LST>
          <Obj RefId="49">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">D</S>
              <S N="LogicalName">RestoreTimeClean</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\DATA\RestoreTimeClean.mdf</S>
            </MS>
          </Obj>
          <Obj RefId="50">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">L</S>
              <S N="LogicalName">RestoreTimeClean_log</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\data\RestoreTimeClean_log.ldf</S>
            </MS>
          </Obj>
        </LST>
      </Obj>
      <I32 N="Position">1</I32>
      <Obj N="FirstLsn" RefId="51">
        <TNRef RefId="5" />
        <ToString>34000000021300001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="DatabaseBackupLsn" RefId="52">
        <TNRef RefId="5" />
        <ToString>34000000006900179</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="CheckpointLsn" RefId="53">
        <TNRef RefId="5" />
        <ToString>34000000021500002</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">true</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="LastLsn" RefId="54">
        <TNRef RefId="5" />
        <ToString>34000000022900001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <I32 N="SoftwareVersionMajor">13</I32>
      <B N="IsCopyOnly">false</B>
      <G N="LastRecoveryForkGUID">00000000-0000-0000-0000-000000000000</G>
    </Props>
    <MS>
      <S N="OriginalDatabase">RestoreTimeClean</S>
      <Ref N="OriginalFileList" RefId="48" />
      <Ref N="OriginalFullName" RefId="47" />
      <B N="IsVerified">false</B>
    </MS>
  </Obj>
  <Obj RefId="55">
    <TNRef RefId="0" />
    <ToString>Sqlcollaborative.Dbatools.Database.BackupHistory</ToString>
    <Props>
      <S N="ComputerName">CGE100LAP-42</S>
      <S N="InstanceName"></S>
      <S N="SqlInstance">CGE100LAP-42\SQLEXPRESS2016</S>
      <S N="Database">RestoreTimeClean</S>
      <S N="UserName">ADS\COM3MOORESM</S>
      <DT N="Start">2017-08-01T11:13:36</DT>
      <DT N="End">2017-08-01T11:13:36</DT>
      <Obj N="Duration" RefId="56">
        <TNRef RefId="1" />
        <ToString>00:00:00</ToString>
        <Props>
          <I32 N="Days">0</I32>
          <I32 N="Hours">0</I32>
          <I32 N="Milliseconds">0</I32>
          <I32 N="Minutes">0</I32>
          <I32 N="Seconds">0</I32>
          <I64 N="Ticks">0</I64>
          <Db N="TotalDays">0</Db>
          <Db N="TotalHours">0</Db>
          <Db N="TotalMilliseconds">0</Db>
          <Db N="TotalMinutes">0</Db>
          <Db N="TotalSeconds">0</Db>
        </Props>
      </Obj>
      <Obj N="Path" RefId="57">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\RestoreTimeClean\RestoreTimeClean_22.trn</S>
        </LST>
      </Obj>
      <Nil N="TotalSize" />
      <S N="Type">Transaction Log</S>
      <S N="BackupSetId">5577b1b4-df72-4978-b4ae-d7de24ee68ce</S>
      <S N="DeviceType">Disk</S>
      <Nil N="Software" />
      <Obj N="FullName" RefId="58">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\RestoreTimeClean\RestoreTimeClean_22.trn</S>
        </LST>
      </Obj>
      <Obj N="FileList" RefId="59">
        <TNRef RefId="3" />
        <LST>
          <Obj RefId="60">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">D</S>
              <S N="LogicalName">RestoreTimeClean</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\DATA\RestoreTimeClean.mdf</S>
            </MS>
          </Obj>
          <Obj RefId="61">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">L</S>
              <S N="LogicalName">RestoreTimeClean_log</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\data\RestoreTimeClean_log.ldf</S>
            </MS>
          </Obj>
        </LST>
      </Obj>
      <I32 N="Position">1</I32>
      <Obj N="FirstLsn" RefId="62">
        <TNRef RefId="5" />
        <ToString>34000000022900001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="DatabaseBackupLsn" RefId="63">
        <TNRef RefId="5" />
        <ToString>34000000006900179</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="CheckpointLsn" RefId="64">
        <TNRef RefId="5" />
        <ToString>34000000023100002</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">true</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="LastLsn" RefId="65">
        <TNRef RefId="5" />
        <ToString>34000000024500001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <I32 N="SoftwareVersionMajor">13</I32>
      <B N="IsCopyOnly">false</B>
      <G N="LastRecoveryForkGUID">00000000-0000-0000-0000-000000000000</G>
    </Props>
    <MS>
      <S N="OriginalDatabase">RestoreTimeClean</S>
      <Ref N="OriginalFileList" RefId="59" />
      <Ref N="OriginalFullName" RefId="58" />
      <B N="IsVerified">false</B>
    </MS>
  </Obj>
  <Obj RefId="66">
    <TNRef RefId="0" />
    <ToString>Sqlcollaborative.Dbatools.Database.BackupHistory</ToString>
    <Props>
      <S N="ComputerName">CGE100LAP-42</S>
      <S N="InstanceName"></S>
      <S N="SqlInstance">CGE100LAP-42\SQLEXPRESS2016</S>
      <S N="Database">RestoreTimeClean</S>
      <S N="UserName">ADS\COM3MOORESM</S>
      <DT N="Start">2017-08-01T11:14:26</DT>
      <DT N="End">2017-08-01T11:14:26</DT>
      <Obj N="Duration" RefId="67">
        <TNRef RefId="1" />
        <ToString>00:00:00</ToString>
        <Props>
          <I32 N="Days">0</I32>
          <I32 N="Hours">0</I32>
          <I32 N="Milliseconds">0</I32>
          <I32 N="Minutes">0</I32>
          <I32 N="Seconds">0</I32>
          <I64 N="Ticks">0</I64>
          <Db N="TotalDays">0</Db>
          <Db N="TotalHours">0</Db>
          <Db N="TotalMilliseconds">0</Db>
          <Db N="TotalMinutes">0</Db>
          <Db N="TotalSeconds">0</Db>
        </Props>
      </Obj>
      <Obj N="Path" RefId="68">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\RestoreTimeClean\RestoreTimeClean_23.trn</S>
        </LST>
      </Obj>
      <Nil N="TotalSize" />
      <S N="Type">Transaction Log</S>
      <S N="BackupSetId">01e79d9c-d181-4050-bb47-a6c4fceaca1b</S>
      <S N="DeviceType">Disk</S>
      <Nil N="Software" />
      <Obj N="FullName" RefId="69">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\RestoreTimeClean\RestoreTimeClean_23.trn</S>
        </LST>
      </Obj>
      <Obj N="FileList" RefId="70">
        <TNRef RefId="3" />
        <LST>
          <Obj RefId="71">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">D</S>
              <S N="LogicalName">RestoreTimeClean</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\DATA\RestoreTimeClean.mdf</S>
            </MS>
          </Obj>
          <Obj RefId="72">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">L</S>
              <S N="LogicalName">RestoreTimeClean_log</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\data\RestoreTimeClean_log.ldf</S>
            </MS>
          </Obj>
        </LST>
      </Obj>
      <I32 N="Position">1</I32>
      <Obj N="FirstLsn" RefId="73">
        <TNRef RefId="5" />
        <ToString>34000000024500001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="DatabaseBackupLsn" RefId="74">
        <TNRef RefId="5" />
        <ToString>34000000006900179</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="CheckpointLsn" RefId="75">
        <TNRef RefId="5" />
        <ToString>34000000024700002</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">true</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="LastLsn" RefId="76">
        <TNRef RefId="5" />
        <ToString>34000000026100001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <I32 N="SoftwareVersionMajor">13</I32>
      <B N="IsCopyOnly">false</B>
      <G N="LastRecoveryForkGUID">00000000-0000-0000-0000-000000000000</G>
    </Props>
    <MS>
      <S N="OriginalDatabase">RestoreTimeClean</S>
      <Ref N="OriginalFileList" RefId="70" />
      <Ref N="OriginalFullName" RefId="69" />
      <B N="IsVerified">false</B>
    </MS>
  </Obj>
</Objs>
tools\dbatools\tests\ObjectDefinitions\BackupRestore\RawInput\ContinuePointTest.xml
<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
  <Obj RefId="0">
    <TN RefId="0">
      <T>Selected.Sqlcollaborative.Dbatools.Database.BackupHistory</T>
      <T>System.Management.Automation.PSCustomObject</T>
      <T>System.Object</T>
    </TN>
    <MS>
      <S N="ComputerName">CGE100LAP-42</S>
      <S N="InstanceName"></S>
      <S N="SqlInstance">CGE100LAP-42\SQLEXPRESS2016</S>
      <S N="Database">ContinuePointTest</S>
      <DT N="Start">2017-10-27T17:33:54</DT>
      <DT N="End">2017-10-27T17:33:54</DT>
      <Obj N="Duration" RefId="1">
        <TN RefId="1">
          <T>Sqlcollaborative.Dbatools.Utility.DbaTimeSpan</T>
          <T>System.Object</T>
        </TN>
        <ToString>00:00:00</ToString>
        <Props>
          <I32 N="Days">0</I32>
          <I32 N="Hours">0</I32>
          <I32 N="Milliseconds">0</I32>
          <I32 N="Minutes">0</I32>
          <I32 N="Seconds">0</I32>
          <I64 N="Ticks">0</I64>
          <Db N="TotalDays">0</Db>
          <Db N="TotalHours">0</Db>
          <Db N="TotalMilliseconds">0</Db>
          <Db N="TotalMinutes">0</Db>
          <Db N="TotalSeconds">0</Db>
        </Props>
      </Obj>
      <Obj N="Path" RefId="2">
        <TN RefId="2">
          <T>System.String[]</T>
          <T>System.Array</T>
          <T>System.Object</T>
        </TN>
        <LST>
          <S>C:\dbatools\ContinuePointTest\ContinuePointTest_23.trn</S>
        </LST>
      </Obj>
      <Nil N="TotalSize" />
      <S N="Type">Transaction Log</S>
      <S N="BackupSetId">ddaae491-c08b-448c-8e75-190697309085</S>
      <S N="DeviceType">Disk</S>
      <Nil N="Software" />
      <Obj N="FullName" RefId="3">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\ContinuePointTest\ContinuePointTest_23.trn</S>
        </LST>
      </Obj>
      <Obj N="FileList" RefId="4">
        <TN RefId="3">
          <T>System.Object[]</T>
          <T>System.Array</T>
          <T>System.Object</T>
        </TN>
        <LST>
          <Obj RefId="5">
            <TN RefId="4">
              <T>Selected.System.Management.Automation.PSCustomObject</T>
              <T>System.Management.Automation.PSCustomObject</T>
              <T>System.Object</T>
            </TN>
            <MS>
              <S N="Type">D</S>
              <S N="LogicalName">ContinuePointTest</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\DATA\ContinuePointTest.mdf</S>
            </MS>
          </Obj>
          <Obj RefId="6">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">L</S>
              <S N="LogicalName">ContinuePointTest_log</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\data\ContinuePointTest_log.ldf</S>
            </MS>
          </Obj>
        </LST>
      </Obj>
      <I32 N="Position">1</I32>
      <Obj N="FirstLsn" RefId="7">
        <TN RefId="5">
          <T>System.Numerics.BigInteger</T>
          <T>System.ValueType</T>
          <T>System.Object</T>
        </TN>
        <ToString>34000000024200001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="DatabaseBackupLsn" RefId="8">
        <TNRef RefId="5" />
        <ToString>34000000006800179</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="CheckpointLsn" RefId="9">
        <TNRef RefId="5" />
        <ToString>34000000024400002</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">true</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="LastLsn" RefId="10">
        <TNRef RefId="5" />
        <ToString>34000000025800001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <I32 N="SoftwareVersionMajor">13</I32>
      <B N="IsCopyOnly">false</B>
      <G N="LastRecoveryForkGUID">00000000-0000-0000-0000-000000000000</G>
      <S N="UserName">Stuart</S>
    </MS>
  </Obj>
  <Obj RefId="11">
    <TNRef RefId="0" />
    <MS>
      <S N="ComputerName">CGE100LAP-42</S>
      <S N="InstanceName"></S>
      <S N="SqlInstance">CGE100LAP-42\SQLEXPRESS2016</S>
      <S N="Database">ContinuePointTest</S>
      <DT N="Start">2017-10-27T17:33:04</DT>
      <DT N="End">2017-10-27T17:33:04</DT>
      <Obj N="Duration" RefId="12">
        <TNRef RefId="1" />
        <ToString>00:00:00</ToString>
        <Props>
          <I32 N="Days">0</I32>
          <I32 N="Hours">0</I32>
          <I32 N="Milliseconds">0</I32>
          <I32 N="Minutes">0</I32>
          <I32 N="Seconds">0</I32>
          <I64 N="Ticks">0</I64>
          <Db N="TotalDays">0</Db>
          <Db N="TotalHours">0</Db>
          <Db N="TotalMilliseconds">0</Db>
          <Db N="TotalMinutes">0</Db>
          <Db N="TotalSeconds">0</Db>
        </Props>
      </Obj>
      <Obj N="Path" RefId="13">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\ContinuePointTest\ContinuePointTest_22.trn</S>
        </LST>
      </Obj>
      <Nil N="TotalSize" />
      <S N="Type">Transaction Log</S>
      <S N="BackupSetId">95b8e2ad-2112-4a6b-9870-778026644e41</S>
      <S N="DeviceType">Disk</S>
      <Nil N="Software" />
      <Obj N="FullName" RefId="14">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\ContinuePointTest\ContinuePointTest_22.trn</S>
        </LST>
      </Obj>
      <Obj N="FileList" RefId="15">
        <TNRef RefId="3" />
        <LST>
          <Obj RefId="16">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">D</S>
              <S N="LogicalName">ContinuePointTest</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\DATA\ContinuePointTest.mdf</S>
            </MS>
          </Obj>
          <Obj RefId="17">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">L</S>
              <S N="LogicalName">ContinuePointTest_log</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\data\ContinuePointTest_log.ldf</S>
            </MS>
          </Obj>
        </LST>
      </Obj>
      <I32 N="Position">1</I32>
      <Obj N="FirstLsn" RefId="18">
        <TNRef RefId="5" />
        <ToString>34000000022600001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="DatabaseBackupLsn" RefId="19">
        <TNRef RefId="5" />
        <ToString>34000000006800179</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="CheckpointLsn" RefId="20">
        <TNRef RefId="5" />
        <ToString>34000000022800002</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">true</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="LastLsn" RefId="21">
        <TNRef RefId="5" />
        <ToString>34000000024200001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <I32 N="SoftwareVersionMajor">13</I32>
      <B N="IsCopyOnly">false</B>
      <G N="LastRecoveryForkGUID">00000000-0000-0000-0000-000000000000</G>
      <S N="UserName">Stuart</S>
    </MS>
  </Obj>
  <Obj RefId="22">
    <TNRef RefId="0" />
    <MS>
      <S N="ComputerName">CGE100LAP-42</S>
      <S N="InstanceName"></S>
      <S N="SqlInstance">CGE100LAP-42\SQLEXPRESS2016</S>
      <S N="Database">ContinuePointTest</S>
      <DT N="Start">2017-10-27T17:32:14</DT>
      <DT N="End">2017-10-27T17:32:14</DT>
      <Obj N="Duration" RefId="23">
        <TNRef RefId="1" />
        <ToString>00:00:00</ToString>
        <Props>
          <I32 N="Days">0</I32>
          <I32 N="Hours">0</I32>
          <I32 N="Milliseconds">0</I32>
          <I32 N="Minutes">0</I32>
          <I32 N="Seconds">0</I32>
          <I64 N="Ticks">0</I64>
          <Db N="TotalDays">0</Db>
          <Db N="TotalHours">0</Db>
          <Db N="TotalMilliseconds">0</Db>
          <Db N="TotalMinutes">0</Db>
          <Db N="TotalSeconds">0</Db>
        </Props>
      </Obj>
      <Obj N="Path" RefId="24">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\ContinuePointTest\ContinuePointTest_21.trn</S>
        </LST>
      </Obj>
      <Nil N="TotalSize" />
      <S N="Type">Transaction Log</S>
      <S N="BackupSetId">b4ae2a8c-512e-4348-96ef-61394e1f13e8</S>
      <S N="DeviceType">Disk</S>
      <Nil N="Software" />
      <Obj N="FullName" RefId="25">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\ContinuePointTest\ContinuePointTest_21.trn</S>
        </LST>
      </Obj>
      <Obj N="FileList" RefId="26">
        <TNRef RefId="3" />
        <LST>
          <Obj RefId="27">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">D</S>
              <S N="LogicalName">ContinuePointTest</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\DATA\ContinuePointTest.mdf</S>
            </MS>
          </Obj>
          <Obj RefId="28">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">L</S>
              <S N="LogicalName">ContinuePointTest_log</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\data\ContinuePointTest_log.ldf</S>
            </MS>
          </Obj>
        </LST>
      </Obj>
      <I32 N="Position">1</I32>
      <Obj N="FirstLsn" RefId="29">
        <TNRef RefId="5" />
        <ToString>34000000019600001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="DatabaseBackupLsn" RefId="30">
        <TNRef RefId="5" />
        <ToString>34000000006800179</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="CheckpointLsn" RefId="31">
        <TNRef RefId="5" />
        <ToString>34000000021200005</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="LastLsn" RefId="32">
        <TNRef RefId="5" />
        <ToString>34000000022600001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <I32 N="SoftwareVersionMajor">13</I32>
      <B N="IsCopyOnly">false</B>
      <G N="LastRecoveryForkGUID">00000000-0000-0000-0000-000000000000</G>
      <S N="UserName">Stuart</S>
    </MS>
  </Obj>
  <Obj RefId="33">
    <TNRef RefId="0" />
    <MS>
      <S N="ComputerName">CGE100LAP-42</S>
      <S N="InstanceName"></S>
      <S N="SqlInstance">CGE100LAP-42\SQLEXPRESS2016</S>
      <S N="Database">ContinuePointTest</S>
      <DT N="Start">2017-10-27T17:31:23</DT>
      <DT N="End">2017-10-27T17:31:23</DT>
      <Obj N="Duration" RefId="34">
        <TNRef RefId="1" />
        <ToString>00:00:00</ToString>
        <Props>
          <I32 N="Days">0</I32>
          <I32 N="Hours">0</I32>
          <I32 N="Milliseconds">0</I32>
          <I32 N="Minutes">0</I32>
          <I32 N="Seconds">0</I32>
          <I64 N="Ticks">0</I64>
          <Db N="TotalDays">0</Db>
          <Db N="TotalHours">0</Db>
          <Db N="TotalMilliseconds">0</Db>
          <Db N="TotalMinutes">0</Db>
          <Db N="TotalSeconds">0</Db>
        </Props>
      </Obj>
      <Obj N="Path" RefId="35">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\ContinuePointTest\ContinuePointTest2.bak</S>
        </LST>
      </Obj>
      <Nil N="TotalSize" />
      <S N="Type">Database Differential</S>
      <S N="BackupSetId">941a52a0-1325-41f5-87f3-1694f97fe507</S>
      <S N="DeviceType">Disk</S>
      <Nil N="Software" />
      <Obj N="FullName" RefId="36">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\ContinuePointTest\ContinuePointTest2.bak</S>
        </LST>
      </Obj>
      <Obj N="FileList" RefId="37">
        <TNRef RefId="3" />
        <LST>
          <Obj RefId="38">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">D</S>
              <S N="LogicalName">ContinuePointTest</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\DATA\ContinuePointTest.mdf</S>
            </MS>
          </Obj>
          <Obj RefId="39">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">L</S>
              <S N="LogicalName">ContinuePointTest_log</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\data\ContinuePointTest_log.ldf</S>
            </MS>
          </Obj>
        </LST>
      </Obj>
      <I32 N="Position">1</I32>
      <Obj N="FirstLsn" RefId="40">
        <TNRef RefId="5" />
        <ToString>34000000021200005</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="DatabaseBackupLsn" RefId="41">
        <TNRef RefId="5" />
        <ToString>34000000006800179</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="CheckpointLsn" RefId="42">
        <TNRef RefId="5" />
        <ToString>34000000021200005</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="LastLsn" RefId="43">
        <TNRef RefId="5" />
        <ToString>34000000021600001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <I32 N="SoftwareVersionMajor">13</I32>
      <B N="IsCopyOnly">false</B>
      <G N="LastRecoveryForkGUID">00000000-0000-0000-0000-000000000000</G>
      <S N="UserName">Stuart</S>
    </MS>
  </Obj>
  <Obj RefId="44">
    <TNRef RefId="0" />
    <MS>
      <S N="ComputerName">CGE100LAP-42</S>
      <S N="InstanceName"></S>
      <S N="SqlInstance">CGE100LAP-42\SQLEXPRESS2016</S>
      <S N="Database">ContinuePointTest</S>
      <DT N="Start">2017-10-27T17:30:33</DT>
      <DT N="End">2017-10-27T17:30:33</DT>
      <Obj N="Duration" RefId="45">
        <TNRef RefId="1" />
        <ToString>00:00:00</ToString>
        <Props>
          <I32 N="Days">0</I32>
          <I32 N="Hours">0</I32>
          <I32 N="Milliseconds">0</I32>
          <I32 N="Minutes">0</I32>
          <I32 N="Seconds">0</I32>
          <I64 N="Ticks">0</I64>
          <Db N="TotalDays">0</Db>
          <Db N="TotalHours">0</Db>
          <Db N="TotalMilliseconds">0</Db>
          <Db N="TotalMinutes">0</Db>
          <Db N="TotalSeconds">0</Db>
        </Props>
      </Obj>
      <Obj N="Path" RefId="46">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\ContinuePointTest\ContinuePointTest_2.trn</S>
        </LST>
      </Obj>
      <Nil N="TotalSize" />
      <S N="Type">Transaction Log</S>
      <S N="BackupSetId">ac5a75bf-9262-421c-8911-b2e38b0c5a4c</S>
      <S N="DeviceType">Disk</S>
      <Nil N="Software" />
      <Obj N="FullName" RefId="47">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\ContinuePointTest\ContinuePointTest_2.trn</S>
        </LST>
      </Obj>
      <Obj N="FileList" RefId="48">
        <TNRef RefId="3" />
        <LST>
          <Obj RefId="49">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">D</S>
              <S N="LogicalName">ContinuePointTest</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\DATA\ContinuePointTest.mdf</S>
            </MS>
          </Obj>
          <Obj RefId="50">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">L</S>
              <S N="LogicalName">ContinuePointTest_log</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\data\ContinuePointTest_log.ldf</S>
            </MS>
          </Obj>
        </LST>
      </Obj>
      <I32 N="Position">1</I32>
      <Obj N="FirstLsn" RefId="51">
        <TNRef RefId="5" />
        <ToString>34000000016700001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="DatabaseBackupLsn" RefId="52">
        <TNRef RefId="5" />
        <ToString>34000000006800179</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="CheckpointLsn" RefId="53">
        <TNRef RefId="5" />
        <ToString>34000000016900002</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">true</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="LastLsn" RefId="54">
        <TNRef RefId="5" />
        <ToString>34000000019600001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <I32 N="SoftwareVersionMajor">13</I32>
      <B N="IsCopyOnly">false</B>
      <G N="LastRecoveryForkGUID">00000000-0000-0000-0000-000000000000</G>
      <S N="UserName">Stuart</S>
    </MS>
  </Obj>
  <Obj RefId="55">
    <TNRef RefId="0" />
    <MS>
      <S N="ComputerName">CGE100LAP-42</S>
      <S N="InstanceName"></S>
      <S N="SqlInstance">CGE100LAP-42\SQLEXPRESS2016</S>
      <S N="Database">ContinuePointTest</S>
      <DT N="Start">2017-10-27T17:29:43</DT>
      <DT N="End">2017-10-27T17:29:43</DT>
      <Obj N="Duration" RefId="56">
        <TNRef RefId="1" />
        <ToString>00:00:00</ToString>
        <Props>
          <I32 N="Days">0</I32>
          <I32 N="Hours">0</I32>
          <I32 N="Milliseconds">0</I32>
          <I32 N="Minutes">0</I32>
          <I32 N="Seconds">0</I32>
          <I64 N="Ticks">0</I64>
          <Db N="TotalDays">0</Db>
          <Db N="TotalHours">0</Db>
          <Db N="TotalMilliseconds">0</Db>
          <Db N="TotalMinutes">0</Db>
          <Db N="TotalSeconds">0</Db>
        </Props>
      </Obj>
      <Obj N="Path" RefId="57">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\ContinuePointTest\ContinuePointTest_1.trn</S>
        </LST>
      </Obj>
      <Nil N="TotalSize" />
      <S N="Type">Transaction Log</S>
      <S N="BackupSetId">19e05f28-6482-4915-9fad-23d7045573a1</S>
      <S N="DeviceType">Disk</S>
      <Nil N="Software" />
      <Obj N="FullName" RefId="58">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\ContinuePointTest\ContinuePointTest_1.trn</S>
        </LST>
      </Obj>
      <Obj N="FileList" RefId="59">
        <TNRef RefId="3" />
        <LST>
          <Obj RefId="60">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">D</S>
              <S N="LogicalName">ContinuePointTest</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\DATA\ContinuePointTest.mdf</S>
            </MS>
          </Obj>
          <Obj RefId="61">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">L</S>
              <S N="LogicalName">ContinuePointTest_log</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\data\ContinuePointTest_log.ldf</S>
            </MS>
          </Obj>
        </LST>
      </Obj>
      <I32 N="Position">1</I32>
      <Obj N="FirstLsn" RefId="62">
        <TNRef RefId="5" />
        <ToString>34000000006800179</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="DatabaseBackupLsn" RefId="63">
        <TNRef RefId="5" />
        <ToString>34000000006800179</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="CheckpointLsn" RefId="64">
        <TNRef RefId="5" />
        <ToString>34000000006800179</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="LastLsn" RefId="65">
        <TNRef RefId="5" />
        <ToString>34000000016700001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <I32 N="SoftwareVersionMajor">13</I32>
      <B N="IsCopyOnly">false</B>
      <G N="LastRecoveryForkGUID">00000000-0000-0000-0000-000000000000</G>
      <S N="UserName">Stuart</S>
    </MS>
  </Obj>
  <Obj RefId="66">
    <TNRef RefId="0" />
    <MS>
      <S N="ComputerName">CGE100LAP-42</S>
      <S N="InstanceName"></S>
      <S N="SqlInstance">CGE100LAP-42\SQLEXPRESS2016</S>
      <S N="Database">ContinuePointTest</S>
      <DT N="Start">2017-10-27T17:28:53</DT>
      <DT N="End">2017-10-27T17:28:53</DT>
      <Obj N="Duration" RefId="67">
        <TNRef RefId="1" />
        <ToString>00:00:00</ToString>
        <Props>
          <I32 N="Days">0</I32>
          <I32 N="Hours">0</I32>
          <I32 N="Milliseconds">0</I32>
          <I32 N="Minutes">0</I32>
          <I32 N="Seconds">0</I32>
          <I64 N="Ticks">0</I64>
          <Db N="TotalDays">0</Db>
          <Db N="TotalHours">0</Db>
          <Db N="TotalMilliseconds">0</Db>
          <Db N="TotalMinutes">0</Db>
          <Db N="TotalSeconds">0</Db>
        </Props>
      </Obj>
      <Obj N="Path" RefId="68">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\ContinuePointTest\ContinuePointTest.bak</S>
        </LST>
      </Obj>
      <Nil N="TotalSize" />
      <S N="Type">Database</S>
      <S N="BackupSetId">71f603c5-d1fd-4c93-87be-b2f33f5c25de</S>
      <S N="DeviceType">Disk</S>
      <Nil N="Software" />
      <Obj N="FullName" RefId="69">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\ContinuePointTest\ContinuePointTest.bak</S>
        </LST>
      </Obj>
      <Obj N="FileList" RefId="70">
        <TNRef RefId="3" />
        <LST>
          <Obj RefId="71">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">D</S>
              <S N="LogicalName">ContinuePointTest</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\DATA\ContinuePointTest.mdf</S>
            </MS>
          </Obj>
          <Obj RefId="72">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">L</S>
              <S N="LogicalName">ContinuePointTest_log</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\data\ContinuePointTest_log.ldf</S>
            </MS>
          </Obj>
        </LST>
      </Obj>
      <I32 N="Position">1</I32>
      <Obj N="FirstLsn" RefId="73">
        <TNRef RefId="5" />
        <ToString>34000000006800179</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="DatabaseBackupLsn" RefId="74">
        <TNRef RefId="5" />
        <ToString>0</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">true</B>
          <B N="IsOne">false</B>
          <B N="IsEven">true</B>
          <I32 N="Sign">0</I32>
        </Props>
      </Obj>
      <Obj N="CheckpointLsn" RefId="75">
        <TNRef RefId="5" />
        <ToString>34000000006800179</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="LastLsn" RefId="76">
        <TNRef RefId="5" />
        <ToString>34000000014300001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <I32 N="SoftwareVersionMajor">13</I32>
      <B N="IsCopyOnly">false</B>
      <G N="LastRecoveryForkGUID">00000000-0000-0000-0000-000000000000</G>
      <S N="UserName">Stuart</S>
    </MS>
  </Obj>
</Objs>
tools\dbatools\tests\ObjectDefinitions\BackupRestore\RawInput\DiffIssues.json
[
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000010739200127,
        "LastLSN":  17126681000011151300001,
        "CheckpointLSN":  17126681000010739200127,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  17126658000000314600038,
        "BackupStartDate":  "\/Date(1500224400000)\/",
        "BackupFinishDate":  "\/Date(1500224501000)\/",
        "BackupTypeDescription":  "Database Differential",
        "BackupSetGUID":  "8ccfd67c-0969-4d74-89ef-0d2efc9d0284",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Database Differential",
        "End":  "\/Date(1500224501000)\/",
        "Start":  "\/Date(1500224400000)\/",
        "BackupSetId":  "8ccfd67c-0969-4d74-89ef-0d2efc9d0284",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126786000011858200224,
        "LastLSN":  17126787000007305800001,
        "CheckpointLSN":  17126786000011858200224,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  17126658000000314600038,
        "BackupStartDate":  "\/Date(1500310800000)\/",
        "BackupFinishDate":  "\/Date(1500311003000)\/",
        "BackupTypeDescription":  "Database Differential",
        "BackupSetGUID":  "b756c794-790e-4a37-b3cf-1ab15ac26247",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Database Differential",
        "End":  "\/Date(1500311003000)\/",
        "Start":  "\/Date(1500310800000)\/",
        "BackupSetId":  "b756c794-790e-4a37-b3cf-1ab15ac26247",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000000314600038,
        "LastLSN":  17126658000004509500001,
        "CheckpointLSN":  17126658000000314600038,
        "DatabaseBackupLSN":  17126050000002195100134,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500138000000)\/",
        "BackupFinishDate":  "\/Date(1500139557000)\/",
        "BackupTypeDescription":  "Database",
        "BackupSetGUID":  "8a14e614-1ee7-4b74-92c0-71c3a045fef9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Database",
        "End":  "\/Date(1500139557000)\/",
        "Start":  "\/Date(1500138000000)\/",
        "BackupSetId":  "8a14e614-1ee7-4b74-92c0-71c3a045fef9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126657000007603200001,
        "LastLSN":  17126657000008405600001,
        "CheckpointLSN":  17126657000006446100001,
        "DatabaseBackupLSN":  17126050000002195100134,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500136201000)\/",
        "BackupFinishDate":  "\/Date(1500136201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b102f57c-0830-4745-8f96-25872336018d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500136201000)\/",
        "Start":  "\/Date(1500136201000)\/",
        "BackupSetId":  "b102f57c-0830-4745-8f96-25872336018d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126657000008405600001,
        "LastLSN":  17126657000009239000001,
        "CheckpointLSN":  17126657000008515000001,
        "DatabaseBackupLSN":  17126050000002195100134,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500136500000)\/",
        "BackupFinishDate":  "\/Date(1500136500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2f2bbd3e-38e5-4a10-b5da-6dbf7abcc0c8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500136500000)\/",
        "Start":  "\/Date(1500136500000)\/",
        "BackupSetId":  "2f2bbd3e-38e5-4a10-b5da-6dbf7abcc0c8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126657000009239000001,
        "LastLSN":  17126657000010031800001,
        "CheckpointLSN":  17126657000008515000001,
        "DatabaseBackupLSN":  17126050000002195100134,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500136800000)\/",
        "BackupFinishDate":  "\/Date(1500136800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e47b6df9-1bcb-4b12-b00b-c657788a1684",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500136800000)\/",
        "Start":  "\/Date(1500136800000)\/",
        "BackupSetId":  "e47b6df9-1bcb-4b12-b00b-c657788a1684",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126657000010031800001,
        "LastLSN":  17126657000010852900001,
        "CheckpointLSN":  17126657000010586000001,
        "DatabaseBackupLSN":  17126050000002195100134,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500137100000)\/",
        "BackupFinishDate":  "\/Date(1500137100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e1059b75-c092-4bc2-99b3-212760dbf6a1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500137100000)\/",
        "Start":  "\/Date(1500137100000)\/",
        "BackupSetId":  "e1059b75-c092-4bc2-99b3-212760dbf6a1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126657000010852900001,
        "LastLSN":  17126657000011643300001,
        "CheckpointLSN":  17126657000010586000001,
        "DatabaseBackupLSN":  17126050000002195100134,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500137401000)\/",
        "BackupFinishDate":  "\/Date(1500137401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "df01f6ad-3414-4f7e-82d0-349cb8ab0392",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500137401000)\/",
        "Start":  "\/Date(1500137401000)\/",
        "BackupSetId":  "df01f6ad-3414-4f7e-82d0-349cb8ab0392",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126657000011643300001,
        "LastLSN":  17126657000012449000001,
        "CheckpointLSN":  17126657000010586000001,
        "DatabaseBackupLSN":  17126050000002195100134,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500137700000)\/",
        "BackupFinishDate":  "\/Date(1500137700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "38a3ce29-be3e-4e98-b6a4-ee6167cfff61",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500137700000)\/",
        "Start":  "\/Date(1500137700000)\/",
        "BackupSetId":  "38a3ce29-be3e-4e98-b6a4-ee6167cfff61",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126657000012449000001,
        "LastLSN":  17126658000000181500001,
        "CheckpointLSN":  17126657000012653200001,
        "DatabaseBackupLSN":  17126050000002195100134,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500138000000)\/",
        "BackupFinishDate":  "\/Date(1500138000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fefea32b-9398-48f0-b5fc-0676f57ddfdb",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500138000000)\/",
        "Start":  "\/Date(1500138000000)\/",
        "BackupSetId":  "fefea32b-9398-48f0-b5fc-0676f57ddfdb",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000000181500001,
        "LastLSN":  17126658000001119000001,
        "CheckpointLSN":  17126658000000314600038,
        "DatabaseBackupLSN":  17126050000002195100134,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500138300000)\/",
        "BackupFinishDate":  "\/Date(1500138300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d64aea20-a6fd-4271-88b5-e7dbab63dc80",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500138300000)\/",
        "Start":  "\/Date(1500138300000)\/",
        "BackupSetId":  "d64aea20-a6fd-4271-88b5-e7dbab63dc80",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000001119000001,
        "LastLSN":  17126658000001923900001,
        "CheckpointLSN":  17126658000000314600038,
        "DatabaseBackupLSN":  17126050000002195100134,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500138601000)\/",
        "BackupFinishDate":  "\/Date(1500138601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d85266f8-9332-4140-8100-bcf5144603e8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500138601000)\/",
        "Start":  "\/Date(1500138601000)\/",
        "BackupSetId":  "d85266f8-9332-4140-8100-bcf5144603e8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000001923900001,
        "LastLSN":  17126658000002735600001,
        "CheckpointLSN":  17126658000002375600001,
        "DatabaseBackupLSN":  17126050000002195100134,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500138901000)\/",
        "BackupFinishDate":  "\/Date(1500138901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7e30983e-a392-4860-9294-61aa012a64f6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500138901000)\/",
        "Start":  "\/Date(1500138901000)\/",
        "BackupSetId":  "7e30983e-a392-4860-9294-61aa012a64f6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000002735600001,
        "LastLSN":  17126658000003541500001,
        "CheckpointLSN":  17126658000002375600001,
        "DatabaseBackupLSN":  17126050000002195100134,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500139201000)\/",
        "BackupFinishDate":  "\/Date(1500139201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5a878541-29bc-4c3f-9e93-b46a25a156b1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500139201000)\/",
        "Start":  "\/Date(1500139201000)\/",
        "BackupSetId":  "5a878541-29bc-4c3f-9e93-b46a25a156b1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000003541500001,
        "LastLSN":  17126658000004334900001,
        "CheckpointLSN":  17126658000002375600001,
        "DatabaseBackupLSN":  17126050000002195100134,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500139501000)\/",
        "BackupFinishDate":  "\/Date(1500139501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7b32e695-d6a2-45e3-ad37-92427310cd0a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500139501000)\/",
        "Start":  "\/Date(1500139501000)\/",
        "BackupSetId":  "7b32e695-d6a2-45e3-ad37-92427310cd0a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000004334900001,
        "LastLSN":  17126658000005147400001,
        "CheckpointLSN":  17126658000004445000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500139801000)\/",
        "BackupFinishDate":  "\/Date(1500139801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "823cea78-03d6-4328-a3e9-77e77073bb15",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500139801000)\/",
        "Start":  "\/Date(1500139801000)\/",
        "BackupSetId":  "823cea78-03d6-4328-a3e9-77e77073bb15",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000005147400001,
        "LastLSN":  17126658000005940600001,
        "CheckpointLSN":  17126658000004445000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500140101000)\/",
        "BackupFinishDate":  "\/Date(1500140101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "956a6e34-fc5f-49dc-9cd5-70ee7ab48ab3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500140101000)\/",
        "Start":  "\/Date(1500140101000)\/",
        "BackupSetId":  "956a6e34-fc5f-49dc-9cd5-70ee7ab48ab3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000005940600001,
        "LastLSN":  17126658000006794100001,
        "CheckpointLSN":  17126658000006512600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500140401000)\/",
        "BackupFinishDate":  "\/Date(1500140401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "24360dd6-280b-4625-adb2-82586b3d1ff3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500140401000)\/",
        "Start":  "\/Date(1500140401000)\/",
        "BackupSetId":  "24360dd6-280b-4625-adb2-82586b3d1ff3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000006794100001,
        "LastLSN":  17126658000007617100001,
        "CheckpointLSN":  17126658000006512600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500140700000)\/",
        "BackupFinishDate":  "\/Date(1500140700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "32fa2f83-c532-4d3d-a396-29d01b955bcf",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500140700000)\/",
        "Start":  "\/Date(1500140700000)\/",
        "BackupSetId":  "32fa2f83-c532-4d3d-a396-29d01b955bcf",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000007617100001,
        "LastLSN":  17126658000008421500001,
        "CheckpointLSN":  17126658000006512600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500141001000)\/",
        "BackupFinishDate":  "\/Date(1500141001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "70d61bfa-9a60-4506-aef5-def0d90a3e90",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500141001000)\/",
        "Start":  "\/Date(1500141001000)\/",
        "BackupSetId":  "70d61bfa-9a60-4506-aef5-def0d90a3e90",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000008421500001,
        "LastLSN":  17126658000009259200001,
        "CheckpointLSN":  17126658000008581300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500141300000)\/",
        "BackupFinishDate":  "\/Date(1500141300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "55065563-c0bd-408b-974e-d7fcc779a20b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500141300000)\/",
        "Start":  "\/Date(1500141300000)\/",
        "BackupSetId":  "55065563-c0bd-408b-974e-d7fcc779a20b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000009259200001,
        "LastLSN":  17126658000010064800001,
        "CheckpointLSN":  17126658000008581300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500141600000)\/",
        "BackupFinishDate":  "\/Date(1500141600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c5e5803a-106f-48e8-8d61-14cf878e3d4c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500141600000)\/",
        "Start":  "\/Date(1500141600000)\/",
        "BackupSetId":  "c5e5803a-106f-48e8-8d61-14cf878e3d4c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000010064800001,
        "LastLSN":  17126658000010899700001,
        "CheckpointLSN":  17126658000010656400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500141900000)\/",
        "BackupFinishDate":  "\/Date(1500141900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ca157657-13c1-4aba-8464-8c826c84e4a2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500141900000)\/",
        "Start":  "\/Date(1500141900000)\/",
        "BackupSetId":  "ca157657-13c1-4aba-8464-8c826c84e4a2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000010899700001,
        "LastLSN":  17126658000011702700001,
        "CheckpointLSN":  17126658000010656400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500142200000)\/",
        "BackupFinishDate":  "\/Date(1500142201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f7f076a8-909c-4af6-8b1a-72c016d9cab0",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500142201000)\/",
        "Start":  "\/Date(1500142200000)\/",
        "BackupSetId":  "f7f076a8-909c-4af6-8b1a-72c016d9cab0",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000011702700001,
        "LastLSN":  17126658000012516800001,
        "CheckpointLSN":  17126658000010656400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500142500000)\/",
        "BackupFinishDate":  "\/Date(1500142500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9a74f843-3186-4ace-a092-b6f43007b281",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500142500000)\/",
        "Start":  "\/Date(1500142500000)\/",
        "BackupSetId":  "9a74f843-3186-4ace-a092-b6f43007b281",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126658000012516800001,
        "LastLSN":  17126659000000216700001,
        "CheckpointLSN":  17126658000012725700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500142800000)\/",
        "BackupFinishDate":  "\/Date(1500142800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8b3dfc41-c3f1-4248-ae74-f4304eac2524",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500142800000)\/",
        "Start":  "\/Date(1500142800000)\/",
        "BackupSetId":  "8b3dfc41-c3f1-4248-ae74-f4304eac2524",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126659000000216700001,
        "LastLSN":  17126659000001049300001,
        "CheckpointLSN":  17126659000000216700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500143100000)\/",
        "BackupFinishDate":  "\/Date(1500143100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "557d112b-5dcf-4c64-a330-1be79dc6da90",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500143100000)\/",
        "Start":  "\/Date(1500143100000)\/",
        "BackupSetId":  "557d112b-5dcf-4c64-a330-1be79dc6da90",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126659000001049300001,
        "LastLSN":  17126659000001853300001,
        "CheckpointLSN":  17126659000000216700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500143400000)\/",
        "BackupFinishDate":  "\/Date(1500143400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6208f520-0fb5-48bb-8ba4-f21d85bb1737",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500143400000)\/",
        "Start":  "\/Date(1500143400000)\/",
        "BackupSetId":  "6208f520-0fb5-48bb-8ba4-f21d85bb1737",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126659000001853300001,
        "LastLSN":  17126659000002688100001,
        "CheckpointLSN":  17126659000002280700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500143701000)\/",
        "BackupFinishDate":  "\/Date(1500143701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8a57d3d3-7507-449f-afed-5aa1c9f7d68f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500143701000)\/",
        "Start":  "\/Date(1500143701000)\/",
        "BackupSetId":  "8a57d3d3-7507-449f-afed-5aa1c9f7d68f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126659000002688100001,
        "LastLSN":  17126659000003480900001,
        "CheckpointLSN":  17126659000002280700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500144000000)\/",
        "BackupFinishDate":  "\/Date(1500144000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fe0f5db4-8014-435c-9f01-72527caffcf5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500144000000)\/",
        "Start":  "\/Date(1500144000000)\/",
        "BackupSetId":  "fe0f5db4-8014-435c-9f01-72527caffcf5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126659000003480900001,
        "LastLSN":  17126659000004283600001,
        "CheckpointLSN":  17126659000002280700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500144300000)\/",
        "BackupFinishDate":  "\/Date(1500144300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "afc843b0-f4b6-4ae9-acc5-7fca112c8c6c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500144300000)\/",
        "Start":  "\/Date(1500144300000)\/",
        "BackupSetId":  "afc843b0-f4b6-4ae9-acc5-7fca112c8c6c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126659000004283600001,
        "LastLSN":  17126659000005092200001,
        "CheckpointLSN":  17126659000004369700029,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500144600000)\/",
        "BackupFinishDate":  "\/Date(1500144600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "17a8897e-54f3-447e-aa7e-51545e0b9abc",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500144600000)\/",
        "Start":  "\/Date(1500144600000)\/",
        "BackupSetId":  "17a8897e-54f3-447e-aa7e-51545e0b9abc",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126659000005092200001,
        "LastLSN":  17126659000005897800001,
        "CheckpointLSN":  17126659000004369700029,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500144900000)\/",
        "BackupFinishDate":  "\/Date(1500144901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "bdaeebf1-26f7-4273-b718-1fd200403851",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500144901000)\/",
        "Start":  "\/Date(1500144900000)\/",
        "BackupSetId":  "bdaeebf1-26f7-4273-b718-1fd200403851",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126659000005897800001,
        "LastLSN":  17126659000006733600001,
        "CheckpointLSN":  17126659000006480800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500145200000)\/",
        "BackupFinishDate":  "\/Date(1500145200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3b8f6454-7928-43d8-a9dd-e29ab3133c3c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500145200000)\/",
        "Start":  "\/Date(1500145200000)\/",
        "BackupSetId":  "3b8f6454-7928-43d8-a9dd-e29ab3133c3c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126659000006733600001,
        "LastLSN":  17126659000007522100001,
        "CheckpointLSN":  17126659000006480800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500145500000)\/",
        "BackupFinishDate":  "\/Date(1500145500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b90fb423-f3b3-47d3-a58c-efab54ed085a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500145500000)\/",
        "Start":  "\/Date(1500145500000)\/",
        "BackupSetId":  "b90fb423-f3b3-47d3-a58c-efab54ed085a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126659000007522100001,
        "LastLSN":  17126659000008327300001,
        "CheckpointLSN":  17126659000006480800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500145800000)\/",
        "BackupFinishDate":  "\/Date(1500145800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "cefa4790-fd56-47ba-a04e-daf100b1cab5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500145800000)\/",
        "Start":  "\/Date(1500145800000)\/",
        "BackupSetId":  "cefa4790-fd56-47ba-a04e-daf100b1cab5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126659000008327300001,
        "LastLSN":  17126659000009133000001,
        "CheckpointLSN":  17126659000008552500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500146100000)\/",
        "BackupFinishDate":  "\/Date(1500146100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4e9e5a3b-0b58-49b8-b92a-89703af99600",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500146100000)\/",
        "Start":  "\/Date(1500146100000)\/",
        "BackupSetId":  "4e9e5a3b-0b58-49b8-b92a-89703af99600",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126659000009133000001,
        "LastLSN":  17126659000009935800001,
        "CheckpointLSN":  17126659000008552500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500146400000)\/",
        "BackupFinishDate":  "\/Date(1500146400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "77437117-7e97-4294-a5f8-c46655a7649e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500146400000)\/",
        "Start":  "\/Date(1500146400000)\/",
        "BackupSetId":  "77437117-7e97-4294-a5f8-c46655a7649e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126659000009935800001,
        "LastLSN":  17126659000010746900001,
        "CheckpointLSN":  17126659000010636900031,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500146700000)\/",
        "BackupFinishDate":  "\/Date(1500146700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "420c6440-8f63-4f3f-9236-482dbdf0c5e2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500146700000)\/",
        "Start":  "\/Date(1500146700000)\/",
        "BackupSetId":  "420c6440-8f63-4f3f-9236-482dbdf0c5e2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126659000010746900001,
        "LastLSN":  17126659000011537000001,
        "CheckpointLSN":  17126659000010636900031,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500147000000)\/",
        "BackupFinishDate":  "\/Date(1500147000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b7fe7c92-785c-4b08-963e-05fc295709d4",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500147000000)\/",
        "Start":  "\/Date(1500147000000)\/",
        "BackupSetId":  "b7fe7c92-785c-4b08-963e-05fc295709d4",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126659000011537000001,
        "LastLSN":  17126659000012353200001,
        "CheckpointLSN":  17126659000010636900031,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500147301000)\/",
        "BackupFinishDate":  "\/Date(1500147301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e055914d-4608-44ab-88f7-46806dc8732b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500147301000)\/",
        "Start":  "\/Date(1500147301000)\/",
        "BackupSetId":  "e055914d-4608-44ab-88f7-46806dc8732b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126659000012353200001,
        "LastLSN":  17126660000000093400001,
        "CheckpointLSN":  17126659000012783100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500147600000)\/",
        "BackupFinishDate":  "\/Date(1500147600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a5acc570-eb24-4412-a684-42c3fdb3fe44",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500147600000)\/",
        "Start":  "\/Date(1500147600000)\/",
        "BackupSetId":  "a5acc570-eb24-4412-a684-42c3fdb3fe44",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126660000000093400001,
        "LastLSN":  17126660000000938600001,
        "CheckpointLSN":  17126660000000093400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500147900000)\/",
        "BackupFinishDate":  "\/Date(1500147900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "791ee6e2-f9ea-4920-a5d4-388dab35e9a9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500147900000)\/",
        "Start":  "\/Date(1500147900000)\/",
        "BackupSetId":  "791ee6e2-f9ea-4920-a5d4-388dab35e9a9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126660000000938600001,
        "LastLSN":  17126660000001743000001,
        "CheckpointLSN":  17126660000000093400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500148200000)\/",
        "BackupFinishDate":  "\/Date(1500148200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "95af85e5-25fb-4d6c-a116-6ac6f60a32fb",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500148200000)\/",
        "Start":  "\/Date(1500148200000)\/",
        "BackupSetId":  "95af85e5-25fb-4d6c-a116-6ac6f60a32fb",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126660000001743000001,
        "LastLSN":  17126660000002581700001,
        "CheckpointLSN":  17126660000002159100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500148500000)\/",
        "BackupFinishDate":  "\/Date(1500148500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c84a2b73-1fe9-43be-8d57-1f7264d1740c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500148500000)\/",
        "Start":  "\/Date(1500148500000)\/",
        "BackupSetId":  "c84a2b73-1fe9-43be-8d57-1f7264d1740c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126660000002581700001,
        "LastLSN":  17126660000003385400001,
        "CheckpointLSN":  17126660000002159100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500148800000)\/",
        "BackupFinishDate":  "\/Date(1500148800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "db59d738-51c0-40e7-8352-a5351412f89c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500148800000)\/",
        "Start":  "\/Date(1500148800000)\/",
        "BackupSetId":  "db59d738-51c0-40e7-8352-a5351412f89c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126660000003385400001,
        "LastLSN":  17126660000004285700001,
        "CheckpointLSN":  17126660000004236300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500149100000)\/",
        "BackupFinishDate":  "\/Date(1500149100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f850ccf6-fa68-4fe9-aa71-50bff9483de8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500149100000)\/",
        "Start":  "\/Date(1500149100000)\/",
        "BackupSetId":  "f850ccf6-fa68-4fe9-aa71-50bff9483de8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126660000004285700001,
        "LastLSN":  17126660000005090300001,
        "CheckpointLSN":  17126660000004236300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500149400000)\/",
        "BackupFinishDate":  "\/Date(1500149401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "430fe02a-3770-49d4-bfe9-5d811fcb8449",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500149401000)\/",
        "Start":  "\/Date(1500149400000)\/",
        "BackupSetId":  "430fe02a-3770-49d4-bfe9-5d811fcb8449",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126660000005090300001,
        "LastLSN":  17126660000005905900001,
        "CheckpointLSN":  17126660000004236300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500149701000)\/",
        "BackupFinishDate":  "\/Date(1500149701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8538bee9-4d9f-402f-9f6e-2a6f21fb1697",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500149701000)\/",
        "Start":  "\/Date(1500149701000)\/",
        "BackupSetId":  "8538bee9-4d9f-402f-9f6e-2a6f21fb1697",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126660000005905900001,
        "LastLSN":  17126660000006714900001,
        "CheckpointLSN":  17126660000006312800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500150000000)\/",
        "BackupFinishDate":  "\/Date(1500150000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "cfbddc3d-8d42-444e-a4d8-0ac24186a86a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500150000000)\/",
        "Start":  "\/Date(1500150000000)\/",
        "BackupSetId":  "cfbddc3d-8d42-444e-a4d8-0ac24186a86a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126660000006714900001,
        "LastLSN":  17126660000007536300001,
        "CheckpointLSN":  17126660000006312800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500150300000)\/",
        "BackupFinishDate":  "\/Date(1500150300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b473add3-eed5-4b26-acc1-d9c252556730",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500150300000)\/",
        "Start":  "\/Date(1500150300000)\/",
        "BackupSetId":  "b473add3-eed5-4b26-acc1-d9c252556730",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126660000007536300001,
        "LastLSN":  17126660000008329200001,
        "CheckpointLSN":  17126660000006312800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500150600000)\/",
        "BackupFinishDate":  "\/Date(1500150600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fa0163e2-de31-480a-92ab-16495a25bb35",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500150600000)\/",
        "Start":  "\/Date(1500150600000)\/",
        "BackupSetId":  "fa0163e2-de31-480a-92ab-16495a25bb35",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126660000008329200001,
        "LastLSN":  17126660000009166900001,
        "CheckpointLSN":  17126660000008382400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500150901000)\/",
        "BackupFinishDate":  "\/Date(1500150901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "af0bc4e1-7e9e-4c9a-9494-cf81213d3af5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500150901000)\/",
        "Start":  "\/Date(1500150901000)\/",
        "BackupSetId":  "af0bc4e1-7e9e-4c9a-9494-cf81213d3af5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126660000009166900001,
        "LastLSN":  17126660000009959700001,
        "CheckpointLSN":  17126660000008382400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500151200000)\/",
        "BackupFinishDate":  "\/Date(1500151200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fca9bf01-c3b6-4372-b7bb-9e5116199c74",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500151200000)\/",
        "Start":  "\/Date(1500151200000)\/",
        "BackupSetId":  "fca9bf01-c3b6-4372-b7bb-9e5116199c74",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126660000009959700001,
        "LastLSN":  17126660000010777600001,
        "CheckpointLSN":  17126660000010462900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500151500000)\/",
        "BackupFinishDate":  "\/Date(1500151500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "da3c3063-a343-41a0-8016-77bca6f2f9fc",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500151500000)\/",
        "Start":  "\/Date(1500151500000)\/",
        "BackupSetId":  "da3c3063-a343-41a0-8016-77bca6f2f9fc",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126660000010777600001,
        "LastLSN":  17126660000011568300001,
        "CheckpointLSN":  17126660000010462900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500151800000)\/",
        "BackupFinishDate":  "\/Date(1500151800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1aab51df-0c93-4d6d-a5b7-a72864441c4a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500151800000)\/",
        "Start":  "\/Date(1500151800000)\/",
        "BackupSetId":  "1aab51df-0c93-4d6d-a5b7-a72864441c4a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126660000011568300001,
        "LastLSN":  17126660000012371700001,
        "CheckpointLSN":  17126660000010462900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500152100000)\/",
        "BackupFinishDate":  "\/Date(1500152101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4a8a6f72-e93c-4aed-832a-e253df31c284",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500152101000)\/",
        "Start":  "\/Date(1500152100000)\/",
        "BackupSetId":  "4a8a6f72-e93c-4aed-832a-e253df31c284",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126660000012371700001,
        "LastLSN":  17126661000000105600001,
        "CheckpointLSN":  17126660000012545900004,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500152401000)\/",
        "BackupFinishDate":  "\/Date(1500152401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "63f3fd30-023f-4fe3-9172-fac735abc995",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500152401000)\/",
        "Start":  "\/Date(1500152401000)\/",
        "BackupSetId":  "63f3fd30-023f-4fe3-9172-fac735abc995",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000000105600001,
        "LastLSN":  17126661000000917100001,
        "CheckpointLSN":  17126661000000105600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500152701000)\/",
        "BackupFinishDate":  "\/Date(1500152701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b9c56fd4-e340-495f-b49f-2c07099d70fa",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500152701000)\/",
        "Start":  "\/Date(1500152701000)\/",
        "BackupSetId":  "b9c56fd4-e340-495f-b49f-2c07099d70fa",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000000917100001,
        "LastLSN":  17126661000001719300001,
        "CheckpointLSN":  17126661000000105600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500153000000)\/",
        "BackupFinishDate":  "\/Date(1500153000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "02643266-0df5-4ac9-a5af-387c75bb283d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500153000000)\/",
        "Start":  "\/Date(1500153000000)\/",
        "BackupSetId":  "02643266-0df5-4ac9-a5af-387c75bb283d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000001719300001,
        "LastLSN":  17126661000002525600001,
        "CheckpointLSN":  17126661000002176300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500153300000)\/",
        "BackupFinishDate":  "\/Date(1500153300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "468736c3-c396-43c7-b173-bf5ed6701840",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500153300000)\/",
        "Start":  "\/Date(1500153300000)\/",
        "BackupSetId":  "468736c3-c396-43c7-b173-bf5ed6701840",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000002525600001,
        "LastLSN":  17126661000003328800001,
        "CheckpointLSN":  17126661000002176300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500153601000)\/",
        "BackupFinishDate":  "\/Date(1500153601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "61d64fca-7869-4a13-a97a-0f0a7dd799fd",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500153601000)\/",
        "Start":  "\/Date(1500153601000)\/",
        "BackupSetId":  "61d64fca-7869-4a13-a97a-0f0a7dd799fd",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000003328800001,
        "LastLSN":  17126661000003733400001,
        "CheckpointLSN":  17126661000002176300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500153900000)\/",
        "BackupFinishDate":  "\/Date(1500153900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "37038563-9c5c-44a6-8e80-47aa1b3eae86",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500153900000)\/",
        "Start":  "\/Date(1500153900000)\/",
        "BackupSetId":  "37038563-9c5c-44a6-8e80-47aa1b3eae86",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000003733400001,
        "LastLSN":  17126661000004161000001,
        "CheckpointLSN":  17126661000002176300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500154203000)\/",
        "BackupFinishDate":  "\/Date(1500154203000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8c3f94ac-4972-406a-beaf-31961b8767cd",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500154203000)\/",
        "Start":  "\/Date(1500154203000)\/",
        "BackupSetId":  "8c3f94ac-4972-406a-beaf-31961b8767cd",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000004161000001,
        "LastLSN":  17126661000004617200001,
        "CheckpointLSN":  17126661000004258900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500154503000)\/",
        "BackupFinishDate":  "\/Date(1500154503000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8be53728-8c53-4ca1-8d7d-0a0ebff69bcb",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500154503000)\/",
        "Start":  "\/Date(1500154503000)\/",
        "BackupSetId":  "8be53728-8c53-4ca1-8d7d-0a0ebff69bcb",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000004617200001,
        "LastLSN":  17126661000005179900001,
        "CheckpointLSN":  17126661000004258900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500154801000)\/",
        "BackupFinishDate":  "\/Date(1500154801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3dcdcb4f-2937-4bb7-ba79-f95c6a3c87e5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500154801000)\/",
        "Start":  "\/Date(1500154801000)\/",
        "BackupSetId":  "3dcdcb4f-2937-4bb7-ba79-f95c6a3c87e5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000005179900001,
        "LastLSN":  17126661000005981000001,
        "CheckpointLSN":  17126661000004258900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500155101000)\/",
        "BackupFinishDate":  "\/Date(1500155101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e218fb19-1447-4a4e-810e-399f6e0e7df0",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500155101000)\/",
        "Start":  "\/Date(1500155101000)\/",
        "BackupSetId":  "e218fb19-1447-4a4e-810e-399f6e0e7df0",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000005981000001,
        "LastLSN":  17126661000006803200001,
        "CheckpointLSN":  17126661000006534000028,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500155401000)\/",
        "BackupFinishDate":  "\/Date(1500155401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "69a831a2-f9c6-4e61-9cf8-42650ecdac1a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500155401000)\/",
        "Start":  "\/Date(1500155401000)\/",
        "BackupSetId":  "69a831a2-f9c6-4e61-9cf8-42650ecdac1a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000006803200001,
        "LastLSN":  17126661000007620600001,
        "CheckpointLSN":  17126661000006534000028,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500155700000)\/",
        "BackupFinishDate":  "\/Date(1500155700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f5ff034f-2ff9-4520-8c11-cb67e1ac43d3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500155700000)\/",
        "Start":  "\/Date(1500155700000)\/",
        "BackupSetId":  "f5ff034f-2ff9-4520-8c11-cb67e1ac43d3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000007620600001,
        "LastLSN":  17126661000008422500001,
        "CheckpointLSN":  17126661000006534000028,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500156000000)\/",
        "BackupFinishDate":  "\/Date(1500156000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "546afe4a-c2aa-4ca7-844b-928ce5df6efe",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500156000000)\/",
        "Start":  "\/Date(1500156000000)\/",
        "BackupSetId":  "546afe4a-c2aa-4ca7-844b-928ce5df6efe",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000008422500001,
        "LastLSN":  17126661000009256200001,
        "CheckpointLSN":  17126661000008610500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500156300000)\/",
        "BackupFinishDate":  "\/Date(1500156300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8afd1eb2-fc0b-416b-ae5f-66b99506c049",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500156300000)\/",
        "Start":  "\/Date(1500156300000)\/",
        "BackupSetId":  "8afd1eb2-fc0b-416b-ae5f-66b99506c049",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000009256200001,
        "LastLSN":  17126661000010059500001,
        "CheckpointLSN":  17126661000008610500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500156600000)\/",
        "BackupFinishDate":  "\/Date(1500156600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "be7011af-866c-41e8-bdf0-0c9637089b4d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500156600000)\/",
        "Start":  "\/Date(1500156600000)\/",
        "BackupSetId":  "be7011af-866c-41e8-bdf0-0c9637089b4d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000010059500001,
        "LastLSN":  17126661000010895200001,
        "CheckpointLSN":  17126661000010688800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500156901000)\/",
        "BackupFinishDate":  "\/Date(1500156901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d59f69c1-8654-4158-8afb-2fc5a30dea38",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500156901000)\/",
        "Start":  "\/Date(1500156901000)\/",
        "BackupSetId":  "d59f69c1-8654-4158-8afb-2fc5a30dea38",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000010895200001,
        "LastLSN":  17126661000011689200001,
        "CheckpointLSN":  17126661000010688800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500157201000)\/",
        "BackupFinishDate":  "\/Date(1500157201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f31f21c8-c168-42d6-9770-b6ee0d1f2a23",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500157201000)\/",
        "Start":  "\/Date(1500157201000)\/",
        "BackupSetId":  "f31f21c8-c168-42d6-9770-b6ee0d1f2a23",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000011689200001,
        "LastLSN":  17126661000012503200001,
        "CheckpointLSN":  17126661000010688800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500157501000)\/",
        "BackupFinishDate":  "\/Date(1500157501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3d0ee49f-6fa3-4a89-8d96-c14a4f3e1dc5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500157501000)\/",
        "Start":  "\/Date(1500157501000)\/",
        "BackupSetId":  "3d0ee49f-6fa3-4a89-8d96-c14a4f3e1dc5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126661000012503200001,
        "LastLSN":  17126662000000220800001,
        "CheckpointLSN":  17126661000012789500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500157800000)\/",
        "BackupFinishDate":  "\/Date(1500157800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c1f34952-6b9d-4e18-94a0-a9069f53074d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500157800000)\/",
        "Start":  "\/Date(1500157800000)\/",
        "BackupSetId":  "c1f34952-6b9d-4e18-94a0-a9069f53074d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126662000000220800001,
        "LastLSN":  17126662000001055800001,
        "CheckpointLSN":  17126662000000220800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500158100000)\/",
        "BackupFinishDate":  "\/Date(1500158100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6174c748-72ed-4891-ab0f-d65ce7100c00",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500158100000)\/",
        "Start":  "\/Date(1500158100000)\/",
        "BackupSetId":  "6174c748-72ed-4891-ab0f-d65ce7100c00",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126662000001055800001,
        "LastLSN":  17126662000001847500001,
        "CheckpointLSN":  17126662000000220800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500158400000)\/",
        "BackupFinishDate":  "\/Date(1500158400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f249f25b-268c-40d8-b94a-acab3a8dc973",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500158400000)\/",
        "Start":  "\/Date(1500158400000)\/",
        "BackupSetId":  "f249f25b-268c-40d8-b94a-acab3a8dc973",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126662000001847500001,
        "LastLSN":  17126662000002669200001,
        "CheckpointLSN":  17126662000002288300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500158700000)\/",
        "BackupFinishDate":  "\/Date(1500158700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4d89fc7c-694b-4b5c-80eb-aa2e56d1e478",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500158700000)\/",
        "Start":  "\/Date(1500158700000)\/",
        "BackupSetId":  "4d89fc7c-694b-4b5c-80eb-aa2e56d1e478",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126662000002669200001,
        "LastLSN":  17126662000003461300001,
        "CheckpointLSN":  17126662000002288300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500159000000)\/",
        "BackupFinishDate":  "\/Date(1500159000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "da2be1d7-43a9-4863-98f8-d94b35420fdf",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500159000000)\/",
        "Start":  "\/Date(1500159000000)\/",
        "BackupSetId":  "da2be1d7-43a9-4863-98f8-d94b35420fdf",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126662000003461300001,
        "LastLSN":  17126662000004266500001,
        "CheckpointLSN":  17126662000002288300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500159301000)\/",
        "BackupFinishDate":  "\/Date(1500159301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "360ed6f2-3344-410a-abc4-6467bf06dc8f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500159301000)\/",
        "Start":  "\/Date(1500159301000)\/",
        "BackupSetId":  "360ed6f2-3344-410a-abc4-6467bf06dc8f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126662000004266500001,
        "LastLSN":  17126662000005101200001,
        "CheckpointLSN":  17126662000004356900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500159601000)\/",
        "BackupFinishDate":  "\/Date(1500159601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0c5ea9e3-7c5e-499f-bdfc-8b8cb536f83f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500159601000)\/",
        "Start":  "\/Date(1500159601000)\/",
        "BackupSetId":  "0c5ea9e3-7c5e-499f-bdfc-8b8cb536f83f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126662000005101200001,
        "LastLSN":  17126662000011164400001,
        "CheckpointLSN":  17126662000010456500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500159900000)\/",
        "BackupFinishDate":  "\/Date(1500159900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1b13ffe8-13af-480d-af09-669e938b5488",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500159900000)\/",
        "Start":  "\/Date(1500159900000)\/",
        "BackupSetId":  "1b13ffe8-13af-480d-af09-669e938b5488",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126662000011164400001,
        "LastLSN":  17126662000011967700001,
        "CheckpointLSN":  17126662000010456500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500160200000)\/",
        "BackupFinishDate":  "\/Date(1500160200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e575d400-be12-4981-b5a1-1598d59c18ef",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500160200000)\/",
        "Start":  "\/Date(1500160200000)\/",
        "BackupSetId":  "e575d400-be12-4981-b5a1-1598d59c18ef",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126662000011967700001,
        "LastLSN":  17126662000012779700001,
        "CheckpointLSN":  17126662000012523800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500160500000)\/",
        "BackupFinishDate":  "\/Date(1500160500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8fda1df6-fdd6-42c3-99a9-d5b087be0d13",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500160500000)\/",
        "Start":  "\/Date(1500160500000)\/",
        "BackupSetId":  "8fda1df6-fdd6-42c3-99a9-d5b087be0d13",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126662000012779700001,
        "LastLSN":  17126663000000478700001,
        "CheckpointLSN":  17126662000012523800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500160800000)\/",
        "BackupFinishDate":  "\/Date(1500160800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "898643a5-c36c-4894-bb9e-d2bad9effcf3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500160800000)\/",
        "Start":  "\/Date(1500160800000)\/",
        "BackupSetId":  "898643a5-c36c-4894-bb9e-d2bad9effcf3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126663000000478700001,
        "LastLSN":  17126663000001289100001,
        "CheckpointLSN":  17126663000000478700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500161101000)\/",
        "BackupFinishDate":  "\/Date(1500161101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8b9ea978-7803-44be-af28-91158c5d303d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500161101000)\/",
        "Start":  "\/Date(1500161101000)\/",
        "BackupSetId":  "8b9ea978-7803-44be-af28-91158c5d303d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126663000001289100001,
        "LastLSN":  17126663000002079700001,
        "CheckpointLSN":  17126663000000478700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500161400000)\/",
        "BackupFinishDate":  "\/Date(1500161400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "58ecd0ff-e967-47e7-a12c-cd11138f5a6c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500161400000)\/",
        "Start":  "\/Date(1500161400000)\/",
        "BackupSetId":  "58ecd0ff-e967-47e7-a12c-cd11138f5a6c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126663000002079700001,
        "LastLSN":  17126664000000776300001,
        "CheckpointLSN":  17126664000000260100005,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500161700000)\/",
        "BackupFinishDate":  "\/Date(1500161701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "44288c06-dc71-4131-bded-800c7d11a893",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500161701000)\/",
        "Start":  "\/Date(1500161700000)\/",
        "BackupSetId":  "44288c06-dc71-4131-bded-800c7d11a893",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126664000000776300001,
        "LastLSN":  17126665000001904300001,
        "CheckpointLSN":  17126665000001742200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500162000000)\/",
        "BackupFinishDate":  "\/Date(1500162001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fff80027-431f-469e-894c-2ea22b04984b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500162001000)\/",
        "Start":  "\/Date(1500162000000)\/",
        "BackupSetId":  "fff80027-431f-469e-894c-2ea22b04984b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126665000001904300001,
        "LastLSN":  17126666000003628600001,
        "CheckpointLSN":  17126666000000786700002,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500162300000)\/",
        "BackupFinishDate":  "\/Date(1500162301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "feabb09e-01be-41a8-b543-11159dadb85e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500162301000)\/",
        "Start":  "\/Date(1500162300000)\/",
        "BackupSetId":  "feabb09e-01be-41a8-b543-11159dadb85e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126666000003628600001,
        "LastLSN":  17126669000001952600001,
        "CheckpointLSN":  17126669000000629200002,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500162601000)\/",
        "BackupFinishDate":  "\/Date(1500162601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5d82631a-c50f-4d21-8b88-66d1212594d6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500162601000)\/",
        "Start":  "\/Date(1500162601000)\/",
        "BackupSetId":  "5d82631a-c50f-4d21-8b88-66d1212594d6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126669000001952600001,
        "LastLSN":  17126669000002772300001,
        "CheckpointLSN":  17126669000000629200002,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500162900000)\/",
        "BackupFinishDate":  "\/Date(1500162900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "bc61f0bc-66e8-4ce4-985a-b25a9ca2173f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500162900000)\/",
        "Start":  "\/Date(1500162900000)\/",
        "BackupSetId":  "bc61f0bc-66e8-4ce4-985a-b25a9ca2173f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126669000002772300001,
        "LastLSN":  17126669000003610400001,
        "CheckpointLSN":  17126669000002788000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500163200000)\/",
        "BackupFinishDate":  "\/Date(1500163200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e0df9982-b6d6-4824-9305-cfce51101acb",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500163200000)\/",
        "Start":  "\/Date(1500163200000)\/",
        "BackupSetId":  "e0df9982-b6d6-4824-9305-cfce51101acb",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126669000003610400001,
        "LastLSN":  17126669000004427300001,
        "CheckpointLSN":  17126669000002788000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500163500000)\/",
        "BackupFinishDate":  "\/Date(1500163500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9abdb23c-ee24-49e2-bd06-88ae1cbd9f19",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500163500000)\/",
        "Start":  "\/Date(1500163500000)\/",
        "BackupSetId":  "9abdb23c-ee24-49e2-bd06-88ae1cbd9f19",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126669000004427300001,
        "LastLSN":  17126669000005250700001,
        "CheckpointLSN":  17126669000004885200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500163800000)\/",
        "BackupFinishDate":  "\/Date(1500163800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2ea2ed1a-a789-411f-aa4c-615119294db4",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500163800000)\/",
        "Start":  "\/Date(1500163800000)\/",
        "BackupSetId":  "2ea2ed1a-a789-411f-aa4c-615119294db4",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126669000005250700001,
        "LastLSN":  17126669000006068900001,
        "CheckpointLSN":  17126669000004885200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500164100000)\/",
        "BackupFinishDate":  "\/Date(1500164101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "169e9131-ffe3-4c91-8cf9-0463ffe3b8b7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500164101000)\/",
        "Start":  "\/Date(1500164100000)\/",
        "BackupSetId":  "169e9131-ffe3-4c91-8cf9-0463ffe3b8b7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126669000006068900001,
        "LastLSN":  17126669000006861300001,
        "CheckpointLSN":  17126669000004885200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500164400000)\/",
        "BackupFinishDate":  "\/Date(1500164400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9d72fd8a-717b-4520-b643-edb01d8f8339",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500164400000)\/",
        "Start":  "\/Date(1500164400000)\/",
        "BackupSetId":  "9d72fd8a-717b-4520-b643-edb01d8f8339",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126669000006861300001,
        "LastLSN":  17126669000007699200001,
        "CheckpointLSN":  17126669000006999600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500164700000)\/",
        "BackupFinishDate":  "\/Date(1500164700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a5d3f401-9583-48b8-8657-60e557f6391c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500164700000)\/",
        "Start":  "\/Date(1500164700000)\/",
        "BackupSetId":  "a5d3f401-9583-48b8-8657-60e557f6391c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126669000007699200001,
        "LastLSN":  17126669000008506200001,
        "CheckpointLSN":  17126669000006999600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500165000000)\/",
        "BackupFinishDate":  "\/Date(1500165000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a5f29546-a881-47c5-9f05-dc6d2e61e4d6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500165000000)\/",
        "Start":  "\/Date(1500165000000)\/",
        "BackupSetId":  "a5f29546-a881-47c5-9f05-dc6d2e61e4d6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126669000008506200001,
        "LastLSN":  17126669000009342200001,
        "CheckpointLSN":  17126669000009069000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500165300000)\/",
        "BackupFinishDate":  "\/Date(1500165300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "43540116-96fc-4f4d-a305-9ec311bcb20f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500165300000)\/",
        "Start":  "\/Date(1500165300000)\/",
        "BackupSetId":  "43540116-96fc-4f4d-a305-9ec311bcb20f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126669000009342200001,
        "LastLSN":  17126669000010137500001,
        "CheckpointLSN":  17126669000009069000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500165600000)\/",
        "BackupFinishDate":  "\/Date(1500165601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e95383af-86f5-4afa-951f-a491f6acb4b3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500165601000)\/",
        "Start":  "\/Date(1500165600000)\/",
        "BackupSetId":  "e95383af-86f5-4afa-951f-a491f6acb4b3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126669000010137500001,
        "LastLSN":  17126669000010938800001,
        "CheckpointLSN":  17126669000009069000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500165900000)\/",
        "BackupFinishDate":  "\/Date(1500165900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "984dad94-24c1-4db8-932a-f17a004f9eb5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500165900000)\/",
        "Start":  "\/Date(1500165900000)\/",
        "BackupSetId":  "984dad94-24c1-4db8-932a-f17a004f9eb5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126669000010938800001,
        "LastLSN":  17126669000011750200001,
        "CheckpointLSN":  17126669000011136700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500166200000)\/",
        "BackupFinishDate":  "\/Date(1500166200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7c18e157-f418-4406-a8e3-1f0a16aa8ce2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500166200000)\/",
        "Start":  "\/Date(1500166200000)\/",
        "BackupSetId":  "7c18e157-f418-4406-a8e3-1f0a16aa8ce2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126669000011750200001,
        "LastLSN":  17126669000012557300001,
        "CheckpointLSN":  17126669000011136700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500166500000)\/",
        "BackupFinishDate":  "\/Date(1500166500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "74e04bab-d592-439d-b9be-6d3074897980",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500166500000)\/",
        "Start":  "\/Date(1500166500000)\/",
        "BackupSetId":  "74e04bab-d592-439d-b9be-6d3074897980",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126669000012557300001,
        "LastLSN":  17126670000000286800001,
        "CheckpointLSN":  17126670000000105700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500166800000)\/",
        "BackupFinishDate":  "\/Date(1500166801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ed20b933-1f60-41ec-b6f9-5ee00e366f89",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500166801000)\/",
        "Start":  "\/Date(1500166800000)\/",
        "BackupSetId":  "ed20b933-1f60-41ec-b6f9-5ee00e366f89",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126670000000286800001,
        "LastLSN":  17126670000001080100001,
        "CheckpointLSN":  17126670000000105700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500167101000)\/",
        "BackupFinishDate":  "\/Date(1500167101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a0e0e24a-235c-4147-bd9e-9c2859912f8b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500167101000)\/",
        "Start":  "\/Date(1500167101000)\/",
        "BackupSetId":  "a0e0e24a-235c-4147-bd9e-9c2859912f8b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126670000001080100001,
        "LastLSN":  17126670000001881600001,
        "CheckpointLSN":  17126670000000105700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500167400000)\/",
        "BackupFinishDate":  "\/Date(1500167400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "17dbe046-7403-4bf4-a02f-fa1de48fa0a1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500167400000)\/",
        "Start":  "\/Date(1500167400000)\/",
        "BackupSetId":  "17dbe046-7403-4bf4-a02f-fa1de48fa0a1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126670000001881600001,
        "LastLSN":  17126670000002693200001,
        "CheckpointLSN":  17126670000002193200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500167700000)\/",
        "BackupFinishDate":  "\/Date(1500167700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fa1e3e4e-d4ba-4f11-9dc7-a0e47373d684",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500167700000)\/",
        "Start":  "\/Date(1500167700000)\/",
        "BackupSetId":  "fa1e3e4e-d4ba-4f11-9dc7-a0e47373d684",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126670000002693200001,
        "LastLSN":  17126670000003497400001,
        "CheckpointLSN":  17126670000002193200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500168000000)\/",
        "BackupFinishDate":  "\/Date(1500168001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "256d838e-d5ea-490e-83d3-6c13135bafb4",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500168001000)\/",
        "Start":  "\/Date(1500168000000)\/",
        "BackupSetId":  "256d838e-d5ea-490e-83d3-6c13135bafb4",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126670000003497400001,
        "LastLSN":  17126670000004307300001,
        "CheckpointLSN":  17126670000004263000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500168300000)\/",
        "BackupFinishDate":  "\/Date(1500168300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "356a5869-dc46-418b-adb8-dd8aad353d06",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500168300000)\/",
        "Start":  "\/Date(1500168300000)\/",
        "BackupSetId":  "356a5869-dc46-418b-adb8-dd8aad353d06",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126670000004307300001,
        "LastLSN":  17126670000005099100001,
        "CheckpointLSN":  17126670000004263000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500168601000)\/",
        "BackupFinishDate":  "\/Date(1500168601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "915e8418-b4d1-4312-b82e-37e4682ef3e6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500168601000)\/",
        "Start":  "\/Date(1500168601000)\/",
        "BackupSetId":  "915e8418-b4d1-4312-b82e-37e4682ef3e6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126670000005099100001,
        "LastLSN":  17126670000005914800001,
        "CheckpointLSN":  17126670000004263000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500168900000)\/",
        "BackupFinishDate":  "\/Date(1500168900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "665dcbe5-1225-4bea-a5d4-757faf210b12",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500168900000)\/",
        "Start":  "\/Date(1500168900000)\/",
        "BackupSetId":  "665dcbe5-1225-4bea-a5d4-757faf210b12",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126670000005914800001,
        "LastLSN":  17126670000006764000001,
        "CheckpointLSN":  17126670000006331400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500169200000)\/",
        "BackupFinishDate":  "\/Date(1500169200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "60e5c305-6d05-4c7e-9ac1-d41d19b8b121",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500169200000)\/",
        "Start":  "\/Date(1500169200000)\/",
        "BackupSetId":  "60e5c305-6d05-4c7e-9ac1-d41d19b8b121",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126670000006764000001,
        "LastLSN":  17126670000007592600001,
        "CheckpointLSN":  17126670000006331400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500169500000)\/",
        "BackupFinishDate":  "\/Date(1500169500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "bcd4c2f5-2e36-4ee9-9365-b7532cdd6352",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500169500000)\/",
        "Start":  "\/Date(1500169500000)\/",
        "BackupSetId":  "bcd4c2f5-2e36-4ee9-9365-b7532cdd6352",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126670000007592600001,
        "LastLSN":  17126670000008398300001,
        "CheckpointLSN":  17126670000006331400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500169801000)\/",
        "BackupFinishDate":  "\/Date(1500169801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "316f894b-f248-4dff-83c4-6f25c024947d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500169801000)\/",
        "Start":  "\/Date(1500169801000)\/",
        "BackupSetId":  "316f894b-f248-4dff-83c4-6f25c024947d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126670000008398300001,
        "LastLSN":  17126670000009236200001,
        "CheckpointLSN":  17126670000008400200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500170100000)\/",
        "BackupFinishDate":  "\/Date(1500170100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7b75dcc9-ccdf-4046-a940-1dc642d49859",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500170100000)\/",
        "Start":  "\/Date(1500170100000)\/",
        "BackupSetId":  "7b75dcc9-ccdf-4046-a940-1dc642d49859",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126670000009236200001,
        "LastLSN":  17126670000010042300001,
        "CheckpointLSN":  17126670000008400200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500170400000)\/",
        "BackupFinishDate":  "\/Date(1500170400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e6ce917d-3522-4299-b2b0-dd82a60e5be4",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500170400000)\/",
        "Start":  "\/Date(1500170400000)\/",
        "BackupSetId":  "e6ce917d-3522-4299-b2b0-dd82a60e5be4",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126670000010042300001,
        "LastLSN":  17126670000010878100001,
        "CheckpointLSN":  17126670000010467400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500170700000)\/",
        "BackupFinishDate":  "\/Date(1500170700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "077b964b-ff34-4cb2-abc4-58e86bcf15c5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500170700000)\/",
        "Start":  "\/Date(1500170700000)\/",
        "BackupSetId":  "077b964b-ff34-4cb2-abc4-58e86bcf15c5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126670000010878100001,
        "LastLSN":  17126670000011681900001,
        "CheckpointLSN":  17126670000010467400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500171000000)\/",
        "BackupFinishDate":  "\/Date(1500171001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "890b8b69-ac64-461b-bde1-441b84f0e956",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500171001000)\/",
        "Start":  "\/Date(1500171000000)\/",
        "BackupSetId":  "890b8b69-ac64-461b-bde1-441b84f0e956",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126670000011681900001,
        "LastLSN":  17126670000012497500001,
        "CheckpointLSN":  17126670000010467400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500171301000)\/",
        "BackupFinishDate":  "\/Date(1500171301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e70bfd6c-441e-4d0d-8e2f-41f14affe464",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500171301000)\/",
        "Start":  "\/Date(1500171301000)\/",
        "BackupSetId":  "e70bfd6c-441e-4d0d-8e2f-41f14affe464",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126670000012497500001,
        "LastLSN":  17126671000000202900001,
        "CheckpointLSN":  17126670000012562200004,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500171600000)\/",
        "BackupFinishDate":  "\/Date(1500171600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "40411649-d419-4be5-998a-45058666e8ab",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500171600000)\/",
        "Start":  "\/Date(1500171600000)\/",
        "BackupSetId":  "40411649-d419-4be5-998a-45058666e8ab",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126671000000202900001,
        "LastLSN":  17126671000001034700001,
        "CheckpointLSN":  17126671000000203300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500171900000)\/",
        "BackupFinishDate":  "\/Date(1500171900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "48608d55-efa7-40a1-9e81-21ca010facfa",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500171900000)\/",
        "Start":  "\/Date(1500171900000)\/",
        "BackupSetId":  "48608d55-efa7-40a1-9e81-21ca010facfa",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126671000001034700001,
        "LastLSN":  17126671000001838200001,
        "CheckpointLSN":  17126671000000203300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500172200000)\/",
        "BackupFinishDate":  "\/Date(1500172200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3c5dd58f-b5d2-4cc1-aa99-38224ea74e2b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500172200000)\/",
        "Start":  "\/Date(1500172200000)\/",
        "BackupSetId":  "3c5dd58f-b5d2-4cc1-aa99-38224ea74e2b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126671000001838200001,
        "LastLSN":  17126671000002673400001,
        "CheckpointLSN":  17126671000002278100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500172501000)\/",
        "BackupFinishDate":  "\/Date(1500172501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a9465409-7b55-4d27-9df6-bd0bea11578c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500172501000)\/",
        "Start":  "\/Date(1500172501000)\/",
        "BackupSetId":  "a9465409-7b55-4d27-9df6-bd0bea11578c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126671000002673400001,
        "LastLSN":  17126671000003449200001,
        "CheckpointLSN":  17126671000002278100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500172800000)\/",
        "BackupFinishDate":  "\/Date(1500172800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "52345a04-15d7-4513-b541-cf41e708a6df",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500172800000)\/",
        "Start":  "\/Date(1500172800000)\/",
        "BackupSetId":  "52345a04-15d7-4513-b541-cf41e708a6df",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126671000003449200001,
        "LastLSN":  17126671000004251000001,
        "CheckpointLSN":  17126671000002278100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500173100000)\/",
        "BackupFinishDate":  "\/Date(1500173100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "512ff79a-0a97-4976-ae7d-20f119ada4e1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500173100000)\/",
        "Start":  "\/Date(1500173100000)\/",
        "BackupSetId":  "512ff79a-0a97-4976-ae7d-20f119ada4e1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126671000004251000001,
        "LastLSN":  17126671000005060400001,
        "CheckpointLSN":  17126671000004345900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500173400000)\/",
        "BackupFinishDate":  "\/Date(1500173400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "af4d5a8e-3386-40fd-b657-9cde922287f5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500173400000)\/",
        "Start":  "\/Date(1500173400000)\/",
        "BackupSetId":  "af4d5a8e-3386-40fd-b657-9cde922287f5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126671000005060400001,
        "LastLSN":  17126671000005867100001,
        "CheckpointLSN":  17126671000004345900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500173700000)\/",
        "BackupFinishDate":  "\/Date(1500173701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ca768efb-11d3-475e-8508-7f8ca35cc1cb",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500173701000)\/",
        "Start":  "\/Date(1500173700000)\/",
        "BackupSetId":  "ca768efb-11d3-475e-8508-7f8ca35cc1cb",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126671000005867100001,
        "LastLSN":  17126671000006705600001,
        "CheckpointLSN":  17126671000006431200023,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500174000000)\/",
        "BackupFinishDate":  "\/Date(1500174000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9d5a68b7-2b45-4338-a929-cf558926949a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500174000000)\/",
        "Start":  "\/Date(1500174000000)\/",
        "BackupSetId":  "9d5a68b7-2b45-4338-a929-cf558926949a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126671000006705600001,
        "LastLSN":  17126671000007496500001,
        "CheckpointLSN":  17126671000006431200023,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500174300000)\/",
        "BackupFinishDate":  "\/Date(1500174300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fa863c75-da1f-4f0d-af64-d9bcd90db809",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500174300000)\/",
        "Start":  "\/Date(1500174300000)\/",
        "BackupSetId":  "fa863c75-da1f-4f0d-af64-d9bcd90db809",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126671000007496500001,
        "LastLSN":  17126671000008303500001,
        "CheckpointLSN":  17126671000006431200023,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500174600000)\/",
        "BackupFinishDate":  "\/Date(1500174600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "30da268e-a28d-4b2d-a88c-9b1bcdf35937",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500174600000)\/",
        "Start":  "\/Date(1500174600000)\/",
        "BackupSetId":  "30da268e-a28d-4b2d-a88c-9b1bcdf35937",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126671000008303500001,
        "LastLSN":  17126671000009114600001,
        "CheckpointLSN":  17126671000008557900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500174900000)\/",
        "BackupFinishDate":  "\/Date(1500174900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5c173e09-7c83-4440-ab61-242243d9c5a1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500174900000)\/",
        "Start":  "\/Date(1500174900000)\/",
        "BackupSetId":  "5c173e09-7c83-4440-ab61-242243d9c5a1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126671000009114600001,
        "LastLSN":  17126671000009918300001,
        "CheckpointLSN":  17126671000008557900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500175201000)\/",
        "BackupFinishDate":  "\/Date(1500175201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "66ebd5b8-d484-44bc-9ecd-6be0c480ee37",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500175201000)\/",
        "Start":  "\/Date(1500175201000)\/",
        "BackupSetId":  "66ebd5b8-d484-44bc-9ecd-6be0c480ee37",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126671000009918300001,
        "LastLSN":  17126671000010710900001,
        "CheckpointLSN":  17126671000010627900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500175500000)\/",
        "BackupFinishDate":  "\/Date(1500175500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "620eb2be-b435-47ec-9df6-9b006b299ae9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500175500000)\/",
        "Start":  "\/Date(1500175500000)\/",
        "BackupSetId":  "620eb2be-b435-47ec-9df6-9b006b299ae9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126671000010710900001,
        "LastLSN":  17126671000011502300001,
        "CheckpointLSN":  17126671000010627900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500175800000)\/",
        "BackupFinishDate":  "\/Date(1500175800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "53c90d72-1a48-49bb-b246-e84617c8c2b2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500175800000)\/",
        "Start":  "\/Date(1500175800000)\/",
        "BackupSetId":  "53c90d72-1a48-49bb-b246-e84617c8c2b2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126671000011502300001,
        "LastLSN":  17126671000012318700001,
        "CheckpointLSN":  17126671000010627900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500176100000)\/",
        "BackupFinishDate":  "\/Date(1500176100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "09109be2-5104-4ec0-920d-05b21e661d91",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500176100000)\/",
        "Start":  "\/Date(1500176100000)\/",
        "BackupSetId":  "09109be2-5104-4ec0-920d-05b21e661d91",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126671000012318700001,
        "LastLSN":  17126672000000061800001,
        "CheckpointLSN":  17126671000012697200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500176400000)\/",
        "BackupFinishDate":  "\/Date(1500176400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8726c270-6597-4275-84df-86bad4e1d739",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500176400000)\/",
        "Start":  "\/Date(1500176400000)\/",
        "BackupSetId":  "8726c270-6597-4275-84df-86bad4e1d739",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126672000000061800001,
        "LastLSN":  17126672000000910400001,
        "CheckpointLSN":  17126672000000061800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500176700000)\/",
        "BackupFinishDate":  "\/Date(1500176700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2238100a-dd03-4f13-814d-598fa88265ef",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500176700000)\/",
        "Start":  "\/Date(1500176700000)\/",
        "BackupSetId":  "2238100a-dd03-4f13-814d-598fa88265ef",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126672000000910400001,
        "LastLSN":  17126672000001713000001,
        "CheckpointLSN":  17126672000000061800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500177000000)\/",
        "BackupFinishDate":  "\/Date(1500177000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2bf48f8a-b439-4495-980b-07b5b141ef7f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500177000000)\/",
        "Start":  "\/Date(1500177000000)\/",
        "BackupSetId":  "2bf48f8a-b439-4495-980b-07b5b141ef7f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126672000001713000001,
        "LastLSN":  17126672000002552200001,
        "CheckpointLSN":  17126672000002128900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500177300000)\/",
        "BackupFinishDate":  "\/Date(1500177300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b794d0c3-11fb-44e5-9ae6-9aca4aa40263",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500177300000)\/",
        "Start":  "\/Date(1500177300000)\/",
        "BackupSetId":  "b794d0c3-11fb-44e5-9ae6-9aca4aa40263",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126672000002552200001,
        "LastLSN":  17126672000003358300001,
        "CheckpointLSN":  17126672000002128900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500177601000)\/",
        "BackupFinishDate":  "\/Date(1500177601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0d8e9ae9-a6e8-480e-a36c-cd2ee37d790e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500177601000)\/",
        "Start":  "\/Date(1500177601000)\/",
        "BackupSetId":  "0d8e9ae9-a6e8-480e-a36c-cd2ee37d790e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126672000003358300001,
        "LastLSN":  17126672000004174300001,
        "CheckpointLSN":  17126672000002128900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500177900000)\/",
        "BackupFinishDate":  "\/Date(1500177900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e82e2742-b476-40e3-a01f-0db39de1bd93",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500177900000)\/",
        "Start":  "\/Date(1500177900000)\/",
        "BackupSetId":  "e82e2742-b476-40e3-a01f-0db39de1bd93",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126672000004174300001,
        "LastLSN":  17126672000004999000001,
        "CheckpointLSN":  17126672000004196800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500178200000)\/",
        "BackupFinishDate":  "\/Date(1500178200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9d1e5fa6-b36e-4917-9cbd-c8ed01f5c696",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500178200000)\/",
        "Start":  "\/Date(1500178200000)\/",
        "BackupSetId":  "9d1e5fa6-b36e-4917-9cbd-c8ed01f5c696",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126672000004999000001,
        "LastLSN":  17126672000005817200001,
        "CheckpointLSN":  17126672000004196800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500178500000)\/",
        "BackupFinishDate":  "\/Date(1500178500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ee805c96-2032-42f7-8864-aefb99c13945",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500178500000)\/",
        "Start":  "\/Date(1500178500000)\/",
        "BackupSetId":  "ee805c96-2032-42f7-8864-aefb99c13945",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126672000005817200001,
        "LastLSN":  17126672000006625700001,
        "CheckpointLSN":  17126672000006297500021,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500178800000)\/",
        "BackupFinishDate":  "\/Date(1500178801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "835e7217-1a89-4441-9681-876d7ccca10c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500178801000)\/",
        "Start":  "\/Date(1500178800000)\/",
        "BackupSetId":  "835e7217-1a89-4441-9681-876d7ccca10c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126672000006625700001,
        "LastLSN":  17126672000007442400001,
        "CheckpointLSN":  17126672000006297500021,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500179100000)\/",
        "BackupFinishDate":  "\/Date(1500179100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5c3032f5-698f-476b-bcd4-0a75d484776d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500179100000)\/",
        "Start":  "\/Date(1500179100000)\/",
        "BackupSetId":  "5c3032f5-698f-476b-bcd4-0a75d484776d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126672000007442400001,
        "LastLSN":  17126672000008248100001,
        "CheckpointLSN":  17126672000006297500021,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500179400000)\/",
        "BackupFinishDate":  "\/Date(1500179400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "74f32ffc-448e-4ab6-b8ef-dd42c0b10593",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500179400000)\/",
        "Start":  "\/Date(1500179400000)\/",
        "BackupSetId":  "74f32ffc-448e-4ab6-b8ef-dd42c0b10593",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126672000008248100001,
        "LastLSN":  17126672000009082300001,
        "CheckpointLSN":  17126672000008366500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500179700000)\/",
        "BackupFinishDate":  "\/Date(1500179700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "317b3328-29f0-4d66-8dca-fede8eb9e086",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500179700000)\/",
        "Start":  "\/Date(1500179700000)\/",
        "BackupSetId":  "317b3328-29f0-4d66-8dca-fede8eb9e086",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126672000009082300001,
        "LastLSN":  17126672000009873200001,
        "CheckpointLSN":  17126672000008366500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500180000000)\/",
        "BackupFinishDate":  "\/Date(1500180000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e862a5a9-46f5-49c9-b3f9-a981eca01f7d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500180000000)\/",
        "Start":  "\/Date(1500180000000)\/",
        "BackupSetId":  "e862a5a9-46f5-49c9-b3f9-a981eca01f7d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126672000009873200001,
        "LastLSN":  17126672000010695100001,
        "CheckpointLSN":  17126672000010449400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500180300000)\/",
        "BackupFinishDate":  "\/Date(1500180300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "97c9247c-6b90-4cc2-bc1b-524e2555cb50",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500180300000)\/",
        "Start":  "\/Date(1500180300000)\/",
        "BackupSetId":  "97c9247c-6b90-4cc2-bc1b-524e2555cb50",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126672000010695100001,
        "LastLSN":  17126672000011486300001,
        "CheckpointLSN":  17126672000010449400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500180601000)\/",
        "BackupFinishDate":  "\/Date(1500180601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e99e4a34-e970-4294-879e-88d8c84fdd8c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500180601000)\/",
        "Start":  "\/Date(1500180601000)\/",
        "BackupSetId":  "e99e4a34-e970-4294-879e-88d8c84fdd8c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126672000011486300001,
        "LastLSN":  17126672000012293700001,
        "CheckpointLSN":  17126672000010449400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500180900000)\/",
        "BackupFinishDate":  "\/Date(1500180900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0b0048fe-2727-45ca-84d3-3920c44a579c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500180900000)\/",
        "Start":  "\/Date(1500180900000)\/",
        "BackupSetId":  "0b0048fe-2727-45ca-84d3-3920c44a579c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126672000012293700001,
        "LastLSN":  17126673000000025400001,
        "CheckpointLSN":  17126672000012568800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500181200000)\/",
        "BackupFinishDate":  "\/Date(1500181200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6641cf06-9266-486e-b91d-57189d288531",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500181200000)\/",
        "Start":  "\/Date(1500181200000)\/",
        "BackupSetId":  "6641cf06-9266-486e-b91d-57189d288531",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126673000000025400001,
        "LastLSN":  17126673000003570500001,
        "CheckpointLSN":  17126673000002098400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500181500000)\/",
        "BackupFinishDate":  "\/Date(1500181500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "cf889cf0-6690-410e-90fd-f1bcff71a437",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500181500000)\/",
        "Start":  "\/Date(1500181500000)\/",
        "BackupSetId":  "cf889cf0-6690-410e-90fd-f1bcff71a437",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126673000003570500001,
        "LastLSN":  17126673000004400300001,
        "CheckpointLSN":  17126673000004202700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500181800000)\/",
        "BackupFinishDate":  "\/Date(1500181800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5988fcc4-3216-4dfd-8fd9-c54c378aff36",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500181800000)\/",
        "Start":  "\/Date(1500181800000)\/",
        "BackupSetId":  "5988fcc4-3216-4dfd-8fd9-c54c378aff36",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126673000004400300001,
        "LastLSN":  17126673000005189900001,
        "CheckpointLSN":  17126673000004202700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500182101000)\/",
        "BackupFinishDate":  "\/Date(1500182101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "42cefd12-ef9e-4e05-ae1e-92c3a41cae5e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500182101000)\/",
        "Start":  "\/Date(1500182101000)\/",
        "BackupSetId":  "42cefd12-ef9e-4e05-ae1e-92c3a41cae5e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126673000005189900001,
        "LastLSN":  17126673000005992800001,
        "CheckpointLSN":  17126673000004202700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500182400000)\/",
        "BackupFinishDate":  "\/Date(1500182400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "bafcb75b-bc77-4b7a-9352-e1bb9770f9cd",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500182400000)\/",
        "Start":  "\/Date(1500182400000)\/",
        "BackupSetId":  "bafcb75b-bc77-4b7a-9352-e1bb9770f9cd",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126673000005992800001,
        "LastLSN":  17126673000006801400001,
        "CheckpointLSN":  17126673000006278200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500182700000)\/",
        "BackupFinishDate":  "\/Date(1500182700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7c92d710-412d-421c-987e-95e38e80d714",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500182700000)\/",
        "Start":  "\/Date(1500182700000)\/",
        "BackupSetId":  "7c92d710-412d-421c-987e-95e38e80d714",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126673000006801400001,
        "LastLSN":  17126673000007592900001,
        "CheckpointLSN":  17126673000006278200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500183000000)\/",
        "BackupFinishDate":  "\/Date(1500183000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "153138bc-7d4f-4bf4-8d1b-9c2cd3022520",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500183000000)\/",
        "Start":  "\/Date(1500183000000)\/",
        "BackupSetId":  "153138bc-7d4f-4bf4-8d1b-9c2cd3022520",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126673000007592900001,
        "LastLSN":  17126673000008429800001,
        "CheckpointLSN":  17126673000008359300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500183300000)\/",
        "BackupFinishDate":  "\/Date(1500183300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a950d546-5203-4cb4-abf6-ddc7a0bd9dba",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500183300000)\/",
        "Start":  "\/Date(1500183300000)\/",
        "BackupSetId":  "a950d546-5203-4cb4-abf6-ddc7a0bd9dba",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126673000008429800001,
        "LastLSN":  17126673000009260100001,
        "CheckpointLSN":  17126673000008359300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500183600000)\/",
        "BackupFinishDate":  "\/Date(1500183600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "30ac2de7-665e-4f08-afc5-e6c441931e1e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500183600000)\/",
        "Start":  "\/Date(1500183600000)\/",
        "BackupSetId":  "30ac2de7-665e-4f08-afc5-e6c441931e1e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126673000009260100001,
        "LastLSN":  17126673000010089500001,
        "CheckpointLSN":  17126673000008359300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500183900000)\/",
        "BackupFinishDate":  "\/Date(1500183900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b2f8d031-c7a8-4a72-9075-198fc12a6f23",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500183900000)\/",
        "Start":  "\/Date(1500183900000)\/",
        "BackupSetId":  "b2f8d031-c7a8-4a72-9075-198fc12a6f23",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126673000010089500001,
        "LastLSN":  17126673000010912200001,
        "CheckpointLSN":  17126673000010441400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500184200000)\/",
        "BackupFinishDate":  "\/Date(1500184200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5f532efa-14ce-4330-8fa8-5e9767ee6503",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500184200000)\/",
        "Start":  "\/Date(1500184200000)\/",
        "BackupSetId":  "5f532efa-14ce-4330-8fa8-5e9767ee6503",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126673000010912200001,
        "LastLSN":  17126673000011731100001,
        "CheckpointLSN":  17126673000010441400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500184500000)\/",
        "BackupFinishDate":  "\/Date(1500184500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1c3aeeb9-f2e7-498d-8555-e5e10b4f656d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500184500000)\/",
        "Start":  "\/Date(1500184500000)\/",
        "BackupSetId":  "1c3aeeb9-f2e7-498d-8555-e5e10b4f656d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126673000011731100001,
        "LastLSN":  17126673000012535000001,
        "CheckpointLSN":  17126673000010441400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500184801000)\/",
        "BackupFinishDate":  "\/Date(1500184801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f791dc87-280a-4291-bbf7-2b622f44b07e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500184801000)\/",
        "Start":  "\/Date(1500184801000)\/",
        "BackupSetId":  "f791dc87-280a-4291-bbf7-2b622f44b07e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126673000012535000001,
        "LastLSN":  17126674000000263600001,
        "CheckpointLSN":  17126673000012544700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500185100000)\/",
        "BackupFinishDate":  "\/Date(1500185100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "dffc204a-b30a-4e06-9689-ea5c2af1d516",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500185100000)\/",
        "Start":  "\/Date(1500185100000)\/",
        "BackupSetId":  "dffc204a-b30a-4e06-9689-ea5c2af1d516",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126674000000263600001,
        "LastLSN":  17126674000001088000001,
        "CheckpointLSN":  17126674000000263600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500185400000)\/",
        "BackupFinishDate":  "\/Date(1500185400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a35cee8f-d6fb-4497-9e1f-d7bb0186a8d1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500185400000)\/",
        "Start":  "\/Date(1500185400000)\/",
        "BackupSetId":  "a35cee8f-d6fb-4497-9e1f-d7bb0186a8d1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126674000001088000001,
        "LastLSN":  17126674000001903600001,
        "CheckpointLSN":  17126674000000263600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500185700000)\/",
        "BackupFinishDate":  "\/Date(1500185700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "250cfd25-476f-425e-8a7d-8433a3f725e1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500185700000)\/",
        "Start":  "\/Date(1500185700000)\/",
        "BackupSetId":  "250cfd25-476f-425e-8a7d-8433a3f725e1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126674000001903600001,
        "LastLSN":  17126674000002715300001,
        "CheckpointLSN":  17126674000002330900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500186000000)\/",
        "BackupFinishDate":  "\/Date(1500186000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "92eaf48b-59cc-404e-808b-ab3652e8d1c7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500186000000)\/",
        "Start":  "\/Date(1500186000000)\/",
        "BackupSetId":  "92eaf48b-59cc-404e-808b-ab3652e8d1c7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126674000002715300001,
        "LastLSN":  17126674000003530700001,
        "CheckpointLSN":  17126674000002330900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500186300000)\/",
        "BackupFinishDate":  "\/Date(1500186300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "19234118-65f5-48bc-8555-8aa17dac51a5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500186300000)\/",
        "Start":  "\/Date(1500186300000)\/",
        "BackupSetId":  "19234118-65f5-48bc-8555-8aa17dac51a5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126674000003530700001,
        "LastLSN":  17126674000004334000001,
        "CheckpointLSN":  17126674000002330900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500186600000)\/",
        "BackupFinishDate":  "\/Date(1500186600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4b877d13-7a00-43d1-9da4-77d15549c49b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500186600000)\/",
        "Start":  "\/Date(1500186600000)\/",
        "BackupSetId":  "4b877d13-7a00-43d1-9da4-77d15549c49b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126674000004334000001,
        "LastLSN":  17126674000005171100001,
        "CheckpointLSN":  17126674000004399000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500186900000)\/",
        "BackupFinishDate":  "\/Date(1500186900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "93c56816-f247-480a-a055-d56b33763e73",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500186900000)\/",
        "Start":  "\/Date(1500186900000)\/",
        "BackupSetId":  "93c56816-f247-480a-a055-d56b33763e73",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126674000005171100001,
        "LastLSN":  17126674000005963600001,
        "CheckpointLSN":  17126674000004399000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500187200000)\/",
        "BackupFinishDate":  "\/Date(1500187200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0f5f969b-f5f6-477b-bc2b-96e6dc2f83ba",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500187200000)\/",
        "Start":  "\/Date(1500187200000)\/",
        "BackupSetId":  "0f5f969b-f5f6-477b-bc2b-96e6dc2f83ba",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126674000005963600001,
        "LastLSN":  17126674000006785200001,
        "CheckpointLSN":  17126674000006468800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500187500000)\/",
        "BackupFinishDate":  "\/Date(1500187500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ff535a10-d45e-454f-90ba-b53ff37a8f56",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500187500000)\/",
        "Start":  "\/Date(1500187500000)\/",
        "BackupSetId":  "ff535a10-d45e-454f-90ba-b53ff37a8f56",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126674000006785200001,
        "LastLSN":  17126674000007577900001,
        "CheckpointLSN":  17126674000006468800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500187800000)\/",
        "BackupFinishDate":  "\/Date(1500187800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c17f556d-1e1a-42b0-9b25-ff330a5290f8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500187800000)\/",
        "Start":  "\/Date(1500187800000)\/",
        "BackupSetId":  "c17f556d-1e1a-42b0-9b25-ff330a5290f8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126674000007577900001,
        "LastLSN":  17126674000008385600001,
        "CheckpointLSN":  17126674000006468800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500188100000)\/",
        "BackupFinishDate":  "\/Date(1500188101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a58e991e-4d4e-462a-a99b-21081a747c81",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500188101000)\/",
        "Start":  "\/Date(1500188100000)\/",
        "BackupSetId":  "a58e991e-4d4e-462a-a99b-21081a747c81",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126674000008385600001,
        "LastLSN":  17126674000009222400001,
        "CheckpointLSN":  17126674000008536400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500188400000)\/",
        "BackupFinishDate":  "\/Date(1500188400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6cca14be-f085-4108-9efd-4a67b0cdfc1d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500188400000)\/",
        "Start":  "\/Date(1500188400000)\/",
        "BackupSetId":  "6cca14be-f085-4108-9efd-4a67b0cdfc1d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126674000009222400001,
        "LastLSN":  17126674000010012400001,
        "CheckpointLSN":  17126674000008536400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500188700000)\/",
        "BackupFinishDate":  "\/Date(1500188700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d8112172-c97c-412a-96bc-9b161a8740db",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500188700000)\/",
        "Start":  "\/Date(1500188700000)\/",
        "BackupSetId":  "d8112172-c97c-412a-96bc-9b161a8740db",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126674000010012400001,
        "LastLSN":  17126674000010835400001,
        "CheckpointLSN":  17126674000010624200040,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500189000000)\/",
        "BackupFinishDate":  "\/Date(1500189000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e0bc3070-e569-46fa-8fe3-8f281f0305c3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500189000000)\/",
        "Start":  "\/Date(1500189000000)\/",
        "BackupSetId":  "e0bc3070-e569-46fa-8fe3-8f281f0305c3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126674000010835400001,
        "LastLSN":  17126674000011626900001,
        "CheckpointLSN":  17126674000010624200040,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500189300000)\/",
        "BackupFinishDate":  "\/Date(1500189300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5a8710af-fd8a-4600-a9a0-4597d151829a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500189300000)\/",
        "Start":  "\/Date(1500189300000)\/",
        "BackupSetId":  "5a8710af-fd8a-4600-a9a0-4597d151829a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126674000011626900001,
        "LastLSN":  17126674000012429900001,
        "CheckpointLSN":  17126674000010624200040,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500189601000)\/",
        "BackupFinishDate":  "\/Date(1500189601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "05becbbb-cf5c-4607-b72d-3696f55e67a4",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500189601000)\/",
        "Start":  "\/Date(1500189601000)\/",
        "BackupSetId":  "05becbbb-cf5c-4607-b72d-3696f55e67a4",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126674000012429900001,
        "LastLSN":  17126675000000133100001,
        "CheckpointLSN":  17126674000012722300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500189900000)\/",
        "BackupFinishDate":  "\/Date(1500189900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "522bd44b-dcad-4a2f-9c02-f03eabd5f18b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500189900000)\/",
        "Start":  "\/Date(1500189900000)\/",
        "BackupSetId":  "522bd44b-dcad-4a2f-9c02-f03eabd5f18b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126675000000133100001,
        "LastLSN":  17126675000000940900001,
        "CheckpointLSN":  17126675000000133100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500190200000)\/",
        "BackupFinishDate":  "\/Date(1500190200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e14b353b-7be1-4b76-8683-6946cfefa441",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500190200000)\/",
        "Start":  "\/Date(1500190200000)\/",
        "BackupSetId":  "e14b353b-7be1-4b76-8683-6946cfefa441",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126675000000940900001,
        "LastLSN":  17126675000001759200001,
        "CheckpointLSN":  17126675000000133100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500190500000)\/",
        "BackupFinishDate":  "\/Date(1500190500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "eea08527-9829-43d0-91d5-cba5ebb651ca",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500190500000)\/",
        "Start":  "\/Date(1500190500000)\/",
        "BackupSetId":  "eea08527-9829-43d0-91d5-cba5ebb651ca",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126675000001759200001,
        "LastLSN":  17126675000002608800001,
        "CheckpointLSN":  17126675000002198300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500190800000)\/",
        "BackupFinishDate":  "\/Date(1500190800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d447b0a5-bb17-41f3-a065-4f1f5dfad0d7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500190800000)\/",
        "Start":  "\/Date(1500190800000)\/",
        "BackupSetId":  "d447b0a5-bb17-41f3-a065-4f1f5dfad0d7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126675000002608800001,
        "LastLSN":  17126675000003438200001,
        "CheckpointLSN":  17126675000002198300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500191100000)\/",
        "BackupFinishDate":  "\/Date(1500191101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c79de9d9-229c-4c2f-963d-f4f5ca6827dd",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500191101000)\/",
        "Start":  "\/Date(1500191100000)\/",
        "BackupSetId":  "c79de9d9-229c-4c2f-963d-f4f5ca6827dd",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126675000003438200001,
        "LastLSN":  17126675000004241200001,
        "CheckpointLSN":  17126675000002198300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500191400000)\/",
        "BackupFinishDate":  "\/Date(1500191400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "77c1e56b-21bd-4547-8b06-36726a68d45b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500191400000)\/",
        "Start":  "\/Date(1500191400000)\/",
        "BackupSetId":  "77c1e56b-21bd-4547-8b06-36726a68d45b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126675000004241200001,
        "LastLSN":  17126675000005080500001,
        "CheckpointLSN":  17126675000004270900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500191700000)\/",
        "BackupFinishDate":  "\/Date(1500191700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ca8b7e0e-1382-4dd0-8ed3-253e8db36e63",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500191700000)\/",
        "Start":  "\/Date(1500191700000)\/",
        "BackupSetId":  "ca8b7e0e-1382-4dd0-8ed3-253e8db36e63",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126675000005080500001,
        "LastLSN":  17126675000005882900001,
        "CheckpointLSN":  17126675000004270900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500192000000)\/",
        "BackupFinishDate":  "\/Date(1500192000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d090ebb1-fd9c-4cdb-a671-0e4615f6be16",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500192000000)\/",
        "Start":  "\/Date(1500192000000)\/",
        "BackupSetId":  "d090ebb1-fd9c-4cdb-a671-0e4615f6be16",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126675000005882900001,
        "LastLSN":  17126675000006718900001,
        "CheckpointLSN":  17126675000006339000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500192300000)\/",
        "BackupFinishDate":  "\/Date(1500192300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "909df065-ffae-4da5-84e6-f22eed5a5097",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500192300000)\/",
        "Start":  "\/Date(1500192300000)\/",
        "BackupSetId":  "909df065-ffae-4da5-84e6-f22eed5a5097",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126675000006718900001,
        "LastLSN":  17126675000007520700001,
        "CheckpointLSN":  17126675000006339000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500192601000)\/",
        "BackupFinishDate":  "\/Date(1500192601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d4cd1168-5a0a-4a12-b341-25ccda1cf472",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500192601000)\/",
        "Start":  "\/Date(1500192601000)\/",
        "BackupSetId":  "d4cd1168-5a0a-4a12-b341-25ccda1cf472",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126675000007520700001,
        "LastLSN":  17126675000008337800001,
        "CheckpointLSN":  17126675000006339000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500192900000)\/",
        "BackupFinishDate":  "\/Date(1500192900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d34c2c86-b80b-4f48-9be5-49065e5a6eba",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500192900000)\/",
        "Start":  "\/Date(1500192900000)\/",
        "BackupSetId":  "d34c2c86-b80b-4f48-9be5-49065e5a6eba",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126675000008337800001,
        "LastLSN":  17126675000009146400001,
        "CheckpointLSN":  17126675000008407400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500193200000)\/",
        "BackupFinishDate":  "\/Date(1500193200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "cd1ab380-864d-4c73-b53e-7cb1a14ea180",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500193200000)\/",
        "Start":  "\/Date(1500193200000)\/",
        "BackupSetId":  "cd1ab380-864d-4c73-b53e-7cb1a14ea180",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126675000009146400001,
        "LastLSN":  17126675000009964300001,
        "CheckpointLSN":  17126675000008407400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500193500000)\/",
        "BackupFinishDate":  "\/Date(1500193500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d73196b1-3a65-40a5-8c9d-fc5a1ebea39e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500193500000)\/",
        "Start":  "\/Date(1500193500000)\/",
        "BackupSetId":  "d73196b1-3a65-40a5-8c9d-fc5a1ebea39e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126675000009964300001,
        "LastLSN":  17126675000010788900001,
        "CheckpointLSN":  17126675000010476200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500193800000)\/",
        "BackupFinishDate":  "\/Date(1500193801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "bdd81806-e4af-49fe-9f91-abd3226250d8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500193801000)\/",
        "Start":  "\/Date(1500193800000)\/",
        "BackupSetId":  "bdd81806-e4af-49fe-9f91-abd3226250d8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126675000010788900001,
        "LastLSN":  17126675000011607300001,
        "CheckpointLSN":  17126675000010476200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500194100000)\/",
        "BackupFinishDate":  "\/Date(1500194100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "63bf7d27-7f06-4391-bca6-746d2f7d11ac",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500194100000)\/",
        "Start":  "\/Date(1500194100000)\/",
        "BackupSetId":  "63bf7d27-7f06-4391-bca6-746d2f7d11ac",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126675000011607300001,
        "LastLSN":  17126675000012402000001,
        "CheckpointLSN":  17126675000010476200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500194400000)\/",
        "BackupFinishDate":  "\/Date(1500194400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d44af580-8d1d-46b5-99db-99bbd278f0e6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500194400000)\/",
        "Start":  "\/Date(1500194400000)\/",
        "BackupSetId":  "d44af580-8d1d-46b5-99db-99bbd278f0e6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126675000012402000001,
        "LastLSN":  17126676000000118900001,
        "CheckpointLSN":  17126675000012545000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500194700000)\/",
        "BackupFinishDate":  "\/Date(1500194700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e477a0a0-0ec9-4ca8-aa64-9c0c3527f334",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500194700000)\/",
        "Start":  "\/Date(1500194700000)\/",
        "BackupSetId":  "e477a0a0-0ec9-4ca8-aa64-9c0c3527f334",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126676000000118900001,
        "LastLSN":  17126676000000925300001,
        "CheckpointLSN":  17126676000000119300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500195000000)\/",
        "BackupFinishDate":  "\/Date(1500195000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3360a528-a523-408e-b9c1-11abd589463c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500195000000)\/",
        "Start":  "\/Date(1500195000000)\/",
        "BackupSetId":  "3360a528-a523-408e-b9c1-11abd589463c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126676000000925300001,
        "LastLSN":  17126676000001732300001,
        "CheckpointLSN":  17126676000000119300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500195300000)\/",
        "BackupFinishDate":  "\/Date(1500195301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "59799c78-938f-4bc1-baec-57759b4372e5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500195301000)\/",
        "Start":  "\/Date(1500195300000)\/",
        "BackupSetId":  "59799c78-938f-4bc1-baec-57759b4372e5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126676000001732300001,
        "LastLSN":  17126676000002567200001,
        "CheckpointLSN":  17126676000002182900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500195600000)\/",
        "BackupFinishDate":  "\/Date(1500195600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b8976932-960d-4f0e-bd4c-866b158f646d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500195600000)\/",
        "Start":  "\/Date(1500195600000)\/",
        "BackupSetId":  "b8976932-960d-4f0e-bd4c-866b158f646d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126676000002567200001,
        "LastLSN":  17126676000003359000001,
        "CheckpointLSN":  17126676000002182900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500195900000)\/",
        "BackupFinishDate":  "\/Date(1500195900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1e5caf1d-1b53-480a-98ed-9ecf676f4fd8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500195900000)\/",
        "Start":  "\/Date(1500195900000)\/",
        "BackupSetId":  "1e5caf1d-1b53-480a-98ed-9ecf676f4fd8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126676000003359000001,
        "LastLSN":  17126676000004162700001,
        "CheckpointLSN":  17126676000002182900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500196200000)\/",
        "BackupFinishDate":  "\/Date(1500196200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e4b1ea0d-ce13-45b9-879e-fa51f6dd4101",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500196200000)\/",
        "Start":  "\/Date(1500196200000)\/",
        "BackupSetId":  "e4b1ea0d-ce13-45b9-879e-fa51f6dd4101",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126676000004162700001,
        "LastLSN":  17126676000004972300001,
        "CheckpointLSN":  17126676000004248600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500196500000)\/",
        "BackupFinishDate":  "\/Date(1500196500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "57aa3495-c28d-4f6f-a63c-9c14314cd0e3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500196500000)\/",
        "Start":  "\/Date(1500196500000)\/",
        "BackupSetId":  "57aa3495-c28d-4f6f-a63c-9c14314cd0e3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126676000004972300001,
        "LastLSN":  17126676000005778200001,
        "CheckpointLSN":  17126676000004248600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500196800000)\/",
        "BackupFinishDate":  "\/Date(1500196800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c5a51cff-9d5c-4485-a6bf-b5e81729128b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500196800000)\/",
        "Start":  "\/Date(1500196800000)\/",
        "BackupSetId":  "c5a51cff-9d5c-4485-a6bf-b5e81729128b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126676000005778200001,
        "LastLSN":  17126676000006590000001,
        "CheckpointLSN":  17126676000006333200021,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500197101000)\/",
        "BackupFinishDate":  "\/Date(1500197101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c121cb29-5fa0-4ee1-8dc4-432856b5efaa",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500197101000)\/",
        "Start":  "\/Date(1500197101000)\/",
        "BackupSetId":  "c121cb29-5fa0-4ee1-8dc4-432856b5efaa",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126676000006590000001,
        "LastLSN":  17126676000007364400001,
        "CheckpointLSN":  17126676000006333200021,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500197400000)\/",
        "BackupFinishDate":  "\/Date(1500197400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ff24c957-d0cd-46c7-a5c0-d563299c89e6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500197400000)\/",
        "Start":  "\/Date(1500197400000)\/",
        "BackupSetId":  "ff24c957-d0cd-46c7-a5c0-d563299c89e6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126676000007364400001,
        "LastLSN":  17126676000008180100001,
        "CheckpointLSN":  17126676000006333200021,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500197700000)\/",
        "BackupFinishDate":  "\/Date(1500197700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "306439cf-1205-49ae-ade3-c8e1e3b23eac",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500197700000)\/",
        "Start":  "\/Date(1500197700000)\/",
        "BackupSetId":  "306439cf-1205-49ae-ade3-c8e1e3b23eac",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126676000008180100001,
        "LastLSN":  17126676000009030800001,
        "CheckpointLSN":  17126676000008485800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500198000000)\/",
        "BackupFinishDate":  "\/Date(1500198000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5b902f4a-d1dd-429c-b894-6fc527bb8a4b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500198000000)\/",
        "Start":  "\/Date(1500198000000)\/",
        "BackupSetId":  "5b902f4a-d1dd-429c-b894-6fc527bb8a4b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126676000009030800001,
        "LastLSN":  17126676000009861600001,
        "CheckpointLSN":  17126676000008485800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500198300000)\/",
        "BackupFinishDate":  "\/Date(1500198301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "47fa9874-f26e-4cea-a6a4-2a5db1892a4a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500198301000)\/",
        "Start":  "\/Date(1500198300000)\/",
        "BackupSetId":  "47fa9874-f26e-4cea-a6a4-2a5db1892a4a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126676000009861600001,
        "LastLSN":  17126676000010667100001,
        "CheckpointLSN":  17126676000010552200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500198600000)\/",
        "BackupFinishDate":  "\/Date(1500198600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ac9f9ab4-d93c-4efc-b071-790b03990c5e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500198600000)\/",
        "Start":  "\/Date(1500198600000)\/",
        "BackupSetId":  "ac9f9ab4-d93c-4efc-b071-790b03990c5e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126676000010667100001,
        "LastLSN":  17126676000011487700001,
        "CheckpointLSN":  17126676000010552200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500198900000)\/",
        "BackupFinishDate":  "\/Date(1500198900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "64224497-7ae0-4648-a574-d72624cc8a60",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500198900000)\/",
        "Start":  "\/Date(1500198900000)\/",
        "BackupSetId":  "64224497-7ae0-4648-a574-d72624cc8a60",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126676000011487700001,
        "LastLSN":  17126676000012297200001,
        "CheckpointLSN":  17126676000010552200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500199200000)\/",
        "BackupFinishDate":  "\/Date(1500199200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ceae87e7-9195-4bc6-b383-975e96f81b35",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500199200000)\/",
        "Start":  "\/Date(1500199200000)\/",
        "BackupSetId":  "ceae87e7-9195-4bc6-b383-975e96f81b35",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126676000012297200001,
        "LastLSN":  17126677000000020200001,
        "CheckpointLSN":  17126676000012622400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500199500000)\/",
        "BackupFinishDate":  "\/Date(1500199501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9d365e8a-38b9-4248-a2e7-8c3d9e785216",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500199501000)\/",
        "Start":  "\/Date(1500199500000)\/",
        "BackupSetId":  "9d365e8a-38b9-4248-a2e7-8c3d9e785216",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000000020200001,
        "LastLSN":  17126677000000809800001,
        "CheckpointLSN":  17126677000000022400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500199800000)\/",
        "BackupFinishDate":  "\/Date(1500199800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "413b4754-7a64-466e-a8d8-bf7e087bda0a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500199800000)\/",
        "Start":  "\/Date(1500199800000)\/",
        "BackupSetId":  "413b4754-7a64-466e-a8d8-bf7e087bda0a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000000809800001,
        "LastLSN":  17126677000001628000001,
        "CheckpointLSN":  17126677000000022400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500200100000)\/",
        "BackupFinishDate":  "\/Date(1500200100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2904d291-1d04-46f7-9867-16739fbfc7b1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500200100000)\/",
        "Start":  "\/Date(1500200100000)\/",
        "BackupSetId":  "2904d291-1d04-46f7-9867-16739fbfc7b1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000001628000001,
        "LastLSN":  17126677000002438500001,
        "CheckpointLSN":  17126677000002107200003,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500200400000)\/",
        "BackupFinishDate":  "\/Date(1500200400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "146b3217-d461-4dd3-b84b-c2c3c38cbc99",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500200400000)\/",
        "Start":  "\/Date(1500200400000)\/",
        "BackupSetId":  "146b3217-d461-4dd3-b84b-c2c3c38cbc99",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000002438500001,
        "LastLSN":  17126677000003255000001,
        "CheckpointLSN":  17126677000002107200003,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500200700000)\/",
        "BackupFinishDate":  "\/Date(1500200700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8e2f3276-6468-4ad7-91ee-b3dff4ba9dd6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500200700000)\/",
        "Start":  "\/Date(1500200700000)\/",
        "BackupSetId":  "8e2f3276-6468-4ad7-91ee-b3dff4ba9dd6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000003255000001,
        "LastLSN":  17126677000004061700001,
        "CheckpointLSN":  17126677000002107200003,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500201001000)\/",
        "BackupFinishDate":  "\/Date(1500201001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d668f317-6c2f-4541-8aea-7ea63ae244e9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500201001000)\/",
        "Start":  "\/Date(1500201001000)\/",
        "BackupSetId":  "d668f317-6c2f-4541-8aea-7ea63ae244e9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000004061700001,
        "LastLSN":  17126677000004861600001,
        "CheckpointLSN":  17126677000004173100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500201300000)\/",
        "BackupFinishDate":  "\/Date(1500201300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "94cdbfbb-ffda-4567-8b7d-5c86de8951c6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500201300000)\/",
        "Start":  "\/Date(1500201300000)\/",
        "BackupSetId":  "94cdbfbb-ffda-4567-8b7d-5c86de8951c6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000004861600001,
        "LastLSN":  17126677000005654300001,
        "CheckpointLSN":  17126677000004173100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500201600000)\/",
        "BackupFinishDate":  "\/Date(1500201600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a89e08e2-9e9f-4e0e-95d8-f639be38b4cd",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500201600000)\/",
        "Start":  "\/Date(1500201600000)\/",
        "BackupSetId":  "a89e08e2-9e9f-4e0e-95d8-f639be38b4cd",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000005654300001,
        "LastLSN":  17126677000006477600001,
        "CheckpointLSN":  17126677000006254600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500201900000)\/",
        "BackupFinishDate":  "\/Date(1500201900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d667ed28-a3ff-4b58-958c-6d586218ed04",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500201900000)\/",
        "Start":  "\/Date(1500201900000)\/",
        "BackupSetId":  "d667ed28-a3ff-4b58-958c-6d586218ed04",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000006477600001,
        "LastLSN":  17126677000007271200001,
        "CheckpointLSN":  17126677000006254600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500202200000)\/",
        "BackupFinishDate":  "\/Date(1500202200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "730bc922-a979-41f2-a7ad-778af044b52e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500202200000)\/",
        "Start":  "\/Date(1500202200000)\/",
        "BackupSetId":  "730bc922-a979-41f2-a7ad-778af044b52e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000007271200001,
        "LastLSN":  17126677000008079100001,
        "CheckpointLSN":  17126677000006254600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500202500000)\/",
        "BackupFinishDate":  "\/Date(1500202500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "91cd0658-d309-480b-b0e6-c7cbca3b52f9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500202500000)\/",
        "Start":  "\/Date(1500202500000)\/",
        "BackupSetId":  "91cd0658-d309-480b-b0e6-c7cbca3b52f9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000008079100001,
        "LastLSN":  17126677000008913500001,
        "CheckpointLSN":  17126677000008320300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500202801000)\/",
        "BackupFinishDate":  "\/Date(1500202801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0ac06cd1-30c7-4878-85fe-367e9f6f0f28",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500202801000)\/",
        "Start":  "\/Date(1500202801000)\/",
        "BackupSetId":  "0ac06cd1-30c7-4878-85fe-367e9f6f0f28",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000008913500001,
        "LastLSN":  17126677000009705800001,
        "CheckpointLSN":  17126677000008320300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500203100000)\/",
        "BackupFinishDate":  "\/Date(1500203100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c6ee9ad5-12d4-4856-8b49-3bf3c9eb4c3f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500203100000)\/",
        "Start":  "\/Date(1500203100000)\/",
        "BackupSetId":  "c6ee9ad5-12d4-4856-8b49-3bf3c9eb4c3f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000009705800001,
        "LastLSN":  17126677000010528100001,
        "CheckpointLSN":  17126677000010399600023,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500203400000)\/",
        "BackupFinishDate":  "\/Date(1500203400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f95de4b6-c580-4f87-b059-23110e2a0b2d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500203400000)\/",
        "Start":  "\/Date(1500203400000)\/",
        "BackupSetId":  "f95de4b6-c580-4f87-b059-23110e2a0b2d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000010528100001,
        "LastLSN":  17126677000011320700001,
        "CheckpointLSN":  17126677000010399600023,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500203700000)\/",
        "BackupFinishDate":  "\/Date(1500203700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1acacccc-3fd0-4e93-9424-ce24f4568914",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500203700000)\/",
        "Start":  "\/Date(1500203700000)\/",
        "BackupSetId":  "1acacccc-3fd0-4e93-9424-ce24f4568914",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000011320700001,
        "LastLSN":  17126677000012123700001,
        "CheckpointLSN":  17126677000010399600023,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500204001000)\/",
        "BackupFinishDate":  "\/Date(1500204001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "87a80fc3-e4db-4002-9224-6ab65df3b951",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500204001000)\/",
        "Start":  "\/Date(1500204001000)\/",
        "BackupSetId":  "87a80fc3-e4db-4002-9224-6ab65df3b951",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000012123700001,
        "LastLSN":  17126677000012934400001,
        "CheckpointLSN":  17126677000012541000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500204300000)\/",
        "BackupFinishDate":  "\/Date(1500204300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b20c784e-f362-425a-8fc9-4411228185d1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500204300000)\/",
        "Start":  "\/Date(1500204300000)\/",
        "BackupSetId":  "b20c784e-f362-425a-8fc9-4411228185d1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126677000012934400001,
        "LastLSN":  17126678000000621600001,
        "CheckpointLSN":  17126677000012541000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500204600000)\/",
        "BackupFinishDate":  "\/Date(1500204600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e936eabd-c0e2-43df-902c-4ee42656ec34",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500204600000)\/",
        "Start":  "\/Date(1500204600000)\/",
        "BackupSetId":  "e936eabd-c0e2-43df-902c-4ee42656ec34",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126678000000621600001,
        "LastLSN":  17126678000001456500001,
        "CheckpointLSN":  17126678000000621600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500204900000)\/",
        "BackupFinishDate":  "\/Date(1500204900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "60fa3bba-66b1-4d5a-951a-8bd87a5041a8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500204900000)\/",
        "Start":  "\/Date(1500204900000)\/",
        "BackupSetId":  "60fa3bba-66b1-4d5a-951a-8bd87a5041a8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126678000001456500001,
        "LastLSN":  17126678000002291700001,
        "CheckpointLSN":  17126678000000621600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500205200000)\/",
        "BackupFinishDate":  "\/Date(1500205200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d31aaa76-2e32-48b9-a610-acf4dc3ae2a0",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500205200000)\/",
        "Start":  "\/Date(1500205200000)\/",
        "BackupSetId":  "d31aaa76-2e32-48b9-a610-acf4dc3ae2a0",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126678000002291700001,
        "LastLSN":  17126678000003138900001,
        "CheckpointLSN":  17126678000002685200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500205501000)\/",
        "BackupFinishDate":  "\/Date(1500205501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "73c3fa86-e8f5-403f-a6fe-86767cb4bc96",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500205501000)\/",
        "Start":  "\/Date(1500205501000)\/",
        "BackupSetId":  "73c3fa86-e8f5-403f-a6fe-86767cb4bc96",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126678000003138900001,
        "LastLSN":  17126678000003944400001,
        "CheckpointLSN":  17126678000002685200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500205800000)\/",
        "BackupFinishDate":  "\/Date(1500205800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "74815d45-6cc4-4441-80be-d644b78509d3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500205800000)\/",
        "Start":  "\/Date(1500205800000)\/",
        "BackupSetId":  "74815d45-6cc4-4441-80be-d644b78509d3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126678000003944400001,
        "LastLSN":  17126678000004782400001,
        "CheckpointLSN":  17126678000004753000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500206100000)\/",
        "BackupFinishDate":  "\/Date(1500206100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "869f8aa0-ef73-480a-b020-72fe9b569890",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500206100000)\/",
        "Start":  "\/Date(1500206100000)\/",
        "BackupSetId":  "869f8aa0-ef73-480a-b020-72fe9b569890",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126678000004782400001,
        "LastLSN":  17126678000005586800001,
        "CheckpointLSN":  17126678000004753000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500206400000)\/",
        "BackupFinishDate":  "\/Date(1500206400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "969637e9-9441-4b1c-83b0-22df942cc687",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500206400000)\/",
        "Start":  "\/Date(1500206400000)\/",
        "BackupSetId":  "969637e9-9441-4b1c-83b0-22df942cc687",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126678000005586800001,
        "LastLSN":  17126678000006407300001,
        "CheckpointLSN":  17126678000004753000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500206701000)\/",
        "BackupFinishDate":  "\/Date(1500206701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8e7d1ea0-2460-4414-b65e-4f98cbbd304a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500206701000)\/",
        "Start":  "\/Date(1500206701000)\/",
        "BackupSetId":  "8e7d1ea0-2460-4414-b65e-4f98cbbd304a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126678000006407300001,
        "LastLSN":  17126678000007229100001,
        "CheckpointLSN":  17126678000006818700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500207000000)\/",
        "BackupFinishDate":  "\/Date(1500207000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6ffd1007-3982-4642-956c-5895d1bb53e5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500207000000)\/",
        "Start":  "\/Date(1500207000000)\/",
        "BackupSetId":  "6ffd1007-3982-4642-956c-5895d1bb53e5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126678000007229100001,
        "LastLSN":  17126678000008048200001,
        "CheckpointLSN":  17126678000006818700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500207300000)\/",
        "BackupFinishDate":  "\/Date(1500207300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c705c557-2d95-4dc3-859a-fe01e65e7f53",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500207300000)\/",
        "Start":  "\/Date(1500207300000)\/",
        "BackupSetId":  "c705c557-2d95-4dc3-859a-fe01e65e7f53",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126678000008048200001,
        "LastLSN":  17126678000008841600001,
        "CheckpointLSN":  17126678000006818700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500207600000)\/",
        "BackupFinishDate":  "\/Date(1500207601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "38a1de6b-a700-4687-a6d3-6c99545a0c0f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500207601000)\/",
        "Start":  "\/Date(1500207600000)\/",
        "BackupSetId":  "38a1de6b-a700-4687-a6d3-6c99545a0c0f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126678000008841600001,
        "LastLSN":  17126678000009647800001,
        "CheckpointLSN":  17126678000008897300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500207901000)\/",
        "BackupFinishDate":  "\/Date(1500207901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "383b7be7-0745-4d02-86a8-d03e9f8ed168",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500207901000)\/",
        "Start":  "\/Date(1500207901000)\/",
        "BackupSetId":  "383b7be7-0745-4d02-86a8-d03e9f8ed168",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126678000009647800001,
        "LastLSN":  17126678000010362600001,
        "CheckpointLSN":  17126678000008897300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500208200000)\/",
        "BackupFinishDate":  "\/Date(1500208200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f0e26e0c-0061-4f4d-9673-7893f5d64481",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500208200000)\/",
        "Start":  "\/Date(1500208200000)\/",
        "BackupSetId":  "f0e26e0c-0061-4f4d-9673-7893f5d64481",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126678000010362600001,
        "LastLSN":  17126678000011109000001,
        "CheckpointLSN":  17126678000010987200023,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500208500000)\/",
        "BackupFinishDate":  "\/Date(1500208500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "74232158-89a5-4d40-afc8-3999d3fd0625",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500208500000)\/",
        "Start":  "\/Date(1500208500000)\/",
        "BackupSetId":  "74232158-89a5-4d40-afc8-3999d3fd0625",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126678000011109000001,
        "LastLSN":  17126678000011813400001,
        "CheckpointLSN":  17126678000010987200023,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500208800000)\/",
        "BackupFinishDate":  "\/Date(1500208800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fb5bcc27-b2e0-4cae-98c9-868023acaed0",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500208800000)\/",
        "Start":  "\/Date(1500208800000)\/",
        "BackupSetId":  "fb5bcc27-b2e0-4cae-98c9-868023acaed0",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126678000011813400001,
        "LastLSN":  17126678000012528800001,
        "CheckpointLSN":  17126678000010987200023,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500209100000)\/",
        "BackupFinishDate":  "\/Date(1500209100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3179ccfa-7637-4f53-9636-99ea6bee64ac",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500209100000)\/",
        "Start":  "\/Date(1500209100000)\/",
        "BackupSetId":  "3179ccfa-7637-4f53-9636-99ea6bee64ac",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126678000012528800001,
        "LastLSN":  17126679000000142600001,
        "CheckpointLSN":  17126679000000045100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500209400000)\/",
        "BackupFinishDate":  "\/Date(1500209400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c617b60c-397a-4a3e-9d94-fde9b5603058",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500209400000)\/",
        "Start":  "\/Date(1500209400000)\/",
        "BackupSetId":  "c617b60c-397a-4a3e-9d94-fde9b5603058",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000000142600001,
        "LastLSN":  17126679000000857500001,
        "CheckpointLSN":  17126679000000045100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500209700000)\/",
        "BackupFinishDate":  "\/Date(1500209700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "44e6a1f5-86cf-41e8-85b0-ff67ddaede0f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500209700000)\/",
        "Start":  "\/Date(1500209700000)\/",
        "BackupSetId":  "44e6a1f5-86cf-41e8-85b0-ff67ddaede0f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000000857500001,
        "LastLSN":  17126679000001584300001,
        "CheckpointLSN":  17126679000000045100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500210000000)\/",
        "BackupFinishDate":  "\/Date(1500210000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b79e599f-1ee8-477b-b012-47ca24778988",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500210000000)\/",
        "Start":  "\/Date(1500210000000)\/",
        "BackupSetId":  "b79e599f-1ee8-477b-b012-47ca24778988",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000001584300001,
        "LastLSN":  17126679000002303200001,
        "CheckpointLSN":  17126679000002195800023,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500210300000)\/",
        "BackupFinishDate":  "\/Date(1500210300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c11dab05-9f6e-4f9d-832d-a57634975679",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500210300000)\/",
        "Start":  "\/Date(1500210300000)\/",
        "BackupSetId":  "c11dab05-9f6e-4f9d-832d-a57634975679",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000002303200001,
        "LastLSN":  17126679000003020500001,
        "CheckpointLSN":  17126679000002195800023,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500210600000)\/",
        "BackupFinishDate":  "\/Date(1500210600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "471d3251-5248-4125-9737-9c61098b6c02",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500210600000)\/",
        "Start":  "\/Date(1500210600000)\/",
        "BackupSetId":  "471d3251-5248-4125-9737-9c61098b6c02",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000003020500001,
        "LastLSN":  17126679000003721800001,
        "CheckpointLSN":  17126679000002195800023,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500210901000)\/",
        "BackupFinishDate":  "\/Date(1500210901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c6493046-5314-465a-a0a5-03c3dea8808f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500210901000)\/",
        "Start":  "\/Date(1500210901000)\/",
        "BackupSetId":  "c6493046-5314-465a-a0a5-03c3dea8808f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000003721800001,
        "LastLSN":  17126679000004452800001,
        "CheckpointLSN":  17126679000004347100020,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500211200000)\/",
        "BackupFinishDate":  "\/Date(1500211200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "44c0fc49-a585-4690-8389-485eb74a5b7b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500211200000)\/",
        "Start":  "\/Date(1500211200000)\/",
        "BackupSetId":  "44c0fc49-a585-4690-8389-485eb74a5b7b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000004452800001,
        "LastLSN":  17126679000005155200001,
        "CheckpointLSN":  17126679000004347100020,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500211500000)\/",
        "BackupFinishDate":  "\/Date(1500211500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e2ec47fb-954c-41ae-80c0-dd32aef7fba0",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500211500000)\/",
        "Start":  "\/Date(1500211500000)\/",
        "BackupSetId":  "e2ec47fb-954c-41ae-80c0-dd32aef7fba0",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000005155200001,
        "LastLSN":  17126679000005856100001,
        "CheckpointLSN":  17126679000004347100020,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500211800000)\/",
        "BackupFinishDate":  "\/Date(1500211800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "731950a1-daf7-42aa-81f1-0c31fcdd97a0",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500211800000)\/",
        "Start":  "\/Date(1500211800000)\/",
        "BackupSetId":  "731950a1-daf7-42aa-81f1-0c31fcdd97a0",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000005856100001,
        "LastLSN":  17126679000006600400001,
        "CheckpointLSN":  17126679000006493300008,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500212100000)\/",
        "BackupFinishDate":  "\/Date(1500212100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "76619470-8829-47bd-b94d-88c5eb2ad9ad",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500212100000)\/",
        "Start":  "\/Date(1500212100000)\/",
        "BackupSetId":  "76619470-8829-47bd-b94d-88c5eb2ad9ad",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000006600400001,
        "LastLSN":  17126679000007343800001,
        "CheckpointLSN":  17126679000006493300008,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500212401000)\/",
        "BackupFinishDate":  "\/Date(1500212401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c870fc80-33de-4bd3-a675-6f25c96f0467",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500212401000)\/",
        "Start":  "\/Date(1500212401000)\/",
        "BackupSetId":  "c870fc80-33de-4bd3-a675-6f25c96f0467",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000007343800001,
        "LastLSN":  17126679000008081800001,
        "CheckpointLSN":  17126679000006493300008,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500212700000)\/",
        "BackupFinishDate":  "\/Date(1500212700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0dff8253-aecd-4050-83c4-659ab7cfff35",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500212700000)\/",
        "Start":  "\/Date(1500212700000)\/",
        "BackupSetId":  "0dff8253-aecd-4050-83c4-659ab7cfff35",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000008081800001,
        "LastLSN":  17126679000008816700001,
        "CheckpointLSN":  17126679000008631400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500213000000)\/",
        "BackupFinishDate":  "\/Date(1500213000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9751b4ca-5837-4bf4-ab9c-0e9d5eb7b774",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500213000000)\/",
        "Start":  "\/Date(1500213000000)\/",
        "BackupSetId":  "9751b4ca-5837-4bf4-ab9c-0e9d5eb7b774",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000008816700001,
        "LastLSN":  17126679000009546700001,
        "CheckpointLSN":  17126679000008631400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500213300000)\/",
        "BackupFinishDate":  "\/Date(1500213300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b5be0e6c-3e04-431c-8e5a-07970770d457",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500213300000)\/",
        "Start":  "\/Date(1500213300000)\/",
        "BackupSetId":  "b5be0e6c-3e04-431c-8e5a-07970770d457",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000009546700001,
        "LastLSN":  17126679000010262700001,
        "CheckpointLSN":  17126679000008631400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500213600000)\/",
        "BackupFinishDate":  "\/Date(1500213600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4bf7d7da-7ac8-4079-b424-b2bf91aece41",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500213600000)\/",
        "Start":  "\/Date(1500213600000)\/",
        "BackupSetId":  "4bf7d7da-7ac8-4079-b424-b2bf91aece41",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000010262700001,
        "LastLSN":  17126679000011007300001,
        "CheckpointLSN":  17126679000010697500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500213900000)\/",
        "BackupFinishDate":  "\/Date(1500213901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "52ab811e-8fc6-4623-8788-e07717e70ce6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500213901000)\/",
        "Start":  "\/Date(1500213900000)\/",
        "BackupSetId":  "52ab811e-8fc6-4623-8788-e07717e70ce6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000011007300001,
        "LastLSN":  17126679000011722700001,
        "CheckpointLSN":  17126679000010697500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500214200000)\/",
        "BackupFinishDate":  "\/Date(1500214200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "de4a62e9-d888-4542-8bda-011fc343daab",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500214200000)\/",
        "Start":  "\/Date(1500214200000)\/",
        "BackupSetId":  "de4a62e9-d888-4542-8bda-011fc343daab",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000011722700001,
        "LastLSN":  17126679000012449700001,
        "CheckpointLSN":  17126679000010697500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500214500000)\/",
        "BackupFinishDate":  "\/Date(1500214500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8c60a5db-b4da-40fd-9f6a-886e1735c204",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500214500000)\/",
        "Start":  "\/Date(1500214500000)\/",
        "BackupSetId":  "8c60a5db-b4da-40fd-9f6a-886e1735c204",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126679000012449700001,
        "LastLSN":  17126680000000063800001,
        "CheckpointLSN":  17126679000012765500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500214800000)\/",
        "BackupFinishDate":  "\/Date(1500214800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "271e29b0-413c-4a9b-aae6-c9a196956e51",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500214800000)\/",
        "Start":  "\/Date(1500214800000)\/",
        "BackupSetId":  "271e29b0-413c-4a9b-aae6-c9a196956e51",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000000063800001,
        "LastLSN":  17126680000000805400001,
        "CheckpointLSN":  17126680000000063800030,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500215100000)\/",
        "BackupFinishDate":  "\/Date(1500215100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "08c0e74f-9fc1-482a-996f-6314bfc50a4e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500215100000)\/",
        "Start":  "\/Date(1500215100000)\/",
        "BackupSetId":  "08c0e74f-9fc1-482a-996f-6314bfc50a4e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000000805400001,
        "LastLSN":  17126680000001522700001,
        "CheckpointLSN":  17126680000000063800030,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500215400000)\/",
        "BackupFinishDate":  "\/Date(1500215401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6dceb2e8-8fb2-46d0-af53-76f97cbbd69a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500215401000)\/",
        "Start":  "\/Date(1500215400000)\/",
        "BackupSetId":  "6dceb2e8-8fb2-46d0-af53-76f97cbbd69a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000001522700001,
        "LastLSN":  17126680000002268200001,
        "CheckpointLSN":  17126680000002131600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500215701000)\/",
        "BackupFinishDate":  "\/Date(1500215701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "851d8df0-aae4-4d89-92cd-ff4759811ea8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500215701000)\/",
        "Start":  "\/Date(1500215701000)\/",
        "BackupSetId":  "851d8df0-aae4-4d89-92cd-ff4759811ea8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000002268200001,
        "LastLSN":  17126680000002974200001,
        "CheckpointLSN":  17126680000002131600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500216000000)\/",
        "BackupFinishDate":  "\/Date(1500216000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "18839b34-298f-434e-aee8-fe5bd8efb4be",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500216000000)\/",
        "Start":  "\/Date(1500216000000)\/",
        "BackupSetId":  "18839b34-298f-434e-aee8-fe5bd8efb4be",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000002974200001,
        "LastLSN":  17126680000003692400001,
        "CheckpointLSN":  17126680000002131600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500216300000)\/",
        "BackupFinishDate":  "\/Date(1500216300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7dba5e97-937c-473d-a237-602bf6c14c3b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500216300000)\/",
        "Start":  "\/Date(1500216300000)\/",
        "BackupSetId":  "7dba5e97-937c-473d-a237-602bf6c14c3b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000003692400001,
        "LastLSN":  17126680000004413600001,
        "CheckpointLSN":  17126680000004243700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500216600000)\/",
        "BackupFinishDate":  "\/Date(1500216600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0be95ed7-8b55-401e-80b6-bb1ab5066dbe",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500216600000)\/",
        "Start":  "\/Date(1500216600000)\/",
        "BackupSetId":  "0be95ed7-8b55-401e-80b6-bb1ab5066dbe",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000004413600001,
        "LastLSN":  17126680000005134100001,
        "CheckpointLSN":  17126680000004243700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500216901000)\/",
        "BackupFinishDate":  "\/Date(1500216901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "862e467d-1548-4fd5-a5c4-20ebe715c8cc",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500216901000)\/",
        "Start":  "\/Date(1500216901000)\/",
        "BackupSetId":  "862e467d-1548-4fd5-a5c4-20ebe715c8cc",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000005134100001,
        "LastLSN":  17126680000005864000001,
        "CheckpointLSN":  17126680000004243700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500217200000)\/",
        "BackupFinishDate":  "\/Date(1500217200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "891f5484-c248-4e75-9e8a-3bd846b3f26d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500217200000)\/",
        "Start":  "\/Date(1500217200000)\/",
        "BackupSetId":  "891f5484-c248-4e75-9e8a-3bd846b3f26d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000005864000001,
        "LastLSN":  17126680000006584000001,
        "CheckpointLSN":  17126680000006310800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500217500000)\/",
        "BackupFinishDate":  "\/Date(1500217500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0928f3d2-a45f-48e2-9230-6b6789ca6417",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500217500000)\/",
        "Start":  "\/Date(1500217500000)\/",
        "BackupSetId":  "0928f3d2-a45f-48e2-9230-6b6789ca6417",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000006584000001,
        "LastLSN":  17126680000007298600001,
        "CheckpointLSN":  17126680000006310800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500217800000)\/",
        "BackupFinishDate":  "\/Date(1500217800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "20153807-99d2-482f-93dc-a224fe72f02e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500217800000)\/",
        "Start":  "\/Date(1500217800000)\/",
        "BackupSetId":  "20153807-99d2-482f-93dc-a224fe72f02e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000007298600001,
        "LastLSN":  17126680000008002200001,
        "CheckpointLSN":  17126680000006310800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500218101000)\/",
        "BackupFinishDate":  "\/Date(1500218101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "903df7a9-4592-47c2-8368-17a858556cf1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500218101000)\/",
        "Start":  "\/Date(1500218101000)\/",
        "BackupSetId":  "903df7a9-4592-47c2-8368-17a858556cf1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000008002200001,
        "LastLSN":  17126680000008729800001,
        "CheckpointLSN":  17126680000008376600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500218400000)\/",
        "BackupFinishDate":  "\/Date(1500218400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8d0e6e57-e3f8-4216-af60-80aa64d08b15",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500218400000)\/",
        "Start":  "\/Date(1500218400000)\/",
        "BackupSetId":  "8d0e6e57-e3f8-4216-af60-80aa64d08b15",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000008729800001,
        "LastLSN":  17126680000009438300001,
        "CheckpointLSN":  17126680000008376600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500218700000)\/",
        "BackupFinishDate":  "\/Date(1500218700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0fbc90bf-1d05-464f-b8b6-1a25ce7f240c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500218700000)\/",
        "Start":  "\/Date(1500218700000)\/",
        "BackupSetId":  "0fbc90bf-1d05-464f-b8b6-1a25ce7f240c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000009438300001,
        "LastLSN":  17126680000010140500001,
        "CheckpointLSN":  17126680000008376600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500219000000)\/",
        "BackupFinishDate":  "\/Date(1500219000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2c98f2c5-c780-46b1-9c2f-f54e318a6e30",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500219000000)\/",
        "Start":  "\/Date(1500219000000)\/",
        "BackupSetId":  "2c98f2c5-c780-46b1-9c2f-f54e318a6e30",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000010140500001,
        "LastLSN":  17126680000010890200001,
        "CheckpointLSN":  17126680000010444000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500219300000)\/",
        "BackupFinishDate":  "\/Date(1500219300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "18acba9e-3fd2-4286-9637-a60d19804f2f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500219300000)\/",
        "Start":  "\/Date(1500219300000)\/",
        "BackupSetId":  "18acba9e-3fd2-4286-9637-a60d19804f2f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000010890200001,
        "LastLSN":  17126680000012325500001,
        "CheckpointLSN":  17126680000010444000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500219601000)\/",
        "BackupFinishDate":  "\/Date(1500219601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e318c099-ed1e-4b0b-9ae1-31cfd1eb1575",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500219601000)\/",
        "Start":  "\/Date(1500219601000)\/",
        "BackupSetId":  "e318c099-ed1e-4b0b-9ae1-31cfd1eb1575",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000012325500001,
        "LastLSN":  17126680000013083200001,
        "CheckpointLSN":  17126680000012514000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500219900000)\/",
        "BackupFinishDate":  "\/Date(1500219900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ec2c7b0f-6d35-4d1e-b6cf-f90193e63387",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500219900000)\/",
        "Start":  "\/Date(1500219900000)\/",
        "BackupSetId":  "ec2c7b0f-6d35-4d1e-b6cf-f90193e63387",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126680000013083200001,
        "LastLSN":  17126681000000702400001,
        "CheckpointLSN":  17126680000012514000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500220200000)\/",
        "BackupFinishDate":  "\/Date(1500220200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "cdf94a75-4f41-4eaf-bada-82d150cb6b81",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500220200000)\/",
        "Start":  "\/Date(1500220200000)\/",
        "BackupSetId":  "cdf94a75-4f41-4eaf-bada-82d150cb6b81",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000000702400001,
        "LastLSN":  17126681000001451300001,
        "CheckpointLSN":  17126681000000702400032,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500220500000)\/",
        "BackupFinishDate":  "\/Date(1500220500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c10d9242-7806-4861-b58e-782f01c0faac",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500220500000)\/",
        "Start":  "\/Date(1500220500000)\/",
        "BackupSetId":  "c10d9242-7806-4861-b58e-782f01c0faac",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000001451300001,
        "LastLSN":  17126681000002166200001,
        "CheckpointLSN":  17126681000000702400032,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500220800000)\/",
        "BackupFinishDate":  "\/Date(1500220801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "48b9fdb6-7770-40dd-9b05-40a6418320e6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500220801000)\/",
        "Start":  "\/Date(1500220800000)\/",
        "BackupSetId":  "48b9fdb6-7770-40dd-9b05-40a6418320e6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000002166200001,
        "LastLSN":  17126681000002765900001,
        "CheckpointLSN":  17126681000000702400032,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500221100000)\/",
        "BackupFinishDate":  "\/Date(1500221100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8673dd0a-946e-48a6-b68f-4861090530dd",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500221100000)\/",
        "Start":  "\/Date(1500221100000)\/",
        "BackupSetId":  "8673dd0a-946e-48a6-b68f-4861090530dd",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000002765900001,
        "LastLSN":  17126681000003499800001,
        "CheckpointLSN":  17126681000002782900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500221400000)\/",
        "BackupFinishDate":  "\/Date(1500221400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7db09a5a-0c4b-419a-a2f5-d11d05fd9428",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500221400000)\/",
        "Start":  "\/Date(1500221400000)\/",
        "BackupSetId":  "7db09a5a-0c4b-419a-a2f5-d11d05fd9428",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000003499800001,
        "LastLSN":  17126681000004229400001,
        "CheckpointLSN":  17126681000002782900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500221700000)\/",
        "BackupFinishDate":  "\/Date(1500221700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "15cb41c2-3987-46a5-919c-239773bb7278",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500221700000)\/",
        "Start":  "\/Date(1500221700000)\/",
        "BackupSetId":  "15cb41c2-3987-46a5-919c-239773bb7278",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000004229400001,
        "LastLSN":  17126681000004952100001,
        "CheckpointLSN":  17126681000004909800027,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500222000000)\/",
        "BackupFinishDate":  "\/Date(1500222000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "237f52ff-eca9-4a53-8b7c-e2139d791e48",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500222000000)\/",
        "Start":  "\/Date(1500222000000)\/",
        "BackupSetId":  "237f52ff-eca9-4a53-8b7c-e2139d791e48",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000004952100001,
        "LastLSN":  17126681000005680900001,
        "CheckpointLSN":  17126681000004909800027,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500222301000)\/",
        "BackupFinishDate":  "\/Date(1500222301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c17e9cdb-963b-41f4-8df4-90bbc5e9d48e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500222301000)\/",
        "Start":  "\/Date(1500222301000)\/",
        "BackupSetId":  "c17e9cdb-963b-41f4-8df4-90bbc5e9d48e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000005680900001,
        "LastLSN":  17126681000006395700001,
        "CheckpointLSN":  17126681000004909800027,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500222600000)\/",
        "BackupFinishDate":  "\/Date(1500222600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "bdaf5521-d683-45b3-8cb3-b4fe8760446e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500222600000)\/",
        "Start":  "\/Date(1500222600000)\/",
        "BackupSetId":  "bdaf5521-d683-45b3-8cb3-b4fe8760446e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000006395700001,
        "LastLSN":  17126681000007139900001,
        "CheckpointLSN":  17126681000006976900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500222900000)\/",
        "BackupFinishDate":  "\/Date(1500222900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "38b68220-944d-4665-a498-2d8f1b8d5ab8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500222900000)\/",
        "Start":  "\/Date(1500222900000)\/",
        "BackupSetId":  "38b68220-944d-4665-a498-2d8f1b8d5ab8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000007139900001,
        "LastLSN":  17126681000007845400001,
        "CheckpointLSN":  17126681000006976900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500223200000)\/",
        "BackupFinishDate":  "\/Date(1500223200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e7b09873-d9a0-4486-b0ce-b33584c792a7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500223200000)\/",
        "Start":  "\/Date(1500223200000)\/",
        "BackupSetId":  "e7b09873-d9a0-4486-b0ce-b33584c792a7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000007845400001,
        "LastLSN":  17126681000008560100001,
        "CheckpointLSN":  17126681000006976900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500223500000)\/",
        "BackupFinishDate":  "\/Date(1500223500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "255e1f9d-5e4a-441b-8a8b-4ad911de09f0",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500223500000)\/",
        "Start":  "\/Date(1500223500000)\/",
        "BackupSetId":  "255e1f9d-5e4a-441b-8a8b-4ad911de09f0",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000008560100001,
        "LastLSN":  17126681000009279900001,
        "CheckpointLSN":  17126681000009045600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500223800000)\/",
        "BackupFinishDate":  "\/Date(1500223800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "79f42353-649b-41fe-a174-e9078e0b1ccb",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500223800000)\/",
        "Start":  "\/Date(1500223800000)\/",
        "BackupSetId":  "79f42353-649b-41fe-a174-e9078e0b1ccb",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000009279900001,
        "LastLSN":  17126681000010000200001,
        "CheckpointLSN":  17126681000009045600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500224100000)\/",
        "BackupFinishDate":  "\/Date(1500224100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1c078fff-5906-471e-888b-f83a3e89340c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500224100000)\/",
        "Start":  "\/Date(1500224100000)\/",
        "BackupSetId":  "1c078fff-5906-471e-888b-f83a3e89340c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000010000200001,
        "LastLSN":  17126681000010727200001,
        "CheckpointLSN":  17126681000009045600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500224400000)\/",
        "BackupFinishDate":  "\/Date(1500224400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "753c5f71-b26e-4296-99da-a876390d5bdf",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500224400000)\/",
        "Start":  "\/Date(1500224400000)\/",
        "BackupSetId":  "753c5f71-b26e-4296-99da-a876390d5bdf",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000010727200001,
        "LastLSN":  17126681000011444900001,
        "CheckpointLSN":  17126681000010739200127,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500224700000)\/",
        "BackupFinishDate":  "\/Date(1500224701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "dddfa0f2-ce99-4d09-8f8f-2786f6ccc6c3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500224701000)\/",
        "Start":  "\/Date(1500224700000)\/",
        "BackupSetId":  "dddfa0f2-ce99-4d09-8f8f-2786f6ccc6c3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000011444900001,
        "LastLSN":  17126681000012159400001,
        "CheckpointLSN":  17126681000010739200127,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500225001000)\/",
        "BackupFinishDate":  "\/Date(1500225001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b20e822e-e77f-4e2f-af4e-f57c7209456b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500225001000)\/",
        "Start":  "\/Date(1500225001000)\/",
        "BackupSetId":  "b20e822e-e77f-4e2f-af4e-f57c7209456b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000012159400001,
        "LastLSN":  17126681000012878100001,
        "CheckpointLSN":  17126681000012792900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500225300000)\/",
        "BackupFinishDate":  "\/Date(1500225300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "66b1c85d-9c92-4f3e-8370-baa4a7cc2d06",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500225300000)\/",
        "Start":  "\/Date(1500225300000)\/",
        "BackupSetId":  "66b1c85d-9c92-4f3e-8370-baa4a7cc2d06",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126681000012878100001,
        "LastLSN":  17126682000000487900001,
        "CheckpointLSN":  17126681000012792900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500225600000)\/",
        "BackupFinishDate":  "\/Date(1500225600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a0543d6a-9334-4540-9e4c-5a9e400fd4e3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500225600000)\/",
        "Start":  "\/Date(1500225600000)\/",
        "BackupSetId":  "a0543d6a-9334-4540-9e4c-5a9e400fd4e3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000000487900001,
        "LastLSN":  17126682000001211100001,
        "CheckpointLSN":  17126682000000487900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500225900000)\/",
        "BackupFinishDate":  "\/Date(1500225900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "28b4b6b1-e482-415c-b5c5-38796be84858",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500225900000)\/",
        "Start":  "\/Date(1500225900000)\/",
        "BackupSetId":  "28b4b6b1-e482-415c-b5c5-38796be84858",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000001211100001,
        "LastLSN":  17126682000001914300001,
        "CheckpointLSN":  17126682000000487900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500226200000)\/",
        "BackupFinishDate":  "\/Date(1500226200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3982c28b-5e3b-4c77-aa51-70bc361de2f5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500226200000)\/",
        "Start":  "\/Date(1500226200000)\/",
        "BackupSetId":  "3982c28b-5e3b-4c77-aa51-70bc361de2f5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000001914300001,
        "LastLSN":  17126682000002659900001,
        "CheckpointLSN":  17126682000002553900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500226500000)\/",
        "BackupFinishDate":  "\/Date(1500226501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3156ea6a-4aaf-457f-bcbf-184697e673a6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500226501000)\/",
        "Start":  "\/Date(1500226500000)\/",
        "BackupSetId":  "3156ea6a-4aaf-457f-bcbf-184697e673a6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000002659900001,
        "LastLSN":  17126682000003403700001,
        "CheckpointLSN":  17126682000002553900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500226800000)\/",
        "BackupFinishDate":  "\/Date(1500226800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "33b8c6e4-6709-4e1e-b3e4-30b8a08f22e5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500226800000)\/",
        "Start":  "\/Date(1500226800000)\/",
        "BackupSetId":  "33b8c6e4-6709-4e1e-b3e4-30b8a08f22e5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000003403700001,
        "LastLSN":  17126682000004146900001,
        "CheckpointLSN":  17126682000002553900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500227100000)\/",
        "BackupFinishDate":  "\/Date(1500227100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "de6cc3d1-1526-4183-9700-9cdd078fca96",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500227100000)\/",
        "Start":  "\/Date(1500227100000)\/",
        "BackupSetId":  "de6cc3d1-1526-4183-9700-9cdd078fca96",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000004146900001,
        "LastLSN":  17126682000004881300001,
        "CheckpointLSN":  17126682000004624800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500227400000)\/",
        "BackupFinishDate":  "\/Date(1500227400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9fddcd40-310f-47b4-b707-2285f5d9bd77",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500227400000)\/",
        "Start":  "\/Date(1500227400000)\/",
        "BackupSetId":  "9fddcd40-310f-47b4-b707-2285f5d9bd77",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000004881300001,
        "LastLSN":  17126682000005611300001,
        "CheckpointLSN":  17126682000004624800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500227701000)\/",
        "BackupFinishDate":  "\/Date(1500227701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a7898fe5-40d7-4b67-a3d3-8f595684277a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500227701000)\/",
        "Start":  "\/Date(1500227701000)\/",
        "BackupSetId":  "a7898fe5-40d7-4b67-a3d3-8f595684277a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000005611300001,
        "LastLSN":  17126682000006326400001,
        "CheckpointLSN":  17126682000004624800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500228000000)\/",
        "BackupFinishDate":  "\/Date(1500228000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0ddd4cd8-0e3f-478d-b248-4b027c936a6d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500228000000)\/",
        "Start":  "\/Date(1500228000000)\/",
        "BackupSetId":  "0ddd4cd8-0e3f-478d-b248-4b027c936a6d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000006326400001,
        "LastLSN":  17126682000007074400001,
        "CheckpointLSN":  17126682000006691600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500228300000)\/",
        "BackupFinishDate":  "\/Date(1500228300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "abd61b4f-79cf-460c-ae4b-3448ddfd0b96",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500228300000)\/",
        "Start":  "\/Date(1500228300000)\/",
        "BackupSetId":  "abd61b4f-79cf-460c-ae4b-3448ddfd0b96",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000007074400001,
        "LastLSN":  17126682000007791400001,
        "CheckpointLSN":  17126682000006691600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500228600000)\/",
        "BackupFinishDate":  "\/Date(1500228600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ce54ab35-1116-40ac-82ec-69b0cd314828",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500228600000)\/",
        "Start":  "\/Date(1500228600000)\/",
        "BackupSetId":  "ce54ab35-1116-40ac-82ec-69b0cd314828",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000007791400001,
        "LastLSN":  17126682000008519100001,
        "CheckpointLSN":  17126682000006691600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500228900000)\/",
        "BackupFinishDate":  "\/Date(1500228900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "96ff25bf-817b-4b66-b910-04ee551a76d5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500228900000)\/",
        "Start":  "\/Date(1500228900000)\/",
        "BackupSetId":  "96ff25bf-817b-4b66-b910-04ee551a76d5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000008519100001,
        "LastLSN":  17126682000009242700001,
        "CheckpointLSN":  17126682000008764200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500229201000)\/",
        "BackupFinishDate":  "\/Date(1500229201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "35f8dcd4-76d3-49e3-a1e1-b4502e135731",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500229201000)\/",
        "Start":  "\/Date(1500229201000)\/",
        "BackupSetId":  "35f8dcd4-76d3-49e3-a1e1-b4502e135731",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000009242700001,
        "LastLSN":  17126682000009970700001,
        "CheckpointLSN":  17126682000008764200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500229500000)\/",
        "BackupFinishDate":  "\/Date(1500229500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ac379ce4-cef7-41ff-a322-125350220d20",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500229500000)\/",
        "Start":  "\/Date(1500229500000)\/",
        "BackupSetId":  "ac379ce4-cef7-41ff-a322-125350220d20",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000009970700001,
        "LastLSN":  17126682000010686000001,
        "CheckpointLSN":  17126682000008764200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500229800000)\/",
        "BackupFinishDate":  "\/Date(1500229800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "da9a3dd8-7add-4208-bb33-b8edf9321b91",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500229800000)\/",
        "Start":  "\/Date(1500229800000)\/",
        "BackupSetId":  "da9a3dd8-7add-4208-bb33-b8edf9321b91",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000010686000001,
        "LastLSN":  17126682000011432000001,
        "CheckpointLSN":  17126682000010830700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500230100000)\/",
        "BackupFinishDate":  "\/Date(1500230100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fe0e3763-312a-474d-b01f-c432388a0133",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500230100000)\/",
        "Start":  "\/Date(1500230100000)\/",
        "BackupSetId":  "fe0e3763-312a-474d-b01f-c432388a0133",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000011432000001,
        "LastLSN":  17126682000012133800001,
        "CheckpointLSN":  17126682000010830700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500230401000)\/",
        "BackupFinishDate":  "\/Date(1500230401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ebe6a308-8279-4039-bf0d-447c1a235a46",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500230401000)\/",
        "Start":  "\/Date(1500230401000)\/",
        "BackupSetId":  "ebe6a308-8279-4039-bf0d-447c1a235a46",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000012133800001,
        "LastLSN":  17126682000012850000001,
        "CheckpointLSN":  17126682000010830700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500230700000)\/",
        "BackupFinishDate":  "\/Date(1500230700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b981a1ea-70f9-42db-9014-a145b8361dcb",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500230700000)\/",
        "Start":  "\/Date(1500230700000)\/",
        "BackupSetId":  "b981a1ea-70f9-42db-9014-a145b8361dcb",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126682000012850000001,
        "LastLSN":  17126683000000470400001,
        "CheckpointLSN":  17126682000012915200020,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500231000000)\/",
        "BackupFinishDate":  "\/Date(1500231000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5675acc2-185a-48a4-a963-eb292be45af8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500231000000)\/",
        "Start":  "\/Date(1500231000000)\/",
        "BackupSetId":  "5675acc2-185a-48a4-a963-eb292be45af8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000000470400001,
        "LastLSN":  17126683000001205800001,
        "CheckpointLSN":  17126683000000470500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500231300000)\/",
        "BackupFinishDate":  "\/Date(1500231300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5e9fc50f-379d-43f6-9a0e-31ab460e6311",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500231300000)\/",
        "Start":  "\/Date(1500231300000)\/",
        "BackupSetId":  "5e9fc50f-379d-43f6-9a0e-31ab460e6311",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000001205800001,
        "LastLSN":  17126683000001934100001,
        "CheckpointLSN":  17126683000000470500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500231601000)\/",
        "BackupFinishDate":  "\/Date(1500231601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b99b6e07-8b64-4a54-9549-a66c1ef6757b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500231601000)\/",
        "Start":  "\/Date(1500231601000)\/",
        "BackupSetId":  "b99b6e07-8b64-4a54-9549-a66c1ef6757b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000001934100001,
        "LastLSN":  17126683000003017500001,
        "CheckpointLSN":  17126683000002534300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500231900000)\/",
        "BackupFinishDate":  "\/Date(1500231900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a8d6e797-b163-430e-b741-71c045d1565e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500231900000)\/",
        "Start":  "\/Date(1500231900000)\/",
        "BackupSetId":  "a8d6e797-b163-430e-b741-71c045d1565e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000003017500001,
        "LastLSN":  17126683000003904900001,
        "CheckpointLSN":  17126683000002534300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500232200000)\/",
        "BackupFinishDate":  "\/Date(1500232201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "28960343-6340-48b5-a557-470e658a3269",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500232201000)\/",
        "Start":  "\/Date(1500232200000)\/",
        "BackupSetId":  "28960343-6340-48b5-a557-470e658a3269",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000003904900001,
        "LastLSN":  17126683000004771000001,
        "CheckpointLSN":  17126683000004614100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500232500000)\/",
        "BackupFinishDate":  "\/Date(1500232500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ce7faf45-8273-4cdb-b38e-bcbb245b7c6e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500232500000)\/",
        "Start":  "\/Date(1500232500000)\/",
        "BackupSetId":  "ce7faf45-8273-4cdb-b38e-bcbb245b7c6e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000004771000001,
        "LastLSN":  17126683000005488900001,
        "CheckpointLSN":  17126683000004614100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500232801000)\/",
        "BackupFinishDate":  "\/Date(1500232801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f4fa7ac6-74aa-4438-b458-ab7369d266da",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500232801000)\/",
        "Start":  "\/Date(1500232801000)\/",
        "BackupSetId":  "f4fa7ac6-74aa-4438-b458-ab7369d266da",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000005488900001,
        "LastLSN":  17126683000006189900001,
        "CheckpointLSN":  17126683000004614100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500233100000)\/",
        "BackupFinishDate":  "\/Date(1500233100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "654035aa-5f05-40aa-9e25-8591182ecbbc",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500233100000)\/",
        "Start":  "\/Date(1500233100000)\/",
        "BackupSetId":  "654035aa-5f05-40aa-9e25-8591182ecbbc",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000006189900001,
        "LastLSN":  17126683000006908300001,
        "CheckpointLSN":  17126683000006679800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500233400000)\/",
        "BackupFinishDate":  "\/Date(1500233400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3782fbfe-9ee6-4b39-8f05-4f0db92d473b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500233400000)\/",
        "Start":  "\/Date(1500233400000)\/",
        "BackupSetId":  "3782fbfe-9ee6-4b39-8f05-4f0db92d473b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000006908300001,
        "LastLSN":  17126683000007636900001,
        "CheckpointLSN":  17126683000006679800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500233700000)\/",
        "BackupFinishDate":  "\/Date(1500233701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "68f4ee67-1ef8-4e5c-b694-b74cf5c452d2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500233701000)\/",
        "Start":  "\/Date(1500233700000)\/",
        "BackupSetId":  "68f4ee67-1ef8-4e5c-b694-b74cf5c452d2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000007636900001,
        "LastLSN":  17126683000008377600001,
        "CheckpointLSN":  17126683000006679800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500234000000)\/",
        "BackupFinishDate":  "\/Date(1500234000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f3649c2a-391e-453d-8073-4cdce21d638a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500234000000)\/",
        "Start":  "\/Date(1500234000000)\/",
        "BackupSetId":  "f3649c2a-391e-453d-8073-4cdce21d638a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000008377600001,
        "LastLSN":  17126683000009132500001,
        "CheckpointLSN":  17126683000008745700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500234300000)\/",
        "BackupFinishDate":  "\/Date(1500234300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "012bc43c-b554-4887-a6fd-d8da7f6e2e4a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500234300000)\/",
        "Start":  "\/Date(1500234300000)\/",
        "BackupSetId":  "012bc43c-b554-4887-a6fd-d8da7f6e2e4a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000009132500001,
        "LastLSN":  17126683000009847200001,
        "CheckpointLSN":  17126683000008745700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500234600000)\/",
        "BackupFinishDate":  "\/Date(1500234600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e037904c-0e04-47bb-a9c4-12aa085f0454",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500234600000)\/",
        "Start":  "\/Date(1500234600000)\/",
        "BackupSetId":  "e037904c-0e04-47bb-a9c4-12aa085f0454",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000009847200001,
        "LastLSN":  17126683000010578500001,
        "CheckpointLSN":  17126683000008745700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500234900000)\/",
        "BackupFinishDate":  "\/Date(1500234900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2344c6db-3994-4e95-9437-00bb3d846339",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500234900000)\/",
        "Start":  "\/Date(1500234900000)\/",
        "BackupSetId":  "2344c6db-3994-4e95-9437-00bb3d846339",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000010578500001,
        "LastLSN":  17126683000011311300001,
        "CheckpointLSN":  17126683000010814600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500235200000)\/",
        "BackupFinishDate":  "\/Date(1500235201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "93b6f4f5-d941-48a3-9cf1-4a057b560f6b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500235201000)\/",
        "Start":  "\/Date(1500235200000)\/",
        "BackupSetId":  "93b6f4f5-d941-48a3-9cf1-4a057b560f6b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000011311300001,
        "LastLSN":  17126683000012101400001,
        "CheckpointLSN":  17126683000010814600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500235500000)\/",
        "BackupFinishDate":  "\/Date(1500235501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d4ddd413-7f61-46ed-8fed-7b42b3c64fac",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500235501000)\/",
        "Start":  "\/Date(1500235500000)\/",
        "BackupSetId":  "d4ddd413-7f61-46ed-8fed-7b42b3c64fac",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000012101400001,
        "LastLSN":  17126683000012814300001,
        "CheckpointLSN":  17126683000010814600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500235800000)\/",
        "BackupFinishDate":  "\/Date(1500235800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b8601787-062e-42c1-9da7-0fdc5ef34f01",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500235800000)\/",
        "Start":  "\/Date(1500235800000)\/",
        "BackupSetId":  "b8601787-062e-42c1-9da7-0fdc5ef34f01",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126683000012814300001,
        "LastLSN":  17126684000000465300001,
        "CheckpointLSN":  17126683000012901100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500236100000)\/",
        "BackupFinishDate":  "\/Date(1500236100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "83abeb49-abc9-4408-947a-abd769145eba",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500236100000)\/",
        "Start":  "\/Date(1500236100000)\/",
        "BackupSetId":  "83abeb49-abc9-4408-947a-abd769145eba",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000000465300001,
        "LastLSN":  17126684000001183800001,
        "CheckpointLSN":  17126684000000465300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500236400000)\/",
        "BackupFinishDate":  "\/Date(1500236400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b8a9d594-ef73-4e38-bb82-acf49a4adeda",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500236400000)\/",
        "Start":  "\/Date(1500236400000)\/",
        "BackupSetId":  "b8a9d594-ef73-4e38-bb82-acf49a4adeda",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000001183800001,
        "LastLSN":  17126684000001912500001,
        "CheckpointLSN":  17126684000000465300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500236700000)\/",
        "BackupFinishDate":  "\/Date(1500236700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8405ffd7-f6f2-4ca2-a3e7-19c02b1b7a5f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500236700000)\/",
        "Start":  "\/Date(1500236700000)\/",
        "BackupSetId":  "8405ffd7-f6f2-4ca2-a3e7-19c02b1b7a5f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000001912500001,
        "LastLSN":  17126684000002644100001,
        "CheckpointLSN":  17126684000002530900004,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500237001000)\/",
        "BackupFinishDate":  "\/Date(1500237001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b5686649-0a07-4a26-9454-eb0056fe83af",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500237001000)\/",
        "Start":  "\/Date(1500237001000)\/",
        "BackupSetId":  "b5686649-0a07-4a26-9454-eb0056fe83af",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000002644100001,
        "LastLSN":  17126684000003344900001,
        "CheckpointLSN":  17126684000002530900004,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500237300000)\/",
        "BackupFinishDate":  "\/Date(1500237300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "504353e3-0034-4aa7-8743-cb620f75e107",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500237300000)\/",
        "Start":  "\/Date(1500237300000)\/",
        "BackupSetId":  "504353e3-0034-4aa7-8743-cb620f75e107",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000003344900001,
        "LastLSN":  17126684000004031300001,
        "CheckpointLSN":  17126684000002530900004,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500237600000)\/",
        "BackupFinishDate":  "\/Date(1500237600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "68d00574-4d9a-432d-b830-f74792d22d28",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500237600000)\/",
        "Start":  "\/Date(1500237600000)\/",
        "BackupSetId":  "68d00574-4d9a-432d-b830-f74792d22d28",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000004031300001,
        "LastLSN":  17126684000004762900001,
        "CheckpointLSN":  17126684000004597700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500237900000)\/",
        "BackupFinishDate":  "\/Date(1500237900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6ab4e7a1-c887-4e17-bb79-a729014e75a1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500237900000)\/",
        "Start":  "\/Date(1500237900000)\/",
        "BackupSetId":  "6ab4e7a1-c887-4e17-bb79-a729014e75a1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000004762900001,
        "LastLSN":  17126684000005463800001,
        "CheckpointLSN":  17126684000004597700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500238201000)\/",
        "BackupFinishDate":  "\/Date(1500238201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f812e7d1-33c9-4870-bae6-9709f01e7eb3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500238201000)\/",
        "Start":  "\/Date(1500238201000)\/",
        "BackupSetId":  "f812e7d1-33c9-4870-bae6-9709f01e7eb3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000005463800001,
        "LastLSN":  17126684000006180400001,
        "CheckpointLSN":  17126684000004597700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500238500000)\/",
        "BackupFinishDate":  "\/Date(1500238500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "31b044a1-60ba-4773-b413-0ae1c3deaf70",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500238500000)\/",
        "Start":  "\/Date(1500238500000)\/",
        "BackupSetId":  "31b044a1-60ba-4773-b413-0ae1c3deaf70",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000006180400001,
        "LastLSN":  17126684000006925200001,
        "CheckpointLSN":  17126684000006664600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500238800000)\/",
        "BackupFinishDate":  "\/Date(1500238800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "56b853f5-d622-4672-bfa9-a2001335c596",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500238800000)\/",
        "Start":  "\/Date(1500238800000)\/",
        "BackupSetId":  "56b853f5-d622-4672-bfa9-a2001335c596",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000006925200001,
        "LastLSN":  17126684000007627800001,
        "CheckpointLSN":  17126684000006664600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500239100000)\/",
        "BackupFinishDate":  "\/Date(1500239100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "42585c63-ad28-4d02-952f-fd444eabc4d8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500239100000)\/",
        "Start":  "\/Date(1500239100000)\/",
        "BackupSetId":  "42585c63-ad28-4d02-952f-fd444eabc4d8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000007627800001,
        "LastLSN":  17126684000008342100001,
        "CheckpointLSN":  17126684000006664600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500239400000)\/",
        "BackupFinishDate":  "\/Date(1500239400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3af0f859-cc4b-4927-8fbf-fc8109d954e5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500239400000)\/",
        "Start":  "\/Date(1500239400000)\/",
        "BackupSetId":  "3af0f859-cc4b-4927-8fbf-fc8109d954e5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000008342100001,
        "LastLSN":  17126684000009061800001,
        "CheckpointLSN":  17126684000008730200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500239701000)\/",
        "BackupFinishDate":  "\/Date(1500239701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9640df99-a515-4445-90c3-68caf91f0232",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500239701000)\/",
        "Start":  "\/Date(1500239701000)\/",
        "BackupSetId":  "9640df99-a515-4445-90c3-68caf91f0232",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000009061800001,
        "LastLSN":  17126684000009777600001,
        "CheckpointLSN":  17126684000008730200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500240000000)\/",
        "BackupFinishDate":  "\/Date(1500240000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0057b8f9-6d60-4525-9fd3-c60bec5b1eb6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500240000000)\/",
        "Start":  "\/Date(1500240000000)\/",
        "BackupSetId":  "0057b8f9-6d60-4525-9fd3-c60bec5b1eb6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000009777600001,
        "LastLSN":  17126684000010478200001,
        "CheckpointLSN":  17126684000008730200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500240300000)\/",
        "BackupFinishDate":  "\/Date(1500240300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "603a1400-45ab-4cce-8273-0d6eee141082",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500240300000)\/",
        "Start":  "\/Date(1500240300000)\/",
        "BackupSetId":  "603a1400-45ab-4cce-8273-0d6eee141082",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000010478200001,
        "LastLSN":  17126684000011197700001,
        "CheckpointLSN":  17126684000010800900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500240600000)\/",
        "BackupFinishDate":  "\/Date(1500240600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e659924f-adf8-45b1-8d8c-2a9ce0116568",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500240600000)\/",
        "Start":  "\/Date(1500240600000)\/",
        "BackupSetId":  "e659924f-adf8-45b1-8d8c-2a9ce0116568",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000011197700001,
        "LastLSN":  17126684000011925700001,
        "CheckpointLSN":  17126684000010800900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500240901000)\/",
        "BackupFinishDate":  "\/Date(1500240901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fc81ea12-d814-4366-a595-028f8198b2ec",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500240901000)\/",
        "Start":  "\/Date(1500240901000)\/",
        "BackupSetId":  "fc81ea12-d814-4366-a595-028f8198b2ec",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000011925700001,
        "LastLSN":  17126684000012668100001,
        "CheckpointLSN":  17126684000010800900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500241200000)\/",
        "BackupFinishDate":  "\/Date(1500241200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c13b0050-0834-4dce-8439-92ba251e84f7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500241200000)\/",
        "Start":  "\/Date(1500241200000)\/",
        "BackupSetId":  "c13b0050-0834-4dce-8439-92ba251e84f7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126684000012668100001,
        "LastLSN":  17126687000000215000001,
        "CheckpointLSN":  17126684000012871700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500241500000)\/",
        "BackupFinishDate":  "\/Date(1500241500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4da030e1-a61f-4e23-b6b1-ff4b21aeb0dc",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500241500000)\/",
        "Start":  "\/Date(1500241500000)\/",
        "BackupSetId":  "4da030e1-a61f-4e23-b6b1-ff4b21aeb0dc",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126687000000215000001,
        "LastLSN":  17126687000000948600001,
        "CheckpointLSN":  17126687000000215100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500241800000)\/",
        "BackupFinishDate":  "\/Date(1500241800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4097a22b-a79c-4978-a5ad-88aec254dcd3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500241800000)\/",
        "Start":  "\/Date(1500241800000)\/",
        "BackupSetId":  "4097a22b-a79c-4978-a5ad-88aec254dcd3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126687000000948600001,
        "LastLSN":  17126687000001681600001,
        "CheckpointLSN":  17126687000000215100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500242100000)\/",
        "BackupFinishDate":  "\/Date(1500242101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9882ac97-c42d-408a-ad17-6534d437e6ab",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500242101000)\/",
        "Start":  "\/Date(1500242100000)\/",
        "BackupSetId":  "9882ac97-c42d-408a-ad17-6534d437e6ab",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126687000001681600001,
        "LastLSN":  17126687000002414600001,
        "CheckpointLSN":  17126687000002280700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500242401000)\/",
        "BackupFinishDate":  "\/Date(1500242401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8b26e506-6641-4719-8952-4f07fc725186",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500242401000)\/",
        "Start":  "\/Date(1500242401000)\/",
        "BackupSetId":  "8b26e506-6641-4719-8952-4f07fc725186",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126687000002414600001,
        "LastLSN":  17126687000003143700001,
        "CheckpointLSN":  17126687000002280700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500242700000)\/",
        "BackupFinishDate":  "\/Date(1500242700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a6e4b7e5-2e96-4ab4-95a5-dd4938b2135b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500242700000)\/",
        "Start":  "\/Date(1500242700000)\/",
        "BackupSetId":  "a6e4b7e5-2e96-4ab4-95a5-dd4938b2135b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126687000003143700001,
        "LastLSN":  17126687000003860400001,
        "CheckpointLSN":  17126687000002280700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500243000000)\/",
        "BackupFinishDate":  "\/Date(1500243000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8c8f942c-53ec-44af-8936-a34b58600ac9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500243000000)\/",
        "Start":  "\/Date(1500243000000)\/",
        "BackupSetId":  "8c8f942c-53ec-44af-8936-a34b58600ac9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126687000003860400001,
        "LastLSN":  17126687000004606600001,
        "CheckpointLSN":  17126687000004347500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500243300000)\/",
        "BackupFinishDate":  "\/Date(1500243300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0afa1fc6-8bbc-4b21-9105-c909a92f6f45",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500243300000)\/",
        "Start":  "\/Date(1500243300000)\/",
        "BackupSetId":  "0afa1fc6-8bbc-4b21-9105-c909a92f6f45",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126687000004606600001,
        "LastLSN":  17126687000005307200001,
        "CheckpointLSN":  17126687000004347500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500243600000)\/",
        "BackupFinishDate":  "\/Date(1500243601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "98df7cf6-818a-4c01-a617-b29e350dc0a0",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500243601000)\/",
        "Start":  "\/Date(1500243600000)\/",
        "BackupSetId":  "98df7cf6-818a-4c01-a617-b29e350dc0a0",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126687000005307200001,
        "LastLSN":  17126687000006035500001,
        "CheckpointLSN":  17126687000004347500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500243900000)\/",
        "BackupFinishDate":  "\/Date(1500243900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "42dc5510-7c3d-4c7c-8cb1-64bdf03824f9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500243900000)\/",
        "Start":  "\/Date(1500243900000)\/",
        "BackupSetId":  "42dc5510-7c3d-4c7c-8cb1-64bdf03824f9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126687000006035500001,
        "LastLSN":  17126687000006768500001,
        "CheckpointLSN":  17126687000006425200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500244200000)\/",
        "BackupFinishDate":  "\/Date(1500244200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1bfffeaf-2e97-4b21-8c37-39272562c5b4",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500244200000)\/",
        "Start":  "\/Date(1500244200000)\/",
        "BackupSetId":  "1bfffeaf-2e97-4b21-8c37-39272562c5b4",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126687000006768500001,
        "LastLSN":  17126687000007495600001,
        "CheckpointLSN":  17126687000006425200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500244500000)\/",
        "BackupFinishDate":  "\/Date(1500244500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c55be641-c225-4ef9-9040-5aba1f016dfc",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500244500000)\/",
        "Start":  "\/Date(1500244500000)\/",
        "BackupSetId":  "c55be641-c225-4ef9-9040-5aba1f016dfc",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126687000007495600001,
        "LastLSN":  17126687000008200500001,
        "CheckpointLSN":  17126687000006425200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500244800000)\/",
        "BackupFinishDate":  "\/Date(1500244800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "01450ac4-d686-4dfb-8082-7dd10c56e2ff",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500244800000)\/",
        "Start":  "\/Date(1500244800000)\/",
        "BackupSetId":  "01450ac4-d686-4dfb-8082-7dd10c56e2ff",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126687000008200500001,
        "LastLSN":  17126687000008934000001,
        "CheckpointLSN":  17126687000008494600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500245100000)\/",
        "BackupFinishDate":  "\/Date(1500245100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "775ea127-677e-4173-91b2-daf49a1c7a43",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500245100000)\/",
        "Start":  "\/Date(1500245100000)\/",
        "BackupSetId":  "775ea127-677e-4173-91b2-daf49a1c7a43",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126687000008934000001,
        "LastLSN":  17126687000009636300001,
        "CheckpointLSN":  17126687000008494600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500245400000)\/",
        "BackupFinishDate":  "\/Date(1500245400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b48182e4-4c5d-47b1-8d3e-d9b82773e78e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500245400000)\/",
        "Start":  "\/Date(1500245400000)\/",
        "BackupSetId":  "b48182e4-4c5d-47b1-8d3e-d9b82773e78e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126687000009636300001,
        "LastLSN":  17126687000010355200001,
        "CheckpointLSN":  17126687000008494600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500245700000)\/",
        "BackupFinishDate":  "\/Date(1500245700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9d20a7cb-6595-41c6-b6c4-bcee1fa43292",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500245700000)\/",
        "Start":  "\/Date(1500245700000)\/",
        "BackupSetId":  "9d20a7cb-6595-41c6-b6c4-bcee1fa43292",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126687000010355200001,
        "LastLSN":  17126687000011101300001,
        "CheckpointLSN":  17126687000010561700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500246000000)\/",
        "BackupFinishDate":  "\/Date(1500246000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2b0fb94e-2865-4c80-a389-30df74273792",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500246000000)\/",
        "Start":  "\/Date(1500246000000)\/",
        "BackupSetId":  "2b0fb94e-2865-4c80-a389-30df74273792",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126687000011101300001,
        "LastLSN":  17126688000005292100001,
        "CheckpointLSN":  17126688000004924900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500246300000)\/",
        "BackupFinishDate":  "\/Date(1500246301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "45300aa9-6702-4506-aaa5-6693cb69326a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500246301000)\/",
        "Start":  "\/Date(1500246300000)\/",
        "BackupSetId":  "45300aa9-6702-4506-aaa5-6693cb69326a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126688000005292100001,
        "LastLSN":  17126688000006006500001,
        "CheckpointLSN":  17126688000004924900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500246600000)\/",
        "BackupFinishDate":  "\/Date(1500246600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4df27c61-e3cf-48b8-bd64-2ad8c8059236",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500246600000)\/",
        "Start":  "\/Date(1500246600000)\/",
        "BackupSetId":  "4df27c61-e3cf-48b8-bd64-2ad8c8059236",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126688000006006500001,
        "LastLSN":  17126688000006708300001,
        "CheckpointLSN":  17126688000004924900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500246900000)\/",
        "BackupFinishDate":  "\/Date(1500246900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "90bdb2af-cae2-4b7c-bae5-45e79017bc40",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500246900000)\/",
        "Start":  "\/Date(1500246900000)\/",
        "BackupSetId":  "90bdb2af-cae2-4b7c-bae5-45e79017bc40",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126688000006708300001,
        "LastLSN":  17126688000007442900001,
        "CheckpointLSN":  17126688000006991000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500247200000)\/",
        "BackupFinishDate":  "\/Date(1500247200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9fd10ed0-d6d0-4369-a12a-815bd60907ed",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500247200000)\/",
        "Start":  "\/Date(1500247200000)\/",
        "BackupSetId":  "9fd10ed0-d6d0-4369-a12a-815bd60907ed",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126688000007442900001,
        "LastLSN":  17126688000008145900001,
        "CheckpointLSN":  17126688000006991000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500247500000)\/",
        "BackupFinishDate":  "\/Date(1500247500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f50b1c44-c467-4661-aed0-c5d6944d5af2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500247500000)\/",
        "Start":  "\/Date(1500247500000)\/",
        "BackupSetId":  "f50b1c44-c467-4661-aed0-c5d6944d5af2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126688000008145900001,
        "LastLSN":  17126688000008846700001,
        "CheckpointLSN":  17126688000006991000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500247800000)\/",
        "BackupFinishDate":  "\/Date(1500247800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b18cca4c-3823-4bb5-8d41-404eb2e8434d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500247800000)\/",
        "Start":  "\/Date(1500247800000)\/",
        "BackupSetId":  "b18cca4c-3823-4bb5-8d41-404eb2e8434d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126688000008846700001,
        "LastLSN":  17126689000008054800001,
        "CheckpointLSN":  17126689000006141400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500248101000)\/",
        "BackupFinishDate":  "\/Date(1500248101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1702b488-96ce-41ab-98ac-c13e1945eb1e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500248101000)\/",
        "Start":  "\/Date(1500248101000)\/",
        "BackupSetId":  "1702b488-96ce-41ab-98ac-c13e1945eb1e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126689000008054800001,
        "LastLSN":  17126690000009720900001,
        "CheckpointLSN":  17126690000008855400003,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500248400000)\/",
        "BackupFinishDate":  "\/Date(1500248400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "bba380d5-114a-4409-ac7d-a3b2b22c7637",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500248400000)\/",
        "Start":  "\/Date(1500248400000)\/",
        "BackupSetId":  "bba380d5-114a-4409-ac7d-a3b2b22c7637",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126690000009720900001,
        "LastLSN":  17126691000011881700001,
        "CheckpointLSN":  17126691000011431000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500248700000)\/",
        "BackupFinishDate":  "\/Date(1500248700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6024ff27-c2ab-4ee5-945d-34b31f8215e3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500248700000)\/",
        "Start":  "\/Date(1500248700000)\/",
        "BackupSetId":  "6024ff27-c2ab-4ee5-945d-34b31f8215e3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126691000011881700001,
        "LastLSN":  17126693000003831600001,
        "CheckpointLSN":  17126693000003759700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500249000000)\/",
        "BackupFinishDate":  "\/Date(1500249001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2f61d707-44e9-4ca0-88e1-0c85a8b2e353",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500249001000)\/",
        "Start":  "\/Date(1500249000000)\/",
        "BackupSetId":  "2f61d707-44e9-4ca0-88e1-0c85a8b2e353",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126693000003831600001,
        "LastLSN":  17126694000011369200001,
        "CheckpointLSN":  17126694000007839400009,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500249300000)\/",
        "BackupFinishDate":  "\/Date(1500249301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c41828ec-4149-456f-81ef-2779ca82ac40",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500249301000)\/",
        "Start":  "\/Date(1500249300000)\/",
        "BackupSetId":  "c41828ec-4149-456f-81ef-2779ca82ac40",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126694000011369200001,
        "LastLSN":  17126696000005429500001,
        "CheckpointLSN":  17126696000002669000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500249601000)\/",
        "BackupFinishDate":  "\/Date(1500249602000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8d2370cb-fccd-47d1-8e84-83c839ce9ada",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500249602000)\/",
        "Start":  "\/Date(1500249601000)\/",
        "BackupSetId":  "8d2370cb-fccd-47d1-8e84-83c839ce9ada",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126696000005429500001,
        "LastLSN":  17126697000002299300001,
        "CheckpointLSN":  17126697000002057900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500249900000)\/",
        "BackupFinishDate":  "\/Date(1500249901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a8ef71fe-e48b-4cb6-8beb-99d4557398a5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500249901000)\/",
        "Start":  "\/Date(1500249900000)\/",
        "BackupSetId":  "a8ef71fe-e48b-4cb6-8beb-99d4557398a5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126697000002299300001,
        "LastLSN":  17126697000003013100001,
        "CheckpointLSN":  17126697000002057900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500250201000)\/",
        "BackupFinishDate":  "\/Date(1500250201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "52e2e7fd-bb18-44ac-bbdf-585131b6b4d9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500250201000)\/",
        "Start":  "\/Date(1500250201000)\/",
        "BackupSetId":  "52e2e7fd-bb18-44ac-bbdf-585131b6b4d9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126697000003013100001,
        "LastLSN":  17126697000003738100001,
        "CheckpointLSN":  17126697000002057900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500250500000)\/",
        "BackupFinishDate":  "\/Date(1500250500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e6285c0a-6d69-4334-b6ff-628647024238",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500250500000)\/",
        "Start":  "\/Date(1500250500000)\/",
        "BackupSetId":  "e6285c0a-6d69-4334-b6ff-628647024238",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126697000003738100001,
        "LastLSN":  17126697000004457100001,
        "CheckpointLSN":  17126697000004175300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500250800000)\/",
        "BackupFinishDate":  "\/Date(1500250800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ace942e3-ee77-4947-8789-935ec066e6b6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500250800000)\/",
        "Start":  "\/Date(1500250800000)\/",
        "BackupSetId":  "ace942e3-ee77-4947-8789-935ec066e6b6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126697000004457100001,
        "LastLSN":  17126697000005185000001,
        "CheckpointLSN":  17126697000004175300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500251101000)\/",
        "BackupFinishDate":  "\/Date(1500251101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "540e22b1-ee98-41b2-8bcd-b3a3a34c6942",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500251101000)\/",
        "Start":  "\/Date(1500251101000)\/",
        "BackupSetId":  "540e22b1-ee98-41b2-8bcd-b3a3a34c6942",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126697000005185000001,
        "LastLSN":  17126697000005902200001,
        "CheckpointLSN":  17126697000004175300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500251401000)\/",
        "BackupFinishDate":  "\/Date(1500251401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5a53147a-01ba-4b64-b7cc-2afdf29640a5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500251401000)\/",
        "Start":  "\/Date(1500251401000)\/",
        "BackupSetId":  "5a53147a-01ba-4b64-b7cc-2afdf29640a5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126697000005902200001,
        "LastLSN":  17126697000006649100001,
        "CheckpointLSN":  17126697000006242100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500251701000)\/",
        "BackupFinishDate":  "\/Date(1500251701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1eaa54f9-9a44-4b10-95e0-4161d6e44226",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500251701000)\/",
        "Start":  "\/Date(1500251701000)\/",
        "BackupSetId":  "1eaa54f9-9a44-4b10-95e0-4161d6e44226",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126697000006649100001,
        "LastLSN":  17126697000007354500001,
        "CheckpointLSN":  17126697000006242100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500252000000)\/",
        "BackupFinishDate":  "\/Date(1500252000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c48cd96d-b82b-468e-a36d-12b58db68013",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500252000000)\/",
        "Start":  "\/Date(1500252000000)\/",
        "BackupSetId":  "c48cd96d-b82b-468e-a36d-12b58db68013",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126697000007354500001,
        "LastLSN":  17126697000008071000001,
        "CheckpointLSN":  17126697000006242100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500252300000)\/",
        "BackupFinishDate":  "\/Date(1500252300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c376c204-ab49-40c6-8ed8-9f54c4e0e295",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500252300000)\/",
        "Start":  "\/Date(1500252300000)\/",
        "BackupSetId":  "c376c204-ab49-40c6-8ed8-9f54c4e0e295",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126697000008071000001,
        "LastLSN":  17126697000008790100001,
        "CheckpointLSN":  17126697000008320700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500252600000)\/",
        "BackupFinishDate":  "\/Date(1500252600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1bf44aba-cdd7-4db5-9729-c5548bcc1a85",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500252600000)\/",
        "Start":  "\/Date(1500252600000)\/",
        "BackupSetId":  "1bf44aba-cdd7-4db5-9729-c5548bcc1a85",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126697000008790100001,
        "LastLSN":  17126697000009509900001,
        "CheckpointLSN":  17126697000008320700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500252900000)\/",
        "BackupFinishDate":  "\/Date(1500252900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "bebcc39f-d3ae-450d-bedd-cd50c6dab78d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500252900000)\/",
        "Start":  "\/Date(1500252900000)\/",
        "BackupSetId":  "bebcc39f-d3ae-450d-bedd-cd50c6dab78d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126697000009509900001,
        "LastLSN":  17126697000010235800001,
        "CheckpointLSN":  17126697000008320700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500253201000)\/",
        "BackupFinishDate":  "\/Date(1500253201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "98df9d17-541d-4238-b466-cb39729264a2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500253201000)\/",
        "Start":  "\/Date(1500253201000)\/",
        "BackupSetId":  "98df9d17-541d-4238-b466-cb39729264a2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126697000010235800001,
        "LastLSN":  17126697000010955300001,
        "CheckpointLSN":  17126697000010397200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500253500000)\/",
        "BackupFinishDate":  "\/Date(1500253500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "57d04681-6fe1-49c2-b1e3-cafb65c46ba2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500253500000)\/",
        "Start":  "\/Date(1500253500000)\/",
        "BackupSetId":  "57d04681-6fe1-49c2-b1e3-cafb65c46ba2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126697000010955300001,
        "LastLSN":  17126697000011669800001,
        "CheckpointLSN":  17126697000010397200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500253800000)\/",
        "BackupFinishDate":  "\/Date(1500253800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f05c0151-d2ec-4a7e-b8ed-cc4cc970916e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500253800000)\/",
        "Start":  "\/Date(1500253800000)\/",
        "BackupSetId":  "f05c0151-d2ec-4a7e-b8ed-cc4cc970916e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126697000011669800001,
        "LastLSN":  17126697000012372000001,
        "CheckpointLSN":  17126697000010397200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500254100000)\/",
        "BackupFinishDate":  "\/Date(1500254100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "95444bca-e7d9-4be6-8b44-5e91988bbda5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500254100000)\/",
        "Start":  "\/Date(1500254100000)\/",
        "BackupSetId":  "95444bca-e7d9-4be6-8b44-5e91988bbda5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126697000012372000001,
        "LastLSN":  17126697000013101600001,
        "CheckpointLSN":  17126697000012533300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500254400000)\/",
        "BackupFinishDate":  "\/Date(1500254400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7585f2e6-33f5-44ba-a9b7-5423ed94bf85",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500254400000)\/",
        "Start":  "\/Date(1500254400000)\/",
        "BackupSetId":  "7585f2e6-33f5-44ba-a9b7-5423ed94bf85",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126697000013101600001,
        "LastLSN":  17126698000000694300001,
        "CheckpointLSN":  17126697000012533300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500254700000)\/",
        "BackupFinishDate":  "\/Date(1500254700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3b68377d-d26a-46c7-a13d-a067a566213e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500254700000)\/",
        "Start":  "\/Date(1500254700000)\/",
        "BackupSetId":  "3b68377d-d26a-46c7-a13d-a067a566213e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000000694300001,
        "LastLSN":  17126698000001414800001,
        "CheckpointLSN":  17126698000000694500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500255000000)\/",
        "BackupFinishDate":  "\/Date(1500255000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "644376d9-beb9-4d2e-a317-fa87eaac7df9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500255000000)\/",
        "Start":  "\/Date(1500255000000)\/",
        "BackupSetId":  "644376d9-beb9-4d2e-a317-fa87eaac7df9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000001414800001,
        "LastLSN":  17126698000002142800001,
        "CheckpointLSN":  17126698000000694500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500255300000)\/",
        "BackupFinishDate":  "\/Date(1500255300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ec9a8d07-1206-4ddd-ba68-aaf4bf64e3ac",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500255300000)\/",
        "Start":  "\/Date(1500255300000)\/",
        "BackupSetId":  "ec9a8d07-1206-4ddd-ba68-aaf4bf64e3ac",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000002142800001,
        "LastLSN":  17126698000002903500001,
        "CheckpointLSN":  17126698000002760800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500255600000)\/",
        "BackupFinishDate":  "\/Date(1500255600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ec7fe888-6685-4baf-b406-7eac4ee96331",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500255600000)\/",
        "Start":  "\/Date(1500255600000)\/",
        "BackupSetId":  "ec7fe888-6685-4baf-b406-7eac4ee96331",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000002903500001,
        "LastLSN":  17126698000003642300001,
        "CheckpointLSN":  17126698000002760800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500255901000)\/",
        "BackupFinishDate":  "\/Date(1500255901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "29b70dc4-d1c3-45e2-b543-7ce2e5f7689f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500255901000)\/",
        "Start":  "\/Date(1500255901000)\/",
        "BackupSetId":  "29b70dc4-d1c3-45e2-b543-7ce2e5f7689f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000003642300001,
        "LastLSN":  17126698000004356200001,
        "CheckpointLSN":  17126698000002760800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500256200000)\/",
        "BackupFinishDate":  "\/Date(1500256200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e8313c1f-f706-40c1-bd28-e813a75bc672",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500256200000)\/",
        "Start":  "\/Date(1500256200000)\/",
        "BackupSetId":  "e8313c1f-f706-40c1-bd28-e813a75bc672",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000004356200001,
        "LastLSN":  17126698000005103600001,
        "CheckpointLSN":  17126698000004827600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500256500000)\/",
        "BackupFinishDate":  "\/Date(1500256500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2e859051-ed18-490f-b05c-de10a0b0708f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500256500000)\/",
        "Start":  "\/Date(1500256500000)\/",
        "BackupSetId":  "2e859051-ed18-490f-b05c-de10a0b0708f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000005103600001,
        "LastLSN":  17126698000005818900001,
        "CheckpointLSN":  17126698000004827600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500256801000)\/",
        "BackupFinishDate":  "\/Date(1500256801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "86b975d0-e902-4789-aed1-9b844caa56b3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500256801000)\/",
        "Start":  "\/Date(1500256801000)\/",
        "BackupSetId":  "86b975d0-e902-4789-aed1-9b844caa56b3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000005818900001,
        "LastLSN":  17126698000006547300001,
        "CheckpointLSN":  17126698000004827600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500257100000)\/",
        "BackupFinishDate":  "\/Date(1500257100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4c79988d-8760-4380-b02d-64819dbaec32",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500257100000)\/",
        "Start":  "\/Date(1500257100000)\/",
        "BackupSetId":  "4c79988d-8760-4380-b02d-64819dbaec32",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000006547300001,
        "LastLSN":  17126698000007280100001,
        "CheckpointLSN":  17126698000006893700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500257400000)\/",
        "BackupFinishDate":  "\/Date(1500257400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "26bd67c9-368b-4725-95f9-313f72c26fe2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500257400000)\/",
        "Start":  "\/Date(1500257400000)\/",
        "BackupSetId":  "26bd67c9-368b-4725-95f9-313f72c26fe2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000007280100001,
        "LastLSN":  17126698000008008200001,
        "CheckpointLSN":  17126698000006893700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500257700000)\/",
        "BackupFinishDate":  "\/Date(1500257700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5859afcb-6871-4f92-9061-b89e47b441c2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500257700000)\/",
        "Start":  "\/Date(1500257700000)\/",
        "BackupSetId":  "5859afcb-6871-4f92-9061-b89e47b441c2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000008008200001,
        "LastLSN":  17126698000008710700001,
        "CheckpointLSN":  17126698000006893700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500258000000)\/",
        "BackupFinishDate":  "\/Date(1500258000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fa663310-8e04-46d1-94da-54c8b5cf22df",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500258000000)\/",
        "Start":  "\/Date(1500258000000)\/",
        "BackupSetId":  "fa663310-8e04-46d1-94da-54c8b5cf22df",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000008710700001,
        "LastLSN":  17126698000009456000001,
        "CheckpointLSN":  17126698000008964900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500258301000)\/",
        "BackupFinishDate":  "\/Date(1500258301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "14b36756-9b23-4c5b-97a9-ee0023266ba5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500258301000)\/",
        "Start":  "\/Date(1500258301000)\/",
        "BackupSetId":  "14b36756-9b23-4c5b-97a9-ee0023266ba5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000009456000001,
        "LastLSN":  17126698000010169000001,
        "CheckpointLSN":  17126698000008964900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500258600000)\/",
        "BackupFinishDate":  "\/Date(1500258600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3e9a99a5-1578-4fe3-9500-cdb90c5f720b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500258600000)\/",
        "Start":  "\/Date(1500258600000)\/",
        "BackupSetId":  "3e9a99a5-1578-4fe3-9500-cdb90c5f720b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000010169000001,
        "LastLSN":  17126698000010893400001,
        "CheckpointLSN":  17126698000008964900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500258900000)\/",
        "BackupFinishDate":  "\/Date(1500258900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a8123b4a-2ab9-4d7d-b663-5d911d3a4fa4",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500258900000)\/",
        "Start":  "\/Date(1500258900000)\/",
        "BackupSetId":  "a8123b4a-2ab9-4d7d-b663-5d911d3a4fa4",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000010893400001,
        "LastLSN":  17126698000011613500001,
        "CheckpointLSN":  17126698000011051600007,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500259200000)\/",
        "BackupFinishDate":  "\/Date(1500259200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "896d800a-6d26-46b2-af7b-9e9972e9e134",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500259200000)\/",
        "Start":  "\/Date(1500259200000)\/",
        "BackupSetId":  "896d800a-6d26-46b2-af7b-9e9972e9e134",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000011613500001,
        "LastLSN":  17126698000012326300001,
        "CheckpointLSN":  17126698000011051600007,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500259500000)\/",
        "BackupFinishDate":  "\/Date(1500259500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "cca604c4-d944-448f-8cce-c0790aea94cf",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500259500000)\/",
        "Start":  "\/Date(1500259500000)\/",
        "BackupSetId":  "cca604c4-d944-448f-8cce-c0790aea94cf",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000012326300001,
        "LastLSN":  17126698000013027500001,
        "CheckpointLSN":  17126698000011051600007,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500259800000)\/",
        "BackupFinishDate":  "\/Date(1500259800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "17b023dc-1690-4bcc-aedf-933d274f93a6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500259800000)\/",
        "Start":  "\/Date(1500259800000)\/",
        "BackupSetId":  "17b023dc-1690-4bcc-aedf-933d274f93a6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126698000013027500001,
        "LastLSN":  17126699000000658200001,
        "CheckpointLSN":  17126699000000023100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500260100000)\/",
        "BackupFinishDate":  "\/Date(1500260100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a7d5d2d8-6e2c-48c2-abd6-469291793c00",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500260100000)\/",
        "Start":  "\/Date(1500260100000)\/",
        "BackupSetId":  "a7d5d2d8-6e2c-48c2-abd6-469291793c00",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000000658200001,
        "LastLSN":  17126699000001384300001,
        "CheckpointLSN":  17126699000000023100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500260400000)\/",
        "BackupFinishDate":  "\/Date(1500260400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "50e3f599-e347-4dfe-bdec-ef8d54788d47",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500260400000)\/",
        "Start":  "\/Date(1500260400000)\/",
        "BackupSetId":  "50e3f599-e347-4dfe-bdec-ef8d54788d47",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000001384300001,
        "LastLSN":  17126699000002085900001,
        "CheckpointLSN":  17126699000000023100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500260700000)\/",
        "BackupFinishDate":  "\/Date(1500260700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0ead9533-e15f-447e-b26b-914c6b8e0101",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500260700000)\/",
        "Start":  "\/Date(1500260700000)\/",
        "BackupSetId":  "0ead9533-e15f-447e-b26b-914c6b8e0101",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000002085900001,
        "LastLSN":  17126699000002819700001,
        "CheckpointLSN":  17126699000002091000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500261001000)\/",
        "BackupFinishDate":  "\/Date(1500261001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f4e6116b-1bf3-491c-acfc-87b72ca312ce",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500261001000)\/",
        "Start":  "\/Date(1500261001000)\/",
        "BackupSetId":  "f4e6116b-1bf3-491c-acfc-87b72ca312ce",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000002819700001,
        "LastLSN":  17126699000003519300001,
        "CheckpointLSN":  17126699000002091000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500261300000)\/",
        "BackupFinishDate":  "\/Date(1500261300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "dc8915ff-1c84-4643-b2c8-56bad47f93a9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500261300000)\/",
        "Start":  "\/Date(1500261300000)\/",
        "BackupSetId":  "dc8915ff-1c84-4643-b2c8-56bad47f93a9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000003519300001,
        "LastLSN":  17126699000004255300001,
        "CheckpointLSN":  17126699000004158600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500261600000)\/",
        "BackupFinishDate":  "\/Date(1500261600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b2013af9-1a0b-4540-bb17-316ebf7b11f8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500261600000)\/",
        "Start":  "\/Date(1500261600000)\/",
        "BackupSetId":  "b2013af9-1a0b-4540-bb17-316ebf7b11f8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000004255300001,
        "LastLSN":  17126699000004956300001,
        "CheckpointLSN":  17126699000004158600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500261900000)\/",
        "BackupFinishDate":  "\/Date(1500261900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4c71862a-f460-4a25-8819-42413b87f977",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500261900000)\/",
        "Start":  "\/Date(1500261900000)\/",
        "BackupSetId":  "4c71862a-f460-4a25-8819-42413b87f977",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000004956300001,
        "LastLSN":  17126699000005657700001,
        "CheckpointLSN":  17126699000004158600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500262201000)\/",
        "BackupFinishDate":  "\/Date(1500262201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1a51d11d-3273-4a1c-ae84-936c6634115e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500262201000)\/",
        "Start":  "\/Date(1500262201000)\/",
        "BackupSetId":  "1a51d11d-3273-4a1c-ae84-936c6634115e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000005657700001,
        "LastLSN":  17126699000006404600001,
        "CheckpointLSN":  17126699000006228500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500262500000)\/",
        "BackupFinishDate":  "\/Date(1500262500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f5da5162-7ba9-4a21-9a9d-154d30cb4fb8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500262500000)\/",
        "Start":  "\/Date(1500262500000)\/",
        "BackupSetId":  "f5da5162-7ba9-4a21-9a9d-154d30cb4fb8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000006404600001,
        "LastLSN":  17126699000007144200001,
        "CheckpointLSN":  17126699000006228500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500262800000)\/",
        "BackupFinishDate":  "\/Date(1500262800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d102c256-87b9-4209-b269-db46e0a740c6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500262800000)\/",
        "Start":  "\/Date(1500262800000)\/",
        "BackupSetId":  "d102c256-87b9-4209-b269-db46e0a740c6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000007144200001,
        "LastLSN":  17126699000007884800001,
        "CheckpointLSN":  17126699000006228500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500263100000)\/",
        "BackupFinishDate":  "\/Date(1500263100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "60b736e2-ce1c-4bb2-a828-680e808a2ffd",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500263100000)\/",
        "Start":  "\/Date(1500263100000)\/",
        "BackupSetId":  "60b736e2-ce1c-4bb2-a828-680e808a2ffd",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000007884800001,
        "LastLSN":  17126699000008619000001,
        "CheckpointLSN":  17126699000008298300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500263400000)\/",
        "BackupFinishDate":  "\/Date(1500263401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a19bfc9a-a3fc-4df0-9458-99ede57dcff0",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500263401000)\/",
        "Start":  "\/Date(1500263400000)\/",
        "BackupSetId":  "a19bfc9a-a3fc-4df0-9458-99ede57dcff0",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000008619000001,
        "LastLSN":  17126699000009350600001,
        "CheckpointLSN":  17126699000008298300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500263701000)\/",
        "BackupFinishDate":  "\/Date(1500263701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "644a2215-9789-4284-be6d-ba7fb90a03f6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500263701000)\/",
        "Start":  "\/Date(1500263701000)\/",
        "BackupSetId":  "644a2215-9789-4284-be6d-ba7fb90a03f6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000009350600001,
        "LastLSN":  17126699000010065600001,
        "CheckpointLSN":  17126699000008298300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500264000000)\/",
        "BackupFinishDate":  "\/Date(1500264000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d4c529b6-48da-40ae-bee0-6cd65d55f048",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500264000000)\/",
        "Start":  "\/Date(1500264000000)\/",
        "BackupSetId":  "d4c529b6-48da-40ae-bee0-6cd65d55f048",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000010065600001,
        "LastLSN":  17126699000010811000001,
        "CheckpointLSN":  17126699000010368800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500264300000)\/",
        "BackupFinishDate":  "\/Date(1500264300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "347ac8b2-809a-41c1-952c-46ee666630da",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500264300000)\/",
        "Start":  "\/Date(1500264300000)\/",
        "BackupSetId":  "347ac8b2-809a-41c1-952c-46ee666630da",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000010811000001,
        "LastLSN":  17126699000011527200001,
        "CheckpointLSN":  17126699000010368800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500264600000)\/",
        "BackupFinishDate":  "\/Date(1500264600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "74d8d621-9588-4fb0-a029-2b520504a1a1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500264600000)\/",
        "Start":  "\/Date(1500264600000)\/",
        "BackupSetId":  "74d8d621-9588-4fb0-a029-2b520504a1a1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000011527200001,
        "LastLSN":  17126699000012256200001,
        "CheckpointLSN":  17126699000010368800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500264900000)\/",
        "BackupFinishDate":  "\/Date(1500264900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f535f808-be3e-4d2e-9cab-7ecfb31d0331",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500264900000)\/",
        "Start":  "\/Date(1500264900000)\/",
        "BackupSetId":  "f535f808-be3e-4d2e-9cab-7ecfb31d0331",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000012256200001,
        "LastLSN":  17126699000012976500001,
        "CheckpointLSN":  17126699000012459400059,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500265200000)\/",
        "BackupFinishDate":  "\/Date(1500265201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "536da3db-8998-488d-a8c6-a2119e95ce6f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500265201000)\/",
        "Start":  "\/Date(1500265200000)\/",
        "BackupSetId":  "536da3db-8998-488d-a8c6-a2119e95ce6f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126699000012976500001,
        "LastLSN":  17126700000000599500001,
        "CheckpointLSN":  17126699000012459400059,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500265500000)\/",
        "BackupFinishDate":  "\/Date(1500265500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "cef03a15-6726-4d42-a857-85f98ecf4780",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500265500000)\/",
        "Start":  "\/Date(1500265500000)\/",
        "BackupSetId":  "cef03a15-6726-4d42-a857-85f98ecf4780",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126700000000599500001,
        "LastLSN":  17126700000001331400001,
        "CheckpointLSN":  17126700000000599600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500265800000)\/",
        "BackupFinishDate":  "\/Date(1500265800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "bf8d020a-ecf6-4b70-a9cc-4deddaf8d664",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500265800000)\/",
        "Start":  "\/Date(1500265800000)\/",
        "BackupSetId":  "bf8d020a-ecf6-4b70-a9cc-4deddaf8d664",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126700000001331400001,
        "LastLSN":  17126700000002058800001,
        "CheckpointLSN":  17126700000000599600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500266100000)\/",
        "BackupFinishDate":  "\/Date(1500266100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ad79dd5c-92ac-46be-8e2a-2988ac71b7c4",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500266100000)\/",
        "Start":  "\/Date(1500266100000)\/",
        "BackupSetId":  "ad79dd5c-92ac-46be-8e2a-2988ac71b7c4",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126700000002058800001,
        "LastLSN":  17126700000002782600001,
        "CheckpointLSN":  17126700000002665300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500266400000)\/",
        "BackupFinishDate":  "\/Date(1500266400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "27449349-1290-4edc-a940-dc5645c5e2b9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500266400000)\/",
        "Start":  "\/Date(1500266400000)\/",
        "BackupSetId":  "27449349-1290-4edc-a940-dc5645c5e2b9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126700000002782600001,
        "LastLSN":  17126700000003495600001,
        "CheckpointLSN":  17126700000002665300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500266701000)\/",
        "BackupFinishDate":  "\/Date(1500266701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a094ad2e-fca0-406a-9709-1d0d35bd9219",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500266701000)\/",
        "Start":  "\/Date(1500266701000)\/",
        "BackupSetId":  "a094ad2e-fca0-406a-9709-1d0d35bd9219",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126700000003495600001,
        "LastLSN":  17126700000004196400001,
        "CheckpointLSN":  17126700000002665300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500267000000)\/",
        "BackupFinishDate":  "\/Date(1500267000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "459bfb0e-61a3-4839-8e0b-eb2ce9611446",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500267000000)\/",
        "Start":  "\/Date(1500267000000)\/",
        "BackupSetId":  "459bfb0e-61a3-4839-8e0b-eb2ce9611446",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126700000004196400001,
        "LastLSN":  17126700000004931800001,
        "CheckpointLSN":  17126700000004737400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500267300000)\/",
        "BackupFinishDate":  "\/Date(1500267300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "034aa99b-fd50-46ed-a691-b681fc1b649a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500267300000)\/",
        "Start":  "\/Date(1500267300000)\/",
        "BackupSetId":  "034aa99b-fd50-46ed-a691-b681fc1b649a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126700000004931800001,
        "LastLSN":  17126700000005664300001,
        "CheckpointLSN":  17126700000004737400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500267601000)\/",
        "BackupFinishDate":  "\/Date(1500267601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "05ac3cfe-9264-4955-928c-97a7a24038f8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500267601000)\/",
        "Start":  "\/Date(1500267601000)\/",
        "BackupSetId":  "05ac3cfe-9264-4955-928c-97a7a24038f8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126700000005664300001,
        "LastLSN":  17126700000009094200001,
        "CheckpointLSN":  17126700000008897500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500267900000)\/",
        "BackupFinishDate":  "\/Date(1500267901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "577ab783-a88a-4e73-a21f-b64d76815d99",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500267901000)\/",
        "Start":  "\/Date(1500267900000)\/",
        "BackupSetId":  "577ab783-a88a-4e73-a21f-b64d76815d99",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126700000009094200001,
        "LastLSN":  17126700000009807000001,
        "CheckpointLSN":  17126700000008897500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500268200000)\/",
        "BackupFinishDate":  "\/Date(1500268200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b963fedc-b8f7-49f6-b7e1-1b8c4ca1a8d6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500268200000)\/",
        "Start":  "\/Date(1500268200000)\/",
        "BackupSetId":  "b963fedc-b8f7-49f6-b7e1-1b8c4ca1a8d6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126700000009807000001,
        "LastLSN":  17126700000010508900001,
        "CheckpointLSN":  17126700000008897500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500268500000)\/",
        "BackupFinishDate":  "\/Date(1500268500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3e812b2c-9a6d-4dfe-8e41-843b32a9ab3b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500268500000)\/",
        "Start":  "\/Date(1500268500000)\/",
        "BackupSetId":  "3e812b2c-9a6d-4dfe-8e41-843b32a9ab3b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126700000010508900001,
        "LastLSN":  17126700000011241400001,
        "CheckpointLSN":  17126700000010971200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500268800000)\/",
        "BackupFinishDate":  "\/Date(1500268800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "14872544-d36d-4209-99e3-f5c4249fe12b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500268800000)\/",
        "Start":  "\/Date(1500268800000)\/",
        "BackupSetId":  "14872544-d36d-4209-99e3-f5c4249fe12b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126700000011241400001,
        "LastLSN":  17126700000011949300001,
        "CheckpointLSN":  17126700000010971200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500269100000)\/",
        "BackupFinishDate":  "\/Date(1500269100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c5cab591-278b-48c8-9a1c-c6b1d579a5dc",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500269100000)\/",
        "Start":  "\/Date(1500269100000)\/",
        "BackupSetId":  "c5cab591-278b-48c8-9a1c-c6b1d579a5dc",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126700000011949300001,
        "LastLSN":  17126701000000226500001,
        "CheckpointLSN":  17126700000013038300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500269401000)\/",
        "BackupFinishDate":  "\/Date(1500269401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "563e8f15-3feb-42a2-871b-7aa0545d8959",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500269401000)\/",
        "Start":  "\/Date(1500269401000)\/",
        "BackupSetId":  "563e8f15-3feb-42a2-871b-7aa0545d8959",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126701000000226500001,
        "LastLSN":  17126701000010334700001,
        "CheckpointLSN":  17126701000008264300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500269700000)\/",
        "BackupFinishDate":  "\/Date(1500269700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8923699b-2451-4c87-a9ac-ab2a53bbb41d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500269700000)\/",
        "Start":  "\/Date(1500269700000)\/",
        "BackupSetId":  "8923699b-2451-4c87-a9ac-ab2a53bbb41d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126701000010334700001,
        "LastLSN":  17126702000006262100001,
        "CheckpointLSN":  17126702000005302000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500270000000)\/",
        "BackupFinishDate":  "\/Date(1500270000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2de72270-b7b5-4a01-95fb-139dc3e88856",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500270000000)\/",
        "Start":  "\/Date(1500270000000)\/",
        "BackupSetId":  "2de72270-b7b5-4a01-95fb-139dc3e88856",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126702000006262100001,
        "LastLSN":  17126702000010625200001,
        "CheckpointLSN":  17126702000009475700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500270300000)\/",
        "BackupFinishDate":  "\/Date(1500270300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "425191a0-8a43-429d-a31a-e9f64a1a6413",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500270300000)\/",
        "Start":  "\/Date(1500270300000)\/",
        "BackupSetId":  "425191a0-8a43-429d-a31a-e9f64a1a6413",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126702000010625200001,
        "LastLSN":  17126702000011592600001,
        "CheckpointLSN":  17126702000011542200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500270600000)\/",
        "BackupFinishDate":  "\/Date(1500270600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "469d59fb-41cb-4d59-9af4-dac146f9ab01",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500270600000)\/",
        "Start":  "\/Date(1500270600000)\/",
        "BackupSetId":  "469d59fb-41cb-4d59-9af4-dac146f9ab01",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126702000011592600001,
        "LastLSN":  17126702000012321300001,
        "CheckpointLSN":  17126702000011542200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500270900000)\/",
        "BackupFinishDate":  "\/Date(1500270900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "84d0791d-8988-4f3f-941a-47b6edfb33d0",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500270900000)\/",
        "Start":  "\/Date(1500270900000)\/",
        "BackupSetId":  "84d0791d-8988-4f3f-941a-47b6edfb33d0",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126702000012321300001,
        "LastLSN":  17126705000000438400001,
        "CheckpointLSN":  17126705000000398200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500271200000)\/",
        "BackupFinishDate":  "\/Date(1500271200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6bb8dd74-63aa-4be9-b404-5a635e13f6da",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500271200000)\/",
        "Start":  "\/Date(1500271200000)\/",
        "BackupSetId":  "6bb8dd74-63aa-4be9-b404-5a635e13f6da",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126705000000438400001,
        "LastLSN":  17126705000010433900001,
        "CheckpointLSN":  17126705000008624400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500271500000)\/",
        "BackupFinishDate":  "\/Date(1500271501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ee457ba4-a49e-4ea2-b4b3-accff7ee144e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500271501000)\/",
        "Start":  "\/Date(1500271500000)\/",
        "BackupSetId":  "ee457ba4-a49e-4ea2-b4b3-accff7ee144e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126705000010433900001,
        "LastLSN":  17126706000004172600001,
        "CheckpointLSN":  17126706000001755500033,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500271800000)\/",
        "BackupFinishDate":  "\/Date(1500271800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "147919d1-01fb-49a1-af04-0b64951f1360",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500271800000)\/",
        "Start":  "\/Date(1500271800000)\/",
        "BackupSetId":  "147919d1-01fb-49a1-af04-0b64951f1360",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126706000004172600001,
        "LastLSN":  17126706000005136700001,
        "CheckpointLSN":  17126706000001755500033,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500272100000)\/",
        "BackupFinishDate":  "\/Date(1500272100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ba30775e-50fc-408b-a57d-cafe734becb7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500272100000)\/",
        "Start":  "\/Date(1500272100000)\/",
        "BackupSetId":  "ba30775e-50fc-408b-a57d-cafe734becb7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126706000005136700001,
        "LastLSN":  17126706000006063400001,
        "CheckpointLSN":  17126706000005413900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500272400000)\/",
        "BackupFinishDate":  "\/Date(1500272400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "76afd155-de94-4a2c-8a4a-9654682cd70e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500272400000)\/",
        "Start":  "\/Date(1500272400000)\/",
        "BackupSetId":  "76afd155-de94-4a2c-8a4a-9654682cd70e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126706000006063400001,
        "LastLSN":  17126706000006866800001,
        "CheckpointLSN":  17126706000005413900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500272700000)\/",
        "BackupFinishDate":  "\/Date(1500272700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8ed83789-d65c-408f-a17f-ef2d09732570",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500272700000)\/",
        "Start":  "\/Date(1500272700000)\/",
        "BackupSetId":  "8ed83789-d65c-408f-a17f-ef2d09732570",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126706000006866800001,
        "LastLSN":  17126706000007651500001,
        "CheckpointLSN":  17126706000007485400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500273001000)\/",
        "BackupFinishDate":  "\/Date(1500273001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "25ac1c84-7c3d-4ef6-ab63-53025fc35435",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500273001000)\/",
        "Start":  "\/Date(1500273001000)\/",
        "BackupSetId":  "25ac1c84-7c3d-4ef6-ab63-53025fc35435",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126706000007651500001,
        "LastLSN":  17126706000008425100001,
        "CheckpointLSN":  17126706000007485400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500273300000)\/",
        "BackupFinishDate":  "\/Date(1500273300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "bacf6c8e-68d0-4224-95b7-6b1bc2156349",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500273300000)\/",
        "Start":  "\/Date(1500273300000)\/",
        "BackupSetId":  "bacf6c8e-68d0-4224-95b7-6b1bc2156349",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126706000008425100001,
        "LastLSN":  17126706000009762500001,
        "CheckpointLSN":  17126706000009568900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500273600000)\/",
        "BackupFinishDate":  "\/Date(1500273600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4ba1caa7-0a76-41fd-94b9-d31638ffa130",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500273600000)\/",
        "Start":  "\/Date(1500273600000)\/",
        "BackupSetId":  "4ba1caa7-0a76-41fd-94b9-d31638ffa130",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126706000009762500001,
        "LastLSN":  17126706000010929100001,
        "CheckpointLSN":  17126706000009568900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500273900000)\/",
        "BackupFinishDate":  "\/Date(1500273900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1d95aae5-cbf5-4146-8239-e17061ea04f3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500273900000)\/",
        "Start":  "\/Date(1500273900000)\/",
        "BackupSetId":  "1d95aae5-cbf5-4146-8239-e17061ea04f3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126706000010929100001,
        "LastLSN":  17126707000003905600001,
        "CheckpointLSN":  17126707000000637600008,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500274200000)\/",
        "BackupFinishDate":  "\/Date(1500274201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "90620cc6-76d1-4795-a0f3-788157b3054c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500274201000)\/",
        "Start":  "\/Date(1500274200000)\/",
        "BackupSetId":  "90620cc6-76d1-4795-a0f3-788157b3054c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126707000003905600001,
        "LastLSN":  17126707000012950800001,
        "CheckpointLSN":  17126707000012508600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500274501000)\/",
        "BackupFinishDate":  "\/Date(1500274501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "cda21b51-5aca-46bf-a1d3-c567e06612d1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500274501000)\/",
        "Start":  "\/Date(1500274501000)\/",
        "BackupSetId":  "cda21b51-5aca-46bf-a1d3-c567e06612d1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126707000012950800001,
        "LastLSN":  17126708000004222700001,
        "CheckpointLSN":  17126708000003616100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500274800000)\/",
        "BackupFinishDate":  "\/Date(1500274800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "eb550d9c-d132-4c61-944e-2ce6db5de8da",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500274800000)\/",
        "Start":  "\/Date(1500274800000)\/",
        "BackupSetId":  "eb550d9c-d132-4c61-944e-2ce6db5de8da",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126708000004222700001,
        "LastLSN":  17126708000005608800001,
        "CheckpointLSN":  17126708000003616100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500275100000)\/",
        "BackupFinishDate":  "\/Date(1500275100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "206c6805-0847-4c13-bdd6-6cd0f190dc55",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500275100000)\/",
        "Start":  "\/Date(1500275100000)\/",
        "BackupSetId":  "206c6805-0847-4c13-bdd6-6cd0f190dc55",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126708000005608800001,
        "LastLSN":  17126708000008208300001,
        "CheckpointLSN":  17126708000007799700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500275400000)\/",
        "BackupFinishDate":  "\/Date(1500275401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ab2be580-42c9-4113-98e6-ca02ff0b1a7b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500275401000)\/",
        "Start":  "\/Date(1500275400000)\/",
        "BackupSetId":  "ab2be580-42c9-4113-98e6-ca02ff0b1a7b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126708000008208300001,
        "LastLSN":  17126708000011340700001,
        "CheckpointLSN":  17126708000009892100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500275701000)\/",
        "BackupFinishDate":  "\/Date(1500275701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "da15e5d1-7bf1-48d8-a1f5-8d1cb3cb1557",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500275701000)\/",
        "Start":  "\/Date(1500275701000)\/",
        "BackupSetId":  "da15e5d1-7bf1-48d8-a1f5-8d1cb3cb1557",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126708000011340700001,
        "LastLSN":  17126709000001895500001,
        "CheckpointLSN":  17126709000000941300014,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500276000000)\/",
        "BackupFinishDate":  "\/Date(1500276000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c8d69fd4-c913-4e6b-ac80-e9a0073bc07d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500276000000)\/",
        "Start":  "\/Date(1500276000000)\/",
        "BackupSetId":  "c8d69fd4-c913-4e6b-ac80-e9a0073bc07d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126709000001895500001,
        "LastLSN":  17126710000002368400001,
        "CheckpointLSN":  17126710000002262900009,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500276300000)\/",
        "BackupFinishDate":  "\/Date(1500276300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4e23ac24-fada-4e59-b423-0c53ddad9520",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500276300000)\/",
        "Start":  "\/Date(1500276300000)\/",
        "BackupSetId":  "4e23ac24-fada-4e59-b423-0c53ddad9520",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126710000002368400001,
        "LastLSN":  17126712000008541300001,
        "CheckpointLSN":  17126712000004575500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500276600000)\/",
        "BackupFinishDate":  "\/Date(1500276602000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6badf75e-ef48-4da6-a1eb-5fd9a80de48b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500276602000)\/",
        "Start":  "\/Date(1500276600000)\/",
        "BackupSetId":  "6badf75e-ef48-4da6-a1eb-5fd9a80de48b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126712000008541300001,
        "LastLSN":  17126713000008587000001,
        "CheckpointLSN":  17126713000008019700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500276900000)\/",
        "BackupFinishDate":  "\/Date(1500276901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "83c197ad-04a8-4152-a510-5dea58bc2a88",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500276901000)\/",
        "Start":  "\/Date(1500276900000)\/",
        "BackupSetId":  "83c197ad-04a8-4152-a510-5dea58bc2a88",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126713000008587000001,
        "LastLSN":  17126713000009533600001,
        "CheckpointLSN":  17126713000008019700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500277200000)\/",
        "BackupFinishDate":  "\/Date(1500277200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ba7e9011-1ad4-43b6-9a3a-05a873df0c42",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500277200000)\/",
        "Start":  "\/Date(1500277200000)\/",
        "BackupSetId":  "ba7e9011-1ad4-43b6-9a3a-05a873df0c42",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126713000009533600001,
        "LastLSN":  17126716000002041800001,
        "CheckpointLSN":  17126716000001614700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500277500000)\/",
        "BackupFinishDate":  "\/Date(1500277501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d00d0ed4-2f25-40b0-b054-1d7ab821215a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500277501000)\/",
        "Start":  "\/Date(1500277500000)\/",
        "BackupSetId":  "d00d0ed4-2f25-40b0-b054-1d7ab821215a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126716000002041800001,
        "LastLSN":  17126718000002541400001,
        "CheckpointLSN":  17126718000002509700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500277800000)\/",
        "BackupFinishDate":  "\/Date(1500277801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3ef0378d-26ce-4adc-9da5-ab9a341baaf8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500277801000)\/",
        "Start":  "\/Date(1500277800000)\/",
        "BackupSetId":  "3ef0378d-26ce-4adc-9da5-ab9a341baaf8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126718000002541400001,
        "LastLSN":  17126718000009557300001,
        "CheckpointLSN":  17126718000007631900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500278100000)\/",
        "BackupFinishDate":  "\/Date(1500278101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "cb262a50-b3c5-404e-97c3-a3497cc1ff12",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500278101000)\/",
        "Start":  "\/Date(1500278100000)\/",
        "BackupSetId":  "cb262a50-b3c5-404e-97c3-a3497cc1ff12",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126718000009557300001,
        "LastLSN":  17126719000007606800001,
        "CheckpointLSN":  17126719000007379900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500278401000)\/",
        "BackupFinishDate":  "\/Date(1500278401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "40035edd-7f75-44d0-a1f7-e2a76476029e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500278401000)\/",
        "Start":  "\/Date(1500278401000)\/",
        "BackupSetId":  "40035edd-7f75-44d0-a1f7-e2a76476029e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126719000007606800001,
        "LastLSN":  17126720000000489600001,
        "CheckpointLSN":  17126719000011629900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500278700000)\/",
        "BackupFinishDate":  "\/Date(1500278700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3bc8f9d5-9574-4cc3-851b-623cc3b60f8a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500278700000)\/",
        "Start":  "\/Date(1500278700000)\/",
        "BackupSetId":  "3bc8f9d5-9574-4cc3-851b-623cc3b60f8a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126720000000489600001,
        "LastLSN":  17126723000005599500001,
        "CheckpointLSN":  17126723000005521400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500279000000)\/",
        "BackupFinishDate":  "\/Date(1500279001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "01b0bfa4-6f2f-43b7-9eeb-65f346b02dc8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500279001000)\/",
        "Start":  "\/Date(1500279000000)\/",
        "BackupSetId":  "01b0bfa4-6f2f-43b7-9eeb-65f346b02dc8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126723000005599500001,
        "LastLSN":  17126725000007625400001,
        "CheckpointLSN":  17126725000007320300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500279300000)\/",
        "BackupFinishDate":  "\/Date(1500279301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1e4249c0-a609-42df-bac1-26f83b85b92e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500279301000)\/",
        "Start":  "\/Date(1500279300000)\/",
        "BackupSetId":  "1e4249c0-a609-42df-bac1-26f83b85b92e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126725000007625400001,
        "LastLSN":  17126726000000470700001,
        "CheckpointLSN":  17126726000000274200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500279600000)\/",
        "BackupFinishDate":  "\/Date(1500279601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "24e79a8f-64e8-4093-8d59-63631a853176",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500279601000)\/",
        "Start":  "\/Date(1500279600000)\/",
        "BackupSetId":  "24e79a8f-64e8-4093-8d59-63631a853176",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126726000000470700001,
        "LastLSN":  17126727000007729200001,
        "CheckpointLSN":  17126727000004068000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500279901000)\/",
        "BackupFinishDate":  "\/Date(1500279902000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "419ec4fc-becf-416d-96ad-b1d3647b7e69",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500279902000)\/",
        "Start":  "\/Date(1500279901000)\/",
        "BackupSetId":  "419ec4fc-becf-416d-96ad-b1d3647b7e69",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126727000007729200001,
        "LastLSN":  17126730000002553300001,
        "CheckpointLSN":  17126730000002380700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500280200000)\/",
        "BackupFinishDate":  "\/Date(1500280202000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "66f73880-849a-4ed0-b48a-ef6d7b9ae5f1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500280202000)\/",
        "Start":  "\/Date(1500280200000)\/",
        "BackupSetId":  "66f73880-849a-4ed0-b48a-ef6d7b9ae5f1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126730000002553300001,
        "LastLSN":  17126733000011493000001,
        "CheckpointLSN":  17126733000011432400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500280500000)\/",
        "BackupFinishDate":  "\/Date(1500280502000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4f3c893f-7b13-4973-abbd-8cf6a68f02bc",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500280502000)\/",
        "Start":  "\/Date(1500280500000)\/",
        "BackupSetId":  "4f3c893f-7b13-4973-abbd-8cf6a68f02bc",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126733000011493000001,
        "LastLSN":  17126735000001923200001,
        "CheckpointLSN":  17126734000005021300030,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500280800000)\/",
        "BackupFinishDate":  "\/Date(1500280801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4562d610-ac10-4853-b49d-92b63f4128c9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500280801000)\/",
        "Start":  "\/Date(1500280800000)\/",
        "BackupSetId":  "4562d610-ac10-4853-b49d-92b63f4128c9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126735000001923200001,
        "LastLSN":  17126736000012961600001,
        "CheckpointLSN":  17126736000012791100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500281100000)\/",
        "BackupFinishDate":  "\/Date(1500281102000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a8190ac3-86b7-45e2-8764-8d6f7d494568",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500281102000)\/",
        "Start":  "\/Date(1500281100000)\/",
        "BackupSetId":  "a8190ac3-86b7-45e2-8764-8d6f7d494568",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126736000012961600001,
        "LastLSN":  17126741000003983800001,
        "CheckpointLSN":  17126741000002378000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500281400000)\/",
        "BackupFinishDate":  "\/Date(1500281401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "50d07a3b-32a1-4c89-bf51-8b9f33ba0039",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500281401000)\/",
        "Start":  "\/Date(1500281400000)\/",
        "BackupSetId":  "50d07a3b-32a1-4c89-bf51-8b9f33ba0039",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126741000003983800001,
        "LastLSN":  17126741000012053600001,
        "CheckpointLSN":  17126741000010858400010,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500281700000)\/",
        "BackupFinishDate":  "\/Date(1500281700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7232f045-b04d-4f3c-ac59-afdace0c50c5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500281700000)\/",
        "Start":  "\/Date(1500281700000)\/",
        "BackupSetId":  "7232f045-b04d-4f3c-ac59-afdace0c50c5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126741000012053600001,
        "LastLSN":  17126743000003341600001,
        "CheckpointLSN":  17126743000001957500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500282000000)\/",
        "BackupFinishDate":  "\/Date(1500282001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "22c1c5b4-6791-4e88-99f1-5b74a4e5a23e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500282001000)\/",
        "Start":  "\/Date(1500282000000)\/",
        "BackupSetId":  "22c1c5b4-6791-4e88-99f1-5b74a4e5a23e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126743000003341600001,
        "LastLSN":  17126743000010911300001,
        "CheckpointLSN":  17126743000010805400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500282300000)\/",
        "BackupFinishDate":  "\/Date(1500282301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f56fbee4-0670-4ce1-98e8-bdfd079ada72",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500282301000)\/",
        "Start":  "\/Date(1500282300000)\/",
        "BackupSetId":  "f56fbee4-0670-4ce1-98e8-bdfd079ada72",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126743000010911300001,
        "LastLSN":  17126744000003412000001,
        "CheckpointLSN":  17126744000002218700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500282600000)\/",
        "BackupFinishDate":  "\/Date(1500282601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c6d617df-7d3f-4f6a-abdc-ca31ae579004",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500282601000)\/",
        "Start":  "\/Date(1500282600000)\/",
        "BackupSetId":  "c6d617df-7d3f-4f6a-abdc-ca31ae579004",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126744000003412000001,
        "LastLSN":  17126744000007380700001,
        "CheckpointLSN":  17126744000006421800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500282901000)\/",
        "BackupFinishDate":  "\/Date(1500282901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6b9e3a06-edb6-4242-a2cb-3b3eef90c064",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500282901000)\/",
        "Start":  "\/Date(1500282901000)\/",
        "BackupSetId":  "6b9e3a06-edb6-4242-a2cb-3b3eef90c064",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126744000007380700001,
        "LastLSN":  17126745000003806200001,
        "CheckpointLSN":  17126745000002160200017,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500283200000)\/",
        "BackupFinishDate":  "\/Date(1500283201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "dade97f4-5291-452d-bf4a-20b1645e1706",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500283201000)\/",
        "Start":  "\/Date(1500283200000)\/",
        "BackupSetId":  "dade97f4-5291-452d-bf4a-20b1645e1706",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126745000003806200001,
        "LastLSN":  17126745000010781900001,
        "CheckpointLSN":  17126745000009743200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500283500000)\/",
        "BackupFinishDate":  "\/Date(1500283500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d22a4b51-21b5-4171-b8bf-2b1ea837abd5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500283500000)\/",
        "Start":  "\/Date(1500283500000)\/",
        "BackupSetId":  "d22a4b51-21b5-4171-b8bf-2b1ea837abd5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126745000010781900001,
        "LastLSN":  17126746000007456300001,
        "CheckpointLSN":  17126746000006772000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500283800000)\/",
        "BackupFinishDate":  "\/Date(1500283801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2250a249-3c70-46fc-9389-188df8c15e36",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500283801000)\/",
        "Start":  "\/Date(1500283800000)\/",
        "BackupSetId":  "2250a249-3c70-46fc-9389-188df8c15e36",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126746000007456300001,
        "LastLSN":  17126747000004278900001,
        "CheckpointLSN":  17126747000003860800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500284100000)\/",
        "BackupFinishDate":  "\/Date(1500284101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ac0cd0c9-e896-4502-b0e8-d1b7a2dfe2ec",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500284101000)\/",
        "Start":  "\/Date(1500284100000)\/",
        "BackupSetId":  "ac0cd0c9-e896-4502-b0e8-d1b7a2dfe2ec",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126747000004278900001,
        "LastLSN":  17126748000001476800001,
        "CheckpointLSN":  17126748000000828300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500284401000)\/",
        "BackupFinishDate":  "\/Date(1500284401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4019d957-4390-42c7-812a-ecd3bc4b0355",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500284401000)\/",
        "Start":  "\/Date(1500284401000)\/",
        "BackupSetId":  "4019d957-4390-42c7-812a-ecd3bc4b0355",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126748000001476800001,
        "LastLSN":  17126749000002053400001,
        "CheckpointLSN":  17126749000000739500014,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500284700000)\/",
        "BackupFinishDate":  "\/Date(1500284700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "95de62c3-0434-4021-8ae5-7825ae30567c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500284700000)\/",
        "Start":  "\/Date(1500284700000)\/",
        "BackupSetId":  "95de62c3-0434-4021-8ae5-7825ae30567c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126749000002053400001,
        "LastLSN":  17126749000013025600001,
        "CheckpointLSN":  17126749000012397400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500285000000)\/",
        "BackupFinishDate":  "\/Date(1500285000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fc9c0f0a-46fc-4344-a22b-f7d513350d48",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500285000000)\/",
        "Start":  "\/Date(1500285000000)\/",
        "BackupSetId":  "fc9c0f0a-46fc-4344-a22b-f7d513350d48",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126749000013025600001,
        "LastLSN":  17126750000010362200001,
        "CheckpointLSN":  17126750000009396000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500285300000)\/",
        "BackupFinishDate":  "\/Date(1500285300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6a62c97a-88f8-4bde-8824-fe8e1f4ac87b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500285300000)\/",
        "Start":  "\/Date(1500285300000)\/",
        "BackupSetId":  "6a62c97a-88f8-4bde-8824-fe8e1f4ac87b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126750000010362200001,
        "LastLSN":  17126751000011703800001,
        "CheckpointLSN":  17126751000008199400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500285600000)\/",
        "BackupFinishDate":  "\/Date(1500285601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b909e8c6-8fc9-428f-80eb-8aa30e41629f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500285601000)\/",
        "Start":  "\/Date(1500285600000)\/",
        "BackupSetId":  "b909e8c6-8fc9-428f-80eb-8aa30e41629f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126751000011703800001,
        "LastLSN":  17126753000003318200001,
        "CheckpointLSN":  17126753000002501500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500285900000)\/",
        "BackupFinishDate":  "\/Date(1500285901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b6493786-baf5-4f93-bf34-658a5f67490f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500285901000)\/",
        "Start":  "\/Date(1500285900000)\/",
        "BackupSetId":  "b6493786-baf5-4f93-bf34-658a5f67490f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126753000003318200001,
        "LastLSN":  17126754000008863900001,
        "CheckpointLSN":  17126754000006844400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500286201000)\/",
        "BackupFinishDate":  "\/Date(1500286202000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9d0fcc82-7ce6-40b4-b3e4-0eb1ca6ee911",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500286202000)\/",
        "Start":  "\/Date(1500286201000)\/",
        "BackupSetId":  "9d0fcc82-7ce6-40b4-b3e4-0eb1ca6ee911",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126754000008863900001,
        "LastLSN":  17126759000003670200001,
        "CheckpointLSN":  17126759000003375900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500286501000)\/",
        "BackupFinishDate":  "\/Date(1500286502000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4cfc47c0-2a7c-47e0-8806-2bdb9195e72f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500286502000)\/",
        "Start":  "\/Date(1500286501000)\/",
        "BackupSetId":  "4cfc47c0-2a7c-47e0-8806-2bdb9195e72f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126759000003670200001,
        "LastLSN":  17126761000008817200001,
        "CheckpointLSN":  17126761000007590400023,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500286800000)\/",
        "BackupFinishDate":  "\/Date(1500286801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5d5854ff-c79b-4393-b870-9fdc3068ac93",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500286801000)\/",
        "Start":  "\/Date(1500286800000)\/",
        "BackupSetId":  "5d5854ff-c79b-4393-b870-9fdc3068ac93",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126761000008817200001,
        "LastLSN":  17126762000011823300001,
        "CheckpointLSN":  17126762000011774500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500287100000)\/",
        "BackupFinishDate":  "\/Date(1500287101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "cb0f6323-f091-4d6c-ba84-02f8fafcac93",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500287101000)\/",
        "Start":  "\/Date(1500287100000)\/",
        "BackupSetId":  "cb0f6323-f091-4d6c-ba84-02f8fafcac93",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126762000011823300001,
        "LastLSN":  17126763000005679700001,
        "CheckpointLSN":  17126763000005234400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500287401000)\/",
        "BackupFinishDate":  "\/Date(1500287401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "efbe13f0-05e0-4029-bc98-d9bf904ea2c1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500287401000)\/",
        "Start":  "\/Date(1500287401000)\/",
        "BackupSetId":  "efbe13f0-05e0-4029-bc98-d9bf904ea2c1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126763000005679700001,
        "LastLSN":  17126763000012080600001,
        "CheckpointLSN":  17126763000011641100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500287701000)\/",
        "BackupFinishDate":  "\/Date(1500287701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c39c7126-a90d-4527-8f40-5c644f7e4afa",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500287701000)\/",
        "Start":  "\/Date(1500287701000)\/",
        "BackupSetId":  "c39c7126-a90d-4527-8f40-5c644f7e4afa",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126763000012080600001,
        "LastLSN":  17126764000005193900001,
        "CheckpointLSN":  17126764000004786900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500288000000)\/",
        "BackupFinishDate":  "\/Date(1500288000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "33bdaebd-823e-48a6-ad3f-9b9fcdc53d31",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500288000000)\/",
        "Start":  "\/Date(1500288000000)\/",
        "BackupSetId":  "33bdaebd-823e-48a6-ad3f-9b9fcdc53d31",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126764000005193900001,
        "LastLSN":  17126764000011387100001,
        "CheckpointLSN":  17126764000010991400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500288300000)\/",
        "BackupFinishDate":  "\/Date(1500288300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8a7538f1-9906-48aa-8f72-53fe24fdcb3c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500288300000)\/",
        "Start":  "\/Date(1500288300000)\/",
        "BackupSetId":  "8a7538f1-9906-48aa-8f72-53fe24fdcb3c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126764000011387100001,
        "LastLSN":  17126765000010602100001,
        "CheckpointLSN":  17126765000010029500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500288600000)\/",
        "BackupFinishDate":  "\/Date(1500288601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "229332d4-44d3-40c8-a7bd-b1b1bf88c22c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500288601000)\/",
        "Start":  "\/Date(1500288600000)\/",
        "BackupSetId":  "229332d4-44d3-40c8-a7bd-b1b1bf88c22c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126765000010602100001,
        "LastLSN":  17126766000007348100001,
        "CheckpointLSN":  17126766000006462500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500288901000)\/",
        "BackupFinishDate":  "\/Date(1500288901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b1ffda08-4d53-492f-876c-1c3793dbf41b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500288901000)\/",
        "Start":  "\/Date(1500288901000)\/",
        "BackupSetId":  "b1ffda08-4d53-492f-876c-1c3793dbf41b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126766000007348100001,
        "LastLSN":  17126767000001039900001,
        "CheckpointLSN":  17126767000000580000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500289200000)\/",
        "BackupFinishDate":  "\/Date(1500289200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b34fa9a7-a565-4365-b125-7fe845a773f5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500289200000)\/",
        "Start":  "\/Date(1500289200000)\/",
        "BackupSetId":  "b34fa9a7-a565-4365-b125-7fe845a773f5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126767000001039900001,
        "LastLSN":  17126767000007460900001,
        "CheckpointLSN":  17126767000007035800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500289500000)\/",
        "BackupFinishDate":  "\/Date(1500289500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a9b51572-f246-4c95-9da0-685ef39b5b92",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500289500000)\/",
        "Start":  "\/Date(1500289500000)\/",
        "BackupSetId":  "a9b51572-f246-4c95-9da0-685ef39b5b92",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126767000007460900001,
        "LastLSN":  17126768000000779800001,
        "CheckpointLSN":  17126768000000349200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500289800000)\/",
        "BackupFinishDate":  "\/Date(1500289800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d3c3ec44-9bd5-4860-893a-6a888845e1da",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500289800000)\/",
        "Start":  "\/Date(1500289800000)\/",
        "BackupSetId":  "d3c3ec44-9bd5-4860-893a-6a888845e1da",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126768000000779800001,
        "LastLSN":  17126768000007256800001,
        "CheckpointLSN":  17126768000006834400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500290100000)\/",
        "BackupFinishDate":  "\/Date(1500290101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "810fea30-a598-4888-99a0-9705d22a658e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500290101000)\/",
        "Start":  "\/Date(1500290100000)\/",
        "BackupSetId":  "810fea30-a598-4888-99a0-9705d22a658e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126768000007256800001,
        "LastLSN":  17126769000005497500001,
        "CheckpointLSN":  17126769000005149200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500290400000)\/",
        "BackupFinishDate":  "\/Date(1500290400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c1ce98b7-f3e1-4e12-9baf-9e838b08613c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500290400000)\/",
        "Start":  "\/Date(1500290400000)\/",
        "BackupSetId":  "c1ce98b7-f3e1-4e12-9baf-9e838b08613c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126769000005497500001,
        "LastLSN":  17126769000010193000001,
        "CheckpointLSN":  17126769000009331900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500290700000)\/",
        "BackupFinishDate":  "\/Date(1500290700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b69144c9-6ad9-4ebd-a0b3-f18454230105",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500290700000)\/",
        "Start":  "\/Date(1500290700000)\/",
        "BackupSetId":  "b69144c9-6ad9-4ebd-a0b3-f18454230105",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126769000010193000001,
        "LastLSN":  17126769000011671200001,
        "CheckpointLSN":  17126769000011418800011,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500291000000)\/",
        "BackupFinishDate":  "\/Date(1500291000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7626874d-be1c-4206-b69f-200264192cd4",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500291000000)\/",
        "Start":  "\/Date(1500291000000)\/",
        "BackupSetId":  "7626874d-be1c-4206-b69f-200264192cd4",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126769000011671200001,
        "LastLSN":  17126769000012913600001,
        "CheckpointLSN":  17126769000011418800011,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500291300000)\/",
        "BackupFinishDate":  "\/Date(1500291300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c9f7af16-d57b-418a-a18f-c2d4f525e224",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500291300000)\/",
        "Start":  "\/Date(1500291300000)\/",
        "BackupSetId":  "c9f7af16-d57b-418a-a18f-c2d4f525e224",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126769000012913600001,
        "LastLSN":  17126770000001083100001,
        "CheckpointLSN":  17126770000000413000011,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500291600000)\/",
        "BackupFinishDate":  "\/Date(1500291601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d75ae974-2efa-493a-b28a-ff7f250ce1f6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500291601000)\/",
        "Start":  "\/Date(1500291600000)\/",
        "BackupSetId":  "d75ae974-2efa-493a-b28a-ff7f250ce1f6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126770000001083100001,
        "LastLSN":  17126770000002357900001,
        "CheckpointLSN":  17126770000000413000011,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500291900000)\/",
        "BackupFinishDate":  "\/Date(1500291900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "07f3868d-2928-414a-955f-b62ae1e00aff",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500291900000)\/",
        "Start":  "\/Date(1500291900000)\/",
        "BackupSetId":  "07f3868d-2928-414a-955f-b62ae1e00aff",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126770000002357900001,
        "LastLSN":  17126770000007457400001,
        "CheckpointLSN":  17126770000006959000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500292200000)\/",
        "BackupFinishDate":  "\/Date(1500292200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4dfed5ef-7809-4e69-bdaf-40b5fe64096d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500292200000)\/",
        "Start":  "\/Date(1500292200000)\/",
        "BackupSetId":  "4dfed5ef-7809-4e69-bdaf-40b5fe64096d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126770000007457400001,
        "LastLSN":  17126770000010477000001,
        "CheckpointLSN":  17126770000009061600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500292500000)\/",
        "BackupFinishDate":  "\/Date(1500292500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "341898cd-8ec5-4ac1-a4cc-02d967f96284",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500292500000)\/",
        "Start":  "\/Date(1500292500000)\/",
        "BackupSetId":  "341898cd-8ec5-4ac1-a4cc-02d967f96284",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126770000010477000001,
        "LastLSN":  17126770000012142600001,
        "CheckpointLSN":  17126770000011146000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500292801000)\/",
        "BackupFinishDate":  "\/Date(1500292801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b84e61ca-3000-4556-b2f0-e377d62c4c43",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500292801000)\/",
        "Start":  "\/Date(1500292801000)\/",
        "BackupSetId":  "b84e61ca-3000-4556-b2f0-e377d62c4c43",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126770000012142600001,
        "LastLSN":  17126771000001282200001,
        "CheckpointLSN":  17126771000000141500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500293100000)\/",
        "BackupFinishDate":  "\/Date(1500293100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6e6aeb6b-c296-4fd0-998f-10f4723a2820",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500293100000)\/",
        "Start":  "\/Date(1500293100000)\/",
        "BackupSetId":  "6e6aeb6b-c296-4fd0-998f-10f4723a2820",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126771000001282200001,
        "LastLSN":  17126771000002618700001,
        "CheckpointLSN":  17126771000002233100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500293400000)\/",
        "BackupFinishDate":  "\/Date(1500293400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2a6a24cb-8327-4fec-9ee6-0ff9f303b58d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500293400000)\/",
        "Start":  "\/Date(1500293400000)\/",
        "BackupSetId":  "2a6a24cb-8327-4fec-9ee6-0ff9f303b58d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126771000002618700001,
        "LastLSN":  17126771000005599100001,
        "CheckpointLSN":  17126771000004348400016,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500293700000)\/",
        "BackupFinishDate":  "\/Date(1500293700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "94af23e3-d660-4e93-a158-da35872c8f5f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500293700000)\/",
        "Start":  "\/Date(1500293700000)\/",
        "BackupSetId":  "94af23e3-d660-4e93-a158-da35872c8f5f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126771000005599100001,
        "LastLSN":  17126771000007157100001,
        "CheckpointLSN":  17126771000006809600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500294000000)\/",
        "BackupFinishDate":  "\/Date(1500294001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b512ed21-e9fe-47fc-b558-a9c37ed69cec",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500294001000)\/",
        "Start":  "\/Date(1500294000000)\/",
        "BackupSetId":  "b512ed21-e9fe-47fc-b558-a9c37ed69cec",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126771000007157100001,
        "LastLSN":  17126771000008810100001,
        "CheckpointLSN":  17126771000006809600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500294301000)\/",
        "BackupFinishDate":  "\/Date(1500294301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2efbc472-83cd-413f-800c-a7836d4cd91c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500294301000)\/",
        "Start":  "\/Date(1500294301000)\/",
        "BackupSetId":  "2efbc472-83cd-413f-800c-a7836d4cd91c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126771000008810100001,
        "LastLSN":  17126771000010016100001,
        "CheckpointLSN":  17126771000008901300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500294600000)\/",
        "BackupFinishDate":  "\/Date(1500294600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "eb911dc9-e1fb-4f11-9013-144b938f9d89",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500294600000)\/",
        "Start":  "\/Date(1500294600000)\/",
        "BackupSetId":  "eb911dc9-e1fb-4f11-9013-144b938f9d89",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126771000010016100001,
        "LastLSN":  17126773000000178000001,
        "CheckpointLSN":  17126772000010626500021,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500294900000)\/",
        "BackupFinishDate":  "\/Date(1500294901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "dedf7209-9510-4ec2-abf4-ad5bf4854ad2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500294901000)\/",
        "Start":  "\/Date(1500294900000)\/",
        "BackupSetId":  "dedf7209-9510-4ec2-abf4-ad5bf4854ad2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126773000000178000001,
        "LastLSN":  17126773000002010100001,
        "CheckpointLSN":  17126773000000178500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500295200000)\/",
        "BackupFinishDate":  "\/Date(1500295200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6d4a8b85-b8a5-4497-9741-27811db8ac86",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500295200000)\/",
        "Start":  "\/Date(1500295200000)\/",
        "BackupSetId":  "6d4a8b85-b8a5-4497-9741-27811db8ac86",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126773000002010100001,
        "LastLSN":  17126773000003503800001,
        "CheckpointLSN":  17126773000002261000037,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500295500000)\/",
        "BackupFinishDate":  "\/Date(1500295501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "123be27a-dc47-4cf4-8166-36f4183c1401",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500295501000)\/",
        "Start":  "\/Date(1500295500000)\/",
        "BackupSetId":  "123be27a-dc47-4cf4-8166-36f4183c1401",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126773000003503800001,
        "LastLSN":  17126773000004968500001,
        "CheckpointLSN":  17126773000004363500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500295800000)\/",
        "BackupFinishDate":  "\/Date(1500295800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0237d9df-8eaf-4c3f-8258-2b01d083a76d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500295800000)\/",
        "Start":  "\/Date(1500295800000)\/",
        "BackupSetId":  "0237d9df-8eaf-4c3f-8258-2b01d083a76d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126773000004968500001,
        "LastLSN":  17126773000006835600001,
        "CheckpointLSN":  17126773000006453900014,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500296100000)\/",
        "BackupFinishDate":  "\/Date(1500296100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e7c0c765-1f74-45ea-be91-321257ce9ae5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500296100000)\/",
        "Start":  "\/Date(1500296100000)\/",
        "BackupSetId":  "e7c0c765-1f74-45ea-be91-321257ce9ae5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126773000006835600001,
        "LastLSN":  17126774000000125600001,
        "CheckpointLSN":  17126773000011195400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500296400000)\/",
        "BackupFinishDate":  "\/Date(1500296400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4330bde6-bbb8-4b42-b0fb-83e47911e6af",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500296400000)\/",
        "Start":  "\/Date(1500296400000)\/",
        "BackupSetId":  "4330bde6-bbb8-4b42-b0fb-83e47911e6af",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126774000000125600001,
        "LastLSN":  17126774000003548800001,
        "CheckpointLSN":  17126774000002244200026,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500296700000)\/",
        "BackupFinishDate":  "\/Date(1500296701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1e9b8c9d-0a26-46de-9f5d-b0d76bff0da8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500296701000)\/",
        "Start":  "\/Date(1500296700000)\/",
        "BackupSetId":  "1e9b8c9d-0a26-46de-9f5d-b0d76bff0da8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126774000003548800001,
        "LastLSN":  17126774000010613100001,
        "CheckpointLSN":  17126774000009373500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500297001000)\/",
        "BackupFinishDate":  "\/Date(1500297001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "12b8522c-0156-4142-8ba9-45313ead74e1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500297001000)\/",
        "Start":  "\/Date(1500297001000)\/",
        "BackupSetId":  "12b8522c-0156-4142-8ba9-45313ead74e1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126774000010613100001,
        "LastLSN":  17126777000002520900001,
        "CheckpointLSN":  17126777000000748800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500297300000)\/",
        "BackupFinishDate":  "\/Date(1500297300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a4b3a60f-47c7-4f8d-9a89-ce206c25393b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500297300000)\/",
        "Start":  "\/Date(1500297300000)\/",
        "BackupSetId":  "a4b3a60f-47c7-4f8d-9a89-ce206c25393b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126777000002520900001,
        "LastLSN":  17126777000004313900001,
        "CheckpointLSN":  17126777000002832300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500297600000)\/",
        "BackupFinishDate":  "\/Date(1500297600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ca3bce0b-2617-4e8f-a356-d69573d411a8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500297600000)\/",
        "Start":  "\/Date(1500297600000)\/",
        "BackupSetId":  "ca3bce0b-2617-4e8f-a356-d69573d411a8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126777000004313900001,
        "LastLSN":  17126777000006093700001,
        "CheckpointLSN":  17126777000004915000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500297900000)\/",
        "BackupFinishDate":  "\/Date(1500297900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a21045a6-2154-4978-964c-f641a66c7560",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500297900000)\/",
        "Start":  "\/Date(1500297900000)\/",
        "BackupSetId":  "a21045a6-2154-4978-964c-f641a66c7560",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126777000006093700001,
        "LastLSN":  17126777000008065100001,
        "CheckpointLSN":  17126777000006989400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500298200000)\/",
        "BackupFinishDate":  "\/Date(1500298200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "730892d2-2ab7-4509-a474-d057797ea3f3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500298200000)\/",
        "Start":  "\/Date(1500298200000)\/",
        "BackupSetId":  "730892d2-2ab7-4509-a474-d057797ea3f3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126777000008065100001,
        "LastLSN":  17126777000010590300001,
        "CheckpointLSN":  17126777000009089300021,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500298500000)\/",
        "BackupFinishDate":  "\/Date(1500298501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "89f62dec-7431-40d4-a84e-c70ef479b348",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500298501000)\/",
        "Start":  "\/Date(1500298500000)\/",
        "BackupSetId":  "89f62dec-7431-40d4-a84e-c70ef479b348",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126777000010590300001,
        "LastLSN":  17126778000002117600001,
        "CheckpointLSN":  17126778000000271200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500298801000)\/",
        "BackupFinishDate":  "\/Date(1500298801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b5988b14-0dfb-490c-a6fc-883aeea0eedd",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500298801000)\/",
        "Start":  "\/Date(1500298801000)\/",
        "BackupSetId":  "b5988b14-0dfb-490c-a6fc-883aeea0eedd",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126778000002117600001,
        "LastLSN":  17126778000003921600001,
        "CheckpointLSN":  17126778000002352400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500299100000)\/",
        "BackupFinishDate":  "\/Date(1500299100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3bceadf0-e7b9-4213-a51b-d53e1d5f7487",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500299100000)\/",
        "Start":  "\/Date(1500299100000)\/",
        "BackupSetId":  "3bceadf0-e7b9-4213-a51b-d53e1d5f7487",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126778000003921600001,
        "LastLSN":  17126778000006263200001,
        "CheckpointLSN":  17126778000004435600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500299400000)\/",
        "BackupFinishDate":  "\/Date(1500299400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "61fd0a07-3844-43bd-b3b6-2c040c564d58",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500299400000)\/",
        "Start":  "\/Date(1500299400000)\/",
        "BackupSetId":  "61fd0a07-3844-43bd-b3b6-2c040c564d58",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126778000006263200001,
        "LastLSN":  17126778000008246700001,
        "CheckpointLSN":  17126778000006522900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500299700000)\/",
        "BackupFinishDate":  "\/Date(1500299700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7e4e821e-cf7a-4608-af25-268c4c5d3c8e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500299700000)\/",
        "Start":  "\/Date(1500299700000)\/",
        "BackupSetId":  "7e4e821e-cf7a-4608-af25-268c4c5d3c8e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126778000008246700001,
        "LastLSN":  17126778000010081000001,
        "CheckpointLSN":  17126778000008606700003,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500300001000)\/",
        "BackupFinishDate":  "\/Date(1500300001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d1699682-4059-4480-9e2c-0800a085789e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500300001000)\/",
        "Start":  "\/Date(1500300001000)\/",
        "BackupSetId":  "d1699682-4059-4480-9e2c-0800a085789e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126778000010081000001,
        "LastLSN":  17126778000011836900001,
        "CheckpointLSN":  17126778000010687500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500300300000)\/",
        "BackupFinishDate":  "\/Date(1500300300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "85d85017-6a09-48ff-9acd-ada7359e953d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500300300000)\/",
        "Start":  "\/Date(1500300300000)\/",
        "BackupSetId":  "85d85017-6a09-48ff-9acd-ada7359e953d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126778000011836900001,
        "LastLSN":  17126779000000810100001,
        "CheckpointLSN":  17126778000012797400011,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500300600000)\/",
        "BackupFinishDate":  "\/Date(1500300600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ed3937d8-b02d-40e2-9889-ae8b3af4429f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500300600000)\/",
        "Start":  "\/Date(1500300600000)\/",
        "BackupSetId":  "ed3937d8-b02d-40e2-9889-ae8b3af4429f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126779000000810100001,
        "LastLSN":  17126779000004732900001,
        "CheckpointLSN":  17126779000002926400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500300900000)\/",
        "BackupFinishDate":  "\/Date(1500300900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "36f8b3f4-5f1a-4027-afde-b35af46389d5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500300900000)\/",
        "Start":  "\/Date(1500300900000)\/",
        "BackupSetId":  "36f8b3f4-5f1a-4027-afde-b35af46389d5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126779000004732900001,
        "LastLSN":  17126779000008977700001,
        "CheckpointLSN":  17126779000007086000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500301200000)\/",
        "BackupFinishDate":  "\/Date(1500301201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "13cae25a-7d70-4895-9339-1143a3e46c04",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500301201000)\/",
        "Start":  "\/Date(1500301200000)\/",
        "BackupSetId":  "13cae25a-7d70-4895-9339-1143a3e46c04",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126779000008977700001,
        "LastLSN":  17126780000000122300001,
        "CheckpointLSN":  17126779000011324900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500301500000)\/",
        "BackupFinishDate":  "\/Date(1500301501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ab91028a-56a2-43db-8847-7759a842fb88",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500301501000)\/",
        "Start":  "\/Date(1500301500000)\/",
        "BackupSetId":  "ab91028a-56a2-43db-8847-7759a842fb88",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126780000000122300001,
        "LastLSN":  17126780000006889000001,
        "CheckpointLSN":  17126780000004272600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500301800000)\/",
        "BackupFinishDate":  "\/Date(1500301800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "199c3e8f-3f7f-4fa5-94b8-11c7cf861bfa",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500301800000)\/",
        "Start":  "\/Date(1500301800000)\/",
        "BackupSetId":  "199c3e8f-3f7f-4fa5-94b8-11c7cf861bfa",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126780000006889000001,
        "LastLSN":  17126781000000129200001,
        "CheckpointLSN":  17126780000011125600020,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500302100000)\/",
        "BackupFinishDate":  "\/Date(1500302100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6172e35b-9c3a-4b09-a157-8495f8151fbf",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500302100000)\/",
        "Start":  "\/Date(1500302100000)\/",
        "BackupSetId":  "6172e35b-9c3a-4b09-a157-8495f8151fbf",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126781000000129200001,
        "LastLSN":  17126781000007955900001,
        "CheckpointLSN":  17126781000006067200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500302400000)\/",
        "BackupFinishDate":  "\/Date(1500302400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5e54dbe6-9c82-4123-b2bb-c88373031c02",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500302400000)\/",
        "Start":  "\/Date(1500302400000)\/",
        "BackupSetId":  "5e54dbe6-9c82-4123-b2bb-c88373031c02",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126781000007955900001,
        "LastLSN":  17126782000000191800001,
        "CheckpointLSN":  17126781000012584300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500302700000)\/",
        "BackupFinishDate":  "\/Date(1500302700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1b63b665-eb4c-4906-a003-57bb5de0dd13",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500302700000)\/",
        "Start":  "\/Date(1500302700000)\/",
        "BackupSetId":  "1b63b665-eb4c-4906-a003-57bb5de0dd13",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126782000000191800001,
        "LastLSN":  17126782000003846200001,
        "CheckpointLSN":  17126782000002271400021,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500303000000)\/",
        "BackupFinishDate":  "\/Date(1500303000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9e1a787e-ca4a-4800-90e9-62fe9da9e61c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500303000000)\/",
        "Start":  "\/Date(1500303000000)\/",
        "BackupSetId":  "9e1a787e-ca4a-4800-90e9-62fe9da9e61c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126782000003846200001,
        "LastLSN":  17126782000007532100001,
        "CheckpointLSN":  17126782000006493500033,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500303300000)\/",
        "BackupFinishDate":  "\/Date(1500303301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "22cd95f2-6237-437b-b9c4-68d8a91b03b2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500303301000)\/",
        "Start":  "\/Date(1500303300000)\/",
        "BackupSetId":  "22cd95f2-6237-437b-b9c4-68d8a91b03b2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126782000007532100001,
        "LastLSN":  17126782000011336100001,
        "CheckpointLSN":  17126782000011301400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500303600000)\/",
        "BackupFinishDate":  "\/Date(1500303600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b58cfb81-6a1c-41eb-a89f-5a6c2d13765f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500303600000)\/",
        "Start":  "\/Date(1500303600000)\/",
        "BackupSetId":  "b58cfb81-6a1c-41eb-a89f-5a6c2d13765f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126782000011336100001,
        "LastLSN":  17126783000003856900001,
        "CheckpointLSN":  17126783000002377900010,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500303900000)\/",
        "BackupFinishDate":  "\/Date(1500303900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "804850b6-1f91-4897-95b9-cf29522025c7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500303900000)\/",
        "Start":  "\/Date(1500303900000)\/",
        "BackupSetId":  "804850b6-1f91-4897-95b9-cf29522025c7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126783000003856900001,
        "LastLSN":  17126783000007774900001,
        "CheckpointLSN":  17126783000006572700012,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500304200000)\/",
        "BackupFinishDate":  "\/Date(1500304200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "97093961-a8e1-4a1d-b51f-2641e5963deb",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500304200000)\/",
        "Start":  "\/Date(1500304200000)\/",
        "BackupSetId":  "97093961-a8e1-4a1d-b51f-2641e5963deb",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126783000007774900001,
        "LastLSN":  17126783000009876500001,
        "CheckpointLSN":  17126783000008783800010,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500304500000)\/",
        "BackupFinishDate":  "\/Date(1500304500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "81b96f14-ab1a-4ccf-b74b-8ac9265ea8e2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500304500000)\/",
        "Start":  "\/Date(1500304500000)\/",
        "BackupSetId":  "81b96f14-ab1a-4ccf-b74b-8ac9265ea8e2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126783000009876500001,
        "LastLSN":  17126783000011970900001,
        "CheckpointLSN":  17126783000010917100020,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500304800000)\/",
        "BackupFinishDate":  "\/Date(1500304801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f4109d94-3369-4fbd-af37-a845e30527cd",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500304801000)\/",
        "Start":  "\/Date(1500304800000)\/",
        "BackupSetId":  "f4109d94-3369-4fbd-af37-a845e30527cd",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126783000011970900001,
        "LastLSN":  17126784000000917400001,
        "CheckpointLSN":  17126783000013021300012,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500305101000)\/",
        "BackupFinishDate":  "\/Date(1500305101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9b753932-0740-4f60-a9b9-53480db2a860",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500305101000)\/",
        "Start":  "\/Date(1500305101000)\/",
        "BackupSetId":  "9b753932-0740-4f60-a9b9-53480db2a860",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126784000000917400001,
        "LastLSN":  17126784000002971800001,
        "CheckpointLSN":  17126784000000917400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500305400000)\/",
        "BackupFinishDate":  "\/Date(1500305400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "930bec35-8193-4e23-8708-0ecf42a3d1ee",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500305400000)\/",
        "Start":  "\/Date(1500305400000)\/",
        "BackupSetId":  "930bec35-8193-4e23-8708-0ecf42a3d1ee",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126784000002971800001,
        "LastLSN":  17126784000005112100001,
        "CheckpointLSN":  17126784000005045900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500305700000)\/",
        "BackupFinishDate":  "\/Date(1500305700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "61df2d80-752a-4b5a-9010-9d414be9ea43",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500305700000)\/",
        "Start":  "\/Date(1500305700000)\/",
        "BackupSetId":  "61df2d80-752a-4b5a-9010-9d414be9ea43",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126784000005112100001,
        "LastLSN":  17126784000009792700001,
        "CheckpointLSN":  17126784000009487000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500306000000)\/",
        "BackupFinishDate":  "\/Date(1500306000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "111d878a-6fff-4923-a43c-5c69411e0439",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500306000000)\/",
        "Start":  "\/Date(1500306000000)\/",
        "BackupSetId":  "111d878a-6fff-4923-a43c-5c69411e0439",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126784000009792700001,
        "LastLSN":  17126785000003622200001,
        "CheckpointLSN":  17126785000003095000013,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500306300000)\/",
        "BackupFinishDate":  "\/Date(1500306301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0a59e961-41fc-4064-9ae7-8117dcf74d65",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500306301000)\/",
        "Start":  "\/Date(1500306300000)\/",
        "BackupSetId":  "0a59e961-41fc-4064-9ae7-8117dcf74d65",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126785000003622200001,
        "LastLSN":  17126785000006995200001,
        "CheckpointLSN":  17126785000005295800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500306600000)\/",
        "BackupFinishDate":  "\/Date(1500306600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ef28a791-4910-4776-b427-3dcad88030fe",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500306600000)\/",
        "Start":  "\/Date(1500306600000)\/",
        "BackupSetId":  "ef28a791-4910-4776-b427-3dcad88030fe",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126785000006995200001,
        "LastLSN":  17126785000009533100001,
        "CheckpointLSN":  17126785000009451100017,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500306900000)\/",
        "BackupFinishDate":  "\/Date(1500306900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ee4ab663-6d30-4d2f-8758-e722c7d8b7db",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500306900000)\/",
        "Start":  "\/Date(1500306900000)\/",
        "BackupSetId":  "ee4ab663-6d30-4d2f-8758-e722c7d8b7db",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126785000009533100001,
        "LastLSN":  17126785000010803400001,
        "CheckpointLSN":  17126785000009451100017,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500307200000)\/",
        "BackupFinishDate":  "\/Date(1500307200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8b9235ab-650d-41d4-a2a7-ac7807da7eec",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500307200000)\/",
        "Start":  "\/Date(1500307200000)\/",
        "BackupSetId":  "8b9235ab-650d-41d4-a2a7-ac7807da7eec",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126785000010803400001,
        "LastLSN":  17126785000012009100001,
        "CheckpointLSN":  17126785000011552000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500307500000)\/",
        "BackupFinishDate":  "\/Date(1500307500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9f86cd81-7997-4c7e-b131-4e9d8b263f93",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500307500000)\/",
        "Start":  "\/Date(1500307500000)\/",
        "BackupSetId":  "9f86cd81-7997-4c7e-b131-4e9d8b263f93",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126785000012009100001,
        "LastLSN":  17126786000000075800001,
        "CheckpointLSN":  17126785000011552000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500307800000)\/",
        "BackupFinishDate":  "\/Date(1500307800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "92c64cba-3cd4-423e-855d-9a168bef3431",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500307800000)\/",
        "Start":  "\/Date(1500307800000)\/",
        "BackupSetId":  "92c64cba-3cd4-423e-855d-9a168bef3431",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126786000000075800001,
        "LastLSN":  17126786000001283700001,
        "CheckpointLSN":  17126786000000075800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500308100000)\/",
        "BackupFinishDate":  "\/Date(1500308100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c5aeff97-bfdd-4892-9cbb-ee37a8ca9599",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500308100000)\/",
        "Start":  "\/Date(1500308100000)\/",
        "BackupSetId":  "c5aeff97-bfdd-4892-9cbb-ee37a8ca9599",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126786000001283700001,
        "LastLSN":  17126786000002464700001,
        "CheckpointLSN":  17126786000002158000018,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500308400000)\/",
        "BackupFinishDate":  "\/Date(1500308400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "65f4baf9-bfe4-41d1-842f-1b4265f781f1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500308400000)\/",
        "Start":  "\/Date(1500308400000)\/",
        "BackupSetId":  "65f4baf9-bfe4-41d1-842f-1b4265f781f1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126786000002464700001,
        "LastLSN":  17126786000003657100001,
        "CheckpointLSN":  17126786000002158000018,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500308700000)\/",
        "BackupFinishDate":  "\/Date(1500308700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "83043ee6-68a2-44ea-9157-dda30714019c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500308700000)\/",
        "Start":  "\/Date(1500308700000)\/",
        "BackupSetId":  "83043ee6-68a2-44ea-9157-dda30714019c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126786000003657100001,
        "LastLSN":  17126786000004852900001,
        "CheckpointLSN":  17126786000004262600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500309001000)\/",
        "BackupFinishDate":  "\/Date(1500309001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "26b98811-378c-444b-a818-76a4375dfdbf",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500309001000)\/",
        "Start":  "\/Date(1500309001000)\/",
        "BackupSetId":  "26b98811-378c-444b-a818-76a4375dfdbf",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126786000004852900001,
        "LastLSN":  17126786000006060600001,
        "CheckpointLSN":  17126786000004262600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500309300000)\/",
        "BackupFinishDate":  "\/Date(1500309300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ddbd7bb1-67f7-478f-8a34-7b91ad163cf7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500309300000)\/",
        "Start":  "\/Date(1500309300000)\/",
        "BackupSetId":  "ddbd7bb1-67f7-478f-8a34-7b91ad163cf7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126786000006060600001,
        "LastLSN":  17126786000007231300001,
        "CheckpointLSN":  17126786000006350500025,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500309600000)\/",
        "BackupFinishDate":  "\/Date(1500309600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f5bac5f6-6dc4-48c4-be02-e3109ef21bab",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500309600000)\/",
        "Start":  "\/Date(1500309600000)\/",
        "BackupSetId":  "f5bac5f6-6dc4-48c4-be02-e3109ef21bab",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126786000007231300001,
        "LastLSN":  17126786000008387300001,
        "CheckpointLSN":  17126786000006350500025,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500309900000)\/",
        "BackupFinishDate":  "\/Date(1500309900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "19cdc4ee-1c30-4754-a8bb-efac97be38d6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500309900000)\/",
        "Start":  "\/Date(1500309900000)\/",
        "BackupSetId":  "19cdc4ee-1c30-4754-a8bb-efac97be38d6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126786000008387300001,
        "LastLSN":  17126786000009546900001,
        "CheckpointLSN":  17126786000008421600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500310200000)\/",
        "BackupFinishDate":  "\/Date(1500310200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f208e5b6-b897-4769-a745-29a362d3c0da",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500310200000)\/",
        "Start":  "\/Date(1500310200000)\/",
        "BackupSetId":  "f208e5b6-b897-4769-a745-29a362d3c0da",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126786000009546900001,
        "LastLSN":  17126786000010698900001,
        "CheckpointLSN":  17126786000010503000015,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500310500000)\/",
        "BackupFinishDate":  "\/Date(1500310500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "19b76ecf-21eb-4cb5-91d8-5f41c46f536b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500310500000)\/",
        "Start":  "\/Date(1500310500000)\/",
        "BackupSetId":  "19b76ecf-21eb-4cb5-91d8-5f41c46f536b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126786000010698900001,
        "LastLSN":  17126786000011867500001,
        "CheckpointLSN":  17126786000011858200224,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500310801000)\/",
        "BackupFinishDate":  "\/Date(1500310801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "96c4f67a-bc49-443e-9a3a-6ea789dbccba",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500310801000)\/",
        "Start":  "\/Date(1500310801000)\/",
        "BackupSetId":  "96c4f67a-bc49-443e-9a3a-6ea789dbccba",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126786000011867500001,
        "LastLSN":  17126787000009695300001,
        "CheckpointLSN":  17126787000008980800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500311100000)\/",
        "BackupFinishDate":  "\/Date(1500311101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "69fd6bfd-9799-4b9f-b361-a2e7f378a4e9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500311101000)\/",
        "Start":  "\/Date(1500311100000)\/",
        "BackupSetId":  "69fd6bfd-9799-4b9f-b361-a2e7f378a4e9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126787000009695300001,
        "LastLSN":  17126788000000905000001,
        "CheckpointLSN":  17126788000000012500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500311400000)\/",
        "BackupFinishDate":  "\/Date(1500311400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1e257e33-4a25-4cb0-a931-9c24beb9eb74",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500311400000)\/",
        "Start":  "\/Date(1500311400000)\/",
        "BackupSetId":  "1e257e33-4a25-4cb0-a931-9c24beb9eb74",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126788000000905000001,
        "LastLSN":  17126788000002017500001,
        "CheckpointLSN":  17126788000000012500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500311700000)\/",
        "BackupFinishDate":  "\/Date(1500311700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0db20a5e-da84-4bd2-8089-e4a15af46fdb",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500311700000)\/",
        "Start":  "\/Date(1500311700000)\/",
        "BackupSetId":  "0db20a5e-da84-4bd2-8089-e4a15af46fdb",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126788000002017500001,
        "LastLSN":  17126788000003236700001,
        "CheckpointLSN":  17126788000002076700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500312000000)\/",
        "BackupFinishDate":  "\/Date(1500312000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "aefcf752-99f7-40db-8a39-109cd7e5f3c3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500312000000)\/",
        "Start":  "\/Date(1500312000000)\/",
        "BackupSetId":  "aefcf752-99f7-40db-8a39-109cd7e5f3c3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126788000003236700001,
        "LastLSN":  17126788000004427900001,
        "CheckpointLSN":  17126788000004155300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500312300000)\/",
        "BackupFinishDate":  "\/Date(1500312301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d4c37d96-ef83-49f4-8a37-c88e58fcc64e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500312301000)\/",
        "Start":  "\/Date(1500312300000)\/",
        "BackupSetId":  "d4c37d96-ef83-49f4-8a37-c88e58fcc64e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126788000004427900001,
        "LastLSN":  17126788000005594500001,
        "CheckpointLSN":  17126788000004155300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500312600000)\/",
        "BackupFinishDate":  "\/Date(1500312600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0da97386-ed6f-40ea-bca3-821cce95d411",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500312600000)\/",
        "Start":  "\/Date(1500312600000)\/",
        "BackupSetId":  "0da97386-ed6f-40ea-bca3-821cce95d411",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126788000005594500001,
        "LastLSN":  17126788000006802800001,
        "CheckpointLSN":  17126788000006230200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500312900000)\/",
        "BackupFinishDate":  "\/Date(1500312900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fbbd0454-1665-4688-8f49-9716c73ac406",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500312900000)\/",
        "Start":  "\/Date(1500312900000)\/",
        "BackupSetId":  "fbbd0454-1665-4688-8f49-9716c73ac406",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126788000006802800001,
        "LastLSN":  17126788000008012800001,
        "CheckpointLSN":  17126788000006230200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500313200000)\/",
        "BackupFinishDate":  "\/Date(1500313200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a17ca55b-b711-4e7c-9b52-c9feb7a4b01f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500313200000)\/",
        "Start":  "\/Date(1500313200000)\/",
        "BackupSetId":  "a17ca55b-b711-4e7c-9b52-c9feb7a4b01f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126788000008012800001,
        "LastLSN":  17126788000009239900001,
        "CheckpointLSN":  17126788000008319400021,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500313500000)\/",
        "BackupFinishDate":  "\/Date(1500313500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d1333f54-d262-4c65-b8a2-08e24342f01d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500313500000)\/",
        "Start":  "\/Date(1500313500000)\/",
        "BackupSetId":  "d1333f54-d262-4c65-b8a2-08e24342f01d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126788000009239900001,
        "LastLSN":  17126788000010434800001,
        "CheckpointLSN":  17126788000010390000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500313800000)\/",
        "BackupFinishDate":  "\/Date(1500313800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "450e3a77-22cd-4721-9e8b-4908ca88e1f8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500313800000)\/",
        "Start":  "\/Date(1500313800000)\/",
        "BackupSetId":  "450e3a77-22cd-4721-9e8b-4908ca88e1f8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126788000010434800001,
        "LastLSN":  17126788000011630800001,
        "CheckpointLSN":  17126788000010390000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500314100000)\/",
        "BackupFinishDate":  "\/Date(1500314100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ed74e744-d90f-4608-a2bc-b0f309cce63e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500314100000)\/",
        "Start":  "\/Date(1500314100000)\/",
        "BackupSetId":  "ed74e744-d90f-4608-a2bc-b0f309cce63e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126788000011630800001,
        "LastLSN":  17126788000012836800001,
        "CheckpointLSN":  17126788000012478400019,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500314400000)\/",
        "BackupFinishDate":  "\/Date(1500314400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3f54addf-32db-478e-935f-3fd8248531b2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500314400000)\/",
        "Start":  "\/Date(1500314400000)\/",
        "BackupSetId":  "3f54addf-32db-478e-935f-3fd8248531b2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126788000012836800001,
        "LastLSN":  17126789000000926900001,
        "CheckpointLSN":  17126788000012478400019,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500314700000)\/",
        "BackupFinishDate":  "\/Date(1500314700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "bcaf896e-bac4-41da-b075-de9ee17104dd",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500314700000)\/",
        "Start":  "\/Date(1500314700000)\/",
        "BackupSetId":  "bcaf896e-bac4-41da-b075-de9ee17104dd",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126789000000926900001,
        "LastLSN":  17126789000002123200001,
        "CheckpointLSN":  17126789000000926900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500315000000)\/",
        "BackupFinishDate":  "\/Date(1500315000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "929003ea-be9f-446b-bc1e-72d53c20a51f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500315000000)\/",
        "Start":  "\/Date(1500315000000)\/",
        "BackupSetId":  "929003ea-be9f-446b-bc1e-72d53c20a51f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126789000002123200001,
        "LastLSN":  17126789000003334600001,
        "CheckpointLSN":  17126789000003013500028,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500315300000)\/",
        "BackupFinishDate":  "\/Date(1500315300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "03da547b-31a1-4bf6-902b-5fd073294502",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500315300000)\/",
        "Start":  "\/Date(1500315300000)\/",
        "BackupSetId":  "03da547b-31a1-4bf6-902b-5fd073294502",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126789000003334600001,
        "LastLSN":  17126789000004497500001,
        "CheckpointLSN":  17126789000003013500028,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500315600000)\/",
        "BackupFinishDate":  "\/Date(1500315600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "01ee91bb-3361-4fdf-9d9a-7915bc961aef",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500315600000)\/",
        "Start":  "\/Date(1500315600000)\/",
        "BackupSetId":  "01ee91bb-3361-4fdf-9d9a-7915bc961aef",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126789000004497500001,
        "LastLSN":  17126789000005711100001,
        "CheckpointLSN":  17126789000005126100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500315900000)\/",
        "BackupFinishDate":  "\/Date(1500315900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1af11de0-cd53-4654-b1fc-2acb27a37667",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500315900000)\/",
        "Start":  "\/Date(1500315900000)\/",
        "BackupSetId":  "1af11de0-cd53-4654-b1fc-2acb27a37667",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126789000005711100001,
        "LastLSN":  17126789000006890700001,
        "CheckpointLSN":  17126789000005126100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500316200000)\/",
        "BackupFinishDate":  "\/Date(1500316200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c3594826-a7d7-400b-a31f-6415ab5db356",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500316200000)\/",
        "Start":  "\/Date(1500316200000)\/",
        "BackupSetId":  "c3594826-a7d7-400b-a31f-6415ab5db356",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126789000006890700001,
        "LastLSN":  17126789000008104100001,
        "CheckpointLSN":  17126789000007196100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500316500000)\/",
        "BackupFinishDate":  "\/Date(1500316500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c58fb382-2fac-4441-ac41-249ce0aec21d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500316500000)\/",
        "Start":  "\/Date(1500316500000)\/",
        "BackupSetId":  "c58fb382-2fac-4441-ac41-249ce0aec21d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126789000008104100001,
        "LastLSN":  17126789000009290400001,
        "CheckpointLSN":  17126789000009264800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500316800000)\/",
        "BackupFinishDate":  "\/Date(1500316800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b93dbe64-b2fc-4dca-be13-9c67bfde22e4",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500316800000)\/",
        "Start":  "\/Date(1500316800000)\/",
        "BackupSetId":  "b93dbe64-b2fc-4dca-be13-9c67bfde22e4",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126789000009290400001,
        "LastLSN":  17126789000010467300001,
        "CheckpointLSN":  17126789000009264800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500317100000)\/",
        "BackupFinishDate":  "\/Date(1500317101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "830b5a49-5bbf-48eb-958c-2649de4d38cf",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500317101000)\/",
        "Start":  "\/Date(1500317100000)\/",
        "BackupSetId":  "830b5a49-5bbf-48eb-958c-2649de4d38cf",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126789000010467300001,
        "LastLSN":  17126789000011655000001,
        "CheckpointLSN":  17126789000011355400018,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500317400000)\/",
        "BackupFinishDate":  "\/Date(1500317400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7754519e-9d85-4330-b411-13f9dec08ddb",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500317400000)\/",
        "Start":  "\/Date(1500317400000)\/",
        "BackupSetId":  "7754519e-9d85-4330-b411-13f9dec08ddb",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126789000011655000001,
        "LastLSN":  17126789000012838400001,
        "CheckpointLSN":  17126789000011355400018,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500317700000)\/",
        "BackupFinishDate":  "\/Date(1500317700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c09b4498-633d-4878-9275-ace3c14f13de",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500317700000)\/",
        "Start":  "\/Date(1500317700000)\/",
        "BackupSetId":  "c09b4498-633d-4878-9275-ace3c14f13de",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126789000012838400001,
        "LastLSN":  17126790000000941500001,
        "CheckpointLSN":  17126790000000364500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500318000000)\/",
        "BackupFinishDate":  "\/Date(1500318000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f410cf53-1fc3-4d33-8704-315ba49a2b5e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500318000000)\/",
        "Start":  "\/Date(1500318000000)\/",
        "BackupSetId":  "f410cf53-1fc3-4d33-8704-315ba49a2b5e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126790000000941500001,
        "LastLSN":  17126790000002240600001,
        "CheckpointLSN":  17126790000000364500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500318301000)\/",
        "BackupFinishDate":  "\/Date(1500318301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0b28ee12-0115-4dcc-a155-48a2b2635372",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500318301000)\/",
        "Start":  "\/Date(1500318301000)\/",
        "BackupSetId":  "0b28ee12-0115-4dcc-a155-48a2b2635372",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126790000002240600001,
        "LastLSN":  17126790000003876300001,
        "CheckpointLSN":  17126790000002434300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500318600000)\/",
        "BackupFinishDate":  "\/Date(1500318600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "94d028ab-6264-4c2c-a39c-b2f2382b2d61",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500318600000)\/",
        "Start":  "\/Date(1500318600000)\/",
        "BackupSetId":  "94d028ab-6264-4c2c-a39c-b2f2382b2d61",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126790000003876300001,
        "LastLSN":  17126790000005588800001,
        "CheckpointLSN":  17126790000004507300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500318900000)\/",
        "BackupFinishDate":  "\/Date(1500318900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "801e8890-30d1-42af-97d4-6d50480cfeb2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500318900000)\/",
        "Start":  "\/Date(1500318900000)\/",
        "BackupSetId":  "801e8890-30d1-42af-97d4-6d50480cfeb2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126790000005588800001,
        "LastLSN":  17126790000012694800001,
        "CheckpointLSN":  17126790000011694400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500319200000)\/",
        "BackupFinishDate":  "\/Date(1500319201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "387bd64c-9dd3-4d80-a834-91d1b240d22a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500319201000)\/",
        "Start":  "\/Date(1500319200000)\/",
        "BackupSetId":  "387bd64c-9dd3-4d80-a834-91d1b240d22a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126790000012694800001,
        "LastLSN":  17126795000003531600001,
        "CheckpointLSN":  17126795000003138100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500319500000)\/",
        "BackupFinishDate":  "\/Date(1500319501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c2757c70-94aa-4669-9a25-ca8c4f978bb8",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500319501000)\/",
        "Start":  "\/Date(1500319500000)\/",
        "BackupSetId":  "c2757c70-94aa-4669-9a25-ca8c4f978bb8",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126795000003531600001,
        "LastLSN":  17126848000002843800001,
        "CheckpointLSN":  17126840000008736200036,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500319800000)\/",
        "BackupFinishDate":  "\/Date(1500319821000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a1d080d6-1487-4dba-8aa4-ca8d4010dc95",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500319821000)\/",
        "Start":  "\/Date(1500319800000)\/",
        "BackupSetId":  "a1d080d6-1487-4dba-8aa4-ca8d4010dc95",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126848000002843800001,
        "LastLSN":  17126848000004172700001,
        "CheckpointLSN":  17126848000002870200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500320101000)\/",
        "BackupFinishDate":  "\/Date(1500320101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "22e9e106-8ddd-42d8-98ae-5d778cd8c2ef",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500320101000)\/",
        "Start":  "\/Date(1500320101000)\/",
        "BackupSetId":  "22e9e106-8ddd-42d8-98ae-5d778cd8c2ef",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126848000004172700001,
        "LastLSN":  17126848000005396500001,
        "CheckpointLSN":  17126848000004934500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500320400000)\/",
        "BackupFinishDate":  "\/Date(1500320400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "10115706-e329-46d9-812b-03271af0e193",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500320400000)\/",
        "Start":  "\/Date(1500320400000)\/",
        "BackupSetId":  "10115706-e329-46d9-812b-03271af0e193",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126848000005396500001,
        "LastLSN":  17126848000006603200001,
        "CheckpointLSN":  17126848000004934500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500320700000)\/",
        "BackupFinishDate":  "\/Date(1500320700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "646fe6c1-9e63-4d6e-b85d-3a4753ddbcba",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500320700000)\/",
        "Start":  "\/Date(1500320700000)\/",
        "BackupSetId":  "646fe6c1-9e63-4d6e-b85d-3a4753ddbcba",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126848000006603200001,
        "LastLSN":  17126848000007803200001,
        "CheckpointLSN":  17126848000007008000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500321000000)\/",
        "BackupFinishDate":  "\/Date(1500321000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "06e17623-8252-4383-be66-e021c039216d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500321000000)\/",
        "Start":  "\/Date(1500321000000)\/",
        "BackupSetId":  "06e17623-8252-4383-be66-e021c039216d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126848000007803200001,
        "LastLSN":  17126848000009001600001,
        "CheckpointLSN":  17126848000007008000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500321300000)\/",
        "BackupFinishDate":  "\/Date(1500321300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "db351aee-6d6d-429c-a9e0-bde4d54b8c3e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500321300000)\/",
        "Start":  "\/Date(1500321300000)\/",
        "BackupSetId":  "db351aee-6d6d-429c-a9e0-bde4d54b8c3e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126848000009001600001,
        "LastLSN":  17126848000010197800001,
        "CheckpointLSN":  17126848000009078700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500321600000)\/",
        "BackupFinishDate":  "\/Date(1500321600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ecafd19b-1fb1-41f0-afa6-adf449bda841",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500321600000)\/",
        "Start":  "\/Date(1500321600000)\/",
        "BackupSetId":  "ecafd19b-1fb1-41f0-afa6-adf449bda841",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126848000010197800001,
        "LastLSN":  17126848000011480700001,
        "CheckpointLSN":  17126848000011165700080,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500321900000)\/",
        "BackupFinishDate":  "\/Date(1500321900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f71f7b17-ada2-44c8-a411-67d52a381e2c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500321900000)\/",
        "Start":  "\/Date(1500321900000)\/",
        "BackupSetId":  "f71f7b17-ada2-44c8-a411-67d52a381e2c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126848000011480700001,
        "LastLSN":  17126848000012660000001,
        "CheckpointLSN":  17126848000011165700080,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500322200000)\/",
        "BackupFinishDate":  "\/Date(1500322200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "aa3dea0c-9816-4140-9969-c79258595a51",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500322200000)\/",
        "Start":  "\/Date(1500322200000)\/",
        "BackupSetId":  "aa3dea0c-9816-4140-9969-c79258595a51",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126848000012660000001,
        "LastLSN":  17126849000000746500001,
        "CheckpointLSN":  17126849000000195400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500322500000)\/",
        "BackupFinishDate":  "\/Date(1500322501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fc5cd42b-ce88-43dd-83ec-928893a8c103",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500322501000)\/",
        "Start":  "\/Date(1500322500000)\/",
        "BackupSetId":  "fc5cd42b-ce88-43dd-83ec-928893a8c103",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126849000000746500001,
        "LastLSN":  17126849000001913300001,
        "CheckpointLSN":  17126849000000195400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500322800000)\/",
        "BackupFinishDate":  "\/Date(1500322800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3428d0b5-b8bf-435a-8d61-0c58c6566af4",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500322800000)\/",
        "Start":  "\/Date(1500322800000)\/",
        "BackupSetId":  "3428d0b5-b8bf-435a-8d61-0c58c6566af4",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126849000001913300001,
        "LastLSN":  17126849000003125200001,
        "CheckpointLSN":  17126849000002265400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500323100000)\/",
        "BackupFinishDate":  "\/Date(1500323100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "75f1be35-8fb8-43aa-80e5-bff41f6216a7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500323100000)\/",
        "Start":  "\/Date(1500323100000)\/",
        "BackupSetId":  "75f1be35-8fb8-43aa-80e5-bff41f6216a7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126849000003125200001,
        "LastLSN":  17126849000004305900001,
        "CheckpointLSN":  17126849000002265400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500323400000)\/",
        "BackupFinishDate":  "\/Date(1500323400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "10cc640f-4c2a-41e5-bec8-728f69d10929",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500323400000)\/",
        "Start":  "\/Date(1500323400000)\/",
        "BackupSetId":  "10cc640f-4c2a-41e5-bec8-728f69d10929",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126849000004305900001,
        "LastLSN":  17126849000005523700001,
        "CheckpointLSN":  17126849000004341300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500323700000)\/",
        "BackupFinishDate":  "\/Date(1500323700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d886afa4-4dc3-42e3-9c98-6cc867e364cb",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500323700000)\/",
        "Start":  "\/Date(1500323700000)\/",
        "BackupSetId":  "d886afa4-4dc3-42e3-9c98-6cc867e364cb",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126849000005523700001,
        "LastLSN":  17126849000006710400001,
        "CheckpointLSN":  17126849000006427300051,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500324001000)\/",
        "BackupFinishDate":  "\/Date(1500324001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "595ebcc5-8492-4fbf-9766-b059f38995f0",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500324001000)\/",
        "Start":  "\/Date(1500324001000)\/",
        "BackupSetId":  "595ebcc5-8492-4fbf-9766-b059f38995f0",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126849000006710400001,
        "LastLSN":  17126849000007887500001,
        "CheckpointLSN":  17126849000006427300051,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500324300000)\/",
        "BackupFinishDate":  "\/Date(1500324300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e3a3801c-df3a-4043-a797-8aa80b36724d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500324300000)\/",
        "Start":  "\/Date(1500324300000)\/",
        "BackupSetId":  "e3a3801c-df3a-4043-a797-8aa80b36724d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126849000007887500001,
        "LastLSN":  17126849000009079800001,
        "CheckpointLSN":  17126849000008551800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500324600000)\/",
        "BackupFinishDate":  "\/Date(1500324600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1577db61-1e85-450a-a1fe-e57017cb51ef",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500324600000)\/",
        "Start":  "\/Date(1500324600000)\/",
        "BackupSetId":  "1577db61-1e85-450a-a1fe-e57017cb51ef",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126849000009079800001,
        "LastLSN":  17126849000010260700001,
        "CheckpointLSN":  17126849000008551800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500324900000)\/",
        "BackupFinishDate":  "\/Date(1500324900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b406618c-ae24-4989-974a-1ad01497cb19",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500324900000)\/",
        "Start":  "\/Date(1500324900000)\/",
        "BackupSetId":  "b406618c-ae24-4989-974a-1ad01497cb19",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126849000010260700001,
        "LastLSN":  17126849000011476200001,
        "CheckpointLSN":  17126849000010623300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500325200000)\/",
        "BackupFinishDate":  "\/Date(1500325200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0dd6b853-377c-4e97-a7ae-6e2f261b8a6d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500325200000)\/",
        "Start":  "\/Date(1500325200000)\/",
        "BackupSetId":  "0dd6b853-377c-4e97-a7ae-6e2f261b8a6d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126849000011476200001,
        "LastLSN":  17126849000012642500001,
        "CheckpointLSN":  17126849000010623300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500325501000)\/",
        "BackupFinishDate":  "\/Date(1500325501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "150b886a-9921-4d7a-805a-80ad0c7a85d7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500325501000)\/",
        "Start":  "\/Date(1500325501000)\/",
        "BackupSetId":  "150b886a-9921-4d7a-805a-80ad0c7a85d7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126849000012642500001,
        "LastLSN":  17126850000000739600001,
        "CheckpointLSN":  17126849000012700100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500325800000)\/",
        "BackupFinishDate":  "\/Date(1500325800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "890d8f2c-a0d6-4bdd-80bc-811d500f910f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500325800000)\/",
        "Start":  "\/Date(1500325800000)\/",
        "BackupSetId":  "890d8f2c-a0d6-4bdd-80bc-811d500f910f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126850000000739600001,
        "LastLSN":  17126850000001930200001,
        "CheckpointLSN":  17126850000000739600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500326100000)\/",
        "BackupFinishDate":  "\/Date(1500326100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ff160963-4a54-4592-a1a9-29000be31344",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500326100000)\/",
        "Start":  "\/Date(1500326100000)\/",
        "BackupSetId":  "ff160963-4a54-4592-a1a9-29000be31344",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126850000001930200001,
        "LastLSN":  17126850000003133300001,
        "CheckpointLSN":  17126850000002828400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500326400000)\/",
        "BackupFinishDate":  "\/Date(1500326400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c58d23e7-16a0-4240-82de-0724583a5791",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500326400000)\/",
        "Start":  "\/Date(1500326400000)\/",
        "BackupSetId":  "c58d23e7-16a0-4240-82de-0724583a5791",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126850000003133300001,
        "LastLSN":  17126850000004305200001,
        "CheckpointLSN":  17126850000002828400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500326700000)\/",
        "BackupFinishDate":  "\/Date(1500326700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "01871022-45b6-4945-8c65-5220e13dca9e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500326700000)\/",
        "Start":  "\/Date(1500326700000)\/",
        "BackupSetId":  "01871022-45b6-4945-8c65-5220e13dca9e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126850000004305200001,
        "LastLSN":  17126850000005491700001,
        "CheckpointLSN":  17126850000004977600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500327001000)\/",
        "BackupFinishDate":  "\/Date(1500327001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8fde00c7-3298-4a8d-9d66-369d149ed34a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500327001000)\/",
        "Start":  "\/Date(1500327001000)\/",
        "BackupSetId":  "8fde00c7-3298-4a8d-9d66-369d149ed34a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126850000005491700001,
        "LastLSN":  17126850000006512400001,
        "CheckpointLSN":  17126850000004977600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500327300000)\/",
        "BackupFinishDate":  "\/Date(1500327300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "494454d0-6a24-4d9e-827a-d203db649127",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500327300000)\/",
        "Start":  "\/Date(1500327300000)\/",
        "BackupSetId":  "494454d0-6a24-4d9e-827a-d203db649127",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126850000006512400001,
        "LastLSN":  17126850000007738800001,
        "CheckpointLSN":  17126850000007045000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500327600000)\/",
        "BackupFinishDate":  "\/Date(1500327600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4d5d4d27-b0fb-4a17-827c-1703d7204df1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500327600000)\/",
        "Start":  "\/Date(1500327600000)\/",
        "BackupSetId":  "4d5d4d27-b0fb-4a17-827c-1703d7204df1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126850000007738800001,
        "LastLSN":  17126850000008950100001,
        "CheckpointLSN":  17126850000007045000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500327900000)\/",
        "BackupFinishDate":  "\/Date(1500327900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "bcd56797-7262-4af2-8de4-1669d477779d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500327900000)\/",
        "Start":  "\/Date(1500327900000)\/",
        "BackupSetId":  "bcd56797-7262-4af2-8de4-1669d477779d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126850000008950100001,
        "LastLSN":  17126850000010154300001,
        "CheckpointLSN":  17126850000009120600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500328200000)\/",
        "BackupFinishDate":  "\/Date(1500328200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4f695068-8fc0-4fd1-848e-cf418d37399d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500328200000)\/",
        "Start":  "\/Date(1500328200000)\/",
        "BackupSetId":  "4f695068-8fc0-4fd1-848e-cf418d37399d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126850000010154300001,
        "LastLSN":  17126850000011364200001,
        "CheckpointLSN":  17126850000011212800011,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500328501000)\/",
        "BackupFinishDate":  "\/Date(1500328501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1fcef890-2bd1-4386-9723-f77416dbec03",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500328501000)\/",
        "Start":  "\/Date(1500328501000)\/",
        "BackupSetId":  "1fcef890-2bd1-4386-9723-f77416dbec03",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126850000011364200001,
        "LastLSN":  17126850000012543500001,
        "CheckpointLSN":  17126850000011212800011,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500328800000)\/",
        "BackupFinishDate":  "\/Date(1500328800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ac61e18d-a542-4307-85ff-b38580c277b1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500328800000)\/",
        "Start":  "\/Date(1500328800000)\/",
        "BackupSetId":  "ac61e18d-a542-4307-85ff-b38580c277b1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126850000012543500001,
        "LastLSN":  17126851000000649300001,
        "CheckpointLSN":  17126851000000277500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500329100000)\/",
        "BackupFinishDate":  "\/Date(1500329100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "233368ad-ea60-410f-8e59-94a33dfce4ea",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500329100000)\/",
        "Start":  "\/Date(1500329100000)\/",
        "BackupSetId":  "233368ad-ea60-410f-8e59-94a33dfce4ea",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126851000000649300001,
        "LastLSN":  17126851000001829800001,
        "CheckpointLSN":  17126851000000277500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500329400000)\/",
        "BackupFinishDate":  "\/Date(1500329400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "50c6c412-50e5-4d74-8762-175fafc34275",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500329400000)\/",
        "Start":  "\/Date(1500329400000)\/",
        "BackupSetId":  "50c6c412-50e5-4d74-8762-175fafc34275",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126851000001829800001,
        "LastLSN":  17126851000003040000001,
        "CheckpointLSN":  17126851000002352400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500329700000)\/",
        "BackupFinishDate":  "\/Date(1500329701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "66174307-c6e0-489d-a51c-f436da6efbec",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500329701000)\/",
        "Start":  "\/Date(1500329700000)\/",
        "BackupSetId":  "66174307-c6e0-489d-a51c-f436da6efbec",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126851000003040000001,
        "LastLSN":  17126851000004203300001,
        "CheckpointLSN":  17126851000002352400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500330001000)\/",
        "BackupFinishDate":  "\/Date(1500330001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "aab849b4-7c81-46e1-b656-454399fbb455",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500330001000)\/",
        "Start":  "\/Date(1500330001000)\/",
        "BackupSetId":  "aab849b4-7c81-46e1-b656-454399fbb455",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126851000004203300001,
        "LastLSN":  17126851000005393900001,
        "CheckpointLSN":  17126851000004420900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500330300000)\/",
        "BackupFinishDate":  "\/Date(1500330300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "175e729c-6f7b-4191-9ee3-28fdcd38117d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500330300000)\/",
        "Start":  "\/Date(1500330300000)\/",
        "BackupSetId":  "175e729c-6f7b-4191-9ee3-28fdcd38117d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126851000005393900001,
        "LastLSN":  17126851000006598600001,
        "CheckpointLSN":  17126851000006503100024,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500330600000)\/",
        "BackupFinishDate":  "\/Date(1500330600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f8f6fbd6-333b-4968-8537-94e90739c205",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500330600000)\/",
        "Start":  "\/Date(1500330600000)\/",
        "BackupSetId":  "f8f6fbd6-333b-4968-8537-94e90739c205",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126851000006598600001,
        "LastLSN":  17126851000007789800001,
        "CheckpointLSN":  17126851000006503100024,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500330900000)\/",
        "BackupFinishDate":  "\/Date(1500330900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "73b03202-3b87-4885-8f41-1b462555c27d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500330900000)\/",
        "Start":  "\/Date(1500330900000)\/",
        "BackupSetId":  "73b03202-3b87-4885-8f41-1b462555c27d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126851000007789800001,
        "LastLSN":  17126851000008973800001,
        "CheckpointLSN":  17126851000008641500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500331200000)\/",
        "BackupFinishDate":  "\/Date(1500331201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d9e44c8a-04f3-4621-b35a-e588724f6b5a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500331201000)\/",
        "Start":  "\/Date(1500331200000)\/",
        "BackupSetId":  "d9e44c8a-04f3-4621-b35a-e588724f6b5a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126851000008973800001,
        "LastLSN":  17126851000010154200001,
        "CheckpointLSN":  17126851000008641500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500331501000)\/",
        "BackupFinishDate":  "\/Date(1500331501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8d927337-da17-4698-9f34-e50c239de051",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500331501000)\/",
        "Start":  "\/Date(1500331501000)\/",
        "BackupSetId":  "8d927337-da17-4698-9f34-e50c239de051",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126851000010154200001,
        "LastLSN":  17126851000011348400001,
        "CheckpointLSN":  17126851000010711500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500331800000)\/",
        "BackupFinishDate":  "\/Date(1500331800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "de358622-cf2f-457c-bb04-2345008fb794",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500331800000)\/",
        "Start":  "\/Date(1500331800000)\/",
        "BackupSetId":  "de358622-cf2f-457c-bb04-2345008fb794",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126851000011348400001,
        "LastLSN":  17126851000012533800001,
        "CheckpointLSN":  17126851000010711500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500332100000)\/",
        "BackupFinishDate":  "\/Date(1500332100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1469aa5d-171c-4c53-aace-742c946a07b7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500332100000)\/",
        "Start":  "\/Date(1500332100000)\/",
        "BackupSetId":  "1469aa5d-171c-4c53-aace-742c946a07b7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126851000012533800001,
        "LastLSN":  17126852000000641200001,
        "CheckpointLSN":  17126851000012784400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500332401000)\/",
        "BackupFinishDate":  "\/Date(1500332402000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "af89f882-aad0-4eb3-b499-c07d73123c6a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500332402000)\/",
        "Start":  "\/Date(1500332401000)\/",
        "BackupSetId":  "af89f882-aad0-4eb3-b499-c07d73123c6a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126852000000641200001,
        "LastLSN":  17126857000002747100001,
        "CheckpointLSN":  17126857000001983800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500332701000)\/",
        "BackupFinishDate":  "\/Date(1500332702000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c2f4e6bd-ad6c-409a-979e-e0a52489ea0c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500332702000)\/",
        "Start":  "\/Date(1500332701000)\/",
        "BackupSetId":  "c2f4e6bd-ad6c-409a-979e-e0a52489ea0c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126857000002747100001,
        "LastLSN":  17126857000003926300001,
        "CheckpointLSN":  17126857000001983800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500333000000)\/",
        "BackupFinishDate":  "\/Date(1500333000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e15c039a-1f59-4f78-bde5-83fc16a8af6d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500333000000)\/",
        "Start":  "\/Date(1500333000000)\/",
        "BackupSetId":  "e15c039a-1f59-4f78-bde5-83fc16a8af6d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126857000003926300001,
        "LastLSN":  17126857000005115300001,
        "CheckpointLSN":  17126857000004067200066,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500333300000)\/",
        "BackupFinishDate":  "\/Date(1500333300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0598ddf9-dd4f-4539-94d4-c9bdeb4cd93a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500333300000)\/",
        "Start":  "\/Date(1500333300000)\/",
        "BackupSetId":  "0598ddf9-dd4f-4539-94d4-c9bdeb4cd93a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126857000005115300001,
        "LastLSN":  17126857000006319800001,
        "CheckpointLSN":  17126857000006215500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500333600000)\/",
        "BackupFinishDate":  "\/Date(1500333600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "51e0a0c5-0d35-42bd-acb8-6a943d58b0bc",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500333600000)\/",
        "Start":  "\/Date(1500333600000)\/",
        "BackupSetId":  "51e0a0c5-0d35-42bd-acb8-6a943d58b0bc",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126857000006319800001,
        "LastLSN":  17126857000007488300001,
        "CheckpointLSN":  17126857000006215500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500333900000)\/",
        "BackupFinishDate":  "\/Date(1500333901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "bc34d0cc-c331-4ecc-88af-8c7c5b979670",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500333901000)\/",
        "Start":  "\/Date(1500333900000)\/",
        "BackupSetId":  "bc34d0cc-c331-4ecc-88af-8c7c5b979670",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126857000007488300001,
        "LastLSN":  17126857000008671500001,
        "CheckpointLSN":  17126857000008304100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500334200000)\/",
        "BackupFinishDate":  "\/Date(1500334200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f2ea5b8e-9c98-47e2-ba07-93c426464cd4",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500334200000)\/",
        "Start":  "\/Date(1500334200000)\/",
        "BackupSetId":  "f2ea5b8e-9c98-47e2-ba07-93c426464cd4",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126857000008671500001,
        "LastLSN":  17126858000008379100001,
        "CheckpointLSN":  17126858000005205500004,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500334500000)\/",
        "BackupFinishDate":  "\/Date(1500334501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "58d046ce-5c9f-42f6-a77c-c7e4b8ae24e7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500334501000)\/",
        "Start":  "\/Date(1500334500000)\/",
        "BackupSetId":  "58d046ce-5c9f-42f6-a77c-c7e4b8ae24e7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126858000008379100001,
        "LastLSN":  17126859000010072600001,
        "CheckpointLSN":  17126859000007895800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500334800000)\/",
        "BackupFinishDate":  "\/Date(1500334801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0c832de2-04f9-4046-a7ca-c358009fc03f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500334801000)\/",
        "Start":  "\/Date(1500334800000)\/",
        "BackupSetId":  "0c832de2-04f9-4046-a7ca-c358009fc03f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126859000010072600001,
        "LastLSN":  17126860000012621900001,
        "CheckpointLSN":  17126860000010735200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500335100000)\/",
        "BackupFinishDate":  "\/Date(1500335101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "98cb76c1-c545-4637-b497-d8cce8e7eb7e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500335101000)\/",
        "Start":  "\/Date(1500335100000)\/",
        "BackupSetId":  "98cb76c1-c545-4637-b497-d8cce8e7eb7e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126860000012621900001,
        "LastLSN":  17126862000003493200001,
        "CheckpointLSN":  17126862000002389800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500335401000)\/",
        "BackupFinishDate":  "\/Date(1500335401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3c9a3df0-c605-461a-84ff-daabdaa1a17c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500335401000)\/",
        "Start":  "\/Date(1500335401000)\/",
        "BackupSetId":  "3c9a3df0-c605-461a-84ff-daabdaa1a17c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126862000003493200001,
        "LastLSN":  17126862000011152200001,
        "CheckpointLSN":  17126862000009251500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500335700000)\/",
        "BackupFinishDate":  "\/Date(1500335700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9c4d7227-db94-4dbf-b826-ee62412cfad9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500335700000)\/",
        "Start":  "\/Date(1500335700000)\/",
        "BackupSetId":  "9c4d7227-db94-4dbf-b826-ee62412cfad9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126862000011152200001,
        "LastLSN":  17126862000012370500001,
        "CheckpointLSN":  17126862000011349100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500336000000)\/",
        "BackupFinishDate":  "\/Date(1500336000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f1fb9ebe-e900-4901-9b58-a299eb10b9a1",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500336000000)\/",
        "Start":  "\/Date(1500336000000)\/",
        "BackupSetId":  "f1fb9ebe-e900-4901-9b58-a299eb10b9a1",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126862000012370500001,
        "LastLSN":  17126863000000509600001,
        "CheckpointLSN":  17126863000000330700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500336300000)\/",
        "BackupFinishDate":  "\/Date(1500336300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8ccfc393-c788-49d2-b8cc-243aff04d1e4",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500336300000)\/",
        "Start":  "\/Date(1500336300000)\/",
        "BackupSetId":  "8ccfc393-c788-49d2-b8cc-243aff04d1e4",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126863000000509600001,
        "LastLSN":  17126863000001691600001,
        "CheckpointLSN":  17126863000000330700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500336601000)\/",
        "BackupFinishDate":  "\/Date(1500336601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "29c4f820-2527-4a73-8d71-909f5c5402c4",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500336601000)\/",
        "Start":  "\/Date(1500336601000)\/",
        "BackupSetId":  "29c4f820-2527-4a73-8d71-909f5c5402c4",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126863000001691600001,
        "LastLSN":  17126863000002906300001,
        "CheckpointLSN":  17126863000002422500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500336900000)\/",
        "BackupFinishDate":  "\/Date(1500336900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e652a672-b83b-4f49-990e-89c15c239932",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500336900000)\/",
        "Start":  "\/Date(1500336900000)\/",
        "BackupSetId":  "e652a672-b83b-4f49-990e-89c15c239932",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126863000002906300001,
        "LastLSN":  17126863000004075700001,
        "CheckpointLSN":  17126863000002422500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500337200000)\/",
        "BackupFinishDate":  "\/Date(1500337200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "ef67e2da-9ca6-40ba-8d7c-aa88625cedbe",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500337200000)\/",
        "Start":  "\/Date(1500337200000)\/",
        "BackupSetId":  "ef67e2da-9ca6-40ba-8d7c-aa88625cedbe",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126863000004075700001,
        "LastLSN":  17126863000005292900001,
        "CheckpointLSN":  17126863000004492100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500337500000)\/",
        "BackupFinishDate":  "\/Date(1500337500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3be33864-6ec3-498a-b8b3-28dc4adc91e6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500337500000)\/",
        "Start":  "\/Date(1500337500000)\/",
        "BackupSetId":  "3be33864-6ec3-498a-b8b3-28dc4adc91e6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126863000005292900001,
        "LastLSN":  17126863000006480700001,
        "CheckpointLSN":  17126863000004492100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500337801000)\/",
        "BackupFinishDate":  "\/Date(1500337801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2a149c14-de99-49be-8a7c-4996688956f2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500337801000)\/",
        "Start":  "\/Date(1500337801000)\/",
        "BackupSetId":  "2a149c14-de99-49be-8a7c-4996688956f2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126863000006480700001,
        "LastLSN":  17126863000007698300001,
        "CheckpointLSN":  17126863000006573300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500338101000)\/",
        "BackupFinishDate":  "\/Date(1500338101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "eca4c3ba-d7c7-430b-8adb-42cd02464719",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500338101000)\/",
        "Start":  "\/Date(1500338101000)\/",
        "BackupSetId":  "eca4c3ba-d7c7-430b-8adb-42cd02464719",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126863000007698300001,
        "LastLSN":  17126863000008889900001,
        "CheckpointLSN":  17126863000008723500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500338400000)\/",
        "BackupFinishDate":  "\/Date(1500338400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7087f6ee-be57-4557-87ed-9342ff781c6c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500338400000)\/",
        "Start":  "\/Date(1500338400000)\/",
        "BackupSetId":  "7087f6ee-be57-4557-87ed-9342ff781c6c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126863000008889900001,
        "LastLSN":  17126863000010077100001,
        "CheckpointLSN":  17126863000008723500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500338700000)\/",
        "BackupFinishDate":  "\/Date(1500338700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d4a6ab8c-af48-46d3-a36c-9ec9a4dc2632",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500338700000)\/",
        "Start":  "\/Date(1500338700000)\/",
        "BackupSetId":  "d4a6ab8c-af48-46d3-a36c-9ec9a4dc2632",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126863000010077100001,
        "LastLSN":  17126863000011267100001,
        "CheckpointLSN":  17126863000010797700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500339000000)\/",
        "BackupFinishDate":  "\/Date(1500339000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b2cbac93-0cd3-43c5-a4fa-6bbeadf6a5c2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500339000000)\/",
        "Start":  "\/Date(1500339000000)\/",
        "BackupSetId":  "b2cbac93-0cd3-43c5-a4fa-6bbeadf6a5c2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126863000011267100001,
        "LastLSN":  17126863000012544900001,
        "CheckpointLSN":  17126863000010797700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500339300000)\/",
        "BackupFinishDate":  "\/Date(1500339300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a44c60cc-f541-423c-9eaf-7a3dd14bb93e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500339300000)\/",
        "Start":  "\/Date(1500339300000)\/",
        "BackupSetId":  "a44c60cc-f541-423c-9eaf-7a3dd14bb93e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126863000012544900001,
        "LastLSN":  17126864000000655200001,
        "CheckpointLSN":  17126863000012884500028,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500339601000)\/",
        "BackupFinishDate":  "\/Date(1500339601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1ad57804-d2ec-47b3-a2f9-c4499a955d23",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500339601000)\/",
        "Start":  "\/Date(1500339601000)\/",
        "BackupSetId":  "1ad57804-d2ec-47b3-a2f9-c4499a955d23",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126864000000655200001,
        "LastLSN":  17126864000001846500001,
        "CheckpointLSN":  17126864000000655200009,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500339900000)\/",
        "BackupFinishDate":  "\/Date(1500339900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a24fe4c3-6456-4e5b-ae58-248dea915e39",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500339900000)\/",
        "Start":  "\/Date(1500339900000)\/",
        "BackupSetId":  "a24fe4c3-6456-4e5b-ae58-248dea915e39",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126864000001846500001,
        "LastLSN":  17126864000003049600001,
        "CheckpointLSN":  17126864000002726800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500340200000)\/",
        "BackupFinishDate":  "\/Date(1500340200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e30b77a2-70fe-4d88-8b1b-0dbf3ae4e475",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500340200000)\/",
        "Start":  "\/Date(1500340200000)\/",
        "BackupSetId":  "e30b77a2-70fe-4d88-8b1b-0dbf3ae4e475",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126864000003049600001,
        "LastLSN":  17126864000004218700001,
        "CheckpointLSN":  17126864000002726800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500340500000)\/",
        "BackupFinishDate":  "\/Date(1500340500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fc24e571-be90-4263-838f-85bf972b8d22",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500340500000)\/",
        "Start":  "\/Date(1500340500000)\/",
        "BackupSetId":  "fc24e571-be90-4263-838f-85bf972b8d22",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126864000004218700001,
        "LastLSN":  17126864000005422300001,
        "CheckpointLSN":  17126864000004803300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500340800000)\/",
        "BackupFinishDate":  "\/Date(1500340800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9b22102e-1350-435c-9f74-1fc19d368df9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500340800000)\/",
        "Start":  "\/Date(1500340800000)\/",
        "BackupSetId":  "9b22102e-1350-435c-9f74-1fc19d368df9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126864000005422300001,
        "LastLSN":  17126864000006589100001,
        "CheckpointLSN":  17126864000004803300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500341100000)\/",
        "BackupFinishDate":  "\/Date(1500341100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "96a35891-7e94-4ff2-8af7-8e885c9f261e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500341100000)\/",
        "Start":  "\/Date(1500341100000)\/",
        "BackupSetId":  "96a35891-7e94-4ff2-8af7-8e885c9f261e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126864000006589100001,
        "LastLSN":  17126864000007781100001,
        "CheckpointLSN":  17126864000006887400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500341400000)\/",
        "BackupFinishDate":  "\/Date(1500341400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b34572b0-73d3-4124-afbb-5322ed17bd3a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500341400000)\/",
        "Start":  "\/Date(1500341400000)\/",
        "BackupSetId":  "b34572b0-73d3-4124-afbb-5322ed17bd3a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126864000007781100001,
        "LastLSN":  17126864000008973500001,
        "CheckpointLSN":  17126864000006887400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500341700000)\/",
        "BackupFinishDate":  "\/Date(1500341700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e449eb09-2cab-4f1e-b6d8-9c6322870bab",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500341700000)\/",
        "Start":  "\/Date(1500341700000)\/",
        "BackupSetId":  "e449eb09-2cab-4f1e-b6d8-9c6322870bab",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126864000008973500001,
        "LastLSN":  17126864000010200800001,
        "CheckpointLSN":  17126864000009050500020,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500342000000)\/",
        "BackupFinishDate":  "\/Date(1500342000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "36105646-8ba9-4b82-becd-8fcc1101ec8a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500342000000)\/",
        "Start":  "\/Date(1500342000000)\/",
        "BackupSetId":  "36105646-8ba9-4b82-becd-8fcc1101ec8a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126864000010200800001,
        "LastLSN":  17126864000011430900001,
        "CheckpointLSN":  17126864000011227000005,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500342301000)\/",
        "BackupFinishDate":  "\/Date(1500342301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6ba1247f-8e10-46cd-957f-ebb480a15fc3",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500342301000)\/",
        "Start":  "\/Date(1500342301000)\/",
        "BackupSetId":  "6ba1247f-8e10-46cd-957f-ebb480a15fc3",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126864000011430900001,
        "LastLSN":  17126864000012614000001,
        "CheckpointLSN":  17126864000011227000005,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500342600000)\/",
        "BackupFinishDate":  "\/Date(1500342600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "41f089c8-fedf-4e7d-baef-e1e1a58726f9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500342600000)\/",
        "Start":  "\/Date(1500342600000)\/",
        "BackupSetId":  "41f089c8-fedf-4e7d-baef-e1e1a58726f9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126864000012614000001,
        "LastLSN":  17126867000000713400001,
        "CheckpointLSN":  17126867000000098800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500342900000)\/",
        "BackupFinishDate":  "\/Date(1500342900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "79f62d1f-9cd4-4183-a3dd-8254c5214d78",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500342900000)\/",
        "Start":  "\/Date(1500342900000)\/",
        "BackupSetId":  "79f62d1f-9cd4-4183-a3dd-8254c5214d78",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126867000000713400001,
        "LastLSN":  17126867000001889200001,
        "CheckpointLSN":  17126867000000098800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500343201000)\/",
        "BackupFinishDate":  "\/Date(1500343201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b943443a-42a9-4937-9912-99245a310664",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500343201000)\/",
        "Start":  "\/Date(1500343201000)\/",
        "BackupSetId":  "b943443a-42a9-4937-9912-99245a310664",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126867000001889200001,
        "LastLSN":  17126867000003104000001,
        "CheckpointLSN":  17126867000002188300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500343500000)\/",
        "BackupFinishDate":  "\/Date(1500343501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c3aa0dd8-8aae-4eb1-af0c-94e39c255562",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500343501000)\/",
        "Start":  "\/Date(1500343500000)\/",
        "BackupSetId":  "c3aa0dd8-8aae-4eb1-af0c-94e39c255562",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126867000003104000001,
        "LastLSN":  17126867000004282600001,
        "CheckpointLSN":  17126867000002188300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500343800000)\/",
        "BackupFinishDate":  "\/Date(1500343800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7a13c982-895b-4401-b26c-5dcbfdea3c1d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500343800000)\/",
        "Start":  "\/Date(1500343800000)\/",
        "BackupSetId":  "7a13c982-895b-4401-b26c-5dcbfdea3c1d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126867000004282600001,
        "LastLSN":  17126867000005502800001,
        "CheckpointLSN":  17126867000004350600024,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500344100000)\/",
        "BackupFinishDate":  "\/Date(1500344100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "49a58acc-e8ea-432d-acce-b847e8b16158",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500344100000)\/",
        "Start":  "\/Date(1500344100000)\/",
        "BackupSetId":  "49a58acc-e8ea-432d-acce-b847e8b16158",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126867000005502800001,
        "LastLSN":  17126867000006690100001,
        "CheckpointLSN":  17126867000006454800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500344400000)\/",
        "BackupFinishDate":  "\/Date(1500344400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5de40e0d-bd3c-49e7-908e-ec56a494ab23",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500344400000)\/",
        "Start":  "\/Date(1500344400000)\/",
        "BackupSetId":  "5de40e0d-bd3c-49e7-908e-ec56a494ab23",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126867000006690100001,
        "LastLSN":  17126867000007885200001,
        "CheckpointLSN":  17126867000006454800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500344700000)\/",
        "BackupFinishDate":  "\/Date(1500344700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "89629b9d-6f2c-4011-a293-ac3f2cefb6db",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500344700000)\/",
        "Start":  "\/Date(1500344700000)\/",
        "BackupSetId":  "89629b9d-6f2c-4011-a293-ac3f2cefb6db",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126867000007885200001,
        "LastLSN":  17126867000009084700001,
        "CheckpointLSN":  17126867000008547000021,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500345001000)\/",
        "BackupFinishDate":  "\/Date(1500345001000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "bccb7d12-c500-44cc-b12c-25194548c61c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500345001000)\/",
        "Start":  "\/Date(1500345001000)\/",
        "BackupSetId":  "bccb7d12-c500-44cc-b12c-25194548c61c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126867000009084700001,
        "LastLSN":  17126867000010273700001,
        "CheckpointLSN":  17126867000008547000021,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500345300000)\/",
        "BackupFinishDate":  "\/Date(1500345300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e184a9a1-dbab-4aed-99e7-45cae7ebed13",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500345300000)\/",
        "Start":  "\/Date(1500345300000)\/",
        "BackupSetId":  "e184a9a1-dbab-4aed-99e7-45cae7ebed13",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126867000010273700001,
        "LastLSN":  17126867000011463700001,
        "CheckpointLSN":  17126867000010691600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500345600000)\/",
        "BackupFinishDate":  "\/Date(1500345600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d99cbd0b-58c0-41db-9e4a-010016e85a9a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500345600000)\/",
        "Start":  "\/Date(1500345600000)\/",
        "BackupSetId":  "d99cbd0b-58c0-41db-9e4a-010016e85a9a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126867000011463700001,
        "LastLSN":  17126867000012644400001,
        "CheckpointLSN":  17126867000010691600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500345900000)\/",
        "BackupFinishDate":  "\/Date(1500345900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "35fe03ee-79d2-4218-aebc-bf98d15aef3d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500345900000)\/",
        "Start":  "\/Date(1500345900000)\/",
        "BackupSetId":  "35fe03ee-79d2-4218-aebc-bf98d15aef3d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126867000012644400001,
        "LastLSN":  17126868000000726300001,
        "CheckpointLSN":  17126867000012775000040,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500346200000)\/",
        "BackupFinishDate":  "\/Date(1500346201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3678a4dd-701d-4b11-ab67-383a270b2812",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500346201000)\/",
        "Start":  "\/Date(1500346200000)\/",
        "BackupSetId":  "3678a4dd-701d-4b11-ab67-383a270b2812",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126868000000726300001,
        "LastLSN":  17126868000002023300001,
        "CheckpointLSN":  17126868000000726300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500346500000)\/",
        "BackupFinishDate":  "\/Date(1500346500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "19afde50-c33e-4dd3-aacc-bfc0628e2d7f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500346500000)\/",
        "Start":  "\/Date(1500346500000)\/",
        "BackupSetId":  "19afde50-c33e-4dd3-aacc-bfc0628e2d7f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126868000002023300001,
        "LastLSN":  17126868000003242200001,
        "CheckpointLSN":  17126868000002794600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500346800000)\/",
        "BackupFinishDate":  "\/Date(1500346800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4e530977-6466-401c-9e7a-fc0f47dc4315",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500346800000)\/",
        "Start":  "\/Date(1500346800000)\/",
        "BackupSetId":  "4e530977-6466-401c-9e7a-fc0f47dc4315",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126868000003242200001,
        "LastLSN":  17126868000004413900001,
        "CheckpointLSN":  17126868000002794600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500347100000)\/",
        "BackupFinishDate":  "\/Date(1500347100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b3b88b7f-ff4d-4f98-8a2f-88adefcff1cf",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500347100000)\/",
        "Start":  "\/Date(1500347100000)\/",
        "BackupSetId":  "b3b88b7f-ff4d-4f98-8a2f-88adefcff1cf",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126868000004413900001,
        "LastLSN":  17126868000005617400001,
        "CheckpointLSN":  17126868000004873300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500347401000)\/",
        "BackupFinishDate":  "\/Date(1500347401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6b3ebbfc-f6a0-41c7-9db6-558b1fc0d5e2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500347401000)\/",
        "Start":  "\/Date(1500347401000)\/",
        "BackupSetId":  "6b3ebbfc-f6a0-41c7-9db6-558b1fc0d5e2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126868000005617400001,
        "LastLSN":  17126868000006786600001,
        "CheckpointLSN":  17126868000004873300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500347700000)\/",
        "BackupFinishDate":  "\/Date(1500347700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "47784e33-7b71-4059-856b-fc432fc72c29",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500347700000)\/",
        "Start":  "\/Date(1500347700000)\/",
        "BackupSetId":  "47784e33-7b71-4059-856b-fc432fc72c29",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126868000006786600001,
        "LastLSN":  17126868000007988200001,
        "CheckpointLSN":  17126868000006955900010,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500348000000)\/",
        "BackupFinishDate":  "\/Date(1500348000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "860b3d75-dfd3-4fe1-af80-aea949d455b0",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500348000000)\/",
        "Start":  "\/Date(1500348000000)\/",
        "BackupSetId":  "860b3d75-dfd3-4fe1-af80-aea949d455b0",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126868000007988200001,
        "LastLSN":  17126868000009173200001,
        "CheckpointLSN":  17126868000009048300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500348300000)\/",
        "BackupFinishDate":  "\/Date(1500348300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3c31755b-b5db-42a0-9a59-04edbcd53319",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500348300000)\/",
        "Start":  "\/Date(1500348300000)\/",
        "BackupSetId":  "3c31755b-b5db-42a0-9a59-04edbcd53319",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126868000009173200001,
        "LastLSN":  17126868000010337800001,
        "CheckpointLSN":  17126868000009048300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500348600000)\/",
        "BackupFinishDate":  "\/Date(1500348600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3e44e943-afab-4a2b-ad12-10c6783f0cc9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500348600000)\/",
        "Start":  "\/Date(1500348600000)\/",
        "BackupSetId":  "3e44e943-afab-4a2b-ad12-10c6783f0cc9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126868000010337800001,
        "LastLSN":  17126868000011550300001,
        "CheckpointLSN":  17126868000011117400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500348900000)\/",
        "BackupFinishDate":  "\/Date(1500348900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f8868efd-a0f7-4b48-8b16-60de3b2804e7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500348900000)\/",
        "Start":  "\/Date(1500348900000)\/",
        "BackupSetId":  "f8868efd-a0f7-4b48-8b16-60de3b2804e7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126868000011550300001,
        "LastLSN":  17126868000012758900001,
        "CheckpointLSN":  17126868000011117400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500349200000)\/",
        "BackupFinishDate":  "\/Date(1500349200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "bf503bf6-ad63-47a6-8b42-f1a095321afd",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500349200000)\/",
        "Start":  "\/Date(1500349200000)\/",
        "BackupSetId":  "bf503bf6-ad63-47a6-8b42-f1a095321afd",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126868000012758900001,
        "LastLSN":  17126869000000879700001,
        "CheckpointLSN":  17126869000000082400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500349500000)\/",
        "BackupFinishDate":  "\/Date(1500349501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "cf5ae0df-0d4c-44eb-a539-37d94e3d2dfa",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500349501000)\/",
        "Start":  "\/Date(1500349500000)\/",
        "BackupSetId":  "cf5ae0df-0d4c-44eb-a539-37d94e3d2dfa",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126869000000879700001,
        "LastLSN":  17126869000002062500001,
        "CheckpointLSN":  17126869000000082400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500349800000)\/",
        "BackupFinishDate":  "\/Date(1500349800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "85fb8cc9-67bf-4e02-93bd-f3145d2791a2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500349800000)\/",
        "Start":  "\/Date(1500349800000)\/",
        "BackupSetId":  "85fb8cc9-67bf-4e02-93bd-f3145d2791a2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126869000002062500001,
        "LastLSN":  17126869000003371900001,
        "CheckpointLSN":  17126869000002189000047,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500350100000)\/",
        "BackupFinishDate":  "\/Date(1500350100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "1eaa3707-5190-4a6b-a4a0-852c729f5cdb",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500350100000)\/",
        "Start":  "\/Date(1500350100000)\/",
        "BackupSetId":  "1eaa3707-5190-4a6b-a4a0-852c729f5cdb",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126869000003371900001,
        "LastLSN":  17126869000004574900001,
        "CheckpointLSN":  17126869000004337800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500350400000)\/",
        "BackupFinishDate":  "\/Date(1500350401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a255ebbd-09ec-4806-818e-b84bbc0c18b7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500350401000)\/",
        "Start":  "\/Date(1500350400000)\/",
        "BackupSetId":  "a255ebbd-09ec-4806-818e-b84bbc0c18b7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126869000004574900001,
        "LastLSN":  17126869000005773100001,
        "CheckpointLSN":  17126869000004337800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500350701000)\/",
        "BackupFinishDate":  "\/Date(1500350701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e1c1e30f-52a8-4877-a1ff-60816700c5fc",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500350701000)\/",
        "Start":  "\/Date(1500350701000)\/",
        "BackupSetId":  "e1c1e30f-52a8-4877-a1ff-60816700c5fc",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126869000005773100001,
        "LastLSN":  17126869000006971800001,
        "CheckpointLSN":  17126869000006420400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500351000000)\/",
        "BackupFinishDate":  "\/Date(1500351000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b9560ac4-2134-4a00-9eca-dd778767f7af",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500351000000)\/",
        "Start":  "\/Date(1500351000000)\/",
        "BackupSetId":  "b9560ac4-2134-4a00-9eca-dd778767f7af",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126869000006971800001,
        "LastLSN":  17126869000008166900001,
        "CheckpointLSN":  17126869000006420400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500351300000)\/",
        "BackupFinishDate":  "\/Date(1500351300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "342e88ba-1adc-441e-bb96-2f59e38ce662",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500351300000)\/",
        "Start":  "\/Date(1500351300000)\/",
        "BackupSetId":  "342e88ba-1adc-441e-bb96-2f59e38ce662",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126869000008166900001,
        "LastLSN":  17126869000009356400001,
        "CheckpointLSN":  17126869000008509600040,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500351600000)\/",
        "BackupFinishDate":  "\/Date(1500351600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "83468b03-134f-42f8-8f6a-285265889405",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500351600000)\/",
        "Start":  "\/Date(1500351600000)\/",
        "BackupSetId":  "83468b03-134f-42f8-8f6a-285265889405",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126869000009356400001,
        "LastLSN":  17126869000010547700001,
        "CheckpointLSN":  17126869000008509600040,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500351900000)\/",
        "BackupFinishDate":  "\/Date(1500351901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "de9f7e2c-f476-4581-89dd-35ca65e2f0d6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500351901000)\/",
        "Start":  "\/Date(1500351900000)\/",
        "BackupSetId":  "de9f7e2c-f476-4581-89dd-35ca65e2f0d6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126869000010547700001,
        "LastLSN":  17126869000011751200001,
        "CheckpointLSN":  17126869000010662900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500352200000)\/",
        "BackupFinishDate":  "\/Date(1500352200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "163d3d1b-759a-4089-b4c5-7cfc723d62cf",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500352200000)\/",
        "Start":  "\/Date(1500352200000)\/",
        "BackupSetId":  "163d3d1b-759a-4089-b4c5-7cfc723d62cf",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126869000011751200001,
        "LastLSN":  17126869000012968700001,
        "CheckpointLSN":  17126869000012816200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500352500000)\/",
        "BackupFinishDate":  "\/Date(1500352500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d48b036d-1a8f-40d5-a3e1-0b2eea786fa9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500352500000)\/",
        "Start":  "\/Date(1500352500000)\/",
        "BackupSetId":  "d48b036d-1a8f-40d5-a3e1-0b2eea786fa9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126869000012968700001,
        "LastLSN":  17126870000001031900001,
        "CheckpointLSN":  17126869000012816200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500352800000)\/",
        "BackupFinishDate":  "\/Date(1500352800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "cfabf3a4-b3e2-47d4-89cb-471894040193",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500352800000)\/",
        "Start":  "\/Date(1500352800000)\/",
        "BackupSetId":  "cfabf3a4-b3e2-47d4-89cb-471894040193",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126870000001031900001,
        "LastLSN":  17126870000002234600001,
        "CheckpointLSN":  17126870000001032000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500353100000)\/",
        "BackupFinishDate":  "\/Date(1500353100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5cb1d297-4d00-4eda-956a-50848b45c03d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500353100000)\/",
        "Start":  "\/Date(1500353100000)\/",
        "BackupSetId":  "5cb1d297-4d00-4eda-956a-50848b45c03d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126870000002234600001,
        "LastLSN":  17126870000003421400001,
        "CheckpointLSN":  17126870000003105800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500353400000)\/",
        "BackupFinishDate":  "\/Date(1500353400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4b46e1a2-34bc-41fd-ab97-4fdfc3f64cf7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500353400000)\/",
        "Start":  "\/Date(1500353400000)\/",
        "BackupSetId":  "4b46e1a2-34bc-41fd-ab97-4fdfc3f64cf7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126870000003421400001,
        "LastLSN":  17126870000004698000001,
        "CheckpointLSN":  17126870000003105800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500353700000)\/",
        "BackupFinishDate":  "\/Date(1500353700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fb6d2b23-588c-4cea-bf1e-df8575114efc",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500353700000)\/",
        "Start":  "\/Date(1500353700000)\/",
        "BackupSetId":  "fb6d2b23-588c-4cea-bf1e-df8575114efc",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126870000004698000001,
        "LastLSN":  17126870000005917600001,
        "CheckpointLSN":  17126870000005176800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500354000000)\/",
        "BackupFinishDate":  "\/Date(1500354000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c5552d0a-cf67-41fa-9c26-549bdb46a5db",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500354000000)\/",
        "Start":  "\/Date(1500354000000)\/",
        "BackupSetId":  "c5552d0a-cf67-41fa-9c26-549bdb46a5db",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126870000005917600001,
        "LastLSN":  17126870000008737700001,
        "CheckpointLSN":  17126870000007248700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500354300000)\/",
        "BackupFinishDate":  "\/Date(1500354301000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7905f569-c829-4a1b-bb05-b5bb88d2444c",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500354301000)\/",
        "Start":  "\/Date(1500354300000)\/",
        "BackupSetId":  "7905f569-c829-4a1b-bb05-b5bb88d2444c",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126870000008737700001,
        "LastLSN":  17126870000009951200001,
        "CheckpointLSN":  17126870000009344300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500354600000)\/",
        "BackupFinishDate":  "\/Date(1500354600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6970a96e-55df-4eb6-9f06-ffd99f04f658",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500354600000)\/",
        "Start":  "\/Date(1500354600000)\/",
        "BackupSetId":  "6970a96e-55df-4eb6-9f06-ffd99f04f658",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126870000009951200001,
        "LastLSN":  17126870000011117600001,
        "CheckpointLSN":  17126870000009344300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500354900000)\/",
        "BackupFinishDate":  "\/Date(1500354900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7da6973d-e1fc-438b-a714-7a5e0619d39a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500354900000)\/",
        "Start":  "\/Date(1500354900000)\/",
        "BackupSetId":  "7da6973d-e1fc-438b-a714-7a5e0619d39a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126870000011117600001,
        "LastLSN":  17126870000012318700001,
        "CheckpointLSN":  17126870000011440200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500355200000)\/",
        "BackupFinishDate":  "\/Date(1500355200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2f014732-38b2-4310-a4d7-0fb1539f4c10",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500355200000)\/",
        "Start":  "\/Date(1500355200000)\/",
        "BackupSetId":  "2f014732-38b2-4310-a4d7-0fb1539f4c10",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126870000012318700001,
        "LastLSN":  17126871000000378200001,
        "CheckpointLSN":  17126870000011440200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500355500000)\/",
        "BackupFinishDate":  "\/Date(1500355500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "61eb6e2e-6097-4464-b875-0944a96b30ed",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500355500000)\/",
        "Start":  "\/Date(1500355500000)\/",
        "BackupSetId":  "61eb6e2e-6097-4464-b875-0944a96b30ed",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126871000000378200001,
        "LastLSN":  17126871000001565900001,
        "CheckpointLSN":  17126871000000378200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500355800000)\/",
        "BackupFinishDate":  "\/Date(1500355800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d892fcb1-6ac9-460a-bf62-c678dcc09d88",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500355800000)\/",
        "Start":  "\/Date(1500355800000)\/",
        "BackupSetId":  "d892fcb1-6ac9-460a-bf62-c678dcc09d88",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126871000001565900001,
        "LastLSN":  17126871000002785100001,
        "CheckpointLSN":  17126871000002448200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500356100000)\/",
        "BackupFinishDate":  "\/Date(1500356100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7891ce32-3ec6-4cfe-8445-7ecfc9258c51",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500356100000)\/",
        "Start":  "\/Date(1500356100000)\/",
        "BackupSetId":  "7891ce32-3ec6-4cfe-8445-7ecfc9258c51",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126871000002785100001,
        "LastLSN":  17126871000004028400001,
        "CheckpointLSN":  17126871000002448200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500356400000)\/",
        "BackupFinishDate":  "\/Date(1500356400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "81913f11-47a9-4d43-8c85-6b9c230033be",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500356400000)\/",
        "Start":  "\/Date(1500356400000)\/",
        "BackupSetId":  "81913f11-47a9-4d43-8c85-6b9c230033be",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126871000004028400001,
        "LastLSN":  17126871000005205200001,
        "CheckpointLSN":  17126871000004517700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500356700000)\/",
        "BackupFinishDate":  "\/Date(1500356700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "b7a68861-6714-42ef-9f26-75dbc0dc14db",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500356700000)\/",
        "Start":  "\/Date(1500356700000)\/",
        "BackupSetId":  "b7a68861-6714-42ef-9f26-75dbc0dc14db",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126871000005205200001,
        "LastLSN":  17126871000009851900001,
        "CheckpointLSN":  17126871000009332000013,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500357000000)\/",
        "BackupFinishDate":  "\/Date(1500357000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "36b71b5b-b2d6-4258-b464-586342d3a817",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500357000000)\/",
        "Start":  "\/Date(1500357000000)\/",
        "BackupSetId":  "36b71b5b-b2d6-4258-b464-586342d3a817",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126871000009851900001,
        "LastLSN":  17126871000011000900001,
        "CheckpointLSN":  17126871000009332000013,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500357300000)\/",
        "BackupFinishDate":  "\/Date(1500357300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "93bfd71b-f199-432b-8286-f0c9f524d4b7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500357300000)\/",
        "Start":  "\/Date(1500357300000)\/",
        "BackupSetId":  "93bfd71b-f199-432b-8286-f0c9f524d4b7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126871000011000900001,
        "LastLSN":  17126871000012156700001,
        "CheckpointLSN":  17126871000011428900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500357600000)\/",
        "BackupFinishDate":  "\/Date(1500357600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0afb9ee4-a4c9-48ef-abb8-2ddb631653d6",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500357600000)\/",
        "Start":  "\/Date(1500357600000)\/",
        "BackupSetId":  "0afb9ee4-a4c9-48ef-abb8-2ddb631653d6",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126871000012156700001,
        "LastLSN":  17126872000000196900001,
        "CheckpointLSN":  17126871000011428900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500357900000)\/",
        "BackupFinishDate":  "\/Date(1500357901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2c6f565a-43a8-4bb1-aae6-13a2c1b9e9ba",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500357901000)\/",
        "Start":  "\/Date(1500357900000)\/",
        "BackupSetId":  "2c6f565a-43a8-4bb1-aae6-13a2c1b9e9ba",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126872000000196900001,
        "LastLSN":  17126872000001348300001,
        "CheckpointLSN":  17126872000000196900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500358200000)\/",
        "BackupFinishDate":  "\/Date(1500358200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3c020dfe-48b3-4ad9-93e3-036fdb220cd5",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500358200000)\/",
        "Start":  "\/Date(1500358200000)\/",
        "BackupSetId":  "3c020dfe-48b3-4ad9-93e3-036fdb220cd5",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126872000001348300001,
        "LastLSN":  17126872000002508300001,
        "CheckpointLSN":  17126872000002263900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500358500000)\/",
        "BackupFinishDate":  "\/Date(1500358500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "155b3751-4464-48a9-a68c-5b3fc9fad130",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500358500000)\/",
        "Start":  "\/Date(1500358500000)\/",
        "BackupSetId":  "155b3751-4464-48a9-a68c-5b3fc9fad130",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126872000002508300001,
        "LastLSN":  17126872000004522900001,
        "CheckpointLSN":  17126872000004332000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500358800000)\/",
        "BackupFinishDate":  "\/Date(1500358800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "7c3de697-0a5e-4be4-b4e4-24a548627079",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500358800000)\/",
        "Start":  "\/Date(1500358800000)\/",
        "BackupSetId":  "7c3de697-0a5e-4be4-b4e4-24a548627079",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126872000004522900001,
        "LastLSN":  17126872000009167800001,
        "CheckpointLSN":  17126872000008513600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500359101000)\/",
        "BackupFinishDate":  "\/Date(1500359101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "9e432f12-bbf9-46d1-b629-ae66adec9350",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500359101000)\/",
        "Start":  "\/Date(1500359101000)\/",
        "BackupSetId":  "9e432f12-bbf9-46d1-b629-ae66adec9350",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126872000009167800001,
        "LastLSN":  17126872000010465000001,
        "CheckpointLSN":  17126872000008513600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500359400000)\/",
        "BackupFinishDate":  "\/Date(1500359400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f726fcb8-d701-44cc-9553-27124768e950",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500359400000)\/",
        "Start":  "\/Date(1500359400000)\/",
        "BackupSetId":  "f726fcb8-d701-44cc-9553-27124768e950",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126872000010465000001,
        "LastLSN":  17126872000011866800001,
        "CheckpointLSN":  17126872000010599800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500359700000)\/",
        "BackupFinishDate":  "\/Date(1500359700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a9bbbf73-b3b0-4bb3-9aa9-7088f48502e7",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500359700000)\/",
        "Start":  "\/Date(1500359700000)\/",
        "BackupSetId":  "a9bbbf73-b3b0-4bb3-9aa9-7088f48502e7",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126872000011866800001,
        "LastLSN":  17126873000002164700001,
        "CheckpointLSN":  17126873000001757000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500360000000)\/",
        "BackupFinishDate":  "\/Date(1500360000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fac68e59-74ab-42da-84f9-c4d413d5154b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500360000000)\/",
        "Start":  "\/Date(1500360000000)\/",
        "BackupSetId":  "fac68e59-74ab-42da-84f9-c4d413d5154b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126873000002164700001,
        "LastLSN":  17126873000007518500001,
        "CheckpointLSN":  17126873000006352900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500360300000)\/",
        "BackupFinishDate":  "\/Date(1500360300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6afc679a-7823-4d0a-bd6f-175ffd70634f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500360300000)\/",
        "Start":  "\/Date(1500360300000)\/",
        "BackupSetId":  "6afc679a-7823-4d0a-bd6f-175ffd70634f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126873000007518500001,
        "LastLSN":  17126873000009190200001,
        "CheckpointLSN":  17126873000008427500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500360600000)\/",
        "BackupFinishDate":  "\/Date(1500360601000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "3477008d-d3bf-4f9e-a925-4cbb5bae4e93",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500360601000)\/",
        "Start":  "\/Date(1500360600000)\/",
        "BackupSetId":  "3477008d-d3bf-4f9e-a925-4cbb5bae4e93",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126873000009190200001,
        "LastLSN":  17126873000010665000001,
        "CheckpointLSN":  17126873000010499300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500360901000)\/",
        "BackupFinishDate":  "\/Date(1500360901000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "dc2b3a02-3b5d-4539-ad47-2f354aeff254",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500360901000)\/",
        "Start":  "\/Date(1500360901000)\/",
        "BackupSetId":  "dc2b3a02-3b5d-4539-ad47-2f354aeff254",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126873000010665000001,
        "LastLSN":  17126874000006566100001,
        "CheckpointLSN":  17126874000006039300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500361200000)\/",
        "BackupFinishDate":  "\/Date(1500361200000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "c8bdc950-3c4b-4485-9aa3-78c485c1c395",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500361200000)\/",
        "Start":  "\/Date(1500361200000)\/",
        "BackupSetId":  "c8bdc950-3c4b-4485-9aa3-78c485c1c395",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126874000006566100001,
        "LastLSN":  17126875000009651500001,
        "CheckpointLSN":  17126875000009531200001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500361500000)\/",
        "BackupFinishDate":  "\/Date(1500361500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8b79613d-1da1-4ebf-88c1-8f8fa1c5860a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500361500000)\/",
        "Start":  "\/Date(1500361500000)\/",
        "BackupSetId":  "8b79613d-1da1-4ebf-88c1-8f8fa1c5860a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126875000009651500001,
        "LastLSN":  17126877000001129500001,
        "CheckpointLSN":  17126877000001079100005,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500361800000)\/",
        "BackupFinishDate":  "\/Date(1500361801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "4e2b4164-ede3-43e0-a855-a7be9910381f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500361801000)\/",
        "Start":  "\/Date(1500361800000)\/",
        "BackupSetId":  "4e2b4164-ede3-43e0-a855-a7be9910381f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126877000001129500001,
        "LastLSN":  17126878000001254100001,
        "CheckpointLSN":  17126877000012531300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500362100000)\/",
        "BackupFinishDate":  "\/Date(1500362101000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "055e2ecb-e076-4934-a764-ee949f1e8331",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500362101000)\/",
        "Start":  "\/Date(1500362100000)\/",
        "BackupSetId":  "055e2ecb-e076-4934-a764-ee949f1e8331",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126878000001254100001,
        "LastLSN":  17126878000008324500001,
        "CheckpointLSN":  17126878000007543800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500362401000)\/",
        "BackupFinishDate":  "\/Date(1500362401000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f96d0ae7-9c0d-4ecc-b8c1-de9a4589f9fd",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500362401000)\/",
        "Start":  "\/Date(1500362401000)\/",
        "BackupSetId":  "f96d0ae7-9c0d-4ecc-b8c1-de9a4589f9fd",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126878000008324500001,
        "LastLSN":  17126879000005426500001,
        "CheckpointLSN":  17126879000003856700001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500362701000)\/",
        "BackupFinishDate":  "\/Date(1500362701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "eb1957b8-37e7-4431-9dce-7a5cffa5cf6b",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500362701000)\/",
        "Start":  "\/Date(1500362701000)\/",
        "BackupSetId":  "eb1957b8-37e7-4431-9dce-7a5cffa5cf6b",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126879000005426500001,
        "LastLSN":  17126880000000251900001,
        "CheckpointLSN":  17126879000012839400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500363000000)\/",
        "BackupFinishDate":  "\/Date(1500363000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "af1bb19a-cafd-4da3-900c-3d289a5e849a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500363000000)\/",
        "Start":  "\/Date(1500363000000)\/",
        "BackupSetId":  "af1bb19a-cafd-4da3-900c-3d289a5e849a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126880000000251900001,
        "LastLSN":  17126880000004591500001,
        "CheckpointLSN":  17126880000004435100044,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500363300000)\/",
        "BackupFinishDate":  "\/Date(1500363300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "bef5fe43-4327-4f1e-8e44-68e6a48e368a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500363300000)\/",
        "Start":  "\/Date(1500363300000)\/",
        "BackupSetId":  "bef5fe43-4327-4f1e-8e44-68e6a48e368a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126880000004591500001,
        "LastLSN":  17126880000010221200001,
        "CheckpointLSN":  17126880000008836100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500363600000)\/",
        "BackupFinishDate":  "\/Date(1500363600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d150f208-c47f-426b-8bf8-87f3a009cd2d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500363600000)\/",
        "Start":  "\/Date(1500363600000)\/",
        "BackupSetId":  "d150f208-c47f-426b-8bf8-87f3a009cd2d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126880000010221200001,
        "LastLSN":  17126881000003709800001,
        "CheckpointLSN":  17126881000003016100026,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500363900000)\/",
        "BackupFinishDate":  "\/Date(1500363900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "d17e3f4c-a012-4e97-ac76-e0021a8c9926",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500363900000)\/",
        "Start":  "\/Date(1500363900000)\/",
        "BackupSetId":  "d17e3f4c-a012-4e97-ac76-e0021a8c9926",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126881000003709800001,
        "LastLSN":  17126882000002880400001,
        "CheckpointLSN":  17126882000001158100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500364200000)\/",
        "BackupFinishDate":  "\/Date(1500364201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "aa55f2e1-2945-48b9-a7fb-ae9225b21c74",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500364201000)\/",
        "Start":  "\/Date(1500364200000)\/",
        "BackupSetId":  "aa55f2e1-2945-48b9-a7fb-ae9225b21c74",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126882000002880400001,
        "LastLSN":  17126885000002910700001,
        "CheckpointLSN":  17126885000001518800001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500364500000)\/",
        "BackupFinishDate":  "\/Date(1500364501000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "f3097802-3895-48ee-bbab-bb340e2b1864",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500364501000)\/",
        "Start":  "\/Date(1500364500000)\/",
        "BackupSetId":  "f3097802-3895-48ee-bbab-bb340e2b1864",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126885000002910700001,
        "LastLSN":  17126885000012446100001,
        "CheckpointLSN":  17126885000012397000001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500364801000)\/",
        "BackupFinishDate":  "\/Date(1500364801000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "6d343efe-fcf5-4b61-ab59-9e63793f1c21",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500364801000)\/",
        "Start":  "\/Date(1500364801000)\/",
        "BackupSetId":  "6d343efe-fcf5-4b61-ab59-9e63793f1c21",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126885000012446100001,
        "LastLSN":  17126886000006098700001,
        "CheckpointLSN":  17126886000005556300004,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500365100000)\/",
        "BackupFinishDate":  "\/Date(1500365100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "00de6130-8b73-4723-9717-53bbb5980809",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500365100000)\/",
        "Start":  "\/Date(1500365100000)\/",
        "BackupSetId":  "00de6130-8b73-4723-9717-53bbb5980809",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126886000006098700001,
        "LastLSN":  17126886000008372000001,
        "CheckpointLSN":  17126886000007692600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500365400000)\/",
        "BackupFinishDate":  "\/Date(1500365400000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "93c25af6-e78c-4093-bec9-fa4a000a0e1d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500365400000)\/",
        "Start":  "\/Date(1500365400000)\/",
        "BackupSetId":  "93c25af6-e78c-4093-bec9-fa4a000a0e1d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126886000008372000001,
        "LastLSN":  17126886000009731900001,
        "CheckpointLSN":  17126886000007692600001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500365700000)\/",
        "BackupFinishDate":  "\/Date(1500365701000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "5817e7d4-fb36-4574-a16f-81746bbefdd2",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500365701000)\/",
        "Start":  "\/Date(1500365700000)\/",
        "BackupSetId":  "5817e7d4-fb36-4574-a16f-81746bbefdd2",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126886000009731900001,
        "LastLSN":  17126886000012009900001,
        "CheckpointLSN":  17126886000011886300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500366000000)\/",
        "BackupFinishDate":  "\/Date(1500366000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "fd6efedb-49ff-4747-aadc-d1afcd63a79e",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500366000000)\/",
        "Start":  "\/Date(1500366000000)\/",
        "BackupSetId":  "fd6efedb-49ff-4747-aadc-d1afcd63a79e",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126886000012009900001,
        "LastLSN":  17126887000001819300001,
        "CheckpointLSN":  17126887000000883800033,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500366300000)\/",
        "BackupFinishDate":  "\/Date(1500366300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "58c1ae74-46e5-4d23-add8-bbd8ceca7872",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500366300000)\/",
        "Start":  "\/Date(1500366300000)\/",
        "BackupSetId":  "58c1ae74-46e5-4d23-add8-bbd8ceca7872",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126887000001819300001,
        "LastLSN":  17126887000004629100001,
        "CheckpointLSN":  17126887000003078000027,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500366600000)\/",
        "BackupFinishDate":  "\/Date(1500366600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "cde67a18-9d64-4c5a-a8b4-736bc22011c0",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500366600000)\/",
        "Start":  "\/Date(1500366600000)\/",
        "BackupSetId":  "cde67a18-9d64-4c5a-a8b4-736bc22011c0",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126887000004629100001,
        "LastLSN":  17126887000007492200001,
        "CheckpointLSN":  17126887000007463100001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500366900000)\/",
        "BackupFinishDate":  "\/Date(1500366900000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "a352d833-494e-4ad1-93f3-38e96e77230f",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500366900000)\/",
        "Start":  "\/Date(1500366900000)\/",
        "BackupSetId":  "a352d833-494e-4ad1-93f3-38e96e77230f",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126887000007492200001,
        "LastLSN":  17126888000001473200001,
        "CheckpointLSN":  17126887000012639900001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500367200000)\/",
        "BackupFinishDate":  "\/Date(1500367201000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "2afd92ef-c314-48aa-89d2-4bba8f2913be",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500367201000)\/",
        "Start":  "\/Date(1500367200000)\/",
        "BackupSetId":  "2afd92ef-c314-48aa-89d2-4bba8f2913be",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126888000001473200001,
        "LastLSN":  17126888000007702900001,
        "CheckpointLSN":  17126888000006332400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500367500000)\/",
        "BackupFinishDate":  "\/Date(1500367500000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "094a53f2-00f4-4178-ab1a-0af54a698cfd",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500367500000)\/",
        "Start":  "\/Date(1500367500000)\/",
        "BackupSetId":  "094a53f2-00f4-4178-ab1a-0af54a698cfd",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126888000007702900001,
        "LastLSN":  17126888000012832900001,
        "CheckpointLSN":  17126888000010667400007,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500367800000)\/",
        "BackupFinishDate":  "\/Date(1500367800000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "42d36e76-5b5b-46c1-b21c-706b6961a694",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500367800000)\/",
        "Start":  "\/Date(1500367800000)\/",
        "BackupSetId":  "42d36e76-5b5b-46c1-b21c-706b6961a694",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126888000012832900001,
        "LastLSN":  17126889000004147900001,
        "CheckpointLSN":  17126889000002242400030,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500368100000)\/",
        "BackupFinishDate":  "\/Date(1500368100000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "feda57fc-cd7a-407c-8c01-68804a7d03bd",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500368100000)\/",
        "Start":  "\/Date(1500368100000)\/",
        "BackupSetId":  "feda57fc-cd7a-407c-8c01-68804a7d03bd",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126889000004147900001,
        "LastLSN":  17126889000008328700001,
        "CheckpointLSN":  17126889000006512000046,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500368401000)\/",
        "BackupFinishDate":  "\/Date(1500368402000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "403c379f-92f3-4d5f-9c3d-fe3dc391387d",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500368402000)\/",
        "Start":  "\/Date(1500368401000)\/",
        "BackupSetId":  "403c379f-92f3-4d5f-9c3d-fe3dc391387d",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126889000008328700001,
        "LastLSN":  17126889000011872200001,
        "CheckpointLSN":  17126889000011097500001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500368700000)\/",
        "BackupFinishDate":  "\/Date(1500368700000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0c4bce56-1f86-48b7-a4bd-c510a059e235",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500368700000)\/",
        "Start":  "\/Date(1500368700000)\/",
        "BackupSetId":  "0c4bce56-1f86-48b7-a4bd-c510a059e235",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126889000011872200001,
        "LastLSN":  17126890000007715700001,
        "CheckpointLSN":  17126890000005513400001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500369000000)\/",
        "BackupFinishDate":  "\/Date(1500369000000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "e5e28fed-19f0-4e91-8368-cb6856385ce9",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500369000000)\/",
        "Start":  "\/Date(1500369000000)\/",
        "BackupSetId":  "e5e28fed-19f0-4e91-8368-cb6856385ce9",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126890000007715700001,
        "LastLSN":  17126891000001634500001,
        "CheckpointLSN":  17126890000012226400010,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500369300000)\/",
        "BackupFinishDate":  "\/Date(1500369300000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "8d47c5ef-065f-4d26-bdac-72a28f099e4a",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500369300000)\/",
        "Start":  "\/Date(1500369300000)\/",
        "BackupSetId":  "8d47c5ef-065f-4d26-bdac-72a28f099e4a",
        "Database":  "testdb"
    },
    {
        "servername":  "testserver",
        "databasename":  "testdb",
        "FirstLSN":  17126891000001634500001,
        "LastLSN":  17126891000006141500001,
        "CheckpointLSN":  17126891000005984300001,
        "DatabaseBackupLSN":  17126658000000314600038,
        "DifferentialBaseLSN":  null,
        "BackupStartDate":  "\/Date(1500369600000)\/",
        "BackupFinishDate":  "\/Date(1500369600000)\/",
        "BackupTypeDescription":  "Transaction Log",
        "BackupSetGUID":  "0928531e-88b9-469c-83ab-8c0e113f7e11",
        "FirstRecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "RecoveryForkID":  "3b085538-ee7b-46ac-9b63-1916d40a59cf",
        "Type":  "Transaction Log",
        "End":  "\/Date(1500369600000)\/",
        "Start":  "\/Date(1500369600000)\/",
        "BackupSetId":  "0928531e-88b9-469c-83ab-8c0e113f7e11",
        "Database":  "testdb"
    }
]
tools\dbatools\tests\ObjectDefinitions\BackupRestore\RawInput\DiffRestore.json
[
    {
        "ServerName":  "CGE100LAP-42\\SQLEXPRESS2016",
        "DatabaseName":  "RestoreTimeDiff",
        "FirstLSN":  34000000006900179,
        "LastLSN":  34000000014400001,
        "CheckpointLSN":  34000000006900179,
        "DatabaseBackupLSN":  0,
        "BackupStartDate":  "\/Date(1493883737000)\/",
        "FileList":  {
                         "CaseSensitive":  false,
                         "IsInitialized":  true,
                         "RemotingFormat":  0,
                         "ChildRelations":  "",
                         "Columns":  "LogicalName PhysicalName Type FileGroupName Size MaxSize FileId CreateLSN DropLSN UniqueId ReadOnlyLSN ReadWriteLSN BackupSizeInBytes SourceBlockSize FileGroupId LogGroupGUID DifferentialBaseLSN DifferentialBaseGUID IsReadOnly IsPresent TDEThumbprint SnapshotUrl",
                         "Constraints":  "",
                         "DataSet":  "System.Data.DataSet",
                         "DefaultView":  "System.Data.DataRowView System.Data.DataRowView",
                         "DisplayExpression":  "",
                         "ExtendedProperties":  "System.Data.PropertyCollection",
                         "HasErrors":  false,
                         "Locale":  "",
                         "MinimumCapacity":  50,
                         "ParentRelations":  "",
                         "PrimaryKey":  "",
                         "Rows":  "System.Data.DataRow System.Data.DataRow",
                         "TableName":  "Table",
                         "Namespace":  "",
                         "Prefix":  "",
                         "Site":  null,
                         "Container":  null,
                         "DesignMode":  false
                     },
        "SoftwareVersionMajor":  13,
        "SoftwareVersionMinor":  0,
        "Position":  1,
        "BackupSetGUID":  "cbe03448-ebd3-4831-80bf-8f5181e4e385",
        "BackupTypeDescription":  "Database",
        "BackupPath":  "C:\\dbatools\\RestoreTimeDiff\\RestoreTimeDiff.bak",
        "BackupFinishDate":  "\/Date(1493883737000)\/",
        "Type":  "Database",
        "End":  "\/Date(1493883737000)\/",
        "Start":  "\/Date(1493883737000)\/",
        "BackupSetId":  "cbe03448-ebd3-4831-80bf-8f5181e4e385",
        "Database":  "RestoreTimeDiff"
    },
    {
        "ServerName":  "CGE100LAP-42\\SQLEXPRESS2016",
        "DatabaseName":  "RestoreTimeDiff",
        "FirstLSN":  34000000021900001,
        "LastLSN":  34000000022200001,
        "CheckpointLSN":  34000000021900001,
        "DatabaseBackupLSN":  34000000006900179,
        "BackupStartDate":  "\/Date(1493884188000)\/",
        "FileList":  {
                         "CaseSensitive":  false,
                         "IsInitialized":  true,
                         "RemotingFormat":  0,
                         "ChildRelations":  "",
                         "Columns":  "LogicalName PhysicalName Type FileGroupName Size MaxSize FileId CreateLSN DropLSN UniqueId ReadOnlyLSN ReadWriteLSN BackupSizeInBytes SourceBlockSize FileGroupId LogGroupGUID DifferentialBaseLSN DifferentialBaseGUID IsReadOnly IsPresent TDEThumbprint SnapshotUrl",
                         "Constraints":  "",
                         "DataSet":  "System.Data.DataSet",
                         "DefaultView":  "System.Data.DataRowView System.Data.DataRowView",
                         "DisplayExpression":  "",
                         "ExtendedProperties":  "System.Data.PropertyCollection",
                         "HasErrors":  false,
                         "Locale":  "",
                         "MinimumCapacity":  50,
                         "ParentRelations":  "",
                         "PrimaryKey":  "",
                         "Rows":  "System.Data.DataRow System.Data.DataRow",
                         "TableName":  "Table",
                         "Namespace":  "",
                         "Prefix":  "",
                         "Site":  null,
                         "Container":  null,
                         "DesignMode":  false
                     },
        "SoftwareVersionMajor":  13,
        "SoftwareVersionMinor":  0,
        "Position":  1,
        "BackupSetGUID":  "5d078de5-f6d0-4d8f-8c9f-ae5baf22bd54",
        "BackupTypeDescription":  "Database Differential",
        "BackupPath":  "C:\\dbatools\\RestoreTimeDiff\\RestoreTimeDiff2.bak",
        "BackupFinishDate":  "\/Date(1493884188000)\/",
        "Type":  "Database Differential",
        "End":  "\/Date(1493884188000)\/",
        "Start":  "\/Date(1493884188000)\/",
        "BackupSetId":  "5d078de5-f6d0-4d8f-8c9f-ae5baf22bd54",
        "Database":  "RestoreTimeDiff"
    },
    {
        "ServerName":  "CGE100LAP-42\\SQLEXPRESS2016",
        "DatabaseName":  "RestoreTimeDiff",
        "FirstLSN":  34000000006900179,
        "LastLSN":  34000000016800001,
        "CheckpointLSN":  34000000006900179,
        "DatabaseBackupLSN":  34000000006900179,
        "BackupStartDate":  "\/Date(1493883887000)\/",
        "FileList":  {
                         "CaseSensitive":  false,
                         "IsInitialized":  true,
                         "RemotingFormat":  0,
                         "ChildRelations":  "",
                         "Columns":  "LogicalName PhysicalName Type FileGroupName Size MaxSize FileId CreateLSN DropLSN UniqueId ReadOnlyLSN ReadWriteLSN BackupSizeInBytes SourceBlockSize FileGroupId LogGroupGUID DifferentialBaseLSN DifferentialBaseGUID IsReadOnly IsPresent TDEThumbprint SnapshotUrl",
                         "Constraints":  "",
                         "DataSet":  "System.Data.DataSet",
                         "DefaultView":  "System.Data.DataRowView System.Data.DataRowView",
                         "DisplayExpression":  "",
                         "ExtendedProperties":  "System.Data.PropertyCollection",
                         "HasErrors":  false,
                         "Locale":  "",
                         "MinimumCapacity":  50,
                         "ParentRelations":  "",
                         "PrimaryKey":  "",
                         "Rows":  "System.Data.DataRow System.Data.DataRow",
                         "TableName":  "Table",
                         "Namespace":  "",
                         "Prefix":  "",
                         "Site":  null,
                         "Container":  null,
                         "DesignMode":  false
                     },
        "SoftwareVersionMajor":  13,
        "SoftwareVersionMinor":  0,
        "Position":  1,
        "BackupSetGUID":  "1326b0cb-ea0c-4a24-95b4-2e8c566190f4",
        "BackupTypeDescription":  "Transaction Log",
        "BackupPath":  "C:\\dbatools\\RestoreTimeDiff\\RestoreTimeDiff_1.trn",
        "BackupFinishDate":  "\/Date(1493883887000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493883887000)\/",
        "Start":  "\/Date(1493883887000)\/",
        "BackupSetId":  "1326b0cb-ea0c-4a24-95b4-2e8c566190f4",
        "Database":  "RestoreTimeDiff"
    },
    {
        "ServerName":  "CGE100LAP-42\\SQLEXPRESS2016",
        "DatabaseName":  "RestoreTimeDiff",
        "FirstLSN":  34000000016800001,
        "LastLSN":  34000000019700001,
        "CheckpointLSN":  34000000017000002,
        "DatabaseBackupLSN":  34000000006900179,
        "BackupStartDate":  "\/Date(1493884037000)\/",
        "FileList":  {
                         "CaseSensitive":  false,
                         "IsInitialized":  true,
                         "RemotingFormat":  0,
                         "ChildRelations":  "",
                         "Columns":  "LogicalName PhysicalName Type FileGroupName Size MaxSize FileId CreateLSN DropLSN UniqueId ReadOnlyLSN ReadWriteLSN BackupSizeInBytes SourceBlockSize FileGroupId LogGroupGUID DifferentialBaseLSN DifferentialBaseGUID IsReadOnly IsPresent TDEThumbprint SnapshotUrl",
                         "Constraints":  "",
                         "DataSet":  "System.Data.DataSet",
                         "DefaultView":  "System.Data.DataRowView System.Data.DataRowView",
                         "DisplayExpression":  "",
                         "ExtendedProperties":  "System.Data.PropertyCollection",
                         "HasErrors":  false,
                         "Locale":  "",
                         "MinimumCapacity":  50,
                         "ParentRelations":  "",
                         "PrimaryKey":  "",
                         "Rows":  "System.Data.DataRow System.Data.DataRow",
                         "TableName":  "Table",
                         "Namespace":  "",
                         "Prefix":  "",
                         "Site":  null,
                         "Container":  null,
                         "DesignMode":  false
                     },
        "SoftwareVersionMajor":  13,
        "SoftwareVersionMinor":  0,
        "Position":  1,
        "BackupSetGUID":  "acf4a999-e0c7-4020-bc78-80bbba09afb9",
        "BackupTypeDescription":  "Transaction Log",
        "BackupPath":  "C:\\dbatools\\RestoreTimeDiff\\RestoreTimeDiff_2.trn",
        "BackupFinishDate":  "\/Date(1493884037000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493884037000)\/",
        "Start":  "\/Date(1493884037000)\/",
        "BackupSetId":  "acf4a999-e0c7-4020-bc78-80bbba09afb9",
        "Database":  "RestoreTimeDiff"
    },
    {
        "ServerName":  "CGE100LAP-42\\SQLEXPRESS2016",
        "DatabaseName":  "RestoreTimeDiff",
        "FirstLSN":  34000000021300001,
        "LastLSN":  34000000023200001,
        "CheckpointLSN":  34000000021900001,
        "DatabaseBackupLSN":  34000000006900179,
        "BackupStartDate":  "\/Date(1493884338000)\/",
        "FileList":  {
                         "CaseSensitive":  false,
                         "IsInitialized":  true,
                         "RemotingFormat":  0,
                         "ChildRelations":  "",
                         "Columns":  "LogicalName PhysicalName Type FileGroupName Size MaxSize FileId CreateLSN DropLSN UniqueId ReadOnlyLSN ReadWriteLSN BackupSizeInBytes SourceBlockSize FileGroupId LogGroupGUID DifferentialBaseLSN DifferentialBaseGUID IsReadOnly IsPresent TDEThumbprint SnapshotUrl",
                         "Constraints":  "",
                         "DataSet":  "System.Data.DataSet",
                         "DefaultView":  "System.Data.DataRowView System.Data.DataRowView",
                         "DisplayExpression":  "",
                         "ExtendedProperties":  "System.Data.PropertyCollection",
                         "HasErrors":  false,
                         "Locale":  "",
                         "MinimumCapacity":  50,
                         "ParentRelations":  "",
                         "PrimaryKey":  "",
                         "Rows":  "System.Data.DataRow System.Data.DataRow",
                         "TableName":  "Table",
                         "Namespace":  "",
                         "Prefix":  "",
                         "Site":  null,
                         "Container":  null,
                         "DesignMode":  false
                     },
        "SoftwareVersionMajor":  13,
        "SoftwareVersionMinor":  0,
        "Position":  1,
        "BackupSetGUID":  "e3e4198c-da8b-434b-b320-9ee22e826fa9",
        "BackupTypeDescription":  "Transaction Log",
        "BackupPath":  "C:\\dbatools\\RestoreTimeDiff\\RestoreTimeDiff_21.trn",
        "BackupFinishDate":  "\/Date(1493884338000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493884338000)\/",
        "Start":  "\/Date(1493884338000)\/",
        "BackupSetId":  "e3e4198c-da8b-434b-b320-9ee22e826fa9",
        "Database":  "RestoreTimeDiff"
    },
    {
        "ServerName":  "CGE100LAP-42\\SQLEXPRESS2016",
        "DatabaseName":  "RestoreTimeDiff",
        "FirstLSN":  34000000023200001,
        "LastLSN":  34000000024800001,
        "CheckpointLSN":  34000000023400002,
        "DatabaseBackupLSN":  34000000006900179,
        "BackupStartDate":  "\/Date(1493884488000)\/",
        "FileList":  {
                         "CaseSensitive":  false,
                         "IsInitialized":  true,
                         "RemotingFormat":  0,
                         "ChildRelations":  "",
                         "Columns":  "LogicalName PhysicalName Type FileGroupName Size MaxSize FileId CreateLSN DropLSN UniqueId ReadOnlyLSN ReadWriteLSN BackupSizeInBytes SourceBlockSize FileGroupId LogGroupGUID DifferentialBaseLSN DifferentialBaseGUID IsReadOnly IsPresent TDEThumbprint SnapshotUrl",
                         "Constraints":  "",
                         "DataSet":  "System.Data.DataSet",
                         "DefaultView":  "System.Data.DataRowView System.Data.DataRowView",
                         "DisplayExpression":  "",
                         "ExtendedProperties":  "System.Data.PropertyCollection",
                         "HasErrors":  false,
                         "Locale":  "",
                         "MinimumCapacity":  50,
                         "ParentRelations":  "",
                         "PrimaryKey":  "",
                         "Rows":  "System.Data.DataRow System.Data.DataRow",
                         "TableName":  "Table",
                         "Namespace":  "",
                         "Prefix":  "",
                         "Site":  null,
                         "Container":  null,
                         "DesignMode":  false
                     },
        "SoftwareVersionMajor":  13,
        "SoftwareVersionMinor":  0,
        "Position":  1,
        "BackupSetGUID":  "b845d9c8-e9fa-49cf-9148-ed1283610935",
        "BackupTypeDescription":  "Transaction Log",
        "BackupPath":  "C:\\dbatools\\RestoreTimeDiff\\RestoreTimeDiff_22.trn",
        "BackupFinishDate":  "\/Date(1493884488000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493884488000)\/",
        "Start":  "\/Date(1493884488000)\/",
        "BackupSetId":  "b845d9c8-e9fa-49cf-9148-ed1283610935",
        "Database":  "RestoreTimeDiff"
    },
    {
        "ServerName":  "CGE100LAP-42\\SQLEXPRESS2016",
        "DatabaseName":  "RestoreTimeDiff",
        "FirstLSN":  34000000024800001,
        "LastLSN":  34000000026400001,
        "CheckpointLSN":  34000000025000002,
        "DatabaseBackupLSN":  34000000006900179,
        "BackupStartDate":  "\/Date(1493884638000)\/",
        "FileList":  {
                         "CaseSensitive":  false,
                         "IsInitialized":  true,
                         "RemotingFormat":  0,
                         "ChildRelations":  "",
                         "Columns":  "LogicalName PhysicalName Type FileGroupName Size MaxSize FileId CreateLSN DropLSN UniqueId ReadOnlyLSN ReadWriteLSN BackupSizeInBytes SourceBlockSize FileGroupId LogGroupGUID DifferentialBaseLSN DifferentialBaseGUID IsReadOnly IsPresent TDEThumbprint SnapshotUrl",
                         "Constraints":  "",
                         "DataSet":  "System.Data.DataSet",
                         "DefaultView":  "System.Data.DataRowView System.Data.DataRowView",
                         "DisplayExpression":  "",
                         "ExtendedProperties":  "System.Data.PropertyCollection",
                         "HasErrors":  false,
                         "Locale":  "",
                         "MinimumCapacity":  50,
                         "ParentRelations":  "",
                         "PrimaryKey":  "",
                         "Rows":  "System.Data.DataRow System.Data.DataRow",
                         "TableName":  "Table",
                         "Namespace":  "",
                         "Prefix":  "",
                         "Site":  null,
                         "Container":  null,
                         "DesignMode":  false
                     },
        "SoftwareVersionMajor":  13,
        "SoftwareVersionMinor":  0,
        "Position":  1,
        "BackupSetGUID":  "6b18433c-9178-4b47-972f-7dae2972f3a5",
        "BackupTypeDescription":  "Transaction Log",
        "BackupPath":  "C:\\dbatools\\RestoreTimeDiff\\RestoreTimeDiff_23.trn",
        "BackupFinishDate":  "\/Date(1493884638000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493884638000)\/",
        "Start":  "\/Date(1493884638000)\/",
        "BackupSetId":  "6b18433c-9178-4b47-972f-7dae2972f3a5",
        "Database":  "RestoreTimeDiff"
    },
    {
        "ServerName":  "CGE100LAP-42\\SQLEXPRESS2016",
        "DatabaseName":  "RestoreTimeDiff",
        "FirstLSN":  34000000026400001,
        "LastLSN":  34000000027000001,
        "CheckpointLSN":  34000000026600002,
        "DatabaseBackupLSN":  34000000006900179,
        "BackupStartDate":  "\/Date(1493884638000)\/",
        "FileList":  {
                         "CaseSensitive":  false,
                         "IsInitialized":  true,
                         "RemotingFormat":  0,
                         "ChildRelations":  "",
                         "Columns":  "LogicalName PhysicalName Type FileGroupName Size MaxSize FileId CreateLSN DropLSN UniqueId ReadOnlyLSN ReadWriteLSN BackupSizeInBytes SourceBlockSize FileGroupId LogGroupGUID DifferentialBaseLSN DifferentialBaseGUID IsReadOnly IsPresent TDEThumbprint SnapshotUrl",
                         "Constraints":  "",
                         "DataSet":  "System.Data.DataSet",
                         "DefaultView":  "System.Data.DataRowView System.Data.DataRowView",
                         "DisplayExpression":  "",
                         "ExtendedProperties":  "System.Data.PropertyCollection",
                         "HasErrors":  false,
                         "Locale":  "",
                         "MinimumCapacity":  50,
                         "ParentRelations":  "",
                         "PrimaryKey":  "",
                         "Rows":  "System.Data.DataRow System.Data.DataRow",
                         "TableName":  "Table",
                         "Namespace":  "",
                         "Prefix":  "",
                         "Site":  null,
                         "Container":  null,
                         "DesignMode":  false
                     },
        "SoftwareVersionMajor":  13,
        "SoftwareVersionMinor":  0,
        "Position":  1,
        "BackupSetGUID":  "29685266-3ec0-4c80-aa47-25fe5e59c206",
        "BackupTypeDescription":  "Transaction Log",
        "BackupPath":  "C:\\dbatools\\RestoreTimeDiff\\RestoreTimeDiff_24.trn",
        "BackupFinishDate":  "\/Date(1493884638000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493884638000)\/",
        "Start":  "\/Date(1493884638000)\/",
        "BackupSetId":  "29685266-3ec0-4c80-aa47-25fe5e59c206",
        "Database":  "RestoreTimeDiff"
    },
    {
        "ServerName":  "CGE100LAP-42\\SQLEXPRESS2016",
        "DatabaseName":  "RestoreTimeDiff",
        "FirstLSN":  34000000027000001,
        "LastLSN":  34000000028400001,
        "CheckpointLSN":  34000000027200002,
        "DatabaseBackupLSN":  34000000006900179,
        "BackupStartDate":  "\/Date(1493884788000)\/",
        "FileList":  {
                         "CaseSensitive":  false,
                         "IsInitialized":  true,
                         "RemotingFormat":  0,
                         "ChildRelations":  "",
                         "Columns":  "LogicalName PhysicalName Type FileGroupName Size MaxSize FileId CreateLSN DropLSN UniqueId ReadOnlyLSN ReadWriteLSN BackupSizeInBytes SourceBlockSize FileGroupId LogGroupGUID DifferentialBaseLSN DifferentialBaseGUID IsReadOnly IsPresent TDEThumbprint SnapshotUrl",
                         "Constraints":  "",
                         "DataSet":  "System.Data.DataSet",
                         "DefaultView":  "System.Data.DataRowView System.Data.DataRowView",
                         "DisplayExpression":  "",
                         "ExtendedProperties":  "System.Data.PropertyCollection",
                         "HasErrors":  false,
                         "Locale":  "",
                         "MinimumCapacity":  50,
                         "ParentRelations":  "",
                         "PrimaryKey":  "",
                         "Rows":  "System.Data.DataRow System.Data.DataRow",
                         "TableName":  "Table",
                         "Namespace":  "",
                         "Prefix":  "",
                         "Site":  null,
                         "Container":  null,
                         "DesignMode":  false
                     },
        "SoftwareVersionMajor":  13,
        "SoftwareVersionMinor":  0,
        "Position":  1,
        "BackupSetGUID":  "c29236ca-f88f-464e-ade6-34009bc1155c",
        "BackupTypeDescription":  "Transaction Log",
        "BackupPath":  "C:\\dbatools\\RestoreTimeDiff\\RestoreTimeDiff_25.trn",
        "BackupFinishDate":  "\/Date(1493884788000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493884788000)\/",
        "Start":  "\/Date(1493884788000)\/",
        "BackupSetId":  "c29236ca-f88f-464e-ade6-34009bc1155c",
        "Database":  "RestoreTimeDiff"
    },
    {
        "ServerName":  "CGE100LAP-42\\SQLEXPRESS2016",
        "DatabaseName":  "RestoreTimeDiff",
        "FirstLSN":  34000000019700001,
        "LastLSN":  34000000021300001,
        "CheckpointLSN":  34000000019900002,
        "DatabaseBackupLSN":  34000000006900179,
        "BackupStartDate":  "\/Date(1493884187000)\/",
        "FileList":  {
                         "CaseSensitive":  false,
                         "IsInitialized":  true,
                         "RemotingFormat":  0,
                         "ChildRelations":  "",
                         "Columns":  "LogicalName PhysicalName Type FileGroupName Size MaxSize FileId CreateLSN DropLSN UniqueId ReadOnlyLSN ReadWriteLSN BackupSizeInBytes SourceBlockSize FileGroupId LogGroupGUID DifferentialBaseLSN DifferentialBaseGUID IsReadOnly IsPresent TDEThumbprint SnapshotUrl",
                         "Constraints":  "",
                         "DataSet":  "System.Data.DataSet",
                         "DefaultView":  "System.Data.DataRowView System.Data.DataRowView",
                         "DisplayExpression":  "",
                         "ExtendedProperties":  "System.Data.PropertyCollection",
                         "HasErrors":  false,
                         "Locale":  "",
                         "MinimumCapacity":  50,
                         "ParentRelations":  "",
                         "PrimaryKey":  "",
                         "Rows":  "System.Data.DataRow System.Data.DataRow",
                         "TableName":  "Table",
                         "Namespace":  "",
                         "Prefix":  "",
                         "Site":  null,
                         "Container":  null,
                         "DesignMode":  false
                     },
        "SoftwareVersionMajor":  13,
        "SoftwareVersionMinor":  0,
        "Position":  1,
        "BackupSetGUID":  "b989fb98-32b6-4abf-8483-25af6c6f0c9c",
        "BackupTypeDescription":  "Transaction Log",
        "BackupPath":  "C:\\dbatools\\RestoreTimeDiff\\RestoreTimeDiff_3.trn",
        "BackupFinishDate":  "\/Date(1493884187000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493884187000)\/",
        "Start":  "\/Date(1493884187000)\/",
        "BackupSetId":  "b989fb98-32b6-4abf-8483-25af6c6f0c9c",
        "Database":  "RestoreTimeDiff"
    }
]
tools\dbatools\tests\ObjectDefinitions\BackupRestore\RawInput\EmptyTLogData.json
[
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Database",
        "FirstLSN":  14975000000268200036,
        "LastLSN":  14975000000269800001,
        "CheckpointLSN":  14975000000268200036,
        "DatabaseBackupLSN":  14975000000265600036,
        "BackupSetGUID":  "b7e35ab2-e45f-41f8-9d8a-06a734d4771e",
        "BackupStartDate":  "\/Date(1493233263000)\/",
        "BackupFinishDate":  "\/Date(1493233263000)\/",
        "Type":  "Database",
        "End":  "\/Date(1493233263000)\/",
        "Start":  "\/Date(1493233263000)\/",
        "BackupSetId":  "b7e35ab2-e45f-41f8-9d8a-06a734d4771e",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Database",
        "FirstLSN":  14975000000270800036,
        "LastLSN":  14975000000272400001,
        "CheckpointLSN":  14975000000270800036,
        "DatabaseBackupLSN":  14975000000268200036,
        "BackupSetGUID":  "f3907981-d9f0-41c3-9204-2c5535b896bc",
        "BackupStartDate":  "\/Date(1493319676000)\/",
        "BackupFinishDate":  "\/Date(1493319676000)\/",
        "Type":  "Database",
        "End":  "\/Date(1493319676000)\/",
        "Start":  "\/Date(1493319676000)\/",
        "BackupSetId":  "f3907981-d9f0-41c3-9204-2c5535b896bc",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000268000001,
        "LastLSN":  14975000000270600001,
        "CheckpointLSN":  14975000000268200036,
        "DatabaseBackupLSN":  14975000000268200036,
        "BackupSetGUID":  "294f1724-09a3-4149-b1c7-c083a305899b",
        "BackupStartDate":  "\/Date(1493234100000)\/",
        "BackupFinishDate":  "\/Date(1493234100000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493234100000)\/",
        "Start":  "\/Date(1493234100000)\/",
        "BackupSetId":  "294f1724-09a3-4149-b1c7-c083a305899b",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000270600001,
        "LastLSN":  14975000000273200001,
        "CheckpointLSN":  14975000000270800036,
        "DatabaseBackupLSN":  14975000000270800036,
        "BackupSetGUID":  "b5ca8726-f4a9-444f-8cbc-fc5db703ceef",
        "BackupStartDate":  "\/Date(1493320500000)\/",
        "BackupFinishDate":  "\/Date(1493320500000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493320500000)\/",
        "Start":  "\/Date(1493320500000)\/",
        "BackupSetId":  "b5ca8726-f4a9-444f-8cbc-fc5db703ceef",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000268000001,
        "LastLSN":  14975000000268000001,
        "CheckpointLSN":  14975000000265600036,
        "DatabaseBackupLSN":  14975000000265600036,
        "BackupSetGUID":  "2b49fa55-b871-4dfe-a2cf-f88ec38afdbf",
        "BackupStartDate":  "\/Date(1493233201000)\/",
        "BackupFinishDate":  "\/Date(1493233201000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493233201000)\/",
        "Start":  "\/Date(1493233201000)\/",
        "BackupSetId":  "2b49fa55-b871-4dfe-a2cf-f88ec38afdbf",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000268000001,
        "LastLSN":  14975000000268000001,
        "CheckpointLSN":  14975000000265600036,
        "DatabaseBackupLSN":  14975000000265600036,
        "BackupSetGUID":  "0aa9d443-b159-4a53-be12-7d89787013d9",
        "BackupStartDate":  "\/Date(1493232300000)\/",
        "BackupFinishDate":  "\/Date(1493232300000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493232300000)\/",
        "Start":  "\/Date(1493232300000)\/",
        "BackupSetId":  "0aa9d443-b159-4a53-be12-7d89787013d9",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000268000001,
        "LastLSN":  14975000000268000001,
        "CheckpointLSN":  14975000000265600036,
        "DatabaseBackupLSN":  14975000000265600036,
        "BackupSetGUID":  "b83d5cdd-bfb1-41ee-a6f1-197ba60c3d09",
        "BackupStartDate":  "\/Date(1493231401000)\/",
        "BackupFinishDate":  "\/Date(1493231401000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493231401000)\/",
        "Start":  "\/Date(1493231401000)\/",
        "BackupSetId":  "b83d5cdd-bfb1-41ee-a6f1-197ba60c3d09",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000268000001,
        "LastLSN":  14975000000268000001,
        "CheckpointLSN":  14975000000265600036,
        "DatabaseBackupLSN":  14975000000265600036,
        "BackupSetGUID":  "347ed365-6963-49d1-b8ec-352591d8cb6e",
        "BackupStartDate":  "\/Date(1493230500000)\/",
        "BackupFinishDate":  "\/Date(1493230500000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493230500000)\/",
        "Start":  "\/Date(1493230500000)\/",
        "BackupSetId":  "347ed365-6963-49d1-b8ec-352591d8cb6e",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000268000001,
        "LastLSN":  14975000000268000001,
        "CheckpointLSN":  14975000000265600036,
        "DatabaseBackupLSN":  14975000000265600036,
        "BackupSetGUID":  "65bb92c1-6ec1-4bbf-8faa-25e5c72a6994",
        "BackupStartDate":  "\/Date(1493229601000)\/",
        "BackupFinishDate":  "\/Date(1493229601000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493229601000)\/",
        "Start":  "\/Date(1493229601000)\/",
        "BackupSetId":  "65bb92c1-6ec1-4bbf-8faa-25e5c72a6994",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000268000001,
        "LastLSN":  14975000000268000001,
        "CheckpointLSN":  14975000000265600036,
        "DatabaseBackupLSN":  14975000000265600036,
        "BackupSetGUID":  "cdbfd236-102e-4241-bd58-fe24443d7342",
        "BackupStartDate":  "\/Date(1493228700000)\/",
        "BackupFinishDate":  "\/Date(1493228700000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493228700000)\/",
        "Start":  "\/Date(1493228700000)\/",
        "BackupSetId":  "cdbfd236-102e-4241-bd58-fe24443d7342",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000268000001,
        "LastLSN":  14975000000268000001,
        "CheckpointLSN":  14975000000265600036,
        "DatabaseBackupLSN":  14975000000265600036,
        "BackupSetGUID":  "cf542a14-bd63-40ea-a674-4a4c2585cab8",
        "BackupStartDate":  "\/Date(1493227801000)\/",
        "BackupFinishDate":  "\/Date(1493227801000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493227801000)\/",
        "Start":  "\/Date(1493227801000)\/",
        "BackupSetId":  "cf542a14-bd63-40ea-a674-4a4c2585cab8",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000268000001,
        "LastLSN":  14975000000268000001,
        "CheckpointLSN":  14975000000265600036,
        "DatabaseBackupLSN":  14975000000265600036,
        "BackupSetGUID":  "c3197714-02a3-4672-bffc-cffc8a8fe09a",
        "BackupStartDate":  "\/Date(1493226900000)\/",
        "BackupFinishDate":  "\/Date(1493226900000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493226900000)\/",
        "Start":  "\/Date(1493226900000)\/",
        "BackupSetId":  "c3197714-02a3-4672-bffc-cffc8a8fe09a",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000268000001,
        "LastLSN":  14975000000268000001,
        "CheckpointLSN":  14975000000265600036,
        "DatabaseBackupLSN":  14975000000265600036,
        "BackupSetGUID":  "7fa63174-137e-4eea-b8b1-8fbb3ceec472",
        "BackupStartDate":  "\/Date(1493226001000)\/",
        "BackupFinishDate":  "\/Date(1493226001000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493226001000)\/",
        "Start":  "\/Date(1493226001000)\/",
        "BackupSetId":  "7fa63174-137e-4eea-b8b1-8fbb3ceec472",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000268000001,
        "LastLSN":  14975000000268000001,
        "CheckpointLSN":  14975000000265600036,
        "DatabaseBackupLSN":  14975000000265600036,
        "BackupSetGUID":  "c3516d11-b4ae-4ea3-9ea1-2a966c4792ec",
        "BackupStartDate":  "\/Date(1493225101000)\/",
        "BackupFinishDate":  "\/Date(1493225101000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493225101000)\/",
        "Start":  "\/Date(1493225101000)\/",
        "BackupSetId":  "c3516d11-b4ae-4ea3-9ea1-2a966c4792ec",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000270600001,
        "LastLSN":  14975000000270600001,
        "CheckpointLSN":  14975000000268200036,
        "DatabaseBackupLSN":  14975000000268200036,
        "BackupSetGUID":  "bc8803c2-0d8d-40c1-a27f-7c741ba37b8e",
        "BackupStartDate":  "\/Date(1493319601000)\/",
        "BackupFinishDate":  "\/Date(1493319601000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493319601000)\/",
        "Start":  "\/Date(1493319601000)\/",
        "BackupSetId":  "bc8803c2-0d8d-40c1-a27f-7c741ba37b8e",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000270600001,
        "LastLSN":  14975000000270600001,
        "CheckpointLSN":  14975000000268200036,
        "DatabaseBackupLSN":  14975000000268200036,
        "BackupSetGUID":  "8f6f9d86-c1e7-409b-b90e-b1f71c23ad08",
        "BackupStartDate":  "\/Date(1493318700000)\/",
        "BackupFinishDate":  "\/Date(1493318700000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493318700000)\/",
        "Start":  "\/Date(1493318700000)\/",
        "BackupSetId":  "8f6f9d86-c1e7-409b-b90e-b1f71c23ad08",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000270600001,
        "LastLSN":  14975000000270600001,
        "CheckpointLSN":  14975000000268200036,
        "DatabaseBackupLSN":  14975000000268200036,
        "BackupSetGUID":  "1f425381-5728-44c1-8772-73240968b652",
        "BackupStartDate":  "\/Date(1493317801000)\/",
        "BackupFinishDate":  "\/Date(1493317801000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493317801000)\/",
        "Start":  "\/Date(1493317801000)\/",
        "BackupSetId":  "1f425381-5728-44c1-8772-73240968b652",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000270600001,
        "LastLSN":  14975000000270600001,
        "CheckpointLSN":  14975000000268200036,
        "DatabaseBackupLSN":  14975000000268200036,
        "BackupSetGUID":  "46830b28-d10b-4c0a-9152-85ef6c5e9885",
        "BackupStartDate":  "\/Date(1493316901000)\/",
        "BackupFinishDate":  "\/Date(1493316901000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493316901000)\/",
        "Start":  "\/Date(1493316901000)\/",
        "BackupSetId":  "46830b28-d10b-4c0a-9152-85ef6c5e9885",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000270600001,
        "LastLSN":  14975000000270600001,
        "CheckpointLSN":  14975000000268200036,
        "DatabaseBackupLSN":  14975000000268200036,
        "BackupSetGUID":  "209bdbd0-63a4-44d4-afd2-6047ef38d2c8",
        "BackupStartDate":  "\/Date(1493316000000)\/",
        "BackupFinishDate":  "\/Date(1493316000000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493316000000)\/",
        "Start":  "\/Date(1493316000000)\/",
        "BackupSetId":  "209bdbd0-63a4-44d4-afd2-6047ef38d2c8",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000270600001,
        "LastLSN":  14975000000270600001,
        "CheckpointLSN":  14975000000268200036,
        "DatabaseBackupLSN":  14975000000268200036,
        "BackupSetGUID":  "2529a0a3-2a68-4925-b07b-43f503b911e2",
        "BackupStartDate":  "\/Date(1493315101000)\/",
        "BackupFinishDate":  "\/Date(1493315101000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493315101000)\/",
        "Start":  "\/Date(1493315101000)\/",
        "BackupSetId":  "2529a0a3-2a68-4925-b07b-43f503b911e2",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000270600001,
        "LastLSN":  14975000000270600001,
        "CheckpointLSN":  14975000000268200036,
        "DatabaseBackupLSN":  14975000000268200036,
        "BackupSetGUID":  "29a89731-1ed4-4e6c-b872-80aad33f9d3e",
        "BackupStartDate":  "\/Date(1493314200000)\/",
        "BackupFinishDate":  "\/Date(1493314200000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493314200000)\/",
        "Start":  "\/Date(1493314200000)\/",
        "BackupSetId":  "29a89731-1ed4-4e6c-b872-80aad33f9d3e",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000270600001,
        "LastLSN":  14975000000270600001,
        "CheckpointLSN":  14975000000268200036,
        "DatabaseBackupLSN":  14975000000268200036,
        "BackupSetGUID":  "4fc16d31-3fb3-43f3-8760-d9cef6603620",
        "BackupStartDate":  "\/Date(1493313301000)\/",
        "BackupFinishDate":  "\/Date(1493313301000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493313301000)\/",
        "Start":  "\/Date(1493313301000)\/",
        "BackupSetId":  "4fc16d31-3fb3-43f3-8760-d9cef6603620",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000270600001,
        "LastLSN":  14975000000270600001,
        "CheckpointLSN":  14975000000268200036,
        "DatabaseBackupLSN":  14975000000268200036,
        "BackupSetGUID":  "d3f79330-8312-4db5-83d6-953a69c16377",
        "BackupStartDate":  "\/Date(1493312400000)\/",
        "BackupFinishDate":  "\/Date(1493312400000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493312400000)\/",
        "Start":  "\/Date(1493312400000)\/",
        "BackupSetId":  "d3f79330-8312-4db5-83d6-953a69c16377",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000270600001,
        "LastLSN":  14975000000270600001,
        "CheckpointLSN":  14975000000268200036,
        "DatabaseBackupLSN":  14975000000268200036,
        "BackupSetGUID":  "8fd0b5e0-8b68-4ab0-8f92-82b592803dcb",
        "BackupStartDate":  "\/Date(1493311501000)\/",
        "BackupFinishDate":  "\/Date(1493311501000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493311501000)\/",
        "Start":  "\/Date(1493311501000)\/",
        "BackupSetId":  "8fd0b5e0-8b68-4ab0-8f92-82b592803dcb",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000273200001,
        "LastLSN":  14975000000273200001,
        "CheckpointLSN":  14975000000270800036,
        "DatabaseBackupLSN":  14975000000270800036,
        "BackupSetGUID":  "8b23aa8e-505b-4ce2-9b47-71ff33c01f92",
        "BackupStartDate":  "\/Date(1493381701000)\/",
        "BackupFinishDate":  "\/Date(1493381701000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493381701000)\/",
        "Start":  "\/Date(1493381701000)\/",
        "BackupSetId":  "8b23aa8e-505b-4ce2-9b47-71ff33c01f92",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000273200001,
        "LastLSN":  14975000000273200001,
        "CheckpointLSN":  14975000000270800036,
        "DatabaseBackupLSN":  14975000000270800036,
        "BackupSetGUID":  "0830fd5c-cfea-4240-ad0b-ee2edc0dacc7",
        "BackupStartDate":  "\/Date(1493380800000)\/",
        "BackupFinishDate":  "\/Date(1493380800000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493380800000)\/",
        "Start":  "\/Date(1493380800000)\/",
        "BackupSetId":  "0830fd5c-cfea-4240-ad0b-ee2edc0dacc7",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000273200001,
        "LastLSN":  14975000000273200001,
        "CheckpointLSN":  14975000000270800036,
        "DatabaseBackupLSN":  14975000000270800036,
        "BackupSetGUID":  "29eda15e-9912-49e7-bcdc-e8bc71ab31ae",
        "BackupStartDate":  "\/Date(1493379901000)\/",
        "BackupFinishDate":  "\/Date(1493379901000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493379901000)\/",
        "Start":  "\/Date(1493379901000)\/",
        "BackupSetId":  "29eda15e-9912-49e7-bcdc-e8bc71ab31ae",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000273200001,
        "LastLSN":  14975000000273200001,
        "CheckpointLSN":  14975000000270800036,
        "DatabaseBackupLSN":  14975000000270800036,
        "BackupSetGUID":  "aed83508-9d38-4830-8f22-6f2cfae8871a",
        "BackupStartDate":  "\/Date(1493379000000)\/",
        "BackupFinishDate":  "\/Date(1493379000000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493379000000)\/",
        "Start":  "\/Date(1493379000000)\/",
        "BackupSetId":  "aed83508-9d38-4830-8f22-6f2cfae8871a",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000273200001,
        "LastLSN":  14975000000273200001,
        "CheckpointLSN":  14975000000270800036,
        "DatabaseBackupLSN":  14975000000270800036,
        "BackupSetGUID":  "9f05a554-f1b4-4e12-9bb1-ee745c501449",
        "BackupStartDate":  "\/Date(1493378101000)\/",
        "BackupFinishDate":  "\/Date(1493378101000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493378101000)\/",
        "Start":  "\/Date(1493378101000)\/",
        "BackupSetId":  "9f05a554-f1b4-4e12-9bb1-ee745c501449",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000273200001,
        "LastLSN":  14975000000273200001,
        "CheckpointLSN":  14975000000270800036,
        "DatabaseBackupLSN":  14975000000270800036,
        "BackupSetGUID":  "03ac9354-69ac-4f8d-93d3-55fb85cafc96",
        "BackupStartDate":  "\/Date(1493377201000)\/",
        "BackupFinishDate":  "\/Date(1493377201000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493377201000)\/",
        "Start":  "\/Date(1493377201000)\/",
        "BackupSetId":  "03ac9354-69ac-4f8d-93d3-55fb85cafc96",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000273200001,
        "LastLSN":  14975000000273200001,
        "CheckpointLSN":  14975000000270800036,
        "DatabaseBackupLSN":  14975000000270800036,
        "BackupSetGUID":  "085468a4-7113-4f21-9048-e4175221ebf3",
        "BackupStartDate":  "\/Date(1493376300000)\/",
        "BackupFinishDate":  "\/Date(1493376300000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493376300000)\/",
        "Start":  "\/Date(1493376300000)\/",
        "BackupSetId":  "085468a4-7113-4f21-9048-e4175221ebf3",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000273200001,
        "LastLSN":  14975000000273200001,
        "CheckpointLSN":  14975000000270800036,
        "DatabaseBackupLSN":  14975000000270800036,
        "BackupSetGUID":  "cbbcbd46-7f94-4b58-af6c-64198837c3a1",
        "BackupStartDate":  "\/Date(1493375401000)\/",
        "BackupFinishDate":  "\/Date(1493375401000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493375401000)\/",
        "Start":  "\/Date(1493375401000)\/",
        "BackupSetId":  "cbbcbd46-7f94-4b58-af6c-64198837c3a1",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000273200001,
        "LastLSN":  14975000000273200001,
        "CheckpointLSN":  14975000000270800036,
        "DatabaseBackupLSN":  14975000000270800036,
        "BackupSetGUID":  "2aa15dca-703a-4aba-bcd0-875c7d851f38",
        "BackupStartDate":  "\/Date(1493374500000)\/",
        "BackupFinishDate":  "\/Date(1493374500000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493374500000)\/",
        "Start":  "\/Date(1493374500000)\/",
        "BackupSetId":  "2aa15dca-703a-4aba-bcd0-875c7d851f38",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000273200001,
        "LastLSN":  14975000000273200001,
        "CheckpointLSN":  14975000000270800036,
        "DatabaseBackupLSN":  14975000000270800036,
        "BackupSetGUID":  "234c7f1b-0106-40b3-bdf4-01cbf3bb09b0",
        "BackupStartDate":  "\/Date(1493373601000)\/",
        "BackupFinishDate":  "\/Date(1493373601000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493373601000)\/",
        "Start":  "\/Date(1493373601000)\/",
        "BackupSetId":  "234c7f1b-0106-40b3-bdf4-01cbf3bb09b0",
        "Database":  "ETLogTest"
    }
]
tools\dbatools\tests\ObjectDefinitions\BackupRestore\RawInput\RestoreCommaIssues.json
[
    {
        "ServerName":  "TestSQL1\\Testinstance1,1234",
        "DatabaseName":  "Test, command and conquer ",
        "FirstLSN":  34000000006900179,
        "LastLSN":  34000000014400001,
        "CheckpointLSN":  34000000006900179,
        "DatabaseBackupLSN":  0,
        "BackupStartDate":  "\/Date(1493883737000)\/",
        "FileList":  {
                         "CaseSensitive":  false,
                         "IsInitialized":  true,
                         "RemotingFormat":  0,
                         "ChildRelations":  "",
                         "Columns":  "LogicalName PhysicalName Type FileGroupName Size MaxSize FileId CreateLSN DropLSN UniqueId ReadOnlyLSN ReadWriteLSN BackupSizeInBytes SourceBlockSize FileGroupId LogGroupGUID DifferentialBaseLSN DifferentialBaseGUID IsReadOnly IsPresent TDEThumbprint SnapshotUrl",
                         "Constraints":  "",
                         "DataSet":  "System.Data.DataSet",
                         "DefaultView":  "System.Data.DataRowView System.Data.DataRowView",
                         "DisplayExpression":  "",
                         "ExtendedProperties":  "System.Data.PropertyCollection",
                         "HasErrors":  false,
                         "Locale":  "",
                         "MinimumCapacity":  50,
                         "ParentRelations":  "",
                         "PrimaryKey":  "",
                         "Rows":  "System.Data.DataRow System.Data.DataRow",
                         "TableName":  "Table",
                         "Namespace":  "",
                         "Prefix":  "",
                         "Site":  null,
                         "Container":  null,
                         "DesignMode":  false
                     },
        "SoftwareVersionMajor":  13,
        "SoftwareVersionMinor":  0,
        "Position":  1,
        "BackupSetGUID":  "cbe03448-ebd3-4831-80bf-8f5181e4e385",
        "BackupTypeDescription":  "Database",
        "BackupPath":  "C:\\dbatools\\Test, command and conquer\\RestoreTimeDiff.bak",
        "BackupFinishDate":  "\/Date(1493883737000)\/",
        "Type":  "Database",
        "End":  "\/Date(1493883737000)\/",
        "Start":  "\/Date(1493883737000)\/",
        "BackupSetId":  "cbe03448-ebd3-4831-80bf-8f5181e4e385",
        "Database":  "Test, command and conquer "
    },
    {
        "ServerName":  "TestSQL1\\Testinstance1,1234",
        "DatabaseName":  "Test, command and conquer ",
        "FirstLSN":  34000000021900001,
        "LastLSN":  34000000022200001,
        "CheckpointLSN":  34000000021900001,
        "DatabaseBackupLSN":  34000000006900179,
        "BackupStartDate":  "\/Date(1493884188000)\/",
        "FileList":  {
                         "CaseSensitive":  false,
                         "IsInitialized":  true,
                         "RemotingFormat":  0,
                         "ChildRelations":  "",
                         "Columns":  "LogicalName PhysicalName Type FileGroupName Size MaxSize FileId CreateLSN DropLSN UniqueId ReadOnlyLSN ReadWriteLSN BackupSizeInBytes SourceBlockSize FileGroupId LogGroupGUID DifferentialBaseLSN DifferentialBaseGUID IsReadOnly IsPresent TDEThumbprint SnapshotUrl",
                         "Constraints":  "",
                         "DataSet":  "System.Data.DataSet",
                         "DefaultView":  "System.Data.DataRowView System.Data.DataRowView",
                         "DisplayExpression":  "",
                         "ExtendedProperties":  "System.Data.PropertyCollection",
                         "HasErrors":  false,
                         "Locale":  "",
                         "MinimumCapacity":  50,
                         "ParentRelations":  "",
                         "PrimaryKey":  "",
                         "Rows":  "System.Data.DataRow System.Data.DataRow",
                         "TableName":  "Table",
                         "Namespace":  "",
                         "Prefix":  "",
                         "Site":  null,
                         "Container":  null,
                         "DesignMode":  false
                     },
        "SoftwareVersionMajor":  13,
        "SoftwareVersionMinor":  0,
        "Position":  1,
        "BackupSetGUID":  "5d078de5-f6d0-4d8f-8c9f-ae5baf22bd54",
        "BackupTypeDescription":  "Database Differential",
        "BackupPath":  "C:\\dbatools\\Test, command and conquer\\RestoreTimeDiff2.bak",
        "BackupFinishDate":  "\/Date(1493884188000)\/",
        "Type":  "Database Differential",
        "End":  "\/Date(1493884188000)\/",
        "Start":  "\/Date(1493884188000)\/",
        "BackupSetId":  "5d078de5-f6d0-4d8f-8c9f-ae5baf22bd54",
        "Database":  "Test, command and conquer "
    },
    {
        "ServerName":  "TestSQL1\\Testinstance1,1234",
        "DatabaseName":  "Test, command and conquer ",
        "FirstLSN":  34000000006900179,
        "LastLSN":  34000000016800001,
        "CheckpointLSN":  34000000006900179,
        "DatabaseBackupLSN":  34000000006900179,
        "BackupStartDate":  "\/Date(1493883887000)\/",
        "FileList":  {
                         "CaseSensitive":  false,
                         "IsInitialized":  true,
                         "RemotingFormat":  0,
                         "ChildRelations":  "",
                         "Columns":  "LogicalName PhysicalName Type FileGroupName Size MaxSize FileId CreateLSN DropLSN UniqueId ReadOnlyLSN ReadWriteLSN BackupSizeInBytes SourceBlockSize FileGroupId LogGroupGUID DifferentialBaseLSN DifferentialBaseGUID IsReadOnly IsPresent TDEThumbprint SnapshotUrl",
                         "Constraints":  "",
                         "DataSet":  "System.Data.DataSet",
                         "DefaultView":  "System.Data.DataRowView System.Data.DataRowView",
                         "DisplayExpression":  "",
                         "ExtendedProperties":  "System.Data.PropertyCollection",
                         "HasErrors":  false,
                         "Locale":  "",
                         "MinimumCapacity":  50,
                         "ParentRelations":  "",
                         "PrimaryKey":  "",
                         "Rows":  "System.Data.DataRow System.Data.DataRow",
                         "TableName":  "Table",
                         "Namespace":  "",
                         "Prefix":  "",
                         "Site":  null,
                         "Container":  null,
                         "DesignMode":  false
                     },
        "SoftwareVersionMajor":  13,
        "SoftwareVersionMinor":  0,
        "Position":  1,
        "BackupSetGUID":  "1326b0cb-ea0c-4a24-95b4-2e8c566190f4",
        "BackupTypeDescription":  "Transaction Log",
        "BackupPath":  "C:\\dbatools\\Test, command and conquer\\RestoreTimeDiff_1.trn",
        "BackupFinishDate":  "\/Date(1493883887000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493883887000)\/",
        "Start":  "\/Date(1493883887000)\/",
        "BackupSetId":  "1326b0cb-ea0c-4a24-95b4-2e8c566190f4",
        "Database":  "Test, command and conquer "
    },
    {
        "ServerName":  "TestSQL1\\Testinstance1,1234",
        "DatabaseName":  "Test, command and conquer ",
        "FirstLSN":  34000000016800001,
        "LastLSN":  34000000019700001,
        "CheckpointLSN":  34000000017000002,
        "DatabaseBackupLSN":  34000000006900179,
        "BackupStartDate":  "\/Date(1493884037000)\/",
        "FileList":  {
                         "CaseSensitive":  false,
                         "IsInitialized":  true,
                         "RemotingFormat":  0,
                         "ChildRelations":  "",
                         "Columns":  "LogicalName PhysicalName Type FileGroupName Size MaxSize FileId CreateLSN DropLSN UniqueId ReadOnlyLSN ReadWriteLSN BackupSizeInBytes SourceBlockSize FileGroupId LogGroupGUID DifferentialBaseLSN DifferentialBaseGUID IsReadOnly IsPresent TDEThumbprint SnapshotUrl",
                         "Constraints":  "",
                         "DataSet":  "System.Data.DataSet",
                         "DefaultView":  "System.Data.DataRowView System.Data.DataRowView",
                         "DisplayExpression":  "",
                         "ExtendedProperties":  "System.Data.PropertyCollection",
                         "HasErrors":  false,
                         "Locale":  "",
                         "MinimumCapacity":  50,
                         "ParentRelations":  "",
                         "PrimaryKey":  "",
                         "Rows":  "System.Data.DataRow System.Data.DataRow",
                         "TableName":  "Table",
                         "Namespace":  "",
                         "Prefix":  "",
                         "Site":  null,
                         "Container":  null,
                         "DesignMode":  false
                     },
        "SoftwareVersionMajor":  13,
        "SoftwareVersionMinor":  0,
        "Position":  1,
        "BackupSetGUID":  "acf4a999-e0c7-4020-bc78-80bbba09afb9",
        "BackupTypeDescription":  "Transaction Log",
        "BackupPath":  "C:\\dbatools\\Test, command and conquer\\RestoreTimeDiff_2.trn",
        "BackupFinishDate":  "\/Date(1493884037000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493884037000)\/",
        "Start":  "\/Date(1493884037000)\/",
        "BackupSetId":  "acf4a999-e0c7-4020-bc78-80bbba09afb9",
        "Database":  "Test, command and conquer "
    },
    {
        "ServerName":  "TestSQL1\\Testinstance1,1234",
        "DatabaseName":  "Test, command and conquer ",
        "FirstLSN":  34000000021300001,
        "LastLSN":  34000000023200001,
        "CheckpointLSN":  34000000021900001,
        "DatabaseBackupLSN":  34000000006900179,
        "BackupStartDate":  "\/Date(1493884338000)\/",
        "FileList":  {
                         "CaseSensitive":  false,
                         "IsInitialized":  true,
                         "RemotingFormat":  0,
                         "ChildRelations":  "",
                         "Columns":  "LogicalName PhysicalName Type FileGroupName Size MaxSize FileId CreateLSN DropLSN UniqueId ReadOnlyLSN ReadWriteLSN BackupSizeInBytes SourceBlockSize FileGroupId LogGroupGUID DifferentialBaseLSN DifferentialBaseGUID IsReadOnly IsPresent TDEThumbprint SnapshotUrl",
                         "Constraints":  "",
                         "DataSet":  "System.Data.DataSet",
                         "DefaultView":  "System.Data.DataRowView System.Data.DataRowView",
                         "DisplayExpression":  "",
                         "ExtendedProperties":  "System.Data.PropertyCollection",
                         "HasErrors":  false,
                         "Locale":  "",
                         "MinimumCapacity":  50,
                         "ParentRelations":  "",
                         "PrimaryKey":  "",
                         "Rows":  "System.Data.DataRow System.Data.DataRow",
                         "TableName":  "Table",
                         "Namespace":  "",
                         "Prefix":  "",
                         "Site":  null,
                         "Container":  null,
                         "DesignMode":  false
                     },
        "SoftwareVersionMajor":  13,
        "SoftwareVersionMinor":  0,
        "Position":  1,
        "BackupSetGUID":  "e3e4198c-da8b-434b-b320-9ee22e826fa9",
        "BackupTypeDescription":  "Transaction Log",
        "BackupPath":  "C:\\dbatools\\Test, command and conquer\\RestoreTimeDiff_21.trn",
        "BackupFinishDate":  "\/Date(1493884338000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493884338000)\/",
        "Start":  "\/Date(1493884338000)\/",
        "BackupSetId":  "e3e4198c-da8b-434b-b320-9ee22e826fa9",
        "Database":  "Test, command and conquer "
    },
    {
        "ServerName":  "TestSQL1\\Testinstance1,1234",
        "DatabaseName":  "Test, command and conquer ",
        "FirstLSN":  34000000023200001,
        "LastLSN":  34000000024800001,
        "CheckpointLSN":  34000000023400002,
        "DatabaseBackupLSN":  34000000006900179,
        "BackupStartDate":  "\/Date(1493884488000)\/",
        "FileList":  {
                         "CaseSensitive":  false,
                         "IsInitialized":  true,
                         "RemotingFormat":  0,
                         "ChildRelations":  "",
                         "Columns":  "LogicalName PhysicalName Type FileGroupName Size MaxSize FileId CreateLSN DropLSN UniqueId ReadOnlyLSN ReadWriteLSN BackupSizeInBytes SourceBlockSize FileGroupId LogGroupGUID DifferentialBaseLSN DifferentialBaseGUID IsReadOnly IsPresent TDEThumbprint SnapshotUrl",
                         "Constraints":  "",
                         "DataSet":  "System.Data.DataSet",
                         "DefaultView":  "System.Data.DataRowView System.Data.DataRowView",
                         "DisplayExpression":  "",
                         "ExtendedProperties":  "System.Data.PropertyCollection",
                         "HasErrors":  false,
                         "Locale":  "",
                         "MinimumCapacity":  50,
                         "ParentRelations":  "",
                         "PrimaryKey":  "",
                         "Rows":  "System.Data.DataRow System.Data.DataRow",
                         "TableName":  "Table",
                         "Namespace":  "",
                         "Prefix":  "",
                         "Site":  null,
                         "Container":  null,
                         "DesignMode":  false
                     },
        "SoftwareVersionMajor":  13,
        "SoftwareVersionMinor":  0,
        "Position":  1,
        "BackupSetGUID":  "b845d9c8-e9fa-49cf-9148-ed1283610935",
        "BackupTypeDescription":  "Transaction Log",
        "BackupPath":  "C:\\dbatools\\Test, command and conquer\\RestoreTimeDiff_22.trn",
        "BackupFinishDate":  "\/Date(1493884488000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493884488000)\/",
        "Start":  "\/Date(1493884488000)\/",
        "BackupSetId":  "b845d9c8-e9fa-49cf-9148-ed1283610935",
        "Database":  "Test, command and conquer "
    },
    {
        "ServerName":  "TestSQL1\\Testinstance1,1234",
        "DatabaseName":  "Test, command and conquer ",
        "FirstLSN":  34000000024800001,
        "LastLSN":  34000000026400001,
        "CheckpointLSN":  34000000025000002,
        "DatabaseBackupLSN":  34000000006900179,
        "BackupStartDate":  "\/Date(1493884638000)\/",
        "FileList":  {
                         "CaseSensitive":  false,
                         "IsInitialized":  true,
                         "RemotingFormat":  0,
                         "ChildRelations":  "",
                         "Columns":  "LogicalName PhysicalName Type FileGroupName Size MaxSize FileId CreateLSN DropLSN UniqueId ReadOnlyLSN ReadWriteLSN BackupSizeInBytes SourceBlockSize FileGroupId LogGroupGUID DifferentialBaseLSN DifferentialBaseGUID IsReadOnly IsPresent TDEThumbprint SnapshotUrl",
                         "Constraints":  "",
                         "DataSet":  "System.Data.DataSet",
                         "DefaultView":  "System.Data.DataRowView System.Data.DataRowView",
                         "DisplayExpression":  "",
                         "ExtendedProperties":  "System.Data.PropertyCollection",
                         "HasErrors":  false,
                         "Locale":  "",
                         "MinimumCapacity":  50,
                         "ParentRelations":  "",
                         "PrimaryKey":  "",
                         "Rows":  "System.Data.DataRow System.Data.DataRow",
                         "TableName":  "Table",
                         "Namespace":  "",
                         "Prefix":  "",
                         "Site":  null,
                         "Container":  null,
                         "DesignMode":  false
                     },
        "SoftwareVersionMajor":  13,
        "SoftwareVersionMinor":  0,
        "Position":  1,
        "BackupSetGUID":  "6b18433c-9178-4b47-972f-7dae2972f3a5",
        "BackupTypeDescription":  "Transaction Log",
        "BackupPath":  "C:\\dbatools\\Test, command and conquer\\RestoreTimeDiff_23.trn",
        "BackupFinishDate":  "\/Date(1493884638000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493884638000)\/",
        "Start":  "\/Date(1493884638000)\/",
        "BackupSetId":  "6b18433c-9178-4b47-972f-7dae2972f3a5",
        "Database":  "Test, command and conquer "
    },
    {
        "ServerName":  "TestSQL1\\Testinstance1,1234",
        "DatabaseName":  "Test, command and conquer ",
        "FirstLSN":  34000000026400001,
        "LastLSN":  34000000027000001,
        "CheckpointLSN":  34000000026600002,
        "DatabaseBackupLSN":  34000000006900179,
        "BackupStartDate":  "\/Date(1493884638000)\/",
        "FileList":  {
                         "CaseSensitive":  false,
                         "IsInitialized":  true,
                         "RemotingFormat":  0,
                         "ChildRelations":  "",
                         "Columns":  "LogicalName PhysicalName Type FileGroupName Size MaxSize FileId CreateLSN DropLSN UniqueId ReadOnlyLSN ReadWriteLSN BackupSizeInBytes SourceBlockSize FileGroupId LogGroupGUID DifferentialBaseLSN DifferentialBaseGUID IsReadOnly IsPresent TDEThumbprint SnapshotUrl",
                         "Constraints":  "",
                         "DataSet":  "System.Data.DataSet",
                         "DefaultView":  "System.Data.DataRowView System.Data.DataRowView",
                         "DisplayExpression":  "",
                         "ExtendedProperties":  "System.Data.PropertyCollection",
                         "HasErrors":  false,
                         "Locale":  "",
                         "MinimumCapacity":  50,
                         "ParentRelations":  "",
                         "PrimaryKey":  "",
                         "Rows":  "System.Data.DataRow System.Data.DataRow",
                         "TableName":  "Table",
                         "Namespace":  "",
                         "Prefix":  "",
                         "Site":  null,
                         "Container":  null,
                         "DesignMode":  false
                     },
        "SoftwareVersionMajor":  13,
        "SoftwareVersionMinor":  0,
        "Position":  1,
        "BackupSetGUID":  "29685266-3ec0-4c80-aa47-25fe5e59c206",
        "BackupTypeDescription":  "Transaction Log",
        "BackupPath":  "C:\\dbatools\\Test, command and conquer\\RestoreTimeDiff_24.trn",
        "BackupFinishDate":  "\/Date(1493884638000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493884638000)\/",
        "Start":  "\/Date(1493884638000)\/",
        "BackupSetId":  "29685266-3ec0-4c80-aa47-25fe5e59c206",
        "Database":  "Test, command and conquer "
    },
    {
        "ServerName":  "TestSQL1\\Testinstance1,1234",
        "DatabaseName":  "Test, command and conquer ",
        "FirstLSN":  34000000027000001,
        "LastLSN":  34000000028400001,
        "CheckpointLSN":  34000000027200002,
        "DatabaseBackupLSN":  34000000006900179,
        "BackupStartDate":  "\/Date(1493884788000)\/",
        "FileList":  {
                         "CaseSensitive":  false,
                         "IsInitialized":  true,
                         "RemotingFormat":  0,
                         "ChildRelations":  "",
                         "Columns":  "LogicalName PhysicalName Type FileGroupName Size MaxSize FileId CreateLSN DropLSN UniqueId ReadOnlyLSN ReadWriteLSN BackupSizeInBytes SourceBlockSize FileGroupId LogGroupGUID DifferentialBaseLSN DifferentialBaseGUID IsReadOnly IsPresent TDEThumbprint SnapshotUrl",
                         "Constraints":  "",
                         "DataSet":  "System.Data.DataSet",
                         "DefaultView":  "System.Data.DataRowView System.Data.DataRowView",
                         "DisplayExpression":  "",
                         "ExtendedProperties":  "System.Data.PropertyCollection",
                         "HasErrors":  false,
                         "Locale":  "",
                         "MinimumCapacity":  50,
                         "ParentRelations":  "",
                         "PrimaryKey":  "",
                         "Rows":  "System.Data.DataRow System.Data.DataRow",
                         "TableName":  "Table",
                         "Namespace":  "",
                         "Prefix":  "",
                         "Site":  null,
                         "Container":  null,
                         "DesignMode":  false
                     },
        "SoftwareVersionMajor":  13,
        "SoftwareVersionMinor":  0,
        "Position":  1,
        "BackupSetGUID":  "c29236ca-f88f-464e-ade6-34009bc1155c",
        "BackupTypeDescription":  "Transaction Log",
        "BackupPath":  "C:\\dbatools\\Test, command and conquer\\RestoreTimeDiff_25.trn",
        "BackupFinishDate":  "\/Date(1493884788000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493884788000)\/",
        "Start":  "\/Date(1493884788000)\/",
        "BackupSetId":  "c29236ca-f88f-464e-ade6-34009bc1155c",
        "Database":  "Test, command and conquer "
    },
    {
        "ServerName":  "TestSQL1\\Testinstance1,1234",
        "DatabaseName":  "Test, command and conquer ",
        "FirstLSN":  34000000019700001,
        "LastLSN":  34000000021300001,
        "CheckpointLSN":  34000000019900002,
        "DatabaseBackupLSN":  34000000006900179,
        "BackupStartDate":  "\/Date(1493884187000)\/",
        "FileList":  {
                         "CaseSensitive":  false,
                         "IsInitialized":  true,
                         "RemotingFormat":  0,
                         "ChildRelations":  "",
                         "Columns":  "LogicalName PhysicalName Type FileGroupName Size MaxSize FileId CreateLSN DropLSN UniqueId ReadOnlyLSN ReadWriteLSN BackupSizeInBytes SourceBlockSize FileGroupId LogGroupGUID DifferentialBaseLSN DifferentialBaseGUID IsReadOnly IsPresent TDEThumbprint SnapshotUrl",
                         "Constraints":  "",
                         "DataSet":  "System.Data.DataSet",
                         "DefaultView":  "System.Data.DataRowView System.Data.DataRowView",
                         "DisplayExpression":  "",
                         "ExtendedProperties":  "System.Data.PropertyCollection",
                         "HasErrors":  false,
                         "Locale":  "",
                         "MinimumCapacity":  50,
                         "ParentRelations":  "",
                         "PrimaryKey":  "",
                         "Rows":  "System.Data.DataRow System.Data.DataRow",
                         "TableName":  "Table",
                         "Namespace":  "",
                         "Prefix":  "",
                         "Site":  null,
                         "Container":  null,
                         "DesignMode":  false
                     },
        "SoftwareVersionMajor":  13,
        "SoftwareVersionMinor":  0,
        "Position":  1,
        "BackupSetGUID":  "b989fb98-32b6-4abf-8483-25af6c6f0c9c",
        "BackupTypeDescription":  "Transaction Log",
        "BackupPath":  "C:\\dbatools\\Test, command and conquer\\RestoreTimeDiff_3.trn",
        "BackupFinishDate":  "\/Date(1493884187000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493884187000)\/",
        "Start":  "\/Date(1493884187000)\/",
        "BackupSetId":  "b989fb98-32b6-4abf-8483-25af6c6f0c9c",
        "Database":  "Test, command and conquer "
    }
]
tools\dbatools\tests\ObjectDefinitions\BackupRestore\RawInput\RestoreTimeClean.xml
<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
  <Obj RefId="0">
    <TN RefId="0">
      <T>Sqlcollaborative.Dbatools.Database.BackupHistory</T>
      <T>System.Object</T>
    </TN>
    <ToString>Sqlcollaborative.Dbatools.Database.BackupHistory</ToString>
    <Props>
      <S N="ComputerName">CGE100LAP-42</S>
      <S N="InstanceName"></S>
      <S N="SqlInstance">CGE100LAP-42\SQLEXPRESS2016</S>
      <S N="Database">RestoreTimeClean</S>
      <S N="UserName">ADS\COM3MOORESM</S>
      <DT N="Start">2017-08-01T11:14:26</DT>
      <DT N="End">2017-08-01T11:14:26</DT>
      <Obj N="Duration" RefId="1">
        <TN RefId="1">
          <T>Sqlcollaborative.Dbatools.Utility.DbaTimeSpan</T>
          <T>System.Object</T>
        </TN>
        <ToString>00:00:00</ToString>
        <Props>
          <I32 N="Days">0</I32>
          <I32 N="Hours">0</I32>
          <I32 N="Milliseconds">0</I32>
          <I32 N="Minutes">0</I32>
          <I32 N="Seconds">0</I32>
          <I64 N="Ticks">0</I64>
          <Db N="TotalDays">0</Db>
          <Db N="TotalHours">0</Db>
          <Db N="TotalMilliseconds">0</Db>
          <Db N="TotalMinutes">0</Db>
          <Db N="TotalSeconds">0</Db>
        </Props>
      </Obj>
      <Obj N="Path" RefId="2">
        <TN RefId="2">
          <T>System.String[]</T>
          <T>System.Array</T>
          <T>System.Object</T>
        </TN>
        <LST>
          <S>C:\dbatools\RestoreTimeClean\RestoreTimeClean_23.trn</S>
        </LST>
      </Obj>
      <Nil N="TotalSize" />
      <S N="Type">Transaction Log</S>
      <S N="BackupSetId">01e79d9c-d181-4050-bb47-a6c4fceaca1b</S>
      <S N="DeviceType">Disk</S>
      <Nil N="Software" />
      <Obj N="FullName" RefId="3">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\RestoreTimeClean\RestoreTimeClean_23.trn</S>
        </LST>
      </Obj>
      <Obj N="FileList" RefId="4">
        <TN RefId="3">
          <T>System.Object[]</T>
          <T>System.Array</T>
          <T>System.Object</T>
        </TN>
        <LST>
          <Obj RefId="5">
            <TN RefId="4">
              <T>Selected.System.Management.Automation.PSCustomObject</T>
              <T>System.Management.Automation.PSCustomObject</T>
              <T>System.Object</T>
            </TN>
            <MS>
              <S N="Type">D</S>
              <S N="LogicalName">RestoreTimeClean</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\DATA\RestoreTimeClean.mdf</S>
            </MS>
          </Obj>
          <Obj RefId="6">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">L</S>
              <S N="LogicalName">RestoreTimeClean_log</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\data\RestoreTimeClean_log.ldf</S>
            </MS>
          </Obj>
        </LST>
      </Obj>
      <I32 N="Position">1</I32>
      <Obj N="FirstLsn" RefId="7">
        <TN RefId="5">
          <T>System.Numerics.BigInteger</T>
          <T>System.ValueType</T>
          <T>System.Object</T>
        </TN>
        <ToString>34000000024500001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="DatabaseBackupLsn" RefId="8">
        <TNRef RefId="5" />
        <ToString>34000000006900179</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="CheckpointLsn" RefId="9">
        <TNRef RefId="5" />
        <ToString>34000000024700002</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">true</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="LastLsn" RefId="10">
        <TNRef RefId="5" />
        <ToString>34000000026100001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <I32 N="SoftwareVersionMajor">13</I32>
      <B N="IsCopyOnly">false</B>
      <G N="LastRecoveryForkGUID">00000000-0000-0000-0000-000000000000</G>
    </Props>
  </Obj>
  <Obj RefId="11">
    <TNRef RefId="0" />
    <ToString>Sqlcollaborative.Dbatools.Database.BackupHistory</ToString>
    <Props>
      <S N="ComputerName">CGE100LAP-42</S>
      <S N="InstanceName"></S>
      <S N="SqlInstance">CGE100LAP-42\SQLEXPRESS2016</S>
      <S N="Database">RestoreTimeClean</S>
      <S N="UserName">Stuart</S>
      <DT N="Start">2017-08-01T11:13:36</DT>
      <DT N="End">2017-08-01T11:13:36</DT>
      <Obj N="Duration" RefId="12">
        <TNRef RefId="1" />
        <ToString>00:00:00</ToString>
        <Props>
          <I32 N="Days">0</I32>
          <I32 N="Hours">0</I32>
          <I32 N="Milliseconds">0</I32>
          <I32 N="Minutes">0</I32>
          <I32 N="Seconds">0</I32>
          <I64 N="Ticks">0</I64>
          <Db N="TotalDays">0</Db>
          <Db N="TotalHours">0</Db>
          <Db N="TotalMilliseconds">0</Db>
          <Db N="TotalMinutes">0</Db>
          <Db N="TotalSeconds">0</Db>
        </Props>
      </Obj>
      <Obj N="Path" RefId="13">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\RestoreTimeClean\RestoreTimeClean_22.trn</S>
        </LST>
      </Obj>
      <Nil N="TotalSize" />
      <S N="Type">Transaction Log</S>
      <S N="BackupSetId">5577b1b4-df72-4978-b4ae-d7de24ee68ce</S>
      <S N="DeviceType">Disk</S>
      <Nil N="Software" />
      <Obj N="FullName" RefId="14">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\RestoreTimeClean\RestoreTimeClean_22.trn</S>
        </LST>
      </Obj>
      <Obj N="FileList" RefId="15">
        <TNRef RefId="3" />
        <LST>
          <Obj RefId="16">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">D</S>
              <S N="LogicalName">RestoreTimeClean</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\DATA\RestoreTimeClean.mdf</S>
            </MS>
          </Obj>
          <Obj RefId="17">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">L</S>
              <S N="LogicalName">RestoreTimeClean_log</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\data\RestoreTimeClean_log.ldf</S>
            </MS>
          </Obj>
        </LST>
      </Obj>
      <I32 N="Position">1</I32>
      <Obj N="FirstLsn" RefId="18">
        <TNRef RefId="5" />
        <ToString>34000000022900001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="DatabaseBackupLsn" RefId="19">
        <TNRef RefId="5" />
        <ToString>34000000006900179</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="CheckpointLsn" RefId="20">
        <TNRef RefId="5" />
        <ToString>34000000023100002</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">true</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="LastLsn" RefId="21">
        <TNRef RefId="5" />
        <ToString>34000000024500001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <I32 N="SoftwareVersionMajor">13</I32>
      <B N="IsCopyOnly">false</B>
      <G N="LastRecoveryForkGUID">00000000-0000-0000-0000-000000000000</G>
    </Props>
  </Obj>
  <Obj RefId="22">
    <TNRef RefId="0" />
    <ToString>Sqlcollaborative.Dbatools.Database.BackupHistory</ToString>
    <Props>
      <S N="ComputerName">CGE100LAP-42</S>
      <S N="InstanceName"></S>
      <S N="SqlInstance">CGE100LAP-42\SQLEXPRESS2016</S>
      <S N="Database">RestoreTimeClean</S>
      <S N="UserName">ADS\COM3MOORESM</S>
      <DT N="Start">2017-08-01T11:12:45</DT>
      <DT N="End">2017-08-01T11:12:45</DT>
      <Obj N="Duration" RefId="23">
        <TNRef RefId="1" />
        <ToString>00:00:00</ToString>
        <Props>
          <I32 N="Days">0</I32>
          <I32 N="Hours">0</I32>
          <I32 N="Milliseconds">0</I32>
          <I32 N="Minutes">0</I32>
          <I32 N="Seconds">0</I32>
          <I64 N="Ticks">0</I64>
          <Db N="TotalDays">0</Db>
          <Db N="TotalHours">0</Db>
          <Db N="TotalMilliseconds">0</Db>
          <Db N="TotalMinutes">0</Db>
          <Db N="TotalSeconds">0</Db>
        </Props>
      </Obj>
      <Obj N="Path" RefId="24">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\RestoreTimeClean\RestoreTimeClean_21.trn</S>
        </LST>
      </Obj>
      <Nil N="TotalSize" />
      <S N="Type">Transaction Log</S>
      <S N="BackupSetId">16237419-48a6-47e0-920e-290325c1e9d4</S>
      <S N="DeviceType">Disk</S>
      <Nil N="Software" />
      <Obj N="FullName" RefId="25">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\RestoreTimeClean\RestoreTimeClean_21.trn</S>
        </LST>
      </Obj>
      <Obj N="FileList" RefId="26">
        <TNRef RefId="3" />
        <LST>
          <Obj RefId="27">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">D</S>
              <S N="LogicalName">RestoreTimeClean</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\DATA\RestoreTimeClean.mdf</S>
            </MS>
          </Obj>
          <Obj RefId="28">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">L</S>
              <S N="LogicalName">RestoreTimeClean_log</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\data\RestoreTimeClean_log.ldf</S>
            </MS>
          </Obj>
        </LST>
      </Obj>
      <I32 N="Position">1</I32>
      <Obj N="FirstLsn" RefId="29">
        <TNRef RefId="5" />
        <ToString>34000000021300001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="DatabaseBackupLsn" RefId="30">
        <TNRef RefId="5" />
        <ToString>34000000006900179</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="CheckpointLsn" RefId="31">
        <TNRef RefId="5" />
        <ToString>34000000021500002</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">true</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="LastLsn" RefId="32">
        <TNRef RefId="5" />
        <ToString>34000000022900001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <I32 N="SoftwareVersionMajor">13</I32>
      <B N="IsCopyOnly">false</B>
      <G N="LastRecoveryForkGUID">00000000-0000-0000-0000-000000000000</G>
    </Props>
  </Obj>
  <Obj RefId="33">
    <TNRef RefId="0" />
    <ToString>Sqlcollaborative.Dbatools.Database.BackupHistory</ToString>
    <Props>
      <S N="ComputerName">CGE100LAP-42</S>
      <S N="InstanceName"></S>
      <S N="SqlInstance">CGE100LAP-42\SQLEXPRESS2016</S>
      <S N="Database">RestoreTimeClean</S>
      <S N="UserName">ADS\COM3MOORESM</S>
      <DT N="Start">2017-08-01T11:11:55</DT>
      <DT N="End">2017-08-01T11:11:55</DT>
      <Obj N="Duration" RefId="34">
        <TNRef RefId="1" />
        <ToString>00:00:00</ToString>
        <Props>
          <I32 N="Days">0</I32>
          <I32 N="Hours">0</I32>
          <I32 N="Milliseconds">0</I32>
          <I32 N="Minutes">0</I32>
          <I32 N="Seconds">0</I32>
          <I64 N="Ticks">0</I64>
          <Db N="TotalDays">0</Db>
          <Db N="TotalHours">0</Db>
          <Db N="TotalMilliseconds">0</Db>
          <Db N="TotalMinutes">0</Db>
          <Db N="TotalSeconds">0</Db>
        </Props>
      </Obj>
      <Obj N="Path" RefId="35">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\RestoreTimeClean\RestoreTimeClean_3.trn</S>
        </LST>
      </Obj>
      <Nil N="TotalSize" />
      <S N="Type">Transaction Log</S>
      <S N="BackupSetId">ba177689-2085-4955-ac54-5631ee943f22</S>
      <S N="DeviceType">Disk</S>
      <Nil N="Software" />
      <Obj N="FullName" RefId="36">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\RestoreTimeClean\RestoreTimeClean_3.trn</S>
        </LST>
      </Obj>
      <Obj N="FileList" RefId="37">
        <TNRef RefId="3" />
        <LST>
          <Obj RefId="38">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">D</S>
              <S N="LogicalName">RestoreTimeClean</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\DATA\RestoreTimeClean.mdf</S>
            </MS>
          </Obj>
          <Obj RefId="39">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">L</S>
              <S N="LogicalName">RestoreTimeClean_log</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\data\RestoreTimeClean_log.ldf</S>
            </MS>
          </Obj>
        </LST>
      </Obj>
      <I32 N="Position">1</I32>
      <Obj N="FirstLsn" RefId="40">
        <TNRef RefId="5" />
        <ToString>34000000019700001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="DatabaseBackupLsn" RefId="41">
        <TNRef RefId="5" />
        <ToString>34000000006900179</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="CheckpointLsn" RefId="42">
        <TNRef RefId="5" />
        <ToString>34000000019900002</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">true</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="LastLsn" RefId="43">
        <TNRef RefId="5" />
        <ToString>34000000021300001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <I32 N="SoftwareVersionMajor">13</I32>
      <B N="IsCopyOnly">false</B>
      <G N="LastRecoveryForkGUID">00000000-0000-0000-0000-000000000000</G>
    </Props>
  </Obj>
  <Obj RefId="44">
    <TNRef RefId="0" />
    <ToString>Sqlcollaborative.Dbatools.Database.BackupHistory</ToString>
    <Props>
      <S N="ComputerName">CGE100LAP-42</S>
      <S N="InstanceName"></S>
      <S N="SqlInstance">CGE100LAP-42\SQLEXPRESS2016</S>
      <S N="Database">RestoreTimeClean</S>
      <S N="UserName">ADS\COM3MOORESM</S>
      <DT N="Start">2017-08-01T11:11:05</DT>
      <DT N="End">2017-08-01T11:11:05</DT>
      <Obj N="Duration" RefId="45">
        <TNRef RefId="1" />
        <ToString>00:00:00</ToString>
        <Props>
          <I32 N="Days">0</I32>
          <I32 N="Hours">0</I32>
          <I32 N="Milliseconds">0</I32>
          <I32 N="Minutes">0</I32>
          <I32 N="Seconds">0</I32>
          <I64 N="Ticks">0</I64>
          <Db N="TotalDays">0</Db>
          <Db N="TotalHours">0</Db>
          <Db N="TotalMilliseconds">0</Db>
          <Db N="TotalMinutes">0</Db>
          <Db N="TotalSeconds">0</Db>
        </Props>
      </Obj>
      <Obj N="Path" RefId="46">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\RestoreTimeClean\RestoreTimeClean_2.trn</S>
        </LST>
      </Obj>
      <Nil N="TotalSize" />
      <S N="Type">Transaction Log</S>
      <S N="BackupSetId">0c0e1500-f405-46a1-bda5-701d400fe690</S>
      <S N="DeviceType">Disk</S>
      <Nil N="Software" />
      <Obj N="FullName" RefId="47">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\RestoreTimeClean\RestoreTimeClean_2.trn</S>
        </LST>
      </Obj>
      <Obj N="FileList" RefId="48">
        <TNRef RefId="3" />
        <LST>
          <Obj RefId="49">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">D</S>
              <S N="LogicalName">RestoreTimeClean</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\DATA\RestoreTimeClean.mdf</S>
            </MS>
          </Obj>
          <Obj RefId="50">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">L</S>
              <S N="LogicalName">RestoreTimeClean_log</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\data\RestoreTimeClean_log.ldf</S>
            </MS>
          </Obj>
        </LST>
      </Obj>
      <I32 N="Position">1</I32>
      <Obj N="FirstLsn" RefId="51">
        <TNRef RefId="5" />
        <ToString>34000000016800001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="DatabaseBackupLsn" RefId="52">
        <TNRef RefId="5" />
        <ToString>34000000006900179</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="CheckpointLsn" RefId="53">
        <TNRef RefId="5" />
        <ToString>34000000017000002</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">true</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="LastLsn" RefId="54">
        <TNRef RefId="5" />
        <ToString>34000000019700001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <I32 N="SoftwareVersionMajor">13</I32>
      <B N="IsCopyOnly">false</B>
      <G N="LastRecoveryForkGUID">00000000-0000-0000-0000-000000000000</G>
    </Props>
  </Obj>
  <Obj RefId="55">
    <TNRef RefId="0" />
    <ToString>Sqlcollaborative.Dbatools.Database.BackupHistory</ToString>
    <Props>
      <S N="ComputerName">CGE100LAP-42</S>
      <S N="InstanceName"></S>
      <S N="SqlInstance">CGE100LAP-42\SQLEXPRESS2016</S>
      <S N="Database">RestoreTimeClean</S>
      <S N="UserName">ADS\COM3MOORESM</S>
      <DT N="Start">2017-08-01T11:10:15</DT>
      <DT N="End">2017-08-01T11:10:15</DT>
      <Obj N="Duration" RefId="56">
        <TNRef RefId="1" />
        <ToString>00:00:00</ToString>
        <Props>
          <I32 N="Days">0</I32>
          <I32 N="Hours">0</I32>
          <I32 N="Milliseconds">0</I32>
          <I32 N="Minutes">0</I32>
          <I32 N="Seconds">0</I32>
          <I64 N="Ticks">0</I64>
          <Db N="TotalDays">0</Db>
          <Db N="TotalHours">0</Db>
          <Db N="TotalMilliseconds">0</Db>
          <Db N="TotalMinutes">0</Db>
          <Db N="TotalSeconds">0</Db>
        </Props>
      </Obj>
      <Obj N="Path" RefId="57">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\RestoreTimeClean\RestoreTimeClean_1.trn</S>
        </LST>
      </Obj>
      <Nil N="TotalSize" />
      <S N="Type">Transaction Log</S>
      <S N="BackupSetId">293a6268-a873-4eea-8b03-e9d002459d91</S>
      <S N="DeviceType">Disk</S>
      <Nil N="Software" />
      <Obj N="FullName" RefId="58">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\RestoreTimeClean\RestoreTimeClean_1.trn</S>
        </LST>
      </Obj>
      <Obj N="FileList" RefId="59">
        <TNRef RefId="3" />
        <LST>
          <Obj RefId="60">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">D</S>
              <S N="LogicalName">RestoreTimeClean</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\DATA\RestoreTimeClean.mdf</S>
            </MS>
          </Obj>
          <Obj RefId="61">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">L</S>
              <S N="LogicalName">RestoreTimeClean_log</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\data\RestoreTimeClean_log.ldf</S>
            </MS>
          </Obj>
        </LST>
      </Obj>
      <I32 N="Position">1</I32>
      <Obj N="FirstLsn" RefId="62">
        <TNRef RefId="5" />
        <ToString>34000000006900179</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="DatabaseBackupLsn" RefId="63">
        <TNRef RefId="5" />
        <ToString>34000000006900179</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="CheckpointLsn" RefId="64">
        <TNRef RefId="5" />
        <ToString>34000000006900179</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="LastLsn" RefId="65">
        <TNRef RefId="5" />
        <ToString>34000000016800001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <I32 N="SoftwareVersionMajor">13</I32>
      <B N="IsCopyOnly">false</B>
      <G N="LastRecoveryForkGUID">00000000-0000-0000-0000-000000000000</G>
    </Props>
  </Obj>
  <Obj RefId="66">
    <TNRef RefId="0" />
    <ToString>Sqlcollaborative.Dbatools.Database.BackupHistory</ToString>
    <Props>
      <S N="ComputerName">CGE100LAP-42</S>
      <S N="InstanceName"></S>
      <S N="SqlInstance">CGE100LAP-42\SQLEXPRESS2016</S>
      <S N="Database">RestoreTimeClean</S>
      <S N="UserName">ADS\COM3MOORESM</S>
      <DT N="Start">2017-08-01T11:09:25</DT>
      <DT N="End">2017-08-01T11:09:25</DT>
      <Obj N="Duration" RefId="67">
        <TNRef RefId="1" />
        <ToString>00:00:00</ToString>
        <Props>
          <I32 N="Days">0</I32>
          <I32 N="Hours">0</I32>
          <I32 N="Milliseconds">0</I32>
          <I32 N="Minutes">0</I32>
          <I32 N="Seconds">0</I32>
          <I64 N="Ticks">0</I64>
          <Db N="TotalDays">0</Db>
          <Db N="TotalHours">0</Db>
          <Db N="TotalMilliseconds">0</Db>
          <Db N="TotalMinutes">0</Db>
          <Db N="TotalSeconds">0</Db>
        </Props>
      </Obj>
      <Obj N="Path" RefId="68">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\RestoreTimeClean\RestoreTimeClean.bak</S>
        </LST>
      </Obj>
      <Nil N="TotalSize" />
      <S N="Type">Database</S>
      <S N="BackupSetId">95383afa-d1dc-4a95-92c2-06b2ba1277db</S>
      <S N="DeviceType">Disk</S>
      <Nil N="Software" />
      <Obj N="FullName" RefId="69">
        <TNRef RefId="2" />
        <LST>
          <S>C:\dbatools\RestoreTimeClean\RestoreTimeClean.bak</S>
        </LST>
      </Obj>
      <Obj N="FileList" RefId="70">
        <TNRef RefId="3" />
        <LST>
          <Obj RefId="71">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">D</S>
              <S N="LogicalName">RestoreTimeClean</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\DATA\RestoreTimeClean.mdf</S>
            </MS>
          </Obj>
          <Obj RefId="72">
            <TNRef RefId="4" />
            <MS>
              <S N="Type">L</S>
              <S N="LogicalName">RestoreTimeClean_log</S>
              <S N="PhysicalName">C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS2016\MSSQL\data\RestoreTimeClean_log.ldf</S>
            </MS>
          </Obj>
        </LST>
      </Obj>
      <I32 N="Position">1</I32>
      <Obj N="FirstLsn" RefId="73">
        <TNRef RefId="5" />
        <ToString>34000000006900179</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="DatabaseBackupLsn" RefId="74">
        <TNRef RefId="5" />
        <ToString>0</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">true</B>
          <B N="IsOne">false</B>
          <B N="IsEven">true</B>
          <I32 N="Sign">0</I32>
        </Props>
      </Obj>
      <Obj N="CheckpointLsn" RefId="75">
        <TNRef RefId="5" />
        <ToString>34000000006900179</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <Obj N="LastLsn" RefId="76">
        <TNRef RefId="5" />
        <ToString>34000000014400001</ToString>
        <Props>
          <B N="IsPowerOfTwo">false</B>
          <B N="IsZero">false</B>
          <B N="IsOne">false</B>
          <B N="IsEven">false</B>
          <I32 N="Sign">1</I32>
        </Props>
      </Obj>
      <I32 N="SoftwareVersionMajor">13</I32>
      <B N="IsCopyOnly">false</B>
      <G N="LastRecoveryForkGUID">00000000-0000-0000-0000-000000000000</G>
    </Props>
  </Obj>
</Objs>
tools\dbatools\tests\ObjectDefinitions\BackupRestore\RawInput\TLogBWFirstLastLsn.json
[
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Database",
        "FirstLSN":  14975000000265600001,
        "LastLSN":  14975000000274400001,
        "CheckpointLSN":  14975000000265600001,
        "DatabaseBackupLSN":  14975000000265600036,
        "BackupSetGUID":  "b7e35ab2-e45f-41f8-9d8a-06a734d4771e",
        "BackupStartDate":  "\/Date(1493233263000)\/",
        "BackupFinishDate":  "\/Date(1493233263000)\/",
        "Type":  "Database",
        "End":  "\/Date(1493233263000)\/",
        "Start":  "\/Date(1493233263000)\/",
        "BackupSetId":  "b7e35ab2-e45f-41f8-9d8a-06a734d4771e",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000246400001,
        "LastLSN":  14975000000252800001,
        "CheckpointLSN":  14975000000246400001,
        "DatabaseBackupLSN":  14975000000265600001,
        "BackupSetGUID":  "f3907981-d9f0-41c3-9204-2c5535b896bc",
        "BackupStartDate":  "\/Date(1493233273000)\/",
        "BackupFinishDate":  "\/Date(1493233273000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493233273000)\/",
        "Start":  "\/Date(1493233273000)\/",
        "BackupSetId":  "f3907981-d9f0-41c3-9204-2c5535b896bc",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000252800001,
        "LastLSN":  14975000000259200001,
        "CheckpointLSN":  14975000000252800001,
        "DatabaseBackupLSN":  14975000000265600001,
        "BackupSetGUID":  "294f1724-09a3-4149-b1c7-c083a305899b",
        "BackupStartDate":  "\/Date(1493233283000)\/",
        "BackupFinishDate":  "\/Date(1493233283000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493233283000)\/",
        "Start":  "\/Date(1493233283000)\/",
        "BackupSetId":  "294f1724-09a3-4149-b1c7-c083a305899b",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000259200001,
        "LastLSN":  14975000000265600001,
        "CheckpointLSN":  14975000000259200001,
        "DatabaseBackupLSN":  14975000000265600001,
        "BackupSetGUID":  "b5ca8726-f4a9-444f-8cbc-fc5db703ceef",
        "BackupStartDate":  "\/Date(1493233293000)\/",
        "BackupFinishDate":  "\/Date(1493233293000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493233293000)\/",
        "Start":  "\/Date(1493233293000)\/",
        "BackupSetId":  "b5ca8726-f4a9-444f-8cbc-fc5db703ceef",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000265600001,
        "LastLSN":  14975000000281600001,
        "CheckpointLSN":  14975000000265600001,
        "DatabaseBackupLSN":  14975000000265600001,
        "BackupSetGUID":  "2b49fa55-b871-4dfe-a2cf-f88ec38afdbf",
        "BackupStartDate":  "\/Date(1493233303000)\/",
        "BackupFinishDate":  "\/Date(1493233303000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493233303000)\/",
        "Start":  "\/Date(1493233303000)\/",
        "BackupSetId":  "2b49fa55-b871-4dfe-a2cf-f88ec38afdbf",
        "Database":  "ETLogTest"
    },
    {
        "DatabaseName":  "ETLogTest",
        "ServerName":  "PesterDummy",
        "BackupTypeDescription":  "Transaction Log",
        "FirstLSN":  14975000000281600001,
        "LastLSN":  14975000000288000001,
        "CheckpointLSN":  14975000000281600001,
        "DatabaseBackupLSN":  14975000000265600001,
        "BackupSetGUID":  "0aa9d443-b159-4a53-be12-7d89787013d9",
        "BackupStartDate":  "\/Date(1493233313000)\/",
        "BackupFinishDate":  "\/Date(1493233313000)\/",
        "Type":  "Transaction Log",
        "End":  "\/Date(1493233313000)\/",
        "Start":  "\/Date(1493233313000)\/",
        "BackupSetId":  "0aa9d443-b159-4a53-be12-7d89787013d9",
        "Database":  "ETLogTest"
    }
]
tools\dbatools\tests\pester.groups.ps1
# this files describes which tests to run on which environment of the build matrix

$TestsRunGroups = @{
    # run on scenario 2008R2
    "2008R2"                    = 'autodetect_$script:instance1'
    # run on scenario 2016
    "2016"                      = 'autodetect_$script:instance2'
    # run on scenario 2016_2017 - tests that need developer license
    "2016_2017"                 = 'autodetect_$script:instance2,$script:instance3'
    #run on scenario service_restarts - SQL Server service tests that might disrupt other tests
    "service_restarts"             = @(
        'Start-DbaSqlService',
        'Stop-DbaSqlService',
        'Restart-DbaSqlService',
        'Get-DbaSqlService',
        'Update-DbaSqlServiceAccount',
        'Enable-DbaAgHadr',
        'Disable-DbaAgHadr',
        'Reset-DbaAdmin'
    )
    # do not run on appveyor
    # a bug in SMO prevents availability group scripting :(
    "appveyor_disabled"               = @(
    'Export-DbaAvailabilityGroup'
    )
    # do not run everywhere
    "disabled"                  = @()
}
tools\dbatools\tests\Publish-DbaDacpac.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        Get-DbaProcess -SqlInstance $script:instance1, $script:instance2 -Program 'dbatools PowerShell module - dbatools.io' | Stop-DbaProcess -WarningAction SilentlyContinue
        $dbname = "dbatoolsci_publishdacpac"
        $server = Connect-DbaInstance -SqlInstance $script:instance1
        $null = $server.Query("Create Database [$dbname]")
        $db = Get-DbaDatabase -SqlInstance $script:instance1 -Database $dbname
        $null = $db.Query("CREATE TABLE dbo.example (id int);
            INSERT dbo.example
            SELECT top 100 1
            FROM sys.objects")
        $publishprofile = New-DbaPublishProfile -SqlInstance $script:instance1 -Database $dbname -Path C:\temp
        $dacpac = Export-DbaDacpac -SqlInstance $script:instance1 -Database $dbname
    }
    AfterAll {
        Remove-DbaDatabase -SqlInstance $script:instance1, $script:instance2 -Database $dbname -Confirm:$false
        Remove-Item -Confirm:$false -Path $publishprofile.FileName -ErrorAction SilentlyContinue
    }
    if ($publishprofile.FileName -or -not $env:appveyor) {
        It "shows that the update is complete" {
            $results = $dacpac | Publish-DbaDacpac -PublishXml $publishprofile.FileName -Database $dbname -SqlInstance $script:instance2
            $results.Result -match 'Update complete.' | Should Be $true
            if (($dacpac).Path) {
                Remove-Item -Confirm:$false -Path ($dacpac).Path -ErrorAction SilentlyContinue
            }
        }
    }
}
tools\dbatools\tests\Read-DbaAuditFile.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

$base = (Get-Module -Name dbatools).ModuleBase

# Add-Type -Path "$base\bin\smo\Microsoft.SqlServer.XE.Core.dll"
# Add-Type -Path "$base\bin\smo\Microsoft.SqlServer.XEvent.Configuration.dll"
# Add-Type -Path "$base\bin\smo\Microsoft.SqlServer.XEvent.dll"
# Add-Type -Path "$base\bin\smo\Microsoft.SqlServer.XEvent.Linq.dll"

Describe "$CommandName Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        $server = Connect-DbaInstance -SqlInstance $script:instance2
        $path = $server.ErrorLogPath
        $sql = "CREATE SERVER AUDIT LoginAudit
                TO FILE (FILEPATH = N'$path',MAXSIZE = 10 MB,MAX_ROLLOVER_FILES = 1,RESERVE_DISK_SPACE = OFF)
                WITH (QUEUE_DELAY = 1000, ON_FAILURE = CONTINUE)

                CREATE SERVER AUDIT SPECIFICATION TrackAllLogins
                FOR SERVER AUDIT LoginAudit ADD (SUCCESSFUL_LOGIN_GROUP) WITH (STATE = ON)

                ALTER SERVER AUDIT LoginAudit WITH (STATE = ON)"
        $server.Query($sql)
        # generate a login
        $null = Get-DbaDatabase -SqlInstance $script:instance2
        $null = Get-DbaDatabaseFile -SqlInstance $script:instance2
        # Give it a chance to write
        Start-Sleep 2
    }
    AfterAll {
        $sql = "ALTER SERVER AUDIT SPECIFICATION TrackAllLogins WITH (STATE = OFF)
                ALTER SERVER AUDIT LoginAudit WITH (STATE = OFF)
                DROP SERVER AUDIT SPECIFICATION TrackAllLogins
                DROP SERVER AUDIT LoginAudit"
        $server.Query($sql)
    }
    Context "Verifying command output" {
        It "returns some results" {
            $results = Get-DbaServerAudit -SqlInstance $script:instance2 -Audit LoginAudit | Read-DbaAuditFile -Raw -WarningAction SilentlyContinue
            [System.Linq.Enumerable]::Count($results) -gt 1 | Should Be $true
        }
        It "returns some results" {
            $results = Get-DbaServerAudit -SqlInstance $script:instance2 -Audit LoginAudit | Read-DbaAuditFile | Select-Object -First 1
            $results.server_principal_name | Should -Not -Be $null
        }
    }
}
tools\dbatools\tests\Read-DbaTraceFile.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        $configs = $script:instance1, $script:instance2 | Get-DbaSpConfigure -ConfigName DefaultTraceEnabled
        $configs | Set-DbaSpConfigure -Value $true -WarningAction SilentlyContinue
    }
    AfterAll {
        foreach ($config in $configs) {
            if (-not $config.DefaultTraceEnabled) {
                $config | Set-DbaSpConfigure -Value $false -WarningAction SilentlyContinue
            }
        }
    }

    Context "Verifying command output" {
        It "returns results" {
            $results = $script:instance1, $script:instance2 | Get-DbaTrace -Id 1 | Read-DbaTraceFile

            $results.DatabaseName.Count | Should -BeGreaterThan 0
        }

        It "supports where for multiple servers" {
            $where = "DatabaseName is not NULL
                    and DatabaseName != 'tempdb'
                    and ApplicationName != 'SQLServerCEIP'
                    and ApplicationName != 'Report Server'
                    and ApplicationName not like 'dbatools%'
                    and ApplicationName not like 'SQLAgent%'
                    and ApplicationName not like 'Microsoft SQL Server Management Studio%'"

            # Collect the results into a variable so that the bulk import is supafast
            $results = $script:instance1, $script:instance2 | Get-DbaTrace -Id 1 | Read-DbaTraceFile -Where $where -WarningAction SilentlyContinue -WarningVariable warn
            $warn | Should -Be $null
        }
    }
}
tools\dbatools\tests\Read-DbaXEFile.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

$base = (Get-Module -Name dbatools).ModuleBase

# Add-Type -Path "$base\bin\smo\Microsoft.SqlServer.XE.Core.dll"
# Add-Type -Path "$base\bin\smo\Microsoft.SqlServer.XEvent.Configuration.dll"
# Add-Type -Path "$base\bin\smo\Microsoft.SqlServer.XEvent.dll"
# Add-Type -Path "$base\bin\smo\Microsoft.SqlServer.XEvent.Linq.dll"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    Context "Verifying command output" {
        It "returns some results" {
            $results = Get-DbaXESession -SqlInstance $script:instance2 | Read-DbaXEFile -Raw -WarningAction SilentlyContinue
            [System.Linq.Enumerable]::Count($results) -gt 1 | Should Be $true
        }
        It "returns some results" {
            $results = Get-DbaXESession -SqlInstance $script:instance2 | Read-DbaXEFile -WarningAction SilentlyContinue
            $results.Count -gt 1 | Should Be $true
        }
    }
}
tools\dbatools\tests\Remove-DbaAgentJob.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        $paramCount = 7
        $defaultParamCount = 13
        [object[]]$params = (Get-ChildItem function:\Remove-DbaAgentJob).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'Job', 'KeepHistory', 'KeepUnusedSchedule', 'Mode', 'EnableException'
        It "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}
Describe "$CommandName Integration Tests" -Tag "IntegrationTests" {
    Context "Command removes jobs" {
        BeforeAll {
            $null = New-DbaAgentSchedule -SqlInstance $script:instance3 -Schedule dbatoolsci_daily -FrequencyType Daily -FrequencyInterval Everyday -Force
            $null = New-DbaAgentJob -SqlInstance $script:instance3 -Job dbatoolsci_testjob -Schedule dbatoolsci_daily
            $null = New-DbaAgentJobStep -SqlInstance $script:instance3 -Job dbatoolsci_testjob -StepId 1 -StepName dbatoolsci_step1 -Subsystem TransactSql -Command 'select 1'
            $null = Start-DbaAgentJob -SqlInstance $script:instance3 -Job dbatoolsci_testjob
        }
        AfterAll {
            if (Get-DbaAgentSchedule -SqlInstance $script:instance3 -Schedule dbatoolsci_daily) { Remove-DbaAgentSchedule -SqlInstance $script:instance3 -Schedule dbatoolsci_daily }
        }
        $null = Remove-DbaAgentJob -SqlInstance $script:instance3 -Job dbatoolsci_testjob
        It "Should have deleted job: dbatoolsci_testjob" {
            (Get-DbaAgentJob -SqlInstance $script:instance3 -Job dbatoolsci_testjob) | Should BeNullOrEmpty
        }
        It "Should have deleted schedule: dbatoolsci_daily" {
            (Get-DbaAgentSchedule -SqlInstance $script:instance3 -Schedule dbatoolsci_daily) | Should BeNullOrEmpty
        }
        It "Should have deleted history: dbatoolsci_daily" {
            (Get-DbaAgentJobHistory -SqlInstance $script:instance3 -Job dbatoolsci_testjob) | Should BeNullOrEmpty
        }
    }
    Context "Command removes job but not schedule" {
        BeforeAll {
            $null = New-DbaAgentSchedule -SqlInstance $script:instance3 -Schedule dbatoolsci_weekly -FrequencyType Weekly -FrequencyInterval Everyday -Force
            $null = New-DbaAgentJob -SqlInstance $script:instance3 -Job dbatoolsci_testjob_schedule -Schedule dbatoolsci_weekly
            $null = New-DbaAgentJobStep -SqlInstance $script:instance3 -Job dbatoolsci_testjob_schedule -StepId 1 -StepName dbatoolsci_step1 -Subsystem TransactSql -Command 'select 1'
        }
        AfterAll {
            if (Get-DbaAgentSchedule -SqlInstance $script:instance3 -Schedule dbatoolsci_weekly) { Remove-DbaAgentSchedule -SqlInstance $script:instance3 -Schedule dbatoolsci_weekly }
        }
        $null = Remove-DbaAgentJob -SqlInstance $script:instance3 -Job dbatoolsci_testjob_schedule -KeepUnusedSchedule
        It "Should have deleted job: dbatoolsci_testjob_schedule" {
            (Get-DbaAgentJob -SqlInstance $script:instance3 -Job dbatoolsci_testjob_schedule) | Should BeNullOrEmpty
        }
        It "Should not have deleted schedule: dbatoolsci_weekly" {
            (Get-DbaAgentSchedule -SqlInstance $script:instance3 -Schedule dbatoolsci_weekly) | Should Not BeNullOrEmpty
        }
    }
    Context "Command removes job but not history" {
        BeforeAll {
            $jobId = New-DbaAgentJob -SqlInstance $script:instance3 -Job dbatoolsci_testjob_history | Select-Object -ExpandProperty JobId
            $null = New-DbaAgentJobStep -SqlInstance $script:instance3 -Job dbatoolsci_testjob_history -StepId 1 -StepName dbatoolsci_step1 -Subsystem TransactSql -Command 'select 1'
            $null = Start-DbaAgentJob -SqlInstance $script:instance3 -Job dbatoolsci_testjob_history
            $server = Connect-DbaInstance -SqlInstance $script:instance3
        }
        $null = Remove-DbaAgentJob -SqlInstance $script:instance3 -Job dbatoolsci_testjob_history -KeepHistory
        It "Should have deleted job: dbatoolsci_testjob_history" {
            (Get-DbaAgentJob -SqlInstance $script:instance3 -Job dbatoolsci_testjob_history) | Should BeNullOrEmpty
        }
        It -Skip "Should not have deleted history: dbatoolsci_testjob_history" {
            ($server.Query("select 1 from sysjobhistory where job_id = '$jobId'", "msdb")) | Should Not BeNullOrEmpty
        }
        AfterAll {
            $server.Query("delete from sysjobhistory where job_id = '$jobId'", "msdb")
        }
    }
} # $script:instance2 for appveyor
tools\dbatools\tests\Remove-DbaAgentJobCategory.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    Context "New Agent Job Category is changed properly" {

        It "Should have the right name and category type" {
            $results = New-DbaAgentJobCategory -SqlInstance $script:instance2 -Category CategoryTest1, CategoryTest2, CategoryTest3
            $results[0].Name | Should Be "CategoryTest1"
            $results[0].CategoryType | Should Be "LocalJob"
            $results[1].Name | Should Be "CategoryTest2"
            $results[1].CategoryType | Should Be "LocalJob"
            $results[2].Name | Should Be "CategoryTest3"
            $results[2].CategoryType | Should Be "LocalJob"
        }

        It "Should actually for sure exist" {
            $newresults = Get-DbaAgentJobCategory -SqlInstance $script:instance2 -Category CategoryTest1, CategoryTest2, CategoryTest3
            $newresults.Count | Should Be 3
        }

        It "Remove the job categories" {
            Remove-DbaAgentJobCategory -SqlInstance $script:instance2 -Category CategoryTest1, CategoryTest2, Categorytest3

            $newresults = Get-DbaAgentJobCategory -SqlInstance $script:instance2 -Category CategoryTest1, CategoryTest2, CategoryTest3

            $newresults.Count | Should Be 0
        }
    }
}
tools\dbatools\tests\Remove-DbaBackup.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"


Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        $paramCount = 6
        $defaultParamCount = 13
        [object[]]$params = (Get-ChildItem function:\Remove-DbaBackup).Parameters.Keys
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
    Context "It's Confirm impact should be medium" {
        $command = Get-Command Remove-DbaBackup
        $metadata = [System.Management.Automation.CommandMetadata]$command
        $metadata.ConfirmImpact | Should Be 'Medium'
    }
}


Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    $testPath = "TestDrive:\sqlbackups"
    if (!(Test-Path $testPath)) {
        New-Item -Path $testPath -ItemType Container
    }
    Context "Path validation" {
        { Remove-DbaBackup -Path 'funnypath' -BackupFileExtension 'bak' -RetentionPeriod '0d' -EnableException } | Should Throw "not found"
    }
    Context "RetentionPeriod validation" {
        { Remove-DbaBackup -Path $testPath -BackupFileExtension 'bak' -RetentionPeriod 'ad' -EnableException } | Should Throw "format invalid"
        { Remove-DbaBackup -Path $testPath -BackupFileExtension 'bak' -RetentionPeriod '11y' -EnableException } | Should Throw "units invalid"
    }
    Context "BackupFileExtension validation" {
        { Remove-DbaBackup -Path $testPath -BackupFileExtension '.bak' -RetentionPeriod '0d' -EnableException -WarningAction SilentlyContinue } | Should Not Throw
    }
    Context "BackupFileExtension message validation" {
        Remove-DbaBackup -Path $testPath -BackupFileExtension '.bak' -RetentionPeriod '0d' -WarningAction SilentlyContinue -WarningVariable warnmessage
        $warnmessage | Should -Match period
    }
    Context "Files are removed" {
        for ($i = 1; $i -le 5; $i++) {
            $filepath = Join-Path $testPath "dbatoolsci_$($i)_backup.bak"
            Set-Content $filepath -value "."
            (Get-ChildItem $filepath).LastWriteTime = (Get-Date).AddDays(-5)
        }
        It "Should remove all files with retention 0d" {
            $null = Remove-DbaBackup -Path $testPath -BackupFileExtension 'bak' -RetentionPeriod '0d'
            (Get-ChildItem -Path $testPath -File -Recurse).Count | Should Be 0
        }
    }
    Context "Files with matching extensions only are removed" {
        for ($i = 1; $i -le 5; $i++) {
            $filepath = Join-Path $testPath "dbatoolsci_$($i)_backup.bak"
            Set-Content $filepath -value "."
            (Get-ChildItem $filepath).LastWriteTime = (Get-Date).AddDays(-5)
        }
        for ($i = 1; $i -le 5; $i++) {
            $filepath = Join-Path $testPath "dbatoolsci_$($i)_backup.trn"
            Set-Content $filepath -value "."
            (Get-ChildItem $filepath).LastWriteTime = (Get-Date).AddDays(-5)
        }
        It "Should remove all files but not the trn ones" {
            $null = Remove-DbaBackup -Path $testPath -BackupFileExtension 'bak' -RetentionPeriod '0d'
            (Get-ChildItem -Path $testPath -File -Recurse).Count | Should Be 5
            (Get-ChildItem -Path $testPath -File -Recurse).Name | Should BeLike '*trn'
        }
    }
    Context "Cleanup empty folders" {
        $testPathinner_empty = "TestDrive:\sqlbackups\empty"
        if (!(Test-Path $testPathinner_empty)) {
            New-Item -Path $testPathinner_empty -ItemType Container
        }
        $testPathinner = "TestDrive:\sqlbackups\inner"
        if (!(Test-Path $testPathinner)) {
            New-Item -Path $testPathinner -ItemType Container
        }
        for ($i = 1; $i -le 5; $i++) {
            $filepath = Join-Path $testPath "dbatoolsci_$($i)_backup.bak"
            Set-Content $filepath -value "."
            (Get-ChildItem $filepath).LastWriteTime = (Get-Date).AddDays(-5)
        }
        for ($i = 1; $i -le 5; $i++) {
            $filepath = Join-Path $testPathinner "dbatoolsci_$($i)_backup.bak"
            Set-Content $filepath -value "."
            (Get-ChildItem $filepath).LastWriteTime = (Get-Date).AddDays(-5)
        }
        It "Removes files but leaves empty dirs" {
            Remove-DbaBackup -Path $testPath -BackupFileExtension 'bak' -RetentionPeriod '0d'
            (Get-ChildItem -Path $testPath -Directory -Recurse).Count | Should Be 2
        }
        It "Removes files and removes empty dirs" {
            Remove-DbaBackup -Path $testPath -BackupFileExtension 'bak' -RetentionPeriod '0d' -RemoveEmptyBackupFolder
            (Get-ChildItem -Path $testPath -Directory -Recurse).Count | Should Be 0
        }
    }
}
tools\dbatools\tests\Remove-DbaClientAlias.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $null = New-DbaClientAlias -ServerName sql2016 -Alias dbatoolscialias-new -Verbose:$false
    }
    Context "adds the alias" {
        $results = Remove-DbaClientAlias -Alias dbatoolscialias-new -Verbose:$false
        It "alias is not included in results" {
            $results.AliasName -notcontains 'dbatoolscialias-new' | Should Be $true
        }
    }
}
tools\dbatools\tests\Remove-DbaComputerCertificate.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Can remove a certificate" {
        BeforeAll {
            $null = Add-DbaComputerCertificate -Path $script:appveyorlabrepo\certificates\localhost.crt -Confirm:$false
            $thumbprint = "29C469578D6C6211076A09CEE5C5797EEA0C2713"
        }

        $results = Remove-DbaComputerCertificate -Thumbprint $thumbprint -Confirm:$false

        It "returns the store Name" {
            $results.Store -eq "LocalMachine" | Should Be $true
        }
        It "returns the folder Name" {
            $results.Folder -eq "My" | Should Be $true
        }

        It "reports the proper status of Removed" {
            $results.Status -eq "Removed" | Should Be $true
        }

        It "really removed it" {
            $results = Get-DbaComputerCertificate -Thumbprint $thumbprint
            $results | Should Be $null
        }
    }
}
tools\dbatools\tests\Remove-DbaDatabase.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Should not munge system databases unless explicitly told to." {

        $dbs = @( "master", "model", "tempdb", "msdb" )

        It "Should not attempt to remove system databases." {
            foreach ($db in $dbs) {
                $db1 = Get-DbaDatabase -SqlInstance $script:instance1 -Database $db
                Remove-DbaDatabase -Confirm:$false -SqlInstance $script:instance1 -Database $db
                $db2 = Get-DbaDatabase -SqlInstance $script:instance1 -Database $db
                $db2.Name | Should Be $db1.Name
            }
        }

        It "Should not take system databases offline or change their status." {
            foreach ($db in $dbs) {
                $db1 = Get-DbaDatabase -SqlInstance $script:instance1 -Database $db
                Remove-DbaDatabase -Confirm:$false -SqlInstance $script:instance1 -Database $db
                $db2 = Get-DbaDatabase -SqlInstance $script:instance1 -Database $db
                $db2.Status | Should Be $db1.Status
                $db2.IsAccessible | Should Be $db1.IsAccessible
            }
        }
    }
    Context "Should remove user databases and return useful errors if it cannot." {
        It "Should remove a non system database." {
            Remove-DbaDatabase -Confirm:$false -SqlInstance $script:instance1 -Database singlerestore
            Get-DbaProcess -SqlInstance $script:instance1 -Database singlerestore | Stop-DbaProcess
            Restore-DbaDatabase -SqlInstance $script:instance1 -Path $script:appveyorlabrepo\singlerestore\singlerestore.bak -WithReplace
            (Get-DbaDatabase -SqlInstance $script:instance1 -Database singlerestore).IsAccessible | Should Be $true
            Remove-DbaDatabase -Confirm:$false -SqlInstance $script:instance1 -Database singlerestore
            Get-DbaDatabase -SqlInstance $script:instance1 -Database singlerestore | Should Be $null
        }
    }
    Context "Should remove restoring database and return useful errors if it cannot." {
        It "Should remove a non system database." {
            Remove-DbaDatabase -Confirm:$false -SqlInstance $script:instance1 -Database singlerestore
            Get-DbaProcess -SqlInstance $script:instance1 -Database singlerestore | Stop-DbaProcess
            Restore-DbaDatabase -SqlInstance $script:instance1 -Path $script:appveyorlabrepo\singlerestore\singlerestore.bak -WithReplace -NoRecovery
            (Connect-DbaInstance -SqlInstance $script:instance1).Databases['singlerestore'].IsAccessible | Should Be $false
            Remove-DbaDatabase -Confirm:$false -SqlInstance $script:instance1 -Database singlerestore
            Get-DbaDatabase -SqlInstance $script:instance1 -Database singlerestore | Should Be $null
        }
    }
}
tools\dbatools\tests\Remove-DbaDatabaseSafely.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        $null = Get-DbaProcess -SqlInstance $script:instance1, $script:instance2 -Program 'dbatools PowerShell module - dbatools.io' | Stop-DbaProcess -WarningAction SilentlyContinue
        $db1 = "dbatoolsci_safely"
        $server = Connect-DbaInstance -SqlInstance $script:instance2
        $server.Query("CREATE DATABASE $db1")
        $server = Connect-DbaInstance -SqlInstance $script:instance1
        $server.Query("CREATE DATABASE $db1")
    }
    AfterAll {
        $null = Remove-DbaDatabase -Confirm:$false -SqlInstance $script:instance1, $script:instance2 -Database $db1
        $null = Remove-DbaAgentJob -Confirm:$false -SqlInstance $script:instance2 -Job 'Rationalised Database Restore Script for dbatoolsci_safely'
    }
    Context "Command actually works" {
        $results = Remove-DbaDatabaseSafely -SqlInstance $script:instance2 -Database $db1 -BackupFolder C:\temp -NoDbccCheckDb
        It "Should have database name of $db1" {
            foreach ($result in $results) {
                $result.DatabaseName | Should -Be $db1
            }
        }
        $results = Remove-DbaDatabaseSafely -SqlInstance $script:instance1 -Database $db1 -BackupFolder C:\temp -NoDbccCheckDb -WarningAction SilentlyContinue -WarningVariable warn
        It "should warn and quit" {
            $results | Should -Be $null
            $warn -match 'Failure starting SQL Agent' | Should -Be $true
        }
        
        # Add back after rewrite, this should work
        It -Skip "Should restore to another server" {
            Remove-DbaAgentJob -Confirm:$false -SqlInstance $script:instance2 -Job 'Rationalised Database Restore Script for dbatoolsci_safely'
            $results = Remove-DbaDatabaseSafely -SqlInstance $script:instance1 -Database $db1 -BackupFolder C:\temp -NoDbccCheckDb -Destination $script:instance2
            foreach ($result in $results) {
                $result.SqlInstance | Should -Be $script:instance1
                $result.TestingInstance | Should -Be $script:instance2
            }
        }
    }
}
tools\dbatools\tests\Remove-DbaDbCertificate.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Can remove a database certificate" {
        BeforeAll {
            if (-not (Get-DbaDatabaseMasterKey -SqlInstance $script:instance1 -Database master)) {
                $masterkey = New-DbaDatabaseMasterKey -SqlInstance $script:instance1 -Database master -Password $(ConvertTo-SecureString -String "GoodPass1234!" -AsPlainText -Force) -Confirm:$false
            }
        }
        AfterAll {
            if ($masterKey) { $masterkey | Remove-DbaDatabasemasterKey -Confirm:$false }
        }

        $results = New-DbaDbCertificate -SqlInstance $script:instance1 | Remove-DbaDbCertificate -Confirm:$false

        It "Successfully removes database certificate in master" {
            "$($results.Status)" -match 'Success' | Should Be $true
        }
    }
}
tools\dbatools\tests\Remove-DbaDbSnapshot.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

# Targets only instance2 because it's the only one where Snapshots can happen
Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        Get-DbaProcess -SqlInstance $script:instance2 | Where-Object Program -match dbatools | Stop-DbaProcess -Confirm:$false -WarningAction SilentlyContinue
        $server = Connect-DbaInstance -SqlInstance $script:instance2
        $db1 = "dbatoolsci_RemoveSnap"
        $db1_snap1 = "dbatoolsci_RemoveSnap_snapshotted1"
        $db1_snap2 = "dbatoolsci_RemoveSnap_snapshotted2"
        $db2 = "dbatoolsci_RemoveSnap2"
        $db2_snap1 = "dbatoolsci_RemoveSnap2_snapshotted"
        Remove-DbaDbSnapshot -SqlInstance $script:instance2 -Database $db1, $db2 -Confirm:$false
        Get-DbaDatabase -SqlInstance $script:instance2 -Database $db1, $db2 | Remove-DbaDatabase -Confirm:$false
        $server.Query("CREATE DATABASE $db1")
        $server.Query("CREATE DATABASE $db2")
    }
    AfterAll {
        Remove-DbaDbSnapshot -SqlInstance $script:instance2 -Database $db1, $db2 -Confirm:$false -ErrorAction SilentlyContinue
        Remove-DbaDatabase -Confirm:$false -SqlInstance $script:instance2 -Database $db1, $db2 -ErrorAction SilentlyContinue
    }
    Context "Parameters validation" {
        It "Stops if no Database or AllDatabases" {
            { Remove-DbaDbSnapshot -SqlInstance $script:instance2 -EnableException -WarningAction SilentlyContinue } | Should Throw "You must pipe"
        }
        It "Is nice by default" {
            { Remove-DbaDbSnapshot -SqlInstance $script:instance2 *> $null } | Should Not Throw "You must pipe"
        }
    }

    Context "Operations on snapshots" {
        BeforeEach {
            $null = New-DbaDbSnapshot -SqlInstance $script:instance2 -Database $db1 -Name $db1_snap1 -ErrorAction SilentlyContinue
            $null = New-DbaDbSnapshot -SqlInstance $script:instance2 -Database $db1 -Name $db1_snap2 -ErrorAction SilentlyContinue
            $null = New-DbaDbSnapshot -SqlInstance $script:instance2 -Database $db2 -Name $db2_snap1 -ErrorAction SilentlyContinue
        }
        AfterEach {
            Remove-DbaDbSnapshot -SqlInstance $script:instance2 -Database $db1, $db2 -Confirm:$false -ErrorAction SilentlyContinue
        }

        It "Honors the Database parameter, dropping only snapshots of that database" {
            $results = Remove-DbaDbSnapshot -SqlInstance $script:instance2 -Database $db1 -Confirm:$false
            $results.Count | Should Be 2
            $result = Remove-DbaDbSnapshot -SqlInstance $script:instance2 -Database $db2 -Confirm:$false
            $result.Name | Should Be $db2_snap1
        }

        It "Honors the ExcludeDatabase parameter, returning relevant snapshots" {
            $alldbs = (Get-DbaDatabase -SqlInstance $script:instance2 | Where-Object IsDatabaseSnapShot -eq $false | Where-Object Name -notin @($db1, $db2)).Name
            $results = Remove-DbaDbSnapshot -SqlInstance $script:instance2 -ExcludeDatabase $alldbs -Confirm:$false
            $results.Count | Should Be 3
        }
        It "Honors the Snapshot parameter" {
            $result = Remove-DbaDbSnapshot -SqlInstance $script:instance2 -Snapshot $db1_snap1
            $result.Name | Should Be $db1_snap1
        }
        It "Works with piped snapshots" {
            $result = Get-DbaDbSnapshot -SqlInstance $script:instance2 -Snapshot $db1_snap1 | Remove-DbaDbSnapshot -Confirm:$false
            $result.Name | Should Be $db1_snap1
            $result = Get-DbaDbSnapshot -SqlInstance $script:instance2 -Snapshot $db1_snap1
            $result | Should Be $null
        }
        It "Has the correct default properties" {
            $result = Remove-DbaDbSnapshot -SqlInstance $script:instance2 -Database $db2 -Confirm:$false
            $ExpectedPropsDefault = 'ComputerName', 'Name', 'InstanceName', 'SqlInstance', 'Status'
            ($result.PSStandardMembers.DefaultDisplayPropertySet.ReferencedPropertyNames | Sort-Object) | Should Be ($ExpectedPropsDefault | Sort-Object)
        }
    }
}
tools\dbatools\tests\Remove-DbaDbUser.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    Context "Verifying User is removed" {
        BeforeAll {
            $server = Connect-DbaInstance -SqlInstance $script:instance1
            $db = Get-DbaDatabase $server -Database tempdb
            $securePassword = ConvertTo-SecureString "password" -AsPlainText -Force
            $loginTest = New-DbaLogin $server -Login dbatoolsci_remove_dba_db_user -Password $securePassword
        }
        BeforeEach {
            $user = New-Object Microsoft.SqlServer.Management.SMO.User($db, $loginTest.Name)
            $user.Login = $loginTest.Name
            $user.Create()
        }
        AfterEach {
            $user = $db.Users[$loginTest.Name]
            if ($user) {
                $schemaUrns = $user.EnumOwnedObjects() | Where-Object Type -EQ Schema
                foreach ($schemaUrn in $schemaUrns) {
                    $schema = $server.GetSmoObject($schemaUrn)
                    $ownedUrns = $schema.EnumOwnedObjects()
                    foreach ($ownedUrn in $ownedUrns) {
                        $obj = $server.GetSmoObject($ownedUrn)
                        $obj.Drop()
                    }
                    $schema.Drop()
                }
                $user.Drop()
            }
        }
        AfterAll {
            if ($loginTest) {
                $loginTest.Drop()
            }
        }

        It "drops a user with no ownerships" {
            Remove-DbaDbUser $server -Database tempdb -User $user.Name
            $db.Users[$user.Name] | Should BeNullOrEmpty
        }

        It "drops a user with a schema of the same name, but no objects owned by the schema" {
            $schema = New-Object Microsoft.SqlServer.Management.SMO.Schema($db, $user.Name)
            $schema.Owner = $user.Name
            $schema.Create()
            Remove-DbaDbUser $server -Database tempdb -User $user.Name
            $db.Users[$user.Name] | Should BeNullOrEmpty
        }

        It "does NOT drop a user that owns objects other than a schema" {
            $schema = New-Object Microsoft.SqlServer.Management.SMO.Schema($db, $user.Name)
            $schema.Owner = $user.Name
            $schema.Create()
            $table = New-Object Microsoft.SqlServer.Management.SMO.Table($db, "dbtoolsci_remove_dba_db_user", $user.Name)
            $col1 = New-Object Microsoft.SqlServer.Management.SMO.Column($table, "col1", [Microsoft.SqlServer.Management.SMO.DataType]::Int)
            $table.Columns.Add($col1)
            $table.Create()
            Remove-DbaDbUser $server -Database tempdb -User $user.Name -WarningAction SilentlyContinue
            $db.Users[$user.Name] | Should Be $user
        }
    }
}
tools\dbatools\tests\Remove-DbaLogin.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $login = "dbatoolsci_removelogin"
        $password = 'MyV3ry$ecur3P@ssw0rd'
        $securePassword = ConvertTo-SecureString $password -AsPlainText -Force
        $newlogin = New-DbaLogin -SqlInstance $script:instance1 -Login $login -Password $securePassword
    }

    It "removes the login" {
        $results = Remove-DbaLogin -SqlInstance $script:instance1 -Login $login -Confirm:$false
        $results.Status -eq "Dropped"
        $login1 = Get-Dbalogin -SqlInstance $script:instance1 -login $removed
        $null -eq $login1
    }
}
tools\dbatools\tests\Remove-DbaPfDataCollectorCounter.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    BeforeEach {
        $null = Get-DbaPfDataCollectorSetTemplate -Template 'Long Running Queries' | Import-DbaPfDataCollectorSetTemplate
    }
    AfterAll {
        $null = Get-DbaPfDataCollectorSet -CollectorSet 'Long Running Queries' | Remove-DbaPfDataCollectorSet -Confirm:$false
    }
    Context "Verifying command returns all the required results" {
        It "returns the correct values" {
            $results = Get-DbaPfDataCollectorSet -CollectorSet 'Long Running Queries' | Get-DbaPfDataCollector |
            Get-DbaPfDataCollectorCounter -Counter '\LogicalDisk(*)\Avg. Disk Queue Length' |
            Remove-DbaPfDataCollectorCounter -Counter '\LogicalDisk(*)\Avg. Disk Queue Length' -Confirm:$false
            $results.DataCollectorSet | Should Be 'Long Running Queries'
            $results.Name | Should Be '\LogicalDisk(*)\Avg. Disk Queue Length'
            $results.Status | Should Be 'Removed'
        }
    }
}
tools\dbatools\tests\Remove-DbaPfDataCollectorSet.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    BeforeEach {
        $null = Get-DbaPfDataCollectorSetTemplate -Template 'Long Running Queries' | Import-DbaPfDataCollectorSetTemplate
    }
    Context "Verifying command return the proper results" {
        
        It "removes the data collector set" {
            $results = Get-DbaPfDataCollectorSet -CollectorSet 'Long Running Queries' | Remove-DbaPfDataCollectorSet -Confirm:$false
            $results.Name | Should Be 'Long Running Queries'
            $results.Status | Should Be 'Removed'
        }
        
        It "returns a result" {
            $results = Get-DbaPfDataCollectorSet -CollectorSet 'Long Running Queries'
            $results.Name | Should Be 'Long Running Queries'
        }
        
        It "returns no results" {
            $null = Remove-DbaPfDataCollectorSet -CollectorSet 'Long Running Queries' -Confirm:$false
            $results = Get-DbaPfDataCollectorSet -CollectorSet 'Long Running Queries'
            $results.Name | Should Be $null
        }
    }
}
tools\dbatools\tests\Remove-DbaRegisteredServer.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tag "IntegrationTests" {
    Context "Setup" {
        BeforeAll {
            $srvName = "dbatoolsci-server1"
            $regSrvName = "dbatoolsci-server12"
            $regSrvDesc = "dbatoolsci-server123"
            $newServer = Add-DbaRegisteredServer -SqlInstance $script:instance1 -ServerName $srvName -Name $regSrvName -Description $regSrvDesc
            
            $srvName2 = "dbatoolsci-server2"
            $regSrvName2 = "dbatoolsci-server21"
            $regSrvDesc2 = "dbatoolsci-server321"
            $newServer2 = Add-DbaRegisteredServer -SqlInstance $script:instance1 -ServerName $srvName2 -Name $regSrvName2 -Description $regSrvDesc2
        }
        AfterAll {
            Get-DbaRegisteredServer -SqlInstance $script:instance1 -Name $regSrvName, $regSrvName2, $regSrvName3 | Remove-DbaRegisteredServer -Confirm:$false
        }
        
        It "supports dropping via the pipeline" {
            $results = $newServer | Remove-DbaRegisteredServer -Confirm:$false
            $results.Name | Should -Be $regSrvName
            $results.Status | Should -Be 'Dropped'
        }
        
        It "supports dropping manually" {
            $results = Remove-DbaRegisteredServer -Confirm:$false -SqlInstance $script:instance1 -Name $regSrvName2
            $results.Name | Should -Be $regSrvName2
            $results.Status | Should -Be 'Dropped'
        }
    }
}
tools\dbatools\tests\Remove-DbaRegisteredServerGroup.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tag "IntegrationTests" {
    Context "Setup" {
        BeforeAll {
            $group = "dbatoolsci-group1"
            $newGroup = Add-DbaRegisteredServerGroup -SqlInstance $script:instance1 -Name $group
            
            $group2 = "dbatoolsci-group1a"
            $newGroup2 = Add-DbaRegisteredServerGroup -SqlInstance $script:instance1 -Name $group2
            
            $hellagroup = Get-DbaRegisteredServerGroup -SqlInstance $script:instance1 -Id 1 | Add-DbaRegisteredServerGroup -Name dbatoolsci-first | Add-DbaRegisteredServerGroup -Name dbatoolsci-second | Add-DbaRegisteredServerGroup -Name dbatoolsci-third | Add-DbaRegisteredServer -ServerName dbatoolsci-test -Description ridiculous
        }
        AfterAll {
            Get-DbaRegisteredServerGroup -SqlInstance $script:instance1 | Where-Object Name -match dbatoolsci | Remove-DbaRegisteredServerGroup -Confirm:$false
        }
        
        It "supports dropping via the pipeline" {
            $results = $newGroup | Remove-DbaRegisteredServerGroup -Confirm:$false
            $results.Name | Should -Be $group
            $results.Status | Should -Be 'Dropped'
        }
        
        It "supports dropping manually" {
            $results = Remove-DbaRegisteredServerGroup -Confirm:$false -SqlInstance $script:instance1 -Name $group2
            $results.Name | Should -Be $group2
            $results.Status | Should -Be 'Dropped'
        }
        
        It "supports hella long group name" {
            $results = Get-DbaRegisteredServerGroup -SqlInstance $script:instance1 -Group $hellagroup.Group
            $results.Name | Should -Be 'dbatoolsci-third'
        }
    }
}
tools\dbatools\tests\Remove-DbaTrace.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $sql = "-- Create a Queue
                declare @rc int
                declare @TraceID int
                declare @maxfilesize bigint
                set @maxfilesize = 5
                exec @rc = sp_trace_create @TraceID output, 0, N'C:\windows\temp\temptrace', @maxfilesize, NULL

                -- Set the events
                declare @on bit
                set @on = 1
                exec sp_trace_setevent @TraceID, 14, 1, @on
                exec sp_trace_setevent @TraceID, 14, 9, @on
                exec sp_trace_setevent @TraceID, 14, 10, @on
                exec sp_trace_setevent @TraceID, 14, 11, @on
                exec sp_trace_setevent @TraceID, 14, 6, @on
                exec sp_trace_setevent @TraceID, 14, 12, @on
                exec sp_trace_setevent @TraceID, 14, 14, @on
                exec sp_trace_setevent @TraceID, 15, 11, @on
                exec sp_trace_setevent @TraceID, 15, 6, @on
                exec sp_trace_setevent @TraceID, 15, 9, @on
                exec sp_trace_setevent @TraceID, 15, 10, @on
                exec sp_trace_setevent @TraceID, 15, 12, @on
                exec sp_trace_setevent @TraceID, 15, 13, @on
                exec sp_trace_setevent @TraceID, 15, 14, @on
                exec sp_trace_setevent @TraceID, 15, 15, @on
                exec sp_trace_setevent @TraceID, 15, 16, @on
                exec sp_trace_setevent @TraceID, 15, 17, @on
                exec sp_trace_setevent @TraceID, 15, 18, @on
                exec sp_trace_setevent @TraceID, 17, 1, @on
                exec sp_trace_setevent @TraceID, 17, 9, @on
                exec sp_trace_setevent @TraceID, 17, 10, @on
                exec sp_trace_setevent @TraceID, 17, 11, @on
                exec sp_trace_setevent @TraceID, 17, 6, @on
                exec sp_trace_setevent @TraceID, 17, 12, @on
                exec sp_trace_setevent @TraceID, 17, 14, @on
                exec sp_trace_setevent @TraceID, 10, 9, @on
                exec sp_trace_setevent @TraceID, 10, 2, @on
                exec sp_trace_setevent @TraceID, 10, 10, @on
                exec sp_trace_setevent @TraceID, 10, 6, @on
                exec sp_trace_setevent @TraceID, 10, 11, @on
                exec sp_trace_setevent @TraceID, 10, 12, @on
                exec sp_trace_setevent @TraceID, 10, 13, @on
                exec sp_trace_setevent @TraceID, 10, 14, @on
                exec sp_trace_setevent @TraceID, 10, 15, @on
                exec sp_trace_setevent @TraceID, 10, 16, @on
                exec sp_trace_setevent @TraceID, 10, 17, @on
                exec sp_trace_setevent @TraceID, 10, 18, @on
                exec sp_trace_setevent @TraceID, 12, 1, @on
                exec sp_trace_setevent @TraceID, 12, 9, @on
                exec sp_trace_setevent @TraceID, 12, 11, @on
                exec sp_trace_setevent @TraceID, 12, 6, @on
                exec sp_trace_setevent @TraceID, 12, 10, @on
                exec sp_trace_setevent @TraceID, 12, 12, @on
                exec sp_trace_setevent @TraceID, 12, 13, @on
                exec sp_trace_setevent @TraceID, 12, 14, @on
                exec sp_trace_setevent @TraceID, 12, 15, @on
                exec sp_trace_setevent @TraceID, 12, 16, @on
                exec sp_trace_setevent @TraceID, 12, 17, @on
                exec sp_trace_setevent @TraceID, 12, 18, @on
                exec sp_trace_setevent @TraceID, 13, 1, @on
                exec sp_trace_setevent @TraceID, 13, 9, @on
                exec sp_trace_setevent @TraceID, 13, 11, @on
                exec sp_trace_setevent @TraceID, 13, 6, @on
                exec sp_trace_setevent @TraceID, 13, 10, @on
                exec sp_trace_setevent @TraceID, 13, 12, @on
                exec sp_trace_setevent @TraceID, 13, 14, @on

                -- Set the Filters
                declare @intfilter int
                declare @bigintfilter bigint

                exec sp_trace_setfilter @TraceID, 10, 0, 7, N'SQL Server Profiler - 934a8575-0dc1-4937-bde1-edac1cb9691f'
                -- Set the trace status to start
                exec sp_trace_setstatus @TraceID, 1

                -- display trace id for future references
                select TraceID=@TraceID"
        $server = Connect-DbaInstance -SqlInstance $script:instance1
        $traceid = ($server.Query($sql)).TraceID
    }
    AfterAll {
        Remove-Item C:\windows\temp\temptrace.trc
    }
    Context "Test Removing Trace" {
        $results = Get-DbaTrace -SqlInstance $script:instance1 -Id $traceid | Remove-DbaTrace
        It "returns the right values" {
            $results.Id | Should Be $traceid
            $results.Status | Should Be "Stopped, closed and deleted"
        }
        It "doesn't return any result for trace file id $traceid" {
            Get-DbaTrace -SqlInstance $script:instance1 -Id $traceid | Should Be $null
        }
    }
}
tools\dbatools\tests\Remove-DbaXESession.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"


Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $null = Get-DbaXESession -SqlInstance $script:instance2 -Session 'Profiler TSQL Duration' | Remove-DbaXESession
    }
    AfterAll {
        $null = Get-DbaXESession -SqlInstance $script:instance2 -Session 'Profiler TSQL Duration' | Remove-DbaXESession
    }
    Context "Test Importing Session Template" {
        $results = Import-DbaXESessionTemplate -SqlInstance $script:instance2 -Template 'Profiler TSQL Duration'
        
        It "session should exist" {
            $results.Name | Should Be 'Profiler TSQL Duration'
        }
        
        $null = Get-DbaXESession -SqlInstance $script:instance2 -Session 'Profiler TSQL Duration' | Remove-DbaXESession
        $results = Get-DbaXESession -SqlInstance $script:instance2 -Session 'Profiler TSQL Duration'
        
        It "session should no longer exist" {
            $results.Name | Should Be $null
            $results.Status | Should Be $null
        }
    }
}
tools\dbatools\tests\Rename-DbaLogin.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $login = "dbatoolsci_renamelogin"
        $renamed = "dbatoolsci_renamelogin2"
        $password = 'MyV3ry$ecur3P@ssw0rd'
        $securePassword = ConvertTo-SecureString $password -AsPlainText -Force
        $newlogin = New-DbaLogin -SqlInstance $script:instance1 -Login $login -Password $securePassword
    }
    AfterAll {
        Stop-DbaProcess -SqlInstance $script:instance1 -Login $renamed
        (Get-Dbalogin -SqlInstance $script:instance1 -Login $renamed).Drop()
    }

    It "renames the login" {
        $results = Rename-DbaLogin -SqlInstance $script:instance1 -Login $login -NewLogin $renamed
        $results.Status -eq "Successful"
        $results.OldLogin = $login
        $results.NewLogin = $renamed
        $login1 = Get-Dbalogin -SqlInstance $script:instance1 -login $renamed
        $null -ne $login1
    }
}
tools\dbatools\tests\Repair-DbaOrphanUser.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        $paramCount = 8
        $commonParamCount = 13
        [object[]]$params = (Get-ChildItem function:\Repair-DbaOrphanUser).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'Database', 'ExcludeDatabase', 'Users', 'RemoveNotExisting', 'Force', 'EnableException'
        It "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $commonParamCount | Should Be $paramCount
        }
    }
}


Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        $loginsq = @'
CREATE LOGIN [dbatoolsci_orphan1] WITH PASSWORD = N'password1', CHECK_EXPIRATION = OFF, CHECK_POLICY = OFF;
CREATE LOGIN [dbatoolsci_orphan2] WITH PASSWORD = N'password2', CHECK_EXPIRATION = OFF, CHECK_POLICY = OFF;
CREATE LOGIN [dbatoolsci_orphan3] WITH PASSWORD = N'password3', CHECK_EXPIRATION = OFF, CHECK_POLICY = OFF;
CREATE DATABASE dbatoolsci_orphan;
'@
        $server = Connect-DbaInstance -SqlInstance $script:instance1
        $null = Remove-DbaLogin -SqlInstance $server -Login dbatoolsci_orphan1, dbatoolsci_orphan2, dbatoolsci_orphan3 -Force -Confirm:$false
        $null = Remove-DbaDatabase -SqlInstance $server -Database dbatoolsci_orphan -Confirm:$false
        $null = Invoke-DbaSqlQuery -SqlInstance $server -Query $loginsq
        $usersq = @'
CREATE USER [dbatoolsci_orphan1] FROM LOGIN [dbatoolsci_orphan1];
CREATE USER [dbatoolsci_orphan2] FROM LOGIN [dbatoolsci_orphan2];
CREATE USER [dbatoolsci_orphan3] FROM LOGIN [dbatoolsci_orphan3];
'@
        Invoke-DbaSqlQuery -SqlInstance $server -Query $usersq -Database dbatoolsci_orphan
        $dropOrphan = "DROP LOGIN [dbatoolsci_orphan1];DROP LOGIN [dbatoolsci_orphan2];"
        Invoke-DbaSqlQuery -SqlInstance $server -Query $dropOrphan
        $loginsq = @'
CREATE LOGIN [dbatoolsci_orphan1] WITH PASSWORD = N'password1', CHECK_EXPIRATION = OFF, CHECK_POLICY = OFF;
CREATE LOGIN [dbatoolsci_orphan2] WITH PASSWORD = N'password2', CHECK_EXPIRATION = OFF, CHECK_POLICY = OFF;
'@
        Invoke-DbaSqlQuery -SqlInstance $server -Query $loginsq
    }
    AfterAll {
        $server = Connect-DbaInstance -SqlInstance $script:instance1
        $null = Remove-DbaLogin -SqlInstance $server -Login dbatoolsci_orphan1, dbatoolsci_orphan2, dbatoolsci_orphan3 -Force -Confirm:$false
        $null = Remove-DbaDatabase -SqlInstance $server -Database dbatoolsci_orphan -Confirm:$false
    }
    It "shows time taken for preparation" {
        1 | Should -Be 1
    }
    $results = Repair-DbaOrphanUser -SqlInstance $script:instance1 -Database dbatoolsci_orphan
    It "Finds two orphans" {
        $results.Count | Should -Be 2
        foreach ($user in $Users) {
            $user.User | Should -BeIn @('dbatoolsci_orphan1', 'dbatoolsci_orphan2')
            $user.DatabaseName | Should -Be 'dbatoolsci_orphan'
            $user.Status | Should -Be 'Success'
        }
    }
    It "has the correct properties" {
        $result = $results[0]
        $ExpectedProps = 'ComputerName,InstanceName,SqlInstance,DatabaseName,User,Status'.Split(',')
        ($result.PsObject.Properties.Name | Sort-Object) | Should Be ($ExpectedProps | Sort-Object)
    }
    $results = Repair-DbaOrphanUser -SqlInstance $script:instance1 -Database dbatoolsci_orphan
    It "does not find any other orphan" {
        $results | Should -BeNullOrEmpty
    }
}
tools\dbatools\tests\Reset-DbaAdmin.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    AfterAll {
        Get-DbaProcess -SqlInstance $script:instance2 -Login dbatoolsci_resetadmin | Stop-DbaProcess -WarningAction SilentlyContinue
        (Get-DbaLogin -SqlInstance $script:instance2 -Login dbatoolsci_resetadmin).Drop()
    }
    Context "adds a sql login" {
        It "adds the login as sysadmin" {
            $password = ConvertTo-SecureString -Force -AsPlainText resetadmin1
            $cred = New-Object System.Management.Automation.PSCredential ("dbatoolsci_resetadmin", $password)
            Reset-DbaAdmin -SqlInstance $script:instance2 -Login dbatoolsci_resetadmin -SecurePassword $password -Confirm:$false -WarningAction SilentlyContinue
            $server = Connect-DbaInstance -SqlInstance $script:instance2 -Credential $cred
            $server.Name | Should Be $script:instance2
            $server.ConnectionContext.FixedServerRoles -match 'SysAdmin'
        }
    }
}
tools\dbatools\tests\Restart-DbaSqlService.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"
. "$PSScriptRoot\..\internal\functions\Connect-SqlInstance.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {

    Context "Command actually works" {

        $instanceName = (Connect-SqlInstance -SqlInstance $script:instance2).ServiceName
        It "restarts some services" {
            $services = Restart-DbaSqlService -ComputerName $script:instance2 -InstanceName $instanceName -Type Agent
            $services | Should Not Be $null
            foreach ($service in $services) {
                $service.State | Should Be 'Running'
                $service.Status | Should Be 'Successful'
            }
        }

        It "restarts some services through pipeline" {
            $services = Get-DbaSqlService -ComputerName $script:instance2 -InstanceName $instanceName -Type Agent, Engine | Restart-DbaSqlService
            $services | Should Not Be $null
            foreach ($service in $services) {
                $service.State | Should Be 'Running'
                $service.Status | Should Be 'Successful'
            }
        }
    }
}
tools\dbatools\tests\Restore-DbaDatabase.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tag "IntegrationTests" {
    #Setup variable for multiple contexts
    $DataFolder = 'c:\temp\datafiles'
    $LogFolder = 'C:\temp\logfiles'
    New-Item -ItemType Directory $DataFolder -ErrorAction SilentlyContinue
    New-Item -ItemType Directory $LogFolder -ErrorAction SilentlyContinue
    
    Context "Properly restores a database on the local drive using Path" {
        $null = Get-DbaDatabase -SqlInstance $script:instance1 -ExcludeAllSystemDb | Remove-DbaDatabase -Confirm:$false
        $results = Restore-DbaDatabase -SqlInstance $script:instance1 -Path $script:appveyorlabrepo\singlerestore\singlerestore.bak
        It "Should Return the proper backup file location" {
            $results.BackupFile | Should Be "$script:appveyorlabrepo\singlerestore\singlerestore.bak"
        }
        It "Should return successful restore" {
            $results.RestoreComplete | Should Be $true
        }
    }
    
    Context "Ensuring warning is thrown if database already exists" {
        $results = Restore-DbaDatabase -SqlInstance $script:instance1 -Path $script:appveyorlabrepo\singlerestore\singlerestore.bak -WarningVariable warning -WarningAction SilentlyContinue
        It "Should warn" {
            $warning | Where-Object { $_ -like '*Test-DbaBackupInformation*Database*' } | Should Match "exists and WithReplace not specified, stopping"
        }
        It "Should not return object" {
            $results | Should Be $null
        }
    }
    
    Context "Database is properly removed again after withreplace test" {
        Get-DbaProcess $script:instance1 -Database singlerestore | Stop-DbaProcess -WarningVariable warn -WarningAction SilentlyContinue
        $results = Remove-DbaDatabase -Confirm:$false -SqlInstance $script:instance1 -Database singlerestore
        Get-DbaProcess $script:instance1 -Database singlerestore | Stop-DbaProcess -WarningVariable warn -WarningAction SilentlyContinue
        $results = Remove-DbaDatabase -Confirm:$false -SqlInstance $script:instance1 -Database singlerestore
        It "Should say the status was dropped" {
            $results.Status -eq "Dropped" -or $results.Status -eq $null
        }
    }
    
    Get-DbaProcess $script:instance1 -NoSystemSpid | Stop-DbaProcess -WarningVariable warn -WarningAction SilentlyContinue
    Context "Properly restores a database on the local drive using piped Get-ChildItem results" {
        $results = Get-ChildItem $script:appveyorlabrepo\singlerestore\singlerestore.bak | Restore-DbaDatabase -SqlInstance $script:instance1
        It "Should Return the proper backup file location" {
            $results.BackupFile | Should Be "$script:appveyorlabrepo\singlerestore\singlerestore.bak"
        }
        It "Should return successful restore" {
            $results.RestoreComplete | Should Be $true
        }
    }
    
    Context "Test VerifyOnly works with db in existence" {
        $results = Get-ChildItem $script:appveyorlabrepo\singlerestore\singlerestore.bak | Restore-DbaDatabase -SqlInstance $script:instance1 -VerifyOnly
        It "Should have verified Successfully" {
            $results[0] | Should Be "Verify successful"
        }
    }
    
    Get-DbaProcess $script:instance1 -NoSystemSpid | Stop-DbaProcess -WarningVariable warn -WarningAction SilentlyContinue
    Context "Database is properly removed again after gci tests" {
        $results = Remove-DbaDatabase -Confirm:$false -SqlInstance $script:instance1 -Database singlerestore
        It "Should say the status was dropped" {
            $results.Status | Should Be "Dropped"
        }
    }
    
    Get-DbaProcess $script:instance1 -NoSystemSpid | Stop-DbaProcess -WarningVariable warn -WarningAction SilentlyContinue
    Clear-DbaSqlConnectionPool
    Start-Sleep -Seconds 2
    
    Context "Database is restored with correct renamings" {
        $results = Get-ChildItem $script:appveyorlabrepo\singlerestore\singlerestore.bak | Restore-DbaDatabase -SqlInstance $script:instance1 -DestinationFilePrefix prefix
        It "Should return successful restore with prefix" {
            $results.RestoreComplete | Should Be $true
        }
        It "Should return the 2 prefixed files" {
            (($results.RestoredFile -split ',').substring(0, 6) -eq 'prefix').count | Should be 2
        }
        $results = Get-ChildItem $script:appveyorlabrepo\singlerestore\singlerestore.bak | Restore-DbaDatabase -SqlInstance $script:instance1 -DestinationFileSuffix suffix -WithReplace
        It "Should return successful restore with suffix" {
            ($results.RestoreComplete -eq $true) | Should Be $true
        }
        It "Should return the 2 suffixed files" {
            (($Results.RestoredFile -split ',') -match "suffix\.").count | Should be 2
        }
        $results = Get-ChildItem $script:appveyorlabrepo\singlerestore\singlerestore.bak | Restore-DbaDatabase -SqlInstance $script:instance1 -DestinationFileSuffix suffix -DestinationFilePrefix prefix -WithReplace
        It "Should return successful restore with suffix and prefix" {
            ($results.RestoreComplete -eq $true) | Should Be $true
        }
        It "Should return the 2 prefixed and suffixed files" {
            (($Results.RestoredFile -split ',') -match "^prefix.*suffix\.").count | Should be 2
        }
    }
    
    Get-DbaProcess $script:instance1 -NoSystemSpid | Stop-DbaProcess -WarningVariable warn -WarningAction SilentlyContinue
    Context "Database is properly removed again post prefix and suffix tests" {
        $results = Remove-DbaDatabase -Confirm:$false -SqlInstance $script:instance1 -Database singlerestore
        It "Should say the status was dropped" {
            $results.Status | Should Be "Dropped"
        }
        
    }
    
    Context "Replace databasename in Restored File" {
        $results = Get-ChildItem $script:appveyorlabrepo\singlerestore\singlerestore.bak | Restore-DbaDatabase -SqlInstance $script:instance1 -DatabaseName Pestering -replaceDbNameInFile -WithReplace
        It "Should return the 2 files swapping singlerestore for pestering (output)" {
            (($Results.RestoredFile -split ',') -like "*pestering*").count | Should be 2
        }
        ForEach ($file in ($results.RestoredFileFull -split ',')) {
            It "$file Should exist on Filesystem" {
                $file | Should Exist
            }
        }
    }
    
    Context "Database is properly removed (name change)" {
        $results = Remove-DbaDatabase -Confirm:$false -SqlInstance $script:instance1 -Database pestering
        It "Should say the status was dropped" {
            $results.Status | Should Be "Dropped"
        }
    }
    
    Get-DbaProcess $script:instance1 -NoSystemSpid | Stop-DbaProcess -WarningVariable warn -WarningAction SilentlyContinue
    Clear-DbaSqlConnectionPool
    Start-Sleep -Seconds 2
    
    Context "Folder restore options" {
        $results = Get-ChildItem $script:appveyorlabrepo\singlerestore\singlerestore.bak | Restore-DbaDatabase -SqlInstance $script:instance1 -DestinationDataDirectory $DataFolder
        It "Should return successful restore with DestinationDataDirectory" {
            $results.RestoreComplete | Should Be $true
        }
        It "Should have moved all files to $DataFolder" {
            (($results.RestoredFileFull -split ',') -like "$DataFolder*").count | Should be 2
        }
        ForEach ($file in ($results.RestoredFileFull -split ',')) {
            It "$file Should exist on Filesystem" {
                $file | Should Exist
            }
        }
        
        $results = Get-ChildItem $script:appveyorlabrepo\singlerestore\singlerestore.bak | Restore-DbaDatabase -SqlInstance $script:instance1 -DestinationDataDirectory $DataFolder -DestinationLogDirectory $LogFolder -WithReplace
        It "Should have moved data file to $DataFolder" {
            (($results.RestoredFileFull -split ',') -like "$DataFolder*").count | Should be 1
        }
        It "Should have moved Log file to $LogFolder" {
            (($results.RestoredFileFull -split ',') -like "$LogFolder*").count | Should be 1
        }
        ForEach ($file in ($results.RestoredFileFull -split ',')) {
            It "$file Should exist on Filesystem" {
                $file | Should Exist
            }
        }
    }
    
    Context "Database is properly removed again after folder options tests" {
        $results = Remove-DbaDatabase -Confirm:$false -SqlInstance $script:instance1 -Database singlerestore
        It "Should say the status was dropped" {
            $results.Status | Should Be "Dropped"
        }
    }
    
    Clear-DbaSqlConnectionPool
    Start-Sleep -Seconds 2
    Context "Putting all restore file modification options together" {
        $results = Get-ChildItem $script:appveyorlabrepo\singlerestore\singlerestore.bak | Restore-DbaDatabase -SqlInstance $script:instance1 -DestinationDataDirectory $DataFolder -DestinationLogDirectory $LogFolder -DestinationFileSuffix Suffix -DestinationFilePrefix prefix
        It "Should return successful restore with all file mod options" {
            $results.RestoreComplete | Should Be $true
        }
        It "Should have moved data file to $DataFolder (output)" {
            (($results.RestoredFileFull -split ',') -like "$DataFolder*").count | Should be 1
        }
        It "Should have moved Log file to $LogFolder (output)" {
            (($results.RestoredFileFull -split ',') -like "$LogFolder*").count | Should be 1
        }
        It "Should return the 2 prefixed and suffixed files" {
            (($Results.RestoredFile -split ',') -match "^prefix.*suffix\.").count | Should be 2
        }
        ForEach ($file in ($results.RestoredFileFull -split ',')) {
            It "$file Should exist on Filesystem" {
                $file | Should Exist
            }
        }
    }
    
    Clear-DbaSqlConnectionPool
    Start-Sleep -Seconds 1
    Context "Database is properly removed again after all file mods test" {
        $results = Remove-DbaDatabase -Confirm:$false -SqlInstance $script:instance1 -Database singlerestore
        It "Should say the status was dropped" {
            $results.Status | Should Be "Dropped"
        }
    }
    
    Get-DbaProcess $script:instance1 -NoSystemSpid | Stop-DbaProcess -WarningVariable warn -WarningAction SilentlyContinue
    Clear-DbaSqlConnectionPool
    Start-Sleep -Seconds 5
    Clear-DbaSqlConnectionPool
    
    Context "Properly restores an instance using ola-style backups via pipe" {
        $results = Get-ChildItem $script:appveyorlabrepo\sql2008-backups | Restore-DbaDatabase -SqlInstance $script:instance1
        It "Restored files count should be the right number" {
            $results.DatabaseName.Count | Should Be 28
        }
        It "Should return successful restore" {
            ($results.RestoreComplete -contains $false) | Should Be $false
            ($results.count -gt 0) | Should be $True
        }
    }
    
    Context "Database is properly removed again after ola pipe test" {
        Get-DbaProcess $script:instance1 -NoSystemSpid | Stop-DbaProcess -WarningVariable warn -WarningAction SilentlyContinue
        $results = Get-DbaDatabase -SqlInstance $script:instance1 -ExcludeAllSystemDb | Remove-DbaDatabase -Confirm:$false
        Get-DbaProcess $script:instance1 -NoSystemSpid | Stop-DbaProcess -WarningVariable warn -WarningAction SilentlyContinue
        $results = Get-DbaDatabase -SqlInstance $script:instance1 -ExcludeAllSystemDb | Remove-DbaDatabase -Confirm:$false
        
        It "Should say the status was dropped or null" {
            foreach ($result in $results) {
                $result.Status -eq "Dropped" -or $result.Status -eq $null
            }
        }
    }
    
    Context "Properly restores an instance using ola-style backups via string" {
        $results = Restore-DbaDatabase -SqlInstance $script:instance1 -Path $script:appveyorlabrepo\sql2008-backups
        It "Restored files count should be the right number" {
            $results.DatabaseName.Count | Should Be 28
        }
        It "Should return successful restore" {
            ($results.RestoreComplete -contains $false) | Should Be $false
            ($results.count -gt 0) | Should be $True
        }
    }
    
    Get-DbaProcess $script:instance1 -NoSystemSpid | Stop-DbaProcess -WarningVariable warn -WarningAction SilentlyContinue
    
    Context "All user databases are removed post ola-style test" {
        $results = Get-DbaDatabase -SqlInstance $script:instance1 -ExcludeAllSystemDb | Remove-DbaDatabase -Confirm:$false
        It -Skip "Should say the status was dropped" {
            $results | ForEach-Object { $_.Status | Should Be "Dropped" }
        }
    }
    
    Get-DbaProcess $script:instance1 -NoSystemSpid | Stop-DbaProcess -WarningVariable warn -WarningAction SilentlyContinue
    Clear-DbaSqlConnectionPool
    Start-Sleep -Seconds 2
    
    Context "RestoreTime setup checks" {
        $results = Restore-DbaDatabase -SqlInstance $script:instance1 -path $script:appveyorlabrepo\RestoreTimeClean
        $sqlResults = Invoke-Sqlcmd2 -ServerInstance $script:instance1 -Query "select convert(datetime,convert(varchar(20),max(dt),120)) as maxdt, convert(datetime,convert(varchar(20),min(dt),120)) as mindt from RestoreTimeClean.dbo.steps"
        It "Should restore cleanly" {
            ($results.RestoreComplete -contains $false) | Should Be $false
            ($results.count -gt 0) | Should be $True
        }
        It "Should have restored 5 files" {
            $results.count | Should be 5
        }
        It "Should have restored from 2017-06-01 12:59:12" {
            $sqlResults.mindt | Should be (get-date "2017-06-01 12:59:12")
        }
        It "Should have restored to 2017-06-01 13:28:43" {
            $sqlResults.maxdt | Should be (get-date "2017-06-01 13:28:43")
        }
    }
    
    Clear-DbaSqlConnectionPool
    Start-Sleep -Seconds 1
    
    Context "All user databases are removed post RestoreTime check" {
        $results = Get-DbaDatabase -SqlInstance $script:instance1 -ExcludeAllSystemDb | Remove-DbaDatabase -Confirm:$false
        It "Should say the status was dropped" {
            Foreach ($db in $results) { $db.Status | Should Be "Dropped" }
        }
    }
    
    Clear-DbaSqlConnectionPool
    Start-Sleep -Seconds 1
    
    Context "RestoreTime point in time" {
        $results = Restore-DbaDatabase -SqlInstance $script:instance1 -path $script:appveyorlabrepo\RestoreTimeClean -RestoreTime (get-date "2017-06-01 13:22:44")
        $sqlResults = Invoke-Sqlcmd2 -ServerInstance $script:instance1 -Query "select convert(datetime,convert(varchar(20),max(dt),120)) as maxdt, convert(datetime,convert(varchar(20),min(dt),120)) as mindt from RestoreTimeClean.dbo.steps"
        It "Should have restored 4 files" {
            $results.count | Should be 4
        }
        It "Should have restored from 2017-06-01 12:59:12" {
            $sqlResults.mindt | Should be (get-date "2017-06-01 12:59:12")
        }
        It "Should have restored to 2017-06-01 13:28:43" {
            $sqlResults.maxdt | Should be (get-date "2017-06-01 13:22:43")
        }
    }
    
    Context "All user databases are removed" {
        $results = Get-DbaDatabase -SqlInstance $script:instance1 -ExcludeAllSystemDb | Remove-DbaDatabase -Confirm:$false
        It -Skip "Should say the status was dropped post point in time test" {
            Foreach ($db in $results) { $db.Status | Should Be "Dropped" }
        }
    }
    
    Clear-DbaSqlConnectionPool
    Start-Sleep -Seconds 1
    
    Context "RestoreTime point in time with Simple Model" {
        $results = Restore-DbaDatabase -SqlInstance $script:instance1 -path $script:appveyorlabrepo\sql2008-backups\SimpleRecovery\ -RestoreTime (get-date "2018-04-06 10:37:44")
        $sqlResults = Invoke-Sqlcmd2 -ServerInstance $script:instance1 -Query "select convert(datetime,convert(varchar(20),max(dt),120)) as maxdt, convert(datetime,convert(varchar(20),min(dt),120)) as mindt from SimpleBackTest.dbo.steps"
        
        It "Should have restored 2 files" {
            $results.count | Should be 2
        }
        It "Should have restored from 2018-04-06 10:30:32" {
            $sqlResults.mindt | Should be (get-date "2018-04-06 10:30:32")
        }
        It "Should have restored to 2018-04-06 10:35:02" {
            $sqlResults.maxdt | Should be (get-date "2018-04-06 10:35:02")
        }
    }
    
    Context "All user databases are removed" {
        $results = Get-DbaDatabase -SqlInstance $script:instance1 -ExcludeAllSystemDb | Remove-DbaDatabase -Confirm:$false
        It "Should say the status was dropped post point in time test" {
            Foreach ($db in $results) { $db.Status | Should Be "Dropped" }
        }
    }
    
    Clear-DbaSqlConnectionPool
    Start-Sleep -Seconds 1
    
    Context "RestoreTime point in time and continue" {
        AfterAll {
            $null = Get-DbaDatabase -SqlInstance $script:instance1 -ExcludeAllSystemDb | Remove-DbaDatabase -Confirm:$false
        }
        $Should_Run = (Connect-DbaInstance -SqlInstance $script:instance1).Version.ToString() -like '10.50*'
        if (-not ($Should_Run)) {
            It "The test can run" {
                Set-TestInconclusive -Message "a 2008R2 is strictly needed"
            }
            return
        }
        $results = Restore-DbaDatabase -SqlInstance $script:instance1 -path $script:appveyorlabrepo\RestoreTimeClean -RestoreTime (get-date "2017-06-01 13:22:44") -StandbyDirectory c:\temp
        $sqlResults = Invoke-Sqlcmd2 -ServerInstance $script:instance1 -Query "select convert(datetime,convert(varchar(20),max(dt),120)) as maxdt, convert(datetime,convert(varchar(20),min(dt),120)) as mindt from RestoreTimeClean.dbo.steps"
        It "Should have restored 4 files" {
            $results.count | Should be 4
        }
        It "Should have restored from 2017-06-01 12:59:12" {
            $sqlResults.mindt | Should be (get-date "2017-06-01 12:59:12")
        }
        It "Should have restored to 2017-06-01 13:22:43" {
            $sqlResults.maxdt | Should be (get-date "2017-06-01 13:22:43")
        }
        $results2 = Restore-DbaDatabase -SqlInstance $script:instance1 -path $script:appveyorlabrepo\RestoreTimeClean -Continue
        $sqlResults2 = Invoke-Sqlcmd2 -ServerInstance $script:instance1 -Query "select convert(datetime,convert(varchar(20),max(dt),120)) as maxdt, convert(datetime,convert(varchar(20),min(dt),120)) as mindt from RestoreTimeClean.dbo.steps"
        It "Should have restored 2 files" {
            $results2.count | Should be 2
        }
        It "Should have restored from 2017-06-01 12:59:12" {
            $sqlResults2.mindt | Should be (get-date "2017-06-01 12:59:12")
        }
        It "Should have restored to 2017-06-01 13:28:43" {
            $sqlResults2.maxdt | Should be (get-date "2017-06-01 13:28:43")
        }
        
    }
    
    Context "Backup DB For next test" {
        $null = Restore-DbaDatabase -SqlInstance $script:instance1 -path $script:appveyorlabrepo\RestoreTimeClean -RestoreTime (get-date "2017-06-01 13:22:44")
        $results = Backup-DbaDatabase -SqlInstance $script:instance1 -Database RestoreTimeClean -BackupDirectory C:\temp
        It "Should return successful backup" {
            $results.BackupComplete | Should Be $true
        }
    }
    
    Context "All user databases are removed post continue test" {
        $results = Get-DbaDatabase -SqlInstance $script:instance1 -ExcludeAllSystemDb | Remove-DbaDatabase -Confirm:$false
        It "Should say the status was dropped" {
            Foreach ($db in $results) { $db.Status | Should Be "Dropped" }
        }
    }
    
    Clear-DbaSqlConnectionPool
    Start-Sleep -Seconds 1
    
    Get-DbaProcess $script:instance1 | Where-Object Program -match 'dbatools PowerShell module - dbatools.io' | Stop-DbaProcess -WarningAction SilentlyContinue
    Context "Check Get-DbaBackupHistory pipes into Restore-DbaDatabase" {
        $history = Get-DbaBackupHistory -SqlInstance $script:instance1 -Database RestoreTimeClean -Last
        $results = $history | Restore-DbaDatabase -SqlInstance $script:instance1 -WithReplace -TrustDbBackupHistory
        It "Should have restored everything successfully" {
            ($results.RestoreComplete -contains $false) | Should be $False
            (($results | Measure-Object).count -gt 0) | Should be $True
        }
    }
    
    Clear-DbaSqlConnectionPool
    Start-Sleep -Seconds 1
    
    Context "All user databases are removed post history test" {
        $results = Get-DbaDatabase -SqlInstance $script:instance1 -ExcludeAllSystemDb | Remove-DbaDatabase -Confirm:$false
        It "Should say the status was dropped" {
            Foreach ($db in $results) { $db.Status | Should Be "Dropped" }
        }
    }
    
    Context "Restores a db with log and file files missing extensions" {
        $results = Restore-DbaDatabase -SqlInstance $script:instance1 -path $script:appveyorlabrepo\sql2008-backups\Noextension.bak -ErrorVariable Errvar -WarningVariable WarnVar
        It "Should Restore successfully" {
            ($results.RestoreComplete -contains $false) | Should Be $false
            (($results | Measure-Object).count -gt 0) | Should be $True
        }
    }
    Clear-DbaSqlConnectionPool
    Start-Sleep -Seconds 1
    
    Context "All user databases are removed post history test" {
        $results = Get-DbaDatabase -SqlInstance $script:instance1 -ExcludeAllSystemDb | Remove-DbaDatabase -Confirm:$false
        It "Should say the status was dropped" {
            Foreach ($db in $results) { $db.Status | Should Be "Dropped" }
        }
    }
    
    Context "Setup for Recovery Tests" {
        $DatabaseName = 'rectest'
        $results = Restore-DbaDatabase -SqlInstance $script:instance1 -Path $script:appveyorlabrepo\singlerestore\singlerestore.bak -NoRecovery -DatabaseName $DatabaseName -DestinationFilePrefix $DatabaseName -WithReplace
        It "Should have restored everything successfully" {
            ($results.RestoreComplete -contains $false) | Should be $False
            (($results | measure-Object).count -gt 0) | Should be $True
        }
        $check = Get-DbaDatabase -SqlInstance $script:instance1 -Database $DatabaseName
        It "Should return 1 database" {
            $check.count | Should Be 1
        }
        It "Should be a database in Restoring state" {
            $check.status | Should Be 'Restoring'
        }
    }
    
    Context "Test recovery via parameter" {
        $DatabaseName = 'rectest'
        $results = Restore-DbaDatabase -SqlInstance $script:instance1 -Recover -DatabaseName $DatabaseName
        It "Should have restored everything successfully" {
            ($results.RestoreComplete -contains $false) | Should be $False
            (($results | measure-Object).count -gt 0) | Should be $True
        }
        $check = Get-DbaDatabase -SqlInstance $script:instance1 -Database $DatabaseName
        It "Should return 1 database" {
            $check.count | Should Be 1
        }
        It "Should be a database in Restoring state" {
            'Normal' -in $check.status | Should Be $True
        }
    }
    
    Context "Setup for Recovery Tests" {
        $DatabaseName = 'rectest'
        $results = Restore-DbaDatabase -SqlInstance $script:instance1 -Path $script:appveyorlabrepo\singlerestore\singlerestore.bak -NoRecovery -DatabaseName $DatabaseName -DestinationFilePrefix $DatabaseName -WithReplace
        It "Should have restored everything successfully" {
            ($results.RestoreComplete -contains $false) | Should be $False
            (($results | measure-Object).count -gt 0) | Should be $True
        }
        $check = Get-DbaDatabase -SqlInstance $script:instance1 -Database $DatabaseName
        It "Should return 1 database" {
            $check.count | Should Be 1
        }
        It "Should be a database in Restoring state" {
            $check.status | Should Be 'Restoring'
        }
    }
    
    Context "Test recovery via pipeline" {
        $DatabaseName = 'rectest'
        $results = Get-DbaDatabase -SqlInstance $script:instance1 -Database $DatabaseName | Restore-DbaDatabase -SqlInstance $script:instance1 -Recover
        It "Should have restored everything successfully" {
            ($results.RestoreComplete -contains $false) | Should be $False
            (($results | measure-Object).count -gt 0) | Should be $True
        }
        $check = Get-DbaDatabase -SqlInstance $script:instance1 -Database $DatabaseName
        It "Should return 1 database" {
            $check.count | Should Be 1
        }
        It "Should be a database in Restoring state" {
            'Normal' -in $check.status | Should Be $True
        }
    }
    
    Context "Checking we cope with a port number (#244)" {
        $DatabaseName = 'rectest'
        $results = Restore-DbaDatabase -SqlInstance $script:instance1_detailed -Path $script:appveyorlabrepo\singlerestore\singlerestore.bak -DatabaseName $DatabaseName -DestinationFilePrefix $DatabaseName -WithReplace
        It "Should have restored everything successfully" {
            ($results.RestoreComplete -contains $false) | Should be $False
            (($results | measure-Object).count -gt 0) | Should be $True
        }
    }
    
    Context "All user databases are removed post port test" {
        $results = Get-DbaDatabase -SqlInstance $script:instance1 -ExcludeAllSystemDb | Remove-DbaDatabase -Confirm:$false
        It "Should say the status was dropped" {
            Foreach ($db in $results) { $db.Status | Should Be "Dropped" }
        }
    }
    
    Context "Checking OutputScriptOnly only outputs script" {
        $DatabaseName = 'rectestSO'
        $results = Restore-DbaDatabase -SqlInstance $script:instance1 -Path $script:appveyorlabrepo\singlerestore\singlerestore.bak -DatabaseName $DatabaseName -OutputScriptOnly
        $db = Get-DbaDatabase -SqlInstance $script:instance1 -Database $DatabaseName
        It "Should only output a script" {
            $results -match 'RESTORE DATABASE' | Should be $True
            ($null -eq $db) | Should be $True
        }
    }
    Context "Checking OutputScriptOnly only outputs script without changing state for existing dbs (#2940)" {
        $DatabaseName = 'dbatoolsci_rectestSO'
        Get-DbaDatabase -SqlInstance $script:instance1 -Database $DatabaseName | Remove-DbaDatabase -Confirm:$false
        $server = Connect-DbaInstance $script:instance1
        $server.Query("CREATE DATABASE $DatabaseName")
        $results = Restore-DbaDatabase -SqlInstance $script:instance1 -Path $script:appveyorlabrepo\singlerestore\singlerestore.bak -DatabaseName $DatabaseName -OutputScriptOnly -WithReplace
        $db = Get-DbaDatabase -SqlInstance $script:instance1 -Database $DatabaseName
        It "Should only output a script" {
            $results -match 'RESTORE DATABASE' | Should be $True
        }
        It "Doesn't change the status of the existing database" {
            $db.UserAccess | Should Be 'Multiple'
        }
        $db | Remove-DbaDatabase -Confirm:$false
    }
    Context "All user databases are removed post Output script test" {
        $results = Get-DbaDatabase -SqlInstance $script:instance1 -ExcludeAllSystemDb | Remove-DbaDatabase -Confirm:$false
        It "Should say the status was dropped" {
            Foreach ($db in $results) { $db.Status | Should Be "Dropped" }
        }
    }
    Context "Checking Output vs input" {
        $DatabaseName = 'rectestSO'
        $results = Restore-DbaDatabase -SqlInstance $script:instance1 -Path $script:appveyorlabrepo\singlerestore\singlerestore.bak -DatabaseName $DatabaseName -BufferCount 24 -MaxTransferSize 128kb -BlockSize 64kb
        
        It "Should return the destination instance" {
            $results.SqlInstance = $script:instance1
        }
        
        It "Should have a BlockSize of 65536" {
            $results.Script | Should match 'BLOCKSIZE = 65536'
        }
        
        It "Should have a BufferCount of 24" {
            $results.Script | Should match 'BUFFERCOUNT = 24'
        }
        
        It "Should have a MaxTransferSize of 131072" {
            $results.Script | Should match 'MAXTRANSFERSIZE = 131072'
        }
    }
    
    Context "All user databases are removed post Output vs Input test" {
        $results = Get-DbaDatabase -SqlInstance $script:instance1 -ExcludeAllSystemDb | Remove-DbaDatabase -Confirm:$false
        It "Should say the status was dropped" {
            Foreach ($db in $results) { $db.Status | Should Be "Dropped" }
        }
    }
    
    Context "Checking CDC parameter " {
        $output = Restore-DbaDatabase -SqlInstance $script:instance1 -Path $script:appveyorlabrepo\singlerestore\singlerestore.bak -DatabaseName $DatabaseName -OutputScriptOnly -KeepCDC -WithReplace
        It "Should have KEEP_CDC in the SQL" {
            ($output -like '*KEEP_CDC*') | Should be $True
        }
        $output = Restore-DbaDatabase -SqlInstance $script:instance1 -Path $script:appveyorlabrepo\singlerestore\singlerestore.bak -DatabaseName $DatabaseName -OutputScriptOnly -KeepCDC -WithReplace -WarningVariable warnvar -NoRecovery -WarningAction SilentlyContinue
        It "Should not output, and warn if Norecovery and KeepCDC specified" {
            ($warnvar -like '*KeepCDC cannot be specified with Norecovery or Standby as it needs recovery to work') | Should be $True
            $output | Should be $null
        }
        $output = Restore-DbaDatabase -SqlInstance $script:instance1 -Path $script:appveyorlabrepo\singlerestore\singlerestore.bak -DatabaseName $DatabaseName -OutputScriptOnly -KeepCDC -WithReplace -WarningVariable warnvar -StandbyDirectory c:\temp\ -WarningAction SilentlyContinue
        It "Should not output, and warn if StandbyDirectory and KeepCDC specified" {
            ($warnvar -like '*KeepCDC cannot be specified with Norecovery or Standby as it needs recovery to work') | Should be $True
            $output | Should be $null
        }
    }
    
    Context "Page level restores" {
        Get-DbaDatabase -SqlInstance $script:instance1 -ExcludeAllSystemDb | Remove-DbaDatabase -confirm:$false
        $null = Restore-DbaDatabase -SqlInstance $script:instance1 -Path $script:appveyorlabrepo\singlerestore\singlerestore.bak -DatabaseName PageRestore -DestinationFilePrefix PageRestore
        $sql = "alter database PageRestore set Recovery Full
        Create table testpage(
            Filler char(8000)
        )

        insert into testpage values (REPLICATE('a','8000'))
        insert into testpage values (REPLICATE('b','8000'))
        insert into testpage values (REPLICATE('c','8000'))
        insert into testpage values (REPLICATE('d','8000'))

        Backup database PageRestore to disk='c:\temp\pagerestore.bak'
        Create table #TmpIndex(
        PageFiD int,
        PagePid int,
        IAMFID int,
        IAMPid int,
        ObjectID int,
        IndexID int,
        PartitionNumber bigint,
        ParitionId bigint,
        iam_chain_type varchar(50),
        PageType int,
        IndexLevel int,
        NextPageFID int,
        NextPagePID int,
        prevPageFid int,
        PrevPagePID int
        )

        insert #TmpIndex exec ('dbcc ind(PageRestore,testpage,-1)')
        dbcc ind(PageRestore,testpage,-1)

        declare @pageid int
        select top 1 @pageid=PagePid from #TmpIndex where IAMFID is not null and IAmPID is not null

        --select * from #TmpIndex
        --pageid = 256
        alter database pagerestore set single_user with rollback immediate

        dbcc writepage(pagerestore,1,@pageid,0,1,0x41,1)
        dbcc writepage(pagerestore,1,@pageid,1,1,0x41,1)
        dbcc writepage(pagerestore,1,@pageid,2,1,0x41,1)

        alter database pagerestore set multi_user

        insert into testpage values (REPLICATE('e','8000'))

        Backup log PageRestore to disk='c:\temp\PageRestore.trn'

        insert into testpage values (REPLICATE('f','8000'))
        use master"
        $null = Invoke-Sqlcmd2 -ServerInstance $script:instance1 -Query $sql -Database Pagerestore
        $sqlResults2 = Invoke-SqlCmd2 -ServerInstance $script:instance1 -Database Master -Query "select * from pagerestore.dbo.testpage where filler like 'a%'" -ErrorVariable errvar -ErrorAction SilentlyContinue
        It "Should have warned about corruption" {
            ($errvar -match "SQL Server detected a logical consistency-based I/O error: incorrect checksum \(expected") | Should be $True
            ($null -eq $sqlResults2) | SHould be $True
        }
        $null = Get-DbaBackupHistory -SqlInstance $script:instance1 -Database pagerestore -last | Restore-DbaDatabase -SqlInstance $script:instance1 -PageRestore (Get-DbaSuspectPage -SqlInstance $script:instance1 -Database PageRestore) -TrustDbBackupHistory -AllowContinue -DatabaseName PageRestore -PageRestoreTailFolder c:\temp -ErrorAction SilentlyContinue
        $sqlResults3 = Invoke-SqlCmd2 -ServerInstance $script:instance1 -Query "select * from pagerestore.dbo.testpage where filler like 'f%'" -ErrorVariable errvar3 -ErrorAction SilentlyContinue
        It "Should work after page restore" {
            #($null -eq $errvar3) | Should Be $True
            ($null -eq $sqlResults3) | SHould be $False
        }
        
        
    }
    
    Context "Testing Backup to Restore piping" {
        Get-DbaDatabase -SqlInstance $script:instance1 -ExcludeAllSystemDb | Remove-DbaDatabase -Confirm:$false
        $null = Restore-DbaDatabase -SqlInstance $script:instance1 -Path $script:appveyorlabrepo\singlerestore\singlerestore.bak -DatabaseName PipeTest -DestinationFilePrefix PipeTest
        $results = Backup-DbaDatabase -SqlInstance $script:instance1 -Database Pipetest -BackupDirectory c:\temp -CopyOnly -WarningAction SilentlyContinue -WarningVariable bwarnvar -ErrorAction SilentlyContinue -ErrorVariable berrvar | Restore-DbaDatabase -SqlInstance $script:instance1 -DatabaseName restored -ReplaceDbNameInFile -WarningAction SilentlyContinue -WarningVariable rwarnvar -ErrorAction SilentlyContinue -ErrorVariable rerrvar
        It "Should backup and restore cleanly" {
            $results.RestoreComplete | Should Be $True
        }
    }
    
    Context "Check we restore striped database" {
        Get-DbaDatabase -SqlInstance $script:instance1 -ExcludeAllSystemDb | Remove-DbaDatabase -Confirm:$false
        $results = Restore-DbaDatabase -SqlInstance $script:instance1 -Path $script:appveyorlabrepo\sql2008-backups\RestoreTimeStripe -DatabaseName StripeTest -DestinationFilePrefix StripeTest
        It "Should backup and restore cleanly" {
            ($results | Where-Object { $_.RestoreComplete -eq $True }).count | Should Be $Results.count
        }
    }
    
    if ($env:azurepasswd) {
        Context "Restores to Azure" {
            BeforeAll {
                $server = Connect-DbaInstance -SqlInstance $script:instance2
                $sql = "CREATE CREDENTIAL [https://dbatools.blob.core.windows.net/sql] WITH IDENTITY = N'SHARED ACCESS SIGNATURE', SECRET = N'$env:azurepasswd'"
                $server.Query($sql)
                $server.Query("CREATE DATABASE dbatoolsci_azure")
            }
            AfterAll {
                $server.Query("DROP CREDENTIAL [https://dbatools.blob.core.windows.net/sql]")
                Get-DbaDatabase -SqlInstance $script:instance2 -Database "dbatoolsci_azure" | Remove-DbaDatabase -Confirm:$false
            }
            It "Should restore cleanly" {
                $results = Restore-DbaDatabase -SqlInstance $script:instance2 -WithReplace -DatabaseName dbatoolsci_azure -Path https://dbatools.blob.core.windows.net/sql/dbatoolsci_azure.bak
                $results.BackupFile | Should -Be 'https://dbatools.blob.core.windows.net/sql/dbatoolsci_azure.bak'
            }
        }
    }
    
    if ($env:azurelegacypasswd) {
        Context "Restores to Azure" {
            BeforeAll {
                $server = Connect-DbaInstance -SqlInstance $script:instance2
                $sql = "CREATE CREDENTIAL [dbatools_ci] WITH IDENTITY = N'dbatools', SECRET = N'$env:azurelegacypasswd'"
                $server.Query($sql)
                $server.Query("CREATE DATABASE dbatoolsci_azure")
            }
            AfterAll {
                $server.Query("DROP CREDENTIAL dbatools_ci")
                Get-DbaDatabase -SqlInstance $script:instance2 -Database "dbatoolsci_azure" | Remove-DbaDatabase -Confirm:$false
            }
            It "supports legacy credential setups" {
                $results = Restore-DbaDatabase -SqlInstance $script:instance2 -WithReplace -DatabaseName dbatoolsci_azure -Path https://dbatools.blob.core.windows.net/legacy/dbatoolsci_azure.bak -AzureCredential dbatools_ci
                $results.BackupFile | Should -Be 'https://dbatools.blob.core.windows.net/legacy/dbatoolsci_azure.bak'
                $results.Script -match 'CREDENTIAL' | Should -Be $true
            }
        }
    }
}
tools\dbatools\tests\Restore-DbaDbCertificate.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Can create a database certificate" {
        BeforeAll {
            $masterkey = New-DbaDatabaseMasterKey -SqlInstance $script:instance1 -Database tempdb -Password $(ConvertTo-SecureString -String "GoodPass1234!" -AsPlainText -Force) -Confirm:$false
        }
        AfterAll {
            $null = $masterkey | Remove-DbaDatabaseMasterKey -Confirm:$false
        }

        $password = ConvertTo-SecureString -AsPlainText "GoodPass1234!!" -force
        $cert = New-DbaDbCertificate -SqlInstance $script:instance1 -Database tempdb
        $backup = Backup-DbaDbCertificate -SqlInstance $script:instance1 -Database tempdb -EncryptionPassword $password
        $null = Remove-DbaDbCertificate -SqlInstance $script:instance1 -Certificate $cert.Name -Database tempdb -Confirm:$false
        $results = Restore-DbaDbCertificate -SqlInstance $script:instance1 -Path $backup.ExportPath -Password $password -Database tempdb

        It "restores the db cert" {
            $results.Parent.Name -eq 'tempdb'
            $null -ne $results.Name
            $results.PrivateKeyEncryptionType -eq "Password"
        }

        $results | Remove-DbaDbCertificate -Confirm:$false
    }
}
tools\dbatools\tests\Restore-DbaDbSnapshot.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

# Targets only instance2 because it's the only one where Snapshots can happen
Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        Get-DbaProcess -SqlInstance $script:instance2 | Where-Object Program -match dbatools | Stop-DbaProcess -Confirm:$false -WarningAction SilentlyContinue
        $server = Connect-DbaInstance -SqlInstance $script:instance2
        $db1 = "dbatoolsci_RestoreSnap1"
        $db1_snap1 = "dbatoolsci_RestoreSnap1_snapshotted1"
        $db1_snap2 = "dbatoolsci_RestoreSnap1_snapshotted2"
        $db2 = "dbatoolsci_RestoreSnap2"
        $db2_snap1 = "dbatoolsci_RestoreSnap2_snapshotted1"
        Remove-DbaDbSnapshot -SqlInstance $script:instance2 -Database $db1, $db2 -Confirm:$false
        Get-DbaDatabase -SqlInstance $script:instance2 -Database $db1, $db2 | Remove-DbaDatabase -Confirm:$false
        $server.Query("CREATE DATABASE $db1")
        $server.Query("ALTER DATABASE $db1 MODIFY FILE ( NAME = N'$($db1)_log', SIZE = 13312KB )")
        $server.Query("CREATE DATABASE $db2")
        $server.Query("CREATE TABLE [$db1].[dbo].[Example] (id int identity, name nvarchar(max))")
        $server.Query("INSERT INTO [$db1].[dbo].[Example] values ('sample')")
        $server.Query("CREATE TABLE [$db2].[dbo].[Example] (id int identity, name nvarchar(max))")
        $server.Query("INSERT INTO [$db2].[dbo].[Example] values ('sample')")
    }
    AfterAll {
        Remove-DbaDbSnapshot -SqlInstance $script:instance2 -Database $db1, $db2 -Confirm:$false -ErrorAction SilentlyContinue
        Remove-DbaDatabase -Confirm:$false -SqlInstance $script:instance2 -Database $db1, $db2 -ErrorAction SilentlyContinue
    }
    Context "Parameters validation" {
        It "Stops if no Database or Snapshot" {
            { Restore-DbaDbSnapshot -SqlInstance $script:instance2 -EnableException } | Should Throw "You must specify"
        }
        It "Is nice by default" {
            { Restore-DbaDbSnapshot -SqlInstance $script:instance2 *> $null } | Should Not Throw "You must specify"
        }
    }
    Context "Operations on snapshots" {
        BeforeEach {
            $null = New-DbaDbSnapshot -SqlInstance $script:instance2 -Database $db1 -Name $db1_snap1 -ErrorAction SilentlyContinue
            $null = New-DbaDbSnapshot -SqlInstance $script:instance2 -Database $db1 -Name $db1_snap2 -ErrorAction SilentlyContinue
            $null = New-DbaDbSnapshot -SqlInstance $script:instance2 -Database $db2 -Name $db2_snap1 -ErrorAction SilentlyContinue
        }
        AfterEach {
            Remove-DbaDbSnapshot -SqlInstance $script:instance2 -Database $db1, $db2 -Confirm:$false -ErrorAction SilentlyContinue
        }

        It "Honors the Database parameter, restoring only snapshots of that database" {
            $result = Restore-DbaDbSnapshot -SqlInstance $script:instance2 -Database $db2 -Confirm:$false -EnableException -Force
            $result.Status | Should Be "Normal"
            $result.Name | Should Be $db2

            $server.Query("INSERT INTO [$db1].[dbo].[Example] values ('sample2')")
            $result = Restore-DbaDbSnapshot -SqlInstance $script:instance2 -Database $db1 -Confirm:$false -Force
            $result.Name | Should Be $db1

            # the other snapshot has been dropped
            $result = Get-DbaDbSnapshot -SqlInstance $script:instance2 -Database $db1
            $result.Count | Should Be 1

            # the query doesn't return records inserted before the restore
            $result = Invoke-SqlCmd2 -ServerInstance $script:instance2 -Query "SELECT * FROM [$db1].[dbo].[Example]" -QueryTimeout 10 -ConnectionTimeout 10
            $result.id | Should Be 1
        }

        It "Honors the Snapshot parameter" {
            $result = Restore-DbaDbSnapshot -SqlInstance $script:instance2 -Snapshot $db1_snap1 -Confirm:$false -EnableException -Force
            $result.Name | Should Be $db1
            $result.Status | Should Be "Normal"

            # the other snapshot has been dropped
            $result = Get-DbaDbSnapshot -SqlInstance $script:instance2 -Database $db1
            $result.SnapshotOf | Should Be $db1
            $result.Database.Name | Should Be $db1_snap

            # the log size has been restored to the correct size
            $server.databases[$db1].Logfiles.Size | Should Be 13312
        }

        It "Stops if multiple snapshot for the same db are passed" {
            $result = Restore-DbaDbSnapshot -SqlInstance $script:instance2 -Snapshot $db1_snap1, $db1_snap2 -Confirm:$false *> $null
            $result | Should Be $null
        }

        It "has the correct default properties" {
            $result = Get-DbaDbSnapshot -SqlInstance $script:instance2 -Database $db2
            $ExpectedPropsDefault = 'ComputerName', 'CreateDate', 'InstanceName', 'Name', 'SnapshotOf', 'SqlInstance', 'DiskUsage'
            ($result.PSStandardMembers.DefaultDisplayPropertySet.ReferencedPropertyNames | Sort-Object) | Should Be ($ExpectedPropsDefault | Sort-Object)
        }
    }
}
tools\dbatools\tests\Select-DbaBackupInformation.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Unit Tests" -Tag 'UnitTests' {
    InModuleScope dbatools {
        Context "Empty TLog Backup Issues" {
               $Header = ConvertFrom-Json -InputObject (Get-Content $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\EmptyTlogData.json -raw)
            $header | Add-Member -Type NoteProperty -Name FullName -Value 1
            $Output = Select-DbaBackupInformation -BackupHistory $header #-EnableException:$true

            It "Should return an array of 3 items" {
                $Output.count | Should be 2
            }
            It "Should return 1 Full backups" {
                ($Output | Where-Object { $_.BackupTypeDescription -eq 'Database' } | Measure-Object).count | Should Be 1
            }
            It "Should return 0 Diff backups" {
                ($Output | Where-Object { $_.BackupTypeDescription -eq 'Database Differential' } | Measure-Object).count | Should Be 0
            }
            It "Should return 2 log backups" {
                ($Output | Where-Object { $_.BackupTypeDescription -eq 'Transaction Log' } | Measure-Object).count | Should Be 1
            }
        }
        Context "General Diff Restore" {
            $Header = ConvertFrom-Json -InputObject (Get-Content $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\DiffRestore.json -raw)
            $header | Add-Member -Type NoteProperty -Name FullName -Value 1
            $Output = Select-DbaBackupInformation -BackupHistory $header -EnableException:$true

            It "Should return an array of 7 items" {
                $Output.count | Should be 7
            }
            It "Should return 1 Full backups" {
                ($Output | Where-Object { $_.BackupTypeDescription -eq 'Database' } | Measure-Object).count | Should Be 1
            }
            It "Should return 1 Diff backups" {
                ($Output | Where-Object { $_.BackupTypeDescription -eq 'Database Differential' } | Measure-Object).count | Should Be 1
            }
            It "Should return 5 log backups" {
                ($Output | Where-Object { $_.BackupTypeDescription -eq 'Transaction Log' } | Measure-Object).count | Should Be 5
            }
        }


        Context "General Diff Restore from Pipeline" {
            $Header = ConvertFrom-Json -InputObject (Get-Content $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\DiffRestore.json -raw)
            $header | Add-Member -Type NoteProperty -Name FullName -Value 1
            $Output = $Header | Select-DbaBackupInformation -EnableException:$true

            It "Should return an array of 7 items" {
                $Output.count | Should be 7
            }
            It "Should return 1 Full backups" {
                ($Output | Where-Object { $_.BackupTypeDescription -eq 'Database' } | Measure-Object).count | Should Be 1
            }
            It "Should return 1 Diff backups" {
                ($Output | Where-Object { $_.BackupTypeDescription -eq 'Database Differential' } | Measure-Object).count | Should Be 1
            }
            It "Should return 5 log backups" {
                ($Output | Where-Object { $_.BackupTypeDescription -eq 'Transaction Log' } | Measure-Object).count | Should Be 5
            }
        }
        Context "General Diff Restore from Pipeline with IgnoreDiff" {
            $Header = ConvertFrom-Json -InputObject (Get-Content $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\DiffRestore.json -raw)
            $header | Add-Member -Type NoteProperty -Name FullName -Value 1
            $Output = $Header | Select-DbaBackupInformation -EnableException:$true -IgnoreDiff

            It "Should return an array of 9 items" {
                $Output.count | Should be 9
            }
            It "Should return 1 Full backups" {
                ($Output | Where-Object { $_.BackupTypeDescription -eq 'Database' } | Measure-Object).count | Should Be 1
            }
            It "Should return 0 Diff backups" {
                ($Output | Where-Object { $_.BackupTypeDescription -eq 'Database Differential' } | Measure-Object).count | Should Be 0
            }
            It "Should return 8 log backups" {
                ($Output | Where-Object { $_.BackupTypeDescription -eq 'Transaction Log' } | Measure-Object).count | Should Be 8
            }
        }
        Context "General Diff Restore from Pipeline with IgnoreLog" {
            $Header = ConvertFrom-Json -InputObject (Get-Content $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\DiffRestore.json -raw)
            $header | Add-Member -Type NoteProperty -Name FullName -Value 1
            $Output = $Header | Select-DbaBackupInformation -EnableException:$true -IgnoreLogs

            It "Should return an array of 2 items" {
                $Output.count | Should be 2
            }
            It "Should return 1 Full backups" {
                ($Output | Where-Object { $_.BackupTypeDescription -eq 'Database' } | Measure-Object).count | Should Be 1
            }
            It "Should return 1 Diff backups" {
                ($Output | Where-Object { $_.BackupTypeDescription -eq 'Database Differential' } | Measure-Object).count | Should Be 1
            }
            It "Should return 0 log backups" {
                ($Output | Where-Object { $_.BackupTypeDescription -eq 'Transaction Log' } | Measure-Object).count | Should Be 0
            }
        }
        Context "Server/database names and file paths have commas and spaces" {
            $Header = ConvertFrom-Json -InputObject (Get-Content $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\RestoreCommaIssues.json -raw)
            $header | Add-Member -Type NoteProperty -Name FullName -Value $_.BackupPath

            $Output = Select-DbaBackupInformation -BackupHistory $header

            It "Should return an array of 7 items" {
                $Output.count | Should be 7
            }
            It "Should return 1 Full backups" {
                ($Output | Where-Object { $_.BackupTypeDescription -eq 'Database' } | Measure-Object).count | Should Be 1
            }
            It "Should return 1 Diff backups" {
                ($Output | Where-Object { $_.BackupTypeDescription -eq 'Database Differential' } | Measure-Object).count | Should Be 1
            }
            It "Should return 5 log backups" {
                ($Output | Where-Object { $_.BackupTypeDescription -eq 'Transaction Log' } | Measure-Object).count | Should Be 5
            }
        }
        Context "Missing Diff Restore" {
            $Header = ConvertFrom-Json -InputObject (Get-Content $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\DiffRestore.json -raw)
            $header = $header | Where-Object { $_.BackupTypeDescription -ne 'Database Differential' }
            $header | Add-Member -Type NoteProperty -Name FullName -Value 1

            $Output = Select-DbaBackupInformation  -BackupHistory $header

            It "Should return an array of 9 items" {
                $Output.count | Should be 9
            }
            It "Should return 1 Full backups" {
                ($Output | Where-Object { $_.BackupTypeDescription -eq 'Database' } | Measure-Object).count | Should Be 1
            }
            It "Should return 0 Diff backups" {
                ($Output | Where-Object { $_.BackupTypeDescription -eq 'Database Differential' } | Measure-Object).count | Should Be 0
            }
            It "Should return 8 log backups" {
                ($Output | Where-Object { $_.BackupTypeDescription -eq 'Transaction Log' } | Measure-Object).count | Should Be 8
            }
        }
        Context "Overlapping Diff and log Restore" {
            $Header = ConvertFrom-Json -InputObject (Get-Content $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\DiffIssues.json -raw)
            $header | Add-Member -Type NoteProperty -Name FullName -Value 1

            $RestoreDate = Get-date "2017-07-18 09:00:00"
            $Output = Select-DbaBackupInformation  -BackupHistory $Header -RestoreTime $RestoreDate

            It "Should return an array of 193 items" {
                $Output.count | Should be 194
            }
            It "Should return 1 Full backups" {
                ($Output | Where-Object { $_.BackupTypeDescription -eq 'Database' } | Measure-Object).count | Should Be 1
            }
            It "Should return 1 Diff backups" {
                ($Output | Where-Object { $_.BackupTypeDescription -eq 'Database Differential' } | Measure-Object).count | Should Be 1
            }
            It "Should return 192 log backups" {
                ($Output | Where-Object { $_.BackupTypeDescription -eq 'Transaction Log' } | Measure-Object).count | Should Be 192
            }
            It "Should not contain the Log backup with LastLsn 17126786000011867500001 " {
                ($Output | Where-Object { $_.LastLsn -eq '17126786000011867500001' } | Measure-Object).count | Should Be 0
            }
        }
        Context "When FirstLSN ne CheckPointLsn" {
            $Header = ConvertFrom-Json -InputObject (Get-Content $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\chkptLSN-ne-firstLSN.json -raw)
            $header | Add-Member -Type NoteProperty -Name FullName -Value 1

            $RestoreDate = Get-date "2017-07-18 09:00:00"
            $Output = Select-DbaBackupInformation  -BackupHistory $Header -RestoreTime $RestoreDate

            It "Should return an array of 193 items" {
                $Output.count | Should be 194
            }
            It "Should return 1 Full backups" {
                ($Output | Where-Object { $_.BackupTypeDescription -eq 'Database' } | Measure-Object).count | Should Be 1
            }
            It "Should return 1 Diff backups" {
                ($Output | Where-Object { $_.BackupTypeDescription -eq 'Database Differential' } | Measure-Object).count | Should Be 1
            }
            It "Should return 191 log backups" {
                ($Output | Where-Object { $_.BackupTypeDescription -eq 'Transaction Log' } | Measure-Object).count | Should Be 192
            }
            It "Should not contain the Log backup with LastLsn 17126786000011867500001 " {
                ($Output | Where-Object { $_.LastLsn -eq '17126786000011867500001' } | Measure-Object).count | Should Be 0
            }
        }
        Context "When TLogs between full's FirstLsn and LastLsn" {
            $Header = ConvertFrom-Json -InputObject (Get-Content $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\TLogBWFirstLastLsn.json -raw)
            $header | Add-Member -Type NoteProperty -Name FullName -Value 1

            $RestoreDate = Get-date "2017-07-18 09:00:00"
            $Output = Select-DbaBackupInformation -BackupHistory $Header -RestoreTime $RestoreDate

            It "Should return an array of 3 items" {
                $Output.count | Should be 3
            }
            It "Should return 1 Full backups" {
                ($Output | Where-Object { $_.BackupTypeDescription -eq 'Database' } | Measure-Object).count | Should Be 1
            }
            It "Should return 0 Diff backups" {
                ($Output | Where-Object { $_.BackupTypeDescription -eq 'Database Differential' } | Measure-Object).count | Should Be 0
            }
            It "Should return 2 log backups" {
                ($Output | Where-Object { $_.BackupTypeDescription -eq 'Transaction Log' } | Measure-Object).count | Should Be 2
            }
            It "Should not contain the Log backup with LastLsn 14975000000265600001 " {
                ($Output | Where-Object { $_.LastLsn -eq '14975000000265600001' } | Measure-Object).count | Should Be 0
            }
        }
        Context "Last log backup has same lastlsn as consequent backups" {
            $Header = ConvertFrom-Json -InputObject (Get-Content $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\broken_chain.json -raw)
            $header | Add-Member -Type NoteProperty -Name FullName -Value 1

            $RestoreDate = Get-date "7/16/2017 5:51:30 PM"
            $Output = Select-DbaBackupInformation -BackupHistory $Header -RestoreTime $RestoreDate

            It "Should return an array of 3 items" {
                $Output.count | Should be 3
            }
            It "Should return 1 Full backups" {
                ($Output | Where-Object { $_.BackupTypeDescription -eq 'Database' } | Measure-Object).count | Should Be 1
            }
            It "Should return 0 Diff backups" {
                ($Output | Where-Object { $_.BackupTypeDescription -eq 'Database Differential' } | Measure-Object).count | Should Be 0
            }
            It "Should return 2 log backups" {
                ($Output | Where-Object { $_.BackupTypeDescription -eq 'Transaction Log' } | Measure-Object).count | Should Be 2
            }
            It "Should not contain the Log backup with FirstLsn=LastLsn=17126658000000315600037 " {
                ($Output | Where-Object { $_.LastLsn -eq '17126658000000315600037' -and $_.FirstLsn -eq '17126658000000315600037' } | Measure-Object).count | Should Be 0
            }
            It "Should contain the Log backup with FirstLsn 17126658000000314600037 " {
                ($Output | Where-Object { $_.FirstLsn -eq '17126658000000314600037' } | Measure-Object).count | Should Be 1
            }
        }
        Context "Continue Points" {
            $BackupInfo = Get-DbaBackupInformation -Import -Path $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\ContinuePointTest.xml
            [bigint]$redo_start_lsn = '34000000016700004'
            $ContinuePoints = [PsCustomObject]@{
                redo_start_lsn      = $redo_start_lsn
                FirstRecoveryForkID = '00000000-0000-0000-0000-000000000000'
                Database            = 'ContinuePointTest'
            }
            $Output = Select-DbaBackupInformation -BackupHistory $BackupInfo -EnableException:$true -ContinuePoints $ContinuePoints

            It "Should return an array of 4 items" {
                $Output.count | Should be 4
            }
            It "Should return 0 Full backups" {
                ($Output | Where-Object { $_.Type -eq 'Database' } | Measure-Object).count | Should Be 0
            }
            It "Should return 0 Diff backups" {
                ($Output | Where-Object { $_.Type -eq 'Database Differential' } | Measure-Object).count | Should Be 0
            }
            It "Should return 4 log backups" {
                ($Output | Where-Object { $_.Type -eq 'Transaction Log' } | Measure-Object).count | Should Be 4
            }
            It "Should start with a log backup including redo_start_lsn" {
                $tmp = ($output | sort-object -property FirstLSn)[0]
                ($redo_start_lsn -ge $tmp.FirstLsn -and $redo_start_lsn -le $tmp.LastLsn) | Should Be $True
            }
        }

    }
}
tools\dbatools\tests\Select-DbaObject.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Unit Tests" -Tag "UnitTests" {
    $global:object = [PSCustomObject]@{
        Foo  = 42
        Bar  = 18
        Tara = 21
    }
    
    $global:object2 = [PSCustomObject]@{
        Foo    = 42000
        Bar    = 23
    }
    
    $global:list = @()
    $global:list += $object
    $global:list += [PSCustomObject]@{
        Foo   = 23
        Bar   = 88
        Tara  = 28
    }
    
    It "renames Bar to Bar2" {
        ($object | Select-DbaObject -Property 'Foo', 'Bar as Bar2').PSObject.Properties.Name | Should -Be 'Foo', 'Bar2'
    }
    
    It "changes Bar to string" {
        ($object | Select-DbaObject -Property 'Bar to string').Bar.GetType().FullName | Should -Be 'System.String'
    }
    
    it "converts numbers to sizes" {
        ($object2 | Select-DbaObject -Property 'Foo size KB:1').Foo | Should -Be 41
        ($object2 | Select-DbaObject -Property 'Foo size KB:1:1').Foo | Should -Be "41 KB"
    }
    
    it "picks values from other variables" {
        ($object2 | Select-DbaObject -Property 'Tara from object').Tara | Should -Be 21
    }
    
    it "picks values from the properties of the right object in a list" {
        ($object2 | Select-DbaObject -Property 'Tara from List where Foo = Bar').Tara | Should -Be 28
    }
}
tools\dbatools\tests\Set-DbaAgentAlert.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"
Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        $server = Connect-DbaInstance -SqlInstance $script:instance2 -Database master
        $server.Query("EXEC msdb.dbo.sp_add_alert @name=N'dbatoolsci test alert',@message_id=0,@severity=6,@enabled=1,@delay_between_responses=0,@include_event_description_in=0,@category_name=N'[Uncategorized]',@job_id=N'00000000-0000-0000-0000-000000000000'")
    }
    AfterAll {
        $server = Connect-DbaInstance -SqlInstance $script:instance2 -Database master
        $server.Query("EXEC msdb.dbo.sp_delete_alert @name=N'dbatoolsci test alert NEW'")
    }

    $results = Set-DbaAgentAlert -SqlInstance $script:instance2 -Alert 'dbatoolsci test alert' -Disabled
    It "changes new alert to disabled" {
        $results.IsEnabled | Should Be 'False'
    }

    $results = Set-DbaAgentAlert -SqlInstance $script:instance2 -Alert 'dbatoolsci test alert' -NewName 'dbatoolsci test alert NEW'
    It "changes new alert name to dbatoolsci test alert NEW" {
        $results.Name | Should Be 'dbatoolsci test alert NEW'
    }
}
tools\dbatools\tests\Set-DbaAgentJobCategory.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    Context "New Agent Job Category is changed properly" {

        It "Should have the right name and category type" {
            $results = New-DbaAgentJobCategory -SqlInstance $script:instance2 -Category CategoryTest1
            $results.Name | Should Be "CategoryTest1"
            $results.CategoryType | Should Be "LocalJob"
        }

        It "Should actually for sure exist" {
            $newresults = Get-DbaAgentJobCategory -SqlInstance $script:instance2 -Category CategoryTest1
            $newresults.Name | Should Be "CategoryTest1"
            $newresults.CategoryType | Should Be "LocalJob"
        }

        It "Change the name of the job category" {
            $results = Set-DbaAgentJobCategory -SqlInstance $script:instance2 -Category CategoryTest1 -NewName CategoryTest2
            $results.NewCategoryName | Should Be "CategoryTest2"
        }

        # Cleanup and ignore all output
        Remove-DbaAgentJobCategory -SqlInstance $script:instance2 -Category CategoryTest2 *> $null
    }
}
tools\dbatools\tests\Set-DbaDatabaseState.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Parameters validation" {
        BeforeAll {
            $server = Connect-DbaInstance -SqlInstance $script:instance2
            $db1 = "dbatoolsci_dbsetstate_online"
            $server.Query("CREATE DATABASE $db1")
        }
        AfterAll {
            Remove-DbaDatabase -Confirm:$false -SqlInstance $script:instance2 -Database $db1
        }
        It "Stops if no Database or AllDatabases" {
            { Set-DbaDatabaseState -SqlInstance $script:instance2 -EnableException } | Should Throw "You must specify"
        }
        It "Is nice by default" {
            { Set-DbaDatabaseState -SqlInstance $script:instance2 *> $null } | Should Not Throw "You must specify"
        }
        It "Errors out when multiple 'access' params are passed with EnableException" {
            { Set-DbaDatabaseState -SqlInstance $script:instance2 -Database $db1 -SingleUser -RestrictedUser -EnableException } | Should Throw "You can only specify one of: -SingleUser,-RestrictedUser,-MultiUser"
            { Set-DbaDatabaseState -SqlInstance $script:instance2 -Database $db1 -MultiUser -RestrictedUser -EnableException } | Should Throw "You can only specify one of: -SingleUser,-RestrictedUser,-MultiUser"
        }
        It "Errors out when multiple 'access' params are passed without EnableException" {
            { Set-DbaDatabaseState -SqlInstance $script:instance2 -Database $db1 -SingleUser -RestrictedUser *> $null } | Should Not Throw
            { Set-DbaDatabaseState -SqlInstance $script:instance2 -Database $db1 -MultiUser -RestrictedUser *> $null } | Should Not Throw
            $result = Set-DbaDatabaseState -SqlInstance $script:instance2 -Database $db1 -SingleUser -RestrictedUser *> $null
            $result | Should Be $null
        }
        It "Errors out when multiple 'status' params are passed with EnableException" {
            { Set-DbaDatabaseState -SqlInstance $script:instance2 -Database $db1 -Offline -Online -EnableException } | Should Throw "You can only specify one of: -Online,-Offline,-Emergency,-Detached"
            { Set-DbaDatabaseState -SqlInstance $script:instance2 -Database $db1 -Emergency -Online -EnableException } | Should Throw "You can only specify one of: -Online,-Offline,-Emergency,-Detached"
        }
        It "Errors out when multiple 'status' params are passed without Silent" {
            { Set-DbaDatabaseState -SqlInstance $script:instance2 -Database $db1 -Offline -Online *> $null } | Should Not Throw
            { Set-DbaDatabaseState -SqlInstance $script:instance2 -Database $db1 -Emergency -Online *> $null } | Should Not Throw
            $result = Set-DbaDatabaseState -SqlInstance $script:instance2 -Database $db1 -Offline -Online *> $null
            $result | Should Be $null
        }
        It "Errors out when multiple 'rw' params are passed with EnableException" {
            { Set-DbaDatabaseState -SqlInstance $script:instance2 -Database $db1 -ReadOnly -ReadWrite -EnableException } | Should Throw "You can only specify one of: -ReadOnly,-ReadWrite"
        }
        It "Errors out when multiple 'rw' params are passed without EnableException" {
            { Set-DbaDatabaseState -SqlInstance $script:instance2 -Database $db1 -ReadOnly -ReadWrite *> $null } | Should Not Throw
            $result = Set-DbaDatabaseState -SqlInstance $script:instance2 -Database $db1 -ReadOnly -ReadWrite *> $null
            $result | Should Be $null
        }
    }
    Context "Operations on databases" {
        BeforeAll {
            $server = Connect-DbaInstance -SqlInstance $script:instance2
            $db1 = "dbatoolsci_dbsetstate_online"
            $db2 = "dbatoolsci_dbsetstate_offline"
            $db3 = "dbatoolsci_dbsetstate_emergency"
            $db4 = "dbatoolsci_dbsetstate_single"
            $db5 = "dbatoolsci_dbsetstate_restricted"
            $db6 = "dbatoolsci_dbsetstate_multi"
            $db7 = "dbatoolsci_dbsetstate_rw"
            $db8 = "dbatoolsci_dbsetstate_ro"
            Get-DbaDatabase -SqlInstance $script:instance2 -Database $db1, $db2, $db3, $db4, $db5, $db6, $db7, $db8 | Remove-DbaDatabase -Confirm:$false
            $server.Query("CREATE DATABASE $db1")
            $server.Query("CREATE DATABASE $db2")
            $server.Query("CREATE DATABASE $db3")
            $server.Query("CREATE DATABASE $db4")
            $server.Query("CREATE DATABASE $db5")
            $server.Query("CREATE DATABASE $db6")
            $server.Query("CREATE DATABASE $db7")
            $server.Query("CREATE DATABASE $db8")
            $setupright = $true
            $needed_ = $server.Query("select name from sys.databases")
            $needed = $needed_ | Where-Object name -in $db1, $db2, $db3, $db4, $db5, $db6, $db7, $db8
            if ($needed.Count -ne 8) {
                $setupright = $false
                it "has failed setup" {
                    Set-TestInconclusive -message "Setup failed"
                }
            }
        }
        AfterAll {
            $null = Set-DbaDatabaseState -Sqlinstance $script:instance2 -Database $db1, $db2, $db3, $db4, $db5, $db7 -Online -ReadWrite -MultiUser -Force
            $null = Remove-DbaDatabase -Confirm:$false -SqlInstance $script:instance2 -Database $db1, $db2, $db3, $db4, $db5, $db6, $db7, $db8
        }
        if ($setupright) {
            # just to have a correct report on how much time BeforeAll takes
            It "Waits for BeforeAll to finish" {
                $true | Should Be $true
            }
            It "Honors the Database parameter" {
                $result = Set-DbaDatabaseState -SqlInstance $script:instance2 -Database $db2 -Emergency -Force
                $result.DatabaseName | Should be $db2
                $result.Status | Should Be 'EMERGENCY'
                $results = Set-DbaDatabaseState -SqlInstance $script:instance2 -Database $db1, $db2 -Emergency -Force
                $results.Count | Should be 2
            }
            It "Honors the ExcludeDatabase parameter" {
                $alldbs_ = $server.Query("select name from sys.databases")
                $alldbs = ($alldbs_ | Where-Object Name -notin @($db1, $db2, $db3, $db4, $db5, $db6, $db7, $db8)).name
                $results = Set-DbaDatabaseState -SqlInstance $script:instance2 -ExcludeDatabase $alldbs -Online -Force
                $comparison = Compare-Object -ReferenceObject ($results.DatabaseName) -DifferenceObject (@($db1, $db2, $db3, $db4, $db5, $db6, $db7, $db8))
                $comparison.Count | Should Be 0
            }

            It "Sets a database as online" {
                $null = Set-DbaDatabaseState -SqlInstance $script:instance2 -Database $db1 -Emergency -Force
                $result = Set-DbaDatabaseState -SqlInstance $script:instance2 -Database $db1 -Online -Force
                $result.DatabaseName | Should Be $db1
                $result.Status | Should Be "ONLINE"
            }

            It "Sets a database as offline" {
                $result = Set-DbaDatabaseState -SqlInstance $script:instance2 -Database $db2 -Offline -Force
                $result.DatabaseName | Should Be $db2
                $result.Status | Should Be "OFFLINE"
            }

            It "Sets a database as emergency" {
                $result = Set-DbaDatabaseState -SqlInstance $script:instance2 -Database $db3 -Emergency -Force
                $result.DatabaseName | Should Be $db3
                $result.Status | Should Be "EMERGENCY"
            }
            
            It "Sets a database as single_user" {
                $result = Set-DbaDatabaseState -SqlInstance $script:instance2 -Database $db4 -SingleUser -Force
                $result.DatabaseName | Should Be $db4
                $result.Access | Should Be "SINGLE_USER"
            }
            It "Sets a database as multi_user" {
                $null = Set-DbaDatabaseState -SqlInstance $script:instance2 -Database $db6 -RestrictedUser -Force
                $result = Set-DbaDatabaseState -SqlInstance $script:instance2 -Database $db6 -MultiUser -Force
                $result.DatabaseName | Should Be $db6
                $result.Access | Should Be "MULTI_USER"
            }
            
            It "Sets a database as restricted_user" {
                $result = Set-DbaDatabaseState -SqlInstance $script:instance2 -Database $db5 -RestrictedUser -Force
                $result.DatabaseName | Should Be $db5
                $result.Access | Should Be "RESTRICTED_USER"
            }
            It "Sets a database as read_write" {
                $null = Set-DbaDatabaseState -SqlInstance $script:instance2 -Database $db7 -ReadOnly -Force
                $result = Set-DbaDatabaseState -SqlInstance $script:instance2 -Database $db7 -ReadWrite -Force
                $result.DatabaseName | Should Be $db7
                $result.RW | Should Be "READ_WRITE"
            }
            It "Sets a database as read_only" {
                $result = Set-DbaDatabaseState -SqlInstance $script:instance2 -Database $db8 -ReadOnly -Force
                $result.DatabaseName | Should Be $db8
                $result.RW | Should Be "READ_ONLY"
            }
            It "Works when piped from Get-DbaDatabaseState" {
                $results = Get-DbaDatabaseState -SqlInstance $script:instance2 -Database $db7, $db8 | Set-DbaDatabaseState -Online -MultiUser -Force
                $results.Count | Should Be 2
                $comparison = Compare-Object -ReferenceObject ($results.DatabaseName) -DifferenceObject (@($db7, $db8))
                $comparison.Count | Should Be 0
            }
            It "Works when piped from Get-DbaDatabase" {
                $results = Get-DbaDatabase -SqlInstance $script:instance2 -Database $db7, $db8 | Set-DbaDatabaseState -Online -MultiUser -Force
                $results.Count | Should Be 2
                $comparison = Compare-Object -ReferenceObject ($results.DatabaseName) -DifferenceObject (@($db7, $db8))
                $comparison.Count | Should Be 0
            }
            $result = Set-DbaDatabaseState -SqlInstance $script:instance2 -Database $db1 -Emergency -Force
            It "Has the correct properties" {
                $ExpectedProps = 'ComputerName,InstanceName,SqlInstance,DatabaseName,RW,Status,Access,Notes,Database'.Split(',')
                ($result.PsObject.Properties.Name | Sort-Object) | Should Be ($ExpectedProps | Sort-Object)
            }

            It "Has the correct default properties" {
                $ExpectedPropsDefault = 'ComputerName,InstanceName,SqlInstance,DatabaseName,RW,Status,Access,Notes'.Split(',')
                ($result.PSStandardMembers.DefaultDisplayPropertySet.ReferencedPropertyNames | Sort-Object) | Should Be ($ExpectedPropsDefault | Sort-Object)
            }
        }
    }
}
tools\dbatools\tests\Set-DbaDbCompression.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        $paramCount = 9
        $defaultParamCount = 13
        [object[]]$params = (Get-ChildItem function:\Set-DbaDbCompression).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential','Database','ExcludeDatabase','CompressionType','MaxRunTime','PercentCompression','InputObject','EnableException'
        It "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $dbname = "dbatoolsci_test_$(get-random)"
        $server = Connect-DbaInstance -SqlInstance $script:instance2
        $null = $server.Query("Create Database [$dbname]")
        $null = $server.Query("select * into syscols from sys.all_columns
                                select * into sysallparams from sys.all_parameters
                                create clustered index CL_sysallparams on sysallparams (object_id)
                                create nonclustered index NC_syscols on syscols (precision) include (collation_name)",$dbname)
       }
    AfterAll {
        Get-DbaProcess -SqlInstance $script:instance2 -Database $dbname | Stop-DbaProcess -WarningAction SilentlyContinue
        Remove-DbaDatabase -SqlInstance $script:instance2 -Database $dbname -Confirm:$false
    }
    $InputObject = Test-DbaDbCompression -SqlInstance $script:instance2 -Database $dbname
    $results = Set-DbaDbCompression -SqlInstance $script:instance2 -Database $dbname -MaxRunTime 5 -PercentCompression 0
    Context "Command gets results" {
        It "Should contain objects" {
            $results | Should Not Be $null
        }
    }

    Context "Command handles heaps and clustered indexes" {
        foreach ($row in $results | Where-Object {$_.IndexId -le 1}){
            It "Should process object $($row.TableName)" {
                $row.AlreadyProcesssed | Should Be $True
            }
        }
    }
    Context "Command handles nonclustered indexes" {
        foreach ($row in $results | Where-Object {$_.IndexId -gt 1}){
            It "Should process nonclustered index $($row.IndexName)" {
                $row.AlreadyProcesssed | Should Be $True
            }
        }
    }
    Context "Command excludes results for specified database" {
        $server.Databases[$dbname].Tables['syscols'].PhysicalPartitions[0].DataCompression = "NONE"
        $server.Databases[$dbname].Tables['syscols'].Rebuild()
        It "Shouldn't get any results for $dbname" {
            $(Set-DbaDbCompression -SqlInstance $script:instance2 -Database $dbname -ExcludeDatabase $dbname -MaxRunTime 5 -PercentCompression 0).Database | Should not Match $dbname
        }
    }
    Context "Command can accept InputObject from Test-DbaDbCompression" {
        $results = @(Set-DbaDbCompression -SqlInstance $script:instance2 -Database $dbname -MaxRunTime 5 -PercentCompression 0 -InputObject $InputObject)
        It "Should get results" {
            $results | Should not be $null
        }
        foreach ($row in $results) {
            It "Should process object $($row.TableName) from InputObject" {
                $row.AlreadyProcesssed | Should Be $True
            }
        }
    }
    Context "Command sets compression to Row all objects" {
        $null = Set-DbaDbCompression -SqlInstance $script:instance2 -Database $dbname -CompressionType Row
        $results = Get-DbaDbCompression -SqlInstance $script:instance2 -Database $dbname
        foreach ($row in $results) {
            It "The $($row.IndexType) for $($row.schema).$($row.TableName) is row compressed" {
                $row.DataCompression | Should Be "Row"
            }
        }
    }
    Context "Command sets compression to Page for all objects" {
        $null = Set-DbaDbCompression -SqlInstance $script:instance2 -Database $dbname -CompressionType Page
        $results = Get-DbaDbCompression -SqlInstance $script:instance2 -Database $dbname
        foreach ($row in $results) {
            It "The $($row.IndexType) for $($row.schema).$($row.TableName) is page compressed" {
                $row.DataCompression | Should Be "Page"
            }
        }
    }
    Context "Command sets compression to None for all objects" {
        $null = Set-DbaDbCompression -SqlInstance $script:instance2 -Database $dbname -CompressionType None
        $results = Get-DbaDbCompression -SqlInstance $script:instance2 -Database $dbname
        foreach ($row in $results) {
            It "The $($row.IndexType) for $($row.schema).$($row.TableName) is not compressed" {
                $row.DataCompression | Should Be "None"
            }
        }
    }
}
tools\dbatools\tests\Set-DbaDbQueryStoreOptions.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $null = Get-DbaDatabase -SqlInstance $script:instance1, $script:instance2 | Where-Object Name -Match 'dbatoolsci' | Remove-DbaDatabase -Confirm:$false
    }
    Context "Set some options" {
        foreach ($instance in ($script:instance1, $script:instance2)) {
            $server = Connect-DbaInstance -SqlInstance $instance
            $results = Get-DbaDbQueryStoreOptions -SqlInstance $instance -WarningVariable warning  3>&1

            if ($server.VersionMajor -lt 13) {
                It "should warn" {
                    $warning | Should Not Be $null
                }
            }
            else {
                It "should return a default" {
                    $result = $results | Where-Object Database -eq msdb
                    $result.MaxStorageSizeInMB | Should BeGreaterThan 1
                }

                $newnumber = $oldnumber + 1
                $null = Set-DbaDbQueryStoreOptions -SqlInstance $instance -Database msdb -State Off

                It "should warn that state is off" {
                    $results = Set-DbaDbQueryStoreOptions -SqlInstance $instance -Database msdb -FlushInterval $newnumber -WarningVariable warning  3>&1
                    $warning | Should Not Be $null
                }

                It "should change the specified param to the new value" {
                    $results = Set-DbaDbQueryStoreOptions -SqlInstance $instance -Database msdb -FlushInterval $newnumber -State ReadWrite
                    $results.DataFlushIntervalInSeconds | Should Be $newnumber
                }
            }
        }
    }
}
tools\dbatools\tests\Set-DbaDbRecoveryModel.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Recovery model is correctly set" {
        BeforeAll {
            $server = Connect-DbaInstance -SqlInstance $script:instance2
            $dbname = "dbatoolsci_recoverymodel"
            Get-DbaDatabase -SqlInstance $server -Database $dbname | Remove-DbaDatabase -Confirm:$false
            $server.Query("CREATE DATABASE $dbname")
        }
        AfterAll {
            Get-DbaDatabase -SqlInstance $script:instance2 -Database $dbname | Remove-DbaDatabase -Confirm:$false
        }

        $results = Set-DbaDbRecoveryModel -SqlInstance $script:instance2 -Database $dbname -RecoveryModel BulkLogged -Confirm:$false

        It "sets the proper recovery model" {
            $results.RecoveryModel -eq "BulkLogged" | Should Be $true
        }

        It "supports the pipeline" {
            $results = Get-DbaDatabase -SqlInstance $script:instance2 -Database $dbname | Set-DbaDbRecoveryModel -RecoveryModel Simple -Confirm:$false
            $results.RecoveryModel -eq "Simple" | Should Be $true
        }

        It "requires Database, ExcludeDatabase or AllDatabases" {
            $results = Set-DbaDbRecoveryModel -SqlInstance $script:instance2 -RecoveryModel Simple -WarningAction SilentlyContinue -WarningVariable warn -Confirm:$false
            $warn -match "AllDatabases" | Should Be $true
        }

    }
}
tools\dbatools\tests\Set-DbaErrorLogConfig.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag "UnitTests" {
    Context "Validate parameters" {
        $paramCount = 5
        $defaultParamCount = 13
        [object[]]$params = (Get-ChildItem function:\Set-DbaErrorLogConfig).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'LogSize', 'LogCount', 'EnableException'
        It "Should contain our specific parameters" {
            ((Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}

Describe "$CommandName Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        $server = Connect-DbaInstance -SqlInstance $script:instance2
        $logfiles = $server.NumberOfLogFiles
        $logsize = $server.ErrorLogSizeKb

        $server.NumberOfLogFiles = 4
        $server.ErrorLogSizeKb = 1024
        $server.Alter()

        $server = Connect-DbaInstance -SqlInstance $script:instance1
        $logfiles2 = $server.NumberOfLogFiles
        $logsize2 = $server.ErrorLogSizeKb

        $server.NumberOfLogFiles = 4
        $server.Alter()
    }
    AfterAll {
        $server = Connect-DbaInstance -SqlInstance $script:instance1
        $server.NumberOfLogFiles = $logfiles2
        $server.Alter()

        $server = Connect-DbaInstance -SqlInstance $script:instance2
        $server.NumberOfLogFiles = $logfiles
        $server.ErrorLogSizeKb = $logsize
        $server.Alter()
    }

    Context "Apply LogCount to multiple instances" {
        $results = Set-DbaErrorLogConfig -SqlInstance $script:instance2, $script:instance1 -LogCount 8
        foreach ($result in $results) {
            It 'Returns LogCount set to 3 for each instance' {
                $result.LogCount | Should Be 8
            }
        }
    }
    Context "Apply LogSize to multiple instances" {
        $results = Set-DbaErrorLogConfig -SqlInstance $script:instance2, $script:instance1 -LogSize 100 -WarningAction SilentlyContinue -WarningVariable warn2
        foreach ($result in $results) {
            It 'Returns LogSize set to 100 for each instance' {
                $result.LogSize.Kilobyte | Should Be 100
            }
        }

        It "returns a warning for invalid version" {
            $warn2 | Should Match 'not supported'
        }
    }
}
tools\dbatools\tests\Set-DbaLogin.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        $paramCount = 14
        [object[]]$params = (Get-ChildItem function:\Set-DbaLogin).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'Login', 'Password', 'Unlock', 'MustChange', 'NewName', 'Disable', 'Enable', 'DenyLogin', 'GrantLogin', 'AddRole', 'RemoveRole', 'EnableException'
        It "Contains our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
    }
}

Describe "$CommandName Unittests" -Tag 'UnitTests' {
    Context "Change login" {

        BeforeAll {
            # Create the new password
            $password1 = ConvertTo-SecureString -String "password1" -AsPlainText -Force
            $password2 = ConvertTo-SecureString -String "password2" -AsPlainText -Force

            # Create the login
            New-DbaLogin -SqlInstance $script:instance2 -Login testlogin -Password $password1
        }

        It "Does test login exist" {
            $logins = Get-DbaLogin -SqlInstance $script:instance2 | Where-Object {$_.Name -eq "testlogin"} | Select-Object Name
            $logins.Name | Should -Be "testlogin"
        }

        It "Change the password"{
            $result = Set-DbaLogin -SqlInstance $script:instance2 -Login testlogin -Password $password2

            $result.PasswordChanged | Should -Be $true
        }

        It "Disable the login" {
            $result = Set-DbaLogin -SqlInstance $script:instance2 -Login testlogin -Disable

            $result.IsDisabled | Should -Be $true
        }

        It "Enable the login" {
            $result = Set-DbaLogin -SqlInstance $script:instance2 -Login testlogin -Enable

            $result.IsDisabled | Should -Be $false
        }

        It "Deny access to login" {
            $result = Set-DbaLogin -SqlInstance $script:instance2 -Login testlogin -DenyLogin

            $result.DenyLogin | Should -Be $true
        }

        It "Grant access to login" {
            $result = Set-DbaLogin -SqlInstance $script:instance2 -Login testlogin -GrantLogin

            $result.DenyLogin | Should -Be $false
        }

        It "Enforces password policy on login" {
            $result = Set-DbaLogin -SqlInstance $script:instance2 -Login testlogin -PasswordPolicyEnforced

            $result.PasswordPolicyEnforced | Should Be $true
        }

        It "Disables enforcing password policy on login" {
            $result = Set-DbaLogin -SqlInstance $script:instance2 -Login testlogin -PasswordPolicyEnforced:$false

            $result.PasswordPolicyEnforced | Should Be $false
        }

        It "Add roles to login" {
            $result = Set-DbaLogin -SqlInstance $script:instance2 -Login testlogin -AddRole serveradmin, processadmin

            $result.ServerRole | Should -Be "processadmin,serveradmin"
        }

        It "Remove roles from login" {
            $result = Set-DbaLogin -SqlInstance $script:instance2 -Login testlogin -RemoveRole serveradmin

            $result.ServerRole | Should -Be "processadmin"
        }

        AfterAll {
            Remove-DbaLogin -SqlInstance $script:instance2 -Login testlogin -Confirm:$false
        }
    }
}
tools\dbatools\tests\Set-DbaMaxDop.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        Get-DbaProcess -SqlInstance $script:instance1, $script:instance2 -Program 'dbatools PowerShell module - dbatools.io' | Stop-DbaProcess -WarningAction SilentlyContinue
        $singledb = "dbatoolsci_singledb"
        $dbs = "dbatoolsci_lildb", "dbatoolsci_testMaxDop", $singledb
        $null = Get-DbaDatabase -SqlInstance $script:instance2 -Database $dbs | Remove-DbaDatabase -Confirm:$false
        foreach ($db in $dbs) {
            Invoke-DbaSqlQuery -SqlInstance $script:instance1 -Query "CREATE DATABASE $db"
            Invoke-DbaSqlQuery -SqlInstance $script:instance2 -Query "CREATE DATABASE $db"
        }
    }
    AfterAll {
        Get-DbaDatabase -SqlInstance $script:instance1 -Database $dbs | Remove-DbaDatabase -Confirm:$false
        Get-DbaDatabase -SqlInstance $script:instance2 -Database $dbs | Remove-DbaDatabase -Confirm:$false
    }
    
    Context "Apply to multiple instances" {
        $results = Set-DbaMaxDop -SqlInstance $script:instance1, $script:instance2 -MaxDop 2
        foreach ($result in $results) {
            It 'Returns MaxDop 2 for each instance' {
                $result.CurrentInstanceMaxDop | Should Be 2
            }
        }
    }
    
    Context "Connects to 2016+ instance and apply configuration to single database" {
        $results = Set-DbaMaxDop -SqlInstance $script:instance2 -MaxDop 4 -Database $singledb
        foreach ($result in $results) {
            It 'Returns 4 for each database' {
                $result.DatabaseMaxDop | Should Be 4
            }
        }
    }
    
    Context "Connects to 2016+ instance and apply configuration to multiple databases" {
        $results = Set-DbaMaxDop -SqlInstance $script:instance2 -MaxDop 8 -Database $dbs
        foreach ($result in $results) {
            It 'Returns 8 for each database' {
                $result.DatabaseMaxDop | Should Be 8
            }
        }
    }
}

Describe "$commandname Unit Tests" -Tag "UnitTests", Set-DbaMaxDop {
    Context "Input validation" {
        BeforeAll {
            Mock Stop-Function { } -ModuleName dbatools
        }
        It "Should Call Stop-Function. -Database, -AllDatabases and -ExcludeDatabase are mutually exclusive." {
            Set-DbaMaxDop -SqlInstance $script:instance1 -MaxDop 12 -Database $singledb -AllDatabases -ExcludeDatabase "master" | Should Be
        }
        It "Validates that Stop Function Mock has been called" {
            $assertMockParams = @{
                'CommandName'  = 'Stop-Function'
                'Times'        = 1
                'Exactly'      = $true
                'Module'       = 'dbatools'
            }
            Assert-MockCalled @assertMockParams
        }
    }
}
tools\dbatools\tests\Set-DbaMaxMemory.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $inst1CurrentSqlMax = (Get-DbaMaxMemory -SqlInstance $script:instance1).SqlMaxMB
        $inst2CurrentSqlMax = (Get-DbaMaxMemory -SqlInstance $script:instance2).SqlMaxMB
    }
    AfterAll {
       $null = Set-DbaMaxMemory -SqlInstance $script:instance1 -MaxMB $inst1CurrentSqlMax
       $null = Set-DbaMaxMemory -SqlInstance $script:instance2 -MaxMB $inst2CurrentSqlMax
    }
    Context "Connects to multiple instances" {
        $results = Set-DbaMaxMemory -SqlInstance $script:instance1, $script:instance2 -MaxMB 1024
        foreach ($result in $results) {
            It 'Returns 1024 MB for each instance' {
                $result.CurrentMaxValue | Should Be 1024
            }
        }
    }
}

Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    InModuleScope dbatools {
        Context 'Validate input arguments' {
            It 'SqlInstance parameter host cannot be found' {
                Set-DbaMaxMemory -SqlInstance 'ABC' 3> $null | Should be $null
            }
        }
    }
}
tools\dbatools\tests\Set-DbaSpConfigure.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Set configuration" {
        BeforeAll {
            $remotequerytimeout = (Get-DbaSpConfigure -SqlInstance $script:instance1 -ConfigName RemoteQueryTimeout).ConfiguredValue
            $newtimeout = $remotequerytimeout + 1
        }

        # Sanity check
        if ($null -eq $remotequerytimeout) {
            return
        }

        It "changes the remote query timeout from $remotequerytimeout to $newtimeout" {
            $results = Set-DbaSpConfigure -SqlInstance $script:instance1 -ConfigName RemoteQueryTimeout -Value $newtimeout
            $results.OldValue | Should Be $remotequerytimeout
            $results.NewValue | Should Be $newtimeout
        }

        It "changes the remote query timeout from $newtimeout to $remotequerytimeout" {
            $results = Set-DbaSpConfigure -SqlInstance $script:instance1 -ConfigName RemoteQueryTimeout -Value $remotequerytimeout
            $results.OldValue | Should Be $newtimeout
            $results.NewValue | Should Be $remotequerytimeout
        }

        $results = Set-DbaSpConfigure -SqlInstance $script:instance1 -ConfigName RemoteQueryTimeout -Value $remotequerytimeout -WarningVariable warning -WarningAction SilentlyContinue
        It "returns a warning when if the new value is the same as the old" {
            $warning -match "existing" | Should be $true
        }
    }
}
tools\dbatools\tests\Start-DbaAgentJob.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    It "returns a CurrentRunStatus of not Idle and supports pipe" {
        $null = Get-DbaAgentJob -SqlInstance $script:instance2 -Job 'DatabaseBackup - SYSTEM_DATABASES - FULL' | Start-DbaAgentJob
        $results.CurrentRunStatus -ne 'Idle' | Should Be $true
    }
    
    It "does not run all jobs" {
        $null = Start-DbaAgentJob -SqlInstance $script:instance2 -WarningAction SilentlyContinue -WarningVariable warn
        $warn -match 'use one of the job' | Should Be $true
    }
}
tools\dbatools\tests\Start-DbaPfDataCollectorSet.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $script:set = Get-DbaPfDataCollectorSet | Select-Object -First 1
        $script:set | Stop-DbaPfDataCollectorSet -WarningAction SilentlyContinue
        Start-Sleep 2
    }
    AfterAll {
        $script:set | Stop-DbaPfDataCollectorSet -WarningAction SilentlyContinue
    }
    Context "Verifying command works" {
        It "returns a result with the right computername and name is not null" {
            $results = $script:set | Select-Object -First 1 | Start-DbaPfDataCollectorSet -WarningAction SilentlyContinue -WarningVariable warn
            if (-not $warn) {
                $results.ComputerName | Should Be $env:COMPUTERNAME
                $results.Name | Should Not Be $null
            }
        }
    }
}
tools\dbatools\tests\Start-DbaSqlService.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"
. "$PSScriptRoot\..\internal\functions\Connect-SqlInstance.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {

    Context "Command actually works" {

        $server = Connect-SqlInstance -SqlInstance $script:instance2
        $instanceName = $server.ServiceName
        $computerName = $server.NetName


        #Stop services using native cmdlets
        if ($instanceName -eq 'MSSQLSERVER') {
            $serviceName = "SQLSERVERAGENT"
        }
        else {
            $serviceName = "SqlAgent`$$instanceName"
        }
        Get-Service -ComputerName $computerName -Name $serviceName | Stop-Service -WarningAction SilentlyContinue | Out-Null

        It "starts the services back" {
            $services = Start-DbaSqlService -ComputerName $script:instance2 -Type Agent -InstanceName $instanceName
            $services | Should Not Be $null
            foreach ($service in $services) {
                $service.State | Should Be 'Running'
                $service.Status | Should Be 'Successful'
            }
        }

        #Stop services using native cmdlets
        if ($instanceName -eq 'MSSQLSERVER') {
            $serviceName = "SQLSERVERAGENT", "MSSQLSERVER"
        }
        else {
            $serviceName = "SqlAgent`$$instanceName", "MsSql`$$instanceName"
        }
        foreach ($sn in $servicename) { Get-Service -ComputerName $computerName -Name $sn | Stop-Service -WarningAction SilentlyContinue | Out-Null }

        It "starts the services back through pipeline" {
            $services = Get-DbaSqlService -ComputerName $script:instance2 -InstanceName $instanceName -Type Agent, Engine | Start-DbaSqlService
            $services | Should Not Be $null
            foreach ($service in $services) {
                $service.State | Should Be 'Running'
                $service.Status | Should Be 'Successful'
            }
        }

        It "errors when passing an invalid InstanceName" {
            { Start-DbaSqlService -ComputerName $script:instance2 -Type 'Agent' -InstanceName 'ThisIsInvalid' -EnableException } | Should Throw 'No SQL Server services found with current parameters.'
        }
    }
}
tools\dbatools\tests\Start-DbaTrace.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $sql = "-- Create a Queue
                declare @rc int
                declare @TraceID int
                declare @maxfilesize bigint
                set @maxfilesize = 5
                exec @rc = sp_trace_create @TraceID output, 0, N'C:\windows\temp\temptrace', @maxfilesize, NULL

                -- Set the events
                declare @on bit
                set @on = 1
                exec sp_trace_setevent @TraceID, 14, 1, @on
                exec sp_trace_setevent @TraceID, 14, 9, @on
                exec sp_trace_setevent @TraceID, 14, 10, @on
                exec sp_trace_setevent @TraceID, 14, 11, @on
                exec sp_trace_setevent @TraceID, 14, 6, @on
                exec sp_trace_setevent @TraceID, 14, 12, @on
                exec sp_trace_setevent @TraceID, 14, 14, @on
                exec sp_trace_setevent @TraceID, 15, 11, @on
                exec sp_trace_setevent @TraceID, 15, 6, @on
                exec sp_trace_setevent @TraceID, 15, 9, @on
                exec sp_trace_setevent @TraceID, 15, 10, @on
                exec sp_trace_setevent @TraceID, 15, 12, @on
                exec sp_trace_setevent @TraceID, 15, 13, @on
                exec sp_trace_setevent @TraceID, 15, 14, @on
                exec sp_trace_setevent @TraceID, 15, 15, @on
                exec sp_trace_setevent @TraceID, 15, 16, @on
                exec sp_trace_setevent @TraceID, 15, 17, @on
                exec sp_trace_setevent @TraceID, 15, 18, @on
                exec sp_trace_setevent @TraceID, 17, 1, @on
                exec sp_trace_setevent @TraceID, 17, 9, @on
                exec sp_trace_setevent @TraceID, 17, 10, @on
                exec sp_trace_setevent @TraceID, 17, 11, @on
                exec sp_trace_setevent @TraceID, 17, 6, @on
                exec sp_trace_setevent @TraceID, 17, 12, @on
                exec sp_trace_setevent @TraceID, 17, 14, @on
                exec sp_trace_setevent @TraceID, 10, 9, @on
                exec sp_trace_setevent @TraceID, 10, 2, @on
                exec sp_trace_setevent @TraceID, 10, 10, @on
                exec sp_trace_setevent @TraceID, 10, 6, @on
                exec sp_trace_setevent @TraceID, 10, 11, @on
                exec sp_trace_setevent @TraceID, 10, 12, @on
                exec sp_trace_setevent @TraceID, 10, 13, @on
                exec sp_trace_setevent @TraceID, 10, 14, @on
                exec sp_trace_setevent @TraceID, 10, 15, @on
                exec sp_trace_setevent @TraceID, 10, 16, @on
                exec sp_trace_setevent @TraceID, 10, 17, @on
                exec sp_trace_setevent @TraceID, 10, 18, @on
                exec sp_trace_setevent @TraceID, 12, 1, @on
                exec sp_trace_setevent @TraceID, 12, 9, @on
                exec sp_trace_setevent @TraceID, 12, 11, @on
                exec sp_trace_setevent @TraceID, 12, 6, @on
                exec sp_trace_setevent @TraceID, 12, 10, @on
                exec sp_trace_setevent @TraceID, 12, 12, @on
                exec sp_trace_setevent @TraceID, 12, 13, @on
                exec sp_trace_setevent @TraceID, 12, 14, @on
                exec sp_trace_setevent @TraceID, 12, 15, @on
                exec sp_trace_setevent @TraceID, 12, 16, @on
                exec sp_trace_setevent @TraceID, 12, 17, @on
                exec sp_trace_setevent @TraceID, 12, 18, @on
                exec sp_trace_setevent @TraceID, 13, 1, @on
                exec sp_trace_setevent @TraceID, 13, 9, @on
                exec sp_trace_setevent @TraceID, 13, 11, @on
                exec sp_trace_setevent @TraceID, 13, 6, @on
                exec sp_trace_setevent @TraceID, 13, 10, @on
                exec sp_trace_setevent @TraceID, 13, 12, @on
                exec sp_trace_setevent @TraceID, 13, 14, @on

                -- Set the Filters
                declare @intfilter int
                declare @bigintfilter bigint

                exec sp_trace_setfilter @TraceID, 10, 0, 7, N'SQL Server Profiler - 934a8575-0dc1-4937-bde1-edac1cb9691f'
                -- Set the trace status to start
                exec sp_trace_setstatus @TraceID, 1

                -- display trace id for future references
                select TraceID=@TraceID"
        $server = Connect-DbaInstance -SqlInstance $script:instance1
        $traceid = ($server.Query($sql)).TraceID
        $null = Get-DbaTrace -SqlInstance $script:instance1 -Id $traceid | Stop-DbaTrace
    }
    AfterAll {
        $null = Remove-DbaTrace -SqlInstance $script:instance1 -Id $traceid
        Remove-Item C:\windows\temp\temptrace.trc
    }
    Context "Test Starting Trace" {
        $results = Get-DbaTrace -SqlInstance $script:instance1 -Id $traceid
        It "starts in a stopped state" {
            $results.Id | Should Be $traceid
            $results.IsRunning | Should Be $false
        }
        $results = Get-DbaTrace -SqlInstance $script:instance1 -Id $traceid | Start-DbaTrace
        It "is now running" {
            $results.Id | Should Be $traceid
            $results.IsRunning | Should Be $true
        }
    }
}
tools\dbatools\tests\Start-DbaXESession.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $server = Connect-DbaInstance -SqlInstance $script:instance2
        $conn = $server.ConnectionContext
        # Get the systemhealth session
        $systemhealth = Get-DbaXESession -SqlInstance $server -Session system_health
        # Create a session with an invalid target
        $conn.ExecuteNonQuery("CREATE EVENT SESSION [dbatoolsci_session_invalid] ON SERVER ADD EVENT sqlserver.lock_acquired ADD TARGET package0.etw_classic_sync_target (SET default_etw_session_logfile_path = N'C:\dbatoolsci_session_doesnotexist\logfile.etl' );")
        $dbatoolsciInvalid = Get-DbaXESession -SqlInstance $server -Session dbatoolsci_session_invalid
        # Create a valid session
        $conn.ExecuteNonQuery("CREATE EVENT SESSION [dbatoolsci_session_valid] ON SERVER ADD EVENT sqlserver.lock_acquired;")
        $dbatoolsciValid = Get-DbaXESession -SqlInstance $server -Session dbatoolsci_session_valid
        # Record the Status of all sessions
        $allSessions = Get-DbaXESession -SqlInstance $server
    }
    BeforeEach {
        <#
        $systemhealth.Refresh()
        if ($systemhealth.IsRunning) {
            $systemhealth.Stop()
        }
        #>
        $systemhealth | Stop-DbaXESession #-ErrorAction SilentlyContinue
    }
    AfterAll {
        # Set the Status of all session back to what they were before the test
        foreach ($session in $allSessions) {
            $session.Refresh()
            if ($session.Status -eq "Stopped") {
                if ($session.IsRunning) {
                    $session.Stop()
                }
            }
            else {
                if (-Not $session.IsRunning) {
                    $session.Start()
                }
            }
        }

        # Drop created objects
        $conn.ExecuteNonQuery("IF EXISTS(SELECT * FROM sys.server_event_sessions WHERE name = 'dbatoolsci_session_invalid') DROP EVENT SESSION [dbatoolsci_session_invalid] ON SERVER;")
        $conn.ExecuteNonQuery("IF EXISTS(SELECT * FROM sys.server_event_sessions WHERE name = 'dbatoolsci_session_valid') DROP EVENT SESSION [dbatoolsci_session_valid] ON SERVER;")
    }

    Context "Verifying command works" {
        It "starts the system_health session" {
            $systemhealth | Start-DbaXESession
            $systemhealth.Refresh()
            $systemhealth.IsRunning | Should Be $true
        }

        It "does not change state if XE session is already started" {
            if (-Not $systemhealth.IsRunning) {
                $systemhealth.Start()
            }
            $systemhealth | Start-DbaXESession -WarningAction SilentlyContinue
            $systemhealth.Refresh()
            $systemhealth.IsRunning | Should Be $true
        }

        It "starts the other XE Sessions when one has an error" {
            # Start system_health and the invalid session
            Start-DbaXESession $server -Session $systemhealth.Name, $dbatoolsciInvalid.Name -WarningAction SilentlyContinue
            $systemhealth.Refresh()
            $dbatoolsciInvalid.Refresh()
            $systemhealth.IsRunning | Should Be $true
            $dbatoolsciInvalid.IsRunning | Should Be $false
        }

        It "starts all XE Sessions except the system ones if -AllSessions is used" {
            Start-DbaXESession $server -AllSessions -WarningAction SilentlyContinue
            $systemhealth.Refresh()
            $dbatoolsciValid.Refresh()
            $systemhealth.IsRunning | Should Be $false
            $dbatoolsciValid.IsRunning | Should Be $true
        }

    }
}
tools\dbatools\tests\Stop-DbaAgentJob.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "executes and returns the accurate info" {
        It -Skip "returns a CurrentRunStatus of Idle" {
            $agent = Get-DbaAgentJob -SqlInstance $script:instance2 -Job 'DatabaseBackup - SYSTEM_DATABASES - FULL' | Start-DbaAgentJob | Stop-DbaAgentJob
            $results.CurrentRunStatus -eq 'Idle' | Should Be $true
        }
    }
}
tools\dbatools\tests\Stop-DbaPfDataCollectorSet.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $script:set = Get-DbaPfDataCollectorSet | Select-Object -First 1
        $script:set | Start-DbaPfDataCollectorSet -WarningAction SilentlyContinue
        Start-Sleep 2
    }
    AfterAll {
        $script:set | Stop-DbaPfDataCollectorSet -WarningAction SilentlyContinue
    }
    Context "Verifying command works" {
        It "returns a result with the right computername and name is not null" {
            $results = $script:set | Select-Object -First 1 | Stop-DbaPfDataCollectorSet -WarningAction SilentlyContinue -WarningVariable warn
            if (-not $warn) {
                $results.ComputerName | Should Be $env:COMPUTERNAME
                $results.Name | Should Not Be $null
            }
        }
    }
}
tools\dbatools\tests\Stop-DbaProcess.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    Context "command works as expected" {
        $fakeapp = Connect-DbaInstance -SqlInstance $script:instance1 -ClientName 'dbatoolsci test app'
        $results = Stop-DbaProcess -SqlInstance $script:instance1 -Program 'dbatoolsci test app'
        It "kills only this specific process" {
            $results.Program.Count | Should -Be 1
            $results.Program | Should -Be 'dbatoolsci test app'
            $results.Status | Should -Be 'Killed'
        }
        $fakeapp = Connect-DbaInstance -SqlInstance $script:instance1 -ClientName 'dbatoolsci test app'
        $results = Get-DbaProcess -SqlInstance $script:instance1 -Program 'dbatoolsci test app' | Stop-DbaProcess
        It "supports piping" {
            $results.Program.Count | Should -Be 1
            $results.Program | Should -Be 'dbatoolsci test app'
            $results.Status | Should -Be 'Killed'
        }
    }
}
tools\dbatools\tests\Stop-DbaSqlService.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"
. "$PSScriptRoot\..\internal\functions\Connect-SqlInstance.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {

    Context "Command actually works" {

        $server = Connect-SqlInstance -SqlInstance $script:instance2
        $instanceName = $server.ServiceName
        $computerName = $server.NetName

        It "stops some services" {
            $services = Stop-DbaSqlService -ComputerName $script:instance2 -InstanceName $instanceName -Type Agent
            $services | Should Not Be $null
            foreach ($service in $services) {
                $service.State | Should Be 'Stopped'
                $service.Status | Should Be 'Successful'
            }
        }

        #Start services using native cmdlets
        if ($instanceName -eq 'MSSQLSERVER') {
            $serviceName = "SQLSERVERAGENT"
        }
        else {
            $serviceName = "SqlAgent`$$instanceName"
        }
        Get-Service -ComputerName $computerName -Name $serviceName | Start-Service -WarningAction SilentlyContinue | Out-Null

        It "stops specific services based on instance name through pipeline" {
            $services = Get-DbaSqlService -ComputerName $script:instance2 -InstanceName $instanceName -Type Agent, Engine | Stop-DbaSqlService
            $services | Should Not Be $null
            foreach ($service in $services) {
                $service.State | Should Be 'Stopped'
                $service.Status | Should Be 'Successful'
            }
        }

        #Start services using native cmdlets
        if ($instanceName -eq 'MSSQLSERVER') {
            $serviceName = "MSSQLSERVER", "SQLSERVERAGENT"
        }
        else {
            $serviceName = "MsSql`$$instanceName", "SqlAgent`$$instanceName"
        }
        foreach ($sn in $servicename) { Get-Service -ComputerName $computerName -Name $sn | Start-Service -WarningAction SilentlyContinue | Out-Null }

    }
}
tools\dbatools\tests\Stop-DbaTrace.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $sql = "-- Create a Queue
                declare @rc int
                declare @TraceID int
                declare @maxfilesize bigint
                set @maxfilesize = 5
                exec @rc = sp_trace_create @TraceID output, 0, N'C:\windows\temp\temptrace', @maxfilesize, NULL

                -- Set the events
                declare @on bit
                set @on = 1
                exec sp_trace_setevent @TraceID, 14, 1, @on
                exec sp_trace_setevent @TraceID, 14, 9, @on
                exec sp_trace_setevent @TraceID, 14, 10, @on
                exec sp_trace_setevent @TraceID, 14, 11, @on
                exec sp_trace_setevent @TraceID, 14, 6, @on
                exec sp_trace_setevent @TraceID, 14, 12, @on
                exec sp_trace_setevent @TraceID, 14, 14, @on
                exec sp_trace_setevent @TraceID, 15, 11, @on
                exec sp_trace_setevent @TraceID, 15, 6, @on
                exec sp_trace_setevent @TraceID, 15, 9, @on
                exec sp_trace_setevent @TraceID, 15, 10, @on
                exec sp_trace_setevent @TraceID, 15, 12, @on
                exec sp_trace_setevent @TraceID, 15, 13, @on
                exec sp_trace_setevent @TraceID, 15, 14, @on
                exec sp_trace_setevent @TraceID, 15, 15, @on
                exec sp_trace_setevent @TraceID, 15, 16, @on
                exec sp_trace_setevent @TraceID, 15, 17, @on
                exec sp_trace_setevent @TraceID, 15, 18, @on
                exec sp_trace_setevent @TraceID, 17, 1, @on
                exec sp_trace_setevent @TraceID, 17, 9, @on
                exec sp_trace_setevent @TraceID, 17, 10, @on
                exec sp_trace_setevent @TraceID, 17, 11, @on
                exec sp_trace_setevent @TraceID, 17, 6, @on
                exec sp_trace_setevent @TraceID, 17, 12, @on
                exec sp_trace_setevent @TraceID, 17, 14, @on
                exec sp_trace_setevent @TraceID, 10, 9, @on
                exec sp_trace_setevent @TraceID, 10, 2, @on
                exec sp_trace_setevent @TraceID, 10, 10, @on
                exec sp_trace_setevent @TraceID, 10, 6, @on
                exec sp_trace_setevent @TraceID, 10, 11, @on
                exec sp_trace_setevent @TraceID, 10, 12, @on
                exec sp_trace_setevent @TraceID, 10, 13, @on
                exec sp_trace_setevent @TraceID, 10, 14, @on
                exec sp_trace_setevent @TraceID, 10, 15, @on
                exec sp_trace_setevent @TraceID, 10, 16, @on
                exec sp_trace_setevent @TraceID, 10, 17, @on
                exec sp_trace_setevent @TraceID, 10, 18, @on
                exec sp_trace_setevent @TraceID, 12, 1, @on
                exec sp_trace_setevent @TraceID, 12, 9, @on
                exec sp_trace_setevent @TraceID, 12, 11, @on
                exec sp_trace_setevent @TraceID, 12, 6, @on
                exec sp_trace_setevent @TraceID, 12, 10, @on
                exec sp_trace_setevent @TraceID, 12, 12, @on
                exec sp_trace_setevent @TraceID, 12, 13, @on
                exec sp_trace_setevent @TraceID, 12, 14, @on
                exec sp_trace_setevent @TraceID, 12, 15, @on
                exec sp_trace_setevent @TraceID, 12, 16, @on
                exec sp_trace_setevent @TraceID, 12, 17, @on
                exec sp_trace_setevent @TraceID, 12, 18, @on
                exec sp_trace_setevent @TraceID, 13, 1, @on
                exec sp_trace_setevent @TraceID, 13, 9, @on
                exec sp_trace_setevent @TraceID, 13, 11, @on
                exec sp_trace_setevent @TraceID, 13, 6, @on
                exec sp_trace_setevent @TraceID, 13, 10, @on
                exec sp_trace_setevent @TraceID, 13, 12, @on
                exec sp_trace_setevent @TraceID, 13, 14, @on

                -- Set the Filters
                declare @intfilter int
                declare @bigintfilter bigint

                exec sp_trace_setfilter @TraceID, 10, 0, 7, N'SQL Server Profiler - 934a8575-0dc1-4937-bde1-edac1cb9691f'
                -- Set the trace status to start
                exec sp_trace_setstatus @TraceID, 1

                -- display trace id for future references
                select TraceID=@TraceID"
        $server = Connect-DbaInstance -SqlInstance $script:instance1
        $traceid = ($server.Query($sql)).TraceID
        $null = Get-DbaTrace -SqlInstance $script:instance1 -Id $traceid | Start-DbaTrace
    }
    AfterAll {
        $null = Remove-DbaTrace -SqlInstance $script:instance1 -Id $traceid
        Remove-Item C:\windows\temp\temptrace.trc
    }
    Context "Test Stopping Trace" {
        $results = Get-DbaTrace -SqlInstance $script:instance1 -Id $traceid
        It "starts in a running state" {
            $results.Id | Should Be $traceid
            $results.IsRunning | Should Be $true
        }
        $results = Get-DbaTrace -SqlInstance $script:instance1 -Id $traceid | Stop-DbaTrace
        It "is now in a stopped state" {
            $results.Id | Should Be $traceid
            $results.IsRunning | Should Be $false
        }
    }
}
tools\dbatools\tests\Stop-DbaXESession.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $server = Connect-DbaInstance -SqlInstance $script:instance2
        $conn = $server.ConnectionContext
        # Get the systemhealth session
        $systemhealth = Get-DbaXESession -SqlInstance $server -Session system_health
        # Create a valid session and start it
        $conn.ExecuteNonQuery("CREATE EVENT SESSION [dbatoolsci_session_valid] ON SERVER ADD EVENT sqlserver.lock_acquired;")
        $dbatoolsciValid = Get-DbaXESession -SqlInstance $server -Session dbatoolsci_session_valid
        $dbatoolsciValid.Start()
        # Record the Status of all sessions
        $allSessions = Get-DbaXESession -SqlInstance $server
    }
    BeforeEach {
        $systemhealth.Refresh()
        if (-Not $systemhealth.IsRunning) {
            $systemhealth.Start()
        }
    }
    AfterAll {
        # Set the Status of all session back to what they were before the test
        foreach ($session in $allSessions) {
            $session.Refresh()
            if ($session.Status -eq "Stopped") {
                if ($session.IsRunning) {
                    $session.Stop()
                }
            }
            else {
                if (-Not $session.IsRunning) {
                    $session.Start()
                }
            }
        }

        # Drop created objects
        $conn.ExecuteNonQuery("IF EXISTS(SELECT * FROM sys.server_event_sessions WHERE name = 'dbatoolsci_session_valid') DROP EVENT SESSION [dbatoolsci_session_valid] ON SERVER;")
    }

    Context "Verifying command works" {
        It "stops the system_health session" {
            $systemhealth | Stop-DbaXESession
            $systemhealth.Refresh()
            $systemhealth.IsRunning | Should Be $false
        }

        It "does not change state if XE session is already stopped" {
            if ($systemhealth.IsRunning) {
                $systemhealth.Stop()
            }
            Stop-DbaXESession $server -Session $systemhealth.Name -WarningAction SilentlyContinue
            $systemhealth.Refresh()
            $systemhealth.IsRunning | Should Be $false
        }

        It "stops all XE Sessions except the system ones if -AllSessions is used" {
            Stop-DbaXESession $server -AllSessions -WarningAction SilentlyContinue
            $systemhealth.Refresh()
            $dbatoolsciValid.Refresh()
            $systemhealth.IsRunning | Should Be $true
            $dbatoolsciValid.IsRunning | Should Be $false
        }
    }
}
tools\dbatools\tests\Stop-Function.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"
. "$PSScriptRoot\..\internal\functions\Stop-Function.ps1"

Describe "$commandname Unit Tests" -Tag 'UnitTests' {
    Context "Testing non-EnableException: Explicit call" {
        try {
            $warning = Stop-Function -Message "Nonsilent Foo" -EnableException $false -Category InvalidResult -FunctionName "Invoke-Pester" -Target "Bar" -ErrorAction Stop 3>&1
            $record = $Error[0]
            $failed = $false
        }
        catch {
            $record = $null
            $failed = $true
        }

        It "Should not have failed to execute without an exception!" {
            $failed | Should Be $false
        }

        It "Should have written the test warning 'Nonsilent Foo'" {
            $warning[0] | Should BeLike "*Nonsilent Foo"
        }

        It "Should have created an error record with the correct exception" {
            $record.Exception.Message | Should Be "Nonsilent Foo"
        }

        It "Should have created an error record with the caegory 'InvalidResult'" {
            $record.CategoryInfo.Category | Should BeLike "InvalidResult"
        }

        It "Should have created an error record with the targetobject 'Bar'" {
            $record.TargetObject | Should Be "Bar"
        }

        It "Should have created an error record with the ErrorID 'dbatools_Invoke-Pester'" {
            $record.FullyQualifiedErrorId | Should Be "dbatools_Invoke-Pester,Stop-Function"
        }
    }

    Context "Testing non-EnableException: In try/catch" {
        try {
            try {
                $null.GetType()
            }
            catch {
                $warning = Stop-Function -Message "Nonsilent Foo" -EnableException $false -InnerErrorRecord $_ -FunctionName "Invoke-Pester" -Target "Bar" -ErrorAction Stop 3>&1
                $record = $Error[0]
                $failed = $false
            }
        }
        catch {
            $record = $null
            $failed = $true
        }

        It "Should not have failed to execute without an exception!" {
            $failed | Should Be $false
        }

        It "Should have written the test warning 'Nonsilent Foo | '" {
            $warning[0] | Should BeLike "*Nonsilent Foo | *"
        }

        It "Should have created an error record with the correct exception" {
            $record.Exception.InnerException.GetType().FullName | Should Be "System.Management.Automation.RuntimeException"
        }

        It "Should have created an error record with the category 'InvalidOperation'" {
            $record.CategoryInfo.Category | Should BeLike "InvalidOperation"
        }

        It "Should have created an error record with the targetobject 'Bar'" {
            $record.TargetObject | Should Be "Bar"
        }

        It "Should have created an error record with the ErrorID 'dbatools_Invoke-Pester'" {
            $record.FullyQualifiedErrorId | Should Be "dbatools_Invoke-Pester,Stop-Function"
        }

        It "Should have created an error record with the an inner NULL-invocation exception" {
            try {
                $ExceptionName = $record.Exception.InnerException.GetType().FullName
            }
            catch {
                $ExceptionName = "Meeep!"
            }

            $ExceptionName | Should Be "System.Management.Automation.RuntimeException"
        }
    }

    Context "Testing non-EnableException: Continue & ContinueLabel" {
        Mock -CommandName "Write-Warning" -MockWith { Param ($Message) }

        #region Run Tests
        try {
            $failed = $false
            $a = 0
            $b = 0
            foreach ($number in (1 .. 3)) {
                $a++
                Stop-Function -Message "Nonsilent Foo" -EnableException $false -Category InvalidOperation -Continue -ErrorAction Stop 3>&1
                $b++
            }
        }
        catch {
            $failed = $true
        }

        try {
            $failed2 = $false
            $c = 0
            $d = 0
            $e = 0
            $f = 0

            :main foreach ($number in (1 .. 3)) {
                $c++
                foreach ($Counter in (1 .. 3)) {
                    $d++
                    Stop-Function -Message "Nonsilent Foo" -EnableException $false -Category InvalidOperation -Continue -ContinueLabel "main" -ErrorAction Stop 3>&1
                    $e++
                }
                $f++
            }
        }
        catch {
            $failed2 = $true
        }
        #endregion Run Tests

        #region Evaluate Results
        It "Should not have failed to execute without an exception when testing Continue without a label!" {
            $failed | Should Be $false
        }

        It "Should not have failed to execute without an exception when testing Continue with a label!" {
            $failed2 | Should Be $false
        }

        It "Should have incremented the first counter when calling continue without a label" {
            $a | Should Be 3
        }

        It "Should not have incremented the second counter when calling continue without a label" {
            $b | Should Be 0
        }

        It "Should have incremented the first two counters thrice, but skipped the other two when calling continue with a label" {
            [int[]]$result = @($c, $d, $e, $f)
            [int[]]$reference = @(3, 3, 0, 0)
            $result | Should Be $reference
        }
        #endregion Evaluate Results
    }

    Context "Testing silent: Explicit call" {
        try {
            Stop-Function -Message "Nonsilent Foo" -EnableException $true -Category InvalidResult -FunctionName "Invoke-Pester" -Target "Bar" -ErrorAction Stop
            $record = $null
            $failed = $false
        }
        catch {
            $record = $_
            $failed = $true
        }

        It "Should not have failed to terminate with an exception!" {
            $failed | Should Be $true
        }

        It "Should have created an error record with the correct exception" {
            $record.Exception.Message | Should Be "Nonsilent Foo"
        }

        It "Should have created an error record with the caegory 'InvalidResult'" {
            $record.CategoryInfo.Category | Should BeLike "InvalidResult"
        }

        It "Should have created an error record with the targetobject 'Bar'" {
            $record.TargetObject | Should Be "Bar"
        }

        It "Should have created an error record with the ErrorID 'dbatools_Invoke-Pester,Stop-Function'" {
            $record.FullyQualifiedErrorId | Should Be "dbatools_Invoke-Pester"
        }
    }

    Context "Testing silent: In try/catch" {
        try {
            try {
                $null.GetType()
            }
            catch {
                Stop-Function -Message "Nonsilent Foo" -EnableException $true -InnerErrorRecord $_ -FunctionName "Invoke-Pester" -Target "Bar" -ErrorAction Stop
                $record = $null
                $failed = $false
            }
        }
        catch {
            $record = $_
            $failed = $true
        }

        It "Should not have failed to terminate with an exception!" {
            $failed | Should Be $true
        }

        It "Should have created an error record with the correct exception" {
            $record.Exception.InnerException.GetType().FullName | Should Be "System.Management.Automation.RuntimeException"
        }

        It "Should have created an error record with the caegory 'InvalidOperation'" {
            $record.CategoryInfo.Category | Should BeLike "InvalidOperation"
        }

        It "Should have created an error record with the targetobject 'Bar'" {
            $record.TargetObject | Should Be "Bar"
        }

        It "Should have created an error record with the ErrorID 'dbatools_Invoke-Pester'" {
            $record.FullyQualifiedErrorId | Should Be "dbatools_Invoke-Pester"
        }
    }

    Context "Testing silent: Continue & ContinueLabel" {
        Mock -CommandName "Write-Error" -MockWith { Param ($Message) }

        #region Run Tests
        try {
            $failed = $false
            $a = 0
            $b = 0
            foreach ($number in (1 .. 3)) {
                $a++
                Stop-Function -Message "Nonsilent Foo" -EnableException $true -Category InvalidOperation -SilentlyContinue -ErrorAction Stop
                $b++
            }
        }
        catch {
            $failed = $true
        }

        try {
            $failed2 = $false
            $c = 0
            $d = 0
            $e = 0
            $f = 0

            :main foreach ($number in (1 .. 3)) {
                $c++
                foreach ($Counter in (1 .. 3)) {
                    $d++
                    Stop-Function -Message "Nonsilent Foo" -EnableException $true -Category InvalidOperation -SilentlyContinue -ContinueLabel "main" -ErrorAction Stop
                    $e++
                }
                $f++
            }
        }
        catch {
            $failed2 = $true
        }
        #endregion Run Tests

        #region Evaluate Results
        It "Should not have failed to execute without an exception when testing Continue without a label!" {
            $failed | Should Be $false
        }

        It "Should not have failed to execute without an exception when testing Continue with a label!" {
            $failed2 | Should Be $false
        }

        It "Should have incremented the first counter when calling continue without a label" {
            $a | Should Be 3
        }

        It "Should not have incremented the second counter when calling continue without a label" {
            $b | Should Be 0
        }

        It "Should have incremented the first two counters thrice, but skipped the other two when calling continue with a label" {
            [int[]]$result = @($c, $d, $e, $f)
            [int[]]$reference = @(3, 3, 0, 0)
            $result | Should Be $reference
        }
        #endregion Evaluate Results
    }
}
tools\dbatools\tests\Test-DbaBackupInformation.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Unit Tests" -Tag 'UnitTests' {
    InModuleScope dbatools {
        Context "Everything as it should" {
            $BackupHistory = Import-CliXml $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\CleanFormatDbaInformation.xml
            $BackupHistory = $BackupHistory | Format-DbaBackupInformation
            Mock Connect-SqlInstance -MockWith {
                $obj = [PSCustomObject]@{
                    Name                 = 'BASEName'
                    NetName              = 'BASENetName'
                    InstanceName         = 'BASEInstanceName'
                    DomainInstanceName   = 'BASEDomainInstanceName'
                    InstallDataDirectory = 'BASEInstallDataDirectory'
                    ErrorLogPath         = 'BASEErrorLog_{0}_{1}_{2}_Path' -f "'", '"', ']'
                    ServiceName          = 'BASEServiceName'
                    VersionMajor         = 9
                    ConnectionContext    = New-Object PSObject
                }
                Add-Member -InputObject $obj.ConnectionContext -Name ConnectionString  -MemberType NoteProperty -Value 'put=an=equal=in=it'
                Add-Member -InputObject $obj -Name Query -MemberType ScriptMethod -Value {
                    param($query)
                    if ($query -eq "SELECT DB_NAME(database_id) AS Name, physical_name AS PhysicalName FROM sys.master_files") {
                        return @(
                        @{ "Name"         = "master"
                           "PhysicalName" = "C:\temp\master.mdf"
                        }
                        )
                    }
                }
                $obj.PSObject.TypeNames.Clear()
                $obj.PSObject.TypeNames.Add("Microsoft.SqlServer.Management.Smo.Server")
                return $obj
            }
            Mock Get-DbaDatabase { $null }

            Mock New-DbaSqlDirectory {$true}
            Mock Test-DbaSqlPath { [pscustomobject]@{
                    FilePath   = 'does\exists'
                    FileExists = $true
                }
            }
            Mock New-DbaSqlDirectory {$True}
            It "Should pass as all systems Green" {
                $output = $BackupHistory | Test-DbaBackupInformation -SqlServer NotExist -WarningVariable warnvar -WarningAction SilentlyContinue
                ($output.Count) -gt 0 | Should be $true
                $false -in ($Output.IsVerified) | Should be $False
                ($null -ne $WarnVar) | Should be $True
            }
        }
        Context "Not being able to see backups is bad" {
            $BackupHistory = Import-CliXml $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\CleanFormatDbaInformation.xml
            $BackupHistory = $BackupHistory | Format-DbaBackupInformation
            Mock Connect-SqlInstance -MockWith {
                $obj = [PSCustomObject]@{
                    Name                 = 'BASEName'
                    NetName              = 'BASENetName'
                    InstanceName         = 'BASEInstanceName'
                    DomainInstanceName   = 'BASEDomainInstanceName'
                    InstallDataDirectory = 'BASEInstallDataDirectory'
                    ErrorLogPath         = 'BASEErrorLog_{0}_{1}_{2}_Path' -f "'", '"', ']'
                    ServiceName          = 'BASEServiceName'
                    VersionMajor         = 9
                    ConnectionContext    = New-Object PSObject
                }
                Add-Member -InputObject $obj.ConnectionContext -Name ConnectionString  -MemberType NoteProperty -Value 'put=an=equal=in=it'
                Add-Member -InputObject $obj -Name Query -MemberType ScriptMethod -Value {
                    param($query)
                    if ($query -eq "SELECT DB_NAME(database_id) AS Name, physical_name AS PhysicalName FROM sys.master_files") {
                        return @(
                        @{ "Name"         = "master"
                           "PhysicalName" = "C:\temp\master.mdf"
                        }
                        )
                    }
                }
                $obj.PSObject.TypeNames.Clear()
                $obj.PSObject.TypeNames.Add("Microsoft.SqlServer.Management.Smo.Server")
                return $obj
            }
            Mock Get-DbaDatabase { $null }
            Mock New-DbaSqlDirectory {$true}
            Mock Test-DbaSqlPath { [pscustomobject]@{
                    FilePath   = 'does\not\exists'
                    FileExists = $false
                }
            }
            Mock New-DbaSqlDirectory {$True}
            It "Should return fail as backup files don't exist" {
                $output = $BackupHistory | Test-DbaBackupInformation -SqlServer NotExist -WarningVariable warnvar -WarningAction SilentlyContinue
                ($output.Count) -gt 0 | Should be $true
                $true -in ($Output.IsVerified) | Should be $false
                ($null -ne $WarnVar) | Should be $True
            }
        }
        Context "Multiple source dbs for restore is bad" {
            $BackupHistory = Import-CliXml $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\CleanFormatDbaInformation.xml
            $BackupHistory = $BackupHistory | Format-DbaBackupInformation
            $BackupHistory[1].OriginalDatabase = 'Error'
            Mock Connect-SqlInstance -MockWith {
                $obj = [PSCustomObject]@{
                    Name                 = 'BASEName'
                    NetName              = 'BASENetName'
                    InstanceName         = 'BASEInstanceName'
                    DomainInstanceName   = 'BASEDomainInstanceName'
                    InstallDataDirectory = 'BASEInstallDataDirectory'
                    ErrorLogPath         = 'BASEErrorLog_{0}_{1}_{2}_Path' -f "'", '"', ']'
                    ServiceName          = 'BASEServiceName'
                    VersionMajor         = 9
                    ConnectionContext    = New-Object PSObject
                }
                Add-Member -InputObject $obj.ConnectionContext -Name ConnectionString  -MemberType NoteProperty -Value 'put=an=equal=in=it'
                Add-Member -InputObject $obj -Name Query -MemberType ScriptMethod -Value {
                    param($query)
                    if ($query -eq "SELECT DB_NAME(database_id) AS Name, physical_name AS PhysicalName FROM sys.master_files") {
                        return @(
                        @{ "Name"         = "master"
                           "PhysicalName" = "C:\temp\master.mdf"
                        }
                        )
                    }
                }
                $obj.PSObject.TypeNames.Clear()
                $obj.PSObject.TypeNames.Add("Microsoft.SqlServer.Management.Smo.Server")
                return $obj
            }
            Mock Get-DbaDatabase { $null }
            Mock New-DbaSqlDirectory {$true}
            Mock Test-DbaSqlPath { [pscustomobject]@{
                    FilePath   = 'does\exists'
                    FileExists = $true
                }
            }
            Mock New-DbaSqlDirectory {$True}
            It "Should return fail as 2 origin dbs" {
                $output = $BackupHistory | Test-DbaBackupInformation -SqlServer NotExist -WarningVariable warnvar -WarningAction SilentlyContinue
                ($output.Count) -gt 0 | Should be $true
                $true -in ($Output.IsVerified) | Should be $False
                ($null -ne $WarnVar) | Should be $True
            }
        }
        Context "Fail if Destination db exists" {
            $BackupHistory = Import-CliXml $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\CleanFormatDbaInformation.xml
            $BackupHistory = $BackupHistory | Format-DbaBackupInformation
            $BackupHistory[1].OriginalDatabase = 'Error'
            Mock Connect-SqlInstance -MockWith {
                $obj = [PSCustomObject]@{
                    Name                 = 'BASEName'
                    NetName              = 'BASENetName'
                    InstanceName         = 'BASEInstanceName'
                    DomainInstanceName   = 'BASEDomainInstanceName'
                    InstallDataDirectory = 'BASEInstallDataDirectory'
                    ErrorLogPath         = 'BASEErrorLog_{0}_{1}_{2}_Path' -f "'", '"', ']'
                    ServiceName          = 'BASEServiceName'
                    VersionMajor         = 9
                    ConnectionContext    = New-Object PSObject
                }
                Add-Member -InputObject $obj.ConnectionContext -Name ConnectionString  -MemberType NoteProperty -Value 'put=an=equal=in=it'
                Add-Member -InputObject $obj -Name Query -MemberType ScriptMethod -Value {
                    param($query)
                    if ($query -eq "SELECT DB_NAME(database_id) AS Name, physical_name AS PhysicalName FROM sys.master_files") {
                        return @(
                        @{ "Name"         = "master"
                           "PhysicalName" = "C:\temp\master.mdf"
                        }
                        )
                    }
                }
                $obj.PSObject.TypeNames.Clear()
                $obj.PSObject.TypeNames.Add("Microsoft.SqlServer.Management.Smo.Server")
                return $obj
            }
            Mock Get-DbaDatabase { '1' }
            Mock New-DbaSqlDirectory {$true}
            Mock Test-DbaSqlPath { [pscustomobject]@{
                    FilePath   = 'does\exists'
                    FileExists = $true
                }
            }
            Mock New-DbaSqlDirectory {$True}
            It "Should return fail if dest db exists" {
                $output = $BackupHistory | Test-DbaBackupInformation -SqlServer NotExist -WarningVariable warnvar -WarningAction SilentlyContinue
                ($output.Count) -gt 0 | Should be $true
                $true -in ($Output.IsVerified) | Should be $False
                ($null -ne $WarnVar) | Should be $True
            }
        }
        Context "Pass if Destination db exists and WithReplace set" {
            $BackupHistory = Import-CliXml $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\CleanFormatDbaInformation.xml
            $BackupHistory = $BackupHistory | Format-DbaBackupInformation
            $BackupHistory[1].OriginalDatabase = 'Error'
            Mock Connect-SqlInstance -MockWith {
                $obj = [PSCustomObject]@{
                    Name                 = 'BASEName'
                    NetName              = 'BASENetName'
                    InstanceName         = 'BASEInstanceName'
                    DomainInstanceName   = 'BASEDomainInstanceName'
                    InstallDataDirectory = 'BASEInstallDataDirectory'
                    ErrorLogPath         = 'BASEErrorLog_{0}_{1}_{2}_Path' -f "'", '"', ']'
                    ServiceName          = 'BASEServiceName'
                    VersionMajor         = 9
                    ConnectionContext    = New-Object PSObject
                }
                Add-Member -InputObject $obj.ConnectionContext -Name ConnectionString  -MemberType NoteProperty -Value 'put=an=equal=in=it'
                Add-Member -InputObject $obj -Name Query -MemberType ScriptMethod -Value {
                    param($query)
                    if ($query -eq "SELECT DB_NAME(database_id) AS Name, physical_name AS PhysicalName FROM sys.master_files") {
                        return @(
                        @{ "Name"         = "master"
                           "PhysicalName" = "C:\temp\master.mdf"
                        }
                        )
                    }
                }
                $obj.PSObject.TypeNames.Clear()
                $obj.PSObject.TypeNames.Add("Microsoft.SqlServer.Management.Smo.Server")
                return $obj
            }
            Mock Get-DbaDatabase { '1' }
            Mock New-DbaSqlDirectory {$true}
            Mock Test-DbaSqlPath { [pscustomobject]@{
                    FilePath   = 'does\exists'
                    FileExists = $true
                }
            }
            Mock New-DbaSqlDirectory {$True}
            It "Should pass if destdb exists and WithReplace specified" {
                $output = $BackupHistory | Test-DbaBackupInformation -SqlServer NotExist -WarningVariable warnvar -WarningAction SilentlyContinue -WithReplace
                ($output.Count) -gt 0 | Should be $true
                $true -in ($Output.IsVerified) | Should be $False
                ($null -ne $WarnVar) | Should be $True
            }
        }
    }
}
tools\dbatools\tests\Test-DbaCmConnection.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    $results = Test-DbaCmConnection -Type Wmi
    It "returns some valid info" {
        $results.ComputerName -eq $env:COMPUTERNAME
        $results.Available -is [bool]
    }
}
tools\dbatools\tests\Test-DbaConnection.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Testing if command works" {

        $results = Test-DbaConnection -SqlInstance $script:instance1
        $whoami = whoami
        It "returns the correct port" {
            $results.TcpPort -eq 1433 | Should Be $true
        }

        It "returns the correct authtype" {
            $results.AuthType -eq 'Windows Authentication' | Should Be $true
        }

        It "returns the correct user" {
            $results.ConnectingAsUser -eq $whoami | Should Be $true
        }
    }
}
tools\dbatools\tests\Test-DbaConnectionAuthScheme.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "returns the proper transport" {
        $results = Test-DbaConnectionAuthScheme -SqlInstance $script:instance1
        It "returns ntlm auth scheme" {
            $results.AuthScheme | Should Be 'ntlm'
        }
    }
}
tools\dbatools\tests\Test-DbaDatabaseCollation.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {

    Context "testing collation of a single database" {
        BeforeAll {
            $server = Connect-DbaInstance -SqlInstance $script:instance1
            $db1 = "dbatoolsci_collation"
            Get-DbaDatabase -SqlInstance $server -Database $db1 | Remove-DbaDatabase -Confirm:$false
            $server.Query("CREATE DATABASE $db1")
        }
        AfterAll {
            Get-DbaDatabase -SqlInstance $server -Database $db1 | Remove-DbaDatabase -Confirm:$false
        }

        It "confirms the db is the same collation as the server" {
            $result = Test-DbaDatabaseCollation -SqlInstance $script:instance1 -Database $db1
            $result.IsEqual
        }
    }
}
tools\dbatools\tests\Test-DbaDatabaseOwner.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        Get-DbaProcess -SqlInstance $script:instance1 -Program 'dbatools PowerShell module - dbatools.io' | Stop-DbaProcess -WarningAction SilentlyContinue
        $dbname = "dbatoolsci_testdbowner"
        $server = Connect-DbaInstance -SqlInstance $script:instance1
        $null = $server.Query("Create Database [$dbname]")
       }
    AfterAll {
        Remove-DbaDatabase -SqlInstance $script:instance1 -Database $dbname -Confirm:$false
    }
    
    It "return the correct information including database, currentowner and targetowner" {
        $whoami = whoami
        $results = Test-DbaDatabaseOwner -SqlInstance $script:instance1 -Database $dbname
        $results.Database -eq $dbname
        $results.CurrentOwner -eq $whoami
        $results.TargetOwner -eq 'sa'
    }
}

Describe "$CommandName Unit Tests" -Tag "UnitTests" {
    Context "Validate parameters" {
        <#
            The $paramCount is adjusted based on the parameters your command will have.

            The $defaultParamCount is adjusted based on what type of command you are writing the test for:
                - Commands that *do not* include SupportShouldProcess, set defaultParamCount    = 11
                - Commands that *do* include SupportShouldProcess, set defaultParamCount        = 13
        #>
        $paramCount = 7
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Test-DbaDatabaseOwner).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'Database', 'ExcludeDatabase', 'TargetLogin', 'EnableException', 'Detailed'
        It "Should contain our specific parameters" {
            ((Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
    InModuleScope 'dbatools' {
        Context "Connects to SQL Server" {
            It -Skip "Should not throw" {
                Mock Connect-SQLInstance -MockWith {
                    [object]@{
                        Name       = 'SQLServerName';
                        Databases  = [object]@(
                            @{
                                Name    = 'db1';
                                Status  = 'Normal';
                                Owner   = 'sa'
                            }
                        ); #databases
                        Logins     = [object]@(
                            @{
                                ID    = 1;
                                Name  = 'sa';
                            }
                        ) #logins
                    } #object
                } #mock connect-sqlserver
                
                { Test-DbaDatabaseOwner -SqlInstance 'SQLServerName' } | Should Not throw
            } #It
            It -Skip "Should not return if no wrong owner for default" {
                Mock Connect-SQLInstance -MockWith {
                    [object]@{
                        Name       = 'SQLServerName';
                        Databases  = [object]@(
                            @{
                                Name    = 'db1';
                                Status  = 'Normal';
                                Owner   = 'sa'
                            }
                        ); #databases
                        Logins     = [object]@(
                            @{
                                ID    = 1;
                                Name  = 'sa';
                            }
                        ) #logins
                    } #object
                } #mock connect-sqlserver
                
                { Test-DbaDatabaseOwner -SqlInstance 'SQLServerName' } | Should Not throw
            } #It
            It -Skip "Should return wrong owner information for one database with no owner specified" {
                Mock Connect-SQLInstance -MockWith {
                    [object]@{
                        DomainInstanceName  = 'SQLServerName';
                        Databases           = [object]@(
                            @{
                                Name    = 'db1';
                                Status  = 'Normal';
                                Owner   = 'WrongOWner'
                            }
                        ); #databases
                        Logins              = [object]@(
                            @{
                                ID    = 1;
                                Name  = 'sa';
                            }
                        ) #logins
                    } #object
                } #mock connect-sqlserver
                
                $Result = Test-DbaDatabaseOwner -SqlInstance 'SQLServerName'
                $Result[0].SqlInstance | Should Be 'SQLServerName'
                $Result[0].Database | Should Be 'db1';
                $Result[0].DBState | Should Be 'Normal';
                $Result[0].CurrentOwner | Should Be 'WrongOWner';
                $Result[0].TargetOwner | Should Be 'sa';
                $Result[0].OwnerMatch | Should Be $False
            } # it
            It -Skip "Should return information for one database with correct owner with detail parameter" {
                Mock Connect-SQLInstance -MockWith {
                    [object]@{
                        DomainInstanceName  = 'SQLServerName';
                        Databases           = [object]@(
                            @{
                                Name    = 'db1';
                                Status  = 'Normal';
                                Owner   = 'sa'
                            }
                        ); #databases
                        Logins              = [object]@(
                            @{
                                ID    = 1;
                                Name  = 'sa';
                            }
                        ) #logins
                    } #object
                } #mock connect-sqlserver
                
                $Result = Test-DbaDatabaseOwner -SqlInstance 'SQLServerName'
                $Result.SqlInstance | Should Be 'SQLServerName'
                $Result.Database | Should Be 'db1';
                $Result.DBState | Should Be 'Normal';
                $Result.CurrentOwner | Should Be 'sa';
                $Result.TargetOwner | Should Be 'sa';
                $Result.OwnerMatch | Should Be $True
            } # it
            It -Skip "Should return wrong owner information for one database with no owner specified and multiple databases" {
                Mock Connect-SQLInstance -MockWith {
                    [object]@{
                        DomainInstanceName  = 'SQLServerName';
                        Databases           = [object]@(
                            @{
                                Name    = 'db1';
                                Status  = 'Normal';
                                Owner   = 'WrongOWner'
                            }
                            @{
                                Name    = 'db2';
                                Status  = 'Normal';
                                Owner   = 'sa'
                            }
                        ); #databases
                        Logins              = [object]@(
                            @{
                                ID    = 1;
                                Name  = 'sa';
                            }
                        ) #logins
                    } #object
                } #mock connect-sqlserver
                
                $Result = Test-DbaDatabaseOwner -SqlInstance 'SQLServerName'
                $Result[0].SqlInstance | Should Be 'SQLServerName'
                $Result[0].Database | Should Be 'db1';
                $Result[0].DBState | Should Be 'Normal';
                $Result[0].CurrentOwner | Should Be 'WrongOWner';
                $Result[0].TargetOwner | Should Be 'sa';
                $Result[0].OwnerMatch | Should Be $False
            } # it
            It -Skip "Should return wrong owner information for two databases with no owner specified and multiple databases" {
                Mock Connect-SQLInstance -MockWith {
                    [object]@{
                        DomainInstanceName  = 'SQLServerName';
                        Databases           = [object]@(
                            @{
                                Name    = 'db1';
                                Status  = 'Normal';
                                Owner   = 'WrongOWner'
                            }
                            @{
                                Name    = 'db2';
                                Status  = 'Normal';
                                Owner   = 'WrongOWner'
                            }
                        ); #databases
                        Logins              = [object]@(
                            @{
                                ID    = 1;
                                Name  = 'sa';
                            }
                        ) #logins
                    } #object
                } #mock connect-sqlserver
                
                $Result = Test-DbaDatabaseOwner -SqlInstance 'SQLServerName'
                $Result[0].SqlInstance | Should Be 'SQLServerName'
                $Result[1].SqlInstance | Should Be 'SQLServerName'
                $Result[0].Database | Should Be 'db1';
                $Result[1].Database | Should Be 'db2';
                $Result[0].DBState | Should Be 'Normal';
                $Result[1].DBState | Should Be 'Normal';
                $Result[0].CurrentOwner | Should Be 'WrongOWner';
                $Result[1].CurrentOwner | Should Be 'WrongOWner';
                $Result[0].TargetOwner | Should Be 'sa';
                $Result[1].TargetOwner | Should Be 'sa';
                $Result[0].OwnerMatch | Should Be $False
                $Result[1].OwnerMatch | Should Be $False
            } # it
            
            It -Skip "Should call Stop-Function one time if Target Login does not exist on Server" {
                Mock Connect-SQLInstance -MockWith {
                    [object]@{
                        DomainInstanceName  = 'SQLServerName';
                        Databases           = [object]@(
                            @{
                                Name    = 'db1';
                                Status  = 'Normal';
                                Owner   = 'WrongOwner'
                            }
                            @{
                                Name    = 'db2';
                                Status  = 'Normal';
                                Owner   = 'WrongOwner'
                            }
                        ); #databases
                        Logins              = [object]@(
                            @{
                                ID    = 1;
                                Name  = 'sa';
                            }
                        ) #logins
                    } #object
                } #mock connect-sqlserver
                Mock Stop-Function { }
                
                $null = Test-DbaDatabaseOwner -SqlInstance 'SQLServerName' -TargetLogin 'WrongLogin'
                $assertMockParams = @{
                    'CommandName'  = 'Stop-Function'
                    'Times'        = 1
                    'Exactly'      = $true
                }
                Assert-MockCalled @assertMockParams
            } # it
            It -Skip "Returns all information with detailed for correct and incorrect owner" {
                Mock Connect-SQLInstance -MockWith {
                    [object]@{
                        DomainInstanceName  = 'SQLServerName';
                        Databases           = [object]@(
                            @{
                                Name    = 'db1';
                                Status  = 'Normal';
                                Owner   = 'WrongOWner'
                            }
                            @{
                                Name    = 'db2';
                                Status  = 'Normal';
                                Owner   = 'sa'
                            }
                        ); #databases
                        Logins              = [object]@(
                            @{
                                ID    = 1;
                                Name  = 'sa';
                            }
                        ) #logins
                    } #object
                } #mock connect-sqlserver
                
                $Result = Test-DbaDatabaseOwner -SqlInstance 'SQLServerName'
                $Result[0].SqlInstance | Should Be 'SQLServerName'
                $Result[1].SqlInstance | Should Be 'SQLServerName'
                $Result[0].Database | Should Be 'db1'
                $Result[1].Database | Should Be 'db2'
                $Result[0].DBState | Should Be 'Normal'
                $Result[1].DBState | Should Be 'Normal'
                $Result[0].CurrentOwner | Should Be 'WrongOWner'
                $Result[1].CurrentOwner | Should Be 'sa'
                $Result[0].TargetOwner | Should Be 'sa'
                $Result[1].TargetOwner | Should Be 'sa'
                $Result[0].OwnerMatch | Should Be $False
                $Result[1].OwnerMatch | Should Be $true
            } # it
        } # Context
    } #modulescope
} #describe
tools\dbatools\tests\Test-DbaDbCompression.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        $paramCount = 5
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Test-DbaDbCompression).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential','Database','ExcludeDatabase','EnableException'
        It "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        Get-DbaProcess -SqlInstance $script:instance2 -Program 'dbatools PowerShell module - dbatools.io' | Stop-DbaProcess -WarningAction SilentlyContinue
        $dbname = "dbatoolsci_test_$(get-random)"
        $server = Connect-DbaInstance -SqlInstance $script:instance2
        $null = $server.Query("Create Database [$dbname]")
        $null = $server.Query("select * into syscols from sys.all_columns
                                select * into sysallparams from sys.all_parameters
                                create clustered index CL_sysallparams on sysallparams (object_id)
                                create nonclustered index NC_syscols on syscols (precision) include (collation_name)
                                update sysallparams set is_xml_document = 1 where name = '@dbname'
                                ",$dbname)
       }
    AfterAll {
        Get-DbaProcess -SqlInstance $script:instance2 -Database $dbname | Stop-DbaProcess -WarningAction SilentlyContinue
        Remove-DbaDatabase -SqlInstance $script:instance2 -Database $dbname -Confirm:$false
    }
    Context "Command gets suggestions" {
        $results = Test-DbaDbCompression -SqlInstance $script:instance2 -Database $dbname
        It "Should get results for $dbaname" {
            $results | Should Not Be $null
        }
        $results.foreach{
            It "Should suggest ROW, PAGE or NO_GAIN for $($PSitem.TableName) - $($PSitem.IndexType) " {
                $PSitem.CompressionTypeRecommendation | Should BeIn ("ROW","PAGE","NO_GAIN")
            }
        }
    }
    Context "Command makes right suggestions" {
        $results = Test-DbaDbCompression -SqlInstance $script:instance2 -Database $dbname
        It "Should sugggest PAGE compression for a table with no updates or scans" {
            $($results | Where-Object { $_.TableName -eq "syscols" -and $_.IndexType -eq "HEAP"}).CompressionTypeRecommendation | Should Be "PAGE"
        }
        It "Should sugggest ROW compression for table with more updates" {
            $($results | Where-Object { $_.TableName -eq "sysallparams"}).CompressionTypeRecommendation | Should Be "ROW"
        }
    }
    Context "Command excludes results for specified database" {
        It "Shouldn't get any results for $dbname" {
            $(Test-DbaDbCompression -SqlInstance $script:instance2 -Database $dbname -ExcludeDatabase $dbname).Database | Should not Match $dbname
        }
    }
}
tools\dbatools\tests\Test-DbaDbVirtualLogFile.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

<#
    Unit test is required for any command added
#>
Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        <#
            The $paramCount is adjusted based on the parameters your command will have.

            The $defaultParamCount is adjusted based on what type of command you are writing the test for:
                - Commands that *do not* include SupportShouldProcess, set defaultParamCount    = 11
                - Commands that *do* include SupportShouldProcess, set defaultParamCount        = 13
        #>
        $paramCount = 6
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Test-DbaDbVirtualLogFile).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'Database', 'ExcludeDatabase', 'IncludeSystemDbs', 'EnableException'
        It "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}
# Get-DbaNoun
Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $server = Connect-DbaInstance -SqlInstance $script:instance2
        $db1 = "dbatoolsci_testvlf"
        $server.Query("CREATE DATABASE $db1")
        $needed = Get-DbaDatabase -SqlInstance $script:instance2 -Database $db1
        $setupright = $true
        if ($needed.Count -ne 1) {
            $setupright = $false
            it "has failed setup" {
                Set-TestInconclusive -message "Setup failed"
            }
        }
    }
    AfterAll {
        Remove-DbaDatabase -Confirm:$false -SqlInstance $script:instance2 -Database $db1
    }

    Context "Command actually works" {
        $results = Test-DbaDbVirtualLogFile -SqlInstance $script:instance2 -Database $db1

        It "Should have correct properties" {
            $ExpectedProps = 'ComputerName,InstanceName,SqlInstance,Database,Total,TotalCount,Inactive,Active,LogFileName,LogFileGrowth,LogFileGrowthType'.Split(',')
            ($results.PsObject.Properties.Name | Sort-Object) | Should Be ($ExpectedProps | Sort-Object)
        }

        It "Should have database name of $db1" {
            foreach ($result in $results) {
                $result.Database | Should Be $db1
            }
        }

        It "Should have values for Total property" {
            foreach ($result in $results) {
                $result.Total | Should Not BeNullOrEmpty
                $result.Total | Should BeGreaterThan 0
            }
        }
    }
}
tools\dbatools\tests\Test-DbaDiskAlignment.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        <#
            The $paramCount is adjusted based on the parameters your command will have.

            The $defaultParamCount is adjusted based on what type of command you are writing the test for:
                - Commands that *do not* include SupportShouldProcess, set defaultParamCount    = 11
                - Commands that *do* include SupportShouldProcess, set defaultParamCount        = 13
        #>
        $paramCount = 6
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Test-DbaDiskAlignment).Parameters.Keys
        $knownParameters = 'ComputerName', 'SqlCredential', 'Credential', 'NoSqlCheck', 'EnableException', 'Detailed'
        It "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}
tools\dbatools\tests\Test-DbaDiskSpeed.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    Context "Command actually works" {
        It "should have info for model" {
           $results = Test-DbaDiskSpeed -SqlInstance $script:instance1
           $results.FileName -contains 'modellog.ldf'
        }
        It "returns only for master" {
            $results = Test-DbaDiskSpeed -SqlInstance $script:instance1 -Database master
            $results.Count -eq 2
            foreach ($result in $results) {
                $result.Reads -gt 0
            }
        }
    }
}
tools\dbatools\tests\Test-DbaIdentityUsage.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Verify Test Identity Usage on TinyInt" {
        BeforeAll {
            $table = "TestTable_$(Get-random)"
            $tableDDL = "CREATE TABLE $table (testId TINYINT IDENTITY(1,1),testData DATETIME2 DEFAULT getdate() )"
            Invoke-Sqlcmd2 -ServerInstance $script:instance1 -Query $tableDDL -database TempDb

        }
        AfterAll {
            $cleanup = "Drop table $table"
            Invoke-Sqlcmd2 -ServerInstance $script:instance1 -Query $cleanup -database TempDb
        }

        $insertSql = "INSERT INTO $table (testData) DEFAULT VALUES"
        for ($i = 1; $i -le 128; $i++) {
            Invoke-Sqlcmd2 -ServerInstance $script:instance1 -Query $insertSql -database TempDb
        }
        $results = Test-DbaIdentityUsage -SqlInstance $script:instance1 -Database TempDb | Where-Object {$_.Table -eq $table}

        It "Identity column should have 128 uses" {
            $results.NumberOfUses | Should Be 128
        }
        It "TinyInt identity column with 128 rows inserted should be 50.20% full" {
            $results.PercentUsed | Should Be 50.20
        }

        $insertSql = "INSERT INTO $table (testData) DEFAULT VALUES"
        for ($i = 1; $i -le 127; $i++) {
            Invoke-Sqlcmd2 -ServerInstance $script:instance1 -Query $insertSql -database TempDb
        }
        $results = Test-DbaIdentityUsage -SqlInstance $script:instance1 -Database TempDb | Where-Object {$_.Table -eq $table}

        It "Identity column should have 255 uses" {
            $results.NumberOfUses | Should Be 255
        }
        It "TinyInt with 255 rows should be 100% full" {
            $results.PercentUsed | Should Be 100
        }

    }

    Context "Verify Test Identity Usage with increment of 5" {
        BeforeAll {
            $table = "TestTable_$(Get-random)"
            $tableDDL = "CREATE TABLE $table (testId tinyint IDENTITY(0,5),testData DATETIME2 DEFAULT getdate() )"
            Invoke-Sqlcmd2 -ServerInstance $script:instance1 -Query $tableDDL -database TempDb

        }
        AfterAll {
            $cleanup = "Drop table $table"
            Invoke-Sqlcmd2 -ServerInstance $script:instance1 -Query $cleanup -database TempDb
        }

        $insertSql = "INSERT INTO $table (testData) DEFAULT VALUES"
        for ($i = 1; $i -le 25; $i++) {
            Invoke-Sqlcmd2 -ServerInstance $script:instance1 -Query $insertSql -database TempDb
        }
        $results = Test-DbaIdentityUsage -SqlInstance $script:instance1 -Database TempDb | Where-Object {$_.Table -eq $table}

        It "Identity column should have 24 uses" {
            $results.NumberOfUses | Should Be 24
        }
        It "TinyInt identity column with 25 rows using increment of 5 should be 47.06% full" {
            $results.PercentUsed | Should Be 47.06
        }

    }
}
tools\dbatools\tests\Test-DbaJobOwner.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        $paramCount = 7
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Test-DbaJobOwner).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'Job', 'ExcludeJob', 'Login', 'EnableException', 'Detailed'
        It "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $saJob = ("dbatoolsci_sa_{0}" -f $(Get-Random))
        $notSaJob = ("dbatoolsci_nonsa_{0}" -f $(Get-Random))
        $null = New-DbaAgentJob -SqlInstance $script:instance2 -Job $saJob -OwnerLogin 'sa'
        $null = New-DbaAgentJob -SqlInstance $script:instance2 -Job $notSaJob -OwnerLogin 'NT AUTHORITY\SYSTEM'
    }
    AfterAll {
        $null = Remove-DbaAgentJob -SqlInstance $script:instance2 -Job $saJob, $notSaJob
    }

    Context "Command actually works" {
        $results = Test-DbaJobOwner -SqlInstance $script:instance2
        It "Should return $notSaJob"{
            $results | Where-Object {$_.Job -eq $notsajob} | Should Not Be Null
        }
    }

    Context "Command works for specific jobs" {
        $results = Test-DbaJobOwner -SqlInstance $script:instance2 -Job $saJob, $notSaJob
        It "Should find $sajob owner matches default sa"{
            $($results | Where-Object {$_.Job -eq $sajob}).OwnerMatch | Should Be $True
        }
        It "Should find $notSaJob owner doesn't match default sa"{
            $($results | Where-Object {$_.Job -eq $notSaJob}).OwnerMatch | Should Be $False
        }
    }

    Context "Exclusions work" {
        $results = Test-DbaJobOwner -SqlInstance $script:instance2 -ExcludeJob $notSaJob
        It "Should exclude $notsajob job"{
            $results.job | Should Not Match $notSaJob
        }
    }
}
tools\dbatools\tests\Test-DbaLastBackup.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"


Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $dbs = $testlastbackup, "dbatoolsci_lildb", "dbatoolsci_testrestore", "dbatoolsci_singlerestore"
        $null = Get-DbaDatabase -SqlInstance $script:instance2 -Database $dbs | Remove-DbaDatabase -Confirm:$false
        $server = Connect-DbaInstance -SqlInstance $script:instance2
        $random = Get-Random
        $testlastbackup = "dbatoolsci_testlastbackup$random"
        $dbs = $testlastbackup, "dbatoolsci_lildb", "dbatoolsci_testrestore", "dbatoolsci_singlerestore"

        foreach ($db in $dbs) {
            $server.Query("CREATE DATABASE $db")
            $server.Query("ALTER DATABASE $db SET RECOVERY FULL WITH NO_WAIT")
            $server.Query("CREATE TABLE [$db].[dbo].[Example] (id int identity, name nvarchar(max))")
            $server.Query("INSERT INTO [$db].[dbo].[Example] values ('sample')")
        }

    }
    AfterAll {
        # these for sure
        Get-DbaDatabase -SqlInstance $script:instance2 -Database $dbs | Remove-DbaDatabase -Confirm:$false
        # those just in case test-dbalastbackup didn't cooperate
        Get-DbaDatabase -SqlInstance $script:instance2 | Where-Object Name -like 'dbatools-testrestore-dbatoolsci_*' | Remove-DbaDatabase -Confirm:$false
        # see "Restores using a specific path"
        Get-ChildItem -Path C:\Temp\dbatools-testrestore-dbatoolsci_singlerestore* | Remove-Item
    }
    Context "Setup restores and backups on the local drive for Test-DbaLastBackup" {
        Get-DbaDatabase -SqlInstance $script:instance2 -Database $dbs | Backup-DbaDatabase -Type Database
        $server.Query("INSERT INTO [$testlastbackup].[dbo].[Example] values ('sample')")
        Get-DbaDatabase -SqlInstance $script:instance2 -Database $testlastbackup | Backup-DbaDatabase -Type Differential
        $server.Query("INSERT INTO [$testlastbackup].[dbo].[Example] values ('sample1')")
        Get-DbaDatabase -SqlInstance $script:instance2 -Database $testlastbackup | Backup-DbaDatabase -Type Differential
        $server.Query("INSERT INTO [$testlastbackup].[dbo].[Example] values ('sample2')")
        Get-DbaDatabase -SqlInstance $script:instance2 -Database $testlastbackup | Backup-DbaDatabase -Type Log
        $server.Query("INSERT INTO [$testlastbackup].[dbo].[Example] values ('sample3')")
        Get-DbaDatabase -SqlInstance $script:instance2 -Database $testlastbackup | Backup-DbaDatabase -Type Log
        $server.Query("INSERT INTO [$testlastbackup].[dbo].[Example] values ('sample4')")
    }

    Context "Test a single database" {
        $results = Test-DbaLastBackup -SqlInstance $script:instance2 -Database $testlastbackup

        It "Should return success" {
            $results.RestoreResult | Should Be "Success"
            $results.DbccResult | Should Be "Success"
        }
    }

    Context "Testing the whole instance" {
        $results = Test-DbaLastBackup -SqlInstance $script:instance2 -ExcludeDatabase tempdb
        It "Should be more than 3 databases" {
            $results.count | Should BeGreaterThan 3
        }
    }

    Context "Restores using a specific path" {
        $null = Get-DbaDatabase -SqlInstance $script:instance2 -Database "dbatoolsci_singlerestore" | Backup-DbaDatabase
        $null = Test-DbaLastBackup -SqlInstance $script:instance2 -Database "dbatoolsci_singlerestore" -DataDirectory C:\Temp -LogDirectory C:\Temp -NoDrop
        $results = Get-DbaDatabaseFile -SqlInstance $script:instance2 -Database "dbatools-testrestore-dbatoolsci_singlerestore"
        It "Should match C:\Temp" {
            ('C:\Temp\dbatools-testrestore-dbatoolsci_singlerestore.mdf' -in $results.PhysicalName) | Should Be $true
            ('C:\Temp\dbatools-testrestore-dbatoolsci_singlerestore_log.ldf' -in $results.PhysicalName) | Should Be $true
        }
    }
}
tools\dbatools\tests\Test-DbaLinkedServerConnection.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"
Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        Get-DbaProcess -SqlInstance $script:instance2 -Program 'dbatools PowerShell module - dbatools.io' | Stop-DbaProcess -WarningAction SilentlyContinue
        $server = Connect-DbaInstance -SqlInstance $script:instance2 -Database master
        $server.Query("EXEC master.dbo.sp_addlinkedserver @server = N'localhost', @srvproduct=N'SQL Server'")
    }
    AfterAll {
        Get-DbaProcess -SqlInstance $script:instance2 -Program 'dbatools PowerShell module - dbatools.io' | Stop-DbaProcess -WarningAction SilentlyContinue
        $server = Connect-DbaInstance -SqlInstance $script:instance2 -Database master
        $server.Query("EXEC master.dbo.sp_dropserver @server=N'localhost', @droplogins='droplogins'")
    }
    
    $results = Test-DbaLinkedServerConnection -SqlInstance $script:instance2 | Where-Object LinkedServerName -eq 'localhost'
    It "can connect to linked server 'localhost'" {
        $results.LinkedServerName -eq 'localhost'
        $results.Connectivity -eq $true
    }
}
tools\dbatools\tests\Test-DbaLoginPassword.test.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag UnitTests, Get-DbaLogin {
    Context "$Command Name Input" {
        $Params = (Get-Command Get-DbaLogin).Parameters
        It "Should have a mandatory parameter SQLInstance" {
            $Params['SQLInstance'].Attributes.Mandatory | Should be $true
        }
        It "Should have Alias of ServerInstance and SqlServer for Parameter SQLInstance" {
            $params['SQLInstance'].Aliases | Should Be @('ServerInstance', 'SqlServer')
        }
        It "Should have a parameter SqlCredential" {
            $Params['SqlCredential'].Count | Should Be 1
        }
        # took Dictionary out cuz it failed even though it existed
        It "Should have a parameter EnableException" {
            $Params['EnableException'].Count | Should Be 1
        }
    }
}

Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    BeforeAll {
        $server = Connect-DbaInstance -SqlInstance $script:instance1
        $weaksauce = "dbatoolsci_testweak"
        $weakpass = ConvertTo-SecureString $weaksauce -AsPlainText -Force
        $newlogin = New-DbaLogin -SqlInstance $script:instance1 -Login $weaksauce -HashedPassword (Get-PasswordHash $weakpass $server.VersionMajor) -Force
    }
    AfterAll {
        try {
            $newlogin.Drop()
        }
        catch {
            # dont care
        }
    }

    Context "making sure command works" {
        It "finds the new weak password and supports piping" {
            $results = Get-DbaLogin -SqlInstance $script:instance1 | Test-DbaLoginPassword
            $results.SqlLogin | Should -Contain $weaksauce
        }
        It "returns just one login" {
            $results = Test-DbaLoginPassword -SqlInstance $script:instance1 -Login $weaksauce
            $results.SqlLogin | Should -Be $weaksauce
        }
    }
}
tools\dbatools\tests\Test-DbaLogShippingStatus.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    It "warns if SQL instance edition is not supported" {
        $results = Test-DbaLogShippingStatus -SqlInstance $script:instance1 -WarningAction SilentlyContinue -WarningVariable editionwarn
        $editionwarn -match "Express" | Should Be $true
    }

    It "warns if no log shipping found" {
        $results = Test-DbaLogShippingStatus -SqlInstance $script:instance2 -Database 'master' -WarningAction SilentlyContinue -WarningVariable doesntexist
        $doesntexist -match "No information available" | Should Be $true
    }
}
tools\dbatools\tests\Test-DbaLsnChain.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Unit Tests" -Tag 'UnitTests' {
    InModuleScope dbatools {
        Context "General Diff restore" {
            $Header = ConvertFrom-Json -InputObject (Get-Content $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\DiffRestore.json -raw)
            $header | Add-Member -Type NoteProperty -Name FullName -Value 1

            $filteredFiles = $header | Select-DbaBackupInformation
            It "Should Return 7" {
                $FilteredFiles.count | should be 7
            }
            It "Should return True" {
                $Output = Test-DbaLsnChain -FilteredRestoreFiles $FilteredFiles -WarningAction SilentlyContinue
                $Output | Should be True
            }
            $Header = ConvertFrom-Json -InputObject (Get-Content $PSScriptRoot\..\tests\ObjectDefinitions\BackupRestore\RawInput\DiffRestore.json -raw)
            $header = $Header | Where-Object { $_.BackupTypeDescription -ne 'Database Differential' }
            $header | Add-Member -Type NoteProperty -Name FullName -Value 1

            $FilteredFiles = $Header | Select-DbaBackupInformation
            It "Should return true if we remove diff backup" {
                $Output = Test-DbaLsnChain -WarningAction SilentlyContinue -FilteredRestoreFiles ($FilteredFiles | Where-Object { $_.BackupTypeDescription -ne 'Database Differential' })
                $Output | Should be True
            }

            It "Should return False (faked lsn)" {
                $FilteredFiles[4].FirstLsn = 2
                $FilteredFiles[4].LastLsn = 1
                $Output = Test-DbaLsnChain -WarningAction SilentlyContinue -FilteredRestoreFiles $FilteredFiles
                $Output | Should be $False
            }
        }
    }
}
tools\dbatools\tests\Test-DbaMaxDop.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

# Removed unit tests that were integration tests
Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        Get-DbaProcess -SqlInstance $script:instance2 -Program 'dbatools PowerShell module - dbatools.io' | Stop-DbaProcess -WarningAction SilentlyContinue
        $server = Connect-DbaInstance -SqlInstance $script:instance2
        $db1 = "dbatoolsci_testMaxDop"
        $server.Query("CREATE DATABASE dbatoolsci_testMaxDop")
        $needed = Get-DbaDatabase -SqlInstance $script:instance2 -Database dbatoolsci_testMaxDop
        $setupright = $true
        if ($needed.Count -ne 1) {
            $setupright = $false
            it "has failed setup" {
                Set-TestInconclusive -message "Setup failed"
            }
        }
    }
    AfterAll {
        if (-not $appveyor) {
            Remove-DbaDatabase -Confirm:$false -SqlInstance $script:instance2 -Database dbatoolsci_testMaxDop
        }
    }

    Context "Command works on SQL Server 2016 or higher instances" {
        $results = Test-DbaMaxDop -SqlInstance $script:instance2

        It "Should have correct properties" {
            $ExpectedProps = 'ComputerName,InstanceName,SqlInstance,Database,DatabaseMaxDop,CurrentInstanceMaxDop,RecommendedMaxDop,Notes'.Split(',')
            foreach ($result in $results) {
                ($result.PSStandardMembers.DefaultDIsplayPropertySet.ReferencedPropertyNames | Sort-Object) | Should Be ($ExpectedProps | Sort-Object)
            }
        }

        It "Should have only one result for database name of dbatoolsci_testMaxDop" {
            @($results | Where-Object Database -eq dbatoolsci_testMaxDop).Count | Should Be 1
        }
    }
}
tools\dbatools\tests\Test-DbaMaxMemory.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Connects to multiple instances" {
        It 'Returns multiple objects' {
            $results = Test-DbaMaxMemory -SqlInstance $script:instance1, $script:instance2
            $results.Count | Should BeGreaterThan 1 # and ultimately not throw an exception
        }
    }
}

Describe "$commandname Unit Tests" -Tag 'UnitTests' {
    InModuleScope dbatools {
        Context 'Validate input arguments' {
            It 'No "SQL Server" Windows service is running on the host' {
                { Test-DbaMaxMemory -SqlInstance 'ABC' -EnableException } | Should Throw
            }

            It 'SqlInstance parameter is empty throws an exception' {
                Mock Get-DbaMaxMemory -MockWith { return $null }
                { Test-DbaMaxMemory -SqlInstance '' } | Should Throw
            }

            It 'SqlInstance parameter host cannot be found' {
                Mock Get-DbaMaxMemory -MockWith { return $null }
                Test-DbaMaxMemory -SqlInstance 'ABC' 3> $null | Should be $null
            }

        }

        Context 'Validate functionality - Single Instance' {
            Mock Connect-SqlInstance -MockWith {
                "nothing"
            }

            Mock Get-DbaMaxMemory -MockWith {
                New-Object PSObject -Property @{
                    ComputerName = "SQL2016"
                    InstanceName = "MSSQLSERVER"
                    SqlInstance  = "SQL2016"
                    TotalMB      = 4096
                    SqlMaxMB     = 2147483647
                }
            }

            Mock Get-DbaSqlService -MockWith {
                New-Object PSObject -Property @{
                    InstanceName = "foo"
                    State        = "Running"
                }
            }

            It 'Connect to SQL Server' {
                Mock Get-DbaMaxMemory -MockWith { }

                $result = Test-DbaMaxMemory -SqlInstance 'ABC'

                Assert-MockCalled Connect-SqlInstance -Scope It -Times 1
                Assert-MockCalled Get-DbaSqlService -Scope It -Times 1
                Assert-MockCalled Get-DbaMaxMemory -Scope It -Times 1
            }

            It 'Connect to SQL Server and retrieve the "Max Server Memory" setting' {
                Mock Get-DbaMaxMemory -MockWith {
                    return @{ SqlMaxMB = 2147483647 }
                }

                (Test-DbaMaxMemory -SqlInstance 'ABC').SqlMaxMB | Should be 2147483647
            }

            It 'Calculate recommended memory - Single instance, Total 4GB, Expected 2GB, Reserved 2GB (.5x Memory)' {
                Mock Get-DbaMaxMemory -MockWith {
                    return @{ TotalMB = 4096 }
                }

                $result = Test-DbaMaxMemory -SqlInstance 'ABC'
                $result.InstanceCount | Should Be 1
                $result.RecommendedMB | Should Be 2048
            }

            It 'Calculate recommended memory - Single instance, Total 6GB, Expected 3GB, Reserved 3GB (Iterations => 2x 8GB)' {
                Mock Get-DbaMaxMemory -MockWith {
                    return @{ TotalMB = 6144 }
                }

                $result = Test-DbaMaxMemory -SqlInstance 'ABC'
                $result.InstanceCount | Should Be 1
                $result.RecommendedMB | Should Be 3072
            }

            It 'Calculate recommended memory - Single instance, Total 8GB, Expected 5GB, Reserved 3GB (Iterations => 2x 8GB)' {
                Mock Get-DbaMaxMemory -MockWith {
                    return @{ TotalMB = 8192 }
                }

                $result = Test-DbaMaxMemory -SqlInstance 'ABC'
                $result.InstanceCount | Should Be 1
                $result.RecommendedMB | Should Be 5120
            }

            It 'Calculate recommended memory - Single instance, Total 16GB, Expected 11GB, Reserved 5GB (Iterations => 4x 8GB)' {
                Mock Get-DbaMaxMemory -MockWith {
                    return @{ TotalMB = 16384 }
                }

                $result = Test-DbaMaxMemory -SqlInstance 'ABC'
                $result.InstanceCount | Should Be 1
                $result.RecommendedMB | Should Be 11264
            }

            It 'Calculate recommended memory - Single instance, Total 18GB, Expected 13GB, Reserved 5GB (Iterations => 1x 16GB, 3x 8GB)' {
                Mock Get-DbaMaxMemory -MockWith {
                    return @{ TotalMB = 18432 }
                }

                $result = Test-DbaMaxMemory -SqlInstance 'ABC'
                $result.InstanceCount | Should Be 1
                $result.RecommendedMB | Should Be 13312
            }

            It 'Calculate recommended memory - Single instance, Total 32GB, Expected 25GB, Reserved 7GB (Iterations => 2x 16GB, 4x 8GB)' {
                Mock Get-DbaMaxMemory -MockWith {
                    return @{ TotalMB = 32768 }
                }

                $result = Test-DbaMaxMemory -SqlInstance 'ABC'
                $result.InstanceCount | Should Be 1
                $result.RecommendedMB | Should Be 25600
            }
        }
    }
}
tools\dbatools\tests\Test-DbaMigrationConstraint.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Integration Tests" -Tag 'IntegrationTests' {
    BeforeAll {
        Get-DbaProcess -SqlInstance $script:instance1 -Program 'dbatools PowerShell module - dbatools.io' | Stop-DbaProcess -WarningAction SilentlyContinue
        $db1 = "dbatoolsci_testMigrationConstraint"
        $db2 = "dbatoolsci_testMigrationConstraint_2"
        Invoke-DbaSqlQuery -SqlInstance $script:instance1 -Query "CREATE DATABASE $db1"
        Invoke-DbaSqlQuery -SqlInstance $script:instance1 -Query "CREATE DATABASE $db2"
        $needed = Get-DbaDatabase -SqlInstance $script:instance1 -Database $db1, $db2
        $setupright = $true
        if ($needed.Count -ne 2) {
            $setupright = $false
            it "has failed setup" {
                Set-TestInconclusive -message "Setup failed"
            }
        }
    }
    AfterAll {
        if (-not $appveyor) {
            Remove-DbaDatabase -Confirm:$false -SqlInstance $script:instance1 -Database $db1, $db2 -ErrorAction SilentlyContinue
        }
    }
    Context "Validate multiple databases" {
        It 'Both databases are migratable' {
            $results = Test-DbaMigrationConstraint -Source $script:instance1 -Destination $script:instance2
            foreach ($result in $results) {
                $result.IsMigratable | Should Be $true
            }
        }
    }
    Context "Validate single database" {
        It 'Databases are migratable' {
            (Test-DbaMigrationConstraint -Source $script:instance1 -Destination $script:instance2 -Database $db1).IsMigratable | Should Be $true
        }
    }
}
tools\dbatools\tests\Test-DbaNetworkLatency.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Command returns proper info" {
        $results = $instances | Test-DbaNetworkLatency

        It "returns two objects" {
            $results.Count | Should Be 2
        }

        $results = Test-DbaNetworkLatency -SqlInstance $instances

        It "executes 3 times by default" {
            $results.ExecutionCount | Should Be 3, 3
        }

        It "has the correct properties" {
            $result = $results | Select-Object -First 1
            $ExpectedPropsDefault = 'ComputerName,InstanceName,SqlInstance,ExecutionCount,Total,Average,ExecuteOnlyTotal,ExecuteOnlyAverage,NetworkOnlyTotal'.Split(',')
            ($result.PSStandardMembers.DefaultDisplayPropertySet.ReferencedPropertyNames | Sort-Object) | Should Be ($ExpectedPropsDefault | Sort-Object)
        }
    }
}
tools\dbatools\tests\Test-DbaOptimizeForAdHoc.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        $paramCount = 3
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Test-DbaOptimizeForAdHoc).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'EnableException'
        It "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    Context "Command actually works" {
        $results = Test-DbaOptimizeForAdHoc -SqlInstance $script:instance2
        It "Should return result for the server" {
            $results | Should Not Be Null
        }
        It "Should return 'CurrentOptimizeAdHoc' property as int" {
            $results.CurrentOptimizeAdHoc | Should BeOfType System.Int32
        }
        It "Should return 'RecommendedOptimizeAdHoc' property as int" {
            $results.RecommendedOptimizeAdHoc  | Should BeOfType System.Int32
        }
    }
}
tools\dbatools\tests\Test-DbaPowerPlan.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        $paramCount = 5
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Test-DbaPowerPlan).Parameters.Keys
        $knownParameters = 'ComputerName', 'Credential', 'CustomPowerPlan', 'Detailed', 'EnableException'
        It "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    Context "Command actually works" {
        It "Should return result for the server" {
            $results = Test-DbaPowerPlan -ComputerName $script:instance2
            $results | Should Not Be Null
        }
        It "Should state 'Balanced' plan does not meet best practice" {
            $results = Test-DbaPowerPlan -ComputerName $script:instance2 -CustomPowerPlan 'Balanced'
            $results.isBestPractice | Should Be $false
        }
    }
}
tools\dbatools\tests\Test-DbaRecoveryModel.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        <#
            The $paramCount is adjusted based on the parameters your command will have.

            The $defaultParamCount is adjusted based on what type of command you are writing the test for:
                - Commands that *do not* include SupportShouldProcess, set defaultParamCount    = 11
                - Commands that *do* include SupportShouldProcess, set defaultParamCount        = 13
        #>
        $paramCount = 7
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Test-DbaRecoveryModel).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential','RecoveryModel', 'Database', 'ExcludeDatabase', 'EnableException', 'Detailed'
        It "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}
Describe "$CommandName Intigration Tests" -Tag  "IntegrationTests" {
    BeforeAll {
        $fullRecovery = "dbatoolsci_RecoveryModelFull"
        $bulkLoggedRecovery = "dbatoolsci_RecoveryModelBulk"
        $simpleRecovery = "dbatoolsci_RecoveryModelSimple"
        $psudoSimpleRecovery = "dbatoolsci_RecoveryModelPsudoSimple"
        $server = Connect-DbaInstance -SqlInstance $script:instance2

        Stop-DbaProcess -SqlInstance $script:instance2 -Database model
        $server.Query("CREATE DATABASE $fullRecovery")
        Stop-DbaProcess -SqlInstance $script:instance2 -Database model
        $server.Query("CREATE DATABASE $bulkLoggedRecovery")
        Stop-DbaProcess -SqlInstance $script:instance2 -Database model
        $server.Query("CREATE DATABASE $simpleRecovery")
        Stop-DbaProcess -SqlInstance $script:instance2 -Database model
        $server.Query("CREATE DATABASE $psudoSimpleRecovery")

        Set-DbaDbRecoveryModel -sqlInstance $script:instance2 -RecoveryModel BulkLogged -Database $bulkLoggedRecovery -Confirm:$false
        Set-DbaDbRecoveryModel -SqlInstance $script:instance2 -RecoveryModel Simple -Database $simpleRecovery -Confirm:$false
        Set-DbaDbRecoveryModel -SqlInstance $script:instance2 -RecoveryModel Simple -Database $psudoSimpleRecovery -Confirm:$false
        Set-DbaDbRecoveryModel -SqlInstance $script:instance2 -RecoveryModel Full -Database $psudoSimpleRecovery -Confirm:$false

    }
    AfterAll {
        Remove-DbaDatabase -Confirm:$false -SqlInstance $script:instance2 -Database $fullRecovery, $bulkLoggedRecovery, $simpleRecovery, $psudoSimpleRecovery
    }

    Context "Default Execution" {
        $results = Test-DbaRecoveryModel -SqlInstance $script:instance2 -Database $fullRecovery,$psudoSimpleRecovery,'Model'

       It "Should return $fullRecovery, $psudoSimpleRecovery, and Model" {
            $results.Database | should -BeIn ($fullRecovery,$psudoSimpleRecovery,'Model')
       }

    }

    Context "Full Recovery" {
        $results = Test-DbaRecoveryModel -SqlInstance $script:instance2 -RecoveryModel Full -Database $fullRecovery,$psudoSimpleRecovery -ExcludeDatabase 'Model'

        It "Should return $fullRecovery and $psudoSimpleRecovery" {
            $results.Database | should -BeIn ($fullRecovery,$psudoSimpleRecovery)
       }
    }

    Context "Bulk Logged Recovery" {
        $results =  Test-DbaRecoveryModel -SqlInstance $script:instance2 -RecoveryModel Bulk_Logged -Database $bulkLoggedRecovery

        It "Should return $bulkLoggedRecovery" {
            $results.Database | should -Be "$bulkLoggedRecovery"
        }

    }

    Context "Simple Recovery" {
        $results =  Test-DbaRecoveryModel -SqlInstance $script:instance2 -RecoveryModel Simple -Database $simpleRecovery

        It "Should return $simpleRecovery" {
            $results.Database | should -Be "$simpleRecovery"
        }

    }

    Context "Psudo Simple Recovery" {
        $results =  Test-DbaRecoveryModel -SqlInstance $script:instance2 -RecoveryModel Full | Where {$_.database -eq "$psudoSimpleRecovery"}

        It "Should return $psudoSimpleRecovery" {
            $results.Database | should -Be "$psudoSimpleRecovery"
        }

    }

    Context "Error Check" {

        It "Should Throw Error for Incorrect Recovery Model" {
            {Test-DbaRecoveryModel -SqlInstance $script:instance2 -RecoveryModel Awesome -EnableException -Database 'dontexist' } | should -Throw
        }

        Mock Connect-SqlInstance { Throw } -ModuleName dbatools
        It "Should Thow Error for a DB Connection Error" {
            {Test-DbaRecoveryModel -SqlInstance $script:instance2 -EnableException | Should -Throw }
        }

        Mock Select-DefaultView { Throw } -ModuleName dbatools
        It "Should Thow Error for Output Error " {
            {Test-DbaRecoveryModel -SqlInstance $script:instance2 -EnableException | Should -Throw }
        }

    }


}
tools\dbatools\tests\Test-DbaServerName.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        $paramCount = 5
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Test-DbaServerName).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'Detailed', 'ExcludeSsrs', 'EnableException'
        it "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        it "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "Command tests servername" {
        $results = Test-DbaServerName -SqlInstance $script:instance2
        It "should say rename is not required" {
            $results.RenameRequired | Should -Be $false
        }
        
        It "returns the correct properties" {
            $ExpectedProps = 'ComputerName,ServerName,RenameRequired,Updatable,Warnings,Blockers,SqlInstance,InstanceName'.Split(',')
            ($results.PsObject.Properties.Name | Sort-Object) | Should -Be ($ExpectedProps | Sort-Object)
        }
    }
}
tools\dbatools\tests\Test-DbaSpn.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
    Context "gets spn information" {
        Mock Resolve-DbaNetworkName {
            [pscustomobject]@{
                InputName         = $env:COMPUTERNAME
                ComputerName      = $env:COMPUTERNAME
                IPAddress         = "127.0.0.1"
                DNSHostName       = $env:COMPUTERNAME
                DNSDomain         = $env:COMPUTERNAME
                Domain            = $env:COMPUTERNAME
                DNSHostEntry      = $env:COMPUTERNAME
                FQDN              = $env:COMPUTERNAME
                FullComputerName  = $env:COMPUTERNAME
            }
        }
        $results = Test-DbaSpn -ComputerName $env:COMPUTERNAME -WarningAction SilentlyContinue
        It "returns some results" {
            $null -ne $results.RequiredSPN | Should -Be $true
        }
        foreach ($result in $results) {
            It "has the right properties" {
                $result.RequiredSPN -match 'MSSQLSvc' | Should -Be $true
                $result.Cluster -eq $false | Should -Be $true
                $result.TcpEnabled | Should -Be $true
                $result.IsSet -is [bool] | Should -Be $true
            }
        }
    }
}
tools\dbatools\tests\Test-DbaSqlManagementObject.Tests.ps1
<#
    The below statement stays in for every test you build.
#>
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

<#
    Unit test is required for any command added
#>
Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        <#
            The $paramCount is adjusted based on the parameters your command will have.

            The $defaultParamCount is adjusted based on what type of command you are writing the test for:
                - Commands that *do not* include SupportShouldProcess, set defaultParamCount    = 11
                - Commands that *do* include SupportShouldProcess, set defaultParamCount        = 13
        #>
        $paramCount = 4
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem Function:\Test-DbaSqlManagementObject).Parameters.Keys
        $knownParameters = 'ComputerName', 'Credential', 'VersionNumber', 'EnableException'
        it "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        it "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}
Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $versionMajor = (Connect-DbaInstance -SqlInstance $script:instance2).VersionMajor
    }
    Context "Command actually works" {
        $trueResults = Test-DbaSqlManagementObject -ComputerName $script:instance2 -VersionNumber $versionMajor
        It "Should have correct properties" {
            $ExpectedProps = 'ComputerName,Version,Exists'.Split(',')
            ($trueResults[0].PsObject.Properties.Name | Sort-Object) | Should Be ($ExpectedProps | Sort-Object)
        }

        It "Should return true for VersionNumber $versionMajor" {
            $trueResults.Exists | Should Be $true
        }

        $falseResults = Test-DbaSqlManagementObject -ComputerName $script:instance2 -VersionNumber -1
        It "Should return false for VersionNumber -1" {
            $falseResults.Exists | Should Be $false
        }
    }
}
tools\dbatools\tests\Test-DbaSqlPath.Tests.ps1
<#
    The below statement stays in for every test you build.
#>
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

<#
    Unit test is required for any command added
#>
Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        <#
            The $paramCount is adjusted based on the parameters your command will have.

            The $defaultParamCount is adjusted based on what type of command you are writing the test for:
                - Commands that *do not* include SupportShouldProcess, set defaultParamCount    = 11
                - Commands that *do* include SupportShouldProcess, set defaultParamCount        = 13
        #>
        $paramCount = 4
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Test-DbaSqlPath).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'Path', 'EnableException'
        It "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}

Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    BeforeAll {
        $trueTest = (Get-DbaDatabaseFile -SqlInstance $script:instance2 -Database master)[0].PhysicalName
        if ($trueTest.Length -eq 0) {
            It "has failed setup" {
                Set-TestInconclusive -message "Setup failed"
            }
        }
        $falseTest = 'B:\FloppyDiskAreAwesome'
        $trueTestPath = [System.IO.Path]::GetDirectoryName($trueTest)
    }
    Context "Command actually works" {
        $result = Test-DbaSqlPath -SqlInstance $script:instance2 -Path $trueTest
        It "Should only return true if the path IS accessible to the instance" {
            $result | Should Be $true
        }

        $result = Test-DbaSqlPath -SqlInstance $script:instance2 -Path $falseTest
        It "Should only return false if the path IS NOT accessible to the instance" {
            $result | Should Be $false
        }
        $results = Test-DbaSqlPath -SqlInstance $script:instance2 -Path $trueTest, $falseTest
        It "Should return multiple results when passed multiple paths" {
            ($results | Where-Object FilePath -eq $trueTest).FileExists | Should Be $true
            ($results | Where-Object FilePath -eq $falseTest).FileExists | Should Be $false
        }
        $results = Test-DbaSqlPath -SqlInstance $script:instance2,$script:instance1 -Path $falseTest
        It "Should return multiple results when passed multiple instances" {
            foreach($result in $results) {
                $result.FileExists | Should Be $false
            }
            ($results.SqlInstance | Sort-Object -Unique).Count | Should Be 2
        }
        $results = Test-DbaSqlPath -SqlInstance $script:instance2 -Path @($trueTest)
        It "Should return pscustomobject results when passed an array (even with one path)" {
            ($results | Where-Object FilePath -eq $trueTest).FileExists | Should Be $true
        }
        $results = Test-DbaSqlPath -SqlInstance $script:instance2 -Path @($trueTest, $trueTestPath)
        It "Should return pscustomobject results indicating if the path is a file or a directory" {
            ($results | Where-Object FilePath -eq $trueTest).FileExists | Should Be $true
            ($results | Where-Object FilePath -eq $trueTestPath).FileExists | Should Be $true
            ($results | Where-Object FilePath -eq $trueTest).IsContainer | Should Be $false
            ($results | Where-Object FilePath -eq $trueTestPath).IsContainer | Should Be $true
        }
    }
}
tools\dbatools\tests\Test-DbaTempDbConfiguration.Tests.ps1
<#
    The below statement stays in for every test you build.
#>
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

<#
    Unit test is required for any command added
#>
Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        <#
            The $paramCount is adjusted based on the parameters your command will have.

            The $defaultParamCount is adjusted based on what type of command you are writing the test for:
                - Commands that *do not* include SupportShouldProcess, set defaultParamCount    = 11
                - Commands that *do* include SupportShouldProcess, set defaultParamCount        = 13
        #>
        $paramCount = 4
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Test-DbaTempDbConfiguration).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'EnableException', 'Detailed'
        It "Should contain our specific parameters" {
            ((Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}

Describe "$CommandName Integration Tests" -Tag "IntegrationTests" {
    Context "Command actually works on $script:instance2" {
        $results = Test-DbaTempDbConfiguration -SqlInstance $script:instance2
        It "Should have correct properties" {
            $ExpectedProps = 'ComputerName,InstanceName,SqlInstance,Rule,Recommended,CurrentSetting,IsBestPractice,Notes'.Split(',')
            ($results[0].PsObject.Properties.Name | Sort-Object) | Should Be ($ExpectedProps | Sort-Object)
        }
        
        $rule = 'File Location'
        It "Should return false for IsBestPractice with rule: $rule" {
            ($results | Where-Object Rule -match $rule).IsBestPractice | Should Be $false
        }
        It "Should return false for Recommended with rule: $rule" {
            ($results | Where-Object Rule -match $rule).Recommended | Should Be $false
        }
        $rule = 'TF 1118 Enabled'
        It "Should return true for IsBestPractice with rule: $rule" {
            ($results | Where-Object Rule -match $rule).Recommended | Should Be $true
        }
    }
}
tools\dbatools\tests\Test-DbaWindowsLogin.Tests.ps1
<#
    The below statement stays in for every test you build.
#>
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

<#
    Unit test is required for any command added
#>
Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        <#
            The $paramCount is adjusted based on the parameters your command will have.

            The $defaultParamCount is adjusted based on what type of command you are writing the test for:
                - Commands that *do not* include SupportShouldProcess, set defaultParamCount    = 11
                - Commands that *do* include SupportShouldProcess, set defaultParamCount        = 13
        #>
        $paramCount = 8
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Test-DbaWindowsLogin).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'Login', 'ExcludeLogin', 'FilterBy', 'IgnoreDomains', 'EnableException', 'Detailed'
        It "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        It "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}
<#
Did not include these tests yet as I was unsure if AppVeyor was capable of testing domain logins. Included these for future use.
Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    Context "Command actually works" {
        $results = Test-DbaWindowsLogin -SqlInstance $script:instance2
        It "Should return correct properties" {
            $ExpectedProps = 'AccountNotDelegated,AllowReversiblePasswordEncryption,CannotChangePassword,DisabledInSQLServer,Domain,Enabled,Found,LockedOut,Login,PasswordExpired,PasswordNeverExpires,PasswordNotRequired,Server,SmartcardLogonRequired,TrustedForDelegation,Type,UserAccountControl'.Split(',')
            ($results[0].PsObject.Properties.Name | Sort-Object) | Should Be ($ExpectedProps | Sort-Object)
        }

        $Type = 'User'
        It "Should return true if Account type is: $Type" {
            ($results | Where-Object Type -match $Type) | Should Be $true
        }
        It "Should return true if Account is Found" {
            ($results | Where-Object Found).Found | Should Be $true
        }
    }
}#>
tools\dbatools\tests\Test-PSRemoting.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag "UnitTests" {
    Context "Validate parameters" {
        $paramCount = 3
        <#
            Get commands, Default count = 11
            Commands with SupportShouldProcess = 13
        #>
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Test-PSRemoting).Parameters.Keys
        $knownParameters = 'ComputerName', 'Credential', 'EnableException'
        It "Contains our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        It "Contains $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {
    Context "returns a boolean with no exceptions" {
        $result = Test-PSRemoting -ComputerName "funny"
        It "returns $false when failing" {
            $result | Should Be $false
        }
        $result = Test-PSRemoting -ComputerName localhost
        It "returns $true when succeeding" {
            $result | Should Be $true
        }
    }
    Context "handles an instance, using just the computername" {
        $result = Test-PSRemoting -ComputerName $script:instance1
        It "returns $true when succeeding" {
            $result | Should Be $true
        }
    }
}
tools\dbatools\tests\Update-DbaSqlServiceAccount.Tests.ps1
$commandname = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"
. "$PSScriptRoot\..\internal\functions\Connect-SqlInstance.ps1"

Describe "$commandname Integration Tests" -Tags "IntegrationTests" {

    $login = 'winLogin'
    $password = 'MyV3ry$ecur3P@ssw0rd'
    $securePassword = ConvertTo-SecureString $password -AsPlainText -Force
    $newPassword = 'Myxtr33mly$ecur3P@ssw0rd'
    $newSecurePassword = ConvertTo-SecureString $newPassword -AsPlainText -Force
    $server = Connect-SqlInstance -SqlInstance $script:instance2
    $computerName = $server.NetName
    $instanceName = $server.ServiceName
    $winLogin = "$computerName\$login"

    #cleanup
    $computer = [ADSI]"WinNT://$computerName"
    try {
        $user = [ADSI]"WinNT://$computerName/$login,user"
        if ($user.Name -eq $login) {
            $computer.Delete('User', $login)
        }
    }
    catch {<#User does not exist#>}

    if ($l = Get-DbaLogin -SqlInstance $script:instance2 -Login $winLogin) {
        $results = $server.Query("IF EXISTS (SELECT * FROM sys.server_principals WHERE name = '$winLogin') EXEC sp_who '$winLogin'")
        foreach ($spid in $results.spid) {
            $null = $server.Query("kill $spid")
        }
        if ($c = $l.EnumCredentials()) {
            $l.DropCredential($c)
        }
        $l.Drop()
    }

    #create Windows login
    $user = $computer.Create("user", $login)
    $user.SetPassword($password)
    $user.SetInfo()

    #Get current service users
    $services = Get-DbaSqlservice -ComputerName $script:instance2 -Type Engine, Agent -Instance $instanceName
    $currentAgentUser = ($services | Where-Object { $_.ServiceType -eq 'Agent' }).StartName
    $currentEngineUser = ($services | Where-Object { $_.ServiceType -eq 'Engine' }).StartName

    #Create a new sysadmin login on SQL Server
    $newLogin = New-Object Microsoft.SqlServer.Management.Smo.Login($server, $winLogin)
    $newLogin.LoginType = "WindowsUser"
    $newLogin.Create()
    $server.Roles['sysadmin'].AddMember($winLogin)

    $isRevertable = $true
    ForEach ($svcaccount in $currentAgentUser, $currentEngineUser) {
        if (! ($svcaccount.EndsWith('$') -or $svcaccount.StartsWith('NT AUTHORITY\') -or $svcaccount.StartsWith('NT Service\'))) {
            $isRevertable = $false
        }
    }

    Context "Current configuration to be able to roll back" {
        It "Both agent and engine services must exist" {
            ($services | Measure-Object).Count | Should Be 2
        }
        It "Current service accounts should be localsystem-like or MSA to allow for a rollback" {
            $isRevertable | Should be $true
        }
    }

    #Do not continue with the test if current configuration cannot be rolled back
    if (!$isRevertable) {
        Throw 'Current configuration cannot be rolled back - the test will not continue.'
    }

    Context "Set new service account for SQL Services" {


        $errVar = $warnVar = $null
        $cred = New-Object System.Management.Automation.PSCredential($login, $securePassword)
        $results = Update-DbaSqlServiceAccount -ComputerName $computerName -ServiceName $services.ServiceName -ServiceCredential $cred -ErrorVariable $errVar -WarningVariable $warnVar

        It "Should return something" {
            $results | Should Not Be $null
        }
        It "Should have no errors or warnings" {
            $errVar | Should Be $null
            $warnVar | Should Be $null
        }
        It "Should be successful" {
            foreach ($result in $results) {
                $result.Status | Should Be 'Successful'
                $result.State | Should Be 'Running'
                $result.StartName | Should Be ".\$login"
            }
        }
    }

    Context "Change password of the service account" {
        #Change the password
        ([adsi]"WinNT://$computerName/$login,user").SetPassword($newPassword)

        $errVar = $warnVar = $null
        $results = $services | Sort-Object ServicePriority | Update-DbaSqlServiceAccount -Password $newSecurePassword -ErrorVariable $errVar -WarningVariable $warnVar

        It "Password change should return something" {
            $results | Should Not Be $null
        }
        It "Should have no errors or warnings" {
            $errVar | Should Be $null
            $warnVar | Should Be $null
        }
        It "Should be successful" {
            foreach ($result in $results) {
                $result.Status | Should Be 'Successful'
                $result.State | Should Be 'Running'
            }
        }

        $results = Get-DbaSqlService -ComputerName $computerName -ServiceName $services.ServiceName | Restart-DbaSqlService
        It "Service restart should return something" {
            $results | Should Not Be $null
        }
        It "Service restart should be successful" {
            foreach ($result in $results) {
                $result.Status | Should Be 'Successful'
                $result.State | Should Be 'Running'
            }
        }
    }

    Context "Change agent service account to local system" {
        $errVar = $warnVar = $null
        $results = $services | Where-Object { $_.ServiceType -eq 'Agent' } | Update-DbaSqlServiceAccount -Username 'NT AUTHORITY\LOCAL SYSTEM' -ErrorVariable $errVar -WarningVariable $warnVar

        It "Should return something" {
            $results | Should Not Be $null
        }
        It "Should have no errors or warnings" {
            $errVar | Should Be $null
            $warnVar | Should Be $null
        }
        It "Should be successful" {
            foreach ($result in $results) {
                $result.Status | Should Be 'Successful'
                $result.State | Should Be 'Running'
                $result.StartName | Should Be 'LocalSystem'
            }
        }
    }
    Context "Revert SQL Agent service account changes ($currentAgentUser)" {
        $errVar = $warnVar = $null
        $results = $services | Where-Object { $_.ServiceType -eq 'Agent' } | Update-DbaSqlServiceAccount -Username $currentAgentUser -ErrorVariable $errVar -WarningVariable $warnVar

        It "Should return something" {
            $results | Should Not Be $null
        }
        It "Should have no errors or warnings" {
            $errVar | Should Be $null
            $warnVar | Should Be $null
        }
        It "Should be successful" {
            foreach ($result in $results) {
                $result.Status | Should Be 'Successful'
                $result.State | Should Be 'Running'
                $result.StartName | Should Be $currentAgentUser
            }
        }
    }
    Context "Revert SQL Engine service account changes ($currentEngineUser)" {
        $errVar = $warnVar = $null
        $results = $services | Where-Object { $_.ServiceType -eq 'Engine' } | Update-DbaSqlServiceAccount -Username $currentEngineUser -ErrorVariable $errVar -WarningVariable $warnVar

        It "Should return something" {
            $results | Should Not Be $null
        }
        It "Should have no errors or warnings" {
            $errVar | Should Be $null
            $warnVar | Should Be $null
        }
        It "Should be successful" {
            foreach ($result in $results) {
                $result.Status | Should Be 'Successful'
                $result.State | Should Be 'Running'
                $result.StartName | Should Be $currentEngineUser
            }
        }

    }

    #Cleanup
    $server.Logins[$winLogin].Drop()
    $computer.Delete('User', $login)
}
tools\dbatools\tests\Watch-DbaDbLogin.Tests.ps1
<#
    The below statement stays in for every test you build.
#>
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

<#
    Unit test is required for any command added
#>
Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    Context "Validate parameters" {
        <#
            The $paramCount is adjusted based on the parameters your command will have.

            The $defaultParamCount is adjusted based on what type of command you are writing the test for:
                - Commands that *do not* include SupportShouldProcess, set defaultParamCount    = 11
                - Commands that *do* include SupportShouldProcess, set defaultParamCount        = 13
        #>
        $paramCount = 7
        $defaultParamCount = 11
        [object[]]$params = (Get-ChildItem function:\Watch-DbaDbLogin).Parameters.Keys
        $knownParameters = 'SqlInstance', 'SqlCredential', 'EnableException', 'Database', 'Table', 'SqlCms', 'ServersFromFile'
        it "Should contain our specific parameters" {
            ( (Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params -IncludeEqual | Where-Object SideIndicator -eq "==").Count ) | Should Be $paramCount
        }
        it "Should only contain $paramCount parameters" {
            $params.Count - $defaultParamCount | Should Be $paramCount
        }
    }
}
<#
    Integration test are custom to the command you are writing it for,
        but something similar to below should be included if applicable.

    The below examples are by no means set in stone and there are already
        a number of test that you can pull examples from in how they are done.
#>
<#
Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    $testFile = 'C:\temp\Servers.txt'
    $tableName = 'dbatoolsciwatchdblogin'
    $databaseName = 'master'
    if (Test-Path $testFile) {
        Remove-Item $testFile -Force
    }
    $script:instance1, $script:instance2 | Out-File $testFile
    AfterAll {
        $null = (Connect-DbaInstance -SqlInstance $script:instance1).Databases[$databaseName].Query("DROP TABLE $tableName")
    }
    Context "Command actually works" {
        Watch-DbaDbLogin -SqlInstance $script:instance1 -Database $databaseName -Table $tableName -ServersFromFile $testFile -EnableException
        $result = Get-DbaTable -SqlInstance $script:instance1 -Database $databaseName -Table $tableName -IncludeSystemDBs
        It "Should have created table $tableName in database $databaseName" {
            $result.Name | Should Be $tableName
        }
        It "Should have data in table $tableName in database $databaseName" {
            $result.Count | Should BeGreaterThan 0
        }
    }
}
#>
tools\dbatools\tests\Watch-DbaXESession.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

# This command is special and runs infinitely so don't actually try to run it
Describe "$CommandName Integration Tests" -Tags "IntegrationTests" {
    Context "Command functions as expected" {
        It "warns if SQL instance version is not supported" {
            $results = Watch-DbaXESession -SqlInstance $script:instance1 -Session system_health -WarningAction SilentlyContinue -WarningVariable versionwarn
            $versionwarn -match "Unsupported version" | Should Be $true
        }
    }
}
tools\dbatools\tests\Write-DbaDataTable.Tests.ps1
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan
. "$PSScriptRoot\constants.ps1"

Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
    BeforeAll {
        $server = Connect-DbaInstance -SqlInstance $script:instance2
        $random = Get-Random
        $db = "dbatoolsci_writedbadaatable$random"
        $server.Query("CREATE DATABASE $db")
    }
    AfterAll {
        $null = Get-DbaDatabase -SqlInstance $server -Database $db | Remove-DbaDatabase -Confirm:$false
    }

    # calling random function to throw data into a table
    It "defaults to dbo if no schema is specified" {
        $results = Get-ChildItem | ConvertTo-DbaDataTable
        $results | Write-DbaDataTable -SqlInstance $script:instance2 -Database $db -Table 'childitem' -AutoCreateTable

        ($server.Databases[$db].Tables | Where-Object { $_.Schema -eq 'dbo' -and $_.Name -eq 'childitem' }).Count | Should Be 1
    }
}
tools\dbatools\xml\dbatools.Format.ps1xml
 
tools\dbatools\xml\dbatools.Types.ps1xml
 
tools\LICENSE.txt
From: https://raw.githubusercontent.com/sqlcollaborative/dbatools/master/LICENSE

LICENSE

MIT License

Copyright (c) 2018 Chrissy LeMaire

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
tools\VERIFICATION.txt
VERIFICATION
Verification is intended to assist the Chocolatey moderators and community in verifying that this package's contents are trustworthy.

To verify the files using the project source:

1. Please go to the project source location (https://github.com/sqlcollaborative/dbatools) and download the source files;
2. Build the source to create the binary files to verify;
3. Use Get-FileHash -Path <FILE TO VERIFY> to get the file hash value from both the built file (from step 1 above) and the file from the package and compare them;

Alternatively you can download the module from the PowerShell Gallery ...

    Save-Module -Name dbatools -Path <PATH TO DOWNLOAD TO>

... and compare the files from the package against those in the installed module. Again use Get-FileHash -Path <FILE TO VERIFY> to retrieve those hash values.

Log in or click on link to see number of positives.

In cases where actual malware is found, the packages are subject to removal. Software sometimes has false positives. Moderators do not necessarily validate the safety of the underlying software, only that a package retrieves software from the official distribution point and/or validate embedded software against official distribution point (where distribution rights allow redistribution).

Chocolatey Pro provides runtime protection from possible malware.

Add to Builder Version Downloads Last Updated Status
dbatools (PowerShell Module) 2.1.13 560 Friday, April 12, 2024 Approved
dbatools (PowerShell Module) 2.1.12 569 Saturday, March 30, 2024 Approved
dbatools (PowerShell Module) 2.1.11 609 Saturday, March 16, 2024 Approved
dbatools (PowerShell Module) 2.1.10 335 Saturday, March 9, 2024 Approved
dbatools (PowerShell Module) 2.1.9 44 Saturday, March 9, 2024 Approved
dbatools (PowerShell Module) 2.1.8 636 Thursday, February 22, 2024 Approved
dbatools (PowerShell Module) 2.1.6 1081 Friday, December 22, 2023 Approved
dbatools (PowerShell Module) 2.1.5 902 Sunday, November 5, 2023 Approved
dbatools (PowerShell Module) 2.1.4 662 Saturday, October 14, 2023 Approved
dbatools (PowerShell Module) 2.1.3 288 Wednesday, October 11, 2023 Approved
dbatools (PowerShell Module) 2.1.2 432 Wednesday, October 4, 2023 Approved
dbatools (PowerShell Module) 2.1.1 417 Friday, September 29, 2023 Approved
dbatools (PowerShell Module) 2.1.0 516 Thursday, September 21, 2023 Approved
dbatools (PowerShell Module) 2.0.4 2320 Friday, June 9, 2023 Approved
dbatools (PowerShell Module) 1.1.145 5987 Friday, January 27, 2023 Approved
dbatools (PowerShell Module) 1.1.144 230 Thursday, January 26, 2023 Approved
dbatools (PowerShell Module) 1.1.143 1847 Thursday, November 17, 2022 Approved
dbatools (PowerShell Module) 1.1.142 1139 Sunday, October 23, 2022 Approved
dbatools (PowerShell Module) 1.1.141 145 Friday, October 21, 2022 Approved
dbatools (PowerShell Module) 1.1.140 322 Monday, October 17, 2022 Approved
dbatools (PowerShell Module) 1.1.139 476 Tuesday, October 11, 2022 Approved
dbatools (PowerShell Module) 1.1.138 276 Tuesday, October 11, 2022 Approved
dbatools (PowerShell Module) 1.1.137 350 Thursday, October 6, 2022 Approved
dbatools (PowerShell Module) 1.1.136 392 Friday, September 30, 2022 Approved
dbatools (PowerShell Module) 1.1.134 496 Wednesday, September 21, 2022 Approved
dbatools (PowerShell Module) 1.1.133 302 Saturday, September 17, 2022 Approved
dbatools (PowerShell Module) 1.1.132 530 Friday, September 9, 2022 Approved
dbatools (PowerShell Module) 1.1.131 141 Friday, September 9, 2022 Approved
dbatools (PowerShell Module) 1.1.130 184 Wednesday, September 7, 2022 Approved
dbatools (PowerShell Module) 1.1.129 264 Monday, September 5, 2022 Approved
dbatools (PowerShell Module) 1.1.128 372 Tuesday, August 30, 2022 Approved
dbatools (PowerShell Module) 1.1.127 351 Wednesday, August 24, 2022 Approved
dbatools (PowerShell Module) 1.1.126 362 Friday, August 19, 2022 Approved
dbatools (PowerShell Module) 1.1.125 293 Tuesday, August 16, 2022 Approved
dbatools (PowerShell Module) 1.1.124 179 Tuesday, August 16, 2022 Approved
dbatools (PowerShell Module) 1.1.122 470 Saturday, August 6, 2022 Approved
dbatools (PowerShell Module) 1.1.121 241 Thursday, August 4, 2022 Approved
dbatools (PowerShell Module) 1.1.120 294 Monday, August 1, 2022 Approved
dbatools (PowerShell Module) 1.1.119 244 Saturday, July 30, 2022 Approved
dbatools (PowerShell Module) 1.1.118 393 Monday, July 25, 2022 Approved
dbatools (PowerShell Module) 1.1.117 271 Thursday, July 21, 2022 Approved
dbatools (PowerShell Module) 1.1.116 137 Thursday, July 21, 2022 Approved
dbatools (PowerShell Module) 1.1.115 138 Thursday, July 21, 2022 Approved
dbatools (PowerShell Module) 1.1.114 443 Thursday, July 14, 2022 Approved
dbatools (PowerShell Module) 1.1.113 293 Monday, July 11, 2022 Approved
dbatools (PowerShell Module) 1.1.112 254 Friday, July 8, 2022 Approved
dbatools (PowerShell Module) 1.1.111 333 Sunday, July 3, 2022 Approved
dbatools (PowerShell Module) 1.1.110 205 Thursday, June 30, 2022 Approved
dbatools (PowerShell Module) 1.1.109 227 Wednesday, June 29, 2022 Approved
dbatools (PowerShell Module) 1.1.108 370 Friday, June 24, 2022 Approved
dbatools (PowerShell Module) 1.1.107 276 Wednesday, June 22, 2022 Approved
dbatools (PowerShell Module) 1.1.106 532 Wednesday, June 15, 2022 Approved
dbatools (PowerShell Module) 1.1.105 112 Wednesday, June 15, 2022 Approved
dbatools (PowerShell Module) 1.1.104 206 Tuesday, June 14, 2022 Approved
dbatools (PowerShell Module) 1.1.103 452 Sunday, June 5, 2022 Approved
dbatools (PowerShell Module) 1.1.102 237 Thursday, June 2, 2022 Approved
dbatools (PowerShell Module) 1.1.101 313 Sunday, May 29, 2022 Approved
dbatools (PowerShell Module) 1.1.100 141 Saturday, May 28, 2022 Approved
dbatools (PowerShell Module) 1.1.99 194 Friday, May 27, 2022 Approved
dbatools (PowerShell Module) 1.1.98 246 Tuesday, May 24, 2022 Approved
dbatools (PowerShell Module) 1.1.97 133 Tuesday, May 24, 2022 Approved
dbatools (PowerShell Module) 1.1.96 508 Saturday, May 21, 2022 Approved
dbatools (PowerShell Module) 1.1.95 973 Wednesday, May 11, 2022 Approved
dbatools (PowerShell Module) 1.1.94 53 Tuesday, May 10, 2022 Approved
dbatools (PowerShell Module) 1.1.93 221 Tuesday, May 10, 2022 Approved
dbatools (PowerShell Module) 1.1.92 361 Sunday, May 8, 2022 Approved
dbatools (PowerShell Module) 1.1.91 307 Friday, May 6, 2022 Approved
dbatools (PowerShell Module) 1.1.90 311 Wednesday, May 4, 2022 Approved
dbatools (PowerShell Module) 1.1.89 621 Thursday, April 28, 2022 Approved
dbatools (PowerShell Module) 1.1.88 1379 Friday, April 8, 2022 Approved
dbatools (PowerShell Module) 1.1.87 251 Wednesday, April 6, 2022 Approved
dbatools (PowerShell Module) 1.1.86 246 Monday, April 4, 2022 Approved
dbatools (PowerShell Module) 1.1.85 315 Saturday, April 2, 2022 Approved
dbatools (PowerShell Module) 1.1.84 472 Thursday, March 31, 2022 Approved
dbatools (PowerShell Module) 1.1.83 241 Tuesday, March 29, 2022 Approved
dbatools (PowerShell Module) 1.1.82 657 Friday, March 25, 2022 Approved
dbatools (PowerShell Module) 1.1.81 918 Wednesday, March 16, 2022 Approved
dbatools (PowerShell Module) 1.1.80 188 Tuesday, March 15, 2022 Approved
dbatools (PowerShell Module) 1.1.79 144 Monday, March 14, 2022 Approved
dbatools (PowerShell Module) 1.1.78 588 Friday, March 11, 2022 Approved
dbatools (PowerShell Module) 1.1.77 95 Friday, March 11, 2022 Approved
dbatools (PowerShell Module) 1.1.76 751 Saturday, March 5, 2022 Approved
dbatools (PowerShell Module) 1.1.75 787 Saturday, February 26, 2022 Approved
dbatools (PowerShell Module) 1.1.74 194 Thursday, February 24, 2022 Approved
dbatools (PowerShell Module) 1.1.73 221 Tuesday, February 22, 2022 Approved
dbatools (PowerShell Module) 1.1.72 257 Monday, February 21, 2022 Approved
dbatools (PowerShell Module) 1.1.71 684 Tuesday, February 15, 2022 Approved
dbatools (PowerShell Module) 1.1.70 764 Wednesday, February 9, 2022 Approved
dbatools (PowerShell Module) 1.1.69 266 Monday, February 7, 2022 Approved
dbatools (PowerShell Module) 1.1.68 152 Monday, February 7, 2022 Approved
dbatools (PowerShell Module) 1.1.67 522 Saturday, February 5, 2022 Approved
dbatools (PowerShell Module) 1.1.66 161 Friday, February 4, 2022 Approved
dbatools (PowerShell Module) 1.1.65 232 Thursday, February 3, 2022 Approved
dbatools (PowerShell Module) 1.1.64 105 Wednesday, February 2, 2022 Approved
dbatools (PowerShell Module) 1.1.63 241 Tuesday, February 1, 2022 Approved
dbatools (PowerShell Module) 1.1.62 336 Sunday, January 30, 2022 Approved
dbatools (PowerShell Module) 1.1.61 356 Saturday, January 29, 2022 Approved
dbatools (PowerShell Module) 1.1.60 306 Tuesday, January 25, 2022 Approved
dbatools (PowerShell Module) 1.1.59 633 Thursday, January 20, 2022 Approved
dbatools (PowerShell Module) 1.1.58 126 Thursday, January 20, 2022 Approved
dbatools (PowerShell Module) 1.1.57 141 Thursday, January 20, 2022 Approved
dbatools (PowerShell Module) 1.1.56 233 Tuesday, January 18, 2022 Approved
dbatools (PowerShell Module) 1.1.55 145 Tuesday, January 18, 2022 Approved
dbatools (PowerShell Module) 1.1.54 543 Saturday, January 15, 2022 Approved
dbatools (PowerShell Module) 1.1.53 155 Friday, January 14, 2022 Approved
dbatools (PowerShell Module) 1.1.52 87 Friday, January 14, 2022 Approved
dbatools (PowerShell Module) 1.1.51 163 Thursday, January 13, 2022 Approved
dbatools (PowerShell Module) 1.1.50 208 Tuesday, January 11, 2022 Approved
dbatools (PowerShell Module) 1.1.49 635 Thursday, January 6, 2022 Approved
dbatools (PowerShell Module) 1.1.48 245 Tuesday, January 4, 2022 Approved
dbatools (PowerShell Module) 1.1.46 839 Thursday, December 23, 2021 Approved
dbatools (PowerShell Module) 1.1.45 185 Wednesday, December 22, 2021 Approved
dbatools (PowerShell Module) 1.1.44 745 Tuesday, December 14, 2021 Approved
dbatools (PowerShell Module) 1.1.43 616 Friday, December 10, 2021 Approved
dbatools (PowerShell Module) 1.1.42 211 Wednesday, December 8, 2021 Approved
dbatools (PowerShell Module) 1.1.41 651 Friday, December 3, 2021 Approved
dbatools (PowerShell Module) 1.1.40 216 Wednesday, December 1, 2021 Approved
dbatools (PowerShell Module) 1.1.39 198 Monday, November 29, 2021 Approved
dbatools (PowerShell Module) 1.1.38 685 Wednesday, November 24, 2021 Approved
dbatools (PowerShell Module) 1.1.37 182 Tuesday, November 23, 2021 Approved
dbatools (PowerShell Module) 1.1.36 668 Friday, November 19, 2021 Approved
dbatools (PowerShell Module) 1.1.35 364 Monday, November 15, 2021 Approved
dbatools (PowerShell Module) 1.1.34 572 Thursday, November 11, 2021 Approved
dbatools (PowerShell Module) 1.1.33 233 Tuesday, November 9, 2021 Approved
dbatools (PowerShell Module) 1.1.32 604 Saturday, November 6, 2021 Approved
dbatools (PowerShell Module) 1.1.31 421 Monday, November 1, 2021 Approved
dbatools (PowerShell Module) 1.1.30 92 Monday, November 1, 2021 Approved
dbatools (PowerShell Module) 1.1.29 473 Thursday, October 28, 2021 Approved
dbatools (PowerShell Module) 1.1.28 93 Thursday, October 28, 2021 Approved
dbatools (PowerShell Module) 1.1.27 294 Tuesday, October 26, 2021 Approved
dbatools (PowerShell Module) 1.1.26 472 Saturday, October 23, 2021 Approved
dbatools (PowerShell Module) 1.1.25 307 Wednesday, October 20, 2021 Approved
dbatools (PowerShell Module) 1.1.24 820 Tuesday, October 12, 2021 Approved
dbatools (PowerShell Module) 1.1.23 714 Wednesday, October 6, 2021 Approved
dbatools (PowerShell Module) 1.1.22 267 Tuesday, October 5, 2021 Approved
dbatools (PowerShell Module) 1.1.21 837 Monday, September 27, 2021 Approved
dbatools (PowerShell Module) 1.1.20 602 Wednesday, September 22, 2021 Approved
dbatools (PowerShell Module) 1.1.19 612 Saturday, September 18, 2021 Approved
dbatools (PowerShell Module) 1.1.18 352 Wednesday, September 15, 2021 Approved
dbatools (PowerShell Module) 1.1.17 93 Tuesday, September 14, 2021 Approved
dbatools (PowerShell Module) 1.1.16 112 Tuesday, September 14, 2021 Approved
dbatools (PowerShell Module) 1.1.15 1001 Thursday, September 2, 2021 Approved
dbatools (PowerShell Module) 1.1.14 71 Tuesday, August 31, 2021 Approved
dbatools (PowerShell Module) 1.1.13 281 Tuesday, August 31, 2021 Approved
dbatools (PowerShell Module) 1.1.12 919 Tuesday, August 24, 2021 Approved
dbatools (PowerShell Module) 1.1.11 1070 Wednesday, August 11, 2021 Approved
dbatools (PowerShell Module) 1.1.10 227 Tuesday, August 10, 2021 Approved
dbatools (PowerShell Module) 1.1.9 621 Thursday, August 5, 2021 Approved
dbatools (PowerShell Module) 1.1.8 109 Thursday, August 5, 2021 Approved
dbatools (PowerShell Module) 1.1.7 235 Tuesday, August 3, 2021 Approved
dbatools (PowerShell Module) 1.1.6 202 Monday, August 2, 2021 Approved
dbatools (PowerShell Module) 1.1.5 513 Saturday, July 31, 2021 Approved
dbatools (PowerShell Module) 1.1.4 157 Friday, July 30, 2021 Approved
dbatools (PowerShell Module) 1.1.3 162 Friday, July 30, 2021 Approved
dbatools (PowerShell Module) 1.1.1 203 Tuesday, July 27, 2021 Approved
dbatools (PowerShell Module) 1.1.0 656 Friday, July 23, 2021 Approved
dbatools (PowerShell Module) 1.0.173 758 Tuesday, July 13, 2021 Approved
dbatools (PowerShell Module) 1.0.171 262 Monday, July 12, 2021 Approved
dbatools (PowerShell Module) 1.0.170 609 Wednesday, July 7, 2021 Approved
dbatools (PowerShell Module) 1.0.169 184 Tuesday, July 6, 2021 Approved
dbatools (PowerShell Module) 1.0.168 387 Sunday, July 4, 2021 Approved
dbatools (PowerShell Module) 1.0.167 314 Saturday, July 3, 2021 Approved
dbatools (PowerShell Module) 1.0.166 182 Friday, July 2, 2021 Approved
dbatools (PowerShell Module) 1.0.165 332 Tuesday, June 29, 2021 Approved
dbatools (PowerShell Module) 1.0.164 450 Friday, June 25, 2021 Approved
dbatools (PowerShell Module) 1.0.163 213 Thursday, June 24, 2021 Approved
dbatools (PowerShell Module) 1.0.162 247 Tuesday, June 22, 2021 Approved
dbatools (PowerShell Module) 1.0.161 175 Monday, June 21, 2021 Approved
dbatools (PowerShell Module) 1.0.160 325 Friday, June 18, 2021 Approved
dbatools (PowerShell Module) 1.0.156 607 Friday, June 11, 2021 Approved
dbatools (PowerShell Module) 1.0.155 306 Wednesday, June 9, 2021 Approved
dbatools (PowerShell Module) 1.0.153 987 Wednesday, May 26, 2021 Approved
dbatools (PowerShell Module) 1.0.152 221 Tuesday, May 25, 2021 Approved
dbatools (PowerShell Module) 1.0.151 379 Sunday, May 23, 2021 Approved
dbatools (PowerShell Module) 1.0.150 147 Saturday, May 22, 2021 Approved
dbatools (PowerShell Module) 1.0.149 405 Wednesday, May 19, 2021 Approved
dbatools (PowerShell Module) 1.0.148 344 Friday, May 14, 2021 Approved
dbatools (PowerShell Module) 1.0.147 527 Thursday, May 6, 2021 Approved
dbatools (PowerShell Module) 1.0.146 210 Wednesday, May 5, 2021 Approved
dbatools (PowerShell Module) 1.0.145 822 Sunday, April 18, 2021 Approved
dbatools (PowerShell Module) 1.0.144 135 Sunday, April 18, 2021 Approved
dbatools (PowerShell Module) 1.0.142 844 Sunday, April 4, 2021 Approved
dbatools (PowerShell Module) 1.0.141 1101 Thursday, March 11, 2021 Approved
dbatools (PowerShell Module) 1.0.140 817 Tuesday, February 23, 2021 Approved
dbatools (PowerShell Module) 1.0.139 519 Tuesday, February 16, 2021 Approved
dbatools (PowerShell Module) 1.0.138 219 Monday, February 15, 2021 Approved
dbatools (PowerShell Module) 1.0.137 1621 Tuesday, January 19, 2021 Approved
dbatools (PowerShell Module) 1.0.136 1026 Monday, December 28, 2020 Approved
dbatools (PowerShell Module) 1.0.135 1042 Sunday, December 6, 2020 Approved
dbatools (PowerShell Module) 1.0.134 180 Friday, December 4, 2020 Approved
dbatools (PowerShell Module) 1.0.133 538 Monday, November 23, 2020 Approved
dbatools (PowerShell Module) 1.0.131 423 Monday, November 16, 2020 Approved
dbatools (PowerShell Module) 1.0.130 707 Friday, November 6, 2020 Approved
dbatools (PowerShell Module) 1.0.128 523 Monday, November 2, 2020 Approved
dbatools (PowerShell Module) 1.0.127 330 Wednesday, October 28, 2020 Approved
dbatools (PowerShell Module) 1.0.126 597 Thursday, October 22, 2020 Approved
dbatools (PowerShell Module) 1.0.125 503 Saturday, October 17, 2020 Approved
dbatools (PowerShell Module) 1.0.124 576 Wednesday, October 7, 2020 Approved
dbatools (PowerShell Module) 1.0.123 462 Friday, October 2, 2020 Approved
dbatools (PowerShell Module) 1.0.122 209 Thursday, October 1, 2020 Approved
dbatools (PowerShell Module) 1.0.121 382 Friday, September 25, 2020 Approved
dbatools (PowerShell Module) 1.0.120 573 Friday, September 18, 2020 Approved
dbatools (PowerShell Module) 1.0.119 389 Monday, September 14, 2020 Approved
dbatools (PowerShell Module) 1.0.116 788 Friday, August 28, 2020 Approved
dbatools (PowerShell Module) 1.0.115 1414 Saturday, July 25, 2020 Approved
dbatools (PowerShell Module) 1.0.114 1129 Saturday, July 4, 2020 Approved
dbatools (PowerShell Module) 1.0.113 955 Monday, June 15, 2020 Approved
dbatools (PowerShell Module) 1.0.112 874 Tuesday, June 2, 2020 Approved
dbatools (PowerShell Module) 1.0.111 791 Friday, May 22, 2020 Approved
dbatools (PowerShell Module) 1.0.110 201 Thursday, May 21, 2020 Approved
dbatools (PowerShell Module) 1.0.109 643 Thursday, May 14, 2020 Approved
dbatools (PowerShell Module) 1.0.108 798 Wednesday, May 6, 2020 Approved
dbatools (PowerShell Module) 1.0.107 683 Monday, April 27, 2020 Approved
dbatools (PowerShell Module) 1.0.106 521 Monday, April 20, 2020 Approved
dbatools (PowerShell Module) 1.0.105 508 Wednesday, April 15, 2020 Approved
dbatools (PowerShell Module) 1.0.104 851 Monday, March 30, 2020 Approved
dbatools (PowerShell Module) 1.0.103 566 Monday, March 23, 2020 Approved
dbatools (PowerShell Module) 1.0.102 436 Thursday, March 19, 2020 Approved
dbatools (PowerShell Module) 1.0.101 688 Tuesday, March 3, 2020 Approved
dbatools (PowerShell Module) 1.0.100 422 Friday, February 28, 2020 Approved
dbatools (PowerShell Module) 1.0.99 260 Thursday, February 27, 2020 Approved
dbatools (PowerShell Module) 1.0.97 650 Saturday, February 15, 2020 Approved
dbatools (PowerShell Module) 1.0.96 229 Saturday, February 15, 2020 Approved
dbatools (PowerShell Module) 1.0.95 235 Friday, February 14, 2020 Approved
dbatools (PowerShell Module) 1.0.93 274 Wednesday, February 12, 2020 Approved
dbatools (PowerShell Module) 1.0.92 330 Monday, February 10, 2020 Approved
dbatools (PowerShell Module) 1.0.91 396 Thursday, February 6, 2020 Approved
dbatools (PowerShell Module) 1.0.90 284 Monday, February 3, 2020 Approved
dbatools (PowerShell Module) 1.0.89 303 Saturday, February 1, 2020 Approved
dbatools (PowerShell Module) 1.0.88 508 Saturday, January 25, 2020 Approved
dbatools (PowerShell Module) 1.0.87 273 Thursday, January 23, 2020 Approved
dbatools (PowerShell Module) 1.0.86 263 Thursday, January 23, 2020 Approved
dbatools (PowerShell Module) 1.0.85 245 Monday, January 20, 2020 Approved
dbatools (PowerShell Module) 1.0.84 390 Friday, January 17, 2020 Approved
dbatools (PowerShell Module) 1.0.83 421 Friday, January 10, 2020 Approved
dbatools (PowerShell Module) 1.0.81 301 Wednesday, January 8, 2020 Approved
dbatools (PowerShell Module) 1.0.80 314 Monday, January 6, 2020 Approved
dbatools (PowerShell Module) 1.0.79 303 Friday, January 3, 2020 Approved
dbatools (PowerShell Module) 1.0.78 235 Thursday, January 2, 2020 Approved
dbatools (PowerShell Module) 1.0.77 307 Friday, December 20, 2019 Approved
dbatools (PowerShell Module) 1.0.75 493 Sunday, December 8, 2019 Approved
dbatools (PowerShell Module) 1.0.74 365 Monday, December 2, 2019 Approved
dbatools (PowerShell Module) 1.0.73 399 Friday, November 22, 2019 Approved
dbatools (PowerShell Module) 1.0.72 345 Tuesday, November 19, 2019 Approved
dbatools (PowerShell Module) 1.0.71 351 Thursday, November 14, 2019 Approved
dbatools (PowerShell Module) 1.0.68 284 Monday, November 11, 2019 Approved
dbatools (PowerShell Module) 1.0.67 593 Tuesday, November 5, 2019 Approved
dbatools (PowerShell Module) 1.0.66 216 Monday, November 4, 2019 Approved
dbatools (PowerShell Module) 1.0.65 323 Friday, November 1, 2019 Approved
dbatools (PowerShell Module) 1.0.64 290 Tuesday, October 29, 2019 Approved
dbatools (PowerShell Module) 1.0.62 288 Monday, October 28, 2019 Approved
dbatools (PowerShell Module) 1.0.61 287 Thursday, October 24, 2019 Approved
dbatools (PowerShell Module) 1.0.60 283 Tuesday, October 22, 2019 Approved
dbatools (PowerShell Module) 1.0.59 233 Monday, October 21, 2019 Approved
dbatools (PowerShell Module) 1.0.58 265 Sunday, October 20, 2019 Approved
dbatools (PowerShell Module) 1.0.57 257 Friday, October 18, 2019 Approved
dbatools (PowerShell Module) 1.0.55 206 Friday, October 18, 2019 Approved
dbatools (PowerShell Module) 1.0.54 277 Thursday, October 17, 2019 Approved
dbatools (PowerShell Module) 1.0.53 272 Tuesday, October 15, 2019 Approved
dbatools (PowerShell Module) 1.0.52 324 Monday, October 7, 2019 Approved
dbatools (PowerShell Module) 1.0.51 323 Wednesday, October 2, 2019 Approved
dbatools (PowerShell Module) 1.0.50 316 Friday, September 27, 2019 Approved
dbatools (PowerShell Module) 1.0.49 338 Tuesday, September 24, 2019 Approved
dbatools (PowerShell Module) 1.0.48 268 Monday, September 23, 2019 Approved
dbatools (PowerShell Module) 1.0.47 286 Friday, September 20, 2019 Approved
dbatools (PowerShell Module) 1.0.46 386 Saturday, September 14, 2019 Approved
dbatools (PowerShell Module) 1.0.45 254 Friday, September 13, 2019 Approved
dbatools (PowerShell Module) 1.0.44 264 Thursday, September 12, 2019 Approved
dbatools (PowerShell Module) 1.0.43 279 Tuesday, September 10, 2019 Approved
dbatools (PowerShell Module) 1.0.42 273 Sunday, September 8, 2019 Approved
dbatools (PowerShell Module) 1.0.41 242 Saturday, September 7, 2019 Approved
dbatools (PowerShell Module) 1.0.40 264 Thursday, September 5, 2019 Approved
dbatools (PowerShell Module) 1.0.39 532 Wednesday, September 4, 2019 Approved
dbatools (PowerShell Module) 1.0.38 464 Wednesday, August 28, 2019 Approved
dbatools (PowerShell Module) 1.0.36 268 Tuesday, August 27, 2019 Approved
dbatools (PowerShell Module) 1.0.35 351 Thursday, August 22, 2019 Approved
dbatools (PowerShell Module) 1.0.34 281 Monday, August 19, 2019 Approved
dbatools (PowerShell Module) 1.0.33 309 Thursday, August 15, 2019 Approved
dbatools (PowerShell Module) 1.0.32 269 Wednesday, August 14, 2019 Approved
dbatools (PowerShell Module) 1.0.30 348 Thursday, August 8, 2019 Approved
dbatools (PowerShell Module) 1.0.29 294 Saturday, August 3, 2019 Approved
dbatools (PowerShell Module) 1.0.28 247 Friday, August 2, 2019 Approved
dbatools (PowerShell Module) 1.0.27 285 Wednesday, July 31, 2019 Approved
dbatools (PowerShell Module) 1.0.26 241 Monday, July 29, 2019 Approved
dbatools (PowerShell Module) 1.0.25 219 Monday, July 29, 2019 Approved
dbatools (PowerShell Module) 1.0.23 361 Tuesday, July 23, 2019 Approved
dbatools (PowerShell Module) 1.0.22 362 Tuesday, July 16, 2019 Approved
dbatools (PowerShell Module) 1.0.21 316 Friday, July 12, 2019 Approved
dbatools (PowerShell Module) 1.0.20 308 Wednesday, July 10, 2019 Approved
dbatools (PowerShell Module) 1.0.19 246 Wednesday, July 10, 2019 Approved
dbatools (PowerShell Module) 1.0.17 272 Tuesday, July 9, 2019 Approved
dbatools (PowerShell Module) 1.0.15 288 Saturday, July 6, 2019 Approved
dbatools (PowerShell Module) 1.0.14 217 Friday, July 5, 2019 Approved
dbatools (PowerShell Module) 1.0.13 232 Thursday, July 4, 2019 Approved
dbatools (PowerShell Module) 1.0.12 300 Tuesday, July 2, 2019 Approved
dbatools (PowerShell Module) 1.0.11 202 Monday, July 1, 2019 Approved
dbatools (PowerShell Module) 1.0.10 235 Saturday, June 29, 2019 Approved
dbatools (PowerShell Module) 1.0.9 311 Wednesday, June 26, 2019 Approved
dbatools (PowerShell Module) 1.0.6 245 Wednesday, June 26, 2019 Approved
dbatools (PowerShell Module) 1.0.5 255 Monday, June 24, 2019 Approved
dbatools (PowerShell Module) 1.0.4 285 Sunday, June 23, 2019 Approved
dbatools (PowerShell Module) 1.0.3 245 Sunday, June 23, 2019 Approved
dbatools (PowerShell Module) 1.0.2 272 Friday, June 21, 2019 Approved
dbatools (PowerShell Module) 1.0.0 309 Thursday, June 20, 2019 Approved
dbatools (PowerShell Module) 0.9.834 458 Friday, May 31, 2019 Approved
dbatools (PowerShell Module) 0.9.833 239 Thursday, May 30, 2019 Approved
dbatools (PowerShell Module) 0.9.832 302 Friday, May 24, 2019 Approved
dbatools (PowerShell Module) 0.9.831 280 Thursday, May 23, 2019 Approved
dbatools (PowerShell Module) 0.9.830 287 Tuesday, May 21, 2019 Approved
dbatools (PowerShell Module) 0.9.829 214 Friday, May 17, 2019 Approved
dbatools (PowerShell Module) 0.9.828 296 Wednesday, May 15, 2019 Approved
dbatools (PowerShell Module) 0.9.827 284 Tuesday, May 14, 2019 Approved
dbatools (PowerShell Module) 0.9.826 271 Monday, May 13, 2019 Approved
dbatools (PowerShell Module) 0.9.825 315 Friday, May 10, 2019 Approved
dbatools (PowerShell Module) 0.9.824 257 Friday, May 10, 2019 Approved
dbatools (PowerShell Module) 0.9.823 269 Wednesday, May 8, 2019 Approved
dbatools (PowerShell Module) 0.9.822 292 Monday, May 6, 2019 Approved
dbatools (PowerShell Module) 0.9.821 320 Friday, May 3, 2019 Approved
dbatools (PowerShell Module) 0.9.819 275 Wednesday, May 1, 2019 Approved
dbatools (PowerShell Module) 0.9.818 268 Tuesday, April 30, 2019 Approved
dbatools (PowerShell Module) 0.9.817 242 Tuesday, April 30, 2019 Approved
dbatools (PowerShell Module) 0.9.815 245 Tuesday, April 30, 2019 Approved
dbatools (PowerShell Module) 0.9.814 263 Monday, April 29, 2019 Approved
dbatools (PowerShell Module) 0.9.812 293 Saturday, April 27, 2019 Approved
dbatools (PowerShell Module) 0.9.811 251 Thursday, April 25, 2019 Approved
dbatools (PowerShell Module) 0.9.810 293 Thursday, April 25, 2019 Approved
dbatools (PowerShell Module) 0.9.809 306 Saturday, April 20, 2019 Approved
dbatools (PowerShell Module) 0.9.808 237 Thursday, April 18, 2019 Approved
dbatools (PowerShell Module) 0.9.807 239 Wednesday, April 17, 2019 Approved
dbatools (PowerShell Module) 0.9.804 296 Monday, April 15, 2019 Approved
dbatools (PowerShell Module) 0.9.803 301 Wednesday, April 10, 2019 Approved
dbatools (PowerShell Module) 0.9.802 302 Sunday, April 7, 2019 Approved
dbatools (PowerShell Module) 0.9.801 266 Thursday, April 4, 2019 Approved
dbatools (PowerShell Module) 0.9.800 303 Monday, April 1, 2019 Approved
dbatools (PowerShell Module) 0.9.799 248 Sunday, March 31, 2019 Approved
dbatools (PowerShell Module) 0.9.798 260 Thursday, March 28, 2019 Approved
dbatools (PowerShell Module) 0.9.797 263 Tuesday, March 26, 2019 Approved
dbatools (PowerShell Module) 0.9.795 244 Sunday, March 24, 2019 Approved
dbatools (PowerShell Module) 0.9.794 268 Saturday, March 23, 2019 Approved
dbatools (PowerShell Module) 0.9.793 259 Thursday, March 21, 2019 Approved
dbatools (PowerShell Module) 0.9.792 283 Wednesday, March 20, 2019 Approved
dbatools (PowerShell Module) 0.9.785 198 Saturday, March 16, 2019 Approved
dbatools (PowerShell Module) 0.9.784 212 Tuesday, March 12, 2019 Approved
dbatools (PowerShell Module) 0.9.782 187 Monday, March 11, 2019 Approved
dbatools (PowerShell Module) 0.9.781 177 Saturday, March 9, 2019 Approved
dbatools (PowerShell Module) 0.9.780 201 Thursday, March 7, 2019 Approved
dbatools (PowerShell Module) 0.9.779 193 Wednesday, March 6, 2019 Approved
dbatools (PowerShell Module) 0.9.778 154 Tuesday, March 5, 2019 Approved
dbatools (PowerShell Module) 0.9.777 146 Monday, March 4, 2019 Approved
dbatools (PowerShell Module) 0.9.775 223 Tuesday, February 26, 2019 Approved
dbatools (PowerShell Module) 0.9.774 155 Tuesday, February 26, 2019 Approved
dbatools (PowerShell Module) 0.9.773 224 Monday, February 25, 2019 Approved
dbatools (PowerShell Module) 0.9.772 201 Sunday, February 24, 2019 Approved
dbatools (PowerShell Module) 0.9.771 193 Wednesday, February 20, 2019 Approved
dbatools (PowerShell Module) 0.9.770 249 Sunday, February 17, 2019 Approved
dbatools (PowerShell Module) 0.9.757 217 Tuesday, February 12, 2019 Approved
dbatools (PowerShell Module) 0.9.755 203 Sunday, February 10, 2019 Approved
dbatools (PowerShell Module) 0.9.754 184 Thursday, February 7, 2019 Approved
dbatools (PowerShell Module) 0.9.753 146 Thursday, February 7, 2019 Approved
dbatools (PowerShell Module) 0.9.752 184 Sunday, February 3, 2019 Approved
dbatools (PowerShell Module) 0.9.751 179 Thursday, January 31, 2019 Approved
dbatools (PowerShell Module) 0.9.750 305 Friday, January 25, 2019 Approved
dbatools (PowerShell Module) 0.9.749 253 Thursday, January 24, 2019 Approved
dbatools (PowerShell Module) 0.9.748 260 Thursday, January 24, 2019 Approved
dbatools (PowerShell Module) 0.9.747 386 Thursday, January 24, 2019 Approved
dbatools (PowerShell Module) 0.9.745 196 Wednesday, January 23, 2019 Approved
dbatools (PowerShell Module) 0.9.744 195 Wednesday, January 23, 2019 Approved
dbatools (PowerShell Module) 0.9.743 312 Monday, January 21, 2019 Approved
dbatools (PowerShell Module) 0.9.742 251 Tuesday, January 15, 2019 Approved
dbatools (PowerShell Module) 0.9.740 288 Friday, January 11, 2019 Approved
dbatools (PowerShell Module) 0.9.739 169 Friday, January 11, 2019 Approved
dbatools (PowerShell Module) 0.9.738 204 Thursday, January 10, 2019 Approved
dbatools (PowerShell Module) 0.9.737 193 Wednesday, January 9, 2019 Approved
dbatools (PowerShell Module) 0.9.735 169 Tuesday, January 8, 2019 Approved
dbatools (PowerShell Module) 0.9.734 265 Thursday, January 3, 2019 Approved
dbatools (PowerShell Module) 0.9.733 234 Monday, December 31, 2018 Approved
dbatools (PowerShell Module) 0.9.732 220 Thursday, December 27, 2018 Approved
dbatools (PowerShell Module) 0.9.731 204 Sunday, December 23, 2018 Approved
dbatools (PowerShell Module) 0.9.730 262 Friday, December 21, 2018 Approved
dbatools (PowerShell Module) 0.9.725 223 Thursday, December 20, 2018 Approved
dbatools (PowerShell Module) 0.9.724 195 Thursday, December 20, 2018 Approved
dbatools (PowerShell Module) 0.9.722 181 Wednesday, December 19, 2018 Approved
dbatools (PowerShell Module) 0.9.721 218 Tuesday, December 18, 2018 Approved
dbatools (PowerShell Module) 0.9.720 254 Sunday, December 16, 2018 Approved
dbatools (PowerShell Module) 0.9.719 209 Saturday, December 15, 2018 Approved
dbatools (PowerShell Module) 0.9.718 196 Friday, December 14, 2018 Approved
dbatools (PowerShell Module) 0.9.717 205 Friday, December 14, 2018 Approved
dbatools (PowerShell Module) 0.9.715 202 Thursday, December 13, 2018 Approved
dbatools (PowerShell Module) 0.9.714 248 Monday, December 10, 2018 Approved
dbatools (PowerShell Module) 0.9.712 313 Sunday, December 9, 2018 Approved
dbatools (PowerShell Module) 0.9.711 447 Friday, December 7, 2018 Approved
dbatools (PowerShell Module) 0.9.710 385 Thursday, December 6, 2018 Approved
dbatools (PowerShell Module) 0.9.709 198 Tuesday, December 4, 2018 Approved
dbatools (PowerShell Module) 0.9.707 147 Tuesday, December 4, 2018 Approved
dbatools (PowerShell Module) 0.9.704 211 Monday, December 3, 2018 Approved
dbatools (PowerShell Module) 0.9.703 193 Monday, December 3, 2018 Approved
dbatools (PowerShell Module) 0.9.702 144 Sunday, December 2, 2018 Approved
dbatools (PowerShell Module) 0.9.701 185 Saturday, December 1, 2018 Approved
dbatools (PowerShell Module) 0.9.538 192 Friday, November 30, 2018 Approved
dbatools (PowerShell Module) 0.9.537 185 Friday, November 30, 2018 Approved
dbatools (PowerShell Module) 0.9.535 157 Thursday, November 29, 2018 Approved
dbatools (PowerShell Module) 0.9.533 223 Wednesday, November 28, 2018 Approved
dbatools (PowerShell Module) 0.9.532 184 Tuesday, November 27, 2018 Approved
dbatools (PowerShell Module) 0.9.531 186 Sunday, November 25, 2018 Approved
dbatools (PowerShell Module) 0.9.530 165 Saturday, November 24, 2018 Approved
dbatools (PowerShell Module) 0.9.527 180 Saturday, November 24, 2018 Approved
dbatools (PowerShell Module) 0.9.523 146 Friday, November 23, 2018 Approved
dbatools (PowerShell Module) 0.9.521 249 Thursday, November 22, 2018 Approved
dbatools (PowerShell Module) 0.9.520 189 Thursday, November 22, 2018 Approved
dbatools (PowerShell Module) 0.9.519 202 Sunday, November 18, 2018 Approved
dbatools (PowerShell Module) 0.9.518 141 Friday, November 16, 2018 Approved
dbatools (PowerShell Module) 0.9.517 206 Friday, November 16, 2018 Approved
dbatools (PowerShell Module) 0.9.512 170 Wednesday, November 14, 2018 Approved
dbatools (PowerShell Module) 0.9.510 208 Wednesday, November 14, 2018 Approved
dbatools (PowerShell Module) 0.9.509 189 Monday, November 12, 2018 Approved
dbatools (PowerShell Module) 0.9.508 193 Monday, November 12, 2018 Approved
dbatools (PowerShell Module) 0.9.507 173 Saturday, November 10, 2018 Approved
dbatools (PowerShell Module) 0.9.504 178 Thursday, November 8, 2018 Approved
dbatools (PowerShell Module) 0.9.503 175 Thursday, November 8, 2018 Approved
dbatools (PowerShell Module) 0.9.502 157 Wednesday, November 7, 2018 Approved
dbatools (PowerShell Module) 0.9.501 181 Monday, November 5, 2018 Approved
dbatools (PowerShell Module) 0.9.500 178 Saturday, November 3, 2018 Approved
dbatools (PowerShell Module) 0.9.499 209 Friday, November 2, 2018 Approved
dbatools (PowerShell Module) 0.9.498 147 Friday, November 2, 2018 Approved
dbatools (PowerShell Module) 0.9.497 144 Wednesday, October 31, 2018 Approved
dbatools (PowerShell Module) 0.9.495 179 Tuesday, October 30, 2018 Approved
dbatools (PowerShell Module) 0.9.494 164 Tuesday, October 30, 2018 Approved
dbatools (PowerShell Module) 0.9.492 159 Sunday, October 28, 2018 Approved
dbatools (PowerShell Module) 0.9.491 191 Friday, October 26, 2018 Approved
dbatools (PowerShell Module) 0.9.490 152 Wednesday, October 24, 2018 Approved
dbatools (PowerShell Module) 0.9.489 165 Wednesday, October 24, 2018 Approved
dbatools (PowerShell Module) 0.9.487 211 Friday, October 19, 2018 Approved
dbatools (PowerShell Module) 0.9.485 126 Thursday, October 18, 2018 Approved
dbatools (PowerShell Module) 0.9.484 176 Tuesday, October 16, 2018 Approved
dbatools (PowerShell Module) 0.9.483 168 Monday, October 15, 2018 Approved
dbatools (PowerShell Module) 0.9.481 190 Monday, October 15, 2018 Approved
dbatools (PowerShell Module) 0.9.479 183 Monday, October 15, 2018 Approved
dbatools (PowerShell Module) 0.9.477 156 Friday, October 12, 2018 Approved
dbatools (PowerShell Module) 0.9.475 143 Friday, October 12, 2018 Approved
dbatools (PowerShell Module) 0.9.472 186 Tuesday, October 9, 2018 Approved
dbatools (PowerShell Module) 0.9.471 166 Monday, October 8, 2018 Approved
dbatools (PowerShell Module) 0.9.470 169 Sunday, October 7, 2018 Approved
dbatools (PowerShell Module) 0.9.459 165 Sunday, October 7, 2018 Approved
dbatools (PowerShell Module) 0.9.458 182 Saturday, October 6, 2018 Approved
dbatools (PowerShell Module) 0.9.455 196 Friday, October 5, 2018 Approved
dbatools (PowerShell Module) 0.9.454 169 Thursday, October 4, 2018 Approved
dbatools (PowerShell Module) 0.9.450 184 Monday, October 1, 2018 Approved
dbatools (PowerShell Module) 0.9.447 183 Saturday, September 29, 2018 Approved
dbatools (PowerShell Module) 0.9.445 140 Thursday, September 27, 2018 Approved
dbatools (PowerShell Module) 0.9.442 162 Wednesday, September 26, 2018 Approved
dbatools (PowerShell Module) 0.9.440 147 Wednesday, September 26, 2018 Approved
dbatools (PowerShell Module) 0.9.439 163 Wednesday, September 26, 2018 Approved
dbatools (PowerShell Module) 0.9.438 174 Wednesday, September 26, 2018 Approved
dbatools (PowerShell Module) 0.9.435 178 Monday, September 24, 2018 Approved
dbatools (PowerShell Module) 0.9.434 174 Sunday, September 23, 2018 Approved
dbatools (PowerShell Module) 0.9.433 179 Friday, September 21, 2018 Approved
dbatools (PowerShell Module) 0.9.432 144 Friday, September 21, 2018 Approved
dbatools (PowerShell Module) 0.9.431 181 Wednesday, September 19, 2018 Approved
dbatools (PowerShell Module) 0.9.428 150 Wednesday, September 19, 2018 Approved
dbatools (PowerShell Module) 0.9.427 157 Tuesday, September 18, 2018 Approved
dbatools (PowerShell Module) 0.9.424 180 Tuesday, September 18, 2018 Approved
dbatools (PowerShell Module) 0.9.422 197 Monday, September 17, 2018 Approved
dbatools (PowerShell Module) 0.9.419 174 Friday, September 14, 2018 Approved
dbatools (PowerShell Module) 0.9.417 438 Sunday, September 9, 2018 Approved
dbatools (PowerShell Module) 0.9.415 163 Saturday, September 8, 2018 Approved
dbatools (PowerShell Module) 0.9.412 151 Saturday, September 8, 2018 Approved
dbatools (PowerShell Module) 0.9.411 161 Friday, September 7, 2018 Approved
dbatools (PowerShell Module) 0.9.410 210 Friday, September 7, 2018 Approved
dbatools (PowerShell Module) 0.9.402 166 Thursday, September 6, 2018 Approved
dbatools (PowerShell Module) 0.9.400 151 Wednesday, September 5, 2018 Approved
dbatools (PowerShell Module) 0.9.399 185 Friday, August 31, 2018 Approved
dbatools (PowerShell Module) 0.9.398 194 Friday, August 31, 2018 Approved
dbatools (PowerShell Module) 0.9.395 249 Tuesday, August 28, 2018 Approved
dbatools (PowerShell Module) 0.9.394 172 Monday, August 27, 2018 Approved
dbatools (PowerShell Module) 0.9.393 202 Friday, August 24, 2018 Approved
dbatools (PowerShell Module) 0.9.392 176 Wednesday, August 22, 2018 Approved
dbatools (PowerShell Module) 0.9.390 166 Tuesday, August 21, 2018 Approved
dbatools (PowerShell Module) 0.9.389 166 Sunday, August 19, 2018 Approved
dbatools (PowerShell Module) 0.9.388 141 Friday, August 17, 2018 Approved
dbatools (PowerShell Module) 0.9.387 204 Thursday, August 16, 2018 Approved
dbatools (PowerShell Module) 0.9.385 218 Wednesday, August 8, 2018 Approved
dbatools (PowerShell Module) 0.9.384 204 Friday, August 3, 2018 Approved
dbatools (PowerShell Module) 0.9.383 172 Thursday, August 2, 2018 Approved
dbatools (PowerShell Module) 0.9.382 224 Thursday, July 26, 2018 Approved
dbatools (PowerShell Module) 0.9.381 158 Thursday, July 26, 2018 Approved
dbatools (PowerShell Module) 0.9.380 184 Tuesday, July 24, 2018 Approved
dbatools (PowerShell Module) 0.9.378 218 Monday, July 23, 2018 Approved
dbatools (PowerShell Module) 0.9.377 199 Saturday, July 21, 2018 Approved
dbatools (PowerShell Module) 0.9.376 219 Wednesday, July 18, 2018 Approved
dbatools (PowerShell Module) 0.9.375 219 Monday, July 16, 2018 Approved
dbatools (PowerShell Module) 0.9.362 245 Friday, June 29, 2018 Approved

This package has no dependencies.

Discussion for the dbatools (PowerShell Module) Package

Ground Rules:

  • This discussion is only about dbatools (PowerShell Module) and the dbatools (PowerShell Module) package. If you have feedback for Chocolatey, please contact the Google Group.
  • This discussion will carry over multiple versions. If you have a comment about a particular version, please note that in your comments.
  • The maintainers of this Chocolatey Package will be notified about new comments that are posted to this Disqus thread, however, it is NOT a guarantee that you will get a response. If you do not hear back from the maintainers after posting a message below, please follow up by using the link on the left side of this page or follow this link to contact maintainers. If you still hear nothing back, please follow the package triage process.
  • Tell us what you love about the package or dbatools (PowerShell Module), or tell us what needs improvement.
  • Share your experiences with the package, or extra configuration or gotchas that you've found.
  • If you use a url, the comment will be flagged for moderation until you've been whitelisted. Disqus moderated comments are approved on a weekly schedule if not sooner. It could take between 1-5 days for your comment to show up.
comments powered by Disqus